aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.changelog.yml17
-rw-r--r--.devcontainer/devcontainer.json6
-rw-r--r--.dockerignore21
-rw-r--r--.editorconfig4
-rw-r--r--.eslintrc.cjs126
-rw-r--r--.gitattributes3
-rw-r--r--.github/ISSUE_TEMPLATE/config.yml2
-rw-r--r--.github/labeler.yml10
-rw-r--r--.github/workflows/cron-licenses.yml6
-rw-r--r--.github/workflows/files-changed.yml9
-rw-r--r--.github/workflows/pull-compliance.yml10
-rw-r--r--.github/workflows/pull-db-tests.yml10
-rw-r--r--.github/workflows/pull-e2e-tests.yml6
-rw-r--r--.github/workflows/release-nightly.yml28
-rw-r--r--.github/workflows/release-tag-rc.yml30
-rw-r--r--.github/workflows/release-tag-version.yml34
-rw-r--r--.gitignore28
-rw-r--r--.golangci.yml283
-rw-r--r--.ignore3
-rw-r--r--.mailmap2
-rw-r--r--CHANGELOG.md978
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--Dockerfile4
-rw-r--r--Dockerfile.rootless5
-rw-r--r--MAINTAINERS2
-rw-r--r--Makefile286
-rw-r--r--README.md108
-rw-r--r--README.zh-cn.md206
-rw-r--r--README.zh-tw.md206
-rw-r--r--README_ZH.md61
-rw-r--r--SECURITY.md54
-rw-r--r--assets/go-licenses.json86
-rw-r--r--build.go11
-rw-r--r--build/generate-bindata.go83
-rw-r--r--build/generate-licenses.go176
-rw-r--r--build/license/aliasgenerator.go41
-rw-r--r--build/license/aliasgenerator_test.go39
-rw-r--r--cmd/actions.go10
-rw-r--r--cmd/admin.go43
-rw-r--r--cmd/admin_auth.go15
-rw-r--r--cmd/admin_auth_ldap.go150
-rw-r--r--cmd/admin_auth_ldap_test.go111
-rw-r--r--cmd/admin_auth_oauth.go75
-rw-r--r--cmd/admin_auth_oauth_test.go333
-rw-r--r--cmd/admin_auth_smtp.go (renamed from cmd/admin_auth_stmp.go)80
-rw-r--r--cmd/admin_auth_smtp_test.go271
-rw-r--r--cmd/admin_regenerate.go14
-rw-r--r--cmd/admin_user.go12
-rw-r--r--cmd/admin_user_change_password.go64
-rw-r--r--cmd/admin_user_change_password_test.go91
-rw-r--r--cmd/admin_user_create.go202
-rw-r--r--cmd/admin_user_create_test.go132
-rw-r--r--cmd/admin_user_delete.go67
-rw-r--r--cmd/admin_user_delete_test.go111
-rw-r--r--cmd/admin_user_generate_access_token.go17
-rw-r--r--cmd/admin_user_list.go8
-rw-r--r--cmd/admin_user_must_change_password.go55
-rw-r--r--cmd/admin_user_must_change_password_test.go78
-rw-r--r--cmd/cert.go128
-rw-r--r--cmd/cert_test.go123
-rw-r--r--cmd/cmd.go25
-rw-r--r--cmd/cmd_test.go38
-rw-r--r--cmd/docs.go18
-rw-r--r--cmd/doctor.go58
-rw-r--r--cmd/doctor_convert.go10
-rw-r--r--cmd/doctor_test.go13
-rw-r--r--cmd/dump.go39
-rw-r--r--cmd/dump_repo.go34
-rw-r--r--cmd/embedded.go37
-rw-r--r--cmd/generate.go13
-rw-r--r--cmd/hook.go30
-rw-r--r--cmd/hook_test.go3
-rw-r--r--cmd/keys.go10
-rw-r--r--cmd/mailer.go12
-rw-r--r--cmd/main.go61
-rw-r--r--cmd/main_test.go40
-rw-r--r--cmd/manager.go30
-rw-r--r--cmd/manager_logging.go52
-rw-r--r--cmd/migrate.go13
-rw-r--r--cmd/migrate_storage.go63
-rw-r--r--cmd/migrate_storage_test.go7
-rw-r--r--cmd/restore_repo.go8
-rw-r--r--cmd/serv.go111
-rw-r--r--cmd/web.go40
-rw-r--r--cmd/web_acme.go25
-rw-r--r--cmd/web_graceful.go6
-rw-r--r--contrib/backport/backport.go61
-rw-r--r--contrib/environment-to-ini/environment-to-ini.go9
-rw-r--r--custom/conf/app.example.ini230
-rw-r--r--docker/manifest.rootless.tmpl5
-rw-r--r--docker/manifest.tmpl5
-rwxr-xr-xdocker/root/etc/s6/openssh/setup15
-rwxr-xr-xdocker/root/usr/bin/entrypoint2
-rw-r--r--flake.lock12
-rw-r--r--flake.nix5
-rw-r--r--go.mod271
-rw-r--r--go.sum598
-rw-r--r--main.go2
-rw-r--r--main_timezones.go16
-rw-r--r--models/actions/artifact.go50
-rw-r--r--models/actions/run.go59
-rw-r--r--models/actions/run_job.go33
-rw-r--r--models/actions/run_job_list.go60
-rw-r--r--models/actions/run_job_status_test.go6
-rw-r--r--models/actions/run_list.go38
-rw-r--r--models/actions/runner.go66
-rw-r--r--models/actions/runner_token_test.go6
-rw-r--r--models/actions/schedule.go22
-rw-r--r--models/actions/schedule_spec_list.go2
-rw-r--r--models/actions/status.go9
-rw-r--r--models/actions/task.go17
-rw-r--r--models/actions/task_list.go4
-rw-r--r--models/actions/utils.go19
-rw-r--r--models/actions/variable.go67
-rw-r--r--models/activities/action.go194
-rw-r--r--models/activities/action_list.go55
-rw-r--r--models/activities/action_test.go39
-rw-r--r--models/activities/notification_list.go20
-rw-r--r--models/activities/notification_test.go12
-rw-r--r--models/activities/repo_activity.go5
-rw-r--r--models/activities/statistic.go21
-rw-r--r--models/activities/user_heatmap.go2
-rw-r--r--models/admin/task.go2
-rw-r--r--models/asymkey/error.go4
-rw-r--r--models/asymkey/gpg_key.go54
-rw-r--r--models/asymkey/gpg_key_add.go12
-rw-r--r--models/asymkey/gpg_key_commit_verification.go341
-rw-r--r--models/asymkey/gpg_key_common.go33
-rw-r--r--models/asymkey/gpg_key_test.go51
-rw-r--r--models/asymkey/gpg_key_verify.go4
-rw-r--r--models/asymkey/ssh_key_commit_verification.go80
-rw-r--r--models/asymkey/ssh_key_fingerprint.go44
-rw-r--r--models/asymkey/ssh_key_parse.go76
-rw-r--r--models/asymkey/ssh_key_test.go32
-rw-r--r--models/asymkey/ssh_key_verify.go8
-rw-r--r--models/auth/access_token_scope.go25
-rw-r--r--models/auth/access_token_scope_test.go31
-rw-r--r--models/auth/auth_token.go2
-rw-r--r--models/auth/oauth2.go8
-rw-r--r--models/auth/oauth2_test.go4
-rw-r--r--models/auth/source.go43
-rw-r--r--models/auth/source_test.go2
-rw-r--r--models/auth/twofactor.go10
-rw-r--r--models/db/context.go16
-rw-r--r--models/db/context_test.go2
-rwxr-xr-xmodels/db/engine.go2
-rw-r--r--models/db/engine_hook.go25
-rw-r--r--models/db/engine_init.go11
-rw-r--r--models/db/engine_test.go11
-rw-r--r--models/db/error.go2
-rw-r--r--models/db/list_test.go2
-rw-r--r--models/db/log.go2
-rw-r--r--models/db/name.go11
-rw-r--r--models/db/search.go4
-rw-r--r--models/db/sql_postgres_with_schema.go4
-rw-r--r--models/dbfs/dbfile.go20
-rw-r--r--models/dbfs/dbfs_test.go24
-rw-r--r--models/fixtures/access.yml6
-rw-r--r--models/fixtures/action_artifact.yml72
-rw-r--r--models/fixtures/action_run.yml67
-rw-r--r--models/fixtures/action_run_job.yml60
-rw-r--r--models/fixtures/action_runner.yml51
-rw-r--r--models/fixtures/action_task.yml60
-rw-r--r--models/fixtures/branch.yml120
-rw-r--r--models/fixtures/commit_status.yml5
-rw-r--r--models/fixtures/email_address.yml2
-rw-r--r--models/fixtures/hook_task.yml2
-rw-r--r--models/fixtures/issue_pin.yml6
-rw-r--r--models/fixtures/repo_transfer.yml8
-rw-r--r--models/fixtures/repository.yml13
-rw-r--r--models/fixtures/user.yml2
-rw-r--r--models/fixtures/webhook.yml29
-rw-r--r--models/git/branch.go26
-rw-r--r--models/git/branch_test.go2
-rw-r--r--models/git/commit_status.go130
-rw-r--r--models/git/commit_status_summary.go20
-rw-r--r--models/git/commit_status_test.go93
-rw-r--r--models/git/lfs.go2
-rw-r--r--models/git/protected_branch.go4
-rw-r--r--models/git/protected_branch_list_test.go2
-rw-r--r--models/git/protected_branch_test.go2
-rw-r--r--models/issues/comment.go61
-rw-r--r--models/issues/comment_code.go11
-rw-r--r--models/issues/comment_list.go57
-rw-r--r--models/issues/comment_test.go18
-rw-r--r--models/issues/issue.go297
-rw-r--r--models/issues/issue_label.go1
-rw-r--r--models/issues/issue_list.go116
-rw-r--r--models/issues/issue_list_test.go18
-rw-r--r--models/issues/issue_lock.go10
-rw-r--r--models/issues/issue_pin.go246
-rw-r--r--models/issues/issue_project.go50
-rw-r--r--models/issues/issue_search.go72
-rw-r--r--models/issues/issue_stats.go7
-rw-r--r--models/issues/issue_test.go43
-rw-r--r--models/issues/issue_update.go273
-rw-r--r--models/issues/label.go12
-rw-r--r--models/issues/label_test.go28
-rw-r--r--models/issues/milestone_test.go6
-rw-r--r--models/issues/pull.go59
-rw-r--r--models/issues/pull_list.go29
-rw-r--r--models/issues/pull_list_test.go14
-rw-r--r--models/issues/pull_test.go59
-rw-r--r--models/issues/reaction.go8
-rw-r--r--models/issues/review.go23
-rw-r--r--models/issues/review_list.go77
-rw-r--r--models/issues/review_test.go3
-rw-r--r--models/issues/stopwatch.go174
-rw-r--r--models/issues/stopwatch_test.go61
-rw-r--r--models/issues/tracked_time.go5
-rw-r--r--models/migrations/base/db.go16
-rw-r--r--models/migrations/base/tests.go14
-rw-r--r--models/migrations/migrations.go17
-rw-r--r--models/migrations/migrations_test.go6
-rw-r--r--models/migrations/v1_10/v100.go2
-rw-r--r--models/migrations/v1_10/v101.go2
-rw-r--r--models/migrations/v1_10/v88.go2
-rw-r--r--models/migrations/v1_10/v89.go2
-rw-r--r--models/migrations/v1_10/v90.go2
-rw-r--r--models/migrations/v1_10/v91.go2
-rw-r--r--models/migrations/v1_10/v92.go2
-rw-r--r--models/migrations/v1_10/v93.go2
-rw-r--r--models/migrations/v1_10/v94.go2
-rw-r--r--models/migrations/v1_10/v95.go2
-rw-r--r--models/migrations/v1_10/v96.go2
-rw-r--r--models/migrations/v1_10/v97.go2
-rw-r--r--models/migrations/v1_10/v98.go2
-rw-r--r--models/migrations/v1_10/v99.go2
-rw-r--r--models/migrations/v1_11/v102.go2
-rw-r--r--models/migrations/v1_11/v103.go2
-rw-r--r--models/migrations/v1_11/v104.go2
-rw-r--r--models/migrations/v1_11/v105.go2
-rw-r--r--models/migrations/v1_11/v106.go2
-rw-r--r--models/migrations/v1_11/v107.go2
-rw-r--r--models/migrations/v1_11/v108.go2
-rw-r--r--models/migrations/v1_11/v109.go2
-rw-r--r--models/migrations/v1_11/v110.go2
-rw-r--r--models/migrations/v1_11/v111.go9
-rw-r--r--models/migrations/v1_11/v112.go6
-rw-r--r--models/migrations/v1_11/v113.go2
-rw-r--r--models/migrations/v1_11/v114.go2
-rw-r--r--models/migrations/v1_11/v115.go4
-rw-r--r--models/migrations/v1_11/v116.go2
-rw-r--r--models/migrations/v1_12/v117.go2
-rw-r--r--models/migrations/v1_12/v118.go2
-rw-r--r--models/migrations/v1_12/v119.go2
-rw-r--r--models/migrations/v1_12/v120.go2
-rw-r--r--models/migrations/v1_12/v121.go2
-rw-r--r--models/migrations/v1_12/v122.go2
-rw-r--r--models/migrations/v1_12/v123.go2
-rw-r--r--models/migrations/v1_12/v124.go2
-rw-r--r--models/migrations/v1_12/v125.go2
-rw-r--r--models/migrations/v1_12/v126.go2
-rw-r--r--models/migrations/v1_12/v127.go2
-rw-r--r--models/migrations/v1_12/v128.go12
-rw-r--r--models/migrations/v1_12/v129.go2
-rw-r--r--models/migrations/v1_12/v130.go2
-rw-r--r--models/migrations/v1_12/v131.go2
-rw-r--r--models/migrations/v1_12/v132.go2
-rw-r--r--models/migrations/v1_12/v133.go2
-rw-r--r--models/migrations/v1_12/v134.go8
-rw-r--r--models/migrations/v1_12/v135.go2
-rw-r--r--models/migrations/v1_12/v136.go2
-rw-r--r--models/migrations/v1_12/v137.go2
-rw-r--r--models/migrations/v1_12/v138.go2
-rw-r--r--models/migrations/v1_12/v139.go2
-rw-r--r--models/migrations/v1_13/v140.go11
-rw-r--r--models/migrations/v1_13/v141.go2
-rw-r--r--models/migrations/v1_13/v142.go2
-rw-r--r--models/migrations/v1_13/v143.go2
-rw-r--r--models/migrations/v1_13/v144.go2
-rw-r--r--models/migrations/v1_13/v145.go8
-rw-r--r--models/migrations/v1_13/v146.go2
-rw-r--r--models/migrations/v1_13/v147.go2
-rw-r--r--models/migrations/v1_13/v148.go2
-rw-r--r--models/migrations/v1_13/v149.go2
-rw-r--r--models/migrations/v1_13/v150.go2
-rw-r--r--models/migrations/v1_13/v151.go5
-rw-r--r--models/migrations/v1_13/v152.go2
-rw-r--r--models/migrations/v1_13/v153.go2
-rw-r--r--models/migrations/v1_13/v154.go2
-rw-r--r--models/migrations/v1_14/main_test.go2
-rw-r--r--models/migrations/v1_14/v155.go2
-rw-r--r--models/migrations/v1_14/v156.go2
-rw-r--r--models/migrations/v1_14/v157.go13
-rw-r--r--models/migrations/v1_14/v158.go6
-rw-r--r--models/migrations/v1_14/v159.go2
-rw-r--r--models/migrations/v1_14/v160.go2
-rw-r--r--models/migrations/v1_14/v161.go2
-rw-r--r--models/migrations/v1_14/v162.go2
-rw-r--r--models/migrations/v1_14/v163.go2
-rw-r--r--models/migrations/v1_14/v164.go2
-rw-r--r--models/migrations/v1_14/v165.go12
-rw-r--r--models/migrations/v1_14/v166.go2
-rw-r--r--models/migrations/v1_14/v167.go2
-rw-r--r--models/migrations/v1_14/v168.go2
-rw-r--r--models/migrations/v1_14/v169.go2
-rw-r--r--models/migrations/v1_14/v170.go2
-rw-r--r--models/migrations/v1_14/v171.go2
-rw-r--r--models/migrations/v1_14/v172.go2
-rw-r--r--models/migrations/v1_14/v173.go2
-rw-r--r--models/migrations/v1_14/v174.go2
-rw-r--r--models/migrations/v1_14/v175.go2
-rw-r--r--models/migrations/v1_14/v176.go2
-rw-r--r--models/migrations/v1_14/v176_test.go2
-rw-r--r--models/migrations/v1_14/v177.go2
-rw-r--r--models/migrations/v1_14/v177_test.go2
-rw-r--r--models/migrations/v1_15/main_test.go2
-rw-r--r--models/migrations/v1_15/v178.go2
-rw-r--r--models/migrations/v1_15/v179.go2
-rw-r--r--models/migrations/v1_15/v180.go2
-rw-r--r--models/migrations/v1_15/v181.go2
-rw-r--r--models/migrations/v1_15/v181_test.go6
-rw-r--r--models/migrations/v1_15/v182.go2
-rw-r--r--models/migrations/v1_15/v182_test.go2
-rw-r--r--models/migrations/v1_15/v183.go2
-rw-r--r--models/migrations/v1_15/v184.go2
-rw-r--r--models/migrations/v1_15/v185.go2
-rw-r--r--models/migrations/v1_15/v186.go2
-rw-r--r--models/migrations/v1_15/v187.go2
-rw-r--r--models/migrations/v1_15/v188.go2
-rw-r--r--models/migrations/v1_16/main_test.go2
-rw-r--r--models/migrations/v1_16/v189.go2
-rw-r--r--models/migrations/v1_16/v189_test.go6
-rw-r--r--models/migrations/v1_16/v190.go2
-rw-r--r--models/migrations/v1_16/v191.go2
-rw-r--r--models/migrations/v1_16/v192.go2
-rw-r--r--models/migrations/v1_16/v193.go2
-rw-r--r--models/migrations/v1_16/v193_test.go6
-rw-r--r--models/migrations/v1_16/v194.go2
-rw-r--r--models/migrations/v1_16/v195.go2
-rw-r--r--models/migrations/v1_16/v195_test.go2
-rw-r--r--models/migrations/v1_16/v196.go2
-rw-r--r--models/migrations/v1_16/v197.go2
-rw-r--r--models/migrations/v1_16/v198.go2
-rw-r--r--models/migrations/v1_16/v199.go2
-rw-r--r--models/migrations/v1_16/v200.go2
-rw-r--r--models/migrations/v1_16/v201.go2
-rw-r--r--models/migrations/v1_16/v202.go2
-rw-r--r--models/migrations/v1_16/v203.go2
-rw-r--r--models/migrations/v1_16/v204.go2
-rw-r--r--models/migrations/v1_16/v205.go2
-rw-r--r--models/migrations/v1_16/v206.go2
-rw-r--r--models/migrations/v1_16/v207.go2
-rw-r--r--models/migrations/v1_16/v208.go2
-rw-r--r--models/migrations/v1_16/v209.go2
-rw-r--r--models/migrations/v1_16/v210.go2
-rw-r--r--models/migrations/v1_16/v210_test.go4
-rw-r--r--models/migrations/v1_17/main_test.go2
-rw-r--r--models/migrations/v1_17/v211.go2
-rw-r--r--models/migrations/v1_17/v212.go2
-rw-r--r--models/migrations/v1_17/v213.go2
-rw-r--r--models/migrations/v1_17/v214.go2
-rw-r--r--models/migrations/v1_17/v215.go2
-rw-r--r--models/migrations/v1_17/v216.go2
-rw-r--r--models/migrations/v1_17/v217.go2
-rw-r--r--models/migrations/v1_17/v218.go2
-rw-r--r--models/migrations/v1_17/v219.go2
-rw-r--r--models/migrations/v1_17/v220.go2
-rw-r--r--models/migrations/v1_17/v221.go2
-rw-r--r--models/migrations/v1_17/v221_test.go2
-rw-r--r--models/migrations/v1_17/v222.go5
-rw-r--r--models/migrations/v1_17/v223.go2
-rw-r--r--models/migrations/v1_18/main_test.go2
-rw-r--r--models/migrations/v1_18/v224.go2
-rw-r--r--models/migrations/v1_18/v225.go2
-rw-r--r--models/migrations/v1_18/v226.go2
-rw-r--r--models/migrations/v1_18/v227.go2
-rw-r--r--models/migrations/v1_18/v228.go2
-rw-r--r--models/migrations/v1_18/v229.go2
-rw-r--r--models/migrations/v1_18/v229_test.go2
-rw-r--r--models/migrations/v1_18/v230.go2
-rw-r--r--models/migrations/v1_18/v230_test.go2
-rw-r--r--models/migrations/v1_19/main_test.go2
-rw-r--r--models/migrations/v1_19/v231.go2
-rw-r--r--models/migrations/v1_19/v232.go2
-rw-r--r--models/migrations/v1_19/v233.go2
-rw-r--r--models/migrations/v1_19/v233_test.go4
-rw-r--r--models/migrations/v1_19/v234.go2
-rw-r--r--models/migrations/v1_19/v235.go2
-rw-r--r--models/migrations/v1_19/v236.go2
-rw-r--r--models/migrations/v1_19/v237.go2
-rw-r--r--models/migrations/v1_19/v238.go2
-rw-r--r--models/migrations/v1_19/v239.go2
-rw-r--r--models/migrations/v1_19/v240.go2
-rw-r--r--models/migrations/v1_19/v241.go2
-rw-r--r--models/migrations/v1_19/v242.go2
-rw-r--r--models/migrations/v1_19/v243.go2
-rw-r--r--models/migrations/v1_20/main_test.go2
-rw-r--r--models/migrations/v1_20/v244.go2
-rw-r--r--models/migrations/v1_20/v245.go5
-rw-r--r--models/migrations/v1_20/v246.go2
-rw-r--r--models/migrations/v1_20/v247.go2
-rw-r--r--models/migrations/v1_20/v248.go2
-rw-r--r--models/migrations/v1_20/v249.go2
-rw-r--r--models/migrations/v1_20/v250.go2
-rw-r--r--models/migrations/v1_20/v251.go2
-rw-r--r--models/migrations/v1_20/v252.go2
-rw-r--r--models/migrations/v1_20/v253.go2
-rw-r--r--models/migrations/v1_20/v254.go2
-rw-r--r--models/migrations/v1_20/v255.go2
-rw-r--r--models/migrations/v1_20/v256.go2
-rw-r--r--models/migrations/v1_20/v257.go2
-rw-r--r--models/migrations/v1_20/v258.go2
-rw-r--r--models/migrations/v1_20/v259.go4
-rw-r--r--models/migrations/v1_20/v259_test.go4
-rw-r--r--models/migrations/v1_21/main_test.go2
-rw-r--r--models/migrations/v1_21/v260.go2
-rw-r--r--models/migrations/v1_21/v261.go2
-rw-r--r--models/migrations/v1_21/v262.go2
-rw-r--r--models/migrations/v1_21/v263.go2
-rw-r--r--models/migrations/v1_21/v264.go6
-rw-r--r--models/migrations/v1_21/v265.go2
-rw-r--r--models/migrations/v1_21/v266.go2
-rw-r--r--models/migrations/v1_21/v267.go2
-rw-r--r--models/migrations/v1_21/v268.go2
-rw-r--r--models/migrations/v1_21/v269.go2
-rw-r--r--models/migrations/v1_21/v270.go2
-rw-r--r--models/migrations/v1_21/v271.go3
-rw-r--r--models/migrations/v1_21/v272.go3
-rw-r--r--models/migrations/v1_21/v273.go3
-rw-r--r--models/migrations/v1_21/v274.go3
-rw-r--r--models/migrations/v1_21/v275.go2
-rw-r--r--models/migrations/v1_21/v276.go4
-rw-r--r--models/migrations/v1_21/v277.go2
-rw-r--r--models/migrations/v1_21/v278.go2
-rw-r--r--models/migrations/v1_21/v279.go2
-rw-r--r--models/migrations/v1_22/main_test.go2
-rw-r--r--models/migrations/v1_22/v280.go2
-rw-r--r--models/migrations/v1_22/v281.go2
-rw-r--r--models/migrations/v1_22/v282.go2
-rw-r--r--models/migrations/v1_22/v283.go2
-rw-r--r--models/migrations/v1_22/v283_test.go2
-rw-r--r--models/migrations/v1_22/v284.go3
-rw-r--r--models/migrations/v1_22/v285.go2
-rw-r--r--models/migrations/v1_22/v286.go2
-rw-r--r--models/migrations/v1_22/v286_test.go6
-rw-r--r--models/migrations/v1_22/v287.go2
-rw-r--r--models/migrations/v1_22/v287_test.go6
-rw-r--r--models/migrations/v1_22/v288.go2
-rw-r--r--models/migrations/v1_22/v289.go2
-rw-r--r--models/migrations/v1_22/v290.go2
-rw-r--r--models/migrations/v1_22/v291.go2
-rw-r--r--models/migrations/v1_22/v292.go2
-rw-r--r--models/migrations/v1_22/v293.go2
-rw-r--r--models/migrations/v1_22/v293_test.go2
-rw-r--r--models/migrations/v1_22/v294.go2
-rw-r--r--models/migrations/v1_22/v294_test.go5
-rw-r--r--models/migrations/v1_22/v295.go2
-rw-r--r--models/migrations/v1_22/v296.go2
-rw-r--r--models/migrations/v1_22/v297.go2
-rw-r--r--models/migrations/v1_22/v298.go2
-rw-r--r--models/migrations/v1_23/main_test.go2
-rw-r--r--models/migrations/v1_23/v299.go8
-rw-r--r--models/migrations/v1_23/v300.go8
-rw-r--r--models/migrations/v1_23/v301.go8
-rw-r--r--models/migrations/v1_23/v302.go7
-rw-r--r--models/migrations/v1_23/v302_test.go51
-rw-r--r--models/migrations/v1_23/v303.go8
-rw-r--r--models/migrations/v1_23/v304.go7
-rw-r--r--models/migrations/v1_23/v304_test.go40
-rw-r--r--models/migrations/v1_23/v305.go2
-rw-r--r--models/migrations/v1_23/v306.go8
-rw-r--r--models/migrations/v1_23/v307.go2
-rw-r--r--models/migrations/v1_23/v308.go2
-rw-r--r--models/migrations/v1_23/v309.go2
-rw-r--r--models/migrations/v1_23/v310.go8
-rw-r--r--models/migrations/v1_23/v311.go9
-rw-r--r--models/migrations/v1_24/v312.go25
-rw-r--r--models/migrations/v1_24/v313.go31
-rw-r--r--models/migrations/v1_24/v314.go19
-rw-r--r--models/migrations/v1_24/v315.go19
-rw-r--r--models/migrations/v1_24/v316.go24
-rw-r--r--models/migrations/v1_24/v317.go56
-rw-r--r--models/migrations/v1_24/v318.go21
-rw-r--r--models/migrations/v1_24/v319.go19
-rw-r--r--models/migrations/v1_24/v320.go57
-rw-r--r--models/migrations/v1_6/v70.go2
-rw-r--r--models/migrations/v1_6/v71.go2
-rw-r--r--models/migrations/v1_6/v72.go2
-rw-r--r--models/migrations/v1_7/v73.go2
-rw-r--r--models/migrations/v1_7/v74.go2
-rw-r--r--models/migrations/v1_7/v75.go2
-rw-r--r--models/migrations/v1_8/v76.go2
-rw-r--r--models/migrations/v1_8/v77.go2
-rw-r--r--models/migrations/v1_8/v78.go2
-rw-r--r--models/migrations/v1_8/v79.go2
-rw-r--r--models/migrations/v1_8/v80.go2
-rw-r--r--models/migrations/v1_8/v81.go2
-rw-r--r--models/migrations/v1_9/v82.go2
-rw-r--r--models/migrations/v1_9/v83.go2
-rw-r--r--models/migrations/v1_9/v84.go2
-rw-r--r--models/migrations/v1_9/v85.go2
-rw-r--r--models/migrations/v1_9/v86.go2
-rw-r--r--models/migrations/v1_9/v87.go2
-rw-r--r--models/organization/org.go11
-rw-r--r--models/organization/org_list.go22
-rw-r--r--models/organization/org_list_test.go43
-rw-r--r--models/organization/org_test.go43
-rw-r--r--models/organization/org_user.go2
-rw-r--r--models/organization/org_user_test.go2
-rw-r--r--models/organization/org_worktime.go103
-rw-r--r--models/organization/team.go24
-rw-r--r--models/organization/team_list.go3
-rw-r--r--models/organization/team_repo.go33
-rw-r--r--models/organization/team_repo_test.go2
-rw-r--r--models/organization/team_test.go6
-rw-r--r--models/packages/container/search.go12
-rw-r--r--models/packages/descriptor.go44
-rw-r--r--models/packages/nuget/search.go2
-rw-r--r--models/packages/package.go9
-rw-r--r--models/packages/package_file.go5
-rw-r--r--models/packages/package_property.go18
-rw-r--r--models/packages/package_version.go38
-rw-r--r--models/perm/access/repo_permission.go160
-rw-r--r--models/perm/access/repo_permission_test.go80
-rw-r--r--models/project/column.go56
-rw-r--r--models/project/column_test.go16
-rw-r--r--models/project/issue.go47
-rw-r--r--models/project/project.go11
-rw-r--r--models/project/project_test.go4
-rw-r--r--models/pull/automerge.go26
-rw-r--r--models/pull/review_state.go5
-rw-r--r--models/renderhelper/commit_checker.go2
-rw-r--r--models/renderhelper/repo_comment.go43
-rw-r--r--models/renderhelper/repo_comment_test.go14
-rw-r--r--models/renderhelper/repo_file.go18
-rw-r--r--models/renderhelper/repo_file_test.go23
-rw-r--r--models/renderhelper/repo_wiki.go19
-rw-r--r--models/renderhelper/repo_wiki_test.go13
-rw-r--r--models/renderhelper/simple_document.go10
-rw-r--r--models/renderhelper/simple_document_test.go5
-rw-r--r--models/repo/archiver.go17
-rw-r--r--models/repo/attachment.go2
-rw-r--r--models/repo/avatar.go3
-rw-r--r--models/repo/collaboration_test.go10
-rw-r--r--models/repo/license.go1
-rw-r--r--models/repo/org_repo.go56
-rw-r--r--models/repo/pushmirror_test.go2
-rw-r--r--models/repo/release.go68
-rw-r--r--models/repo/repo.go208
-rw-r--r--models/repo/repo_list.go45
-rw-r--r--models/repo/repo_list_test.go86
-rw-r--r--models/repo/repo_test.go101
-rw-r--r--models/repo/repo_unit.go28
-rw-r--r--models/repo/repo_unit_test.go10
-rw-r--r--models/repo/topic_test.go2
-rw-r--r--models/repo/transfer.go71
-rw-r--r--models/repo/update.go30
-rw-r--r--models/repo/upload.go16
-rw-r--r--models/repo/user_repo.go5
-rw-r--r--models/repo/user_repo_test.go17
-rw-r--r--models/repo/watch_test.go2
-rw-r--r--models/repo/wiki.go9
-rw-r--r--models/repo/wiki_test.go2
-rw-r--r--models/repo_test.go4
-rw-r--r--models/secret/secret.go51
-rw-r--r--models/system/notice_test.go2
-rw-r--r--models/system/setting_test.go12
-rw-r--r--models/unit/unit.go58
-rw-r--r--models/unittest/consistency.go17
-rw-r--r--models/unittest/fixtures_loader.go84
-rw-r--r--models/unittest/fixtures_test.go4
-rw-r--r--models/unittest/fscopy.go52
-rw-r--r--models/unittest/testdb.go23
-rw-r--r--models/unittest/unit_tests.go4
-rw-r--r--models/user/avatar.go26
-rw-r--r--models/user/avatar_test.go39
-rw-r--r--models/user/badge.go2
-rw-r--r--models/user/email_address.go15
-rw-r--r--models/user/email_address_test.go10
-rw-r--r--models/user/must_change_password.go2
-rw-r--r--models/user/openid.go5
-rw-r--r--models/user/openid_test.go24
-rw-r--r--models/user/search.go9
-rw-r--r--models/user/setting.go5
-rw-r--r--models/user/setting_keys.go3
-rw-r--r--models/user/setting_test.go8
-rw-r--r--models/user/user.go271
-rw-r--r--models/user/user_list.go48
-rw-r--r--models/user/user_system.go49
-rw-r--r--models/user/user_system_test.go32
-rw-r--r--models/user/user_test.go167
-rw-r--r--models/webhook/hooktask.go5
-rw-r--r--models/webhook/webhook.go203
-rw-r--r--models/webhook/webhook_system.go13
-rw-r--r--models/webhook/webhook_system_test.go37
-rw-r--r--models/webhook/webhook_test.go25
-rw-r--r--modules/actions/artifacts.go48
-rw-r--r--modules/actions/workflows.go113
-rw-r--r--modules/actions/workflows_test.go18
-rw-r--r--modules/analyze/vendor_test.go11
-rw-r--r--modules/assetfs/embed.go375
-rw-r--r--modules/assetfs/embed_test.go98
-rw-r--r--modules/assetfs/layered.go4
-rw-r--r--modules/assetfs/layered_test.go18
-rw-r--r--modules/auth/httpauth/httpauth.go47
-rw-r--r--modules/auth/httpauth/httpauth_test.go43
-rw-r--r--modules/auth/openid/discovery_cache_test.go28
-rw-r--r--modules/auth/pam/pam_test.go2
-rw-r--r--modules/auth/password/hash/common.go2
-rw-r--r--modules/auth/password/password.go2
-rw-r--r--modules/auth/password/password_test.go2
-rw-r--r--modules/auth/password/pwn/pwn.go2
-rw-r--r--modules/auth/password/pwn/pwn_test.go33
-rw-r--r--modules/avatar/avatar_test.go4
-rw-r--r--modules/avatar/hash_test.go8
-rw-r--r--modules/avatar/identicon/block.go4
-rw-r--r--modules/avatar/identicon/identicon.go5
-rw-r--r--modules/badge/badge.go133
-rw-r--r--modules/badge/badge_glyph_width.go206
-rw-r--r--modules/base/tool.go45
-rw-r--r--modules/base/tool_test.go30
-rw-r--r--modules/cache/cache.go21
-rw-r--r--modules/cache/cache_redis.go2
-rw-r--r--modules/cache/cache_test.go19
-rw-r--r--modules/cache/cache_twoqueue.go2
-rw-r--r--modules/cache/context.go179
-rw-r--r--modules/cache/context_test.go66
-rw-r--r--modules/cache/ephemeral.go90
-rw-r--r--modules/cache/string_cache.go2
-rw-r--r--modules/cachegroup/cachegroup.go12
-rw-r--r--modules/charset/ambiguous_gen_test.go2
-rw-r--r--modules/charset/charset.go2
-rw-r--r--modules/charset/charset_test.go4
-rw-r--r--modules/commitstatus/commit_status.go (renamed from modules/structs/commit_status.go)60
-rw-r--r--modules/commitstatus/commit_status_test.go201
-rw-r--r--modules/csv/csv_test.go17
-rw-r--r--modules/dump/dumper_test.go4
-rw-r--r--modules/emoji/emoji_test.go33
-rw-r--r--modules/eventsource/event_test.go17
-rw-r--r--modules/fileicon/basic.go31
-rw-r--r--modules/fileicon/entry.go31
-rw-r--r--modules/fileicon/material.go162
-rw-r--r--modules/fileicon/material_test.go27
-rw-r--r--modules/fileicon/render.go41
-rw-r--r--modules/git/attribute.go35
-rw-r--r--modules/git/attribute/attribute.go114
-rw-r--r--modules/git/attribute/attribute_test.go37
-rw-r--r--modules/git/attribute/batch.go216
-rw-r--r--modules/git/attribute/batch_test.go172
-rw-r--r--modules/git/attribute/checker.go101
-rw-r--r--modules/git/attribute/checker_test.go84
-rw-r--r--modules/git/attribute/main_test.go41
-rw-r--r--modules/git/batch.go13
-rw-r--r--modules/git/batch_reader.go12
-rw-r--r--modules/git/blame.go74
-rw-r--r--modules/git/blame_sha256_test.go5
-rw-r--r--modules/git/blame_test.go5
-rw-r--r--modules/git/blob.go84
-rw-r--r--modules/git/blob_test.go6
-rw-r--r--modules/git/cmdverb.go36
-rw-r--r--modules/git/command.go79
-rw-r--r--modules/git/command_race_test.go8
-rw-r--r--modules/git/command_test.go29
-rw-r--r--modules/git/commit.go49
-rw-r--r--modules/git/commit_info.go2
-rw-r--r--modules/git/commit_info_gogit.go4
-rw-r--r--modules/git/commit_info_nogogit.go50
-rw-r--r--modules/git/commit_info_test.go7
-rw-r--r--modules/git/commit_reader.go132
-rw-r--r--modules/git/commit_sha256_test.go19
-rw-r--r--modules/git/commit_submodule.go4
-rw-r--r--modules/git/commit_submodule_file.go131
-rw-r--r--modules/git/commit_submodule_file_test.go49
-rw-r--r--modules/git/commit_test.go44
-rw-r--r--modules/git/config.go16
-rw-r--r--modules/git/diff.go22
-rw-r--r--modules/git/diff_test.go10
-rw-r--r--modules/git/error.go16
-rw-r--r--modules/git/foreachref/format.go2
-rw-r--r--modules/git/fsck.go2
-rw-r--r--modules/git/git.go4
-rw-r--r--modules/git/git_test.go7
-rw-r--r--modules/git/grep.go33
-rw-r--r--modules/git/grep_test.go15
-rw-r--r--modules/git/hook.go65
-rw-r--r--modules/git/key.go15
-rw-r--r--modules/git/languagestats/language_stats.go (renamed from modules/git/repo_language_stats.go)30
-rw-r--r--modules/git/languagestats/language_stats_gogit.go (renamed from modules/git/repo_language_stats_gogit.go)73
-rw-r--r--modules/git/languagestats/language_stats_nogogit.go (renamed from modules/git/repo_language_stats_nogogit.go)94
-rw-r--r--modules/git/languagestats/language_stats_test.go (renamed from modules/git/repo_language_stats_test.go)26
-rw-r--r--modules/git/languagestats/main_test.go41
-rw-r--r--modules/git/last_commit_cache.go2
-rw-r--r--modules/git/log_name_status.go30
-rw-r--r--modules/git/notes_test.go9
-rw-r--r--modules/git/object_id.go2
-rw-r--r--modules/git/parse.go16
-rw-r--r--modules/git/parse_nogogit.go2
-rw-r--r--modules/git/parse_nogogit_test.go4
-rw-r--r--modules/git/pipeline/catfile.go10
-rw-r--r--modules/git/pipeline/lfs_nogogit.go2
-rw-r--r--modules/git/pipeline/namerev.go2
-rw-r--r--modules/git/pipeline/revlist.go8
-rw-r--r--modules/git/ref.go58
-rw-r--r--modules/git/ref_test.go11
-rw-r--r--modules/git/remote.go17
-rw-r--r--modules/git/repo.go44
-rw-r--r--modules/git/repo_archive.go40
-rw-r--r--modules/git/repo_archive_test.go32
-rw-r--r--modules/git/repo_attribute.go323
-rw-r--r--modules/git/repo_attribute_test.go97
-rw-r--r--modules/git/repo_base_gogit.go7
-rw-r--r--modules/git/repo_base_nogogit.go15
-rw-r--r--modules/git/repo_blame.go4
-rw-r--r--modules/git/repo_branch.go87
-rw-r--r--modules/git/repo_branch_gogit.go2
-rw-r--r--modules/git/repo_branch_nogogit.go2
-rw-r--r--modules/git/repo_branch_test.go14
-rw-r--r--modules/git/repo_commit.go95
-rw-r--r--modules/git/repo_commit_gogit.go2
-rw-r--r--modules/git/repo_commit_nogogit.go18
-rw-r--r--modules/git/repo_commitgraph.go2
-rw-r--r--modules/git/repo_commitgraph_gogit.go4
-rw-r--r--modules/git/repo_compare.go48
-rw-r--r--modules/git/repo_gpg.go20
-rw-r--r--modules/git/repo_index.go39
-rw-r--r--modules/git/repo_object.go20
-rw-r--r--modules/git/repo_ref.go11
-rw-r--r--modules/git/repo_ref_nogogit.go2
-rw-r--r--modules/git/repo_stats.go9
-rw-r--r--modules/git/repo_stats_test.go2
-rw-r--r--modules/git/repo_tag.go24
-rw-r--r--modules/git/repo_tag_nogogit.go7
-rw-r--r--modules/git/repo_tag_test.go61
-rw-r--r--modules/git/repo_test.go7
-rw-r--r--modules/git/repo_tree.go15
-rw-r--r--modules/git/repo_tree_gogit.go2
-rw-r--r--modules/git/repo_tree_nogogit.go6
-rw-r--r--modules/git/signature_test.go2
-rw-r--r--modules/git/submodule.go6
-rw-r--r--modules/git/submodule_test.go21
-rw-r--r--modules/git/tag.go5
-rw-r--r--modules/git/tests/repos/language_stats_repo/config2
-rw-r--r--modules/git/tests/repos/repo3_notes/config2
-rw-r--r--modules/git/tests/repos/repo4_commitsbetween/config2
-rw-r--r--modules/git/tree.go10
-rw-r--r--modules/git/tree_blob_gogit.go1
-rw-r--r--modules/git/tree_blob_nogogit.go36
-rw-r--r--modules/git/tree_entry.go98
-rw-r--r--modules/git/tree_entry_common_test.go76
-rw-r--r--modules/git/tree_entry_gogit.go10
-rw-r--r--modules/git/tree_entry_mode.go68
-rw-r--r--modules/git/tree_entry_nogogit.go14
-rw-r--r--modules/git/tree_entry_test.go47
-rw-r--r--modules/git/tree_gogit.go3
-rw-r--r--modules/git/tree_nogogit.go6
-rw-r--r--modules/git/tree_test.go6
-rw-r--r--modules/git/url/url.go100
-rw-r--r--modules/git/url/url_test.go107
-rw-r--r--modules/git/utils.go28
-rw-r--r--modules/gitrepo/branch.go20
-rw-r--r--modules/gitrepo/gitrepo.go36
-rw-r--r--modules/gitrepo/hooks.go (renamed from modules/repository/hooks.go)17
-rw-r--r--modules/gitrepo/tag.go15
-rw-r--r--modules/globallock/globallock_test.go4
-rw-r--r--modules/globallock/locker_test.go22
-rw-r--r--modules/globallock/redis_locker.go3
-rw-r--r--modules/graceful/manager_windows.go3
-rw-r--r--modules/graceful/releasereopen/releasereopen_test.go12
-rw-r--r--modules/gtprof/event.go32
-rw-r--r--modules/gtprof/trace.go175
-rw-r--r--modules/gtprof/trace_builtin.go96
-rw-r--r--modules/gtprof/trace_const.go19
-rw-r--r--modules/gtprof/trace_test.go93
-rw-r--r--modules/highlight/highlight.go3
-rw-r--r--modules/highlight/highlight_test.go4
-rw-r--r--modules/hostmatcher/hostmatcher.go11
-rw-r--r--modules/htmlutil/html.go10
-rw-r--r--modules/htmlutil/html_test.go9
-rw-r--r--modules/httpcache/httpcache.go53
-rw-r--r--modules/httplib/request.go26
-rw-r--r--modules/httplib/serve.go31
-rw-r--r--modules/httplib/serve_test.go19
-rw-r--r--modules/httplib/url.go113
-rw-r--r--modules/httplib/url_test.go64
-rw-r--r--modules/indexer/code/bleve/bleve.go48
-rw-r--r--modules/indexer/code/bleve/token/path/path.go12
-rw-r--r--modules/indexer/code/elasticsearch/elasticsearch.go29
-rw-r--r--modules/indexer/code/elasticsearch/elasticsearch_test.go4
-rw-r--r--modules/indexer/code/git.go20
-rw-r--r--modules/indexer/code/gitgrep/gitgrep.go66
-rw-r--r--modules/indexer/code/gitgrep/gitgrep_test.go (renamed from routers/web/repo/search_test.go)2
-rw-r--r--modules/indexer/code/indexer.go15
-rw-r--r--modules/indexer/code/indexer_test.go53
-rw-r--r--modules/indexer/code/internal/indexer.go16
-rw-r--r--modules/indexer/code/internal/util.go6
-rw-r--r--modules/indexer/code/search.go3
-rw-r--r--modules/indexer/indexer.go54
-rw-r--r--modules/indexer/internal/bleve/indexer.go10
-rw-r--r--modules/indexer/internal/bleve/query.go10
-rw-r--r--modules/indexer/internal/elasticsearch/indexer.go9
-rw-r--r--modules/indexer/internal/indexer.go6
-rw-r--r--modules/indexer/internal/meilisearch/indexer.go9
-rw-r--r--modules/indexer/issues/bleve/bleve.go50
-rw-r--r--modules/indexer/issues/db/db.go50
-rw-r--r--modules/indexer/issues/db/options.go14
-rw-r--r--modules/indexer/issues/dboptions.go19
-rw-r--r--modules/indexer/issues/elasticsearch/elasticsearch.go44
-rw-r--r--modules/indexer/issues/elasticsearch/elasticsearch_test.go18
-rw-r--r--modules/indexer/issues/indexer.go25
-rw-r--r--modules/indexer/issues/indexer_test.go117
-rw-r--r--modules/indexer/issues/internal/indexer.go14
-rw-r--r--modules/indexer/issues/internal/model.go8
-rw-r--r--modules/indexer/issues/internal/tests/tests.go86
-rw-r--r--modules/indexer/issues/meilisearch/meilisearch.go26
-rw-r--r--modules/indexer/issues/meilisearch/meilisearch_test.go29
-rw-r--r--modules/indexer/issues/util.go7
-rw-r--r--modules/indexer/stats/db.go13
-rw-r--r--modules/indexer/stats/indexer_test.go3
-rw-r--r--modules/indexer/stats/queue.go4
-rw-r--r--modules/issue/template/template.go19
-rw-r--r--modules/issue/template/template_test.go7
-rw-r--r--modules/json/json.go9
-rw-r--r--modules/json/json_test.go18
-rw-r--r--modules/label/label.go23
-rw-r--r--modules/label/parser.go4
-rw-r--r--modules/lfs/http_client.go10
-rw-r--r--modules/lfs/http_client_test.go8
-rw-r--r--modules/lfs/pointer.go13
-rw-r--r--modules/lfs/pointer_scanner_gogit.go2
-rw-r--r--modules/lfs/transferadapter_test.go9
-rw-r--r--modules/lfstransfer/backend/backend.go46
-rw-r--r--modules/lfstransfer/backend/lock.go26
-rw-r--r--modules/lfstransfer/backend/util.go106
-rw-r--r--modules/lfstransfer/backend/util_test.go53
-rw-r--r--modules/log/event_format.go36
-rw-r--r--modules/log/event_writer_base.go2
-rw-r--r--modules/log/event_writer_conn_test.go3
-rw-r--r--modules/log/flags.go2
-rw-r--r--modules/log/flags_test.go14
-rw-r--r--modules/log/level_test.go6
-rw-r--r--modules/log/logger.go4
-rw-r--r--modules/log/logger_global.go2
-rw-r--r--modules/log/logger_impl.go56
-rw-r--r--modules/log/logger_test.go83
-rw-r--r--modules/log/manager_test.go2
-rw-r--r--modules/log/misc.go16
-rw-r--r--modules/markup/asciicast/asciicast.go2
-rw-r--r--modules/markup/common/footnote.go14
-rw-r--r--modules/markup/common/linkify.go5
-rw-r--r--modules/markup/console/console.go38
-rw-r--r--modules/markup/console/console_test.go33
-rw-r--r--modules/markup/csv/csv_test.go5
-rw-r--r--modules/markup/external/external.go33
-rw-r--r--modules/markup/html.go93
-rw-r--r--modules/markup/html_commit.go11
-rw-r--r--modules/markup/html_email.go14
-rw-r--r--modules/markup/html_internal_test.go18
-rw-r--r--modules/markup/html_issue.go4
-rw-r--r--modules/markup/html_issue_test.go23
-rw-r--r--modules/markup/html_link.go6
-rw-r--r--modules/markup/html_mention.go4
-rw-r--r--modules/markup/html_node.go113
-rw-r--r--modules/markup/html_test.go73
-rw-r--r--modules/markup/internal/internal_test.go10
-rw-r--r--modules/markup/internal/renderinternal.go2
-rw-r--r--modules/markup/markdown/ast.go53
-rw-r--r--modules/markup/markdown/convertyaml.go43
-rw-r--r--modules/markup/markdown/goldmark.go52
-rw-r--r--modules/markup/markdown/markdown.go56
-rw-r--r--modules/markup/markdown/markdown_attention_test.go14
-rw-r--r--modules/markup/markdown/markdown_benchmark_test.go4
-rw-r--r--modules/markup/markdown/markdown_math_test.go67
-rw-r--r--modules/markup/markdown/markdown_test.go112
-rw-r--r--modules/markup/markdown/math/block_renderer.go8
-rw-r--r--modules/markup/markdown/math/inline_parser.go48
-rw-r--r--modules/markup/markdown/math/inline_renderer.go2
-rw-r--r--modules/markup/markdown/math/math.go21
-rw-r--r--modules/markup/markdown/meta_test.go12
-rw-r--r--modules/markup/markdown/renderconfig.go11
-rw-r--r--modules/markup/markdown/renderconfig_test.go37
-rw-r--r--modules/markup/markdown/toc.go3
-rw-r--r--modules/markup/markdown/transform_blockquote.go5
-rw-r--r--modules/markup/markdown/transform_codespan.go2
-rw-r--r--modules/markup/markdown/transform_heading.go4
-rw-r--r--modules/markup/markdown/transform_image.go59
-rw-r--r--modules/markup/markdown/transform_link.go27
-rw-r--r--modules/markup/mdstripper/mdstripper.go9
-rw-r--r--modules/markup/mdstripper/mdstripper_test.go4
-rw-r--r--modules/markup/orgmode/orgmode.go28
-rw-r--r--modules/markup/orgmode/orgmode_test.go29
-rw-r--r--modules/markup/render.go20
-rw-r--r--modules/markup/render_helper.go15
-rw-r--r--modules/markup/render_link.go18
-rw-r--r--modules/markup/render_link_test.go3
-rw-r--r--modules/markup/renderer.go12
-rw-r--r--modules/markup/renderer_test.go4
-rw-r--r--modules/markup/sanitizer_default.go9
-rw-r--r--modules/markup/sanitizer_default_test.go6
-rwxr-xr-xmodules/metrics/collector.go11
-rw-r--r--modules/migration/downloader.go21
-rw-r--r--modules/migration/null_downloader.go23
-rw-r--r--modules/migration/retry_downloader.go73
-rw-r--r--modules/migration/schemas_bindata.go24
-rw-r--r--modules/migration/schemas_static.go15
-rw-r--r--modules/migration/uploader.go24
-rw-r--r--modules/nosql/redis_test.go9
-rw-r--r--modules/optional/option.go17
-rw-r--r--modules/optional/option_test.go13
-rw-r--r--modules/optional/serialization_test.go14
-rw-r--r--modules/options/options_bindata.go17
-rw-r--r--modules/options/options_dynamic.go (renamed from modules/options/dynamic.go)0
-rw-r--r--modules/options/static.go14
-rw-r--r--modules/packages/conda/metadata.go6
-rw-r--r--modules/packages/container/const.go (renamed from models/packages/container/const.go)2
-rw-r--r--modules/packages/container/metadata.go26
-rw-r--r--modules/packages/container/metadata_test.go5
-rw-r--r--modules/packages/content_store.go3
-rw-r--r--modules/packages/goproxy/metadata.go3
-rw-r--r--modules/packages/hashed_buffer.go5
-rw-r--r--modules/packages/hashed_buffer_test.go3
-rw-r--r--modules/packages/npm/creator.go6
-rw-r--r--modules/packages/npm/metadata.go3
-rw-r--r--modules/packages/nuget/metadata.go90
-rw-r--r--modules/packages/nuget/metadata_test.go96
-rw-r--r--modules/packages/nuget/symbol_extractor.go8
-rw-r--r--modules/packages/nuget/symbol_extractor_test.go9
-rw-r--r--modules/packages/rubygems/marshal.go2
-rw-r--r--modules/packages/swift/metadata.go2
-rw-r--r--modules/paginator/paginator.go61
-rw-r--r--modules/paginator/paginator_test.go4
-rw-r--r--modules/private/actions.go2
-rw-r--r--modules/private/hook.go29
-rw-r--r--modules/private/internal.go15
-rw-r--r--modules/private/key.go4
-rw-r--r--modules/private/mail.go2
-rw-r--r--modules/private/manager.go22
-rw-r--r--modules/private/restore_repo.go2
-rw-r--r--modules/private/serv.go14
-rw-r--r--modules/process/manager_test.go8
-rw-r--r--modules/proxyprotocol/errors.go2
-rw-r--r--modules/public/public.go29
-rw-r--r--modules/public/public_bindata.go17
-rw-r--r--modules/public/public_dynamic.go (renamed from modules/public/serve_dynamic.go)0
-rw-r--r--modules/public/serve_static.go24
-rw-r--r--modules/queue/base_levelqueue_common.go2
-rw-r--r--modules/queue/base_levelqueue_test.go5
-rw-r--r--modules/queue/base_redis.go2
-rw-r--r--modules/queue/base_redis_test.go5
-rw-r--r--modules/queue/base_test.go26
-rw-r--r--modules/queue/manager.go5
-rw-r--r--modules/queue/manager_test.go13
-rw-r--r--modules/queue/workerqueue_test.go57
-rw-r--r--modules/references/references.go7
-rw-r--r--modules/references/references_test.go10
-rw-r--r--modules/regexplru/regexplru_test.go4
-rw-r--r--modules/repository/branch.go9
-rw-r--r--modules/repository/branch_test.go2
-rw-r--r--modules/repository/commits.go16
-rw-r--r--modules/repository/commits_test.go36
-rw-r--r--modules/repository/create.go103
-rw-r--r--modules/repository/create_test.go23
-rw-r--r--modules/repository/env.go8
-rw-r--r--modules/repository/init.go38
-rw-r--r--modules/repository/init_test.go6
-rw-r--r--modules/repository/repo.go133
-rw-r--r--modules/repository/repo_test.go4
-rw-r--r--modules/repository/temp.go33
-rw-r--r--modules/reqctx/datastore.go10
-rw-r--r--modules/secret/secret.go6
-rw-r--r--modules/session/key.go11
-rw-r--r--modules/setting/actions_test.go26
-rw-r--r--modules/setting/api.go2
-rw-r--r--modules/setting/attachment_test.go22
-rw-r--r--modules/setting/config_env.go2
-rw-r--r--modules/setting/config_env_test.go21
-rw-r--r--modules/setting/config_provider.go5
-rw-r--r--modules/setting/config_provider_test.go8
-rw-r--r--modules/setting/cron_test.go2
-rw-r--r--modules/setting/git_test.go24
-rw-r--r--modules/setting/global_lock_test.go6
-rw-r--r--modules/setting/incoming_email.go3
-rw-r--r--modules/setting/indexer.go2
-rw-r--r--modules/setting/lfs_test.go22
-rw-r--r--modules/setting/log.go7
-rw-r--r--modules/setting/mailer.go5
-rw-r--r--modules/setting/mailer_test.go4
-rw-r--r--modules/setting/markup.go90
-rw-r--r--modules/setting/markup_test.go51
-rw-r--r--modules/setting/mirror.go6
-rw-r--r--modules/setting/oauth2_test.go2
-rw-r--r--modules/setting/packages.go18
-rw-r--r--modules/setting/packages_test.go24
-rw-r--r--modules/setting/path.go16
-rw-r--r--modules/setting/repository.go33
-rw-r--r--modules/setting/repository_archive_test.go12
-rw-r--r--modules/setting/security.go12
-rw-r--r--modules/setting/server.go85
-rw-r--r--modules/setting/service.go35
-rw-r--r--modules/setting/service_test.go41
-rw-r--r--modules/setting/setting.go4
-rw-r--r--modules/setting/ssh.go35
-rw-r--r--modules/setting/storage.go18
-rw-r--r--modules/setting/storage_test.go126
-rw-r--r--modules/setting/ui.go2
-rw-r--r--modules/ssh/init.go12
-rw-r--r--modules/ssh/ssh.go5
-rw-r--r--modules/storage/azureblob_test.go16
-rw-r--r--modules/storage/local_test.go7
-rw-r--r--modules/storage/minio.go9
-rw-r--r--modules/storage/minio_test.go12
-rw-r--r--modules/storage/storage.go4
-rw-r--r--modules/storage/storage_test.go4
-rw-r--r--modules/structs/admin_user.go7
-rw-r--r--modules/structs/commit_status_test.go174
-rw-r--r--modules/structs/git_blob.go13
-rw-r--r--modules/structs/hook.go96
-rw-r--r--modules/structs/issue.go7
-rw-r--r--modules/structs/issue_tracked_time.go5
-rw-r--r--modules/structs/org.go11
-rw-r--r--modules/structs/package.go4
-rw-r--r--modules/structs/pull.go10
-rw-r--r--modules/structs/release.go1
-rw-r--r--modules/structs/repo.go6
-rw-r--r--modules/structs/repo_actions.go154
-rw-r--r--modules/structs/repo_branch.go9
-rw-r--r--modules/structs/repo_file.go90
-rw-r--r--modules/structs/repo_tag.go4
-rw-r--r--modules/structs/secret.go7
-rw-r--r--modules/structs/settings.go1
-rw-r--r--modules/structs/status.go38
-rw-r--r--modules/structs/user.go8
-rw-r--r--modules/structs/user_app.go16
-rw-r--r--modules/structs/user_email.go1
-rw-r--r--modules/structs/user_gpgkey.go4
-rw-r--r--modules/structs/user_key.go3
-rw-r--r--modules/structs/variable.go12
-rw-r--r--modules/system/appstate_test.go6
-rw-r--r--modules/tailmsg/talimsg.go73
-rw-r--r--modules/tempdir/tempdir.go112
-rw-r--r--modules/tempdir/tempdir_test.go75
-rw-r--r--modules/templates/eval/eval_test.go2
-rw-r--r--modules/templates/helper.go61
-rw-r--r--modules/templates/helper_test.go19
-rw-r--r--modules/templates/htmlrenderer.go6
-rw-r--r--modules/templates/htmlrenderer_test.go4
-rw-r--r--modules/templates/scopedtmpl/scopedtmpl.go38
-rw-r--r--modules/templates/static.go22
-rw-r--r--modules/templates/templates_bindata.go17
-rw-r--r--modules/templates/templates_dynamic.go (renamed from modules/templates/dynamic.go)0
-rw-r--r--modules/templates/util_avatar.go7
-rw-r--r--modules/templates/util_date.go2
-rw-r--r--modules/templates/util_date_legacy.go23
-rw-r--r--modules/templates/util_date_test.go16
-rw-r--r--modules/templates/util_dict.go3
-rw-r--r--modules/templates/util_format.go3
-rw-r--r--modules/templates/util_format_test.go2
-rw-r--r--modules/templates/util_json.go4
-rw-r--r--modules/templates/util_misc.go7
-rw-r--r--modules/templates/util_render.go85
-rw-r--r--modules/templates/util_render_legacy.go52
-rw-r--r--modules/templates/util_render_test.go158
-rw-r--r--modules/templates/util_test.go2
-rw-r--r--modules/templates/vars/vars.go2
-rw-r--r--modules/templates/vars/vars_test.go2
-rw-r--r--modules/test/logchecker.go4
-rw-r--r--modules/test/utils.go13
-rw-r--r--modules/testlogger/testlogger.go5
-rw-r--r--modules/timeutil/executable.go50
-rw-r--r--modules/translation/i18n/errors.go13
-rw-r--r--modules/translation/i18n/format.go3
-rw-r--r--modules/translation/i18n/localestore.go3
-rw-r--r--modules/translation/translation_test.go14
-rw-r--r--modules/typesniffer/typesniffer.go65
-rw-r--r--modules/typesniffer/typesniffer_test.go16
-rw-r--r--modules/updatechecker/update_checker.go2
-rw-r--r--modules/user/user_test.go22
-rw-r--r--modules/util/error.go68
-rw-r--r--modules/util/filebuffer/file_backed_buffer.go35
-rw-r--r--modules/util/filebuffer/file_backed_buffer_test.go3
-rw-r--r--modules/util/legacy_test.go2
-rw-r--r--modules/util/map.go13
-rw-r--r--modules/util/map_test.go26
-rw-r--r--modules/util/paginate_test.go14
-rw-r--r--modules/util/path.go5
-rw-r--r--modules/util/remove.go6
-rw-r--r--modules/util/rotatingfilewriter/writer_test.go2
-rw-r--r--modules/util/runtime_test.go4
-rw-r--r--modules/util/sec_to_time.go65
-rw-r--r--modules/util/sec_to_time_test.go24
-rw-r--r--modules/util/shellquote_test.go10
-rw-r--r--modules/util/slice.go7
-rw-r--r--modules/util/string.go23
-rw-r--r--modules/util/truncate_test.go11
-rw-r--r--modules/util/util.go18
-rw-r--r--modules/util/util_test.go17
-rw-r--r--modules/validation/binding_test.go2
-rw-r--r--modules/validation/glob_pattern_test.go56
-rw-r--r--modules/validation/helpers.go35
-rw-r--r--modules/validation/helpers_test.go5
-rw-r--r--modules/validation/refname_test.go419
-rw-r--r--modules/validation/regex_pattern_test.go56
-rw-r--r--modules/validation/validurl_test.go158
-rw-r--r--modules/validation/validurllist_test.go238
-rw-r--r--modules/web/handler.go4
-rw-r--r--modules/web/middleware/binding.go4
-rw-r--r--modules/web/middleware/flash.go25
-rw-r--r--modules/web/middleware/request.go14
-rw-r--r--modules/web/routemock_test.go22
-rw-r--r--modules/web/router.go4
-rw-r--r--modules/web/router_path.go58
-rw-r--r--modules/web/router_test.go105
-rw-r--r--modules/web/routing/context.go25
-rw-r--r--modules/web/routing/funcinfo_test.go12
-rw-r--r--modules/web/routing/logger.go32
-rw-r--r--modules/webhook/events.go20
-rw-r--r--modules/webhook/structs.go39
-rw-r--r--modules/webhook/type.go61
-rw-r--r--modules/zstd/zstd_test.go8
-rw-r--r--options/fileicon/material-icon-rules.json10007
-rw-r--r--options/fileicon/material-icon-svgs.json1122
-rw-r--r--options/gitignore/Flutter119
-rw-r--r--options/gitignore/Nix3
-rw-r--r--options/gitignore/Node6
-rw-r--r--options/gitignore/NotesAndCoreConfiguration16
-rw-r--r--options/gitignore/NotesAndExtendedConfiguration38
-rw-r--r--options/gitignore/NotesOnly4
-rw-r--r--options/gitignore/Processing1
-rw-r--r--options/gitignore/Python3
-rw-r--r--options/gitignore/Rust4
-rw-r--r--options/label/Advanced.yaml11
-rw-r--r--options/license/389-exception7
-rw-r--r--options/license/3D-Slicer-1.0190
-rw-r--r--options/license/AAL23
-rw-r--r--options/license/ADSL1
-rw-r--r--options/license/AFL-1.127
-rw-r--r--options/license/AFL-1.228
-rw-r--r--options/license/AFL-2.045
-rw-r--r--options/license/AFL-2.145
-rw-r--r--options/license/AFL-3.043
-rw-r--r--options/license/AGPL-1.0-only86
-rw-r--r--options/license/AGPL-1.0-or-later86
-rw-r--r--options/license/AGPL-3.0 (renamed from options/license/AGPL-3.0-only)0
-rw-r--r--options/license/AGPL-3.0-or-later235
-rw-r--r--options/license/AMD-newlib11
-rw-r--r--options/license/AMDPLPA20
-rw-r--r--options/license/AML9
-rw-r--r--options/license/AML-glslang41
-rw-r--r--options/license/AMPAS13
-rw-r--r--options/license/ANTLR-PD5
-rw-r--r--options/license/ANTLR-PD-fallback7
-rw-r--r--options/license/APAFML3
-rw-r--r--options/license/APL-1.0295
-rw-r--r--options/license/APSL-1.0109
-rw-r--r--options/license/APSL-1.1108
-rw-r--r--options/license/APSL-1.2103
-rw-r--r--options/license/APSL-2.0102
-rw-r--r--options/license/ASWF-Digital-Assets-1.017
-rw-r--r--options/license/ASWF-Digital-Assets-1.117
-rw-r--r--options/license/Abstyles11
-rw-r--r--options/license/AdaCore-doc1
-rw-r--r--options/license/Adobe-200612
-rw-r--r--options/license/Adobe-Display-PostScript30
-rw-r--r--options/license/Adobe-Glyph10
-rw-r--r--options/license/Adobe-Utopia12
-rw-r--r--options/license/Afmparse10
-rw-r--r--options/license/Aladdin62
-rw-r--r--options/license/Apache-1.020
-rw-r--r--options/license/Apache-1.121
-rw-r--r--options/license/App-s2p5
-rw-r--r--options/license/Arphic-199958
-rw-r--r--options/license/Artistic-1.049
-rw-r--r--options/license/Artistic-1.0-Perl51
-rw-r--r--options/license/Artistic-1.0-cl851
-rw-r--r--options/license/Artistic-2.085
-rw-r--r--options/license/Asterisk-exception5
-rw-r--r--options/license/Asterisk-linking-protocols-exception13
-rw-r--r--options/license/Autoconf-exception-2.05
-rw-r--r--options/license/Autoconf-exception-3.026
-rw-r--r--options/license/Autoconf-exception-generic4
-rw-r--r--options/license/Autoconf-exception-generic-3.06
-rw-r--r--options/license/Autoconf-exception-macro12
-rw-r--r--options/license/BSD-1-Clause7
-rw-r--r--options/license/BSD-2-Clause-Darwin28
-rw-r--r--options/license/BSD-2-Clause-Patent19
-rw-r--r--options/license/BSD-2-Clause-Views11
-rw-r--r--options/license/BSD-2-Clause-first-lines27
-rw-r--r--options/license/BSD-3-Clause-Attribution11
-rw-r--r--options/license/BSD-3-Clause-HP23
-rw-r--r--options/license/BSD-3-Clause-LBNL12
-rw-r--r--options/license/BSD-3-Clause-Modification35
-rw-r--r--options/license/BSD-3-Clause-No-Military-License16
-rw-r--r--options/license/BSD-3-Clause-No-Nuclear-License14
-rw-r--r--options/license/BSD-3-Clause-No-Nuclear-License-201416
-rw-r--r--options/license/BSD-3-Clause-No-Nuclear-Warranty14
-rw-r--r--options/license/BSD-3-Clause-Open-MPI34
-rw-r--r--options/license/BSD-3-Clause-Sun29
-rw-r--r--options/license/BSD-3-Clause-acpica26
-rw-r--r--options/license/BSD-3-Clause-flex42
-rw-r--r--options/license/BSD-4-Clause14
-rw-r--r--options/license/BSD-4-Clause-Shortened13
-rw-r--r--options/license/BSD-4-Clause-UC15
-rw-r--r--options/license/BSD-4.3RENO9
-rw-r--r--options/license/BSD-4.3TAHOE11
-rw-r--r--options/license/BSD-Advertising-Acknowledgement37
-rw-r--r--options/license/BSD-Attribution-HPND-disclaimer37
-rw-r--r--options/license/BSD-Inferno-Nettverk41
-rw-r--r--options/license/BSD-Protection53
-rw-r--r--options/license/BSD-Source-Code10
-rw-r--r--options/license/BSD-Source-beginning-file23
-rw-r--r--options/license/BSD-Systemics39
-rw-r--r--options/license/BSD-Systemics-W3Works62
-rw-r--r--options/license/BUSL-1.171
-rw-r--r--options/license/Baekmuk9
-rw-r--r--options/license/Bahyph11
-rw-r--r--options/license/Barr1
-rw-r--r--options/license/Beerware1
-rw-r--r--options/license/Bison-exception-1.244
-rw-r--r--options/license/Bison-exception-2.25
-rw-r--r--options/license/BitTorrent-1.0330
-rw-r--r--options/license/BitTorrent-1.1137
-rw-r--r--options/license/Bitstream-Charter9
-rw-r--r--options/license/Bitstream-Vera15
-rw-r--r--options/license/BlueOak-1.0.055
-rw-r--r--options/license/Boehm-GC12
-rw-r--r--options/license/Boehm-GC-without-fee14
-rw-r--r--options/license/Bootloader-exception10
-rw-r--r--options/license/Borceux19
-rw-r--r--options/license/Brian-Gladman-2-Clause17
-rw-r--r--options/license/Brian-Gladman-3-Clause26
-rw-r--r--options/license/C-UDA-1.047
-rw-r--r--options/license/CAL-1.0354
-rw-r--r--options/license/CAL-1.0-Combined-Work-Exception354
-rw-r--r--options/license/CATOSL-1.1114
-rw-r--r--options/license/CC-BY-1.080
-rw-r--r--options/license/CC-BY-2.081
-rw-r--r--options/license/CC-BY-2.581
-rw-r--r--options/license/CC-BY-2.5-AU112
-rw-r--r--options/license/CC-BY-3.0319
-rw-r--r--options/license/CC-BY-3.0-AT111
-rw-r--r--options/license/CC-BY-3.0-AU136
-rw-r--r--options/license/CC-BY-3.0-DE108
-rw-r--r--options/license/CC-BY-3.0-IGO101
-rw-r--r--options/license/CC-BY-3.0-NL97
-rw-r--r--options/license/CC-BY-3.0-US83
-rw-r--r--options/license/CC-BY-NC-1.075
-rw-r--r--options/license/CC-BY-NC-2.081
-rw-r--r--options/license/CC-BY-NC-2.581
-rw-r--r--options/license/CC-BY-NC-3.0334
-rw-r--r--options/license/CC-BY-NC-3.0-DE110
-rw-r--r--options/license/CC-BY-NC-4.0158
-rw-r--r--options/license/CC-BY-NC-ND-1.075
-rw-r--r--options/license/CC-BY-NC-ND-2.077
-rw-r--r--options/license/CC-BY-NC-ND-2.577
-rw-r--r--options/license/CC-BY-NC-ND-3.0308
-rw-r--r--options/license/CC-BY-NC-ND-3.0-DE101
-rw-r--r--options/license/CC-BY-NC-ND-3.0-IGO99
-rw-r--r--options/license/CC-BY-NC-ND-4.0155
-rw-r--r--options/license/CC-BY-NC-SA-1.083
-rw-r--r--options/license/CC-BY-NC-SA-2.087
-rw-r--r--options/license/CC-BY-NC-SA-2.0-DE85
-rw-r--r--options/license/CC-BY-NC-SA-2.0-FR93
-rw-r--r--options/license/CC-BY-NC-SA-2.0-UK149
-rw-r--r--options/license/CC-BY-NC-SA-2.587
-rw-r--r--options/license/CC-BY-NC-SA-3.0360
-rw-r--r--options/license/CC-BY-NC-SA-3.0-DE125
-rw-r--r--options/license/CC-BY-NC-SA-3.0-IGO105
-rw-r--r--options/license/CC-BY-NC-SA-4.0170
-rw-r--r--options/license/CC-BY-ND-1.073
-rw-r--r--options/license/CC-BY-ND-2.075
-rw-r--r--options/license/CC-BY-ND-2.575
-rw-r--r--options/license/CC-BY-ND-3.0293
-rw-r--r--options/license/CC-BY-ND-3.0-DE100
-rw-r--r--options/license/CC-BY-ND-4.0154
-rw-r--r--options/license/CC-BY-SA-1.081
-rw-r--r--options/license/CC-BY-SA-2.085
-rw-r--r--options/license/CC-BY-SA-2.0-UK147
-rw-r--r--options/license/CC-BY-SA-2.1-JP83
-rw-r--r--options/license/CC-BY-SA-2.585
-rw-r--r--options/license/CC-BY-SA-3.0359
-rw-r--r--options/license/CC-BY-SA-3.0-AT139
-rw-r--r--options/license/CC-BY-SA-3.0-DE135
-rw-r--r--options/license/CC-BY-SA-3.0-IGO107
-rw-r--r--options/license/CC-PDDC8
-rw-r--r--options/license/CC-PDM-1.027
-rw-r--r--options/license/CC-SA-1.0198
-rw-r--r--options/license/CDDL-1.0119
-rw-r--r--options/license/CDDL-1.1123
-rw-r--r--options/license/CDL-1.053
-rw-r--r--options/license/CDLA-Permissive-1.085
-rw-r--r--options/license/CDLA-Permissive-2.035
-rw-r--r--options/license/CDLA-Sharing-1.089
-rw-r--r--options/license/CECILL-1.0216
-rw-r--r--options/license/CECILL-1.1229
-rw-r--r--options/license/CECILL-2.0506
-rw-r--r--options/license/CECILL-2.1518
-rw-r--r--options/license/CECILL-B515
-rw-r--r--options/license/CECILL-C517
-rw-r--r--options/license/CERN-OHL-1.147
-rw-r--r--options/license/CERN-OHL-1.248
-rw-r--r--options/license/CERN-OHL-P-2.0199
-rw-r--r--options/license/CERN-OHL-S-2.0289
-rw-r--r--options/license/CERN-OHL-W-2.0310
-rw-r--r--options/license/CFITSIO7
-rw-r--r--options/license/CGAL-linking-exception4
-rw-r--r--options/license/CLISP-exception-2.015
-rw-r--r--options/license/CMU-Mach22
-rw-r--r--options/license/CMU-Mach-nodoc11
-rw-r--r--options/license/CNRI-Jython12
-rw-r--r--options/license/CNRI-Python25
-rw-r--r--options/license/CNRI-Python-GPL-Compatible23
-rw-r--r--options/license/COIL-1.030
-rw-r--r--options/license/CPAL-1.0172
-rw-r--r--options/license/CPL-1.087
-rw-r--r--options/license/CPOL-1.0298
-rw-r--r--options/license/CUA-OPL-1.0143
-rw-r--r--options/license/Caldera25
-rw-r--r--options/license/Caldera-no-preamble35
-rw-r--r--options/license/Catharon121
-rw-r--r--options/license/ClArtistic61
-rw-r--r--options/license/Classpath-exception-2.03
-rw-r--r--options/license/Clips15
-rw-r--r--options/license/Community-Spec-1.0293
-rw-r--r--options/license/Condor-1.138
-rw-r--r--options/license/Cornell-Lossless-JPEG20
-rw-r--r--options/license/Cronyx11
-rw-r--r--options/license/Crossword5
-rw-r--r--options/license/CrystalStacker7
-rw-r--r--options/license/Cube17
-rw-r--r--options/license/D-FSL-1.0147
-rw-r--r--options/license/DEC-3-Clause28
-rw-r--r--options/license/DL-DE-BY-2.045
-rw-r--r--options/license/DL-DE-ZERO-2.025
-rw-r--r--options/license/DOC15
-rw-r--r--options/license/DRL-1.012
-rw-r--r--options/license/DRL-1.117
-rw-r--r--options/license/DSDP18
-rw-r--r--options/license/DigiRule-FOSS-exception54
-rw-r--r--options/license/DocBook-Schema22
-rw-r--r--options/license/DocBook-Stylesheet13
-rw-r--r--options/license/DocBook-XML48
-rw-r--r--options/license/Dotseqn5
-rw-r--r--options/license/ECL-1.023
-rw-r--r--options/license/ECL-2.098
-rw-r--r--options/license/EFL-1.013
-rw-r--r--options/license/EFL-2.09
-rw-r--r--options/license/EPICS32
-rw-r--r--options/license/EUDatagrid24
-rw-r--r--options/license/EUPL-1.0154
-rw-r--r--options/license/EUPL-1.1157
-rw-r--r--options/license/Elastic-2.093
-rw-r--r--options/license/Entessa22
-rw-r--r--options/license/ErlPL-1.193
-rw-r--r--options/license/Eurosym18
-rw-r--r--options/license/FBM6
-rw-r--r--options/license/FDK-AAC79
-rw-r--r--options/license/FLTK-exception17
-rw-r--r--options/license/FSFAP1
-rw-r--r--options/license/FSFAP-no-warranty-disclaimer5
-rw-r--r--options/license/FSFUL3
-rw-r--r--options/license/FSFULLR3
-rw-r--r--options/license/FSFULLRWD11
-rw-r--r--options/license/FTL79
-rw-r--r--options/license/Fair7
-rw-r--r--options/license/Fawkes-Runtime-exception1
-rw-r--r--options/license/Ferguson-Twofish15
-rw-r--r--options/license/Font-exception-2.01
-rw-r--r--options/license/Frameworx-1.069
-rw-r--r--options/license/FreeBSD-DOC23
-rw-r--r--options/license/FreeImage117
-rw-r--r--options/license/Furuseth13
-rw-r--r--options/license/GCC-exception-2.01
-rw-r--r--options/license/GCC-exception-2.0-note16
-rw-r--r--options/license/GCC-exception-3.133
-rw-r--r--options/license/GCR-docs30
-rw-r--r--options/license/GD24
-rw-r--r--options/license/GFDL-1.1-invariants-only119
-rw-r--r--options/license/GFDL-1.1-invariants-or-later119
-rw-r--r--options/license/GFDL-1.1-no-invariants-only119
-rw-r--r--options/license/GFDL-1.1-no-invariants-or-later119
-rw-r--r--options/license/GFDL-1.1-only119
-rw-r--r--options/license/GFDL-1.1-or-later119
-rw-r--r--options/license/GFDL-1.2-invariants-only130
-rw-r--r--options/license/GFDL-1.2-invariants-or-later130
-rw-r--r--options/license/GFDL-1.2-no-invariants-only130
-rw-r--r--options/license/GFDL-1.2-no-invariants-or-later130
-rw-r--r--options/license/GFDL-1.2-only130
-rw-r--r--options/license/GFDL-1.2-or-later130
-rw-r--r--options/license/GFDL-1.3-invariants-only149
-rw-r--r--options/license/GFDL-1.3-invariants-or-later149
-rw-r--r--options/license/GFDL-1.3-no-invariants-only149
-rw-r--r--options/license/GFDL-1.3-no-invariants-or-later149
-rw-r--r--options/license/GFDL-1.3-only149
-rw-r--r--options/license/GFDL-1.3-or-later149
-rw-r--r--options/license/GL2PS13
-rw-r--r--options/license/GLWTPL25
-rw-r--r--options/license/GNAT-exception6
-rw-r--r--options/license/GNOME-examples-exception1
-rw-r--r--options/license/GNU-compiler-exception6
-rw-r--r--options/license/GPL-1.0-only100
-rw-r--r--options/license/GPL-1.0-or-later100
-rw-r--r--options/license/GPL-2.0 (renamed from options/license/GPL-2.0-only)0
-rw-r--r--options/license/GPL-2.0-or-later117
-rw-r--r--options/license/GPL-3.0 (renamed from options/license/GPL-3.0-only)0
-rw-r--r--options/license/GPL-3.0-389-ds-base-exception10
-rw-r--r--options/license/GPL-3.0-interface-exception7
-rw-r--r--options/license/GPL-3.0-linking-exception3
-rw-r--r--options/license/GPL-3.0-linking-source-exception3
-rw-r--r--options/license/GPL-3.0-or-later232
-rw-r--r--options/license/GPL-CC-1.046
-rw-r--r--options/license/GStreamer-exception-20051
-rw-r--r--options/license/GStreamer-exception-20081
-rw-r--r--options/license/Giftware9
-rw-r--r--options/license/Glide95
-rw-r--r--options/license/Glulxe3
-rw-r--r--options/license/Gmsh-exception16
-rw-r--r--options/license/Graphics-Gems5
-rw-r--r--options/license/Gutmann2
-rw-r--r--options/license/HIDAPI2
-rw-r--r--options/license/HP-198610
-rw-r--r--options/license/HP-198916
-rw-r--r--options/license/HPND7
-rw-r--r--options/license/HPND-DEC22
-rw-r--r--options/license/HPND-Fenneberg-Livingston13
-rw-r--r--options/license/HPND-INRIA-IMAG9
-rw-r--r--options/license/HPND-Intel25
-rw-r--r--options/license/HPND-Kevlin-Henney10
-rw-r--r--options/license/HPND-MIT-disclaimer18
-rw-r--r--options/license/HPND-Markus-Kuhn3
-rw-r--r--options/license/HPND-Netrek10
-rw-r--r--options/license/HPND-Pbmplus8
-rw-r--r--options/license/HPND-UC8
-rw-r--r--options/license/HPND-UC-export-US10
-rw-r--r--options/license/HPND-doc8
-rw-r--r--options/license/HPND-doc-sell9
-rw-r--r--options/license/HPND-export-US5
-rw-r--r--options/license/HPND-export-US-acknowledgement22
-rw-r--r--options/license/HPND-export-US-modify24
-rw-r--r--options/license/HPND-export2-US21
-rw-r--r--options/license/HPND-merchantability-variant9
-rw-r--r--options/license/HPND-sell-MIT-disclaimer-xserver12
-rw-r--r--options/license/HPND-sell-regexpr9
-rw-r--r--options/license/HPND-sell-variant19
-rw-r--r--options/license/HPND-sell-variant-MIT-disclaimer20
-rw-r--r--options/license/HPND-sell-variant-MIT-disclaimer-rev15
-rw-r--r--options/license/HTMLTIDY13
-rw-r--r--options/license/HaskellReport6
-rw-r--r--options/license/Hippocratic-2.133
-rw-r--r--options/license/IBM-pibs8
-rw-r--r--options/license/ICU12
-rw-r--r--options/license/IEC-Code-Components-EULA37
-rw-r--r--options/license/IJG38
-rw-r--r--options/license/IJG-short35
-rw-r--r--options/license/IPA83
-rw-r--r--options/license/IPL-1.0215
-rw-r--r--options/license/ISC-Veillard9
-rw-r--r--options/license/ImageMagick98
-rw-r--r--options/license/Imlib29
-rw-r--r--options/license/Independent-modules-exception18
-rw-r--r--options/license/Info-ZIP16
-rw-r--r--options/license/Inner-Net-2.034
-rw-r--r--options/license/InnoSetup27
-rw-r--r--options/license/Intel13
-rw-r--r--options/license/Intel-ACPI34
-rw-r--r--options/license/Interbase-1.0199
-rw-r--r--options/license/JPL-image21
-rw-r--r--options/license/JPNIC40
-rw-r--r--options/license/JSON11
-rw-r--r--options/license/Jam5
-rw-r--r--options/license/JasPer-2.017
-rw-r--r--options/license/Kastrup3
-rw-r--r--options/license/Kazlib4
-rw-r--r--options/license/KiCad-libraries-exception1
-rw-r--r--options/license/Knuth-CTAN5
-rw-r--r--options/license/LAL-1.267
-rw-r--r--options/license/LAL-1.388
-rw-r--r--options/license/LGPL-2.0-only175
-rw-r--r--options/license/LGPL-2.0-or-later175
-rw-r--r--options/license/LGPL-2.1 (renamed from options/license/LGPL-2.1-only)0
-rw-r--r--options/license/LGPL-2.1-or-later176
-rw-r--r--options/license/LGPL-3.0 (renamed from options/license/LGPL-3.0-only)0
-rw-r--r--options/license/LGPL-3.0-linking-exception16
-rw-r--r--options/license/LGPL-3.0-or-later304
-rw-r--r--options/license/LGPLLR89
-rw-r--r--options/license/LLGPL56
-rw-r--r--options/license/LLVM-exception15
-rw-r--r--options/license/LOOP44
-rw-r--r--options/license/LPD-document8
-rw-r--r--options/license/LPL-1.081
-rw-r--r--options/license/LPL-1.0285
-rw-r--r--options/license/LPPL-1.0103
-rw-r--r--options/license/LPPL-1.1141
-rw-r--r--options/license/LPPL-1.2139
-rw-r--r--options/license/LPPL-1.3a175
-rw-r--r--options/license/LPPL-1.3c184
-rw-r--r--options/license/LZMA-SDK-9.11-to-9.208
-rw-r--r--options/license/LZMA-SDK-9.2215
-rw-r--r--options/license/LZMA-exception3
-rw-r--r--options/license/Latex2e9
-rw-r--r--options/license/Latex2e-translated-notice26
-rw-r--r--options/license/Leptonica9
-rw-r--r--options/license/LiLiQ-P-1.170
-rw-r--r--options/license/LiLiQ-R-1.194
-rw-r--r--options/license/LiLiQ-Rplus-1.188
-rw-r--r--options/license/Libpng76
-rw-r--r--options/license/Libtool-exception1
-rw-r--r--options/license/Linux-OpenIB18
-rw-r--r--options/license/Linux-man-pages-1-para4
-rw-r--r--options/license/Linux-man-pages-copyleft21
-rw-r--r--options/license/Linux-man-pages-copyleft-2-para8
-rw-r--r--options/license/Linux-man-pages-copyleft-var16
-rw-r--r--options/license/Linux-syscall-note12
-rw-r--r--options/license/Lucida-Bitmap-Fonts53
-rw-r--r--options/license/MIPS4
-rw-r--r--options/license/MIT-CMU7
-rw-r--r--options/license/MIT-Click30
-rw-r--r--options/license/MIT-Festival22
-rw-r--r--options/license/MIT-Khronos-old23
-rw-r--r--options/license/MIT-Modern-Variant17
-rw-r--r--options/license/MIT-Wu28
-rw-r--r--options/license/MIT-advertising7
-rw-r--r--options/license/MIT-enna9
-rw-r--r--options/license/MIT-feh5
-rw-r--r--options/license/MIT-open-group23
-rw-r--r--options/license/MIT-testregex17
-rw-r--r--options/license/MITNFA7
-rw-r--r--options/license/MMIXware17
-rw-r--r--options/license/MPEG-SSG23
-rw-r--r--options/license/MPL-1.0123
-rw-r--r--options/license/MPL-1.1143
-rw-r--r--options/license/MPL-2.0-no-copyleft-exception373
-rw-r--r--options/license/MS-LPL24
-rw-r--r--options/license/MS-PL22
-rw-r--r--options/license/MS-RL30
-rw-r--r--options/license/MTLL24
-rw-r--r--options/license/Mackerras-3-Clause25
-rw-r--r--options/license/Mackerras-3-Clause-acknowledgment25
-rw-r--r--options/license/MakeIndex19
-rw-r--r--options/license/Martin-Birgmeier5
-rw-r--r--options/license/McPhee-slideshow6
-rw-r--r--options/license/Minpack51
-rw-r--r--options/license/MirOS7
-rw-r--r--options/license/Motosoto372
-rw-r--r--options/license/MulanPSL-1.0116
-rw-r--r--options/license/Multics13
-rw-r--r--options/license/Mup13
-rw-r--r--options/license/NAIST-200370
-rw-r--r--options/license/NASA-1.385
-rw-r--r--options/license/NBPL-1.059
-rw-r--r--options/license/NCBI-PD19
-rw-r--r--options/license/NCGL-UK-2.067
-rw-r--r--options/license/NCL32
-rw-r--r--options/license/NCSA15
-rw-r--r--options/license/NGPL21
-rw-r--r--options/license/NICTA-1.061
-rw-r--r--options/license/NIST-PD15
-rw-r--r--options/license/NIST-PD-fallback5
-rw-r--r--options/license/NIST-Software28
-rw-r--r--options/license/NLOD-1.079
-rw-r--r--options/license/NLOD-2.080
-rw-r--r--options/license/NLPL14
-rw-r--r--options/license/NOSL150
-rw-r--r--options/license/NPL-1.0102
-rw-r--r--options/license/NPL-1.1186
-rw-r--r--options/license/NPOSL-3.059
-rw-r--r--options/license/NRL28
-rw-r--r--options/license/NTP5
-rw-r--r--options/license/NTP-05
-rw-r--r--options/license/Naumen21
-rw-r--r--options/license/NetCDF7
-rw-r--r--options/license/Newsletr7
-rw-r--r--options/license/Nokia159
-rw-r--r--options/license/Nokia-Qt-exception-1.116
-rw-r--r--options/license/Noweb9
-rw-r--r--options/license/O-UDA-1.047
-rw-r--r--options/license/OAR12
-rw-r--r--options/license/OCCT-PL112
-rw-r--r--options/license/OCCT-exception-1.03
-rw-r--r--options/license/OCLC-2.076
-rw-r--r--options/license/OCaml-LGPL-linking-exception1
-rw-r--r--options/license/ODC-By-1.0195
-rw-r--r--options/license/ODbL-1.0540
-rw-r--r--options/license/OFFIS22
-rw-r--r--options/license/OFL-1.049
-rw-r--r--options/license/OFL-1.0-RFN49
-rw-r--r--options/license/OFL-1.0-no-RFN49
-rw-r--r--options/license/OFL-1.1-RFN43
-rw-r--r--options/license/OFL-1.1-no-RFN43
-rw-r--r--options/license/OGC-1.017
-rw-r--r--options/license/OGDL-Taiwan-1.0141
-rw-r--r--options/license/OGL-Canada-2.051
-rw-r--r--options/license/OGL-UK-1.069
-rw-r--r--options/license/OGL-UK-2.072
-rw-r--r--options/license/OGL-UK-3.069
-rw-r--r--options/license/OGTSL55
-rw-r--r--options/license/OLDAP-1.160
-rw-r--r--options/license/OLDAP-1.260
-rw-r--r--options/license/OLDAP-1.362
-rw-r--r--options/license/OLDAP-1.462
-rw-r--r--options/license/OLDAP-2.018
-rw-r--r--options/license/OLDAP-2.0.118
-rw-r--r--options/license/OLDAP-2.120
-rw-r--r--options/license/OLDAP-2.222
-rw-r--r--options/license/OLDAP-2.2.122
-rw-r--r--options/license/OLDAP-2.2.224
-rw-r--r--options/license/OLDAP-2.324
-rw-r--r--options/license/OLDAP-2.422
-rw-r--r--options/license/OLDAP-2.522
-rw-r--r--options/license/OLDAP-2.620
-rw-r--r--options/license/OLDAP-2.720
-rw-r--r--options/license/OLDAP-2.820
-rw-r--r--options/license/OLFL-1.3220
-rw-r--r--options/license/OML5
-rw-r--r--options/license/OPL-1.0136
-rw-r--r--options/license/OPL-UK-3.0114
-rw-r--r--options/license/OPUBL-1.078
-rw-r--r--options/license/OSET-PL-2.1162
-rw-r--r--options/license/OSL-1.045
-rw-r--r--options/license/OSL-1.147
-rw-r--r--options/license/OSL-2.047
-rw-r--r--options/license/OSL-2.147
-rw-r--r--options/license/OpenJDK-assembly-exception-1.031
-rw-r--r--options/license/OpenPBS-2.376
-rw-r--r--options/license/OpenSSL48
-rw-r--r--options/license/OpenSSL-standalone50
-rw-r--r--options/license/OpenVision33
-rw-r--r--options/license/PADL6
-rw-r--r--options/license/PCRE2-exception8
-rw-r--r--options/license/PDDL-1.0136
-rw-r--r--options/license/PHP-3.028
-rw-r--r--options/license/PHP-3.0127
-rw-r--r--options/license/PPL96
-rw-r--r--options/license/PS-or-PDF-font-exception-201708178
-rw-r--r--options/license/PSF-2.047
-rw-r--r--options/license/Parity-6.0.044
-rw-r--r--options/license/Parity-7.0.071
-rw-r--r--options/license/Pixar174
-rw-r--r--options/license/Plexus15
-rw-r--r--options/license/PolyForm-Noncommercial-1.0.0131
-rw-r--r--options/license/PolyForm-Small-Business-1.0.0121
-rw-r--r--options/license/PostgreSQL12
-rw-r--r--options/license/Python-2.072
-rw-r--r--options/license/Python-2.0.1193
-rw-r--r--options/license/QPL-1.050
-rw-r--r--options/license/QPL-1.0-INRIA-2004102
-rw-r--r--options/license/QPL-1.0-INRIA-2004-exception5
-rw-r--r--options/license/Qhull17
-rw-r--r--options/license/Qt-GPL-exception-1.021
-rw-r--r--options/license/Qt-LGPL-exception-1.122
-rw-r--r--options/license/Qwt-exception-1.012
-rw-r--r--options/license/RHeCos-1.1137
-rw-r--r--options/license/RPL-1.1177
-rw-r--r--options/license/RPL-1.5167
-rw-r--r--options/license/RPSL-1.0179
-rw-r--r--options/license/RRDtool-FLOSS-exception-2.066
-rw-r--r--options/license/RSA-MD9
-rw-r--r--options/license/RSCPL128
-rw-r--r--options/license/Rdisc13
-rw-r--r--options/license/Ruby29
-rw-r--r--options/license/Ruby-pty10
-rw-r--r--options/license/SANE-exception20
-rw-r--r--options/license/SAX-PD31
-rw-r--r--options/license/SAX-PD-2.010
-rw-r--r--options/license/SCEA60
-rw-r--r--options/license/SGI-B-1.082
-rw-r--r--options/license/SGI-B-1.184
-rw-r--r--options/license/SGI-B-2.012
-rw-r--r--options/license/SGI-OpenGL34
-rw-r--r--options/license/SGP41
-rw-r--r--options/license/SHL-0.564
-rw-r--r--options/license/SHL-0.5165
-rw-r--r--options/license/SHL-2.022
-rw-r--r--options/license/SHL-2.145
-rw-r--r--options/license/SISSL116
-rw-r--r--options/license/SISSL-1.2113
-rw-r--r--options/license/SL4
-rw-r--r--options/license/SMAIL-GPL144
-rw-r--r--options/license/SMLNJ7
-rw-r--r--options/license/SMPPL29
-rw-r--r--options/license/SNIA122
-rw-r--r--options/license/SPL-1.0149
-rw-r--r--options/license/SSH-OpenSSH67
-rw-r--r--options/license/SSH-short5
-rw-r--r--options/license/SSLeay-standalone58
-rw-r--r--options/license/SSPL-1.0557
-rw-r--r--options/license/SWI-exception6
-rw-r--r--options/license/SWL7
-rw-r--r--options/license/Saxpath19
-rw-r--r--options/license/SchemeReport3
-rw-r--r--options/license/Sendmail36
-rw-r--r--options/license/Sendmail-8.2336
-rw-r--r--options/license/Sendmail-Open-Source-1.175
-rw-r--r--options/license/SimPL-2.037
-rw-r--r--options/license/Sleepycat37
-rw-r--r--options/license/Soundex9
-rw-r--r--options/license/Spencer-869
-rw-r--r--options/license/Spencer-9412
-rw-r--r--options/license/Spencer-999
-rw-r--r--options/license/SugarCRM-1.1.3149
-rw-r--r--options/license/Sun-PPP13
-rw-r--r--options/license/Sun-PPP-200013
-rw-r--r--options/license/SunPro6
-rw-r--r--options/license/Swift-exception6
-rw-r--r--options/license/Symlinks10
-rw-r--r--options/license/TAPR-OHL-1.0266
-rw-r--r--options/license/TCL9
-rw-r--r--options/license/TCP-wrappers7
-rw-r--r--options/license/TGPPL-1.0181
-rw-r--r--options/license/TMate21
-rw-r--r--options/license/TORQUE-1.125
-rw-r--r--options/license/TOSL9
-rw-r--r--options/license/TPDL2
-rw-r--r--options/license/TPL-1.0475
-rw-r--r--options/license/TTWL8
-rw-r--r--options/license/TTYP029
-rw-r--r--options/license/TU-Berlin-1.010
-rw-r--r--options/license/TU-Berlin-2.020
-rw-r--r--options/license/TermReadKey1
-rw-r--r--options/license/Texinfo-exception4
-rw-r--r--options/license/ThirdEye7
-rw-r--r--options/license/TrustedQSL58
-rw-r--r--options/license/UBDL-exception59
-rw-r--r--options/license/UCAR32
-rw-r--r--options/license/UCL-1.048
-rw-r--r--options/license/UMich-Merit19
-rw-r--r--options/license/URT-RLE15
-rw-r--r--options/license/Ubuntu-font-1.096
-rw-r--r--options/license/Unicode-3.039
-rw-r--r--options/license/Unicode-DFS-201519
-rw-r--r--options/license/Unicode-DFS-201622
-rw-r--r--options/license/Unicode-TOU51
-rw-r--r--options/license/Universal-FOSS-exception-1.011
-rw-r--r--options/license/UnixCrypt6
-rw-r--r--options/license/VOSTROM27
-rw-r--r--options/license/VSL-1.018
-rw-r--r--options/license/Vim30
-rw-r--r--options/license/W3C29
-rw-r--r--options/license/W3C-1998072023
-rw-r--r--options/license/W3C-2015051317
-rw-r--r--options/license/Watcom-1.0106
-rw-r--r--options/license/Widget-Workshop19
-rw-r--r--options/license/Wsuipa5
-rw-r--r--options/license/WxWindows-exception-3.19
-rw-r--r--options/license/X1113
-rw-r--r--options/license/X11-distribute-modifications-variant25
-rw-r--r--options/license/X11-swapped23
-rw-r--r--options/license/XFree86-1.116
-rw-r--r--options/license/XSkat10
-rw-r--r--options/license/Xdebug-1.0360
-rw-r--r--options/license/Xerox5
-rw-r--r--options/license/Xfig7
-rw-r--r--options/license/Xnet11
-rw-r--r--options/license/YPL-1.047
-rw-r--r--options/license/YPL-1.147
-rw-r--r--options/license/ZPL-1.133
-rw-r--r--options/license/ZPL-2.023
-rw-r--r--options/license/ZPL-2.121
-rw-r--r--options/license/Zed3
-rw-r--r--options/license/Zeeff3
-rw-r--r--options/license/Zend-2.018
-rw-r--r--options/license/Zimbra-1.347
-rw-r--r--options/license/Zimbra-1.447
-rw-r--r--options/license/any-OSI3
-rw-r--r--options/license/any-OSI-perl-modules11
-rw-r--r--options/license/bcrypt-Solar-Designer11
-rw-r--r--options/license/blessing5
-rw-r--r--options/license/bzip2-1.0.615
-rw-r--r--options/license/check-cvs2
-rw-r--r--options/license/checkmk9
-rw-r--r--options/license/copyleft-next-0.3.0219
-rw-r--r--options/license/copyleft-next-0.3.1220
-rw-r--r--options/license/cryptsetup-OpenSSL-exception12
-rw-r--r--options/license/curl10
-rw-r--r--options/license/cve-tou16
-rw-r--r--options/license/diffmark2
-rw-r--r--options/license/dtoa14
-rw-r--r--options/license/dvipdfm1
-rw-r--r--options/license/eCos-exception-2.03
-rw-r--r--options/license/eGenix40
-rw-r--r--options/license/erlang-otp-linking-exception11
-rw-r--r--options/license/etalab-2.0179
-rw-r--r--options/license/etc/license-aliases.json1
-rw-r--r--options/license/fmt-exception6
-rw-r--r--options/license/freertos-exception-2.019
-rw-r--r--options/license/fwlw5
-rw-r--r--options/license/gSOAP-1.3b148
-rw-r--r--options/license/generic-xts17
-rw-r--r--options/license/gnu-javamail-exception1
-rw-r--r--options/license/gnuplot14
-rw-r--r--options/license/gtkbook6
-rw-r--r--options/license/harbour-exception23
-rw-r--r--options/license/hdparm9
-rw-r--r--options/license/i2p-gpl-java-exception1
-rw-r--r--options/license/iMatix39
-rw-r--r--options/license/libpng-2.033
-rw-r--r--options/license/libpri-OpenH323-exception4
-rw-r--r--options/license/libselinux-1.021
-rw-r--r--options/license/libtiff8
-rw-r--r--options/license/libutil-David-Nugent15
-rw-r--r--options/license/lsof26
-rw-r--r--options/license/magaz4
-rw-r--r--options/license/mailprio9
-rw-r--r--options/license/metamail12
-rw-r--r--options/license/mif-exception1
-rw-r--r--options/license/mpi-permissive15
-rw-r--r--options/license/mpich224
-rw-r--r--options/license/mplus6
-rw-r--r--options/license/mxml-exception16
-rw-r--r--options/license/openvpn-openssl-exception3
-rw-r--r--options/license/pkgconf7
-rw-r--r--options/license/pnmstitch23
-rw-r--r--options/license/psfrag5
-rw-r--r--options/license/psutils29
-rw-r--r--options/license/python-ldap10
-rw-r--r--options/license/radvd37
-rw-r--r--options/license/romic-exception6
-rw-r--r--options/license/snprintf3
-rw-r--r--options/license/softSurfer6
-rw-r--r--options/license/ssh-keyscan5
-rw-r--r--options/license/stunnel-exception5
-rw-r--r--options/license/swrule1
-rw-r--r--options/license/threeparttable3
-rw-r--r--options/license/u-boot-exception-2.06
-rw-r--r--options/license/ulem4
-rw-r--r--options/license/vsftpd-openssl-exception5
-rw-r--r--options/license/w3m11
-rw-r--r--options/license/wwl5
-rw-r--r--options/license/x11vnc-openssl-exception9
-rw-r--r--options/license/xinetd25
-rw-r--r--options/license/xkeyboard-config-Zinoviev15
-rw-r--r--options/license/xlock14
-rw-r--r--options/license/xpp21
-rw-r--r--options/license/xzoom12
-rw-r--r--options/license/zlib-acknowledgement15
-rw-r--r--options/locale/TRANSLATORS1
-rw-r--r--options/locale/locale_cs-CZ.ini108
-rw-r--r--options/locale/locale_de-DE.ini142
-rw-r--r--options/locale/locale_el-GR.ini45
-rw-r--r--options/locale/locale_en-US.ini182
-rw-r--r--options/locale/locale_es-ES.ini44
-rw-r--r--options/locale/locale_fa-IR.ini26
-rw-r--r--options/locale/locale_fi-FI.ini17
-rw-r--r--options/locale/locale_fr-FR.ini240
-rw-r--r--options/locale/locale_ga-IE.ini196
-rw-r--r--options/locale/locale_hu-HU.ini19
-rw-r--r--options/locale/locale_id-ID.ini17
-rw-r--r--options/locale/locale_is-IS.ini16
-rw-r--r--options/locale/locale_it-IT.ini31
-rw-r--r--options/locale/locale_ja-JP.ini178
-rw-r--r--options/locale/locale_ko-KR.ini51
-rw-r--r--options/locale/locale_lv-LV.ini45
-rw-r--r--options/locale/locale_nl-NL.ini28
-rw-r--r--options/locale/locale_pl-PL.ini27
-rw-r--r--options/locale/locale_pt-BR.ini46
-rw-r--r--options/locale/locale_pt-PT.ini188
-rw-r--r--options/locale/locale_ru-RU.ini44
-rw-r--r--options/locale/locale_si-LK.ini26
-rw-r--r--options/locale/locale_sk-SK.ini15
-rw-r--r--options/locale/locale_sv-SE.ini22
-rw-r--r--options/locale/locale_tr-TR.ini168
-rw-r--r--options/locale/locale_uk-UA.ini2484
-rw-r--r--options/locale/locale_zh-CN.ini1353
-rw-r--r--options/locale/locale_zh-HK.ini16
-rw-r--r--options/locale/locale_zh-TW.ini53
-rw-r--r--package-lock.json7095
-rw-r--r--package.json128
-rw-r--r--poetry.lock118
-rw-r--r--poetry.toml1
-rw-r--r--public/assets/img/feishu.pngbin1982 -> 0 bytes
-rw-r--r--public/assets/img/svg/gitea-chef.svg2
-rw-r--r--public/assets/img/svg/gitea-codecommit.svg2
-rw-r--r--public/assets/img/svg/gitea-debian.svg2
-rw-r--r--public/assets/img/svg/gitea-feishu.svg1
-rw-r--r--public/assets/img/svg/gitea-gitbucket.svg2
-rw-r--r--public/assets/img/svg/gitea-gitlab.svg2
-rw-r--r--public/assets/img/svg/gitea-google.svg2
-rw-r--r--public/assets/img/svg/gitea-maven.svg2
-rw-r--r--public/assets/img/svg/gitea-microsoftonline.svg2
-rw-r--r--public/assets/img/svg/gitea-npm.svg2
-rw-r--r--public/assets/img/svg/gitea-onedev.svg2
-rw-r--r--public/assets/img/svg/gitea-openid.svg2
-rw-r--r--public/assets/img/svg/gitea-rubygems.svg2
-rw-r--r--public/assets/img/svg/gitea-swift.svg2
-rw-r--r--public/assets/img/svg/gitea-vagrant.svg2
-rw-r--r--public/assets/img/svg/material-folder-generic.svg1
-rw-r--r--public/assets/img/svg/material-folder-symlink.svg1
-rw-r--r--public/assets/img/svg/octicon-pause.svg1
-rw-r--r--public/assets/img/svg/octicon-sparkle.svg1
-rw-r--r--public/assets/img/svg/octicon-square-circle.svg1
-rw-r--r--pyproject.toml4
-rw-r--r--routers/api/actions/artifacts.go53
-rw-r--r--routers/api/actions/artifacts_chunks.go6
-rw-r--r--routers/api/actions/artifacts_utils.go10
-rw-r--r--routers/api/actions/artifactsv4.go78
-rw-r--r--routers/api/actions/ping/ping_test.go3
-rw-r--r--routers/api/actions/runner/main_test.go14
-rw-r--r--routers/api/actions/runner/runner.go23
-rw-r--r--routers/api/actions/runner/utils.go217
-rw-r--r--routers/api/packages/alpine/alpine.go6
-rw-r--r--routers/api/packages/api.go401
-rw-r--r--routers/api/packages/arch/arch.go4
-rw-r--r--routers/api/packages/cargo/cargo.go13
-rw-r--r--routers/api/packages/chef/auth.go7
-rw-r--r--routers/api/packages/chef/chef.go8
-rw-r--r--routers/api/packages/composer/api.go22
-rw-r--r--routers/api/packages/composer/composer.go9
-rw-r--r--routers/api/packages/conan/conan.go25
-rw-r--r--routers/api/packages/conan/search.go11
-rw-r--r--routers/api/packages/conda/conda.go28
-rw-r--r--routers/api/packages/container/auth.go2
-rw-r--r--routers/api/packages/container/blob.go48
-rw-r--r--routers/api/packages/container/container.go144
-rw-r--r--routers/api/packages/container/manifest.go346
-rw-r--r--routers/api/packages/cran/cran.go2
-rw-r--r--routers/api/packages/debian/debian.go8
-rw-r--r--routers/api/packages/generic/generic.go8
-rw-r--r--routers/api/packages/goproxy/goproxy.go2
-rw-r--r--routers/api/packages/helm/helm.go4
-rw-r--r--routers/api/packages/maven/api.go9
-rw-r--r--routers/api/packages/maven/maven.go18
-rw-r--r--routers/api/packages/npm/api.go1
-rw-r--r--routers/api/packages/npm/npm.go14
-rw-r--r--routers/api/packages/nuget/api_v2.go46
-rw-r--r--routers/api/packages/nuget/nuget.go18
-rw-r--r--routers/api/packages/pub/pub.go8
-rw-r--r--routers/api/packages/pypi/pypi.go52
-rw-r--r--routers/api/packages/pypi/pypi_test.go10
-rw-r--r--routers/api/packages/rpm/rpm.go4
-rw-r--r--routers/api/packages/rubygems/rubygems.go54
-rw-r--r--routers/api/packages/rubygems/rubygems_test.go41
-rw-r--r--routers/api/packages/swift/swift.go38
-rw-r--r--routers/api/packages/vagrant/vagrant.go5
-rw-r--r--routers/api/v1/activitypub/person.go8
-rw-r--r--routers/api/v1/activitypub/reqsignature.go7
-rw-r--r--routers/api/v1/admin/action.go93
-rw-r--r--routers/api/v1/admin/adopt.go26
-rw-r--r--routers/api/v1/admin/cron.go2
-rw-r--r--routers/api/v1/admin/email.go2
-rw-r--r--routers/api/v1/admin/hooks.go36
-rw-r--r--routers/api/v1/admin/org.go10
-rw-r--r--routers/api/v1/admin/repo.go2
-rw-r--r--routers/api/v1/admin/runners.go78
-rw-r--r--routers/api/v1/admin/user.go86
-rw-r--r--routers/api/v1/admin/user_badge.go12
-rw-r--r--routers/api/v1/api.go345
-rw-r--r--routers/api/v1/misc/gitignore.go2
-rw-r--r--routers/api/v1/misc/label_templates.go2
-rw-r--r--routers/api/v1/misc/licenses.go3
-rw-r--r--routers/api/v1/misc/markup.go10
-rw-r--r--routers/api/v1/misc/markup_test.go10
-rw-r--r--routers/api/v1/misc/nodeinfo.go2
-rw-r--r--routers/api/v1/misc/signing.go79
-rw-r--r--routers/api/v1/notify/notifications.go4
-rw-r--r--routers/api/v1/notify/repo.go12
-rw-r--r--routers/api/v1/notify/threads.go12
-rw-r--r--routers/api/v1/notify/user.go12
-rw-r--r--routers/api/v1/org/action.go278
-rw-r--r--routers/api/v1/org/avatar.go6
-rw-r--r--routers/api/v1/org/block.go6
-rw-r--r--routers/api/v1/org/hook.go2
-rw-r--r--routers/api/v1/org/label.go22
-rw-r--r--routers/api/v1/org/member.go57
-rw-r--r--routers/api/v1/org/org.go80
-rw-r--r--routers/api/v1/org/team.go112
-rw-r--r--routers/api/v1/packages/package.go291
-rw-r--r--routers/api/v1/repo/action.go1182
-rw-r--r--routers/api/v1/repo/actions_run.go64
-rw-r--r--routers/api/v1/repo/avatar.go6
-rw-r--r--routers/api/v1/repo/blob.go6
-rw-r--r--routers/api/v1/repo/branch.go313
-rw-r--r--routers/api/v1/repo/collaborators.go54
-rw-r--r--routers/api/v1/repo/commits.go81
-rw-r--r--routers/api/v1/repo/compare.go4
-rw-r--r--routers/api/v1/repo/download.go17
-rw-r--r--routers/api/v1/repo/file.go611
-rw-r--r--routers/api/v1/repo/fork.go34
-rw-r--r--routers/api/v1/repo/git_hook.go25
-rw-r--r--routers/api/v1/repo/git_ref.go5
-rw-r--r--routers/api/v1/repo/hook.go12
-rw-r--r--routers/api/v1/repo/hook_test.go2
-rw-r--r--routers/api/v1/repo/issue.go143
-rw-r--r--routers/api/v1/repo/issue_attachment.go28
-rw-r--r--routers/api/v1/repo/issue_comment.go90
-rw-r--r--routers/api/v1/repo/issue_comment_attachment.go38
-rw-r--r--routers/api/v1/repo/issue_dependency.go58
-rw-r--r--routers/api/v1/repo/issue_label.go54
-rw-r--r--routers/api/v1/repo/issue_lock.go152
-rw-r--r--routers/api/v1/repo/issue_pin.go46
-rw-r--r--routers/api/v1/repo/issue_reaction.go54
-rw-r--r--routers/api/v1/repo/issue_stopwatch.go72
-rw-r--r--routers/api/v1/repo/issue_subscription.go34
-rw-r--r--routers/api/v1/repo/issue_tracked_time.go94
-rw-r--r--routers/api/v1/repo/key.go32
-rw-r--r--routers/api/v1/repo/label.go22
-rw-r--r--routers/api/v1/repo/language.go2
-rw-r--r--routers/api/v1/repo/license.go2
-rw-r--r--routers/api/v1/repo/migrate.go60
-rw-r--r--routers/api/v1/repo/milestone.go14
-rw-r--r--routers/api/v1/repo/mirror.go57
-rw-r--r--routers/api/v1/repo/notes.go16
-rw-r--r--routers/api/v1/repo/patch.go68
-rw-r--r--routers/api/v1/repo/pull.go327
-rw-r--r--routers/api/v1/repo/pull_review.go124
-rw-r--r--routers/api/v1/repo/release.go57
-rw-r--r--routers/api/v1/repo/release_attachment.go48
-rw-r--r--routers/api/v1/repo/release_tags.go18
-rw-r--r--routers/api/v1/repo/repo.go128
-rw-r--r--routers/api/v1/repo/repo_test.go4
-rw-r--r--routers/api/v1/repo/star.go4
-rw-r--r--routers/api/v1/repo/status.go35
-rw-r--r--routers/api/v1/repo/subscriber.go2
-rw-r--r--routers/api/v1/repo/tag.go84
-rw-r--r--routers/api/v1/repo/teams.go26
-rw-r--r--routers/api/v1/repo/topic.go14
-rw-r--r--routers/api/v1/repo/transfer.go94
-rw-r--r--routers/api/v1/repo/tree.go4
-rw-r--r--routers/api/v1/repo/wiki.go64
-rw-r--r--routers/api/v1/settings/settings.go1
-rw-r--r--routers/api/v1/shared/action.go187
-rw-r--r--routers/api/v1/shared/block.go20
-rw-r--r--routers/api/v1/shared/runners.go97
-rw-r--r--routers/api/v1/swagger/action.go14
-rw-r--r--routers/api/v1/swagger/options.go12
-rw-r--r--routers/api/v1/swagger/repo.go74
-rw-r--r--routers/api/v1/user/action.go163
-rw-r--r--routers/api/v1/user/app.go54
-rw-r--r--routers/api/v1/user/avatar.go6
-rw-r--r--routers/api/v1/user/block.go6
-rw-r--r--routers/api/v1/user/email.go16
-rw-r--r--routers/api/v1/user/follower.go26
-rw-r--r--routers/api/v1/user/gpg_key.go44
-rw-r--r--routers/api/v1/user/helper.go6
-rw-r--r--routers/api/v1/user/hook.go4
-rw-r--r--routers/api/v1/user/key.go31
-rw-r--r--routers/api/v1/user/repo.go18
-rw-r--r--routers/api/v1/user/runners.go78
-rw-r--r--routers/api/v1/user/settings.go2
-rw-r--r--routers/api/v1/user/star.go22
-rw-r--r--routers/api/v1/user/user.go14
-rw-r--r--routers/api/v1/user/watch.go14
-rw-r--r--routers/api/v1/utils/git.go104
-rw-r--r--routers/api/v1/utils/hook.go159
-rw-r--r--routers/api/v1/utils/hook_test.go82
-rw-r--r--routers/api/v1/utils/main_test.go21
-rw-r--r--routers/common/actions.go71
-rw-r--r--routers/common/blockexpensive.go90
-rw-r--r--routers/common/blockexpensive_test.go30
-rw-r--r--routers/common/codesearch.go30
-rw-r--r--routers/common/db.go11
-rw-r--r--routers/common/errpage.go2
-rw-r--r--routers/common/errpage_test.go3
-rw-r--r--routers/common/markup.go14
-rw-r--r--routers/common/middleware.go27
-rw-r--r--routers/common/pagetmpl.go83
-rw-r--r--routers/common/qos.go145
-rw-r--r--routers/common/qos_test.go91
-rw-r--r--routers/common/serve.go17
-rw-r--r--routers/init.go2
-rw-r--r--routers/install/install.go52
-rw-r--r--routers/install/routes.go2
-rw-r--r--routers/install/routes_test.go13
-rw-r--r--routers/private/hook_post_receive.go58
-rw-r--r--routers/private/hook_post_receive_test.go4
-rw-r--r--routers/private/hook_pre_receive.go19
-rw-r--r--routers/private/hook_proc_receive.go16
-rw-r--r--routers/private/hook_verification.go17
-rw-r--r--routers/private/hook_verification_test.go7
-rw-r--r--routers/private/internal.go4
-rw-r--r--routers/private/manager.go2
-rw-r--r--routers/private/serv.go10
-rw-r--r--routers/utils/utils_test.go7
-rw-r--r--routers/web/admin/admin.go1
-rw-r--r--routers/web/admin/admin_test.go4
-rw-r--r--routers/web/admin/applications.go5
-rw-r--r--routers/web/admin/auths.go32
-rw-r--r--routers/web/admin/config.go4
-rw-r--r--routers/web/admin/diagnosis.go22
-rw-r--r--routers/web/admin/emails.go2
-rw-r--r--routers/web/admin/notice.go5
-rw-r--r--routers/web/admin/orgs.go2
-rw-r--r--routers/web/admin/packages.go5
-rw-r--r--routers/web/admin/perftrace.go18
-rw-r--r--routers/web/admin/stacktrace.go11
-rw-r--r--routers/web/admin/users.go24
-rw-r--r--routers/web/auth/2fa.go2
-rw-r--r--routers/web/auth/auth.go54
-rw-r--r--routers/web/auth/auth_test.go40
-rw-r--r--routers/web/auth/linkaccount.go16
-rw-r--r--routers/web/auth/oauth.go32
-rw-r--r--routers/web/auth/oauth2_provider.go52
-rw-r--r--routers/web/auth/openid.go15
-rw-r--r--routers/web/auth/password.go11
-rw-r--r--routers/web/auth/webauthn.go10
-rw-r--r--routers/web/base.go24
-rw-r--r--routers/web/devtest/devtest.go201
-rw-r--r--routers/web/devtest/mock_actions.go54
-rw-r--r--routers/web/explore/code.go18
-rw-r--r--routers/web/explore/org.go2
-rw-r--r--routers/web/explore/repo.go3
-rw-r--r--routers/web/explore/topic.go2
-rw-r--r--routers/web/explore/user.go6
-rw-r--r--routers/web/feed/branch.go8
-rw-r--r--routers/web/feed/convert.go14
-rw-r--r--routers/web/feed/file.go8
-rw-r--r--routers/web/feed/profile.go14
-rw-r--r--routers/web/feed/profile_test.go38
-rw-r--r--routers/web/feed/render.go2
-rw-r--r--routers/web/githttp.go16
-rw-r--r--routers/web/goget.go6
-rw-r--r--routers/web/home.go6
-rw-r--r--routers/web/misc/markup.go2
-rw-r--r--routers/web/misc/misc.go4
-rw-r--r--routers/web/nodeinfo.go3
-rw-r--r--routers/web/org/block.go10
-rw-r--r--routers/web/org/home.go18
-rw-r--r--routers/web/org/members.go20
-rw-r--r--routers/web/org/org.go5
-rw-r--r--routers/web/org/org_labels.go48
-rw-r--r--routers/web/org/projects.go95
-rw-r--r--routers/web/org/setting.go123
-rw-r--r--routers/web/org/setting_oauth2.go5
-rw-r--r--routers/web/org/setting_packages.go20
-rw-r--r--routers/web/org/teams.go99
-rw-r--r--routers/web/org/worktime.go82
-rw-r--r--routers/web/repo/actions/actions.go235
-rw-r--r--routers/web/repo/actions/badge.go25
-rw-r--r--routers/web/repo/actions/view.go510
-rw-r--r--routers/web/repo/activity.go19
-rw-r--r--routers/web/repo/attachment.go22
-rw-r--r--routers/web/repo/blame.go94
-rw-r--r--routers/web/repo/branch.go36
-rw-r--r--routers/web/repo/cherry_pick.go184
-rw-r--r--routers/web/repo/code_frequency.go4
-rw-r--r--routers/web/repo/commit.go136
-rw-r--r--routers/web/repo/compare.go111
-rw-r--r--routers/web/repo/contributors.go2
-rw-r--r--routers/web/repo/download.go16
-rw-r--r--routers/web/repo/editor.go1037
-rw-r--r--routers/web/repo/editor_apply_patch.go51
-rw-r--r--routers/web/repo/editor_cherry_pick.go86
-rw-r--r--routers/web/repo/editor_error.go82
-rw-r--r--routers/web/repo/editor_fork.go31
-rw-r--r--routers/web/repo/editor_preview.go41
-rw-r--r--routers/web/repo/editor_test.go79
-rw-r--r--routers/web/repo/editor_uploader.go61
-rw-r--r--routers/web/repo/editor_util.go110
-rw-r--r--routers/web/repo/fork.go63
-rw-r--r--routers/web/repo/githttp.go48
-rw-r--r--routers/web/repo/githttp_test.go2
-rw-r--r--routers/web/repo/helper.go17
-rw-r--r--routers/web/repo/issue.go57
-rw-r--r--routers/web/repo/issue_comment.go65
-rw-r--r--routers/web/repo/issue_content_history.go15
-rw-r--r--routers/web/repo/issue_dependency.go8
-rw-r--r--routers/web/repo/issue_label.go52
-rw-r--r--routers/web/repo/issue_label_test.go104
-rw-r--r--routers/web/repo/issue_list.go157
-rw-r--r--routers/web/repo/issue_lock.go5
-rw-r--r--routers/web/repo/issue_new.go34
-rw-r--r--routers/web/repo/issue_page_meta.go34
-rw-r--r--routers/web/repo/issue_pin.go47
-rw-r--r--routers/web/repo/issue_poster.go10
-rw-r--r--routers/web/repo/issue_stopwatch.go80
-rw-r--r--routers/web/repo/issue_suggestions.go52
-rw-r--r--routers/web/repo/issue_timetrack.go14
-rw-r--r--routers/web/repo/issue_view.go184
-rw-r--r--routers/web/repo/issue_watch.go4
-rw-r--r--routers/web/repo/middlewares.go12
-rw-r--r--routers/web/repo/migrate.go8
-rw-r--r--routers/web/repo/milestone.go15
-rw-r--r--routers/web/repo/packages.go9
-rw-r--r--routers/web/repo/patch.go116
-rw-r--r--routers/web/repo/projects.go66
-rw-r--r--routers/web/repo/pull.go194
-rw-r--r--routers/web/repo/pull_review.go17
-rw-r--r--routers/web/repo/pull_review_test.go16
-rw-r--r--routers/web/repo/recent_commits.go15
-rw-r--r--routers/web/repo/release.go261
-rw-r--r--routers/web/repo/release_test.go155
-rw-r--r--routers/web/repo/render.go12
-rw-r--r--routers/web/repo/repo.go225
-rw-r--r--routers/web/repo/search.go48
-rw-r--r--routers/web/repo/setting/default_branch.go4
-rw-r--r--routers/web/repo/setting/git_hooks.go4
-rw-r--r--routers/web/repo/setting/lfs.go80
-rw-r--r--routers/web/repo/setting/protected_branch.go62
-rw-r--r--routers/web/repo/setting/protected_tag.go5
-rw-r--r--routers/web/repo/setting/public_access.go155
-rw-r--r--routers/web/repo/setting/runners.go187
-rw-r--r--routers/web/repo/setting/secrets.go5
-rw-r--r--routers/web/repo/setting/setting.go1492
-rw-r--r--routers/web/repo/setting/settings_test.go46
-rw-r--r--routers/web/repo/setting/variables.go140
-rw-r--r--routers/web/repo/setting/webhook.go60
-rw-r--r--routers/web/repo/star.go31
-rw-r--r--routers/web/repo/transfer.go38
-rw-r--r--routers/web/repo/treelist.go99
-rw-r--r--routers/web/repo/treelist_test.go68
-rw-r--r--routers/web/repo/view.go100
-rw-r--r--routers/web/repo/view_file.go427
-rw-r--r--routers/web/repo/view_home.go225
-rw-r--r--routers/web/repo/view_home_test.go32
-rw-r--r--routers/web/repo/view_readme.go76
-rw-r--r--routers/web/repo/watch.go31
-rw-r--r--routers/web/repo/webgit.go40
-rw-r--r--routers/web/repo/wiki.go223
-rw-r--r--routers/web/repo/wiki_test.go49
-rw-r--r--routers/web/shared/actions/runners.go236
-rw-r--r--routers/web/shared/actions/variables.go183
-rw-r--r--routers/web/shared/issue/issue_label.go21
-rw-r--r--routers/web/shared/label/label.go26
-rw-r--r--routers/web/shared/packages/packages.go14
-rw-r--r--routers/web/shared/project/column.go2
-rw-r--r--routers/web/shared/secrets/secrets.go8
-rw-r--r--routers/web/shared/user/header.go92
-rw-r--r--routers/web/shared/user/helper.go19
-rw-r--r--routers/web/swagger_json.go5
-rw-r--r--routers/web/user/avatar.go28
-rw-r--r--routers/web/user/code.go25
-rw-r--r--routers/web/user/home.go97
-rw-r--r--routers/web/user/home_test.go16
-rw-r--r--routers/web/user/notification.go52
-rw-r--r--routers/web/user/package.go132
-rw-r--r--routers/web/user/profile.go73
-rw-r--r--routers/web/user/search.go2
-rw-r--r--routers/web/user/setting/account.go11
-rw-r--r--routers/web/user/setting/account_test.go2
-rw-r--r--routers/web/user/setting/applications.go35
-rw-r--r--routers/web/user/setting/keys.go14
-rw-r--r--routers/web/user/setting/oauth2_common.go16
-rw-r--r--routers/web/user/setting/profile.go17
-rw-r--r--routers/web/user/setting/security/2fa.go19
-rw-r--r--routers/web/user/setting/security/openid.go6
-rw-r--r--routers/web/user/setting/security/security.go4
-rw-r--r--routers/web/user/setting/security/webauthn.go13
-rw-r--r--routers/web/user/setting/settings.go26
-rw-r--r--routers/web/user/stop_watch.go6
-rw-r--r--routers/web/web.go547
-rw-r--r--routers/web/webfinger.go21
-rw-r--r--services/actions/auth.go5
-rw-r--r--services/actions/auth_test.go4
-rw-r--r--services/actions/cleanup.go160
-rw-r--r--services/actions/clear_tasks.go39
-rw-r--r--services/actions/commit_status.go33
-rw-r--r--services/actions/context.go201
-rw-r--r--services/actions/context_test.go (renamed from routers/api/actions/runner/utils_test.go)8
-rw-r--r--services/actions/init_test.go4
-rw-r--r--services/actions/interface.go12
-rw-r--r--services/actions/job_emitter.go29
-rw-r--r--services/actions/notifier.go53
-rw-r--r--services/actions/notifier_helper.go84
-rw-r--r--services/actions/schedule_tasks.go15
-rw-r--r--services/actions/task.go132
-rw-r--r--services/actions/variables.go19
-rw-r--r--services/actions/workflow.go219
-rw-r--r--services/agit/agit.go40
-rw-r--r--services/asymkey/commit.go475
-rw-r--r--services/asymkey/commit_test.go54
-rw-r--r--services/asymkey/sign.go189
-rw-r--r--services/attachment/attachment_test.go2
-rw-r--r--services/auth/auth.go81
-rw-r--r--services/auth/auth_test.go69
-rw-r--r--services/auth/basic.go28
-rw-r--r--services/auth/httpsign.go6
-rw-r--r--services/auth/interface.go5
-rw-r--r--services/auth/oauth2.go23
-rw-r--r--services/auth/oauth2_test.go7
-rw-r--r--services/auth/reverseproxy.go4
-rw-r--r--services/auth/source/db/source.go4
-rw-r--r--services/auth/source/ldap/assert_interface_test.go2
-rw-r--r--services/auth/source/ldap/source.go10
-rw-r--r--services/auth/source/ldap/source_authenticate.go20
-rw-r--r--services/auth/source/ldap/source_search.go4
-rw-r--r--services/auth/source/ldap/source_sync.go52
-rw-r--r--services/auth/source/oauth2/assert_interface_test.go1
-rw-r--r--services/auth/source/oauth2/source.go11
-rw-r--r--services/auth/source/oauth2/source_callout.go4
-rw-r--r--services/auth/source/oauth2/source_register.go6
-rw-r--r--services/auth/source/oauth2/source_sync.go12
-rw-r--r--services/auth/source/oauth2/source_sync_test.go39
-rw-r--r--services/auth/source/oauth2/urlmapping.go10
-rw-r--r--services/auth/source/pam/assert_interface_test.go1
-rw-r--r--services/auth/source/pam/source.go13
-rw-r--r--services/auth/source/pam/source_authenticate.go7
-rw-r--r--services/auth/source/smtp/assert_interface_test.go1
-rw-r--r--services/auth/source/smtp/source.go11
-rw-r--r--services/auth/source/smtp/source_authenticate.go7
-rw-r--r--services/auth/source/sspi/source.go2
-rw-r--r--services/auth/sspi.go11
-rw-r--r--services/automerge/automerge.go21
-rw-r--r--services/context/access_log.go106
-rw-r--r--services/context/access_log_test.go71
-rw-r--r--services/context/api.go141
-rw-r--r--services/context/api_test.go2
-rw-r--r--services/context/base.go35
-rw-r--r--services/context/base_form.go4
-rw-r--r--services/context/base_test.go4
-rw-r--r--services/context/context.go60
-rw-r--r--services/context/context_model.go20
-rw-r--r--services/context/context_response.go10
-rw-r--r--services/context/org.go332
-rw-r--r--services/context/package.go24
-rw-r--r--services/context/pagination.go14
-rw-r--r--services/context/permission.go89
-rw-r--r--services/context/private.go12
-rw-r--r--services/context/repo.go549
-rw-r--r--services/context/response.go36
-rw-r--r--services/context/upload/upload.go28
-rw-r--r--services/context/user.go20
-rw-r--r--services/contexttest/context_tests.go42
-rw-r--r--services/convert/convert.go311
-rw-r--r--services/convert/git_commit.go10
-rw-r--r--services/convert/git_commit_test.go2
-rw-r--r--services/convert/issue.go12
-rw-r--r--services/convert/issue_comment.go2
-rw-r--r--services/convert/pull.go163
-rw-r--r--services/convert/pull_review_test.go4
-rw-r--r--services/convert/pull_test.go9
-rw-r--r--services/convert/release_test.go4
-rw-r--r--services/convert/repository.go13
-rw-r--r--services/convert/status.go38
-rw-r--r--services/convert/user_test.go4
-rw-r--r--services/convert/utils.go2
-rw-r--r--services/convert/utils_test.go18
-rw-r--r--services/cron/tasks_basic.go2
-rw-r--r--services/doctor/actions.go2
-rw-r--r--services/doctor/authorizedkeys.go3
-rw-r--r--services/doctor/dbconsistency.go22
-rw-r--r--services/doctor/dbconsistency_test.go5
-rw-r--r--services/doctor/dbversion.go3
-rw-r--r--services/doctor/doctor.go8
-rw-r--r--services/doctor/fix16961_test.go10
-rw-r--r--services/doctor/heads.go6
-rw-r--r--services/doctor/lfs.go4
-rw-r--r--services/doctor/mergebase.go10
-rw-r--r--services/doctor/misc.go17
-rw-r--r--services/doctor/paths.go5
-rw-r--r--services/doctor/repository.go4
-rw-r--r--services/doctor/storage.go2
-rw-r--r--services/externalaccount/link.go4
-rw-r--r--services/feed/feed.go147
-rw-r--r--services/feed/feed_test.go43
-rw-r--r--services/feed/notifier.go40
-rw-r--r--services/forms/auth_form.go9
-rw-r--r--services/forms/org.go6
-rw-r--r--services/forms/repo_form.go240
-rw-r--r--services/forms/repo_form_editor.go57
-rw-r--r--services/forms/repo_form_test.go25
-rw-r--r--services/forms/user_form.go21
-rw-r--r--services/forms/user_form_test.go50
-rw-r--r--services/git/commit.go97
-rw-r--r--services/gitdiff/csv.go10
-rw-r--r--services/gitdiff/csv_test.go13
-rw-r--r--services/gitdiff/git_diff_tree.go250
-rw-r--r--services/gitdiff/git_diff_tree_test.go427
-rw-r--r--services/gitdiff/gitdiff.go598
-rw-r--r--services/gitdiff/gitdiff_test.go61
-rw-r--r--services/gitdiff/highlightdiff.go99
-rw-r--r--services/gitdiff/highlightdiff_test.go151
-rw-r--r--services/gitdiff/submodule.go65
-rw-r--r--services/gitdiff/submodule_test.go234
-rw-r--r--services/gitdiff/testdata/academic-module/HEAD1
-rw-r--r--services/gitdiff/testdata/academic-module/config10
-rw-r--r--services/gitdiff/testdata/academic-module/indexbin46960 -> 0 bytes
-rw-r--r--services/gitdiff/testdata/academic-module/logs/HEAD1
-rw-r--r--services/gitdiff/testdata/academic-module/logs/refs/heads/master1
-rw-r--r--services/gitdiff/testdata/academic-module/logs/refs/remotes/origin/HEAD1
-rw-r--r--services/gitdiff/testdata/academic-module/objects/pack/pack-597efbc3613c7ba790e33b178fd9fc1fe17b4245.idxbin65332 -> 0 bytes
-rw-r--r--services/gitdiff/testdata/academic-module/objects/pack/pack-597efbc3613c7ba790e33b178fd9fc1fe17b4245.packbin1167905 -> 0 bytes
-rw-r--r--services/gitdiff/testdata/academic-module/packed-refs2
-rw-r--r--services/gitdiff/testdata/academic-module/refs/heads/master1
-rw-r--r--services/gitdiff/testdata/academic-module/refs/remotes/origin/HEAD1
-rw-r--r--services/issue/assignee.go4
-rw-r--r--services/issue/comments.go43
-rw-r--r--services/issue/issue.go111
-rw-r--r--services/issue/issue_test.go10
-rw-r--r--services/issue/milestone.go3
-rw-r--r--services/issue/pull.go48
-rw-r--r--services/issue/status.go4
-rw-r--r--services/issue/suggestion.go73
-rw-r--r--services/issue/suggestion_test.go57
-rw-r--r--services/lfs/locks.go10
-rw-r--r--services/lfs/server.go43
-rw-r--r--services/mailer/mail.go549
-rw-r--r--services/mailer/mail_comment.go10
-rw-r--r--services/mailer/mail_issue.go111
-rw-r--r--services/mailer/mail_issue_common.go336
-rw-r--r--services/mailer/mail_release.go13
-rw-r--r--services/mailer/mail_repo.go3
-rw-r--r--services/mailer/mail_team_invite.go9
-rw-r--r--services/mailer/mail_test.go187
-rw-r--r--services/mailer/mail_user.go161
-rw-r--r--services/mailer/notify.go21
-rw-r--r--services/mailer/sender/message_test.go4
-rw-r--r--services/mailer/sender/smtp.go3
-rw-r--r--services/mailer/sender/smtp_auth.go3
-rw-r--r--services/markup/renderhelper.go4
-rw-r--r--services/markup/renderhelper_codepreview.go14
-rw-r--r--services/markup/renderhelper_issueicontitle.go7
-rw-r--r--services/markup/renderhelper_mention_test.go11
-rw-r--r--services/migrations/codebase.go50
-rw-r--r--services/migrations/codebase_test.go19
-rw-r--r--services/migrations/codecommit.go48
-rw-r--r--services/migrations/dump.go48
-rw-r--r--services/migrations/error.go2
-rw-r--r--services/migrations/git.go8
-rw-r--r--services/migrations/gitea_downloader.go37
-rw-r--r--services/migrations/gitea_downloader_test.go40
-rw-r--r--services/migrations/gitea_uploader.go120
-rw-r--r--services/migrations/gitea_uploader_test.go55
-rw-r--r--services/migrations/github.go186
-rw-r--r--services/migrations/github_test.go60
-rw-r--r--services/migrations/gitlab.go74
-rw-r--r--services/migrations/gitlab_test.go38
-rw-r--r--services/migrations/gogs.go83
-rw-r--r--services/migrations/gogs_test.go17
-rw-r--r--services/migrations/migrate.go69
-rw-r--r--services/migrations/onedev.go52
-rw-r--r--services/migrations/onedev_test.go18
-rw-r--r--services/migrations/restore.go27
-rw-r--r--services/mirror/mirror.go6
-rw-r--r--services/mirror/mirror_pull.go126
-rw-r--r--services/mirror/mirror_pull_test.go94
-rw-r--r--services/mirror/mirror_push.go19
-rw-r--r--services/mirror/mirror_test.go46
-rw-r--r--services/notify/notifier.go5
-rw-r--r--services/notify/notify.go40
-rw-r--r--services/notify/null.go7
-rw-r--r--services/oauth2_provider/access_token.go37
-rw-r--r--services/oauth2_provider/additional_scopes_test.go2
-rw-r--r--services/oauth2_provider/init.go2
-rw-r--r--services/oauth2_provider/jwtsigningkey.go4
-rw-r--r--services/oauth2_provider/token.go7
-rw-r--r--services/org/team.go31
-rw-r--r--services/org/team_test.go31
-rw-r--r--services/org/user.go3
-rw-r--r--services/org/user_test.go2
-rw-r--r--services/packages/alpine/repository.go2
-rw-r--r--services/packages/arch/repository.go51
-rw-r--r--services/packages/arch/vercmp.go108
-rw-r--r--services/packages/arch/vercmp_test.go27
-rw-r--r--services/packages/auth.go5
-rw-r--r--services/packages/cargo/index.go40
-rw-r--r--services/packages/cleanup/cleanup.go211
-rw-r--r--services/packages/container/blob_uploader.go21
-rw-r--r--services/packages/container/cleanup.go4
-rw-r--r--services/packages/container/common.go31
-rw-r--r--services/packages/debian/repository.go8
-rw-r--r--services/packages/package_update.go79
-rw-r--r--services/packages/packages.go44
-rw-r--r--services/packages/rpm/repository.go7
-rw-r--r--services/projects/issue.go156
-rw-r--r--services/projects/issue_test.go210
-rw-r--r--services/projects/main_test.go17
-rw-r--r--services/pull/check.go160
-rw-r--r--services/pull/check_test.go7
-rw-r--r--services/pull/commit_status.go111
-rw-r--r--services/pull/commit_status_test.go101
-rw-r--r--services/pull/merge.go189
-rw-r--r--services/pull/merge_ff_only.go2
-rw-r--r--services/pull/merge_merge.go2
-rw-r--r--services/pull/merge_prepare.go34
-rw-r--r--services/pull/merge_rebase.go12
-rw-r--r--services/pull/merge_squash.go20
-rw-r--r--services/pull/merge_test.go25
-rw-r--r--services/pull/patch.go47
-rw-r--r--services/pull/patch_unmerged.go4
-rw-r--r--services/pull/protected_branch.go49
-rw-r--r--services/pull/pull.go199
-rw-r--r--services/pull/review.go4
-rw-r--r--services/pull/reviewer.go2
-rw-r--r--services/pull/temp_repo.go40
-rw-r--r--services/pull/update.go32
-rw-r--r--services/pull/update_rebase.go6
-rw-r--r--services/release/release.go16
-rw-r--r--services/release/release_test.go12
-rw-r--r--services/repository/adopt.go122
-rw-r--r--services/repository/adopt_test.go35
-rw-r--r--services/repository/archiver/archiver.go57
-rw-r--r--services/repository/archiver/archiver_test.go33
-rw-r--r--services/repository/avatar.go6
-rw-r--r--services/repository/avatar_test.go2
-rw-r--r--services/repository/branch.go121
-rw-r--r--services/repository/check.go6
-rw-r--r--services/repository/commitstatus/commitstatus.go8
-rw-r--r--services/repository/contributors_graph.go4
-rw-r--r--services/repository/contributors_graph_test.go6
-rw-r--r--services/repository/create.go244
-rw-r--r--services/repository/create_test.go57
-rw-r--r--services/repository/delete.go56
-rw-r--r--services/repository/files/cherry_pick.go36
-rw-r--r--services/repository/files/commit.go4
-rw-r--r--services/repository/files/content.go292
-rw-r--r--services/repository/files/content_test.go235
-rw-r--r--services/repository/files/diff.go12
-rw-r--r--services/repository/files/diff_test.go9
-rw-r--r--services/repository/files/file.go108
-rw-r--r--services/repository/files/file_test.go119
-rw-r--r--services/repository/files/patch.go56
-rw-r--r--services/repository/files/temp_repo.go172
-rw-r--r--services/repository/files/tree.go119
-rw-r--r--services/repository/files/tree_test.go69
-rw-r--r--services/repository/files/update.go372
-rw-r--r--services/repository/files/upload.go228
-rw-r--r--services/repository/fork.go179
-rw-r--r--services/repository/fork_test.go46
-rw-r--r--services/repository/generate.go117
-rw-r--r--services/repository/generate_test.go24
-rw-r--r--services/repository/gitgraph/graph.go (renamed from modules/gitgraph/graph.go)6
-rw-r--r--services/repository/gitgraph/graph_models.go (renamed from modules/gitgraph/graph_models.go)9
-rw-r--r--services/repository/gitgraph/graph_test.go (renamed from modules/gitgraph/graph_test.go)925
-rw-r--r--services/repository/gitgraph/parser.go (renamed from modules/gitgraph/parser.go)0
-rw-r--r--services/repository/hooks.go5
-rw-r--r--services/repository/init.go19
-rw-r--r--services/repository/lfs_test.go3
-rw-r--r--services/repository/license.go49
-rw-r--r--services/repository/license_test.go13
-rw-r--r--services/repository/merge_upstream.go89
-rw-r--r--services/repository/migrate.go61
-rw-r--r--services/repository/push.go266
-rw-r--r--services/repository/repository.go237
-rw-r--r--services/repository/repository_test.go21
-rw-r--r--services/repository/setting.go3
-rw-r--r--services/repository/template.go143
-rw-r--r--services/repository/transfer.go272
-rw-r--r--services/repository/transfer_test.go77
-rw-r--r--services/secrets/secrets.go6
-rw-r--r--services/task/task.go5
-rw-r--r--services/user/block.go13
-rw-r--r--services/user/update.go39
-rw-r--r--services/user/update_test.go12
-rw-r--r--services/user/user.go5
-rw-r--r--services/user/user_test.go2
-rw-r--r--services/versioned_migration/migration.go24
-rw-r--r--services/webhook/deliver.go34
-rw-r--r--services/webhook/deliver_test.go19
-rw-r--r--services/webhook/dingtalk.go28
-rw-r--r--services/webhook/dingtalk_test.go3
-rw-r--r--services/webhook/discord.go33
-rw-r--r--services/webhook/discord_test.go3
-rw-r--r--services/webhook/feishu.go58
-rw-r--r--services/webhook/feishu_test.go9
-rw-r--r--services/webhook/general.go132
-rw-r--r--services/webhook/general_test.go4
-rw-r--r--services/webhook/matrix.go26
-rw-r--r--services/webhook/matrix_test.go3
-rw-r--r--services/webhook/msteams.go61
-rw-r--r--services/webhook/msteams_test.go7
-rw-r--r--services/webhook/notifier.go152
-rw-r--r--services/webhook/packagist.go16
-rw-r--r--services/webhook/packagist_test.go7
-rw-r--r--services/webhook/payloader.go14
-rw-r--r--services/webhook/slack.go28
-rw-r--r--services/webhook/slack_test.go3
-rw-r--r--services/webhook/telegram.go24
-rw-r--r--services/webhook/telegram_test.go3
-rw-r--r--services/webhook/webhook.go26
-rw-r--r--services/webhook/webhook_test.go4
-rw-r--r--services/webhook/wechatwork.go22
-rw-r--r--services/webtheme/webtheme.go136
-rw-r--r--services/webtheme/webtheme_test.go37
-rw-r--r--services/wiki/wiki.go32
-rw-r--r--services/wiki/wiki_test.go55
-rw-r--r--snap/snapcraft.yaml2
-rw-r--r--stylelint.config.js119
-rw-r--r--tailwind.config.js3
-rw-r--r--templates/admin/auth/edit.tmpl54
-rw-r--r--templates/admin/auth/list.tmpl2
-rw-r--r--templates/admin/config.tmpl6
-rw-r--r--templates/admin/dashboard.tmpl26
-rw-r--r--templates/admin/emails/list.tmpl2
-rw-r--r--templates/admin/hooks.tmpl3
-rw-r--r--templates/admin/navbar.tmpl6
-rw-r--r--templates/admin/notice.tmpl2
-rw-r--r--templates/admin/org/list.tmpl2
-rw-r--r--templates/admin/packages/list.tmpl21
-rw-r--r--templates/admin/perftrace.tmpl13
-rw-r--r--templates/admin/repo/list.tmpl23
-rw-r--r--templates/admin/stacktrace-row.tmpl5
-rw-r--r--templates/admin/stacktrace.tmpl28
-rw-r--r--templates/admin/trace_tabs.tmpl19
-rw-r--r--templates/admin/user/edit.tmpl9
-rw-r--r--templates/admin/user/list.tmpl2
-rw-r--r--templates/admin/user/view.tmpl17
-rw-r--r--templates/admin/user/view_details.tmpl31
-rw-r--r--templates/admin/user/view_emails.tmpl2
-rw-r--r--templates/base/alert.tmpl21
-rw-r--r--templates/base/footer.tmpl8
-rw-r--r--templates/base/head.tmpl6
-rw-r--r--templates/base/head_navbar.tmpl70
-rw-r--r--templates/base/head_navbar_icons.tmpl25
-rw-r--r--templates/base/head_script.tmpl2
-rw-r--r--templates/base/paginate.tmpl16
-rw-r--r--templates/devtest/badge-actions-svg.tmpl25
-rw-r--r--templates/devtest/badge-commit-sign.tmpl (renamed from templates/devtest/commit-sign-badge.tmpl)0
-rw-r--r--templates/devtest/flex-list.tmpl2
-rw-r--r--templates/devtest/fomantic-dropdown.tmpl4
-rw-r--r--templates/devtest/fomantic-modal.tmpl10
-rw-r--r--templates/devtest/gitea-ui.tmpl18
-rw-r--r--templates/devtest/markup-render.tmpl71
-rw-r--r--templates/devtest/repo-action-view.tmpl9
-rw-r--r--templates/explore/repos.tmpl4
-rw-r--r--templates/home.tmpl12
-rw-r--r--templates/install.tmpl3
-rw-r--r--templates/org/create.tmpl80
-rw-r--r--templates/org/header.tmpl2
-rw-r--r--templates/org/home.tmpl13
-rw-r--r--templates/org/member/members.tmpl4
-rw-r--r--templates/org/menu.tmpl5
-rw-r--r--templates/org/projects/new.tmpl23
-rw-r--r--templates/org/projects/view.tmpl4
-rw-r--r--templates/org/settings/delete.tmpl35
-rw-r--r--templates/org/settings/hooks.tmpl2
-rw-r--r--templates/org/settings/navbar.tmpl3
-rw-r--r--templates/org/settings/options.tmpl178
-rw-r--r--templates/org/settings/options_dangerzone.tmpl93
-rw-r--r--templates/org/team/members.tmpl2
-rw-r--r--templates/org/team/new.tmpl14
-rw-r--r--templates/org/team/repositories.tmpl2
-rw-r--r--templates/org/team/sidebar.tmpl6
-rw-r--r--templates/org/team/teams.tmpl4
-rw-r--r--templates/org/worktime.tmpl40
-rw-r--r--templates/org/worktime/table_members.tmpl16
-rw-r--r--templates/org/worktime/table_milestones.tmpl28
-rw-r--r--templates/org/worktime/table_repos.tmpl16
-rw-r--r--templates/package/content/container.tmpl48
-rw-r--r--templates/package/content/maven.tmpl2
-rw-r--r--templates/package/content/pypi.tmpl2
-rw-r--r--templates/package/shared/view.tmpl107
-rw-r--r--templates/package/view.tmpl122
-rw-r--r--templates/post-install.tmpl4
-rw-r--r--templates/projects/list.tmpl29
-rw-r--r--templates/projects/new.tmpl2
-rw-r--r--templates/projects/view.tmpl64
-rw-r--r--templates/repo/actions/runs_list.tmpl45
-rw-r--r--templates/repo/actions/status.tmpl2
-rw-r--r--templates/repo/actions/view_component.tmpl3
-rw-r--r--templates/repo/actions/workflow_dispatch.tmpl29
-rw-r--r--templates/repo/actions/workflow_dispatch_inputs.tmpl46
-rw-r--r--templates/repo/blame.tmpl12
-rw-r--r--templates/repo/branch/list.tmpl38
-rw-r--r--templates/repo/branch_dropdown.tmpl23
-rw-r--r--templates/repo/clone_panel.tmpl16
-rw-r--r--templates/repo/code/recently_pushed_new_branches.tmpl2
-rw-r--r--templates/repo/code/upstream_diverging_info.tmpl17
-rw-r--r--templates/repo/commit_page.tmpl18
-rw-r--r--templates/repo/commit_sign_badge.tmpl5
-rw-r--r--templates/repo/commit_status.tmpl3
-rw-r--r--templates/repo/commit_statuses.tmpl4
-rw-r--r--templates/repo/commits.tmpl17
-rw-r--r--templates/repo/commits_list.tmpl33
-rw-r--r--templates/repo/commits_list_small.tmpl4
-rw-r--r--templates/repo/commits_table.tmpl2
-rw-r--r--templates/repo/create.tmpl357
-rw-r--r--templates/repo/diff/box.tmpl98
-rw-r--r--templates/repo/diff/comment_form.tmpl2
-rw-r--r--templates/repo/diff/comments.tmpl6
-rw-r--r--templates/repo/diff/compare.tmpl10
-rw-r--r--templates/repo/diff/conversation.tmpl34
-rw-r--r--templates/repo/diff/image_diff.tmpl24
-rw-r--r--templates/repo/diff/new_review.tmpl95
-rw-r--r--templates/repo/diff/options_dropdown.tmpl1
-rw-r--r--templates/repo/editor/cherry_pick.tmpl11
-rw-r--r--templates/repo/editor/commit_form.tmpl31
-rw-r--r--templates/repo/editor/common_breadcrumb.tmpl16
-rw-r--r--templates/repo/editor/common_top.tmpl6
-rw-r--r--templates/repo/editor/delete.tmpl4
-rw-r--r--templates/repo/editor/edit.tmpl73
-rw-r--r--templates/repo/editor/fork.tmpl18
-rw-r--r--templates/repo/editor/patch.tmpl7
-rw-r--r--templates/repo/editor/upload.tmpl20
-rw-r--r--templates/repo/empty.tmpl23
-rw-r--r--templates/repo/file_info.tmpl2
-rw-r--r--templates/repo/forks.tmpl14
-rw-r--r--templates/repo/graph.tmpl43
-rw-r--r--templates/repo/graph/commits.tmpl15
-rw-r--r--templates/repo/graph/div.tmpl10
-rw-r--r--templates/repo/header.tmpl29
-rw-r--r--templates/repo/home.tmpl113
-rw-r--r--templates/repo/home_sidebar_bottom.tmpl6
-rw-r--r--templates/repo/home_sidebar_top.tmpl9
-rw-r--r--templates/repo/icon.tmpl2
-rw-r--r--templates/repo/issue/branch_selector_field.tmpl2
-rw-r--r--templates/repo/issue/card.tmpl21
-rw-r--r--templates/repo/issue/choose.tmpl12
-rw-r--r--templates/repo/issue/fields/markdown.tmpl2
-rw-r--r--templates/repo/issue/filter_item_label.tmpl2
-rw-r--r--templates/repo/issue/filter_item_user_assign.tmpl26
-rw-r--r--templates/repo/issue/filter_list.tmpl14
-rw-r--r--templates/repo/issue/filters.tmpl2
-rw-r--r--templates/repo/issue/labels/label_edit_modal.tmpl13
-rw-r--r--templates/repo/issue/labels/label_list.tmpl19
-rw-r--r--templates/repo/issue/list.tmpl7
-rw-r--r--templates/repo/issue/milestone_issues.tmpl16
-rw-r--r--templates/repo/issue/milestone_new.tmpl2
-rw-r--r--templates/repo/issue/milestones.tmpl20
-rw-r--r--templates/repo/issue/new_form.tmpl9
-rw-r--r--templates/repo/issue/openclose.tmpl2
-rw-r--r--templates/repo/issue/search.tmpl1
-rw-r--r--templates/repo/issue/sidebar/allow_maintainer_edit.tmpl15
-rw-r--r--templates/repo/issue/sidebar/assignee_list.tmpl15
-rw-r--r--templates/repo/issue/sidebar/issue_dependencies.tmpl53
-rw-r--r--templates/repo/issue/sidebar/issue_management.tmpl36
-rw-r--r--templates/repo/issue/sidebar/label_list.tmpl9
-rw-r--r--templates/repo/issue/sidebar/milestone_list.tmpl16
-rw-r--r--templates/repo/issue/sidebar/project_list.tmpl11
-rw-r--r--templates/repo/issue/sidebar/reference_link.tmpl10
-rw-r--r--templates/repo/issue/sidebar/reviewer_list.tmpl14
-rw-r--r--templates/repo/issue/sidebar/stopwatch_timetracker.tmpl19
-rw-r--r--templates/repo/issue/sidebar/wip_switch.tmpl8
-rw-r--r--templates/repo/issue/view_content.tmpl8
-rw-r--r--templates/repo/issue/view_content/add_reaction.tmpl2
-rw-r--r--templates/repo/issue/view_content/attachments.tmpl2
-rw-r--r--templates/repo/issue/view_content/comments.tmpl21
-rw-r--r--templates/repo/issue/view_content/conversation.tmpl16
-rw-r--r--templates/repo/issue/view_content/pull_merge_box.tmpl (renamed from templates/repo/issue/view_content/pull.tmpl)23
-rw-r--r--templates/repo/issue/view_content/pull_merge_instruction.tmpl98
-rw-r--r--templates/repo/issue/view_content/reactions.tmpl6
-rw-r--r--templates/repo/issue/view_content/reference_issue_dialog.tmpl4
-rw-r--r--templates/repo/issue/view_content/update_branch_by_merge.tmpl10
-rw-r--r--templates/repo/issue/view_title.tmpl4
-rw-r--r--templates/repo/latest_commit.tmpl6
-rw-r--r--templates/repo/migrate/codebase.tmpl184
-rw-r--r--templates/repo/migrate/codecommit.tmpl184
-rw-r--r--templates/repo/migrate/git.tmpl142
-rw-r--r--templates/repo/migrate/gitbucket.tmpl206
-rw-r--r--templates/repo/migrate/gitea.tmpl200
-rw-r--r--templates/repo/migrate/github.tmpl204
-rw-r--r--templates/repo/migrate/gitlab.tmpl198
-rw-r--r--templates/repo/migrate/gogs.tmpl204
-rw-r--r--templates/repo/migrate/migrate.tmpl2
-rw-r--r--templates/repo/migrate/migrating.tmpl12
-rw-r--r--templates/repo/migrate/onedev.tmpl185
-rw-r--r--templates/repo/navbar.tmpl23
-rw-r--r--templates/repo/projects/view.tmpl9
-rw-r--r--templates/repo/pulls/fork.tmpl138
-rw-r--r--templates/repo/pulls/tab_menu.tmpl6
-rw-r--r--templates/repo/pulse.tmpl8
-rw-r--r--templates/repo/release/list.tmpl43
-rw-r--r--templates/repo/release/new.tmpl32
-rw-r--r--templates/repo/release_tag_header.tmpl4
-rw-r--r--templates/repo/settings/branches.tmpl17
-rw-r--r--templates/repo/settings/collaboration.tmpl123
-rw-r--r--templates/repo/settings/deploy_keys.tmpl13
-rw-r--r--templates/repo/settings/githooks.tmpl16
-rw-r--r--templates/repo/settings/lfs.tmpl4
-rw-r--r--templates/repo/settings/lfs_file.tmpl6
-rw-r--r--templates/repo/settings/lfs_locks.tmpl2
-rw-r--r--templates/repo/settings/navbar.tmpl5
-rw-r--r--templates/repo/settings/options.tmpl46
-rw-r--r--templates/repo/settings/protected_branch.tmpl10
-rw-r--r--templates/repo/settings/public_access.tmpl54
-rw-r--r--templates/repo/settings/push_mirror_sync_modal.tmpl50
-rw-r--r--templates/repo/settings/tags.tmpl4
-rw-r--r--templates/repo/settings/webhook/base.tmpl2
-rw-r--r--templates/repo/settings/webhook/base_list.tmpl17
-rw-r--r--templates/repo/settings/webhook/delete_modal.tmpl10
-rw-r--r--templates/repo/settings/webhook/dingtalk.tmpl3
-rw-r--r--templates/repo/settings/webhook/discord.tmpl3
-rw-r--r--templates/repo/settings/webhook/feishu.tmpl8
-rw-r--r--templates/repo/settings/webhook/gitea.tmpl11
-rw-r--r--templates/repo/settings/webhook/gogs.tmpl11
-rw-r--r--templates/repo/settings/webhook/history.tmpl2
-rw-r--r--templates/repo/settings/webhook/list.tmpl4
-rw-r--r--templates/repo/settings/webhook/matrix.tmpl2
-rw-r--r--templates/repo/settings/webhook/msteams.tmpl3
-rw-r--r--templates/repo/settings/webhook/packagist.tmpl3
-rw-r--r--templates/repo/settings/webhook/settings.tmpl159
-rw-r--r--templates/repo/settings/webhook/slack.tmpl3
-rw-r--r--templates/repo/settings/webhook/telegram.tmpl3
-rw-r--r--templates/repo/settings/webhook/wechatwork.tmpl3
-rw-r--r--templates/repo/star_unstar.tmpl4
-rw-r--r--templates/repo/sub_menu.tmpl9
-rw-r--r--templates/repo/tag/list.tmpl85
-rw-r--r--templates/repo/tag/name.tmpl2
-rw-r--r--templates/repo/unicode_escape_prompt.tmpl10
-rw-r--r--templates/repo/view.tmpl29
-rw-r--r--templates/repo/view_content.tmpl111
-rw-r--r--templates/repo/view_file.tmpl100
-rw-r--r--templates/repo/view_file_tree.tmpl15
-rw-r--r--templates/repo/view_list.tmpl30
-rw-r--r--templates/repo/watch_unwatch.tmpl4
-rw-r--r--templates/repo/wiki/new.tmpl4
-rw-r--r--templates/repo/wiki/pages.tmpl4
-rw-r--r--templates/repo/wiki/revision.tmpl12
-rw-r--r--templates/repo/wiki/start.tmpl2
-rw-r--r--templates/repo/wiki/view.tmpl41
-rw-r--r--templates/shared/actions/runner_badge.tmpl25
-rw-r--r--templates/shared/actions/runner_badge_flat-square.tmpl15
-rw-r--r--templates/shared/actions/runner_badge_flat.tmpl27
-rw-r--r--templates/shared/actions/runner_edit.tmpl2
-rw-r--r--templates/shared/actions/runner_list.tmpl22
-rw-r--r--templates/shared/avatar_upload_crop.tmpl8
-rw-r--r--templates/shared/combomarkdowneditor.tmpl2
-rw-r--r--templates/shared/issueicon.tmpl3
-rw-r--r--templates/shared/issuelist.tmpl22
-rw-r--r--templates/shared/repo/list.tmpl (renamed from templates/explore/repo_list.tmpl)12
-rw-r--r--templates/shared/repo/search.tmpl (renamed from templates/shared/repo_search.tmpl)2
-rw-r--r--templates/shared/search/code/results.tmpl4
-rw-r--r--templates/shared/search/code/search.tmpl8
-rw-r--r--templates/shared/search/combo.tmpl30
-rw-r--r--templates/shared/search/combo_fuzzy.tmpl10
-rw-r--r--templates/shared/search/fuzzy.tmpl10
-rw-r--r--templates/shared/secrets/add_list.tmpl40
-rw-r--r--templates/shared/user/authorlink.tmpl2
-rw-r--r--templates/shared/user/block_user_dialog.tmpl2
-rw-r--r--templates/shared/user/blocked_users.tmpl2
-rw-r--r--templates/shared/user/profile_big_avatar.tmpl2
-rw-r--r--templates/shared/variables/variable_list.tmpl16
-rw-r--r--templates/shared/webhook/icon.tmpl18
-rw-r--r--templates/status/404.tmpl1
-rw-r--r--templates/status/500.tmpl2
-rw-r--r--templates/status/503.tmpl12
-rw-r--r--templates/swagger/ui.tmpl1
-rw-r--r--templates/swagger/v1_input.json6
-rw-r--r--templates/swagger/v1_json.tmpl3336
-rw-r--r--templates/user/auth/captcha.tmpl6
-rw-r--r--templates/user/auth/finalize_openid.tmpl47
-rw-r--r--templates/user/auth/grant.tmpl60
-rw-r--r--templates/user/auth/grant_error.tmpl19
-rw-r--r--templates/user/auth/link_account.tmpl7
-rw-r--r--templates/user/auth/oidc_wellknown.tmpl14
-rw-r--r--templates/user/auth/reset_passwd.tmpl24
-rw-r--r--templates/user/auth/signin.tmpl1
-rw-r--r--templates/user/auth/signin_inner.tmpl15
-rw-r--r--templates/user/auth/signup_inner.tmpl7
-rw-r--r--templates/user/auth/signup_openid_connect.tmpl58
-rw-r--r--templates/user/auth/signup_openid_register.tmpl2
-rw-r--r--templates/user/auth/webauthn.tmpl2
-rw-r--r--templates/user/dashboard/dashboard.tmpl6
-rw-r--r--templates/user/dashboard/feeds.tmpl11
-rw-r--r--templates/user/dashboard/guide.tmpl10
-rw-r--r--templates/user/dashboard/issues.tmpl10
-rw-r--r--templates/user/dashboard/milestones.tmpl10
-rw-r--r--templates/user/dashboard/navbar.tmpl26
-rw-r--r--templates/user/dashboard/repolist.tmpl4
-rw-r--r--templates/user/notification/notification_div.tmpl2
-rw-r--r--templates/user/notification/notification_subscriptions.tmpl6
-rw-r--r--templates/user/profile.tmpl10
-rw-r--r--templates/user/settings/account.tmpl8
-rw-r--r--templates/user/settings/appearance.tmpl2
-rw-r--r--templates/user/settings/applications.tmpl76
-rw-r--r--templates/user/settings/applications_oauth2_list.tmpl54
-rw-r--r--templates/user/settings/hooks.tmpl2
-rw-r--r--templates/user/settings/keys_ssh.tmpl11
-rw-r--r--templates/user/settings/organization.tmpl2
-rw-r--r--templates/user/settings/profile.tmpl10
-rw-r--r--templates/user/settings/repos.tmpl2
-rw-r--r--tests/e2e/e2e_test.go4
-rw-r--r--tests/e2e/utils_e2e.ts11
-rw-r--r--tests/e2e/utils_e2e_test.go2
-rw-r--r--tests/fuzz/fuzz_test.go2
-rw-r--r--tests/gitea-repositories-meta/user2/test_commit_revert.git/HEAD1
-rw-r--r--tests/gitea-repositories-meta/user2/test_commit_revert.git/config8
-rw-r--r--tests/gitea-repositories-meta/user2/test_commit_revert.git/objects/pack/pack-91200c8e6707636a6cc3e0d8101fba08b19dcb91.idxbin1268 -> 0 bytes
-rw-r--r--tests/gitea-repositories-meta/user2/test_commit_revert.git/objects/pack/pack-91200c8e6707636a6cc3e0d8101fba08b19dcb91.packbin609 -> 0 bytes
-rw-r--r--tests/gitea-repositories-meta/user2/test_commit_revert.git/packed-refs3
-rw-r--r--tests/gitea-repositories-meta/user2/test_commit_revert.git/refs/heads/main1
-rw-r--r--tests/integration/actions_delete_run_test.go181
-rw-r--r--tests/integration/actions_job_test.go270
-rw-r--r--tests/integration/actions_log_test.go185
-rw-r--r--tests/integration/actions_runner_modify_test.go150
-rw-r--r--tests/integration/actions_runner_test.go37
-rw-r--r--tests/integration/actions_trigger_test.go1275
-rw-r--r--tests/integration/actions_variables_test.go148
-rw-r--r--tests/integration/admin_user_test.go42
-rw-r--r--tests/integration/api_actions_artifact_v4_test.go275
-rw-r--r--tests/integration/api_actions_delete_run_test.go98
-rw-r--r--tests/integration/api_actions_runner_test.go342
-rw-r--r--tests/integration/api_activitypub_person_test.go60
-rw-r--r--tests/integration/api_admin_org_test.go53
-rw-r--r--tests/integration/api_admin_test.go14
-rw-r--r--tests/integration/api_branch_test.go67
-rw-r--r--tests/integration/api_comment_test.go8
-rw-r--r--tests/integration/api_fork_test.go41
-rw-r--r--tests/integration/api_gitignore_templates_test.go3
-rw-r--r--tests/integration/api_gpg_keys_test.go12
-rw-r--r--tests/integration/api_helper_for_declarative_test.go13
-rw-r--r--tests/integration/api_issue_label_test.go20
-rw-r--r--tests/integration/api_issue_lock_test.go74
-rw-r--r--tests/integration/api_issue_milestone_test.go2
-rw-r--r--tests/integration/api_issue_stopwatch_test.go10
-rw-r--r--tests/integration/api_issue_subscription_test.go8
-rw-r--r--tests/integration/api_issue_test.go16
-rw-r--r--tests/integration/api_issue_tracked_time_test.go6
-rw-r--r--tests/integration/api_keys_test.go4
-rw-r--r--tests/integration/api_label_templates_test.go3
-rw-r--r--tests/integration/api_license_templates_test.go3
-rw-r--r--tests/integration/api_nodeinfo_test.go34
-rw-r--r--tests/integration/api_notification_test.go8
-rw-r--r--tests/integration/api_oauth2_apps_test.go20
-rw-r--r--tests/integration/api_org_test.go232
-rw-r--r--tests/integration/api_packages_arch_test.go94
-rw-r--r--tests/integration/api_packages_cargo_test.go2
-rw-r--r--tests/integration/api_packages_chef_test.go12
-rw-r--r--tests/integration/api_packages_composer_test.go37
-rw-r--r--tests/integration/api_packages_conan_test.go54
-rw-r--r--tests/integration/api_packages_conda_test.go10
-rw-r--r--tests/integration/api_packages_container_test.go137
-rw-r--r--tests/integration/api_packages_cran_test.go8
-rw-r--r--tests/integration/api_packages_debian_test.go2
-rw-r--r--tests/integration/api_packages_generic_test.go7
-rw-r--r--tests/integration/api_packages_goproxy_test.go2
-rw-r--r--tests/integration/api_packages_helm_test.go4
-rw-r--r--tests/integration/api_packages_maven_test.go16
-rw-r--r--tests/integration/api_packages_npm_test.go49
-rw-r--r--tests/integration/api_packages_nuget_test.go121
-rw-r--r--tests/integration/api_packages_pub_test.go2
-rw-r--r--tests/integration/api_packages_pypi_test.go69
-rw-r--r--tests/integration/api_packages_rpm_test.go14
-rw-r--r--tests/integration/api_packages_rubygems_test.go8
-rw-r--r--tests/integration/api_packages_swift_test.go98
-rw-r--r--tests/integration/api_packages_test.go147
-rw-r--r--tests/integration/api_packages_vagrant_test.go4
-rw-r--r--tests/integration/api_private_serv_test.go4
-rw-r--r--tests/integration/api_pull_commits_test.go5
-rw-r--r--tests/integration/api_pull_review_test.go44
-rw-r--r--tests/integration/api_pull_test.go169
-rw-r--r--tests/integration/api_releases_test.go10
-rw-r--r--tests/integration/api_repo_archive_test.go21
-rw-r--r--tests/integration/api_repo_branch_test.go32
-rw-r--r--tests/integration/api_repo_collaborator_test.go188
-rw-r--r--tests/integration/api_repo_file_create_test.go142
-rw-r--r--tests/integration/api_repo_file_delete_test.go32
-rw-r--r--tests/integration/api_repo_file_get_test.go2
-rw-r--r--tests/integration/api_repo_file_update_test.go89
-rw-r--r--tests/integration/api_repo_files_change_test.go118
-rw-r--r--tests/integration/api_repo_files_get_test.go157
-rw-r--r--tests/integration/api_repo_get_contents_list_test.go55
-rw-r--r--tests/integration/api_repo_get_contents_test.go226
-rw-r--r--tests/integration/api_repo_git_blobs_test.go4
-rw-r--r--tests/integration/api_repo_git_commits_test.go22
-rw-r--r--tests/integration/api_repo_git_tags_test.go4
-rw-r--r--tests/integration/api_repo_git_trees_test.go2
-rw-r--r--tests/integration/api_repo_languages_test.go5
-rw-r--r--tests/integration/api_repo_lfs_locks_test.go8
-rw-r--r--tests/integration/api_repo_lfs_migrate_test.go2
-rw-r--r--tests/integration/api_repo_lfs_test.go7
-rw-r--r--tests/integration/api_repo_license_test.go6
-rw-r--r--tests/integration/api_repo_raw_test.go4
-rw-r--r--tests/integration/api_repo_secrets_test.go27
-rw-r--r--tests/integration/api_repo_tags_test.go10
-rw-r--r--tests/integration/api_repo_teams_test.go12
-rw-r--r--tests/integration/api_repo_test.go53
-rw-r--r--tests/integration/api_repo_topic_test.go10
-rw-r--r--tests/integration/api_repo_variables_test.go8
-rw-r--r--tests/integration/api_settings_test.go7
-rw-r--r--tests/integration/api_team_test.go24
-rw-r--r--tests/integration/api_team_user_test.go52
-rw-r--r--tests/integration/api_token_test.go2
-rw-r--r--tests/integration/api_twofa_test.go2
-rw-r--r--tests/integration/api_user_block_test.go18
-rw-r--r--tests/integration/api_user_email_test.go6
-rw-r--r--tests/integration/api_user_follow_test.go8
-rw-r--r--tests/integration/api_user_info_test.go9
-rw-r--r--tests/integration/api_user_search_test.go8
-rw-r--r--tests/integration/api_user_secrets_test.go7
-rw-r--r--tests/integration/api_user_star_test.go74
-rw-r--r--tests/integration/api_user_variables_test.go17
-rw-r--r--tests/integration/api_wiki_test.go24
-rw-r--r--tests/integration/auth_ldap_test.go20
-rw-r--r--tests/integration/benchmarks_test.go69
-rw-r--r--tests/integration/change_default_branch_test.go97
-rw-r--r--tests/integration/cmd_keys_test.go17
-rw-r--r--tests/integration/compare_test.go2
-rw-r--r--tests/integration/db_collation_test.go2
-rw-r--r--tests/integration/delete_user_test.go5
-rw-r--r--tests/integration/dump_restore_test.go31
-rw-r--r--tests/integration/editor_test.go527
-rw-r--r--tests/integration/empty_repo_test.go66
-rw-r--r--tests/integration/ephemeral_actions_runner_deletion_test.go77
-rw-r--r--tests/integration/feed_repo_test.go35
-rw-r--r--tests/integration/feed_user_test.go (renamed from tests/integration/api_feed_user_test.go)25
-rw-r--r--tests/integration/git_general_test.go220
-rw-r--r--tests/integration/git_helper_for_declarative_test.go33
-rw-r--r--tests/integration/git_lfs_ssh_test.go10
-rw-r--r--tests/integration/git_misc_test.go117
-rw-r--r--tests/integration/git_push_test.go20
-rw-r--r--tests/integration/git_smart_http_test.go64
-rw-r--r--tests/integration/gpg_ssh_git_test.go (renamed from tests/integration/gpg_git_test.go)99
-rw-r--r--tests/integration/html_helper.go13
-rw-r--r--tests/integration/incoming_email_test.go4
-rw-r--r--tests/integration/integration_test.go83
-rw-r--r--tests/integration/issue_test.go73
-rw-r--r--tests/integration/lfs_local_endpoint_test.go27
-rw-r--r--tests/integration/lfs_view_test.go18
-rw-r--r--tests/integration/linguist_test.go3
-rw-r--r--tests/integration/links_test.go81
-rw-r--r--tests/integration/markup_external_test.go14
-rw-r--r--tests/integration/migrate_test.go11
-rw-r--r--tests/integration/migration-test/migration_test.go28
-rw-r--r--tests/integration/mirror_pull_test.go37
-rw-r--r--tests/integration/mirror_push_test.go22
-rw-r--r--tests/integration/nonascii_branches_test.go40
-rw-r--r--tests/integration/oauth_test.go54
-rw-r--r--tests/integration/org_count_test.go11
-rw-r--r--tests/integration/org_team_invite_test.go20
-rw-r--r--tests/integration/org_test.go45
-rw-r--r--tests/integration/org_worktime_test.go293
-rw-r--r--tests/integration/project_test.go10
-rw-r--r--tests/integration/pull_commit_test.go41
-rw-r--r--tests/integration/pull_compare_test.go64
-rw-r--r--tests/integration/pull_create_test.go8
-rw-r--r--tests/integration/pull_merge_test.go100
-rw-r--r--tests/integration/pull_review_test.go34
-rw-r--r--tests/integration/pull_status_test.go106
-rw-r--r--tests/integration/pull_update_test.go32
-rw-r--r--tests/integration/release_test.go48
-rw-r--r--tests/integration/rename_branch_test.go11
-rw-r--r--tests/integration/repo_activity_test.go13
-rw-r--r--tests/integration/repo_branch_test.go116
-rw-r--r--tests/integration/repo_commits_search_test.go2
-rw-r--r--tests/integration/repo_commits_test.go42
-rw-r--r--tests/integration/repo_fork_test.go20
-rw-r--r--tests/integration/repo_generate_test.go11
-rw-r--r--tests/integration/repo_merge_upstream_test.go183
-rw-r--r--tests/integration/repo_mergecommit_revert_test.go37
-rw-r--r--tests/integration/repo_search_test.go2
-rw-r--r--tests/integration/repo_tag_test.go22
-rw-r--r--tests/integration/repo_test.go313
-rw-r--r--tests/integration/repo_topic_test.go10
-rw-r--r--tests/integration/repo_webhook_test.go1183
-rw-r--r--tests/integration/repofiles_change_test.go306
-rw-r--r--tests/integration/session_test.go4
-rw-r--r--tests/integration/setting_test.go6
-rw-r--r--tests/integration/signin_test.go84
-rw-r--r--tests/integration/signup_test.go11
-rw-r--r--tests/integration/ssh_key_test.go6
-rw-r--r--tests/integration/timetracking_test.go18
-rw-r--r--tests/integration/user_avatar_test.go96
-rw-r--r--tests/integration/user_settings_test.go48
-rw-r--r--tests/integration/user_test.go29
-rw-r--r--tests/integration/view_test.go40
-rw-r--r--tests/integration/webfinger_test.go12
-rw-r--r--tests/integration/wiki_test.go10
-rw-r--r--tests/integration/workflow_run_api_check_test.go167
-rw-r--r--tests/integration/xss_test.go4
-rw-r--r--tests/mssql.ini.tmpl7
-rw-r--r--tests/mysql.ini.tmpl7
-rw-r--r--tests/pgsql.ini.tmpl7
-rw-r--r--tests/sqlite.ini.tmpl7
-rw-r--r--tests/test_utils.go13
-rw-r--r--tools/generate-svg-vscode-extensions.json570
-rwxr-xr-xtools/generate-svg.js87
-rwxr-xr-xtools/lint-go-gopls.sh4
-rw-r--r--tsconfig.json18
-rw-r--r--updates.config.js5
-rw-r--r--web_src/css/actions.css9
-rw-r--r--web_src/css/base.css402
-rw-r--r--web_src/css/editor/combomarkdowneditor.css64
-rw-r--r--web_src/css/editor/fileeditor.css9
-rw-r--r--web_src/css/features/cropper.css2
-rw-r--r--web_src/css/features/expander.css96
-rw-r--r--web_src/css/features/gitgraph.css72
-rw-r--r--web_src/css/features/projects.css71
-rw-r--r--web_src/css/features/tribute.css32
-rw-r--r--web_src/css/form.css464
-rw-r--r--web_src/css/home.css4
-rw-r--r--web_src/css/index.css7
-rw-r--r--web_src/css/markup/codecopy.css9
-rw-r--r--web_src/css/markup/content.css77
-rw-r--r--web_src/css/modules/animations.css15
-rw-r--r--web_src/css/modules/breadcrumb.css6
-rw-r--r--web_src/css/modules/button.css111
-rw-r--r--web_src/css/modules/checkbox.css10
-rw-r--r--web_src/css/modules/container.css5
-rw-r--r--web_src/css/modules/dimmer.css2
-rw-r--r--web_src/css/modules/grid.css52
-rw-r--r--web_src/css/modules/label.css80
-rw-r--r--web_src/css/modules/list.css1
-rw-r--r--web_src/css/modules/menu.css2
-rw-r--r--web_src/css/modules/modal.css1
-rw-r--r--web_src/css/modules/navbar.css20
-rw-r--r--web_src/css/modules/segment.css7
-rw-r--r--web_src/css/modules/select.css25
-rw-r--r--web_src/css/modules/svg.css4
-rw-r--r--web_src/css/modules/tab.css7
-rw-r--r--web_src/css/modules/table.css25
-rw-r--r--web_src/css/modules/tippy.css8
-rw-r--r--web_src/css/modules/toast.css2
-rw-r--r--web_src/css/org.css87
-rw-r--r--web_src/css/repo.css206
-rw-r--r--web_src/css/repo/clone.css16
-rw-r--r--web_src/css/repo/commit-sign.css3
-rw-r--r--web_src/css/repo/file-view.css92
-rw-r--r--web_src/css/repo/header.css44
-rw-r--r--web_src/css/repo/home-file-list.css30
-rw-r--r--web_src/css/repo/home.css26
-rw-r--r--web_src/css/repo/issue-card.css10
-rw-r--r--web_src/css/repo/issue-label.css25
-rw-r--r--web_src/css/repo/linebutton.css18
-rw-r--r--web_src/css/repo/list-header.css5
-rw-r--r--web_src/css/repo/packages.css25
-rw-r--r--web_src/css/repo/release-tag.css25
-rw-r--r--web_src/css/repo/wiki.css4
-rw-r--r--web_src/css/review.css14
-rw-r--r--web_src/css/shared/flex-list.css13
-rw-r--r--web_src/css/shared/milestone.css2
-rw-r--r--web_src/css/themes/theme-gitea-auto-protanopia-deuteranopia.css4
-rw-r--r--web_src/css/themes/theme-gitea-auto.css4
-rw-r--r--web_src/css/themes/theme-gitea-dark-protanopia-deuteranopia.css4
-rw-r--r--web_src/css/themes/theme-gitea-dark.css5
-rw-r--r--web_src/css/themes/theme-gitea-light-protanopia-deuteranopia.css4
-rw-r--r--web_src/css/themes/theme-gitea-light.css5
-rw-r--r--web_src/fomantic/.npmrc7
-rw-r--r--web_src/fomantic/build/components/api.js1169
-rw-r--r--web_src/fomantic/build/components/dropdown.css1755
-rw-r--r--web_src/fomantic/build/components/dropdown.js4245
-rw-r--r--web_src/fomantic/build/components/form.css1633
-rw-r--r--web_src/fomantic/build/components/modal.css698
-rw-r--r--web_src/fomantic/build/components/modal.js1209
-rw-r--r--web_src/fomantic/build/components/search.css520
-rw-r--r--web_src/fomantic/build/components/search.js1565
-rw-r--r--web_src/fomantic/build/fomantic.css4
-rw-r--r--web_src/fomantic/build/fomantic.js6
-rw-r--r--web_src/fomantic/build/semantic.css5250
-rw-r--r--web_src/fomantic/build/semantic.js11238
-rw-r--r--web_src/fomantic/build/themes/default/assets/fonts/icons.woff2bin79444 -> 0 bytes
-rw-r--r--web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2bin13584 -> 0 bytes
-rw-r--r--web_src/fomantic/package-lock.json8777
-rw-r--r--web_src/fomantic/package.json5
-rw-r--r--web_src/fomantic/theme.config.less19
-rw-r--r--web_src/js/bootstrap.ts7
-rw-r--r--web_src/js/components/ActionRunStatus.vue12
-rw-r--r--web_src/js/components/ActivityHeatmap.vue4
-rw-r--r--web_src/js/components/ContextPopup.vue12
-rw-r--r--web_src/js/components/DashboardRepoList.vue117
-rw-r--r--web_src/js/components/DiffCommitSelector.vue50
-rw-r--r--web_src/js/components/DiffFileList.vue60
-rw-r--r--web_src/js/components/DiffFileTree.vue90
-rw-r--r--web_src/js/components/DiffFileTreeItem.vue91
-rw-r--r--web_src/js/components/PullRequestMergeForm.vue44
-rw-r--r--web_src/js/components/RepoActionView.vue218
-rw-r--r--web_src/js/components/RepoActivityTopAuthors.vue27
-rw-r--r--web_src/js/components/RepoBranchTagSelector.vue114
-rw-r--r--web_src/js/components/RepoCodeFrequency.vue14
-rw-r--r--web_src/js/components/RepoContributors.vue37
-rw-r--r--web_src/js/components/RepoRecentCommits.vue12
-rw-r--r--web_src/js/components/ScopedAccessTokenSelector.vue81
-rw-r--r--web_src/js/components/ViewFileTree.vue38
-rw-r--r--web_src/js/components/ViewFileTreeItem.vue128
-rw-r--r--web_src/js/components/ViewFileTreeStore.ts45
-rw-r--r--web_src/js/features/admin/common.ts191
-rw-r--r--web_src/js/features/autofocus-end.ts6
-rw-r--r--web_src/js/features/captcha.ts13
-rw-r--r--web_src/js/features/citation.ts4
-rw-r--r--web_src/js/features/common-button.test.ts14
-rw-r--r--web_src/js/features/common-button.ts97
-rw-r--r--web_src/js/features/common-fetch-action.ts101
-rw-r--r--web_src/js/features/common-form.ts4
-rw-r--r--web_src/js/features/common-issue-list.ts6
-rw-r--r--web_src/js/features/common-organization.ts2
-rw-r--r--web_src/js/features/common-page.ts105
-rw-r--r--web_src/js/features/comp/ComboMarkdownEditor.ts57
-rw-r--r--web_src/js/features/comp/ConfirmModal.ts37
-rw-r--r--web_src/js/features/comp/Cropper.ts9
-rw-r--r--web_src/js/features/comp/EditorMarkdown.test.ts170
-rw-r--r--web_src/js/features/comp/EditorMarkdown.ts147
-rw-r--r--web_src/js/features/comp/EditorUpload.test.ts12
-rw-r--r--web_src/js/features/comp/EditorUpload.ts74
-rw-r--r--web_src/js/features/comp/LabelEdit.ts14
-rw-r--r--web_src/js/features/comp/QuickSubmit.ts2
-rw-r--r--web_src/js/features/comp/ReactionSelector.ts48
-rw-r--r--web_src/js/features/comp/SearchUserBox.ts4
-rw-r--r--web_src/js/features/comp/TextExpander.ts62
-rw-r--r--web_src/js/features/comp/WebHookEditor.ts6
-rw-r--r--web_src/js/features/contextpopup.ts4
-rw-r--r--web_src/js/features/copycontent.ts24
-rw-r--r--web_src/js/features/dashboard.ts9
-rw-r--r--web_src/js/features/dropzone.ts24
-rw-r--r--web_src/js/features/emoji.ts12
-rw-r--r--web_src/js/features/file-fold.ts6
-rw-r--r--web_src/js/features/file-view.ts76
-rw-r--r--web_src/js/features/heatmap.ts2
-rw-r--r--web_src/js/features/imagediff.ts20
-rw-r--r--web_src/js/features/install.ts19
-rw-r--r--web_src/js/features/issue.ts23
-rw-r--r--web_src/js/features/notification.ts6
-rw-r--r--web_src/js/features/org-team.ts41
-rw-r--r--web_src/js/features/pull-view-file.ts17
-rw-r--r--web_src/js/features/repo-actions.ts48
-rw-r--r--web_src/js/features/repo-code.test.ts17
-rw-r--r--web_src/js/features/repo-code.ts171
-rw-r--r--web_src/js/features/repo-commit.ts29
-rw-r--r--web_src/js/features/repo-common.test.ts22
-rw-r--r--web_src/js/features/repo-common.ts84
-rw-r--r--web_src/js/features/repo-diff-filetree.ts9
-rw-r--r--web_src/js/features/repo-diff.ts194
-rw-r--r--web_src/js/features/repo-editor.ts70
-rw-r--r--web_src/js/features/repo-findfile.ts14
-rw-r--r--web_src/js/features/repo-graph.ts142
-rw-r--r--web_src/js/features/repo-home.ts8
-rw-r--r--web_src/js/features/repo-issue-content.ts4
-rw-r--r--web_src/js/features/repo-issue-edit.ts21
-rw-r--r--web_src/js/features/repo-issue-list.ts23
-rw-r--r--web_src/js/features/repo-issue-pr-form.ts10
-rw-r--r--web_src/js/features/repo-issue-pr-status.ts10
-rw-r--r--web_src/js/features/repo-issue-pull.ts133
-rw-r--r--web_src/js/features/repo-issue-sidebar-combolist.ts25
-rw-r--r--web_src/js/features/repo-issue-sidebar.md5
-rw-r--r--web_src/js/features/repo-issue-sidebar.ts12
-rw-r--r--web_src/js/features/repo-issue.ts337
-rw-r--r--web_src/js/features/repo-legacy.ts25
-rw-r--r--web_src/js/features/repo-migrate.ts6
-rw-r--r--web_src/js/features/repo-migration.ts21
-rw-r--r--web_src/js/features/repo-new.ts39
-rw-r--r--web_src/js/features/repo-projects.ts27
-rw-r--r--web_src/js/features/repo-settings.ts86
-rw-r--r--web_src/js/features/repo-view-file-tree.ts37
-rw-r--r--web_src/js/features/repo-wiki.ts7
-rw-r--r--web_src/js/features/scoped-access-token.ts20
-rw-r--r--web_src/js/features/sshkey-helper.ts2
-rw-r--r--web_src/js/features/stopwatch.ts35
-rw-r--r--web_src/js/features/tablesort.ts2
-rw-r--r--web_src/js/features/tribute.ts49
-rw-r--r--web_src/js/features/user-auth-webauthn.ts12
-rw-r--r--web_src/js/features/user-settings.ts12
-rw-r--r--web_src/js/globals.d.ts21
-rw-r--r--web_src/js/globals.ts5
-rw-r--r--web_src/js/htmx.ts35
-rw-r--r--web_src/js/index-domready.ts177
-rw-r--r--web_src/js/index.ts222
-rw-r--r--web_src/js/markup/anchors.ts25
-rw-r--r--web_src/js/markup/asciicast.ts18
-rw-r--r--web_src/js/markup/codecopy.ts17
-rw-r--r--web_src/js/markup/content.ts25
-rw-r--r--web_src/js/markup/html2markdown.ts14
-rw-r--r--web_src/js/markup/math.ts29
-rw-r--r--web_src/js/markup/mermaid.ts36
-rw-r--r--web_src/js/markup/tasklist.ts124
-rw-r--r--web_src/js/modules/diff-file.test.ts51
-rw-r--r--web_src/js/modules/diff-file.ts82
-rw-r--r--web_src/js/modules/dirauto.ts44
-rw-r--r--web_src/js/modules/fomantic.ts6
-rw-r--r--web_src/js/modules/fomantic/api.ts41
-rw-r--r--web_src/js/modules/fomantic/base.ts8
-rw-r--r--web_src/js/modules/fomantic/dimmer.ts2
-rw-r--r--web_src/js/modules/fomantic/dropdown.test.ts24
-rw-r--r--web_src/js/modules/fomantic/dropdown.ts126
-rw-r--r--web_src/js/modules/fomantic/modal.ts36
-rw-r--r--web_src/js/modules/fomantic/tab.ts19
-rw-r--r--web_src/js/modules/init.ts26
-rw-r--r--web_src/js/modules/observer.ts112
-rw-r--r--web_src/js/modules/stores.ts11
-rw-r--r--web_src/js/modules/tippy.ts37
-rw-r--r--web_src/js/modules/toast.ts31
-rw-r--r--web_src/js/render/pdf.ts19
-rw-r--r--web_src/js/render/plugin.ts10
-rw-r--r--web_src/js/render/plugins/3d-viewer.ts60
-rw-r--r--web_src/js/render/plugins/pdf-viewer.ts20
-rw-r--r--web_src/js/standalone/devtest.ts2
-rw-r--r--web_src/js/standalone/swagger.ts4
-rw-r--r--web_src/js/svg.test.ts3
-rw-r--r--web_src/js/svg.ts30
-rw-r--r--web_src/js/types.ts7
-rw-r--r--web_src/js/utils.test.ts23
-rw-r--r--web_src/js/utils.ts45
-rw-r--r--web_src/js/utils/dom.test.ts24
-rw-r--r--web_src/js/utils/dom.ts117
-rw-r--r--web_src/js/utils/html.test.ts8
-rw-r--r--web_src/js/utils/html.ts32
-rw-r--r--web_src/js/utils/image.test.ts2
-rw-r--r--web_src/js/utils/time.ts2
-rw-r--r--web_src/js/webcomponents/absolute-date.test.ts4
-rw-r--r--web_src/js/webcomponents/absolute-date.ts2
-rw-r--r--web_src/js/webcomponents/overflow-menu.ts45
-rw-r--r--web_src/js/webcomponents/polyfill.test.ts7
-rw-r--r--web_src/js/webcomponents/polyfills.ts16
-rw-r--r--web_src/svg/gitea-feishu.svg1
-rw-r--r--web_src/svg/material-folder-generic.svg1
-rw-r--r--web_src/svg/material-folder-symlink.svg1
-rw-r--r--webpack.config.js9
3263 files changed, 97309 insertions, 110476 deletions
diff --git a/.changelog.yml b/.changelog.yml
index bfdee0c0ca..a7df8779de 100644
--- a/.changelog.yml
+++ b/.changelog.yml
@@ -23,20 +23,25 @@ groups:
labels:
- type/feature
-
- name: API
- labels:
- - modifies/api
- -
name: ENHANCEMENTS
labels:
- type/enhancement
- - type/refactoring
- - topic/ui
+ -
+ name: PERFORMANCE
+ labels:
+ - performance/memory
+ - performance/speed
+ - performance/bigrepo
+ - performance/cpu
-
name: BUGFIXES
labels:
- type/bug
-
+ name: API
+ labels:
+ - modifies/api
+ -
name: TESTING
labels:
- type/testing
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 9f9f6f27d1..104f1cfeed 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,13 +1,13 @@
{
"name": "Gitea DevContainer",
- "image": "mcr.microsoft.com/devcontainers/go:1.23-bookworm",
+ "image": "mcr.microsoft.com/devcontainers/go:1.24-bookworm",
"features": {
// installs nodejs into container
"ghcr.io/devcontainers/features/node:1": {
- "version": "20"
+ "version": "lts"
},
"ghcr.io/devcontainers/features/git-lfs:1.2.2": {},
- "ghcr.io/devcontainers-contrib/features/poetry:2": {},
+ "ghcr.io/devcontainers-extra/features/poetry:2": {},
"ghcr.io/devcontainers/features/python:1": {
"version": "3.12"
},
diff --git a/.dockerignore b/.dockerignore
index b696e1603c..843f12a7be 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -36,15 +36,6 @@ _testmain.go
coverage.all
cpu.out
-/modules/migration/bindata.go
-/modules/migration/bindata.go.hash
-/modules/options/bindata.go
-/modules/options/bindata.go.hash
-/modules/public/bindata.go
-/modules/public/bindata.go.hash
-/modules/templates/bindata.go
-/modules/templates/bindata.go.hash
-
*.db
*.log
@@ -79,18 +70,6 @@ cpu.out
/public/assets/fonts
/public/assets/img/avatar
/vendor
-/web_src/fomantic/node_modules
-/web_src/fomantic/build/*
-!/web_src/fomantic/build/semantic.js
-!/web_src/fomantic/build/semantic.css
-!/web_src/fomantic/build/themes
-/web_src/fomantic/build/themes/*
-!/web_src/fomantic/build/themes/default
-/web_src/fomantic/build/themes/default/assets/*
-!/web_src/fomantic/build/themes/default/assets/fonts
-/web_src/fomantic/build/themes/default/assets/fonts/*
-!/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
-!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
/VERSION
/.air
/.go-licenses
diff --git a/.editorconfig b/.editorconfig
index c0946ac997..13aa8d50f0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -12,11 +12,15 @@ insert_final_newline = true
[*.{go,tmpl,html}]
indent_style = tab
+[go.*]
+indent_style = tab
+
[templates/custom/*.tmpl]
insert_final_newline = false
[templates/swagger/v1_json.tmpl]
indent_style = space
+insert_final_newline = false
[templates/user/auth/oidc_wellknown.tmpl]
indent_style = space
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index f52da3fa5d..57c6b19600 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -1,3 +1,4 @@
+const vitestPlugin = require('@vitest/eslint-plugin');
const restrictedSyntax = ['WithStatement', 'ForInStatement', 'LabeledStatement', 'SequenceExpression'];
module.exports = {
@@ -37,8 +38,6 @@ module.exports = {
'eslint-plugin-regexp',
'eslint-plugin-sonarjs',
'eslint-plugin-unicorn',
- 'eslint-plugin-vitest',
- 'eslint-plugin-vitest-globals',
'eslint-plugin-wc',
],
env: {
@@ -47,6 +46,13 @@ module.exports = {
},
overrides: [
{
+ files: ['**/*.cjs'],
+ rules: {
+ 'import-x/no-commonjs': [0],
+ '@typescript-eslint/no-require-imports': [0],
+ },
+ },
+ {
files: ['web_src/**/*'],
globals: {
__webpack_public_path__: true,
@@ -82,59 +88,59 @@ module.exports = {
},
{
files: ['**/*.test.*', 'web_src/js/test/setup.ts'],
- env: {
- 'vitest-globals/env': true,
- },
+ plugins: ['@vitest/eslint-plugin'],
+ globals: vitestPlugin.environments.env.globals,
rules: {
- 'vitest/consistent-test-filename': [0],
- 'vitest/consistent-test-it': [0],
- 'vitest/expect-expect': [0],
- 'vitest/max-expects': [0],
- 'vitest/max-nested-describe': [0],
- 'vitest/no-alias-methods': [0],
- 'vitest/no-commented-out-tests': [0],
- 'vitest/no-conditional-expect': [0],
- 'vitest/no-conditional-in-test': [0],
- 'vitest/no-conditional-tests': [0],
- 'vitest/no-disabled-tests': [0],
- 'vitest/no-done-callback': [0],
- 'vitest/no-duplicate-hooks': [0],
- 'vitest/no-focused-tests': [0],
- 'vitest/no-hooks': [0],
- 'vitest/no-identical-title': [2],
- 'vitest/no-interpolation-in-snapshots': [0],
- 'vitest/no-large-snapshots': [0],
- 'vitest/no-mocks-import': [0],
- 'vitest/no-restricted-matchers': [0],
- 'vitest/no-restricted-vi-methods': [0],
- 'vitest/no-standalone-expect': [0],
- 'vitest/no-test-prefixes': [0],
- 'vitest/no-test-return-statement': [0],
- 'vitest/prefer-called-with': [0],
- 'vitest/prefer-comparison-matcher': [0],
- 'vitest/prefer-each': [0],
- 'vitest/prefer-equality-matcher': [0],
- 'vitest/prefer-expect-resolves': [0],
- 'vitest/prefer-hooks-in-order': [0],
- 'vitest/prefer-hooks-on-top': [2],
- 'vitest/prefer-lowercase-title': [0],
- 'vitest/prefer-mock-promise-shorthand': [0],
- 'vitest/prefer-snapshot-hint': [0],
- 'vitest/prefer-spy-on': [0],
- 'vitest/prefer-strict-equal': [0],
- 'vitest/prefer-to-be': [0],
- 'vitest/prefer-to-be-falsy': [0],
- 'vitest/prefer-to-be-object': [0],
- 'vitest/prefer-to-be-truthy': [0],
- 'vitest/prefer-to-contain': [0],
- 'vitest/prefer-to-have-length': [0],
- 'vitest/prefer-todo': [0],
- 'vitest/require-hook': [0],
- 'vitest/require-to-throw-message': [0],
- 'vitest/require-top-level-describe': [0],
- 'vitest/valid-describe-callback': [2],
- 'vitest/valid-expect': [2],
- 'vitest/valid-title': [2],
+ 'github/unescaped-html-literal': [0],
+ '@vitest/consistent-test-filename': [0],
+ '@vitest/consistent-test-it': [0],
+ '@vitest/expect-expect': [0],
+ '@vitest/max-expects': [0],
+ '@vitest/max-nested-describe': [0],
+ '@vitest/no-alias-methods': [0],
+ '@vitest/no-commented-out-tests': [0],
+ '@vitest/no-conditional-expect': [0],
+ '@vitest/no-conditional-in-test': [0],
+ '@vitest/no-conditional-tests': [0],
+ '@vitest/no-disabled-tests': [0],
+ '@vitest/no-done-callback': [0],
+ '@vitest/no-duplicate-hooks': [0],
+ '@vitest/no-focused-tests': [2],
+ '@vitest/no-hooks': [0],
+ '@vitest/no-identical-title': [2],
+ '@vitest/no-interpolation-in-snapshots': [0],
+ '@vitest/no-large-snapshots': [0],
+ '@vitest/no-mocks-import': [0],
+ '@vitest/no-restricted-matchers': [0],
+ '@vitest/no-restricted-vi-methods': [0],
+ '@vitest/no-standalone-expect': [0],
+ '@vitest/no-test-prefixes': [0],
+ '@vitest/no-test-return-statement': [0],
+ '@vitest/prefer-called-with': [0],
+ '@vitest/prefer-comparison-matcher': [0],
+ '@vitest/prefer-each': [0],
+ '@vitest/prefer-equality-matcher': [0],
+ '@vitest/prefer-expect-resolves': [0],
+ '@vitest/prefer-hooks-in-order': [0],
+ '@vitest/prefer-hooks-on-top': [2],
+ '@vitest/prefer-lowercase-title': [0],
+ '@vitest/prefer-mock-promise-shorthand': [0],
+ '@vitest/prefer-snapshot-hint': [0],
+ '@vitest/prefer-spy-on': [0],
+ '@vitest/prefer-strict-equal': [0],
+ '@vitest/prefer-to-be': [0],
+ '@vitest/prefer-to-be-falsy': [0],
+ '@vitest/prefer-to-be-object': [0],
+ '@vitest/prefer-to-be-truthy': [0],
+ '@vitest/prefer-to-contain': [0],
+ '@vitest/prefer-to-have-length': [0],
+ '@vitest/prefer-todo': [0],
+ '@vitest/require-hook': [0],
+ '@vitest/require-to-throw-message': [0],
+ '@vitest/require-top-level-describe': [0],
+ '@vitest/valid-describe-callback': [2],
+ '@vitest/valid-expect': [2],
+ '@vitest/valid-title': [2],
},
},
{
@@ -150,7 +156,7 @@ module.exports = {
'eslint-plugin-vue-scoped-css',
],
extends: [
- 'plugin:vue/vue3-recommended',
+ 'plugin:vue/recommended',
'plugin:vue-scoped-css/vue3-recommended',
],
rules: {
@@ -163,7 +169,7 @@ module.exports = {
{
files: ['tests/e2e/**'],
plugins: [
- 'eslint-plugin-playwright'
+ 'eslint-plugin-playwright',
],
extends: [
'plugin:playwright/recommended',
@@ -331,7 +337,7 @@ module.exports = {
'@typescript-eslint/no-unsafe-unary-minus': [2],
'@typescript-eslint/no-unused-expressions': [0],
'@typescript-eslint/no-unused-vars': [2, {vars: 'all', args: 'all', caughtErrors: 'all', ignoreRestSiblings: false, argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_'}],
- '@typescript-eslint/no-use-before-define': [0],
+ '@typescript-eslint/no-use-before-define': [2, {functions: false, classes: true, variables: true, allowNamedExports: true, typedefs: false, enums: false, ignoreTypeReferences: true}],
'@typescript-eslint/no-useless-constructor': [0],
'@typescript-eslint/no-useless-empty-export': [0],
'@typescript-eslint/no-wrapper-object-types': [2],
@@ -403,7 +409,7 @@ module.exports = {
'github/a11y-svg-has-accessible-name': [0],
'github/array-foreach': [0],
'github/async-currenttarget': [2],
- 'github/async-preventdefault': [2],
+ 'github/async-preventdefault': [0], // https://github.com/github/eslint-plugin-github/issues/599
'github/authenticity-token': [0],
'github/get-attribute': [0],
'github/js-class-name': [0],
@@ -418,7 +424,7 @@ module.exports = {
'github/no-useless-passive': [2],
'github/prefer-observers': [2],
'github/require-passive-events': [2],
- 'github/unescaped-html-literal': [0],
+ 'github/unescaped-html-literal': [2],
'grouped-accessor-pairs': [2],
'guard-for-in': [0],
'id-blacklist': [0],
@@ -688,7 +694,7 @@ module.exports = {
'no-unused-labels': [2],
'no-unused-private-class-members': [2],
'no-unused-vars': [0], // handled by @typescript-eslint/no-unused-vars
- 'no-use-before-define': [2, {functions: false, classes: true, variables: true, allowNamedExports: true}],
+ 'no-use-before-define': [0], // handled by @typescript-eslint/no-use-before-define
'no-use-extend-native/no-use-extend-native': [2],
'no-useless-backreference': [2],
'no-useless-call': [2],
diff --git a/.gitattributes b/.gitattributes
index 9fb4a4e83d..e218bbe25d 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -4,8 +4,7 @@
/assets/*.json linguist-generated
/public/assets/img/svg/*.svg linguist-generated
/templates/swagger/v1_json.tmpl linguist-generated
+/options/fileicon/** linguist-generated
/vendor/** -text -eol linguist-vendored
-/web_src/fomantic/build/** linguist-generated
-/web_src/fomantic/_site/globals/site.variables linguist-language=Less
/web_src/js/vendor/** -text -eol linguist-vendored
Dockerfile.* linguist-language=Dockerfile
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index d37ce219c3..d409f18cd9 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -13,5 +13,5 @@ contact_links:
url: https://docs.gitea.com/help/faq
about: Please check if your question isn't mentioned here.
- name: Crowdin Translations
- url: https://crowdin.com/project/gitea
+ url: https://translate.gitea.com
about: Translations are managed here.
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 46efbcb194..0af43cd029 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -41,7 +41,7 @@ modifies/internal:
- ".dockerignore"
- "docker/**"
- ".editorconfig"
- - ".eslintrc.yaml"
+ - ".eslintrc.cjs"
- ".golangci.yml"
- ".gitpod.yml"
- ".markdownlint.yaml"
@@ -49,7 +49,7 @@ modifies/internal:
- "stylelint.config.js"
- ".yamllint.yaml"
- ".github/**"
- - ".gitea/"
+ - ".gitea/**"
- ".devcontainer/**"
- "build.go"
- "build/**"
@@ -73,9 +73,9 @@ modifies/go:
modifies/frontend:
- changed-files:
- any-glob-to-any-file:
- - "**/*.js"
- - "**/*.ts"
- - "**/*.vue"
+ - "*.js"
+ - "*.ts"
+ - "web_src/**"
docs-update-needed:
- changed-files:
diff --git a/.github/workflows/cron-licenses.yml b/.github/workflows/cron-licenses.yml
index cd8386ecc5..c34066d318 100644
--- a/.github/workflows/cron-licenses.yml
+++ b/.github/workflows/cron-licenses.yml
@@ -1,8 +1,8 @@
name: cron-licenses
on:
- schedule:
- - cron: "7 0 * * 1" # every Monday at 00:07 UTC
+ # schedule:
+ # - cron: "7 0 * * 1" # every Monday at 00:07 UTC
workflow_dispatch:
jobs:
@@ -15,7 +15,7 @@ jobs:
with:
go-version-file: go.mod
check-latest: true
- - run: make generate-license generate-gitignore
+ - run: make generate-gitignore
timeout-minutes: 40
- name: push translations to repo
uses: appleboy/git-push-action@v0.0.3
diff --git a/.github/workflows/files-changed.yml b/.github/workflows/files-changed.yml
index 7c1fb02442..be27537924 100644
--- a/.github/workflows/files-changed.yml
+++ b/.github/workflows/files-changed.yml
@@ -51,14 +51,16 @@ jobs:
- "options/locale/locale_en-US.ini"
frontend:
- - "**/*.js"
+ - "*.js"
+ - "*.ts"
- "web_src/**"
+ - "tools/*.js"
+ - "tools/*.ts"
- "assets/emoji.json"
- "package.json"
- "package-lock.json"
- "Makefile"
- - ".eslintrc.yaml"
- - "stylelint.config.js"
+ - ".eslintrc.cjs"
- ".npmrc"
docs:
@@ -85,6 +87,7 @@ jobs:
swagger:
- "templates/swagger/v1_json.tmpl"
+ - "templates/swagger/v1_input.json"
- "Makefile"
- "package.json"
- "package-lock.json"
diff --git a/.github/workflows/pull-compliance.yml b/.github/workflows/pull-compliance.yml
index 7e988e0449..f6720bf2f6 100644
--- a/.github/workflows/pull-compliance.yml
+++ b/.github/workflows/pull-compliance.yml
@@ -37,7 +37,7 @@ jobs:
python-version: "3.12"
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: pip install poetry
@@ -66,7 +66,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend
@@ -95,7 +95,7 @@ jobs:
go-version-file: go.mod
check-latest: true
- run: make deps-backend deps-tools
- - run: make lint-go-windows lint-go-vet
+ - run: make lint-go-windows lint-go-gitea-vet
env:
TAGS: bindata sqlite sqlite_unlock_notify
GOOS: windows
@@ -137,7 +137,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend
@@ -186,7 +186,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend
diff --git a/.github/workflows/pull-db-tests.yml b/.github/workflows/pull-db-tests.yml
index 0b23de0a66..55c2d2bf5e 100644
--- a/.github/workflows/pull-db-tests.yml
+++ b/.github/workflows/pull-db-tests.yml
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
services:
pgsql:
- image: postgres:12
+ image: postgres:14
env:
POSTGRES_DB: test
POSTGRES_PASSWORD: postgres
@@ -98,7 +98,7 @@ jobs:
ports:
- "9200:9200"
meilisearch:
- image: getmeili/meilisearch:v1.2.0
+ image: getmeili/meilisearch:v1
env:
MEILI_ENV: development # disable auth
ports:
@@ -202,12 +202,10 @@ jobs:
test-mssql:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed
- # specifying the version of ubuntu in use as mssql fails on newer kernels
- # pending resolution from vendor
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
services:
mssql:
- image: mcr.microsoft.com/mssql/server:2017-latest
+ image: mcr.microsoft.com/mssql/server:2019-latest
env:
ACCEPT_EULA: Y
MSSQL_PID: Standard
diff --git a/.github/workflows/pull-e2e-tests.yml b/.github/workflows/pull-e2e-tests.yml
index b84c69e4a0..cc3fbd9c34 100644
--- a/.github/workflows/pull-e2e-tests.yml
+++ b/.github/workflows/pull-e2e-tests.yml
@@ -12,7 +12,9 @@ jobs:
uses: ./.github/workflows/files-changed.yml
test-e2e:
- if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
+ # the "test-e2e" won't pass, and it seems that there is no useful test, so skip
+ # if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
+ if: false
needs: files-changed
runs-on: ubuntu-latest
steps:
@@ -23,7 +25,7 @@ jobs:
check-latest: true
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend frontend deps-backend
diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml
index 2264c9e822..c2cc14f771 100644
--- a/.github/workflows/release-nightly.yml
+++ b/.github/workflows/release-nightly.yml
@@ -22,7 +22,7 @@ jobs:
check-latest: true
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend
@@ -59,6 +59,8 @@ jobs:
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
nightly-docker-rootful:
runs-on: namespace-profile-gitea-release-docker
+ permissions:
+ packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -85,17 +87,27 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Login to GHCR using PAT
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
- name: fetch go modules
run: make vendor
- name: build rootful docker image
uses: docker/build-push-action@v5
with:
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
- tags: gitea/gitea:${{ steps.clean_name.outputs.branch }}
+ tags: |-
+ gitea/gitea:${{ steps.clean_name.outputs.branch }}
+ ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}
nightly-docker-rootless:
runs-on: namespace-profile-gitea-release-docker
+ permissions:
+ packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -122,6 +134,12 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Login to GHCR using PAT
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
- name: fetch go modules
run: make vendor
- name: build rootless docker image
@@ -131,4 +149,6 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
file: Dockerfile.rootless
- tags: gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
+ tags: |-
+ gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
+ ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
diff --git a/.github/workflows/release-tag-rc.yml b/.github/workflows/release-tag-rc.yml
index a406602dc0..c9c15c31a0 100644
--- a/.github/workflows/release-tag-rc.yml
+++ b/.github/workflows/release-tag-rc.yml
@@ -23,7 +23,7 @@ jobs:
check-latest: true
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend
@@ -69,6 +69,8 @@ jobs:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
docker-rootful:
runs-on: namespace-profile-gitea-release-docker
+ permissions:
+ packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -79,7 +81,9 @@ jobs:
- uses: docker/metadata-action@v5
id: meta
with:
- images: gitea/gitea
+ images: |-
+ gitea/gitea
+ ghcr.io/go-gitea/gitea
flavor: |
latest=false
# 1.2.3-rc0
@@ -90,16 +94,24 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Login to GHCR using PAT
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
- name: build rootful docker image
uses: docker/build-push-action@v5
with:
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
docker-rootless:
runs-on: namespace-profile-gitea-release-docker
+ permissions:
+ packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -110,7 +122,9 @@ jobs:
- uses: docker/metadata-action@v5
id: meta
with:
- images: gitea/gitea
+ images: |-
+ gitea/gitea
+ ghcr.io/go-gitea/gitea
# each tag below will have the suffix of -rootless
flavor: |
latest=false
@@ -123,11 +137,17 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Login to GHCR using PAT
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
- name: build rootless docker image
uses: docker/build-push-action@v5
with:
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
file: Dockerfile.rootless
tags: ${{ steps.meta.outputs.tags }}
diff --git a/.github/workflows/release-tag-version.yml b/.github/workflows/release-tag-version.yml
index f67b76f408..ae717c7cec 100644
--- a/.github/workflows/release-tag-version.yml
+++ b/.github/workflows/release-tag-version.yml
@@ -14,6 +14,8 @@ concurrency:
jobs:
binary:
runs-on: namespace-profile-gitea-release-binary
+ permissions:
+ packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -25,7 +27,7 @@ jobs:
check-latest: true
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend
@@ -71,6 +73,8 @@ jobs:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
docker-rootful:
runs-on: namespace-profile-gitea-release-docker
+ permissions:
+ packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -81,26 +85,34 @@ jobs:
- uses: docker/metadata-action@v5
id: meta
with:
- images: gitea/gitea
+ images: |-
+ gitea/gitea
+ ghcr.io/go-gitea/gitea
# this will generate tags in the following format:
# latest
# 1
# 1.2
# 1.2.3
tags: |
+ type=semver,pattern={{version}}
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
- type=semver,pattern={{version}}
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Login to GHCR using PAT
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
- name: build rootful docker image
uses: docker/build-push-action@v5
with:
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
@@ -116,7 +128,9 @@ jobs:
- uses: docker/metadata-action@v5
id: meta
with:
- images: gitea/gitea
+ images: |-
+ gitea/gitea
+ ghcr.io/go-gitea/gitea
# each tag below will have the suffix of -rootless
flavor: |
suffix=-rootless,onlatest=true
@@ -126,19 +140,25 @@ jobs:
# 1.2
# 1.2.3
tags: |
+ type=semver,pattern={{version}}
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
- type=semver,pattern={{version}}
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Login to GHCR using PAT
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
- name: build rootless docker image
uses: docker/build-push-action@v5
with:
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
file: Dockerfile.rootless
tags: ${{ steps.meta.outputs.tags }}
diff --git a/.gitignore b/.gitignore
index 619cb1cabb..0791a17c71 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@ _test
# IntelliJ
.idea
+.run
# IntelliJ Gateway
.uuid
@@ -21,6 +22,9 @@ _test
.vscode
__debug_bin*
+# Visual Studio
+/.vs/
+
*.cgo1.go
*.cgo2.c
_cgo_defun.c
@@ -38,14 +42,10 @@ _testmain.go
coverage.all
cpu.out
-/modules/migration/bindata.go
-/modules/migration/bindata.go.hash
-/modules/options/bindata.go
-/modules/options/bindata.go.hash
-/modules/public/bindata.go
-/modules/public/bindata.go.hash
-/modules/templates/bindata.go
-/modules/templates/bindata.go.hash
+/modules/migration/bindata.*
+/modules/options/bindata.*
+/modules/public/bindata.*
+/modules/templates/bindata.*
*.db
*.log
@@ -83,18 +83,6 @@ cpu.out
/public/assets/fonts
/public/assets/licenses.txt
/vendor
-/web_src/fomantic/node_modules
-/web_src/fomantic/build/*
-!/web_src/fomantic/build/semantic.js
-!/web_src/fomantic/build/semantic.css
-!/web_src/fomantic/build/themes
-/web_src/fomantic/build/themes/*
-!/web_src/fomantic/build/themes/default
-/web_src/fomantic/build/themes/default/assets/*
-!/web_src/fomantic/build/themes/default/assets/fonts
-/web_src/fomantic/build/themes/default/assets/fonts/*
-!/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
-!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
/VERSION
/.air
/.go-licenses
diff --git a/.golangci.yml b/.golangci.yml
index c39d7ac5f2..70efd288ff 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,7 +1,9 @@
+version: "2"
+output:
+ sort-order:
+ - file
linters:
- enable-all: false
- disable-all: true
- fast: false
+ default: none
enable:
- bidichk
- depguard
@@ -9,139 +11,170 @@ linters:
- errcheck
- forbidigo
- gocritic
- - gofmt
- - gofumpt
- - gosimple
- govet
- ineffassign
+ - mirror
- nakedret
- nolintlint
+ - perfsprint
- revive
- staticcheck
- - stylecheck
- - tenv
- testifylint
- - typecheck
- unconvert
- - unused
- unparam
+ - unused
+ - usestdlibvars
+ - usetesting
- wastedassign
-
-run:
- timeout: 10m
-
-output:
- sort-results: true
- sort-order: [file]
- show-stats: true
-
-linters-settings:
- testifylint:
- disable:
- - go-require
- - require-error
- stylecheck:
- checks: ["all", "-ST1005", "-ST1003"]
- nakedret:
- max-func-lines: 0
- gocritic:
- disabled-checks:
- - ifElseChain
- - singleCaseSwitch # Every time this occurred in the code, there was no other way.
- revive:
- severity: error
- rules:
- - name: atomic
- - name: bare-return
- - name: blank-imports
- - name: constant-logical-expr
- - name: context-as-argument
- - name: context-keys-type
- - name: dot-imports
- - name: duplicated-imports
- - name: empty-lines
- - name: error-naming
- - name: error-return
- - name: error-strings
- - name: errorf
- - name: exported
- - name: identical-branches
- - name: if-return
- - name: increment-decrement
- - name: indent-error-flow
- - name: modifies-value-receiver
- - name: package-comments
- - name: range
- - name: receiver-naming
- - name: redefines-builtin-id
- - name: string-of-int
- - name: superfluous-else
- - name: time-naming
- - name: unconditional-recursion
- - name: unexported-return
- - name: unreachable-code
- - name: var-declaration
- - name: var-naming
- gofumpt:
- extra-rules: true
- depguard:
+ settings:
+ depguard:
+ rules:
+ main:
+ deny:
+ - pkg: encoding/json
+ desc: use gitea's modules/json instead of encoding/json
+ - pkg: github.com/unknwon/com
+ desc: use gitea's util and replacements
+ - pkg: io/ioutil
+ desc: use os or io instead
+ - pkg: golang.org/x/exp
+ desc: it's experimental and unreliable
+ - pkg: code.gitea.io/gitea/modules/git/internal
+ desc: do not use the internal package, use AddXxx function instead
+ - pkg: gopkg.in/ini.v1
+ desc: do not use the ini package, use gitea's config system instead
+ - pkg: gitea.com/go-chi/cache
+ desc: do not use the go-chi cache package, use gitea's cache system
+ nolintlint:
+ allow-unused: false
+ require-explanation: true
+ require-specific: true
+ gocritic:
+ disabled-checks:
+ - ifElseChain
+ - singleCaseSwitch # Every time this occurred in the code, there was no other way.
+ revive:
+ severity: error
+ rules:
+ - name: atomic
+ - name: bare-return
+ - name: blank-imports
+ - name: constant-logical-expr
+ - name: context-as-argument
+ - name: context-keys-type
+ - name: dot-imports
+ - name: duplicated-imports
+ - name: empty-lines
+ - name: error-naming
+ - name: error-return
+ - name: error-strings
+ - name: errorf
+ - name: exported
+ - name: identical-branches
+ - name: if-return
+ - name: increment-decrement
+ - name: indent-error-flow
+ - name: modifies-value-receiver
+ - name: package-comments
+ - name: range
+ - name: receiver-naming
+ - name: redefines-builtin-id
+ - name: string-of-int
+ - name: superfluous-else
+ - name: time-naming
+ - name: unconditional-recursion
+ - name: unexported-return
+ - name: unreachable-code
+ - name: var-declaration
+ - name: var-naming
+ arguments:
+ - [] # AllowList - do not remove as args for the rule are positional and won't work without lists first
+ - [] # DenyList
+ - - skip-package-name-checks: true # supress errors from underscore in migration packages
+ staticcheck:
+ checks:
+ - all
+ - -ST1003
+ - -ST1005
+ - -QF1001
+ - -QF1006
+ - -QF1008
+ testifylint:
+ disable:
+ - go-require
+ - require-error
+ usetesting:
+ os-temp-dir: true
+ exclusions:
+ generated: lax
+ presets:
+ - comments
+ - common-false-positives
+ - legacy
+ - std-error-handling
rules:
- main:
- deny:
- - pkg: encoding/json
- desc: use gitea's modules/json instead of encoding/json
- - pkg: github.com/unknwon/com
- desc: use gitea's util and replacements
- - pkg: io/ioutil
- desc: use os or io instead
- - pkg: golang.org/x/exp
- desc: it's experimental and unreliable
- - pkg: code.gitea.io/gitea/modules/git/internal
- desc: do not use the internal package, use AddXxx function instead
- - pkg: gopkg.in/ini.v1
- desc: do not use the ini package, use gitea's config system instead
- - pkg: gitea.com/go-chi/cache
- desc: do not use the go-chi cache package, use gitea's cache system
-
+ - linters:
+ - dupl
+ - errcheck
+ - gocyclo
+ - gosec
+ - staticcheck
+ - unparam
+ path: _test\.go
+ - linters:
+ - dupl
+ - errcheck
+ - gocyclo
+ - gosec
+ path: models/migrations/v
+ - linters:
+ - forbidigo
+ path: cmd
+ - linters:
+ - dupl
+ text: (?i)webhook
+ - linters:
+ - gocritic
+ text: (?i)`ID' should not be capitalized
+ - linters:
+ - deadcode
+ - unused
+ text: (?i)swagger
+ - linters:
+ - staticcheck
+ text: (?i)argument x is overwritten before first use
+ - linters:
+ - gocritic
+ text: '(?i)commentFormatting: put a space between `//` and comment text'
+ - linters:
+ - gocritic
+ text: '(?i)exitAfterDefer:'
+ paths:
+ - node_modules
+ - public
+ - web_src
+ - third_party$
+ - builtin$
+ - examples$
issues:
max-issues-per-linter: 0
max-same-issues: 0
- exclude-dirs: [node_modules, public, web_src]
- exclude-case-sensitive: true
- exclude-rules:
- - path: _test\.go
- linters:
- - gocyclo
- - errcheck
- - dupl
- - gosec
- - unparam
- - staticcheck
- - path: models/migrations/v
- linters:
- - gocyclo
- - errcheck
- - dupl
- - gosec
- - path: cmd
- linters:
- - forbidigo
- - text: "webhook"
- linters:
- - dupl
- - text: "`ID' should not be capitalized"
- linters:
- - gocritic
- - text: "swagger"
- linters:
- - unused
- - deadcode
- - text: "argument x is overwritten before first use"
- linters:
- - staticcheck
- - text: "commentFormatting: put a space between `//` and comment text"
- linters:
- - gocritic
- - text: "exitAfterDefer:"
- linters:
- - gocritic
+formatters:
+ enable:
+ - gofmt
+ - gofumpt
+ settings:
+ gofumpt:
+ extra-rules: true
+ exclusions:
+ generated: lax
+ paths:
+ - node_modules
+ - public
+ - web_src
+ - third_party$
+ - builtin$
+ - examples$
+
+run:
+ timeout: 10m
diff --git a/.ignore b/.ignore
index 5b96dabd38..29912ad5c3 100644
--- a/.ignore
+++ b/.ignore
@@ -1,9 +1,6 @@
*.min.css
*.min.js
/assets/*.json
-/modules/options/bindata.go
-/modules/public/bindata.go
-/modules/templates/bindata.go
/options/gitignore
/options/license
/public/assets
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000000..88ff1591a4
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,2 @@
+Unknwon <u@gogs.io> <joe2010xtmf@163.com>
+Unknwon <u@gogs.io> æ— é—» <u@gogs.io>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ab8c2ac223..b72ac4849a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,984 @@ This changelog goes through the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.com).
+## [1.24.0](https://github.com/go-gitea/gitea/releases/tag/1.24.0) - 2025-05-26
+
+* BREAKING
+ * Make Gitea always use its internal config, ignore `/etc/gitconfig` (#33076)
+ * Improve log format (#33814)
+ * Fix markdown render behaviors (#34122)
+ * Add package version api endpoints (#34173)
+
+* FEATURES
+ * Enforce two-factor auth (2FA: TOTP or WebAuthn) (#34187)
+ * Add fullscreen mode as a more efficient operation way to view projects (#34081)
+ * Add anonymous access support for private/unlisted repositories (#34051)
+ * Support public code/issue access for private repositories (#33127)
+ * Add middleware for request prioritization (#33951)
+ * Add cli flags LDAP group configuration (#33933)
+ * Add file tree to file view page (#32721)
+ * Add material icons for file list (#33837)
+ * Artifacts download api for artifact actions v4 (#33510)
+ * Support choose email when creating a commit via web UI (#33432)
+ * Add basic auth support to rss/atom feeds (#33371)
+ * Add sorting by exclusive labels (issue priority) (#33206)
+ * Add sub issue list support (#32940)
+ * Private README.md for organization (#32872)
+ * Email option to embed images as base64 instead of link (#32061)
+ * Option to delay conflict checking of old pull requests until page view (#27779)
+ * Worktime tracking for the organization level (#19808)
+
+* PERFORMANCE
+ * Add cache for common package queries (#22491)
+ * Move issue pin to an standalone table for querying performance (#33452)
+ * Improve commits list performance to reduce unnecessary database queries (#33528)
+ * Optimize total count of feed when loading activities in user dashboard. (#33841)
+ * Optimize heatmap query (#33853)
+ * Only use prev and next buttons for pagination on user dashboard (#33981)
+ * Improve pull request list API performance (#34052)
+ * Cache GPG keys, emails and users when list commits (#34086)
+ * Refactor Git Attribute & performance optimization (#34154)
+ * Performance optimization for tags synchronization (#34355) #34522
+
+* ENHANCEMENTS
+ * Code
+ * Display when a release attachment was uploaded (#34261)
+ * Support creating relative link to raw path in markdown (#34105)
+ * Improve code block readability and isolate copy button (#34009)
+ * Improve repository commit view (#33877)
+ * Full-file syntax highlighting for diff pages (#33766)
+ * Clone repository with Tea CLI (#33725)
+ * Improve sync fork behavior (#33319)
+ * Make git clone URL could use current signed-in user (#33091)
+ * Add submodule diff links (#33097)
+ * Link to tree views of submodules if possible (#33424)
+ * Only keep popular licenses (#33832)
+ * De-emphasize signed commits (#31160)
+
+ * Actions
+ * Add flat-square action badge style (#34062)
+ * Update action status badge layout (#34018)
+ * Download actions job logs from API (#33858)
+ * Always show the "rerun" button for action jobs (#33692)
+ * Add auto-expanding running actions step (#30058)
+ * Update status check for all supported on.pull_request.types in Gitea (#33117)
+ * Workflow_dispatch use workflow from trigger branch (#33098)
+ * Add action auto-scroll (#30057)
+ * Add workflow_job webhook (#33694)
+ * Add a button editing action secret (#34462)
+
+ * Pull Request
+ * Auto expand "New PR" form (#33971)
+ * Mark parent directory as viewed when all files are viewed (#33958)
+ * Show info about maintainers are allowed to edit a PR (#33738)
+ * Automerge supports deleting branch automatically after merging (#32343)
+ * Add additional command hints for PowerShell & CMD (#33548)
+
+ * Issues
+ * Allow filtering issues by any assignee (#33343)
+ * Show warning on navigation if currently editing comment or title (#32920)
+ * Make tracked time representation display as hours (#33315)
+ * Add No Results Prompt Message on Issue List Page (#33699)
+ * Add sort option recentclose for issues and pulls (#34525) #34539
+
+ * Packages
+ * Link to nuget dependencies (#26554)
+ * Add composor source field (#33502)
+
+ * Administration
+ * Improve navbar: add "admin" tip, add "active" style (#32927)
+ * Add a option "--user-type bot" to admin user create, improve role display (#27885)
+ * Improve admin user view page (#33735)
+ * Support performance trace (#32973)
+ * Change pprof labels to be prometheus compatible (#32865)
+ * Allow admins and org owners to change org member public status (#28294)
+ * Optimize the installation page (#32994)
+ * Make public URL generation configurable (#34250)
+ * Add a --fullname arg to gitea admin user create. (#34241)
+
+ * Others
+ * Improve oauth2 error handling (#33969)
+ * Fail mirroring more gracefully (#34002)
+ * Align User Details Page Header Layout with Design Specifications (#34192)
+ * Webhook add X-Gitea-Hook-Installation-Target-Type Header (#33752)
+ * Optimize the dashboard (#32990)
+ * Improve button layout on small screens (#33633)
+ * Add cropping support when modifying the user/org/repo avatar (#33498)
+ * Make ROOT_URL support using request Host header (#32564)
+ * Add `show more` organizations icon in user's profile (#32986)
+ * Introduce `--page-space-bottom` at 64px (#30692)
+ * Improve theme display (#30671)
+ * Add alphabetical project sorting (#33504)
+ * Add global lock for migrations to make upgrade more safe with multiple replications (#33706)
+ * Add descriptions for private repo public access settings and improve the UI (#34057)
+
+* API
+ * Actions Runner rest api (#33873)
+ * Inclusion of rename organization api (#33303)
+ * Add API to support link package to repository and unlink it (#33481)
+ * Add API endpoint to request contents of multiple files simultaniously (#34139)
+ * Actions artifacts API list/download check status upload confirmed (#34273)
+ * Add API routes to lock and unlock issues (#34165)
+ * Fix some user name usages (#33689)
+ * Allow filtering /repos/{owner}/{repo}/pulls by target base branch queryparam (#33684)
+ * Improve swagger generation (#33664)
+ * Support Ephemeral action runners (#33570)
+ * Support workflow event dispatch via API (#33545)
+ * Support workflow event dispatch via API (#32059)
+ * Added Description Field for Secrets and Variables (#33526)
+ * Reject star-related requests if stars are disabled (#33208)
+ * Let API create and edit system webhooks, attempt 2 (#33180)
+ * Use `Project-URL` metadata field to get a PyPI package's homepage URL (#33089)
+ * Add `last_committer_date` and `last_author_date` for file contents API (#32921)
+
+* REFACTORS
+ * Remove context from git struct (#33793)
+ * Refactor admin/common.ts (#33788)
+ * Refactor repo-settings.ts (#33785)
+ * Refactor repo-issue.ts (#33784)
+ * Small refactor to reduce unnecessary database queries and remove duplicated functions (#33779)
+ * Refactor initRepoBranchTagSelector to use new init framework (#33776)
+ * Refactor buttons to use new init framework (#33774)
+ * Refactor markup and pdf-viewer to use new init framework (#33772)
+ * Refactor error system (#33771)
+ * Refactor mail code (#33768)
+ * Update TypeScript types (#33799)
+ * Refactor older tests to use testify (#33140)
+ * Move notifywatch to service layer (#33825)
+ * Decouple context from repository related structs (#33823)
+ * Remove context from mail struct (#33811)
+ * Refactor dropdown ellipsis (#34123)
+ * Refactor functions to reduce repopath expose (#33892)
+ * Refactor repo-diff.ts (#33746)
+ * Refactor web route handler (#33488)
+ * Refactor user & avatar (#33433)
+ * Refactor user package (#33423)
+ * Refactor decouple context from migration structs (#33399)
+ * Refactor context flash msg and global variables (#33375)
+ * Refactor response writer & access logger (#33323)
+ * Refactor ref type (#33242)
+ * Refactor context repository (#33202)
+ * Refactor legacy JS (#33115)
+ * Refactor legacy line-number and scroll code (#33094)
+ * Refactor env var related code (#33075)
+ * Move SetMerged to service layer (#33045)
+ * Merge updatecommentattachment functions (#33044)
+ * Refactor pull-request compare&create page (#33071)
+ * Refactor repo-new.ts (#33070)
+ * Refactor pagination (#33037)
+ * Refactor tests (#33021)
+ * Refactor markup render to fix various path problems (#34114)
+ * Refactor Branch struct in package modules/git (#33980)
+ * Don't create duplicated functions for code repositories and wiki repositories (#33924)
+ * Move git references checking to gitrepo packages to reduce expose of repository path (#33891)
+ * Refactor cache-control (#33861)
+ * Decouple diff stats query from actual diffing (#33810)
+ * Move part of updating protected branch logic to service layer (#33742)
+ * Decouple Batch from git.Repository to simplify usage without requiring the creation of a Repository struct. (#34001)
+ * Refactor tmpl and blob_excerpt (#32967)
+ * Refactor template & test related code (#32938)
+ * Refactor db package and remove unnecessary `DumpTables` (#32930)
+ * Refactor pprof labels and process desc (#32909)
+ * Refactor repo-projects.ts (#32892)
+ * Refactor getpatch/getdiff functions and remove unnecessary fallback (#32817)
+ * Uniform all temporary directories and allow customizing temp path (#32352)
+ * Remove context from retry downloader (#33871)
+ * Refactor global init code and add more comments (#33755)
+ * Remove some unnecessary template helpers (#33069)
+ * Move and rename UpdateRepository (#34136)
+ * Move hooks function to gitrepo and reduce expose repopath (#33890)
+ * Add abstraction layer to delete repository from disk (#33879)
+ * Add abstraction layer to check if the repository exists on disk (#33874)
+ * Move ParseCommitWithSSHSignature to service layer (#34087)
+ * Move duplicated functions (#33977)
+ * Extract code to their own functions for push update (#33944)
+ * Move gitgraph from modules to services layer (#33527)
+ * Move commits signature and verify functions to service layers (#33605)
+ * Use `CloseIssue` and `ReopenIssue` instead of `ChangeStatus` (#32467)
+ * Refactor arch route handlers (#32993)
+ * Refactor "string truncate" (#32984)
+ * Refactor arch route handlers (#32972)
+ * Clarify path param naming (#32969)
+ * Refactor request context (#32956)
+ * Move some errors to their own sub packages (#32880)
+ * Move RepoTransfer from models to models/repo sub package (#32506)
+ * Move delete deploy keys into service layer (#32201)
+ * Refactor webhook events (#33337)
+ * Move some Actions related functions from `routers` to `services` (#33280)
+ * Refactor RefName (#33234)
+ * Refactor context RefName and RepoAssignment (#33226)
+ * Refactor repository transfer (#33211)
+ * Refactor error system (#33626)
+ * Refactor error system (#33610)
+ * Refactor package (routes and error handling, npm peer dependency) (#33111)
+ * Use test context in tests and new loop system in benchmarks (#33648)
+ * Some small refactors (#33144)
+ * Simplify context ref name (#33267)
+
+* BUGFIXES
+ * Fix some dropdown problems on the issue sidebar (#34308) #34327
+ * Do not return archive download URLs in API if downloads are disabled (#34324) #34338
+ * Fix LFS files being editable in web UI (#34356) #34362
+ * Fix only text/* being viewable in web UI (#34374) #34378
+ * Fix LFS file not stored in LFS when uploaded/edited via API or web UI (#34367)
+ * Grey out expired artifact on Artifacts list (#34314) #34404
+ * Fix incorrect divergence cache after switching default branch (#34370) #34406
+ * Refactor commit message rendering and fix bugs (#34412) #34414
+ * Merge and tweak markup editor expander CSS (#34409) #34415
+ * Fix GetUsersByEmails (#34423) #34425
+ * Only git operations should update last changed of a repository (#34388) #34427
+ * Fix comment textarea scroll issue in Firefox (#34438) #34446
+ * Fix repo broken check (#34444) #34452
+ * Fix remove org user failure on mssql (#34449) #34453
+ * Fix Workflow run Not Found page (#34459) #34466
+ * When updating comment, if the content is the same, just return and not update the database (#34422) #34464
+ * Fix project board view (#34470) #34475
+ * Fix get / delete runner to use consistent http 404 and 500 status (#34480) #34488
+ * Fix url validation in webhook add/edit API (#34492) #34496
+ * Fix edithook api can not update package, status and workflow_job events (#34495) #34499
+ * Fix ephemeral runner deletion (#34447) #34513
+ * Don't display error log when .git-blame-ignore-revs doesn't exist (#34457)
+ * Only allow admins to rename default/protected branches (#33276)
+ * Improve "lock conversation" UI (#34207)
+ * Fix incorrect file links (#34189)
+ * Optimize Overflow Menu (#34183)
+ * Check user/org repo limit instead of doer (#34147)
+ * Make markdown render match GitHub's behavior (#34129)
+ * Fix team permission (#34128)
+ * Correctly handle submodule view and avoid throwing 500 error (#34121)
+ * Fix users being able bypass limits with repo transfers (#34031)
+ * Avoid creating unnecessary temporary cat file sub process (#33942)
+ * Refactor organization menu (#33928)
+ * Fix various Fomantic UI and htmx problems (#33851)
+ * Fix 500 error when error occurred in migration page (#33256)
+ * Validate that the tag doesn't exist when creating a tag via the web (#33241)
+ * Add missed transaction on setmerged (#33079)
+ * Rework create/fork/adopt/generate repository to make sure resources will be cleanup once failed (#31035)
+ * Valid email address should only start with alphanumeric (#28174)
+ * Fix webhook url (#34186)
+ * Fix "toAbsoluteLocaleDate" test when system locale is not en-US (#33939)
+ * Fix file name could not be searched if the file was not a text file when using the Bleve indexer (#33959)
+ * Fix cannot delete runners via the modal dialog (#33895)
+ * Fix unpin hint on the pinned pull requests (#33207)
+ * Fix parentCommit invalid memory address or nil pointer dereference. (#33204)
+ * Fix comment header padding (#33377)
+ * Fix some migration and repo name problems (#33986)
+ * Fix various trivial frontend problems (#34263)
+ * Fix Set Email Preference dropdown and button placement (#34255)
+ * Fix quoted replies incorrectly render user input as part of the quote (#34216)
+ * Fix button alignments and remove unnecessary styles (#34206)
+ * Restore form inputs on organization create error (#34201)
+ * Try to fix ACME (3rd) (#33807)
+ * Fix incorrect ref "blob" (#33240)
+ * Fix dynamic content loading init problem (#33748)
+ * Fix git empty check and HEAD request (#33690)
+ * Fix Untranslated Text on Actions Page (#33635)
+ * Fix issue label delete incorrect labels webhook payload (#34575)
+ * Fix incorrect page navigation with up and down arrow on last item of dashboard repos (#34570)
+ * Fix/improve avatar sync from LDAP (#34573)
+ * Fix some trivial problems (#34579)
+ * Retain issue sort type when a keyword search is introduced (#34559)
+ * Always use an empty line to separate the commit message and trailer (#34512)
+ * Fix line-button issue after file selection in file tree (#34574)
+ * Fix doctor deleting orphaned issues attachments (#34142)
+ * Add webhook assigning test and fix possible bug (#34420)
+ * Fix possible nil description of pull request when migrating from CodeCommit (#34541)
+ * Refactor commit reader (#34542)
+ * Fix possible pull request broken when leave the page immediately after clicking the update button #34509
+ * Ignore "Close" error when uploading container blob (#34620)
+ * Fix missed merge commit sha and time when migrating from codecommit (#34645)
+ * Fix GetUsersByEmails (#34643)
+ * Misc CSS fixes (#34638)
+ * Add codecommit to supported services in api docs (#34626)
+ * Validate hex colors when creating/editing labels (#34623)
+ * Fix possible pull request broken when leave the page immediately after clicking the update button (#34509)
+ * Fix margin issue in markup paragraph rendering (#34599)
+ * Fix migration pull request title too long (#34577)
+ * Fix footnote jump behavior on the issue page. (#34621)
+ * Fix "oras" OCI client compatibility (#34666)
+ * Fix last admin check when syncing users (#34649)
+ * Fix skip paths check on tag push events in workflows (#34602) #34670
+
+* MISC
+
+ * Bump to alpine 3.22 (#34613)
+ * Make pull request and issue history more compact (#34588)
+ * Run integration tests against postgres 14 (#34514) #34536
+ * Enable addtional linters (#34085)
+ * Enable testifylint rules (#34075)
+ * Enable staticcheck QFxxxx rules (#34064)
+ * Improve Actions test (#32883)
+ * Drop fomantic build (#33845)
+ * Go1.24 (#33562)
+ * Run yamllint with strict mode, fix issue (#33551)
+ * Disable cron task to update license (#33486)
+ * Optimize makefile help information generation (#33390)
+ * Convert github.com/xanzy/go-gitlab into gitlab.com/gitlab-org/api/client-go (#33126)
+ * Add missed changelogs (#33649)
+ * Update .changelog file to add performance label group (#33472)
+ * Add missing POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES in app.example.ini (#33363)
+ * Update README screenshots (#33347)
+ * Update unrs-resolver (#34279)
+ * Update go&js dependencies (#34262)
+ * Optimize the calling code of queryElems (#34235)
+ * Update protected_branch.tmpl (#34193)
+ * Feat/optimize span svg layout (#34185)
+ * Set MERMAID_MAX_SOURCE_CHARACTERS to 50000 (#34152)
+ * Update JS and PY deps (#34143)
+ * Add Chinese translations for README files (#34132)
+ * Use `overflow-wrap: anywhere` to replace `word-break: break-all` (#34126)
+ * Clarify ownership in password change error messages (#34092)
+ * Add toggleClass function in dom.ts (#34063)
+ * Update to golangci-lint v2 (#34054)
+ * Update Makefile test comments (#34013)
+ * Update go mod dependencies (#33988)
+ * Use filepath.Join instead of path.Join for file system file operations (#33978)
+ * Prepare common tmpl functions in a middleware (#33957)
+ * Remove unused or abused styles (#33918)
+ * Update JS and PY deps, misc tweaks (#33903)
+ * Try to figure out attribute checker problem (#33901)
+ * Add lock for a repository pull mirror (#33876)
+ * Fine tune push mirror UI (#33866)
+ * Improve issue & code search (#33860)
+ * Use pullrequestlist instead of []*pullrequest (#33765)
+ * Upgrade act to 0.261.4 and actions-proto-go to v0.4.1 (#33760)
+ * Align sidebar gears to the right (#33721)
+ * Update Go dependencies (skip blevesearch, meilisearch) (#33655)
+ * Add migrations and doctor fixes (#33556)
+ * Remove "class-name" from svg icon (#33540)
+ * Update MAINTAINERS (#33529)
+ * Add "No data available" display when list is empty (#33517)
+ * Use `git diff-tree` for `DiffFileTree` on diff pages (#33514)
+ * Give organisation members access to organisation feeds (#33508)
+ * Update feishu icon (#33470)
+ * Hide/disable unusable UI elements when a repository is archived (#33459)
+ * Update `@github/text-expander-element` to 2.9.0 (#33435)
+ * Do not access GitRepo when a repo is being created (#33380)
+ * Fix incorrect ref usages (#33301)
+ * Prepare for support performance trace (#33286)
+ * Enable Typescript `noImplicitThis` (#33250)
+ * Remove unused CSS styles and move some styles to proper files (#33217)
+ * Add .run to gitignore (#33175)
+ * Fix typo in gitea downloader test and add missing codebase in `ToGitServiceType` (#33146)
+ * Remove extended glob pattern from branch protection UI (#33125)
+ * Clean up legacy form CSS styles (#33081)
+ * Unset XDG_HOME_CONFIG as gitea manages configuration locations (#33067)
+ * Add IntelliJ Gateway's .uuid to gitignore (#33052)
+ * User facing messages for AGit errors (#33012)
+ * Always show assignees on right (#33006)
+ * Fix eslint (#33002)
+ * Update JS dependencies (#32914)
+ * Bump x/net (#32896) (#32900)
+ * Only activity tab needs heatmap data loading (#34652)
+
+## [1.23.8](https://github.com/go-gitea/gitea/releases/tag/1.23.8) - 2025-05-11
+
+* SECURITY
+ * Fix a bug when uploading file via lfs ssh command (#34408) (#34411)
+ * Update net package (#34228) (#34232)
+* BUGFIXES
+ * Fix releases sidebar navigation link (#34436) #34439
+ * Fix bug webhook milestone is not right. (#34419) #34429
+ * Fix two missed null value checks on the wiki page. (#34205) (#34215)
+ * Swift files can be passed either as file or as form value (#34068) (#34236)
+ * Fix bug when API get pull changed files for deleted head repository (#34333) (#34368)
+ * Upgrade github v61 -> v71 to fix migrating bug (#34389)
+ * Fix bug when visiting comparation page (#34334) (#34364)
+ * Fix wrong review requests when updating the pull request (#34286) (#34304)
+ * Fix github migration error when using multiple tokens (#34144) (#34302)
+ * Explicitly not update indexes when sync database schemas (#34281) (#34295)
+ * Fix panic when comment is nil (#34257) (#34277)
+ * Fix project board links to related Pull Requests (#34213) (#34222)
+ * Don't assume the default wiki branch is master in the wiki API (#34244) (#34245)
+* DOCUMENTATION
+ * Update token creation API swagger documentation (#34288) (#34296)
+* MISC
+ * Fix CI Build (#34315)
+ * Add riscv64 support (#34199) (#34204)
+ * Bump go version in go.mod (#34160)
+ * remove hardcoded 'code' string in clone_panel.tmpl (#34153) (#34158)
+
+## [1.23.7](https://github.com/go-gitea/gitea/releases/tag/1.23.7) - 2025-04-07
+
+* Enhancements
+ * Add a config option to block "expensive" pages for anonymous users (#34024) (#34071)
+ * Also check default ssh-cert location for host (#34099) (#34100) (#34116)
+* BUGFIXES
+ * Fix discord webhook 400 status code when description limit is exceeded (#34084) (#34124)
+ * Get changed files based on merge base when checking `pull_request` actions trigger (#34106) (#34120)
+ * Fix invalid version in RPM package path (#34112) (#34115)
+ * Return default avatar url when user id is zero rather than updating database (#34094) (#34095)
+ * Add additional ReplaceAll in pathsep to cater for different pathsep (#34061) (#34070)
+ * Try to fix check-attr bug (#34029) (#34033)
+ * Git client will follow 301 but 307 (#34005) (#34010)
+ * Fix block expensive for 1.23 (#34127)
+ * Fix markdown frontmatter rendering (#34102) (#34107)
+ * Add new CLI flags to set name and scopes when creating a user with access token (#34080) (#34103)
+ * Do not show 500 error when default branch doesn't exist (#34096) (#34097)
+ * Hide activity contributors, recent commits and code frequrency left tabs if there is no code permission (#34053) (#34065)
+ * Simplify emoji rendering (#34048) (#34049)
+ * Adjust the layout of the toolbar on the Issues/Projects page (#33667) (#34047)
+ * Pull request updates will also trigger code owners review requests (#33744) (#34045)
+ * Fix org repo creation being limited by user limits (#34030) (#34044)
+ * Fix git client accessing renamed repo (#34034) (#34043)
+ * Fix the issue with error message logging for the `check-attr` command on Windows OS. (#34035) (#34036)
+ * Polyfill WeakRef (#34025) (#34028)
+
+## [1.23.6](https://github.com/go-gitea/gitea/releases/tag/v1.23.6) - 2025-03-24
+
+* SECURITY
+ * Fix LFS URL (#33840) (#33843)
+ * Update jwt and redis packages (#33984) (#33987)
+ * Update golang crypto and net (#33989)
+* BUGFIXES
+ * Drop timeout for requests made to the internal hook api (#33947) (#33970)
+ * Fix maven panic when no package exists (#33888) (#33889)
+ * Fix markdown render (#33870) (#33875)
+ * Fix auto concurrency cancellation skips commit status updates (#33764) (#33849)
+ * Fix oauth2 auth (#33961) (#33962)
+ * Fix incorrect 1.23 translations (#33932)
+ * Try to figure out attribute checker problem (#33901) (#33902)
+ * Ignore trivial errors when updating push data (#33864) (#33887)
+ * Fix some UI problems for 1.23 (#33856)
+ * Removing unwanted ui container (#33833) (#33835)
+ * Support disable passkey auth (#33348) (#33819)
+ * Do not call "git diff" when listing PRs (#33817)
+ * Try to fix ACME (3rd) (#33807) (#33808)
+ * Fix incorrect code search indexer options (#33992) #33999
+
+## [1.23.5](https://github.com/go-gitea/gitea/releases/tag/v1.23.5) - 2025-03-04
+
+* SECURITY
+ * Bump x/oauth2 & x/crypto (#33704) (#33727)
+* PERFORMANCE
+ * Optimize user dashboard loading (#33686) (#33708)
+* BUGFIXES
+ * Fix navbar dropdown item align (#33782)
+ * Fix inconsistent closed issue list icon (#33722) (#33728)
+ * Fix for Maven Package Naming Convention Handling (#33678) (#33679)
+ * Improve Open-with URL encoding (#33666) (#33680)
+ * Deleting repository should unlink all related packages (#33653) (#33673)
+ * Fix omitempty bug (#33663) (#33670)
+ * Upgrade go-crypto from 1.1.4 to 1.1.6 (#33745) (#33754)
+ * Fix OCI image.version annotation for releases to use full semver (#33698) (#33701)
+ * Try to fix ACME path when renew (#33668) (#33693)
+ * Fix mCaptcha bug (#33659) (#33661)
+ * Git graph: don't show detached commits (#33645) (#33650)
+ * Use MatchPhraseQuery for bleve code search (#33628)
+ * Adjust appearence of commit status webhook (#33778) #33789
+ * Upgrade golang net from 0.35.0 -> 0.36.0 (#33795) #33796
+
+## [1.23.4](https://github.com/go-gitea/gitea/releases/tag/v1.23.4) - 2025-02-16
+
+* SECURITY
+ * Enhance routers for the Actions variable operations (#33547) (#33553)
+ * Enhance routers for the Actions runner operations (#33549) (#33555)
+ * Fix project issues list and counting (#33594) #33619
+* PERFORMANCES
+ * Performance optimization for pull request files loading comments attachments (#33585) (#33592)
+* BUGFIXES
+ * Add a transaction to `pickTask` (#33543) (#33563)
+ * Fix mirror bug (#33597) (#33607)
+ * Use default Git timeout when checking repo health (#33593) (#33598)
+ * Fix PR's target branch dropdown (#33589) (#33591)
+ * Fix various problems (artifact order, api empty slice, assignee check, fuzzy prompt, mirror proxy, adopt git) (#33569) (#33577)
+ * Rework suggestion backend (#33538) (#33546)
+ * Fix context usage (#33554) (#33557)
+ * Only show the latest version in the Arch index (#33262) (#33580)
+ * Skip deletion error for action artifacts (#33476) (#33568)
+ * Make actions URL in commit status webhooks absolute (#33620) #33632
+ * Add missing locale (#33641) #33642
+
+## [1.23.3](https://github.com/go-gitea/gitea/releases/tag/v1.23.3) - 2025-02-06
+
+* Security
+ * Build Gitea with Golang v1.23.6 to fix security bugs
+* BUGFIXES
+ * Fix a bug caused by status webhook template #33512
+
+## [1.23.2](https://github.com/go-gitea/gitea/releases/tag/1.23.2) - 2025-02-04
+
+* BREAKING
+ * Add tests for webhook and fix some webhook bugs (#33396) (#33442)
+ * Package webhook’s Organization was incorrectly used as the User struct. This PR fixes the issue.
+ * This changelog is just a hint. The change is not really breaking because most fields are the same, most users are not affected.
+* ENHANCEMENTS
+ * Clone button enhancements (#33362) (#33404)
+ * Repo homepage styling tweaks (#33289) (#33381)
+ * Add a confirm dialog for "sync fork" (#33270) (#33273)
+ * Make tracked time representation display as hours (#33315) (#33334)
+ * Improve sync fork behavior (#33319) (#33332)
+* BUGFIXES
+ * Fix code button alignment (#33345) (#33351)
+ * Correct bot label `vertical-align` (#33477) (#33480)
+ * Fix SSH LFS memory usage (#33455) (#33460)
+ * Fix issue sidebar dropdown keyboard support (#33447) (#33450)
+ * Fix user avatar (#33439)
+ * Fix `GetCommitBranchStart` bug (#33298) (#33421)
+ * Add pubdate for repository rss and add some tests (#33411) (#33416)
+ * Add missed auto merge feed message on dashboard (#33309) (#33405)
+ * Fix issue suggestion bug (#33389) (#33391)
+ * Make issue suggestion work for all editors (#33340) (#33342)
+ * Fix issue count (#33338) (#33341)
+ * Fix Account linking page (#33325) (#33327)
+ * Fix closed dependency title (#33285) (#33287)
+ * Fix sidebar milestone link (#33269) (#33272)
+ * Fix missing license when sync mirror (#33255) (#33258)
+ * Fix upload file form (#33230) (#33233)
+ * Fix mirror bug (#33224) (#33225)
+ * Fix system admin cannot fork or get private fork with API (#33401) (#33417)
+ * Fix push message behavior (#33215) (#33317)
+ * Trivial fixes (#33304) (#33312)
+ * Fix "stop time tracking button" on navbar (#33084) (#33300)
+ * Fix tag route and empty repo (#33253)
+ * Fix cache test triggered by non memory cache (#33220) (#33221)
+ * Revert empty lfs ref name (#33454) (#33457)
+ * Fix flex width (#33414) (#33418)
+ * Fix commit status events (#33320) #33493
+ * Fix unnecessary comment when moving issue on the same project column (#33496) #33499
+ * Add timetzdata build tag to binary releases (#33463) #33503
+* MISC
+ * Use ProtonMail/go-crypto to replace keybase/go-crypto (#33402) (#33410)
+ * Update katex to latest version (#33361)
+ * Update go tool dependencies (#32916) (#33355)
+
+## [1.23.1](https://github.com/go-gitea/gitea/releases/tag/v1.23.1) - 2025-01-09
+
+* ENHANCEMENTS
+ * Move repo size to sidebar (#33155) (#33182)
+* BUGFIXES
+ * Use updated path to s6-svscan after alpine upgrade (#33185) (#33188)
+ * Fix fuzz test (#33156) (#33158)
+ * Fix raw file API ref handling (#33172) (#33189)
+ * Fix ACME panic (#33178) (#33186)
+ * Fix branch dropdown not display ref name (#33159) (#33183)
+ * Fix assignee list overlapping in Issue sidebar (#33176) (#33181)
+ * Fix sync fork for consistency (#33147) #33192
+ * Fix editor markdown not incrementing in a numbered list (#33187) #33193
+
+## [1.23.0](https://github.com/go-gitea/gitea/releases/tag/v1.23.0) - 2025-01-08
+
+* BREAKING
+ * Rename config option `[camo].Allways` to `[camo].Always` (#32097)
+ * Remove SHA1 for support for ssh rsa signing (#31857)
+ * Use UTC as default timezone when schedule Actions cron tasks (#31742)
+ * Delete Actions logs older than 1 year by default (#31735)
+ * Make OIDC introspection authentication strictly require Client ID and secret (#31632)
+
+* SECURITY
+ * Include file extension checks in attachment API (#32151)
+ * Include all security fixes which have been backported to v1.22
+
+* FEATURES
+ * Allow to fork repository into the same owner (#32819)
+ * Support "merge upstream branch" (Sync fork) (#32741)
+ * Add Arch package registry (#32692)
+ * Allow to disable the password-based login (sign-in) form (#32687)
+ * Allow cropping an avatar before setting it (#32565)
+ * Support quote selected comments to reply (#32431)
+ * Add reviewers selection to new pull request (#32403)
+ * Suggestions for issues (#32327)
+ * Add priority to protected branch (#32286)
+ * Included tag search capabilities (#32045)
+ * Add option to filter board cards by labels and assignees (#31999)
+ * Add automatic light/dark option for the colorblind theme (#31997)
+ * Support migration from AWS CodeCommit (#31981)
+ * Introduce globallock as distributed locks (#31908 & #31813)
+ * Support compression for Actions logs & enable by default (#31761 & #32013)
+ * Add pure SSH LFS support (#31516)
+ * Add Passkey login support (#31504)
+ * Actions support workflow dispatch event (#28163)
+ * Support repo license (#24872)
+ * Issue time estimate, meaningful time tracking (#23113)
+ * GitHub like repo home page (#32213 & #32847)
+ * Rearrange Clone Panel (#31142)
+ * Enhancing Gitea OAuth2 Provider with Granular Scopes for Resource Access (#32573)
+ * Use env GITEA_RUNNER_REGISTRATION_TOKEN as global runner token (#32946) #32964
+ * Update i18n.go - Language Picker (#32933) #32935
+
+* PERFORMANCE
+ * Perf: add extra index to notification table (#32395)
+ * Introduce OrgList and add LoadTeams, optimaze Load teams for orgs (#32543)
+ * Improve performance of diffs (#32393)
+ * Make LFS http_client parallel within a batch. (#32369)
+ * Add new index for action to resolve the performance problem (#32333)
+ * Improve get feed with pagination (#31821)
+ * Performance improvements for pull request list API (#30490)
+ * Use batch database operations instead of one by one to optimze api pulls (#32680)
+ * Use gitrepo.GetTreePathLatestCommit to get file lastest commit instead from latest commit cache (#32987) #33046
+
+* ENHANCEMENTS
+ * Code
+ * Remove unnecessary border in repo home page sidebar (#32767)
+ * Add 'Copy path' button to file view (#32584)
+ * Improve diff file tree (#32658)
+ * Add new [lfs_client].BATCH_SIZE and [server].LFS_MAX_BATCH_SIZE config settings. (#32307)
+ * Updated tokenizer to better matching when search for code snippets (#32261)
+ * Change the code search to sort results by relevance (#32134)
+ * Support migrating GitHub/GitLab PR draft status (#32242)
+ * Move lock icon position and add additional tooltips to branch list page (#31839)
+ * Add tag name in the commits list (#31082)
+ * Add `MAX_ROWS` option for CSV rendering (#30268)
+ * Allow code search by filename (#32210)
+ * Make git push options accept short name (#32245)
+ * Repo file list enhancements (#32835)
+
+ * Markdown & Editor
+ * Refactor markdown math render, add dollor-backquote syntax support (#32831)
+ * Make Monaco theme follow browser, fully type codeeditor.ts (#32756)
+ * Refactor markdown editor and use it for milestone description editor (#32688)
+ * Add some handy markdown editor features (#32400)
+ * Improve markdown textarea for indentation and lists (#31406)
+
+ * Issue
+ * Add label/author/assignee filters to the user/org home issue list (#32779)
+ * Refactor issue filter (labels, poster, assignee) (#32771)
+ * Style unification for the issue_management area (#32605)
+ * Add "View all branches/tags" entry to Branch Selector (#32653)
+ * Improve textarea paste (#31948)
+ * Add avif image file support (#32508)
+ * Prevent from submitting issue/comment on uploading (#32263)
+ * Issue Templates: add option to have dropdown printed list (#31577)
+ * Allow searching issues by ID (#31479)
+ * Add `is_archived` option for issue indexer (#32735)
+ * Improve attachment upload methods (#30513)
+ * Support issue template assignees (#31083)
+ * Prevent simultaneous editing of comments and issues (#31053)
+ * Add issue comment when moving issues from one column to another of the project (#29311)
+
+ * Pull Request
+ * Display head branch more comfortable on pull request view (#32000)
+ * Simplify review UI (#31062)
+ * Allow force push to protected branches (#28086)
+ * Add line-through for deleted branch on pull request view page (#32500)
+ * Support requested_reviewers data in comment webhook events (#26178)
+ * Allow maintainers to view and edit files of private repos when "Allow maintainers to edit" is enabled (#32215)
+ * Allow including `Reviewed-on`/`Reviewed-by` lines for custom merge messages (#31211)
+
+ * Actions
+ * Render job title as commit message (#32748)
+ * Refactor RepoActionView.vue, add `::group::` support (#32713)
+ * Make RepoActionView.vue support `##[group]` (#32770)
+ * Support `pull_request_target` event for commit status (#31703)
+ * Detect whether action view branch was deleted (#32764)
+ * Allow users with write permission to run actions (#32644)
+ * Show latest run when visit /run/latest (#31808)
+
+ * Packages
+ * Improve rubygems package registry (#31357)
+ * Add support for npm bundleDependencies (#30751)
+ * Add signature support for the RPM module (#27069)
+ * Extract and display readme and comments for Composer packages (#30927)
+
+ * Project
+ * Add title to project view page (#32747)
+ * Set the columns height to hug all its contents (#31726)
+ * Rename project `board` -> `column` to make the UI less confusing (#30170)
+
+ * User & Organazition
+ * Use better name for userinfo structure (#32544)
+ * Use user.FullName in Oauth2 id_token response (#32542)
+ * Limit org member view of restricted users (#32211)
+ * Allow disabling authentication related user features (#31535)
+ * Add option to change mail from user display name (#31528)
+ * Use FullName in Emails to address the recipient if possible (#31527)
+
+ * Administration
+ * Add support for a credentials chain for minio access (#31051)
+ * Move admin routers from /admin to /-/admin (#32189)
+ * Add cache test for admins (#31265)
+ * Add option for mailer to override mail headers (#27860)
+ * Azure blob storage support (#30995)
+ * Supports forced use of S3 virtual-hosted style (#30969)
+ * Move repository visibility to danger zone in the settings area (#31126)
+
+ * Others
+ * Remove urls from translations (#31950)
+ * Simplify 404/500 page (#31409)
+ * Optimize installation-page experience (#32558)
+ * Refactor login page (#31530)
+ * Add new event commit status creation and webhook implementation (#27151)
+ * Repo Activity: count new issues that were closed (#31776)
+ * Set manual `tabindex`es on login page (#31689)
+ * Add `YEAR`, `MONTH`, `MONTH_ENGLISH`, `DAY` variables for template repos (#31584)
+ * Add typescript guideline and typescript-specific eslint plugins and fix issues (#31521)
+ * Make toast support preventDuplicates (#31501)
+ * Fix tautological conditions (#30735)
+ * Issue change title notifications (#33050) #33065
+
+* API
+ * Implement update branch API (#32433)
+ * Fix missing outputs for jobs with matrix (#32823)
+ * Make API "compare" accept commit IDs (#32801)
+ * Add github compatible tarball download API endpoints (#32572)
+ * Harden runner updateTask and updateLog api (#32462)
+ * Add `DISABLE_ORGANIZATIONS_PAGE` and `DISABLE_CODE_PAGE` settings for explore pages and fix an issue related to user search (#32288)
+ * Make admins adhere to branch protection rules (#32248)
+ * Calculate `PublicOnly` for org membership only once (#32234)
+ * Allow filtering PRs by poster in the ListPullRequests API (#32209)
+ * Return 404 instead of error when commit not exist (#31977)
+ * Save initial signup information for users to aid in spam prevention (#31852)
+ * Fix upload maven pacakge parallelly (#31851)
+ * Fix null requested_reviewer from API (#31773)
+ * Add permission description for API to add repo collaborator (#31744)
+ * Add return type to GetRawFileOrLFS and GetRawFile (#31680)
+ * Add skip secondary authorization option for public oauth2 clients (#31454)
+ * Add tag protection via rest api #17862 (#31295)
+ * Document possible action types for the user activity feed API (#31196)
+ * Add topics for repository API (#31127)
+ * Add support for searching users by email (#30908)
+ * Add API endpoints for getting action jobs status (#26673)
+
+* REFACTOR
+ * Update JS and PY dependencies (#31940)
+ * Enable `no-jquery/no-parse-html-literal` and fix violation (#31684)
+ * Refactor image diff (#31444)
+ * Refactor CSRF token (#32216)
+ * Fix some typescript issues (#32586)
+ * Refactor names (#31405)
+ * Use per package global lock for container uploads instead of memory lock (#31860)
+ * Move team related functions to service layer (#32537)
+ * Move GetFeeds to service layer (#32526)
+ * Resolve lint for unused parameter and unnecessary type arguments (#30750)
+ * Reimplement GetUserOrgsList to make it simple and clear (#32486)
+ * Move some functions from issue.go to standalone files (#32468)
+ * Refactor sidebar assignee&milestone&project selectors (#32465)
+ * Refactor sidebar label selector (#32460)
+ * Fix a number of typescript issues (#32459)
+ * Refactor language menu and dom utils (#32450)
+ * Refactor issue page info (#32445)
+ * Split issue sidebar into small templates (#32444)
+ * Refactor template ctx and render utils (#32422)
+ * Refactor repo legacy (#32404)
+ * Refactor markup package (#32399)
+ * Refactor markup render system (#32533 & #32589 & #32612)
+ * Refactor the DB migration system slightly (#32344)
+ * Remove jQuery import from some files (#32512)
+ * Strict pagination check (#32548)
+ * Split mail sender sub package from mailer service package (#32618)
+ * Remove outdated code about fixture generation (#32708)
+ * Refactor RepoBranchTagSelector (#32681)
+ * Refactor issue list (#32755)
+ * Refactor LabelEdit (#32752)
+ * Split issue/pull view router function as multiple smaller functions (#32749)
+ * Refactor some LDAP code (#32849)
+ * Unify repo search order by logic (#30876)
+ * Remove duplicate empty repo check in delete branch API (#32569)
+ * Replace deprecated `math/rand` functions (#30733)
+ * Remove fomantic dimmer module (#30723)
+ * Add types to fetch,toast,bootstrap,svg (#31627)
+ * Refactor webhook (#31587)
+ * Move AddCollabrator and CreateRepositoryByExample to service layer (#32419)
+ * Refactor RepoRefByType (#32413)
+ * Refactor: remove redundant err declarations (#32381)
+ * Refactor markup code (#31399)
+ * Refactor render system (orgmode) (#32671)
+ * Refactor render system (#32492)
+ * Refactor markdown render (#32736 & #32728)
+ * Refactor repo unit "disabled" check (#31389)
+ * Refactor route path normalization (#31381)
+ * Refactor to use UnsafeStringToBytes (#31358)
+ * Migrate vue components to setup (#32329)
+ * Refactor globallock (#31933)
+ * Use correct function name (#31887)
+ * Use a common message template instead of a special one (#31878)
+ * Fix a number of Typescript issues (#31877)
+ * Refactor dropzone (#31482)
+ * Move custom `tw-` helpers to tailwind plugin (#31184)
+ * Replace `gt-word-break` with `tw-break-anywhere` (#31183)
+ * Drop `IDOrderDesc` for listing Actions task and always order by `id DESC` (#31150)
+ * Split common-global.js into separate files (#31438)
+ * Improve detecting empty files (#31332)
+ * Use `querySelector` over alternative DOM methods (#31280)
+ * Remove jQuery `.text()` (#30506)
+ * Use repo as of renderctx's member rather than a repoPath on metas (#29222)
+ * Refactor some frontend problems (#32646)
+ * Refactor DateUtils and merge TimeSince (#32409)
+ * Replace DateTime with proper functions (#32402)
+ * Replace DateTime with DateUtils (#32383)
+ * Convert frontend code to typescript (#31559)
+ * Refactor maven package registry (#33049) #33057
+ * Refactor testfixtures #33028
+
+* BUGFIXES
+ * Fix issues with inconsistent spacing in areas (#32607)
+ * Fix incomplete Actions status aggregations (#32859)
+ * In some lfs server implementations, they require the ref attribute. (#32838)
+ * Update the list of watchers and stargazers when clicking watch/unwatch or star/unstar (#32570)
+ * Fix `recentupdate` sorting bugs (#32505)
+ * Fix incorrect "Target branch does not exist" in PR title (#32222)
+ * Handle "close" actionable references for manual merges (#31879)
+ * render plain text file if the LFS object doesn't exist (#31812)
+ * Fix Null Pointer error for CommitStatusesHideActionsURL (#31731)
+ * Fix loadRepository error when access user dashboard (#31719)
+ * Hide the "Details" link of commit status when the user cannot access actions (#30156)
+ * Fix duplicate dropdown dividers (#32760)
+ * Fix SSPI button visibility when SSPI is the only enabled method (#32841)
+ * Fix overflow on org header (#32837)
+ * Exclude protected branches from recently pushed (#31748)
+ * Fix large image overflow in comment page (#31740)
+ * Fix milestone deadline and date related problems (#32339)
+ * Fix markdown preview $$ support (#31514)
+ * Fix a compilation error in the Gitpod environment (#32559)
+ * Fix PR diff review form submit (#32596)
+ * Fix a number of typescript issues (#32308)
+ * Fix some function names in comment (#32300)
+ * Fix absolute-date (#32375)
+ * Clarify Actions resources ownership (#31724)
+ * Try to fix ACME directory problem (#33072) #33077
+ * Inherit submodules from template repository content (#16237) #33068
+ * Use project's redirect url instead of composing url (#33058) #33064
+ * Fix toggle commit body button ui when latest commit message is long (#32997) #33034
+ * Fix package error handling and npm meta and empty repo guide #33112
+ * Fix empty git repo handling logic and fix mobile view (#33101) #33102
+ * Fix line-number and scroll bugs (#33094) #33095
+ * Fix bleve fuzziness search (#33078) #33087
+ * Fix broken forms #33082
+ * Fix empty repo updated time (#33120) #33124
+ * Add missing transaction when set merge #33113
+ * Fix issue comment number (#30556) #33055
+ * Fix duplicate co-author in squashed merge commit messages (#33020) #33054
+ * Fix Agit pull request permission check (#32999) #33005
+ * Fix scoped label ui when contains emoji (#33007) #33014
+ * Fix bug on activities (#33008) #33016
+ * Fix review code comment avatar alignment (#33031) #33032
+ * Fix templating in pull request comparison (#33025) #33038
+ * Fix bug automerge cannot be chosed when there is only 1 merge style (#33040) #33043
+ * Fix settings not being loaded at CLI (#26402) #33048
+ * Support for email addresses containing uppercase characters when activating user account (#32998) #33001
+ * Support org labels when adding labels by label names (#32988) #32996
+ * Do not render truncated links in markdown (#32980) #32983
+ * Demilestone should not include milestone (#32923) #32979
+ * Fix Azure blob object Seek (#32974) #32975
+ * Fix maven pom inheritance (#32943) #32976
+ * Fix textarea newline handle (#32966) #32977
+ * Fix outdated tmpl code (#32953) #32961
+ * Fix commit range paging (#32944) #32962
+ * Fix repo avatar conflict (#32958) #32960
+ * Fix trailing comma not matched in the case of alphanumeric issue (#32945)
+ * Relax the version checking for Arch packages (#32908) #32913
+ * Add more load functions to make sure the reference object loaded (#32901) #32912
+ * Filter reviews of one pull request in memory instead of database to reduce slow response because of lacking database index (#33106) #33128
+ * Fix git remote error check, fix dependencies, fix js error (#33129) #33133
+
+* MISC
+ * Optimize branch protection rule loading (#32280)
+ * Bump to go 1.23 (#31855)
+ * Remove unused call to $.HeadRepo in view_title template (#32317)
+ * Do not display `attestation-manifest` and use short sha256 instead of full sha256 (#32851)
+ * Upgrade htmx to 2.0.4 (#32834)
+ * Improve JSX/TSX support in code editor (#32833)
+ * Add User-Agent for gitea's self-implemented lfs client. (#32832)
+ * Use errors.New to replace fmt.Errorf with no parameters (#32800)
+ * Add "n commits" link to contributors in contributors graph page (#32799)
+ * Update dependencies, tweak eslint (#32719)
+ * Remove all "floated" CSS styles (#32691)
+ * Show tag name on branch/tag selector if repo shown from tag ref (#32689)
+ * Use new mail package instead of an unmintained one (#32682)
+ * Optimize the styling of icon buttons within file-header-right (#32675)
+ * Validate OAuth Redirect URIs (#32643)
+ * Support optional/configurable IAMEndpoint for Minio Client (#32581) (#32581)
+ * Make search box in issue sidebar dropdown list always show when scrolling (#32576)
+ * Bump CI,Flake and Snap to Node 22 (#32487)
+ * Update `github.com/meilisearch/meilisearch-go` (#32484)
+ * Add `DEFAULT_MIRROR_REPO_UNITS` and `DEFAULT_TEMPLATE_REPO_UNITS` options (#32416)
+ * Update go dependencies (#32389)
+ * Update JS and PY dependencies (#32388)
+ * Upgrade rollup to 4.24.0 (#32312)
+ * Upgrade vue to 3.5.12 (#32311)
+ * Improve the maintainblity of the reserved username list (#32229)
+ * Upgrade htmx to 2.0.3 (#32192)
+ * Count typescript files as frontend for labeling (#32088)
+ * Only use Host header from reverse proxy (#32060)
+ * Failed authentications are logged to level Warning (#32016)
+ * Enhance USER_DISABLED_FEATURES to allow disabling change username or full name (#31959)
+ * Distinguish official vs non-official reviews, add tool tips, and upgr… (#31924)
+ * Update mermaid to v11 (#31913)
+ * Bump relative-time-element to v4.4.3 (#31910)
+ * Upgrade `htmx` to `2.0.2` (#31847)
+ * Add warning message in merge instructions when `AutodetectManualMerge` was not enabled (#31805)
+ * Add types to various low-level functions (#31781)
+ * Update JS dependencies (#31766)
+ * Remove unused code from models/repos/release.go (#31756)
+ * Support delete user email in admin panel (#31690)
+ * Add `username` to OIDC introspection response (#31688)
+ * Use GetDisplayName() instead of DisplayName() to generate rss feeds (#31687)
+ * Code editor theme enhancements (#31629)
+ * Update JS dependencies (#31616)
+ * Add types for js globals (#31586)
+ * Add back esbuild-loader for .js files (#31585)
+ * Don't show hidden labels when filling out an issue template (#31576)
+ * Allow synchronizing user status from OAuth2 login providers (#31572)
+ * Display app name in the registration email title (#31562)
+ * Use stable version of fabric (#31526)
+ * Support legacy _links LFS batch responses (#31513)
+ * Fix JS error with disabled attachment and easymde (#31511)
+ * Always use HTML attributes for avatar size (#31509)
+ * Use nolyfill to remove some polyfills (#31468)
+ * Disable issue/PR comment button given empty input (#31463)
+ * Add simple JS init performance trace (#31459)
+ * Bump htmx to 2.0.0 (#31413)
+ * Update JS dependencies, remove `eslint-plugin-jquery` (#31402)
+ * Split org Propfile README to a new tab `overview` (#31373)
+ * Update nix flake and add gofumpt (#31320)
+ * Code optimization (#31315)
+ * Enable poetry non-package mode (#31282)
+ * Optimize profile layout to enhance visual experience (#31278)
+ * Update `golang.org/x/net` (#31260)
+ * Bump `@github/relative-time-element` to v4.4.1 (#31232)
+ * Remove unnecessary inline style for tab-size (#31224)
+ * Update golangci-lint to v1.59.0 (#31221)
+ * Update chroma to v2.14.0 (#31177)
+ * Update JS dependencies (#31120)
+ * Improve the handling of `jobs.<job_id>.if` (#31070)
+ * Clean up revive linter config, tweak golangci output (#30980)
+ * Use CSS `inset` shorthand (#30939)
+ * Forbid deprecated `break-word` in CSS (#30934)
+ * Remove obsolete monaco workaround (#30893)
+ * Update JS dependencies, add new eslint rules (#30840)
+ * Fix body margin shifting with modals, fix error on project column edit (#30831)
+ * Remove disk-clean workflow (#30741)
+ * Bump `github.com/google/go-github` to v61 (#30738)
+ * Add built js files to eslint ignore (#30737)
+ * Use `ProtonMail/go-crypto` for `opengpg` in tests (#30736)
+ * Upgrade xorm to v1.3.9 and improve some migrations Sync (#29899)
+ * Added default sorting milestones by name (#27084)
+ * Enable `unparam` linter (#31277)
+ * Use Alpine 3.21 for the docker images (#32924) #32951
+ * Bump x/net (#32896) #32899
+ * Use -s -w ldflags for release artifacts (#33041) #33042
+ * Remove aws go sdk package dependency (#33029) #33047
+
+## [1.22.6](https://github.com/go-gitea/gitea/releases/tag/v1.22.6) - 2024-12-12
+
+* SECURITY
+ * Fix misuse of PublicKeyCallback(#32810)
+* BUGFIXES
+ * Fix lfs migration (#32812) (#32818)
+ * Add missing two sync feed for refs/pull (#32815)
+* TESTING
+ * Avoid MacOS keychain dialog in integration tests (#32813) (#32816)
+
+## [1.22.5](https://github.com/go-gitea/gitea/releases/tag/v1.22.5) - 2024-12-11
+
+* SECURITY
+ * Upgrade crypto library (#32791)
+ * Fix delete branch perm checking (#32654) (#32707)
+* BUGFIXES
+ * Add standard-compliant route to serve outdated R packages (#32783) (#32789)
+ * Fix internal server error when updating labels without write permission (#32776) (#32785)
+ * Add Swift login endpoint (#32693) (#32701)
+ * Fix fork page branch selection (#32711) (#32725)
+ * Fix word overflow in file search page (#32695) (#32699)
+ * Fix gogit `GetRefCommitID` (#32705) (#32712)
+ * Fix race condition in mermaid observer (#32599) (#32673)
+ * Fixe a keystring misuse and refactor duplicates keystrings (#32668) (#32792)
+ * Bump relative-time-element to v4.4.4 (#32739)
+* PERFORMANCE
+ * Make wiki pages visit fast (#32732) (#32745)
+* MISC
+ * Don't create action when syncing mirror pull refs (#32659) (#32664)
+
## [1.22.4](https://github.com/go-gitea/gitea/releases/tag/v1.22.4) - 2024-11-14
* SECURITY
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 60146276db..11c99d1e3a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -182,7 +182,7 @@ Here's how to run the test suite:
## Translation
-All translation work happens on [Crowdin](https://crowdin.com/project/gitea).
+All translation work happens on [Crowdin](https://translate.gitea.com).
The only translation that is maintained in this repository is [the English translation](https://github.com/go-gitea/gitea/blob/main/options/locale/locale_en-US.ini).
It is synced regularly with Crowdin. \
Other locales on main branch **should not** be updated manually as they will be overwritten with each sync. \
diff --git a/Dockerfile b/Dockerfile
index b95ba83289..c9e6a2d3db 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
# Build stage
-FROM docker.io/library/golang:1.23-alpine3.21 AS build-env
+FROM docker.io/library/golang:1.24-alpine3.22 AS build-env
ARG GOPROXY
ENV GOPROXY=${GOPROXY:-direct}
@@ -41,7 +41,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
/go/src/code.gitea.io/gitea/environment-to-ini
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
-FROM docker.io/library/alpine:3.21
+FROM docker.io/library/alpine:3.22
LABEL maintainer="maintainers@gitea.io"
EXPOSE 22 3000
diff --git a/Dockerfile.rootless b/Dockerfile.rootless
index be6f125104..558e6cf73b 100644
--- a/Dockerfile.rootless
+++ b/Dockerfile.rootless
@@ -1,5 +1,5 @@
# Build stage
-FROM docker.io/library/golang:1.23-alpine3.21 AS build-env
+FROM docker.io/library/golang:1.24-alpine3.22 AS build-env
ARG GOPROXY
ENV GOPROXY=${GOPROXY:-direct}
@@ -39,7 +39,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
/go/src/code.gitea.io/gitea/environment-to-ini
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
-FROM docker.io/library/alpine:3.21
+FROM docker.io/library/alpine:3.22
LABEL maintainer="maintainers@gitea.io"
EXPOSE 2222 3000
@@ -52,6 +52,7 @@ RUN apk --no-cache add \
git \
curl \
gnupg \
+ openssh-keygen \
&& rm -rf /var/cache/apk/*
RUN addgroup \
diff --git a/MAINTAINERS b/MAINTAINERS
index f0caae4d22..7643ab000f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -63,3 +63,5 @@ Kemal Zebari <kemalzebra@gmail.com> (@kemzeb)
Rowan Bohde <rowan.bohde@gmail.com> (@bohde)
hiifong <i@hiif.ong> (@hiifong)
metiftikci <metiftikci@hotmail.com> (@metiftikci)
+Christopher Homberger <christopher.homberger@web.de> (@ChristopherHX)
+Tobias Balle-Petersen <tobiasbp@gmail.com> (@tobiasbp)
diff --git a/Makefile b/Makefile
index 5524cfb08c..6a3fa60e49 100644
--- a/Makefile
+++ b/Makefile
@@ -23,20 +23,21 @@ SHASUM ?= shasum -a 256
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
COMMA := ,
-XGO_VERSION := go-1.23.x
+XGO_VERSION := go-1.24.x
AIR_PACKAGE ?= github.com/air-verse/air@v1
-EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.0.3
-GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0
-GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
+EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3
+GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.8.0
+GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.12
-MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0
-SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0
+MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.7.0
+SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.32.3
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1
-GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.17.0
+GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.19.1
+GOPLS_MODERNIZE_PACKAGE ?= golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@v0.19.1
DOCKER_IMAGE ?= gitea/gitea
DOCKER_TAG ?= latest
@@ -73,13 +74,13 @@ EXTRA_GOFLAGS ?=
MAKE_VERSION := $(shell "$(MAKE)" -v | cat | head -n 1)
MAKE_EVIDENCE_DIR := .make_evidence
+GOTESTFLAGS ?=
ifeq ($(RACE_ENABLED),true)
GOFLAGS += -race
GOTESTFLAGS += -race
endif
STORED_VERSION_FILE := VERSION
-HUGO_VERSION ?= 0.111.3
GITHUB_REF_TYPE ?= branch
GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
@@ -109,20 +110,17 @@ endif
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
-LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
+LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/riscv64
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list code.gitea.io/gitea/models/migrations/...)
-FOMANTIC_WORK_DIR := web_src/fomantic
-
WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f)
WEBPACK_CONFIGS := webpack.config.js tailwind.config.js
WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css
WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts
-BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
-BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
+BINDATA_DEST_WILDCARD := modules/migration/bindata.* modules/public/bindata.* modules/options/bindata.* modules/templates/bindata.*
GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go
@@ -139,25 +137,19 @@ TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
TEST_TAGS ?= $(TAGS_SPLIT) sqlite sqlite_unlock_notify
-TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMANTIC_WORK_DIR)/node_modules $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) $(GO_LICENSE_TMP_DIR)
+TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) $(GO_LICENSE_TMP_DIR)
GO_DIRS := build cmd models modules routers services tests
WEB_DIRS := web_src/js web_src/css
-ESLINT_FILES := web_src/js tools *.js *.ts tests/e2e
+ESLINT_FILES := web_src/js tools *.js *.ts *.cjs tests/e2e
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
-SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.js *.md *.yml *.yaml *.toml))
+SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.js *.md *.yml *.yaml *.toml)) $(filter-out tools/misspellings.csv, $(wildcard tools/*))
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini
GO_SOURCES := $(wildcard *.go)
-GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" ! -path modules/options/bindata.go ! -path modules/public/bindata.go ! -path modules/templates/bindata.go)
+GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go")
GO_SOURCES += $(GENERATED_GO_DEST)
-GO_SOURCES_NO_BINDATA := $(GO_SOURCES)
-
-ifeq ($(filter $(TAGS_SPLIT),bindata),bindata)
- GO_SOURCES += $(BINDATA_DEST)
- GENERATED_GO_DEST += $(BINDATA_DEST)
-endif
# Force installation of playwright dependencies by setting this flag
ifdef DEPS_PLAYWRIGHT
@@ -165,10 +157,8 @@ ifdef DEPS_PLAYWRIGHT
endif
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
-SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape}}/api/v1"|g
-SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape}}/api/v1"|"basePath": "/api/v1"|g
+SWAGGER_SPEC_INPUT := templates/swagger/v1_input.json
SWAGGER_EXCLUDE := code.gitea.io/sdk
-SWAGGER_NEWLINE_COMMAND := -e '$$a\'
TEST_MYSQL_HOST ?= mysql:3306
TEST_MYSQL_DBNAME ?= testgitea
@@ -189,67 +179,11 @@ TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1
all: build
.PHONY: help
-help:
- @echo "Make Routines:"
- @echo " - \"\" equivalent to \"build\""
- @echo " - build build everything"
- @echo " - frontend build frontend files"
- @echo " - backend build backend files"
- @echo " - watch watch everything and continuously rebuild"
- @echo " - watch-frontend watch frontend files and continuously rebuild"
- @echo " - watch-backend watch backend files and continuously rebuild"
- @echo " - clean delete backend and integration files"
- @echo " - clean-all delete backend, frontend and integration files"
- @echo " - deps install dependencies"
- @echo " - deps-frontend install frontend dependencies"
- @echo " - deps-backend install backend dependencies"
- @echo " - deps-tools install tool dependencies"
- @echo " - deps-py install python dependencies"
- @echo " - lint lint everything"
- @echo " - lint-fix lint everything and fix issues"
- @echo " - lint-actions lint action workflow files"
- @echo " - lint-frontend lint frontend files"
- @echo " - lint-frontend-fix lint frontend files and fix issues"
- @echo " - lint-backend lint backend files"
- @echo " - lint-backend-fix lint backend files and fix issues"
- @echo " - lint-go lint go files"
- @echo " - lint-go-fix lint go files and fix issues"
- @echo " - lint-go-vet lint go files with vet"
- @echo " - lint-go-gopls lint go files with gopls"
- @echo " - lint-js lint js files"
- @echo " - lint-js-fix lint js files and fix issues"
- @echo " - lint-css lint css files"
- @echo " - lint-css-fix lint css files and fix issues"
- @echo " - lint-md lint markdown files"
- @echo " - lint-swagger lint swagger files"
- @echo " - lint-templates lint template files"
- @echo " - lint-yaml lint yaml files"
- @echo " - lint-spell lint spelling"
- @echo " - lint-spell-fix lint spelling and fix issues"
- @echo " - checks run various consistency checks"
- @echo " - checks-frontend check frontend files"
- @echo " - checks-backend check backend files"
- @echo " - test test everything"
- @echo " - test-frontend test frontend files"
- @echo " - test-backend test backend files"
- @echo " - test-e2e[\#TestSpecificName] test end to end using playwright"
- @echo " - update update js and py dependencies"
- @echo " - update-js update js dependencies"
- @echo " - update-py update py dependencies"
- @echo " - webpack build webpack files"
- @echo " - svg build svg files"
- @echo " - fomantic build fomantic files"
- @echo " - generate run \"go generate\""
- @echo " - fmt format the Go code"
- @echo " - generate-license update license files"
- @echo " - generate-gitignore update gitignore files"
- @echo " - generate-manpage generate manpage"
- @echo " - generate-swagger generate the swagger spec from code comments"
- @echo " - swagger-validate check if the swagger spec is valid"
- @echo " - go-licenses regenerate go licenses"
- @echo " - tidy run go mod tidy"
- @echo " - test[\#TestSpecificName] run unit test"
- @echo " - test-sqlite[\#TestSpecificName] run integration test for sqlite"
+help: Makefile ## print Makefile help information.
+ @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m[TARGETS] default target: build\033[0m\n\n\033[35mTargets:\033[0m\n"} /^[0-9A-Za-z._-]+:.*?##/ { printf " \033[36m%-45s\033[0m %s\n", $$1, $$2 }' Makefile #$(MAKEFILE_LIST)
+ @printf " \033[36m%-46s\033[0m %s\n" "test-e2e[#TestSpecificName]" "test end to end using playwright"
+ @printf " \033[36m%-46s\033[0m %s\n" "test[#TestSpecificName]" "run unit test"
+ @printf " \033[36m%-46s\033[0m %s\n" "test-sqlite[#TestSpecificName]" "run integration test for sqlite"
.PHONY: go-check
go-check:
@@ -280,12 +214,12 @@ node-check:
fi
.PHONY: clean-all
-clean-all: clean
+clean-all: clean ## delete backend, frontend and integration files
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
.PHONY: clean
-clean:
- rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \
+clean: ## delete backend and integration files
+ rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST_WILDCARD) \
integrations*.test \
e2e*.test \
tests/integration/gitea-integration-* \
@@ -296,7 +230,7 @@ clean:
tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/
.PHONY: fmt
-fmt:
+fmt: ## format the Go and template code
@GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
$(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl'))
@# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only
@@ -311,7 +245,20 @@ fmt-check: fmt
@diff=$$(git diff --color=always $(GO_SOURCES) templates $(WEB_DIRS)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make fmt' and commit the result:"; \
- echo "$${diff}"; \
+ printf "%s" "$${diff}"; \
+ exit 1; \
+ fi
+
+.PHONY: fix
+fix: ## apply automated fixes to Go code
+ $(GO) run $(GOPLS_MODERNIZE_PACKAGE) -fix ./...
+
+.PHONY: fix-check
+fix-check: fix
+ @diff=$$(git diff --color=always $(GO_SOURCES)); \
+ if [ -n "$$diff" ]; then \
+ echo "Please run 'make fix' and commit the result:"; \
+ printf "%s" "$${diff}"; \
exit 1; \
fi
@@ -325,95 +272,95 @@ TAGS_PREREQ := $(TAGS_EVIDENCE)
endif
.PHONY: generate-swagger
-generate-swagger: $(SWAGGER_SPEC)
+generate-swagger: $(SWAGGER_SPEC) ## generate the swagger spec from code comments
-$(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA)
- $(GO) run $(SWAGGER_PACKAGE) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
- $(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
- $(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
+$(SWAGGER_SPEC): $(GO_SOURCES) $(SWAGGER_SPEC_INPUT)
+ $(GO) run $(SWAGGER_PACKAGE) generate spec --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)'
.PHONY: swagger-check
swagger-check: generate-swagger
@diff=$$(git diff --color=always '$(SWAGGER_SPEC)'); \
if [ -n "$$diff" ]; then \
echo "Please run 'make generate-swagger' and commit the result:"; \
- echo "$${diff}"; \
+ printf "%s" "$${diff}"; \
exit 1; \
fi
.PHONY: swagger-validate
-swagger-validate:
- $(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
+swagger-validate: ## check if the swagger spec is valid
+ @# swagger "validate" requires that the "basePath" must start with a slash, but we are using Golang template "{{...}}"
+ @$(SED_INPLACE) -E -e 's|"basePath":( *)"(.*)"|"basePath":\1"/\2"|g' './$(SWAGGER_SPEC)' # add a prefix slash to basePath
+ @# FIXME: there are some warnings
$(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
- $(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
+ @$(SED_INPLACE) -E -e 's|"basePath":( *)"/(.*)"|"basePath":\1"\2"|g' './$(SWAGGER_SPEC)' # remove the prefix slash from basePath
.PHONY: checks
-checks: checks-frontend checks-backend
+checks: checks-frontend checks-backend ## run various consistency checks
.PHONY: checks-frontend
-checks-frontend: lockfile-check svg-check
+checks-frontend: lockfile-check svg-check ## check frontend files
.PHONY: checks-backend
-checks-backend: tidy-check swagger-check fmt-check swagger-validate security-check
+checks-backend: tidy-check swagger-check fmt-check fix-check swagger-validate security-check ## check backend files
.PHONY: lint
-lint: lint-frontend lint-backend lint-spell
+lint: lint-frontend lint-backend lint-spell ## lint everything
.PHONY: lint-fix
-lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix
+lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix ## lint everything and fix issues
.PHONY: lint-frontend
-lint-frontend: lint-js lint-css
+lint-frontend: lint-js lint-css ## lint frontend files
.PHONY: lint-frontend-fix
-lint-frontend-fix: lint-js-fix lint-css-fix
+lint-frontend-fix: lint-js-fix lint-css-fix ## lint frontend files and fix issues
.PHONY: lint-backend
-lint-backend: lint-go lint-go-vet lint-go-gopls lint-editorconfig
+lint-backend: lint-go lint-go-gitea-vet lint-go-gopls lint-editorconfig ## lint backend files
.PHONY: lint-backend-fix
-lint-backend-fix: lint-go-fix lint-go-vet lint-editorconfig
+lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backend files and fix issues
.PHONY: lint-js
-lint-js: node_modules
+lint-js: node_modules ## lint js files
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES)
npx vue-tsc
.PHONY: lint-js-fix
-lint-js-fix: node_modules
+lint-js-fix: node_modules ## lint js files and fix issues
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES) --fix
npx vue-tsc
.PHONY: lint-css
-lint-css: node_modules
+lint-css: node_modules ## lint css files
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES)
.PHONY: lint-css-fix
-lint-css-fix: node_modules
+lint-css-fix: node_modules ## lint css files and fix issues
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix
.PHONY: lint-swagger
-lint-swagger: node_modules
+lint-swagger: node_modules ## lint swagger files
npx spectral lint -q -F hint $(SWAGGER_SPEC)
.PHONY: lint-md
-lint-md: node_modules
+lint-md: node_modules ## lint markdown files
npx markdownlint *.md
.PHONY: lint-spell
-lint-spell:
+lint-spell: ## lint spelling
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -error $(SPELLCHECK_FILES)
.PHONY: lint-spell-fix
-lint-spell-fix:
+lint-spell-fix: ## lint spelling and fix issues
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -w $(SPELLCHECK_FILES)
.PHONY: lint-go
-lint-go:
+lint-go: ## lint go files
$(GO) run $(GOLANGCI_LINT_PACKAGE) run
.PHONY: lint-go-fix
-lint-go-fix:
+lint-go-fix: ## lint go files and fix issues
$(GO) run $(GOLANGCI_LINT_PACKAGE) run --fix
# workaround step for the lint-go-windows CI task because 'go run' can not
@@ -423,57 +370,58 @@ lint-go-windows:
@GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE)
golangci-lint run
-.PHONY: lint-go-vet
-lint-go-vet:
- @echo "Running go vet..."
+.PHONY: lint-go-gitea-vet
+lint-go-gitea-vet: ## lint go files with gitea-vet
+ @echo "Running gitea-vet..."
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
@$(GO) vet -vettool=gitea-vet ./...
.PHONY: lint-go-gopls
-lint-go-gopls:
+lint-go-gopls: ## lint go files with gopls
@echo "Running gopls check..."
- @GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES_NO_BINDATA)
+ @GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES)
.PHONY: lint-editorconfig
lint-editorconfig:
+ @echo "Running editorconfig check..."
@$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) $(EDITORCONFIG_FILES)
.PHONY: lint-actions
-lint-actions:
+lint-actions: ## lint action workflow files
$(GO) run $(ACTIONLINT_PACKAGE)
.PHONY: lint-templates
-lint-templates: .venv node_modules
+lint-templates: .venv node_modules ## lint template files
@node tools/lint-templates-svg.js
@poetry run djlint $(shell find templates -type f -iname '*.tmpl')
.PHONY: lint-yaml
-lint-yaml: .venv
- @poetry run yamllint .
+lint-yaml: .venv ## lint yaml files
+ @poetry run yamllint -s .
.PHONY: watch
-watch:
+watch: ## watch everything and continuously rebuild
@bash tools/watch.sh
.PHONY: watch-frontend
-watch-frontend: node-check node_modules
+watch-frontend: node-check node_modules ## watch frontend files and continuously rebuild
@rm -rf $(WEBPACK_DEST_ENTRIES)
NODE_ENV=development npx webpack --watch --progress
.PHONY: watch-backend
-watch-backend: go-check
+watch-backend: go-check ## watch backend files and continuously rebuild
GITEA_RUN_MODE=dev $(GO) run $(AIR_PACKAGE) -c .air.toml
.PHONY: test
-test: test-frontend test-backend
+test: test-frontend test-backend ## test everything
.PHONY: test-backend
-test-backend:
+test-backend: ## test backend files
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
@$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES)
.PHONY: test-frontend
-test-frontend: node_modules
+test-frontend: node_modules ## test frontend files
npx vitest
.PHONY: test-check
@@ -482,7 +430,7 @@ test-check:
@diff=$$(git status -s); \
if [ -n "$$diff" ]; then \
echo "make test-backend has changed files in the source tree:"; \
- echo "$${diff}"; \
+ printf "%s" "$${diff}"; \
echo "You should change the tests to create these files in a temporary directory."; \
echo "Do not simply add these files to .gitignore"; \
exit 1; \
@@ -505,7 +453,7 @@ unit-test-coverage:
@$(GO) test $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
.PHONY: tidy
-tidy:
+tidy: ## run go mod tidy
$(eval MIN_GO_VERSION := $(shell grep -Eo '^go\s+[0-9]+\.[0-9.]+' go.mod | cut -d' ' -f2))
$(GO) mod tidy -compat=$(MIN_GO_VERSION)
@$(MAKE) --no-print-directory $(GO_LICENSE_FILE)
@@ -519,15 +467,17 @@ tidy-check: tidy
@diff=$$(git diff --color=always go.mod go.sum $(GO_LICENSE_FILE)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make tidy' and commit the result:"; \
- echo "$${diff}"; \
+ printf "%s" "$${diff}"; \
exit 1; \
fi
.PHONY: go-licenses
-go-licenses: $(GO_LICENSE_FILE)
+go-licenses: $(GO_LICENSE_FILE) ## regenerate go licenses
$(GO_LICENSE_FILE): go.mod go.sum
- -$(GO) run $(GO_LICENSES_PACKAGE) save . --force --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null
+ @rm -rf $(GO_LICENSE_FILE)
+ $(GO) install $(GO_LICENSES_PACKAGE)
+ -GOOS=linux CGO_ENABLED=1 go-licenses save . --force --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null
$(GO) run build/generate-go-licenses.go $(GO_LICENSE_TMP_DIR) $(GO_LICENSE_FILE)
@rm -rf $(GO_LICENSE_TMP_DIR)
@@ -771,17 +721,17 @@ install: $(wildcard *.go)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
.PHONY: build
-build: frontend backend
+build: frontend backend ## build everything
.PHONY: frontend
-frontend: $(WEBPACK_DEST)
+frontend: $(WEBPACK_DEST) ## build frontend files
.PHONY: backend
-backend: go-check generate-backend $(EXECUTABLE)
+backend: go-check generate-backend $(EXECUTABLE) ## build backend files
# We generate the backend before the frontend in case we in future we want to generate things in the frontend from generated files in backend
.PHONY: generate
-generate: generate-backend
+generate: generate-backend ## run "go generate"
.PHONY: generate-backend
generate-backend: $(TAGS_PREREQ) generate-go
@@ -793,7 +743,7 @@ generate-go: $(TAGS_PREREQ)
.PHONY: security-check
security-check:
- go run $(GOVULNCHECK_PACKAGE) ./...
+ go run $(GOVULNCHECK_PACKAGE) -show color ./...
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
@@ -846,20 +796,20 @@ release-sources: | $(DIST_DIRS)
rm -f $(STORED_VERSION_FILE)
.PHONY: deps
-deps: deps-frontend deps-backend deps-tools deps-py
+deps: deps-frontend deps-backend deps-tools deps-py ## install dependencies
.PHONY: deps-py
-deps-py: .venv
+deps-py: .venv ## install python dependencies
.PHONY: deps-frontend
-deps-frontend: node_modules
+deps-frontend: node_modules ## install frontend dependencies
.PHONY: deps-backend
-deps-backend:
+deps-backend: ## install backend dependencies
$(GO) mod download
.PHONY: deps-tools
-deps-tools:
+deps-tools: ## install tool dependencies
$(GO) install $(AIR_PACKAGE) & \
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE) & \
$(GO) install $(GOFUMPT_PACKAGE) & \
@@ -872,6 +822,7 @@ deps-tools:
$(GO) install $(GOVULNCHECK_PACKAGE) & \
$(GO) install $(ACTIONLINT_PACKAGE) & \
$(GO) install $(GOPLS_PACKAGE) & \
+ $(GO) install $(GOPLS_MODERNIZE_PACKAGE) & \
wait
node_modules: package-lock.json
@@ -883,10 +834,10 @@ node_modules: package-lock.json
@touch .venv
.PHONY: update
-update: update-js update-py
+update: update-js update-py ## update js and py dependencies
.PHONY: update-js
-update-js: node-check | node_modules
+update-js: node-check | node_modules ## update js dependencies
npx updates -u -f package.json
rm -rf node_modules package-lock.json
npm install --package-lock
@@ -895,27 +846,14 @@ update-js: node-check | node_modules
@touch node_modules
.PHONY: update-py
-update-py: node-check | node_modules
+update-py: node-check | node_modules ## update py dependencies
npx updates -u -f pyproject.toml
rm -rf .venv poetry.lock
poetry install
@touch .venv
-.PHONY: fomantic
-fomantic:
- rm -rf $(FOMANTIC_WORK_DIR)/build
- cd $(FOMANTIC_WORK_DIR) && npm install --no-save
- cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config
- cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/
- $(SED_INPLACE) -e 's/ overrideBrowserslist\r/ overrideBrowserslist: ["defaults"]\r/g' $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/tasks/config/tasks.js
- cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build
- # fomantic uses "touchstart" as click event for some browsers, it's not ideal, so we force fomantic to always use "click" as click event
- $(SED_INPLACE) -e 's/clickEvent[ \t]*=/clickEvent = "click", unstableClickEvent =/g' $(FOMANTIC_WORK_DIR)/build/semantic.js
- $(SED_INPLACE) -e 's/\r//g' $(FOMANTIC_WORK_DIR)/build/semantic.css $(FOMANTIC_WORK_DIR)/build/semantic.js
- rm -f $(FOMANTIC_WORK_DIR)/build/*.min.*
-
.PHONY: webpack
-webpack: $(WEBPACK_DEST)
+webpack: $(WEBPACK_DEST) ## build webpack files
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
@$(MAKE) -s node-check node_modules
@@ -925,7 +863,7 @@ $(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
@touch $(WEBPACK_DEST)
.PHONY: svg
-svg: node-check | node_modules
+svg: node-check | node_modules ## build svg files
rm -rf $(SVG_DEST_DIR)
node tools/generate-svg.js
@@ -935,7 +873,7 @@ svg-check: svg
@diff=$$(git diff --color=always --cached $(SVG_DEST_DIR)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make svg' and 'git add $(SVG_DEST_DIR)' and commit the result:"; \
- echo "$${diff}"; \
+ printf "%s" "$${diff}"; \
exit 1; \
fi
@@ -946,7 +884,7 @@ lockfile-check:
if [ -n "$$diff" ]; then \
echo "package-lock.json is inconsistent with package.json"; \
echo "Please run 'npm install --package-lock-only' and commit the result:"; \
- echo "$${diff}"; \
+ printf "%s" "$${diff}"; \
exit 1; \
fi
@@ -960,12 +898,8 @@ update-translations:
mv ./translations/*.ini ./options/locale/
rmdir ./translations
-.PHONY: generate-license
-generate-license:
- $(GO) run build/generate-licenses.go
-
.PHONY: generate-gitignore
-generate-gitignore:
+generate-gitignore: ## update gitignore files
$(GO) run build/generate-gitignores.go
.PHONY: generate-images
@@ -974,7 +908,7 @@ generate-images: | node_modules
node tools/generate-images.js $(TAGS)
.PHONY: generate-manpage
-generate-manpage:
+generate-manpage: ## generate manpage
@[ -f gitea ] || make backend
@mkdir -p man/man1/ man/man5
@./gitea docs --man > man/man1/gitea.1
diff --git a/README.md b/README.md
index c280c832ac..017ca629d0 100644
--- a/README.md
+++ b/README.md
@@ -9,9 +9,9 @@
[![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
[![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT")
[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod&color=green)](https://gitpod.io/#https://github.com/go-gitea/gitea)
-[![](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea "Crowdin")
+[![](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com "Crowdin")
-[View this document in Chinese](./README_ZH.md)
+[ç¹é«”中文](./README.zh-tw.md) | [简体中文](./README.zh-cn.md)
## Purpose
@@ -31,6 +31,14 @@ For accessing free Gitea service (with a limited number of repositories), you ca
To quickly deploy your own dedicated Gitea instance on Gitea Cloud, you can start a free trial at [cloud.gitea.com](https://cloud.gitea.com).
+## Documentation
+
+You can find comprehensive documentation on our official [documentation website](https://docs.gitea.com/).
+
+It includes installation, administration, usage, development, contributing guides, and more to help you get started and explore all features effectively.
+
+If you have any suggestions or would like to contribute to it, you can visit the [documentation repository](https://gitea.com/gitea/docs)
+
## Building
From the root of the source tree, run:
@@ -52,6 +60,8 @@ More info: https://docs.gitea.com/installation/install-from-source
## Using
+After building, a binary file named `gitea` will be generated in the root of the source tree by default. To run it, use:
+
./gitea web
> [!NOTE]
@@ -68,22 +78,25 @@ Expected workflow is: Fork -> Patch -> Push -> Pull Request
## Translating
-Translations are done through Crowdin. If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.
+[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com)
+
+Translations are done through [Crowdin](https://translate.gitea.com). If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.
You can also just create an issue for adding a language or ask on discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty but we hope to fill it as questions pop up.
-https://docs.gitea.com/contributing/localization
+Get more information from [documentation](https://docs.gitea.com/contributing/localization).
-[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea)
+## Official and Third-Party Projects
-## Further information
+We provide an official [go-sdk](https://gitea.com/gitea/go-sdk), a CLI tool called [tea](https://gitea.com/gitea/tea) and an [action runner](https://gitea.com/gitea/act_runner) for Gitea Action.
+
+We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea), where you can discover more third-party projects, including SDKs, plugins, themes, and more.
-For more information and instructions about how to install Gitea, please look at our [documentation](https://docs.gitea.com/).
-If you have questions that are not covered by the documentation, you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://forum.gitea.com/).
+## Communication
-We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea).
+[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
-The official Gitea CLI is developed at [gitea/tea](https://gitea.com/gitea/tea).
+If you have questions that are not covered by the [documentation](https://docs.gitea.com/), you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://forum.gitea.com/).
## Authors
@@ -122,18 +135,79 @@ Gitea is pronounced [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY) as in "gi-tea"
We're [working on it](https://github.com/go-gitea/gitea/issues/1029).
+**Where can I find the security patches?**
+
+In the [release log](https://github.com/go-gitea/gitea/releases) or the [change log](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md), search for the keyword `SECURITY` to find the security patches.
+
## License
This project is licensed under the MIT License.
See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file
for the full license text.
-## Screenshots
+## Further information
+
+<details>
+<summary>Looking for an overview of the interface? Check it out!</summary>
+
+### Login/Register Page
+
+![Login](https://dl.gitea.com/screenshots/login.png)
+![Register](https://dl.gitea.com/screenshots/register.png)
+
+### User Dashboard
+
+![Home](https://dl.gitea.com/screenshots/home.png)
+![Issues](https://dl.gitea.com/screenshots/issues.png)
+![Pull Requests](https://dl.gitea.com/screenshots/pull_requests.png)
+![Milestones](https://dl.gitea.com/screenshots/milestones.png)
+
+### User Profile
+
+![Profile](https://dl.gitea.com/screenshots/user_profile.png)
+
+### Explore
+
+![Repos](https://dl.gitea.com/screenshots/explore_repos.png)
+![Users](https://dl.gitea.com/screenshots/explore_users.png)
+![Orgs](https://dl.gitea.com/screenshots/explore_orgs.png)
+
+### Repository
+
+![Home](https://dl.gitea.com/screenshots/repo_home.png)
+![Commits](https://dl.gitea.com/screenshots/repo_commits.png)
+![Branches](https://dl.gitea.com/screenshots/repo_branches.png)
+![Labels](https://dl.gitea.com/screenshots/repo_labels.png)
+![Milestones](https://dl.gitea.com/screenshots/repo_milestones.png)
+![Releases](https://dl.gitea.com/screenshots/repo_releases.png)
+![Tags](https://dl.gitea.com/screenshots/repo_tags.png)
+
+#### Repository Issue
+
+![List](https://dl.gitea.com/screenshots/repo_issues.png)
+![Issue](https://dl.gitea.com/screenshots/repo_issue.png)
+
+#### Repository Pull Requests
+
+![List](https://dl.gitea.com/screenshots/repo_pull_requests.png)
+![Pull Request](https://dl.gitea.com/screenshots/repo_pull_request.png)
+![File](https://dl.gitea.com/screenshots/repo_pull_request_file.png)
+![Commits](https://dl.gitea.com/screenshots/repo_pull_request_commits.png)
+
+#### Repository Actions
+
+![List](https://dl.gitea.com/screenshots/repo_actions.png)
+![Details](https://dl.gitea.com/screenshots/repo_actions_run.png)
+
+#### Repository Activity
+
+![Activity](https://dl.gitea.com/screenshots/repo_activity.png)
+![Contributors](https://dl.gitea.com/screenshots/repo_contributors.png)
+![Code Frequency](https://dl.gitea.com/screenshots/repo_code_frequency.png)
+![Recent Commits](https://dl.gitea.com/screenshots/repo_recent_commits.png)
+
+### Organization
-Looking for an overview of the interface? Check it out!
+![Home](https://dl.gitea.com/screenshots/org_home.png)
-|![Dashboard](https://dl.gitea.com/screenshots/home_timeline.png)|![User Profile](https://dl.gitea.com/screenshots/user_profile.png)|![Global Issues](https://dl.gitea.com/screenshots/global_issues.png)|
-|:---:|:---:|:---:|
-|![Branches](https://dl.gitea.com/screenshots/branches.png)|![Web Editor](https://dl.gitea.com/screenshots/web_editor.png)|![Activity](https://dl.gitea.com/screenshots/activity.png)|
-|![New Migration](https://dl.gitea.com/screenshots/migration.png)|![Migrating](https://dl.gitea.com/screenshots/migration.gif)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)|
-|![Pull Request Dark](https://dl.gitea.com/screenshots/pull_requests_dark.png)|![Diff Review Dark](https://dl.gitea.com/screenshots/review_dark.png)|![Diff Dark](https://dl.gitea.com/screenshots/diff_dark.png)|
+</details>
diff --git a/README.zh-cn.md b/README.zh-cn.md
new file mode 100644
index 0000000000..f34b25b945
--- /dev/null
+++ b/README.zh-cn.md
@@ -0,0 +1,206 @@
+# Gitea
+
+[![](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
+[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
+[![](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
+[![](https://pkg.go.dev/badge/code.gitea.io/gitea?status.svg)](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
+[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
+[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
+[![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
+[![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT")
+[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod&color=green)](https://gitpod.io/#https://github.com/go-gitea/gitea)
+[![](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com "Crowdin")
+
+[English](./README.md) | [ç¹é«”中文](./README.zh-tw.md)
+
+## 目的
+
+这个项目的目标是æä¾›æœ€ç®€å•ã€æœ€å¿«é€Ÿã€æœ€æ— ç—›çš„æ–¹å¼æ¥è®¾ç½®è‡ªæ‰˜ç®¡çš„ Git æœåŠ¡ã€‚
+
+由于 Gitea 是用 Go 语言编写的,它å¯ä»¥åœ¨ Go 支æŒçš„æ‰€æœ‰å¹³å°å’Œæž¶æž„上è¿è¡Œï¼ŒåŒ…括 Linuxã€macOS å’Œ Windows çš„ x86ã€amd64ã€ARM å’Œ PowerPC 架构。这个项目自 2016 å¹´ 11 月从 [Gogs](https://gogs.io) [分å‰](https://blog.gitea.com/welcome-to-gitea/) 而æ¥ï¼Œä½†å·²ç»æœ‰äº†å¾ˆå¤šå˜åŒ–。
+
+在线演示å¯ä»¥è®¿é—® [demo.gitea.com](https://demo.gitea.com)。
+
+è¦è®¿é—®å…费的 Gitea æœåŠ¡ï¼ˆæœ‰ä¸€å®šæ•°é‡çš„仓库é™åˆ¶ï¼‰ï¼Œå¯ä»¥è®¿é—® [gitea.com](https://gitea.com/user/login)。
+
+è¦å¿«é€Ÿéƒ¨ç½²æ‚¨è‡ªå·±çš„专用 Gitea 实例,å¯ä»¥åœ¨ [cloud.gitea.com](https://cloud.gitea.com) 开始å…费试用。
+
+## 文件
+
+您å¯ä»¥åœ¨æˆ‘们的官方 [文件网站](https://docs.gitea.com/) 上找到全é¢çš„æ–‡ä»¶ã€‚
+
+它包括安装ã€ç®¡ç†ã€ä½¿ç”¨ã€å¼€å‘ã€è´¡çŒ®æŒ‡å—等,帮助您快速入门并有效地探索所有功能。
+
+如果您有任何建议或想è¦è´¡çŒ®ï¼Œå¯ä»¥è®¿é—® [文件仓库](https://gitea.com/gitea/docs)
+
+## 构建
+
+从æºä»£ç æ ‘的根目录è¿è¡Œï¼š
+
+ TAGS="bindata" make build
+
+å¦‚æžœéœ€è¦ SQLite 支æŒï¼š
+
+ TAGS="bindata sqlite sqlite_unlock_notify" make build
+
+`build` 目标分为两个å­ç›®æ ‡ï¼š
+
+- `make backend` éœ€è¦ [Go Stable](https://go.dev/dl/),所需版本在 [go.mod](/go.mod) 中定义。
+- `make frontend` éœ€è¦ [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
+
+需è¦äº’è”网连接æ¥ä¸‹è½½ go å’Œ npm 模å—。从包å«é¢„构建å‰ç«¯æ–‡ä»¶çš„官方æºä»£ç åŽ‹ç¼©åŒ…æž„å»ºæ—¶ï¼Œä¸ä¼šè§¦å‘ `frontend` 目标,因此å¯ä»¥åœ¨æ²¡æœ‰ Node.js 的情况下构建。
+
+更多信æ¯ï¼šhttps://docs.gitea.com/installation/install-from-source
+
+## 使用
+
+构建åŽï¼Œé»˜è®¤æƒ…况下会在æºä»£ç æ ‘的根目录生æˆä¸€ä¸ªå为 `gitea` 的二进制文件。è¦è¿è¡Œå®ƒï¼Œè¯·ä½¿ç”¨ï¼š
+
+ ./gitea web
+
+> [!注æ„]
+> 如果您对使用我们的 API 感兴趣,我们æä¾›äº†å®žéªŒæ€§æ”¯æŒï¼Œå¹¶é™„有 [文件](https://docs.gitea.com/api)。
+
+## 贡献
+
+预期的工作æµç¨‹æ˜¯ï¼šFork -> Patch -> Push -> Pull Request
+
+> [!注æ„]
+>
+> 1. **在开始进行 Pull Request 之å‰ï¼Œæ‚¨å¿…须阅读 [贡献者指å—](CONTRIBUTING.md)。**
+> 2. 如果您在项目中å‘çŽ°äº†æ¼æ´žï¼Œè¯·ç§ä¸‹å†™ä¿¡ç»™ **security@gitea.io**。谢谢ï¼
+
+## 翻译
+
+[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com)
+
+翻译通过 [Crowdin](https://translate.gitea.com) è¿›è¡Œã€‚å¦‚æžœæ‚¨æƒ³ç¿»è¯‘æˆæ–°çš„语言,请在 Crowdin 项目中请求管ç†å‘˜æ·»åŠ æ–°è¯­è¨€ã€‚
+
+您也å¯ä»¥åˆ›å»ºä¸€ä¸ª issue æ¥æ·»åŠ è¯­è¨€ï¼Œæˆ–è€…åœ¨ discord çš„ #translation 频é“上询问。如果您需è¦ä¸Šä¸‹æ–‡æˆ–å‘现一些翻译问题,å¯ä»¥åœ¨å­—符串上留言或在 Discord ä¸Šè¯¢é—®ã€‚å¯¹äºŽä¸€èˆ¬çš„ç¿»è¯‘é—®é¢˜ï¼Œæ–‡æ¡£ä¸­æœ‰ä¸€ä¸ªéƒ¨åˆ†ã€‚ç›®å‰æœ‰ç‚¹ç©ºï¼Œä½†æˆ‘们希望éšç€é—®é¢˜çš„出现而填充它。
+
+更多信æ¯è¯·å‚阅 [文件](https://docs.gitea.com/contributing/localization)。
+
+## 官方和第三方项目
+
+我们æä¾›äº†ä¸€ä¸ªå®˜æ–¹çš„ [go-sdk](https://gitea.com/gitea/go-sdk),一个å为 [tea](https://gitea.com/gitea/tea) çš„ CLI 工具和一个 Gitea Action çš„ [action runner](https://gitea.com/gitea/act_runner)。
+
+我们在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 维护了一个 Gitea 相关项目的列表,您å¯ä»¥åœ¨é‚£é‡Œå‘现更多的第三方项目,包括 SDKã€æ’ä»¶ã€ä¸»é¢˜ç­‰ã€‚
+
+## 通讯
+
+[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
+
+如果您有任何文件未涵盖的问题,å¯ä»¥åœ¨æˆ‘们的 [Discord æœåС噍](https://discord.gg/Gitea) 上与我们è”系,或者在 [discourse 论å›](https://forum.gitea.com/) 上创建帖å­ã€‚
+
+## 作者
+
+- [维护者](https://github.com/orgs/go-gitea/people)
+- [贡献者](https://github.com/go-gitea/gitea/graphs/contributors)
+- [翻译者](options/locale/TRANSLATORS)
+
+## 支æŒè€…
+
+感谢所有支æŒè€…ï¼ ðŸ™ [[æˆä¸ºæ”¯æŒè€…](https://opencollective.com/gitea#backer)]
+
+<a href="https://opencollective.com/gitea#backers" target="_blank"><img src="https://opencollective.com/gitea/backers.svg?width=890"></a>
+
+## 赞助商
+
+通过æˆä¸ºèµžåŠ©å•†æ¥æ”¯æŒè¿™ä¸ªé¡¹ç›®ã€‚您的标志将显示在这里,并带有链接到您的网站。 [[æˆä¸ºèµžåЩ商](https://opencollective.com/gitea#sponsor)]
+
+<a href="https://opencollective.com/gitea/sponsor/0/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/0/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/1/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/1/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/2/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/2/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/3/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/3/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/4/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/4/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/5/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/5/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/6/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/6/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/7/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/7/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/8/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/8/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/9/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/9/avatar.svg"></a>
+
+## 常è§é—®é¢˜
+
+**Gitea 怎么å‘音?**
+
+Gitea çš„å‘音是 [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY)ï¼Œå°±åƒ "gi-tea" 一样,g 是硬音。
+
+**为什么这个项目没有托管在 Gitea 实例上?**
+
+我们正在 [努力](https://github.com/go-gitea/gitea/issues/1029)。
+
+**在哪里å¯ä»¥æ‰¾åˆ°å®‰å…¨è¡¥ä¸ï¼Ÿ**
+
+在 [å‘布日志](https://github.com/go-gitea/gitea/releases) 或 [å˜æ›´æ—¥å¿—](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) 中,æœç´¢å…³é”®è¯ `SECURITY` 以找到安全补ä¸ã€‚
+
+## 许å¯è¯
+
+è¿™ä¸ªé¡¹ç›®æ˜¯æ ¹æ® MIT 许å¯è¯æŽˆæƒçš„。
+请å‚阅 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件以获å–完整的许å¯è¯æ–‡æœ¬ã€‚
+
+## 进一步信æ¯
+
+<details>
+<summary>å¯»æ‰¾ç•Œé¢æ¦‚述?查看这里ï¼</summary>
+
+### 登录/注册页é¢
+
+![Login](https://dl.gitea.com/screenshots/login.png)
+![Register](https://dl.gitea.com/screenshots/register.png)
+
+### 用户仪表æ¿
+
+![Home](https://dl.gitea.com/screenshots/home.png)
+![Issues](https://dl.gitea.com/screenshots/issues.png)
+![Pull Requests](https://dl.gitea.com/screenshots/pull_requests.png)
+![Milestones](https://dl.gitea.com/screenshots/milestones.png)
+
+### 用户资料
+
+![Profile](https://dl.gitea.com/screenshots/user_profile.png)
+
+### 探索
+
+![Repos](https://dl.gitea.com/screenshots/explore_repos.png)
+![Users](https://dl.gitea.com/screenshots/explore_users.png)
+![Orgs](https://dl.gitea.com/screenshots/explore_orgs.png)
+
+### 仓库
+
+![Home](https://dl.gitea.com/screenshots/repo_home.png)
+![Commits](https://dl.gitea.com/screenshots/repo_commits.png)
+![Branches](https://dl.gitea.com/screenshots/repo_branches.png)
+![Labels](https://dl.gitea.com/screenshots/repo_labels.png)
+![Milestones](https://dl.gitea.com/screenshots/repo_milestones.png)
+![Releases](https://dl.gitea.com/screenshots/repo_releases.png)
+![Tags](https://dl.gitea.com/screenshots/repo_tags.png)
+
+#### 仓库问题
+
+![List](https://dl.gitea.com/screenshots/repo_issues.png)
+![Issue](https://dl.gitea.com/screenshots/repo_issue.png)
+
+#### 仓库拉å–请求
+
+![List](https://dl.gitea.com/screenshots/repo_pull_requests.png)
+![Pull Request](https://dl.gitea.com/screenshots/repo_pull_request.png)
+![File](https://dl.gitea.com/screenshots/repo_pull_request_file.png)
+![Commits](https://dl.gitea.com/screenshots/repo_pull_request_commits.png)
+
+#### 仓库æ“作
+
+![List](https://dl.gitea.com/screenshots/repo_actions.png)
+![Details](https://dl.gitea.com/screenshots/repo_actions_run.png)
+
+#### 仓库活动
+
+![Activity](https://dl.gitea.com/screenshots/repo_activity.png)
+![Contributors](https://dl.gitea.com/screenshots/repo_contributors.png)
+![Code Frequency](https://dl.gitea.com/screenshots/repo_code_frequency.png)
+![Recent Commits](https://dl.gitea.com/screenshots/repo_recent_commits.png)
+
+### 组织
+
+![Home](https://dl.gitea.com/screenshots/org_home.png)
+
+</details>
diff --git a/README.zh-tw.md b/README.zh-tw.md
new file mode 100644
index 0000000000..9de3f85dd5
--- /dev/null
+++ b/README.zh-tw.md
@@ -0,0 +1,206 @@
+# Gitea
+
+[![](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
+[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
+[![](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
+[![](https://pkg.go.dev/badge/code.gitea.io/gitea?status.svg)](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
+[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
+[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
+[![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
+[![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT")
+[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod&color=green)](https://gitpod.io/#https://github.com/go-gitea/gitea)
+[![](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com "Crowdin")
+
+[English](./README.md) | [简体中文](./README.zh-cn.md)
+
+## 目的
+
+這個項目的目標是æä¾›æœ€ç°¡å–®ã€æœ€å¿«é€Ÿã€æœ€ç„¡ç—›çš„æ–¹å¼ä¾†è¨­ç½®è‡ªè¨—管的 Git æœå‹™ã€‚
+
+由於 Gitea 是用 Go 語言編寫的,它å¯ä»¥åœ¨ Go 支æ´çš„æ‰€æœ‰å¹³å°å’Œæž¶æ§‹ä¸Šé‹è¡Œï¼ŒåŒ…括 Linuxã€macOS å’Œ Windows çš„ x86ã€amd64ã€ARM å’Œ PowerPC 架構。這個項目自 2016 å¹´ 11 月從 [Gogs](https://gogs.io) [分å‰](https://blog.gitea.com/welcome-to-gitea/) 而來,但已經有了很多變化。
+
+在線演示å¯ä»¥è¨ªå• [demo.gitea.com](https://demo.gitea.com)。
+
+è¦è¨ªå•å…費的 Gitea æœå‹™ï¼ˆæœ‰ä¸€å®šæ•¸é‡çš„倉庫é™åˆ¶ï¼‰ï¼Œå¯ä»¥è¨ªå• [gitea.com](https://gitea.com/user/login)。
+
+è¦å¿«é€Ÿéƒ¨ç½²æ‚¨è‡ªå·±çš„專用 Gitea 實例,å¯ä»¥åœ¨ [cloud.gitea.com](https://cloud.gitea.com) é–‹å§‹å…費試用。
+
+## 文件
+
+您å¯ä»¥åœ¨æˆ‘們的官方 [文件網站](https://docs.gitea.com/) 上找到全é¢çš„æ–‡ä»¶ã€‚
+
+它包括安è£ã€ç®¡ç†ã€ä½¿ç”¨ã€é–‹ç™¼ã€è²¢ç»æŒ‡å—等,幫助您快速入門並有效地探索所有功能。
+
+如果您有任何建議或想è¦è²¢ç»ï¼Œå¯ä»¥è¨ªå• [文件倉庫](https://gitea.com/gitea/docs)
+
+## 構建
+
+從æºä»£ç¢¼æ¨¹çš„æ ¹ç›®éŒ„é‹è¡Œï¼š
+
+ TAGS="bindata" make build
+
+å¦‚æžœéœ€è¦ SQLite 支æ´ï¼š
+
+ TAGS="bindata sqlite sqlite_unlock_notify" make build
+
+`build` 目標分為兩個å­ç›®æ¨™ï¼š
+
+- `make backend` éœ€è¦ [Go Stable](https://go.dev/dl/),所需版本在 [go.mod](/go.mod) 中定義。
+- `make frontend` éœ€è¦ [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
+
+需è¦äº’è¯ç¶²é€£æŽ¥ä¾†ä¸‹è¼‰ go å’Œ npm 模塊。從包å«é æ§‹å»ºå‰ç«¯æ–‡ä»¶çš„官方æºä»£ç¢¼å£“ç¸®åŒ…æ§‹å»ºæ™‚ï¼Œä¸æœƒè§¸ç™¼ `frontend` 目標,因此å¯ä»¥åœ¨æ²’有 Node.js 的情æ³ä¸‹æ§‹å»ºã€‚
+
+更多信æ¯ï¼šhttps://docs.gitea.com/installation/install-from-source
+
+## 使用
+
+æ§‹å»ºå¾Œï¼Œé»˜èªæƒ…æ³ä¸‹æœƒåœ¨æºä»£ç¢¼æ¨¹çš„æ ¹ç›®éŒ„生æˆä¸€å€‹å為 `gitea` 的二進制文件。è¦é‹è¡Œå®ƒï¼Œè«‹ä½¿ç”¨ï¼š
+
+ ./gitea web
+
+> [!注æ„]
+> 如果您å°ä½¿ç”¨æˆ‘們的 API 感興趣,我們æä¾›äº†å¯¦é©—性支æ´ï¼Œä¸¦é™„有 [文件](https://docs.gitea.com/api)。
+
+## è²¢ç»
+
+é æœŸçš„工作æµç¨‹æ˜¯ï¼šFork -> Patch -> Push -> Pull Request
+
+> [!注æ„]
+>
+> 1. **在開始進行 Pull Request 之å‰ï¼Œæ‚¨å¿…須閱讀 [è²¢ç»è€…指å—](CONTRIBUTING.md)。**
+> 2. 如果您在項目中發ç¾äº†æ¼æ´žï¼Œè«‹ç§ä¸‹å¯«ä¿¡çµ¦ **security@gitea.io**。è¬è¬ï¼
+
+## 翻譯
+
+[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com)
+
+ç¿»è­¯é€šéŽ [Crowdin](https://translate.gitea.com) é€²è¡Œã€‚å¦‚æžœæ‚¨æƒ³ç¿»è­¯æˆæ–°çš„語言,請在 Crowdin 項目中請求管ç†å“¡æ·»åŠ æ–°èªžè¨€ã€‚
+
+您也å¯ä»¥å‰µå»ºä¸€å€‹ issue 來添加語言,或者在 discord çš„ #translation é »é“上詢å•。如果您需è¦ä¸Šä¸‹æ–‡æˆ–發ç¾ä¸€äº›ç¿»è­¯å•題,å¯ä»¥åœ¨å­—符串上留言或在 Discord 上詢å•ã€‚å°æ–¼ä¸€èˆ¬çš„翻譯å•é¡Œï¼Œæ–‡æª”ä¸­æœ‰ä¸€å€‹éƒ¨åˆ†ã€‚ç›®å‰æœ‰é»žç©ºï¼Œä½†æˆ‘們希望隨著å•題的出ç¾è€Œå¡«å……它。
+
+更多信æ¯è«‹åƒé–± [文件](https://docs.gitea.com/contributing/localization)。
+
+## 官方和第三方項目
+
+我們æä¾›äº†ä¸€å€‹å®˜æ–¹çš„ [go-sdk](https://gitea.com/gitea/go-sdk),一個å為 [tea](https://gitea.com/gitea/tea) çš„ CLI 工具和一個 Gitea Action çš„ [action runner](https://gitea.com/gitea/act_runner)。
+
+我們在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 維護了一個 Gitea 相關項目的列表,您å¯ä»¥åœ¨é‚£è£¡ç™¼ç¾æ›´å¤šçš„第三方項目,包括 SDKã€æ’ä»¶ã€ä¸»é¡Œç­‰ã€‚
+
+## 通訊
+
+[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
+
+如果您有任何文件未涵蓋的å•題,å¯ä»¥åœ¨æˆ‘們的 [Discord æœå‹™å™¨](https://discord.gg/Gitea) 上與我們è¯ç¹«ï¼Œæˆ–者在 [discourse 論壇](https://forum.gitea.com/) 上創建帖å­ã€‚
+
+## 作者
+
+- [維護者](https://github.com/orgs/go-gitea/people)
+- [è²¢ç»è€…](https://github.com/go-gitea/gitea/graphs/contributors)
+- [翻譯者](options/locale/TRANSLATORS)
+
+## 支æŒè€…
+
+æ„Ÿè¬æ‰€æœ‰æ”¯æŒè€…ï¼ ðŸ™ [[æˆç‚ºæ”¯æŒè€…](https://opencollective.com/gitea#backer)]
+
+<a href="https://opencollective.com/gitea#backers" target="_blank"><img src="https://opencollective.com/gitea/backers.svg?width=890"></a>
+
+## 贊助商
+
+é€šéŽæˆç‚ºè´ŠåŠ©å•†ä¾†æ”¯æŒé€™å€‹é …ç›®ã€‚æ‚¨çš„æ¨™èªŒå°‡é¡¯ç¤ºåœ¨é€™è£¡ï¼Œä¸¦å¸¶æœ‰éˆæŽ¥åˆ°æ‚¨çš„ç¶²ç«™ã€‚ [[æˆç‚ºè´ŠåЩ商](https://opencollective.com/gitea#sponsor)]
+
+<a href="https://opencollective.com/gitea/sponsor/0/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/0/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/1/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/1/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/2/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/2/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/3/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/3/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/4/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/4/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/5/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/5/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/6/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/6/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/7/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/7/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/8/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/8/avatar.svg"></a>
+<a href="https://opencollective.com/gitea/sponsor/9/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/9/avatar.svg"></a>
+
+## 常見å•題
+
+**Gitea 怎麼發音?**
+
+Gitea 的發音是 [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY)ï¼Œå°±åƒ "gi-tea" 一樣,g 是硬音。
+
+**為什麼這個項目沒有託管在 Gitea 實例上?**
+
+我們正在 [努力](https://github.com/go-gitea/gitea/issues/1029)。
+
+**在哪裡å¯ä»¥æ‰¾åˆ°å®‰å…¨è£œä¸ï¼Ÿ**
+
+在 [發佈日誌](https://github.com/go-gitea/gitea/releases) 或 [變更日誌](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) 中,æœç´¢é—œéµè©ž `SECURITY` 以找到安全補ä¸ã€‚
+
+## 許å¯è­‰
+
+這個項目是根據 MIT 許å¯è­‰æŽˆæ¬Šçš„。
+è«‹åƒé–± [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件以ç²å–完整的許å¯è­‰æ–‡æœ¬ã€‚
+
+## 進一步信æ¯
+
+<details>
+<summary>å°‹æ‰¾ç•Œé¢æ¦‚述?查看這裡ï¼</summary>
+
+### 登錄/註冊é é¢
+
+![Login](https://dl.gitea.com/screenshots/login.png)
+![Register](https://dl.gitea.com/screenshots/register.png)
+
+### 用戶儀表æ¿
+
+![Home](https://dl.gitea.com/screenshots/home.png)
+![Issues](https://dl.gitea.com/screenshots/issues.png)
+![Pull Requests](https://dl.gitea.com/screenshots/pull_requests.png)
+![Milestones](https://dl.gitea.com/screenshots/milestones.png)
+
+### 用戶資料
+
+![Profile](https://dl.gitea.com/screenshots/user_profile.png)
+
+### 探索
+
+![Repos](https://dl.gitea.com/screenshots/explore_repos.png)
+![Users](https://dl.gitea.com/screenshots/explore_users.png)
+![Orgs](https://dl.gitea.com/screenshots/explore_orgs.png)
+
+### 倉庫
+
+![Home](https://dl.gitea.com/screenshots/repo_home.png)
+![Commits](https://dl.gitea.com/screenshots/repo_commits.png)
+![Branches](https://dl.gitea.com/screenshots/repo_branches.png)
+![Labels](https://dl.gitea.com/screenshots/repo_labels.png)
+![Milestones](https://dl.gitea.com/screenshots/repo_milestones.png)
+![Releases](https://dl.gitea.com/screenshots/repo_releases.png)
+![Tags](https://dl.gitea.com/screenshots/repo_tags.png)
+
+#### 倉庫å•題
+
+![List](https://dl.gitea.com/screenshots/repo_issues.png)
+![Issue](https://dl.gitea.com/screenshots/repo_issue.png)
+
+#### 倉庫拉å–請求
+
+![List](https://dl.gitea.com/screenshots/repo_pull_requests.png)
+![Pull Request](https://dl.gitea.com/screenshots/repo_pull_request.png)
+![File](https://dl.gitea.com/screenshots/repo_pull_request_file.png)
+![Commits](https://dl.gitea.com/screenshots/repo_pull_request_commits.png)
+
+#### 倉庫æ“作
+
+![List](https://dl.gitea.com/screenshots/repo_actions.png)
+![Details](https://dl.gitea.com/screenshots/repo_actions_run.png)
+
+#### 倉庫活動
+
+![Activity](https://dl.gitea.com/screenshots/repo_activity.png)
+![Contributors](https://dl.gitea.com/screenshots/repo_contributors.png)
+![Code Frequency](https://dl.gitea.com/screenshots/repo_code_frequency.png)
+![Recent Commits](https://dl.gitea.com/screenshots/repo_recent_commits.png)
+
+### 組織
+
+![Home](https://dl.gitea.com/screenshots/org_home.png)
+
+</details>
diff --git a/README_ZH.md b/README_ZH.md
deleted file mode 100644
index a2e36dc22f..0000000000
--- a/README_ZH.md
+++ /dev/null
@@ -1,61 +0,0 @@
-# Gitea
-
-[![](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
-[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
-[![](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
-[![](https://pkg.go.dev/badge/code.gitea.io/gitea?status.svg)](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
-[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
-[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
-[![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
-[![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT")
-[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod&color=green)](https://gitpod.io/#https://github.com/go-gitea/gitea)
-[![](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea "Crowdin")
-
-[View this document in English](./README.md)
-
-## 目标
-
-Gitea 的首è¦ç›®æ ‡æ˜¯åˆ›å»ºä¸€ä¸ªæžæ˜“安装,è¿è¡Œéžå¸¸å¿«é€Ÿï¼Œå®‰è£…和使用体验良好的自建 Git æœåŠ¡ã€‚æˆ‘ä»¬é‡‡ç”¨ Go 作为åŽç«¯è¯­è¨€ï¼Œè¿™ä½¿æˆ‘们åªè¦ç”Ÿæˆä¸€ä¸ªå¯æ‰§è¡Œç¨‹åºå³å¯ã€‚并且他还支æŒè·¨å¹³å°ï¼Œæ”¯æŒ Linux, macOS å’Œ Windows 以åŠå„ç§æž¶æž„,除了 x86,amd64,还包括 ARM å’Œ PowerPC。
-
-如果你想试用在线演示和报告问题,请访问 [demo.gitea.com](https://demo.gitea.com/)。
-
-如果你想使用å…费的 Gitea æœåŠ¡ï¼ˆæœ‰ä»“åº“æ•°é‡é™åˆ¶ï¼‰ï¼Œè¯·è®¿é—® [gitea.com](https://gitea.com/user/login)。
-
-如果你想在 Gitea Cloud 上快速部署你自己独享的 Gitea 实例,请访问 [cloud.gitea.com](https://cloud.gitea.com) 开始å…费试用。
-
-## æç¤º
-
-1. **开始贡献代ç ä¹‹å‰è¯·ç¡®ä¿ä½ å·²ç»çœ‹è¿‡äº† [贡献者å‘导(英文)](CONTRIBUTING.md)**.
-2. 所有的安全问题,请ç§ä¸‹å‘é€é‚®ä»¶ç»™ **security@gitea.io**。谢谢ï¼
-3. 如果你è¦ä½¿ç”¨API,请å‚è§ [API 文档](https://godoc.org/code.gitea.io/sdk/gitea).
-
-## 文档
-
-关于如何安装请访问我们的 [文档站](https://docs.gitea.com/zh-cn/category/installation),如果没有找到对应的文档,你也å¯ä»¥é€šè¿‡ [Discord - 英文](https://discord.gg/gitea) å’Œ QQ群 328432459 æ¥å’Œæˆ‘们交æµã€‚
-
-## 贡献æµç¨‹
-
-Fork -> Patch -> Push -> Pull Request
-
-## 翻译
-
-多语言翻译是基于Crowdin进行的.
-[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea)
-
-## 作者
-
-* [Maintainers](https://github.com/orgs/go-gitea/people)
-* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
-* [Translators](options/locale/TRANSLATORS)
-
-## 授æƒè®¸å¯
-
-本项目采用 MIT å¼€æºæŽˆæƒè®¸å¯è¯ï¼Œå®Œæ•´çš„æŽˆæƒè¯´æ˜Žå·²æ”¾ç½®åœ¨ [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件中。
-
-## 截图
-
-|![Dashboard](https://dl.gitea.com/screenshots/home_timeline.png)|![User Profile](https://dl.gitea.com/screenshots/user_profile.png)|![Global Issues](https://dl.gitea.com/screenshots/global_issues.png)|
-|:---:|:---:|:---:|
-|![Branches](https://dl.gitea.com/screenshots/branches.png)|![Web Editor](https://dl.gitea.com/screenshots/web_editor.png)|![Activity](https://dl.gitea.com/screenshots/activity.png)|
-|![New Migration](https://dl.gitea.com/screenshots/migration.png)|![Migrating](https://dl.gitea.com/screenshots/migration.gif)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)|
-|![Pull Request Dark](https://dl.gitea.com/screenshots/pull_requests_dark.png)|![Diff Review Dark](https://dl.gitea.com/screenshots/review_dark.png)|![Diff Dark](https://dl.gitea.com/screenshots/diff_dark.png)|
diff --git a/SECURITY.md b/SECURITY.md
index c9dbf859f5..d7c27ea613 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -14,12 +14,12 @@ Please **DO NOT** file a public issue, instead send your report privately to `se
Due to the sensitive nature of security information, you can use the below GPG public key to encrypt your mail body.
-The PGP key is valid until July 9, 2025.
+The PGP key is valid until July 4, 2026.
```
Key ID: 6FCD2D5B
Key Type: RSA
-Expires: 7/9/2025
+Expires: 7/4/2026
Key Size: 4096/4096
Fingerprint: 3DE0 3D1E 144A 7F06 9359 99DC AAFD 2381 6FCD 2D5B
```
@@ -42,18 +42,18 @@ lzpAjnN9/KLtQroutrm+Ft0mdjDiJUeFVl1cOHDhoyfCsQh62HumoyZoZvqzQd6e
AbN11nq6aViMe2Q3je1AbiBnRnQSHxt1Tc8X4IshO3MQK1Sk7oPI6LA5oQARAQAB
tCJHaXRlYSBTZWN1cml0eSA8c2VjdXJpdHlAZ2l0ZWEuaW8+iQJXBBMBCABBAhsD
BQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAFiEEPeA9HhRKfwaTWZncqv0jgW/N
-LVsFAmaMse0FCQW4fW8ACgkQqv0jgW/NLVtXLg/+PF4G9Jhlui15BTNlEBJAV2P/
-1QlAV2krk0fP7tykn0FR9RfGIfVV/kwC1f+ouosYPQDDevl9LWdUIM+g94DtNo2o
-7ACpcL3morvt5lVGpIZHL8TbX0qmFRXL/pB/cB+K6IwYvh2mrbp2zH+r4SCRyFYq
-BjgXYFTI1MylJ1ShAjU6Z+m3oJ+2xs5LzHS0X6zkTjzA2Zl4zQzciQ9T+wJcE7Zi
-HXdM1+YMF8KGNP8J9Rpug5oNDJ98lgZirRY7c3A/1xmYBiPnULwuuymdqEZO7l70
-SeAlE1RWYX8kbOBnBb/KY4XwE3Vic1oEzc9DiPWVH1ElX86WNNsFzuyULiwoBoWg
-pqZGhL9x1p5+46RGQSDczsHM7YGVtfYOiDo2PAVrmwsT0BnXnK8Oe3YIkvmUPEJu
-OkLt0Z6A5n8pz8zhQzuApwBsK4ncJ8zTCpvz/pfKKqZC/Vnoh3gKGhDGvOZ+b5IJ
-0kUTe2JsbnwFixDUMDtacQ1op8XOyLoLVmgqLn0+Pws4XPBlMof2bioFir3yHKnP
-gNchsF1agrlSIo5GA8u4ga+IlCSfvFIKrl7+cxacKcJYt/vbOU5KcvVJI5EtHKCG
-xfHjHY2ah1Qww7SxW6IXiRZZzPpsL2mBM2CD7N3qh9bV2s27wxYCdUodsIZbiyHe
-oWPzfBnkmiAN8KlZxHm5Ag0EYrVn/gEQALrFLQjCR3GjuHSindz0rd3Fnx/t7Sen
+LVsFAmhoHmkFCQeT6esACgkQqv0jgW/NLVuFLRAAmjBQSKRAgs2bFIEj7HLAbDp4
+f+XkdH+GsT3jRPOZ9QZgmtM+TfoE4yNgIVfOl+s4RdjM/W4QzqZuPQ55hbEHd056
+cJmm7B+6GsHFcdrPmh65sOCEIyh4+t45dUfeWpFsDPqm9j1UHXAJQIpB8vDEVAPH
+t+3wLCk8GMPJs1o5tIyMmaO23ngvkwn8eG7KgY+rp2PzObrb5g7ppci0ILzILkrp
+HVjZsEfUWRgSVF7LuU5ppqDKrlcqwUpQq6n3kGMZcLrCp6ACKP04TBmTfUxNwdL7
+I0N7apI2Pbct9T1Gv/lYAUFWyU2c3gh/EBLbO6BukaLOFRQHrtNfdJV/YnMPlcXr
+LUJjK9K4eAH9DsrZqrisz/LthsC2BaNIN3KRMTk5YTYgmIh8GXzSgihORmtDFELC
+RroID3pTuS0zjXh+wpY9GuPTh7UW23p42Daxca4fAT4k5EclvDRUrL21xMopPMiL
+HuNdELz4FVchRTy05PjzKVyjVInDNojE2KUxnjxZDzYJ6aT/g+coD5yfntYm8BEj
++ZzL0ndZES54hzKLpv7zwBQwFzam68clZYmDPILOPTflQDfpGEWmJK4undFU5obz
+ZsQRz0R3ulspChATbZxO0d5LX2obLpKO9X3b5VoO1KF+R8Vjw1Y0KxrNZ6rIcfqH
+Z50QVQKSe9dm08K0ON+5Ag0EYrVn/gEQALrFLQjCR3GjuHSindz0rd3Fnx/t7Sen
T+p07yCSSoSlmnJHCQmwh4vfg1blyz0zZ4vkIhtpHsEgc+ZAG+WQXSsJ2iRz+eSN
GwoOQl4XC3n+QWkc1ws+btr48+6UqXIQU+F8TPQyx/PIgi2nZXJB7f5+mjCqsk46
XvH4nTr4kJjuqMSR/++wvre2qNQRa/q/dTsK0OaN/mJsdX6Oi+aGNaQJUhIG7F+E
@@ -65,19 +65,19 @@ s+GsP9I3cmWWQcKYxWHtE8xTXnNCVPFZQj2nwhJzae8ypfOtulBRA3dUKWGKuDH/
axFENhUsT397aOU3qkP/od4a64JyNIEo4CTTSPVeWd7njsGqli2U3A4xL2CcyYvt
D/MWcMBGEoLSNTswwKdom4FaJpn5KThnK/T0bQcmJblJhoCtppXisbexZnCpuS0x
Zdlm2T14KJ3LABEBAAGJAjwEGAEIACYCGwwWIQQ94D0eFEp/BpNZmdyq/SOBb80t
-WwUCZoyyjQUJBbh+DwAKCRCq/SOBb80tW18XD/9MXztmf01MT+1kZdBouZ/7Rp/7
-9kuqo//B1G+RXau4oFtPqb67kNe2WaIc3u5B73PUHsMf3i6z4ib2KbMhZZerLn0O
-dRglcuPeNWmsASY3dH/XVG0cT0zvvWegagd12TJEl3Vs+7XNrOw4cwDj9L1+GH9m
-kSt4uaANWn/6a3RvMRhiVEYuNwhAzcKaactPmYqrLJgoVLbRSDkgyHaMQ2jKgLxk
-ifS/fvluGV0ub2Po6DJiqfRpd1tDvPhe9y1+r1WFDZsOcvTcZUfSt/7dXMGfqGu0
-2daVFlfeSXSALrDE5uc0UxodHCpP3sqRYDZevGLBRaaTkIjYXG/+N898+7K5WJF4
-xXOLWxM2cwGkG7eC9pugcDnBp9XlF7O+GBiZ05JUe5flXDQFZ+h3exjopu6KHF1B
-RnzNy8LC0UKb+AuvRIOLV92a9Q9wGWU/jaVDu6nZ0umAeuSzxiHoDsonm0Fl9QAz
-2/xCokebuoeLrEK7R2af3X86mqq3sVO4ax+HPYChzOaVQBiHUW/TAldWcldYYphR
-/e2WsbmQfvCRtz/bZfo+aUVnrHNjzVMtF2SszdVmA/04Y8pS28MqtuRqhm5DPOOd
-g1YeUywK5jRZ1twyo1kzJEFPLaoeaXaycsR1PMVBW0Urik5mrR/pOWq7PPoZoKb2
-lXYLE8bwkuQTmsyL1g==
-=9i7d
+WwUCaGgeJAUJB5PppgAKCRCq/SOBb80tW/NWEACB6Jrf0gWlk7e+hNCdnbM0ZVWU
+f2sHNFfXxxsdhpcDgKbNHtkZb8nZgv8AX+5fTtUwMVa3vKcdw30xFiIM5N7cCIPV
+vg/5z5BtfEaitnabEUG2iiVDIy8IHXIcK10rX+7BosA3QDl2PsiBHwyi5G13lRk8
+zGTSNDuOalug33h5/lr2dPigamkq74Aoy29q8Rjad6GfWHipL2bFimgtY+Zdi0BH
+NLk4EJXxj1SgVx5dtkQzWJReBA5M+FQ4QYQZBO+f4TDoOLmjui152uhkoLBQbGAa
+WWJFTVxm0bG5MXloEL3gA8DfU7XDwuW/sHJC5pBko8RpQViooOhckMepZV3Y83DK
+bwLYa3JmPgj2rEv4993dvrJbQhpGd082HOxOsllCs8pgNq1SnXpWYfcGTgGKC3ts
+U8YZUUJUQ7mi2L8Tv3ix20c9EiGmA30JAmA8eZTC3cWup91ZkkVBFRml2czTXajd
+RWZ6GbHV5503ueDQcB8yBVgF3CSixs67+dGSbD3p86OqGrjAcJzM5TFbNKcnGLdE
+kGbZpNwAISy750lXzXKmyrh5RTCeTOQerbwCMBvHZO+HAevA/LXDTw2OAiSIQlP5
+sYA4sFYLQ30OAkgJcmdp/pSgVj/erNtSN07ClrOpDb/uFpQymO6K2h0Pst3feNVK
+9M2VbqL9C51z/wyHLg==
+=SfZA
-----END PGP PUBLIC KEY BLOCK-----
```
diff --git a/assets/go-licenses.json b/assets/go-licenses.json
index 8c155ceadb..d961444239 100644
--- a/assets/go-licenses.json
+++ b/assets/go-licenses.json
@@ -115,8 +115,8 @@
"licenseText": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
},
{
- "name": "github.com/RoaringBitmap/roaring",
- "path": "github.com/RoaringBitmap/roaring/LICENSE",
+ "name": "github.com/RoaringBitmap/roaring/v2",
+ "path": "github.com/RoaringBitmap/roaring/v2/LICENSE",
"licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2016 by the authors\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n================================================================================\n\nPortions of runcontainer.go are from the Go standard library, which is licensed\nunder:\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the following disclaimer\n in the documentation and/or other materials provided with the\n distribution.\n * Neither the name of Google Inc. nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
},
{
@@ -290,6 +290,16 @@
"licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License."
},
{
+ "name": "github.com/bmatcuk/doublestar/v4",
+ "path": "github.com/bmatcuk/doublestar/v4/LICENSE",
+ "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Bob Matcuk\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
+ },
+ {
+ "name": "github.com/bohde/codel",
+ "path": "github.com/bohde/codel/LICENSE",
+ "licenseText": "BSD 3-Clause License\n\nCopyright (c) 2018, Rowan Bohde\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* Neither the name of the copyright holder nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+ },
+ {
"name": "github.com/boombuler/barcode",
"path": "github.com/boombuler/barcode/LICENSE",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Florian Sundermann\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
@@ -527,7 +537,7 @@
{
"name": "github.com/go-ldap/ldap/v3",
"path": "github.com/go-ldap/ldap/v3/LICENSE",
- "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)\nPortions copyright (c) 2015-2016 go-ldap Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
+ "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)\nPortions copyright (c) 2015-2024 go-ldap Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
},
{
"name": "github.com/go-redsync/redsync/v4",
@@ -590,11 +600,6 @@
"licenseText": "Copyright (c) 2017 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
},
{
- "name": "github.com/golang/geo",
- "path": "github.com/golang/geo/LICENSE",
- "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
- },
- {
"name": "github.com/golang/groupcache/lru",
"path": "github.com/golang/groupcache/lru/LICENSE",
"licenseText": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, \"control\" means (i) the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n\"submitted\" means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution.\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\nYou must cause any modified files to carry prominent notices stating that You\nchanged the files; and\nYou must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n5. Submission of Contributions.\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n6. Trademarks.\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty.\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n8. Limitation of Liability.\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability.\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"[]\" replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same \"printed page\" as the copyright notice for easier identification within\nthird-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
@@ -615,8 +620,13 @@
"licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
},
{
- "name": "github.com/google/go-github/v61/github",
- "path": "github.com/google/go-github/v61/github/LICENSE",
+ "name": "github.com/google/flatbuffers/go",
+ "path": "github.com/google/flatbuffers/go/LICENSE",
+ "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
+ },
+ {
+ "name": "github.com/google/go-github/v71/github",
+ "path": "github.com/google/go-github/v71/github/LICENSE",
"licenseText": "Copyright (c) 2013 The go-github AUTHORS. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
},
{
@@ -745,11 +755,6 @@
"licenseText": "Copyright (c) 2017 Kevin Burke.\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\n===================\n\nThe lexer and parser borrow heavily from github.com/pelletier/go-toml. The\nlicense for that project is copied below.\n\nThe MIT License (MIT)\n\nCopyright (c) 2013 - 2017 Thomas Pelletier, Eric Anderton\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
},
{
- "name": "github.com/keybase/go-crypto",
- "path": "github.com/keybase/go-crypto/LICENSE",
- "licenseText": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
- },
- {
"name": "github.com/klauspost/compress",
"path": "github.com/klauspost/compress/LICENSE",
"licenseText": "Copyright (c) 2012 The Go Authors. All rights reserved.\nCopyright (c) 2019 Klaus Post. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n------------------\n\nFiles: gzhttp/*\n\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2016-2017 The New York Times Company\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n------------------\n\nFiles: s2/cmd/internal/readahead/*\n\nThe MIT License (MIT)\n\nCopyright (c) 2015 Klaus Post\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n---------------------\nFiles: snappy/*\nFiles: internal/snapref/*\n\nCopyright (c) 2011 The Snappy-Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n-----------------\n\nFiles: s2/cmd/internal/filepathx/*\n\nCopyright 2016 The filepathx Authors\n\nPermission 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:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
@@ -825,13 +830,18 @@
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2016 Yasuhiro Matsumoto\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
},
{
+ "name": "github.com/mattn/go-shellwords",
+ "path": "github.com/mattn/go-shellwords/LICENSE",
+ "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2017 Yasuhiro Matsumoto\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
+ },
+ {
"name": "github.com/meilisearch/meilisearch-go",
"path": "github.com/meilisearch/meilisearch-go/LICENSE",
- "licenseText": "MIT License\n\nCopyright (c) 2020-2024 Meili SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
+ "licenseText": "MIT License\n\nCopyright (c) 2020-2025 Meili SAS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
},
{
- "name": "github.com/mholt/acmez/v2",
- "path": "github.com/mholt/acmez/v2/LICENSE",
+ "name": "github.com/mholt/acmez/v3",
+ "path": "github.com/mholt/acmez/v3/LICENSE",
"licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
},
{
@@ -860,6 +870,11 @@
"licenseText": "BSD 3-Clause License\n\nCopyright (c) 2009, The Go Authors. Extensions copyright (c) 2011, Miek Gieben. \nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
},
{
+ "name": "github.com/minio/crc64nvme",
+ "path": "github.com/minio/crc64nvme/LICENSE",
+ "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
+ },
+ {
"name": "github.com/minio/md5-simd",
"path": "github.com/minio/md5-simd/LICENSE",
"licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
@@ -937,7 +952,7 @@
{
"name": "github.com/pjbgf/sha1cd",
"path": "github.com/pjbgf/sha1cd/LICENSE",
- "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
+ "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2023 pjbgf\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
},
{
"name": "github.com/pkg/errors",
@@ -1065,9 +1080,14 @@
"licenseText": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, \"control\" means (i) the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n\"submitted\" means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution.\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\nYou must cause any modified files to carry prominent notices stating that You\nchanged the files; and\nYou must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n5. Submission of Contributions.\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n6. Trademarks.\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty.\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n8. Limitation of Liability.\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability.\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"[]\" replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same \"printed page\" as the copyright notice for easier identification within\nthird-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License."
},
{
- "name": "github.com/urfave/cli/v2",
- "path": "github.com/urfave/cli/v2/LICENSE",
- "licenseText": "MIT License\n\nCopyright (c) 2022 urfave/cli maintainers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
+ "name": "github.com/urfave/cli-docs/v3",
+ "path": "github.com/urfave/cli-docs/v3/LICENSE",
+ "licenseText": "MIT License\n\nCopyright (c) 2023 urfave/cli maintainers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
+ },
+ {
+ "name": "github.com/urfave/cli/v3",
+ "path": "github.com/urfave/cli/v3/LICENSE",
+ "licenseText": "MIT License\n\nCopyright (c) 2023 urfave/cli maintainers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
},
{
"name": "github.com/valyala/fastjson",
@@ -1077,7 +1097,7 @@
{
"name": "github.com/wneessen/go-mail",
"path": "github.com/wneessen/go-mail/LICENSE",
- "licenseText": "MIT License\n\nCopyright (c) 2022-2023 The go-mail Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
+ "licenseText": "MIT License\n\nCopyright (c) 2022-2025 The go-mail Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
},
{
"name": "github.com/wneessen/go-mail/smtp",
@@ -1090,21 +1110,11 @@
"licenseText": "MIT License\n\nCopyright (c) 2019 Montgomery Edwardsâ´â´â¸ and Faye Amacker\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
},
{
- "name": "github.com/xanzy/go-gitlab",
- "path": "github.com/xanzy/go-gitlab/LICENSE",
- "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"{}\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright {yyyy} {name of copyright owner}\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
- },
- {
"name": "github.com/xanzy/ssh-agent",
"path": "github.com/xanzy/ssh-agent/LICENSE",
"licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"{}\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright {yyyy} {name of copyright owner}\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n"
},
{
- "name": "github.com/xrash/smetrics",
- "path": "github.com/xrash/smetrics/LICENSE",
- "licenseText": "Copyright (C) 2016 Felipe da Cunha Gonçalves\nAll Rights Reserved.\n\nMIT LICENSE\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
- },
- {
"name": "github.com/yohcop/openid-go",
"path": "github.com/yohcop/openid-go/LICENSE",
"licenseText": "Copyright 2015 Yohann Coppel\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
@@ -1130,6 +1140,11 @@
"licenseText": "This work is released into the public domain with CC0 1.0.\n\n-------------------------------------------------------------------------------\n\nCreative Commons Legal Code\n\nCC0 1.0 Universal\n\n CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE\n LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN\n ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS\n INFORMATION ON AN \"AS-IS\" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES\n REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS\n PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM\n THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED\n HEREUNDER.\n\nStatement of Purpose\n\nThe laws of most jurisdictions throughout the world automatically confer\nexclusive Copyright and Related Rights (defined below) upon the creator\nand subsequent owner(s) (each and all, an \"owner\") of an original work of\nauthorship and/or a database (each, a \"Work\").\n\nCertain owners wish to permanently relinquish those rights to a Work for\nthe purpose of contributing to a commons of creative, cultural and\nscientific works (\"Commons\") that the public can reliably and without fear\nof later claims of infringement build upon, modify, incorporate in other\nworks, reuse and redistribute as freely as possible in any form whatsoever\nand for any purposes, including without limitation commercial purposes.\nThese owners may contribute to the Commons to promote the ideal of a free\nculture and the further production of creative, cultural and scientific\nworks, or to gain reputation or greater distribution for their Work in\npart through the use and efforts of others.\n\nFor these and/or other purposes and motivations, and without any\nexpectation of additional consideration or compensation, the person\nassociating CC0 with a Work (the \"Affirmer\"), to the extent that he or she\nis an owner of Copyright and Related Rights in the Work, voluntarily\nelects to apply CC0 to the Work and publicly distribute the Work under its\nterms, with knowledge of his or her Copyright and Related Rights in the\nWork and the meaning and intended legal effect of CC0 on those rights.\n\n1. Copyright and Related Rights. A Work made available under CC0 may be\nprotected by copyright and related or neighboring rights (\"Copyright and\nRelated Rights\"). Copyright and Related Rights include, but are not\nlimited to, the following:\n\n i. the right to reproduce, adapt, distribute, perform, display,\n communicate, and translate a Work;\n ii. moral rights retained by the original author(s) and/or performer(s);\niii. publicity and privacy rights pertaining to a person's image or\n likeness depicted in a Work;\n iv. rights protecting against unfair competition in regards to a Work,\n subject to the limitations in paragraph 4(a), below;\n v. rights protecting the extraction, dissemination, use and reuse of data\n in a Work;\n vi. database rights (such as those arising under Directive 96/9/EC of the\n European Parliament and of the Council of 11 March 1996 on the legal\n protection of databases, and under any national implementation\n thereof, including any amended or successor version of such\n directive); and\nvii. other similar, equivalent or corresponding rights throughout the\n world based on applicable law or treaty, and any national\n implementations thereof.\n\n2. Waiver. To the greatest extent permitted by, but not in contravention\nof, applicable law, Affirmer hereby overtly, fully, permanently,\nirrevocably and unconditionally waives, abandons, and surrenders all of\nAffirmer's Copyright and Related Rights and associated claims and causes\nof action, whether now known or unknown (including existing as well as\nfuture claims and causes of action), in the Work (i) in all territories\nworldwide, (ii) for the maximum duration provided by applicable law or\ntreaty (including future time extensions), (iii) in any current or future\nmedium and for any number of copies, and (iv) for any purpose whatsoever,\nincluding without limitation commercial, advertising or promotional\npurposes (the \"Waiver\"). Affirmer makes the Waiver for the benefit of each\nmember of the public at large and to the detriment of Affirmer's heirs and\nsuccessors, fully intending that such Waiver shall not be subject to\nrevocation, rescission, cancellation, termination, or any other legal or\nequitable action to disrupt the quiet enjoyment of the Work by the public\nas contemplated by Affirmer's express Statement of Purpose.\n\n3. Public License Fallback. Should any part of the Waiver for any reason\nbe judged legally invalid or ineffective under applicable law, then the\nWaiver shall be preserved to the maximum extent permitted taking into\naccount Affirmer's express Statement of Purpose. In addition, to the\nextent the Waiver is so judged Affirmer hereby grants to each affected\nperson a royalty-free, non transferable, non sublicensable, non exclusive,\nirrevocable and unconditional license to exercise Affirmer's Copyright and\nRelated Rights in the Work (i) in all territories worldwide, (ii) for the\nmaximum duration provided by applicable law or treaty (including future\ntime extensions), (iii) in any current or future medium and for any number\nof copies, and (iv) for any purpose whatsoever, including without\nlimitation commercial, advertising or promotional purposes (the\n\"License\"). The License shall be deemed effective as of the date CC0 was\napplied by Affirmer to the Work. Should any part of the License for any\nreason be judged legally invalid or ineffective under applicable law, such\npartial invalidity or ineffectiveness shall not invalidate the remainder\nof the License, and in such case Affirmer hereby affirms that he or she\nwill not (i) exercise any of his or her remaining Copyright and Related\nRights in the Work or (ii) assert any associated claims and causes of\naction with respect to the Work, in either case contrary to Affirmer's\nexpress Statement of Purpose.\n\n4. Limitations and Disclaimers.\n\n a. No trademark or patent rights held by Affirmer are waived, abandoned,\n surrendered, licensed or otherwise affected by this document.\n b. Affirmer offers the Work as-is and makes no representations or\n warranties of any kind concerning the Work, express, implied,\n statutory or otherwise, including without limitation warranties of\n title, merchantability, fitness for a particular purpose, non\n infringement, or the absence of latent or other defects, accuracy, or\n the present or absence of errors, whether or not discoverable, all to\n the greatest extent permissible under applicable law.\n c. Affirmer disclaims responsibility for clearing rights of other persons\n that may apply to the Work or any use thereof, including without\n limitation any person's Copyright and Related Rights in the Work.\n Further, Affirmer disclaims responsibility for obtaining any necessary\n consents, permissions or other rights required for any use of the\n Work.\n d. Affirmer understands and acknowledges that Creative Commons is not a\n party to this document and has no duty or obligation with respect to\n this CC0 or use of the Work.\n"
},
{
+ "name": "gitlab.com/gitlab-org/api/client-go",
+ "path": "gitlab.com/gitlab-org/api/client-go/LICENSE",
+ "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"{}\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright {yyyy} {name of copyright owner}\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"
+ },
+ {
"name": "go.etcd.io/bbolt",
"path": "go.etcd.io/bbolt/LICENSE",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2013 Ben Johnson\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
@@ -1150,6 +1165,11 @@
"licenseText": "Copyright (c) 2016-2017 Uber Technologies, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
},
{
+ "name": "go.uber.org/zap/exp/zapslog",
+ "path": "go.uber.org/zap/exp/zapslog/LICENSE",
+ "licenseText": "Copyright (c) 2016-2024 Uber Technologies, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
+ },
+ {
"name": "golang.org/x/crypto",
"path": "golang.org/x/crypto/LICENSE",
"licenseText": "Copyright 2009 The Go Authors.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google LLC nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
diff --git a/build.go b/build.go
index 234579b514..e81ba54690 100644
--- a/build.go
+++ b/build.go
@@ -5,19 +5,10 @@
package main
-// Libraries that are included to vendor utilities used during build.
+// Libraries that are included to vendor utilities used during Makefile build.
// These libraries will not be included in a normal compilation.
import (
- // for embed
- _ "github.com/shurcooL/vfsgen"
-
- // for cover merge
- _ "golang.org/x/tools/cover"
-
// for vet
_ "code.gitea.io/gitea-vet"
-
- // for swagger
- _ "github.com/go-swagger/go-swagger/cmd/swagger"
)
diff --git a/build/generate-bindata.go b/build/generate-bindata.go
index 2fcb7c2f2a..2553770762 100644
--- a/build/generate-bindata.go
+++ b/build/generate-bindata.go
@@ -6,87 +6,22 @@
package main
import (
- "bytes"
- "crypto/sha1"
"fmt"
- "log"
- "net/http"
"os"
- "path/filepath"
- "strconv"
- "github.com/shurcooL/vfsgen"
+ "code.gitea.io/gitea/modules/assetfs"
)
-func needsUpdate(dir, filename string) (bool, []byte) {
- needRegen := false
- _, err := os.Stat(filename)
- if err != nil {
- needRegen = true
- }
-
- oldHash, err := os.ReadFile(filename + ".hash")
- if err != nil {
- oldHash = []byte{}
- }
-
- hasher := sha1.New()
-
- err = filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
- if err != nil {
- return err
- }
- info, err := d.Info()
- if err != nil {
- return err
- }
- _, _ = hasher.Write([]byte(d.Name()))
- _, _ = hasher.Write([]byte(info.ModTime().String()))
- _, _ = hasher.Write([]byte(strconv.FormatInt(info.Size(), 16)))
- return nil
- })
- if err != nil {
- return true, oldHash
- }
-
- newHash := hasher.Sum([]byte{})
-
- if bytes.Compare(oldHash, newHash) != 0 {
- return true, newHash
- }
-
- return needRegen, newHash
-}
-
func main() {
- if len(os.Args) < 4 {
- log.Fatal("Insufficient number of arguments. Need: directory packageName filename")
- }
-
- dir, packageName, filename := os.Args[1], os.Args[2], os.Args[3]
- var useGlobalModTime bool
- if len(os.Args) == 5 {
- useGlobalModTime, _ = strconv.ParseBool(os.Args[4])
- }
-
- update, newHash := needsUpdate(dir, filename)
-
- if !update {
- fmt.Printf("bindata for %s already up-to-date\n", packageName)
- return
+ if len(os.Args) != 3 {
+ fmt.Println("usage: ./generate-bindata {local-directory} {bindata-filename}")
+ os.Exit(1)
}
- fmt.Printf("generating bindata for %s\n", packageName)
- var fsTemplates http.FileSystem = http.Dir(dir)
- err := vfsgen.Generate(fsTemplates, vfsgen.Options{
- PackageName: packageName,
- BuildTags: "bindata",
- VariableName: "Assets",
- Filename: filename,
- UseGlobalModTime: useGlobalModTime,
- })
- if err != nil {
- log.Fatalf("%v\n", err)
+ dir, filename := os.Args[1], os.Args[2]
+ fmt.Printf("generating bindata for %s to %s\n", dir, filename)
+ if err := assetfs.GenerateEmbedBindata(dir, filename); err != nil {
+ fmt.Printf("failed: %s\n", err.Error())
+ os.Exit(1)
}
- _ = os.WriteFile(filename+".hash", newHash, 0o666)
}
diff --git a/build/generate-licenses.go b/build/generate-licenses.go
deleted file mode 100644
index 66e1d37755..0000000000
--- a/build/generate-licenses.go
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-//go:build ignore
-
-package main
-
-import (
- "archive/tar"
- "compress/gzip"
- "crypto/md5"
- "encoding/hex"
- "flag"
- "fmt"
- "io"
- "log"
- "net/http"
- "os"
- "path"
- "path/filepath"
- "strings"
-
- "code.gitea.io/gitea/build/license"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/util"
-)
-
-func main() {
- var (
- prefix = "gitea-licenses"
- url = "https://api.github.com/repos/spdx/license-list-data/tarball"
- githubApiToken = ""
- githubUsername = ""
- destination = ""
- )
-
- flag.StringVar(&destination, "dest", "options/license/", "destination for the licenses")
- flag.StringVar(&githubUsername, "username", "", "github username")
- flag.StringVar(&githubApiToken, "token", "", "github api token")
- flag.Parse()
-
- file, err := os.CreateTemp(os.TempDir(), prefix)
- if err != nil {
- log.Fatalf("Failed to create temp file. %s", err)
- }
-
- defer util.Remove(file.Name())
-
- if err := os.RemoveAll(destination); err != nil {
- log.Fatalf("Cannot clean destination folder: %v", err)
- }
-
- if err := os.MkdirAll(destination, 0o755); err != nil {
- log.Fatalf("Cannot create destination: %v", err)
- }
-
- req, err := http.NewRequest("GET", url, nil)
- if err != nil {
- log.Fatalf("Failed to download archive. %s", err)
- }
-
- if len(githubApiToken) > 0 && len(githubUsername) > 0 {
- req.SetBasicAuth(githubUsername, githubApiToken)
- }
-
- resp, err := http.DefaultClient.Do(req)
- if err != nil {
- log.Fatalf("Failed to download archive. %s", err)
- }
-
- defer resp.Body.Close()
-
- if _, err := io.Copy(file, resp.Body); err != nil {
- log.Fatalf("Failed to copy archive to file. %s", err)
- }
-
- if _, err := file.Seek(0, 0); err != nil {
- log.Fatalf("Failed to reset seek on archive. %s", err)
- }
-
- gz, err := gzip.NewReader(file)
- if err != nil {
- log.Fatalf("Failed to gunzip the archive. %s", err)
- }
-
- tr := tar.NewReader(gz)
- aliasesFiles := make(map[string][]string)
- for {
- hdr, err := tr.Next()
-
- if err == io.EOF {
- break
- }
-
- if err != nil {
- log.Fatalf("Failed to iterate archive. %s", err)
- }
-
- if !strings.Contains(hdr.Name, "/text/") {
- continue
- }
-
- if filepath.Ext(hdr.Name) != ".txt" {
- continue
- }
-
- fileBaseName := filepath.Base(hdr.Name)
- licenseName := strings.TrimSuffix(fileBaseName, ".txt")
-
- if strings.HasPrefix(fileBaseName, "README") {
- continue
- }
-
- if strings.HasPrefix(fileBaseName, "deprecated_") {
- continue
- }
- out, err := os.Create(path.Join(destination, licenseName))
- if err != nil {
- log.Fatalf("Failed to create new file. %s", err)
- }
-
- defer out.Close()
-
- // some license files have same content, so we need to detect these files and create a convert map into a json file
- // Later we use this convert map to avoid adding same license content with different license name
- h := md5.New()
- // calculate md5 and write file in the same time
- r := io.TeeReader(tr, h)
- if _, err := io.Copy(out, r); err != nil {
- log.Fatalf("Failed to write new file. %s", err)
- } else {
- fmt.Printf("Written %s\n", out.Name())
-
- md5 := hex.EncodeToString(h.Sum(nil))
- aliasesFiles[md5] = append(aliasesFiles[md5], licenseName)
- }
- }
-
- // generate convert license name map
- licenseAliases := make(map[string]string)
- for _, fileNames := range aliasesFiles {
- if len(fileNames) > 1 {
- licenseName := license.GetLicenseNameFromAliases(fileNames)
- if licenseName == "" {
- // license name should not be empty as expected
- // if it is empty, we need to rewrite the logic of GetLicenseNameFromAliases
- log.Fatalf("GetLicenseNameFromAliases: license name is empty")
- }
- for _, fileName := range fileNames {
- licenseAliases[fileName] = licenseName
- }
- }
- }
- // save convert license name map to file
- b, err := json.Marshal(licenseAliases)
- if err != nil {
- log.Fatalf("Failed to create json bytes. %s", err)
- }
-
- licenseAliasesDestination := filepath.Join(destination, "etc", "license-aliases.json")
- if err := os.MkdirAll(filepath.Dir(licenseAliasesDestination), 0o755); err != nil {
- log.Fatalf("Failed to create directory for license aliases json file. %s", err)
- }
-
- f, err := os.Create(licenseAliasesDestination)
- if err != nil {
- log.Fatalf("Failed to create license aliases json file. %s", err)
- }
- defer f.Close()
-
- if _, err = f.Write(b); err != nil {
- log.Fatalf("Failed to write license aliases json file. %s", err)
- }
-
- fmt.Println("Done")
-}
diff --git a/build/license/aliasgenerator.go b/build/license/aliasgenerator.go
deleted file mode 100644
index 7de1e6fbd6..0000000000
--- a/build/license/aliasgenerator.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2024 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package license
-
-import "strings"
-
-func GetLicenseNameFromAliases(fnl []string) string {
- if len(fnl) == 0 {
- return ""
- }
-
- shortestItem := func(list []string) string {
- s := list[0]
- for _, l := range list[1:] {
- if len(l) < len(s) {
- s = l
- }
- }
- return s
- }
- allHasPrefix := func(list []string, s string) bool {
- for _, l := range list {
- if !strings.HasPrefix(l, s) {
- return false
- }
- }
- return true
- }
-
- sl := shortestItem(fnl)
- slv := strings.Split(sl, "-")
- var result string
- for i := len(slv); i >= 0; i-- {
- result = strings.Join(slv[:i], "-")
- if allHasPrefix(fnl, result) {
- return result
- }
- }
- return ""
-}
diff --git a/build/license/aliasgenerator_test.go b/build/license/aliasgenerator_test.go
deleted file mode 100644
index 239181b736..0000000000
--- a/build/license/aliasgenerator_test.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2024 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package license
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestGetLicenseNameFromAliases(t *testing.T) {
- tests := []struct {
- target string
- inputs []string
- }{
- {
- // real case which you can find in license-aliases.json
- target: "AGPL-1.0",
- inputs: []string{
- "AGPL-1.0-only",
- "AGPL-1.0-or-late",
- },
- },
- {
- target: "",
- inputs: []string{
- "APSL-1.0",
- "AGPL-1.0-only",
- "AGPL-1.0-or-late",
- },
- },
- }
-
- for _, tt := range tests {
- result := GetLicenseNameFromAliases(tt.inputs)
- assert.Equal(t, result, tt.target)
- }
-}
diff --git a/cmd/actions.go b/cmd/actions.go
index f582c16c81..2c51c6a1bc 100644
--- a/cmd/actions.go
+++ b/cmd/actions.go
@@ -4,12 +4,13 @@
package cmd
import (
+ "context"
"fmt"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var (
@@ -17,7 +18,7 @@ var (
CmdActions = &cli.Command{
Name: "actions",
Usage: "Manage Gitea Actions",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
subcmdActionsGenRunnerToken,
},
}
@@ -38,10 +39,7 @@ var (
}
)
-func runGenerateActionsRunnerToken(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runGenerateActionsRunnerToken(ctx context.Context, c *cli.Command) error {
setting.MustInstalled()
scope := c.String("scope")
diff --git a/cmd/admin.go b/cmd/admin.go
index 6c9480e76e..559544edd3 100644
--- a/cmd/admin.go
+++ b/cmd/admin.go
@@ -15,7 +15,7 @@ import (
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var (
@@ -23,7 +23,7 @@ var (
CmdAdmin = &cli.Command{
Name: "admin",
Usage: "Perform common administrative operations",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
subcmdUser,
subcmdRepoSyncReleases,
subcmdRegenerate,
@@ -41,7 +41,7 @@ var (
subcmdRegenerate = &cli.Command{
Name: "regenerate",
Usage: "Regenerate specific files",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
microcmdRegenHooks,
microcmdRegenKeys,
},
@@ -50,15 +50,15 @@ var (
subcmdAuth = &cli.Command{
Name: "auth",
Usage: "Modify external auth providers",
- Subcommands: []*cli.Command{
- microcmdAuthAddOauth,
- microcmdAuthUpdateOauth,
- microcmdAuthAddLdapBindDn,
- microcmdAuthUpdateLdapBindDn,
- microcmdAuthAddLdapSimpleAuth,
- microcmdAuthUpdateLdapSimpleAuth,
- microcmdAuthAddSMTP,
- microcmdAuthUpdateSMTP,
+ Commands: []*cli.Command{
+ microcmdAuthAddOauth(),
+ microcmdAuthUpdateOauth(),
+ microcmdAuthAddLdapBindDn(),
+ microcmdAuthUpdateLdapBindDn(),
+ microcmdAuthAddLdapSimpleAuth(),
+ microcmdAuthUpdateLdapSimpleAuth(),
+ microcmdAuthAddSMTP(),
+ microcmdAuthUpdateSMTP(),
microcmdAuthList,
microcmdAuthDelete,
},
@@ -70,9 +70,9 @@ var (
Action: runSendMail,
Flags: []cli.Flag{
&cli.StringFlag{
- Name: "title",
- Usage: `a title of a message`,
- Value: "",
+ Name: "title",
+ Usage: "a title of a message",
+ Required: true,
},
&cli.StringFlag{
Name: "content",
@@ -86,17 +86,16 @@ var (
},
},
}
+)
- idFlag = &cli.Int64Flag{
+func idFlag() *cli.Int64Flag {
+ return &cli.Int64Flag{
Name: "id",
Usage: "ID of authentication source",
}
-)
-
-func runRepoSyncReleases(_ *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
+}
+func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error {
if err := initDB(ctx); err != nil {
return err
}
@@ -107,7 +106,7 @@ func runRepoSyncReleases(_ *cli.Context) error {
log.Trace("Synchronizing repository releases (this may take a while)")
for page := 1; ; page++ {
- repos, count, err := repo_model.SearchRepositoryByName(ctx, &repo_model.SearchRepoOptions{
+ repos, count, err := repo_model.SearchRepositoryByName(ctx, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: repo_model.RepositoryListDefaultPageSize,
Page: page,
diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go
index 4777a92908..1a09366722 100644
--- a/cmd/admin_auth.go
+++ b/cmd/admin_auth.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"errors"
"fmt"
"os"
@@ -13,14 +14,14 @@ import (
"code.gitea.io/gitea/models/db"
auth_service "code.gitea.io/gitea/services/auth"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var (
microcmdAuthDelete = &cli.Command{
Name: "delete",
Usage: "Delete specific auth source",
- Flags: []cli.Flag{idFlag},
+ Flags: []cli.Flag{idFlag()},
Action: runDeleteAuth,
}
microcmdAuthList = &cli.Command{
@@ -56,10 +57,7 @@ var (
}
)
-func runListAuth(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runListAuth(ctx context.Context, c *cli.Command) error {
if err := initDB(ctx); err != nil {
return err
}
@@ -90,14 +88,11 @@ func runListAuth(c *cli.Context) error {
return nil
}
-func runDeleteAuth(c *cli.Context) error {
+func runDeleteAuth(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
- ctx, cancel := installSignals()
- defer cancel()
-
if err := initDB(ctx); err != nil {
return err
}
diff --git a/cmd/admin_auth_ldap.go b/cmd/admin_auth_ldap.go
index aff2a12855..069ad6600c 100644
--- a/cmd/admin_auth_ldap.go
+++ b/cmd/admin_auth_ldap.go
@@ -9,9 +9,10 @@ import (
"strings"
"code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/auth/source/ldap"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
type (
@@ -23,8 +24,8 @@ type (
}
)
-var (
- commonLdapCLIFlags = []cli.Flag{
+func commonLdapCLIFlags() []cli.Flag {
+ return []cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: "Authentication name.",
@@ -102,8 +103,10 @@ var (
Usage: "The attribute of the user’s LDAP record containing the user’s avatar.",
},
}
+}
- ldapBindDnCLIFlags = append(commonLdapCLIFlags,
+func ldapBindDnCLIFlags() []cli.Flag {
+ return append(commonLdapCLIFlags(),
&cli.StringFlag{
Name: "bind-dn",
Usage: "The DN to bind to the LDAP server with when searching for the user.",
@@ -127,50 +130,88 @@ var (
&cli.UintFlag{
Name: "page-size",
Usage: "Search page size.",
+ },
+ &cli.BoolFlag{
+ Name: "enable-groups",
+ Usage: "Enable LDAP groups",
+ },
+ &cli.StringFlag{
+ Name: "group-search-base-dn",
+ Usage: "The LDAP base DN at which group accounts will be searched for",
+ },
+ &cli.StringFlag{
+ Name: "group-member-attribute",
+ Usage: "Group attribute containing list of users",
+ },
+ &cli.StringFlag{
+ Name: "group-user-attribute",
+ Usage: "User attribute listed in group",
+ },
+ &cli.StringFlag{
+ Name: "group-filter",
+ Usage: "Verify group membership in LDAP",
+ },
+ &cli.StringFlag{
+ Name: "group-team-map",
+ Usage: "Map LDAP groups to Organization teams",
+ },
+ &cli.BoolFlag{
+ Name: "group-team-map-removal",
+ Usage: "Remove users from synchronized teams if user does not belong to corresponding LDAP group",
})
+}
- ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags,
+func ldapSimpleAuthCLIFlags() []cli.Flag {
+ return append(commonLdapCLIFlags(),
&cli.StringFlag{
Name: "user-dn",
Usage: "The user's DN.",
})
+}
- microcmdAuthAddLdapBindDn = &cli.Command{
+func microcmdAuthAddLdapBindDn() *cli.Command {
+ return &cli.Command{
Name: "add-ldap",
Usage: "Add new LDAP (via Bind DN) authentication source",
- Action: func(c *cli.Context) error {
- return newAuthService().addLdapBindDn(c)
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().addLdapBindDn(ctx, cmd)
},
- Flags: ldapBindDnCLIFlags,
+ Flags: ldapBindDnCLIFlags(),
}
+}
- microcmdAuthUpdateLdapBindDn = &cli.Command{
+func microcmdAuthUpdateLdapBindDn() *cli.Command {
+ return &cli.Command{
Name: "update-ldap",
Usage: "Update existing LDAP (via Bind DN) authentication source",
- Action: func(c *cli.Context) error {
- return newAuthService().updateLdapBindDn(c)
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().updateLdapBindDn(ctx, cmd)
},
- Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...),
+ Flags: append([]cli.Flag{idFlag()}, ldapBindDnCLIFlags()...),
}
+}
- microcmdAuthAddLdapSimpleAuth = &cli.Command{
+func microcmdAuthAddLdapSimpleAuth() *cli.Command {
+ return &cli.Command{
Name: "add-ldap-simple",
Usage: "Add new LDAP (simple auth) authentication source",
- Action: func(c *cli.Context) error {
- return newAuthService().addLdapSimpleAuth(c)
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().addLdapSimpleAuth(ctx, cmd)
},
- Flags: ldapSimpleAuthCLIFlags,
+ Flags: ldapSimpleAuthCLIFlags(),
}
+}
- microcmdAuthUpdateLdapSimpleAuth = &cli.Command{
+func microcmdAuthUpdateLdapSimpleAuth() *cli.Command {
+ return &cli.Command{
Name: "update-ldap-simple",
Usage: "Update existing LDAP (simple auth) authentication source",
- Action: func(c *cli.Context) error {
- return newAuthService().updateLdapSimpleAuth(c)
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().updateLdapSimpleAuth(ctx, cmd)
},
- Flags: append([]cli.Flag{idFlag}, ldapSimpleAuthCLIFlags...),
+ Flags: append([]cli.Flag{idFlag()}, ldapSimpleAuthCLIFlags()...),
}
-)
+}
// newAuthService creates a service with default functions.
func newAuthService() *authService {
@@ -182,8 +223,8 @@ func newAuthService() *authService {
}
}
-// parseAuthSource assigns values on authSource according to command line flags.
-func parseAuthSource(c *cli.Context, authSource *auth.Source) {
+// parseAuthSourceLdap assigns values on authSource according to command line flags.
+func parseAuthSourceLdap(c *cli.Command, authSource *auth.Source) {
if c.IsSet("name") {
authSource.Name = c.String("name")
}
@@ -199,10 +240,11 @@ func parseAuthSource(c *cli.Context, authSource *auth.Source) {
if c.IsSet("disable-synchronize-users") {
authSource.IsSyncEnabled = !c.Bool("disable-synchronize-users")
}
+ authSource.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
}
// parseLdapConfig assigns values on config according to command line flags.
-func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
+func parseLdapConfig(c *cli.Command, config *ldap.Source) error {
if c.IsSet("name") {
config.Name = c.String("name")
}
@@ -215,7 +257,7 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
if c.IsSet("security-protocol") {
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol"))
if !ok {
- return fmt.Errorf("Unknown security protocol name: %s", c.String("security-protocol"))
+ return fmt.Errorf("unknown security protocol name: %s", c.String("security-protocol"))
}
config.SecurityProtocol = p
}
@@ -270,8 +312,26 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
if c.IsSet("allow-deactivate-all") {
config.AllowDeactivateAll = c.Bool("allow-deactivate-all")
}
- if c.IsSet("skip-local-2fa") {
- config.SkipLocalTwoFA = c.Bool("skip-local-2fa")
+ if c.IsSet("enable-groups") {
+ config.GroupsEnabled = c.Bool("enable-groups")
+ }
+ if c.IsSet("group-search-base-dn") {
+ config.GroupDN = c.String("group-search-base-dn")
+ }
+ if c.IsSet("group-member-attribute") {
+ config.GroupMemberUID = c.String("group-member-attribute")
+ }
+ if c.IsSet("group-user-attribute") {
+ config.UserUID = c.String("group-user-attribute")
+ }
+ if c.IsSet("group-filter") {
+ config.GroupFilter = c.String("group-filter")
+ }
+ if c.IsSet("group-team-map") {
+ config.GroupTeamMap = c.String("group-team-map")
+ }
+ if c.IsSet("group-team-map-removal") {
+ config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
}
return nil
}
@@ -289,32 +349,27 @@ func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
// getAuthSource gets the login source by its id defined in the command line flags.
// It returns an error if the id is not set, does not match any source or if the source is not of expected type.
-func (a *authService) getAuthSource(ctx context.Context, c *cli.Context, authType auth.Type) (*auth.Source, error) {
+func (a *authService) getAuthSource(ctx context.Context, c *cli.Command, authType auth.Type) (*auth.Source, error) {
if err := argsSet(c, "id"); err != nil {
return nil, err
}
-
authSource, err := a.getAuthSourceByID(ctx, c.Int64("id"))
if err != nil {
return nil, err
}
if authSource.Type != authType {
- return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", authType.String(), authSource.Type.String())
+ return nil, fmt.Errorf("invalid authentication type. expected: %s, actual: %s", authType.String(), authSource.Type.String())
}
return authSource, nil
}
// addLdapBindDn adds a new LDAP via Bind DN authentication source.
-func (a *authService) addLdapBindDn(c *cli.Context) error {
+func (a *authService) addLdapBindDn(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil {
return err
}
-
- ctx, cancel := installSignals()
- defer cancel()
-
if err := a.initDB(ctx); err != nil {
return err
}
@@ -327,7 +382,7 @@ func (a *authService) addLdapBindDn(c *cli.Context) error {
},
}
- parseAuthSource(c, authSource)
+ parseAuthSourceLdap(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err
}
@@ -336,10 +391,7 @@ func (a *authService) addLdapBindDn(c *cli.Context) error {
}
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
-func (a *authService) updateLdapBindDn(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) error {
if err := a.initDB(ctx); err != nil {
return err
}
@@ -349,7 +401,7 @@ func (a *authService) updateLdapBindDn(c *cli.Context) error {
return err
}
- parseAuthSource(c, authSource)
+ parseAuthSourceLdap(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err
}
@@ -358,14 +410,11 @@ func (a *authService) updateLdapBindDn(c *cli.Context) error {
}
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
-func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
+func (a *authService) addLdapSimpleAuth(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil {
return err
}
- ctx, cancel := installSignals()
- defer cancel()
-
if err := a.initDB(ctx); err != nil {
return err
}
@@ -378,7 +427,7 @@ func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
},
}
- parseAuthSource(c, authSource)
+ parseAuthSourceLdap(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err
}
@@ -387,10 +436,7 @@ func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
}
// updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source.
-func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func (a *authService) updateLdapSimpleAuth(ctx context.Context, c *cli.Command) error {
if err := a.initDB(ctx); err != nil {
return err
}
@@ -400,7 +446,7 @@ func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
return err
}
- parseAuthSource(c, authSource)
+ parseAuthSourceLdap(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err
}
diff --git a/cmd/admin_auth_ldap_test.go b/cmd/admin_auth_ldap_test.go
index 7791f3a9cc..2da7ebc573 100644
--- a/cmd/admin_auth_ldap_test.go
+++ b/cmd/admin_auth_ldap_test.go
@@ -8,17 +8,16 @@ import (
"testing"
"code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/services/auth/source/ldap"
"github.com/stretchr/testify/assert"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
func TestAddLdapBindDn(t *testing.T) {
// Mock cli functions to do not exit on error
- osExiter := cli.OsExiter
- defer func() { cli.OsExiter = osExiter }()
- cli.OsExiter = func(code int) {}
+ defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
@@ -51,6 +50,13 @@ func TestAddLdapBindDn(t *testing.T) {
"--attributes-in-bind",
"--synchronize-users",
"--page-size", "99",
+ "--enable-groups",
+ "--group-search-base-dn", "ou=group,dc=full-domain-bind,dc=org",
+ "--group-member-attribute", "memberUid",
+ "--group-user-attribute", "uid",
+ "--group-filter", "(|(cn=gitea_users)(cn=admins))",
+ "--group-team-map", `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
+ "--group-team-map-removal",
},
source: &auth.Source{
Type: auth.LDAP,
@@ -78,6 +84,13 @@ func TestAddLdapBindDn(t *testing.T) {
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)",
Enabled: true,
+ GroupsEnabled: true,
+ GroupDN: "ou=group,dc=full-domain-bind,dc=org",
+ GroupMemberUID: "memberUid",
+ UserUID: "uid",
+ GroupFilter: "(|(cn=gitea_users)(cn=admins))",
+ GroupTeamMap: `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
+ GroupTeamMapRemoval: true,
},
},
},
@@ -121,7 +134,7 @@ func TestAddLdapBindDn(t *testing.T) {
"--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
"--email-attribute", "mail",
},
- errMsg: "Unknown security protocol name: zzzzz",
+ errMsg: "unknown security protocol name: zzzzz",
},
// case 3
{
@@ -215,22 +228,23 @@ func TestAddLdapBindDn(t *testing.T) {
return nil
},
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
- assert.FailNow(t, "case %d: should not call updateAuthSource", n)
+ assert.FailNow(t, "updateAuthSource called", "case %d: should not call updateAuthSource", n)
return nil
},
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
- assert.FailNow(t, "case %d: should not call getAuthSourceByID", n)
+ assert.FailNow(t, "getAuthSourceByID called", "case %d: should not call getAuthSourceByID", n)
return nil, nil
},
}
// Create a copy of command to test
- app := cli.NewApp()
- app.Flags = microcmdAuthAddLdapBindDn.Flags
- app.Action = service.addLdapBindDn
+ app := cli.Command{
+ Flags: microcmdAuthAddLdapBindDn().Flags,
+ Action: service.addLdapBindDn,
+ }
// Run it
- err := app.Run(c.args)
+ err := app.Run(t.Context(), c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
@@ -242,9 +256,7 @@ func TestAddLdapBindDn(t *testing.T) {
func TestAddLdapSimpleAuth(t *testing.T) {
// Mock cli functions to do not exit on error
- osExiter := cli.OsExiter
- defer func() { cli.OsExiter = osExiter }()
- cli.OsExiter = func(code int) {}
+ defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
@@ -334,12 +346,12 @@ func TestAddLdapSimpleAuth(t *testing.T) {
"--name", "ldap (simple auth) source",
"--security-protocol", "zzzzz",
"--host", "ldap-server",
- "--port", "123",
+ "--port", "1234",
"--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
"--email-attribute", "mail",
"--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
},
- errMsg: "Unknown security protocol name: zzzzz",
+ errMsg: "unknown security protocol name: zzzzz",
},
// case 3
{
@@ -446,22 +458,23 @@ func TestAddLdapSimpleAuth(t *testing.T) {
return nil
},
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
- assert.FailNow(t, "case %d: should not call updateAuthSource", n)
+ assert.FailNow(t, "updateAuthSource called", "case %d: should not call updateAuthSource", n)
return nil
},
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
- assert.FailNow(t, "case %d: should not call getAuthSourceByID", n)
+ assert.FailNow(t, "getAuthSourceById called", "case %d: should not call getAuthSourceByID", n)
return nil, nil
},
}
// Create a copy of command to test
- app := cli.NewApp()
- app.Flags = microcmdAuthAddLdapSimpleAuth.Flags
- app.Action = service.addLdapSimpleAuth
+ app := &cli.Command{
+ Flags: microcmdAuthAddLdapSimpleAuth().Flags,
+ Action: service.addLdapSimpleAuth,
+ }
// Run it
- err := app.Run(c.args)
+ err := app.Run(t.Context(), c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
@@ -473,9 +486,7 @@ func TestAddLdapSimpleAuth(t *testing.T) {
func TestUpdateLdapBindDn(t *testing.T) {
// Mock cli functions to do not exit on error
- osExiter := cli.OsExiter
- defer func() { cli.OsExiter = osExiter }()
- cli.OsExiter = func(code int) {}
+ defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
@@ -510,6 +521,13 @@ func TestUpdateLdapBindDn(t *testing.T) {
"--bind-password", "secret-bind-full",
"--synchronize-users",
"--page-size", "99",
+ "--enable-groups",
+ "--group-search-base-dn", "ou=group,dc=full-domain-bind,dc=org",
+ "--group-member-attribute", "memberUid",
+ "--group-user-attribute", "uid",
+ "--group-filter", "(|(cn=gitea_users)(cn=admins))",
+ "--group-team-map", `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
+ "--group-team-map-removal",
},
id: 23,
existingAuthSource: &auth.Source{
@@ -545,6 +563,13 @@ func TestUpdateLdapBindDn(t *testing.T) {
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)",
Enabled: true,
+ GroupsEnabled: true,
+ GroupDN: "ou=group,dc=full-domain-bind,dc=org",
+ GroupMemberUID: "memberUid",
+ UserUID: "uid",
+ GroupFilter: "(|(cn=gitea_users)(cn=admins))",
+ GroupTeamMap: `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
+ GroupTeamMapRemoval: true,
},
},
},
@@ -836,7 +861,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
"--id", "1",
"--security-protocol", "xxxxx",
},
- errMsg: "Unknown security protocol name: xxxxx",
+ errMsg: "unknown security protocol name: xxxxx",
},
// case 22
{
@@ -855,7 +880,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
Type: auth.OAuth2,
Cfg: &ldap.Source{},
},
- errMsg: "Invalid authentication type. expected: LDAP (via BindDN), actual: OAuth2",
+ errMsg: "invalid authentication type. expected: LDAP (via BindDN), actual: OAuth2",
},
// case 24
{
@@ -897,7 +922,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
return nil
},
createAuthSource: func(ctx context.Context, authSource *auth.Source) error {
- assert.FailNow(t, "case %d: should not call createAuthSource", n)
+ assert.FailNow(t, "createAuthSource called", "case %d: should not call createAuthSource", n)
return nil
},
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
@@ -919,12 +944,12 @@ func TestUpdateLdapBindDn(t *testing.T) {
}
// Create a copy of command to test
- app := cli.NewApp()
- app.Flags = microcmdAuthUpdateLdapBindDn.Flags
- app.Action = service.updateLdapBindDn
-
+ app := cli.Command{
+ Flags: microcmdAuthUpdateLdapBindDn().Flags,
+ Action: service.updateLdapBindDn,
+ }
// Run it
- err := app.Run(c.args)
+ err := app.Run(t.Context(), c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
@@ -936,9 +961,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
func TestUpdateLdapSimpleAuth(t *testing.T) {
// Mock cli functions to do not exit on error
- osExiter := cli.OsExiter
- defer func() { cli.OsExiter = osExiter }()
- cli.OsExiter = func(code int) {}
+ defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
@@ -1229,7 +1252,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
"--id", "1",
"--security-protocol", "xxxxx",
},
- errMsg: "Unknown security protocol name: xxxxx",
+ errMsg: "unknown security protocol name: xxxxx",
},
// case 18
{
@@ -1248,7 +1271,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
Type: auth.PAM,
Cfg: &ldap.Source{},
},
- errMsg: "Invalid authentication type. expected: LDAP (simple auth), actual: PAM",
+ errMsg: "invalid authentication type. expected: LDAP (simple auth), actual: PAM",
},
// case 20
{
@@ -1287,7 +1310,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
return nil
},
createAuthSource: func(ctx context.Context, authSource *auth.Source) error {
- assert.FailNow(t, "case %d: should not call createAuthSource", n)
+ assert.FailNow(t, "createAuthSource called", "case %d: should not call createAuthSource", n)
return nil
},
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
@@ -1309,12 +1332,12 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
}
// Create a copy of command to test
- app := cli.NewApp()
- app.Flags = microcmdAuthUpdateLdapSimpleAuth.Flags
- app.Action = service.updateLdapSimpleAuth
-
+ app := cli.Command{
+ Flags: microcmdAuthUpdateLdapSimpleAuth().Flags,
+ Action: service.updateLdapSimpleAuth,
+ }
// Run it
- err := app.Run(c.args)
+ err := app.Run(t.Context(), c.args)
if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else {
diff --git a/cmd/admin_auth_oauth.go b/cmd/admin_auth_oauth.go
index 8e6239ac33..d1aa753500 100644
--- a/cmd/admin_auth_oauth.go
+++ b/cmd/admin_auth_oauth.go
@@ -4,18 +4,20 @@
package cmd
import (
+ "context"
"errors"
"fmt"
"net/url"
auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/auth/source/oauth2"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-var (
- oauthCLIFlags = []cli.Flag{
+func oauthCLIFlags() []cli.Flag {
+ return []cli.Flag{
&cli.StringFlag{
Name: "name",
Value: "",
@@ -120,23 +122,34 @@ var (
Usage: "Activate automatic team membership removal depending on groups",
},
}
+}
- microcmdAuthAddOauth = &cli.Command{
- Name: "add-oauth",
- Usage: "Add new Oauth authentication source",
- Action: runAddOauth,
- Flags: oauthCLIFlags,
+func microcmdAuthAddOauth() *cli.Command {
+ return &cli.Command{
+ Name: "add-oauth",
+ Usage: "Add new Oauth authentication source",
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().runAddOauth(ctx, cmd)
+ },
+ Flags: oauthCLIFlags(),
}
+}
- microcmdAuthUpdateOauth = &cli.Command{
- Name: "update-oauth",
- Usage: "Update existing Oauth authentication source",
- Action: runUpdateOauth,
- Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
+func microcmdAuthUpdateOauth() *cli.Command {
+ return &cli.Command{
+ Name: "update-oauth",
+ Usage: "Update existing Oauth authentication source",
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().runUpdateOauth(ctx, cmd)
+ },
+ Flags: append(oauthCLIFlags()[:1], append([]cli.Flag{&cli.Int64Flag{
+ Name: "id",
+ Usage: "ID of authentication source",
+ }}, oauthCLIFlags()[1:]...)...),
}
-)
+}
-func parseOAuth2Config(c *cli.Context) *oauth2.Source {
+func parseOAuth2Config(c *cli.Command) *oauth2.Source {
var customURLMapping *oauth2.CustomURLMapping
if c.IsSet("use-custom-urls") {
customURLMapping = &oauth2.CustomURLMapping{
@@ -156,7 +169,6 @@ func parseOAuth2Config(c *cli.Context) *oauth2.Source {
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
CustomURLMapping: customURLMapping,
IconURL: c.String("icon-url"),
- SkipLocalTwoFA: c.Bool("skip-local-2fa"),
Scopes: c.StringSlice("scopes"),
RequiredClaimName: c.String("required-claim-name"),
RequiredClaimValue: c.String("required-claim-value"),
@@ -168,11 +180,8 @@ func parseOAuth2Config(c *cli.Context) *oauth2.Source {
}
}
-func runAddOauth(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(ctx); err != nil {
+func (a *authService) runAddOauth(ctx context.Context, c *cli.Command) error {
+ if err := a.initDB(ctx); err != nil {
return err
}
@@ -184,27 +193,25 @@ func runAddOauth(c *cli.Context) error {
}
}
- return auth_model.CreateSource(ctx, &auth_model.Source{
- Type: auth_model.OAuth2,
- Name: c.String("name"),
- IsActive: true,
- Cfg: config,
+ return a.createAuthSource(ctx, &auth_model.Source{
+ Type: auth_model.OAuth2,
+ Name: c.String("name"),
+ IsActive: true,
+ Cfg: config,
+ TwoFactorPolicy: util.Iif(c.Bool("skip-local-2fa"), "skip", ""),
})
}
-func runUpdateOauth(c *cli.Context) error {
+func (a *authService) runUpdateOauth(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
- ctx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(ctx); err != nil {
+ if err := a.initDB(ctx); err != nil {
return err
}
- source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
+ source, err := a.getAuthSourceByID(ctx, c.Int64("id"))
if err != nil {
return err
}
@@ -294,6 +301,6 @@ func runUpdateOauth(c *cli.Context) error {
oAuth2Config.CustomURLMapping = customURLMapping
source.Cfg = oAuth2Config
-
- return auth_model.UpdateSource(ctx, source)
+ source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
+ return a.updateAuthSource(ctx, source)
}
diff --git a/cmd/admin_auth_oauth_test.go b/cmd/admin_auth_oauth_test.go
new file mode 100644
index 0000000000..df1bd9c1a6
--- /dev/null
+++ b/cmd/admin_auth_oauth_test.go
@@ -0,0 +1,333 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "context"
+ "testing"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/services/auth/source/oauth2"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/urfave/cli/v3"
+)
+
+func TestAddOauth(t *testing.T) {
+ testCases := []struct {
+ name string
+ args []string
+ source *auth_model.Source
+ errMsg string
+ }{
+ {
+ name: "valid config",
+ args: []string{
+ "--name", "test",
+ "--provider", "github",
+ "--key", "some_key",
+ "--secret", "some_secret",
+ },
+ source: &auth_model.Source{
+ Type: auth_model.OAuth2,
+ Name: "test",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ Scopes: []string{},
+ Provider: "github",
+ ClientID: "some_key",
+ ClientSecret: "some_secret",
+ },
+ TwoFactorPolicy: "",
+ },
+ },
+ {
+ name: "valid config with openid connect",
+ args: []string{
+ "--name", "test",
+ "--provider", "openidConnect",
+ "--key", "some_key",
+ "--secret", "some_secret",
+ "--auto-discover-url", "https://example.com",
+ },
+ source: &auth_model.Source{
+ Type: auth_model.OAuth2,
+ Name: "test",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ Scopes: []string{},
+ Provider: "openidConnect",
+ ClientID: "some_key",
+ ClientSecret: "some_secret",
+ OpenIDConnectAutoDiscoveryURL: "https://example.com",
+ },
+ TwoFactorPolicy: "",
+ },
+ },
+ {
+ name: "valid config with options",
+ args: []string{
+ "--name", "test",
+ "--provider", "gitlab",
+ "--key", "some_key",
+ "--secret", "some_secret",
+ "--use-custom-urls", "true",
+ "--custom-token-url", "https://example.com/token",
+ "--custom-auth-url", "https://example.com/auth",
+ "--custom-profile-url", "https://example.com/profile",
+ "--custom-email-url", "https://example.com/email",
+ "--custom-tenant-id", "some_tenant",
+ "--icon-url", "https://example.com/icon",
+ "--scopes", "scope1,scope2",
+ "--skip-local-2fa", "true",
+ "--required-claim-name", "claim_name",
+ "--required-claim-value", "claim_value",
+ "--group-claim-name", "group_name",
+ "--admin-group", "admin",
+ "--restricted-group", "restricted",
+ "--group-team-map", `{"group1": [1,2]}`,
+ "--group-team-map-removal=true",
+ },
+ source: &auth_model.Source{
+ Type: auth_model.OAuth2,
+ Name: "test",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ Provider: "gitlab",
+ ClientID: "some_key",
+ ClientSecret: "some_secret",
+ CustomURLMapping: &oauth2.CustomURLMapping{
+ TokenURL: "https://example.com/token",
+ AuthURL: "https://example.com/auth",
+ ProfileURL: "https://example.com/profile",
+ EmailURL: "https://example.com/email",
+ Tenant: "some_tenant",
+ },
+ IconURL: "https://example.com/icon",
+ Scopes: []string{"scope1", "scope2"},
+ RequiredClaimName: "claim_name",
+ RequiredClaimValue: "claim_value",
+ GroupClaimName: "group_name",
+ AdminGroup: "admin",
+ RestrictedGroup: "restricted",
+ GroupTeamMap: `{"group1": [1,2]}`,
+ GroupTeamMapRemoval: true,
+ },
+ TwoFactorPolicy: "skip",
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ var createdSource *auth_model.Source
+ a := &authService{
+ initDB: func(ctx context.Context) error {
+ return nil
+ },
+ createAuthSource: func(ctx context.Context, source *auth_model.Source) error {
+ createdSource = source
+ return nil
+ },
+ }
+
+ app := &cli.Command{
+ Flags: microcmdAuthAddOauth().Flags,
+ Action: a.runAddOauth,
+ }
+
+ args := []string{"oauth-test"}
+ args = append(args, tc.args...)
+
+ err := app.Run(t.Context(), args)
+
+ if tc.errMsg != "" {
+ assert.EqualError(t, err, tc.errMsg)
+ } else {
+ assert.NoError(t, err)
+ assert.Equal(t, tc.source, createdSource)
+ }
+ })
+ }
+}
+
+func TestUpdateOauth(t *testing.T) {
+ testCases := []struct {
+ name string
+ args []string
+ id int64
+ existingAuthSource *auth_model.Source
+ authSource *auth_model.Source
+ errMsg string
+ }{
+ {
+ name: "missing id",
+ args: []string{
+ "--name", "test",
+ },
+ errMsg: "--id flag is missing",
+ },
+ {
+ name: "valid config",
+ id: 1,
+ existingAuthSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.OAuth2,
+ Name: "old name",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ Provider: "github",
+ ClientID: "old_key",
+ ClientSecret: "old_secret",
+ },
+ TwoFactorPolicy: "",
+ },
+ args: []string{
+ "--id", "1",
+ "--name", "test",
+ "--provider", "gitlab",
+ "--key", "new_key",
+ "--secret", "new_secret",
+ },
+ authSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.OAuth2,
+ Name: "test",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ Provider: "gitlab",
+ ClientID: "new_key",
+ ClientSecret: "new_secret",
+ CustomURLMapping: &oauth2.CustomURLMapping{},
+ },
+ TwoFactorPolicy: "",
+ },
+ },
+ {
+ name: "valid config with options",
+ id: 1,
+ existingAuthSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.OAuth2,
+ Name: "old name",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ Provider: "gitlab",
+ ClientID: "old_key",
+ ClientSecret: "old_secret",
+ CustomURLMapping: &oauth2.CustomURLMapping{
+ TokenURL: "https://old.example.com/token",
+ AuthURL: "https://old.example.com/auth",
+ ProfileURL: "https://old.example.com/profile",
+ EmailURL: "https://old.example.com/email",
+ Tenant: "old_tenant",
+ },
+ IconURL: "https://old.example.com/icon",
+ Scopes: []string{"old_scope1", "old_scope2"},
+ RequiredClaimName: "old_claim_name",
+ RequiredClaimValue: "old_claim_value",
+ GroupClaimName: "old_group_name",
+ AdminGroup: "old_admin",
+ RestrictedGroup: "old_restricted",
+ GroupTeamMap: `{"old_group1": [1,2]}`,
+ GroupTeamMapRemoval: true,
+ },
+ TwoFactorPolicy: "",
+ },
+ args: []string{
+ "--id", "1",
+ "--name", "test",
+ "--provider", "github",
+ "--key", "new_key",
+ "--secret", "new_secret",
+ "--use-custom-urls", "true",
+ "--custom-token-url", "https://example.com/token",
+ "--custom-auth-url", "https://example.com/auth",
+ "--custom-profile-url", "https://example.com/profile",
+ "--custom-email-url", "https://example.com/email",
+ "--custom-tenant-id", "new_tenant",
+ "--icon-url", "https://example.com/icon",
+ "--scopes", "scope1,scope2",
+ "--skip-local-2fa=true",
+ "--required-claim-name", "claim_name",
+ "--required-claim-value", "claim_value",
+ "--group-claim-name", "group_name",
+ "--admin-group", "admin",
+ "--restricted-group", "restricted",
+ "--group-team-map", `{"group1": [1,2]}`,
+ "--group-team-map-removal=false",
+ },
+ authSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.OAuth2,
+ Name: "test",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ Provider: "github",
+ ClientID: "new_key",
+ ClientSecret: "new_secret",
+ CustomURLMapping: &oauth2.CustomURLMapping{
+ TokenURL: "https://example.com/token",
+ AuthURL: "https://example.com/auth",
+ ProfileURL: "https://example.com/profile",
+ EmailURL: "https://example.com/email",
+ Tenant: "new_tenant",
+ },
+ IconURL: "https://example.com/icon",
+ Scopes: []string{"scope1", "scope2"},
+ RequiredClaimName: "claim_name",
+ RequiredClaimValue: "claim_value",
+ GroupClaimName: "group_name",
+ AdminGroup: "admin",
+ RestrictedGroup: "restricted",
+ GroupTeamMap: `{"group1": [1,2]}`,
+ GroupTeamMapRemoval: false,
+ },
+ TwoFactorPolicy: "skip",
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ a := &authService{
+ initDB: func(ctx context.Context) error {
+ return nil
+ },
+ getAuthSourceByID: func(ctx context.Context, id int64) (*auth_model.Source, error) {
+ return &auth_model.Source{
+ ID: 1,
+ Type: auth_model.OAuth2,
+ Name: "test",
+ IsActive: true,
+ Cfg: &oauth2.Source{
+ CustomURLMapping: &oauth2.CustomURLMapping{},
+ },
+ TwoFactorPolicy: "skip",
+ }, nil
+ },
+ updateAuthSource: func(ctx context.Context, source *auth_model.Source) error {
+ assert.Equal(t, tc.authSource, source)
+ return nil
+ },
+ }
+
+ app := &cli.Command{
+ Flags: microcmdAuthUpdateOauth().Flags,
+ Action: a.runUpdateOauth,
+ }
+
+ args := []string{"oauth-test"}
+ args = append(args, tc.args...)
+
+ err := app.Run(t.Context(), args)
+
+ if tc.errMsg != "" {
+ assert.EqualError(t, err, tc.errMsg)
+ } else {
+ assert.NoError(t, err)
+ }
+ })
+ }
+}
diff --git a/cmd/admin_auth_stmp.go b/cmd/admin_auth_smtp.go
index d724746905..93e0587fc3 100644
--- a/cmd/admin_auth_stmp.go
+++ b/cmd/admin_auth_smtp.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"errors"
"strings"
@@ -11,11 +12,11 @@ import (
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/auth/source/smtp"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-var (
- smtpCLIFlags = []cli.Flag{
+func smtpCLIFlags() []cli.Flag {
+ return []cli.Flag{
&cli.StringFlag{
Name: "name",
Value: "",
@@ -38,12 +39,10 @@ var (
&cli.BoolFlag{
Name: "force-smtps",
Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.",
- Value: true,
},
&cli.BoolFlag{
Name: "skip-verify",
Usage: "Skip TLS verify.",
- Value: true,
},
&cli.StringFlag{
Name: "helo-hostname",
@@ -53,7 +52,6 @@ var (
&cli.BoolFlag{
Name: "disable-helo",
Usage: "Disable SMTP helo.",
- Value: true,
},
&cli.StringFlag{
Name: "allowed-domains",
@@ -63,7 +61,6 @@ var (
&cli.BoolFlag{
Name: "skip-local-2fa",
Usage: "Skip 2FA to log on.",
- Value: true,
},
&cli.BoolFlag{
Name: "active",
@@ -71,23 +68,34 @@ var (
Value: true,
},
}
+}
- microcmdAuthAddSMTP = &cli.Command{
- Name: "add-smtp",
- Usage: "Add new SMTP authentication source",
- Action: runAddSMTP,
- Flags: smtpCLIFlags,
+func microcmdAuthUpdateSMTP() *cli.Command {
+ return &cli.Command{
+ Name: "update-smtp",
+ Usage: "Update existing SMTP authentication source",
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().runUpdateSMTP(ctx, cmd)
+ },
+ Flags: append(smtpCLIFlags()[:1], append([]cli.Flag{&cli.Int64Flag{
+ Name: "id",
+ Usage: "ID of authentication source",
+ }}, smtpCLIFlags()[1:]...)...),
}
+}
- microcmdAuthUpdateSMTP = &cli.Command{
- Name: "update-smtp",
- Usage: "Update existing SMTP authentication source",
- Action: runUpdateSMTP,
- Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...),
+func microcmdAuthAddSMTP() *cli.Command {
+ return &cli.Command{
+ Name: "add-smtp",
+ Usage: "Add new SMTP authentication source",
+ Action: func(ctx context.Context, cmd *cli.Command) error {
+ return newAuthService().runAddSMTP(ctx, cmd)
+ },
+ Flags: smtpCLIFlags(),
}
-)
+}
-func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
+func parseSMTPConfig(c *cli.Command, conf *smtp.Source) error {
if c.IsSet("auth-type") {
conf.Auth = c.String("auth-type")
validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
@@ -117,17 +125,11 @@ func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
if c.IsSet("disable-helo") {
conf.DisableHelo = c.Bool("disable-helo")
}
- if c.IsSet("skip-local-2fa") {
- conf.SkipLocalTwoFA = c.Bool("skip-local-2fa")
- }
return nil
}
-func runAddSMTP(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(ctx); err != nil {
+func (a *authService) runAddSMTP(ctx context.Context, c *cli.Command) error {
+ if err := a.initDB(ctx); err != nil {
return err
}
@@ -155,27 +157,25 @@ func runAddSMTP(c *cli.Context) error {
smtpConfig.Auth = "PLAIN"
}
- return auth_model.CreateSource(ctx, &auth_model.Source{
- Type: auth_model.SMTP,
- Name: c.String("name"),
- IsActive: active,
- Cfg: &smtpConfig,
+ return a.createAuthSource(ctx, &auth_model.Source{
+ Type: auth_model.SMTP,
+ Name: c.String("name"),
+ IsActive: active,
+ Cfg: &smtpConfig,
+ TwoFactorPolicy: util.Iif(c.Bool("skip-local-2fa"), "skip", ""),
})
}
-func runUpdateSMTP(c *cli.Context) error {
+func (a *authService) runUpdateSMTP(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
- ctx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(ctx); err != nil {
+ if err := a.initDB(ctx); err != nil {
return err
}
- source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
+ source, err := a.getAuthSourceByID(ctx, c.Int64("id"))
if err != nil {
return err
}
@@ -195,6 +195,6 @@ func runUpdateSMTP(c *cli.Context) error {
}
source.Cfg = smtpConfig
-
- return auth_model.UpdateSource(ctx, source)
+ source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
+ return a.updateAuthSource(ctx, source)
}
diff --git a/cmd/admin_auth_smtp_test.go b/cmd/admin_auth_smtp_test.go
new file mode 100644
index 0000000000..e54e01830c
--- /dev/null
+++ b/cmd/admin_auth_smtp_test.go
@@ -0,0 +1,271 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "context"
+ "testing"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/services/auth/source/smtp"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/urfave/cli/v3"
+)
+
+func TestAddSMTP(t *testing.T) {
+ testCases := []struct {
+ name string
+ args []string
+ source *auth_model.Source
+ errMsg string
+ }{
+ {
+ name: "missing name",
+ args: []string{
+ "--host", "localhost",
+ "--port", "25",
+ },
+ errMsg: "name must be set",
+ },
+ {
+ name: "missing host",
+ args: []string{
+ "--name", "test",
+ "--port", "25",
+ },
+ errMsg: "host must be set",
+ },
+ {
+ name: "missing port",
+ args: []string{
+ "--name", "test",
+ "--host", "localhost",
+ },
+ errMsg: "port must be set",
+ },
+ {
+ name: "valid config",
+ args: []string{
+ "--name", "test",
+ "--host", "localhost",
+ "--port", "25",
+ },
+ source: &auth_model.Source{
+ Type: auth_model.SMTP,
+ Name: "test",
+ IsActive: true,
+ Cfg: &smtp.Source{
+ Auth: "PLAIN",
+ Host: "localhost",
+ Port: 25,
+ },
+ TwoFactorPolicy: "",
+ },
+ },
+ {
+ name: "valid config with options",
+ args: []string{
+ "--name", "test",
+ "--host", "localhost",
+ "--port", "25",
+ "--auth-type", "LOGIN",
+ "--force-smtps",
+ "--skip-verify",
+ "--helo-hostname", "example.com",
+ "--disable-helo=true",
+ "--allowed-domains", "example.com,example.org",
+ "--skip-local-2fa",
+ "--active=false",
+ },
+ source: &auth_model.Source{
+ Type: auth_model.SMTP,
+ Name: "test",
+ IsActive: false,
+ Cfg: &smtp.Source{
+ Auth: "LOGIN",
+ Host: "localhost",
+ Port: 25,
+ ForceSMTPS: true,
+ SkipVerify: true,
+ HeloHostname: "example.com",
+ DisableHelo: true,
+ AllowedDomains: "example.com,example.org",
+ },
+ TwoFactorPolicy: "skip",
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ a := &authService{
+ initDB: func(ctx context.Context) error {
+ return nil
+ },
+ createAuthSource: func(ctx context.Context, source *auth_model.Source) error {
+ assert.Equal(t, tc.source, source)
+ return nil
+ },
+ }
+
+ cmd := &cli.Command{
+ Flags: microcmdAuthAddSMTP().Flags,
+ Action: a.runAddSMTP,
+ }
+
+ args := []string{"smtp-test"}
+ args = append(args, tc.args...)
+
+ t.Log(args)
+ err := cmd.Run(t.Context(), args)
+
+ if tc.errMsg != "" {
+ assert.EqualError(t, err, tc.errMsg)
+ } else {
+ assert.NoError(t, err)
+ }
+ })
+ }
+}
+
+func TestUpdateSMTP(t *testing.T) {
+ testCases := []struct {
+ name string
+ args []string
+ existingAuthSource *auth_model.Source
+ authSource *auth_model.Source
+ errMsg string
+ }{
+ {
+ name: "missing id",
+ args: []string{
+ "--name", "test",
+ "--host", "localhost",
+ "--port", "25",
+ },
+ errMsg: "--id flag is missing",
+ },
+ {
+ name: "valid config",
+ existingAuthSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.SMTP,
+ Name: "old name",
+ IsActive: true,
+ Cfg: &smtp.Source{
+ Auth: "PLAIN",
+ Host: "old host",
+ Port: 26,
+ },
+ },
+ args: []string{
+ "--id", "1",
+ "--name", "test",
+ "--host", "localhost",
+ "--port", "25",
+ },
+ authSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.SMTP,
+ Name: "test",
+ IsActive: true,
+ Cfg: &smtp.Source{
+ Auth: "PLAIN",
+ Host: "localhost",
+ Port: 25,
+ },
+ },
+ },
+ {
+ name: "valid config with options",
+ existingAuthSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.SMTP,
+ Name: "old name",
+ IsActive: true,
+ Cfg: &smtp.Source{
+ Auth: "PLAIN",
+ Host: "old host",
+ Port: 26,
+ HeloHostname: "old.example.com",
+ AllowedDomains: "old.example.com",
+ },
+ TwoFactorPolicy: "",
+ },
+ args: []string{
+ "--id", "1",
+ "--name", "test",
+ "--host", "localhost",
+ "--port", "25",
+ "--auth-type", "LOGIN",
+ "--force-smtps",
+ "--skip-verify",
+ "--helo-hostname", "example.com",
+ "--disable-helo",
+ "--allowed-domains", "example.com,example.org",
+ "--skip-local-2fa",
+ "--active=false",
+ },
+ authSource: &auth_model.Source{
+ ID: 1,
+ Type: auth_model.SMTP,
+ Name: "test",
+ IsActive: false,
+ Cfg: &smtp.Source{
+ Auth: "LOGIN",
+ Host: "localhost",
+ Port: 25,
+ ForceSMTPS: true,
+ SkipVerify: true,
+ HeloHostname: "example.com",
+ DisableHelo: true,
+ AllowedDomains: "example.com,example.org",
+ },
+ TwoFactorPolicy: "skip",
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ a := &authService{
+ initDB: func(ctx context.Context) error {
+ return nil
+ },
+ getAuthSourceByID: func(ctx context.Context, id int64) (*auth_model.Source, error) {
+ return &auth_model.Source{
+ ID: 1,
+ Type: auth_model.SMTP,
+ Name: "test",
+ IsActive: true,
+ Cfg: &smtp.Source{
+ Auth: "PLAIN",
+ },
+ }, nil
+ },
+
+ updateAuthSource: func(ctx context.Context, source *auth_model.Source) error {
+ assert.Equal(t, tc.authSource, source)
+ return nil
+ },
+ }
+
+ app := &cli.Command{
+ Flags: microcmdAuthUpdateSMTP().Flags,
+ Action: a.runUpdateSMTP,
+ }
+ args := []string{"smtp-tests"}
+ args = append(args, tc.args...)
+
+ err := app.Run(t.Context(), args)
+
+ if tc.errMsg != "" {
+ assert.EqualError(t, err, tc.errMsg)
+ } else {
+ assert.NoError(t, err)
+ }
+ })
+ }
+}
diff --git a/cmd/admin_regenerate.go b/cmd/admin_regenerate.go
index ab769f6d0c..a5f1bd5105 100644
--- a/cmd/admin_regenerate.go
+++ b/cmd/admin_regenerate.go
@@ -4,11 +4,13 @@
package cmd
import (
+ "context"
+
"code.gitea.io/gitea/modules/graceful"
asymkey_service "code.gitea.io/gitea/services/asymkey"
repo_service "code.gitea.io/gitea/services/repository"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var (
@@ -25,20 +27,14 @@ var (
}
)
-func runRegenerateHooks(_ *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runRegenerateHooks(ctx context.Context, _ *cli.Command) error {
if err := initDB(ctx); err != nil {
return err
}
return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
}
-func runRegenerateKeys(_ *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runRegenerateKeys(ctx context.Context, _ *cli.Command) error {
if err := initDB(ctx); err != nil {
return err
}
diff --git a/cmd/admin_user.go b/cmd/admin_user.go
index 967a6ed88a..3a24c3e56f 100644
--- a/cmd/admin_user.go
+++ b/cmd/admin_user.go
@@ -4,18 +4,18 @@
package cmd
import (
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var subcmdUser = &cli.Command{
Name: "user",
Usage: "Modify users",
- Subcommands: []*cli.Command{
- microcmdUserCreate,
+ Commands: []*cli.Command{
+ microcmdUserCreate(),
microcmdUserList,
- microcmdUserChangePassword,
- microcmdUserDelete,
+ microcmdUserChangePassword(),
+ microcmdUserDelete(),
microcmdUserGenerateAccessToken,
- microcmdUserMustChangePassword,
+ microcmdUserMustChangePassword(),
},
}
diff --git a/cmd/admin_user_change_password.go b/cmd/admin_user_change_password.go
index f1ed46e70b..c27905b4db 100644
--- a/cmd/admin_user_change_password.go
+++ b/cmd/admin_user_change_password.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"errors"
"fmt"
@@ -13,44 +14,41 @@ import (
"code.gitea.io/gitea/modules/setting"
user_service "code.gitea.io/gitea/services/user"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-var microcmdUserChangePassword = &cli.Command{
- Name: "change-password",
- Usage: "Change a user's password",
- Action: runChangePassword,
- Flags: []cli.Flag{
- &cli.StringFlag{
- Name: "username",
- Aliases: []string{"u"},
- Value: "",
- Usage: "The user to change password for",
+func microcmdUserChangePassword() *cli.Command {
+ return &cli.Command{
+ Name: "change-password",
+ Usage: "Change a user's password",
+ Action: runChangePassword,
+ Flags: []cli.Flag{
+ &cli.StringFlag{
+ Name: "username",
+ Aliases: []string{"u"},
+ Usage: "The user to change password for",
+ Required: true,
+ },
+ &cli.StringFlag{
+ Name: "password",
+ Aliases: []string{"p"},
+ Usage: "New password to set for user",
+ Required: true,
+ },
+ &cli.BoolFlag{
+ Name: "must-change-password",
+ Usage: "User must change password (can be disabled by --must-change-password=false)",
+ Value: true,
+ },
},
- &cli.StringFlag{
- Name: "password",
- Aliases: []string{"p"},
- Value: "",
- Usage: "New password to set for user",
- },
- &cli.BoolFlag{
- Name: "must-change-password",
- Usage: "User must change password (can be disabled by --must-change-password=false)",
- Value: true,
- },
- },
-}
-
-func runChangePassword(c *cli.Context) error {
- if err := argsSet(c, "username", "password"); err != nil {
- return err
}
+}
- ctx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(ctx); err != nil {
- return err
+func runChangePassword(ctx context.Context, c *cli.Command) error {
+ if !setting.IsInTesting {
+ if err := initDB(ctx); err != nil {
+ return err
+ }
}
user, err := user_model.GetUserByName(ctx, c.String("username"))
diff --git a/cmd/admin_user_change_password_test.go b/cmd/admin_user_change_password_test.go
new file mode 100644
index 0000000000..17d0382af7
--- /dev/null
+++ b/cmd/admin_user_change_password_test.go
@@ -0,0 +1,91 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestChangePasswordCommand(t *testing.T) {
+ ctx := t.Context()
+
+ defer func() {
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
+ }()
+
+ t.Run("change password successfully", func(t *testing.T) {
+ // defer func() {
+ // require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
+ // }()
+ // Prepare test user
+ unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
+ err := microcmdUserCreate().Run(ctx, []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
+ require.NoError(t, err)
+
+ // load test user
+ userBase := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+
+ // Change the password
+ err = microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "newpassword"})
+ require.NoError(t, err)
+
+ // Verify the password has been changed
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.NotEqual(t, userBase.Passwd, user.Passwd)
+ assert.NotEqual(t, userBase.Salt, user.Salt)
+
+ // Additional check for must-change-password flag
+ require.NoError(t, microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "anotherpassword", "--must-change-password=false"}))
+ user = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.False(t, user.MustChangePassword)
+
+ require.NoError(t, microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "yetanotherpassword", "--must-change-password"}))
+ user = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.True(t, user.MustChangePassword)
+ })
+
+ t.Run("failure cases", func(t *testing.T) {
+ testCases := []struct {
+ name string
+ args []string
+ expectedErr string
+ }{
+ {
+ name: "user does not exist",
+ args: []string{"change-password", "--username", "nonexistentuser", "--password", "newpassword"},
+ expectedErr: "user does not exist",
+ },
+ {
+ name: "missing username",
+ args: []string{"change-password", "--password", "newpassword"},
+ expectedErr: `"username" not set`,
+ },
+ {
+ name: "missing password",
+ args: []string{"change-password", "--username", "testuser"},
+ expectedErr: `"password" not set`,
+ },
+ {
+ name: "too short password",
+ args: []string{"change-password", "--username", "testuser", "--password", "1"},
+ expectedErr: "password is not long enough",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ err := microcmdUserChangePassword().Run(ctx, tc.args)
+ require.Error(t, err)
+ require.Contains(t, err.Error(), tc.expectedErr)
+ })
+ }
+ })
+}
diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go
index bf8cbc7c4c..cbdb5f90e2 100644
--- a/cmd/admin_user_create.go
+++ b/cmd/admin_user_create.go
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
+ "strings"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
@@ -15,73 +16,110 @@ import (
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-var microcmdUserCreate = &cli.Command{
- Name: "create",
- Usage: "Create a new user in database",
- Action: runCreateUser,
- Flags: []cli.Flag{
- &cli.StringFlag{
- Name: "name",
- Usage: "Username. DEPRECATED: use username instead",
+func microcmdUserCreate() *cli.Command {
+ return &cli.Command{
+ Name: "create",
+ Usage: "Create a new user in database",
+ Action: runCreateUser,
+ MutuallyExclusiveFlags: []cli.MutuallyExclusiveFlags{
+ {
+ Flags: [][]cli.Flag{
+ {
+ &cli.StringFlag{
+ Name: "name",
+ Usage: "Username. DEPRECATED: use username instead",
+ },
+ &cli.StringFlag{
+ Name: "username",
+ Usage: "Username",
+ },
+ },
+ },
+ Required: true,
+ },
},
- &cli.StringFlag{
- Name: "username",
- Usage: "Username",
+ Flags: []cli.Flag{
+ &cli.StringFlag{
+ Name: "user-type",
+ Usage: "Set user's type: individual or bot",
+ Value: "individual",
+ },
+ &cli.StringFlag{
+ Name: "password",
+ Usage: "User password",
+ },
+ &cli.StringFlag{
+ Name: "email",
+ Usage: "User email address",
+ Required: true,
+ },
+ &cli.BoolFlag{
+ Name: "admin",
+ Usage: "User is an admin",
+ },
+ &cli.BoolFlag{
+ Name: "random-password",
+ Usage: "Generate a random password for the user",
+ },
+ &cli.BoolFlag{
+ Name: "must-change-password",
+ Usage: "User must change password after initial login, defaults to true for all users except the first one (can be disabled by --must-change-password=false)",
+ HideDefault: true,
+ },
+ &cli.IntFlag{
+ Name: "random-password-length",
+ Usage: "Length of the random password to be generated",
+ Value: 12,
+ },
+ &cli.BoolFlag{
+ Name: "access-token",
+ Usage: "Generate access token for the user",
+ },
+ &cli.StringFlag{
+ Name: "access-token-name",
+ Usage: `Name of the generated access token`,
+ Value: "gitea-admin",
+ },
+ &cli.StringFlag{
+ Name: "access-token-scopes",
+ Usage: `Scopes of the generated access token, comma separated. Examples: "all", "public-only,read:issue", "write:repository,write:user"`,
+ Value: "all",
+ },
+ &cli.BoolFlag{
+ Name: "restricted",
+ Usage: "Make a restricted user account",
+ },
+ &cli.StringFlag{
+ Name: "fullname",
+ Usage: `The full, human-readable name of the user`,
+ },
},
- &cli.StringFlag{
- Name: "password",
- Usage: "User password",
- },
- &cli.StringFlag{
- Name: "email",
- Usage: "User email address",
- },
- &cli.BoolFlag{
- Name: "admin",
- Usage: "User is an admin",
- },
- &cli.BoolFlag{
- Name: "random-password",
- Usage: "Generate a random password for the user",
- },
- &cli.BoolFlag{
- Name: "must-change-password",
- Usage: "User must change password after initial login, defaults to true for all users except the first one (can be disabled by --must-change-password=false)",
- DisableDefaultText: true,
- },
- &cli.IntFlag{
- Name: "random-password-length",
- Usage: "Length of the random password to be generated",
- Value: 12,
- },
- &cli.BoolFlag{
- Name: "access-token",
- Usage: "Generate access token for the user",
- },
- &cli.BoolFlag{
- Name: "restricted",
- Usage: "Make a restricted user account",
- },
- },
+ }
}
-func runCreateUser(c *cli.Context) error {
+func runCreateUser(ctx context.Context, c *cli.Command) error {
// this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first
// duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future.
setting.LoadSettings()
- if err := argsSet(c, "email"); err != nil {
- return err
+ userTypes := map[string]user_model.UserType{
+ "individual": user_model.UserTypeIndividual,
+ "bot": user_model.UserTypeBot,
}
-
- if c.IsSet("name") && c.IsSet("username") {
- return errors.New("cannot set both --name and --username flags")
+ userType, ok := userTypes[c.String("user-type")]
+ if !ok {
+ return fmt.Errorf("invalid user type: %s", c.String("user-type"))
}
- if !c.IsSet("name") && !c.IsSet("username") {
- return errors.New("one of --name or --username flags must be set")
+ if userType != user_model.UserTypeIndividual {
+ // Some other commands like "change-password" also only support individual users.
+ // It needs to clarify the "password" behavior for bot users in the future.
+ // At the moment, we do not allow setting password for bot users.
+ if c.IsSet("password") || c.IsSet("random-password") {
+ return errors.New("password can only be set for individual users")
+ }
}
if c.IsSet("password") && c.IsSet("random-password") {
@@ -93,16 +131,12 @@ func runCreateUser(c *cli.Context) error {
username = c.String("username")
} else {
username = c.String("name")
- _, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
+ _, _ = fmt.Fprintf(c.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
}
- ctx := c.Context
if !setting.IsInTesting {
- // FIXME: need to refactor the "installSignals/initDB" related code later
+ // FIXME: need to refactor the "initDB" related code later
// it doesn't make sense to call it in (almost) every command action function
- var cancel context.CancelFunc
- ctx, cancel = installSignals()
- defer cancel()
if err := initDB(ctx); err != nil {
return err
}
@@ -118,16 +152,19 @@ func runCreateUser(c *cli.Context) error {
return err
}
fmt.Printf("generated random password is '%s'\n", password)
- } else {
+ } else if userType == user_model.UserTypeIndividual {
return errors.New("must set either password or random-password flag")
}
isAdmin := c.Bool("admin")
mustChangePassword := true // always default to true
if c.IsSet("must-change-password") {
+ if userType != user_model.UserTypeIndividual {
+ return errors.New("must-change-password flag can only be set for individual users")
+ }
// if the flag is set, use the value provided by the user
mustChangePassword = c.Bool("must-change-password")
- } else {
+ } else if userType == user_model.UserTypeIndividual {
// check whether there are users in the database
hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{})
if err != nil {
@@ -151,10 +188,12 @@ func runCreateUser(c *cli.Context) error {
u := &user_model.User{
Name: username,
Email: c.String("email"),
- Passwd: password,
IsAdmin: isAdmin,
+ Type: userType,
+ Passwd: password,
MustChangePassword: mustChangePassword,
Visibility: visibility,
+ FullName: c.String("fullname"),
}
overwriteDefault := &user_model.CreateUserOverwriteOptions{
@@ -162,23 +201,40 @@ func runCreateUser(c *cli.Context) error {
IsRestricted: restricted,
}
+ var accessTokenName string
+ var accessTokenScope auth_model.AccessTokenScope
+ if c.IsSet("access-token") {
+ accessTokenName = strings.TrimSpace(c.String("access-token-name"))
+ if accessTokenName == "" {
+ return errors.New("access-token-name cannot be empty")
+ }
+ var err error
+ accessTokenScope, err = auth_model.AccessTokenScope(c.String("access-token-scopes")).Normalize()
+ if err != nil {
+ return fmt.Errorf("invalid access token scope provided: %w", err)
+ }
+ if !accessTokenScope.HasPermissionScope() {
+ return errors.New("access token does not have any permission")
+ }
+ } else if c.IsSet("access-token-name") || c.IsSet("access-token-scopes") {
+ return errors.New("access-token-name and access-token-scopes flags are only valid when access-token flag is set")
+ }
+
+ // arguments should be prepared before creating the user & access token, in case there is anything wrong
+
+ // create the user
if err := user_model.CreateUser(ctx, u, &user_model.Meta{}, overwriteDefault); err != nil {
return fmt.Errorf("CreateUser: %w", err)
}
+ fmt.Printf("New user '%s' has been successfully created!\n", username)
- if c.Bool("access-token") {
- t := &auth_model.AccessToken{
- Name: "gitea-admin",
- UID: u.ID,
- }
-
+ // create the access token
+ if accessTokenScope != "" {
+ t := &auth_model.AccessToken{Name: accessTokenName, UID: u.ID, Scope: accessTokenScope}
if err := auth_model.NewAccessToken(ctx, t); err != nil {
return err
}
-
fmt.Printf("Access token was successfully created... %s\n", t.Token)
}
-
- fmt.Printf("New user '%s' has been successfully created!\n", username)
return nil
}
diff --git a/cmd/admin_user_create_test.go b/cmd/admin_user_create_test.go
index 83754e97b1..437e07d9a2 100644
--- a/cmd/admin_user_create_test.go
+++ b/cmd/admin_user_create_test.go
@@ -8,37 +8,127 @@ import (
"strings"
"testing"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestAdminUserCreate(t *testing.T) {
- app := NewMainApp(AppVersion{})
-
reset := func() {
- assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
- assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &auth_model.AccessToken{}))
}
- type createCheck struct{ IsAdmin, MustChangePassword bool }
- createUser := func(name, args string) createCheck {
- assert.NoError(t, app.Run(strings.Fields(fmt.Sprintf("./gitea admin user create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
- u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
- return createCheck{u.IsAdmin, u.MustChangePassword}
+ t.Run("MustChangePassword", func(t *testing.T) {
+ type check struct {
+ IsAdmin bool
+ MustChangePassword bool
+ }
+
+ createCheck := func(name, args string) check {
+ require.NoError(t, microcmdUserCreate().Run(t.Context(), strings.Fields(fmt.Sprintf("create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
+ u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
+ return check{IsAdmin: u.IsAdmin, MustChangePassword: u.MustChangePassword}
+ }
+ reset()
+ assert.Equal(t, check{IsAdmin: false, MustChangePassword: false}, createCheck("u", ""), "first non-admin user doesn't need to change password")
+
+ reset()
+ assert.Equal(t, check{IsAdmin: true, MustChangePassword: false}, createCheck("u", "--admin"), "first admin user doesn't need to change password")
+
+ reset()
+ assert.Equal(t, check{IsAdmin: true, MustChangePassword: true}, createCheck("u", "--admin --must-change-password"))
+ assert.Equal(t, check{IsAdmin: true, MustChangePassword: true}, createCheck("u2", "--admin"))
+ assert.Equal(t, check{IsAdmin: true, MustChangePassword: false}, createCheck("u3", "--admin --must-change-password=false"))
+ assert.Equal(t, check{IsAdmin: false, MustChangePassword: true}, createCheck("u4", ""))
+ assert.Equal(t, check{IsAdmin: false, MustChangePassword: false}, createCheck("u5", "--must-change-password=false"))
+ })
+
+ createUser := func(name string, args ...string) error {
+ return microcmdUserCreate().Run(t.Context(), append([]string{"create", "--username", name, "--email", name + "@gitea.local"}, args...))
}
- reset()
- assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u", ""), "first non-admin user doesn't need to change password")
-
- reset()
- assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u", "--admin"), "first admin user doesn't need to change password")
-
- reset()
- assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u", "--admin --must-change-password"))
- assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u2", "--admin"))
- assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u3", "--admin --must-change-password=false"))
- assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: true}, createUser("u4", ""))
- assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u5", "--must-change-password=false"))
+
+ t.Run("UserType", func(t *testing.T) {
+ reset()
+ assert.ErrorContains(t, createUser("u", "--user-type", "invalid"), "invalid user type")
+ assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--password", "123"), "can only be set for individual users")
+ assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--must-change-password"), "can only be set for individual users")
+
+ assert.NoError(t, createUser("u", "--user-type", "bot"))
+ u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u"})
+ assert.Equal(t, user_model.UserTypeBot, u.Type)
+ assert.Empty(t, u.Passwd)
+ })
+
+ t.Run("AccessToken", func(t *testing.T) {
+ // no generated access token
+ reset()
+ assert.NoError(t, createUser("u", "--random-password"))
+ assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
+ assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
+
+ // using "--access-token" only means "all" access
+ reset()
+ assert.NoError(t, createUser("u", "--random-password", "--access-token"))
+ assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
+ assert.Equal(t, 1, unittest.GetCount(t, &auth_model.AccessToken{}))
+ accessToken := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{Name: "gitea-admin"})
+ hasScopes, err := accessToken.Scope.HasScope(auth_model.AccessTokenScopeWriteAdmin, auth_model.AccessTokenScopeWriteRepository)
+ assert.NoError(t, err)
+ assert.True(t, hasScopes)
+
+ // using "--access-token" with name & scopes
+ reset()
+ assert.NoError(t, createUser("u", "--random-password", "--access-token", "--access-token-name", "new-token-name", "--access-token-scopes", "read:issue,read:user"))
+ assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
+ assert.Equal(t, 1, unittest.GetCount(t, &auth_model.AccessToken{}))
+ accessToken = unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{Name: "new-token-name"})
+ hasScopes, err = accessToken.Scope.HasScope(auth_model.AccessTokenScopeReadIssue, auth_model.AccessTokenScopeReadUser)
+ assert.NoError(t, err)
+ assert.True(t, hasScopes)
+ hasScopes, err = accessToken.Scope.HasScope(auth_model.AccessTokenScopeWriteAdmin, auth_model.AccessTokenScopeWriteRepository)
+ assert.NoError(t, err)
+ assert.False(t, hasScopes)
+
+ // using "--access-token-name" without "--access-token"
+ reset()
+ err = createUser("u", "--random-password", "--access-token-name", "new-token-name")
+ assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
+ assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
+ assert.ErrorContains(t, err, "access-token-name and access-token-scopes flags are only valid when access-token flag is set")
+
+ // using "--access-token-scopes" without "--access-token"
+ reset()
+ err = createUser("u", "--random-password", "--access-token-scopes", "read:issue")
+ assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
+ assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
+ assert.ErrorContains(t, err, "access-token-name and access-token-scopes flags are only valid when access-token flag is set")
+
+ // empty permission
+ reset()
+ err = createUser("u", "--random-password", "--access-token", "--access-token-scopes", "public-only")
+ assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
+ assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
+ assert.ErrorContains(t, err, "access token does not have any permission")
+ })
+
+ t.Run("UserFields", func(t *testing.T) {
+ reset()
+ assert.NoError(t, createUser("u-FullNameWithSpace", "--random-password", "--fullname", "First O'Middle Last"))
+ unittest.AssertExistsAndLoadBean(t, &user_model.User{
+ Name: "u-FullNameWithSpace",
+ LowerName: "u-fullnamewithspace",
+ FullName: "First O'Middle Last",
+ Email: "u-FullNameWithSpace@gitea.local",
+ })
+
+ assert.NoError(t, createUser("u-FullNameEmpty", "--random-password", "--fullname", ""))
+ u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u-fullnameempty"})
+ assert.Empty(t, u.FullName)
+ })
}
diff --git a/cmd/admin_user_delete.go b/cmd/admin_user_delete.go
index 520557554a..f91041577c 100644
--- a/cmd/admin_user_delete.go
+++ b/cmd/admin_user_delete.go
@@ -4,53 +4,56 @@
package cmd
import (
+ "context"
"errors"
"fmt"
"strings"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
user_service "code.gitea.io/gitea/services/user"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-var microcmdUserDelete = &cli.Command{
- Name: "delete",
- Usage: "Delete specific user by id, name or email",
- Flags: []cli.Flag{
- &cli.Int64Flag{
- Name: "id",
- Usage: "ID of user of the user to delete",
+func microcmdUserDelete() *cli.Command {
+ return &cli.Command{
+ Name: "delete",
+ Usage: "Delete specific user by id, name or email",
+ Flags: []cli.Flag{
+ &cli.Int64Flag{
+ Name: "id",
+ Usage: "ID of user of the user to delete",
+ },
+ &cli.StringFlag{
+ Name: "username",
+ Aliases: []string{"u"},
+ Usage: "Username of the user to delete",
+ },
+ &cli.StringFlag{
+ Name: "email",
+ Aliases: []string{"e"},
+ Usage: "Email of the user to delete",
+ },
+ &cli.BoolFlag{
+ Name: "purge",
+ Usage: "Purge user, all their repositories, organizations and comments",
+ },
},
- &cli.StringFlag{
- Name: "username",
- Aliases: []string{"u"},
- Usage: "Username of the user to delete",
- },
- &cli.StringFlag{
- Name: "email",
- Aliases: []string{"e"},
- Usage: "Email of the user to delete",
- },
- &cli.BoolFlag{
- Name: "purge",
- Usage: "Purge user, all their repositories, organizations and comments",
- },
- },
- Action: runDeleteUser,
+ Action: runDeleteUser,
+ }
}
-func runDeleteUser(c *cli.Context) error {
+func runDeleteUser(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
return errors.New("You must provide the id, username or email of a user to delete")
}
- ctx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(ctx); err != nil {
- return err
+ if !setting.IsInTesting {
+ if err := initDB(ctx); err != nil {
+ return err
+ }
}
if err := storage.Init(); err != nil {
@@ -70,11 +73,11 @@ func runDeleteUser(c *cli.Context) error {
return err
}
if c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) {
- return fmt.Errorf("The user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username"))
+ return fmt.Errorf("the user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username"))
}
if c.IsSet("id") && user.ID != c.Int64("id") {
- return fmt.Errorf("The user %s does not match the provided id %d", user.Name, c.Int64("id"))
+ return fmt.Errorf("the user %s does not match the provided id %d", user.Name, c.Int64("id"))
}
return user_service.DeleteUser(ctx, user, c.Bool("purge"))
diff --git a/cmd/admin_user_delete_test.go b/cmd/admin_user_delete_test.go
new file mode 100644
index 0000000000..d0330582d7
--- /dev/null
+++ b/cmd/admin_user_delete_test.go
@@ -0,0 +1,111 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "strconv"
+ "strings"
+ "testing"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestAdminUserDelete(t *testing.T) {
+ ctx := t.Context()
+ defer func() {
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &auth_model.AccessToken{}))
+ }()
+
+ setupTestUser := func(t *testing.T) {
+ unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
+ err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
+ require.NoError(t, err)
+ }
+
+ t.Run("delete user by id", func(t *testing.T) {
+ setupTestUser(t)
+
+ u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--id", strconv.FormatInt(u.ID, 10)})
+ require.NoError(t, err)
+ unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
+ })
+ t.Run("delete user by username", func(t *testing.T) {
+ setupTestUser(t)
+
+ err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--username", "testuser"})
+ require.NoError(t, err)
+ unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
+ })
+ t.Run("delete user by email", func(t *testing.T) {
+ setupTestUser(t)
+
+ err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--email", "testuser@gitea.local"})
+ require.NoError(t, err)
+ unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
+ })
+ t.Run("delete user by all 3 attributes", func(t *testing.T) {
+ setupTestUser(t)
+
+ u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ err := microcmdUserDelete().Run(ctx, []string{"delete", "--id", strconv.FormatInt(u.ID, 10), "--username", "testuser", "--email", "testuser@gitea.local"})
+ require.NoError(t, err)
+ unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
+ })
+}
+
+func TestAdminUserDeleteFailure(t *testing.T) {
+ testCases := []struct {
+ name string
+ args []string
+ expectedErr string
+ }{
+ {
+ name: "no user to delete",
+ args: []string{"delete", "--username", "nonexistentuser"},
+ expectedErr: "user does not exist",
+ },
+ {
+ name: "user exists but provided username does not match",
+ args: []string{"delete", "--email", "testuser@gitea.local", "--username", "wrongusername"},
+ expectedErr: "the user testuser who has email testuser@gitea.local does not match the provided username wrongusername",
+ },
+ {
+ name: "user exists but provided id does not match",
+ args: []string{"delete", "--username", "testuser", "--id", "999"},
+ expectedErr: "the user testuser does not match the provided id 999",
+ },
+ {
+ name: "no required flags are provided",
+ args: []string{"delete"},
+ expectedErr: "You must provide the id, username or email of a user to delete",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ ctx := t.Context()
+ if strings.Contains(tc.name, "user exists") {
+ unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
+ err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
+ require.NoError(t, err)
+ }
+
+ err := microcmdUserDelete().Run(ctx, tc.args)
+ require.Error(t, err)
+ require.Contains(t, err.Error(), tc.expectedErr)
+ })
+
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &auth_model.AccessToken{}))
+ }
+}
diff --git a/cmd/admin_user_generate_access_token.go b/cmd/admin_user_generate_access_token.go
index 6c2c10494e..61064fdef4 100644
--- a/cmd/admin_user_generate_access_token.go
+++ b/cmd/admin_user_generate_access_token.go
@@ -4,13 +4,14 @@
package cmd
import (
+ "context"
"errors"
"fmt"
auth_model "code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var microcmdUserGenerateAccessToken = &cli.Command{
@@ -34,21 +35,18 @@ var microcmdUserGenerateAccessToken = &cli.Command{
},
&cli.StringFlag{
Name: "scopes",
- Value: "",
- Usage: "Comma separated list of scopes to apply to access token",
+ Value: "all",
+ Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`,
},
},
Action: runGenerateAccessToken,
}
-func runGenerateAccessToken(c *cli.Context) error {
+func runGenerateAccessToken(ctx context.Context, c *cli.Command) error {
if !c.IsSet("username") {
- return errors.New("You must provide a username to generate a token for")
+ return errors.New("you must provide a username to generate a token for")
}
- ctx, cancel := installSignals()
- defer cancel()
-
if err := initDB(ctx); err != nil {
return err
}
@@ -77,6 +75,9 @@ func runGenerateAccessToken(c *cli.Context) error {
if err != nil {
return fmt.Errorf("invalid access token scope provided: %w", err)
}
+ if !accessTokenScope.HasPermissionScope() {
+ return errors.New("access token does not have any permission")
+ }
t.Scope = accessTokenScope
// create the token
diff --git a/cmd/admin_user_list.go b/cmd/admin_user_list.go
index 4c2b26d1df..e3d345e2f2 100644
--- a/cmd/admin_user_list.go
+++ b/cmd/admin_user_list.go
@@ -4,13 +4,14 @@
package cmd
import (
+ "context"
"fmt"
"os"
"text/tabwriter"
user_model "code.gitea.io/gitea/models/user"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var microcmdUserList = &cli.Command{
@@ -25,10 +26,7 @@ var microcmdUserList = &cli.Command{
},
}
-func runListUsers(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runListUsers(ctx context.Context, c *cli.Command) error {
if err := initDB(ctx); err != nil {
return err
}
diff --git a/cmd/admin_user_must_change_password.go b/cmd/admin_user_must_change_password.go
index 2794414259..8521853dc1 100644
--- a/cmd/admin_user_must_change_password.go
+++ b/cmd/admin_user_must_change_password.go
@@ -4,40 +4,41 @@
package cmd
import (
+ "context"
"errors"
"fmt"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-var microcmdUserMustChangePassword = &cli.Command{
- Name: "must-change-password",
- Usage: "Set the must change password flag for the provided users or all users",
- Action: runMustChangePassword,
- Flags: []cli.Flag{
- &cli.BoolFlag{
- Name: "all",
- Aliases: []string{"A"},
- Usage: "All users must change password, except those explicitly excluded with --exclude",
+func microcmdUserMustChangePassword() *cli.Command {
+ return &cli.Command{
+ Name: "must-change-password",
+ Usage: "Set the must change password flag for the provided users or all users",
+ Action: runMustChangePassword,
+ Flags: []cli.Flag{
+ &cli.BoolFlag{
+ Name: "all",
+ Aliases: []string{"A"},
+ Usage: "All users must change password, except those explicitly excluded with --exclude",
+ },
+ &cli.StringSliceFlag{
+ Name: "exclude",
+ Aliases: []string{"e"},
+ Usage: "Do not change the must-change-password flag for these users",
+ },
+ &cli.BoolFlag{
+ Name: "unset",
+ Usage: "Instead of setting the must-change-password flag, unset it",
+ },
},
- &cli.StringSliceFlag{
- Name: "exclude",
- Aliases: []string{"e"},
- Usage: "Do not change the must-change-password flag for these users",
- },
- &cli.BoolFlag{
- Name: "unset",
- Usage: "Instead of setting the must-change-password flag, unset it",
- },
- },
+ }
}
-func runMustChangePassword(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runMustChangePassword(ctx context.Context, c *cli.Command) error {
if c.NArg() == 0 && !c.IsSet("all") {
return errors.New("either usernames or --all must be provided")
}
@@ -46,8 +47,10 @@ func runMustChangePassword(c *cli.Context) error {
all := c.Bool("all")
exclude := c.StringSlice("exclude")
- if err := initDB(ctx); err != nil {
- return err
+ if !setting.IsInTesting {
+ if err := initDB(ctx); err != nil {
+ return err
+ }
}
n, err := user_model.SetMustChangePassword(ctx, all, mustChangePassword, c.Args().Slice(), exclude)
diff --git a/cmd/admin_user_must_change_password_test.go b/cmd/admin_user_must_change_password_test.go
new file mode 100644
index 0000000000..a6611fdc04
--- /dev/null
+++ b/cmd/admin_user_must_change_password_test.go
@@ -0,0 +1,78 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestMustChangePassword(t *testing.T) {
+ defer func() {
+ require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
+ }()
+ err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
+ require.NoError(t, err)
+ err = microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuserexclude", "--email", "testuserexclude@gitea.local", "--random-password"})
+ require.NoError(t, err)
+ // Reset password change flag
+ err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset"})
+ require.NoError(t, err)
+
+ testUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.False(t, testUser.MustChangePassword)
+ testUserExclude := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
+ assert.False(t, testUserExclude.MustChangePassword)
+
+ // Make all users change password
+ err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all"})
+ require.NoError(t, err)
+
+ testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.True(t, testUser.MustChangePassword)
+ testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
+ assert.True(t, testUserExclude.MustChangePassword)
+
+ // Reset password change flag but exclude all tested users
+ err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset", "--exclude", "testuser,testuserexclude"})
+ require.NoError(t, err)
+
+ testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.True(t, testUser.MustChangePassword)
+ testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
+ assert.True(t, testUserExclude.MustChangePassword)
+
+ // Reset password change flag by listing multiple users
+ err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser", "testuserexclude"})
+ require.NoError(t, err)
+
+ testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.False(t, testUser.MustChangePassword)
+ testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
+ assert.False(t, testUserExclude.MustChangePassword)
+
+ // Exclude a user from all user
+ err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--exclude", "testuserexclude"})
+ require.NoError(t, err)
+
+ testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.True(t, testUser.MustChangePassword)
+ testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
+ assert.False(t, testUserExclude.MustChangePassword)
+
+ // Unset a flag for single user
+ err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser"})
+ require.NoError(t, err)
+
+ testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
+ assert.False(t, testUser.MustChangePassword)
+ testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
+ assert.False(t, testUserExclude.MustChangePassword)
+}
diff --git a/cmd/cert.go b/cmd/cert.go
index 38241d71a3..53b4f9dcb4 100644
--- a/cmd/cert.go
+++ b/cmd/cert.go
@@ -6,6 +6,7 @@
package cmd
import (
+ "context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
@@ -13,6 +14,7 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
+ "fmt"
"log"
"math/big"
"net"
@@ -20,47 +22,59 @@ import (
"strings"
"time"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-// CmdCert represents the available cert sub-command.
-var CmdCert = &cli.Command{
- Name: "cert",
- Usage: "Generate self-signed certificate",
- Description: `Generate a self-signed X.509 certificate for a TLS server.
+// cmdCert represents the available cert sub-command.
+func cmdCert() *cli.Command {
+ return &cli.Command{
+ Name: "cert",
+ Usage: "Generate self-signed certificate",
+ Description: `Generate a self-signed X.509 certificate for a TLS server.
Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
- Action: runCert,
- Flags: []cli.Flag{
- &cli.StringFlag{
- Name: "host",
- Value: "",
- Usage: "Comma-separated hostnames and IPs to generate a certificate for",
+ Action: runCert,
+ Flags: []cli.Flag{
+ &cli.StringFlag{
+ Name: "host",
+ Usage: "Comma-separated hostnames and IPs to generate a certificate for",
+ Required: true,
+ },
+ &cli.StringFlag{
+ Name: "ecdsa-curve",
+ Value: "",
+ Usage: "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521",
+ },
+ &cli.IntFlag{
+ Name: "rsa-bits",
+ Value: 3072,
+ Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set",
+ },
+ &cli.StringFlag{
+ Name: "start-date",
+ Value: "",
+ Usage: "Creation date formatted as Jan 1 15:04:05 2011",
+ },
+ &cli.DurationFlag{
+ Name: "duration",
+ Value: 365 * 24 * time.Hour,
+ Usage: "Duration that certificate is valid for",
+ },
+ &cli.BoolFlag{
+ Name: "ca",
+ Usage: "whether this cert should be its own Certificate Authority",
+ },
+ &cli.StringFlag{
+ Name: "out",
+ Value: "cert.pem",
+ Usage: "Path to the file where there certificate will be saved",
+ },
+ &cli.StringFlag{
+ Name: "keyout",
+ Value: "key.pem",
+ Usage: "Path to the file where there certificate key will be saved",
+ },
},
- &cli.StringFlag{
- Name: "ecdsa-curve",
- Value: "",
- Usage: "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521",
- },
- &cli.IntFlag{
- Name: "rsa-bits",
- Value: 3072,
- Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set",
- },
- &cli.StringFlag{
- Name: "start-date",
- Value: "",
- Usage: "Creation date formatted as Jan 1 15:04:05 2011",
- },
- &cli.DurationFlag{
- Name: "duration",
- Value: 365 * 24 * time.Hour,
- Usage: "Duration that certificate is valid for",
- },
- &cli.BoolFlag{
- Name: "ca",
- Usage: "whether this cert should be its own Certificate Authority",
- },
- },
+ }
}
func publicKey(priv any) any {
@@ -89,11 +103,7 @@ func pemBlockForKey(priv any) *pem.Block {
}
}
-func runCert(c *cli.Context) error {
- if err := argsSet(c, "host"); err != nil {
- return err
- }
-
+func runCert(_ context.Context, c *cli.Command) error {
var priv any
var err error
switch c.String("ecdsa-curve") {
@@ -108,17 +118,17 @@ func runCert(c *cli.Context) error {
case "P521":
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
default:
- log.Fatalf("Unrecognized elliptic curve: %q", c.String("ecdsa-curve"))
+ err = fmt.Errorf("unrecognized elliptic curve: %q", c.String("ecdsa-curve"))
}
if err != nil {
- log.Fatalf("Failed to generate private key: %v", err)
+ return fmt.Errorf("failed to generate private key: %w", err)
}
var notBefore time.Time
if startDate := c.String("start-date"); startDate != "" {
notBefore, err = time.Parse("Jan 2 15:04:05 2006", startDate)
if err != nil {
- log.Fatalf("Failed to parse creation date: %v", err)
+ return fmt.Errorf("failed to parse creation date %w", err)
}
} else {
notBefore = time.Now()
@@ -129,7 +139,7 @@ func runCert(c *cli.Context) error {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
- log.Fatalf("Failed to generate serial number: %v", err)
+ return fmt.Errorf("failed to generate serial number: %w", err)
}
template := x509.Certificate{
@@ -146,8 +156,8 @@ func runCert(c *cli.Context) error {
BasicConstraintsValid: true,
}
- hosts := strings.Split(c.String("host"), ",")
- for _, h := range hosts {
+ hosts := strings.SplitSeq(c.String("host"), ",")
+ for h := range hosts {
if ip := net.ParseIP(h); ip != nil {
template.IPAddresses = append(template.IPAddresses, ip)
} else {
@@ -162,35 +172,35 @@ func runCert(c *cli.Context) error {
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
if err != nil {
- log.Fatalf("Failed to create certificate: %v", err)
+ return fmt.Errorf("failed to create certificate: %w", err)
}
- certOut, err := os.Create("cert.pem")
+ certOut, err := os.Create(c.String("out"))
if err != nil {
- log.Fatalf("Failed to open cert.pem for writing: %v", err)
+ return fmt.Errorf("failed to open %s for writing: %w", c.String("keyout"), err)
}
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
if err != nil {
- log.Fatalf("Failed to encode certificate: %v", err)
+ return fmt.Errorf("failed to encode certificate: %w", err)
}
err = certOut.Close()
if err != nil {
- log.Fatalf("Failed to write cert: %v", err)
+ return fmt.Errorf("failed to write cert: %w", err)
}
- log.Println("Written cert.pem")
+ fmt.Fprintf(c.Writer, "Written cert to %s\n", c.String("out"))
- keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
+ keyOut, err := os.OpenFile(c.String("keyout"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
if err != nil {
- log.Fatalf("Failed to open key.pem for writing: %v", err)
+ return fmt.Errorf("failed to open %s for writing: %w", c.String("keyout"), err)
}
err = pem.Encode(keyOut, pemBlockForKey(priv))
if err != nil {
- log.Fatalf("Failed to encode key: %v", err)
+ return fmt.Errorf("failed to encode key: %w", err)
}
err = keyOut.Close()
if err != nil {
- log.Fatalf("Failed to write key: %v", err)
+ return fmt.Errorf("failed to write key: %w", err)
}
- log.Println("Written key.pem")
+ fmt.Fprintf(c.Writer, "Written key to %s\n", c.String("keyout"))
return nil
}
diff --git a/cmd/cert_test.go b/cmd/cert_test.go
new file mode 100644
index 0000000000..4242d8915b
--- /dev/null
+++ b/cmd/cert_test.go
@@ -0,0 +1,123 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestCertCommand(t *testing.T) {
+ cases := []struct {
+ name string
+ args []string
+ }{
+ {
+ name: "RSA cert generation",
+ args: []string{
+ "cert-test",
+ "--host", "localhost",
+ "--rsa-bits", "2048",
+ "--duration", "1h",
+ "--start-date", "Jan 1 00:00:00 2024",
+ },
+ },
+ {
+ name: "ECDSA cert generation",
+ args: []string{
+ "cert-test",
+ "--host", "localhost",
+ "--ecdsa-curve", "P256",
+ "--duration", "1h",
+ "--start-date", "Jan 1 00:00:00 2024",
+ },
+ },
+ {
+ name: "mixed host, certificate authority",
+ args: []string{
+ "cert-test",
+ "--host", "localhost,127.0.0.1",
+ "--duration", "1h",
+ "--start-date", "Jan 1 00:00:00 2024",
+ },
+ },
+ }
+
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ app := cmdCert()
+ tempDir := t.TempDir()
+
+ certFile := filepath.Join(tempDir, "cert.pem")
+ keyFile := filepath.Join(tempDir, "key.pem")
+
+ err := app.Run(t.Context(), append(c.args, "--out", certFile, "--keyout", keyFile))
+ require.NoError(t, err)
+
+ assert.FileExists(t, certFile)
+ assert.FileExists(t, keyFile)
+ })
+ }
+}
+
+func TestCertCommandFailures(t *testing.T) {
+ cases := []struct {
+ name string
+ args []string
+ errMsg string
+ }{
+ {
+ name: "Start Date Parsing failure",
+ args: []string{
+ "cert-test",
+ "--host", "localhost",
+ "--start-date", "invalid-date",
+ },
+ errMsg: "parsing time",
+ },
+ {
+ name: "Unknown curve",
+ args: []string{
+ "cert-test",
+ "--host", "localhost",
+ "--ecdsa-curve", "invalid-curve",
+ },
+ errMsg: "unrecognized elliptic curve",
+ },
+ {
+ name: "Key generation failure",
+ args: []string{
+ "cert-test",
+ "--host", "localhost",
+ "--rsa-bits", "invalid-bits",
+ },
+ },
+ {
+ name: "Missing parameters",
+ args: []string{
+ "cert-test",
+ },
+ errMsg: `"host" not set`,
+ },
+ }
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ app := cmdCert()
+ tempDir := t.TempDir()
+
+ certFile := filepath.Join(tempDir, "cert.pem")
+ keyFile := filepath.Join(tempDir, "key.pem")
+ err := app.Run(t.Context(), append(c.args, "--out", certFile, "--keyout", keyFile))
+ require.Error(t, err)
+ if c.errMsg != "" {
+ assert.ErrorContains(t, err, c.errMsg)
+ }
+ assert.NoFileExists(t, certFile)
+ assert.NoFileExists(t, keyFile)
+ })
+ }
+}
diff --git a/cmd/cmd.go b/cmd/cmd.go
index 423dce2674..5b96bcbf9a 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -18,20 +18,19 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// argsSet checks that all the required arguments are set. args is a list of
// arguments that must be set in the passed Context.
-func argsSet(c *cli.Context, args ...string) error {
+func argsSet(c *cli.Command, args ...string) error {
for _, a := range args {
if !c.IsSet(a) {
return errors.New(a + " is not set")
}
- if util.IsEmptyString(c.String(a)) {
+ if c.Value(a) == nil {
return errors.New(a + " is required")
}
}
@@ -109,7 +108,7 @@ func setupConsoleLogger(level log.Level, colorize bool, out io.Writer) {
log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer)
}
-func globalBool(c *cli.Context, name string) bool {
+func globalBool(c *cli.Command, name string) bool {
for _, ctx := range c.Lineage() {
if ctx.Bool(name) {
return true
@@ -120,8 +119,8 @@ func globalBool(c *cli.Context, name string) bool {
// PrepareConsoleLoggerLevel by default, use INFO level for console logger, but some sub-commands (for git/ssh protocol) shouldn't output any log to stdout.
// Any log appears in git stdout pipe will break the git protocol, eg: client can't push and hangs forever.
-func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(*cli.Context) error {
- return func(c *cli.Context) error {
+func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cli.Command) (context.Context, error) {
+ return func(ctx context.Context, c *cli.Command) (context.Context, error) {
level := defaultLevel
if globalBool(c, "quiet") {
level = log.FATAL
@@ -130,6 +129,16 @@ func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(*cli.Context) error
level = log.TRACE
}
log.SetConsoleLogger(log.DEFAULT, "console-default", level)
- return nil
+ return ctx, nil
}
}
+
+func isValidDefaultSubCommand(cmd *cli.Command) (string, bool) {
+ // Dirty patch for urfave/cli's strange design.
+ // "./gitea bad-cmd" should not start the web server.
+ rootArgs := cmd.Root().Args().Slice()
+ if len(rootArgs) != 0 && rootArgs[0] != cmd.Name {
+ return rootArgs[0], false
+ }
+ return "", true
+}
diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go
new file mode 100644
index 0000000000..a36d05c76e
--- /dev/null
+++ b/cmd/cmd_test.go
@@ -0,0 +1,38 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/urfave/cli/v3"
+)
+
+func TestDefaultCommand(t *testing.T) {
+ test := func(t *testing.T, args []string, expectedRetName string, expectedRetValid bool) {
+ called := false
+ cmd := &cli.Command{
+ DefaultCommand: "test",
+ Commands: []*cli.Command{
+ {
+ Name: "test",
+ Action: func(ctx context.Context, command *cli.Command) error {
+ retName, retValid := isValidDefaultSubCommand(command)
+ assert.Equal(t, expectedRetName, retName)
+ assert.Equal(t, expectedRetValid, retValid)
+ called = true
+ return nil
+ },
+ },
+ },
+ }
+ assert.NoError(t, cmd.Run(t.Context(), args))
+ assert.True(t, called)
+ }
+ test(t, []string{"./gitea"}, "", true)
+ test(t, []string{"./gitea", "test"}, "", true)
+ test(t, []string{"./gitea", "other"}, "other", false)
+}
diff --git a/cmd/docs.go b/cmd/docs.go
index 605d02e3ef..098c0e9a8a 100644
--- a/cmd/docs.go
+++ b/cmd/docs.go
@@ -4,11 +4,13 @@
package cmd
import (
+ "context"
"fmt"
"os"
"strings"
- "github.com/urfave/cli/v2"
+ cli_docs "github.com/urfave/cli-docs/v3"
+ "github.com/urfave/cli/v3"
)
// CmdDocs represents the available docs sub-command.
@@ -30,16 +32,16 @@ var CmdDocs = &cli.Command{
},
}
-func runDocs(ctx *cli.Context) error {
- docs, err := ctx.App.ToMarkdown()
- if ctx.Bool("man") {
- docs, err = ctx.App.ToMan()
+func runDocs(_ context.Context, cmd *cli.Command) error {
+ docs, err := cli_docs.ToMarkdown(cmd.Root())
+ if cmd.Bool("man") {
+ docs, err = cli_docs.ToMan(cmd.Root())
}
if err != nil {
return err
}
- if !ctx.Bool("man") {
+ if !cmd.Bool("man") {
// Clean up markdown. The following bug was fixed in v2, but is present in v1.
// It affects markdown output (even though the issue is referring to man pages)
// https://github.com/urfave/cli/issues/1040
@@ -51,8 +53,8 @@ func runDocs(ctx *cli.Context) error {
}
out := os.Stdout
- if ctx.String("output") != "" {
- fi, err := os.Create(ctx.String("output"))
+ if cmd.String("output") != "" {
+ fi, err := os.Create(cmd.String("output"))
if err != nil {
return err
}
diff --git a/cmd/doctor.go b/cmd/doctor.go
index e433f4adc5..9e0fcbf877 100644
--- a/cmd/doctor.go
+++ b/cmd/doctor.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"fmt"
golog "log"
"os"
@@ -19,7 +20,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/doctor"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
"xorm.io/xorm"
)
@@ -29,7 +30,7 @@ var CmdDoctor = &cli.Command{
Usage: "Diagnose and optionally fix problems, convert or re-create database tables",
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
cmdDoctorCheck,
cmdRecreateTable,
cmdDoctorConvert,
@@ -92,16 +93,13 @@ You should back-up your database before doing this and ensure that your database
Action: runRecreateTable,
}
-func runRecreateTable(ctx *cli.Context) error {
- stdCtx, cancel := installSignals()
- defer cancel()
-
+func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
// Redirect the default golog to here
golog.SetFlags(0)
golog.SetPrefix("")
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
- debug := ctx.Bool("debug")
+ debug := cmd.Bool("debug")
setting.MustInstalled()
setting.LoadDBSetting()
@@ -112,15 +110,15 @@ func runRecreateTable(ctx *cli.Context) error {
}
setting.Database.LogSQL = debug
- if err := db.InitEngine(stdCtx); err != nil {
+ if err := db.InitEngine(ctx); err != nil {
fmt.Println(err)
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
return nil
}
- args := ctx.Args()
- names := make([]string, 0, ctx.NArg())
- for i := 0; i < ctx.NArg(); i++ {
+ args := cmd.Args()
+ names := make([]string, 0, cmd.NArg())
+ for i := 0; i < cmd.NArg(); i++ {
names = append(names, args.Get(i))
}
@@ -130,24 +128,25 @@ func runRecreateTable(ctx *cli.Context) error {
}
recreateTables := migrate_base.RecreateTables(beans...)
- return db.InitEngineWithMigration(stdCtx, func(x *xorm.Engine) error {
- if err := migrations.EnsureUpToDate(x); err != nil {
+ return db.InitEngineWithMigration(ctx, func(ctx context.Context, x *xorm.Engine) error {
+ if err := migrations.EnsureUpToDate(ctx, x); err != nil {
return err
}
return recreateTables(x)
})
}
-func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) {
+func setupDoctorDefaultLogger(cmd *cli.Command, colorize bool) {
// Silence the default loggers
setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr)
- logFile := ctx.String("log-file")
- if logFile == "" {
+ logFile := cmd.String("log-file")
+ switch logFile {
+ case "":
return // if no doctor log-file is set, do not show any log from default logger
- } else if logFile == "-" {
+ case "-":
setupConsoleLogger(log.TRACE, colorize, os.Stdout)
- } else {
+ default:
logFile, _ = filepath.Abs(logFile)
writeMode := log.WriterMode{Level: log.TRACE, WriterOption: log.WriterFileOption{FileName: logFile}}
writer, err := log.NewEventWriter("console-to-file", "file", writeMode)
@@ -159,23 +158,20 @@ func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) {
}
}
-func runDoctorCheck(ctx *cli.Context) error {
- stdCtx, cancel := installSignals()
- defer cancel()
-
+func runDoctorCheck(ctx context.Context, cmd *cli.Command) error {
colorize := log.CanColorStdout
- if ctx.IsSet("color") {
- colorize = ctx.Bool("color")
+ if cmd.IsSet("color") {
+ colorize = cmd.Bool("color")
}
- setupDoctorDefaultLogger(ctx, colorize)
+ setupDoctorDefaultLogger(cmd, colorize)
// Finally redirect the default golang's log to here
golog.SetFlags(0)
golog.SetPrefix("")
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
- if ctx.IsSet("list") {
+ if cmd.IsSet("list") {
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
doctor.SortChecks(doctor.Checks)
@@ -193,12 +189,12 @@ func runDoctorCheck(ctx *cli.Context) error {
}
var checks []*doctor.Check
- if ctx.Bool("all") {
+ if cmd.Bool("all") {
checks = make([]*doctor.Check, len(doctor.Checks))
copy(checks, doctor.Checks)
- } else if ctx.IsSet("run") {
- addDefault := ctx.Bool("default")
- runNamesSet := container.SetOf(ctx.StringSlice("run")...)
+ } else if cmd.IsSet("run") {
+ addDefault := cmd.Bool("default")
+ runNamesSet := container.SetOf(cmd.StringSlice("run")...)
for _, check := range doctor.Checks {
if (addDefault && check.IsDefault) || runNamesSet.Contains(check.Name) {
checks = append(checks, check)
@@ -215,5 +211,5 @@ func runDoctorCheck(ctx *cli.Context) error {
}
}
}
- return doctor.RunChecks(stdCtx, colorize, ctx.Bool("fix"), checks)
+ return doctor.RunChecks(ctx, colorize, cmd.Bool("fix"), checks)
}
diff --git a/cmd/doctor_convert.go b/cmd/doctor_convert.go
index 48c835ad0e..8cb718d383 100644
--- a/cmd/doctor_convert.go
+++ b/cmd/doctor_convert.go
@@ -4,13 +4,14 @@
package cmd
import (
+ "context"
"fmt"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// cmdDoctorConvert represents the available convert sub-command.
@@ -21,11 +22,8 @@ var cmdDoctorConvert = &cli.Command{
Action: runDoctorConvert,
}
-func runDoctorConvert(ctx *cli.Context) error {
- stdCtx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(stdCtx); err != nil {
+func runDoctorConvert(ctx context.Context, cmd *cli.Command) error {
+ if err := initDB(ctx); err != nil {
return err
}
diff --git a/cmd/doctor_test.go b/cmd/doctor_test.go
index 3e1ff299c5..da942b38b6 100644
--- a/cmd/doctor_test.go
+++ b/cmd/doctor_test.go
@@ -11,7 +11,7 @@ import (
"code.gitea.io/gitea/services/doctor"
"github.com/stretchr/testify/assert"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
func TestDoctorRun(t *testing.T) {
@@ -22,12 +22,13 @@ func TestDoctorRun(t *testing.T) {
SkipDatabaseInitialization: true,
})
- app := cli.NewApp()
- app.Commands = []*cli.Command{cmdDoctorCheck}
- err := app.Run([]string{"./gitea", "check", "--run", "test-check"})
+ app := &cli.Command{
+ Commands: []*cli.Command{cmdDoctorCheck},
+ }
+ err := app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check"})
assert.NoError(t, err)
- err = app.Run([]string{"./gitea", "check", "--run", "no-such"})
+ err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "no-such"})
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
- err = app.Run([]string{"./gitea", "check", "--run", "test-check,no-such"})
+ err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check,no-such"})
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
}
diff --git a/cmd/dump.go b/cmd/dump.go
index ececc80f72..ed19e3d4bf 100644
--- a/cmd/dump.go
+++ b/cmd/dump.go
@@ -5,7 +5,7 @@
package cmd
import (
- "fmt"
+ "context"
"os"
"path"
"path/filepath"
@@ -21,7 +21,7 @@ import (
"gitea.com/go-chi/session"
"github.com/mholt/archiver/v3"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// CmdDump represents the available dump sub-command.
@@ -93,7 +93,7 @@ var CmdDump = &cli.Command{
},
&cli.StringFlag{
Name: "type",
- Usage: fmt.Sprintf(`Dump output format, default to "zip", supported types: %s`, strings.Join(dump.SupportedOutputTypes, ", ")),
+ Usage: `Dump output format, default to "zip", supported types: ` + strings.Join(dump.SupportedOutputTypes, ", "),
},
},
}
@@ -102,17 +102,17 @@ func fatal(format string, args ...any) {
log.Fatal(format, args...)
}
-func runDump(ctx *cli.Context) error {
+func runDump(ctx context.Context, cmd *cli.Command) error {
setting.MustInstalled()
- quite := ctx.Bool("quiet")
- verbose := ctx.Bool("verbose")
+ quite := cmd.Bool("quiet")
+ verbose := cmd.Bool("verbose")
if verbose && quite {
fatal("Option --quiet and --verbose cannot both be set")
}
// outFileName is either "-" or a file name (will be made absolute)
- outFileName, outType := dump.PrepareFileNameAndType(ctx.String("file"), ctx.String("type"))
+ outFileName, outType := dump.PrepareFileNameAndType(cmd.String("file"), cmd.String("type"))
if outType == "" {
fatal("Invalid output type")
}
@@ -137,10 +137,7 @@ func runDump(ctx *cli.Context) error {
setting.DisableLoggerInit()
setting.LoadSettings() // cannot access session settings otherwise
- stdCtx, cancel := installSignals()
- defer cancel()
-
- err := db.InitEngine(stdCtx)
+ err := db.InitEngine(ctx)
if err != nil {
return err
}
@@ -166,7 +163,7 @@ func runDump(ctx *cli.Context) error {
}
dumper.GlobalExcludeAbsPath(outFileName)
- if ctx.IsSet("skip-repository") && ctx.Bool("skip-repository") {
+ if cmd.IsSet("skip-repository") && cmd.Bool("skip-repository") {
log.Info("Skip dumping local repositories")
} else {
log.Info("Dumping local repositories... %s", setting.RepoRootPath)
@@ -174,7 +171,7 @@ func runDump(ctx *cli.Context) error {
fatal("Failed to include repositories: %v", err)
}
- if ctx.IsSet("skip-lfs-data") && ctx.Bool("skip-lfs-data") {
+ if cmd.IsSet("skip-lfs-data") && cmd.Bool("skip-lfs-data") {
log.Info("Skip dumping LFS data")
} else if !setting.LFS.StartServer {
log.Info("LFS isn't enabled. Skip dumping LFS data")
@@ -189,12 +186,12 @@ func runDump(ctx *cli.Context) error {
}
}
- if ctx.Bool("skip-db") {
+ if cmd.Bool("skip-db") {
// Ensure that we don't dump the database file that may reside in setting.AppDataPath or elsewhere.
dumper.GlobalExcludeAbsPath(setting.Database.Path)
log.Info("Skipping database")
} else {
- tmpDir := ctx.String("tempdir")
+ tmpDir := cmd.String("tempdir")
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
fatal("Path does not exist: %s", tmpDir)
}
@@ -210,7 +207,7 @@ func runDump(ctx *cli.Context) error {
}
}()
- targetDBType := ctx.String("database")
+ targetDBType := cmd.String("database")
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
} else {
@@ -231,7 +228,7 @@ func runDump(ctx *cli.Context) error {
fatal("Failed to include specified app.ini: %v", err)
}
- if ctx.IsSet("skip-custom-dir") && ctx.Bool("skip-custom-dir") {
+ if cmd.IsSet("skip-custom-dir") && cmd.Bool("skip-custom-dir") {
log.Info("Skipping custom directory")
} else {
customDir, err := os.Stat(setting.CustomPath)
@@ -264,7 +261,7 @@ func runDump(ctx *cli.Context) error {
excludes = append(excludes, opts.ProviderConfig)
}
- if ctx.IsSet("skip-index") && ctx.Bool("skip-index") {
+ if cmd.IsSet("skip-index") && cmd.Bool("skip-index") {
excludes = append(excludes, setting.Indexer.RepoPath)
excludes = append(excludes, setting.Indexer.IssuePath)
}
@@ -279,7 +276,7 @@ func runDump(ctx *cli.Context) error {
}
}
- if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-attachment-data") {
+ if cmd.IsSet("skip-attachment-data") && cmd.Bool("skip-attachment-data") {
log.Info("Skip dumping attachment data")
} else if err := storage.Attachments.IterateObjects("", func(objPath string, object storage.Object) error {
info, err := object.Stat()
@@ -291,7 +288,7 @@ func runDump(ctx *cli.Context) error {
fatal("Failed to dump attachments: %v", err)
}
- if ctx.IsSet("skip-package-data") && ctx.Bool("skip-package-data") {
+ if cmd.IsSet("skip-package-data") && cmd.Bool("skip-package-data") {
log.Info("Skip dumping package data")
} else if !setting.Packages.Enabled {
log.Info("Packages isn't enabled. Skip dumping package data")
@@ -308,7 +305,7 @@ func runDump(ctx *cli.Context) error {
// Doesn't check if LogRootPath exists before processing --skip-log intentionally,
// ensuring that it's clear the dump is skipped whether the directory's initialized
// yet or not.
- if ctx.IsSet("skip-log") && ctx.Bool("skip-log") {
+ if cmd.IsSet("skip-log") && cmd.Bool("skip-log") {
log.Info("Skip dumping log files")
} else {
isExist, err := util.IsExist(setting.Log.RootPath)
diff --git a/cmd/dump_repo.go b/cmd/dump_repo.go
index 3a24cf6c5f..a75b2d1b94 100644
--- a/cmd/dump_repo.go
+++ b/cmd/dump_repo.go
@@ -19,7 +19,7 @@ import (
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/migrations"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// CmdDumpRepository represents the available dump repository sub-command.
@@ -79,11 +79,13 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
},
}
-func runDumpRepository(ctx *cli.Context) error {
- stdCtx, cancel := installSignals()
- defer cancel()
+func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
+ setupConsoleLogger(log.INFO, log.CanColorStderr, os.Stderr)
- if err := initDB(stdCtx); err != nil {
+ setting.DisableLoggerInit()
+ setting.LoadSettings() // cannot access skip_tls_verify settings otherwise
+
+ if err := initDB(ctx); err != nil {
return err
}
@@ -100,8 +102,8 @@ func runDumpRepository(ctx *cli.Context) error {
var (
serviceType structs.GitServiceType
- cloneAddr = ctx.String("clone_addr")
- serviceStr = ctx.String("git_service")
+ cloneAddr = cmd.String("clone_addr")
+ serviceStr = cmd.String("git_service")
)
if strings.HasPrefix(strings.ToLower(cloneAddr), "https://github.com/") {
@@ -119,13 +121,13 @@ func runDumpRepository(ctx *cli.Context) error {
opts := base.MigrateOptions{
GitServiceType: serviceType,
CloneAddr: cloneAddr,
- AuthUsername: ctx.String("auth_username"),
- AuthPassword: ctx.String("auth_password"),
- AuthToken: ctx.String("auth_token"),
- RepoName: ctx.String("repo_name"),
+ AuthUsername: cmd.String("auth_username"),
+ AuthPassword: cmd.String("auth_password"),
+ AuthToken: cmd.String("auth_token"),
+ RepoName: cmd.String("repo_name"),
}
- if len(ctx.String("units")) == 0 {
+ if len(cmd.String("units")) == 0 {
opts.Wiki = true
opts.Issues = true
opts.Milestones = true
@@ -135,8 +137,8 @@ func runDumpRepository(ctx *cli.Context) error {
opts.PullRequests = true
opts.ReleaseAssets = true
} else {
- units := strings.Split(ctx.String("units"), ",")
- for _, unit := range units {
+ units := strings.SplitSeq(cmd.String("units"), ",")
+ for unit := range units {
switch strings.ToLower(strings.TrimSpace(unit)) {
case "":
continue
@@ -164,7 +166,7 @@ func runDumpRepository(ctx *cli.Context) error {
// the repo_dir will be removed if error occurs in DumpRepository
// make sure the directory doesn't exist or is empty, prevent from deleting user files
- repoDir := ctx.String("repo_dir")
+ repoDir := cmd.String("repo_dir")
if exists, err := util.IsExist(repoDir); err != nil {
return fmt.Errorf("unable to stat repo_dir %q: %w", repoDir, err)
} else if exists {
@@ -179,7 +181,7 @@ func runDumpRepository(ctx *cli.Context) error {
if err := migrations.DumpRepository(
context.Background(),
repoDir,
- ctx.String("owner_name"),
+ cmd.String("owner_name"),
opts,
); err != nil {
log.Fatal("Failed to dump repository: %v", err)
diff --git a/cmd/embedded.go b/cmd/embedded.go
index 9f03f7be7c..1908352453 100644
--- a/cmd/embedded.go
+++ b/cmd/embedded.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"errors"
"fmt"
"os"
@@ -19,7 +20,7 @@ import (
"code.gitea.io/gitea/modules/util"
"github.com/gobwas/glob"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// CmdEmbedded represents the available extract sub-command.
@@ -28,7 +29,7 @@ var (
Name: "embedded",
Usage: "Extract embedded resources",
Description: "A command for extracting embedded resources, like templates and images",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
subcmdList,
subcmdView,
subcmdExtract,
@@ -100,7 +101,7 @@ type assetFile struct {
path string
}
-func initEmbeddedExtractor(c *cli.Context) error {
+func initEmbeddedExtractor(c *cli.Command) error {
setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr)
patterns, err := compileCollectPatterns(c.Args().Slice())
@@ -115,31 +116,31 @@ func initEmbeddedExtractor(c *cli.Context) error {
return nil
}
-func runList(c *cli.Context) error {
+func runList(_ context.Context, c *cli.Command) error {
if err := runListDo(c); err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
+ _, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
return err
}
return nil
}
-func runView(c *cli.Context) error {
+func runView(_ context.Context, c *cli.Command) error {
if err := runViewDo(c); err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
+ _, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
return err
}
return nil
}
-func runExtract(c *cli.Context) error {
+func runExtract(_ context.Context, c *cli.Command) error {
if err := runExtractDo(c); err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
+ _, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
return err
}
return nil
}
-func runListDo(c *cli.Context) error {
+func runListDo(c *cli.Command) error {
if err := initEmbeddedExtractor(c); err != nil {
return err
}
@@ -151,7 +152,7 @@ func runListDo(c *cli.Context) error {
return nil
}
-func runViewDo(c *cli.Context) error {
+func runViewDo(c *cli.Command) error {
if err := initEmbeddedExtractor(c); err != nil {
return err
}
@@ -174,7 +175,7 @@ func runViewDo(c *cli.Context) error {
return nil
}
-func runExtractDo(c *cli.Context) error {
+func runExtractDo(c *cli.Command) error {
if err := initEmbeddedExtractor(c); err != nil {
return err
}
@@ -216,7 +217,7 @@ func runExtractDo(c *cli.Context) error {
for _, a := range matchedAssetFiles {
if err := extractAsset(destdir, a, overwrite, rename); err != nil {
// Non-fatal error
- fmt.Fprintf(os.Stderr, "%s: %v", a.path, err)
+ _, _ = fmt.Fprintf(os.Stderr, "%s: %v\n", a.path, err)
}
}
@@ -271,7 +272,7 @@ func extractAsset(d string, a assetFile, overwrite, rename bool) error {
return nil
}
-func collectAssetFilesByPattern(c *cli.Context, globs []glob.Glob, path string, layer *assetfs.Layer) {
+func collectAssetFilesByPattern(c *cli.Command, globs []glob.Glob, path string, layer *assetfs.Layer) {
fs := assetfs.Layered(layer)
files, err := fs.ListAllFiles(".", true)
if err != nil {
@@ -294,16 +295,14 @@ func collectAssetFilesByPattern(c *cli.Context, globs []glob.Glob, path string,
}
}
-func compileCollectPatterns(args []string) ([]glob.Glob, error) {
+func compileCollectPatterns(args []string) (_ []glob.Glob, err error) {
if len(args) == 0 {
args = []string{"**"}
}
pat := make([]glob.Glob, len(args))
for i := range args {
- if g, err := glob.Compile(args[i], '/'); err != nil {
- return nil, fmt.Errorf("'%s': Invalid glob pattern: %w", args[i], err)
- } else { //nolint:revive
- pat[i] = g
+ if pat[i], err = glob.Compile(args[i], '/'); err != nil {
+ return nil, fmt.Errorf("invalid glob patterh %q: %w", args[i], err)
}
}
return pat, nil
diff --git a/cmd/generate.go b/cmd/generate.go
index 90b32ecaf0..cf491604ef 100644
--- a/cmd/generate.go
+++ b/cmd/generate.go
@@ -5,13 +5,14 @@
package cmd
import (
+ "context"
"fmt"
"os"
"code.gitea.io/gitea/modules/generate"
"github.com/mattn/go-isatty"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var (
@@ -19,7 +20,7 @@ var (
CmdGenerate = &cli.Command{
Name: "generate",
Usage: "Generate Gitea's secrets/keys/tokens",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
subcmdSecret,
},
}
@@ -27,7 +28,7 @@ var (
subcmdSecret = &cli.Command{
Name: "secret",
Usage: "Generate a secret token",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
microcmdGenerateInternalToken,
microcmdGenerateLfsJwtSecret,
microcmdGenerateSecretKey,
@@ -54,7 +55,7 @@ var (
}
)
-func runGenerateInternalToken(c *cli.Context) error {
+func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
internalToken, err := generate.NewInternalToken()
if err != nil {
return err
@@ -69,7 +70,7 @@ func runGenerateInternalToken(c *cli.Context) error {
return nil
}
-func runGenerateLfsJwtSecret(c *cli.Context) error {
+func runGenerateLfsJwtSecret(_ context.Context, c *cli.Command) error {
_, jwtSecretBase64, err := generate.NewJwtSecretWithBase64()
if err != nil {
return err
@@ -84,7 +85,7 @@ func runGenerateLfsJwtSecret(c *cli.Context) error {
return nil
}
-func runGenerateSecretKey(c *cli.Context) error {
+func runGenerateSecretKey(_ context.Context, c *cli.Command) error {
secretKey, err := generate.NewSecretKey()
if err != nil {
return err
diff --git a/cmd/hook.go b/cmd/hook.go
index 578380ab40..2ce272b411 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -20,11 +20,11 @@ import (
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
const (
- hookBatchSize = 30
+ hookBatchSize = 500
)
var (
@@ -34,7 +34,7 @@ var (
Usage: "(internal) Should only be called by Git",
Description: "Delegate commands to corresponding Git hooks",
Before: PrepareConsoleLoggerLevel(log.FATAL),
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
subcmdHookPreReceive,
subcmdHookUpdate,
subcmdHookPostReceive,
@@ -161,12 +161,10 @@ func (n *nilWriter) WriteString(s string) (int, error) {
return len(s), nil
}
-func runHookPreReceive(c *cli.Context) error {
+func runHookPreReceive(ctx context.Context, c *cli.Command) error {
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil
}
- ctx, cancel := installSignals()
- defer cancel()
setup(ctx, c.Bool("debug"))
@@ -292,7 +290,7 @@ Gitea or set your environment appropriately.`, "")
// runHookUpdate avoid to do heavy operations on update hook because it will be
// invoked for every ref update which does not like pre-receive and post-receive
-func runHookUpdate(c *cli.Context) error {
+func runHookUpdate(_ context.Context, c *cli.Command) error {
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil
}
@@ -309,15 +307,12 @@ func runHookUpdate(c *cli.Context) error {
return nil
}
-func runHookPostReceive(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runHookPostReceive(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
// First of all run update-server-info no matter what
- if _, _, err := git.NewCommand(ctx, "update-server-info").RunStdString(nil); err != nil {
- return fmt.Errorf("Failed to call 'git update-server-info': %w", err)
+ if _, _, err := git.NewCommand("update-server-info").RunStdString(ctx, nil); err != nil {
+ return fmt.Errorf("failed to call 'git update-server-info': %w", err)
}
// Now if we're an internal don't do anything else
@@ -485,7 +480,7 @@ func hookPrintResult(output, isCreate bool, branch, url string) {
func pushOptions() map[string]string {
opts := make(map[string]string)
if pushCount, err := strconv.Atoi(os.Getenv(private.GitPushOptionCount)); err == nil {
- for idx := 0; idx < pushCount; idx++ {
+ for idx := range pushCount {
opt := os.Getenv(fmt.Sprintf("GIT_PUSH_OPTION_%d", idx))
kv := strings.SplitN(opt, "=", 2)
if len(kv) == 2 {
@@ -496,10 +491,7 @@ func pushOptions() map[string]string {
return opts
}
-func runHookProcReceive(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runHookProcReceive(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
@@ -740,7 +732,7 @@ func readPktLine(ctx context.Context, in *bufio.Reader, requestType pktLineType)
// read prefix
lengthBytes := make([]byte, 4)
- for i := 0; i < 4; i++ {
+ for i := range 4 {
lengthBytes[i], err = in.ReadByte()
if err != nil {
return nil, fail(ctx, "Protocol: stdin error", "Pkt-Line: read stdin failed : %v", err)
diff --git a/cmd/hook_test.go b/cmd/hook_test.go
index 91f24ff2b4..86cd4834f2 100644
--- a/cmd/hook_test.go
+++ b/cmd/hook_test.go
@@ -6,7 +6,6 @@ package cmd
import (
"bufio"
"bytes"
- "context"
"strings"
"testing"
@@ -15,7 +14,7 @@ import (
func TestPktLine(t *testing.T) {
// test read
- ctx := context.Background()
+ ctx := t.Context()
s := strings.NewReader("0000")
r := bufio.NewReader(s)
result, err := readPktLine(ctx, r, pktLineTypeFlush)
diff --git a/cmd/keys.go b/cmd/keys.go
index 7fdbe16119..8710756a81 100644
--- a/cmd/keys.go
+++ b/cmd/keys.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"errors"
"fmt"
"strings"
@@ -11,7 +12,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// CmdKeys represents the available keys sub-command
@@ -49,7 +50,7 @@ var CmdKeys = &cli.Command{
},
}
-func runKeys(c *cli.Context) error {
+func runKeys(ctx context.Context, c *cli.Command) error {
if !c.IsSet("username") {
return errors.New("No username provided")
}
@@ -68,9 +69,6 @@ func runKeys(c *cli.Context) error {
return errors.New("No key type and content provided")
}
- ctx, cancel := installSignals()
- defer cancel()
-
setup(ctx, c.Bool("debug"))
authorizedString, extra := private.AuthorizedPublicKeyByContent(ctx, content)
@@ -78,6 +76,6 @@ func runKeys(c *cli.Context) error {
if extra.Error != nil {
return extra.Error
}
- _, _ = fmt.Fprintln(c.App.Writer, strings.TrimSpace(authorizedString.Text))
+ _, _ = fmt.Fprintln(c.Root().Writer, strings.TrimSpace(authorizedString.Text))
return nil
}
diff --git a/cmd/mailer.go b/cmd/mailer.go
index 0c5f2c8c8d..72bd8e5601 100644
--- a/cmd/mailer.go
+++ b/cmd/mailer.go
@@ -4,24 +4,18 @@
package cmd
import (
+ "context"
"fmt"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
-func runSendMail(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runSendMail(ctx context.Context, c *cli.Command) error {
setting.MustInstalled()
- if err := argsSet(c, "title"); err != nil {
- return err
- }
-
subject := c.String("title")
confirmSkiped := c.Bool("force")
body := c.String("content")
diff --git a/cmd/main.go b/cmd/main.go
index 7251bd09a3..3b8a8a9311 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"fmt"
"os"
"strings"
@@ -11,7 +12,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// cmdHelp is our own help subcommand with more information
@@ -22,18 +23,18 @@ func cmdHelp() *cli.Command {
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
- Action: func(c *cli.Context) (err error) {
- lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea, {Command:nil}
+ Action: func(ctx context.Context, c *cli.Command) (err error) {
+ lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea
targetCmdIdx := 0
- if c.Command.Name == "help" {
+ if c.Name == "help" {
targetCmdIdx = 1
}
- if lineage[targetCmdIdx+1].Command != nil {
- err = cli.ShowCommandHelp(lineage[targetCmdIdx+1], lineage[targetCmdIdx].Command.Name)
+ if lineage[targetCmdIdx] != lineage[targetCmdIdx].Root() {
+ err = cli.ShowCommandHelp(ctx, lineage[targetCmdIdx+1] /* parent cmd */, lineage[targetCmdIdx].Name /* sub cmd */)
} else {
err = cli.ShowAppHelp(c)
}
- _, _ = fmt.Fprintf(c.App.Writer, `
+ _, _ = fmt.Fprintf(c.Root().Writer, `
DEFAULT CONFIGURATION:
AppPath: %s
WorkPath: %s
@@ -74,25 +75,25 @@ func appGlobalFlags() []cli.Flag {
}
}
-func prepareSubcommandWithConfig(command *cli.Command, globalFlags []cli.Flag) {
- command.Flags = append(append([]cli.Flag{}, globalFlags...), command.Flags...)
+func prepareSubcommandWithGlobalFlags(command *cli.Command) {
+ command.Flags = append(append([]cli.Flag{}, appGlobalFlags()...), command.Flags...)
command.Action = prepareWorkPathAndCustomConf(command.Action)
command.HideHelp = true
if command.Name != "help" {
- command.Subcommands = append(command.Subcommands, cmdHelp())
+ command.Commands = append(command.Commands, cmdHelp())
}
- for i := range command.Subcommands {
- prepareSubcommandWithConfig(command.Subcommands[i], globalFlags)
+ for i := range command.Commands {
+ prepareSubcommandWithGlobalFlags(command.Commands[i])
}
}
// prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config
// It can't use "Before", because each level's sub-command's Before will be called one by one, so the "init" would be done multiple times
-func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context) error {
- return func(ctx *cli.Context) error {
+func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(context.Context, *cli.Command) error {
+ return func(ctx context.Context, cmd *cli.Command) error {
var args setting.ArgWorkPathAndCustomConf
// from children to parent, check the global flags
- for _, curCtx := range ctx.Lineage() {
+ for _, curCtx := range cmd.Lineage() {
if curCtx.IsSet("work-path") && args.WorkPath == "" {
args.WorkPath = curCtx.String("work-path")
}
@@ -104,11 +105,11 @@ func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context)
}
}
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
- if ctx.Bool("help") || action == nil {
+ if cmd.Bool("help") || action == nil {
// the default behavior of "urfave/cli": "nil action" means "show help"
- return cmdHelp().Action(ctx)
+ return cmdHelp().Action(ctx, cmd)
}
- return action(ctx)
+ return action(ctx, cmd)
}
}
@@ -117,14 +118,13 @@ type AppVersion struct {
Extra string
}
-func NewMainApp(appVer AppVersion) *cli.App {
- app := cli.NewApp()
- app.Name = "Gitea"
- app.HelpName = "gitea"
+func NewMainApp(appVer AppVersion) *cli.Command {
+ app := &cli.Command{}
+ app.Name = "gitea" // must be lower-cased because it appears in the "USAGE" section like "gitea doctor [command [command options]]"
app.Usage = "A painless self-hosted Git service"
app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.`
app.Version = appVer.Version + appVer.Extra
- app.EnableBashCompletion = true
+ app.EnableShellCompletion = true
// these sub-commands need to use config file
subCmdWithConfig := []*cli.Command{
@@ -147,20 +147,21 @@ func NewMainApp(appVer AppVersion) *cli.App {
// these sub-commands do not need the config file, and they do not depend on any path or environment variable.
subCmdStandalone := []*cli.Command{
- CmdCert,
+ cmdCert(),
CmdGenerate,
CmdDocs,
}
+ // TODO: we should eventually drop the default command,
+ // but not sure whether it would break Windows users who used to double-click the EXE to run.
app.DefaultCommand = CmdWeb.Name
- globalFlags := appGlobalFlags()
app.Flags = append(app.Flags, cli.VersionFlag)
- app.Flags = append(app.Flags, globalFlags...)
+ app.Flags = append(app.Flags, appGlobalFlags()...)
app.HideHelp = true // use our own help action to show helps (with more information like default config)
app.Before = PrepareConsoleLoggerLevel(log.INFO)
for i := range subCmdWithConfig {
- prepareSubcommandWithConfig(subCmdWithConfig[i], globalFlags)
+ prepareSubcommandWithGlobalFlags(subCmdWithConfig[i])
}
app.Commands = append(app.Commands, subCmdWithConfig...)
app.Commands = append(app.Commands, subCmdStandalone...)
@@ -169,8 +170,10 @@ func NewMainApp(appVer AppVersion) *cli.App {
return app
}
-func RunMainApp(app *cli.App, args ...string) error {
- err := app.Run(args)
+func RunMainApp(app *cli.Command, args ...string) error {
+ ctx, cancel := installSignals()
+ defer cancel()
+ err := app.Run(ctx, args)
if err == nil {
return nil
}
diff --git a/cmd/main_test.go b/cmd/main_test.go
index 3ec584d323..7dfa87a0ef 100644
--- a/cmd/main_test.go
+++ b/cmd/main_test.go
@@ -4,6 +4,8 @@
package cmd
import (
+ "context"
+ "errors"
"fmt"
"io"
"path/filepath"
@@ -15,7 +17,7 @@ import (
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
func TestMain(m *testing.M) {
@@ -26,10 +28,10 @@ func makePathOutput(workPath, customPath, customConf string) string {
return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf)
}
-func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App {
+func newTestApp(testCmdAction cli.ActionFunc) *cli.Command {
app := NewMainApp(AppVersion{})
testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction}
- prepareSubcommandWithConfig(testCmd, appGlobalFlags())
+ prepareSubcommandWithGlobalFlags(testCmd)
app.Commands = append(app.Commands, testCmd)
app.DefaultCommand = testCmd.Name
return app
@@ -41,7 +43,7 @@ type runResult struct {
ExitCode int
}
-func runTestApp(app *cli.App, args ...string) (runResult, error) {
+func runTestApp(app *cli.Command, args ...string) (runResult, error) {
outBuf := new(strings.Builder)
errBuf := new(strings.Builder)
app.Writer = outBuf
@@ -64,7 +66,7 @@ func TestCliCmd(t *testing.T) {
defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini")
cli.CommandHelpTemplate = "(command help template)"
- cli.AppHelpTemplate = "(app help template)"
+ cli.RootCommandHelpTemplate = "(app help template)"
cli.SubcommandHelpTemplate = "(subcommand help template)"
cases := []struct {
@@ -108,12 +110,12 @@ func TestCliCmd(t *testing.T) {
},
}
- app := newTestApp(func(ctx *cli.Context) error {
- _, _ = fmt.Fprint(ctx.App.Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
- return nil
- })
for _, c := range cases {
t.Run(c.cmd, func(t *testing.T) {
+ app := newTestApp(func(ctx context.Context, cmd *cli.Command) error {
+ _, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
+ return nil
+ })
for k, v := range c.env {
t.Setenv(k, v)
}
@@ -127,31 +129,31 @@ func TestCliCmd(t *testing.T) {
}
func TestCliCmdError(t *testing.T) {
- app := newTestApp(func(ctx *cli.Context) error { return fmt.Errorf("normal error") })
+ app := newTestApp(func(ctx context.Context, cmd *cli.Command) error { return errors.New("normal error") })
r, err := runTestApp(app, "./gitea", "test-cmd")
assert.Error(t, err)
assert.Equal(t, 1, r.ExitCode)
- assert.Equal(t, "", r.Stdout)
+ assert.Empty(t, r.Stdout)
assert.Equal(t, "Command error: normal error\n", r.Stderr)
- app = newTestApp(func(ctx *cli.Context) error { return cli.Exit("exit error", 2) })
+ app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return cli.Exit("exit error", 2) })
r, err = runTestApp(app, "./gitea", "test-cmd")
assert.Error(t, err)
assert.Equal(t, 2, r.ExitCode)
- assert.Equal(t, "", r.Stdout)
+ assert.Empty(t, r.Stdout)
assert.Equal(t, "exit error\n", r.Stderr)
- app = newTestApp(func(ctx *cli.Context) error { return nil })
+ app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return nil })
r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such")
assert.Error(t, err)
assert.Equal(t, 1, r.ExitCode)
- assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stdout)
- assert.Equal(t, "", r.Stderr) // the cli package's strange behavior, the error message is not in stderr ....
+ assert.Empty(t, r.Stdout)
+ assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr)
- app = newTestApp(func(ctx *cli.Context) error { return nil })
+ app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return nil })
r, err = runTestApp(app, "./gitea", "test-cmd")
assert.NoError(t, err)
assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called
- assert.Equal(t, "", r.Stdout)
- assert.Equal(t, "", r.Stderr)
+ assert.Empty(t, r.Stdout)
+ assert.Empty(t, r.Stderr)
}
diff --git a/cmd/manager.go b/cmd/manager.go
index bd2da8edc7..f0935ea065 100644
--- a/cmd/manager.go
+++ b/cmd/manager.go
@@ -4,12 +4,13 @@
package cmd
import (
+ "context"
"os"
"time"
"code.gitea.io/gitea/modules/private"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var (
@@ -18,7 +19,7 @@ var (
Name: "manager",
Usage: "Manage the running gitea process",
Description: "This is a command for managing the running gitea process",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
subcmdShutdown,
subcmdRestart,
subcmdReloadTemplates,
@@ -108,46 +109,31 @@ var (
}
)
-func runShutdown(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runShutdown(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
extra := private.Shutdown(ctx)
return handleCliResponseExtra(extra)
}
-func runRestart(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runRestart(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
extra := private.Restart(ctx)
return handleCliResponseExtra(extra)
}
-func runReloadTemplates(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runReloadTemplates(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
extra := private.ReloadTemplates(ctx)
return handleCliResponseExtra(extra)
}
-func runFlushQueues(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runFlushQueues(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
extra := private.FlushQueues(ctx, c.Duration("timeout"), c.Bool("non-blocking"))
return handleCliResponseExtra(extra)
}
-func runProcesses(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runProcesses(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
extra := private.Processes(ctx, os.Stdout, c.Bool("flat"), c.Bool("no-system"), c.Bool("stacktraces"), c.Bool("json"), c.String("cancel"))
return handleCliResponseExtra(extra)
diff --git a/cmd/manager_logging.go b/cmd/manager_logging.go
index c2ae25ec57..ac29e7d3e5 100644
--- a/cmd/manager_logging.go
+++ b/cmd/manager_logging.go
@@ -4,6 +4,7 @@
package cmd
import (
+ "context"
"errors"
"fmt"
"os"
@@ -11,7 +12,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
var (
@@ -60,7 +61,7 @@ var (
subcmdLogging = &cli.Command{
Name: "logging",
Usage: "Adjust logging commands",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
{
Name: "pause",
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
@@ -104,7 +105,7 @@ var (
}, {
Name: "add",
Usage: "Add a logger",
- Subcommands: []*cli.Command{
+ Commands: []*cli.Command{
{
Name: "file",
Usage: "Add a file logger",
@@ -118,7 +119,6 @@ var (
Name: "rotate",
Aliases: []string{"r"},
Usage: "Rotate logs",
- Value: true,
},
&cli.Int64Flag{
Name: "max-size",
@@ -129,7 +129,6 @@ var (
Name: "daily",
Aliases: []string{"d"},
Usage: "Rotate logs daily",
- Value: true,
},
&cli.IntFlag{
Name: "max-days",
@@ -140,7 +139,6 @@ var (
Name: "compress",
Aliases: []string{"z"},
Usage: "Compress rotated logs",
- Value: true,
},
&cli.IntFlag{
Name: "compression-level",
@@ -195,10 +193,7 @@ var (
}
)
-func runRemoveLogger(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runRemoveLogger(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
logger := c.String("logger")
if len(logger) == 0 {
@@ -210,10 +205,7 @@ func runRemoveLogger(c *cli.Context) error {
return handleCliResponseExtra(extra)
}
-func runAddConnLogger(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runAddConnLogger(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
vals := map[string]any{}
mode := "conn"
@@ -237,13 +229,10 @@ func runAddConnLogger(c *cli.Context) error {
if c.IsSet("reconnect-on-message") {
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
}
- return commonAddLogger(c, mode, vals)
+ return commonAddLogger(ctx, c, mode, vals)
}
-func runAddFileLogger(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runAddFileLogger(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
vals := map[string]any{}
mode := "file"
@@ -270,10 +259,10 @@ func runAddFileLogger(c *cli.Context) error {
if c.IsSet("compression-level") {
vals["compressionLevel"] = c.Int("compression-level")
}
- return commonAddLogger(c, mode, vals)
+ return commonAddLogger(ctx, c, mode, vals)
}
-func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error {
+func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[string]any) error {
if len(c.String("level")) > 0 {
vals["level"] = log.LevelFromString(c.String("level")).String()
}
@@ -300,46 +289,33 @@ func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error {
if c.IsSet("writer") {
writer = c.String("writer")
}
- ctx, cancel := installSignals()
- defer cancel()
extra := private.AddLogger(ctx, logger, writer, mode, vals)
return handleCliResponseExtra(extra)
}
-func runPauseLogging(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runPauseLogging(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
userMsg := private.PauseLogging(ctx)
_, _ = fmt.Fprintln(os.Stdout, userMsg)
return nil
}
-func runResumeLogging(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runResumeLogging(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
userMsg := private.ResumeLogging(ctx)
_, _ = fmt.Fprintln(os.Stdout, userMsg)
return nil
}
-func runReleaseReopenLogging(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runReleaseReopenLogging(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
userMsg := private.ReleaseReopenLogging(ctx)
_, _ = fmt.Fprintln(os.Stdout, userMsg)
return nil
}
-func runSetLogSQL(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
+func runSetLogSQL(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
extra := private.SetLogSQL(ctx, !c.Bool("off"))
diff --git a/cmd/migrate.go b/cmd/migrate.go
index 459805a76d..e24dc9e572 100644
--- a/cmd/migrate.go
+++ b/cmd/migrate.go
@@ -7,11 +7,11 @@ import (
"context"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/migrations"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/versioned_migration"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// CmdMigrate represents the available migrate sub-command.
@@ -22,11 +22,8 @@ var CmdMigrate = &cli.Command{
Action: runMigrate,
}
-func runMigrate(ctx *cli.Context) error {
- stdCtx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(stdCtx); err != nil {
+func runMigrate(ctx context.Context, c *cli.Command) error {
+ if err := initDB(ctx); err != nil {
return err
}
@@ -36,7 +33,7 @@ func runMigrate(ctx *cli.Context) error {
log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf)
- if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
+ if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil {
log.Fatal("Failed to initialize ORM engine: %v", err)
return err
}
diff --git a/cmd/migrate_storage.go b/cmd/migrate_storage.go
index 6ece4bf661..2c63e15f50 100644
--- a/cmd/migrate_storage.go
+++ b/cmd/migrate_storage.go
@@ -13,7 +13,6 @@ import (
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/migrations"
packages_model "code.gitea.io/gitea/models/packages"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
@@ -21,8 +20,9 @@ import (
packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
+ "code.gitea.io/gitea/services/versioned_migration"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// CmdMigrateStorage represents the available migrate storage sub-command.
@@ -196,7 +196,7 @@ func migrateActionsLog(ctx context.Context, dstStorage storage.ObjectStorage) er
func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStorage) error {
return db.Iterate(ctx, nil, func(ctx context.Context, artifact *actions_model.ActionArtifact) error {
- if artifact.Status == int64(actions_model.ArtifactStatusExpired) {
+ if artifact.Status == actions_model.ArtifactStatusExpired {
return nil
}
@@ -213,11 +213,8 @@ func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStora
})
}
-func runMigrateStorage(ctx *cli.Context) error {
- stdCtx, cancel := installSignals()
- defer cancel()
-
- if err := initDB(stdCtx); err != nil {
+func runMigrateStorage(ctx context.Context, cmd *cli.Command) error {
+ if err := initDB(ctx); err != nil {
return err
}
@@ -227,7 +224,7 @@ func runMigrateStorage(ctx *cli.Context) error {
log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf)
- if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
+ if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil {
log.Fatal("Failed to initialize ORM engine: %v", err)
return err
}
@@ -238,51 +235,51 @@ func runMigrateStorage(ctx *cli.Context) error {
var dstStorage storage.ObjectStorage
var err error
- switch strings.ToLower(ctx.String("storage")) {
+ switch strings.ToLower(cmd.String("storage")) {
case "":
fallthrough
case string(setting.LocalStorageType):
- p := ctx.String("path")
+ p := cmd.String("path")
if p == "" {
log.Fatal("Path must be given when storage is local")
return nil
}
dstStorage, err = storage.NewLocalStorage(
- stdCtx,
+ ctx,
&setting.Storage{
Path: p,
})
case string(setting.MinioStorageType):
dstStorage, err = storage.NewMinioStorage(
- stdCtx,
+ ctx,
&setting.Storage{
MinioConfig: setting.MinioStorageConfig{
- Endpoint: ctx.String("minio-endpoint"),
- AccessKeyID: ctx.String("minio-access-key-id"),
- SecretAccessKey: ctx.String("minio-secret-access-key"),
- Bucket: ctx.String("minio-bucket"),
- Location: ctx.String("minio-location"),
- BasePath: ctx.String("minio-base-path"),
- UseSSL: ctx.Bool("minio-use-ssl"),
- InsecureSkipVerify: ctx.Bool("minio-insecure-skip-verify"),
- ChecksumAlgorithm: ctx.String("minio-checksum-algorithm"),
- BucketLookUpType: ctx.String("minio-bucket-lookup-type"),
+ Endpoint: cmd.String("minio-endpoint"),
+ AccessKeyID: cmd.String("minio-access-key-id"),
+ SecretAccessKey: cmd.String("minio-secret-access-key"),
+ Bucket: cmd.String("minio-bucket"),
+ Location: cmd.String("minio-location"),
+ BasePath: cmd.String("minio-base-path"),
+ UseSSL: cmd.Bool("minio-use-ssl"),
+ InsecureSkipVerify: cmd.Bool("minio-insecure-skip-verify"),
+ ChecksumAlgorithm: cmd.String("minio-checksum-algorithm"),
+ BucketLookUpType: cmd.String("minio-bucket-lookup-type"),
},
})
case string(setting.AzureBlobStorageType):
dstStorage, err = storage.NewAzureBlobStorage(
- stdCtx,
+ ctx,
&setting.Storage{
AzureBlobConfig: setting.AzureBlobStorageConfig{
- Endpoint: ctx.String("azureblob-endpoint"),
- AccountName: ctx.String("azureblob-account-name"),
- AccountKey: ctx.String("azureblob-account-key"),
- Container: ctx.String("azureblob-container"),
- BasePath: ctx.String("azureblob-base-path"),
+ Endpoint: cmd.String("azureblob-endpoint"),
+ AccountName: cmd.String("azureblob-account-name"),
+ AccountKey: cmd.String("azureblob-account-key"),
+ Container: cmd.String("azureblob-container"),
+ BasePath: cmd.String("azureblob-base-path"),
},
})
default:
- return fmt.Errorf("unsupported storage type: %s", ctx.String("storage"))
+ return fmt.Errorf("unsupported storage type: %s", cmd.String("storage"))
}
if err != nil {
return err
@@ -299,14 +296,14 @@ func runMigrateStorage(ctx *cli.Context) error {
"actions-artifacts": migrateActionsArtifacts,
}
- tp := strings.ToLower(ctx.String("type"))
+ tp := strings.ToLower(cmd.String("type"))
if m, ok := migratedMethods[tp]; ok {
- if err := m(stdCtx, dstStorage); err != nil {
+ if err := m(ctx, dstStorage); err != nil {
return err
}
log.Info("%s files have successfully been copied to the new storage.", tp)
return nil
}
- return fmt.Errorf("unsupported storage: %s", ctx.String("type"))
+ return fmt.Errorf("unsupported storage: %s", cmd.String("type"))
}
diff --git a/cmd/migrate_storage_test.go b/cmd/migrate_storage_test.go
index 5d8c867993..6817867e28 100644
--- a/cmd/migrate_storage_test.go
+++ b/cmd/migrate_storage_test.go
@@ -4,7 +4,6 @@
package cmd
import (
- "context"
"os"
"strings"
"testing"
@@ -53,7 +52,7 @@ func TestMigratePackages(t *testing.T) {
assert.NotNil(t, v)
assert.NotNil(t, f)
- ctx := context.Background()
+ ctx := t.Context()
p := t.TempDir()
@@ -70,6 +69,6 @@ func TestMigratePackages(t *testing.T) {
entries, err := os.ReadDir(p)
assert.NoError(t, err)
assert.Len(t, entries, 2)
- assert.EqualValues(t, "01", entries[0].Name())
- assert.EqualValues(t, "tmp", entries[1].Name())
+ assert.Equal(t, "01", entries[0].Name())
+ assert.Equal(t, "tmp", entries[1].Name())
}
diff --git a/cmd/restore_repo.go b/cmd/restore_repo.go
index 37b32aa304..c61f5a582e 100644
--- a/cmd/restore_repo.go
+++ b/cmd/restore_repo.go
@@ -4,12 +4,13 @@
package cmd
import (
+ "context"
"strings"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// CmdRestoreRepository represents the available restore a repository sub-command.
@@ -48,10 +49,7 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
},
}
-func runRestoreRepository(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runRestoreRepository(ctx context.Context, c *cli.Command) error {
setting.MustInstalled()
var units []string
if s := c.String("units"); s != "" {
diff --git a/cmd/serv.go b/cmd/serv.go
index d2271b68d2..8c6001e727 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -11,7 +11,6 @@ import (
"os"
"os/exec"
"path/filepath"
- "regexp"
"strconv"
"strings"
"time"
@@ -20,7 +19,7 @@ import (
asymkey_model "code.gitea.io/gitea/models/asymkey"
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/perm"
- "code.gitea.io/gitea/modules/container"
+ "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/lfstransfer"
@@ -34,15 +33,7 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/kballard/go-shellquote"
- "github.com/urfave/cli/v2"
-)
-
-const (
- verbUploadPack = "git-upload-pack"
- verbUploadArchive = "git-upload-archive"
- verbReceivePack = "git-receive-pack"
- verbLfsAuthenticate = "git-lfs-authenticate"
- verbLfsTransfer = "git-lfs-transfer"
+ "github.com/urfave/cli/v3"
)
// CmdServ represents the available serv sub-command.
@@ -78,22 +69,6 @@ func setup(ctx context.Context, debug bool) {
}
}
-var (
- // keep getAccessMode() in sync
- allowedCommands = container.SetOf(
- verbUploadPack,
- verbUploadArchive,
- verbReceivePack,
- verbLfsAuthenticate,
- verbLfsTransfer,
- )
- allowedCommandsLfs = container.SetOf(
- verbLfsAuthenticate,
- verbLfsTransfer,
- )
- alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
-)
-
// fail prints message to stdout, it's mainly used for git serv and git hook commands.
// The output will be passed to git client and shown to user.
func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error {
@@ -104,7 +79,10 @@ func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error
// There appears to be a chance to cause a zombie process and failure to read the Exit status
// if nothing is outputted on stdout.
_, _ = fmt.Fprintln(os.Stdout, "")
- _, _ = fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
+ // add extra empty lines to separate our message from other git errors to get more attention
+ _, _ = fmt.Fprintln(os.Stderr, "error:")
+ _, _ = fmt.Fprintln(os.Stderr, "error:", userMessage)
+ _, _ = fmt.Fprintln(os.Stderr, "error:")
if logMsgFmt != "" {
logMsg := fmt.Sprintf(logMsgFmt, args...)
@@ -136,19 +114,20 @@ func handleCliResponseExtra(extra private.ResponseExtra) error {
func getAccessMode(verb, lfsVerb string) perm.AccessMode {
switch verb {
- case verbUploadPack, verbUploadArchive:
+ case git.CmdVerbUploadPack, git.CmdVerbUploadArchive:
return perm.AccessModeRead
- case verbReceivePack:
+ case git.CmdVerbReceivePack:
return perm.AccessModeWrite
- case verbLfsAuthenticate, verbLfsTransfer:
+ case git.CmdVerbLfsAuthenticate, git.CmdVerbLfsTransfer:
switch lfsVerb {
- case "upload":
+ case git.CmdSubVerbLfsUpload:
return perm.AccessModeWrite
- case "download":
+ case git.CmdSubVerbLfsDownload:
return perm.AccessModeRead
}
}
// should be unreachable
+ setting.PanicInDevOrTesting("unknown verb: %s %s", verb, lfsVerb)
return perm.AccessModeNone
}
@@ -170,13 +149,10 @@ func getLFSAuthToken(ctx context.Context, lfsVerb string, results *private.ServC
if err != nil {
return "", fail(ctx, "Failed to sign JWT Token", "Failed to sign JWT token: %v", err)
}
- return fmt.Sprintf("Bearer %s", tokenString), nil
+ return "Bearer " + tokenString, nil
}
-func runServ(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runServ(ctx context.Context, c *cli.Command) error {
// FIXME: This needs to internationalised
setup(ctx, c.Bool("debug"))
@@ -227,41 +203,37 @@ func runServ(c *cli.Context) error {
log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND"))
}
- words, err := shellquote.Split(cmd)
+ sshCmdArgs, err := shellquote.Split(cmd)
if err != nil {
return fail(ctx, "Error parsing arguments", "Failed to parse arguments: %v", err)
}
- if len(words) < 2 {
+ if len(sshCmdArgs) < 2 {
if git.DefaultFeatures().SupportProcReceive {
// for AGit Flow
if cmd == "ssh_info" {
- fmt.Print(`{"type":"gitea","version":1}`)
+ fmt.Print(`{"type":"agit","version":1}`)
return nil
}
}
return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd)
}
- verb := words[0]
- repoPath := strings.TrimPrefix(words[1], "/")
-
- var lfsVerb string
-
- rr := strings.SplitN(repoPath, "/", 2)
- if len(rr) != 2 {
+ repoPath := strings.TrimPrefix(sshCmdArgs[1], "/")
+ repoPathFields := strings.SplitN(repoPath, "/", 2)
+ if len(repoPathFields) != 2 {
return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath)
}
- username := rr[0]
- reponame := strings.TrimSuffix(rr[1], ".git")
+ username := repoPathFields[0]
+ reponame := strings.TrimSuffix(repoPathFields[1], ".git") // “the-repo-name" or "the-repo-name.wiki"
// LowerCase and trim the repoPath as that's how they are stored.
// This should be done after splitting the repoPath into username and reponame
// so that username and reponame are not affected.
repoPath = strings.ToLower(strings.TrimSpace(repoPath))
- if alphaDashDotPattern.MatchString(reponame) {
+ if !repo.IsValidSSHAccessRepoName(reponame) {
return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame)
}
@@ -283,22 +255,23 @@ func runServ(c *cli.Context) error {
}()
}
- if allowedCommands.Contains(verb) {
- if allowedCommandsLfs.Contains(verb) {
- if !setting.LFS.StartServer {
- return fail(ctx, "LFS Server is not enabled", "")
- }
- if verb == verbLfsTransfer && !setting.LFS.AllowPureSSH {
- return fail(ctx, "LFS SSH transfer is not enabled", "")
- }
- if len(words) > 2 {
- lfsVerb = words[2]
- }
- }
- } else {
+ verb, lfsVerb := sshCmdArgs[0], ""
+ if !git.IsAllowedVerbForServe(verb) {
return fail(ctx, "Unknown git command", "Unknown git command %s", verb)
}
+ if git.IsAllowedVerbForServeLfs(verb) {
+ if !setting.LFS.StartServer {
+ return fail(ctx, "LFS Server is not enabled", "")
+ }
+ if verb == git.CmdVerbLfsTransfer && !setting.LFS.AllowPureSSH {
+ return fail(ctx, "LFS SSH transfer is not enabled", "")
+ }
+ if len(sshCmdArgs) > 2 {
+ lfsVerb = sshCmdArgs[2]
+ }
+ }
+
requestedMode := getAccessMode(verb, lfsVerb)
results, extra := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb)
@@ -307,7 +280,7 @@ func runServ(c *cli.Context) error {
}
// LFS SSH protocol
- if verb == verbLfsTransfer {
+ if verb == git.CmdVerbLfsTransfer {
token, err := getLFSAuthToken(ctx, lfsVerb, results)
if err != nil {
return err
@@ -316,7 +289,7 @@ func runServ(c *cli.Context) error {
}
// LFS token authentication
- if verb == verbLfsAuthenticate {
+ if verb == git.CmdVerbLfsAuthenticate {
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
token, err := getLFSAuthToken(ctx, lfsVerb, results)
@@ -369,9 +342,9 @@ func runServ(c *cli.Context) error {
repo_module.EnvPusherEmail+"="+results.UserEmail,
repo_module.EnvPusherID+"="+strconv.FormatInt(results.UserID, 10),
repo_module.EnvRepoID+"="+strconv.FormatInt(results.RepoID, 10),
- repo_module.EnvPRID+"="+fmt.Sprintf("%d", 0),
- repo_module.EnvDeployKeyID+"="+fmt.Sprintf("%d", results.DeployKeyID),
- repo_module.EnvKeyID+"="+fmt.Sprintf("%d", results.KeyID),
+ repo_module.EnvPRID+"="+strconv.Itoa(0),
+ repo_module.EnvDeployKeyID+"="+strconv.FormatInt(results.DeployKeyID, 10),
+ repo_module.EnvKeyID+"="+strconv.FormatInt(results.KeyID, 10),
repo_module.EnvAppURL+"="+setting.AppURL,
)
// to avoid breaking, here only use the minimal environment variables for the "gitea serv" command.
diff --git a/cmd/web.go b/cmd/web.go
index f8217758e5..61ee3cbc20 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -18,15 +18,17 @@ import (
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/graceful"
+ "code.gitea.io/gitea/modules/gtprof"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/install"
"github.com/felixge/fgprof"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// PIDFile could be set from build tag
@@ -128,19 +130,19 @@ func showWebStartupMessage(msg string) {
}
}
-func serveInstall(ctx *cli.Context) error {
+func serveInstall(cmd *cli.Command) error {
showWebStartupMessage("Prepare to run install page")
routers.InitWebInstallPage(graceful.GetManager().HammerContext())
// Flag for port number in case first time run conflict
- if ctx.IsSet("port") {
- if err := setPort(ctx.String("port")); err != nil {
+ if cmd.IsSet("port") {
+ if err := setPort(cmd.String("port")); err != nil {
return err
}
}
- if ctx.IsSet("install-port") {
- if err := setPort(ctx.String("install-port")); err != nil {
+ if cmd.IsSet("install-port") {
+ if err := setPort(cmd.String("install-port")); err != nil {
return err
}
}
@@ -161,7 +163,7 @@ func serveInstall(ctx *cli.Context) error {
return nil
}
-func serveInstalled(ctx *cli.Context) error {
+func serveInstalled(c *cli.Command) error {
setting.InitCfgProvider(setting.CustomConf)
setting.LoadCommonSettings()
setting.MustInstalled()
@@ -211,13 +213,19 @@ func serveInstalled(ctx *cli.Context) error {
log.Fatal("Can not find APP_DATA_PATH %q", setting.AppDataPath)
}
+ // the AppDataTempDir is fully managed by us with a safe sub-path
+ // so it's safe to automatically remove the outdated files
+ setting.AppDataTempDir("").RemoveOutdated(3 * 24 * time.Hour)
+
// Override the provided port number within the configuration
- if ctx.IsSet("port") {
- if err := setPort(ctx.String("port")); err != nil {
+ if c.IsSet("port") {
+ if err := setPort(c.String("port")); err != nil {
return err
}
}
+ gtprof.EnableBuiltinTracer(util.Iif(setting.IsProd, 2000*time.Millisecond, 100*time.Millisecond))
+
// Set up Chi routes
webRoutes := routers.NormalRoutes()
err := listen(webRoutes, true)
@@ -236,13 +244,17 @@ func servePprof() {
finished()
}
-func runWeb(ctx *cli.Context) error {
+func runWeb(_ context.Context, cmd *cli.Command) error {
defer func() {
if panicked := recover(); panicked != nil {
log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2))
}
}()
+ if subCmdName, valid := isValidDefaultSubCommand(cmd); !valid {
+ return fmt.Errorf("unknown command: %s", subCmdName)
+ }
+
managerCtx, cancel := context.WithCancel(context.Background())
graceful.InitManager(managerCtx)
defer cancel()
@@ -254,12 +266,12 @@ func runWeb(ctx *cli.Context) error {
}
// Set pid file setting
- if ctx.IsSet("pid") {
- createPIDFile(ctx.String("pid"))
+ if cmd.IsSet("pid") {
+ createPIDFile(cmd.String("pid"))
}
if !setting.InstallLock {
- if err := serveInstall(ctx); err != nil {
+ if err := serveInstall(cmd); err != nil {
return err
}
} else {
@@ -270,7 +282,7 @@ func runWeb(ctx *cli.Context) error {
go servePprof()
}
- return serveInstalled(ctx)
+ return serveInstalled(cmd)
}
func setPort(port string) error {
diff --git a/cmd/web_acme.go b/cmd/web_acme.go
index 2fe14c1f54..5f7a308334 100644
--- a/cmd/web_acme.go
+++ b/cmd/web_acme.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"github.com/caddyserver/certmagic"
)
@@ -54,8 +55,6 @@ func runACME(listenAddr string, m http.Handler) error {
altTLSALPNPort = p
}
- magic := &certmagic.Default
- magic.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
// Try to use private CA root if provided, otherwise defaults to system's trust
var certPool *x509.CertPool
if setting.AcmeCARoot != "" {
@@ -65,8 +64,20 @@ func runACME(listenAddr string, m http.Handler) error {
log.Warn("Failed to parse CA Root certificate, using default CA trust: %v", err)
}
}
- myACME := certmagic.NewACMEIssuer(magic, certmagic.ACMEIssuer{
- CA: setting.AcmeURL,
+ // FIXME: this path is not right, it uses "AppWorkPath" incorrectly, and writes the data into "AppWorkPath/https"
+ // Ideally it should migrate to AppDataPath write to "AppDataPath/https"
+ // And one more thing, no idea why we should set the global default variables here
+ // But it seems that the current ACME code needs these global variables to make renew work.
+ // Otherwise, "renew" will use incorrect storage path
+ oldDefaultACME := certmagic.DefaultACME
+ certmagic.Default.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
+ certmagic.DefaultACME = certmagic.ACMEIssuer{
+ // try to use the default values provided by DefaultACME
+ CA: util.IfZero(setting.AcmeURL, oldDefaultACME.CA),
+ TestCA: oldDefaultACME.TestCA,
+ Logger: oldDefaultACME.Logger,
+ HTTPProxy: oldDefaultACME.HTTPProxy,
+
TrustedRoots: certPool,
Email: setting.AcmeEmail,
Agreed: setting.AcmeTOS,
@@ -75,8 +86,10 @@ func runACME(listenAddr string, m http.Handler) error {
ListenHost: setting.HTTPAddr,
AltTLSALPNPort: altTLSALPNPort,
AltHTTPPort: altHTTPPort,
- })
+ }
+ magic := certmagic.NewDefault()
+ myACME := certmagic.NewACMEIssuer(magic, certmagic.DefaultACME)
magic.Issuers = []certmagic.Issuer{myACME}
// this obtains certificates or renews them if necessary
@@ -123,7 +136,7 @@ func runACME(listenAddr string, m http.Handler) error {
}
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != "GET" && r.Method != "HEAD" {
+ if r.Method != http.MethodGet && r.Method != http.MethodHead {
http.Error(w, "Use HTTPS", http.StatusBadRequest)
return
}
diff --git a/cmd/web_graceful.go b/cmd/web_graceful.go
index 996537be3b..5e06d2c216 100644
--- a/cmd/web_graceful.go
+++ b/cmd/web_graceful.go
@@ -23,12 +23,6 @@ func NoHTTPRedirector() {
graceful.GetManager().InformCleanup()
}
-// NoMainListener tells our cleanup routine that we will not be using a possibly provided listener
-// for our main HTTP/HTTPS service
-func NoMainListener() {
- graceful.GetManager().InformCleanup()
-}
-
// NoInstallListener tells our cleanup routine that we will not be using a possibly provided listener
// for our install HTTP/HTTPS service
func NoInstallListener() {
diff --git a/contrib/backport/backport.go b/contrib/backport/backport.go
index eb19437445..2052295fb1 100644
--- a/contrib/backport/backport.go
+++ b/contrib/backport/backport.go
@@ -1,31 +1,30 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-//nolint:forbidigo
+//nolint:forbidigo // use of print functions is allowed in cli
package main
import (
"context"
+ "errors"
"fmt"
"log"
"net/http"
"os"
"os/exec"
- "os/signal"
"path"
"strconv"
"strings"
- "syscall"
- "github.com/google/go-github/v61/github"
- "github.com/urfave/cli/v2"
+ "github.com/google/go-github/v71/github"
+ "github.com/urfave/cli/v3"
"gopkg.in/yaml.v3"
)
const defaultVersion = "v1.18" // to backport to
func main() {
- app := cli.NewApp()
+ app := &cli.Command{}
app.Name = "backport"
app.Usage = "Backport provided PR-number on to the current or previous released version"
app.Description = `Backport will look-up the PR in Gitea's git log and attempt to cherry-pick it on the current version`
@@ -90,7 +89,7 @@ func main() {
Usage: "Set this flag to continue from a git cherry-pick that has broken",
},
}
- cli.AppHelpTemplate = `NAME:
+ cli.RootCommandHelpTemplate = `NAME:
{{.Name}} - {{.Usage}}
USAGE:
{{.HelpName}} {{if .VisibleFlags}}[options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
@@ -104,16 +103,12 @@ OPTIONS:
`
app.Action = runBackport
-
- if err := app.Run(os.Args); err != nil {
+ if err := app.Run(context.Background(), os.Args); err != nil {
fmt.Fprintf(os.Stderr, "Unable to backport: %v\n", err)
}
}
-func runBackport(c *cli.Context) error {
- ctx, cancel := installSignals()
- defer cancel()
-
+func runBackport(ctx context.Context, c *cli.Command) error {
continuing := c.Bool("continue")
var pr string
@@ -158,7 +153,7 @@ func runBackport(c *cli.Context) error {
args := c.Args().Slice()
if len(args) == 0 && pr == "" {
- return fmt.Errorf("no PR number provided\nProvide a PR number to backport")
+ return errors.New("no PR number provided\nProvide a PR number to backport")
} else if len(args) != 1 && pr == "" {
return fmt.Errorf("multiple PRs provided %v\nOnly a single PR can be backported at a time", args)
}
@@ -342,8 +337,8 @@ func determineRemote(ctx context.Context, forkUser string) (string, string, erro
fmt.Fprintf(os.Stderr, "Unable to list git remotes:\n%s\n", string(out))
return "", "", fmt.Errorf("unable to determine forked remote: %w", err)
}
- lines := strings.Split(string(out), "\n")
- for _, line := range lines {
+ lines := strings.SplitSeq(string(out), "\n")
+ for line := range lines {
fields := strings.Split(line, "\t")
name, remote := fields[0], fields[1]
// only look at pushers
@@ -361,12 +356,12 @@ func determineRemote(ctx context.Context, forkUser string) (string, string, erro
if !strings.Contains(remote, forkUser) {
continue
}
- if strings.HasPrefix(remote, "git@github.com:") {
- forkUser = strings.TrimPrefix(remote, "git@github.com:")
- } else if strings.HasPrefix(remote, "https://github.com/") {
- forkUser = strings.TrimPrefix(remote, "https://github.com/")
- } else if strings.HasPrefix(remote, "https://www.github.com/") {
- forkUser = strings.TrimPrefix(remote, "https://www.github.com/")
+ if after, ok := strings.CutPrefix(remote, "git@github.com:"); ok {
+ forkUser = after
+ } else if after, ok := strings.CutPrefix(remote, "https://github.com/"); ok {
+ forkUser = after
+ } else if after, ok := strings.CutPrefix(remote, "https://www.github.com/"); ok {
+ forkUser = after
} else if forkUser == "" {
return "", "", fmt.Errorf("unable to extract forkUser from remote %s: %s", name, remote)
}
@@ -459,25 +454,3 @@ func determineSHAforPR(ctx context.Context, prStr, accessToken string) (string,
return "", nil
}
-
-func installSignals() (context.Context, context.CancelFunc) {
- ctx, cancel := context.WithCancel(context.Background())
- go func() {
- // install notify
- signalChannel := make(chan os.Signal, 1)
-
- signal.Notify(
- signalChannel,
- syscall.SIGINT,
- syscall.SIGTERM,
- )
- select {
- case <-signalChannel:
- case <-ctx.Done():
- }
- cancel()
- signal.Reset()
- }()
-
- return ctx, cancel
-}
diff --git a/contrib/environment-to-ini/environment-to-ini.go b/contrib/environment-to-ini/environment-to-ini.go
index a7d7a6d293..5eb576c6fe 100644
--- a/contrib/environment-to-ini/environment-to-ini.go
+++ b/contrib/environment-to-ini/environment-to-ini.go
@@ -4,16 +4,17 @@
package main
import (
+ "context"
"os"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
func main() {
- app := cli.NewApp()
+ app := cli.Command{}
app.Name = "environment-to-ini"
app.Usage = "Use provided environment to update configuration ini"
app.Description = `As a helper to allow docker users to update the gitea configuration
@@ -72,13 +73,13 @@ func main() {
},
}
app.Action = runEnvironmentToIni
- err := app.Run(os.Args)
+ err := app.Run(context.Background(), os.Args)
if err != nil {
log.Fatal("Failed to run app with %s: %v", os.Args, err)
}
}
-func runEnvironmentToIni(c *cli.Context) error {
+func runEnvironmentToIni(_ context.Context, c *cli.Command) error {
// the config system may change the environment variables, so get a copy first, to be used later
env := append([]string{}, os.Environ()...)
setting.InitWorkPathAndCfgProvider(os.Getenv, setting.ArgWorkPathAndCustomConf{
diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index eacc732c22..aa2fcee765 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -59,27 +59,22 @@ RUN_USER = ; git
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
-;; The protocol the server listens on. One of 'http', 'https', 'http+unix', 'fcgi' or 'fcgi+unix'. Defaults to 'http'
-;; Note: Value must be lowercase.
+;; The protocol the server listens on. One of "http", "https", "http+unix", "fcgi" or "fcgi+unix".
;PROTOCOL = http
;;
-;; Expect PROXY protocol headers on connections
-;USE_PROXY_PROTOCOL = false
-;;
-;; Use PROXY protocol in TLS Bridging mode
-;PROXY_PROTOCOL_TLS_BRIDGING = false
-;;
-; Timeout to wait for PROXY protocol header (set to 0 to have no timeout)
-;PROXY_PROTOCOL_HEADER_TIMEOUT=5s
-;;
-; Accept PROXY protocol headers with UNKNOWN type
-;PROXY_PROTOCOL_ACCEPT_UNKNOWN=false
-;;
-;; Set the domain for the server
+;; Set the domain for the server.
;DOMAIN = localhost
;;
-;; Overwrite the automatically generated public URL. Necessary for proxies and docker.
-;ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
+;; The AppURL is used to generate public URL links, defaults to "{PROTOCOL}://{DOMAIN}:{HTTP_PORT}/".
+;; Most users should set it to the real website URL of their Gitea instance when there is a reverse proxy.
+;ROOT_URL =
+;;
+;; Controls how to detect the public URL.
+;; Although it defaults to "legacy" (to avoid breaking existing users), most instances should use the "auto" behavior,
+;; especially when the Gitea instance needs to be accessed in a container network.
+;; * legacy: detect the public URL from "Host" header if "X-Forwarded-Proto" header exists, otherwise use "ROOT_URL".
+;; * auto: always use "Host" header, and also use "X-Forwarded-Proto" header if it exists. If no "Host" header, use "ROOT_URL".
+;PUBLIC_URL_DETECTION = legacy
;;
;; For development purpose only. It makes Gitea handle sub-path ("/sub-path/owner/repo/...") directly when debugging without a reverse proxy.
;; DO NOT USE IT IN PRODUCTION!!!
@@ -89,13 +84,25 @@ RUN_USER = ; git
;STATIC_URL_PREFIX =
;;
;; The address to listen on. Either a IPv4/IPv6 address or the path to a unix socket.
-;; If PROTOCOL is set to `http+unix` or `fcgi+unix`, this should be the name of the Unix socket file to use.
+;; If PROTOCOL is set to "http+unix" or "fcgi+unix", this should be the name of the Unix socket file to use.
;; Relative paths will be made absolute against the _`AppWorkPath`_.
;HTTP_ADDR = 0.0.0.0
;;
-;; The port to listen on. Leave empty when using a unix socket.
+;; The port to listen on for "http" or "https" protocol. Leave empty when using a unix socket.
;HTTP_PORT = 3000
;;
+;; Expect PROXY protocol headers on connections
+;USE_PROXY_PROTOCOL = false
+;;
+;; Use PROXY protocol in TLS Bridging mode
+;PROXY_PROTOCOL_TLS_BRIDGING = false
+;;
+;; Timeout to wait for PROXY protocol header (set to 0 to have no timeout)
+;PROXY_PROTOCOL_HEADER_TIMEOUT = 5s
+;;
+;; Accept PROXY protocol headers with UNKNOWN type
+;PROXY_PROTOCOL_ACCEPT_UNKNOWN = false
+;;
;; If REDIRECT_OTHER_PORT is true, and PROTOCOL is set to https an http server
;; will be started on PORT_TO_REDIRECT and it will redirect plain, non-secure http requests to the main
;; ROOT_URL. Defaults are false for REDIRECT_OTHER_PORT and 80 for
@@ -103,8 +110,8 @@ RUN_USER = ; git
;REDIRECT_OTHER_PORT = false
;PORT_TO_REDIRECT = 80
;;
-;; expect PROXY protocol header on connections to https redirector.
-;REDIRECTOR_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)s
+;; expect PROXY protocol header on connections to https redirector, defaults to USE_PROXY_PROTOCOL
+;REDIRECTOR_USE_PROXY_PROTOCOL =
;; Minimum and maximum supported TLS versions
;SSL_MIN_VERSION=TLSv1.2
;SSL_MAX_VERSION=
@@ -128,13 +135,14 @@ RUN_USER = ; git
;; most cases you do not need to change the default value. Alter it only if
;; your SSH server node is not the same as HTTP node. For different protocol, the default
;; values are different. If `PROTOCOL` is `http+unix`, the default value is `http://unix/`.
-;; If `PROTOCOL` is `fcgi` or `fcgi+unix`, the default value is `%(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/`.
-;; If listen on `0.0.0.0`, the default value is `%(PROTOCOL)s://localhost:%(HTTP_PORT)s/`, Otherwise the default
-;; value is `%(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/`.
-;LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
+;; If `PROTOCOL` is `fcgi` or `fcgi+unix`, the default value is `{PROTOCOL}://{HTTP_ADDR}:{HTTP_PORT}/`.
+;; If listen on `0.0.0.0`, the default value is `{PROTOCOL}://localhost:{HTTP_PORT}/`.
+;; Otherwise the default value is `{PROTOCOL}://{HTTP_ADDR}:{HTTP_PORT}/`.
+;; Most users don't need (and shouldn't) set this value.
+;LOCAL_ROOT_URL =
;;
-;; When making local connections pass the PROXY protocol header.
-;LOCAL_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)s
+;; When making local connections pass the PROXY protocol header, defaults to USE_PROXY_PROTOCOL
+;LOCAL_USE_PROXY_PROTOCOL =
;;
;; Disable SSH feature when not available
;DISABLE_SSH = false
@@ -146,13 +154,17 @@ RUN_USER = ; git
;SSH_SERVER_USE_PROXY_PROTOCOL = false
;;
;; Username to use for the builtin SSH server. If blank, then it is the value of RUN_USER.
-;BUILTIN_SSH_SERVER_USER = %(RUN_USER)s
+;BUILTIN_SSH_SERVER_USER =
;;
-;; Domain name to be exposed in clone URL
-;SSH_DOMAIN = %(DOMAIN)s
+;; Domain name to be exposed in clone URL, defaults to DOMAIN or the domain part of ROOT_URL
+;SSH_DOMAIN =
;;
-;; SSH username displayed in clone URLs.
-;SSH_USER = %(BUILTIN_SSH_SERVER_USER)s
+;; SSH username displayed in clone URLs. It defaults to BUILTIN_SSH_SERVER_USER or RUN_USER.
+;; If it is set to "(DOER_USERNAME)", it will use current signed-in user's username.
+;; This option is only for some advanced users who have configured their SSH reverse-proxy
+;; and need to use different usernames for git SSH clone.
+;; Most users should just leave it blank.
+;SSH_USER =
;;
;; The network interface the builtin SSH server should listen on
;SSH_LISTEN_HOST =
@@ -160,8 +172,8 @@ RUN_USER = ; git
;; Port number to be exposed in clone URL
;SSH_PORT = 22
;;
-;; The port number the builtin SSH server should listen on
-;SSH_LISTEN_PORT = %(SSH_PORT)s
+;; The port number the builtin SSH server should listen on, defaults to SSH_PORT
+;SSH_LISTEN_PORT =
;;
;; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
;SSH_ROOT_PATH =
@@ -174,30 +186,19 @@ RUN_USER = ; git
;; If you intend to use the AuthorizedPrincipalsCommand functionality then you should turn this off.
;SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE = true
;;
-;; For the built-in SSH server, choose the ciphers to support for SSH connections,
-;; for system SSH this setting has no effect
-;SSH_SERVER_CIPHERS = chacha20-poly1305@openssh.com, aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, aes256-gcm@openssh.com
-;;
-;; For the built-in SSH server, choose the key exchange algorithms to support for SSH connections,
-;; for system SSH this setting has no effect
-;SSH_SERVER_KEY_EXCHANGES = curve25519-sha256, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1
-;;
-;; For the built-in SSH server, choose the MACs to support for SSH connections,
-;; for system SSH this setting has no effect
-;SSH_SERVER_MACS = hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1
+;; For the builtin SSH server, choose the supported ciphers/key-exchange-algorithms/MACs for SSH connections.
+;; The supported names are listed in https://github.com/golang/crypto/blob/master/ssh/common.go.
+;; Leave them empty to use the Golang crypto's recommended default values.
+;; For system SSH (non-builtin SSH server), this setting has no effect.
+;SSH_SERVER_CIPHERS =
+;SSH_SERVER_KEY_EXCHANGES =
+;SSH_SERVER_MACS =
;;
;; For the built-in SSH server, choose the keypair to offer as the host key
;; The private key should be at SSH_SERVER_HOST_KEY and the public SSH_SERVER_HOST_KEY.pub
-;; relative paths are made absolute relative to the %(APP_DATA_PATH)s
+;; relative paths are made absolute relative to the APP_DATA_PATH
;SSH_SERVER_HOST_KEYS=ssh/gitea.rsa, ssh/gogs.rsa
;;
-;; Directory to create temporary files in when testing public keys using ssh-keygen,
-;; default is the system temporary directory.
-;SSH_KEY_TEST_PATH =
-;;
-;; Use `ssh-keygen` to parse public SSH keys. The value is passed to the shell. By default, Gitea does the parsing itself.
-;SSH_KEYGEN_PATH =
-;;
;; Enable SSH Authorized Key Backup when rewriting all keys, default is false
;SSH_AUTHORIZED_KEYS_BACKUP = false
;;
@@ -288,6 +289,9 @@ RUN_USER = ; git
;; Default path for App data
;APP_DATA_PATH = data ; relative paths will be made absolute with _`AppWorkPath`_
;;
+;; Base path for App's temp files, leave empty to use the managed tmp directory in APP_DATA_PATH
+;APP_TEMP_PATH =
+;;
;; Enable gzip compression for runtime-generated content, static resources excluded
;ENABLE_GZIP = false
;;
@@ -516,6 +520,10 @@ INTERNAL_TOKEN =
;;
;; On user registration, record the IP address and user agent of the user to help identify potential abuse.
;; RECORD_USER_SIGNUP_METADATA = false
+;;
+;; Set the two-factor auth behavior.
+;; Set to "enforced", to force users to enroll into Two-Factor Authentication, users without 2FA have no access to repositories via API or web.
+;TWO_FACTOR_AUTH =
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -582,7 +590,7 @@ ENABLED = true
[log]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Root path for the log files - defaults to %(GITEA_WORK_DIR)/log
+;; Root path for the log files - defaults to "{AppWorkPath}/log"
;ROOT_PATH =
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -682,8 +690,8 @@ LEVEL = Info
;; The path of git executable. If empty, Gitea searches through the PATH environment.
;PATH =
;;
-;; The HOME directory for Git
-;HOME_PATH = %(APP_DATA_PATH)s/home
+;; The HOME directory for Git, defaults to "{APP_DATA_PATH}/home"
+;HOME_PATH =
;;
;; Disables highlight of added and removed changes
;DISABLE_DIFF_HIGHLIGHT = false
@@ -774,6 +782,9 @@ LEVEL = Info
;ALLOW_ONLY_EXTERNAL_REGISTRATION = false
;;
;; User must sign in to view anything.
+;; It could be set to "expensive" to block anonymous users accessing some pages which consume a lot of resources,
+;; for example: block anonymous AI crawlers from accessing repo code pages.
+;; The "expensive" mode is experimental and subject to change.
;REQUIRE_SIGNIN_VIEW = false
;;
;; Mail notification
@@ -784,10 +795,13 @@ LEVEL = Info
;; Please note that setting this to false will not disable OAuth Basic or Basic authentication using a token
;ENABLE_BASIC_AUTHENTICATION = true
;;
-;; Show the password sign-in form (for password-based login), otherwise, only show OAuth2 login methods.
+;; Show the password sign-in form (for password-based login), otherwise, only show OAuth2 or passkey login methods if they are enabled.
;; If you set it to false, maybe it also needs to set ENABLE_BASIC_AUTHENTICATION to false to completely disable password-based authentication.
;ENABLE_PASSWORD_SIGNIN_FORM = true
;;
+;; Allow users to sign-in with a passkey
+;ENABLE_PASSKEY_AUTHENTICATION = true
+;;
;; More detail: https://github.com/gogits/gogs/issues/165
;ENABLE_REVERSE_PROXY_AUTHENTICATION = false
; Enable this to allow reverse proxy authentication for API requests, the reverse proxy is responsible for ensuring that no CSRF is possible.
@@ -932,7 +946,29 @@ LEVEL = Info
;;
;; Disable the code explore page.
;DISABLE_CODE_PAGE = false
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;[qos]
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Enable request quality of service and overload protection.
+; ENABLED = false
+;;
+;; The maximum number of concurrent requests that the server will
+;; process before enqueueing new requests. Default is "CpuNum * 4".
+; MAX_INFLIGHT =
;;
+;; The maximum number of requests that can be enqueued before new
+;; requests will be dropped.
+; MAX_WAITING = 100
+;;
+;; Target maximum wait time a request may be enqueued for. Requests
+;; that are enqueued for less than this amount of time will not be
+;; dropped. When wait times exceed this amount, a portion of requests
+;; will be dropped until wait times have decreased below this amount.
+; TARGET_WAIT_TIME = 250ms
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -946,8 +982,8 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[repository]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Root path for storing all repository data. By default, it is set to %(APP_DATA_PATH)s/gitea-repositories.
-;; A relative path is interpreted as _`AppWorkPath`_/%(ROOT)s
+;; Root path for storing all repository data. By default, it is set to "{APP_DATA_PATH}/gitea-repositories".
+;; A relative path is interpreted as "{AppWorkPath}/{ROOT}" (use AppWorkPath as base path).
;ROOT =
;;
;; The script type this server supports. Usually this is `bash`, but some users report that only `sh` is available.
@@ -1059,15 +1095,6 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;[repository.local]
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Path for local repository copy. Defaults to `tmp/local-repo` (content gets deleted on gitea restart)
-;LOCAL_COPY_PATH = tmp/local-repo
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[repository.upload]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1075,9 +1102,6 @@ LEVEL = Info
;; Whether repository file uploads are enabled. Defaults to `true`
;ENABLED = true
;;
-;; Path for uploads. Defaults to `data/tmp/uploads` (content gets deleted on gitea restart)
-;TEMP_PATH = data/tmp/uploads
-;;
;; Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
;ALLOWED_TYPES =
;;
@@ -1120,6 +1144,9 @@ LEVEL = Info
;; In default merge messages only include approvers who are official
;DEFAULT_MERGE_MESSAGE_OFFICIAL_APPROVERS_ONLY = true
;;
+;; In default squash-merge messages include the commit message of all commits comprising the pull request.
+;POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES = false
+;;
;; Add co-authored-by and co-committed-by trailers if committer does not match author
;ADD_CO_COMMITTER_TRAILERS = true
;;
@@ -1128,6 +1155,10 @@ LEVEL = Info
;;
;; Retarget child pull requests to the parent pull request branch target on merge of parent pull request. It only works on merged PRs where the head and base branch target the same repo.
;RETARGET_CHILDREN_ON_MERGE = true
+;;
+;; Delay mergeable check until page view or API access, for pull requests that have not been updated in the specified days when their base branches get updated.
+;; Use "-1" to always check all pull requests (old behavior). Use "0" to always delay the checks.
+;DELAY_CHECK_FOR_INACTIVE_DAYS = 7
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1155,17 +1186,24 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
-;; GPG key to use to sign commits, Defaults to the default - that is the value of git config --get user.signingkey
+;; GPG or SSH key to use to sign commits, Defaults to the default - that is the value of git config --get user.signingkey
+;; Depending on the value of SIGNING_FORMAT this is either:
+;; - openpgp: the GPG key ID
+;; - ssh: the path to the ssh public key "/path/to/key.pub": where "/path/to/key" is the private key, use ssh-keygen -t ed25519 to generate a new key pair without password
;; run in the context of the RUN_USER
;; Switch to none to stop signing completely
;SIGNING_KEY = default
;;
-;; If a SIGNING_KEY ID is provided and is not set to default, use the provided Name and Email address as the signer.
+;; If a SIGNING_KEY ID is provided and is not set to default, use the provided Name and Email address as the signer and the signing format.
;; These should match a publicized name and email address for the key. (When SIGNING_KEY is default these are set to
-;; the results of git config --get user.name and git config --get user.email respectively and can only be overridden
+;; the results of git config --get user.name, git config --get user.email and git config --default openpgp --get gpg.format respectively and can only be overridden
;; by setting the SIGNING_KEY ID to the correct ID.)
;SIGNING_NAME =
;SIGNING_EMAIL =
+;; SIGNING_FORMAT can be one of:
+;; - openpgp (default): use GPG to sign commits
+;; - ssh: use SSH to sign commits
+;SIGNING_FORMAT = openpgp
;;
;; Sets the default trust model for repositories. Options are: collaborator, committer, collaboratorcommitter
;DEFAULT_TRUST_MODEL = collaborator
@@ -1192,6 +1230,13 @@ LEVEL = Info
;; - commitssigned: require that all the commits in the head branch are signed.
;; - approved: only sign when merging an approved pr to a protected branch
;MERGES = pubkey, twofa, basesigned, commitssigned
+;;
+;; Determines which additional ssh keys are trusted for all signed commits regardless of the user
+;; This is useful for ssh signing key rotation.
+;; Exposes the provided SIGNING_NAME and SIGNING_EMAIL as the signer, regardless of the SIGNING_FORMAT value.
+;; Multiple keys should be comma separated.
+;; E.g."ssh-<algorithm> <key>". or "ssh-<algorithm> <key1>, ssh-<algorithm> <key2>".
+;TRUSTED_SSH_KEYS =
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1282,6 +1327,9 @@ LEVEL = Info
;; Leave it empty to allow users to select any theme from "{CustomPath}/public/assets/css/theme-*.css"
;THEMES =
;;
+;; The icons for file list (basic/material), this is a temporary option which will be replaced by a user setting in the future.
+;FILE_ICON_THEME = material
+;;
;; All available reactions users can choose on issues/prs and comments.
;; Values can be emoji alias (:smile:) or a unicode emoji.
;; For custom reactions, add a tightly cropped square image to public/assets/img/emoji/reaction_name.png
@@ -1395,14 +1443,14 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
-;; Render soft line breaks as hard line breaks, which means a single newline character between
-;; paragraphs will cause a line break and adding trailing whitespace to paragraphs is not
-;; necessary to force a line break.
-;; Render soft line breaks as hard line breaks for comments
-;ENABLE_HARD_LINE_BREAK_IN_COMMENTS = true
-;;
-;; Render soft line breaks as hard line breaks for markdown documents
-;ENABLE_HARD_LINE_BREAK_IN_DOCUMENTS = false
+;; Customize render options for different contexts. Set to "none" to disable the defaults, or use comma separated list:
+;; * short-issue-pattern: recognized "#123" issue reference and render it as a link to the issue
+;; * new-line-hard-break: render soft line breaks as hard line breaks, which means a single newline character between
+;; paragraphs will cause a line break and adding trailing whitespace to paragraphs is not
+;; necessary to force a line break.
+;RENDER_OPTIONS_COMMENT = short-issue-pattern, new-line-hard-break
+;RENDER_OPTIONS_WIKI = short-issue-pattern
+;RENDER_OPTIONS_REPO_FILE =
;;
;; Comma separated list of custom URL-Schemes that are allowed as links when rendering Markdown
;; for example git,magnet,ftp (more at https://en.wikipedia.org/wiki/List_of_URI_schemes)
@@ -1416,6 +1464,11 @@ LEVEL = Info
;;
;; Enables math inline and block detection
;ENABLE_MATH = true
+;;
+;; Enable delimiters for math code block detection. Set to "none" to disable all,
+;; or use comma separated list: inline-dollar, inline-parentheses, block-dollar, block-square-brackets
+;; Defaults to "inline-dollar,block-dollar" to follow GitHub's behavior.
+;MATH_CODE_BLOCK_DETECTION =
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1506,7 +1559,8 @@ LEVEL = Info
;TYPE = persistable-channel
;;
;; data-dir for storing persistable queues and level queues, individual queues will default to `queues/common` meaning the queue is shared.
-;DATADIR = queues/ ; Relative paths will be made absolute against `%(APP_DATA_PATH)s`.
+;; Relative paths will be made absolute against "APP_DATA_PATH"
+;DATADIR = queues/
;;
;; Default queue length before a channel queue will block
;LENGTH = 100000
@@ -1754,6 +1808,9 @@ LEVEL = Info
;;
;; convert \r\n to \n for Sendmail
;SENDMAIL_CONVERT_CRLF = true
+;;
+;; convert links of attached images to inline images. Only for images hosted in this gitea instance.
+;EMBED_ATTACHMENT_IMAGES = false
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -2405,6 +2462,8 @@ LEVEL = Info
;DEFAULT_GIT_TREES_PER_PAGE = 1000
;; Default max size of a blob returned by the blobs API (default is 10MiB)
;DEFAULT_MAX_BLOB_SIZE = 10485760
+;; Default max combined size of all blobs returned by the files API (default is 100MiB)
+;DEFAULT_MAX_RESPONSE_SIZE = 104857600
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -2445,7 +2504,7 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set the maximum number of characters in a mermaid source. (Set to -1 to disable limits)
-;MERMAID_MAX_SOURCE_CHARACTERS = 5000
+;MERMAID_MAX_SOURCE_CHARACTERS = 50000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -2566,9 +2625,6 @@ LEVEL = Info
;; Currently, only `minio` and `azureblob` is supported.
;SERVE_DIRECT = false
;;
-;; Path for chunked uploads. Defaults to APP_DATA_PATH + `tmp/package-upload`
-;CHUNKED_UPLOAD_PATH = tmp/package-upload
-;;
;; Maximum count of package versions a single owner can have (`-1` means no limits)
;LIMIT_TOTAL_OWNER_COUNT = -1
;; Maximum size of packages a single owner can use (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
diff --git a/docker/manifest.rootless.tmpl b/docker/manifest.rootless.tmpl
index 1ebf5b73c8..3fa94ab0ec 100644
--- a/docker/manifest.rootless.tmpl
+++ b/docker/manifest.rootless.tmpl
@@ -22,3 +22,8 @@ manifests:
architecture: arm64
os: linux
variant: v8
+ -
+ image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}nightly{{/if}}-linux-riscv64-rootless
+ platform:
+ architecture: riscv64
+ os: linux
diff --git a/docker/manifest.tmpl b/docker/manifest.tmpl
index 08ccf61b57..c68ca46dd8 100644
--- a/docker/manifest.tmpl
+++ b/docker/manifest.tmpl
@@ -22,3 +22,8 @@ manifests:
architecture: arm64
os: linux
variant: v8
+ -
+ image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}nightly{{/if}}-linux-riscv64
+ platform:
+ architecture: riscv64
+ os: linux
diff --git a/docker/root/etc/s6/openssh/setup b/docker/root/etc/s6/openssh/setup
index dbb3bafd35..48e7d4b211 100755
--- a/docker/root/etc/s6/openssh/setup
+++ b/docker/root/etc/s6/openssh/setup
@@ -31,6 +31,21 @@ if [ -e /data/ssh/ssh_host_ecdsa_cert ]; then
SSH_ECDSA_CERT=${SSH_ECDSA_CERT:-"/data/ssh/ssh_host_ecdsa_cert"}
fi
+# In case someone wants to sign the `{keyname}.pub` key by `ssh-keygen -s ca -I identity ...` to
+# make use of the ssh-key certificate authority feature (see ssh-keygen CERTIFICATES section),
+# the generated key file name is `{keyname}-cert.pub`
+if [ -e /data/ssh/ssh_host_ed25519_key-cert.pub ]; then
+ SSH_ED25519_CERT=${SSH_ED25519_CERT:-"/data/ssh/ssh_host_ed25519_key-cert.pub"}
+fi
+
+if [ -e /data/ssh/ssh_host_rsa_key-cert.pub ]; then
+ SSH_RSA_CERT=${SSH_RSA_CERT:-"/data/ssh/ssh_host_rsa_key-cert.pub"}
+fi
+
+if [ -e /data/ssh/ssh_host_ecdsa_key-cert.pub ]; then
+ SSH_ECDSA_CERT=${SSH_ECDSA_CERT:-"/data/ssh/ssh_host_ecdsa_key-cert.pub"}
+fi
+
if [ -d /etc/ssh ]; then
SSH_PORT=${SSH_PORT:-"22"} \
SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-"${SSH_PORT}"} \
diff --git a/docker/root/usr/bin/entrypoint b/docker/root/usr/bin/entrypoint
index d9dbb3ebe0..08587fc4f4 100755
--- a/docker/root/usr/bin/entrypoint
+++ b/docker/root/usr/bin/entrypoint
@@ -37,5 +37,5 @@ done
if [ $# -gt 0 ]; then
exec "$@"
else
- exec /bin/s6-svscan /etc/s6
+ exec /usr/bin/s6-svscan /etc/s6
fi
diff --git a/flake.lock b/flake.lock
index 1890b82dcf..da3f19bbd2 100644
--- a/flake.lock
+++ b/flake.lock
@@ -5,11 +5,11 @@
"systems": "systems"
},
"locked": {
- "lastModified": 1726560853,
- "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
+ "lastModified": 1731533236,
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
- "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1731139594,
- "narHash": "sha256-IigrKK3vYRpUu+HEjPL/phrfh7Ox881er1UEsZvw9Q4=",
+ "lastModified": 1747179050,
+ "narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "76612b17c0ce71689921ca12d9ffdc9c23ce40b2",
+ "rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
index e3655b627e..1b930649d0 100644
--- a/flake.nix
+++ b/flake.nix
@@ -29,9 +29,14 @@
poetry
# backend
+ go_1_24
gofumpt
sqlite
];
+ shellHook = ''
+ export GO="${pkgs.go_1_24}/bin/go"
+ export GOROOT="${pkgs.go_1_24}/share/go"
+ '';
};
}
);
diff --git a/go.mod b/go.mod
index dc80d2ca2b..afe7c990e4 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module code.gitea.io/gitea
-go 1.23
+go 1.24.4
// rfc5280 said: "The serial number is an integer assigned by the CA to each certificate."
// But some CAs use negative serial number, just relax the check. related:
@@ -8,11 +8,11 @@ go 1.23
godebug x509negativeserial=1
require (
- code.gitea.io/actions-proto-go v0.4.0
+ code.gitea.io/actions-proto-go v0.4.1
code.gitea.io/gitea-vet v0.2.3
- code.gitea.io/sdk/gitea v0.19.0
+ code.gitea.io/sdk/gitea v0.21.0
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
- connectrpc.com/connect v1.17.0
+ connectrpc.com/connect v1.18.1
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
gitea.com/go-chi/cache v0.2.1
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
@@ -21,19 +21,20 @@ require (
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
github.com/42wim/httpsig v1.2.2
github.com/42wim/sshsig v0.0.0-20240818000253-e3a6333df815
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0
- github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0
+ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
- github.com/ProtonMail/go-crypto v1.0.0
- github.com/PuerkitoBio/goquery v1.10.0
+ github.com/ProtonMail/go-crypto v1.2.0
+ github.com/PuerkitoBio/goquery v1.10.3
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3
- github.com/alecthomas/chroma/v2 v2.14.0
- github.com/aws/aws-sdk-go-v2/credentials v1.17.42
- github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.3
+ github.com/alecthomas/chroma/v2 v2.17.0
+ github.com/aws/aws-sdk-go-v2/credentials v1.17.67
+ github.com/aws/aws-sdk-go-v2/service/codecommit v1.28.2
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
- github.com/blevesearch/bleve/v2 v2.4.2
- github.com/buildkite/terminal-to-html/v3 v3.16.3
- github.com/caddyserver/certmagic v0.21.4
+ github.com/blevesearch/bleve/v2 v2.5.0
+ github.com/bohde/codel v0.2.0
+ github.com/buildkite/terminal-to-html/v3 v3.16.8
+ github.com/caddyserver/certmagic v0.23.0
github.com/charmbracelet/git-lfs-transfer v0.2.0
github.com/chi-middleware/proxy v1.1.1
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
@@ -41,37 +42,35 @@ require (
github.com/djherbis/nio/v3 v3.0.1
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5
github.com/dustin/go-humanize v1.0.1
- github.com/editorconfig/editorconfig-core-go/v2 v2.6.2
+ github.com/editorconfig/editorconfig-core-go/v2 v2.6.3
github.com/emersion/go-imap v1.2.1
github.com/emirpasic/gods v1.18.1
github.com/ethantkoenig/rupture v1.0.1
github.com/felixge/fgprof v0.9.5
- github.com/fsnotify/fsnotify v1.7.0
+ github.com/fsnotify/fsnotify v1.9.0
github.com/gliderlabs/ssh v0.3.8
- github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c
+ github.com/go-ap/activitypub v0.0.0-20250409143848-7113328b1f3d
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
- github.com/go-chi/chi/v5 v5.1.0
+ github.com/go-chi/chi/v5 v5.2.2
github.com/go-chi/cors v1.2.1
github.com/go-co-op/gocron v1.37.0
- github.com/go-enry/go-enry/v2 v2.9.1
- github.com/go-git/go-billy/v5 v5.6.0
- github.com/go-git/go-git/v5 v5.12.0
- github.com/go-ldap/ldap/v3 v3.4.8
+ github.com/go-enry/go-enry/v2 v2.9.2
+ github.com/go-git/go-billy/v5 v5.6.2
+ github.com/go-git/go-git/v5 v5.16.0
+ github.com/go-ldap/ldap/v3 v3.4.11
github.com/go-redsync/redsync/v4 v4.13.0
- github.com/go-sql-driver/mysql v1.8.1
- github.com/go-swagger/go-swagger v0.31.0
- github.com/go-webauthn/webauthn v0.11.2
+ github.com/go-sql-driver/mysql v1.9.2
+ github.com/go-webauthn/webauthn v0.12.3
github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
- github.com/golang-jwt/jwt/v5 v5.2.1
- github.com/google/go-github/v61 v61.0.0
+ github.com/golang-jwt/jwt/v5 v5.2.2
+ github.com/google/go-github/v71 v71.0.0
github.com/google/licenseclassifier/v2 v2.0.0
- github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db
+ github.com/google/pprof v0.0.0-20250422154841-e1f9c1950416
github.com/google/uuid v1.6.0
github.com/gorilla/feeds v1.2.0
github.com/gorilla/sessions v1.4.0
- github.com/h2non/gock v1.2.0
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/huandu/xstrings v1.5.0
@@ -79,217 +78,179 @@ require (
github.com/jhillyerd/enmime v1.3.0
github.com/json-iterator/go v1.1.12
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
- github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
- github.com/klauspost/compress v1.17.11
- github.com/klauspost/cpuid/v2 v2.2.8
+ github.com/klauspost/compress v1.18.0
+ github.com/klauspost/cpuid/v2 v2.2.10
github.com/lib/pq v1.10.9
- github.com/markbates/goth v1.80.0
+ github.com/markbates/goth v1.81.0
github.com/mattn/go-isatty v0.0.20
- github.com/mattn/go-sqlite3 v1.14.24
- github.com/meilisearch/meilisearch-go v0.29.1-0.20241106140435-0bf60fad690a
+ github.com/mattn/go-sqlite3 v1.14.28
+ github.com/meilisearch/meilisearch-go v0.31.0
github.com/mholt/archiver/v3 v3.5.1
github.com/microcosm-cc/bluemonday v1.0.27
- github.com/microsoft/go-mssqldb v1.7.2
- github.com/minio/minio-go/v7 v7.0.80
+ github.com/microsoft/go-mssqldb v1.8.0
+ github.com/minio/minio-go/v7 v7.0.91
github.com/msteinert/pam v1.2.0
github.com/nektos/act v0.2.63
- github.com/niklasfasching/go-org v1.7.0
+ github.com/niklasfasching/go-org v1.8.0
github.com/olivere/elastic/v7 v7.0.32
github.com/opencontainers/go-digest v1.0.0
- github.com/opencontainers/image-spec v1.1.0
+ github.com/opencontainers/image-spec v1.1.1
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.4.0
- github.com/prometheus/client_golang v1.20.5
+ github.com/prometheus/client_golang v1.22.0
github.com/quasoft/websspi v1.1.2
- github.com/redis/go-redis/v9 v9.7.0
+ github.com/redis/go-redis/v9 v9.7.3
github.com/robfig/cron/v3 v3.0.1
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/sassoftware/go-rpmutils v0.4.0
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
- github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92
- github.com/stretchr/testify v1.9.0
+ github.com/stretchr/testify v1.10.0
github.com/syndtr/goleveldb v1.0.0
github.com/tstranex/u2f v1.0.0
github.com/ulikunitz/xz v0.5.12
- github.com/urfave/cli/v2 v2.27.5
- github.com/wneessen/go-mail v0.5.2
- github.com/xanzy/go-gitlab v0.112.0
+ github.com/urfave/cli-docs/v3 v3.0.0-alpha6
+ github.com/urfave/cli/v3 v3.3.3
+ github.com/wneessen/go-mail v0.6.2
github.com/xeipuuv/gojsonschema v1.2.0
github.com/yohcop/openid-go v1.0.1
- github.com/yuin/goldmark v1.7.8
+ github.com/yuin/goldmark v1.7.10
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
github.com/yuin/goldmark-meta v1.1.0
- golang.org/x/crypto v0.31.0
- golang.org/x/image v0.21.0
- golang.org/x/net v0.33.0
- golang.org/x/oauth2 v0.23.0
- golang.org/x/sync v0.10.0
- golang.org/x/sys v0.28.0
- golang.org/x/text v0.21.0
- golang.org/x/tools v0.26.0
- google.golang.org/grpc v1.67.1
- google.golang.org/protobuf v1.35.1
+ gitlab.com/gitlab-org/api/client-go v0.127.0
+ golang.org/x/crypto v0.39.0
+ golang.org/x/image v0.26.0
+ golang.org/x/net v0.40.0
+ golang.org/x/oauth2 v0.29.0
+ golang.org/x/sync v0.15.0
+ golang.org/x/sys v0.33.0
+ golang.org/x/text v0.26.0
+ google.golang.org/grpc v1.72.0
+ google.golang.org/protobuf v1.36.6
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
- mvdan.cc/xurls/v2 v2.5.0
+ mvdan.cc/xurls/v2 v2.6.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
xorm.io/builder v0.3.13
xorm.io/xorm v1.3.9
)
require (
- cloud.google.com/go/compute/metadata v0.5.2 // indirect
+ cloud.google.com/go/compute/metadata v0.6.0 // indirect
dario.cat/mergo v1.0.1 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
- github.com/DataDog/zstd v1.5.6 // indirect
- github.com/Masterminds/goutils v1.1.1 // indirect
- github.com/Masterminds/semver/v3 v3.3.0 // indirect
- github.com/Masterminds/sprig/v3 v3.3.0 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
+ github.com/DataDog/zstd v1.5.7 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
- github.com/RoaringBitmap/roaring v1.9.4 // indirect
+ github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
- github.com/andybalholm/cascadia v1.3.2 // indirect
+ github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
- github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
- github.com/aws/aws-sdk-go-v2 v1.32.3 // indirect
- github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 // indirect
- github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 // indirect
- github.com/aws/smithy-go v1.22.0 // indirect
+ github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
+ github.com/aws/smithy-go v1.22.3 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
- github.com/bits-and-blooms/bitset v1.14.3 // indirect
- github.com/blevesearch/bleve_index_api v1.1.12 // indirect
- github.com/blevesearch/geo v0.1.20 // indirect
- github.com/blevesearch/go-faiss v1.0.23 // indirect
+ github.com/bits-and-blooms/bitset v1.22.0 // indirect
+ github.com/blevesearch/bleve_index_api v1.2.8 // indirect
+ github.com/blevesearch/geo v0.2.0 // indirect
+ github.com/blevesearch/go-faiss v1.0.25 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
- github.com/blevesearch/scorch_segment_api/v2 v2.2.16 // indirect
+ github.com/blevesearch/scorch_segment_api/v2 v2.3.10 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
- github.com/blevesearch/vellum v1.0.10 // indirect
- github.com/blevesearch/zapx/v11 v11.3.10 // indirect
- github.com/blevesearch/zapx/v12 v12.3.10 // indirect
- github.com/blevesearch/zapx/v13 v13.3.10 // indirect
- github.com/blevesearch/zapx/v14 v14.3.10 // indirect
- github.com/blevesearch/zapx/v15 v15.3.16 // indirect
- github.com/blevesearch/zapx/v16 v16.1.7 // indirect
+ github.com/blevesearch/vellum v1.1.0 // indirect
+ github.com/blevesearch/zapx/v11 v11.4.1 // indirect
+ github.com/blevesearch/zapx/v12 v12.4.1 // indirect
+ github.com/blevesearch/zapx/v13 v13.4.1 // indirect
+ github.com/blevesearch/zapx/v14 v14.4.1 // indirect
+ github.com/blevesearch/zapx/v15 v15.4.1 // indirect
+ github.com/blevesearch/zapx/v16 v16.2.3 // indirect
+ github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect
github.com/boombuler/barcode v1.0.2 // indirect
- github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
+ github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
github.com/caddyserver/zerossl v0.1.3 // indirect
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
- github.com/cloudflare/circl v1.5.0 // indirect
+ github.com/cloudflare/circl v1.6.1 // indirect
github.com/couchbase/go-couchbase v0.1.1 // indirect
- github.com/couchbase/gomemcached v0.3.2 // indirect
+ github.com/couchbase/gomemcached v0.3.3 // indirect
github.com/couchbase/goutils v0.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
- github.com/cyphar/filepath-securejoin v0.3.4 // indirect
+ github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
- github.com/dlclark/regexp2 v1.11.4 // indirect
+ github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
github.com/fatih/color v1.18.0 // indirect
- github.com/felixge/httpsnoop v1.0.4 // indirect
- github.com/fxamacker/cbor/v2 v2.7.0 // indirect
+ github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
- github.com/go-ap/errors v0.0.0-20240910140019-1e9d33cc1568 // indirect
- github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
+ github.com/go-ap/errors v0.0.0-20250409143711-5686c11ae650 // indirect
+ github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
github.com/go-enry/go-oniguruma v1.2.1 // indirect
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-ini/ini v1.67.0 // indirect
- github.com/go-openapi/analysis v0.23.0 // indirect
- github.com/go-openapi/errors v0.22.0 // indirect
- github.com/go-openapi/inflect v0.21.0 // indirect
- github.com/go-openapi/jsonpointer v0.21.0 // indirect
- github.com/go-openapi/jsonreference v0.21.0 // indirect
- github.com/go-openapi/loads v0.22.0 // indirect
- github.com/go-openapi/runtime v0.28.0 // indirect
- github.com/go-openapi/spec v0.21.0 // indirect
- github.com/go-openapi/strfmt v0.23.0 // indirect
- github.com/go-openapi/swag v0.23.0 // indirect
- github.com/go-openapi/validate v0.24.0 // indirect
- github.com/go-webauthn/x v0.1.15 // indirect
- github.com/goccy/go-json v0.10.3 // indirect
- github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
+ github.com/go-webauthn/x v0.1.20 // indirect
+ github.com/goccy/go-json v0.10.5 // indirect
+ github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
- github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect
- github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
+ github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/protobuf v1.5.4 // indirect
- github.com/golang/snappy v0.0.4 // indirect
+ github.com/golang/snappy v1.0.0 // indirect
github.com/google/btree v1.1.3 // indirect
+ github.com/google/flatbuffers v25.2.10+incompatible // indirect
github.com/google/go-querystring v1.1.0 // indirect
- github.com/google/go-tpm v0.9.1 // indirect
+ github.com/google/go-tpm v0.9.3 // indirect
github.com/gorilla/css v1.0.1 // indirect
- github.com/gorilla/handlers v1.5.2 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
- github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
- github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
- github.com/jessevdk/go-flags v1.6.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
- github.com/kr/pretty v0.3.1 // indirect
- github.com/kr/text v0.2.0 // indirect
- github.com/libdns/libdns v0.2.2 // indirect
- github.com/magiconair/properties v1.8.7 // indirect
- github.com/mailru/easyjson v0.7.7 // indirect
+ github.com/libdns/libdns v1.0.0-beta.1 // indirect
+ github.com/mailru/easyjson v0.9.0 // indirect
github.com/markbates/going v1.0.3 // indirect
- github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
- github.com/mholt/acmez/v2 v2.0.3 // indirect
- github.com/miekg/dns v1.1.62 // indirect
+ github.com/mattn/go-shellwords v1.0.12 // indirect
+ github.com/mholt/acmez/v3 v3.1.2 // indirect
+ github.com/miekg/dns v1.1.65 // indirect
+ github.com/minio/crc64nvme v1.0.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
- github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
- github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
- github.com/oklog/ulid v1.3.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
- github.com/pelletier/go-toml/v2 v2.2.3 // indirect
- github.com/pierrec/lz4/v4 v4.1.21 // indirect
- github.com/pjbgf/sha1cd v0.3.0 // indirect
+ github.com/pierrec/lz4/v4 v4.1.22 // indirect
+ github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
- github.com/prometheus/client_model v0.6.1 // indirect
- github.com/prometheus/common v0.60.1 // indirect
- github.com/prometheus/procfs v0.15.1 // indirect
- github.com/rhysd/actionlint v1.7.3 // indirect
+ github.com/prometheus/client_model v0.6.2 // indirect
+ github.com/prometheus/common v0.63.0 // indirect
+ github.com/prometheus/procfs v0.16.1 // indirect
+ github.com/rhysd/actionlint v1.7.7 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
- github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
- github.com/sagikazarmark/locafero v0.6.0 // indirect
- github.com/sagikazarmark/slog-shim v0.1.0 // indirect
- github.com/shopspring/decimal v1.4.0 // indirect
- github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
- github.com/skeema/knownhosts v1.3.0 // indirect
- github.com/sourcegraph/conc v0.3.0 // indirect
- github.com/spf13/afero v1.11.0 // indirect
- github.com/spf13/cast v1.7.0 // indirect
- github.com/spf13/pflag v1.0.5 // indirect
- github.com/spf13/viper v1.19.0 // indirect
+ github.com/skeema/knownhosts v1.3.1 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
- github.com/subosito/gotenv v1.6.0 // indirect
- github.com/toqueteos/webbrowser v1.2.0 // indirect
github.com/unknwon/com v1.0.1 // indirect
github.com/valyala/fastjson v1.6.4 // indirect
github.com/x448/float16 v0.8.4 // indirect
@@ -297,27 +258,25 @@ require (
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
- github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
github.com/zeebo/assert v1.3.0 // indirect
github.com/zeebo/blake3 v0.2.4 // indirect
- go.etcd.io/bbolt v1.3.11 // indirect
- go.mongodb.org/mongo-driver v1.17.1 // indirect
+ go.etcd.io/bbolt v1.4.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
- golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
- golang.org/x/mod v0.21.0 // indirect
- golang.org/x/time v0.7.0 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
+ go.uber.org/zap/exp v0.3.0 // indirect
+ golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
+ golang.org/x/mod v0.25.0 // indirect
+ golang.org/x/time v0.11.0 // indirect
+ golang.org/x/tools v0.33.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
-replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0
-
-replace github.com/nektos/act => gitea.com/gitea/act v0.261.3
+replace github.com/nektos/act => gitea.com/gitea/act v0.261.6
// TODO: the only difference is in `PutObject`: the fork doesn't use `NewVerifyingReader(r, sha256.New(), oid, expectedSize)`, need to figure out why
replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-transfer v0.2.0
@@ -325,6 +284,8 @@ replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-tra
// TODO: This could be removed after https://github.com/mholt/archiver/pull/396 merged
replace github.com/mholt/archiver/v3 => github.com/anchore/archiver/v3 v3.5.2
+replace git.sr.ht/~mariusor/go-xsd-duration => gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078
+
exclude github.com/gofrs/uuid v3.2.0+incompatible
exclude github.com/gofrs/uuid v4.0.0+incompatible
diff --git a/go.sum b/go.sum
index b5e64321b5..2e7c51f747 100644
--- a/go.sum
+++ b/go.sum
@@ -1,25 +1,25 @@
-cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
-cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
-code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU=
-code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas=
+cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
+cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
+code.gitea.io/actions-proto-go v0.4.1 h1:l0EYhjsgpUe/1VABo2eK7zcoNX2W44WOnb0MSLrKfls=
+code.gitea.io/actions-proto-go v0.4.1/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas=
code.gitea.io/gitea-vet v0.2.3 h1:gdFmm6WOTM65rE8FUBTRzeQZYzXePKSSB1+r574hWwI=
code.gitea.io/gitea-vet v0.2.3/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
-code.gitea.io/sdk/gitea v0.19.0 h1:8I6s1s4RHgzxiPHhOQdgim1RWIRcr0LVMbHBjBFXq4Y=
-code.gitea.io/sdk/gitea v0.19.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI=
+code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4=
+code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA=
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY=
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM=
-connectrpc.com/connect v1.17.0 h1:W0ZqMhtVzn9Zhn2yATuUokDLO5N+gIuBWMOnsQrfmZk=
-connectrpc.com/connect v1.17.0/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8=
+connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw=
+connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
-git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg=
-git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
-gitea.com/gitea/act v0.261.3 h1:BhiYpGJQKGq0XMYYICCYAN4KnsEWHyLbA6dxhZwFcV4=
-gitea.com/gitea/act v0.261.3/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
+gitea.com/gitea/act v0.261.6 h1:CjZwKOyejonNFDmsXOw3wGm5Vet573hHM6VMLsxtvPY=
+gitea.com/gitea/act v0.261.6/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
gitea.com/gitea/git-lfs-transfer v0.2.0 h1:baHaNoBSRaeq/xKayEXwiDQtlIjps4Ac/Ll4KqLMB40=
gitea.com/gitea/git-lfs-transfer v0.2.0/go.mod h1:UrXUCm3xLQkq15fu7qlXHUMlrhdlXHoi13KH2Dfiits=
+gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:BAFmdZpRW7zMQZQDClaCWobRj9uL1MR3MzpCVJvc5s4=
+gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed h1:EZZBtilMLSZNWtHHcgq2mt6NSGhJSZBuduAlinMEmso=
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed/go.mod h1:E3i3cgB04dDx0v3CytCgRTTn9Z/9x891aet3r456RVw=
gitea.com/go-chi/cache v0.2.1 h1:bfAPkvXlbcZxPCpcmDVCWoHgiBSBmZN/QosnZvEC0+g=
@@ -40,52 +40,46 @@ github.com/42wim/sshsig v0.0.0-20240818000253-e3a6333df815 h1:5EoemV++kUK2Sw98yW
github.com/42wim/sshsig v0.0.0-20240818000253-e3a6333df815/go.mod h1:zjsWZdDLrcDojDIfpQg7A6J4YZLT0cbwuAD26AppDBo=
github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U=
github.com/6543/go-version v1.3.1/go.mod h1:oqFAHCwtLVUTLdhQmVZWYvaHXTdsbB4SY85at64SQEo=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 h1:PiSrjRPpkQNjrM8H0WwKMnZUdu1RGMtd/LdGKUrOo+c=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
-github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1 h1:cf+OIKbkmMHBaC3u78AXomweqM0oxQSgBXRZf3WH4yM=
-github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1/go.mod h1:ap1dmS6vQKJxSMNiGJcq4QuUQkOynyD93gLw6MDF7ek=
+github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0 h1:UXT0o77lXQrikd1kgwIPQOUect7EoR/+sbP4wQKdzxM=
+github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0/go.mod h1:cTvi54pg19DoT07ekoeMgE/taAwNtCShVeZqA+Iv2xI=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY=
-github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
+github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
+github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74=
-github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
-github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
-github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
-github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
-github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
-github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
-github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
-github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
-github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4=
-github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4=
+github.com/ProtonMail/go-crypto v1.2.0 h1:+PhXXn4SPGd+qk76TlEePBfOfivE0zkWFenhGhFLzWs=
+github.com/ProtonMail/go-crypto v1.2.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
+github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
+github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
github.com/RoaringBitmap/roaring v0.7.1/go.mod h1:jdT9ykXwHFNdJbEtxePexlFYH9LXucApeS0/+/g+p1I=
-github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ=
-github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
+github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg=
+github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0=
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3 h1:BP0HiyNT3AQEYi+if3wkRcIdQFHtsw6xX3Kx0glckgA=
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3/go.mod h1:hMNtySovKkn2gdDuLqnqveP+mfhUSaBdoBcr2I7Zt0E=
-github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
-github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
+github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
+github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
-github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
-github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
+github.com/alecthomas/chroma/v2 v2.17.0 h1:3r2Cgk+nXNICMBxIFGnTRTbQFUwMiLisW+9uos0TtUI=
+github.com/alecthomas/chroma/v2 v2.17.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
@@ -96,27 +90,25 @@ github.com/anchore/archiver/v3 v3.5.2/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnons
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
-github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
-github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
+github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
+github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
-github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
-github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
-github.com/aws/aws-sdk-go-v2 v1.32.3 h1:T0dRlFBKcdaUPGNtkBSwHZxrtis8CQU17UpNBZYd0wk=
-github.com/aws/aws-sdk-go-v2 v1.32.3/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.42 h1:sBP0RPjBU4neGpIYyx8mkU2QqLPl5u9cmdTWVzIpHkM=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.42/go.mod h1:FwZBfU530dJ26rv9saAbxa9Ej3eF/AK0OAY86k13n4M=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 h1:Jw50LwEkVjuVzE1NzkhNKkBf9cRN7MtE1F/b2cOKTUM=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22/go.mod h1:Y/SmAyPcOTmpeVaWSzSKiILfXTVJwrGmYZhcRbhWuEY=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 h1:981MHwBaRZM7+9QSR6XamDzF/o7ouUGxFzr+nVSIhrs=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22/go.mod h1:1RA1+aBEfn+CAB/Mh0MB6LsdCYCnjZm7tKXtnk499ZQ=
-github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.3 h1:NbAYtQnTXzv4u5uesdDhXGWDTsEtTyFXlzfXELtI6cc=
-github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.3/go.mod h1:ZhgiuB7I3d3UU6PnCWwb0IYFgGzJz0VT+DK3Xv1xtF8=
-github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM=
-github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
+github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
+github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
+github.com/aws/aws-sdk-go-v2/service/codecommit v1.28.2 h1:enL75gIdaPAoBztv/GDuMgOocEUpO2jYc45qp2Uweqs=
+github.com/aws/aws-sdk-go-v2/service/codecommit v1.28.2/go.mod h1:JsdLne5QNlqJdCQFm2DbHLNmNfEWSU7HnTuvi8SIl+E=
+github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
+github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -124,20 +116,20 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bits-and-blooms/bitset v1.1.10/go.mod h1:w0XsmFg8qg6cmpTtJ0z3pKgjTDBMMnI/+I2syrE6XBE=
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
-github.com/bits-and-blooms/bitset v1.14.3 h1:Gd2c8lSNf9pKXom5JtD7AaKO8o7fGQ2LtFj1436qilA=
-github.com/bits-and-blooms/bitset v1.14.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
+github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4=
+github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/blevesearch/bleve/v2 v2.0.5/go.mod h1:ZjWibgnbRX33c+vBRgla9QhPb4QOjD6fdVJ+R1Bk8LM=
-github.com/blevesearch/bleve/v2 v2.4.2 h1:NooYP1mb3c0StkiY9/xviiq2LGSaE8BQBCc/pirMx0U=
-github.com/blevesearch/bleve/v2 v2.4.2/go.mod h1:ATNKj7Yl2oJv/lGuF4kx39bST2dveX6w0th2FFYLkc8=
+github.com/blevesearch/bleve/v2 v2.5.0 h1:HzYqBy/5/M9Ul9ESEmXzN/3Jl7YpmWBdHM/+zzv/3k4=
+github.com/blevesearch/bleve/v2 v2.5.0/go.mod h1:PcJzTPnEynO15dCf9isxOga7YFRa/cMSsbnRwnszXUk=
github.com/blevesearch/bleve_index_api v1.0.0/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4=
-github.com/blevesearch/bleve_index_api v1.1.12 h1:P4bw9/G/5rulOF7SJ9l4FsDoo7UFJ+5kexNy1RXfegY=
-github.com/blevesearch/bleve_index_api v1.1.12/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8=
-github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM=
-github.com/blevesearch/geo v0.1.20/go.mod h1:DVG2QjwHNMFmjo+ZgzrIq2sfCh6rIHzy9d9d0B59I6w=
-github.com/blevesearch/go-faiss v1.0.23 h1:Wmc5AFwDLKGl2L6mjLX1Da3vCL0EKa2uHHSorcIS1Uc=
-github.com/blevesearch/go-faiss v1.0.23/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
+github.com/blevesearch/bleve_index_api v1.2.8 h1:Y98Pu5/MdlkRyLM0qDHostYo7i+Vv1cDNhqTeR4Sy6Y=
+github.com/blevesearch/bleve_index_api v1.2.8/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
+github.com/blevesearch/geo v0.2.0 h1:f+IE3/C3mGeXDyhtMbWel6BgqBqaOUz43GtWg26GlB0=
+github.com/blevesearch/geo v0.2.0/go.mod h1:k8Hyfz12kM8QmeWLhgX7VMMCoVFmttBnr62V5zniXak=
+github.com/blevesearch/go-faiss v1.0.25 h1:lel1rkOUGbT1CJ0YgzKwC7k+XH0XVBHnCVWahdCXk4U=
+github.com/blevesearch/go-faiss v1.0.25/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
@@ -146,8 +138,8 @@ github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
github.com/blevesearch/scorch_segment_api/v2 v2.0.1/go.mod h1:lq7yK2jQy1yQjtjTfU931aVqz7pYxEudHaDwOt1tXfU=
-github.com/blevesearch/scorch_segment_api/v2 v2.2.16 h1:uGvKVvG7zvSxCwcm4/ehBa9cCEuZVE+/zvrSl57QUVY=
-github.com/blevesearch/scorch_segment_api/v2 v2.2.16/go.mod h1:VF5oHVbIFTu+znY1v30GjSpT5+9YFs9dV2hjvuh34F0=
+github.com/blevesearch/scorch_segment_api/v2 v2.3.10 h1:Yqk0XD1mE0fDZAJXTjawJ8If/85JxnLd8v5vG/jWE/s=
+github.com/blevesearch/scorch_segment_api/v2 v2.3.10/go.mod h1:Z3e6ChN3qyN35yaQpl00MfI5s8AxUJbpTR/DL8QOQ+8=
github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ=
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
@@ -158,39 +150,43 @@ github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMG
github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ=
github.com/blevesearch/vellum v1.0.3/go.mod h1:2u5ax02KeDuNWu4/C+hVQMD6uLN4txH1JbtpaDNLJRo=
github.com/blevesearch/vellum v1.0.4/go.mod h1:cMhywHI0de50f7Nj42YgvyD6bFJ2WkNRvNBlNMrEVgY=
-github.com/blevesearch/vellum v1.0.10 h1:HGPJDT2bTva12hrHepVT3rOyIKFFF4t7Gf6yMxyMIPI=
-github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
+github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w=
+github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y=
github.com/blevesearch/zapx/v11 v11.2.0/go.mod h1:gN/a0alGw1FZt/YGTo1G6Z6XpDkeOfujX5exY9sCQQM=
-github.com/blevesearch/zapx/v11 v11.3.10 h1:hvjgj9tZ9DeIqBCxKhi70TtSZYMdcFn7gDb71Xo/fvk=
-github.com/blevesearch/zapx/v11 v11.3.10/go.mod h1:0+gW+FaE48fNxoVtMY5ugtNHHof/PxCqh7CnhYdnMzQ=
+github.com/blevesearch/zapx/v11 v11.4.1 h1:qFCPlFbsEdwbbckJkysptSQOsHn4s6ZOHL5GMAIAVHA=
+github.com/blevesearch/zapx/v11 v11.4.1/go.mod h1:qNOGxIqdPC1MXauJCD9HBG487PxviTUUbmChFOAosGs=
github.com/blevesearch/zapx/v12 v12.2.0/go.mod h1:fdjwvCwWWwJW/EYTYGtAp3gBA0geCYGLcVTtJEZnY6A=
-github.com/blevesearch/zapx/v12 v12.3.10 h1:yHfj3vXLSYmmsBleJFROXuO08mS3L1qDCdDK81jDl8s=
-github.com/blevesearch/zapx/v12 v12.3.10/go.mod h1:0yeZg6JhaGxITlsS5co73aqPtM04+ycnI6D1v0mhbCs=
+github.com/blevesearch/zapx/v12 v12.4.1 h1:K77bhypII60a4v8mwvav7r4IxWA8qxhNjgF9xGdb9eQ=
+github.com/blevesearch/zapx/v12 v12.4.1/go.mod h1:QRPrlPOzAxBNMI0MkgdD+xsTqx65zbuPr3Ko4Re49II=
github.com/blevesearch/zapx/v13 v13.2.0/go.mod h1:o5rAy/lRS5JpAbITdrOHBS/TugWYbkcYZTz6VfEinAQ=
-github.com/blevesearch/zapx/v13 v13.3.10 h1:0KY9tuxg06rXxOZHg3DwPJBjniSlqEgVpxIqMGahDE8=
-github.com/blevesearch/zapx/v13 v13.3.10/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk=
+github.com/blevesearch/zapx/v13 v13.4.1 h1:EnkEMZFUK0lsW/jOJJF2xOcp+W8TjEsyeN5BeAZEYYE=
+github.com/blevesearch/zapx/v13 v13.4.1/go.mod h1:e6duBMlCvgbH9rkzNMnUa9hRI9F7ri2BRcHfphcmGn8=
github.com/blevesearch/zapx/v14 v14.2.0/go.mod h1:GNgZusc1p4ot040cBQMRGEZobvwjCquiEKYh1xLFK9g=
-github.com/blevesearch/zapx/v14 v14.3.10 h1:SG6xlsL+W6YjhX5N3aEiL/2tcWh3DO75Bnz77pSwwKU=
-github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns=
+github.com/blevesearch/zapx/v14 v14.4.1 h1:G47kGCshknBZzZAtjcnIAMn3oNx8XBLxp8DMq18ogyE=
+github.com/blevesearch/zapx/v14 v14.4.1/go.mod h1:O7sDxiaL2r2PnCXbhh1Bvm7b4sP+jp4unE9DDPWGoms=
github.com/blevesearch/zapx/v15 v15.2.0/go.mod h1:MmQceLpWfME4n1WrBFIwplhWmaQbQqLQARpaKUEOs/A=
-github.com/blevesearch/zapx/v15 v15.3.16 h1:Ct3rv7FUJPfPk99TI/OofdC+Kpb4IdyfdMH48sb+FmE=
-github.com/blevesearch/zapx/v15 v15.3.16/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg=
-github.com/blevesearch/zapx/v16 v16.1.7 h1:I07qV6l1rPda19zyof9Q2J9E8cjZ57pQhNY0+ePI5vM=
-github.com/blevesearch/zapx/v16 v16.1.7/go.mod h1:JqQlOqlRVaYDkpLIl3JnKql8u4zKTNlVEa3nLsi0Gn8=
+github.com/blevesearch/zapx/v15 v15.4.1 h1:B5IoTMUCEzFdc9FSQbhVOxAY+BO17c05866fNruiI7g=
+github.com/blevesearch/zapx/v15 v15.4.1/go.mod h1:b/MreHjYeQoLjyY2+UaM0hGZZUajEbE0xhnr1A2/Q6Y=
+github.com/blevesearch/zapx/v16 v16.2.3 h1:7Y0r+a3diEvlazsncexq1qoFOcBd64xwMS7aDm4lo1s=
+github.com/blevesearch/zapx/v16 v16.2.3/go.mod h1:wVJ+GtURAaRG9KQAMNYyklq0egV+XJlGcXNCE0OFjjA=
+github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
+github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
+github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q=
+github.com/bohde/codel v0.2.0 h1:fzF7ibgKmCfQbOzQCblmQcwzDRmV7WO7VMLm/hDvD3E=
+github.com/bohde/codel v0.2.0/go.mod h1:Idb1IRvTdwkRjIjguLIo+FXhIBhcpGl94o7xra6ggWk=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4=
github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
-github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
-github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
+github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf h1:TqhNAT4zKbTdLa62d2HDBFdvgSbIGB3eJE8HqhgiL9I=
+github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
-github.com/buildkite/terminal-to-html/v3 v3.16.3 h1:IGuJjboHjuMLWOGsKZKNxbbn41emOLiHzXPmQZk31fk=
-github.com/buildkite/terminal-to-html/v3 v3.16.3/go.mod h1:r/J7cC9c3EzBzP3/wDz0RJLPwv5PUAMp+KF2w+ntMc0=
-github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
-github.com/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0=
-github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE=
+github.com/buildkite/terminal-to-html/v3 v3.16.8 h1:QN/daUob6cmK8GcdKnwn9+YTlPr1vNj+oeAIiJK6fPc=
+github.com/buildkite/terminal-to-html/v3 v3.16.8/go.mod h1:+k1KVKROZocrTLsEQ9PEf9A+8+X8uaVV5iO1ZIOwKYM=
+github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU=
+github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4=
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
@@ -205,17 +201,16 @@ github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moA
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
-github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
-github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
-github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
+github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
+github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
github.com/couchbase/go-couchbase v0.1.1 h1:ClFXELcKj/ojyoTYbsY34QUrrYCBi/1G749sXSCkdhk=
github.com/couchbase/go-couchbase v0.1.1/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A=
-github.com/couchbase/gomemcached v0.3.2 h1:08rxiOoNcv0x5LTxgcYhnx1aPvV7iEtfeyUgqsJyPk0=
-github.com/couchbase/gomemcached v0.3.2/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo=
+github.com/couchbase/gomemcached v0.3.3 h1:D7qqXLO8wNa4pn5oE65lT3pA3IeStn4joT7/JgGXzKc=
+github.com/couchbase/gomemcached v0.3.3/go.mod h1:pISAjweI42vljCumsJIo7CVhqIMIIP9g3Wfhl1JJw68=
github.com/couchbase/goutils v0.1.2 h1:gWr8B6XNWPIhfalHNog3qQKfGiYyh4K4VhO3P2o9BCs=
github.com/couchbase/goutils v0.1.2/go.mod h1:h89Ek/tiOxxqjz30nPPlwZdQbdB8BwgnuBxeoUe/ViE=
github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs=
@@ -223,8 +218,8 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8=
-github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM=
+github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
+github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@@ -243,19 +238,19 @@ github.com/djherbis/nio/v3 v3.0.1/go.mod h1:Ng4h80pbZFMla1yKzm61cF0tqqilXZYrogmW
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
-github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
-github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
+github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
+github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/dvyukov/go-fuzz v0.0.0-20210429054444-fca39067bc72/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
-github.com/editorconfig/editorconfig-core-go/v2 v2.6.2 h1:dKG8sc7n321deIVRcQtwlMNoBEra7j0qQ8RwxO8RN0w=
-github.com/editorconfig/editorconfig-core-go/v2 v2.6.2/go.mod h1:7dvD3GCm7eBw53xZ/lsiq72LqobdMg3ITbMBxnmJmqY=
+github.com/editorconfig/editorconfig-core-go/v2 v2.6.3 h1:XVUp6qW3BIkmM3/1EkrHpa6bL56APOynfXcZEmIgOhs=
+github.com/editorconfig/editorconfig-core-go/v2 v2.6.3/go.mod h1:ThHVc+hqbUsmE1wmK/MASpQEhCleWu1JDJDNhUOMy0c=
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
-github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
-github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
+github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
+github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA=
github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
@@ -271,80 +266,53 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
-github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
-github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
-github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
-github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
-github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
-github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
-github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
+github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
+github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0=
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
-github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c h1:82lzmsy5Nr6JA6HcLRVxGfbdSoWfW45C6jnY3zFS7Ks=
-github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c/go.mod h1:rpIPGre4qtTgSpVT0zz3hycAMuLtUt7BNngVNpyXhL8=
-github.com/go-ap/errors v0.0.0-20240910140019-1e9d33cc1568 h1:eQEXAzWEijFbwtm/Hr2EtFbM0LvATRd1ltpDb+mfjQk=
-github.com/go-ap/errors v0.0.0-20240910140019-1e9d33cc1568/go.mod h1:Vkh+Z3f24K8nMsJKXo1FHn5ebPsXvB/WDH5JRtYqdNo=
+github.com/go-ap/activitypub v0.0.0-20250409143848-7113328b1f3d h1:IWrWGnmKzpHqginJ18ljKkty/X8glxM8Mg3pk6bkb8g=
+github.com/go-ap/activitypub v0.0.0-20250409143848-7113328b1f3d/go.mod h1:EUtZuXtHo4yKkTJmcbAZYW+X1G2poeT8icmBh24eq7o=
+github.com/go-ap/errors v0.0.0-20250409143711-5686c11ae650 h1:tlwla5IQUea0CuktkBd2FLDwVzts4OeTWPPkhQPSK5Q=
+github.com/go-ap/errors v0.0.0-20250409143711-5686c11ae650/go.mod h1:Vkh+Z3f24K8nMsJKXo1FHn5ebPsXvB/WDH5JRtYqdNo=
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 h1:GMKIYXyXPGIp+hYiWOhfqK4A023HdgisDT4YGgf99mw=
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5LaADntW+UEsMjl3IlIwk+DxlYNsbofQkGA=
-github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
-github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk=
-github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
+github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
+github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
-github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
-github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
+github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
+github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY=
-github.com/go-enry/go-enry/v2 v2.9.1 h1:G9iDteJ/Mc0F4Di5NeQknf83R2OkRbwY9cAYmcqVG6U=
-github.com/go-enry/go-enry/v2 v2.9.1/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8=
+github.com/go-enry/go-enry/v2 v2.9.2 h1:giOQAtCgBX08kosrX818DCQJTCNtKwoPBGu0qb6nKTY=
+github.com/go-enry/go-enry/v2 v2.9.2/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8=
github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo=
github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4=
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e h1:oRq/fiirun5HqlEWMLIcDmLpIELlG4iGbd0s8iqgPi8=
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
-github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
-github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM=
+github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
+github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
-github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
-github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
+github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ=
+github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
-github.com/go-ldap/ldap/v3 v3.4.8 h1:loKJyspcRezt2Q3ZRMq2p/0v8iOurlmeXDPw6fikSvQ=
-github.com/go-ldap/ldap/v3 v3.4.8/go.mod h1:qS3Sjlu76eHfHGpUdWkAXQTw4beih+cHsco2jXlIXrk=
-github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
-github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=
-github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w=
-github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE=
-github.com/go-openapi/inflect v0.21.0 h1:FoBjBTQEcbg2cJUWX6uwL9OyIW8eqc9k4KhN4lfbeYk=
-github.com/go-openapi/inflect v0.21.0/go.mod h1:INezMuUu7SJQc2AyR3WO0DqqYUJSj8Kb4hBd7WtjlAw=
-github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
-github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
-github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
-github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
-github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=
-github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs=
-github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ=
-github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc=
-github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
-github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
-github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=
-github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=
-github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
-github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
-github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
-github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
+github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
+github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI=
@@ -353,40 +321,37 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-redsync/redsync/v4 v4.13.0 h1:49X6GJfnbLGaIpBBREM/zA4uIMDXKAh1NDkvQ1EkZKA=
github.com/go-redsync/redsync/v4 v4.13.0/go.mod h1:HMW4Q224GZQz6x1Xc7040Yfgacukdzu7ifTDAKiyErQ=
-github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
-github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
-github.com/go-swagger/go-swagger v0.31.0 h1:H8eOYQnY2u7vNKWDNykv2xJP3pBhRG/R+SOCAmKrLlc=
-github.com/go-swagger/go-swagger v0.31.0/go.mod h1:WSigRRWEig8zV6t6Sm8Y+EmUjlzA/HoaZJ5edupq7po=
+github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
+github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
-github.com/go-webauthn/webauthn v0.11.2 h1:Fgx0/wlmkClTKlnOsdOQ+K5HcHDsDcYIvtYmfhEOSUc=
-github.com/go-webauthn/webauthn v0.11.2/go.mod h1:aOtudaF94pM71g3jRwTYYwQTG1KyTILTcZqN1srkmD0=
-github.com/go-webauthn/x v0.1.15 h1:eG1OhggBJTkDE8gUeOlGRbRe8E/PSVG26YG4AyFbwkU=
-github.com/go-webauthn/x v0.1.15/go.mod h1:pf7VI23raFLHPO9VVIs9/u1etqwAOP0S2KoHGL6WbZ8=
+github.com/go-webauthn/webauthn v0.12.3 h1:hHQl1xkUuabUU9uS+ISNCMLs9z50p9mDUZI/FmkayNE=
+github.com/go-webauthn/webauthn v0.12.3/go.mod h1:4JRe8Z3W7HIw8NGEWn2fnUwecoDzkkeach/NnvhkqGY=
+github.com/go-webauthn/x v0.1.20 h1:brEBDqfiPtNNCdS/peu8gARtq8fIPsHz0VzpPjGvgiw=
+github.com/go-webauthn/x v0.1.20/go.mod h1:n/gAc8ssZJGATM0qThE+W+vfgXiMedsWi3wf/C4lld0=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
-github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
-github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
+github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0=
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU=
-github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
-github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
-github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
+github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
+github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
+github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
-github.com/golang/geo v0.0.0-20230421003525-6adc56603217 h1:HKlyj6in2JV6wVkmQ4XmG/EIm+SCYlPZ+V4GWit7Z+I=
-github.com/golang/geo v0.0.0-20230421003525-6adc56603217/go.mod h1:8wI0hitZ3a1IxZfeH3/5I97CI8i5cLGsYe7xNhQGs9U=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
+github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
@@ -400,33 +365,37 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
-github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
+github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
+github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
+github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
+github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-github/v61 v61.0.0 h1:VwQCBwhyE9JclCI+22/7mLB1PuU9eowCXKY5pNlu1go=
-github.com/google/go-github/v61 v61.0.0/go.mod h1:0WR+KmsWX75G2EbpyGsGmradjo3IiciuI4BmdVCobQY=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/go-github/v71 v71.0.0 h1:Zi16OymGKZZMm8ZliffVVJ/Q9YZreDKONCr+WUd0Z30=
+github.com/google/go-github/v71 v71.0.0/go.mod h1:URZXObp2BLlMjwu0O8g4y6VBneUj2bCHgnI8FfgZ51M=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
-github.com/google/go-tpm v0.9.1 h1:0pGc4X//bAlmZzMKf8iz6IsDo1nYTbYJ6FZN/rg4zdM=
-github.com/google/go-tpm v0.9.1/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
+github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc=
+github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/licenseclassifier/v2 v2.0.0 h1:1Y57HHILNf4m0ABuMVb6xk4vAJYEUO0gDxNpog0pyeA=
github.com/google/licenseclassifier/v2 v2.0.0/go.mod h1:cOjbdH0kyC9R22sdQbYsFkto4NGCAc+ZSwbeThazEtM=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
-github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
-github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/pprof v0.0.0-20250422154841-e1f9c1950416 h1:1/qwHx8P72glDXdyCKesJ+/c40x71SY4q2avOxJ2iYQ=
+github.com/google/pprof v0.0.0-20250422154841-e1f9c1950416/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -439,8 +408,6 @@ github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc=
github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
-github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
-github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1 h1:LqbZZ9sNMWVjeXS4NN5oVvhMjDyLhmA1LG86oSo+IqY=
@@ -449,13 +416,8 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
-github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
-github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
-github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
-github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
-github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -467,12 +429,10 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
-github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
-github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
@@ -497,8 +457,6 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
-github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
-github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
github.com/jhillyerd/enmime v1.3.0 h1:LV5kzfLidiOr8qRGIpYYmUZCnhrPbcFAnAFUnWn99rw=
github.com/jhillyerd/enmime v1.3.0/go.mod h1:6c6jg5HdRRV2FtvVL69LjiX1M8oE0xDX9VEhV3oy4gs=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -512,16 +470,14 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
-github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 h1:cTxwSmnaqLoo+4tLukHoB9iqHOu3LmLhRmgUxZo6Vp4=
-github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
-github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
-github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
+github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
@@ -540,51 +496,47 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
-github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
-github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 h1:F/3FfGmKdiKFa8kL3YrpZ7pe9H4l4AzA1pbaOUnRvPI=
-github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0/go.mod h1:JEfTc3+2DF9Z4PXhLLvXL42zexJyh8rIq3OzUj/0rAk=
+github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ=
+github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
-github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
-github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
+github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/markbates/going v1.0.3 h1:mY45T5TvW+Xz5A6jY7lf4+NLg9D8+iuStIHyR7M8qsE=
github.com/markbates/going v1.0.3/go.mod h1:fQiT6v6yQar9UD6bd/D4Z5Afbk9J6BBVBtLiyY4gp2o=
-github.com/markbates/goth v1.80.0 h1:NnvatczZDzOs1hn9Ug+dVYf2Viwwkp/ZDX5K+GLjan8=
-github.com/markbates/goth v1.80.0/go.mod h1:4/GYHo+W6NWisrMPZnq0Yr2Q70UntNLn7KXEFhrIdAY=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/markbates/goth v1.81.0 h1:XVcCkeGWokynPV7MXvgb8pd2s3r7DS40P7931w6kdnE=
+github.com/markbates/goth v1.81.0/go.mod h1:+6z31QyUms84EHmuBY7iuqYSxyoN3njIgg9iCF/lR1k=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
-github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
-github.com/meilisearch/meilisearch-go v0.29.1-0.20241106140435-0bf60fad690a h1:F0y+3QtCG00mr4KueQWuHv1tlIQeNXhH+XAKYLhb3X4=
-github.com/meilisearch/meilisearch-go v0.29.1-0.20241106140435-0bf60fad690a/go.mod h1:NYOgjEGt/+oExD+NixreBMqxtIB0kCndXOOgpGhoqEs=
-github.com/mholt/acmez/v2 v2.0.3 h1:CgDBlEwg3QBp6s45tPQmFIBrkRIkBT4rW4orMM6p4sw=
-github.com/mholt/acmez/v2 v2.0.3/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw=
+github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
+github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
+github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
+github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
+github.com/meilisearch/meilisearch-go v0.31.0 h1:yZRhY1qJqdH8h6GFZALGtkDLyj8f9v5aJpsNMyrUmnY=
+github.com/meilisearch/meilisearch-go v0.31.0/go.mod h1:aNtyuwurDg/ggxQIcKqWH6G9g2ptc8GyY7PLY4zMn/g=
+github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
+github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
-github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
-github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
-github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
-github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
+github.com/microsoft/go-mssqldb v1.8.0 h1:7cyZ/AT7ycDsEoWPIXibd+aVKFtteUNhDGf3aobP+tw=
+github.com/microsoft/go-mssqldb v1.8.0/go.mod h1:6znkekS3T2vp0waiMhen4GPU1BiAsrP+iXHcE7a7rFo=
+github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
+github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
+github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY=
+github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
-github.com/minio/minio-go/v7 v7.0.80 h1:2mdUHXEykRdY/BigLt3Iuu1otL0JTogT0Nmltg0wujk=
-github.com/minio/minio-go/v7 v7.0.80/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0=
-github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
-github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
+github.com/minio/minio-go/v7 v7.0.91 h1:tWLZnEfo3OZl5PoXQwcwTAPNNrjyWwOh6cbZitW5JQc=
+github.com/minio/minio-go/v7 v7.0.91/go.mod h1:uvMUcGrpgeSAAI6+sD3818508nUyMULw94j2Nxku/Go=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
-github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -599,18 +551,14 @@ github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE=
github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
-github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
-github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek=
-github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o=
+github.com/niklasfasching/go-org v1.8.0 h1:WyGLaajLLp8JbQzkmapZ1y0MOzKuKV47HkZRloi+HGY=
+github.com/niklasfasching/go-org v1.8.0/go.mod h1:e2A9zJs7cdONrEGs3gvxCcaAEpwwPNPG7csDpXckMNg=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
-github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E=
@@ -627,18 +575,16 @@ github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
-github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
-github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
+github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
+github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
-github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
-github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
-github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
+github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
+github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
+github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@@ -649,25 +595,25 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
-github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
-github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
-github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
-github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
-github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
-github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
-github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
+github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
+github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
+github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
+github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
+github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/quasoft/websspi v1.1.2 h1:/mA4w0LxWlE3novvsoEL6BBA1WnjJATbjkh1kFrTidw=
github.com/quasoft/websspi v1.1.2/go.mod h1:HmVdl939dQ0WIXZhyik+ARdI03M6bQzaSEKcgpFmewk=
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
-github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
-github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
+github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
+github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/redis/rueidis v1.0.19 h1:s65oWtotzlIFN8eMPhyYwxlwLR1lUdhza2KtWprKYSo=
github.com/redis/rueidis v1.0.19/go.mod h1:8B+r5wdnjwK3lTFml5VtxjzGOQAC+5UmujoD12pDrEo=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
-github.com/rhysd/actionlint v1.7.3 h1:WD919WuLYrSCwY8VGBqJBEuzyVEIL5viXmXqRRcKOVs=
-github.com/rhysd/actionlint v1.7.3/go.mod h1:rl+8ZoX1rqnbcMWKaTyOHmw08mmb/zlmG/Zu1fY47F4=
+github.com/rhysd/actionlint v1.7.7 h1:0KgkoNTrYY7vmOCs9BW2AHxLvvpoY9nEUzgBHiPUr0k=
+github.com/rhysd/actionlint v1.7.7/go.mod h1:AE6I6vJEkNaIfWqC2GNE5spIJNhxf8NCtLEKU4NnUXg=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
@@ -676,18 +622,13 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
-github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
-github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
-github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
-github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
-github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
@@ -696,37 +637,23 @@ github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLS
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
-github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
-github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
-github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs=
-github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
-github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
+github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
+github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck=
github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
-github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
-github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
-github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
-github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
-github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
-github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc=
@@ -734,6 +661,7 @@ github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -743,17 +671,14 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
-github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
-github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
-github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ=
-github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
github.com/tstranex/u2f v1.0.0 h1:HhJkSzDDlVSVIVt7pDJwCHQj67k7A5EeBgPmeD+pVsQ=
github.com/tstranex/u2f v1.0.0/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGBSayo=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
@@ -763,17 +688,17 @@ github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
-github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
-github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
+github.com/urfave/cli-docs/v3 v3.0.0-alpha6 h1:w/l/N0xw1rO/aHRIGXJ0lDwwYFOzilup1qGvIytP3BI=
+github.com/urfave/cli-docs/v3 v3.0.0-alpha6/go.mod h1:p7Z4lg8FSTrPB9GTaNyTrK3ygffHZcK3w0cU2VE+mzU=
+github.com/urfave/cli/v3 v3.3.3 h1:byCBaVdIXuLPIDm5CYZRVG6NvT7tv1ECqdU4YzlEa3I=
+github.com/urfave/cli/v3 v3.3.3/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
-github.com/wneessen/go-mail v0.5.2 h1:MZKwgHJoRboLJ+EHMLuHpZc95wo+u1xViL/4XSswDT8=
-github.com/wneessen/go-mail v0.5.2/go.mod h1:kRroJvEq2hOSEPFRiKjN7Csrz0G1w+RpiGR3b6yo+Ck=
+github.com/wneessen/go-mail v0.6.2 h1:c6V7c8D2mz868z9WJ+8zDKtUyLfZ1++uAZmo2GRFji8=
+github.com/wneessen/go-mail v0.6.2/go.mod h1:L/PYjPK3/2ZlNb2/FjEBIn9n1rUWjW+Toy531oVmeb4=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
-github.com/xanzy/go-gitlab v0.112.0 h1:6Z0cqEooCvBMfBIHw+CgO4AKGRV8na/9781xOb0+DKw=
-github.com/xanzy/go-gitlab v0.112.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -786,8 +711,6 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
-github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
-github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js=
@@ -796,8 +719,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
-github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
+github.com/yuin/goldmark v1.7.10 h1:S+LrtBjRmqMac2UdtB6yyCEJm+UILZ2fefI4p7o0QpI=
+github.com/yuin/goldmark v1.7.10/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
@@ -808,11 +731,11 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
+gitlab.com/gitlab-org/api/client-go v0.127.0 h1:8xnxcNKGF2gDazEoMs+hOZfOspSSw8D0vAoWhQk9U+U=
+gitlab.com/gitlab-org/api/client-go v0.127.0/go.mod h1:bYC6fPORKSmtuPRyD9Z2rtbAjE7UeNatu2VWHRf4/LE=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
-go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
-go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
-go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
-go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
+go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
+go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
@@ -822,6 +745,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
+go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -829,20 +754,18 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
-golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
-golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
-golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
-golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
-golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
-golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
+golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
+golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
+golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
+golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
+golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
+golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
+golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
+golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -852,12 +775,11 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
-golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
+golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
@@ -865,33 +787,32 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
-golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
-golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
-golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
-golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
+golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
+golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
+golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
+golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -915,51 +836,45 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
+golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
-golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
-golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
-golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
-golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
+golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
+golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
+golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
+golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
-golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
-golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
-golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
+golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
+golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
+golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -970,24 +885,24 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
-golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
-golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
+golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
+golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
-google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
-google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f h1:N/PrbTw4kdkqNRzVfWPrBekzLuarFREcbFOiOLkXon4=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
+google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
+google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
-google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
+google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1009,6 +924,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
@@ -1031,8 +947,10 @@ modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
-mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8=
-mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE=
+mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI=
+mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk=
+pgregory.net/rapid v0.4.2 h1:lsi9jhvZTYvzVpeG93WWgimPRmiJQfGFRNTEZh1dtY0=
+pgregory.net/rapid v0.4.2/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs=
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo=
diff --git a/main.go b/main.go
index 756c3e0f9b..2c25bac4e3 100644
--- a/main.go
+++ b/main.go
@@ -21,7 +21,7 @@ import (
_ "code.gitea.io/gitea/modules/markup/markdown"
_ "code.gitea.io/gitea/modules/markup/orgmode"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
// these flags will be set by the build flags
diff --git a/main_timezones.go b/main_timezones.go
new file mode 100644
index 0000000000..e1233007c6
--- /dev/null
+++ b/main_timezones.go
@@ -0,0 +1,16 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+//go:build windows
+
+package main
+
+// Golang has the ability to load OS's timezone data from most UNIX systems (https://github.com/golang/go/blob/master/src/time/zoneinfo_unix.go)
+// Even if the timezone data is missing, users could install the related packages to get it.
+// But on Windows, although `zoneinfo_windows.go` tries to load the timezone data from Windows registry,
+// some users still suffer from the issue that the timezone data is missing: https://github.com/go-gitea/gitea/issues/33235
+// So we import the tzdata package to make sure the timezone data is included in the binary.
+//
+// For non-Windows package builders, they could still use the "TAGS=timetzdata" to include the tzdata package in the binary.
+// If we decided to add the tzdata for other platforms, modify the "go:build" directive above.
+import _ "time/tzdata"
diff --git a/models/actions/artifact.go b/models/actions/artifact.go
index 0bc66ba24e..757bd13acd 100644
--- a/models/actions/artifact.go
+++ b/models/actions/artifact.go
@@ -30,6 +30,25 @@ const (
ArtifactStatusDeleted // 6, ArtifactStatusDeleted is the status of an artifact that is deleted
)
+func (status ArtifactStatus) ToString() string {
+ switch status {
+ case ArtifactStatusUploadPending:
+ return "upload is not yet completed"
+ case ArtifactStatusUploadConfirmed:
+ return "upload is completed"
+ case ArtifactStatusUploadError:
+ return "upload failed"
+ case ArtifactStatusExpired:
+ return "expired"
+ case ArtifactStatusPendingDeletion:
+ return "pending deletion"
+ case ArtifactStatusDeleted:
+ return "deleted"
+ default:
+ return "unknown"
+ }
+}
+
func init() {
db.RegisterModel(new(ActionArtifact))
}
@@ -48,7 +67,7 @@ type ActionArtifact struct {
ContentEncoding string // The content encoding of the artifact
ArtifactPath string `xorm:"index unique(runid_name_path)"` // The path to the artifact when runner uploads it
ArtifactName string `xorm:"index unique(runid_name_path)"` // The name of the artifact when runner uploads it
- Status int64 `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
+ Status ArtifactStatus `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated index"`
ExpiredUnix timeutil.TimeStamp `xorm:"index"` // The time when the artifact will be expired
@@ -68,7 +87,7 @@ func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPa
RepoID: t.RepoID,
OwnerID: t.OwnerID,
CommitSHA: t.CommitSHA,
- Status: int64(ArtifactStatusUploadPending),
+ Status: ArtifactStatusUploadPending,
ExpiredUnix: timeutil.TimeStamp(time.Now().Unix() + timeutil.Day*expiredDays),
}
if _, err := db.GetEngine(ctx).Insert(artifact); err != nil {
@@ -108,12 +127,19 @@ func UpdateArtifactByID(ctx context.Context, id int64, art *ActionArtifact) erro
type FindArtifactsOptions struct {
db.ListOptions
- RepoID int64
- RunID int64
- ArtifactName string
- Status int
+ RepoID int64
+ RunID int64
+ ArtifactName string
+ Status int
+ FinalizedArtifactsV4 bool
}
+func (opts FindArtifactsOptions) ToOrders() string {
+ return "id"
+}
+
+var _ db.FindOptionsOrder = (*FindArtifactsOptions)(nil)
+
func (opts FindArtifactsOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RepoID > 0 {
@@ -128,11 +154,15 @@ func (opts FindArtifactsOptions) ToConds() builder.Cond {
if opts.Status > 0 {
cond = cond.And(builder.Eq{"status": opts.Status})
}
+ if opts.FinalizedArtifactsV4 {
+ cond = cond.And(builder.Eq{"status": ArtifactStatusUploadConfirmed}.Or(builder.Eq{"status": ArtifactStatusExpired}))
+ cond = cond.And(builder.Eq{"content_encoding": "application/zip"})
+ }
return cond
}
-// ActionArtifactMeta is the meta data of an artifact
+// ActionArtifactMeta is the meta-data of an artifact
type ActionArtifactMeta struct {
ArtifactName string
FileSize int64
@@ -166,18 +196,18 @@ func ListPendingDeleteArtifacts(ctx context.Context, limit int) ([]*ActionArtifa
// SetArtifactExpired sets an artifact to expired
func SetArtifactExpired(ctx context.Context, artifactID int64) error {
- _, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusExpired)})
+ _, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusExpired})
return err
}
// SetArtifactNeedDelete sets an artifact to need-delete, cron job will delete it
func SetArtifactNeedDelete(ctx context.Context, runID int64, name string) error {
- _, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusPendingDeletion)})
+ _, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusPendingDeletion})
return err
}
// SetArtifactDeleted sets an artifact to deleted
func SetArtifactDeleted(ctx context.Context, artifactID int64) error {
- _, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusDeleted)})
+ _, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusDeleted})
return err
}
diff --git a/models/actions/run.go b/models/actions/run.go
index a224a910ab..f0ab61b200 100644
--- a/models/actions/run.go
+++ b/models/actions/run.go
@@ -5,6 +5,7 @@ package actions
import (
"context"
+ "errors"
"fmt"
"slices"
"strings"
@@ -15,6 +16,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
@@ -88,7 +90,7 @@ func (run *ActionRun) RefLink() string {
if refName.IsPull() {
return run.Repo.Link() + "/pulls/" + refName.ShortName()
}
- return git.RefURL(run.Repo.Link(), run.Ref)
+ return run.Repo.Link() + "/src/" + refName.RefWebLinkPath()
}
// PrettyRef return #id for pull ref or ShortName for others
@@ -154,7 +156,7 @@ func (run *ActionRun) GetPushEventPayload() (*api.PushPayload, error) {
}
func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, error) {
- if run.Event == webhook_module.HookEventPullRequest || run.Event == webhook_module.HookEventPullRequestSync {
+ if run.Event.IsPullRequest() {
var payload api.PullRequestPayload
if err := json.Unmarshal([]byte(run.EventPayload), &payload); err != nil {
return nil, err
@@ -164,12 +166,24 @@ func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, err
return nil, fmt.Errorf("event %s is not a pull request event", run.Event)
}
+func (run *ActionRun) GetWorkflowRunEventPayload() (*api.WorkflowRunPayload, error) {
+ if run.Event == webhook_module.HookEventWorkflowRun {
+ var payload api.WorkflowRunPayload
+ if err := json.Unmarshal([]byte(run.EventPayload), &payload); err != nil {
+ return nil, err
+ }
+ return &payload, nil
+ }
+ return nil, fmt.Errorf("event %s is not a workflow run event", run.Event)
+}
+
func (run *ActionRun) IsSchedule() bool {
return run.ScheduleID > 0
}
func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error {
_, err := db.GetEngine(ctx).ID(repo.ID).
+ NoAutoTime().
SetExpr("num_action_runs",
builder.Select("count(*)").From("action_run").
Where(builder.Eq{"repo_id": repo.ID}),
@@ -194,7 +208,7 @@ func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) err
// CancelPreviousJobs cancels all previous jobs of the same repository, reference, workflow, and event.
// It's useful when a new run is triggered, and all previous runs needn't be continued anymore.
-func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) error {
+func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) ([]*ActionRunJob, error) {
// Find all runs in the specified repository, reference, and workflow with non-final status
runs, total, err := db.FindAndCount[ActionRun](ctx, FindRunOptions{
RepoID: repoID,
@@ -204,14 +218,16 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
Status: []Status{StatusRunning, StatusWaiting, StatusBlocked},
})
if err != nil {
- return err
+ return nil, err
}
// If there are no runs found, there's no need to proceed with cancellation, so return nil.
if total == 0 {
- return nil
+ return nil, nil
}
+ cancelledJobs := make([]*ActionRunJob, 0, total)
+
// Iterate over each found run and cancel its associated jobs.
for _, run := range runs {
// Find all jobs associated with the current run.
@@ -219,7 +235,7 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
RunID: run.ID,
})
if err != nil {
- return err
+ return cancelledJobs, err
}
// Iterate over each job and attempt to cancel it.
@@ -238,27 +254,29 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
// Update the job's status and stopped time in the database.
n, err := UpdateRunJob(ctx, job, builder.Eq{"task_id": 0}, "status", "stopped")
if err != nil {
- return err
+ return cancelledJobs, err
}
// If the update affected 0 rows, it means the job has changed in the meantime, so we need to try again.
if n == 0 {
- return fmt.Errorf("job has changed, try again")
+ return cancelledJobs, errors.New("job has changed, try again")
}
+ cancelledJobs = append(cancelledJobs, job)
// Continue with the next job.
continue
}
// If the job has an associated task, try to stop the task, effectively cancelling the job.
if err := StopTask(ctx, job.TaskID, StatusCancelled); err != nil {
- return err
+ return cancelledJobs, err
}
+ cancelledJobs = append(cancelledJobs, job)
}
}
// Return nil to indicate successful cancellation of all running and waiting jobs.
- return nil
+ return cancelledJobs, nil
}
// InsertRun inserts a run
@@ -337,13 +355,13 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
return committer.Commit()
}
-func GetRunByID(ctx context.Context, id int64) (*ActionRun, error) {
+func GetRunByRepoAndID(ctx context.Context, repoID, runID int64) (*ActionRun, error) {
var run ActionRun
- has, err := db.GetEngine(ctx).Where("id=?", id).Get(&run)
+ has, err := db.GetEngine(ctx).Where("id=? AND repo_id=?", runID, repoID).Get(&run)
if err != nil {
return nil, err
} else if !has {
- return nil, fmt.Errorf("run with id %d: %w", id, util.ErrNotExist)
+ return nil, fmt.Errorf("run with id %d: %w", runID, util.ErrNotExist)
}
return &run, nil
@@ -408,23 +426,16 @@ func UpdateRun(ctx context.Context, run *ActionRun, cols ...string) error {
return err
}
if affected == 0 {
- return fmt.Errorf("run has changed")
+ return errors.New("run has changed")
// It's impossible that the run is not found, since Gitea never deletes runs.
}
if run.Status != 0 || slices.Contains(cols, "status") {
if run.RepoID == 0 {
- run, err = GetRunByID(ctx, run.ID)
- if err != nil {
- return err
- }
+ setting.PanicInDevOrTesting("RepoID should not be 0")
}
- if run.Repo == nil {
- repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
- if err != nil {
- return err
- }
- run.Repo = repo
+ if err = run.LoadRepo(ctx); err != nil {
+ return err
}
if err := updateRepoRunsNumbers(ctx, run.Repo); err != nil {
return err
diff --git a/models/actions/run_job.go b/models/actions/run_job.go
index de4b6aab66..bad895036d 100644
--- a/models/actions/run_job.go
+++ b/models/actions/run_job.go
@@ -10,6 +10,7 @@ import (
"time"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
@@ -19,11 +20,12 @@ import (
// ActionRunJob represents a job of a run
type ActionRunJob struct {
ID int64
- RunID int64 `xorm:"index"`
- Run *ActionRun `xorm:"-"`
- RepoID int64 `xorm:"index"`
- OwnerID int64 `xorm:"index"`
- CommitSHA string `xorm:"index"`
+ RunID int64 `xorm:"index"`
+ Run *ActionRun `xorm:"-"`
+ RepoID int64 `xorm:"index"`
+ Repo *repo_model.Repository `xorm:"-"`
+ OwnerID int64 `xorm:"index"`
+ CommitSHA string `xorm:"index"`
IsForkPullRequest bool
Name string `xorm:"VARCHAR(255)"`
Attempt int64
@@ -49,7 +51,7 @@ func (job *ActionRunJob) Duration() time.Duration {
func (job *ActionRunJob) LoadRun(ctx context.Context) error {
if job.Run == nil {
- run, err := GetRunByID(ctx, job.RunID)
+ run, err := GetRunByRepoAndID(ctx, job.RepoID, job.RunID)
if err != nil {
return err
}
@@ -58,6 +60,17 @@ func (job *ActionRunJob) LoadRun(ctx context.Context) error {
return nil
}
+func (job *ActionRunJob) LoadRepo(ctx context.Context) error {
+ if job.Repo == nil {
+ repo, err := repo_model.GetRepositoryByID(ctx, job.RepoID)
+ if err != nil {
+ return err
+ }
+ job.Repo = repo
+ }
+ return nil
+}
+
// LoadAttributes load Run if not loaded
func (job *ActionRunJob) LoadAttributes(ctx context.Context) error {
if job == nil {
@@ -83,7 +96,7 @@ func GetRunJobByID(ctx context.Context, id int64) (*ActionRunJob, error) {
return &job, nil
}
-func GetRunJobsByRunID(ctx context.Context, runID int64) ([]*ActionRunJob, error) {
+func GetRunJobsByRunID(ctx context.Context, runID int64) (ActionJobList, error) {
var jobs []*ActionRunJob
if err := db.GetEngine(ctx).Where("run_id=?", runID).OrderBy("id").Find(&jobs); err != nil {
return nil, err
@@ -129,7 +142,7 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
{
// Other goroutines may aggregate the status of the run and update it too.
// So we need load the run and its jobs before updating the run.
- run, err := GetRunByID(ctx, job.RunID)
+ run, err := GetRunByRepoAndID(ctx, job.RepoID, job.RunID)
if err != nil {
return 0, err
}
@@ -172,10 +185,10 @@ func AggregateJobStatus(jobs []*ActionRunJob) Status {
return StatusSuccess
case hasCancelled:
return StatusCancelled
- case hasFailure:
- return StatusFailure
case hasRunning:
return StatusRunning
+ case hasFailure:
+ return StatusFailure
case hasWaiting:
return StatusWaiting
case hasBlocked:
diff --git a/models/actions/run_job_list.go b/models/actions/run_job_list.go
index 6c5d3b3252..5f7bb62878 100644
--- a/models/actions/run_job_list.go
+++ b/models/actions/run_job_list.go
@@ -7,6 +7,7 @@ import (
"context"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/timeutil"
@@ -21,7 +22,33 @@ func (jobs ActionJobList) GetRunIDs() []int64 {
})
}
+func (jobs ActionJobList) LoadRepos(ctx context.Context) error {
+ repoIDs := container.FilterSlice(jobs, func(j *ActionRunJob) (int64, bool) {
+ return j.RepoID, j.RepoID != 0 && j.Repo == nil
+ })
+ if len(repoIDs) == 0 {
+ return nil
+ }
+
+ repos := make(map[int64]*repo_model.Repository, len(repoIDs))
+ if err := db.GetEngine(ctx).In("id", repoIDs).Find(&repos); err != nil {
+ return err
+ }
+ for _, j := range jobs {
+ if j.RepoID > 0 && j.Repo == nil {
+ j.Repo = repos[j.RepoID]
+ }
+ }
+ return nil
+}
+
func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error {
+ if withRepo {
+ if err := jobs.LoadRepos(ctx); err != nil {
+ return err
+ }
+ }
+
runIDs := jobs.GetRunIDs()
runs := make(map[int64]*ActionRun, len(runIDs))
if err := db.GetEngine(ctx).In("id", runIDs).Find(&runs); err != nil {
@@ -30,15 +57,9 @@ func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error {
for _, j := range jobs {
if j.RunID > 0 && j.Run == nil {
j.Run = runs[j.RunID]
+ j.Run.Repo = j.Repo
}
}
- if withRepo {
- var runsList RunList = make([]*ActionRun, 0, len(runs))
- for _, r := range runs {
- runsList = append(runsList, r)
- }
- return runsList.LoadRepos(ctx)
- }
return nil
}
@@ -59,22 +80,31 @@ type FindRunJobOptions struct {
func (opts FindRunJobOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RunID > 0 {
- cond = cond.And(builder.Eq{"run_id": opts.RunID})
+ cond = cond.And(builder.Eq{"`action_run_job`.run_id": opts.RunID})
}
if opts.RepoID > 0 {
- cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
- }
- if opts.OwnerID > 0 {
- cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
+ cond = cond.And(builder.Eq{"`action_run_job`.repo_id": opts.RepoID})
}
if opts.CommitSHA != "" {
- cond = cond.And(builder.Eq{"commit_sha": opts.CommitSHA})
+ cond = cond.And(builder.Eq{"`action_run_job`.commit_sha": opts.CommitSHA})
}
if len(opts.Statuses) > 0 {
- cond = cond.And(builder.In("status", opts.Statuses))
+ cond = cond.And(builder.In("`action_run_job`.status", opts.Statuses))
}
if opts.UpdatedBefore > 0 {
- cond = cond.And(builder.Lt{"updated": opts.UpdatedBefore})
+ cond = cond.And(builder.Lt{"`action_run_job`.updated": opts.UpdatedBefore})
}
return cond
}
+
+func (opts FindRunJobOptions) ToJoins() []db.JoinFunc {
+ if opts.OwnerID > 0 {
+ return []db.JoinFunc{
+ func(sess db.Engine) error {
+ sess.Join("INNER", "repository", "repository.id = repo_id AND repository.owner_id = ?", opts.OwnerID)
+ return nil
+ },
+ }
+ }
+ return nil
+}
diff --git a/models/actions/run_job_status_test.go b/models/actions/run_job_status_test.go
index 523d38327e..2a5eb00a6f 100644
--- a/models/actions/run_job_status_test.go
+++ b/models/actions/run_job_status_test.go
@@ -58,14 +58,14 @@ func TestAggregateJobStatus(t *testing.T) {
{[]Status{StatusCancelled, StatusRunning}, StatusCancelled},
{[]Status{StatusCancelled, StatusBlocked}, StatusCancelled},
- // failure with other status, fail fast
- // Should "running" win? Maybe no: old code does make "running" win, but GitHub does fail fast.
+ // failure with other status, usually fail fast, but "running" wins to match GitHub's behavior
+ // another reason that we can't make "failure" wins over "running": it would cause a weird behavior that user cannot cancel a workflow or get current running workflows correctly by filter after a job fail.
{[]Status{StatusFailure}, StatusFailure},
{[]Status{StatusFailure, StatusSuccess}, StatusFailure},
{[]Status{StatusFailure, StatusSkipped}, StatusFailure},
{[]Status{StatusFailure, StatusCancelled}, StatusCancelled},
{[]Status{StatusFailure, StatusWaiting}, StatusFailure},
- {[]Status{StatusFailure, StatusRunning}, StatusFailure},
+ {[]Status{StatusFailure, StatusRunning}, StatusRunning},
{[]Status{StatusFailure, StatusBlocked}, StatusFailure},
// skipped with other status
diff --git a/models/actions/run_list.go b/models/actions/run_list.go
index 4046c7d369..12c55e538e 100644
--- a/models/actions/run_list.go
+++ b/models/actions/run_list.go
@@ -10,6 +10,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
+ "code.gitea.io/gitea/modules/translation"
webhook_module "code.gitea.io/gitea/modules/webhook"
"xorm.io/builder"
@@ -71,39 +72,50 @@ type FindRunOptions struct {
TriggerEvent webhook_module.HookEventType
Approved bool // not util.OptionalBool, it works only when it's true
Status []Status
+ CommitSHA string
}
func (opts FindRunOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RepoID > 0 {
- cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
- }
- if opts.OwnerID > 0 {
- cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
+ cond = cond.And(builder.Eq{"`action_run`.repo_id": opts.RepoID})
}
if opts.WorkflowID != "" {
- cond = cond.And(builder.Eq{"workflow_id": opts.WorkflowID})
+ cond = cond.And(builder.Eq{"`action_run`.workflow_id": opts.WorkflowID})
}
if opts.TriggerUserID > 0 {
- cond = cond.And(builder.Eq{"trigger_user_id": opts.TriggerUserID})
+ cond = cond.And(builder.Eq{"`action_run`.trigger_user_id": opts.TriggerUserID})
}
if opts.Approved {
- cond = cond.And(builder.Gt{"approved_by": 0})
+ cond = cond.And(builder.Gt{"`action_run`.approved_by": 0})
}
if len(opts.Status) > 0 {
- cond = cond.And(builder.In("status", opts.Status))
+ cond = cond.And(builder.In("`action_run`.status", opts.Status))
}
if opts.Ref != "" {
- cond = cond.And(builder.Eq{"ref": opts.Ref})
+ cond = cond.And(builder.Eq{"`action_run`.ref": opts.Ref})
}
if opts.TriggerEvent != "" {
- cond = cond.And(builder.Eq{"trigger_event": opts.TriggerEvent})
+ cond = cond.And(builder.Eq{"`action_run`.trigger_event": opts.TriggerEvent})
+ }
+ if opts.CommitSHA != "" {
+ cond = cond.And(builder.Eq{"`action_run`.commit_sha": opts.CommitSHA})
}
return cond
}
+func (opts FindRunOptions) ToJoins() []db.JoinFunc {
+ if opts.OwnerID > 0 {
+ return []db.JoinFunc{func(sess db.Engine) error {
+ sess.Join("INNER", "repository", "repository.id = repo_id AND repository.owner_id = ?", opts.OwnerID)
+ return nil
+ }}
+ }
+ return nil
+}
+
func (opts FindRunOptions) ToOrders() string {
- return "`id` DESC"
+ return "`action_run`.`id` DESC"
}
type StatusInfo struct {
@@ -112,14 +124,14 @@ type StatusInfo struct {
}
// GetStatusInfoList returns a slice of StatusInfo
-func GetStatusInfoList(ctx context.Context) []StatusInfo {
+func GetStatusInfoList(ctx context.Context, lang translation.Locale) []StatusInfo {
// same as those in aggregateJobStatus
allStatus := []Status{StatusSuccess, StatusFailure, StatusWaiting, StatusRunning}
statusInfoList := make([]StatusInfo, 0, 4)
for _, s := range allStatus {
statusInfoList = append(statusInfoList, StatusInfo{
Status: int(s),
- DisplayedStatus: s.String(),
+ DisplayedStatus: s.LocaleString(lang),
})
}
return statusInfoList
diff --git a/models/actions/runner.go b/models/actions/runner.go
index 0d5464a5be..81d4249ae0 100644
--- a/models/actions/runner.go
+++ b/models/actions/runner.go
@@ -5,6 +5,7 @@ package actions
import (
"context"
+ "errors"
"fmt"
"strings"
"time"
@@ -14,6 +15,7 @@ import (
"code.gitea.io/gitea/models/shared/types"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/optional"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/util"
@@ -57,6 +59,8 @@ type ActionRunner struct {
// Store labels defined in state file (default: .runner file) of `act_runner`
AgentLabels []string `xorm:"TEXT"`
+ // Store if this is a runner that only ever get one single job assigned
+ Ephemeral bool `xorm:"ephemeral NOT NULL DEFAULT false"`
Created timeutil.TimeStamp `xorm:"created"`
Updated timeutil.TimeStamp `xorm:"updated"`
@@ -84,9 +88,10 @@ func (r *ActionRunner) BelongsToOwnerType() types.OwnerType {
return types.OwnerTypeRepository
}
if r.OwnerID != 0 {
- if r.Owner.Type == user_model.UserTypeOrganization {
+ switch r.Owner.Type {
+ case user_model.UserTypeOrganization:
return types.OwnerTypeOrganization
- } else if r.Owner.Type == user_model.UserTypeIndividual {
+ case user_model.UserTypeIndividual:
return types.OwnerTypeIndividual
}
}
@@ -120,8 +125,15 @@ func (r *ActionRunner) IsOnline() bool {
return false
}
-// Editable checks if the runner is editable by the user
-func (r *ActionRunner) Editable(ownerID, repoID int64) bool {
+// EditableInContext checks if the runner is editable by the "context" owner/repo
+// ownerID == 0 and repoID == 0 means "admin" context, any runner including global runners could be edited
+// ownerID == 0 and repoID != 0 means "repo" context, any runner belonging to the given repo could be edited
+// ownerID != 0 and repoID == 0 means "owner(org/user)" context, any runner belonging to the given user/org could be edited
+// ownerID != 0 and repoID != 0 means "owner" OR "repo" context, legacy behavior, but we should forbid using it
+func (r *ActionRunner) EditableInContext(ownerID, repoID int64) bool {
+ if ownerID != 0 && repoID != 0 {
+ setting.PanicInDevOrTesting("ownerID and repoID should not be both set")
+ }
if ownerID == 0 && repoID == 0 {
return true
}
@@ -165,8 +177,15 @@ func init() {
db.RegisterModel(&ActionRunner{})
}
+// FindRunnerOptions
+// ownerID == 0 and repoID == 0 means any runner including global runners
+// repoID != 0 and WithAvailable == false means any runner for the given repo
+// repoID != 0 and WithAvailable == true means any runner for the given repo, parent user/org, and global runners
+// ownerID != 0 and repoID == 0 and WithAvailable == false means any runner for the given user/org
+// ownerID != 0 and repoID == 0 and WithAvailable == true means any runner for the given user/org and global runners
type FindRunnerOptions struct {
db.ListOptions
+ IDs []int64
RepoID int64
OwnerID int64 // it will be ignored if RepoID is set
Sort string
@@ -178,6 +197,14 @@ type FindRunnerOptions struct {
func (opts FindRunnerOptions) ToConds() builder.Cond {
cond := builder.NewCond()
+ if len(opts.IDs) > 0 {
+ if len(opts.IDs) == 1 {
+ cond = cond.And(builder.Eq{"id": opts.IDs[0]})
+ } else {
+ cond = cond.And(builder.In("id", opts.IDs))
+ }
+ }
+
if opts.RepoID > 0 {
c := builder.NewCond().And(builder.Eq{"repo_id": opts.RepoID})
if opts.WithAvailable {
@@ -272,6 +299,23 @@ func DeleteRunner(ctx context.Context, id int64) error {
return err
}
+// DeleteEphemeralRunner deletes a ephemeral runner by given ID.
+func DeleteEphemeralRunner(ctx context.Context, id int64) error {
+ runner, err := GetRunnerByID(ctx, id)
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ return nil
+ }
+ return err
+ }
+ if !runner.Ephemeral {
+ return nil
+ }
+
+ _, err = db.DeleteByID[ActionRunner](ctx, id)
+ return err
+}
+
// CreateRunner creates new runner.
func CreateRunner(ctx context.Context, t *ActionRunner) error {
if t.OwnerID != 0 && t.RepoID != 0 {
@@ -328,3 +372,17 @@ func FixRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
}
return res.RowsAffected()
}
+
+func CountWrongRepoLevelRunners(ctx context.Context) (int64, error) {
+ var result int64
+ _, err := db.GetEngine(ctx).SQL("SELECT count(`id`) FROM `action_runner` WHERE `repo_id` > 0 AND `owner_id` > 0").Get(&result)
+ return result, err
+}
+
+func UpdateWrongRepoLevelRunners(ctx context.Context) (int64, error) {
+ result, err := db.GetEngine(ctx).Exec("UPDATE `action_runner` SET `owner_id` = 0 WHERE `repo_id` > 0 AND `owner_id` > 0")
+ if err != nil {
+ return 0, err
+ }
+ return result.RowsAffected()
+}
diff --git a/models/actions/runner_token_test.go b/models/actions/runner_token_test.go
index 159805e5f7..21614b7086 100644
--- a/models/actions/runner_token_test.go
+++ b/models/actions/runner_token_test.go
@@ -17,7 +17,7 @@ func TestGetLatestRunnerToken(t *testing.T) {
token := unittest.AssertExistsAndLoadBean(t, &ActionRunnerToken{ID: 3})
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
assert.NoError(t, err)
- assert.EqualValues(t, expectedToken, token)
+ assert.Equal(t, expectedToken, token)
}
func TestNewRunnerToken(t *testing.T) {
@@ -26,7 +26,7 @@ func TestNewRunnerToken(t *testing.T) {
assert.NoError(t, err)
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
assert.NoError(t, err)
- assert.EqualValues(t, expectedToken, token)
+ assert.Equal(t, expectedToken, token)
}
func TestUpdateRunnerToken(t *testing.T) {
@@ -36,5 +36,5 @@ func TestUpdateRunnerToken(t *testing.T) {
assert.NoError(t, UpdateRunnerToken(db.DefaultContext, token))
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
assert.NoError(t, err)
- assert.EqualValues(t, expectedToken, token)
+ assert.Equal(t, expectedToken, token)
}
diff --git a/models/actions/schedule.go b/models/actions/schedule.go
index e2cc32eedc..2edf483fe0 100644
--- a/models/actions/schedule.go
+++ b/models/actions/schedule.go
@@ -43,15 +43,12 @@ func init() {
// GetSchedulesMapByIDs returns the schedules by given id slice.
func GetSchedulesMapByIDs(ctx context.Context, ids []int64) (map[int64]*ActionSchedule, error) {
schedules := make(map[int64]*ActionSchedule, len(ids))
+ if len(ids) == 0 {
+ return schedules, nil
+ }
return schedules, db.GetEngine(ctx).In("id", ids).Find(&schedules)
}
-// GetReposMapByIDs returns the repos by given id slice.
-func GetReposMapByIDs(ctx context.Context, ids []int64) (map[int64]*repo_model.Repository, error) {
- repos := make(map[int64]*repo_model.Repository, len(ids))
- return repos, db.GetEngine(ctx).In("id", ids).Find(&repos)
-}
-
// CreateScheduleTask creates new schedule task.
func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error {
// Return early if there are no rows to insert
@@ -120,21 +117,22 @@ func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error {
return committer.Commit()
}
-func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) error {
+func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) ([]*ActionRunJob, error) {
// If actions disabled when there is schedule task, this will remove the outdated schedule tasks
// There is no other place we can do this because the app.ini will be changed manually
if err := DeleteScheduleTaskByRepo(ctx, repo.ID); err != nil {
- return fmt.Errorf("DeleteCronTaskByRepo: %v", err)
+ return nil, fmt.Errorf("DeleteCronTaskByRepo: %v", err)
}
// cancel running cron jobs of this repository and delete old schedules
- if err := CancelPreviousJobs(
+ jobs, err := CancelPreviousJobs(
ctx,
repo.ID,
repo.DefaultBranch,
"",
webhook_module.HookEventSchedule,
- ); err != nil {
- return fmt.Errorf("CancelPreviousJobs: %v", err)
+ )
+ if err != nil {
+ return jobs, fmt.Errorf("CancelPreviousJobs: %v", err)
}
- return nil
+ return jobs, nil
}
diff --git a/models/actions/schedule_spec_list.go b/models/actions/schedule_spec_list.go
index f7dac72f8b..e26b2c1120 100644
--- a/models/actions/schedule_spec_list.go
+++ b/models/actions/schedule_spec_list.go
@@ -32,7 +32,7 @@ func (specs SpecList) LoadSchedules(ctx context.Context) error {
}
repoIDs := specs.GetRepoIDs()
- repos, err := GetReposMapByIDs(ctx, repoIDs)
+ repos, err := repo_model.GetRepositoriesMapByIDs(ctx, repoIDs)
if err != nil {
return err
}
diff --git a/models/actions/status.go b/models/actions/status.go
index eda2234137..2b1d70613c 100644
--- a/models/actions/status.go
+++ b/models/actions/status.go
@@ -4,6 +4,8 @@
package actions
import (
+ "slices"
+
"code.gitea.io/gitea/modules/translation"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
@@ -88,12 +90,7 @@ func (s Status) IsBlocked() bool {
// In returns whether s is one of the given statuses
func (s Status) In(statuses ...Status) bool {
- for _, v := range statuses {
- if s == v {
- return true
- }
- }
- return false
+ return slices.Contains(statuses, s)
}
func (s Status) AsResult() runnerv1.Result {
diff --git a/models/actions/task.go b/models/actions/task.go
index 9f13ff94c9..e0756b10c2 100644
--- a/models/actions/task.go
+++ b/models/actions/task.go
@@ -6,6 +6,7 @@ package actions
import (
"context"
"crypto/subtle"
+ "errors"
"fmt"
"time"
@@ -277,14 +278,13 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
return nil, false, err
}
- var workflowJob *jobparser.Job
- if gots, err := jobparser.Parse(job.WorkflowPayload); err != nil {
+ parsedWorkflows, err := jobparser.Parse(job.WorkflowPayload)
+ if err != nil {
return nil, false, fmt.Errorf("parse workflow of job %d: %w", job.ID, err)
- } else if len(gots) != 1 {
+ } else if len(parsedWorkflows) != 1 {
return nil, false, fmt.Errorf("workflow of job %d: not single workflow", job.ID)
- } else { //nolint:revive
- _, workflowJob = gots[0].Job()
}
+ _, workflowJob := parsedWorkflows[0].Job()
if _, err := e.Insert(task); err != nil {
return nil, false, err
@@ -335,6 +335,11 @@ func UpdateTask(ctx context.Context, task *ActionTask, cols ...string) error {
sess.Cols(cols...)
}
_, err := sess.Update(task)
+
+ // Automatically delete the ephemeral runner if the task is done
+ if err == nil && task.Status.IsDone() && util.SliceContainsString(cols, "status") {
+ return DeleteEphemeralRunner(ctx, task.RunnerID)
+ }
return err
}
@@ -361,7 +366,7 @@ func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.Task
} else if !has {
return nil, util.ErrNotExist
} else if runnerID != task.RunnerID {
- return nil, fmt.Errorf("invalid runner for task")
+ return nil, errors.New("invalid runner for task")
}
if task.Status.IsDone() {
diff --git a/models/actions/task_list.go b/models/actions/task_list.go
index df4b43c5ef..0c80397899 100644
--- a/models/actions/task_list.go
+++ b/models/actions/task_list.go
@@ -48,6 +48,7 @@ func (tasks TaskList) LoadAttributes(ctx context.Context) error {
type FindTaskOptions struct {
db.ListOptions
RepoID int64
+ JobID int64
OwnerID int64
CommitSHA string
Status Status
@@ -61,6 +62,9 @@ func (opts FindTaskOptions) ToConds() builder.Cond {
if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
}
+ if opts.JobID > 0 {
+ cond = cond.And(builder.Eq{"job_id": opts.JobID})
+ }
if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
}
diff --git a/models/actions/utils.go b/models/actions/utils.go
index 12657942fc..f6ba661ae3 100644
--- a/models/actions/utils.go
+++ b/models/actions/utils.go
@@ -82,3 +82,22 @@ func calculateDuration(started, stopped timeutil.TimeStamp, status Status) time.
}
return timeSince(s).Truncate(time.Second)
}
+
+// best effort function to convert an action schedule to action run, to be used in GenerateGiteaContext
+func (s *ActionSchedule) ToActionRun() *ActionRun {
+ return &ActionRun{
+ Title: s.Title,
+ RepoID: s.RepoID,
+ Repo: s.Repo,
+ OwnerID: s.OwnerID,
+ WorkflowID: s.WorkflowID,
+ TriggerUserID: s.TriggerUserID,
+ TriggerUser: s.TriggerUser,
+ Ref: s.Ref,
+ CommitSHA: s.CommitSHA,
+ Event: s.Event,
+ EventPayload: s.EventPayload,
+ Created: s.Created,
+ Updated: s.Updated,
+ }
+}
diff --git a/models/actions/variable.go b/models/actions/variable.go
index d0f917d923..7154843c17 100644
--- a/models/actions/variable.go
+++ b/models/actions/variable.go
@@ -6,10 +6,12 @@ package actions
import (
"context"
"strings"
+ "unicode/utf8"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
@@ -32,32 +34,46 @@ type ActionVariable struct {
RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name)"`
Name string `xorm:"UNIQUE(owner_repo_name) NOT NULL"`
Data string `xorm:"LONGTEXT NOT NULL"`
+ Description string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
+const (
+ VariableDataMaxLength = 65536
+ VariableDescriptionMaxLength = 4096
+)
+
func init() {
db.RegisterModel(new(ActionVariable))
}
-func InsertVariable(ctx context.Context, ownerID, repoID int64, name, data string) (*ActionVariable, error) {
+func InsertVariable(ctx context.Context, ownerID, repoID int64, name, data, description string) (*ActionVariable, error) {
if ownerID != 0 && repoID != 0 {
// It's trying to create a variable that belongs to a repository, but OwnerID has been set accidentally.
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
ownerID = 0
}
+ if utf8.RuneCountInString(data) > VariableDataMaxLength {
+ return nil, util.NewInvalidArgumentErrorf("data too long")
+ }
+
+ description = util.TruncateRunes(description, VariableDescriptionMaxLength)
+
variable := &ActionVariable{
- OwnerID: ownerID,
- RepoID: repoID,
- Name: strings.ToUpper(name),
- Data: data,
+ OwnerID: ownerID,
+ RepoID: repoID,
+ Name: strings.ToUpper(name),
+ Data: data,
+ Description: description,
}
return variable, db.Insert(ctx, variable)
}
type FindVariablesOpts struct {
db.ListOptions
+ IDs []int64
RepoID int64
OwnerID int64 // it will be ignored if RepoID is set
Name string
@@ -65,6 +81,15 @@ type FindVariablesOpts struct {
func (opts FindVariablesOpts) ToConds() builder.Cond {
cond := builder.NewCond()
+
+ if len(opts.IDs) > 0 {
+ if len(opts.IDs) == 1 {
+ cond = cond.And(builder.Eq{"id": opts.IDs[0]})
+ } else {
+ cond = cond.And(builder.In("id", opts.IDs))
+ }
+ }
+
// Since we now support instance-level variables,
// there is no need to check for null values for `owner_id` and `repo_id`
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
@@ -85,12 +110,18 @@ func FindVariables(ctx context.Context, opts FindVariablesOpts) ([]*ActionVariab
return db.Find[ActionVariable](ctx, opts)
}
-func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error) {
- count, err := db.GetEngine(ctx).ID(variable.ID).Cols("name", "data").
- Update(&ActionVariable{
- Name: variable.Name,
- Data: variable.Data,
- })
+func UpdateVariableCols(ctx context.Context, variable *ActionVariable, cols ...string) (bool, error) {
+ if utf8.RuneCountInString(variable.Data) > VariableDataMaxLength {
+ return false, util.NewInvalidArgumentErrorf("data too long")
+ }
+
+ variable.Description = util.TruncateRunes(variable.Description, VariableDescriptionMaxLength)
+
+ variable.Name = strings.ToUpper(variable.Name)
+ count, err := db.GetEngine(ctx).
+ ID(variable.ID).
+ Cols(cols...).
+ Update(variable)
return count != 0, err
}
@@ -137,3 +168,17 @@ func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string,
return variables, nil
}
+
+func CountWrongRepoLevelVariables(ctx context.Context) (int64, error) {
+ var result int64
+ _, err := db.GetEngine(ctx).SQL("SELECT count(`id`) FROM `action_variable` WHERE `repo_id` > 0 AND `owner_id` > 0").Get(&result)
+ return result, err
+}
+
+func UpdateWrongRepoLevelVariables(ctx context.Context) (int64, error) {
+ result, err := db.GetEngine(ctx).Exec("UPDATE `action_variable` SET `owner_id` = 0 WHERE `repo_id` > 0 AND `owner_id` > 0")
+ if err != nil {
+ return 0, err
+ }
+ return result.RowsAffected()
+}
diff --git a/models/activities/action.go b/models/activities/action.go
index 8304210188..1a0dfe6412 100644
--- a/models/activities/action.go
+++ b/models/activities/action.go
@@ -9,6 +9,7 @@ import (
"fmt"
"net/url"
"path"
+ "slices"
"strconv"
"strings"
"time"
@@ -16,9 +17,7 @@ import (
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
- access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
@@ -72,9 +71,9 @@ func (at ActionType) String() string {
case ActionRenameRepo:
return "rename_repo"
case ActionStarRepo:
- return "star_repo"
+ return "star_repo" // will not displayed in feeds.tmpl
case ActionWatchRepo:
- return "watch_repo"
+ return "watch_repo" // will not displayed in feeds.tmpl
case ActionCommitRepo:
return "commit_repo"
case ActionCreateIssue:
@@ -127,12 +126,7 @@ func (at ActionType) String() string {
}
func (at ActionType) InActions(actions ...string) bool {
- for _, action := range actions {
- if action == at.String() {
- return true
- }
- }
- return false
+ return slices.Contains(actions, at.String())
}
// Action represents user operation type and other information to
@@ -174,7 +168,10 @@ func (a *Action) TableIndices() []*schemas.Index {
cuIndex := schemas.NewIndex("c_u", schemas.IndexType)
cuIndex.AddColumn("user_id", "is_deleted")
- indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex, cuIndex}
+ actUserUserIndex := schemas.NewIndex("au_c_u", schemas.IndexType)
+ actUserUserIndex.AddColumn("act_user_id", "created_unix", "user_id")
+
+ indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex, cuIndex, actUserUserIndex}
return indices
}
@@ -190,7 +187,7 @@ func (a *Action) LoadActUser(ctx context.Context) {
return
}
var err error
- a.ActUser, err = user_model.GetUserByID(ctx, a.ActUserID)
+ a.ActUser, err = user_model.GetPossibleUserByID(ctx, a.ActUserID)
if err == nil {
return
} else if user_model.IsErrUserNotExist(err) {
@@ -200,15 +197,13 @@ func (a *Action) LoadActUser(ctx context.Context) {
}
}
-func (a *Action) LoadRepo(ctx context.Context) {
+func (a *Action) LoadRepo(ctx context.Context) error {
if a.Repo != nil {
- return
+ return nil
}
var err error
a.Repo, err = repo_model.GetRepositoryByID(ctx, a.RepoID)
- if err != nil {
- log.Error("repo_model.GetRepositoryByID(%d): %v", a.RepoID, err)
- }
+ return err
}
// GetActFullName gets the action's user full name.
@@ -250,7 +245,7 @@ func (a *Action) GetActDisplayNameTitle(ctx context.Context) string {
// GetRepoUserName returns the name of the action repository owner.
func (a *Action) GetRepoUserName(ctx context.Context) string {
- a.LoadRepo(ctx)
+ _ = a.LoadRepo(ctx)
if a.Repo == nil {
return "(non-existing-repo)"
}
@@ -265,7 +260,7 @@ func (a *Action) ShortRepoUserName(ctx context.Context) string {
// GetRepoName returns the name of the action repository.
func (a *Action) GetRepoName(ctx context.Context) string {
- a.LoadRepo(ctx)
+ _ = a.LoadRepo(ctx)
if a.Repo == nil {
return "(non-existing-repo)"
}
@@ -355,7 +350,7 @@ func (a *Action) GetBranch() string {
// GetRefLink returns the action's ref link.
func (a *Action) GetRefLink(ctx context.Context) string {
- return git.RefURL(a.GetRepoLink(ctx), a.RefName)
+ return a.GetRepoLink(ctx) + "/src/" + git.RefName(a.RefName).RefWebLinkPath()
}
// GetTag returns the action's repository tag.
@@ -446,6 +441,7 @@ type GetFeedsOptions struct {
OnlyPerformedBy bool // only actions performed by requested user
IncludeDeleted bool // include deleted actions
Date string // the day we want activity for: YYYY-MM-DD
+ DontCount bool // do counting in GetFeeds
}
// ActivityReadable return whether doer can read activities of user
@@ -454,6 +450,24 @@ func ActivityReadable(user, doer *user_model.User) bool {
doer != nil && (doer.IsAdmin || user.ID == doer.ID)
}
+func FeedDateCond(opts GetFeedsOptions) builder.Cond {
+ cond := builder.NewCond()
+ if opts.Date == "" {
+ return cond
+ }
+
+ dateLow, err := time.ParseInLocation("2006-01-02", opts.Date, setting.DefaultUILocation)
+ if err != nil {
+ log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err)
+ } else {
+ dateHigh := dateLow.Add(86399000000000) // 23h59m59s
+
+ cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()})
+ cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()})
+ }
+ return cond
+}
+
func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.Cond, error) {
cond := builder.NewCond()
@@ -511,8 +525,8 @@ func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.
}
if opts.RequestedTeam != nil {
- env := repo_model.AccessibleTeamReposEnv(ctx, organization.OrgFromUser(opts.RequestedUser), opts.RequestedTeam)
- teamRepoIDs, err := env.RepoIDs(1, opts.RequestedUser.NumRepos)
+ env := repo_model.AccessibleTeamReposEnv(organization.OrgFromUser(opts.RequestedUser), opts.RequestedTeam)
+ teamRepoIDs, err := env.RepoIDs(ctx)
if err != nil {
return nil, fmt.Errorf("GetTeamRepositories: %w", err)
}
@@ -534,17 +548,7 @@ func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.
cond = cond.And(builder.Eq{"is_deleted": false})
}
- if opts.Date != "" {
- dateLow, err := time.ParseInLocation("2006-01-02", opts.Date, setting.DefaultUILocation)
- if err != nil {
- log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err)
- } else {
- dateHigh := dateLow.Add(86399000000000) // 23h59m59s
-
- cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()})
- cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()})
- }
- }
+ cond = cond.And(FeedDateCond(opts))
return cond, nil
}
@@ -559,130 +563,6 @@ func DeleteOldActions(ctx context.Context, olderThan time.Duration) (err error)
return err
}
-// NotifyWatchers creates batch of actions for every watcher.
-// It could insert duplicate actions for a repository action, like this:
-// * Original action: UserID=1 (the real actor), ActUserID=1
-// * Organization action: UserID=100 (the repo's org), ActUserID=1
-// * Watcher action: UserID=20 (a user who is watching a repo), ActUserID=1
-func NotifyWatchers(ctx context.Context, actions ...*Action) error {
- var watchers []*repo_model.Watch
- var repo *repo_model.Repository
- var err error
- var permCode []bool
- var permIssue []bool
- var permPR []bool
-
- e := db.GetEngine(ctx)
-
- for _, act := range actions {
- repoChanged := repo == nil || repo.ID != act.RepoID
-
- if repoChanged {
- // Add feeds for user self and all watchers.
- watchers, err = repo_model.GetWatchers(ctx, act.RepoID)
- if err != nil {
- return fmt.Errorf("get watchers: %w", err)
- }
- }
-
- // Add feed for actioner.
- act.UserID = act.ActUserID
- if _, err = e.Insert(act); err != nil {
- return fmt.Errorf("insert new actioner: %w", err)
- }
-
- if repoChanged {
- act.LoadRepo(ctx)
- repo = act.Repo
-
- // check repo owner exist.
- if err := act.Repo.LoadOwner(ctx); err != nil {
- return fmt.Errorf("can't get repo owner: %w", err)
- }
- } else if act.Repo == nil {
- act.Repo = repo
- }
-
- // Add feed for organization
- if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
- act.ID = 0
- act.UserID = act.Repo.Owner.ID
- if err = db.Insert(ctx, act); err != nil {
- return fmt.Errorf("insert new actioner: %w", err)
- }
- }
-
- if repoChanged {
- permCode = make([]bool, len(watchers))
- permIssue = make([]bool, len(watchers))
- permPR = make([]bool, len(watchers))
- for i, watcher := range watchers {
- user, err := user_model.GetUserByID(ctx, watcher.UserID)
- if err != nil {
- permCode[i] = false
- permIssue[i] = false
- permPR[i] = false
- continue
- }
- perm, err := access_model.GetUserRepoPermission(ctx, repo, user)
- if err != nil {
- permCode[i] = false
- permIssue[i] = false
- permPR[i] = false
- continue
- }
- permCode[i] = perm.CanRead(unit.TypeCode)
- permIssue[i] = perm.CanRead(unit.TypeIssues)
- permPR[i] = perm.CanRead(unit.TypePullRequests)
- }
- }
-
- for i, watcher := range watchers {
- if act.ActUserID == watcher.UserID {
- continue
- }
- act.ID = 0
- act.UserID = watcher.UserID
- act.Repo.Units = nil
-
- switch act.OpType {
- case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionPublishRelease, ActionDeleteBranch:
- if !permCode[i] {
- continue
- }
- case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue:
- if !permIssue[i] {
- continue
- }
- case ActionCreatePullRequest, ActionCommentPull, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest, ActionAutoMergePullRequest:
- if !permPR[i] {
- continue
- }
- }
-
- if err = db.Insert(ctx, act); err != nil {
- return fmt.Errorf("insert new action: %w", err)
- }
- }
- }
- return nil
-}
-
-// NotifyWatchersActions creates batch of actions for every watcher.
-func NotifyWatchersActions(ctx context.Context, acts []*Action) error {
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
- for _, act := range acts {
- if err := NotifyWatchers(ctx, act); err != nil {
- return err
- }
- }
- return committer.Commit()
-}
-
// DeleteIssueActions delete all actions related with issueID
func DeleteIssueActions(ctx context.Context, repoID, issueID, issueIndex int64) error {
// delete actions assigned to this issue
diff --git a/models/activities/action_list.go b/models/activities/action_list.go
index 5f9acb8f2a..b52cf7ee49 100644
--- a/models/activities/action_list.go
+++ b/models/activities/action_list.go
@@ -5,6 +5,7 @@ package activities
import (
"context"
+ "errors"
"fmt"
"strconv"
@@ -205,12 +206,34 @@ func (actions ActionList) LoadIssues(ctx context.Context) error {
// GetFeeds returns actions according to the provided options
func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, error) {
if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil {
- return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
+ return nil, 0, errors.New("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
}
- cond, err := ActivityQueryCondition(ctx, opts)
- if err != nil {
- return nil, 0, err
+ var err error
+ var cond builder.Cond
+ // if the actor is the requested user or is an administrator, we can skip the ActivityQueryCondition
+ if opts.Actor != nil && opts.RequestedUser != nil && (opts.Actor.IsAdmin || opts.Actor.ID == opts.RequestedUser.ID) {
+ cond = builder.Eq{
+ "user_id": opts.RequestedUser.ID,
+ }.And(
+ FeedDateCond(opts),
+ )
+
+ if !opts.IncludeDeleted {
+ cond = cond.And(builder.Eq{"is_deleted": false})
+ }
+
+ if !opts.IncludePrivate {
+ cond = cond.And(builder.Eq{"is_private": false})
+ }
+ if opts.OnlyPerformedBy {
+ cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
+ }
+ } else {
+ cond, err = ActivityQueryCondition(ctx, opts)
+ if err != nil {
+ return nil, 0, err
+ }
}
actions := make([]*Action, 0, opts.PageSize)
@@ -221,7 +244,11 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
sess := db.GetEngine(ctx).Where(cond)
sess = db.SetSessionPagination(sess, &opts)
- count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
+ if opts.DontCount {
+ err = sess.Desc("`action`.created_unix").Find(&actions)
+ } else {
+ count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
+ }
if err != nil {
return nil, 0, fmt.Errorf("FindAndCount: %w", err)
}
@@ -235,11 +262,13 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
return nil, 0, fmt.Errorf("Find(actionsIDs): %w", err)
}
- count, err = db.GetEngine(ctx).Where(cond).
- Table("action").
- Cols("`action`.id").Count()
- if err != nil {
- return nil, 0, fmt.Errorf("Count: %w", err)
+ if !opts.DontCount {
+ count, err = db.GetEngine(ctx).Where(cond).
+ Table("action").
+ Cols("`action`.id").Count()
+ if err != nil {
+ return nil, 0, fmt.Errorf("Count: %w", err)
+ }
}
if err := db.GetEngine(ctx).In("`action`.id", actionIDs).Desc("`action`.created_unix").Find(&actions); err != nil {
@@ -253,3 +282,9 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
return actions, count, nil
}
+
+func CountUserFeeds(ctx context.Context, userID int64) (int64, error) {
+ return db.GetEngine(ctx).Where("user_id = ?", userID).
+ And("is_deleted = ?", false).
+ Count(&Action{})
+}
diff --git a/models/activities/action_test.go b/models/activities/action_test.go
index 9cfe981656..ff311ac891 100644
--- a/models/activities/action_test.go
+++ b/models/activities/action_test.go
@@ -82,43 +82,6 @@ func TestActivityReadable(t *testing.T) {
}
}
-func TestNotifyWatchers(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- action := &activities_model.Action{
- ActUserID: 8,
- RepoID: 1,
- OpType: activities_model.ActionStarRepo,
- }
- assert.NoError(t, activities_model.NotifyWatchers(db.DefaultContext, action))
-
- // One watchers are inactive, thus action is only created for user 8, 1, 4, 11
- unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
- ActUserID: action.ActUserID,
- UserID: 8,
- RepoID: action.RepoID,
- OpType: action.OpType,
- })
- unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
- ActUserID: action.ActUserID,
- UserID: 1,
- RepoID: action.RepoID,
- OpType: action.OpType,
- })
- unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
- ActUserID: action.ActUserID,
- UserID: 4,
- RepoID: action.RepoID,
- OpType: action.OpType,
- })
- unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
- ActUserID: action.ActUserID,
- UserID: 11,
- RepoID: action.RepoID,
- OpType: action.OpType,
- })
-}
-
func TestConsistencyUpdateAction(t *testing.T) {
if !setting.Database.Type.IsSQLite3() {
t.Skip("Test is only for SQLite database.")
@@ -167,7 +130,7 @@ func TestDeleteIssueActions(t *testing.T) {
// load an issue
issue := unittest.AssertExistsAndLoadBean(t, &issue_model.Issue{ID: 4})
- assert.NotEqualValues(t, issue.ID, issue.Index) // it needs to use different ID/Index to test the DeleteIssueActions to delete some actions by IssueIndex
+ assert.NotEqual(t, issue.ID, issue.Index) // it needs to use different ID/Index to test the DeleteIssueActions to delete some actions by IssueIndex
// insert a comment
err := db.Insert(db.DefaultContext, &issue_model.Comment{Type: issue_model.CommentTypeComment, IssueID: issue.ID})
diff --git a/models/activities/notification_list.go b/models/activities/notification_list.go
index 0cbb91df3c..b47f5dc404 100644
--- a/models/activities/notification_list.go
+++ b/models/activities/notification_list.go
@@ -208,10 +208,7 @@ func (nl NotificationList) LoadRepos(ctx context.Context) (repo_model.Repository
repos := make(map[int64]*repo_model.Repository, len(repoIDs))
left := len(repoIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("id", repoIDs[:limit]).
Rows(new(repo_model.Repository))
@@ -282,10 +279,7 @@ func (nl NotificationList) LoadIssues(ctx context.Context) ([]int, error) {
issues := make(map[int64]*issues_model.Issue, len(issueIDs))
left := len(issueIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("id", issueIDs[:limit]).
Rows(new(issues_model.Issue))
@@ -377,10 +371,7 @@ func (nl NotificationList) LoadUsers(ctx context.Context) ([]int, error) {
users := make(map[int64]*user_model.User, len(userIDs))
left := len(userIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("id", userIDs[:limit]).
Rows(new(user_model.User))
@@ -428,10 +419,7 @@ func (nl NotificationList) LoadComments(ctx context.Context) ([]int, error) {
comments := make(map[int64]*issues_model.Comment, len(commentIDs))
left := len(commentIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("id", commentIDs[:limit]).
Rows(new(issues_model.Comment))
diff --git a/models/activities/notification_test.go b/models/activities/notification_test.go
index 52f0eacba1..5d2a29bc36 100644
--- a/models/activities/notification_test.go
+++ b/models/activities/notification_test.go
@@ -44,11 +44,11 @@ func TestNotificationsForUser(t *testing.T) {
assert.NoError(t, err)
if assert.Len(t, notfs, 3) {
assert.EqualValues(t, 5, notfs[0].ID)
- assert.EqualValues(t, user.ID, notfs[0].UserID)
+ assert.Equal(t, user.ID, notfs[0].UserID)
assert.EqualValues(t, 4, notfs[1].ID)
- assert.EqualValues(t, user.ID, notfs[1].UserID)
+ assert.Equal(t, user.ID, notfs[1].UserID)
assert.EqualValues(t, 2, notfs[2].ID)
- assert.EqualValues(t, user.ID, notfs[2].UserID)
+ assert.Equal(t, user.ID, notfs[2].UserID)
}
}
@@ -58,7 +58,7 @@ func TestNotification_GetRepo(t *testing.T) {
repo, err := notf.GetRepo(db.DefaultContext)
assert.NoError(t, err)
assert.Equal(t, repo, notf.Repository)
- assert.EqualValues(t, notf.RepoID, repo.ID)
+ assert.Equal(t, notf.RepoID, repo.ID)
}
func TestNotification_GetIssue(t *testing.T) {
@@ -67,7 +67,7 @@ func TestNotification_GetIssue(t *testing.T) {
issue, err := notf.GetIssue(db.DefaultContext)
assert.NoError(t, err)
assert.Equal(t, issue, notf.Issue)
- assert.EqualValues(t, notf.IssueID, issue.ID)
+ assert.Equal(t, notf.IssueID, issue.ID)
}
func TestGetNotificationCount(t *testing.T) {
@@ -136,5 +136,5 @@ func TestSetIssueReadBy(t *testing.T) {
nt, err := activities_model.GetIssueNotification(db.DefaultContext, user.ID, issue.ID)
assert.NoError(t, err)
- assert.EqualValues(t, activities_model.NotificationStatusRead, nt.Status)
+ assert.Equal(t, activities_model.NotificationStatusRead, nt.Status)
}
diff --git a/models/activities/repo_activity.go b/models/activities/repo_activity.go
index 3ccdbd47d3..aeaa452c9e 100644
--- a/models/activities/repo_activity.go
+++ b/models/activities/repo_activity.go
@@ -139,10 +139,7 @@ func GetActivityStatsTopAuthors(ctx context.Context, repo *repo_model.Repository
return v[i].Commits > v[j].Commits
})
- cnt := count
- if cnt > len(v) {
- cnt = len(v)
- }
+ cnt := min(count, len(v))
return v[:cnt], nil
}
diff --git a/models/activities/statistic.go b/models/activities/statistic.go
index ff81ad78a1..940651d359 100644
--- a/models/activities/statistic.go
+++ b/models/activities/statistic.go
@@ -17,13 +17,16 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
+ "code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/structs"
)
// Statistic contains the database statistics
type Statistic struct {
Counter struct {
- User, Org, PublicKey,
+ UsersActive, UsersNotActive,
+ Org, PublicKey,
Repo, Watch, Star, Access,
Issue, IssueClosed, IssueOpen,
Comment, Oauth, Follow,
@@ -53,8 +56,20 @@ type IssueByRepositoryCount struct {
// GetStatistic returns the database statistics
func GetStatistic(ctx context.Context) (stats Statistic) {
e := db.GetEngine(ctx)
- stats.Counter.User = user_model.CountUsers(ctx, nil)
- stats.Counter.Org, _ = db.Count[organization.Organization](ctx, organization.FindOrgOptions{IncludePrivate: true})
+
+ // Number of active users
+ usersActiveOpts := user_model.CountUserFilter{
+ IsActive: optional.Some(true),
+ }
+ stats.Counter.UsersActive = user_model.CountUsers(ctx, &usersActiveOpts)
+
+ // Number of inactive users
+ usersNotActiveOpts := user_model.CountUserFilter{
+ IsActive: optional.Some(false),
+ }
+ stats.Counter.UsersNotActive = user_model.CountUsers(ctx, &usersNotActiveOpts)
+
+ stats.Counter.Org, _ = db.Count[organization.Organization](ctx, organization.FindOrgOptions{IncludeVisibility: structs.VisibleTypePrivate})
stats.Counter.PublicKey, _ = e.Count(new(asymkey_model.PublicKey))
stats.Counter.Repo, _ = repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{})
stats.Counter.Watch, _ = e.Count(new(repo_model.Watch))
diff --git a/models/activities/user_heatmap.go b/models/activities/user_heatmap.go
index 1f8f0f590e..ef67838be7 100644
--- a/models/activities/user_heatmap.go
+++ b/models/activities/user_heatmap.go
@@ -66,7 +66,7 @@ func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organi
Select(groupBy+" AS timestamp, count(user_id) as contributions").
Table("action").
Where(cond).
- And("created_unix > ?", timeutil.TimeStampNow()-31536000).
+ And("created_unix > ?", timeutil.TimeStampNow()-(366+7)*86400). // (366+7) days to include the first week for the heatmap
GroupBy(groupByName).
OrderBy("timestamp").
Find(&hdata)
diff --git a/models/admin/task.go b/models/admin/task.go
index 10f8e6d570..0541a8ec78 100644
--- a/models/admin/task.go
+++ b/models/admin/task.go
@@ -44,7 +44,7 @@ func init() {
// TranslatableMessage represents JSON struct that can be translated with a Locale
type TranslatableMessage struct {
Format string
- Args []any `json:"omitempty"`
+ Args []any `json:",omitempty"`
}
// LoadRepo loads repository of the task
diff --git a/models/asymkey/error.go b/models/asymkey/error.go
index 2e65d76612..b765624579 100644
--- a/models/asymkey/error.go
+++ b/models/asymkey/error.go
@@ -25,7 +25,7 @@ func (err ErrKeyUnableVerify) Error() string {
}
// ErrKeyIsPrivate is returned when the provided key is a private key not a public key
-var ErrKeyIsPrivate = util.NewSilentWrapErrorf(util.ErrInvalidArgument, "the provided key is a private key")
+var ErrKeyIsPrivate = util.ErrorWrap(util.ErrInvalidArgument, "the provided key is a private key")
// ErrKeyNotExist represents a "KeyNotExist" kind of error.
type ErrKeyNotExist struct {
@@ -132,7 +132,7 @@ func IsErrGPGKeyParsing(err error) bool {
}
func (err ErrGPGKeyParsing) Error() string {
- return fmt.Sprintf("failed to parse gpg key %s", err.ParseError.Error())
+ return "failed to parse gpg key " + err.ParseError.Error()
}
// ErrGPGKeyNotExist represents a "GPGKeyNotExist" kind of error.
diff --git a/models/asymkey/gpg_key.go b/models/asymkey/gpg_key.go
index 5236b2d450..220f46ad1d 100644
--- a/models/asymkey/gpg_key.go
+++ b/models/asymkey/gpg_key.go
@@ -5,6 +5,7 @@ package asymkey
import (
"context"
+ "errors"
"fmt"
"strings"
"time"
@@ -13,8 +14,8 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
- "github.com/keybase/go-crypto/openpgp"
- "github.com/keybase/go-crypto/openpgp/packet"
+ "github.com/ProtonMail/go-crypto/openpgp"
+ "github.com/ProtonMail/go-crypto/openpgp/packet"
"xorm.io/builder"
)
@@ -106,7 +107,7 @@ func GPGKeyToEntity(ctx context.Context, k *GPGKey) (*openpgp.Entity, error) {
if err != nil {
return nil, err
}
- keys, err := checkArmoredGPGKeyString(impKey.Content)
+ keys, err := CheckArmoredGPGKeyString(impKey.Content)
if err != nil {
return nil, err
}
@@ -115,7 +116,7 @@ func GPGKeyToEntity(ctx context.Context, k *GPGKey) (*openpgp.Entity, error) {
// parseSubGPGKey parse a sub Key
func parseSubGPGKey(ownerID int64, primaryID string, pubkey *packet.PublicKey, expiry time.Time) (*GPGKey, error) {
- content, err := base64EncPubKey(pubkey)
+ content, err := Base64EncPubKey(pubkey)
if err != nil {
return nil, err
}
@@ -141,7 +142,11 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified
// Parse Subkeys
subkeys := make([]*GPGKey, len(e.Subkeys))
for i, k := range e.Subkeys {
- subs, err := parseSubGPGKey(ownerID, pubkey.KeyIdString(), k.PublicKey, expiry)
+ subkeyExpiry := expiry
+ if k.Sig.KeyLifetimeSecs != nil {
+ subkeyExpiry = k.PublicKey.CreationTime.Add(time.Duration(*k.Sig.KeyLifetimeSecs) * time.Second)
+ }
+ subs, err := parseSubGPGKey(ownerID, pubkey.KeyIdString(), k.PublicKey, subkeyExpiry)
if err != nil {
return nil, ErrGPGKeyParsing{ParseError: err}
}
@@ -156,7 +161,7 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified
emails := make([]*user_model.EmailAddress, 0, len(e.Identities))
for _, ident := range e.Identities {
- if ident.Revocation != nil {
+ if ident.Revoked(time.Now()) {
continue
}
email := strings.ToLower(strings.TrimSpace(ident.UserId.Email))
@@ -179,7 +184,7 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified
}
}
- content, err := base64EncPubKey(pubkey)
+ content, err := Base64EncPubKey(pubkey)
if err != nil {
return nil, err
}
@@ -203,7 +208,7 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified
// deleteGPGKey does the actual key deletion
func deleteGPGKey(ctx context.Context, keyID string) (int64, error) {
if keyID == "" {
- return 0, fmt.Errorf("empty KeyId forbidden") // Should never happen but just to be sure
+ return 0, errors.New("empty KeyId forbidden") // Should never happen but just to be sure
}
// Delete imported key
n, err := db.GetEngine(ctx).Where("key_id=?", keyID).Delete(new(GPGKeyImport))
@@ -236,32 +241,9 @@ func DeleteGPGKey(ctx context.Context, doer *user_model.User, id int64) (err err
return committer.Commit()
}
-func checkKeyEmails(ctx context.Context, email string, keys ...*GPGKey) (bool, string) {
- uid := int64(0)
- var userEmails []*user_model.EmailAddress
- var user *user_model.User
- for _, key := range keys {
- for _, e := range key.Emails {
- if e.IsActivated && (email == "" || strings.EqualFold(e.Email, email)) {
- return true, e.Email
- }
- }
- if key.Verified && key.OwnerID != 0 {
- if uid != key.OwnerID {
- userEmails, _ = user_model.GetEmailAddresses(ctx, key.OwnerID)
- uid = key.OwnerID
- user = &user_model.User{ID: uid}
- _, _ = user_model.GetUser(ctx, user)
- }
- for _, e := range userEmails {
- if e.IsActivated && (email == "" || strings.EqualFold(e.Email, email)) {
- return true, e.Email
- }
- }
- if user.KeepEmailPrivate && strings.EqualFold(email, user.GetEmail()) {
- return true, user.GetEmail()
- }
- }
- }
- return false, email
+func FindGPGKeyWithSubKeys(ctx context.Context, keyID string) ([]*GPGKey, error) {
+ return db.Find[GPGKey](ctx, FindGPGKeyOptions{
+ KeyID: keyID,
+ IncludeSubKeys: true,
+ })
}
diff --git a/models/asymkey/gpg_key_add.go b/models/asymkey/gpg_key_add.go
index 11124b1366..1c7d2c1da2 100644
--- a/models/asymkey/gpg_key_add.go
+++ b/models/asymkey/gpg_key_add.go
@@ -10,7 +10,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
- "github.com/keybase/go-crypto/openpgp"
+ "github.com/ProtonMail/go-crypto/openpgp"
)
// __________________ ________ ____ __.
@@ -67,7 +67,7 @@ func addGPGSubKey(ctx context.Context, key *GPGKey) (err error) {
// AddGPGKey adds new public key to database.
func AddGPGKey(ctx context.Context, ownerID int64, content, token, signature string) ([]*GPGKey, error) {
- ekeys, err := checkArmoredGPGKeyString(content)
+ ekeys, err := CheckArmoredGPGKeyString(content)
if err != nil {
return nil, err
}
@@ -83,15 +83,15 @@ func AddGPGKey(ctx context.Context, ownerID int64, content, token, signature str
verified := false
// Handle provided signature
if signature != "" {
- signer, err := openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token), strings.NewReader(signature))
+ signer, err := openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token), strings.NewReader(signature), nil)
if err != nil {
- signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\n"), strings.NewReader(signature))
+ signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\n"), strings.NewReader(signature), nil)
}
if err != nil {
- signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\r\n"), strings.NewReader(signature))
+ signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\r\n"), strings.NewReader(signature), nil)
}
if err != nil {
- log.Error("Unable to validate token signature. Error: %v", err)
+ log.Debug("AddGPGKey CheckArmoredDetachedSignature failed: %v", err)
return nil, ErrGPGInvalidTokenSignature{
ID: ekeys[0].PrimaryKey.KeyIdString(),
Wrapped: err,
diff --git a/models/asymkey/gpg_key_commit_verification.go b/models/asymkey/gpg_key_commit_verification.go
index 26fad3bb3f..39ec893606 100644
--- a/models/asymkey/gpg_key_commit_verification.go
+++ b/models/asymkey/gpg_key_commit_verification.go
@@ -4,19 +4,15 @@
package asymkey
import (
- "context"
+ "errors"
"fmt"
"hash"
- "strings"
- "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "github.com/keybase/go-crypto/openpgp/packet"
+ "github.com/ProtonMail/go-crypto/openpgp/packet"
)
// __________________ ________ ____ __.
@@ -70,267 +66,10 @@ const (
NoKeyFound = "gpg.error.no_gpg_keys_found"
)
-// ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys.
-func ParseCommitsWithSignature(ctx context.Context, oldCommits []*user_model.UserCommit, repoTrustModel repo_model.TrustModelType, isOwnerMemberCollaborator func(*user_model.User) (bool, error)) []*SignCommit {
- newCommits := make([]*SignCommit, 0, len(oldCommits))
- keyMap := map[string]bool{}
-
- for _, c := range oldCommits {
- signCommit := &SignCommit{
- UserCommit: c,
- Verification: ParseCommitWithSignature(ctx, c.Commit),
- }
-
- _ = CalculateTrustStatus(signCommit.Verification, repoTrustModel, isOwnerMemberCollaborator, &keyMap)
-
- newCommits = append(newCommits, signCommit)
- }
- return newCommits
-}
-
-// ParseCommitWithSignature check if signature is good against keystore.
-func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *CommitVerification {
- var committer *user_model.User
- if c.Committer != nil {
- var err error
- // Find Committer account
- committer, err = user_model.GetUserByEmail(ctx, c.Committer.Email) // This finds the user by primary email or activated email so commit will not be valid if email is not
- if err != nil { // Skipping not user for committer
- committer = &user_model.User{
- Name: c.Committer.Name,
- Email: c.Committer.Email,
- }
- // We can expect this to often be an ErrUserNotExist. in the case
- // it is not, however, it is important to log it.
- if !user_model.IsErrUserNotExist(err) {
- log.Error("GetUserByEmail: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.no_committer_account",
- }
- }
- }
- }
-
- // If no signature just report the committer
- if c.Signature == nil {
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false, // Default value
- Reason: "gpg.error.not_signed_commit", // Default value
- }
- }
-
- // If this a SSH signature handle it differently
- if strings.HasPrefix(c.Signature.Signature, "-----BEGIN SSH SIGNATURE-----") {
- return ParseCommitWithSSHSignature(ctx, c, committer)
- }
-
- // Parsing signature
- sig, err := extractSignature(c.Signature.Signature)
- if err != nil { // Skipping failed to extract sign
- log.Error("SignatureRead err: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.extract_sign",
- }
- }
-
- keyID := tryGetKeyIDFromSignature(sig)
- defaultReason := NoKeyFound
-
- // First check if the sig has a keyID and if so just look at that
- if commitVerification := hashAndVerifyForKeyID(
- ctx,
- sig,
- c.Signature.Payload,
- committer,
- keyID,
- setting.AppName,
- ""); commitVerification != nil {
- if commitVerification.Reason == BadSignature {
- defaultReason = BadSignature
- } else {
- return commitVerification
- }
- }
-
- // Now try to associate the signature with the committer, if present
- if committer.ID != 0 {
- keys, err := db.Find[GPGKey](ctx, FindGPGKeyOptions{
- OwnerID: committer.ID,
- })
- if err != nil { // Skipping failed to get gpg keys of user
- log.Error("ListGPGKeys: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.failed_retrieval_gpg_keys",
- }
- }
-
- if err := GPGKeyList(keys).LoadSubKeys(ctx); err != nil {
- log.Error("LoadSubKeys: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.failed_retrieval_gpg_keys",
- }
- }
-
- committerEmailAddresses, _ := user_model.GetEmailAddresses(ctx, committer.ID)
- activated := false
- for _, e := range committerEmailAddresses {
- if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
- activated = true
- break
- }
- }
-
- for _, k := range keys {
- // Pre-check (& optimization) that emails attached to key can be attached to the committer email and can validate
- canValidate := false
- email := ""
- if k.Verified && activated {
- canValidate = true
- email = c.Committer.Email
- }
- if !canValidate {
- for _, e := range k.Emails {
- if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
- canValidate = true
- email = e.Email
- break
- }
- }
- }
- if !canValidate {
- continue // Skip this key
- }
-
- commitVerification := hashAndVerifyWithSubKeysCommitVerification(sig, c.Signature.Payload, k, committer, committer, email)
- if commitVerification != nil {
- return commitVerification
- }
- }
- }
-
- if setting.Repository.Signing.SigningKey != "" && setting.Repository.Signing.SigningKey != "default" && setting.Repository.Signing.SigningKey != "none" {
- // OK we should try the default key
- gpgSettings := git.GPGSettings{
- Sign: true,
- KeyID: setting.Repository.Signing.SigningKey,
- Name: setting.Repository.Signing.SigningName,
- Email: setting.Repository.Signing.SigningEmail,
- }
- if err := gpgSettings.LoadPublicKeyContent(); err != nil {
- log.Error("Error getting default signing key: %s %v", gpgSettings.KeyID, err)
- } else if commitVerification := verifyWithGPGSettings(ctx, &gpgSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
- if commitVerification.Reason == BadSignature {
- defaultReason = BadSignature
- } else {
- return commitVerification
- }
- }
- }
-
- defaultGPGSettings, err := c.GetRepositoryDefaultPublicGPGKey(false)
- if err != nil {
- log.Error("Error getting default public gpg key: %v", err)
- } else if defaultGPGSettings == nil {
- log.Warn("Unable to get defaultGPGSettings for unattached commit: %s", c.ID.String())
- } else if defaultGPGSettings.Sign {
- if commitVerification := verifyWithGPGSettings(ctx, defaultGPGSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
- if commitVerification.Reason == BadSignature {
- defaultReason = BadSignature
- } else {
- return commitVerification
- }
- }
- }
-
- return &CommitVerification{ // Default at this stage
- CommittingUser: committer,
- Verified: false,
- Warning: defaultReason != NoKeyFound,
- Reason: defaultReason,
- SigningKey: &GPGKey{
- KeyID: keyID,
- },
- }
-}
-
-func verifyWithGPGSettings(ctx context.Context, gpgSettings *git.GPGSettings, sig *packet.Signature, payload string, committer *user_model.User, keyID string) *CommitVerification {
- // First try to find the key in the db
- if commitVerification := hashAndVerifyForKeyID(ctx, sig, payload, committer, gpgSettings.KeyID, gpgSettings.Name, gpgSettings.Email); commitVerification != nil {
- return commitVerification
- }
-
- // Otherwise we have to parse the key
- ekeys, err := checkArmoredGPGKeyString(gpgSettings.PublicKeyContent)
- if err != nil {
- log.Error("Unable to get default signing key: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.generate_hash",
- }
- }
- for _, ekey := range ekeys {
- pubkey := ekey.PrimaryKey
- content, err := base64EncPubKey(pubkey)
- if err != nil {
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.generate_hash",
- }
- }
- k := &GPGKey{
- Content: content,
- CanSign: pubkey.CanSign(),
- KeyID: pubkey.KeyIdString(),
- }
- for _, subKey := range ekey.Subkeys {
- content, err := base64EncPubKey(subKey.PublicKey)
- if err != nil {
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.generate_hash",
- }
- }
- k.SubsKey = append(k.SubsKey, &GPGKey{
- Content: content,
- CanSign: subKey.PublicKey.CanSign(),
- KeyID: subKey.PublicKey.KeyIdString(),
- })
- }
- if commitVerification := hashAndVerifyWithSubKeysCommitVerification(sig, payload, k, committer, &user_model.User{
- Name: gpgSettings.Name,
- Email: gpgSettings.Email,
- }, gpgSettings.Email); commitVerification != nil {
- return commitVerification
- }
- if keyID == k.KeyID {
- // This is a bad situation ... We have a key id that matches our default key but the signature doesn't match.
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Warning: true,
- Reason: BadSignature,
- }
- }
- }
- return nil
-}
-
func verifySign(s *packet.Signature, h hash.Hash, k *GPGKey) error {
// Check if key can sign
if !k.CanSign {
- return fmt.Errorf("key can not sign")
+ return errors.New("key can not sign")
}
// Decode key
pkey, err := base64DecPubKey(k.Content)
@@ -369,7 +108,7 @@ func hashAndVerifyWithSubKeys(sig *packet.Signature, payload string, k *GPGKey)
return nil, nil
}
-func hashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload string, k *GPGKey, committer, signer *user_model.User, email string) *CommitVerification {
+func HashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload string, k *GPGKey, committer, signer *user_model.User, email string) *CommitVerification {
key, err := hashAndVerifyWithSubKeys(sig, payload, k)
if err != nil { // Skipping failed to generate hash
return &CommitVerification{
@@ -392,78 +131,6 @@ func hashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload s
return nil
}
-func hashAndVerifyForKeyID(ctx context.Context, sig *packet.Signature, payload string, committer *user_model.User, keyID, name, email string) *CommitVerification {
- if keyID == "" {
- return nil
- }
- keys, err := db.Find[GPGKey](ctx, FindGPGKeyOptions{
- KeyID: keyID,
- IncludeSubKeys: true,
- })
- if err != nil {
- log.Error("GetGPGKeysByKeyID: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.failed_retrieval_gpg_keys",
- }
- }
- if len(keys) == 0 {
- return nil
- }
- for _, key := range keys {
- var primaryKeys []*GPGKey
- if key.PrimaryKeyID != "" {
- primaryKeys, err = db.Find[GPGKey](ctx, FindGPGKeyOptions{
- KeyID: key.PrimaryKeyID,
- IncludeSubKeys: true,
- })
- if err != nil {
- log.Error("GetGPGKeysByKeyID: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.failed_retrieval_gpg_keys",
- }
- }
- }
-
- activated, email := checkKeyEmails(ctx, email, append([]*GPGKey{key}, primaryKeys...)...)
- if !activated {
- continue
- }
-
- signer := &user_model.User{
- Name: name,
- Email: email,
- }
- if key.OwnerID != 0 {
- owner, err := user_model.GetUserByID(ctx, key.OwnerID)
- if err == nil {
- signer = owner
- } else if !user_model.IsErrUserNotExist(err) {
- log.Error("Failed to user_model.GetUserByID: %d for key ID: %d (%s) %v", key.OwnerID, key.ID, key.KeyID, err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.no_committer_account",
- }
- }
- }
- commitVerification := hashAndVerifyWithSubKeysCommitVerification(sig, payload, key, committer, signer, email)
- if commitVerification != nil {
- return commitVerification
- }
- }
- // This is a bad situation ... We have a key id that is in our database but the signature doesn't match.
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Warning: true,
- Reason: BadSignature,
- }
-}
-
// CalculateTrustStatus will calculate the TrustStatus for a commit verification within a repository
// There are several trust models in Gitea
func CalculateTrustStatus(verification *CommitVerification, repoTrustModel repo_model.TrustModelType, isOwnerMemberCollaborator func(*user_model.User) (bool, error), keyMap *map[string]bool) error {
diff --git a/models/asymkey/gpg_key_common.go b/models/asymkey/gpg_key_common.go
index 28cb8f4e76..76f52a3ca4 100644
--- a/models/asymkey/gpg_key_common.go
+++ b/models/asymkey/gpg_key_common.go
@@ -7,15 +7,16 @@ import (
"bytes"
"crypto"
"encoding/base64"
+ "errors"
"fmt"
"hash"
"io"
"strings"
"time"
- "github.com/keybase/go-crypto/openpgp"
- "github.com/keybase/go-crypto/openpgp/armor"
- "github.com/keybase/go-crypto/openpgp/packet"
+ "github.com/ProtonMail/go-crypto/openpgp"
+ "github.com/ProtonMail/go-crypto/openpgp/armor"
+ "github.com/ProtonMail/go-crypto/openpgp/packet"
)
// __________________ ________ ____ __.
@@ -33,9 +34,9 @@ import (
// This file provides common functions relating to GPG Keys
-// checkArmoredGPGKeyString checks if the given key string is a valid GPG armored key.
+// CheckArmoredGPGKeyString checks if the given key string is a valid GPG armored key.
// The function returns the actual public key on success
-func checkArmoredGPGKeyString(content string) (openpgp.EntityList, error) {
+func CheckArmoredGPGKeyString(content string) (openpgp.EntityList, error) {
list, err := openpgp.ReadArmoredKeyRing(strings.NewReader(content))
if err != nil {
return nil, ErrGPGKeyParsing{err}
@@ -43,8 +44,8 @@ func checkArmoredGPGKeyString(content string) (openpgp.EntityList, error) {
return list, nil
}
-// base64EncPubKey encode public key content to base 64
-func base64EncPubKey(pubkey *packet.PublicKey) (string, error) {
+// Base64EncPubKey encode public key content to base 64
+func Base64EncPubKey(pubkey *packet.PublicKey) (string, error) {
var w bytes.Buffer
err := pubkey.Serialize(&w)
if err != nil {
@@ -75,12 +76,12 @@ func base64DecPubKey(content string) (*packet.PublicKey, error) {
// Check type
pkey, ok := p.(*packet.PublicKey)
if !ok {
- return nil, fmt.Errorf("key is not a public key")
+ return nil, errors.New("key is not a public key")
}
return pkey, nil
}
-// getExpiryTime extract the expire time of primary key based on sig
+// getExpiryTime extract the expiry time of primary key based on sig
func getExpiryTime(e *openpgp.Entity) time.Time {
expiry := time.Time{}
// Extract self-sign for expire date based on : https://github.com/golang/crypto/blob/master/openpgp/keys.go#L165
@@ -88,12 +89,12 @@ func getExpiryTime(e *openpgp.Entity) time.Time {
for _, ident := range e.Identities {
if selfSig == nil {
selfSig = ident.SelfSignature
- } else if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
+ } else if ident.SelfSignature != nil && ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
selfSig = ident.SelfSignature
break
}
}
- if selfSig.KeyLifetimeSecs != nil {
+ if selfSig != nil && selfSig.KeyLifetimeSecs != nil {
expiry = e.PrimaryKey.CreationTime.Add(time.Duration(*selfSig.KeyLifetimeSecs) * time.Second)
}
return expiry
@@ -119,23 +120,23 @@ func readArmoredSign(r io.Reader) (body io.Reader, err error) {
return block.Body, nil
}
-func extractSignature(s string) (*packet.Signature, error) {
+func ExtractSignature(s string) (*packet.Signature, error) {
r, err := readArmoredSign(strings.NewReader(s))
if err != nil {
- return nil, fmt.Errorf("Failed to read signature armor")
+ return nil, errors.New("Failed to read signature armor")
}
p, err := packet.Read(r)
if err != nil {
- return nil, fmt.Errorf("Failed to read signature packet")
+ return nil, errors.New("Failed to read signature packet")
}
sig, ok := p.(*packet.Signature)
if !ok {
- return nil, fmt.Errorf("Packet is not a signature")
+ return nil, errors.New("Packet is not a signature")
}
return sig, nil
}
-func tryGetKeyIDFromSignature(sig *packet.Signature) string {
+func TryGetKeyIDFromSignature(sig *packet.Signature) string {
if sig.IssuerKeyId != nil && (*sig.IssuerKeyId) != 0 {
return fmt.Sprintf("%016X", *sig.IssuerKeyId)
}
diff --git a/models/asymkey/gpg_key_test.go b/models/asymkey/gpg_key_test.go
index d3fbb01d82..408cf15763 100644
--- a/models/asymkey/gpg_key_test.go
+++ b/models/asymkey/gpg_key_test.go
@@ -13,8 +13,10 @@ import (
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
- "github.com/keybase/go-crypto/openpgp/packet"
+ "github.com/ProtonMail/go-crypto/openpgp"
+ "github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestCheckArmoredGPGKeyString(t *testing.T) {
@@ -49,7 +51,7 @@ MkM/fdpyc2hY7Dl/+qFmN5MG5yGmMpQcX+RNNR222ibNC1D3wg==
=i9b7
-----END PGP PUBLIC KEY BLOCK-----`
- key, err := checkArmoredGPGKeyString(testGPGArmor)
+ key, err := CheckArmoredGPGKeyString(testGPGArmor)
assert.NoError(t, err, "Could not parse a valid GPG public armored rsa key", key)
// TODO verify value of key
}
@@ -70,7 +72,7 @@ OyjLLnFQiVmq7kEA/0z0CQe3ZQiQIq5zrs7Nh1XRkFAo8GlU/SGC9XFFi722
=ZiSe
-----END PGP PUBLIC KEY BLOCK-----`
- key, err := checkArmoredGPGKeyString(testGPGArmor)
+ key, err := CheckArmoredGPGKeyString(testGPGArmor)
assert.NoError(t, err, "Could not parse a valid GPG public armored brainpoolP256r1 key", key)
// TODO verify value of key
}
@@ -106,15 +108,14 @@ Av844q/BfRuVsJsK1NDNG09LC30B0l3LKBqlrRmRTUMHtgchdX2dY+p7GPOoSzlR
MkM/fdpyc2hY7Dl/+qFmN5MG5yGmMpQcX+RNNR222ibNC1D3wg==
=i9b7
-----END PGP PUBLIC KEY BLOCK-----`
- keys, err := checkArmoredGPGKeyString(testGPGArmor)
- if !assert.NotEmpty(t, keys) {
- return
- }
+ keys, err := CheckArmoredGPGKeyString(testGPGArmor)
+ require.NotEmpty(t, keys)
+
ekey := keys[0]
assert.NoError(t, err, "Could not parse a valid GPG armored key", ekey)
pubkey := ekey.PrimaryKey
- content, err := base64EncPubKey(pubkey)
+ content, err := Base64EncPubKey(pubkey)
assert.NoError(t, err, "Could not base64 encode a valid PublicKey content", ekey)
key := &GPGKey{
@@ -175,9 +176,9 @@ committer Antoine GIRARD <sapk@sapk.fr> 1489013107 +0100
Unknown GPG key with good email
`
// Reading Sign
- goodSig, err := extractSignature(testGoodSigArmor)
+ goodSig, err := ExtractSignature(testGoodSigArmor)
assert.NoError(t, err, "Could not parse a valid GPG armored signature", testGoodSigArmor)
- badSig, err := extractSignature(testBadSigArmor)
+ badSig, err := ExtractSignature(testBadSigArmor)
assert.NoError(t, err, "Could not parse a valid GPG armored signature", testBadSigArmor)
// Generating hash of commit
@@ -385,7 +386,7 @@ epiDVQ==
=VSKJ
-----END PGP PUBLIC KEY BLOCK-----
`
- keys, err := checkArmoredGPGKeyString(testIssue6599)
+ keys, err := CheckArmoredGPGKeyString(testIssue6599)
assert.NoError(t, err)
if assert.NotEmpty(t, keys) {
ekey := keys[0]
@@ -395,11 +396,33 @@ epiDVQ==
}
func TestTryGetKeyIDFromSignature(t *testing.T) {
- assert.Empty(t, tryGetKeyIDFromSignature(&packet.Signature{}))
- assert.Equal(t, "038D1A3EADDBEA9C", tryGetKeyIDFromSignature(&packet.Signature{
+ assert.Empty(t, TryGetKeyIDFromSignature(&packet.Signature{}))
+ assert.Equal(t, "038D1A3EADDBEA9C", TryGetKeyIDFromSignature(&packet.Signature{
IssuerKeyId: util.ToPointer(uint64(0x38D1A3EADDBEA9C)),
}))
- assert.Equal(t, "038D1A3EADDBEA9C", tryGetKeyIDFromSignature(&packet.Signature{
+ assert.Equal(t, "038D1A3EADDBEA9C", TryGetKeyIDFromSignature(&packet.Signature{
IssuerFingerprint: []uint8{0xb, 0x23, 0x24, 0xc7, 0xe6, 0xfe, 0x4f, 0x3a, 0x6, 0x26, 0xc1, 0x21, 0x3, 0x8d, 0x1a, 0x3e, 0xad, 0xdb, 0xea, 0x9c},
}))
}
+
+func TestParseGPGKey(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ assert.NoError(t, db.Insert(db.DefaultContext, &user_model.EmailAddress{UID: 1, Email: "email1@example.com", IsActivated: true}))
+
+ // create a key for test email
+ e, err := openpgp.NewEntity("name", "comment", "email1@example.com", nil)
+ require.NoError(t, err)
+ k, err := parseGPGKey(db.DefaultContext, 1, e, true)
+ require.NoError(t, err)
+ assert.NotEmpty(t, k.KeyID)
+ assert.NotEmpty(t, k.Emails) // the key is valid, matches the email
+
+ // then revoke the key
+ for _, id := range e.Identities {
+ id.Revocations = append(id.Revocations, &packet.Signature{RevocationReason: util.ToPointer(packet.KeyCompromised)})
+ }
+ k, err = parseGPGKey(db.DefaultContext, 1, e, true)
+ require.NoError(t, err)
+ assert.NotEmpty(t, k.KeyID)
+ assert.Empty(t, k.Emails) // the key is revoked, matches no email
+}
diff --git a/models/asymkey/gpg_key_verify.go b/models/asymkey/gpg_key_verify.go
index 01812a2d54..5ab2fd8081 100644
--- a/models/asymkey/gpg_key_verify.go
+++ b/models/asymkey/gpg_key_verify.go
@@ -50,7 +50,7 @@ func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature st
return "", err
}
- sig, err := extractSignature(signature)
+ sig, err := ExtractSignature(signature)
if err != nil {
return "", ErrGPGInvalidTokenSignature{
ID: key.KeyID,
@@ -85,7 +85,7 @@ func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature st
}
if signer == nil {
- log.Error("Unable to validate token signature. Error: %v", err)
+ log.Debug("VerifyGPGKey failed: no signer")
return "", ErrGPGInvalidTokenSignature{
ID: key.KeyID,
}
diff --git a/models/asymkey/ssh_key_commit_verification.go b/models/asymkey/ssh_key_commit_verification.go
deleted file mode 100644
index 27c6df3578..0000000000
--- a/models/asymkey/ssh_key_commit_verification.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package asymkey
-
-import (
- "bytes"
- "context"
- "fmt"
- "strings"
-
- "code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
-
- "github.com/42wim/sshsig"
-)
-
-// ParseCommitWithSSHSignature check if signature is good against keystore.
-func ParseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committer *user_model.User) *CommitVerification {
- // Now try to associate the signature with the committer, if present
- if committer.ID != 0 {
- keys, err := db.Find[PublicKey](ctx, FindPublicKeyOptions{
- OwnerID: committer.ID,
- NotKeytype: KeyTypePrincipal,
- })
- if err != nil { // Skipping failed to get ssh keys of user
- log.Error("ListPublicKeys: %v", err)
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: "gpg.error.failed_retrieval_gpg_keys",
- }
- }
-
- committerEmailAddresses, err := user_model.GetEmailAddresses(ctx, committer.ID)
- if err != nil {
- log.Error("GetEmailAddresses: %v", err)
- }
-
- activated := false
- for _, e := range committerEmailAddresses {
- if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
- activated = true
- break
- }
- }
-
- for _, k := range keys {
- if k.Verified && activated {
- commitVerification := verifySSHCommitVerification(c.Signature.Signature, c.Signature.Payload, k, committer, committer, c.Committer.Email)
- if commitVerification != nil {
- return commitVerification
- }
- }
- }
- }
-
- return &CommitVerification{
- CommittingUser: committer,
- Verified: false,
- Reason: NoKeyFound,
- }
-}
-
-func verifySSHCommitVerification(sig, payload string, k *PublicKey, committer, signer *user_model.User, email string) *CommitVerification {
- if err := sshsig.Verify(bytes.NewBuffer([]byte(payload)), []byte(sig), []byte(k.Content), "git"); err != nil {
- return nil
- }
-
- return &CommitVerification{ // Everything is ok
- CommittingUser: committer,
- Verified: true,
- Reason: fmt.Sprintf("%s / %s", signer.Name, k.Fingerprint),
- SigningUser: signer,
- SigningSSHKey: k,
- SigningEmail: email,
- }
-}
diff --git a/models/asymkey/ssh_key_fingerprint.go b/models/asymkey/ssh_key_fingerprint.go
index 1ed3b5df2a..4dcfe1f279 100644
--- a/models/asymkey/ssh_key_fingerprint.go
+++ b/models/asymkey/ssh_key_fingerprint.go
@@ -6,27 +6,13 @@ package asymkey
import (
"context"
"fmt"
- "strings"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
"golang.org/x/crypto/ssh"
"xorm.io/builder"
)
-// ___________.__ .__ __
-// \_ _____/|__| ____ ____ ________________________|__| _____/ |_
-// | __) | |/ \ / ___\_/ __ \_ __ \____ \_ __ \ |/ \ __\
-// | \ | | | \/ /_/ > ___/| | \/ |_> > | \/ | | \ |
-// \___ / |__|___| /\___ / \___ >__| | __/|__| |__|___| /__|
-// \/ \//_____/ \/ |__| \/
-//
-// This file contains functions for fingerprinting SSH keys
-//
// The database is used in checkKeyFingerprint however most of these functions probably belong in a module
// checkKeyFingerprint only checks if key fingerprint has been used as public key,
@@ -41,29 +27,6 @@ func checkKeyFingerprint(ctx context.Context, fingerprint string) error {
return nil
}
-func calcFingerprintSSHKeygen(publicKeyContent string) (string, error) {
- // Calculate fingerprint.
- tmpPath, err := writeTmpKeyFile(publicKeyContent)
- if err != nil {
- return "", err
- }
- defer func() {
- if err := util.Remove(tmpPath); err != nil {
- log.Warn("Unable to remove temporary key file: %s: Error: %v", tmpPath, err)
- }
- }()
- stdout, stderr, err := process.GetManager().Exec("AddPublicKey", "ssh-keygen", "-lf", tmpPath)
- if err != nil {
- if strings.Contains(stderr, "is not a public key file") {
- return "", ErrKeyUnableVerify{stderr}
- }
- return "", util.NewInvalidArgumentErrorf("'ssh-keygen -lf %s' failed with error '%s': %s", tmpPath, err, stderr)
- } else if len(stdout) < 2 {
- return "", util.NewInvalidArgumentErrorf("not enough output for calculating fingerprint: %s", stdout)
- }
- return strings.Split(stdout, " ")[1], nil
-}
-
func calcFingerprintNative(publicKeyContent string) (string, error) {
// Calculate fingerprint.
pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publicKeyContent))
@@ -75,15 +38,12 @@ func calcFingerprintNative(publicKeyContent string) (string, error) {
// CalcFingerprint calculate public key's fingerprint
func CalcFingerprint(publicKeyContent string) (string, error) {
- // Call the method based on configuration
- useNative := setting.SSH.KeygenPath == ""
- calcFn := util.Iif(useNative, calcFingerprintNative, calcFingerprintSSHKeygen)
- fp, err := calcFn(publicKeyContent)
+ fp, err := calcFingerprintNative(publicKeyContent)
if err != nil {
if IsErrKeyUnableVerify(err) {
return "", err
}
- return "", fmt.Errorf("CalcFingerprint(%s): %w", util.Iif(useNative, "native", "ssh-keygen"), err)
+ return "", fmt.Errorf("CalcFingerprint: %w", err)
}
return fp, nil
}
diff --git a/models/asymkey/ssh_key_parse.go b/models/asymkey/ssh_key_parse.go
index 94b1cf112b..fc39f28624 100644
--- a/models/asymkey/ssh_key_parse.go
+++ b/models/asymkey/ssh_key_parse.go
@@ -10,14 +10,12 @@ import (
"encoding/base64"
"encoding/binary"
"encoding/pem"
+ "errors"
"fmt"
"math/big"
- "os"
- "strconv"
"strings"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@@ -93,7 +91,7 @@ func parseKeyString(content string) (string, error) {
block, _ := pem.Decode([]byte(content))
if block == nil {
- return "", fmt.Errorf("failed to parse PEM block containing the public key")
+ return "", errors.New("failed to parse PEM block containing the public key")
}
if strings.Contains(block.Type, "PRIVATE") {
return "", ErrKeyIsPrivate
@@ -174,20 +172,9 @@ func CheckPublicKeyString(content string) (_ string, err error) {
return content, nil
}
- var (
- fnName string
- keyType string
- length int
- )
- if len(setting.SSH.KeygenPath) == 0 {
- fnName = "SSHNativeParsePublicKey"
- keyType, length, err = SSHNativeParsePublicKey(content)
- } else {
- fnName = "SSHKeyGenParsePublicKey"
- keyType, length, err = SSHKeyGenParsePublicKey(content)
- }
+ keyType, length, err := SSHNativeParsePublicKey(content)
if err != nil {
- return "", fmt.Errorf("%s: %w", fnName, err)
+ return "", fmt.Errorf("SSHNativeParsePublicKey: %w", err)
}
log.Trace("Key info [native: %v]: %s-%d", setting.SSH.StartBuiltinServer, keyType, length)
@@ -221,7 +208,7 @@ func SSHNativeParsePublicKey(keyLine string) (string, int, error) {
// The ssh library can parse the key, so next we find out what key exactly we have.
switch pkey.Type() {
- case ssh.KeyAlgoDSA:
+ case ssh.KeyAlgoDSA: //nolint:staticcheck // it's deprecated
rawPub := struct {
Name string
P, Q, G, Y *big.Int
@@ -257,56 +244,3 @@ func SSHNativeParsePublicKey(keyLine string) (string, int, error) {
}
return "", 0, fmt.Errorf("unsupported key length detection for type: %s", pkey.Type())
}
-
-// writeTmpKeyFile writes key content to a temporary file
-// and returns the name of that file, along with any possible errors.
-func writeTmpKeyFile(content string) (string, error) {
- tmpFile, err := os.CreateTemp(setting.SSH.KeyTestPath, "gitea_keytest")
- if err != nil {
- return "", fmt.Errorf("TempFile: %w", err)
- }
- defer tmpFile.Close()
-
- if _, err = tmpFile.WriteString(content); err != nil {
- return "", fmt.Errorf("WriteString: %w", err)
- }
- return tmpFile.Name(), nil
-}
-
-// SSHKeyGenParsePublicKey extracts key type and length using ssh-keygen.
-func SSHKeyGenParsePublicKey(key string) (string, int, error) {
- tmpName, err := writeTmpKeyFile(key)
- if err != nil {
- return "", 0, fmt.Errorf("writeTmpKeyFile: %w", err)
- }
- defer func() {
- if err := util.Remove(tmpName); err != nil {
- log.Warn("Unable to remove temporary key file: %s: Error: %v", tmpName, err)
- }
- }()
-
- keygenPath := setting.SSH.KeygenPath
- if len(keygenPath) == 0 {
- keygenPath = "ssh-keygen"
- }
-
- stdout, stderr, err := process.GetManager().Exec("SSHKeyGenParsePublicKey", keygenPath, "-lf", tmpName)
- if err != nil {
- return "", 0, fmt.Errorf("fail to parse public key: %s - %s", err, stderr)
- }
- if strings.Contains(stdout, "is not a public key file") {
- return "", 0, ErrKeyUnableVerify{stdout}
- }
-
- fields := strings.Split(stdout, " ")
- if len(fields) < 4 {
- return "", 0, fmt.Errorf("invalid public key line: %s", stdout)
- }
-
- keyType := strings.Trim(fields[len(fields)-1], "()\r\n")
- length, err := strconv.ParseInt(fields[0], 10, 32)
- if err != nil {
- return "", 0, err
- }
- return strings.ToLower(keyType), int(length), nil
-}
diff --git a/models/asymkey/ssh_key_test.go b/models/asymkey/ssh_key_test.go
index 3650f1892f..21e4ddf62e 100644
--- a/models/asymkey/ssh_key_test.go
+++ b/models/asymkey/ssh_key_test.go
@@ -42,29 +42,7 @@ func Test_SSHParsePublicKey(t *testing.T) {
keyTypeN, lengthN, err := SSHNativeParsePublicKey(tc.content)
assert.NoError(t, err)
assert.Equal(t, tc.keyType, keyTypeN)
- assert.EqualValues(t, tc.length, lengthN)
- })
- if tc.skipSSHKeygen {
- return
- }
- t.Run("SSHKeygen", func(t *testing.T) {
- keyTypeK, lengthK, err := SSHKeyGenParsePublicKey(tc.content)
- if err != nil {
- // Some servers do not support ecdsa format.
- if !strings.Contains(err.Error(), "line 1 too long:") {
- assert.FailNow(t, "%v", err)
- }
- }
- assert.Equal(t, tc.keyType, keyTypeK)
- assert.EqualValues(t, tc.length, lengthK)
- })
- t.Run("SSHParseKeyNative", func(t *testing.T) {
- keyTypeK, lengthK, err := SSHNativeParsePublicKey(tc.content)
- if err != nil {
- assert.FailNow(t, "%v", err)
- }
- assert.Equal(t, tc.keyType, keyTypeK)
- assert.EqualValues(t, tc.length, lengthK)
+ assert.Equal(t, tc.length, lengthN)
})
})
}
@@ -186,14 +164,6 @@ func Test_calcFingerprint(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, tc.fp, fpN)
})
- if tc.skipSSHKeygen {
- return
- }
- t.Run("SSHKeygen", func(t *testing.T) {
- fpK, err := calcFingerprintSSHKeygen(tc.content)
- assert.NoError(t, err)
- assert.Equal(t, tc.fp, fpK)
- })
})
}
}
diff --git a/models/asymkey/ssh_key_verify.go b/models/asymkey/ssh_key_verify.go
index 208288c77b..0cf29ca9f1 100644
--- a/models/asymkey/ssh_key_verify.go
+++ b/models/asymkey/ssh_key_verify.go
@@ -4,8 +4,8 @@
package asymkey
import (
- "bytes"
"context"
+ "strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
@@ -30,12 +30,12 @@ func VerifySSHKey(ctx context.Context, ownerID int64, fingerprint, token, signat
return "", ErrKeyNotExist{}
}
- err = sshsig.Verify(bytes.NewBuffer([]byte(token)), []byte(signature), []byte(key.Content), "gitea")
+ err = sshsig.Verify(strings.NewReader(token), []byte(signature), []byte(key.Content), "gitea")
if err != nil {
// edge case for Windows based shells that will add CR LF if piped to ssh-keygen command
// see https://github.com/PowerShell/PowerShell/issues/5974
- if sshsig.Verify(bytes.NewBuffer([]byte(token+"\r\n")), []byte(signature), []byte(key.Content), "gitea") != nil {
- log.Error("Unable to validate token signature. Error: %v", err)
+ if sshsig.Verify(strings.NewReader(token+"\r\n"), []byte(signature), []byte(key.Content), "gitea") != nil {
+ log.Debug("VerifySSHKey sshsig.Verify failed: %v", err)
return "", ErrSSHInvalidTokenSignature{
Fingerprint: key.Fingerprint,
}
diff --git a/models/auth/access_token_scope.go b/models/auth/access_token_scope.go
index 897ff3fc9e..3eae19b2a5 100644
--- a/models/auth/access_token_scope.go
+++ b/models/auth/access_token_scope.go
@@ -5,6 +5,7 @@ package auth
import (
"fmt"
+ "slices"
"strings"
"code.gitea.io/gitea/models/perm"
@@ -14,7 +15,7 @@ import (
type AccessTokenScopeCategory int
const (
- AccessTokenScopeCategoryActivityPub = iota
+ AccessTokenScopeCategoryActivityPub AccessTokenScopeCategory = iota
AccessTokenScopeCategoryAdmin
AccessTokenScopeCategoryMisc // WARN: this is now just a placeholder, don't remove it which will change the following values
AccessTokenScopeCategoryNotification
@@ -193,6 +194,14 @@ var accessTokenScopes = map[AccessTokenScopeLevel]map[AccessTokenScopeCategory]A
},
}
+func GetAccessTokenCategories() (res []string) {
+ for _, cat := range accessTokenScopes[Read] {
+ res = append(res, strings.TrimPrefix(string(cat), "read:"))
+ }
+ slices.Sort(res)
+ return res
+}
+
// GetRequiredScopes gets the specific scopes for a given level and categories
func GetRequiredScopes(level AccessTokenScopeLevel, scopeCategories ...AccessTokenScopeCategory) []AccessTokenScope {
scopes := make([]AccessTokenScope, 0, len(scopeCategories))
@@ -204,12 +213,7 @@ func GetRequiredScopes(level AccessTokenScopeLevel, scopeCategories ...AccessTok
// ContainsCategory checks if a list of categories contains a specific category
func ContainsCategory(categories []AccessTokenScopeCategory, category AccessTokenScopeCategory) bool {
- for _, c := range categories {
- if c == category {
- return true
- }
- }
- return false
+ return slices.Contains(categories, category)
}
// GetScopeLevelFromAccessMode converts permission access mode to scope level
@@ -270,6 +274,9 @@ func (s AccessTokenScope) parse() (accessTokenScopeBitmap, error) {
// StringSlice returns the AccessTokenScope as a []string
func (s AccessTokenScope) StringSlice() []string {
+ if s == "" {
+ return nil
+ }
return strings.Split(string(s), ",")
}
@@ -283,6 +290,10 @@ func (s AccessTokenScope) Normalize() (AccessTokenScope, error) {
return bitmap.toScope(), nil
}
+func (s AccessTokenScope) HasPermissionScope() bool {
+ return s != "" && s != AccessTokenScopePublicOnly
+}
+
// PublicOnly checks if this token scope is limited to public resources
func (s AccessTokenScope) PublicOnly() (bool, error) {
bitmap, err := s.parse()
diff --git a/models/auth/access_token_scope_test.go b/models/auth/access_token_scope_test.go
index a6097e45d7..b93c25528f 100644
--- a/models/auth/access_token_scope_test.go
+++ b/models/auth/access_token_scope_test.go
@@ -17,6 +17,7 @@ type scopeTestNormalize struct {
}
func TestAccessTokenScope_Normalize(t *testing.T) {
+ assert.Equal(t, []string{"activitypub", "admin", "issue", "misc", "notification", "organization", "package", "repository", "user"}, GetAccessTokenCategories())
tests := []scopeTestNormalize{
{"", "", nil},
{"write:misc,write:notification,read:package,write:notification,public-only", "public-only,write:misc,write:notification,read:package", nil},
@@ -25,13 +26,13 @@ func TestAccessTokenScope_Normalize(t *testing.T) {
{"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user,public-only", "public-only,all", nil},
}
- for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} {
+ for _, scope := range GetAccessTokenCategories() {
tests = append(tests,
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%s", scope)), AccessTokenScope(fmt.Sprintf("read:%s", scope)), nil},
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("write:%s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil},
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("write:%[1]s,read:%[1]s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil},
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%[1]s,write:%[1]s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil},
- scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%[1]s,write:%[1]s,write:%[1]s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil},
+ scopeTestNormalize{AccessTokenScope("read:" + scope), AccessTokenScope("read:" + scope), nil},
+ scopeTestNormalize{AccessTokenScope("write:" + scope), AccessTokenScope("write:" + scope), nil},
+ scopeTestNormalize{AccessTokenScope(fmt.Sprintf("write:%[1]s,read:%[1]s", scope)), AccessTokenScope("write:" + scope), nil},
+ scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%[1]s,write:%[1]s", scope)), AccessTokenScope("write:" + scope), nil},
+ scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%[1]s,write:%[1]s,write:%[1]s", scope)), AccessTokenScope("write:" + scope), nil},
)
}
@@ -59,23 +60,23 @@ func TestAccessTokenScope_HasScope(t *testing.T) {
{"public-only", "read:issue", false, nil},
}
- for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} {
+ for _, scope := range GetAccessTokenCategories() {
tests = append(tests,
scopeTestHasScope{
- AccessTokenScope(fmt.Sprintf("read:%s", scope)),
- AccessTokenScope(fmt.Sprintf("read:%s", scope)), true, nil,
+ AccessTokenScope("read:" + scope),
+ AccessTokenScope("read:" + scope), true, nil,
},
scopeTestHasScope{
- AccessTokenScope(fmt.Sprintf("write:%s", scope)),
- AccessTokenScope(fmt.Sprintf("write:%s", scope)), true, nil,
+ AccessTokenScope("write:" + scope),
+ AccessTokenScope("write:" + scope), true, nil,
},
scopeTestHasScope{
- AccessTokenScope(fmt.Sprintf("write:%s", scope)),
- AccessTokenScope(fmt.Sprintf("read:%s", scope)), true, nil,
+ AccessTokenScope("write:" + scope),
+ AccessTokenScope("read:" + scope), true, nil,
},
scopeTestHasScope{
- AccessTokenScope(fmt.Sprintf("read:%s", scope)),
- AccessTokenScope(fmt.Sprintf("write:%s", scope)), false, nil,
+ AccessTokenScope("read:" + scope),
+ AccessTokenScope("write:" + scope), false, nil,
},
)
}
diff --git a/models/auth/auth_token.go b/models/auth/auth_token.go
index 81f07d1a83..54ff5a0d75 100644
--- a/models/auth/auth_token.go
+++ b/models/auth/auth_token.go
@@ -15,7 +15,7 @@ import (
var ErrAuthTokenNotExist = util.NewNotExistErrorf("auth token does not exist")
-type AuthToken struct { //nolint:revive
+type AuthToken struct { //nolint:revive // export stutter
ID string `xorm:"pk"`
TokenHash string
UserID int64 `xorm:"INDEX"`
diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go
index c270e4856e..c2b6690116 100644
--- a/models/auth/oauth2.go
+++ b/models/auth/oauth2.go
@@ -12,6 +12,7 @@ import (
"fmt"
"net"
"net/url"
+ "slices"
"strings"
"code.gitea.io/gitea/models/db"
@@ -511,12 +512,7 @@ func (grant *OAuth2Grant) IncreaseCounter(ctx context.Context) error {
// ScopeContains returns true if the grant scope contains the specified scope
func (grant *OAuth2Grant) ScopeContains(scope string) bool {
- for _, currentScope := range strings.Split(grant.Scope, " ") {
- if scope == currentScope {
- return true
- }
- }
- return false
+ return slices.Contains(strings.Split(grant.Scope, " "), scope)
}
// SetNonce updates the current nonce value of a grant
diff --git a/models/auth/oauth2_test.go b/models/auth/oauth2_test.go
index 43daa0b5ec..c6626b283e 100644
--- a/models/auth/oauth2_test.go
+++ b/models/auth/oauth2_test.go
@@ -25,7 +25,7 @@ func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
func BenchmarkOAuth2Application_GenerateClientSecret(b *testing.B) {
assert.NoError(b, unittest.PrepareTestDatabase())
app := unittest.AssertExistsAndLoadBean(b, &auth_model.OAuth2Application{ID: 1})
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
_, _ = app.GenerateClientSecret(db.DefaultContext)
}
}
@@ -126,7 +126,7 @@ func TestOAuth2Application_CreateGrant(t *testing.T) {
assert.NotNil(t, grant)
assert.Equal(t, int64(2), grant.UserID)
assert.Equal(t, int64(1), grant.ApplicationID)
- assert.Equal(t, "", grant.Scope)
+ assert.Empty(t, grant.Scope)
}
//////////////////// Grant
diff --git a/models/auth/source.go b/models/auth/source.go
index a3a250cd91..7d7bc0f03c 100644
--- a/models/auth/source.go
+++ b/models/auth/source.go
@@ -58,6 +58,15 @@ var Names = map[Type]string{
// Config represents login config as far as the db is concerned
type Config interface {
convert.Conversion
+ SetAuthSource(*Source)
+}
+
+type ConfigBase struct {
+ AuthSource *Source
+}
+
+func (p *ConfigBase) SetAuthSource(s *Source) {
+ p.AuthSource = s
}
// SkipVerifiable configurations provide a IsSkipVerify to check if SkipVerify is set
@@ -104,19 +113,15 @@ func RegisterTypeConfig(typ Type, exemplar Config) {
}
}
-// SourceSettable configurations can have their authSource set on them
-type SourceSettable interface {
- SetAuthSource(*Source)
-}
-
// Source represents an external way for authorizing users.
type Source struct {
- ID int64 `xorm:"pk autoincr"`
- Type Type
- Name string `xorm:"UNIQUE"`
- IsActive bool `xorm:"INDEX NOT NULL DEFAULT false"`
- IsSyncEnabled bool `xorm:"INDEX NOT NULL DEFAULT false"`
- Cfg convert.Conversion `xorm:"TEXT"`
+ ID int64 `xorm:"pk autoincr"`
+ Type Type
+ Name string `xorm:"UNIQUE"`
+ IsActive bool `xorm:"INDEX NOT NULL DEFAULT false"`
+ IsSyncEnabled bool `xorm:"INDEX NOT NULL DEFAULT false"`
+ TwoFactorPolicy string `xorm:"two_factor_policy NOT NULL DEFAULT ''"`
+ Cfg Config `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
@@ -140,9 +145,7 @@ func (source *Source) BeforeSet(colName string, val xorm.Cell) {
return
}
source.Cfg = constructor()
- if settable, ok := source.Cfg.(SourceSettable); ok {
- settable.SetAuthSource(source)
- }
+ source.Cfg.SetAuthSource(source)
}
}
@@ -200,6 +203,10 @@ func (source *Source) SkipVerify() bool {
return ok && skipVerifiable.IsSkipVerify()
}
+func (source *Source) TwoFactorShouldSkip() bool {
+ return source.TwoFactorPolicy == "skip"
+}
+
// CreateSource inserts a AuthSource in the DB if not already
// existing with the given name.
func CreateSource(ctx context.Context, source *Source) error {
@@ -223,9 +230,7 @@ func CreateSource(ctx context.Context, source *Source) error {
return nil
}
- if settable, ok := source.Cfg.(SourceSettable); ok {
- settable.SetAuthSource(source)
- }
+ source.Cfg.SetAuthSource(source)
registerableSource, ok := source.Cfg.(RegisterableSource)
if !ok {
@@ -320,9 +325,7 @@ func UpdateSource(ctx context.Context, source *Source) error {
return nil
}
- if settable, ok := source.Cfg.(SourceSettable); ok {
- settable.SetAuthSource(source)
- }
+ source.Cfg.SetAuthSource(source)
registerableSource, ok := source.Cfg.(RegisterableSource)
if !ok {
diff --git a/models/auth/source_test.go b/models/auth/source_test.go
index 84aede0a6b..64c7460b64 100644
--- a/models/auth/source_test.go
+++ b/models/auth/source_test.go
@@ -19,6 +19,8 @@ import (
)
type TestSource struct {
+ auth_model.ConfigBase
+
Provider string
ClientID string
ClientSecret string
diff --git a/models/auth/twofactor.go b/models/auth/twofactor.go
index d0c341a192..200ce7c7c0 100644
--- a/models/auth/twofactor.go
+++ b/models/auth/twofactor.go
@@ -164,3 +164,13 @@ func DeleteTwoFactorByID(ctx context.Context, id, userID int64) error {
}
return nil
}
+
+func HasTwoFactorOrWebAuthn(ctx context.Context, id int64) (bool, error) {
+ has, err := HasTwoFactorByUID(ctx, id)
+ if err != nil {
+ return false, err
+ } else if has {
+ return true, nil
+ }
+ return HasWebAuthnRegistrationsByUID(ctx, id)
+}
diff --git a/models/db/context.go b/models/db/context.go
index 51627712b1..ad99ada8c8 100644
--- a/models/db/context.go
+++ b/models/db/context.go
@@ -67,7 +67,7 @@ func contextSafetyCheck(e Engine) {
_ = e.SQL("SELECT 1").Iterate(&m{}, func(int, any) error {
callers := make([]uintptr, 32)
callerNum := runtime.Callers(1, callers)
- for i := 0; i < callerNum; i++ {
+ for i := range callerNum {
if funcName := runtime.FuncForPC(callers[i]).Name(); funcName == "xorm.io/xorm.(*Session).Iterate" {
contextSafetyDeniedFuncPCs = append(contextSafetyDeniedFuncPCs, callers[i])
}
@@ -82,7 +82,7 @@ func contextSafetyCheck(e Engine) {
// it should be very fast: xxxx ns/op
callers := make([]uintptr, 32)
callerNum := runtime.Callers(3, callers) // skip 3: runtime.Callers, contextSafetyCheck, GetEngine
- for i := 0; i < callerNum; i++ {
+ for i := range callerNum {
if slices.Contains(contextSafetyDeniedFuncPCs, callers[i]) {
panic(errors.New("using database context in an iterator would cause corrupted results"))
}
@@ -178,6 +178,15 @@ func WithTx(parentCtx context.Context, f func(ctx context.Context) error) error
return txWithNoCheck(parentCtx, f)
}
+// WithTx2 is similar to WithTx, but it has two return values: result and error.
+func WithTx2[T any](parentCtx context.Context, f func(ctx context.Context) (T, error)) (ret T, errRet error) {
+ errRet = WithTx(parentCtx, func(ctx context.Context) (errInner error) {
+ ret, errInner = f(ctx)
+ return errInner
+ })
+ return ret, errRet
+}
+
func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error) error {
sess := xormEngine.NewSession()
defer sess.Close()
@@ -289,6 +298,9 @@ func FindIDs(ctx context.Context, tableName, idCol string, cond builder.Cond) ([
// DecrByIDs decreases the given column for entities of the "bean" type with one of the given ids by one
// Timestamps of the entities won't be updated
func DecrByIDs(ctx context.Context, ids []int64, decrCol string, bean any) error {
+ if len(ids) == 0 {
+ return nil
+ }
_, err := GetEngine(ctx).Decr(decrCol).In("id", ids).NoAutoCondition().NoAutoTime().Update(bean)
return err
}
diff --git a/models/db/context_test.go b/models/db/context_test.go
index e8c6b74d93..a6bd11d2ae 100644
--- a/models/db/context_test.go
+++ b/models/db/context_test.go
@@ -118,7 +118,7 @@ func TestContextSafety(t *testing.T) {
})
return nil
})
- assert.EqualValues(t, testCount, actualCount)
+ assert.Equal(t, testCount, actualCount)
// deny the bad usages
assert.PanicsWithError(t, "using database context in an iterator would cause corrupted results", func() {
diff --git a/models/db/engine.go b/models/db/engine.go
index 91015f7038..ba287d58f0 100755
--- a/models/db/engine.go
+++ b/models/db/engine.go
@@ -127,7 +127,7 @@ func IsTableNotEmpty(beanOrTableName any) (bool, error) {
// DeleteAllRecords will delete all the records of this table
func DeleteAllRecords(tableName string) error {
- _, err := xormEngine.Exec(fmt.Sprintf("DELETE FROM %s", tableName))
+ _, err := xormEngine.Exec("DELETE FROM " + tableName)
return err
}
diff --git a/models/db/engine_hook.go b/models/db/engine_hook.go
index b4c543c3dd..8709a2c2a1 100644
--- a/models/db/engine_hook.go
+++ b/models/db/engine_hook.go
@@ -7,28 +7,41 @@ import (
"context"
"time"
+ "code.gitea.io/gitea/modules/gtprof"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
"xorm.io/xorm/contexts"
)
-type SlowQueryHook struct {
+type EngineHook struct {
Threshold time.Duration
Logger log.Logger
}
-var _ contexts.Hook = (*SlowQueryHook)(nil)
+var _ contexts.Hook = (*EngineHook)(nil)
-func (*SlowQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
- return c.Ctx, nil
+func (*EngineHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
+ ctx, _ := gtprof.GetTracer().Start(c.Ctx, gtprof.TraceSpanDatabase)
+ return ctx, nil
}
-func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error {
+func (h *EngineHook) AfterProcess(c *contexts.ContextHook) error {
+ span := gtprof.GetContextSpan(c.Ctx)
+ if span != nil {
+ // Do not record SQL parameters here:
+ // * It shouldn't expose the parameters because they contain sensitive information, end users need to report the trace details safely.
+ // * Some parameters contain quite long texts, waste memory and are difficult to display.
+ span.SetAttributeString(gtprof.TraceAttrDbSQL, c.SQL)
+ span.End()
+ } else {
+ setting.PanicInDevOrTesting("span in database engine hook is nil")
+ }
if c.ExecuteTime >= h.Threshold {
// 8 is the amount of skips passed to runtime.Caller, so that in the log the correct function
// is being displayed (the function that ultimately wants to execute the query in the code)
// instead of the function of the slow query hook being called.
- h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
+ h.Logger.Log(8, &log.Event{Level: log.WARN}, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
}
return nil
}
diff --git a/models/db/engine_init.go b/models/db/engine_init.go
index da85018957..bb02aff274 100644
--- a/models/db/engine_init.go
+++ b/models/db/engine_init.go
@@ -42,9 +42,10 @@ func newXORMEngine() (*xorm.Engine, error) {
if err != nil {
return nil, err
}
- if setting.Database.Type == "mysql" {
+ switch setting.Database.Type {
+ case "mysql":
engine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"})
- } else if setting.Database.Type == "mssql" {
+ case "mssql":
engine.Dialect().SetParams(map[string]string{"DEFAULT_VARCHAR": "nvarchar"})
}
engine.SetSchema(setting.Database.Schema)
@@ -72,7 +73,7 @@ func InitEngine(ctx context.Context) error {
xe.SetDefaultContext(ctx)
if setting.Database.SlowQueryThreshold > 0 {
- xe.AddHook(&SlowQueryHook{
+ xe.AddHook(&EngineHook{
Threshold: setting.Database.SlowQueryThreshold,
Logger: log.GetLogger("xorm"),
})
@@ -105,7 +106,7 @@ func UnsetDefaultEngine() {
// When called from the "doctor" command, the migration function is a version check
// that prevents the doctor from fixing anything in the database if the migration level
// is different from the expected value.
-func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err error) {
+func InitEngineWithMigration(ctx context.Context, migrateFunc func(context.Context, *xorm.Engine) error) (err error) {
if err = InitEngine(ctx); err != nil {
return err
}
@@ -122,7 +123,7 @@ func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine)
// Installation should only be being re-run if users want to recover an old database.
// However, we should think carefully about should we support re-install on an installed instance,
// as there may be other problems due to secret reinitialization.
- if err = migrateFunc(xormEngine); err != nil {
+ if err = migrateFunc(ctx, xormEngine); err != nil {
return fmt.Errorf("migrate: %w", err)
}
diff --git a/models/db/engine_test.go b/models/db/engine_test.go
index e3dbfbdc24..a236f83735 100644
--- a/models/db/engine_test.go
+++ b/models/db/engine_test.go
@@ -15,6 +15,7 @@ import (
_ "code.gitea.io/gitea/cmd" // for TestPrimaryKeys
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestDumpDatabase(t *testing.T) {
@@ -51,7 +52,7 @@ func TestDeleteOrphanedObjects(t *testing.T) {
countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{})
assert.NoError(t, err)
- assert.EqualValues(t, countBefore, countAfter)
+ assert.Equal(t, countBefore, countAfter)
}
func TestPrimaryKeys(t *testing.T) {
@@ -62,9 +63,7 @@ func TestPrimaryKeys(t *testing.T) {
// 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)
- }
+ require.NoError(t, err)
whitelist := map[string]string{
"the_table_name_to_skip_checking": "Write a note here to explain why",
@@ -79,8 +78,6 @@ func TestPrimaryKeys(t *testing.T) {
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)
- }
+ assert.NotEmpty(t, table.PrimaryKeys, "table %q has no primary key", table.Name)
}
}
diff --git a/models/db/error.go b/models/db/error.go
index 665e970e17..d47c7adac4 100644
--- a/models/db/error.go
+++ b/models/db/error.go
@@ -65,7 +65,7 @@ func (err ErrNotExist) Error() string {
if err.ID != 0 {
return fmt.Sprintf("%s does not exist [id: %d]", name, err.ID)
}
- return fmt.Sprintf("%s does not exist", name)
+ return name + " does not exist"
}
// Unwrap unwraps this as a ErrNotExist err
diff --git a/models/db/list_test.go b/models/db/list_test.go
index 45194611f8..170473a968 100644
--- a/models/db/list_test.go
+++ b/models/db/list_test.go
@@ -47,6 +47,6 @@ func TestFind(t *testing.T) {
repoUnits, newCnt, err := db.FindAndCount[repo_model.RepoUnit](db.DefaultContext, opts)
assert.NoError(t, err)
- assert.EqualValues(t, cnt, newCnt)
+ assert.Equal(t, cnt, newCnt)
assert.Len(t, repoUnits, repoUnitCount)
}
diff --git a/models/db/log.go b/models/db/log.go
index 307788ea2e..a9df6f541d 100644
--- a/models/db/log.go
+++ b/models/db/log.go
@@ -29,7 +29,7 @@ const stackLevel = 8
// Log a message with defined skip and at logging level
func (l *XORMLogBridge) Log(skip int, level log.Level, format string, v ...any) {
- l.logger.Log(skip+1, level, format, v...)
+ l.logger.Log(skip+1, &log.Event{Level: level}, format, v...)
}
// Debug show debug log
diff --git a/models/db/name.go b/models/db/name.go
index 5f98edbb28..48c7fdbce5 100644
--- a/models/db/name.go
+++ b/models/db/name.go
@@ -5,14 +5,13 @@ package db
import (
"fmt"
+ "slices"
"strings"
"unicode/utf8"
"code.gitea.io/gitea/modules/util"
)
-var ErrNameEmpty = util.SilentWrap{Message: "name is empty", Err: util.ErrInvalidArgument}
-
// ErrNameReserved represents a "reserved name" error.
type ErrNameReserved struct {
Name string
@@ -79,13 +78,11 @@ func (err ErrNameCharsNotAllowed) Unwrap() error {
func IsUsableName(reservedNames, reservedPatterns []string, name string) error {
name = strings.TrimSpace(strings.ToLower(name))
if utf8.RuneCountInString(name) == 0 {
- return ErrNameEmpty
+ return util.NewInvalidArgumentErrorf("name is empty")
}
- for i := range reservedNames {
- if name == reservedNames[i] {
- return ErrNameReserved{name}
- }
+ if slices.Contains(reservedNames, name) {
+ return ErrNameReserved{name}
}
for _, pat := range reservedPatterns {
diff --git a/models/db/search.go b/models/db/search.go
index e0a1b6bde9..44d54f21fc 100644
--- a/models/db/search.go
+++ b/models/db/search.go
@@ -29,7 +29,3 @@ const (
// NoConditionID means a condition to filter the records which don't match any id.
// eg: "milestone_id=-1" means "find the items without any milestone.
const NoConditionID int64 = -1
-
-// NonExistingID means a condition to match no result (eg: a non-existing user)
-// It doesn't use -1 or -2 because they are used as builtin users.
-const NonExistingID int64 = -1000000
diff --git a/models/db/sql_postgres_with_schema.go b/models/db/sql_postgres_with_schema.go
index 64b61b2ef3..812fe4a6a6 100644
--- a/models/db/sql_postgres_with_schema.go
+++ b/models/db/sql_postgres_with_schema.go
@@ -39,7 +39,7 @@ func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) {
// golangci lint is incorrect here - there is no benefit to using driver.ExecerContext here
// and in any case pq does not implement it
- if execer, ok := conn.(driver.Execer); ok { //nolint:staticcheck
+ if execer, ok := conn.(driver.Execer); ok { //nolint:staticcheck // see above
_, err := execer.Exec(`SELECT set_config(
'search_path',
$1 || ',' || current_setting('search_path'),
@@ -64,7 +64,7 @@ func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) {
// driver.String.ConvertValue will never return err for string
// golangci lint is incorrect here - there is no benefit to using stmt.ExecWithContext here
- _, err = stmt.Exec([]driver.Value{schemaValue}) //nolint:staticcheck
+ _, err = stmt.Exec([]driver.Value{schemaValue}) //nolint:staticcheck // see above
if err != nil {
_ = conn.Close()
return nil, err
diff --git a/models/dbfs/dbfile.go b/models/dbfs/dbfile.go
index dd27b5c36b..eaf506fbe6 100644
--- a/models/dbfs/dbfile.go
+++ b/models/dbfs/dbfile.go
@@ -46,10 +46,7 @@ func (f *file) readAt(fileMeta *dbfsMeta, offset int64, p []byte) (n int, err er
blobPos := int(offset % f.blockSize)
blobOffset := offset - int64(blobPos)
blobRemaining := int(f.blockSize) - blobPos
- needRead := len(p)
- if needRead > blobRemaining {
- needRead = blobRemaining
- }
+ needRead := min(len(p), blobRemaining)
if blobOffset+int64(blobPos)+int64(needRead) > fileMeta.FileSize {
needRead = int(fileMeta.FileSize - blobOffset - int64(blobPos))
}
@@ -66,14 +63,8 @@ func (f *file) readAt(fileMeta *dbfsMeta, offset int64, p []byte) (n int, err er
blobData = nil
}
- canCopy := len(blobData) - blobPos
- if canCopy <= 0 {
- canCopy = 0
- }
- realRead := needRead
- if realRead > canCopy {
- realRead = canCopy
- }
+ canCopy := max(len(blobData)-blobPos, 0)
+ realRead := min(needRead, canCopy)
if realRead > 0 {
copy(p[:realRead], fileData.BlobData[blobPos:blobPos+realRead])
}
@@ -113,10 +104,7 @@ func (f *file) Write(p []byte) (n int, err error) {
blobPos := int(f.offset % f.blockSize)
blobOffset := f.offset - int64(blobPos)
blobRemaining := int(f.blockSize) - blobPos
- needWrite := len(p)
- if needWrite > blobRemaining {
- needWrite = blobRemaining
- }
+ needWrite := min(len(p), blobRemaining)
buf := make([]byte, f.blockSize)
readBytes, err := f.readAt(fileMeta, blobOffset, buf)
if err != nil && !errors.Is(err, io.EOF) {
diff --git a/models/dbfs/dbfs_test.go b/models/dbfs/dbfs_test.go
index 96cb1014c7..0257d2bd15 100644
--- a/models/dbfs/dbfs_test.go
+++ b/models/dbfs/dbfs_test.go
@@ -31,15 +31,15 @@ func TestDbfsBasic(t *testing.T) {
n, err := f.Write([]byte("0123456789")) // blocks: 0123 4567 89
assert.NoError(t, err)
- assert.EqualValues(t, 10, n)
+ assert.Equal(t, 10, n)
_, err = f.Seek(0, io.SeekStart)
assert.NoError(t, err)
buf, err := io.ReadAll(f)
assert.NoError(t, err)
- assert.EqualValues(t, 10, n)
- assert.EqualValues(t, "0123456789", string(buf))
+ assert.Equal(t, 10, n)
+ assert.Equal(t, "0123456789", string(buf))
// write some new data
_, err = f.Seek(1, io.SeekStart)
@@ -50,14 +50,14 @@ func TestDbfsBasic(t *testing.T) {
// read from offset
buf, err = io.ReadAll(f)
assert.NoError(t, err)
- assert.EqualValues(t, "9", string(buf))
+ assert.Equal(t, "9", string(buf))
// read all
_, err = f.Seek(0, io.SeekStart)
assert.NoError(t, err)
buf, err = io.ReadAll(f)
assert.NoError(t, err)
- assert.EqualValues(t, "0bcdefghi9", string(buf))
+ assert.Equal(t, "0bcdefghi9", string(buf))
// write to new size
_, err = f.Seek(-1, io.SeekEnd)
@@ -68,7 +68,7 @@ func TestDbfsBasic(t *testing.T) {
assert.NoError(t, err)
buf, err = io.ReadAll(f)
assert.NoError(t, err)
- assert.EqualValues(t, "0bcdefghiJKLMNOP", string(buf))
+ assert.Equal(t, "0bcdefghiJKLMNOP", string(buf))
// write beyond EOF and fill with zero
_, err = f.Seek(5, io.SeekCurrent)
@@ -79,7 +79,7 @@ func TestDbfsBasic(t *testing.T) {
assert.NoError(t, err)
buf, err = io.ReadAll(f)
assert.NoError(t, err)
- assert.EqualValues(t, "0bcdefghiJKLMNOP\x00\x00\x00\x00\x00xyzu", string(buf))
+ assert.Equal(t, "0bcdefghiJKLMNOP\x00\x00\x00\x00\x00xyzu", string(buf))
// write to the block with zeros
_, err = f.Seek(-6, io.SeekCurrent)
@@ -90,7 +90,7 @@ func TestDbfsBasic(t *testing.T) {
assert.NoError(t, err)
buf, err = io.ReadAll(f)
assert.NoError(t, err)
- assert.EqualValues(t, "0bcdefghiJKLMNOP\x00\x00\x00ABCDzu", string(buf))
+ assert.Equal(t, "0bcdefghiJKLMNOP\x00\x00\x00ABCDzu", string(buf))
assert.NoError(t, f.Close())
@@ -117,7 +117,7 @@ func TestDbfsBasic(t *testing.T) {
assert.NoError(t, err)
stat, err := f.Stat()
assert.NoError(t, err)
- assert.EqualValues(t, "test.txt", stat.Name())
+ assert.Equal(t, "test.txt", stat.Name())
assert.EqualValues(t, 0, stat.Size())
_, err = f.Write([]byte("0123456789"))
assert.NoError(t, err)
@@ -144,7 +144,7 @@ func TestDbfsReadWrite(t *testing.T) {
line, err := f2r.ReadString('\n')
assert.NoError(t, err)
- assert.EqualValues(t, "line 1\n", line)
+ assert.Equal(t, "line 1\n", line)
_, err = f2r.ReadString('\n')
assert.ErrorIs(t, err, io.EOF)
@@ -153,7 +153,7 @@ func TestDbfsReadWrite(t *testing.T) {
line, err = f2r.ReadString('\n')
assert.NoError(t, err)
- assert.EqualValues(t, "line 2\n", line)
+ assert.Equal(t, "line 2\n", line)
_, err = f2r.ReadString('\n')
assert.ErrorIs(t, err, io.EOF)
}
@@ -186,5 +186,5 @@ func TestDbfsSeekWrite(t *testing.T) {
buf, err := io.ReadAll(fr)
assert.NoError(t, err)
- assert.EqualValues(t, "111333", string(buf))
+ assert.Equal(t, "111333", string(buf))
}
diff --git a/models/fixtures/access.yml b/models/fixtures/access.yml
index 4171e31fef..596046e950 100644
--- a/models/fixtures/access.yml
+++ b/models/fixtures/access.yml
@@ -171,3 +171,9 @@
user_id: 40
repo_id: 61
mode: 4
+
+-
+ id: 30
+ user_id: 40
+ repo_id: 1
+ mode: 2
diff --git a/models/fixtures/action_artifact.yml b/models/fixtures/action_artifact.yml
index 2c51c11ebd..ee8ef0d5ce 100644
--- a/models/fixtures/action_artifact.yml
+++ b/models/fixtures/action_artifact.yml
@@ -11,6 +11,24 @@
content_encoding: ""
artifact_path: "abc.txt"
artifact_name: "artifact-download"
+ status: 2
+ created_unix: 1712338649
+ updated_unix: 1712338649
+ expired_unix: 1720114649
+
+-
+ id: 2
+ run_id: 791
+ runner_id: 1
+ repo_id: 4
+ owner_id: 1
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ storage_path: ""
+ file_size: 1024
+ file_compressed_size: 1024
+ content_encoding: "30/20/1712348022422036662.chunk"
+ artifact_path: "abc.txt"
+ artifact_name: "artifact-download-incomplete"
status: 1
created_unix: 1712338649
updated_unix: 1712338649
@@ -69,3 +87,57 @@
created_unix: 1730330775
updated_unix: 1730330775
expired_unix: 1738106775
+
+-
+ id: 23
+ run_id: 793
+ runner_id: 1
+ repo_id: 2
+ owner_id: 2
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ storage_path: "27/5/1730330775594233150.chunk"
+ file_size: 1024
+ file_compressed_size: 1024
+ content_encoding: "application/zip"
+ artifact_path: "artifact-v4-download.zip"
+ artifact_name: "artifact-v4-download"
+ status: 2
+ created_unix: 1730330775
+ updated_unix: 1730330775
+ expired_unix: 1738106775
+
+-
+ id: 24
+ run_id: 795
+ runner_id: 1
+ repo_id: 2
+ owner_id: 2
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ storage_path: "27/5/1730330775594233150.chunk"
+ file_size: 1024
+ file_compressed_size: 1024
+ content_encoding: "application/zip"
+ artifact_path: "artifact-795-1.zip"
+ artifact_name: "artifact-795-1"
+ status: 2
+ created_unix: 1730330775
+ updated_unix: 1730330775
+ expired_unix: 1738106775
+
+-
+ id: 25
+ run_id: 795
+ runner_id: 1
+ repo_id: 2
+ owner_id: 2
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ storage_path: "27/5/1730330775594233150.chunk"
+ file_size: 1024
+ file_compressed_size: 1024
+ content_encoding: "application/zip"
+ artifact_path: "artifact-795-2.zip"
+ artifact_name: "artifact-795-2"
+ status: 2
+ created_unix: 1730330775
+ updated_unix: 1730330775
+ expired_unix: 1738106775
diff --git a/models/fixtures/action_run.yml b/models/fixtures/action_run.yml
index 1db849352f..09dfa6cccb 100644
--- a/models/fixtures/action_run.yml
+++ b/models/fixtures/action_run.yml
@@ -9,6 +9,7 @@
ref: "refs/heads/master"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
+ trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
@@ -28,6 +29,7 @@
ref: "refs/heads/master"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
+ trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
@@ -47,8 +49,9 @@
ref: "refs/heads/master"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
+ trigger_event: "push"
is_fork_pull_request: 0
- status: 1
+ status: 6 # running
started: 1683636528
stopped: 1683636626
created: 1683636108
@@ -66,6 +69,47 @@
ref: "refs/heads/test"
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
event: "push"
+ trigger_event: "push"
+ is_fork_pull_request: 0
+ status: 1
+ started: 1683636528
+ stopped: 1683636626
+ created: 1683636108
+ updated: 1683636626
+ need_approval: 0
+ approved_by: 0
+-
+ id: 802
+ title: "workflow run list"
+ repo_id: 5
+ owner_id: 3
+ workflow_id: "test.yaml"
+ index: 191
+ trigger_user_id: 1
+ ref: "refs/heads/test"
+ commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
+ event: "push"
+ trigger_event: "push"
+ is_fork_pull_request: 0
+ status: 1
+ started: 1683636528
+ stopped: 1683636626
+ created: 1683636108
+ updated: 1683636626
+ need_approval: 0
+ approved_by: 0
+-
+ id: 803
+ title: "workflow run list for user"
+ repo_id: 2
+ owner_id: 0
+ workflow_id: "test.yaml"
+ index: 192
+ trigger_user_id: 1
+ ref: "refs/heads/test"
+ commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
+ event: "push"
+ trigger_event: "push"
is_fork_pull_request: 0
status: 1
started: 1683636528
@@ -74,3 +118,24 @@
updated: 1683636626
need_approval: 0
approved_by: 0
+
+-
+ id: 795
+ title: "to be deleted (test)"
+ repo_id: 2
+ owner_id: 2
+ workflow_id: "test.yaml"
+ index: 191
+ trigger_user_id: 1
+ ref: "refs/heads/test"
+ commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
+ event: "push"
+ trigger_event: "push"
+ is_fork_pull_request: 0
+ status: 2
+ started: 1683636528
+ stopped: 1683636626
+ created: 1683636108
+ updated: 1683636626
+ need_approval: 0
+ approved_by: 0
diff --git a/models/fixtures/action_run_job.yml b/models/fixtures/action_run_job.yml
index 8837e6ec2d..6c06d94aa4 100644
--- a/models/fixtures/action_run_job.yml
+++ b/models/fixtures/action_run_job.yml
@@ -69,3 +69,63 @@
status: 5
started: 1683636528
stopped: 1683636626
+
+-
+ id: 198
+ run_id: 795
+ repo_id: 2
+ owner_id: 2
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ is_fork_pull_request: 0
+ name: job_1
+ attempt: 1
+ job_id: job_1
+ task_id: 53
+ status: 1
+ started: 1683636528
+ stopped: 1683636626
+
+-
+ id: 199
+ run_id: 795
+ repo_id: 2
+ owner_id: 2
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ is_fork_pull_request: 0
+ name: job_2
+ attempt: 1
+ job_id: job_2
+ task_id: 54
+ status: 2
+ started: 1683636528
+ stopped: 1683636626
+-
+ id: 203
+ run_id: 802
+ repo_id: 5
+ owner_id: 0
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ is_fork_pull_request: 0
+ name: job2
+ attempt: 1
+ job_id: job2
+ needs: '["job1"]'
+ task_id: 51
+ status: 5
+ started: 1683636528
+ stopped: 1683636626
+-
+ id: 204
+ run_id: 803
+ repo_id: 2
+ owner_id: 0
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ is_fork_pull_request: 0
+ name: job2
+ attempt: 1
+ job_id: job2
+ needs: '["job1"]'
+ task_id: 51
+ status: 5
+ started: 1683636528
+ stopped: 1683636626
diff --git a/models/fixtures/action_runner.yml b/models/fixtures/action_runner.yml
new file mode 100644
index 0000000000..ecb7214006
--- /dev/null
+++ b/models/fixtures/action_runner.yml
@@ -0,0 +1,51 @@
+-
+ id: 34346
+ name: runner_to_be_deleted-user
+ uuid: 3EF231BD-FBB7-4E4B-9602-E6F28363EF18
+ token_hash: 3EF231BD-FBB7-4E4B-9602-E6F28363EF18
+ version: "1.0.0"
+ owner_id: 1
+ repo_id: 0
+ description: "This runner is going to be deleted"
+ agent_labels: '["runner_to_be_deleted","linux"]'
+-
+ id: 34347
+ name: runner_to_be_deleted-org
+ uuid: 3EF231BD-FBB7-4E4B-9602-E6F28363EF19
+ token_hash: 3EF231BD-FBB7-4E4B-9602-E6F28363EF19
+ version: "1.0.0"
+ owner_id: 3
+ repo_id: 0
+ description: "This runner is going to be deleted"
+ agent_labels: '["runner_to_be_deleted","linux"]'
+-
+ id: 34348
+ name: runner_to_be_deleted-repo1
+ uuid: 3EF231BD-FBB7-4E4B-9602-E6F28363EF20
+ token_hash: 3EF231BD-FBB7-4E4B-9602-E6F28363EF20
+ version: "1.0.0"
+ owner_id: 0
+ repo_id: 1
+ description: "This runner is going to be deleted"
+ agent_labels: '["runner_to_be_deleted","linux"]'
+-
+ id: 34349
+ name: runner_to_be_deleted
+ uuid: 3EF231BD-FBB7-4E4B-9602-E6F28363EF17
+ token_hash: 3EF231BD-FBB7-4E4B-9602-E6F28363EF17
+ version: "1.0.0"
+ owner_id: 0
+ repo_id: 0
+ description: "This runner is going to be deleted"
+ agent_labels: '["runner_to_be_deleted","linux"]'
+-
+ id: 34350
+ name: runner_to_be_deleted-org-ephemeral
+ uuid: 3FF231BD-FBB7-4E4B-9602-E6F28363EF20
+ token_hash: 3FF231BD-FBB7-4E4B-9602-E6F28363EF20
+ ephemeral: true
+ version: "1.0.0"
+ owner_id: 3
+ repo_id: 0
+ description: "This runner is going to be deleted"
+ agent_labels: '["runner_to_be_deleted","linux"]'
diff --git a/models/fixtures/action_task.yml b/models/fixtures/action_task.yml
index 506a47d8a0..c79fb07050 100644
--- a/models/fixtures/action_task.yml
+++ b/models/fixtures/action_task.yml
@@ -117,3 +117,63 @@
log_length: 707
log_size: 90179
log_expired: 0
+-
+ id: 52
+ job_id: 196
+ attempt: 1
+ runner_id: 34350
+ status: 6 # running
+ started: 1683636528
+ stopped: 1683636626
+ repo_id: 4
+ owner_id: 1
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ is_fork_pull_request: 0
+ token_hash: f8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784222
+ token_salt: ffffffffff
+ token_last_eight: ffffffff
+ log_filename: artifact-test2/2f/47.log
+ log_in_storage: 1
+ log_length: 707
+ log_size: 90179
+ log_expired: 0
+-
+ id: 53
+ job_id: 198
+ attempt: 1
+ runner_id: 1
+ status: 1
+ started: 1683636528
+ stopped: 1683636626
+ repo_id: 2
+ owner_id: 2
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ is_fork_pull_request: 0
+ token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784223
+ token_salt: ffffffffff
+ token_last_eight: ffffffff
+ log_filename: artifact-test2/2f/47.log
+ log_in_storage: 1
+ log_length: 0
+ log_size: 0
+ log_expired: 0
+-
+ id: 54
+ job_id: 199
+ attempt: 1
+ runner_id: 1
+ status: 2
+ started: 1683636528
+ stopped: 1683636626
+ repo_id: 2
+ owner_id: 2
+ commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+ is_fork_pull_request: 0
+ token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784224
+ token_salt: ffffffffff
+ token_last_eight: ffffffff
+ log_filename: artifact-test2/2f/47.log
+ log_in_storage: 1
+ log_length: 0
+ log_size: 0
+ log_expired: 0
diff --git a/models/fixtures/branch.yml b/models/fixtures/branch.yml
index 17b1869ab6..03e21d04b4 100644
--- a/models/fixtures/branch.yml
+++ b/models/fixtures/branch.yml
@@ -93,3 +93,123 @@
is_deleted: false
deleted_by_id: 0
deleted_unix: 0
+
+-
+ id: 16
+ repo_id: 16
+ name: 'master'
+ commit_id: '69554a64c1e6030f051e5c3f94bfbd773cd6a324'
+ commit_message: 'not signed commit'
+ commit_time: 1502042309
+ pusher_id: 2
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 17
+ repo_id: 16
+ name: 'not-signed'
+ commit_id: '69554a64c1e6030f051e5c3f94bfbd773cd6a324'
+ commit_message: 'not signed commit'
+ commit_time: 1502042309
+ pusher_id: 2
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 18
+ repo_id: 16
+ name: 'good-sign-not-yet-validated'
+ commit_id: '27566bd5738fc8b4e3fef3c5e72cce608537bd95'
+ commit_message: 'good signed commit (with not yet validated email)'
+ commit_time: 1502042234
+ pusher_id: 2
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 19
+ repo_id: 16
+ name: 'good-sign'
+ commit_id: 'f27c2b2b03dcab38beaf89b0ab4ff61f6de63441'
+ commit_message: 'good signed commit'
+ commit_time: 1502042101
+ pusher_id: 2
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 20
+ repo_id: 1
+ name: 'feature/1'
+ commit_id: '65f1bf27bc3bf70f64657658635e66094edbcb4d'
+ commit_message: 'Initial commit'
+ commit_time: 1489950479
+ pusher_id: 2
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 21
+ repo_id: 49
+ name: 'master'
+ commit_id: 'aacbdfe9e1c4b47f60abe81849045fa4e96f1d75'
+ commit_message: "Add 'test/test.txt'"
+ commit_time: 1572535577
+ pusher_id: 2
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 22
+ repo_id: 1
+ name: 'develop'
+ commit_id: '65f1bf27bc3bf70f64657658635e66094edbcb4d'
+ commit_message: "Initial commit"
+ commit_time: 1489927679
+ pusher_id: 1
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 23
+ repo_id: 3
+ name: 'master'
+ commit_id: '2a47ca4b614a9f5a43abbd5ad851a54a616ffee6'
+ commit_message: "init project"
+ commit_time: 1497448461
+ pusher_id: 1
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 24
+ repo_id: 3
+ name: 'test_branch'
+ commit_id: 'd22b4d4daa5be07329fcef6ed458f00cf3392da0'
+ commit_message: "test commit"
+ commit_time: 1602935385
+ pusher_id: 1
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 25
+ repo_id: 54
+ name: 'master'
+ commit_id: '73cf03db6ece34e12bf91e8853dc58f678f2f82d'
+ commit_message: 'Initial commit'
+ commit_time: 1671663402
+ pusher_id: 2
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
diff --git a/models/fixtures/commit_status.yml b/models/fixtures/commit_status.yml
index 20d57975ef..87c652e53a 100644
--- a/models/fixtures/commit_status.yml
+++ b/models/fixtures/commit_status.yml
@@ -7,6 +7,7 @@
target_url: https://example.com/builds/
description: My awesome CI-service
context: ci/awesomeness
+ context_hash: c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7
creator_id: 2
-
@@ -18,6 +19,7 @@
target_url: https://example.com/converage/
description: My awesome Coverage service
context: cov/awesomeness
+ context_hash: 3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe
creator_id: 2
-
@@ -29,6 +31,7 @@
target_url: https://example.com/converage/
description: My awesome Coverage service
context: cov/awesomeness
+ context_hash: 3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe
creator_id: 2
-
@@ -40,6 +43,7 @@
target_url: https://example.com/builds/
description: My awesome CI-service
context: ci/awesomeness
+ context_hash: c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7
creator_id: 2
-
@@ -51,4 +55,5 @@
target_url: https://example.com/builds/
description: My awesome deploy service
context: deploy/awesomeness
+ context_hash: ae9547713a6665fc4261d0756904932085a41cf2
creator_id: 2
diff --git a/models/fixtures/email_address.yml b/models/fixtures/email_address.yml
index b2a0432635..0f6bd9ee6d 100644
--- a/models/fixtures/email_address.yml
+++ b/models/fixtures/email_address.yml
@@ -81,7 +81,7 @@
-
id: 11
uid: 4
- email: user4@example.com
+ email: User4@Example.Com
lower_email: user4@example.com
is_activated: true
is_primary: true
diff --git a/models/fixtures/hook_task.yml b/models/fixtures/hook_task.yml
index d573406b36..6023719b1e 100644
--- a/models/fixtures/hook_task.yml
+++ b/models/fixtures/hook_task.yml
@@ -18,7 +18,7 @@
id: 2
hook_id: 1
uuid: uuid2
- is_delivered: false
+ is_delivered: true
-
id: 3
diff --git a/models/fixtures/issue_pin.yml b/models/fixtures/issue_pin.yml
new file mode 100644
index 0000000000..14b7a72d84
--- /dev/null
+++ b/models/fixtures/issue_pin.yml
@@ -0,0 +1,6 @@
+-
+ id: 1
+ repo_id: 2
+ issue_id: 4
+ is_pull: false
+ pin_order: 1
diff --git a/models/fixtures/repo_transfer.yml b/models/fixtures/repo_transfer.yml
index db92c95248..b12e6b207f 100644
--- a/models/fixtures/repo_transfer.yml
+++ b/models/fixtures/repo_transfer.yml
@@ -21,3 +21,11 @@
repo_id: 32
created_unix: 1553610671
updated_unix: 1553610671
+
+-
+ id: 4
+ doer_id: 3
+ recipient_id: 1
+ repo_id: 5
+ created_unix: 1553610671
+ updated_unix: 1553610671
diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml
index bbb028eb7b..552a78cbd2 100644
--- a/models/fixtures/repository.yml
+++ b/models/fixtures/repository.yml
@@ -1695,19 +1695,6 @@
close_issues_via_commit_in_any_branch: false
-
- id: 59
- owner_id: 2
- owner_name: user2
- lower_name: test_commit_revert
- name: test_commit_revert
- default_branch: main
- is_empty: false
- is_archived: false
- is_private: true
- status: 0
- num_issues: 0
-
--
id: 60
owner_id: 40
owner_name: user40
diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml
index b3bece5589..976a236011 100644
--- a/models/fixtures/user.yml
+++ b/models/fixtures/user.yml
@@ -67,7 +67,7 @@
num_followers: 2
num_following: 1
num_stars: 2
- num_repos: 15
+ num_repos: 14
num_teams: 0
num_members: 0
visibility: 0
diff --git a/models/fixtures/webhook.yml b/models/fixtures/webhook.yml
index f62bae1f31..ec282914b8 100644
--- a/models/fixtures/webhook.yml
+++ b/models/fixtures/webhook.yml
@@ -1,7 +1,7 @@
-
id: 1
repo_id: 1
- url: www.example.com/url1
+ url: https://www.example.com/url1
content_type: 1 # json
events: '{"push_only":true,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":false}}'
is_active: true
@@ -9,7 +9,7 @@
-
id: 2
repo_id: 1
- url: www.example.com/url2
+ url: https://www.example.com/url2
content_type: 1 # json
events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}'
is_active: false
@@ -18,14 +18,35 @@
id: 3
owner_id: 3
repo_id: 3
- url: www.example.com/url3
+ url: https://www.example.com/url3
content_type: 1 # json
events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}'
is_active: true
+
-
id: 4
repo_id: 2
- url: www.example.com/url4
+ url: https://www.example.com/url4
+ content_type: 1 # json
+ events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
+ is_active: true
+
+-
+ id: 5
+ repo_id: 0
+ owner_id: 0
+ url: https://www.example.com/url5
+ content_type: 1 # json
+ events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
+ is_active: true
+ is_system_webhook: true
+
+-
+ id: 6
+ repo_id: 0
+ owner_id: 0
+ url: https://www.example.com/url6
content_type: 1 # json
events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
is_active: true
+ is_system_webhook: false
diff --git a/models/git/branch.go b/models/git/branch.go
index e683ce47e6..07c94a8ba5 100644
--- a/models/git/branch.go
+++ b/models/git/branch.go
@@ -167,9 +167,24 @@ func GetBranch(ctx context.Context, repoID int64, branchName string) (*Branch, e
BranchName: branchName,
}
}
+ // FIXME: this design is not right: it doesn't check `branch.IsDeleted`, it doesn't make sense to make callers to check IsDeleted again and again.
+ // It causes inconsistency with `GetBranches` and `git.GetBranch`, and will lead to strange bugs
+ // In the future, there should be 2 functions: `GetBranchExisting` and `GetBranchWithDeleted`
return &branch, nil
}
+// IsBranchExist returns true if the branch exists in the repository.
+func IsBranchExist(ctx context.Context, repoID int64, branchName string) (bool, error) {
+ var branch Branch
+ has, err := db.GetEngine(ctx).Where("repo_id=?", repoID).And("name=?", branchName).Get(&branch)
+ if err != nil {
+ return false, err
+ } else if !has {
+ return false, nil
+ }
+ return !branch.IsDeleted, nil
+}
+
func GetBranches(ctx context.Context, repoID int64, branchNames []string, includeDeleted bool) ([]*Branch, error) {
branches := make([]*Branch, 0, len(branchNames))
@@ -220,6 +235,11 @@ func GetDeletedBranchByID(ctx context.Context, repoID, branchID int64) (*Branch,
return &branch, nil
}
+func DeleteRepoBranches(ctx context.Context, repoID int64) error {
+ _, err := db.GetEngine(ctx).Where("repo_id=?", repoID).Delete(new(Branch))
+ return err
+}
+
func DeleteBranches(ctx context.Context, repoID, doerID int64, branchIDs []int64) error {
return db.WithTx(ctx, func(ctx context.Context) error {
branches := make([]*Branch, 0, len(branchIDs))
@@ -440,6 +460,8 @@ type FindRecentlyPushedNewBranchesOptions struct {
}
type RecentlyPushedNewBranch struct {
+ BranchRepo *repo_model.Repository
+ BranchName string
BranchDisplayName string
BranchLink string
BranchCompareURL string
@@ -465,7 +487,7 @@ func FindRecentlyPushedNewBranches(ctx context.Context, doer *user_model.User, o
ForkFrom: opts.BaseRepo.ID,
Archived: optional.Some(false),
}
- repoCond := repo_model.SearchRepositoryCondition(&repoOpts).And(repo_model.AccessibleRepositoryCondition(doer, unit.TypeCode))
+ repoCond := repo_model.SearchRepositoryCondition(repoOpts).And(repo_model.AccessibleRepositoryCondition(doer, unit.TypeCode))
if opts.Repo.ID == opts.BaseRepo.ID {
// should also include the base repo's branches
repoCond = repoCond.Or(builder.Eq{"id": opts.BaseRepo.ID})
@@ -540,7 +562,9 @@ func FindRecentlyPushedNewBranches(ctx context.Context, doer *user_model.User, o
branchDisplayName = fmt.Sprintf("%s:%s", branch.Repo.FullName(), branchDisplayName)
}
newBranches = append(newBranches, &RecentlyPushedNewBranch{
+ BranchRepo: branch.Repo,
BranchDisplayName: branchDisplayName,
+ BranchName: branch.Name,
BranchLink: fmt.Sprintf("%s/src/branch/%s", branch.Repo.Link(), util.PathEscapeSegments(branch.Name)),
BranchCompareURL: branch.Repo.ComposeBranchCompareURL(opts.BaseRepo, branch.Name),
CommitTime: branch.CommitTime,
diff --git a/models/git/branch_test.go b/models/git/branch_test.go
index b8ea663e81..252dcc5690 100644
--- a/models/git/branch_test.go
+++ b/models/git/branch_test.go
@@ -21,7 +21,7 @@ import (
func TestAddDeletedBranch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
- assert.EqualValues(t, git.Sha1ObjectFormat.Name(), repo.ObjectFormatName)
+ assert.Equal(t, git.Sha1ObjectFormat.Name(), repo.ObjectFormatName)
firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
assert.True(t, firstBranch.IsDeleted)
diff --git a/models/git/commit_status.go b/models/git/commit_status.go
index 0579a41209..f85e1b15e5 100644
--- a/models/git/commit_status.go
+++ b/models/git/commit_status.go
@@ -17,10 +17,10 @@ import (
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/commitstatus"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/translation"
@@ -30,17 +30,17 @@ import (
// CommitStatus holds a single Status of a single Commit
type CommitStatus struct {
- ID int64 `xorm:"pk autoincr"`
- Index int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
- RepoID int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
- Repo *repo_model.Repository `xorm:"-"`
- State api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
- SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_sha_index)"`
- TargetURL string `xorm:"TEXT"`
- Description string `xorm:"TEXT"`
- ContextHash string `xorm:"VARCHAR(64) index"`
- Context string `xorm:"TEXT"`
- Creator *user_model.User `xorm:"-"`
+ ID int64 `xorm:"pk autoincr"`
+ Index int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
+ RepoID int64 `xorm:"INDEX UNIQUE(repo_sha_index)"`
+ Repo *repo_model.Repository `xorm:"-"`
+ State commitstatus.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
+ SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_sha_index)"`
+ TargetURL string `xorm:"TEXT"`
+ Description string `xorm:"TEXT"`
+ ContextHash string `xorm:"VARCHAR(64) index"`
+ Context string `xorm:"TEXT"`
+ Creator *user_model.User `xorm:"-"`
CreatorID int64
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
@@ -222,7 +222,7 @@ func (status *CommitStatus) HideActionsURL(ctx context.Context) {
}
}
- prefix := fmt.Sprintf("%s/actions", status.Repo.Link())
+ prefix := status.Repo.Link() + "/actions"
if strings.HasPrefix(status.TargetURL, prefix) {
status.TargetURL = ""
}
@@ -230,22 +230,25 @@ func (status *CommitStatus) HideActionsURL(ctx context.Context) {
// CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus {
- var lastStatus *CommitStatus
- state := api.CommitStatusSuccess
+ if len(statuses) == 0 {
+ return nil
+ }
+
+ states := make(commitstatus.CommitStatusStates, 0, len(statuses))
+ targetURL := ""
for _, status := range statuses {
- if status.State.NoBetterThan(state) {
- state = status.State
- lastStatus = status
+ states = append(states, status.State)
+ if status.TargetURL != "" {
+ targetURL = status.TargetURL
}
}
- if lastStatus == nil {
- if len(statuses) > 0 {
- lastStatus = statuses[0]
- } else {
- lastStatus = &CommitStatus{}
- }
+
+ return &CommitStatus{
+ RepoID: statuses[0].RepoID,
+ SHA: statuses[0].SHA,
+ State: states.Combine(),
+ TargetURL: targetURL,
}
- return lastStatus
}
// CommitStatusOptions holds the options for query commit statuses
@@ -298,27 +301,37 @@ type CommitStatusIndex struct {
MaxIndex int64 `xorm:"index"`
}
+func makeRepoCommitQuery(ctx context.Context, repoID int64, sha string) *xorm.Session {
+ return db.GetEngine(ctx).Table(&CommitStatus{}).
+ Where("repo_id = ?", repoID).And("sha = ?", sha)
+}
+
// GetLatestCommitStatus returns all statuses with a unique context for a given commit.
-func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOptions db.ListOptions) ([]*CommitStatus, int64, error) {
- getBase := func() *xorm.Session {
- return db.GetEngine(ctx).Table(&CommitStatus{}).
- Where("repo_id = ?", repoID).And("sha = ?", sha)
- }
+func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOptions db.ListOptions) ([]*CommitStatus, error) {
indices := make([]int64, 0, 10)
- sess := getBase().Select("max( `index` ) as `index`").
- GroupBy("context_hash").OrderBy("max( `index` ) desc")
+ sess := makeRepoCommitQuery(ctx, repoID, sha).
+ Select("max( `index` ) as `index`").
+ GroupBy("context_hash").
+ OrderBy("max( `index` ) desc")
if !listOptions.IsListAll() {
sess = db.SetSessionPagination(sess, &listOptions)
}
- count, err := sess.FindAndCount(&indices)
- if err != nil {
- return nil, count, err
+ if err := sess.Find(&indices); err != nil {
+ return nil, err
}
statuses := make([]*CommitStatus, 0, len(indices))
if len(indices) == 0 {
- return statuses, count, nil
+ return statuses, nil
}
- return statuses, count, getBase().And(builder.In("`index`", indices)).Find(&statuses)
+ err := makeRepoCommitQuery(ctx, repoID, sha).And(builder.In("`index`", indices)).Find(&statuses)
+ return statuses, err
+}
+
+func CountLatestCommitStatus(ctx context.Context, repoID int64, sha string) (int64, error) {
+ return makeRepoCommitQuery(ctx, repoID, sha).
+ Select("count(context_hash)").
+ GroupBy("context_hash").
+ Count()
}
// GetLatestCommitStatusForPairs returns all statuses with a unique context for a given list of repo-sha pairs
@@ -453,9 +466,8 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
return fmt.Errorf("NewCommitStatus[nil, %s]: no repository specified", opts.SHA)
}
- repoPath := opts.Repo.RepoPath()
if opts.Creator == nil {
- return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", repoPath, opts.SHA)
+ return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", opts.Repo.FullName(), opts.SHA)
}
ctx, committer, err := db.TxContext(ctx)
@@ -477,13 +489,13 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
opts.CommitStatus.CreatorID = opts.Creator.ID
opts.CommitStatus.RepoID = opts.Repo.ID
opts.CommitStatus.Index = idx
- log.Debug("NewCommitStatus[%s, %s]: %d", repoPath, opts.SHA, opts.CommitStatus.Index)
+ log.Debug("NewCommitStatus[%s, %s]: %d", opts.Repo.FullName(), opts.SHA, opts.CommitStatus.Index)
opts.CommitStatus.ContextHash = hashCommitStatusContext(opts.CommitStatus.Context)
// Insert new CommitStatus
if _, err = db.GetEngine(ctx).Insert(opts.CommitStatus); err != nil {
- return fmt.Errorf("insert CommitStatus[%s, %s]: %w", repoPath, opts.SHA, err)
+ return fmt.Errorf("insert CommitStatus[%s, %s]: %w", opts.Repo.FullName(), opts.SHA, err)
}
return committer.Commit()
@@ -496,47 +508,11 @@ type SignCommitWithStatuses struct {
*asymkey_model.SignCommit
}
-// ParseCommitsWithStatus checks commits latest statuses and calculates its worst status state
-func ParseCommitsWithStatus(ctx context.Context, oldCommits []*asymkey_model.SignCommit, repo *repo_model.Repository) []*SignCommitWithStatuses {
- newCommits := make([]*SignCommitWithStatuses, 0, len(oldCommits))
-
- for _, c := range oldCommits {
- commit := &SignCommitWithStatuses{
- SignCommit: c,
- }
- statuses, _, err := GetLatestCommitStatus(ctx, repo.ID, commit.ID.String(), db.ListOptions{})
- if err != nil {
- log.Error("GetLatestCommitStatus: %v", err)
- } else {
- commit.Statuses = statuses
- commit.Status = CalcCommitStatus(statuses)
- }
-
- newCommits = append(newCommits, commit)
- }
- return newCommits
-}
-
// hashCommitStatusContext hash context
func hashCommitStatusContext(context string) string {
return fmt.Sprintf("%x", sha1.Sum([]byte(context)))
}
-// ConvertFromGitCommit converts git commits into SignCommitWithStatuses
-func ConvertFromGitCommit(ctx context.Context, commits []*git.Commit, repo *repo_model.Repository) []*SignCommitWithStatuses {
- return ParseCommitsWithStatus(ctx,
- asymkey_model.ParseCommitsWithSignature(
- ctx,
- user_model.ValidateCommitsWithEmails(ctx, commits),
- repo.GetTrustModel(),
- func(user *user_model.User) (bool, error) {
- return repo_model.IsOwnerMemberCollaborator(ctx, repo, user.ID)
- },
- ),
- repo,
- )
-}
-
// CommitStatusesHideActionsURL hide Gitea Actions urls
func CommitStatusesHideActionsURL(ctx context.Context, statuses []*CommitStatus) {
idToRepos := make(map[int64]*repo_model.Repository)
diff --git a/models/git/commit_status_summary.go b/models/git/commit_status_summary.go
index 7603e7aa65..dd416fa015 100644
--- a/models/git/commit_status_summary.go
+++ b/models/git/commit_status_summary.go
@@ -7,19 +7,19 @@ import (
"context"
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/commitstatus"
"code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
"xorm.io/builder"
)
// CommitStatusSummary holds the latest commit Status of a single Commit
type CommitStatusSummary struct {
- ID int64 `xorm:"pk autoincr"`
- RepoID int64 `xorm:"INDEX UNIQUE(repo_id_sha)"`
- SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"`
- State api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
- TargetURL string `xorm:"TEXT"`
+ ID int64 `xorm:"pk autoincr"`
+ RepoID int64 `xorm:"INDEX UNIQUE(repo_id_sha)"`
+ SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"`
+ State commitstatus.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
+ TargetURL string `xorm:"TEXT"`
}
func init() {
@@ -55,11 +55,15 @@ func GetLatestCommitStatusForRepoAndSHAs(ctx context.Context, repoSHAs []RepoSHA
}
func UpdateCommitStatusSummary(ctx context.Context, repoID int64, sha string) error {
- commitStatuses, _, err := GetLatestCommitStatus(ctx, repoID, sha, db.ListOptionsAll)
+ commitStatuses, err := GetLatestCommitStatus(ctx, repoID, sha, db.ListOptionsAll)
if err != nil {
return err
}
- state := CalcCommitStatus(commitStatuses)
+ // it guarantees that commitStatuses is not empty because this function is always called after a commit status is created
+ if len(commitStatuses) == 0 {
+ setting.PanicInDevOrTesting("no commit statuses found for repo %d and sha %s", repoID, sha)
+ }
+ state := CalcCommitStatus(commitStatuses) // non-empty commitStatuses is guaranteed
// mysql will return 0 when update a record which state hasn't been changed which behaviour is different from other database,
// so we need to use insert in on duplicate
if setting.Database.Type.IsMySQL() {
diff --git a/models/git/commit_status_test.go b/models/git/commit_status_test.go
index 37d785e938..4c0f5e891b 100644
--- a/models/git/commit_status_test.go
+++ b/models/git/commit_status_test.go
@@ -14,9 +14,9 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/commitstatus"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/structs"
"github.com/stretchr/testify/assert"
)
@@ -26,7 +26,7 @@ func TestGetCommitStatuses(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
- sha1 := "1234123412341234123412341234123412341234"
+ sha1 := "1234123412341234123412341234123412341234" // the mocked commit ID in test fixtures
statuses, maxResults, err := db.FindAndCount[git_model.CommitStatus](db.DefaultContext, &git_model.CommitStatusOptions{
ListOptions: db.ListOptions{Page: 1, PageSize: 50},
@@ -38,23 +38,23 @@ func TestGetCommitStatuses(t *testing.T) {
assert.Len(t, statuses, 5)
assert.Equal(t, "ci/awesomeness", statuses[0].Context)
- assert.Equal(t, structs.CommitStatusPending, statuses[0].State)
+ assert.Equal(t, commitstatus.CommitStatusPending, statuses[0].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[0].APIURL(db.DefaultContext))
assert.Equal(t, "cov/awesomeness", statuses[1].Context)
- assert.Equal(t, structs.CommitStatusWarning, statuses[1].State)
+ assert.Equal(t, commitstatus.CommitStatusWarning, statuses[1].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[1].APIURL(db.DefaultContext))
assert.Equal(t, "cov/awesomeness", statuses[2].Context)
- assert.Equal(t, structs.CommitStatusSuccess, statuses[2].State)
+ assert.Equal(t, commitstatus.CommitStatusSuccess, statuses[2].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[2].APIURL(db.DefaultContext))
assert.Equal(t, "ci/awesomeness", statuses[3].Context)
- assert.Equal(t, structs.CommitStatusFailure, statuses[3].State)
+ assert.Equal(t, commitstatus.CommitStatusFailure, statuses[3].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[3].APIURL(db.DefaultContext))
assert.Equal(t, "deploy/awesomeness", statuses[4].Context)
- assert.Equal(t, structs.CommitStatusError, statuses[4].State)
+ assert.Equal(t, commitstatus.CommitStatusError, statuses[4].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[4].APIURL(db.DefaultContext))
statuses, maxResults, err = db.FindAndCount[git_model.CommitStatus](db.DefaultContext, &git_model.CommitStatusOptions{
@@ -75,110 +75,110 @@ func Test_CalcCommitStatus(t *testing.T) {
{
statuses: []*git_model.CommitStatus{
{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
},
expected: &git_model.CommitStatus{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
},
expected: &git_model.CommitStatus{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: structs.CommitStatusError,
+ State: commitstatus.CommitStatusError,
},
{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
- State: structs.CommitStatusError,
+ State: commitstatus.CommitStatusFailure,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: structs.CommitStatusWarning,
+ State: commitstatus.CommitStatusWarning,
},
{
- State: structs.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
},
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
- State: structs.CommitStatusWarning,
+ State: commitstatus.CommitStatusPending,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
},
expected: &git_model.CommitStatus{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
},
},
{
statuses: []*git_model.CommitStatus{
{
- State: structs.CommitStatusFailure,
+ State: commitstatus.CommitStatusFailure,
},
{
- State: structs.CommitStatusError,
+ State: commitstatus.CommitStatusError,
},
{
- State: structs.CommitStatusWarning,
+ State: commitstatus.CommitStatusWarning,
},
},
expected: &git_model.CommitStatus{
- State: structs.CommitStatusError,
+ State: commitstatus.CommitStatusFailure,
},
},
}
for _, kase := range kases {
- assert.Equal(t, kase.expected, git_model.CalcCommitStatus(kase.statuses))
+ assert.Equal(t, kase.expected, git_model.CalcCommitStatus(kase.statuses), "statuses: %v", kase.statuses)
}
}
@@ -208,7 +208,7 @@ func TestFindRepoRecentCommitStatusContexts(t *testing.T) {
Creator: user2,
SHA: commit.ID,
CommitStatus: &git_model.CommitStatus{
- State: structs.CommitStatusFailure,
+ State: commitstatus.CommitStatusFailure,
TargetURL: "https://example.com/tests/",
Context: "compliance/lint-backend",
},
@@ -220,7 +220,7 @@ func TestFindRepoRecentCommitStatusContexts(t *testing.T) {
Creator: user2,
SHA: commit.ID,
CommitStatus: &git_model.CommitStatus{
- State: structs.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
TargetURL: "https://example.com/tests/",
Context: "compliance/lint-backend",
},
@@ -256,3 +256,26 @@ func TestCommitStatusesHideActionsURL(t *testing.T) {
assert.Empty(t, statuses[0].TargetURL)
assert.Equal(t, "https://mycicd.org/1", statuses[1].TargetURL)
}
+
+func TestGetCountLatestCommitStatus(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+
+ sha1 := "1234123412341234123412341234123412341234" // the mocked commit ID in test fixtures
+
+ commitStatuses, err := git_model.GetLatestCommitStatus(db.DefaultContext, repo1.ID, sha1, db.ListOptions{
+ Page: 1,
+ PageSize: 2,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, commitStatuses, 2)
+ assert.Equal(t, commitstatus.CommitStatusFailure, commitStatuses[0].State)
+ assert.Equal(t, "ci/awesomeness", commitStatuses[0].Context)
+ assert.Equal(t, commitstatus.CommitStatusError, commitStatuses[1].State)
+ assert.Equal(t, "deploy/awesomeness", commitStatuses[1].Context)
+
+ count, err := git_model.CountLatestCommitStatus(db.DefaultContext, repo1.ID, sha1)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 3, count)
+}
diff --git a/models/git/lfs.go b/models/git/lfs.go
index bb6361050a..e4fa2b446a 100644
--- a/models/git/lfs.go
+++ b/models/git/lfs.go
@@ -112,7 +112,6 @@ type LFSMetaObject struct {
ID int64 `xorm:"pk autoincr"`
lfs.Pointer `xorm:"extends"`
RepositoryID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
- Existing bool `xorm:"-"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
@@ -146,7 +145,6 @@ func NewLFSMetaObject(ctx context.Context, repoID int64, p lfs.Pointer) (*LFSMet
if err != nil {
return nil, err
} else if exist {
- m.Existing = true
return m, committer.Commit()
}
diff --git a/models/git/protected_branch.go b/models/git/protected_branch.go
index a3caed73c4..55bbe6938c 100644
--- a/models/git/protected_branch.go
+++ b/models/git/protected_branch.go
@@ -246,7 +246,7 @@ func (protectBranch *ProtectedBranch) GetUnprotectedFilePatterns() []glob.Glob {
func getFilePatterns(filePatterns string) []glob.Glob {
extarr := make([]glob.Glob, 0, 10)
- for _, expr := range strings.Split(strings.ToLower(filePatterns), ";") {
+ for expr := range strings.SplitSeq(strings.ToLower(filePatterns), ";") {
expr = strings.TrimSpace(expr)
if expr != "" {
if g, err := glob.Compile(expr, '.', '/'); err != nil {
@@ -518,7 +518,7 @@ func updateTeamWhitelist(ctx context.Context, repo *repo_model.Repository, curre
return currentWhitelist, nil
}
- teams, err := organization.GetTeamsWithAccessToRepo(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead)
+ teams, err := organization.GetTeamsWithAccessToAnyRepoUnit(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead, unit.TypeCode, unit.TypePullRequests)
if err != nil {
return nil, fmt.Errorf("GetTeamsWithAccessToRepo [org_id: %d, repo_id: %d]: %v", repo.OwnerID, repo.ID, err)
}
diff --git a/models/git/protected_branch_list_test.go b/models/git/protected_branch_list_test.go
index a46402c543..298c2fa074 100644
--- a/models/git/protected_branch_list_test.go
+++ b/models/git/protected_branch_list_test.go
@@ -70,7 +70,7 @@ func TestBranchRuleMatchPriority(t *testing.T) {
assert.Error(t, fmt.Errorf("no matched rules but expected %s[%d]", kase.Rules[kase.ExpectedMatchIdx], kase.ExpectedMatchIdx))
}
} else {
- assert.EqualValues(t, kase.Rules[kase.ExpectedMatchIdx], matchedPB.RuleName)
+ assert.Equal(t, kase.Rules[kase.ExpectedMatchIdx], matchedPB.RuleName)
}
}
}
diff --git a/models/git/protected_branch_test.go b/models/git/protected_branch_test.go
index e1c91d927d..367992081d 100644
--- a/models/git/protected_branch_test.go
+++ b/models/git/protected_branch_test.go
@@ -74,7 +74,7 @@ func TestBranchRuleMatch(t *testing.T) {
} else {
infact = " not"
}
- assert.EqualValues(t, kase.ExpectedMatch, pb.Match(kase.BranchName),
+ assert.Equal(t, kase.ExpectedMatch, pb.Match(kase.BranchName),
"%s should%s match %s but it is%s", kase.BranchName, should, kase.Rule, infact,
)
}
diff --git a/models/issues/comment.go b/models/issues/comment.go
index a7ec8f57fc..4fdb0c1808 100644
--- a/models/issues/comment.go
+++ b/models/issues/comment.go
@@ -9,6 +9,7 @@ import (
"context"
"fmt"
"html/template"
+ "slices"
"strconv"
"unicode/utf8"
@@ -19,8 +20,6 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/references"
@@ -112,8 +111,8 @@ const (
CommentTypePRScheduledToAutoMerge // 34 pr was scheduled to auto merge when checks succeed
CommentTypePRUnScheduledToAutoMerge // 35 pr was un scheduled to auto merge when checks succeed
- CommentTypePin // 36 pin Issue
- CommentTypeUnpin // 37 unpin Issue
+ CommentTypePin // 36 pin Issue/PullRequest
+ CommentTypeUnpin // 37 unpin Issue/PullRequest
CommentTypeChangeTimeEstimate // 38 Change time estimate
)
@@ -198,12 +197,7 @@ func (t CommentType) HasMailReplySupport() bool {
}
func (t CommentType) CountedAsConversation() bool {
- for _, ct := range ConversationCountedCommentType() {
- if t == ct {
- return true
- }
- }
- return false
+ return slices.Contains(ConversationCountedCommentType(), t)
}
// ConversationCountedCommentType returns the comment types that are counted as a conversation
@@ -616,7 +610,7 @@ func UpdateCommentAttachments(ctx context.Context, c *Comment, uuids []string) e
if err != nil {
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err)
}
- for i := 0; i < len(attachments); i++ {
+ for i := range attachments {
attachments[i].IssueID = c.IssueID
attachments[i].CommentID = c.ID
if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil {
@@ -721,7 +715,8 @@ func (c *Comment) LoadReactions(ctx context.Context, repo *repo_model.Repository
return nil
}
-func (c *Comment) loadReview(ctx context.Context) (err error) {
+// LoadReview loads the associated review
+func (c *Comment) LoadReview(ctx context.Context) (err error) {
if c.ReviewID == 0 {
return nil
}
@@ -738,11 +733,6 @@ func (c *Comment) loadReview(ctx context.Context) (err error) {
return nil
}
-// LoadReview loads the associated review
-func (c *Comment) LoadReview(ctx context.Context) error {
- return c.loadReview(ctx)
-}
-
// DiffSide returns "previous" if Comment.Line is a LOC of the previous changes and "proposed" if it is a LOC of the proposed changes.
func (c *Comment) DiffSide() string {
if c.Line < 0 {
@@ -774,41 +764,6 @@ func (c *Comment) CodeCommentLink(ctx context.Context) string {
return fmt.Sprintf("%s/files#%s", c.Issue.Link(), c.HashTag())
}
-// LoadPushCommits Load push commits
-func (c *Comment) LoadPushCommits(ctx context.Context) (err error) {
- if c.Content == "" || c.Commits != nil || c.Type != CommentTypePullRequestPush {
- return nil
- }
-
- var data PushActionContent
-
- err = json.Unmarshal([]byte(c.Content), &data)
- if err != nil {
- return err
- }
-
- c.IsForcePush = data.IsForcePush
-
- if c.IsForcePush {
- if len(data.CommitIDs) != 2 {
- return nil
- }
- c.OldCommit = data.CommitIDs[0]
- c.NewCommit = data.CommitIDs[1]
- } else {
- gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, c.Issue.Repo)
- if err != nil {
- return err
- }
- defer closer.Close()
-
- c.Commits = git_model.ConvertFromGitCommit(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo)
- c.CommitsNum = int64(len(c.Commits))
- }
-
- return err
-}
-
// CreateComment creates comment with context
func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment, err error) {
ctx, committer, err := db.TxContext(ctx)
@@ -897,7 +852,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
}
if comment.ReviewID != 0 {
if comment.Review == nil {
- if err := comment.loadReview(ctx); err != nil {
+ if err := comment.LoadReview(ctx); err != nil {
return err
}
}
diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go
index 67a77ceb13..55e67a1243 100644
--- a/models/issues/comment_code.go
+++ b/models/issues/comment_code.go
@@ -5,6 +5,7 @@ package issues
import (
"context"
+ "strconv"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/renderhelper"
@@ -86,8 +87,10 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
ids = append(ids, comment.ReviewID)
}
}
- if err := e.In("id", ids).Find(&reviews); err != nil {
- return nil, err
+ if len(ids) > 0 {
+ if err := e.In("id", ids).Find(&reviews); err != nil {
+ return nil, err
+ }
}
n := 0
@@ -112,7 +115,9 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
}
var err error
- rctx := renderhelper.NewRenderContextRepoComment(ctx, issue.Repo)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, issue.Repo, renderhelper.RepoCommentOptions{
+ FootnoteContextID: strconv.FormatInt(comment.ID, 10),
+ })
if comment.RenderedContent, err = markdown.RenderString(rctx, comment.Content); err != nil {
return nil, err
}
diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go
index 61ac1c8f56..f6c485449f 100644
--- a/models/issues/comment_list.go
+++ b/models/issues/comment_list.go
@@ -26,14 +26,14 @@ func (comments CommentList) LoadPosters(ctx context.Context) error {
return c.PosterID, c.Poster == nil && c.PosterID > 0
})
- posterMaps, err := getPostersByIDs(ctx, posterIDs)
+ posterMaps, err := user_model.GetUsersMapByIDs(ctx, posterIDs)
if err != nil {
return err
}
for _, comment := range comments {
if comment.Poster == nil {
- comment.Poster = getPoster(comment.PosterID, posterMaps)
+ comment.Poster = user_model.GetPossibleUserFromMap(comment.PosterID, posterMaps)
}
}
return nil
@@ -41,7 +41,7 @@ func (comments CommentList) LoadPosters(ctx context.Context) error {
func (comments CommentList) getLabelIDs() []int64 {
return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
- return comment.LabelID, comment.LabelID > 0
+ return comment.LabelID, comment.LabelID > 0 && comment.Label == nil
})
}
@@ -51,13 +51,13 @@ func (comments CommentList) loadLabels(ctx context.Context) error {
}
labelIDs := comments.getLabelIDs()
+ if len(labelIDs) == 0 {
+ return nil
+ }
commentLabels := make(map[int64]*Label, len(labelIDs))
left := len(labelIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("id", labelIDs[:limit]).
Rows(new(Label))
@@ -104,10 +104,7 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
err := db.GetEngine(ctx).
In("id", milestoneIDs[:limit]).
Find(&milestoneMaps)
@@ -118,8 +115,8 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
milestoneIDs = milestoneIDs[limit:]
}
- for _, issue := range comments {
- issue.Milestone = milestoneMaps[issue.MilestoneID]
+ for _, comment := range comments {
+ comment.Milestone = milestoneMaps[comment.MilestoneID]
}
return nil
}
@@ -143,10 +140,7 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error {
milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
err := db.GetEngine(ctx).
In("id", milestoneIDs[:limit]).
Find(&milestoneMaps)
@@ -175,13 +169,13 @@ func (comments CommentList) loadAssignees(ctx context.Context) error {
}
assigneeIDs := comments.getAssigneeIDs()
+ if len(assigneeIDs) == 0 {
+ return nil
+ }
assignees := make(map[int64]*user_model.User, len(assigneeIDs))
left := len(assigneeIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("id", assigneeIDs[:limit]).
Rows(new(user_model.User))
@@ -250,10 +244,7 @@ func (comments CommentList) LoadIssues(ctx context.Context) error {
issues := make(map[int64]*Issue, len(issueIDs))
left := len(issueIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("id", issueIDs[:limit]).
Rows(new(Issue))
@@ -301,13 +292,13 @@ func (comments CommentList) loadDependentIssues(ctx context.Context) error {
e := db.GetEngine(ctx)
issueIDs := comments.getDependentIssueIDs()
+ if len(issueIDs) == 0 {
+ return nil
+ }
issues := make(map[int64]*Issue, len(issueIDs))
left := len(issueIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := e.
In("id", issueIDs[:limit]).
Rows(new(Issue))
@@ -383,10 +374,7 @@ func (comments CommentList) LoadAttachments(ctx context.Context) (err error) {
commentsIDs := comments.getAttachmentCommentIDs()
left := len(commentsIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("comment_id", commentsIDs[:limit]).
Rows(new(repo_model.Attachment))
@@ -427,6 +415,9 @@ func (comments CommentList) loadReviews(ctx context.Context) error {
}
reviewIDs := comments.getReviewIDs()
+ if len(reviewIDs) == 0 {
+ return nil
+ }
reviews := make(map[int64]*Review, len(reviewIDs))
if err := db.GetEngine(ctx).In("id", reviewIDs).Find(&reviews); err != nil {
return err
diff --git a/models/issues/comment_test.go b/models/issues/comment_test.go
index ae0bc3ce17..c08e3b970d 100644
--- a/models/issues/comment_test.go
+++ b/models/issues/comment_test.go
@@ -34,10 +34,10 @@ func TestCreateComment(t *testing.T) {
assert.NoError(t, err)
then := time.Now().Unix()
- assert.EqualValues(t, issues_model.CommentTypeComment, comment.Type)
- assert.EqualValues(t, "Hello", comment.Content)
- assert.EqualValues(t, issue.ID, comment.IssueID)
- assert.EqualValues(t, doer.ID, comment.PosterID)
+ assert.Equal(t, issues_model.CommentTypeComment, comment.Type)
+ assert.Equal(t, "Hello", comment.Content)
+ assert.Equal(t, issue.ID, comment.IssueID)
+ assert.Equal(t, doer.ID, comment.PosterID)
unittest.AssertInt64InRange(t, now, then, int64(comment.CreatedUnix))
unittest.AssertExistsAndLoadBean(t, comment) // assert actually added to DB
@@ -58,9 +58,9 @@ func Test_UpdateCommentAttachment(t *testing.T) {
assert.NoError(t, err)
attachment2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: attachment.ID})
- assert.EqualValues(t, attachment.Name, attachment2.Name)
- assert.EqualValues(t, comment.ID, attachment2.CommentID)
- assert.EqualValues(t, comment.IssueID, attachment2.IssueID)
+ assert.Equal(t, attachment.Name, attachment2.Name)
+ assert.Equal(t, comment.ID, attachment2.CommentID)
+ assert.Equal(t, comment.IssueID, attachment2.IssueID)
}
func TestFetchCodeComments(t *testing.T) {
@@ -111,7 +111,7 @@ func TestMigrate_InsertIssueComments(t *testing.T) {
assert.NoError(t, err)
issueModified := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
- assert.EqualValues(t, issue.NumComments+1, issueModified.NumComments)
+ assert.Equal(t, issue.NumComments+1, issueModified.NumComments)
unittest.CheckConsistencyFor(t, &issues_model.Issue{})
}
@@ -122,5 +122,5 @@ func Test_UpdateIssueNumComments(t *testing.T) {
assert.NoError(t, issues_model.UpdateIssueNumComments(db.DefaultContext, issue2.ID))
issue2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
- assert.EqualValues(t, 1, issue2.NumComments)
+ assert.Equal(t, 1, issue2.NumComments)
}
diff --git a/models/issues/issue.go b/models/issues/issue.go
index fe347c2715..a86d50ca9d 100644
--- a/models/issues/issue.go
+++ b/models/issues/issue.go
@@ -10,6 +10,7 @@ import (
"html/template"
"regexp"
"slices"
+ "strconv"
"code.gitea.io/gitea/models/db"
project_model "code.gitea.io/gitea/models/project"
@@ -17,6 +18,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
@@ -46,23 +48,6 @@ func (err ErrIssueNotExist) Unwrap() error {
return util.ErrNotExist
}
-// ErrIssueIsClosed represents a "IssueIsClosed" kind of error.
-type ErrIssueIsClosed struct {
- ID int64
- RepoID int64
- Index int64
-}
-
-// IsErrIssueIsClosed checks if an error is a ErrIssueNotExist.
-func IsErrIssueIsClosed(err error) bool {
- _, ok := err.(ErrIssueIsClosed)
- return ok
-}
-
-func (err ErrIssueIsClosed) Error() string {
- return fmt.Sprintf("issue is closed [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index)
-}
-
// ErrNewIssueInsert is used when the INSERT statement in newIssue fails
type ErrNewIssueInsert struct {
OriginalError error
@@ -78,22 +63,6 @@ func (err ErrNewIssueInsert) Error() string {
return err.OriginalError.Error()
}
-// ErrIssueWasClosed is used when close a closed issue
-type ErrIssueWasClosed struct {
- ID int64
- Index int64
-}
-
-// IsErrIssueWasClosed checks if an error is a ErrIssueWasClosed.
-func IsErrIssueWasClosed(err error) bool {
- _, ok := err.(ErrIssueWasClosed)
- return ok
-}
-
-func (err ErrIssueWasClosed) Error() string {
- return fmt.Sprintf("Issue [%d] %d was already closed", err.ID, err.Index)
-}
-
var ErrIssueAlreadyChanged = util.NewInvalidArgumentErrorf("the issue is already changed")
// Issue represents an issue or pull request of repository.
@@ -129,7 +98,7 @@ type Issue struct {
// TODO: RemoveIssueRef: see "repo/issue/branch_selector_field.tmpl"
Ref string
- PinOrder int `xorm:"DEFAULT 0"`
+ PinOrder int `xorm:"-"` // 0 means not loaded, -1 means loaded but not pinned
DeadlineUnix timeutil.TimeStamp `xorm:"INDEX"`
@@ -271,6 +240,9 @@ func (issue *Issue) loadCommentsByType(ctx context.Context, tp CommentType) (err
IssueID: issue.ID,
Type: tp,
})
+ for _, comment := range issue.Comments {
+ comment.Issue = issue
+ }
return err
}
@@ -320,6 +292,23 @@ func (issue *Issue) LoadMilestone(ctx context.Context) (err error) {
return nil
}
+func (issue *Issue) LoadPinOrder(ctx context.Context) error {
+ if issue.PinOrder != 0 {
+ return nil
+ }
+ issuePin, err := GetIssuePin(ctx, issue)
+ if err != nil && !db.IsErrNotExist(err) {
+ return err
+ }
+
+ if issuePin != nil {
+ issue.PinOrder = issuePin.PinOrder
+ } else {
+ issue.PinOrder = -1
+ }
+ return nil
+}
+
// LoadAttributes loads the attribute of this issue.
func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
if err = issue.LoadRepo(ctx); err != nil {
@@ -359,6 +348,10 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
return err
}
+ if err = issue.LoadPinOrder(ctx); err != nil {
+ return err
+ }
+
if err = issue.Comments.LoadAttributes(ctx); err != nil {
return err
}
@@ -371,6 +364,14 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
return issue.loadReactions(ctx)
}
+// IsPinned returns if a Issue is pinned
+func (issue *Issue) IsPinned() bool {
+ if issue.PinOrder == 0 {
+ setting.PanicInDevOrTesting("issue's pinorder has not been loaded")
+ }
+ return issue.PinOrder > 0
+}
+
func (issue *Issue) ResetAttributesLoaded() {
issue.isLabelsLoaded = false
issue.isMilestoneLoaded = false
@@ -531,6 +532,45 @@ func GetIssueByIndex(ctx context.Context, repoID, index int64) (*Issue, error) {
return issue, nil
}
+func isPullToCond(isPull optional.Option[bool]) builder.Cond {
+ if isPull.Has() {
+ return builder.Eq{"is_pull": isPull.Value()}
+ }
+ return builder.NewCond()
+}
+
+func FindLatestUpdatedIssues(ctx context.Context, repoID int64, isPull optional.Option[bool], pageSize int) (IssueList, error) {
+ issues := make([]*Issue, 0, pageSize)
+ err := db.GetEngine(ctx).Where("repo_id = ?", repoID).
+ And(isPullToCond(isPull)).
+ OrderBy("updated_unix DESC").
+ Limit(pageSize).
+ Find(&issues)
+ return issues, err
+}
+
+func FindIssuesSuggestionByKeyword(ctx context.Context, repoID int64, keyword string, isPull optional.Option[bool], excludedID int64, pageSize int) (IssueList, error) {
+ cond := builder.NewCond()
+ if excludedID > 0 {
+ cond = cond.And(builder.Neq{"`id`": excludedID})
+ }
+
+ // It seems that GitHub searches both title and content (maybe sorting by the search engine's ranking system?)
+ // The first PR (https://github.com/go-gitea/gitea/pull/32327) uses "search indexer" to search "name(title) + content"
+ // But it seems that searching "content" (especially LIKE by DB engine) generates worse (unusable) results.
+ // So now (https://github.com/go-gitea/gitea/pull/33538) it only searches "name(title)", leave the improvements to the future.
+ cond = cond.And(db.BuildCaseInsensitiveLike("`name`", keyword))
+
+ issues := make([]*Issue, 0, pageSize)
+ err := db.GetEngine(ctx).Where("repo_id = ?", repoID).
+ And(isPullToCond(isPull)).
+ And(cond).
+ OrderBy("updated_unix DESC, `index` DESC").
+ Limit(pageSize).
+ Find(&issues)
+ return issues, err
+}
+
// GetIssueWithAttrsByIndex returns issue by index in a repository.
func GetIssueWithAttrsByIndex(ctx context.Context, repoID, index int64) (*Issue, error) {
issue, err := GetIssueByIndex(ctx, repoID, index)
@@ -556,6 +596,9 @@ func GetIssueByID(ctx context.Context, id int64) (*Issue, error) {
// If keepOrder is true, the order of the returned issues will be the same as the given IDs.
func GetIssuesByIDs(ctx context.Context, issueIDs []int64, keepOrder ...bool) (IssueList, error) {
issues := make([]*Issue, 0, len(issueIDs))
+ if len(issueIDs) == 0 {
+ return issues, nil
+ }
if err := db.GetEngine(ctx).In("id", issueIDs).Find(&issues); err != nil {
return nil, err
@@ -710,190 +753,6 @@ func (issue *Issue) HasOriginalAuthor() bool {
return issue.OriginalAuthor != "" && issue.OriginalAuthorID != 0
}
-var ErrIssueMaxPinReached = util.NewInvalidArgumentErrorf("the max number of pinned issues has been readched")
-
-// IsPinned returns if a Issue is pinned
-func (issue *Issue) IsPinned() bool {
- return issue.PinOrder != 0
-}
-
-// Pin pins a Issue
-func (issue *Issue) Pin(ctx context.Context, user *user_model.User) error {
- // If the Issue is already pinned, we don't need to pin it twice
- if issue.IsPinned() {
- return nil
- }
-
- var maxPin int
- _, err := db.GetEngine(ctx).SQL("SELECT MAX(pin_order) FROM issue WHERE repo_id = ? AND is_pull = ?", issue.RepoID, issue.IsPull).Get(&maxPin)
- if err != nil {
- return err
- }
-
- // Check if the maximum allowed Pins reached
- if maxPin >= setting.Repository.Issue.MaxPinned {
- return ErrIssueMaxPinReached
- }
-
- _, err = db.GetEngine(ctx).Table("issue").
- Where("id = ?", issue.ID).
- Update(map[string]any{
- "pin_order": maxPin + 1,
- })
- if err != nil {
- return err
- }
-
- // Add the pin event to the history
- opts := &CreateCommentOptions{
- Type: CommentTypePin,
- Doer: user,
- Repo: issue.Repo,
- Issue: issue,
- }
- if _, err = CreateComment(ctx, opts); err != nil {
- return err
- }
-
- return nil
-}
-
-// UnpinIssue unpins a Issue
-func (issue *Issue) Unpin(ctx context.Context, user *user_model.User) error {
- // If the Issue is not pinned, we don't need to unpin it
- if !issue.IsPinned() {
- return nil
- }
-
- // This sets the Pin for all Issues that come after the unpined Issue to the correct value
- _, err := db.GetEngine(ctx).Exec("UPDATE issue SET pin_order = pin_order - 1 WHERE repo_id = ? AND is_pull = ? AND pin_order > ?", issue.RepoID, issue.IsPull, issue.PinOrder)
- if err != nil {
- return err
- }
-
- _, err = db.GetEngine(ctx).Table("issue").
- Where("id = ?", issue.ID).
- Update(map[string]any{
- "pin_order": 0,
- })
- if err != nil {
- return err
- }
-
- // Add the unpin event to the history
- opts := &CreateCommentOptions{
- Type: CommentTypeUnpin,
- Doer: user,
- Repo: issue.Repo,
- Issue: issue,
- }
- if _, err = CreateComment(ctx, opts); err != nil {
- return err
- }
-
- return nil
-}
-
-// PinOrUnpin pins or unpins a Issue
-func (issue *Issue) PinOrUnpin(ctx context.Context, user *user_model.User) error {
- if !issue.IsPinned() {
- return issue.Pin(ctx, user)
- }
-
- return issue.Unpin(ctx, user)
-}
-
-// MovePin moves a Pinned Issue to a new Position
-func (issue *Issue) MovePin(ctx context.Context, newPosition int) error {
- // If the Issue is not pinned, we can't move them
- if !issue.IsPinned() {
- return nil
- }
-
- if newPosition < 1 {
- return fmt.Errorf("The Position can't be lower than 1")
- }
-
- dbctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
-
- var maxPin int
- _, err = db.GetEngine(dbctx).SQL("SELECT MAX(pin_order) FROM issue WHERE repo_id = ? AND is_pull = ?", issue.RepoID, issue.IsPull).Get(&maxPin)
- if err != nil {
- return err
- }
-
- // If the new Position bigger than the current Maximum, set it to the Maximum
- if newPosition > maxPin+1 {
- newPosition = maxPin + 1
- }
-
- // Lower the Position of all Pinned Issue that came after the current Position
- _, err = db.GetEngine(dbctx).Exec("UPDATE issue SET pin_order = pin_order - 1 WHERE repo_id = ? AND is_pull = ? AND pin_order > ?", issue.RepoID, issue.IsPull, issue.PinOrder)
- if err != nil {
- return err
- }
-
- // Higher the Position of all Pinned Issues that comes after the new Position
- _, err = db.GetEngine(dbctx).Exec("UPDATE issue SET pin_order = pin_order + 1 WHERE repo_id = ? AND is_pull = ? AND pin_order >= ?", issue.RepoID, issue.IsPull, newPosition)
- if err != nil {
- return err
- }
-
- _, err = db.GetEngine(dbctx).Table("issue").
- Where("id = ?", issue.ID).
- Update(map[string]any{
- "pin_order": newPosition,
- })
- if err != nil {
- return err
- }
-
- return committer.Commit()
-}
-
-// GetPinnedIssues returns the pinned Issues for the given Repo and type
-func GetPinnedIssues(ctx context.Context, repoID int64, isPull bool) (IssueList, error) {
- issues := make(IssueList, 0)
-
- err := db.GetEngine(ctx).
- Table("issue").
- Where("repo_id = ?", repoID).
- And("is_pull = ?", isPull).
- And("pin_order > 0").
- OrderBy("pin_order").
- Find(&issues)
- if err != nil {
- return nil, err
- }
-
- err = issues.LoadAttributes(ctx)
- if err != nil {
- return nil, err
- }
-
- return issues, nil
-}
-
-// IsNewPinAllowed returns if a new Issue or Pull request can be pinned
-func IsNewPinAllowed(ctx context.Context, repoID int64, isPull bool) (bool, error) {
- var maxPin int
- _, err := db.GetEngine(ctx).SQL("SELECT COUNT(pin_order) FROM issue WHERE repo_id = ? AND is_pull = ? AND pin_order > 0", repoID, isPull).Get(&maxPin)
- if err != nil {
- return false, err
- }
-
- return maxPin < setting.Repository.Issue.MaxPinned, nil
-}
-
-// IsErrIssueMaxPinReached returns if the error is, that the User can't pin more Issues
-func IsErrIssueMaxPinReached(err error) bool {
- return err == ErrIssueMaxPinReached
-}
-
// InsertIssues insert issues to database
func InsertIssues(ctx context.Context, issues ...*Issue) error {
ctx, committer, err := db.TxContext(ctx)
@@ -957,7 +816,7 @@ func ChangeIssueTimeEstimate(ctx context.Context, issue *Issue, doer *user_model
Doer: doer,
Repo: issue.Repo,
Issue: issue,
- Content: fmt.Sprintf("%d", timeEstimate),
+ Content: strconv.FormatInt(timeEstimate, 10),
}
if _, err := CreateComment(ctx, opts); err != nil {
return fmt.Errorf("createComment: %w", err)
diff --git a/models/issues/issue_label.go b/models/issues/issue_label.go
index 10fc821454..f082079e07 100644
--- a/models/issues/issue_label.go
+++ b/models/issues/issue_label.go
@@ -206,6 +206,7 @@ func DeleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *use
}
issue.Labels = nil
+ issue.isLabelsLoaded = false
return issue.LoadLabels(ctx)
}
diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go
index 22a4548adc..26b93189b8 100644
--- a/models/issues/issue_list.go
+++ b/models/issues/issue_list.go
@@ -42,10 +42,7 @@ func (issues IssueList) LoadRepositories(ctx context.Context) (repo_model.Reposi
repoMaps := make(map[int64]*repo_model.Repository, len(repoIDs))
left := len(repoIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
err := db.GetEngine(ctx).
In("id", repoIDs[:limit]).
Find(&repoMaps)
@@ -81,53 +78,19 @@ func (issues IssueList) LoadPosters(ctx context.Context) error {
return issue.PosterID, issue.Poster == nil && issue.PosterID > 0
})
- posterMaps, err := getPostersByIDs(ctx, posterIDs)
+ posterMaps, err := user_model.GetUsersMapByIDs(ctx, posterIDs)
if err != nil {
return err
}
for _, issue := range issues {
if issue.Poster == nil {
- issue.Poster = getPoster(issue.PosterID, posterMaps)
+ issue.Poster = user_model.GetPossibleUserFromMap(issue.PosterID, posterMaps)
}
}
return nil
}
-func getPostersByIDs(ctx context.Context, posterIDs []int64) (map[int64]*user_model.User, error) {
- posterMaps := make(map[int64]*user_model.User, len(posterIDs))
- left := len(posterIDs)
- for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
- err := db.GetEngine(ctx).
- In("id", posterIDs[:limit]).
- Find(&posterMaps)
- if err != nil {
- return nil, err
- }
- left -= limit
- posterIDs = posterIDs[limit:]
- }
- return posterMaps, nil
-}
-
-func getPoster(posterID int64, posterMaps map[int64]*user_model.User) *user_model.User {
- if posterID == user_model.ActionsUserID {
- return user_model.NewActionsUser()
- }
- if posterID <= 0 {
- return nil
- }
- poster, ok := posterMaps[posterID]
- if !ok {
- return user_model.NewGhostUser()
- }
- return poster
-}
-
func (issues IssueList) getIssueIDs() []int64 {
ids := make([]int64, 0, len(issues))
for _, issue := range issues {
@@ -150,10 +113,7 @@ func (issues IssueList) LoadLabels(ctx context.Context) error {
issueIDs := issues.getIssueIDs()
left := len(issueIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).Table("label").
Join("LEFT", "issue_label", "issue_label.label_id = label.id").
In("issue_label.issue_id", issueIDs[:limit]).
@@ -205,10 +165,7 @@ func (issues IssueList) LoadMilestones(ctx context.Context) error {
milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
err := db.GetEngine(ctx).
In("id", milestoneIDs[:limit]).
Find(&milestoneMaps)
@@ -237,10 +194,7 @@ func (issues IssueList) LoadProjects(ctx context.Context) error {
}
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
projects := make([]*projectWithIssueID, 0, limit)
err := db.GetEngine(ctx).
@@ -279,10 +233,7 @@ func (issues IssueList) LoadAssignees(ctx context.Context) error {
issueIDs := issues.getIssueIDs()
left := len(issueIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).Table("issue_assignees").
Join("INNER", "`user`", "`user`.id = `issue_assignees`.assignee_id").
In("`issue_assignees`.issue_id", issueIDs[:limit]).OrderBy(user_model.GetOrderByName()).
@@ -340,10 +291,7 @@ func (issues IssueList) LoadPullRequests(ctx context.Context) error {
pullRequestMaps := make(map[int64]*PullRequest, len(issuesIDs))
left := len(issuesIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("issue_id", issuesIDs[:limit]).
Rows(new(PullRequest))
@@ -388,10 +336,7 @@ func (issues IssueList) LoadAttachments(ctx context.Context) (err error) {
issuesIDs := issues.getIssueIDs()
left := len(issuesIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).
In("issue_id", issuesIDs[:limit]).
Rows(new(repo_model.Attachment))
@@ -433,10 +378,7 @@ func (issues IssueList) loadComments(ctx context.Context, cond builder.Cond) (er
issuesIDs := issues.getIssueIDs()
left := len(issuesIDs)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
rows, err := db.GetEngine(ctx).Table("comment").
Join("INNER", "issue", "issue.id = comment.issue_id").
In("issue.id", issuesIDs[:limit]).
@@ -500,10 +442,7 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) {
left := len(ids)
for left > 0 {
- limit := db.DefaultMaxInSize
- if left < limit {
- limit = left
- }
+ limit := min(left, db.DefaultMaxInSize)
// select issue_id, sum(time) from tracked_time where issue_id in (<issue ids in current page>) group by issue_id
rows, err := db.GetEngine(ctx).Table("tracked_time").
@@ -540,6 +479,39 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) {
return nil
}
+func (issues IssueList) LoadPinOrder(ctx context.Context) error {
+ if len(issues) == 0 {
+ return nil
+ }
+
+ issueIDs := container.FilterSlice(issues, func(issue *Issue) (int64, bool) {
+ return issue.ID, issue.PinOrder == 0
+ })
+ if len(issueIDs) == 0 {
+ return nil
+ }
+ issuePins, err := GetIssuePinsByIssueIDs(ctx, issueIDs)
+ if err != nil {
+ return err
+ }
+
+ for _, issue := range issues {
+ if issue.PinOrder != 0 {
+ continue
+ }
+ for _, pin := range issuePins {
+ if pin.IssueID == issue.ID {
+ issue.PinOrder = pin.PinOrder
+ break
+ }
+ }
+ if issue.PinOrder == 0 {
+ issue.PinOrder = -1
+ }
+ }
+ return nil
+}
+
// loadAttributes loads all attributes, expect for attachments and comments
func (issues IssueList) LoadAttributes(ctx context.Context) error {
if _, err := issues.LoadRepositories(ctx); err != nil {
diff --git a/models/issues/issue_list_test.go b/models/issues/issue_list_test.go
index 9069e1012d..5b4d2ca5ab 100644
--- a/models/issues/issue_list_test.go
+++ b/models/issues/issue_list_test.go
@@ -27,7 +27,7 @@ func TestIssueList_LoadRepositories(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, repos, 2)
for _, issue := range issueList {
- assert.EqualValues(t, issue.RepoID, issue.Repo.ID)
+ assert.Equal(t, issue.RepoID, issue.Repo.ID)
}
}
@@ -41,28 +41,28 @@ func TestIssueList_LoadAttributes(t *testing.T) {
assert.NoError(t, issueList.LoadAttributes(db.DefaultContext))
for _, issue := range issueList {
- assert.EqualValues(t, issue.RepoID, issue.Repo.ID)
+ assert.Equal(t, issue.RepoID, issue.Repo.ID)
for _, label := range issue.Labels {
- assert.EqualValues(t, issue.RepoID, label.RepoID)
+ assert.Equal(t, issue.RepoID, label.RepoID)
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID})
}
if issue.PosterID > 0 {
- assert.EqualValues(t, issue.PosterID, issue.Poster.ID)
+ assert.Equal(t, issue.PosterID, issue.Poster.ID)
}
if issue.AssigneeID > 0 {
- assert.EqualValues(t, issue.AssigneeID, issue.Assignee.ID)
+ assert.Equal(t, issue.AssigneeID, issue.Assignee.ID)
}
if issue.MilestoneID > 0 {
- assert.EqualValues(t, issue.MilestoneID, issue.Milestone.ID)
+ assert.Equal(t, issue.MilestoneID, issue.Milestone.ID)
}
if issue.IsPull {
- assert.EqualValues(t, issue.ID, issue.PullRequest.IssueID)
+ assert.Equal(t, issue.ID, issue.PullRequest.IssueID)
}
for _, attachment := range issue.Attachments {
- assert.EqualValues(t, issue.ID, attachment.IssueID)
+ assert.Equal(t, issue.ID, attachment.IssueID)
}
for _, comment := range issue.Comments {
- assert.EqualValues(t, issue.ID, comment.IssueID)
+ assert.Equal(t, issue.ID, comment.IssueID)
}
if issue.ID == int64(1) {
assert.Equal(t, int64(400), issue.TotalTrackedTime)
diff --git a/models/issues/issue_lock.go b/models/issues/issue_lock.go
index b21629b529..fa0d128f74 100644
--- a/models/issues/issue_lock.go
+++ b/models/issues/issue_lock.go
@@ -12,8 +12,14 @@ import (
// IssueLockOptions defines options for locking and/or unlocking an issue/PR
type IssueLockOptions struct {
- Doer *user_model.User
- Issue *Issue
+ Doer *user_model.User
+ Issue *Issue
+
+ // Reason is the doer-provided comment message for the locked issue
+ // GitHub doesn't support changing the "reasons" by config file, so GitHub has pre-defined "reason" enum values.
+ // Gitea is not like GitHub, it allows site admin to define customized "reasons" in the config file.
+ // So the API caller might not know what kind of "reasons" are valid, and the customized reasons are not translatable.
+ // To make things clear and simple: doer have the chance to use any reason they like, we do not do validation.
Reason string
}
diff --git a/models/issues/issue_pin.go b/models/issues/issue_pin.go
new file mode 100644
index 0000000000..ae6195b05d
--- /dev/null
+++ b/models/issues/issue_pin.go
@@ -0,0 +1,246 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package issues
+
+import (
+ "context"
+ "errors"
+ "sort"
+
+ "code.gitea.io/gitea/models/db"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+)
+
+type IssuePin struct {
+ ID int64 `xorm:"pk autoincr"`
+ RepoID int64 `xorm:"UNIQUE(s) NOT NULL"`
+ IssueID int64 `xorm:"UNIQUE(s) NOT NULL"`
+ IsPull bool `xorm:"NOT NULL"`
+ PinOrder int `xorm:"DEFAULT 0"`
+}
+
+var ErrIssueMaxPinReached = util.NewInvalidArgumentErrorf("the max number of pinned issues has been readched")
+
+// IsErrIssueMaxPinReached returns if the error is, that the User can't pin more Issues
+func IsErrIssueMaxPinReached(err error) bool {
+ return err == ErrIssueMaxPinReached
+}
+
+func init() {
+ db.RegisterModel(new(IssuePin))
+}
+
+func GetIssuePin(ctx context.Context, issue *Issue) (*IssuePin, error) {
+ pin := new(IssuePin)
+ has, err := db.GetEngine(ctx).
+ Where("repo_id = ?", issue.RepoID).
+ And("issue_id = ?", issue.ID).Get(pin)
+ if err != nil {
+ return nil, err
+ } else if !has {
+ return nil, db.ErrNotExist{
+ Resource: "IssuePin",
+ ID: issue.ID,
+ }
+ }
+ return pin, nil
+}
+
+func GetIssuePinsByIssueIDs(ctx context.Context, issueIDs []int64) ([]IssuePin, error) {
+ var pins []IssuePin
+ if err := db.GetEngine(ctx).In("issue_id", issueIDs).Find(&pins); err != nil {
+ return nil, err
+ }
+ return pins, nil
+}
+
+// Pin pins a Issue
+func PinIssue(ctx context.Context, issue *Issue, user *user_model.User) error {
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ pinnedIssuesNum, err := getPinnedIssuesNum(ctx, issue.RepoID, issue.IsPull)
+ if err != nil {
+ return err
+ }
+
+ // Check if the maximum allowed Pins reached
+ if pinnedIssuesNum >= setting.Repository.Issue.MaxPinned {
+ return ErrIssueMaxPinReached
+ }
+
+ pinnedIssuesMaxPinOrder, err := getPinnedIssuesMaxPinOrder(ctx, issue.RepoID, issue.IsPull)
+ if err != nil {
+ return err
+ }
+
+ if _, err = db.GetEngine(ctx).Insert(&IssuePin{
+ RepoID: issue.RepoID,
+ IssueID: issue.ID,
+ IsPull: issue.IsPull,
+ PinOrder: pinnedIssuesMaxPinOrder + 1,
+ }); err != nil {
+ return err
+ }
+
+ // Add the pin event to the history
+ _, err = CreateComment(ctx, &CreateCommentOptions{
+ Type: CommentTypePin,
+ Doer: user,
+ Repo: issue.Repo,
+ Issue: issue,
+ })
+ return err
+ })
+}
+
+// UnpinIssue unpins a Issue
+func UnpinIssue(ctx context.Context, issue *Issue, user *user_model.User) error {
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ // This sets the Pin for all Issues that come after the unpined Issue to the correct value
+ cnt, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Delete(new(IssuePin))
+ if err != nil {
+ return err
+ }
+ if cnt == 0 {
+ return nil
+ }
+
+ // Add the unpin event to the history
+ _, err = CreateComment(ctx, &CreateCommentOptions{
+ Type: CommentTypeUnpin,
+ Doer: user,
+ Repo: issue.Repo,
+ Issue: issue,
+ })
+ return err
+ })
+}
+
+func getPinnedIssuesNum(ctx context.Context, repoID int64, isPull bool) (int, error) {
+ var pinnedIssuesNum int
+ _, err := db.GetEngine(ctx).SQL("SELECT count(pin_order) FROM issue_pin WHERE repo_id = ? AND is_pull = ?", repoID, isPull).Get(&pinnedIssuesNum)
+ return pinnedIssuesNum, err
+}
+
+func getPinnedIssuesMaxPinOrder(ctx context.Context, repoID int64, isPull bool) (int, error) {
+ var maxPinnedIssuesMaxPinOrder int
+ _, err := db.GetEngine(ctx).SQL("SELECT max(pin_order) FROM issue_pin WHERE repo_id = ? AND is_pull = ?", repoID, isPull).Get(&maxPinnedIssuesMaxPinOrder)
+ return maxPinnedIssuesMaxPinOrder, err
+}
+
+// MovePin moves a Pinned Issue to a new Position
+func MovePin(ctx context.Context, issue *Issue, newPosition int) error {
+ if newPosition < 1 {
+ return errors.New("The Position can't be lower than 1")
+ }
+
+ issuePin, err := GetIssuePin(ctx, issue)
+ if err != nil {
+ return err
+ }
+ if issuePin.PinOrder == newPosition {
+ return nil
+ }
+
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ if issuePin.PinOrder > newPosition { // move the issue to a lower position
+ _, err = db.GetEngine(ctx).Exec("UPDATE issue_pin SET pin_order = pin_order + 1 WHERE repo_id = ? AND is_pull = ? AND pin_order >= ? AND pin_order < ?", issue.RepoID, issue.IsPull, newPosition, issuePin.PinOrder)
+ } else { // move the issue to a higher position
+ // Lower the Position of all Pinned Issue that came after the current Position
+ _, err = db.GetEngine(ctx).Exec("UPDATE issue_pin SET pin_order = pin_order - 1 WHERE repo_id = ? AND is_pull = ? AND pin_order > ? AND pin_order <= ?", issue.RepoID, issue.IsPull, issuePin.PinOrder, newPosition)
+ }
+ if err != nil {
+ return err
+ }
+
+ _, err = db.GetEngine(ctx).
+ Table("issue_pin").
+ Where("id = ?", issuePin.ID).
+ Update(map[string]any{
+ "pin_order": newPosition,
+ })
+ return err
+ })
+}
+
+func GetPinnedIssueIDs(ctx context.Context, repoID int64, isPull bool) ([]int64, error) {
+ var issuePins []IssuePin
+ if err := db.GetEngine(ctx).
+ Table("issue_pin").
+ Where("repo_id = ?", repoID).
+ And("is_pull = ?", isPull).
+ Find(&issuePins); err != nil {
+ return nil, err
+ }
+
+ sort.Slice(issuePins, func(i, j int) bool {
+ return issuePins[i].PinOrder < issuePins[j].PinOrder
+ })
+
+ var ids []int64
+ for _, pin := range issuePins {
+ ids = append(ids, pin.IssueID)
+ }
+ return ids, nil
+}
+
+func GetIssuePinsByRepoID(ctx context.Context, repoID int64, isPull bool) ([]*IssuePin, error) {
+ var pins []*IssuePin
+ if err := db.GetEngine(ctx).Where("repo_id = ? AND is_pull = ?", repoID, isPull).Find(&pins); err != nil {
+ return nil, err
+ }
+ return pins, nil
+}
+
+// GetPinnedIssues returns the pinned Issues for the given Repo and type
+func GetPinnedIssues(ctx context.Context, repoID int64, isPull bool) (IssueList, error) {
+ issuePins, err := GetIssuePinsByRepoID(ctx, repoID, isPull)
+ if err != nil {
+ return nil, err
+ }
+ if len(issuePins) == 0 {
+ return IssueList{}, nil
+ }
+ ids := make([]int64, 0, len(issuePins))
+ for _, pin := range issuePins {
+ ids = append(ids, pin.IssueID)
+ }
+
+ issues := make(IssueList, 0, len(ids))
+ if err := db.GetEngine(ctx).In("id", ids).Find(&issues); err != nil {
+ return nil, err
+ }
+ for _, issue := range issues {
+ for _, pin := range issuePins {
+ if pin.IssueID == issue.ID {
+ issue.PinOrder = pin.PinOrder
+ break
+ }
+ }
+ if (!setting.IsProd || setting.IsInTesting) && issue.PinOrder == 0 {
+ panic("It should not happen that a pinned Issue has no PinOrder")
+ }
+ }
+ sort.Slice(issues, func(i, j int) bool {
+ return issues[i].PinOrder < issues[j].PinOrder
+ })
+
+ if err = issues.LoadAttributes(ctx); err != nil {
+ return nil, err
+ }
+
+ return issues, nil
+}
+
+// IsNewPinAllowed returns if a new Issue or Pull request can be pinned
+func IsNewPinAllowed(ctx context.Context, repoID int64, isPull bool) (bool, error) {
+ var maxPin int
+ _, err := db.GetEngine(ctx).SQL("SELECT COUNT(pin_order) FROM issue_pin WHERE repo_id = ? AND is_pull = ?", repoID, isPull).Get(&maxPin)
+ if err != nil {
+ return false, err
+ }
+
+ return maxPin < setting.Repository.Issue.MaxPinned, nil
+}
diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go
index c4515fd898..0185244783 100644
--- a/models/issues/issue_project.go
+++ b/models/issues/issue_project.go
@@ -38,13 +38,30 @@ func (issue *Issue) projectID(ctx context.Context) int64 {
}
// ProjectColumnID return project column id if issue was assigned to one
-func (issue *Issue) ProjectColumnID(ctx context.Context) int64 {
+func (issue *Issue) ProjectColumnID(ctx context.Context) (int64, error) {
var ip project_model.ProjectIssue
has, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Get(&ip)
- if err != nil || !has {
- return 0
+ if err != nil {
+ return 0, err
+ } else if !has {
+ return 0, nil
+ }
+ return ip.ProjectColumnID, nil
+}
+
+func LoadProjectIssueColumnMap(ctx context.Context, projectID, defaultColumnID int64) (map[int64]int64, error) {
+ issues := make([]project_model.ProjectIssue, 0)
+ if err := db.GetEngine(ctx).Where("project_id=?", projectID).Find(&issues); err != nil {
+ return nil, err
+ }
+ result := make(map[int64]int64, len(issues))
+ for _, issue := range issues {
+ if issue.ProjectColumnID == 0 {
+ issue.ProjectColumnID = defaultColumnID
+ }
+ result[issue.IssueID] = issue.ProjectColumnID
}
- return ip.ProjectColumnID
+ return result, nil
}
// LoadIssuesFromColumn load issues assigned to this column
@@ -59,11 +76,11 @@ func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column, opts *Is
}
if b.Default {
- issues, err := Issues(ctx, &IssuesOptions{
- ProjectColumnID: db.NoConditionID,
- ProjectID: b.ProjectID,
- SortType: "project-column-sorting",
- })
+ issues, err := Issues(ctx, opts.Copy(func(o *IssuesOptions) {
+ o.ProjectColumnID = db.NoConditionID
+ o.ProjectID = b.ProjectID
+ o.SortType = "project-column-sorting"
+ }))
if err != nil {
return nil, err
}
@@ -77,19 +94,6 @@ func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column, opts *Is
return issueList, nil
}
-// LoadIssuesFromColumnList load issues assigned to the columns
-func LoadIssuesFromColumnList(ctx context.Context, bs project_model.ColumnList, opts *IssuesOptions) (map[int64]IssueList, error) {
- issuesMap := make(map[int64]IssueList, len(bs))
- for i := range bs {
- il, err := LoadIssuesFromColumn(ctx, bs[i], opts)
- if err != nil {
- return nil, err
- }
- issuesMap[bs[i].ID] = il
- }
- return issuesMap, nil
-}
-
// IssueAssignOrRemoveProject changes the project associated with an issue
// If newProjectID is 0, the issue is removed from the project
func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID, newColumnID int64) error {
@@ -110,7 +114,7 @@ func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_mo
return util.NewPermissionDeniedErrorf("issue %d can't be accessed by project %d", issue.ID, newProject.ID)
}
if newColumnID == 0 {
- newDefaultColumn, err := newProject.GetDefaultColumn(ctx)
+ newDefaultColumn, err := newProject.MustDefaultColumn(ctx)
if err != nil {
return err
}
diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go
index f1cd125d49..79bd6a19b0 100644
--- a/models/issues/issue_search.go
+++ b/models/issues/issue_search.go
@@ -21,14 +21,16 @@ import (
"xorm.io/xorm"
)
+const ScopeSortPrefix = "scope-"
+
// IssuesOptions represents options of an issue.
-type IssuesOptions struct { //nolint
+type IssuesOptions struct { //nolint:revive // export stutter
Paginator *db.ListOptions
RepoIDs []int64 // overwrites RepoCond if the length is not 0
AllPublic bool // include also all public repositories
RepoCond builder.Cond
- AssigneeID optional.Option[int64]
- PosterID optional.Option[int64]
+ AssigneeID string // "(none)" or "(any)" or a user ID
+ PosterID string // "(none)" or "(any)" or a user ID
MentionedID int64
ReviewRequestedID int64
ReviewedID int64
@@ -49,9 +51,9 @@ type IssuesOptions struct { //nolint
// prioritize issues from this repo
PriorityRepoID int64
IsArchived optional.Option[bool]
- Org *organization.Organization // issues permission scope
- Team *organization.Team // issues permission scope
- User *user_model.User // issues permission scope
+ Owner *user_model.User // issues permission scope, it could be an organization or a user
+ Team *organization.Team // issues permission scope
+ Doer *user_model.User // issues permission scope
}
// Copy returns a copy of the options.
@@ -70,11 +72,24 @@ func (o *IssuesOptions) Copy(edit ...func(options *IssuesOptions)) *IssuesOption
// applySorts sort an issues-related session based on the provided
// sortType string
func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) {
+ // Since this sortType is dynamically created, it has to be treated specially.
+ if after, ok := strings.CutPrefix(sortType, ScopeSortPrefix); ok {
+ scope := after
+ sess.Join("LEFT", "issue_label", "issue.id = issue_label.issue_id")
+ // "exclusive_order=0" means "no order is set", so exclude it from the JOIN criteria and then "LEFT JOIN" result is also null
+ sess.Join("LEFT", "label", "label.id = issue_label.label_id AND label.exclusive_order <> 0 AND label.name LIKE ?", scope+"/%")
+ // Use COALESCE to make sure we sort NULL last regardless of backend DB (2147483647 == max int)
+ sess.OrderBy("COALESCE(label.exclusive_order, 2147483647) ASC").Desc("issue.id")
+ return
+ }
+
switch sortType {
case "oldest":
sess.Asc("issue.created_unix").Asc("issue.id")
case "recentupdate":
sess.Desc("issue.updated_unix").Desc("issue.created_unix").Desc("issue.id")
+ case "recentclose":
+ sess.Desc("issue.closed_unix").Desc("issue.created_unix").Desc("issue.id")
case "leastupdate":
sess.Asc("issue.updated_unix").Asc("issue.created_unix").Asc("issue.id")
case "mostcomment":
@@ -273,8 +288,12 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) {
applyLabelsCondition(sess, opts)
- if opts.User != nil {
- sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.Value()))
+ if opts.Owner != nil {
+ sess.And(repo_model.UserOwnedRepoCond(opts.Owner.ID))
+ }
+
+ if opts.Doer != nil && !opts.Doer.IsAdmin {
+ sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.Doer.ID, opts.Owner, opts.Team, opts.IsPull.Value()))
}
}
@@ -321,20 +340,20 @@ func teamUnitsRepoCond(id string, userID, orgID, teamID int64, units ...unit.Typ
}
// issuePullAccessibleRepoCond userID must not be zero, this condition require join repository table
-func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organization.Organization, team *organization.Team, isPull bool) builder.Cond {
+func issuePullAccessibleRepoCond(repoIDstr string, userID int64, owner *user_model.User, team *organization.Team, isPull bool) builder.Cond {
cond := builder.NewCond()
unitType := unit.TypeIssues
if isPull {
unitType = unit.TypePullRequests
}
- if org != nil {
+ if owner != nil && owner.IsOrganization() {
if team != nil {
- cond = cond.And(teamUnitsRepoCond(repoIDstr, userID, org.ID, team.ID, unitType)) // special team member repos
+ cond = cond.And(teamUnitsRepoCond(repoIDstr, userID, owner.ID, team.ID, unitType)) // special team member repos
} else {
cond = cond.And(
builder.Or(
- repo_model.UserOrgUnitRepoCond(repoIDstr, userID, org.ID, unitType), // team member repos
- repo_model.UserOrgPublicUnitRepoCond(userID, org.ID), // user org public non-member repos, TODO: check repo has issues
+ repo_model.UserOrgUnitRepoCond(repoIDstr, userID, owner.ID, unitType), // team member repos
+ repo_model.UserOrgPublicUnitRepoCond(userID, owner.ID), // user org public non-member repos, TODO: check repo has issues
),
)
}
@@ -352,26 +371,25 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organizati
return cond
}
-func applyAssigneeCondition(sess *xorm.Session, assigneeID optional.Option[int64]) {
+func applyAssigneeCondition(sess *xorm.Session, assigneeID string) {
// old logic: 0 is also treated as "not filtering assignee", because the "assignee" was read as FormInt64
- if !assigneeID.Has() || assigneeID.Value() == 0 {
- return
- }
- if assigneeID.Value() == db.NoConditionID {
+ if assigneeID == "(none)" {
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)")
- } else {
+ } else if assigneeID == "(any)" {
+ sess.Where("issue.id IN (SELECT issue_id FROM issue_assignees)")
+ } else if assigneeIDInt64, _ := strconv.ParseInt(assigneeID, 10, 64); assigneeIDInt64 > 0 {
sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
- And("issue_assignees.assignee_id = ?", assigneeID.Value())
+ And("issue_assignees.assignee_id = ?", assigneeIDInt64)
}
}
-func applyPosterCondition(sess *xorm.Session, posterID optional.Option[int64]) {
- if !posterID.Has() {
- return
- }
- // poster doesn't need to support db.NoConditionID(-1), so just use the value as-is
- if posterID.Has() {
- sess.And("issue.poster_id=?", posterID.Value())
+func applyPosterCondition(sess *xorm.Session, posterID string) {
+ // Actually every issue has a poster.
+ // The "(none)" is for internal usage only: when doer tries to search non-existing user as poster, use "(none)" to return empty result.
+ if posterID == "(none)" {
+ sess.And("issue.poster_id=0")
+ } else if posterIDInt64, _ := strconv.ParseInt(posterID, 10, 64); posterIDInt64 > 0 {
+ sess.And("issue.poster_id=?", posterIDInt64)
}
}
diff --git a/models/issues/issue_stats.go b/models/issues/issue_stats.go
index 9ef9347a16..adedaa3d3a 100644
--- a/models/issues/issue_stats.go
+++ b/models/issues/issue_stats.go
@@ -94,10 +94,7 @@ func GetIssueStats(ctx context.Context, opts *IssuesOptions) (*IssueStats, error
// ids in a temporary table and join from them.
accum := &IssueStats{}
for i := 0; i < len(opts.IssueIDs); {
- chunk := i + MaxQueryParameters
- if chunk > len(opts.IssueIDs) {
- chunk = len(opts.IssueIDs)
- }
+ chunk := min(i+MaxQueryParameters, len(opts.IssueIDs))
stats, err := getIssueStatsChunk(ctx, opts, opts.IssueIDs[i:chunk])
if err != nil {
return nil, err
@@ -107,7 +104,7 @@ func GetIssueStats(ctx context.Context, opts *IssuesOptions) (*IssueStats, error
accum.YourRepositoriesCount += stats.YourRepositoriesCount
accum.AssignCount += stats.AssignCount
accum.CreateCount += stats.CreateCount
- accum.OpenCount += stats.MentionCount
+ accum.MentionCount += stats.MentionCount
accum.ReviewRequestedCount += stats.ReviewRequestedCount
accum.ReviewedCount += stats.ReviewedCount
i = chunk
diff --git a/models/issues/issue_test.go b/models/issues/issue_test.go
index dbbb1e4179..1c5db55bbc 100644
--- a/models/issues/issue_test.go
+++ b/models/issues/issue_test.go
@@ -4,8 +4,8 @@
package issues_test
import (
- "context"
"fmt"
+ "slices"
"sort"
"sync"
"testing"
@@ -16,7 +16,6 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
@@ -143,8 +142,8 @@ func TestUpdateIssueCols(t *testing.T) {
then := time.Now().Unix()
updatedIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID})
- assert.EqualValues(t, newTitle, updatedIssue.Title)
- assert.EqualValues(t, prevContent, updatedIssue.Content)
+ assert.Equal(t, newTitle, updatedIssue.Title)
+ assert.Equal(t, prevContent, updatedIssue.Content)
unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
}
@@ -156,7 +155,7 @@ func TestIssues(t *testing.T) {
}{
{
issues_model.IssuesOptions{
- AssigneeID: optional.Some(int64(1)),
+ AssigneeID: "1",
SortType: "oldest",
},
[]int64{1, 6},
@@ -203,7 +202,7 @@ func TestIssues(t *testing.T) {
assert.NoError(t, err)
if assert.Len(t, issues, len(test.ExpectedIssueIDs)) {
for i, issue := range issues {
- assert.EqualValues(t, test.ExpectedIssueIDs[i], issue.ID)
+ assert.Equal(t, test.ExpectedIssueIDs[i], issue.ID)
}
}
}
@@ -236,10 +235,10 @@ func testInsertIssue(t *testing.T, title, content string, expectIndex int64) *is
has, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Get(&newIssue)
assert.NoError(t, err)
assert.True(t, has)
- assert.EqualValues(t, issue.Title, newIssue.Title)
- assert.EqualValues(t, issue.Content, newIssue.Content)
+ assert.Equal(t, issue.Title, newIssue.Title)
+ assert.Equal(t, issue.Content, newIssue.Content)
if expectIndex > 0 {
- assert.EqualValues(t, expectIndex, newIssue.Index)
+ assert.Equal(t, expectIndex, newIssue.Index)
}
})
return &newIssue
@@ -272,8 +271,8 @@ func TestIssue_ResolveMentions(t *testing.T) {
for i, user := range resolved {
ids[i] = user.ID
}
- sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
- assert.EqualValues(t, expected, ids)
+ slices.Sort(ids)
+ assert.Equal(t, expected, ids)
}
// Public repo, existing user
@@ -294,7 +293,7 @@ func TestResourceIndex(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
var wg sync.WaitGroup
- for i := 0; i < 100; i++ {
+ for i := range 100 {
wg.Add(1)
go func(i int) {
testInsertIssue(t, fmt.Sprintf("issue %d", i+1), "my issue", 0)
@@ -316,7 +315,7 @@ func TestCorrectIssueStats(t *testing.T) {
issueAmount := issues_model.MaxQueryParameters + 10
var wg sync.WaitGroup
- for i := 0; i < issueAmount; i++ {
+ for i := range issueAmount {
wg.Add(1)
go func(i int) {
testInsertIssue(t, fmt.Sprintf("Issue %d", i+1), "Bugs are nasty", 0)
@@ -326,7 +325,7 @@ func TestCorrectIssueStats(t *testing.T) {
wg.Wait()
// Now we will get all issueID's that match the "Bugs are nasty" query.
- issues, err := issues_model.Issues(context.TODO(), &issues_model.IssuesOptions{
+ issues, err := issues_model.Issues(t.Context(), &issues_model.IssuesOptions{
Paginator: &db.ListOptions{
PageSize: issueAmount,
},
@@ -394,28 +393,28 @@ func TestIssueLoadAttributes(t *testing.T) {
for _, issue := range issueList {
assert.NoError(t, issue.LoadAttributes(db.DefaultContext))
- assert.EqualValues(t, issue.RepoID, issue.Repo.ID)
+ assert.Equal(t, issue.RepoID, issue.Repo.ID)
for _, label := range issue.Labels {
- assert.EqualValues(t, issue.RepoID, label.RepoID)
+ assert.Equal(t, issue.RepoID, label.RepoID)
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID})
}
if issue.PosterID > 0 {
- assert.EqualValues(t, issue.PosterID, issue.Poster.ID)
+ assert.Equal(t, issue.PosterID, issue.Poster.ID)
}
if issue.AssigneeID > 0 {
- assert.EqualValues(t, issue.AssigneeID, issue.Assignee.ID)
+ assert.Equal(t, issue.AssigneeID, issue.Assignee.ID)
}
if issue.MilestoneID > 0 {
- assert.EqualValues(t, issue.MilestoneID, issue.Milestone.ID)
+ assert.Equal(t, issue.MilestoneID, issue.Milestone.ID)
}
if issue.IsPull {
- assert.EqualValues(t, issue.ID, issue.PullRequest.IssueID)
+ assert.Equal(t, issue.ID, issue.PullRequest.IssueID)
}
for _, attachment := range issue.Attachments {
- assert.EqualValues(t, issue.ID, attachment.IssueID)
+ assert.Equal(t, issue.ID, attachment.IssueID)
}
for _, comment := range issue.Comments {
- assert.EqualValues(t, issue.ID, comment.IssueID)
+ assert.Equal(t, issue.ID, comment.IssueID)
}
if issue.ID == int64(1) {
assert.Equal(t, int64(400), issue.TotalTrackedTime)
diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go
index 479834045c..9b99787e3b 100644
--- a/models/issues/issue_update.go
+++ b/models/issues/issue_update.go
@@ -5,16 +5,14 @@ package issues
import (
"context"
+ "errors"
"fmt"
"strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
- project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo"
- system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
@@ -28,38 +26,40 @@ import (
// UpdateIssueCols updates cols of issue
func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error {
- if _, err := db.GetEngine(ctx).ID(issue.ID).Cols(cols...).Update(issue); err != nil {
- return err
- }
- return nil
+ _, err := db.GetEngine(ctx).ID(issue.ID).Cols(cols...).Update(issue)
+ return err
}
-func ChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) {
- // Reload the issue
- currentIssue, err := GetIssueByID(ctx, issue.ID)
- if err != nil {
- return nil, err
- }
+// ErrIssueIsClosed is used when close a closed issue
+type ErrIssueIsClosed struct {
+ ID int64
+ RepoID int64
+ Index int64
+ IsPull bool
+}
- // Nothing should be performed if current status is same as target status
- if currentIssue.IsClosed == isClosed {
- if !issue.IsPull {
- return nil, ErrIssueWasClosed{
- ID: issue.ID,
- }
- }
- return nil, ErrPullWasClosed{
- ID: issue.ID,
- }
- }
+// IsErrIssueIsClosed checks if an error is a ErrIssueIsClosed.
+func IsErrIssueIsClosed(err error) bool {
+ _, ok := err.(ErrIssueIsClosed)
+ return ok
+}
- issue.IsClosed = isClosed
- return doChangeIssueStatus(ctx, issue, doer, isMergePull)
+func (err ErrIssueIsClosed) Error() string {
+ return fmt.Sprintf("%s [id: %d, repo_id: %d, index: %d] is already closed", util.Iif(err.IsPull, "Pull Request", "Issue"), err.ID, err.RepoID, err.Index)
}
-func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isMergePull bool) (*Comment, error) {
+func SetIssueAsClosed(ctx context.Context, issue *Issue, doer *user_model.User, isMergePull bool) (*Comment, error) {
+ if issue.IsClosed {
+ return nil, ErrIssueIsClosed{
+ ID: issue.ID,
+ RepoID: issue.RepoID,
+ Index: issue.Index,
+ IsPull: issue.IsPull,
+ }
+ }
+
// Check for open dependencies
- if issue.IsClosed && issue.Repo.IsDependenciesEnabled(ctx) {
+ if issue.Repo.IsDependenciesEnabled(ctx) {
// only check if dependencies are enabled and we're about to close an issue, otherwise reopening an issue would fail when there are unsatisfied dependencies
noDeps, err := IssueNoDependenciesLeft(ctx, issue)
if err != nil {
@@ -71,16 +71,63 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use
}
}
- if issue.IsClosed {
- issue.ClosedUnix = timeutil.TimeStampNow()
- } else {
- issue.ClosedUnix = 0
+ issue.IsClosed = true
+ issue.ClosedUnix = timeutil.TimeStampNow()
+
+ if cnt, err := db.GetEngine(ctx).ID(issue.ID).Cols("is_closed", "closed_unix").
+ Where("is_closed = ?", false).
+ Update(issue); err != nil {
+ return nil, err
+ } else if cnt != 1 {
+ return nil, ErrIssueAlreadyChanged
}
- if err := UpdateIssueCols(ctx, issue, "is_closed", "closed_unix"); err != nil {
+ return updateIssueNumbers(ctx, issue, doer, util.Iif(isMergePull, CommentTypeMergePull, CommentTypeClose))
+}
+
+// ErrIssueIsOpen is used when reopen an opened issue
+type ErrIssueIsOpen struct {
+ ID int64
+ RepoID int64
+ IsPull bool
+ Index int64
+}
+
+// IsErrIssueIsOpen checks if an error is a ErrIssueIsOpen.
+func IsErrIssueIsOpen(err error) bool {
+ _, ok := err.(ErrIssueIsOpen)
+ return ok
+}
+
+func (err ErrIssueIsOpen) Error() string {
+ return fmt.Sprintf("%s [id: %d, repo_id: %d, index: %d] is already open", util.Iif(err.IsPull, "Pull Request", "Issue"), err.ID, err.RepoID, err.Index)
+}
+
+func setIssueAsReopen(ctx context.Context, issue *Issue, doer *user_model.User) (*Comment, error) {
+ if !issue.IsClosed {
+ return nil, ErrIssueIsOpen{
+ ID: issue.ID,
+ RepoID: issue.RepoID,
+ Index: issue.Index,
+ IsPull: issue.IsPull,
+ }
+ }
+
+ issue.IsClosed = false
+ issue.ClosedUnix = 0
+
+ if cnt, err := db.GetEngine(ctx).ID(issue.ID).Cols("is_closed", "closed_unix").
+ Where("is_closed = ?", true).
+ Update(issue); err != nil {
return nil, err
+ } else if cnt != 1 {
+ return nil, ErrIssueAlreadyChanged
}
+ return updateIssueNumbers(ctx, issue, doer, CommentTypeReopen)
+}
+
+func updateIssueNumbers(ctx context.Context, issue *Issue, doer *user_model.User, cmtType CommentType) (*Comment, error) {
// Update issue count of labels
if err := issue.LoadLabels(ctx); err != nil {
return nil, err
@@ -103,14 +150,6 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use
return nil, err
}
- // New action comment
- cmtType := CommentTypeClose
- if !issue.IsClosed {
- cmtType = CommentTypeReopen
- } else if isMergePull {
- cmtType = CommentTypeMergePull
- }
-
return CreateComment(ctx, &CreateCommentOptions{
Type: cmtType,
Doer: doer,
@@ -134,7 +173,7 @@ func CloseIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Comm
}
defer committer.Close()
- comment, err := ChangeIssueStatus(ctx, issue, doer, true, false)
+ comment, err := SetIssueAsClosed(ctx, issue, doer, false)
if err != nil {
return nil, err
}
@@ -159,7 +198,7 @@ func ReopenIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Com
}
defer committer.Close()
- comment, err := ChangeIssueStatus(ctx, issue, doer, false, false)
+ comment, err := setIssueAsReopen(ctx, issue, doer)
if err != nil {
return nil, err
}
@@ -265,7 +304,7 @@ func UpdateIssueAttachments(ctx context.Context, issueID int64, uuids []string)
if err != nil {
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err)
}
- for i := 0; i < len(attachments); i++ {
+ for i := range attachments {
attachments[i].IssueID = issueID
if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil {
return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err)
@@ -345,10 +384,10 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue
}
if opts.Issue.Index <= 0 {
- return fmt.Errorf("no issue index provided")
+ return errors.New("no issue index provided")
}
if opts.Issue.ID > 0 {
- return fmt.Errorf("issue exist")
+ return errors.New("issue exist")
}
if _, err := e.Insert(opts.Issue); err != nil {
@@ -570,7 +609,7 @@ func ResolveIssueMentionsByVisibility(ctx context.Context, issue *Issue, doer *u
unittype = unit.TypePullRequests
}
for _, team := range teams {
- if team.AccessMode >= perm.AccessModeAdmin {
+ if team.HasAdminAccess() {
checked = append(checked, team.ID)
resolved[issue.Repo.Owner.LowerName+"/"+team.LowerName] = true
continue
@@ -674,137 +713,13 @@ func UpdateReactionsMigrationsByType(ctx context.Context, gitServiceType api.Git
return err
}
-// DeleteIssuesByRepoID deletes issues by repositories id
-func DeleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []string, err error) {
- // MariaDB has a performance bug: https://jira.mariadb.org/browse/MDEV-16289
- // so here it uses "DELETE ... WHERE IN" with pre-queried IDs.
- sess := db.GetEngine(ctx)
-
- for {
- issueIDs := make([]int64, 0, db.DefaultMaxInSize)
-
- err := sess.Table(&Issue{}).Where("repo_id = ?", repoID).OrderBy("id").Limit(db.DefaultMaxInSize).Cols("id").Find(&issueIDs)
- if err != nil {
- return nil, err
- }
-
- if len(issueIDs) == 0 {
- break
- }
-
- // Delete content histories
- _, err = sess.In("issue_id", issueIDs).Delete(&ContentHistory{})
- if err != nil {
- return nil, err
- }
-
- // Delete comments and attachments
- _, err = sess.In("issue_id", issueIDs).Delete(&Comment{})
- if err != nil {
- return nil, err
- }
-
- // Dependencies for issues in this repository
- _, err = sess.In("issue_id", issueIDs).Delete(&IssueDependency{})
- if err != nil {
- return nil, err
- }
-
- // Delete dependencies for issues in other repositories
- _, err = sess.In("dependency_id", issueIDs).Delete(&IssueDependency{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("issue_id", issueIDs).Delete(&IssueUser{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("issue_id", issueIDs).Delete(&Reaction{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("issue_id", issueIDs).Delete(&IssueWatch{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("issue_id", issueIDs).Delete(&Stopwatch{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("issue_id", issueIDs).Delete(&TrackedTime{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("issue_id", issueIDs).Delete(&project_model.ProjectIssue{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("dependent_issue_id", issueIDs).Delete(&Comment{})
- if err != nil {
- return nil, err
- }
-
- var attachments []*repo_model.Attachment
- err = sess.In("issue_id", issueIDs).Find(&attachments)
- if err != nil {
- return nil, err
- }
-
- for j := range attachments {
- attachmentPaths = append(attachmentPaths, attachments[j].RelativePath())
- }
-
- _, err = sess.In("issue_id", issueIDs).Delete(&repo_model.Attachment{})
- if err != nil {
- return nil, err
- }
-
- _, err = sess.In("id", issueIDs).Delete(&Issue{})
- if err != nil {
- return nil, err
- }
- }
-
- return attachmentPaths, err
-}
-
-// DeleteOrphanedIssues delete issues without a repo
-func DeleteOrphanedIssues(ctx context.Context) error {
- var attachmentPaths []string
- err := db.WithTx(ctx, func(ctx context.Context) error {
- var ids []int64
-
- if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id").
- Join("LEFT", "repository", "issue.repo_id=repository.id").
- Where(builder.IsNull{"repository.id"}).GroupBy("issue.repo_id").
- Find(&ids); err != nil {
- return err
- }
-
- for i := range ids {
- paths, err := DeleteIssuesByRepoID(ctx, ids[i])
- if err != nil {
- return err
- }
- attachmentPaths = append(attachmentPaths, paths...)
- }
-
- return nil
- })
- if err != nil {
- return err
- }
-
- // Remove issue attachment files.
- for i := range attachmentPaths {
- system_model.RemoveAllWithNotice(ctx, "Delete issue attachment", attachmentPaths[i])
+func GetOrphanedIssueRepoIDs(ctx context.Context) ([]int64, error) {
+ var repoIDs []int64
+ if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id").
+ Join("LEFT", "repository", "issue.repo_id=repository.id").
+ Where(builder.IsNull{"repository.id"}).
+ Find(&repoIDs); err != nil {
+ return nil, err
}
- return nil
+ return repoIDs, nil
}
diff --git a/models/issues/label.go b/models/issues/label.go
index b9d24bbe99..cfbe100926 100644
--- a/models/issues/label.go
+++ b/models/issues/label.go
@@ -87,6 +87,7 @@ type Label struct {
OrgID int64 `xorm:"INDEX"`
Name string
Exclusive bool
+ ExclusiveOrder int `xorm:"DEFAULT 0"` // 0 means no exclusive order
Description string
Color string `xorm:"VARCHAR(7)"`
NumIssues int
@@ -236,7 +237,7 @@ func UpdateLabel(ctx context.Context, l *Label) error {
}
l.Color = color
- return updateLabelCols(ctx, l, "name", "description", "color", "exclusive", "archived_unix")
+ return updateLabelCols(ctx, l, "name", "description", "color", "exclusive", "exclusive_order", "archived_unix")
}
// DeleteLabel delete a label
@@ -299,6 +300,9 @@ func GetLabelByID(ctx context.Context, labelID int64) (*Label, error) {
// GetLabelsByIDs returns a list of labels by IDs
func GetLabelsByIDs(ctx context.Context, labelIDs []int64, cols ...string) ([]*Label, error) {
labels := make([]*Label, 0, len(labelIDs))
+ if len(labelIDs) == 0 {
+ return labels, nil
+ }
return labels, db.GetEngine(ctx).Table("label").
In("id", labelIDs).
Asc("name").
@@ -375,6 +379,9 @@ func BuildLabelNamesIssueIDsCondition(labelNames []string) *builder.Builder {
// it silently ignores label IDs that do not belong to the repository.
func GetLabelsInRepoByIDs(ctx context.Context, repoID int64, labelIDs []int64) ([]*Label, error) {
labels := make([]*Label, 0, len(labelIDs))
+ if len(labelIDs) == 0 {
+ return labels, nil
+ }
return labels, db.GetEngine(ctx).
Where("repo_id = ?", repoID).
In("id", labelIDs).
@@ -447,6 +454,9 @@ func GetLabelInOrgByID(ctx context.Context, orgID, labelID int64) (*Label, error
// it silently ignores label IDs that do not belong to the organization.
func GetLabelsInOrgByIDs(ctx context.Context, orgID int64, labelIDs []int64) ([]*Label, error) {
labels := make([]*Label, 0, len(labelIDs))
+ if len(labelIDs) == 0 {
+ return labels, nil
+ }
return labels, db.GetEngine(ctx).
Where("org_id = ?", orgID).
In("id", labelIDs).
diff --git a/models/issues/label_test.go b/models/issues/label_test.go
index 185fa11bbc..226036d543 100644
--- a/models/issues/label_test.go
+++ b/models/issues/label_test.go
@@ -20,7 +20,7 @@ func TestLabel_CalOpenIssues(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
label.CalOpenIssues()
- assert.EqualValues(t, 2, label.NumOpenIssues)
+ assert.Equal(t, 2, label.NumOpenIssues)
}
func TestLabel_LoadSelectedLabelsAfterClick(t *testing.T) {
@@ -154,7 +154,7 @@ func TestGetLabelsByRepoID(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, labels, len(expectedIssueIDs))
for i, label := range labels {
- assert.EqualValues(t, expectedIssueIDs[i], label.ID)
+ assert.Equal(t, expectedIssueIDs[i], label.ID)
}
}
testSuccess(1, "leastissues", []int64{2, 1})
@@ -221,7 +221,7 @@ func TestGetLabelsByOrgID(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, labels, len(expectedIssueIDs))
for i, label := range labels {
- assert.EqualValues(t, expectedIssueIDs[i], label.ID)
+ assert.Equal(t, expectedIssueIDs[i], label.ID)
}
}
testSuccess(3, "leastissues", []int64{3, 4})
@@ -267,10 +267,10 @@ func TestUpdateLabel(t *testing.T) {
label.Name = update.Name
assert.NoError(t, issues_model.UpdateLabel(db.DefaultContext, update))
newLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
- assert.EqualValues(t, label.ID, newLabel.ID)
- assert.EqualValues(t, label.Color, newLabel.Color)
- assert.EqualValues(t, label.Name, newLabel.Name)
- assert.EqualValues(t, label.Description, newLabel.Description)
+ assert.Equal(t, label.ID, newLabel.ID)
+ assert.Equal(t, label.Color, newLabel.Color)
+ assert.Equal(t, label.Name, newLabel.Name)
+ assert.Equal(t, label.Description, newLabel.Description)
assert.EqualValues(t, 0, newLabel.ArchivedUnix)
unittest.CheckConsistencyFor(t, &issues_model.Label{}, &repo_model.Repository{})
}
@@ -313,7 +313,7 @@ func TestNewIssueLabel(t *testing.T) {
Content: "1",
})
label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2})
- assert.EqualValues(t, prevNumIssues+1, label.NumIssues)
+ assert.Equal(t, prevNumIssues+1, label.NumIssues)
// re-add existing IssueLabel
assert.NoError(t, issues_model.NewIssueLabel(db.DefaultContext, issue, label, doer))
@@ -366,11 +366,11 @@ func TestNewIssueLabels(t *testing.T) {
})
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label1.ID})
label1 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
- assert.EqualValues(t, 3, label1.NumIssues)
- assert.EqualValues(t, 1, label1.NumClosedIssues)
+ assert.Equal(t, 3, label1.NumIssues)
+ assert.Equal(t, 1, label1.NumClosedIssues)
label2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2})
- assert.EqualValues(t, 1, label2.NumIssues)
- assert.EqualValues(t, 1, label2.NumClosedIssues)
+ assert.Equal(t, 1, label2.NumIssues)
+ assert.Equal(t, 1, label2.NumClosedIssues)
// corner case: test empty slice
assert.NoError(t, issues_model.NewIssueLabels(db.DefaultContext, issue, []*issues_model.Label{}, doer))
@@ -408,8 +408,8 @@ func TestDeleteIssueLabel(t *testing.T) {
LabelID: labelID,
}, `content=''`)
label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID})
- assert.EqualValues(t, expectedNumIssues, label.NumIssues)
- assert.EqualValues(t, expectedNumClosedIssues, label.NumClosedIssues)
+ assert.Equal(t, expectedNumIssues, label.NumIssues)
+ assert.Equal(t, expectedNumClosedIssues, label.NumClosedIssues)
}
testSuccess(1, 1, 2)
testSuccess(2, 5, 2)
diff --git a/models/issues/milestone_test.go b/models/issues/milestone_test.go
index 28cd0c028b..f73355c27d 100644
--- a/models/issues/milestone_test.go
+++ b/models/issues/milestone_test.go
@@ -69,7 +69,7 @@ func TestGetMilestonesByRepoID(t *testing.T) {
assert.Len(t, milestones, n)
for _, milestone := range milestones {
- assert.EqualValues(t, repoID, milestone.RepoID)
+ assert.Equal(t, repoID, milestone.RepoID)
}
}
test(1, api.StateOpen)
@@ -327,7 +327,7 @@ func TestUpdateMilestone(t *testing.T) {
milestone.Content = "newMilestoneContent"
assert.NoError(t, issues_model.UpdateMilestone(db.DefaultContext, milestone, milestone.IsClosed))
milestone = unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1})
- assert.EqualValues(t, "newMilestoneName", milestone.Name)
+ assert.Equal(t, "newMilestoneName", milestone.Name)
unittest.CheckConsistencyFor(t, &issues_model.Milestone{})
}
@@ -364,7 +364,7 @@ func TestMigrate_InsertMilestones(t *testing.T) {
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, ms)
repoModified := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo.ID})
- assert.EqualValues(t, repo.NumMilestones+1, repoModified.NumMilestones)
+ assert.Equal(t, repo.NumMilestones+1, repoModified.NumMilestones)
unittest.CheckConsistencyFor(t, &issues_model.Milestone{})
}
diff --git a/models/issues/pull.go b/models/issues/pull.go
index 8c43eb19dd..0ff32e2473 100644
--- a/models/issues/pull.go
+++ b/models/issues/pull.go
@@ -6,10 +6,10 @@ package issues
import (
"context"
+ "errors"
"fmt"
"io"
"regexp"
- "strconv"
"strings"
"code.gitea.io/gitea/models/db"
@@ -80,22 +80,6 @@ func (err ErrPullRequestAlreadyExists) Unwrap() error {
return util.ErrAlreadyExist
}
-// ErrPullWasClosed is used close a closed pull request
-type ErrPullWasClosed struct {
- ID int64
- Index int64
-}
-
-// IsErrPullWasClosed checks if an error is a ErrErrPullWasClosed.
-func IsErrPullWasClosed(err error) bool {
- _, ok := err.(ErrPullWasClosed)
- return ok
-}
-
-func (err ErrPullWasClosed) Error() string {
- return fmt.Sprintf("Pull request [%d] %d was already closed", err.ID, err.Index)
-}
-
// PullRequestType defines pull request type
type PullRequestType int
@@ -119,27 +103,6 @@ const (
PullRequestStatusAncestor
)
-func (status PullRequestStatus) String() string {
- switch status {
- case PullRequestStatusConflict:
- return "CONFLICT"
- case PullRequestStatusChecking:
- return "CHECKING"
- case PullRequestStatusMergeable:
- return "MERGEABLE"
- case PullRequestStatusManuallyMerged:
- return "MANUALLY_MERGED"
- case PullRequestStatusError:
- return "ERROR"
- case PullRequestStatusEmpty:
- return "EMPTY"
- case PullRequestStatusAncestor:
- return "ANCESTOR"
- default:
- return strconv.Itoa(int(status))
- }
-}
-
// PullRequestFlow the flow of pull request
type PullRequestFlow int
@@ -301,7 +264,7 @@ func (pr *PullRequest) LoadRequestedReviewers(ctx context.Context) error {
return nil
}
- reviews, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
+ reviews, _, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
if err != nil {
return err
}
@@ -320,7 +283,7 @@ func (pr *PullRequest) LoadRequestedReviewers(ctx context.Context) error {
// LoadRequestedReviewersTeams loads the requested reviewers teams.
func (pr *PullRequest) LoadRequestedReviewersTeams(ctx context.Context) error {
- reviews, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
+ reviews, _, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
if err != nil {
return err
}
@@ -686,12 +649,6 @@ func GetAllUnmergedAgitPullRequestByPoster(ctx context.Context, uid int64) ([]*P
return pulls, err
}
-// Update updates all fields of pull request.
-func (pr *PullRequest) Update(ctx context.Context) error {
- _, err := db.GetEngine(ctx).ID(pr.ID).AllCols().Update(pr)
- return err
-}
-
// UpdateCols updates specific fields of pull request.
func (pr *PullRequest) UpdateCols(ctx context.Context, cols ...string) error {
_, err := db.GetEngine(ctx).ID(pr.ID).Cols(cols...).Update(pr)
@@ -748,7 +705,7 @@ func (pr *PullRequest) GetWorkInProgressPrefix(ctx context.Context) string {
// UpdateCommitDivergence update Divergence of a pull request
func (pr *PullRequest) UpdateCommitDivergence(ctx context.Context, ahead, behind int) error {
if pr.ID == 0 {
- return fmt.Errorf("pull ID is 0")
+ return errors.New("pull ID is 0")
}
pr.CommitsAhead = ahead
pr.CommitsBehind = behind
@@ -941,7 +898,7 @@ func ParseCodeOwnersLine(ctx context.Context, tokens []string) (*CodeOwnerRule,
if strings.Contains(user, "/") {
s := strings.Split(user, "/")
if len(s) != 2 {
- warnings = append(warnings, fmt.Sprintf("incorrect codeowner group: %s", user))
+ warnings = append(warnings, "incorrect codeowner group: "+user)
continue
}
orgName := s[0]
@@ -949,12 +906,12 @@ func ParseCodeOwnersLine(ctx context.Context, tokens []string) (*CodeOwnerRule,
org, err := org_model.GetOrgByName(ctx, orgName)
if err != nil {
- warnings = append(warnings, fmt.Sprintf("incorrect codeowner organization: %s", user))
+ warnings = append(warnings, "incorrect codeowner organization: "+user)
continue
}
teams, err := org.LoadTeams(ctx)
if err != nil {
- warnings = append(warnings, fmt.Sprintf("incorrect codeowner team: %s", user))
+ warnings = append(warnings, "incorrect codeowner team: "+user)
continue
}
@@ -966,7 +923,7 @@ func ParseCodeOwnersLine(ctx context.Context, tokens []string) (*CodeOwnerRule,
} else {
u, err := user_model.GetUserByName(ctx, user)
if err != nil {
- warnings = append(warnings, fmt.Sprintf("incorrect codeowner user: %s", user))
+ warnings = append(warnings, "incorrect codeowner user: "+user)
continue
}
rule.Users = append(rule.Users, u)
diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go
index 59010aa9d0..84f9f6166d 100644
--- a/models/issues/pull_list.go
+++ b/models/issues/pull_list.go
@@ -28,11 +28,16 @@ type PullRequestsOptions struct {
Labels []int64
MilestoneID int64
PosterID int64
+ BaseBranch string
}
func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullRequestsOptions) *xorm.Session {
sess := db.GetEngine(ctx).Where("pull_request.base_repo_id=?", baseRepoID)
+ if opts.BaseBranch != "" {
+ sess.And("pull_request.base_branch=?", opts.BaseBranch)
+ }
+
sess.Join("INNER", "issue", "pull_request.issue_id = issue.id")
switch opts.State {
case "closed", "open":
@@ -56,7 +61,7 @@ func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullR
}
// GetUnmergedPullRequestsByHeadInfo returns all pull requests that are open and has not been merged
-func GetUnmergedPullRequestsByHeadInfo(ctx context.Context, repoID int64, branch string) ([]*PullRequest, error) {
+func GetUnmergedPullRequestsByHeadInfo(ctx context.Context, repoID int64, branch string) (PullRequestList, error) {
prs := make([]*PullRequest, 0, 2)
sess := db.GetEngine(ctx).
Join("INNER", "issue", "issue.id = pull_request.issue_id").
@@ -111,7 +116,7 @@ func HasUnmergedPullRequestsByHeadInfo(ctx context.Context, repoID int64, branch
// GetUnmergedPullRequestsByBaseInfo returns all pull requests that are open and has not been merged
// by given base information (repo and branch).
-func GetUnmergedPullRequestsByBaseInfo(ctx context.Context, repoID int64, branch string) ([]*PullRequest, error) {
+func GetUnmergedPullRequestsByBaseInfo(ctx context.Context, repoID int64, branch string) (PullRequestList, error) {
prs := make([]*PullRequest, 0, 2)
return prs, db.GetEngine(ctx).
Where("base_repo_id=? AND base_branch=? AND has_merged=? AND issue.is_closed=?",
@@ -147,7 +152,8 @@ func PullRequests(ctx context.Context, baseRepoID int64, opts *PullRequestsOptio
applySorts(findSession, opts.SortType, 0)
findSession = db.SetSessionPagination(findSession, opts)
prs := make([]*PullRequest, 0, opts.PageSize)
- return prs, maxResults, findSession.Find(&prs)
+ found := findSession.Find(&prs)
+ return prs, maxResults, found
}
// PullRequestList defines a list of pull requests
@@ -166,6 +172,23 @@ func (prs PullRequestList) getRepositoryIDs() []int64 {
return repoIDs.Values()
}
+func (prs PullRequestList) SetBaseRepo(baseRepo *repo_model.Repository) {
+ for _, pr := range prs {
+ if pr.BaseRepo == nil {
+ pr.BaseRepo = baseRepo
+ }
+ }
+}
+
+func (prs PullRequestList) SetHeadRepo(headRepo *repo_model.Repository) {
+ for _, pr := range prs {
+ if pr.HeadRepo == nil {
+ pr.HeadRepo = headRepo
+ pr.isHeadRepoLoaded = true
+ }
+ }
+}
+
func (prs PullRequestList) LoadRepositories(ctx context.Context) error {
repoIDs := prs.getRepositoryIDs()
reposMap := make(map[int64]*repo_model.Repository, len(repoIDs))
diff --git a/models/issues/pull_list_test.go b/models/issues/pull_list_test.go
index c7a898ca4e..eb2de006d6 100644
--- a/models/issues/pull_list_test.go
+++ b/models/issues/pull_list_test.go
@@ -16,11 +16,11 @@ import (
func TestPullRequestList_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- prs := []*issues_model.PullRequest{
+ prs := issues_model.PullRequestList{
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
}
- assert.NoError(t, issues_model.PullRequestList(prs).LoadAttributes(db.DefaultContext))
+ assert.NoError(t, prs.LoadAttributes(db.DefaultContext))
for _, pr := range prs {
assert.NotNil(t, pr.Issue)
assert.Equal(t, pr.IssueID, pr.Issue.ID)
@@ -32,26 +32,26 @@ func TestPullRequestList_LoadAttributes(t *testing.T) {
func TestPullRequestList_LoadReviewCommentsCounts(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- prs := []*issues_model.PullRequest{
+ prs := issues_model.PullRequestList{
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
}
- reviewComments, err := issues_model.PullRequestList(prs).LoadReviewCommentsCounts(db.DefaultContext)
+ reviewComments, err := prs.LoadReviewCommentsCounts(db.DefaultContext)
assert.NoError(t, err)
assert.Len(t, reviewComments, 2)
for _, pr := range prs {
- assert.EqualValues(t, 1, reviewComments[pr.IssueID])
+ assert.Equal(t, 1, reviewComments[pr.IssueID])
}
}
func TestPullRequestList_LoadReviews(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- prs := []*issues_model.PullRequest{
+ prs := issues_model.PullRequestList{
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
}
- reviewList, err := issues_model.PullRequestList(prs).LoadReviews(db.DefaultContext)
+ reviewList, err := prs.LoadReviews(db.DefaultContext)
assert.NoError(t, err)
// 1, 7, 8, 9, 10, 22
assert.Len(t, reviewList, 6)
diff --git a/models/issues/pull_test.go b/models/issues/pull_test.go
index 090659864a..39efaa5792 100644
--- a/models/issues/pull_test.go
+++ b/models/issues/pull_test.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestPullRequest_LoadAttributes(t *testing.T) {
@@ -76,6 +77,47 @@ func TestPullRequestsNewest(t *testing.T) {
}
}
+func TestPullRequests_Closed_RecentSortType(t *testing.T) {
+ // Issue ID | Closed At. | Updated At
+ // 2 | 1707270001 | 1707270001
+ // 3 | 1707271000 | 1707279999
+ // 11 | 1707279999 | 1707275555
+ tests := []struct {
+ sortType string
+ expectedIssueIDOrder []int64
+ }{
+ {"recentupdate", []int64{3, 11, 2}},
+ {"recentclose", []int64{11, 3, 2}},
+ }
+
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ _, err := db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707270001, updated_unix = 1707270001, is_closed = true WHERE id = 2")
+ require.NoError(t, err)
+ _, err = db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707271000, updated_unix = 1707279999, is_closed = true WHERE id = 3")
+ require.NoError(t, err)
+ _, err = db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707279999, updated_unix = 1707275555, is_closed = true WHERE id = 11")
+ require.NoError(t, err)
+
+ for _, test := range tests {
+ t.Run(test.sortType, func(t *testing.T) {
+ prs, _, err := issues_model.PullRequests(db.DefaultContext, 1, &issues_model.PullRequestsOptions{
+ ListOptions: db.ListOptions{
+ Page: 1,
+ },
+ State: "closed",
+ SortType: test.sortType,
+ })
+ require.NoError(t, err)
+
+ if assert.Len(t, prs, len(test.expectedIssueIDOrder)) {
+ for i := range test.expectedIssueIDOrder {
+ assert.Equal(t, test.expectedIssueIDOrder[i], prs[i].IssueID)
+ }
+ }
+ })
+ }
+}
+
func TestLoadRequestedReviewers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
@@ -206,19 +248,6 @@ func TestGetPullRequestByIssueID(t *testing.T) {
assert.True(t, issues_model.IsErrPullRequestNotExist(err))
}
-func TestPullRequest_Update(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
- pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
- pr.BaseBranch = "baseBranch"
- pr.HeadBranch = "headBranch"
- pr.Update(db.DefaultContext)
-
- pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
- assert.Equal(t, "baseBranch", pr.BaseBranch)
- assert.Equal(t, "headBranch", pr.HeadBranch)
- unittest.CheckConsistencyFor(t, pr)
-}
-
func TestPullRequest_UpdateCols(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr := &issues_model.PullRequest{
@@ -285,7 +314,7 @@ func TestDeleteOrphanedObjects(t *testing.T) {
countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{})
assert.NoError(t, err)
- assert.EqualValues(t, countBefore, countAfter)
+ assert.Equal(t, countBefore, countAfter)
}
func TestParseCodeOwnersLine(t *testing.T) {
@@ -318,7 +347,7 @@ func TestGetApprovers(t *testing.T) {
setting.Repository.PullRequest.DefaultMergeMessageOfficialApproversOnly = false
approvers := pr.GetApprovers(db.DefaultContext)
expected := "Reviewed-by: User Five <user5@example.com>\nReviewed-by: Org Six <org6@example.com>\n"
- assert.EqualValues(t, expected, approvers)
+ assert.Equal(t, expected, approvers)
}
func TestGetPullRequestByMergedCommit(t *testing.T) {
diff --git a/models/issues/reaction.go b/models/issues/reaction.go
index 11b3c6be20..f24001fd23 100644
--- a/models/issues/reaction.go
+++ b/models/issues/reaction.go
@@ -7,6 +7,7 @@ import (
"bytes"
"context"
"fmt"
+ "strings"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
@@ -321,6 +322,11 @@ func valuesUser(m map[int64]*user_model.User) []*user_model.User {
return values
}
+// newMigrationOriginalUser creates and returns a fake user for external user
+func newMigrationOriginalUser(name string) *user_model.User {
+ return &user_model.User{ID: 0, Name: name, LowerName: strings.ToLower(name)}
+}
+
// LoadUsers loads reactions' all users
func (list ReactionList) LoadUsers(ctx context.Context, repo *repo_model.Repository) ([]*user_model.User, error) {
if len(list) == 0 {
@@ -338,7 +344,7 @@ func (list ReactionList) LoadUsers(ctx context.Context, repo *repo_model.Reposit
for _, reaction := range list {
if reaction.OriginalAuthor != "" {
- reaction.User = user_model.NewReplaceUser(fmt.Sprintf("%s(%s)", reaction.OriginalAuthor, repo.OriginalServiceType.Name()))
+ reaction.User = newMigrationOriginalUser(fmt.Sprintf("%s(%s)", reaction.OriginalAuthor, repo.OriginalServiceType.Name()))
} else if user, ok := userMaps[reaction.UserID]; ok {
reaction.User = user
} else {
diff --git a/models/issues/review.go b/models/issues/review.go
index 3e787273be..71fdb7456f 100644
--- a/models/issues/review.go
+++ b/models/issues/review.go
@@ -5,6 +5,7 @@ package issues
import (
"context"
+ "errors"
"fmt"
"slices"
"strings"
@@ -374,7 +375,7 @@ func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error
review.Type = ReviewTypeRequest
review.ReviewerTeamID = opts.ReviewerTeam.ID
} else {
- return nil, fmt.Errorf("provide either reviewer or reviewer team")
+ return nil, errors.New("provide either reviewer or reviewer team")
}
if _, err := sess.Insert(review); err != nil {
@@ -663,7 +664,7 @@ func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_mo
}
if review != nil {
- // skip it when reviewer hase been request to review
+ // skip it when reviewer has been request to review
if review.Type == ReviewTypeRequest {
return nil, committer.Commit() // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction.
}
@@ -930,17 +931,19 @@ func MarkConversation(ctx context.Context, comment *Comment, doer *user_model.Us
}
// CanMarkConversation Add or remove Conversation mark for a code comment permission check
-// the PR writer , offfcial reviewer and poster can do it
+// the PR writer , official reviewer and poster can do it
func CanMarkConversation(ctx context.Context, issue *Issue, doer *user_model.User) (permResult bool, err error) {
if doer == nil || issue == nil {
- return false, fmt.Errorf("issue or doer is nil")
+ return false, errors.New("issue or doer is nil")
}
+ if err = issue.LoadRepo(ctx); err != nil {
+ return false, err
+ }
+ if issue.Repo.IsArchived {
+ return false, nil
+ }
if doer.ID != issue.PosterID {
- if err = issue.LoadRepo(ctx); err != nil {
- return false, err
- }
-
p, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
if err != nil {
return false, err
@@ -970,11 +973,11 @@ func DeleteReview(ctx context.Context, r *Review) error {
defer committer.Close()
if r.ID == 0 {
- return fmt.Errorf("review is not allowed to be 0")
+ return errors.New("review is not allowed to be 0")
}
if r.Type == ReviewTypeRequest {
- return fmt.Errorf("review request can not be deleted using this method")
+ return errors.New("review request can not be deleted using this method")
}
opts := FindCommentsOptions{
diff --git a/models/issues/review_list.go b/models/issues/review_list.go
index bc7d7ec0f0..bbb8c489fa 100644
--- a/models/issues/review_list.go
+++ b/models/issues/review_list.go
@@ -5,6 +5,8 @@ package issues
import (
"context"
+ "slices"
+ "sort"
"code.gitea.io/gitea/models/db"
organization_model "code.gitea.io/gitea/models/organization"
@@ -20,7 +22,7 @@ type ReviewList []*Review
// LoadReviewers loads reviewers
func (reviews ReviewList) LoadReviewers(ctx context.Context) error {
reviewerIDs := make([]int64, len(reviews))
- for i := 0; i < len(reviews); i++ {
+ for i := range reviews {
reviewerIDs[i] = reviews[i].ReviewerID
}
reviewers, err := user_model.GetPossibleUserByIDs(ctx, reviewerIDs)
@@ -153,43 +155,60 @@ func CountReviews(ctx context.Context, opts FindReviewOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toCond()).Count(&Review{})
}
-// GetReviewersFromOriginalAuthorsByIssueID gets the latest review of each original authors for a pull request
-func GetReviewersFromOriginalAuthorsByIssueID(ctx context.Context, issueID int64) (ReviewList, error) {
+// GetReviewsByIssueID gets the latest review of each reviewer for a pull request
+// The first returned parameter is the latest review of each individual reviewer or team
+// The second returned parameter is the latest review of each original author which is migrated from other systems
+// The reviews are sorted by updated time
+func GetReviewsByIssueID(ctx context.Context, issueID int64) (latestReviews, migratedOriginalReviews ReviewList, err error) {
reviews := make([]*Review, 0, 10)
- // Get latest review of each reviewer, sorted in order they were made
- if err := db.GetEngine(ctx).SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id = ? AND reviewer_team_id = 0 AND type in (?, ?, ?) AND original_author_id <> 0 GROUP BY issue_id, original_author_id) ORDER BY review.updated_unix ASC",
- issueID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest).
- Find(&reviews); err != nil {
- return nil, err
+ // Get all reviews for the issue id
+ if err := db.GetEngine(ctx).Where("issue_id=?", issueID).OrderBy("updated_unix ASC").Find(&reviews); err != nil {
+ return nil, nil, err
}
- return reviews, nil
-}
-
-// GetReviewsByIssueID gets the latest review of each reviewer for a pull request
-func GetReviewsByIssueID(ctx context.Context, issueID int64) (ReviewList, error) {
- reviews := make([]*Review, 0, 10)
-
- sess := db.GetEngine(ctx)
+ // filter them in memory to get the latest review of each reviewer
+ // Since the reviews should not be too many for one issue, less than 100 commonly, it's acceptable to do this in memory
+ // And since there are too less indexes in review table, it will be very slow to filter in the database
+ reviewersMap := make(map[int64][]*Review) // key is reviewer id
+ originalReviewersMap := make(map[int64][]*Review) // key is original author id
+ reviewTeamsMap := make(map[int64][]*Review) // key is reviewer team id
+ countedReivewTypes := []ReviewType{ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest}
+ for _, review := range reviews {
+ if review.ReviewerTeamID == 0 && slices.Contains(countedReivewTypes, review.Type) && !review.Dismissed {
+ if review.OriginalAuthorID != 0 {
+ originalReviewersMap[review.OriginalAuthorID] = append(originalReviewersMap[review.OriginalAuthorID], review)
+ } else {
+ reviewersMap[review.ReviewerID] = append(reviewersMap[review.ReviewerID], review)
+ }
+ } else if review.ReviewerTeamID != 0 && review.OriginalAuthorID == 0 {
+ reviewTeamsMap[review.ReviewerTeamID] = append(reviewTeamsMap[review.ReviewerTeamID], review)
+ }
+ }
- // Get latest review of each reviewer, sorted in order they were made
- if err := sess.SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id = ? AND reviewer_team_id = 0 AND type in (?, ?, ?) AND dismissed = ? AND original_author_id = 0 GROUP BY issue_id, reviewer_id) ORDER BY review.updated_unix ASC",
- issueID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, false).
- Find(&reviews); err != nil {
- return nil, err
+ individualReviews := make([]*Review, 0, 10)
+ for _, reviews := range reviewersMap {
+ individualReviews = append(individualReviews, reviews[len(reviews)-1])
}
+ sort.Slice(individualReviews, func(i, j int) bool {
+ return individualReviews[i].UpdatedUnix < individualReviews[j].UpdatedUnix
+ })
- teamReviewRequests := make([]*Review, 0, 5)
- if err := sess.SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id = ? AND reviewer_team_id <> 0 AND original_author_id = 0 GROUP BY issue_id, reviewer_team_id) ORDER BY review.updated_unix ASC",
- issueID).
- Find(&teamReviewRequests); err != nil {
- return nil, err
+ originalReviews := make([]*Review, 0, 10)
+ for _, reviews := range originalReviewersMap {
+ originalReviews = append(originalReviews, reviews[len(reviews)-1])
}
+ sort.Slice(originalReviews, func(i, j int) bool {
+ return originalReviews[i].UpdatedUnix < originalReviews[j].UpdatedUnix
+ })
- if len(teamReviewRequests) > 0 {
- reviews = append(reviews, teamReviewRequests...)
+ teamReviewRequests := make([]*Review, 0, 5)
+ for _, reviews := range reviewTeamsMap {
+ teamReviewRequests = append(teamReviewRequests, reviews[len(reviews)-1])
}
+ sort.Slice(teamReviewRequests, func(i, j int) bool {
+ return teamReviewRequests[i].UpdatedUnix < teamReviewRequests[j].UpdatedUnix
+ })
- return reviews, nil
+ return append(individualReviews, teamReviewRequests...), originalReviews, nil
}
diff --git a/models/issues/review_test.go b/models/issues/review_test.go
index 50330e3ff2..2588b8ba41 100644
--- a/models/issues/review_test.go
+++ b/models/issues/review_test.go
@@ -162,8 +162,9 @@ func TestGetReviewersByIssueID(t *testing.T) {
},
)
- allReviews, err := issues_model.GetReviewsByIssueID(db.DefaultContext, issue.ID)
+ allReviews, migratedReviews, err := issues_model.GetReviewsByIssueID(db.DefaultContext, issue.ID)
assert.NoError(t, err)
+ assert.Empty(t, migratedReviews)
for _, review := range allReviews {
assert.NoError(t, review.LoadReviewer(db.DefaultContext))
}
diff --git a/models/issues/stopwatch.go b/models/issues/stopwatch.go
index 629af95b57..761b8f91a0 100644
--- a/models/issues/stopwatch.go
+++ b/models/issues/stopwatch.go
@@ -5,7 +5,6 @@ package issues
import (
"context"
- "fmt"
"time"
"code.gitea.io/gitea/models/db"
@@ -15,20 +14,6 @@ import (
"code.gitea.io/gitea/modules/util"
)
-// ErrIssueStopwatchNotExist represents an error that stopwatch is not exist
-type ErrIssueStopwatchNotExist struct {
- UserID int64
- IssueID int64
-}
-
-func (err ErrIssueStopwatchNotExist) Error() string {
- return fmt.Sprintf("issue stopwatch doesn't exist[uid: %d, issue_id: %d", err.UserID, err.IssueID)
-}
-
-func (err ErrIssueStopwatchNotExist) Unwrap() error {
- return util.ErrNotExist
-}
-
// Stopwatch represents a stopwatch for time tracking.
type Stopwatch struct {
ID int64 `xorm:"pk autoincr"`
@@ -46,11 +31,6 @@ func (s Stopwatch) Seconds() int64 {
return int64(timeutil.TimeStampNow() - s.CreatedUnix)
}
-// Duration returns a human-readable duration string based on local server time
-func (s Stopwatch) Duration() string {
- return util.SecToTime(s.Seconds())
-}
-
func getStopwatch(ctx context.Context, userID, issueID int64) (sw *Stopwatch, exists bool, err error) {
sw = new(Stopwatch)
exists, err = db.GetEngine(ctx).
@@ -60,13 +40,11 @@ func getStopwatch(ctx context.Context, userID, issueID int64) (sw *Stopwatch, ex
return sw, exists, err
}
-// UserIDCount is a simple coalition of UserID and Count
type UserStopwatch struct {
UserID int64
StopWatches []*Stopwatch
}
-// GetUIDsAndNotificationCounts between the two provided times
func GetUIDsAndStopwatch(ctx context.Context) ([]*UserStopwatch, error) {
sws := []*Stopwatch{}
if err := db.GetEngine(ctx).Where("issue_id != 0").Find(&sws); err != nil {
@@ -92,7 +70,7 @@ func GetUIDsAndStopwatch(ctx context.Context) ([]*UserStopwatch, error) {
return res, nil
}
-// GetUserStopwatches return list of all stopwatches of a user
+// GetUserStopwatches return list of the user's all stopwatches
func GetUserStopwatches(ctx context.Context, userID int64, listOptions db.ListOptions) ([]*Stopwatch, error) {
sws := make([]*Stopwatch, 0, 8)
sess := db.GetEngine(ctx).Where("stopwatch.user_id = ?", userID)
@@ -107,7 +85,7 @@ func GetUserStopwatches(ctx context.Context, userID int64, listOptions db.ListOp
return sws, nil
}
-// CountUserStopwatches return count of all stopwatches of a user
+// CountUserStopwatches return count of the user's all stopwatches
func CountUserStopwatches(ctx context.Context, userID int64) (int64, error) {
return db.GetEngine(ctx).Where("user_id = ?", userID).Count(&Stopwatch{})
}
@@ -141,43 +119,21 @@ func HasUserStopwatch(ctx context.Context, userID int64) (exists bool, sw *Stopw
return exists, sw, issue, err
}
-// FinishIssueStopwatchIfPossible if stopwatch exist then finish it otherwise ignore
-func FinishIssueStopwatchIfPossible(ctx context.Context, user *user_model.User, issue *Issue) error {
- _, exists, err := getStopwatch(ctx, user.ID, issue.ID)
- if err != nil {
- return err
- }
- if !exists {
- return nil
- }
- return FinishIssueStopwatch(ctx, user, issue)
-}
-
-// CreateOrStopIssueStopwatch create an issue stopwatch if it's not exist, otherwise finish it
-func CreateOrStopIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error {
- _, exists, err := getStopwatch(ctx, user.ID, issue.ID)
- if err != nil {
- return err
- }
- if exists {
- return FinishIssueStopwatch(ctx, user, issue)
- }
- return CreateIssueStopwatch(ctx, user, issue)
-}
-
-// FinishIssueStopwatch if stopwatch exist then finish it otherwise return an error
-func FinishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error {
+// FinishIssueStopwatch if stopwatch exists, then finish it.
+func FinishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) (ok bool, err error) {
sw, exists, err := getStopwatch(ctx, user.ID, issue.ID)
if err != nil {
- return err
+ return false, err
+ } else if !exists {
+ return false, nil
}
- if !exists {
- return ErrIssueStopwatchNotExist{
- UserID: user.ID,
- IssueID: issue.ID,
- }
+ if err = finishIssueStopwatch(ctx, user, issue, sw); err != nil {
+ return false, err
}
+ return true, nil
+}
+func finishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue, sw *Stopwatch) error {
// Create tracked time out of the time difference between start date and actual date
timediff := time.Now().Unix() - int64(sw.CreatedUnix)
@@ -189,101 +145,81 @@ func FinishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Iss
Time: timediff,
}
- if err := db.Insert(ctx, tt); err != nil {
+ if err := issue.LoadRepo(ctx); err != nil {
return err
}
-
- if err := issue.LoadRepo(ctx); err != nil {
+ if err := db.Insert(ctx, tt); err != nil {
return err
}
-
if _, err := CreateComment(ctx, &CreateCommentOptions{
Doer: user,
Issue: issue,
Repo: issue.Repo,
- Content: util.SecToTime(timediff),
+ Content: util.SecToHours(timediff),
Type: CommentTypeStopTracking,
TimeID: tt.ID,
}); err != nil {
return err
}
- _, err = db.DeleteByBean(ctx, sw)
+ _, err := db.DeleteByBean(ctx, sw)
return err
}
-// CreateIssueStopwatch creates a stopwatch if not exist, otherwise return an error
-func CreateIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error {
- if err := issue.LoadRepo(ctx); err != nil {
- return err
- }
-
- // if another stopwatch is running: stop it
- exists, _, otherIssue, err := HasUserStopwatch(ctx, user.ID)
- if err != nil {
- return err
- }
- if exists {
- if err := FinishIssueStopwatch(ctx, user, otherIssue); err != nil {
- return err
+// CreateIssueStopwatch creates a stopwatch if the issue doesn't have the user's stopwatch.
+// It also stops any other stopwatch that might be running for the user.
+func CreateIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) (ok bool, err error) {
+ { // if another issue's stopwatch is running: stop it; if this issue has a stopwatch: return an error.
+ exists, otherStopWatch, otherIssue, err := HasUserStopwatch(ctx, user.ID)
+ if err != nil {
+ return false, err
+ }
+ if exists {
+ if otherStopWatch.IssueID == issue.ID {
+ // don't allow starting stopwatch for the same issue
+ return false, nil
+ }
+ // stop the other issue's stopwatch
+ if err = finishIssueStopwatch(ctx, user, otherIssue, otherStopWatch); err != nil {
+ return false, err
+ }
}
}
- // Create stopwatch
- sw := &Stopwatch{
- UserID: user.ID,
- IssueID: issue.ID,
- }
-
- if err := db.Insert(ctx, sw); err != nil {
- return err
+ if err = issue.LoadRepo(ctx); err != nil {
+ return false, err
}
-
- if err := issue.LoadRepo(ctx); err != nil {
- return err
+ if err = db.Insert(ctx, &Stopwatch{UserID: user.ID, IssueID: issue.ID}); err != nil {
+ return false, err
}
-
- if _, err := CreateComment(ctx, &CreateCommentOptions{
+ if _, err = CreateComment(ctx, &CreateCommentOptions{
Doer: user,
Issue: issue,
Repo: issue.Repo,
Type: CommentTypeStartTracking,
}); err != nil {
- return err
+ return false, err
}
-
- return nil
+ return true, nil
}
// CancelStopwatch removes the given stopwatch and logs it into issue's timeline.
-func CancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error {
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
- if err := cancelStopwatch(ctx, user, issue); err != nil {
- return err
- }
- return committer.Commit()
-}
-
-func cancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error {
- e := db.GetEngine(ctx)
- sw, exists, err := getStopwatch(ctx, user.ID, issue.ID)
- if err != nil {
- return err
- }
-
- if exists {
- if _, err := e.Delete(sw); err != nil {
+func CancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) (ok bool, err error) {
+ err = db.WithTx(ctx, func(ctx context.Context) error {
+ e := db.GetEngine(ctx)
+ sw, exists, err := getStopwatch(ctx, user.ID, issue.ID)
+ if err != nil {
return err
+ } else if !exists {
+ return nil
}
- if err := issue.LoadRepo(ctx); err != nil {
+ if err = issue.LoadRepo(ctx); err != nil {
return err
}
-
- if _, err := CreateComment(ctx, &CreateCommentOptions{
+ if _, err = e.Delete(sw); err != nil {
+ return err
+ }
+ if _, err = CreateComment(ctx, &CreateCommentOptions{
Doer: user,
Issue: issue,
Repo: issue.Repo,
@@ -291,6 +227,8 @@ func cancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) e
}); err != nil {
return err
}
- }
- return nil
+ ok = true
+ return nil
+ })
+ return ok, err
}
diff --git a/models/issues/stopwatch_test.go b/models/issues/stopwatch_test.go
index a1bf9dc931..6333c10234 100644
--- a/models/issues/stopwatch_test.go
+++ b/models/issues/stopwatch_test.go
@@ -10,7 +10,6 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
)
@@ -18,26 +17,22 @@ import (
func TestCancelStopwatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user1, err := user_model.GetUserByID(db.DefaultContext, 1)
- assert.NoError(t, err)
-
- issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1)
- assert.NoError(t, err)
- issue2, err := issues_model.GetIssueByID(db.DefaultContext, 2)
- assert.NoError(t, err)
+ user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+ issue1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
- err = issues_model.CancelStopwatch(db.DefaultContext, user1, issue1)
+ ok, err := issues_model.CancelStopwatch(db.DefaultContext, user1, issue1)
assert.NoError(t, err)
+ assert.True(t, ok)
unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: user1.ID, IssueID: issue1.ID})
+ unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeCancelTracking, PosterID: user1.ID, IssueID: issue1.ID})
- _ = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeCancelTracking, PosterID: user1.ID, IssueID: issue1.ID})
-
- assert.NoError(t, issues_model.CancelStopwatch(db.DefaultContext, user1, issue2))
+ ok, err = issues_model.CancelStopwatch(db.DefaultContext, user1, issue1)
+ assert.NoError(t, err)
+ assert.False(t, ok)
}
func TestStopwatchExists(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
-
assert.True(t, issues_model.StopwatchExists(db.DefaultContext, 1, 1))
assert.False(t, issues_model.StopwatchExists(db.DefaultContext, 1, 2))
}
@@ -58,21 +53,35 @@ func TestHasUserStopwatch(t *testing.T) {
func TestCreateOrStopIssueStopwatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user2, err := user_model.GetUserByID(db.DefaultContext, 2)
+ user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
+ issue1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
+ issue3 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3})
+
+ // create a new stopwatch
+ ok, err := issues_model.CreateIssueStopwatch(db.DefaultContext, user4, issue1)
assert.NoError(t, err)
- org3, err := user_model.GetUserByID(db.DefaultContext, 3)
+ assert.True(t, ok)
+ unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: user4.ID, IssueID: issue1.ID})
+ // should not create a second stopwatch for the same issue
+ ok, err = issues_model.CreateIssueStopwatch(db.DefaultContext, user4, issue1)
assert.NoError(t, err)
-
- issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1)
+ assert.False(t, ok)
+ // on a different issue, it will finish the existing stopwatch and create a new one
+ ok, err = issues_model.CreateIssueStopwatch(db.DefaultContext, user4, issue3)
assert.NoError(t, err)
- issue2, err := issues_model.GetIssueByID(db.DefaultContext, 2)
+ assert.True(t, ok)
+ unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: user4.ID, IssueID: issue1.ID})
+ unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: user4.ID, IssueID: issue3.ID})
+
+ // user2 already has a stopwatch in test fixture
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
+ ok, err = issues_model.FinishIssueStopwatch(db.DefaultContext, user2, issue2)
assert.NoError(t, err)
-
- assert.NoError(t, issues_model.CreateOrStopIssueStopwatch(db.DefaultContext, org3, issue1))
- sw := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: 3, IssueID: 1})
- assert.LessOrEqual(t, sw.CreatedUnix, timeutil.TimeStampNow())
-
- assert.NoError(t, issues_model.CreateOrStopIssueStopwatch(db.DefaultContext, user2, issue2))
- unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: 2, IssueID: 2})
- unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{UserID: 2, IssueID: 2})
+ assert.True(t, ok)
+ unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: user2.ID, IssueID: issue2.ID})
+ unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{UserID: user2.ID, IssueID: issue2.ID})
+ ok, err = issues_model.FinishIssueStopwatch(db.DefaultContext, user2, issue2)
+ assert.NoError(t, err)
+ assert.False(t, ok)
}
diff --git a/models/issues/tracked_time.go b/models/issues/tracked_time.go
index ea404d36cd..2afbe272ed 100644
--- a/models/issues/tracked_time.go
+++ b/models/issues/tracked_time.go
@@ -350,10 +350,7 @@ func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed
// we get the statistics in smaller chunks and get accumulates
var accum int64
for i := 0; i < len(opts.IssueIDs); {
- chunk := i + MaxQueryParameters
- if chunk > len(opts.IssueIDs) {
- chunk = len(opts.IssueIDs)
- }
+ chunk := min(i+MaxQueryParameters, len(opts.IssueIDs))
time, err := getIssueTotalTrackedTimeChunk(ctx, opts, isClosed, opts.IssueIDs[i:chunk])
if err != nil {
return 0, err
diff --git a/models/migrations/base/db.go b/models/migrations/base/db.go
index eb1c44a79e..479a46379c 100644
--- a/models/migrations/base/db.go
+++ b/models/migrations/base/db.go
@@ -52,7 +52,7 @@ func RecreateTable(sess *xorm.Session, bean any) error {
// TODO: This will not work if there are foreign keys
tableName := sess.Engine().TableName(bean)
- tempTableName := fmt.Sprintf("tmp_recreate__%s", tableName)
+ tempTableName := "tmp_recreate__" + tableName
// We need to move the old table away and create a new one with the correct columns
// We will need to do this in stages to prevent data loss
@@ -82,7 +82,7 @@ func RecreateTable(sess *xorm.Session, bean any) error {
}
newTableColumns := table.Columns()
if len(newTableColumns) == 0 {
- return fmt.Errorf("no columns in new table")
+ return errors.New("no columns in new table")
}
hasID := false
for _, column := range newTableColumns {
@@ -518,7 +518,7 @@ func ModifyColumn(x *xorm.Engine, tableName string, col *schemas.Column) error {
func removeAllWithRetry(dir string) error {
var err error
- for i := 0; i < 20; i++ {
+ for range 20 {
err = os.RemoveAll(dir)
if err == nil {
break
@@ -552,11 +552,11 @@ func deleteDB() error {
}
defer db.Close()
- if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", setting.Database.Name)); err != nil {
+ if _, err = db.Exec("DROP DATABASE IF EXISTS " + setting.Database.Name); err != nil {
return err
}
- if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", setting.Database.Name)); err != nil {
+ if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + setting.Database.Name); err != nil {
return err
}
return nil
@@ -568,11 +568,11 @@ func deleteDB() error {
}
defer db.Close()
- if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", setting.Database.Name)); err != nil {
+ if _, err = db.Exec("DROP DATABASE IF EXISTS " + setting.Database.Name); err != nil {
return err
}
- if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", setting.Database.Name)); err != nil {
+ if _, err = db.Exec("CREATE DATABASE " + setting.Database.Name); err != nil {
return err
}
db.Close()
@@ -594,7 +594,7 @@ func deleteDB() error {
if !schrows.Next() {
// Create and setup a DB schema
- _, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s", setting.Database.Schema))
+ _, err = db.Exec("CREATE SCHEMA " + setting.Database.Schema)
if err != nil {
return err
}
diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go
index fe6de9c517..33fd1df707 100644
--- a/models/migrations/base/tests.go
+++ b/models/migrations/base/tests.go
@@ -1,7 +1,6 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-//nolint:forbidigo
package base
import (
@@ -15,6 +14,7 @@ import (
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/tempdir"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/testlogger"
@@ -105,7 +105,7 @@ func MainTest(m *testing.M) {
giteaConf := os.Getenv("GITEA_CONF")
if giteaConf == "" {
giteaConf = filepath.Join(filepath.Dir(setting.AppPath), "tests/sqlite.ini")
- fmt.Printf("Environment variable $GITEA_CONF not set - defaulting to %s\n", giteaConf)
+ _, _ = fmt.Fprintf(os.Stderr, "Environment variable $GITEA_CONF not set - defaulting to %s\n", giteaConf)
}
if !filepath.IsAbs(giteaConf) {
@@ -114,15 +114,16 @@ func MainTest(m *testing.M) {
setting.CustomConf = giteaConf
}
- tmpDataPath, err := os.MkdirTemp("", "data")
+ tmpDataPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("data")
if err != nil {
testlogger.Fatalf("Unable to create temporary data path %v\n", err)
}
+ defer cleanup()
setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
setting.AppDataPath = tmpDataPath
- unittest.InitSettings()
+ unittest.InitSettingsForTesting()
if err = git.InitFull(context.Background()); err != nil {
testlogger.Fatalf("Unable to InitFull: %v\n", err)
}
@@ -132,10 +133,7 @@ func MainTest(m *testing.M) {
exitStatus := m.Run()
if err := removeAllWithRetry(setting.RepoRootPath); err != nil {
- fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err)
- }
- if err := removeAllWithRetry(tmpDataPath); err != nil {
- fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err)
+ _, _ = fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err)
}
os.Exit(exitStatus)
}
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 52d10c4fe8..176372486e 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -6,6 +6,7 @@ package migrations
import (
"context"
+ "errors"
"fmt"
"code.gitea.io/gitea/models/migrations/v1_10"
@@ -22,6 +23,7 @@ import (
"code.gitea.io/gitea/models/migrations/v1_21"
"code.gitea.io/gitea/models/migrations/v1_22"
"code.gitea.io/gitea/models/migrations/v1_23"
+ "code.gitea.io/gitea/models/migrations/v1_24"
"code.gitea.io/gitea/models/migrations/v1_6"
"code.gitea.io/gitea/models/migrations/v1_7"
"code.gitea.io/gitea/models/migrations/v1_8"
@@ -369,6 +371,17 @@ func prepareMigrationTasks() []*migration {
newMigration(309, "Improve Notification table indices", v1_23.ImproveNotificationTableIndices),
newMigration(310, "Add Priority to ProtectedBranch", v1_23.AddPriorityToProtectedBranch),
newMigration(311, "Add TimeEstimate to Issue table", v1_23.AddTimeEstimateColumnToIssueTable),
+
+ // Gitea 1.23.0-rc0 ends at migration ID number 311 (database version 312)
+ newMigration(312, "Add DeleteBranchAfterMerge to AutoMerge", v1_24.AddDeleteBranchAfterMergeForAutoMerge),
+ newMigration(313, "Move PinOrder from issue table to a new table issue_pin", v1_24.MovePinOrderToTableIssuePin),
+ newMigration(314, "Update OwnerID as zero for repository level action tables", v1_24.UpdateOwnerIDOfRepoLevelActionsTables),
+ newMigration(315, "Add Ephemeral to ActionRunner", v1_24.AddEphemeralToActionRunner),
+ newMigration(316, "Add description for secrets and variables", v1_24.AddDescriptionForSecretsAndVariables),
+ newMigration(317, "Add new index for action for heatmap", v1_24.AddNewIndexForUserDashboard),
+ newMigration(318, "Add anonymous_access_mode for repo_unit", v1_24.AddRepoUnitAnonymousAccessMode),
+ newMigration(319, "Add ExclusiveOrder to Label table", v1_24.AddExclusiveOrderColumnToLabelTable),
+ newMigration(320, "Migrate two_factor_policy to login_source table", v1_24.MigrateSkipTwoFactor),
}
return preparedMigrations
}
@@ -407,14 +420,14 @@ func ExpectedDBVersion() int64 {
}
// EnsureUpToDate will check if the db is at the correct version
-func EnsureUpToDate(x *xorm.Engine) error {
+func EnsureUpToDate(ctx context.Context, x *xorm.Engine) error {
currentDB, err := GetCurrentDBVersion(x)
if err != nil {
return err
}
if currentDB < 0 {
- return fmt.Errorf("database has not been initialized")
+ return errors.New("database has not been initialized")
}
if minDBVersion > currentDB {
diff --git a/models/migrations/migrations_test.go b/models/migrations/migrations_test.go
index e66b015b3d..8649d116f5 100644
--- a/models/migrations/migrations_test.go
+++ b/models/migrations/migrations_test.go
@@ -22,7 +22,7 @@ func TestMigrations(t *testing.T) {
assert.EqualValues(t, 71, migrationIDNumberToDBVersion(70))
- assert.EqualValues(t, []*migration{{idNumber: 70}, {idNumber: 71}}, getPendingMigrations(70, preparedMigrations))
- assert.EqualValues(t, []*migration{{idNumber: 71}}, getPendingMigrations(71, preparedMigrations))
- assert.EqualValues(t, []*migration{}, getPendingMigrations(72, preparedMigrations))
+ assert.Equal(t, []*migration{{idNumber: 70}, {idNumber: 71}}, getPendingMigrations(70, preparedMigrations))
+ assert.Equal(t, []*migration{{idNumber: 71}}, getPendingMigrations(71, preparedMigrations))
+ assert.Equal(t, []*migration{}, getPendingMigrations(72, preparedMigrations))
}
diff --git a/models/migrations/v1_10/v100.go b/models/migrations/v1_10/v100.go
index 5d2fd8e244..1742bea296 100644
--- a/models/migrations/v1_10/v100.go
+++ b/models/migrations/v1_10/v100.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import (
"net/url"
diff --git a/models/migrations/v1_10/v101.go b/models/migrations/v1_10/v101.go
index f023a2a0e7..6c8dfe2486 100644
--- a/models/migrations/v1_10/v101.go
+++ b/models/migrations/v1_10/v101.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_10/v88.go b/models/migrations/v1_10/v88.go
index 7e86ac364f..eb8e81c19e 100644
--- a/models/migrations/v1_10/v88.go
+++ b/models/migrations/v1_10/v88.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import (
"crypto/sha1"
diff --git a/models/migrations/v1_10/v89.go b/models/migrations/v1_10/v89.go
index d5f27ffdc6..0df2a6e17b 100644
--- a/models/migrations/v1_10/v89.go
+++ b/models/migrations/v1_10/v89.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v90.go b/models/migrations/v1_10/v90.go
index 295d4b1c1b..5521a97e32 100644
--- a/models/migrations/v1_10/v90.go
+++ b/models/migrations/v1_10/v90.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v91.go b/models/migrations/v1_10/v91.go
index 48cac2de70..08db6c2742 100644
--- a/models/migrations/v1_10/v91.go
+++ b/models/migrations/v1_10/v91.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v92.go b/models/migrations/v1_10/v92.go
index 9080108594..b6c04a9234 100644
--- a/models/migrations/v1_10/v92.go
+++ b/models/migrations/v1_10/v92.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import (
"xorm.io/builder"
diff --git a/models/migrations/v1_10/v93.go b/models/migrations/v1_10/v93.go
index ee59a8db39..c131be9a8d 100644
--- a/models/migrations/v1_10/v93.go
+++ b/models/migrations/v1_10/v93.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v94.go b/models/migrations/v1_10/v94.go
index c131af162b..13b7d7b303 100644
--- a/models/migrations/v1_10/v94.go
+++ b/models/migrations/v1_10/v94.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v95.go b/models/migrations/v1_10/v95.go
index 3b1f67fd9c..86b52026bf 100644
--- a/models/migrations/v1_10/v95.go
+++ b/models/migrations/v1_10/v95.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v96.go b/models/migrations/v1_10/v96.go
index 34c8240031..ca35a169c4 100644
--- a/models/migrations/v1_10/v96.go
+++ b/models/migrations/v1_10/v96.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import (
"path/filepath"
diff --git a/models/migrations/v1_10/v97.go b/models/migrations/v1_10/v97.go
index dee45b32e3..5872bb63e5 100644
--- a/models/migrations/v1_10/v97.go
+++ b/models/migrations/v1_10/v97.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v98.go b/models/migrations/v1_10/v98.go
index bdd9aed089..d21c326459 100644
--- a/models/migrations/v1_10/v98.go
+++ b/models/migrations/v1_10/v98.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import "xorm.io/xorm"
diff --git a/models/migrations/v1_10/v99.go b/models/migrations/v1_10/v99.go
index ebe6597f7c..223c188057 100644
--- a/models/migrations/v1_10/v99.go
+++ b/models/migrations/v1_10/v99.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_10 //nolint
+package v1_10
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_11/v102.go b/models/migrations/v1_11/v102.go
index 9358e4cef3..e52290afb0 100644
--- a/models/migrations/v1_11/v102.go
+++ b/models/migrations/v1_11/v102.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_11/v103.go b/models/migrations/v1_11/v103.go
index 53527dac58..a515710160 100644
--- a/models/migrations/v1_11/v103.go
+++ b/models/migrations/v1_11/v103.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v104.go b/models/migrations/v1_11/v104.go
index 3e8ee64bc1..3b0d3c64b2 100644
--- a/models/migrations/v1_11/v104.go
+++ b/models/migrations/v1_11/v104.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_11/v105.go b/models/migrations/v1_11/v105.go
index b91340c30a..d86973a0f6 100644
--- a/models/migrations/v1_11/v105.go
+++ b/models/migrations/v1_11/v105.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v106.go b/models/migrations/v1_11/v106.go
index ecb11cdd1e..edffe18683 100644
--- a/models/migrations/v1_11/v106.go
+++ b/models/migrations/v1_11/v106.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v107.go b/models/migrations/v1_11/v107.go
index f0bfe5862c..a158e3bb50 100644
--- a/models/migrations/v1_11/v107.go
+++ b/models/migrations/v1_11/v107.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v108.go b/models/migrations/v1_11/v108.go
index a85096234d..8f14504ceb 100644
--- a/models/migrations/v1_11/v108.go
+++ b/models/migrations/v1_11/v108.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v109.go b/models/migrations/v1_11/v109.go
index ea565ccda3..f7616aec7b 100644
--- a/models/migrations/v1_11/v109.go
+++ b/models/migrations/v1_11/v109.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v110.go b/models/migrations/v1_11/v110.go
index 81afa1331d..512f728c03 100644
--- a/models/migrations/v1_11/v110.go
+++ b/models/migrations/v1_11/v110.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_11/v111.go b/models/migrations/v1_11/v111.go
index ff108479a9..c27465f051 100644
--- a/models/migrations/v1_11/v111.go
+++ b/models/migrations/v1_11/v111.go
@@ -1,10 +1,11 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"fmt"
+ "slices"
"xorm.io/xorm"
)
@@ -344,10 +345,8 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
}
return AccessModeWrite <= perm.UnitsMode[UnitTypeCode], nil
}
- for _, id := range protectedBranch.ApprovalsWhitelistUserIDs {
- if id == reviewer.ID {
- return true, nil
- }
+ if slices.Contains(protectedBranch.ApprovalsWhitelistUserIDs, reviewer.ID) {
+ return true, nil
}
// isUserInTeams
diff --git a/models/migrations/v1_11/v112.go b/models/migrations/v1_11/v112.go
index 0857663119..fe45cf9222 100644
--- a/models/migrations/v1_11/v112.go
+++ b/models/migrations/v1_11/v112.go
@@ -1,12 +1,12 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
- "fmt"
"path/filepath"
+ "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@@ -31,7 +31,7 @@ func RemoveAttachmentMissedRepo(x *xorm.Engine) error {
for i := 0; i < len(attachments); i++ {
uuid := attachments[i].UUID
if err = util.RemoveAll(filepath.Join(setting.Attachment.Storage.Path, uuid[0:1], uuid[1:2], uuid)); err != nil {
- fmt.Printf("Error: %v", err) //nolint:forbidigo
+ log.Warn("Unable to remove attachment file by UUID %s: %v", uuid, err)
}
}
diff --git a/models/migrations/v1_11/v113.go b/models/migrations/v1_11/v113.go
index dea344a44f..a4d54f66fb 100644
--- a/models/migrations/v1_11/v113.go
+++ b/models/migrations/v1_11/v113.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"fmt"
diff --git a/models/migrations/v1_11/v114.go b/models/migrations/v1_11/v114.go
index 95adcee989..9467a8a90c 100644
--- a/models/migrations/v1_11/v114.go
+++ b/models/migrations/v1_11/v114.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"net/url"
diff --git a/models/migrations/v1_11/v115.go b/models/migrations/v1_11/v115.go
index 8c631cfd0b..5933c0520f 100644
--- a/models/migrations/v1_11/v115.go
+++ b/models/migrations/v1_11/v115.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"crypto/md5"
@@ -146,7 +146,7 @@ func copyOldAvatarToNewLocation(userID int64, oldAvatar string) (string, error)
return "", fmt.Errorf("io.ReadAll: %w", err)
}
- newAvatar := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", userID, md5.Sum(data)))))
+ newAvatar := fmt.Sprintf("%x", md5.Sum(fmt.Appendf(nil, "%d-%x", userID, md5.Sum(data))))
if newAvatar == oldAvatar {
return newAvatar, nil
}
diff --git a/models/migrations/v1_11/v116.go b/models/migrations/v1_11/v116.go
index 85aa76c1e0..729fbad18b 100644
--- a/models/migrations/v1_11/v116.go
+++ b/models/migrations/v1_11/v116.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_11 //nolint
+package v1_11
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v117.go b/models/migrations/v1_12/v117.go
index 8eadcdef2b..73b58ca34b 100644
--- a/models/migrations/v1_12/v117.go
+++ b/models/migrations/v1_12/v117.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v118.go b/models/migrations/v1_12/v118.go
index eb022dc5e4..e8b4249743 100644
--- a/models/migrations/v1_12/v118.go
+++ b/models/migrations/v1_12/v118.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v119.go b/models/migrations/v1_12/v119.go
index 60bfe6a57d..b4bf29a935 100644
--- a/models/migrations/v1_12/v119.go
+++ b/models/migrations/v1_12/v119.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v120.go b/models/migrations/v1_12/v120.go
index 3f7ed8d373..14d515f5a7 100644
--- a/models/migrations/v1_12/v120.go
+++ b/models/migrations/v1_12/v120.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v121.go b/models/migrations/v1_12/v121.go
index 175ec9164d..a28ae4e1c9 100644
--- a/models/migrations/v1_12/v121.go
+++ b/models/migrations/v1_12/v121.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import "xorm.io/xorm"
diff --git a/models/migrations/v1_12/v122.go b/models/migrations/v1_12/v122.go
index 6e31d863a1..bc1b175f6a 100644
--- a/models/migrations/v1_12/v122.go
+++ b/models/migrations/v1_12/v122.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v123.go b/models/migrations/v1_12/v123.go
index b0c3af07a3..52b10bb850 100644
--- a/models/migrations/v1_12/v123.go
+++ b/models/migrations/v1_12/v123.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v124.go b/models/migrations/v1_12/v124.go
index d2ba03ffe0..9a93f436d4 100644
--- a/models/migrations/v1_12/v124.go
+++ b/models/migrations/v1_12/v124.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v125.go b/models/migrations/v1_12/v125.go
index ec4ffaab25..7f582ecff5 100644
--- a/models/migrations/v1_12/v125.go
+++ b/models/migrations/v1_12/v125.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
diff --git a/models/migrations/v1_12/v126.go b/models/migrations/v1_12/v126.go
index ca9ec3aa3f..64fd7f7478 100644
--- a/models/migrations/v1_12/v126.go
+++ b/models/migrations/v1_12/v126.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/builder"
diff --git a/models/migrations/v1_12/v127.go b/models/migrations/v1_12/v127.go
index 00e391dc87..9bd78db95e 100644
--- a/models/migrations/v1_12/v127.go
+++ b/models/migrations/v1_12/v127.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
diff --git a/models/migrations/v1_12/v128.go b/models/migrations/v1_12/v128.go
index 44d44a26c5..e7dbff3766 100644
--- a/models/migrations/v1_12/v128.go
+++ b/models/migrations/v1_12/v128.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
@@ -82,17 +82,17 @@ func FixMergeBase(x *xorm.Engine) error {
if !pr.HasMerged {
var err error
- pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).RunStdString(&git.RunOpts{Dir: repoPath})
+ pr.MergeBase, _, err = git.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
if err != nil {
var err2 error
- pr.MergeBase, _, err2 = git.NewCommand(git.DefaultContext, "rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).RunStdString(&git.RunOpts{Dir: repoPath})
+ pr.MergeBase, _, err2 = git.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix+pr.BaseBranch).RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
if err2 != nil {
log.Error("Unable to get merge base for PR ID %d, Index %d in %s/%s. Error: %v & %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err, err2)
continue
}
}
} else {
- parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
+ parentsString, _, err := git.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
if err != nil {
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue
@@ -104,9 +104,9 @@ func FixMergeBase(x *xorm.Engine) error {
refs := append([]string{}, parents[1:]...)
refs = append(refs, gitRefName)
- cmd := git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(refs...)
+ cmd := git.NewCommand("merge-base").AddDashesAndList(refs...)
- pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
+ pr.MergeBase, _, err = cmd.RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
if err != nil {
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue
diff --git a/models/migrations/v1_12/v129.go b/models/migrations/v1_12/v129.go
index cf228242b9..3e4d3aca68 100644
--- a/models/migrations/v1_12/v129.go
+++ b/models/migrations/v1_12/v129.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v130.go b/models/migrations/v1_12/v130.go
index 391810c7ca..107bb756fd 100644
--- a/models/migrations/v1_12/v130.go
+++ b/models/migrations/v1_12/v130.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"code.gitea.io/gitea/modules/json"
diff --git a/models/migrations/v1_12/v131.go b/models/migrations/v1_12/v131.go
index 5184bc3590..1266c2f185 100644
--- a/models/migrations/v1_12/v131.go
+++ b/models/migrations/v1_12/v131.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
diff --git a/models/migrations/v1_12/v132.go b/models/migrations/v1_12/v132.go
index 3b2b28f7ab..8b1ae6db93 100644
--- a/models/migrations/v1_12/v132.go
+++ b/models/migrations/v1_12/v132.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
diff --git a/models/migrations/v1_12/v133.go b/models/migrations/v1_12/v133.go
index c9087fc8c1..69e20597d8 100644
--- a/models/migrations/v1_12/v133.go
+++ b/models/migrations/v1_12/v133.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import "xorm.io/xorm"
diff --git a/models/migrations/v1_12/v134.go b/models/migrations/v1_12/v134.go
index 3d1c82f09e..09d743964d 100644
--- a/models/migrations/v1_12/v134.go
+++ b/models/migrations/v1_12/v134.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
@@ -79,7 +79,7 @@ func RefixMergeBase(x *xorm.Engine) error {
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
- parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
+ parentsString, _, err := git.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
if err != nil {
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue
@@ -92,9 +92,9 @@ func RefixMergeBase(x *xorm.Engine) error {
// we should recalculate
refs := append([]string{}, parents[1:]...)
refs = append(refs, gitRefName)
- cmd := git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(refs...)
+ cmd := git.NewCommand("merge-base").AddDashesAndList(refs...)
- pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
+ pr.MergeBase, _, err = cmd.RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
if err != nil {
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue
diff --git a/models/migrations/v1_12/v135.go b/models/migrations/v1_12/v135.go
index 8898011df5..5df0ad7fc4 100644
--- a/models/migrations/v1_12/v135.go
+++ b/models/migrations/v1_12/v135.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
diff --git a/models/migrations/v1_12/v136.go b/models/migrations/v1_12/v136.go
index d91ff92feb..0f53278b46 100644
--- a/models/migrations/v1_12/v136.go
+++ b/models/migrations/v1_12/v136.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
diff --git a/models/migrations/v1_12/v137.go b/models/migrations/v1_12/v137.go
index 0d86b72010..9d38483488 100644
--- a/models/migrations/v1_12/v137.go
+++ b/models/migrations/v1_12/v137.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_12/v138.go b/models/migrations/v1_12/v138.go
index 8c8d353f40..4485adeb2d 100644
--- a/models/migrations/v1_12/v138.go
+++ b/models/migrations/v1_12/v138.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"fmt"
diff --git a/models/migrations/v1_12/v139.go b/models/migrations/v1_12/v139.go
index 279aa7df87..a3799841ac 100644
--- a/models/migrations/v1_12/v139.go
+++ b/models/migrations/v1_12/v139.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_12 //nolint
+package v1_12
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_13/v140.go b/models/migrations/v1_13/v140.go
index 2d3337012d..a9a047bca9 100644
--- a/models/migrations/v1_13/v140.go
+++ b/models/migrations/v1_13/v140.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"fmt"
@@ -21,12 +21,7 @@ func FixLanguageStatsToSaveSize(x *xorm.Engine) error {
// RepoIndexerType specifies the repository indexer type
type RepoIndexerType int
- const (
- // RepoIndexerTypeCode code indexer - 0
- RepoIndexerTypeCode RepoIndexerType = iota //nolint:unused
- // RepoIndexerTypeStats repository stats indexer - 1
- RepoIndexerTypeStats
- )
+ const RepoIndexerTypeStats RepoIndexerType = 1
// RepoIndexerStatus see models/repo_indexer.go
type RepoIndexerStatus struct {
@@ -46,7 +41,7 @@ func FixLanguageStatsToSaveSize(x *xorm.Engine) error {
}
// Delete language stats
- if _, err := x.Exec(fmt.Sprintf("%s language_stat", truncExpr)); err != nil {
+ if _, err := x.Exec(truncExpr + " language_stat"); err != nil {
return err
}
diff --git a/models/migrations/v1_13/v141.go b/models/migrations/v1_13/v141.go
index ae211e0e44..b54bc1727c 100644
--- a/models/migrations/v1_13/v141.go
+++ b/models/migrations/v1_13/v141.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"fmt"
diff --git a/models/migrations/v1_13/v142.go b/models/migrations/v1_13/v142.go
index 7c7c01ad47..d08a0ae0bf 100644
--- a/models/migrations/v1_13/v142.go
+++ b/models/migrations/v1_13/v142.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_13/v143.go b/models/migrations/v1_13/v143.go
index 885768dff3..b9a856ed0f 100644
--- a/models/migrations/v1_13/v143.go
+++ b/models/migrations/v1_13/v143.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_13/v144.go b/models/migrations/v1_13/v144.go
index f5a0bc5751..9352d78bc8 100644
--- a/models/migrations/v1_13/v144.go
+++ b/models/migrations/v1_13/v144.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_13/v145.go b/models/migrations/v1_13/v145.go
index 8acb29bf33..86ebb4f9d9 100644
--- a/models/migrations/v1_13/v145.go
+++ b/models/migrations/v1_13/v145.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"fmt"
@@ -42,7 +42,7 @@ func IncreaseLanguageField(x *xorm.Engine) error {
switch {
case setting.Database.Type.IsMySQL():
- if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE language_stat MODIFY COLUMN language %s", sqlType)); err != nil {
+ if _, err := sess.Exec("ALTER TABLE language_stat MODIFY COLUMN language " + sqlType); err != nil {
return err
}
case setting.Database.Type.IsMSSQL():
@@ -64,7 +64,7 @@ func IncreaseLanguageField(x *xorm.Engine) error {
return fmt.Errorf("Drop table `language_stat` constraint `%s`: %w", constraint, err)
}
}
- if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE language_stat ALTER COLUMN language %s", sqlType)); err != nil {
+ if _, err := sess.Exec("ALTER TABLE language_stat ALTER COLUMN language " + sqlType); err != nil {
return err
}
// Finally restore the constraint
@@ -72,7 +72,7 @@ func IncreaseLanguageField(x *xorm.Engine) error {
return err
}
case setting.Database.Type.IsPostgreSQL():
- if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE language_stat ALTER COLUMN language TYPE %s", sqlType)); err != nil {
+ if _, err := sess.Exec("ALTER TABLE language_stat ALTER COLUMN language TYPE " + sqlType); err != nil {
return err
}
}
diff --git a/models/migrations/v1_13/v146.go b/models/migrations/v1_13/v146.go
index 7d9a878704..355c772c26 100644
--- a/models/migrations/v1_13/v146.go
+++ b/models/migrations/v1_13/v146.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_13/v147.go b/models/migrations/v1_13/v147.go
index 510ef39b28..0059c06220 100644
--- a/models/migrations/v1_13/v147.go
+++ b/models/migrations/v1_13/v147.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_13/v148.go b/models/migrations/v1_13/v148.go
index 7bb8ab700b..d276db3d61 100644
--- a/models/migrations/v1_13/v148.go
+++ b/models/migrations/v1_13/v148.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_13/v149.go b/models/migrations/v1_13/v149.go
index 2a1db04cbb..a96b8e5ac7 100644
--- a/models/migrations/v1_13/v149.go
+++ b/models/migrations/v1_13/v149.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"fmt"
diff --git a/models/migrations/v1_13/v150.go b/models/migrations/v1_13/v150.go
index d5ba489566..590ea72903 100644
--- a/models/migrations/v1_13/v150.go
+++ b/models/migrations/v1_13/v150.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_13/v151.go b/models/migrations/v1_13/v151.go
index 25af1d03ec..454929534f 100644
--- a/models/migrations/v1_13/v151.go
+++ b/models/migrations/v1_13/v151.go
@@ -1,10 +1,11 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"context"
+ "errors"
"fmt"
"strings"
@@ -113,7 +114,7 @@ func SetDefaultPasswordToArgon2(x *xorm.Engine) error {
newTableColumns := table.Columns()
if len(newTableColumns) == 0 {
- return fmt.Errorf("no columns in new table")
+ return errors.New("no columns in new table")
}
hasID := false
for _, column := range newTableColumns {
diff --git a/models/migrations/v1_13/v152.go b/models/migrations/v1_13/v152.go
index 502c82a40d..648e26446f 100644
--- a/models/migrations/v1_13/v152.go
+++ b/models/migrations/v1_13/v152.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import "xorm.io/xorm"
diff --git a/models/migrations/v1_13/v153.go b/models/migrations/v1_13/v153.go
index 0b2dd3eb62..e5462fc162 100644
--- a/models/migrations/v1_13/v153.go
+++ b/models/migrations/v1_13/v153.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_13/v154.go b/models/migrations/v1_13/v154.go
index 60cc56713e..5477d1b889 100644
--- a/models/migrations/v1_13/v154.go
+++ b/models/migrations/v1_13/v154.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_13 //nolint
+package v1_13
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_14/main_test.go b/models/migrations/v1_14/main_test.go
index 7a091b9b9a..978f88577c 100644
--- a/models/migrations/v1_14/main_test.go
+++ b/models/migrations/v1_14/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"testing"
diff --git a/models/migrations/v1_14/v155.go b/models/migrations/v1_14/v155.go
index e814f59938..505a9ae033 100644
--- a/models/migrations/v1_14/v155.go
+++ b/models/migrations/v1_14/v155.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v156.go b/models/migrations/v1_14/v156.go
index 2cf4954a15..2fa5819610 100644
--- a/models/migrations/v1_14/v156.go
+++ b/models/migrations/v1_14/v156.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v157.go b/models/migrations/v1_14/v157.go
index 7187278d29..2c5625ebbd 100644
--- a/models/migrations/v1_14/v157.go
+++ b/models/migrations/v1_14/v157.go
@@ -1,24 +1,13 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"xorm.io/xorm"
)
func FixRepoTopics(x *xorm.Engine) error {
- type Topic struct { //nolint:unused
- ID int64 `xorm:"pk autoincr"`
- Name string `xorm:"UNIQUE VARCHAR(25)"`
- RepoCount int
- }
-
- type RepoTopic struct { //nolint:unused
- RepoID int64 `xorm:"pk"`
- TopicID int64 `xorm:"pk"`
- }
-
type Repository struct {
ID int64 `xorm:"pk autoincr"`
Topics []string `xorm:"TEXT JSON"`
diff --git a/models/migrations/v1_14/v158.go b/models/migrations/v1_14/v158.go
index 1094d8abf7..3c57e8e3da 100644
--- a/models/migrations/v1_14/v158.go
+++ b/models/migrations/v1_14/v158.go
@@ -1,10 +1,10 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
- "fmt"
+ "errors"
"strconv"
"code.gitea.io/gitea/modules/log"
@@ -82,7 +82,7 @@ func UpdateCodeCommentReplies(x *xorm.Engine) error {
sqlCmd = "SELECT TOP " + strconv.Itoa(batchSize) + " * FROM #temp_comments WHERE " +
"(id NOT IN ( SELECT TOP " + strconv.Itoa(start) + " id FROM #temp_comments ORDER BY id )) ORDER BY id"
default:
- return fmt.Errorf("Unsupported database type")
+ return errors.New("Unsupported database type")
}
if err := sess.SQL(sqlCmd).Find(&comments); err != nil {
diff --git a/models/migrations/v1_14/v159.go b/models/migrations/v1_14/v159.go
index 149ae0f6a8..e6f6f0f061 100644
--- a/models/migrations/v1_14/v159.go
+++ b/models/migrations/v1_14/v159.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_14/v160.go b/models/migrations/v1_14/v160.go
index 4dea91b514..73f3798954 100644
--- a/models/migrations/v1_14/v160.go
+++ b/models/migrations/v1_14/v160.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_14/v161.go b/models/migrations/v1_14/v161.go
index ac7e821a80..eb92dee77c 100644
--- a/models/migrations/v1_14/v161.go
+++ b/models/migrations/v1_14/v161.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"context"
diff --git a/models/migrations/v1_14/v162.go b/models/migrations/v1_14/v162.go
index 2e4e0b8eb0..a0ddd36d55 100644
--- a/models/migrations/v1_14/v162.go
+++ b/models/migrations/v1_14/v162.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_14/v163.go b/models/migrations/v1_14/v163.go
index 0cd8ba68c8..84c35190b7 100644
--- a/models/migrations/v1_14/v163.go
+++ b/models/migrations/v1_14/v163.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_14/v164.go b/models/migrations/v1_14/v164.go
index 54f6951427..d2fd9b8464 100644
--- a/models/migrations/v1_14/v164.go
+++ b/models/migrations/v1_14/v164.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v165.go b/models/migrations/v1_14/v165.go
index 926350cdf7..6e1b34156b 100644
--- a/models/migrations/v1_14/v165.go
+++ b/models/migrations/v1_14/v165.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"code.gitea.io/gitea/models/migrations/base"
@@ -16,10 +16,7 @@ func ConvertHookTaskTypeToVarcharAndTrim(x *xorm.Engine) error {
return nil
}
- type HookTask struct { //nolint:unused
- Typ string `xorm:"VARCHAR(16) index"`
- }
-
+ // HookTask: Typ string `xorm:"VARCHAR(16) index"`
if err := base.ModifyColumn(x, "hook_task", &schemas.Column{
Name: "typ",
SQLType: schemas.SQLType{
@@ -42,10 +39,7 @@ func ConvertHookTaskTypeToVarcharAndTrim(x *xorm.Engine) error {
return err
}
- type Webhook struct { //nolint:unused
- Type string `xorm:"VARCHAR(16) index"`
- }
-
+ // Webhook: Type string `xorm:"VARCHAR(16) index"`
if err := base.ModifyColumn(x, "webhook", &schemas.Column{
Name: "type",
SQLType: schemas.SQLType{
diff --git a/models/migrations/v1_14/v166.go b/models/migrations/v1_14/v166.go
index e5731582fd..4c106bd7da 100644
--- a/models/migrations/v1_14/v166.go
+++ b/models/migrations/v1_14/v166.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"crypto/sha256"
diff --git a/models/migrations/v1_14/v167.go b/models/migrations/v1_14/v167.go
index 9d416f6a32..d77bbc401e 100644
--- a/models/migrations/v1_14/v167.go
+++ b/models/migrations/v1_14/v167.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v168.go b/models/migrations/v1_14/v168.go
index a30a8859f7..aa93eec19b 100644
--- a/models/migrations/v1_14/v168.go
+++ b/models/migrations/v1_14/v168.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import "xorm.io/xorm"
diff --git a/models/migrations/v1_14/v169.go b/models/migrations/v1_14/v169.go
index 5b81bb58b1..4f9df0d96f 100644
--- a/models/migrations/v1_14/v169.go
+++ b/models/migrations/v1_14/v169.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_14/v170.go b/models/migrations/v1_14/v170.go
index 7b6498a3e9..a2ff4623e1 100644
--- a/models/migrations/v1_14/v170.go
+++ b/models/migrations/v1_14/v170.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v171.go b/models/migrations/v1_14/v171.go
index 51a35a02ad..7b200e960a 100644
--- a/models/migrations/v1_14/v171.go
+++ b/models/migrations/v1_14/v171.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v172.go b/models/migrations/v1_14/v172.go
index 0f9bef902a..bbd61d87b2 100644
--- a/models/migrations/v1_14/v172.go
+++ b/models/migrations/v1_14/v172.go
@@ -1,7 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_14/v173.go b/models/migrations/v1_14/v173.go
index 2d9eee9197..7752fbe966 100644
--- a/models/migrations/v1_14/v173.go
+++ b/models/migrations/v1_14/v173.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v174.go b/models/migrations/v1_14/v174.go
index c839e15db8..4049e43070 100644
--- a/models/migrations/v1_14/v174.go
+++ b/models/migrations/v1_14/v174.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v175.go b/models/migrations/v1_14/v175.go
index 70d72b2600..92ed130473 100644
--- a/models/migrations/v1_14/v175.go
+++ b/models/migrations/v1_14/v175.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v176.go b/models/migrations/v1_14/v176.go
index 1ed49f75fa..ef5dce9a02 100644
--- a/models/migrations/v1_14/v176.go
+++ b/models/migrations/v1_14/v176.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_14/v176_test.go b/models/migrations/v1_14/v176_test.go
index ea3e750d7f..5c1db4db71 100644
--- a/models/migrations/v1_14/v176_test.go
+++ b/models/migrations/v1_14/v176_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"testing"
diff --git a/models/migrations/v1_14/v177.go b/models/migrations/v1_14/v177.go
index 6e1838f369..96676bf8d9 100644
--- a/models/migrations/v1_14/v177.go
+++ b/models/migrations/v1_14/v177.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"fmt"
diff --git a/models/migrations/v1_14/v177_test.go b/models/migrations/v1_14/v177_test.go
index 5568a18fec..263f69f338 100644
--- a/models/migrations/v1_14/v177_test.go
+++ b/models/migrations/v1_14/v177_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_14 //nolint
+package v1_14
import (
"testing"
diff --git a/models/migrations/v1_15/main_test.go b/models/migrations/v1_15/main_test.go
index 366f19788e..d01585e997 100644
--- a/models/migrations/v1_15/main_test.go
+++ b/models/migrations/v1_15/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"testing"
diff --git a/models/migrations/v1_15/v178.go b/models/migrations/v1_15/v178.go
index 6d236eb049..ca3a5c262e 100644
--- a/models/migrations/v1_15/v178.go
+++ b/models/migrations/v1_15/v178.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_15/v179.go b/models/migrations/v1_15/v179.go
index f6b142eb42..d6fb86ffec 100644
--- a/models/migrations/v1_15/v179.go
+++ b/models/migrations/v1_15/v179.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_15/v180.go b/models/migrations/v1_15/v180.go
index c71e771861..dd132f8330 100644
--- a/models/migrations/v1_15/v180.go
+++ b/models/migrations/v1_15/v180.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"code.gitea.io/gitea/modules/json"
diff --git a/models/migrations/v1_15/v181.go b/models/migrations/v1_15/v181.go
index 2185ed0213..fb1d3d7a75 100644
--- a/models/migrations/v1_15/v181.go
+++ b/models/migrations/v1_15/v181.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"strings"
diff --git a/models/migrations/v1_15/v181_test.go b/models/migrations/v1_15/v181_test.go
index 1b075be7a0..73b5c1f3d6 100644
--- a/models/migrations/v1_15/v181_test.go
+++ b/models/migrations/v1_15/v181_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"strings"
@@ -49,7 +49,7 @@ func Test_AddPrimaryEmail2EmailAddress(t *testing.T) {
assert.NoError(t, err)
assert.True(t, has)
assert.True(t, emailAddress.IsPrimary)
- assert.EqualValues(t, user.IsActive, emailAddress.IsActivated)
- assert.EqualValues(t, user.ID, emailAddress.UID)
+ assert.Equal(t, user.IsActive, emailAddress.IsActivated)
+ assert.Equal(t, user.ID, emailAddress.UID)
}
}
diff --git a/models/migrations/v1_15/v182.go b/models/migrations/v1_15/v182.go
index 9ca500c0f9..f53ff11df9 100644
--- a/models/migrations/v1_15/v182.go
+++ b/models/migrations/v1_15/v182.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_15/v182_test.go b/models/migrations/v1_15/v182_test.go
index 75ef8e1cd8..5fc6a0c467 100644
--- a/models/migrations/v1_15/v182_test.go
+++ b/models/migrations/v1_15/v182_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"testing"
diff --git a/models/migrations/v1_15/v183.go b/models/migrations/v1_15/v183.go
index effad1b467..5d0582f03d 100644
--- a/models/migrations/v1_15/v183.go
+++ b/models/migrations/v1_15/v183.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"fmt"
diff --git a/models/migrations/v1_15/v184.go b/models/migrations/v1_15/v184.go
index 4b3dd1467a..2823bc1f7a 100644
--- a/models/migrations/v1_15/v184.go
+++ b/models/migrations/v1_15/v184.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"context"
diff --git a/models/migrations/v1_15/v185.go b/models/migrations/v1_15/v185.go
index e5878ec193..60af59edca 100644
--- a/models/migrations/v1_15/v185.go
+++ b/models/migrations/v1_15/v185.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_15/v186.go b/models/migrations/v1_15/v186.go
index 01aab3add5..67dc97d13d 100644
--- a/models/migrations/v1_15/v186.go
+++ b/models/migrations/v1_15/v186.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_15/v187.go b/models/migrations/v1_15/v187.go
index 21cd6772b7..5fd90c65fb 100644
--- a/models/migrations/v1_15/v187.go
+++ b/models/migrations/v1_15/v187.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_15/v188.go b/models/migrations/v1_15/v188.go
index 71e45cab0e..4494e6ff05 100644
--- a/models/migrations/v1_15/v188.go
+++ b/models/migrations/v1_15/v188.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_15 //nolint
+package v1_15
import "xorm.io/xorm"
diff --git a/models/migrations/v1_16/main_test.go b/models/migrations/v1_16/main_test.go
index 817a0c13a4..7f93d6e9e5 100644
--- a/models/migrations/v1_16/main_test.go
+++ b/models/migrations/v1_16/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"testing"
diff --git a/models/migrations/v1_16/v189.go b/models/migrations/v1_16/v189.go
index 5649645051..6bc99e58ab 100644
--- a/models/migrations/v1_16/v189.go
+++ b/models/migrations/v1_16/v189.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"encoding/binary"
diff --git a/models/migrations/v1_16/v189_test.go b/models/migrations/v1_16/v189_test.go
index 32ef821d27..fb56ac8e11 100644
--- a/models/migrations/v1_16/v189_test.go
+++ b/models/migrations/v1_16/v189_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"testing"
@@ -75,8 +75,8 @@ func Test_UnwrapLDAPSourceCfg(t *testing.T) {
return
}
- assert.EqualValues(t, expected, converted, "UnwrapLDAPSourceCfg failed for %d", source.ID)
- assert.EqualValues(t, source.ID%2 == 0, source.IsActive, "UnwrapLDAPSourceCfg failed for %d", source.ID)
+ assert.Equal(t, expected, converted, "UnwrapLDAPSourceCfg failed for %d", source.ID)
+ assert.Equal(t, source.ID%2 == 0, source.IsActive, "UnwrapLDAPSourceCfg failed for %d", source.ID)
}
}
}
diff --git a/models/migrations/v1_16/v190.go b/models/migrations/v1_16/v190.go
index 5953802849..1eb6b6ddb4 100644
--- a/models/migrations/v1_16/v190.go
+++ b/models/migrations/v1_16/v190.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v191.go b/models/migrations/v1_16/v191.go
index c618783c08..957c82e484 100644
--- a/models/migrations/v1_16/v191.go
+++ b/models/migrations/v1_16/v191.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_16/v192.go b/models/migrations/v1_16/v192.go
index 2d5d158a09..9d03fbe3c8 100644
--- a/models/migrations/v1_16/v192.go
+++ b/models/migrations/v1_16/v192.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_16/v193.go b/models/migrations/v1_16/v193.go
index 8d3ce7a558..a5af2de380 100644
--- a/models/migrations/v1_16/v193.go
+++ b/models/migrations/v1_16/v193.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v193_test.go b/models/migrations/v1_16/v193_test.go
index b279967a2c..2e827f0550 100644
--- a/models/migrations/v1_16/v193_test.go
+++ b/models/migrations/v1_16/v193_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"testing"
@@ -62,7 +62,7 @@ func Test_AddRepoIDForAttachment(t *testing.T) {
has, err := x.ID(attach.IssueID).Get(&issue)
assert.NoError(t, err)
assert.True(t, has)
- assert.EqualValues(t, attach.RepoID, issue.RepoID)
+ assert.Equal(t, attach.RepoID, issue.RepoID)
}
var releaseAttachments []*NewAttachment
@@ -75,6 +75,6 @@ func Test_AddRepoIDForAttachment(t *testing.T) {
has, err := x.ID(attach.ReleaseID).Get(&release)
assert.NoError(t, err)
assert.True(t, has)
- assert.EqualValues(t, attach.RepoID, release.RepoID)
+ assert.Equal(t, attach.RepoID, release.RepoID)
}
}
diff --git a/models/migrations/v1_16/v194.go b/models/migrations/v1_16/v194.go
index 6aa13c50cf..2e4ed8340e 100644
--- a/models/migrations/v1_16/v194.go
+++ b/models/migrations/v1_16/v194.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v195.go b/models/migrations/v1_16/v195.go
index 6d7e94141e..4fd42b7bd2 100644
--- a/models/migrations/v1_16/v195.go
+++ b/models/migrations/v1_16/v195.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v195_test.go b/models/migrations/v1_16/v195_test.go
index 742397bf32..946e06e399 100644
--- a/models/migrations/v1_16/v195_test.go
+++ b/models/migrations/v1_16/v195_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"testing"
diff --git a/models/migrations/v1_16/v196.go b/models/migrations/v1_16/v196.go
index 7cbafc61e5..6c9caa100f 100644
--- a/models/migrations/v1_16/v196.go
+++ b/models/migrations/v1_16/v196.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v197.go b/models/migrations/v1_16/v197.go
index 97888b2847..862bdfdcbd 100644
--- a/models/migrations/v1_16/v197.go
+++ b/models/migrations/v1_16/v197.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v198.go b/models/migrations/v1_16/v198.go
index 115bb313a0..f35ede138a 100644
--- a/models/migrations/v1_16/v198.go
+++ b/models/migrations/v1_16/v198.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v199.go b/models/migrations/v1_16/v199.go
index 6adcf890af..4020352f2b 100644
--- a/models/migrations/v1_16/v199.go
+++ b/models/migrations/v1_16/v199.go
@@ -1,6 +1,6 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
// We used to use a table `remote_version` to store information for updater, now we use `AppState`, so this migration task is a no-op now.
diff --git a/models/migrations/v1_16/v200.go b/models/migrations/v1_16/v200.go
index c08c20e51d..de57fad8fe 100644
--- a/models/migrations/v1_16/v200.go
+++ b/models/migrations/v1_16/v200.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v201.go b/models/migrations/v1_16/v201.go
index 35e0c9f2fb..2c43698b0c 100644
--- a/models/migrations/v1_16/v201.go
+++ b/models/migrations/v1_16/v201.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v202.go b/models/migrations/v1_16/v202.go
index 6ba36152f1..d8c8fdcadc 100644
--- a/models/migrations/v1_16/v202.go
+++ b/models/migrations/v1_16/v202.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v203.go b/models/migrations/v1_16/v203.go
index e8e6b52453..c3241cba57 100644
--- a/models/migrations/v1_16/v203.go
+++ b/models/migrations/v1_16/v203.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v204.go b/models/migrations/v1_16/v204.go
index ece03e1305..4d375307e7 100644
--- a/models/migrations/v1_16/v204.go
+++ b/models/migrations/v1_16/v204.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import "xorm.io/xorm"
diff --git a/models/migrations/v1_16/v205.go b/models/migrations/v1_16/v205.go
index d6c577083c..78241bad5b 100644
--- a/models/migrations/v1_16/v205.go
+++ b/models/migrations/v1_16/v205.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_16/v206.go b/models/migrations/v1_16/v206.go
index 581a7d76e9..01a9c386eb 100644
--- a/models/migrations/v1_16/v206.go
+++ b/models/migrations/v1_16/v206.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"fmt"
diff --git a/models/migrations/v1_16/v207.go b/models/migrations/v1_16/v207.go
index 91208f066c..19126ead1f 100644
--- a/models/migrations/v1_16/v207.go
+++ b/models/migrations/v1_16/v207.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v208.go b/models/migrations/v1_16/v208.go
index 1a11ef096a..fb643324f4 100644
--- a/models/migrations/v1_16/v208.go
+++ b/models/migrations/v1_16/v208.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v209.go b/models/migrations/v1_16/v209.go
index be3100e02a..230838647b 100644
--- a/models/migrations/v1_16/v209.go
+++ b/models/migrations/v1_16/v209.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_16/v210.go b/models/migrations/v1_16/v210.go
index 51b7d81e99..0b94baf8e3 100644
--- a/models/migrations/v1_16/v210.go
+++ b/models/migrations/v1_16/v210.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"encoding/base32"
diff --git a/models/migrations/v1_16/v210_test.go b/models/migrations/v1_16/v210_test.go
index d43fb03106..3b4ac7aa4b 100644
--- a/models/migrations/v1_16/v210_test.go
+++ b/models/migrations/v1_16/v210_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_16 //nolint
+package v1_16
import (
"testing"
@@ -71,5 +71,5 @@ func Test_RemigrateU2FCredentials(t *testing.T) {
return
}
- assert.EqualValues(t, expected, got)
+ assert.Equal(t, expected, got)
}
diff --git a/models/migrations/v1_17/main_test.go b/models/migrations/v1_17/main_test.go
index 79cb3fa078..571a4f55a3 100644
--- a/models/migrations/v1_17/main_test.go
+++ b/models/migrations/v1_17/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"testing"
diff --git a/models/migrations/v1_17/v211.go b/models/migrations/v1_17/v211.go
index 9b72c8610b..517cf19388 100644
--- a/models/migrations/v1_17/v211.go
+++ b/models/migrations/v1_17/v211.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_17/v212.go b/models/migrations/v1_17/v212.go
index e3f9437121..788792211f 100644
--- a/models/migrations/v1_17/v212.go
+++ b/models/migrations/v1_17/v212.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_17/v213.go b/models/migrations/v1_17/v213.go
index bb3f466e52..b2bbdf7279 100644
--- a/models/migrations/v1_17/v213.go
+++ b/models/migrations/v1_17/v213.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_17/v214.go b/models/migrations/v1_17/v214.go
index 2268164919..1925324f0f 100644
--- a/models/migrations/v1_17/v214.go
+++ b/models/migrations/v1_17/v214.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_17/v215.go b/models/migrations/v1_17/v215.go
index b338f85417..748539225d 100644
--- a/models/migrations/v1_17/v215.go
+++ b/models/migrations/v1_17/v215.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"code.gitea.io/gitea/models/pull"
diff --git a/models/migrations/v1_17/v216.go b/models/migrations/v1_17/v216.go
index 268f472a42..37aeacb6fc 100644
--- a/models/migrations/v1_17/v216.go
+++ b/models/migrations/v1_17/v216.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
// This migration added non-ideal indices to the action table which on larger datasets slowed things down
// it has been superseded by v218.go
diff --git a/models/migrations/v1_17/v217.go b/models/migrations/v1_17/v217.go
index 3f970b68a5..04626bcbc5 100644
--- a/models/migrations/v1_17/v217.go
+++ b/models/migrations/v1_17/v217.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_17/v218.go b/models/migrations/v1_17/v218.go
index 4c05a9b539..17d4cd89d4 100644
--- a/models/migrations/v1_17/v218.go
+++ b/models/migrations/v1_17/v218.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_17/v219.go b/models/migrations/v1_17/v219.go
index d266029fd9..6e335cb813 100644
--- a/models/migrations/v1_17/v219.go
+++ b/models/migrations/v1_17/v219.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"time"
diff --git a/models/migrations/v1_17/v220.go b/models/migrations/v1_17/v220.go
index 904ddc5192..4ac8c58e1e 100644
--- a/models/migrations/v1_17/v220.go
+++ b/models/migrations/v1_17/v220.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
packages_model "code.gitea.io/gitea/models/packages"
diff --git a/models/migrations/v1_17/v221.go b/models/migrations/v1_17/v221.go
index 9e159388bd..9e6a67eb18 100644
--- a/models/migrations/v1_17/v221.go
+++ b/models/migrations/v1_17/v221.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"encoding/base32"
diff --git a/models/migrations/v1_17/v221_test.go b/models/migrations/v1_17/v221_test.go
index 9ca54142e2..a2dc0fae55 100644
--- a/models/migrations/v1_17/v221_test.go
+++ b/models/migrations/v1_17/v221_test.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"encoding/base32"
diff --git a/models/migrations/v1_17/v222.go b/models/migrations/v1_17/v222.go
index 2ffb94eb1c..a5ea537d8a 100644
--- a/models/migrations/v1_17/v222.go
+++ b/models/migrations/v1_17/v222.go
@@ -1,10 +1,11 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"context"
+ "errors"
"fmt"
"code.gitea.io/gitea/models/migrations/base"
@@ -29,7 +30,7 @@ func DropOldCredentialIDColumn(x *xorm.Engine) error {
}
if !credentialIDBytesExists {
// looks like 221 hasn't properly run
- return fmt.Errorf("webauthn_credential does not have a credential_id_bytes column... it is not safe to run this migration")
+ return errors.New("webauthn_credential does not have a credential_id_bytes column... it is not safe to run this migration")
}
// Create webauthnCredential table
diff --git a/models/migrations/v1_17/v223.go b/models/migrations/v1_17/v223.go
index 018451ee4c..b2bfb76551 100644
--- a/models/migrations/v1_17/v223.go
+++ b/models/migrations/v1_17/v223.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_17 //nolint
+package v1_17
import (
"context"
diff --git a/models/migrations/v1_18/main_test.go b/models/migrations/v1_18/main_test.go
index f71a21d1fb..ebcfb45a94 100644
--- a/models/migrations/v1_18/main_test.go
+++ b/models/migrations/v1_18/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"testing"
diff --git a/models/migrations/v1_18/v224.go b/models/migrations/v1_18/v224.go
index f3d522b91a..6dc12020ea 100644
--- a/models/migrations/v1_18/v224.go
+++ b/models/migrations/v1_18/v224.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_18/v225.go b/models/migrations/v1_18/v225.go
index b0ac3777fc..bc6117e38f 100644
--- a/models/migrations/v1_18/v225.go
+++ b/models/migrations/v1_18/v225.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_18/v226.go b/models/migrations/v1_18/v226.go
index f87e24b11d..8ed9761476 100644
--- a/models/migrations/v1_18/v226.go
+++ b/models/migrations/v1_18/v226.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"xorm.io/builder"
diff --git a/models/migrations/v1_18/v227.go b/models/migrations/v1_18/v227.go
index 5fe5dcd0c9..3aca686d59 100644
--- a/models/migrations/v1_18/v227.go
+++ b/models/migrations/v1_18/v227.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_18/v228.go b/models/migrations/v1_18/v228.go
index 3e7a36de15..b13f6461bd 100644
--- a/models/migrations/v1_18/v228.go
+++ b/models/migrations/v1_18/v228.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_18/v229.go b/models/migrations/v1_18/v229.go
index 10d9f35097..bc15e01390 100644
--- a/models/migrations/v1_18/v229.go
+++ b/models/migrations/v1_18/v229.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"fmt"
diff --git a/models/migrations/v1_18/v229_test.go b/models/migrations/v1_18/v229_test.go
index d489328c00..5722dd3557 100644
--- a/models/migrations/v1_18/v229_test.go
+++ b/models/migrations/v1_18/v229_test.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"testing"
diff --git a/models/migrations/v1_18/v230.go b/models/migrations/v1_18/v230.go
index ea5b4d02e1..078fce7643 100644
--- a/models/migrations/v1_18/v230.go
+++ b/models/migrations/v1_18/v230.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_18/v230_test.go b/models/migrations/v1_18/v230_test.go
index 40db4c2ffe..25b2f6525d 100644
--- a/models/migrations/v1_18/v230_test.go
+++ b/models/migrations/v1_18/v230_test.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_18 //nolint
+package v1_18
import (
"testing"
diff --git a/models/migrations/v1_19/main_test.go b/models/migrations/v1_19/main_test.go
index 59f42af111..87e807be6e 100644
--- a/models/migrations/v1_19/main_test.go
+++ b/models/migrations/v1_19/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"testing"
diff --git a/models/migrations/v1_19/v231.go b/models/migrations/v1_19/v231.go
index 79e46132f0..8ef1e4e743 100644
--- a/models/migrations/v1_19/v231.go
+++ b/models/migrations/v1_19/v231.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v232.go b/models/migrations/v1_19/v232.go
index 9caf587c1e..493dbc6df8 100644
--- a/models/migrations/v1_19/v232.go
+++ b/models/migrations/v1_19/v232.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_19/v233.go b/models/migrations/v1_19/v233.go
index ba4cd8e20b..9eb6d40509 100644
--- a/models/migrations/v1_19/v233.go
+++ b/models/migrations/v1_19/v233.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"fmt"
diff --git a/models/migrations/v1_19/v233_test.go b/models/migrations/v1_19/v233_test.go
index 32c10ab0f4..7436ff7483 100644
--- a/models/migrations/v1_19/v233_test.go
+++ b/models/migrations/v1_19/v233_test.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"testing"
@@ -64,7 +64,7 @@ func Test_AddHeaderAuthorizationEncryptedColWebhook(t *testing.T) {
assert.Equal(t, e.Meta, got[i].Meta)
if e.HeaderAuthorization == "" {
- assert.Equal(t, "", got[i].HeaderAuthorizationEncrypted)
+ assert.Empty(t, got[i].HeaderAuthorizationEncrypted)
} else {
cipherhex := got[i].HeaderAuthorizationEncrypted
cleartext, err := secret.DecryptSecret(setting.SecretKey, cipherhex)
diff --git a/models/migrations/v1_19/v234.go b/models/migrations/v1_19/v234.go
index 728a580807..3475384d6f 100644
--- a/models/migrations/v1_19/v234.go
+++ b/models/migrations/v1_19/v234.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_19/v235.go b/models/migrations/v1_19/v235.go
index 3715de3920..297d90f65a 100644
--- a/models/migrations/v1_19/v235.go
+++ b/models/migrations/v1_19/v235.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v236.go b/models/migrations/v1_19/v236.go
index f172a85b1f..0ed4d97a27 100644
--- a/models/migrations/v1_19/v236.go
+++ b/models/migrations/v1_19/v236.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_19/v237.go b/models/migrations/v1_19/v237.go
index b23c765aa5..cf30226ccd 100644
--- a/models/migrations/v1_19/v237.go
+++ b/models/migrations/v1_19/v237.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v238.go b/models/migrations/v1_19/v238.go
index 266e6cea58..de681bfc7a 100644
--- a/models/migrations/v1_19/v238.go
+++ b/models/migrations/v1_19/v238.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_19/v239.go b/models/migrations/v1_19/v239.go
index 10076f2401..8f4a65be95 100644
--- a/models/migrations/v1_19/v239.go
+++ b/models/migrations/v1_19/v239.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v240.go b/models/migrations/v1_19/v240.go
index 4505f86299..7fdbaeb9dc 100644
--- a/models/migrations/v1_19/v240.go
+++ b/models/migrations/v1_19/v240.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"code.gitea.io/gitea/models/db"
diff --git a/models/migrations/v1_19/v241.go b/models/migrations/v1_19/v241.go
index a617d6fd2f..e35801a057 100644
--- a/models/migrations/v1_19/v241.go
+++ b/models/migrations/v1_19/v241.go
@@ -1,7 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_19/v242.go b/models/migrations/v1_19/v242.go
index 4470835214..e9e759eaaa 100644
--- a/models/migrations/v1_19/v242.go
+++ b/models/migrations/v1_19/v242.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_19/v243.go b/models/migrations/v1_19/v243.go
index 55bbfafb2f..9c3f372594 100644
--- a/models/migrations/v1_19/v243.go
+++ b/models/migrations/v1_19/v243.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_19 //nolint
+package v1_19
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/main_test.go b/models/migrations/v1_20/main_test.go
index 92a1a9f622..2fd63a7118 100644
--- a/models/migrations/v1_20/main_test.go
+++ b/models/migrations/v1_20/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"testing"
diff --git a/models/migrations/v1_20/v244.go b/models/migrations/v1_20/v244.go
index 977566ad7d..76cdccaca5 100644
--- a/models/migrations/v1_20/v244.go
+++ b/models/migrations/v1_20/v244.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v245.go b/models/migrations/v1_20/v245.go
index ab58d12880..4acb11416c 100644
--- a/models/migrations/v1_20/v245.go
+++ b/models/migrations/v1_20/v245.go
@@ -1,11 +1,10 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"context"
- "fmt"
"code.gitea.io/gitea/models/migrations/base"
"code.gitea.io/gitea/modules/setting"
@@ -57,7 +56,7 @@ func RenameWebhookOrgToOwner(x *xorm.Engine) error {
return err
}
sqlType := x.Dialect().SQLType(inferredTable.GetColumn("org_id"))
- if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `webhook` CHANGE org_id owner_id %s", sqlType)); err != nil {
+ if _, err := sess.Exec("ALTER TABLE `webhook` CHANGE org_id owner_id " + sqlType); err != nil {
return err
}
case setting.Database.Type.IsMSSQL():
diff --git a/models/migrations/v1_20/v246.go b/models/migrations/v1_20/v246.go
index e6340ef079..22bf723404 100644
--- a/models/migrations/v1_20/v246.go
+++ b/models/migrations/v1_20/v246.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v247.go b/models/migrations/v1_20/v247.go
index 59fc5c46b5..4f82937e18 100644
--- a/models/migrations/v1_20/v247.go
+++ b/models/migrations/v1_20/v247.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_20/v248.go b/models/migrations/v1_20/v248.go
index 40555210e7..4f2091e4bc 100644
--- a/models/migrations/v1_20/v248.go
+++ b/models/migrations/v1_20/v248.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import "xorm.io/xorm"
diff --git a/models/migrations/v1_20/v249.go b/models/migrations/v1_20/v249.go
index 02951a74d6..c6d3a177ca 100644
--- a/models/migrations/v1_20/v249.go
+++ b/models/migrations/v1_20/v249.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_20/v250.go b/models/migrations/v1_20/v250.go
index 86388ef0b8..ec45e6e5c3 100644
--- a/models/migrations/v1_20/v250.go
+++ b/models/migrations/v1_20/v250.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"strings"
diff --git a/models/migrations/v1_20/v251.go b/models/migrations/v1_20/v251.go
index 7743248a3f..a274c22a73 100644
--- a/models/migrations/v1_20/v251.go
+++ b/models/migrations/v1_20/v251.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_20/v252.go b/models/migrations/v1_20/v252.go
index ab61cd9b8b..d6aa602753 100644
--- a/models/migrations/v1_20/v252.go
+++ b/models/migrations/v1_20/v252.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_20/v253.go b/models/migrations/v1_20/v253.go
index 96c494bd8d..c96454dbf9 100644
--- a/models/migrations/v1_20/v253.go
+++ b/models/migrations/v1_20/v253.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"code.gitea.io/gitea/modules/log"
diff --git a/models/migrations/v1_20/v254.go b/models/migrations/v1_20/v254.go
index 1e26979a5b..9cdbfb3916 100644
--- a/models/migrations/v1_20/v254.go
+++ b/models/migrations/v1_20/v254.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v255.go b/models/migrations/v1_20/v255.go
index 14b70f8f96..caf198700e 100644
--- a/models/migrations/v1_20/v255.go
+++ b/models/migrations/v1_20/v255.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_20/v256.go b/models/migrations/v1_20/v256.go
index 822153b93e..7b84c1e154 100644
--- a/models/migrations/v1_20/v256.go
+++ b/models/migrations/v1_20/v256.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v257.go b/models/migrations/v1_20/v257.go
index 6c6ca4c748..9d5f7c07df 100644
--- a/models/migrations/v1_20/v257.go
+++ b/models/migrations/v1_20/v257.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_20/v258.go b/models/migrations/v1_20/v258.go
index 47174ce805..1d3faffdae 100644
--- a/models/migrations/v1_20/v258.go
+++ b/models/migrations/v1_20/v258.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_20/v259.go b/models/migrations/v1_20/v259.go
index 5b8ced4ad7..9e0dc9b61d 100644
--- a/models/migrations/v1_20/v259.go
+++ b/models/migrations/v1_20/v259.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"fmt"
@@ -329,7 +329,7 @@ func ConvertScopedAccessTokens(x *xorm.Engine) error {
for _, token := range tokens {
var scopes []string
allNewScopesMap := make(map[AccessTokenScope]bool)
- for _, oldScope := range strings.Split(token.Scope, ",") {
+ for oldScope := range strings.SplitSeq(token.Scope, ",") {
if newScopes, exists := accessTokenScopeMap[OldAccessTokenScope(oldScope)]; exists {
for _, newScope := range newScopes {
allNewScopesMap[newScope] = true
diff --git a/models/migrations/v1_20/v259_test.go b/models/migrations/v1_20/v259_test.go
index 5bc9a71391..0bf63719e5 100644
--- a/models/migrations/v1_20/v259_test.go
+++ b/models/migrations/v1_20/v259_test.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_20 //nolint
+package v1_20
import (
"sort"
@@ -96,7 +96,7 @@ func Test_ConvertScopedAccessTokens(t *testing.T) {
tokens := make([]AccessToken, 0)
err = x.Find(&tokens)
assert.NoError(t, err)
- assert.Equal(t, len(tests), len(tokens))
+ assert.Len(t, tokens, len(tests))
// sort the tokens (insertion order by auto-incrementing primary key)
sort.Slice(tokens, func(i, j int) bool {
diff --git a/models/migrations/v1_21/main_test.go b/models/migrations/v1_21/main_test.go
index 9afdea1677..536a7ade08 100644
--- a/models/migrations/v1_21/main_test.go
+++ b/models/migrations/v1_21/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"testing"
diff --git a/models/migrations/v1_21/v260.go b/models/migrations/v1_21/v260.go
index 6ca52c5998..8540c58ae8 100644
--- a/models/migrations/v1_21/v260.go
+++ b/models/migrations/v1_21/v260.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_21/v261.go b/models/migrations/v1_21/v261.go
index 4ec1160d0b..122b98eb93 100644
--- a/models/migrations/v1_21/v261.go
+++ b/models/migrations/v1_21/v261.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_21/v262.go b/models/migrations/v1_21/v262.go
index 23e900572a..6e88e29b9d 100644
--- a/models/migrations/v1_21/v262.go
+++ b/models/migrations/v1_21/v262.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v263.go b/models/migrations/v1_21/v263.go
index 2c7cbadf0d..55c418bde0 100644
--- a/models/migrations/v1_21/v263.go
+++ b/models/migrations/v1_21/v263.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"fmt"
diff --git a/models/migrations/v1_21/v264.go b/models/migrations/v1_21/v264.go
index e81a17ad6d..7fc0ec6024 100644
--- a/models/migrations/v1_21/v264.go
+++ b/models/migrations/v1_21/v264.go
@@ -1,11 +1,11 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"context"
- "fmt"
+ "errors"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/timeutil"
@@ -57,7 +57,7 @@ func AddBranchTable(x *xorm.Engine) error {
if err != nil {
return err
} else if !has {
- return fmt.Errorf("no admin user found")
+ return errors.New("no admin user found")
}
branches := make([]Branch, 0, 100)
diff --git a/models/migrations/v1_21/v265.go b/models/migrations/v1_21/v265.go
index 800eb95f72..b6892acc27 100644
--- a/models/migrations/v1_21/v265.go
+++ b/models/migrations/v1_21/v265.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v266.go b/models/migrations/v1_21/v266.go
index 79a5f5e14c..440549e868 100644
--- a/models/migrations/v1_21/v266.go
+++ b/models/migrations/v1_21/v266.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v267.go b/models/migrations/v1_21/v267.go
index bc0e954bdc..394139a17e 100644
--- a/models/migrations/v1_21/v267.go
+++ b/models/migrations/v1_21/v267.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_21/v268.go b/models/migrations/v1_21/v268.go
index 332793ff07..b677d2383e 100644
--- a/models/migrations/v1_21/v268.go
+++ b/models/migrations/v1_21/v268.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v269.go b/models/migrations/v1_21/v269.go
index 475ec02380..042040927d 100644
--- a/models/migrations/v1_21/v269.go
+++ b/models/migrations/v1_21/v269.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v270.go b/models/migrations/v1_21/v270.go
index b9cc84d3ac..ab7c5660ba 100644
--- a/models/migrations/v1_21/v270.go
+++ b/models/migrations/v1_21/v270.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v271.go b/models/migrations/v1_21/v271.go
index 098f6499d5..05e1af1351 100644
--- a/models/migrations/v1_21/v271.go
+++ b/models/migrations/v1_21/v271.go
@@ -1,7 +1,8 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
+
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_21/v272.go b/models/migrations/v1_21/v272.go
index a729c49f1b..14c1e0c4b0 100644
--- a/models/migrations/v1_21/v272.go
+++ b/models/migrations/v1_21/v272.go
@@ -1,7 +1,8 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
+
import (
"xorm.io/xorm"
)
diff --git a/models/migrations/v1_21/v273.go b/models/migrations/v1_21/v273.go
index 61c79f4a76..e614a56a7d 100644
--- a/models/migrations/v1_21/v273.go
+++ b/models/migrations/v1_21/v273.go
@@ -1,7 +1,8 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
+
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_21/v274.go b/models/migrations/v1_21/v274.go
index df5994f159..d0b557a151 100644
--- a/models/migrations/v1_21/v274.go
+++ b/models/migrations/v1_21/v274.go
@@ -1,7 +1,8 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
+
import (
"time"
diff --git a/models/migrations/v1_21/v275.go b/models/migrations/v1_21/v275.go
index 78804a59d6..2bfe5c72fa 100644
--- a/models/migrations/v1_21/v275.go
+++ b/models/migrations/v1_21/v275.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v276.go b/models/migrations/v1_21/v276.go
index 15177bf040..3ab7e22cd0 100644
--- a/models/migrations/v1_21/v276.go
+++ b/models/migrations/v1_21/v276.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"context"
@@ -172,7 +172,7 @@ func getRemoteAddress(ownerName, repoName, remoteName string) (string, error) {
return "", fmt.Errorf("get remote %s's address of %s/%s failed: %v", remoteName, ownerName, repoName, err)
}
- u, err := giturl.Parse(remoteURL)
+ u, err := giturl.ParseGitURL(remoteURL)
if err != nil {
return "", err
}
diff --git a/models/migrations/v1_21/v277.go b/models/migrations/v1_21/v277.go
index 12529160b7..0c102eddde 100644
--- a/models/migrations/v1_21/v277.go
+++ b/models/migrations/v1_21/v277.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v278.go b/models/migrations/v1_21/v278.go
index d6a462d1e7..846f228678 100644
--- a/models/migrations/v1_21/v278.go
+++ b/models/migrations/v1_21/v278.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_21/v279.go b/models/migrations/v1_21/v279.go
index 2abd1bbe84..beb39effe1 100644
--- a/models/migrations/v1_21/v279.go
+++ b/models/migrations/v1_21/v279.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_21 //nolint
+package v1_21
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/main_test.go b/models/migrations/v1_22/main_test.go
index efd8dbaa8c..ac8facd6aa 100644
--- a/models/migrations/v1_22/main_test.go
+++ b/models/migrations/v1_22/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"testing"
diff --git a/models/migrations/v1_22/v280.go b/models/migrations/v1_22/v280.go
index a8ee4a3bf7..2271cb6089 100644
--- a/models/migrations/v1_22/v280.go
+++ b/models/migrations/v1_22/v280.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/v281.go b/models/migrations/v1_22/v281.go
index fc1866aa83..129ec2cba0 100644
--- a/models/migrations/v1_22/v281.go
+++ b/models/migrations/v1_22/v281.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_22/v282.go b/models/migrations/v1_22/v282.go
index baad9e0916..eed64c30f7 100644
--- a/models/migrations/v1_22/v282.go
+++ b/models/migrations/v1_22/v282.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/v283.go b/models/migrations/v1_22/v283.go
index 0a45c51245..0eca031b65 100644
--- a/models/migrations/v1_22/v283.go
+++ b/models/migrations/v1_22/v283.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"fmt"
diff --git a/models/migrations/v1_22/v283_test.go b/models/migrations/v1_22/v283_test.go
index e89a7cbfc2..743f860466 100644
--- a/models/migrations/v1_22/v283_test.go
+++ b/models/migrations/v1_22/v283_test.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"testing"
diff --git a/models/migrations/v1_22/v284.go b/models/migrations/v1_22/v284.go
index 2b95078980..31b38f6aed 100644
--- a/models/migrations/v1_22/v284.go
+++ b/models/migrations/v1_22/v284.go
@@ -1,7 +1,8 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
+
import (
"xorm.io/xorm"
)
diff --git a/models/migrations/v1_22/v285.go b/models/migrations/v1_22/v285.go
index a55cc17c04..fed89f670e 100644
--- a/models/migrations/v1_22/v285.go
+++ b/models/migrations/v1_22/v285.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"time"
diff --git a/models/migrations/v1_22/v286.go b/models/migrations/v1_22/v286.go
index 1fcde33202..f3ba50dbb6 100644
--- a/models/migrations/v1_22/v286.go
+++ b/models/migrations/v1_22/v286.go
@@ -1,6 +1,6 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"errors"
diff --git a/models/migrations/v1_22/v286_test.go b/models/migrations/v1_22/v286_test.go
index 1f213ddb6e..b4a50f6fcb 100644
--- a/models/migrations/v1_22/v286_test.go
+++ b/models/migrations/v1_22/v286_test.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"testing"
@@ -108,11 +108,11 @@ func Test_RepositoryFormat(t *testing.T) {
ok, err := x.ID(2).Get(repo)
assert.NoError(t, err)
assert.True(t, ok)
- assert.EqualValues(t, "sha1", repo.ObjectFormatName)
+ assert.Equal(t, "sha1", repo.ObjectFormatName)
repo = new(Repository)
ok, err = x.ID(id).Get(repo)
assert.NoError(t, err)
assert.True(t, ok)
- assert.EqualValues(t, "sha256", repo.ObjectFormatName)
+ assert.Equal(t, "sha256", repo.ObjectFormatName)
}
diff --git a/models/migrations/v1_22/v287.go b/models/migrations/v1_22/v287.go
index c8b1593286..5fd901f9de 100644
--- a/models/migrations/v1_22/v287.go
+++ b/models/migrations/v1_22/v287.go
@@ -1,7 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/v287_test.go b/models/migrations/v1_22/v287_test.go
index 9c7b10947d..2b42a33c38 100644
--- a/models/migrations/v1_22/v287_test.go
+++ b/models/migrations/v1_22/v287_test.go
@@ -1,10 +1,10 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
- "fmt"
+ "strconv"
"testing"
"code.gitea.io/gitea/models/migrations/base"
@@ -50,7 +50,7 @@ func Test_UpdateBadgeColName(t *testing.T) {
for i, e := range oldBadges {
got := got[i+1] // 1 is in the badge.yml
assert.Equal(t, e.ID, got.ID)
- assert.Equal(t, fmt.Sprintf("%d", e.ID), got.Slug)
+ assert.Equal(t, strconv.FormatInt(e.ID, 10), got.Slug)
}
// TODO: check if badges have been updated
diff --git a/models/migrations/v1_22/v288.go b/models/migrations/v1_22/v288.go
index 7c93bfcc66..26c850c218 100644
--- a/models/migrations/v1_22/v288.go
+++ b/models/migrations/v1_22/v288.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_22/v289.go b/models/migrations/v1_22/v289.go
index b9941aadd9..78689a4ffa 100644
--- a/models/migrations/v1_22/v289.go
+++ b/models/migrations/v1_22/v289.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import "xorm.io/xorm"
diff --git a/models/migrations/v1_22/v290.go b/models/migrations/v1_22/v290.go
index 9c54d4e87c..0f4d78410c 100644
--- a/models/migrations/v1_22/v290.go
+++ b/models/migrations/v1_22/v290.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_22/v291.go b/models/migrations/v1_22/v291.go
index 74726fae96..823a644a95 100644
--- a/models/migrations/v1_22/v291.go
+++ b/models/migrations/v1_22/v291.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import "xorm.io/xorm"
diff --git a/models/migrations/v1_22/v292.go b/models/migrations/v1_22/v292.go
index beca556aee..440f48ce80 100644
--- a/models/migrations/v1_22/v292.go
+++ b/models/migrations/v1_22/v292.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
// NOTE: noop the original migration has bug which some projects will be skip, so
// these projects will have no default board.
diff --git a/models/migrations/v1_22/v293.go b/models/migrations/v1_22/v293.go
index 53cc719294..5299b8618f 100644
--- a/models/migrations/v1_22/v293.go
+++ b/models/migrations/v1_22/v293.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_22/v293_test.go b/models/migrations/v1_22/v293_test.go
index cfe4345143..2c8f7683a8 100644
--- a/models/migrations/v1_22/v293_test.go
+++ b/models/migrations/v1_22/v293_test.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"testing"
diff --git a/models/migrations/v1_22/v294.go b/models/migrations/v1_22/v294.go
index 20e261fb1b..8776e51a16 100644
--- a/models/migrations/v1_22/v294.go
+++ b/models/migrations/v1_22/v294.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"fmt"
diff --git a/models/migrations/v1_22/v294_test.go b/models/migrations/v1_22/v294_test.go
index a1d702cb77..1cf03d6120 100644
--- a/models/migrations/v1_22/v294_test.go
+++ b/models/migrations/v1_22/v294_test.go
@@ -1,10 +1,9 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
- "slices"
"testing"
"code.gitea.io/gitea/models/migrations/base"
@@ -44,7 +43,7 @@ func Test_AddUniqueIndexForProjectIssue(t *testing.T) {
for _, index := range tables[0].Indexes {
if index.Type == schemas.UniqueType {
found = true
- slices.Equal(index.Cols, []string{"project_id", "issue_id"})
+ assert.ElementsMatch(t, index.Cols, []string{"project_id", "issue_id"})
break
}
}
diff --git a/models/migrations/v1_22/v295.go b/models/migrations/v1_22/v295.go
index 17bdadb4ad..319b1a399b 100644
--- a/models/migrations/v1_22/v295.go
+++ b/models/migrations/v1_22/v295.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import "xorm.io/xorm"
diff --git a/models/migrations/v1_22/v296.go b/models/migrations/v1_22/v296.go
index 1ecacab95f..75350f9f65 100644
--- a/models/migrations/v1_22/v296.go
+++ b/models/migrations/v1_22/v296.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import "xorm.io/xorm"
diff --git a/models/migrations/v1_22/v297.go b/models/migrations/v1_22/v297.go
index 7d4b506925..9a4405f266 100644
--- a/models/migrations/v1_22/v297.go
+++ b/models/migrations/v1_22/v297.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import (
"code.gitea.io/gitea/models/perm"
diff --git a/models/migrations/v1_22/v298.go b/models/migrations/v1_22/v298.go
index b9f3b95ade..7700173a00 100644
--- a/models/migrations/v1_22/v298.go
+++ b/models/migrations/v1_22/v298.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_22 //nolint
+package v1_22
import "xorm.io/xorm"
diff --git a/models/migrations/v1_23/main_test.go b/models/migrations/v1_23/main_test.go
index b7948bd4dd..f7b2caed83 100644
--- a/models/migrations/v1_23/main_test.go
+++ b/models/migrations/v1_23/main_test.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"testing"
diff --git a/models/migrations/v1_23/v299.go b/models/migrations/v1_23/v299.go
index f6db960c3b..11021d8855 100644
--- a/models/migrations/v1_23/v299.go
+++ b/models/migrations/v1_23/v299.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import "xorm.io/xorm"
@@ -14,5 +14,9 @@ func AddContentVersionToIssueAndComment(x *xorm.Engine) error {
ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
}
- return x.Sync(new(Comment), new(Issue))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(Comment), new(Issue))
+ return err
}
diff --git a/models/migrations/v1_23/v300.go b/models/migrations/v1_23/v300.go
index f1f1cccdbf..13c6489c5e 100644
--- a/models/migrations/v1_23/v300.go
+++ b/models/migrations/v1_23/v300.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import "xorm.io/xorm"
@@ -13,5 +13,9 @@ func AddForcePushBranchProtection(x *xorm.Engine) error {
ForcePushAllowlistTeamIDs []int64 `xorm:"JSON TEXT"`
ForcePushAllowlistDeployKeys bool `xorm:"NOT NULL DEFAULT false"`
}
- return x.Sync(new(ProtectedBranch))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(ProtectedBranch))
+ return err
}
diff --git a/models/migrations/v1_23/v301.go b/models/migrations/v1_23/v301.go
index b7797f6c6b..ed8e9ef059 100644
--- a/models/migrations/v1_23/v301.go
+++ b/models/migrations/v1_23/v301.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import "xorm.io/xorm"
@@ -10,5 +10,9 @@ func AddSkipSecondaryAuthColumnToOAuth2ApplicationTable(x *xorm.Engine) error {
type oauth2Application struct {
SkipSecondaryAuthorization bool `xorm:"NOT NULL DEFAULT FALSE"`
}
- return x.Sync(new(oauth2Application))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(oauth2Application))
+ return err
}
diff --git a/models/migrations/v1_23/v302.go b/models/migrations/v1_23/v302.go
index d7ea03eb3d..e4a50b3ec8 100644
--- a/models/migrations/v1_23/v302.go
+++ b/models/migrations/v1_23/v302.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"code.gitea.io/gitea/modules/timeutil"
@@ -14,5 +14,8 @@ func AddIndexToActionTaskStoppedLogExpired(x *xorm.Engine) error {
Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"`
LogExpired bool `xorm:"index(stopped_log_expired)"`
}
- return x.Sync(new(ActionTask))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreDropIndices: true,
+ }, new(ActionTask))
+ return err
}
diff --git a/models/migrations/v1_23/v302_test.go b/models/migrations/v1_23/v302_test.go
new file mode 100644
index 0000000000..b008b6fc03
--- /dev/null
+++ b/models/migrations/v1_23/v302_test.go
@@ -0,0 +1,51 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_23
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/migrations/base"
+ "code.gitea.io/gitea/modules/timeutil"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_AddIndexToActionTaskStoppedLogExpired(t *testing.T) {
+ type ActionTask struct {
+ ID int64
+ JobID int64
+ Attempt int64
+ RunnerID int64 `xorm:"index"`
+ Status int `xorm:"index"`
+ Started timeutil.TimeStamp `xorm:"index"`
+ Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"`
+
+ RepoID int64 `xorm:"index"`
+ OwnerID int64 `xorm:"index"`
+ CommitSHA string `xorm:"index"`
+ IsForkPullRequest bool
+
+ Token string `xorm:"-"`
+ TokenHash string `xorm:"UNIQUE"` // sha256 of token
+ TokenSalt string
+ TokenLastEight string `xorm:"index token_last_eight"`
+
+ LogFilename string // file name of log
+ LogInStorage bool // read log from database or from storage
+ LogLength int64 // lines count
+ LogSize int64 // blob size
+ LogIndexes []int64 `xorm:"LONGBLOB"` // line number to offset
+ LogExpired bool `xorm:"index(stopped_log_expired)"` // files that are too old will be deleted
+
+ Created timeutil.TimeStamp `xorm:"created"`
+ Updated timeutil.TimeStamp `xorm:"updated index"`
+ }
+
+ // Prepare and load the testing database
+ x, deferable := base.PrepareTestEnv(t, 0, new(ActionTask))
+ defer deferable()
+
+ assert.NoError(t, AddIndexToActionTaskStoppedLogExpired(x))
+}
diff --git a/models/migrations/v1_23/v303.go b/models/migrations/v1_23/v303.go
index adfe917d3f..dc541a9535 100644
--- a/models/migrations/v1_23/v303.go
+++ b/models/migrations/v1_23/v303.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"xorm.io/xorm"
@@ -19,5 +19,9 @@ func AddCommentMetaDataColumn(x *xorm.Engine) error {
CommentMetaData *CommentMetaData `xorm:"JSON TEXT"` // put all non-index metadata in a single field
}
- return x.Sync(new(Comment))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(Comment))
+ return err
}
diff --git a/models/migrations/v1_23/v304.go b/models/migrations/v1_23/v304.go
index 65cffedbd9..35d4d4881a 100644
--- a/models/migrations/v1_23/v304.go
+++ b/models/migrations/v1_23/v304.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import "xorm.io/xorm"
@@ -9,5 +9,8 @@ func AddIndexForReleaseSha1(x *xorm.Engine) error {
type Release struct {
Sha1 string `xorm:"INDEX VARCHAR(64)"`
}
- return x.Sync(new(Release))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreDropIndices: true,
+ }, new(Release))
+ return err
}
diff --git a/models/migrations/v1_23/v304_test.go b/models/migrations/v1_23/v304_test.go
new file mode 100644
index 0000000000..c3dfa5e7e7
--- /dev/null
+++ b/models/migrations/v1_23/v304_test.go
@@ -0,0 +1,40 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_23
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/migrations/base"
+ "code.gitea.io/gitea/modules/timeutil"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_AddIndexForReleaseSha1(t *testing.T) {
+ type Release struct {
+ ID int64 `xorm:"pk autoincr"`
+ RepoID int64 `xorm:"INDEX UNIQUE(n)"`
+ PublisherID int64 `xorm:"INDEX"`
+ TagName string `xorm:"INDEX UNIQUE(n)"`
+ OriginalAuthor string
+ OriginalAuthorID int64 `xorm:"index"`
+ LowerTagName string
+ Target string
+ Title string
+ Sha1 string `xorm:"VARCHAR(64)"`
+ NumCommits int64
+ Note string `xorm:"TEXT"`
+ IsDraft bool `xorm:"NOT NULL DEFAULT false"`
+ IsPrerelease bool `xorm:"NOT NULL DEFAULT false"`
+ IsTag bool `xorm:"NOT NULL DEFAULT false"` // will be true only if the record is a tag and has no related releases
+ CreatedUnix timeutil.TimeStamp `xorm:"INDEX"`
+ }
+
+ // Prepare and load the testing database
+ x, deferable := base.PrepareTestEnv(t, 0, new(Release))
+ defer deferable()
+
+ assert.NoError(t, AddIndexForReleaseSha1(x))
+}
diff --git a/models/migrations/v1_23/v305.go b/models/migrations/v1_23/v305.go
index 4d881192b2..3762279de1 100644
--- a/models/migrations/v1_23/v305.go
+++ b/models/migrations/v1_23/v305.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_23/v306.go b/models/migrations/v1_23/v306.go
index 276b438e95..c5c89dbeb8 100644
--- a/models/migrations/v1_23/v306.go
+++ b/models/migrations/v1_23/v306.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import "xorm.io/xorm"
@@ -9,5 +9,9 @@ func AddBlockAdminMergeOverrideBranchProtection(x *xorm.Engine) error {
type ProtectedBranch struct {
BlockAdminMergeOverride bool `xorm:"NOT NULL DEFAULT false"`
}
- return x.Sync(new(ProtectedBranch))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(ProtectedBranch))
+ return err
}
diff --git a/models/migrations/v1_23/v307.go b/models/migrations/v1_23/v307.go
index ef7f5f2c3f..54a69d250b 100644
--- a/models/migrations/v1_23/v307.go
+++ b/models/migrations/v1_23/v307.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_23/v308.go b/models/migrations/v1_23/v308.go
index 1e8a9b0af2..695fdfcc2d 100644
--- a/models/migrations/v1_23/v308.go
+++ b/models/migrations/v1_23/v308.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_23/v309.go b/models/migrations/v1_23/v309.go
index 5b39398443..e629b718a8 100644
--- a/models/migrations/v1_23/v309.go
+++ b/models/migrations/v1_23/v309.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_23/v310.go b/models/migrations/v1_23/v310.go
index 394417f5a0..074b1c54d3 100644
--- a/models/migrations/v1_23/v310.go
+++ b/models/migrations/v1_23/v310.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"xorm.io/xorm"
@@ -12,5 +12,9 @@ func AddPriorityToProtectedBranch(x *xorm.Engine) error {
Priority int64 `xorm:"NOT NULL DEFAULT 0"`
}
- return x.Sync(new(ProtectedBranch))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(ProtectedBranch))
+ return err
}
diff --git a/models/migrations/v1_23/v311.go b/models/migrations/v1_23/v311.go
index 0fc1ac8c0e..ef48085c79 100644
--- a/models/migrations/v1_23/v311.go
+++ b/models/migrations/v1_23/v311.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_23 //nolint
+package v1_23
import (
"xorm.io/xorm"
@@ -11,6 +11,9 @@ func AddTimeEstimateColumnToIssueTable(x *xorm.Engine) error {
type Issue struct {
TimeEstimate int64 `xorm:"NOT NULL DEFAULT 0"`
}
-
- return x.Sync(new(Issue))
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(Issue))
+ return err
}
diff --git a/models/migrations/v1_24/v312.go b/models/migrations/v1_24/v312.go
new file mode 100644
index 0000000000..823b0eae40
--- /dev/null
+++ b/models/migrations/v1_24/v312.go
@@ -0,0 +1,25 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "xorm.io/xorm"
+)
+
+type pullAutoMerge struct {
+ DeleteBranchAfterMerge bool
+}
+
+// TableName return database table name for xorm
+func (pullAutoMerge) TableName() string {
+ return "pull_auto_merge"
+}
+
+func AddDeleteBranchAfterMergeForAutoMerge(x *xorm.Engine) error {
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(pullAutoMerge))
+ return err
+}
diff --git a/models/migrations/v1_24/v313.go b/models/migrations/v1_24/v313.go
new file mode 100644
index 0000000000..7e6cda6bfd
--- /dev/null
+++ b/models/migrations/v1_24/v313.go
@@ -0,0 +1,31 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "code.gitea.io/gitea/models/migrations/base"
+
+ "xorm.io/xorm"
+)
+
+func MovePinOrderToTableIssuePin(x *xorm.Engine) error {
+ type IssuePin struct {
+ ID int64 `xorm:"pk autoincr"`
+ RepoID int64 `xorm:"UNIQUE(s) NOT NULL"`
+ IssueID int64 `xorm:"UNIQUE(s) NOT NULL"`
+ IsPull bool `xorm:"NOT NULL"`
+ PinOrder int `xorm:"DEFAULT 0"`
+ }
+
+ if err := x.Sync(new(IssuePin)); err != nil {
+ return err
+ }
+
+ if _, err := x.Exec("INSERT INTO issue_pin (repo_id, issue_id, is_pull, pin_order) SELECT repo_id, id, is_pull, pin_order FROM issue WHERE pin_order > 0"); err != nil {
+ return err
+ }
+ sess := x.NewSession()
+ defer sess.Close()
+ return base.DropTableColumns(sess, "issue", "pin_order")
+}
diff --git a/models/migrations/v1_24/v314.go b/models/migrations/v1_24/v314.go
new file mode 100644
index 0000000000..51cb2e34aa
--- /dev/null
+++ b/models/migrations/v1_24/v314.go
@@ -0,0 +1,19 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "xorm.io/xorm"
+)
+
+func UpdateOwnerIDOfRepoLevelActionsTables(x *xorm.Engine) error {
+ if _, err := x.Exec("UPDATE `action_runner` SET `owner_id` = 0 WHERE `repo_id` > 0 AND `owner_id` > 0"); err != nil {
+ return err
+ }
+ if _, err := x.Exec("UPDATE `action_variable` SET `owner_id` = 0 WHERE `repo_id` > 0 AND `owner_id` > 0"); err != nil {
+ return err
+ }
+ _, err := x.Exec("UPDATE `secret` SET `owner_id` = 0 WHERE `repo_id` > 0 AND `owner_id` > 0")
+ return err
+}
diff --git a/models/migrations/v1_24/v315.go b/models/migrations/v1_24/v315.go
new file mode 100644
index 0000000000..52b9b44785
--- /dev/null
+++ b/models/migrations/v1_24/v315.go
@@ -0,0 +1,19 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "xorm.io/xorm"
+)
+
+func AddEphemeralToActionRunner(x *xorm.Engine) error {
+ type ActionRunner struct {
+ Ephemeral bool `xorm:"ephemeral NOT NULL DEFAULT false"`
+ }
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(ActionRunner))
+ return err
+}
diff --git a/models/migrations/v1_24/v316.go b/models/migrations/v1_24/v316.go
new file mode 100644
index 0000000000..14e888f9ee
--- /dev/null
+++ b/models/migrations/v1_24/v316.go
@@ -0,0 +1,24 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "xorm.io/xorm"
+)
+
+func AddDescriptionForSecretsAndVariables(x *xorm.Engine) error {
+ type Secret struct {
+ Description string `xorm:"TEXT"`
+ }
+
+ type ActionVariable struct {
+ Description string `xorm:"TEXT"`
+ }
+
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(Secret), new(ActionVariable))
+ return err
+}
diff --git a/models/migrations/v1_24/v317.go b/models/migrations/v1_24/v317.go
new file mode 100644
index 0000000000..a13db2dd27
--- /dev/null
+++ b/models/migrations/v1_24/v317.go
@@ -0,0 +1,56 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "code.gitea.io/gitea/modules/timeutil"
+
+ "xorm.io/xorm"
+ "xorm.io/xorm/schemas"
+)
+
+type improveActionTableIndicesAction struct {
+ ID int64 `xorm:"pk autoincr"`
+ UserID int64 `xorm:"INDEX"` // Receiver user id.
+ OpType int
+ ActUserID int64 // Action user id.
+ RepoID int64
+ CommentID int64 `xorm:"INDEX"`
+ IsDeleted bool `xorm:"NOT NULL DEFAULT false"`
+ RefName string
+ IsPrivate bool `xorm:"NOT NULL DEFAULT false"`
+ Content string `xorm:"TEXT"`
+ CreatedUnix timeutil.TimeStamp `xorm:"created"`
+}
+
+// TableName sets the name of this table
+func (*improveActionTableIndicesAction) TableName() string {
+ return "action"
+}
+
+// TableIndices implements xorm's TableIndices interface
+func (a *improveActionTableIndicesAction) TableIndices() []*schemas.Index {
+ repoIndex := schemas.NewIndex("r_u_d", schemas.IndexType)
+ repoIndex.AddColumn("repo_id", "user_id", "is_deleted")
+
+ actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
+ actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
+
+ cudIndex := schemas.NewIndex("c_u_d", schemas.IndexType)
+ cudIndex.AddColumn("created_unix", "user_id", "is_deleted")
+
+ cuIndex := schemas.NewIndex("c_u", schemas.IndexType)
+ cuIndex.AddColumn("user_id", "is_deleted")
+
+ actUserUserIndex := schemas.NewIndex("au_c_u", schemas.IndexType)
+ actUserUserIndex.AddColumn("act_user_id", "created_unix", "user_id")
+
+ indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex, cuIndex, actUserUserIndex}
+
+ return indices
+}
+
+func AddNewIndexForUserDashboard(x *xorm.Engine) error {
+ return x.Sync(new(improveActionTableIndicesAction))
+}
diff --git a/models/migrations/v1_24/v318.go b/models/migrations/v1_24/v318.go
new file mode 100644
index 0000000000..9b4a540960
--- /dev/null
+++ b/models/migrations/v1_24/v318.go
@@ -0,0 +1,21 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "code.gitea.io/gitea/models/perm"
+
+ "xorm.io/xorm"
+)
+
+func AddRepoUnitAnonymousAccessMode(x *xorm.Engine) error {
+ type RepoUnit struct { //revive:disable-line:exported
+ AnonymousAccessMode perm.AccessMode `xorm:"NOT NULL DEFAULT 0"`
+ }
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(RepoUnit))
+ return err
+}
diff --git a/models/migrations/v1_24/v319.go b/models/migrations/v1_24/v319.go
new file mode 100644
index 0000000000..648081f74e
--- /dev/null
+++ b/models/migrations/v1_24/v319.go
@@ -0,0 +1,19 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "xorm.io/xorm"
+)
+
+func AddExclusiveOrderColumnToLabelTable(x *xorm.Engine) error {
+ type Label struct {
+ ExclusiveOrder int `xorm:"DEFAULT 0"`
+ }
+ _, err := x.SyncWithOptions(xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ }, new(Label))
+ return err
+}
diff --git a/models/migrations/v1_24/v320.go b/models/migrations/v1_24/v320.go
new file mode 100644
index 0000000000..ebef71939c
--- /dev/null
+++ b/models/migrations/v1_24/v320.go
@@ -0,0 +1,57 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_24
+
+import (
+ "code.gitea.io/gitea/modules/json"
+
+ "xorm.io/xorm"
+)
+
+func MigrateSkipTwoFactor(x *xorm.Engine) error {
+ type LoginSource struct {
+ TwoFactorPolicy string `xorm:"two_factor_policy NOT NULL DEFAULT ''"`
+ }
+ _, err := x.SyncWithOptions(
+ xorm.SyncOptions{
+ IgnoreConstrains: true,
+ IgnoreIndices: true,
+ },
+ new(LoginSource),
+ )
+ if err != nil {
+ return err
+ }
+
+ type LoginSourceSimple struct {
+ ID int64
+ Cfg string
+ }
+
+ var loginSources []LoginSourceSimple
+ err = x.Table("login_source").Find(&loginSources)
+ if err != nil {
+ return err
+ }
+
+ for _, source := range loginSources {
+ if source.Cfg == "" {
+ continue
+ }
+
+ var cfg map[string]any
+ err = json.Unmarshal([]byte(source.Cfg), &cfg)
+ if err != nil {
+ return err
+ }
+
+ if cfg["SkipLocalTwoFA"] == true {
+ _, err = x.Exec("UPDATE login_source SET two_factor_policy = 'skip' WHERE id = ?", source.ID)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
diff --git a/models/migrations/v1_6/v70.go b/models/migrations/v1_6/v70.go
index 74434a84a1..41f0966942 100644
--- a/models/migrations/v1_6/v70.go
+++ b/models/migrations/v1_6/v70.go
@@ -1,7 +1,7 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_6 //nolint
+package v1_6
import (
"fmt"
diff --git a/models/migrations/v1_6/v71.go b/models/migrations/v1_6/v71.go
index 586187228b..2b11f57c92 100644
--- a/models/migrations/v1_6/v71.go
+++ b/models/migrations/v1_6/v71.go
@@ -1,7 +1,7 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_6 //nolint
+package v1_6
import (
"fmt"
diff --git a/models/migrations/v1_6/v72.go b/models/migrations/v1_6/v72.go
index 04cef9a170..9fad88a1b6 100644
--- a/models/migrations/v1_6/v72.go
+++ b/models/migrations/v1_6/v72.go
@@ -1,7 +1,7 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_6 //nolint
+package v1_6
import (
"fmt"
diff --git a/models/migrations/v1_7/v73.go b/models/migrations/v1_7/v73.go
index b5a748aae3..e0b7a28537 100644
--- a/models/migrations/v1_7/v73.go
+++ b/models/migrations/v1_7/v73.go
@@ -1,7 +1,7 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_7 //nolint
+package v1_7
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_7/v74.go b/models/migrations/v1_7/v74.go
index f0567e3c9b..376be37a24 100644
--- a/models/migrations/v1_7/v74.go
+++ b/models/migrations/v1_7/v74.go
@@ -1,7 +1,7 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_7 //nolint
+package v1_7
import "xorm.io/xorm"
diff --git a/models/migrations/v1_7/v75.go b/models/migrations/v1_7/v75.go
index fa7430970c..ef11575466 100644
--- a/models/migrations/v1_7/v75.go
+++ b/models/migrations/v1_7/v75.go
@@ -1,7 +1,7 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_7 //nolint
+package v1_7
import (
"xorm.io/builder"
diff --git a/models/migrations/v1_8/v76.go b/models/migrations/v1_8/v76.go
index d3fbd94deb..81e9307549 100644
--- a/models/migrations/v1_8/v76.go
+++ b/models/migrations/v1_8/v76.go
@@ -1,7 +1,7 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_8 //nolint
+package v1_8
import (
"fmt"
diff --git a/models/migrations/v1_8/v77.go b/models/migrations/v1_8/v77.go
index 8b19993924..4fe5ebe635 100644
--- a/models/migrations/v1_8/v77.go
+++ b/models/migrations/v1_8/v77.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_8 //nolint
+package v1_8
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_8/v78.go b/models/migrations/v1_8/v78.go
index 8f041c1484..e67f464131 100644
--- a/models/migrations/v1_8/v78.go
+++ b/models/migrations/v1_8/v78.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_8 //nolint
+package v1_8
import (
"code.gitea.io/gitea/models/migrations/base"
diff --git a/models/migrations/v1_8/v79.go b/models/migrations/v1_8/v79.go
index eb3a9ed0f4..3f50114d5a 100644
--- a/models/migrations/v1_8/v79.go
+++ b/models/migrations/v1_8/v79.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_8 //nolint
+package v1_8
import (
"code.gitea.io/gitea/modules/setting"
diff --git a/models/migrations/v1_8/v80.go b/models/migrations/v1_8/v80.go
index cebbbead28..6f9df47a93 100644
--- a/models/migrations/v1_8/v80.go
+++ b/models/migrations/v1_8/v80.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_8 //nolint
+package v1_8
import "xorm.io/xorm"
diff --git a/models/migrations/v1_8/v81.go b/models/migrations/v1_8/v81.go
index a100dc1ef7..3c2acc6458 100644
--- a/models/migrations/v1_8/v81.go
+++ b/models/migrations/v1_8/v81.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_8 //nolint
+package v1_8
import (
"fmt"
diff --git a/models/migrations/v1_9/v82.go b/models/migrations/v1_9/v82.go
index 26806dd645..c685d3b86b 100644
--- a/models/migrations/v1_9/v82.go
+++ b/models/migrations/v1_9/v82.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_9 //nolint
+package v1_9
import (
"fmt"
diff --git a/models/migrations/v1_9/v83.go b/models/migrations/v1_9/v83.go
index 10e6c45875..a0cd57f7c5 100644
--- a/models/migrations/v1_9/v83.go
+++ b/models/migrations/v1_9/v83.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_9 //nolint
+package v1_9
import (
"code.gitea.io/gitea/modules/timeutil"
diff --git a/models/migrations/v1_9/v84.go b/models/migrations/v1_9/v84.go
index c7155fe9cf..423915ae57 100644
--- a/models/migrations/v1_9/v84.go
+++ b/models/migrations/v1_9/v84.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_9 //nolint
+package v1_9
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_9/v85.go b/models/migrations/v1_9/v85.go
index a23d7c5d6e..48e1cd5dc4 100644
--- a/models/migrations/v1_9/v85.go
+++ b/models/migrations/v1_9/v85.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_9 //nolint
+package v1_9
import (
"fmt"
diff --git a/models/migrations/v1_9/v86.go b/models/migrations/v1_9/v86.go
index cf2725d158..9464ff0cf6 100644
--- a/models/migrations/v1_9/v86.go
+++ b/models/migrations/v1_9/v86.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_9 //nolint
+package v1_9
import (
"xorm.io/xorm"
diff --git a/models/migrations/v1_9/v87.go b/models/migrations/v1_9/v87.go
index fa01b6e5e3..81a4ebf80d 100644
--- a/models/migrations/v1_9/v87.go
+++ b/models/migrations/v1_9/v87.go
@@ -1,7 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package v1_9 //nolint
+package v1_9
import (
"xorm.io/xorm"
diff --git a/models/organization/org.go b/models/organization/org.go
index 3e55a36758..0f3aef146c 100644
--- a/models/organization/org.go
+++ b/models/organization/org.go
@@ -178,12 +178,6 @@ func (org *Organization) HomeLink() string {
return org.AsUser().HomeLink()
}
-// CanCreateRepo returns if user login can create a repository
-// NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised
-func (org *Organization) CanCreateRepo() bool {
- return org.AsUser().CanCreateRepo()
-}
-
// FindOrgMembersOpts represensts find org members conditions
type FindOrgMembersOpts struct {
db.ListOptions
@@ -608,8 +602,3 @@ func getUserTeamIDsQueryBuilder(orgID, userID int64) *builder.Builder {
"team_user.uid": userID,
})
}
-
-// TeamsWithAccessToRepo returns all teams that have given access level to the repository.
-func (org *Organization) TeamsWithAccessToRepo(ctx context.Context, repoID int64, mode perm.AccessMode) ([]*Team, error) {
- return GetTeamsWithAccessToRepo(ctx, org.ID, repoID, mode)
-}
diff --git a/models/organization/org_list.go b/models/organization/org_list.go
index 4c4168af1f..81457191fe 100644
--- a/models/organization/org_list.go
+++ b/models/organization/org_list.go
@@ -50,8 +50,8 @@ type SearchOrganizationsOptions struct {
// FindOrgOptions finds orgs options
type FindOrgOptions struct {
db.ListOptions
- UserID int64
- IncludePrivate bool
+ UserID int64
+ IncludeVisibility structs.VisibleType
}
func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
@@ -65,11 +65,10 @@ func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
func (opts FindOrgOptions) ToConds() builder.Cond {
var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeOrganization}
if opts.UserID > 0 {
- cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
- }
- if !opts.IncludePrivate {
- cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic})
+ cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludeVisibility == structs.VisibleTypePrivate)))
}
+ // public=0, limited=1, private=2
+ cond = cond.And(builder.Lte{"`user`.visibility": opts.IncludeVisibility})
return cond
}
@@ -77,6 +76,16 @@ func (opts FindOrgOptions) ToOrders() string {
return "`user`.lower_name ASC"
}
+func DoerViewOtherVisibility(doer, other *user_model.User) structs.VisibleType {
+ if doer == nil || other == nil {
+ return structs.VisibleTypePublic
+ }
+ if doer.IsAdmin || doer.ID == other.ID {
+ return structs.VisibleTypePrivate
+ }
+ return structs.VisibleTypeLimited
+}
+
// GetOrgsCanCreateRepoByUserID returns a list of organizations where given user ID
// are allowed to create repos.
func GetOrgsCanCreateRepoByUserID(ctx context.Context, userID int64) ([]*Organization, error) {
@@ -124,6 +133,7 @@ func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg,
if err := db.GetEngine(ctx).Select(columnsStr).
Table("user").
Where(builder.In("`user`.`id`", queryUserOrgIDs(user.ID, true))).
+ OrderBy("`user`.lower_name ASC").
Find(&orgs); err != nil {
return nil, err
}
diff --git a/models/organization/org_list_test.go b/models/organization/org_list_test.go
index 0f0f8a4bcd..a2a25c6f91 100644
--- a/models/organization/org_list_test.go
+++ b/models/organization/org_list_test.go
@@ -10,25 +10,32 @@ import (
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/structs"
"github.com/stretchr/testify/assert"
)
-func TestCountOrganizations(t *testing.T) {
+func TestOrgList(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
+ t.Run("CountOrganizations", testCountOrganizations)
+ t.Run("FindOrgs", testFindOrgs)
+ t.Run("GetUserOrgsList", testGetUserOrgsList)
+ t.Run("LoadOrgListTeams", testLoadOrgListTeams)
+ t.Run("DoerViewOtherVisibility", testDoerViewOtherVisibility)
+}
+
+func testCountOrganizations(t *testing.T) {
expected, err := db.GetEngine(db.DefaultContext).Where("type=?", user_model.UserTypeOrganization).Count(&organization.Organization{})
assert.NoError(t, err)
- cnt, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{IncludePrivate: true})
+ cnt, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{IncludeVisibility: structs.VisibleTypePrivate})
assert.NoError(t, err)
assert.Equal(t, expected, cnt)
}
-func TestFindOrgs(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
+func testFindOrgs(t *testing.T) {
orgs, err := db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
- UserID: 4,
- IncludePrivate: true,
+ UserID: 4,
+ IncludeVisibility: structs.VisibleTypePrivate,
})
assert.NoError(t, err)
if assert.Len(t, orgs, 1) {
@@ -36,33 +43,30 @@ func TestFindOrgs(t *testing.T) {
}
orgs, err = db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
- UserID: 4,
- IncludePrivate: false,
+ UserID: 4,
})
assert.NoError(t, err)
assert.Empty(t, orgs)
total, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
- UserID: 4,
- IncludePrivate: true,
+ UserID: 4,
+ IncludeVisibility: structs.VisibleTypePrivate,
})
assert.NoError(t, err)
assert.EqualValues(t, 1, total)
}
-func TestGetUserOrgsList(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
+func testGetUserOrgsList(t *testing.T) {
orgs, err := organization.GetUserOrgsList(db.DefaultContext, &user_model.User{ID: 4})
assert.NoError(t, err)
if assert.Len(t, orgs, 1) {
assert.EqualValues(t, 3, orgs[0].ID)
// repo_id: 3 is in the team, 32 is public, 5 is private with no team
- assert.EqualValues(t, 2, orgs[0].NumRepos)
+ assert.Equal(t, 2, orgs[0].NumRepos)
}
}
-func TestLoadOrgListTeams(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
+func testLoadOrgListTeams(t *testing.T) {
orgs, err := organization.GetUserOrgsList(db.DefaultContext, &user_model.User{ID: 4})
assert.NoError(t, err)
assert.Len(t, orgs, 1)
@@ -71,3 +75,10 @@ func TestLoadOrgListTeams(t *testing.T) {
assert.Len(t, teamsMap, 1)
assert.Len(t, teamsMap[3], 5)
}
+
+func testDoerViewOtherVisibility(t *testing.T) {
+ assert.Equal(t, structs.VisibleTypePublic, organization.DoerViewOtherVisibility(nil, nil))
+ assert.Equal(t, structs.VisibleTypeLimited, organization.DoerViewOtherVisibility(&user_model.User{ID: 1}, &user_model.User{ID: 2}))
+ assert.Equal(t, structs.VisibleTypePrivate, organization.DoerViewOtherVisibility(&user_model.User{ID: 1}, &user_model.User{ID: 1}))
+ assert.Equal(t, structs.VisibleTypePrivate, organization.DoerViewOtherVisibility(&user_model.User{ID: 1, IsAdmin: true}, &user_model.User{ID: 2}))
+}
diff --git a/models/organization/org_test.go b/models/organization/org_test.go
index 2c5b4090df..234325a8cd 100644
--- a/models/organization/org_test.go
+++ b/models/organization/org_test.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/structs"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestUser_IsOwnedBy(t *testing.T) {
@@ -134,7 +135,7 @@ func TestIsOrganizationOwner(t *testing.T) {
test := func(orgID, userID int64, expected bool) {
isOwner, err := organization.IsOrganizationOwner(db.DefaultContext, orgID, userID)
assert.NoError(t, err)
- assert.EqualValues(t, expected, isOwner)
+ assert.Equal(t, expected, isOwner)
}
test(3, 2, true)
test(3, 3, false)
@@ -148,7 +149,7 @@ func TestIsOrganizationMember(t *testing.T) {
test := func(orgID, userID int64, expected bool) {
isMember, err := organization.IsOrganizationMember(db.DefaultContext, orgID, userID)
assert.NoError(t, err)
- assert.EqualValues(t, expected, isMember)
+ assert.Equal(t, expected, isMember)
}
test(3, 2, true)
test(3, 3, false)
@@ -163,7 +164,7 @@ func TestIsPublicMembership(t *testing.T) {
test := func(orgID, userID int64, expected bool) {
isMember, err := organization.IsPublicMembership(db.DefaultContext, orgID, userID)
assert.NoError(t, err)
- assert.EqualValues(t, expected, isMember)
+ assert.Equal(t, expected, isMember)
}
test(3, 2, true)
test(3, 3, false)
@@ -180,9 +181,8 @@ func TestRestrictedUserOrgMembers(t *testing.T) {
ID: 29,
IsRestricted: true,
})
- if !assert.True(t, restrictedUser.IsRestricted) {
- return // ensure fixtures return restricted user
- }
+ // ensure fixtures return restricted user
+ require.True(t, restrictedUser.IsRestricted)
testCases := []struct {
name string
@@ -237,7 +237,7 @@ func TestRestrictedUserOrgMembers(t *testing.T) {
memberUIDs = append(memberUIDs, member.UID)
}
slices.Sort(memberUIDs)
- assert.EqualValues(t, tc.expectedUIDs, memberUIDs)
+ assert.Equal(t, tc.expectedUIDs, memberUIDs)
})
}
}
@@ -255,7 +255,7 @@ func TestGetOrgUsersByOrgID(t *testing.T) {
sort.Slice(orgUsers, func(i, j int) bool {
return orgUsers[i].ID < orgUsers[j].ID
})
- assert.EqualValues(t, []*organization.OrgUser{{
+ assert.Equal(t, []*organization.OrgUser{{
ID: 1,
OrgID: 3,
UID: 2,
@@ -320,9 +320,9 @@ func TestAccessibleReposEnv_CountRepos(t *testing.T) {
testSuccess := func(userID, expectedCount int64) {
env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
- count, err := env.CountRepos()
+ count, err := env.CountRepos(db.DefaultContext)
assert.NoError(t, err)
- assert.EqualValues(t, expectedCount, count)
+ assert.Equal(t, expectedCount, count)
}
testSuccess(2, 3)
testSuccess(4, 2)
@@ -334,7 +334,7 @@ func TestAccessibleReposEnv_RepoIDs(t *testing.T) {
testSuccess := func(userID int64, expectedRepoIDs []int64) {
env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
- repoIDs, err := env.RepoIDs(1, 100)
+ repoIDs, err := env.RepoIDs(db.DefaultContext)
assert.NoError(t, err)
assert.Equal(t, expectedRepoIDs, repoIDs)
}
@@ -342,32 +342,13 @@ func TestAccessibleReposEnv_RepoIDs(t *testing.T) {
testSuccess(4, []int64{3, 32})
}
-func TestAccessibleReposEnv_Repos(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
- testSuccess := func(userID int64, expectedRepoIDs []int64) {
- env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
- assert.NoError(t, err)
- repos, err := env.Repos(1, 100)
- assert.NoError(t, err)
- expectedRepos := make(repo_model.RepositoryList, len(expectedRepoIDs))
- for i, repoID := range expectedRepoIDs {
- expectedRepos[i] = unittest.AssertExistsAndLoadBean(t,
- &repo_model.Repository{ID: repoID})
- }
- assert.Equal(t, expectedRepos, repos)
- }
- testSuccess(2, []int64{3, 5, 32})
- testSuccess(4, []int64{3, 32})
-}
-
func TestAccessibleReposEnv_MirrorRepos(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
testSuccess := func(userID int64, expectedRepoIDs []int64) {
env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
- repos, err := env.MirrorRepos()
+ repos, err := env.MirrorRepos(db.DefaultContext)
assert.NoError(t, err)
expectedRepos := make(repo_model.RepositoryList, len(expectedRepoIDs))
for i, repoID := range expectedRepoIDs {
diff --git a/models/organization/org_user.go b/models/organization/org_user.go
index 08d936d922..4d7527c15f 100644
--- a/models/organization/org_user.go
+++ b/models/organization/org_user.go
@@ -78,7 +78,7 @@ func IsOrganizationAdmin(ctx context.Context, orgID, uid int64) (bool, error) {
return false, err
}
for _, t := range teams {
- if t.AccessMode >= perm.AccessModeAdmin {
+ if t.HasAdminAccess() {
return true, nil
}
}
diff --git a/models/organization/org_user_test.go b/models/organization/org_user_test.go
index c5110b2a34..689544430d 100644
--- a/models/organization/org_user_test.go
+++ b/models/organization/org_user_test.go
@@ -139,7 +139,7 @@ func TestAddOrgUser(t *testing.T) {
unittest.AssertExistsAndLoadBean(t, ou)
assert.Equal(t, isPublic, ou.IsPublic)
org = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID})
- assert.EqualValues(t, expectedNumMembers, org.NumMembers)
+ assert.Equal(t, expectedNumMembers, org.NumMembers)
}
setting.Service.DefaultOrgMemberVisible = false
diff --git a/models/organization/org_worktime.go b/models/organization/org_worktime.go
new file mode 100644
index 0000000000..7b57182a8a
--- /dev/null
+++ b/models/organization/org_worktime.go
@@ -0,0 +1,103 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package organization
+
+import (
+ "sort"
+
+ "code.gitea.io/gitea/models/db"
+
+ "xorm.io/builder"
+)
+
+type WorktimeSumByRepos struct {
+ RepoName string
+ SumTime int64
+}
+
+func GetWorktimeByRepos(org *Organization, unitFrom, unixTo int64) (results []WorktimeSumByRepos, err error) {
+ err = db.GetEngine(db.DefaultContext).
+ Select("repository.name AS repo_name, SUM(tracked_time.time) AS sum_time").
+ Table("tracked_time").
+ Join("INNER", "issue", "tracked_time.issue_id = issue.id").
+ Join("INNER", "repository", "issue.repo_id = repository.id").
+ Where(builder.Eq{"repository.owner_id": org.ID}).
+ And(builder.Eq{"tracked_time.deleted": false}).
+ And(builder.Gte{"tracked_time.created_unix": unitFrom}).
+ And(builder.Lte{"tracked_time.created_unix": unixTo}).
+ GroupBy("repository.name").
+ OrderBy("repository.name").
+ Find(&results)
+ return results, err
+}
+
+type WorktimeSumByMilestones struct {
+ RepoName string
+ MilestoneName string
+ MilestoneID int64
+ MilestoneDeadline int64
+ SumTime int64
+ HideRepoName bool
+}
+
+func GetWorktimeByMilestones(org *Organization, unitFrom, unixTo int64) (results []WorktimeSumByMilestones, err error) {
+ err = db.GetEngine(db.DefaultContext).
+ Select("repository.name AS repo_name, milestone.name AS milestone_name, milestone.id AS milestone_id, milestone.deadline_unix as milestone_deadline, SUM(tracked_time.time) AS sum_time").
+ Table("tracked_time").
+ Join("INNER", "issue", "tracked_time.issue_id = issue.id").
+ Join("INNER", "repository", "issue.repo_id = repository.id").
+ Join("LEFT", "milestone", "issue.milestone_id = milestone.id").
+ Where(builder.Eq{"repository.owner_id": org.ID}).
+ And(builder.Eq{"tracked_time.deleted": false}).
+ And(builder.Gte{"tracked_time.created_unix": unitFrom}).
+ And(builder.Lte{"tracked_time.created_unix": unixTo}).
+ GroupBy("repository.name, milestone.name, milestone.deadline_unix, milestone.id").
+ OrderBy("repository.name, milestone.deadline_unix, milestone.id").
+ Find(&results)
+
+ // TODO: pgsql: NULL values are sorted last in default ascending order, so we need to sort them manually again.
+ sort.Slice(results, func(i, j int) bool {
+ if results[i].RepoName != results[j].RepoName {
+ return results[i].RepoName < results[j].RepoName
+ }
+ if results[i].MilestoneDeadline != results[j].MilestoneDeadline {
+ return results[i].MilestoneDeadline < results[j].MilestoneDeadline
+ }
+ return results[i].MilestoneID < results[j].MilestoneID
+ })
+
+ // Show only the first RepoName, for nicer output.
+ prevRepoName := ""
+ for i := 0; i < len(results); i++ {
+ res := &results[i]
+ res.MilestoneDeadline = 0 // clear the deadline because we do not really need it
+ if prevRepoName == res.RepoName {
+ res.HideRepoName = true
+ }
+ prevRepoName = res.RepoName
+ }
+ return results, err
+}
+
+type WorktimeSumByMembers struct {
+ UserName string
+ SumTime int64
+}
+
+func GetWorktimeByMembers(org *Organization, unitFrom, unixTo int64) (results []WorktimeSumByMembers, err error) {
+ err = db.GetEngine(db.DefaultContext).
+ Select("`user`.name AS user_name, SUM(tracked_time.time) AS sum_time").
+ Table("tracked_time").
+ Join("INNER", "issue", "tracked_time.issue_id = issue.id").
+ Join("INNER", "repository", "issue.repo_id = repository.id").
+ Join("INNER", "`user`", "tracked_time.user_id = `user`.id").
+ Where(builder.Eq{"repository.owner_id": org.ID}).
+ And(builder.Eq{"tracked_time.deleted": false}).
+ And(builder.Gte{"tracked_time.created_unix": unitFrom}).
+ And(builder.Lte{"tracked_time.created_unix": unixTo}).
+ GroupBy("`user`.name").
+ OrderBy("sum_time DESC").
+ Find(&results)
+ return results, err
+}
diff --git a/models/organization/team.go b/models/organization/team.go
index 96666da39a..7f3a9b3829 100644
--- a/models/organization/team.go
+++ b/models/organization/team.go
@@ -113,7 +113,7 @@ func (t *Team) LoadUnits(ctx context.Context) (err error) {
// GetUnitNames returns the team units names
func (t *Team) GetUnitNames() (res []string) {
- if t.AccessMode >= perm.AccessModeAdmin {
+ if t.HasAdminAccess() {
return unit.AllUnitKeyNames()
}
@@ -126,7 +126,7 @@ func (t *Team) GetUnitNames() (res []string) {
// GetUnitsMap returns the team units permissions
func (t *Team) GetUnitsMap() map[string]string {
m := make(map[string]string)
- if t.AccessMode >= perm.AccessModeAdmin {
+ if t.HasAdminAccess() {
for _, u := range unit.Units {
m[u.NameKey] = t.AccessMode.ToString()
}
@@ -153,6 +153,10 @@ func (t *Team) IsMember(ctx context.Context, userID int64) bool {
return isMember
}
+func (t *Team) HasAdminAccess() bool {
+ return t.AccessMode >= perm.AccessModeAdmin
+}
+
// LoadMembers returns paginated members in team of organization.
func (t *Team) LoadMembers(ctx context.Context) (err error) {
t.Members, err = GetTeamMembers(ctx, &SearchMembersOptions{
@@ -238,22 +242,6 @@ func GetTeamByID(ctx context.Context, teamID int64) (*Team, error) {
return t, nil
}
-// GetTeamNamesByID returns team's lower name from a list of team ids.
-func GetTeamNamesByID(ctx context.Context, teamIDs []int64) ([]string, error) {
- if len(teamIDs) == 0 {
- return []string{}, nil
- }
-
- var teamNames []string
- err := db.GetEngine(ctx).Table("team").
- Select("lower_name").
- In("id", teamIDs).
- Asc("name").
- Find(&teamNames)
-
- return teamNames, err
-}
-
// IncrTeamRepoNum increases the number of repos for the given team by 1
func IncrTeamRepoNum(ctx context.Context, teamID int64) error {
_, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team))
diff --git a/models/organization/team_list.go b/models/organization/team_list.go
index 6f2a922e95..0274f9c5ba 100644
--- a/models/organization/team_list.go
+++ b/models/organization/team_list.go
@@ -133,5 +133,8 @@ func GetTeamsByOrgIDs(ctx context.Context, orgIDs []int64) (TeamList, error) {
func GetTeamsByIDs(ctx context.Context, teamIDs []int64) (map[int64]*Team, error) {
teams := make(map[int64]*Team, len(teamIDs))
+ if len(teamIDs) == 0 {
+ return teams, nil
+ }
return teams, db.GetEngine(ctx).Where(builder.In("`id`", teamIDs)).Find(&teams)
}
diff --git a/models/organization/team_repo.go b/models/organization/team_repo.go
index 53edd203a8..b3e266dbc7 100644
--- a/models/organization/team_repo.go
+++ b/models/organization/team_repo.go
@@ -9,6 +9,8 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
+
+ "xorm.io/builder"
)
// TeamRepo represents an team-repository relation.
@@ -48,26 +50,27 @@ func RemoveTeamRepo(ctx context.Context, teamID, repoID int64) error {
return err
}
-// GetTeamsWithAccessToRepo returns all teams in an organization that have given access level to the repository.
-func GetTeamsWithAccessToRepo(ctx context.Context, orgID, repoID int64, mode perm.AccessMode) ([]*Team, error) {
+// GetTeamsWithAccessToAnyRepoUnit returns all teams in an organization that have given access level to the repository special unit.
+// This function is only used for finding some teams that can be used as branch protection allowlist or reviewers, it isn't really used for access control.
+// FIXME: TEAM-UNIT-PERMISSION this logic is not complete, search the fixme keyword to see more details
+func GetTeamsWithAccessToAnyRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type, unitTypesMore ...unit.Type) ([]*Team, error) {
teams := make([]*Team, 0, 5)
- return teams, db.GetEngine(ctx).Where("team.authorize >= ?", mode).
- Join("INNER", "team_repo", "team_repo.team_id = team.id").
- And("team_repo.org_id = ?", orgID).
- And("team_repo.repo_id = ?", repoID).
- OrderBy("name").
- Find(&teams)
-}
-// GetTeamsWithAccessToRepoUnit returns all teams in an organization that have given access level to the repository special unit.
-func GetTeamsWithAccessToRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type) ([]*Team, error) {
- teams := make([]*Team, 0, 5)
- return teams, db.GetEngine(ctx).Where("team_unit.access_mode >= ?", mode).
+ sub := builder.Select("team_id").From("team_unit").
+ Where(builder.Expr("team_unit.team_id = team.id")).
+ And(builder.In("team_unit.type", append([]unit.Type{unitType}, unitTypesMore...))).
+ And(builder.Expr("team_unit.access_mode >= ?", mode))
+
+ err := db.GetEngine(ctx).
Join("INNER", "team_repo", "team_repo.team_id = team.id").
- Join("INNER", "team_unit", "team_unit.team_id = team.id").
And("team_repo.org_id = ?", orgID).
And("team_repo.repo_id = ?", repoID).
- And("team_unit.type = ?", unitType).
+ And(builder.Or(
+ builder.Expr("team.authorize >= ?", mode),
+ builder.In("team.id", sub),
+ )).
OrderBy("name").
Find(&teams)
+
+ return teams, err
}
diff --git a/models/organization/team_repo_test.go b/models/organization/team_repo_test.go
index c0d6750df9..73a06a93c2 100644
--- a/models/organization/team_repo_test.go
+++ b/models/organization/team_repo_test.go
@@ -22,7 +22,7 @@ func TestGetTeamsWithAccessToRepoUnit(t *testing.T) {
org41 := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 41})
repo61 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 61})
- teams, err := organization.GetTeamsWithAccessToRepoUnit(db.DefaultContext, org41.ID, repo61.ID, perm.AccessModeRead, unit.TypePullRequests)
+ teams, err := organization.GetTeamsWithAccessToAnyRepoUnit(db.DefaultContext, org41.ID, repo61.ID, perm.AccessModeRead, unit.TypePullRequests)
assert.NoError(t, err)
if assert.Len(t, teams, 2) {
assert.EqualValues(t, 21, teams[0].ID)
diff --git a/models/organization/team_test.go b/models/organization/team_test.go
index deaabbfa2c..b0bf842584 100644
--- a/models/organization/team_test.go
+++ b/models/organization/team_test.go
@@ -77,7 +77,7 @@ func TestGetTeam(t *testing.T) {
testSuccess := func(orgID int64, name string) {
team, err := organization.GetTeam(db.DefaultContext, orgID, name)
assert.NoError(t, err)
- assert.EqualValues(t, orgID, team.OrgID)
+ assert.Equal(t, orgID, team.OrgID)
assert.Equal(t, name, team.Name)
}
testSuccess(3, "Owners")
@@ -95,7 +95,7 @@ func TestGetTeamByID(t *testing.T) {
testSuccess := func(teamID int64) {
team, err := organization.GetTeamByID(db.DefaultContext, teamID)
assert.NoError(t, err)
- assert.EqualValues(t, teamID, team.ID)
+ assert.Equal(t, teamID, team.ID)
}
testSuccess(1)
testSuccess(2)
@@ -163,7 +163,7 @@ func TestGetUserOrgTeams(t *testing.T) {
teams, err := organization.GetUserOrgTeams(db.DefaultContext, orgID, userID)
assert.NoError(t, err)
for _, team := range teams {
- assert.EqualValues(t, orgID, team.OrgID)
+ assert.Equal(t, orgID, team.OrgID)
unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{TeamID: team.ID, UID: userID})
}
}
diff --git a/models/packages/container/search.go b/models/packages/container/search.go
index 5df35117ce..9321d9eb41 100644
--- a/models/packages/container/search.go
+++ b/models/packages/container/search.go
@@ -25,6 +25,7 @@ type BlobSearchOptions struct {
Digest string
Tag string
IsManifest bool
+ OnlyLead bool
Repository string
}
@@ -43,7 +44,10 @@ func (opts *BlobSearchOptions) toConds() builder.Cond {
cond = cond.And(builder.Eq{"package_version.lower_version": strings.ToLower(opts.Tag)})
}
if opts.IsManifest {
- cond = cond.And(builder.Eq{"package_file.lower_name": ManifestFilename})
+ cond = cond.And(builder.Eq{"package_file.lower_name": container_module.ManifestFilename})
+ }
+ if opts.OnlyLead {
+ cond = cond.And(builder.Eq{"package_file.is_lead": true})
}
if opts.Digest != "" {
var propsCond builder.Cond = builder.Eq{
@@ -73,11 +77,9 @@ func GetContainerBlob(ctx context.Context, opts *BlobSearchOptions) (*packages.P
pfds, err := getContainerBlobsLimit(ctx, opts, 1)
if err != nil {
return nil, err
- }
- if len(pfds) != 1 {
+ } else if len(pfds) == 0 {
return nil, ErrContainerBlobNotExist
}
-
return pfds[0], nil
}
@@ -233,7 +235,7 @@ func SearchImageTags(ctx context.Context, opts *ImageTagsSearchOptions) ([]*pack
func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([]*packages.PackageFile, error) {
var cond builder.Cond = builder.Eq{
"package_version.is_internal": true,
- "package_version.lower_version": UploadVersion,
+ "package_version.lower_version": container_module.UploadVersion,
"package.type": packages.TypeContainer,
}
cond = cond.And(builder.Lt{"package_file.created_unix": time.Now().Add(-olderThan).Unix()})
diff --git a/models/packages/descriptor.go b/models/packages/descriptor.go
index 803b73c968..2d43dc3046 100644
--- a/models/packages/descriptor.go
+++ b/models/packages/descriptor.go
@@ -11,6 +11,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/packages/alpine"
"code.gitea.io/gitea/modules/packages/arch"
@@ -102,19 +103,26 @@ func (pd *PackageDescriptor) CalculateBlobSize() int64 {
// GetPackageDescriptor gets the package description for a version
func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDescriptor, error) {
- p, err := GetPackageByID(ctx, pv.PackageID)
+ return GetPackageDescriptorWithCache(ctx, pv, cache.NewEphemeralCache())
+}
+
+func GetPackageDescriptorWithCache(ctx context.Context, pv *PackageVersion, c *cache.EphemeralCache) (*PackageDescriptor, error) {
+ p, err := cache.GetWithEphemeralCache(ctx, c, "package", pv.PackageID, GetPackageByID)
if err != nil {
return nil, err
}
- o, err := user_model.GetUserByID(ctx, p.OwnerID)
+ o, err := cache.GetWithEphemeralCache(ctx, c, "user", p.OwnerID, user_model.GetUserByID)
if err != nil {
return nil, err
}
- repository, err := repo_model.GetRepositoryByID(ctx, p.RepoID)
- if err != nil && !repo_model.IsErrRepoNotExist(err) {
- return nil, err
+ var repository *repo_model.Repository
+ if p.RepoID > 0 {
+ repository, err = cache.GetWithEphemeralCache(ctx, c, "repo", p.RepoID, repo_model.GetRepositoryByID)
+ if err != nil && !repo_model.IsErrRepoNotExist(err) {
+ return nil, err
+ }
}
- creator, err := user_model.GetUserByID(ctx, pv.CreatorID)
+ creator, err := cache.GetWithEphemeralCache(ctx, c, "user", pv.CreatorID, user_model.GetUserByID)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
creator = user_model.NewGhostUser()
@@ -142,9 +150,13 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
return nil, err
}
- pfds, err := GetPackageFileDescriptors(ctx, pfs)
- if err != nil {
- return nil, err
+ pfds := make([]*PackageFileDescriptor, 0, len(pfs))
+ for _, pf := range pfs {
+ pfd, err := getPackageFileDescriptor(ctx, pf, c)
+ if err != nil {
+ return nil, err
+ }
+ pfds = append(pfds, pfd)
}
var metadata any
@@ -194,7 +206,7 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
case TypeVagrant:
metadata = &vagrant.Metadata{}
default:
- panic(fmt.Sprintf("unknown package type: %s", string(p.Type)))
+ panic("unknown package type: " + string(p.Type))
}
if metadata != nil {
if err := json.Unmarshal([]byte(pv.MetadataJSON), &metadata); err != nil {
@@ -218,7 +230,11 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
// GetPackageFileDescriptor gets a package file descriptor for a package file
func GetPackageFileDescriptor(ctx context.Context, pf *PackageFile) (*PackageFileDescriptor, error) {
- pb, err := GetBlobByID(ctx, pf.BlobID)
+ return getPackageFileDescriptor(ctx, pf, cache.NewEphemeralCache())
+}
+
+func getPackageFileDescriptor(ctx context.Context, pf *PackageFile, c *cache.EphemeralCache) (*PackageFileDescriptor, error) {
+ pb, err := cache.GetWithEphemeralCache(ctx, c, "package_file_blob", pf.BlobID, GetBlobByID)
if err != nil {
return nil, err
}
@@ -248,9 +264,13 @@ func GetPackageFileDescriptors(ctx context.Context, pfs []*PackageFile) ([]*Pack
// GetPackageDescriptors gets the package descriptions for the versions
func GetPackageDescriptors(ctx context.Context, pvs []*PackageVersion) ([]*PackageDescriptor, error) {
+ return getPackageDescriptors(ctx, pvs, cache.NewEphemeralCache())
+}
+
+func getPackageDescriptors(ctx context.Context, pvs []*PackageVersion, c *cache.EphemeralCache) ([]*PackageDescriptor, error) {
pds := make([]*PackageDescriptor, 0, len(pvs))
for _, pv := range pvs {
- pd, err := GetPackageDescriptor(ctx, pv)
+ pd, err := GetPackageDescriptorWithCache(ctx, pv, c)
if err != nil {
return nil, err
}
diff --git a/models/packages/nuget/search.go b/models/packages/nuget/search.go
index 7a505ff08f..a4b23f31d5 100644
--- a/models/packages/nuget/search.go
+++ b/models/packages/nuget/search.go
@@ -33,7 +33,7 @@ func SearchVersions(ctx context.Context, opts *packages_model.PackageSearchOptio
Where(cond).
OrderBy("package.name ASC")
if opts.Paginator != nil {
- skip, take := opts.GetSkipTake()
+ skip, take := opts.Paginator.GetSkipTake()
inner = inner.Limit(take, skip)
}
diff --git a/models/packages/package.go b/models/packages/package.go
index 31e1277a6e..38d1cdcf66 100644
--- a/models/packages/package.go
+++ b/models/packages/package.go
@@ -127,7 +127,7 @@ func (pt Type) Name() string {
case TypeVagrant:
return "Vagrant"
}
- panic(fmt.Sprintf("unknown package type: %s", string(pt)))
+ panic("unknown package type: " + string(pt))
}
// SVGName gets the name of the package type svg image
@@ -178,7 +178,7 @@ func (pt Type) SVGName() string {
case TypeVagrant:
return "gitea-vagrant"
}
- panic(fmt.Sprintf("unknown package type: %s", string(pt)))
+ panic("unknown package type: " + string(pt))
}
// Package represents a package
@@ -228,6 +228,11 @@ func SetRepositoryLink(ctx context.Context, packageID, repoID int64) error {
return err
}
+func UnlinkRepository(ctx context.Context, packageID int64) error {
+ _, err := db.GetEngine(ctx).ID(packageID).Cols("repo_id").Update(&Package{RepoID: 0})
+ return err
+}
+
// UnlinkRepositoryFromAllPackages unlinks every package from the repository
func UnlinkRepositoryFromAllPackages(ctx context.Context, repoID int64) error {
_, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Cols("repo_id").Update(&Package{})
diff --git a/models/packages/package_file.go b/models/packages/package_file.go
index 270cb32fdf..bf877485d6 100644
--- a/models/packages/package_file.go
+++ b/models/packages/package_file.go
@@ -115,6 +115,11 @@ func DeleteFileByID(ctx context.Context, fileID int64) error {
return err
}
+func UpdateFile(ctx context.Context, pf *PackageFile, cols []string) error {
+ _, err := db.GetEngine(ctx).ID(pf.ID).Cols(cols...).Update(pf)
+ return err
+}
+
// PackageFileSearchOptions are options for SearchXXX methods
type PackageFileSearchOptions struct {
OwnerID int64
diff --git a/models/packages/package_property.go b/models/packages/package_property.go
index e0170016cf..7ddbfd97e9 100644
--- a/models/packages/package_property.go
+++ b/models/packages/package_property.go
@@ -66,6 +66,20 @@ func UpdateProperty(ctx context.Context, pp *PackageProperty) error {
return err
}
+func InsertOrUpdateProperty(ctx context.Context, refType PropertyType, refID int64, name, value string) error {
+ pp := PackageProperty{RefType: refType, RefID: refID, Name: name}
+ ok, err := db.GetEngine(ctx).Get(&pp)
+ if err != nil {
+ return err
+ }
+ if ok {
+ _, err = db.GetEngine(ctx).Where("ref_type=? AND ref_id=? AND name=?", refType, refID, name).Cols("value").Update(&PackageProperty{Value: value})
+ return err
+ }
+ _, err = InsertProperty(ctx, refType, refID, name, value)
+ return err
+}
+
// DeleteAllProperties deletes all properties of a ref
func DeleteAllProperties(ctx context.Context, refType PropertyType, refID int64) error {
_, err := db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ?", refType, refID).Delete(&PackageProperty{})
@@ -78,8 +92,8 @@ func DeletePropertyByID(ctx context.Context, propertyID int64) error {
return err
}
-// DeletePropertyByName deletes properties by name
-func DeletePropertyByName(ctx context.Context, refType PropertyType, refID int64, name string) error {
+// DeletePropertiesByName deletes properties by name
+func DeletePropertiesByName(ctx context.Context, refType PropertyType, refID int64, name string) error {
_, err := db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ? AND name = ?", refType, refID, name).Delete(&PackageProperty{})
return err
}
diff --git a/models/packages/package_version.go b/models/packages/package_version.go
index 278e8e3a86..5672e0efbf 100644
--- a/models/packages/package_version.go
+++ b/models/packages/package_version.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
+ "xorm.io/xorm"
)
// ErrDuplicatePackageVersion indicates a duplicated package version error
@@ -187,7 +188,7 @@ type PackageSearchOptions struct {
HasFileWithName string // only results are found which are associated with a file with the specific name
HasFiles optional.Option[bool] // only results are found which have associated files
Sort VersionSort
- db.Paginator
+ Paginator db.Paginator
}
func (opts *PackageSearchOptions) ToConds() builder.Cond {
@@ -279,9 +280,19 @@ func (opts *PackageSearchOptions) configureOrderBy(e db.Engine) {
default:
e.Desc("package_version.created_unix")
}
+ e.Desc("package_version.id") // Sort by id for stable order with duplicates in the other field
+}
- // Sort by id for stable order with duplicates in the other field
- e.Asc("package_version.id")
+func searchVersionsBySession(sess *xorm.Session, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
+ opts.configureOrderBy(sess)
+ pvs := make([]*PackageVersion, 0, 10)
+ if opts.Paginator != nil {
+ sess = db.SetSessionPagination(sess, opts.Paginator)
+ count, err := sess.FindAndCount(&pvs)
+ return pvs, count, err
+ }
+ err := sess.Find(&pvs)
+ return pvs, int64(len(pvs)), err
}
// SearchVersions gets all versions of packages matching the search options
@@ -291,16 +302,7 @@ func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*Package
Table("package_version").
Join("INNER", "package", "package.id = package_version.package_id").
Where(opts.ToConds())
-
- opts.configureOrderBy(sess)
-
- if opts.Paginator != nil {
- sess = db.SetSessionPagination(sess, opts)
- }
-
- pvs := make([]*PackageVersion, 0, 10)
- count, err := sess.FindAndCount(&pvs)
- return pvs, count, err
+ return searchVersionsBySession(sess, opts)
}
// SearchLatestVersions gets the latest version of every package matching the search options
@@ -318,15 +320,7 @@ func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*P
Join("INNER", "package", "package.id = package_version.package_id").
Where(builder.In("package_version.id", in))
- opts.configureOrderBy(sess)
-
- if opts.Paginator != nil {
- sess = db.SetSessionPagination(sess, opts)
- }
-
- pvs := make([]*PackageVersion, 0, 10)
- count, err := sess.FindAndCount(&pvs)
- return pvs, count, err
+ return searchVersionsBySession(sess, opts)
}
// ExistVersion checks if a version matching the search options exist
diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go
index 0ed116a132..7de43ecd07 100644
--- a/models/perm/access/repo_permission.go
+++ b/models/perm/access/repo_permission.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)
@@ -25,7 +26,8 @@ type Permission struct {
units []*repo_model.RepoUnit
unitsMode map[unit.Type]perm_model.AccessMode
- everyoneAccessMode map[unit.Type]perm_model.AccessMode
+ everyoneAccessMode map[unit.Type]perm_model.AccessMode // the unit's minimal access mode for every signed-in user
+ anonymousAccessMode map[unit.Type]perm_model.AccessMode // the unit's minimal access mode for anonymous (non-signed-in) user
}
// IsOwner returns true if current user is the owner of repository.
@@ -39,7 +41,8 @@ func (p *Permission) IsAdmin() bool {
}
// HasAnyUnitAccess returns true if the user might have at least one access mode to any unit of this repository.
-// It doesn't count the "everyone access mode".
+// It doesn't count the "public(anonymous/everyone) access mode".
+// TODO: most calls to this function should be replaced with `HasAnyUnitAccessOrPublicAccess`
func (p *Permission) HasAnyUnitAccess() bool {
for _, v := range p.unitsMode {
if v >= perm_model.AccessModeRead {
@@ -49,13 +52,22 @@ func (p *Permission) HasAnyUnitAccess() bool {
return p.AccessMode >= perm_model.AccessModeRead
}
-func (p *Permission) HasAnyUnitAccessOrEveryoneAccess() bool {
+func (p *Permission) HasAnyUnitPublicAccess() bool {
+ for _, v := range p.anonymousAccessMode {
+ if v >= perm_model.AccessModeRead {
+ return true
+ }
+ }
for _, v := range p.everyoneAccessMode {
if v >= perm_model.AccessModeRead {
return true
}
}
- return p.HasAnyUnitAccess()
+ return false
+}
+
+func (p *Permission) HasAnyUnitAccessOrPublicAccess() bool {
+ return p.HasAnyUnitPublicAccess() || p.HasAnyUnitAccess()
}
// HasUnits returns true if the permission contains attached units
@@ -73,14 +85,16 @@ func (p *Permission) GetFirstUnitRepoID() int64 {
}
// UnitAccessMode returns current user access mode to the specify unit of the repository
-// It also considers "everyone access mode"
+// It also considers "public (anonymous/everyone) access mode"
func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
// if the units map contains the access mode, use it, but admin/owner mode could override it
if m, ok := p.unitsMode[unitType]; ok {
return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
}
// if the units map does not contain the access mode, return the default access mode if the unit exists
- unitDefaultAccessMode := max(p.AccessMode, p.everyoneAccessMode[unitType])
+ unitDefaultAccessMode := p.AccessMode
+ unitDefaultAccessMode = max(unitDefaultAccessMode, p.anonymousAccessMode[unitType])
+ unitDefaultAccessMode = max(unitDefaultAccessMode, p.everyoneAccessMode[unitType])
hasUnit := slices.ContainsFunc(p.units, func(u *repo_model.RepoUnit) bool { return u.Type == unitType })
return util.Iif(hasUnit, unitDefaultAccessMode, perm_model.AccessModeNone)
}
@@ -152,7 +166,7 @@ func (p *Permission) ReadableUnitTypes() []unit.Type {
}
func (p *Permission) LogString() string {
- format := "<Permission AccessMode=%s, %d Units, %d UnitsMode(s): [ "
+ format := "<Permission AccessMode=%s, %d Units, %d UnitsMode(s): ["
args := []any{p.AccessMode.ToString(), len(p.units), len(p.unitsMode)}
for i, u := range p.units {
@@ -164,27 +178,77 @@ func (p *Permission) LogString() string {
config = err.Error()
}
}
- format += "\nUnits[%d]: ID: %d RepoID: %d Type: %s Config: %s"
+ format += "\n\tunits[%d]: ID=%d RepoID=%d Type=%s Config=%s"
args = append(args, i, u.ID, u.RepoID, u.Type.LogString(), config)
}
for key, value := range p.unitsMode {
- format += "\nUnitMode[%-v]: %-v"
+ format += "\n\tunitsMode[%-v]: %-v"
args = append(args, key.LogString(), value.LogString())
}
- format += " ]>"
+ format += "\n\tanonymousAccessMode: %-v"
+ args = append(args, p.anonymousAccessMode)
+ format += "\n\teveryoneAccessMode: %-v"
+ args = append(args, p.everyoneAccessMode)
+ format += "\n\t]>"
return fmt.Sprintf(format, args...)
}
-func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
+func applyPublicAccessPermission(unitType unit.Type, accessMode perm_model.AccessMode, modeMap *map[unit.Type]perm_model.AccessMode) {
+ if setting.Repository.ForcePrivate {
+ return
+ }
+ if accessMode >= perm_model.AccessModeRead && accessMode > (*modeMap)[unitType] {
+ if *modeMap == nil {
+ *modeMap = make(map[unit.Type]perm_model.AccessMode)
+ }
+ (*modeMap)[unitType] = accessMode
+ }
+}
+
+func finalProcessRepoUnitPermission(user *user_model.User, perm *Permission) {
+ // apply public (anonymous) access permissions
+ for _, u := range perm.units {
+ applyPublicAccessPermission(u.Type, u.AnonymousAccessMode, &perm.anonymousAccessMode)
+ }
+
if user == nil || user.ID <= 0 {
+ // for anonymous access, it could be:
+ // AccessMode is None or Read, units has repo units, unitModes is nil
return
}
+
+ // apply public (everyone) access permissions
for _, u := range perm.units {
- if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.everyoneAccessMode[u.Type] {
- if perm.everyoneAccessMode == nil {
- perm.everyoneAccessMode = make(map[unit.Type]perm_model.AccessMode)
+ applyPublicAccessPermission(u.Type, u.EveryoneAccessMode, &perm.everyoneAccessMode)
+ }
+
+ if perm.unitsMode == nil {
+ // if unitsMode is not set, then it means that the default p.AccessMode applies to all units
+ return
+ }
+
+ // remove no permission units
+ origPermUnits := perm.units
+ perm.units = make([]*repo_model.RepoUnit, 0, len(perm.units))
+ for _, u := range origPermUnits {
+ shouldKeep := false
+ for t := range perm.unitsMode {
+ if shouldKeep = u.Type == t; shouldKeep {
+ break
+ }
+ }
+ for t := range perm.anonymousAccessMode {
+ if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
+ break
}
- perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
+ }
+ for t := range perm.everyoneAccessMode {
+ if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
+ break
+ }
+ }
+ if shouldKeep {
+ perm.units = append(perm.units, u)
}
}
}
@@ -193,11 +257,9 @@ func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (perm Permission, err error) {
defer func() {
if err == nil {
- applyEveryoneRepoPermission(user, &perm)
- }
- if log.IsTrace() {
- log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
+ finalProcessRepoUnitPermission(user, &perm)
}
+ log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
}()
if err = repo.LoadUnits(ctx); err != nil {
@@ -206,7 +268,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
perm.units = repo.Units
// anonymous user visit private repo.
- // TODO: anonymous user visit public unit of private repo???
if user == nil && repo.IsPrivate {
perm.AccessMode = perm_model.AccessModeNone
return perm, nil
@@ -225,7 +286,8 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
}
// Prevent strangers from checking out public repo of private organization/users
- // Allow user if they are collaborator of a repo within a private user or a private organization but not a member of the organization itself
+ // Allow user if they are a collaborator of a repo within a private user or a private organization but not a member of the organization itself
+ // TODO: rename it to "IsOwnerVisibleToDoer"
if !organization.HasOrgOrUserVisible(ctx, repo.Owner, user) && !isCollaborator {
perm.AccessMode = perm_model.AccessModeNone
return perm, nil
@@ -243,7 +305,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
return perm, nil
}
- // plain user
+ // plain user TODO: this check should be replaced, only need to check collaborator access mode
perm.AccessMode, err = accessLevel(ctx, user, repo)
if err != nil {
return perm, err
@@ -253,6 +315,19 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
return perm, nil
}
+ // now: the owner is visible to doer, if the repo is public, then the min access mode is read
+ minAccessMode := util.Iif(!repo.IsPrivate && !user.IsRestricted, perm_model.AccessModeRead, perm_model.AccessModeNone)
+ perm.AccessMode = max(perm.AccessMode, minAccessMode)
+
+ // get units mode from teams
+ teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
+ if err != nil {
+ return perm, err
+ }
+ if len(teams) == 0 {
+ return perm, nil
+ }
+
perm.unitsMode = make(map[unit.Type]perm_model.AccessMode)
// Collaborators on organization
@@ -262,15 +337,9 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
}
}
- // get units mode from teams
- teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
- if err != nil {
- return perm, err
- }
-
// if user in an owner team
for _, team := range teams {
- if team.AccessMode >= perm_model.AccessModeAdmin {
+ if team.HasAdminAccess() {
perm.AccessMode = perm_model.AccessModeOwner
perm.unitsMode = nil
return perm, nil
@@ -278,29 +347,12 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
}
for _, u := range repo.Units {
- var found bool
for _, team := range teams {
+ unitAccessMode := minAccessMode
if teamMode, exist := team.UnitAccessModeEx(ctx, u.Type); exist {
- perm.unitsMode[u.Type] = max(perm.unitsMode[u.Type], teamMode)
- found = true
- }
- }
-
- // for a public repo on an organization, a non-restricted user has read permission on non-team defined units.
- if !found && !repo.IsPrivate && !user.IsRestricted {
- if _, ok := perm.unitsMode[u.Type]; !ok {
- perm.unitsMode[u.Type] = perm_model.AccessModeRead
- }
- }
- }
-
- // remove no permission units
- perm.units = make([]*repo_model.RepoUnit, 0, len(repo.Units))
- for t := range perm.unitsMode {
- for _, u := range repo.Units {
- if u.Type == t {
- perm.units = append(perm.units, u)
+ unitAccessMode = max(perm.unitsMode[u.Type], unitAccessMode, teamMode)
}
+ perm.unitsMode[u.Type] = unitAccessMode
}
}
@@ -348,7 +400,7 @@ func IsUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *use
}
for _, team := range teams {
- if team.AccessMode >= perm_model.AccessModeAdmin {
+ if team.HasAdminAccess() {
return true, nil
}
}
@@ -357,13 +409,13 @@ func IsUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *use
// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
// user does not have access.
-func AccessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Repository) (perm_model.AccessMode, error) { //nolint
+func AccessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Repository) (perm_model.AccessMode, error) { //nolint:revive // export stutter
return AccessLevelUnit(ctx, user, repo, unit.TypeCode)
}
// AccessLevelUnit returns the Access a user has to a repository's. Will return NoneAccess if the
// user does not have access.
-func AccessLevelUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type) (perm_model.AccessMode, error) { //nolint
+func AccessLevelUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type) (perm_model.AccessMode, error) { //nolint:revive // export stutter
perm, err := GetUserRepoPermission(ctx, repo, user)
if err != nil {
return perm_model.AccessModeNone, err
@@ -471,3 +523,7 @@ func CheckRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *u
return perm.CanRead(unitType)
}
+
+func PermissionNoAccess() Permission {
+ return Permission{AccessMode: perm_model.AccessModeNone}
+}
diff --git a/models/perm/access/repo_permission_test.go b/models/perm/access/repo_permission_test.go
index 50070c4368..c8675b1ded 100644
--- a/models/perm/access/repo_permission_test.go
+++ b/models/perm/access/repo_permission_test.go
@@ -6,12 +6,16 @@ package access
import (
"testing"
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/organization"
perm_model "code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
+ "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestHasAnyUnitAccess(t *testing.T) {
@@ -22,14 +26,21 @@ func TestHasAnyUnitAccess(t *testing.T) {
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
}
assert.False(t, perm.HasAnyUnitAccess())
- assert.False(t, perm.HasAnyUnitAccessOrEveryoneAccess())
+ assert.False(t, perm.HasAnyUnitAccessOrPublicAccess())
perm = Permission{
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
everyoneAccessMode: map[unit.Type]perm_model.AccessMode{unit.TypeIssues: perm_model.AccessModeRead},
}
assert.False(t, perm.HasAnyUnitAccess())
- assert.True(t, perm.HasAnyUnitAccessOrEveryoneAccess())
+ assert.True(t, perm.HasAnyUnitAccessOrPublicAccess())
+
+ perm = Permission{
+ units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
+ anonymousAccessMode: map[unit.Type]perm_model.AccessMode{unit.TypeIssues: perm_model.AccessModeRead},
+ }
+ assert.False(t, perm.HasAnyUnitAccess())
+ assert.True(t, perm.HasAnyUnitAccessOrPublicAccess())
perm = Permission{
AccessMode: perm_model.AccessModeRead,
@@ -43,23 +54,32 @@ func TestHasAnyUnitAccess(t *testing.T) {
assert.True(t, perm.HasAnyUnitAccess())
}
-func TestApplyEveryoneRepoPermission(t *testing.T) {
+func TestApplyPublicAccessRepoPermission(t *testing.T) {
perm := Permission{
AccessMode: perm_model.AccessModeNone,
units: []*repo_model.RepoUnit{
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
}
- applyEveryoneRepoPermission(nil, &perm)
+ finalProcessRepoUnitPermission(nil, &perm)
assert.False(t, perm.CanRead(unit.TypeWiki))
perm = Permission{
AccessMode: perm_model.AccessModeNone,
units: []*repo_model.RepoUnit{
+ {Type: unit.TypeWiki, AnonymousAccessMode: perm_model.AccessModeRead},
+ },
+ }
+ finalProcessRepoUnitPermission(nil, &perm)
+ assert.True(t, perm.CanRead(unit.TypeWiki))
+
+ perm = Permission{
+ AccessMode: perm_model.AccessModeNone,
+ units: []*repo_model.RepoUnit{
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
}
- applyEveryoneRepoPermission(&user_model.User{ID: 0}, &perm)
+ finalProcessRepoUnitPermission(&user_model.User{ID: 0}, &perm)
assert.False(t, perm.CanRead(unit.TypeWiki))
perm = Permission{
@@ -68,7 +88,7 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
}
- applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
+ finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
assert.True(t, perm.CanRead(unit.TypeWiki))
perm = Permission{
@@ -77,20 +97,22 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
}
- applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
+ finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
// it should work the same as "EveryoneAccessMode: none" because the default AccessMode should be applied to units
assert.True(t, perm.CanWrite(unit.TypeWiki))
perm = Permission{
units: []*repo_model.RepoUnit{
+ {Type: unit.TypeCode}, // will be removed
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
unitsMode: map[unit.Type]perm_model.AccessMode{
unit.TypeWiki: perm_model.AccessModeWrite,
},
}
- applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
+ finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
assert.True(t, perm.CanWrite(unit.TypeWiki))
+ assert.Len(t, perm.units, 1)
}
func TestUnitAccessMode(t *testing.T) {
@@ -134,3 +156,45 @@ func TestUnitAccessMode(t *testing.T) {
}
assert.Equal(t, perm_model.AccessModeRead, perm.UnitAccessMode(unit.TypeWiki), "has unit, and map, use map")
}
+
+func TestGetUserRepoPermission(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ ctx := t.Context()
+ repo32 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 32}) // org public repo
+ require.NoError(t, repo32.LoadOwner(ctx))
+ require.True(t, repo32.Owner.IsOrganization())
+
+ require.NoError(t, db.TruncateBeans(ctx, &organization.Team{}, &organization.TeamUser{}, &organization.TeamRepo{}, &organization.TeamUnit{}))
+ org := repo32.Owner
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
+ team := &organization.Team{OrgID: org.ID, LowerName: "test_team"}
+ require.NoError(t, db.Insert(ctx, team))
+
+ t.Run("DoerInTeamWithNoRepo", func(t *testing.T) {
+ require.NoError(t, db.Insert(ctx, &organization.TeamUser{OrgID: org.ID, TeamID: team.ID, UID: user.ID}))
+ perm, err := GetUserRepoPermission(ctx, repo32, user)
+ require.NoError(t, err)
+ assert.Equal(t, perm_model.AccessModeRead, perm.AccessMode)
+ assert.Nil(t, perm.unitsMode) // doer in the team, but has no access to the repo
+ })
+
+ require.NoError(t, db.Insert(ctx, &organization.TeamRepo{OrgID: org.ID, TeamID: team.ID, RepoID: repo32.ID}))
+ require.NoError(t, db.Insert(ctx, &organization.TeamUnit{OrgID: org.ID, TeamID: team.ID, Type: unit.TypeCode, AccessMode: perm_model.AccessModeNone}))
+ t.Run("DoerWithTeamUnitAccessNone", func(t *testing.T) {
+ perm, err := GetUserRepoPermission(ctx, repo32, user)
+ require.NoError(t, err)
+ assert.Equal(t, perm_model.AccessModeRead, perm.AccessMode)
+ assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeCode])
+ assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeIssues])
+ })
+
+ require.NoError(t, db.TruncateBeans(ctx, &organization.TeamUnit{}))
+ require.NoError(t, db.Insert(ctx, &organization.TeamUnit{OrgID: org.ID, TeamID: team.ID, Type: unit.TypeCode, AccessMode: perm_model.AccessModeWrite}))
+ t.Run("DoerWithTeamUnitAccessWrite", func(t *testing.T) {
+ perm, err := GetUserRepoPermission(ctx, repo32, user)
+ require.NoError(t, err)
+ assert.Equal(t, perm_model.AccessModeRead, perm.AccessMode)
+ assert.Equal(t, perm_model.AccessModeWrite, perm.unitsMode[unit.TypeCode])
+ assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeIssues])
+ })
+}
diff --git a/models/project/column.go b/models/project/column.go
index 222f448599..9b9d874997 100644
--- a/models/project/column.go
+++ b/models/project/column.go
@@ -48,6 +48,8 @@ type Column struct {
ProjectID int64 `xorm:"INDEX NOT NULL"`
CreatorID int64 `xorm:"NOT NULL"`
+ NumIssues int64 `xorm:"-"`
+
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
@@ -57,20 +59,6 @@ func (Column) TableName() string {
return "project_board" // TODO: the legacy table name should be project_column
}
-// NumIssues return counter of all issues assigned to the column
-func (c *Column) NumIssues(ctx context.Context) int {
- total, err := db.GetEngine(ctx).Table("project_issue").
- Where("project_id=?", c.ProjectID).
- And("project_board_id=?", c.ID).
- GroupBy("issue_id").
- Cols("issue_id").
- Count()
- if err != nil {
- return 0
- }
- return int(total)
-}
-
func (c *Column) GetIssues(ctx context.Context) ([]*ProjectIssue, error) {
issues := make([]*ProjectIssue, 0, 5)
if err := db.GetEngine(ctx).Where("project_id=?", c.ProjectID).
@@ -159,7 +147,7 @@ func NewColumn(ctx context.Context, column *Column) error {
return err
}
if res.ColumnCount >= maxProjectColumns {
- return fmt.Errorf("NewBoard: maximum number of columns reached")
+ return errors.New("NewBoard: maximum number of columns reached")
}
column.Sorting = int8(util.Iif(res.ColumnCount > 0, res.MaxSorting+1, 0))
_, err := db.GetEngine(ctx).Insert(column)
@@ -184,7 +172,7 @@ func deleteColumnByID(ctx context.Context, columnID int64) error {
}
if column.Default {
- return fmt.Errorf("deleteColumnByID: cannot delete default column")
+ return errors.New("deleteColumnByID: cannot delete default column")
}
// move all issues to the default column
@@ -192,7 +180,7 @@ func deleteColumnByID(ctx context.Context, columnID int64) error {
if err != nil {
return err
}
- defaultColumn, err := project.GetDefaultColumn(ctx)
+ defaultColumn, err := project.MustDefaultColumn(ctx)
if err != nil {
return err
}
@@ -257,8 +245,8 @@ func (p *Project) GetColumns(ctx context.Context) (ColumnList, error) {
return columns, nil
}
-// GetDefaultColumn return default column and ensure only one exists
-func (p *Project) GetDefaultColumn(ctx context.Context) (*Column, error) {
+// getDefaultColumn return default column and ensure only one exists
+func (p *Project) getDefaultColumn(ctx context.Context) (*Column, error) {
var column Column
has, err := db.GetEngine(ctx).
Where("project_id=? AND `default` = ?", p.ID, true).
@@ -270,6 +258,33 @@ func (p *Project) GetDefaultColumn(ctx context.Context) (*Column, error) {
if has {
return &column, nil
}
+ return nil, ErrProjectColumnNotExist{ColumnID: 0}
+}
+
+// MustDefaultColumn returns the default column for a project.
+// If one exists, it is returned
+// If none exists, the first column will be elevated to the default column of this project
+func (p *Project) MustDefaultColumn(ctx context.Context) (*Column, error) {
+ c, err := p.getDefaultColumn(ctx)
+ if err != nil && !IsErrProjectColumnNotExist(err) {
+ return nil, err
+ }
+ if c != nil {
+ return c, nil
+ }
+
+ var column Column
+ has, err := db.GetEngine(ctx).Where("project_id=?", p.ID).OrderBy("sorting, id").Get(&column)
+ if err != nil {
+ return nil, err
+ }
+ if has {
+ column.Default = true
+ if _, err := db.GetEngine(ctx).ID(column.ID).Cols("`default`").Update(&column); err != nil {
+ return nil, err
+ }
+ return &column, nil
+ }
// create a default column if none is found
column = Column{
@@ -321,6 +336,9 @@ func UpdateColumnSorting(ctx context.Context, cl ColumnList) error {
func GetColumnsByIDs(ctx context.Context, projectID int64, columnsIDs []int64) (ColumnList, error) {
columns := make([]*Column, 0, 5)
+ if len(columnsIDs) == 0 {
+ return columns, nil
+ }
if err := db.GetEngine(ctx).
Where("project_id =?", projectID).
In("id", columnsIDs).
diff --git a/models/project/column_test.go b/models/project/column_test.go
index 566667e45d..6a615090a5 100644
--- a/models/project/column_test.go
+++ b/models/project/column_test.go
@@ -20,19 +20,19 @@ func TestGetDefaultColumn(t *testing.T) {
assert.NoError(t, err)
// check if default column was added
- column, err := projectWithoutDefault.GetDefaultColumn(db.DefaultContext)
+ column, err := projectWithoutDefault.MustDefaultColumn(db.DefaultContext)
assert.NoError(t, err)
assert.Equal(t, int64(5), column.ProjectID)
- assert.Equal(t, "Uncategorized", column.Title)
+ assert.Equal(t, "Done", column.Title)
projectWithMultipleDefaults, err := GetProjectByID(db.DefaultContext, 6)
assert.NoError(t, err)
// check if multiple defaults were removed
- column, err = projectWithMultipleDefaults.GetDefaultColumn(db.DefaultContext)
+ column, err = projectWithMultipleDefaults.MustDefaultColumn(db.DefaultContext)
assert.NoError(t, err)
assert.Equal(t, int64(6), column.ProjectID)
- assert.Equal(t, int64(9), column.ID)
+ assert.Equal(t, int64(9), column.ID) // there are 2 default columns in the test data, use the latest one
// set 8 as default column
assert.NoError(t, SetDefaultColumn(db.DefaultContext, column.ProjectID, 8))
@@ -97,9 +97,9 @@ func Test_MoveColumnsOnProject(t *testing.T) {
columnsAfter, err := project1.GetColumns(db.DefaultContext)
assert.NoError(t, err)
assert.Len(t, columnsAfter, 3)
- assert.EqualValues(t, columns[1].ID, columnsAfter[0].ID)
- assert.EqualValues(t, columns[2].ID, columnsAfter[1].ID)
- assert.EqualValues(t, columns[0].ID, columnsAfter[2].ID)
+ assert.Equal(t, columns[1].ID, columnsAfter[0].ID)
+ assert.Equal(t, columns[2].ID, columnsAfter[1].ID)
+ assert.Equal(t, columns[0].ID, columnsAfter[2].ID)
}
func Test_NewColumn(t *testing.T) {
@@ -110,7 +110,7 @@ func Test_NewColumn(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, columns, 3)
- for i := 0; i < maxProjectColumns-3; i++ {
+ for i := range maxProjectColumns - 3 {
err := NewColumn(db.DefaultContext, &Column{
Title: fmt.Sprintf("column-%d", i+4),
ProjectID: project1.ID,
diff --git a/models/project/issue.go b/models/project/issue.go
index b4347a9c2b..47d1537ec7 100644
--- a/models/project/issue.go
+++ b/models/project/issue.go
@@ -5,10 +5,9 @@ package project
import (
"context"
- "fmt"
+ "errors"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
)
@@ -34,51 +33,9 @@ func deleteProjectIssuesByProjectID(ctx context.Context, projectID int64) error
return err
}
-// NumIssues return counter of all issues assigned to a project
-func (p *Project) NumIssues(ctx context.Context) int {
- c, err := db.GetEngine(ctx).Table("project_issue").
- Where("project_id=?", p.ID).
- GroupBy("issue_id").
- Cols("issue_id").
- Count()
- if err != nil {
- log.Error("NumIssues: %v", err)
- return 0
- }
- return int(c)
-}
-
-// NumClosedIssues return counter of closed issues assigned to a project
-func (p *Project) NumClosedIssues(ctx context.Context) int {
- c, err := db.GetEngine(ctx).Table("project_issue").
- Join("INNER", "issue", "project_issue.issue_id=issue.id").
- Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, true).
- Cols("issue_id").
- Count()
- if err != nil {
- log.Error("NumClosedIssues: %v", err)
- return 0
- }
- return int(c)
-}
-
-// NumOpenIssues return counter of open issues assigned to a project
-func (p *Project) NumOpenIssues(ctx context.Context) int {
- c, err := db.GetEngine(ctx).Table("project_issue").
- Join("INNER", "issue", "project_issue.issue_id=issue.id").
- Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, false).
- Cols("issue_id").
- Count()
- if err != nil {
- log.Error("NumOpenIssues: %v", err)
- return 0
- }
- return int(c)
-}
-
func (c *Column) moveIssuesToAnotherColumn(ctx context.Context, newColumn *Column) error {
if c.ProjectID != newColumn.ProjectID {
- return fmt.Errorf("columns have to be in the same project")
+ return errors.New("columns have to be in the same project")
}
if c.ID == newColumn.ID {
diff --git a/models/project/project.go b/models/project/project.go
index edeb0b4742..f516466854 100644
--- a/models/project/project.go
+++ b/models/project/project.go
@@ -97,6 +97,9 @@ type Project struct {
Type Type
RenderedContent template.HTML `xorm:"-"`
+ NumOpenIssues int64 `xorm:"-"`
+ NumClosedIssues int64 `xorm:"-"`
+ NumIssues int64 `xorm:"-"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
@@ -126,11 +129,11 @@ func (p *Project) LoadRepo(ctx context.Context) (err error) {
return err
}
-func ProjectLinkForOrg(org *user_model.User, projectID int64) string { //nolint
+func ProjectLinkForOrg(org *user_model.User, projectID int64) string { //nolint:revive // export stutter
return fmt.Sprintf("%s/-/projects/%d", org.HomeLink(), projectID)
}
-func ProjectLinkForRepo(repo *repo_model.Repository, projectID int64) string { //nolint
+func ProjectLinkForRepo(repo *repo_model.Repository, projectID int64) string { //nolint:revive // export stutter
return fmt.Sprintf("%s/projects/%d", repo.Link(), projectID)
}
@@ -244,6 +247,10 @@ func GetSearchOrderByBySortType(sortType string) db.SearchOrderBy {
return db.SearchOrderByRecentUpdated
case "leastupdate":
return db.SearchOrderByLeastUpdated
+ case "alphabetically":
+ return "title ASC"
+ case "reversealphabetically":
+ return "title DESC"
default:
return db.SearchOrderByNewest
}
diff --git a/models/project/project_test.go b/models/project/project_test.go
index dd421b4659..c2e924e8ae 100644
--- a/models/project/project_test.go
+++ b/models/project/project_test.go
@@ -113,10 +113,10 @@ func TestProjectsSort(t *testing.T) {
OrderBy: GetSearchOrderByBySortType(tt.sortType),
})
assert.NoError(t, err)
- assert.EqualValues(t, int64(6), count)
+ assert.Equal(t, int64(6), count)
if assert.Len(t, projects, 6) {
for i := range projects {
- assert.EqualValues(t, tt.wants[i], projects[i].ID)
+ assert.Equal(t, tt.wants[i], projects[i].ID)
}
}
}
diff --git a/models/pull/automerge.go b/models/pull/automerge.go
index f69fcb60d1..3cafacc3a4 100644
--- a/models/pull/automerge.go
+++ b/models/pull/automerge.go
@@ -15,13 +15,14 @@ import (
// AutoMerge represents a pull request scheduled for merging when checks succeed
type AutoMerge struct {
- ID int64 `xorm:"pk autoincr"`
- PullID int64 `xorm:"UNIQUE"`
- DoerID int64 `xorm:"INDEX NOT NULL"`
- Doer *user_model.User `xorm:"-"`
- MergeStyle repo_model.MergeStyle `xorm:"varchar(30)"`
- Message string `xorm:"LONGTEXT"`
- CreatedUnix timeutil.TimeStamp `xorm:"created"`
+ ID int64 `xorm:"pk autoincr"`
+ PullID int64 `xorm:"UNIQUE"`
+ DoerID int64 `xorm:"INDEX NOT NULL"`
+ Doer *user_model.User `xorm:"-"`
+ MergeStyle repo_model.MergeStyle `xorm:"varchar(30)"`
+ Message string `xorm:"LONGTEXT"`
+ DeleteBranchAfterMerge bool
+ CreatedUnix timeutil.TimeStamp `xorm:"created"`
}
// TableName return database table name for xorm
@@ -49,7 +50,7 @@ func IsErrAlreadyScheduledToAutoMerge(err error) bool {
}
// ScheduleAutoMerge schedules a pull request to be merged when all checks succeed
-func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64, style repo_model.MergeStyle, message string) error {
+func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64, style repo_model.MergeStyle, message string, deleteBranchAfterMerge bool) error {
// Check if we already have a merge scheduled for that pull request
if exists, _, err := GetScheduledMergeByPullID(ctx, pullID); err != nil {
return err
@@ -58,10 +59,11 @@ func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64,
}
_, err := db.GetEngine(ctx).Insert(&AutoMerge{
- DoerID: doer.ID,
- PullID: pullID,
- MergeStyle: style,
- Message: message,
+ DoerID: doer.ID,
+ PullID: pullID,
+ MergeStyle: style,
+ Message: message,
+ DeleteBranchAfterMerge: deleteBranchAfterMerge,
})
return err
}
diff --git a/models/pull/review_state.go b/models/pull/review_state.go
index e46a22a49d..137af00eab 100644
--- a/models/pull/review_state.go
+++ b/models/pull/review_state.go
@@ -6,6 +6,7 @@ package pull
import (
"context"
"fmt"
+ "maps"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
@@ -100,9 +101,7 @@ func mergeFiles(oldFiles, newFiles map[string]ViewedState) map[string]ViewedStat
return oldFiles
}
- for file, viewed := range newFiles {
- oldFiles[file] = viewed
- }
+ maps.Copy(oldFiles, newFiles)
return oldFiles
}
diff --git a/models/renderhelper/commit_checker.go b/models/renderhelper/commit_checker.go
index 4815643e67..407e45fb54 100644
--- a/models/renderhelper/commit_checker.go
+++ b/models/renderhelper/commit_checker.go
@@ -47,7 +47,7 @@ func (c *commitChecker) IsCommitIDExisting(commitID string) bool {
c.gitRepo, c.gitRepoCloser = r, closer
}
- exist = c.gitRepo.IsReferenceExist(commitID) // Don't use IsObjectExist since it doesn't support short hashs with gogit edition.
+ exist = c.gitRepo.IsReferenceExist(commitID) // Don't use IsObjectExist since it doesn't support short hashes with gogit edition.
c.commitCache[commitID] = exist
return exist
}
diff --git a/models/renderhelper/repo_comment.go b/models/renderhelper/repo_comment.go
index 6bd5e91ad1..ae0fbf0abd 100644
--- a/models/renderhelper/repo_comment.go
+++ b/models/renderhelper/repo_comment.go
@@ -28,14 +28,14 @@ func (r *RepoComment) IsCommitIDExisting(commitID string) bool {
return r.commitChecker.IsCommitIDExisting(commitID)
}
-func (r *RepoComment) ResolveLink(link string, likeType markup.LinkType) (finalLink string) {
- switch likeType {
- case markup.LinkTypeApp:
- finalLink = r.ctx.ResolveLinkApp(link)
+func (r *RepoComment) ResolveLink(link, preferLinkType string) string {
+ linkType, link := markup.ParseRenderedLink(link, preferLinkType)
+ switch linkType {
+ case markup.LinkTypeRoot:
+ return r.ctx.ResolveLinkRoot(link)
default:
- finalLink = r.ctx.ResolveLinkRelative(r.repoLink, r.opts.CurrentRefPath, link)
+ return r.ctx.ResolveLinkRelative(r.repoLink, r.opts.CurrentRefPath, link)
}
- return finalLink
}
var _ markup.RenderHelper = (*RepoComment)(nil)
@@ -44,30 +44,31 @@ type RepoCommentOptions struct {
DeprecatedRepoName string // it is only a patch for the non-standard "markup" api
DeprecatedOwnerName string // it is only a patch for the non-standard "markup" api
CurrentRefPath string // eg: "branch/main" or "commit/11223344"
+ FootnoteContextID string // the extra context ID for footnotes, used to avoid conflicts with other footnotes in the same page
}
func NewRenderContextRepoComment(ctx context.Context, repo *repo_model.Repository, opts ...RepoCommentOptions) *markup.RenderContext {
- helper := &RepoComment{
- repoLink: repo.Link(),
- opts: util.OptionalArg(opts),
- }
+ helper := &RepoComment{opts: util.OptionalArg(opts)}
rctx := markup.NewRenderContext(ctx)
helper.ctx = rctx
+ var metas map[string]string
if repo != nil {
helper.repoLink = repo.Link()
helper.commitChecker = newCommitChecker(ctx, repo)
- rctx = rctx.WithMetas(repo.ComposeMetas(ctx))
+ metas = repo.ComposeCommentMetas(ctx)
} else {
- // this is almost dead code, only to pass the incorrect tests
- helper.repoLink = fmt.Sprintf("%s/%s", helper.opts.DeprecatedOwnerName, helper.opts.DeprecatedRepoName)
- rctx = rctx.WithMetas(map[string]string{
- "user": helper.opts.DeprecatedOwnerName,
- "repo": helper.opts.DeprecatedRepoName,
-
- "markdownLineBreakStyle": "comment",
- "markupAllowShortIssuePattern": "true",
- })
+ // repo can be nil when rendering a commit message in user's dashboard feedback whose repository has been deleted
+ metas = map[string]string{}
+ if helper.opts.DeprecatedOwnerName != "" {
+ // this is almost dead code, only to pass the incorrect tests
+ helper.repoLink = fmt.Sprintf("%s/%s", helper.opts.DeprecatedOwnerName, helper.opts.DeprecatedRepoName)
+ metas["user"] = helper.opts.DeprecatedOwnerName
+ metas["repo"] = helper.opts.DeprecatedRepoName
+ }
+ metas["markdownNewLineHardBreak"] = "true"
+ metas["markupAllowShortIssuePattern"] = "true"
}
- rctx = rctx.WithHelper(helper)
+ metas["footnoteContextId"] = helper.opts.FootnoteContextID
+ rctx = rctx.WithMetas(metas).WithHelper(helper)
return rctx
}
diff --git a/models/renderhelper/repo_comment_test.go b/models/renderhelper/repo_comment_test.go
index 01e20b9e02..3b13bff73c 100644
--- a/models/renderhelper/repo_comment_test.go
+++ b/models/renderhelper/repo_comment_test.go
@@ -4,7 +4,6 @@
package renderhelper
import (
- "context"
"testing"
repo_model "code.gitea.io/gitea/models/repo"
@@ -21,7 +20,7 @@ func TestRepoComment(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
t.Run("AutoLink", func(t *testing.T) {
- rctx := NewRenderContextRepoComment(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+ rctx := NewRenderContextRepoComment(t.Context(), repo1).WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
65f1bf27bc3bf70f64657658635e66094edbcb4d
#1
@@ -36,7 +35,7 @@ func TestRepoComment(t *testing.T) {
})
t.Run("AbsoluteAndRelative", func(t *testing.T) {
- rctx := NewRenderContextRepoComment(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+ rctx := NewRenderContextRepoComment(t.Context(), repo1).WithMarkupType(markdown.MarkupName)
// It is Gitea's old behavior, the relative path is resolved to the repo path
// It is different from GitHub, GitHub resolves relative links to current page's path
@@ -56,7 +55,7 @@ func TestRepoComment(t *testing.T) {
})
t.Run("WithCurrentRefPath", func(t *testing.T) {
- rctx := NewRenderContextRepoComment(context.Background(), repo1, RepoCommentOptions{CurrentRefPath: "/commit/1234"}).
+ rctx := NewRenderContextRepoComment(t.Context(), repo1, RepoCommentOptions{CurrentRefPath: "/commit/1234"}).
WithMarkupType(markdown.MarkupName)
// the ref path is only used to render commit message: a commit message is rendered at the commit page with its commit ID path
@@ -73,4 +72,11 @@ func TestRepoComment(t *testing.T) {
<a href="/user2/repo1/commit/1234/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/commit/1234/image" alt="./image"/></a></p>
`, rendered)
})
+
+ t.Run("NoRepo", func(t *testing.T) {
+ rctx := NewRenderContextRepoComment(t.Context(), nil).WithMarkupType(markdown.MarkupName)
+ rendered, err := markup.RenderString(rctx, "any")
+ assert.NoError(t, err)
+ assert.Equal(t, "<p>any</p>\n", rendered)
+ })
}
diff --git a/models/renderhelper/repo_file.go b/models/renderhelper/repo_file.go
index 794828c617..e0375ed280 100644
--- a/models/renderhelper/repo_file.go
+++ b/models/renderhelper/repo_file.go
@@ -29,17 +29,17 @@ func (r *RepoFile) IsCommitIDExisting(commitID string) bool {
return r.commitChecker.IsCommitIDExisting(commitID)
}
-func (r *RepoFile) ResolveLink(link string, likeType markup.LinkType) string {
- finalLink := link
- switch likeType {
- case markup.LinkTypeApp:
- finalLink = r.ctx.ResolveLinkApp(link)
- case markup.LinkTypeDefault:
- finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "src", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link)
+func (r *RepoFile) ResolveLink(link, preferLinkType string) (finalLink string) {
+ linkType, link := markup.ParseRenderedLink(link, preferLinkType)
+ switch linkType {
+ case markup.LinkTypeRoot:
+ finalLink = r.ctx.ResolveLinkRoot(link)
case markup.LinkTypeRaw:
finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "raw", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link)
case markup.LinkTypeMedia:
finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "media", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link)
+ default:
+ finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "src", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link)
}
return finalLink
}
@@ -61,15 +61,13 @@ func NewRenderContextRepoFile(ctx context.Context, repo *repo_model.Repository,
if repo != nil {
helper.repoLink = repo.Link()
helper.commitChecker = newCommitChecker(ctx, repo)
- rctx = rctx.WithMetas(repo.ComposeDocumentMetas(ctx))
+ rctx = rctx.WithMetas(repo.ComposeRepoFileMetas(ctx))
} else {
// this is almost dead code, only to pass the incorrect tests
helper.repoLink = fmt.Sprintf("%s/%s", helper.opts.DeprecatedOwnerName, helper.opts.DeprecatedRepoName)
rctx = rctx.WithMetas(map[string]string{
"user": helper.opts.DeprecatedOwnerName,
"repo": helper.opts.DeprecatedRepoName,
-
- "markdownLineBreakStyle": "document",
})
}
rctx = rctx.WithHelper(helper)
diff --git a/models/renderhelper/repo_file_test.go b/models/renderhelper/repo_file_test.go
index 959648b660..3b48efba3a 100644
--- a/models/renderhelper/repo_file_test.go
+++ b/models/renderhelper/repo_file_test.go
@@ -4,7 +4,6 @@
package renderhelper
import (
- "context"
"testing"
repo_model "code.gitea.io/gitea/models/repo"
@@ -22,7 +21,7 @@ func TestRepoFile(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
t.Run("AutoLink", func(t *testing.T) {
- rctx := NewRenderContextRepoFile(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+ rctx := NewRenderContextRepoFile(t.Context(), repo1).WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
65f1bf27bc3bf70f64657658635e66094edbcb4d
#1
@@ -37,7 +36,7 @@ func TestRepoFile(t *testing.T) {
})
t.Run("AbsoluteAndRelative", func(t *testing.T) {
- rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{CurrentRefPath: "branch/main"}).
+ rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{CurrentRefPath: "branch/main"}).
WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
[/test](/test)
@@ -49,13 +48,13 @@ func TestRepoFile(t *testing.T) {
assert.Equal(t,
`<p><a href="/user2/repo1/src/branch/main/test" rel="nofollow">/test</a>
<a href="/user2/repo1/src/branch/main/test" rel="nofollow">./test</a>
-<a href="/user2/repo1/media/branch/main/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/branch/main/image" alt="/image"/></a>
-<a href="/user2/repo1/media/branch/main/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/branch/main/image" alt="./image"/></a></p>
+<a href="/user2/repo1/src/branch/main/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/branch/main/image" alt="/image"/></a>
+<a href="/user2/repo1/src/branch/main/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/branch/main/image" alt="./image"/></a></p>
`, rendered)
})
t.Run("WithCurrentRefPath", func(t *testing.T) {
- rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{CurrentRefPath: "/commit/1234"}).
+ rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{CurrentRefPath: "/commit/1234"}).
WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
[/test](/test)
@@ -63,12 +62,12 @@ func TestRepoFile(t *testing.T) {
`)
assert.NoError(t, err)
assert.Equal(t, `<p><a href="/user2/repo1/src/commit/1234/test" rel="nofollow">/test</a>
-<a href="/user2/repo1/media/commit/1234/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/commit/1234/image" alt="/image"/></a></p>
+<a href="/user2/repo1/src/commit/1234/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/commit/1234/image" alt="/image"/></a></p>
`, rendered)
})
t.Run("WithCurrentRefPathByTag", func(t *testing.T) {
- rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{
+ rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{
CurrentRefPath: "/commit/1234",
CurrentTreePath: "my-dir",
}).
@@ -78,7 +77,7 @@ func TestRepoFile(t *testing.T) {
<video src="LINK">
`)
assert.NoError(t, err)
- assert.Equal(t, `<a href="/user2/repo1/media/commit/1234/my-dir/LINK" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/commit/1234/my-dir/LINK"/></a>
+ assert.Equal(t, `<a href="/user2/repo1/src/commit/1234/my-dir/LINK" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/commit/1234/my-dir/LINK"/></a>
<video src="/user2/repo1/media/commit/1234/my-dir/LINK">
</video>`, rendered)
})
@@ -89,7 +88,7 @@ func TestRepoFileOrgMode(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
t.Run("Links", func(t *testing.T) {
- rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{
+ rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{
CurrentRefPath: "/commit/1234",
CurrentTreePath: "my-dir",
}).WithRelativePath("my-dir/a.org")
@@ -101,12 +100,12 @@ func TestRepoFileOrgMode(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, `<p>
<a href="https://google.com/" rel="nofollow">https://google.com/</a>
-<a href="/user2/repo1/media/commit/1234/my-dir/ImageLink.svg" rel="nofollow">The Image Desc</a></p>
+<a href="/user2/repo1/src/commit/1234/my-dir/ImageLink.svg" rel="nofollow">The Image Desc</a></p>
`, rendered)
})
t.Run("CodeHighlight", func(t *testing.T) {
- rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{}).WithRelativePath("my-dir/a.org")
+ rctx := NewRenderContextRepoFile(t.Context(), repo1, RepoFileOptions{}).WithRelativePath("my-dir/a.org")
rendered, err := markup.RenderString(rctx, `
#+begin_src c
diff --git a/models/renderhelper/repo_wiki.go b/models/renderhelper/repo_wiki.go
index aa456bf6ce..b75f1b9701 100644
--- a/models/renderhelper/repo_wiki.go
+++ b/models/renderhelper/repo_wiki.go
@@ -30,18 +30,16 @@ func (r *RepoWiki) IsCommitIDExisting(commitID string) bool {
return r.commitChecker.IsCommitIDExisting(commitID)
}
-func (r *RepoWiki) ResolveLink(link string, likeType markup.LinkType) string {
- finalLink := link
- switch likeType {
- case markup.LinkTypeApp:
- finalLink = r.ctx.ResolveLinkApp(link)
- case markup.LinkTypeDefault:
- finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "wiki", r.opts.currentRefPath), r.opts.currentTreePath, link)
- case markup.LinkTypeMedia:
+func (r *RepoWiki) ResolveLink(link, preferLinkType string) (finalLink string) {
+ linkType, link := markup.ParseRenderedLink(link, preferLinkType)
+ switch linkType {
+ case markup.LinkTypeRoot:
+ finalLink = r.ctx.ResolveLinkRoot(link)
+ case markup.LinkTypeMedia, markup.LinkTypeRaw:
finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "wiki/raw", r.opts.currentRefPath), r.opts.currentTreePath, link)
- case markup.LinkTypeRaw: // wiki doesn't use it
+ default:
+ finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "wiki", r.opts.currentRefPath), r.opts.currentTreePath, link)
}
-
return finalLink
}
@@ -70,7 +68,6 @@ func NewRenderContextRepoWiki(ctx context.Context, repo *repo_model.Repository,
"user": helper.opts.DeprecatedOwnerName,
"repo": helper.opts.DeprecatedRepoName,
- "markdownLineBreakStyle": "document",
"markupAllowShortIssuePattern": "true",
})
}
diff --git a/models/renderhelper/repo_wiki_test.go b/models/renderhelper/repo_wiki_test.go
index beab2570e7..4f6da541a5 100644
--- a/models/renderhelper/repo_wiki_test.go
+++ b/models/renderhelper/repo_wiki_test.go
@@ -4,7 +4,6 @@
package renderhelper
import (
- "context"
"testing"
repo_model "code.gitea.io/gitea/models/repo"
@@ -20,7 +19,7 @@ func TestRepoWiki(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
t.Run("AutoLink", func(t *testing.T) {
- rctx := NewRenderContextRepoWiki(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+ rctx := NewRenderContextRepoWiki(t.Context(), repo1).WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
65f1bf27bc3bf70f64657658635e66094edbcb4d
#1
@@ -35,7 +34,7 @@ func TestRepoWiki(t *testing.T) {
})
t.Run("AbsoluteAndRelative", func(t *testing.T) {
- rctx := NewRenderContextRepoWiki(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+ rctx := NewRenderContextRepoWiki(t.Context(), repo1).WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
[/test](/test)
[./test](./test)
@@ -46,19 +45,19 @@ func TestRepoWiki(t *testing.T) {
assert.Equal(t,
`<p><a href="/user2/repo1/wiki/test" rel="nofollow">/test</a>
<a href="/user2/repo1/wiki/test" rel="nofollow">./test</a>
-<a href="/user2/repo1/wiki/raw/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/wiki/raw/image" alt="/image"/></a>
-<a href="/user2/repo1/wiki/raw/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/wiki/raw/image" alt="./image"/></a></p>
+<a href="/user2/repo1/wiki/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/wiki/raw/image" alt="/image"/></a>
+<a href="/user2/repo1/wiki/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/wiki/raw/image" alt="./image"/></a></p>
`, rendered)
})
t.Run("PathInTag", func(t *testing.T) {
- rctx := NewRenderContextRepoWiki(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
+ rctx := NewRenderContextRepoWiki(t.Context(), repo1).WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
<img src="LINK">
<video src="LINK">
`)
assert.NoError(t, err)
- assert.Equal(t, `<a href="/user2/repo1/wiki/raw/LINK" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/wiki/raw/LINK"/></a>
+ assert.Equal(t, `<a href="/user2/repo1/wiki/LINK" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/wiki/raw/LINK"/></a>
<video src="/user2/repo1/wiki/raw/LINK">
</video>`, rendered)
})
diff --git a/models/renderhelper/simple_document.go b/models/renderhelper/simple_document.go
index 91d888aa87..9b3dacaea3 100644
--- a/models/renderhelper/simple_document.go
+++ b/models/renderhelper/simple_document.go
@@ -15,8 +15,14 @@ type SimpleDocument struct {
baseLink string
}
-func (r *SimpleDocument) ResolveLink(link string, likeType markup.LinkType) string {
- return r.ctx.ResolveLinkRelative(r.baseLink, "", link)
+func (r *SimpleDocument) ResolveLink(link, preferLinkType string) string {
+ linkType, link := markup.ParseRenderedLink(link, preferLinkType)
+ switch linkType {
+ case markup.LinkTypeRoot:
+ return r.ctx.ResolveLinkRoot(link)
+ default:
+ return r.ctx.ResolveLinkRelative(r.baseLink, "", link)
+ }
}
var _ markup.RenderHelper = (*SimpleDocument)(nil)
diff --git a/models/renderhelper/simple_document_test.go b/models/renderhelper/simple_document_test.go
index c0d5fd7429..890592860a 100644
--- a/models/renderhelper/simple_document_test.go
+++ b/models/renderhelper/simple_document_test.go
@@ -4,7 +4,6 @@
package renderhelper
import (
- "context"
"testing"
"code.gitea.io/gitea/models/unittest"
@@ -16,7 +15,7 @@ import (
func TestSimpleDocument(t *testing.T) {
unittest.PrepareTestEnv(t)
- rctx := NewRenderContextSimpleDocument(context.Background(), "/base").WithMarkupType(markdown.MarkupName)
+ rctx := NewRenderContextSimpleDocument(t.Context(), "/base").WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, `
65f1bf27bc3bf70f64657658635e66094edbcb4d
#1
@@ -31,7 +30,7 @@ func TestSimpleDocument(t *testing.T) {
assert.Equal(t,
`<p>65f1bf27bc3bf70f64657658635e66094edbcb4d
#1
-<a href="/base/user2" rel="nofollow">@user2</a></p>
+<a href="/user2" rel="nofollow">@user2</a></p>
<p><a href="/base/test" rel="nofollow">/test</a>
<a href="/base/test" rel="nofollow">./test</a>
<a href="/base/image" target="_blank" rel="nofollow noopener"><img src="/base/image" alt="/image"/></a>
diff --git a/models/repo/archiver.go b/models/repo/archiver.go
index 14ffa1d89b..d06e94e5ac 100644
--- a/models/repo/archiver.go
+++ b/models/repo/archiver.go
@@ -50,22 +50,17 @@ func (archiver *RepoArchiver) RelativePath() string {
func repoArchiverForRelativePath(relativePath string) (*RepoArchiver, error) {
parts := strings.SplitN(relativePath, "/", 3)
if len(parts) != 3 {
- return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument}
+ return nil, util.NewInvalidArgumentErrorf("invalid storage path: must have 3 parts")
}
repoID, err := strconv.ParseInt(parts[0], 10, 64)
if err != nil {
- return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument}
+ return nil, util.NewInvalidArgumentErrorf("invalid storage path: invalid repo id")
}
- nameExts := strings.SplitN(parts[2], ".", 2)
- if len(nameExts) != 2 {
- return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument}
+ commitID, archiveType := git.SplitArchiveNameType(parts[2])
+ if archiveType == git.ArchiveUnknown {
+ return nil, util.NewInvalidArgumentErrorf("invalid storage path: invalid archive type")
}
-
- return &RepoArchiver{
- RepoID: repoID,
- CommitID: parts[1] + nameExts[0],
- Type: git.ToArchiveType(nameExts[1]),
- }, nil
+ return &RepoArchiver{RepoID: repoID, CommitID: commitID, Type: archiveType}, nil
}
// GetRepoArchiver get an archiver
diff --git a/models/repo/attachment.go b/models/repo/attachment.go
index fa4f6c47e6..835bee5402 100644
--- a/models/repo/attachment.go
+++ b/models/repo/attachment.go
@@ -224,7 +224,7 @@ func DeleteAttachmentsByComment(ctx context.Context, commentID int64, remove boo
// UpdateAttachmentByUUID Updates attachment via uuid
func UpdateAttachmentByUUID(ctx context.Context, attach *Attachment, cols ...string) error {
if attach.UUID == "" {
- return fmt.Errorf("attachment uuid should be not blank")
+ return errors.New("attachment uuid should be not blank")
}
_, err := db.GetEngine(ctx).Where("uuid=?", attach.UUID).Cols(cols...).Update(attach)
return err
diff --git a/models/repo/avatar.go b/models/repo/avatar.go
index ccfac12cad..eff64bd239 100644
--- a/models/repo/avatar.go
+++ b/models/repo/avatar.go
@@ -9,6 +9,7 @@ import (
"image/png"
"io"
"net/url"
+ "strconv"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/avatar"
@@ -37,7 +38,7 @@ func (repo *Repository) RelAvatarLink(ctx context.Context) string {
// generateRandomAvatar generates a random avatar for repository.
func generateRandomAvatar(ctx context.Context, repo *Repository) error {
- idToString := fmt.Sprintf("%d", repo.ID)
+ idToString := strconv.FormatInt(repo.ID, 10)
seed := idToString
img, err := avatar.RandomImage([]byte(seed))
diff --git a/models/repo/collaboration_test.go b/models/repo/collaboration_test.go
index 639050f5fd..7b07dbffdf 100644
--- a/models/repo/collaboration_test.go
+++ b/models/repo/collaboration_test.go
@@ -25,8 +25,8 @@ func TestRepository_GetCollaborators(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, collaborators, int(expectedLen))
for _, collaborator := range collaborators {
- assert.EqualValues(t, collaborator.User.ID, collaborator.Collaboration.UserID)
- assert.EqualValues(t, repoID, collaborator.Collaboration.RepoID)
+ assert.Equal(t, collaborator.User.ID, collaborator.Collaboration.UserID)
+ assert.Equal(t, repoID, collaborator.Collaboration.RepoID)
}
}
test(1)
@@ -51,7 +51,7 @@ func TestRepository_GetCollaborators(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, collaborators2, 1)
- assert.NotEqualValues(t, collaborators1[0].ID, collaborators2[0].ID)
+ assert.NotEqual(t, collaborators1[0].ID, collaborators2[0].ID)
}
func TestRepository_IsCollaborator(t *testing.T) {
@@ -76,10 +76,10 @@ func TestRepository_ChangeCollaborationAccessMode(t *testing.T) {
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, 4, perm.AccessModeAdmin))
collaboration := unittest.AssertExistsAndLoadBean(t, &repo_model.Collaboration{RepoID: repo.ID, UserID: 4})
- assert.EqualValues(t, perm.AccessModeAdmin, collaboration.Mode)
+ assert.Equal(t, perm.AccessModeAdmin, collaboration.Mode)
access := unittest.AssertExistsAndLoadBean(t, &access_model.Access{UserID: 4, RepoID: repo.ID})
- assert.EqualValues(t, perm.AccessModeAdmin, access.Mode)
+ assert.Equal(t, perm.AccessModeAdmin, access.Mode)
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, 4, perm.AccessModeAdmin))
diff --git a/models/repo/license.go b/models/repo/license.go
index 366b4598cc..9bcf0f7bc9 100644
--- a/models/repo/license.go
+++ b/models/repo/license.go
@@ -54,6 +54,7 @@ func UpdateRepoLicenses(ctx context.Context, repo *Repository, commitID string,
for _, o := range oldLicenses {
// Update already existing license
if o.License == license {
+ o.CommitID = commitID
if _, err := db.GetEngine(ctx).ID(o.ID).Cols("`commit_id`").Update(o); err != nil {
return err
}
diff --git a/models/repo/org_repo.go b/models/repo/org_repo.go
index 5f0af2d475..96f21ba2ac 100644
--- a/models/repo/org_repo.go
+++ b/models/repo/org_repo.go
@@ -47,10 +47,9 @@ func GetTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) (Repo
// AccessibleReposEnvironment operations involving the repositories that are
// accessible to a particular user
type AccessibleReposEnvironment interface {
- CountRepos() (int64, error)
- RepoIDs(page, pageSize int) ([]int64, error)
- Repos(page, pageSize int) (RepositoryList, error)
- MirrorRepos() (RepositoryList, error)
+ CountRepos(ctx context.Context) (int64, error)
+ RepoIDs(ctx context.Context) ([]int64, error)
+ MirrorRepos(ctx context.Context) (RepositoryList, error)
AddKeyword(keyword string)
SetSort(db.SearchOrderBy)
}
@@ -60,7 +59,6 @@ type accessibleReposEnv struct {
user *user_model.User
team *org_model.Team
teamIDs []int64
- ctx context.Context
keyword string
orderBy db.SearchOrderBy
}
@@ -86,18 +84,16 @@ func AccessibleReposEnv(ctx context.Context, org *org_model.Organization, userID
org: org,
user: user,
teamIDs: teamIDs,
- ctx: ctx,
orderBy: db.SearchOrderByRecentUpdated,
}, nil
}
// AccessibleTeamReposEnv an AccessibleReposEnvironment for the repositories in `org`
// that are accessible to the specified team.
-func AccessibleTeamReposEnv(ctx context.Context, org *org_model.Organization, team *org_model.Team) AccessibleReposEnvironment {
+func AccessibleTeamReposEnv(org *org_model.Organization, team *org_model.Team) AccessibleReposEnvironment {
return &accessibleReposEnv{
org: org,
team: team,
- ctx: ctx,
orderBy: db.SearchOrderByRecentUpdated,
}
}
@@ -123,8 +119,8 @@ func (env *accessibleReposEnv) cond() builder.Cond {
return cond
}
-func (env *accessibleReposEnv) CountRepos() (int64, error) {
- repoCount, err := db.GetEngine(env.ctx).
+func (env *accessibleReposEnv) CountRepos(ctx context.Context) (int64, error) {
+ repoCount, err := db.GetEngine(ctx).
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
Where(env.cond()).
Distinct("`repository`.id").
@@ -135,43 +131,21 @@ func (env *accessibleReposEnv) CountRepos() (int64, error) {
return repoCount, nil
}
-func (env *accessibleReposEnv) RepoIDs(page, pageSize int) ([]int64, error) {
- if page <= 0 {
- page = 1
- }
-
- repoIDs := make([]int64, 0, pageSize)
- return repoIDs, db.GetEngine(env.ctx).
+func (env *accessibleReposEnv) RepoIDs(ctx context.Context) ([]int64, error) {
+ var repoIDs []int64
+ return repoIDs, db.GetEngine(ctx).
Table("repository").
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
Where(env.cond()).
- GroupBy("`repository`.id,`repository`."+strings.Fields(string(env.orderBy))[0]).
+ GroupBy("`repository`.id,`repository`." + strings.Fields(string(env.orderBy))[0]).
OrderBy(string(env.orderBy)).
- Limit(pageSize, (page-1)*pageSize).
Cols("`repository`.id").
Find(&repoIDs)
}
-func (env *accessibleReposEnv) Repos(page, pageSize int) (RepositoryList, error) {
- repoIDs, err := env.RepoIDs(page, pageSize)
- if err != nil {
- return nil, fmt.Errorf("GetUserRepositoryIDs: %w", err)
- }
-
- repos := make([]*Repository, 0, len(repoIDs))
- if len(repoIDs) == 0 {
- return repos, nil
- }
-
- return repos, db.GetEngine(env.ctx).
- In("`repository`.id", repoIDs).
- OrderBy(string(env.orderBy)).
- Find(&repos)
-}
-
-func (env *accessibleReposEnv) MirrorRepoIDs() ([]int64, error) {
+func (env *accessibleReposEnv) MirrorRepoIDs(ctx context.Context) ([]int64, error) {
repoIDs := make([]int64, 0, 10)
- return repoIDs, db.GetEngine(env.ctx).
+ return repoIDs, db.GetEngine(ctx).
Table("repository").
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true).
Where(env.cond()).
@@ -181,8 +155,8 @@ func (env *accessibleReposEnv) MirrorRepoIDs() ([]int64, error) {
Find(&repoIDs)
}
-func (env *accessibleReposEnv) MirrorRepos() (RepositoryList, error) {
- repoIDs, err := env.MirrorRepoIDs()
+func (env *accessibleReposEnv) MirrorRepos(ctx context.Context) (RepositoryList, error) {
+ repoIDs, err := env.MirrorRepoIDs(ctx)
if err != nil {
return nil, fmt.Errorf("MirrorRepoIDs: %w", err)
}
@@ -192,7 +166,7 @@ func (env *accessibleReposEnv) MirrorRepos() (RepositoryList, error) {
return repos, nil
}
- return repos, db.GetEngine(env.ctx).
+ return repos, db.GetEngine(ctx).
In("`repository`.id", repoIDs).
Find(&repos)
}
diff --git a/models/repo/pushmirror_test.go b/models/repo/pushmirror_test.go
index e19749d93a..9fb7471147 100644
--- a/models/repo/pushmirror_test.go
+++ b/models/repo/pushmirror_test.go
@@ -39,8 +39,6 @@ func TestPushMirrorsIterate(t *testing.T) {
Interval: 0,
})
- time.Sleep(1 * time.Millisecond)
-
repo_model.PushMirrorsIterate(db.DefaultContext, 1, func(idx int, bean any) error {
m, ok := bean.(*repo_model.PushMirror)
assert.True(t, ok)
diff --git a/models/repo/release.go b/models/repo/release.go
index 1c2e4a48e3..59f4caf5aa 100644
--- a/models/repo/release.go
+++ b/models/repo/release.go
@@ -161,6 +161,11 @@ func UpdateRelease(ctx context.Context, rel *Release) error {
return err
}
+func UpdateReleaseNumCommits(ctx context.Context, rel *Release) error {
+ _, err := db.GetEngine(ctx).ID(rel.ID).Cols("num_commits").Update(rel)
+ return err
+}
+
// AddReleaseAttachments adds a release attachments
func AddReleaseAttachments(ctx context.Context, releaseID int64, attachmentUUIDs []string) (err error) {
// Check attachments
@@ -175,7 +180,7 @@ func AddReleaseAttachments(ctx context.Context, releaseID int64, attachmentUUIDs
}
attachments[i].ReleaseID = releaseID
// No assign value could be 0, so ignore AllCols().
- if _, err = db.GetEngine(ctx).ID(attachments[i].ID).Update(attachments[i]); err != nil {
+ if _, err = db.GetEngine(ctx).ID(attachments[i].ID).Cols("release_id").Update(attachments[i]); err != nil {
return fmt.Errorf("update attachment [%d]: %w", attachments[i].ID, err)
}
}
@@ -418,8 +423,8 @@ func UpdateReleasesMigrationsByType(ctx context.Context, gitServiceType structs.
return err
}
-// PushUpdateDeleteTagsContext updates a number of delete tags with context
-func PushUpdateDeleteTagsContext(ctx context.Context, repo *Repository, tags []string) error {
+// PushUpdateDeleteTags updates a number of delete tags with context
+func PushUpdateDeleteTags(ctx context.Context, repo *Repository, tags []string) error {
if len(tags) == 0 {
return nil
}
@@ -448,58 +453,6 @@ func PushUpdateDeleteTagsContext(ctx context.Context, repo *Repository, tags []s
return nil
}
-// PushUpdateDeleteTag must be called for any push actions to delete tag
-func PushUpdateDeleteTag(ctx context.Context, repo *Repository, tagName string) error {
- rel, err := GetRelease(ctx, repo.ID, tagName)
- if err != nil {
- if IsErrReleaseNotExist(err) {
- return nil
- }
- return fmt.Errorf("GetRelease: %w", err)
- }
- if rel.IsTag {
- if _, err = db.DeleteByID[Release](ctx, rel.ID); err != nil {
- return fmt.Errorf("Delete: %w", err)
- }
- } else {
- rel.IsDraft = true
- rel.NumCommits = 0
- rel.Sha1 = ""
- if _, err = db.GetEngine(ctx).ID(rel.ID).AllCols().Update(rel); err != nil {
- return fmt.Errorf("Update: %w", err)
- }
- }
-
- return nil
-}
-
-// SaveOrUpdateTag must be called for any push actions to add tag
-func SaveOrUpdateTag(ctx context.Context, repo *Repository, newRel *Release) error {
- rel, err := GetRelease(ctx, repo.ID, newRel.TagName)
- if err != nil && !IsErrReleaseNotExist(err) {
- return fmt.Errorf("GetRelease: %w", err)
- }
-
- if rel == nil {
- rel = newRel
- if _, err = db.GetEngine(ctx).Insert(rel); err != nil {
- return fmt.Errorf("InsertOne: %w", err)
- }
- } else {
- rel.Sha1 = newRel.Sha1
- rel.CreatedUnix = newRel.CreatedUnix
- rel.NumCommits = newRel.NumCommits
- rel.IsDraft = false
- if rel.IsTag && newRel.PublisherID > 0 {
- rel.PublisherID = newRel.PublisherID
- }
- if _, err = db.GetEngine(ctx).ID(rel.ID).AllCols().Update(rel); err != nil {
- return fmt.Errorf("Update: %w", err)
- }
- }
- return nil
-}
-
// RemapExternalUser ExternalUserRemappable interface
func (r *Release) RemapExternalUser(externalName string, externalID, userID int64) error {
r.OriginalAuthor = externalName
@@ -558,3 +511,8 @@ func FindTagsByCommitIDs(ctx context.Context, repoID int64, commitIDs ...string)
}
return res, nil
}
+
+func DeleteRepoReleases(ctx context.Context, repoID int64) error {
+ _, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Delete(new(Release))
+ return err
+}
diff --git a/models/repo/repo.go b/models/repo/repo.go
index 5ef4d470c3..34d1bf55f6 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -5,6 +5,7 @@ package repo
import (
"context"
+ "errors"
"fmt"
"html/template"
"maps"
@@ -14,12 +15,14 @@ import (
"regexp"
"strconv"
"strings"
+ "sync"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
+ giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
@@ -57,23 +60,42 @@ type ErrRepoIsArchived struct {
}
func (err ErrRepoIsArchived) Error() string {
- return fmt.Sprintf("%s is archived", err.Repo.LogString())
+ return err.Repo.LogString() + " is archived"
}
-var (
- validRepoNamePattern = regexp.MustCompile(`[-.\w]+`)
- invalidRepoNamePattern = regexp.MustCompile(`[.]{2,}`)
- reservedRepoNames = []string{".", "..", "-"}
- reservedRepoPatterns = []string{"*.git", "*.wiki", "*.rss", "*.atom"}
-)
+type globalVarsStruct struct {
+ validRepoNamePattern *regexp.Regexp
+ invalidRepoNamePattern *regexp.Regexp
+ reservedRepoNames []string
+ reservedRepoNamePatterns []string
+}
+
+var globalVars = sync.OnceValue(func() *globalVarsStruct {
+ return &globalVarsStruct{
+ validRepoNamePattern: regexp.MustCompile(`^[-.\w]+$`),
+ invalidRepoNamePattern: regexp.MustCompile(`[.]{2,}`),
+ reservedRepoNames: []string{".", "..", "-"},
+ reservedRepoNamePatterns: []string{"*.wiki", "*.git", "*.rss", "*.atom"},
+ }
+})
// IsUsableRepoName returns true when name is usable
func IsUsableRepoName(name string) error {
- if !validRepoNamePattern.MatchString(name) || invalidRepoNamePattern.MatchString(name) {
+ vars := globalVars()
+ if !vars.validRepoNamePattern.MatchString(name) || vars.invalidRepoNamePattern.MatchString(name) {
// Note: usually this error is normally caught up earlier in the UI
return db.ErrNameCharsNotAllowed{Name: name}
}
- return db.IsUsableName(reservedRepoNames, reservedRepoPatterns, name)
+ return db.IsUsableName(vars.reservedRepoNames, vars.reservedRepoNamePatterns, name)
+}
+
+// IsValidSSHAccessRepoName is like IsUsableRepoName, but it allows "*.wiki" because wiki repo needs to be accessed in SSH code
+func IsValidSSHAccessRepoName(name string) bool {
+ vars := globalVars()
+ if !vars.validRepoNamePattern.MatchString(name) || vars.invalidRepoNamePattern.MatchString(name) {
+ return false
+ }
+ return db.IsUsableName(vars.reservedRepoNames, vars.reservedRepoNamePatterns[1:], name) == nil
}
// TrustModelType defines the types of trust model for this repository
@@ -203,12 +225,24 @@ func init() {
db.RegisterModel(new(Repository))
}
-func (repo *Repository) GetName() string {
- return repo.Name
+func RelativePath(ownerName, repoName string) string {
+ return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".git"
}
-func (repo *Repository) GetOwnerName() string {
- return repo.OwnerName
+// RelativePath should be an unix style path like username/reponame.git
+func (repo *Repository) RelativePath() string {
+ return RelativePath(repo.OwnerName, repo.Name)
+}
+
+type StorageRepo string
+
+// RelativePath should be an unix style path like username/reponame.git
+func (sr StorageRepo) RelativePath() string {
+ return string(sr)
+}
+
+func (repo *Repository) WikiStorageRepo() StorageRepo {
+ return StorageRepo(strings.ToLower(repo.OwnerName) + "/" + strings.ToLower(repo.Name) + ".wiki.git")
}
// SanitizedOriginalURL returns a sanitized OriginalURL
@@ -279,6 +313,8 @@ func (repo *Repository) IsBroken() bool {
}
// MarkAsBrokenEmpty marks the repo as broken and empty
+// FIXME: the status "broken" and "is_empty" were abused,
+// The code always set them together, no way to distinguish whether a repo is really "empty" or "broken"
func (repo *Repository) MarkAsBrokenEmpty() {
repo.Status = RepositoryBroken
repo.IsEmpty = true
@@ -399,32 +435,33 @@ func (repo *Repository) MustGetUnit(ctx context.Context, tp unit.Type) *RepoUnit
return ru
}
- if tp == unit.TypeExternalWiki {
+ switch tp {
+ case unit.TypeExternalWiki:
return &RepoUnit{
Type: tp,
Config: new(ExternalWikiConfig),
}
- } else if tp == unit.TypeExternalTracker {
+ case unit.TypeExternalTracker:
return &RepoUnit{
Type: tp,
Config: new(ExternalTrackerConfig),
}
- } else if tp == unit.TypePullRequests {
+ case unit.TypePullRequests:
return &RepoUnit{
Type: tp,
Config: new(PullRequestsConfig),
}
- } else if tp == unit.TypeIssues {
+ case unit.TypeIssues:
return &RepoUnit{
Type: tp,
Config: new(IssuesConfig),
}
- } else if tp == unit.TypeActions {
+ case unit.TypeActions:
return &RepoUnit{
Type: tp,
Config: new(ActionsConfig),
}
- } else if tp == unit.TypeProjects {
+ case unit.TypeProjects:
cfg := new(ProjectsConfig)
cfg.ProjectsMode = ProjectsModeNone
return &RepoUnit{
@@ -484,15 +521,15 @@ func (repo *Repository) composeCommonMetas(ctx context.Context) map[string]strin
"repo": repo.Name,
}
- unit, err := repo.GetUnit(ctx, unit.TypeExternalTracker)
+ unitExternalTracker, err := repo.GetUnit(ctx, unit.TypeExternalTracker)
if err == nil {
- metas["format"] = unit.ExternalTrackerConfig().ExternalTrackerFormat
- switch unit.ExternalTrackerConfig().ExternalTrackerStyle {
+ metas["format"] = unitExternalTracker.ExternalTrackerConfig().ExternalTrackerFormat
+ switch unitExternalTracker.ExternalTrackerConfig().ExternalTrackerStyle {
case markup.IssueNameStyleAlphanumeric:
metas["style"] = markup.IssueNameStyleAlphanumeric
case markup.IssueNameStyleRegexp:
metas["style"] = markup.IssueNameStyleRegexp
- metas["regexp"] = unit.ExternalTrackerConfig().ExternalTrackerRegexpPattern
+ metas["regexp"] = unitExternalTracker.ExternalTrackerConfig().ExternalTrackerRegexpPattern
default:
metas["style"] = markup.IssueNameStyleNumeric
}
@@ -516,11 +553,11 @@ func (repo *Repository) composeCommonMetas(ctx context.Context) map[string]strin
return repo.commonRenderingMetas
}
-// ComposeMetas composes a map of metas for properly rendering comments or comment-like contents (commit message)
-func (repo *Repository) ComposeMetas(ctx context.Context) map[string]string {
+// ComposeCommentMetas composes a map of metas for properly rendering comments or comment-like contents (commit message)
+func (repo *Repository) ComposeCommentMetas(ctx context.Context) map[string]string {
metas := maps.Clone(repo.composeCommonMetas(ctx))
- metas["markdownLineBreakStyle"] = "comment"
- metas["markupAllowShortIssuePattern"] = "true"
+ metas["markdownNewLineHardBreak"] = strconv.FormatBool(setting.Markdown.RenderOptionsComment.NewLineHardBreak)
+ metas["markupAllowShortIssuePattern"] = strconv.FormatBool(setting.Markdown.RenderOptionsComment.ShortIssuePattern)
return metas
}
@@ -528,16 +565,17 @@ func (repo *Repository) ComposeMetas(ctx context.Context) map[string]string {
func (repo *Repository) ComposeWikiMetas(ctx context.Context) map[string]string {
// does wiki need the "teams" and "org" from common metas?
metas := maps.Clone(repo.composeCommonMetas(ctx))
- metas["markdownLineBreakStyle"] = "document"
- metas["markupAllowShortIssuePattern"] = "true"
+ metas["markdownNewLineHardBreak"] = strconv.FormatBool(setting.Markdown.RenderOptionsWiki.NewLineHardBreak)
+ metas["markupAllowShortIssuePattern"] = strconv.FormatBool(setting.Markdown.RenderOptionsWiki.ShortIssuePattern)
return metas
}
-// ComposeDocumentMetas composes a map of metas for properly rendering documents (repo files)
-func (repo *Repository) ComposeDocumentMetas(ctx context.Context) map[string]string {
+// ComposeRepoFileMetas composes a map of metas for properly rendering documents (repo files)
+func (repo *Repository) ComposeRepoFileMetas(ctx context.Context) map[string]string {
// does document(file) need the "teams" and "org" from common metas?
metas := maps.Clone(repo.composeCommonMetas(ctx))
- metas["markdownLineBreakStyle"] = "document"
+ metas["markdownNewLineHardBreak"] = strconv.FormatBool(setting.Markdown.RenderOptionsRepoFile.NewLineHardBreak)
+ metas["markupAllowShortIssuePattern"] = strconv.FormatBool(setting.Markdown.RenderOptionsRepoFile.ShortIssuePattern)
return metas
}
@@ -615,7 +653,7 @@ func (repo *Repository) AllowsPulls(ctx context.Context) bool {
// CanEnableEditor returns true if repository meets the requirements of web editor.
func (repo *Repository) CanEnableEditor() bool {
- return !repo.IsMirror
+ return !repo.IsMirror && !repo.IsArchived
}
// DescriptionHTML does special handles to description and return HTML string.
@@ -632,17 +670,31 @@ func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML {
type CloneLink struct {
SSH string
HTTPS string
+ Tea string
}
-// ComposeHTTPSCloneURL returns HTTPS clone URL based on given owner and repository name.
-func ComposeHTTPSCloneURL(owner, repo string) string {
- return fmt.Sprintf("%s%s/%s.git", setting.AppURL, url.PathEscape(owner), url.PathEscape(repo))
+// ComposeHTTPSCloneURL returns HTTPS clone URL based on the given owner and repository name.
+func ComposeHTTPSCloneURL(ctx context.Context, owner, repo string) string {
+ return fmt.Sprintf("%s%s/%s.git", httplib.GuessCurrentAppURL(ctx), url.PathEscape(owner), url.PathEscape(repo))
}
-func ComposeSSHCloneURL(ownerName, repoName string) string {
+// ComposeSSHCloneURL returns SSH clone URL based on the given owner and repository name.
+func ComposeSSHCloneURL(doer *user_model.User, ownerName, repoName string) string {
sshUser := setting.SSH.User
sshDomain := setting.SSH.Domain
+ if sshUser == "(DOER_USERNAME)" {
+ // Some users use SSH reverse-proxy and need to use the current signed-in username as the SSH user
+ // to make the SSH reverse-proxy could prepare the user's public keys ahead.
+ // For most cases we have the correct "doer", then use it as the SSH user.
+ // If we can't get the doer, then use the built-in SSH user.
+ if doer != nil {
+ sshUser = doer.Name
+ } else {
+ sshUser = setting.SSH.BuiltinServerUser
+ }
+ }
+
// non-standard port, it must use full URI
if setting.SSH.Port != 22 {
sshHost := net.JoinHostPort(sshDomain, strconv.Itoa(setting.SSH.Port))
@@ -660,21 +712,26 @@ func ComposeSSHCloneURL(ownerName, repoName string) string {
return fmt.Sprintf("%s@%s:%s/%s.git", sshUser, sshHost, url.PathEscape(ownerName), url.PathEscape(repoName))
}
-func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
- repoName := repo.Name
- if isWiki {
- repoName += ".wiki"
- }
+// ComposeTeaCloneCommand returns Tea CLI clone command based on the given owner and repository name.
+func ComposeTeaCloneCommand(ctx context.Context, owner, repo string) string {
+ return fmt.Sprintf("tea clone %s/%s", url.PathEscape(owner), url.PathEscape(repo))
+}
- cl := new(CloneLink)
- cl.SSH = ComposeSSHCloneURL(repo.OwnerName, repoName)
- cl.HTTPS = ComposeHTTPSCloneURL(repo.OwnerName, repoName)
- return cl
+func (repo *Repository) cloneLink(ctx context.Context, doer *user_model.User, repoPathName string) *CloneLink {
+ return &CloneLink{
+ SSH: ComposeSSHCloneURL(doer, repo.OwnerName, repoPathName),
+ HTTPS: ComposeHTTPSCloneURL(ctx, repo.OwnerName, repoPathName),
+ Tea: ComposeTeaCloneCommand(ctx, repo.OwnerName, repoPathName),
+ }
}
// CloneLink returns clone URLs of repository.
-func (repo *Repository) CloneLink() (cl *CloneLink) {
- return repo.cloneLink(false)
+func (repo *Repository) CloneLink(ctx context.Context, doer *user_model.User) (cl *CloneLink) {
+ return repo.cloneLink(ctx, doer, repo.Name)
+}
+
+func (repo *Repository) CloneLinkGeneral(ctx context.Context) (cl *CloneLink) {
+ return repo.cloneLink(ctx, nil /* no doer, use a general git user */, repo.Name)
}
// GetOriginalURLHostname returns the hostname of a URL or the URL
@@ -770,47 +827,25 @@ func GetRepositoryByName(ctx context.Context, ownerID int64, name string) (*Repo
return &repo, err
}
-// getRepositoryURLPathSegments returns segments (owner, reponame) extracted from a url
-func getRepositoryURLPathSegments(repoURL string) []string {
- if strings.HasPrefix(repoURL, setting.AppURL) {
- return strings.Split(strings.TrimPrefix(repoURL, setting.AppURL), "/")
- }
-
- sshURLVariants := [4]string{
- setting.SSH.Domain + ":",
- setting.SSH.User + "@" + setting.SSH.Domain + ":",
- "git+ssh://" + setting.SSH.Domain + "/",
- "git+ssh://" + setting.SSH.User + "@" + setting.SSH.Domain + "/",
- }
-
- for _, sshURL := range sshURLVariants {
- if strings.HasPrefix(repoURL, sshURL) {
- return strings.Split(strings.TrimPrefix(repoURL, sshURL), "/")
- }
- }
-
- return nil
-}
-
// GetRepositoryByURL returns the repository by given url
func GetRepositoryByURL(ctx context.Context, repoURL string) (*Repository, error) {
- // possible urls for git:
- // https://my.domain/sub-path/<owner>/<repo>.git
- // https://my.domain/sub-path/<owner>/<repo>
- // git+ssh://user@my.domain/<owner>/<repo>.git
- // git+ssh://user@my.domain/<owner>/<repo>
- // user@my.domain:<owner>/<repo>.git
- // user@my.domain:<owner>/<repo>
-
- pathSegments := getRepositoryURLPathSegments(repoURL)
-
- if len(pathSegments) != 2 {
- return nil, fmt.Errorf("unknown or malformed repository URL")
+ ret, err := giturl.ParseRepositoryURL(ctx, repoURL)
+ if err != nil || ret.OwnerName == "" {
+ return nil, errors.New("unknown or malformed repository URL")
}
+ return GetRepositoryByOwnerAndName(ctx, ret.OwnerName, ret.RepoName)
+}
- ownerName := pathSegments[0]
- repoName := strings.TrimSuffix(pathSegments[1], ".git")
- return GetRepositoryByOwnerAndName(ctx, ownerName, repoName)
+// GetRepositoryByURLRelax also accepts an SSH clone URL without user part
+func GetRepositoryByURLRelax(ctx context.Context, repoURL string) (*Repository, error) {
+ if !strings.Contains(repoURL, "://") && !strings.Contains(repoURL, "@") {
+ // convert "example.com:owner/repo" to "@example.com:owner/repo"
+ p1, p2, p3 := strings.Index(repoURL, "."), strings.Index(repoURL, ":"), strings.Index(repoURL, "/")
+ if 0 < p1 && p1 < p2 && p2 < p3 {
+ repoURL = "@" + repoURL
+ }
+ }
+ return GetRepositoryByURL(ctx, repoURL)
}
// GetRepositoryByID returns the repository by given id if exists.
@@ -828,6 +863,9 @@ func GetRepositoryByID(ctx context.Context, id int64) (*Repository, error) {
// GetRepositoriesMapByIDs returns the repositories by given id slice.
func GetRepositoriesMapByIDs(ctx context.Context, ids []int64) (map[int64]*Repository, error) {
repos := make(map[int64]*Repository, len(ids))
+ if len(ids) == 0 {
+ return repos, nil
+ }
return repos, db.GetEngine(ctx).In("id", ids).Find(&repos)
}
diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go
index 9bed2e9197..f2cdd2f284 100644
--- a/models/repo/repo_list.go
+++ b/models/repo/repo_list.go
@@ -21,11 +21,6 @@ import (
"xorm.io/builder"
)
-// FindReposMapByIDs find repos as map
-func FindReposMapByIDs(ctx context.Context, repoIDs []int64, res map[int64]*Repository) error {
- return db.GetEngine(ctx).In("id", repoIDs).Find(&res)
-}
-
// RepositoryListDefaultPageSize is the default number of repositories
// to load in memory when running administrative tasks on all (or almost
// all) of them.
@@ -364,7 +359,7 @@ func UserOrgPublicUnitRepoCond(userID, orgID int64) builder.Cond {
}
// SearchRepositoryCondition creates a query condition according search repository options
-func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
+func SearchRepositoryCondition(opts SearchRepoOptions) builder.Cond {
cond := builder.NewCond()
if opts.Private {
@@ -454,7 +449,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
if opts.Keyword != "" {
// separate keyword
subQueryCond := builder.NewCond()
- for _, v := range strings.Split(opts.Keyword, ",") {
+ for v := range strings.SplitSeq(opts.Keyword, ",") {
if opts.TopicOnly {
subQueryCond = subQueryCond.Or(builder.Eq{"topic.name": strings.ToLower(v)})
} else {
@@ -469,7 +464,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
keywordCond := builder.In("id", subQuery)
if !opts.TopicOnly {
likes := builder.NewCond()
- for _, v := range strings.Split(opts.Keyword, ",") {
+ for v := range strings.SplitSeq(opts.Keyword, ",") {
likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)})
// If the string looks like "org/repo", match against that pattern too
@@ -556,18 +551,18 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
// SearchRepository returns repositories based on search options,
// it returns results in given range and number of total results.
-func SearchRepository(ctx context.Context, opts *SearchRepoOptions) (RepositoryList, int64, error) {
+func SearchRepository(ctx context.Context, opts SearchRepoOptions) (RepositoryList, int64, error) {
cond := SearchRepositoryCondition(opts)
return SearchRepositoryByCondition(ctx, opts, cond, true)
}
// CountRepository counts repositories based on search options,
-func CountRepository(ctx context.Context, opts *SearchRepoOptions) (int64, error) {
+func CountRepository(ctx context.Context, opts SearchRepoOptions) (int64, error) {
return db.GetEngine(ctx).Where(SearchRepositoryCondition(opts)).Count(new(Repository))
}
// SearchRepositoryByCondition search repositories by condition
-func SearchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, cond builder.Cond, loadAttributes bool) (RepositoryList, int64, error) {
+func SearchRepositoryByCondition(ctx context.Context, opts SearchRepoOptions, cond builder.Cond, loadAttributes bool) (RepositoryList, int64, error) {
sess, count, err := searchRepositoryByCondition(ctx, opts, cond)
if err != nil {
return nil, 0, err
@@ -595,23 +590,25 @@ func SearchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, c
return repos, count, nil
}
-func searchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, cond builder.Cond) (db.Engine, int64, error) {
- if opts.Page <= 0 {
- opts.Page = 1
+func searchRepositoryByCondition(ctx context.Context, opts SearchRepoOptions, cond builder.Cond) (db.Engine, int64, error) {
+ page := opts.Page
+ if page <= 0 {
+ page = 1
}
- if len(opts.OrderBy) == 0 {
- opts.OrderBy = db.SearchOrderByAlphabetically
+ orderBy := opts.OrderBy
+ if len(orderBy) == 0 {
+ orderBy = db.SearchOrderByAlphabetically
}
args := make([]any, 0)
if opts.PriorityOwnerID > 0 {
- opts.OrderBy = db.SearchOrderBy(fmt.Sprintf("CASE WHEN owner_id = ? THEN 0 ELSE owner_id END, %s", opts.OrderBy))
+ orderBy = db.SearchOrderBy(fmt.Sprintf("CASE WHEN owner_id = ? THEN 0 ELSE owner_id END, %s", orderBy))
args = append(args, opts.PriorityOwnerID)
} else if strings.Count(opts.Keyword, "/") == 1 {
// With "owner/repo" search times, prioritise results which match the owner field
orgName := strings.Split(opts.Keyword, "/")[0]
- opts.OrderBy = db.SearchOrderBy(fmt.Sprintf("CASE WHEN owner_name LIKE ? THEN 0 ELSE 1 END, %s", opts.OrderBy))
+ orderBy = db.SearchOrderBy(fmt.Sprintf("CASE WHEN owner_name LIKE ? THEN 0 ELSE 1 END, %s", orderBy))
args = append(args, orgName)
}
@@ -628,9 +625,9 @@ func searchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, c
}
}
- sess = sess.Where(cond).OrderBy(opts.OrderBy.String(), args...)
+ sess = sess.Where(cond).OrderBy(orderBy.String(), args...)
if opts.PageSize > 0 {
- sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
+ sess = sess.Limit(opts.PageSize, (page-1)*opts.PageSize)
}
return sess, count, nil
}
@@ -694,14 +691,14 @@ func AccessibleRepositoryCondition(user *user_model.User, unitType unit.Type) bu
// SearchRepositoryByName takes keyword and part of repository name to search,
// it returns results in given range and number of total results.
-func SearchRepositoryByName(ctx context.Context, opts *SearchRepoOptions) (RepositoryList, int64, error) {
+func SearchRepositoryByName(ctx context.Context, opts SearchRepoOptions) (RepositoryList, int64, error) {
opts.IncludeDescription = false
return SearchRepository(ctx, opts)
}
// SearchRepositoryIDs takes keyword and part of repository name to search,
// it returns results in given range and number of total results.
-func SearchRepositoryIDs(ctx context.Context, opts *SearchRepoOptions) ([]int64, int64, error) {
+func SearchRepositoryIDs(ctx context.Context, opts SearchRepoOptions) ([]int64, int64, error) {
opts.IncludeDescription = false
cond := SearchRepositoryCondition(opts)
@@ -745,7 +742,7 @@ func FindUserCodeAccessibleOwnerRepoIDs(ctx context.Context, ownerID int64, user
}
// GetUserRepositories returns a list of repositories of given user.
-func GetUserRepositories(ctx context.Context, opts *SearchRepoOptions) (RepositoryList, int64, error) {
+func GetUserRepositories(ctx context.Context, opts SearchRepoOptions) (RepositoryList, int64, error) {
if len(opts.OrderBy) == 0 {
opts.OrderBy = "updated_unix DESC"
}
@@ -772,5 +769,5 @@ func GetUserRepositories(ctx context.Context, opts *SearchRepoOptions) (Reposito
sess = sess.Where(cond).OrderBy(opts.OrderBy.String())
repos := make(RepositoryList, 0, opts.PageSize)
- return repos, count, db.SetSessionPagination(sess, opts).Find(&repos)
+ return repos, count, db.SetSessionPagination(sess, &opts).Find(&repos)
}
diff --git a/models/repo/repo_list_test.go b/models/repo/repo_list_test.go
index ca6007f6c7..7eb76416c2 100644
--- a/models/repo/repo_list_test.go
+++ b/models/repo/repo_list_test.go
@@ -17,162 +17,162 @@ import (
func getTestCases() []struct {
name string
- opts *repo_model.SearchRepoOptions
+ opts repo_model.SearchRepoOptions
count int
} {
testCases := []struct {
name string
- opts *repo_model.SearchRepoOptions
+ opts repo_model.SearchRepoOptions
count int
}{
{
name: "PublicRepositoriesByName",
- opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, Collaborate: optional.Some(false)},
count: 7,
},
{
name: "PublicAndPrivateRepositoriesByName",
- opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: optional.Some(false)},
count: 14,
},
{
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFirstPage",
- opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
count: 14,
},
{
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitSecondPage",
- opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
count: 14,
},
{
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitThirdPage",
- opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
count: 14,
},
{
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFourthPage",
- opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
count: 14,
},
{
name: "PublicRepositoriesOfUser",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: optional.Some(false)},
count: 2,
},
{
name: "PublicRepositoriesOfUser2",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: optional.Some(false)},
count: 0,
},
{
name: "PublicRepositoriesOfOrg3",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: optional.Some(false)},
count: 2,
},
{
name: "PublicAndPrivateRepositoriesOfUser",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: optional.Some(false)},
count: 4,
},
{
name: "PublicAndPrivateRepositoriesOfUser2",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: optional.Some(false)},
count: 0,
},
{
name: "PublicAndPrivateRepositoriesOfOrg3",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: optional.Some(false)},
count: 4,
},
{
name: "PublicRepositoriesOfUserIncludingCollaborative",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15},
count: 5,
},
{
name: "PublicRepositoriesOfUser2IncludingCollaborative",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18},
count: 1,
},
{
name: "PublicRepositoriesOfOrg3IncludingCollaborative",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20},
count: 3,
},
{
name: "PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true},
count: 9,
},
{
name: "PublicAndPrivateRepositoriesOfUser2IncludingCollaborative",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true},
count: 4,
},
{
name: "PublicAndPrivateRepositoriesOfOrg3IncludingCollaborative",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true},
count: 7,
},
{
name: "PublicRepositoriesOfOrganization",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Collaborate: optional.Some(false)},
count: 1,
},
{
name: "PublicAndPrivateRepositoriesOfOrganization",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: optional.Some(false)},
count: 2,
},
{
name: "AllPublic/PublicRepositoriesByName",
- opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, AllPublic: true, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, AllPublic: true, Collaborate: optional.Some(false)},
count: 7,
},
{
name: "AllPublic/PublicAndPrivateRepositoriesByName",
- opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: optional.Some(false)},
count: 14,
},
{
name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: optional.Some(false)},
count: 34,
},
{
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: optional.Some(false)},
count: 39,
},
{
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName",
- opts: &repo_model.SearchRepoOptions{Keyword: "test", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true},
+ opts: repo_model.SearchRepoOptions{Keyword: "test", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true},
count: 15,
},
{
name: "AllPublic/PublicAndPrivateRepositoriesOfUser2IncludingCollaborativeByName",
- opts: &repo_model.SearchRepoOptions{Keyword: "test", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, AllPublic: true},
+ opts: repo_model.SearchRepoOptions{Keyword: "test", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, AllPublic: true},
count: 13,
},
{
name: "AllPublic/PublicRepositoriesOfOrganization",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: optional.Some(false), Template: optional.Some(false)},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: optional.Some(false), Template: optional.Some(false)},
count: 34,
},
{
name: "AllTemplates",
- opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: optional.Some(true)},
+ opts: repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: optional.Some(true)},
count: 2,
},
{
name: "OwnerSlashRepoSearch",
- opts: &repo_model.SearchRepoOptions{Keyword: "user/repo2", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0},
+ opts: repo_model.SearchRepoOptions{Keyword: "user/repo2", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0},
count: 2,
},
{
name: "OwnerSlashSearch",
- opts: &repo_model.SearchRepoOptions{Keyword: "user20/", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0},
+ opts: repo_model.SearchRepoOptions{Keyword: "user20/", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0},
count: 4,
},
}
@@ -184,7 +184,7 @@ func TestSearchRepository(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// test search public repository on explore page
- repos, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
+ repos, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
@@ -199,7 +199,7 @@ func TestSearchRepository(t *testing.T) {
}
assert.Equal(t, int64(1), count)
- repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
@@ -213,7 +213,7 @@ func TestSearchRepository(t *testing.T) {
assert.Len(t, repos, 2)
// test search private repository on explore page
- repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
@@ -229,7 +229,7 @@ func TestSearchRepository(t *testing.T) {
}
assert.Equal(t, int64(1), count)
- repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
@@ -244,14 +244,14 @@ func TestSearchRepository(t *testing.T) {
assert.Len(t, repos, 3)
// Test non existing owner
- repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{OwnerID: unittest.NonexistentID})
+ repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, repo_model.SearchRepoOptions{OwnerID: unittest.NonexistentID})
assert.NoError(t, err)
assert.Empty(t, repos)
assert.Equal(t, int64(0), count)
// Test search within description
- repos, count, err = repo_model.SearchRepository(db.DefaultContext, &repo_model.SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepository(db.DefaultContext, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
@@ -268,7 +268,7 @@ func TestSearchRepository(t *testing.T) {
assert.Equal(t, int64(1), count)
// Test NOT search within description
- repos, count, err = repo_model.SearchRepository(db.DefaultContext, &repo_model.SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepository(db.DefaultContext, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
@@ -374,22 +374,22 @@ func TestSearchRepositoryByTopicName(t *testing.T) {
testCases := []struct {
name string
- opts *repo_model.SearchRepoOptions
+ opts repo_model.SearchRepoOptions
count int
}{
{
name: "AllPublic/SearchPublicRepositoriesFromTopicAndName",
- opts: &repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql"},
+ opts: repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql"},
count: 2,
},
{
name: "AllPublic/OnlySearchPublicRepositoriesFromTopic",
- opts: &repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql", TopicOnly: true},
+ opts: repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql", TopicOnly: true},
count: 1,
},
{
name: "AllPublic/OnlySearchMultipleKeywordPublicRepositoriesFromTopic",
- opts: &repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql,golang", TopicOnly: true},
+ opts: repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql,golang", TopicOnly: true},
count: 2,
},
}
diff --git a/models/repo/repo_test.go b/models/repo/repo_test.go
index 001f8ecd84..66abe864fc 100644
--- a/models/repo/repo_test.go
+++ b/models/repo/repo_test.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
var (
@@ -85,7 +86,7 @@ func TestMetas(t *testing.T) {
repo.Units = nil
- metas := repo.ComposeMetas(db.DefaultContext)
+ metas := repo.ComposeCommentMetas(db.DefaultContext)
assert.Equal(t, "testRepo", metas["repo"])
assert.Equal(t, "testOwner", metas["user"])
@@ -99,7 +100,7 @@ func TestMetas(t *testing.T) {
testSuccess := func(expectedStyle string) {
repo.Units = []*RepoUnit{&externalTracker}
repo.commonRenderingMetas = nil
- metas := repo.ComposeMetas(db.DefaultContext)
+ metas := repo.ComposeCommentMetas(db.DefaultContext)
assert.Equal(t, expectedStyle, metas["style"])
assert.Equal(t, "testRepo", metas["repo"])
assert.Equal(t, "testOwner", metas["user"])
@@ -120,7 +121,7 @@ func TestMetas(t *testing.T) {
repo, err := GetRepositoryByID(db.DefaultContext, 3)
assert.NoError(t, err)
- metas = repo.ComposeMetas(db.DefaultContext)
+ metas = repo.ComposeCommentMetas(db.DefaultContext)
assert.Contains(t, metas, "org")
assert.Contains(t, metas, "teams")
assert.Equal(t, "org3", metas["org"])
@@ -132,60 +133,43 @@ func TestGetRepositoryByURL(t *testing.T) {
t.Run("InvalidPath", func(t *testing.T) {
repo, err := GetRepositoryByURL(db.DefaultContext, "something")
-
assert.Nil(t, repo)
assert.Error(t, err)
})
- t.Run("ValidHttpURL", func(t *testing.T) {
- test := func(t *testing.T, url string) {
- repo, err := GetRepositoryByURL(db.DefaultContext, url)
-
- assert.NotNil(t, repo)
- assert.NoError(t, err)
-
- assert.Equal(t, int64(2), repo.ID)
- assert.Equal(t, int64(2), repo.OwnerID)
- }
+ testRepo2 := func(t *testing.T, url string) {
+ repo, err := GetRepositoryByURL(db.DefaultContext, url)
+ require.NoError(t, err)
+ assert.EqualValues(t, 2, repo.ID)
+ assert.EqualValues(t, 2, repo.OwnerID)
+ }
- test(t, "https://try.gitea.io/user2/repo2")
- test(t, "https://try.gitea.io/user2/repo2.git")
+ t.Run("ValidHttpURL", func(t *testing.T) {
+ testRepo2(t, "https://try.gitea.io/user2/repo2")
+ testRepo2(t, "https://try.gitea.io/user2/repo2.git")
})
t.Run("ValidGitSshURL", func(t *testing.T) {
- test := func(t *testing.T, url string) {
- repo, err := GetRepositoryByURL(db.DefaultContext, url)
-
- assert.NotNil(t, repo)
- assert.NoError(t, err)
-
- assert.Equal(t, int64(2), repo.ID)
- assert.Equal(t, int64(2), repo.OwnerID)
- }
-
- test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2")
- test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2.git")
+ testRepo2(t, "git+ssh://sshuser@try.gitea.io/user2/repo2")
+ testRepo2(t, "git+ssh://sshuser@try.gitea.io/user2/repo2.git")
- test(t, "git+ssh://try.gitea.io/user2/repo2")
- test(t, "git+ssh://try.gitea.io/user2/repo2.git")
+ testRepo2(t, "git+ssh://try.gitea.io/user2/repo2")
+ testRepo2(t, "git+ssh://try.gitea.io/user2/repo2.git")
})
t.Run("ValidImplicitSshURL", func(t *testing.T) {
- test := func(t *testing.T, url string) {
- repo, err := GetRepositoryByURL(db.DefaultContext, url)
-
- assert.NotNil(t, repo)
- assert.NoError(t, err)
+ testRepo2(t, "sshuser@try.gitea.io:user2/repo2")
+ testRepo2(t, "sshuser@try.gitea.io:user2/repo2.git")
+ testRelax := func(t *testing.T, url string) {
+ repo, err := GetRepositoryByURLRelax(db.DefaultContext, url)
+ require.NoError(t, err)
assert.Equal(t, int64(2), repo.ID)
assert.Equal(t, int64(2), repo.OwnerID)
}
-
- test(t, "sshuser@try.gitea.io:user2/repo2")
- test(t, "sshuser@try.gitea.io:user2/repo2.git")
-
- test(t, "try.gitea.io:user2/repo2")
- test(t, "try.gitea.io:user2/repo2.git")
+ // TODO: it doesn't seem to be common git ssh URL, should we really support this?
+ testRelax(t, "try.gitea.io:user2/repo2")
+ testRelax(t, "try.gitea.io:user2/repo2.git")
})
}
@@ -199,23 +183,30 @@ func TestComposeSSHCloneURL(t *testing.T) {
setting.SSH.Domain = "domain"
setting.SSH.Port = 22
setting.Repository.UseCompatSSHURI = false
- assert.Equal(t, "git@domain:user/repo.git", ComposeSSHCloneURL("user", "repo"))
+ assert.Equal(t, "git@domain:user/repo.git", ComposeSSHCloneURL(&user_model.User{Name: "doer"}, "user", "repo"))
setting.Repository.UseCompatSSHURI = true
- assert.Equal(t, "ssh://git@domain/user/repo.git", ComposeSSHCloneURL("user", "repo"))
+ assert.Equal(t, "ssh://git@domain/user/repo.git", ComposeSSHCloneURL(&user_model.User{Name: "doer"}, "user", "repo"))
// test SSH_DOMAIN while use non-standard SSH port
setting.SSH.Port = 123
setting.Repository.UseCompatSSHURI = false
- assert.Equal(t, "ssh://git@domain:123/user/repo.git", ComposeSSHCloneURL("user", "repo"))
+ assert.Equal(t, "ssh://git@domain:123/user/repo.git", ComposeSSHCloneURL(nil, "user", "repo"))
setting.Repository.UseCompatSSHURI = true
- assert.Equal(t, "ssh://git@domain:123/user/repo.git", ComposeSSHCloneURL("user", "repo"))
+ assert.Equal(t, "ssh://git@domain:123/user/repo.git", ComposeSSHCloneURL(nil, "user", "repo"))
// test IPv6 SSH_DOMAIN
setting.Repository.UseCompatSSHURI = false
setting.SSH.Domain = "::1"
setting.SSH.Port = 22
- assert.Equal(t, "git@[::1]:user/repo.git", ComposeSSHCloneURL("user", "repo"))
+ assert.Equal(t, "git@[::1]:user/repo.git", ComposeSSHCloneURL(nil, "user", "repo"))
setting.SSH.Port = 123
- assert.Equal(t, "ssh://git@[::1]:123/user/repo.git", ComposeSSHCloneURL("user", "repo"))
+ assert.Equal(t, "ssh://git@[::1]:123/user/repo.git", ComposeSSHCloneURL(nil, "user", "repo"))
+
+ setting.SSH.User = "(DOER_USERNAME)"
+ setting.SSH.Domain = "domain"
+ setting.SSH.Port = 22
+ assert.Equal(t, "doer@domain:user/repo.git", ComposeSSHCloneURL(&user_model.User{Name: "doer"}, "user", "repo"))
+ setting.SSH.Port = 123
+ assert.Equal(t, "ssh://doer@domain:123/user/repo.git", ComposeSSHCloneURL(&user_model.User{Name: "doer"}, "user", "repo"))
}
func TestIsUsableRepoName(t *testing.T) {
@@ -225,7 +216,23 @@ func TestIsUsableRepoName(t *testing.T) {
assert.Error(t, IsUsableRepoName("-"))
assert.Error(t, IsUsableRepoName("🌞"))
+ assert.Error(t, IsUsableRepoName("the/repo"))
assert.Error(t, IsUsableRepoName("the..repo"))
assert.Error(t, IsUsableRepoName("foo.wiki"))
assert.Error(t, IsUsableRepoName("foo.git"))
+ assert.Error(t, IsUsableRepoName("foo.RSS"))
+}
+
+func TestIsValidSSHAccessRepoName(t *testing.T) {
+ assert.True(t, IsValidSSHAccessRepoName("a"))
+ assert.True(t, IsValidSSHAccessRepoName("-1_."))
+ assert.True(t, IsValidSSHAccessRepoName(".profile"))
+ assert.True(t, IsValidSSHAccessRepoName("foo.wiki"))
+
+ assert.False(t, IsValidSSHAccessRepoName("-"))
+ assert.False(t, IsValidSSHAccessRepoName("🌞"))
+ assert.False(t, IsValidSSHAccessRepoName("the/repo"))
+ assert.False(t, IsValidSSHAccessRepoName("the..repo"))
+ assert.False(t, IsValidSSHAccessRepoName("foo.git"))
+ assert.False(t, IsValidSSHAccessRepoName("foo.RSS"))
}
diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go
index cb52c2c9e2..a5207bc22a 100644
--- a/models/repo/repo_unit.go
+++ b/models/repo/repo_unit.go
@@ -5,7 +5,6 @@ package repo
import (
"context"
- "fmt"
"slices"
"strings"
@@ -33,7 +32,7 @@ func IsErrUnitTypeNotExist(err error) bool {
}
func (err ErrUnitTypeNotExist) Error() string {
- return fmt.Sprintf("Unit type does not exist: %s", err.UT.LogString())
+ return "Unit type does not exist: " + err.UT.LogString()
}
func (err ErrUnitTypeNotExist) Unwrap() error {
@@ -42,12 +41,13 @@ func (err ErrUnitTypeNotExist) Unwrap() error {
// RepoUnit describes all units of a repository
type RepoUnit struct { //revive:disable-line:exported
- ID int64
- RepoID int64 `xorm:"INDEX(s)"`
- Type unit.Type `xorm:"INDEX(s)"`
- Config convert.Conversion `xorm:"TEXT"`
- CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
- EveryoneAccessMode perm.AccessMode `xorm:"NOT NULL DEFAULT 0"`
+ ID int64
+ RepoID int64 `xorm:"INDEX(s)"`
+ Type unit.Type `xorm:"INDEX(s)"`
+ Config convert.Conversion `xorm:"TEXT"`
+ CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
+ AnonymousAccessMode perm.AccessMode `xorm:"NOT NULL DEFAULT 0"`
+ EveryoneAccessMode perm.AccessMode `xorm:"NOT NULL DEFAULT 0"`
}
func init() {
@@ -185,10 +185,8 @@ func (cfg *ActionsConfig) IsWorkflowDisabled(file string) bool {
}
func (cfg *ActionsConfig) DisableWorkflow(file string) {
- for _, workflow := range cfg.DisabledWorkflows {
- if file == workflow {
- return
- }
+ if slices.Contains(cfg.DisabledWorkflows, file) {
+ return
}
cfg.DisabledWorkflows = append(cfg.DisabledWorkflows, file)
@@ -341,3 +339,9 @@ func UpdateRepoUnit(ctx context.Context, unit *RepoUnit) error {
_, err := db.GetEngine(ctx).ID(unit.ID).Update(unit)
return err
}
+
+func UpdateRepoUnitPublicAccess(ctx context.Context, unit *RepoUnit) error {
+ _, err := db.GetEngine(ctx).Where("repo_id=? AND `type`=?", unit.RepoID, unit.Type).
+ Cols("anonymous_access_mode", "everyone_access_mode").Update(unit)
+ return err
+}
diff --git a/models/repo/repo_unit_test.go b/models/repo/repo_unit_test.go
index a760594013..56dda5672d 100644
--- a/models/repo/repo_unit_test.go
+++ b/models/repo/repo_unit_test.go
@@ -12,19 +12,19 @@ import (
func TestActionsConfig(t *testing.T) {
cfg := &ActionsConfig{}
cfg.DisableWorkflow("test1.yaml")
- assert.EqualValues(t, []string{"test1.yaml"}, cfg.DisabledWorkflows)
+ assert.Equal(t, []string{"test1.yaml"}, cfg.DisabledWorkflows)
cfg.DisableWorkflow("test1.yaml")
- assert.EqualValues(t, []string{"test1.yaml"}, cfg.DisabledWorkflows)
+ assert.Equal(t, []string{"test1.yaml"}, cfg.DisabledWorkflows)
cfg.EnableWorkflow("test1.yaml")
- assert.EqualValues(t, []string{}, cfg.DisabledWorkflows)
+ assert.Equal(t, []string{}, cfg.DisabledWorkflows)
cfg.EnableWorkflow("test1.yaml")
- assert.EqualValues(t, []string{}, cfg.DisabledWorkflows)
+ assert.Equal(t, []string{}, cfg.DisabledWorkflows)
cfg.DisableWorkflow("test1.yaml")
cfg.DisableWorkflow("test2.yaml")
cfg.DisableWorkflow("test3.yaml")
- assert.EqualValues(t, "test1.yaml,test2.yaml,test3.yaml", cfg.ToString())
+ assert.Equal(t, "test1.yaml,test2.yaml,test3.yaml", cfg.ToString())
}
diff --git a/models/repo/topic_test.go b/models/repo/topic_test.go
index 1600896b6e..b6a7aed7b1 100644
--- a/models/repo/topic_test.go
+++ b/models/repo/topic_test.go
@@ -53,7 +53,7 @@ func TestAddTopic(t *testing.T) {
totalNrOfTopics++
topic, err := repo_model.GetTopicByName(db.DefaultContext, "gitea")
assert.NoError(t, err)
- assert.EqualValues(t, 1, topic.RepoCount)
+ assert.Equal(t, 1, topic.RepoCount)
topics, err = db.Find[repo_model.Topic](db.DefaultContext, &repo_model.FindTopicOptions{})
assert.NoError(t, err)
diff --git a/models/repo/transfer.go b/models/repo/transfer.go
index 43e15b33bc..3fb8cb69ab 100644
--- a/models/repo/transfer.go
+++ b/models/repo/transfer.go
@@ -61,13 +61,14 @@ func (err ErrRepoTransferInProgress) Unwrap() error {
}
// RepoTransfer is used to manage repository transfers
-type RepoTransfer struct { //nolint
+type RepoTransfer struct { //nolint:revive // export stutter
ID int64 `xorm:"pk autoincr"`
DoerID int64
Doer *user_model.User `xorm:"-"`
RecipientID int64
Recipient *user_model.User `xorm:"-"`
RepoID int64
+ Repo *Repository `xorm:"-"`
TeamIDs []int64
Teams []*organization.Team `xorm:"-"`
@@ -79,48 +80,65 @@ func init() {
db.RegisterModel(new(RepoTransfer))
}
-// LoadAttributes fetches the transfer recipient from the database
-func (r *RepoTransfer) LoadAttributes(ctx context.Context) error {
+func (r *RepoTransfer) LoadRecipient(ctx context.Context) error {
if r.Recipient == nil {
u, err := user_model.GetUserByID(ctx, r.RecipientID)
if err != nil {
return err
}
-
r.Recipient = u
}
- if r.Recipient.IsOrganization() && len(r.TeamIDs) != len(r.Teams) {
- for _, v := range r.TeamIDs {
- team, err := organization.GetTeamByID(ctx, v)
- if err != nil {
- return err
- }
+ return nil
+}
- if team.OrgID != r.Recipient.ID {
- return fmt.Errorf("team %d belongs not to org %d", v, r.Recipient.ID)
- }
+func (r *RepoTransfer) LoadRepo(ctx context.Context) error {
+ if r.Repo == nil {
+ repo, err := GetRepositoryByID(ctx, r.RepoID)
+ if err != nil {
+ return err
+ }
+ r.Repo = repo
+ }
+
+ return nil
+}
+
+// LoadAttributes fetches the transfer recipient from the database
+func (r *RepoTransfer) LoadAttributes(ctx context.Context) error {
+ if err := r.LoadRecipient(ctx); err != nil {
+ return err
+ }
+ if r.Recipient.IsOrganization() && r.Teams == nil {
+ teamsMap, err := organization.GetTeamsByIDs(ctx, r.TeamIDs)
+ if err != nil {
+ return err
+ }
+ for _, team := range teamsMap {
r.Teams = append(r.Teams, team)
}
}
+ if err := r.LoadRepo(ctx); err != nil {
+ return err
+ }
+
if r.Doer == nil {
u, err := user_model.GetUserByID(ctx, r.DoerID)
if err != nil {
return err
}
-
r.Doer = u
}
return nil
}
-// CanUserAcceptTransfer checks if the user has the rights to accept/decline a repo transfer.
+// CanUserAcceptOrRejectTransfer checks if the user has the rights to accept/decline a repo transfer.
// For user, it checks if it's himself
// For organizations, it checks if the user is able to create repos
-func (r *RepoTransfer) CanUserAcceptTransfer(ctx context.Context, u *user_model.User) bool {
+func (r *RepoTransfer) CanUserAcceptOrRejectTransfer(ctx context.Context, u *user_model.User) bool {
if err := r.LoadAttributes(ctx); err != nil {
log.Error("LoadAttributes: %v", err)
return false
@@ -166,6 +184,10 @@ func GetPendingRepositoryTransfers(ctx context.Context, opts *PendingRepositoryT
Find(&transfers)
}
+func IsRepositoryTransferExist(ctx context.Context, repoID int64) (bool, error) {
+ return db.GetEngine(ctx).Where("repo_id = ?", repoID).Exist(new(RepoTransfer))
+}
+
// GetPendingRepositoryTransfer fetches the most recent and ongoing transfer
// process for the repository
func GetPendingRepositoryTransfer(ctx context.Context, repo *Repository) (*RepoTransfer, error) {
@@ -206,13 +228,28 @@ func CreatePendingRepositoryTransfer(ctx context.Context, doer, newOwner *user_m
return err
}
+ if _, err := user_model.GetUserByID(ctx, newOwner.ID); err != nil {
+ return err
+ }
+
// Make sure repo is ready to transfer
if err := TestRepositoryReadyForTransfer(repo.Status); err != nil {
return err
}
+ exist, err := IsRepositoryTransferExist(ctx, repo.ID)
+ if err != nil {
+ return err
+ }
+ if exist {
+ return ErrRepoTransferInProgress{
+ Uname: repo.Owner.LowerName,
+ Name: repo.Name,
+ }
+ }
+
repo.Status = RepositoryPendingTransfer
- if err := UpdateRepositoryCols(ctx, repo, "status"); err != nil {
+ if err := UpdateRepositoryColsNoAutoTime(ctx, repo, "status"); err != nil {
return err
}
diff --git a/models/repo/update.go b/models/repo/update.go
index e7ca224028..64065f11c4 100644
--- a/models/repo/update.go
+++ b/models/repo/update.go
@@ -25,7 +25,7 @@ func UpdateRepositoryOwnerNames(ctx context.Context, ownerID int64, ownerName st
}
defer committer.Close()
- if _, err := db.GetEngine(ctx).Where("owner_id = ?", ownerID).Cols("owner_name").Update(&Repository{
+ if _, err := db.GetEngine(ctx).Where("owner_id = ?", ownerID).Cols("owner_name").NoAutoTime().Update(&Repository{
OwnerName: ownerName,
}); err != nil {
return err
@@ -40,9 +40,15 @@ func UpdateRepositoryUpdatedTime(ctx context.Context, repoID int64, updateTime t
return err
}
-// UpdateRepositoryCols updates repository's columns
-func UpdateRepositoryCols(ctx context.Context, repo *Repository, cols ...string) error {
- _, err := db.GetEngine(ctx).ID(repo.ID).Cols(cols...).Update(repo)
+// UpdateRepositoryColsWithAutoTime updates repository's columns and the timestamp fields automatically
+func UpdateRepositoryColsWithAutoTime(ctx context.Context, repo *Repository, colName string, moreColNames ...string) error {
+ _, err := db.GetEngine(ctx).ID(repo.ID).Cols(append([]string{colName}, moreColNames...)...).Update(repo)
+ return err
+}
+
+// UpdateRepositoryColsNoAutoTime updates repository's columns, doesn't change timestamp field automatically
+func UpdateRepositoryColsNoAutoTime(ctx context.Context, repo *Repository, colName string, moreColNames ...string) error {
+ _, err := db.GetEngine(ctx).ID(repo.ID).Cols(append([]string{colName}, moreColNames...)...).NoAutoTime().Update(repo)
return err
}
@@ -105,31 +111,31 @@ func (err ErrRepoFilesAlreadyExist) Unwrap() error {
return util.ErrAlreadyExist
}
-// CheckCreateRepository check if could created a repository
-func CheckCreateRepository(ctx context.Context, doer, u *user_model.User, name string, overwriteOrAdopt bool) error {
- if !doer.CanCreateRepo() {
- return ErrReachLimitOfRepo{u.MaxRepoCreation}
+// CheckCreateRepository check if doer could create a repository in new owner
+func CheckCreateRepository(ctx context.Context, doer, owner *user_model.User, name string, overwriteOrAdopt bool) error {
+ if !doer.CanCreateRepoIn(owner) {
+ return ErrReachLimitOfRepo{owner.MaxRepoCreation}
}
if err := IsUsableRepoName(name); err != nil {
return err
}
- has, err := IsRepositoryModelOrDirExist(ctx, u, name)
+ has, err := IsRepositoryModelOrDirExist(ctx, owner, name)
if err != nil {
return fmt.Errorf("IsRepositoryExist: %w", err)
} else if has {
- return ErrRepoAlreadyExist{u.Name, name}
+ return ErrRepoAlreadyExist{owner.Name, name}
}
- repoPath := RepoPath(u.Name, name)
+ repoPath := RepoPath(owner.Name, name)
isExist, err := util.IsExist(repoPath)
if err != nil {
log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
return err
}
if !overwriteOrAdopt && isExist {
- return ErrRepoFilesAlreadyExist{u.Name, name}
+ return ErrRepoFilesAlreadyExist{owner.Name, name}
}
return nil
}
diff --git a/models/repo/upload.go b/models/repo/upload.go
index 18834f6b83..20a8fa26fe 100644
--- a/models/repo/upload.go
+++ b/models/repo/upload.go
@@ -10,7 +10,7 @@ import (
"io"
"mime/multipart"
"os"
- "path"
+ "path/filepath"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
@@ -51,14 +51,10 @@ func init() {
db.RegisterModel(new(Upload))
}
-// UploadLocalPath returns where uploads is stored in local file system based on given UUID.
-func UploadLocalPath(uuid string) string {
- return path.Join(setting.Repository.Upload.TempPath, uuid[0:1], uuid[1:2], uuid)
-}
-
-// LocalPath returns where uploads are temporarily stored in local file system.
+// LocalPath returns where uploads are temporarily stored in local file system based on given UUID.
func (upload *Upload) LocalPath() string {
- return UploadLocalPath(upload.UUID)
+ uuid := upload.UUID
+ return setting.AppDataTempDir("repo-uploads").JoinPath(uuid[0:1], uuid[1:2], uuid)
}
// NewUpload creates a new upload object.
@@ -69,7 +65,7 @@ func NewUpload(ctx context.Context, name string, buf []byte, file multipart.File
}
localPath := upload.LocalPath()
- if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil {
+ if err = os.MkdirAll(filepath.Dir(localPath), os.ModePerm); err != nil {
return nil, fmt.Errorf("MkdirAll: %w", err)
}
@@ -128,7 +124,7 @@ func DeleteUploads(ctx context.Context, uploads ...*Upload) (err error) {
defer committer.Close()
ids := make([]int64, len(uploads))
- for i := 0; i < len(uploads); i++ {
+ for i := range uploads {
ids[i] = uploads[i].ID
}
if err = db.DeleteByIDs[Upload](ctx, ids...); err != nil {
diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go
index a9b1360df1..232087d865 100644
--- a/models/repo/user_repo.go
+++ b/models/repo/user_repo.go
@@ -5,6 +5,7 @@ package repo
import (
"context"
+ "strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
@@ -149,9 +150,9 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us
// If isShowFullName is set to true, also include full name prefix search
func GetIssuePostersWithSearch(ctx context.Context, repo *Repository, isPull bool, search string, isShowFullName bool) ([]*user_model.User, error) {
users := make([]*user_model.User, 0, 30)
- var prefixCond builder.Cond = builder.Like{"name", search + "%"}
+ var prefixCond builder.Cond = builder.Like{"lower_name", strings.ToLower(search) + "%"}
if isShowFullName {
- prefixCond = prefixCond.Or(builder.Like{"full_name", "%" + search + "%"})
+ prefixCond = prefixCond.Or(db.BuildCaseInsensitiveLike("full_name", "%"+search+"%"))
}
cond := builder.In("`user`.id",
diff --git a/models/repo/user_repo_test.go b/models/repo/user_repo_test.go
index 44ebe5f214..50c970344c 100644
--- a/models/repo/user_repo_test.go
+++ b/models/repo/user_repo_test.go
@@ -12,6 +12,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestRepoAssignees(t *testing.T) {
@@ -38,3 +39,19 @@ func TestRepoAssignees(t *testing.T) {
assert.NotContains(t, []int64{users[0].ID, users[1].ID, users[2].ID}, 15)
}
}
+
+func TestGetIssuePostersWithSearch(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
+
+ users, err := repo_model.GetIssuePostersWithSearch(db.DefaultContext, repo2, false, "USER", false /* full name */)
+ require.NoError(t, err)
+ require.Len(t, users, 1)
+ assert.Equal(t, "user2", users[0].Name)
+
+ users, err = repo_model.GetIssuePostersWithSearch(db.DefaultContext, repo2, false, "TW%O", true /* full name */)
+ require.NoError(t, err)
+ require.Len(t, users, 1)
+ assert.Equal(t, "user2", users[0].Name)
+}
diff --git a/models/repo/watch_test.go b/models/repo/watch_test.go
index c39ef607e8..7ed72386c9 100644
--- a/models/repo/watch_test.go
+++ b/models/repo/watch_test.go
@@ -36,7 +36,7 @@ func TestGetWatchers(t *testing.T) {
// One watchers are inactive, thus minus 1
assert.Len(t, watches, repo.NumWatches-1)
for _, watch := range watches {
- assert.EqualValues(t, repo.ID, watch.RepoID)
+ assert.Equal(t, repo.ID, watch.RepoID)
}
watches, err = repo_model.GetWatchers(db.DefaultContext, unittest.NonexistentID)
diff --git a/models/repo/wiki.go b/models/repo/wiki.go
index b378666a20..832e15ae0d 100644
--- a/models/repo/wiki.go
+++ b/models/repo/wiki.go
@@ -5,6 +5,7 @@
package repo
import (
+ "context"
"fmt"
"path/filepath"
"strings"
@@ -45,7 +46,7 @@ func IsErrWikiReservedName(err error) bool {
}
func (err ErrWikiReservedName) Error() string {
- return fmt.Sprintf("wiki title is reserved: %s", err.Title)
+ return "wiki title is reserved: " + err.Title
}
func (err ErrWikiReservedName) Unwrap() error {
@@ -64,7 +65,7 @@ func IsErrWikiInvalidFileName(err error) bool {
}
func (err ErrWikiInvalidFileName) Error() string {
- return fmt.Sprintf("Invalid wiki filename: %s", err.FileName)
+ return "Invalid wiki filename: " + err.FileName
}
func (err ErrWikiInvalidFileName) Unwrap() error {
@@ -72,8 +73,8 @@ func (err ErrWikiInvalidFileName) Unwrap() error {
}
// WikiCloneLink returns clone URLs of repository wiki.
-func (repo *Repository) WikiCloneLink() *CloneLink {
- return repo.cloneLink(true)
+func (repo *Repository) WikiCloneLink(ctx context.Context, doer *user_model.User) *CloneLink {
+ return repo.cloneLink(ctx, doer, repo.Name+".wiki")
}
// WikiPath returns wiki data path by given user and repository name.
diff --git a/models/repo/wiki_test.go b/models/repo/wiki_test.go
index 629986f741..103420a392 100644
--- a/models/repo/wiki_test.go
+++ b/models/repo/wiki_test.go
@@ -18,7 +18,7 @@ func TestRepository_WikiCloneLink(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
- cloneLink := repo.WikiCloneLink()
+ cloneLink := repo.WikiCloneLink(t.Context(), nil)
assert.Equal(t, "ssh://sshuser@try.gitea.io:3000/user2/repo1.wiki.git", cloneLink.SSH)
assert.Equal(t, "https://try.gitea.io/user2/repo1.wiki.git", cloneLink.HTTPS)
}
diff --git a/models/repo_test.go b/models/repo_test.go
index bcf62237f0..b6c53fd197 100644
--- a/models/repo_test.go
+++ b/models/repo_test.go
@@ -29,10 +29,10 @@ func Test_repoStatsCorrectIssueNumComments(t *testing.T) {
issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
assert.NotNil(t, issue2)
- assert.EqualValues(t, 0, issue2.NumComments) // the fixture data is wrong, but we don't fix it here
+ assert.Equal(t, 0, issue2.NumComments) // the fixture data is wrong, but we don't fix it here
assert.NoError(t, repoStatsCorrectIssueNumComments(db.DefaultContext, 2))
// reload the issue
issue2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
- assert.EqualValues(t, 1, issue2.NumComments)
+ assert.Equal(t, 1, issue2.NumComments)
}
diff --git a/models/secret/secret.go b/models/secret/secret.go
index ce0ad65a79..10a0287dfd 100644
--- a/models/secret/secret.go
+++ b/models/secret/secret.go
@@ -40,9 +40,15 @@ type Secret struct {
RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOT NULL DEFAULT 0"`
Name string `xorm:"UNIQUE(owner_repo_name) NOT NULL"`
Data string `xorm:"LONGTEXT"` // encrypted data
+ Description string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
}
+const (
+ SecretDataMaxLength = 65536
+ SecretDescriptionMaxLength = 4096
+)
+
// ErrSecretNotFound represents a "secret not found" error.
type ErrSecretNotFound struct {
Name string
@@ -57,7 +63,7 @@ func (err ErrSecretNotFound) Unwrap() error {
}
// InsertEncryptedSecret Creates, encrypts, and validates a new secret with yet unencrypted data and insert into database
-func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, data string) (*Secret, error) {
+func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, data, description string) (*Secret, error) {
if ownerID != 0 && repoID != 0 {
// It's trying to create a secret that belongs to a repository, but OwnerID has been set accidentally.
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
@@ -67,15 +73,23 @@ func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, dat
return nil, fmt.Errorf("%w: ownerID and repoID cannot be both zero, global secrets are not supported", util.ErrInvalidArgument)
}
+ if len(data) > SecretDataMaxLength {
+ return nil, util.NewInvalidArgumentErrorf("data too long")
+ }
+
+ description = util.TruncateRunes(description, SecretDescriptionMaxLength)
+
encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data)
if err != nil {
return nil, err
}
+
secret := &Secret{
- OwnerID: ownerID,
- RepoID: repoID,
- Name: strings.ToUpper(name),
- Data: encrypted,
+ OwnerID: ownerID,
+ RepoID: repoID,
+ Name: strings.ToUpper(name),
+ Data: encrypted,
+ Description: description,
}
return secret, db.Insert(ctx, secret)
}
@@ -114,16 +128,23 @@ func (opts FindSecretsOptions) ToConds() builder.Cond {
}
// UpdateSecret changes org or user reop secret.
-func UpdateSecret(ctx context.Context, secretID int64, data string) error {
+func UpdateSecret(ctx context.Context, secretID int64, data, description string) error {
+ if len(data) > SecretDataMaxLength {
+ return util.NewInvalidArgumentErrorf("data too long")
+ }
+
+ description = util.TruncateRunes(description, SecretDescriptionMaxLength)
+
encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data)
if err != nil {
return err
}
s := &Secret{
- Data: encrypted,
+ Data: encrypted,
+ Description: description,
}
- affected, err := db.GetEngine(ctx).ID(secretID).Cols("data").Update(s)
+ affected, err := db.GetEngine(ctx).ID(secretID).Cols("data", "description").Update(s)
if affected != 1 {
return ErrSecretNotFound{}
}
@@ -165,3 +186,17 @@ func GetSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) (map[
return secrets, nil
}
+
+func CountWrongRepoLevelSecrets(ctx context.Context) (int64, error) {
+ var result int64
+ _, err := db.GetEngine(ctx).SQL("SELECT count(`id`) FROM `secret` WHERE `repo_id` > 0 AND `owner_id` > 0").Get(&result)
+ return result, err
+}
+
+func UpdateWrongRepoLevelSecrets(ctx context.Context) (int64, error) {
+ result, err := db.GetEngine(ctx).Exec("UPDATE `secret` SET `owner_id` = 0 WHERE `repo_id` > 0 AND `owner_id` > 0")
+ if err != nil {
+ return 0, err
+ }
+ return result.RowsAffected()
+}
diff --git a/models/system/notice_test.go b/models/system/notice_test.go
index 599b2fb65c..9fc9e6cce1 100644
--- a/models/system/notice_test.go
+++ b/models/system/notice_test.go
@@ -45,8 +45,6 @@ func TestCreateRepositoryNotice(t *testing.T) {
unittest.AssertExistsAndLoadBean(t, noticeBean)
}
-// TODO TestRemoveAllWithNotice
-
func TestCountNotices(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
assert.Equal(t, int64(3), system.CountNotices(db.DefaultContext))
diff --git a/models/system/setting_test.go b/models/system/setting_test.go
index 8f04412fb4..7e7e0c8fca 100644
--- a/models/system/setting_test.go
+++ b/models/system/setting_test.go
@@ -21,24 +21,24 @@ func TestSettings(t *testing.T) {
rev, settings, err := system.GetAllSettings(db.DefaultContext)
assert.NoError(t, err)
- assert.EqualValues(t, 1, rev)
+ assert.Equal(t, 1, rev)
assert.Len(t, settings, 1) // there is only one "revision" key
err = system.SetSettings(db.DefaultContext, map[string]string{keyName: "true"})
assert.NoError(t, err)
rev, settings, err = system.GetAllSettings(db.DefaultContext)
assert.NoError(t, err)
- assert.EqualValues(t, 2, rev)
+ assert.Equal(t, 2, rev)
assert.Len(t, settings, 2)
- assert.EqualValues(t, "true", settings[keyName])
+ assert.Equal(t, "true", settings[keyName])
err = system.SetSettings(db.DefaultContext, map[string]string{keyName: "false"})
assert.NoError(t, err)
rev, settings, err = system.GetAllSettings(db.DefaultContext)
assert.NoError(t, err)
- assert.EqualValues(t, 3, rev)
+ assert.Equal(t, 3, rev)
assert.Len(t, settings, 2)
- assert.EqualValues(t, "false", settings[keyName])
+ assert.Equal(t, "false", settings[keyName])
// setting the same value should not trigger DuplicateKey error, and the "version" should be increased
err = system.SetSettings(db.DefaultContext, map[string]string{keyName: "false"})
@@ -47,5 +47,5 @@ func TestSettings(t *testing.T) {
rev, settings, err = system.GetAllSettings(db.DefaultContext)
assert.NoError(t, err)
assert.Len(t, settings, 2)
- assert.EqualValues(t, 4, rev)
+ assert.Equal(t, 4, rev)
}
diff --git a/models/unit/unit.go b/models/unit/unit.go
index c816fc6c68..c0560678ca 100644
--- a/models/unit/unit.go
+++ b/models/unit/unit.go
@@ -6,6 +6,7 @@ package unit
import (
"errors"
"fmt"
+ "slices"
"strings"
"sync/atomic"
@@ -20,17 +21,21 @@ type Type int
// Enumerate all the unit types
const (
- TypeInvalid Type = iota // 0 invalid
- TypeCode // 1 code
- TypeIssues // 2 issues
- TypePullRequests // 3 PRs
- TypeReleases // 4 Releases
- TypeWiki // 5 Wiki
- TypeExternalWiki // 6 ExternalWiki
- TypeExternalTracker // 7 ExternalTracker
- TypeProjects // 8 Projects
- TypePackages // 9 Packages
- TypeActions // 10 Actions
+ TypeInvalid Type = iota // 0 invalid
+
+ TypeCode // 1 code
+ TypeIssues // 2 issues
+ TypePullRequests // 3 PRs
+ TypeReleases // 4 Releases
+ TypeWiki // 5 Wiki
+ TypeExternalWiki // 6 ExternalWiki
+ TypeExternalTracker // 7 ExternalTracker
+ TypeProjects // 8 Projects
+ TypePackages // 9 Packages
+ TypeActions // 10 Actions
+
+ // FIXME: TEAM-UNIT-PERMISSION: the team unit "admin" permission's design is not right, when a new unit is added in the future,
+ // admin team won't inherit the correct admin permission for the new unit, need to have a complete fix before adding any new unit.
)
// Value returns integer value for unit type (used by template)
@@ -200,22 +205,12 @@ func LoadUnitConfig() error {
// UnitGlobalDisabled checks if unit type is global disabled
func (u Type) UnitGlobalDisabled() bool {
- for _, ud := range DisabledRepoUnitsGet() {
- if u == ud {
- return true
- }
- }
- return false
+ return slices.Contains(DisabledRepoUnitsGet(), u)
}
// CanBeDefault checks if the unit type can be a default repo unit
func (u *Type) CanBeDefault() bool {
- for _, nadU := range NotAllowedDefaultRepoUnits {
- if *u == nadU {
- return false
- }
- }
- return true
+ return !slices.Contains(NotAllowedDefaultRepoUnits, *u)
}
// Unit is a section of one repository
@@ -380,20 +375,3 @@ func AllUnitKeyNames() []string {
}
return res
}
-
-// MinUnitAccessMode returns the minial permission of the permission map
-func MinUnitAccessMode(unitsMap map[Type]perm.AccessMode) perm.AccessMode {
- res := perm.AccessModeNone
- for t, mode := range unitsMap {
- // Don't allow `TypeExternal{Tracker,Wiki}` to influence this as they can only be set to READ perms.
- if t == TypeExternalTracker || t == TypeExternalWiki {
- continue
- }
-
- // get the minial permission great than AccessModeNone except all are AccessModeNone
- if mode > perm.AccessModeNone && (res == perm.AccessModeNone || mode < res) {
- res = mode
- }
- }
- return res
-}
diff --git a/models/unittest/consistency.go b/models/unittest/consistency.go
index 71839001be..364afb5c52 100644
--- a/models/unittest/consistency.go
+++ b/models/unittest/consistency.go
@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models/db"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
"xorm.io/builder"
)
@@ -24,7 +25,7 @@ const (
var consistencyCheckMap = make(map[string]func(t assert.TestingT, bean any))
// CheckConsistencyFor test that all matching database entries are consistent
-func CheckConsistencyFor(t assert.TestingT, beansToCheck ...any) {
+func CheckConsistencyFor(t require.TestingT, beansToCheck ...any) {
for _, bean := range beansToCheck {
sliceType := reflect.SliceOf(reflect.TypeOf(bean))
sliceValue := reflect.MakeSlice(sliceType, 0, 10)
@@ -42,13 +43,11 @@ func CheckConsistencyFor(t assert.TestingT, beansToCheck ...any) {
}
}
-func checkForConsistency(t assert.TestingT, bean any) {
+func checkForConsistency(t require.TestingT, bean any) {
tb, err := db.TableInfo(bean)
assert.NoError(t, err)
f := consistencyCheckMap[tb.Name]
- if f == nil {
- assert.FailNow(t, "unknown bean type: %#v", bean)
- }
+ require.NotNil(t, f, "unknown bean type: %#v", bean)
f(t, bean)
}
@@ -71,8 +70,8 @@ func init() {
AssertCountByCond(t, "follow", builder.Eq{"user_id": user.int("ID")}, user.int("NumFollowing"))
AssertCountByCond(t, "follow", builder.Eq{"follow_id": user.int("ID")}, user.int("NumFollowers"))
if user.int("Type") != modelsUserTypeOrganization {
- assert.EqualValues(t, 0, user.int("NumMembers"), "Unexpected number of members for user id: %d", user.int("ID"))
- assert.EqualValues(t, 0, user.int("NumTeams"), "Unexpected number of teams for user id: %d", user.int("ID"))
+ assert.Equal(t, 0, user.int("NumMembers"), "Unexpected number of members for user id: %d", user.int("ID"))
+ assert.Equal(t, 0, user.int("NumTeams"), "Unexpected number of teams for user id: %d", user.int("ID"))
}
}
@@ -119,7 +118,7 @@ func init() {
assert.EqualValues(t, issue.int("NumComments"), actual, "Unexpected number of comments for issue id: %d", issue.int("ID"))
if issue.bool("IsPull") {
prRow := AssertExistsAndLoadMap(t, "pull_request", builder.Eq{"issue_id": issue.int("ID")})
- assert.EqualValues(t, parseInt(prRow["index"]), issue.int("Index"), "Unexpected index for issue id: %d", issue.int("ID"))
+ assert.Equal(t, parseInt(prRow["index"]), issue.int("Index"), "Unexpected index for issue id: %d", issue.int("ID"))
}
}
@@ -127,7 +126,7 @@ func init() {
pr := reflectionWrap(bean)
issueRow := AssertExistsAndLoadMap(t, "issue", builder.Eq{"id": pr.int("IssueID")})
assert.True(t, parseBool(issueRow["is_pull"]))
- assert.EqualValues(t, parseInt(issueRow["index"]), pr.int("Index"), "Unexpected index for pull request id: %d", pr.int("ID"))
+ assert.Equal(t, parseInt(issueRow["index"]), pr.int("Index"), "Unexpected index for pull request id: %d", pr.int("ID"))
}
checkForMilestoneConsistency := func(t assert.TestingT, bean any) {
diff --git a/models/unittest/fixtures_loader.go b/models/unittest/fixtures_loader.go
index 14686caf63..0560da8349 100644
--- a/models/unittest/fixtures_loader.go
+++ b/models/unittest/fixtures_loader.go
@@ -12,13 +12,17 @@ import (
"slices"
"strings"
+ "code.gitea.io/gitea/models/db"
+
"gopkg.in/yaml.v3"
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)
-type fixtureItem struct {
- tableName string
+type FixtureItem struct {
+ fileFullPath string
+ tableName string
+
tableNameQuoted string
sqlInserts []string
sqlInsertArgs [][]any
@@ -27,10 +31,11 @@ type fixtureItem struct {
}
type fixturesLoaderInternal struct {
+ xormEngine *xorm.Engine
+ xormTableNames map[string]bool
db *sql.DB
dbType schemas.DBType
- files []string
- fixtures map[string]*fixtureItem
+ fixtures map[string]*FixtureItem
quoteObject func(string) string
paramPlaceholder func(idx int) string
}
@@ -59,29 +64,27 @@ func (f *fixturesLoaderInternal) preprocessFixtureRow(row []map[string]any) (err
return nil
}
-func (f *fixturesLoaderInternal) prepareFixtureItem(file string) (_ *fixtureItem, err error) {
- fixture := &fixtureItem{}
- fixture.tableName, _, _ = strings.Cut(filepath.Base(file), ".")
+func (f *fixturesLoaderInternal) prepareFixtureItem(fixture *FixtureItem) (err error) {
fixture.tableNameQuoted = f.quoteObject(fixture.tableName)
if f.dbType == schemas.MSSQL {
fixture.mssqlHasIdentityColumn, err = f.mssqlTableHasIdentityColumn(f.db, fixture.tableName)
if err != nil {
- return nil, err
+ return err
}
}
- data, err := os.ReadFile(file)
+ data, err := os.ReadFile(fixture.fileFullPath)
if err != nil {
- return nil, fmt.Errorf("failed to read file %q: %w", file, err)
+ return fmt.Errorf("failed to read file %q: %w", fixture.fileFullPath, err)
}
var rows []map[string]any
if err = yaml.Unmarshal(data, &rows); err != nil {
- return nil, fmt.Errorf("failed to unmarshal yaml data from %q: %w", file, err)
+ return fmt.Errorf("failed to unmarshal yaml data from %q: %w", fixture.fileFullPath, err)
}
if err = f.preprocessFixtureRow(rows); err != nil {
- return nil, fmt.Errorf("failed to preprocess fixture rows from %q: %w", file, err)
+ return fmt.Errorf("failed to preprocess fixture rows from %q: %w", fixture.fileFullPath, err)
}
var sqlBuf []byte
@@ -107,19 +110,17 @@ func (f *fixturesLoaderInternal) prepareFixtureItem(file string) (_ *fixtureItem
sqlBuf = sqlBuf[:0]
sqlArguments = sqlArguments[:0]
}
- return fixture, nil
+ return nil
}
-func (f *fixturesLoaderInternal) loadFixtures(tx *sql.Tx, file string) (err error) {
- fixture := f.fixtures[file]
- if fixture == nil {
- if fixture, err = f.prepareFixtureItem(file); err != nil {
+func (f *fixturesLoaderInternal) loadFixtures(tx *sql.Tx, fixture *FixtureItem) (err error) {
+ if fixture.tableNameQuoted == "" {
+ if err = f.prepareFixtureItem(fixture); err != nil {
return err
}
- f.fixtures[file] = fixture
}
- _, err = tx.Exec(fmt.Sprintf("DELETE FROM %s", fixture.tableNameQuoted)) // sqlite3 doesn't support truncate
+ _, err = tx.Exec("DELETE FROM " + fixture.tableNameQuoted) // sqlite3 doesn't support truncate
if err != nil {
return err
}
@@ -147,15 +148,26 @@ func (f *fixturesLoaderInternal) Load() error {
}
defer func() { _ = tx.Rollback() }()
- for _, file := range f.files {
- if err := f.loadFixtures(tx, file); err != nil {
- return fmt.Errorf("failed to load fixtures from %s: %w", file, err)
+ for _, fixture := range f.fixtures {
+ if !f.xormTableNames[fixture.tableName] {
+ continue
+ }
+ if err := f.loadFixtures(tx, fixture); err != nil {
+ return fmt.Errorf("failed to load fixtures from %s: %w", fixture.fileFullPath, err)
}
}
- return tx.Commit()
+ if err = tx.Commit(); err != nil {
+ return err
+ }
+ for xormTableName := range f.xormTableNames {
+ if f.fixtures[xormTableName] == nil {
+ _, _ = f.xormEngine.Exec("DELETE FROM `" + xormTableName + "`")
+ }
+ }
+ return nil
}
-func FixturesFileFullPaths(dir string, files []string) ([]string, error) {
+func FixturesFileFullPaths(dir string, files []string) (map[string]*FixtureItem, error) {
if files != nil && len(files) == 0 {
return nil, nil // load nothing
}
@@ -169,20 +181,25 @@ func FixturesFileFullPaths(dir string, files []string) ([]string, error) {
files = append(files, e.Name())
}
}
- for i, file := range files {
- if !filepath.IsAbs(file) {
- files[i] = filepath.Join(dir, file)
+ fixtureItems := map[string]*FixtureItem{}
+ for _, file := range files {
+ fileFillPath := file
+ if !filepath.IsAbs(fileFillPath) {
+ fileFillPath = filepath.Join(dir, file)
}
+ tableName, _, _ := strings.Cut(filepath.Base(file), ".")
+ fixtureItems[tableName] = &FixtureItem{fileFullPath: fileFillPath, tableName: tableName}
}
- return files, nil
+ return fixtureItems, nil
}
func NewFixturesLoader(x *xorm.Engine, opts FixturesOptions) (FixturesLoader, error) {
- files, err := FixturesFileFullPaths(opts.Dir, opts.Files)
+ fixtureItems, err := FixturesFileFullPaths(opts.Dir, opts.Files)
if err != nil {
return nil, fmt.Errorf("failed to get fixtures files: %w", err)
}
- f := &fixturesLoaderInternal{db: x.DB().DB, dbType: x.Dialect().URI().DBType, files: files, fixtures: map[string]*fixtureItem{}}
+
+ f := &fixturesLoaderInternal{xormEngine: x, db: x.DB().DB, dbType: x.Dialect().URI().DBType, fixtures: fixtureItems}
switch f.dbType {
case schemas.SQLITE:
f.quoteObject = func(s string) string { return fmt.Sprintf(`"%s"`, s) }
@@ -197,5 +214,12 @@ func NewFixturesLoader(x *xorm.Engine, opts FixturesOptions) (FixturesLoader, er
f.quoteObject = func(s string) string { return fmt.Sprintf("[%s]", s) }
f.paramPlaceholder = func(idx int) string { return "?" }
}
+
+ xormBeans, _ := db.NamesToBean()
+ f.xormTableNames = map[string]bool{}
+ for _, bean := range xormBeans {
+ f.xormTableNames[db.TableName(bean)] = true
+ }
+
return f, nil
}
diff --git a/models/unittest/fixtures_test.go b/models/unittest/fixtures_test.go
index a4c55f4e55..8a4c5f1793 100644
--- a/models/unittest/fixtures_test.go
+++ b/models/unittest/fixtures_test.go
@@ -99,7 +99,7 @@ func BenchmarkFixturesLoader(b *testing.B) {
// BenchmarkFixturesLoader/Internal
// BenchmarkFixturesLoader/Internal-12 1746 670457 ns/op
b.Run("Internal", func(b *testing.B) {
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
require.NoError(b, loaderInternal.Load())
}
})
@@ -107,7 +107,7 @@ func BenchmarkFixturesLoader(b *testing.B) {
if loaderVendor == nil {
b.Skip()
}
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
require.NoError(b, loaderVendor.Load())
}
})
diff --git a/models/unittest/fscopy.go b/models/unittest/fscopy.go
index 690089bbc5..98b01815bd 100644
--- a/models/unittest/fscopy.go
+++ b/models/unittest/fscopy.go
@@ -11,35 +11,13 @@ import (
"code.gitea.io/gitea/modules/util"
)
-// Copy copies file from source to target path.
-func Copy(src, dest string) error {
- // Gather file information to set back later.
- si, err := os.Lstat(src)
- if err != nil {
- return err
- }
-
- // Handle symbolic link.
- if si.Mode()&os.ModeSymlink != 0 {
- target, err := os.Readlink(src)
- if err != nil {
- return err
- }
- // NOTE: os.Chmod and os.Chtimes don't recognize symbolic link,
- // which will lead "no such file or directory" error.
- return os.Symlink(target, dest)
- }
-
- return util.CopyFile(src, dest)
-}
-
-// Sync synchronizes the two files. This is skipped if both files
+// SyncFile synchronizes the two files. This is skipped if both files
// exist and the size, modtime, and mode match.
-func Sync(srcPath, destPath string) error {
+func SyncFile(srcPath, destPath string) error {
dest, err := os.Stat(destPath)
if err != nil {
if os.IsNotExist(err) {
- return Copy(srcPath, destPath)
+ return util.CopyFile(srcPath, destPath)
}
return err
}
@@ -50,12 +28,12 @@ func Sync(srcPath, destPath string) error {
}
if src.Size() == dest.Size() &&
- src.ModTime() == dest.ModTime() &&
+ src.ModTime().Equal(dest.ModTime()) &&
src.Mode() == dest.Mode() {
return nil
}
- return Copy(srcPath, destPath)
+ return util.CopyFile(srcPath, destPath)
}
// SyncDirs synchronizes files recursively from source to target directory.
@@ -66,6 +44,10 @@ func SyncDirs(srcPath, destPath string) error {
return err
}
+ // the keep file is used to keep the directory in a git repository, it doesn't need to be synced
+ // and go-git doesn't work with the ".keep" file (it would report errors like "ref is empty")
+ const keepFile = ".keep"
+
// find and delete all untracked files
destFiles, err := util.ListDirRecursively(destPath, &util.ListDirOptions{IncludeDir: true})
if err != nil {
@@ -73,16 +55,20 @@ func SyncDirs(srcPath, destPath string) error {
}
for _, destFile := range destFiles {
destFilePath := filepath.Join(destPath, destFile)
+ shouldRemove := filepath.Base(destFilePath) == keepFile
if _, err = os.Stat(filepath.Join(srcPath, destFile)); err != nil {
if os.IsNotExist(err) {
- // if src file does not exist, remove dest file
- if err = os.RemoveAll(destFilePath); err != nil {
- return err
- }
+ shouldRemove = true
} else {
return err
}
}
+ // if src file does not exist, remove dest file
+ if shouldRemove {
+ if err = os.RemoveAll(destFilePath); err != nil {
+ return err
+ }
+ }
}
// sync src files to dest
@@ -95,8 +81,8 @@ func SyncDirs(srcPath, destPath string) error {
// util.ListDirRecursively appends a slash to the directory name
if strings.HasSuffix(srcFile, "/") {
err = os.MkdirAll(destFilePath, os.ModePerm)
- } else {
- err = Sync(filepath.Join(srcPath, srcFile), destFilePath)
+ } else if filepath.Base(destFilePath) != keepFile {
+ err = SyncFile(filepath.Join(srcPath, srcFile), destFilePath)
}
if err != nil {
return err
diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go
index 0dcff166cc..cb60cf5f85 100644
--- a/models/unittest/testdb.go
+++ b/models/unittest/testdb.go
@@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/setting/config"
"code.gitea.io/gitea/modules/storage"
+ "code.gitea.io/gitea/modules/tempdir"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/util"
@@ -35,8 +36,8 @@ func fatalTestError(fmtStr string, args ...any) {
os.Exit(1)
}
-// InitSettings initializes config provider and load common settings for tests
-func InitSettings() {
+// InitSettingsForTesting initializes config provider and load common settings for tests
+func InitSettingsForTesting() {
setting.IsInTesting = true
log.OsExiter = func(code int) {
if code != 0 {
@@ -75,7 +76,7 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) {
testOpts := util.OptionalArg(testOptsArg, &TestOptions{})
giteaRoot = test.SetupGiteaRoot()
setting.CustomPath = filepath.Join(giteaRoot, "custom")
- InitSettings()
+ InitSettingsForTesting()
fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles}
if err := CreateTestEngine(fixturesOpts); err != nil {
@@ -84,6 +85,7 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) {
setting.IsInTesting = true
setting.AppURL = "https://try.gitea.io/"
+ setting.Domain = "try.gitea.io"
setting.RunUser = "runuser"
setting.SSH.User = "sshuser"
setting.SSH.BuiltinServerUser = "builtinuser"
@@ -91,15 +93,19 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) {
setting.SSH.Domain = "try.gitea.io"
setting.Database.Type = "sqlite3"
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
- repoRootPath, err := os.MkdirTemp(os.TempDir(), "repos")
+ repoRootPath, cleanup1, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("repos")
if err != nil {
fatalTestError("TempDir: %v\n", err)
}
+ defer cleanup1()
+
setting.RepoRootPath = repoRootPath
- appDataPath, err := os.MkdirTemp(os.TempDir(), "appdata")
+ appDataPath, cleanup2, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("appdata")
if err != nil {
fatalTestError("TempDir: %v\n", err)
}
+ defer cleanup2()
+
setting.AppDataPath = appDataPath
setting.AppWorkPath = giteaRoot
setting.StaticRootPath = giteaRoot
@@ -152,13 +158,6 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) {
fatalTestError("tear down failed: %v\n", err)
}
}
-
- if err = util.RemoveAll(repoRootPath); err != nil {
- fatalTestError("util.RemoveAll: %v\n", err)
- }
- if err = util.RemoveAll(appDataPath); err != nil {
- fatalTestError("util.RemoveAll: %v\n", err)
- }
os.Exit(exitStatus)
}
diff --git a/models/unittest/unit_tests.go b/models/unittest/unit_tests.go
index 1c5595aef8..4a4cec40ae 100644
--- a/models/unittest/unit_tests.go
+++ b/models/unittest/unit_tests.go
@@ -153,9 +153,9 @@ func DumpQueryResult(t require.TestingT, sqlOrBean any, sqlArgs ...any) {
goDB := x.DB().DB
sql, ok := sqlOrBean.(string)
if !ok {
- sql = fmt.Sprintf("SELECT * FROM %s", db.TableName(sqlOrBean))
+ sql = "SELECT * FROM " + db.TableName(sqlOrBean)
} else if !strings.Contains(sql, " ") {
- sql = fmt.Sprintf("SELECT * FROM %s", sql)
+ sql = "SELECT * FROM " + sql
}
rows, err := goDB.Query(sql, sqlArgs...)
require.NoError(t, err)
diff --git a/models/user/avatar.go b/models/user/avatar.go
index 5453c78fc6..542bd93b98 100644
--- a/models/user/avatar.go
+++ b/models/user/avatar.go
@@ -5,7 +5,6 @@ package user
import (
"context"
- "crypto/md5"
"fmt"
"image/png"
"io"
@@ -38,27 +37,32 @@ func GenerateRandomAvatar(ctx context.Context, u *User) error {
u.Avatar = avatars.HashEmail(seed)
- // Don't share the images so that we can delete them easily
- if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error {
- if err := png.Encode(w, img); err != nil {
- log.Error("Encode: %v", err)
+ _, err = storage.Avatars.Stat(u.CustomAvatarRelativePath())
+ if err != nil {
+ // If unable to Stat the avatar file (usually it means non-existing), then try to save a new one
+ // Don't share the images so that we can delete them easily
+ if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error {
+ if err := png.Encode(w, img); err != nil {
+ log.Error("Encode: %v", err)
+ }
+ return nil
+ }); err != nil {
+ return fmt.Errorf("failed to save avatar %s: %w", u.CustomAvatarRelativePath(), err)
}
- return err
- }); err != nil {
- return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err)
}
if _, err := db.GetEngine(ctx).ID(u.ID).Cols("avatar").Update(u); err != nil {
return err
}
- log.Info("New random avatar created: %d", u.ID)
return nil
}
// AvatarLinkWithSize returns a link to the user's avatar with size. size <= 0 means default size
func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
- if u.IsGhost() {
+ // ghost user was deleted, Gitea actions is a bot user, 0 means the user should be a virtual user
+ // which comes from git configure information
+ if u.IsGhost() || u.IsGiteaActions() || u.ID <= 0 {
return avatars.DefaultAvatarLink()
}
@@ -101,7 +105,7 @@ func (u *User) IsUploadAvatarChanged(data []byte) bool {
if !u.UseCustomAvatar || len(u.Avatar) == 0 {
return true
}
- avatarID := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", u.ID, md5.Sum(data)))))
+ avatarID := avatar.HashAvatar(u.ID, data)
return u.Avatar != avatarID
}
diff --git a/models/user/avatar_test.go b/models/user/avatar_test.go
index 1078875ee1..941068957c 100644
--- a/models/user/avatar_test.go
+++ b/models/user/avatar_test.go
@@ -4,13 +4,18 @@
package user
import (
+ "io"
+ "strings"
"testing"
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestUserAvatarLink(t *testing.T) {
@@ -26,3 +31,37 @@ func TestUserAvatarLink(t *testing.T) {
link = u.AvatarLink(db.DefaultContext)
assert.Equal(t, "https://localhost/sub-path/avatars/avatar.png", link)
}
+
+func TestUserAvatarGenerate(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ var err error
+ tmpDir := t.TempDir()
+ storage.Avatars, err = storage.NewLocalStorage(t.Context(), &setting.Storage{Path: tmpDir})
+ require.NoError(t, err)
+
+ u := unittest.AssertExistsAndLoadBean(t, &User{ID: 2})
+
+ // there was no avatar, generate a new one
+ assert.Empty(t, u.Avatar)
+ err = GenerateRandomAvatar(db.DefaultContext, u)
+ require.NoError(t, err)
+ assert.NotEmpty(t, u.Avatar)
+
+ // make sure the generated one exists
+ oldAvatarPath := u.CustomAvatarRelativePath()
+ _, err = storage.Avatars.Stat(u.CustomAvatarRelativePath())
+ require.NoError(t, err)
+ // and try to change its content
+ _, err = storage.Avatars.Save(u.CustomAvatarRelativePath(), strings.NewReader("abcd"), 4)
+ require.NoError(t, err)
+
+ // try to generate again
+ err = GenerateRandomAvatar(db.DefaultContext, u)
+ require.NoError(t, err)
+ assert.Equal(t, oldAvatarPath, u.CustomAvatarRelativePath())
+ f, err := storage.Avatars.Open(u.CustomAvatarRelativePath())
+ require.NoError(t, err)
+ defer f.Close()
+ content, _ := io.ReadAll(f)
+ assert.Equal(t, "abcd", string(content))
+}
diff --git a/models/user/badge.go b/models/user/badge.go
index 3ff3530a36..e475ceb748 100644
--- a/models/user/badge.go
+++ b/models/user/badge.go
@@ -19,7 +19,7 @@ type Badge struct {
}
// UserBadge represents a user badge
-type UserBadge struct { //nolint:revive
+type UserBadge struct { //nolint:revive // export stutter
ID int64 `xorm:"pk autoincr"`
BadgeID int64
UserID int64 `xorm:"INDEX"`
diff --git a/models/user/email_address.go b/models/user/email_address.go
index 74ba5f617a..2ba6a56450 100644
--- a/models/user/email_address.go
+++ b/models/user/email_address.go
@@ -8,7 +8,6 @@ import (
"context"
"fmt"
"net/mail"
- "regexp"
"strings"
"time"
@@ -153,8 +152,6 @@ func UpdateEmailAddress(ctx context.Context, email *EmailAddress) error {
return err
}
-var emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
-
// ValidateEmail check if email is a valid & allowed address
func ValidateEmail(email string) error {
if err := validateEmailBasic(email); err != nil {
@@ -514,7 +511,7 @@ func validateEmailBasic(email string) error {
return ErrEmailInvalid{email}
}
- if !emailRegexp.MatchString(email) {
+ if !globalVars().emailRegexp.MatchString(email) {
return ErrEmailCharIsNotSupported{email}
}
@@ -545,3 +542,13 @@ func IsEmailDomainAllowed(email string) bool {
return validation.IsEmailDomainListed(setting.Service.EmailDomainAllowList, email)
}
+
+func GetActivatedEmailAddresses(ctx context.Context, uid int64) ([]string, error) {
+ emails := make([]string, 0, 2)
+ if err := db.GetEngine(ctx).Table("email_address").Select("email").
+ Where("uid=? AND is_activated=?", uid, true).Asc("id").
+ Find(&emails); err != nil {
+ return nil, err
+ }
+ return emails, nil
+}
diff --git a/models/user/email_address_test.go b/models/user/email_address_test.go
index d72d873de2..c0666246b0 100644
--- a/models/user/email_address_test.go
+++ b/models/user/email_address_test.go
@@ -4,6 +4,7 @@
package user_test
import (
+ "slices"
"testing"
"code.gitea.io/gitea/models/db"
@@ -100,12 +101,7 @@ func TestListEmails(t *testing.T) {
assert.Greater(t, count, int64(5))
contains := func(match func(s *user_model.SearchEmailResult) bool) bool {
- for _, v := range emails {
- if match(v) {
- return true
- }
- }
- return false
+ return slices.ContainsFunc(emails, match)
}
assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.UID == 18 }))
@@ -205,7 +201,7 @@ func TestEmailAddressValidate(t *testing.T) {
}
for kase, err := range kases {
t.Run(kase, func(t *testing.T) {
- assert.EqualValues(t, err, user_model.ValidateEmail(kase))
+ assert.Equal(t, err, user_model.ValidateEmail(kase))
})
}
}
diff --git a/models/user/must_change_password.go b/models/user/must_change_password.go
index 7eab08de89..686847c7d7 100644
--- a/models/user/must_change_password.go
+++ b/models/user/must_change_password.go
@@ -34,7 +34,7 @@ func SetMustChangePassword(ctx context.Context, all, mustChangePassword bool, in
if !all {
include = sliceTrimSpaceDropEmpty(include)
if len(include) == 0 {
- return 0, util.NewSilentWrapErrorf(util.ErrInvalidArgument, "no users to include provided")
+ return 0, util.ErrorWrap(util.ErrInvalidArgument, "no users to include provided")
}
cond = cond.And(builder.In("lower_name", include))
diff --git a/models/user/openid.go b/models/user/openid.go
index ee4ecabae0..420c67ca18 100644
--- a/models/user/openid.go
+++ b/models/user/openid.go
@@ -11,9 +11,6 @@ import (
"code.gitea.io/gitea/modules/util"
)
-// ErrOpenIDNotExist openid is not known
-var ErrOpenIDNotExist = util.NewNotExistErrorf("OpenID is unknown")
-
// UserOpenID is the list of all OpenID identities of a user.
// Since this is a middle table, name it OpenID is not suitable, so we ignore the lint here
type UserOpenID struct { //revive:disable-line:exported
@@ -99,7 +96,7 @@ func DeleteUserOpenID(ctx context.Context, openid *UserOpenID) (err error) {
if err != nil {
return err
} else if deleted != 1 {
- return ErrOpenIDNotExist
+ return util.NewNotExistErrorf("OpenID is unknown")
}
return nil
}
diff --git a/models/user/openid_test.go b/models/user/openid_test.go
index 27e6edd1e0..708af9e653 100644
--- a/models/user/openid_test.go
+++ b/models/user/openid_test.go
@@ -11,6 +11,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestGetUserOpenIDs(t *testing.T) {
@@ -34,30 +35,23 @@ func TestGetUserOpenIDs(t *testing.T) {
func TestToggleUserOpenIDVisibility(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
oids, err := user_model.GetUserOpenIDs(db.DefaultContext, int64(2))
- if !assert.NoError(t, err) || !assert.Len(t, oids, 1) {
- return
- }
+ require.NoError(t, err)
+ require.Len(t, oids, 1)
assert.True(t, oids[0].Show)
err = user_model.ToggleUserOpenIDVisibility(db.DefaultContext, oids[0].ID)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
oids, err = user_model.GetUserOpenIDs(db.DefaultContext, int64(2))
- if !assert.NoError(t, err) || !assert.Len(t, oids, 1) {
- return
- }
+ require.NoError(t, err)
+ require.Len(t, oids, 1)
+
assert.False(t, oids[0].Show)
err = user_model.ToggleUserOpenIDVisibility(db.DefaultContext, oids[0].ID)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
oids, err = user_model.GetUserOpenIDs(db.DefaultContext, int64(2))
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
if assert.Len(t, oids, 1) {
assert.True(t, oids[0].Show)
}
diff --git a/models/user/search.go b/models/user/search.go
index 85915f4020..cfd0d011bc 100644
--- a/models/user/search.go
+++ b/models/user/search.go
@@ -45,13 +45,14 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess
var cond builder.Cond
cond = builder.Eq{"type": opts.Type}
if opts.IncludeReserved {
- if opts.Type == UserTypeIndividual {
+ switch opts.Type {
+ case UserTypeIndividual:
cond = cond.Or(builder.Eq{"type": UserTypeUserReserved}).Or(
builder.Eq{"type": UserTypeBot},
).Or(
builder.Eq{"type": UserTypeRemoteUser},
)
- } else if opts.Type == UserTypeOrganization {
+ case UserTypeOrganization:
cond = cond.Or(builder.Eq{"type": UserTypeOrganizationReserved})
}
}
@@ -136,7 +137,7 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess
// SearchUsers takes options i.e. keyword and part of user name to search,
// it returns results in given range and number of total results.
-func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _ int64, _ error) {
+func SearchUsers(ctx context.Context, opts SearchUserOptions) (users []*User, _ int64, _ error) {
sessCount := opts.toSearchQueryBase(ctx)
defer sessCount.Close()
count, err := sessCount.Count(new(User))
@@ -151,7 +152,7 @@ func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _
sessQuery := opts.toSearchQueryBase(ctx).OrderBy(opts.OrderBy.String())
defer sessQuery.Close()
if opts.Page > 0 {
- sessQuery = db.SetSessionPagination(sessQuery, opts)
+ sessQuery = db.SetSessionPagination(sessQuery, &opts)
}
// the sql may contain JOIN, so we must only select User related columns
diff --git a/models/user/setting.go b/models/user/setting.go
index b4af0e5ccd..c65afae76c 100644
--- a/models/user/setting.go
+++ b/models/user/setting.go
@@ -5,6 +5,7 @@ package user
import (
"context"
+ "errors"
"fmt"
"strings"
@@ -114,10 +115,10 @@ func GetUserAllSettings(ctx context.Context, uid int64) (map[string]*Setting, er
func validateUserSettingKey(key string) error {
if len(key) == 0 {
- return fmt.Errorf("setting key must be set")
+ return errors.New("setting key must be set")
}
if strings.ToLower(key) != key {
- return fmt.Errorf("setting key should be lowercase")
+ return errors.New("setting key should be lowercase")
}
return nil
}
diff --git a/models/user/setting_keys.go b/models/user/setting_keys.go
index 3149aae18b..2c2ed078be 100644
--- a/models/user/setting_keys.go
+++ b/models/user/setting_keys.go
@@ -10,6 +10,7 @@ const (
SettingsKeyDiffWhitespaceBehavior = "diff.whitespace_behaviour"
// SettingsKeyShowOutdatedComments is the setting key wether or not to show outdated comments in PRs
SettingsKeyShowOutdatedComments = "comment_code.show_outdated"
+
// UserActivityPubPrivPem is user's private key
UserActivityPubPrivPem = "activitypub.priv_pem"
// UserActivityPubPubPem is user's public key
@@ -18,4 +19,6 @@ const (
SignupIP = "signup.ip"
// SignupUserAgent is the user agent that the user signed up with
SignupUserAgent = "signup.user_agent"
+
+ SettingsKeyCodeViewShowFileTree = "code_view.show_file_tree"
)
diff --git a/models/user/setting_test.go b/models/user/setting_test.go
index c607d9fd00..3c199013f3 100644
--- a/models/user/setting_test.go
+++ b/models/user/setting_test.go
@@ -30,15 +30,15 @@ func TestSettings(t *testing.T) {
settings, err := user_model.GetSettings(db.DefaultContext, 99, []string{keyName})
assert.NoError(t, err)
assert.Len(t, settings, 1)
- assert.EqualValues(t, newSetting.SettingValue, settings[keyName].SettingValue)
+ assert.Equal(t, newSetting.SettingValue, settings[keyName].SettingValue)
settingValue, err := user_model.GetUserSetting(db.DefaultContext, 99, keyName)
assert.NoError(t, err)
- assert.EqualValues(t, newSetting.SettingValue, settingValue)
+ assert.Equal(t, newSetting.SettingValue, settingValue)
settingValue, err = user_model.GetUserSetting(db.DefaultContext, 99, "no_such")
assert.NoError(t, err)
- assert.EqualValues(t, "", settingValue)
+ assert.Empty(t, settingValue)
// updated setting
updatedSetting := &user_model.Setting{UserID: 99, SettingKey: keyName, SettingValue: "Updated"}
@@ -49,7 +49,7 @@ func TestSettings(t *testing.T) {
settings, err = user_model.GetUserAllSettings(db.DefaultContext, 99)
assert.NoError(t, err)
assert.Len(t, settings, 1)
- assert.EqualValues(t, updatedSetting.SettingValue, settings[updatedSetting.SettingKey].SettingValue)
+ assert.Equal(t, updatedSetting.SettingValue, settings[updatedSetting.SettingKey].SettingValue)
// delete setting
err = user_model.DeleteUserSetting(db.DefaultContext, 99, keyName)
diff --git a/models/user/user.go b/models/user/user.go
index 19879fbcc7..7c871bf575 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -14,6 +14,7 @@ import (
"path/filepath"
"regexp"
"strings"
+ "sync"
"time"
"unicode"
@@ -213,7 +214,7 @@ func (u *User) GetPlaceholderEmail() string {
return fmt.Sprintf("%s@%s", u.LowerName, setting.Service.NoReplyAddress)
}
-// GetEmail returns an noreply email, if the user has set to keep his
+// GetEmail returns a noreply email, if the user has set to keep his
// email address private, otherwise the primary email address.
func (u *User) GetEmail() string {
if u.KeepEmailPrivate {
@@ -246,19 +247,20 @@ func (u *User) MaxCreationLimit() int {
return u.MaxRepoCreation
}
-// CanCreateRepo returns if user login can create a repository
-// NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised
-func (u *User) CanCreateRepo() bool {
+// CanCreateRepoIn checks whether the doer(u) can create a repository in the owner
+// NOTE: functions calling this assume a failure due to repository count limit; it ONLY checks the repo number LIMIT, if new checks are added, those functions should be revised
+func (u *User) CanCreateRepoIn(owner *User) bool {
if u.IsAdmin {
return true
}
- if u.MaxRepoCreation <= -1 {
- if setting.Repository.MaxCreationLimit <= -1 {
+ const noLimit = -1
+ if owner.MaxRepoCreation == noLimit {
+ if setting.Repository.MaxCreationLimit == noLimit {
return true
}
- return u.NumRepos < setting.Repository.MaxCreationLimit
+ return owner.NumRepos < setting.Repository.MaxCreationLimit
}
- return u.NumRepos < u.MaxRepoCreation
+ return owner.NumRepos < owner.MaxRepoCreation
}
// CanCreateOrganization returns true if user can create organisation.
@@ -271,13 +273,12 @@ func (u *User) CanEditGitHook() bool {
return !setting.DisableGitHooks && (u.IsAdmin || u.AllowGitHook)
}
-// CanForkRepo returns if user login can fork a repository
-// It checks especially that the user can create repos, and potentially more
-func (u *User) CanForkRepo() bool {
+// CanForkRepoIn ONLY checks repository count limit
+func (u *User) CanForkRepoIn(owner *User) bool {
if setting.Repository.AllowForkWithoutMaximumLimit {
return true
}
- return u.CanCreateRepo()
+ return u.CanCreateRepoIn(owner)
}
// CanImportLocal returns true if user can migrate repository by local path.
@@ -384,11 +385,12 @@ func (u *User) ValidatePassword(passwd string) bool {
}
// IsPasswordSet checks if the password is set or left empty
+// TODO: It's better to clarify the "password" behavior for different types (individual, bot)
func (u *User) IsPasswordSet() bool {
- return len(u.Passwd) != 0
+ return u.Passwd != ""
}
-// IsOrganization returns true if user is actually a organization.
+// IsOrganization returns true if user is actually an organization.
func (u *User) IsOrganization() bool {
return u.Type == UserTypeOrganization
}
@@ -398,13 +400,14 @@ func (u *User) IsIndividual() bool {
return u.Type == UserTypeIndividual
}
-func (u *User) IsUser() bool {
- return u.Type == UserTypeIndividual || u.Type == UserTypeBot
+// IsTypeBot returns whether the user is of type bot
+func (u *User) IsTypeBot() bool {
+ return u.Type == UserTypeBot
}
-// IsBot returns whether or not the user is of type bot
-func (u *User) IsBot() bool {
- return u.Type == UserTypeBot
+// IsTokenAccessAllowed returns whether the user is an individual or a bot (which allows for token access)
+func (u *User) IsTokenAccessAllowed() bool {
+ return u.Type == UserTypeIndividual || u.Type == UserTypeBot
}
// DisplayName returns full name if it's not empty,
@@ -417,19 +420,9 @@ func (u *User) DisplayName() string {
return u.Name
}
-var emailToReplacer = strings.NewReplacer(
- "\n", "",
- "\r", "",
- "<", "",
- ">", "",
- ",", "",
- ":", "",
- ";", "",
-)
-
// EmailTo returns a string suitable to be put into a e-mail `To:` header.
func (u *User) EmailTo() string {
- sanitizedDisplayName := emailToReplacer.Replace(u.DisplayName())
+ sanitizedDisplayName := globalVars().emailToReplacer.Replace(u.DisplayName())
// should be an edge case but nice to have
if sanitizedDisplayName == u.Email {
@@ -502,10 +495,10 @@ func (u *User) IsMailable() bool {
return u.IsActive
}
-// IsUserExist checks if given user name exist,
-// the user name should be noncased unique.
+// IsUserExist checks if given username exist,
+// the username should be non-cased unique.
// If uid is presented, then check will rule out that one,
-// it is used when update a user name in settings page.
+// it is used when update a username in settings page.
func IsUserExist(ctx context.Context, uid int64, name string) (bool, error) {
if len(name) == 0 {
return false, nil
@@ -515,7 +508,7 @@ func IsUserExist(ctx context.Context, uid int64, name string) (bool, error) {
Get(&User{LowerName: strings.ToLower(name)})
}
-// Note: As of the beginning of 2022, it is recommended to use at least
+// SaltByteLength as of the beginning of 2022, it is recommended to use at least
// 64 bits of salt, but NIST is already recommending to use to 128 bits.
// (16 bytes = 16 * 8 = 128 bits)
const SaltByteLength = 16
@@ -526,28 +519,58 @@ func GetUserSalt() (string, error) {
if err != nil {
return "", err
}
- // Returns a 32 bytes long string.
+ // Returns a 32-byte long string.
return hex.EncodeToString(rBytes), nil
}
-// Note: The set of characters here can safely expand without a breaking change,
-// but characters removed from this set can cause user account linking to break
-var (
- customCharsReplacement = strings.NewReplacer("Æ", "AE")
- removeCharsRE = regexp.MustCompile("['`´]")
- transformDiacritics = transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
- replaceCharsHyphenRE = regexp.MustCompile(`[\s~+]`)
-)
+type globalVarsStruct struct {
+ customCharsReplacement *strings.Replacer
+ removeCharsRE *regexp.Regexp
+ transformDiacritics transform.Transformer
+ replaceCharsHyphenRE *regexp.Regexp
+ emailToReplacer *strings.Replacer
+ emailRegexp *regexp.Regexp
+ systemUserNewFuncs map[int64]func() *User
+}
+
+var globalVars = sync.OnceValue(func() *globalVarsStruct {
+ return &globalVarsStruct{
+ // Note: The set of characters here can safely expand without a breaking change,
+ // but characters removed from this set can cause user account linking to break
+ customCharsReplacement: strings.NewReplacer("Æ", "AE"),
+
+ removeCharsRE: regexp.MustCompile("['`´]"),
+ transformDiacritics: transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC),
+ replaceCharsHyphenRE: regexp.MustCompile(`[\s~+]`),
+
+ emailToReplacer: strings.NewReplacer(
+ "\n", "",
+ "\r", "",
+ "<", "",
+ ">", "",
+ ",", "",
+ ":", "",
+ ";", "",
+ ),
+ emailRegexp: regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"),
+
+ systemUserNewFuncs: map[int64]func() *User{
+ GhostUserID: NewGhostUser,
+ ActionsUserID: NewActionsUser,
+ },
+ }
+})
// NormalizeUserName only takes the name part if it is an email address, transforms it diacritics to ASCII characters.
// It returns a string with the single-quotes removed, and any other non-supported username characters are replaced with a `-` character
func NormalizeUserName(s string) (string, error) {
+ vars := globalVars()
s, _, _ = strings.Cut(s, "@")
- strDiacriticsRemoved, n, err := transform.String(transformDiacritics, customCharsReplacement.Replace(s))
+ strDiacriticsRemoved, n, err := transform.String(vars.transformDiacritics, vars.customCharsReplacement.Replace(s))
if err != nil {
return "", fmt.Errorf("failed to normalize the string of provided username %q at position %d", s, n)
}
- return replaceCharsHyphenRE.ReplaceAllLiteralString(removeCharsRE.ReplaceAllLiteralString(strDiacriticsRemoved, ""), "-"), nil
+ return vars.replaceCharsHyphenRE.ReplaceAllLiteralString(vars.removeCharsRE.ReplaceAllLiteralString(strDiacriticsRemoved, ""), "-"), nil
}
var (
@@ -805,6 +828,21 @@ func IsLastAdminUser(ctx context.Context, user *User) bool {
type CountUserFilter struct {
LastLoginSince *int64
IsAdmin optional.Option[bool]
+ IsActive optional.Option[bool]
+}
+
+// HasUsers checks whether there are any users in the database, or only one user exists.
+func HasUsers(ctx context.Context) (ret struct {
+ HasAnyUser, HasOnlyOneUser bool
+}, err error,
+) {
+ res, err := db.GetEngine(ctx).Table(&User{}).Cols("id").Limit(2).Query()
+ if err != nil {
+ return ret, fmt.Errorf("error checking user existence: %w", err)
+ }
+ ret.HasAnyUser = len(res) != 0
+ ret.HasOnlyOneUser = len(res) == 1
+ return ret, nil
}
// CountUsers returns number of users.
@@ -825,6 +863,10 @@ func countUsers(ctx context.Context, opts *CountUserFilter) int64 {
if opts.IsAdmin.Has() {
cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.Value()})
}
+
+ if opts.IsActive.Has() {
+ cond = cond.And(builder.Eq{"is_active": opts.IsActive.Value()})
+ }
}
count, err := sess.Where(cond).Count(new(User))
@@ -963,30 +1005,28 @@ func GetUserByIDs(ctx context.Context, ids []int64) ([]*User, error) {
return users, err
}
-// GetPossibleUserByID returns the user if id > 0 or return system usrs if id < 0
+// GetPossibleUserByID returns the user if id > 0 or returns system user if id < 0
func GetPossibleUserByID(ctx context.Context, id int64) (*User, error) {
- switch id {
- case GhostUserID:
- return NewGhostUser(), nil
- case ActionsUserID:
- return NewActionsUser(), nil
- case 0:
+ if id < 0 {
+ if newFunc, ok := globalVars().systemUserNewFuncs[id]; ok {
+ return newFunc(), nil
+ }
+ return nil, ErrUserNotExist{UID: id}
+ } else if id == 0 {
return nil, ErrUserNotExist{}
- default:
- return GetUserByID(ctx, id)
}
+ return GetUserByID(ctx, id)
}
-// GetPossibleUserByIDs returns the users if id > 0 or return system users if id < 0
+// GetPossibleUserByIDs returns the users if id > 0 or returns system users if id < 0
func GetPossibleUserByIDs(ctx context.Context, ids []int64) ([]*User, error) {
uniqueIDs := container.SetOf(ids...)
users := make([]*User, 0, len(ids))
_ = uniqueIDs.Remove(0)
- if uniqueIDs.Remove(GhostUserID) {
- users = append(users, NewGhostUser())
- }
- if uniqueIDs.Remove(ActionsUserID) {
- users = append(users, NewActionsUser())
+ for systemUID, newFunc := range globalVars().systemUserNewFuncs {
+ if uniqueIDs.Remove(systemUID) {
+ users = append(users, newFunc())
+ }
}
res, err := GetUserByIDs(ctx, uniqueIDs.Values())
if err != nil {
@@ -996,7 +1036,7 @@ func GetPossibleUserByIDs(ctx context.Context, ids []int64) ([]*User, error) {
return users, nil
}
-// GetUserByNameCtx returns user by given name.
+// GetUserByName returns user by given name.
func GetUserByName(ctx context.Context, name string) (*User, error) {
if len(name) == 0 {
return nil, ErrUserNotExist{Name: name}
@@ -1027,8 +1067,8 @@ func GetUserEmailsByNames(ctx context.Context, names []string) []string {
return mails
}
-// GetMaileableUsersByIDs gets users from ids, but only if they can receive mails
-func GetMaileableUsersByIDs(ctx context.Context, ids []int64, isMention bool) ([]*User, error) {
+// GetMailableUsersByIDs gets users from ids, but only if they can receive mails
+func GetMailableUsersByIDs(ctx context.Context, ids []int64, isMention bool) ([]*User, error) {
if len(ids) == 0 {
return nil, nil
}
@@ -1053,17 +1093,6 @@ func GetMaileableUsersByIDs(ctx context.Context, ids []int64, isMention bool) ([
Find(&ous)
}
-// GetUserNamesByIDs returns usernames for all resolved users from a list of Ids.
-func GetUserNamesByIDs(ctx context.Context, ids []int64) ([]string, error) {
- unames := make([]string, 0, len(ids))
- err := db.GetEngine(ctx).In("id", ids).
- Table("user").
- Asc("name").
- Cols("name").
- Find(&unames)
- return unames, err
-}
-
// GetUserNameByID returns username for the id
func GetUserNameByID(ctx context.Context, id int64) (string, error) {
var name string
@@ -1119,28 +1148,96 @@ func ValidateCommitWithEmail(ctx context.Context, c *git.Commit) *User {
}
// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
-func ValidateCommitsWithEmails(ctx context.Context, oldCommits []*git.Commit) []*UserCommit {
+func ValidateCommitsWithEmails(ctx context.Context, oldCommits []*git.Commit) ([]*UserCommit, error) {
var (
- emails = make(map[string]*User)
newCommits = make([]*UserCommit, 0, len(oldCommits))
+ emailSet = make(container.Set[string])
)
for _, c := range oldCommits {
- var u *User
if c.Author != nil {
- if v, ok := emails[c.Author.Email]; !ok {
- u, _ = GetUserByEmail(ctx, c.Author.Email)
- emails[c.Author.Email] = u
- } else {
- u = v
- }
+ emailSet.Add(c.Author.Email)
}
+ }
+
+ emailUserMap, err := GetUsersByEmails(ctx, emailSet.Values())
+ if err != nil {
+ return nil, err
+ }
+ for _, c := range oldCommits {
+ user := emailUserMap.GetByEmail(c.Author.Email) // FIXME: why ValidateCommitsWithEmails uses "Author", but ParseCommitsWithSignature uses "Committer"?
+ if user == nil {
+ user = &User{
+ Name: c.Author.Name,
+ Email: c.Author.Email,
+ }
+ }
newCommits = append(newCommits, &UserCommit{
- User: u,
+ User: user,
Commit: c,
})
}
- return newCommits
+ return newCommits, nil
+}
+
+type EmailUserMap struct {
+ m map[string]*User
+}
+
+func (eum *EmailUserMap) GetByEmail(email string) *User {
+ return eum.m[strings.ToLower(email)]
+}
+
+func GetUsersByEmails(ctx context.Context, emails []string) (*EmailUserMap, error) {
+ if len(emails) == 0 {
+ return nil, nil
+ }
+
+ needCheckEmails := make(container.Set[string])
+ needCheckUserNames := make(container.Set[string])
+ for _, email := range emails {
+ if strings.HasSuffix(email, "@"+setting.Service.NoReplyAddress) {
+ username := strings.TrimSuffix(email, "@"+setting.Service.NoReplyAddress)
+ needCheckUserNames.Add(strings.ToLower(username))
+ } else {
+ needCheckEmails.Add(strings.ToLower(email))
+ }
+ }
+
+ emailAddresses := make([]*EmailAddress, 0, len(needCheckEmails))
+ if err := db.GetEngine(ctx).In("lower_email", needCheckEmails.Values()).
+ And("is_activated=?", true).
+ Find(&emailAddresses); err != nil {
+ return nil, err
+ }
+ userIDs := make(container.Set[int64])
+ for _, email := range emailAddresses {
+ userIDs.Add(email.UID)
+ }
+ results := make(map[string]*User, len(emails))
+
+ if len(userIDs) > 0 {
+ users, err := GetUsersMapByIDs(ctx, userIDs.Values())
+ if err != nil {
+ return nil, err
+ }
+
+ for _, email := range emailAddresses {
+ user := users[email.UID]
+ if user != nil {
+ results[email.LowerEmail] = user
+ }
+ }
+ }
+
+ users := make(map[int64]*User, len(needCheckUserNames))
+ if err := db.GetEngine(ctx).In("lower_name", needCheckUserNames.Values()).Find(&users); err != nil {
+ return nil, err
+ }
+ for _, user := range users {
+ results[strings.ToLower(user.GetPlaceholderEmail())] = user
+ }
+ return &EmailUserMap{results}, nil
}
// GetUserByEmail returns the user object by given e-mail if exists.
@@ -1161,8 +1258,8 @@ func GetUserByEmail(ctx context.Context, email string) (*User, error) {
}
// Finally, if email address is the protected email address:
- if strings.HasSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) {
- username := strings.TrimSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress))
+ if strings.HasSuffix(email, "@"+setting.Service.NoReplyAddress) {
+ username := strings.TrimSuffix(email, "@"+setting.Service.NoReplyAddress)
user := &User{}
has, err := db.GetEngine(ctx).Where("lower_name=?", username).Get(user)
if err != nil {
diff --git a/models/user/user_list.go b/models/user/user_list.go
new file mode 100644
index 0000000000..1b6a27dd86
--- /dev/null
+++ b/models/user/user_list.go
@@ -0,0 +1,48 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package user
+
+import (
+ "context"
+
+ "code.gitea.io/gitea/models/db"
+)
+
+func GetUsersMapByIDs(ctx context.Context, userIDs []int64) (map[int64]*User, error) {
+ userMaps := make(map[int64]*User, len(userIDs))
+ if len(userIDs) == 0 {
+ return userMaps, nil
+ }
+
+ left := len(userIDs)
+ for left > 0 {
+ limit := min(left, db.DefaultMaxInSize)
+ err := db.GetEngine(ctx).
+ In("id", userIDs[:limit]).
+ Find(&userMaps)
+ if err != nil {
+ return nil, err
+ }
+ left -= limit
+ userIDs = userIDs[limit:]
+ }
+ return userMaps, nil
+}
+
+func GetPossibleUserFromMap(userID int64, usererMaps map[int64]*User) *User {
+ switch userID {
+ case GhostUserID:
+ return NewGhostUser()
+ case ActionsUserID:
+ return NewActionsUser()
+ case 0:
+ return nil
+ default:
+ user, ok := usererMaps[userID]
+ if !ok {
+ return NewGhostUser()
+ }
+ return user
+ }
+}
diff --git a/models/user/user_system.go b/models/user/user_system.go
index 612cdb2cae..e07274d291 100644
--- a/models/user/user_system.go
+++ b/models/user/user_system.go
@@ -10,9 +10,8 @@ import (
)
const (
- GhostUserID = -1
- GhostUserName = "Ghost"
- GhostUserLowerName = "ghost"
+ GhostUserID int64 = -1
+ GhostUserName = "Ghost"
)
// NewGhostUser creates and returns a fake user for someone has deleted their account.
@@ -20,10 +19,14 @@ func NewGhostUser() *User {
return &User{
ID: GhostUserID,
Name: GhostUserName,
- LowerName: GhostUserLowerName,
+ LowerName: strings.ToLower(GhostUserName),
}
}
+func IsGhostUserName(name string) bool {
+ return strings.EqualFold(name, GhostUserName)
+}
+
// IsGhost check if user is fake user for a deleted account
func (u *User) IsGhost() bool {
if u == nil {
@@ -32,22 +35,16 @@ func (u *User) IsGhost() bool {
return u.ID == GhostUserID && u.Name == GhostUserName
}
-// NewReplaceUser creates and returns a fake user for external user
-func NewReplaceUser(name string) *User {
- return &User{
- ID: 0,
- Name: name,
- LowerName: strings.ToLower(name),
- }
-}
-
const (
- ActionsUserID = -2
- ActionsUserName = "gitea-actions"
- ActionsFullName = "Gitea Actions"
- ActionsEmail = "teabot@gitea.io"
+ ActionsUserID int64 = -2
+ ActionsUserName = "gitea-actions"
+ ActionsUserEmail = "teabot@gitea.io"
)
+func IsGiteaActionsUserName(name string) bool {
+ return strings.EqualFold(name, ActionsUserName)
+}
+
// NewActionsUser creates and returns a fake user for running the actions.
func NewActionsUser() *User {
return &User{
@@ -55,16 +52,26 @@ func NewActionsUser() *User {
Name: ActionsUserName,
LowerName: ActionsUserName,
IsActive: true,
- FullName: ActionsFullName,
- Email: ActionsEmail,
+ FullName: "Gitea Actions",
+ Email: ActionsUserEmail,
KeepEmailPrivate: true,
LoginName: ActionsUserName,
- Type: UserTypeIndividual,
+ Type: UserTypeBot,
AllowCreateOrganization: true,
Visibility: structs.VisibleTypePublic,
}
}
-func (u *User) IsActions() bool {
+func (u *User) IsGiteaActions() bool {
return u != nil && u.ID == ActionsUserID
}
+
+func GetSystemUserByName(name string) *User {
+ if IsGhostUserName(name) {
+ return NewGhostUser()
+ }
+ if IsGiteaActionsUserName(name) {
+ return NewActionsUser()
+ }
+ return nil
+}
diff --git a/models/user/user_system_test.go b/models/user/user_system_test.go
new file mode 100644
index 0000000000..97768b509b
--- /dev/null
+++ b/models/user/user_system_test.go
@@ -0,0 +1,32 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package user
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestSystemUser(t *testing.T) {
+ u, err := GetPossibleUserByID(db.DefaultContext, -1)
+ require.NoError(t, err)
+ assert.Equal(t, "Ghost", u.Name)
+ assert.Equal(t, "ghost", u.LowerName)
+ assert.True(t, u.IsGhost())
+ assert.True(t, IsGhostUserName("gHost"))
+
+ u, err = GetPossibleUserByID(db.DefaultContext, -2)
+ require.NoError(t, err)
+ assert.Equal(t, "gitea-actions", u.Name)
+ assert.Equal(t, "gitea-actions", u.LowerName)
+ assert.True(t, u.IsGiteaActions())
+ assert.True(t, IsGiteaActionsUserName("Gitea-actionS"))
+
+ _, err = GetPossibleUserByID(db.DefaultContext, -3)
+ require.Error(t, err)
+}
diff --git a/models/user/user_test.go b/models/user/user_test.go
index 7ebc64f69e..a2597ba3f5 100644
--- a/models/user/user_test.go
+++ b/models/user/user_test.go
@@ -4,7 +4,6 @@
package user_test
import (
- "context"
"crypto/rand"
"fmt"
"strings"
@@ -20,11 +19,28 @@ import (
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
+func TestIsUsableUsername(t *testing.T) {
+ assert.NoError(t, user_model.IsUsableUsername("a"))
+ assert.NoError(t, user_model.IsUsableUsername("foo.wiki"))
+ assert.NoError(t, user_model.IsUsableUsername("foo.git"))
+
+ assert.Error(t, user_model.IsUsableUsername("a--b"))
+ assert.Error(t, user_model.IsUsableUsername("-1_."))
+ assert.Error(t, user_model.IsUsableUsername(".profile"))
+ assert.Error(t, user_model.IsUsableUsername("-"))
+ assert.Error(t, user_model.IsUsableUsername("🌞"))
+ assert.Error(t, user_model.IsUsableUsername("the..repo"))
+ assert.Error(t, user_model.IsUsableUsername("foo.RSS"))
+ assert.Error(t, user_model.IsUsableUsername("foo.PnG"))
+}
+
func TestOAuth2Application_LoadUser(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
app := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: 1})
@@ -33,14 +49,43 @@ func TestOAuth2Application_LoadUser(t *testing.T) {
assert.NotNil(t, user)
}
-func TestGetUserEmailsByNames(t *testing.T) {
+func TestUserEmails(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
-
- // ignore none active user email
- assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user9"}))
- assert.ElementsMatch(t, []string{"user8@example.com", "user5@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user5"}))
-
- assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "org7"}))
+ t.Run("GetUserEmailsByNames", func(t *testing.T) {
+ // ignore none active user email
+ assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user9"}))
+ assert.ElementsMatch(t, []string{"user8@example.com", "user5@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user5"}))
+ assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "org7"}))
+ })
+ t.Run("GetUsersByEmails", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Service.NoReplyAddress, "NoReply.gitea.internal")()
+ testGetUserByEmail := func(t *testing.T, email string, uid int64) {
+ m, err := user_model.GetUsersByEmails(db.DefaultContext, []string{email})
+ require.NoError(t, err)
+ user := m.GetByEmail(email)
+ if uid == 0 {
+ require.Nil(t, user)
+ return
+ }
+ require.NotNil(t, user)
+ assert.Equal(t, uid, user.ID)
+ }
+ cases := []struct {
+ Email string
+ UID int64
+ }{
+ {"UseR1@example.com", 1},
+ {"user1-2@example.COM", 1},
+ {"USER2@" + setting.Service.NoReplyAddress, 2},
+ {"user4@example.com", 4},
+ {"no-such", 0},
+ }
+ for _, c := range cases {
+ t.Run(c.Email, func(t *testing.T) {
+ testGetUserByEmail(t, c.Email, c.UID)
+ })
+ }
+ })
}
func TestCanCreateOrganization(t *testing.T) {
@@ -63,73 +108,73 @@ func TestCanCreateOrganization(t *testing.T) {
func TestSearchUsers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- testSuccess := func(opts *user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) {
+ testSuccess := func(opts user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) {
users, _, err := user_model.SearchUsers(db.DefaultContext, opts)
assert.NoError(t, err)
cassText := fmt.Sprintf("ids: %v, opts: %v", expectedUserOrOrgIDs, opts)
if assert.Len(t, users, len(expectedUserOrOrgIDs), "case: %s", cassText) {
for i, expectedID := range expectedUserOrOrgIDs {
- assert.EqualValues(t, expectedID, users[i].ID, "case: %s", cassText)
+ assert.Equal(t, expectedID, users[i].ID, "case: %s", cassText)
}
}
}
// test orgs
- testOrgSuccess := func(opts *user_model.SearchUserOptions, expectedOrgIDs []int64) {
+ testOrgSuccess := func(opts user_model.SearchUserOptions, expectedOrgIDs []int64) {
opts.Type = user_model.UserTypeOrganization
testSuccess(opts, expectedOrgIDs)
}
- testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1, PageSize: 2}},
+ testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1, PageSize: 2}},
[]int64{3, 6})
- testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 2, PageSize: 2}},
+ testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 2, PageSize: 2}},
[]int64{7, 17})
- testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 3, PageSize: 2}},
+ testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 3, PageSize: 2}},
[]int64{19, 25})
- testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 4, PageSize: 2}},
+ testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 4, PageSize: 2}},
[]int64{26, 41})
- testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 5, PageSize: 2}},
+ testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 5, PageSize: 2}},
[]int64{42})
- testOrgSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 6, PageSize: 2}},
+ testOrgSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 6, PageSize: 2}},
[]int64{})
// test users
- testUserSuccess := func(opts *user_model.SearchUserOptions, expectedUserIDs []int64) {
+ testUserSuccess := func(opts user_model.SearchUserOptions, expectedUserIDs []int64) {
opts.Type = user_model.UserTypeIndividual
testSuccess(opts, expectedUserIDs)
}
- testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}},
+ testUserSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}},
[]int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
- testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)},
+ testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)},
[]int64{9})
- testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
+ testUserSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
[]int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
- testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
+ testUserSuccess(user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
// order by name asc default
- testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
+ testUserSuccess(user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
- testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)},
+ testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)},
[]int64{1})
- testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)},
+ testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)},
[]int64{29})
- testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)},
+ testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)},
[]int64{37})
- testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},
+ testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},
[]int64{24})
}
@@ -159,9 +204,9 @@ func TestHashPasswordDeterministic(t *testing.T) {
b := make([]byte, 16)
u := &user_model.User{}
algos := hash.RecommendedHashAlgorithms
- for j := 0; j < len(algos); j++ {
+ for j := range algos {
u.PasswdHashAlgo = algos[j]
- for i := 0; i < 50; i++ {
+ for range 50 {
// generate a random password
rand.Read(b)
pass := string(b)
@@ -186,7 +231,7 @@ func BenchmarkHashPassword(b *testing.B) {
pass := "password1337"
u := &user_model.User{Passwd: pass}
b.ResetTimer()
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
u.SetPassword(pass)
}
}
@@ -264,7 +309,7 @@ func TestCreateUserCustomTimestamps(t *testing.T) {
err := user_model.CreateUser(db.DefaultContext, user, &user_model.Meta{})
assert.NoError(t, err)
- fetched, err := user_model.GetUserByID(context.Background(), user.ID)
+ fetched, err := user_model.GetUserByID(t.Context(), user.ID)
assert.NoError(t, err)
assert.Equal(t, creationTimestamp, fetched.CreatedUnix)
assert.Equal(t, creationTimestamp, fetched.UpdatedUnix)
@@ -291,7 +336,7 @@ func TestCreateUserWithoutCustomTimestamps(t *testing.T) {
timestampEnd := time.Now().Unix()
- fetched, err := user_model.GetUserByID(context.Background(), user.ID)
+ fetched, err := user_model.GetUserByID(t.Context(), user.ID)
assert.NoError(t, err)
assert.LessOrEqual(t, timestampStart, fetched.CreatedUnix)
@@ -318,14 +363,14 @@ func TestGetUserIDsByNames(t *testing.T) {
func TestGetMaileableUsersByIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- results, err := user_model.GetMaileableUsersByIDs(db.DefaultContext, []int64{1, 4}, false)
+ results, err := user_model.GetMailableUsersByIDs(db.DefaultContext, []int64{1, 4}, false)
assert.NoError(t, err)
assert.Len(t, results, 1)
if len(results) > 1 {
assert.Equal(t, 1, results[0].ID)
}
- results, err = user_model.GetMaileableUsersByIDs(db.DefaultContext, []int64{1, 4}, true)
+ results, err = user_model.GetMailableUsersByIDs(db.DefaultContext, []int64{1, 4}, true)
assert.NoError(t, err)
assert.Len(t, results, 2)
if len(results) > 2 {
@@ -488,18 +533,15 @@ func TestIsUserVisibleToViewer(t *testing.T) {
}
func Test_ValidateUser(t *testing.T) {
- oldSetting := setting.Service.AllowedUserVisibilityModesSlice
- defer func() {
- setting.Service.AllowedUserVisibilityModesSlice = oldSetting
- }()
- setting.Service.AllowedUserVisibilityModesSlice = []bool{true, false, true}
+ defer test.MockVariableValue(&setting.Service.AllowedUserVisibilityModesSlice, []bool{true, false, true})()
+
kases := map[*user_model.User]bool{
{ID: 1, Visibility: structs.VisibleTypePublic}: true,
{ID: 2, Visibility: structs.VisibleTypeLimited}: false,
{ID: 2, Visibility: structs.VisibleTypePrivate}: true,
}
for kase, expected := range kases {
- assert.EqualValues(t, expected, nil == user_model.ValidateUser(kase), "case: %+v", kase)
+ assert.Equal(t, expected, nil == user_model.ValidateUser(kase), "case: %+v", kase)
}
}
@@ -523,7 +565,7 @@ func Test_NormalizeUserFromEmail(t *testing.T) {
for _, testCase := range testCases {
normalizedName, err := user_model.NormalizeUserName(testCase.Input)
assert.NoError(t, err)
- assert.EqualValues(t, testCase.Expected, normalizedName)
+ assert.Equal(t, testCase.Expected, normalizedName)
if testCase.IsNormalizedValid {
assert.NoError(t, user_model.IsUsableUsername(normalizedName))
} else {
@@ -550,7 +592,7 @@ func TestEmailTo(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.result, func(t *testing.T) {
testUser := &user_model.User{FullName: testCase.fullName, Email: testCase.mail}
- assert.EqualValues(t, testCase.result, testUser.EmailTo())
+ assert.Equal(t, testCase.result, testUser.EmailTo())
})
}
}
@@ -561,12 +603,7 @@ func TestDisabledUserFeatures(t *testing.T) {
testValues := container.SetOf(setting.UserFeatureDeletion,
setting.UserFeatureManageSSHKeys,
setting.UserFeatureManageGPGKeys)
-
- oldSetting := setting.Admin.ExternalUserDisableFeatures
- defer func() {
- setting.Admin.ExternalUserDisableFeatures = oldSetting
- }()
- setting.Admin.ExternalUserDisableFeatures = testValues
+ defer test.MockVariableValue(&setting.Admin.ExternalUserDisableFeatures, testValues)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
@@ -602,3 +639,37 @@ func TestGetInactiveUsers(t *testing.T) {
assert.NoError(t, err)
assert.Empty(t, users)
}
+
+func TestCanCreateRepo(t *testing.T) {
+ defer test.MockVariableValue(&setting.Repository.MaxCreationLimit)()
+ const noLimit = -1
+ doerNormal := &user_model.User{}
+ doerAdmin := &user_model.User{IsAdmin: true}
+ t.Run("NoGlobalLimit", func(t *testing.T) {
+ setting.Repository.MaxCreationLimit = noLimit
+
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
+ assert.False(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
+
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
+ })
+
+ t.Run("GlobalLimit50", func(t *testing.T) {
+ setting.Repository.MaxCreationLimit = 50
+
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
+ assert.False(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: noLimit})) // limited by global limit
+ assert.False(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: 100}))
+
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: noLimit}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: 100}))
+ })
+}
diff --git a/models/webhook/hooktask.go b/models/webhook/hooktask.go
index ff3fdbadb2..96ec11e43f 100644
--- a/models/webhook/hooktask.go
+++ b/models/webhook/hooktask.go
@@ -198,7 +198,8 @@ func MarkTaskDelivered(ctx context.Context, task *HookTask) (bool, error) {
func CleanupHookTaskTable(ctx context.Context, cleanupType HookTaskCleanupType, olderThan time.Duration, numberToKeep int) error {
log.Trace("Doing: CleanupHookTaskTable")
- if cleanupType == OlderThan {
+ switch cleanupType {
+ case OlderThan:
deleteOlderThan := time.Now().Add(-olderThan).UnixNano()
deletes, err := db.GetEngine(ctx).
Where("is_delivered = ? and delivered < ?", true, deleteOlderThan).
@@ -207,7 +208,7 @@ func CleanupHookTaskTable(ctx context.Context, cleanupType HookTaskCleanupType,
return err
}
log.Trace("Deleted %d rows from hook_task", deletes)
- } else if cleanupType == PerWebhook {
+ case PerWebhook:
hookIDs := make([]int64, 0, 10)
err := db.GetEngine(ctx).
Table("webhook").
diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go
index 894357e36a..b234d9ffee 100644
--- a/models/webhook/webhook.go
+++ b/models/webhook/webhook.go
@@ -167,186 +167,39 @@ func (w *Webhook) UpdateEvent() error {
return err
}
-// HasCreateEvent returns true if hook enabled create event.
-func (w *Webhook) HasCreateEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.Create)
-}
-
-// HasDeleteEvent returns true if hook enabled delete event.
-func (w *Webhook) HasDeleteEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.Delete)
-}
-
-// HasForkEvent returns true if hook enabled fork event.
-func (w *Webhook) HasForkEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.Fork)
-}
-
-// HasIssuesEvent returns true if hook enabled issues event.
-func (w *Webhook) HasIssuesEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.Issues)
-}
-
-// HasIssuesAssignEvent returns true if hook enabled issues assign event.
-func (w *Webhook) HasIssuesAssignEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.IssueAssign)
-}
-
-// HasIssuesLabelEvent returns true if hook enabled issues label event.
-func (w *Webhook) HasIssuesLabelEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.IssueLabel)
-}
-
-// HasIssuesMilestoneEvent returns true if hook enabled issues milestone event.
-func (w *Webhook) HasIssuesMilestoneEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.IssueMilestone)
-}
-
-// HasIssueCommentEvent returns true if hook enabled issue_comment event.
-func (w *Webhook) HasIssueCommentEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.IssueComment)
-}
-
-// HasPushEvent returns true if hook enabled push event.
-func (w *Webhook) HasPushEvent() bool {
- return w.PushOnly || w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.Push)
-}
-
-// HasPullRequestEvent returns true if hook enabled pull request event.
-func (w *Webhook) HasPullRequestEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.PullRequest)
-}
-
-// HasPullRequestAssignEvent returns true if hook enabled pull request assign event.
-func (w *Webhook) HasPullRequestAssignEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.PullRequestAssign)
-}
-
-// HasPullRequestLabelEvent returns true if hook enabled pull request label event.
-func (w *Webhook) HasPullRequestLabelEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.PullRequestLabel)
-}
-
-// HasPullRequestMilestoneEvent returns true if hook enabled pull request milestone event.
-func (w *Webhook) HasPullRequestMilestoneEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.PullRequestMilestone)
-}
-
-// HasPullRequestCommentEvent returns true if hook enabled pull_request_comment event.
-func (w *Webhook) HasPullRequestCommentEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.PullRequestComment)
-}
-
-// HasPullRequestApprovedEvent returns true if hook enabled pull request review event.
-func (w *Webhook) HasPullRequestApprovedEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.PullRequestReview)
-}
-
-// HasPullRequestRejectedEvent returns true if hook enabled pull request review event.
-func (w *Webhook) HasPullRequestRejectedEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.PullRequestReview)
-}
-
-// HasPullRequestReviewCommentEvent returns true if hook enabled pull request review event.
-func (w *Webhook) HasPullRequestReviewCommentEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.PullRequestReview)
-}
-
-// HasPullRequestSyncEvent returns true if hook enabled pull request sync event.
-func (w *Webhook) HasPullRequestSyncEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.PullRequestSync)
-}
-
-// HasWikiEvent returns true if hook enabled wiki event.
-func (w *Webhook) HasWikiEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvent.Wiki)
-}
-
-// HasReleaseEvent returns if hook enabled release event.
-func (w *Webhook) HasReleaseEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.Release)
-}
-
-// HasRepositoryEvent returns if hook enabled repository event.
-func (w *Webhook) HasRepositoryEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.Repository)
-}
-
-// HasPackageEvent returns if hook enabled package event.
-func (w *Webhook) HasPackageEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.Package)
-}
-
-// HasPullRequestReviewRequestEvent returns true if hook enabled pull request review request event.
-func (w *Webhook) HasPullRequestReviewRequestEvent() bool {
- return w.SendEverything ||
- (w.ChooseEvents && w.HookEvents.PullRequestReviewRequest)
-}
-
-// EventCheckers returns event checkers
-func (w *Webhook) EventCheckers() []struct {
- Has func() bool
- Type webhook_module.HookEventType
-} {
- return []struct {
- Has func() bool
- Type webhook_module.HookEventType
- }{
- {w.HasCreateEvent, webhook_module.HookEventCreate},
- {w.HasDeleteEvent, webhook_module.HookEventDelete},
- {w.HasForkEvent, webhook_module.HookEventFork},
- {w.HasPushEvent, webhook_module.HookEventPush},
- {w.HasIssuesEvent, webhook_module.HookEventIssues},
- {w.HasIssuesAssignEvent, webhook_module.HookEventIssueAssign},
- {w.HasIssuesLabelEvent, webhook_module.HookEventIssueLabel},
- {w.HasIssuesMilestoneEvent, webhook_module.HookEventIssueMilestone},
- {w.HasIssueCommentEvent, webhook_module.HookEventIssueComment},
- {w.HasPullRequestEvent, webhook_module.HookEventPullRequest},
- {w.HasPullRequestAssignEvent, webhook_module.HookEventPullRequestAssign},
- {w.HasPullRequestLabelEvent, webhook_module.HookEventPullRequestLabel},
- {w.HasPullRequestMilestoneEvent, webhook_module.HookEventPullRequestMilestone},
- {w.HasPullRequestCommentEvent, webhook_module.HookEventPullRequestComment},
- {w.HasPullRequestApprovedEvent, webhook_module.HookEventPullRequestReviewApproved},
- {w.HasPullRequestRejectedEvent, webhook_module.HookEventPullRequestReviewRejected},
- {w.HasPullRequestCommentEvent, webhook_module.HookEventPullRequestReviewComment},
- {w.HasPullRequestSyncEvent, webhook_module.HookEventPullRequestSync},
- {w.HasWikiEvent, webhook_module.HookEventWiki},
- {w.HasRepositoryEvent, webhook_module.HookEventRepository},
- {w.HasReleaseEvent, webhook_module.HookEventRelease},
- {w.HasPackageEvent, webhook_module.HookEventPackage},
- {w.HasPullRequestReviewRequestEvent, webhook_module.HookEventPullRequestReviewRequest},
+func (w *Webhook) HasEvent(evt webhook_module.HookEventType) bool {
+ if w.SendEverything {
+ return true
}
+ if w.PushOnly {
+ return evt == webhook_module.HookEventPush
+ }
+ checkEvt := evt
+ switch evt {
+ case webhook_module.HookEventPullRequestReviewApproved, webhook_module.HookEventPullRequestReviewRejected, webhook_module.HookEventPullRequestReviewComment:
+ checkEvt = webhook_module.HookEventPullRequestReview
+ }
+ return w.HookEvents[checkEvt]
}
// EventsArray returns an array of hook events
func (w *Webhook) EventsArray() []string {
- events := make([]string, 0, 7)
+ if w.SendEverything {
+ events := make([]string, 0, len(webhook_module.AllEvents()))
+ for _, evt := range webhook_module.AllEvents() {
+ events = append(events, string(evt))
+ }
+ return events
+ }
+
+ if w.PushOnly {
+ return []string{string(webhook_module.HookEventPush)}
+ }
- for _, c := range w.EventCheckers() {
- if c.Has() {
- events = append(events, string(c.Type))
+ events := make([]string, 0, len(w.HookEvents))
+ for event, enabled := range w.HookEvents {
+ if enabled {
+ events = append(events, string(event))
}
}
return events
@@ -387,7 +240,7 @@ func CreateWebhooks(ctx context.Context, ws []*Webhook) error {
if len(ws) == 0 {
return nil
}
- for i := 0; i < len(ws); i++ {
+ for i := range ws {
ws[i].Type = strings.TrimSpace(ws[i].Type)
}
return db.Insert(ctx, ws)
diff --git a/models/webhook/webhook_system.go b/models/webhook/webhook_system.go
index a2a9ee321a..58d9d4a5c1 100644
--- a/models/webhook/webhook_system.go
+++ b/models/webhook/webhook_system.go
@@ -11,6 +11,19 @@ import (
"code.gitea.io/gitea/modules/optional"
)
+// GetSystemOrDefaultWebhooks returns webhooks by given argument or all if argument is missing.
+func GetSystemOrDefaultWebhooks(ctx context.Context, isSystemWebhook optional.Option[bool]) ([]*Webhook, error) {
+ webhooks := make([]*Webhook, 0, 5)
+ if !isSystemWebhook.Has() {
+ return webhooks, db.GetEngine(ctx).Where("repo_id=? AND owner_id=?", 0, 0).
+ Find(&webhooks)
+ }
+
+ return webhooks, db.GetEngine(ctx).
+ Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, isSystemWebhook.Value()).
+ Find(&webhooks)
+}
+
// GetDefaultWebhooks returns all admin-default webhooks.
func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) {
webhooks := make([]*Webhook, 0, 5)
diff --git a/models/webhook/webhook_system_test.go b/models/webhook/webhook_system_test.go
new file mode 100644
index 0000000000..96157ed9c9
--- /dev/null
+++ b/models/webhook/webhook_system_test.go
@@ -0,0 +1,37 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package webhook
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/optional"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGetSystemOrDefaultWebhooks(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ hooks, err := GetSystemOrDefaultWebhooks(db.DefaultContext, optional.None[bool]())
+ assert.NoError(t, err)
+ if assert.Len(t, hooks, 2) {
+ assert.Equal(t, int64(5), hooks[0].ID)
+ assert.Equal(t, int64(6), hooks[1].ID)
+ }
+
+ hooks, err = GetSystemOrDefaultWebhooks(db.DefaultContext, optional.Some(true))
+ assert.NoError(t, err)
+ if assert.Len(t, hooks, 1) {
+ assert.Equal(t, int64(5), hooks[0].ID)
+ }
+
+ hooks, err = GetSystemOrDefaultWebhooks(db.DefaultContext, optional.Some(false))
+ assert.NoError(t, err)
+ if assert.Len(t, hooks, 1) {
+ assert.Equal(t, int64(6), hooks[0].ID)
+ }
+}
diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go
index c6c3f40d46..edad8fc996 100644
--- a/models/webhook/webhook_test.go
+++ b/models/webhook/webhook_test.go
@@ -4,7 +4,6 @@
package webhook
import (
- "context"
"testing"
"time"
@@ -54,9 +53,9 @@ func TestWebhook_UpdateEvent(t *testing.T) {
SendEverything: false,
ChooseEvents: false,
HookEvents: webhook_module.HookEvents{
- Create: false,
- Push: true,
- PullRequest: false,
+ webhook_module.HookEventCreate: false,
+ webhook_module.HookEventPush: true,
+ webhook_module.HookEventPullRequest: false,
},
}
webhook.HookEvent = hookEvent
@@ -73,8 +72,8 @@ func TestWebhook_EventsArray(t *testing.T) {
"issues", "issue_assign", "issue_label", "issue_milestone", "issue_comment",
"pull_request", "pull_request_assign", "pull_request_label", "pull_request_milestone",
"pull_request_comment", "pull_request_review_approved", "pull_request_review_rejected",
- "pull_request_review_comment", "pull_request_sync", "wiki", "repository", "release",
- "package", "pull_request_review_request",
+ "pull_request_review_comment", "pull_request_sync", "pull_request_review_request", "wiki", "repository", "release",
+ "package", "status", "workflow_run", "workflow_job",
},
(&Webhook{
HookEvent: &webhook_module.HookEvent{SendEverything: true},
@@ -91,7 +90,7 @@ func TestWebhook_EventsArray(t *testing.T) {
func TestCreateWebhook(t *testing.T) {
hook := &Webhook{
RepoID: 3,
- URL: "www.example.com/unit_test",
+ URL: "https://www.example.com/unit_test",
ContentType: ContentTypeJSON,
Events: `{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}`,
}
@@ -245,7 +244,7 @@ func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) {
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask)
- assert.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 0))
+ assert.NoError(t, CleanupHookTaskTable(t.Context(), PerWebhook, 168*time.Hour, 0))
unittest.AssertNotExistsBean(t, hookTask)
}
@@ -261,7 +260,7 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) {
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask)
- assert.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 0))
+ assert.NoError(t, CleanupHookTaskTable(t.Context(), PerWebhook, 168*time.Hour, 0))
unittest.AssertExistsAndLoadBean(t, hookTask)
}
@@ -278,7 +277,7 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) {
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask)
- assert.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 1))
+ assert.NoError(t, CleanupHookTaskTable(t.Context(), PerWebhook, 168*time.Hour, 1))
unittest.AssertExistsAndLoadBean(t, hookTask)
}
@@ -295,7 +294,7 @@ func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) {
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask)
- assert.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0))
+ assert.NoError(t, CleanupHookTaskTable(t.Context(), OlderThan, 168*time.Hour, 0))
unittest.AssertNotExistsBean(t, hookTask)
}
@@ -311,7 +310,7 @@ func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) {
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask)
- assert.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0))
+ assert.NoError(t, CleanupHookTaskTable(t.Context(), OlderThan, 168*time.Hour, 0))
unittest.AssertExistsAndLoadBean(t, hookTask)
}
@@ -328,6 +327,6 @@ func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *test
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask)
- assert.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0))
+ assert.NoError(t, CleanupHookTaskTable(t.Context(), OlderThan, 168*time.Hour, 0))
unittest.AssertExistsAndLoadBean(t, hookTask)
}
diff --git a/modules/actions/artifacts.go b/modules/actions/artifacts.go
new file mode 100644
index 0000000000..4d074435ef
--- /dev/null
+++ b/modules/actions/artifacts.go
@@ -0,0 +1,48 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package actions
+
+import (
+ "net/http"
+
+ actions_model "code.gitea.io/gitea/models/actions"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/storage"
+ "code.gitea.io/gitea/services/context"
+)
+
+// Artifacts using the v4 backend are stored as a single combined zip file per artifact on the backend
+// The v4 backend ensures ContentEncoding is set to "application/zip", which is not the case for the old backend
+func IsArtifactV4(art *actions_model.ActionArtifact) bool {
+ return art.ArtifactName+".zip" == art.ArtifactPath && art.ContentEncoding == "application/zip"
+}
+
+func DownloadArtifactV4ServeDirectOnly(ctx *context.Base, art *actions_model.ActionArtifact) (bool, error) {
+ if setting.Actions.ArtifactStorage.ServeDirect() {
+ u, err := storage.ActionsArtifacts.URL(art.StoragePath, art.ArtifactPath, nil)
+ if u != nil && err == nil {
+ ctx.Redirect(u.String(), http.StatusFound)
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+func DownloadArtifactV4Fallback(ctx *context.Base, art *actions_model.ActionArtifact) error {
+ f, err := storage.ActionsArtifacts.Open(art.StoragePath)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ http.ServeContent(ctx.Resp, ctx.Req, art.ArtifactName+".zip", art.CreatedUnix.AsLocalTime(), f)
+ return nil
+}
+
+func DownloadArtifactV4(ctx *context.Base, art *actions_model.ActionArtifact) error {
+ ok, err := DownloadArtifactV4ServeDirectOnly(ctx, art)
+ if ok || err != nil {
+ return err
+ }
+ return DownloadArtifactV4Fallback(ctx, art)
+}
diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go
index 0d2b0dd919..27bcafa649 100644
--- a/modules/actions/workflows.go
+++ b/modules/actions/workflows.go
@@ -6,6 +6,7 @@ package actions
import (
"bytes"
"io"
+ "slices"
"strings"
"code.gitea.io/gitea/modules/git"
@@ -43,21 +44,23 @@ func IsWorkflow(path string) bool {
return strings.HasPrefix(path, ".gitea/workflows") || strings.HasPrefix(path, ".github/workflows")
}
-func ListWorkflows(commit *git.Commit) (git.Entries, error) {
- tree, err := commit.SubTree(".gitea/workflows")
+func ListWorkflows(commit *git.Commit) (string, git.Entries, error) {
+ rpath := ".gitea/workflows"
+ tree, err := commit.SubTree(rpath)
if _, ok := err.(git.ErrNotExist); ok {
- tree, err = commit.SubTree(".github/workflows")
+ rpath = ".github/workflows"
+ tree, err = commit.SubTree(rpath)
}
if _, ok := err.(git.ErrNotExist); ok {
- return nil, nil
+ return "", nil, nil
}
if err != nil {
- return nil, err
+ return "", nil, err
}
entries, err := tree.ListEntriesRecursiveFast()
if err != nil {
- return nil, err
+ return "", nil, err
}
ret := make(git.Entries, 0, len(entries))
@@ -66,7 +69,7 @@ func ListWorkflows(commit *git.Commit) (git.Entries, error) {
ret = append(ret, entry)
}
}
- return ret, nil
+ return rpath, ret, nil
}
func GetContentFromEntry(entry *git.TreeEntry) ([]byte, error) {
@@ -102,7 +105,7 @@ func DetectWorkflows(
payload api.Payloader,
detectSchedule bool,
) ([]*DetectedWorkflow, []*DetectedWorkflow, error) {
- entries, err := ListWorkflows(commit)
+ _, entries, err := ListWorkflows(commit)
if err != nil {
return nil, nil, err
}
@@ -147,7 +150,7 @@ func DetectWorkflows(
}
func DetectScheduledWorkflows(gitRepo *git.Repository, commit *git.Commit) ([]*DetectedWorkflow, error) {
- entries, err := ListWorkflows(commit)
+ _, entries, err := ListWorkflows(commit)
if err != nil {
return nil, err
}
@@ -243,6 +246,10 @@ func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent web
webhook_module.HookEventPackage:
return matchPackageEvent(payload.(*api.PackagePayload), evt)
+ case // workflow_run
+ webhook_module.HookEventWorkflowRun:
+ return matchWorkflowRunEvent(payload.(*api.WorkflowRunPayload), evt)
+
default:
log.Warn("unsupported event %q", triggedEvent)
return false
@@ -311,6 +318,10 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa
matchTimes++
}
case "paths":
+ if refName.IsTag() {
+ matchTimes++
+ break
+ }
filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before)
if err != nil {
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err)
@@ -324,6 +335,10 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa
}
}
case "paths-ignore":
+ if refName.IsTag() {
+ matchTimes++
+ break
+ }
filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before)
if err != nil {
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err)
@@ -463,7 +478,7 @@ func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayloa
matchTimes++
}
case "paths":
- filesChanged, err := headCommit.GetFilesChangedSinceCommit(prPayload.PullRequest.Base.Ref)
+ filesChanged, err := headCommit.GetFilesChangedSinceCommit(prPayload.PullRequest.MergeBase)
if err != nil {
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", headCommit.ID.String(), err)
} else {
@@ -476,7 +491,7 @@ func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayloa
}
}
case "paths-ignore":
- filesChanged, err := headCommit.GetFilesChangedSinceCommit(prPayload.PullRequest.Base.Ref)
+ filesChanged, err := headCommit.GetFilesChangedSinceCommit(prPayload.PullRequest.MergeBase)
if err != nil {
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", headCommit.ID.String(), err)
} else {
@@ -554,21 +569,12 @@ func matchPullRequestReviewEvent(prPayload *api.PullRequestPayload, evt *jobpars
actions = append(actions, "submitted", "edited")
}
- matched := false
for _, val := range vals {
- for _, action := range actions {
- if glob.MustCompile(val, '/').Match(action) {
- matched = true
- break
- }
- }
- if matched {
+ if slices.ContainsFunc(actions, glob.MustCompile(val, '/').Match) {
+ matchTimes++
break
}
}
- if matched {
- matchTimes++
- }
default:
log.Warn("pull request review event unsupported condition %q", cond)
}
@@ -603,21 +609,12 @@ func matchPullRequestReviewCommentEvent(prPayload *api.PullRequestPayload, evt *
actions = append(actions, "created", "edited")
}
- matched := false
for _, val := range vals {
- for _, action := range actions {
- if glob.MustCompile(val, '/').Match(action) {
- matched = true
- break
- }
- }
- if matched {
+ if slices.ContainsFunc(actions, glob.MustCompile(val, '/').Match) {
+ matchTimes++
break
}
}
- if matched {
- matchTimes++
- }
default:
log.Warn("pull request review comment event unsupported condition %q", cond)
}
@@ -698,3 +695,53 @@ func matchPackageEvent(payload *api.PackagePayload, evt *jobparser.Event) bool {
}
return matchTimes == len(evt.Acts())
}
+
+func matchWorkflowRunEvent(payload *api.WorkflowRunPayload, evt *jobparser.Event) bool {
+ // with no special filter parameters
+ if len(evt.Acts()) == 0 {
+ return true
+ }
+
+ matchTimes := 0
+ // all acts conditions should be satisfied
+ for cond, vals := range evt.Acts() {
+ switch cond {
+ case "types":
+ action := payload.Action
+ for _, val := range vals {
+ if glob.MustCompile(val, '/').Match(action) {
+ matchTimes++
+ break
+ }
+ }
+ case "workflows":
+ workflow := payload.Workflow
+ patterns, err := workflowpattern.CompilePatterns(vals...)
+ if err != nil {
+ break
+ }
+ if !workflowpattern.Skip(patterns, []string{workflow.Name}, &workflowpattern.EmptyTraceWriter{}) {
+ matchTimes++
+ }
+ case "branches":
+ patterns, err := workflowpattern.CompilePatterns(vals...)
+ if err != nil {
+ break
+ }
+ if !workflowpattern.Skip(patterns, []string{payload.WorkflowRun.HeadBranch}, &workflowpattern.EmptyTraceWriter{}) {
+ matchTimes++
+ }
+ case "branches-ignore":
+ patterns, err := workflowpattern.CompilePatterns(vals...)
+ if err != nil {
+ break
+ }
+ if !workflowpattern.Filter(patterns, []string{payload.WorkflowRun.HeadBranch}, &workflowpattern.EmptyTraceWriter{}) {
+ matchTimes++
+ }
+ default:
+ log.Warn("workflow run event unsupported condition %q", cond)
+ }
+ }
+ return matchTimes == len(evt.Acts())
+}
diff --git a/modules/actions/workflows_test.go b/modules/actions/workflows_test.go
index c8e1e553fe..e23431651d 100644
--- a/modules/actions/workflows_test.go
+++ b/modules/actions/workflows_test.go
@@ -125,6 +125,24 @@ func TestDetectMatched(t *testing.T) {
yamlOn: "on: schedule",
expected: true,
},
+ {
+ desc: "push to tag matches workflow with paths condition (should skip paths check)",
+ triggedEvent: webhook_module.HookEventPush,
+ payload: &api.PushPayload{
+ Ref: "refs/tags/v1.0.0",
+ Before: "0000000",
+ Commits: []*api.PayloadCommit{
+ {
+ ID: "abcdef123456",
+ Added: []string{"src/main.go"},
+ Message: "Release v1.0.0",
+ },
+ },
+ },
+ commit: nil,
+ yamlOn: "on:\n push:\n paths:\n - src/**",
+ expected: true,
+ },
}
for _, tc := range testCases {
diff --git a/modules/analyze/vendor_test.go b/modules/analyze/vendor_test.go
index aafd3c431b..02a51d4c8f 100644
--- a/modules/analyze/vendor_test.go
+++ b/modules/analyze/vendor_test.go
@@ -3,7 +3,11 @@
package analyze
-import "testing"
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
func TestIsVendor(t *testing.T) {
tests := []struct {
@@ -33,9 +37,8 @@ func TestIsVendor(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.path, func(t *testing.T) {
- if got := IsVendor(tt.path); got != tt.want {
- t.Errorf("IsVendor() = %v, want %v", got, tt.want)
- }
+ got := IsVendor(tt.path)
+ assert.Equal(t, tt.want, got)
})
}
}
diff --git a/modules/assetfs/embed.go b/modules/assetfs/embed.go
new file mode 100644
index 0000000000..95176372d1
--- /dev/null
+++ b/modules/assetfs/embed.go
@@ -0,0 +1,375 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package assetfs
+
+import (
+ "bytes"
+ "compress/gzip"
+ "io"
+ "io/fs"
+ "os"
+ "path"
+ "path/filepath"
+ "strings"
+ "sync"
+ "time"
+
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/util"
+)
+
+type EmbeddedFile interface {
+ io.ReadSeeker
+ fs.ReadDirFile
+ ReadDir(n int) ([]fs.DirEntry, error)
+}
+
+type EmbeddedFileInfo interface {
+ fs.FileInfo
+ fs.DirEntry
+ GetGzipContent() ([]byte, bool)
+}
+
+type decompressor interface {
+ io.Reader
+ Close() error
+ Reset(io.Reader) error
+}
+
+type embeddedFileInfo struct {
+ fs *embeddedFS
+ fullName string
+ data []byte
+
+ BaseName string `json:"n"`
+ OriginSize int64 `json:"s,omitempty"`
+ DataBegin int64 `json:"b,omitempty"`
+ DataLen int64 `json:"l,omitempty"`
+ Children []*embeddedFileInfo `json:"c,omitempty"`
+}
+
+func (fi *embeddedFileInfo) GetGzipContent() ([]byte, bool) {
+ // when generating the bindata, if the compressed data equals or is larger than the original data, we store the original data
+ if fi.DataLen == fi.OriginSize {
+ return nil, false
+ }
+ return fi.data, true
+}
+
+type EmbeddedFileBase struct {
+ info *embeddedFileInfo
+ dataReader io.ReadSeeker
+ seekPos int64
+}
+
+func (f *EmbeddedFileBase) ReadDir(n int) ([]fs.DirEntry, error) {
+ // this method is used to satisfy the "func (f ioFile) ReadDir(...)" in httpfs
+ l, err := f.info.fs.ReadDir(f.info.fullName)
+ if err != nil {
+ return nil, err
+ }
+ if n < 0 || n > len(l) {
+ return l, nil
+ }
+ return l[:n], nil
+}
+
+type EmbeddedOriginFile struct {
+ EmbeddedFileBase
+}
+
+type EmbeddedCompressedFile struct {
+ EmbeddedFileBase
+ decompressor decompressor
+ decompressorPos int64
+}
+
+type embeddedFS struct {
+ meta func() *EmbeddedMeta
+
+ files map[string]*embeddedFileInfo
+ filesMu sync.RWMutex
+
+ data []byte
+}
+
+type EmbeddedMeta struct {
+ Root *embeddedFileInfo
+}
+
+func NewEmbeddedFS(data []byte) fs.ReadDirFS {
+ efs := &embeddedFS{data: data, files: make(map[string]*embeddedFileInfo)}
+ efs.meta = sync.OnceValue(func() *EmbeddedMeta {
+ var meta EmbeddedMeta
+ p := bytes.LastIndexByte(data, '\n')
+ if p < 0 {
+ return &meta
+ }
+ if err := json.Unmarshal(data[p+1:], &meta); err != nil {
+ panic("embedded file is not valid")
+ }
+ return &meta
+ })
+ return efs
+}
+
+var _ fs.ReadDirFS = (*embeddedFS)(nil)
+
+func (e *embeddedFS) ReadDir(name string) (l []fs.DirEntry, err error) {
+ fi, err := e.getFileInfo(name)
+ if err != nil {
+ return nil, err
+ }
+ if !fi.IsDir() {
+ return nil, fs.ErrNotExist
+ }
+ l = make([]fs.DirEntry, len(fi.Children))
+ for i, child := range fi.Children {
+ l[i], err = e.getFileInfo(name + "/" + child.BaseName)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return l, nil
+}
+
+func (e *embeddedFS) getFileInfo(fullName string) (*embeddedFileInfo, error) {
+ // no need to do heavy "path.Clean()" because we don't want to support "foo/../bar" or absolute paths
+ fullName = strings.TrimPrefix(fullName, "./")
+ if fullName == "" {
+ fullName = "."
+ }
+
+ e.filesMu.RLock()
+ fi := e.files[fullName]
+ e.filesMu.RUnlock()
+ if fi != nil {
+ return fi, nil
+ }
+
+ fields := strings.Split(fullName, "/")
+ fi = e.meta().Root
+ if fullName != "." {
+ found := true
+ for _, field := range fields {
+ for _, child := range fi.Children {
+ if found = child.BaseName == field; found {
+ fi = child
+ break
+ }
+ }
+ if !found {
+ return nil, fs.ErrNotExist
+ }
+ }
+ }
+
+ e.filesMu.Lock()
+ defer e.filesMu.Unlock()
+ if fi != nil {
+ fi.fs = e
+ fi.fullName = fullName
+ fi.data = e.data[fi.DataBegin : fi.DataBegin+fi.DataLen]
+ e.files[fullName] = fi // do not cache nil, otherwise keeping accessing random non-existing file will cause OOM
+ return fi, nil
+ }
+ return nil, fs.ErrNotExist
+}
+
+func (e *embeddedFS) Open(name string) (fs.File, error) {
+ info, err := e.getFileInfo(name)
+ if err != nil {
+ return nil, err
+ }
+ base := EmbeddedFileBase{info: info}
+ base.dataReader = bytes.NewReader(base.info.data)
+ if info.DataLen != info.OriginSize {
+ decomp, err := gzip.NewReader(base.dataReader)
+ if err != nil {
+ return nil, err
+ }
+ return &EmbeddedCompressedFile{EmbeddedFileBase: base, decompressor: decomp}, nil
+ }
+ return &EmbeddedOriginFile{base}, nil
+}
+
+var (
+ _ EmbeddedFileInfo = (*embeddedFileInfo)(nil)
+ _ EmbeddedFile = (*EmbeddedOriginFile)(nil)
+ _ EmbeddedFile = (*EmbeddedCompressedFile)(nil)
+)
+
+func (f *EmbeddedOriginFile) Read(p []byte) (n int, err error) {
+ return f.dataReader.Read(p)
+}
+
+func (f *EmbeddedCompressedFile) Read(p []byte) (n int, err error) {
+ if f.decompressorPos > f.seekPos {
+ if err = f.decompressor.Reset(bytes.NewReader(f.info.data)); err != nil {
+ return 0, err
+ }
+ f.decompressorPos = 0
+ }
+ if f.decompressorPos < f.seekPos {
+ if _, err = io.CopyN(io.Discard, f.decompressor, f.seekPos-f.decompressorPos); err != nil {
+ return 0, err
+ }
+ f.decompressorPos = f.seekPos
+ }
+ n, err = f.decompressor.Read(p)
+ f.decompressorPos += int64(n)
+ f.seekPos = f.decompressorPos
+ return n, err
+}
+
+func (f *EmbeddedFileBase) Seek(offset int64, whence int) (int64, error) {
+ switch whence {
+ case io.SeekStart:
+ f.seekPos = offset
+ case io.SeekCurrent:
+ f.seekPos += offset
+ case io.SeekEnd:
+ f.seekPos = f.info.OriginSize + offset
+ }
+ return f.seekPos, nil
+}
+
+func (f *EmbeddedFileBase) Stat() (fs.FileInfo, error) {
+ return f.info, nil
+}
+
+func (f *EmbeddedOriginFile) Close() error {
+ return nil
+}
+
+func (f *EmbeddedCompressedFile) Close() error {
+ return f.decompressor.Close()
+}
+
+func (fi *embeddedFileInfo) Name() string {
+ return fi.BaseName
+}
+
+func (fi *embeddedFileInfo) Size() int64 {
+ return fi.OriginSize
+}
+
+func (fi *embeddedFileInfo) Mode() fs.FileMode {
+ return util.Iif(fi.IsDir(), fs.ModeDir|0o555, 0o444)
+}
+
+func (fi *embeddedFileInfo) ModTime() time.Time {
+ return getExecutableModTime()
+}
+
+func (fi *embeddedFileInfo) IsDir() bool {
+ return fi.Children != nil
+}
+
+func (fi *embeddedFileInfo) Sys() any {
+ return nil
+}
+
+func (fi *embeddedFileInfo) Type() fs.FileMode {
+ return util.Iif(fi.IsDir(), fs.ModeDir, 0)
+}
+
+func (fi *embeddedFileInfo) Info() (fs.FileInfo, error) {
+ return fi, nil
+}
+
+// getExecutableModTime returns the modification time of the executable file.
+// In bindata, we can't use the ModTime of the files because we need to make the build reproducible
+var getExecutableModTime = sync.OnceValue(func() (modTime time.Time) {
+ exePath, err := os.Executable()
+ if err != nil {
+ return modTime
+ }
+ exePath, err = filepath.Abs(exePath)
+ if err != nil {
+ return modTime
+ }
+ exePath, err = filepath.EvalSymlinks(exePath)
+ if err != nil {
+ return modTime
+ }
+ st, err := os.Stat(exePath)
+ if err != nil {
+ return modTime
+ }
+ return st.ModTime()
+})
+
+func GenerateEmbedBindata(fsRootPath, outputFile string) error {
+ output, err := os.OpenFile(outputFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)
+ if err != nil {
+ return err
+ }
+ defer output.Close()
+
+ meta := &EmbeddedMeta{}
+ meta.Root = &embeddedFileInfo{}
+ var outputOffset int64
+ var embedFiles func(parent *embeddedFileInfo, fsPath, embedPath string) error
+ embedFiles = func(parent *embeddedFileInfo, fsPath, embedPath string) error {
+ dirEntries, err := os.ReadDir(fsPath)
+ if err != nil {
+ return err
+ }
+ for _, dirEntry := range dirEntries {
+ if err != nil {
+ return err
+ }
+ if dirEntry.IsDir() {
+ child := &embeddedFileInfo{
+ BaseName: dirEntry.Name(),
+ Children: []*embeddedFileInfo{}, // non-nil means it's a directory
+ }
+ parent.Children = append(parent.Children, child)
+ if err = embedFiles(child, filepath.Join(fsPath, dirEntry.Name()), path.Join(embedPath, dirEntry.Name())); err != nil {
+ return err
+ }
+ } else {
+ data, err := os.ReadFile(filepath.Join(fsPath, dirEntry.Name()))
+ if err != nil {
+ return err
+ }
+ var compressed bytes.Buffer
+ gz, _ := gzip.NewWriterLevel(&compressed, gzip.BestCompression)
+ if _, err = gz.Write(data); err != nil {
+ return err
+ }
+ if err = gz.Close(); err != nil {
+ return err
+ }
+
+ // only use the compressed data if it is smaller than the original data
+ outputBytes := util.Iif(len(compressed.Bytes()) < len(data), compressed.Bytes(), data)
+ child := &embeddedFileInfo{
+ BaseName: dirEntry.Name(),
+ OriginSize: int64(len(data)),
+ DataBegin: outputOffset,
+ DataLen: int64(len(outputBytes)),
+ }
+ if _, err = output.Write(outputBytes); err != nil {
+ return err
+ }
+ outputOffset += child.DataLen
+ parent.Children = append(parent.Children, child)
+ }
+ }
+ return nil
+ }
+
+ if err = embedFiles(meta.Root, fsRootPath, ""); err != nil {
+ return err
+ }
+ jsonBuf, err := json.Marshal(meta) // can't use json.NewEncoder here because it writes extra EOL
+ if err != nil {
+ return err
+ }
+ _, _ = output.Write([]byte{'\n'})
+ _, err = output.Write(jsonBuf)
+ return err
+}
diff --git a/modules/assetfs/embed_test.go b/modules/assetfs/embed_test.go
new file mode 100644
index 0000000000..06598da4c4
--- /dev/null
+++ b/modules/assetfs/embed_test.go
@@ -0,0 +1,98 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package assetfs
+
+import (
+ "bytes"
+ "io/fs"
+ "net/http"
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestEmbed(t *testing.T) {
+ tmpDir := t.TempDir()
+ tmpDataDir := tmpDir + "/data"
+ _ = os.MkdirAll(tmpDataDir+"/foo/bar", 0o755)
+ _ = os.WriteFile(tmpDataDir+"/a.txt", []byte("a"), 0o644)
+ _ = os.WriteFile(tmpDataDir+"/foo/bar/b.txt", bytes.Repeat([]byte("a"), 1000), 0o644)
+ _ = os.WriteFile(tmpDataDir+"/foo/c.txt", []byte("c"), 0o644)
+ require.NoError(t, GenerateEmbedBindata(tmpDataDir, tmpDir+"/out.dat"))
+
+ data, err := os.ReadFile(tmpDir + "/out.dat")
+ require.NoError(t, err)
+ efs := NewEmbeddedFS(data)
+
+ // test a non-existing file
+ _, err = fs.ReadFile(efs, "not exist")
+ assert.ErrorIs(t, err, fs.ErrNotExist)
+
+ // test a normal file (no compression)
+ content, err := fs.ReadFile(efs, "a.txt")
+ require.NoError(t, err)
+ assert.Equal(t, "a", string(content))
+ fi, err := fs.Stat(efs, "a.txt")
+ require.NoError(t, err)
+ _, ok := fi.(EmbeddedFileInfo).GetGzipContent()
+ assert.False(t, ok)
+
+ // test a compressed file
+ content, err = fs.ReadFile(efs, "foo/bar/b.txt")
+ require.NoError(t, err)
+ assert.Equal(t, bytes.Repeat([]byte("a"), 1000), content)
+ fi, err = fs.Stat(efs, "foo/bar/b.txt")
+ require.NoError(t, err)
+ assert.False(t, fi.Mode().IsDir())
+ assert.True(t, fi.Mode().IsRegular())
+ gzipContent, ok := fi.(EmbeddedFileInfo).GetGzipContent()
+ assert.True(t, ok)
+ assert.Greater(t, len(gzipContent), 1)
+ assert.Less(t, len(gzipContent), 1000)
+
+ // test list root directory
+ entries, err := fs.ReadDir(efs, ".")
+ require.NoError(t, err)
+ assert.Len(t, entries, 2)
+ assert.Equal(t, "a.txt", entries[0].Name())
+ assert.False(t, entries[0].IsDir())
+
+ // test list subdirectory
+ entries, err = fs.ReadDir(efs, "foo")
+ require.NoError(t, err)
+ require.Len(t, entries, 2)
+ assert.Equal(t, "bar", entries[0].Name())
+ assert.True(t, entries[0].IsDir())
+ assert.Equal(t, "c.txt", entries[1].Name())
+ assert.False(t, entries[1].IsDir())
+
+ // test directory mode
+ fi, err = fs.Stat(efs, "foo")
+ require.NoError(t, err)
+ assert.True(t, fi.IsDir())
+ assert.True(t, fi.Mode().IsDir())
+ assert.False(t, fi.Mode().IsRegular())
+
+ // test httpfs
+ hfs := http.FS(efs)
+ hf, err := hfs.Open("foo/bar/b.txt")
+ require.NoError(t, err)
+ hi, err := hf.Stat()
+ require.NoError(t, err)
+ fiEmbedded, ok := hi.(EmbeddedFileInfo)
+ require.True(t, ok)
+ gzipContent, ok = fiEmbedded.GetGzipContent()
+ assert.True(t, ok)
+ assert.Greater(t, len(gzipContent), 1)
+ assert.Less(t, len(gzipContent), 1000)
+
+ // test httpfs directory listing
+ hf, err = hfs.Open("foo")
+ require.NoError(t, err)
+ dirs, err := hf.Readdir(1)
+ require.NoError(t, err)
+ assert.Len(t, dirs, 1)
+}
diff --git a/modules/assetfs/layered.go b/modules/assetfs/layered.go
index 4f3811ba2b..ce55475bd9 100644
--- a/modules/assetfs/layered.go
+++ b/modules/assetfs/layered.go
@@ -52,8 +52,8 @@ func Local(name, base string, sub ...string) *Layer {
}
// Bindata returns a new Layer with the given name, it serves files from the given bindata asset.
-func Bindata(name string, fs http.FileSystem) *Layer {
- return &Layer{name: name, fs: fs}
+func Bindata(name string, fs fs.FS) *Layer {
+ return &Layer{name: name, fs: http.FS(fs)}
}
// LayeredFS is a layered asset file-system. It works like http.FileSystem, but it can have multiple layers.
diff --git a/modules/assetfs/layered_test.go b/modules/assetfs/layered_test.go
index 03a3ae0d7c..b549815ea5 100644
--- a/modules/assetfs/layered_test.go
+++ b/modules/assetfs/layered_test.go
@@ -52,7 +52,7 @@ func TestLayered(t *testing.T) {
assert.NoError(t, err)
bs, err := io.ReadAll(f)
assert.NoError(t, err)
- assert.EqualValues(t, "f1", string(bs))
+ assert.Equal(t, "f1", string(bs))
_ = f.Close()
assertRead := func(expected string, expectedErr error, elems ...string) {
@@ -76,27 +76,27 @@ func TestLayered(t *testing.T) {
files, err := assets.ListFiles(".", true)
assert.NoError(t, err)
- assert.EqualValues(t, []string{"f1", "f2", "fa"}, files)
+ assert.Equal(t, []string{"f1", "f2", "fa"}, files)
files, err = assets.ListFiles(".", false)
assert.NoError(t, err)
- assert.EqualValues(t, []string{"d1", "d2", "da"}, files)
+ assert.Equal(t, []string{"d1", "d2", "da"}, files)
files, err = assets.ListFiles(".")
assert.NoError(t, err)
- assert.EqualValues(t, []string{"d1", "d2", "da", "f1", "f2", "fa"}, files)
+ assert.Equal(t, []string{"d1", "d2", "da", "f1", "f2", "fa"}, files)
files, err = assets.ListAllFiles(".", true)
assert.NoError(t, err)
- assert.EqualValues(t, []string{"d1/f", "d2/f", "da/f", "f1", "f2", "fa"}, files)
+ assert.Equal(t, []string{"d1/f", "d2/f", "da/f", "f1", "f2", "fa"}, files)
files, err = assets.ListAllFiles(".", false)
assert.NoError(t, err)
- assert.EqualValues(t, []string{"d1", "d2", "da", "da/sub1", "da/sub2"}, files)
+ assert.Equal(t, []string{"d1", "d2", "da", "da/sub1", "da/sub2"}, files)
files, err = assets.ListAllFiles(".")
assert.NoError(t, err)
- assert.EqualValues(t, []string{
+ assert.Equal(t, []string{
"d1", "d1/f",
"d2", "d2/f",
"da", "da/f", "da/sub1", "da/sub2",
@@ -104,6 +104,6 @@ func TestLayered(t *testing.T) {
}, files)
assert.Empty(t, assets.GetFileLayerName("no-such"))
- assert.EqualValues(t, "l1", assets.GetFileLayerName("f1"))
- assert.EqualValues(t, "l2", assets.GetFileLayerName("f2"))
+ assert.Equal(t, "l1", assets.GetFileLayerName("f1"))
+ assert.Equal(t, "l2", assets.GetFileLayerName("f2"))
}
diff --git a/modules/auth/httpauth/httpauth.go b/modules/auth/httpauth/httpauth.go
new file mode 100644
index 0000000000..7f1f1ee152
--- /dev/null
+++ b/modules/auth/httpauth/httpauth.go
@@ -0,0 +1,47 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package httpauth
+
+import (
+ "encoding/base64"
+ "strings"
+
+ "code.gitea.io/gitea/modules/util"
+)
+
+type BasicAuth struct {
+ Username, Password string
+}
+
+type BearerToken struct {
+ Token string
+}
+
+type ParsedAuthorizationHeader struct {
+ BasicAuth *BasicAuth
+ BearerToken *BearerToken
+}
+
+func ParseAuthorizationHeader(header string) (ret ParsedAuthorizationHeader, _ bool) {
+ parts := strings.Fields(header)
+ if len(parts) != 2 {
+ return ret, false
+ }
+ if util.AsciiEqualFold(parts[0], "basic") {
+ s, err := base64.StdEncoding.DecodeString(parts[1])
+ if err != nil {
+ return ret, false
+ }
+ u, p, ok := strings.Cut(string(s), ":")
+ if !ok {
+ return ret, false
+ }
+ ret.BasicAuth = &BasicAuth{Username: u, Password: p}
+ return ret, true
+ } else if util.AsciiEqualFold(parts[0], "token") || util.AsciiEqualFold(parts[0], "bearer") {
+ ret.BearerToken = &BearerToken{Token: parts[1]}
+ return ret, true
+ }
+ return ret, false
+}
diff --git a/modules/auth/httpauth/httpauth_test.go b/modules/auth/httpauth/httpauth_test.go
new file mode 100644
index 0000000000..087b86917f
--- /dev/null
+++ b/modules/auth/httpauth/httpauth_test.go
@@ -0,0 +1,43 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package httpauth
+
+import (
+ "encoding/base64"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestParseAuthorizationHeader(t *testing.T) {
+ type parsed = ParsedAuthorizationHeader
+ type basic = BasicAuth
+ type bearer = BearerToken
+ cases := []struct {
+ headerValue string
+ expected parsed
+ ok bool
+ }{
+ {"", parsed{}, false},
+ {"?", parsed{}, false},
+ {"foo", parsed{}, false},
+ {"any value", parsed{}, false},
+
+ {"Basic ?", parsed{}, false},
+ {"Basic " + base64.StdEncoding.EncodeToString([]byte("foo")), parsed{}, false},
+ {"Basic " + base64.StdEncoding.EncodeToString([]byte("foo:bar")), parsed{BasicAuth: &basic{"foo", "bar"}}, true},
+ {"basic " + base64.StdEncoding.EncodeToString([]byte("foo:bar")), parsed{BasicAuth: &basic{"foo", "bar"}}, true},
+
+ {"token value", parsed{BearerToken: &bearer{"value"}}, true},
+ {"Token value", parsed{BearerToken: &bearer{"value"}}, true},
+ {"bearer value", parsed{BearerToken: &bearer{"value"}}, true},
+ {"Bearer value", parsed{BearerToken: &bearer{"value"}}, true},
+ {"Bearer wrong value", parsed{}, false},
+ }
+ for _, c := range cases {
+ ret, ok := ParseAuthorizationHeader(c.headerValue)
+ assert.Equal(t, c.ok, ok, "header %q", c.headerValue)
+ assert.Equal(t, c.expected, ret, "header %q", c.headerValue)
+ }
+}
diff --git a/modules/auth/openid/discovery_cache_test.go b/modules/auth/openid/discovery_cache_test.go
index 5a7f450937..f3d7dd226e 100644
--- a/modules/auth/openid/discovery_cache_test.go
+++ b/modules/auth/openid/discovery_cache_test.go
@@ -6,6 +6,9 @@ package openid
import (
"testing"
"time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
type testDiscoveredInfo struct{}
@@ -23,27 +26,24 @@ func (s *testDiscoveredInfo) OpLocalID() string {
}
func TestTimedDiscoveryCache(t *testing.T) {
- dc := newTimedDiscoveryCache(1 * time.Second)
+ ttl := 50 * time.Millisecond
+ dc := newTimedDiscoveryCache(ttl)
// Put some initial values
dc.Put("foo", &testDiscoveredInfo{}) // openid.opEndpoint: "a", openid.opLocalID: "b", openid.claimedID: "c"})
// Make sure we can retrieve them
- if di := dc.Get("foo"); di == nil {
- t.Errorf("Expected a result, got nil")
- } else if di.OpEndpoint() != "opEndpoint" || di.OpLocalID() != "opLocalID" || di.ClaimedID() != "claimedID" {
- t.Errorf("Expected opEndpoint opLocalID claimedID, got %v %v %v", di.OpEndpoint(), di.OpLocalID(), di.ClaimedID())
- }
+ di := dc.Get("foo")
+ require.NotNil(t, di)
+ assert.Equal(t, "opEndpoint", di.OpEndpoint())
+ assert.Equal(t, "opLocalID", di.OpLocalID())
+ assert.Equal(t, "claimedID", di.ClaimedID())
// Attempt to get a non-existent value
- if di := dc.Get("bar"); di != nil {
- t.Errorf("Expected nil, got %v", di)
- }
+ assert.Nil(t, dc.Get("bar"))
- // Sleep one second and try retrieve again
- time.Sleep(1 * time.Second)
+ // Sleep for a while and try to retrieve again
+ time.Sleep(ttl * 3 / 2)
- if di := dc.Get("foo"); di != nil {
- t.Errorf("Expected a nil, got a result")
- }
+ assert.Nil(t, dc.Get("foo"))
}
diff --git a/modules/auth/pam/pam_test.go b/modules/auth/pam/pam_test.go
index 7265b5d0c1..d4ab058ec7 100644
--- a/modules/auth/pam/pam_test.go
+++ b/modules/auth/pam/pam_test.go
@@ -15,5 +15,5 @@ func TestPamAuth(t *testing.T) {
result, err := Auth("gitea", "user1", "false-pwd")
assert.Error(t, err)
assert.EqualError(t, err, "Authentication failure")
- assert.Len(t, result)
+ assert.Empty(t, result)
}
diff --git a/modules/auth/password/hash/common.go b/modules/auth/password/hash/common.go
index 487c0738f4..d5e2c34314 100644
--- a/modules/auth/password/hash/common.go
+++ b/modules/auth/password/hash/common.go
@@ -18,7 +18,7 @@ func parseIntParam(value, param, algorithmName, config string, previousErr error
return parsed, previousErr // <- Keep the previous error as this function should still return an error once everything has been checked if any call failed
}
-func parseUIntParam(value, param, algorithmName, config string, previousErr error) (uint64, error) { //nolint:unparam
+func parseUIntParam(value, param, algorithmName, config string, previousErr error) (uint64, error) { //nolint:unparam // algorithmName is always argon2
parsed, err := strconv.ParseUint(value, 10, 64)
if err != nil {
log.Error("invalid integer for %s representation in %s hash spec %s", param, algorithmName, config)
diff --git a/modules/auth/password/password.go b/modules/auth/password/password.go
index c66b62937f..a1e101dd62 100644
--- a/modules/auth/password/password.go
+++ b/modules/auth/password/password.go
@@ -101,7 +101,7 @@ func Generate(n int) (string, error) {
buffer := make([]byte, n)
maxInt := big.NewInt(int64(len(validChars)))
for {
- for j := 0; j < n; j++ {
+ for j := range n {
rnd, err := rand.Int(rand.Reader, maxInt)
if err != nil {
return "", err
diff --git a/modules/auth/password/password_test.go b/modules/auth/password/password_test.go
index 6c35dc86bd..0fea593c85 100644
--- a/modules/auth/password/password_test.go
+++ b/modules/auth/password/password_test.go
@@ -50,7 +50,7 @@ func TestComplexity_Generate(t *testing.T) {
test := func(t *testing.T, modes []string) {
testComplextity(modes)
- for i := 0; i < maxCount; i++ {
+ for range maxCount {
pwd, err := Generate(pwdLen)
assert.NoError(t, err)
assert.Len(t, pwd, pwdLen)
diff --git a/modules/auth/password/pwn/pwn.go b/modules/auth/password/pwn/pwn.go
index f77ce9f40b..99a6ca6cea 100644
--- a/modules/auth/password/pwn/pwn.go
+++ b/modules/auth/password/pwn/pwn.go
@@ -101,7 +101,7 @@ func (c *Client) CheckPassword(pw string, padding bool) (int, error) {
}
defer resp.Body.Close()
- for _, pair := range strings.Split(string(body), "\n") {
+ for pair := range strings.SplitSeq(string(body), "\n") {
parts := strings.Split(pair, ":")
if len(parts) != 2 {
continue
diff --git a/modules/auth/password/pwn/pwn_test.go b/modules/auth/password/pwn/pwn_test.go
index b3e7734c3f..ae03fabc57 100644
--- a/modules/auth/password/pwn/pwn_test.go
+++ b/modules/auth/password/pwn/pwn_test.go
@@ -4,46 +4,57 @@
package pwn
import (
+ "errors"
+ "io"
"net/http"
+ "strings"
"testing"
- "time"
- "github.com/h2non/gock"
"github.com/stretchr/testify/assert"
)
-var client = New(WithHTTP(&http.Client{
- Timeout: time.Second * 2,
-}))
+type mockTransport struct{}
+
+func (mockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
+ if req.URL.Host != "api.pwnedpasswords.com" {
+ return nil, errors.New("unsupported host")
+ }
+ respMap := map[string]string{
+ "/range/5c1d8": "EAF2F254732680E8AC339B84F3266ECCBB5:1\r\nFC446EB88938834178CB9322C1EE273C2A7:2",
+ "/range/ba189": "FD4CB34F0378BCB15D23F6FFD28F0775C9E:3\r\nFDF342FCD8C3611DAE4D76E8A992A3E4169:4",
+ "/range/a1733": "C4CE0F1F0062B27B9E2F41AF0C08218017C:1\r\nFC446EB88938834178CB9322C1EE273C2A7:2\r\nFE81480327C992FE62065A827429DD1318B:0",
+ "/range/5617b": "FD4CB34F0378BCB15D23F6FFD28F0775C9E:3\r\nFDF342FCD8C3611DAE4D76E8A992A3E4169:4\r\nFE81480327C992FE62065A827429DD1318B:0",
+ "/range/79082": "FDF342FCD8C3611DAE4D76E8A992A3E4169:4\r\nFE81480327C992FE62065A827429DD1318B:0\r\nAFEF386F56EB0B4BE314E07696E5E6E6536:0",
+ }
+ if resp, ok := respMap[req.URL.Path]; ok {
+ return &http.Response{Request: req, Body: io.NopCloser(strings.NewReader(resp))}, nil
+ }
+ return nil, errors.New("unsupported path")
+}
func TestPassword(t *testing.T) {
- defer gock.Off()
+ client := New(WithHTTP(&http.Client{Transport: mockTransport{}}))
count, err := client.CheckPassword("", false)
assert.ErrorIs(t, err, ErrEmptyPassword, "blank input should return ErrEmptyPassword")
assert.Equal(t, -1, count)
- gock.New("https://api.pwnedpasswords.com").Get("/range/5c1d8").Times(1).Reply(200).BodyString("EAF2F254732680E8AC339B84F3266ECCBB5:1\r\nFC446EB88938834178CB9322C1EE273C2A7:2")
count, err = client.CheckPassword("pwned", false)
assert.NoError(t, err)
assert.Equal(t, 1, count)
- gock.New("https://api.pwnedpasswords.com").Get("/range/ba189").Times(1).Reply(200).BodyString("FD4CB34F0378BCB15D23F6FFD28F0775C9E:3\r\nFDF342FCD8C3611DAE4D76E8A992A3E4169:4")
count, err = client.CheckPassword("notpwned", false)
assert.NoError(t, err)
assert.Equal(t, 0, count)
- gock.New("https://api.pwnedpasswords.com").Get("/range/a1733").Times(1).Reply(200).BodyString("C4CE0F1F0062B27B9E2F41AF0C08218017C:1\r\nFC446EB88938834178CB9322C1EE273C2A7:2\r\nFE81480327C992FE62065A827429DD1318B:0")
count, err = client.CheckPassword("paddedpwned", true)
assert.NoError(t, err)
assert.Equal(t, 1, count)
- gock.New("https://api.pwnedpasswords.com").Get("/range/5617b").Times(1).Reply(200).BodyString("FD4CB34F0378BCB15D23F6FFD28F0775C9E:3\r\nFDF342FCD8C3611DAE4D76E8A992A3E4169:4\r\nFE81480327C992FE62065A827429DD1318B:0")
count, err = client.CheckPassword("paddednotpwned", true)
assert.NoError(t, err)
assert.Equal(t, 0, count)
- gock.New("https://api.pwnedpasswords.com").Get("/range/79082").Times(1).Reply(200).BodyString("FDF342FCD8C3611DAE4D76E8A992A3E4169:4\r\nFE81480327C992FE62065A827429DD1318B:0\r\nAFEF386F56EB0B4BE314E07696E5E6E6536:0")
count, err = client.CheckPassword("paddednotpwnedzero", true)
assert.NoError(t, err)
assert.Equal(t, 0, count)
diff --git a/modules/avatar/avatar_test.go b/modules/avatar/avatar_test.go
index a721c77868..a5a1a7c1b0 100644
--- a/modules/avatar/avatar_test.go
+++ b/modules/avatar/avatar_test.go
@@ -94,8 +94,8 @@ func Test_ProcessAvatarImage(t *testing.T) {
assert.NotEqual(t, origin, result)
decoded, err := png.Decode(bytes.NewReader(result))
assert.NoError(t, err)
- assert.EqualValues(t, scaledSize, decoded.Bounds().Max.X)
- assert.EqualValues(t, scaledSize, decoded.Bounds().Max.Y)
+ assert.Equal(t, scaledSize, decoded.Bounds().Max.X)
+ assert.Equal(t, scaledSize, decoded.Bounds().Max.Y)
// if origin image is smaller than the default size, use the origin image
origin = newImgData(1)
diff --git a/modules/avatar/hash_test.go b/modules/avatar/hash_test.go
index 1b8249c696..c518144b47 100644
--- a/modules/avatar/hash_test.go
+++ b/modules/avatar/hash_test.go
@@ -19,8 +19,8 @@ func Test_HashAvatar(t *testing.T) {
var buff bytes.Buffer
png.Encode(&buff, myImage)
- assert.EqualValues(t, "9ddb5bac41d57e72aa876321d0c09d71090c05f94bc625303801be2f3240d2cb", avatar.HashAvatar(1, buff.Bytes()))
- assert.EqualValues(t, "9a5d44e5d637b9582a976676e8f3de1dccd877c2fe3e66ca3fab1629f2f47609", avatar.HashAvatar(8, buff.Bytes()))
- assert.EqualValues(t, "ed7399158672088770de6f5211ce15528ebd675e92fc4fc060c025f4b2794ccb", avatar.HashAvatar(1024, buff.Bytes()))
- assert.EqualValues(t, "161178642c7d59eb25a61dddced5e6b66eae1c70880d5f148b1b497b767e72d9", avatar.HashAvatar(1024, []byte{}))
+ assert.Equal(t, "9ddb5bac41d57e72aa876321d0c09d71090c05f94bc625303801be2f3240d2cb", avatar.HashAvatar(1, buff.Bytes()))
+ assert.Equal(t, "9a5d44e5d637b9582a976676e8f3de1dccd877c2fe3e66ca3fab1629f2f47609", avatar.HashAvatar(8, buff.Bytes()))
+ assert.Equal(t, "ed7399158672088770de6f5211ce15528ebd675e92fc4fc060c025f4b2794ccb", avatar.HashAvatar(1024, buff.Bytes()))
+ assert.Equal(t, "161178642c7d59eb25a61dddced5e6b66eae1c70880d5f148b1b497b767e72d9", avatar.HashAvatar(1024, []byte{}))
}
diff --git a/modules/avatar/identicon/block.go b/modules/avatar/identicon/block.go
index cb1803a231..fc8ce90212 100644
--- a/modules/avatar/identicon/block.go
+++ b/modules/avatar/identicon/block.go
@@ -24,8 +24,8 @@ func drawBlock(img *image.Paletted, x, y, size, angle int, points []int) {
rotate(points, m, m, angle)
}
- for i := 0; i < size; i++ {
- for j := 0; j < size; j++ {
+ for i := range size {
+ for j := range size {
if pointInPolygon(i, j, points) {
img.SetColorIndex(x+i, y+j, 1)
}
diff --git a/modules/avatar/identicon/identicon.go b/modules/avatar/identicon/identicon.go
index 63926d5f19..ee92416a53 100644
--- a/modules/avatar/identicon/identicon.go
+++ b/modules/avatar/identicon/identicon.go
@@ -8,6 +8,7 @@ package identicon
import (
"crypto/sha256"
+ "errors"
"fmt"
"image"
"image/color"
@@ -29,7 +30,7 @@ type Identicon struct {
// fore all possible foreground colors. only one foreground color will be picked randomly for one image
func New(size int, back color.Color, fore ...color.Color) (*Identicon, error) {
if len(fore) == 0 {
- return nil, fmt.Errorf("foreground is not set")
+ return nil, errors.New("foreground is not set")
}
if size < minImageSize {
@@ -133,7 +134,7 @@ func drawBlocks(p *image.Paletted, size int, c, b1, b2 blockFunc, b1Angle, b2Ang
// then we make it left-right mirror, so we didn't draw 3/6/9 before
for x := 0; x < size/2; x++ {
- for y := 0; y < size; y++ {
+ for y := range size {
p.SetColorIndex(size-x, y, p.ColorIndexAt(x, y))
}
}
diff --git a/modules/badge/badge.go b/modules/badge/badge.go
index b30d0b4729..d2e9bd9d1b 100644
--- a/modules/badge/badge.go
+++ b/modules/badge/badge.go
@@ -4,6 +4,10 @@
package badge
import (
+ "strings"
+ "sync"
+ "unicode"
+
actions_model "code.gitea.io/gitea/models/actions"
)
@@ -11,94 +15,115 @@ import (
// We use 10x scale to calculate more precisely
// Then scale down to normal size in tmpl file
-type Label struct {
- text string
- width int
-}
-
-func (l Label) Text() string {
- return l.text
-}
-
-func (l Label) Width() int {
- return l.width
-}
-
-func (l Label) TextLength() int {
- return int(float64(l.width-defaultOffset) * 9.5)
-}
-
-func (l Label) X() int {
- return l.width*5 + 10
-}
-
-type Message struct {
+type Text struct {
text string
width int
x int
}
-func (m Message) Text() string {
- return m.text
+func (t Text) Text() string {
+ return t.text
}
-func (m Message) Width() int {
- return m.width
+func (t Text) Width() int {
+ return t.width
}
-func (m Message) X() int {
- return m.x
+func (t Text) X() int {
+ return t.x
}
-func (m Message) TextLength() int {
- return int(float64(m.width-defaultOffset) * 9.5)
+func (t Text) TextLength() int {
+ return int(float64(t.width-defaultOffset) * 10)
}
type Badge struct {
- Color string
- FontSize int
- Label Label
- Message Message
+ IDPrefix string
+ FontFamily string
+ Color string
+ FontSize int
+ Label Text
+ Message Text
}
func (b Badge) Width() int {
return b.Label.width + b.Message.width
}
+// Style follows https://shields.io/badges
const (
- defaultOffset = 9
- defaultFontSize = 11
- DefaultColor = "#9f9f9f" // Grey
- defaultFontWidth = 7 // approximate speculation
+ StyleFlat = "flat"
+ StyleFlatSquare = "flat-square"
)
-var StatusColorMap = map[actions_model.Status]string{
- actions_model.StatusSuccess: "#4c1", // Green
- actions_model.StatusSkipped: "#dfb317", // Yellow
- actions_model.StatusUnknown: "#97ca00", // Light Green
- actions_model.StatusFailure: "#e05d44", // Red
- actions_model.StatusCancelled: "#fe7d37", // Orange
- actions_model.StatusWaiting: "#dfb317", // Yellow
- actions_model.StatusRunning: "#dfb317", // Yellow
- actions_model.StatusBlocked: "#dfb317", // Yellow
-}
+const (
+ defaultOffset = 10
+ defaultFontSize = 11
+ DefaultColor = "#9f9f9f" // Grey
+ DefaultFontFamily = "DejaVu Sans,Verdana,Geneva,sans-serif"
+ DefaultStyle = StyleFlat
+)
+
+var GlobalVars = sync.OnceValue(func() (ret struct {
+ StatusColorMap map[actions_model.Status]string
+ DejaVuGlyphWidthData map[rune]uint8
+ AllStyles []string
+},
+) {
+ ret.StatusColorMap = map[actions_model.Status]string{
+ actions_model.StatusSuccess: "#4c1", // Green
+ actions_model.StatusSkipped: "#dfb317", // Yellow
+ actions_model.StatusUnknown: "#97ca00", // Light Green
+ actions_model.StatusFailure: "#e05d44", // Red
+ actions_model.StatusCancelled: "#fe7d37", // Orange
+ actions_model.StatusWaiting: "#dfb317", // Yellow
+ actions_model.StatusRunning: "#dfb317", // Yellow
+ actions_model.StatusBlocked: "#dfb317", // Yellow
+ }
+ ret.DejaVuGlyphWidthData = dejaVuGlyphWidthDataFunc()
+ ret.AllStyles = []string{StyleFlat, StyleFlatSquare}
+ return ret
+})
// GenerateBadge generates badge with given template
func GenerateBadge(label, message, color string) Badge {
- lw := defaultFontWidth*len(label) + defaultOffset
- mw := defaultFontWidth*len(message) + defaultOffset
- x := lw*10 + mw*5 - 10
+ lw := calculateTextWidth(label) + defaultOffset
+ mw := calculateTextWidth(message) + defaultOffset
+
+ lx := lw * 5
+ mx := lw*10 + mw*5 - 10
return Badge{
- Label: Label{
+ FontFamily: DefaultFontFamily,
+ Label: Text{
text: label,
width: lw,
+ x: lx,
},
- Message: Message{
+ Message: Text{
text: message,
width: mw,
- x: x,
+ x: mx,
},
FontSize: defaultFontSize * 10,
Color: color,
}
}
+
+func calculateTextWidth(text string) int {
+ width := 0
+ widthData := GlobalVars().DejaVuGlyphWidthData
+ for _, char := range strings.TrimSpace(text) {
+ charWidth, ok := widthData[char]
+ if !ok {
+ // use the width of 'm' in case of missing glyph width data for a printable character
+ if unicode.IsPrint(char) {
+ charWidth = widthData['m']
+ } else {
+ charWidth = 0
+ }
+ }
+ width += int(charWidth)
+ }
+
+ return width
+}
diff --git a/modules/badge/badge_glyph_width.go b/modules/badge/badge_glyph_width.go
new file mode 100644
index 0000000000..0d950c5a70
--- /dev/null
+++ b/modules/badge/badge_glyph_width.go
@@ -0,0 +1,206 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package badge
+
+// DejaVuGlyphWidthData is generated by `sfnt.Face.GlyphAdvance(nil, <rune>, 11, font.HintingNone)` with DejaVu Sans
+// v2.37 (https://github.com/dejavu-fonts/dejavu-fonts/releases/download/version_2_37/dejavu-sans-ttf-2.37.zip).
+//
+// Fonts defined in "DefaultFontFamily" all have similar widths (including "DejaVu Sans"),
+// and these widths are fixed and don't seem to change.
+//
+// A devtest page "/devtest/badge-actions-svg" could be used to check the rendered images.
+
+func dejaVuGlyphWidthDataFunc() map[rune]uint8 {
+ return map[rune]uint8{
+ 32: 3,
+ 33: 4,
+ 34: 5,
+ 35: 9,
+ 36: 7,
+ 37: 10,
+ 38: 9,
+ 39: 3,
+ 40: 4,
+ 41: 4,
+ 42: 6,
+ 43: 9,
+ 44: 3,
+ 45: 4,
+ 46: 3,
+ 47: 4,
+ 48: 7,
+ 49: 7,
+ 50: 7,
+ 51: 7,
+ 52: 7,
+ 53: 7,
+ 54: 7,
+ 55: 7,
+ 56: 7,
+ 57: 7,
+ 58: 4,
+ 59: 4,
+ 60: 9,
+ 61: 9,
+ 62: 9,
+ 63: 6,
+ 64: 11,
+ 65: 8,
+ 66: 8,
+ 67: 8,
+ 68: 8,
+ 69: 7,
+ 70: 6,
+ 71: 9,
+ 72: 8,
+ 73: 3,
+ 74: 3,
+ 75: 7,
+ 76: 6,
+ 77: 9,
+ 78: 8,
+ 79: 9,
+ 80: 7,
+ 81: 9,
+ 82: 8,
+ 83: 7,
+ 84: 7,
+ 85: 8,
+ 86: 8,
+ 87: 11,
+ 88: 8,
+ 89: 7,
+ 90: 8,
+ 91: 4,
+ 92: 4,
+ 93: 4,
+ 94: 9,
+ 95: 6,
+ 96: 6,
+ 97: 7,
+ 98: 7,
+ 99: 6,
+ 100: 7,
+ 101: 7,
+ 102: 4,
+ 103: 7,
+ 104: 7,
+ 105: 3,
+ 106: 3,
+ 107: 6,
+ 108: 3,
+ 109: 11,
+ 110: 7,
+ 111: 7,
+ 112: 7,
+ 113: 7,
+ 114: 5,
+ 115: 6,
+ 116: 4,
+ 117: 7,
+ 118: 7,
+ 119: 9,
+ 120: 7,
+ 121: 7,
+ 122: 6,
+ 123: 7,
+ 124: 4,
+ 125: 7,
+ 126: 9,
+ 161: 4,
+ 162: 7,
+ 163: 7,
+ 164: 7,
+ 165: 7,
+ 166: 4,
+ 167: 6,
+ 168: 6,
+ 169: 11,
+ 170: 5,
+ 171: 7,
+ 172: 9,
+ 174: 11,
+ 175: 6,
+ 176: 6,
+ 177: 9,
+ 178: 4,
+ 179: 4,
+ 180: 6,
+ 181: 7,
+ 182: 7,
+ 183: 3,
+ 184: 6,
+ 185: 4,
+ 186: 5,
+ 187: 7,
+ 188: 11,
+ 189: 11,
+ 190: 11,
+ 191: 6,
+ 192: 8,
+ 193: 8,
+ 194: 8,
+ 195: 8,
+ 196: 8,
+ 197: 8,
+ 198: 11,
+ 199: 8,
+ 200: 7,
+ 201: 7,
+ 202: 7,
+ 203: 7,
+ 204: 3,
+ 205: 3,
+ 206: 3,
+ 207: 3,
+ 208: 9,
+ 209: 8,
+ 210: 9,
+ 211: 9,
+ 212: 9,
+ 213: 9,
+ 214: 9,
+ 215: 9,
+ 216: 9,
+ 217: 8,
+ 218: 8,
+ 219: 8,
+ 220: 8,
+ 221: 7,
+ 222: 7,
+ 223: 7,
+ 224: 7,
+ 225: 7,
+ 226: 7,
+ 227: 7,
+ 228: 7,
+ 229: 7,
+ 230: 11,
+ 231: 6,
+ 232: 7,
+ 233: 7,
+ 234: 7,
+ 235: 7,
+ 236: 3,
+ 237: 3,
+ 238: 3,
+ 239: 3,
+ 240: 7,
+ 241: 7,
+ 242: 7,
+ 243: 7,
+ 244: 7,
+ 245: 7,
+ 246: 7,
+ 247: 9,
+ 248: 7,
+ 249: 7,
+ 250: 7,
+ 251: 7,
+ 252: 7,
+ 253: 7,
+ 254: 7,
+ 255: 7,
+ }
+}
diff --git a/modules/base/tool.go b/modules/base/tool.go
index 1d16186bc5..ed94575e74 100644
--- a/modules/base/tool.go
+++ b/modules/base/tool.go
@@ -8,17 +8,12 @@ import (
"crypto/sha1"
"crypto/sha256"
"crypto/subtle"
- "encoding/base64"
"encoding/hex"
- "errors"
"fmt"
"hash"
"strconv"
- "strings"
"time"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@@ -38,19 +33,6 @@ func ShortSha(sha1 string) string {
return util.TruncateRunes(sha1, 10)
}
-// BasicAuthDecode decode basic auth string
-func BasicAuthDecode(encoded string) (string, string, error) {
- s, err := base64.StdEncoding.DecodeString(encoded)
- if err != nil {
- return "", "", err
- }
-
- if username, password, ok := strings.Cut(string(s), ":"); ok {
- return username, password, nil
- }
- return "", "", errors.New("invalid basic authentication")
-}
-
// VerifyTimeLimitCode verify time limit code
func VerifyTimeLimitCode(now time.Time, data string, minutes int, code string) bool {
if len(code) <= 18 {
@@ -64,10 +46,7 @@ func VerifyTimeLimitCode(now time.Time, data string, minutes int, code string) b
// check code
retCode := CreateTimeLimitCode(data, aliveTime, startTimeStr, nil)
if subtle.ConstantTimeCompare([]byte(retCode), []byte(code)) != 1 {
- retCode = CreateTimeLimitCode(data, aliveTime, startTimeStr, sha1.New()) // TODO: this is only for the support of legacy codes, remove this in/after 1.23
- if subtle.ConstantTimeCompare([]byte(retCode), []byte(code)) != 1 {
- return false
- }
+ return false
}
// check time is expired or not: startTime <= now && now < startTime + minutes
@@ -143,25 +122,3 @@ func Int64sToStrings(ints []int64) []string {
}
return strs
}
-
-// EntryIcon returns the octicon class for displaying files/directories
-func EntryIcon(entry *git.TreeEntry) string {
- switch {
- case entry.IsLink():
- te, err := entry.FollowLink()
- if err != nil {
- log.Debug(err.Error())
- return "file-symlink-file"
- }
- if te.IsDir() {
- return "file-directory-symlink"
- }
- return "file-symlink-file"
- case entry.IsDir():
- return "file-directory-fill"
- case entry.IsSubModule():
- return "file-submodule"
- }
-
- return "file"
-}
diff --git a/modules/base/tool_test.go b/modules/base/tool_test.go
index c821a55c19..b7365e40c4 100644
--- a/modules/base/tool_test.go
+++ b/modules/base/tool_test.go
@@ -26,25 +26,6 @@ func TestShortSha(t *testing.T) {
assert.Equal(t, "veryverylo", ShortSha("veryverylong"))
}
-func TestBasicAuthDecode(t *testing.T) {
- _, _, err := BasicAuthDecode("?")
- assert.Equal(t, "illegal base64 data at input byte 0", err.Error())
-
- user, pass, err := BasicAuthDecode("Zm9vOmJhcg==")
- assert.NoError(t, err)
- assert.Equal(t, "foo", user)
- assert.Equal(t, "bar", pass)
-
- _, _, err = BasicAuthDecode("aW52YWxpZA==")
- assert.Error(t, err)
-
- _, _, err = BasicAuthDecode("invalid")
- assert.Error(t, err)
-
- _, _, err = BasicAuthDecode("YWxpY2U=") // "alice", no colon
- assert.Error(t, err)
-}
-
func TestVerifyTimeLimitCode(t *testing.T) {
defer test.MockVariableValue(&setting.InstallLock, true)()
initGeneralSecret := func(secret string) {
@@ -86,13 +67,10 @@ JWT_SECRET = %s
verifyDataCode := func(c string) bool {
return VerifyTimeLimitCode(now, "data", 2, c)
}
- code1 := CreateTimeLimitCode("data", 2, now, sha1.New())
- code2 := CreateTimeLimitCode("data", 2, now, nil)
- assert.True(t, verifyDataCode(code1))
- assert.True(t, verifyDataCode(code2))
+ code := CreateTimeLimitCode("data", 2, now, nil)
+ assert.True(t, verifyDataCode(code))
initGeneralSecret("000_QLUd4fYVyxetjxC4eZkrBgWM2SndOOWDNtgUUko")
- assert.False(t, verifyDataCode(code1))
- assert.False(t, verifyDataCode(code2))
+ assert.False(t, verifyDataCode(code))
})
}
@@ -137,5 +115,3 @@ func TestInt64sToStrings(t *testing.T) {
Int64sToStrings([]int64{1, 4, 16, 64, 256}),
)
}
-
-// TODO: Test EntryIcon
diff --git a/modules/cache/cache.go b/modules/cache/cache.go
index b5400b0bd6..039caa9fbc 100644
--- a/modules/cache/cache.go
+++ b/modules/cache/cache.go
@@ -4,6 +4,8 @@
package cache
import (
+ "encoding/hex"
+ "errors"
"fmt"
"strconv"
"time"
@@ -22,7 +24,7 @@ func Init() error {
if err != nil {
return err
}
- for i := 0; i < 10; i++ {
+ for range 10 {
if err = c.Ping(); err == nil {
break
}
@@ -37,16 +39,21 @@ func Init() error {
}
const (
- testCacheKey = "DefaultCache.TestKey"
- SlowCacheThreshold = 100 * time.Microsecond
+ testCacheKey = "DefaultCache.TestKey"
+ // SlowCacheThreshold marks cache tests as slow
+ // set to 30ms per discussion: https://github.com/go-gitea/gitea/issues/33190
+ // TODO: Replace with metrics histogram
+ SlowCacheThreshold = 30 * time.Millisecond
)
+// Test performs delete, put and get operations on a predefined key
+// returns
func Test() (time.Duration, error) {
if defaultCache == nil {
- return 0, fmt.Errorf("default cache not initialized")
+ return 0, errors.New("default cache not initialized")
}
- testData := fmt.Sprintf("%x", make([]byte, 500))
+ testData := hex.EncodeToString(make([]byte, 500))
start := time.Now()
@@ -58,10 +65,10 @@ func Test() (time.Duration, error) {
}
testVal, hit := defaultCache.Get(testCacheKey)
if !hit {
- return 0, fmt.Errorf("expect cache hit but got none")
+ return 0, errors.New("expect cache hit but got none")
}
if testVal != testData {
- return 0, fmt.Errorf("expect cache to return same value as stored but got other")
+ return 0, errors.New("expect cache to return same value as stored but got other")
}
return time.Since(start), nil
diff --git a/modules/cache/cache_redis.go b/modules/cache/cache_redis.go
index c5b52a2086..7473c938af 100644
--- a/modules/cache/cache_redis.go
+++ b/modules/cache/cache_redis.go
@@ -11,7 +11,7 @@ import (
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/nosql"
- "gitea.com/go-chi/cache" //nolint:depguard
+ "gitea.com/go-chi/cache" //nolint:depguard // we wrap this package here
"github.com/redis/go-redis/v9"
)
diff --git a/modules/cache/cache_test.go b/modules/cache/cache_test.go
index d0352947a8..d6ea2032ee 100644
--- a/modules/cache/cache_test.go
+++ b/modules/cache/cache_test.go
@@ -4,7 +4,7 @@
package cache
import (
- "fmt"
+ "errors"
"testing"
"time"
@@ -43,7 +43,8 @@ func TestTest(t *testing.T) {
elapsed, err := Test()
assert.NoError(t, err)
// mem cache should take from 300ns up to 1ms on modern hardware ...
- assert.Less(t, elapsed, time.Millisecond)
+ assert.Positive(t, elapsed)
+ assert.Less(t, elapsed, SlowCacheThreshold)
}
func TestGetCache(t *testing.T) {
@@ -56,22 +57,22 @@ func TestGetString(t *testing.T) {
createTestCache()
data, err := GetString("key", func() (string, error) {
- return "", fmt.Errorf("some error")
+ return "", errors.New("some error")
})
assert.Error(t, err)
- assert.Equal(t, "", data)
+ assert.Empty(t, data)
data, err = GetString("key", func() (string, error) {
return "", nil
})
assert.NoError(t, err)
- assert.Equal(t, "", data)
+ assert.Empty(t, data)
data, err = GetString("key", func() (string, error) {
return "some data", nil
})
assert.NoError(t, err)
- assert.Equal(t, "", data)
+ assert.Empty(t, data)
Remove("key")
data, err = GetString("key", func() (string, error) {
@@ -81,7 +82,7 @@ func TestGetString(t *testing.T) {
assert.Equal(t, "some data", data)
data, err = GetString("key", func() (string, error) {
- return "", fmt.Errorf("some error")
+ return "", errors.New("some error")
})
assert.NoError(t, err)
assert.Equal(t, "some data", data)
@@ -92,7 +93,7 @@ func TestGetInt64(t *testing.T) {
createTestCache()
data, err := GetInt64("key", func() (int64, error) {
- return 0, fmt.Errorf("some error")
+ return 0, errors.New("some error")
})
assert.Error(t, err)
assert.EqualValues(t, 0, data)
@@ -117,7 +118,7 @@ func TestGetInt64(t *testing.T) {
assert.EqualValues(t, 100, data)
data, err = GetInt64("key", func() (int64, error) {
- return 0, fmt.Errorf("some error")
+ return 0, errors.New("some error")
})
assert.NoError(t, err)
assert.EqualValues(t, 100, data)
diff --git a/modules/cache/cache_twoqueue.go b/modules/cache/cache_twoqueue.go
index 1eda2debc4..c8db686e57 100644
--- a/modules/cache/cache_twoqueue.go
+++ b/modules/cache/cache_twoqueue.go
@@ -10,7 +10,7 @@ import (
"code.gitea.io/gitea/modules/json"
- mc "gitea.com/go-chi/cache" //nolint:depguard
+ mc "gitea.com/go-chi/cache" //nolint:depguard // we wrap this package here
lru "github.com/hashicorp/golang-lru/v2"
)
diff --git a/modules/cache/context.go b/modules/cache/context.go
index 484cee659a..23f7c23a52 100644
--- a/modules/cache/context.go
+++ b/modules/cache/context.go
@@ -5,176 +5,39 @@ package cache
import (
"context"
- "sync"
"time"
-
- "code.gitea.io/gitea/modules/log"
)
-// cacheContext is a context that can be used to cache data in a request level context
-// This is useful for caching data that is expensive to calculate and is likely to be
-// used multiple times in a request.
-type cacheContext struct {
- data map[any]map[any]any
- lock sync.RWMutex
- created time.Time
- discard bool
-}
-
-func (cc *cacheContext) Get(tp, key any) any {
- cc.lock.RLock()
- defer cc.lock.RUnlock()
- return cc.data[tp][key]
-}
-
-func (cc *cacheContext) Put(tp, key, value any) {
- cc.lock.Lock()
- defer cc.lock.Unlock()
-
- if cc.discard {
- return
- }
-
- d := cc.data[tp]
- if d == nil {
- d = make(map[any]any)
- cc.data[tp] = d
- }
- d[key] = value
-}
-
-func (cc *cacheContext) Delete(tp, key any) {
- cc.lock.Lock()
- defer cc.lock.Unlock()
- delete(cc.data[tp], key)
-}
-
-func (cc *cacheContext) Discard() {
- cc.lock.Lock()
- defer cc.lock.Unlock()
- cc.data = nil
- cc.discard = true
-}
-
-func (cc *cacheContext) isDiscard() bool {
- cc.lock.RLock()
- defer cc.lock.RUnlock()
- return cc.discard
-}
-
-// cacheContextLifetime is the max lifetime of cacheContext.
-// Since cacheContext is used to cache data in a request level context, 5 minutes is enough.
-// If a cacheContext is used more than 5 minutes, it's probably misuse.
-const cacheContextLifetime = 5 * time.Minute
-
-var timeNow = time.Now
+type cacheContextKeyType struct{}
-func (cc *cacheContext) Expired() bool {
- return timeNow().Sub(cc.created) > cacheContextLifetime
-}
-
-var cacheContextKey = struct{}{}
-
-/*
-Since there are both WithCacheContext and WithNoCacheContext,
-it may be confusing when there is nesting.
-
-Some cases to explain the design:
-
-When:
-- A, B or C means a cache context.
-- A', B' or C' means a discard cache context.
-- ctx means context.Backgrand().
-- A(ctx) means a cache context with ctx as the parent context.
-- B(A(ctx)) means a cache context with A(ctx) as the parent context.
-- With is alias of WithCacheContext.
-- WithNo is alias of WithNoCacheContext.
+var cacheContextKey = cacheContextKeyType{}
-So:
-- With(ctx) -> A(ctx)
-- With(With(ctx)) -> A(ctx), not B(A(ctx)), always reuse parent cache context if possible.
-- With(With(With(ctx))) -> A(ctx), not C(B(A(ctx))), ditto.
-- WithNo(ctx) -> ctx, not A'(ctx), don't create new cache context if we don't have to.
-- WithNo(With(ctx)) -> A'(ctx)
-- WithNo(WithNo(With(ctx))) -> A'(ctx), not B'(A'(ctx)), don't create new cache context if we don't have to.
-- With(WithNo(With(ctx))) -> B(A'(ctx)), not A(ctx), never reuse a discard cache context.
-- WithNo(With(WithNo(With(ctx)))) -> B'(A'(ctx))
-- With(WithNo(With(WithNo(With(ctx))))) -> C(B'(A'(ctx))), so there's always only one not-discard cache context.
-*/
+// contextCacheLifetime is the max lifetime of context cache.
+// Since context cache is used to cache data in a request level context, 5 minutes is enough.
+// If a context cache is used more than 5 minutes, it's probably abused.
+const contextCacheLifetime = 5 * time.Minute
func WithCacheContext(ctx context.Context) context.Context {
- if c, ok := ctx.Value(cacheContextKey).(*cacheContext); ok {
- if !c.isDiscard() {
- // reuse parent context
- return ctx
- }
- }
- // FIXME: review the use of this nolint directive
- return context.WithValue(ctx, cacheContextKey, &cacheContext{ //nolint:staticcheck
- data: make(map[any]map[any]any),
- created: timeNow(),
- })
-}
-
-func WithNoCacheContext(ctx context.Context) context.Context {
- if c, ok := ctx.Value(cacheContextKey).(*cacheContext); ok {
- // The caller want to run long-life tasks, but the parent context is a cache context.
- // So we should disable and clean the cache data, or it will be kept in memory for a long time.
- c.Discard()
+ if c := GetContextCache(ctx); c != nil {
return ctx
}
-
- return ctx
-}
-
-func GetContextData(ctx context.Context, tp, key any) any {
- if c, ok := ctx.Value(cacheContextKey).(*cacheContext); ok {
- if c.Expired() {
- // The warning means that the cache context is misused for long-life task,
- // it can be resolved with WithNoCacheContext(ctx).
- log.Warn("cache context is expired, is highly likely to be misused for long-life tasks: %v", c)
- return nil
- }
- return c.Get(tp, key)
- }
- return nil
+ return context.WithValue(ctx, cacheContextKey, NewEphemeralCache(contextCacheLifetime))
}
-func SetContextData(ctx context.Context, tp, key, value any) {
- if c, ok := ctx.Value(cacheContextKey).(*cacheContext); ok {
- if c.Expired() {
- // The warning means that the cache context is misused for long-life task,
- // it can be resolved with WithNoCacheContext(ctx).
- log.Warn("cache context is expired, is highly likely to be misused for long-life tasks: %v", c)
- return
- }
- c.Put(tp, key, value)
- return
- }
-}
-
-func RemoveContextData(ctx context.Context, tp, key any) {
- if c, ok := ctx.Value(cacheContextKey).(*cacheContext); ok {
- if c.Expired() {
- // The warning means that the cache context is misused for long-life task,
- // it can be resolved with WithNoCacheContext(ctx).
- log.Warn("cache context is expired, is highly likely to be misused for long-life tasks: %v", c)
- return
- }
- c.Delete(tp, key)
- }
+func GetContextCache(ctx context.Context) *EphemeralCache {
+ c, _ := ctx.Value(cacheContextKey).(*EphemeralCache)
+ return c
}
// GetWithContextCache returns the cache value of the given key in the given context.
-func GetWithContextCache[T any](ctx context.Context, cacheGroupKey string, cacheTargetID any, f func() (T, error)) (T, error) {
- v := GetContextData(ctx, cacheGroupKey, cacheTargetID)
- if vv, ok := v.(T); ok {
- return vv, nil
- }
- t, err := f()
- if err != nil {
- return t, err
- }
- SetContextData(ctx, cacheGroupKey, cacheTargetID, t)
- return t, nil
+// FIXME: in some cases, the "context cache" should not be used, because it has uncontrollable behaviors
+// For example, these calls:
+// * GetWithContextCache(TargetID) -> OtherCodeCreateModel(TargetID) -> GetWithContextCache(TargetID)
+// Will cause the second call is not able to get the correct created target.
+// UNLESS it is certain that the target won't be changed during the request, DO NOT use it.
+func GetWithContextCache[T, K any](ctx context.Context, groupKey string, targetKey K, f func(context.Context, K) (T, error)) (T, error) {
+ if c := GetContextCache(ctx); c != nil {
+ return GetWithEphemeralCache(ctx, c, groupKey, targetKey, f)
+ }
+ return f(ctx, targetKey)
}
diff --git a/modules/cache/context_test.go b/modules/cache/context_test.go
index c01b9e8d84..8371c2b908 100644
--- a/modules/cache/context_test.go
+++ b/modules/cache/context_test.go
@@ -8,71 +8,43 @@ import (
"testing"
"time"
+ "code.gitea.io/gitea/modules/test"
+
"github.com/stretchr/testify/assert"
)
func TestWithCacheContext(t *testing.T) {
- ctx := WithCacheContext(context.Background())
-
- v := GetContextData(ctx, "empty_field", "my_config1")
+ ctx := WithCacheContext(t.Context())
+ c := GetContextCache(ctx)
+ v, _ := c.Get("empty_field", "my_config1")
assert.Nil(t, v)
const field = "system_setting"
- v = GetContextData(ctx, field, "my_config1")
+ v, _ = c.Get(field, "my_config1")
assert.Nil(t, v)
- SetContextData(ctx, field, "my_config1", 1)
- v = GetContextData(ctx, field, "my_config1")
+ c.Put(field, "my_config1", 1)
+ v, _ = c.Get(field, "my_config1")
assert.NotNil(t, v)
- assert.EqualValues(t, 1, v.(int))
+ assert.Equal(t, 1, v.(int))
- RemoveContextData(ctx, field, "my_config1")
- RemoveContextData(ctx, field, "my_config2") // remove a non-exist key
+ c.Delete(field, "my_config1")
+ c.Delete(field, "my_config2") // remove a non-exist key
- v = GetContextData(ctx, field, "my_config1")
+ v, _ = c.Get(field, "my_config1")
assert.Nil(t, v)
- vInt, err := GetWithContextCache(ctx, field, "my_config1", func() (int, error) {
+ vInt, err := GetWithContextCache(ctx, field, "my_config1", func(context.Context, string) (int, error) {
return 1, nil
})
assert.NoError(t, err)
- assert.EqualValues(t, 1, vInt)
+ assert.Equal(t, 1, vInt)
- v = GetContextData(ctx, field, "my_config1")
+ v, _ = c.Get(field, "my_config1")
assert.EqualValues(t, 1, v)
- now := timeNow
- defer func() {
- timeNow = now
- }()
- timeNow = func() time.Time {
- return now().Add(5 * time.Minute)
- }
- v = GetContextData(ctx, field, "my_config1")
- assert.Nil(t, v)
-}
-
-func TestWithNoCacheContext(t *testing.T) {
- ctx := context.Background()
-
- const field = "system_setting"
-
- v := GetContextData(ctx, field, "my_config1")
- assert.Nil(t, v)
- SetContextData(ctx, field, "my_config1", 1)
- v = GetContextData(ctx, field, "my_config1")
- assert.Nil(t, v) // still no cache
-
- ctx = WithCacheContext(ctx)
- v = GetContextData(ctx, field, "my_config1")
- assert.Nil(t, v)
- SetContextData(ctx, field, "my_config1", 1)
- v = GetContextData(ctx, field, "my_config1")
- assert.NotNil(t, v)
-
- ctx = WithNoCacheContext(ctx)
- v = GetContextData(ctx, field, "my_config1")
+ defer test.MockVariableValue(&timeNow, func() time.Time {
+ return time.Now().Add(5 * time.Minute)
+ })()
+ v, _ = c.Get(field, "my_config1")
assert.Nil(t, v)
- SetContextData(ctx, field, "my_config1", 1)
- v = GetContextData(ctx, field, "my_config1")
- assert.Nil(t, v) // still no cache
}
diff --git a/modules/cache/ephemeral.go b/modules/cache/ephemeral.go
new file mode 100644
index 0000000000..6996010ac4
--- /dev/null
+++ b/modules/cache/ephemeral.go
@@ -0,0 +1,90 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cache
+
+import (
+ "context"
+ "sync"
+ "time"
+
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/util"
+)
+
+// EphemeralCache is a cache that can be used to store data in a request level context
+// This is useful for caching data that is expensive to calculate and is likely to be
+// used multiple times in a request.
+type EphemeralCache struct {
+ data map[any]map[any]any
+ lock sync.RWMutex
+ created time.Time
+ checkLifeTime time.Duration
+}
+
+var timeNow = time.Now
+
+func NewEphemeralCache(checkLifeTime ...time.Duration) *EphemeralCache {
+ return &EphemeralCache{
+ data: make(map[any]map[any]any),
+ created: timeNow(),
+ checkLifeTime: util.OptionalArg(checkLifeTime, 0),
+ }
+}
+
+func (cc *EphemeralCache) checkExceededLifeTime(tp, key any) bool {
+ if cc.checkLifeTime > 0 && timeNow().Sub(cc.created) > cc.checkLifeTime {
+ log.Warn("EphemeralCache is expired, is highly likely to be abused for long-life tasks: %v, %v", tp, key)
+ return true
+ }
+ return false
+}
+
+func (cc *EphemeralCache) Get(tp, key any) (any, bool) {
+ if cc.checkExceededLifeTime(tp, key) {
+ return nil, false
+ }
+ cc.lock.RLock()
+ defer cc.lock.RUnlock()
+ ret, ok := cc.data[tp][key]
+ return ret, ok
+}
+
+func (cc *EphemeralCache) Put(tp, key, value any) {
+ if cc.checkExceededLifeTime(tp, key) {
+ return
+ }
+
+ cc.lock.Lock()
+ defer cc.lock.Unlock()
+
+ d := cc.data[tp]
+ if d == nil {
+ d = make(map[any]any)
+ cc.data[tp] = d
+ }
+ d[key] = value
+}
+
+func (cc *EphemeralCache) Delete(tp, key any) {
+ if cc.checkExceededLifeTime(tp, key) {
+ return
+ }
+
+ cc.lock.Lock()
+ defer cc.lock.Unlock()
+ delete(cc.data[tp], key)
+}
+
+func GetWithEphemeralCache[T, K any](ctx context.Context, c *EphemeralCache, groupKey string, targetKey K, f func(context.Context, K) (T, error)) (T, error) {
+ v, has := c.Get(groupKey, targetKey)
+ if vv, ok := v.(T); has && ok {
+ return vv, nil
+ }
+ t, err := f(ctx, targetKey)
+ if err != nil {
+ return t, err
+ }
+ c.Put(groupKey, targetKey, t)
+ return t, nil
+}
diff --git a/modules/cache/string_cache.go b/modules/cache/string_cache.go
index 4f659616f5..3562b7a926 100644
--- a/modules/cache/string_cache.go
+++ b/modules/cache/string_cache.go
@@ -11,7 +11,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
- chi_cache "gitea.com/go-chi/cache" //nolint:depguard
+ chi_cache "gitea.com/go-chi/cache" //nolint:depguard // we wrap this package here
)
type GetJSONError struct {
diff --git a/modules/cachegroup/cachegroup.go b/modules/cachegroup/cachegroup.go
new file mode 100644
index 0000000000..06085f860f
--- /dev/null
+++ b/modules/cachegroup/cachegroup.go
@@ -0,0 +1,12 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cachegroup
+
+const (
+ User = "user"
+ EmailAvatarLink = "email_avatar_link"
+ UserEmailAddresses = "user_email_addresses"
+ GPGKeyWithSubKeys = "gpg_key_with_subkeys"
+ RepoUserPermission = "repo_user_permission"
+)
diff --git a/modules/charset/ambiguous_gen_test.go b/modules/charset/ambiguous_gen_test.go
index 221c27d0e1..d3be0b1a13 100644
--- a/modules/charset/ambiguous_gen_test.go
+++ b/modules/charset/ambiguous_gen_test.go
@@ -14,7 +14,7 @@ import (
func TestAmbiguousCharacters(t *testing.T) {
for locale, ambiguous := range AmbiguousCharacters {
assert.Equal(t, locale, ambiguous.Locale)
- assert.Equal(t, len(ambiguous.Confusable), len(ambiguous.With))
+ assert.Len(t, ambiguous.With, len(ambiguous.Confusable))
assert.True(t, sort.SliceIsSorted(ambiguous.Confusable, func(i, j int) bool {
return ambiguous.Confusable[i] < ambiguous.Confusable[j]
}))
diff --git a/modules/charset/charset.go b/modules/charset/charset.go
index 1855446a98..597ce5120c 100644
--- a/modules/charset/charset.go
+++ b/modules/charset/charset.go
@@ -164,7 +164,7 @@ func DetectEncoding(content []byte) (string, error) {
}
times := 1024 / len(content)
detectContent = make([]byte, 0, times*len(content))
- for i := 0; i < times; i++ {
+ for range times {
detectContent = append(detectContent, content...)
}
} else {
diff --git a/modules/charset/charset_test.go b/modules/charset/charset_test.go
index 19b1303365..cd2e3b9aaa 100644
--- a/modules/charset/charset_test.go
+++ b/modules/charset/charset_test.go
@@ -242,7 +242,7 @@ func stringMustEndWith(t *testing.T, expected, value string) {
func TestToUTF8WithFallbackReader(t *testing.T) {
resetDefaultCharsetsOrder()
- for testLen := 0; testLen < 2048; testLen++ {
+ for testLen := range 2048 {
pattern := " test { () }\n"
input := ""
for len(input) < testLen {
@@ -252,7 +252,7 @@ func TestToUTF8WithFallbackReader(t *testing.T) {
input += "// Выключаем"
rd := ToUTF8WithFallbackReader(bytes.NewReader([]byte(input)), ConvertOpts{})
r, _ := io.ReadAll(rd)
- assert.EqualValuesf(t, input, string(r), "testing string len=%d", testLen)
+ assert.Equalf(t, input, string(r), "testing string len=%d", testLen)
}
truncatedOneByteExtension := failFastBytes
diff --git a/modules/structs/commit_status.go b/modules/commitstatus/commit_status.go
index dc880ef5eb..a0ab4e7186 100644
--- a/modules/structs/commit_status.go
+++ b/modules/commitstatus/commit_status.go
@@ -1,11 +1,11 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package structs
+package commitstatus
// CommitStatusState holds the state of a CommitStatus
-// It can be "pending", "success", "error" and "failure"
-type CommitStatusState string
+// swagger:enum CommitStatusState
+type CommitStatusState string //nolint:revive // export stutter
const (
// CommitStatusPending is for when the CommitStatus is Pending
@@ -18,35 +18,14 @@ const (
CommitStatusFailure CommitStatusState = "failure"
// CommitStatusWarning is for when the CommitStatus is Warning
CommitStatusWarning CommitStatusState = "warning"
+ // CommitStatusSkipped is for when CommitStatus is Skipped
+ CommitStatusSkipped CommitStatusState = "skipped"
)
-var commitStatusPriorities = map[CommitStatusState]int{
- CommitStatusError: 0,
- CommitStatusFailure: 1,
- CommitStatusWarning: 2,
- CommitStatusPending: 3,
- CommitStatusSuccess: 4,
-}
-
func (css CommitStatusState) String() string {
return string(css)
}
-// NoBetterThan returns true if this State is no better than the given State
-// This function only handles the states defined in CommitStatusPriorities
-func (css CommitStatusState) NoBetterThan(css2 CommitStatusState) bool {
- // NoBetterThan only handles the 5 states above
- if _, exist := commitStatusPriorities[css]; !exist {
- return false
- }
-
- if _, exist := commitStatusPriorities[css2]; !exist {
- return false
- }
-
- return commitStatusPriorities[css] <= commitStatusPriorities[css2]
-}
-
// IsPending represents if commit status state is pending
func (css CommitStatusState) IsPending() bool {
return css == CommitStatusPending
@@ -71,3 +50,32 @@ func (css CommitStatusState) IsFailure() bool {
func (css CommitStatusState) IsWarning() bool {
return css == CommitStatusWarning
}
+
+// IsSkipped represents if commit status state is skipped
+func (css CommitStatusState) IsSkipped() bool {
+ return css == CommitStatusSkipped
+}
+
+type CommitStatusStates []CommitStatusState //nolint:revive // export stutter
+
+// According to https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#get-the-combined-status-for-a-specific-reference
+// > Additionally, a combined state is returned. The state is one of:
+// > failure if any of the contexts report as error or failure
+// > pending if there are no statuses or a context is pending
+// > success if the latest status for all contexts is success
+func (css CommitStatusStates) Combine() CommitStatusState {
+ successCnt := 0
+ for _, state := range css {
+ switch {
+ case state.IsError() || state.IsFailure():
+ return CommitStatusFailure
+ case state.IsPending():
+ case state.IsSuccess() || state.IsWarning() || state.IsSkipped():
+ successCnt++
+ }
+ }
+ if successCnt > 0 && successCnt == len(css) {
+ return CommitStatusSuccess
+ }
+ return CommitStatusPending
+}
diff --git a/modules/commitstatus/commit_status_test.go b/modules/commitstatus/commit_status_test.go
new file mode 100644
index 0000000000..10d8f20aa4
--- /dev/null
+++ b/modules/commitstatus/commit_status_test.go
@@ -0,0 +1,201 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package commitstatus
+
+import "testing"
+
+func TestCombine(t *testing.T) {
+ tests := []struct {
+ name string
+ states CommitStatusStates
+ expected CommitStatusState
+ }{
+ // 0 states
+ {
+ name: "empty",
+ states: CommitStatusStates{},
+ expected: CommitStatusPending,
+ },
+ // 1 state
+ {
+ name: "pending",
+ states: CommitStatusStates{CommitStatusPending},
+ expected: CommitStatusPending,
+ },
+ {
+ name: "success",
+ states: CommitStatusStates{CommitStatusSuccess},
+ expected: CommitStatusSuccess,
+ },
+ {
+ name: "error",
+ states: CommitStatusStates{CommitStatusError},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "failure",
+ states: CommitStatusStates{CommitStatusFailure},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "warning",
+ states: CommitStatusStates{CommitStatusWarning},
+ expected: CommitStatusSuccess,
+ },
+ // 2 states
+ {
+ name: "pending and success",
+ states: CommitStatusStates{CommitStatusPending, CommitStatusSuccess},
+ expected: CommitStatusPending,
+ },
+ {
+ name: "pending and error",
+ states: CommitStatusStates{CommitStatusPending, CommitStatusError},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "pending and failure",
+ states: CommitStatusStates{CommitStatusPending, CommitStatusFailure},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "pending and warning",
+ states: CommitStatusStates{CommitStatusPending, CommitStatusWarning},
+ expected: CommitStatusPending,
+ },
+ {
+ name: "success and error",
+ states: CommitStatusStates{CommitStatusSuccess, CommitStatusError},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "success and failure",
+ states: CommitStatusStates{CommitStatusSuccess, CommitStatusFailure},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "success and warning",
+ states: CommitStatusStates{CommitStatusSuccess, CommitStatusWarning},
+ expected: CommitStatusSuccess,
+ },
+ {
+ name: "error and failure",
+ states: CommitStatusStates{CommitStatusError, CommitStatusFailure},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "error and warning",
+ states: CommitStatusStates{CommitStatusError, CommitStatusWarning},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "failure and warning",
+ states: CommitStatusStates{CommitStatusFailure, CommitStatusWarning},
+ expected: CommitStatusFailure,
+ },
+ // 3 states
+ {
+ name: "pending, success and warning",
+ states: CommitStatusStates{CommitStatusPending, CommitStatusSuccess, CommitStatusWarning},
+ expected: CommitStatusPending,
+ },
+ {
+ name: "pending, success and error",
+ states: CommitStatusStates{CommitStatusPending, CommitStatusSuccess, CommitStatusError},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "pending, success and failure",
+ states: CommitStatusStates{CommitStatusPending, CommitStatusSuccess, CommitStatusFailure},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "pending, error and failure",
+ states: CommitStatusStates{CommitStatusPending, CommitStatusError, CommitStatusFailure},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "success, error and warning",
+ states: CommitStatusStates{CommitStatusSuccess, CommitStatusError, CommitStatusWarning},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "success, failure and warning",
+ states: CommitStatusStates{CommitStatusSuccess, CommitStatusFailure, CommitStatusWarning},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "error, failure and warning",
+ states: CommitStatusStates{CommitStatusError, CommitStatusFailure, CommitStatusWarning},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "success, warning and skipped",
+ states: CommitStatusStates{CommitStatusSuccess, CommitStatusWarning, CommitStatusSkipped},
+ expected: CommitStatusSuccess,
+ },
+ // All success
+ {
+ name: "all success",
+ states: CommitStatusStates{CommitStatusSuccess, CommitStatusSuccess, CommitStatusSuccess},
+ expected: CommitStatusSuccess,
+ },
+ // All pending
+ {
+ name: "all pending",
+ states: CommitStatusStates{CommitStatusPending, CommitStatusPending, CommitStatusPending},
+ expected: CommitStatusPending,
+ },
+ {
+ name: "all skipped",
+ states: CommitStatusStates{CommitStatusSkipped, CommitStatusSkipped, CommitStatusSkipped},
+ expected: CommitStatusSuccess,
+ },
+ // 4 states
+ {
+ name: "pending, success, error and warning",
+ states: CommitStatusStates{CommitStatusPending, CommitStatusSuccess, CommitStatusError, CommitStatusWarning},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "pending, success, failure and warning",
+ states: CommitStatusStates{CommitStatusPending, CommitStatusSuccess, CommitStatusFailure, CommitStatusWarning},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "pending, error, failure and warning",
+ states: CommitStatusStates{CommitStatusPending, CommitStatusError, CommitStatusFailure, CommitStatusWarning},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "success, error, failure and warning",
+ states: CommitStatusStates{CommitStatusSuccess, CommitStatusError, CommitStatusFailure, CommitStatusWarning},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "mixed states",
+ states: CommitStatusStates{CommitStatusPending, CommitStatusSuccess, CommitStatusError, CommitStatusWarning},
+ expected: CommitStatusFailure,
+ },
+ {
+ name: "mixed states with all success",
+ states: CommitStatusStates{CommitStatusSuccess, CommitStatusSuccess, CommitStatusPending, CommitStatusWarning},
+ expected: CommitStatusPending,
+ },
+ {
+ name: "all success with warning",
+ states: CommitStatusStates{CommitStatusSuccess, CommitStatusSuccess, CommitStatusSuccess, CommitStatusWarning},
+ expected: CommitStatusSuccess,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := tt.states.Combine()
+ if result != tt.expected {
+ t.Errorf("expected %v, got %v", tt.expected, result)
+ }
+ })
+ }
+}
diff --git a/modules/csv/csv_test.go b/modules/csv/csv_test.go
index 29ed58db97..be9fc5f823 100644
--- a/modules/csv/csv_test.go
+++ b/modules/csv/csv_test.go
@@ -5,7 +5,6 @@ package csv
import (
"bytes"
- "context"
"encoding/csv"
"io"
"strconv"
@@ -100,10 +99,10 @@ j, ,\x20
for n, c := range cases {
rd, err := CreateReaderAndDetermineDelimiter(nil, strings.NewReader(decodeSlashes(t, c.csv)))
assert.NoError(t, err, "case %d: should not throw error: %v\n", n, err)
- assert.EqualValues(t, c.expectedDelimiter, rd.Comma, "case %d: delimiter should be '%c', got '%c'", n, c.expectedDelimiter, rd.Comma)
+ assert.Equal(t, c.expectedDelimiter, rd.Comma, "case %d: delimiter should be '%c', got '%c'", n, c.expectedDelimiter, rd.Comma)
rows, err := rd.ReadAll()
assert.NoError(t, err, "case %d: should not throw error: %v\n", n, err)
- assert.EqualValues(t, c.expectedRows, rows, "case %d: rows should be equal", n)
+ assert.Equal(t, c.expectedRows, rows, "case %d: rows should be equal", n)
}
}
@@ -231,8 +230,8 @@ John Doe john@doe.com This,note,had,a,lot,of,commas,to,test,delimiters`,
}
for n, c := range cases {
- delimiter := determineDelimiter(markup.NewRenderContext(context.Background()).WithRelativePath(c.filename), []byte(decodeSlashes(t, c.csv)))
- assert.EqualValues(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter)
+ delimiter := determineDelimiter(markup.NewRenderContext(t.Context()).WithRelativePath(c.filename), []byte(decodeSlashes(t, c.csv)))
+ assert.Equal(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter)
}
}
@@ -297,7 +296,7 @@ abc | |123
for n, c := range cases {
modifiedText := removeQuotedString(decodeSlashes(t, c.text))
- assert.EqualValues(t, c.expectedText, modifiedText, "case %d: modified text should be equal", n)
+ assert.Equal(t, c.expectedText, modifiedText, "case %d: modified text should be equal", n)
}
}
@@ -452,7 +451,7 @@ jkl`,
for n, c := range cases {
delimiter := guessDelimiter([]byte(decodeSlashes(t, c.csv)))
- assert.EqualValues(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter)
+ assert.Equal(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter)
}
}
@@ -544,7 +543,7 @@ a|"he said, ""here I am"""`,
for n, c := range cases {
delimiter := guessFromBeforeAfterQuotes([]byte(decodeSlashes(t, c.csv)))
- assert.EqualValues(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter)
+ assert.Equal(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter)
}
}
@@ -580,7 +579,7 @@ func TestFormatError(t *testing.T) {
assert.Error(t, err, "case %d: expected an error to be returned", n)
} else {
assert.NoError(t, err, "case %d: no error was expected, got error: %v", n, err)
- assert.EqualValues(t, c.expectedMessage, message, "case %d: messages should be equal, expected '%s' got '%s'", n, c.expectedMessage, message)
+ assert.Equal(t, c.expectedMessage, message, "case %d: messages should be equal, expected '%s' got '%s'", n, c.expectedMessage, message)
}
}
}
diff --git a/modules/dump/dumper_test.go b/modules/dump/dumper_test.go
index 2db3a598a4..8f06c1851d 100644
--- a/modules/dump/dumper_test.go
+++ b/modules/dump/dumper_test.go
@@ -103,11 +103,11 @@ func TestDumper(t *testing.T) {
d.GlobalExcludeAbsPath(filepath.Join(tmpDir, "include/exclude1"))
err := d.AddRecursiveExclude("include", filepath.Join(tmpDir, "include"), []string{filepath.Join(tmpDir, "include/exclude2")})
assert.NoError(t, err)
- assert.EqualValues(t, sortStrings([]string{"include/a", "include/sub", "include/sub/b"}), sortStrings(tw.added))
+ assert.Equal(t, sortStrings([]string{"include/a", "include/sub", "include/sub/b"}), sortStrings(tw.added))
tw = &testWriter{}
d = &Dumper{Writer: tw}
err = d.AddRecursiveExclude("include", filepath.Join(tmpDir, "include"), nil)
assert.NoError(t, err)
- assert.EqualValues(t, sortStrings([]string{"include/exclude2", "include/exclude2/a-2", "include/a", "include/sub", "include/sub/b", "include/exclude1", "include/exclude1/a-1"}), sortStrings(tw.added))
+ assert.Equal(t, sortStrings([]string{"include/exclude2", "include/exclude2/a-2", "include/a", "include/sub", "include/sub/b", "include/exclude1", "include/exclude1/a-1"}), sortStrings(tw.added))
}
diff --git a/modules/emoji/emoji_test.go b/modules/emoji/emoji_test.go
index 2526cd121e..fbf80fe41a 100644
--- a/modules/emoji/emoji_test.go
+++ b/modules/emoji/emoji_test.go
@@ -5,7 +5,6 @@
package emoji
import (
- "reflect"
"testing"
"github.com/stretchr/testify/assert"
@@ -22,32 +21,18 @@ func TestLookup(t *testing.T) {
c := FromAlias(":beer:")
d := FromAlias("beer")
- if !reflect.DeepEqual(a, b) {
- t.Errorf("a and b should equal")
- }
- if !reflect.DeepEqual(b, c) {
- t.Errorf("b and c should equal")
- }
- if !reflect.DeepEqual(c, d) {
- t.Errorf("c and d should equal")
- }
- if !reflect.DeepEqual(a, d) {
- t.Errorf("a and d should equal")
- }
+ assert.Equal(t, a, b)
+ assert.Equal(t, b, c)
+ assert.Equal(t, c, d)
+ assert.Equal(t, a, d)
m := FromCode("\U0001f44d")
n := FromAlias(":thumbsup:")
o := FromAlias("+1")
- if !reflect.DeepEqual(m, n) {
- t.Errorf("m and n should equal")
- }
- if !reflect.DeepEqual(n, o) {
- t.Errorf("n and o should equal")
- }
- if !reflect.DeepEqual(m, o) {
- t.Errorf("m and o should equal")
- }
+ assert.Equal(t, m, n)
+ assert.Equal(t, m, o)
+ assert.Equal(t, n, o)
}
func TestReplacers(t *testing.T) {
@@ -61,9 +46,7 @@ func TestReplacers(t *testing.T) {
for i, x := range tests {
s := x.f(x.v)
- if s != x.exp {
- t.Errorf("test %d `%s` expected `%s`, got: `%s`", i, x.v, x.exp, s)
- }
+ assert.Equalf(t, x.exp, s, "test %d `%s` expected `%s`, got: `%s`", i, x.v, x.exp, s)
}
}
diff --git a/modules/eventsource/event_test.go b/modules/eventsource/event_test.go
index 4c4272880d..a1c3e5c7a8 100644
--- a/modules/eventsource/event_test.go
+++ b/modules/eventsource/event_test.go
@@ -6,6 +6,9 @@ package eventsource
import (
"bytes"
"testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func Test_wrapNewlines(t *testing.T) {
@@ -38,16 +41,10 @@ func Test_wrapNewlines(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
w := &bytes.Buffer{}
gotSum, err := wrapNewlines(w, []byte(tt.prefix), []byte(tt.value))
- if err != nil {
- t.Errorf("wrapNewlines() error = %v", err)
- return
- }
- if gotSum != int64(len(tt.output)) {
- t.Errorf("wrapNewlines() = %v, want %v", gotSum, int64(len(tt.output)))
- }
- if gotW := w.String(); gotW != tt.output {
- t.Errorf("wrapNewlines() = %v, want %v", gotW, tt.output)
- }
+ require.NoError(t, err)
+
+ assert.EqualValues(t, len(tt.output), gotSum)
+ assert.Equal(t, tt.output, w.String())
})
}
}
diff --git a/modules/fileicon/basic.go b/modules/fileicon/basic.go
new file mode 100644
index 0000000000..9c513ccbd9
--- /dev/null
+++ b/modules/fileicon/basic.go
@@ -0,0 +1,31 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package fileicon
+
+import (
+ "html/template"
+
+ "code.gitea.io/gitea/modules/svg"
+ "code.gitea.io/gitea/modules/util"
+)
+
+func BasicEntryIconName(entry *EntryInfo) string {
+ svgName := "octicon-file"
+ switch {
+ case entry.EntryMode.IsLink():
+ svgName = "octicon-file-symlink-file"
+ if entry.SymlinkToMode.IsDir() {
+ svgName = "octicon-file-directory-symlink"
+ }
+ case entry.EntryMode.IsDir():
+ svgName = util.Iif(entry.IsOpen, "octicon-file-directory-open-fill", "octicon-file-directory-fill")
+ case entry.EntryMode.IsSubModule():
+ svgName = "octicon-file-submodule"
+ }
+ return svgName
+}
+
+func BasicEntryIconHTML(entry *EntryInfo) template.HTML {
+ return svg.RenderHTML(BasicEntryIconName(entry))
+}
diff --git a/modules/fileicon/entry.go b/modules/fileicon/entry.go
new file mode 100644
index 0000000000..0326c2bfa8
--- /dev/null
+++ b/modules/fileicon/entry.go
@@ -0,0 +1,31 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package fileicon
+
+import "code.gitea.io/gitea/modules/git"
+
+type EntryInfo struct {
+ BaseName string
+ EntryMode git.EntryMode
+ SymlinkToMode git.EntryMode
+ IsOpen bool
+}
+
+func EntryInfoFromGitTreeEntry(commit *git.Commit, fullPath string, gitEntry *git.TreeEntry) *EntryInfo {
+ ret := &EntryInfo{BaseName: gitEntry.Name(), EntryMode: gitEntry.Mode()}
+ if gitEntry.IsLink() {
+ if res, err := git.EntryFollowLink(commit, fullPath, gitEntry); err == nil && res.TargetEntry.IsDir() {
+ ret.SymlinkToMode = res.TargetEntry.Mode()
+ }
+ }
+ return ret
+}
+
+func EntryInfoFolder() *EntryInfo {
+ return &EntryInfo{EntryMode: git.EntryModeTree}
+}
+
+func EntryInfoFolderOpen() *EntryInfo {
+ return &EntryInfo{EntryMode: git.EntryModeTree, IsOpen: true}
+}
diff --git a/modules/fileicon/material.go b/modules/fileicon/material.go
new file mode 100644
index 0000000000..5361592d8a
--- /dev/null
+++ b/modules/fileicon/material.go
@@ -0,0 +1,162 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package fileicon
+
+import (
+ "html/template"
+ "strings"
+ "sync"
+
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/options"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/svg"
+ "code.gitea.io/gitea/modules/util"
+)
+
+type materialIconRulesData struct {
+ FileNames map[string]string `json:"fileNames"`
+ FolderNames map[string]string `json:"folderNames"`
+ FileExtensions map[string]string `json:"fileExtensions"`
+ LanguageIDs map[string]string `json:"languageIds"`
+}
+
+type MaterialIconProvider struct {
+ once sync.Once
+ rules *materialIconRulesData
+ svgs map[string]string
+}
+
+var materialIconProvider MaterialIconProvider
+
+func DefaultMaterialIconProvider() *MaterialIconProvider {
+ materialIconProvider.once.Do(materialIconProvider.loadData)
+ return &materialIconProvider
+}
+
+func (m *MaterialIconProvider) loadData() {
+ buf, err := options.AssetFS().ReadFile("fileicon/material-icon-rules.json")
+ if err != nil {
+ log.Error("Failed to read material icon rules: %v", err)
+ return
+ }
+ err = json.Unmarshal(buf, &m.rules)
+ if err != nil {
+ log.Error("Failed to unmarshal material icon rules: %v", err)
+ return
+ }
+
+ buf, err = options.AssetFS().ReadFile("fileicon/material-icon-svgs.json")
+ if err != nil {
+ log.Error("Failed to read material icon rules: %v", err)
+ return
+ }
+ err = json.Unmarshal(buf, &m.svgs)
+ if err != nil {
+ log.Error("Failed to unmarshal material icon rules: %v", err)
+ return
+ }
+ log.Debug("Loaded material icon rules and SVG images")
+}
+
+func (m *MaterialIconProvider) renderFileIconSVG(p *RenderedIconPool, name, svg, extraClass string) template.HTML {
+ // This part is a bit hacky, but it works really well. It should be safe to do so because all SVG icons are generated by us.
+ // Will try to refactor this in the future.
+ if !strings.HasPrefix(svg, "<svg") {
+ panic("Invalid SVG icon")
+ }
+ svgID := "svg-mfi-" + name
+ svgCommonAttrs := `class="svg git-entry-icon ` + extraClass + `" width="16" height="16" aria-hidden="true"`
+ svgHTML := template.HTML(`<svg id="` + svgID + `" ` + svgCommonAttrs + svg[4:])
+ if p == nil {
+ return svgHTML
+ }
+ if p.IconSVGs[svgID] == "" {
+ p.IconSVGs[svgID] = svgHTML
+ }
+ return template.HTML(`<svg ` + svgCommonAttrs + `><use xlink:href="#` + svgID + `"></use></svg>`)
+}
+
+func (m *MaterialIconProvider) EntryIconHTML(p *RenderedIconPool, entry *EntryInfo) template.HTML {
+ if m.rules == nil {
+ return BasicEntryIconHTML(entry)
+ }
+
+ if entry.EntryMode.IsLink() {
+ if entry.SymlinkToMode.IsDir() {
+ // keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
+ return svg.RenderHTML("material-folder-symlink", 16, "octicon-file-directory-symlink")
+ }
+ return svg.RenderHTML("octicon-file-symlink-file") // TODO: find some better icons for them
+ }
+
+ name := m.FindIconName(entry)
+ iconSVG := m.svgs[name]
+ if iconSVG == "" {
+ name = "file"
+ if entry.EntryMode.IsDir() {
+ name = util.Iif(entry.IsOpen, "folder-open", "folder")
+ }
+ iconSVG = m.svgs[name]
+ if iconSVG == "" {
+ setting.PanicInDevOrTesting("missing file icon for %s", name)
+ }
+ }
+
+ // keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
+ extraClass := "octicon-file"
+ switch {
+ case entry.EntryMode.IsDir():
+ extraClass = BasicEntryIconName(entry)
+ case entry.EntryMode.IsSubModule():
+ extraClass = "octicon-file-submodule"
+ }
+ return m.renderFileIconSVG(p, name, iconSVG, extraClass)
+}
+
+func (m *MaterialIconProvider) findIconNameWithLangID(s string) string {
+ if _, ok := m.svgs[s]; ok {
+ return s
+ }
+ if s, ok := m.rules.LanguageIDs[s]; ok {
+ if _, ok = m.svgs[s]; ok {
+ return s
+ }
+ }
+ return ""
+}
+
+func (m *MaterialIconProvider) FindIconName(entry *EntryInfo) string {
+ if entry.EntryMode.IsSubModule() {
+ return "folder-git"
+ }
+
+ fileNameLower := strings.ToLower(entry.BaseName)
+ if entry.EntryMode.IsDir() {
+ if s, ok := m.rules.FolderNames[fileNameLower]; ok {
+ return s
+ }
+ return util.Iif(entry.IsOpen, "folder-open", "folder")
+ }
+
+ if s, ok := m.rules.FileNames[fileNameLower]; ok {
+ if s = m.findIconNameWithLangID(s); s != "" {
+ return s
+ }
+ }
+
+ for i := len(fileNameLower) - 1; i >= 0; i-- {
+ if fileNameLower[i] == '.' {
+ ext := fileNameLower[i+1:]
+ if s, ok := m.rules.FileExtensions[ext]; ok {
+ if s = m.findIconNameWithLangID(s); s != "" {
+ return s
+ }
+ }
+ }
+ }
+
+ return "file"
+}
diff --git a/modules/fileicon/material_test.go b/modules/fileicon/material_test.go
new file mode 100644
index 0000000000..d2a769eaac
--- /dev/null
+++ b/modules/fileicon/material_test.go
@@ -0,0 +1,27 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package fileicon_test
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/fileicon"
+ "code.gitea.io/gitea/modules/git"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMain(m *testing.M) {
+ unittest.MainTest(m, &unittest.TestOptions{FixtureFiles: []string{}})
+}
+
+func TestFindIconName(t *testing.T) {
+ unittest.PrepareTestEnv(t)
+ p := fileicon.DefaultMaterialIconProvider()
+ assert.Equal(t, "php", p.FindIconName(&fileicon.EntryInfo{BaseName: "foo.php", EntryMode: git.EntryModeBlob}))
+ assert.Equal(t, "php", p.FindIconName(&fileicon.EntryInfo{BaseName: "foo.PHP", EntryMode: git.EntryModeBlob}))
+ assert.Equal(t, "javascript", p.FindIconName(&fileicon.EntryInfo{BaseName: "foo.js", EntryMode: git.EntryModeBlob}))
+ assert.Equal(t, "visualstudio", p.FindIconName(&fileicon.EntryInfo{BaseName: "foo.vba", EntryMode: git.EntryModeBlob}))
+}
diff --git a/modules/fileicon/render.go b/modules/fileicon/render.go
new file mode 100644
index 0000000000..8ed86b9ac0
--- /dev/null
+++ b/modules/fileicon/render.go
@@ -0,0 +1,41 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package fileicon
+
+import (
+ "html/template"
+ "strings"
+
+ "code.gitea.io/gitea/modules/setting"
+)
+
+type RenderedIconPool struct {
+ IconSVGs map[string]template.HTML
+}
+
+func NewRenderedIconPool() *RenderedIconPool {
+ return &RenderedIconPool{
+ IconSVGs: make(map[string]template.HTML),
+ }
+}
+
+func (p *RenderedIconPool) RenderToHTML() template.HTML {
+ if len(p.IconSVGs) == 0 {
+ return ""
+ }
+ sb := &strings.Builder{}
+ sb.WriteString(`<div class=tw-hidden>`)
+ for _, icon := range p.IconSVGs {
+ sb.WriteString(string(icon))
+ }
+ sb.WriteString(`</div>`)
+ return template.HTML(sb.String())
+}
+
+func RenderEntryIconHTML(renderedIconPool *RenderedIconPool, entry *EntryInfo) template.HTML {
+ if setting.UI.FileIconTheme == "material" {
+ return DefaultMaterialIconProvider().EntryIconHTML(renderedIconPool, entry)
+ }
+ return BasicEntryIconHTML(entry)
+}
diff --git a/modules/git/attribute.go b/modules/git/attribute.go
deleted file mode 100644
index 4dfa510369..0000000000
--- a/modules/git/attribute.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2024 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package git
-
-import (
- "code.gitea.io/gitea/modules/optional"
-)
-
-const (
- AttributeLinguistVendored = "linguist-vendored"
- AttributeLinguistGenerated = "linguist-generated"
- AttributeLinguistDocumentation = "linguist-documentation"
- AttributeLinguistDetectable = "linguist-detectable"
- AttributeLinguistLanguage = "linguist-language"
- AttributeGitlabLanguage = "gitlab-language"
-)
-
-// true if "set"/"true", false if "unset"/"false", none otherwise
-func AttributeToBool(attr map[string]string, name string) optional.Option[bool] {
- switch attr[name] {
- case "set", "true":
- return optional.Some(true)
- case "unset", "false":
- return optional.Some(false)
- }
- return optional.None[bool]()
-}
-
-func AttributeToString(attr map[string]string, name string) optional.Option[string] {
- if value, has := attr[name]; has && value != "unspecified" {
- return optional.Some(value)
- }
- return optional.None[string]()
-}
diff --git a/modules/git/attribute/attribute.go b/modules/git/attribute/attribute.go
new file mode 100644
index 0000000000..adf323ef41
--- /dev/null
+++ b/modules/git/attribute/attribute.go
@@ -0,0 +1,114 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package attribute
+
+import (
+ "strings"
+
+ "code.gitea.io/gitea/modules/optional"
+)
+
+type Attribute string
+
+const (
+ LinguistVendored = "linguist-vendored"
+ LinguistGenerated = "linguist-generated"
+ LinguistDocumentation = "linguist-documentation"
+ LinguistDetectable = "linguist-detectable"
+ LinguistLanguage = "linguist-language"
+ GitlabLanguage = "gitlab-language"
+ Lockable = "lockable"
+ Filter = "filter"
+)
+
+var LinguistAttributes = []string{
+ LinguistVendored,
+ LinguistGenerated,
+ LinguistDocumentation,
+ LinguistDetectable,
+ LinguistLanguage,
+ GitlabLanguage,
+}
+
+func (a Attribute) IsUnspecified() bool {
+ return a == "" || a == "unspecified"
+}
+
+func (a Attribute) ToString() optional.Option[string] {
+ if !a.IsUnspecified() {
+ return optional.Some(string(a))
+ }
+ return optional.None[string]()
+}
+
+// ToBool converts the attribute value to optional boolean: true if "set"/"true", false if "unset"/"false", none otherwise
+func (a Attribute) ToBool() optional.Option[bool] {
+ switch a {
+ case "set", "true":
+ return optional.Some(true)
+ case "unset", "false":
+ return optional.Some(false)
+ }
+ return optional.None[bool]()
+}
+
+type Attributes struct {
+ m map[string]Attribute
+}
+
+func NewAttributes() *Attributes {
+ return &Attributes{m: make(map[string]Attribute)}
+}
+
+func (attrs *Attributes) Get(name string) Attribute {
+ if value, has := attrs.m[name]; has {
+ return value
+ }
+ return ""
+}
+
+func (attrs *Attributes) GetVendored() optional.Option[bool] {
+ return attrs.Get(LinguistVendored).ToBool()
+}
+
+func (attrs *Attributes) GetGenerated() optional.Option[bool] {
+ return attrs.Get(LinguistGenerated).ToBool()
+}
+
+func (attrs *Attributes) GetDocumentation() optional.Option[bool] {
+ return attrs.Get(LinguistDocumentation).ToBool()
+}
+
+func (attrs *Attributes) GetDetectable() optional.Option[bool] {
+ return attrs.Get(LinguistDetectable).ToBool()
+}
+
+func (attrs *Attributes) GetLinguistLanguage() optional.Option[string] {
+ return attrs.Get(LinguistLanguage).ToString()
+}
+
+func (attrs *Attributes) GetGitlabLanguage() optional.Option[string] {
+ attrStr := attrs.Get(GitlabLanguage).ToString()
+ if attrStr.Has() {
+ raw := attrStr.Value()
+ // gitlab-language may have additional parameters after the language
+ // ignore them and just use the main language
+ // https://docs.gitlab.com/ee/user/project/highlighting.html#override-syntax-highlighting-for-a-file-type
+ if idx := strings.IndexByte(raw, '?'); idx >= 0 {
+ return optional.Some(raw[:idx])
+ }
+ }
+ return attrStr
+}
+
+func (attrs *Attributes) GetLanguage() optional.Option[string] {
+ // prefer linguist-language over gitlab-language
+ // if linguist-language is not set, use gitlab-language
+ // if both are not set, return none
+ language := attrs.GetLinguistLanguage()
+ if language.Value() == "" {
+ language = attrs.GetGitlabLanguage()
+ }
+ return language
+}
diff --git a/modules/git/attribute/attribute_test.go b/modules/git/attribute/attribute_test.go
new file mode 100644
index 0000000000..dadb5582a3
--- /dev/null
+++ b/modules/git/attribute/attribute_test.go
@@ -0,0 +1,37 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package attribute
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_Attribute(t *testing.T) {
+ assert.Empty(t, Attribute("").ToString().Value())
+ assert.Empty(t, Attribute("unspecified").ToString().Value())
+ assert.Equal(t, "python", Attribute("python").ToString().Value())
+ assert.Equal(t, "Java", Attribute("Java").ToString().Value())
+
+ attributes := Attributes{
+ m: map[string]Attribute{
+ LinguistGenerated: "true",
+ LinguistDocumentation: "false",
+ LinguistDetectable: "set",
+ LinguistLanguage: "Python",
+ GitlabLanguage: "Java",
+ "filter": "unspecified",
+ "test": "",
+ },
+ }
+
+ assert.Empty(t, attributes.Get("test").ToString().Value())
+ assert.Empty(t, attributes.Get("filter").ToString().Value())
+ assert.Equal(t, "Python", attributes.Get(LinguistLanguage).ToString().Value())
+ assert.Equal(t, "Java", attributes.Get(GitlabLanguage).ToString().Value())
+ assert.True(t, attributes.Get(LinguistGenerated).ToBool().Value())
+ assert.False(t, attributes.Get(LinguistDocumentation).ToBool().Value())
+ assert.True(t, attributes.Get(LinguistDetectable).ToBool().Value())
+}
diff --git a/modules/git/attribute/batch.go b/modules/git/attribute/batch.go
new file mode 100644
index 0000000000..4e31fda575
--- /dev/null
+++ b/modules/git/attribute/batch.go
@@ -0,0 +1,216 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package attribute
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "os"
+ "path/filepath"
+ "time"
+
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/log"
+)
+
+// BatchChecker provides a reader for check-attribute content that can be long running
+type BatchChecker struct {
+ attributesNum int
+ repo *git.Repository
+ stdinWriter *os.File
+ stdOut *nulSeparatedAttributeWriter
+ ctx context.Context
+ cancel context.CancelFunc
+ cmd *git.Command
+}
+
+// NewBatchChecker creates a check attribute reader for the current repository and provided commit ID
+// If treeish is empty, then it will use current working directory, otherwise it will use the provided treeish on the bare repo
+func NewBatchChecker(repo *git.Repository, treeish string, attributes []string) (checker *BatchChecker, returnedErr error) {
+ ctx, cancel := context.WithCancel(repo.Ctx)
+ defer func() {
+ if returnedErr != nil {
+ cancel()
+ }
+ }()
+
+ cmd, envs, cleanup, err := checkAttrCommand(repo, treeish, nil, attributes)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ if returnedErr != nil {
+ cleanup()
+ }
+ }()
+
+ cmd.AddArguments("--stdin")
+
+ checker = &BatchChecker{
+ attributesNum: len(attributes),
+ repo: repo,
+ ctx: ctx,
+ cmd: cmd,
+ cancel: func() {
+ cancel()
+ cleanup()
+ },
+ }
+
+ stdinReader, stdinWriter, err := os.Pipe()
+ if err != nil {
+ return nil, err
+ }
+ checker.stdinWriter = stdinWriter
+
+ lw := new(nulSeparatedAttributeWriter)
+ lw.attributes = make(chan attributeTriple, len(attributes))
+ lw.closed = make(chan struct{})
+ checker.stdOut = lw
+
+ go func() {
+ defer func() {
+ _ = stdinReader.Close()
+ _ = lw.Close()
+ }()
+ stdErr := new(bytes.Buffer)
+ err := cmd.Run(ctx, &git.RunOpts{
+ Env: envs,
+ Dir: repo.Path,
+ Stdin: stdinReader,
+ Stdout: lw,
+ Stderr: stdErr,
+ })
+
+ if err != nil && !git.IsErrCanceledOrKilled(err) {
+ log.Error("Attribute checker for commit %s exits with error: %v", treeish, err)
+ }
+ checker.cancel()
+ }()
+
+ return checker, nil
+}
+
+// CheckPath check attr for given path
+func (c *BatchChecker) CheckPath(path string) (rs *Attributes, err error) {
+ defer func() {
+ if err != nil && err != c.ctx.Err() {
+ log.Error("Unexpected error when checking path %s in %s, error: %v", path, filepath.Base(c.repo.Path), err)
+ }
+ }()
+
+ select {
+ case <-c.ctx.Done():
+ return nil, c.ctx.Err()
+ default:
+ }
+
+ if _, err = c.stdinWriter.Write([]byte(path + "\x00")); err != nil {
+ defer c.Close()
+ return nil, err
+ }
+
+ reportTimeout := func() error {
+ stdOutClosed := false
+ select {
+ case <-c.stdOut.closed:
+ stdOutClosed = true
+ default:
+ }
+ debugMsg := fmt.Sprintf("check path %q in repo %q", path, filepath.Base(c.repo.Path))
+ debugMsg += fmt.Sprintf(", stdOut: tmp=%q, pos=%d, closed=%v", string(c.stdOut.tmp), c.stdOut.pos, stdOutClosed)
+ if c.cmd != nil {
+ debugMsg += fmt.Sprintf(", process state: %q", c.cmd.ProcessState())
+ }
+ _ = c.Close()
+ return fmt.Errorf("CheckPath timeout: %s", debugMsg)
+ }
+
+ rs = NewAttributes()
+ for i := 0; i < c.attributesNum; i++ {
+ select {
+ case <-time.After(5 * time.Second):
+ // there is no "hang" problem now. This code is just used to catch other potential problems.
+ return nil, reportTimeout()
+ case attr, ok := <-c.stdOut.ReadAttribute():
+ if !ok {
+ return nil, c.ctx.Err()
+ }
+ rs.m[attr.Attribute] = Attribute(attr.Value)
+ case <-c.ctx.Done():
+ return nil, c.ctx.Err()
+ }
+ }
+ return rs, nil
+}
+
+func (c *BatchChecker) Close() error {
+ c.cancel()
+ err := c.stdinWriter.Close()
+ return err
+}
+
+type attributeTriple struct {
+ Filename string
+ Attribute string
+ Value string
+}
+
+type nulSeparatedAttributeWriter struct {
+ tmp []byte
+ attributes chan attributeTriple
+ closed chan struct{}
+ working attributeTriple
+ pos int
+}
+
+func (wr *nulSeparatedAttributeWriter) Write(p []byte) (n int, err error) {
+ l, read := len(p), 0
+
+ nulIdx := bytes.IndexByte(p, '\x00')
+ for nulIdx >= 0 {
+ wr.tmp = append(wr.tmp, p[:nulIdx]...)
+ switch wr.pos {
+ case 0:
+ wr.working = attributeTriple{
+ Filename: string(wr.tmp),
+ }
+ case 1:
+ wr.working.Attribute = string(wr.tmp)
+ case 2:
+ wr.working.Value = string(wr.tmp)
+ }
+ wr.tmp = wr.tmp[:0]
+ wr.pos++
+ if wr.pos > 2 {
+ wr.attributes <- wr.working
+ wr.pos = 0
+ }
+ read += nulIdx + 1
+ if l > read {
+ p = p[nulIdx+1:]
+ nulIdx = bytes.IndexByte(p, '\x00')
+ } else {
+ return l, nil
+ }
+ }
+ wr.tmp = append(wr.tmp, p...)
+ return l, nil
+}
+
+func (wr *nulSeparatedAttributeWriter) ReadAttribute() <-chan attributeTriple {
+ return wr.attributes
+}
+
+func (wr *nulSeparatedAttributeWriter) Close() error {
+ select {
+ case <-wr.closed:
+ return nil
+ default:
+ }
+ close(wr.attributes)
+ close(wr.closed)
+ return nil
+}
diff --git a/modules/git/attribute/batch_test.go b/modules/git/attribute/batch_test.go
new file mode 100644
index 0000000000..30a3d805fe
--- /dev/null
+++ b/modules/git/attribute/batch_test.go
@@ -0,0 +1,172 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package attribute
+
+import (
+ "path/filepath"
+ "testing"
+ "time"
+
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) {
+ wr := &nulSeparatedAttributeWriter{
+ attributes: make(chan attributeTriple, 5),
+ }
+
+ testStr := ".gitignore\"\n\x00linguist-vendored\x00unspecified\x00"
+
+ n, err := wr.Write([]byte(testStr))
+
+ assert.Len(t, testStr, n)
+ assert.NoError(t, err)
+ select {
+ case attr := <-wr.ReadAttribute():
+ assert.Equal(t, ".gitignore\"\n", attr.Filename)
+ assert.Equal(t, LinguistVendored, attr.Attribute)
+ assert.Equal(t, "unspecified", attr.Value)
+ case <-time.After(100 * time.Millisecond):
+ assert.FailNow(t, "took too long to read an attribute from the list")
+ }
+ // Write a second attribute again
+ n, err = wr.Write([]byte(testStr))
+
+ assert.Len(t, testStr, n)
+ assert.NoError(t, err)
+
+ select {
+ case attr := <-wr.ReadAttribute():
+ assert.Equal(t, ".gitignore\"\n", attr.Filename)
+ assert.Equal(t, LinguistVendored, attr.Attribute)
+ assert.Equal(t, "unspecified", attr.Value)
+ case <-time.After(100 * time.Millisecond):
+ assert.FailNow(t, "took too long to read an attribute from the list")
+ }
+
+ // Write a partial attribute
+ _, err = wr.Write([]byte("incomplete-file"))
+ assert.NoError(t, err)
+ _, err = wr.Write([]byte("name\x00"))
+ assert.NoError(t, err)
+
+ select {
+ case <-wr.ReadAttribute():
+ assert.FailNow(t, "There should not be an attribute ready to read")
+ case <-time.After(100 * time.Millisecond):
+ }
+ _, err = wr.Write([]byte("attribute\x00"))
+ assert.NoError(t, err)
+ select {
+ case <-wr.ReadAttribute():
+ assert.FailNow(t, "There should not be an attribute ready to read")
+ case <-time.After(100 * time.Millisecond):
+ }
+
+ _, err = wr.Write([]byte("value\x00"))
+ assert.NoError(t, err)
+
+ attr := <-wr.ReadAttribute()
+ assert.Equal(t, "incomplete-filename", attr.Filename)
+ assert.Equal(t, "attribute", attr.Attribute)
+ assert.Equal(t, "value", attr.Value)
+
+ _, err = wr.Write([]byte("shouldbe.vendor\x00linguist-vendored\x00set\x00shouldbe.vendor\x00linguist-generated\x00unspecified\x00shouldbe.vendor\x00linguist-language\x00unspecified\x00"))
+ assert.NoError(t, err)
+ attr = <-wr.ReadAttribute()
+ assert.NoError(t, err)
+ assert.Equal(t, attributeTriple{
+ Filename: "shouldbe.vendor",
+ Attribute: LinguistVendored,
+ Value: "set",
+ }, attr)
+ attr = <-wr.ReadAttribute()
+ assert.NoError(t, err)
+ assert.Equal(t, attributeTriple{
+ Filename: "shouldbe.vendor",
+ Attribute: LinguistGenerated,
+ Value: "unspecified",
+ }, attr)
+ attr = <-wr.ReadAttribute()
+ assert.NoError(t, err)
+ assert.Equal(t, attributeTriple{
+ Filename: "shouldbe.vendor",
+ Attribute: LinguistLanguage,
+ Value: "unspecified",
+ }, attr)
+}
+
+func expectedAttrs() *Attributes {
+ return &Attributes{
+ m: map[string]Attribute{
+ LinguistGenerated: "unspecified",
+ LinguistDetectable: "unspecified",
+ LinguistDocumentation: "unspecified",
+ LinguistVendored: "unspecified",
+ LinguistLanguage: "Python",
+ GitlabLanguage: "unspecified",
+ },
+ }
+}
+
+func Test_BatchChecker(t *testing.T) {
+ setting.AppDataPath = t.TempDir()
+ repoPath := "../tests/repos/language_stats_repo"
+ gitRepo, err := git.OpenRepository(t.Context(), repoPath)
+ require.NoError(t, err)
+ defer gitRepo.Close()
+
+ commitID := "8fee858da5796dfb37704761701bb8e800ad9ef3"
+
+ t.Run("Create index file to run git check-attr", func(t *testing.T) {
+ defer test.MockVariableValue(&git.DefaultFeatures().SupportCheckAttrOnBare, false)()
+ checker, err := NewBatchChecker(gitRepo, commitID, LinguistAttributes)
+ assert.NoError(t, err)
+ defer checker.Close()
+ attributes, err := checker.CheckPath("i-am-a-python.p")
+ assert.NoError(t, err)
+ assert.Equal(t, expectedAttrs(), attributes)
+ })
+
+ // run git check-attr on work tree
+ t.Run("Run git check-attr on git work tree", func(t *testing.T) {
+ dir := filepath.Join(t.TempDir(), "test-repo")
+ err := git.Clone(t.Context(), repoPath, dir, git.CloneRepoOptions{
+ Shared: true,
+ Branch: "master",
+ })
+ assert.NoError(t, err)
+
+ tempRepo, err := git.OpenRepository(t.Context(), dir)
+ assert.NoError(t, err)
+ defer tempRepo.Close()
+
+ checker, err := NewBatchChecker(tempRepo, "", LinguistAttributes)
+ assert.NoError(t, err)
+ defer checker.Close()
+ attributes, err := checker.CheckPath("i-am-a-python.p")
+ assert.NoError(t, err)
+ assert.Equal(t, expectedAttrs(), attributes)
+ })
+
+ if !git.DefaultFeatures().SupportCheckAttrOnBare {
+ t.Skip("git version 2.40 is required to support run check-attr on bare repo")
+ return
+ }
+
+ t.Run("Run git check-attr in bare repository", func(t *testing.T) {
+ checker, err := NewBatchChecker(gitRepo, commitID, LinguistAttributes)
+ assert.NoError(t, err)
+ defer checker.Close()
+
+ attributes, err := checker.CheckPath("i-am-a-python.p")
+ assert.NoError(t, err)
+ assert.Equal(t, expectedAttrs(), attributes)
+ })
+}
diff --git a/modules/git/attribute/checker.go b/modules/git/attribute/checker.go
new file mode 100644
index 0000000000..167b31416e
--- /dev/null
+++ b/modules/git/attribute/checker.go
@@ -0,0 +1,101 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package attribute
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "os"
+
+ "code.gitea.io/gitea/modules/git"
+)
+
+func checkAttrCommand(gitRepo *git.Repository, treeish string, filenames, attributes []string) (*git.Command, []string, func(), error) {
+ cancel := func() {}
+ envs := []string{"GIT_FLUSH=1"}
+ cmd := git.NewCommand("check-attr", "-z")
+ if len(attributes) == 0 {
+ cmd.AddArguments("--all")
+ }
+
+ // there is treeish, read from bare repo or temp index created by "read-tree"
+ if treeish != "" {
+ if git.DefaultFeatures().SupportCheckAttrOnBare {
+ cmd.AddArguments("--source")
+ cmd.AddDynamicArguments(treeish)
+ } else {
+ indexFilename, worktree, deleteTemporaryFile, err := gitRepo.ReadTreeToTemporaryIndex(treeish)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ cmd.AddArguments("--cached")
+ envs = append(envs,
+ "GIT_INDEX_FILE="+indexFilename,
+ "GIT_WORK_TREE="+worktree,
+ )
+ cancel = deleteTemporaryFile
+ }
+ } else {
+ // Read from existing index, in cases where the repo is bare and has an index,
+ // or the work tree contains unstaged changes that shouldn't affect the attribute check.
+ // It is caller's responsibility to add changed ".gitattributes" into the index if they want to respect the new changes.
+ cmd.AddArguments("--cached")
+ }
+
+ cmd.AddDynamicArguments(attributes...)
+ if len(filenames) > 0 {
+ cmd.AddDashesAndList(filenames...)
+ }
+ return cmd, envs, cancel, nil
+}
+
+type CheckAttributeOpts struct {
+ Filenames []string
+ Attributes []string
+}
+
+// CheckAttributes return the attributes of the given filenames and attributes in the given treeish.
+// If treeish is empty, then it will use current working directory, otherwise it will use the provided treeish on the bare repo
+func CheckAttributes(ctx context.Context, gitRepo *git.Repository, treeish string, opts CheckAttributeOpts) (map[string]*Attributes, error) {
+ cmd, envs, cancel, err := checkAttrCommand(gitRepo, treeish, opts.Filenames, opts.Attributes)
+ if err != nil {
+ return nil, err
+ }
+ defer cancel()
+
+ stdOut := new(bytes.Buffer)
+ stdErr := new(bytes.Buffer)
+
+ if err := cmd.Run(ctx, &git.RunOpts{
+ Env: append(os.Environ(), envs...),
+ Dir: gitRepo.Path,
+ Stdout: stdOut,
+ Stderr: stdErr,
+ }); err != nil {
+ return nil, fmt.Errorf("failed to run check-attr: %w\n%s\n%s", err, stdOut.String(), stdErr.String())
+ }
+
+ fields := bytes.Split(stdOut.Bytes(), []byte{'\000'})
+ if len(fields)%3 != 1 {
+ return nil, errors.New("wrong number of fields in return from check-attr")
+ }
+
+ attributesMap := make(map[string]*Attributes)
+ for i := 0; i < (len(fields) / 3); i++ {
+ filename := string(fields[3*i])
+ attribute := string(fields[3*i+1])
+ info := string(fields[3*i+2])
+ attribute2info, ok := attributesMap[filename]
+ if !ok {
+ attribute2info = NewAttributes()
+ attributesMap[filename] = attribute2info
+ }
+ attribute2info.m[attribute] = Attribute(info)
+ }
+
+ return attributesMap, nil
+}
diff --git a/modules/git/attribute/checker_test.go b/modules/git/attribute/checker_test.go
new file mode 100644
index 0000000000..67fbda8918
--- /dev/null
+++ b/modules/git/attribute/checker_test.go
@@ -0,0 +1,84 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package attribute
+
+import (
+ "path/filepath"
+ "testing"
+
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func Test_Checker(t *testing.T) {
+ setting.AppDataPath = t.TempDir()
+ repoPath := "../tests/repos/language_stats_repo"
+ gitRepo, err := git.OpenRepository(t.Context(), repoPath)
+ require.NoError(t, err)
+ defer gitRepo.Close()
+
+ commitID := "8fee858da5796dfb37704761701bb8e800ad9ef3"
+
+ t.Run("Create index file to run git check-attr", func(t *testing.T) {
+ defer test.MockVariableValue(&git.DefaultFeatures().SupportCheckAttrOnBare, false)()
+ attrs, err := CheckAttributes(t.Context(), gitRepo, commitID, CheckAttributeOpts{
+ Filenames: []string{"i-am-a-python.p"},
+ Attributes: LinguistAttributes,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, attrs, 1)
+ assert.Equal(t, expectedAttrs(), attrs["i-am-a-python.p"])
+ })
+
+ // run git check-attr on work tree
+ t.Run("Run git check-attr on git work tree", func(t *testing.T) {
+ dir := filepath.Join(t.TempDir(), "test-repo")
+ err := git.Clone(t.Context(), repoPath, dir, git.CloneRepoOptions{
+ Shared: true,
+ Branch: "master",
+ })
+ assert.NoError(t, err)
+
+ tempRepo, err := git.OpenRepository(t.Context(), dir)
+ assert.NoError(t, err)
+ defer tempRepo.Close()
+
+ attrs, err := CheckAttributes(t.Context(), tempRepo, "", CheckAttributeOpts{
+ Filenames: []string{"i-am-a-python.p"},
+ Attributes: LinguistAttributes,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, attrs, 1)
+ assert.Equal(t, expectedAttrs(), attrs["i-am-a-python.p"])
+ })
+
+ t.Run("Run git check-attr in bare repository using index", func(t *testing.T) {
+ attrs, err := CheckAttributes(t.Context(), gitRepo, "", CheckAttributeOpts{
+ Filenames: []string{"i-am-a-python.p"},
+ Attributes: LinguistAttributes,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, attrs, 1)
+ assert.Equal(t, expectedAttrs(), attrs["i-am-a-python.p"])
+ })
+
+ if !git.DefaultFeatures().SupportCheckAttrOnBare {
+ t.Skip("git version 2.40 is required to support run check-attr on bare repo without using index")
+ return
+ }
+
+ t.Run("Run git check-attr in bare repository", func(t *testing.T) {
+ attrs, err := CheckAttributes(t.Context(), gitRepo, commitID, CheckAttributeOpts{
+ Filenames: []string{"i-am-a-python.p"},
+ Attributes: LinguistAttributes,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, attrs, 1)
+ assert.Equal(t, expectedAttrs(), attrs["i-am-a-python.p"])
+ })
+}
diff --git a/modules/git/attribute/main_test.go b/modules/git/attribute/main_test.go
new file mode 100644
index 0000000000..df8241bfb0
--- /dev/null
+++ b/modules/git/attribute/main_test.go
@@ -0,0 +1,41 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package attribute
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "testing"
+
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+)
+
+func testRun(m *testing.M) error {
+ gitHomePath, err := os.MkdirTemp(os.TempDir(), "git-home")
+ if err != nil {
+ return fmt.Errorf("unable to create temp dir: %w", err)
+ }
+ defer util.RemoveAll(gitHomePath)
+ setting.Git.HomePath = gitHomePath
+
+ if err = git.InitFull(context.Background()); err != nil {
+ return fmt.Errorf("failed to call Init: %w", err)
+ }
+
+ exitCode := m.Run()
+ if exitCode != 0 {
+ return fmt.Errorf("run test failed, ExitCode=%d", exitCode)
+ }
+ return nil
+}
+
+func TestMain(m *testing.M) {
+ if err := testRun(m); err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Test failed: %v", err)
+ os.Exit(1)
+ }
+}
diff --git a/modules/git/batch.go b/modules/git/batch.go
index 3ec4f1ddcc..f9e1748b54 100644
--- a/modules/git/batch.go
+++ b/modules/git/batch.go
@@ -14,25 +14,26 @@ type Batch struct {
Writer WriteCloserError
}
-func (repo *Repository) NewBatch(ctx context.Context) (*Batch, error) {
+// NewBatch creates a new batch for the given repository, the Close must be invoked before release the batch
+func NewBatch(ctx context.Context, repoPath string) (*Batch, error) {
// Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first!
- if err := ensureValidGitRepository(ctx, repo.Path); err != nil {
+ if err := ensureValidGitRepository(ctx, repoPath); err != nil {
return nil, err
}
var batch Batch
- batch.Writer, batch.Reader, batch.cancel = catFileBatch(ctx, repo.Path)
+ batch.Writer, batch.Reader, batch.cancel = catFileBatch(ctx, repoPath)
return &batch, nil
}
-func (repo *Repository) NewBatchCheck(ctx context.Context) (*Batch, error) {
+func NewBatchCheck(ctx context.Context, repoPath string) (*Batch, error) {
// Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first!
- if err := ensureValidGitRepository(ctx, repo.Path); err != nil {
+ if err := ensureValidGitRepository(ctx, repoPath); err != nil {
return nil, err
}
var check Batch
- check.Writer, check.Reader, check.cancel = catFileBatchCheck(ctx, repo.Path)
+ check.Writer, check.Reader, check.cancel = catFileBatchCheck(ctx, repoPath)
return &check, nil
}
diff --git a/modules/git/batch_reader.go b/modules/git/batch_reader.go
index 33e54fe75c..7bbab76bb8 100644
--- a/modules/git/batch_reader.go
+++ b/modules/git/batch_reader.go
@@ -29,8 +29,8 @@ type WriteCloserError interface {
// This is needed otherwise the git cat-file will hang for invalid repositories.
func ensureValidGitRepository(ctx context.Context, repoPath string) error {
stderr := strings.Builder{}
- err := NewCommand(ctx, "rev-parse").
- Run(&RunOpts{
+ err := NewCommand("rev-parse").
+ Run(ctx, &RunOpts{
Dir: repoPath,
Stderr: &stderr,
})
@@ -61,8 +61,8 @@ func catFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError,
go func() {
stderr := strings.Builder{}
- err := NewCommand(ctx, "cat-file", "--batch-check").
- Run(&RunOpts{
+ err := NewCommand("cat-file", "--batch-check").
+ Run(ctx, &RunOpts{
Dir: repoPath,
Stdin: batchStdinReader,
Stdout: batchStdoutWriter,
@@ -109,8 +109,8 @@ func catFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
go func() {
stderr := strings.Builder{}
- err := NewCommand(ctx, "cat-file", "--batch").
- Run(&RunOpts{
+ err := NewCommand("cat-file", "--batch").
+ Run(ctx, &RunOpts{
Dir: repoPath,
Stdin: batchStdinReader,
Stdout: batchStdoutWriter,
diff --git a/modules/git/blame.go b/modules/git/blame.go
index cad720edf4..659dec34a1 100644
--- a/modules/git/blame.go
+++ b/modules/git/blame.go
@@ -11,7 +11,7 @@ import (
"os"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/setting"
)
// BlamePart represents block of blame - continuous lines with one sha
@@ -29,12 +29,13 @@ type BlameReader struct {
bufferedReader *bufio.Reader
done chan error
lastSha *string
- ignoreRevsFile *string
+ ignoreRevsFile string
objectFormat ObjectFormat
+ cleanupFuncs []func()
}
func (r *BlameReader) UsesIgnoreRevs() bool {
- return r.ignoreRevsFile != nil
+ return r.ignoreRevsFile != ""
}
// NextPart returns next part of blame (sequential code lines with the same commit)
@@ -122,40 +123,49 @@ func (r *BlameReader) Close() error {
r.bufferedReader = nil
_ = r.reader.Close()
_ = r.output.Close()
- if r.ignoreRevsFile != nil {
- _ = util.Remove(*r.ignoreRevsFile)
+ for _, cleanup := range r.cleanupFuncs {
+ if cleanup != nil {
+ cleanup()
+ }
}
return err
}
// CreateBlameReader creates reader for given repository, commit and file
-func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (*BlameReader, error) {
- var ignoreRevsFile *string
+func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (rd *BlameReader, err error) {
+ var ignoreRevsFileName string
+ var ignoreRevsFileCleanup func()
+ defer func() {
+ if err != nil && ignoreRevsFileCleanup != nil {
+ ignoreRevsFileCleanup()
+ }
+ }()
+
+ cmd := NewCommandNoGlobals("blame", "--porcelain")
+
if DefaultFeatures().CheckVersionAtLeast("2.23") && !bypassBlameIgnore {
- ignoreRevsFile = tryCreateBlameIgnoreRevsFile(commit)
+ ignoreRevsFileName, ignoreRevsFileCleanup, err = tryCreateBlameIgnoreRevsFile(commit)
+ if err != nil && !IsErrNotExist(err) {
+ return nil, err
+ }
+ if ignoreRevsFileName != "" {
+ // Possible improvement: use --ignore-revs-file /dev/stdin on unix
+ // There is no equivalent on Windows. May be implemented if Gitea uses an external git backend.
+ cmd.AddOptionValues("--ignore-revs-file", ignoreRevsFileName)
+ }
}
- cmd := NewCommandContextNoGlobals(ctx, "blame", "--porcelain")
- if ignoreRevsFile != nil {
- // Possible improvement: use --ignore-revs-file /dev/stdin on unix
- // There is no equivalent on Windows. May be implemented if Gitea uses an external git backend.
- cmd.AddOptionValues("--ignore-revs-file", *ignoreRevsFile)
- }
cmd.AddDynamicArguments(commit.ID.String()).AddDashesAndList(file)
+
+ done := make(chan error, 1)
reader, stdout, err := os.Pipe()
if err != nil {
- if ignoreRevsFile != nil {
- _ = util.Remove(*ignoreRevsFile)
- }
return nil, err
}
-
- done := make(chan error, 1)
-
go func() {
stderr := bytes.Buffer{}
// TODO: it doesn't work for directories (the directories shouldn't be "blamed"), and the "err" should be returned by "Read" but not by "Close"
- err := cmd.Run(&RunOpts{
+ err := cmd.Run(ctx, &RunOpts{
UseContextTimeout: true,
Dir: repoPath,
Stdout: stdout,
@@ -169,40 +179,40 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath
}()
bufferedReader := bufio.NewReader(reader)
-
return &BlameReader{
output: stdout,
reader: reader,
bufferedReader: bufferedReader,
done: done,
- ignoreRevsFile: ignoreRevsFile,
+ ignoreRevsFile: ignoreRevsFileName,
objectFormat: objectFormat,
+ cleanupFuncs: []func(){ignoreRevsFileCleanup},
}, nil
}
-func tryCreateBlameIgnoreRevsFile(commit *Commit) *string {
+func tryCreateBlameIgnoreRevsFile(commit *Commit) (string, func(), error) {
entry, err := commit.GetTreeEntryByPath(".git-blame-ignore-revs")
if err != nil {
- return nil
+ return "", nil, err
}
r, err := entry.Blob().DataAsync()
if err != nil {
- return nil
+ return "", nil, err
}
defer r.Close()
- f, err := os.CreateTemp("", "gitea_git-blame-ignore-revs")
+ f, cleanup, err := setting.AppDataTempDir("git-repo-content").CreateTempFileRandom("git-blame-ignore-revs")
if err != nil {
- return nil
+ return "", nil, err
}
-
+ filename := f.Name()
_, err = io.Copy(f, r)
_ = f.Close()
if err != nil {
- _ = util.Remove(f.Name())
- return nil
+ cleanup()
+ return "", nil, err
}
- return util.ToPointer(f.Name())
+ return filename, cleanup, nil
}
diff --git a/modules/git/blame_sha256_test.go b/modules/git/blame_sha256_test.go
index da451f22fc..c0a97bed3b 100644
--- a/modules/git/blame_sha256_test.go
+++ b/modules/git/blame_sha256_test.go
@@ -7,11 +7,14 @@ import (
"context"
"testing"
+ "code.gitea.io/gitea/modules/setting"
+
"github.com/stretchr/testify/assert"
)
func TestReadingBlameOutputSha256(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
+ setting.AppDataPath = t.TempDir()
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
if isGogit {
diff --git a/modules/git/blame_test.go b/modules/git/blame_test.go
index 4220c85600..809d6fbcf7 100644
--- a/modules/git/blame_test.go
+++ b/modules/git/blame_test.go
@@ -7,11 +7,14 @@ import (
"context"
"testing"
+ "code.gitea.io/gitea/modules/setting"
+
"github.com/stretchr/testify/assert"
)
func TestReadingBlameOutput(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
+ setting.AppDataPath = t.TempDir()
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
t.Run("Without .git-blame-ignore-revs", func(t *testing.T) {
diff --git a/modules/git/blob.go b/modules/git/blob.go
index bcecb42e16..40d8f44e79 100644
--- a/modules/git/blob.go
+++ b/modules/git/blob.go
@@ -7,7 +7,9 @@ package git
import (
"bytes"
"encoding/base64"
+ "errors"
"io"
+ "strings"
"code.gitea.io/gitea/modules/typesniffer"
"code.gitea.io/gitea/modules/util"
@@ -20,22 +22,28 @@ func (b *Blob) Name() string {
return b.name
}
-// GetBlobContent Gets the limited content of the blob as raw text
-func (b *Blob) GetBlobContent(limit int64) (string, error) {
+// GetBlobBytes Gets the limited content of the blob
+func (b *Blob) GetBlobBytes(limit int64) ([]byte, error) {
if limit <= 0 {
- return "", nil
+ return nil, nil
}
dataRc, err := b.DataAsync()
if err != nil {
- return "", err
+ return nil, err
}
defer dataRc.Close()
- buf, err := util.ReadWithLimit(dataRc, int(limit))
+ return util.ReadWithLimit(dataRc, int(limit))
+}
+
+// GetBlobContent Gets the limited content of the blob as raw text
+func (b *Blob) GetBlobContent(limit int64) (string, error) {
+ buf, err := b.GetBlobBytes(limit)
return string(buf), err
}
-// GetBlobLineCount gets line count of the blob
-func (b *Blob) GetBlobLineCount() (int, error) {
+// GetBlobLineCount gets line count of the blob.
+// It will also try to write the content to w if it's not nil, then we could pre-fetch the content without reading it again.
+func (b *Blob) GetBlobLineCount(w io.Writer) (int, error) {
reader, err := b.DataAsync()
if err != nil {
return 0, err
@@ -44,59 +52,61 @@ func (b *Blob) GetBlobLineCount() (int, error) {
buf := make([]byte, 32*1024)
count := 1
lineSep := []byte{'\n'}
-
- c, err := reader.Read(buf)
- if c == 0 && err == io.EOF {
- return 0, nil
- }
for {
+ c, err := reader.Read(buf)
+ if w != nil {
+ if _, err := w.Write(buf[:c]); err != nil {
+ return count, err
+ }
+ }
count += bytes.Count(buf[:c], lineSep)
switch {
- case err == io.EOF:
+ case errors.Is(err, io.EOF):
return count, nil
case err != nil:
return count, err
}
- c, err = reader.Read(buf)
}
}
-// GetBlobContentBase64 Reads the content of the blob with a base64 encode and returns the encoded string
-func (b *Blob) GetBlobContentBase64() (string, error) {
+// GetBlobContentBase64 Reads the content of the blob with a base64 encoding and returns the encoded string
+func (b *Blob) GetBlobContentBase64(originContent *strings.Builder) (string, error) {
dataRc, err := b.DataAsync()
if err != nil {
return "", err
}
defer dataRc.Close()
- pr, pw := io.Pipe()
- encoder := base64.NewEncoder(base64.StdEncoding, pw)
-
- go func() {
- _, err := io.Copy(encoder, dataRc)
- _ = encoder.Close()
-
- if err != nil {
- _ = pw.CloseWithError(err)
- } else {
- _ = pw.Close()
+ base64buf := &strings.Builder{}
+ encoder := base64.NewEncoder(base64.StdEncoding, base64buf)
+ buf := make([]byte, 32*1024)
+loop:
+ for {
+ n, err := dataRc.Read(buf)
+ if n > 0 {
+ if originContent != nil {
+ _, _ = originContent.Write(buf[:n])
+ }
+ if _, err := encoder.Write(buf[:n]); err != nil {
+ return "", err
+ }
+ }
+ switch {
+ case errors.Is(err, io.EOF):
+ break loop
+ case err != nil:
+ return "", err
}
- }()
-
- out, err := io.ReadAll(pr)
- if err != nil {
- return "", err
}
- return string(out), nil
+ _ = encoder.Close()
+ return base64buf.String(), nil
}
// GuessContentType guesses the content type of the blob.
func (b *Blob) GuessContentType() (typesniffer.SniffedType, error) {
- r, err := b.DataAsync()
+ buf, err := b.GetBlobBytes(typesniffer.SniffContentSize)
if err != nil {
return typesniffer.SniffedType{}, err
}
- defer r.Close()
-
- return typesniffer.DetectContentTypeFromReader(r)
+ return typesniffer.DetectContentType(buf), nil
}
diff --git a/modules/git/blob_test.go b/modules/git/blob_test.go
index 63374384f6..f21e8d146d 100644
--- a/modules/git/blob_test.go
+++ b/modules/git/blob_test.go
@@ -17,9 +17,7 @@ func TestBlob_Data(t *testing.T) {
output := "file2\n"
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
repo, err := openRepositoryWithDefaultContext(bareRepo1Path)
- if !assert.NoError(t, err) {
- t.Fatal()
- }
+ require.NoError(t, err)
defer repo.Close()
testBlob, err := repo.GetBlob("6c493ff740f9380390d5c9ddef4af18697ac9375")
@@ -49,7 +47,7 @@ func Benchmark_Blob_Data(b *testing.B) {
b.Fatal(err)
}
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
r, err := testBlob.DataAsync()
if err != nil {
b.Fatal(err)
diff --git a/modules/git/cmdverb.go b/modules/git/cmdverb.go
new file mode 100644
index 0000000000..3d6f4ae0c6
--- /dev/null
+++ b/modules/git/cmdverb.go
@@ -0,0 +1,36 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package git
+
+const (
+ CmdVerbUploadPack = "git-upload-pack"
+ CmdVerbUploadArchive = "git-upload-archive"
+ CmdVerbReceivePack = "git-receive-pack"
+ CmdVerbLfsAuthenticate = "git-lfs-authenticate"
+ CmdVerbLfsTransfer = "git-lfs-transfer"
+
+ CmdSubVerbLfsUpload = "upload"
+ CmdSubVerbLfsDownload = "download"
+)
+
+func IsAllowedVerbForServe(verb string) bool {
+ switch verb {
+ case CmdVerbUploadPack,
+ CmdVerbUploadArchive,
+ CmdVerbReceivePack,
+ CmdVerbLfsAuthenticate,
+ CmdVerbLfsTransfer:
+ return true
+ }
+ return false
+}
+
+func IsAllowedVerbForServeLfs(verb string) bool {
+ switch verb {
+ case CmdVerbLfsAuthenticate,
+ CmdVerbLfsTransfer:
+ return true
+ }
+ return false
+}
diff --git a/modules/git/command.go b/modules/git/command.go
index 2584e3cc57..22f1d02339 100644
--- a/modules/git/command.go
+++ b/modules/git/command.go
@@ -18,6 +18,7 @@ import (
"time"
"code.gitea.io/gitea/modules/git/internal" //nolint:depguard // only this file can use the internal type CmdArg, other files and packages should use AddXxx functions
+ "code.gitea.io/gitea/modules/gtprof"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/util"
@@ -43,9 +44,10 @@ const DefaultLocale = "C"
type Command struct {
prog string
args []string
- parentContext context.Context
globalArgsLength int
brokenArgs []string
+ cmd *exec.Cmd // for debug purpose only
+ configArgs []string
}
func logArgSanitize(arg string) string {
@@ -54,7 +56,7 @@ func logArgSanitize(arg string) string {
} else if filepath.IsAbs(arg) {
base := filepath.Base(arg)
dir := filepath.Dir(arg)
- return filepath.Join(filepath.Base(dir), base)
+ return ".../" + filepath.Join(filepath.Base(dir), base)
}
return arg
}
@@ -79,9 +81,16 @@ func (c *Command) LogString() string {
return strings.Join(a, " ")
}
+func (c *Command) ProcessState() string {
+ if c.cmd == nil {
+ return ""
+ }
+ return c.cmd.ProcessState.String()
+}
+
// NewCommand creates and returns a new Git Command based on given command and arguments.
// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
-func NewCommand(ctx context.Context, args ...internal.CmdArg) *Command {
+func NewCommand(args ...internal.CmdArg) *Command {
// Make an explicit copy of globalCommandArgs, otherwise append might overwrite it
cargs := make([]string, 0, len(globalCommandArgs)+len(args))
for _, arg := range globalCommandArgs {
@@ -93,31 +102,23 @@ func NewCommand(ctx context.Context, args ...internal.CmdArg) *Command {
return &Command{
prog: GitExecutable,
args: cargs,
- parentContext: ctx,
globalArgsLength: len(globalCommandArgs),
}
}
-// NewCommandContextNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args
+// NewCommandNoGlobals creates and returns a new Git Command based on given command and arguments only with the specified args and don't use global command args
// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
-func NewCommandContextNoGlobals(ctx context.Context, args ...internal.CmdArg) *Command {
+func NewCommandNoGlobals(args ...internal.CmdArg) *Command {
cargs := make([]string, 0, len(args))
for _, arg := range args {
cargs = append(cargs, string(arg))
}
return &Command{
- prog: GitExecutable,
- args: cargs,
- parentContext: ctx,
+ prog: GitExecutable,
+ args: cargs,
}
}
-// SetParentContext sets the parent context for this command
-func (c *Command) SetParentContext(ctx context.Context) *Command {
- c.parentContext = ctx
- return c
-}
-
// isSafeArgumentValue checks if the argument is safe to be used as a value (not an option)
func isSafeArgumentValue(s string) bool {
return s == "" || s[0] != '-'
@@ -196,6 +197,16 @@ func (c *Command) AddDashesAndList(list ...string) *Command {
return c
}
+func (c *Command) AddConfig(key, value string) *Command {
+ kv := key + "=" + value
+ if !isSafeArgumentValue(kv) {
+ c.brokenArgs = append(c.brokenArgs, key)
+ } else {
+ c.configArgs = append(c.configArgs, "-c", kv)
+ }
+ return c
+}
+
// ToTrustedCmdArgs converts a list of strings (trusted as argument) to TrustedCmdArgs
// In most cases, it shouldn't be used. Use NewCommand().AddXxx() function instead
func ToTrustedCmdArgs(args []string) TrustedCmdArgs {
@@ -276,11 +287,11 @@ func CommonCmdServEnvs() []string {
var ErrBrokenCommand = errors.New("git command is broken")
// Run runs the command with the RunOpts
-func (c *Command) Run(opts *RunOpts) error {
- return c.run(1, opts)
+func (c *Command) Run(ctx context.Context, opts *RunOpts) error {
+ return c.run(ctx, 1, opts)
}
-func (c *Command) run(skip int, opts *RunOpts) error {
+func (c *Command) run(ctx context.Context, skip int, opts *RunOpts) error {
if len(c.brokenArgs) != 0 {
log.Error("git command is broken: %s, broken args: %s", c.LogString(), strings.Join(c.brokenArgs, " "))
return ErrBrokenCommand
@@ -295,29 +306,34 @@ func (c *Command) run(skip int, opts *RunOpts) error {
timeout = defaultCommandExecutionTimeout
}
- var desc string
+ cmdLogString := c.LogString()
callerInfo := util.CallerFuncName(1 /* util */ + 1 /* this */ + skip /* parent */)
if pos := strings.LastIndex(callerInfo, "/"); pos >= 0 {
callerInfo = callerInfo[pos+1:]
}
// these logs are for debugging purposes only, so no guarantee of correctness or stability
- desc = fmt.Sprintf("git.Run(by:%s, repo:%s): %s", callerInfo, logArgSanitize(opts.Dir), c.LogString())
+ desc := fmt.Sprintf("git.Run(by:%s, repo:%s): %s", callerInfo, logArgSanitize(opts.Dir), cmdLogString)
log.Debug("git.Command: %s", desc)
- var ctx context.Context
+ _, span := gtprof.GetTracer().Start(ctx, gtprof.TraceSpanGitRun)
+ defer span.End()
+ span.SetAttributeString(gtprof.TraceAttrFuncCaller, callerInfo)
+ span.SetAttributeString(gtprof.TraceAttrGitCommand, cmdLogString)
+
var cancel context.CancelFunc
var finished context.CancelFunc
if opts.UseContextTimeout {
- ctx, cancel, finished = process.GetManager().AddContext(c.parentContext, desc)
+ ctx, cancel, finished = process.GetManager().AddContext(ctx, desc)
} else {
- ctx, cancel, finished = process.GetManager().AddContextTimeout(c.parentContext, timeout, desc)
+ ctx, cancel, finished = process.GetManager().AddContextTimeout(ctx, timeout, desc)
}
defer finished()
startTime := time.Now()
- cmd := exec.CommandContext(ctx, c.prog, c.args...)
+ cmd := exec.CommandContext(ctx, c.prog, append(c.configArgs, c.args...)...)
+ c.cmd = cmd // for debug purpose only
if opts.Env == nil {
cmd.Env = os.Environ()
} else {
@@ -352,9 +368,10 @@ func (c *Command) run(skip int, opts *RunOpts) error {
// We need to check if the context is canceled by the program on Windows.
// This is because Windows does not have signal checking when terminating the process.
// It always returns exit code 1, unlike Linux, which has many exit codes for signals.
+ // `err.Error()` returns "exit status 1" when using the `git check-attr` command after the context is canceled.
if runtime.GOOS == "windows" &&
err != nil &&
- err.Error() == "" &&
+ (err.Error() == "" || err.Error() == "exit status 1") &&
cmd.ProcessState.ExitCode() == 1 &&
ctx.Err() == context.Canceled {
return ctx.Err()
@@ -404,8 +421,8 @@ func IsErrorExitCode(err error, code int) bool {
}
// RunStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr).
-func (c *Command) RunStdString(opts *RunOpts) (stdout, stderr string, runErr RunStdError) {
- stdoutBytes, stderrBytes, err := c.runStdBytes(opts)
+func (c *Command) RunStdString(ctx context.Context, opts *RunOpts) (stdout, stderr string, runErr RunStdError) {
+ stdoutBytes, stderrBytes, err := c.runStdBytes(ctx, opts)
stdout = util.UnsafeBytesToString(stdoutBytes)
stderr = util.UnsafeBytesToString(stderrBytes)
if err != nil {
@@ -416,11 +433,11 @@ func (c *Command) RunStdString(opts *RunOpts) (stdout, stderr string, runErr Run
}
// RunStdBytes runs the command with options and returns stdout/stderr as bytes. and store stderr to returned error (err combined with stderr).
-func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunStdError) {
- return c.runStdBytes(opts)
+func (c *Command) RunStdBytes(ctx context.Context, opts *RunOpts) (stdout, stderr []byte, runErr RunStdError) {
+ return c.runStdBytes(ctx, opts)
}
-func (c *Command) runStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunStdError) {
+func (c *Command) runStdBytes(ctx context.Context, opts *RunOpts) (stdout, stderr []byte, runErr RunStdError) {
if opts == nil {
opts = &RunOpts{}
}
@@ -443,7 +460,7 @@ func (c *Command) runStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunS
PipelineFunc: opts.PipelineFunc,
}
- err := c.run(2, newOpts)
+ err := c.run(ctx, 2, newOpts)
stderr = stderrBuf.Bytes()
if err != nil {
return nil, stderr, &runStdError{err: err, stderr: util.UnsafeBytesToString(stderr)}
diff --git a/modules/git/command_race_test.go b/modules/git/command_race_test.go
index f567406822..a6aa3a1580 100644
--- a/modules/git/command_race_test.go
+++ b/modules/git/command_race_test.go
@@ -15,9 +15,9 @@ func TestRunWithContextNoTimeout(t *testing.T) {
maxLoops := 10
// 'git --version' does not block so it must be finished before the timeout triggered.
- cmd := NewCommand(context.Background(), "--version")
+ cmd := NewCommand("--version")
for i := 0; i < maxLoops; i++ {
- if err := cmd.Run(&RunOpts{}); err != nil {
+ if err := cmd.Run(t.Context(), &RunOpts{}); err != nil {
t.Fatal(err)
}
}
@@ -27,9 +27,9 @@ func TestRunWithContextTimeout(t *testing.T) {
maxLoops := 10
// 'git hash-object --stdin' blocks on stdin so we can have the timeout triggered.
- cmd := NewCommand(context.Background(), "hash-object", "--stdin")
+ cmd := NewCommand("hash-object", "--stdin")
for i := 0; i < maxLoops; i++ {
- if err := cmd.Run(&RunOpts{Timeout: 1 * time.Millisecond}); err != nil {
+ if err := cmd.Run(t.Context(), &RunOpts{Timeout: 1 * time.Millisecond}); err != nil {
if err != context.DeadlineExceeded {
t.Fatalf("Testing %d/%d: %v", i, maxLoops, err)
}
diff --git a/modules/git/command_test.go b/modules/git/command_test.go
index 0823afd7f7..eb112707e7 100644
--- a/modules/git/command_test.go
+++ b/modules/git/command_test.go
@@ -4,21 +4,20 @@
package git
import (
- "context"
"testing"
"github.com/stretchr/testify/assert"
)
func TestRunWithContextStd(t *testing.T) {
- cmd := NewCommand(context.Background(), "--version")
- stdout, stderr, err := cmd.RunStdString(&RunOpts{})
+ cmd := NewCommand("--version")
+ stdout, stderr, err := cmd.RunStdString(t.Context(), &RunOpts{})
assert.NoError(t, err)
assert.Empty(t, stderr)
assert.Contains(t, stdout, "git version")
- cmd = NewCommand(context.Background(), "--no-such-arg")
- stdout, stderr, err = cmd.RunStdString(&RunOpts{})
+ cmd = NewCommand("--no-such-arg")
+ stdout, stderr, err = cmd.RunStdString(t.Context(), &RunOpts{})
if assert.Error(t, err) {
assert.Equal(t, stderr, err.Stderr())
assert.Contains(t, err.Stderr(), "unknown option:")
@@ -26,17 +25,17 @@ func TestRunWithContextStd(t *testing.T) {
assert.Empty(t, stdout)
}
- cmd = NewCommand(context.Background())
+ cmd = NewCommand()
cmd.AddDynamicArguments("-test")
- assert.ErrorIs(t, cmd.Run(&RunOpts{}), ErrBrokenCommand)
+ assert.ErrorIs(t, cmd.Run(t.Context(), &RunOpts{}), ErrBrokenCommand)
- cmd = NewCommand(context.Background())
+ cmd = NewCommand()
cmd.AddDynamicArguments("--test")
- assert.ErrorIs(t, cmd.Run(&RunOpts{}), ErrBrokenCommand)
+ assert.ErrorIs(t, cmd.Run(t.Context(), &RunOpts{}), ErrBrokenCommand)
subCmd := "version"
- cmd = NewCommand(context.Background()).AddDynamicArguments(subCmd) // for test purpose only, the sub-command should never be dynamic for production
- stdout, stderr, err = cmd.RunStdString(&RunOpts{})
+ cmd = NewCommand().AddDynamicArguments(subCmd) // for test purpose only, the sub-command should never be dynamic for production
+ stdout, stderr, err = cmd.RunStdString(t.Context(), &RunOpts{})
assert.NoError(t, err)
assert.Empty(t, stderr)
assert.Contains(t, stdout, "git version")
@@ -54,9 +53,9 @@ func TestGitArgument(t *testing.T) {
}
func TestCommandString(t *testing.T) {
- cmd := NewCommandContextNoGlobals(context.Background(), "a", "-m msg", "it's a test", `say "hello"`)
- assert.EqualValues(t, cmd.prog+` a "-m msg" "it's a test" "say \"hello\""`, cmd.LogString())
+ cmd := NewCommandNoGlobals("a", "-m msg", "it's a test", `say "hello"`)
+ assert.Equal(t, cmd.prog+` a "-m msg" "it's a test" "say \"hello\""`, cmd.LogString())
- cmd = NewCommandContextNoGlobals(context.Background(), "url: https://a:b@c/", "/root/dir-a/dir-b")
- assert.EqualValues(t, cmd.prog+` "url: https://sanitized-credential@c/" dir-a/dir-b`, cmd.LogString())
+ cmd = NewCommandNoGlobals("url: https://a:b@c/", "/root/dir-a/dir-b")
+ assert.Equal(t, cmd.prog+` "url: https://sanitized-credential@c/" .../dir-a/dir-b`, cmd.LogString())
}
diff --git a/modules/git/commit.go b/modules/git/commit.go
index 0ed268e346..ed4876e7b3 100644
--- a/modules/git/commit.go
+++ b/modules/git/commit.go
@@ -20,7 +20,8 @@ import (
// Commit represents a git commit.
type Commit struct {
- Tree
+ Tree // FIXME: bad design, this field can be nil if the commit is from "last commit cache"
+
ID ObjectID // The ID of this commit object
Author *Signature
Committer *Signature
@@ -34,7 +35,7 @@ type Commit struct {
// CommitSignature represents a git commit signature part.
type CommitSignature struct {
Signature string
- Payload string // TODO check if can be reconstruct from the rest of commit information to not have duplicate data
+ Payload string
}
// Message returns the commit message. Same as retrieving CommitMessage directly.
@@ -91,12 +92,12 @@ func AddChanges(repoPath string, all bool, files ...string) error {
// AddChangesWithArgs marks local changes to be ready for commit.
func AddChangesWithArgs(repoPath string, globalArgs TrustedCmdArgs, all bool, files ...string) error {
- cmd := NewCommandContextNoGlobals(DefaultContext, globalArgs...).AddArguments("add")
+ cmd := NewCommandNoGlobals(globalArgs...).AddArguments("add")
if all {
cmd.AddArguments("--all")
}
cmd.AddDashesAndList(files...)
- _, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
+ _, _, err := cmd.RunStdString(DefaultContext, &RunOpts{Dir: repoPath})
return err
}
@@ -118,7 +119,7 @@ func CommitChanges(repoPath string, opts CommitChangesOptions) error {
// CommitChangesWithArgs commits local changes with given committer, author and message.
// If author is nil, it will be the same as committer.
func CommitChangesWithArgs(repoPath string, args TrustedCmdArgs, opts CommitChangesOptions) error {
- cmd := NewCommandContextNoGlobals(DefaultContext, args...)
+ cmd := NewCommandNoGlobals(args...)
if opts.Committer != nil {
cmd.AddOptionValues("-c", "user.name="+opts.Committer.Name)
cmd.AddOptionValues("-c", "user.email="+opts.Committer.Email)
@@ -133,7 +134,7 @@ func CommitChangesWithArgs(repoPath string, args TrustedCmdArgs, opts CommitChan
}
cmd.AddOptionFormat("--message=%s", opts.Message)
- _, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
+ _, _, err := cmd.RunStdString(DefaultContext, &RunOpts{Dir: repoPath})
// No stderr but exit status 1 means nothing to commit.
if err != nil && err.Error() == "exit status 1" {
return nil
@@ -143,7 +144,7 @@ func CommitChangesWithArgs(repoPath string, args TrustedCmdArgs, opts CommitChan
// AllCommitsCount returns count of all commits in repository
func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, files ...string) (int64, error) {
- cmd := NewCommand(ctx, "rev-list")
+ cmd := NewCommand("rev-list")
if hidePRRefs {
cmd.AddArguments("--exclude=" + PullPrefix + "*")
}
@@ -152,7 +153,7 @@ func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, file
cmd.AddDashesAndList(files...)
}
- stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
+ stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath})
if err != nil {
return 0, err
}
@@ -166,11 +167,13 @@ type CommitsCountOptions struct {
Not string
Revision []string
RelPath []string
+ Since string
+ Until string
}
// CommitsCount returns number of total commits of until given revision.
func CommitsCount(ctx context.Context, opts CommitsCountOptions) (int64, error) {
- cmd := NewCommand(ctx, "rev-list", "--count")
+ cmd := NewCommand("rev-list", "--count")
cmd.AddDynamicArguments(opts.Revision...)
@@ -182,7 +185,7 @@ func CommitsCount(ctx context.Context, opts CommitsCountOptions) (int64, error)
cmd.AddDashesAndList(opts.RelPath...)
}
- stdout, _, err := cmd.RunStdString(&RunOpts{Dir: opts.RepoPath})
+ stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: opts.RepoPath})
if err != nil {
return 0, err
}
@@ -199,8 +202,8 @@ func (c *Commit) CommitsCount() (int64, error) {
}
// CommitsByRange returns the specific page commits before current revision, every page's number default by CommitsRangeSize
-func (c *Commit) CommitsByRange(page, pageSize int, not string) ([]*Commit, error) {
- return c.repo.commitsByRange(c.ID, page, pageSize, not)
+func (c *Commit) CommitsByRange(page, pageSize int, not, since, until string) ([]*Commit, error) {
+ return c.repo.commitsByRangeWithTime(c.ID, page, pageSize, not, since, until)
}
// CommitsBefore returns all the commits before current revision
@@ -217,7 +220,7 @@ func (c *Commit) HasPreviousCommit(objectID ObjectID) (bool, error) {
return false, nil
}
- _, _, err := NewCommand(c.repo.Ctx, "merge-base", "--is-ancestor").AddDynamicArguments(that, this).RunStdString(&RunOpts{Dir: c.repo.Path})
+ _, _, err := NewCommand("merge-base", "--is-ancestor").AddDynamicArguments(that, this).RunStdString(c.repo.Ctx, &RunOpts{Dir: c.repo.Path})
if err == nil {
return true, nil
}
@@ -275,8 +278,8 @@ func NewSearchCommitsOptions(searchString string, forAllRefs bool) SearchCommits
var keywords, authors, committers []string
var after, before string
- fields := strings.Fields(searchString)
- for _, k := range fields {
+ fields := strings.FieldsSeq(searchString)
+ for k := range fields {
switch {
case strings.HasPrefix(k, "author:"):
authors = append(authors, strings.TrimPrefix(k, "author:"))
@@ -358,12 +361,12 @@ func (c *Commit) GetFileContent(filename string, limit int) (string, error) {
// GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only')
func (c *Commit) GetBranchName() (string, error) {
- cmd := NewCommand(c.repo.Ctx, "name-rev")
+ cmd := NewCommand("name-rev")
if DefaultFeatures().CheckVersionAtLeast("2.13.0") {
cmd.AddArguments("--exclude", "refs/tags/*")
}
cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String())
- data, _, err := cmd.RunStdString(&RunOpts{Dir: c.repo.Path})
+ data, _, err := cmd.RunStdString(c.repo.Ctx, &RunOpts{Dir: c.repo.Path})
if err != nil {
// handle special case where git can not describe commit
if strings.Contains(err.Error(), "cannot describe") {
@@ -441,7 +444,7 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi
}()
stderr := new(bytes.Buffer)
- err := NewCommand(ctx, "log", "--name-status", "-m", "--pretty=format:", "--first-parent", "--no-renames", "-z", "-1").AddDynamicArguments(commitID).Run(&RunOpts{
+ err := NewCommand("log", "--name-status", "-m", "--pretty=format:", "--first-parent", "--no-renames", "-z", "-1").AddDynamicArguments(commitID).Run(ctx, &RunOpts{
Dir: repoPath,
Stdout: w,
Stderr: stderr,
@@ -457,7 +460,7 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi
// GetFullCommitID returns full length (40) of commit ID by given short SHA in a repository.
func GetFullCommitID(ctx context.Context, repoPath, shortID string) (string, error) {
- commitID, _, err := NewCommand(ctx, "rev-parse").AddDynamicArguments(shortID).RunStdString(&RunOpts{Dir: repoPath})
+ commitID, _, err := NewCommand("rev-parse").AddDynamicArguments(shortID).RunStdString(ctx, &RunOpts{Dir: repoPath})
if err != nil {
if strings.Contains(err.Error(), "exit status 128") {
return "", ErrNotExist{shortID, ""}
@@ -476,8 +479,12 @@ func (c *Commit) GetRepositoryDefaultPublicGPGKey(forceUpdate bool) (*GPGSetting
}
func IsStringLikelyCommitID(objFmt ObjectFormat, s string, minLength ...int) bool {
- minLen := util.OptionalArg(minLength, objFmt.FullLength())
- if len(s) < minLen || len(s) > objFmt.FullLength() {
+ maxLen := 64 // sha256
+ if objFmt != nil {
+ maxLen = objFmt.FullLength()
+ }
+ minLen := util.OptionalArg(minLength, maxLen)
+ if len(s) < minLen || len(s) > maxLen {
return false
}
for _, c := range s {
diff --git a/modules/git/commit_info.go b/modules/git/commit_info.go
index 545081275b..c046acbb50 100644
--- a/modules/git/commit_info.go
+++ b/modules/git/commit_info.go
@@ -7,5 +7,5 @@ package git
type CommitInfo struct {
Entry *TreeEntry
Commit *Commit
- SubModuleFile *CommitSubModuleFile
+ SubmoduleFile *CommitSubmoduleFile
}
diff --git a/modules/git/commit_info_gogit.go b/modules/git/commit_info_gogit.go
index 11b44f7c35..314c2df728 100644
--- a/modules/git/commit_info_gogit.go
+++ b/modules/git/commit_info_gogit.go
@@ -85,8 +85,8 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
} else if subModule != nil {
subModuleURL = subModule.URL
}
- subModuleFile := NewCommitSubModuleFile(subModuleURL, entry.ID.String())
- commitsInfo[i].SubModuleFile = subModuleFile
+ subModuleFile := NewCommitSubmoduleFile(subModuleURL, entry.ID.String())
+ commitsInfo[i].SubmoduleFile = subModuleFile
}
}
diff --git a/modules/git/commit_info_nogogit.go b/modules/git/commit_info_nogogit.go
index 20d586f0ff..1b45fc8a6c 100644
--- a/modules/git/commit_info_nogogit.go
+++ b/modules/git/commit_info_nogogit.go
@@ -7,8 +7,7 @@ package git
import (
"context"
- "fmt"
- "io"
+ "maps"
"path"
"sort"
@@ -40,9 +39,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
return nil, nil, err
}
- for pth, found := range commits {
- revs[pth] = found
- }
+ maps.Copy(revs, commits)
}
} else {
sort.Strings(entryPaths)
@@ -65,7 +62,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
log.Debug("missing commit for %s", entry.Name())
}
- // If the entry if a submodule add a submodule file for this
+ // If the entry is a submodule add a submodule file for this
if entry.IsSubModule() {
subModuleURL := ""
var fullPath string
@@ -79,14 +76,14 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
} else if subModule != nil {
subModuleURL = subModule.URL
}
- subModuleFile := NewCommitSubModuleFile(subModuleURL, entry.ID.String())
- commitsInfo[i].SubModuleFile = subModuleFile
+ subModuleFile := NewCommitSubmoduleFile(subModuleURL, entry.ID.String())
+ commitsInfo[i].SubmoduleFile = subModuleFile
}
}
// Retrieve the commit for the treePath itself (see above). We basically
- // get it for free during the tree traversal and it's used for listing
- // pages to display information about newest commit for a given path.
+ // get it for free during the tree traversal, and it's used for listing
+ // pages to display information about the newest commit for a given path.
var treeCommit *Commit
var ok bool
if treePath == "" {
@@ -124,48 +121,25 @@ func GetLastCommitForPaths(ctx context.Context, commit *Commit, treePath string,
return nil, err
}
- batchStdinWriter, batchReader, cancel, err := commit.repo.CatFileBatch(ctx)
- if err != nil {
- return nil, err
- }
- defer cancel()
-
commitsMap := map[string]*Commit{}
commitsMap[commit.ID.String()] = commit
commitCommits := map[string]*Commit{}
for path, commitID := range revs {
- c, ok := commitsMap[commitID]
- if ok {
- commitCommits[path] = c
+ if len(commitID) == 0 {
continue
}
- if len(commitID) == 0 {
+ c, ok := commitsMap[commitID]
+ if ok {
+ commitCommits[path] = c
continue
}
- _, err := batchStdinWriter.Write([]byte(commitID + "\n"))
- if err != nil {
- return nil, err
- }
- _, typ, size, err := ReadBatchLine(batchReader)
+ c, err := commit.repo.GetCommit(commitID) // Ensure the commit exists in the repository
if err != nil {
return nil, err
}
- if typ != "commit" {
- if err := DiscardFull(batchReader, size+1); err != nil {
- return nil, err
- }
- return nil, fmt.Errorf("unexpected type: %s for commit id: %s", typ, commitID)
- }
- c, err = CommitFromReader(commit.repo, MustIDFromString(commitID), io.LimitReader(batchReader, size))
- if err != nil {
- return nil, err
- }
- if _, err := batchReader.Discard(1); err != nil {
- return nil, err
- }
commitCommits[path] = c
}
diff --git a/modules/git/commit_info_test.go b/modules/git/commit_info_test.go
index 1e331fac00..ba518ab245 100644
--- a/modules/git/commit_info_test.go
+++ b/modules/git/commit_info_test.go
@@ -4,7 +4,6 @@
package git
import (
- "context"
"path/filepath"
"testing"
"time"
@@ -83,7 +82,7 @@ func testGetCommitsInfo(t *testing.T, repo1 *Repository) {
}
// FIXME: Context.TODO() - if graceful has started we should use its Shutdown context otherwise use install signals in TestMain.
- commitsInfo, treeCommit, err := entries.GetCommitsInfo(context.TODO(), commit, testCase.Path)
+ commitsInfo, treeCommit, err := entries.GetCommitsInfo(t.Context(), commit, testCase.Path)
assert.NoError(t, err, "Unable to get commit information for entries of subtree: %s in commit: %s from testcase due to error: %v", testCase.Path, testCase.CommitID, err)
if err != nil {
t.FailNow()
@@ -159,8 +158,8 @@ func BenchmarkEntries_GetCommitsInfo(b *testing.B) {
entries.Sort()
b.ResetTimer()
b.Run(benchmark.name, func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- _, _, err := entries.GetCommitsInfo(context.Background(), commit, "")
+ for b.Loop() {
+ _, _, err := entries.GetCommitsInfo(b.Context(), commit, "")
if err != nil {
b.Fatal(err)
}
diff --git a/modules/git/commit_reader.go b/modules/git/commit_reader.go
index 228bbaf314..eb8f4c6322 100644
--- a/modules/git/commit_reader.go
+++ b/modules/git/commit_reader.go
@@ -6,10 +6,44 @@ package git
import (
"bufio"
"bytes"
+ "fmt"
"io"
- "strings"
)
+const (
+ commitHeaderGpgsig = "gpgsig"
+ commitHeaderGpgsigSha256 = "gpgsig-sha256"
+)
+
+func assignCommitFields(gitRepo *Repository, commit *Commit, headerKey string, headerValue []byte) error {
+ if len(headerValue) > 0 && headerValue[len(headerValue)-1] == '\n' {
+ headerValue = headerValue[:len(headerValue)-1] // remove trailing newline
+ }
+ switch headerKey {
+ case "tree":
+ objID, err := NewIDFromString(string(headerValue))
+ if err != nil {
+ return fmt.Errorf("invalid tree ID %q: %w", string(headerValue), err)
+ }
+ commit.Tree = *NewTree(gitRepo, objID)
+ case "parent":
+ objID, err := NewIDFromString(string(headerValue))
+ if err != nil {
+ return fmt.Errorf("invalid parent ID %q: %w", string(headerValue), err)
+ }
+ commit.Parents = append(commit.Parents, objID)
+ case "author":
+ commit.Author.Decode(headerValue)
+ case "committer":
+ commit.Committer.Decode(headerValue)
+ case commitHeaderGpgsig, commitHeaderGpgsigSha256:
+ // if there are duplicate "gpgsig" and "gpgsig-sha256" headers, then the signature must have already been invalid
+ // so we don't need to handle duplicate headers here
+ commit.Signature = &CommitSignature{Signature: string(headerValue)}
+ }
+ return nil
+}
+
// CommitFromReader will generate a Commit from a provided reader
// We need this to interpret commits from cat-file or cat-file --batch
//
@@ -21,90 +55,46 @@ func CommitFromReader(gitRepo *Repository, objectID ObjectID, reader io.Reader)
Committer: &Signature{},
}
- payloadSB := new(strings.Builder)
- signatureSB := new(strings.Builder)
- messageSB := new(strings.Builder)
- message := false
- pgpsig := false
-
- bufReader, ok := reader.(*bufio.Reader)
- if !ok {
- bufReader = bufio.NewReader(reader)
- }
-
-readLoop:
+ bufReader := bufio.NewReader(reader)
+ inHeader := true
+ var payloadSB, messageSB bytes.Buffer
+ var headerKey string
+ var headerValue []byte
for {
line, err := bufReader.ReadBytes('\n')
- if err != nil {
- if err == io.EOF {
- if message {
- _, _ = messageSB.Write(line)
- }
- _, _ = payloadSB.Write(line)
- break readLoop
- }
- return nil, err
+ if err != nil && err != io.EOF {
+ return nil, fmt.Errorf("unable to read commit %q: %w", objectID.String(), err)
}
- if pgpsig {
- if len(line) > 0 && line[0] == ' ' {
- _, _ = signatureSB.Write(line[1:])
- continue
- }
- pgpsig = false
+ if len(line) == 0 {
+ break
}
- if !message {
- // This is probably not correct but is copied from go-gits interpretation...
- trimmed := bytes.TrimSpace(line)
- if len(trimmed) == 0 {
- message = true
- _, _ = payloadSB.Write(line)
- continue
- }
-
- split := bytes.SplitN(trimmed, []byte{' '}, 2)
- var data []byte
- if len(split) > 1 {
- data = split[1]
+ if inHeader {
+ inHeader = !(len(line) == 1 && line[0] == '\n') // still in header if line is not just a newline
+ k, v, _ := bytes.Cut(line, []byte{' '})
+ if len(k) != 0 || !inHeader {
+ if headerKey != "" {
+ if err = assignCommitFields(gitRepo, commit, headerKey, headerValue); err != nil {
+ return nil, fmt.Errorf("unable to parse commit %q: %w", objectID.String(), err)
+ }
+ }
+ headerKey = string(k) // it also resets the headerValue to empty string if not inHeader
+ headerValue = v
+ } else {
+ headerValue = append(headerValue, v...)
}
-
- switch string(split[0]) {
- case "tree":
- commit.Tree = *NewTree(gitRepo, MustIDFromString(string(data)))
+ if headerKey != commitHeaderGpgsig && headerKey != commitHeaderGpgsigSha256 {
_, _ = payloadSB.Write(line)
- case "parent":
- commit.Parents = append(commit.Parents, MustIDFromString(string(data)))
- _, _ = payloadSB.Write(line)
- case "author":
- commit.Author = &Signature{}
- commit.Author.Decode(data)
- _, _ = payloadSB.Write(line)
- case "committer":
- commit.Committer = &Signature{}
- commit.Committer.Decode(data)
- _, _ = payloadSB.Write(line)
- case "encoding":
- _, _ = payloadSB.Write(line)
- case "gpgsig":
- fallthrough
- case "gpgsig-sha256": // FIXME: no intertop, so only 1 exists at present.
- _, _ = signatureSB.Write(data)
- _ = signatureSB.WriteByte('\n')
- pgpsig = true
}
} else {
_, _ = messageSB.Write(line)
_, _ = payloadSB.Write(line)
}
}
+
commit.CommitMessage = messageSB.String()
- commit.Signature = &CommitSignature{
- Signature: signatureSB.String(),
- Payload: payloadSB.String(),
- }
- if len(commit.Signature.Signature) == 0 {
- commit.Signature = nil
+ if commit.Signature != nil {
+ commit.Signature.Payload = payloadSB.String()
}
-
return commit, nil
}
diff --git a/modules/git/commit_sha256_test.go b/modules/git/commit_sha256_test.go
index 2184a9c47c..97ccecdacc 100644
--- a/modules/git/commit_sha256_test.go
+++ b/modules/git/commit_sha256_test.go
@@ -11,6 +11,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestCommitsCountSha256(t *testing.T) {
@@ -59,8 +60,7 @@ func TestGetFullCommitIDErrorSha256(t *testing.T) {
}
func TestCommitFromReaderSha256(t *testing.T) {
- commitString := `9433b2a62b964c17a4485ae180f45f595d3e69d31b786087775e28c6b6399df0 commit 1114
-tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e
+ commitString := `tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e
parent 26e9ccc29fad747e9c5d9f4c9ddeb7eff61cc45ef6a8dc258cbeb181afc055e8
author Adam Majer <amajer@suse.de> 1698676906 +0100
committer Adam Majer <amajer@suse.de> 1698676906 +0100
@@ -94,11 +94,9 @@ signed commit`
commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString))
assert.NoError(t, err)
- if !assert.NotNil(t, commitFromReader) {
- return
- }
+ require.NotNil(t, commitFromReader)
assert.EqualValues(t, sha, commitFromReader.ID)
- assert.EqualValues(t, `-----BEGIN PGP SIGNATURE-----
+ assert.Equal(t, `-----BEGIN PGP SIGNATURE-----
iQIrBAABCgAtFiEES+fB08xlgTrzSdQvhkUIsBsmec8FAmU/wKoPHGFtYWplckBz
dXNlLmRlAAoJEIZFCLAbJnnP4s4PQIJATa++WPzR6/H4etT7bsOGoMyguEJYyWOd
@@ -113,21 +111,20 @@ VAEUo6ecdDxSpyt2naeg9pKus/BRi7P6g4B1hkk/zZstUX/QP4IQuAJbXjkvsC+X
HKRr3NlRM/DygzTyj0gN74uoa0goCIbyAQhiT42nm0cuhM7uN/W0ayrlZjGF1cbR
8NCJUL2Nwj0ywKIavC99Ipkb8AsFwpVT6U6effs6
=xybZ
------END PGP SIGNATURE-----
-`, commitFromReader.Signature.Signature)
- assert.EqualValues(t, `tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e
+-----END PGP SIGNATURE-----`, commitFromReader.Signature.Signature)
+ assert.Equal(t, `tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e
parent 26e9ccc29fad747e9c5d9f4c9ddeb7eff61cc45ef6a8dc258cbeb181afc055e8
author Adam Majer <amajer@suse.de> 1698676906 +0100
committer Adam Majer <amajer@suse.de> 1698676906 +0100
signed commit`, commitFromReader.Signature.Payload)
- assert.EqualValues(t, "Adam Majer <amajer@suse.de>", commitFromReader.Author.String())
+ assert.Equal(t, "Adam Majer <amajer@suse.de>", commitFromReader.Author.String())
commitFromReader2, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString+"\n\n"))
assert.NoError(t, err)
commitFromReader.CommitMessage += "\n\n"
commitFromReader.Signature.Payload += "\n\n"
- assert.EqualValues(t, commitFromReader, commitFromReader2)
+ assert.Equal(t, commitFromReader, commitFromReader2)
}
func TestHasPreviousCommitSha256(t *testing.T) {
diff --git a/modules/git/commit_submodule.go b/modules/git/commit_submodule.go
index 6603061da2..031fd4e5d0 100644
--- a/modules/git/commit_submodule.go
+++ b/modules/git/commit_submodule.go
@@ -3,6 +3,10 @@
package git
+type SubmoduleWebLink struct {
+ RepoWebLink, CommitWebLink string
+}
+
// GetSubModules get all the submodules of current revision git tree
func (c *Commit) GetSubModules() (*ObjectCache[*SubModule], error) {
if c.submoduleCache != nil {
diff --git a/modules/git/commit_submodule_file.go b/modules/git/commit_submodule_file.go
index bdec35f682..729401f752 100644
--- a/modules/git/commit_submodule_file.go
+++ b/modules/git/commit_submodule_file.go
@@ -5,107 +5,50 @@
package git
import (
- "fmt"
- "net"
- "net/url"
- "path"
- "regexp"
- "strings"
-)
+ "context"
-var scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+@)?([a-zA-Z0-9._-]+):(.*)$`)
+ giturl "code.gitea.io/gitea/modules/git/url"
+)
-// CommitSubModuleFile represents a file with submodule type.
-type CommitSubModuleFile struct {
- refURL string
- refID string
+// CommitSubmoduleFile represents a file with submodule type.
+type CommitSubmoduleFile struct {
+ refURL string
+ parsedURL *giturl.RepositoryURL
+ parsed bool
+ refID string
+ repoLink string
}
-// NewCommitSubModuleFile create a new submodule file
-func NewCommitSubModuleFile(refURL, refID string) *CommitSubModuleFile {
- return &CommitSubModuleFile{
- refURL: refURL,
- refID: refID,
- }
+// NewCommitSubmoduleFile create a new submodule file
+func NewCommitSubmoduleFile(refURL, refID string) *CommitSubmoduleFile {
+ return &CommitSubmoduleFile{refURL: refURL, refID: refID}
}
-func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string {
- if refURL == "" {
- return ""
- }
-
- refURI := strings.TrimSuffix(refURL, ".git")
-
- prefixURL, _ := url.Parse(urlPrefix)
- urlPrefixHostname, _, err := net.SplitHostPort(prefixURL.Host)
- if err != nil {
- urlPrefixHostname = prefixURL.Host
- }
-
- urlPrefix = strings.TrimSuffix(urlPrefix, "/")
-
- // FIXME: Need to consider branch - which will require changes in modules/git/commit.go:GetSubModules
- // Relative url prefix check (according to git submodule documentation)
- if strings.HasPrefix(refURI, "./") || strings.HasPrefix(refURI, "../") {
- return urlPrefix + path.Clean(path.Join("/", repoFullName, refURI))
- }
-
- if !strings.Contains(refURI, "://") {
- // scp style syntax which contains *no* port number after the : (and is not parsed by net/url)
- // ex: git@try.gitea.io:go-gitea/gitea
- match := scpSyntax.FindAllStringSubmatch(refURI, -1)
- if len(match) > 0 {
- m := match[0]
- refHostname := m[2]
- pth := m[3]
-
- if !strings.HasPrefix(pth, "/") {
- pth = "/" + pth
- }
-
- if urlPrefixHostname == refHostname || refHostname == sshDomain {
- return urlPrefix + path.Clean(path.Join("/", pth))
- }
- return "http://" + refHostname + pth
- }
- }
-
- ref, err := url.Parse(refURI)
- if err != nil {
- return ""
- }
+func (sf *CommitSubmoduleFile) RefID() string {
+ return sf.refID // this function is only used in templates
+}
- refHostname, _, err := net.SplitHostPort(ref.Host)
- if err != nil {
- refHostname = ref.Host
+// SubmoduleWebLink tries to make some web links for a submodule, it also works on "nil" receiver
+func (sf *CommitSubmoduleFile) SubmoduleWebLink(ctx context.Context, optCommitID ...string) *SubmoduleWebLink {
+ if sf == nil {
+ return nil
}
-
- supportedSchemes := []string{"http", "https", "git", "ssh", "git+ssh"}
-
- for _, scheme := range supportedSchemes {
- if ref.Scheme == scheme {
- if ref.Scheme == "http" || ref.Scheme == "https" {
- if len(ref.User.Username()) > 0 {
- return ref.Scheme + "://" + fmt.Sprintf("%v", ref.User) + "@" + ref.Host + ref.Path
- }
- return ref.Scheme + "://" + ref.Host + ref.Path
- } else if urlPrefixHostname == refHostname || refHostname == sshDomain {
- return urlPrefix + path.Clean(path.Join("/", ref.Path))
- }
- return "http://" + refHostname + ref.Path
+ if !sf.parsed {
+ sf.parsed = true
+ parsedURL, err := giturl.ParseRepositoryURL(ctx, sf.refURL)
+ if err != nil {
+ return nil
}
- }
-
- return ""
-}
-
-// RefURL guesses and returns reference URL.
-// FIXME: template passes AppURL as urlPrefix, it needs to figure out the correct approach (no hard-coded AppURL anymore)
-func (sf *CommitSubModuleFile) RefURL(urlPrefix, repoFullName, sshDomain string) string {
- return getRefURL(sf.refURL, urlPrefix, repoFullName, sshDomain)
-}
-
-// RefID returns reference ID.
-func (sf *CommitSubModuleFile) RefID() string {
- return sf.refID
+ sf.parsedURL = parsedURL
+ sf.repoLink = giturl.MakeRepositoryWebLink(sf.parsedURL)
+ }
+ var commitLink string
+ if len(optCommitID) == 2 {
+ commitLink = sf.repoLink + "/compare/" + optCommitID[0] + "..." + optCommitID[1]
+ } else if len(optCommitID) == 1 {
+ commitLink = sf.repoLink + "/tree/" + optCommitID[0]
+ } else {
+ commitLink = sf.repoLink + "/tree/" + sf.refID
+ }
+ return &SubmoduleWebLink{RepoWebLink: sf.repoLink, CommitWebLink: commitLink}
}
diff --git a/modules/git/commit_submodule_file_test.go b/modules/git/commit_submodule_file_test.go
index 473b996b82..6581fa8712 100644
--- a/modules/git/commit_submodule_file_test.go
+++ b/modules/git/commit_submodule_file_test.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The Gitea Authors. All rights reserved.
+// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
@@ -9,34 +9,21 @@ import (
"github.com/stretchr/testify/assert"
)
-func TestCommitSubModuleFileGetRefURL(t *testing.T) {
- kases := []struct {
- refURL string
- prefixURL string
- parentPath string
- SSHDomain string
- expect string
- }{
- {"git://github.com/user1/repo1", "/", "user1/repo2", "", "http://github.com/user1/repo1"},
- {"https://localhost/user1/repo1.git", "/", "user1/repo2", "", "https://localhost/user1/repo1"},
- {"http://localhost/user1/repo1.git", "/", "owner/reponame", "", "http://localhost/user1/repo1"},
- {"git@github.com:user1/repo1.git", "/", "owner/reponame", "", "http://github.com/user1/repo1"},
- {"ssh://git@git.zefie.net:2222/zefie/lge_g6_kernel_scripts.git", "/", "zefie/lge_g6_kernel", "", "http://git.zefie.net/zefie/lge_g6_kernel_scripts"},
- {"git@git.zefie.net:2222/zefie/lge_g6_kernel_scripts.git", "/", "zefie/lge_g6_kernel", "", "http://git.zefie.net/2222/zefie/lge_g6_kernel_scripts"},
- {"git@try.gitea.io:go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea"},
- {"ssh://git@try.gitea.io:9999/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea"},
- {"git://git@try.gitea.io:9999/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea"},
- {"ssh://git@127.0.0.1:9999/go-gitea/gitea", "https://127.0.0.1:3000/", "go-gitea/sdk", "", "https://127.0.0.1:3000/go-gitea/gitea"},
- {"https://gitea.com:3000/user1/repo1.git", "https://127.0.0.1:3000/", "user/repo2", "", "https://gitea.com:3000/user1/repo1"},
- {"https://example.gitea.com/gitea/user1/repo1.git", "https://example.gitea.com/gitea/", "", "user/repo2", "https://example.gitea.com/gitea/user1/repo1"},
- {"https://username:password@github.com/username/repository.git", "/", "username/repository2", "", "https://username:password@github.com/username/repository"},
- {"somethingbad", "https://127.0.0.1:3000/go-gitea/gitea", "/", "", ""},
- {"git@localhost:user/repo", "https://localhost/", "user2/repo1", "", "https://localhost/user/repo"},
- {"../path/to/repo.git/", "https://localhost/", "user/repo2", "", "https://localhost/user/path/to/repo.git"},
- {"ssh://git@ssh.gitea.io:2222/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "ssh.gitea.io", "https://try.gitea.io/go-gitea/gitea"},
- }
-
- for _, kase := range kases {
- assert.EqualValues(t, kase.expect, getRefURL(kase.refURL, kase.prefixURL, kase.parentPath, kase.SSHDomain))
- }
+func TestCommitSubmoduleLink(t *testing.T) {
+ sf := NewCommitSubmoduleFile("git@github.com:user/repo.git", "aaaa")
+
+ wl := sf.SubmoduleWebLink(t.Context())
+ assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink)
+ assert.Equal(t, "https://github.com/user/repo/tree/aaaa", wl.CommitWebLink)
+
+ wl = sf.SubmoduleWebLink(t.Context(), "1111")
+ assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink)
+ assert.Equal(t, "https://github.com/user/repo/tree/1111", wl.CommitWebLink)
+
+ wl = sf.SubmoduleWebLink(t.Context(), "1111", "2222")
+ assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink)
+ assert.Equal(t, "https://github.com/user/repo/compare/1111...2222", wl.CommitWebLink)
+
+ wl = (*CommitSubmoduleFile)(nil).SubmoduleWebLink(t.Context())
+ assert.Nil(t, wl)
}
diff --git a/modules/git/commit_test.go b/modules/git/commit_test.go
index 6ac65564dc..81fb91dfc6 100644
--- a/modules/git/commit_test.go
+++ b/modules/git/commit_test.go
@@ -4,13 +4,13 @@
package git
import (
- "context"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestCommitsCount(t *testing.T) {
@@ -59,8 +59,7 @@ func TestGetFullCommitIDError(t *testing.T) {
}
func TestCommitFromReader(t *testing.T) {
- commitString := `feaf4ba6bc635fec442f46ddd4512416ec43c2c2 commit 1074
-tree f1a6cb52b2d16773290cefe49ad0684b50a4f930
+ commitString := `tree f1a6cb52b2d16773290cefe49ad0684b50a4f930
parent 37991dec2c8e592043f47155ce4808d4580f9123
author silverwind <me@silverwind.io> 1563741793 +0200
committer silverwind <me@silverwind.io> 1563741793 +0200
@@ -91,11 +90,9 @@ empty commit`
commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString))
assert.NoError(t, err)
- if !assert.NotNil(t, commitFromReader) {
- return
- }
+ require.NotNil(t, commitFromReader)
assert.EqualValues(t, sha, commitFromReader.ID)
- assert.EqualValues(t, `-----BEGIN PGP SIGNATURE-----
+ assert.Equal(t, `-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEEWPb2jX6FS2mqyJRQLmK0HJOGlEMFAl00zmEACgkQLmK0HJOG
lEMDFBAAhQKKqLD1VICygJMEB8t1gBmNLgvziOLfpX4KPWdPtBk3v/QJ7OrfMrVK
@@ -110,26 +107,24 @@ sD53z/f0J+We4VZjY+pidvA9BGZPFVdR3wd3xGs8/oH6UWaLJAMGkLG6dDb3qDLm
mfeFhT57UbE4qukTDIQ0Y0WM40UYRTakRaDY7ubhXgLgx09Cnp9XTVMsHgT6j9/i
1pxsB104XLWjQHTjr1JtiaBQEwFh9r2OKTcpvaLcbNtYpo7CzOs=
=FRsO
------END PGP SIGNATURE-----
-`, commitFromReader.Signature.Signature)
- assert.EqualValues(t, `tree f1a6cb52b2d16773290cefe49ad0684b50a4f930
+-----END PGP SIGNATURE-----`, commitFromReader.Signature.Signature)
+ assert.Equal(t, `tree f1a6cb52b2d16773290cefe49ad0684b50a4f930
parent 37991dec2c8e592043f47155ce4808d4580f9123
author silverwind <me@silverwind.io> 1563741793 +0200
committer silverwind <me@silverwind.io> 1563741793 +0200
empty commit`, commitFromReader.Signature.Payload)
- assert.EqualValues(t, "silverwind <me@silverwind.io>", commitFromReader.Author.String())
+ assert.Equal(t, "silverwind <me@silverwind.io>", commitFromReader.Author.String())
commitFromReader2, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString+"\n\n"))
assert.NoError(t, err)
commitFromReader.CommitMessage += "\n\n"
commitFromReader.Signature.Payload += "\n\n"
- assert.EqualValues(t, commitFromReader, commitFromReader2)
+ assert.Equal(t, commitFromReader, commitFromReader2)
}
func TestCommitWithEncodingFromReader(t *testing.T) {
- commitString := `feaf4ba6bc635fec442f46ddd4512416ec43c2c2 commit 1074
-tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
+ commitString := `tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
parent 47b24e7ab977ed31c5a39989d570847d6d0052af
author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
committer KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
@@ -159,11 +154,9 @@ ISO-8859-1`
commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString))
assert.NoError(t, err)
- if !assert.NotNil(t, commitFromReader) {
- return
- }
+ require.NotNil(t, commitFromReader)
assert.EqualValues(t, sha, commitFromReader.ID)
- assert.EqualValues(t, `-----BEGIN PGP SIGNATURE-----
+ assert.Equal(t, `-----BEGIN PGP SIGNATURE-----
iQGzBAABCgAdFiEE9HRrbqvYxPT8PXbefPSEkrowAa8FAmYGg7IACgkQfPSEkrow
Aa9olwv+P0HhtCM6CRvlUmPaqswRsDPNR4i66xyXGiSxdI9V5oJL7HLiQIM7KrFR
@@ -176,22 +169,21 @@ SONRzusmu5n3DgV956REL7x62h7JuqmBz/12HZkr0z0zgXkcZ04q08pSJATX5N1F
yN+tWxTsWg+zhDk96d5Esdo9JMjcFvPv0eioo30GAERaz1hoD7zCMT4jgUFTQwgz
jw4YcO5u
=r3UU
------END PGP SIGNATURE-----
-`, commitFromReader.Signature.Signature)
- assert.EqualValues(t, `tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
+-----END PGP SIGNATURE-----`, commitFromReader.Signature.Signature)
+ assert.Equal(t, `tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
parent 47b24e7ab977ed31c5a39989d570847d6d0052af
author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
committer KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
encoding ISO-8859-1
ISO-8859-1`, commitFromReader.Signature.Payload)
- assert.EqualValues(t, "KN4CK3R <admin@oldschoolhack.me>", commitFromReader.Author.String())
+ assert.Equal(t, "KN4CK3R <admin@oldschoolhack.me>", commitFromReader.Author.String())
commitFromReader2, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString+"\n\n"))
assert.NoError(t, err)
commitFromReader.CommitMessage += "\n\n"
commitFromReader.Signature.Payload += "\n\n"
- assert.EqualValues(t, commitFromReader, commitFromReader2)
+ assert.Equal(t, commitFromReader, commitFromReader2)
}
func TestHasPreviousCommit(t *testing.T) {
@@ -350,15 +342,15 @@ func TestGetCommitFileStatusMerges(t *testing.T) {
func Test_GetCommitBranchStart(t *testing.T) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
- repo, err := OpenRepository(context.Background(), bareRepo1Path)
+ repo, err := OpenRepository(t.Context(), bareRepo1Path)
assert.NoError(t, err)
defer repo.Close()
commit, err := repo.GetBranchCommit("branch1")
assert.NoError(t, err)
- assert.EqualValues(t, "2839944139e0de9737a044f78b0e4b40d989a9e3", commit.ID.String())
+ assert.Equal(t, "2839944139e0de9737a044f78b0e4b40d989a9e3", commit.ID.String())
startCommitID, err := repo.GetCommitBranchStart(os.Environ(), "branch1", commit.ID.String())
assert.NoError(t, err)
assert.NotEmpty(t, startCommitID)
- assert.EqualValues(t, "9c9aef8dd84e02bc7ec12641deb4c930a7c30185", startCommitID)
+ assert.Equal(t, "95bb4d39648ee7e325106df01a621c530863a653", startCommitID)
}
diff --git a/modules/git/config.go b/modules/git/config.go
index 9c36cf1654..234be7b955 100644
--- a/modules/git/config.go
+++ b/modules/git/config.go
@@ -116,7 +116,7 @@ func syncGitConfig() (err error) {
}
func configSet(key, value string) error {
- stdout, _, err := NewCommand(DefaultContext, "config", "--global", "--get").AddDynamicArguments(key).RunStdString(nil)
+ stdout, _, err := NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(DefaultContext, nil)
if err != nil && !IsErrorExitCode(err, 1) {
return fmt.Errorf("failed to get git config %s, err: %w", key, err)
}
@@ -126,7 +126,7 @@ func configSet(key, value string) error {
return nil
}
- _, _, err = NewCommand(DefaultContext, "config", "--global").AddDynamicArguments(key, value).RunStdString(nil)
+ _, _, err = NewCommand("config", "--global").AddDynamicArguments(key, value).RunStdString(DefaultContext, nil)
if err != nil {
return fmt.Errorf("failed to set git global config %s, err: %w", key, err)
}
@@ -135,14 +135,14 @@ func configSet(key, value string) error {
}
func configSetNonExist(key, value string) error {
- _, _, err := NewCommand(DefaultContext, "config", "--global", "--get").AddDynamicArguments(key).RunStdString(nil)
+ _, _, err := NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(DefaultContext, nil)
if err == nil {
// already exist
return nil
}
if IsErrorExitCode(err, 1) {
// not exist, set new config
- _, _, err = NewCommand(DefaultContext, "config", "--global").AddDynamicArguments(key, value).RunStdString(nil)
+ _, _, err = NewCommand("config", "--global").AddDynamicArguments(key, value).RunStdString(DefaultContext, nil)
if err != nil {
return fmt.Errorf("failed to set git global config %s, err: %w", key, err)
}
@@ -153,14 +153,14 @@ func configSetNonExist(key, value string) error {
}
func configAddNonExist(key, value string) error {
- _, _, err := NewCommand(DefaultContext, "config", "--global", "--get").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(nil)
+ _, _, err := NewCommand("config", "--global", "--get").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(DefaultContext, nil)
if err == nil {
// already exist
return nil
}
if IsErrorExitCode(err, 1) {
// not exist, add new config
- _, _, err = NewCommand(DefaultContext, "config", "--global", "--add").AddDynamicArguments(key, value).RunStdString(nil)
+ _, _, err = NewCommand("config", "--global", "--add").AddDynamicArguments(key, value).RunStdString(DefaultContext, nil)
if err != nil {
return fmt.Errorf("failed to add git global config %s, err: %w", key, err)
}
@@ -170,10 +170,10 @@ func configAddNonExist(key, value string) error {
}
func configUnsetAll(key, value string) error {
- _, _, err := NewCommand(DefaultContext, "config", "--global", "--get").AddDynamicArguments(key).RunStdString(nil)
+ _, _, err := NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(DefaultContext, nil)
if err == nil {
// exist, need to remove
- _, _, err = NewCommand(DefaultContext, "config", "--global", "--unset-all").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(nil)
+ _, _, err = NewCommand("config", "--global", "--unset-all").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(DefaultContext, nil)
if err != nil {
return fmt.Errorf("failed to unset git global config %s, err: %w", key, err)
}
diff --git a/modules/git/diff.go b/modules/git/diff.go
index 833f6220f9..c4df6b8063 100644
--- a/modules/git/diff.go
+++ b/modules/git/diff.go
@@ -34,8 +34,8 @@ func GetRawDiff(repo *Repository, commitID string, diffType RawDiffType, writer
// GetReverseRawDiff dumps the reverse diff results of repository in given commit ID to io.Writer.
func GetReverseRawDiff(ctx context.Context, repoPath, commitID string, writer io.Writer) error {
stderr := new(bytes.Buffer)
- cmd := NewCommand(ctx, "show", "--pretty=format:revert %H%n", "-R").AddDynamicArguments(commitID)
- if err := cmd.Run(&RunOpts{
+ cmd := NewCommand("show", "--pretty=format:revert %H%n", "-R").AddDynamicArguments(commitID)
+ if err := cmd.Run(ctx, &RunOpts{
Dir: repoPath,
Stdout: writer,
Stderr: stderr,
@@ -56,7 +56,7 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
files = append(files, file)
}
- cmd := NewCommand(repo.Ctx)
+ cmd := NewCommand()
switch diffType {
case RawDiffNormal:
if len(startCommit) != 0 {
@@ -64,7 +64,10 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
} else if commit.ParentCount() == 0 {
cmd.AddArguments("show").AddDynamicArguments(endCommit).AddDashesAndList(files...)
} else {
- c, _ := commit.Parent(0)
+ c, err := commit.Parent(0)
+ if err != nil {
+ return err
+ }
cmd.AddArguments("diff", "-M").AddDynamicArguments(c.ID.String(), endCommit).AddDashesAndList(files...)
}
case RawDiffPatch:
@@ -74,7 +77,10 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
} else if commit.ParentCount() == 0 {
cmd.AddArguments("format-patch", "--no-signature", "--stdout", "--root").AddDynamicArguments(endCommit).AddDashesAndList(files...)
} else {
- c, _ := commit.Parent(0)
+ c, err := commit.Parent(0)
+ if err != nil {
+ return err
+ }
query := fmt.Sprintf("%s...%s", endCommit, c.ID.String())
cmd.AddArguments("format-patch", "--no-signature", "--stdout").AddDynamicArguments(query).AddDashesAndList(files...)
}
@@ -83,7 +89,7 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
}
stderr := new(bytes.Buffer)
- if err = cmd.Run(&RunOpts{
+ if err = cmd.Run(repo.Ctx, &RunOpts{
Dir: repo.Path,
Stdout: writer,
Stderr: stderr,
@@ -295,8 +301,8 @@ func GetAffectedFiles(repo *Repository, branchName, oldCommitID, newCommitID str
affectedFiles := make([]string, 0, 32)
// Run `git diff --name-only` to get the names of the changed files
- err = NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(oldCommitID, newCommitID).
- Run(&RunOpts{
+ err = NewCommand("diff", "--name-only").AddDynamicArguments(oldCommitID, newCommitID).
+ Run(repo.Ctx, &RunOpts{
Env: env,
Dir: repo.Path,
Stdout: stdoutWriter,
diff --git a/modules/git/diff_test.go b/modules/git/diff_test.go
index 0f865c52a8..7671fffcc1 100644
--- a/modules/git/diff_test.go
+++ b/modules/git/diff_test.go
@@ -154,7 +154,7 @@ func TestCutDiffAroundLine(t *testing.T) {
}
func BenchmarkCutDiffAroundLine(b *testing.B) {
- for n := 0; n < b.N; n++ {
+ for b.Loop() {
CutDiffAroundLine(strings.NewReader(exampleDiff), 3, true, 3)
}
}
@@ -177,8 +177,8 @@ func ExampleCutDiffAroundLine() {
func TestParseDiffHunkString(t *testing.T) {
leftLine, leftHunk, rightLine, rightHunk := ParseDiffHunkString("@@ -19,3 +19,5 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER")
- assert.EqualValues(t, 19, leftLine)
- assert.EqualValues(t, 3, leftHunk)
- assert.EqualValues(t, 19, rightLine)
- assert.EqualValues(t, 5, rightHunk)
+ assert.Equal(t, 19, leftLine)
+ assert.Equal(t, 3, leftHunk)
+ assert.Equal(t, 19, rightLine)
+ assert.Equal(t, 5, rightHunk)
}
diff --git a/modules/git/error.go b/modules/git/error.go
index 10fb37be07..7d131345d0 100644
--- a/modules/git/error.go
+++ b/modules/git/error.go
@@ -32,22 +32,6 @@ func (err ErrNotExist) Unwrap() error {
return util.ErrNotExist
}
-// ErrBadLink entry.FollowLink error
-type ErrBadLink struct {
- Name string
- Message string
-}
-
-func (err ErrBadLink) Error() string {
- return fmt.Sprintf("%s: %s", err.Name, err.Message)
-}
-
-// IsErrBadLink if some error is ErrBadLink
-func IsErrBadLink(err error) bool {
- _, ok := err.(ErrBadLink)
- return ok
-}
-
// ErrBranchNotExist represents a "BranchNotExist" kind of error.
type ErrBranchNotExist struct {
Name string
diff --git a/modules/git/foreachref/format.go b/modules/git/foreachref/format.go
index 97e8ee4724..d9573a55d6 100644
--- a/modules/git/foreachref/format.go
+++ b/modules/git/foreachref/format.go
@@ -76,7 +76,7 @@ func (f Format) Parser(r io.Reader) *Parser {
// would turn into "%0a%00".
func (f Format) hexEscaped(delim []byte) string {
escaped := ""
- for i := 0; i < len(delim); i++ {
+ for i := range delim {
escaped += "%" + hex.EncodeToString([]byte{delim[i]})
}
return escaped
diff --git a/modules/git/fsck.go b/modules/git/fsck.go
index cec27f165b..a52684c84f 100644
--- a/modules/git/fsck.go
+++ b/modules/git/fsck.go
@@ -10,5 +10,5 @@ import (
// Fsck verifies the connectivity and validity of the objects in the database
func Fsck(ctx context.Context, repoPath string, timeout time.Duration, args TrustedCmdArgs) error {
- return NewCommand(ctx, "fsck").AddArguments(args...).Run(&RunOpts{Timeout: timeout, Dir: repoPath})
+ return NewCommand("fsck").AddArguments(args...).Run(ctx, &RunOpts{Timeout: timeout, Dir: repoPath})
}
diff --git a/modules/git/git.go b/modules/git/git.go
index e3e5b83274..a2ffd6d289 100644
--- a/modules/git/git.go
+++ b/modules/git/git.go
@@ -30,6 +30,7 @@ type Features struct {
SupportProcReceive bool // >= 2.29
SupportHashSha256 bool // >= 2.42, SHA-256 repositories no longer an ‘experimental curiosity’
SupportedObjectFormats []ObjectFormat // sha1, sha256
+ SupportCheckAttrOnBare bool // >= 2.40
}
var (
@@ -60,7 +61,7 @@ func DefaultFeatures() *Features {
}
func loadGitVersionFeatures() (*Features, error) {
- stdout, _, runErr := NewCommand(DefaultContext, "version").RunStdString(nil)
+ stdout, _, runErr := NewCommand("version").RunStdString(DefaultContext, nil)
if runErr != nil {
return nil, runErr
}
@@ -77,6 +78,7 @@ func loadGitVersionFeatures() (*Features, error) {
if features.SupportHashSha256 {
features.SupportedObjectFormats = append(features.SupportedObjectFormats, Sha256ObjectFormat)
}
+ features.SupportCheckAttrOnBare = features.CheckVersionAtLeast("2.40")
return features, nil
}
diff --git a/modules/git/git_test.go b/modules/git/git_test.go
index 5472842b76..58ba01cabc 100644
--- a/modules/git/git_test.go
+++ b/modules/git/git_test.go
@@ -10,18 +10,19 @@ import (
"testing"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/tempdir"
"github.com/hashicorp/go-version"
"github.com/stretchr/testify/assert"
)
func testRun(m *testing.M) error {
- gitHomePath, err := os.MkdirTemp(os.TempDir(), "git-home")
+ gitHomePath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("git-home")
if err != nil {
return fmt.Errorf("unable to create temp dir: %w", err)
}
- defer util.RemoveAll(gitHomePath)
+ defer cleanup()
+
setting.Git.HomePath = gitHomePath
if err = InitFull(context.Background()); err != nil {
diff --git a/modules/git/grep.go b/modules/git/grep.go
index bf6b41a886..66711650c9 100644
--- a/modules/git/grep.go
+++ b/modules/git/grep.go
@@ -23,11 +23,19 @@ type GrepResult struct {
LineCodes []string
}
+type GrepModeType string
+
+const (
+ GrepModeExact GrepModeType = "exact"
+ GrepModeWords GrepModeType = "words"
+ GrepModeRegexp GrepModeType = "regexp"
+)
+
type GrepOptions struct {
RefName string
MaxResultLimit int
ContextLineNumber int
- IsFuzzy bool
+ GrepMode GrepModeType
MaxLineLength int // the maximum length of a line to parse, exceeding chars will be truncated
PathspecList []string
}
@@ -52,21 +60,30 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
2^@repo: go-gitea/gitea
*/
var results []*GrepResult
- cmd := NewCommand(ctx, "grep", "--null", "--break", "--heading", "--fixed-strings", "--line-number", "--ignore-case", "--full-name")
- cmd.AddOptionValues("--context", fmt.Sprint(opts.ContextLineNumber))
- if opts.IsFuzzy {
+ cmd := NewCommand("grep", "--null", "--break", "--heading", "--line-number", "--full-name")
+ cmd.AddOptionValues("--context", strconv.Itoa(opts.ContextLineNumber))
+ switch opts.GrepMode {
+ case GrepModeExact:
+ cmd.AddArguments("--fixed-strings")
+ cmd.AddOptionValues("-e", strings.TrimLeft(search, "-"))
+ case GrepModeRegexp:
+ cmd.AddArguments("--perl-regexp")
+ cmd.AddOptionValues("-e", strings.TrimLeft(search, "-"))
+ default: /* words */
words := strings.Fields(search)
- for _, word := range words {
+ cmd.AddArguments("--fixed-strings", "--ignore-case")
+ for i, word := range words {
cmd.AddOptionValues("-e", strings.TrimLeft(word, "-"))
+ if i < len(words)-1 {
+ cmd.AddOptionValues("--and")
+ }
}
- } else {
- cmd.AddOptionValues("-e", strings.TrimLeft(search, "-"))
}
cmd.AddDynamicArguments(util.IfZero(opts.RefName, "HEAD"))
cmd.AddDashesAndList(opts.PathspecList...)
opts.MaxResultLimit = util.IfZero(opts.MaxResultLimit, 50)
stderr := bytes.Buffer{}
- err = cmd.Run(&RunOpts{
+ err = cmd.Run(ctx, &RunOpts{
Dir: repo.Path,
Stdout: stdoutWriter,
Stderr: &stderr,
diff --git a/modules/git/grep_test.go b/modules/git/grep_test.go
index 005d539726..0dce464b7c 100644
--- a/modules/git/grep_test.go
+++ b/modules/git/grep_test.go
@@ -4,7 +4,6 @@
package git
import (
- "context"
"path/filepath"
"testing"
@@ -16,7 +15,7 @@ func TestGrepSearch(t *testing.T) {
assert.NoError(t, err)
defer repo.Close()
- res, err := GrepSearch(context.Background(), repo, "void", GrepOptions{})
+ res, err := GrepSearch(t.Context(), repo, "void", GrepOptions{})
assert.NoError(t, err)
assert.Equal(t, []*GrepResult{
{
@@ -31,7 +30,7 @@ func TestGrepSearch(t *testing.T) {
},
}, res)
- res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{PathspecList: []string{":(glob)java-hello/*"}})
+ res, err = GrepSearch(t.Context(), repo, "void", GrepOptions{PathspecList: []string{":(glob)java-hello/*"}})
assert.NoError(t, err)
assert.Equal(t, []*GrepResult{
{
@@ -41,7 +40,7 @@ func TestGrepSearch(t *testing.T) {
},
}, res)
- res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{PathspecList: []string{":(glob,exclude)java-hello/*"}})
+ res, err = GrepSearch(t.Context(), repo, "void", GrepOptions{PathspecList: []string{":(glob,exclude)java-hello/*"}})
assert.NoError(t, err)
assert.Equal(t, []*GrepResult{
{
@@ -51,7 +50,7 @@ func TestGrepSearch(t *testing.T) {
},
}, res)
- res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{MaxResultLimit: 1})
+ res, err = GrepSearch(t.Context(), repo, "void", GrepOptions{MaxResultLimit: 1})
assert.NoError(t, err)
assert.Equal(t, []*GrepResult{
{
@@ -61,7 +60,7 @@ func TestGrepSearch(t *testing.T) {
},
}, res)
- res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{MaxResultLimit: 1, MaxLineLength: 39})
+ res, err = GrepSearch(t.Context(), repo, "void", GrepOptions{MaxResultLimit: 1, MaxLineLength: 39})
assert.NoError(t, err)
assert.Equal(t, []*GrepResult{
{
@@ -71,11 +70,11 @@ func TestGrepSearch(t *testing.T) {
},
}, res)
- res, err = GrepSearch(context.Background(), repo, "no-such-content", GrepOptions{})
+ res, err = GrepSearch(t.Context(), repo, "no-such-content", GrepOptions{})
assert.NoError(t, err)
assert.Empty(t, res)
- res, err = GrepSearch(context.Background(), &Repository{Path: "no-such-git-repo"}, "no-such-content", GrepOptions{})
+ res, err = GrepSearch(t.Context(), &Repository{Path: "no-such-git-repo"}, "no-such-content", GrepOptions{})
assert.Error(t, err)
assert.Empty(t, res)
}
diff --git a/modules/git/hook.go b/modules/git/hook.go
index 46f93ce13e..548a59971d 100644
--- a/modules/git/hook.go
+++ b/modules/git/hook.go
@@ -7,11 +7,10 @@ package git
import (
"errors"
"os"
- "path"
"path/filepath"
+ "slices"
"strings"
- "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
)
@@ -27,12 +26,7 @@ var ErrNotValidHook = errors.New("not a valid Git hook")
// IsValidHookName returns true if given name is a valid Git hook.
func IsValidHookName(name string) bool {
- for _, hn := range hookNames {
- if hn == name {
- return true
- }
- }
- return false
+ return slices.Contains(hookNames, name)
}
// Hook represents a Git hook.
@@ -51,17 +45,28 @@ func GetHook(repoPath, name string) (*Hook, error) {
}
h := &Hook{
name: name,
- path: path.Join(repoPath, "hooks", name+".d", name),
+ path: filepath.Join(repoPath, "hooks", name+".d", name),
}
- samplePath := filepath.Join(repoPath, "hooks", name+".sample")
- if isFile(h.path) {
+ isFile, err := util.IsFile(h.path)
+ if err != nil {
+ return nil, err
+ }
+ if isFile {
data, err := os.ReadFile(h.path)
if err != nil {
return nil, err
}
h.IsActive = true
h.Content = string(data)
- } else if isFile(samplePath) {
+ return h, nil
+ }
+
+ samplePath := filepath.Join(repoPath, "hooks", name+".sample")
+ isFile, err = util.IsFile(samplePath)
+ if err != nil {
+ return nil, err
+ }
+ if isFile {
data, err := os.ReadFile(samplePath)
if err != nil {
return nil, err
@@ -79,7 +84,11 @@ func (h *Hook) Name() string {
// Update updates hook settings.
func (h *Hook) Update() error {
if len(strings.TrimSpace(h.Content)) == 0 {
- if isExist(h.path) {
+ exist, err := util.IsExist(h.path)
+ if err != nil {
+ return err
+ }
+ if exist {
err := util.Remove(h.path)
if err != nil {
return err
@@ -103,7 +112,10 @@ func (h *Hook) Update() error {
// ListHooks returns a list of Git hooks of given repository.
func ListHooks(repoPath string) (_ []*Hook, err error) {
- if !isDir(path.Join(repoPath, "hooks")) {
+ exist, err := util.IsDir(filepath.Join(repoPath, "hooks"))
+ if err != nil {
+ return nil, err
+ } else if !exist {
return nil, errors.New("hooks path does not exist")
}
@@ -116,28 +128,3 @@ func ListHooks(repoPath string) (_ []*Hook, err error) {
}
return hooks, nil
}
-
-const (
- // HookPathUpdate hook update path
- HookPathUpdate = "hooks/update"
-)
-
-// SetUpdateHook writes given content to update hook of the repository.
-func SetUpdateHook(repoPath, content string) (err error) {
- log.Debug("Setting update hook: %s", repoPath)
- hookPath := path.Join(repoPath, HookPathUpdate)
- isExist, err := util.IsExist(hookPath)
- if err != nil {
- log.Debug("Unable to check if %s exists. Error: %v", hookPath, err)
- return err
- }
- if isExist {
- err = util.Remove(hookPath)
- } else {
- err = os.MkdirAll(path.Dir(hookPath), os.ModePerm)
- }
- if err != nil {
- return err
- }
- return os.WriteFile(hookPath, []byte(content), 0o777)
-}
diff --git a/modules/git/key.go b/modules/git/key.go
new file mode 100644
index 0000000000..2513c048b7
--- /dev/null
+++ b/modules/git/key.go
@@ -0,0 +1,15 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package git
+
+// Based on https://git-scm.com/docs/git-config#Documentation/git-config.txt-gpgformat
+const (
+ SigningKeyFormatOpenPGP = "openpgp" // for GPG keys, the expected default of git cli
+ SigningKeyFormatSSH = "ssh"
+)
+
+type SigningKey struct {
+ KeyID string
+ Format string
+}
diff --git a/modules/git/repo_language_stats.go b/modules/git/languagestats/language_stats.go
index 8551ea9d24..a71284c3e4 100644
--- a/modules/git/repo_language_stats.go
+++ b/modules/git/languagestats/language_stats.go
@@ -1,13 +1,15 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package git
+package languagestats
import (
+ "context"
"strings"
"unicode"
- "code.gitea.io/gitea/modules/optional"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/git/attribute"
)
const (
@@ -49,19 +51,15 @@ func mergeLanguageStats(stats map[string]int64) map[string]int64 {
return res
}
-func TryReadLanguageAttribute(attrs map[string]string) optional.Option[string] {
- language := AttributeToString(attrs, AttributeLinguistLanguage)
- if language.Value() == "" {
- language = AttributeToString(attrs, AttributeGitlabLanguage)
- if language.Has() {
- raw := language.Value()
- // gitlab-language may have additional parameters after the language
- // ignore them and just use the main language
- // https://docs.gitlab.com/ee/user/project/highlighting.html#override-syntax-highlighting-for-a-file-type
- if idx := strings.IndexByte(raw, '?'); idx >= 0 {
- language = optional.Some(raw[:idx])
- }
- }
+// GetFileLanguage tries to get the (linguist) language of the file content
+func GetFileLanguage(ctx context.Context, gitRepo *git.Repository, treeish, treePath string) (string, error) {
+ attributesMap, err := attribute.CheckAttributes(ctx, gitRepo, treeish, attribute.CheckAttributeOpts{
+ Attributes: []string{attribute.LinguistLanguage, attribute.GitlabLanguage},
+ Filenames: []string{treePath},
+ })
+ if err != nil {
+ return "", err
}
- return language
+
+ return attributesMap[treePath].GetLanguage().Value(), nil
}
diff --git a/modules/git/repo_language_stats_gogit.go b/modules/git/languagestats/language_stats_gogit.go
index a34c03c781..418c05b157 100644
--- a/modules/git/repo_language_stats_gogit.go
+++ b/modules/git/languagestats/language_stats_gogit.go
@@ -3,13 +3,15 @@
//go:build gogit
-package git
+package languagestats
import (
"bytes"
"io"
"code.gitea.io/gitea/modules/analyze"
+ git_module "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/git/attribute"
"code.gitea.io/gitea/modules/optional"
"github.com/go-enry/go-enry/v2"
@@ -19,7 +21,7 @@ import (
)
// GetLanguageStats calculates language stats for git repository at specified commit
-func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, error) {
+func GetLanguageStats(repo *git_module.Repository, commitID string) (map[string]int64, error) {
r, err := git.PlainOpen(repo.Path)
if err != nil {
return nil, err
@@ -40,8 +42,11 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
return nil, err
}
- checker, deferable := repo.CheckAttributeReader(commitID)
- defer deferable()
+ checker, err := attribute.NewBatchChecker(repo, commitID, attribute.LinguistAttributes)
+ if err != nil {
+ return nil, err
+ }
+ defer checker.Close()
// sizes contains the current calculated size of all files by language
sizes := make(map[string]int64)
@@ -62,43 +67,41 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
isDocumentation := optional.None[bool]()
isDetectable := optional.None[bool]()
- if checker != nil {
- attrs, err := checker.CheckPath(f.Name)
- if err == nil {
- isVendored = AttributeToBool(attrs, AttributeLinguistVendored)
- if isVendored.ValueOrDefault(false) {
- return nil
- }
-
- isGenerated = AttributeToBool(attrs, AttributeLinguistGenerated)
- if isGenerated.ValueOrDefault(false) {
- return nil
- }
+ attrs, err := checker.CheckPath(f.Name)
+ if err == nil {
+ isVendored = attrs.GetVendored()
+ if isVendored.ValueOrDefault(false) {
+ return nil
+ }
- isDocumentation = AttributeToBool(attrs, AttributeLinguistDocumentation)
- if isDocumentation.ValueOrDefault(false) {
- return nil
- }
+ isGenerated = attrs.GetGenerated()
+ if isGenerated.ValueOrDefault(false) {
+ return nil
+ }
- isDetectable = AttributeToBool(attrs, AttributeLinguistDetectable)
- if !isDetectable.ValueOrDefault(true) {
- return nil
- }
+ isDocumentation = attrs.GetDocumentation()
+ if isDocumentation.ValueOrDefault(false) {
+ return nil
+ }
- hasLanguage := TryReadLanguageAttribute(attrs)
- if hasLanguage.Value() != "" {
- language := hasLanguage.Value()
+ isDetectable = attrs.GetDetectable()
+ if !isDetectable.ValueOrDefault(true) {
+ return nil
+ }
- // group languages, such as Pug -> HTML; SCSS -> CSS
- group := enry.GetLanguageGroup(language)
- if len(group) != 0 {
- language = group
- }
+ hasLanguage := attrs.GetLanguage()
+ if hasLanguage.Value() != "" {
+ language := hasLanguage.Value()
- // this language will always be added to the size
- sizes[language] += f.Size
- return nil
+ // group languages, such as Pug -> HTML; SCSS -> CSS
+ group := enry.GetLanguageGroup(language)
+ if len(group) != 0 {
+ language = group
}
+
+ // this language will always be added to the size
+ sizes[language] += f.Size
+ return nil
}
}
diff --git a/modules/git/repo_language_stats_nogogit.go b/modules/git/languagestats/language_stats_nogogit.go
index de7707bd6c..94cf9fff8c 100644
--- a/modules/git/repo_language_stats_nogogit.go
+++ b/modules/git/languagestats/language_stats_nogogit.go
@@ -3,13 +3,15 @@
//go:build !gogit
-package git
+package languagestats
import (
"bytes"
"io"
"code.gitea.io/gitea/modules/analyze"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/git/attribute"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
@@ -17,7 +19,7 @@ import (
)
// GetLanguageStats calculates language stats for git repository at specified commit
-func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, error) {
+func GetLanguageStats(repo *git.Repository, commitID string) (map[string]int64, error) {
// We will feed the commit IDs in order into cat-file --batch, followed by blobs as necessary.
// so let's create a batch stdin and stdout
batchStdinWriter, batchReader, cancel, err := repo.CatFileBatch(repo.Ctx)
@@ -34,19 +36,19 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
if err := writeID(commitID); err != nil {
return nil, err
}
- shaBytes, typ, size, err := ReadBatchLine(batchReader)
+ shaBytes, typ, size, err := git.ReadBatchLine(batchReader)
if typ != "commit" {
log.Debug("Unable to get commit for: %s. Err: %v", commitID, err)
- return nil, ErrNotExist{commitID, ""}
+ return nil, git.ErrNotExist{ID: commitID}
}
- sha, err := NewIDFromString(string(shaBytes))
+ sha, err := git.NewIDFromString(string(shaBytes))
if err != nil {
log.Debug("Unable to get commit for: %s. Err: %v", commitID, err)
- return nil, ErrNotExist{commitID, ""}
+ return nil, git.ErrNotExist{ID: commitID}
}
- commit, err := CommitFromReader(repo, sha, io.LimitReader(batchReader, size))
+ commit, err := git.CommitFromReader(repo, sha, io.LimitReader(batchReader, size))
if err != nil {
log.Debug("Unable to get commit for: %s. Err: %v", commitID, err)
return nil, err
@@ -62,8 +64,11 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
return nil, err
}
- checker, deferable := repo.CheckAttributeReader(commitID)
- defer deferable()
+ checker, err := attribute.NewBatchChecker(repo, commitID, attribute.LinguistAttributes)
+ if err != nil {
+ return nil, err
+ }
+ defer checker.Close()
contentBuf := bytes.Buffer{}
var content []byte
@@ -92,47 +97,40 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
}
isVendored := optional.None[bool]()
- isGenerated := optional.None[bool]()
isDocumentation := optional.None[bool]()
isDetectable := optional.None[bool]()
- if checker != nil {
- attrs, err := checker.CheckPath(f.Name())
- if err == nil {
- isVendored = AttributeToBool(attrs, AttributeLinguistVendored)
- if isVendored.ValueOrDefault(false) {
- continue
- }
-
- isGenerated = AttributeToBool(attrs, AttributeLinguistGenerated)
- if isGenerated.ValueOrDefault(false) {
- continue
- }
+ attrs, err := checker.CheckPath(f.Name())
+ attrLinguistGenerated := optional.None[bool]()
+ if err == nil {
+ if isVendored = attrs.GetVendored(); isVendored.ValueOrDefault(false) {
+ continue
+ }
- isDocumentation = AttributeToBool(attrs, AttributeLinguistDocumentation)
- if isDocumentation.ValueOrDefault(false) {
- continue
- }
+ if attrLinguistGenerated = attrs.GetGenerated(); attrLinguistGenerated.ValueOrDefault(false) {
+ continue
+ }
- isDetectable = AttributeToBool(attrs, AttributeLinguistDetectable)
- if !isDetectable.ValueOrDefault(true) {
- continue
- }
+ if isDocumentation = attrs.GetDocumentation(); isDocumentation.ValueOrDefault(false) {
+ continue
+ }
- hasLanguage := TryReadLanguageAttribute(attrs)
- if hasLanguage.Value() != "" {
- language := hasLanguage.Value()
+ if isDetectable = attrs.GetDetectable(); !isDetectable.ValueOrDefault(true) {
+ continue
+ }
- // group languages, such as Pug -> HTML; SCSS -> CSS
- group := enry.GetLanguageGroup(language)
- if len(group) != 0 {
- language = group
- }
+ if hasLanguage := attrs.GetLanguage(); hasLanguage.Value() != "" {
+ language := hasLanguage.Value()
- // this language will always be added to the size
- sizes[language] += f.Size()
- continue
+ // group languages, such as Pug -> HTML; SCSS -> CSS
+ group := enry.GetLanguageGroup(language)
+ if len(group) != 0 {
+ language = group
}
+
+ // this language will always be added to the size
+ sizes[language] += f.Size()
+ continue
}
}
@@ -149,7 +147,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
if err := writeID(f.ID.String()); err != nil {
return nil, err
}
- _, _, size, err := ReadBatchLine(batchReader)
+ _, _, size, err := git.ReadBatchLine(batchReader)
if err != nil {
log.Debug("Error reading blob: %s Err: %v", f.ID.String(), err)
return nil, err
@@ -167,11 +165,19 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
return nil, err
}
content = contentBuf.Bytes()
- if err := DiscardFull(batchReader, discard); err != nil {
+ if err := git.DiscardFull(batchReader, discard); err != nil {
return nil, err
}
}
- if !isGenerated.Has() && enry.IsGenerated(f.Name(), content) {
+
+ // if "generated" attribute is set, use it, otherwise use enry.IsGenerated to guess
+ var isGenerated bool
+ if attrLinguistGenerated.Has() {
+ isGenerated = attrLinguistGenerated.Value()
+ } else {
+ isGenerated = enry.IsGenerated(f.Name(), content)
+ }
+ if isGenerated {
continue
}
diff --git a/modules/git/repo_language_stats_test.go b/modules/git/languagestats/language_stats_test.go
index da3871e909..b908ae6413 100644
--- a/modules/git/repo_language_stats_test.go
+++ b/modules/git/languagestats/language_stats_test.go
@@ -3,36 +3,36 @@
//go:build !gogit
-package git
+package languagestats
import (
- "path/filepath"
"testing"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
+
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestRepository_GetLanguageStats(t *testing.T) {
- repoPath := filepath.Join(testReposDir, "language_stats_repo")
- gitRepo, err := openRepositoryWithDefaultContext(repoPath)
- if !assert.NoError(t, err) {
- t.Fatal()
- }
+ setting.AppDataPath = t.TempDir()
+ repoPath := "../tests/repos/language_stats_repo"
+ gitRepo, err := git.OpenRepository(t.Context(), repoPath)
+ require.NoError(t, err)
defer gitRepo.Close()
- stats, err := gitRepo.GetLanguageStats("8fee858da5796dfb37704761701bb8e800ad9ef3")
- if !assert.NoError(t, err) {
- t.Fatal()
- }
+ stats, err := GetLanguageStats(gitRepo, "8fee858da5796dfb37704761701bb8e800ad9ef3")
+ require.NoError(t, err)
- assert.EqualValues(t, map[string]int64{
+ assert.Equal(t, map[string]int64{
"Python": 134,
"Java": 112,
}, stats)
}
func TestMergeLanguageStats(t *testing.T) {
- assert.EqualValues(t, map[string]int64{
+ assert.Equal(t, map[string]int64{
"PHP": 1,
"python": 10,
"JAVA": 700,
diff --git a/modules/git/languagestats/main_test.go b/modules/git/languagestats/main_test.go
new file mode 100644
index 0000000000..707d268c81
--- /dev/null
+++ b/modules/git/languagestats/main_test.go
@@ -0,0 +1,41 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package languagestats
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "testing"
+
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+)
+
+func testRun(m *testing.M) error {
+ gitHomePath, err := os.MkdirTemp(os.TempDir(), "git-home")
+ if err != nil {
+ return fmt.Errorf("unable to create temp dir: %w", err)
+ }
+ defer util.RemoveAll(gitHomePath)
+ setting.Git.HomePath = gitHomePath
+
+ if err = git.InitFull(context.Background()); err != nil {
+ return fmt.Errorf("failed to call Init: %w", err)
+ }
+
+ exitCode := m.Run()
+ if exitCode != 0 {
+ return fmt.Errorf("run test failed, ExitCode=%d", exitCode)
+ }
+ return nil
+}
+
+func TestMain(m *testing.M) {
+ if err := testRun(m); err != nil {
+ _, _ = fmt.Fprintf(os.Stderr, "Test failed: %v", err)
+ os.Exit(1)
+ }
+}
diff --git a/modules/git/last_commit_cache.go b/modules/git/last_commit_cache.go
index cf9c10d7b4..cff2556083 100644
--- a/modules/git/last_commit_cache.go
+++ b/modules/git/last_commit_cache.go
@@ -13,7 +13,7 @@ import (
)
func getCacheKey(repoPath, commitID, entryPath string) string {
- hashBytes := sha256.Sum256([]byte(fmt.Sprintf("%s:%s:%s", repoPath, commitID, entryPath)))
+ hashBytes := sha256.Sum256(fmt.Appendf(nil, "%s:%s:%s", repoPath, commitID, entryPath))
return fmt.Sprintf("last_commit:%x", hashBytes)
}
diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go
index 1fd58abfcd..dfdef38ef9 100644
--- a/modules/git/log_name_status.go
+++ b/modules/git/log_name_status.go
@@ -34,7 +34,7 @@ func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, p
_ = stdoutWriter.Close()
}
- cmd := NewCommand(ctx)
+ cmd := NewCommand()
cmd.AddArguments("log", "--name-status", "-c", "--format=commit%x00%H %P%x00", "--parents", "--no-renames", "-t", "-z").AddDynamicArguments(head)
var files []string
@@ -64,7 +64,7 @@ func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, p
go func() {
stderr := strings.Builder{}
- err := cmd.Run(&RunOpts{
+ err := cmd.Run(ctx, &RunOpts{
Dir: repository,
Stdout: stdoutWriter,
Stderr: &stderr,
@@ -118,11 +118,12 @@ func (g *LogNameStatusRepoParser) Next(treepath string, paths2ids map[string]int
g.buffull = false
g.next, err = g.rd.ReadSlice('\x00')
if err != nil {
- if err == bufio.ErrBufferFull {
+ switch err {
+ case bufio.ErrBufferFull:
g.buffull = true
- } else if err == io.EOF {
+ case io.EOF:
return nil, nil
- } else {
+ default:
return nil, err
}
}
@@ -132,11 +133,12 @@ func (g *LogNameStatusRepoParser) Next(treepath string, paths2ids map[string]int
if bytes.Equal(g.next, []byte("commit\000")) {
g.next, err = g.rd.ReadSlice('\x00')
if err != nil {
- if err == bufio.ErrBufferFull {
+ switch err {
+ case bufio.ErrBufferFull:
g.buffull = true
- } else if err == io.EOF {
+ case io.EOF:
return nil, nil
- } else {
+ default:
return nil, err
}
}
@@ -214,11 +216,12 @@ diffloop:
}
g.next, err = g.rd.ReadSlice('\x00')
if err != nil {
- if err == bufio.ErrBufferFull {
+ switch err {
+ case bufio.ErrBufferFull:
g.buffull = true
- } else if err == io.EOF {
+ case io.EOF:
return &ret, nil
- } else {
+ default:
return nil, err
}
}
@@ -343,10 +346,7 @@ func WalkGitLog(ctx context.Context, repo *Repository, head *Commit, treepath st
results := make([]string, len(paths))
remaining := len(paths)
- nextRestart := (len(paths) * 3) / 4
- if nextRestart > 70 {
- nextRestart = 70
- }
+ nextRestart := min((len(paths)*3)/4, 70)
lastEmptyParent := head.ID.String()
commitSinceLastEmptyParent := uint64(0)
commitSinceNextRestart := uint64(0)
diff --git a/modules/git/notes_test.go b/modules/git/notes_test.go
index 267671d8fa..ca05a9e525 100644
--- a/modules/git/notes_test.go
+++ b/modules/git/notes_test.go
@@ -4,7 +4,6 @@
package git
import (
- "context"
"path/filepath"
"testing"
@@ -18,7 +17,7 @@ func TestGetNotes(t *testing.T) {
defer bareRepo1.Close()
note := Note{}
- err = GetNote(context.Background(), bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653", &note)
+ err = GetNote(t.Context(), bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653", &note)
assert.NoError(t, err)
assert.Equal(t, []byte("Note contents\n"), note.Message)
assert.Equal(t, "Vladimir Panteleev", note.Commit.Author.Name)
@@ -31,10 +30,10 @@ func TestGetNestedNotes(t *testing.T) {
defer repo.Close()
note := Note{}
- err = GetNote(context.Background(), repo, "3e668dbfac39cbc80a9ff9c61eb565d944453ba4", &note)
+ err = GetNote(t.Context(), repo, "3e668dbfac39cbc80a9ff9c61eb565d944453ba4", &note)
assert.NoError(t, err)
assert.Equal(t, []byte("Note 2"), note.Message)
- err = GetNote(context.Background(), repo, "ba0a96fa63532d6c5087ecef070b0250ed72fa47", &note)
+ err = GetNote(t.Context(), repo, "ba0a96fa63532d6c5087ecef070b0250ed72fa47", &note)
assert.NoError(t, err)
assert.Equal(t, []byte("Note 1"), note.Message)
}
@@ -46,7 +45,7 @@ func TestGetNonExistentNotes(t *testing.T) {
defer bareRepo1.Close()
note := Note{}
- err = GetNote(context.Background(), bareRepo1, "non_existent_sha", &note)
+ err = GetNote(t.Context(), bareRepo1, "non_existent_sha", &note)
assert.Error(t, err)
assert.IsType(t, ErrNotExist{}, err)
}
diff --git a/modules/git/object_id.go b/modules/git/object_id.go
index 82d30184df..25dfef3ec5 100644
--- a/modules/git/object_id.go
+++ b/modules/git/object_id.go
@@ -99,5 +99,5 @@ type ErrInvalidSHA struct {
}
func (err ErrInvalidSHA) Error() string {
- return fmt.Sprintf("invalid sha: %s", err.SHA)
+ return "invalid sha: " + err.SHA
}
diff --git a/modules/git/parse.go b/modules/git/parse.go
index eb26632cc0..a7f5c58e89 100644
--- a/modules/git/parse.go
+++ b/modules/git/parse.go
@@ -46,19 +46,9 @@ func parseLsTreeLine(line []byte) (*LsTreeEntry, error) {
entry.Size = optional.Some(size)
}
- switch string(entryMode) {
- case "100644":
- entry.EntryMode = EntryModeBlob
- case "100755":
- entry.EntryMode = EntryModeExec
- case "120000":
- entry.EntryMode = EntryModeSymlink
- case "160000":
- entry.EntryMode = EntryModeCommit
- case "040000", "040755": // git uses 040000 for tree object, but some users may get 040755 for unknown reasons
- entry.EntryMode = EntryModeTree
- default:
- return nil, fmt.Errorf("unknown type: %v", string(entryMode))
+ entry.EntryMode, err = ParseEntryMode(string(entryMode))
+ if err != nil || entry.EntryMode == EntryModeNoEntry {
+ return nil, fmt.Errorf("invalid ls-tree output (invalid mode): %q, err: %w", line, err)
}
entry.ID, err = NewIDFromString(string(entryObjectID))
diff --git a/modules/git/parse_nogogit.go b/modules/git/parse_nogogit.go
index 676bb3c76c..78a0162889 100644
--- a/modules/git/parse_nogogit.go
+++ b/modules/git/parse_nogogit.go
@@ -19,7 +19,7 @@ func ParseTreeEntries(data []byte) ([]*TreeEntry, error) {
return parseTreeEntries(data, nil)
}
-// parseTreeEntries FIXME this function's design is not right, it should make the caller read all data into memory
+// parseTreeEntries FIXME this function's design is not right, it should not make the caller read all data into memory
func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
entries := make([]*TreeEntry, 0, bytes.Count(data, []byte{'\n'})+1)
for pos := 0; pos < len(data); {
diff --git a/modules/git/parse_nogogit_test.go b/modules/git/parse_nogogit_test.go
index a4436ce499..6594c84269 100644
--- a/modules/git/parse_nogogit_test.go
+++ b/modules/git/parse_nogogit_test.go
@@ -58,7 +58,7 @@ func TestParseTreeEntriesLong(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, entries, len(testCase.Expected))
for i, entry := range entries {
- assert.EqualValues(t, testCase.Expected[i], entry)
+ assert.Equal(t, testCase.Expected[i], entry)
}
}
}
@@ -91,7 +91,7 @@ func TestParseTreeEntriesShort(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, entries, len(testCase.Expected))
for i, entry := range entries {
- assert.EqualValues(t, testCase.Expected[i], entry)
+ assert.Equal(t, testCase.Expected[i], entry)
}
}
}
diff --git a/modules/git/pipeline/catfile.go b/modules/git/pipeline/catfile.go
index 4677218150..5ddc36cc01 100644
--- a/modules/git/pipeline/catfile.go
+++ b/modules/git/pipeline/catfile.go
@@ -25,8 +25,8 @@ func CatFileBatchCheck(ctx context.Context, shasToCheckReader *io.PipeReader, ca
stderr := new(bytes.Buffer)
var errbuf strings.Builder
- cmd := git.NewCommand(ctx, "cat-file", "--batch-check")
- if err := cmd.Run(&git.RunOpts{
+ cmd := git.NewCommand("cat-file", "--batch-check")
+ if err := cmd.Run(ctx, &git.RunOpts{
Dir: tmpBasePath,
Stdin: shasToCheckReader,
Stdout: catFileCheckWriter,
@@ -43,8 +43,8 @@ func CatFileBatchCheckAllObjects(ctx context.Context, catFileCheckWriter *io.Pip
stderr := new(bytes.Buffer)
var errbuf strings.Builder
- cmd := git.NewCommand(ctx, "cat-file", "--batch-check", "--batch-all-objects")
- if err := cmd.Run(&git.RunOpts{
+ cmd := git.NewCommand("cat-file", "--batch-check", "--batch-all-objects")
+ if err := cmd.Run(ctx, &git.RunOpts{
Dir: tmpBasePath,
Stdout: catFileCheckWriter,
Stderr: stderr,
@@ -64,7 +64,7 @@ func CatFileBatch(ctx context.Context, shasToBatchReader *io.PipeReader, catFile
stderr := new(bytes.Buffer)
var errbuf strings.Builder
- if err := git.NewCommand(ctx, "cat-file", "--batch").Run(&git.RunOpts{
+ if err := git.NewCommand("cat-file", "--batch").Run(ctx, &git.RunOpts{
Dir: tmpBasePath,
Stdout: catFileBatchWriter,
Stdin: shasToBatchReader,
diff --git a/modules/git/pipeline/lfs_nogogit.go b/modules/git/pipeline/lfs_nogogit.go
index 92e35c5a10..c5eed73701 100644
--- a/modules/git/pipeline/lfs_nogogit.go
+++ b/modules/git/pipeline/lfs_nogogit.go
@@ -32,7 +32,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
go func() {
stderr := strings.Builder{}
- err := git.NewCommand(repo.Ctx, "rev-list", "--all").Run(&git.RunOpts{
+ err := git.NewCommand("rev-list", "--all").Run(repo.Ctx, &git.RunOpts{
Dir: repo.Path,
Stdout: revListWriter,
Stderr: &stderr,
diff --git a/modules/git/pipeline/namerev.go b/modules/git/pipeline/namerev.go
index ad583a7479..06731c5051 100644
--- a/modules/git/pipeline/namerev.go
+++ b/modules/git/pipeline/namerev.go
@@ -22,7 +22,7 @@ func NameRevStdin(ctx context.Context, shasToNameReader *io.PipeReader, nameRevS
stderr := new(bytes.Buffer)
var errbuf strings.Builder
- if err := git.NewCommand(ctx, "name-rev", "--stdin", "--name-only", "--always").Run(&git.RunOpts{
+ if err := git.NewCommand("name-rev", "--stdin", "--name-only", "--always").Run(ctx, &git.RunOpts{
Dir: tmpBasePath,
Stdout: nameRevStdinWriter,
Stdin: shasToNameReader,
diff --git a/modules/git/pipeline/revlist.go b/modules/git/pipeline/revlist.go
index d88ebe78ef..31627a0f3a 100644
--- a/modules/git/pipeline/revlist.go
+++ b/modules/git/pipeline/revlist.go
@@ -23,8 +23,8 @@ func RevListAllObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sy
stderr := new(bytes.Buffer)
var errbuf strings.Builder
- cmd := git.NewCommand(ctx, "rev-list", "--objects", "--all")
- if err := cmd.Run(&git.RunOpts{
+ cmd := git.NewCommand("rev-list", "--objects", "--all")
+ if err := cmd.Run(ctx, &git.RunOpts{
Dir: basePath,
Stdout: revListWriter,
Stderr: stderr,
@@ -42,11 +42,11 @@ func RevListObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.
defer revListWriter.Close()
stderr := new(bytes.Buffer)
var errbuf strings.Builder
- cmd := git.NewCommand(ctx, "rev-list", "--objects").AddDynamicArguments(headSHA)
+ cmd := git.NewCommand("rev-list", "--objects").AddDynamicArguments(headSHA)
if baseSHA != "" {
cmd = cmd.AddArguments("--not").AddDynamicArguments(baseSHA)
}
- if err := cmd.Run(&git.RunOpts{
+ if err := cmd.Run(ctx, &git.RunOpts{
Dir: tmpBasePath,
Stdout: revListWriter,
Stderr: stderr,
diff --git a/modules/git/ref.go b/modules/git/ref.go
index aab4c5d77d..56b2db858a 100644
--- a/modules/git/ref.go
+++ b/modules/git/ref.go
@@ -80,6 +80,10 @@ func RefNameFromTag(shortName string) RefName {
return RefName(TagPrefix + shortName)
}
+func RefNameFromCommit(shortName string) RefName {
+ return RefName(shortName)
+}
+
func (ref RefName) String() string {
return string(ref)
}
@@ -105,8 +109,8 @@ func (ref RefName) IsFor() bool {
}
func (ref RefName) nameWithoutPrefix(prefix string) string {
- if strings.HasPrefix(string(ref), prefix) {
- return strings.TrimPrefix(string(ref), prefix)
+ if after, ok := strings.CutPrefix(string(ref), prefix); ok {
+ return after
}
return ""
}
@@ -181,32 +185,38 @@ func (ref RefName) RefGroup() string {
return ""
}
+// RefType is a simple ref type of the reference, it is used for UI and webhooks
+type RefType string
+
+const (
+ RefTypeBranch RefType = "branch"
+ RefTypeTag RefType = "tag"
+ RefTypeCommit RefType = "commit"
+)
+
// RefType returns the simple ref type of the reference, e.g. branch, tag
// It's different from RefGroup, which is using the name of the directory under .git/refs
-// Here we using branch but not heads, using tag but not tags
-func (ref RefName) RefType() string {
- var refType string
- if ref.IsBranch() {
- refType = "branch"
- } else if ref.IsTag() {
- refType = "tag"
+func (ref RefName) RefType() RefType {
+ switch {
+ case ref.IsBranch():
+ return RefTypeBranch
+ case ref.IsTag():
+ return RefTypeTag
+ case IsStringLikelyCommitID(nil, string(ref), 6):
+ return RefTypeCommit
}
- return refType
+ return ""
}
-// RefURL returns the absolute URL for a ref in a repository
-func RefURL(repoURL, ref string) string {
- refFullName := RefName(ref)
- refName := util.PathEscapeSegments(refFullName.ShortName())
- switch {
- case refFullName.IsBranch():
- return repoURL + "/src/branch/" + refName
- case refFullName.IsTag():
- return repoURL + "/src/tag/" + refName
- case !Sha1ObjectFormat.IsValid(ref):
- // assume they mean a branch
- return repoURL + "/src/branch/" + refName
- default:
- return repoURL + "/src/commit/" + refName
+// RefWebLinkPath returns a path for the reference that can be used in a web link:
+// * "branch/<branch_name>"
+// * "tag/<tag_name>"
+// * "commit/<commit_id>"
+// It returns an empty string if the reference is not a branch, tag or commit.
+func (ref RefName) RefWebLinkPath() string {
+ refType := ref.RefType()
+ if refType == "" {
+ return ""
}
+ return string(refType) + "/" + util.PathEscapeSegments(ref.ShortName())
}
diff --git a/modules/git/ref_test.go b/modules/git/ref_test.go
index 58f679b7d6..5397191561 100644
--- a/modules/git/ref_test.go
+++ b/modules/git/ref_test.go
@@ -20,6 +20,8 @@ func TestRefName(t *testing.T) {
// Test pull names
assert.Equal(t, "1", RefName("refs/pull/1/head").PullName())
+ assert.True(t, RefName("refs/pull/1/head").IsPull())
+ assert.True(t, RefName("refs/pull/1/merge").IsPull())
assert.Equal(t, "my/pull", RefName("refs/pull/my/pull/head").PullName())
// Test for branch names
@@ -30,9 +32,8 @@ func TestRefName(t *testing.T) {
assert.Equal(t, "c0ffee", RefName("c0ffee").ShortName())
}
-func TestRefURL(t *testing.T) {
- repoURL := "/user/repo"
- assert.Equal(t, repoURL+"/src/branch/foo", RefURL(repoURL, "refs/heads/foo"))
- assert.Equal(t, repoURL+"/src/tag/foo", RefURL(repoURL, "refs/tags/foo"))
- assert.Equal(t, repoURL+"/src/commit/c0ffee", RefURL(repoURL, "c0ffee"))
+func TestRefWebLinkPath(t *testing.T) {
+ assert.Equal(t, "branch/foo", RefName("refs/heads/foo").RefWebLinkPath())
+ assert.Equal(t, "tag/foo", RefName("refs/tags/foo").RefWebLinkPath())
+ assert.Equal(t, "commit/c0ffee", RefName("c0ffee").RefWebLinkPath())
}
diff --git a/modules/git/remote.go b/modules/git/remote.go
index de8d74eded..876c3d6acb 100644
--- a/modules/git/remote.go
+++ b/modules/git/remote.go
@@ -17,12 +17,12 @@ import (
func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) {
var cmd *Command
if DefaultFeatures().CheckVersionAtLeast("2.7") {
- cmd = NewCommand(ctx, "remote", "get-url").AddDynamicArguments(remoteName)
+ cmd = NewCommand("remote", "get-url").AddDynamicArguments(remoteName)
} else {
- cmd = NewCommand(ctx, "config", "--get").AddDynamicArguments("remote." + remoteName + ".url")
+ cmd = NewCommand("config", "--get").AddDynamicArguments("remote." + remoteName + ".url")
}
- result, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
+ result, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath})
if err != nil {
return "", err
}
@@ -39,7 +39,7 @@ func GetRemoteURL(ctx context.Context, repoPath, remoteName string) (*giturl.Git
if err != nil {
return nil, err
}
- return giturl.Parse(addr)
+ return giturl.ParseGitURL(addr)
}
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
@@ -79,6 +79,15 @@ func (err *ErrInvalidCloneAddr) Unwrap() error {
return util.ErrInvalidArgument
}
+// IsRemoteNotExistError checks the prefix of the error message to see whether a remote does not exist.
+func IsRemoteNotExistError(err error) bool {
+ // see: https://github.com/go-gitea/gitea/issues/32889#issuecomment-2571848216
+ // Should not add space in the end, sometimes git will add a `:`
+ prefix1 := "exit status 128 - fatal: No such remote" // git < 2.30
+ prefix2 := "exit status 2 - error: No such remote" // git >= 2.30
+ return strings.HasPrefix(err.Error(), prefix1) || strings.HasPrefix(err.Error(), prefix2)
+}
+
// ParseRemoteAddr checks if given remote address is valid,
// and returns composed URL with needed username and password.
func ParseRemoteAddr(remoteAddr, authUsername, authPassword string) (string, error) {
diff --git a/modules/git/repo.go b/modules/git/repo.go
index 0993a4ac37..f1f6902773 100644
--- a/modules/git/repo.go
+++ b/modules/git/repo.go
@@ -18,6 +18,7 @@ import (
"time"
"code.gitea.io/gitea/modules/proxy"
+ "code.gitea.io/gitea/modules/setting"
)
// GPGSettings represents the default GPG settings for this repository
@@ -27,6 +28,7 @@ type GPGSettings struct {
Email string
Name string
PublicKeyContent string
+ Format string
}
const prettyLogFormat = `--pretty=format:%H`
@@ -42,9 +44,9 @@ func (repo *Repository) parsePrettyFormatLogToList(logs []byte) ([]*Commit, erro
return commits, nil
}
- parts := bytes.Split(logs, []byte{'\n'})
+ parts := bytes.SplitSeq(logs, []byte{'\n'})
- for _, commitID := range parts {
+ for commitID := range parts {
commit, err := repo.GetCommit(string(commitID))
if err != nil {
return nil, err
@@ -57,7 +59,7 @@ func (repo *Repository) parsePrettyFormatLogToList(logs []byte) ([]*Commit, erro
// IsRepoURLAccessible checks if given repository URL is accessible.
func IsRepoURLAccessible(ctx context.Context, url string) bool {
- _, _, err := NewCommand(ctx, "ls-remote", "-q", "-h").AddDynamicArguments(url, "HEAD").RunStdString(nil)
+ _, _, err := NewCommand("ls-remote", "-q", "-h").AddDynamicArguments(url, "HEAD").RunStdString(ctx, nil)
return err == nil
}
@@ -68,7 +70,7 @@ func InitRepository(ctx context.Context, repoPath string, bare bool, objectForma
return err
}
- cmd := NewCommand(ctx, "init")
+ cmd := NewCommand("init")
if !IsValidObjectFormat(objectFormatName) {
return fmt.Errorf("invalid object format: %s", objectFormatName)
@@ -80,15 +82,15 @@ func InitRepository(ctx context.Context, repoPath string, bare bool, objectForma
if bare {
cmd.AddArguments("--bare")
}
- _, _, err = cmd.RunStdString(&RunOpts{Dir: repoPath})
+ _, _, err = cmd.RunStdString(ctx, &RunOpts{Dir: repoPath})
return err
}
// IsEmpty Check if repository is empty.
func (repo *Repository) IsEmpty() (bool, error) {
var errbuf, output strings.Builder
- if err := NewCommand(repo.Ctx).AddOptionFormat("--git-dir=%s", repo.Path).AddArguments("rev-list", "-n", "1", "--all").
- Run(&RunOpts{
+ if err := NewCommand().AddOptionFormat("--git-dir=%s", repo.Path).AddArguments("rev-list", "-n", "1", "--all").
+ Run(repo.Ctx, &RunOpts{
Dir: repo.Path,
Stdout: &output,
Stderr: &errbuf,
@@ -129,7 +131,7 @@ func CloneWithArgs(ctx context.Context, args TrustedCmdArgs, from, to string, op
return err
}
- cmd := NewCommandContextNoGlobals(ctx, args...).AddArguments("clone")
+ cmd := NewCommandNoGlobals(args...).AddArguments("clone")
if opts.SkipTLSVerify {
cmd.AddArguments("-c", "http.sslVerify=false")
}
@@ -170,7 +172,7 @@ func CloneWithArgs(ctx context.Context, args TrustedCmdArgs, from, to string, op
}
stderr := new(bytes.Buffer)
- if err = cmd.Run(&RunOpts{
+ if err = cmd.Run(ctx, &RunOpts{
Timeout: opts.Timeout,
Env: envs,
Stdout: io.Discard,
@@ -193,7 +195,7 @@ type PushOptions struct {
// Push pushs local commits to given remote branch.
func Push(ctx context.Context, repoPath string, opts PushOptions) error {
- cmd := NewCommand(ctx, "push")
+ cmd := NewCommand("push")
if opts.Force {
cmd.AddArguments("-f")
}
@@ -206,7 +208,7 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
}
cmd.AddDashesAndList(remoteBranchArgs...)
- stdout, stderr, err := cmd.RunStdString(&RunOpts{Env: opts.Env, Timeout: opts.Timeout, Dir: repoPath})
+ stdout, stderr, err := cmd.RunStdString(ctx, &RunOpts{Env: opts.Env, Timeout: opts.Timeout, Dir: repoPath})
if err != nil {
if strings.Contains(stderr, "non-fast-forward") {
return &ErrPushOutOfDate{StdOut: stdout, StdErr: stderr, Err: err}
@@ -225,8 +227,8 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
// GetLatestCommitTime returns time for latest commit in repository (across all branches)
func GetLatestCommitTime(ctx context.Context, repoPath string) (time.Time, error) {
- cmd := NewCommand(ctx, "for-each-ref", "--sort=-committerdate", BranchPrefix, "--count", "1", "--format=%(committerdate)")
- stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
+ cmd := NewCommand("for-each-ref", "--sort=-committerdate", BranchPrefix, "--count", "1", "--format=%(committerdate)")
+ stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath})
if err != nil {
return time.Time{}, err
}
@@ -242,9 +244,9 @@ type DivergeObject struct {
// GetDivergingCommits returns the number of commits a targetBranch is ahead or behind a baseBranch
func GetDivergingCommits(ctx context.Context, repoPath, baseBranch, targetBranch string) (do DivergeObject, err error) {
- cmd := NewCommand(ctx, "rev-list", "--count", "--left-right").
+ cmd := NewCommand("rev-list", "--count", "--left-right").
AddDynamicArguments(baseBranch + "..." + targetBranch).AddArguments("--")
- stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
+ stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath})
if err != nil {
return do, err
}
@@ -266,30 +268,30 @@ func GetDivergingCommits(ctx context.Context, repoPath, baseBranch, targetBranch
// CreateBundle create bundle content to the target path
func (repo *Repository) CreateBundle(ctx context.Context, commit string, out io.Writer) error {
- tmp, err := os.MkdirTemp(os.TempDir(), "gitea-bundle")
+ tmp, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("gitea-bundle")
if err != nil {
return err
}
- defer os.RemoveAll(tmp)
+ defer cleanup()
env := append(os.Environ(), "GIT_OBJECT_DIRECTORY="+filepath.Join(repo.Path, "objects"))
- _, _, err = NewCommand(ctx, "init", "--bare").RunStdString(&RunOpts{Dir: tmp, Env: env})
+ _, _, err = NewCommand("init", "--bare").RunStdString(ctx, &RunOpts{Dir: tmp, Env: env})
if err != nil {
return err
}
- _, _, err = NewCommand(ctx, "reset", "--soft").AddDynamicArguments(commit).RunStdString(&RunOpts{Dir: tmp, Env: env})
+ _, _, err = NewCommand("reset", "--soft").AddDynamicArguments(commit).RunStdString(ctx, &RunOpts{Dir: tmp, Env: env})
if err != nil {
return err
}
- _, _, err = NewCommand(ctx, "branch", "-m", "bundle").RunStdString(&RunOpts{Dir: tmp, Env: env})
+ _, _, err = NewCommand("branch", "-m", "bundle").RunStdString(ctx, &RunOpts{Dir: tmp, Env: env})
if err != nil {
return err
}
tmpFile := filepath.Join(tmp, "bundle")
- _, _, err = NewCommand(ctx, "bundle", "create").AddDynamicArguments(tmpFile, "bundle", "HEAD").RunStdString(&RunOpts{Dir: tmp, Env: env})
+ _, _, err = NewCommand("bundle", "create").AddDynamicArguments(tmpFile, "bundle", "HEAD").RunStdString(ctx, &RunOpts{Dir: tmp, Env: env})
if err != nil {
return err
}
diff --git a/modules/git/repo_archive.go b/modules/git/repo_archive.go
index 2b45a50f19..0b2f6f2a45 100644
--- a/modules/git/repo_archive.go
+++ b/modules/git/repo_archive.go
@@ -16,37 +16,35 @@ import (
type ArchiveType int
const (
- // ZIP zip archive type
- ZIP ArchiveType = iota + 1
- // TARGZ tar gz archive type
- TARGZ
- // BUNDLE bundle archive type
- BUNDLE
+ ArchiveUnknown ArchiveType = iota
+ ArchiveZip // 1
+ ArchiveTarGz // 2
+ ArchiveBundle // 3
)
-// String converts an ArchiveType to string
+// String converts an ArchiveType to string: the extension of the archive file without prefix dot
func (a ArchiveType) String() string {
switch a {
- case ZIP:
+ case ArchiveZip:
return "zip"
- case TARGZ:
+ case ArchiveTarGz:
return "tar.gz"
- case BUNDLE:
+ case ArchiveBundle:
return "bundle"
}
return "unknown"
}
-func ToArchiveType(s string) ArchiveType {
- switch s {
- case "zip":
- return ZIP
- case "tar.gz":
- return TARGZ
- case "bundle":
- return BUNDLE
+func SplitArchiveNameType(s string) (string, ArchiveType) {
+ switch {
+ case strings.HasSuffix(s, ".zip"):
+ return strings.TrimSuffix(s, ".zip"), ArchiveZip
+ case strings.HasSuffix(s, ".tar.gz"):
+ return strings.TrimSuffix(s, ".tar.gz"), ArchiveTarGz
+ case strings.HasSuffix(s, ".bundle"):
+ return strings.TrimSuffix(s, ".bundle"), ArchiveBundle
}
- return 0
+ return s, ArchiveUnknown
}
// CreateArchive create archive content to the target path
@@ -55,7 +53,7 @@ func (repo *Repository) CreateArchive(ctx context.Context, format ArchiveType, t
return fmt.Errorf("unknown format: %v", format)
}
- cmd := NewCommand(ctx, "archive")
+ cmd := NewCommand("archive")
if usePrefix {
cmd.AddOptionFormat("--prefix=%s", filepath.Base(strings.TrimSuffix(repo.Path, ".git"))+"/")
}
@@ -63,7 +61,7 @@ func (repo *Repository) CreateArchive(ctx context.Context, format ArchiveType, t
cmd.AddDynamicArguments(commitID)
var stderr strings.Builder
- err := cmd.Run(&RunOpts{
+ err := cmd.Run(ctx, &RunOpts{
Dir: repo.Path,
Stdout: target,
Stderr: &stderr,
diff --git a/modules/git/repo_archive_test.go b/modules/git/repo_archive_test.go
new file mode 100644
index 0000000000..ff7e2dfce1
--- /dev/null
+++ b/modules/git/repo_archive_test.go
@@ -0,0 +1,32 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package git
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestArchiveType(t *testing.T) {
+ name, archiveType := SplitArchiveNameType("test.tar.gz")
+ assert.Equal(t, "test", name)
+ assert.Equal(t, "tar.gz", archiveType.String())
+
+ name, archiveType = SplitArchiveNameType("a/b/test.zip")
+ assert.Equal(t, "a/b/test", name)
+ assert.Equal(t, "zip", archiveType.String())
+
+ name, archiveType = SplitArchiveNameType("1234.bundle")
+ assert.Equal(t, "1234", name)
+ assert.Equal(t, "bundle", archiveType.String())
+
+ name, archiveType = SplitArchiveNameType("test")
+ assert.Equal(t, "test", name)
+ assert.Equal(t, "unknown", archiveType.String())
+
+ name, archiveType = SplitArchiveNameType("test.xz")
+ assert.Equal(t, "test.xz", name)
+ assert.Equal(t, "unknown", archiveType.String())
+}
diff --git a/modules/git/repo_attribute.go b/modules/git/repo_attribute.go
deleted file mode 100644
index 90eb783fe8..0000000000
--- a/modules/git/repo_attribute.go
+++ /dev/null
@@ -1,323 +0,0 @@
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package git
-
-import (
- "bytes"
- "context"
- "fmt"
- "io"
- "os"
-
- "code.gitea.io/gitea/modules/log"
-)
-
-// CheckAttributeOpts represents the possible options to CheckAttribute
-type CheckAttributeOpts struct {
- CachedOnly bool
- AllAttributes bool
- Attributes []string
- Filenames []string
- IndexFile string
- WorkTree string
-}
-
-// CheckAttribute return the Blame object of file
-func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[string]string, error) {
- env := []string{}
-
- if len(opts.IndexFile) > 0 {
- env = append(env, "GIT_INDEX_FILE="+opts.IndexFile)
- }
- if len(opts.WorkTree) > 0 {
- env = append(env, "GIT_WORK_TREE="+opts.WorkTree)
- }
-
- if len(env) > 0 {
- env = append(os.Environ(), env...)
- }
-
- stdOut := new(bytes.Buffer)
- stdErr := new(bytes.Buffer)
-
- cmd := NewCommand(repo.Ctx, "check-attr", "-z")
-
- if opts.AllAttributes {
- cmd.AddArguments("-a")
- } else {
- for _, attribute := range opts.Attributes {
- if attribute != "" {
- cmd.AddDynamicArguments(attribute)
- }
- }
- }
-
- if opts.CachedOnly {
- cmd.AddArguments("--cached")
- }
-
- cmd.AddDashesAndList(opts.Filenames...)
-
- if err := cmd.Run(&RunOpts{
- Env: env,
- Dir: repo.Path,
- Stdout: stdOut,
- Stderr: stdErr,
- }); err != nil {
- return nil, fmt.Errorf("failed to run check-attr: %w\n%s\n%s", err, stdOut.String(), stdErr.String())
- }
-
- // FIXME: This is incorrect on versions < 1.8.5
- fields := bytes.Split(stdOut.Bytes(), []byte{'\000'})
-
- if len(fields)%3 != 1 {
- return nil, fmt.Errorf("wrong number of fields in return from check-attr")
- }
-
- name2attribute2info := make(map[string]map[string]string)
-
- for i := 0; i < (len(fields) / 3); i++ {
- filename := string(fields[3*i])
- attribute := string(fields[3*i+1])
- info := string(fields[3*i+2])
- attribute2info := name2attribute2info[filename]
- if attribute2info == nil {
- attribute2info = make(map[string]string)
- }
- attribute2info[attribute] = info
- name2attribute2info[filename] = attribute2info
- }
-
- return name2attribute2info, nil
-}
-
-// CheckAttributeReader provides a reader for check-attribute content that can be long running
-type CheckAttributeReader struct {
- // params
- Attributes []string
- Repo *Repository
- IndexFile string
- WorkTree string
-
- stdinReader io.ReadCloser
- stdinWriter *os.File
- stdOut attributeWriter
- cmd *Command
- env []string
- ctx context.Context
- cancel context.CancelFunc
-}
-
-// Init initializes the CheckAttributeReader
-func (c *CheckAttributeReader) Init(ctx context.Context) error {
- if len(c.Attributes) == 0 {
- lw := new(nulSeparatedAttributeWriter)
- lw.attributes = make(chan attributeTriple)
- lw.closed = make(chan struct{})
-
- c.stdOut = lw
- c.stdOut.Close()
- return fmt.Errorf("no provided Attributes to check")
- }
-
- c.ctx, c.cancel = context.WithCancel(ctx)
- c.cmd = NewCommand(c.ctx, "check-attr", "--stdin", "-z")
-
- if len(c.IndexFile) > 0 {
- c.cmd.AddArguments("--cached")
- c.env = append(c.env, "GIT_INDEX_FILE="+c.IndexFile)
- }
-
- if len(c.WorkTree) > 0 {
- c.env = append(c.env, "GIT_WORK_TREE="+c.WorkTree)
- }
-
- c.env = append(c.env, "GIT_FLUSH=1")
-
- c.cmd.AddDynamicArguments(c.Attributes...)
-
- var err error
-
- c.stdinReader, c.stdinWriter, err = os.Pipe()
- if err != nil {
- c.cancel()
- return err
- }
-
- lw := new(nulSeparatedAttributeWriter)
- lw.attributes = make(chan attributeTriple, 5)
- lw.closed = make(chan struct{})
- c.stdOut = lw
- return nil
-}
-
-// Run run cmd
-func (c *CheckAttributeReader) Run() error {
- defer func() {
- _ = c.stdinReader.Close()
- _ = c.stdOut.Close()
- }()
- stdErr := new(bytes.Buffer)
- err := c.cmd.Run(&RunOpts{
- Env: c.env,
- Dir: c.Repo.Path,
- Stdin: c.stdinReader,
- Stdout: c.stdOut,
- Stderr: stdErr,
- })
- if err != nil && !IsErrCanceledOrKilled(err) {
- return fmt.Errorf("failed to run attr-check. Error: %w\nStderr: %s", err, stdErr.String())
- }
- return nil
-}
-
-// CheckPath check attr for given path
-func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err error) {
- defer func() {
- if err != nil && err != c.ctx.Err() {
- log.Error("Unexpected error when checking path %s in %s. Error: %v", path, c.Repo.Path, err)
- }
- }()
-
- select {
- case <-c.ctx.Done():
- return nil, c.ctx.Err()
- default:
- }
-
- if _, err = c.stdinWriter.Write([]byte(path + "\x00")); err != nil {
- defer c.Close()
- return nil, err
- }
-
- rs = make(map[string]string)
- for range c.Attributes {
- select {
- case attr, ok := <-c.stdOut.ReadAttribute():
- if !ok {
- return nil, c.ctx.Err()
- }
- rs[attr.Attribute] = attr.Value
- case <-c.ctx.Done():
- return nil, c.ctx.Err()
- }
- }
- return rs, nil
-}
-
-// Close close pip after use
-func (c *CheckAttributeReader) Close() error {
- c.cancel()
- err := c.stdinWriter.Close()
- return err
-}
-
-type attributeWriter interface {
- io.WriteCloser
- ReadAttribute() <-chan attributeTriple
-}
-
-type attributeTriple struct {
- Filename string
- Attribute string
- Value string
-}
-
-type nulSeparatedAttributeWriter struct {
- tmp []byte
- attributes chan attributeTriple
- closed chan struct{}
- working attributeTriple
- pos int
-}
-
-func (wr *nulSeparatedAttributeWriter) Write(p []byte) (n int, err error) {
- l, read := len(p), 0
-
- nulIdx := bytes.IndexByte(p, '\x00')
- for nulIdx >= 0 {
- wr.tmp = append(wr.tmp, p[:nulIdx]...)
- switch wr.pos {
- case 0:
- wr.working = attributeTriple{
- Filename: string(wr.tmp),
- }
- case 1:
- wr.working.Attribute = string(wr.tmp)
- case 2:
- wr.working.Value = string(wr.tmp)
- }
- wr.tmp = wr.tmp[:0]
- wr.pos++
- if wr.pos > 2 {
- wr.attributes <- wr.working
- wr.pos = 0
- }
- read += nulIdx + 1
- if l > read {
- p = p[nulIdx+1:]
- nulIdx = bytes.IndexByte(p, '\x00')
- } else {
- return l, nil
- }
- }
- wr.tmp = append(wr.tmp, p...)
- return len(p), nil
-}
-
-func (wr *nulSeparatedAttributeWriter) ReadAttribute() <-chan attributeTriple {
- return wr.attributes
-}
-
-func (wr *nulSeparatedAttributeWriter) Close() error {
- select {
- case <-wr.closed:
- return nil
- default:
- }
- close(wr.attributes)
- close(wr.closed)
- return nil
-}
-
-// Create a check attribute reader for the current repository and provided commit ID
-func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeReader, context.CancelFunc) {
- indexFilename, worktree, deleteTemporaryFile, err := repo.ReadTreeToTemporaryIndex(commitID)
- if err != nil {
- return nil, func() {}
- }
-
- checker := &CheckAttributeReader{
- Attributes: []string{
- AttributeLinguistVendored,
- AttributeLinguistGenerated,
- AttributeLinguistDocumentation,
- AttributeLinguistDetectable,
- AttributeLinguistLanguage,
- AttributeGitlabLanguage,
- },
- Repo: repo,
- IndexFile: indexFilename,
- WorkTree: worktree,
- }
- ctx, cancel := context.WithCancel(repo.Ctx)
- if err := checker.Init(ctx); err != nil {
- log.Error("Unable to open checker for %s. Error: %v", commitID, err)
- } else {
- go func() {
- err := checker.Run()
- if err != nil && err != ctx.Err() {
- log.Error("Unable to open checker for %s. Error: %v", commitID, err)
- }
- cancel()
- }()
- }
- deferable := func() {
- _ = checker.Close()
- cancel()
- deleteTemporaryFile()
- }
-
- return checker, deferable
-}
diff --git a/modules/git/repo_attribute_test.go b/modules/git/repo_attribute_test.go
deleted file mode 100644
index 0fcd94b4c7..0000000000
--- a/modules/git/repo_attribute_test.go
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package git
-
-import (
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
-)
-
-func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) {
- wr := &nulSeparatedAttributeWriter{
- attributes: make(chan attributeTriple, 5),
- }
-
- testStr := ".gitignore\"\n\x00linguist-vendored\x00unspecified\x00"
-
- n, err := wr.Write([]byte(testStr))
-
- assert.Len(t, testStr, n)
- assert.NoError(t, err)
- select {
- case attr := <-wr.ReadAttribute():
- assert.Equal(t, ".gitignore\"\n", attr.Filename)
- assert.Equal(t, AttributeLinguistVendored, attr.Attribute)
- assert.Equal(t, "unspecified", attr.Value)
- case <-time.After(100 * time.Millisecond):
- assert.FailNow(t, "took too long to read an attribute from the list")
- }
- // Write a second attribute again
- n, err = wr.Write([]byte(testStr))
-
- assert.Len(t, testStr, n)
- assert.NoError(t, err)
-
- select {
- case attr := <-wr.ReadAttribute():
- assert.Equal(t, ".gitignore\"\n", attr.Filename)
- assert.Equal(t, AttributeLinguistVendored, attr.Attribute)
- assert.Equal(t, "unspecified", attr.Value)
- case <-time.After(100 * time.Millisecond):
- assert.FailNow(t, "took too long to read an attribute from the list")
- }
-
- // Write a partial attribute
- _, err = wr.Write([]byte("incomplete-file"))
- assert.NoError(t, err)
- _, err = wr.Write([]byte("name\x00"))
- assert.NoError(t, err)
-
- select {
- case <-wr.ReadAttribute():
- assert.FailNow(t, "There should not be an attribute ready to read")
- case <-time.After(100 * time.Millisecond):
- }
- _, err = wr.Write([]byte("attribute\x00"))
- assert.NoError(t, err)
- select {
- case <-wr.ReadAttribute():
- assert.FailNow(t, "There should not be an attribute ready to read")
- case <-time.After(100 * time.Millisecond):
- }
-
- _, err = wr.Write([]byte("value\x00"))
- assert.NoError(t, err)
-
- attr := <-wr.ReadAttribute()
- assert.Equal(t, "incomplete-filename", attr.Filename)
- assert.Equal(t, "attribute", attr.Attribute)
- assert.Equal(t, "value", attr.Value)
-
- _, err = wr.Write([]byte("shouldbe.vendor\x00linguist-vendored\x00set\x00shouldbe.vendor\x00linguist-generated\x00unspecified\x00shouldbe.vendor\x00linguist-language\x00unspecified\x00"))
- assert.NoError(t, err)
- attr = <-wr.ReadAttribute()
- assert.NoError(t, err)
- assert.EqualValues(t, attributeTriple{
- Filename: "shouldbe.vendor",
- Attribute: AttributeLinguistVendored,
- Value: "set",
- }, attr)
- attr = <-wr.ReadAttribute()
- assert.NoError(t, err)
- assert.EqualValues(t, attributeTriple{
- Filename: "shouldbe.vendor",
- Attribute: AttributeLinguistGenerated,
- Value: "unspecified",
- }, attr)
- attr = <-wr.ReadAttribute()
- assert.NoError(t, err)
- assert.EqualValues(t, attributeTriple{
- Filename: "shouldbe.vendor",
- Attribute: AttributeLinguistLanguage,
- Value: "unspecified",
- }, attr)
-}
diff --git a/modules/git/repo_base_gogit.go b/modules/git/repo_base_gogit.go
index 0ca1ea79c2..293aca159c 100644
--- a/modules/git/repo_base_gogit.go
+++ b/modules/git/repo_base_gogit.go
@@ -49,7 +49,12 @@ func OpenRepository(ctx context.Context, repoPath string) (*Repository, error) {
repoPath, err := filepath.Abs(repoPath)
if err != nil {
return nil, err
- } else if !isDir(repoPath) {
+ }
+ exist, err := util.IsDir(repoPath)
+ if err != nil {
+ return nil, err
+ }
+ if !exist {
return nil, util.NewNotExistErrorf("no such file or directory")
}
diff --git a/modules/git/repo_base_nogogit.go b/modules/git/repo_base_nogogit.go
index 477e3b8742..6f9bfd4b43 100644
--- a/modules/git/repo_base_nogogit.go
+++ b/modules/git/repo_base_nogogit.go
@@ -47,7 +47,12 @@ func OpenRepository(ctx context.Context, repoPath string) (*Repository, error) {
repoPath, err := filepath.Abs(repoPath)
if err != nil {
return nil, err
- } else if !isDir(repoPath) {
+ }
+ exist, err := util.IsDir(repoPath)
+ if err != nil {
+ return nil, err
+ }
+ if !exist {
return nil, util.NewNotExistErrorf("no such file or directory")
}
@@ -62,7 +67,7 @@ func OpenRepository(ctx context.Context, repoPath string) (*Repository, error) {
func (repo *Repository) CatFileBatch(ctx context.Context) (WriteCloserError, *bufio.Reader, func(), error) {
if repo.batch == nil {
var err error
- repo.batch, err = repo.NewBatch(ctx)
+ repo.batch, err = NewBatch(ctx, repo.Path)
if err != nil {
return nil, nil, nil, err
}
@@ -76,7 +81,7 @@ func (repo *Repository) CatFileBatch(ctx context.Context) (WriteCloserError, *bu
}
log.Debug("Opening temporary cat file batch for: %s", repo.Path)
- tempBatch, err := repo.NewBatch(ctx)
+ tempBatch, err := NewBatch(ctx, repo.Path)
if err != nil {
return nil, nil, nil, err
}
@@ -87,7 +92,7 @@ func (repo *Repository) CatFileBatch(ctx context.Context) (WriteCloserError, *bu
func (repo *Repository) CatFileBatchCheck(ctx context.Context) (WriteCloserError, *bufio.Reader, func(), error) {
if repo.check == nil {
var err error
- repo.check, err = repo.NewBatchCheck(ctx)
+ repo.check, err = NewBatchCheck(ctx, repo.Path)
if err != nil {
return nil, nil, nil, err
}
@@ -101,7 +106,7 @@ func (repo *Repository) CatFileBatchCheck(ctx context.Context) (WriteCloserError
}
log.Debug("Opening temporary cat file batch-check for: %s", repo.Path)
- tempBatchCheck, err := repo.NewBatchCheck(ctx)
+ tempBatchCheck, err := NewBatchCheck(ctx, repo.Path)
if err != nil {
return nil, nil, nil, err
}
diff --git a/modules/git/repo_blame.go b/modules/git/repo_blame.go
index 139cdd7be9..6941a76c42 100644
--- a/modules/git/repo_blame.go
+++ b/modules/git/repo_blame.go
@@ -9,10 +9,10 @@ import (
// LineBlame returns the latest commit at the given line
func (repo *Repository) LineBlame(revision, path, file string, line uint) (*Commit, error) {
- res, _, err := NewCommand(repo.Ctx, "blame").
+ res, _, err := NewCommand("blame").
AddOptionFormat("-L %d,%d", line, line).
AddOptionValues("-p", revision).
- AddDashesAndList(file).RunStdString(&RunOpts{Dir: path})
+ AddDashesAndList(file).RunStdString(repo.Ctx, &RunOpts{Dir: path})
if err != nil {
return nil, err
}
diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go
index 552ae2bb8c..e7ecf53f51 100644
--- a/modules/git/repo_branch.go
+++ b/modules/git/repo_branch.go
@@ -7,7 +7,6 @@ package git
import (
"context"
"errors"
- "fmt"
"strings"
)
@@ -16,7 +15,7 @@ const BranchPrefix = "refs/heads/"
// IsReferenceExist returns true if given reference exists in the repository.
func IsReferenceExist(ctx context.Context, repoPath, name string) bool {
- _, _, err := NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name).RunStdString(&RunOpts{Dir: repoPath})
+ _, _, err := NewCommand("show-ref", "--verify").AddDashesAndList(name).RunStdString(ctx, &RunOpts{Dir: repoPath})
return err == nil
}
@@ -25,38 +24,8 @@ func IsBranchExist(ctx context.Context, repoPath, name string) bool {
return IsReferenceExist(ctx, repoPath, BranchPrefix+name)
}
-// Branch represents a Git branch.
-type Branch struct {
- Name string
- Path string
-
- gitRepo *Repository
-}
-
-// GetHEADBranch returns corresponding branch of HEAD.
-func (repo *Repository) GetHEADBranch() (*Branch, error) {
- if repo == nil {
- return nil, fmt.Errorf("nil repo")
- }
- stdout, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD").RunStdString(&RunOpts{Dir: repo.Path})
- if err != nil {
- return nil, err
- }
- stdout = strings.TrimSpace(stdout)
-
- if !strings.HasPrefix(stdout, BranchPrefix) {
- return nil, fmt.Errorf("invalid HEAD branch: %v", stdout)
- }
-
- return &Branch{
- Name: stdout[len(BranchPrefix):],
- Path: stdout,
- gitRepo: repo,
- }, nil
-}
-
func GetDefaultBranch(ctx context.Context, repoPath string) (string, error) {
- stdout, _, err := NewCommand(ctx, "symbolic-ref", "HEAD").RunStdString(&RunOpts{Dir: repoPath})
+ stdout, _, err := NewCommand("symbolic-ref", "HEAD").RunStdString(ctx, &RunOpts{Dir: repoPath})
if err != nil {
return "", err
}
@@ -67,37 +36,6 @@ func GetDefaultBranch(ctx context.Context, repoPath string) (string, error) {
return strings.TrimPrefix(stdout, BranchPrefix), nil
}
-// GetBranch returns a branch by it's name
-func (repo *Repository) GetBranch(branch string) (*Branch, error) {
- if !repo.IsBranchExist(branch) {
- return nil, ErrBranchNotExist{branch}
- }
- return &Branch{
- Path: repo.Path,
- Name: branch,
- gitRepo: repo,
- }, nil
-}
-
-// GetBranches returns a slice of *git.Branch
-func (repo *Repository) GetBranches(skip, limit int) ([]*Branch, int, error) {
- brs, countAll, err := repo.GetBranchNames(skip, limit)
- if err != nil {
- return nil, 0, err
- }
-
- branches := make([]*Branch, len(brs))
- for i := range brs {
- branches[i] = &Branch{
- Path: repo.Path,
- Name: brs[i],
- gitRepo: repo,
- }
- }
-
- return branches, countAll, nil
-}
-
// DeleteBranchOptions Option(s) for delete branch
type DeleteBranchOptions struct {
Force bool
@@ -105,7 +43,7 @@ type DeleteBranchOptions struct {
// DeleteBranch delete a branch by name on repository.
func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) error {
- cmd := NewCommand(repo.Ctx, "branch")
+ cmd := NewCommand("branch")
if opts.Force {
cmd.AddArguments("-D")
@@ -114,46 +52,41 @@ func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) erro
}
cmd.AddDashesAndList(name)
- _, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
+ _, _, err := cmd.RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
return err
}
// CreateBranch create a new branch
func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error {
- cmd := NewCommand(repo.Ctx, "branch")
+ cmd := NewCommand("branch")
cmd.AddDashesAndList(branch, oldbranchOrCommit)
- _, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
+ _, _, err := cmd.RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
return err
}
// AddRemote adds a new remote to repository.
func (repo *Repository) AddRemote(name, url string, fetch bool) error {
- cmd := NewCommand(repo.Ctx, "remote", "add")
+ cmd := NewCommand("remote", "add")
if fetch {
cmd.AddArguments("-f")
}
cmd.AddDynamicArguments(name, url)
- _, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
+ _, _, err := cmd.RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
return err
}
// RemoveRemote removes a remote from repository.
func (repo *Repository) RemoveRemote(name string) error {
- _, _, err := NewCommand(repo.Ctx, "remote", "rm").AddDynamicArguments(name).RunStdString(&RunOpts{Dir: repo.Path})
+ _, _, err := NewCommand("remote", "rm").AddDynamicArguments(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
return err
}
-// GetCommit returns the head commit of a branch
-func (branch *Branch) GetCommit() (*Commit, error) {
- return branch.gitRepo.GetBranchCommit(branch.Name)
-}
-
// RenameBranch rename a branch
func (repo *Repository) RenameBranch(from, to string) error {
- _, _, err := NewCommand(repo.Ctx, "branch", "-m").AddDynamicArguments(from, to).RunStdString(&RunOpts{Dir: repo.Path})
+ _, _, err := NewCommand("branch", "-m").AddDynamicArguments(from, to).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
return err
}
diff --git a/modules/git/repo_branch_gogit.go b/modules/git/repo_branch_gogit.go
index dbc4a5fedc..77aecb21eb 100644
--- a/modules/git/repo_branch_gogit.go
+++ b/modules/git/repo_branch_gogit.go
@@ -57,7 +57,7 @@ func (repo *Repository) IsBranchExist(name string) bool {
// GetBranches returns branches from the repository, skipping "skip" initial branches and
// returning at most "limit" branches, or all branches if "limit" is 0.
-// Branches are returned with sort of `-commiterdate` as the nogogit
+// Branches are returned with sort of `-committerdate` as the nogogit
// implementation. This requires full fetch, sort and then the
// skip/limit applies later as gogit returns in undefined order.
func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
diff --git a/modules/git/repo_branch_nogogit.go b/modules/git/repo_branch_nogogit.go
index 0d2efd4a6b..0d11198523 100644
--- a/modules/git/repo_branch_nogogit.go
+++ b/modules/git/repo_branch_nogogit.go
@@ -109,7 +109,7 @@ func WalkShowRef(ctx context.Context, repoPath string, extraArgs TrustedCmdArgs,
stderrBuilder := &strings.Builder{}
args := TrustedCmdArgs{"for-each-ref", "--format=%(objectname) %(refname)"}
args = append(args, extraArgs...)
- err := NewCommand(ctx, args...).Run(&RunOpts{
+ err := NewCommand(args...).Run(ctx, &RunOpts{
Dir: repoPath,
Stdout: stdoutWriter,
Stderr: stderrBuilder,
diff --git a/modules/git/repo_branch_test.go b/modules/git/repo_branch_test.go
index 5d3b8abb3a..8e8ea16fcd 100644
--- a/modules/git/repo_branch_test.go
+++ b/modules/git/repo_branch_test.go
@@ -21,21 +21,21 @@ func TestRepository_GetBranches(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, branches, 2)
- assert.EqualValues(t, 3, countAll)
+ assert.Equal(t, 3, countAll)
assert.ElementsMatch(t, []string{"master", "branch2"}, branches)
branches, countAll, err = bareRepo1.GetBranchNames(0, 0)
assert.NoError(t, err)
assert.Len(t, branches, 3)
- assert.EqualValues(t, 3, countAll)
+ assert.Equal(t, 3, countAll)
assert.ElementsMatch(t, []string{"master", "branch2", "branch1"}, branches)
branches, countAll, err = bareRepo1.GetBranchNames(5, 1)
assert.NoError(t, err)
assert.Empty(t, branches)
- assert.EqualValues(t, 3, countAll)
+ assert.Equal(t, 3, countAll)
assert.ElementsMatch(t, []string{}, branches)
}
@@ -47,7 +47,7 @@ func BenchmarkRepository_GetBranches(b *testing.B) {
}
defer bareRepo1.Close()
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
_, _, err := bareRepo1.GetBranchNames(0, 0)
if err != nil {
b.Fatal(err)
@@ -71,15 +71,15 @@ func TestGetRefsBySha(t *testing.T) {
// refs/pull/1/head
branches, err = bareRepo5.GetRefsBySha("c83380d7056593c51a699d12b9c00627bd5743e9", PullPrefix)
assert.NoError(t, err)
- assert.EqualValues(t, []string{"refs/pull/1/head"}, branches)
+ assert.Equal(t, []string{"refs/pull/1/head"}, branches)
branches, err = bareRepo5.GetRefsBySha("d8e0bbb45f200e67d9a784ce55bd90821af45ebd", BranchPrefix)
assert.NoError(t, err)
- assert.EqualValues(t, []string{"refs/heads/master", "refs/heads/master-clone"}, branches)
+ assert.Equal(t, []string{"refs/heads/master", "refs/heads/master-clone"}, branches)
branches, err = bareRepo5.GetRefsBySha("58a4bcc53ac13e7ff76127e0fb518b5262bf09af", BranchPrefix)
assert.NoError(t, err)
- assert.EqualValues(t, []string{"refs/heads/test-patch-1"}, branches)
+ assert.Equal(t, []string{"refs/heads/test-patch-1"}, branches)
}
func BenchmarkGetRefsBySha(b *testing.B) {
diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go
index 647894bb21..4066a1ca7b 100644
--- a/modules/git/repo_commit.go
+++ b/modules/git/repo_commit.go
@@ -59,7 +59,7 @@ func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Com
relpath = `\` + relpath
}
- stdout, _, runErr := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat).AddDynamicArguments(id.String()).AddDashesAndList(relpath).RunStdString(&RunOpts{Dir: repo.Path})
+ stdout, _, runErr := NewCommand("log", "-1", prettyLogFormat).AddDynamicArguments(id.String()).AddDashesAndList(relpath).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
if runErr != nil {
return nil, runErr
}
@@ -74,7 +74,7 @@ func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Com
// GetCommitByPath returns the last commit of relative path.
func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
- stdout, _, runErr := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat).AddDashesAndList(relpath).RunStdBytes(&RunOpts{Dir: repo.Path})
+ stdout, _, runErr := NewCommand("log", "-1", prettyLogFormat).AddDashesAndList(relpath).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
if runErr != nil {
return nil, runErr
}
@@ -89,8 +89,9 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
return commits[0], nil
}
-func (repo *Repository) commitsByRange(id ObjectID, page, pageSize int, not string) ([]*Commit, error) {
- cmd := NewCommand(repo.Ctx, "log").
+// commitsByRangeWithTime returns the specific page commits before current revision, with not, since, until support
+func (repo *Repository) commitsByRangeWithTime(id ObjectID, page, pageSize int, not, since, until string) ([]*Commit, error) {
+ cmd := NewCommand("log").
AddOptionFormat("--skip=%d", (page-1)*pageSize).
AddOptionFormat("--max-count=%d", pageSize).
AddArguments(prettyLogFormat).
@@ -99,8 +100,14 @@ func (repo *Repository) commitsByRange(id ObjectID, page, pageSize int, not stri
if not != "" {
cmd.AddOptionValues("--not", not)
}
+ if since != "" {
+ cmd.AddOptionFormat("--since=%s", since)
+ }
+ if until != "" {
+ cmd.AddOptionFormat("--until=%s", until)
+ }
- stdout, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
+ stdout, _, err := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
@@ -134,7 +141,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([
}
// create new git log command with limit of 100 commits
- cmd := NewCommand(repo.Ctx, "log", "-100", prettyLogFormat).AddDynamicArguments(id.String())
+ cmd := NewCommand("log", "-100", prettyLogFormat).AddDynamicArguments(id.String())
// pretend that all refs along with HEAD were listed on command line as <commis>
// https://git-scm.com/docs/git-log#Documentation/git-log.txt---all
@@ -154,7 +161,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([
// search for commits matching given constraints and keywords in commit msg
addCommonSearchArgs(cmd)
- stdout, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
+ stdout, _, err := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
@@ -168,14 +175,14 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([
// ignore anything not matching a valid sha pattern
if id.Type().IsValid(v) {
// create new git log command with 1 commit limit
- hashCmd := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat)
+ hashCmd := NewCommand("log", "-1", prettyLogFormat)
// add previous arguments except for --grep and --all
addCommonSearchArgs(hashCmd)
// add keyword as <commit>
hashCmd.AddDynamicArguments(v)
// search with given constraints for commit matching sha hash of v
- hashMatching, _, err := hashCmd.RunStdBytes(&RunOpts{Dir: repo.Path})
+ hashMatching, _, err := hashCmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil || bytes.Contains(stdout, hashMatching) {
continue
}
@@ -190,7 +197,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([
// FileChangedBetweenCommits Returns true if the file changed between commit IDs id1 and id2
// You must ensure that id1 and id2 are valid commit ids.
func (repo *Repository) FileChangedBetweenCommits(filename, id1, id2 string) (bool, error) {
- stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only", "-z").AddDynamicArguments(id1, id2).AddDashesAndList(filename).RunStdBytes(&RunOpts{Dir: repo.Path})
+ stdout, _, err := NewCommand("diff", "--name-only", "-z").AddDynamicArguments(id1, id2).AddDashesAndList(filename).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return false, err
}
@@ -212,6 +219,8 @@ type CommitsByFileAndRangeOptions struct {
File string
Not string
Page int
+ Since string
+ Until string
}
// CommitsByFileAndRange return the commits according revision file and the page
@@ -223,7 +232,7 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
}()
go func() {
stderr := strings.Builder{}
- gitCmd := NewCommand(repo.Ctx, "rev-list").
+ gitCmd := NewCommand("rev-list").
AddOptionFormat("--max-count=%d", setting.Git.CommitsRangeSize).
AddOptionFormat("--skip=%d", (opts.Page-1)*setting.Git.CommitsRangeSize)
gitCmd.AddDynamicArguments(opts.Revision)
@@ -231,9 +240,15 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
if opts.Not != "" {
gitCmd.AddOptionValues("--not", opts.Not)
}
+ if opts.Since != "" {
+ gitCmd.AddOptionFormat("--since=%s", opts.Since)
+ }
+ if opts.Until != "" {
+ gitCmd.AddOptionFormat("--until=%s", opts.Until)
+ }
gitCmd.AddDashesAndList(opts.File)
- err := gitCmd.Run(&RunOpts{
+ err := gitCmd.Run(repo.Ctx, &RunOpts{
Dir: repo.Path,
Stdout: stdoutWriter,
Stderr: &stderr,
@@ -275,11 +290,11 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
// FilesCountBetween return the number of files changed between two commits
func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) {
- stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(startCommitID + "..." + endCommitID).RunStdString(&RunOpts{Dir: repo.Path})
+ stdout, _, err := NewCommand("diff", "--name-only").AddDynamicArguments(startCommitID+"..."+endCommitID).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil && strings.Contains(err.Error(), "no merge base") {
// git >= 2.28 now returns an error if startCommitID and endCommitID have become unrelated.
// previously it would return the results of git diff --name-only startCommitID endCommitID so let's try that...
- stdout, _, err = NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(startCommitID, endCommitID).RunStdString(&RunOpts{Dir: repo.Path})
+ stdout, _, err = NewCommand("diff", "--name-only").AddDynamicArguments(startCommitID, endCommitID).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
}
if err != nil {
return 0, err
@@ -293,13 +308,13 @@ func (repo *Repository) CommitsBetween(last, before *Commit) ([]*Commit, error)
var stdout []byte
var err error
if before == nil {
- stdout, _, err = NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
+ stdout, _, err = NewCommand("rev-list").AddDynamicArguments(last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
} else {
- stdout, _, err = NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(before.ID.String() + ".." + last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
+ stdout, _, err = NewCommand("rev-list").AddDynamicArguments(before.ID.String()+".."+last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list before last so let's try that...
- stdout, _, err = NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
+ stdout, _, err = NewCommand("rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
}
}
if err != nil {
@@ -313,22 +328,22 @@ func (repo *Repository) CommitsBetweenLimit(last, before *Commit, limit, skip in
var stdout []byte
var err error
if before == nil {
- stdout, _, err = NewCommand(repo.Ctx, "rev-list").
+ stdout, _, err = NewCommand("rev-list").
AddOptionValues("--max-count", strconv.Itoa(limit)).
AddOptionValues("--skip", strconv.Itoa(skip)).
- AddDynamicArguments(last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
+ AddDynamicArguments(last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
} else {
- stdout, _, err = NewCommand(repo.Ctx, "rev-list").
+ stdout, _, err = NewCommand("rev-list").
AddOptionValues("--max-count", strconv.Itoa(limit)).
AddOptionValues("--skip", strconv.Itoa(skip)).
- AddDynamicArguments(before.ID.String() + ".." + last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
+ AddDynamicArguments(before.ID.String()+".."+last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list --max-count n before last so let's try that...
- stdout, _, err = NewCommand(repo.Ctx, "rev-list").
+ stdout, _, err = NewCommand("rev-list").
AddOptionValues("--max-count", strconv.Itoa(limit)).
AddOptionValues("--skip", strconv.Itoa(skip)).
- AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
+ AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
}
}
if err != nil {
@@ -343,13 +358,13 @@ func (repo *Repository) CommitsBetweenNotBase(last, before *Commit, baseBranch s
var stdout []byte
var err error
if before == nil {
- stdout, _, err = NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(&RunOpts{Dir: repo.Path})
+ stdout, _, err = NewCommand("rev-list").AddDynamicArguments(last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
} else {
- stdout, _, err = NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(before.ID.String()+".."+last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(&RunOpts{Dir: repo.Path})
+ stdout, _, err = NewCommand("rev-list").AddDynamicArguments(before.ID.String()+".."+last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list before last so let's try that...
- stdout, _, err = NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(&RunOpts{Dir: repo.Path})
+ stdout, _, err = NewCommand("rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
}
}
if err != nil {
@@ -395,13 +410,13 @@ func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
// commitsBefore the limit is depth, not total number of returned commits.
func (repo *Repository) commitsBefore(id ObjectID, limit int) ([]*Commit, error) {
- cmd := NewCommand(repo.Ctx, "log", prettyLogFormat)
+ cmd := NewCommand("log", prettyLogFormat)
if limit > 0 {
cmd.AddOptionFormat("-%d", limit)
}
cmd.AddDynamicArguments(id.String())
- stdout, _, runErr := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
+ stdout, _, runErr := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
if runErr != nil {
return nil, runErr
}
@@ -438,10 +453,10 @@ func (repo *Repository) getCommitsBeforeLimit(id ObjectID, num int) ([]*Commit,
func (repo *Repository) getBranches(env []string, commitID string, limit int) ([]string, error) {
if DefaultFeatures().CheckVersionAtLeast("2.7.0") {
- stdout, _, err := NewCommand(repo.Ctx, "for-each-ref", "--format=%(refname:strip=2)").
+ stdout, _, err := NewCommand("for-each-ref", "--format=%(refname:strip=2)").
AddOptionFormat("--count=%d", limit).
AddOptionValues("--contains", commitID, BranchPrefix).
- RunStdString(&RunOpts{
+ RunStdString(repo.Ctx, &RunOpts{
Dir: repo.Path,
Env: env,
})
@@ -453,7 +468,7 @@ func (repo *Repository) getBranches(env []string, commitID string, limit int) ([
return branches, nil
}
- stdout, _, err := NewCommand(repo.Ctx, "branch").AddOptionValues("--contains", commitID).RunStdString(&RunOpts{
+ stdout, _, err := NewCommand("branch").AddOptionValues("--contains", commitID).RunStdString(repo.Ctx, &RunOpts{
Dir: repo.Path,
Env: env,
})
@@ -495,7 +510,7 @@ func (repo *Repository) GetCommitsFromIDs(commitIDs []string) []*Commit {
// IsCommitInBranch check if the commit is on the branch
func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err error) {
- stdout, _, err := NewCommand(repo.Ctx, "branch", "--contains").AddDynamicArguments(commitID, branch).RunStdString(&RunOpts{Dir: repo.Path})
+ stdout, _, err := NewCommand("branch", "--contains").AddDynamicArguments(commitID, branch).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return false, err
}
@@ -519,11 +534,12 @@ func (repo *Repository) AddLastCommitCache(cacheKey, fullName, sha string) error
return nil
}
+// GetCommitBranchStart returns the commit where the branch diverged
func (repo *Repository) GetCommitBranchStart(env []string, branch, endCommitID string) (string, error) {
- cmd := NewCommand(repo.Ctx, "log", prettyLogFormat)
+ cmd := NewCommand("log", prettyLogFormat)
cmd.AddDynamicArguments(endCommitID)
- stdout, _, runErr := cmd.RunStdBytes(&RunOpts{
+ stdout, _, runErr := cmd.RunStdBytes(repo.Ctx, &RunOpts{
Dir: repo.Path,
Env: env,
})
@@ -531,21 +547,20 @@ func (repo *Repository) GetCommitBranchStart(env []string, branch, endCommitID s
return "", runErr
}
- parts := bytes.Split(bytes.TrimSpace(stdout), []byte{'\n'})
+ parts := bytes.SplitSeq(bytes.TrimSpace(stdout), []byte{'\n'})
- var startCommitID string
- for _, commitID := range parts {
+ // check the commits one by one until we find a commit contained by another branch
+ // and we think this commit is the divergence point
+ for commitID := range parts {
branches, err := repo.getBranches(env, string(commitID), 2)
if err != nil {
return "", err
}
for _, b := range branches {
if b != branch {
- return startCommitID, nil
+ return string(commitID), nil
}
}
-
- startCommitID = string(commitID)
}
return "", nil
diff --git a/modules/git/repo_commit_gogit.go b/modules/git/repo_commit_gogit.go
index 993013eef7..a88902e209 100644
--- a/modules/git/repo_commit_gogit.go
+++ b/modules/git/repo_commit_gogit.go
@@ -59,7 +59,7 @@ func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) {
}
}
- actualCommitID, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(commitID).RunStdString(&RunOpts{Dir: repo.Path})
+ actualCommitID, _, err := NewCommand("rev-parse", "--verify").AddDynamicArguments(commitID).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
actualCommitID = strings.TrimSpace(actualCommitID)
if err != nil {
if strings.Contains(err.Error(), "unknown revision or path") ||
diff --git a/modules/git/repo_commit_nogogit.go b/modules/git/repo_commit_nogogit.go
index f5ed282a45..3ead3e2216 100644
--- a/modules/git/repo_commit_nogogit.go
+++ b/modules/git/repo_commit_nogogit.go
@@ -16,7 +16,7 @@ import (
// ResolveReference resolves a name to a reference
func (repo *Repository) ResolveReference(name string) (string, error) {
- stdout, _, err := NewCommand(repo.Ctx, "show-ref", "--hash").AddDynamicArguments(name).RunStdString(&RunOpts{Dir: repo.Path})
+ stdout, _, err := NewCommand("show-ref", "--hash").AddDynamicArguments(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
if strings.Contains(err.Error(), "not a valid ref") {
return "", ErrNotExist{name, ""}
@@ -52,13 +52,13 @@ func (repo *Repository) GetRefCommitID(name string) (string, error) {
// SetReference sets the commit ID string of given reference (e.g. branch or tag).
func (repo *Repository) SetReference(name, commitID string) error {
- _, _, err := NewCommand(repo.Ctx, "update-ref").AddDynamicArguments(name, commitID).RunStdString(&RunOpts{Dir: repo.Path})
+ _, _, err := NewCommand("update-ref").AddDynamicArguments(name, commitID).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
return err
}
// RemoveReference removes the given reference (e.g. branch or tag).
func (repo *Repository) RemoveReference(name string) error {
- _, _, err := NewCommand(repo.Ctx, "update-ref", "--no-deref", "-d").AddDynamicArguments(name).RunStdString(&RunOpts{Dir: repo.Path})
+ _, _, err := NewCommand("update-ref", "--no-deref", "-d").AddDynamicArguments(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
return err
}
@@ -68,7 +68,7 @@ func (repo *Repository) IsCommitExist(name string) bool {
log.Error("IsCommitExist: %v", err)
return false
}
- _, _, err := NewCommand(repo.Ctx, "cat-file", "-e").AddDynamicArguments(name).RunStdString(&RunOpts{Dir: repo.Path})
+ _, _, err := NewCommand("cat-file", "-e").AddDynamicArguments(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
return err == nil
}
@@ -81,10 +81,10 @@ func (repo *Repository) getCommit(id ObjectID) (*Commit, error) {
_, _ = wr.Write([]byte(id.String() + "\n"))
- return repo.getCommitFromBatchReader(rd, id)
+ return repo.getCommitFromBatchReader(wr, rd, id)
}
-func (repo *Repository) getCommitFromBatchReader(rd *bufio.Reader, id ObjectID) (*Commit, error) {
+func (repo *Repository) getCommitFromBatchReader(wr WriteCloserError, rd *bufio.Reader, id ObjectID) (*Commit, error) {
_, typ, size, err := ReadBatchLine(rd)
if err != nil {
if errors.Is(err, io.EOF) || IsErrNotExist(err) {
@@ -112,7 +112,11 @@ func (repo *Repository) getCommitFromBatchReader(rd *bufio.Reader, id ObjectID)
return nil, err
}
- commit, err := tag.Commit(repo)
+ if _, err := wr.Write([]byte(tag.Object.String() + "\n")); err != nil {
+ return nil, err
+ }
+
+ commit, err := repo.getCommitFromBatchReader(wr, rd, tag.Object)
if err != nil {
return nil, err
}
diff --git a/modules/git/repo_commitgraph.go b/modules/git/repo_commitgraph.go
index 087d5bcec4..62c6378054 100644
--- a/modules/git/repo_commitgraph.go
+++ b/modules/git/repo_commitgraph.go
@@ -12,7 +12,7 @@ import (
// this requires git v2.18 to be installed
func WriteCommitGraph(ctx context.Context, repoPath string) error {
if DefaultFeatures().CheckVersionAtLeast("2.18") {
- if _, _, err := NewCommand(ctx, "commit-graph", "write").RunStdString(&RunOpts{Dir: repoPath}); err != nil {
+ if _, _, err := NewCommand("commit-graph", "write").RunStdString(ctx, &RunOpts{Dir: repoPath}); err != nil {
return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err)
}
}
diff --git a/modules/git/repo_commitgraph_gogit.go b/modules/git/repo_commitgraph_gogit.go
index d3182f15c6..c0082b62c8 100644
--- a/modules/git/repo_commitgraph_gogit.go
+++ b/modules/git/repo_commitgraph_gogit.go
@@ -8,7 +8,7 @@ package git
import (
"os"
- "path"
+ "path/filepath"
gitealog "code.gitea.io/gitea/modules/log"
@@ -18,7 +18,7 @@ import (
// CommitNodeIndex returns the index for walking commit graph
func (r *Repository) CommitNodeIndex() (cgobject.CommitNodeIndex, *os.File) {
- indexPath := path.Join(r.Path, "objects", "info", "commit-graph")
+ indexPath := filepath.Join(r.Path, "objects", "info", "commit-graph")
file, err := os.Open(indexPath)
if err == nil {
diff --git a/modules/git/repo_compare.go b/modules/git/repo_compare.go
index 877a7ff3b8..ff44506e13 100644
--- a/modules/git/repo_compare.go
+++ b/modules/git/repo_compare.go
@@ -39,13 +39,13 @@ func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, stri
if tmpRemote != "origin" {
tmpBaseName := RemotePrefix + tmpRemote + "/tmp_" + base
// Fetch commit into a temporary branch in order to be able to handle commits and tags
- _, _, err := NewCommand(repo.Ctx, "fetch", "--no-tags").AddDynamicArguments(tmpRemote).AddDashesAndList(base + ":" + tmpBaseName).RunStdString(&RunOpts{Dir: repo.Path})
+ _, _, err := NewCommand("fetch", "--no-tags").AddDynamicArguments(tmpRemote).AddDashesAndList(base+":"+tmpBaseName).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
if err == nil {
base = tmpBaseName
}
}
- stdout, _, err := NewCommand(repo.Ctx, "merge-base").AddDashesAndList(base, head).RunStdString(&RunOpts{Dir: repo.Path})
+ stdout, _, err := NewCommand("merge-base").AddDashesAndList(base, head).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
return strings.TrimSpace(stdout), base, err
}
@@ -94,9 +94,9 @@ func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string,
if !fileOnly {
// avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git <command> [<revision>...] -- [<file>...]'
var logs []byte
- logs, _, err = NewCommand(repo.Ctx, "log").AddArguments(prettyLogFormat).
- AddDynamicArguments(baseCommitID + separator + headBranch).AddArguments("--").
- RunStdBytes(&RunOpts{Dir: repo.Path})
+ logs, _, err = NewCommand("log").AddArguments(prettyLogFormat).
+ AddDynamicArguments(baseCommitID+separator+headBranch).AddArguments("--").
+ RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
@@ -150,8 +150,8 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
}
// avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git <command> [<revision>...] -- [<file>...]'
- if err := NewCommand(repo.Ctx, "diff", "-z", "--name-only").AddDynamicArguments(base + separator + head).AddArguments("--").
- Run(&RunOpts{
+ if err := NewCommand("diff", "-z", "--name-only").AddDynamicArguments(base+separator+head).AddArguments("--").
+ Run(repo.Ctx, &RunOpts{
Dir: repo.Path,
Stdout: w,
Stderr: stderr,
@@ -161,7 +161,7 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
// previously it would return the results of git diff -z --name-only base head so let's try that...
w = &lineCountWriter{}
stderr.Reset()
- if err = NewCommand(repo.Ctx, "diff", "-z", "--name-only").AddDynamicArguments(base, head).AddArguments("--").Run(&RunOpts{
+ if err = NewCommand("diff", "-z", "--name-only").AddDynamicArguments(base, head).AddArguments("--").Run(repo.Ctx, &RunOpts{
Dir: repo.Path,
Stdout: w,
Stderr: stderr,
@@ -174,23 +174,15 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
return w.numLines, nil
}
-// GetDiffShortStat counts number of changed files, number of additions and deletions
-func (repo *Repository) GetDiffShortStat(base, head string) (numFiles, totalAdditions, totalDeletions int, err error) {
- numFiles, totalAdditions, totalDeletions, err = GetDiffShortStat(repo.Ctx, repo.Path, nil, base+"..."+head)
- if err != nil && strings.Contains(err.Error(), "no merge base") {
- return GetDiffShortStat(repo.Ctx, repo.Path, nil, base, head)
- }
- return numFiles, totalAdditions, totalDeletions, err
-}
-
-// GetDiffShortStat counts number of changed files, number of additions and deletions
-func GetDiffShortStat(ctx context.Context, repoPath string, trustedArgs TrustedCmdArgs, dynamicArgs ...string) (numFiles, totalAdditions, totalDeletions int, err error) {
+// GetDiffShortStatByCmdArgs counts number of changed files, number of additions and deletions
+// TODO: it can be merged with another "GetDiffShortStat" in the future
+func GetDiffShortStatByCmdArgs(ctx context.Context, repoPath string, trustedArgs TrustedCmdArgs, dynamicArgs ...string) (numFiles, totalAdditions, totalDeletions int, err error) {
// Now if we call:
// $ git diff --shortstat 1ebb35b98889ff77299f24d82da426b434b0cca0...788b8b1440462d477f45b0088875
// we get:
// " 9902 files changed, 2034198 insertions(+), 298800 deletions(-)\n"
- cmd := NewCommand(ctx, "diff", "--shortstat").AddArguments(trustedArgs...).AddDynamicArguments(dynamicArgs...)
- stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
+ cmd := NewCommand("diff", "--shortstat").AddArguments(trustedArgs...).AddDynamicArguments(dynamicArgs...)
+ stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath})
if err != nil {
return 0, 0, 0, err
}
@@ -236,8 +228,8 @@ func parseDiffStat(stdout string) (numFiles, totalAdditions, totalDeletions int,
// GetDiff generates and returns patch data between given revisions, optimized for human readability
func (repo *Repository) GetDiff(compareArg string, w io.Writer) error {
stderr := new(bytes.Buffer)
- return NewCommand(repo.Ctx, "diff", "-p").AddDynamicArguments(compareArg).
- Run(&RunOpts{
+ return NewCommand("diff", "-p").AddDynamicArguments(compareArg).
+ Run(repo.Ctx, &RunOpts{
Dir: repo.Path,
Stdout: w,
Stderr: stderr,
@@ -246,7 +238,7 @@ func (repo *Repository) GetDiff(compareArg string, w io.Writer) error {
// GetDiffBinary generates and returns patch data between given revisions, including binary diffs.
func (repo *Repository) GetDiffBinary(compareArg string, w io.Writer) error {
- return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--histogram").AddDynamicArguments(compareArg).Run(&RunOpts{
+ return NewCommand("diff", "-p", "--binary", "--histogram").AddDynamicArguments(compareArg).Run(repo.Ctx, &RunOpts{
Dir: repo.Path,
Stdout: w,
})
@@ -255,8 +247,8 @@ func (repo *Repository) GetDiffBinary(compareArg string, w io.Writer) error {
// GetPatch generates and returns format-patch data between given revisions, able to be used with `git apply`
func (repo *Repository) GetPatch(compareArg string, w io.Writer) error {
stderr := new(bytes.Buffer)
- return NewCommand(repo.Ctx, "format-patch", "--binary", "--stdout").AddDynamicArguments(compareArg).
- Run(&RunOpts{
+ return NewCommand("format-patch", "--binary", "--stdout").AddDynamicArguments(compareArg).
+ Run(repo.Ctx, &RunOpts{
Dir: repo.Path,
Stdout: w,
Stderr: stderr,
@@ -271,13 +263,13 @@ func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, err
if err != nil {
return nil, err
}
- cmd := NewCommand(repo.Ctx, "diff-tree", "--name-only", "--root", "--no-commit-id", "-r", "-z")
+ cmd := NewCommand("diff-tree", "--name-only", "--root", "--no-commit-id", "-r", "-z")
if base == objectFormat.EmptyObjectID().String() {
cmd.AddDynamicArguments(head)
} else {
cmd.AddDynamicArguments(base, head)
}
- stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
+ stdout, _, err := cmd.RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
diff --git a/modules/git/repo_gpg.go b/modules/git/repo_gpg.go
index e2b45064fd..0021a7bda7 100644
--- a/modules/git/repo_gpg.go
+++ b/modules/git/repo_gpg.go
@@ -6,6 +6,7 @@ package git
import (
"fmt"
+ "os"
"strings"
"code.gitea.io/gitea/modules/process"
@@ -13,6 +14,14 @@ import (
// LoadPublicKeyContent will load the key from gpg
func (gpgSettings *GPGSettings) LoadPublicKeyContent() error {
+ if gpgSettings.Format == SigningKeyFormatSSH {
+ content, err := os.ReadFile(gpgSettings.KeyID)
+ if err != nil {
+ return fmt.Errorf("unable to read SSH public key file: %s, %w", gpgSettings.KeyID, err)
+ }
+ gpgSettings.PublicKeyContent = string(content)
+ return nil
+ }
content, stderr, err := process.GetManager().Exec(
"gpg -a --export",
"gpg", "-a", "--export", gpgSettings.KeyID)
@@ -33,7 +42,7 @@ func (repo *Repository) GetDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings,
Sign: true,
}
- value, _, _ := NewCommand(repo.Ctx, "config", "--get", "commit.gpgsign").RunStdString(&RunOpts{Dir: repo.Path})
+ value, _, _ := NewCommand("config", "--get", "commit.gpgsign").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
sign, valid := ParseBool(strings.TrimSpace(value))
if !sign || !valid {
gpgSettings.Sign = false
@@ -41,13 +50,16 @@ func (repo *Repository) GetDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings,
return gpgSettings, nil
}
- signingKey, _, _ := NewCommand(repo.Ctx, "config", "--get", "user.signingkey").RunStdString(&RunOpts{Dir: repo.Path})
+ signingKey, _, _ := NewCommand("config", "--get", "user.signingkey").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
gpgSettings.KeyID = strings.TrimSpace(signingKey)
- defaultEmail, _, _ := NewCommand(repo.Ctx, "config", "--get", "user.email").RunStdString(&RunOpts{Dir: repo.Path})
+ format, _, _ := NewCommand("config", "--default", SigningKeyFormatOpenPGP, "--get", "gpg.format").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
+ gpgSettings.Format = strings.TrimSpace(format)
+
+ defaultEmail, _, _ := NewCommand("config", "--get", "user.email").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
gpgSettings.Email = strings.TrimSpace(defaultEmail)
- defaultName, _, _ := NewCommand(repo.Ctx, "config", "--get", "user.name").RunStdString(&RunOpts{Dir: repo.Path})
+ defaultName, _, _ := NewCommand("config", "--get", "user.name").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
gpgSettings.Name = strings.TrimSpace(defaultName)
if err := gpgSettings.LoadPublicKeyContent(); err != nil {
diff --git a/modules/git/repo_index.go b/modules/git/repo_index.go
index f45b6e6191..4879121a41 100644
--- a/modules/git/repo_index.go
+++ b/modules/git/repo_index.go
@@ -10,8 +10,7 @@ import (
"path/filepath"
"strings"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/setting"
)
// ReadTreeToIndex reads a treeish to the index
@@ -22,7 +21,7 @@ func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string)
}
if len(treeish) != objectFormat.FullLength() {
- res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(treeish).RunStdString(&RunOpts{Dir: repo.Path})
+ res, _, err := NewCommand("rev-parse", "--verify").AddDynamicArguments(treeish).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return err
}
@@ -42,7 +41,7 @@ func (repo *Repository) readTreeToIndex(id ObjectID, indexFilename ...string) er
if len(indexFilename) > 0 {
env = append(os.Environ(), "GIT_INDEX_FILE="+indexFilename[0])
}
- _, _, err := NewCommand(repo.Ctx, "read-tree").AddDynamicArguments(id.String()).RunStdString(&RunOpts{Dir: repo.Path, Env: env})
+ _, _, err := NewCommand("read-tree").AddDynamicArguments(id.String()).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path, Env: env})
if err != nil {
return err
}
@@ -59,43 +58,35 @@ func (repo *Repository) ReadTreeToTemporaryIndex(treeish string) (tmpIndexFilena
}
}()
- removeDirFn := func(dir string) func() { // it can't use the return value "tmpDir" directly because it is empty when error occurs
- return func() {
- if err := util.RemoveAll(dir); err != nil {
- log.Error("failed to remove tmp index dir: %v", err)
- }
- }
- }
-
- tmpDir, err = os.MkdirTemp("", "index")
+ tmpDir, cancel, err = setting.AppDataTempDir("git-repo-content").MkdirTempRandom("index")
if err != nil {
return "", "", nil, err
}
tmpIndexFilename = filepath.Join(tmpDir, ".tmp-index")
- cancel = removeDirFn(tmpDir)
+
err = repo.ReadTreeToIndex(treeish, tmpIndexFilename)
if err != nil {
return "", "", cancel, err
}
- return tmpIndexFilename, tmpDir, cancel, err
+ return tmpIndexFilename, tmpDir, cancel, nil
}
// EmptyIndex empties the index
func (repo *Repository) EmptyIndex() error {
- _, _, err := NewCommand(repo.Ctx, "read-tree", "--empty").RunStdString(&RunOpts{Dir: repo.Path})
+ _, _, err := NewCommand("read-tree", "--empty").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
return err
}
// LsFiles checks if the given filenames are in the index
func (repo *Repository) LsFiles(filenames ...string) ([]string, error) {
- cmd := NewCommand(repo.Ctx, "ls-files", "-z").AddDashesAndList(filenames...)
- res, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
+ cmd := NewCommand("ls-files", "-z").AddDashesAndList(filenames...)
+ res, _, err := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
filelist := make([]string, 0, len(filenames))
- for _, line := range bytes.Split(res, []byte{'\000'}) {
+ for line := range bytes.SplitSeq(res, []byte{'\000'}) {
filelist = append(filelist, string(line))
}
@@ -108,7 +99,7 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
if err != nil {
return err
}
- cmd := NewCommand(repo.Ctx, "update-index", "--remove", "-z", "--index-info")
+ cmd := NewCommand("update-index", "--remove", "-z", "--index-info")
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
buffer := new(bytes.Buffer)
@@ -118,7 +109,7 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
buffer.WriteString("0 blob " + objectFormat.EmptyObjectID().String() + "\t" + file + "\000")
}
}
- return cmd.Run(&RunOpts{
+ return cmd.Run(repo.Ctx, &RunOpts{
Dir: repo.Path,
Stdin: bytes.NewReader(buffer.Bytes()),
Stdout: stdout,
@@ -134,7 +125,7 @@ type IndexObjectInfo struct {
// AddObjectsToIndex adds the provided object hashes to the index at the provided filenames
func (repo *Repository) AddObjectsToIndex(objects ...IndexObjectInfo) error {
- cmd := NewCommand(repo.Ctx, "update-index", "--add", "--replace", "-z", "--index-info")
+ cmd := NewCommand("update-index", "--add", "--replace", "-z", "--index-info")
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
buffer := new(bytes.Buffer)
@@ -142,7 +133,7 @@ func (repo *Repository) AddObjectsToIndex(objects ...IndexObjectInfo) error {
// using format: mode SP type SP sha1 TAB path
buffer.WriteString(object.Mode + " blob " + object.Object.String() + "\t" + object.Filename + "\000")
}
- return cmd.Run(&RunOpts{
+ return cmd.Run(repo.Ctx, &RunOpts{
Dir: repo.Path,
Stdin: bytes.NewReader(buffer.Bytes()),
Stdout: stdout,
@@ -157,7 +148,7 @@ func (repo *Repository) AddObjectToIndex(mode string, object ObjectID, filename
// WriteTree writes the current index as a tree to the object db and returns its hash
func (repo *Repository) WriteTree() (*Tree, error) {
- stdout, _, runErr := NewCommand(repo.Ctx, "write-tree").RunStdString(&RunOpts{Dir: repo.Path})
+ stdout, _, runErr := NewCommand("write-tree").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
if runErr != nil {
return nil, runErr
}
diff --git a/modules/git/repo_object.go b/modules/git/repo_object.go
index 3d48b91c6d..08e0413311 100644
--- a/modules/git/repo_object.go
+++ b/modules/git/repo_object.go
@@ -68,13 +68,13 @@ func (repo *Repository) HashObject(reader io.Reader) (ObjectID, error) {
func (repo *Repository) hashObject(reader io.Reader, save bool) (string, error) {
var cmd *Command
if save {
- cmd = NewCommand(repo.Ctx, "hash-object", "-w", "--stdin")
+ cmd = NewCommand("hash-object", "-w", "--stdin")
} else {
- cmd = NewCommand(repo.Ctx, "hash-object", "--stdin")
+ cmd = NewCommand("hash-object", "--stdin")
}
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
- err := cmd.Run(&RunOpts{
+ err := cmd.Run(repo.Ctx, &RunOpts{
Dir: repo.Path,
Stdin: reader,
Stdout: stdout,
@@ -85,17 +85,3 @@ func (repo *Repository) hashObject(reader io.Reader, save bool) (string, error)
}
return strings.TrimSpace(stdout.String()), nil
}
-
-// GetRefType gets the type of the ref based on the string
-func (repo *Repository) GetRefType(ref string) ObjectType {
- if repo.IsTagExist(ref) {
- return ObjectTag
- } else if repo.IsBranchExist(ref) {
- return ObjectBranch
- } else if repo.IsCommitExist(ref) {
- return ObjectCommit
- } else if _, err := repo.GetBlob(ref); err == nil {
- return ObjectBlob
- }
- return ObjectType("invalid")
-}
diff --git a/modules/git/repo_ref.go b/modules/git/repo_ref.go
index 850ec65502..554f9f73e1 100644
--- a/modules/git/repo_ref.go
+++ b/modules/git/repo_ref.go
@@ -18,15 +18,16 @@ func (repo *Repository) GetRefs() ([]*Reference, error) {
// ListOccurrences lists all refs of the given refType the given commit appears in sorted by creation date DESC
// refType should only be a literal "branch" or "tag" and nothing else
func (repo *Repository) ListOccurrences(ctx context.Context, refType, commitSHA string) ([]string, error) {
- cmd := NewCommand(ctx)
- if refType == "branch" {
+ cmd := NewCommand()
+ switch refType {
+ case "branch":
cmd.AddArguments("branch")
- } else if refType == "tag" {
+ case "tag":
cmd.AddArguments("tag")
- } else {
+ default:
return nil, util.NewInvalidArgumentErrorf(`can only use "branch" or "tag" for refType, but got %q`, refType)
}
- stdout, _, err := cmd.AddArguments("--no-color", "--sort=-creatordate", "--contains").AddDynamicArguments(commitSHA).RunStdString(&RunOpts{Dir: repo.Path})
+ stdout, _, err := cmd.AddArguments("--no-color", "--sort=-creatordate", "--contains").AddDynamicArguments(commitSHA).RunStdString(ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
diff --git a/modules/git/repo_ref_nogogit.go b/modules/git/repo_ref_nogogit.go
index ac53d661b5..8d34713eaf 100644
--- a/modules/git/repo_ref_nogogit.go
+++ b/modules/git/repo_ref_nogogit.go
@@ -21,7 +21,7 @@ func (repo *Repository) GetRefsFiltered(pattern string) ([]*Reference, error) {
go func() {
stderrBuilder := &strings.Builder{}
- err := NewCommand(repo.Ctx, "for-each-ref").Run(&RunOpts{
+ err := NewCommand("for-each-ref").Run(repo.Ctx, &RunOpts{
Dir: repo.Path,
Stdout: stdoutWriter,
Stderr: stderrBuilder,
diff --git a/modules/git/repo_stats.go b/modules/git/repo_stats.go
index 83220104bd..8c6f31c38c 100644
--- a/modules/git/repo_stats.go
+++ b/modules/git/repo_stats.go
@@ -40,7 +40,9 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
since := fromTime.Format(time.RFC3339)
- stdout, _, runErr := NewCommand(repo.Ctx, "rev-list", "--count", "--no-merges", "--branches=*", "--date=iso").AddOptionFormat("--since='%s'", since).RunStdString(&RunOpts{Dir: repo.Path})
+ stdout, _, runErr := NewCommand("rev-list", "--count", "--no-merges", "--branches=*", "--date=iso").
+ AddOptionFormat("--since=%s", since).
+ RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
if runErr != nil {
return nil, runErr
}
@@ -60,7 +62,8 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
_ = stdoutWriter.Close()
}()
- gitCmd := NewCommand(repo.Ctx, "log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso").AddOptionFormat("--since='%s'", since)
+ gitCmd := NewCommand("log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso").
+ AddOptionFormat("--since=%s", since)
if len(branch) == 0 {
gitCmd.AddArguments("--branches=*")
} else {
@@ -68,7 +71,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
}
stderr := new(strings.Builder)
- err = gitCmd.Run(&RunOpts{
+ err = gitCmd.Run(repo.Ctx, &RunOpts{
Env: []string{},
Dir: repo.Path,
Stdout: stdoutWriter,
diff --git a/modules/git/repo_stats_test.go b/modules/git/repo_stats_test.go
index 3d032385ee..85d8807a6e 100644
--- a/modules/git/repo_stats_test.go
+++ b/modules/git/repo_stats_test.go
@@ -30,7 +30,7 @@ func TestRepository_GetCodeActivityStats(t *testing.T) {
assert.EqualValues(t, 10, code.Additions)
assert.EqualValues(t, 1, code.Deletions)
assert.Len(t, code.Authors, 3)
- assert.EqualValues(t, "tris.git@shoddynet.org", code.Authors[1].Email)
+ assert.Equal(t, "tris.git@shoddynet.org", code.Authors[1].Email)
assert.EqualValues(t, 3, code.Authors[1].Commits)
assert.EqualValues(t, 5, code.Authors[0].Commits)
}
diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go
index 2026a4c9f5..c8d72eee02 100644
--- a/modules/git/repo_tag.go
+++ b/modules/git/repo_tag.go
@@ -5,7 +5,6 @@
package git
import (
- "context"
"fmt"
"io"
"strings"
@@ -17,20 +16,15 @@ import (
// TagPrefix tags prefix path on the repository
const TagPrefix = "refs/tags/"
-// IsTagExist returns true if given tag exists in the repository.
-func IsTagExist(ctx context.Context, repoPath, name string) bool {
- return IsReferenceExist(ctx, repoPath, TagPrefix+name)
-}
-
// CreateTag create one tag in the repository
func (repo *Repository) CreateTag(name, revision string) error {
- _, _, err := NewCommand(repo.Ctx, "tag").AddDashesAndList(name, revision).RunStdString(&RunOpts{Dir: repo.Path})
+ _, _, err := NewCommand("tag").AddDashesAndList(name, revision).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
return err
}
// CreateAnnotatedTag create one annotated tag in the repository
func (repo *Repository) CreateAnnotatedTag(name, message, revision string) error {
- _, _, err := NewCommand(repo.Ctx, "tag", "-a", "-m").AddDynamicArguments(message).AddDashesAndList(name, revision).RunStdString(&RunOpts{Dir: repo.Path})
+ _, _, err := NewCommand("tag", "-a", "-m").AddDynamicArguments(message).AddDashesAndList(name, revision).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
return err
}
@@ -40,13 +34,13 @@ func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
return "", fmt.Errorf("SHA is too short: %s", sha)
}
- stdout, _, err := NewCommand(repo.Ctx, "show-ref", "--tags", "-d").RunStdString(&RunOpts{Dir: repo.Path})
+ stdout, _, err := NewCommand("show-ref", "--tags", "-d").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return "", err
}
- tagRefs := strings.Split(stdout, "\n")
- for _, tagRef := range tagRefs {
+ tagRefs := strings.SplitSeq(stdout, "\n")
+ for tagRef := range tagRefs {
if len(strings.TrimSpace(tagRef)) > 0 {
fields := strings.Fields(tagRef)
if strings.HasPrefix(fields[0], sha) && strings.HasPrefix(fields[1], TagPrefix) {
@@ -63,12 +57,12 @@ func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
// GetTagID returns the object ID for a tag (annotated tags have both an object SHA AND a commit SHA)
func (repo *Repository) GetTagID(name string) (string, error) {
- stdout, _, err := NewCommand(repo.Ctx, "show-ref", "--tags").AddDashesAndList(name).RunStdString(&RunOpts{Dir: repo.Path})
+ stdout, _, err := NewCommand("show-ref", "--tags").AddDashesAndList(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return "", err
}
// Make sure exact match is used: "v1" != "release/v1"
- for _, line := range strings.Split(stdout, "\n") {
+ for line := range strings.SplitSeq(stdout, "\n") {
fields := strings.Fields(line)
if len(fields) == 2 && fields[1] == "refs/tags/"+name {
return fields[0], nil
@@ -123,9 +117,9 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
rc := &RunOpts{Dir: repo.Path, Stdout: stdoutWriter, Stderr: &stderr}
go func() {
- err := NewCommand(repo.Ctx, "for-each-ref").
+ err := NewCommand("for-each-ref").
AddOptionFormat("--format=%s", forEachRefFmt.Flag()).
- AddArguments("--sort", "-*creatordate", "refs/tags").Run(rc)
+ AddArguments("--sort", "-*creatordate", "refs/tags").Run(repo.Ctx, rc)
if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, stderr.String()))
} else {
diff --git a/modules/git/repo_tag_nogogit.go b/modules/git/repo_tag_nogogit.go
index e0a3104249..3d2b4f52bd 100644
--- a/modules/git/repo_tag_nogogit.go
+++ b/modules/git/repo_tag_nogogit.go
@@ -41,8 +41,11 @@ func (repo *Repository) GetTagType(id ObjectID) (string, error) {
return "", err
}
_, typ, _, err := ReadBatchLine(rd)
- if IsErrNotExist(err) {
- return "", ErrNotExist{ID: id.String()}
+ if err != nil {
+ if IsErrNotExist(err) {
+ return "", ErrNotExist{ID: id.String()}
+ }
+ return "", err
}
return typ, nil
}
diff --git a/modules/git/repo_tag_test.go b/modules/git/repo_tag_test.go
index 0117cb902d..f1f5ff6664 100644
--- a/modules/git/repo_tag_test.go
+++ b/modules/git/repo_tag_test.go
@@ -27,12 +27,12 @@ func TestRepository_GetTags(t *testing.T) {
}
assert.Len(t, tags, 2)
assert.Len(t, tags, total)
- assert.EqualValues(t, "signed-tag", tags[0].Name)
- assert.EqualValues(t, "36f97d9a96457e2bab511db30fe2db03893ebc64", tags[0].ID.String())
- assert.EqualValues(t, "tag", tags[0].Type)
- assert.EqualValues(t, "test", tags[1].Name)
- assert.EqualValues(t, "3ad28a9149a2864384548f3d17ed7f38014c9e8a", tags[1].ID.String())
- assert.EqualValues(t, "tag", tags[1].Type)
+ assert.Equal(t, "signed-tag", tags[0].Name)
+ assert.Equal(t, "36f97d9a96457e2bab511db30fe2db03893ebc64", tags[0].ID.String())
+ assert.Equal(t, "tag", tags[0].Type)
+ assert.Equal(t, "test", tags[1].Name)
+ assert.Equal(t, "3ad28a9149a2864384548f3d17ed7f38014c9e8a", tags[1].ID.String())
+ assert.Equal(t, "tag", tags[1].Type)
}
func TestRepository_GetTag(t *testing.T) {
@@ -64,18 +64,13 @@ func TestRepository_GetTag(t *testing.T) {
// and try to get the Tag for lightweight tag
lTag, err := bareRepo1.GetTag(lTagName)
- if err != nil {
- assert.NoError(t, err)
- return
- }
- if lTag == nil {
- assert.NotNil(t, lTag)
- assert.FailNow(t, "nil lTag: %s", lTagName)
- }
- assert.EqualValues(t, lTagName, lTag.Name)
- assert.EqualValues(t, lTagCommitID, lTag.ID.String())
- assert.EqualValues(t, lTagCommitID, lTag.Object.String())
- assert.EqualValues(t, "commit", lTag.Type)
+ require.NoError(t, err)
+ require.NotNil(t, lTag, "nil lTag: %s", lTagName)
+
+ assert.Equal(t, lTagName, lTag.Name)
+ assert.Equal(t, lTagCommitID, lTag.ID.String())
+ assert.Equal(t, lTagCommitID, lTag.Object.String())
+ assert.Equal(t, "commit", lTag.Type)
// ANNOTATED TAGS
aTagCommitID := "8006ff9adbf0cb94da7dad9e537e53817f9fa5c0"
@@ -97,19 +92,14 @@ func TestRepository_GetTag(t *testing.T) {
}
aTag, err := bareRepo1.GetTag(aTagName)
- if err != nil {
- assert.NoError(t, err)
- return
- }
- if aTag == nil {
- assert.NotNil(t, aTag)
- assert.FailNow(t, "nil aTag: %s", aTagName)
- }
- assert.EqualValues(t, aTagName, aTag.Name)
- assert.EqualValues(t, aTagID, aTag.ID.String())
+ require.NoError(t, err)
+ require.NotNil(t, aTag, "nil aTag: %s", aTagName)
+
+ assert.Equal(t, aTagName, aTag.Name)
+ assert.Equal(t, aTagID, aTag.ID.String())
assert.NotEqual(t, aTagID, aTag.Object.String())
- assert.EqualValues(t, aTagCommitID, aTag.Object.String())
- assert.EqualValues(t, "tag", aTag.Type)
+ assert.Equal(t, aTagCommitID, aTag.Object.String())
+ assert.Equal(t, "tag", aTag.Type)
// RELEASE TAGS
@@ -127,14 +117,14 @@ func TestRepository_GetTag(t *testing.T) {
assert.NoError(t, err)
return
}
- assert.EqualValues(t, rTagCommitID, rTagID)
+ assert.Equal(t, rTagCommitID, rTagID)
oTagID, err := bareRepo1.GetTagID(lTagName)
if err != nil {
assert.NoError(t, err)
return
}
- assert.EqualValues(t, lTagCommitID, oTagID)
+ assert.Equal(t, lTagCommitID, oTagID)
}
func TestRepository_GetAnnotatedTag(t *testing.T) {
@@ -170,9 +160,9 @@ func TestRepository_GetAnnotatedTag(t *testing.T) {
return
}
assert.NotNil(t, tag)
- assert.EqualValues(t, aTagName, tag.Name)
- assert.EqualValues(t, aTagID, tag.ID.String())
- assert.EqualValues(t, "tag", tag.Type)
+ assert.Equal(t, aTagName, tag.Name)
+ assert.Equal(t, aTagID, tag.ID.String())
+ assert.Equal(t, "tag", tag.Type)
// Annotated tag's Commit ID should fail
tag2, err := bareRepo1.GetAnnotatedTag(aTagCommitID)
@@ -182,7 +172,6 @@ func TestRepository_GetAnnotatedTag(t *testing.T) {
// Annotated tag's name should fail
tag3, err := bareRepo1.GetAnnotatedTag(aTagName)
- assert.Error(t, err)
assert.Errorf(t, err, "Length must be 40: %d", len(aTagName))
assert.Nil(t, tag3)
diff --git a/modules/git/repo_test.go b/modules/git/repo_test.go
index 9db78153a1..4638bdac1f 100644
--- a/modules/git/repo_test.go
+++ b/modules/git/repo_test.go
@@ -4,7 +4,6 @@
package git
import (
- "context"
"path/filepath"
"testing"
@@ -33,21 +32,21 @@ func TestRepoIsEmpty(t *testing.T) {
func TestRepoGetDivergingCommits(t *testing.T) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
- do, err := GetDivergingCommits(context.Background(), bareRepo1Path, "master", "branch2")
+ do, err := GetDivergingCommits(t.Context(), bareRepo1Path, "master", "branch2")
assert.NoError(t, err)
assert.Equal(t, DivergeObject{
Ahead: 1,
Behind: 5,
}, do)
- do, err = GetDivergingCommits(context.Background(), bareRepo1Path, "master", "master")
+ do, err = GetDivergingCommits(t.Context(), bareRepo1Path, "master", "master")
assert.NoError(t, err)
assert.Equal(t, DivergeObject{
Ahead: 0,
Behind: 0,
}, do)
- do, err = GetDivergingCommits(context.Background(), bareRepo1Path, "master", "test")
+ do, err = GetDivergingCommits(t.Context(), bareRepo1Path, "master", "test")
assert.NoError(t, err)
assert.Equal(t, DivergeObject{
Ahead: 0,
diff --git a/modules/git/repo_tree.go b/modules/git/repo_tree.go
index ab48d47d13..309a73d759 100644
--- a/modules/git/repo_tree.go
+++ b/modules/git/repo_tree.go
@@ -15,7 +15,7 @@ import (
type CommitTreeOpts struct {
Parents []string
Message string
- KeyID string
+ Key *SigningKey
NoGPGSign bool
AlwaysSign bool
}
@@ -33,7 +33,7 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
"GIT_COMMITTER_EMAIL="+committer.Email,
"GIT_COMMITTER_DATE="+commitTimeStr,
)
- cmd := NewCommand(repo.Ctx, "commit-tree").AddDynamicArguments(tree.ID.String())
+ cmd := NewCommand("commit-tree").AddDynamicArguments(tree.ID.String())
for _, parent := range opts.Parents {
cmd.AddArguments("-p").AddDynamicArguments(parent)
@@ -43,8 +43,13 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
_, _ = messageBytes.WriteString(opts.Message)
_, _ = messageBytes.WriteString("\n")
- if opts.KeyID != "" || opts.AlwaysSign {
- cmd.AddOptionFormat("-S%s", opts.KeyID)
+ if opts.Key != nil {
+ if opts.Key.Format != "" {
+ cmd.AddConfig("gpg.format", opts.Key.Format)
+ }
+ cmd.AddOptionFormat("-S%s", opts.Key.KeyID)
+ } else if opts.AlwaysSign {
+ cmd.AddOptionFormat("-S")
}
if opts.NoGPGSign {
@@ -53,7 +58,7 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
- err := cmd.Run(&RunOpts{
+ err := cmd.Run(repo.Ctx, &RunOpts{
Env: env,
Dir: repo.Path,
Stdin: messageBytes,
diff --git a/modules/git/repo_tree_gogit.go b/modules/git/repo_tree_gogit.go
index 651794a5aa..f77cd83612 100644
--- a/modules/git/repo_tree_gogit.go
+++ b/modules/git/repo_tree_gogit.go
@@ -36,7 +36,7 @@ func (repo *Repository) GetTree(idStr string) (*Tree, error) {
}
if len(idStr) != objectFormat.FullLength() {
- res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(idStr).RunStdString(&RunOpts{Dir: repo.Path})
+ res, _, err := NewCommand("rev-parse", "--verify").AddDynamicArguments(idStr).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
diff --git a/modules/git/repo_tree_nogogit.go b/modules/git/repo_tree_nogogit.go
index d74769ccb2..1954f85162 100644
--- a/modules/git/repo_tree_nogogit.go
+++ b/modules/git/repo_tree_nogogit.go
@@ -35,7 +35,11 @@ func (repo *Repository) getTree(id ObjectID) (*Tree, error) {
if err != nil {
return nil, err
}
- commit, err := tag.Commit(repo)
+
+ if _, err := wr.Write([]byte(tag.Object.String() + "\n")); err != nil {
+ return nil, err
+ }
+ commit, err := repo.getCommitFromBatchReader(wr, rd, tag.Object)
if err != nil {
return nil, err
}
diff --git a/modules/git/signature_test.go b/modules/git/signature_test.go
index 92681feea9..b9b5aff61b 100644
--- a/modules/git/signature_test.go
+++ b/modules/git/signature_test.go
@@ -42,6 +42,6 @@ func TestParseSignatureFromCommitLine(t *testing.T) {
}
for _, test := range tests {
got := parseSignatureFromCommitLine(test.line)
- assert.EqualValues(t, test.want, got)
+ assert.Equal(t, test.want, got)
}
}
diff --git a/modules/git/submodule.go b/modules/git/submodule.go
index 017b644052..31a32f1a9e 100644
--- a/modules/git/submodule.go
+++ b/modules/git/submodule.go
@@ -45,7 +45,7 @@ func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submodul
return scanner.Err()
},
}
- err = NewCommand(ctx, "ls-tree", "-r", "--", "HEAD").Run(opts)
+ err = NewCommand("ls-tree", "-r", "--", "HEAD").Run(ctx, opts)
if err != nil {
return nil, fmt.Errorf("GetTemplateSubmoduleCommits: error running git ls-tree: %v", err)
}
@@ -56,8 +56,8 @@ func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submodul
// It is only for generating new repos based on existing template, requires the .gitmodules file to be already present in the work dir.
func AddTemplateSubmoduleIndexes(ctx context.Context, repoPath string, submodules []TemplateSubmoduleCommit) error {
for _, submodule := range submodules {
- cmd := NewCommand(ctx, "update-index", "--add", "--cacheinfo", "160000").AddDynamicArguments(submodule.Commit, submodule.Path)
- if stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath}); err != nil {
+ cmd := NewCommand("update-index", "--add", "--cacheinfo", "160000").AddDynamicArguments(submodule.Commit, submodule.Path)
+ if stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath}); err != nil {
log.Error("Unable to add %s as submodule to repo %s: stdout %s\nError: %v", submodule.Path, repoPath, stdout, err)
return err
}
diff --git a/modules/git/submodule_test.go b/modules/git/submodule_test.go
index d53946a27d..7893b95e3a 100644
--- a/modules/git/submodule_test.go
+++ b/modules/git/submodule_test.go
@@ -4,7 +4,6 @@
package git
import (
- "context"
"os"
"path/filepath"
"testing"
@@ -20,29 +19,29 @@ func TestGetTemplateSubmoduleCommits(t *testing.T) {
assert.Len(t, submodules, 2)
- assert.EqualValues(t, "<°)))><", submodules[0].Path)
- assert.EqualValues(t, "d2932de67963f23d43e1c7ecf20173e92ee6c43c", submodules[0].Commit)
+ assert.Equal(t, "<°)))><", submodules[0].Path)
+ assert.Equal(t, "d2932de67963f23d43e1c7ecf20173e92ee6c43c", submodules[0].Commit)
- assert.EqualValues(t, "libtest", submodules[1].Path)
- assert.EqualValues(t, "1234567890123456789012345678901234567890", submodules[1].Commit)
+ assert.Equal(t, "libtest", submodules[1].Path)
+ assert.Equal(t, "1234567890123456789012345678901234567890", submodules[1].Commit)
}
func TestAddTemplateSubmoduleIndexes(t *testing.T) {
- ctx := context.Background()
+ ctx := t.Context()
tmpDir := t.TempDir()
var err error
- _, _, err = NewCommand(ctx, "init").RunStdString(&RunOpts{Dir: tmpDir})
+ _, _, err = NewCommand("init").RunStdString(ctx, &RunOpts{Dir: tmpDir})
require.NoError(t, err)
_ = os.Mkdir(filepath.Join(tmpDir, "new-dir"), 0o755)
err = AddTemplateSubmoduleIndexes(ctx, tmpDir, []TemplateSubmoduleCommit{{Path: "new-dir", Commit: "1234567890123456789012345678901234567890"}})
require.NoError(t, err)
- _, _, err = NewCommand(ctx, "add", "--all").RunStdString(&RunOpts{Dir: tmpDir})
+ _, _, err = NewCommand("add", "--all").RunStdString(ctx, &RunOpts{Dir: tmpDir})
require.NoError(t, err)
- _, _, err = NewCommand(ctx, "-c", "user.name=a", "-c", "user.email=b", "commit", "-m=test").RunStdString(&RunOpts{Dir: tmpDir})
+ _, _, err = NewCommand("-c", "user.name=a", "-c", "user.email=b", "commit", "-m=test").RunStdString(ctx, &RunOpts{Dir: tmpDir})
require.NoError(t, err)
submodules, err := GetTemplateSubmoduleCommits(DefaultContext, tmpDir)
require.NoError(t, err)
assert.Len(t, submodules, 1)
- assert.EqualValues(t, "new-dir", submodules[0].Path)
- assert.EqualValues(t, "1234567890123456789012345678901234567890", submodules[0].Commit)
+ assert.Equal(t, "new-dir", submodules[0].Path)
+ assert.Equal(t, "1234567890123456789012345678901234567890", submodules[0].Commit)
}
diff --git a/modules/git/tag.go b/modules/git/tag.go
index f7666aa89b..8bf3658d62 100644
--- a/modules/git/tag.go
+++ b/modules/git/tag.go
@@ -21,11 +21,6 @@ type Tag struct {
Signature *CommitSignature
}
-// Commit return the commit of the tag reference
-func (tag *Tag) Commit(gitRepo *Repository) (*Commit, error) {
- return gitRepo.getCommit(tag.Object)
-}
-
func parsePayloadSignature(data []byte, messageStart int) (payload, msg, sign string) {
pos := messageStart
signStart, signEnd := -1, -1
diff --git a/modules/git/tests/repos/language_stats_repo/config b/modules/git/tests/repos/language_stats_repo/config
index 515f483629..a4ef456cbc 100644
--- a/modules/git/tests/repos/language_stats_repo/config
+++ b/modules/git/tests/repos/language_stats_repo/config
@@ -1,5 +1,5 @@
[core]
repositoryformatversion = 0
filemode = true
- bare = false
+ bare = true
logallrefupdates = true
diff --git a/modules/git/tests/repos/repo3_notes/config b/modules/git/tests/repos/repo3_notes/config
index d545cdabdb..5ed22e23d1 100644
--- a/modules/git/tests/repos/repo3_notes/config
+++ b/modules/git/tests/repos/repo3_notes/config
@@ -1,7 +1,7 @@
[core]
repositoryformatversion = 0
filemode = false
- bare = false
+ bare = true
logallrefupdates = true
symlinks = false
ignorecase = true
diff --git a/modules/git/tests/repos/repo4_commitsbetween/config b/modules/git/tests/repos/repo4_commitsbetween/config
index d545cdabdb..5ed22e23d1 100644
--- a/modules/git/tests/repos/repo4_commitsbetween/config
+++ b/modules/git/tests/repos/repo4_commitsbetween/config
@@ -1,7 +1,7 @@
[core]
repositoryformatversion = 0
filemode = false
- bare = false
+ bare = true
logallrefupdates = true
symlinks = false
ignorecase = true
diff --git a/modules/git/tree.go b/modules/git/tree.go
index 5a644f6c87..38fb45f3b1 100644
--- a/modules/git/tree.go
+++ b/modules/git/tree.go
@@ -48,15 +48,15 @@ func (t *Tree) SubTree(rpath string) (*Tree, error) {
// LsTree checks if the given filenames are in the tree
func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error) {
- cmd := NewCommand(repo.Ctx, "ls-tree", "-z", "--name-only").
+ cmd := NewCommand("ls-tree", "-z", "--name-only").
AddDashesAndList(append([]string{ref}, filenames...)...)
- res, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
+ res, _, err := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
filelist := make([]string, 0, len(filenames))
- for _, line := range bytes.Split(res, []byte{'\000'}) {
+ for line := range bytes.SplitSeq(res, []byte{'\000'}) {
filelist = append(filelist, string(line))
}
@@ -65,9 +65,9 @@ func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error
// GetTreePathLatestCommit returns the latest commit of a tree path
func (repo *Repository) GetTreePathLatestCommit(refName, treePath string) (*Commit, error) {
- stdout, _, err := NewCommand(repo.Ctx, "rev-list", "-1").
+ stdout, _, err := NewCommand("rev-list", "-1").
AddDynamicArguments(refName).AddDashesAndList(treePath).
- RunStdString(&RunOpts{Dir: repo.Path})
+ RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
diff --git a/modules/git/tree_blob_gogit.go b/modules/git/tree_blob_gogit.go
index 92c25cb92c..f29e8f8b9e 100644
--- a/modules/git/tree_blob_gogit.go
+++ b/modules/git/tree_blob_gogit.go
@@ -21,6 +21,7 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
return &TreeEntry{
ID: t.ID,
// Type: ObjectTree,
+ ptree: t,
gogitTreeEntry: &object.TreeEntry{
Name: "",
Mode: filemode.Dir,
diff --git a/modules/git/tree_blob_nogogit.go b/modules/git/tree_blob_nogogit.go
index b7bcf40edd..b18d0fa05e 100644
--- a/modules/git/tree_blob_nogogit.go
+++ b/modules/git/tree_blob_nogogit.go
@@ -11,7 +11,7 @@ import (
)
// GetTreeEntryByPath get the tree entries according the sub dir
-func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
+func (t *Tree) GetTreeEntryByPath(relpath string) (_ *TreeEntry, err error) {
if len(relpath) == 0 {
return &TreeEntry{
ptree: t,
@@ -21,27 +21,25 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
}, nil
}
- // FIXME: This should probably use git cat-file --batch to be a bit more efficient
relpath = path.Clean(relpath)
parts := strings.Split(relpath, "/")
- var err error
+
tree := t
- for i, name := range parts {
- if i == len(parts)-1 {
- entries, err := tree.ListEntries()
- if err != nil {
- return nil, err
- }
- for _, v := range entries {
- if v.Name() == name {
- return v, nil
- }
- }
- } else {
- tree, err = tree.SubTree(name)
- if err != nil {
- return nil, err
- }
+ for _, name := range parts[:len(parts)-1] {
+ tree, err = tree.SubTree(name)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ name := parts[len(parts)-1]
+ entries, err := tree.ListEntries()
+ if err != nil {
+ return nil, err
+ }
+ for _, v := range entries {
+ if v.Name() == name {
+ return v, nil
}
}
return nil, ErrNotExist{"", relpath}
diff --git a/modules/git/tree_entry.go b/modules/git/tree_entry.go
index 9513121487..5099d8ee79 100644
--- a/modules/git/tree_entry.go
+++ b/modules/git/tree_entry.go
@@ -5,9 +5,11 @@
package git
import (
- "io"
+ "path"
"sort"
"strings"
+
+ "code.gitea.io/gitea/modules/util"
)
// Type returns the type of the entry (commit, tree, blob)
@@ -22,83 +24,57 @@ func (te *TreeEntry) Type() string {
}
}
-// FollowLink returns the entry pointed to by a symlink
-func (te *TreeEntry) FollowLink() (*TreeEntry, error) {
+type EntryFollowResult struct {
+ SymlinkContent string
+ TargetFullPath string
+ TargetEntry *TreeEntry
+}
+
+func EntryFollowLink(commit *Commit, fullPath string, te *TreeEntry) (*EntryFollowResult, error) {
if !te.IsLink() {
- return nil, ErrBadLink{te.Name(), "not a symlink"}
+ return nil, util.ErrorWrap(util.ErrUnprocessableContent, "%q is not a symlink", fullPath)
}
- // read the link
- r, err := te.Blob().DataAsync()
- if err != nil {
- return nil, err
+ // git's filename max length is 4096, hopefully a link won't be longer than multiple of that
+ const maxSymlinkSize = 20 * 4096
+ if te.Blob().Size() > maxSymlinkSize {
+ return nil, util.ErrorWrap(util.ErrUnprocessableContent, "%q content exceeds symlink limit", fullPath)
}
- closed := false
- defer func() {
- if !closed {
- _ = r.Close()
- }
- }()
- buf := make([]byte, te.Size())
- _, err = io.ReadFull(r, buf)
+
+ link, err := te.Blob().GetBlobContent(maxSymlinkSize)
if err != nil {
return nil, err
}
- _ = r.Close()
- closed = true
-
- lnk := string(buf)
- t := te.ptree
-
- // traverse up directories
- for ; t != nil && strings.HasPrefix(lnk, "../"); lnk = lnk[3:] {
- t = t.ptree
+ if strings.HasPrefix(link, "/") {
+ // It's said that absolute path will be stored as is in Git
+ return &EntryFollowResult{SymlinkContent: link}, util.ErrorWrap(util.ErrUnprocessableContent, "%q is an absolute symlink", fullPath)
}
- if t == nil {
- return nil, ErrBadLink{te.Name(), "points outside of repo"}
- }
-
- target, err := t.GetTreeEntryByPath(lnk)
+ targetFullPath := path.Join(path.Dir(fullPath), link)
+ targetEntry, err := commit.GetTreeEntryByPath(targetFullPath)
if err != nil {
- if IsErrNotExist(err) {
- return nil, ErrBadLink{te.Name(), "broken link"}
- }
- return nil, err
+ return &EntryFollowResult{SymlinkContent: link}, err
}
- return target, nil
+ return &EntryFollowResult{SymlinkContent: link, TargetFullPath: targetFullPath, TargetEntry: targetEntry}, nil
}
-// FollowLinks returns the entry ultimately pointed to by a symlink
-func (te *TreeEntry) FollowLinks() (*TreeEntry, error) {
- if !te.IsLink() {
- return nil, ErrBadLink{te.Name(), "not a symlink"}
- }
- entry := te
- for i := 0; i < 999; i++ {
- if entry.IsLink() {
- next, err := entry.FollowLink()
- if err != nil {
- return nil, err
- }
- if next.ID == entry.ID {
- return nil, ErrBadLink{
- entry.Name(),
- "recursive link",
- }
- }
- entry = next
- } else {
+func EntryFollowLinks(commit *Commit, firstFullPath string, firstTreeEntry *TreeEntry, optLimit ...int) (res *EntryFollowResult, err error) {
+ limit := util.OptionalArg(optLimit, 10)
+ treeEntry, fullPath := firstTreeEntry, firstFullPath
+ for range limit {
+ res, err = EntryFollowLink(commit, fullPath, treeEntry)
+ if err != nil {
+ return res, err
+ }
+ treeEntry, fullPath = res.TargetEntry, res.TargetFullPath
+ if !treeEntry.IsLink() {
break
}
}
- if entry.IsLink() {
- return nil, ErrBadLink{
- te.Name(),
- "too many levels of symbolic links",
- }
+ if treeEntry.IsLink() {
+ return res, util.ErrorWrap(util.ErrUnprocessableContent, "%q has too many links", firstFullPath)
}
- return entry, nil
+ return res, nil
}
// returns the Tree pointed to by this TreeEntry, or nil if this is not a tree
diff --git a/modules/git/tree_entry_common_test.go b/modules/git/tree_entry_common_test.go
new file mode 100644
index 0000000000..8b63bbb993
--- /dev/null
+++ b/modules/git/tree_entry_common_test.go
@@ -0,0 +1,76 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package git
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/modules/util"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestFollowLink(t *testing.T) {
+ r, err := openRepositoryWithDefaultContext("tests/repos/repo1_bare")
+ require.NoError(t, err)
+ defer r.Close()
+
+ commit, err := r.GetCommit("37991dec2c8e592043f47155ce4808d4580f9123")
+ require.NoError(t, err)
+
+ // get the symlink
+ {
+ lnkFullPath := "foo/bar/link_to_hello"
+ lnk, err := commit.Tree.GetTreeEntryByPath("foo/bar/link_to_hello")
+ require.NoError(t, err)
+ assert.True(t, lnk.IsLink())
+
+ // should be able to dereference to target
+ res, err := EntryFollowLink(commit, lnkFullPath, lnk)
+ require.NoError(t, err)
+ assert.Equal(t, "hello", res.TargetEntry.Name())
+ assert.Equal(t, "foo/nar/hello", res.TargetFullPath)
+ assert.False(t, res.TargetEntry.IsLink())
+ assert.Equal(t, "b14df6442ea5a1b382985a6549b85d435376c351", res.TargetEntry.ID.String())
+ }
+
+ {
+ // should error when called on a normal file
+ entry, err := commit.Tree.GetTreeEntryByPath("file1.txt")
+ require.NoError(t, err)
+ res, err := EntryFollowLink(commit, "file1.txt", entry)
+ assert.ErrorIs(t, err, util.ErrUnprocessableContent)
+ assert.Nil(t, res)
+ }
+
+ {
+ // should error for broken links
+ entry, err := commit.Tree.GetTreeEntryByPath("foo/broken_link")
+ require.NoError(t, err)
+ assert.True(t, entry.IsLink())
+ res, err := EntryFollowLink(commit, "foo/broken_link", entry)
+ assert.ErrorIs(t, err, util.ErrNotExist)
+ assert.Equal(t, "nar/broken_link", res.SymlinkContent)
+ }
+
+ {
+ // should error for external links
+ entry, err := commit.Tree.GetTreeEntryByPath("foo/outside_repo")
+ require.NoError(t, err)
+ assert.True(t, entry.IsLink())
+ res, err := EntryFollowLink(commit, "foo/outside_repo", entry)
+ assert.ErrorIs(t, err, util.ErrNotExist)
+ assert.Equal(t, "../../outside_repo", res.SymlinkContent)
+ }
+
+ {
+ // testing fix for short link bug
+ entry, err := commit.Tree.GetTreeEntryByPath("foo/link_short")
+ require.NoError(t, err)
+ res, err := EntryFollowLink(commit, "foo/link_short", entry)
+ assert.ErrorIs(t, err, util.ErrNotExist)
+ assert.Equal(t, "a", res.SymlinkContent)
+ }
+}
diff --git a/modules/git/tree_entry_gogit.go b/modules/git/tree_entry_gogit.go
index eb9b012681..e6845f1c77 100644
--- a/modules/git/tree_entry_gogit.go
+++ b/modules/git/tree_entry_gogit.go
@@ -19,16 +19,12 @@ type TreeEntry struct {
gogitTreeEntry *object.TreeEntry
ptree *Tree
- size int64
- sized bool
- fullName string
+ size int64
+ sized bool
}
// Name returns the name of the entry
func (te *TreeEntry) Name() string {
- if te.fullName != "" {
- return te.fullName
- }
return te.gogitTreeEntry.Name
}
@@ -55,7 +51,7 @@ func (te *TreeEntry) Size() int64 {
return te.size
}
-// IsSubModule if the entry is a sub module
+// IsSubModule if the entry is a submodule
func (te *TreeEntry) IsSubModule() bool {
return te.gogitTreeEntry.Mode == filemode.Submodule
}
diff --git a/modules/git/tree_entry_mode.go b/modules/git/tree_entry_mode.go
index a399118cf8..f36c07bc2a 100644
--- a/modules/git/tree_entry_mode.go
+++ b/modules/git/tree_entry_mode.go
@@ -3,7 +3,10 @@
package git
-import "strconv"
+import (
+ "fmt"
+ "strconv"
+)
// EntryMode the type of the object in the git tree
type EntryMode int
@@ -11,16 +14,15 @@ type EntryMode int
// There are only a few file modes in Git. They look like unix file modes, but they can only be
// one of these.
const (
- // EntryModeBlob
- EntryModeBlob EntryMode = 0o100644
- // EntryModeExec
- EntryModeExec EntryMode = 0o100755
- // EntryModeSymlink
+ // EntryModeNoEntry is possible if the file was added or removed in a commit. In the case of
+ // when adding the base commit doesn't have the file in its tree, a mode of 0o000000 is used.
+ EntryModeNoEntry EntryMode = 0o000000
+
+ EntryModeBlob EntryMode = 0o100644
+ EntryModeExec EntryMode = 0o100755
EntryModeSymlink EntryMode = 0o120000
- // EntryModeCommit
- EntryModeCommit EntryMode = 0o160000
- // EntryModeTree
- EntryModeTree EntryMode = 0o040000
+ EntryModeCommit EntryMode = 0o160000
+ EntryModeTree EntryMode = 0o040000
)
// String converts an EntryMode to a string
@@ -28,8 +30,46 @@ func (e EntryMode) String() string {
return strconv.FormatInt(int64(e), 8)
}
-// ToEntryMode converts a string to an EntryMode
-func ToEntryMode(value string) EntryMode {
- v, _ := strconv.ParseInt(value, 8, 32)
- return EntryMode(v)
+// IsSubModule if the entry is a submodule
+func (e EntryMode) IsSubModule() bool {
+ return e == EntryModeCommit
+}
+
+// IsDir if the entry is a sub dir
+func (e EntryMode) IsDir() bool {
+ return e == EntryModeTree
+}
+
+// IsLink if the entry is a symlink
+func (e EntryMode) IsLink() bool {
+ return e == EntryModeSymlink
+}
+
+// IsRegular if the entry is a regular file
+func (e EntryMode) IsRegular() bool {
+ return e == EntryModeBlob
+}
+
+// IsExecutable if the entry is an executable file (not necessarily binary)
+func (e EntryMode) IsExecutable() bool {
+ return e == EntryModeExec
+}
+
+func ParseEntryMode(mode string) (EntryMode, error) {
+ switch mode {
+ case "000000":
+ return EntryModeNoEntry, nil
+ case "100644":
+ return EntryModeBlob, nil
+ case "100755":
+ return EntryModeExec, nil
+ case "120000":
+ return EntryModeSymlink, nil
+ case "160000":
+ return EntryModeCommit, nil
+ case "040000", "040755": // git uses 040000 for tree object, but some users may get 040755 for unknown reasons
+ return EntryModeTree, nil
+ default:
+ return 0, fmt.Errorf("unparsable entry mode: %s", mode)
+ }
}
diff --git a/modules/git/tree_entry_nogogit.go b/modules/git/tree_entry_nogogit.go
index 81fb638d56..8fad96cdf8 100644
--- a/modules/git/tree_entry_nogogit.go
+++ b/modules/git/tree_entry_nogogit.go
@@ -18,7 +18,7 @@ type TreeEntry struct {
sized bool
}
-// Name returns the name of the entry
+// Name returns the name of the entry (base name)
func (te *TreeEntry) Name() string {
return te.name
}
@@ -57,29 +57,29 @@ func (te *TreeEntry) Size() int64 {
return te.size
}
-// IsSubModule if the entry is a sub module
+// IsSubModule if the entry is a submodule
func (te *TreeEntry) IsSubModule() bool {
- return te.entryMode == EntryModeCommit
+ return te.entryMode.IsSubModule()
}
// IsDir if the entry is a sub dir
func (te *TreeEntry) IsDir() bool {
- return te.entryMode == EntryModeTree
+ return te.entryMode.IsDir()
}
// IsLink if the entry is a symlink
func (te *TreeEntry) IsLink() bool {
- return te.entryMode == EntryModeSymlink
+ return te.entryMode.IsLink()
}
// IsRegular if the entry is a regular file
func (te *TreeEntry) IsRegular() bool {
- return te.entryMode == EntryModeBlob
+ return te.entryMode.IsRegular()
}
// IsExecutable if the entry is an executable file (not necessarily binary)
func (te *TreeEntry) IsExecutable() bool {
- return te.entryMode == EntryModeExec
+ return te.entryMode.IsExecutable()
}
// Blob returns the blob object the entry
diff --git a/modules/git/tree_entry_test.go b/modules/git/tree_entry_test.go
index 30eee13669..9ca82675e0 100644
--- a/modules/git/tree_entry_test.go
+++ b/modules/git/tree_entry_test.go
@@ -53,50 +53,3 @@ func TestEntriesCustomSort(t *testing.T) {
assert.Equal(t, "bcd", entries[6].Name())
assert.Equal(t, "abc", entries[7].Name())
}
-
-func TestFollowLink(t *testing.T) {
- r, err := openRepositoryWithDefaultContext("tests/repos/repo1_bare")
- assert.NoError(t, err)
- defer r.Close()
-
- commit, err := r.GetCommit("37991dec2c8e592043f47155ce4808d4580f9123")
- assert.NoError(t, err)
-
- // get the symlink
- lnk, err := commit.Tree.GetTreeEntryByPath("foo/bar/link_to_hello")
- assert.NoError(t, err)
- assert.True(t, lnk.IsLink())
-
- // should be able to dereference to target
- target, err := lnk.FollowLink()
- assert.NoError(t, err)
- assert.Equal(t, "hello", target.Name())
- assert.False(t, target.IsLink())
- assert.Equal(t, "b14df6442ea5a1b382985a6549b85d435376c351", target.ID.String())
-
- // should error when called on normal file
- target, err = commit.Tree.GetTreeEntryByPath("file1.txt")
- assert.NoError(t, err)
- _, err = target.FollowLink()
- assert.EqualError(t, err, "file1.txt: not a symlink")
-
- // should error for broken links
- target, err = commit.Tree.GetTreeEntryByPath("foo/broken_link")
- assert.NoError(t, err)
- assert.True(t, target.IsLink())
- _, err = target.FollowLink()
- assert.EqualError(t, err, "broken_link: broken link")
-
- // should error for external links
- target, err = commit.Tree.GetTreeEntryByPath("foo/outside_repo")
- assert.NoError(t, err)
- assert.True(t, target.IsLink())
- _, err = target.FollowLink()
- assert.EqualError(t, err, "outside_repo: points outside of repo")
-
- // testing fix for short link bug
- target, err = commit.Tree.GetTreeEntryByPath("foo/link_short")
- assert.NoError(t, err)
- _, err = target.FollowLink()
- assert.EqualError(t, err, "link_short: broken link")
-}
diff --git a/modules/git/tree_gogit.go b/modules/git/tree_gogit.go
index 421b0ecb0f..272b018ffd 100644
--- a/modules/git/tree_gogit.go
+++ b/modules/git/tree_gogit.go
@@ -69,7 +69,7 @@ func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) {
seen := map[plumbing.Hash]bool{}
walker := object.NewTreeWalker(t.gogitTree, true, seen)
for {
- fullName, entry, err := walker.Next()
+ _, entry, err := walker.Next()
if err == io.EOF {
break
}
@@ -84,7 +84,6 @@ func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) {
ID: ParseGogitHash(entry.Hash),
gogitTreeEntry: &entry,
ptree: t,
- fullName: fullName,
}
entries = append(entries, convertedEntry)
}
diff --git a/modules/git/tree_nogogit.go b/modules/git/tree_nogogit.go
index 993b98edc2..f88788418e 100644
--- a/modules/git/tree_nogogit.go
+++ b/modules/git/tree_nogogit.go
@@ -70,7 +70,7 @@ func (t *Tree) ListEntries() (Entries, error) {
}
}
- stdout, _, runErr := NewCommand(t.repo.Ctx, "ls-tree", "-l").AddDynamicArguments(t.ID.String()).RunStdBytes(&RunOpts{Dir: t.repo.Path})
+ stdout, _, runErr := NewCommand("ls-tree", "-l").AddDynamicArguments(t.ID.String()).RunStdBytes(t.repo.Ctx, &RunOpts{Dir: t.repo.Path})
if runErr != nil {
if strings.Contains(runErr.Error(), "fatal: Not a valid object name") || strings.Contains(runErr.Error(), "fatal: not a tree object") {
return nil, ErrNotExist{
@@ -96,10 +96,10 @@ func (t *Tree) listEntriesRecursive(extraArgs TrustedCmdArgs) (Entries, error) {
return t.entriesRecursive, nil
}
- stdout, _, runErr := NewCommand(t.repo.Ctx, "ls-tree", "-t", "-r").
+ stdout, _, runErr := NewCommand("ls-tree", "-t", "-r").
AddArguments(extraArgs...).
AddDynamicArguments(t.ID.String()).
- RunStdBytes(&RunOpts{Dir: t.repo.Path})
+ RunStdBytes(t.repo.Ctx, &RunOpts{Dir: t.repo.Path})
if runErr != nil {
return nil, runErr
}
diff --git a/modules/git/tree_test.go b/modules/git/tree_test.go
index 5fee64b038..cae11c4b1b 100644
--- a/modules/git/tree_test.go
+++ b/modules/git/tree_test.go
@@ -19,7 +19,7 @@ func TestSubTree_Issue29101(t *testing.T) {
assert.NoError(t, err)
// old code could produce a different error if called multiple times
- for i := 0; i < 10; i++ {
+ for range 10 {
_, err = commit.SubTree("file1.txt")
assert.Error(t, err)
assert.True(t, IsErrNotExist(err))
@@ -33,10 +33,10 @@ func Test_GetTreePathLatestCommit(t *testing.T) {
commitID, err := repo.GetBranchCommitID("master")
assert.NoError(t, err)
- assert.EqualValues(t, "544d8f7a3b15927cddf2299b4b562d6ebd71b6a7", commitID)
+ assert.Equal(t, "544d8f7a3b15927cddf2299b4b562d6ebd71b6a7", commitID)
commit, err := repo.GetTreePathLatestCommit("master", "blame.txt")
assert.NoError(t, err)
assert.NotNil(t, commit)
- assert.EqualValues(t, "45fb6cbc12f970b04eacd5cd4165edd11c8d7376", commit.ID.String())
+ assert.Equal(t, "45fb6cbc12f970b04eacd5cd4165edd11c8d7376", commit.ID.String())
}
diff --git a/modules/git/url/url.go b/modules/git/url/url.go
index 637685183e..aa6fa31c5e 100644
--- a/modules/git/url/url.go
+++ b/modules/git/url/url.go
@@ -4,9 +4,15 @@
package url
import (
+ "context"
"fmt"
+ "net"
stdurl "net/url"
"strings"
+
+ "code.gitea.io/gitea/modules/httplib"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
)
// ErrWrongURLFormat represents an error with wrong url format
@@ -21,7 +27,7 @@ func (err ErrWrongURLFormat) Error() string {
// GitURL represents a git URL
type GitURL struct {
*stdurl.URL
- extraMark int // 0 no extra 1 scp 2 file path with no prefix
+ extraMark int // 0: standard URL with scheme, 1: scp short syntax (no scheme), 2: file path with no prefix
}
// String returns the URL's string
@@ -38,8 +44,11 @@ func (u *GitURL) String() string {
}
}
-// Parse parse all kinds of git URL
-func Parse(remote string) (*GitURL, error) {
+// ParseGitURL parse all kinds of git URL:
+// * Full URL: http://git@host/path, http://git@host:port/path
+// * SCP short syntax: git@host:/path
+// * File path: /dir/repo/path
+func ParseGitURL(remote string) (*GitURL, error) {
if strings.Contains(remote, "://") {
u, err := stdurl.Parse(remote)
if err != nil {
@@ -87,3 +96,88 @@ func Parse(remote string) (*GitURL, error) {
extraMark: 2,
}, nil
}
+
+type RepositoryURL struct {
+ GitURL *GitURL
+
+ // if the URL belongs to current Gitea instance, then the below fields have values
+ OwnerName string
+ RepoName string
+ RemainingPath string
+}
+
+// ParseRepositoryURL tries to parse a Git URL and extract the owner/repository name if it belongs to current Gitea instance.
+func ParseRepositoryURL(ctx context.Context, repoURL string) (*RepositoryURL, error) {
+ // possible urls for git:
+ // https://my.domain/sub-path/<owner>/<repo>[.git]
+ // git+ssh://user@my.domain/<owner>/<repo>[.git]
+ // ssh://user@my.domain/<owner>/<repo>[.git]
+ // user@my.domain:<owner>/<repo>[.git]
+ parsed, err := ParseGitURL(repoURL)
+ if err != nil {
+ return nil, err
+ }
+
+ ret := &RepositoryURL{}
+ ret.GitURL = parsed
+
+ fillPathParts := func(s string) {
+ s = strings.TrimPrefix(s, "/")
+ fields := strings.SplitN(s, "/", 3)
+ if len(fields) >= 2 {
+ ret.OwnerName = fields[0]
+ ret.RepoName = strings.TrimSuffix(fields[1], ".git")
+ if len(fields) == 3 {
+ ret.RemainingPath = "/" + fields[2]
+ }
+ }
+ }
+
+ switch parsed.URL.Scheme {
+ case "http", "https":
+ if !httplib.IsCurrentGiteaSiteURL(ctx, repoURL) {
+ return ret, nil
+ }
+ fillPathParts(strings.TrimPrefix(parsed.URL.Path, setting.AppSubURL))
+ case "ssh", "git+ssh":
+ domainSSH := setting.SSH.Domain
+ domainCur := httplib.GuessCurrentHostDomain(ctx)
+ urlDomain, _, _ := net.SplitHostPort(parsed.URL.Host)
+ urlDomain = util.IfZero(urlDomain, parsed.URL.Host)
+ if urlDomain == "" {
+ return ret, nil
+ }
+ // check whether URL domain is the App domain
+ domainMatches := domainSSH == urlDomain
+ // check whether URL domain is current domain from context
+ domainMatches = domainMatches || (domainCur != "" && domainCur == urlDomain)
+ if domainMatches {
+ fillPathParts(parsed.URL.Path)
+ }
+ }
+ return ret, nil
+}
+
+// MakeRepositoryWebLink generates a web link (http/https) for a git repository (by guessing sometimes)
+func MakeRepositoryWebLink(repoURL *RepositoryURL) string {
+ if repoURL.OwnerName != "" {
+ return setting.AppSubURL + "/" + repoURL.OwnerName + "/" + repoURL.RepoName
+ }
+
+ // now, let's guess, for example:
+ // * git@github.com:owner/submodule.git
+ // * https://github.com/example/submodule1.git
+ switch repoURL.GitURL.Scheme {
+ case "http", "https":
+ return strings.TrimSuffix(repoURL.GitURL.String(), ".git")
+ case "ssh", "git+ssh":
+ hostname, _, _ := net.SplitHostPort(repoURL.GitURL.Host)
+ hostname = util.IfZero(hostname, repoURL.GitURL.Host)
+ urlPath := strings.TrimSuffix(repoURL.GitURL.Path, ".git")
+ urlPath = strings.TrimPrefix(urlPath, "/")
+ urlFull := fmt.Sprintf("https://%s/%s", hostname, urlPath)
+ urlFull = strings.TrimSuffix(urlFull, "/")
+ return urlFull
+ }
+ return ""
+}
diff --git a/modules/git/url/url_test.go b/modules/git/url/url_test.go
index da820ed889..6655c20be3 100644
--- a/modules/git/url/url_test.go
+++ b/modules/git/url/url_test.go
@@ -4,9 +4,15 @@
package url
import (
+ "context"
+ "net/http"
"net/url"
"testing"
+ "code.gitea.io/gitea/modules/httplib"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
+
"github.com/stretchr/testify/assert"
)
@@ -157,10 +163,105 @@ func TestParseGitURLs(t *testing.T) {
for _, kase := range kases {
t.Run(kase.kase, func(t *testing.T) {
- u, err := Parse(kase.kase)
+ u, err := ParseGitURL(kase.kase)
assert.NoError(t, err)
- assert.EqualValues(t, kase.expected.extraMark, u.extraMark)
- assert.EqualValues(t, *kase.expected, *u)
+ assert.Equal(t, kase.expected.extraMark, u.extraMark)
+ assert.Equal(t, *kase.expected, *u)
+ })
+ }
+}
+
+func TestParseRepositoryURL(t *testing.T) {
+ defer test.MockVariableValue(&setting.AppURL, "https://localhost:3000")()
+ defer test.MockVariableValue(&setting.SSH.Domain, "try.gitea.io")()
+
+ ctxURL, _ := url.Parse("https://gitea")
+ ctxReq := &http.Request{URL: ctxURL, Header: http.Header{}}
+ ctxReq.Host = ctxURL.Host
+ ctxReq.Header.Add("X-Forwarded-Proto", ctxURL.Scheme)
+ ctx := context.WithValue(t.Context(), httplib.RequestContextKey, ctxReq)
+ cases := []struct {
+ input string
+ ownerName, repoName, remaining string
+ }{
+ {input: "/user/repo"},
+
+ {input: "https://localhost:3000/user/repo", ownerName: "user", repoName: "repo"},
+ {input: "https://external:3000/user/repo"},
+
+ {input: "https://localhost:3000/user/repo.git/other", ownerName: "user", repoName: "repo", remaining: "/other"},
+
+ {input: "https://gitea/user/repo", ownerName: "user", repoName: "repo"},
+ {input: "https://gitea:3333/user/repo"},
+
+ {input: "ssh://try.gitea.io:2222/user/repo", ownerName: "user", repoName: "repo"},
+ {input: "ssh://external:2222/user/repo"},
+
+ {input: "git+ssh://user@try.gitea.io/user/repo.git", ownerName: "user", repoName: "repo"},
+ {input: "git+ssh://user@external/user/repo.git"},
+
+ {input: "root@try.gitea.io:user/repo.git", ownerName: "user", repoName: "repo"},
+ {input: "root@gitea:user/repo.git", ownerName: "user", repoName: "repo"},
+ {input: "root@external:user/repo.git"},
+ }
+
+ for _, c := range cases {
+ t.Run(c.input, func(t *testing.T) {
+ ret, _ := ParseRepositoryURL(ctx, c.input)
+ assert.Equal(t, c.ownerName, ret.OwnerName)
+ assert.Equal(t, c.repoName, ret.RepoName)
+ assert.Equal(t, c.remaining, ret.RemainingPath)
})
}
+
+ t.Run("WithSubpath", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.AppURL, "https://localhost:3000/subpath")()
+ defer test.MockVariableValue(&setting.AppSubURL, "/subpath")()
+ cases = []struct {
+ input string
+ ownerName, repoName, remaining string
+ }{
+ {input: "https://localhost:3000/user/repo"},
+ {input: "https://localhost:3000/subpath/user/repo.git/other", ownerName: "user", repoName: "repo", remaining: "/other"},
+
+ {input: "ssh://try.gitea.io:2222/user/repo", ownerName: "user", repoName: "repo"},
+ {input: "ssh://external:2222/user/repo"},
+
+ {input: "git+ssh://user@try.gitea.io/user/repo.git", ownerName: "user", repoName: "repo"},
+ {input: "git+ssh://user@external/user/repo.git"},
+
+ {input: "root@try.gitea.io:user/repo.git", ownerName: "user", repoName: "repo"},
+ {input: "root@external:user/repo.git"},
+ }
+
+ for _, c := range cases {
+ t.Run(c.input, func(t *testing.T) {
+ ret, _ := ParseRepositoryURL(ctx, c.input)
+ assert.Equal(t, c.ownerName, ret.OwnerName)
+ assert.Equal(t, c.repoName, ret.RepoName)
+ assert.Equal(t, c.remaining, ret.RemainingPath)
+ })
+ }
+ })
+}
+
+func TestMakeRepositoryBaseLink(t *testing.T) {
+ defer test.MockVariableValue(&setting.AppURL, "https://localhost:3000/subpath")()
+ defer test.MockVariableValue(&setting.AppSubURL, "/subpath")()
+
+ u, err := ParseRepositoryURL(t.Context(), "https://localhost:3000/subpath/user/repo.git")
+ assert.NoError(t, err)
+ assert.Equal(t, "/subpath/user/repo", MakeRepositoryWebLink(u))
+
+ u, err = ParseRepositoryURL(t.Context(), "https://github.com/owner/repo.git")
+ assert.NoError(t, err)
+ assert.Equal(t, "https://github.com/owner/repo", MakeRepositoryWebLink(u))
+
+ u, err = ParseRepositoryURL(t.Context(), "git@github.com:owner/repo.git")
+ assert.NoError(t, err)
+ assert.Equal(t, "https://github.com/owner/repo", MakeRepositoryWebLink(u))
+
+ u, err = ParseRepositoryURL(t.Context(), "git+ssh://other:123/owner/repo.git")
+ assert.NoError(t, err)
+ assert.Equal(t, "https://other/owner/repo", MakeRepositoryWebLink(u))
}
diff --git a/modules/git/utils.go b/modules/git/utils.go
index 56cba9087a..897306efd0 100644
--- a/modules/git/utils.go
+++ b/modules/git/utils.go
@@ -8,7 +8,6 @@ import (
"encoding/hex"
"fmt"
"io"
- "os"
"strconv"
"strings"
"sync"
@@ -41,33 +40,6 @@ func (oc *ObjectCache[T]) Get(id string) (T, bool) {
return obj, has
}
-// isDir returns true if given path is a directory,
-// or returns false when it's a file or does not exist.
-func isDir(dir string) bool {
- f, e := os.Stat(dir)
- if e != nil {
- return false
- }
- return f.IsDir()
-}
-
-// isFile returns true if given path is a file,
-// or returns false when it's a directory or does not exist.
-func isFile(filePath string) bool {
- f, e := os.Stat(filePath)
- if e != nil {
- return false
- }
- return !f.IsDir()
-}
-
-// isExist checks whether a file or directory exists.
-// It returns false when the file or directory does not exist.
-func isExist(path string) bool {
- _, err := os.Stat(path)
- return err == nil || os.IsExist(err)
-}
-
// ConcatenateError concatenats an error with stderr string
func ConcatenateError(err error, stderr string) error {
if len(stderr) == 0 {
diff --git a/modules/gitrepo/branch.go b/modules/gitrepo/branch.go
index e13a4c82e1..d7857819e4 100644
--- a/modules/gitrepo/branch.go
+++ b/modules/gitrepo/branch.go
@@ -11,14 +11,14 @@ import (
// GetBranchesByPath returns a branch by its path
// if limit = 0 it will not limit
-func GetBranchesByPath(ctx context.Context, repo Repository, skip, limit int) ([]*git.Branch, int, error) {
+func GetBranchesByPath(ctx context.Context, repo Repository, skip, limit int) ([]string, int, error) {
gitRepo, err := OpenRepository(ctx, repo)
if err != nil {
return nil, 0, err
}
defer gitRepo.Close()
- return gitRepo.GetBranches(skip, limit)
+ return gitRepo.GetBranchNames(skip, limit)
}
func GetBranchCommitID(ctx context.Context, repo Repository, branch string) (string, error) {
@@ -33,9 +33,9 @@ func GetBranchCommitID(ctx context.Context, repo Repository, branch string) (str
// SetDefaultBranch sets default branch of repository.
func SetDefaultBranch(ctx context.Context, repo Repository, name string) error {
- _, _, err := git.NewCommand(ctx, "symbolic-ref", "HEAD").
- AddDynamicArguments(git.BranchPrefix + name).
- RunStdString(&git.RunOpts{Dir: repoPath(repo)})
+ _, _, err := git.NewCommand("symbolic-ref", "HEAD").
+ AddDynamicArguments(git.BranchPrefix+name).
+ RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)})
return err
}
@@ -44,6 +44,12 @@ func GetDefaultBranch(ctx context.Context, repo Repository) (string, error) {
return git.GetDefaultBranch(ctx, repoPath(repo))
}
-func GetWikiDefaultBranch(ctx context.Context, repo Repository) (string, error) {
- return git.GetDefaultBranch(ctx, wikiPath(repo))
+// IsReferenceExist returns true if given reference exists in the repository.
+func IsReferenceExist(ctx context.Context, repo Repository, name string) bool {
+ return git.IsReferenceExist(ctx, repoPath(repo), name)
+}
+
+// IsBranchExist returns true if given branch exists in the repository.
+func IsBranchExist(ctx context.Context, repo Repository, name string) bool {
+ return IsReferenceExist(ctx, repo, git.BranchPrefix+name)
}
diff --git a/modules/gitrepo/gitrepo.go b/modules/gitrepo/gitrepo.go
index 540b724489..5da65e2452 100644
--- a/modules/gitrepo/gitrepo.go
+++ b/modules/gitrepo/gitrepo.go
@@ -5,9 +5,9 @@ package gitrepo
import (
"context"
+ "fmt"
"io"
"path/filepath"
- "strings"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/reqctx"
@@ -15,17 +15,15 @@ import (
"code.gitea.io/gitea/modules/util"
)
+// Repository represents a git repository which stored in a disk
type Repository interface {
- GetName() string
- GetOwnerName() string
+ RelativePath() string // We don't assume how the directory structure of the repository is, so we only need the relative path
}
+// RelativePath should be an unix style path like username/reponame.git
+// This method should change it according to the current OS.
func repoPath(repo Repository) string {
- return filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".git")
-}
-
-func wikiPath(repo Repository) string {
- return filepath.Join(setting.RepoRootPath, strings.ToLower(repo.GetOwnerName()), strings.ToLower(repo.GetName())+".wiki.git")
+ return filepath.Join(setting.RepoRootPath, filepath.FromSlash(repo.RelativePath()))
}
// OpenRepository opens the repository at the given relative path with the provided context.
@@ -33,10 +31,6 @@ func OpenRepository(ctx context.Context, repo Repository) (*git.Repository, erro
return git.OpenRepository(ctx, repoPath(repo))
}
-func OpenWikiRepository(ctx context.Context, repo Repository) (*git.Repository, error) {
- return git.OpenRepository(ctx, wikiPath(repo))
-}
-
// contextKey is a value for use with context.WithValue.
type contextKey struct {
repoPath string
@@ -69,3 +63,21 @@ func RepositoryFromRequestContextOrOpen(ctx reqctx.RequestContext, repo Reposito
ctx.SetContextValue(ck, gitRepo)
return gitRepo, nil
}
+
+// IsRepositoryExist returns true if the repository directory exists in the disk
+func IsRepositoryExist(ctx context.Context, repo Repository) (bool, error) {
+ return util.IsExist(repoPath(repo))
+}
+
+// DeleteRepository deletes the repository directory from the disk
+func DeleteRepository(ctx context.Context, repo Repository) error {
+ return util.RemoveAll(repoPath(repo))
+}
+
+// RenameRepository renames a repository's name on disk
+func RenameRepository(ctx context.Context, repo, newRepo Repository) error {
+ if err := util.Rename(repoPath(repo), repoPath(newRepo)); err != nil {
+ return fmt.Errorf("rename repository directory: %w", err)
+ }
+ return nil
+}
diff --git a/modules/repository/hooks.go b/modules/gitrepo/hooks.go
index 95849789ab..d9d4a88ff1 100644
--- a/modules/repository/hooks.go
+++ b/modules/gitrepo/hooks.go
@@ -1,9 +1,10 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package repository
+package gitrepo
import (
+ "context"
"fmt"
"os"
"path/filepath"
@@ -106,9 +107,12 @@ done
}
// CreateDelegateHooks creates all the hooks scripts for the repo
-func CreateDelegateHooks(repoPath string) (err error) {
+func CreateDelegateHooks(_ context.Context, repo Repository) (err error) {
+ return createDelegateHooks(filepath.Join(repoPath(repo), "hooks"))
+}
+
+func createDelegateHooks(hookDir string) (err error) {
hookNames, hookTpls, giteaHookTpls := getHookTemplates()
- hookDir := filepath.Join(repoPath, "hooks")
for i, hookName := range hookNames {
oldHookPath := filepath.Join(hookDir, hookName)
@@ -170,10 +174,13 @@ func ensureExecutable(filename string) error {
}
// CheckDelegateHooks checks the hooks scripts for the repo
-func CheckDelegateHooks(repoPath string) ([]string, error) {
+func CheckDelegateHooks(_ context.Context, repo Repository) ([]string, error) {
+ return checkDelegateHooks(filepath.Join(repoPath(repo), "hooks"))
+}
+
+func checkDelegateHooks(hookDir string) ([]string, error) {
hookNames, hookTpls, giteaHookTpls := getHookTemplates()
- hookDir := filepath.Join(repoPath, "hooks")
results := make([]string, 0, 10)
for i, hookName := range hookNames {
diff --git a/modules/gitrepo/tag.go b/modules/gitrepo/tag.go
new file mode 100644
index 0000000000..58ed204a99
--- /dev/null
+++ b/modules/gitrepo/tag.go
@@ -0,0 +1,15 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package gitrepo
+
+import (
+ "context"
+
+ "code.gitea.io/gitea/modules/git"
+)
+
+// IsTagExist returns true if given tag exists in the repository.
+func IsTagExist(ctx context.Context, repo Repository, name string) bool {
+ return IsReferenceExist(ctx, repo, git.TagPrefix+name)
+}
diff --git a/modules/globallock/globallock_test.go b/modules/globallock/globallock_test.go
index f14c7d656b..8d55d9f699 100644
--- a/modules/globallock/globallock_test.go
+++ b/modules/globallock/globallock_test.go
@@ -66,11 +66,11 @@ func TestLockAndDo(t *testing.T) {
func testLockAndDo(t *testing.T) {
const concurrency = 50
- ctx := context.Background()
+ ctx := t.Context()
count := 0
wg := sync.WaitGroup{}
wg.Add(concurrency)
- for i := 0; i < concurrency; i++ {
+ for range concurrency {
go func() {
defer wg.Done()
err := LockAndDo(ctx, "test", func(ctx context.Context) error {
diff --git a/modules/globallock/locker_test.go b/modules/globallock/locker_test.go
index bee4d34b34..c9e73c25d2 100644
--- a/modules/globallock/locker_test.go
+++ b/modules/globallock/locker_test.go
@@ -46,14 +46,14 @@ func TestLocker(t *testing.T) {
func testLocker(t *testing.T, locker Locker) {
t.Run("lock", func(t *testing.T) {
- parentCtx := context.Background()
+ parentCtx := t.Context()
release, err := locker.Lock(parentCtx, "test")
defer release()
assert.NoError(t, err)
func() {
- ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Second)
defer cancel()
release, err := locker.Lock(ctx, "test")
defer release()
@@ -64,7 +64,7 @@ func testLocker(t *testing.T, locker Locker) {
release()
func() {
- release, err := locker.Lock(context.Background(), "test")
+ release, err := locker.Lock(t.Context(), "test")
defer release()
assert.NoError(t, err)
@@ -72,7 +72,7 @@ func testLocker(t *testing.T, locker Locker) {
})
t.Run("try lock", func(t *testing.T) {
- parentCtx := context.Background()
+ parentCtx := t.Context()
ok, release, err := locker.TryLock(parentCtx, "test")
defer release()
@@ -80,7 +80,7 @@ func testLocker(t *testing.T, locker Locker) {
assert.NoError(t, err)
func() {
- ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Second)
defer cancel()
ok, release, err := locker.TryLock(ctx, "test")
defer release()
@@ -92,7 +92,7 @@ func testLocker(t *testing.T, locker Locker) {
release()
func() {
- ok, release, _ := locker.TryLock(context.Background(), "test")
+ ok, release, _ := locker.TryLock(t.Context(), "test")
defer release()
assert.True(t, ok)
@@ -100,7 +100,7 @@ func testLocker(t *testing.T, locker Locker) {
})
t.Run("wait and acquired", func(t *testing.T) {
- ctx := context.Background()
+ ctx := t.Context()
release, err := locker.Lock(ctx, "test")
require.NoError(t, err)
@@ -109,7 +109,7 @@ func testLocker(t *testing.T, locker Locker) {
go func() {
defer wg.Done()
started := time.Now()
- release, err := locker.Lock(context.Background(), "test") // should be blocked for seconds
+ release, err := locker.Lock(t.Context(), "test") // should be blocked for seconds
defer release()
assert.Greater(t, time.Since(started), time.Second)
assert.NoError(t, err)
@@ -122,7 +122,7 @@ func testLocker(t *testing.T, locker Locker) {
})
t.Run("multiple release", func(t *testing.T) {
- ctx := context.Background()
+ ctx := t.Context()
release1, err := locker.Lock(ctx, "test")
require.NoError(t, err)
@@ -159,13 +159,13 @@ func testRedisLocker(t *testing.T, locker *redisLocker) {
// Otherwise, it will affect other tests.
t.Run("close", func(t *testing.T) {
assert.NoError(t, locker.Close())
- _, err := locker.Lock(context.Background(), "test")
+ _, err := locker.Lock(t.Context(), "test")
assert.Error(t, err)
})
}()
t.Run("failed extend", func(t *testing.T) {
- release, err := locker.Lock(context.Background(), "test")
+ release, err := locker.Lock(t.Context(), "test")
defer release()
require.NoError(t, err)
diff --git a/modules/globallock/redis_locker.go b/modules/globallock/redis_locker.go
index 34ed9e389b..45dc769fd4 100644
--- a/modules/globallock/redis_locker.go
+++ b/modules/globallock/redis_locker.go
@@ -6,7 +6,6 @@ package globallock
import (
"context"
"errors"
- "fmt"
"sync"
"sync/atomic"
"time"
@@ -78,7 +77,7 @@ func (l *redisLocker) Close() error {
func (l *redisLocker) lock(ctx context.Context, key string, tries int) (ReleaseFunc, error) {
if l.closed.Load() {
- return func() {}, fmt.Errorf("locker is closed")
+ return func() {}, errors.New("locker is closed")
}
options := []redsync.Option{
diff --git a/modules/graceful/manager_windows.go b/modules/graceful/manager_windows.go
index d776e0e9f9..457768d6ca 100644
--- a/modules/graceful/manager_windows.go
+++ b/modules/graceful/manager_windows.go
@@ -41,8 +41,7 @@ func (g *Manager) start() {
// Make SVC process
run := svc.Run
- //lint:ignore SA1019 We use IsAnInteractiveSession because IsWindowsService has a different permissions profile
- isAnInteractiveSession, err := svc.IsAnInteractiveSession() //nolint:staticcheck
+ isAnInteractiveSession, err := svc.IsAnInteractiveSession() //nolint:staticcheck // must use IsAnInteractiveSession because IsWindowsService has a different permissions profile
if err != nil {
log.Error("Unable to ascertain if running as an Windows Service: %v", err)
return
diff --git a/modules/graceful/releasereopen/releasereopen_test.go b/modules/graceful/releasereopen/releasereopen_test.go
index 0e8b48257d..46e67c2046 100644
--- a/modules/graceful/releasereopen/releasereopen_test.go
+++ b/modules/graceful/releasereopen/releasereopen_test.go
@@ -30,14 +30,14 @@ func TestManager(t *testing.T) {
_ = m.Register(t3)
assert.NoError(t, m.ReleaseReopen())
- assert.EqualValues(t, 1, t1.count)
- assert.EqualValues(t, 1, t2.count)
- assert.EqualValues(t, 1, t3.count)
+ assert.Equal(t, 1, t1.count)
+ assert.Equal(t, 1, t2.count)
+ assert.Equal(t, 1, t3.count)
c2()
assert.NoError(t, m.ReleaseReopen())
- assert.EqualValues(t, 2, t1.count)
- assert.EqualValues(t, 1, t2.count)
- assert.EqualValues(t, 2, t3.count)
+ assert.Equal(t, 2, t1.count)
+ assert.Equal(t, 1, t2.count)
+ assert.Equal(t, 2, t3.count)
}
diff --git a/modules/gtprof/event.go b/modules/gtprof/event.go
new file mode 100644
index 0000000000..da4a0faff9
--- /dev/null
+++ b/modules/gtprof/event.go
@@ -0,0 +1,32 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package gtprof
+
+type EventConfig struct {
+ attributes []*TraceAttribute
+}
+
+type EventOption interface {
+ applyEvent(*EventConfig)
+}
+
+type applyEventFunc func(*EventConfig)
+
+func (f applyEventFunc) applyEvent(cfg *EventConfig) {
+ f(cfg)
+}
+
+func WithAttributes(attrs ...*TraceAttribute) EventOption {
+ return applyEventFunc(func(cfg *EventConfig) {
+ cfg.attributes = append(cfg.attributes, attrs...)
+ })
+}
+
+func eventConfigFromOptions(options ...EventOption) *EventConfig {
+ cfg := &EventConfig{}
+ for _, opt := range options {
+ opt.applyEvent(cfg)
+ }
+ return cfg
+}
diff --git a/modules/gtprof/trace.go b/modules/gtprof/trace.go
new file mode 100644
index 0000000000..ad67c226dc
--- /dev/null
+++ b/modules/gtprof/trace.go
@@ -0,0 +1,175 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package gtprof
+
+import (
+ "context"
+ "fmt"
+ "sync"
+ "time"
+
+ "code.gitea.io/gitea/modules/util"
+)
+
+type contextKey struct {
+ name string
+}
+
+var contextKeySpan = &contextKey{"span"}
+
+type traceStarter interface {
+ start(ctx context.Context, traceSpan *TraceSpan, internalSpanIdx int) (context.Context, traceSpanInternal)
+}
+
+type traceSpanInternal interface {
+ addEvent(name string, cfg *EventConfig)
+ recordError(err error, cfg *EventConfig)
+ end()
+}
+
+type TraceSpan struct {
+ // immutable
+ parent *TraceSpan
+ internalSpans []traceSpanInternal
+ internalContexts []context.Context
+
+ // mutable, must be protected by mutex
+ mu sync.RWMutex
+ name string
+ statusCode uint32
+ statusDesc string
+ startTime time.Time
+ endTime time.Time
+ attributes []*TraceAttribute
+ children []*TraceSpan
+}
+
+type TraceAttribute struct {
+ Key string
+ Value TraceValue
+}
+
+type TraceValue struct {
+ v any
+}
+
+func (t *TraceValue) AsString() string {
+ return fmt.Sprint(t.v)
+}
+
+func (t *TraceValue) AsInt64() int64 {
+ v, _ := util.ToInt64(t.v)
+ return v
+}
+
+func (t *TraceValue) AsFloat64() float64 {
+ v, _ := util.ToFloat64(t.v)
+ return v
+}
+
+var globalTraceStarters []traceStarter
+
+type Tracer struct {
+ starters []traceStarter
+}
+
+func (s *TraceSpan) SetName(name string) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.name = name
+}
+
+func (s *TraceSpan) SetStatus(code uint32, desc string) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.statusCode, s.statusDesc = code, desc
+}
+
+func (s *TraceSpan) AddEvent(name string, options ...EventOption) {
+ cfg := eventConfigFromOptions(options...)
+ for _, tsp := range s.internalSpans {
+ tsp.addEvent(name, cfg)
+ }
+}
+
+func (s *TraceSpan) RecordError(err error, options ...EventOption) {
+ cfg := eventConfigFromOptions(options...)
+ for _, tsp := range s.internalSpans {
+ tsp.recordError(err, cfg)
+ }
+}
+
+func (s *TraceSpan) SetAttributeString(key, value string) *TraceSpan {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.attributes = append(s.attributes, &TraceAttribute{Key: key, Value: TraceValue{v: value}})
+ return s
+}
+
+func (t *Tracer) Start(ctx context.Context, spanName string) (context.Context, *TraceSpan) {
+ starters := t.starters
+ if starters == nil {
+ starters = globalTraceStarters
+ }
+ ts := &TraceSpan{name: spanName, startTime: time.Now()}
+ parentSpan := GetContextSpan(ctx)
+ if parentSpan != nil {
+ parentSpan.mu.Lock()
+ parentSpan.children = append(parentSpan.children, ts)
+ parentSpan.mu.Unlock()
+ ts.parent = parentSpan
+ }
+
+ parentCtx := ctx
+ for internalSpanIdx, tsp := range starters {
+ var internalSpan traceSpanInternal
+ if parentSpan != nil {
+ parentCtx = parentSpan.internalContexts[internalSpanIdx]
+ }
+ ctx, internalSpan = tsp.start(parentCtx, ts, internalSpanIdx)
+ ts.internalContexts = append(ts.internalContexts, ctx)
+ ts.internalSpans = append(ts.internalSpans, internalSpan)
+ }
+ ctx = context.WithValue(ctx, contextKeySpan, ts)
+ return ctx, ts
+}
+
+type mutableContext interface {
+ context.Context
+ SetContextValue(key, value any)
+ GetContextValue(key any) any
+}
+
+// StartInContext starts a trace span in Gitea's mutable context (usually the web request context).
+// Due to the design limitation of Gitea's web framework, it can't use `context.WithValue` to bind a new span into a new context.
+// So here we use our "reqctx" framework to achieve the same result: web request context could always see the latest "span".
+func (t *Tracer) StartInContext(ctx mutableContext, spanName string) (*TraceSpan, func()) {
+ curTraceSpan := GetContextSpan(ctx)
+ _, newTraceSpan := GetTracer().Start(ctx, spanName)
+ ctx.SetContextValue(contextKeySpan, newTraceSpan)
+ return newTraceSpan, func() {
+ newTraceSpan.End()
+ ctx.SetContextValue(contextKeySpan, curTraceSpan)
+ }
+}
+
+func (s *TraceSpan) End() {
+ s.mu.Lock()
+ s.endTime = time.Now()
+ s.mu.Unlock()
+
+ for _, tsp := range s.internalSpans {
+ tsp.end()
+ }
+}
+
+func GetTracer() *Tracer {
+ return &Tracer{}
+}
+
+func GetContextSpan(ctx context.Context) *TraceSpan {
+ ts, _ := ctx.Value(contextKeySpan).(*TraceSpan)
+ return ts
+}
diff --git a/modules/gtprof/trace_builtin.go b/modules/gtprof/trace_builtin.go
new file mode 100644
index 0000000000..2590ed3a13
--- /dev/null
+++ b/modules/gtprof/trace_builtin.go
@@ -0,0 +1,96 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package gtprof
+
+import (
+ "context"
+ "fmt"
+ "strings"
+ "sync/atomic"
+ "time"
+
+ "code.gitea.io/gitea/modules/tailmsg"
+)
+
+type traceBuiltinStarter struct{}
+
+type traceBuiltinSpan struct {
+ ts *TraceSpan
+
+ internalSpanIdx int
+}
+
+func (t *traceBuiltinSpan) addEvent(name string, cfg *EventConfig) {
+ // No-op because builtin tracer doesn't need it.
+ // In the future we might use it to mark the time point between backend logic and network response.
+}
+
+func (t *traceBuiltinSpan) recordError(err error, cfg *EventConfig) {
+ // No-op because builtin tracer doesn't need it.
+ // Actually Gitea doesn't handle err this way in most cases
+}
+
+func (t *traceBuiltinSpan) toString(out *strings.Builder, indent int) {
+ t.ts.mu.RLock()
+ defer t.ts.mu.RUnlock()
+
+ out.WriteString(strings.Repeat(" ", indent))
+ out.WriteString(t.ts.name)
+ if t.ts.endTime.IsZero() {
+ out.WriteString(" duration: (not ended)")
+ } else {
+ fmt.Fprintf(out, " duration=%.4fs", t.ts.endTime.Sub(t.ts.startTime).Seconds())
+ }
+ for _, a := range t.ts.attributes {
+ out.WriteString(" ")
+ out.WriteString(a.Key)
+ out.WriteString("=")
+ value := a.Value.AsString()
+ if strings.ContainsAny(value, " \t\r\n") {
+ quoted := false
+ for _, c := range "\"'`" {
+ if quoted = !strings.Contains(value, string(c)); quoted {
+ value = string(c) + value + string(c)
+ break
+ }
+ }
+ if !quoted {
+ value = fmt.Sprintf("%q", value)
+ }
+ }
+ out.WriteString(value)
+ }
+ out.WriteString("\n")
+ for _, c := range t.ts.children {
+ span := c.internalSpans[t.internalSpanIdx].(*traceBuiltinSpan)
+ span.toString(out, indent+2)
+ }
+}
+
+func (t *traceBuiltinSpan) end() {
+ if t.ts.parent == nil {
+ // TODO: debug purpose only
+ // TODO: it should distinguish between http response network lag and actual processing time
+ threshold := time.Duration(traceBuiltinThreshold.Load())
+ if threshold != 0 && t.ts.endTime.Sub(t.ts.startTime) > threshold {
+ sb := &strings.Builder{}
+ t.toString(sb, 0)
+ tailmsg.GetManager().GetTraceRecorder().Record(sb.String())
+ }
+ }
+}
+
+func (t *traceBuiltinStarter) start(ctx context.Context, traceSpan *TraceSpan, internalSpanIdx int) (context.Context, traceSpanInternal) {
+ return ctx, &traceBuiltinSpan{ts: traceSpan, internalSpanIdx: internalSpanIdx}
+}
+
+func init() {
+ globalTraceStarters = append(globalTraceStarters, &traceBuiltinStarter{})
+}
+
+var traceBuiltinThreshold atomic.Int64
+
+func EnableBuiltinTracer(threshold time.Duration) {
+ traceBuiltinThreshold.Store(int64(threshold))
+}
diff --git a/modules/gtprof/trace_const.go b/modules/gtprof/trace_const.go
new file mode 100644
index 0000000000..af9ce9223f
--- /dev/null
+++ b/modules/gtprof/trace_const.go
@@ -0,0 +1,19 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package gtprof
+
+// Some interesting names could be found in https://github.com/open-telemetry/opentelemetry-go/tree/main/semconv
+
+const (
+ TraceSpanHTTP = "http"
+ TraceSpanGitRun = "git-run"
+ TraceSpanDatabase = "database"
+)
+
+const (
+ TraceAttrFuncCaller = "func.caller"
+ TraceAttrDbSQL = "db.sql"
+ TraceAttrGitCommand = "git.command"
+ TraceAttrHTTPRoute = "http.route"
+)
diff --git a/modules/gtprof/trace_test.go b/modules/gtprof/trace_test.go
new file mode 100644
index 0000000000..0f4e3facba
--- /dev/null
+++ b/modules/gtprof/trace_test.go
@@ -0,0 +1,93 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package gtprof
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+// "vendor span" is a simple demo for a span from a vendor library
+
+var vendorContextKey any = "vendorContextKey"
+
+type vendorSpan struct {
+ name string
+ children []*vendorSpan
+}
+
+func vendorTraceStart(ctx context.Context, name string) (context.Context, *vendorSpan) {
+ span := &vendorSpan{name: name}
+ parentSpan, ok := ctx.Value(vendorContextKey).(*vendorSpan)
+ if ok {
+ parentSpan.children = append(parentSpan.children, span)
+ }
+ ctx = context.WithValue(ctx, vendorContextKey, span)
+ return ctx, span
+}
+
+// below "testTrace*" integrate the vendor span into our trace system
+
+type testTraceSpan struct {
+ vendorSpan *vendorSpan
+}
+
+func (t *testTraceSpan) addEvent(name string, cfg *EventConfig) {}
+
+func (t *testTraceSpan) recordError(err error, cfg *EventConfig) {}
+
+func (t *testTraceSpan) end() {}
+
+type testTraceStarter struct{}
+
+func (t *testTraceStarter) start(ctx context.Context, traceSpan *TraceSpan, internalSpanIdx int) (context.Context, traceSpanInternal) {
+ ctx, span := vendorTraceStart(ctx, traceSpan.name)
+ return ctx, &testTraceSpan{span}
+}
+
+func TestTraceStarter(t *testing.T) {
+ globalTraceStarters = []traceStarter{&testTraceStarter{}}
+
+ ctx := t.Context()
+ ctx, span := GetTracer().Start(ctx, "root")
+ defer span.End()
+
+ func(ctx context.Context) {
+ ctx, span := GetTracer().Start(ctx, "span1")
+ defer span.End()
+ func(ctx context.Context) {
+ _, span := GetTracer().Start(ctx, "spanA")
+ defer span.End()
+ }(ctx)
+ func(ctx context.Context) {
+ _, span := GetTracer().Start(ctx, "spanB")
+ defer span.End()
+ }(ctx)
+ }(ctx)
+
+ func(ctx context.Context) {
+ _, span := GetTracer().Start(ctx, "span2")
+ defer span.End()
+ }(ctx)
+
+ var spanFullNames []string
+ var collectSpanNames func(parentFullName string, s *vendorSpan)
+ collectSpanNames = func(parentFullName string, s *vendorSpan) {
+ fullName := parentFullName + "/" + s.name
+ spanFullNames = append(spanFullNames, fullName)
+ for _, c := range s.children {
+ collectSpanNames(fullName, c)
+ }
+ }
+ collectSpanNames("", span.internalSpans[0].(*testTraceSpan).vendorSpan)
+ assert.Equal(t, []string{
+ "/root",
+ "/root/span1",
+ "/root/span1/spanA",
+ "/root/span1/spanB",
+ "/root/span2",
+ }, spanFullNames)
+}
diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go
index d7ab3f7afd..77f24fa3f3 100644
--- a/modules/highlight/highlight.go
+++ b/modules/highlight/highlight.go
@@ -11,6 +11,7 @@ import (
gohtml "html"
"html/template"
"io"
+ "path"
"path/filepath"
"strings"
"sync"
@@ -83,7 +84,7 @@ func Code(fileName, language, code string) (output template.HTML, lexerName stri
}
if lexer == nil {
- if val, ok := highlightMapping[filepath.Ext(fileName)]; ok {
+ if val, ok := highlightMapping[path.Ext(fileName)]; ok {
// use mapped value to find lexer
lexer = lexers.Get(val)
}
diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go
index 659688bd0f..b36de98c5c 100644
--- a/modules/highlight/highlight_test.go
+++ b/modules/highlight/highlight_test.go
@@ -114,7 +114,7 @@ c=2
t.Run(tt.name, func(t *testing.T) {
out, lexerName, err := File(tt.name, "", []byte(tt.code))
assert.NoError(t, err)
- assert.EqualValues(t, tt.want, out)
+ assert.Equal(t, tt.want, out)
assert.Equal(t, tt.lexerName, lexerName)
})
}
@@ -177,7 +177,7 @@ c=2`),
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
out := PlainText([]byte(tt.code))
- assert.EqualValues(t, tt.want, out)
+ assert.Equal(t, tt.want, out)
})
}
}
diff --git a/modules/hostmatcher/hostmatcher.go b/modules/hostmatcher/hostmatcher.go
index 1069310316..15c6371422 100644
--- a/modules/hostmatcher/hostmatcher.go
+++ b/modules/hostmatcher/hostmatcher.go
@@ -6,6 +6,7 @@ package hostmatcher
import (
"net"
"path/filepath"
+ "slices"
"strings"
)
@@ -38,7 +39,7 @@ func isBuiltin(s string) bool {
// ParseHostMatchList parses the host list HostMatchList
func ParseHostMatchList(settingKeyHint, hostList string) *HostMatchList {
hl := &HostMatchList{SettingKeyHint: settingKeyHint, SettingValue: hostList}
- for _, s := range strings.Split(hostList, ",") {
+ for s := range strings.SplitSeq(hostList, ",") {
s = strings.ToLower(strings.TrimSpace(s))
if s == "" {
continue
@@ -61,7 +62,7 @@ func ParseSimpleMatchList(settingKeyHint, matchList string) *HostMatchList {
SettingKeyHint: settingKeyHint,
SettingValue: matchList,
}
- for _, s := range strings.Split(matchList, ",") {
+ for s := range strings.SplitSeq(matchList, ",") {
s = strings.ToLower(strings.TrimSpace(s))
if s == "" {
continue
@@ -98,10 +99,8 @@ func (hl *HostMatchList) checkPattern(host string) bool {
}
func (hl *HostMatchList) checkIP(ip net.IP) bool {
- for _, pattern := range hl.patterns {
- if pattern == "*" {
- return true
- }
+ if slices.Contains(hl.patterns, "*") {
+ return true
}
for _, builtin := range hl.builtins {
switch builtin {
diff --git a/modules/htmlutil/html.go b/modules/htmlutil/html.go
index 9b5f5a92d8..efbc174b2e 100644
--- a/modules/htmlutil/html.go
+++ b/modules/htmlutil/html.go
@@ -7,6 +7,7 @@ import (
"fmt"
"html/template"
"slices"
+ "strings"
)
// ParseSizeAndClass get size and class from string with default values
@@ -30,7 +31,10 @@ func ParseSizeAndClass(defaultSize int, defaultClass string, others ...any) (int
return size, class
}
-func HTMLFormat(s string, rawArgs ...any) template.HTML {
+func HTMLFormat(s template.HTML, rawArgs ...any) template.HTML {
+ if !strings.Contains(string(s), "%") || len(rawArgs) == 0 {
+ panic("HTMLFormat requires one or more arguments")
+ }
args := slices.Clone(rawArgs)
for i, v := range args {
switch v := v.(type) {
@@ -38,11 +42,13 @@ func HTMLFormat(s string, rawArgs ...any) template.HTML {
// for most basic types (including template.HTML which is safe), just do nothing and use it
case string:
args[i] = template.HTMLEscapeString(v)
+ case template.URL:
+ args[i] = template.HTMLEscapeString(string(v))
case fmt.Stringer:
args[i] = template.HTMLEscapeString(v.String())
default:
args[i] = template.HTMLEscapeString(fmt.Sprint(v))
}
}
- return template.HTML(fmt.Sprintf(s, args...))
+ return template.HTML(fmt.Sprintf(string(s), args...))
}
diff --git a/modules/htmlutil/html_test.go b/modules/htmlutil/html_test.go
index 5ff05d75b3..22258ce59d 100644
--- a/modules/htmlutil/html_test.go
+++ b/modules/htmlutil/html_test.go
@@ -10,6 +10,15 @@ import (
"github.com/stretchr/testify/assert"
)
+type testStringer struct{}
+
+func (t testStringer) String() string {
+ return "&StringMethod"
+}
+
func TestHTMLFormat(t *testing.T) {
assert.Equal(t, template.HTML("<a>&lt; < 1</a>"), HTMLFormat("<a>%s %s %d</a>", "<", template.HTML("<"), 1))
+ assert.Equal(t, template.HTML("%!s(<nil>)"), HTMLFormat("%s", nil))
+ assert.Equal(t, template.HTML("&lt;&gt;"), HTMLFormat("%s", template.URL("<>")))
+ assert.Equal(t, template.HTML("&amp;StringMethod &amp;StringMethod"), HTMLFormat("%s %s", testStringer{}, &testStringer{}))
}
diff --git a/modules/httpcache/httpcache.go b/modules/httpcache/httpcache.go
index 2c9af94405..dd3efab7a5 100644
--- a/modules/httpcache/httpcache.go
+++ b/modules/httpcache/httpcache.go
@@ -4,40 +4,60 @@
package httpcache
import (
- "io"
+ "fmt"
"net/http"
"strconv"
"strings"
"time"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
)
+type CacheControlOptions struct {
+ IsPublic bool
+ MaxAge time.Duration
+ NoTransform bool
+}
+
// SetCacheControlInHeader sets suitable cache-control headers in the response
-func SetCacheControlInHeader(h http.Header, maxAge time.Duration, additionalDirectives ...string) {
- directives := make([]string, 0, 2+len(additionalDirectives))
+func SetCacheControlInHeader(h http.Header, opts *CacheControlOptions) {
+ directives := make([]string, 0, 4)
// "max-age=0 + must-revalidate" (aka "no-cache") is preferred instead of "no-store"
// because browsers may restore some input fields after navigate-back / reload a page.
+ publicPrivate := util.Iif(opts.IsPublic, "public", "private")
if setting.IsProd {
- if maxAge == 0 {
+ if opts.MaxAge == 0 {
directives = append(directives, "max-age=0", "private", "must-revalidate")
} else {
- directives = append(directives, "private", "max-age="+strconv.Itoa(int(maxAge.Seconds())))
+ directives = append(directives, publicPrivate, "max-age="+strconv.Itoa(int(opts.MaxAge.Seconds())))
}
} else {
- directives = append(directives, "max-age=0", "private", "must-revalidate")
+ // use dev-related controls, and remind users they are using non-prod setting.
+ directives = append(directives, "max-age=0", publicPrivate, "must-revalidate")
+ h.Set("X-Gitea-Debug", fmt.Sprintf("RUN_MODE=%v, MaxAge=%s", setting.RunMode, opts.MaxAge))
+ }
- // to remind users they are using non-prod setting.
- h.Set("X-Gitea-Debug", "RUN_MODE="+setting.RunMode)
+ if opts.NoTransform {
+ directives = append(directives, "no-transform")
}
+ h.Set("Cache-Control", strings.Join(directives, ", "))
+}
- h.Set("Cache-Control", strings.Join(append(directives, additionalDirectives...), ", "))
+func CacheControlForPublicStatic() *CacheControlOptions {
+ return &CacheControlOptions{
+ IsPublic: true,
+ MaxAge: setting.StaticCacheTime,
+ NoTransform: true,
+ }
}
-func ServeContentWithCacheControl(w http.ResponseWriter, req *http.Request, name string, modTime time.Time, content io.ReadSeeker) {
- SetCacheControlInHeader(w.Header(), setting.StaticCacheTime)
- http.ServeContent(w, req, name, modTime, content)
+func CacheControlForPrivateStatic() *CacheControlOptions {
+ return &CacheControlOptions{
+ MaxAge: setting.StaticCacheTime,
+ NoTransform: true,
+ }
}
// HandleGenericETagCache handles ETag-based caching for a HTTP request.
@@ -50,7 +70,8 @@ func HandleGenericETagCache(req *http.Request, w http.ResponseWriter, etag strin
return true
}
}
- SetCacheControlInHeader(w.Header(), setting.StaticCacheTime)
+ // not sure whether it is a public content, so just use "private" (old behavior)
+ SetCacheControlInHeader(w.Header(), CacheControlForPrivateStatic())
return false
}
@@ -58,7 +79,7 @@ func HandleGenericETagCache(req *http.Request, w http.ResponseWriter, etag strin
func checkIfNoneMatchIsValid(req *http.Request, etag string) bool {
ifNoneMatch := req.Header.Get("If-None-Match")
if len(ifNoneMatch) > 0 {
- for _, item := range strings.Split(ifNoneMatch, ",") {
+ for item := range strings.SplitSeq(ifNoneMatch, ",") {
item = strings.TrimPrefix(strings.TrimSpace(item), "W/") // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag#directives
if item == etag {
return true
@@ -95,6 +116,8 @@ func HandleGenericETagTimeCache(req *http.Request, w http.ResponseWriter, etag s
}
}
}
- SetCacheControlInHeader(w.Header(), setting.StaticCacheTime)
+
+ // not sure whether it is a public content, so just use "private" (old behavior)
+ SetCacheControlInHeader(w.Header(), CacheControlForPrivateStatic())
return false
}
diff --git a/modules/httplib/request.go b/modules/httplib/request.go
index 880d7ad3cb..49ea6f4b73 100644
--- a/modules/httplib/request.go
+++ b/modules/httplib/request.go
@@ -8,6 +8,7 @@ import (
"bytes"
"context"
"crypto/tls"
+ "errors"
"fmt"
"io"
"net"
@@ -99,18 +100,27 @@ func (r *Request) Param(key, value string) *Request {
return r
}
-// Body adds request raw body.
-// it supports string and []byte.
+// Body adds request raw body. It supports string, []byte and io.Reader as body.
func (r *Request) Body(data any) *Request {
+ if r == nil {
+ return nil
+ }
switch t := data.(type) {
+ case nil: // do nothing
case string:
- bf := bytes.NewBufferString(t)
+ bf := strings.NewReader(t)
r.req.Body = io.NopCloser(bf)
r.req.ContentLength = int64(len(t))
case []byte:
bf := bytes.NewBuffer(t)
r.req.Body = io.NopCloser(bf)
r.req.ContentLength = int64(len(t))
+ case io.ReadCloser:
+ r.req.Body = t
+ case io.Reader:
+ r.req.Body = io.NopCloser(t)
+ default:
+ panic(fmt.Sprintf("unsupported request body type %T", t))
}
return r
}
@@ -133,15 +143,15 @@ func (r *Request) getResponse() (*http.Response, error) {
paramBody = paramBody[0 : len(paramBody)-1]
}
- if r.req.Method == "GET" && len(paramBody) > 0 {
+ if r.req.Method == http.MethodGet && len(paramBody) > 0 {
if strings.Contains(r.url, "?") {
r.url += "&" + paramBody
} else {
r.url = r.url + "?" + paramBody
}
- } else if r.req.Method == "POST" && r.req.Body == nil && len(paramBody) > 0 {
+ } else if r.req.Method == http.MethodPost && r.req.Body == nil && len(paramBody) > 0 {
r.Header("Content-Type", "application/x-www-form-urlencoded")
- r.Body(paramBody)
+ r.Body(paramBody) // string
}
var err error
@@ -185,7 +195,11 @@ func (r *Request) getResponse() (*http.Response, error) {
}
// Response executes request client gets response manually.
+// Caller MUST close the response body if no error occurs
func (r *Request) Response() (*http.Response, error) {
+ if r == nil {
+ return nil, errors.New("invalid request")
+ }
return r.getResponse()
}
diff --git a/modules/httplib/serve.go b/modules/httplib/serve.go
index 8fb667876e..7c1edf432d 100644
--- a/modules/httplib/serve.go
+++ b/modules/httplib/serve.go
@@ -33,6 +33,7 @@ type ServeHeaderOptions struct {
ContentLength *int64
Disposition string // defaults to "attachment"
Filename string
+ CacheIsPublic bool
CacheDuration time.Duration // defaults to 5 minutes
LastModified time.Time
}
@@ -72,11 +73,11 @@ func ServeSetHeaders(w http.ResponseWriter, opts *ServeHeaderOptions) {
header.Set("Access-Control-Expose-Headers", "Content-Disposition")
}
- duration := opts.CacheDuration
- if duration == 0 {
- duration = 5 * time.Minute
- }
- httpcache.SetCacheControlInHeader(header, duration)
+ httpcache.SetCacheControlInHeader(header, &httpcache.CacheControlOptions{
+ IsPublic: opts.CacheIsPublic,
+ MaxAge: opts.CacheDuration,
+ NoTransform: true,
+ })
if !opts.LastModified.IsZero() {
// http.TimeFormat required a UTC time, refer to https://pkg.go.dev/net/http#TimeFormat
@@ -85,19 +86,15 @@ func ServeSetHeaders(w http.ResponseWriter, opts *ServeHeaderOptions) {
}
// ServeData download file from io.Reader
-func setServeHeadersByFile(r *http.Request, w http.ResponseWriter, filePath string, mineBuf []byte) {
+func setServeHeadersByFile(r *http.Request, w http.ResponseWriter, mineBuf []byte, opts *ServeHeaderOptions) {
// do not set "Content-Length", because the length could only be set by callers, and it needs to support range requests
- opts := &ServeHeaderOptions{
- Filename: path.Base(filePath),
- }
-
sniffedType := typesniffer.DetectContentType(mineBuf)
// the "render" parameter came from year 2016: 638dd24c, it doesn't have clear meaning, so I think it could be removed later
isPlain := sniffedType.IsText() || r.FormValue("render") != ""
if setting.MimeTypeMap.Enabled {
- fileExtension := strings.ToLower(filepath.Ext(filePath))
+ fileExtension := strings.ToLower(filepath.Ext(opts.Filename))
opts.ContentType = setting.MimeTypeMap.Map[fileExtension]
}
@@ -114,7 +111,7 @@ func setServeHeadersByFile(r *http.Request, w http.ResponseWriter, filePath stri
if isPlain {
charset, err := charsetModule.DetectEncoding(mineBuf)
if err != nil {
- log.Error("Detect raw file %s charset failed: %v, using by default utf-8", filePath, err)
+ log.Error("Detect raw file %s charset failed: %v, using by default utf-8", opts.Filename, err)
charset = "utf-8"
}
opts.ContentTypeCharset = strings.ToLower(charset)
@@ -142,7 +139,7 @@ func setServeHeadersByFile(r *http.Request, w http.ResponseWriter, filePath stri
const mimeDetectionBufferLen = 1024
-func ServeContentByReader(r *http.Request, w http.ResponseWriter, filePath string, size int64, reader io.Reader) {
+func ServeContentByReader(r *http.Request, w http.ResponseWriter, size int64, reader io.Reader, opts *ServeHeaderOptions) {
buf := make([]byte, mimeDetectionBufferLen)
n, err := util.ReadAtMost(reader, buf)
if err != nil {
@@ -152,7 +149,7 @@ func ServeContentByReader(r *http.Request, w http.ResponseWriter, filePath strin
if n >= 0 {
buf = buf[:n]
}
- setServeHeadersByFile(r, w, filePath, buf)
+ setServeHeadersByFile(r, w, buf, opts)
// reset the reader to the beginning
reader = io.MultiReader(bytes.NewReader(buf), reader)
@@ -215,7 +212,7 @@ func ServeContentByReader(r *http.Request, w http.ResponseWriter, filePath strin
_, _ = io.CopyN(w, reader, partialLength) // just like http.ServeContent, not necessary to handle the error
}
-func ServeContentByReadSeeker(r *http.Request, w http.ResponseWriter, filePath string, modTime *time.Time, reader io.ReadSeeker) {
+func ServeContentByReadSeeker(r *http.Request, w http.ResponseWriter, modTime *time.Time, reader io.ReadSeeker, opts *ServeHeaderOptions) {
buf := make([]byte, mimeDetectionBufferLen)
n, err := util.ReadAtMost(reader, buf)
if err != nil {
@@ -229,9 +226,9 @@ func ServeContentByReadSeeker(r *http.Request, w http.ResponseWriter, filePath s
if n >= 0 {
buf = buf[:n]
}
- setServeHeadersByFile(r, w, filePath, buf)
+ setServeHeadersByFile(r, w, buf, opts)
if modTime == nil {
modTime = &time.Time{}
}
- http.ServeContent(w, r, path.Base(filePath), *modTime, reader)
+ http.ServeContent(w, r, opts.Filename, *modTime, reader)
}
diff --git a/modules/httplib/serve_test.go b/modules/httplib/serve_test.go
index c2229dffe9..78b88c9b5f 100644
--- a/modules/httplib/serve_test.go
+++ b/modules/httplib/serve_test.go
@@ -4,15 +4,16 @@
package httplib
import (
- "fmt"
"net/http"
"net/http/httptest"
"net/url"
"os"
+ "strconv"
"strings"
"testing"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestServeContentByReader(t *testing.T) {
@@ -22,14 +23,14 @@ func TestServeContentByReader(t *testing.T) {
_, rangeStr, _ := strings.Cut(t.Name(), "_range_")
r := &http.Request{Header: http.Header{}, Form: url.Values{}}
if rangeStr != "" {
- r.Header.Set("Range", fmt.Sprintf("bytes=%s", rangeStr))
+ r.Header.Set("Range", "bytes="+rangeStr)
}
reader := strings.NewReader(data)
w := httptest.NewRecorder()
- ServeContentByReader(r, w, "test", int64(len(data)), reader)
+ ServeContentByReader(r, w, int64(len(data)), reader, &ServeHeaderOptions{})
assert.Equal(t, expectedStatusCode, w.Code)
if expectedStatusCode == http.StatusPartialContent || expectedStatusCode == http.StatusOK {
- assert.Equal(t, fmt.Sprint(len(expectedContent)), w.Header().Get("Content-Length"))
+ assert.Equal(t, strconv.Itoa(len(expectedContent)), w.Header().Get("Content-Length"))
assert.Equal(t, expectedContent, w.Body.String())
}
}
@@ -67,20 +68,18 @@ func TestServeContentByReadSeeker(t *testing.T) {
_, rangeStr, _ := strings.Cut(t.Name(), "_range_")
r := &http.Request{Header: http.Header{}, Form: url.Values{}}
if rangeStr != "" {
- r.Header.Set("Range", fmt.Sprintf("bytes=%s", rangeStr))
+ r.Header.Set("Range", "bytes="+rangeStr)
}
seekReader, err := os.OpenFile(tmpFile, os.O_RDONLY, 0o644)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
defer seekReader.Close()
w := httptest.NewRecorder()
- ServeContentByReadSeeker(r, w, "test", nil, seekReader)
+ ServeContentByReadSeeker(r, w, nil, seekReader, &ServeHeaderOptions{})
assert.Equal(t, expectedStatusCode, w.Code)
if expectedStatusCode == http.StatusPartialContent || expectedStatusCode == http.StatusOK {
- assert.Equal(t, fmt.Sprint(len(expectedContent)), w.Header().Get("Content-Length"))
+ assert.Equal(t, strconv.Itoa(len(expectedContent)), w.Header().Get("Content-Length"))
assert.Equal(t, expectedContent, w.Body.String())
}
}
diff --git a/modules/httplib/url.go b/modules/httplib/url.go
index e3bad1e5fb..f51506ac3b 100644
--- a/modules/httplib/url.go
+++ b/modules/httplib/url.go
@@ -5,6 +5,7 @@ package httplib
import (
"context"
+ "net"
"net/http"
"net/url"
"strings"
@@ -52,28 +53,34 @@ func getRequestScheme(req *http.Request) string {
return ""
}
-// GuessCurrentAppURL tries to guess the current full app URL (with sub-path) by http headers. It always has a '/' suffix, exactly the same as setting.AppURL
+// GuessCurrentAppURL tries to guess the current full public URL (with sub-path) by http headers. It always has a '/' suffix, exactly the same as setting.AppURL
+// TODO: should rename it to GuessCurrentPublicURL in the future
func GuessCurrentAppURL(ctx context.Context) string {
return GuessCurrentHostURL(ctx) + setting.AppSubURL + "/"
}
// GuessCurrentHostURL tries to guess the current full host URL (no sub-path) by http headers, there is no trailing slash.
func GuessCurrentHostURL(ctx context.Context) string {
- req, ok := ctx.Value(RequestContextKey).(*http.Request)
- if !ok {
- return strings.TrimSuffix(setting.AppURL, setting.AppSubURL+"/")
- }
- // If no scheme provided by reverse proxy, then do not guess the AppURL, use the configured one.
+ // Try the best guess to get the current host URL (will be used for public URL) by http headers.
// At the moment, if site admin doesn't configure the proxy headers correctly, then Gitea would guess wrong.
// There are some cases:
// 1. The reverse proxy is configured correctly, it passes "X-Forwarded-Proto/Host" headers. Perfect, Gitea can handle it correctly.
// 2. The reverse proxy is not configured correctly, doesn't pass "X-Forwarded-Proto/Host" headers, eg: only one "proxy_pass http://gitea:3000" in Nginx.
// 3. There is no reverse proxy.
- // Without an extra config option, Gitea is impossible to distinguish between case 2 and case 3,
- // then case 2 would result in wrong guess like guessed AppURL becomes "http://gitea:3000/", which is not accessible by end users.
- // So in the future maybe it should introduce a new config option, to let site admin decide how to guess the AppURL.
+ // Without more information, Gitea is impossible to distinguish between case 2 and case 3, then case 2 would result in
+ // wrong guess like guessed public URL becomes "http://gitea:3000/" behind a "https" reverse proxy, which is not accessible by end users.
+ // So we introduced "PUBLIC_URL_DETECTION" option, to control the guessing behavior to satisfy different use cases.
+ req, ok := ctx.Value(RequestContextKey).(*http.Request)
+ if !ok {
+ return strings.TrimSuffix(setting.AppURL, setting.AppSubURL+"/")
+ }
reqScheme := getRequestScheme(req)
if reqScheme == "" {
+ // if no reverse proxy header, try to use "Host" header for absolute URL
+ if setting.PublicURLDetection == setting.PublicURLAuto && req.Host != "" {
+ return util.Iif(req.TLS == nil, "http://", "https://") + req.Host
+ }
+ // fall back to default AppURL
return strings.TrimSuffix(setting.AppURL, setting.AppSubURL+"/")
}
// X-Forwarded-Host has many problems: non-standard, not well-defined (X-Forwarded-Port or not), conflicts with Host header.
@@ -81,8 +88,14 @@ func GuessCurrentHostURL(ctx context.Context) string {
return reqScheme + "://" + req.Host
}
-// MakeAbsoluteURL tries to make a link to an absolute URL:
-// * If link is empty, it returns the current app URL.
+func GuessCurrentHostDomain(ctx context.Context) string {
+ _, host, _ := strings.Cut(GuessCurrentHostURL(ctx), "://")
+ domain, _, _ := net.SplitHostPort(host)
+ return util.IfZero(domain, host)
+}
+
+// MakeAbsoluteURL tries to make a link to an absolute public URL:
+// * If link is empty, it returns the current public URL.
// * If link is absolute, it returns the link.
// * Otherwise, it returns the current host URL + link, the link itself should have correct sub-path (AppSubURL) if needed.
func MakeAbsoluteURL(ctx context.Context, link string) string {
@@ -95,25 +108,77 @@ func MakeAbsoluteURL(ctx context.Context, link string) string {
return GuessCurrentHostURL(ctx) + "/" + strings.TrimPrefix(link, "/")
}
-func IsCurrentGiteaSiteURL(ctx context.Context, s string) bool {
+type urlType int
+
+const (
+ urlTypeGiteaAbsolute urlType = iota + 1 // "http://gitea/subpath"
+ urlTypeGiteaPageRelative // "/subpath"
+ urlTypeGiteaSiteRelative // "?key=val"
+ urlTypeUnknown // "http://other"
+)
+
+func detectURLRoutePath(ctx context.Context, s string) (routePath string, ut urlType) {
u, err := url.Parse(s)
if err != nil {
- return false
+ return "", urlTypeUnknown
}
+ cleanedPath := ""
if u.Path != "" {
- cleanedPath := util.PathJoinRelX(u.Path)
- if cleanedPath == "" || cleanedPath == "." {
- u.Path = "/"
- } else {
- u.Path += "/" + cleanedPath + "/"
- }
+ cleanedPath = util.PathJoinRelX(u.Path)
+ cleanedPath = util.Iif(cleanedPath == ".", "", "/"+cleanedPath)
}
if urlIsRelative(s, u) {
- return u.Path == "" || strings.HasPrefix(strings.ToLower(u.Path), strings.ToLower(setting.AppSubURL+"/"))
- }
- if u.Path == "" {
- u.Path = "/"
+ if u.Path == "" {
+ return "", urlTypeGiteaPageRelative
+ }
+ if strings.HasPrefix(strings.ToLower(cleanedPath+"/"), strings.ToLower(setting.AppSubURL+"/")) {
+ return cleanedPath[len(setting.AppSubURL):], urlTypeGiteaSiteRelative
+ }
+ return "", urlTypeUnknown
}
+ u.Path = cleanedPath + "/"
urlLower := strings.ToLower(u.String())
- return strings.HasPrefix(urlLower, strings.ToLower(setting.AppURL)) || strings.HasPrefix(urlLower, strings.ToLower(GuessCurrentAppURL(ctx)))
+ if strings.HasPrefix(urlLower, strings.ToLower(setting.AppURL)) {
+ return cleanedPath[len(setting.AppSubURL):], urlTypeGiteaAbsolute
+ }
+ guessedCurURL := GuessCurrentAppURL(ctx)
+ if strings.HasPrefix(urlLower, strings.ToLower(guessedCurURL)) {
+ return cleanedPath[len(setting.AppSubURL):], urlTypeGiteaAbsolute
+ }
+ return "", urlTypeUnknown
+}
+
+func IsCurrentGiteaSiteURL(ctx context.Context, s string) bool {
+ _, ut := detectURLRoutePath(ctx, s)
+ return ut != urlTypeUnknown
+}
+
+type GiteaSiteURL struct {
+ RoutePath string
+ OwnerName string
+ RepoName string
+ RepoSubPath string
+}
+
+func ParseGiteaSiteURL(ctx context.Context, s string) *GiteaSiteURL {
+ routePath, ut := detectURLRoutePath(ctx, s)
+ if ut == urlTypeUnknown || ut == urlTypeGiteaPageRelative {
+ return nil
+ }
+ ret := &GiteaSiteURL{RoutePath: routePath}
+ fields := strings.SplitN(strings.TrimPrefix(ret.RoutePath, "/"), "/", 3)
+
+ // TODO: now it only does a quick check for some known reserved paths, should do more strict checks in the future
+ if fields[0] == "attachments" {
+ return ret
+ }
+ if len(fields) < 2 {
+ return ret
+ }
+ ret.OwnerName = fields[0]
+ ret.RepoName = fields[1]
+ if len(fields) == 3 {
+ ret.RepoSubPath = "/" + fields[2]
+ }
+ return ret
}
diff --git a/modules/httplib/url_test.go b/modules/httplib/url_test.go
index fc6c91cd3a..0ffb0cac05 100644
--- a/modules/httplib/url_test.go
+++ b/modules/httplib/url_test.go
@@ -5,6 +5,7 @@ package httplib
import (
"context"
+ "crypto/tls"
"net/http"
"testing"
@@ -39,12 +40,48 @@ func TestIsRelativeURL(t *testing.T) {
}
}
+func TestGuessCurrentHostURL(t *testing.T) {
+ defer test.MockVariableValue(&setting.AppURL, "http://cfg-host/sub/")()
+ defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
+ headersWithProto := http.Header{"X-Forwarded-Proto": {"https"}}
+
+ t.Run("Legacy", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.PublicURLDetection, setting.PublicURLLegacy)()
+
+ assert.Equal(t, "http://cfg-host", GuessCurrentHostURL(t.Context()))
+
+ // legacy: "Host" is not used when there is no "X-Forwarded-Proto" header
+ ctx := context.WithValue(t.Context(), RequestContextKey, &http.Request{Host: "req-host:3000"})
+ assert.Equal(t, "http://cfg-host", GuessCurrentHostURL(ctx))
+
+ // if "X-Forwarded-Proto" exists, then use it and "Host" header
+ ctx = context.WithValue(t.Context(), RequestContextKey, &http.Request{Host: "req-host:3000", Header: headersWithProto})
+ assert.Equal(t, "https://req-host:3000", GuessCurrentHostURL(ctx))
+ })
+
+ t.Run("Auto", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.PublicURLDetection, setting.PublicURLAuto)()
+
+ assert.Equal(t, "http://cfg-host", GuessCurrentHostURL(t.Context()))
+
+ // auto: always use "Host" header, the scheme is determined by "X-Forwarded-Proto" header, or TLS config if no "X-Forwarded-Proto" header
+ ctx := context.WithValue(t.Context(), RequestContextKey, &http.Request{Host: "req-host:3000"})
+ assert.Equal(t, "http://req-host:3000", GuessCurrentHostURL(ctx))
+
+ ctx = context.WithValue(t.Context(), RequestContextKey, &http.Request{Host: "req-host", TLS: &tls.ConnectionState{}})
+ assert.Equal(t, "https://req-host", GuessCurrentHostURL(ctx))
+
+ ctx = context.WithValue(t.Context(), RequestContextKey, &http.Request{Host: "req-host:3000", Header: headersWithProto})
+ assert.Equal(t, "https://req-host:3000", GuessCurrentHostURL(ctx))
+ })
+}
+
func TestMakeAbsoluteURL(t *testing.T) {
defer test.MockVariableValue(&setting.Protocol, "http")()
defer test.MockVariableValue(&setting.AppURL, "http://cfg-host/sub/")()
defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
- ctx := context.Background()
+ ctx := t.Context()
assert.Equal(t, "http://cfg-host/sub/", MakeAbsoluteURL(ctx, ""))
assert.Equal(t, "http://cfg-host/foo", MakeAbsoluteURL(ctx, "foo"))
assert.Equal(t, "http://cfg-host/foo", MakeAbsoluteURL(ctx, "/foo"))
@@ -76,7 +113,7 @@ func TestMakeAbsoluteURL(t *testing.T) {
func TestIsCurrentGiteaSiteURL(t *testing.T) {
defer test.MockVariableValue(&setting.AppURL, "http://localhost:3000/sub/")()
defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
- ctx := context.Background()
+ ctx := t.Context()
good := []string{
"?key=val",
"/sub",
@@ -122,3 +159,26 @@ func TestIsCurrentGiteaSiteURL(t *testing.T) {
assert.True(t, IsCurrentGiteaSiteURL(ctx, "https://user-host"))
assert.False(t, IsCurrentGiteaSiteURL(ctx, "https://forwarded-host"))
}
+
+func TestParseGiteaSiteURL(t *testing.T) {
+ defer test.MockVariableValue(&setting.AppURL, "http://localhost:3000/sub/")()
+ defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
+ ctx := t.Context()
+ tests := []struct {
+ url string
+ exp *GiteaSiteURL
+ }{
+ {"http://localhost:3000/sub?k=v", &GiteaSiteURL{RoutePath: ""}},
+ {"http://localhost:3000/sub/", &GiteaSiteURL{RoutePath: ""}},
+ {"http://localhost:3000/sub/foo", &GiteaSiteURL{RoutePath: "/foo"}},
+ {"http://localhost:3000/sub/foo/bar", &GiteaSiteURL{RoutePath: "/foo/bar", OwnerName: "foo", RepoName: "bar"}},
+ {"http://localhost:3000/sub/foo/bar/", &GiteaSiteURL{RoutePath: "/foo/bar", OwnerName: "foo", RepoName: "bar"}},
+ {"http://localhost:3000/sub/attachments/bar", &GiteaSiteURL{RoutePath: "/attachments/bar"}},
+ {"http://localhost:3000/other", nil},
+ {"http://other/", nil},
+ }
+ for _, test := range tests {
+ su := ParseGiteaSiteURL(ctx, test.url)
+ assert.Equal(t, test.exp, su, "URL = %s", test.url)
+ }
+}
diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go
index 772317fa59..70f0995a01 100644
--- a/modules/indexer/code/bleve/bleve.go
+++ b/modules/indexer/code/bleve/bleve.go
@@ -16,7 +16,7 @@ import (
"code.gitea.io/gitea/modules/analyze"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/indexer"
path_filter "code.gitea.io/gitea/modules/indexer/code/bleve/token/path"
"code.gitea.io/gitea/modules/indexer/code/internal"
indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
@@ -24,11 +24,11 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/typesniffer"
+ "code.gitea.io/gitea/modules/util"
"github.com/blevesearch/bleve/v2"
analyzer_custom "github.com/blevesearch/bleve/v2/analysis/analyzer/custom"
analyzer_keyword "github.com/blevesearch/bleve/v2/analysis/analyzer/keyword"
- "github.com/blevesearch/bleve/v2/analysis/token/camelcase"
"github.com/blevesearch/bleve/v2/analysis/token/lowercase"
"github.com/blevesearch/bleve/v2/analysis/token/unicodenorm"
"github.com/blevesearch/bleve/v2/analysis/tokenizer/letter"
@@ -70,7 +70,7 @@ const (
filenameIndexerAnalyzer = "filenameIndexerAnalyzer"
filenameIndexerTokenizer = "filenameIndexerTokenizer"
repoIndexerDocType = "repoIndexerDocType"
- repoIndexerLatestVersion = 8
+ repoIndexerLatestVersion = 9
)
// generateBleveIndexMapping generates a bleve index mapping for the repo indexer
@@ -107,7 +107,7 @@ func generateBleveIndexMapping() (mapping.IndexMapping, error) {
"type": analyzer_custom.Name,
"char_filters": []string{},
"tokenizer": letter.Name,
- "token_filters": []string{unicodeNormalizeName, camelcase.Name, lowercase.Name},
+ "token_filters": []string{unicodeNormalizeName, lowercase.Name},
}); err != nil {
return nil, err
}
@@ -136,6 +136,10 @@ type Indexer struct {
indexer_internal.Indexer // do not composite inner_bleve.Indexer directly to avoid exposing too much
}
+func (b *Indexer) SupportedSearchModes() []indexer.SearchMode {
+ return indexer.SearchModesExactWords()
+}
+
// NewIndexer creates a new bleve local indexer
func NewIndexer(indexDir string) *Indexer {
inner := inner_bleve.NewIndexer(indexDir, repoIndexerLatestVersion, generateBleveIndexMapping)
@@ -158,7 +162,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro
var err error
if !update.Sized {
var stdout string
- stdout, _, err = git.NewCommand(ctx, "cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
+ stdout, _, err = git.NewCommand("cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
if err != nil {
return err
}
@@ -185,7 +189,8 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro
return err
} else if !typesniffer.DetectContentType(fileContents).IsText() {
// FIXME: UTF-16 files will probably fail here
- return nil
+ // Even if the file is not recognized as a "text file", we could still put its name into the indexers to make the filename become searchable, while leave the content to empty.
+ fileContents = nil
}
if _, err = batchReader.Discard(1); err != nil {
@@ -211,12 +216,7 @@ func (b *Indexer) addDelete(filename string, repo *repo_model.Repository, batch
func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *internal.RepoChanges) error {
batch := inner_bleve.NewFlushingBatch(b.inner.Indexer, maxBatchSize)
if len(changes.Updates) > 0 {
- r, err := gitrepo.OpenRepository(ctx, repo)
- if err != nil {
- return err
- }
- defer r.Close()
- gitBatch, err := r.NewBatch(ctx)
+ gitBatch, err := git.NewBatch(ctx, repo.RepoPath())
if err != nil {
return err
}
@@ -260,17 +260,31 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
var (
indexerQuery query.Query
keywordQuery query.Query
+ contentQuery query.Query
)
pathQuery := bleve.NewPrefixQuery(strings.ToLower(opts.Keyword))
pathQuery.FieldVal = "Filename"
pathQuery.SetBoost(10)
- contentQuery := bleve.NewMatchQuery(opts.Keyword)
- contentQuery.FieldVal = "Content"
-
- if opts.IsKeywordFuzzy {
- contentQuery.Fuzziness = inner_bleve.GuessFuzzinessByKeyword(opts.Keyword)
+ searchMode := util.IfZero(opts.SearchMode, b.SupportedSearchModes()[0].ModeValue)
+ if searchMode == indexer.SearchModeExact {
+ // 1.21 used NewPrefixQuery, but it seems not working well, and later releases changed to NewMatchPhraseQuery
+ q := bleve.NewMatchPhraseQuery(opts.Keyword)
+ q.Analyzer = repoIndexerAnalyzer
+ q.FieldVal = "Content"
+ contentQuery = q
+ } else /* words */ {
+ q := bleve.NewMatchQuery(opts.Keyword)
+ q.FieldVal = "Content"
+ q.Analyzer = repoIndexerAnalyzer
+ if searchMode == indexer.SearchModeFuzzy {
+ // this logic doesn't seem right, it is only used to pass the test-case `Keyword: "dESCRIPTION"`, which doesn't seem to be a real-life use-case.
+ q.Fuzziness = inner_bleve.GuessFuzzinessByKeyword(opts.Keyword)
+ } else {
+ q.Operator = query.MatchQueryOperatorAnd
+ }
+ contentQuery = q
}
keywordQuery = bleve.NewDisjunctionQuery(contentQuery, pathQuery)
diff --git a/modules/indexer/code/bleve/token/path/path.go b/modules/indexer/code/bleve/token/path/path.go
index 107e0da109..6dfc12f146 100644
--- a/modules/indexer/code/bleve/token/path/path.go
+++ b/modules/indexer/code/bleve/token/path/path.go
@@ -51,13 +51,13 @@ func generatePathTokens(input analysis.TokenStream, reversed bool) analysis.Toke
slices.Reverse(input)
}
- for i := 0; i < len(input); i++ {
+ for i := range input {
var sb strings.Builder
- sb.WriteString(string(input[0].Term))
+ sb.Write(input[0].Term)
for j := 1; j < i; j++ {
sb.WriteString("/")
- sb.WriteString(string(input[j].Term))
+ sb.Write(input[j].Term)
}
term := sb.String()
@@ -97,5 +97,9 @@ func generatePathTokens(input analysis.TokenStream, reversed bool) analysis.Toke
}
func init() {
- registry.RegisterTokenFilter(Name, TokenFilterConstructor)
+ // FIXME: move it to the bleve's init function, but do not call it in global init
+ err := registry.RegisterTokenFilter(Name, TokenFilterConstructor)
+ if err != nil {
+ panic(err)
+ }
}
diff --git a/modules/indexer/code/elasticsearch/elasticsearch.go b/modules/indexer/code/elasticsearch/elasticsearch.go
index 1c4dd39eff..f925ce396a 100644
--- a/modules/indexer/code/elasticsearch/elasticsearch.go
+++ b/modules/indexer/code/elasticsearch/elasticsearch.go
@@ -15,7 +15,7 @@ import (
"code.gitea.io/gitea/modules/analyze"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/indexer"
"code.gitea.io/gitea/modules/indexer/code/internal"
indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
inner_elasticsearch "code.gitea.io/gitea/modules/indexer/internal/elasticsearch"
@@ -24,6 +24,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/typesniffer"
+ "code.gitea.io/gitea/modules/util"
"github.com/go-enry/go-enry/v2"
"github.com/olivere/elastic/v7"
@@ -45,6 +46,10 @@ type Indexer struct {
indexer_internal.Indexer // do not composite inner_elasticsearch.Indexer directly to avoid exposing too much
}
+func (b *Indexer) SupportedSearchModes() []indexer.SearchMode {
+ return indexer.SearchModesExactWords()
+}
+
// NewIndexer creates a new elasticsearch indexer
func NewIndexer(url, indexerName string) *Indexer {
inner := inner_elasticsearch.NewIndexer(url, indexerName, esRepoIndexerLatestVersion, defaultMapping)
@@ -142,7 +147,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro
var err error
if !update.Sized {
var stdout string
- stdout, _, err = git.NewCommand(ctx, "cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
+ stdout, _, err = git.NewCommand("cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
if err != nil {
return nil, err
}
@@ -203,12 +208,7 @@ func (b *Indexer) addDelete(filename string, repo *repo_model.Repository) elasti
func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *internal.RepoChanges) error {
reqs := make([]elastic.BulkableRequest, 0)
if len(changes.Updates) > 0 {
- r, err := gitrepo.OpenRepository(ctx, repo)
- if err != nil {
- return err
- }
- defer r.Close()
- batch, err := r.NewBatch(ctx)
+ batch, err := git.NewBatch(ctx, repo.RepoPath())
if err != nil {
return err
}
@@ -359,13 +359,16 @@ func extractAggs(searchResult *elastic.SearchResult) []*internal.SearchResultLan
// Search searches for codes and language stats by given conditions.
func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int64, []*internal.SearchResult, []*internal.SearchResultLanguages, error) {
- searchType := esMultiMatchTypePhrasePrefix
- if opts.IsKeywordFuzzy {
- searchType = esMultiMatchTypeBestFields
+ var contentQuery elastic.Query
+ searchMode := util.IfZero(opts.SearchMode, b.SupportedSearchModes()[0].ModeValue)
+ if searchMode == indexer.SearchModeExact {
+ // 1.21 used NewMultiMatchQuery().Type(esMultiMatchTypePhrasePrefix), but later releases changed to NewMatchPhraseQuery
+ contentQuery = elastic.NewMatchPhraseQuery("content", opts.Keyword)
+ } else /* words */ {
+ contentQuery = elastic.NewMultiMatchQuery("content", opts.Keyword).Type(esMultiMatchTypeBestFields).Operator("and")
}
-
kwQuery := elastic.NewBoolQuery().Should(
- elastic.NewMultiMatchQuery(opts.Keyword, "content").Type(searchType),
+ contentQuery,
elastic.NewMultiMatchQuery(opts.Keyword, "filename^10").Type(esMultiMatchTypePhrasePrefix),
)
query := elastic.NewBoolQuery()
diff --git a/modules/indexer/code/elasticsearch/elasticsearch_test.go b/modules/indexer/code/elasticsearch/elasticsearch_test.go
index a6d2af92b2..e8f1f202ce 100644
--- a/modules/indexer/code/elasticsearch/elasticsearch_test.go
+++ b/modules/indexer/code/elasticsearch/elasticsearch_test.go
@@ -11,6 +11,6 @@ import (
func TestIndexPos(t *testing.T) {
startIdx, endIdx := contentMatchIndexPos("test index start and end", "start", "end")
- assert.EqualValues(t, 11, startIdx)
- assert.EqualValues(t, 15, endIdx)
+ assert.Equal(t, 11, startIdx)
+ assert.Equal(t, 15, endIdx)
}
diff --git a/modules/indexer/code/git.go b/modules/indexer/code/git.go
index df9783288b..41bc74e6ec 100644
--- a/modules/indexer/code/git.go
+++ b/modules/indexer/code/git.go
@@ -16,7 +16,7 @@ import (
)
func getDefaultBranchSha(ctx context.Context, repo *repo_model.Repository) (string, error) {
- stdout, _, err := git.NewCommand(ctx, "show-ref", "-s").AddDynamicArguments(git.BranchPrefix + repo.DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
+ stdout, _, err := git.NewCommand("show-ref", "-s").AddDynamicArguments(git.BranchPrefix+repo.DefaultBranch).RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
if err != nil {
return "", err
}
@@ -32,8 +32,8 @@ func getRepoChanges(ctx context.Context, repo *repo_model.Repository, revision s
needGenesis := len(status.CommitSha) == 0
if !needGenesis {
- hasAncestorCmd := git.NewCommand(ctx, "merge-base").AddDynamicArguments(status.CommitSha, revision)
- stdout, _, _ := hasAncestorCmd.RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
+ hasAncestorCmd := git.NewCommand("merge-base").AddDynamicArguments(status.CommitSha, revision)
+ stdout, _, _ := hasAncestorCmd.RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
needGenesis = len(stdout) == 0
}
@@ -86,7 +86,7 @@ func parseGitLsTreeOutput(stdout []byte) ([]internal.FileUpdate, error) {
// genesisChanges get changes to add repo to the indexer for the first time
func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*internal.RepoChanges, error) {
var changes internal.RepoChanges
- stdout, _, runErr := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l", "-r").AddDynamicArguments(revision).RunStdBytes(&git.RunOpts{Dir: repo.RepoPath()})
+ stdout, _, runErr := git.NewCommand("ls-tree", "--full-tree", "-l", "-r").AddDynamicArguments(revision).RunStdBytes(ctx, &git.RunOpts{Dir: repo.RepoPath()})
if runErr != nil {
return nil, runErr
}
@@ -98,8 +98,8 @@ func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision s
// nonGenesisChanges get changes since the previous indexer update
func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*internal.RepoChanges, error) {
- diffCmd := git.NewCommand(ctx, "diff", "--name-status").AddDynamicArguments(repo.CodeIndexerStatus.CommitSha, revision)
- stdout, _, runErr := diffCmd.RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
+ diffCmd := git.NewCommand("diff", "--name-status").AddDynamicArguments(repo.CodeIndexerStatus.CommitSha, revision)
+ stdout, _, runErr := diffCmd.RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
if runErr != nil {
// previous commit sha may have been removed by a force push, so
// try rebuilding from scratch
@@ -115,9 +115,9 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio
updatedFilenames := make([]string, 0, 10)
updateChanges := func() error {
- cmd := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l").AddDynamicArguments(revision).
+ cmd := git.NewCommand("ls-tree", "--full-tree", "-l").AddDynamicArguments(revision).
AddDashesAndList(updatedFilenames...)
- lsTreeStdout, _, err := cmd.RunStdBytes(&git.RunOpts{Dir: repo.RepoPath()})
+ lsTreeStdout, _, err := cmd.RunStdBytes(ctx, &git.RunOpts{Dir: repo.RepoPath()})
if err != nil {
return err
}
@@ -129,8 +129,8 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio
changes.Updates = append(changes.Updates, updates...)
return nil
}
- lines := strings.Split(stdout, "\n")
- for _, line := range lines {
+ lines := strings.SplitSeq(stdout, "\n")
+ for line := range lines {
line = strings.TrimSpace(line)
if len(line) == 0 {
continue
diff --git a/modules/indexer/code/gitgrep/gitgrep.go b/modules/indexer/code/gitgrep/gitgrep.go
new file mode 100644
index 0000000000..6f6e0b47b9
--- /dev/null
+++ b/modules/indexer/code/gitgrep/gitgrep.go
@@ -0,0 +1,66 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package gitgrep
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/indexer"
+ code_indexer "code.gitea.io/gitea/modules/indexer/code"
+ "code.gitea.io/gitea/modules/setting"
+)
+
+func indexSettingToGitGrepPathspecList() (list []string) {
+ for _, expr := range setting.Indexer.IncludePatterns {
+ list = append(list, ":(glob)"+expr.PatternString())
+ }
+ for _, expr := range setting.Indexer.ExcludePatterns {
+ list = append(list, ":(glob,exclude)"+expr.PatternString())
+ }
+ return list
+}
+
+func PerformSearch(ctx context.Context, page int, repoID int64, gitRepo *git.Repository, ref git.RefName, keyword string, searchMode indexer.SearchModeType) (searchResults []*code_indexer.Result, total int, err error) {
+ grepMode := git.GrepModeWords
+ switch searchMode {
+ case indexer.SearchModeExact:
+ grepMode = git.GrepModeExact
+ case indexer.SearchModeRegexp:
+ grepMode = git.GrepModeRegexp
+ }
+ res, err := git.GrepSearch(ctx, gitRepo, keyword, git.GrepOptions{
+ ContextLineNumber: 1,
+ GrepMode: grepMode,
+ RefName: ref.String(),
+ PathspecList: indexSettingToGitGrepPathspecList(),
+ })
+ if err != nil {
+ // TODO: if no branch exists, it reports: exit status 128, fatal: this operation must be run in a work tree.
+ return nil, 0, fmt.Errorf("git.GrepSearch: %w", err)
+ }
+ commitID, err := gitRepo.GetRefCommitID(ref.String())
+ if err != nil {
+ return nil, 0, fmt.Errorf("gitRepo.GetRefCommitID: %w", err)
+ }
+
+ total = len(res)
+ pageStart := min((page-1)*setting.UI.RepoSearchPagingNum, len(res))
+ pageEnd := min(page*setting.UI.RepoSearchPagingNum, len(res))
+ res = res[pageStart:pageEnd]
+ for _, r := range res {
+ searchResults = append(searchResults, &code_indexer.Result{
+ RepoID: repoID,
+ Filename: r.Filename,
+ CommitID: commitID,
+ // UpdatedUnix: not supported yet
+ // Language: not supported yet
+ // Color: not supported yet
+ Lines: code_indexer.HighlightSearchResultCode(r.Filename, "", r.LineNumbers, strings.Join(r.LineCodes, "\n")),
+ })
+ }
+ return searchResults, total, nil
+}
diff --git a/routers/web/repo/search_test.go b/modules/indexer/code/gitgrep/gitgrep_test.go
index 33a1610384..97dda9d966 100644
--- a/routers/web/repo/search_test.go
+++ b/modules/indexer/code/gitgrep/gitgrep_test.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package repo
+package gitgrep
import (
"testing"
diff --git a/modules/indexer/code/indexer.go b/modules/indexer/code/indexer.go
index 728b37fab6..6035ddfe95 100644
--- a/modules/indexer/code/indexer.go
+++ b/modules/indexer/code/indexer.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/graceful"
+ "code.gitea.io/gitea/modules/indexer"
"code.gitea.io/gitea/modules/indexer/code/bleve"
"code.gitea.io/gitea/modules/indexer/code/elasticsearch"
"code.gitea.io/gitea/modules/indexer/code/internal"
@@ -29,13 +30,11 @@ var (
// When the real indexer is not ready, it will be a dummy indexer which will return error to explain it's not ready.
// So it's always safe use it as *globalIndexer.Load() and call its methods.
globalIndexer atomic.Pointer[internal.Indexer]
- dummyIndexer *internal.Indexer
)
func init() {
- i := internal.NewDummyIndexer()
- dummyIndexer = &i
- globalIndexer.Store(dummyIndexer)
+ dummyIndexer := internal.NewDummyIndexer()
+ globalIndexer.Store(&dummyIndexer)
}
func index(ctx context.Context, indexer internal.Indexer, repoID int64) error {
@@ -304,3 +303,11 @@ func populateRepoIndexer(ctx context.Context) {
}
log.Info("Done (re)populating the repo indexer with existing repositories")
}
+
+func SupportedSearchModes() []indexer.SearchMode {
+ gi := globalIndexer.Load()
+ if gi == nil {
+ return nil
+ }
+ return (*gi).SupportedSearchModes()
+}
diff --git a/modules/indexer/code/indexer_test.go b/modules/indexer/code/indexer_test.go
index f358bbe785..78fea22f10 100644
--- a/modules/indexer/code/indexer_test.go
+++ b/modules/indexer/code/indexer_test.go
@@ -11,12 +11,13 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/git"
+ indexer_module "code.gitea.io/gitea/modules/indexer"
"code.gitea.io/gitea/modules/indexer/code/bleve"
"code.gitea.io/gitea/modules/indexer/code/elasticsearch"
"code.gitea.io/gitea/modules/indexer/code/internal"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
+ "code.gitea.io/gitea/modules/util"
_ "code.gitea.io/gitea/models"
_ "code.gitea.io/gitea/models/actions"
@@ -37,13 +38,14 @@ func TestMain(m *testing.M) {
func testIndexer(name string, t *testing.T, indexer internal.Indexer) {
t.Run(name, func(t *testing.T) {
- assert.NoError(t, setupRepositoryIndexes(git.DefaultContext, indexer))
+ assert.NoError(t, setupRepositoryIndexes(t.Context(), indexer))
keywords := []struct {
- RepoIDs []int64
- Keyword string
- Langs int
- Results []codeSearchResult
+ RepoIDs []int64
+ Keyword string
+ Langs int
+ SearchMode indexer_module.SearchModeType
+ Results []codeSearchResult
}{
// Search for an exact match on the contents of a file
// This scenario yields a single result (the file README.md on the repo '1')
@@ -184,9 +186,10 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) {
},
// Search for matches on the contents of files regardless of case.
{
- RepoIDs: nil,
- Keyword: "dESCRIPTION",
- Langs: 1,
+ RepoIDs: nil,
+ Keyword: "dESCRIPTION",
+ Langs: 1,
+ SearchMode: indexer_module.SearchModeFuzzy,
Results: []codeSearchResult{
{
Filename: "README.md",
@@ -194,7 +197,7 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) {
},
},
},
- // Search for an exact match on the filename within the repo '62' (case insenstive).
+ // Search for an exact match on the filename within the repo '62' (case-insensitive).
// This scenario yields a single result (the file avocado.md on the repo '62')
{
RepoIDs: []int64{62},
@@ -207,7 +210,7 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) {
},
},
},
- // Search for matches on the contents of files when the criteria is a expression.
+ // Search for matches on the contents of files when the criteria are an expression.
{
RepoIDs: []int64{62},
Keyword: "console.log",
@@ -219,7 +222,7 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) {
},
},
},
- // Search for matches on the contents of files when the criteria is part of a expression.
+ // Search for matches on the contents of files when the criteria are parts of an expression.
{
RepoIDs: []int64{62},
Keyword: "log",
@@ -235,17 +238,17 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) {
for _, kw := range keywords {
t.Run(kw.Keyword, func(t *testing.T) {
- total, res, langs, err := indexer.Search(context.TODO(), &internal.SearchOptions{
- RepoIDs: kw.RepoIDs,
- Keyword: kw.Keyword,
+ total, res, langs, err := indexer.Search(t.Context(), &internal.SearchOptions{
+ RepoIDs: kw.RepoIDs,
+ Keyword: kw.Keyword,
+ SearchMode: util.IfZero(kw.SearchMode, indexer_module.SearchModeWords),
Paginator: &db.ListOptions{
Page: 1,
PageSize: 10,
},
- IsKeywordFuzzy: true,
})
- assert.NoError(t, err)
- assert.Len(t, langs, kw.Langs)
+ require.NoError(t, err)
+ require.Len(t, langs, kw.Langs)
hits := make([]codeSearchResult, 0, len(res))
@@ -275,7 +278,7 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) {
})
}
- assert.NoError(t, tearDownRepositoryIndexes(indexer))
+ assert.NoError(t, tearDownRepositoryIndexes(t.Context(), indexer))
})
}
@@ -287,10 +290,10 @@ func TestBleveIndexAndSearch(t *testing.T) {
idx := bleve.NewIndexer(dir)
defer idx.Close()
- _, err := idx.Init(context.Background())
+ _, err := idx.Init(t.Context())
require.NoError(t, err)
- testIndexer("beleve", t, idx)
+ testIndexer("bleve", t, idx)
}
func TestESIndexAndSearch(t *testing.T) {
@@ -303,11 +306,11 @@ func TestESIndexAndSearch(t *testing.T) {
}
indexer := elasticsearch.NewIndexer(u, "gitea_codes")
- if _, err := indexer.Init(context.Background()); err != nil {
+ if _, err := indexer.Init(t.Context()); err != nil {
if indexer != nil {
indexer.Close()
}
- assert.FailNow(t, "Unable to init ES indexer Error: %v", err)
+ require.NoError(t, err, "Unable to init ES indexer")
}
defer indexer.Close()
@@ -324,9 +327,9 @@ func setupRepositoryIndexes(ctx context.Context, indexer internal.Indexer) error
return nil
}
-func tearDownRepositoryIndexes(indexer internal.Indexer) error {
+func tearDownRepositoryIndexes(ctx context.Context, indexer internal.Indexer) error {
for _, repoID := range repositoriesToSearch() {
- if err := indexer.Delete(context.Background(), repoID); err != nil {
+ if err := indexer.Delete(ctx, repoID); err != nil {
return err
}
}
diff --git a/modules/indexer/code/internal/indexer.go b/modules/indexer/code/internal/indexer.go
index c259fcd26e..d58b028124 100644
--- a/modules/indexer/code/internal/indexer.go
+++ b/modules/indexer/code/internal/indexer.go
@@ -5,10 +5,11 @@ package internal
import (
"context"
- "fmt"
+ "errors"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/indexer"
"code.gitea.io/gitea/modules/indexer/internal"
)
@@ -18,6 +19,7 @@ type Indexer interface {
Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *RepoChanges) error
Delete(ctx context.Context, repoID int64) error
Search(ctx context.Context, opts *SearchOptions) (int64, []*SearchResult, []*SearchResultLanguages, error)
+ SupportedSearchModes() []indexer.SearchMode
}
type SearchOptions struct {
@@ -25,7 +27,7 @@ type SearchOptions struct {
Keyword string
Language string
- IsKeywordFuzzy bool
+ SearchMode indexer.SearchModeType
db.Paginator
}
@@ -41,14 +43,18 @@ type dummyIndexer struct {
internal.Indexer
}
+func (d *dummyIndexer) SupportedSearchModes() []indexer.SearchMode {
+ return nil
+}
+
func (d *dummyIndexer) Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *RepoChanges) error {
- return fmt.Errorf("indexer is not ready")
+ return errors.New("indexer is not ready")
}
func (d *dummyIndexer) Delete(ctx context.Context, repoID int64) error {
- return fmt.Errorf("indexer is not ready")
+ return errors.New("indexer is not ready")
}
func (d *dummyIndexer) Search(ctx context.Context, opts *SearchOptions) (int64, []*SearchResult, []*SearchResultLanguages, error) {
- return 0, nil, nil, fmt.Errorf("indexer is not ready")
+ return 0, nil, nil, errors.New("indexer is not ready")
}
diff --git a/modules/indexer/code/internal/util.go b/modules/indexer/code/internal/util.go
index 5b95783d9f..fa958be473 100644
--- a/modules/indexer/code/internal/util.go
+++ b/modules/indexer/code/internal/util.go
@@ -10,9 +10,7 @@ import (
"code.gitea.io/gitea/modules/log"
)
-const (
- filenameMatchNumberOfLines = 7 // Copied from github search
-)
+const filenameMatchNumberOfLines = 7 // Copied from GitHub search
func FilenameIndexerID(repoID int64, filename string) string {
return internal.Base36(repoID) + "_" + filename
@@ -35,7 +33,7 @@ func FilenameOfIndexerID(indexerID string) string {
return indexerID[index+1:]
}
-// Given the contents of file, returns the boundaries of its first seven lines.
+// FilenameMatchIndexPos returns the boundaries of its first seven lines.
func FilenameMatchIndexPos(content string) (int, int) {
count := 1
for i, c := range content {
diff --git a/modules/indexer/code/search.go b/modules/indexer/code/search.go
index 74c957dde6..a7a5d7d2e3 100644
--- a/modules/indexer/code/search.go
+++ b/modules/indexer/code/search.go
@@ -77,7 +77,7 @@ func HighlightSearchResultCode(filename, language string, lineNums []int, code s
// The lineNums outputted by highlight.Code might not match the original lineNums, because "highlight" removes the last `\n`
lines := make([]*ResultLine, min(len(highlightedLines), len(lineNums)))
- for i := 0; i < len(lines); i++ {
+ for i := range lines {
lines[i] = &ResultLine{
Num: lineNums[i],
FormattedContent: template.HTML(highlightedLines[i]),
@@ -129,7 +129,6 @@ func searchResult(result *internal.SearchResult, startIndex, endIndex int) (*Res
}
// PerformSearch perform a search on a repository
-// if isFuzzy is true set the Damerau-Levenshtein distance from 0 to 2
func PerformSearch(ctx context.Context, opts *SearchOptions) (int, []*Result, []*SearchResultLanguages, error) {
if opts == nil || len(opts.Keyword) == 0 {
return 0, nil, nil, nil
diff --git a/modules/indexer/indexer.go b/modules/indexer/indexer.go
new file mode 100644
index 0000000000..1e0f81de89
--- /dev/null
+++ b/modules/indexer/indexer.go
@@ -0,0 +1,54 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package indexer
+
+type SearchModeType string
+
+const (
+ SearchModeExact SearchModeType = "exact"
+ SearchModeWords SearchModeType = "words"
+ SearchModeFuzzy SearchModeType = "fuzzy"
+ SearchModeRegexp SearchModeType = "regexp"
+)
+
+type SearchMode struct {
+ ModeValue SearchModeType
+ TooltipTrKey string
+ TitleTrKey string
+}
+
+func SearchModesExactWords() []SearchMode {
+ return []SearchMode{
+ {
+ ModeValue: SearchModeExact,
+ TooltipTrKey: "search.exact_tooltip",
+ TitleTrKey: "search.exact",
+ },
+ {
+ ModeValue: SearchModeWords,
+ TooltipTrKey: "search.words_tooltip",
+ TitleTrKey: "search.words",
+ },
+ }
+}
+
+func SearchModesExactWordsFuzzy() []SearchMode {
+ return append(SearchModesExactWords(), []SearchMode{
+ {
+ ModeValue: SearchModeFuzzy,
+ TooltipTrKey: "search.fuzzy_tooltip",
+ TitleTrKey: "search.fuzzy",
+ },
+ }...)
+}
+
+func GitGrepSupportedSearchModes() []SearchMode {
+ return append(SearchModesExactWords(), []SearchMode{
+ {
+ ModeValue: SearchModeRegexp,
+ TooltipTrKey: "search.regexp_tooltip",
+ TitleTrKey: "search.regexp",
+ },
+ }...)
+}
diff --git a/modules/indexer/internal/bleve/indexer.go b/modules/indexer/internal/bleve/indexer.go
index 01e53ca636..9d1e24a874 100644
--- a/modules/indexer/internal/bleve/indexer.go
+++ b/modules/indexer/internal/bleve/indexer.go
@@ -5,7 +5,7 @@ package bleve
import (
"context"
- "fmt"
+ "errors"
"code.gitea.io/gitea/modules/indexer/internal"
"code.gitea.io/gitea/modules/log"
@@ -39,11 +39,11 @@ func NewIndexer(indexDir string, version int, mappingGetter func() (mapping.Inde
// Init initializes the indexer
func (i *Indexer) Init(_ context.Context) (bool, error) {
if i == nil {
- return false, fmt.Errorf("cannot init nil indexer")
+ return false, errors.New("cannot init nil indexer")
}
if i.Indexer != nil {
- return false, fmt.Errorf("indexer is already initialized")
+ return false, errors.New("indexer is already initialized")
}
indexer, version, err := openIndexer(i.indexDir, i.version)
@@ -83,10 +83,10 @@ func (i *Indexer) Init(_ context.Context) (bool, error) {
// Ping checks if the indexer is available
func (i *Indexer) Ping(_ context.Context) error {
if i == nil {
- return fmt.Errorf("cannot ping nil indexer")
+ return errors.New("cannot ping nil indexer")
}
if i.Indexer == nil {
- return fmt.Errorf("indexer is not initialized")
+ return errors.New("indexer is not initialized")
}
return nil
}
diff --git a/modules/indexer/internal/bleve/query.go b/modules/indexer/internal/bleve/query.go
index 1b18ca1a77..8895ae2c64 100644
--- a/modules/indexer/internal/bleve/query.go
+++ b/modules/indexer/internal/bleve/query.go
@@ -28,6 +28,16 @@ func MatchPhraseQuery(matchPhrase, field, analyzer string, fuzziness int) *query
return q
}
+// MatchAndQuery generates a match query for the given phrase, field and analyzer
+func MatchAndQuery(matchPhrase, field, analyzer string, fuzziness int) *query.MatchQuery {
+ q := bleve.NewMatchQuery(matchPhrase)
+ q.FieldVal = field
+ q.Analyzer = analyzer
+ q.Fuzziness = fuzziness
+ q.Operator = query.MatchQueryOperatorAnd
+ return q
+}
+
// BoolFieldQuery generates a bool field query for the given value and field
func BoolFieldQuery(value bool, field string) *query.BoolFieldQuery {
q := bleve.NewBoolFieldQuery(value)
diff --git a/modules/indexer/internal/elasticsearch/indexer.go b/modules/indexer/internal/elasticsearch/indexer.go
index 395eea3bce..265ce26585 100644
--- a/modules/indexer/internal/elasticsearch/indexer.go
+++ b/modules/indexer/internal/elasticsearch/indexer.go
@@ -5,6 +5,7 @@ package elasticsearch
import (
"context"
+ "errors"
"fmt"
"code.gitea.io/gitea/modules/indexer/internal"
@@ -36,10 +37,10 @@ func NewIndexer(url, indexName string, version int, mapping string) *Indexer {
// Init initializes the indexer
func (i *Indexer) Init(ctx context.Context) (bool, error) {
if i == nil {
- return false, fmt.Errorf("cannot init nil indexer")
+ return false, errors.New("cannot init nil indexer")
}
if i.Client != nil {
- return false, fmt.Errorf("indexer is already initialized")
+ return false, errors.New("indexer is already initialized")
}
client, err := i.initClient()
@@ -66,10 +67,10 @@ func (i *Indexer) Init(ctx context.Context) (bool, error) {
// Ping checks if the indexer is available
func (i *Indexer) Ping(ctx context.Context) error {
if i == nil {
- return fmt.Errorf("cannot ping nil indexer")
+ return errors.New("cannot ping nil indexer")
}
if i.Client == nil {
- return fmt.Errorf("indexer is not initialized")
+ return errors.New("indexer is not initialized")
}
resp, err := i.Client.ClusterHealth().Do(ctx)
diff --git a/modules/indexer/internal/indexer.go b/modules/indexer/internal/indexer.go
index c7f356da1e..3442bbaff2 100644
--- a/modules/indexer/internal/indexer.go
+++ b/modules/indexer/internal/indexer.go
@@ -5,7 +5,7 @@ package internal
import (
"context"
- "fmt"
+ "errors"
)
// Indexer defines an basic indexer interface
@@ -27,11 +27,11 @@ func NewDummyIndexer() Indexer {
type dummyIndexer struct{}
func (d *dummyIndexer) Init(ctx context.Context) (bool, error) {
- return false, fmt.Errorf("indexer is not ready")
+ return false, errors.New("indexer is not ready")
}
func (d *dummyIndexer) Ping(ctx context.Context) error {
- return fmt.Errorf("indexer is not ready")
+ return errors.New("indexer is not ready")
}
func (d *dummyIndexer) Close() {}
diff --git a/modules/indexer/internal/meilisearch/indexer.go b/modules/indexer/internal/meilisearch/indexer.go
index 01bb49bbfc..65db75bb55 100644
--- a/modules/indexer/internal/meilisearch/indexer.go
+++ b/modules/indexer/internal/meilisearch/indexer.go
@@ -5,6 +5,7 @@ package meilisearch
import (
"context"
+ "errors"
"fmt"
"github.com/meilisearch/meilisearch-go"
@@ -33,11 +34,11 @@ func NewIndexer(url, apiKey, indexName string, version int, settings *meilisearc
// Init initializes the indexer
func (i *Indexer) Init(_ context.Context) (bool, error) {
if i == nil {
- return false, fmt.Errorf("cannot init nil indexer")
+ return false, errors.New("cannot init nil indexer")
}
if i.Client != nil {
- return false, fmt.Errorf("indexer is already initialized")
+ return false, errors.New("indexer is already initialized")
}
i.Client = meilisearch.New(i.url, meilisearch.WithAPIKey(i.apiKey))
@@ -62,10 +63,10 @@ func (i *Indexer) Init(_ context.Context) (bool, error) {
// Ping checks if the indexer is available
func (i *Indexer) Ping(ctx context.Context) error {
if i == nil {
- return fmt.Errorf("cannot ping nil indexer")
+ return errors.New("cannot ping nil indexer")
}
if i.Client == nil {
- return fmt.Errorf("indexer is not initialized")
+ return errors.New("indexer is not initialized")
}
resp, err := i.Client.Health()
if err != nil {
diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go
index bf51bd6c14..39d96cab98 100644
--- a/modules/indexer/issues/bleve/bleve.go
+++ b/modules/indexer/issues/bleve/bleve.go
@@ -5,10 +5,14 @@ package bleve
import (
"context"
+ "strconv"
+ "code.gitea.io/gitea/modules/indexer"
indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
inner_bleve "code.gitea.io/gitea/modules/indexer/internal/bleve"
"code.gitea.io/gitea/modules/indexer/issues/internal"
+ "code.gitea.io/gitea/modules/optional"
+ "code.gitea.io/gitea/modules/util"
"github.com/blevesearch/bleve/v2"
"github.com/blevesearch/bleve/v2/analysis/analyzer/custom"
@@ -120,6 +124,10 @@ type Indexer struct {
indexer_internal.Indexer // do not composite inner_bleve.Indexer directly to avoid exposing too much
}
+func (b *Indexer) SupportedSearchModes() []indexer.SearchMode {
+ return indexer.SearchModesExactWordsFuzzy()
+}
+
// NewIndexer creates a new bleve local indexer
func NewIndexer(indexDir string) *Indexer {
inner := inner_bleve.NewIndexer(indexDir, issueIndexerLatestVersion, generateIssueIndexMapping)
@@ -157,16 +165,24 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
var queries []query.Query
if options.Keyword != "" {
- fuzziness := 0
- if options.IsFuzzyKeyword {
- fuzziness = inner_bleve.GuessFuzzinessByKeyword(options.Keyword)
+ searchMode := util.IfZero(options.SearchMode, b.SupportedSearchModes()[0].ModeValue)
+ if searchMode == indexer.SearchModeWords || searchMode == indexer.SearchModeFuzzy {
+ fuzziness := 0
+ if searchMode == indexer.SearchModeFuzzy {
+ fuzziness = inner_bleve.GuessFuzzinessByKeyword(options.Keyword)
+ }
+ queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
+ inner_bleve.MatchAndQuery(options.Keyword, "title", issueIndexerAnalyzer, fuzziness),
+ inner_bleve.MatchAndQuery(options.Keyword, "content", issueIndexerAnalyzer, fuzziness),
+ inner_bleve.MatchAndQuery(options.Keyword, "comments", issueIndexerAnalyzer, fuzziness),
+ }...))
+ } else /* exact */ {
+ queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
+ inner_bleve.MatchPhraseQuery(options.Keyword, "title", issueIndexerAnalyzer, 0),
+ inner_bleve.MatchPhraseQuery(options.Keyword, "content", issueIndexerAnalyzer, 0),
+ inner_bleve.MatchPhraseQuery(options.Keyword, "comments", issueIndexerAnalyzer, 0),
+ }...))
}
-
- queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
- inner_bleve.MatchPhraseQuery(options.Keyword, "title", issueIndexerAnalyzer, fuzziness),
- inner_bleve.MatchPhraseQuery(options.Keyword, "content", issueIndexerAnalyzer, fuzziness),
- inner_bleve.MatchPhraseQuery(options.Keyword, "comments", issueIndexerAnalyzer, fuzziness),
- }...))
}
if len(options.RepoIDs) > 0 || options.AllPublic {
@@ -232,12 +248,20 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectColumnID.Value(), "project_board_id"))
}
- if options.PosterID.Has() {
- queries = append(queries, inner_bleve.NumericEqualityQuery(options.PosterID.Value(), "poster_id"))
+ if options.PosterID != "" {
+ // "(none)" becomes 0, it means no poster
+ posterIDInt64, _ := strconv.ParseInt(options.PosterID, 10, 64)
+ queries = append(queries, inner_bleve.NumericEqualityQuery(posterIDInt64, "poster_id"))
}
- if options.AssigneeID.Has() {
- queries = append(queries, inner_bleve.NumericEqualityQuery(options.AssigneeID.Value(), "assignee_id"))
+ if options.AssigneeID != "" {
+ if options.AssigneeID == "(any)" {
+ queries = append(queries, inner_bleve.NumericRangeInclusiveQuery(optional.Some[int64](1), optional.None[int64](), "assignee_id"))
+ } else {
+ // "(none)" becomes 0, it means no assignee
+ assigneeIDInt64, _ := strconv.ParseInt(options.AssigneeID, 10, 64)
+ queries = append(queries, inner_bleve.NumericEqualityQuery(assigneeIDInt64, "assignee_id"))
+ }
}
if options.MentionID.Has() {
diff --git a/modules/indexer/issues/db/db.go b/modules/indexer/issues/db/db.go
index 6c9cfcf670..50951f9c88 100644
--- a/modules/indexer/issues/db/db.go
+++ b/modules/indexer/issues/db/db.go
@@ -5,29 +5,35 @@ package db
import (
"context"
+ "strings"
+ "sync"
"code.gitea.io/gitea/models/db"
issue_model "code.gitea.io/gitea/models/issues"
+ "code.gitea.io/gitea/modules/indexer"
indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
inner_db "code.gitea.io/gitea/modules/indexer/internal/db"
"code.gitea.io/gitea/modules/indexer/issues/internal"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
-var _ internal.Indexer = &Indexer{}
+var _ internal.Indexer = (*Indexer)(nil)
// Indexer implements Indexer interface to use database's like search
type Indexer struct {
indexer_internal.Indexer
}
-func NewIndexer() *Indexer {
- return &Indexer{
- Indexer: &inner_db.Indexer{},
- }
+func (i *Indexer) SupportedSearchModes() []indexer.SearchMode {
+ return indexer.SearchModesExactWords()
}
+var GetIndexer = sync.OnceValue(func() *Indexer {
+ return &Indexer{Indexer: &inner_db.Indexer{}}
+})
+
// Index dummy function
func (i *Indexer) Index(_ context.Context, _ ...*internal.IndexerData) error {
return nil
@@ -38,6 +44,26 @@ func (i *Indexer) Delete(_ context.Context, _ ...int64) error {
return nil
}
+func buildMatchQuery(mode indexer.SearchModeType, colName, keyword string) builder.Cond {
+ if mode == indexer.SearchModeExact {
+ return db.BuildCaseInsensitiveLike(colName, keyword)
+ }
+
+ // match words
+ cond := builder.NewCond()
+ fields := strings.Fields(keyword)
+ if len(fields) == 0 {
+ return builder.Expr("1=1")
+ }
+ for _, field := range fields {
+ if field == "" {
+ continue
+ }
+ cond = cond.And(db.BuildCaseInsensitiveLike(colName, field))
+ }
+ return cond
+}
+
// Search searches for issues
func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (*internal.SearchResult, error) {
// FIXME: I tried to avoid importing models here, but it seems to be impossible.
@@ -58,16 +84,16 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
repoCond = builder.Eq{"repo_id": options.RepoIDs[0]}
}
subQuery := builder.Select("id").From("issue").Where(repoCond)
-
+ searchMode := util.IfZero(options.SearchMode, i.SupportedSearchModes()[0].ModeValue)
cond = builder.Or(
- db.BuildCaseInsensitiveLike("issue.name", options.Keyword),
- db.BuildCaseInsensitiveLike("issue.content", options.Keyword),
+ buildMatchQuery(searchMode, "issue.name", options.Keyword),
+ buildMatchQuery(searchMode, "issue.content", options.Keyword),
builder.In("issue.id", builder.Select("issue_id").
From("comment").
Where(builder.And(
builder.Eq{"type": issue_model.CommentTypeComment},
builder.In("issue_id", subQuery),
- db.BuildCaseInsensitiveLike("content", options.Keyword),
+ buildMatchQuery(searchMode, "content", options.Keyword),
)),
),
)
@@ -95,7 +121,11 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
}, nil
}
- ids, total, err := issue_model.IssueIDs(ctx, opt, cond)
+ return i.FindWithIssueOptions(ctx, opt, cond)
+}
+
+func (i *Indexer) FindWithIssueOptions(ctx context.Context, opt *issue_model.IssuesOptions, otherConds ...builder.Cond) (*internal.SearchResult, error) {
+ ids, total, err := issue_model.IssueIDs(ctx, opt, otherConds...)
if err != nil {
return nil, err
}
diff --git a/modules/indexer/issues/db/options.go b/modules/indexer/issues/db/options.go
index 42834f6e88..380a25dc23 100644
--- a/modules/indexer/issues/db/options.go
+++ b/modules/indexer/issues/db/options.go
@@ -6,6 +6,7 @@ package db
import (
"context"
"fmt"
+ "strings"
"code.gitea.io/gitea/models/db"
issue_model "code.gitea.io/gitea/models/issues"
@@ -34,7 +35,11 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m
case internal.SortByDeadlineAsc:
sortType = "nearduedate"
default:
- sortType = "newest"
+ if strings.HasPrefix(string(options.SortBy), issue_model.ScopeSortPrefix) {
+ sortType = string(options.SortBy)
+ } else {
+ sortType = "newest"
+ }
}
// See the comment of issues_model.SearchOptions for the reason why we need to convert
@@ -54,7 +59,7 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m
RepoIDs: options.RepoIDs,
AllPublic: options.AllPublic,
RepoCond: nil,
- AssigneeID: optional.Some(convertID(options.AssigneeID)),
+ AssigneeID: options.AssigneeID,
PosterID: options.PosterID,
MentionedID: convertID(options.MentionID),
ReviewRequestedID: convertID(options.ReviewRequestedID),
@@ -68,14 +73,13 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m
ExcludedLabelNames: nil,
IncludeMilestones: nil,
SortType: sortType,
- IssueIDs: nil,
UpdatedAfterUnix: options.UpdatedAfterUnix.Value(),
UpdatedBeforeUnix: options.UpdatedBeforeUnix.Value(),
PriorityRepoID: 0,
IsArchived: options.IsArchived,
- Org: nil,
+ Owner: nil,
Team: nil,
- User: nil,
+ Doer: nil,
}
if len(options.MilestoneIDs) == 1 && options.MilestoneIDs[0] == 0 {
diff --git a/modules/indexer/issues/dboptions.go b/modules/indexer/issues/dboptions.go
index 4f6ad96d22..f17724664d 100644
--- a/modules/indexer/issues/dboptions.go
+++ b/modules/indexer/issues/dboptions.go
@@ -4,12 +4,19 @@
package issues
import (
+ "strings"
+
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
+ "code.gitea.io/gitea/modules/indexer/issues/internal"
"code.gitea.io/gitea/modules/optional"
+ "code.gitea.io/gitea/modules/setting"
)
func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOptions {
+ if opts.IssueIDs != nil {
+ setting.PanicInDevOrTesting("Indexer SearchOptions doesn't support IssueIDs")
+ }
searchOpt := &SearchOptions{
Keyword: keyword,
RepoIDs: opts.RepoIDs,
@@ -45,11 +52,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
searchOpt.ProjectID = optional.Some[int64](0) // Those issues with no project(projectid==0)
}
- if opts.AssigneeID.Value() == db.NoConditionID {
- searchOpt.AssigneeID = optional.Some[int64](0) // FIXME: this is inconsistent from other places, 0 means "no assignee"
- } else if opts.AssigneeID.Value() != 0 {
- searchOpt.AssigneeID = opts.AssigneeID
- }
+ searchOpt.AssigneeID = opts.AssigneeID
// See the comment of issues_model.SearchOptions for the reason why we need to convert
convertID := func(id int64) optional.Option[int64] {
@@ -99,7 +102,11 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
// Unsupported sort type for search
fallthrough
default:
- searchOpt.SortBy = SortByUpdatedDesc
+ if strings.HasPrefix(opts.SortType, issues_model.ScopeSortPrefix) {
+ searchOpt.SortBy = internal.SortBy(opts.SortType)
+ } else {
+ searchOpt.SortBy = SortByUpdatedDesc
+ }
}
return searchOpt
diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go
index 4c293f3f2a..9d627466ef 100644
--- a/modules/indexer/issues/elasticsearch/elasticsearch.go
+++ b/modules/indexer/issues/elasticsearch/elasticsearch.go
@@ -5,14 +5,15 @@ package elasticsearch
import (
"context"
- "fmt"
"strconv"
"strings"
"code.gitea.io/gitea/modules/graceful"
+ "code.gitea.io/gitea/modules/indexer"
indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
inner_elasticsearch "code.gitea.io/gitea/modules/indexer/internal/elasticsearch"
"code.gitea.io/gitea/modules/indexer/issues/internal"
+ "code.gitea.io/gitea/modules/util"
"github.com/olivere/elastic/v7"
)
@@ -33,6 +34,11 @@ type Indexer struct {
indexer_internal.Indexer // do not composite inner_elasticsearch.Indexer directly to avoid exposing too much
}
+func (b *Indexer) SupportedSearchModes() []indexer.SearchMode {
+ // TODO: es supports fuzzy search, but our code doesn't at the moment, and actually the default fuzziness is already "AUTO"
+ return indexer.SearchModesExactWords()
+}
+
// NewIndexer creates a new elasticsearch indexer
func NewIndexer(url, indexerName string) *Indexer {
inner := inner_elasticsearch.NewIndexer(url, indexerName, issueIndexerLatestVersion, defaultMapping)
@@ -89,7 +95,7 @@ func (b *Indexer) Index(ctx context.Context, issues ...*internal.IndexerData) er
issue := issues[0]
_, err := b.inner.Client.Index().
Index(b.inner.VersionedIndexName()).
- Id(fmt.Sprintf("%d", issue.ID)).
+ Id(strconv.FormatInt(issue.ID, 10)).
BodyJson(issue).
Do(ctx)
return err
@@ -100,7 +106,7 @@ func (b *Indexer) Index(ctx context.Context, issues ...*internal.IndexerData) er
reqs = append(reqs,
elastic.NewBulkIndexRequest().
Index(b.inner.VersionedIndexName()).
- Id(fmt.Sprintf("%d", issue.ID)).
+ Id(strconv.FormatInt(issue.ID, 10)).
Doc(issue),
)
}
@@ -119,7 +125,7 @@ func (b *Indexer) Delete(ctx context.Context, ids ...int64) error {
} else if len(ids) == 1 {
_, err := b.inner.Client.Delete().
Index(b.inner.VersionedIndexName()).
- Id(fmt.Sprintf("%d", ids[0])).
+ Id(strconv.FormatInt(ids[0], 10)).
Do(ctx)
return err
}
@@ -129,7 +135,7 @@ func (b *Indexer) Delete(ctx context.Context, ids ...int64) error {
reqs = append(reqs,
elastic.NewBulkDeleteRequest().
Index(b.inner.VersionedIndexName()).
- Id(fmt.Sprintf("%d", id)),
+ Id(strconv.FormatInt(id, 10)),
)
}
@@ -146,12 +152,12 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
query := elastic.NewBoolQuery()
if options.Keyword != "" {
- searchType := esMultiMatchTypePhrasePrefix
- if options.IsFuzzyKeyword {
- searchType = esMultiMatchTypeBestFields
+ searchMode := util.IfZero(options.SearchMode, b.SupportedSearchModes()[0].ModeValue)
+ if searchMode == indexer.SearchModeExact {
+ query.Must(elastic.NewMultiMatchQuery(options.Keyword, "title", "content", "comments").Type(esMultiMatchTypePhrasePrefix))
+ } else /* words */ {
+ query.Must(elastic.NewMultiMatchQuery(options.Keyword, "title", "content", "comments").Type(esMultiMatchTypeBestFields).Operator("and"))
}
-
- query.Must(elastic.NewMultiMatchQuery(options.Keyword, "title", "content", "comments").Type(searchType))
}
if len(options.RepoIDs) > 0 {
@@ -205,12 +211,22 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
query.Must(elastic.NewTermQuery("project_board_id", options.ProjectColumnID.Value()))
}
- if options.PosterID.Has() {
- query.Must(elastic.NewTermQuery("poster_id", options.PosterID.Value()))
+ if options.PosterID != "" {
+ // "(none)" becomes 0, it means no poster
+ posterIDInt64, _ := strconv.ParseInt(options.PosterID, 10, 64)
+ query.Must(elastic.NewTermQuery("poster_id", posterIDInt64))
}
- if options.AssigneeID.Has() {
- query.Must(elastic.NewTermQuery("assignee_id", options.AssigneeID.Value()))
+ if options.AssigneeID != "" {
+ if options.AssigneeID == "(any)" {
+ q := elastic.NewRangeQuery("assignee_id")
+ q.Gte(1)
+ query.Must(q)
+ } else {
+ // "(none)" becomes 0, it means no assignee
+ assigneeIDInt64, _ := strconv.ParseInt(options.AssigneeID, 10, 64)
+ query.Must(elastic.NewTermQuery("assignee_id", assigneeIDInt64))
+ }
}
if options.MentionID.Has() {
diff --git a/modules/indexer/issues/elasticsearch/elasticsearch_test.go b/modules/indexer/issues/elasticsearch/elasticsearch_test.go
index ffd85b1aa1..dc329c07dd 100644
--- a/modules/indexer/issues/elasticsearch/elasticsearch_test.go
+++ b/modules/indexer/issues/elasticsearch/elasticsearch_test.go
@@ -11,6 +11,8 @@ import (
"time"
"code.gitea.io/gitea/modules/indexer/issues/internal/tests"
+
+ "github.com/stretchr/testify/require"
)
func TestElasticsearchIndexer(t *testing.T) {
@@ -26,20 +28,10 @@ func TestElasticsearchIndexer(t *testing.T) {
}
}
- ok := false
- for i := 0; i < 60; i++ {
+ require.Eventually(t, func() bool {
resp, err := http.Get(url)
- if err == nil && resp.StatusCode == http.StatusOK {
- ok = true
- break
- }
- t.Logf("Waiting for elasticsearch to be up: %v", err)
- time.Sleep(time.Second)
- }
- if !ok {
- t.Fatalf("Failed to wait for elasticsearch to be up")
- return
- }
+ return err == nil && resp.StatusCode == http.StatusOK
+ }, time.Minute, time.Second, "Expected elasticsearch to be up")
indexer := NewIndexer(url, fmt.Sprintf("test_elasticsearch_indexer_%d", time.Now().Unix()))
defer indexer.Close()
diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go
index c82dc0867e..8f25c84b76 100644
--- a/modules/indexer/issues/indexer.go
+++ b/modules/indexer/issues/indexer.go
@@ -14,6 +14,7 @@ import (
db_model "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/graceful"
+ "code.gitea.io/gitea/modules/indexer"
"code.gitea.io/gitea/modules/indexer/issues/bleve"
"code.gitea.io/gitea/modules/indexer/issues/db"
"code.gitea.io/gitea/modules/indexer/issues/elasticsearch"
@@ -102,7 +103,7 @@ func InitIssueIndexer(syncReindex bool) {
log.Fatal("Unable to issueIndexer.Init with connection %s Error: %v", setting.Indexer.IssueConnStr, err)
}
case "db":
- issueIndexer = db.NewIndexer()
+ issueIndexer = db.GetIndexer()
case "meilisearch":
issueIndexer = meilisearch.NewIndexer(setting.Indexer.IssueConnStr, setting.Indexer.IssueConnAuth, setting.Indexer.IssueIndexerName)
existed, err = issueIndexer.Init(ctx)
@@ -216,7 +217,7 @@ func PopulateIssueIndexer(ctx context.Context) error {
return fmt.Errorf("shutdown before completion: %w", ctx.Err())
default:
}
- repos, _, err := repo_model.SearchRepositoryByName(ctx, &repo_model.SearchRepoOptions{
+ repos, _, err := repo_model.SearchRepositoryByName(ctx, repo_model.SearchRepoOptions{
ListOptions: db_model.ListOptions{Page: page, PageSize: repo_model.RepositoryListDefaultPageSize},
OrderBy: db_model.SearchOrderByID,
Private: true,
@@ -281,7 +282,7 @@ const (
// SearchIssues search issues by options.
func SearchIssues(ctx context.Context, opts *SearchOptions) ([]int64, int64, error) {
- indexer := *globalIndexer.Load()
+ ix := *globalIndexer.Load()
if opts.Keyword == "" || opts.IsKeywordNumeric() {
// This is a conservative shortcut.
@@ -290,20 +291,22 @@ func SearchIssues(ctx context.Context, opts *SearchOptions) ([]int64, int64, err
// So if the user creates an issue and list issues immediately, the issue may not be listed because the indexer needs time to index the issue.
// Even worse, the external indexer like elastic search may not be available for a while,
// and the user may not be able to list issues completely until it is available again.
- indexer = db.NewIndexer()
+ ix = db.GetIndexer()
}
- result, err := indexer.Search(ctx, opts)
+ result, err := ix.Search(ctx, opts)
if err != nil {
return nil, 0, err
}
+ return SearchResultToIDSlice(result), result.Total, nil
+}
+func SearchResultToIDSlice(result *internal.SearchResult) []int64 {
ret := make([]int64, 0, len(result.Hits))
for _, hit := range result.Hits {
ret = append(ret, hit.ID)
}
-
- return ret, result.Total, nil
+ return ret
}
// CountIssues counts issues by options. It is a shortcut of SearchIssues(ctx, opts) but only returns the total count.
@@ -313,3 +316,11 @@ func CountIssues(ctx context.Context, opts *SearchOptions) (int64, error) {
_, total, err := SearchIssues(ctx, opts)
return total, err
}
+
+func SupportedSearchModes() []indexer.SearchMode {
+ gi := globalIndexer.Load()
+ if gi == nil {
+ return nil
+ }
+ return (*gi).SupportedSearchModes()
+}
diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go
index 06a6a46c23..3e38ac49b7 100644
--- a/modules/indexer/issues/indexer_test.go
+++ b/modules/indexer/issues/indexer_test.go
@@ -4,7 +4,6 @@
package issues
import (
- "context"
"testing"
"code.gitea.io/gitea/models/db"
@@ -19,6 +18,7 @@ import (
_ "code.gitea.io/gitea/models/activities"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
@@ -26,7 +26,7 @@ func TestMain(m *testing.M) {
}
func TestDBSearchIssues(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
+ require.NoError(t, unittest.PrepareTestDatabase())
setting.Indexer.IssueType = "db"
InitIssueIndexer(true)
@@ -44,6 +44,7 @@ func TestDBSearchIssues(t *testing.T) {
t.Run("search issues with order", searchIssueWithOrder)
t.Run("search issues in project", searchIssueInProject)
t.Run("search issues with paginator", searchIssueWithPaginator)
+ t.Run("search issues with any assignee", searchIssueWithAnyAssignee)
}
func searchIssueWithKeyword(t *testing.T) {
@@ -82,11 +83,11 @@ func searchIssueWithKeyword(t *testing.T) {
}
for _, test := range tests {
- issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
- if !assert.NoError(t, err) {
- return
- }
- assert.Equal(t, test.expectedIDs, issueIDs)
+ t.Run(test.opts.Keyword, func(t *testing.T) {
+ issueIDs, _, err := SearchIssues(t.Context(), &test.opts)
+ require.NoError(t, err)
+ assert.Equal(t, test.expectedIDs, issueIDs)
+ })
}
}
@@ -119,10 +120,8 @@ func searchIssueByIndex(t *testing.T) {
}
for _, test := range tests {
- issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
- if !assert.NoError(t, err) {
- return
- }
+ issueIDs, _, err := SearchIssues(t.Context(), &test.opts)
+ require.NoError(t, err)
assert.Equal(t, test.expectedIDs, issueIDs)
}
}
@@ -165,10 +164,8 @@ func searchIssueInRepo(t *testing.T) {
}
for _, test := range tests {
- issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
- if !assert.NoError(t, err) {
- return
- }
+ issueIDs, _, err := SearchIssues(t.Context(), &test.opts)
+ require.NoError(t, err)
assert.Equal(t, test.expectedIDs, issueIDs)
}
}
@@ -180,19 +177,19 @@ func searchIssueByID(t *testing.T) {
}{
{
opts: SearchOptions{
- PosterID: optional.Some(int64(1)),
+ PosterID: "1",
},
expectedIDs: []int64{11, 6, 3, 2, 1},
},
{
opts: SearchOptions{
- AssigneeID: optional.Some(int64(1)),
+ AssigneeID: "1",
},
expectedIDs: []int64{6, 1},
},
{
- // NOTE: This tests no assignees filtering and also ToSearchOptions() to ensure it will set AssigneeID to 0 when it is passed as -1.
- opts: *ToSearchOptions("", &issues.IssuesOptions{AssigneeID: optional.Some(db.NoConditionID)}),
+ // NOTE: This tests no assignees filtering and also ToSearchOptions() to ensure it handles the filter correctly
+ opts: *ToSearchOptions("", &issues.IssuesOptions{AssigneeID: "(none)"}),
expectedIDs: []int64{22, 21, 16, 15, 14, 13, 12, 11, 20, 5, 19, 18, 10, 7, 4, 9, 8, 3, 2},
},
{
@@ -237,10 +234,8 @@ func searchIssueByID(t *testing.T) {
}
for _, test := range tests {
- issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
- if !assert.NoError(t, err) {
- return
- }
+ issueIDs, _, err := SearchIssues(t.Context(), &test.opts)
+ require.NoError(t, err)
assert.Equal(t, test.expectedIDs, issueIDs)
}
}
@@ -264,10 +259,8 @@ func searchIssueIsPull(t *testing.T) {
},
}
for _, test := range tests {
- issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
- if !assert.NoError(t, err) {
- return
- }
+ issueIDs, _, err := SearchIssues(t.Context(), &test.opts)
+ require.NoError(t, err)
assert.Equal(t, test.expectedIDs, issueIDs)
}
}
@@ -291,10 +284,8 @@ func searchIssueIsClosed(t *testing.T) {
},
}
for _, test := range tests {
- issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
- if !assert.NoError(t, err) {
- return
- }
+ issueIDs, _, err := SearchIssues(t.Context(), &test.opts)
+ require.NoError(t, err)
assert.Equal(t, test.expectedIDs, issueIDs)
}
}
@@ -318,10 +309,8 @@ func searchIssueIsArchived(t *testing.T) {
},
}
for _, test := range tests {
- issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
- if !assert.NoError(t, err) {
- return
- }
+ issueIDs, _, err := SearchIssues(t.Context(), &test.opts)
+ require.NoError(t, err)
assert.Equal(t, test.expectedIDs, issueIDs)
}
}
@@ -345,10 +334,8 @@ func searchIssueByMilestoneID(t *testing.T) {
},
}
for _, test := range tests {
- issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
- if !assert.NoError(t, err) {
- return
- }
+ issueIDs, _, err := SearchIssues(t.Context(), &test.opts)
+ require.NoError(t, err)
assert.Equal(t, test.expectedIDs, issueIDs)
}
}
@@ -378,10 +365,8 @@ func searchIssueByLabelID(t *testing.T) {
},
}
for _, test := range tests {
- issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
- if !assert.NoError(t, err) {
- return
- }
+ issueIDs, _, err := SearchIssues(t.Context(), &test.opts)
+ require.NoError(t, err)
assert.Equal(t, test.expectedIDs, issueIDs)
}
}
@@ -399,10 +384,8 @@ func searchIssueByTime(t *testing.T) {
},
}
for _, test := range tests {
- issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
- if !assert.NoError(t, err) {
- return
- }
+ issueIDs, _, err := SearchIssues(t.Context(), &test.opts)
+ require.NoError(t, err)
assert.Equal(t, test.expectedIDs, issueIDs)
}
}
@@ -420,10 +403,8 @@ func searchIssueWithOrder(t *testing.T) {
},
}
for _, test := range tests {
- issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
- if !assert.NoError(t, err) {
- return
- }
+ issueIDs, _, err := SearchIssues(t.Context(), &test.opts)
+ require.NoError(t, err)
assert.Equal(t, test.expectedIDs, issueIDs)
}
}
@@ -453,10 +434,8 @@ func searchIssueInProject(t *testing.T) {
},
}
for _, test := range tests {
- issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
- if !assert.NoError(t, err) {
- return
- }
+ issueIDs, _, err := SearchIssues(t.Context(), &test.opts)
+ require.NoError(t, err)
assert.Equal(t, test.expectedIDs, issueIDs)
}
}
@@ -478,10 +457,30 @@ func searchIssueWithPaginator(t *testing.T) {
},
}
for _, test := range tests {
- issueIDs, total, err := SearchIssues(context.TODO(), &test.opts)
- if !assert.NoError(t, err) {
- return
- }
+ issueIDs, total, err := SearchIssues(t.Context(), &test.opts)
+ require.NoError(t, err)
+ assert.Equal(t, test.expectedIDs, issueIDs)
+ assert.Equal(t, test.expectedTotal, total)
+ }
+}
+
+func searchIssueWithAnyAssignee(t *testing.T) {
+ tests := []struct {
+ opts SearchOptions
+ expectedIDs []int64
+ expectedTotal int64
+ }{
+ {
+ SearchOptions{
+ AssigneeID: "(any)",
+ },
+ []int64{17, 6, 1},
+ 3,
+ },
+ }
+ for _, test := range tests {
+ issueIDs, total, err := SearchIssues(t.Context(), &test.opts)
+ require.NoError(t, err)
assert.Equal(t, test.expectedIDs, issueIDs)
assert.Equal(t, test.expectedTotal, total)
}
diff --git a/modules/indexer/issues/internal/indexer.go b/modules/indexer/issues/internal/indexer.go
index 95740bc598..59c6f48485 100644
--- a/modules/indexer/issues/internal/indexer.go
+++ b/modules/indexer/issues/internal/indexer.go
@@ -5,8 +5,9 @@ package internal
import (
"context"
- "fmt"
+ "errors"
+ "code.gitea.io/gitea/modules/indexer"
"code.gitea.io/gitea/modules/indexer/internal"
)
@@ -16,6 +17,7 @@ type Indexer interface {
Index(ctx context.Context, issue ...*IndexerData) error
Delete(ctx context.Context, ids ...int64) error
Search(ctx context.Context, options *SearchOptions) (*SearchResult, error)
+ SupportedSearchModes() []indexer.SearchMode
}
// NewDummyIndexer returns a dummy indexer
@@ -29,14 +31,18 @@ type dummyIndexer struct {
internal.Indexer
}
+func (d *dummyIndexer) SupportedSearchModes() []indexer.SearchMode {
+ return nil
+}
+
func (d *dummyIndexer) Index(_ context.Context, _ ...*IndexerData) error {
- return fmt.Errorf("indexer is not ready")
+ return errors.New("indexer is not ready")
}
func (d *dummyIndexer) Delete(_ context.Context, _ ...int64) error {
- return fmt.Errorf("indexer is not ready")
+ return errors.New("indexer is not ready")
}
func (d *dummyIndexer) Search(_ context.Context, _ *SearchOptions) (*SearchResult, error) {
- return nil, fmt.Errorf("indexer is not ready")
+ return nil, errors.New("indexer is not ready")
}
diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go
index 09dcbf4804..0d4f0f727d 100644
--- a/modules/indexer/issues/internal/model.go
+++ b/modules/indexer/issues/internal/model.go
@@ -7,6 +7,7 @@ import (
"strconv"
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/indexer"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/timeutil"
)
@@ -77,7 +78,7 @@ type SearchResult struct {
type SearchOptions struct {
Keyword string // keyword to search
- IsFuzzyKeyword bool // if false the levenshtein distance is 0
+ SearchMode indexer.SearchModeType
RepoIDs []int64 // repository IDs which the issues belong to
AllPublic bool // if include all public repositories
@@ -96,9 +97,8 @@ type SearchOptions struct {
ProjectID optional.Option[int64] // project the issues belong to
ProjectColumnID optional.Option[int64] // project column the issues belong to
- PosterID optional.Option[int64] // poster of the issues
-
- AssigneeID optional.Option[int64] // assignee of the issues, zero means no assignee
+ PosterID string // poster of the issues, "(none)" or "(any)" or a user ID
+ AssigneeID string // assignee of the issues, "(none)" or "(any)" or a user ID
MentionID optional.Option[int64] // mentioned user of the issues
diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go
index 94ce8520bf..7aebbbcd58 100644
--- a/modules/indexer/issues/internal/tests/tests.go
+++ b/modules/indexer/issues/internal/tests/tests.go
@@ -8,7 +8,6 @@
package tests
import (
- "context"
"fmt"
"slices"
"testing"
@@ -24,10 +23,10 @@ import (
)
func TestIndexer(t *testing.T, indexer internal.Indexer) {
- _, err := indexer.Init(context.Background())
+ _, err := indexer.Init(t.Context())
require.NoError(t, err)
- require.NoError(t, indexer.Ping(context.Background()))
+ require.NoError(t, indexer.Ping(t.Context()))
var (
ids []int64
@@ -39,32 +38,32 @@ func TestIndexer(t *testing.T, indexer internal.Indexer) {
ids = append(ids, v.ID)
data[v.ID] = v
}
- require.NoError(t, indexer.Index(context.Background(), d...))
- require.NoError(t, waitData(indexer, int64(len(data))))
+ require.NoError(t, indexer.Index(t.Context(), d...))
+ waitData(t, indexer, int64(len(data)))
}
defer func() {
- require.NoError(t, indexer.Delete(context.Background(), ids...))
+ require.NoError(t, indexer.Delete(t.Context(), ids...))
}()
for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
if len(c.ExtraData) > 0 {
- require.NoError(t, indexer.Index(context.Background(), c.ExtraData...))
+ require.NoError(t, indexer.Index(t.Context(), c.ExtraData...))
for _, v := range c.ExtraData {
data[v.ID] = v
}
- require.NoError(t, waitData(indexer, int64(len(data))))
+ waitData(t, indexer, int64(len(data)))
defer func() {
for _, v := range c.ExtraData {
- require.NoError(t, indexer.Delete(context.Background(), v.ID))
+ require.NoError(t, indexer.Delete(t.Context(), v.ID))
delete(data, v.ID)
}
- require.NoError(t, waitData(indexer, int64(len(data))))
+ waitData(t, indexer, int64(len(data)))
}()
}
- result, err := indexer.Search(context.Background(), c.SearchOptions)
+ result, err := indexer.Search(t.Context(), c.SearchOptions)
require.NoError(t, err)
if c.Expected != nil {
@@ -80,7 +79,7 @@ func TestIndexer(t *testing.T, indexer internal.Indexer) {
// test counting
c.SearchOptions.Paginator = &db.ListOptions{PageSize: 0}
- countResult, err := indexer.Search(context.Background(), c.SearchOptions)
+ countResult, err := indexer.Search(t.Context(), c.SearchOptions)
require.NoError(t, err)
assert.Empty(t, countResult.Hits)
assert.Equal(t, result.Total, countResult.Total)
@@ -93,7 +92,7 @@ var cases = []*testIndexerCase{
Name: "default",
SearchOptions: &internal.SearchOptions{},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
- assert.Equal(t, len(data), len(result.Hits))
+ assert.Len(t, result.Hits, len(data))
assert.Equal(t, len(data), int(result.Total))
},
},
@@ -379,7 +378,7 @@ var cases = []*testIndexerCase{
Paginator: &db.ListOptions{
PageSize: 5,
},
- PosterID: optional.Some(int64(1)),
+ PosterID: "1",
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
assert.Len(t, result.Hits, 5)
@@ -397,7 +396,7 @@ var cases = []*testIndexerCase{
Paginator: &db.ListOptions{
PageSize: 5,
},
- AssigneeID: optional.Some(int64(1)),
+ AssigneeID: "1",
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
assert.Len(t, result.Hits, 5)
@@ -415,7 +414,7 @@ var cases = []*testIndexerCase{
Paginator: &db.ListOptions{
PageSize: 5,
},
- AssigneeID: optional.Some(int64(0)),
+ AssigneeID: "(none)",
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
assert.Len(t, result.Hits, 5)
@@ -526,7 +525,7 @@ var cases = []*testIndexerCase{
SortBy: internal.SortByCreatedDesc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
- assert.Equal(t, len(data), len(result.Hits))
+ assert.Len(t, result.Hits, len(data))
assert.Equal(t, len(data), int(result.Total))
for i, v := range result.Hits {
if i < len(result.Hits)-1 {
@@ -542,7 +541,7 @@ var cases = []*testIndexerCase{
SortBy: internal.SortByUpdatedDesc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
- assert.Equal(t, len(data), len(result.Hits))
+ assert.Len(t, result.Hits, len(data))
assert.Equal(t, len(data), int(result.Total))
for i, v := range result.Hits {
if i < len(result.Hits)-1 {
@@ -558,7 +557,7 @@ var cases = []*testIndexerCase{
SortBy: internal.SortByCommentsDesc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
- assert.Equal(t, len(data), len(result.Hits))
+ assert.Len(t, result.Hits, len(data))
assert.Equal(t, len(data), int(result.Total))
for i, v := range result.Hits {
if i < len(result.Hits)-1 {
@@ -574,7 +573,7 @@ var cases = []*testIndexerCase{
SortBy: internal.SortByDeadlineDesc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
- assert.Equal(t, len(data), len(result.Hits))
+ assert.Len(t, result.Hits, len(data))
assert.Equal(t, len(data), int(result.Total))
for i, v := range result.Hits {
if i < len(result.Hits)-1 {
@@ -590,7 +589,7 @@ var cases = []*testIndexerCase{
SortBy: internal.SortByCreatedAsc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
- assert.Equal(t, len(data), len(result.Hits))
+ assert.Len(t, result.Hits, len(data))
assert.Equal(t, len(data), int(result.Total))
for i, v := range result.Hits {
if i < len(result.Hits)-1 {
@@ -606,7 +605,7 @@ var cases = []*testIndexerCase{
SortBy: internal.SortByUpdatedAsc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
- assert.Equal(t, len(data), len(result.Hits))
+ assert.Len(t, result.Hits, len(data))
assert.Equal(t, len(data), int(result.Total))
for i, v := range result.Hits {
if i < len(result.Hits)-1 {
@@ -622,7 +621,7 @@ var cases = []*testIndexerCase{
SortBy: internal.SortByCommentsAsc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
- assert.Equal(t, len(data), len(result.Hits))
+ assert.Len(t, result.Hits, len(data))
assert.Equal(t, len(data), int(result.Total))
for i, v := range result.Hits {
if i < len(result.Hits)-1 {
@@ -638,7 +637,7 @@ var cases = []*testIndexerCase{
SortBy: internal.SortByDeadlineAsc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
- assert.Equal(t, len(data), len(result.Hits))
+ assert.Len(t, result.Hits, len(data))
assert.Equal(t, len(data), int(result.Total))
for i, v := range result.Hits {
if i < len(result.Hits)-1 {
@@ -647,6 +646,21 @@ var cases = []*testIndexerCase{
}
},
},
+ {
+ Name: "SearchAnyAssignee",
+ SearchOptions: &internal.SearchOptions{
+ AssigneeID: "(any)",
+ },
+ Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
+ assert.Len(t, result.Hits, 180)
+ for _, v := range result.Hits {
+ assert.GreaterOrEqual(t, data[v.ID].AssigneeID, int64(1))
+ }
+ assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool {
+ return v.AssigneeID >= 1
+ }), result.Total)
+ },
+ },
}
type testIndexerCase struct {
@@ -736,22 +750,10 @@ func countIndexerData(data map[int64]*internal.IndexerData, f func(v *internal.I
// waitData waits for the indexer to index all data.
// Some engines like Elasticsearch index data asynchronously, so we need to wait for a while.
-func waitData(indexer internal.Indexer, total int64) error {
- var actual int64
- for i := 0; i < 100; i++ {
- result, err := indexer.Search(context.Background(), &internal.SearchOptions{
- Paginator: &db.ListOptions{
- PageSize: 0,
- },
- })
- if err != nil {
- return err
- }
- actual = result.Total
- if actual == total {
- return nil
- }
- time.Sleep(100 * time.Millisecond)
- }
- return fmt.Errorf("waitData: expected %d, actual %d", total, actual)
+func waitData(t *testing.T, indexer internal.Indexer, total int64) {
+ assert.Eventually(t, func() bool {
+ result, err := indexer.Search(t.Context(), &internal.SearchOptions{Paginator: &db.ListOptions{}})
+ require.NoError(t, err)
+ return result.Total == total
+ }, 10*time.Second, 100*time.Millisecond, "expected total=%d", total)
}
diff --git a/modules/indexer/issues/meilisearch/meilisearch.go b/modules/indexer/issues/meilisearch/meilisearch.go
index 1066e96272..759a98473f 100644
--- a/modules/indexer/issues/meilisearch/meilisearch.go
+++ b/modules/indexer/issues/meilisearch/meilisearch.go
@@ -10,6 +10,7 @@ import (
"strconv"
"strings"
+ "code.gitea.io/gitea/modules/indexer"
indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
inner_meilisearch "code.gitea.io/gitea/modules/indexer/internal/meilisearch"
"code.gitea.io/gitea/modules/indexer/issues/internal"
@@ -35,6 +36,10 @@ type Indexer struct {
indexer_internal.Indexer // do not composite inner_meilisearch.Indexer directly to avoid exposing too much
}
+func (b *Indexer) SupportedSearchModes() []indexer.SearchMode {
+ return indexer.SearchModesExactWords()
+}
+
// NewIndexer creates a new meilisearch indexer
func NewIndexer(url, apiKey, indexerName string) *Indexer {
settings := &meilisearch.Settings{
@@ -182,12 +187,20 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
query.And(inner_meilisearch.NewFilterEq("project_board_id", options.ProjectColumnID.Value()))
}
- if options.PosterID.Has() {
- query.And(inner_meilisearch.NewFilterEq("poster_id", options.PosterID.Value()))
+ if options.PosterID != "" {
+ // "(none)" becomes 0, it means no poster
+ posterIDInt64, _ := strconv.ParseInt(options.PosterID, 10, 64)
+ query.And(inner_meilisearch.NewFilterEq("poster_id", posterIDInt64))
}
- if options.AssigneeID.Has() {
- query.And(inner_meilisearch.NewFilterEq("assignee_id", options.AssigneeID.Value()))
+ if options.AssigneeID != "" {
+ if options.AssigneeID == "(any)" {
+ query.And(inner_meilisearch.NewFilterGte("assignee_id", 1))
+ } else {
+ // "(none)" becomes 0, it means no assignee
+ assigneeIDInt64, _ := strconv.ParseInt(options.AssigneeID, 10, 64)
+ query.And(inner_meilisearch.NewFilterEq("assignee_id", assigneeIDInt64))
+ }
}
if options.MentionID.Has() {
@@ -230,9 +243,8 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
limit = 1
}
- keyword := options.Keyword
- if !options.IsFuzzyKeyword {
- // to make it non fuzzy ("typo tolerance" in meilisearch terms), we have to quote the keyword(s)
+ keyword := options.Keyword // default to match "words"
+ if options.SearchMode == indexer.SearchModeExact {
// https://www.meilisearch.com/docs/reference/api/search#phrase-search
keyword = doubleQuoteKeyword(keyword)
}
diff --git a/modules/indexer/issues/meilisearch/meilisearch_test.go b/modules/indexer/issues/meilisearch/meilisearch_test.go
index 4666df136a..2fea4004cb 100644
--- a/modules/indexer/issues/meilisearch/meilisearch_test.go
+++ b/modules/indexer/issues/meilisearch/meilisearch_test.go
@@ -15,6 +15,7 @@ import (
"github.com/meilisearch/meilisearch-go"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestMeilisearchIndexer(t *testing.T) {
@@ -32,20 +33,10 @@ func TestMeilisearchIndexer(t *testing.T) {
key = os.Getenv("TEST_MEILISEARCH_KEY")
}
- ok := false
- for i := 0; i < 60; i++ {
+ require.Eventually(t, func() bool {
resp, err := http.Get(url)
- if err == nil && resp.StatusCode == http.StatusOK {
- ok = true
- break
- }
- t.Logf("Waiting for meilisearch to be up: %v", err)
- time.Sleep(time.Second)
- }
- if !ok {
- t.Fatalf("Failed to wait for meilisearch to be up")
- return
- }
+ return err == nil && resp.StatusCode == http.StatusOK
+ }, time.Minute, time.Second, "Expected meilisearch to be up")
indexer := NewIndexer(url, key, fmt.Sprintf("test_meilisearch_indexer_%d", time.Now().Unix()))
defer indexer.Close()
@@ -83,13 +74,13 @@ func TestConvertHits(t *testing.T) {
}
hits, err := convertHits(validResponse)
assert.NoError(t, err)
- assert.EqualValues(t, []internal.Match{{ID: 11}, {ID: 22}, {ID: 33}}, hits)
+ assert.Equal(t, []internal.Match{{ID: 11}, {ID: 22}, {ID: 33}}, hits)
}
func TestDoubleQuoteKeyword(t *testing.T) {
- assert.EqualValues(t, "", doubleQuoteKeyword(""))
- assert.EqualValues(t, `"a" "b" "c"`, doubleQuoteKeyword("a b c"))
- assert.EqualValues(t, `"a" "d" "g"`, doubleQuoteKeyword("a d g"))
- assert.EqualValues(t, `"a" "d" "g"`, doubleQuoteKeyword("a d g"))
- assert.EqualValues(t, `"a" "d" "g"`, doubleQuoteKeyword(`a "" "d" """g`))
+ assert.Empty(t, doubleQuoteKeyword(""))
+ assert.Equal(t, `"a" "b" "c"`, doubleQuoteKeyword("a b c"))
+ assert.Equal(t, `"a" "d" "g"`, doubleQuoteKeyword("a d g"))
+ assert.Equal(t, `"a" "d" "g"`, doubleQuoteKeyword("a d g"))
+ assert.Equal(t, `"a" "d" "g"`, doubleQuoteKeyword(`a "" "d" """g`))
}
diff --git a/modules/indexer/issues/util.go b/modules/indexer/issues/util.go
index deb19adc49..19d835a1d8 100644
--- a/modules/indexer/issues/util.go
+++ b/modules/indexer/issues/util.go
@@ -92,6 +92,11 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD
projectID = issue.Project.ID
}
+ projectColumnID, err := issue.ProjectColumnID(ctx)
+ if err != nil {
+ return nil, false, err
+ }
+
return &internal.IndexerData{
ID: issue.ID,
RepoID: issue.RepoID,
@@ -106,7 +111,7 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD
NoLabel: len(labels) == 0,
MilestoneID: issue.MilestoneID,
ProjectID: projectID,
- ProjectColumnID: issue.ProjectColumnID(ctx),
+ ProjectColumnID: projectColumnID,
PosterID: issue.PosterID,
AssigneeID: issue.AssigneeID,
MentionIDs: mentionIDs,
diff --git a/modules/indexer/stats/db.go b/modules/indexer/stats/db.go
index 98a977c700..199d493e97 100644
--- a/modules/indexer/stats/db.go
+++ b/modules/indexer/stats/db.go
@@ -8,6 +8,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/git/languagestats"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
@@ -49,10 +50,10 @@ func (db *DBIndexer) Index(id int64) error {
commitID, err := gitRepo.GetBranchCommitID(repo.DefaultBranch)
if err != nil {
if git.IsErrBranchNotExist(err) || git.IsErrNotExist(err) || setting.IsInTesting {
- log.Debug("Unable to get commit ID for default branch %s in %s ... skipping this repository", repo.DefaultBranch, repo.RepoPath())
+ log.Debug("Unable to get commit ID for default branch %s in %s ... skipping this repository", repo.DefaultBranch, repo.FullName())
return nil
}
- log.Error("Unable to get commit ID for default branch %s in %s. Error: %v", repo.DefaultBranch, repo.RepoPath(), err)
+ log.Error("Unable to get commit ID for default branch %s in %s. Error: %v", repo.DefaultBranch, repo.FullName(), err)
return err
}
@@ -62,20 +63,20 @@ func (db *DBIndexer) Index(id int64) error {
}
// Calculate and save language statistics to database
- stats, err := gitRepo.GetLanguageStats(commitID)
+ stats, err := languagestats.GetLanguageStats(gitRepo, commitID)
if err != nil {
if !setting.IsInTesting {
- log.Error("Unable to get language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, repo.RepoPath(), err)
+ log.Error("Unable to get language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, repo.FullName(), err)
}
return err
}
err = repo_model.UpdateLanguageStats(ctx, repo, commitID, stats)
if err != nil {
- log.Error("Unable to update language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, repo.RepoPath(), err)
+ log.Error("Unable to update language stats for ID %s for default branch %s in %s. Error: %v", commitID, repo.DefaultBranch, repo.FullName(), err)
return err
}
- log.Debug("DBIndexer completed language stats for ID %s for default branch %s in %s. stats count: %d", commitID, repo.DefaultBranch, repo.RepoPath(), len(stats))
+ log.Debug("DBIndexer completed language stats for ID %s for default branch %s in %s. stats count: %d", commitID, repo.DefaultBranch, repo.FullName(), len(stats))
return nil
}
diff --git a/modules/indexer/stats/indexer_test.go b/modules/indexer/stats/indexer_test.go
index 5be45d7a3b..d32a8bf151 100644
--- a/modules/indexer/stats/indexer_test.go
+++ b/modules/indexer/stats/indexer_test.go
@@ -4,7 +4,6 @@
package stats
import (
- "context"
"testing"
"time"
@@ -40,7 +39,7 @@ func TestRepoStatsIndex(t *testing.T) {
err = UpdateRepoIndexer(repo)
assert.NoError(t, err)
- assert.NoError(t, queue.GetManager().FlushAll(context.Background(), 5*time.Second))
+ assert.NoError(t, queue.GetManager().FlushAll(t.Context(), 5*time.Second))
status, err := repo_model.GetIndexerStatus(db.DefaultContext, repo, repo_model.RepoIndexerTypeStats)
assert.NoError(t, err)
diff --git a/modules/indexer/stats/queue.go b/modules/indexer/stats/queue.go
index d002bd57cf..69cde321d8 100644
--- a/modules/indexer/stats/queue.go
+++ b/modules/indexer/stats/queue.go
@@ -4,7 +4,7 @@
package stats
import (
- "fmt"
+ "errors"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/graceful"
@@ -31,7 +31,7 @@ func handler(items ...int64) []int64 {
func initStatsQueue() error {
statsQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "repo_stats_update", handler)
if statsQueue == nil {
- return fmt.Errorf("unable to create repo_stats_update queue")
+ return errors.New("unable to create repo_stats_update queue")
}
go graceful.GetManager().RunWithCancel(statsQueue)
return nil
diff --git a/modules/issue/template/template.go b/modules/issue/template/template.go
index a2b9d6b33e..192aaf8e01 100644
--- a/modules/issue/template/template.go
+++ b/modules/issue/template/template.go
@@ -4,9 +4,11 @@
package template
import (
+ "errors"
"fmt"
"net/url"
"regexp"
+ "slices"
"strconv"
"strings"
@@ -31,17 +33,17 @@ func Validate(template *api.IssueTemplate) error {
func validateMetadata(template *api.IssueTemplate) error {
if strings.TrimSpace(template.Name) == "" {
- return fmt.Errorf("'name' is required")
+ return errors.New("'name' is required")
}
if strings.TrimSpace(template.About) == "" {
- return fmt.Errorf("'about' is required")
+ return errors.New("'about' is required")
}
return nil
}
func validateYaml(template *api.IssueTemplate) error {
if len(template.Fields) == 0 {
- return fmt.Errorf("'body' is required")
+ return errors.New("'body' is required")
}
ids := make(container.Set[string])
for idx, field := range template.Fields {
@@ -401,7 +403,7 @@ func (f *valuedField) Render() string {
}
func (f *valuedField) Value() string {
- return strings.TrimSpace(f.Get(fmt.Sprintf("form-field-%s", f.ID)))
+ return strings.TrimSpace(f.Get("form-field-" + f.ID))
}
func (f *valuedField) Options() []*valuedOption {
@@ -444,14 +446,9 @@ func (o *valuedOption) Label() string {
func (o *valuedOption) IsChecked() bool {
switch o.field.Type {
case api.IssueFormFieldTypeDropdown:
- checks := strings.Split(o.field.Get(fmt.Sprintf("form-field-%s", o.field.ID)), ",")
+ checks := strings.Split(o.field.Get("form-field-"+o.field.ID), ",")
idx := strconv.Itoa(o.index)
- for _, v := range checks {
- if v == idx {
- return true
- }
- }
- return false
+ return slices.Contains(checks, idx)
case api.IssueFormFieldTypeCheckboxes:
return o.field.Get(fmt.Sprintf("form-field-%s-%d", o.field.ID, o.index)) == "on"
}
diff --git a/modules/issue/template/template_test.go b/modules/issue/template/template_test.go
index 689a285b47..7fec9431b6 100644
--- a/modules/issue/template/template_test.go
+++ b/modules/issue/template/template_test.go
@@ -904,7 +904,7 @@ Option 1 of dropdown, Option 2 of dropdown
t.Fatal(err)
}
if got := RenderToMarkdown(template, tt.args.values); got != tt.want {
- assert.EqualValues(t, tt.want, got)
+ assert.Equal(t, tt.want, got)
}
})
}
@@ -957,9 +957,8 @@ func Test_minQuotes(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- if got := minQuotes(tt.args.value); got != tt.want {
- t.Errorf("minQuotes() = %v, want %v", got, tt.want)
- }
+ got := minQuotes(tt.args.value)
+ assert.Equal(t, tt.want, got)
})
}
}
diff --git a/modules/json/json.go b/modules/json/json.go
index 34568c75c6..444dc8526a 100644
--- a/modules/json/json.go
+++ b/modules/json/json.go
@@ -3,11 +3,10 @@
package json
-// Allow "encoding/json" import.
import (
"bytes"
"encoding/binary"
- "encoding/json" //nolint:depguard
+ "encoding/json" //nolint:depguard // this package wraps it
"io"
jsoniter "github.com/json-iterator/go"
@@ -145,6 +144,12 @@ func Valid(data []byte) bool {
// UnmarshalHandleDoubleEncode - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's
// possible that a Blob may be double encoded or gain an unwanted prefix of 0xff 0xfe.
func UnmarshalHandleDoubleEncode(bs []byte, v any) error {
+ if len(bs) == 0 {
+ // json.Unmarshal will report errors if input is empty (nil or zero-length)
+ // It seems that XORM ignores the nil but still passes zero-length string into this function
+ // To be consistent, we should treat all empty inputs as success
+ return nil
+ }
err := json.Unmarshal(bs, v)
if err != nil {
ok := true
diff --git a/modules/json/json_test.go b/modules/json/json_test.go
new file mode 100644
index 0000000000..ace7167913
--- /dev/null
+++ b/modules/json/json_test.go
@@ -0,0 +1,18 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package json
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGiteaDBJSONUnmarshal(t *testing.T) {
+ var m map[any]any
+ err := UnmarshalHandleDoubleEncode(nil, &m)
+ assert.NoError(t, err)
+ err = UnmarshalHandleDoubleEncode([]byte(""), &m)
+ assert.NoError(t, err)
+}
diff --git a/modules/label/label.go b/modules/label/label.go
index d3ef0e1dc9..3e68c4d26e 100644
--- a/modules/label/label.go
+++ b/modules/label/label.go
@@ -7,19 +7,24 @@ import (
"fmt"
"regexp"
"strings"
-)
+ "sync"
-// colorPattern is a regexp which can validate label color
-var colorPattern = regexp.MustCompile("^#?(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})$")
+ "code.gitea.io/gitea/modules/util"
+)
// Label represents label information loaded from template
type Label struct {
- Name string `yaml:"name"`
- Color string `yaml:"color"`
- Description string `yaml:"description,omitempty"`
- Exclusive bool `yaml:"exclusive,omitempty"`
+ Name string `yaml:"name"`
+ Color string `yaml:"color"`
+ Description string `yaml:"description,omitempty"`
+ Exclusive bool `yaml:"exclusive,omitempty"`
+ ExclusiveOrder int `yaml:"exclusive_order,omitempty"`
}
+var colorPattern = sync.OnceValue(func() *regexp.Regexp {
+ return regexp.MustCompile(`^#([\da-fA-F]{3}|[\da-fA-F]{6})$`)
+})
+
// NormalizeColor normalizes a color string to a 6-character hex code
func NormalizeColor(color string) (string, error) {
// normalize case
@@ -30,8 +35,8 @@ func NormalizeColor(color string) (string, error) {
color = "#" + color
}
- if !colorPattern.MatchString(color) {
- return "", fmt.Errorf("bad color code: %s", color)
+ if !colorPattern().MatchString(color) {
+ return "", util.NewInvalidArgumentErrorf("invalid color: %s", color)
}
// convert 3-character shorthand into 6-character version
diff --git a/modules/label/parser.go b/modules/label/parser.go
index 511bac823f..2a10152062 100644
--- a/modules/label/parser.go
+++ b/modules/label/parser.go
@@ -72,7 +72,7 @@ func parseYamlFormat(fileName string, data []byte) ([]*Label, error) {
func parseLegacyFormat(fileName string, data []byte) ([]*Label, error) {
lines := strings.Split(string(data), "\n")
list := make([]*Label, 0, len(lines))
- for i := 0; i < len(lines); i++ {
+ for i := range lines {
line := strings.TrimSpace(lines[i])
if len(line) == 0 {
continue
@@ -108,7 +108,7 @@ func LoadTemplateDescription(fileName string) (string, error) {
return "", err
}
- for i := 0; i < len(list); i++ {
+ for i := range list {
if i > 0 {
buf.WriteString(", ")
}
diff --git a/modules/lfs/http_client.go b/modules/lfs/http_client.go
index 3acd23b8f7..4b51193846 100644
--- a/modules/lfs/http_client.go
+++ b/modules/lfs/http_client.go
@@ -70,12 +70,16 @@ func (c *HTTPClient) transferNames() []string {
func (c *HTTPClient) batch(ctx context.Context, operation string, objects []Pointer) (*BatchResponse, error) {
log.Trace("BATCH operation with objects: %v", objects)
- url := fmt.Sprintf("%s/objects/batch", c.endpoint)
+ url := c.endpoint + "/objects/batch"
+ // Original: In some lfs server implementations, they require the ref attribute. #32838
// `ref` is an "optional object describing the server ref that the objects belong to"
- // but some (incorrect) lfs servers require it, so maybe adding an empty ref here doesn't break the correct ones.
+ // but some (incorrect) lfs servers like aliyun require it, so maybe adding an empty ref here doesn't break the correct ones.
// https://github.com/git-lfs/git-lfs/blob/a32a02b44bf8a511aa14f047627c49e1a7fd5021/docs/api/batch.md?plain=1#L37
- request := &BatchRequest{operation, c.transferNames(), &Reference{}, objects}
+ //
+ // UPDATE: it can't use "empty ref" here because it breaks others like https://github.com/go-gitea/gitea/issues/33453
+ request := &BatchRequest{operation, c.transferNames(), nil, objects}
+
payload := new(bytes.Buffer)
err := json.NewEncoder(payload).Encode(request)
if err != nil {
diff --git a/modules/lfs/http_client_test.go b/modules/lfs/http_client_test.go
index aa7e3c45c4..179bcdb29a 100644
--- a/modules/lfs/http_client_test.go
+++ b/modules/lfs/http_client_test.go
@@ -31,7 +31,7 @@ func (a *DummyTransferAdapter) Name() string {
}
func (a *DummyTransferAdapter) Download(ctx context.Context, l *Link) (io.ReadCloser, error) {
- return io.NopCloser(bytes.NewBufferString("dummy")), nil
+ return io.NopCloser(strings.NewReader("dummy")), nil
}
func (a *DummyTransferAdapter) Upload(ctx context.Context, l *Link, p Pointer, r io.Reader) error {
@@ -49,7 +49,7 @@ func lfsTestRoundtripHandler(req *http.Request) *http.Response {
if strings.Contains(url, "status-not-ok") {
return &http.Response{StatusCode: http.StatusBadRequest}
} else if strings.Contains(url, "invalid-json-response") {
- return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString("invalid json"))}
+ return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader("invalid json"))}
} else if strings.Contains(url, "valid-batch-request-download") {
batchResponse = &BatchResponse{
Transfer: "dummy",
@@ -248,7 +248,7 @@ func TestHTTPClientDownload(t *testing.T) {
},
}
- err := client.Download(context.Background(), []Pointer{p}, func(p Pointer, content io.ReadCloser, objectError error) error {
+ err := client.Download(t.Context(), []Pointer{p}, func(p Pointer, content io.ReadCloser, objectError error) error {
if objectError != nil {
return objectError
}
@@ -348,7 +348,7 @@ func TestHTTPClientUpload(t *testing.T) {
},
}
- err := client.Upload(context.Background(), []Pointer{p}, func(p Pointer, objectError error) (io.ReadCloser, error) {
+ err := client.Upload(t.Context(), []Pointer{p}, func(p Pointer, objectError error) (io.ReadCloser, error) {
return io.NopCloser(new(bytes.Buffer)), objectError
})
if c.expectedError != "" {
diff --git a/modules/lfs/pointer.go b/modules/lfs/pointer.go
index ebde20f826..9c95613057 100644
--- a/modules/lfs/pointer.go
+++ b/modules/lfs/pointer.go
@@ -15,15 +15,13 @@ import (
"strings"
)
+// spec: https://github.com/git-lfs/git-lfs/blob/master/docs/spec.md
const (
- blobSizeCutoff = 1024
+ MetaFileMaxSize = 1024 // spec says the maximum size of a pointer file must be smaller than 1024
- // MetaFileIdentifier is the string appearing at the first line of LFS pointer files.
- // https://github.com/git-lfs/git-lfs/blob/master/docs/spec.md
- MetaFileIdentifier = "version https://git-lfs.github.com/spec/v1"
+ MetaFileIdentifier = "version https://git-lfs.github.com/spec/v1" // the first line of a pointer file
- // MetaFileOidPrefix appears in LFS pointer files on a line before the sha256 hash.
- MetaFileOidPrefix = "oid sha256:"
+ MetaFileOidPrefix = "oid sha256:" // spec says the only supported hash is sha256 at the moment
)
var (
@@ -39,7 +37,7 @@ var (
// ReadPointer tries to read LFS pointer data from the reader
func ReadPointer(reader io.Reader) (Pointer, error) {
- buf := make([]byte, blobSizeCutoff)
+ buf := make([]byte, MetaFileMaxSize)
n, err := io.ReadFull(reader, buf)
if err != nil && err != io.ErrUnexpectedEOF {
return Pointer{}, err
@@ -65,6 +63,7 @@ func ReadPointerFromBuffer(buf []byte) (Pointer, error) {
return p, ErrInvalidStructure
}
+ // spec says "key/value pairs MUST be sorted alphabetically in ascending order (version is exception and must be the first)"
oid := strings.TrimPrefix(splitLines[1], MetaFileOidPrefix)
if len(oid) != 64 || !oidPattern.MatchString(oid) {
return p, ErrInvalidOIDFormat
diff --git a/modules/lfs/pointer_scanner_gogit.go b/modules/lfs/pointer_scanner_gogit.go
index f4302c23bc..e153b8e24e 100644
--- a/modules/lfs/pointer_scanner_gogit.go
+++ b/modules/lfs/pointer_scanner_gogit.go
@@ -31,7 +31,7 @@ func SearchPointerBlobs(ctx context.Context, repo *git.Repository, pointerChan c
default:
}
- if blob.Size > blobSizeCutoff {
+ if blob.Size > MetaFileMaxSize {
return nil
}
diff --git a/modules/lfs/transferadapter_test.go b/modules/lfs/transferadapter_test.go
index a430b71a5f..ef72d76db4 100644
--- a/modules/lfs/transferadapter_test.go
+++ b/modules/lfs/transferadapter_test.go
@@ -5,7 +5,6 @@ package lfs
import (
"bytes"
- "context"
"io"
"net/http"
"strings"
@@ -33,7 +32,7 @@ func TestBasicTransferAdapter(t *testing.T) {
if strings.Contains(url, "download-request") {
assert.Equal(t, "GET", req.Method)
- return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString("dummy"))}
+ return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader("dummy"))}
} else if strings.Contains(url, "upload-request") {
assert.Equal(t, "PUT", req.Method)
assert.Equal(t, "application/octet-stream", req.Header.Get("Content-Type"))
@@ -94,7 +93,7 @@ func TestBasicTransferAdapter(t *testing.T) {
}
for n, c := range cases {
- _, err := a.Download(context.Background(), c.link)
+ _, err := a.Download(t.Context(), c.link)
if len(c.expectederror) > 0 {
assert.Contains(t, err.Error(), c.expectederror, "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror)
} else {
@@ -127,7 +126,7 @@ func TestBasicTransferAdapter(t *testing.T) {
}
for n, c := range cases {
- err := a.Upload(context.Background(), c.link, p, bytes.NewBufferString("dummy"))
+ err := a.Upload(t.Context(), c.link, p, strings.NewReader("dummy"))
if len(c.expectederror) > 0 {
assert.Contains(t, err.Error(), c.expectederror, "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror)
} else {
@@ -160,7 +159,7 @@ func TestBasicTransferAdapter(t *testing.T) {
}
for n, c := range cases {
- err := a.Verify(context.Background(), c.link, p)
+ err := a.Verify(t.Context(), c.link, p)
if len(c.expectederror) > 0 {
assert.Contains(t, err.Error(), c.expectederror, "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror)
} else {
diff --git a/modules/lfstransfer/backend/backend.go b/modules/lfstransfer/backend/backend.go
index 2b1fe49fda..dd4108ea56 100644
--- a/modules/lfstransfer/backend/backend.go
+++ b/modules/lfstransfer/backend/backend.go
@@ -4,7 +4,6 @@
package backend
import (
- "bytes"
"context"
"encoding/base64"
"fmt"
@@ -29,7 +28,7 @@ var Capabilities = []string{
"locking",
}
-var _ transfer.Backend = &GiteaBackend{}
+var _ transfer.Backend = (*GiteaBackend)(nil)
// GiteaBackend is an adapter between git-lfs-transfer library and Gitea's internal LFS API
type GiteaBackend struct {
@@ -48,7 +47,7 @@ func New(ctx context.Context, repo, op, token string, logger transfer.Logger) (t
return nil, err
}
server = server.JoinPath("api/internal/repo", repo, "info/lfs")
- return &GiteaBackend{ctx: ctx, server: server, op: op, authToken: token, internalAuth: fmt.Sprintf("Bearer %s", setting.InternalToken), logger: logger}, nil
+ return &GiteaBackend{ctx: ctx, server: server, op: op, authToken: token, internalAuth: "Bearer " + setting.InternalToken, logger: logger}, nil
}
// Batch implements transfer.Backend
@@ -71,24 +70,23 @@ func (g *GiteaBackend) Batch(_ string, pointers []transfer.BatchItem, args trans
g.logger.Log("json marshal error", err)
return nil, err
}
- url := g.server.JoinPath("objects/batch").String()
headers := map[string]string{
headerAuthorization: g.authToken,
headerGiteaInternalAuth: g.internalAuth,
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}
- req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
+ req := newInternalRequestLFS(g.ctx, g.server.JoinPath("objects/batch").String(), http.MethodPost, headers, bodyBytes)
resp, err := req.Response()
if err != nil {
g.logger.Log("http request error", err)
return nil, err
}
+ defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
g.logger.Log("http statuscode error", resp.StatusCode, statusCodeToErr(resp.StatusCode))
return nil, statusCodeToErr(resp.StatusCode)
}
- defer resp.Body.Close()
respBytes, err := io.ReadAll(resp.Body)
if err != nil {
g.logger.Log("http read error", err)
@@ -158,8 +156,7 @@ func (g *GiteaBackend) Batch(_ string, pointers []transfer.BatchItem, args trans
return pointers, nil
}
-// Download implements transfer.Backend. The returned reader must be closed by the
-// caller.
+// Download implements transfer.Backend. The returned reader must be closed by the caller.
func (g *GiteaBackend) Download(oid string, args transfer.Args) (io.ReadCloser, int64, error) {
idMapStr, exists := args[argID]
if !exists {
@@ -181,31 +178,30 @@ func (g *GiteaBackend) Download(oid string, args transfer.Args) (io.ReadCloser,
g.logger.Log("argument id incorrect")
return nil, 0, transfer.ErrCorruptData
}
- url := action.Href
headers := map[string]string{
headerAuthorization: g.authToken,
headerGiteaInternalAuth: g.internalAuth,
headerAccept: mimeOctetStream,
}
- req := newInternalRequest(g.ctx, url, http.MethodGet, headers, nil)
+ req := newInternalRequestLFS(g.ctx, toInternalLFSURL(action.Href), http.MethodGet, headers, nil)
resp, err := req.Response()
if err != nil {
- return nil, 0, err
+ return nil, 0, fmt.Errorf("failed to get response: %w", err)
}
+ // no need to close the body here by "defer resp.Body.Close()", see below
if resp.StatusCode != http.StatusOK {
return nil, 0, statusCodeToErr(resp.StatusCode)
}
- defer resp.Body.Close()
- respBytes, err := io.ReadAll(resp.Body)
+
+ respSize, err := strconv.ParseInt(resp.Header.Get("X-Gitea-LFS-Content-Length"), 10, 64)
if err != nil {
- return nil, 0, err
+ return nil, 0, fmt.Errorf("failed to parse content length: %w", err)
}
- respSize := int64(len(respBytes))
- respBuf := io.NopCloser(bytes.NewBuffer(respBytes))
- return respBuf, respSize, nil
+ // transfer.Backend will check io.Closer interface and close this Body reader
+ return resp.Body, respSize, nil
}
-// StartUpload implements transfer.Backend.
+// Upload implements transfer.Backend.
func (g *GiteaBackend) Upload(oid string, size int64, r io.Reader, args transfer.Args) error {
idMapStr, exists := args[argID]
if !exists {
@@ -227,22 +223,20 @@ func (g *GiteaBackend) Upload(oid string, size int64, r io.Reader, args transfer
g.logger.Log("argument id incorrect")
return transfer.ErrCorruptData
}
- url := action.Href
headers := map[string]string{
headerAuthorization: g.authToken,
headerGiteaInternalAuth: g.internalAuth,
headerContentType: mimeOctetStream,
headerContentLength: strconv.FormatInt(size, 10),
}
- reqBytes, err := io.ReadAll(r)
- if err != nil {
- return err
- }
- req := newInternalRequest(g.ctx, url, http.MethodPut, headers, reqBytes)
+
+ req := newInternalRequestLFS(g.ctx, toInternalLFSURL(action.Href), http.MethodPut, headers, nil)
+ req.Body(r)
resp, err := req.Response()
if err != nil {
return err
}
+ defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return statusCodeToErr(resp.StatusCode)
}
@@ -277,18 +271,18 @@ func (g *GiteaBackend) Verify(oid string, size int64, args transfer.Args) (trans
// the server sent no verify action
return transfer.SuccessStatus(), nil
}
- url := action.Href
headers := map[string]string{
headerAuthorization: g.authToken,
headerGiteaInternalAuth: g.internalAuth,
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}
- req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
+ req := newInternalRequestLFS(g.ctx, toInternalLFSURL(action.Href), http.MethodPost, headers, bodyBytes)
resp, err := req.Response()
if err != nil {
return transfer.NewStatus(transfer.StatusInternalServerError), err
}
+ defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return transfer.NewStatus(uint32(resp.StatusCode), http.StatusText(resp.StatusCode)), statusCodeToErr(resp.StatusCode)
}
diff --git a/modules/lfstransfer/backend/lock.go b/modules/lfstransfer/backend/lock.go
index f094cce1db..2c3c16a9bb 100644
--- a/modules/lfstransfer/backend/lock.go
+++ b/modules/lfstransfer/backend/lock.go
@@ -5,6 +5,7 @@ package backend
import (
"context"
+ "errors"
"fmt"
"io"
"net/http"
@@ -43,14 +44,13 @@ func (g *giteaLockBackend) Create(path, refname string) (transfer.Lock, error) {
g.logger.Log("json marshal error", err)
return nil, err
}
- url := g.server.String()
headers := map[string]string{
headerAuthorization: g.authToken,
headerGiteaInternalAuth: g.internalAuth,
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}
- req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
+ req := newInternalRequestLFS(g.ctx, g.server.String(), http.MethodPost, headers, bodyBytes)
resp, err := req.Response()
if err != nil {
g.logger.Log("http request error", err)
@@ -75,7 +75,7 @@ func (g *giteaLockBackend) Create(path, refname string) (transfer.Lock, error) {
if respBody.Lock == nil {
g.logger.Log("api returned nil lock")
- return nil, fmt.Errorf("api returned nil lock")
+ return nil, errors.New("api returned nil lock")
}
respLock := respBody.Lock
owner := userUnknown
@@ -95,14 +95,13 @@ func (g *giteaLockBackend) Unlock(lock transfer.Lock) error {
g.logger.Log("json marshal error", err)
return err
}
- url := g.server.JoinPath(lock.ID(), "unlock").String()
headers := map[string]string{
headerAuthorization: g.authToken,
headerGiteaInternalAuth: g.internalAuth,
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}
- req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
+ req := newInternalRequestLFS(g.ctx, g.server.JoinPath(lock.ID(), "unlock").String(), http.MethodPost, headers, bodyBytes)
resp, err := req.Response()
if err != nil {
g.logger.Log("http request error", err)
@@ -176,16 +175,15 @@ func (g *giteaLockBackend) Range(cursor string, limit int, iter func(transfer.Lo
}
func (g *giteaLockBackend) queryLocks(v url.Values) ([]transfer.Lock, string, error) {
- urlq := g.server.JoinPath() // get a copy
- urlq.RawQuery = v.Encode()
- url := urlq.String()
+ serverURLWithQuery := g.server.JoinPath() // get a copy
+ serverURLWithQuery.RawQuery = v.Encode()
headers := map[string]string{
headerAuthorization: g.authToken,
headerGiteaInternalAuth: g.internalAuth,
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}
- req := newInternalRequest(g.ctx, url, http.MethodGet, headers, nil)
+ req := newInternalRequestLFS(g.ctx, serverURLWithQuery.String(), http.MethodGet, headers, nil)
resp, err := req.Response()
if err != nil {
g.logger.Log("http request error", err)
@@ -266,7 +264,7 @@ func (g *giteaLock) CurrentUser() (string, error) {
// AsLockSpec implements transfer.Lock
func (g *giteaLock) AsLockSpec(ownerID bool) ([]string, error) {
msgs := []string{
- fmt.Sprintf("lock %s", g.ID()),
+ "lock " + g.ID(),
fmt.Sprintf("path %s %s", g.ID(), g.Path()),
fmt.Sprintf("locked-at %s %s", g.ID(), g.FormattedTimestamp()),
fmt.Sprintf("ownername %s %s", g.ID(), g.OwnerName()),
@@ -288,9 +286,9 @@ func (g *giteaLock) AsLockSpec(ownerID bool) ([]string, error) {
// AsArguments implements transfer.Lock
func (g *giteaLock) AsArguments() []string {
return []string{
- fmt.Sprintf("id=%s", g.ID()),
- fmt.Sprintf("path=%s", g.Path()),
- fmt.Sprintf("locked-at=%s", g.FormattedTimestamp()),
- fmt.Sprintf("ownername=%s", g.OwnerName()),
+ "id=" + g.ID(),
+ "path=" + g.Path(),
+ "locked-at=" + g.FormattedTimestamp(),
+ "ownername=" + g.OwnerName(),
}
}
diff --git a/modules/lfstransfer/backend/util.go b/modules/lfstransfer/backend/util.go
index cffefef375..afe02f799c 100644
--- a/modules/lfstransfer/backend/util.go
+++ b/modules/lfstransfer/backend/util.go
@@ -5,15 +5,16 @@ package backend
import (
"context"
- "crypto/tls"
"fmt"
- "net"
+ "io"
"net/http"
- "time"
+ "net/url"
+ "strings"
"code.gitea.io/gitea/modules/httplib"
- "code.gitea.io/gitea/modules/proxyprotocol"
+ "code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"github.com/charmbracelet/git-lfs-transfer/transfer"
)
@@ -60,8 +61,7 @@ const (
// Operations enum
const (
- opNone = iota
- opDownload
+ opDownload = iota + 1
opUpload
)
@@ -89,53 +89,61 @@ func statusCodeToErr(code int) error {
}
}
-func newInternalRequest(ctx context.Context, url, method string, headers map[string]string, body []byte) *httplib.Request {
- req := httplib.NewRequest(url, method).
- SetContext(ctx).
- SetTimeout(10*time.Second, 60*time.Second).
- SetTLSClientConfig(&tls.Config{
- InsecureSkipVerify: true,
- })
-
- if setting.Protocol == setting.HTTPUnix {
- req.SetTransport(&http.Transport{
- DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
- var d net.Dialer
- conn, err := d.DialContext(ctx, "unix", setting.HTTPAddr)
- if err != nil {
- return conn, err
- }
- if setting.LocalUseProxyProtocol {
- if err = proxyprotocol.WriteLocalHeader(conn); err != nil {
- _ = conn.Close()
- return nil, err
- }
- }
- return conn, err
- },
- })
- } else if setting.LocalUseProxyProtocol {
- req.SetTransport(&http.Transport{
- DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
- var d net.Dialer
- conn, err := d.DialContext(ctx, network, address)
- if err != nil {
- return conn, err
- }
- if err = proxyprotocol.WriteLocalHeader(conn); err != nil {
- _ = conn.Close()
- return nil, err
- }
- return conn, err
- },
- })
+func toInternalLFSURL(s string) string {
+ pos1 := strings.Index(s, "://")
+ if pos1 == -1 {
+ return ""
}
+ appSubURLWithSlash := setting.AppSubURL + "/"
+ pos2 := strings.Index(s[pos1+3:], appSubURLWithSlash)
+ if pos2 == -1 {
+ return ""
+ }
+ routePath := s[pos1+3+pos2+len(appSubURLWithSlash):]
+ fields := strings.SplitN(routePath, "/", 3)
+ if len(fields) < 3 || !strings.HasPrefix(fields[2], "info/lfs") {
+ return ""
+ }
+ return setting.LocalURL + "api/internal/repo/" + routePath
+}
+
+func isInternalLFSURL(s string) bool {
+ if !strings.HasPrefix(s, setting.LocalURL) {
+ return false
+ }
+ u, err := url.Parse(s)
+ if err != nil {
+ return false
+ }
+ routePath := util.PathJoinRelX(u.Path)
+ subRoutePath, cut := strings.CutPrefix(routePath, "api/internal/repo/")
+ if !cut {
+ return false
+ }
+ fields := strings.SplitN(subRoutePath, "/", 3)
+ if len(fields) < 3 || !strings.HasPrefix(fields[2], "info/lfs") {
+ return false
+ }
+ return true
+}
+func newInternalRequestLFS(ctx context.Context, internalURL, method string, headers map[string]string, body any) *httplib.Request {
+ if !isInternalLFSURL(internalURL) {
+ return nil
+ }
+ req := private.NewInternalRequest(ctx, internalURL, method)
+ req.SetReadWriteTimeout(0)
for k, v := range headers {
req.Header(k, v)
}
-
- req.Body(body)
-
+ switch body := body.(type) {
+ case nil: // do nothing
+ case []byte:
+ req.Body(body) // []byte
+ case io.Reader:
+ req.Body(body) // io.Reader or io.ReadCloser
+ default:
+ panic(fmt.Sprintf("unsupported request body type %T", body))
+ }
return req
}
diff --git a/modules/lfstransfer/backend/util_test.go b/modules/lfstransfer/backend/util_test.go
new file mode 100644
index 0000000000..408b53c369
--- /dev/null
+++ b/modules/lfstransfer/backend/util_test.go
@@ -0,0 +1,53 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package backend
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestToInternalLFSURL(t *testing.T) {
+ defer test.MockVariableValue(&setting.LocalURL, "http://localurl/")()
+ defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
+ cases := []struct {
+ url string
+ expected string
+ }{
+ {"http://appurl/any", ""},
+ {"http://appurl/sub/any", ""},
+ {"http://appurl/sub/owner/repo/any", ""},
+ {"http://appurl/sub/owner/repo/info/any", ""},
+ {"http://appurl/sub/owner/repo/info/lfs/any", "http://localurl/api/internal/repo/owner/repo/info/lfs/any"},
+ }
+ for _, c := range cases {
+ assert.Equal(t, c.expected, toInternalLFSURL(c.url), c.url)
+ }
+}
+
+func TestIsInternalLFSURL(t *testing.T) {
+ defer test.MockVariableValue(&setting.LocalURL, "http://localurl/")()
+ defer test.MockVariableValue(&setting.InternalToken, "mock-token")()
+ cases := []struct {
+ url string
+ expected bool
+ }{
+ {"", false},
+ {"http://otherurl/api/internal/repo/owner/repo/info/lfs/any", false},
+ {"http://localurl/api/internal/repo/owner/repo/info/lfs/any", true},
+ {"http://localurl/api/internal/repo/owner/repo/info", false},
+ {"http://localurl/api/internal/misc/owner/repo/info/lfs/any", false},
+ {"http://localurl/api/internal/owner/repo/info/lfs/any", false},
+ {"http://localurl/api/internal/foo/bar", false},
+ }
+ for _, c := range cases {
+ req := newInternalRequestLFS(t.Context(), c.url, "GET", nil, nil)
+ assert.Equal(t, c.expected, req != nil, c.url)
+ assert.Equal(t, c.expected, isInternalLFSURL(c.url), c.url)
+ }
+}
diff --git a/modules/log/event_format.go b/modules/log/event_format.go
index 8fda0a4980..4cf471d223 100644
--- a/modules/log/event_format.go
+++ b/modules/log/event_format.go
@@ -125,15 +125,19 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms
buf = append(buf, resetBytes...)
}
}
- if flags&(Lshortfile|Llongfile) != 0 {
+ if flags&(Lshortfile|Llongfile) != 0 && event.Filename != "" {
if mode.Colorize {
buf = append(buf, fgGreenBytes...)
}
file := event.Filename
if flags&Lmedfile == Lmedfile {
- startIndex := len(file) - 20
- if startIndex > 0 {
- file = "..." + file[startIndex:]
+ fileLen := len(file)
+ const softLimit = 20
+ if fileLen > softLimit {
+ slashIndex := strings.LastIndexByte(file[:fileLen-softLimit], '/')
+ if slashIndex != -1 {
+ file = ".../" + file[slashIndex+1:]
+ }
}
} else if flags&Lshortfile != 0 {
startIndex := strings.LastIndexByte(file, '/')
@@ -157,14 +161,22 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms
if mode.Colorize {
buf = append(buf, fgGreenBytes...)
}
- funcname := event.Caller
+ funcName := event.Caller
+ shortFuncName := funcName
if flags&Lshortfuncname != 0 {
- lastIndex := strings.LastIndexByte(funcname, '.')
- if lastIndex > 0 && len(funcname) > lastIndex+1 {
- funcname = funcname[lastIndex+1:]
+ // funcName = "code.gitea.io/gitea/modules/foo/bar.MyFunc.func1.2()"
+ slashPos := strings.LastIndexByte(funcName, '/')
+ dotPos := strings.IndexByte(funcName[slashPos+1:], '.')
+ if dotPos > 0 {
+ // shortFuncName = "MyFunc.func1.2()"
+ shortFuncName = funcName[slashPos+1+dotPos+1:]
+ if strings.Contains(shortFuncName, ".") {
+ shortFuncName = strings.ReplaceAll(shortFuncName, ".func", ".")
+ }
}
+ funcName = shortFuncName
}
- buf = append(buf, funcname...)
+ buf = append(buf, funcName...)
if mode.Colorize {
buf = append(buf, resetBytes...)
}
@@ -200,7 +212,7 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms
}
}
if hasColorValue {
- msg = []byte(fmt.Sprintf(msgFormat, msgArgs...))
+ msg = fmt.Appendf(nil, msgFormat, msgArgs...)
}
}
// try to re-use the pre-formatted simple text message
@@ -231,8 +243,8 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms
buf = append(buf, msg...)
if event.Stacktrace != "" && mode.StacktraceLevel <= event.Level {
- lines := bytes.Split([]byte(event.Stacktrace), []byte("\n"))
- for _, line := range lines {
+ lines := bytes.SplitSeq([]byte(event.Stacktrace), []byte("\n"))
+ for line := range lines {
buf = append(buf, "\n\t"...)
buf = append(buf, line...)
}
diff --git a/modules/log/event_writer_base.go b/modules/log/event_writer_base.go
index c327c48ca2..9189ca4e90 100644
--- a/modules/log/event_writer_base.go
+++ b/modules/log/event_writer_base.go
@@ -105,7 +105,7 @@ func (b *EventWriterBaseImpl) Run(ctx context.Context) {
case io.WriterTo:
_, err = msg.WriteTo(b.OutputWriteCloser)
default:
- _, err = b.OutputWriteCloser.Write([]byte(fmt.Sprint(msg)))
+ _, err = fmt.Fprint(b.OutputWriteCloser, msg)
}
if err != nil {
FallbackErrorf("unable to write log message of %q (%v): %v", b.Name, err, event.Msg)
diff --git a/modules/log/event_writer_conn_test.go b/modules/log/event_writer_conn_test.go
index 69e87aa8c4..2aff37812d 100644
--- a/modules/log/event_writer_conn_test.go
+++ b/modules/log/event_writer_conn_test.go
@@ -4,7 +4,6 @@
package log
import (
- "context"
"fmt"
"io"
"net"
@@ -40,7 +39,7 @@ func TestConnLogger(t *testing.T) {
level := INFO
flags := LstdFlags | LUTC | Lfuncname
- logger := NewLoggerWithWriters(context.Background(), "test", NewEventWriterConn("test-conn", WriterMode{
+ logger := NewLoggerWithWriters(t.Context(), "test", NewEventWriterConn("test-conn", WriterMode{
Level: level,
Prefix: prefix,
Flags: FlagsFromBits(flags),
diff --git a/modules/log/flags.go b/modules/log/flags.go
index 8064c91745..f409261150 100644
--- a/modules/log/flags.go
+++ b/modules/log/flags.go
@@ -123,7 +123,7 @@ func FlagsFromString(from string, def ...uint32) Flags {
return Flags{defined: true, flags: def[0]}
}
flags := uint32(0)
- for _, flag := range strings.Split(strings.ToLower(from), ",") {
+ for flag := range strings.SplitSeq(strings.ToLower(from), ",") {
flags |= flagFromString[strings.TrimSpace(flag)]
}
return Flags{defined: true, flags: flags}
diff --git a/modules/log/flags_test.go b/modules/log/flags_test.go
index 03972a9fb0..6eb65d8114 100644
--- a/modules/log/flags_test.go
+++ b/modules/log/flags_test.go
@@ -12,19 +12,19 @@ import (
)
func TestFlags(t *testing.T) {
- assert.EqualValues(t, Ldefault, Flags{}.Bits())
+ assert.Equal(t, Ldefault, Flags{}.Bits())
assert.EqualValues(t, 0, FlagsFromString("").Bits())
- assert.EqualValues(t, Lgopid, FlagsFromString("", Lgopid).Bits())
+ assert.Equal(t, Lgopid, FlagsFromString("", Lgopid).Bits())
assert.EqualValues(t, 0, FlagsFromString("none", Lgopid).Bits())
- assert.EqualValues(t, Ldate|Ltime, FlagsFromString("date,time", Lgopid).Bits())
+ assert.Equal(t, Ldate|Ltime, FlagsFromString("date,time", Lgopid).Bits())
- assert.EqualValues(t, "stdflags", FlagsFromString("stdflags").String())
- assert.EqualValues(t, "medfile", FlagsFromString("medfile").String())
+ assert.Equal(t, "stdflags", FlagsFromString("stdflags").String())
+ assert.Equal(t, "medfile", FlagsFromString("medfile").String())
bs, err := json.Marshal(FlagsFromString("utc,level"))
assert.NoError(t, err)
- assert.EqualValues(t, `"level,utc"`, string(bs))
+ assert.Equal(t, `"level,utc"`, string(bs))
var flags Flags
assert.NoError(t, json.Unmarshal(bs, &flags))
- assert.EqualValues(t, LUTC|Llevel, flags.Bits())
+ assert.Equal(t, LUTC|Llevel, flags.Bits())
}
diff --git a/modules/log/level_test.go b/modules/log/level_test.go
index cd18a807d8..0e59af6cb7 100644
--- a/modules/log/level_test.go
+++ b/modules/log/level_test.go
@@ -32,11 +32,11 @@ func TestLevelMarshalUnmarshalJSON(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, INFO, testLevel.Level)
- err = json.Unmarshal([]byte(fmt.Sprintf(`{"level":%d}`, 2)), &testLevel)
+ err = json.Unmarshal(fmt.Appendf(nil, `{"level":%d}`, 2), &testLevel)
assert.NoError(t, err)
assert.Equal(t, INFO, testLevel.Level)
- err = json.Unmarshal([]byte(fmt.Sprintf(`{"level":%d}`, 10012)), &testLevel)
+ err = json.Unmarshal(fmt.Appendf(nil, `{"level":%d}`, 10012), &testLevel)
assert.NoError(t, err)
assert.Equal(t, INFO, testLevel.Level)
@@ -51,5 +51,5 @@ func TestLevelMarshalUnmarshalJSON(t *testing.T) {
}
func makeTestLevelBytes(level string) []byte {
- return []byte(fmt.Sprintf(`{"level":"%s"}`, level))
+ return fmt.Appendf(nil, `{"level":"%s"}`, level)
}
diff --git a/modules/log/logger.go b/modules/log/logger.go
index a833b6ef0f..8b89e0eb5a 100644
--- a/modules/log/logger.go
+++ b/modules/log/logger.go
@@ -24,7 +24,7 @@ package log
// BaseLogger provides the basic logging functions
type BaseLogger interface {
- Log(skip int, level Level, format string, v ...any)
+ Log(skip int, event *Event, format string, v ...any)
GetLevel() Level
}
@@ -45,6 +45,6 @@ type Logger interface {
LevelLogger
}
-type LogStringer interface { //nolint:revive
+type LogStringer interface { //nolint:revive // export stutter
LogString() string
}
diff --git a/modules/log/logger_global.go b/modules/log/logger_global.go
index 6ce8b70fed..07c25cd62f 100644
--- a/modules/log/logger_global.go
+++ b/modules/log/logger_global.go
@@ -18,7 +18,7 @@ func GetLevel() Level {
}
func Log(skip int, level Level, format string, v ...any) {
- GetLogger(DEFAULT).Log(skip+1, level, format, v...)
+ GetLogger(DEFAULT).Log(skip+1, &Event{Level: level}, format, v...)
}
func Trace(format string, v ...any) {
diff --git a/modules/log/logger_impl.go b/modules/log/logger_impl.go
index 76dd5f43fb..551c1454aa 100644
--- a/modules/log/logger_impl.go
+++ b/modules/log/logger_impl.go
@@ -5,6 +5,7 @@ package log
import (
"context"
+ "reflect"
"runtime"
"strings"
"sync"
@@ -175,29 +176,42 @@ func (l *LoggerImpl) IsEnabled() bool {
return l.level.Load() < int32(FATAL) && len(l.eventWriters) > 0
}
+func asLogStringer(v any) LogStringer {
+ if s, ok := v.(LogStringer); ok {
+ return s
+ } else if a := reflect.ValueOf(v); a.Kind() == reflect.Struct {
+ // in case the receiver is a pointer, but the value is a struct
+ vp := reflect.New(a.Type())
+ vp.Elem().Set(a)
+ if s, ok := vp.Interface().(LogStringer); ok {
+ return s
+ }
+ }
+ return nil
+}
+
// Log prepares the log event, if the level matches, the event will be sent to the writers
-func (l *LoggerImpl) Log(skip int, level Level, format string, logArgs ...any) {
- if Level(l.level.Load()) > level {
+func (l *LoggerImpl) Log(skip int, event *Event, format string, logArgs ...any) {
+ if Level(l.level.Load()) > event.Level {
return
}
- event := &Event{
- Time: time.Now(),
- Level: level,
- Caller: "?()",
+ if event.Time.IsZero() {
+ event.Time = time.Now()
}
-
- pc, filename, line, ok := runtime.Caller(skip + 1)
- if ok {
- fn := runtime.FuncForPC(pc)
- if fn != nil {
- event.Caller = fn.Name() + "()"
+ if event.Caller == "" {
+ pc, filename, line, ok := runtime.Caller(skip + 1)
+ if ok {
+ fn := runtime.FuncForPC(pc)
+ if fn != nil {
+ fnName := fn.Name()
+ event.Caller = strings.ReplaceAll(fnName, "[...]", "") + "()" // generic function names are "foo[...]"
+ }
+ }
+ event.Filename, event.Line = strings.TrimPrefix(filename, projectPackagePrefix), line
+ if l.stacktraceLevel.Load() <= int32(event.Level) {
+ event.Stacktrace = Stack(skip + 1)
}
- }
- event.Filename, event.Line = strings.TrimPrefix(filename, projectPackagePrefix), line
-
- if l.stacktraceLevel.Load() <= int32(level) {
- event.Stacktrace = Stack(skip + 1)
}
// get a simple text message without color
@@ -207,11 +221,11 @@ func (l *LoggerImpl) Log(skip int, level Level, format string, logArgs ...any) {
// handle LogStringer values
for i, v := range msgArgs {
if cv, ok := v.(*ColoredValue); ok {
- if s, ok := cv.v.(LogStringer); ok {
- cv.v = logStringFormatter{v: s}
+ if ls := asLogStringer(cv.v); ls != nil {
+ cv.v = logStringFormatter{v: ls}
}
- } else if s, ok := v.(LogStringer); ok {
- msgArgs[i] = logStringFormatter{v: s}
+ } else if ls := asLogStringer(v); ls != nil {
+ msgArgs[i] = logStringFormatter{v: ls}
}
}
diff --git a/modules/log/logger_test.go b/modules/log/logger_test.go
index 0de14eb411..a74139dc51 100644
--- a/modules/log/logger_test.go
+++ b/modules/log/logger_test.go
@@ -4,7 +4,7 @@
package log
import (
- "context"
+ "regexp"
"sync"
"testing"
"time"
@@ -35,11 +35,11 @@ func (d *dummyWriter) Close() error {
return nil
}
-func (d *dummyWriter) GetLogs() []string {
+func (d *dummyWriter) FetchLogs() []string {
d.mu.Lock()
defer d.mu.Unlock()
- logs := make([]string, len(d.logs))
- copy(logs, d.logs)
+ logs := d.logs
+ d.logs = nil
return logs
}
@@ -53,20 +53,20 @@ func newDummyWriter(name string, level Level, delay time.Duration) *dummyWriter
}
func TestLogger(t *testing.T) {
- logger := NewLoggerWithWriters(context.Background(), "test")
+ logger := NewLoggerWithWriters(t.Context(), "test")
dump := logger.DumpWriters()
assert.Empty(t, dump)
- assert.EqualValues(t, NONE, logger.GetLevel())
+ assert.Equal(t, NONE, logger.GetLevel())
assert.False(t, logger.IsEnabled())
w1 := newDummyWriter("dummy-1", DEBUG, 0)
logger.AddWriters(w1)
- assert.EqualValues(t, DEBUG, logger.GetLevel())
+ assert.Equal(t, DEBUG, logger.GetLevel())
w2 := newDummyWriter("dummy-2", WARN, 200*time.Millisecond)
logger.AddWriters(w2)
- assert.EqualValues(t, DEBUG, logger.GetLevel())
+ assert.Equal(t, DEBUG, logger.GetLevel())
dump = logger.DumpWriters()
assert.Len(t, dump, 2)
@@ -77,18 +77,18 @@ func TestLogger(t *testing.T) {
// w2 is slow, so only w1 has logs
time.Sleep(100 * time.Millisecond)
- assert.Equal(t, []string{"debug-level\n", "error-level\n"}, w1.GetLogs())
- assert.Equal(t, []string{}, w2.GetLogs())
+ assert.Equal(t, []string{"debug-level\n", "error-level\n"}, w1.FetchLogs())
+ assert.Empty(t, w2.FetchLogs())
logger.Close()
// after Close, all logs are flushed
- assert.Equal(t, []string{"debug-level\n", "error-level\n"}, w1.GetLogs())
- assert.Equal(t, []string{"error-level\n"}, w2.GetLogs())
+ assert.Empty(t, w1.FetchLogs())
+ assert.Equal(t, []string{"error-level\n"}, w2.FetchLogs())
}
func TestLoggerPause(t *testing.T) {
- logger := NewLoggerWithWriters(context.Background(), "test")
+ logger := NewLoggerWithWriters(t.Context(), "test")
w1 := newDummyWriter("dummy-1", DEBUG, 0)
logger.AddWriters(w1)
@@ -98,12 +98,12 @@ func TestLoggerPause(t *testing.T) {
logger.Info("info-level")
time.Sleep(100 * time.Millisecond)
- assert.Equal(t, []string{}, w1.GetLogs())
+ assert.Empty(t, w1.FetchLogs())
GetManager().ResumeAll()
time.Sleep(100 * time.Millisecond)
- assert.Equal(t, []string{"info-level\n"}, w1.GetLogs())
+ assert.Equal(t, []string{"info-level\n"}, w1.FetchLogs())
logger.Close()
}
@@ -116,21 +116,54 @@ func (t testLogString) LogString() string {
return "log-string"
}
-func TestLoggerLogString(t *testing.T) {
- logger := NewLoggerWithWriters(context.Background(), "test")
+type testLogStringPtrReceiver struct {
+ Field string
+}
- w1 := newDummyWriter("dummy-1", DEBUG, 0)
- w1.Mode.Colorize = true
- logger.AddWriters(w1)
+func (t *testLogStringPtrReceiver) LogString() string {
+ return "log-string-ptr-receiver"
+}
- logger.Info("%s %s %#v %v", testLogString{}, &testLogString{}, testLogString{Field: "detail"}, NewColoredValue(testLogString{}, FgRed))
- logger.Close()
+func genericFunc[T any](logger Logger, v T) {
+ logger.Info("from genericFunc: %v", v)
+}
- assert.Equal(t, []string{"log-string log-string log.testLogString{Field:\"detail\"} \x1b[31mlog-string\x1b[0m\n"}, w1.GetLogs())
+func TestLoggerOutput(t *testing.T) {
+ t.Run("LogString", func(t *testing.T) {
+ logger := NewLoggerWithWriters(t.Context(), "test")
+ w1 := newDummyWriter("dummy-1", DEBUG, 0)
+ w1.Mode.Colorize = true
+ logger.AddWriters(w1)
+ logger.Info("%s %s %#v %v", testLogString{}, &testLogString{}, testLogString{Field: "detail"}, NewColoredValue(testLogString{}, FgRed))
+ logger.Info("%s %s %#v %v", testLogStringPtrReceiver{}, &testLogStringPtrReceiver{}, testLogStringPtrReceiver{Field: "detail"}, NewColoredValue(testLogStringPtrReceiver{}, FgRed))
+ logger.Close()
+
+ assert.Equal(t, []string{
+ "log-string log-string log.testLogString{Field:\"detail\"} \x1b[31mlog-string\x1b[0m\n",
+ "log-string-ptr-receiver log-string-ptr-receiver &log.testLogStringPtrReceiver{Field:\"detail\"} \x1b[31mlog-string-ptr-receiver\x1b[0m\n",
+ }, w1.FetchLogs())
+ })
+
+ t.Run("Caller", func(t *testing.T) {
+ logger := NewLoggerWithWriters(t.Context(), "test")
+ w1 := newDummyWriter("dummy-1", DEBUG, 0)
+ w1.EventWriterBaseImpl.Mode.Flags.flags = Lmedfile | Lshortfuncname
+ logger.AddWriters(w1)
+ anonymousFunc := func(logger Logger) {
+ logger.Info("from anonymousFunc")
+ }
+ genericFunc(logger, "123")
+ anonymousFunc(logger)
+ logger.Close()
+ logs := w1.FetchLogs()
+ assert.Len(t, logs, 2)
+ assert.Regexp(t, `modules/log/logger_test.go:\w+:`+regexp.QuoteMeta(`genericFunc() from genericFunc: 123`), logs[0])
+ assert.Regexp(t, `modules/log/logger_test.go:\w+:`+regexp.QuoteMeta(`TestLoggerOutput.2.1() from anonymousFunc`), logs[1])
+ })
}
func TestLoggerExpressionFilter(t *testing.T) {
- logger := NewLoggerWithWriters(context.Background(), "test")
+ logger := NewLoggerWithWriters(t.Context(), "test")
w1 := newDummyWriter("dummy-1", DEBUG, 0)
w1.Mode.Expression = "foo.*"
@@ -142,5 +175,5 @@ func TestLoggerExpressionFilter(t *testing.T) {
logger.SendLogEvent(&Event{Level: INFO, Filename: "foo.go", MsgSimpleText: "by filename"})
logger.Close()
- assert.Equal(t, []string{"foo\n", "foo bar\n", "by filename\n"}, w1.GetLogs())
+ assert.Equal(t, []string{"foo\n", "foo bar\n", "by filename\n"}, w1.FetchLogs())
}
diff --git a/modules/log/manager_test.go b/modules/log/manager_test.go
index b8fbf84613..beddbccb73 100644
--- a/modules/log/manager_test.go
+++ b/modules/log/manager_test.go
@@ -37,6 +37,6 @@ func TestSharedWorker(t *testing.T) {
m.Close()
- logs := w.(*dummyWriter).GetLogs()
+ logs := w.(*dummyWriter).FetchLogs()
assert.Equal(t, []string{"msg-1\n", "msg-2\n", "msg-3\n"}, logs)
}
diff --git a/modules/log/misc.go b/modules/log/misc.go
index ae4ce04cf3..c9d230e4ac 100644
--- a/modules/log/misc.go
+++ b/modules/log/misc.go
@@ -19,8 +19,8 @@ func BaseLoggerToGeneralLogger(b BaseLogger) Logger {
var _ Logger = (*baseToLogger)(nil)
-func (s *baseToLogger) Log(skip int, level Level, format string, v ...any) {
- s.base.Log(skip+1, level, format, v...)
+func (s *baseToLogger) Log(skip int, event *Event, format string, v ...any) {
+ s.base.Log(skip+1, event, format, v...)
}
func (s *baseToLogger) GetLevel() Level {
@@ -32,27 +32,27 @@ func (s *baseToLogger) LevelEnabled(level Level) bool {
}
func (s *baseToLogger) Trace(format string, v ...any) {
- s.base.Log(1, TRACE, format, v...)
+ s.base.Log(1, &Event{Level: TRACE}, format, v...)
}
func (s *baseToLogger) Debug(format string, v ...any) {
- s.base.Log(1, DEBUG, format, v...)
+ s.base.Log(1, &Event{Level: DEBUG}, format, v...)
}
func (s *baseToLogger) Info(format string, v ...any) {
- s.base.Log(1, INFO, format, v...)
+ s.base.Log(1, &Event{Level: INFO}, format, v...)
}
func (s *baseToLogger) Warn(format string, v ...any) {
- s.base.Log(1, WARN, format, v...)
+ s.base.Log(1, &Event{Level: WARN}, format, v...)
}
func (s *baseToLogger) Error(format string, v ...any) {
- s.base.Log(1, ERROR, format, v...)
+ s.base.Log(1, &Event{Level: ERROR}, format, v...)
}
func (s *baseToLogger) Critical(format string, v ...any) {
- s.base.Log(1, CRITICAL, format, v...)
+ s.base.Log(1, &Event{Level: CRITICAL}, format, v...)
}
type PrintfLogger struct {
diff --git a/modules/markup/asciicast/asciicast.go b/modules/markup/asciicast/asciicast.go
index 1d0d631650..d86d61d7c4 100644
--- a/modules/markup/asciicast/asciicast.go
+++ b/modules/markup/asciicast/asciicast.go
@@ -46,7 +46,7 @@ func (Renderer) Render(ctx *markup.RenderContext, _ io.Reader, output io.Writer)
setting.AppSubURL,
url.PathEscape(ctx.RenderOptions.Metas["user"]),
url.PathEscape(ctx.RenderOptions.Metas["repo"]),
- ctx.RenderOptions.Metas["BranchNameSubURL"],
+ ctx.RenderOptions.Metas["RefTypeNameSubURL"],
url.PathEscape(ctx.RenderOptions.RelativePath),
)
return ctx.RenderInternal.FormatWithSafeAttrs(output, `<div class="%s" %s="%s"></div>`, playerClassName, playerSrcAttr, rawURL)
diff --git a/modules/markup/common/footnote.go b/modules/markup/common/footnote.go
index 4406803694..1ece436c66 100644
--- a/modules/markup/common/footnote.go
+++ b/modules/markup/common/footnote.go
@@ -53,7 +53,7 @@ type FootnoteLink struct {
// Dump implements Node.Dump.
func (n *FootnoteLink) Dump(source []byte, level int) {
m := map[string]string{}
- m["Index"] = fmt.Sprintf("%v", n.Index)
+ m["Index"] = strconv.Itoa(n.Index)
m["Name"] = fmt.Sprintf("%v", n.Name)
ast.DumpHelper(n, source, level, m, nil)
}
@@ -85,7 +85,7 @@ type FootnoteBackLink struct {
// Dump implements Node.Dump.
func (n *FootnoteBackLink) Dump(source []byte, level int) {
m := map[string]string{}
- m["Index"] = fmt.Sprintf("%v", n.Index)
+ m["Index"] = strconv.Itoa(n.Index)
m["Name"] = fmt.Sprintf("%v", n.Name)
ast.DumpHelper(n, source, level, m, nil)
}
@@ -151,7 +151,7 @@ type FootnoteList struct {
// Dump implements Node.Dump.
func (n *FootnoteList) Dump(source []byte, level int) {
m := map[string]string{}
- m["Count"] = fmt.Sprintf("%v", n.Count)
+ m["Count"] = strconv.Itoa(n.Count)
ast.DumpHelper(n, source, level, m, nil)
}
@@ -197,7 +197,7 @@ func (b *footnoteBlockParser) Open(parent ast.Node, reader text.Reader, pc parse
return nil, parser.NoChildren
}
open := pos + 1
- closure := util.FindClosure(line[pos+1:], '[', ']', false, false) //nolint
+ closure := util.FindClosure(line[pos+1:], '[', ']', false, false) //nolint:staticcheck // deprecated function
closes := pos + 1 + closure
next := closes + 1
if closure > -1 {
@@ -287,7 +287,7 @@ func (s *footnoteParser) Parse(parent ast.Node, block text.Reader, pc parser.Con
return nil
}
open := pos
- closure := util.FindClosure(line[pos:], '[', ']', false, false) //nolint
+ closure := util.FindClosure(line[pos:], '[', ']', false, false) //nolint:staticcheck // deprecated function
if closure < 0 {
return nil
}
@@ -409,9 +409,9 @@ func (r *FootnoteHTMLRenderer) renderFootnoteLink(w util.BufWriter, source []byt
_, _ = w.Write(n.Name)
_, _ = w.WriteString(`"><a href="#fn:`)
_, _ = w.Write(n.Name)
- _, _ = w.WriteString(`" class="footnote-ref" role="doc-noteref">`)
+ _, _ = w.WriteString(`" class="footnote-ref" role="doc-noteref">`) // FIXME: here and below, need to keep the classes
_, _ = w.WriteString(is)
- _, _ = w.WriteString(`</a></sup>`)
+ _, _ = w.WriteString(` </a></sup>`) // the style doesn't work at the moment, so add a space to separate the names
}
return ast.WalkContinue, nil
}
diff --git a/modules/markup/common/linkify.go b/modules/markup/common/linkify.go
index 52888958fa..3eecb97eac 100644
--- a/modules/markup/common/linkify.go
+++ b/modules/markup/common/linkify.go
@@ -85,9 +85,10 @@ func (s *linkifyParser) Parse(parent ast.Node, block text.Reader, pc parser.Cont
} else if lastChar == ')' {
closing := 0
for i := m[1] - 1; i >= m[0]; i-- {
- if line[i] == ')' {
+ switch line[i] {
+ case ')':
closing++
- } else if line[i] == '(' {
+ case '(':
closing--
}
}
diff --git a/modules/markup/console/console.go b/modules/markup/console/console.go
index 06f3acfa68..492579b0a5 100644
--- a/modules/markup/console/console.go
+++ b/modules/markup/console/console.go
@@ -6,13 +6,14 @@ package console
import (
"bytes"
"io"
- "path"
+ "unicode/utf8"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/typesniffer"
+ "code.gitea.io/gitea/modules/util"
trend "github.com/buildkite/terminal-to-html/v3"
- "github.com/go-enry/go-enry/v2"
)
func init() {
@@ -22,6 +23,8 @@ func init() {
// Renderer implements markup.Renderer
type Renderer struct{}
+var _ markup.RendererContentDetector = (*Renderer)(nil)
+
// Name implements markup.Renderer
func (Renderer) Name() string {
return "console"
@@ -40,15 +43,36 @@ func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
}
// CanRender implements markup.RendererContentDetector
-func (Renderer) CanRender(filename string, input io.Reader) bool {
- buf, err := io.ReadAll(input)
- if err != nil {
+func (Renderer) CanRender(filename string, sniffedType typesniffer.SniffedType, prefetchBuf []byte) bool {
+ if !sniffedType.IsTextPlain() {
return false
}
- if enry.GetLanguage(path.Base(filename), buf) != enry.OtherLanguage {
+
+ s := util.UnsafeBytesToString(prefetchBuf)
+ rs := []rune(s)
+ cnt := 0
+ firstErrPos := -1
+ isCtrlSep := func(p int) bool {
+ return p < len(rs) && (rs[p] == ';' || rs[p] == 'm')
+ }
+ for i, c := range rs {
+ if c == 0 {
+ return false
+ }
+ if c == '\x1b' {
+ match := i+1 < len(rs) && rs[i+1] == '['
+ if match && (isCtrlSep(i+2) || isCtrlSep(i+3) || isCtrlSep(i+4) || isCtrlSep(i+5)) {
+ cnt++
+ }
+ }
+ if c == utf8.RuneError && firstErrPos == -1 {
+ firstErrPos = i
+ }
+ }
+ if firstErrPos != -1 && firstErrPos != len(rs)-1 {
return false
}
- return bytes.ContainsRune(buf, '\x1b')
+ return cnt >= 2 // only render it as console output if there are at least two escape sequences
}
// Render renders terminal colors to HTML with all specific handling stuff.
diff --git a/modules/markup/console/console_test.go b/modules/markup/console/console_test.go
index e1f0da1f01..d1192bebc2 100644
--- a/modules/markup/console/console_test.go
+++ b/modules/markup/console/console_test.go
@@ -4,28 +4,43 @@
package console
import (
- "context"
"strings"
"testing"
"code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/modules/typesniffer"
"github.com/stretchr/testify/assert"
)
func TestRenderConsole(t *testing.T) {
- var render Renderer
- kases := map[string]string{
- "\x1b[37m\x1b[40mnpm\x1b[0m \x1b[0m\x1b[32minfo\x1b[0m \x1b[0m\x1b[35mit worked if it ends with\x1b[0m ok": "<span class=\"term-fg37 term-bg40\">npm</span> <span class=\"term-fg32\">info</span> <span class=\"term-fg35\">it worked if it ends with</span> ok",
+ cases := []struct {
+ input string
+ expected string
+ }{
+ {"\x1b[37m\x1b[40mnpm\x1b[0m \x1b[0m\x1b[32minfo\x1b[0m \x1b[0m\x1b[35mit worked if it ends with\x1b[0m ok", `<span class="term-fg37 term-bg40">npm</span> <span class="term-fg32">info</span> <span class="term-fg35">it worked if it ends with</span> ok`},
+ {"\x1b[1;2m \x1b[123m 啊", `<span class="term-fg2"> 啊</span>`},
+ {"\x1b[1;2m \x1b[123m \xef", `<span class="term-fg2"> �</span>`},
+ {"\x1b[1;2m \x1b[123m \xef \xef", ``},
+ {"\x1b[12", ``},
+ {"\x1b[1", ``},
+ {"\x1b[FOO\x1b[", ``},
+ {"\x1b[mFOO\x1b[m", `FOO`},
}
- for k, v := range kases {
+ var render Renderer
+ for i, c := range cases {
var buf strings.Builder
- canRender := render.CanRender("test", strings.NewReader(k))
- assert.True(t, canRender)
+ st := typesniffer.DetectContentType([]byte(c.input))
+ canRender := render.CanRender("test", st, []byte(c.input))
+ if c.expected == "" {
+ assert.False(t, canRender, "case %d: expected not to render", i)
+ continue
+ }
- err := render.Render(markup.NewRenderContext(context.Background()), strings.NewReader(k), &buf)
+ assert.True(t, canRender)
+ err := render.Render(markup.NewRenderContext(t.Context()), strings.NewReader(c.input), &buf)
assert.NoError(t, err)
- assert.EqualValues(t, v, buf.String())
+ assert.Equal(t, c.expected, buf.String())
}
}
diff --git a/modules/markup/csv/csv_test.go b/modules/markup/csv/csv_test.go
index 4c47170c30..fff7f0baca 100644
--- a/modules/markup/csv/csv_test.go
+++ b/modules/markup/csv/csv_test.go
@@ -4,7 +4,6 @@
package markup
import (
- "context"
"strings"
"testing"
@@ -24,8 +23,8 @@ func TestRenderCSV(t *testing.T) {
for k, v := range kases {
var buf strings.Builder
- err := render.Render(markup.NewRenderContext(context.Background()), strings.NewReader(k), &buf)
+ err := render.Render(markup.NewRenderContext(t.Context()), strings.NewReader(k), &buf)
assert.NoError(t, err)
- assert.EqualValues(t, v, buf.String())
+ assert.Equal(t, v, buf.String())
}
}
diff --git a/modules/markup/external/external.go b/modules/markup/external/external.go
index 03242e569e..39861ade12 100644
--- a/modules/markup/external/external.go
+++ b/modules/markup/external/external.go
@@ -12,11 +12,9 @@ import (
"runtime"
"strings"
- "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
)
// RegisterRenderers registers all supported third part renderers according settings
@@ -77,27 +75,22 @@ func envMark(envName string) string {
// Render renders the data of the document to HTML via the external tool.
func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
- var (
- command = strings.NewReplacer(
- envMark("GITEA_PREFIX_SRC"), ctx.RenderHelper.ResolveLink("", markup.LinkTypeDefault),
- envMark("GITEA_PREFIX_RAW"), ctx.RenderHelper.ResolveLink("", markup.LinkTypeRaw),
- ).Replace(p.Command)
- commands = strings.Fields(command)
- args = commands[1:]
- )
+ baseLinkSrc := ctx.RenderHelper.ResolveLink("", markup.LinkTypeDefault)
+ baseLinkRaw := ctx.RenderHelper.ResolveLink("", markup.LinkTypeRaw)
+ command := strings.NewReplacer(
+ envMark("GITEA_PREFIX_SRC"), baseLinkSrc,
+ envMark("GITEA_PREFIX_RAW"), baseLinkRaw,
+ ).Replace(p.Command)
+ commands := strings.Fields(command)
+ args := commands[1:]
if p.IsInputFile {
// write to temp file
- f, err := os.CreateTemp("", "gitea_input")
+ f, cleanup, err := setting.AppDataTempDir("git-repo-content").CreateTempFileRandom("gitea_input")
if err != nil {
return fmt.Errorf("%s create temp file when rendering %s failed: %w", p.Name(), p.Command, err)
}
- tmpPath := f.Name()
- defer func() {
- if err := util.Remove(tmpPath); err != nil {
- log.Warn("Unable to remove temporary file: %s: Error: %v", tmpPath, err)
- }
- }()
+ defer cleanup()
_, err = io.Copy(f, input)
if err != nil {
@@ -112,14 +105,14 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.
args = append(args, f.Name())
}
- processCtx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("Render [%s] for %s", commands[0], ctx.RenderHelper.ResolveLink("", markup.LinkTypeDefault)))
+ processCtx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("Render [%s] for %s", commands[0], baseLinkSrc))
defer finished()
cmd := exec.CommandContext(processCtx, commands[0], args...)
cmd.Env = append(
os.Environ(),
- "GITEA_PREFIX_SRC="+ctx.RenderHelper.ResolveLink("", markup.LinkTypeDefault),
- "GITEA_PREFIX_RAW="+ctx.RenderHelper.ResolveLink("", markup.LinkTypeRaw),
+ "GITEA_PREFIX_SRC="+baseLinkSrc,
+ "GITEA_PREFIX_RAW="+baseLinkRaw,
)
if !p.IsInputFile {
cmd.Stdin = input
diff --git a/modules/markup/html.go b/modules/markup/html.go
index bb12febf27..51afd4be00 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -8,6 +8,7 @@ import (
"fmt"
"io"
"regexp"
+ "slices"
"strings"
"sync"
@@ -32,7 +33,6 @@ type globalVarsType struct {
comparePattern *regexp.Regexp
fullURLPattern *regexp.Regexp
emailRegex *regexp.Regexp
- blackfridayExtRegex *regexp.Regexp
emojiShortCodeRegex *regexp.Regexp
issueFullPattern *regexp.Regexp
filesChangedFullPattern *regexp.Regexp
@@ -47,7 +47,7 @@ var globalVars = sync.OnceValue(func() *globalVarsType {
// NOTE: All below regex matching do not perform any extra validation.
// Thus a link is produced even if the linked entity does not exist.
// While fast, this is also incorrect and lead to false positives.
- // TODO: fix invalid linking issue
+ // TODO: fix invalid linking issue (update: stale TODO, what issues? maybe no TODO anymore)
// valid chars in encoded path and parameter: [-+~_%.a-zA-Z0-9/]
@@ -72,10 +72,8 @@ var globalVars = sync.OnceValue(func() *globalVarsType {
// it is still accepted by the CommonMark specification, as well as the HTML5 spec:
// http://spec.commonmark.org/0.28/#email-address
// https://html.spec.whatwg.org/multipage/input.html#e-mail-state-(type%3Demail)
- v.emailRegex = regexp.MustCompile("(?:\\s|^|\\(|\\[)([a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]{2,}(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+)(?:\\s|$|\\)|\\]|;|,|\\?|!|\\.(\\s|$))")
-
- // blackfridayExtRegex is for blackfriday extensions create IDs like fn:user-content-footnote
- v.blackfridayExtRegex = regexp.MustCompile(`[^:]*:user-content-`)
+ // At the moment, we use stricter rule for rendering purpose: only allow the "name" part starting after the word boundary
+ v.emailRegex = regexp.MustCompile(`\b([-\w.!#$%&'*+/=?^{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9]{2,}(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+)\b`)
// emojiShortCodeRegex find emoji by alias like :smile:
v.emojiShortCodeRegex = regexp.MustCompile(`:[-+\w]+:`)
@@ -89,22 +87,18 @@ var globalVars = sync.OnceValue(func() *globalVarsType {
// codePreviewPattern matches "http://domain/.../{owner}/{repo}/src/commit/{commit}/{filepath}#L10-L20"
v.codePreviewPattern = regexp.MustCompile(`https?://\S+/([^\s/]+)/([^\s/]+)/src/commit/([0-9a-f]{7,64})(/\S+)#(L\d+(-L\d+)?)`)
- v.tagCleaner = regexp.MustCompile(`<((?:/?\w+/\w+)|(?:/[\w ]+/)|(/?[hH][tT][mM][lL]\b)|(/?[hH][eE][aA][dD]\b))`)
+ // cleans: "<foo/bar", "<any words/", ("<html", "<head", "<script", "<style", "<?", "<%")
+ v.tagCleaner = regexp.MustCompile(`(?i)<(/?\w+/\w+|/[\w ]+/|/?(html|head|script|style|%|\?)\b)`)
v.nulCleaner = strings.NewReplacer("\000", "")
return v
})
-// IsFullURLBytes reports whether link fits valid format.
-func IsFullURLBytes(link []byte) bool {
- return globalVars().fullURLPattern.Match(link)
-}
-
func IsFullURLString(link string) bool {
return globalVars().fullURLPattern.MatchString(link)
}
func IsNonEmptyRelativePath(link string) bool {
- return link != "" && !IsFullURLString(link) && link[0] != '/' && link[0] != '?' && link[0] != '#'
+ return link != "" && !IsFullURLString(link) && link[0] != '?' && link[0] != '#'
}
// CustomLinkURLSchemes allows for additional schemes to be detected when parsing links within text
@@ -116,13 +110,7 @@ func CustomLinkURLSchemes(schemes []string) {
if !validScheme.MatchString(s) {
continue
}
- without := false
- for _, sna := range xurls.SchemesNoAuthority {
- if s == sna {
- without = true
- break
- }
- }
+ without := slices.Contains(xurls.SchemesNoAuthority, s)
if without {
s += ":"
} else {
@@ -260,7 +248,7 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output
node, err := html.Parse(io.MultiReader(
// prepend "<html><body>"
strings.NewReader("<html><body>"),
- // Strip out nuls - they're always invalid
+ // strip out NULLs (they're always invalid), and escape known tags
bytes.NewReader(globalVars().tagCleaner.ReplaceAll([]byte(globalVars().nulCleaner.Replace(string(rawHTML))), []byte("&lt;$1"))),
// close the tags
strings.NewReader("</body></html>"),
@@ -316,44 +304,39 @@ func isEmojiNode(node *html.Node) bool {
}
func visitNode(ctx *RenderContext, procs []processor, node *html.Node) *html.Node {
- // Add user-content- to IDs and "#" links if they don't already have them
- for idx, attr := range node.Attr {
- val := strings.TrimPrefix(attr.Val, "#")
- notHasPrefix := !(strings.HasPrefix(val, "user-content-") || globalVars().blackfridayExtRegex.MatchString(val))
-
- if attr.Key == "id" && notHasPrefix {
- node.Attr[idx].Val = "user-content-" + attr.Val
- }
-
- if attr.Key == "href" && strings.HasPrefix(attr.Val, "#") && notHasPrefix {
- node.Attr[idx].Val = "#user-content-" + val
- }
- }
-
- switch node.Type {
- case html.TextNode:
+ if node.Type == html.TextNode {
for _, proc := range procs {
proc(ctx, node) // it might add siblings
}
+ return node.NextSibling
+ }
+ if node.Type != html.ElementNode {
+ return node.NextSibling
+ }
- case html.ElementNode:
- if isEmojiNode(node) {
- // TextNode emoji will be converted to `<span class="emoji">`, then the next iteration will visit the "span"
- // if we don't stop it, it will go into the TextNode again and create an infinite recursion
- return node.NextSibling
- } else if node.Data == "code" || node.Data == "pre" {
- return node.NextSibling // ignore code and pre nodes
- } else if node.Data == "img" {
- return visitNodeImg(ctx, node)
- } else if node.Data == "video" {
- return visitNodeVideo(ctx, node)
- } else if node.Data == "a" {
- procs = emojiProcessors // Restrict text in links to emojis
- }
- for n := node.FirstChild; n != nil; {
- n = visitNode(ctx, procs, n)
- }
- default:
+ processNodeAttrID(node)
+ processFootnoteNode(ctx, node) // FIXME: the footnote processing should be done in the "footnote.go" renderer directly
+
+ if isEmojiNode(node) {
+ // TextNode emoji will be converted to `<span class="emoji">`, then the next iteration will visit the "span"
+ // if we don't stop it, it will go into the TextNode again and create an infinite recursion
+ return node.NextSibling
+ } else if node.Data == "code" || node.Data == "pre" {
+ return node.NextSibling // ignore code and pre nodes
+ } else if node.Data == "img" {
+ return visitNodeImg(ctx, node)
+ } else if node.Data == "video" {
+ return visitNodeVideo(ctx, node)
+ }
+
+ if node.Data == "a" {
+ processNodeA(ctx, node)
+ // only use emoji processors for the content in the "A" tag,
+ // because the content there is not processable, for example: the content is a commit id or a full URL.
+ procs = emojiProcessors
+ }
+ for n := node.FirstChild; n != nil; {
+ n = visitNode(ctx, procs, n)
}
return node.NextSibling
}
diff --git a/modules/markup/html_commit.go b/modules/markup/html_commit.go
index aa1b7d034a..fe7a034967 100644
--- a/modules/markup/html_commit.go
+++ b/modules/markup/html_commit.go
@@ -43,7 +43,6 @@ func createCodeLink(href, content, class string) *html.Node {
code := &html.Node{
Type: html.ElementNode,
Data: atom.Code.String(),
- Attr: []html.Attribute{{Key: "class", Val: "nohighlight"}},
}
code.AppendChild(text)
@@ -63,7 +62,7 @@ func anyHashPatternExtract(s string) (ret anyHashPatternResult, ok bool) {
// if url ends in '.', it's very likely that it is not part of the actual url but used to finish a sentence.
ret.PosEnd--
ret.FullURL = ret.FullURL[:len(ret.FullURL)-1]
- for i := 0; i < len(m); i++ {
+ for i := range m {
m[i] = min(m[i], ret.PosEnd)
}
}
@@ -189,7 +188,7 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
continue
}
- link := ctx.RenderHelper.ResolveLink(util.URLJoin(ctx.RenderOptions.Metas["user"], ctx.RenderOptions.Metas["repo"], "commit", hash), LinkTypeApp)
+ link := "/:root/" + util.URLJoin(ctx.RenderOptions.Metas["user"], ctx.RenderOptions.Metas["repo"], "commit", hash)
replaceContent(node, m[2], m[3], createCodeLink(link, base.ShortSha(hash), "commit"))
start = 0
node = node.NextSibling.NextSibling
@@ -205,9 +204,9 @@ func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) {
return
}
- reftext := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha)
- linkHref := ctx.RenderHelper.ResolveLink(util.URLJoin(ref.Owner, ref.Name, "commit", ref.CommitSha), LinkTypeApp)
- link := createLink(ctx, linkHref, reftext, "commit")
+ refText := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha)
+ linkHref := "/:root/" + util.URLJoin(ref.Owner, ref.Name, "commit", ref.CommitSha)
+ link := createLink(ctx, linkHref, refText, "commit")
replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
node = node.NextSibling.NextSibling
diff --git a/modules/markup/html_email.go b/modules/markup/html_email.go
index cbfae8b829..cf18e99d98 100644
--- a/modules/markup/html_email.go
+++ b/modules/markup/html_email.go
@@ -3,7 +3,11 @@
package markup
-import "golang.org/x/net/html"
+import (
+ "strings"
+
+ "golang.org/x/net/html"
+)
// emailAddressProcessor replaces raw email addresses with a mailto: link.
func emailAddressProcessor(ctx *RenderContext, node *html.Node) {
@@ -14,6 +18,14 @@ func emailAddressProcessor(ctx *RenderContext, node *html.Node) {
return
}
+ var nextByte byte
+ if len(node.Data) > m[3] {
+ nextByte = node.Data[m[3]]
+ }
+ if strings.IndexByte(":/", nextByte) != -1 {
+ // for cases: "git@gitea.com:owner/repo.git", "https://git@gitea.com/owner/repo.git"
+ return
+ }
mail := node.Data[m[2]:m[3]]
replaceContent(node, m[2], m[3], createLink(ctx, "mailto:"+mail, mail, "" /*mailto*/))
node = node.NextSibling.NextSibling
diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go
index 159d712955..467cc509d0 100644
--- a/modules/markup/html_internal_test.go
+++ b/modules/markup/html_internal_test.go
@@ -107,7 +107,7 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
isExternal := false
if marker == "!" {
path = "pulls"
- prefix = "http://localhost:3000/someUser/someRepo/pulls/"
+ prefix = "/someUser/someRepo/pulls/"
} else {
path = "issues"
prefix = "https://someurl.com/someUser/someRepo/"
@@ -116,7 +116,7 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
links := make([]any, len(indices))
for i, index := range indices {
- links[i] = numericIssueLink(util.URLJoin(TestRepoURL, path), "ref-issue", index, marker)
+ links[i] = numericIssueLink(util.URLJoin("/test-owner/test-repo", path), "ref-issue", index, marker)
}
expectedNil := fmt.Sprintf(expectedFmt, links...)
testRenderIssueIndexPattern(t, s, expectedNil, NewTestRenderContext(TestAppURL, localMetas))
@@ -293,13 +293,13 @@ func TestRender_AutoLink(t *testing.T) {
// render valid commit URLs
tmp := util.URLJoin(TestRepoURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae")
- test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24</code></a>")
+ test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code>d8a994ef24</code></a>")
tmp += "#diff-2"
- test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>")
+ test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code>d8a994ef24 (diff-2)</code></a>")
// render other commit URLs
tmp = "https://external-link.gitea.io/go-gitea/gitea/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2"
- test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>")
+ test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code>d8a994ef24 (diff-2)</code></a>")
}
func TestRender_FullIssueURLs(t *testing.T) {
@@ -405,10 +405,10 @@ func TestRegExp_anySHA1Pattern(t *testing.T) {
if v.CommitID == "" {
assert.False(t, ok)
} else {
- assert.EqualValues(t, strings.TrimSuffix(k, "."), ret.FullURL)
- assert.EqualValues(t, v.CommitID, ret.CommitID)
- assert.EqualValues(t, v.SubPath, ret.SubPath)
- assert.EqualValues(t, v.QueryHash, ret.QueryHash)
+ assert.Equal(t, strings.TrimSuffix(k, "."), ret.FullURL)
+ assert.Equal(t, v.CommitID, ret.CommitID)
+ assert.Equal(t, v.SubPath, ret.SubPath)
+ assert.Equal(t, v.QueryHash, ret.QueryHash)
}
}
}
diff --git a/modules/markup/html_issue.go b/modules/markup/html_issue.go
index 7a6f33011a..85bec5db20 100644
--- a/modules/markup/html_issue.go
+++ b/modules/markup/html_issue.go
@@ -82,7 +82,7 @@ func createIssueLinkContentWithSummary(ctx *RenderContext, linkHref string, ref
h, err := DefaultRenderHelperFuncs.RenderRepoIssueIconTitle(ctx, RenderIssueIconTitleOptions{
OwnerName: ref.Owner,
RepoName: ref.Name,
- LinkHref: linkHref,
+ LinkHref: ctx.RenderHelper.ResolveLink(linkHref, LinkTypeDefault),
IssueIndex: issueIndex,
})
if err != nil {
@@ -162,7 +162,7 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
issueOwner := util.Iif(ref.Owner == "", ctx.RenderOptions.Metas["user"], ref.Owner)
issueRepo := util.Iif(ref.Owner == "", ctx.RenderOptions.Metas["repo"], ref.Name)
issuePath := util.Iif(ref.IsPull, "pulls", "issues")
- linkHref := ctx.RenderHelper.ResolveLink(util.URLJoin(issueOwner, issueRepo, issuePath, ref.Issue), LinkTypeApp)
+ linkHref := "/:root/" + util.URLJoin(issueOwner, issueRepo, issuePath, ref.Issue)
// at the moment, only render the issue index in a full line (or simple line) as icon+title
// otherwise it would be too noisy for "take #1 as an example" in a sentence
diff --git a/modules/markup/html_issue_test.go b/modules/markup/html_issue_test.go
index 8d189fbdf6..39cd9dcf6a 100644
--- a/modules/markup/html_issue_test.go
+++ b/modules/markup/html_issue_test.go
@@ -30,6 +30,7 @@ func TestRender_IssueList(t *testing.T) {
rctx := markup.NewTestRenderContext(markup.TestAppURL, map[string]string{
"user": "test-user", "repo": "test-repo",
"markupAllowShortIssuePattern": "true",
+ "footnoteContextId": "12345",
})
out, err := markdown.RenderString(rctx, input)
require.NoError(t, err)
@@ -39,7 +40,7 @@ func TestRender_IssueList(t *testing.T) {
t.Run("NormalIssueRef", func(t *testing.T) {
test(
"#12345",
- `<p><a href="http://localhost:3000/test-user/test-repo/issues/12345" class="ref-issue" rel="nofollow">#12345</a></p>`,
+ `<p><a href="/test-user/test-repo/issues/12345" class="ref-issue" rel="nofollow">#12345</a></p>`,
)
})
@@ -56,7 +57,7 @@ func TestRender_IssueList(t *testing.T) {
test(
"* foo #12345 bar",
`<ul>
-<li>foo <a href="http://localhost:3000/test-user/test-repo/issues/12345" class="ref-issue" rel="nofollow">#12345</a> bar</li>
+<li>foo <a href="/test-user/test-repo/issues/12345" class="ref-issue" rel="nofollow">#12345</a> bar</li>
</ul>`,
)
})
@@ -69,4 +70,22 @@ func TestRender_IssueList(t *testing.T) {
</ul>`,
)
})
+
+ t.Run("IssueFootnote", func(t *testing.T) {
+ test(
+ "foo[^1][^2]\n\n[^1]: bar\n[^2]: baz",
+ `<p>foo<sup id="fnref:user-content-1-12345"><a href="#fn:user-content-1-12345" rel="nofollow">1 </a></sup><sup id="fnref:user-content-2-12345"><a href="#fn:user-content-2-12345" rel="nofollow">2 </a></sup></p>
+<div>
+<hr/>
+<ol>
+<li id="fn:user-content-1-12345">
+<p>bar <a href="#fnref:user-content-1-12345" rel="nofollow">↩︎</a></p>
+</li>
+<li id="fn:user-content-2-12345">
+<p>baz <a href="#fnref:user-content-2-12345" rel="nofollow">↩︎</a></p>
+</li>
+</ol>
+</div>`,
+ )
+ })
}
diff --git a/modules/markup/html_link.go b/modules/markup/html_link.go
index 0e7a988d36..43faef1681 100644
--- a/modules/markup/html_link.go
+++ b/modules/markup/html_link.go
@@ -31,8 +31,8 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
// It makes page handling terrible, but we prefer GitHub syntax
// And fall back to MediaWiki only when it is obvious from the look
// Of text and link contents
- sl := strings.Split(content, "|")
- for _, v := range sl {
+ sl := strings.SplitSeq(content, "|")
+ for v := range sl {
if equalPos := strings.IndexByte(v, '='); equalPos == -1 {
// There is no equal in this argument; this is a mandatory arg
if props["name"] == "" {
@@ -125,7 +125,6 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
}
}
if image {
- link = ctx.RenderHelper.ResolveLink(link, LinkTypeMedia)
title := props["title"]
if title == "" {
title = props["alt"]
@@ -151,7 +150,6 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
childNode.Attr = childNode.Attr[:2]
}
} else {
- link = ctx.RenderHelper.ResolveLink(link, LinkTypeDefault)
childNode.Type = html.TextNode
childNode.Data = name
}
diff --git a/modules/markup/html_mention.go b/modules/markup/html_mention.go
index fffa12e7b7..f97c034cf3 100644
--- a/modules/markup/html_mention.go
+++ b/modules/markup/html_mention.go
@@ -33,7 +33,7 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
if ok && strings.Contains(mention, "/") {
mentionOrgAndTeam := strings.Split(mention, "/")
if mentionOrgAndTeam[0][1:] == ctx.RenderOptions.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") {
- link := ctx.RenderHelper.ResolveLink(util.URLJoin("org", ctx.RenderOptions.Metas["org"], "teams", mentionOrgAndTeam[1]), LinkTypeApp)
+ link := "/:root/" + util.URLJoin("org", ctx.RenderOptions.Metas["org"], "teams", mentionOrgAndTeam[1])
replaceContent(node, loc.Start, loc.End, createLink(ctx, link, mention, "" /*mention*/))
node = node.NextSibling.NextSibling
start = 0
@@ -45,7 +45,7 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
mentionedUsername := mention[1:]
if DefaultRenderHelperFuncs != nil && DefaultRenderHelperFuncs.IsUsernameMentionable(ctx, mentionedUsername) {
- link := ctx.RenderHelper.ResolveLink(mentionedUsername, LinkTypeApp)
+ link := "/:root/" + mentionedUsername
replaceContent(node, loc.Start, loc.End, createLink(ctx, link, mention, "" /*mention*/))
node = node.NextSibling.NextSibling
start = 0
diff --git a/modules/markup/html_node.go b/modules/markup/html_node.go
index 6e8ca67900..4eb78fdd2b 100644
--- a/modules/markup/html_node.go
+++ b/modules/markup/html_node.go
@@ -4,42 +4,105 @@
package markup
import (
+ "strings"
+
"golang.org/x/net/html"
)
+func isAnchorIDUserContent(s string) bool {
+ // blackfridayExtRegex is for blackfriday extensions create IDs like fn:user-content-footnote
+ // old logic: blackfridayExtRegex = regexp.MustCompile(`[^:]*:user-content-`)
+ return strings.HasPrefix(s, "user-content-") || strings.Contains(s, ":user-content-")
+}
+
+func isAnchorIDFootnote(s string) bool {
+ return strings.HasPrefix(s, "fnref:user-content-") || strings.HasPrefix(s, "fn:user-content-")
+}
+
+func isAnchorHrefFootnote(s string) bool {
+ return strings.HasPrefix(s, "#fnref:user-content-") || strings.HasPrefix(s, "#fn:user-content-")
+}
+
+func processNodeAttrID(node *html.Node) {
+ // Add user-content- to IDs and "#" links if they don't already have them,
+ // and convert the link href to a relative link to the host root
+ for idx, attr := range node.Attr {
+ if attr.Key == "id" {
+ if !isAnchorIDUserContent(attr.Val) {
+ node.Attr[idx].Val = "user-content-" + attr.Val
+ }
+ }
+ }
+}
+
+func processFootnoteNode(ctx *RenderContext, node *html.Node) {
+ for idx, attr := range node.Attr {
+ if (attr.Key == "id" && isAnchorIDFootnote(attr.Val)) ||
+ (attr.Key == "href" && isAnchorHrefFootnote(attr.Val)) {
+ if footnoteContextID := ctx.RenderOptions.Metas["footnoteContextId"]; footnoteContextID != "" {
+ node.Attr[idx].Val = attr.Val + "-" + footnoteContextID
+ }
+ continue
+ }
+ }
+}
+
+func processNodeA(ctx *RenderContext, node *html.Node) {
+ for idx, attr := range node.Attr {
+ if attr.Key == "href" {
+ if anchorID, ok := strings.CutPrefix(attr.Val, "#"); ok {
+ if !isAnchorIDUserContent(attr.Val) {
+ node.Attr[idx].Val = "#user-content-" + anchorID
+ }
+ } else {
+ node.Attr[idx].Val = ctx.RenderHelper.ResolveLink(attr.Val, LinkTypeDefault)
+ }
+ }
+ }
+}
+
func visitNodeImg(ctx *RenderContext, img *html.Node) (next *html.Node) {
next = img.NextSibling
- for i, attr := range img.Attr {
- if attr.Key != "src" {
+ attrSrc, hasLazy := "", false
+ for i, imgAttr := range img.Attr {
+ hasLazy = hasLazy || imgAttr.Key == "loading" && imgAttr.Val == "lazy"
+ if imgAttr.Key != "src" {
+ attrSrc = imgAttr.Val
continue
}
- if IsNonEmptyRelativePath(attr.Val) {
- attr.Val = ctx.RenderHelper.ResolveLink(attr.Val, LinkTypeMedia)
+ imgSrcOrigin := imgAttr.Val
+ isLinkable := imgSrcOrigin != "" && !strings.HasPrefix(imgSrcOrigin, "data:")
- // By default, the "<img>" tag should also be clickable,
- // because frontend use `<img>` to paste the re-scaled image into the markdown,
- // so it must match the default markdown image behavior.
- hasParentAnchor := false
- for p := img.Parent; p != nil; p = p.Parent {
- if hasParentAnchor = p.Type == html.ElementNode && p.Data == "a"; hasParentAnchor {
- break
- }
- }
- if !hasParentAnchor {
- imgA := &html.Node{Type: html.ElementNode, Data: "a", Attr: []html.Attribute{
- {Key: "href", Val: attr.Val},
- {Key: "target", Val: "_blank"},
- }}
- parent := img.Parent
- imgNext := img.NextSibling
- parent.RemoveChild(img)
- parent.InsertBefore(imgA, imgNext)
- imgA.AppendChild(img)
+ // By default, the "<img>" tag should also be clickable,
+ // because frontend uses `<img>` to paste the re-scaled image into the Markdown,
+ // so it must match the default Markdown image behavior.
+ cnt := 0
+ for p := img.Parent; isLinkable && p != nil && cnt < 2; p = p.Parent {
+ if hasParentAnchor := p.Type == html.ElementNode && p.Data == "a"; hasParentAnchor {
+ isLinkable = false
+ break
}
+ cnt++
}
- attr.Val = camoHandleLink(attr.Val)
- img.Attr[i] = attr
+ if isLinkable {
+ wrapper := &html.Node{Type: html.ElementNode, Data: "a", Attr: []html.Attribute{
+ {Key: "href", Val: ctx.RenderHelper.ResolveLink(imgSrcOrigin, LinkTypeDefault)},
+ {Key: "target", Val: "_blank"},
+ }}
+ parent := img.Parent
+ imgNext := img.NextSibling
+ parent.RemoveChild(img)
+ parent.InsertBefore(wrapper, imgNext)
+ wrapper.AppendChild(img)
+ }
+
+ imgAttr.Val = ctx.RenderHelper.ResolveLink(imgSrcOrigin, LinkTypeMedia)
+ imgAttr.Val = camoHandleLink(imgAttr.Val)
+ img.Attr[i] = imgAttr
+ }
+ if !RenderBehaviorForTesting.DisableAdditionalAttributes && !hasLazy && !strings.HasPrefix(attrSrc, "data:") {
+ img.Attr = append(img.Attr, html.Attribute{Key: "loading", Val: "lazy"})
}
return next
}
diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go
index 6d8f24184b..5fdbf43f7c 100644
--- a/modules/markup/html_test.go
+++ b/modules/markup/html_test.go
@@ -35,6 +35,7 @@ func TestRender_Commits(t *testing.T) {
sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
repo := markup.TestAppURL + testRepoOwnerName + "/" + testRepoName + "/"
commit := util.URLJoin(repo, "commit", sha)
+ commitPath := "/user13/repo11/commit/" + sha
tree := util.URLJoin(repo, "tree", sha, "src")
file := util.URLJoin(repo, "commit", sha, "example.txt")
@@ -44,9 +45,9 @@ func TestRender_Commits(t *testing.T) {
commitCompare := util.URLJoin(repo, "compare", sha+"..."+sha)
commitCompareWithHash := commitCompare + "#L2"
- test(sha, `<p><a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`)
- test(sha[:7], `<p><a href="`+commit[:len(commit)-(40-7)]+`" rel="nofollow"><code>65f1bf2</code></a></p>`)
- test(sha[:39], `<p><a href="`+commit[:len(commit)-(40-39)]+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`)
+ test(sha, `<p><a href="`+commitPath+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`)
+ test(sha[:7], `<p><a href="`+commitPath[:len(commitPath)-(40-7)]+`" rel="nofollow"><code>65f1bf2</code></a></p>`)
+ test(sha[:39], `<p><a href="`+commitPath[:len(commitPath)-(40-39)]+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`)
test(commit, `<p><a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`)
test(tree, `<p><a href="`+tree+`" rel="nofollow"><code>65f1bf27bc/src</code></a></p>`)
@@ -57,13 +58,13 @@ func TestRender_Commits(t *testing.T) {
test(commitCompare, `<p><a href="`+commitCompare+`" rel="nofollow"><code>65f1bf27bc...65f1bf27bc</code></a></p>`)
test(commitCompareWithHash, `<p><a href="`+commitCompareWithHash+`" rel="nofollow"><code>65f1bf27bc...65f1bf27bc (L2)</code></a></p>`)
- test("commit "+sha, `<p>commit <a href="`+commit+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`)
+ test("commit "+sha, `<p>commit <a href="`+commitPath+`" rel="nofollow"><code>65f1bf27bc</code></a></p>`)
test("/home/gitea/"+sha, "<p>/home/gitea/"+sha+"</p>")
test("deadbeef", `<p>deadbeef</p>`)
test("d27ace93", `<p>d27ace93</p>`)
test(sha[:14]+".x", `<p>`+sha[:14]+`.x</p>`)
- expected14 := `<a href="` + commit[:len(commit)-(40-14)] + `" rel="nofollow"><code>` + sha[:10] + `</code></a>`
+ expected14 := `<a href="` + commitPath[:len(commitPath)-(40-14)] + `" rel="nofollow"><code>` + sha[:10] + `</code></a>`
test(sha[:14]+".", `<p>`+expected14+`.</p>`)
test(sha[:14]+",", `<p>`+expected14+`,</p>`)
test("["+sha[:14]+"]", `<p>[`+expected14+`]</p>`)
@@ -80,10 +81,10 @@ func TestRender_CrossReferences(t *testing.T) {
test(
"test-owner/test-repo#12345",
- `<p><a href="`+util.URLJoin(markup.TestAppURL, "test-owner", "test-repo", "issues", "12345")+`" class="ref-issue" rel="nofollow">test-owner/test-repo#12345</a></p>`)
+ `<p><a href="/test-owner/test-repo/issues/12345" class="ref-issue" rel="nofollow">test-owner/test-repo#12345</a></p>`)
test(
"go-gitea/gitea#12345",
- `<p><a href="`+util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`)
+ `<p><a href="/go-gitea/gitea/issues/12345" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`)
test(
"/home/gitea/go-gitea/gitea#12345",
`<p>/home/gitea/go-gitea/gitea#12345</p>`)
@@ -224,10 +225,10 @@ func TestRender_email(t *testing.T) {
test := func(input, expected string) {
res, err := markup.RenderString(markup.NewTestRenderContext().WithRelativePath("a.md"), input)
assert.NoError(t, err)
- assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res))
+ assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res), "input: %s", input)
}
- // Text that should be turned into email link
+ // Text that should be turned into email link
test(
"info@gitea.com",
`<p><a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a></p>`)
@@ -259,28 +260,48 @@ func TestRender_email(t *testing.T) {
<a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>?
<a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>!</p>`)
+ // match GitHub behavior
+ test("email@domain@domain.com", `<p>email@<a href="mailto:domain@domain.com" rel="nofollow">domain@domain.com</a></p>`)
+
+ // match GitHub behavior
+ test(`"info@gitea.com"`, `<p>&#34;<a href="mailto:info@gitea.com" rel="nofollow">info@gitea.com</a>&#34;</p>`)
+
// Test that should *not* be turned into email links
test(
- "\"info@gitea.com\"",
- `<p>&#34;info@gitea.com&#34;</p>`)
- test(
"/home/gitea/mailstore/info@gitea/com",
`<p>/home/gitea/mailstore/info@gitea/com</p>`)
test(
"git@try.gitea.io:go-gitea/gitea.git",
`<p>git@try.gitea.io:go-gitea/gitea.git</p>`)
test(
+ "https://foo:bar@gitea.io",
+ `<p><a href="https://foo:bar@gitea.io" rel="nofollow">https://foo:bar@gitea.io</a></p>`)
+ test(
"gitea@3",
`<p>gitea@3</p>`)
test(
"gitea@gmail.c",
`<p>gitea@gmail.c</p>`)
test(
- "email@domain@domain.com",
- `<p>email@domain@domain.com</p>`)
- test(
"email@domain..com",
`<p>email@domain..com</p>`)
+
+ cases := []struct {
+ input, expected string
+ }{
+ // match GitHub behavior
+ {"?a@d.zz", `<p>?<a href="mailto:a@d.zz" rel="nofollow">a@d.zz</a></p>`},
+ {"*a@d.zz", `<p>*<a href="mailto:a@d.zz" rel="nofollow">a@d.zz</a></p>`},
+ {"~a@d.zz", `<p>~<a href="mailto:a@d.zz" rel="nofollow">a@d.zz</a></p>`},
+
+ // the following cases don't match GitHub behavior, but they are valid email addresses ...
+ // maybe we should reduce the candidate characters for the "name" part in the future
+ {"a*a@d.zz", `<p><a href="mailto:a*a@d.zz" rel="nofollow">a*a@d.zz</a></p>`},
+ {"a~a@d.zz", `<p><a href="mailto:a~a@d.zz" rel="nofollow">a~a@d.zz</a></p>`},
+ }
+ for _, c := range cases {
+ test(c.input, c.expected)
+ }
}
func TestRender_emoji(t *testing.T) {
@@ -468,7 +489,7 @@ func Test_ParseClusterFuzz(t *testing.T) {
assert.NotContains(t, res.String(), "<html")
}
-func TestPostProcess_RenderDocument(t *testing.T) {
+func TestPostProcess(t *testing.T) {
setting.StaticURLPrefix = markup.TestAppURL // can't run standalone
defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
@@ -479,7 +500,7 @@ func TestPostProcess_RenderDocument(t *testing.T) {
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res.String()))
}
- // Issue index shouldn't be post processing in a document.
+ // Issue index shouldn't be post-processing in a document.
test(
"#1",
"#1")
@@ -487,9 +508,9 @@ func TestPostProcess_RenderDocument(t *testing.T) {
// But cross-referenced issue index should work.
test(
"go-gitea/gitea#12345",
- `<a href="`+util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue">go-gitea/gitea#12345</a>`)
+ `<a href="/go-gitea/gitea/issues/12345" class="ref-issue">go-gitea/gitea#12345</a>`)
- // Test that other post processing still works.
+ // Test that other post-processing still works.
test(
":gitea:",
`<span class="emoji" aria-label="gitea"><img alt=":gitea:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/gitea.png"/></span>`)
@@ -498,6 +519,16 @@ func TestPostProcess_RenderDocument(t *testing.T) {
`Some text with <span class="emoji" aria-label="grinning face with smiling eyes">😄</span> in the middle`)
test("http://localhost:3000/person/repo/issues/4#issuecomment-1234",
`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234" class="ref-issue">person/repo#4 (comment)</a>`)
+
+ // special tags, GitHub's behavior, and for unclosed tags, output as text content as much as possible
+ test("<script>a", `&lt;script&gt;a`)
+ test("<script>a</script>", `&lt;script&gt;a&lt;/script&gt;`)
+ test("<STYLE>a", `&lt;STYLE&gt;a`)
+ test("<style>a</STYLE>", `&lt;style&gt;a&lt;/STYLE&gt;`)
+
+ // other special tags, our special behavior
+ test("<?php\nfoo", "&lt;?php\nfoo")
+ test("<%asp\nfoo", "&lt;%asp\nfoo")
}
func TestIssue16020(t *testing.T) {
@@ -522,7 +553,7 @@ func BenchmarkEmojiPostprocess(b *testing.B) {
data += data
}
b.ResetTimer()
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
var res strings.Builder
err := markup.PostProcessDefault(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
assert.NoError(b, err)
@@ -543,7 +574,7 @@ func TestIssue18471(t *testing.T) {
err := markup.PostProcessDefault(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
assert.NoError(t, err)
- assert.Equal(t, `<a href="http://domain/org/repo/compare/783b039...da951ce" class="compare"><code class="nohighlight">783b039...da951ce</code></a>`, res.String())
+ assert.Equal(t, `<a href="http://domain/org/repo/compare/783b039...da951ce" class="compare"><code>783b039...da951ce</code></a>`, res.String())
}
func TestIsFullURL(t *testing.T) {
diff --git a/modules/markup/internal/internal_test.go b/modules/markup/internal/internal_test.go
index 98ff3bc079..590bcbb67f 100644
--- a/modules/markup/internal/internal_test.go
+++ b/modules/markup/internal/internal_test.go
@@ -35,7 +35,7 @@ func TestRenderInternal(t *testing.T) {
assert.EqualValues(t, c.protected, protected)
_, _ = io.WriteString(in, string(protected))
_ = in.Close()
- assert.EqualValues(t, c.recovered, out.String())
+ assert.Equal(t, c.recovered, out.String())
}
var r1, r2 RenderInternal
@@ -44,11 +44,11 @@ func TestRenderInternal(t *testing.T) {
_ = r1.init("sec", nil)
protected = r1.ProtectSafeAttrs(`<div class="test"></div>`)
assert.EqualValues(t, `<div data-attr-class="sec:test"></div>`, protected)
- assert.EqualValues(t, "data-attr-class", r1.SafeAttr("class"))
- assert.EqualValues(t, "sec:val", r1.SafeValue("val"))
+ assert.Equal(t, "data-attr-class", r1.SafeAttr("class"))
+ assert.Equal(t, "sec:val", r1.SafeValue("val"))
recovered, ok := r1.RecoverProtectedValue("sec:val")
assert.True(t, ok)
- assert.EqualValues(t, "val", recovered)
+ assert.Equal(t, "val", recovered)
recovered, ok = r1.RecoverProtectedValue("other:val")
assert.False(t, ok)
assert.Empty(t, recovered)
@@ -57,5 +57,5 @@ func TestRenderInternal(t *testing.T) {
in2 := r2.init("sec-other", out2)
_, _ = io.WriteString(in2, string(protected))
_ = in2.Close()
- assert.EqualValues(t, `<div data-attr-class="sec:test"></div>`, out2.String(), "different secureID should not recover the value")
+ assert.Equal(t, `<div data-attr-class="sec:test"></div>`, out2.String(), "different secureID should not recover the value")
}
diff --git a/modules/markup/internal/renderinternal.go b/modules/markup/internal/renderinternal.go
index 4d58f160a9..7a3e37b120 100644
--- a/modules/markup/internal/renderinternal.go
+++ b/modules/markup/internal/renderinternal.go
@@ -76,7 +76,7 @@ func (r *RenderInternal) ProtectSafeAttrs(content template.HTML) template.HTML {
return template.HTML(reAttrClass().ReplaceAllString(string(content), `$1 data-attr-class="`+r.secureIDPrefix+`$2"$3`))
}
-func (r *RenderInternal) FormatWithSafeAttrs(w io.Writer, fmt string, a ...any) error {
+func (r *RenderInternal) FormatWithSafeAttrs(w io.Writer, fmt template.HTML, a ...any) error {
_, err := w.Write([]byte(r.ProtectSafeAttrs(htmlutil.HTMLFormat(fmt, a...))))
return err
}
diff --git a/modules/markup/markdown/ast.go b/modules/markup/markdown/ast.go
index ca165b1ba0..f29f883734 100644
--- a/modules/markup/markdown/ast.go
+++ b/modules/markup/markdown/ast.go
@@ -4,6 +4,7 @@
package markdown
import (
+ "html/template"
"strconv"
"github.com/yuin/goldmark/ast"
@@ -29,9 +30,7 @@ func (n *Details) Kind() ast.NodeKind {
// NewDetails returns a new Paragraph node.
func NewDetails() *Details {
- return &Details{
- BaseBlock: ast.BaseBlock{},
- }
+ return &Details{}
}
// Summary is a block that contains the summary of details block
@@ -54,9 +53,7 @@ func (n *Summary) Kind() ast.NodeKind {
// NewSummary returns a new Summary node.
func NewSummary() *Summary {
- return &Summary{
- BaseBlock: ast.BaseBlock{},
- }
+ return &Summary{}
}
// TaskCheckBoxListItem is a block that represents a list item of a markdown block with a checkbox
@@ -95,29 +92,6 @@ type Icon struct {
Name []byte
}
-// Dump implements Node.Dump .
-func (n *Icon) Dump(source []byte, level int) {
- m := map[string]string{}
- m["Name"] = string(n.Name)
- ast.DumpHelper(n, source, level, m, nil)
-}
-
-// KindIcon is the NodeKind for Icon
-var KindIcon = ast.NewNodeKind("Icon")
-
-// Kind implements Node.Kind.
-func (n *Icon) Kind() ast.NodeKind {
- return KindIcon
-}
-
-// NewIcon returns a new Paragraph node.
-func NewIcon(name string) *Icon {
- return &Icon{
- BaseInline: ast.BaseInline{},
- Name: []byte(name),
- }
-}
-
// ColorPreview is an inline for a color preview
type ColorPreview struct {
ast.BaseInline
@@ -175,3 +149,24 @@ func NewAttention(attentionType string) *Attention {
AttentionType: attentionType,
}
}
+
+var KindRawHTML = ast.NewNodeKind("RawHTML")
+
+type RawHTML struct {
+ ast.BaseBlock
+ rawHTML template.HTML
+}
+
+func (n *RawHTML) Dump(source []byte, level int) {
+ m := map[string]string{}
+ m["RawHTML"] = string(n.rawHTML)
+ ast.DumpHelper(n, source, level, m, nil)
+}
+
+func (n *RawHTML) Kind() ast.NodeKind {
+ return KindRawHTML
+}
+
+func NewRawHTML(rawHTML template.HTML) *RawHTML {
+ return &RawHTML{rawHTML: rawHTML}
+}
diff --git a/modules/markup/markdown/convertyaml.go b/modules/markup/markdown/convertyaml.go
index 1675b68be2..04664a9c1d 100644
--- a/modules/markup/markdown/convertyaml.go
+++ b/modules/markup/markdown/convertyaml.go
@@ -4,23 +4,22 @@
package markdown
import (
+ "strings"
+
+ "code.gitea.io/gitea/modules/htmlutil"
+ "code.gitea.io/gitea/modules/svg"
+
"github.com/yuin/goldmark/ast"
east "github.com/yuin/goldmark/extension/ast"
"gopkg.in/yaml.v3"
)
func nodeToTable(meta *yaml.Node) ast.Node {
- for {
- if meta == nil {
- return nil
- }
- switch meta.Kind {
- case yaml.DocumentNode:
- meta = meta.Content[0]
- continue
- default:
- }
- break
+ for meta != nil && meta.Kind == yaml.DocumentNode {
+ meta = meta.Content[0]
+ }
+ if meta == nil {
+ return nil
}
switch meta.Kind {
case yaml.MappingNode:
@@ -72,12 +71,28 @@ func sequenceNodeToTable(meta *yaml.Node) ast.Node {
return table
}
-func nodeToDetails(meta *yaml.Node, icon string) ast.Node {
+func nodeToDetails(g *ASTTransformer, meta *yaml.Node) ast.Node {
+ for meta != nil && meta.Kind == yaml.DocumentNode {
+ meta = meta.Content[0]
+ }
+ if meta == nil {
+ return nil
+ }
+ if meta.Kind != yaml.MappingNode {
+ return nil
+ }
+ var keys []string
+ for i := 0; i < len(meta.Content); i += 2 {
+ if meta.Content[i].Kind == yaml.ScalarNode {
+ keys = append(keys, meta.Content[i].Value)
+ }
+ }
details := NewDetails()
+ details.SetAttributeString(g.renderInternal.SafeAttr("class"), g.renderInternal.SafeValue("frontmatter-content"))
summary := NewSummary()
- summary.AppendChild(summary, NewIcon(icon))
+ summaryInnerHTML := htmlutil.HTMLFormat("%s %s", svg.RenderHTML("octicon-table", 12), strings.Join(keys, ", "))
+ summary.AppendChild(summary, NewRawHTML(summaryInnerHTML))
details.AppendChild(details, summary)
details.AppendChild(details, nodeToTable(meta))
-
return details
}
diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go
index 69c2a96ff1..b28fa9824e 100644
--- a/modules/markup/markdown/goldmark.go
+++ b/modules/markup/markdown/goldmark.go
@@ -5,14 +5,10 @@ package markdown
import (
"fmt"
- "regexp"
- "strings"
- "sync"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/internal"
- "code.gitea.io/gitea/modules/setting"
"github.com/yuin/goldmark/ast"
east "github.com/yuin/goldmark/extension/ast"
@@ -51,7 +47,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
tocList := make([]Header, 0, 20)
if rc.yamlNode != nil {
- metaNode := rc.toMetaNode()
+ metaNode := rc.toMetaNode(g)
if metaNode != nil {
node.InsertBefore(node, firstChild, metaNode)
}
@@ -68,23 +64,12 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
g.transformHeading(ctx, v, reader, &tocList)
case *ast.Paragraph:
g.applyElementDir(v)
- case *ast.Image:
- g.transformImage(ctx, v)
- case *ast.Link:
- g.transformLink(ctx, v)
case *ast.List:
g.transformList(ctx, v, rc)
case *ast.Text:
if v.SoftLineBreak() && !v.HardLineBreak() {
- // TODO: this was a quite unclear part, old code: `if metas["mode"] != "document" { use comment link break setting }`
- // many places render non-comment contents with no mode=document, then these contents also use comment's hard line break setting
- // especially in many tests.
- markdownLineBreakStyle := ctx.RenderOptions.Metas["markdownLineBreakStyle"]
- if markdownLineBreakStyle == "comment" {
- v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInComments)
- } else if markdownLineBreakStyle == "document" {
- v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInDocuments)
- }
+ newLineHardBreak := ctx.RenderOptions.Metas["markdownNewLineHardBreak"] == "true"
+ v.SetHardLineBreak(newLineHardBreak)
}
case *ast.CodeSpan:
g.transformCodeSpan(ctx, v, reader)
@@ -111,11 +96,6 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
}
}
-// it is copied from old code, which is quite doubtful whether it is correct
-var reValidIconName = sync.OnceValue(func() *regexp.Regexp {
- return regexp.MustCompile(`^[-\w]+$`) // old: regexp.MustCompile("^[a-z ]+$")
-})
-
// NewHTMLRenderer creates a HTMLRenderer to render in the gitea form.
func NewHTMLRenderer(renderInternal *internal.RenderInternal, opts ...html.Option) renderer.NodeRenderer {
r := &HTMLRenderer{
@@ -140,11 +120,11 @@ func (r *HTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(ast.KindDocument, r.renderDocument)
reg.Register(KindDetails, r.renderDetails)
reg.Register(KindSummary, r.renderSummary)
- reg.Register(KindIcon, r.renderIcon)
reg.Register(ast.KindCodeSpan, r.renderCodeSpan)
reg.Register(KindAttention, r.renderAttention)
reg.Register(KindTaskCheckBoxListItem, r.renderTaskCheckBoxListItem)
reg.Register(east.KindTaskCheckBox, r.renderTaskCheckBox)
+ reg.Register(KindRawHTML, r.renderRawHTML)
}
func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
@@ -155,7 +135,7 @@ func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.
if entering {
_, err = w.WriteString("<div")
if err == nil {
- _, err = w.WriteString(fmt.Sprintf(` lang=%q`, val))
+ _, err = fmt.Fprintf(w, ` lang=%q`, val)
}
if err == nil {
_, err = w.WriteRune('>')
@@ -206,30 +186,14 @@ func (r *HTMLRenderer) renderSummary(w util.BufWriter, source []byte, node ast.N
return ast.WalkContinue, nil
}
-func (r *HTMLRenderer) renderIcon(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
+func (r *HTMLRenderer) renderRawHTML(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
-
- n := node.(*Icon)
-
- name := strings.TrimSpace(strings.ToLower(string(n.Name)))
-
- if len(name) == 0 {
- // skip this
- return ast.WalkContinue, nil
- }
-
- if !reValidIconName().MatchString(name) {
- // skip this
- return ast.WalkContinue, nil
- }
-
- // FIXME: the "icon xxx" is from Fomantic UI, it's really questionable whether it still works correctly
- err := r.renderInternal.FormatWithSafeAttrs(w, `<i class="icon %s"></i>`, name)
+ n := node.(*RawHTML)
+ _, err := w.WriteString(string(r.renderInternal.ProtectSafeAttrs(n.rawHTML)))
if err != nil {
return ast.WalkStop, err
}
-
return ast.WalkContinue, nil
}
diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go
index b5fffccdb9..3b788432ba 100644
--- a/modules/markup/markdown/markdown.go
+++ b/modules/markup/markdown/markdown.go
@@ -5,7 +5,7 @@
package markdown
import (
- "fmt"
+ "errors"
"html/template"
"io"
"strings"
@@ -48,7 +48,7 @@ func (l *limitWriter) Write(data []byte) (int, error) {
if err != nil {
return n, err
}
- return n, fmt.Errorf("rendered content too large - truncating render")
+ return n, errors.New("rendered content too large - truncating render")
}
n, err := l.w.Write(data)
l.sum += int64(n)
@@ -86,20 +86,15 @@ func (r *GlodmarkRender) highlightingRenderer(w util.BufWriter, c highlighting.C
preClasses += " is-loading"
}
- err := r.ctx.RenderInternal.FormatWithSafeAttrs(w, `<pre class="%s">`, preClasses)
- if err != nil {
- return
- }
-
// include language-x class as part of commonmark spec, "chroma" class is used to highlight the code
// the "display" class is used by "js/markup/math.ts" to render the code element as a block
// the "math.ts" strictly depends on the structure: <pre class="code-block is-loading"><code class="language-math display">...</code></pre>
- err = r.ctx.RenderInternal.FormatWithSafeAttrs(w, `<code class="chroma language-%s display">`, languageStr)
+ err := r.ctx.RenderInternal.FormatWithSafeAttrs(w, `<div class="code-block-container code-overflow-scroll"><pre class="%s"><code class="chroma language-%s display">`, preClasses, languageStr)
if err != nil {
return
}
} else {
- _, err := w.WriteString("</code></pre>")
+ _, err := w.WriteString("</code></pre></div>")
if err != nil {
return
}
@@ -126,11 +121,11 @@ func SpecializedMarkdown(ctx *markup.RenderContext) *GlodmarkRender {
highlighting.WithWrapperRenderer(r.highlightingRenderer),
),
math.NewExtension(&ctx.RenderInternal, math.Options{
- Enabled: setting.Markdown.EnableMath,
- ParseDollarInline: true,
- ParseDollarBlock: true,
- ParseSquareBlock: true, // TODO: this is a bad syntax "\[ ... \]", it conflicts with normal markdown escaping, it should be deprecated in the future (by some config options)
- // ParseBracketInline: true, // TODO: this is also a bad syntax "\( ... \)", it also conflicts, it should be deprecated in the future
+ Enabled: setting.Markdown.EnableMath,
+ ParseInlineDollar: setting.Markdown.MathCodeBlockOptions.ParseInlineDollar,
+ ParseInlineParentheses: setting.Markdown.MathCodeBlockOptions.ParseInlineParentheses, // this is a bad syntax "\( ... \)", it conflicts with normal markdown escaping
+ ParseBlockDollar: setting.Markdown.MathCodeBlockOptions.ParseBlockDollar,
+ ParseBlockSquareBrackets: setting.Markdown.MathCodeBlockOptions.ParseBlockSquareBrackets, // this is a bad syntax "\[ ... \]", it conflicts with normal markdown escaping
}),
meta.Meta,
),
@@ -159,6 +154,14 @@ func render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error
limit: setting.UI.MaxDisplayFileSize * 3,
}
+ // FIXME: Don't read all to memory, but goldmark doesn't support
+ buf, err := io.ReadAll(input)
+ if err != nil {
+ log.Error("Unable to ReadAll: %v", err)
+ return err
+ }
+ buf = giteautil.NormalizeEOL(buf)
+
// FIXME: should we include a timeout to abort the renderer if it takes too long?
defer func() {
err := recover()
@@ -166,35 +169,20 @@ func render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error
return
}
- log.Warn("Unable to render markdown due to panic in goldmark: %v", err)
- if (!setting.IsProd && !setting.IsInTesting) || log.IsDebug() {
- log.Error("Panic in markdown: %v\n%s", err, log.Stack(2))
- }
+ log.Error("Panic in markdown: %v\n%s", err, log.Stack(2))
+ escapedHTML := template.HTMLEscapeString(giteautil.UnsafeBytesToString(buf))
+ _, _ = output.Write(giteautil.UnsafeStringToBytes(escapedHTML))
}()
- // FIXME: Don't read all to memory, but goldmark doesn't support
pc := newParserContext(ctx)
- buf, err := io.ReadAll(input)
- if err != nil {
- log.Error("Unable to ReadAll: %v", err)
- return err
- }
- buf = giteautil.NormalizeEOL(buf)
// Preserve original length.
bufWithMetadataLength := len(buf)
- rc := &RenderConfig{
- Meta: markup.RenderMetaAsDetails,
- Icon: "table",
- Lang: "",
- }
+ rc := &RenderConfig{Meta: markup.RenderMetaAsDetails}
buf, _ = ExtractMetadataBytes(buf, rc)
- metaLength := bufWithMetadataLength - len(buf)
- if metaLength < 0 {
- metaLength = 0
- }
+ metaLength := max(bufWithMetadataLength-len(buf), 0)
rc.metaLength = metaLength
pc.Set(renderConfigKey, rc)
diff --git a/modules/markup/markdown/markdown_attention_test.go b/modules/markup/markdown/markdown_attention_test.go
index f6ec775b2c..7b54653ec0 100644
--- a/modules/markup/markdown/markdown_attention_test.go
+++ b/modules/markup/markdown/markdown_attention_test.go
@@ -23,6 +23,11 @@ func TestAttention(t *testing.T) {
defer svg.MockIcon("octicon-alert")()
defer svg.MockIcon("octicon-stop")()
+ test := func(input, expected string) {
+ result, err := markdown.RenderString(markup.NewTestRenderContext(), input)
+ assert.NoError(t, err)
+ assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(result)))
+ }
renderAttention := func(attention, icon string) string {
tmpl := `<blockquote class="attention-header attention-{attention}"><p><svg class="attention-icon attention-{attention} svg {icon}" width="16" height="16"></svg><strong class="attention-{attention}">{Attention}</strong></p>`
tmpl = strings.ReplaceAll(tmpl, "{attention}", attention)
@@ -31,12 +36,6 @@ func TestAttention(t *testing.T) {
return tmpl
}
- test := func(input, expected string) {
- result, err := markdown.RenderString(markup.NewTestRenderContext(), input)
- assert.NoError(t, err)
- assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(result)))
- }
-
test(`
> [!NOTE]
> text
@@ -53,4 +52,7 @@ func TestAttention(t *testing.T) {
// legacy GitHub style
test(`> **warning**`, renderAttention("warning", "octicon-alert")+"\n</blockquote>")
+
+ // edge case (it used to cause panic)
+ test(">\ntext", "<blockquote>\n</blockquote>\n<p>text</p>")
}
diff --git a/modules/markup/markdown/markdown_benchmark_test.go b/modules/markup/markdown/markdown_benchmark_test.go
index 0f7e3eea6f..e08612f064 100644
--- a/modules/markup/markdown/markdown_benchmark_test.go
+++ b/modules/markup/markdown/markdown_benchmark_test.go
@@ -12,14 +12,14 @@ import (
func BenchmarkSpecializedMarkdown(b *testing.B) {
// 240856 4719 ns/op
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
markdown.SpecializedMarkdown(&markup.RenderContext{})
}
}
func BenchmarkMarkdownRender(b *testing.B) {
// 23202 50840 ns/op
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
_, _ = markdown.RenderString(markup.NewTestRenderContext(), "https://example.com\n- a\n- b\n")
}
}
diff --git a/modules/markup/markdown/markdown_math_test.go b/modules/markup/markdown/markdown_math_test.go
index 813f050965..a75f18d36a 100644
--- a/modules/markup/markdown/markdown_math_test.go
+++ b/modules/markup/markdown/markdown_math_test.go
@@ -8,6 +8,8 @@ import (
"testing"
"code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
@@ -15,6 +17,7 @@ import (
const nl = "\n"
func TestMathRender(t *testing.T) {
+ setting.Markdown.MathCodeBlockOptions = setting.MarkdownMathCodeBlockOptions{ParseInlineDollar: true, ParseInlineParentheses: true}
testcases := []struct {
testcase string
expected string
@@ -69,7 +72,7 @@ func TestMathRender(t *testing.T) {
},
{
"$$a$$",
- `<code class="language-math display">a</code>` + nl,
+ `<p><code class="language-math">a</code></p>` + nl,
},
{
"$$a$$ test",
@@ -111,6 +114,7 @@ func TestMathRender(t *testing.T) {
}
func TestMathRenderBlockIndent(t *testing.T) {
+ setting.Markdown.MathCodeBlockOptions = setting.MarkdownMathCodeBlockOptions{ParseBlockDollar: true, ParseBlockSquareBrackets: true}
testcases := []struct {
name string
testcase string
@@ -243,3 +247,64 @@ x
})
}
}
+
+func TestMathRenderOptions(t *testing.T) {
+ setting.Markdown.MathCodeBlockOptions = setting.MarkdownMathCodeBlockOptions{}
+ defer test.MockVariableValue(&setting.Markdown.MathCodeBlockOptions)
+ test := func(t *testing.T, expected, input string) {
+ res, err := RenderString(markup.NewTestRenderContext(), input)
+ assert.NoError(t, err)
+ assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(res)), "input: %s", input)
+ }
+
+ // default (non-conflict) inline syntax
+ test(t, `<p><code class="language-math">a</code></p>`, "$`a`$")
+
+ // ParseInlineDollar
+ test(t, `<p>$a$</p>`, `$a$`)
+ setting.Markdown.MathCodeBlockOptions.ParseInlineDollar = true
+ test(t, `<p><code class="language-math">a</code></p>`, `$a$`)
+
+ // ParseInlineParentheses
+ test(t, `<p>(a)</p>`, `\(a\)`)
+ setting.Markdown.MathCodeBlockOptions.ParseInlineParentheses = true
+ test(t, `<p><code class="language-math">a</code></p>`, `\(a\)`)
+
+ // ParseBlockDollar
+ test(t, `<p>$$
+a
+$$</p>
+`, `
+$$
+a
+$$
+`)
+ setting.Markdown.MathCodeBlockOptions.ParseBlockDollar = true
+ test(t, `<pre class="code-block is-loading"><code class="language-math display">
+a
+</code></pre>
+`, `
+$$
+a
+$$
+`)
+
+ // ParseBlockSquareBrackets
+ test(t, `<p>[
+a
+]</p>
+`, `
+\[
+a
+\]
+`)
+ setting.Markdown.MathCodeBlockOptions.ParseBlockSquareBrackets = true
+ test(t, `<pre class="code-block is-loading"><code class="language-math display">
+a
+</code></pre>
+`, `
+\[
+a
+\]
+`)
+}
diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go
index 7a09be8665..4eb01bcc2d 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -47,7 +47,7 @@ func TestRender_StandardLinks(t *testing.T) {
func TestRender_Images(t *testing.T) {
setting.AppURL = AppURL
- test := func(input, expected string) {
+ render := func(input, expected string) {
buffer, err := markdown.RenderString(markup.NewTestRenderContext(FullURL), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
@@ -59,27 +59,32 @@ func TestRender_Images(t *testing.T) {
result := util.URLJoin(FullURL, url)
// hint: With Markdown v2.5.2, there is a new syntax: [link](URL){:target="_blank"} , but we do not support it now
- test(
+ render(
"!["+title+"]("+url+")",
`<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`"/></a></p>`)
- test(
+ render(
"[["+title+"|"+url+"]]",
`<p><a href="`+result+`" rel="nofollow"><img src="`+result+`" title="`+title+`" alt="`+title+`"/></a></p>`)
- test(
+ render(
"[!["+title+"]("+url+")]("+href+")",
`<p><a href="`+href+`" rel="nofollow"><img src="`+result+`" alt="`+title+`"/></a></p>`)
- test(
+ render(
"!["+title+"]("+url+")",
`<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`"/></a></p>`)
- test(
+ render(
"[["+title+"|"+url+"]]",
`<p><a href="`+result+`" rel="nofollow"><img src="`+result+`" title="`+title+`" alt="`+title+`"/></a></p>`)
- test(
+ render(
"[!["+title+"]("+url+")]("+href+")",
`<p><a href="`+href+`" rel="nofollow"><img src="`+result+`" alt="`+title+`"/></a></p>`)
+
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, false)()
+ render(
+ "<a><img src='a.jpg'></a>", // by the way, empty "a" tag will be removed
+ `<p dir="auto"><img src="http://localhost:3000/user13/repo11/a.jpg" loading="lazy"/></p>`)
}
func TestTotal_RenderString(t *testing.T) {
@@ -223,7 +228,7 @@ This PR has been generated by [Renovate Bot](https://github.com/renovatebot/reno
<dd>This is another definition of the second term.</dd>
</dl>
<h3 id="user-content-footnotes">Footnotes</h3>
-<p>Here is a simple footnote,<sup id="fnref:user-content-1"><a href="#fn:user-content-1" rel="nofollow">1</a></sup> and here is a longer one.<sup id="fnref:user-content-bignote"><a href="#fn:user-content-bignote" rel="nofollow">2</a></sup></p>
+<p>Here is a simple footnote,<sup id="fnref:user-content-1"><a href="#fn:user-content-1" rel="nofollow">1 </a></sup> and here is a longer one.<sup id="fnref:user-content-bignote"><a href="#fn:user-content-bignote" rel="nofollow">2 </a></sup></p>
<div>
<hr/>
<ol>
@@ -252,7 +257,7 @@ This PR has been generated by [Renovate Bot](https://github.com/renovatebot/reno
return username == "r-lyeh"
},
})
- for i := 0; i < len(sameCases); i++ {
+ for i := range sameCases {
line, err := markdown.RenderString(markup.NewTestRenderContext(localMetas), sameCases[i])
assert.NoError(t, err)
assert.Equal(t, testAnswers[i], string(line))
@@ -308,12 +313,12 @@ func TestRenderSiblingImages_Issue12925(t *testing.T) {
testcase := `![image1](/image1)
![image2](/image2)
`
- expected := `<p><a href="/image1" target="_blank" rel="nofollow noopener"><img src="/image1" alt="image1"></a>
-<a href="/image2" target="_blank" rel="nofollow noopener"><img src="/image2" alt="image2"></a></p>
+ expected := `<p><a href="/image1" target="_blank" rel="nofollow noopener"><img src="/image1" alt="image1"/></a>
+<a href="/image2" target="_blank" rel="nofollow noopener"><img src="/image2" alt="image2"/></a></p>
`
- res, err := markdown.RenderRawString(markup.NewTestRenderContext(), testcase)
+ res, err := markdown.RenderString(markup.NewTestRenderContext(), testcase)
assert.NoError(t, err)
- assert.Equal(t, expected, res)
+ assert.Equal(t, expected, string(res))
}
func TestRenderEmojiInLinks_Issue12331(t *testing.T) {
@@ -383,18 +388,74 @@ func TestColorPreview(t *testing.T) {
}
}
-func TestTaskList(t *testing.T) {
+func TestMarkdownFrontmatter(t *testing.T) {
testcases := []struct {
- testcase string
+ name string
+ input string
expected string
}{
{
+ "MapInFrontmatter",
+ `---
+key1: val1
+key2: val2
+---
+test
+`,
+ `<details class="frontmatter-content"><summary><span>octicon-table(12/)</span> key1, key2</summary><table>
+<thead>
+<tr>
+<th>key1</th>
+<th>key2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>val1</td>
+<td>val2</td>
+</tr>
+</tbody>
+</table>
+</details><p>test</p>
+`,
+ },
+
+ {
+ "ListInFrontmatter",
+ `---
+- item1
+- item2
+---
+test
+`,
+ `- item1
+- item2
+
+<p>test</p>
+`,
+ },
+
+ {
+ "StringInFrontmatter",
+ `---
+anything
+---
+test
+`,
+ `anything
+
+<p>test</p>
+`,
+ },
+
+ {
// data-source-position should take into account YAML frontmatter.
+ "ListAfterFrontmatter",
`---
foo: bar
---
- [ ] task 1`,
- `<details><summary><i class="icon table"></i></summary><table>
+ `<details class="frontmatter-content"><summary><span>octicon-table(12/)</span> foo</summary><table>
<thead>
<tr>
<th>foo</th>
@@ -414,9 +475,9 @@ foo: bar
}
for _, test := range testcases {
- res, err := markdown.RenderString(markup.NewTestRenderContext(), test.testcase)
- assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
- assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
+ res, err := markdown.RenderString(markup.NewTestRenderContext(), test.input)
+ assert.NoError(t, err, "Unexpected error in testcase: %q", test.name)
+ assert.Equal(t, test.expected, string(res), "Unexpected result in testcase %q", test.name)
}
}
@@ -473,3 +534,16 @@ space</p>
assert.NoError(t, err)
assert.Equal(t, expected, string(result))
}
+
+func TestMarkdownLink(t *testing.T) {
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
+ input := `<a href=foo>link1</a>
+<a href='/foo'>link2</a>
+<a href="#foo">link3</a>`
+ result, err := markdown.RenderString(markup.NewTestRenderContext("/base", localMetas), input)
+ assert.NoError(t, err)
+ assert.Equal(t, `<p><a href="/base/foo" rel="nofollow">link1</a>
+<a href="/base/foo" rel="nofollow">link2</a>
+<a href="#user-content-foo" rel="nofollow">link3</a></p>
+`, string(result))
+}
diff --git a/modules/markup/markdown/math/block_renderer.go b/modules/markup/markdown/math/block_renderer.go
index c29f061882..95a336a02c 100644
--- a/modules/markup/markdown/math/block_renderer.go
+++ b/modules/markup/markdown/math/block_renderer.go
@@ -4,6 +4,8 @@
package math
import (
+ "html/template"
+
"code.gitea.io/gitea/modules/markup/internal"
giteaUtil "code.gitea.io/gitea/modules/util"
@@ -40,7 +42,7 @@ func (r *BlockRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
func (r *BlockRenderer) writeLines(w util.BufWriter, source []byte, n gast.Node) {
l := n.Lines().Len()
- for i := 0; i < l; i++ {
+ for i := range l {
line := n.Lines().At(i)
_, _ = w.Write(util.EscapeHTML(line.Value(source)))
}
@@ -49,8 +51,8 @@ func (r *BlockRenderer) writeLines(w util.BufWriter, source []byte, n gast.Node)
func (r *BlockRenderer) renderBlock(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
n := node.(*Block)
if entering {
- code := giteaUtil.Iif(n.Inline, "", `<pre class="code-block is-loading">`) + `<code class="language-math display">`
- _ = r.renderInternal.FormatWithSafeAttrs(w, code)
+ codeHTML := giteaUtil.Iif[template.HTML](n.Inline, "", `<pre class="code-block is-loading">`) + `<code class="language-math display">`
+ _, _ = w.WriteString(string(r.renderInternal.ProtectSafeAttrs(codeHTML)))
r.writeLines(w, source, n)
} else {
_, _ = w.WriteString(`</code>` + giteaUtil.Iif(n.Inline, "", `</pre>`) + "\n")
diff --git a/modules/markup/markdown/math/inline_parser.go b/modules/markup/markdown/math/inline_parser.go
index a57abe9f9b..a711d1e1cd 100644
--- a/modules/markup/markdown/math/inline_parser.go
+++ b/modules/markup/markdown/math/inline_parser.go
@@ -15,26 +15,26 @@ type inlineParser struct {
trigger []byte
endBytesSingleDollar []byte
endBytesDoubleDollar []byte
- endBytesBracket []byte
+ endBytesParentheses []byte
+ enableInlineDollar bool
}
-var defaultInlineDollarParser = &inlineParser{
- trigger: []byte{'$'},
- endBytesSingleDollar: []byte{'$'},
- endBytesDoubleDollar: []byte{'$', '$'},
-}
-
-func NewInlineDollarParser() parser.InlineParser {
- return defaultInlineDollarParser
+func NewInlineDollarParser(enableInlineDollar bool) parser.InlineParser {
+ return &inlineParser{
+ trigger: []byte{'$'},
+ endBytesSingleDollar: []byte{'$'},
+ endBytesDoubleDollar: []byte{'$', '$'},
+ enableInlineDollar: enableInlineDollar,
+ }
}
-var defaultInlineBracketParser = &inlineParser{
- trigger: []byte{'\\', '('},
- endBytesBracket: []byte{'\\', ')'},
+var defaultInlineParenthesesParser = &inlineParser{
+ trigger: []byte{'\\', '('},
+ endBytesParentheses: []byte{'\\', ')'},
}
-func NewInlineBracketParser() parser.InlineParser {
- return defaultInlineBracketParser
+func NewInlineParenthesesParser() parser.InlineParser {
+ return defaultInlineParenthesesParser
}
// Trigger triggers this parser on $ or \
@@ -46,7 +46,7 @@ func isPunctuation(b byte) bool {
return b == '.' || b == '!' || b == '?' || b == ',' || b == ';' || b == ':'
}
-func isBracket(b byte) bool {
+func isParenthesesClose(b byte) bool {
return b == ')'
}
@@ -70,10 +70,11 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
startMarkLen = 1
stopMark = parser.endBytesSingleDollar
if len(line) > 1 {
- if line[1] == '$' {
+ switch line[1] {
+ case '$':
startMarkLen = 2
stopMark = parser.endBytesDoubleDollar
- } else if line[1] == '`' {
+ case '`':
pos := 1
for ; pos < len(line) && line[pos] == '`'; pos++ {
}
@@ -85,7 +86,11 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
}
} else {
startMarkLen = 2
- stopMark = parser.endBytesBracket
+ stopMark = parser.endBytesParentheses
+ }
+
+ if line[0] == '$' && !parser.enableInlineDollar && (len(line) == 1 || line[1] != '`') {
+ return nil
}
if checkSurrounding {
@@ -109,7 +114,7 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
succeedingCharacter = line[i+len(stopMark)]
}
// check valid ending character
- isValidEndingChar := isPunctuation(succeedingCharacter) || isBracket(succeedingCharacter) ||
+ isValidEndingChar := isPunctuation(succeedingCharacter) || isParenthesesClose(succeedingCharacter) ||
succeedingCharacter == ' ' || succeedingCharacter == '\n' || succeedingCharacter == 0
if checkSurrounding && !isValidEndingChar {
break
@@ -121,9 +126,10 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.
i++
continue
}
- if line[i] == '{' {
+ switch line[i] {
+ case '{':
depth++
- } else if line[i] == '}' {
+ case '}':
depth--
}
}
diff --git a/modules/markup/markdown/math/inline_renderer.go b/modules/markup/markdown/math/inline_renderer.go
index d000a7b317..eeeb60cc7e 100644
--- a/modules/markup/markdown/math/inline_renderer.go
+++ b/modules/markup/markdown/math/inline_renderer.go
@@ -28,7 +28,7 @@ func NewInlineRenderer(renderInternal *internal.RenderInternal) renderer.NodeRen
func (r *InlineRenderer) renderInline(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
- _ = r.renderInternal.FormatWithSafeAttrs(w, `<code class="language-math">`)
+ _, _ = w.WriteString(string(r.renderInternal.ProtectSafeAttrs(`<code class="language-math">`)))
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
segment := c.(*ast.Text).Segment
value := util.EscapeHTML(segment.Value(source))
diff --git a/modules/markup/markdown/math/math.go b/modules/markup/markdown/math/math.go
index a6ff593d62..4b74db2d76 100644
--- a/modules/markup/markdown/math/math.go
+++ b/modules/markup/markdown/math/math.go
@@ -14,10 +14,11 @@ import (
)
type Options struct {
- Enabled bool
- ParseDollarInline bool
- ParseDollarBlock bool
- ParseSquareBlock bool
+ Enabled bool
+ ParseInlineDollar bool // inline $$ xxx $$ text
+ ParseInlineParentheses bool // inline \( xxx \) text
+ ParseBlockDollar bool // block $$ multiple-line $$ text
+ ParseBlockSquareBrackets bool // block \[ multiple-line \] text
}
// Extension is a math extension
@@ -42,16 +43,16 @@ func (e *Extension) Extend(m goldmark.Markdown) {
return
}
- inlines := []util.PrioritizedValue{util.Prioritized(NewInlineBracketParser(), 501)}
- if e.options.ParseDollarInline {
- inlines = append(inlines, util.Prioritized(NewInlineDollarParser(), 502))
+ var inlines []util.PrioritizedValue
+ if e.options.ParseInlineParentheses {
+ inlines = append(inlines, util.Prioritized(NewInlineParenthesesParser(), 501))
}
- m.Parser().AddOptions(parser.WithInlineParsers(inlines...))
+ inlines = append(inlines, util.Prioritized(NewInlineDollarParser(e.options.ParseInlineDollar), 502))
+ m.Parser().AddOptions(parser.WithInlineParsers(inlines...))
m.Parser().AddOptions(parser.WithBlockParsers(
- util.Prioritized(NewBlockParser(e.options.ParseDollarBlock, e.options.ParseSquareBlock), 701),
+ util.Prioritized(NewBlockParser(e.options.ParseBlockDollar, e.options.ParseBlockSquareBrackets), 701),
))
-
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewBlockRenderer(e.renderInternal), 501),
util.Prioritized(NewInlineRenderer(e.renderInternal), 502),
diff --git a/modules/markup/markdown/meta_test.go b/modules/markup/markdown/meta_test.go
index 278c33f1d2..283d289d48 100644
--- a/modules/markup/markdown/meta_test.go
+++ b/modules/markup/markdown/meta_test.go
@@ -51,7 +51,7 @@ func TestExtractMetadata(t *testing.T) {
var meta IssueTemplate
body, err := ExtractMetadata(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, sepTest), &meta)
assert.NoError(t, err)
- assert.Equal(t, "", body)
+ assert.Empty(t, body)
assert.Equal(t, metaTest, meta)
assert.True(t, meta.Valid())
})
@@ -60,7 +60,7 @@ func TestExtractMetadata(t *testing.T) {
func TestExtractMetadataBytes(t *testing.T) {
t.Run("ValidFrontAndBody", func(t *testing.T) {
var meta IssueTemplate
- body, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s\n%s", sepTest, frontTest, sepTest, bodyTest)), &meta)
+ body, err := ExtractMetadataBytes(fmt.Appendf(nil, "%s\n%s\n%s\n%s", sepTest, frontTest, sepTest, bodyTest), &meta)
assert.NoError(t, err)
assert.Equal(t, bodyTest, string(body))
assert.Equal(t, metaTest, meta)
@@ -69,21 +69,21 @@ func TestExtractMetadataBytes(t *testing.T) {
t.Run("NoFirstSeparator", func(t *testing.T) {
var meta IssueTemplate
- _, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", frontTest, sepTest, bodyTest)), &meta)
+ _, err := ExtractMetadataBytes(fmt.Appendf(nil, "%s\n%s\n%s", frontTest, sepTest, bodyTest), &meta)
assert.Error(t, err)
})
t.Run("NoLastSeparator", func(t *testing.T) {
var meta IssueTemplate
- _, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, bodyTest)), &meta)
+ _, err := ExtractMetadataBytes(fmt.Appendf(nil, "%s\n%s\n%s", sepTest, frontTest, bodyTest), &meta)
assert.Error(t, err)
})
t.Run("NoBody", func(t *testing.T) {
var meta IssueTemplate
- body, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, sepTest)), &meta)
+ body, err := ExtractMetadataBytes(fmt.Appendf(nil, "%s\n%s\n%s", sepTest, frontTest, sepTest), &meta)
assert.NoError(t, err)
- assert.Equal(t, "", string(body))
+ assert.Empty(t, string(body))
assert.Equal(t, metaTest, meta)
assert.True(t, meta.Valid())
})
diff --git a/modules/markup/markdown/renderconfig.go b/modules/markup/markdown/renderconfig.go
index f4c48d1b3d..d8b1b10ce6 100644
--- a/modules/markup/markdown/renderconfig.go
+++ b/modules/markup/markdown/renderconfig.go
@@ -16,7 +16,6 @@ import (
// RenderConfig represents rendering configuration for this file
type RenderConfig struct {
Meta markup.RenderMetaMode
- Icon string
TOC string // "false": hide, "side"/empty: in sidebar, "main"/"true": in main view
Lang string
yamlNode *yaml.Node
@@ -74,7 +73,7 @@ func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error {
type yamlRenderConfig struct {
Meta *string `yaml:"meta"`
- Icon *string `yaml:"details_icon"`
+ Icon *string `yaml:"details_icon"` // deprecated, because there is no font icon, so no custom icon
TOC *string `yaml:"include_toc"`
Lang *string `yaml:"lang"`
}
@@ -96,10 +95,6 @@ func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error {
rc.Meta = renderMetaModeFromString(*cfg.Gitea.Meta)
}
- if cfg.Gitea.Icon != nil {
- rc.Icon = strings.TrimSpace(strings.ToLower(*cfg.Gitea.Icon))
- }
-
if cfg.Gitea.Lang != nil && *cfg.Gitea.Lang != "" {
rc.Lang = *cfg.Gitea.Lang
}
@@ -111,7 +106,7 @@ func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error {
return nil
}
-func (rc *RenderConfig) toMetaNode() ast.Node {
+func (rc *RenderConfig) toMetaNode(g *ASTTransformer) ast.Node {
if rc.yamlNode == nil {
return nil
}
@@ -119,7 +114,7 @@ func (rc *RenderConfig) toMetaNode() ast.Node {
case markup.RenderMetaAsTable:
return nodeToTable(rc.yamlNode)
case markup.RenderMetaAsDetails:
- return nodeToDetails(rc.yamlNode, rc.Icon)
+ return nodeToDetails(g, rc.yamlNode)
default:
return nil
}
diff --git a/modules/markup/markdown/renderconfig_test.go b/modules/markup/markdown/renderconfig_test.go
index c53acdc77a..53c52177a7 100644
--- a/modules/markup/markdown/renderconfig_test.go
+++ b/modules/markup/markdown/renderconfig_test.go
@@ -7,6 +7,8 @@ import (
"strings"
"testing"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)
@@ -19,42 +21,36 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
{
"empty", &RenderConfig{
Meta: "table",
- Icon: "table",
Lang: "",
}, "",
},
{
"lang", &RenderConfig{
Meta: "table",
- Icon: "table",
Lang: "test",
}, "lang: test",
},
{
"metatable", &RenderConfig{
Meta: "table",
- Icon: "table",
Lang: "",
}, "gitea: table",
},
{
"metanone", &RenderConfig{
Meta: "none",
- Icon: "table",
Lang: "",
}, "gitea: none",
},
{
"metadetails", &RenderConfig{
Meta: "details",
- Icon: "table",
Lang: "",
}, "gitea: details",
},
{
"metawrong", &RenderConfig{
Meta: "details",
- Icon: "table",
Lang: "",
}, "gitea: wrong",
},
@@ -62,7 +58,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
"toc", &RenderConfig{
TOC: "true",
Meta: "table",
- Icon: "table",
Lang: "",
}, "include_toc: true",
},
@@ -70,14 +65,12 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
"tocfalse", &RenderConfig{
TOC: "false",
Meta: "table",
- Icon: "table",
Lang: "",
}, "include_toc: false",
},
{
"toclang", &RenderConfig{
Meta: "table",
- Icon: "table",
TOC: "true",
Lang: "testlang",
}, `
@@ -88,7 +81,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
{
"complexlang", &RenderConfig{
Meta: "table",
- Icon: "table",
Lang: "testlang",
}, `
gitea:
@@ -98,7 +90,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
{
"complexlang2", &RenderConfig{
Meta: "table",
- Icon: "table",
Lang: "testlang",
}, `
lang: notright
@@ -109,7 +100,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
{
"complexlang", &RenderConfig{
Meta: "table",
- Icon: "table",
Lang: "testlang",
}, `
gitea:
@@ -121,7 +111,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
Lang: "two",
Meta: "table",
TOC: "true",
- Icon: "smiley",
}, `
lang: one
include_toc: true
@@ -137,26 +126,14 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
got := &RenderConfig{
Meta: "table",
- Icon: "table",
Lang: "",
}
- if err := yaml.Unmarshal([]byte(strings.ReplaceAll(tt.args, "\t", " ")), got); err != nil {
- t.Errorf("RenderConfig.UnmarshalYAML() error = %v\n%q", err, tt.args)
- return
- }
+ err := yaml.Unmarshal([]byte(strings.ReplaceAll(tt.args, "\t", " ")), got)
+ require.NoError(t, err)
- if got.Meta != tt.expected.Meta {
- t.Errorf("Meta Expected %s Got %s", tt.expected.Meta, got.Meta)
- }
- if got.Icon != tt.expected.Icon {
- t.Errorf("Icon Expected %s Got %s", tt.expected.Icon, got.Icon)
- }
- if got.Lang != tt.expected.Lang {
- t.Errorf("Lang Expected %s Got %s", tt.expected.Lang, got.Lang)
- }
- if got.TOC != tt.expected.TOC {
- t.Errorf("TOC Expected %q Got %q", tt.expected.TOC, got.TOC)
- }
+ assert.Equal(t, tt.expected.Meta, got.Meta)
+ assert.Equal(t, tt.expected.Lang, got.Lang)
+ assert.Equal(t, tt.expected.TOC, got.TOC)
})
}
}
diff --git a/modules/markup/markdown/toc.go b/modules/markup/markdown/toc.go
index ea1af83a3e..a11b9d0390 100644
--- a/modules/markup/markdown/toc.go
+++ b/modules/markup/markdown/toc.go
@@ -4,7 +4,6 @@
package markdown
import (
- "fmt"
"net/url"
"code.gitea.io/gitea/modules/translation"
@@ -50,7 +49,7 @@ func createTOCNode(toc []Header, lang string, detailsAttrs map[string]string) as
}
li := ast.NewListItem(currentLevel * 2)
a := ast.NewLink()
- a.Destination = []byte(fmt.Sprintf("#%s", url.QueryEscape(header.ID)))
+ a.Destination = []byte("#" + url.QueryEscape(header.ID))
a.AppendChild(a, ast.NewString([]byte(header.Text)))
li.AppendChild(li, a)
ul.AppendChild(ul, li)
diff --git a/modules/markup/markdown/transform_blockquote.go b/modules/markup/markdown/transform_blockquote.go
index 2651d44a69..bf17f01681 100644
--- a/modules/markup/markdown/transform_blockquote.go
+++ b/modules/markup/markdown/transform_blockquote.go
@@ -46,7 +46,7 @@ func (g *ASTTransformer) extractBlockquoteAttentionEmphasis(firstParagraph ast.N
if !ok {
return "", nil
}
- val1 := string(node1.Text(reader.Source())) //nolint:staticcheck
+ val1 := string(node1.Text(reader.Source())) //nolint:staticcheck // Text is deprecated
attentionType := strings.ToLower(val1)
if g.attentionTypes.Contains(attentionType) {
return attentionType, []ast.Node{node1}
@@ -115,6 +115,9 @@ func (g *ASTTransformer) transformBlockquote(v *ast.Blockquote, reader text.Read
// grab these nodes and make sure we adhere to the attention blockquote structure
firstParagraph := v.FirstChild()
+ if firstParagraph == nil {
+ return ast.WalkContinue, nil
+ }
g.applyElementDir(firstParagraph)
attentionType, processedNodes := g.extractBlockquoteAttentionEmphasis(firstParagraph, reader)
diff --git a/modules/markup/markdown/transform_codespan.go b/modules/markup/markdown/transform_codespan.go
index bccc43aad2..c2e4295bc2 100644
--- a/modules/markup/markdown/transform_codespan.go
+++ b/modules/markup/markdown/transform_codespan.go
@@ -68,7 +68,7 @@ func cssColorHandler(value string) bool {
}
func (g *ASTTransformer) transformCodeSpan(_ *markup.RenderContext, v *ast.CodeSpan, reader text.Reader) {
- colorContent := v.Text(reader.Source()) //nolint:staticcheck
+ colorContent := v.Text(reader.Source()) //nolint:staticcheck // Text is deprecated
if cssColorHandler(string(colorContent)) {
v.AppendChild(v, NewColorPreview(colorContent))
}
diff --git a/modules/markup/markdown/transform_heading.go b/modules/markup/markdown/transform_heading.go
index 5f8a12794d..a229a7b1a4 100644
--- a/modules/markup/markdown/transform_heading.go
+++ b/modules/markup/markdown/transform_heading.go
@@ -16,10 +16,10 @@ import (
func (g *ASTTransformer) transformHeading(_ *markup.RenderContext, v *ast.Heading, reader text.Reader, tocList *[]Header) {
for _, attr := range v.Attributes() {
if _, ok := attr.Value.([]byte); !ok {
- v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value)))
+ v.SetAttribute(attr.Name, fmt.Appendf(nil, "%v", attr.Value))
}
}
- txt := v.Text(reader.Source()) //nolint:staticcheck
+ txt := v.Text(reader.Source()) //nolint:staticcheck // Text is deprecated
header := Header{
Text: util.UnsafeBytesToString(txt),
Level: v.Level,
diff --git a/modules/markup/markdown/transform_image.go b/modules/markup/markdown/transform_image.go
deleted file mode 100644
index 36512e59a8..0000000000
--- a/modules/markup/markdown/transform_image.go
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2024 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package markdown
-
-import (
- "code.gitea.io/gitea/modules/markup"
-
- "github.com/yuin/goldmark/ast"
-)
-
-func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image) {
- // Images need two things:
- //
- // 1. Their src needs to munged to be a real value
- // 2. If they're not wrapped with a link they need a link wrapper
-
- // Check if the destination is a real link
- if len(v.Destination) > 0 && !markup.IsFullURLBytes(v.Destination) {
- v.Destination = []byte(ctx.RenderHelper.ResolveLink(string(v.Destination), markup.LinkTypeMedia))
- }
-
- parent := v.Parent()
- // Create a link around image only if parent is not already a link
- if _, ok := parent.(*ast.Link); !ok && parent != nil {
- next := v.NextSibling()
-
- // Create a link wrapper
- wrap := ast.NewLink()
- wrap.Destination = v.Destination
- wrap.Title = v.Title
- wrap.SetAttributeString("target", []byte("_blank"))
-
- // Duplicate the current image node
- image := ast.NewImage(ast.NewLink())
- image.Destination = v.Destination
- image.Title = v.Title
- for _, attr := range v.Attributes() {
- image.SetAttribute(attr.Name, attr.Value)
- }
- for child := v.FirstChild(); child != nil; {
- next := child.NextSibling()
- image.AppendChild(image, child)
- child = next
- }
-
- // Append our duplicate image to the wrapper link
- wrap.AppendChild(wrap, image)
-
- // Wire in the next sibling
- wrap.SetNextSibling(next)
-
- // Replace the current node with the wrapper link
- parent.ReplaceChild(parent, v, wrap)
-
- // But most importantly ensure the next sibling is still on the old image too
- v.SetNextSibling(next)
- }
-}
diff --git a/modules/markup/markdown/transform_link.go b/modules/markup/markdown/transform_link.go
deleted file mode 100644
index 51c2c915d8..0000000000
--- a/modules/markup/markdown/transform_link.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2024 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package markdown
-
-import (
- "code.gitea.io/gitea/modules/markup"
-
- "github.com/yuin/goldmark/ast"
-)
-
-func resolveLink(ctx *markup.RenderContext, link, userContentAnchorPrefix string) (result string, resolved bool) {
- isAnchorFragment := link != "" && link[0] == '#'
- if !isAnchorFragment && !markup.IsFullURLString(link) {
- link, resolved = ctx.RenderHelper.ResolveLink(link, markup.LinkTypeDefault), true
- }
- if isAnchorFragment && userContentAnchorPrefix != "" {
- link, resolved = userContentAnchorPrefix+link[1:], true
- }
- return link, resolved
-}
-
-func (g *ASTTransformer) transformLink(ctx *markup.RenderContext, v *ast.Link) {
- if link, resolved := resolveLink(ctx, string(v.Destination), "#user-content-"); resolved {
- v.Destination = []byte(link)
- }
-}
diff --git a/modules/markup/mdstripper/mdstripper.go b/modules/markup/mdstripper/mdstripper.go
index fe0eabb473..6e392444b4 100644
--- a/modules/markup/mdstripper/mdstripper.go
+++ b/modules/markup/mdstripper/mdstripper.go
@@ -46,7 +46,7 @@ func (r *stripRenderer) Render(w io.Writer, source []byte, doc ast.Node) error {
coalesce := prevSibIsText
r.processString(
w,
- v.Text(source), //nolint:staticcheck
+ v.Text(source), //nolint:staticcheck // Text is deprecated
coalesce)
if v.SoftLineBreak() {
r.doubleSpace(w)
@@ -107,11 +107,12 @@ func (r *stripRenderer) processAutoLink(w io.Writer, link []byte) {
}
var sep string
- if parts[3] == "issues" {
+ switch parts[3] {
+ case "issues":
sep = "#"
- } else if parts[3] == "pulls" {
+ case "pulls":
sep = "!"
- } else {
+ default:
// Process out of band
r.links = append(r.links, linkStr)
return
diff --git a/modules/markup/mdstripper/mdstripper_test.go b/modules/markup/mdstripper/mdstripper_test.go
index ea34df0a3b..7fb49c1e01 100644
--- a/modules/markup/mdstripper/mdstripper_test.go
+++ b/modules/markup/mdstripper/mdstripper_test.go
@@ -79,7 +79,7 @@ A HIDDEN ` + "`" + `GHOST` + "`" + ` IN THIS LINE.
lines = append(lines, line)
}
}
- assert.EqualValues(t, test.expectedText, lines)
- assert.EqualValues(t, test.expectedLinks, links)
+ assert.Equal(t, test.expectedText, lines)
+ assert.Equal(t, test.expectedLinks, links)
}
}
diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go
index c6cc334000..93c335d244 100644
--- a/modules/markup/orgmode/orgmode.go
+++ b/modules/markup/orgmode/orgmode.go
@@ -1,7 +1,7 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package markup
+package orgmode
import (
"fmt"
@@ -125,29 +125,15 @@ type orgWriter struct {
var _ org.Writer = (*orgWriter)(nil)
-func (r *orgWriter) resolveLink(kind, link string) string {
- link = strings.TrimPrefix(link, "file:")
- if !strings.HasPrefix(link, "#") && // not a URL fragment
- !markup.IsFullURLString(link) {
- if kind == "regular" {
- // orgmode reports the link kind as "regular" for "[[ImageLink.svg][The Image Desc]]"
- // so we need to try to guess the link kind again here
- kind = org.RegularLink{URL: link}.Kind()
- }
- if kind == "image" || kind == "video" {
- link = r.rctx.RenderHelper.ResolveLink(link, markup.LinkTypeMedia)
- } else {
- link = r.rctx.RenderHelper.ResolveLink(link, markup.LinkTypeDefault)
- }
- }
- return link
+func (r *orgWriter) resolveLink(link string) string {
+ return strings.TrimPrefix(link, "file:")
}
// WriteRegularLink renders images, links or videos
func (r *orgWriter) WriteRegularLink(l org.RegularLink) {
- link := r.resolveLink(l.Kind(), l.URL)
+ link := r.resolveLink(l.URL)
- printHTML := func(html string, a ...any) {
+ printHTML := func(html template.HTML, a ...any) {
_, _ = fmt.Fprint(r, htmlutil.HTMLFormat(html, a...))
}
// Inspired by https://github.com/niklasfasching/go-org/blob/6eb20dbda93cb88c3503f7508dc78cbbc639378f/org/html_writer.go#L406-L427
@@ -156,14 +142,14 @@ func (r *orgWriter) WriteRegularLink(l org.RegularLink) {
if l.Description == nil {
printHTML(`<img src="%s" alt="%s">`, link, link)
} else {
- imageSrc := r.resolveLink(l.Kind(), org.String(l.Description...))
+ imageSrc := r.resolveLink(org.String(l.Description...))
printHTML(`<a href="%s"><img src="%s" alt="%s"></a>`, link, imageSrc, imageSrc)
}
case "video":
if l.Description == nil {
printHTML(`<video src="%s">%s</video>`, link, link)
} else {
- videoSrc := r.resolveLink(l.Kind(), org.String(l.Description...))
+ videoSrc := r.resolveLink(org.String(l.Description...))
printHTML(`<a href="%s"><video src="%s">%s</video></a>`, link, videoSrc, videoSrc)
}
default:
diff --git a/modules/markup/orgmode/orgmode_test.go b/modules/markup/orgmode/orgmode_test.go
index e3cc05b4f0..df4bb38ad1 100644
--- a/modules/markup/orgmode/orgmode_test.go
+++ b/modules/markup/orgmode/orgmode_test.go
@@ -1,7 +1,7 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package markup
+package orgmode_test
import (
"os"
@@ -9,6 +9,7 @@ import (
"testing"
"code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/modules/markup/orgmode"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
@@ -22,7 +23,7 @@ func TestMain(m *testing.M) {
func TestRender_StandardLinks(t *testing.T) {
test := func(input, expected string) {
- buffer, err := RenderString(markup.NewTestRenderContext("/relative-path/media/branch/main/"), input)
+ buffer, err := orgmode.RenderString(markup.NewTestRenderContext(), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
@@ -30,37 +31,37 @@ func TestRender_StandardLinks(t *testing.T) {
test("[[https://google.com/]]",
`<p><a href="https://google.com/">https://google.com/</a></p>`)
test("[[ImageLink.svg][The Image Desc]]",
- `<p><a href="/relative-path/media/branch/main/ImageLink.svg">The Image Desc</a></p>`)
+ `<p><a href="ImageLink.svg">The Image Desc</a></p>`)
}
func TestRender_InternalLinks(t *testing.T) {
test := func(input, expected string) {
- buffer, err := RenderString(markup.NewTestRenderContext("/relative-path/src/branch/main"), input)
+ buffer, err := orgmode.RenderString(markup.NewTestRenderContext(), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
test("[[file:test.org][Test]]",
- `<p><a href="/relative-path/src/branch/main/test.org">Test</a></p>`)
+ `<p><a href="test.org">Test</a></p>`)
test("[[./test.org][Test]]",
- `<p><a href="/relative-path/src/branch/main/test.org">Test</a></p>`)
+ `<p><a href="./test.org">Test</a></p>`)
test("[[test.org][Test]]",
- `<p><a href="/relative-path/src/branch/main/test.org">Test</a></p>`)
+ `<p><a href="test.org">Test</a></p>`)
test("[[path/to/test.org][Test]]",
- `<p><a href="/relative-path/src/branch/main/path/to/test.org">Test</a></p>`)
+ `<p><a href="path/to/test.org">Test</a></p>`)
}
func TestRender_Media(t *testing.T) {
test := func(input, expected string) {
- buffer, err := RenderString(markup.NewTestRenderContext("./relative-path"), input)
+ buffer, err := orgmode.RenderString(markup.NewTestRenderContext(), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
test("[[file:../../.images/src/02/train.jpg]]",
- `<p><img src=".images/src/02/train.jpg" alt=".images/src/02/train.jpg"></p>`)
+ `<p><img src="../../.images/src/02/train.jpg" alt="../../.images/src/02/train.jpg"></p>`)
test("[[file:train.jpg]]",
- `<p><img src="relative-path/train.jpg" alt="relative-path/train.jpg"></p>`)
+ `<p><img src="train.jpg" alt="train.jpg"></p>`)
// With description.
test("[[https://example.com][https://example.com/example.svg]]",
@@ -91,7 +92,7 @@ func TestRender_Media(t *testing.T) {
func TestRender_Source(t *testing.T) {
test := func(input, expected string) {
- buffer, err := RenderString(markup.NewTestRenderContext(), input)
+ buffer, err := orgmode.RenderString(markup.NewTestRenderContext(), input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
@@ -103,8 +104,8 @@ func HelloWorld() {
}
#+end_src
`, `<div class="src src-go">
-<pre><code class="chroma language-go"><span class="c1">// HelloWorld prints &#34;Hello World&#34;
-</span><span class="c1"></span><span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span>
+<pre><code class="chroma language-go"><span class="c1">// HelloWorld prints &#34;Hello World&#34;</span>
+<span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Hello World&#34;</span><span class="p">)</span>
<span class="p">}</span></code></pre>
</div>`)
diff --git a/modules/markup/render.go b/modules/markup/render.go
index b239e59687..79f1f473c2 100644
--- a/modules/markup/render.go
+++ b/modules/markup/render.go
@@ -8,6 +8,7 @@ import (
"fmt"
"io"
"net/url"
+ "strconv"
"strings"
"time"
@@ -44,9 +45,9 @@ type RenderOptions struct {
MarkupType string
// user&repo, format&style&regexp (for external issue pattern), teams&org (for mention)
- // BranchNameSubURL (for iframe&asciicast)
+ // RefTypeNameSubURL (for iframe&asciicast)
// markupAllowShortIssuePattern
- // markdownLineBreakStyle (comment, document)
+ // markdownNewLineHardBreak
Metas map[string]string
// used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page
@@ -170,7 +171,7 @@ sandbox="allow-scripts"
setting.AppSubURL,
url.PathEscape(ctx.RenderOptions.Metas["user"]),
url.PathEscape(ctx.RenderOptions.Metas["repo"]),
- ctx.RenderOptions.Metas["BranchNameSubURL"],
+ ctx.RenderOptions.Metas["RefTypeNameSubURL"],
url.PathEscape(ctx.RenderOptions.RelativePath),
))
return err
@@ -247,7 +248,8 @@ func Init(renderHelpFuncs *RenderHelperFuncs) {
}
func ComposeSimpleDocumentMetas() map[string]string {
- return map[string]string{"markdownLineBreakStyle": "document"}
+ // TODO: there is no separate config option for "simple document" rendering, so temporarily use the same config as "repo file"
+ return map[string]string{"markdownNewLineHardBreak": strconv.FormatBool(setting.Markdown.RenderOptionsRepoFile.NewLineHardBreak)}
}
type TestRenderHelper struct {
@@ -261,8 +263,14 @@ func (r *TestRenderHelper) IsCommitIDExisting(commitID string) bool {
return strings.HasPrefix(commitID, "65f1bf2") //|| strings.HasPrefix(commitID, "88fc37a")
}
-func (r *TestRenderHelper) ResolveLink(link string, likeType LinkType) string {
- return r.ctx.ResolveLinkRelative(r.BaseLink, "", link)
+func (r *TestRenderHelper) ResolveLink(link, preferLinkType string) string {
+ linkType, link := ParseRenderedLink(link, preferLinkType)
+ switch linkType {
+ case LinkTypeRoot:
+ return r.ctx.ResolveLinkRoot(link)
+ default:
+ return r.ctx.ResolveLinkRelative(r.BaseLink, "", link)
+ }
}
var _ RenderHelper = (*TestRenderHelper)(nil)
diff --git a/modules/markup/render_helper.go b/modules/markup/render_helper.go
index 8ff0e7d6fb..b16f1189c5 100644
--- a/modules/markup/render_helper.go
+++ b/modules/markup/render_helper.go
@@ -10,13 +10,11 @@ import (
"code.gitea.io/gitea/modules/setting"
)
-type LinkType string
-
const (
- LinkTypeApp LinkType = "app" // the link is relative to the AppSubURL
- LinkTypeDefault LinkType = "default" // the link is relative to the default base (eg: repo link, or current ref tree path)
- LinkTypeMedia LinkType = "media" // the link should be used to access media files (images, videos)
- LinkTypeRaw LinkType = "raw" // not really useful, mainly for environment GITEA_PREFIX_RAW for external renders
+ LinkTypeDefault = ""
+ LinkTypeRoot = "/:root" // the link is relative to the AppSubURL(ROOT_URL)
+ LinkTypeMedia = "/:media" // the link should be used to access media files (images, videos)
+ LinkTypeRaw = "/:raw" // not really useful, mainly for environment GITEA_PREFIX_RAW for external renders
)
type RenderHelper interface {
@@ -27,7 +25,7 @@ type RenderHelper interface {
// but not make processors to guess "is it rendering a comment or a wiki?" or "does it need to check commit ID?"
IsCommitIDExisting(commitID string) bool
- ResolveLink(link string, likeType LinkType) string
+ ResolveLink(link, preferLinkType string) string
}
// RenderHelperFuncs is used to decouple cycle-import
@@ -51,7 +49,8 @@ func (r *SimpleRenderHelper) IsCommitIDExisting(commitID string) bool {
return false
}
-func (r *SimpleRenderHelper) ResolveLink(link string, likeType LinkType) string {
+func (r *SimpleRenderHelper) ResolveLink(link, preferLinkType string) string {
+ _, link = ParseRenderedLink(link, preferLinkType)
return resolveLinkRelative(context.Background(), setting.AppSubURL+"/", "", link, false)
}
diff --git a/modules/markup/render_link.go b/modules/markup/render_link.go
index b2e0699681..046544ce81 100644
--- a/modules/markup/render_link.go
+++ b/modules/markup/render_link.go
@@ -33,10 +33,24 @@ func resolveLinkRelative(ctx context.Context, base, cur, link string, absolute b
return finalLink
}
-func (ctx *RenderContext) ResolveLinkRelative(base, cur, link string) (finalLink string) {
+func (ctx *RenderContext) ResolveLinkRelative(base, cur, link string) string {
+ if strings.HasPrefix(link, "/:") {
+ setting.PanicInDevOrTesting("invalid link %q, forgot to cut?", link)
+ }
return resolveLinkRelative(ctx, base, cur, link, ctx.RenderOptions.UseAbsoluteLink)
}
-func (ctx *RenderContext) ResolveLinkApp(link string) string {
+func (ctx *RenderContext) ResolveLinkRoot(link string) string {
return ctx.ResolveLinkRelative(setting.AppSubURL+"/", "", link)
}
+
+func ParseRenderedLink(s, preferLinkType string) (linkType, link string) {
+ if strings.HasPrefix(s, "/:") {
+ p := strings.IndexByte(s[1:], '/')
+ if p == -1 {
+ return s, ""
+ }
+ return s[:p+1], s[p+2:]
+ }
+ return preferLinkType, s
+}
diff --git a/modules/markup/render_link_test.go b/modules/markup/render_link_test.go
index c904ec7f18..972e15308c 100644
--- a/modules/markup/render_link_test.go
+++ b/modules/markup/render_link_test.go
@@ -4,7 +4,6 @@
package markup
import (
- "context"
"testing"
"code.gitea.io/gitea/modules/setting"
@@ -13,7 +12,7 @@ import (
)
func TestResolveLinkRelative(t *testing.T) {
- ctx := context.Background()
+ ctx := t.Context()
setting.AppURL = "http://localhost:3000"
assert.Equal(t, "/a", resolveLinkRelative(ctx, "/a", "", "", false))
assert.Equal(t, "/a/b", resolveLinkRelative(ctx, "/a", "b", "", false))
diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go
index 35f90eb46c..b6e9c348b7 100644
--- a/modules/markup/renderer.go
+++ b/modules/markup/renderer.go
@@ -4,12 +4,12 @@
package markup
import (
- "bytes"
"io"
"path"
"strings"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/typesniffer"
)
// Renderer defines an interface for rendering markup file to HTML
@@ -37,7 +37,7 @@ type ExternalRenderer interface {
// RendererContentDetector detects if the content can be rendered
// by specified renderer
type RendererContentDetector interface {
- CanRender(filename string, input io.Reader) bool
+ CanRender(filename string, sniffedType typesniffer.SniffedType, prefetchBuf []byte) bool
}
var (
@@ -60,13 +60,9 @@ func GetRendererByFileName(filename string) Renderer {
}
// DetectRendererType detects the markup type of the content
-func DetectRendererType(filename string, input io.Reader) string {
- buf, err := io.ReadAll(input)
- if err != nil {
- return ""
- }
+func DetectRendererType(filename string, sniffedType typesniffer.SniffedType, prefetchBuf []byte) string {
for _, renderer := range renderers {
- if detector, ok := renderer.(RendererContentDetector); ok && detector.CanRender(filename, bytes.NewReader(buf)) {
+ if detector, ok := renderer.(RendererContentDetector); ok && detector.CanRender(filename, sniffedType, prefetchBuf) {
return renderer.Name()
}
}
diff --git a/modules/markup/renderer_test.go b/modules/markup/renderer_test.go
deleted file mode 100644
index 0791081f94..0000000000
--- a/modules/markup/renderer_test.go
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package markup_test
diff --git a/modules/markup/sanitizer_default.go b/modules/markup/sanitizer_default.go
index 14161eb533..0fbf0f0b24 100644
--- a/modules/markup/sanitizer_default.go
+++ b/modules/markup/sanitizer_default.go
@@ -4,6 +4,7 @@
package markup
import (
+ "html/template"
"io"
"net/url"
"regexp"
@@ -52,6 +53,8 @@ func (st *Sanitizer) createDefaultPolicy() *bluemonday.Policy {
policy.AllowAttrs("src", "autoplay", "controls").OnElements("video")
+ policy.AllowAttrs("loading").OnElements("img")
+
// Allow generally safe attributes (reference: https://github.com/jch/html-pipeline)
generalSafeAttrs := []string{
"abbr", "accept", "accept-charset",
@@ -90,9 +93,9 @@ func (st *Sanitizer) createDefaultPolicy() *bluemonday.Policy {
return policy
}
-// Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist.
-func Sanitize(s string) string {
- return GetDefaultSanitizer().defaultPolicy.Sanitize(s)
+// Sanitize use default sanitizer policy to sanitize a string
+func Sanitize(s string) template.HTML {
+ return template.HTML(GetDefaultSanitizer().defaultPolicy.Sanitize(s))
}
// SanitizeReader sanitizes a Reader
diff --git a/modules/markup/sanitizer_default_test.go b/modules/markup/sanitizer_default_test.go
index e6fbae5056..e5ba018e1b 100644
--- a/modules/markup/sanitizer_default_test.go
+++ b/modules/markup/sanitizer_default_test.go
@@ -62,9 +62,13 @@ func TestSanitizer(t *testing.T) {
`<a href="javascript:alert('xss')">bad</a>`, `bad`,
`<a href="vbscript:no">bad</a>`, `bad`,
`<a href="data:1234">bad</a>`, `bad`,
+
+ // Some classes and attributes are used by the frontend framework and will execute JS code, so make sure they are removed
+ `<div class="link-action" data-attr-class="foo" data-url="xxx">txt</div>`, `<div data-attr-class="foo">txt</div>`,
+ `<div class="form-fetch-action" data-markdown-generated-content="bar" data-global-init="a" data-global-click="b">txt</div>`, `<div data-markdown-generated-content="bar">txt</div>`,
}
for i := 0; i < len(testCases); i += 2 {
- assert.Equal(t, testCases[i+1], Sanitize(testCases[i]))
+ assert.Equal(t, testCases[i+1], string(Sanitize(testCases[i])))
}
}
diff --git a/modules/metrics/collector.go b/modules/metrics/collector.go
index 230260ff94..4d2ec287a9 100755
--- a/modules/metrics/collector.go
+++ b/modules/metrics/collector.go
@@ -184,7 +184,7 @@ func NewCollector() Collector {
Users: prometheus.NewDesc(
namespace+"users",
"Number of Users",
- nil, nil,
+ []string{"state"}, nil,
),
Watches: prometheus.NewDesc(
namespace+"watches",
@@ -373,7 +373,14 @@ func (c Collector) Collect(ch chan<- prometheus.Metric) {
ch <- prometheus.MustNewConstMetric(
c.Users,
prometheus.GaugeValue,
- float64(stats.Counter.User),
+ float64(stats.Counter.UsersActive),
+ "active", // state label
+ )
+ ch <- prometheus.MustNewConstMetric(
+ c.Users,
+ prometheus.GaugeValue,
+ float64(stats.Counter.UsersNotActive),
+ "inactive", // state label
)
ch <- prometheus.MustNewConstMetric(
c.Watches,
diff --git a/modules/migration/downloader.go b/modules/migration/downloader.go
index 08dbbc29a9..669222dea2 100644
--- a/modules/migration/downloader.go
+++ b/modules/migration/downloader.go
@@ -12,18 +12,17 @@ import (
// Downloader downloads the site repo information
type Downloader interface {
- SetContext(context.Context)
- GetRepoInfo() (*Repository, error)
- GetTopics() ([]string, error)
- GetMilestones() ([]*Milestone, error)
- GetReleases() ([]*Release, error)
- GetLabels() ([]*Label, error)
- GetIssues(page, perPage int) ([]*Issue, bool, error)
- GetComments(commentable Commentable) ([]*Comment, bool, error)
- GetAllComments(page, perPage int) ([]*Comment, bool, error)
+ GetRepoInfo(ctx context.Context) (*Repository, error)
+ GetTopics(ctx context.Context) ([]string, error)
+ GetMilestones(ctx context.Context) ([]*Milestone, error)
+ GetReleases(ctx context.Context) ([]*Release, error)
+ GetLabels(ctx context.Context) ([]*Label, error)
+ GetIssues(ctx context.Context, page, perPage int) ([]*Issue, bool, error)
+ GetComments(ctx context.Context, commentable Commentable) ([]*Comment, bool, error)
+ GetAllComments(ctx context.Context, page, perPage int) ([]*Comment, bool, error)
SupportGetRepoComments() bool
- GetPullRequests(page, perPage int) ([]*PullRequest, bool, error)
- GetReviews(reviewable Reviewable) ([]*Review, error)
+ GetPullRequests(ctx context.Context, page, perPage int) ([]*PullRequest, bool, error)
+ GetReviews(ctx context.Context, reviewable Reviewable) ([]*Review, error)
FormatCloneURL(opts MigrateOptions, remoteAddr string) (string, error)
}
diff --git a/modules/migration/null_downloader.go b/modules/migration/null_downloader.go
index e5b69331df..e488f6914f 100644
--- a/modules/migration/null_downloader.go
+++ b/modules/migration/null_downloader.go
@@ -13,56 +13,53 @@ type NullDownloader struct{}
var _ Downloader = &NullDownloader{}
-// SetContext set context
-func (n NullDownloader) SetContext(_ context.Context) {}
-
// GetRepoInfo returns a repository information
-func (n NullDownloader) GetRepoInfo() (*Repository, error) {
+func (n NullDownloader) GetRepoInfo(_ context.Context) (*Repository, error) {
return nil, ErrNotSupported{Entity: "RepoInfo"}
}
// GetTopics return repository topics
-func (n NullDownloader) GetTopics() ([]string, error) {
+func (n NullDownloader) GetTopics(_ context.Context) ([]string, error) {
return nil, ErrNotSupported{Entity: "Topics"}
}
// GetMilestones returns milestones
-func (n NullDownloader) GetMilestones() ([]*Milestone, error) {
+func (n NullDownloader) GetMilestones(_ context.Context) ([]*Milestone, error) {
return nil, ErrNotSupported{Entity: "Milestones"}
}
// GetReleases returns releases
-func (n NullDownloader) GetReleases() ([]*Release, error) {
+func (n NullDownloader) GetReleases(_ context.Context) ([]*Release, error) {
return nil, ErrNotSupported{Entity: "Releases"}
}
// GetLabels returns labels
-func (n NullDownloader) GetLabels() ([]*Label, error) {
+func (n NullDownloader) GetLabels(_ context.Context) ([]*Label, error) {
return nil, ErrNotSupported{Entity: "Labels"}
}
// GetIssues returns issues according start and limit
-func (n NullDownloader) GetIssues(page, perPage int) ([]*Issue, bool, error) {
+func (n NullDownloader) GetIssues(_ context.Context, page, perPage int) ([]*Issue, bool, error) {
return nil, false, ErrNotSupported{Entity: "Issues"}
}
// GetComments returns comments of an issue or PR
-func (n NullDownloader) GetComments(commentable Commentable) ([]*Comment, bool, error) {
+func (n NullDownloader) GetComments(_ context.Context, commentable Commentable) ([]*Comment, bool, error) {
return nil, false, ErrNotSupported{Entity: "Comments"}
}
// GetAllComments returns paginated comments
-func (n NullDownloader) GetAllComments(page, perPage int) ([]*Comment, bool, error) {
+func (n NullDownloader) GetAllComments(_ context.Context, page, perPage int) ([]*Comment, bool, error) {
return nil, false, ErrNotSupported{Entity: "AllComments"}
}
// GetPullRequests returns pull requests according page and perPage
-func (n NullDownloader) GetPullRequests(page, perPage int) ([]*PullRequest, bool, error) {
+func (n NullDownloader) GetPullRequests(_ context.Context, page, perPage int) ([]*PullRequest, bool, error) {
return nil, false, ErrNotSupported{Entity: "PullRequests"}
}
// GetReviews returns pull requests review
-func (n NullDownloader) GetReviews(reviewable Reviewable) ([]*Review, error) {
+func (n NullDownloader) GetReviews(_ context.Context, reviewable Reviewable) ([]*Review, error) {
return nil, ErrNotSupported{Entity: "Reviews"}
}
diff --git a/modules/migration/retry_downloader.go b/modules/migration/retry_downloader.go
index 1cacf5f375..69804b7767 100644
--- a/modules/migration/retry_downloader.go
+++ b/modules/migration/retry_downloader.go
@@ -13,57 +13,49 @@ var _ Downloader = &RetryDownloader{}
// RetryDownloader retry the downloads
type RetryDownloader struct {
Downloader
- ctx context.Context
RetryTimes int // the total execute times
RetryDelay int // time to delay seconds
}
// NewRetryDownloader creates a retry downloader
-func NewRetryDownloader(ctx context.Context, downloader Downloader, retryTimes, retryDelay int) *RetryDownloader {
+func NewRetryDownloader(downloader Downloader, retryTimes, retryDelay int) *RetryDownloader {
return &RetryDownloader{
Downloader: downloader,
- ctx: ctx,
RetryTimes: retryTimes,
RetryDelay: retryDelay,
}
}
-func (d *RetryDownloader) retry(work func() error) error {
+func (d *RetryDownloader) retry(ctx context.Context, work func(context.Context) error) error {
var (
times = d.RetryTimes
err error
)
for ; times > 0; times-- {
- if err = work(); err == nil {
+ if err = work(ctx); err == nil {
return nil
}
if IsErrNotSupported(err) {
return err
}
select {
- case <-d.ctx.Done():
- return d.ctx.Err()
+ case <-ctx.Done():
+ return ctx.Err()
case <-time.After(time.Second * time.Duration(d.RetryDelay)):
}
}
return err
}
-// SetContext set context
-func (d *RetryDownloader) SetContext(ctx context.Context) {
- d.ctx = ctx
- d.Downloader.SetContext(ctx)
-}
-
// GetRepoInfo returns a repository information with retry
-func (d *RetryDownloader) GetRepoInfo() (*Repository, error) {
+func (d *RetryDownloader) GetRepoInfo(ctx context.Context) (*Repository, error) {
var (
repo *Repository
err error
)
- err = d.retry(func() error {
- repo, err = d.Downloader.GetRepoInfo()
+ err = d.retry(ctx, func(ctx context.Context) error {
+ repo, err = d.Downloader.GetRepoInfo(ctx)
return err
})
@@ -71,14 +63,14 @@ func (d *RetryDownloader) GetRepoInfo() (*Repository, error) {
}
// GetTopics returns a repository's topics with retry
-func (d *RetryDownloader) GetTopics() ([]string, error) {
+func (d *RetryDownloader) GetTopics(ctx context.Context) ([]string, error) {
var (
topics []string
err error
)
- err = d.retry(func() error {
- topics, err = d.Downloader.GetTopics()
+ err = d.retry(ctx, func(ctx context.Context) error {
+ topics, err = d.Downloader.GetTopics(ctx)
return err
})
@@ -86,14 +78,14 @@ func (d *RetryDownloader) GetTopics() ([]string, error) {
}
// GetMilestones returns a repository's milestones with retry
-func (d *RetryDownloader) GetMilestones() ([]*Milestone, error) {
+func (d *RetryDownloader) GetMilestones(ctx context.Context) ([]*Milestone, error) {
var (
milestones []*Milestone
err error
)
- err = d.retry(func() error {
- milestones, err = d.Downloader.GetMilestones()
+ err = d.retry(ctx, func(ctx context.Context) error {
+ milestones, err = d.Downloader.GetMilestones(ctx)
return err
})
@@ -101,14 +93,14 @@ func (d *RetryDownloader) GetMilestones() ([]*Milestone, error) {
}
// GetReleases returns a repository's releases with retry
-func (d *RetryDownloader) GetReleases() ([]*Release, error) {
+func (d *RetryDownloader) GetReleases(ctx context.Context) ([]*Release, error) {
var (
releases []*Release
err error
)
- err = d.retry(func() error {
- releases, err = d.Downloader.GetReleases()
+ err = d.retry(ctx, func(ctx context.Context) error {
+ releases, err = d.Downloader.GetReleases(ctx)
return err
})
@@ -116,14 +108,14 @@ func (d *RetryDownloader) GetReleases() ([]*Release, error) {
}
// GetLabels returns a repository's labels with retry
-func (d *RetryDownloader) GetLabels() ([]*Label, error) {
+func (d *RetryDownloader) GetLabels(ctx context.Context) ([]*Label, error) {
var (
labels []*Label
err error
)
- err = d.retry(func() error {
- labels, err = d.Downloader.GetLabels()
+ err = d.retry(ctx, func(ctx context.Context) error {
+ labels, err = d.Downloader.GetLabels(ctx)
return err
})
@@ -131,15 +123,15 @@ func (d *RetryDownloader) GetLabels() ([]*Label, error) {
}
// GetIssues returns a repository's issues with retry
-func (d *RetryDownloader) GetIssues(page, perPage int) ([]*Issue, bool, error) {
+func (d *RetryDownloader) GetIssues(ctx context.Context, page, perPage int) ([]*Issue, bool, error) {
var (
issues []*Issue
isEnd bool
err error
)
- err = d.retry(func() error {
- issues, isEnd, err = d.Downloader.GetIssues(page, perPage)
+ err = d.retry(ctx, func(ctx context.Context) error {
+ issues, isEnd, err = d.Downloader.GetIssues(ctx, page, perPage)
return err
})
@@ -147,15 +139,15 @@ func (d *RetryDownloader) GetIssues(page, perPage int) ([]*Issue, bool, error) {
}
// GetComments returns a repository's comments with retry
-func (d *RetryDownloader) GetComments(commentable Commentable) ([]*Comment, bool, error) {
+func (d *RetryDownloader) GetComments(ctx context.Context, commentable Commentable) ([]*Comment, bool, error) {
var (
comments []*Comment
isEnd bool
err error
)
- err = d.retry(func() error {
- comments, isEnd, err = d.Downloader.GetComments(commentable)
+ err = d.retry(ctx, func(context.Context) error {
+ comments, isEnd, err = d.Downloader.GetComments(ctx, commentable)
return err
})
@@ -163,15 +155,15 @@ func (d *RetryDownloader) GetComments(commentable Commentable) ([]*Comment, bool
}
// GetPullRequests returns a repository's pull requests with retry
-func (d *RetryDownloader) GetPullRequests(page, perPage int) ([]*PullRequest, bool, error) {
+func (d *RetryDownloader) GetPullRequests(ctx context.Context, page, perPage int) ([]*PullRequest, bool, error) {
var (
prs []*PullRequest
err error
isEnd bool
)
- err = d.retry(func() error {
- prs, isEnd, err = d.Downloader.GetPullRequests(page, perPage)
+ err = d.retry(ctx, func(ctx context.Context) error {
+ prs, isEnd, err = d.Downloader.GetPullRequests(ctx, page, perPage)
return err
})
@@ -179,14 +171,13 @@ func (d *RetryDownloader) GetPullRequests(page, perPage int) ([]*PullRequest, bo
}
// GetReviews returns pull requests reviews
-func (d *RetryDownloader) GetReviews(reviewable Reviewable) ([]*Review, error) {
+func (d *RetryDownloader) GetReviews(ctx context.Context, reviewable Reviewable) ([]*Review, error) {
var (
reviews []*Review
err error
)
-
- err = d.retry(func() error {
- reviews, err = d.Downloader.GetReviews(reviewable)
+ err = d.retry(ctx, func(ctx context.Context) error {
+ reviews, err = d.Downloader.GetReviews(ctx, reviewable)
return err
})
diff --git a/modules/migration/schemas_bindata.go b/modules/migration/schemas_bindata.go
index c5db3b3461..695c2c1135 100644
--- a/modules/migration/schemas_bindata.go
+++ b/modules/migration/schemas_bindata.go
@@ -3,6 +3,28 @@
//go:build bindata
+//go:generate go run ../../build/generate-bindata.go ../../modules/migration/schemas bindata.dat
+
package migration
-//go:generate go run ../../build/generate-bindata.go ../../modules/migration/schemas migration bindata.go
+import (
+ "io"
+ "io/fs"
+ "path"
+ "sync"
+
+ _ "embed"
+
+ "code.gitea.io/gitea/modules/assetfs"
+)
+
+//go:embed bindata.dat
+var bindata []byte
+
+var BuiltinAssets = sync.OnceValue(func() fs.FS {
+ return assetfs.NewEmbeddedFS(bindata)
+})
+
+func openSchema(filename string) (io.ReadCloser, error) {
+ return BuiltinAssets().Open(path.Base(filename))
+}
diff --git a/modules/migration/schemas_static.go b/modules/migration/schemas_static.go
deleted file mode 100644
index 8a0c340a65..0000000000
--- a/modules/migration/schemas_static.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-//go:build bindata
-
-package migration
-
-import (
- "io"
- "path"
-)
-
-func openSchema(filename string) (io.ReadCloser, error) {
- return Assets.Open(path.Base(filename))
-}
diff --git a/modules/migration/uploader.go b/modules/migration/uploader.go
index ff642aa4fa..65752e248e 100644
--- a/modules/migration/uploader.go
+++ b/modules/migration/uploader.go
@@ -4,20 +4,22 @@
package migration
+import "context"
+
// Uploader uploads all the information of one repository
type Uploader interface {
MaxBatchInsertSize(tp string) int
- CreateRepo(repo *Repository, opts MigrateOptions) error
- CreateTopics(topic ...string) error
- CreateMilestones(milestones ...*Milestone) error
- CreateReleases(releases ...*Release) error
- SyncTags() error
- CreateLabels(labels ...*Label) error
- CreateIssues(issues ...*Issue) error
- CreateComments(comments ...*Comment) error
- CreatePullRequests(prs ...*PullRequest) error
- CreateReviews(reviews ...*Review) error
+ CreateRepo(ctx context.Context, repo *Repository, opts MigrateOptions) error
+ CreateTopics(ctx context.Context, topic ...string) error
+ CreateMilestones(ctx context.Context, milestones ...*Milestone) error
+ CreateReleases(ctx context.Context, releases ...*Release) error
+ SyncTags(ctx context.Context) error
+ CreateLabels(ctx context.Context, labels ...*Label) error
+ CreateIssues(ctx context.Context, issues ...*Issue) error
+ CreateComments(ctx context.Context, comments ...*Comment) error
+ CreatePullRequests(ctx context.Context, prs ...*PullRequest) error
+ CreateReviews(ctx context.Context, reviews ...*Review) error
Rollback() error
- Finish() error
+ Finish(ctx context.Context) error
Close()
}
diff --git a/modules/nosql/redis_test.go b/modules/nosql/redis_test.go
index 43652e314c..93276ca793 100644
--- a/modules/nosql/redis_test.go
+++ b/modules/nosql/redis_test.go
@@ -5,6 +5,9 @@ package nosql
import (
"testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestToRedisURI(t *testing.T) {
@@ -26,9 +29,9 @@ func TestToRedisURI(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- if got := ToRedisURI(tt.connection); got == nil || got.String() != tt.want {
- t.Errorf(`ToRedisURI(%q) = %s, want %s`, tt.connection, got.String(), tt.want)
- }
+ got := ToRedisURI(tt.connection)
+ require.NotNil(t, got)
+ assert.Equal(t, tt.want, got.String())
})
}
}
diff --git a/modules/optional/option.go b/modules/optional/option.go
index af9e5ac852..6075c6347e 100644
--- a/modules/optional/option.go
+++ b/modules/optional/option.go
@@ -3,6 +3,14 @@
package optional
+import "strconv"
+
+// Option is a generic type that can hold a value of type T or be empty (None).
+//
+// It must use the slice type to work with "chi" form values binding:
+// * non-existing value are represented as an empty slice (None)
+// * existing value is represented as a slice with one element (Some)
+// * multiple values are represented as a slice with multiple elements (Some), the Value is the first element (not well-defined in this case)
type Option[T any] []T
func None[T any]() Option[T] {
@@ -43,3 +51,12 @@ func (o Option[T]) ValueOrDefault(v T) T {
}
return v
}
+
+// ParseBool get the corresponding optional.Option[bool] of a string using strconv.ParseBool
+func ParseBool(s string) Option[bool] {
+ v, e := strconv.ParseBool(s)
+ if e != nil {
+ return None[bool]()
+ }
+ return Some(v)
+}
diff --git a/modules/optional/option_test.go b/modules/optional/option_test.go
index 203e9221e3..f600ff5a2c 100644
--- a/modules/optional/option_test.go
+++ b/modules/optional/option_test.go
@@ -57,3 +57,16 @@ func TestOption(t *testing.T) {
assert.True(t, opt3.Has())
assert.Equal(t, int(1), opt3.Value())
}
+
+func Test_ParseBool(t *testing.T) {
+ assert.Equal(t, optional.None[bool](), optional.ParseBool(""))
+ assert.Equal(t, optional.None[bool](), optional.ParseBool("x"))
+
+ assert.Equal(t, optional.Some(false), optional.ParseBool("0"))
+ assert.Equal(t, optional.Some(false), optional.ParseBool("f"))
+ assert.Equal(t, optional.Some(false), optional.ParseBool("False"))
+
+ assert.Equal(t, optional.Some(true), optional.ParseBool("1"))
+ assert.Equal(t, optional.Some(true), optional.ParseBool("t"))
+ assert.Equal(t, optional.Some(true), optional.ParseBool("True"))
+}
diff --git a/modules/optional/serialization_test.go b/modules/optional/serialization_test.go
index 09a4bddea0..cf81a94cfc 100644
--- a/modules/optional/serialization_test.go
+++ b/modules/optional/serialization_test.go
@@ -4,7 +4,7 @@
package optional_test
import (
- std_json "encoding/json" //nolint:depguard
+ std_json "encoding/json" //nolint:depguard // for testing purpose
"testing"
"code.gitea.io/gitea/modules/json"
@@ -51,11 +51,11 @@ func TestOptionalToJson(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
b, err := json.Marshal(tc.obj)
assert.NoError(t, err)
- assert.EqualValues(t, tc.want, string(b), "gitea json module returned unexpected")
+ assert.Equal(t, tc.want, string(b), "gitea json module returned unexpected")
b, err = std_json.Marshal(tc.obj)
assert.NoError(t, err)
- assert.EqualValues(t, tc.want, string(b), "std json module returned unexpected")
+ assert.Equal(t, tc.want, string(b), "std json module returned unexpected")
})
}
}
@@ -89,12 +89,12 @@ func TestOptionalFromJson(t *testing.T) {
var obj1 testSerializationStruct
err := json.Unmarshal([]byte(tc.data), &obj1)
assert.NoError(t, err)
- assert.EqualValues(t, tc.want, obj1, "gitea json module returned unexpected")
+ assert.Equal(t, tc.want, obj1, "gitea json module returned unexpected")
var obj2 testSerializationStruct
err = std_json.Unmarshal([]byte(tc.data), &obj2)
assert.NoError(t, err)
- assert.EqualValues(t, tc.want, obj2, "std json module returned unexpected")
+ assert.Equal(t, tc.want, obj2, "std json module returned unexpected")
})
}
}
@@ -135,7 +135,7 @@ optional_two_string: null
t.Run(tc.name, func(t *testing.T) {
b, err := yaml.Marshal(tc.obj)
assert.NoError(t, err)
- assert.EqualValues(t, tc.want, string(b), "yaml module returned unexpected")
+ assert.Equal(t, tc.want, string(b), "yaml module returned unexpected")
})
}
}
@@ -184,7 +184,7 @@ optional_twostring: null
var obj testSerializationStruct
err := yaml.Unmarshal([]byte(tc.data), &obj)
assert.NoError(t, err)
- assert.EqualValues(t, tc.want, obj, "yaml module returned unexpected")
+ assert.Equal(t, tc.want, obj, "yaml module returned unexpected")
})
}
}
diff --git a/modules/options/options_bindata.go b/modules/options/options_bindata.go
index 29151cb3cb..b2321d7eb5 100644
--- a/modules/options/options_bindata.go
+++ b/modules/options/options_bindata.go
@@ -3,6 +3,21 @@
//go:build bindata
+//go:generate go run ../../build/generate-bindata.go ../../options bindata.dat
+
package options
-//go:generate go run ../../build/generate-bindata.go ../../options options bindata.go
+import (
+ "sync"
+
+ _ "embed"
+
+ "code.gitea.io/gitea/modules/assetfs"
+)
+
+//go:embed bindata.dat
+var bindata []byte
+
+var BuiltinAssets = sync.OnceValue(func() *assetfs.Layer {
+ return assetfs.Bindata("builtin(bindata)", assetfs.NewEmbeddedFS(bindata))
+})
diff --git a/modules/options/dynamic.go b/modules/options/options_dynamic.go
index 085492d11c..085492d11c 100644
--- a/modules/options/dynamic.go
+++ b/modules/options/options_dynamic.go
diff --git a/modules/options/static.go b/modules/options/static.go
deleted file mode 100644
index 72b28e990e..0000000000
--- a/modules/options/static.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-//go:build bindata
-
-package options
-
-import (
- "code.gitea.io/gitea/modules/assetfs"
-)
-
-func BuiltinAssets() *assetfs.Layer {
- return assetfs.Bindata("builtin(bindata)", Assets)
-}
diff --git a/modules/packages/conda/metadata.go b/modules/packages/conda/metadata.go
index 76ba95eace..5eb2890115 100644
--- a/modules/packages/conda/metadata.go
+++ b/modules/packages/conda/metadata.go
@@ -17,9 +17,9 @@ import (
)
var (
- ErrInvalidStructure = util.SilentWrap{Message: "package structure is invalid", Err: util.ErrInvalidArgument}
- ErrInvalidName = util.SilentWrap{Message: "package name is invalid", Err: util.ErrInvalidArgument}
- ErrInvalidVersion = util.SilentWrap{Message: "package version is invalid", Err: util.ErrInvalidArgument}
+ ErrInvalidStructure = util.NewInvalidArgumentErrorf("package structure is invalid")
+ ErrInvalidName = util.NewInvalidArgumentErrorf("package name is invalid")
+ ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid")
)
const (
diff --git a/models/packages/container/const.go b/modules/packages/container/const.go
index 0dfbda051d..6c7c9b46d1 100644
--- a/models/packages/container/const.go
+++ b/modules/packages/container/const.go
@@ -4,6 +4,8 @@
package container
const (
+ ContentTypeDockerDistributionManifestV2 = "application/vnd.docker.distribution.manifest.v2+json"
+
ManifestFilename = "manifest.json"
UploadVersion = "_upload"
)
diff --git a/modules/packages/container/metadata.go b/modules/packages/container/metadata.go
index 2a41fb9105..3ef0684d13 100644
--- a/modules/packages/container/metadata.go
+++ b/modules/packages/container/metadata.go
@@ -71,14 +71,34 @@ type Manifest struct {
Size int64 `json:"size"`
}
+func IsMediaTypeValid(mt string) bool {
+ return strings.HasPrefix(mt, "application/vnd.docker.") || strings.HasPrefix(mt, "application/vnd.oci.")
+}
+
+func IsMediaTypeImageManifest(mt string) bool {
+ return strings.EqualFold(mt, oci.MediaTypeImageManifest) || strings.EqualFold(mt, "application/vnd.docker.distribution.manifest.v2+json")
+}
+
+func IsMediaTypeImageIndex(mt string) bool {
+ return strings.EqualFold(mt, oci.MediaTypeImageIndex) || strings.EqualFold(mt, "application/vnd.docker.distribution.manifest.list.v2+json")
+}
+
// ParseImageConfig parses the metadata of an image config
-func ParseImageConfig(mt string, r io.Reader) (*Metadata, error) {
- if strings.EqualFold(mt, helm.ConfigMediaType) {
+func ParseImageConfig(mediaType string, r io.Reader) (*Metadata, error) {
+ if strings.EqualFold(mediaType, helm.ConfigMediaType) {
return parseHelmConfig(r)
}
// fallback to OCI Image Config
- return parseOCIImageConfig(r)
+ // FIXME: this fallback is not right, we should strictly check the media type in the future
+ metadata, err := parseOCIImageConfig(r)
+ if err != nil {
+ if !IsMediaTypeImageManifest(mediaType) {
+ return &Metadata{Platform: "unknown/unknown"}, nil
+ }
+ return nil, err
+ }
+ return metadata, nil
}
func parseOCIImageConfig(r io.Reader) (*Metadata, error) {
diff --git a/modules/packages/container/metadata_test.go b/modules/packages/container/metadata_test.go
index 665499b2e6..0f2d702925 100644
--- a/modules/packages/container/metadata_test.go
+++ b/modules/packages/container/metadata_test.go
@@ -11,6 +11,7 @@ import (
oci "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestParseImageConfig(t *testing.T) {
@@ -58,4 +59,8 @@ func TestParseImageConfig(t *testing.T) {
assert.ElementsMatch(t, []string{author}, metadata.Authors)
assert.Equal(t, projectURL, metadata.ProjectURL)
assert.Equal(t, repositoryURL, metadata.RepositoryURL)
+
+ metadata, err = ParseImageConfig("anything-unknown", strings.NewReader(""))
+ require.NoError(t, err)
+ assert.Equal(t, &Metadata{Platform: "unknown/unknown"}, metadata)
}
diff --git a/modules/packages/content_store.go b/modules/packages/content_store.go
index 37612556d7..dadb7eaefc 100644
--- a/modules/packages/content_store.go
+++ b/modules/packages/content_store.go
@@ -28,8 +28,7 @@ func NewContentStore() *ContentStore {
return contentStore
}
-// Get gets a package blob
-func (s *ContentStore) Get(key BlobHash256Key) (storage.Object, error) {
+func (s *ContentStore) OpenBlob(key BlobHash256Key) (storage.Object, error) {
return s.store.Open(KeyToRelativePath(key))
}
diff --git a/modules/packages/goproxy/metadata.go b/modules/packages/goproxy/metadata.go
index 40f7d20508..a67b149f4d 100644
--- a/modules/packages/goproxy/metadata.go
+++ b/modules/packages/goproxy/metadata.go
@@ -5,7 +5,6 @@ package goproxy
import (
"archive/zip"
- "fmt"
"io"
"path"
"strings"
@@ -88,7 +87,7 @@ func ParsePackage(r io.ReaderAt, size int64) (*Package, error) {
return nil, ErrInvalidStructure
}
- p.GoMod = fmt.Sprintf("module %s", p.Name)
+ p.GoMod = "module " + p.Name
return p, nil
}
diff --git a/modules/packages/hashed_buffer.go b/modules/packages/hashed_buffer.go
index 4ab45edcec..0cd657cd44 100644
--- a/modules/packages/hashed_buffer.go
+++ b/modules/packages/hashed_buffer.go
@@ -6,6 +6,7 @@ package packages
import (
"io"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util/filebuffer"
)
@@ -34,11 +35,11 @@ func NewHashedBuffer() (*HashedBuffer, error) {
// NewHashedBufferWithSize creates a hashed buffer with a specific memory size
func NewHashedBufferWithSize(maxMemorySize int) (*HashedBuffer, error) {
- b, err := filebuffer.New(maxMemorySize)
+ tempDir, err := setting.AppDataTempDir("package-hashed-buffer").MkdirAllSub("")
if err != nil {
return nil, err
}
-
+ b := filebuffer.New(maxMemorySize, tempDir)
hash := NewMultiHasher()
combinedWriter := io.MultiWriter(b, hash)
diff --git a/modules/packages/hashed_buffer_test.go b/modules/packages/hashed_buffer_test.go
index 564e782f18..5104c1fb25 100644
--- a/modules/packages/hashed_buffer_test.go
+++ b/modules/packages/hashed_buffer_test.go
@@ -9,10 +9,13 @@ import (
"strings"
"testing"
+ "code.gitea.io/gitea/modules/setting"
+
"github.com/stretchr/testify/assert"
)
func TestHashedBuffer(t *testing.T) {
+ setting.AppDataPath = t.TempDir()
cases := []struct {
MaxMemorySize int
Data string
diff --git a/modules/packages/npm/creator.go b/modules/packages/npm/creator.go
index 7d3d7cd6b5..11b5123c27 100644
--- a/modules/packages/npm/creator.go
+++ b/modules/packages/npm/creator.go
@@ -58,7 +58,7 @@ type PackageMetadata struct {
Time map[string]time.Time `json:"time,omitempty"`
Homepage string `json:"homepage,omitempty"`
Keywords []string `json:"keywords,omitempty"`
- Repository Repository `json:"repository,omitempty"`
+ Repository Repository `json:"repository"`
Author User `json:"author"`
ReadmeFilename string `json:"readmeFilename,omitempty"`
Users map[string]bool `json:"users,omitempty"`
@@ -75,12 +75,13 @@ type PackageMetadataVersion struct {
Author User `json:"author"`
Homepage string `json:"homepage,omitempty"`
License string `json:"license,omitempty"`
- Repository Repository `json:"repository,omitempty"`
+ Repository Repository `json:"repository"`
Keywords []string `json:"keywords,omitempty"`
Dependencies map[string]string `json:"dependencies,omitempty"`
BundleDependencies []string `json:"bundleDependencies,omitempty"`
DevDependencies map[string]string `json:"devDependencies,omitempty"`
PeerDependencies map[string]string `json:"peerDependencies,omitempty"`
+ PeerDependenciesMeta map[string]any `json:"peerDependenciesMeta,omitempty"`
Bin map[string]string `json:"bin,omitempty"`
OptionalDependencies map[string]string `json:"optionalDependencies,omitempty"`
Readme string `json:"readme,omitempty"`
@@ -222,6 +223,7 @@ func ParsePackage(r io.Reader) (*Package, error) {
BundleDependencies: meta.BundleDependencies,
DevelopmentDependencies: meta.DevDependencies,
PeerDependencies: meta.PeerDependencies,
+ PeerDependenciesMeta: meta.PeerDependenciesMeta,
OptionalDependencies: meta.OptionalDependencies,
Bin: meta.Bin,
Readme: meta.Readme,
diff --git a/modules/packages/npm/metadata.go b/modules/packages/npm/metadata.go
index 6bb77f302b..362d0470d5 100644
--- a/modules/packages/npm/metadata.go
+++ b/modules/packages/npm/metadata.go
@@ -19,8 +19,9 @@ type Metadata struct {
BundleDependencies []string `json:"bundleDependencies,omitempty"`
DevelopmentDependencies map[string]string `json:"development_dependencies,omitempty"`
PeerDependencies map[string]string `json:"peer_dependencies,omitempty"`
+ PeerDependenciesMeta map[string]any `json:"peer_dependencies_meta,omitempty"`
OptionalDependencies map[string]string `json:"optional_dependencies,omitempty"`
Bin map[string]string `json:"bin,omitempty"`
Readme string `json:"readme,omitempty"`
- Repository Repository `json:"repository,omitempty"`
+ Repository Repository `json:"repository"`
}
diff --git a/modules/packages/nuget/metadata.go b/modules/packages/nuget/metadata.go
index 1e98ddffde..a122590bf1 100644
--- a/modules/packages/nuget/metadata.go
+++ b/modules/packages/nuget/metadata.go
@@ -57,14 +57,24 @@ type Package struct {
// Metadata represents the metadata of a Nuget package
type Metadata struct {
- Description string `json:"description,omitempty"`
- ReleaseNotes string `json:"release_notes,omitempty"`
- Readme string `json:"readme,omitempty"`
- Authors string `json:"authors,omitempty"`
- ProjectURL string `json:"project_url,omitempty"`
- RepositoryURL string `json:"repository_url,omitempty"`
- RequireLicenseAcceptance bool `json:"require_license_acceptance"`
- Dependencies map[string][]Dependency `json:"dependencies,omitempty"`
+ Authors string `json:"authors,omitempty"`
+ Copyright string `json:"copyright,omitempty"`
+ Description string `json:"description,omitempty"`
+ DevelopmentDependency bool `json:"development_dependency,omitempty"`
+ IconURL string `json:"icon_url,omitempty"`
+ Language string `json:"language,omitempty"`
+ LicenseURL string `json:"license_url,omitempty"`
+ MinClientVersion string `json:"min_client_version,omitempty"`
+ Owners string `json:"owners,omitempty"`
+ ProjectURL string `json:"project_url,omitempty"`
+ Readme string `json:"readme,omitempty"`
+ ReleaseNotes string `json:"release_notes,omitempty"`
+ RepositoryURL string `json:"repository_url,omitempty"`
+ RequireLicenseAcceptance bool `json:"require_license_acceptance"`
+ Tags string `json:"tags,omitempty"`
+ Title string `json:"title,omitempty"`
+
+ Dependencies map[string][]Dependency `json:"dependencies,omitempty"`
}
// Dependency represents a dependency of a Nuget package
@@ -74,24 +84,30 @@ type Dependency struct {
}
// https://learn.microsoft.com/en-us/nuget/reference/nuspec
+// https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Packaging/compiler/resources/nuspec.xsd
type nuspecPackage struct {
Metadata struct {
- ID string `xml:"id"`
- Version string `xml:"version"`
- Authors string `xml:"authors"`
- RequireLicenseAcceptance bool `xml:"requireLicenseAcceptance"`
+ // required fields
+ Authors string `xml:"authors"`
+ Description string `xml:"description"`
+ ID string `xml:"id"`
+ Version string `xml:"version"`
+
+ // optional fields
+ Copyright string `xml:"copyright"`
+ DevelopmentDependency bool `xml:"developmentDependency"`
+ IconURL string `xml:"iconUrl"`
+ Language string `xml:"language"`
+ LicenseURL string `xml:"licenseUrl"`
+ MinClientVersion string `xml:"minClientVersion,attr"`
+ Owners string `xml:"owners"`
ProjectURL string `xml:"projectUrl"`
- Description string `xml:"description"`
- ReleaseNotes string `xml:"releaseNotes"`
Readme string `xml:"readme"`
- PackageTypes struct {
- PackageType []struct {
- Name string `xml:"name,attr"`
- } `xml:"packageType"`
- } `xml:"packageTypes"`
- Repository struct {
- URL string `xml:"url,attr"`
- } `xml:"repository"`
+ ReleaseNotes string `xml:"releaseNotes"`
+ RequireLicenseAcceptance bool `xml:"requireLicenseAcceptance"`
+ Tags string `xml:"tags"`
+ Title string `xml:"title"`
+
Dependencies struct {
Dependency []struct {
ID string `xml:"id,attr"`
@@ -107,6 +123,14 @@ type nuspecPackage struct {
} `xml:"dependency"`
} `xml:"group"`
} `xml:"dependencies"`
+ PackageTypes struct {
+ PackageType []struct {
+ Name string `xml:"name,attr"`
+ } `xml:"packageType"`
+ } `xml:"packageTypes"`
+ Repository struct {
+ URL string `xml:"url,attr"`
+ } `xml:"repository"`
} `xml:"metadata"`
}
@@ -167,13 +191,23 @@ func ParseNuspecMetaData(archive *zip.Reader, r io.Reader) (*Package, error) {
}
m := &Metadata{
- Description: p.Metadata.Description,
- ReleaseNotes: p.Metadata.ReleaseNotes,
Authors: p.Metadata.Authors,
+ Copyright: p.Metadata.Copyright,
+ Description: p.Metadata.Description,
+ DevelopmentDependency: p.Metadata.DevelopmentDependency,
+ IconURL: p.Metadata.IconURL,
+ Language: p.Metadata.Language,
+ LicenseURL: p.Metadata.LicenseURL,
+ MinClientVersion: p.Metadata.MinClientVersion,
+ Owners: p.Metadata.Owners,
ProjectURL: p.Metadata.ProjectURL,
+ ReleaseNotes: p.Metadata.ReleaseNotes,
RepositoryURL: p.Metadata.Repository.URL,
RequireLicenseAcceptance: p.Metadata.RequireLicenseAcceptance,
- Dependencies: make(map[string][]Dependency),
+ Tags: p.Metadata.Tags,
+ Title: p.Metadata.Title,
+
+ Dependencies: make(map[string][]Dependency),
}
if p.Metadata.Readme != "" {
@@ -227,13 +261,13 @@ func ParseNuspecMetaData(archive *zip.Reader, r io.Reader) (*Package, error) {
func toNormalizedVersion(v *version.Version) string {
var buf bytes.Buffer
segments := v.Segments64()
- fmt.Fprintf(&buf, "%d.%d.%d", segments[0], segments[1], segments[2])
+ _, _ = fmt.Fprintf(&buf, "%d.%d.%d", segments[0], segments[1], segments[2])
if len(segments) > 3 && segments[3] > 0 {
- fmt.Fprintf(&buf, ".%d", segments[3])
+ _, _ = fmt.Fprintf(&buf, ".%d", segments[3])
}
pre := v.Prerelease()
if pre != "" {
- fmt.Fprint(&buf, "-", pre)
+ _, _ = fmt.Fprint(&buf, "-", pre)
}
return buf.String()
}
diff --git a/modules/packages/nuget/metadata_test.go b/modules/packages/nuget/metadata_test.go
index f466492f8a..90c3e8dfeb 100644
--- a/modules/packages/nuget/metadata_test.go
+++ b/modules/packages/nuget/metadata_test.go
@@ -12,44 +12,62 @@ import (
)
const (
- id = "System.Gitea"
- semver = "1.0.1"
- authors = "Gitea Authors"
- projectURL = "https://gitea.io"
- description = "Package Description"
- releaseNotes = "Package Release Notes"
- readme = "Readme"
- repositoryURL = "https://gitea.io/gitea/gitea"
- targetFramework = ".NETStandard2.1"
- dependencyID = "System.Text.Json"
- dependencyVersion = "5.0.0"
+ authors = "Gitea Authors"
+ copyright = "Package Copyright"
+ dependencyID = "System.Text.Json"
+ dependencyVersion = "5.0.0"
+ developmentDependency = true
+ description = "Package Description"
+ iconURL = "https://gitea.io/favicon.png"
+ id = "System.Gitea"
+ language = "Package Language"
+ licenseURL = "https://gitea.io/license"
+ minClientVersion = "1.0.0.0"
+ owners = "Package Owners"
+ projectURL = "https://gitea.io"
+ readme = "Readme"
+ releaseNotes = "Package Release Notes"
+ repositoryURL = "https://gitea.io/gitea/gitea"
+ requireLicenseAcceptance = true
+ tags = "tag_1 tag_2 tag_3"
+ targetFramework = ".NETStandard2.1"
+ title = "Package Title"
+ versionStr = "1.0.1"
)
const nuspecContent = `<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
- <metadata>
- <id>` + id + `</id>
- <version>` + semver + `</version>
- <authors>` + authors + `</authors>
- <requireLicenseAcceptance>true</requireLicenseAcceptance>
- <projectUrl>` + projectURL + `</projectUrl>
- <description>` + description + `</description>
- <releaseNotes>` + releaseNotes + `</releaseNotes>
- <repository url="` + repositoryURL + `" />
- <readme>README.md</readme>
- <dependencies>
- <group targetFramework="` + targetFramework + `">
- <dependency id="` + dependencyID + `" version="` + dependencyVersion + `" exclude="Build,Analyzers" />
- </group>
- </dependencies>
- </metadata>
+ <metadata minClientVersion="` + minClientVersion + `">
+ <authors>` + authors + `</authors>
+ <copyright>` + copyright + `</copyright>
+ <description>` + description + `</description>
+ <developmentDependency>true</developmentDependency>
+ <iconUrl>` + iconURL + `</iconUrl>
+ <id>` + id + `</id>
+ <language>` + language + `</language>
+ <licenseUrl>` + licenseURL + `</licenseUrl>
+ <owners>` + owners + `</owners>
+ <projectUrl>` + projectURL + `</projectUrl>
+ <readme>README.md</readme>
+ <releaseNotes>` + releaseNotes + `</releaseNotes>
+ <repository url="` + repositoryURL + `" />
+ <requireLicenseAcceptance>true</requireLicenseAcceptance>
+ <tags>` + tags + `</tags>
+ <title>` + title + `</title>
+ <version>` + versionStr + `</version>
+ <dependencies>
+ <group targetFramework="` + targetFramework + `">
+ <dependency id="` + dependencyID + `" version="` + dependencyVersion + `" exclude="Build,Analyzers" />
+ </group>
+ </dependencies>
+ </metadata>
</package>`
const symbolsNuspecContent = `<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>` + id + `</id>
- <version>` + semver + `</version>
+ <version>` + versionStr + `</version>
<description>` + description + `</description>
<packageTypes>
<packageType name="SymbolsPackage" />
@@ -140,14 +158,26 @@ func TestParsePackageMetaData(t *testing.T) {
assert.NotNil(t, np)
assert.Equal(t, DependencyPackage, np.PackageType)
- assert.Equal(t, id, np.ID)
- assert.Equal(t, semver, np.Version)
assert.Equal(t, authors, np.Metadata.Authors)
- assert.Equal(t, projectURL, np.Metadata.ProjectURL)
assert.Equal(t, description, np.Metadata.Description)
- assert.Equal(t, releaseNotes, np.Metadata.ReleaseNotes)
+ assert.Equal(t, id, np.ID)
+ assert.Equal(t, versionStr, np.Version)
+
+ assert.Equal(t, copyright, np.Metadata.Copyright)
+ assert.Equal(t, developmentDependency, np.Metadata.DevelopmentDependency)
+ assert.Equal(t, iconURL, np.Metadata.IconURL)
+ assert.Equal(t, language, np.Metadata.Language)
+ assert.Equal(t, licenseURL, np.Metadata.LicenseURL)
+ assert.Equal(t, minClientVersion, np.Metadata.MinClientVersion)
+ assert.Equal(t, owners, np.Metadata.Owners)
+ assert.Equal(t, projectURL, np.Metadata.ProjectURL)
assert.Equal(t, readme, np.Metadata.Readme)
+ assert.Equal(t, releaseNotes, np.Metadata.ReleaseNotes)
assert.Equal(t, repositoryURL, np.Metadata.RepositoryURL)
+ assert.Equal(t, requireLicenseAcceptance, np.Metadata.RequireLicenseAcceptance)
+ assert.Equal(t, tags, np.Metadata.Tags)
+ assert.Equal(t, title, np.Metadata.Title)
+
assert.Len(t, np.Metadata.Dependencies, 1)
assert.Contains(t, np.Metadata.Dependencies, targetFramework)
deps := np.Metadata.Dependencies[targetFramework]
@@ -180,7 +210,7 @@ func TestParsePackageMetaData(t *testing.T) {
assert.Equal(t, SymbolsPackage, np.PackageType)
assert.Equal(t, id, np.ID)
- assert.Equal(t, semver, np.Version)
+ assert.Equal(t, versionStr, np.Version)
assert.Equal(t, description, np.Metadata.Description)
assert.Empty(t, np.Metadata.Dependencies)
})
diff --git a/modules/packages/nuget/symbol_extractor.go b/modules/packages/nuget/symbol_extractor.go
index 81bf0371a0..9c952e1f10 100644
--- a/modules/packages/nuget/symbol_extractor.go
+++ b/modules/packages/nuget/symbol_extractor.go
@@ -34,7 +34,7 @@ type PortablePdbList []*PortablePdb
func (l PortablePdbList) Close() {
for _, pdb := range l {
- pdb.Content.Close()
+ _ = pdb.Content.Close()
}
}
@@ -65,7 +65,7 @@ func ExtractPortablePdb(r io.ReaderAt, size int64) (PortablePdbList, error) {
buf, err := packages.CreateHashedBufferFromReader(f)
- f.Close()
+ _ = f.Close()
if err != nil {
return err
@@ -73,12 +73,12 @@ func ExtractPortablePdb(r io.ReaderAt, size int64) (PortablePdbList, error) {
id, err := ParseDebugHeaderID(buf)
if err != nil {
- buf.Close()
+ _ = buf.Close()
return fmt.Errorf("Invalid PDB file: %w", err)
}
if _, err := buf.Seek(0, io.SeekStart); err != nil {
- buf.Close()
+ _ = buf.Close()
return err
}
diff --git a/modules/packages/nuget/symbol_extractor_test.go b/modules/packages/nuget/symbol_extractor_test.go
index fa1b80ee82..e841e377d9 100644
--- a/modules/packages/nuget/symbol_extractor_test.go
+++ b/modules/packages/nuget/symbol_extractor_test.go
@@ -9,6 +9,8 @@ import (
"encoding/base64"
"testing"
+ "code.gitea.io/gitea/modules/setting"
+
"github.com/stretchr/testify/assert"
)
@@ -17,18 +19,19 @@ fgAA3AEAAAQAAAAjU3RyaW5ncwAAAADgAQAABAAAACNVUwDkAQAAMAAAACNHVUlEAAAAFAIAACgB
AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`
func TestExtractPortablePdb(t *testing.T) {
+ setting.AppDataPath = t.TempDir()
createArchive := func(name string, content []byte) []byte {
var buf bytes.Buffer
archive := zip.NewWriter(&buf)
w, _ := archive.Create(name)
- w.Write(content)
- archive.Close()
+ _, _ = w.Write(content)
+ _ = archive.Close()
return buf.Bytes()
}
t.Run("MissingPdbFiles", func(t *testing.T) {
var buf bytes.Buffer
- zip.NewWriter(&buf).Close()
+ _ = zip.NewWriter(&buf).Close()
pdbs, err := ExtractPortablePdb(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
assert.ErrorIs(t, err, ErrMissingPdbFiles)
diff --git a/modules/packages/rubygems/marshal.go b/modules/packages/rubygems/marshal.go
index 4e6a5fc5f8..1505221acc 100644
--- a/modules/packages/rubygems/marshal.go
+++ b/modules/packages/rubygems/marshal.go
@@ -250,7 +250,7 @@ func (e *MarshalEncoder) marshalArray(arr reflect.Value) error {
return err
}
- for i := 0; i < length; i++ {
+ for i := range length {
if err := e.marshal(arr.Index(i).Interface()); err != nil {
return err
}
diff --git a/modules/packages/swift/metadata.go b/modules/packages/swift/metadata.go
index 24c4262ab7..85beb57607 100644
--- a/modules/packages/swift/metadata.go
+++ b/modules/packages/swift/metadata.go
@@ -47,7 +47,7 @@ type Metadata struct {
Keywords []string `json:"keywords,omitempty"`
RepositoryURL string `json:"repository_url,omitempty"`
License string `json:"license,omitempty"`
- Author Person `json:"author,omitempty"`
+ Author Person `json:"author"`
Manifests map[string]*Manifest `json:"manifests,omitempty"`
}
diff --git a/modules/paginator/paginator.go b/modules/paginator/paginator.go
index 8258d194c2..0f64e89d9a 100644
--- a/modules/paginator/paginator.go
+++ b/modules/paginator/paginator.go
@@ -4,6 +4,8 @@
package paginator
+import "code.gitea.io/gitea/modules/util"
+
/*
In template:
@@ -32,25 +34,43 @@ Output:
// Paginator represents a set of results of pagination calculations.
type Paginator struct {
- total int // total rows count
+ total int // total rows count, -1 means unknown
+ totalPages int // total pages count, -1 means unknown
+ current int // current page number
+ curRows int // current page rows count
+
pagingNum int // how many rows in one page
- current int // current page number
numPages int // how many pages to show on the UI
}
// New initialize a new pagination calculation and returns a Paginator as result.
func New(total, pagingNum, current, numPages int) *Paginator {
- if pagingNum <= 0 {
- pagingNum = 1
+ pagingNum = max(pagingNum, 1)
+ totalPages := util.Iif(total == -1, -1, (total+pagingNum-1)/pagingNum)
+ if total >= 0 {
+ current = min(current, totalPages)
}
- if current <= 0 {
- current = 1
+ current = max(current, 1)
+ return &Paginator{
+ total: total,
+ totalPages: totalPages,
+ current: current,
+ pagingNum: pagingNum,
+ numPages: numPages,
}
- p := &Paginator{total, pagingNum, current, numPages}
- if p.current > p.TotalPages() {
- p.current = p.TotalPages()
+}
+
+func (p *Paginator) SetCurRows(rows int) {
+ // For "unlimited paging", we need to know the rows of current page to determine if there is a next page.
+ // There is still an edge case: when curRows==pagingNum, then the "next page" will be an empty page.
+ // Ideally we should query one more row to determine if there is really a next page, but it's impossible in current framework.
+ p.curRows = rows
+ if p.total == -1 && p.current == 1 && !p.HasNext() {
+ // if there is only one page for the "unlimited paging", set total rows/pages count
+ // then the tmpl could decide to hide the nav bar.
+ p.total = rows
+ p.totalPages = util.Iif(p.total == 0, 0, 1)
}
- return p
}
// IsFirst returns true if current page is the first page.
@@ -72,7 +92,10 @@ func (p *Paginator) Previous() int {
// HasNext returns true if there is a next page relative to current page.
func (p *Paginator) HasNext() bool {
- return p.total > p.current*p.pagingNum
+ if p.total == -1 {
+ return p.curRows >= p.pagingNum
+ }
+ return p.current*p.pagingNum < p.total
}
func (p *Paginator) Next() int {
@@ -84,10 +107,7 @@ func (p *Paginator) Next() int {
// IsLast returns true if current page is the last page.
func (p *Paginator) IsLast() bool {
- if p.total == 0 {
- return true
- }
- return p.total > (p.current-1)*p.pagingNum && !p.HasNext()
+ return !p.HasNext()
}
// Total returns number of total rows.
@@ -97,10 +117,7 @@ func (p *Paginator) Total() int {
// TotalPages returns number of total pages.
func (p *Paginator) TotalPages() int {
- if p.total == 0 {
- return 1
- }
- return (p.total + p.pagingNum - 1) / p.pagingNum
+ return p.totalPages
}
// Current returns current page number.
@@ -135,10 +152,10 @@ func getMiddleIdx(numPages int) int {
// If value is -1 means "..." that more pages are not showing.
func (p *Paginator) Pages() []*Page {
if p.numPages == 0 {
- return []*Page{}
- } else if p.numPages == 1 && p.TotalPages() == 1 {
+ return nil
+ } else if p.total == -1 || (p.numPages == 1 && p.TotalPages() == 1) {
// Only show current page.
- return []*Page{{1, true}}
+ return []*Page{{p.current, true}}
}
// Total page number is less or equal.
diff --git a/modules/paginator/paginator_test.go b/modules/paginator/paginator_test.go
index 8a56ee5121..ed46ecea94 100644
--- a/modules/paginator/paginator_test.go
+++ b/modules/paginator/paginator_test.go
@@ -76,9 +76,7 @@ func TestPaginator(t *testing.T) {
t.Run("Only current page", func(t *testing.T) {
p := New(0, 10, 1, 1)
pages := p.Pages()
- assert.Len(t, pages, 1)
- assert.Equal(t, 1, pages[0].Num())
- assert.True(t, pages[0].IsCurrent())
+ assert.Empty(t, pages) // no "total", so no pages
p = New(1, 10, 1, 1)
pages = p.Pages()
diff --git a/modules/private/actions.go b/modules/private/actions.go
index 311a283650..e68f2f85b0 100644
--- a/modules/private/actions.go
+++ b/modules/private/actions.go
@@ -17,7 +17,7 @@ type GenerateTokenRequest struct {
func GenerateActionsRunnerToken(ctx context.Context, scope string) (*ResponseText, ResponseExtra) {
reqURL := setting.LocalURL + "api/internal/actions/generate_actions_runner_token"
- req := newInternalRequest(ctx, reqURL, "POST", GenerateTokenRequest{
+ req := newInternalRequestAPI(ctx, reqURL, "POST", GenerateTokenRequest{
Scope: scope,
})
diff --git a/modules/private/hook.go b/modules/private/hook.go
index 745c200619..215996b9b9 100644
--- a/modules/private/hook.go
+++ b/modules/private/hook.go
@@ -7,9 +7,9 @@ import (
"context"
"fmt"
"net/url"
- "time"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
)
@@ -82,29 +82,32 @@ type HookProcReceiveRefResult struct {
HeadBranch string
}
+func newInternalRequestAPIForHooks(ctx context.Context, hookName, ownerName, repoName string, opts HookOptions) *httplib.Request {
+ reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/%s/%s/%s", hookName, url.PathEscape(ownerName), url.PathEscape(repoName))
+ req := newInternalRequestAPI(ctx, reqURL, "POST", opts)
+ // This "timeout" applies to http.Client's timeout: A Timeout of zero means no timeout.
+ // This "timeout" was previously set to `time.Duration(60+len(opts.OldCommitIDs))` seconds, but it caused unnecessary timeout failures.
+ // It should be good enough to remove the client side timeout, only respect the "ctx" and server side timeout.
+ req.SetReadWriteTimeout(0)
+ return req
+}
+
// HookPreReceive check whether the provided commits are allowed
func HookPreReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) ResponseExtra {
- reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/pre-receive/%s/%s", url.PathEscape(ownerName), url.PathEscape(repoName))
- req := newInternalRequest(ctx, reqURL, "POST", opts)
- req.SetReadWriteTimeout(time.Duration(60+len(opts.OldCommitIDs)) * time.Second)
+ req := newInternalRequestAPIForHooks(ctx, "pre-receive", ownerName, repoName, opts)
_, extra := requestJSONResp(req, &ResponseText{})
return extra
}
// HookPostReceive updates services and users
func HookPostReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) (*HookPostReceiveResult, ResponseExtra) {
- reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/post-receive/%s/%s", url.PathEscape(ownerName), url.PathEscape(repoName))
- req := newInternalRequest(ctx, reqURL, "POST", opts)
- req.SetReadWriteTimeout(time.Duration(60+len(opts.OldCommitIDs)) * time.Second)
+ req := newInternalRequestAPIForHooks(ctx, "post-receive", ownerName, repoName, opts)
return requestJSONResp(req, &HookPostReceiveResult{})
}
// HookProcReceive proc-receive hook
func HookProcReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) (*HookProcReceiveResult, ResponseExtra) {
- reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/proc-receive/%s/%s", url.PathEscape(ownerName), url.PathEscape(repoName))
-
- req := newInternalRequest(ctx, reqURL, "POST", opts)
- req.SetReadWriteTimeout(time.Duration(60+len(opts.OldCommitIDs)) * time.Second)
+ req := newInternalRequestAPIForHooks(ctx, "proc-receive", ownerName, repoName, opts)
return requestJSONResp(req, &HookProcReceiveResult{})
}
@@ -115,7 +118,7 @@ func SetDefaultBranch(ctx context.Context, ownerName, repoName, branch string) R
url.PathEscape(repoName),
url.PathEscape(branch),
)
- req := newInternalRequest(ctx, reqURL, "POST")
+ req := newInternalRequestAPI(ctx, reqURL, "POST")
_, extra := requestJSONResp(req, &ResponseText{})
return extra
}
@@ -123,7 +126,7 @@ func SetDefaultBranch(ctx context.Context, ownerName, repoName, branch string) R
// SSHLog sends ssh error log response
func SSHLog(ctx context.Context, isErr bool, msg string) error {
reqURL := setting.LocalURL + "api/internal/ssh/log"
- req := newInternalRequest(ctx, reqURL, "POST", &SSHLogOption{IsError: isErr, Message: msg})
+ req := newInternalRequestAPI(ctx, reqURL, "POST", &SSHLogOption{IsError: isErr, Message: msg})
_, extra := requestJSONResp(req, &ResponseText{})
return extra.Error
}
diff --git a/modules/private/internal.go b/modules/private/internal.go
index c7e7773524..e599c6eb8e 100644
--- a/modules/private/internal.go
+++ b/modules/private/internal.go
@@ -6,7 +6,6 @@ package private
import (
"context"
"crypto/tls"
- "fmt"
"net"
"net/http"
"os"
@@ -34,16 +33,20 @@ func getClientIP() string {
return strings.Fields(sshConnEnv)[0]
}
-func newInternalRequest(ctx context.Context, url, method string, body ...any) *httplib.Request {
+func NewInternalRequest(ctx context.Context, url, method string) *httplib.Request {
if setting.InternalToken == "" {
log.Fatal(`The INTERNAL_TOKEN setting is missing from the configuration file: %q.
Ensure you are running in the correct environment or set the correct configuration file with -c.`, setting.CustomConf)
}
+ if !strings.HasPrefix(url, setting.LocalURL) {
+ log.Fatal("Invalid internal request URL: %q", url)
+ }
+
req := httplib.NewRequest(url, method).
SetContext(ctx).
Header("X-Real-IP", getClientIP()).
- Header("X-Gitea-Internal-Auth", fmt.Sprintf("Bearer %s", setting.InternalToken)).
+ Header("X-Gitea-Internal-Auth", "Bearer "+setting.InternalToken).
SetTLSClientConfig(&tls.Config{
InsecureSkipVerify: true,
ServerName: setting.Domain,
@@ -82,13 +85,17 @@ Ensure you are running in the correct environment or set the correct configurati
},
})
}
+ return req
+}
+func newInternalRequestAPI(ctx context.Context, url, method string, body ...any) *httplib.Request {
+ req := NewInternalRequest(ctx, url, method)
if len(body) == 1 {
req.Header("Content-Type", "application/json")
jsonBytes, _ := json.Marshal(body[0])
req.Body(jsonBytes)
} else if len(body) > 1 {
- log.Fatal("Too many arguments for newInternalRequest")
+ log.Fatal("Too many arguments for newInternalRequestAPI")
}
req.SetTimeout(10*time.Second, 60*time.Second)
diff --git a/modules/private/key.go b/modules/private/key.go
index dcd1714856..114683b343 100644
--- a/modules/private/key.go
+++ b/modules/private/key.go
@@ -14,7 +14,7 @@ import (
func UpdatePublicKeyInRepo(ctx context.Context, keyID, repoID int64) error {
// Ask for running deliver hook and test pull request tasks.
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/ssh/%d/update/%d", keyID, repoID)
- req := newInternalRequest(ctx, reqURL, "POST")
+ req := newInternalRequestAPI(ctx, reqURL, "POST")
_, extra := requestJSONResp(req, &ResponseText{})
return extra.Error
}
@@ -24,7 +24,7 @@ func UpdatePublicKeyInRepo(ctx context.Context, keyID, repoID int64) error {
func AuthorizedPublicKeyByContent(ctx context.Context, content string) (*ResponseText, ResponseExtra) {
// Ask for running deliver hook and test pull request tasks.
reqURL := setting.LocalURL + "api/internal/ssh/authorized_keys"
- req := newInternalRequest(ctx, reqURL, "POST")
+ req := newInternalRequestAPI(ctx, reqURL, "POST")
req.Param("content", content)
return requestJSONResp(req, &ResponseText{})
}
diff --git a/modules/private/mail.go b/modules/private/mail.go
index 08de5b7e28..3904e37bea 100644
--- a/modules/private/mail.go
+++ b/modules/private/mail.go
@@ -23,7 +23,7 @@ type Email struct {
func SendEmail(ctx context.Context, subject, message string, to []string) (*ResponseText, ResponseExtra) {
reqURL := setting.LocalURL + "api/internal/mail/send"
- req := newInternalRequest(ctx, reqURL, "POST", Email{
+ req := newInternalRequestAPI(ctx, reqURL, "POST", Email{
Subject: subject,
Message: message,
To: to,
diff --git a/modules/private/manager.go b/modules/private/manager.go
index 6055e553bd..e3d5ad57e0 100644
--- a/modules/private/manager.go
+++ b/modules/private/manager.go
@@ -18,21 +18,21 @@ import (
// Shutdown calls the internal shutdown function
func Shutdown(ctx context.Context) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/shutdown"
- req := newInternalRequest(ctx, reqURL, "POST")
+ req := newInternalRequestAPI(ctx, reqURL, "POST")
return requestJSONClientMsg(req, "Shutting down")
}
// Restart calls the internal restart function
func Restart(ctx context.Context) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/restart"
- req := newInternalRequest(ctx, reqURL, "POST")
+ req := newInternalRequestAPI(ctx, reqURL, "POST")
return requestJSONClientMsg(req, "Restarting")
}
// ReloadTemplates calls the internal reload-templates function
func ReloadTemplates(ctx context.Context) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/reload-templates"
- req := newInternalRequest(ctx, reqURL, "POST")
+ req := newInternalRequestAPI(ctx, reqURL, "POST")
return requestJSONClientMsg(req, "Reloaded")
}
@@ -45,7 +45,7 @@ type FlushOptions struct {
// FlushQueues calls the internal flush-queues function
func FlushQueues(ctx context.Context, timeout time.Duration, nonBlocking bool) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/flush-queues"
- req := newInternalRequest(ctx, reqURL, "POST", FlushOptions{Timeout: timeout, NonBlocking: nonBlocking})
+ req := newInternalRequestAPI(ctx, reqURL, "POST", FlushOptions{Timeout: timeout, NonBlocking: nonBlocking})
if timeout > 0 {
req.SetReadWriteTimeout(timeout + 10*time.Second)
}
@@ -55,28 +55,28 @@ func FlushQueues(ctx context.Context, timeout time.Duration, nonBlocking bool) R
// PauseLogging pauses logging
func PauseLogging(ctx context.Context) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/pause-logging"
- req := newInternalRequest(ctx, reqURL, "POST")
+ req := newInternalRequestAPI(ctx, reqURL, "POST")
return requestJSONClientMsg(req, "Logging Paused")
}
// ResumeLogging resumes logging
func ResumeLogging(ctx context.Context) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/resume-logging"
- req := newInternalRequest(ctx, reqURL, "POST")
+ req := newInternalRequestAPI(ctx, reqURL, "POST")
return requestJSONClientMsg(req, "Logging Restarted")
}
// ReleaseReopenLogging releases and reopens logging files
func ReleaseReopenLogging(ctx context.Context) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/release-and-reopen-logging"
- req := newInternalRequest(ctx, reqURL, "POST")
+ req := newInternalRequestAPI(ctx, reqURL, "POST")
return requestJSONClientMsg(req, "Logging Restarted")
}
// SetLogSQL sets database logging
func SetLogSQL(ctx context.Context, on bool) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/set-log-sql?on=" + strconv.FormatBool(on)
- req := newInternalRequest(ctx, reqURL, "POST")
+ req := newInternalRequestAPI(ctx, reqURL, "POST")
return requestJSONClientMsg(req, "Log SQL setting set")
}
@@ -91,7 +91,7 @@ type LoggerOptions struct {
// AddLogger adds a logger
func AddLogger(ctx context.Context, logger, writer, mode string, config map[string]any) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/manager/add-logger"
- req := newInternalRequest(ctx, reqURL, "POST", LoggerOptions{
+ req := newInternalRequestAPI(ctx, reqURL, "POST", LoggerOptions{
Logger: logger,
Writer: writer,
Mode: mode,
@@ -103,7 +103,7 @@ func AddLogger(ctx context.Context, logger, writer, mode string, config map[stri
// RemoveLogger removes a logger
func RemoveLogger(ctx context.Context, logger, writer string) ResponseExtra {
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/manager/remove-logger/%s/%s", url.PathEscape(logger), url.PathEscape(writer))
- req := newInternalRequest(ctx, reqURL, "POST")
+ req := newInternalRequestAPI(ctx, reqURL, "POST")
return requestJSONClientMsg(req, "Removed")
}
@@ -111,7 +111,7 @@ func RemoveLogger(ctx context.Context, logger, writer string) ResponseExtra {
func Processes(ctx context.Context, out io.Writer, flat, noSystem, stacktraces, json bool, cancel string) ResponseExtra {
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/manager/processes?flat=%t&no-system=%t&stacktraces=%t&json=%t&cancel-pid=%s", flat, noSystem, stacktraces, json, url.QueryEscape(cancel))
- req := newInternalRequest(ctx, reqURL, "GET")
+ req := newInternalRequestAPI(ctx, reqURL, "GET")
callback := func(resp *http.Response, extra *ResponseExtra) {
_, extra.Error = io.Copy(out, resp.Body)
}
diff --git a/modules/private/restore_repo.go b/modules/private/restore_repo.go
index 496209d3cb..9c3a008142 100644
--- a/modules/private/restore_repo.go
+++ b/modules/private/restore_repo.go
@@ -24,7 +24,7 @@ type RestoreParams struct {
func RestoreRepo(ctx context.Context, repoDir, ownerName, repoName string, units []string, validation bool) ResponseExtra {
reqURL := setting.LocalURL + "api/internal/restore_repo"
- req := newInternalRequest(ctx, reqURL, "POST", RestoreParams{
+ req := newInternalRequestAPI(ctx, reqURL, "POST", RestoreParams{
RepoDir: repoDir,
OwnerName: ownerName,
RepoName: repoName,
diff --git a/modules/private/serv.go b/modules/private/serv.go
index 480a446954..b1dafbd81b 100644
--- a/modules/private/serv.go
+++ b/modules/private/serv.go
@@ -23,7 +23,7 @@ type KeyAndOwner struct {
// ServNoCommand returns information about the provided key
func ServNoCommand(ctx context.Context, keyID int64) (*asymkey_model.PublicKey, *user_model.User, error) {
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/serv/none/%d", keyID)
- req := newInternalRequest(ctx, reqURL, "GET")
+ req := newInternalRequestAPI(ctx, reqURL, "GET")
keyAndOwner, extra := requestJSONResp(req, &KeyAndOwner{})
if extra.HasError() {
return nil, nil, extra.Error
@@ -46,18 +46,16 @@ type ServCommandResults struct {
}
// ServCommand preps for a serv call
-func ServCommand(ctx context.Context, keyID int64, ownerName, repoName string, mode perm.AccessMode, verbs ...string) (*ServCommandResults, ResponseExtra) {
+func ServCommand(ctx context.Context, keyID int64, ownerName, repoName string, mode perm.AccessMode, verb, lfsVerb string) (*ServCommandResults, ResponseExtra) {
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/serv/command/%d/%s/%s?mode=%d",
keyID,
url.PathEscape(ownerName),
url.PathEscape(repoName),
mode,
)
- for _, verb := range verbs {
- if verb != "" {
- reqURL += fmt.Sprintf("&verb=%s", url.QueryEscape(verb))
- }
- }
- req := newInternalRequest(ctx, reqURL, "GET")
+ reqURL += "&verb=" + url.QueryEscape(verb)
+ // reqURL += "&lfs_verb=" + url.QueryEscape(lfsVerb) // TODO: actually there is no use of this parameter. In the future, the URL construction should be more flexible
+ _ = lfsVerb
+ req := newInternalRequestAPI(ctx, reqURL, "GET")
return requestJSONResp(req, &ServCommandResults{})
}
diff --git a/modules/process/manager_test.go b/modules/process/manager_test.go
index 36b2a912ea..0d637c8acc 100644
--- a/modules/process/manager_test.go
+++ b/modules/process/manager_test.go
@@ -23,7 +23,7 @@ func TestGetManager(t *testing.T) {
func TestManager_AddContext(t *testing.T) {
pm := Manager{processMap: make(map[IDType]*process), next: 1}
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
p1Ctx, _, finished := pm.AddContext(ctx, "foo")
@@ -42,7 +42,7 @@ func TestManager_AddContext(t *testing.T) {
func TestManager_Cancel(t *testing.T) {
pm := Manager{processMap: make(map[IDType]*process), next: 1}
- ctx, _, finished := pm.AddContext(context.Background(), "foo")
+ ctx, _, finished := pm.AddContext(t.Context(), "foo")
defer finished()
pm.Cancel(GetPID(ctx))
@@ -54,7 +54,7 @@ func TestManager_Cancel(t *testing.T) {
}
finished()
- ctx, cancel, finished := pm.AddContext(context.Background(), "foo")
+ ctx, cancel, finished := pm.AddContext(t.Context(), "foo")
defer finished()
cancel()
@@ -70,7 +70,7 @@ func TestManager_Cancel(t *testing.T) {
func TestManager_Remove(t *testing.T) {
pm := Manager{processMap: make(map[IDType]*process), next: 1}
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
p1Ctx, _, finished := pm.AddContext(ctx, "foo")
diff --git a/modules/proxyprotocol/errors.go b/modules/proxyprotocol/errors.go
index 5439a86bd8..f76c82b7f6 100644
--- a/modules/proxyprotocol/errors.go
+++ b/modules/proxyprotocol/errors.go
@@ -20,7 +20,7 @@ type ErrBadAddressType struct {
}
func (e *ErrBadAddressType) Error() string {
- return fmt.Sprintf("Unexpected proxy header address type: %s", e.Address)
+ return "Unexpected proxy header address type: " + e.Address
}
// ErrBadRemote is an error demonstrating a bad proxy header with bad Remote
diff --git a/modules/public/public.go b/modules/public/public.go
index abc6b46158..a7eace1538 100644
--- a/modules/public/public.go
+++ b/modules/public/public.go
@@ -44,7 +44,7 @@ func FileHandlerFunc() http.HandlerFunc {
func parseAcceptEncoding(val string) container.Set[string] {
parts := strings.Split(val, ";")
types := make(container.Set[string])
- for _, v := range strings.Split(parts[0], ",") {
+ for v := range strings.SplitSeq(parts[0], ",") {
types.Add(strings.TrimSpace(v))
}
return types
@@ -86,33 +86,28 @@ func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem,
return
}
- serveContent(w, req, fi, fi.ModTime(), f)
+ servePublicAsset(w, req, fi, fi.ModTime(), f)
}
-type GzipBytesProvider interface {
- GzipBytes() []byte
-}
-
-// serveContent serve http content
-func serveContent(w http.ResponseWriter, req *http.Request, fi os.FileInfo, modtime time.Time, content io.ReadSeeker) {
+// servePublicAsset serve http content
+func servePublicAsset(w http.ResponseWriter, req *http.Request, fi os.FileInfo, modtime time.Time, content io.ReadSeeker) {
setWellKnownContentType(w, fi.Name())
-
+ httpcache.SetCacheControlInHeader(w.Header(), httpcache.CacheControlForPublicStatic())
encodings := parseAcceptEncoding(req.Header.Get("Accept-Encoding"))
- if encodings.Contains("gzip") {
- // try to provide gzip content directly from bindata (provided by vfsgenÛ°CompressedFileInfo)
- if compressed, ok := fi.(GzipBytesProvider); ok {
- rdGzip := bytes.NewReader(compressed.GzipBytes())
+ fiEmbedded, _ := fi.(assetfs.EmbeddedFileInfo)
+ if encodings.Contains("gzip") && fiEmbedded != nil {
+ // try to provide gzip content directly from bindata
+ if gzipBytes, ok := fiEmbedded.GetGzipContent(); ok {
+ rdGzip := bytes.NewReader(gzipBytes)
// all gzipped static files (from bindata) are managed by Gitea, so we can make sure every file has the correct ext name
// then we can get the correct Content-Type, we do not need to do http.DetectContentType on the decompressed data
if w.Header().Get("Content-Type") == "" {
w.Header().Set("Content-Type", "application/octet-stream")
}
w.Header().Set("Content-Encoding", "gzip")
- httpcache.ServeContentWithCacheControl(w, req, fi.Name(), modtime, rdGzip)
+ http.ServeContent(w, req, fi.Name(), modtime, rdGzip)
return
}
}
-
- httpcache.ServeContentWithCacheControl(w, req, fi.Name(), modtime, content)
- return
+ http.ServeContent(w, req, fi.Name(), modtime, content)
}
diff --git a/modules/public/public_bindata.go b/modules/public/public_bindata.go
index 4878f88ad1..2dcf3e72e4 100644
--- a/modules/public/public_bindata.go
+++ b/modules/public/public_bindata.go
@@ -5,4 +5,19 @@
package public
-//go:generate go run ../../build/generate-bindata.go ../../public public bindata.go true
+//go:generate go run ../../build/generate-bindata.go ../../public bindata.dat
+
+import (
+ "sync"
+
+ _ "embed"
+
+ "code.gitea.io/gitea/modules/assetfs"
+)
+
+//go:embed bindata.dat
+var bindata []byte
+
+var BuiltinAssets = sync.OnceValue(func() *assetfs.Layer {
+ return assetfs.Bindata("builtin(bindata)", assetfs.NewEmbeddedFS(bindata))
+})
diff --git a/modules/public/serve_dynamic.go b/modules/public/public_dynamic.go
index a668b17c34..a668b17c34 100644
--- a/modules/public/serve_dynamic.go
+++ b/modules/public/public_dynamic.go
diff --git a/modules/public/serve_static.go b/modules/public/serve_static.go
deleted file mode 100644
index e79085021e..0000000000
--- a/modules/public/serve_static.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2016 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-//go:build bindata
-
-package public
-
-import (
- "time"
-
- "code.gitea.io/gitea/modules/assetfs"
- "code.gitea.io/gitea/modules/timeutil"
-)
-
-var _ GzipBytesProvider = (*vfsgenÛ°CompressedFileInfo)(nil)
-
-// GlobalModTime provide a global mod time for embedded asset files
-func GlobalModTime(filename string) time.Time {
- return timeutil.GetExecutableModTime()
-}
-
-func BuiltinAssets() *assetfs.Layer {
- return assetfs.Bindata("builtin(bindata)", Assets)
-}
diff --git a/modules/queue/base_levelqueue_common.go b/modules/queue/base_levelqueue_common.go
index 78d3b85a8a..d37093b84d 100644
--- a/modules/queue/base_levelqueue_common.go
+++ b/modules/queue/base_levelqueue_common.go
@@ -83,7 +83,7 @@ func prepareLevelDB(cfg *BaseConfig) (conn string, db *leveldb.DB, err error) {
}
conn = cfg.ConnStr
}
- for i := 0; i < 10; i++ {
+ for range 10 {
if db, err = nosql.GetManager().GetLevelDB(conn); err == nil {
break
}
diff --git a/modules/queue/base_levelqueue_test.go b/modules/queue/base_levelqueue_test.go
index b881802ca2..05d8208560 100644
--- a/modules/queue/base_levelqueue_test.go
+++ b/modules/queue/base_levelqueue_test.go
@@ -11,6 +11,7 @@ import (
"gitea.com/lunny/levelqueue"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
"github.com/syndtr/goleveldb/leveldb"
)
@@ -29,9 +30,7 @@ func TestCorruptedLevelQueue(t *testing.T) {
// sometimes the levelqueue could be in a corrupted state, this test is to make sure it can recover from it
dbDir := t.TempDir() + "/levelqueue-test"
db, err := leveldb.OpenFile(dbDir, nil)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
defer db.Close()
assert.NoError(t, db.Put([]byte("other-key"), []byte("other-value"), nil))
diff --git a/modules/queue/base_redis.go b/modules/queue/base_redis.go
index a1e234943d..bea0fd7a98 100644
--- a/modules/queue/base_redis.go
+++ b/modules/queue/base_redis.go
@@ -29,7 +29,7 @@ func newBaseRedisGeneric(cfg *BaseConfig, unique bool) (baseQueue, error) {
client := nosql.GetManager().GetRedisClient(cfg.ConnStr)
var err error
- for i := 0; i < 10; i++ {
+ for range 10 {
err = client.Ping(graceful.GetManager().ShutdownContext()).Err()
if err == nil {
break
diff --git a/modules/queue/base_redis_test.go b/modules/queue/base_redis_test.go
index 19fbccbc8f..6478988d7f 100644
--- a/modules/queue/base_redis_test.go
+++ b/modules/queue/base_redis_test.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func waitRedisReady(conn string, dur time.Duration) (ready bool) {
@@ -61,9 +62,7 @@ func TestBaseRedis(t *testing.T) {
return
}
assert.NoError(t, redisServer.Start())
- if !assert.True(t, waitRedisReady("redis://127.0.0.1:6379/0", 5*time.Second), "start redis-server") {
- return
- }
+ require.True(t, waitRedisReady("redis://127.0.0.1:6379/0", 5*time.Second), "start redis-server")
}
testQueueBasic(t, newBaseRedisSimple, toBaseConfig("baseRedis", setting.QueueSettings{Length: 10}), false)
diff --git a/modules/queue/base_test.go b/modules/queue/base_test.go
index 01b52b3c16..8e7c18d740 100644
--- a/modules/queue/base_test.go
+++ b/modules/queue/base_test.go
@@ -17,11 +17,11 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error)
q, err := newFn(cfg)
assert.NoError(t, err)
- ctx := context.Background()
+ ctx := t.Context()
_ = q.RemoveAll(ctx)
cnt, err := q.Len(ctx)
assert.NoError(t, err)
- assert.EqualValues(t, 0, cnt)
+ assert.Equal(t, 0, cnt)
// push the first item
err = q.PushItem(ctx, []byte("foo"))
@@ -29,7 +29,7 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error)
cnt, err = q.Len(ctx)
assert.NoError(t, err)
- assert.EqualValues(t, 1, cnt)
+ assert.Equal(t, 1, cnt)
// push a duplicate item
err = q.PushItem(ctx, []byte("foo"))
@@ -45,10 +45,10 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error)
has, err := q.HasItem(ctx, []byte("foo"))
assert.NoError(t, err)
if !isUnique {
- assert.EqualValues(t, 2, cnt)
+ assert.Equal(t, 2, cnt)
assert.False(t, has) // non-unique queues don't check for duplicates
} else {
- assert.EqualValues(t, 1, cnt)
+ assert.Equal(t, 1, cnt)
assert.True(t, has)
}
@@ -59,18 +59,18 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error)
// pop the first item (and the duplicate if non-unique)
it, err := q.PopItem(ctx)
assert.NoError(t, err)
- assert.EqualValues(t, "foo", string(it))
+ assert.Equal(t, "foo", string(it))
if !isUnique {
it, err = q.PopItem(ctx)
assert.NoError(t, err)
- assert.EqualValues(t, "foo", string(it))
+ assert.Equal(t, "foo", string(it))
}
// pop another item
it, err = q.PopItem(ctx)
assert.NoError(t, err)
- assert.EqualValues(t, "bar", string(it))
+ assert.Equal(t, "bar", string(it))
// pop an empty queue (timeout, cancel)
ctxTimed, cancel := context.WithTimeout(ctx, 10*time.Millisecond)
@@ -87,7 +87,7 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error)
// test blocking push if queue is full
for i := 0; i < cfg.Length; i++ {
- err = q.PushItem(ctx, []byte(fmt.Sprintf("item-%d", i)))
+ err = q.PushItem(ctx, fmt.Appendf(nil, "item-%d", i))
assert.NoError(t, err)
}
ctxTimed, cancel = context.WithTimeout(ctx, 10*time.Millisecond)
@@ -107,13 +107,13 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error)
// remove all
cnt, err = q.Len(ctx)
assert.NoError(t, err)
- assert.EqualValues(t, cfg.Length, cnt)
+ assert.Equal(t, cfg.Length, cnt)
_ = q.RemoveAll(ctx)
cnt, err = q.Len(ctx)
assert.NoError(t, err)
- assert.EqualValues(t, 0, cnt)
+ assert.Equal(t, 0, cnt)
})
}
@@ -121,12 +121,12 @@ func TestBaseDummy(t *testing.T) {
q, err := newBaseDummy(&BaseConfig{}, true)
assert.NoError(t, err)
- ctx := context.Background()
+ ctx := t.Context()
assert.NoError(t, q.PushItem(ctx, []byte("foo")))
cnt, err := q.Len(ctx)
assert.NoError(t, err)
- assert.EqualValues(t, 0, cnt)
+ assert.Equal(t, 0, cnt)
has, err := q.HasItem(ctx, []byte("foo"))
assert.NoError(t, err)
diff --git a/modules/queue/manager.go b/modules/queue/manager.go
index 079e2bee7a..ae6c51872d 100644
--- a/modules/queue/manager.go
+++ b/modules/queue/manager.go
@@ -6,6 +6,7 @@ package queue
import (
"context"
"errors"
+ "maps"
"sync"
"time"
@@ -70,9 +71,7 @@ func (m *Manager) ManagedQueues() map[int64]ManagedWorkerPoolQueue {
defer m.mu.Unlock()
queues := make(map[int64]ManagedWorkerPoolQueue, len(m.Queues))
- for k, v := range m.Queues {
- queues[k] = v
- }
+ maps.Copy(queues, m.Queues)
return queues
}
diff --git a/modules/queue/manager_test.go b/modules/queue/manager_test.go
index 15dd1b4f2f..fda498cc84 100644
--- a/modules/queue/manager_test.go
+++ b/modules/queue/manager_test.go
@@ -4,7 +4,6 @@
package queue
import (
- "context"
"path/filepath"
"testing"
@@ -48,7 +47,7 @@ CONN_STR = redis://
assert.Equal(t, filepath.Join(setting.AppDataPath, "queues/common"), q.baseConfig.DataFullDir)
assert.Equal(t, 100000, q.baseConfig.Length)
assert.Equal(t, 20, q.batchLength)
- assert.Equal(t, "", q.baseConfig.ConnStr)
+ assert.Empty(t, q.baseConfig.ConnStr)
assert.Equal(t, "default_queue", q.baseConfig.QueueFullName)
assert.Equal(t, "default_queue_unique", q.baseConfig.SetFullName)
assert.NotZero(t, q.GetWorkerMaxNumber())
@@ -80,7 +79,7 @@ MAX_WORKERS = 123
assert.NoError(t, err)
- q1 := createWorkerPoolQueue[string](context.Background(), "no-such", cfgProvider, nil, false)
+ q1 := createWorkerPoolQueue[string](t.Context(), "no-such", cfgProvider, nil, false)
assert.Equal(t, "no-such", q1.GetName())
assert.Equal(t, "dummy", q1.GetType()) // no handler, so it becomes dummy
assert.Equal(t, filepath.Join(setting.AppDataPath, "queues/dir1"), q1.baseConfig.DataFullDir)
@@ -96,13 +95,13 @@ MAX_WORKERS = 123
assert.Equal(t, "string", q1.GetItemTypeName())
qid1 := GetManager().qidCounter
- q2 := createWorkerPoolQueue(context.Background(), "sub", cfgProvider, func(s ...int) (unhandled []int) { return nil }, false)
+ q2 := createWorkerPoolQueue(t.Context(), "sub", cfgProvider, func(s ...int) (unhandled []int) { return nil }, false)
assert.Equal(t, "sub", q2.GetName())
assert.Equal(t, "level", q2.GetType())
assert.Equal(t, filepath.Join(setting.AppDataPath, "queues/dir2"), q2.baseConfig.DataFullDir)
assert.Equal(t, 102, q2.baseConfig.Length)
assert.Equal(t, 22, q2.batchLength)
- assert.Equal(t, "", q2.baseConfig.ConnStr)
+ assert.Empty(t, q2.baseConfig.ConnStr)
assert.Equal(t, "sub_q2", q2.baseConfig.QueueFullName)
assert.Equal(t, "sub_q2_u2", q2.baseConfig.SetFullName)
assert.Equal(t, 123, q2.GetWorkerMaxNumber())
@@ -118,7 +117,7 @@ MAX_WORKERS = 123
assert.Equal(t, 120, q1.workerMaxNum)
stop := runWorkerPoolQueue(q2)
- assert.NoError(t, GetManager().GetManagedQueue(qid2).FlushWithContext(context.Background(), 0))
- assert.NoError(t, GetManager().FlushAll(context.Background(), 0))
+ assert.NoError(t, GetManager().GetManagedQueue(qid2).FlushWithContext(t.Context(), 0))
+ assert.NoError(t, GetManager().FlushAll(t.Context(), 0))
stop()
}
diff --git a/modules/queue/workerqueue_test.go b/modules/queue/workerqueue_test.go
index c0841a1752..a6c369d5f9 100644
--- a/modules/queue/workerqueue_test.go
+++ b/modules/queue/workerqueue_test.go
@@ -4,7 +4,6 @@
package queue
import (
- "context"
"slices"
"strconv"
"sync"
@@ -58,15 +57,15 @@ func TestWorkerPoolQueueUnhandled(t *testing.T) {
testRecorder.Record("push:%v", i)
assert.NoError(t, q.Push(i))
}
- assert.NoError(t, q.FlushWithContext(context.Background(), 0))
+ assert.NoError(t, q.FlushWithContext(t.Context(), 0))
stop()
ok := true
for i := 0; i < queueSetting.Length; i++ {
if i%2 == 0 {
- ok = ok && assert.EqualValues(t, 2, m[i], "test %s: item %d", t.Name(), i)
+ ok = ok && assert.Equal(t, 2, m[i], "test %s: item %d", t.Name(), i)
} else {
- ok = ok && assert.EqualValues(t, 1, m[i], "test %s: item %d", t.Name(), i)
+ ok = ok && assert.Equal(t, 1, m[i], "test %s: item %d", t.Name(), i)
}
}
if !ok {
@@ -78,17 +77,17 @@ func TestWorkerPoolQueueUnhandled(t *testing.T) {
runCount := 2 // we can run these tests even hundreds times to see its stability
t.Run("1/1", func(t *testing.T) {
- for i := 0; i < runCount; i++ {
+ for range runCount {
test(t, setting.QueueSettings{BatchLength: 1, MaxWorkers: 1})
}
})
t.Run("3/1", func(t *testing.T) {
- for i := 0; i < runCount; i++ {
+ for range runCount {
test(t, setting.QueueSettings{BatchLength: 3, MaxWorkers: 1})
}
})
t.Run("4/5", func(t *testing.T) {
- for i := 0; i < runCount; i++ {
+ for range runCount {
test(t, setting.QueueSettings{BatchLength: 4, MaxWorkers: 5})
}
})
@@ -97,17 +96,17 @@ func TestWorkerPoolQueueUnhandled(t *testing.T) {
func TestWorkerPoolQueuePersistence(t *testing.T) {
runCount := 2 // we can run these tests even hundreds times to see its stability
t.Run("1/1", func(t *testing.T) {
- for i := 0; i < runCount; i++ {
+ for range runCount {
testWorkerPoolQueuePersistence(t, setting.QueueSettings{BatchLength: 1, MaxWorkers: 1, Length: 100})
}
})
t.Run("3/1", func(t *testing.T) {
- for i := 0; i < runCount; i++ {
+ for range runCount {
testWorkerPoolQueuePersistence(t, setting.QueueSettings{BatchLength: 3, MaxWorkers: 1, Length: 100})
}
})
t.Run("4/5", func(t *testing.T) {
- for i := 0; i < runCount; i++ {
+ for range runCount {
testWorkerPoolQueuePersistence(t, setting.QueueSettings{BatchLength: 4, MaxWorkers: 5, Length: 100})
}
})
@@ -142,7 +141,7 @@ func testWorkerPoolQueuePersistence(t *testing.T, queueSetting setting.QueueSett
q, _ := newWorkerPoolQueueForTest("pr_patch_checker_test", queueSetting, testHandler, true)
stop := runWorkerPoolQueue(q)
- for i := 0; i < testCount; i++ {
+ for i := range testCount {
_ = q.Push("task-" + strconv.Itoa(i))
}
close(startWhenAllReady)
@@ -166,7 +165,7 @@ func testWorkerPoolQueuePersistence(t *testing.T, queueSetting setting.QueueSett
q, _ := newWorkerPoolQueueForTest("pr_patch_checker_test", queueSetting, testHandler, true)
stop := runWorkerPoolQueue(q)
- assert.NoError(t, q.FlushWithContext(context.Background(), 0))
+ assert.NoError(t, q.FlushWithContext(t.Context(), 0))
stop()
}
@@ -174,7 +173,7 @@ func testWorkerPoolQueuePersistence(t *testing.T, queueSetting setting.QueueSett
assert.NotEmpty(t, tasksQ1)
assert.NotEmpty(t, tasksQ2)
- assert.EqualValues(t, testCount, len(tasksQ1)+len(tasksQ2))
+ assert.Equal(t, testCount, len(tasksQ1)+len(tasksQ2))
}
func TestWorkerPoolQueueActiveWorkers(t *testing.T) {
@@ -187,34 +186,34 @@ func TestWorkerPoolQueueActiveWorkers(t *testing.T) {
q, _ := newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 1, Length: 100}, handler, false)
stop := runWorkerPoolQueue(q)
- for i := 0; i < 5; i++ {
+ for i := range 5 {
assert.NoError(t, q.Push(i))
}
time.Sleep(50 * time.Millisecond)
- assert.EqualValues(t, 1, q.GetWorkerNumber())
- assert.EqualValues(t, 1, q.GetWorkerActiveNumber())
+ assert.Equal(t, 1, q.GetWorkerNumber())
+ assert.Equal(t, 1, q.GetWorkerActiveNumber())
time.Sleep(500 * time.Millisecond)
- assert.EqualValues(t, 1, q.GetWorkerNumber())
- assert.EqualValues(t, 0, q.GetWorkerActiveNumber())
+ assert.Equal(t, 1, q.GetWorkerNumber())
+ assert.Equal(t, 0, q.GetWorkerActiveNumber())
time.Sleep(workerIdleDuration)
- assert.EqualValues(t, 1, q.GetWorkerNumber()) // there is at least one worker after the queue begins working
+ assert.Equal(t, 1, q.GetWorkerNumber()) // there is at least one worker after the queue begins working
stop()
q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 3, Length: 100}, handler, false)
stop = runWorkerPoolQueue(q)
- for i := 0; i < 15; i++ {
+ for i := range 15 {
assert.NoError(t, q.Push(i))
}
time.Sleep(50 * time.Millisecond)
- assert.EqualValues(t, 3, q.GetWorkerNumber())
- assert.EqualValues(t, 3, q.GetWorkerActiveNumber())
+ assert.Equal(t, 3, q.GetWorkerNumber())
+ assert.Equal(t, 3, q.GetWorkerActiveNumber())
time.Sleep(500 * time.Millisecond)
- assert.EqualValues(t, 3, q.GetWorkerNumber())
- assert.EqualValues(t, 0, q.GetWorkerActiveNumber())
+ assert.Equal(t, 3, q.GetWorkerNumber())
+ assert.Equal(t, 0, q.GetWorkerActiveNumber())
time.Sleep(workerIdleDuration)
- assert.EqualValues(t, 1, q.GetWorkerNumber()) // there is at least one worker after the queue begins working
+ assert.Equal(t, 1, q.GetWorkerNumber()) // there is at least one worker after the queue begins working
stop()
}
@@ -241,13 +240,13 @@ func TestWorkerPoolQueueShutdown(t *testing.T) {
}
<-handlerCalled
time.Sleep(200 * time.Millisecond) // wait for a while to make sure all workers are active
- assert.EqualValues(t, 4, q.GetWorkerActiveNumber())
+ assert.Equal(t, 4, q.GetWorkerActiveNumber())
stop() // stop triggers shutdown
- assert.EqualValues(t, 0, q.GetWorkerActiveNumber())
+ assert.Equal(t, 0, q.GetWorkerActiveNumber())
// no item was ever handled, so we still get all of them again
q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", qs, handler, false)
- assert.EqualValues(t, 20, q.GetQueueItemNumber())
+ assert.Equal(t, 20, q.GetQueueItemNumber())
}
func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) {
@@ -275,7 +274,7 @@ func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) {
}
q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 2, Length: 100}, handler, false)
stop := runWorkerPoolQueue(q)
- for i := 0; i < 100; i++ {
+ for i := range 100 {
assert.NoError(t, q.Push(i))
}
time.Sleep(500 * time.Millisecond)
diff --git a/modules/references/references.go b/modules/references/references.go
index a5b102b7f2..592bd4cbe4 100644
--- a/modules/references/references.go
+++ b/modules/references/references.go
@@ -462,11 +462,12 @@ func findAllIssueReferencesBytes(content []byte, links []string) []*rawReference
continue
}
var sep string
- if parts[3] == "issues" {
+ switch parts[3] {
+ case "issues":
sep = "#"
- } else if parts[3] == "pulls" {
+ case "pulls":
sep = "!"
- } else {
+ default:
continue
}
// Note: closing/reopening keywords not supported with URLs
diff --git a/modules/references/references_test.go b/modules/references/references_test.go
index 1b6a968d6a..a15ae99f79 100644
--- a/modules/references/references_test.go
+++ b/modules/references/references_test.go
@@ -46,7 +46,7 @@ owner/repo!123456789
contentBytes := []byte(test)
convertFullHTMLReferencesToShortRefs(re, &contentBytes)
result := string(contentBytes)
- assert.EqualValues(t, expect, result)
+ assert.Equal(t, expect, result)
}
func TestFindAllIssueReferences(t *testing.T) {
@@ -283,9 +283,9 @@ func testFixtures(t *testing.T, fixtures []testFixture, context string) {
}
expref := rawToIssueReferenceList(expraw)
refs := FindAllIssueReferencesMarkdown(fixture.input)
- assert.EqualValues(t, expref, refs, "[%s] Failed to parse: {%s}", context, fixture.input)
+ assert.Equal(t, expref, refs, "[%s] Failed to parse: {%s}", context, fixture.input)
rawrefs := findAllIssueReferencesMarkdown(fixture.input)
- assert.EqualValues(t, expraw, rawrefs, "[%s] Failed to parse: {%s}", context, fixture.input)
+ assert.Equal(t, expraw, rawrefs, "[%s] Failed to parse: {%s}", context, fixture.input)
}
// Restore for other tests that may rely on the original value
@@ -294,7 +294,7 @@ func testFixtures(t *testing.T, fixtures []testFixture, context string) {
func TestFindAllMentions(t *testing.T) {
res := FindAllMentionsBytes([]byte("@tasha, @mike; @lucy: @john"))
- assert.EqualValues(t, []RefSpan{
+ assert.Equal(t, []RefSpan{
{Start: 0, End: 6},
{Start: 8, End: 13},
{Start: 15, End: 20},
@@ -554,7 +554,7 @@ func TestParseCloseKeywords(t *testing.T) {
res := pat.FindAllStringSubmatch(test.match, -1)
assert.Len(t, res, 1)
assert.Len(t, res[0], 2)
- assert.EqualValues(t, test.expected, res[0][1])
+ assert.Equal(t, test.expected, res[0][1])
}
}
}
diff --git a/modules/regexplru/regexplru_test.go b/modules/regexplru/regexplru_test.go
index 9c24b23fa9..4b539c31e9 100644
--- a/modules/regexplru/regexplru_test.go
+++ b/modules/regexplru/regexplru_test.go
@@ -18,9 +18,9 @@ func TestRegexpLru(t *testing.T) {
assert.NoError(t, err)
assert.True(t, r.MatchString("a"))
- assert.EqualValues(t, 1, lruCache.Len())
+ assert.Equal(t, 1, lruCache.Len())
_, err = GetCompiled("(")
assert.Error(t, err)
- assert.EqualValues(t, 2, lruCache.Len())
+ assert.Equal(t, 2, lruCache.Len())
}
diff --git a/modules/repository/branch.go b/modules/repository/branch.go
index 2bf9930f19..30aa0a6e85 100644
--- a/modules/repository/branch.go
+++ b/modules/repository/branch.go
@@ -41,11 +41,12 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository,
if err != nil {
return 0, fmt.Errorf("GetObjectFormat: %w", err)
}
- _, err = db.GetEngine(ctx).ID(repo.ID).Update(&repo_model.Repository{ObjectFormatName: objFmt.Name()})
- if err != nil {
- return 0, fmt.Errorf("UpdateRepository: %w", err)
+ if objFmt.Name() != repo.ObjectFormatName {
+ repo.ObjectFormatName = objFmt.Name()
+ if err = repo_model.UpdateRepositoryColsWithAutoTime(ctx, repo, "object_format_name"); err != nil {
+ return 0, fmt.Errorf("UpdateRepositoryColsWithAutoTime: %w", err)
+ }
}
- repo.ObjectFormatName = objFmt.Name() // keep consistent with db
allBranches := container.Set[string]{}
{
diff --git a/modules/repository/branch_test.go b/modules/repository/branch_test.go
index acf75a1ac0..ead28aa141 100644
--- a/modules/repository/branch_test.go
+++ b/modules/repository/branch_test.go
@@ -27,5 +27,5 @@ func TestSyncRepoBranches(t *testing.T) {
assert.Equal(t, "sha1", repo.ObjectFormatName)
branch, err := git_model.GetBranch(db.DefaultContext, 1, "master")
assert.NoError(t, err)
- assert.EqualValues(t, "master", branch.Name)
+ assert.Equal(t, "master", branch.Name)
}
diff --git a/modules/repository/commits.go b/modules/repository/commits.go
index 6e4b75d5ca..878fdc1603 100644
--- a/modules/repository/commits.go
+++ b/modules/repository/commits.go
@@ -10,8 +10,10 @@ import (
"time"
"code.gitea.io/gitea/models/avatars"
+ repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/cache"
+ "code.gitea.io/gitea/modules/cachegroup"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -43,7 +45,7 @@ func NewPushCommits() *PushCommits {
}
// ToAPIPayloadCommit converts a single PushCommit to an api.PayloadCommit object.
-func ToAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.User, repoPath, repoLink string, commit *PushCommit) (*api.PayloadCommit, error) {
+func ToAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.User, repo *repo_model.Repository, commit *PushCommit) (*api.PayloadCommit, error) {
var err error
authorUsername := ""
author, ok := emailUsers[commit.AuthorEmail]
@@ -70,7 +72,7 @@ func ToAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.U
committerUsername = committer.Name
}
- fileStatus, err := git.GetCommitFileStatus(ctx, repoPath, commit.Sha1)
+ fileStatus, err := git.GetCommitFileStatus(ctx, repo.RepoPath(), commit.Sha1)
if err != nil {
return nil, fmt.Errorf("FileStatus [commit_sha1: %s]: %w", commit.Sha1, err)
}
@@ -78,7 +80,7 @@ func ToAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.U
return &api.PayloadCommit{
ID: commit.Sha1,
Message: commit.Message,
- URL: fmt.Sprintf("%s/commit/%s", repoLink, url.PathEscape(commit.Sha1)),
+ URL: fmt.Sprintf("%s/commit/%s", repo.HTMLURL(), url.PathEscape(commit.Sha1)),
Author: &api.PayloadUser{
Name: commit.AuthorName,
Email: commit.AuthorEmail,
@@ -98,14 +100,14 @@ func ToAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.U
// ToAPIPayloadCommits converts a PushCommits object to api.PayloadCommit format.
// It returns all converted commits and, if provided, the head commit or an error otherwise.
-func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repoPath, repoLink string) ([]*api.PayloadCommit, *api.PayloadCommit, error) {
+func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repo *repo_model.Repository) ([]*api.PayloadCommit, *api.PayloadCommit, error) {
commits := make([]*api.PayloadCommit, len(pc.Commits))
var headCommit *api.PayloadCommit
emailUsers := make(map[string]*user_model.User)
for i, commit := range pc.Commits {
- apiCommit, err := ToAPIPayloadCommit(ctx, emailUsers, repoPath, repoLink, commit)
+ apiCommit, err := ToAPIPayloadCommit(ctx, emailUsers, repo, commit)
if err != nil {
return nil, nil, err
}
@@ -117,7 +119,7 @@ func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repoPath, repoLi
}
if pc.HeadCommit != nil && headCommit == nil {
var err error
- headCommit, err = ToAPIPayloadCommit(ctx, emailUsers, repoPath, repoLink, pc.HeadCommit)
+ headCommit, err = ToAPIPayloadCommit(ctx, emailUsers, repo, pc.HeadCommit)
if err != nil {
return nil, nil, err
}
@@ -130,7 +132,7 @@ func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repoPath, repoLi
func (pc *PushCommits) AvatarLink(ctx context.Context, email string) string {
size := avatars.DefaultAvatarPixelSize * setting.Avatar.RenderedSizeFactor
- v, _ := cache.GetWithContextCache(ctx, "push_commits", email, func() (string, error) {
+ v, _ := cache.GetWithContextCache(ctx, cachegroup.EmailAvatarLink, email, func(ctx context.Context, email string) (string, error) {
u, err := user_model.GetUserByEmail(ctx, email)
if err != nil {
if !user_model.IsErrUserNotExist(err) {
diff --git a/modules/repository/commits_test.go b/modules/repository/commits_test.go
index 3afc116e68..030cd7714d 100644
--- a/modules/repository/commits_test.go
+++ b/modules/repository/commits_test.go
@@ -50,54 +50,54 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
pushCommits.HeadCommit = &PushCommit{Sha1: "69554a6"}
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16})
- payloadCommits, headCommit, err := pushCommits.ToAPIPayloadCommits(git.DefaultContext, repo.RepoPath(), "/user2/repo16")
+ payloadCommits, headCommit, err := pushCommits.ToAPIPayloadCommits(git.DefaultContext, repo)
assert.NoError(t, err)
assert.Len(t, payloadCommits, 3)
assert.NotNil(t, headCommit)
assert.Equal(t, "69554a6", payloadCommits[0].ID)
assert.Equal(t, "not signed commit", payloadCommits[0].Message)
- assert.Equal(t, "/user2/repo16/commit/69554a6", payloadCommits[0].URL)
+ assert.Equal(t, "https://try.gitea.io/user2/repo16/commit/69554a6", payloadCommits[0].URL)
assert.Equal(t, "User2", payloadCommits[0].Committer.Name)
assert.Equal(t, "user2", payloadCommits[0].Committer.UserName)
assert.Equal(t, "User2", payloadCommits[0].Author.Name)
assert.Equal(t, "user2", payloadCommits[0].Author.UserName)
- assert.EqualValues(t, []string{}, payloadCommits[0].Added)
- assert.EqualValues(t, []string{}, payloadCommits[0].Removed)
- assert.EqualValues(t, []string{"readme.md"}, payloadCommits[0].Modified)
+ assert.Equal(t, []string{}, payloadCommits[0].Added)
+ assert.Equal(t, []string{}, payloadCommits[0].Removed)
+ assert.Equal(t, []string{"readme.md"}, payloadCommits[0].Modified)
assert.Equal(t, "27566bd", payloadCommits[1].ID)
assert.Equal(t, "good signed commit (with not yet validated email)", payloadCommits[1].Message)
- assert.Equal(t, "/user2/repo16/commit/27566bd", payloadCommits[1].URL)
+ assert.Equal(t, "https://try.gitea.io/user2/repo16/commit/27566bd", payloadCommits[1].URL)
assert.Equal(t, "User2", payloadCommits[1].Committer.Name)
assert.Equal(t, "user2", payloadCommits[1].Committer.UserName)
assert.Equal(t, "User2", payloadCommits[1].Author.Name)
assert.Equal(t, "user2", payloadCommits[1].Author.UserName)
- assert.EqualValues(t, []string{}, payloadCommits[1].Added)
- assert.EqualValues(t, []string{}, payloadCommits[1].Removed)
- assert.EqualValues(t, []string{"readme.md"}, payloadCommits[1].Modified)
+ assert.Equal(t, []string{}, payloadCommits[1].Added)
+ assert.Equal(t, []string{}, payloadCommits[1].Removed)
+ assert.Equal(t, []string{"readme.md"}, payloadCommits[1].Modified)
assert.Equal(t, "5099b81", payloadCommits[2].ID)
assert.Equal(t, "good signed commit", payloadCommits[2].Message)
- assert.Equal(t, "/user2/repo16/commit/5099b81", payloadCommits[2].URL)
+ assert.Equal(t, "https://try.gitea.io/user2/repo16/commit/5099b81", payloadCommits[2].URL)
assert.Equal(t, "User2", payloadCommits[2].Committer.Name)
assert.Equal(t, "user2", payloadCommits[2].Committer.UserName)
assert.Equal(t, "User2", payloadCommits[2].Author.Name)
assert.Equal(t, "user2", payloadCommits[2].Author.UserName)
- assert.EqualValues(t, []string{"readme.md"}, payloadCommits[2].Added)
- assert.EqualValues(t, []string{}, payloadCommits[2].Removed)
- assert.EqualValues(t, []string{}, payloadCommits[2].Modified)
+ assert.Equal(t, []string{"readme.md"}, payloadCommits[2].Added)
+ assert.Equal(t, []string{}, payloadCommits[2].Removed)
+ assert.Equal(t, []string{}, payloadCommits[2].Modified)
assert.Equal(t, "69554a6", headCommit.ID)
assert.Equal(t, "not signed commit", headCommit.Message)
- assert.Equal(t, "/user2/repo16/commit/69554a6", headCommit.URL)
+ assert.Equal(t, "https://try.gitea.io/user2/repo16/commit/69554a6", headCommit.URL)
assert.Equal(t, "User2", headCommit.Committer.Name)
assert.Equal(t, "user2", headCommit.Committer.UserName)
assert.Equal(t, "User2", headCommit.Author.Name)
assert.Equal(t, "user2", headCommit.Author.UserName)
- assert.EqualValues(t, []string{}, headCommit.Added)
- assert.EqualValues(t, []string{}, headCommit.Removed)
- assert.EqualValues(t, []string{"readme.md"}, headCommit.Modified)
+ assert.Equal(t, []string{}, headCommit.Added)
+ assert.Equal(t, []string{}, headCommit.Removed)
+ assert.Equal(t, []string{"readme.md"}, headCommit.Modified)
}
func TestPushCommits_AvatarLink(t *testing.T) {
@@ -200,5 +200,3 @@ func TestListToPushCommits(t *testing.T) {
assert.Equal(t, now, pushCommits.Commits[1].Timestamp)
}
}
-
-// TODO TestPushUpdate
diff --git a/modules/repository/create.go b/modules/repository/create.go
index b4f7033bd7..a75598a84b 100644
--- a/modules/repository/create.go
+++ b/modules/repository/create.go
@@ -7,19 +7,10 @@ import (
"context"
"fmt"
"os"
- "path"
"path/filepath"
- "strings"
- activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
- access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
- issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
- "code.gitea.io/gitea/modules/log"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
)
const notRegularFileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular
@@ -64,97 +55,3 @@ func UpdateRepoSize(ctx context.Context, repo *repo_model.Repository) error {
return repo_model.UpdateRepoSize(ctx, repo.ID, size, lfsSize)
}
-
-// CheckDaemonExportOK creates/removes git-daemon-export-ok for git-daemon...
-func CheckDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error {
- if err := repo.LoadOwner(ctx); err != nil {
- return err
- }
-
- // Create/Remove git-daemon-export-ok for git-daemon...
- daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`)
-
- isExist, err := util.IsExist(daemonExportFile)
- if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err)
- return err
- }
-
- isPublic := !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePublic
- if !isPublic && isExist {
- if err = util.Remove(daemonExportFile); err != nil {
- log.Error("Failed to remove %s: %v", daemonExportFile, err)
- }
- } else if isPublic && !isExist {
- if f, err := os.Create(daemonExportFile); err != nil {
- log.Error("Failed to create %s: %v", daemonExportFile, err)
- } else {
- f.Close()
- }
- }
-
- return nil
-}
-
-// UpdateRepository updates a repository with db context
-func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibilityChanged bool) (err error) {
- repo.LowerName = strings.ToLower(repo.Name)
-
- e := db.GetEngine(ctx)
-
- if _, err = e.ID(repo.ID).AllCols().Update(repo); err != nil {
- return fmt.Errorf("update: %w", err)
- }
-
- if err = UpdateRepoSize(ctx, repo); err != nil {
- log.Error("Failed to update size for repository: %v", err)
- }
-
- if visibilityChanged {
- if err = repo.LoadOwner(ctx); err != nil {
- return fmt.Errorf("LoadOwner: %w", err)
- }
- if repo.Owner.IsOrganization() {
- // Organization repository need to recalculate access table when visibility is changed.
- if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
- return fmt.Errorf("recalculateTeamAccesses: %w", err)
- }
- }
-
- // If repo has become private, we need to set its actions to private.
- if repo.IsPrivate {
- _, err = e.Where("repo_id = ?", repo.ID).Cols("is_private").Update(&activities_model.Action{
- IsPrivate: true,
- })
- if err != nil {
- return err
- }
-
- if err = repo_model.ClearRepoStars(ctx, repo.ID); err != nil {
- return err
- }
- }
-
- // Create/Remove git-daemon-export-ok for git-daemon...
- if err := CheckDaemonExportOK(ctx, repo); err != nil {
- return err
- }
-
- forkRepos, err := repo_model.GetRepositoriesByForkID(ctx, repo.ID)
- if err != nil {
- return fmt.Errorf("getRepositoriesByForkID: %w", err)
- }
- for i := range forkRepos {
- forkRepos[i].IsPrivate = repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate
- if err = UpdateRepository(ctx, forkRepos[i], true); err != nil {
- return fmt.Errorf("updateRepository[%d]: %w", forkRepos[i].ID, err)
- }
- }
-
- // If visibility is changed, we need to update the issue indexer.
- // Since the data in the issue indexer have field to indicate if the repo is public or not.
- issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
- }
-
- return nil
-}
diff --git a/modules/repository/create_test.go b/modules/repository/create_test.go
index a9151482b4..b85a10adad 100644
--- a/modules/repository/create_test.go
+++ b/modules/repository/create_test.go
@@ -6,7 +6,6 @@ package repository
import (
"testing"
- activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
@@ -14,26 +13,6 @@ import (
"github.com/stretchr/testify/assert"
)
-func TestUpdateRepositoryVisibilityChanged(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- // Get sample repo and change visibility
- repo, err := repo_model.GetRepositoryByID(db.DefaultContext, 9)
- assert.NoError(t, err)
- repo.IsPrivate = true
-
- // Update it
- err = UpdateRepository(db.DefaultContext, repo, true)
- assert.NoError(t, err)
-
- // Check visibility of action has become private
- act := activities_model.Action{}
- _, err = db.GetEngine(db.DefaultContext).ID(3).Get(&act)
-
- assert.NoError(t, err)
- assert.True(t, act.IsPrivate)
-}
-
func TestGetDirectorySize(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo, err := repo_model.GetRepositoryByID(db.DefaultContext, 1)
@@ -41,5 +20,5 @@ func TestGetDirectorySize(t *testing.T) {
size, err := getDirectorySize(repo.RepoPath())
assert.NoError(t, err)
repo.Size = 8165 // real size on the disk
- assert.EqualValues(t, repo.Size, size)
+ assert.Equal(t, repo.Size, size)
}
diff --git a/modules/repository/env.go b/modules/repository/env.go
index e4f32092fc..78e06f86fb 100644
--- a/modules/repository/env.go
+++ b/modules/repository/env.go
@@ -4,8 +4,8 @@
package repository
import (
- "fmt"
"os"
+ "strconv"
"strings"
repo_model "code.gitea.io/gitea/models/repo"
@@ -72,9 +72,9 @@ func FullPushingEnvironment(author, committer *user_model.User, repo *repo_model
EnvRepoUsername+"="+repo.OwnerName,
EnvRepoIsWiki+"="+isWiki,
EnvPusherName+"="+committer.Name,
- EnvPusherID+"="+fmt.Sprintf("%d", committer.ID),
- EnvRepoID+"="+fmt.Sprintf("%d", repo.ID),
- EnvPRID+"="+fmt.Sprintf("%d", prID),
+ EnvPusherID+"="+strconv.FormatInt(committer.ID, 10),
+ EnvRepoID+"="+strconv.FormatInt(repo.ID, 10),
+ EnvPRID+"="+strconv.FormatInt(prID, 10),
EnvAppURL+"="+setting.AppURL,
"SSH_ORIGINAL_COMMAND=gitea-internal",
)
diff --git a/modules/repository/init.go b/modules/repository/init.go
index 24602ae090..12e9606c74 100644
--- a/modules/repository/init.go
+++ b/modules/repository/init.go
@@ -11,10 +11,7 @@ import (
"strings"
issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/label"
- "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@@ -120,30 +117,6 @@ func LoadRepoConfig() error {
return nil
}
-func CheckInitRepository(ctx context.Context, owner, name, objectFormatName string) (err error) {
- // Somehow the directory could exist.
- repoPath := repo_model.RepoPath(owner, name)
- isExist, err := util.IsExist(repoPath)
- if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
- return err
- }
- if isExist {
- return repo_model.ErrRepoFilesAlreadyExist{
- Uname: owner,
- Name: name,
- }
- }
-
- // Init git bare new repository.
- if err = git.InitRepository(ctx, repoPath, true, objectFormatName); err != nil {
- return fmt.Errorf("git.InitRepository: %w", err)
- } else if err = CreateDelegateHooks(repoPath); err != nil {
- return fmt.Errorf("createDelegateHooks: %w", err)
- }
- return nil
-}
-
// InitializeLabels adds a label set to a repository using a template
func InitializeLabels(ctx context.Context, id int64, labelTemplate string, isOrg bool) error {
list, err := LoadTemplateLabelsByDisplayName(labelTemplate)
@@ -152,12 +125,13 @@ func InitializeLabels(ctx context.Context, id int64, labelTemplate string, isOrg
}
labels := make([]*issues_model.Label, len(list))
- for i := 0; i < len(list); i++ {
+ for i := range list {
labels[i] = &issues_model.Label{
- Name: list[i].Name,
- Exclusive: list[i].Exclusive,
- Description: list[i].Description,
- Color: list[i].Color,
+ Name: list[i].Name,
+ Exclusive: list[i].Exclusive,
+ ExclusiveOrder: list[i].ExclusiveOrder,
+ Description: list[i].Description,
+ Color: list[i].Color,
}
if isOrg {
labels[i].OrgID = id
diff --git a/modules/repository/init_test.go b/modules/repository/init_test.go
index 227efdc1db..1fa928105c 100644
--- a/modules/repository/init_test.go
+++ b/modules/repository/init_test.go
@@ -14,17 +14,17 @@ func TestMergeCustomLabels(t *testing.T) {
all: []string{"a", "a.yaml", "a.yml"},
custom: nil,
})
- assert.EqualValues(t, []string{"a.yaml"}, files, "yaml file should win")
+ assert.Equal(t, []string{"a.yaml"}, files, "yaml file should win")
files = mergeCustomLabelFiles(optionFileList{
all: []string{"a", "a.yaml"},
custom: []string{"a"},
})
- assert.EqualValues(t, []string{"a"}, files, "custom file should win")
+ assert.Equal(t, []string{"a"}, files, "custom file should win")
files = mergeCustomLabelFiles(optionFileList{
all: []string{"a", "a.yml", "a.yaml"},
custom: []string{"a", "a.yml"},
})
- assert.EqualValues(t, []string{"a.yml"}, files, "custom yml file should win if no yaml")
+ assert.Equal(t, []string{"a.yml"}, files, "custom yml file should win if no yaml")
}
diff --git a/modules/repository/repo.go b/modules/repository/repo.go
index 97b0343381..ad4a53b858 100644
--- a/modules/repository/repo.go
+++ b/modules/repository/repo.go
@@ -9,13 +9,10 @@ import (
"fmt"
"io"
"strings"
- "time"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/lfs"
@@ -59,118 +56,6 @@ func SyncRepoTags(ctx context.Context, repoID int64) error {
return SyncReleasesWithTags(ctx, repo, gitRepo)
}
-// SyncReleasesWithTags synchronizes release table with repository tags
-func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository) error {
- log.Debug("SyncReleasesWithTags: in Repo[%d:%s/%s]", repo.ID, repo.OwnerName, repo.Name)
-
- // optimized procedure for pull-mirrors which saves a lot of time (in
- // particular for repos with many tags).
- if repo.IsMirror {
- return pullMirrorReleaseSync(ctx, repo, gitRepo)
- }
-
- existingRelTags := make(container.Set[string])
- opts := repo_model.FindReleasesOptions{
- IncludeDrafts: true,
- IncludeTags: true,
- ListOptions: db.ListOptions{PageSize: 50},
- RepoID: repo.ID,
- }
- for page := 1; ; page++ {
- opts.Page = page
- rels, err := db.Find[repo_model.Release](gitRepo.Ctx, opts)
- if err != nil {
- return fmt.Errorf("unable to GetReleasesByRepoID in Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
- }
- if len(rels) == 0 {
- break
- }
- for _, rel := range rels {
- if rel.IsDraft {
- continue
- }
- commitID, err := gitRepo.GetTagCommitID(rel.TagName)
- if err != nil && !git.IsErrNotExist(err) {
- return fmt.Errorf("unable to GetTagCommitID for %q in Repo[%d:%s/%s]: %w", rel.TagName, repo.ID, repo.OwnerName, repo.Name, err)
- }
- if git.IsErrNotExist(err) || commitID != rel.Sha1 {
- if err := repo_model.PushUpdateDeleteTag(ctx, repo, rel.TagName); err != nil {
- return fmt.Errorf("unable to PushUpdateDeleteTag: %q in Repo[%d:%s/%s]: %w", rel.TagName, repo.ID, repo.OwnerName, repo.Name, err)
- }
- } else {
- existingRelTags.Add(strings.ToLower(rel.TagName))
- }
- }
- }
-
- _, err := gitRepo.WalkReferences(git.ObjectTag, 0, 0, func(sha1, refname string) error {
- tagName := strings.TrimPrefix(refname, git.TagPrefix)
- if existingRelTags.Contains(strings.ToLower(tagName)) {
- return nil
- }
-
- if err := PushUpdateAddTag(ctx, repo, gitRepo, tagName, sha1, refname); err != nil {
- // sometimes, some tags will be sync failed. i.e. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tag/?h=v2.6.11
- // this is a tree object, not a tag object which created before git
- log.Error("unable to PushUpdateAddTag: %q to Repo[%d:%s/%s]: %v", tagName, repo.ID, repo.OwnerName, repo.Name, err)
- }
-
- return nil
- })
- return err
-}
-
-// PushUpdateAddTag must be called for any push actions to add tag
-func PushUpdateAddTag(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, tagName, sha1, refname string) error {
- tag, err := gitRepo.GetTagWithID(sha1, tagName)
- if err != nil {
- return fmt.Errorf("unable to GetTag: %w", err)
- }
- commit, err := tag.Commit(gitRepo)
- if err != nil {
- return fmt.Errorf("unable to get tag Commit: %w", err)
- }
-
- sig := tag.Tagger
- if sig == nil {
- sig = commit.Author
- }
- if sig == nil {
- sig = commit.Committer
- }
-
- var author *user_model.User
- createdAt := time.Unix(1, 0)
-
- if sig != nil {
- author, err = user_model.GetUserByEmail(ctx, sig.Email)
- if err != nil && !user_model.IsErrUserNotExist(err) {
- return fmt.Errorf("unable to GetUserByEmail for %q: %w", sig.Email, err)
- }
- createdAt = sig.When
- }
-
- commitsCount, err := commit.CommitsCount()
- if err != nil {
- return fmt.Errorf("unable to get CommitsCount: %w", err)
- }
-
- rel := repo_model.Release{
- RepoID: repo.ID,
- TagName: tagName,
- LowerTagName: strings.ToLower(tagName),
- Sha1: commit.ID.String(),
- NumCommits: commitsCount,
- CreatedUnix: timeutil.TimeStamp(createdAt.Unix()),
- IsTag: true,
- }
- if author != nil {
- rel.PublisherID = author.ID
- }
-
- return repo_model.SaveOrUpdateTag(ctx, repo, &rel)
-}
-
// StoreMissingLfsObjectsInRepository downloads missing LFS objects
func StoreMissingLfsObjectsInRepository(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, lfsClient lfs.Client) error {
contentStore := lfs.NewContentStore()
@@ -286,18 +171,19 @@ func (shortRelease) TableName() string {
return "release"
}
-// pullMirrorReleaseSync is a pull-mirror specific tag<->release table
+// SyncReleasesWithTags is a tag<->release table
// synchronization which overwrites all Releases from the repository tags. This
// can be relied on since a pull-mirror is always identical to its
-// upstream. Hence, after each sync we want the pull-mirror release set to be
+// upstream. Hence, after each sync we want the release set to be
// identical to the upstream tag set. This is much more efficient for
// repositories like https://github.com/vim/vim (with over 13000 tags).
-func pullMirrorReleaseSync(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository) error {
- log.Trace("pullMirrorReleaseSync: rebuilding releases for pull-mirror Repo[%d:%s/%s]", repo.ID, repo.OwnerName, repo.Name)
- tags, numTags, err := gitRepo.GetTagInfos(0, 0)
+func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository) error {
+ log.Debug("SyncReleasesWithTags: in Repo[%d:%s/%s]", repo.ID, repo.OwnerName, repo.Name)
+ tags, _, err := gitRepo.GetTagInfos(0, 0)
if err != nil {
return fmt.Errorf("unable to GetTagInfos in pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
}
+ var added, deleted, updated int
err = db.WithTx(ctx, func(ctx context.Context) error {
dbReleases, err := db.Find[shortRelease](ctx, repo_model.FindReleasesOptions{
RepoID: repo.ID,
@@ -318,9 +204,7 @@ func pullMirrorReleaseSync(ctx context.Context, repo *repo_model.Repository, git
TagName: tag.Name,
LowerTagName: strings.ToLower(tag.Name),
Sha1: tag.Object.String(),
- // NOTE: ignored, since NumCommits are unused
- // for pull-mirrors (only relevant when
- // displaying releases, IsTag: false)
+ // NOTE: ignored, The NumCommits value is calculated and cached on demand when the UI requires it.
NumCommits: -1,
CreatedUnix: timeutil.TimeStamp(tag.Tagger.When.Unix()),
IsTag: true,
@@ -349,13 +233,14 @@ func pullMirrorReleaseSync(ctx context.Context, repo *repo_model.Repository, git
return fmt.Errorf("unable to update tag %s for pull-mirror Repo[%d:%s/%s]: %w", tag.Name, repo.ID, repo.OwnerName, repo.Name, err)
}
}
+ added, deleted, updated = len(deletes), len(updates), len(inserts)
return nil
})
if err != nil {
return fmt.Errorf("unable to rebuild release table for pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
}
- log.Trace("pullMirrorReleaseSync: done rebuilding %d releases", numTags)
+ log.Trace("SyncReleasesWithTags: %d tags added, %d tags deleted, %d tags updated", added, deleted, updated)
return nil
}
diff --git a/modules/repository/repo_test.go b/modules/repository/repo_test.go
index f3e7be6d7d..f79a79ccbd 100644
--- a/modules/repository/repo_test.go
+++ b/modules/repository/repo_test.go
@@ -63,7 +63,7 @@ func Test_calcSync(t *testing.T) {
inserts, deletes, updates := calcSync(gitTags, dbReleases)
if assert.Len(t, inserts, 1, "inserts") {
- assert.EqualValues(t, *gitTags[2], *inserts[0], "inserts equal")
+ assert.Equal(t, *gitTags[2], *inserts[0], "inserts equal")
}
if assert.Len(t, deletes, 1, "deletes") {
@@ -71,6 +71,6 @@ func Test_calcSync(t *testing.T) {
}
if assert.Len(t, updates, 1, "updates") {
- assert.EqualValues(t, *gitTags[1], *updates[0], "updates equal")
+ assert.Equal(t, *gitTags[1], *updates[0], "updates equal")
}
}
diff --git a/modules/repository/temp.go b/modules/repository/temp.go
index 04faa9db3d..d7253d9e02 100644
--- a/modules/repository/temp.go
+++ b/modules/repository/temp.go
@@ -4,42 +4,19 @@
package repository
import (
+ "context"
"fmt"
- "os"
- "path"
- "path/filepath"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
)
-// LocalCopyPath returns the local repository temporary copy path.
-func LocalCopyPath() string {
- if filepath.IsAbs(setting.Repository.Local.LocalCopyPath) {
- return setting.Repository.Local.LocalCopyPath
- }
- return path.Join(setting.AppDataPath, setting.Repository.Local.LocalCopyPath)
-}
-
// CreateTemporaryPath creates a temporary path
-func CreateTemporaryPath(prefix string) (string, error) {
- if err := os.MkdirAll(LocalCopyPath(), os.ModePerm); err != nil {
- log.Error("Unable to create localcopypath directory: %s (%v)", LocalCopyPath(), err)
- return "", fmt.Errorf("Failed to create localcopypath directory %s: %w", LocalCopyPath(), err)
- }
- basePath, err := os.MkdirTemp(LocalCopyPath(), prefix+".git")
+func CreateTemporaryPath(prefix string) (string, context.CancelFunc, error) {
+ basePath, cleanup, err := setting.AppDataTempDir("local-repo").MkdirTempRandom(prefix + ".git")
if err != nil {
log.Error("Unable to create temporary directory: %s-*.git (%v)", prefix, err)
- return "", fmt.Errorf("Failed to create dir %s-*.git: %w", prefix, err)
- }
- return basePath, nil
-}
-
-// RemoveTemporaryPath removes the temporary path
-func RemoveTemporaryPath(basePath string) error {
- if _, err := os.Stat(basePath); !os.IsNotExist(err) {
- return util.RemoveAll(basePath)
+ return "", nil, fmt.Errorf("failed to create dir %s-*.git: %w", prefix, err)
}
- return nil
+ return basePath, cleanup, nil
}
diff --git a/modules/reqctx/datastore.go b/modules/reqctx/datastore.go
index 94232450f3..1d4bee613f 100644
--- a/modules/reqctx/datastore.go
+++ b/modules/reqctx/datastore.go
@@ -6,6 +6,7 @@ package reqctx
import (
"context"
"io"
+ "maps"
"sync"
"code.gitea.io/gitea/modules/process"
@@ -22,9 +23,7 @@ func (ds ContextData) GetData() ContextData {
}
func (ds ContextData) MergeFrom(other ContextData) ContextData {
- for k, v := range other {
- ds[k] = v
- }
+ maps.Copy(ds, other)
return ds
}
@@ -94,6 +93,9 @@ type RequestContext interface {
}
func FromContext(ctx context.Context) RequestContext {
+ if rc, ok := ctx.(RequestContext); ok {
+ return rc
+ }
// here we must use the current ctx and the underlying store
// the current ctx guarantees that the ctx deadline/cancellation/values are respected
// the underlying store guarantees that the request-specific data is available
@@ -134,6 +136,6 @@ func NewRequestContext(parentCtx context.Context, profDesc string) (_ context.Co
// NewRequestContextForTest creates a new RequestContext for testing purposes
// It doesn't add the context to the process manager, nor do cleanup
-func NewRequestContextForTest(parentCtx context.Context) context.Context {
+func NewRequestContextForTest(parentCtx context.Context) RequestContext {
return &requestContext{Context: parentCtx, RequestDataStore: &requestDataStore{values: make(map[any]any)}}
}
diff --git a/modules/secret/secret.go b/modules/secret/secret.go
index e70ae1839c..af894a054c 100644
--- a/modules/secret/secret.go
+++ b/modules/secret/secret.go
@@ -16,6 +16,7 @@ import (
)
// AesEncrypt encrypts text and given key with AES.
+// It is only internally used at the moment to use "SECRET_KEY" for some database values.
func AesEncrypt(key, text []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
@@ -27,12 +28,13 @@ func AesEncrypt(key, text []byte) ([]byte, error) {
if _, err = io.ReadFull(rand.Reader, iv); err != nil {
return nil, fmt.Errorf("AesEncrypt unable to read IV: %w", err)
}
- cfb := cipher.NewCFBEncrypter(block, iv)
+ cfb := cipher.NewCFBEncrypter(block, iv) //nolint:staticcheck // need to migrate and refactor to a new approach
cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
return ciphertext, nil
}
// AesDecrypt decrypts text and given key with AES.
+// It is only internally used at the moment to use "SECRET_KEY" for some database values.
func AesDecrypt(key, text []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
@@ -43,7 +45,7 @@ func AesDecrypt(key, text []byte) ([]byte, error) {
}
iv := text[:aes.BlockSize]
text = text[aes.BlockSize:]
- cfb := cipher.NewCFBDecrypter(block, iv)
+ cfb := cipher.NewCFBDecrypter(block, iv) //nolint:staticcheck // need to migrate and refactor to a new approach
cfb.XORKeyStream(text, text)
data, err := base64.StdEncoding.DecodeString(string(text))
if err != nil {
diff --git a/modules/session/key.go b/modules/session/key.go
new file mode 100644
index 0000000000..c3da997c67
--- /dev/null
+++ b/modules/session/key.go
@@ -0,0 +1,11 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package session
+
+const (
+ KeyUID = "uid"
+ KeyUname = "uname"
+
+ KeyUserHasTwoFactorAuth = "userHasTwoFactorAuth"
+)
diff --git a/modules/setting/actions_test.go b/modules/setting/actions_test.go
index 3645a3f5da..353cc657fa 100644
--- a/modules/setting/actions_test.go
+++ b/modules/setting/actions_test.go
@@ -21,9 +21,9 @@ func Test_getStorageInheritNameSectionTypeForActions(t *testing.T) {
assert.NoError(t, loadActionsFrom(cfg))
assert.EqualValues(t, "minio", Actions.LogStorage.Type)
- assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath)
+ assert.Equal(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath)
assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type)
- assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath)
+ assert.Equal(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath)
iniStr = `
[storage.actions_log]
@@ -34,9 +34,9 @@ STORAGE_TYPE = minio
assert.NoError(t, loadActionsFrom(cfg))
assert.EqualValues(t, "minio", Actions.LogStorage.Type)
- assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath)
+ assert.Equal(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath)
assert.EqualValues(t, "local", Actions.ArtifactStorage.Type)
- assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path))
+ assert.Equal(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path))
iniStr = `
[storage.actions_log]
@@ -50,9 +50,9 @@ STORAGE_TYPE = minio
assert.NoError(t, loadActionsFrom(cfg))
assert.EqualValues(t, "minio", Actions.LogStorage.Type)
- assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath)
+ assert.Equal(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath)
assert.EqualValues(t, "local", Actions.ArtifactStorage.Type)
- assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path))
+ assert.Equal(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path))
iniStr = `
[storage.actions_artifacts]
@@ -66,9 +66,9 @@ STORAGE_TYPE = minio
assert.NoError(t, loadActionsFrom(cfg))
assert.EqualValues(t, "local", Actions.LogStorage.Type)
- assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path))
+ assert.Equal(t, "actions_log", filepath.Base(Actions.LogStorage.Path))
assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type)
- assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath)
+ assert.Equal(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath)
iniStr = `
[storage.actions_artifacts]
@@ -82,9 +82,9 @@ STORAGE_TYPE = minio
assert.NoError(t, loadActionsFrom(cfg))
assert.EqualValues(t, "local", Actions.LogStorage.Type)
- assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path))
+ assert.Equal(t, "actions_log", filepath.Base(Actions.LogStorage.Path))
assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type)
- assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath)
+ assert.Equal(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath)
iniStr = ``
cfg, err = NewConfigProviderFromData(iniStr)
@@ -92,9 +92,9 @@ STORAGE_TYPE = minio
assert.NoError(t, loadActionsFrom(cfg))
assert.EqualValues(t, "local", Actions.LogStorage.Type)
- assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path))
+ assert.Equal(t, "actions_log", filepath.Base(Actions.LogStorage.Path))
assert.EqualValues(t, "local", Actions.ArtifactStorage.Type)
- assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path))
+ assert.Equal(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path))
}
func Test_getDefaultActionsURLForActions(t *testing.T) {
@@ -175,7 +175,7 @@ DEFAULT_ACTIONS_URL = gitea
if !tt.wantErr(t, loadActionsFrom(cfg)) {
return
}
- assert.EqualValues(t, tt.wantURL, Actions.DefaultActionsURL.URL())
+ assert.Equal(t, tt.wantURL, Actions.DefaultActionsURL.URL())
})
}
}
diff --git a/modules/setting/api.go b/modules/setting/api.go
index c36f05cfd1..cdad474cb9 100644
--- a/modules/setting/api.go
+++ b/modules/setting/api.go
@@ -18,6 +18,7 @@ var API = struct {
DefaultPagingNum int
DefaultGitTreesPerPage int
DefaultMaxBlobSize int64
+ DefaultMaxResponseSize int64
}{
EnableSwagger: true,
SwaggerURL: "",
@@ -25,6 +26,7 @@ var API = struct {
DefaultPagingNum: 30,
DefaultGitTreesPerPage: 1000,
DefaultMaxBlobSize: 10485760,
+ DefaultMaxResponseSize: 104857600,
}
func loadAPIFrom(rootCfg ConfigProvider) {
diff --git a/modules/setting/attachment_test.go b/modules/setting/attachment_test.go
index 3e8d2da4d9..c566dfa60c 100644
--- a/modules/setting/attachment_test.go
+++ b/modules/setting/attachment_test.go
@@ -25,9 +25,9 @@ MINIO_ENDPOINT = my_minio:9000
assert.NoError(t, loadAttachmentFrom(cfg))
assert.EqualValues(t, "minio", Attachment.Storage.Type)
- assert.EqualValues(t, "my_minio:9000", Attachment.Storage.MinioConfig.Endpoint)
- assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket)
- assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "my_minio:9000", Attachment.Storage.MinioConfig.Endpoint)
+ assert.Equal(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket)
+ assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
}
func Test_getStorageTypeSectionOverridesStorageSection(t *testing.T) {
@@ -47,8 +47,8 @@ MINIO_BUCKET = gitea
assert.NoError(t, loadAttachmentFrom(cfg))
assert.EqualValues(t, "minio", Attachment.Storage.Type)
- assert.EqualValues(t, "gitea-minio", Attachment.Storage.MinioConfig.Bucket)
- assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea-minio", Attachment.Storage.MinioConfig.Bucket)
+ assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
}
func Test_getStorageSpecificOverridesStorage(t *testing.T) {
@@ -69,8 +69,8 @@ STORAGE_TYPE = local
assert.NoError(t, loadAttachmentFrom(cfg))
assert.EqualValues(t, "minio", Attachment.Storage.Type)
- assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket)
- assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket)
+ assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
}
func Test_getStorageGetDefaults(t *testing.T) {
@@ -80,7 +80,7 @@ func Test_getStorageGetDefaults(t *testing.T) {
assert.NoError(t, loadAttachmentFrom(cfg))
// default storage is local, so bucket is empty
- assert.EqualValues(t, "", Attachment.Storage.MinioConfig.Bucket)
+ assert.Empty(t, Attachment.Storage.MinioConfig.Bucket)
}
func Test_getStorageInheritNameSectionType(t *testing.T) {
@@ -115,7 +115,7 @@ MINIO_SECRET_ACCESS_KEY = correct_key
storage := Attachment.Storage
assert.EqualValues(t, "minio", storage.Type)
- assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket)
+ assert.Equal(t, "gitea", storage.MinioConfig.Bucket)
}
func Test_AttachmentStorage1(t *testing.T) {
@@ -128,6 +128,6 @@ STORAGE_TYPE = minio
assert.NoError(t, loadAttachmentFrom(cfg))
assert.EqualValues(t, "minio", Attachment.Storage.Type)
- assert.EqualValues(t, "gitea", Attachment.Storage.MinioConfig.Bucket)
- assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea", Attachment.Storage.MinioConfig.Bucket)
+ assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
}
diff --git a/modules/setting/config_env.go b/modules/setting/config_env.go
index 5d94a9641f..409588dc44 100644
--- a/modules/setting/config_env.go
+++ b/modules/setting/config_env.go
@@ -97,7 +97,7 @@ func decodeEnvSectionKey(encoded string) (ok bool, section, key string) {
// decodeEnvironmentKey decode the environment key to section and key
// The environment key is in the form of GITEA__SECTION__KEY or GITEA__SECTION__KEY__FILE
-func decodeEnvironmentKey(prefixGitea, suffixFile, envKey string) (ok bool, section, key string, useFileValue bool) { //nolint:unparam
+func decodeEnvironmentKey(prefixGitea, suffixFile, envKey string) (ok bool, section, key string, useFileValue bool) {
if !strings.HasPrefix(envKey, prefixGitea) {
return false, "", "", false
}
diff --git a/modules/setting/config_env_test.go b/modules/setting/config_env_test.go
index 7d07c479a1..7d270ac21a 100644
--- a/modules/setting/config_env_test.go
+++ b/modules/setting/config_env_test.go
@@ -28,8 +28,8 @@ func TestDecodeEnvSectionKey(t *testing.T) {
ok, section, key = decodeEnvSectionKey("SEC")
assert.False(t, ok)
- assert.Equal(t, "", section)
- assert.Equal(t, "", key)
+ assert.Empty(t, section)
+ assert.Empty(t, key)
}
func TestDecodeEnvironmentKey(t *testing.T) {
@@ -38,19 +38,19 @@ func TestDecodeEnvironmentKey(t *testing.T) {
ok, section, key, file := decodeEnvironmentKey(prefix, suffix, "SEC__KEY")
assert.False(t, ok)
- assert.Equal(t, "", section)
- assert.Equal(t, "", key)
+ assert.Empty(t, section)
+ assert.Empty(t, key)
assert.False(t, file)
ok, section, key, file = decodeEnvironmentKey(prefix, suffix, "GITEA__SEC")
assert.False(t, ok)
- assert.Equal(t, "", section)
- assert.Equal(t, "", key)
+ assert.Empty(t, section)
+ assert.Empty(t, key)
assert.False(t, file)
ok, section, key, file = decodeEnvironmentKey(prefix, suffix, "GITEA____KEY")
assert.True(t, ok)
- assert.Equal(t, "", section)
+ assert.Empty(t, section)
assert.Equal(t, "KEY", key)
assert.False(t, file)
@@ -64,8 +64,8 @@ func TestDecodeEnvironmentKey(t *testing.T) {
// but it could be fixed in the future by adding a new suffix like "__VALUE" (no such key VALUE is used in Gitea either)
ok, section, key, file = decodeEnvironmentKey(prefix, suffix, "GITEA__SEC__FILE")
assert.False(t, ok)
- assert.Equal(t, "", section)
- assert.Equal(t, "", key)
+ assert.Empty(t, section)
+ assert.Empty(t, key)
assert.True(t, file)
ok, section, key, file = decodeEnvironmentKey(prefix, suffix, "GITEA__SEC__KEY__FILE")
@@ -73,6 +73,9 @@ func TestDecodeEnvironmentKey(t *testing.T) {
assert.Equal(t, "sec", section)
assert.Equal(t, "KEY", key)
assert.True(t, file)
+
+ ok, _, _, _ = decodeEnvironmentKey("PREFIX__", "", "PREFIX__SEC__KEY")
+ assert.True(t, ok)
}
func TestEnvironmentToConfig(t *testing.T) {
diff --git a/modules/setting/config_provider.go b/modules/setting/config_provider.go
index 3138f8a63e..09eaaefdaf 100644
--- a/modules/setting/config_provider.go
+++ b/modules/setting/config_provider.go
@@ -15,7 +15,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
- "gopkg.in/ini.v1" //nolint:depguard
+ "gopkg.in/ini.v1" //nolint:depguard // wrapper for this package
)
type ConfigKey interface {
@@ -26,6 +26,7 @@ type ConfigKey interface {
In(defaultVal string, candidates []string) string
String() string
Strings(delim string) []string
+ Bool() (bool, error)
MustString(defaultVal string) string
MustBool(defaultVal ...bool) bool
@@ -257,7 +258,7 @@ func (p *iniConfigProvider) Save() error {
}
filename := p.file
if filename == "" {
- return fmt.Errorf("config file path must not be empty")
+ return errors.New("config file path must not be empty")
}
if p.loadedFromEmpty {
if err := os.MkdirAll(filepath.Dir(filename), os.ModePerm); err != nil {
diff --git a/modules/setting/config_provider_test.go b/modules/setting/config_provider_test.go
index a666d124c7..63121f0074 100644
--- a/modules/setting/config_provider_test.go
+++ b/modules/setting/config_provider_test.go
@@ -62,17 +62,17 @@ key = 123
// test default behavior
assert.Equal(t, "123", ConfigSectionKeyString(sec, "key"))
- assert.Equal(t, "", ConfigSectionKeyString(secSub, "key"))
+ assert.Empty(t, ConfigSectionKeyString(secSub, "key"))
assert.Equal(t, "def", ConfigSectionKeyString(secSub, "key", "def"))
assert.Equal(t, "123", ConfigInheritedKeyString(secSub, "key"))
// Workaround for ini package's BuggyKeyOverwritten behavior
- assert.Equal(t, "", ConfigSectionKeyString(sec, "empty"))
- assert.Equal(t, "", ConfigSectionKeyString(secSub, "empty"))
+ assert.Empty(t, ConfigSectionKeyString(sec, "empty"))
+ assert.Empty(t, ConfigSectionKeyString(secSub, "empty"))
assert.Equal(t, "def", ConfigInheritedKey(secSub, "empty").MustString("def"))
assert.Equal(t, "def", ConfigInheritedKey(secSub, "empty").MustString("xyz"))
- assert.Equal(t, "", ConfigSectionKeyString(sec, "empty"))
+ assert.Empty(t, ConfigSectionKeyString(sec, "empty"))
assert.Equal(t, "def", ConfigSectionKeyString(secSub, "empty"))
}
diff --git a/modules/setting/cron_test.go b/modules/setting/cron_test.go
index 55244d7075..39a228068a 100644
--- a/modules/setting/cron_test.go
+++ b/modules/setting/cron_test.go
@@ -38,6 +38,6 @@ EXTEND = true
_, err = getCronSettings(cfg, "test", extended)
assert.NoError(t, err)
assert.True(t, extended.Base)
- assert.EqualValues(t, "white rabbit", extended.Second)
+ assert.Equal(t, "white rabbit", extended.Second)
assert.True(t, extended.Extend)
}
diff --git a/modules/setting/git_test.go b/modules/setting/git_test.go
index 441c514d8c..0d7f634abf 100644
--- a/modules/setting/git_test.go
+++ b/modules/setting/git_test.go
@@ -6,6 +6,8 @@ package setting
import (
"testing"
+ "code.gitea.io/gitea/modules/test"
+
"github.com/stretchr/testify/assert"
)
@@ -23,8 +25,8 @@ a.b = 1
`)
assert.NoError(t, err)
loadGitFrom(cfg)
- assert.EqualValues(t, "1", GitConfig.Options["a.b"])
- assert.EqualValues(t, "histogram", GitConfig.Options["diff.algorithm"])
+ assert.Equal(t, "1", GitConfig.Options["a.b"])
+ assert.Equal(t, "histogram", GitConfig.Options["diff.algorithm"])
cfg, err = NewConfigProviderFromData(`
[git.config]
@@ -32,24 +34,20 @@ diff.algorithm = other
`)
assert.NoError(t, err)
loadGitFrom(cfg)
- assert.EqualValues(t, "other", GitConfig.Options["diff.algorithm"])
+ assert.Equal(t, "other", GitConfig.Options["diff.algorithm"])
}
func TestGitReflog(t *testing.T) {
- oldGit := Git
- oldGitConfig := GitConfig
- defer func() {
- Git = oldGit
- GitConfig = oldGitConfig
- }()
+ defer test.MockVariableValue(&Git)
+ defer test.MockVariableValue(&GitConfig)
// default reflog config without legacy options
cfg, err := NewConfigProviderFromData(``)
assert.NoError(t, err)
loadGitFrom(cfg)
- assert.EqualValues(t, "true", GitConfig.GetOption("core.logAllRefUpdates"))
- assert.EqualValues(t, "90", GitConfig.GetOption("gc.reflogExpire"))
+ assert.Equal(t, "true", GitConfig.GetOption("core.logAllRefUpdates"))
+ assert.Equal(t, "90", GitConfig.GetOption("gc.reflogExpire"))
// custom reflog config by legacy options
cfg, err = NewConfigProviderFromData(`
@@ -60,6 +58,6 @@ EXPIRATION = 123
assert.NoError(t, err)
loadGitFrom(cfg)
- assert.EqualValues(t, "false", GitConfig.GetOption("core.logAllRefUpdates"))
- assert.EqualValues(t, "123", GitConfig.GetOption("gc.reflogExpire"))
+ assert.Equal(t, "false", GitConfig.GetOption("core.logAllRefUpdates"))
+ assert.Equal(t, "123", GitConfig.GetOption("gc.reflogExpire"))
}
diff --git a/modules/setting/global_lock_test.go b/modules/setting/global_lock_test.go
index 5eeb275523..5e15eb3483 100644
--- a/modules/setting/global_lock_test.go
+++ b/modules/setting/global_lock_test.go
@@ -16,7 +16,7 @@ func TestLoadGlobalLockConfig(t *testing.T) {
assert.NoError(t, err)
loadGlobalLockFrom(cfg)
- assert.EqualValues(t, "memory", GlobalLock.ServiceType)
+ assert.Equal(t, "memory", GlobalLock.ServiceType)
})
t.Run("RedisGlobalLockConfig", func(t *testing.T) {
@@ -29,7 +29,7 @@ SERVICE_CONN_STR = addrs=127.0.0.1:6379 db=0
assert.NoError(t, err)
loadGlobalLockFrom(cfg)
- assert.EqualValues(t, "redis", GlobalLock.ServiceType)
- assert.EqualValues(t, "addrs=127.0.0.1:6379 db=0", GlobalLock.ServiceConnStr)
+ assert.Equal(t, "redis", GlobalLock.ServiceType)
+ assert.Equal(t, "addrs=127.0.0.1:6379 db=0", GlobalLock.ServiceConnStr)
})
}
diff --git a/modules/setting/incoming_email.go b/modules/setting/incoming_email.go
index bf81f292a2..4e433dde60 100644
--- a/modules/setting/incoming_email.go
+++ b/modules/setting/incoming_email.go
@@ -4,6 +4,7 @@
package setting
import (
+ "errors"
"fmt"
"net/mail"
"strings"
@@ -50,7 +51,7 @@ func checkReplyToAddress() error {
}
if parsed.Name != "" {
- return fmt.Errorf("name must not be set")
+ return errors.New("name must not be set")
}
c := strings.Count(IncomingEmail.ReplyToAddress, IncomingEmail.TokenPlaceholder)
diff --git a/modules/setting/indexer.go b/modules/setting/indexer.go
index e34baae012..ace7eec70e 100644
--- a/modules/setting/indexer.go
+++ b/modules/setting/indexer.go
@@ -96,7 +96,7 @@ func loadIndexerFrom(rootCfg ConfigProvider) {
// IndexerGlobFromString parses a comma separated list of patterns and returns a glob.Glob slice suited for repo indexing
func IndexerGlobFromString(globstr string) []*GlobMatcher {
extarr := make([]*GlobMatcher, 0, 10)
- for _, expr := range strings.Split(strings.ToLower(globstr), ",") {
+ for expr := range strings.SplitSeq(strings.ToLower(globstr), ",") {
expr = strings.TrimSpace(expr)
if expr != "" {
if g, err := GlobMatcherCompile(expr, '.', '/'); err != nil {
diff --git a/modules/setting/lfs_test.go b/modules/setting/lfs_test.go
index d27dd7c5bf..1b829d8839 100644
--- a/modules/setting/lfs_test.go
+++ b/modules/setting/lfs_test.go
@@ -19,7 +19,7 @@ func Test_getStorageInheritNameSectionTypeForLFS(t *testing.T) {
assert.NoError(t, loadLFSFrom(cfg))
assert.EqualValues(t, "minio", LFS.Storage.Type)
- assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
iniStr = `
[server]
@@ -54,7 +54,7 @@ STORAGE_TYPE = minio
assert.NoError(t, loadLFSFrom(cfg))
assert.EqualValues(t, "minio", LFS.Storage.Type)
- assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
iniStr = `
[lfs]
@@ -68,7 +68,7 @@ STORAGE_TYPE = minio
assert.NoError(t, loadLFSFrom(cfg))
assert.EqualValues(t, "minio", LFS.Storage.Type)
- assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
iniStr = `
[lfs]
@@ -83,7 +83,7 @@ STORAGE_TYPE = minio
assert.NoError(t, loadLFSFrom(cfg))
assert.EqualValues(t, "minio", LFS.Storage.Type)
- assert.EqualValues(t, "my_lfs/", LFS.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "my_lfs/", LFS.Storage.MinioConfig.BasePath)
}
func Test_LFSStorage1(t *testing.T) {
@@ -96,8 +96,8 @@ STORAGE_TYPE = minio
assert.NoError(t, loadLFSFrom(cfg))
assert.EqualValues(t, "minio", LFS.Storage.Type)
- assert.EqualValues(t, "gitea", LFS.Storage.MinioConfig.Bucket)
- assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea", LFS.Storage.MinioConfig.Bucket)
+ assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
}
func Test_LFSClientServerConfigs(t *testing.T) {
@@ -112,9 +112,9 @@ BATCH_SIZE = 0
assert.NoError(t, err)
assert.NoError(t, loadLFSFrom(cfg))
- assert.EqualValues(t, 100, LFS.MaxBatchSize)
- assert.EqualValues(t, 20, LFSClient.BatchSize)
- assert.EqualValues(t, 8, LFSClient.BatchOperationConcurrency)
+ assert.Equal(t, 100, LFS.MaxBatchSize)
+ assert.Equal(t, 20, LFSClient.BatchSize)
+ assert.Equal(t, 8, LFSClient.BatchOperationConcurrency)
iniStr = `
[lfs_client]
@@ -125,6 +125,6 @@ BATCH_OPERATION_CONCURRENCY = 10
assert.NoError(t, err)
assert.NoError(t, loadLFSFrom(cfg))
- assert.EqualValues(t, 50, LFSClient.BatchSize)
- assert.EqualValues(t, 10, LFSClient.BatchOperationConcurrency)
+ assert.Equal(t, 50, LFSClient.BatchSize)
+ assert.Equal(t, 10, LFSClient.BatchOperationConcurrency)
}
diff --git a/modules/setting/log.go b/modules/setting/log.go
index 50c5779994..59866c7605 100644
--- a/modules/setting/log.go
+++ b/modules/setting/log.go
@@ -7,7 +7,6 @@ import (
"fmt"
golog "log"
"os"
- "path"
"path/filepath"
"strings"
@@ -41,7 +40,7 @@ func loadLogGlobalFrom(rootCfg ConfigProvider) {
Log.BufferLen = sec.Key("BUFFER_LEN").MustInt(10000)
Log.Mode = sec.Key("MODE").MustString("console")
- Log.RootPath = sec.Key("ROOT_PATH").MustString(path.Join(AppWorkPath, "log"))
+ Log.RootPath = sec.Key("ROOT_PATH").MustString(filepath.Join(AppWorkPath, "log"))
if !filepath.IsAbs(Log.RootPath) {
Log.RootPath = filepath.Join(AppWorkPath, Log.RootPath)
}
@@ -228,8 +227,8 @@ func initLoggerByName(manager *log.LoggerManager, rootCfg ConfigProvider, logger
}
var eventWriters []log.EventWriter
- modes := strings.Split(modeVal, ",")
- for _, modeName := range modes {
+ modes := strings.SplitSeq(modeVal, ",")
+ for modeName := range modes {
modeName = strings.TrimSpace(modeName)
if modeName == "" {
continue
diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go
index 4c3dff6850..e79ff30447 100644
--- a/modules/setting/mailer.go
+++ b/modules/setting/mailer.go
@@ -13,7 +13,7 @@ import (
"code.gitea.io/gitea/modules/log"
- shellquote "github.com/kballard/go-shellquote"
+ "github.com/kballard/go-shellquote"
)
// Mailer represents mail service.
@@ -29,6 +29,9 @@ type Mailer struct {
SubjectPrefix string `ini:"SUBJECT_PREFIX"`
OverrideHeader map[string][]string `ini:"-"`
+ // Embed attachment images as inline base64 img src attribute
+ EmbedAttachmentImages bool
+
// SMTP sender
Protocol string `ini:"PROTOCOL"`
SMTPAddr string `ini:"SMTP_ADDR"`
diff --git a/modules/setting/mailer_test.go b/modules/setting/mailer_test.go
index fbabf11378..ceef35b051 100644
--- a/modules/setting/mailer_test.go
+++ b/modules/setting/mailer_test.go
@@ -34,8 +34,8 @@ func Test_loadMailerFrom(t *testing.T) {
// Check mailer setting
loadMailerFrom(cfg)
- assert.EqualValues(t, kase.SMTPAddr, MailService.SMTPAddr)
- assert.EqualValues(t, kase.SMTPPort, MailService.SMTPPort)
+ assert.Equal(t, kase.SMTPAddr, MailService.SMTPAddr)
+ assert.Equal(t, kase.SMTPPort, MailService.SMTPPort)
})
}
}
diff --git a/modules/setting/markup.go b/modules/setting/markup.go
index dfce8afa77..057b0650c3 100644
--- a/modules/setting/markup.go
+++ b/modules/setting/markup.go
@@ -8,6 +8,7 @@ import (
"strings"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/util"
)
// ExternalMarkupRenderers represents the external markup renderers
@@ -23,18 +24,33 @@ const (
RenderContentModeIframe = "iframe"
)
+type MarkdownRenderOptions struct {
+ NewLineHardBreak bool
+ ShortIssuePattern bool // Actually it is a "markup" option because it is used in "post processor"
+}
+
+type MarkdownMathCodeBlockOptions struct {
+ ParseInlineDollar bool
+ ParseInlineParentheses bool
+ ParseBlockDollar bool
+ ParseBlockSquareBrackets bool
+}
+
// Markdown settings
var Markdown = struct {
- EnableHardLineBreakInComments bool
- EnableHardLineBreakInDocuments bool
- CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
- FileExtensions []string
- EnableMath bool
+ RenderOptionsComment MarkdownRenderOptions `ini:"-"`
+ RenderOptionsWiki MarkdownRenderOptions `ini:"-"`
+ RenderOptionsRepoFile MarkdownRenderOptions `ini:"-"`
+
+ CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"` // Actually it is a "markup" option because it is used in "post processor"
+ FileExtensions []string
+
+ EnableMath bool
+ MathCodeBlockDetection []string
+ MathCodeBlockOptions MarkdownMathCodeBlockOptions `ini:"-"`
}{
- EnableHardLineBreakInComments: true,
- EnableHardLineBreakInDocuments: false,
- FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd,.livemd", ","),
- EnableMath: true,
+ FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd,.livemd", ","),
+ EnableMath: true,
}
// MarkupRenderer defines the external parser configured in ini
@@ -60,8 +76,58 @@ type MarkupSanitizerRule struct {
func loadMarkupFrom(rootCfg ConfigProvider) {
mustMapSetting(rootCfg, "markdown", &Markdown)
+ const none = "none"
+
+ const renderOptionShortIssuePattern = "short-issue-pattern"
+ const renderOptionNewLineHardBreak = "new-line-hard-break"
+ cfgMarkdown := rootCfg.Section("markdown")
+ parseMarkdownRenderOptions := func(key string, defaults []string) (ret MarkdownRenderOptions) {
+ options := cfgMarkdown.Key(key).Strings(",")
+ options = util.IfEmpty(options, defaults)
+ for _, opt := range options {
+ switch opt {
+ case renderOptionShortIssuePattern:
+ ret.ShortIssuePattern = true
+ case renderOptionNewLineHardBreak:
+ ret.NewLineHardBreak = true
+ case none:
+ ret = MarkdownRenderOptions{}
+ case "":
+ default:
+ log.Error("Unknown markdown render option in %s: %s", key, opt)
+ }
+ }
+ return ret
+ }
+ Markdown.RenderOptionsComment = parseMarkdownRenderOptions("RENDER_OPTIONS_COMMENT", []string{renderOptionShortIssuePattern, renderOptionNewLineHardBreak})
+ Markdown.RenderOptionsWiki = parseMarkdownRenderOptions("RENDER_OPTIONS_WIKI", []string{renderOptionShortIssuePattern})
+ Markdown.RenderOptionsRepoFile = parseMarkdownRenderOptions("RENDER_OPTIONS_REPO_FILE", nil)
+
+ const mathCodeInlineDollar = "inline-dollar"
+ const mathCodeInlineParentheses = "inline-parentheses"
+ const mathCodeBlockDollar = "block-dollar"
+ const mathCodeBlockSquareBrackets = "block-square-brackets"
+ Markdown.MathCodeBlockDetection = util.IfEmpty(Markdown.MathCodeBlockDetection, []string{mathCodeInlineDollar, mathCodeBlockDollar})
+ Markdown.MathCodeBlockOptions = MarkdownMathCodeBlockOptions{}
+ for _, s := range Markdown.MathCodeBlockDetection {
+ switch s {
+ case mathCodeInlineDollar:
+ Markdown.MathCodeBlockOptions.ParseInlineDollar = true
+ case mathCodeInlineParentheses:
+ Markdown.MathCodeBlockOptions.ParseInlineParentheses = true
+ case mathCodeBlockDollar:
+ Markdown.MathCodeBlockOptions.ParseBlockDollar = true
+ case mathCodeBlockSquareBrackets:
+ Markdown.MathCodeBlockOptions.ParseBlockSquareBrackets = true
+ case none:
+ Markdown.MathCodeBlockOptions = MarkdownMathCodeBlockOptions{}
+ case "":
+ default:
+ log.Error("Unknown math code block detection option: %s", s)
+ }
+ }
- MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000)
+ MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(50000)
ExternalMarkupRenderers = make([]*MarkupRenderer, 0, 10)
ExternalSanitizerRules = make([]MarkupSanitizerRule, 0, 10)
@@ -83,8 +149,8 @@ func loadMarkupFrom(rootCfg ConfigProvider) {
func newMarkupSanitizer(name string, sec ConfigSection) {
rule, ok := createMarkupSanitizerRule(name, sec)
if ok {
- if strings.HasPrefix(name, "sanitizer.") {
- names := strings.SplitN(strings.TrimPrefix(name, "sanitizer."), ".", 2)
+ if after, found := strings.CutPrefix(name, "sanitizer."); found {
+ names := strings.SplitN(after, ".", 2)
name = names[0]
}
for _, renderer := range ExternalMarkupRenderers {
diff --git a/modules/setting/markup_test.go b/modules/setting/markup_test.go
new file mode 100644
index 0000000000..c47a38ce15
--- /dev/null
+++ b/modules/setting/markup_test.go
@@ -0,0 +1,51 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package setting
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestLoadMarkup(t *testing.T) {
+ cfg, _ := NewConfigProviderFromData(``)
+ loadMarkupFrom(cfg)
+ assert.Equal(t, MarkdownMathCodeBlockOptions{ParseInlineDollar: true, ParseBlockDollar: true}, Markdown.MathCodeBlockOptions)
+ assert.Equal(t, MarkdownRenderOptions{NewLineHardBreak: true, ShortIssuePattern: true}, Markdown.RenderOptionsComment)
+ assert.Equal(t, MarkdownRenderOptions{ShortIssuePattern: true}, Markdown.RenderOptionsWiki)
+ assert.Equal(t, MarkdownRenderOptions{}, Markdown.RenderOptionsRepoFile)
+
+ t.Run("Math", func(t *testing.T) {
+ cfg, _ = NewConfigProviderFromData(`
+[markdown]
+MATH_CODE_BLOCK_DETECTION = none
+`)
+ loadMarkupFrom(cfg)
+ assert.Equal(t, MarkdownMathCodeBlockOptions{}, Markdown.MathCodeBlockOptions)
+
+ cfg, _ = NewConfigProviderFromData(`
+[markdown]
+MATH_CODE_BLOCK_DETECTION = inline-dollar, inline-parentheses, block-dollar, block-square-brackets
+`)
+ loadMarkupFrom(cfg)
+ assert.Equal(t, MarkdownMathCodeBlockOptions{ParseInlineDollar: true, ParseInlineParentheses: true, ParseBlockDollar: true, ParseBlockSquareBrackets: true}, Markdown.MathCodeBlockOptions)
+ })
+
+ t.Run("Render", func(t *testing.T) {
+ cfg, _ = NewConfigProviderFromData(`
+[markdown]
+RENDER_OPTIONS_COMMENT = none
+`)
+ loadMarkupFrom(cfg)
+ assert.Equal(t, MarkdownRenderOptions{}, Markdown.RenderOptionsComment)
+
+ cfg, _ = NewConfigProviderFromData(`
+[markdown]
+RENDER_OPTIONS_REPO_FILE = short-issue-pattern, new-line-hard-break
+`)
+ loadMarkupFrom(cfg)
+ assert.Equal(t, MarkdownRenderOptions{NewLineHardBreak: true, ShortIssuePattern: true}, Markdown.RenderOptionsRepoFile)
+ })
+}
diff --git a/modules/setting/mirror.go b/modules/setting/mirror.go
index 3aa530a1f4..300711789d 100644
--- a/modules/setting/mirror.go
+++ b/modules/setting/mirror.go
@@ -48,11 +48,7 @@ func loadMirrorFrom(rootCfg ConfigProvider) {
Mirror.MinInterval = 1 * time.Minute
}
if Mirror.DefaultInterval < Mirror.MinInterval {
- if time.Hour*8 < Mirror.MinInterval {
- Mirror.DefaultInterval = Mirror.MinInterval
- } else {
- Mirror.DefaultInterval = time.Hour * 8
- }
+ Mirror.DefaultInterval = max(time.Hour*8, Mirror.MinInterval)
log.Warn("Mirror.DefaultInterval is less than Mirror.MinInterval, set to %s", Mirror.DefaultInterval.String())
}
}
diff --git a/modules/setting/oauth2_test.go b/modules/setting/oauth2_test.go
index d0e5ccf13d..c6e66cad02 100644
--- a/modules/setting/oauth2_test.go
+++ b/modules/setting/oauth2_test.go
@@ -31,7 +31,7 @@ JWT_SECRET = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
actual := GetGeneralTokenSigningSecret()
expected, _ := generate.DecodeJwtSecretBase64("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB")
assert.Len(t, actual, 32)
- assert.EqualValues(t, expected, actual)
+ assert.Equal(t, expected, actual)
}
func TestGetGeneralSigningSecretSave(t *testing.T) {
diff --git a/modules/setting/packages.go b/modules/setting/packages.go
index 3f618cfd64..b598424064 100644
--- a/modules/setting/packages.go
+++ b/modules/setting/packages.go
@@ -6,8 +6,6 @@ package setting
import (
"fmt"
"math"
- "os"
- "path/filepath"
"github.com/dustin/go-humanize"
)
@@ -15,9 +13,8 @@ import (
// Package registry settings
var (
Packages = struct {
- Storage *Storage
- Enabled bool
- ChunkedUploadPath string
+ Storage *Storage
+ Enabled bool
LimitTotalOwnerCount int64
LimitTotalOwnerSize int64
@@ -67,17 +64,6 @@ func loadPackagesFrom(rootCfg ConfigProvider) (err error) {
return err
}
- Packages.ChunkedUploadPath = filepath.ToSlash(sec.Key("CHUNKED_UPLOAD_PATH").MustString("tmp/package-upload"))
- if !filepath.IsAbs(Packages.ChunkedUploadPath) {
- Packages.ChunkedUploadPath = filepath.ToSlash(filepath.Join(AppDataPath, Packages.ChunkedUploadPath))
- }
-
- if HasInstallLock(rootCfg) {
- if err := os.MkdirAll(Packages.ChunkedUploadPath, os.ModePerm); err != nil {
- return fmt.Errorf("unable to create chunked upload directory: %s (%v)", Packages.ChunkedUploadPath, err)
- }
- }
-
Packages.LimitTotalOwnerSize = mustBytes(sec, "LIMIT_TOTAL_OWNER_SIZE")
Packages.LimitSizeAlpine = mustBytes(sec, "LIMIT_SIZE_ALPINE")
Packages.LimitSizeArch = mustBytes(sec, "LIMIT_SIZE_ARCH")
diff --git a/modules/setting/packages_test.go b/modules/setting/packages_test.go
index 87de276041..47378f35ad 100644
--- a/modules/setting/packages_test.go
+++ b/modules/setting/packages_test.go
@@ -41,7 +41,7 @@ STORAGE_TYPE = minio
assert.NoError(t, loadPackagesFrom(cfg))
assert.EqualValues(t, "minio", Packages.Storage.Type)
- assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "packages/", Packages.Storage.MinioConfig.BasePath)
// we can also configure packages storage directly
iniStr = `
@@ -53,7 +53,7 @@ STORAGE_TYPE = minio
assert.NoError(t, loadPackagesFrom(cfg))
assert.EqualValues(t, "minio", Packages.Storage.Type)
- assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "packages/", Packages.Storage.MinioConfig.BasePath)
// or we can indicate the storage type in the packages section
iniStr = `
@@ -68,7 +68,7 @@ STORAGE_TYPE = minio
assert.NoError(t, loadPackagesFrom(cfg))
assert.EqualValues(t, "minio", Packages.Storage.Type)
- assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "packages/", Packages.Storage.MinioConfig.BasePath)
// or we can indicate the storage type and minio base path in the packages section
iniStr = `
@@ -84,7 +84,7 @@ STORAGE_TYPE = minio
assert.NoError(t, loadPackagesFrom(cfg))
assert.EqualValues(t, "minio", Packages.Storage.Type)
- assert.EqualValues(t, "my_packages/", Packages.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "my_packages/", Packages.Storage.MinioConfig.BasePath)
}
func Test_PackageStorage1(t *testing.T) {
@@ -109,8 +109,8 @@ MINIO_SECRET_ACCESS_KEY = correct_key
storage := Packages.Storage
assert.EqualValues(t, "minio", storage.Type)
- assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket)
- assert.EqualValues(t, "packages/", storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea", storage.MinioConfig.Bucket)
+ assert.Equal(t, "packages/", storage.MinioConfig.BasePath)
assert.True(t, storage.MinioConfig.ServeDirect)
}
@@ -136,8 +136,8 @@ MINIO_SECRET_ACCESS_KEY = correct_key
storage := Packages.Storage
assert.EqualValues(t, "minio", storage.Type)
- assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket)
- assert.EqualValues(t, "packages/", storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea", storage.MinioConfig.Bucket)
+ assert.Equal(t, "packages/", storage.MinioConfig.BasePath)
assert.True(t, storage.MinioConfig.ServeDirect)
}
@@ -164,8 +164,8 @@ MINIO_SECRET_ACCESS_KEY = correct_key
storage := Packages.Storage
assert.EqualValues(t, "minio", storage.Type)
- assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket)
- assert.EqualValues(t, "my_packages/", storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea", storage.MinioConfig.Bucket)
+ assert.Equal(t, "my_packages/", storage.MinioConfig.BasePath)
assert.True(t, storage.MinioConfig.ServeDirect)
}
@@ -192,7 +192,7 @@ MINIO_SECRET_ACCESS_KEY = correct_key
storage := Packages.Storage
assert.EqualValues(t, "minio", storage.Type)
- assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket)
- assert.EqualValues(t, "my_packages/", storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea", storage.MinioConfig.Bucket)
+ assert.Equal(t, "my_packages/", storage.MinioConfig.BasePath)
assert.True(t, storage.MinioConfig.ServeDirect)
}
diff --git a/modules/setting/path.go b/modules/setting/path.go
index 0fdc305aa1..f51457a620 100644
--- a/modules/setting/path.go
+++ b/modules/setting/path.go
@@ -11,6 +11,7 @@ import (
"strings"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/tempdir"
)
var (
@@ -196,3 +197,18 @@ func InitWorkPathAndCfgProvider(getEnvFn func(name string) string, args ArgWorkP
CustomPath = tmpCustomPath.Value
CustomConf = tmpCustomConf.Value
}
+
+// AppDataTempDir returns a managed temporary directory for the application data.
+// Using empty sub will get the managed base temp directory, and it's safe to delete it.
+// Gitea only creates subdirectories under it, but not the APP_TEMP_PATH directory itself.
+// * When APP_TEMP_PATH="/tmp": the managed temp directory is "/tmp/gitea-tmp"
+// * When APP_TEMP_PATH is not set: the managed temp directory is "/{APP_DATA_PATH}/tmp"
+func AppDataTempDir(sub string) *tempdir.TempDir {
+ if appTempPathInternal != "" {
+ return tempdir.New(appTempPathInternal, "gitea-tmp/"+sub)
+ }
+ if AppDataPath == "" {
+ panic("setting.AppDataPath is not set")
+ }
+ return tempdir.New(AppDataPath, "tmp/"+sub)
+}
diff --git a/modules/setting/repository.go b/modules/setting/repository.go
index c5619d0f04..318cf41108 100644
--- a/modules/setting/repository.go
+++ b/modules/setting/repository.go
@@ -5,7 +5,6 @@ package setting
import (
"os/exec"
- "path"
"path/filepath"
"strings"
@@ -63,17 +62,11 @@ var (
// Repository upload settings
Upload struct {
Enabled bool
- TempPath string
AllowedTypes string
FileMaxSize int64
MaxFiles int
} `ini:"-"`
- // Repository local settings
- Local struct {
- LocalCopyPath string
- } `ini:"-"`
-
// Pull request settings
PullRequest struct {
WorkInProgressPrefixes []string
@@ -89,6 +82,7 @@ var (
AddCoCommitterTrailers bool
TestConflictingPatchesWithGitApply bool
RetargetChildrenOnMerge bool
+ DelayCheckForInactiveDays int
} `ini:"repository.pull-request"`
// Issue Setting
@@ -106,11 +100,13 @@ var (
SigningKey string
SigningName string
SigningEmail string
+ SigningFormat string
InitialCommit []string
CRUDActions []string `ini:"CRUD_ACTIONS"`
Merges []string
Wiki []string
DefaultTrustModel string
+ TrustedSSHKeys []string `ini:"TRUSTED_SSH_KEYS"`
} `ini:"repository.signing"`
}{
DetectedCharsetsOrder: []string{
@@ -182,25 +178,16 @@ var (
// Repository upload settings
Upload: struct {
Enabled bool
- TempPath string
AllowedTypes string
FileMaxSize int64
MaxFiles int
}{
Enabled: true,
- TempPath: "data/tmp/uploads",
AllowedTypes: "",
FileMaxSize: 50,
MaxFiles: 5,
},
- // Repository local settings
- Local: struct {
- LocalCopyPath string
- }{
- LocalCopyPath: "tmp/local-repo",
- },
-
// Pull request settings
PullRequest: struct {
WorkInProgressPrefixes []string
@@ -216,6 +203,7 @@ var (
AddCoCommitterTrailers bool
TestConflictingPatchesWithGitApply bool
RetargetChildrenOnMerge bool
+ DelayCheckForInactiveDays int
}{
WorkInProgressPrefixes: []string{"WIP:", "[WIP]"},
// Same as GitHub. See
@@ -231,6 +219,7 @@ var (
PopulateSquashCommentWithCommitMessages: false,
AddCoCommitterTrailers: true,
RetargetChildrenOnMerge: true,
+ DelayCheckForInactiveDays: 7,
},
// Issue settings
@@ -255,20 +244,24 @@ var (
SigningKey string
SigningName string
SigningEmail string
+ SigningFormat string
InitialCommit []string
CRUDActions []string `ini:"CRUD_ACTIONS"`
Merges []string
Wiki []string
DefaultTrustModel string
+ TrustedSSHKeys []string `ini:"TRUSTED_SSH_KEYS"`
}{
SigningKey: "default",
SigningName: "",
SigningEmail: "",
+ SigningFormat: "openpgp", // git.SigningKeyFormatOpenPGP
InitialCommit: []string{"always"},
CRUDActions: []string{"pubkey", "twofa", "parentsigned"},
Merges: []string{"pubkey", "twofa", "basesigned", "commitssigned"},
Wiki: []string{"never"},
DefaultTrustModel: "collaborator",
+ TrustedSSHKeys: []string{},
},
}
RepoRootPath string
@@ -284,7 +277,7 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
Repository.GoGetCloneURLProtocol = sec.Key("GO_GET_CLONE_URL_PROTOCOL").MustString("https")
Repository.MaxCreationLimit = sec.Key("MAX_CREATION_LIMIT").MustInt(-1)
Repository.DefaultBranch = sec.Key("DEFAULT_BRANCH").MustString(Repository.DefaultBranch)
- RepoRootPath = sec.Key("ROOT").MustString(path.Join(AppDataPath, "gitea-repositories"))
+ RepoRootPath = sec.Key("ROOT").MustString(filepath.Join(AppDataPath, "gitea-repositories"))
if !filepath.IsAbs(RepoRootPath) {
RepoRootPath = filepath.Join(AppWorkPath, RepoRootPath)
} else {
@@ -309,8 +302,6 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
log.Fatal("Failed to map Repository.Editor settings: %v", err)
} else if err = rootCfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil {
log.Fatal("Failed to map Repository.Upload settings: %v", err)
- } else if err = rootCfg.Section("repository.local").MapTo(&Repository.Local); err != nil {
- log.Fatal("Failed to map Repository.Local settings: %v", err)
} else if err = rootCfg.Section("repository.pull-request").MapTo(&Repository.PullRequest); err != nil {
log.Fatal("Failed to map Repository.PullRequest settings: %v", err)
}
@@ -362,10 +353,6 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
}
}
- if !filepath.IsAbs(Repository.Upload.TempPath) {
- Repository.Upload.TempPath = path.Join(AppWorkPath, Repository.Upload.TempPath)
- }
-
if err := loadRepoArchiveFrom(rootCfg); err != nil {
log.Fatal("loadRepoArchiveFrom: %v", err)
}
diff --git a/modules/setting/repository_archive_test.go b/modules/setting/repository_archive_test.go
index a0f91f0da1..d5b95272d6 100644
--- a/modules/setting/repository_archive_test.go
+++ b/modules/setting/repository_archive_test.go
@@ -20,7 +20,7 @@ STORAGE_TYPE = minio
assert.NoError(t, loadRepoArchiveFrom(cfg))
assert.EqualValues(t, "minio", RepoArchive.Storage.Type)
- assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
// we can also configure packages storage directly
iniStr = `
@@ -32,7 +32,7 @@ STORAGE_TYPE = minio
assert.NoError(t, loadRepoArchiveFrom(cfg))
assert.EqualValues(t, "minio", RepoArchive.Storage.Type)
- assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
// or we can indicate the storage type in the packages section
iniStr = `
@@ -47,7 +47,7 @@ STORAGE_TYPE = minio
assert.NoError(t, loadRepoArchiveFrom(cfg))
assert.EqualValues(t, "minio", RepoArchive.Storage.Type)
- assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
// or we can indicate the storage type and minio base path in the packages section
iniStr = `
@@ -63,7 +63,7 @@ STORAGE_TYPE = minio
assert.NoError(t, loadRepoArchiveFrom(cfg))
assert.EqualValues(t, "minio", RepoArchive.Storage.Type)
- assert.EqualValues(t, "my_archive/", RepoArchive.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "my_archive/", RepoArchive.Storage.MinioConfig.BasePath)
}
func Test_RepoArchiveStorage(t *testing.T) {
@@ -85,7 +85,7 @@ MINIO_SECRET_ACCESS_KEY = correct_key
storage := RepoArchive.Storage
assert.EqualValues(t, "minio", storage.Type)
- assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket)
+ assert.Equal(t, "gitea", storage.MinioConfig.Bucket)
iniStr = `
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -107,5 +107,5 @@ MINIO_SECRET_ACCESS_KEY = correct_key
storage = RepoArchive.Storage
assert.EqualValues(t, "minio", storage.Type)
- assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket)
+ assert.Equal(t, "gitea", storage.MinioConfig.Bucket)
}
diff --git a/modules/setting/security.go b/modules/setting/security.go
index 2f798b75c7..153b6bc944 100644
--- a/modules/setting/security.go
+++ b/modules/setting/security.go
@@ -39,6 +39,7 @@ var (
CSRFCookieName = "_csrf"
CSRFCookieHTTPOnly = true
RecordUserSignupMetadata = false
+ TwoFactorAuthEnforced = false
)
// loadSecret load the secret from ini by uriKey or verbatimKey, only one of them could be set
@@ -110,7 +111,7 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
if SecretKey == "" {
// FIXME: https://github.com/go-gitea/gitea/issues/16832
// Until it supports rotating an existing secret key, we shouldn't move users off of the widely used default value
- SecretKey = "!#@FDEWREWR&*(" //nolint:gosec
+ SecretKey = "!#@FDEWREWR&*("
}
CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible")
@@ -142,6 +143,15 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false)
SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20)
+ twoFactorAuth := sec.Key("TWO_FACTOR_AUTH").String()
+ switch twoFactorAuth {
+ case "":
+ case "enforced":
+ TwoFactorAuthEnforced = true
+ default:
+ log.Fatal("Invalid two-factor auth option: %s", twoFactorAuth)
+ }
+
InternalToken = loadSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN")
if InstallLock && InternalToken == "" {
// if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate
diff --git a/modules/setting/server.go b/modules/setting/server.go
index d7a71578d4..8a22f6a844 100644
--- a/modules/setting/server.go
+++ b/modules/setting/server.go
@@ -7,6 +7,7 @@ import (
"encoding/base64"
"net"
"net/url"
+ "os"
"path/filepath"
"strconv"
"strings"
@@ -40,28 +41,47 @@ const (
LandingPageLogin LandingPage = "/user/login"
)
+const (
+ PublicURLAuto = "auto"
+ PublicURLLegacy = "legacy"
+)
+
// Server settings
var (
// AppURL is the Application ROOT_URL. It always has a '/' suffix
// It maps to ini:"ROOT_URL"
AppURL string
- // AppSubURL represents the sub-url mounting point for gitea. It is either "" or starts with '/' and ends without '/', such as '/{subpath}'.
+
+ // PublicURLDetection controls how to use the HTTP request headers to detect public URL
+ PublicURLDetection string
+
+ // AppSubURL represents the sub-url mounting point for gitea, parsed from "ROOT_URL"
+ // It is either "" or starts with '/' and ends without '/', such as '/{sub-path}'.
// This value is empty if site does not have sub-url.
AppSubURL string
- // UseSubURLPath makes Gitea handle requests with sub-path like "/sub-path/owner/repo/...", to make it easier to debug sub-path related problems without a reverse proxy.
+
+ // UseSubURLPath makes Gitea handle requests with sub-path like "/sub-path/owner/repo/...",
+ // to make it easier to debug sub-path related problems without a reverse proxy.
UseSubURLPath bool
+
// AppDataPath is the default path for storing data.
// It maps to ini:"APP_DATA_PATH" in [server] and defaults to AppWorkPath + "/data"
AppDataPath string
+
// LocalURL is the url for locally running applications to contact Gitea. It always has a '/' suffix
// It maps to ini:"LOCAL_ROOT_URL" in [server]
LocalURL string
- // AssetVersion holds a opaque value that is used for cache-busting assets
+
+ // AssetVersion holds an opaque value that is used for cache-busting assets
AssetVersion string
+ // appTempPathInternal is the temporary path for the app, it is only an internal variable
+ // DO NOT use it directly, always use AppDataTempDir
+ appTempPathInternal string
+
Protocol Scheme
- UseProxyProtocol bool // `ini:"USE_PROXY_PROTOCOL"`
- ProxyProtocolTLSBridging bool //`ini:"PROXY_PROTOCOL_TLS_BRIDGING"`
+ UseProxyProtocol bool
+ ProxyProtocolTLSBridging bool
ProxyProtocolHeaderTimeout time.Duration
ProxyProtocolAcceptUnknown bool
Domain string
@@ -169,20 +189,25 @@ func loadServerFrom(rootCfg ConfigProvider) {
HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")
HTTPPort = sec.Key("HTTP_PORT").MustString("3000")
- Protocol = HTTP
+ // DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version
+ // if these are removed, the warning will not be shown
+ if sec.HasKey("ENABLE_ACME") {
+ EnableAcme = sec.Key("ENABLE_ACME").MustBool(false)
+ } else {
+ deprecatedSetting(rootCfg, "server", "ENABLE_LETSENCRYPT", "server", "ENABLE_ACME", "v1.19.0")
+ EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false)
+ }
+
protocolCfg := sec.Key("PROTOCOL").String()
+ if protocolCfg != "https" && EnableAcme {
+ log.Fatal("ACME could only be used with HTTPS protocol")
+ }
+
switch protocolCfg {
+ case "", "http":
+ Protocol = HTTP
case "https":
Protocol = HTTPS
-
- // DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version
- // if these are removed, the warning will not be shown
- if sec.HasKey("ENABLE_ACME") {
- EnableAcme = sec.Key("ENABLE_ACME").MustBool(false)
- } else {
- deprecatedSetting(rootCfg, "server", "ENABLE_LETSENCRYPT", "server", "ENABLE_ACME", "v1.19.0")
- EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false)
- }
if EnableAcme {
AcmeURL = sec.Key("ACME_URL").MustString("")
AcmeCARoot = sec.Key("ACME_CA_ROOT").MustString("")
@@ -210,6 +235,9 @@ func loadServerFrom(rootCfg ConfigProvider) {
deprecatedSetting(rootCfg, "server", "LETSENCRYPT_EMAIL", "server", "ACME_EMAIL", "v1.19.0")
AcmeEmail = sec.Key("LETSENCRYPT_EMAIL").MustString("")
}
+ if AcmeEmail == "" {
+ log.Fatal("ACME Email is not set (ACME_EMAIL).")
+ }
} else {
CertFile = sec.Key("CERT_FILE").String()
KeyFile = sec.Key("KEY_FILE").String()
@@ -233,7 +261,7 @@ func loadServerFrom(rootCfg ConfigProvider) {
case "unix":
log.Warn("unix PROTOCOL value is deprecated, please use http+unix")
fallthrough
- case "http+unix":
+ default: // "http+unix"
Protocol = HTTPUnix
}
UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
@@ -246,6 +274,8 @@ func loadServerFrom(rootCfg ConfigProvider) {
if !filepath.IsAbs(HTTPAddr) {
HTTPAddr = filepath.Join(AppWorkPath, HTTPAddr)
}
+ default:
+ log.Fatal("Invalid PROTOCOL %q", Protocol)
}
UseProxyProtocol = sec.Key("USE_PROXY_PROTOCOL").MustBool(false)
ProxyProtocolTLSBridging = sec.Key("PROXY_PROTOCOL_TLS_BRIDGING").MustBool(false)
@@ -259,11 +289,15 @@ func loadServerFrom(rootCfg ConfigProvider) {
defaultAppURL := string(Protocol) + "://" + Domain + ":" + HTTPPort
AppURL = sec.Key("ROOT_URL").MustString(defaultAppURL)
+ PublicURLDetection = sec.Key("PUBLIC_URL_DETECTION").MustString(PublicURLLegacy)
+ if PublicURLDetection != PublicURLAuto && PublicURLDetection != PublicURLLegacy {
+ log.Fatal("Invalid PUBLIC_URL_DETECTION value: %s", PublicURLDetection)
+ }
// Check validity of AppURL
appURL, err := url.Parse(AppURL)
if err != nil {
- log.Fatal("Invalid ROOT_URL '%s': %s", AppURL, err)
+ log.Fatal("Invalid ROOT_URL %q: %s", AppURL, err)
}
// Remove default ports from AppURL.
// (scheme-based URL normalization, RFC 3986 section 6.2.3)
@@ -299,13 +333,15 @@ func loadServerFrom(rootCfg ConfigProvider) {
defaultLocalURL = AppURL
case FCGIUnix:
defaultLocalURL = AppURL
- default:
+ case HTTP, HTTPS:
defaultLocalURL = string(Protocol) + "://"
if HTTPAddr == "0.0.0.0" {
defaultLocalURL += net.JoinHostPort("localhost", HTTPPort) + "/"
} else {
defaultLocalURL += net.JoinHostPort(HTTPAddr, HTTPPort) + "/"
}
+ default:
+ log.Fatal("Invalid PROTOCOL %q", Protocol)
}
LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL)
LocalURL = strings.TrimRight(LocalURL, "/") + "/"
@@ -323,6 +359,19 @@ func loadServerFrom(rootCfg ConfigProvider) {
if !filepath.IsAbs(AppDataPath) {
AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath))
}
+ if IsInTesting && HasInstallLock(rootCfg) {
+ // FIXME: in testing, the "app data" directory is not correctly initialized before loading settings
+ if _, err := os.Stat(AppDataPath); err != nil {
+ _ = os.MkdirAll(AppDataPath, os.ModePerm)
+ }
+ }
+
+ appTempPathInternal = sec.Key("APP_TEMP_PATH").String()
+ if appTempPathInternal != "" {
+ if _, err := os.Stat(appTempPathInternal); err != nil {
+ log.Fatal("APP_TEMP_PATH %q is not accessible: %v", appTempPathInternal, err)
+ }
+ }
EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false)
diff --git a/modules/setting/service.go b/modules/setting/service.go
index 526ad64eb4..b1b9fedd62 100644
--- a/modules/setting/service.go
+++ b/modules/setting/service.go
@@ -5,6 +5,7 @@ package setting
import (
"regexp"
+ "runtime"
"strings"
"time"
@@ -43,9 +44,11 @@ var Service = struct {
ShowRegistrationButton bool
EnablePasswordSignInForm bool
ShowMilestonesDashboardPage bool
- RequireSignInView bool
+ RequireSignInViewStrict bool
+ BlockAnonymousAccessExpensive bool
EnableNotifyMail bool
EnableBasicAuth bool
+ EnablePasskeyAuth bool
EnableReverseProxyAuth bool
EnableReverseProxyAuthAPI bool
EnableReverseProxyAutoRegister bool
@@ -96,6 +99,13 @@ var Service = struct {
DisableOrganizationsPage bool `ini:"DISABLE_ORGANIZATIONS_PAGE"`
DisableCodePage bool `ini:"DISABLE_CODE_PAGE"`
} `ini:"service.explore"`
+
+ QoS struct {
+ Enabled bool
+ MaxInFlightRequests int
+ MaxWaitingRequests int
+ TargetWaitTime time.Duration
+ }
}{
AllowedUserVisibilityModesSlice: []bool{true, true, true},
}
@@ -158,9 +168,21 @@ func loadServiceFrom(rootCfg ConfigProvider) {
Service.EmailDomainBlockList = CompileEmailGlobList(sec, "EMAIL_DOMAIN_BLOCKLIST")
Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!(Service.DisableRegistration || Service.AllowOnlyExternalRegistration))
Service.ShowMilestonesDashboardPage = sec.Key("SHOW_MILESTONES_DASHBOARD_PAGE").MustBool(true)
- Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool()
+
+ // boolean values are considered as "strict"
+ var err error
+ Service.RequireSignInViewStrict, err = sec.Key("REQUIRE_SIGNIN_VIEW").Bool()
+ if s := sec.Key("REQUIRE_SIGNIN_VIEW").String(); err != nil && s != "" {
+ // non-boolean value only supports "expensive" at the moment
+ Service.BlockAnonymousAccessExpensive = s == "expensive"
+ if !Service.BlockAnonymousAccessExpensive {
+ log.Fatal("Invalid config option: REQUIRE_SIGNIN_VIEW = %s", s)
+ }
+ }
+
Service.EnableBasicAuth = sec.Key("ENABLE_BASIC_AUTHENTICATION").MustBool(true)
Service.EnablePasswordSignInForm = sec.Key("ENABLE_PASSWORD_SIGNIN_FORM").MustBool(true)
+ Service.EnablePasskeyAuth = sec.Key("ENABLE_PASSKEY_AUTHENTICATION").MustBool(true)
Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()
Service.EnableReverseProxyAuthAPI = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION_API").MustBool()
Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()
@@ -241,6 +263,7 @@ func loadServiceFrom(rootCfg ConfigProvider) {
mustMapSetting(rootCfg, "service.explore", &Service.Explore)
loadOpenIDSetting(rootCfg)
+ loadQosSetting(rootCfg)
}
func loadOpenIDSetting(rootCfg ConfigProvider) {
@@ -262,3 +285,11 @@ func loadOpenIDSetting(rootCfg ConfigProvider) {
}
}
}
+
+func loadQosSetting(rootCfg ConfigProvider) {
+ sec := rootCfg.Section("qos")
+ Service.QoS.Enabled = sec.Key("ENABLED").MustBool(false)
+ Service.QoS.MaxInFlightRequests = sec.Key("MAX_INFLIGHT").MustInt(4 * runtime.NumCPU())
+ Service.QoS.MaxWaitingRequests = sec.Key("MAX_WAITING").MustInt(100)
+ Service.QoS.TargetWaitTime = sec.Key("TARGET_WAIT_TIME").MustDuration(250 * time.Millisecond)
+}
diff --git a/modules/setting/service_test.go b/modules/setting/service_test.go
index 1647bcec16..73736b793a 100644
--- a/modules/setting/service_test.go
+++ b/modules/setting/service_test.go
@@ -7,16 +7,14 @@ import (
"testing"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/test"
"github.com/gobwas/glob"
"github.com/stretchr/testify/assert"
)
func TestLoadServices(t *testing.T) {
- oldService := Service
- defer func() {
- Service = oldService
- }()
+ defer test.MockVariableValue(&Service)()
cfg, err := NewConfigProviderFromData(`
[service]
@@ -48,10 +46,7 @@ EMAIL_DOMAIN_BLOCKLIST = d3, *.b
}
func TestLoadServiceVisibilityModes(t *testing.T) {
- oldService := Service
- defer func() {
- Service = oldService
- }()
+ defer test.MockVariableValue(&Service)()
kases := map[string]func(){
`
@@ -130,3 +125,33 @@ ALLOWED_USER_VISIBILITY_MODES = public, limit, privated
})
}
}
+
+func TestLoadServiceRequireSignInView(t *testing.T) {
+ defer test.MockVariableValue(&Service)()
+
+ cfg, err := NewConfigProviderFromData(`
+[service]
+`)
+ assert.NoError(t, err)
+ loadServiceFrom(cfg)
+ assert.False(t, Service.RequireSignInViewStrict)
+ assert.False(t, Service.BlockAnonymousAccessExpensive)
+
+ cfg, err = NewConfigProviderFromData(`
+[service]
+REQUIRE_SIGNIN_VIEW = true
+`)
+ assert.NoError(t, err)
+ loadServiceFrom(cfg)
+ assert.True(t, Service.RequireSignInViewStrict)
+ assert.False(t, Service.BlockAnonymousAccessExpensive)
+
+ cfg, err = NewConfigProviderFromData(`
+[service]
+REQUIRE_SIGNIN_VIEW = expensive
+`)
+ assert.NoError(t, err)
+ loadServiceFrom(cfg)
+ assert.False(t, Service.RequireSignInViewStrict)
+ assert.True(t, Service.BlockAnonymousAccessExpensive)
+}
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 20da796b58..e14997801f 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -12,8 +12,8 @@ import (
"time"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/user"
- "code.gitea.io/gitea/modules/util"
)
// settings
@@ -159,7 +159,7 @@ func loadRunModeFrom(rootCfg ConfigProvider) {
// The following is a purposefully undocumented option. Please do not run Gitea as root. It will only cause future headaches.
// Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly.
unsafeAllowRunAsRoot := ConfigSectionKeyBool(rootSec, "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")
- unsafeAllowRunAsRoot = unsafeAllowRunAsRoot || util.OptionalBoolParse(os.Getenv("GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")).Value()
+ unsafeAllowRunAsRoot = unsafeAllowRunAsRoot || optional.ParseBool(os.Getenv("GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")).Value()
RunMode = os.Getenv("GITEA_RUN_MODE")
if RunMode == "" {
RunMode = rootSec.Key("RUN_MODE").MustString("prod")
diff --git a/modules/setting/ssh.go b/modules/setting/ssh.go
index ea387e521f..900fc6ade2 100644
--- a/modules/setting/ssh.go
+++ b/modules/setting/ssh.go
@@ -4,8 +4,6 @@
package setting
import (
- "os"
- "path"
"path/filepath"
"strings"
"text/template"
@@ -32,8 +30,6 @@ var SSH = struct {
ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"`
ServerMACs []string `ini:"SSH_SERVER_MACS"`
ServerHostKeys []string `ini:"SSH_SERVER_HOST_KEYS"`
- KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
- KeygenPath string `ini:"SSH_KEYGEN_PATH"`
AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
AuthorizedPrincipalsBackup bool `ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP"`
AuthorizedKeysCommandTemplate string `ini:"SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE"`
@@ -55,10 +51,6 @@ var SSH = struct {
StartBuiltinServer: false,
Domain: "",
Port: 22,
- ServerCiphers: []string{"chacha20-poly1305@openssh.com", "aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "aes256-gcm@openssh.com"},
- ServerKeyExchanges: []string{"curve25519-sha256", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1"},
- ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"},
- KeygenPath: "",
MinimumKeySizeCheck: true,
MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 3071},
ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"},
@@ -111,30 +103,27 @@ func loadSSHFrom(rootCfg ConfigProvider) {
}
homeDir = strings.ReplaceAll(homeDir, "\\", "/")
- SSH.RootPath = path.Join(homeDir, ".ssh")
- serverCiphers := sec.Key("SSH_SERVER_CIPHERS").Strings(",")
- if len(serverCiphers) > 0 {
- SSH.ServerCiphers = serverCiphers
- }
- serverKeyExchanges := sec.Key("SSH_SERVER_KEY_EXCHANGES").Strings(",")
- if len(serverKeyExchanges) > 0 {
- SSH.ServerKeyExchanges = serverKeyExchanges
- }
- serverMACs := sec.Key("SSH_SERVER_MACS").Strings(",")
- if len(serverMACs) > 0 {
- SSH.ServerMACs = serverMACs
- }
- SSH.KeyTestPath = os.TempDir()
+ SSH.RootPath = filepath.Join(homeDir, ".ssh")
+
if err = sec.MapTo(&SSH); err != nil {
log.Fatal("Failed to map SSH settings: %v", err)
}
+
+ serverCiphers := sec.Key("SSH_SERVER_CIPHERS").Strings(",")
+ SSH.ServerCiphers = util.Iif(len(serverCiphers) > 0, serverCiphers, nil)
+
+ serverKeyExchanges := sec.Key("SSH_SERVER_KEY_EXCHANGES").Strings(",")
+ SSH.ServerKeyExchanges = util.Iif(len(serverKeyExchanges) > 0, serverKeyExchanges, nil)
+
+ serverMACs := sec.Key("SSH_SERVER_MACS").Strings(",")
+ SSH.ServerMACs = util.Iif(len(serverMACs) > 0, serverMACs, nil)
+
for i, key := range SSH.ServerHostKeys {
if !filepath.IsAbs(key) {
SSH.ServerHostKeys[i] = filepath.Join(AppDataPath, key)
}
}
- SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").String()
SSH.Port = sec.Key("SSH_PORT").MustInt(22)
SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port)
SSH.UseProxyProtocol = sec.Key("SSH_SERVER_USE_PROXY_PROTOCOL").MustBool(false)
diff --git a/modules/setting/storage.go b/modules/setting/storage.go
index d3d1fb9f30..ee246158d9 100644
--- a/modules/setting/storage.go
+++ b/modules/setting/storage.go
@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"path/filepath"
+ "slices"
"strings"
)
@@ -30,12 +31,7 @@ var storageTypes = []StorageType{
// IsValidStorageType returns true if the given storage type is valid
func IsValidStorageType(storageType StorageType) bool {
- for _, t := range storageTypes {
- if t == storageType {
- return true
- }
- }
- return false
+ return slices.Contains(storageTypes, storageType)
}
// MinioStorageConfig represents the configuration for a minio storage
@@ -162,7 +158,7 @@ const (
targetSecIsSec // target section is from the name seciont [name]
)
-func getStorageSectionByType(rootCfg ConfigProvider, typ string) (ConfigSection, targetSecType, error) { //nolint:unparam
+func getStorageSectionByType(rootCfg ConfigProvider, typ string) (ConfigSection, targetSecType, error) { //nolint:unparam // FIXME: targetSecType is always 0, wrong design?
targetSec, err := rootCfg.GetSection(storageSectionName + "." + typ)
if err != nil {
if !IsValidStorageType(StorageType(typ)) {
@@ -210,8 +206,8 @@ func getStorageTargetSection(rootCfg ConfigProvider, name, typ string, sec Confi
targetSec, _ := rootCfg.GetSection(storageSectionName + "." + name)
if targetSec != nil {
targetType := targetSec.Key("STORAGE_TYPE").String()
- switch {
- case targetType == "":
+ switch targetType {
+ case "":
if targetSec.Key("PATH").String() == "" { // both storage type and path are empty, use default
return getDefaultStorageSection(rootCfg), targetSecIsDefault, nil
}
@@ -287,7 +283,7 @@ func getStorageForLocal(targetSec, overrideSec ConfigSection, tp targetSecType,
return &storage, nil
}
-func getStorageForMinio(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) { //nolint:dupl
+func getStorageForMinio(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) { //nolint:dupl // duplicates azure setup
var storage Storage
storage.Type = StorageType(targetSec.Key("STORAGE_TYPE").String())
if err := targetSec.MapTo(&storage.MinioConfig); err != nil {
@@ -316,7 +312,7 @@ func getStorageForMinio(targetSec, overrideSec ConfigSection, tp targetSecType,
return &storage, nil
}
-func getStorageForAzureBlob(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) { //nolint:dupl
+func getStorageForAzureBlob(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) { //nolint:dupl // duplicates minio setup
var storage Storage
storage.Type = StorageType(targetSec.Key("STORAGE_TYPE").String())
if err := targetSec.MapTo(&storage.AzureBlobConfig); err != nil {
diff --git a/modules/setting/storage_test.go b/modules/setting/storage_test.go
index afff85537e..6f5a54c41c 100644
--- a/modules/setting/storage_test.go
+++ b/modules/setting/storage_test.go
@@ -26,16 +26,16 @@ MINIO_BUCKET = gitea-storage
assert.NoError(t, err)
assert.NoError(t, loadAttachmentFrom(cfg))
- assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket)
- assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket)
+ assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
assert.NoError(t, loadLFSFrom(cfg))
- assert.EqualValues(t, "gitea-lfs", LFS.Storage.MinioConfig.Bucket)
- assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea-lfs", LFS.Storage.MinioConfig.Bucket)
+ assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
assert.NoError(t, loadAvatarsFrom(cfg))
- assert.EqualValues(t, "gitea-storage", Avatar.Storage.MinioConfig.Bucket)
- assert.EqualValues(t, "avatars/", Avatar.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea-storage", Avatar.Storage.MinioConfig.Bucket)
+ assert.Equal(t, "avatars/", Avatar.Storage.MinioConfig.BasePath)
}
func Test_getStorageUseOtherNameAsType(t *testing.T) {
@@ -51,12 +51,12 @@ MINIO_BUCKET = gitea-storage
assert.NoError(t, err)
assert.NoError(t, loadAttachmentFrom(cfg))
- assert.EqualValues(t, "gitea-storage", Attachment.Storage.MinioConfig.Bucket)
- assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea-storage", Attachment.Storage.MinioConfig.Bucket)
+ assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
assert.NoError(t, loadLFSFrom(cfg))
- assert.EqualValues(t, "gitea-storage", LFS.Storage.MinioConfig.Bucket)
- assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea-storage", LFS.Storage.MinioConfig.Bucket)
+ assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
}
func Test_getStorageInheritStorageType(t *testing.T) {
@@ -69,32 +69,32 @@ STORAGE_TYPE = minio
assert.NoError(t, loadPackagesFrom(cfg))
assert.EqualValues(t, "minio", Packages.Storage.Type)
- assert.EqualValues(t, "gitea", Packages.Storage.MinioConfig.Bucket)
- assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea", Packages.Storage.MinioConfig.Bucket)
+ assert.Equal(t, "packages/", Packages.Storage.MinioConfig.BasePath)
assert.NoError(t, loadRepoArchiveFrom(cfg))
assert.EqualValues(t, "minio", RepoArchive.Storage.Type)
- assert.EqualValues(t, "gitea", RepoArchive.Storage.MinioConfig.Bucket)
- assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea", RepoArchive.Storage.MinioConfig.Bucket)
+ assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
assert.NoError(t, loadActionsFrom(cfg))
assert.EqualValues(t, "minio", Actions.LogStorage.Type)
- assert.EqualValues(t, "gitea", Actions.LogStorage.MinioConfig.Bucket)
- assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea", Actions.LogStorage.MinioConfig.Bucket)
+ assert.Equal(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath)
assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type)
- assert.EqualValues(t, "gitea", Actions.ArtifactStorage.MinioConfig.Bucket)
- assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea", Actions.ArtifactStorage.MinioConfig.Bucket)
+ assert.Equal(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath)
assert.NoError(t, loadAvatarsFrom(cfg))
assert.EqualValues(t, "minio", Avatar.Storage.Type)
- assert.EqualValues(t, "gitea", Avatar.Storage.MinioConfig.Bucket)
- assert.EqualValues(t, "avatars/", Avatar.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea", Avatar.Storage.MinioConfig.Bucket)
+ assert.Equal(t, "avatars/", Avatar.Storage.MinioConfig.BasePath)
assert.NoError(t, loadRepoAvatarFrom(cfg))
assert.EqualValues(t, "minio", RepoAvatar.Storage.Type)
- assert.EqualValues(t, "gitea", RepoAvatar.Storage.MinioConfig.Bucket)
- assert.EqualValues(t, "repo-avatars/", RepoAvatar.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "gitea", RepoAvatar.Storage.MinioConfig.Bucket)
+ assert.Equal(t, "repo-avatars/", RepoAvatar.Storage.MinioConfig.BasePath)
}
func Test_getStorageInheritStorageTypeAzureBlob(t *testing.T) {
@@ -107,32 +107,32 @@ STORAGE_TYPE = azureblob
assert.NoError(t, loadPackagesFrom(cfg))
assert.EqualValues(t, "azureblob", Packages.Storage.Type)
- assert.EqualValues(t, "gitea", Packages.Storage.AzureBlobConfig.Container)
- assert.EqualValues(t, "packages/", Packages.Storage.AzureBlobConfig.BasePath)
+ assert.Equal(t, "gitea", Packages.Storage.AzureBlobConfig.Container)
+ assert.Equal(t, "packages/", Packages.Storage.AzureBlobConfig.BasePath)
assert.NoError(t, loadRepoArchiveFrom(cfg))
assert.EqualValues(t, "azureblob", RepoArchive.Storage.Type)
- assert.EqualValues(t, "gitea", RepoArchive.Storage.AzureBlobConfig.Container)
- assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.AzureBlobConfig.BasePath)
+ assert.Equal(t, "gitea", RepoArchive.Storage.AzureBlobConfig.Container)
+ assert.Equal(t, "repo-archive/", RepoArchive.Storage.AzureBlobConfig.BasePath)
assert.NoError(t, loadActionsFrom(cfg))
assert.EqualValues(t, "azureblob", Actions.LogStorage.Type)
- assert.EqualValues(t, "gitea", Actions.LogStorage.AzureBlobConfig.Container)
- assert.EqualValues(t, "actions_log/", Actions.LogStorage.AzureBlobConfig.BasePath)
+ assert.Equal(t, "gitea", Actions.LogStorage.AzureBlobConfig.Container)
+ assert.Equal(t, "actions_log/", Actions.LogStorage.AzureBlobConfig.BasePath)
assert.EqualValues(t, "azureblob", Actions.ArtifactStorage.Type)
- assert.EqualValues(t, "gitea", Actions.ArtifactStorage.AzureBlobConfig.Container)
- assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.AzureBlobConfig.BasePath)
+ assert.Equal(t, "gitea", Actions.ArtifactStorage.AzureBlobConfig.Container)
+ assert.Equal(t, "actions_artifacts/", Actions.ArtifactStorage.AzureBlobConfig.BasePath)
assert.NoError(t, loadAvatarsFrom(cfg))
assert.EqualValues(t, "azureblob", Avatar.Storage.Type)
- assert.EqualValues(t, "gitea", Avatar.Storage.AzureBlobConfig.Container)
- assert.EqualValues(t, "avatars/", Avatar.Storage.AzureBlobConfig.BasePath)
+ assert.Equal(t, "gitea", Avatar.Storage.AzureBlobConfig.Container)
+ assert.Equal(t, "avatars/", Avatar.Storage.AzureBlobConfig.BasePath)
assert.NoError(t, loadRepoAvatarFrom(cfg))
assert.EqualValues(t, "azureblob", RepoAvatar.Storage.Type)
- assert.EqualValues(t, "gitea", RepoAvatar.Storage.AzureBlobConfig.Container)
- assert.EqualValues(t, "repo-avatars/", RepoAvatar.Storage.AzureBlobConfig.BasePath)
+ assert.Equal(t, "gitea", RepoAvatar.Storage.AzureBlobConfig.Container)
+ assert.Equal(t, "repo-avatars/", RepoAvatar.Storage.AzureBlobConfig.BasePath)
}
type testLocalStoragePathCase struct {
@@ -151,7 +151,7 @@ func testLocalStoragePath(t *testing.T, appDataPath, iniStr string, cases []test
assert.EqualValues(t, "local", storage.Type)
assert.True(t, filepath.IsAbs(storage.Path))
- assert.EqualValues(t, filepath.Clean(c.expectedPath), filepath.Clean(storage.Path))
+ assert.Equal(t, filepath.Clean(c.expectedPath), filepath.Clean(storage.Path))
}
}
@@ -389,8 +389,8 @@ MINIO_SECRET_ACCESS_KEY = my_secret_key
assert.NoError(t, loadRepoArchiveFrom(cfg))
cp := RepoArchive.Storage.ToShadowCopy()
- assert.EqualValues(t, "******", cp.MinioConfig.AccessKeyID)
- assert.EqualValues(t, "******", cp.MinioConfig.SecretAccessKey)
+ assert.Equal(t, "******", cp.MinioConfig.AccessKeyID)
+ assert.Equal(t, "******", cp.MinioConfig.SecretAccessKey)
}
func Test_getStorageConfiguration24(t *testing.T) {
@@ -445,10 +445,10 @@ MINIO_USE_SSL = true
`)
assert.NoError(t, err)
assert.NoError(t, loadRepoArchiveFrom(cfg))
- assert.EqualValues(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID)
- assert.EqualValues(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey)
+ assert.Equal(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID)
+ assert.Equal(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey)
assert.True(t, RepoArchive.Storage.MinioConfig.UseSSL)
- assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
}
func Test_getStorageConfiguration28(t *testing.T) {
@@ -462,10 +462,10 @@ MINIO_BASE_PATH = /prefix
`)
assert.NoError(t, err)
assert.NoError(t, loadRepoArchiveFrom(cfg))
- assert.EqualValues(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID)
- assert.EqualValues(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey)
+ assert.Equal(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID)
+ assert.Equal(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey)
assert.True(t, RepoArchive.Storage.MinioConfig.UseSSL)
- assert.EqualValues(t, "/prefix/repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "/prefix/repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
cfg, err = NewConfigProviderFromData(`
[storage]
@@ -476,9 +476,9 @@ MINIO_BASE_PATH = /prefix
`)
assert.NoError(t, err)
assert.NoError(t, loadRepoArchiveFrom(cfg))
- assert.EqualValues(t, "127.0.0.1", RepoArchive.Storage.MinioConfig.IamEndpoint)
+ assert.Equal(t, "127.0.0.1", RepoArchive.Storage.MinioConfig.IamEndpoint)
assert.True(t, RepoArchive.Storage.MinioConfig.UseSSL)
- assert.EqualValues(t, "/prefix/repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "/prefix/repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
cfg, err = NewConfigProviderFromData(`
[storage]
@@ -493,10 +493,10 @@ MINIO_BASE_PATH = /lfs
`)
assert.NoError(t, err)
assert.NoError(t, loadLFSFrom(cfg))
- assert.EqualValues(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID)
- assert.EqualValues(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey)
+ assert.Equal(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID)
+ assert.Equal(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey)
assert.True(t, LFS.Storage.MinioConfig.UseSSL)
- assert.EqualValues(t, "/lfs", LFS.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "/lfs", LFS.Storage.MinioConfig.BasePath)
cfg, err = NewConfigProviderFromData(`
[storage]
@@ -511,10 +511,10 @@ MINIO_BASE_PATH = /lfs
`)
assert.NoError(t, err)
assert.NoError(t, loadLFSFrom(cfg))
- assert.EqualValues(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID)
- assert.EqualValues(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey)
+ assert.Equal(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID)
+ assert.Equal(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey)
assert.True(t, LFS.Storage.MinioConfig.UseSSL)
- assert.EqualValues(t, "/lfs", LFS.Storage.MinioConfig.BasePath)
+ assert.Equal(t, "/lfs", LFS.Storage.MinioConfig.BasePath)
}
func Test_getStorageConfiguration29(t *testing.T) {
@@ -539,9 +539,9 @@ AZURE_BLOB_ACCOUNT_KEY = my_account_key
`)
assert.NoError(t, err)
assert.NoError(t, loadRepoArchiveFrom(cfg))
- assert.EqualValues(t, "my_account_name", RepoArchive.Storage.AzureBlobConfig.AccountName)
- assert.EqualValues(t, "my_account_key", RepoArchive.Storage.AzureBlobConfig.AccountKey)
- assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.AzureBlobConfig.BasePath)
+ assert.Equal(t, "my_account_name", RepoArchive.Storage.AzureBlobConfig.AccountName)
+ assert.Equal(t, "my_account_key", RepoArchive.Storage.AzureBlobConfig.AccountKey)
+ assert.Equal(t, "repo-archive/", RepoArchive.Storage.AzureBlobConfig.BasePath)
}
func Test_getStorageConfiguration31(t *testing.T) {
@@ -554,9 +554,9 @@ AZURE_BLOB_BASE_PATH = /prefix
`)
assert.NoError(t, err)
assert.NoError(t, loadRepoArchiveFrom(cfg))
- assert.EqualValues(t, "my_account_name", RepoArchive.Storage.AzureBlobConfig.AccountName)
- assert.EqualValues(t, "my_account_key", RepoArchive.Storage.AzureBlobConfig.AccountKey)
- assert.EqualValues(t, "/prefix/repo-archive/", RepoArchive.Storage.AzureBlobConfig.BasePath)
+ assert.Equal(t, "my_account_name", RepoArchive.Storage.AzureBlobConfig.AccountName)
+ assert.Equal(t, "my_account_key", RepoArchive.Storage.AzureBlobConfig.AccountKey)
+ assert.Equal(t, "/prefix/repo-archive/", RepoArchive.Storage.AzureBlobConfig.BasePath)
cfg, err = NewConfigProviderFromData(`
[storage]
@@ -570,9 +570,9 @@ AZURE_BLOB_BASE_PATH = /lfs
`)
assert.NoError(t, err)
assert.NoError(t, loadLFSFrom(cfg))
- assert.EqualValues(t, "my_account_name", LFS.Storage.AzureBlobConfig.AccountName)
- assert.EqualValues(t, "my_account_key", LFS.Storage.AzureBlobConfig.AccountKey)
- assert.EqualValues(t, "/lfs", LFS.Storage.AzureBlobConfig.BasePath)
+ assert.Equal(t, "my_account_name", LFS.Storage.AzureBlobConfig.AccountName)
+ assert.Equal(t, "my_account_key", LFS.Storage.AzureBlobConfig.AccountKey)
+ assert.Equal(t, "/lfs", LFS.Storage.AzureBlobConfig.BasePath)
cfg, err = NewConfigProviderFromData(`
[storage]
@@ -586,7 +586,7 @@ AZURE_BLOB_BASE_PATH = /lfs
`)
assert.NoError(t, err)
assert.NoError(t, loadLFSFrom(cfg))
- assert.EqualValues(t, "my_account_name", LFS.Storage.AzureBlobConfig.AccountName)
- assert.EqualValues(t, "my_account_key", LFS.Storage.AzureBlobConfig.AccountKey)
- assert.EqualValues(t, "/lfs", LFS.Storage.AzureBlobConfig.BasePath)
+ assert.Equal(t, "my_account_name", LFS.Storage.AzureBlobConfig.AccountName)
+ assert.Equal(t, "my_account_key", LFS.Storage.AzureBlobConfig.AccountKey)
+ assert.Equal(t, "/lfs", LFS.Storage.AzureBlobConfig.BasePath)
}
diff --git a/modules/setting/ui.go b/modules/setting/ui.go
index 20fc612b43..3d9c916bf7 100644
--- a/modules/setting/ui.go
+++ b/modules/setting/ui.go
@@ -28,6 +28,7 @@ var UI = struct {
DefaultShowFullName bool
DefaultTheme string
Themes []string
+ FileIconTheme string
Reactions []string
ReactionsLookup container.Set[string] `ini:"-"`
CustomEmojis []string
@@ -84,6 +85,7 @@ var UI = struct {
ReactionMaxUserNum: 10,
MaxDisplayFileSize: 8388608,
DefaultTheme: `gitea-auto`,
+ FileIconTheme: `material`,
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`},
CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"},
diff --git a/modules/ssh/init.go b/modules/ssh/init.go
index 21d4f89936..cfb0d5693a 100644
--- a/modules/ssh/init.go
+++ b/modules/ssh/init.go
@@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
)
func Init() error {
@@ -23,20 +24,17 @@ func Init() error {
if setting.SSH.StartBuiltinServer {
Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
- log.Info("SSH server started on %s. Cipher list (%v), key exchange algorithms (%v), MACs (%v)",
+ log.Info("SSH server started on %q. Ciphers: %v, key exchange algorithms: %v, MACs: %v",
net.JoinHostPort(setting.SSH.ListenHost, strconv.Itoa(setting.SSH.ListenPort)),
- setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs,
+ util.Iif[any](setting.SSH.ServerCiphers == nil, "default", setting.SSH.ServerCiphers),
+ util.Iif[any](setting.SSH.ServerKeyExchanges == nil, "default", setting.SSH.ServerKeyExchanges),
+ util.Iif[any](setting.SSH.ServerMACs == nil, "default", setting.SSH.ServerMACs),
)
return nil
}
builtinUnused()
- // FIXME: why 0o644 for a directory .....
- if err := os.MkdirAll(setting.SSH.KeyTestPath, 0o644); err != nil {
- return fmt.Errorf("failed to create directory %q for ssh key test: %w", setting.SSH.KeyTestPath, err)
- }
-
if len(setting.SSH.TrustedUserCAKeys) > 0 && setting.SSH.AuthorizedPrincipalsEnabled {
caKeysFileName := setting.SSH.TrustedUserCAKeysFile
caKeysFileDir := filepath.Dir(caKeysFileName)
diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go
index 7479cfbd95..3fea4851c7 100644
--- a/modules/ssh/ssh.go
+++ b/modules/ssh/ssh.go
@@ -11,7 +11,6 @@ import (
"crypto/x509"
"encoding/pem"
"errors"
- "fmt"
"io"
"net"
"os"
@@ -216,7 +215,7 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
ctx.Permissions().Permissions = &gossh.Permissions{}
setPermExt := func(keyID int64) {
ctx.Permissions().Permissions.Extensions = map[string]string{
- giteaPermissionExtensionKeyID: fmt.Sprint(keyID),
+ giteaPermissionExtensionKeyID: strconv.FormatInt(keyID, 10),
}
}
@@ -334,7 +333,7 @@ func sshConnectionFailed(conn net.Conn, err error) {
log.Warn("Failed authentication attempt from %s", conn.RemoteAddr())
}
-// Listen starts a SSH server listens on given port.
+// Listen starts an SSH server listening on given port.
func Listen(host string, port int, ciphers, keyExchanges, macs []string) {
srv := ssh.Server{
Addr: net.JoinHostPort(host, strconv.Itoa(port)),
diff --git a/modules/storage/azureblob_test.go b/modules/storage/azureblob_test.go
index 6905db5008..b3791b4916 100644
--- a/modules/storage/azureblob_test.go
+++ b/modules/storage/azureblob_test.go
@@ -4,9 +4,9 @@
package storage
import (
- "bytes"
"io"
"os"
+ "strings"
"testing"
"code.gitea.io/gitea/modules/setting"
@@ -33,14 +33,14 @@ func TestAzureBlobStorageIterator(t *testing.T) {
func TestAzureBlobStoragePath(t *testing.T) {
m := &AzureBlobStorage{cfg: &setting.AzureBlobStorageConfig{BasePath: ""}}
- assert.Equal(t, "", m.buildAzureBlobPath("/"))
- assert.Equal(t, "", m.buildAzureBlobPath("."))
+ assert.Empty(t, m.buildAzureBlobPath("/"))
+ assert.Empty(t, m.buildAzureBlobPath("."))
assert.Equal(t, "a", m.buildAzureBlobPath("/a"))
assert.Equal(t, "a/b", m.buildAzureBlobPath("/a/b/"))
m = &AzureBlobStorage{cfg: &setting.AzureBlobStorageConfig{BasePath: "/"}}
- assert.Equal(t, "", m.buildAzureBlobPath("/"))
- assert.Equal(t, "", m.buildAzureBlobPath("."))
+ assert.Empty(t, m.buildAzureBlobPath("/"))
+ assert.Empty(t, m.buildAzureBlobPath("."))
assert.Equal(t, "a", m.buildAzureBlobPath("/a"))
assert.Equal(t, "a/b", m.buildAzureBlobPath("/a/b/"))
@@ -76,7 +76,7 @@ func Test_azureBlobObject(t *testing.T) {
assert.NoError(t, err)
data := "Q2xTckt6Y1hDOWh0"
- _, err = s.Save("test.txt", bytes.NewBufferString(data), int64(len(data)))
+ _, err = s.Save("test.txt", strings.NewReader(data), int64(len(data)))
assert.NoError(t, err)
obj, err := s.Open("test.txt")
assert.NoError(t, err)
@@ -86,7 +86,7 @@ func Test_azureBlobObject(t *testing.T) {
buf1 := make([]byte, 3)
read, err := obj.Read(buf1)
assert.NoError(t, err)
- assert.EqualValues(t, 3, read)
+ assert.Equal(t, 3, read)
assert.Equal(t, data[2:5], string(buf1))
offset, err = obj.Seek(-5, io.SeekEnd)
assert.NoError(t, err)
@@ -94,7 +94,7 @@ func Test_azureBlobObject(t *testing.T) {
buf2 := make([]byte, 4)
read, err = obj.Read(buf2)
assert.NoError(t, err)
- assert.EqualValues(t, 4, read)
+ assert.Equal(t, 4, read)
assert.Equal(t, data[11:15], string(buf2))
assert.NoError(t, obj.Close())
assert.NoError(t, s.Delete("test.txt"))
diff --git a/modules/storage/local_test.go b/modules/storage/local_test.go
index e230323f67..0592fd716b 100644
--- a/modules/storage/local_test.go
+++ b/modules/storage/local_test.go
@@ -4,8 +4,6 @@
package storage
import (
- "os"
- "path/filepath"
"testing"
"code.gitea.io/gitea/modules/setting"
@@ -50,12 +48,11 @@ func TestBuildLocalPath(t *testing.T) {
t.Run(k.path, func(t *testing.T) {
l := LocalStorage{dir: k.localDir}
- assert.EqualValues(t, k.expected, l.buildLocalPath(k.path))
+ assert.Equal(t, k.expected, l.buildLocalPath(k.path))
})
}
}
func TestLocalStorageIterator(t *testing.T) {
- dir := filepath.Join(os.TempDir(), "TestLocalStorageIteratorTestDir")
- testStorageIterator(t, setting.LocalStorageType, &setting.Storage{Path: dir})
+ testStorageIterator(t, setting.LocalStorageType, &setting.Storage{Path: t.TempDir()})
}
diff --git a/modules/storage/minio.go b/modules/storage/minio.go
index 6b92be61fb..1c5d25b2d4 100644
--- a/modules/storage/minio.go
+++ b/modules/storage/minio.go
@@ -86,13 +86,14 @@ func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage,
log.Info("Creating Minio storage at %s:%s with base path %s", config.Endpoint, config.Bucket, config.BasePath)
var lookup minio.BucketLookupType
- if config.BucketLookUpType == "auto" || config.BucketLookUpType == "" {
+ switch config.BucketLookUpType {
+ case "auto", "":
lookup = minio.BucketLookupAuto
- } else if config.BucketLookUpType == "dns" {
+ case "dns":
lookup = minio.BucketLookupDNS
- } else if config.BucketLookUpType == "path" {
+ case "path":
lookup = minio.BucketLookupPath
- } else {
+ default:
return nil, fmt.Errorf("invalid minio bucket lookup type: %s", config.BucketLookUpType)
}
diff --git a/modules/storage/minio_test.go b/modules/storage/minio_test.go
index 395da051e8..2726d765dd 100644
--- a/modules/storage/minio_test.go
+++ b/modules/storage/minio_test.go
@@ -34,19 +34,19 @@ func TestMinioStorageIterator(t *testing.T) {
func TestMinioStoragePath(t *testing.T) {
m := &MinioStorage{basePath: ""}
- assert.Equal(t, "", m.buildMinioPath("/"))
- assert.Equal(t, "", m.buildMinioPath("."))
+ assert.Empty(t, m.buildMinioPath("/"))
+ assert.Empty(t, m.buildMinioPath("."))
assert.Equal(t, "a", m.buildMinioPath("/a"))
assert.Equal(t, "a/b", m.buildMinioPath("/a/b/"))
- assert.Equal(t, "", m.buildMinioDirPrefix(""))
+ assert.Empty(t, m.buildMinioDirPrefix(""))
assert.Equal(t, "a/", m.buildMinioDirPrefix("/a/"))
m = &MinioStorage{basePath: "/"}
- assert.Equal(t, "", m.buildMinioPath("/"))
- assert.Equal(t, "", m.buildMinioPath("."))
+ assert.Empty(t, m.buildMinioPath("/"))
+ assert.Empty(t, m.buildMinioPath("."))
assert.Equal(t, "a", m.buildMinioPath("/a"))
assert.Equal(t, "a/b", m.buildMinioPath("/a/b/"))
- assert.Equal(t, "", m.buildMinioDirPrefix(""))
+ assert.Empty(t, m.buildMinioDirPrefix(""))
assert.Equal(t, "a/", m.buildMinioDirPrefix("/a/"))
m = &MinioStorage{basePath: "/base"}
diff --git a/modules/storage/storage.go b/modules/storage/storage.go
index 750ecdfe0d..b0529941e7 100644
--- a/modules/storage/storage.go
+++ b/modules/storage/storage.go
@@ -93,7 +93,7 @@ func Clean(storage ObjectStorage) error {
}
// SaveFrom saves data to the ObjectStorage with path p from the callback
-func SaveFrom(objStorage ObjectStorage, p string, callback func(w io.Writer) error) error {
+func SaveFrom(objStorage ObjectStorage, path string, callback func(w io.Writer) error) error {
pr, pw := io.Pipe()
defer pr.Close()
go func() {
@@ -103,7 +103,7 @@ func SaveFrom(objStorage ObjectStorage, p string, callback func(w io.Writer) err
}
}()
- _, err := objStorage.Save(p, pr, -1)
+ _, err := objStorage.Save(path, pr, -1)
return err
}
diff --git a/modules/storage/storage_test.go b/modules/storage/storage_test.go
index 7edde558f3..08f274e74b 100644
--- a/modules/storage/storage_test.go
+++ b/modules/storage/storage_test.go
@@ -4,7 +4,7 @@
package storage
import (
- "bytes"
+ "strings"
"testing"
"code.gitea.io/gitea/modules/setting"
@@ -26,7 +26,7 @@ func testStorageIterator(t *testing.T, typStr Type, cfg *setting.Storage) {
{"b/x 4.txt", "bx4"},
}
for _, f := range testFiles {
- _, err = l.Save(f[0], bytes.NewBufferString(f[1]), -1)
+ _, err = l.Save(f[0], strings.NewReader(f[1]), -1)
assert.NoError(t, err)
}
diff --git a/modules/structs/admin_user.go b/modules/structs/admin_user.go
index f7c6d10ba0..c68b59a897 100644
--- a/modules/structs/admin_user.go
+++ b/modules/structs/admin_user.go
@@ -8,8 +8,11 @@ import "time"
// CreateUserOption create user options
type CreateUserOption struct {
- SourceID int64 `json:"source_id"`
+ SourceID int64 `json:"source_id"`
+ // identifier of the user, provided by the external authenticator (if configured)
+ // default: empty
LoginName string `json:"login_name"`
+ // username of the user
// required: true
Username string `json:"username" binding:"Required;Username;MaxSize(40)"`
FullName string `json:"full_name" binding:"MaxSize(100)"`
@@ -32,6 +35,8 @@ type CreateUserOption struct {
type EditUserOption struct {
// required: true
SourceID int64 `json:"source_id"`
+ // identifier of the user, provided by the external authenticator (if configured)
+ // default: empty
// required: true
LoginName string `json:"login_name" binding:"Required"`
// swagger:strfmt email
diff --git a/modules/structs/commit_status_test.go b/modules/structs/commit_status_test.go
deleted file mode 100644
index f06808534c..0000000000
--- a/modules/structs/commit_status_test.go
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package structs
-
-import (
- "testing"
-)
-
-func TestNoBetterThan(t *testing.T) {
- type args struct {
- css CommitStatusState
- css2 CommitStatusState
- }
- var unExpectedState CommitStatusState
- tests := []struct {
- name string
- args args
- want bool
- }{
- {
- name: "success is no better than success",
- args: args{
- css: CommitStatusSuccess,
- css2: CommitStatusSuccess,
- },
- want: true,
- },
- {
- name: "success is no better than pending",
- args: args{
- css: CommitStatusSuccess,
- css2: CommitStatusPending,
- },
- want: false,
- },
- {
- name: "success is no better than failure",
- args: args{
- css: CommitStatusSuccess,
- css2: CommitStatusFailure,
- },
- want: false,
- },
- {
- name: "success is no better than error",
- args: args{
- css: CommitStatusSuccess,
- css2: CommitStatusError,
- },
- want: false,
- },
- {
- name: "pending is no better than success",
- args: args{
- css: CommitStatusPending,
- css2: CommitStatusSuccess,
- },
- want: true,
- },
- {
- name: "pending is no better than pending",
- args: args{
- css: CommitStatusPending,
- css2: CommitStatusPending,
- },
- want: true,
- },
- {
- name: "pending is no better than failure",
- args: args{
- css: CommitStatusPending,
- css2: CommitStatusFailure,
- },
- want: false,
- },
- {
- name: "pending is no better than error",
- args: args{
- css: CommitStatusPending,
- css2: CommitStatusError,
- },
- want: false,
- },
- {
- name: "failure is no better than success",
- args: args{
- css: CommitStatusFailure,
- css2: CommitStatusSuccess,
- },
- want: true,
- },
- {
- name: "failure is no better than pending",
- args: args{
- css: CommitStatusFailure,
- css2: CommitStatusPending,
- },
- want: true,
- },
- {
- name: "failure is no better than failure",
- args: args{
- css: CommitStatusFailure,
- css2: CommitStatusFailure,
- },
- want: true,
- },
- {
- name: "failure is no better than error",
- args: args{
- css: CommitStatusFailure,
- css2: CommitStatusError,
- },
- want: false,
- },
- {
- name: "error is no better than success",
- args: args{
- css: CommitStatusError,
- css2: CommitStatusSuccess,
- },
- want: true,
- },
- {
- name: "error is no better than pending",
- args: args{
- css: CommitStatusError,
- css2: CommitStatusPending,
- },
- want: true,
- },
- {
- name: "error is no better than failure",
- args: args{
- css: CommitStatusError,
- css2: CommitStatusFailure,
- },
- want: true,
- },
- {
- name: "error is no better than error",
- args: args{
- css: CommitStatusError,
- css2: CommitStatusError,
- },
- want: true,
- },
- {
- name: "unExpectedState is no better than success",
- args: args{
- css: unExpectedState,
- css2: CommitStatusSuccess,
- },
- want: false,
- },
- {
- name: "unExpectedState is no better than unExpectedState",
- args: args{
- css: unExpectedState,
- css2: unExpectedState,
- },
- want: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := tt.args.css.NoBetterThan(tt.args.css2)
- if result != tt.want {
- t.Errorf("NoBetterThan() = %v, want %v", result, tt.want)
- }
- })
- }
-}
diff --git a/modules/structs/git_blob.go b/modules/structs/git_blob.go
index 96c7a271a9..643b69ed37 100644
--- a/modules/structs/git_blob.go
+++ b/modules/structs/git_blob.go
@@ -5,9 +5,12 @@ package structs
// GitBlobResponse represents a git blob
type GitBlobResponse struct {
- Content string `json:"content"`
- Encoding string `json:"encoding"`
- URL string `json:"url"`
- SHA string `json:"sha"`
- Size int64 `json:"size"`
+ Content *string `json:"content"`
+ Encoding *string `json:"encoding"`
+ URL string `json:"url"`
+ SHA string `json:"sha"`
+ Size int64 `json:"size"`
+
+ LfsOid *string `json:"lfs_oid,omitempty"`
+ LfsSize *int64 `json:"lfs_size,omitempty"`
}
diff --git a/modules/structs/hook.go b/modules/structs/hook.go
index ce5742e5c7..ac779a5740 100644
--- a/modules/structs/hook.go
+++ b/modules/structs/hook.go
@@ -71,7 +71,8 @@ type PayloadUser struct {
// Full name of the commit author
Name string `json:"name"`
// swagger:strfmt email
- Email string `json:"email"`
+ Email string `json:"email"`
+ // username of the user
UserName string `json:"username"`
}
@@ -116,14 +117,7 @@ var (
_ Payloader = &PackagePayload{}
)
-// _________ __
-// \_ ___ \_______ ____ _____ _/ |_ ____
-// / \ \/\_ __ \_/ __ \\__ \\ __\/ __ \
-// \ \____| | \/\ ___/ / __ \| | \ ___/
-// \______ /|__| \___ >____ /__| \___ >
-// \/ \/ \/ \/
-
-// CreatePayload FIXME
+// CreatePayload represents a payload information of create event.
type CreatePayload struct {
Sha string `json:"sha"`
Ref string `json:"ref"`
@@ -157,13 +151,6 @@ func ParseCreateHook(raw []byte) (*CreatePayload, error) {
return hook, nil
}
-// ________ .__ __
-// \______ \ ____ | | _____/ |_ ____
-// | | \_/ __ \| | _/ __ \ __\/ __ \
-// | ` \ ___/| |_\ ___/| | \ ___/
-// /_______ /\___ >____/\___ >__| \___ >
-// \/ \/ \/ \/
-
// PusherType define the type to push
type PusherType string
@@ -186,13 +173,6 @@ func (p *DeletePayload) JSONPayload() ([]byte, error) {
return json.MarshalIndent(p, "", " ")
}
-// ___________ __
-// \_ _____/__________| | __
-// | __)/ _ \_ __ \ |/ /
-// | \( <_> ) | \/ <
-// \___ / \____/|__| |__|_ \
-// \/ \/
-
// ForkPayload represents fork payload
type ForkPayload struct {
Forkee *Repository `json:"forkee"`
@@ -232,13 +212,6 @@ func (p *IssueCommentPayload) JSONPayload() ([]byte, error) {
return json.MarshalIndent(p, "", " ")
}
-// __________ .__
-// \______ \ ____ | | ____ _____ ______ ____
-// | _// __ \| | _/ __ \\__ \ / ___// __ \
-// | | \ ___/| |_\ ___/ / __ \_\___ \\ ___/
-// |____|_ /\___ >____/\___ >____ /____ >\___ >
-// \/ \/ \/ \/ \/ \/
-
// HookReleaseAction defines hook release action type
type HookReleaseAction string
@@ -302,13 +275,6 @@ func (p *PushPayload) Branch() string {
return strings.ReplaceAll(p.Ref, "refs/heads/", "")
}
-// .___
-// | | ______ ________ __ ____
-// | |/ ___// ___/ | \_/ __ \
-// | |\___ \ \___ \| | /\ ___/
-// |___/____ >____ >____/ \___ >
-// \/ \/ \/
-
// HookIssueAction FIXME
type HookIssueAction string
@@ -321,6 +287,8 @@ const (
HookIssueReOpened HookIssueAction = "reopened"
// HookIssueEdited edited
HookIssueEdited HookIssueAction = "edited"
+ // HookIssueDeleted is an issue action for deleting an issue
+ HookIssueDeleted HookIssueAction = "deleted"
// HookIssueAssigned assigned
HookIssueAssigned HookIssueAction = "assigned"
// HookIssueUnassigned unassigned
@@ -371,13 +339,6 @@ type ChangesPayload struct {
Ref *ChangesFromPayload `json:"ref,omitempty"`
}
-// __________ .__ .__ __________ __
-// \______ \__ __| | | | \______ \ ____ ________ __ ____ _______/ |_
-// | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\
-// | | | | / |_| |__ | | \ ___< <_| | | /\ ___/ \___ \ | |
-// |____| |____/|____/____/ |____|_ /\___ >__ |____/ \___ >____ > |__|
-// \/ \/ |__| \/ \/
-
// PullRequestPayload represents a payload information of pull request event.
type PullRequestPayload struct {
Action HookIssueAction `json:"action"`
@@ -402,13 +363,6 @@ type ReviewPayload struct {
Content string `json:"content"`
}
-// __ __.__ __ .__
-// / \ / \__| | _|__|
-// \ \/\/ / | |/ / |
-// \ /| | <| |
-// \__/\ / |__|__|_ \__|
-// \/ \/
-
// HookWikiAction an action that happens to a wiki page
type HookWikiAction string
@@ -435,13 +389,6 @@ func (p *WikiPayload) JSONPayload() ([]byte, error) {
return json.MarshalIndent(p, "", " ")
}
-//__________ .__ __
-//\______ \ ____ ______ ____ _____|__|/ |_ ___________ ___.__.
-// | _// __ \\____ \ / _ \/ ___/ \ __\/ _ \_ __ < | |
-// | | \ ___/| |_> > <_> )___ \| || | ( <_> ) | \/\___ |
-// |____|_ /\___ > __/ \____/____ >__||__| \____/|__| / ____|
-// \/ \/|__| \/ \/
-
// HookRepoAction an action that happens to a repo
type HookRepoAction string
@@ -480,7 +427,7 @@ type PackagePayload struct {
Action HookPackageAction `json:"action"`
Repository *Repository `json:"repository"`
Package *Package `json:"package"`
- Organization *User `json:"organization"`
+ Organization *Organization `json:"organization"`
Sender *User `json:"sender"`
}
@@ -525,3 +472,34 @@ type CommitStatusPayload struct {
func (p *CommitStatusPayload) JSONPayload() ([]byte, error) {
return json.MarshalIndent(p, "", " ")
}
+
+// WorkflowRunPayload represents a payload information of workflow run event.
+type WorkflowRunPayload struct {
+ Action string `json:"action"`
+ Workflow *ActionWorkflow `json:"workflow"`
+ WorkflowRun *ActionWorkflowRun `json:"workflow_run"`
+ PullRequest *PullRequest `json:"pull_request,omitempty"`
+ Organization *Organization `json:"organization,omitempty"`
+ Repo *Repository `json:"repository"`
+ Sender *User `json:"sender"`
+}
+
+// JSONPayload implements Payload
+func (p *WorkflowRunPayload) JSONPayload() ([]byte, error) {
+ return json.MarshalIndent(p, "", " ")
+}
+
+// WorkflowJobPayload represents a payload information of workflow job event.
+type WorkflowJobPayload struct {
+ Action string `json:"action"`
+ WorkflowJob *ActionWorkflowJob `json:"workflow_job"`
+ PullRequest *PullRequest `json:"pull_request,omitempty"`
+ Organization *Organization `json:"organization,omitempty"`
+ Repo *Repository `json:"repository"`
+ Sender *User `json:"sender"`
+}
+
+// JSONPayload implements Payload
+func (p *WorkflowJobPayload) JSONPayload() ([]byte, error) {
+ return json.MarshalIndent(p, "", " ")
+}
diff --git a/modules/structs/issue.go b/modules/structs/issue.go
index 3682191be5..df0be8f9ec 100644
--- a/modules/structs/issue.go
+++ b/modules/structs/issue.go
@@ -203,7 +203,7 @@ func (l *IssueTemplateStringSlice) UnmarshalYAML(value *yaml.Node) error {
if err != nil {
return err
}
- for _, v := range strings.Split(str, ",") {
+ for v := range strings.SplitSeq(str, ",") {
if v = strings.TrimSpace(v); v == "" {
continue
}
@@ -266,3 +266,8 @@ type IssueMeta struct {
Owner string `json:"owner"`
Name string `json:"repo"`
}
+
+// LockIssueOption options to lock an issue
+type LockIssueOption struct {
+ Reason string `json:"lock_reason"`
+}
diff --git a/modules/structs/issue_tracked_time.go b/modules/structs/issue_tracked_time.go
index a3904af80e..befcfb323d 100644
--- a/modules/structs/issue_tracked_time.go
+++ b/modules/structs/issue_tracked_time.go
@@ -14,7 +14,7 @@ type AddTimeOption struct {
Time int64 `json:"time" binding:"Required"`
// swagger:strfmt date-time
Created time.Time `json:"created"`
- // User who spent the time (optional)
+ // username of the user who spent the time working on the issue (optional)
User string `json:"user_name"`
}
@@ -26,7 +26,8 @@ type TrackedTime struct {
// Time in seconds
Time int64 `json:"time"`
// deprecated (only for backwards compatibility)
- UserID int64 `json:"user_id"`
+ UserID int64 `json:"user_id"`
+ // username of the user
UserName string `json:"user_name"`
// deprecated (only for backwards compatibility)
IssueID int64 `json:"issue_id"`
diff --git a/modules/structs/org.go b/modules/structs/org.go
index c0a545ac1c..33b45c6344 100644
--- a/modules/structs/org.go
+++ b/modules/structs/org.go
@@ -15,6 +15,7 @@ type Organization struct {
Location string `json:"location"`
Visibility string `json:"visibility"`
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
+ // username of the organization
// deprecated
UserName string `json:"username"`
}
@@ -30,6 +31,7 @@ type OrganizationPermissions struct {
// CreateOrgOption options for creating an organization
type CreateOrgOption struct {
+ // username of the organization
// required: true
UserName string `json:"username" binding:"Required;Username;MaxSize(40)"`
FullName string `json:"full_name" binding:"MaxSize(100)"`
@@ -57,3 +59,12 @@ type EditOrgOption struct {
Visibility string `json:"visibility" binding:"In(,public,limited,private)"`
RepoAdminChangeTeamAccess *bool `json:"repo_admin_change_team_access"`
}
+
+// RenameOrgOption options when renaming an organization
+type RenameOrgOption struct {
+ // New username for this org. This name cannot be in use yet by any other user.
+ //
+ // required: true
+ // unique: true
+ NewName string `json:"new_name" binding:"Required"`
+}
diff --git a/modules/structs/package.go b/modules/structs/package.go
index a9a9429de2..1973f925a5 100644
--- a/modules/structs/package.go
+++ b/modules/structs/package.go
@@ -23,8 +23,8 @@ type Package struct {
// PackageFile represents a package file
type PackageFile struct {
- ID int64 `json:"id"`
- Size int64
+ ID int64 `json:"id"`
+ Size int64 `json:"size"`
Name string `json:"name"`
HashMD5 string `json:"md5"`
HashSHA1 string `json:"sha1"`
diff --git a/modules/structs/pull.go b/modules/structs/pull.go
index 55831e642c..f53d6adafc 100644
--- a/modules/structs/pull.go
+++ b/modules/structs/pull.go
@@ -25,11 +25,13 @@ type PullRequest struct {
Draft bool `json:"draft"`
IsLocked bool `json:"is_locked"`
Comments int `json:"comments"`
+
// number of review comments made on the diff of a PR review (not including comments on commits or issues in a PR)
- ReviewComments int `json:"review_comments"`
- Additions int `json:"additions"`
- Deletions int `json:"deletions"`
- ChangedFiles int `json:"changed_files"`
+ ReviewComments int `json:"review_comments,omitempty"`
+
+ Additions *int `json:"additions,omitempty"`
+ Deletions *int `json:"deletions,omitempty"`
+ ChangedFiles *int `json:"changed_files,omitempty"`
HTMLURL string `json:"html_url"`
DiffURL string `json:"diff_url"`
diff --git a/modules/structs/release.go b/modules/structs/release.go
index c7378645c2..fac86ca7a2 100644
--- a/modules/structs/release.go
+++ b/modules/structs/release.go
@@ -33,6 +33,7 @@ type Release struct {
type CreateReleaseOption struct {
// required: true
TagName string `json:"tag_name" binding:"Required"`
+ TagMessage string `json:"tag_message"`
Target string `json:"target_commitish"`
Title string `json:"name"`
Note string `json:"body"`
diff --git a/modules/structs/repo.go b/modules/structs/repo.go
index fb784bd8b3..abc8076387 100644
--- a/modules/structs/repo.go
+++ b/modules/structs/repo.go
@@ -101,6 +101,8 @@ type Repository struct {
AllowSquash bool `json:"allow_squash_merge"`
AllowFastForwardOnly bool `json:"allow_fast_forward_only_merge"`
AllowRebaseUpdate bool `json:"allow_rebase_update"`
+ AllowManualMerge bool `json:"allow_manual_merge"`
+ AutodetectManualMerge bool `json:"autodetect_manual_merge"`
DefaultDeleteBranchAfterMerge bool `json:"default_delete_branch_after_merge"`
DefaultMergeStyle string `json:"default_merge_style"`
DefaultAllowMaintainerEdit bool `json:"default_allow_maintainer_edit"`
@@ -111,7 +113,7 @@ type Repository struct {
// enum: sha1,sha256
ObjectFormatName string `json:"object_format_name"`
// swagger:strfmt date-time
- MirrorUpdated time.Time `json:"mirror_updated,omitempty"`
+ MirrorUpdated time.Time `json:"mirror_updated"`
RepoTransfer *RepoTransfer `json:"repo_transfer"`
Topics []string `json:"topics"`
Licenses []string `json:"licenses"`
@@ -357,7 +359,7 @@ type MigrateRepoOptions struct {
// required: true
RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"`
- // enum: git,github,gitea,gitlab,gogs,onedev,gitbucket,codebase
+ // enum: git,github,gitea,gitlab,gogs,onedev,gitbucket,codebase,codecommit
Service string `json:"service"`
AuthUsername string `json:"auth_username"`
AuthPassword string `json:"auth_password"`
diff --git a/modules/structs/repo_actions.go b/modules/structs/repo_actions.go
index b13f344738..ac1c288270 100644
--- a/modules/structs/repo_actions.go
+++ b/modules/structs/repo_actions.go
@@ -32,3 +32,157 @@ type ActionTaskResponse struct {
Entries []*ActionTask `json:"workflow_runs"`
TotalCount int64 `json:"total_count"`
}
+
+// CreateActionWorkflowDispatch represents the payload for triggering a workflow dispatch event
+// swagger:model
+type CreateActionWorkflowDispatch struct {
+ // required: true
+ // example: refs/heads/main
+ Ref string `json:"ref" binding:"Required"`
+ // required: false
+ Inputs map[string]string `json:"inputs,omitempty"`
+}
+
+// ActionWorkflow represents a ActionWorkflow
+type ActionWorkflow struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Path string `json:"path"`
+ State string `json:"state"`
+ // swagger:strfmt date-time
+ CreatedAt time.Time `json:"created_at"`
+ // swagger:strfmt date-time
+ UpdatedAt time.Time `json:"updated_at"`
+ URL string `json:"url"`
+ HTMLURL string `json:"html_url"`
+ BadgeURL string `json:"badge_url"`
+ // swagger:strfmt date-time
+ DeletedAt time.Time `json:"deleted_at"`
+}
+
+// ActionWorkflowResponse returns a ActionWorkflow
+type ActionWorkflowResponse struct {
+ Workflows []*ActionWorkflow `json:"workflows"`
+ TotalCount int64 `json:"total_count"`
+}
+
+// ActionArtifact represents a ActionArtifact
+type ActionArtifact struct {
+ ID int64 `json:"id"`
+ Name string `json:"name"`
+ SizeInBytes int64 `json:"size_in_bytes"`
+ URL string `json:"url"`
+ ArchiveDownloadURL string `json:"archive_download_url"`
+ Expired bool `json:"expired"`
+ WorkflowRun *ActionWorkflowRun `json:"workflow_run"`
+
+ // swagger:strfmt date-time
+ CreatedAt time.Time `json:"created_at"`
+ // swagger:strfmt date-time
+ UpdatedAt time.Time `json:"updated_at"`
+ // swagger:strfmt date-time
+ ExpiresAt time.Time `json:"expires_at"`
+}
+
+// ActionWorkflowRun represents a WorkflowRun
+type ActionWorkflowRun struct {
+ ID int64 `json:"id"`
+ URL string `json:"url"`
+ HTMLURL string `json:"html_url"`
+ DisplayTitle string `json:"display_title"`
+ Path string `json:"path"`
+ Event string `json:"event"`
+ RunAttempt int64 `json:"run_attempt"`
+ RunNumber int64 `json:"run_number"`
+ RepositoryID int64 `json:"repository_id,omitempty"`
+ HeadSha string `json:"head_sha"`
+ HeadBranch string `json:"head_branch,omitempty"`
+ Status string `json:"status"`
+ Actor *User `json:"actor,omitempty"`
+ TriggerActor *User `json:"trigger_actor,omitempty"`
+ Repository *Repository `json:"repository,omitempty"`
+ HeadRepository *Repository `json:"head_repository,omitempty"`
+ Conclusion string `json:"conclusion,omitempty"`
+ // swagger:strfmt date-time
+ StartedAt time.Time `json:"started_at"`
+ // swagger:strfmt date-time
+ CompletedAt time.Time `json:"completed_at"`
+}
+
+// ActionWorkflowRunsResponse returns ActionWorkflowRuns
+type ActionWorkflowRunsResponse struct {
+ Entries []*ActionWorkflowRun `json:"workflow_runs"`
+ TotalCount int64 `json:"total_count"`
+}
+
+// ActionWorkflowJobsResponse returns ActionWorkflowJobs
+type ActionWorkflowJobsResponse struct {
+ Entries []*ActionWorkflowJob `json:"jobs"`
+ TotalCount int64 `json:"total_count"`
+}
+
+// ActionArtifactsResponse returns ActionArtifacts
+type ActionArtifactsResponse struct {
+ Entries []*ActionArtifact `json:"artifacts"`
+ TotalCount int64 `json:"total_count"`
+}
+
+// ActionWorkflowStep represents a step of a WorkflowJob
+type ActionWorkflowStep struct {
+ Name string `json:"name"`
+ Number int64 `json:"number"`
+ Status string `json:"status"`
+ Conclusion string `json:"conclusion,omitempty"`
+ // swagger:strfmt date-time
+ StartedAt time.Time `json:"started_at"`
+ // swagger:strfmt date-time
+ CompletedAt time.Time `json:"completed_at"`
+}
+
+// ActionWorkflowJob represents a WorkflowJob
+type ActionWorkflowJob struct {
+ ID int64 `json:"id"`
+ URL string `json:"url"`
+ HTMLURL string `json:"html_url"`
+ RunID int64 `json:"run_id"`
+ RunURL string `json:"run_url"`
+ Name string `json:"name"`
+ Labels []string `json:"labels"`
+ RunAttempt int64 `json:"run_attempt"`
+ HeadSha string `json:"head_sha"`
+ HeadBranch string `json:"head_branch,omitempty"`
+ Status string `json:"status"`
+ Conclusion string `json:"conclusion,omitempty"`
+ RunnerID int64 `json:"runner_id,omitempty"`
+ RunnerName string `json:"runner_name,omitempty"`
+ Steps []*ActionWorkflowStep `json:"steps"`
+ // swagger:strfmt date-time
+ CreatedAt time.Time `json:"created_at"`
+ // swagger:strfmt date-time
+ StartedAt time.Time `json:"started_at"`
+ // swagger:strfmt date-time
+ CompletedAt time.Time `json:"completed_at"`
+}
+
+// ActionRunnerLabel represents a Runner Label
+type ActionRunnerLabel struct {
+ ID int64 `json:"id"`
+ Name string `json:"name"`
+ Type string `json:"type"`
+}
+
+// ActionRunner represents a Runner
+type ActionRunner struct {
+ ID int64 `json:"id"`
+ Name string `json:"name"`
+ Status string `json:"status"`
+ Busy bool `json:"busy"`
+ Ephemeral bool `json:"ephemeral"`
+ Labels []*ActionRunnerLabel `json:"labels"`
+}
+
+// ActionRunnersResponse returns Runners
+type ActionRunnersResponse struct {
+ Entries []*ActionRunner `json:"runners"`
+ TotalCount int64 `json:"total_count"`
+}
diff --git a/modules/structs/repo_branch.go b/modules/structs/repo_branch.go
index a9aa1d330a..5416f43b0d 100644
--- a/modules/structs/repo_branch.go
+++ b/modules/structs/repo_branch.go
@@ -133,3 +133,12 @@ type EditBranchProtectionOption struct {
type UpdateBranchProtectionPriories struct {
IDs []int64 `json:"ids"`
}
+
+type MergeUpstreamRequest struct {
+ Branch string `json:"branch"`
+ FfOnly bool `json:"ff_only"`
+}
+
+type MergeUpstreamResponse struct {
+ MergeStyle string `json:"merge_type"`
+}
diff --git a/modules/structs/repo_file.go b/modules/structs/repo_file.go
index 82bde96ab6..5a86db868b 100644
--- a/modules/structs/repo_file.go
+++ b/modules/structs/repo_file.go
@@ -4,6 +4,8 @@
package structs
+import "time"
+
// FileOptions options for all file APIs
type FileOptions struct {
// message (optional) for the commit of this file. if not supplied, a default message will be used
@@ -20,6 +22,23 @@ type FileOptions struct {
Signoff bool `json:"signoff"`
}
+type FileOptionsWithSHA struct {
+ FileOptions
+ // the blob ID (SHA) for the file that already exists, it is required for changing existing files
+ // required: true
+ SHA string `json:"sha" binding:"Required"`
+}
+
+func (f *FileOptions) GetFileOptions() *FileOptions {
+ return f
+}
+
+type FileOptionsInterface interface {
+ GetFileOptions() *FileOptions
+}
+
+var _ FileOptionsInterface = (*FileOptions)(nil)
+
// CreateFileOptions options for creating files
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
type CreateFileOptions struct {
@@ -29,29 +48,16 @@ type CreateFileOptions struct {
ContentBase64 string `json:"content"`
}
-// Branch returns branch name
-func (o *CreateFileOptions) Branch() string {
- return o.FileOptions.BranchName
-}
-
// DeleteFileOptions options for deleting files (used for other File structs below)
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
type DeleteFileOptions struct {
- FileOptions
- // sha is the SHA for the file that already exists
- // required: true
- SHA string `json:"sha" binding:"Required"`
-}
-
-// Branch returns branch name
-func (o *DeleteFileOptions) Branch() string {
- return o.FileOptions.BranchName
+ FileOptionsWithSHA
}
// UpdateFileOptions options for updating files
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
type UpdateFileOptions struct {
- DeleteFileOptions
+ FileOptionsWithSHA
// content must be base64 encoded
// required: true
ContentBase64 string `json:"content"`
@@ -59,23 +65,21 @@ type UpdateFileOptions struct {
FromPath string `json:"from_path" binding:"MaxSize(500)"`
}
-// Branch returns branch name
-func (o *UpdateFileOptions) Branch() string {
- return o.FileOptions.BranchName
-}
+// FIXME: there is no LastCommitID in FileOptions, actually it should be an alternative to the SHA in ChangeFileOperation
// ChangeFileOperation for creating, updating or deleting a file
type ChangeFileOperation struct {
- // indicates what to do with the file
+ // indicates what to do with the file: "create" for creating a new file, "update" for updating an existing file,
+ // "upload" for creating or updating a file, "rename" for renaming a file, and "delete" for deleting an existing file.
// required: true
- // enum: create,update,delete
+ // enum: create,update,upload,rename,delete
Operation string `json:"operation" binding:"Required"`
// path to the existing or new file
// required: true
Path string `json:"path" binding:"Required;MaxSize(500)"`
- // new or updated file content, must be base64 encoded
+ // new or updated file content, it must be base64 encoded
ContentBase64 string `json:"content"`
- // sha is the SHA for the file that already exists, required for update or delete
+ // the blob ID (SHA) for the file that already exists, required for changing existing files
SHA string `json:"sha"`
// old path of the file to move
FromPath string `json:"from_path"`
@@ -90,20 +94,10 @@ type ChangeFilesOptions struct {
Files []*ChangeFileOperation `json:"files" binding:"Required"`
}
-// Branch returns branch name
-func (o *ChangeFilesOptions) Branch() string {
- return o.FileOptions.BranchName
-}
-
-// FileOptionInterface provides a unified interface for the different file options
-type FileOptionInterface interface {
- Branch() string
-}
-
// ApplyDiffPatchFileOptions options for applying a diff patch
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
type ApplyDiffPatchFileOptions struct {
- DeleteFileOptions
+ FileOptions
// required: true
Content string `json:"content"`
}
@@ -115,12 +109,24 @@ type FileLinksResponse struct {
HTMLURL *string `json:"html"`
}
+type ContentsExtResponse struct {
+ FileContents *ContentsResponse `json:"file_contents,omitempty"`
+ DirContents []*ContentsResponse `json:"dir_contents,omitempty"`
+}
+
// ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content
type ContentsResponse struct {
- Name string `json:"name"`
- Path string `json:"path"`
- SHA string `json:"sha"`
- LastCommitSHA string `json:"last_commit_sha"`
+ Name string `json:"name"`
+ Path string `json:"path"`
+ SHA string `json:"sha"`
+
+ LastCommitSHA *string `json:"last_commit_sha,omitempty"`
+ // swagger:strfmt date-time
+ LastCommitterDate *time.Time `json:"last_committer_date,omitempty"`
+ // swagger:strfmt date-time
+ LastAuthorDate *time.Time `json:"last_author_date,omitempty"`
+ LastCommitMessage *string `json:"last_commit_message,omitempty"`
+
// `type` will be `file`, `dir`, `symlink`, or `submodule`
Type string `json:"type"`
Size int64 `json:"size"`
@@ -137,6 +143,9 @@ type ContentsResponse struct {
// `submodule_git_url` is populated when `type` is `submodule`, otherwise null
SubmoduleGitURL *string `json:"submodule_git_url"`
Links *FileLinksResponse `json:"_links"`
+
+ LfsOid *string `json:"lfs_oid,omitempty"`
+ LfsSize *int64 `json:"lfs_size,omitempty"`
}
// FileCommitResponse contains information generated from a Git commit for a repo's file.
@@ -170,3 +179,8 @@ type FileDeleteResponse struct {
Commit *FileCommitResponse `json:"commit"`
Verification *PayloadCommitVerification `json:"verification"`
}
+
+// GetFilesOptions options for retrieving metadate and content of multiple files
+type GetFilesOptions struct {
+ Files []string `json:"files" binding:"Required"`
+}
diff --git a/modules/structs/repo_tag.go b/modules/structs/repo_tag.go
index 5722513f4f..bb8bfd10cb 100644
--- a/modules/structs/repo_tag.go
+++ b/modules/structs/repo_tag.go
@@ -11,8 +11,8 @@ type Tag struct {
Message string `json:"message"`
ID string `json:"id"`
Commit *CommitMeta `json:"commit"`
- ZipballURL string `json:"zipball_url"`
- TarballURL string `json:"tarball_url"`
+ ZipballURL string `json:"zipball_url,omitempty"`
+ TarballURL string `json:"tarball_url,omitempty"`
}
// AnnotatedTag represents an annotated tag
diff --git a/modules/structs/secret.go b/modules/structs/secret.go
index a0673ca08c..2afb41ec43 100644
--- a/modules/structs/secret.go
+++ b/modules/structs/secret.go
@@ -10,6 +10,8 @@ import "time"
type Secret struct {
// the secret's name
Name string `json:"name"`
+ // the secret's description
+ Description string `json:"description"`
// swagger:strfmt date-time
Created time.Time `json:"created_at"`
}
@@ -21,4 +23,9 @@ type CreateOrUpdateSecretOption struct {
//
// required: true
Data string `json:"data" binding:"Required"`
+
+ // Description of the secret to update
+ //
+ // required: false
+ Description string `json:"description"`
}
diff --git a/modules/structs/settings.go b/modules/structs/settings.go
index e48b1a493d..59176210e6 100644
--- a/modules/structs/settings.go
+++ b/modules/structs/settings.go
@@ -26,6 +26,7 @@ type GeneralAPISettings struct {
DefaultPagingNum int `json:"default_paging_num"`
DefaultGitTreesPerPage int `json:"default_git_trees_per_page"`
DefaultMaxBlobSize int64 `json:"default_max_blob_size"`
+ DefaultMaxResponseSize int64 `json:"default_max_response_size"`
}
// GeneralAttachmentSettings contains global Attachment settings exposed by API
diff --git a/modules/structs/status.go b/modules/structs/status.go
index c1d8b902ec..a9779541ff 100644
--- a/modules/structs/status.go
+++ b/modules/structs/status.go
@@ -5,17 +5,19 @@ package structs
import (
"time"
+
+ "code.gitea.io/gitea/modules/commitstatus"
)
// CommitStatus holds a single status of a single Commit
type CommitStatus struct {
- ID int64 `json:"id"`
- State CommitStatusState `json:"status"`
- TargetURL string `json:"target_url"`
- Description string `json:"description"`
- URL string `json:"url"`
- Context string `json:"context"`
- Creator *User `json:"creator"`
+ ID int64 `json:"id"`
+ State commitstatus.CommitStatusState `json:"status"`
+ TargetURL string `json:"target_url"`
+ Description string `json:"description"`
+ URL string `json:"url"`
+ Context string `json:"context"`
+ Creator *User `json:"creator"`
// swagger:strfmt date-time
Created time.Time `json:"created_at"`
// swagger:strfmt date-time
@@ -24,19 +26,19 @@ type CommitStatus struct {
// CombinedStatus holds the combined state of several statuses for a single commit
type CombinedStatus struct {
- State CommitStatusState `json:"state"`
- SHA string `json:"sha"`
- TotalCount int `json:"total_count"`
- Statuses []*CommitStatus `json:"statuses"`
- Repository *Repository `json:"repository"`
- CommitURL string `json:"commit_url"`
- URL string `json:"url"`
+ State commitstatus.CommitStatusState `json:"state"`
+ SHA string `json:"sha"`
+ TotalCount int `json:"total_count"`
+ Statuses []*CommitStatus `json:"statuses"`
+ Repository *Repository `json:"repository"`
+ CommitURL string `json:"commit_url"`
+ URL string `json:"url"`
}
// CreateStatusOption holds the information needed to create a new CommitStatus for a Commit
type CreateStatusOption struct {
- State CommitStatusState `json:"state"`
- TargetURL string `json:"target_url"`
- Description string `json:"description"`
- Context string `json:"context"`
+ State commitstatus.CommitStatusState `json:"state"`
+ TargetURL string `json:"target_url"`
+ Description string `json:"description"`
+ Context string `json:"context"`
}
diff --git a/modules/structs/user.go b/modules/structs/user.go
index 5ed677f239..89349cda2c 100644
--- a/modules/structs/user.go
+++ b/modules/structs/user.go
@@ -15,9 +15,9 @@ import (
type User struct {
// the user's id
ID int64 `json:"id"`
- // the user's username
+ // login of the user, same as `username`
UserName string `json:"login"`
- // the user's authentication sign-in name.
+ // identifier of the user, provided by the external authenticator (if configured)
// default: empty
LoginName string `json:"login_name"`
// The ID of the user's Authentication Source
@@ -35,9 +35,9 @@ type User struct {
// Is the user an administrator
IsAdmin bool `json:"is_admin"`
// swagger:strfmt date-time
- LastLogin time.Time `json:"last_login,omitempty"`
+ LastLogin time.Time `json:"last_login"`
// swagger:strfmt date-time
- Created time.Time `json:"created,omitempty"`
+ Created time.Time `json:"created"`
// Is user restricted
Restricted bool `json:"restricted"`
// Is user active
diff --git a/modules/structs/user_app.go b/modules/structs/user_app.go
index a7d2e28b41..15811ceb66 100644
--- a/modules/structs/user_app.go
+++ b/modules/structs/user_app.go
@@ -11,11 +11,13 @@ import (
// AccessToken represents an API access token.
// swagger:response AccessToken
type AccessToken struct {
- ID int64 `json:"id"`
- Name string `json:"name"`
- Token string `json:"sha1"`
- TokenLastEight string `json:"token_last_eight"`
- Scopes []string `json:"scopes"`
+ ID int64 `json:"id"`
+ Name string `json:"name"`
+ Token string `json:"sha1"`
+ TokenLastEight string `json:"token_last_eight"`
+ Scopes []string `json:"scopes"`
+ Created time.Time `json:"created_at"`
+ Updated time.Time `json:"last_used_at"`
}
// AccessTokenList represents a list of API access token.
@@ -23,9 +25,11 @@ type AccessToken struct {
type AccessTokenList []*AccessToken
// CreateAccessTokenOption options when create access token
+// swagger:model CreateAccessTokenOption
type CreateAccessTokenOption struct {
// required: true
- Name string `json:"name" binding:"Required"`
+ Name string `json:"name" binding:"Required"`
+ // example: ["all", "read:activitypub","read:issue", "write:misc", "read:notification", "read:organization", "read:package", "read:repository", "read:user"]
Scopes []string `json:"scopes"`
}
diff --git a/modules/structs/user_email.go b/modules/structs/user_email.go
index 9319667e8f..01895a0058 100644
--- a/modules/structs/user_email.go
+++ b/modules/structs/user_email.go
@@ -11,6 +11,7 @@ type Email struct {
Verified bool `json:"verified"`
Primary bool `json:"primary"`
UserID int64 `json:"user_id"`
+ // username of the user
UserName string `json:"username"`
}
diff --git a/modules/structs/user_gpgkey.go b/modules/structs/user_gpgkey.go
index ff9b0aea1d..deae70de33 100644
--- a/modules/structs/user_gpgkey.go
+++ b/modules/structs/user_gpgkey.go
@@ -21,9 +21,9 @@ type GPGKey struct {
CanCertify bool `json:"can_certify"`
Verified bool `json:"verified"`
// swagger:strfmt date-time
- Created time.Time `json:"created_at,omitempty"`
+ Created time.Time `json:"created_at"`
// swagger:strfmt date-time
- Expires time.Time `json:"expires_at,omitempty"`
+ Expires time.Time `json:"expires_at"`
}
// GPGKeyEmail an email attached to a GPGKey
diff --git a/modules/structs/user_key.go b/modules/structs/user_key.go
index 08eed59a89..16225a852a 100644
--- a/modules/structs/user_key.go
+++ b/modules/structs/user_key.go
@@ -15,7 +15,8 @@ type PublicKey struct {
Title string `json:"title,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"`
// swagger:strfmt date-time
- Created time.Time `json:"created_at,omitempty"`
+ Created time.Time `json:"created_at"`
+ Updated time.Time `json:"last_used_at"`
Owner *User `json:"user,omitempty"`
ReadOnly bool `json:"read_only,omitempty"`
KeyType string `json:"key_type,omitempty"`
diff --git a/modules/structs/variable.go b/modules/structs/variable.go
index cc846cf0ec..5198937303 100644
--- a/modules/structs/variable.go
+++ b/modules/structs/variable.go
@@ -10,6 +10,11 @@ type CreateVariableOption struct {
//
// required: true
Value string `json:"value" binding:"Required"`
+
+ // Description of the variable to create
+ //
+ // required: false
+ Description string `json:"description"`
}
// UpdateVariableOption the option when updating variable
@@ -21,6 +26,11 @@ type UpdateVariableOption struct {
//
// required: true
Value string `json:"value" binding:"Required"`
+
+ // Description of the variable to update
+ //
+ // required: false
+ Description string `json:"description"`
}
// ActionVariable return value of the query API
@@ -34,4 +44,6 @@ type ActionVariable struct {
Name string `json:"name"`
// the value of the variable
Data string `json:"data"`
+ // the description of the variable
+ Description string `json:"description"`
}
diff --git a/modules/system/appstate_test.go b/modules/system/appstate_test.go
index 911319d00a..b5c057cf88 100644
--- a/modules/system/appstate_test.go
+++ b/modules/system/appstate_test.go
@@ -38,8 +38,8 @@ func TestAppStateDB(t *testing.T) {
item1 := new(testItem1)
assert.NoError(t, as.Get(db.DefaultContext, item1))
- assert.Equal(t, "", item1.Val1)
- assert.EqualValues(t, 0, item1.Val2)
+ assert.Empty(t, item1.Val1)
+ assert.Equal(t, 0, item1.Val2)
item1 = new(testItem1)
item1.Val1 = "a"
@@ -53,7 +53,7 @@ func TestAppStateDB(t *testing.T) {
item1 = new(testItem1)
assert.NoError(t, as.Get(db.DefaultContext, item1))
assert.Equal(t, "a", item1.Val1)
- assert.EqualValues(t, 2, item1.Val2)
+ assert.Equal(t, 2, item1.Val2)
item2 = new(testItem2)
assert.NoError(t, as.Get(db.DefaultContext, item2))
diff --git a/modules/tailmsg/talimsg.go b/modules/tailmsg/talimsg.go
new file mode 100644
index 0000000000..aafc98e2d2
--- /dev/null
+++ b/modules/tailmsg/talimsg.go
@@ -0,0 +1,73 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package tailmsg
+
+import (
+ "sync"
+ "time"
+)
+
+type MsgRecord struct {
+ Time time.Time
+ Content string
+}
+
+type MsgRecorder interface {
+ Record(content string)
+ GetRecords() []*MsgRecord
+}
+
+type memoryMsgRecorder struct {
+ mu sync.RWMutex
+ msgs []*MsgRecord
+ limit int
+}
+
+// TODO: use redis for a clustered environment
+
+func (m *memoryMsgRecorder) Record(content string) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ m.msgs = append(m.msgs, &MsgRecord{
+ Time: time.Now(),
+ Content: content,
+ })
+ if len(m.msgs) > m.limit {
+ m.msgs = m.msgs[len(m.msgs)-m.limit:]
+ }
+}
+
+func (m *memoryMsgRecorder) GetRecords() []*MsgRecord {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ ret := make([]*MsgRecord, len(m.msgs))
+ copy(ret, m.msgs)
+ return ret
+}
+
+func NewMsgRecorder(limit int) MsgRecorder {
+ return &memoryMsgRecorder{
+ limit: limit,
+ }
+}
+
+type Manager struct {
+ traceRecorder MsgRecorder
+ logRecorder MsgRecorder
+}
+
+func (m *Manager) GetTraceRecorder() MsgRecorder {
+ return m.traceRecorder
+}
+
+func (m *Manager) GetLogRecorder() MsgRecorder {
+ return m.logRecorder
+}
+
+var GetManager = sync.OnceValue(func() *Manager {
+ return &Manager{
+ traceRecorder: NewMsgRecorder(100),
+ logRecorder: NewMsgRecorder(1000),
+ }
+})
diff --git a/modules/tempdir/tempdir.go b/modules/tempdir/tempdir.go
new file mode 100644
index 0000000000..22c2e4ea16
--- /dev/null
+++ b/modules/tempdir/tempdir.go
@@ -0,0 +1,112 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package tempdir
+
+import (
+ "os"
+ "path/filepath"
+ "time"
+
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/util"
+)
+
+type TempDir struct {
+ // base is the base directory for temporary files, it must exist before accessing and won't be created automatically.
+ // for example: base="/system-tmpdir", sub="gitea-tmp"
+ base, sub string
+}
+
+func (td *TempDir) JoinPath(elems ...string) string {
+ return filepath.Join(append([]string{td.base, td.sub}, elems...)...)
+}
+
+// MkdirAllSub works like os.MkdirAll, but the base directory must exist
+func (td *TempDir) MkdirAllSub(dir string) (string, error) {
+ if _, err := os.Stat(td.base); err != nil {
+ return "", err
+ }
+ full := filepath.Join(td.base, td.sub, dir)
+ if err := os.MkdirAll(full, os.ModePerm); err != nil {
+ return "", err
+ }
+ return full, nil
+}
+
+func (td *TempDir) prepareDirWithPattern(elems ...string) (dir, pattern string, err error) {
+ if _, err = os.Stat(td.base); err != nil {
+ return "", "", err
+ }
+ dir, pattern = filepath.Split(filepath.Join(append([]string{td.base, td.sub}, elems...)...))
+ if err = os.MkdirAll(dir, os.ModePerm); err != nil {
+ return "", "", err
+ }
+ return dir, pattern, nil
+}
+
+// MkdirTempRandom works like os.MkdirTemp, the last path field is the "pattern"
+func (td *TempDir) MkdirTempRandom(elems ...string) (string, func(), error) {
+ dir, pattern, err := td.prepareDirWithPattern(elems...)
+ if err != nil {
+ return "", nil, err
+ }
+ dir, err = os.MkdirTemp(dir, pattern)
+ if err != nil {
+ return "", nil, err
+ }
+ return dir, func() {
+ if err := util.RemoveAll(dir); err != nil {
+ log.Error("Failed to remove temp directory %s: %v", dir, err)
+ }
+ }, nil
+}
+
+// CreateTempFileRandom works like os.CreateTemp, the last path field is the "pattern"
+func (td *TempDir) CreateTempFileRandom(elems ...string) (*os.File, func(), error) {
+ dir, pattern, err := td.prepareDirWithPattern(elems...)
+ if err != nil {
+ return nil, nil, err
+ }
+ f, err := os.CreateTemp(dir, pattern)
+ if err != nil {
+ return nil, nil, err
+ }
+ filename := f.Name()
+ return f, func() {
+ _ = f.Close()
+ if err := util.Remove(filename); err != nil {
+ log.Error("Unable to remove temporary file: %s: Error: %v", filename, err)
+ }
+ }, err
+}
+
+func (td *TempDir) RemoveOutdated(d time.Duration) {
+ var remove func(path string)
+ remove = func(path string) {
+ entries, _ := os.ReadDir(path)
+ for _, entry := range entries {
+ full := filepath.Join(path, entry.Name())
+ if entry.IsDir() {
+ remove(full)
+ _ = os.Remove(full)
+ continue
+ }
+ info, err := entry.Info()
+ if err == nil && time.Since(info.ModTime()) > d {
+ _ = os.Remove(full)
+ }
+ }
+ }
+ remove(td.JoinPath(""))
+}
+
+// New create a new TempDir instance, "base" must be an existing directory,
+// "sub" could be a multi-level directory and will be created if not exist
+func New(base, sub string) *TempDir {
+ return &TempDir{base: base, sub: sub}
+}
+
+func OsTempDir(sub string) *TempDir {
+ return New(os.TempDir(), sub)
+}
diff --git a/modules/tempdir/tempdir_test.go b/modules/tempdir/tempdir_test.go
new file mode 100644
index 0000000000..d6afcb7bed
--- /dev/null
+++ b/modules/tempdir/tempdir_test.go
@@ -0,0 +1,75 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package tempdir
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTempDir(t *testing.T) {
+ base := t.TempDir()
+
+ t.Run("Create", func(t *testing.T) {
+ td := New(base, "sub1/sub2") // make sure the sub dir supports "/" in the path
+ assert.Equal(t, filepath.Join(base, "sub1", "sub2"), td.JoinPath())
+ assert.Equal(t, filepath.Join(base, "sub1", "sub2/test"), td.JoinPath("test"))
+
+ t.Run("MkdirTempRandom", func(t *testing.T) {
+ s, cleanup, err := td.MkdirTempRandom("foo")
+ assert.NoError(t, err)
+ assert.True(t, strings.HasPrefix(s, filepath.Join(base, "sub1/sub2", "foo")))
+
+ _, err = os.Stat(s)
+ assert.NoError(t, err)
+ cleanup()
+ _, err = os.Stat(s)
+ assert.ErrorIs(t, err, os.ErrNotExist)
+ })
+
+ t.Run("CreateTempFileRandom", func(t *testing.T) {
+ f, cleanup, err := td.CreateTempFileRandom("foo", "bar")
+ filename := f.Name()
+ assert.NoError(t, err)
+ assert.True(t, strings.HasPrefix(filename, filepath.Join(base, "sub1/sub2", "foo", "bar")))
+ _, err = os.Stat(filename)
+ assert.NoError(t, err)
+ cleanup()
+ _, err = os.Stat(filename)
+ assert.ErrorIs(t, err, os.ErrNotExist)
+ })
+
+ t.Run("RemoveOutDated", func(t *testing.T) {
+ fa1, _, err := td.CreateTempFileRandom("dir-a", "f1")
+ assert.NoError(t, err)
+ fa2, _, err := td.CreateTempFileRandom("dir-a", "f2")
+ assert.NoError(t, err)
+ _ = os.Chtimes(fa2.Name(), time.Now().Add(-time.Hour), time.Now().Add(-time.Hour))
+ fb1, _, err := td.CreateTempFileRandom("dir-b", "f1")
+ assert.NoError(t, err)
+ _ = os.Chtimes(fb1.Name(), time.Now().Add(-time.Hour), time.Now().Add(-time.Hour))
+ _, _, _ = fa1.Close(), fa2.Close(), fb1.Close()
+
+ td.RemoveOutdated(time.Minute)
+
+ _, err = os.Stat(fa1.Name())
+ assert.NoError(t, err)
+ _, err = os.Stat(fa2.Name())
+ assert.ErrorIs(t, err, os.ErrNotExist)
+ _, err = os.Stat(fb1.Name())
+ assert.ErrorIs(t, err, os.ErrNotExist)
+ })
+ })
+
+ t.Run("BaseNotExist", func(t *testing.T) {
+ td := New(filepath.Join(base, "not-exist"), "sub")
+ _, _, err := td.MkdirTempRandom("foo")
+ assert.ErrorIs(t, err, os.ErrNotExist)
+ })
+}
diff --git a/modules/templates/eval/eval_test.go b/modules/templates/eval/eval_test.go
index c9e514b5eb..f956f6cbdf 100644
--- a/modules/templates/eval/eval_test.go
+++ b/modules/templates/eval/eval_test.go
@@ -12,7 +12,7 @@ import (
)
func tokens(s string) (a []any) {
- for _, v := range strings.Fields(s) {
+ for v := range strings.FieldsSeq(s) {
a = append(a, v)
}
return a
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 48d3a8ff89..e454bce4bd 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -6,9 +6,9 @@ package templates
import (
"fmt"
- "html"
"html/template"
"net/url"
+ "strconv"
"strings"
"time"
@@ -37,12 +37,9 @@ func NewFuncMap() template.FuncMap {
"dict": dict, // it's lowercase because this name has been widely used. Our other functions should have uppercase names.
"Iif": iif,
"Eval": evalTokens,
- "SafeHTML": safeHTML,
- "HTMLFormat": htmlutil.HTMLFormat,
- "HTMLEscape": htmlEscape,
+ "HTMLFormat": htmlFormat,
"QueryEscape": queryEscape,
"QueryBuild": QueryBuild,
- "JSEscape": jsEscapeSafe,
"SanitizeHTML": SanitizeHTML,
"URLJoin": util.URLJoin,
"DotEscape": dotEscape,
@@ -59,7 +56,6 @@ func NewFuncMap() template.FuncMap {
// -----------------------------------------------------------------
// svg / avatar / icon / color
"svg": svg.RenderHTML,
- "EntryIcon": base.EntryIcon,
"MigrationIcon": migrationIcon,
"ActionIcon": actionIcon,
"SortArrow": sortArrow,
@@ -69,12 +65,12 @@ func NewFuncMap() template.FuncMap {
// time / number / format
"FileSize": base.FileSize,
"CountFmt": countFmt,
- "Sec2Time": util.SecToTime,
+ "Sec2Hour": util.SecToHours,
"TimeEstimateString": timeEstimateString,
"LoadTimes": func(startTime time.Time) string {
- return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
+ return strconv.FormatInt(time.Since(startTime).Nanoseconds()/1e6, 10) + "ms"
},
// -----------------------------------------------------------------
@@ -162,55 +158,28 @@ func NewFuncMap() template.FuncMap {
"FilenameIsImage": filenameIsImage,
"TabSizeClass": tabSizeClass,
-
- // for backward compatibility only, do not use them anymore
- "TimeSince": timeSinceLegacy,
- "TimeSinceUnix": timeSinceLegacy,
- "DateTime": dateTimeLegacy,
-
- "RenderEmoji": renderEmojiLegacy,
- "RenderLabel": renderLabelLegacy,
- "RenderLabels": renderLabelsLegacy,
- "RenderIssueTitle": renderIssueTitleLegacy,
-
- "RenderMarkdownToHtml": renderMarkdownToHtmlLegacy,
-
- "RenderCommitMessage": renderCommitMessageLegacy,
- "RenderCommitMessageLinkSubject": renderCommitMessageLinkSubjectLegacy,
- "RenderCommitBody": renderCommitBodyLegacy,
- }
-}
-
-// safeHTML render raw as HTML
-func safeHTML(s any) template.HTML {
- switch v := s.(type) {
- case string:
- return template.HTML(v)
- case template.HTML:
- return v
}
- panic(fmt.Sprintf("unexpected type %T", s))
}
-// SanitizeHTML sanitizes the input by pre-defined markdown rules
+// SanitizeHTML sanitizes the input by default sanitization rules.
func SanitizeHTML(s string) template.HTML {
- return template.HTML(markup.Sanitize(s))
+ return markup.Sanitize(s)
}
-func htmlEscape(s any) template.HTML {
+func htmlFormat(s any, args ...any) template.HTML {
+ if len(args) == 0 {
+ // to prevent developers from calling "HTMLFormat $userInput" by mistake which will lead to XSS
+ panic("missing arguments for HTMLFormat")
+ }
switch v := s.(type) {
case string:
- return template.HTML(html.EscapeString(v))
+ return htmlutil.HTMLFormat(template.HTML(v), args...)
case template.HTML:
- return v
+ return htmlutil.HTMLFormat(v, args...)
}
panic(fmt.Sprintf("unexpected type %T", s))
}
-func jsEscapeSafe(s string) template.HTML {
- return template.HTML(template.JSEscapeString(s))
-}
-
func queryEscape(s string) template.URL {
return template.URL(url.QueryEscape(s))
}
@@ -353,7 +322,3 @@ func QueryBuild(a ...any) template.URL {
}
return template.URL(s)
}
-
-func panicIfDevOrTesting() {
- setting.PanicInDevOrTesting("legacy template functions are for backward compatibility only, do not use them in new code")
-}
diff --git a/modules/templates/helper_test.go b/modules/templates/helper_test.go
index e35e8a28f8..7e3a952e7b 100644
--- a/modules/templates/helper_test.go
+++ b/modules/templates/helper_test.go
@@ -8,7 +8,6 @@ import (
"strings"
"testing"
- "code.gitea.io/gitea/modules/htmlutil"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
@@ -16,7 +15,7 @@ import (
func TestSubjectBodySeparator(t *testing.T) {
test := func(input, subject, body string) {
- loc := mailSubjectSplit.FindIndex([]byte(input))
+ loc := mailSubjectSplit.FindStringIndex(input)
if loc == nil {
assert.Empty(t, subject, "no subject found, but one expected")
assert.Equal(t, body, input)
@@ -58,10 +57,6 @@ func TestSubjectBodySeparator(t *testing.T) {
"Insufficient\n--\nSeparators")
}
-func TestJSEscapeSafe(t *testing.T) {
- assert.EqualValues(t, `\u0026\u003C\u003E\'\"`, jsEscapeSafe(`&<>'"`))
-}
-
func TestSanitizeHTML(t *testing.T) {
assert.Equal(t, template.HTML(`<a href="/" rel="nofollow">link</a> xss <div>inline</div>`), SanitizeHTML(`<a href="/">link</a> <a href="javascript:">xss</a> <div style="dangerous">inline</div>`))
}
@@ -88,7 +83,7 @@ func TestTemplateIif(t *testing.T) {
func TestTemplateEscape(t *testing.T) {
execTmpl := func(code string) string {
tmpl := template.New("test")
- tmpl.Funcs(template.FuncMap{"QueryBuild": QueryBuild, "HTMLFormat": htmlutil.HTMLFormat})
+ tmpl.Funcs(template.FuncMap{"QueryBuild": QueryBuild, "HTMLFormat": htmlFormat})
template.Must(tmpl.Parse(code))
w := &strings.Builder{}
assert.NoError(t, tmpl.Execute(w, nil))
@@ -121,8 +116,8 @@ func TestTemplateEscape(t *testing.T) {
func TestQueryBuild(t *testing.T) {
t.Run("construct", func(t *testing.T) {
- assert.Equal(t, "", string(QueryBuild()))
- assert.Equal(t, "", string(QueryBuild("a", nil, "b", false, "c", 0, "d", "")))
+ assert.Empty(t, string(QueryBuild()))
+ assert.Empty(t, string(QueryBuild("a", nil, "b", false, "c", 0, "d", "")))
assert.Equal(t, "a=1&b=true", string(QueryBuild("a", 1, "b", "true")))
// path with query parameters
@@ -137,9 +132,9 @@ func TestQueryBuild(t *testing.T) {
// only query parameters
assert.Equal(t, "&k=1", string(QueryBuild("&", "k", 1)))
- assert.Equal(t, "", string(QueryBuild("&", "k", 0)))
- assert.Equal(t, "", string(QueryBuild("&k=a", "k", 0)))
- assert.Equal(t, "", string(QueryBuild("k=a&", "k", 0)))
+ assert.Empty(t, string(QueryBuild("&", "k", 0)))
+ assert.Empty(t, string(QueryBuild("&k=a", "k", 0)))
+ assert.Empty(t, string(QueryBuild("k=a&", "k", 0)))
assert.Equal(t, "a=1&b=2", string(QueryBuild("a=1", "b", 2)))
assert.Equal(t, "&a=1&b=2", string(QueryBuild("&a=1", "b", 2)))
assert.Equal(t, "a=1&b=2&", string(QueryBuild("a=1&", "b", 2)))
diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go
index 529284f7e8..8073a6e5f5 100644
--- a/modules/templates/htmlrenderer.go
+++ b/modules/templates/htmlrenderer.go
@@ -42,7 +42,7 @@ var (
var ErrTemplateNotInitialized = errors.New("template system is not initialized, check your log for errors")
-func (h *HTMLRender) HTML(w io.Writer, status int, tplName TplName, data any, ctx context.Context) error { //nolint:revive
+func (h *HTMLRender) HTML(w io.Writer, status int, tplName TplName, data any, ctx context.Context) error { //nolint:revive // we don't use ctx, only pass it to the template executor
name := string(tplName)
if respWriter, ok := w.(http.ResponseWriter); ok {
if respWriter.Header().Get("Content-Type") == "" {
@@ -57,7 +57,7 @@ func (h *HTMLRender) HTML(w io.Writer, status int, tplName TplName, data any, ct
return t.Execute(w, data)
}
-func (h *HTMLRender) TemplateLookup(name string, ctx context.Context) (TemplateExecutor, error) { //nolint:revive
+func (h *HTMLRender) TemplateLookup(name string, ctx context.Context) (TemplateExecutor, error) { //nolint:revive // we don't use ctx, only pass it to the template executor
tmpls := h.templates.Load()
if tmpls == nil {
return nil, ErrTemplateNotInitialized
@@ -251,7 +251,7 @@ func extractErrorLine(code []byte, lineNum, posNum int, target string) string {
b := bufio.NewReader(bytes.NewReader(code))
var line []byte
var err error
- for i := 0; i < lineNum; i++ {
+ for i := range lineNum {
if line, err = b.ReadBytes('\n'); err != nil {
if i == lineNum-1 && errors.Is(err, io.EOF) {
err = nil
diff --git a/modules/templates/htmlrenderer_test.go b/modules/templates/htmlrenderer_test.go
index 2a74b74c23..e8b01c30fe 100644
--- a/modules/templates/htmlrenderer_test.go
+++ b/modules/templates/htmlrenderer_test.go
@@ -65,7 +65,7 @@ func TestHandleError(t *testing.T) {
_, err = tmpl.Parse(s)
assert.Error(t, err)
msg := h(err)
- assert.EqualValues(t, strings.TrimSpace(expect), strings.TrimSpace(msg))
+ assert.Equal(t, strings.TrimSpace(expect), strings.TrimSpace(msg))
}
test("{{", p.handleGenericTemplateError, `
@@ -102,5 +102,5 @@ god knows XXX
----------------------------------------------------------------------
`
actualMsg := p.handleExpectedEndError(errors.New("template: test:1: expected end; found XXX"))
- assert.EqualValues(t, strings.TrimSpace(expectedMsg), strings.TrimSpace(actualMsg))
+ assert.Equal(t, strings.TrimSpace(expectedMsg), strings.TrimSpace(actualMsg))
}
diff --git a/modules/templates/scopedtmpl/scopedtmpl.go b/modules/templates/scopedtmpl/scopedtmpl.go
index 2722ba97a2..34e8b9ad70 100644
--- a/modules/templates/scopedtmpl/scopedtmpl.go
+++ b/modules/templates/scopedtmpl/scopedtmpl.go
@@ -7,6 +7,7 @@ import (
"fmt"
"html/template"
"io"
+ "maps"
"reflect"
"sync"
texttemplate "text/template"
@@ -40,9 +41,7 @@ func (t *ScopedTemplate) Funcs(funcMap template.FuncMap) {
panic("cannot add new functions to frozen template set")
}
t.all.Funcs(funcMap)
- for k, v := range funcMap {
- t.parseFuncs[k] = v
- }
+ maps.Copy(t.parseFuncs, funcMap)
}
func (t *ScopedTemplate) New(name string) *template.Template {
@@ -103,31 +102,28 @@ func escapeTemplate(t *template.Template) error {
return nil
}
-//nolint:unused
type htmlTemplate struct {
- escapeErr error
- text *texttemplate.Template
+ _/*escapeErr*/ error
+ text *texttemplate.Template
}
-//nolint:unused
type textTemplateCommon struct {
- tmpl map[string]*template.Template // Map from name to defined templates.
- muTmpl sync.RWMutex // protects tmpl
- option struct {
+ _/*tmpl*/ map[string]*template.Template
+ _/*muTmpl*/ sync.RWMutex
+ _/*option*/ struct {
missingKey int
}
- muFuncs sync.RWMutex // protects parseFuncs and execFuncs
- parseFuncs texttemplate.FuncMap
- execFuncs map[string]reflect.Value
+ muFuncs sync.RWMutex
+ _/*parseFuncs*/ texttemplate.FuncMap
+ execFuncs map[string]reflect.Value
}
-//nolint:unused
type textTemplate struct {
- name string
+ _/*name*/ string
*parse.Tree
*textTemplateCommon
- leftDelim string
- rightDelim string
+ _/*leftDelim*/ string
+ _/*rightDelim*/ string
}
func ptr[T, P any](ptr *P) *T {
@@ -159,9 +155,7 @@ func newScopedTemplateSet(all *template.Template, name string) (*scopedTemplateS
textTmplPtr.muFuncs.Lock()
ts.execFuncs = map[string]reflect.Value{}
- for k, v := range textTmplPtr.execFuncs {
- ts.execFuncs[k] = v
- }
+ maps.Copy(ts.execFuncs, textTmplPtr.execFuncs)
textTmplPtr.muFuncs.Unlock()
var collectTemplates func(nodes []parse.Node)
@@ -220,9 +214,7 @@ func (ts *scopedTemplateSet) newExecutor(funcMap map[string]any) TemplateExecuto
tmpl := texttemplate.New("")
tmplPtr := ptr[textTemplate](tmpl)
tmplPtr.execFuncs = map[string]reflect.Value{}
- for k, v := range ts.execFuncs {
- tmplPtr.execFuncs[k] = v
- }
+ maps.Copy(tmplPtr.execFuncs, ts.execFuncs)
if funcMap != nil {
tmpl.Funcs(funcMap)
}
diff --git a/modules/templates/static.go b/modules/templates/static.go
deleted file mode 100644
index b5a7e561ec..0000000000
--- a/modules/templates/static.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2016 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-//go:build bindata
-
-package templates
-
-import (
- "time"
-
- "code.gitea.io/gitea/modules/assetfs"
- "code.gitea.io/gitea/modules/timeutil"
-)
-
-// GlobalModTime provide a global mod time for embedded asset files
-func GlobalModTime(filename string) time.Time {
- return timeutil.GetExecutableModTime()
-}
-
-func BuiltinAssets() *assetfs.Layer {
- return assetfs.Bindata("builtin(bindata)", Assets)
-}
diff --git a/modules/templates/templates_bindata.go b/modules/templates/templates_bindata.go
index 6f1d3cf539..a919591ecf 100644
--- a/modules/templates/templates_bindata.go
+++ b/modules/templates/templates_bindata.go
@@ -3,6 +3,21 @@
//go:build bindata
+//go:generate go run ../../build/generate-bindata.go ../../templates bindata.dat
+
package templates
-//go:generate go run ../../build/generate-bindata.go ../../templates templates bindata.go true
+import (
+ "sync"
+
+ _ "embed"
+
+ "code.gitea.io/gitea/modules/assetfs"
+)
+
+//go:embed bindata.dat
+var bindata []byte
+
+var BuiltinAssets = sync.OnceValue(func() *assetfs.Layer {
+ return assetfs.Bindata("builtin(bindata)", assetfs.NewEmbeddedFS(bindata))
+})
diff --git a/modules/templates/dynamic.go b/modules/templates/templates_dynamic.go
index e1babd83c9..e1babd83c9 100644
--- a/modules/templates/dynamic.go
+++ b/modules/templates/templates_dynamic.go
diff --git a/modules/templates/util_avatar.go b/modules/templates/util_avatar.go
index f7dd408ee2..ee9994ab0b 100644
--- a/modules/templates/util_avatar.go
+++ b/modules/templates/util_avatar.go
@@ -5,9 +5,9 @@ package templates
import (
"context"
- "fmt"
"html"
"html/template"
+ "strconv"
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/avatars"
@@ -28,13 +28,14 @@ func NewAvatarUtils(ctx context.Context) *AvatarUtils {
// AvatarHTML creates the HTML for an avatar
func AvatarHTML(src string, size int, class, name string) template.HTML {
- sizeStr := fmt.Sprintf(`%d`, size)
+ sizeStr := strconv.Itoa(size)
if name == "" {
name = "avatar"
}
- return template.HTML(`<img loading="lazy" class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`)
+ // use empty alt, otherwise if the image fails to load, the width will follow the "alt" text's width
+ return template.HTML(`<img loading="lazy" alt class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`)
}
// Avatar renders user avatars. args: user, size (int), class (string)
diff --git a/modules/templates/util_date.go b/modules/templates/util_date.go
index 658691ee40..fc3f3f2339 100644
--- a/modules/templates/util_date.go
+++ b/modules/templates/util_date.go
@@ -99,7 +99,7 @@ func dateTimeFormat(format string, datetime any) template.HTML {
attrs = append(attrs, `format="datetime"`, `month="short"`, `day="numeric"`, `hour="numeric"`, `minute="numeric"`, `second="numeric"`, `data-tooltip-content`, `data-tooltip-interactive="true"`)
return template.HTML(fmt.Sprintf(`<relative-time %s datetime="%s">%s</relative-time>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped))
default:
- panic(fmt.Sprintf("Unsupported format %s", format))
+ panic("Unsupported format " + format)
}
}
diff --git a/modules/templates/util_date_legacy.go b/modules/templates/util_date_legacy.go
deleted file mode 100644
index ceefb00447..0000000000
--- a/modules/templates/util_date_legacy.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2024 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package templates
-
-import (
- "html/template"
-
- "code.gitea.io/gitea/modules/translation"
-)
-
-func dateTimeLegacy(format string, datetime any, _ ...string) template.HTML {
- panicIfDevOrTesting()
- if s, ok := datetime.(string); ok {
- datetime = parseLegacy(s)
- }
- return dateTimeFormat(format, datetime)
-}
-
-func timeSinceLegacy(time any, _ translation.Locale) template.HTML {
- panicIfDevOrTesting()
- return TimeSince(time)
-}
diff --git a/modules/templates/util_date_test.go b/modules/templates/util_date_test.go
index f3a2409a9f..2c1f2d242e 100644
--- a/modules/templates/util_date_test.go
+++ b/modules/templates/util_date_test.go
@@ -17,12 +17,12 @@ import (
func TestDateTime(t *testing.T) {
testTz, _ := time.LoadLocation("America/New_York")
defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
+ defer test.MockVariableValue(&setting.IsProd, true)()
defer test.MockVariableValue(&setting.IsInTesting, false)()
du := NewDateUtils()
refTimeStr := "2018-01-01T00:00:00Z"
- refDateStr := "2018-01-01"
refTime, _ := time.Parse(time.RFC3339, refTimeStr)
refTimeStamp := timeutil.TimeStamp(refTime.Unix())
@@ -31,18 +31,9 @@ func TestDateTime(t *testing.T) {
assert.EqualValues(t, "-", du.AbsoluteShort(time.Time{}))
assert.EqualValues(t, "-", du.AbsoluteShort(timeutil.TimeStamp(0)))
- actual := dateTimeLegacy("short", "invalid")
- assert.EqualValues(t, `-`, actual)
-
- actual = dateTimeLegacy("short", refTimeStr)
- assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2018-01-01T00:00:00Z">2018-01-01</absolute-date>`, actual)
-
- actual = du.AbsoluteShort(refTime)
+ actual := du.AbsoluteShort(refTime)
assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2018-01-01T00:00:00Z">2018-01-01</absolute-date>`, actual)
- actual = dateTimeLegacy("short", refDateStr)
- assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2018-01-01T00:00:00-05:00">2018-01-01</absolute-date>`, actual)
-
actual = du.AbsoluteShort(refTimeStamp)
assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2017-12-31T19:00:00-05:00">2017-12-31</absolute-date>`, actual)
@@ -53,6 +44,7 @@ func TestDateTime(t *testing.T) {
func TestTimeSince(t *testing.T) {
testTz, _ := time.LoadLocation("America/New_York")
defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
+ defer test.MockVariableValue(&setting.IsProd, true)()
defer test.MockVariableValue(&setting.IsInTesting, false)()
du := NewDateUtils()
@@ -67,6 +59,6 @@ func TestTimeSince(t *testing.T) {
actual = timeSinceTo(&refTime, time.Time{})
assert.EqualValues(t, `<relative-time prefix="" tense="future" datetime="2018-01-01T00:00:00Z" data-tooltip-content data-tooltip-interactive="true">2018-01-01 00:00:00 +00:00</relative-time>`, actual)
- actual = timeSinceLegacy(timeutil.TimeStampNano(refTime.UnixNano()), nil)
+ actual = du.TimeSince(timeutil.TimeStampNano(refTime.UnixNano()))
assert.EqualValues(t, `<relative-time prefix="" tense="past" datetime="2017-12-31T19:00:00-05:00" data-tooltip-content data-tooltip-interactive="true">2017-12-31 19:00:00 -05:00</relative-time>`, actual)
}
diff --git a/modules/templates/util_dict.go b/modules/templates/util_dict.go
index 8d6376b522..cc3018a71c 100644
--- a/modules/templates/util_dict.go
+++ b/modules/templates/util_dict.go
@@ -4,6 +4,7 @@
package templates
import (
+ "errors"
"fmt"
"html"
"html/template"
@@ -33,7 +34,7 @@ func dictMerge(base map[string]any, arg any) bool {
// The dot syntax is highly discouraged because it might cause unclear key conflicts. It's always good to use explicit keys.
func dict(args ...any) (map[string]any, error) {
if len(args)%2 != 0 {
- return nil, fmt.Errorf("invalid dict constructor syntax: must have key-value pairs")
+ return nil, errors.New("invalid dict constructor syntax: must have key-value pairs")
}
m := make(map[string]any, len(args)/2)
for i := 0; i < len(args); i += 2 {
diff --git a/modules/templates/util_format.go b/modules/templates/util_format.go
index bee6fb7b75..3485e3251e 100644
--- a/modules/templates/util_format.go
+++ b/modules/templates/util_format.go
@@ -5,6 +5,7 @@ package templates
import (
"fmt"
+ "strconv"
"code.gitea.io/gitea/modules/util"
)
@@ -24,7 +25,7 @@ func countFmt(data any) string {
return ""
}
if num < 1000 {
- return fmt.Sprintf("%d", num)
+ return strconv.FormatInt(num, 10)
} else if num < 1_000_000 {
num2 := float32(num) / 1000.0
return fmt.Sprintf("%.1fk", num2)
diff --git a/modules/templates/util_format_test.go b/modules/templates/util_format_test.go
index 8d466faff0..13a57c24e2 100644
--- a/modules/templates/util_format_test.go
+++ b/modules/templates/util_format_test.go
@@ -14,5 +14,5 @@ func TestCountFmt(t *testing.T) {
assert.Equal(t, "1.3k", countFmt(int64(1317)))
assert.Equal(t, "21.3M", countFmt(21317675))
assert.Equal(t, "45.7G", countFmt(45721317675))
- assert.Equal(t, "", countFmt("test"))
+ assert.Empty(t, countFmt("test"))
}
diff --git a/modules/templates/util_json.go b/modules/templates/util_json.go
index 71a4e23d36..29a04290fa 100644
--- a/modules/templates/util_json.go
+++ b/modules/templates/util_json.go
@@ -9,11 +9,11 @@ import (
"code.gitea.io/gitea/modules/json"
)
-type JsonUtils struct{} //nolint:revive
+type JsonUtils struct{} //nolint:revive // variable naming triggers on Json, wants JSON
var jsonUtils = JsonUtils{}
-func NewJsonUtils() *JsonUtils { //nolint:revive
+func NewJsonUtils() *JsonUtils { //nolint:revive // variable naming triggers on Json, wants JSON
return &jsonUtils
}
diff --git a/modules/templates/util_misc.go b/modules/templates/util_misc.go
index d645fa013e..cc5bf67b42 100644
--- a/modules/templates/util_misc.go
+++ b/modules/templates/util_misc.go
@@ -38,10 +38,11 @@ func sortArrow(normSort, revSort, urlSort string, isDefault bool) template.HTML
} else {
// if sort arg is in url test if it correlates with column header sort arguments
// the direction of the arrow should indicate the "current sort order", up means ASC(normal), down means DESC(rev)
- if urlSort == normSort {
+ switch urlSort {
+ case normSort:
// the table is sorted with this header normal
return svg.RenderHTML("octicon-triangle-up", 16)
- } else if urlSort == revSort {
+ case revSort:
// the table is sorted with this header reverse
return svg.RenderHTML("octicon-triangle-down", 16)
}
@@ -150,7 +151,7 @@ func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteNa
return ret
}
- u, err := giturl.Parse(remoteURL)
+ u, err := giturl.ParseGitURL(remoteURL)
if err != nil {
log.Error("giturl.Parse %v", err)
return ret
diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go
index 1800747f48..1056c42643 100644
--- a/modules/templates/util_render.go
+++ b/modules/templates/util_render.go
@@ -4,7 +4,6 @@
package templates
import (
- "context"
"encoding/hex"
"fmt"
"html/template"
@@ -15,44 +14,47 @@ import (
"unicode"
issues_model "code.gitea.io/gitea/models/issues"
+ "code.gitea.io/gitea/models/renderhelper"
+ "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/emoji"
"code.gitea.io/gitea/modules/htmlutil"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
+ "code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/util"
)
type RenderUtils struct {
- ctx context.Context
+ ctx reqctx.RequestContext
}
-func NewRenderUtils(ctx context.Context) *RenderUtils {
+func NewRenderUtils(ctx reqctx.RequestContext) *RenderUtils {
return &RenderUtils{ctx: ctx}
}
// RenderCommitMessage renders commit message with XSS-safe and special links.
-func (ut *RenderUtils) RenderCommitMessage(msg string, metas map[string]string) template.HTML {
+func (ut *RenderUtils) RenderCommitMessage(msg string, repo *repo.Repository) template.HTML {
cleanMsg := template.HTMLEscapeString(msg)
- // we can safely assume that it will not return any error, since there
- // shouldn't be any special HTML.
- fullMessage, err := markup.PostProcessCommitMessage(markup.NewRenderContext(ut.ctx).WithMetas(metas), cleanMsg)
+ // we can safely assume that it will not return any error, since there shouldn't be any special HTML.
+ // "repo" can be nil when rendering commit messages for deleted repositories in a user's dashboard feed.
+ fullMessage, err := markup.PostProcessCommitMessage(renderhelper.NewRenderContextRepoComment(ut.ctx, repo), cleanMsg)
if err != nil {
log.Error("PostProcessCommitMessage: %v", err)
return ""
}
msgLines := strings.Split(strings.TrimSpace(fullMessage), "\n")
if len(msgLines) == 0 {
- return template.HTML("")
+ return ""
}
return renderCodeBlock(template.HTML(msgLines[0]))
}
// RenderCommitMessageLinkSubject renders commit message as a XSS-safe link to
// the provided default url, handling for special links without email to links.
-func (ut *RenderUtils) RenderCommitMessageLinkSubject(msg, urlDefault string, metas map[string]string) template.HTML {
+func (ut *RenderUtils) RenderCommitMessageLinkSubject(msg, urlDefault string, repo *repo.Repository) template.HTML {
msgLine := strings.TrimLeftFunc(msg, unicode.IsSpace)
lineEnd := strings.IndexByte(msgLine, '\n')
if lineEnd > 0 {
@@ -63,9 +65,8 @@ func (ut *RenderUtils) RenderCommitMessageLinkSubject(msg, urlDefault string, me
return ""
}
- // we can safely assume that it will not return any error, since there
- // shouldn't be any special HTML.
- renderedMessage, err := markup.PostProcessCommitMessageSubject(markup.NewRenderContext(ut.ctx).WithMetas(metas), urlDefault, template.HTMLEscapeString(msgLine))
+ // we can safely assume that it will not return any error, since there shouldn't be any special HTML.
+ renderedMessage, err := markup.PostProcessCommitMessageSubject(renderhelper.NewRenderContextRepoComment(ut.ctx, repo), urlDefault, template.HTMLEscapeString(msgLine))
if err != nil {
log.Error("PostProcessCommitMessageSubject: %v", err)
return ""
@@ -74,7 +75,7 @@ func (ut *RenderUtils) RenderCommitMessageLinkSubject(msg, urlDefault string, me
}
// RenderCommitBody extracts the body of a commit message without its title.
-func (ut *RenderUtils) RenderCommitBody(msg string, metas map[string]string) template.HTML {
+func (ut *RenderUtils) RenderCommitBody(msg string, repo *repo.Repository) template.HTML {
msgLine := strings.TrimSpace(msg)
lineEnd := strings.IndexByte(msgLine, '\n')
if lineEnd > 0 {
@@ -87,7 +88,7 @@ func (ut *RenderUtils) RenderCommitBody(msg string, metas map[string]string) tem
return ""
}
- renderedMessage, err := markup.PostProcessCommitMessage(markup.NewRenderContext(ut.ctx).WithMetas(metas), template.HTMLEscapeString(msgLine))
+ renderedMessage, err := markup.PostProcessCommitMessage(renderhelper.NewRenderContextRepoComment(ut.ctx, repo), template.HTMLEscapeString(msgLine))
if err != nil {
log.Error("PostProcessCommitMessage: %v", err)
return ""
@@ -105,8 +106,8 @@ func renderCodeBlock(htmlEscapedTextToRender template.HTML) template.HTML {
}
// RenderIssueTitle renders issue/pull title with defined post processors
-func (ut *RenderUtils) RenderIssueTitle(text string, metas map[string]string) template.HTML {
- renderedText, err := markup.PostProcessIssueTitle(markup.NewRenderContext(ut.ctx).WithMetas(metas), template.HTMLEscapeString(text))
+func (ut *RenderUtils) RenderIssueTitle(text string, repo *repo.Repository) template.HTML {
+ renderedText, err := markup.PostProcessIssueTitle(renderhelper.NewRenderContextRepoComment(ut.ctx, repo), template.HTMLEscapeString(text))
if err != nil {
log.Error("PostProcessIssueTitle: %v", err)
return ""
@@ -121,8 +122,23 @@ func (ut *RenderUtils) RenderIssueSimpleTitle(text string) template.HTML {
return ret
}
-// RenderLabel renders a label
+func (ut *RenderUtils) RenderLabelWithLink(label *issues_model.Label, link any) template.HTML {
+ var attrHref template.HTML
+ switch link.(type) {
+ case template.URL, string:
+ attrHref = htmlutil.HTMLFormat(`href="%s"`, link)
+ default:
+ panic(fmt.Sprintf("unexpected type %T for link", link))
+ }
+ return ut.renderLabelWithTag(label, "a", attrHref)
+}
+
func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML {
+ return ut.renderLabelWithTag(label, "span", "")
+}
+
+// RenderLabel renders a label
+func (ut *RenderUtils) renderLabelWithTag(label *issues_model.Label, tagName, tagAttrs template.HTML) template.HTML {
locale := ut.ctx.Value(translation.ContextKey).(translation.Locale)
var extraCSSClasses string
textColor := util.ContrastColor(label.Color)
@@ -136,8 +152,8 @@ func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML {
if labelScope == "" {
// Regular label
- return htmlutil.HTMLFormat(`<div class="ui label %s" style="color: %s !important; background-color: %s !important;" data-tooltip-content title="%s">%s</div>`,
- extraCSSClasses, textColor, label.Color, descriptionText, ut.RenderEmoji(label.Name))
+ return htmlutil.HTMLFormat(`<%s %s class="ui label %s" style="color: %s !important; background-color: %s !important;" data-tooltip-content title="%s"><span class="gt-ellipsis">%s</span></%s>`,
+ tagName, tagAttrs, extraCSSClasses, textColor, label.Color, descriptionText, ut.RenderEmoji(label.Name), tagName)
}
// Scoped label
@@ -151,7 +167,7 @@ func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML {
// Ensure we add the same amount of contrast also near 0 and 1.
darken := contrast + math.Max(luminance+contrast-1.0, 0.0)
lighten := contrast + math.Max(contrast-luminance, 0.0)
- // Compute factor to keep RGB values proportional.
+ // Compute the factor to keep RGB values proportional.
darkenFactor := math.Max(luminance-darken, 0.0) / math.Max(luminance, 1.0/255.0)
lightenFactor := math.Min(luminance+lighten, 1.0) / math.Max(luminance, 1.0/255.0)
@@ -170,13 +186,31 @@ func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML {
itemColor := "#" + hex.EncodeToString(itemBytes)
scopeColor := "#" + hex.EncodeToString(scopeBytes)
- return htmlutil.HTMLFormat(`<span class="ui label %s scope-parent" data-tooltip-content title="%s">`+
+ if label.ExclusiveOrder > 0 {
+ // <scope> | <label> | <order>
+ return htmlutil.HTMLFormat(`<%s %s class="ui label %s scope-parent" data-tooltip-content title="%s">`+
+ `<div class="ui label scope-left" style="color: %s !important; background-color: %s !important">%s</div>`+
+ `<div class="ui label scope-middle" style="color: %s !important; background-color: %s !important">%s</div>`+
+ `<div class="ui label scope-right">%d</div>`+
+ `</%s>`,
+ tagName, tagAttrs,
+ extraCSSClasses, descriptionText,
+ textColor, scopeColor, scopeHTML,
+ textColor, itemColor, itemHTML,
+ label.ExclusiveOrder,
+ tagName)
+ }
+
+ // <scope> | <label>
+ return htmlutil.HTMLFormat(`<%s %s class="ui label %s scope-parent" data-tooltip-content title="%s">`+
`<div class="ui label scope-left" style="color: %s !important; background-color: %s !important">%s</div>`+
`<div class="ui label scope-right" style="color: %s !important; background-color: %s !important">%s</div>`+
- `</span>`,
+ `</%s>`,
+ tagName, tagAttrs,
extraCSSClasses, descriptionText,
textColor, scopeColor, scopeHTML,
- textColor, itemColor, itemHTML)
+ textColor, itemColor, itemHTML,
+ tagName)
}
// RenderEmoji renders html text with emoji post processors
@@ -202,7 +236,7 @@ func reactionToEmoji(reaction string) template.HTML {
return template.HTML(fmt.Sprintf(`<img alt=":%s:" src="%s/assets/img/emoji/%s.png"></img>`, reaction, setting.StaticURLPrefix, url.PathEscape(reaction)))
}
-func (ut *RenderUtils) MarkdownToHtml(input string) template.HTML { //nolint:revive
+func (ut *RenderUtils) MarkdownToHtml(input string) template.HTML { //nolint:revive // variable naming triggers on Html, wants HTML
output, err := markdown.RenderString(markup.NewRenderContext(ut.ctx).WithMetas(markup.ComposeSimpleDocumentMetas()), input)
if err != nil {
log.Error("RenderString: %v", err)
@@ -219,7 +253,8 @@ func (ut *RenderUtils) RenderLabels(labels []*issues_model.Label, repoLink strin
if label == nil {
continue
}
- htmlCode += fmt.Sprintf(`<a href="%s?labels=%d">%s</a>`, baseLink, label.ID, ut.RenderLabel(label))
+ link := fmt.Sprintf("%s?labels=%d", baseLink, label.ID)
+ htmlCode += string(ut.RenderLabelWithLink(label, template.URL(link)))
}
htmlCode += "</span>"
return template.HTML(htmlCode)
diff --git a/modules/templates/util_render_legacy.go b/modules/templates/util_render_legacy.go
deleted file mode 100644
index 994f2fa064..0000000000
--- a/modules/templates/util_render_legacy.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2024 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package templates
-
-import (
- "context"
- "html/template"
-
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/translation"
-)
-
-func renderEmojiLegacy(ctx context.Context, text string) template.HTML {
- panicIfDevOrTesting()
- return NewRenderUtils(ctx).RenderEmoji(text)
-}
-
-func renderLabelLegacy(ctx context.Context, locale translation.Locale, label *issues_model.Label) template.HTML {
- panicIfDevOrTesting()
- return NewRenderUtils(ctx).RenderLabel(label)
-}
-
-func renderLabelsLegacy(ctx context.Context, locale translation.Locale, labels []*issues_model.Label, repoLink string, issue *issues_model.Issue) template.HTML {
- panicIfDevOrTesting()
- return NewRenderUtils(ctx).RenderLabels(labels, repoLink, issue)
-}
-
-func renderMarkdownToHtmlLegacy(ctx context.Context, input string) template.HTML { //nolint:revive
- panicIfDevOrTesting()
- return NewRenderUtils(ctx).MarkdownToHtml(input)
-}
-
-func renderCommitMessageLegacy(ctx context.Context, msg string, metas map[string]string) template.HTML {
- panicIfDevOrTesting()
- return NewRenderUtils(ctx).RenderCommitMessage(msg, metas)
-}
-
-func renderCommitMessageLinkSubjectLegacy(ctx context.Context, msg, urlDefault string, metas map[string]string) template.HTML {
- panicIfDevOrTesting()
- return NewRenderUtils(ctx).RenderCommitMessageLinkSubject(msg, urlDefault, metas)
-}
-
-func renderIssueTitleLegacy(ctx context.Context, text string, metas map[string]string) template.HTML {
- panicIfDevOrTesting()
- return NewRenderUtils(ctx).RenderIssueTitle(text, metas)
-}
-
-func renderCommitBodyLegacy(ctx context.Context, msg string, metas map[string]string) template.HTML {
- panicIfDevOrTesting()
- return NewRenderUtils(ctx).RenderCommitBody(msg, metas)
-}
diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go
index 80094ab26e..5c37f084df 100644
--- a/modules/templates/util_render_test.go
+++ b/modules/templates/util_render_test.go
@@ -11,10 +11,11 @@ import (
"testing"
"code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/models/repo"
+ user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/modules/reqctx"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/translation"
@@ -46,19 +47,8 @@ mail@domain.com
return strings.ReplaceAll(s, "<SPACE>", " ")
}
-var testMetas = map[string]string{
- "user": "user13",
- "repo": "repo11",
- "repoPath": "../../tests/gitea-repositories-meta/user13/repo11.git/",
- "markdownLineBreakStyle": "comment",
- "markupAllowShortIssuePattern": "true",
-}
-
func TestMain(m *testing.M) {
- unittest.InitSettings()
- if err := git.InitSimple(context.Background()); err != nil {
- log.Fatal("git init failed, err: %v", err)
- }
+ setting.Markdown.RenderOptionsComment.ShortIssuePattern = true
markup.Init(&markup.RenderHelperFuncs{
IsUsernameMentionable: func(ctx context.Context, username string) bool {
return username == "mention-user"
@@ -67,52 +57,58 @@ func TestMain(m *testing.M) {
os.Exit(m.Run())
}
-func newTestRenderUtils() *RenderUtils {
- ctx := context.Background()
- ctx = context.WithValue(ctx, translation.ContextKey, &translation.MockLocale{})
+func newTestRenderUtils(t *testing.T) *RenderUtils {
+ ctx := reqctx.NewRequestContextForTest(t.Context())
+ ctx.SetContextValue(translation.ContextKey, &translation.MockLocale{})
return NewRenderUtils(ctx)
}
-func TestRenderCommitBody(t *testing.T) {
- defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
- type args struct {
- msg string
+func TestRenderRepoComment(t *testing.T) {
+ mockRepo := &repo.Repository{
+ ID: 1, OwnerName: "user13", Name: "repo11",
+ Owner: &user_model.User{ID: 13, Name: "user13"},
+ Units: []*repo.RepoUnit{},
}
- tests := []struct {
- name string
- args args
- want template.HTML
- }{
- {
- name: "multiple lines",
- args: args{
- msg: "first line\nsecond line",
+ t.Run("RenderCommitBody", func(t *testing.T) {
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
+ type args struct {
+ msg string
+ }
+ tests := []struct {
+ name string
+ args args
+ want template.HTML
+ }{
+ {
+ name: "multiple lines",
+ args: args{
+ msg: "first line\nsecond line",
+ },
+ want: "second line",
},
- want: "second line",
- },
- {
- name: "multiple lines with leading newlines",
- args: args{
- msg: "\n\n\n\nfirst line\nsecond line",
+ {
+ name: "multiple lines with leading newlines",
+ args: args{
+ msg: "\n\n\n\nfirst line\nsecond line",
+ },
+ want: "second line",
},
- want: "second line",
- },
- {
- name: "multiple lines with trailing newlines",
- args: args{
- msg: "first line\nsecond line\n\n\n",
+ {
+ name: "multiple lines with trailing newlines",
+ args: args{
+ msg: "first line\nsecond line\n\n\n",
+ },
+ want: "second line",
},
- want: "second line",
- },
- }
- ut := newTestRenderUtils()
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- assert.Equalf(t, tt.want, ut.RenderCommitBody(tt.args.msg, nil), "RenderCommitBody(%v, %v)", tt.args.msg, nil)
- })
- }
-
- expected := `/just/a/path.bin
+ }
+ ut := newTestRenderUtils(t)
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.Equalf(t, tt.want, ut.RenderCommitBody(tt.args.msg, mockRepo), "RenderCommitBody(%v, %v)", tt.args.msg, nil)
+ })
+ }
+
+ expected := `/just/a/path.bin
<a href="https://example.com/file.bin">https://example.com/file.bin</a>
[local link](file.bin)
[remote link](<a href="https://example.com">https://example.com</a>)
@@ -122,31 +118,31 @@ func TestRenderCommitBody(t *testing.T) {
![remote image](<a href="https://example.com/image.jpg">https://example.com/image.jpg</a>)
[[local image|image.jpg]]
[[remote link|<a href="https://example.com/image.jpg">https://example.com/image.jpg</a>]]
-<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" class="compare"><code class="nohighlight">88fc37a3c0...12fc37a3c0 (hash)</code></a>
+<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" class="compare"><code>88fc37a3c0...12fc37a3c0 (hash)</code></a>
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
-<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" class="commit"><code class="nohighlight">88fc37a3c0</code></a>
+<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" class="commit"><code>88fc37a3c0</code></a>
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
<span class="emoji" aria-label="thumbs up">ðŸ‘</span>
<a href="mailto:mail@domain.com">mail@domain.com</a>
<a href="/mention-user">@mention-user</a> test
<a href="/user13/repo11/issues/123" class="ref-issue">#123</a>
space`
- assert.EqualValues(t, expected, string(newTestRenderUtils().RenderCommitBody(testInput(), testMetas)))
-}
+ assert.Equal(t, expected, string(newTestRenderUtils(t).RenderCommitBody(testInput(), mockRepo)))
+ })
-func TestRenderCommitMessage(t *testing.T) {
- expected := `space <a href="/mention-user" data-markdown-generated-content="">@mention-user</a> `
- assert.EqualValues(t, expected, newTestRenderUtils().RenderCommitMessage(testInput(), testMetas))
-}
+ t.Run("RenderCommitMessage", func(t *testing.T) {
+ expected := `space <a href="/mention-user" data-markdown-generated-content="">@mention-user</a> `
+ assert.EqualValues(t, expected, newTestRenderUtils(t).RenderCommitMessage(testInput(), mockRepo))
+ })
-func TestRenderCommitMessageLinkSubject(t *testing.T) {
- expected := `<a href="https://example.com/link" class="muted">space </a><a href="/mention-user" data-markdown-generated-content="">@mention-user</a>`
- assert.EqualValues(t, expected, newTestRenderUtils().RenderCommitMessageLinkSubject(testInput(), "https://example.com/link", testMetas))
-}
+ t.Run("RenderCommitMessageLinkSubject", func(t *testing.T) {
+ expected := `<a href="https://example.com/link" class="muted">space </a><a href="/mention-user" data-markdown-generated-content="">@mention-user</a>`
+ assert.EqualValues(t, expected, newTestRenderUtils(t).RenderCommitMessageLinkSubject(testInput(), "https://example.com/link", mockRepo))
+ })
-func TestRenderIssueTitle(t *testing.T) {
- defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
- expected := ` space @mention-user<SPACE><SPACE>
+ t.Run("RenderIssueTitle", func(t *testing.T) {
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
+ expected := ` space @mention-user<SPACE><SPACE>
/just/a/path.bin
https://example.com/file.bin
[local link](file.bin)
@@ -167,8 +163,9 @@ mail@domain.com
<a href="/user13/repo11/issues/123" class="ref-issue">#123</a>
space<SPACE><SPACE>
`
- expected = strings.ReplaceAll(expected, "<SPACE>", " ")
- assert.EqualValues(t, expected, string(newTestRenderUtils().RenderIssueTitle(testInput(), testMetas)))
+ expected = strings.ReplaceAll(expected, "<SPACE>", " ")
+ assert.Equal(t, expected, string(newTestRenderUtils(t).RenderIssueTitle(testInput(), mockRepo)))
+ })
}
func TestRenderMarkdownToHtml(t *testing.T) {
@@ -194,11 +191,11 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
#123
space</p>
`
- assert.Equal(t, expected, string(newTestRenderUtils().MarkdownToHtml(testInput())))
+ assert.Equal(t, expected, string(newTestRenderUtils(t).MarkdownToHtml(testInput())))
}
func TestRenderLabels(t *testing.T) {
- ut := newTestRenderUtils()
+ ut := newTestRenderUtils(t)
label := &issues.Label{ID: 123, Name: "label-name", Color: "label-color"}
issue := &issues.Issue{}
expected := `/owner/repo/issues?labels=123`
@@ -208,10 +205,21 @@ func TestRenderLabels(t *testing.T) {
issue = &issues.Issue{IsPull: true}
expected = `/owner/repo/pulls?labels=123`
assert.Contains(t, ut.RenderLabels([]*issues.Label{label}, "/owner/repo", issue), expected)
+
+ expectedLabel := `<a href="&lt;&gt;" class="ui label " style="color: #fff !important; background-color: label-color !important;" data-tooltip-content title=""><span class="gt-ellipsis">label-name</span></a>`
+ assert.Equal(t, expectedLabel, string(ut.RenderLabelWithLink(label, "<>")))
+ assert.Equal(t, expectedLabel, string(ut.RenderLabelWithLink(label, template.URL("<>"))))
+
+ label = &issues.Label{ID: 123, Name: "</>", Exclusive: true}
+ expectedLabel = `<a href="" class="ui label scope-parent" data-tooltip-content title=""><div class="ui label scope-left" style="color: #fff !important; background-color: #000000 !important">&lt;</div><div class="ui label scope-right" style="color: #fff !important; background-color: #000000 !important">&gt;</div></a>`
+ assert.Equal(t, expectedLabel, string(ut.RenderLabelWithLink(label, "")))
+ label = &issues.Label{ID: 123, Name: "</>", Exclusive: true, ExclusiveOrder: 1}
+ expectedLabel = `<a href="" class="ui label scope-parent" data-tooltip-content title=""><div class="ui label scope-left" style="color: #fff !important; background-color: #000000 !important">&lt;</div><div class="ui label scope-middle" style="color: #fff !important; background-color: #000000 !important">&gt;</div><div class="ui label scope-right">1</div></a>`
+ assert.Equal(t, expectedLabel, string(ut.RenderLabelWithLink(label, "")))
}
func TestUserMention(t *testing.T) {
markup.RenderBehaviorForTesting.DisableAdditionalAttributes = true
- rendered := newTestRenderUtils().MarkdownToHtml("@no-such-user @mention-user @mention-user")
- assert.EqualValues(t, `<p>@no-such-user <a href="/mention-user" rel="nofollow">@mention-user</a> <a href="/mention-user" rel="nofollow">@mention-user</a></p>`, strings.TrimSpace(string(rendered)))
+ rendered := newTestRenderUtils(t).MarkdownToHtml("@no-such-user @mention-user @mention-user")
+ assert.Equal(t, `<p>@no-such-user <a href="/mention-user" rel="nofollow">@mention-user</a> <a href="/mention-user" rel="nofollow">@mention-user</a></p>`, strings.TrimSpace(string(rendered)))
}
diff --git a/modules/templates/util_test.go b/modules/templates/util_test.go
index febaf7fa88..a6448a6ff2 100644
--- a/modules/templates/util_test.go
+++ b/modules/templates/util_test.go
@@ -28,7 +28,7 @@ func TestDict(t *testing.T) {
for _, c := range cases {
got, err := dict(c.args...)
if assert.NoError(t, err) {
- assert.EqualValues(t, c.want, got)
+ assert.Equal(t, c.want, got)
}
}
diff --git a/modules/templates/vars/vars.go b/modules/templates/vars/vars.go
index cc9d0e976f..500078d4b8 100644
--- a/modules/templates/vars/vars.go
+++ b/modules/templates/vars/vars.go
@@ -16,7 +16,7 @@ type ErrWrongSyntax struct {
}
func (err ErrWrongSyntax) Error() string {
- return fmt.Sprintf("wrong syntax found in %s", err.Template)
+ return "wrong syntax found in " + err.Template
}
// ErrVarMissing represents an error that no matched variable
diff --git a/modules/templates/vars/vars_test.go b/modules/templates/vars/vars_test.go
index 8f421d9e4b..9b48167237 100644
--- a/modules/templates/vars/vars_test.go
+++ b/modules/templates/vars/vars_test.go
@@ -60,7 +60,7 @@ func TestExpandVars(t *testing.T) {
for _, kase := range kases {
t.Run(kase.tmpl, func(t *testing.T) {
res, err := Expand(kase.tmpl, kase.data)
- assert.EqualValues(t, kase.out, res)
+ assert.Equal(t, kase.out, res)
if kase.error {
assert.Error(t, err)
} else {
diff --git a/modules/test/logchecker.go b/modules/test/logchecker.go
index 7bf234f560..829f735c7c 100644
--- a/modules/test/logchecker.go
+++ b/modules/test/logchecker.go
@@ -5,7 +5,7 @@ package test
import (
"context"
- "fmt"
+ "strconv"
"strings"
"sync"
"sync/atomic"
@@ -58,7 +58,7 @@ var checkerIndex int64
func NewLogChecker(namePrefix string) (logChecker *LogChecker, cancel func()) {
logger := log.GetManager().GetLogger(namePrefix)
newCheckerIndex := atomic.AddInt64(&checkerIndex, 1)
- writerName := namePrefix + "-" + fmt.Sprint(newCheckerIndex)
+ writerName := namePrefix + "-" + strconv.FormatInt(newCheckerIndex, 10)
lc := &LogChecker{}
lc.EventWriterBaseImpl = log.NewEventWriterBase(writerName, "test-log-checker", log.WriterMode{})
diff --git a/modules/test/utils.go b/modules/test/utils.go
index ec4c976388..53c6a3ed52 100644
--- a/modules/test/utils.go
+++ b/modules/test/utils.go
@@ -4,7 +4,6 @@
package test
import (
- "fmt"
"net/http"
"net/http/httptest"
"os"
@@ -18,6 +17,7 @@ import (
// RedirectURL returns the redirect URL of a http response.
// It also works for JSONRedirect: `{"redirect": "..."}`
+// FIXME: it should separate the logic of checking from header and JSON body
func RedirectURL(resp http.ResponseWriter) string {
loc := resp.Header().Get("Location")
if loc != "" {
@@ -35,6 +35,15 @@ func RedirectURL(resp http.ResponseWriter) string {
return ""
}
+func ParseJSONError(buf []byte) (ret struct {
+ ErrorMessage string `json:"errorMessage"`
+ RenderFormat string `json:"renderFormat"`
+},
+) {
+ _ = json.Unmarshal(buf, &ret)
+ return ret
+}
+
func IsNormalPageCompleted(s string) bool {
return strings.Contains(s, `<footer class="page-footer"`) && strings.Contains(s, `</html>`)
}
@@ -57,7 +66,7 @@ func SetupGiteaRoot() string {
giteaRoot = filepath.Dir(filepath.Dir(filepath.Dir(filename)))
fixturesDir := filepath.Join(giteaRoot, "models", "fixtures")
if exist, _ := util.IsDir(fixturesDir); !exist {
- panic(fmt.Sprintf("fixtures directory not found: %s", fixturesDir))
+ panic("fixtures directory not found: " + fixturesDir)
}
_ = os.Setenv("GITEA_ROOT", giteaRoot)
return giteaRoot
diff --git a/modules/testlogger/testlogger.go b/modules/testlogger/testlogger.go
index 2fbfce6b03..60e281d403 100644
--- a/modules/testlogger/testlogger.go
+++ b/modules/testlogger/testlogger.go
@@ -4,7 +4,6 @@
package testlogger
import (
- "context"
"fmt"
"os"
"runtime"
@@ -93,7 +92,7 @@ func (w *testLoggerWriterCloser) Reset() {
// Printf takes a format and args and prints the string to os.Stdout
func Printf(format string, args ...any) {
if !log.CanColorStdout {
- for i := 0; i < len(args); i++ {
+ for i := range args {
if c, ok := args[i].(*log.ColoredValue); ok {
args[i] = c.Value()
}
@@ -131,7 +130,7 @@ func PrintCurrentTest(t testing.TB, skip ...int) func() {
slowFlushChecker := time.AfterFunc(TestSlowFlush, func() {
Printf("+++ %s ... still flushing after %v ...\n", log.NewColoredValue(t.Name(), log.Bold, log.FgRed), TestSlowFlush)
})
- if err := queue.GetManager().FlushAll(context.Background(), -1); err != nil {
+ if err := queue.GetManager().FlushAll(t.Context(), -1); err != nil {
t.Errorf("Flushing queues failed with error %v", err)
}
slowFlushChecker.Stop()
diff --git a/modules/timeutil/executable.go b/modules/timeutil/executable.go
deleted file mode 100644
index 57ae8b2a9d..0000000000
--- a/modules/timeutil/executable.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package timeutil
-
-import (
- "os"
- "path/filepath"
- "sync"
- "time"
-
- "code.gitea.io/gitea/modules/log"
-)
-
-var (
- executablModTime = time.Now()
- executablModTimeOnce sync.Once
-)
-
-// GetExecutableModTime get executable file modified time of current process.
-func GetExecutableModTime() time.Time {
- executablModTimeOnce.Do(func() {
- exePath, err := os.Executable()
- if err != nil {
- log.Error("os.Executable: %v", err)
- return
- }
-
- exePath, err = filepath.Abs(exePath)
- if err != nil {
- log.Error("filepath.Abs: %v", err)
- return
- }
-
- exePath, err = filepath.EvalSymlinks(exePath)
- if err != nil {
- log.Error("filepath.EvalSymlinks: %v", err)
- return
- }
-
- st, err := os.Stat(exePath)
- if err != nil {
- log.Error("os.Stat: %v", err)
- return
- }
-
- executablModTime = st.ModTime()
- })
- return executablModTime
-}
diff --git a/modules/translation/i18n/errors.go b/modules/translation/i18n/errors.go
deleted file mode 100644
index 7f64ccf908..0000000000
--- a/modules/translation/i18n/errors.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package i18n
-
-import (
- "code.gitea.io/gitea/modules/util"
-)
-
-var (
- ErrLocaleAlreadyExist = util.SilentWrap{Message: "lang already exists", Err: util.ErrAlreadyExist}
- ErrUncertainArguments = util.SilentWrap{Message: "arguments to i18n should not contain uncertain slices", Err: util.ErrInvalidArgument}
-)
diff --git a/modules/translation/i18n/format.go b/modules/translation/i18n/format.go
index e5e221831f..450509333f 100644
--- a/modules/translation/i18n/format.go
+++ b/modules/translation/i18n/format.go
@@ -4,6 +4,7 @@
package i18n
import (
+ "errors"
"fmt"
"reflect"
)
@@ -30,7 +31,7 @@ func Format(format string, args ...any) (msg string, err error) {
fmtArgs = append(fmtArgs, val.Index(i).Interface())
}
} else {
- err = ErrUncertainArguments
+ err = errors.New("arguments to i18n should not contain uncertain slices")
break
}
} else {
diff --git a/modules/translation/i18n/localestore.go b/modules/translation/i18n/localestore.go
index b422996984..4f1ae7e13d 100644
--- a/modules/translation/i18n/localestore.go
+++ b/modules/translation/i18n/localestore.go
@@ -4,6 +4,7 @@
package i18n
import (
+ "errors"
"fmt"
"html/template"
"slices"
@@ -41,7 +42,7 @@ func NewLocaleStore() LocaleStore {
// AddLocaleByIni adds locale by ini into the store
func (store *localeStore) AddLocaleByIni(langName, langDesc string, source, moreSource []byte) error {
if _, ok := store.localeMap[langName]; ok {
- return ErrLocaleAlreadyExist
+ return errors.New("lang has already been added")
}
store.langNames = append(store.langNames, langName)
diff --git a/modules/translation/translation_test.go b/modules/translation/translation_test.go
index 464aa32661..87df9eb825 100644
--- a/modules/translation/translation_test.go
+++ b/modules/translation/translation_test.go
@@ -20,13 +20,13 @@ func TestPrettyNumber(t *testing.T) {
allLangMap["id-ID"] = &LangType{Lang: "id-ID", Name: "Bahasa Indonesia"}
l := NewLocale("id-ID")
- assert.EqualValues(t, "1.000.000", l.PrettyNumber(1000000))
- assert.EqualValues(t, "1.000.000,1", l.PrettyNumber(1000000.1))
- assert.EqualValues(t, "1.000.000", l.PrettyNumber("1000000"))
- assert.EqualValues(t, "1.000.000", l.PrettyNumber("1000000.0"))
- assert.EqualValues(t, "1.000.000,1", l.PrettyNumber("1000000.1"))
+ assert.Equal(t, "1.000.000", l.PrettyNumber(1000000))
+ assert.Equal(t, "1.000.000,1", l.PrettyNumber(1000000.1))
+ assert.Equal(t, "1.000.000", l.PrettyNumber("1000000"))
+ assert.Equal(t, "1.000.000", l.PrettyNumber("1000000.0"))
+ assert.Equal(t, "1.000.000,1", l.PrettyNumber("1000000.1"))
l = NewLocale("nosuch")
- assert.EqualValues(t, "1,000,000", l.PrettyNumber(1000000))
- assert.EqualValues(t, "1,000,000.1", l.PrettyNumber(1000000.1))
+ assert.Equal(t, "1,000,000", l.PrettyNumber(1000000))
+ assert.Equal(t, "1,000,000.1", l.PrettyNumber(1000000.1))
}
diff --git a/modules/typesniffer/typesniffer.go b/modules/typesniffer/typesniffer.go
index 8cb3d278ce..2e8d9c4a1e 100644
--- a/modules/typesniffer/typesniffer.go
+++ b/modules/typesniffer/typesniffer.go
@@ -6,18 +6,14 @@ package typesniffer
import (
"bytes"
"encoding/binary"
- "fmt"
- "io"
"net/http"
"regexp"
"slices"
"strings"
-
- "code.gitea.io/gitea/modules/util"
+ "sync"
)
-// Use at most this many bytes to determine Content Type.
-const sniffLen = 1024
+const SniffContentSize = 1024
const (
MimeTypeImageSvg = "image/svg+xml"
@@ -26,22 +22,30 @@ const (
MimeTypeApplicationOctetStream = "application/octet-stream"
)
-var (
- svgComment = regexp.MustCompile(`(?s)<!--.*?-->`)
- svgTagRegex = regexp.MustCompile(`(?si)\A\s*(?:(<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg\b`)
- svgTagInXMLRegex = regexp.MustCompile(`(?si)\A<\?xml\b.*?\?>\s*(?:(<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg\b`)
-)
-
-// SniffedType contains information about a blobs type.
+var globalVars = sync.OnceValue(func() (ret struct {
+ svgComment, svgTagRegex, svgTagInXMLRegex *regexp.Regexp
+},
+) {
+ ret.svgComment = regexp.MustCompile(`(?s)<!--.*?-->`)
+ ret.svgTagRegex = regexp.MustCompile(`(?si)\A\s*(?:(<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg\b`)
+ ret.svgTagInXMLRegex = regexp.MustCompile(`(?si)\A<\?xml\b.*?\?>\s*(?:(<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg\b`)
+ return ret
+})
+
+// SniffedType contains information about a blob's type.
type SniffedType struct {
contentType string
}
-// IsText etects if content format is plain text.
+// IsText detects if the content format is text family, including text/plain, text/html, text/css, etc.
func (ct SniffedType) IsText() bool {
return strings.Contains(ct.contentType, "text/")
}
+func (ct SniffedType) IsTextPlain() bool {
+ return strings.Contains(ct.contentType, "text/plain")
+}
+
// IsImage detects if data is an image format
func (ct SniffedType) IsImage() bool {
return strings.Contains(ct.contentType, "image/")
@@ -57,12 +61,12 @@ func (ct SniffedType) IsPDF() bool {
return strings.Contains(ct.contentType, "application/pdf")
}
-// IsVideo detects if data is an video format
+// IsVideo detects if data is a video format
func (ct SniffedType) IsVideo() bool {
return strings.Contains(ct.contentType, "video/")
}
-// IsAudio detects if data is an video format
+// IsAudio detects if data is a video format
func (ct SniffedType) IsAudio() bool {
return strings.Contains(ct.contentType, "audio/")
}
@@ -103,33 +107,34 @@ func detectFileTypeBox(data []byte) (brands []string, found bool) {
return brands, true
}
-// DetectContentType extends http.DetectContentType with more content types. Defaults to text/unknown if input is empty.
+// DetectContentType extends http.DetectContentType with more content types. Defaults to text/plain if input is empty.
func DetectContentType(data []byte) SniffedType {
if len(data) == 0 {
- return SniffedType{"text/unknown"}
+ return SniffedType{"text/plain"}
}
ct := http.DetectContentType(data)
- if len(data) > sniffLen {
- data = data[:sniffLen]
+ if len(data) > SniffContentSize {
+ data = data[:SniffContentSize]
}
+ vars := globalVars()
// SVG is unsupported by http.DetectContentType, https://github.com/golang/go/issues/15888
detectByHTML := strings.Contains(ct, "text/plain") || strings.Contains(ct, "text/html")
detectByXML := strings.Contains(ct, "text/xml")
if detectByHTML || detectByXML {
- dataProcessed := svgComment.ReplaceAll(data, nil)
+ dataProcessed := vars.svgComment.ReplaceAll(data, nil)
dataProcessed = bytes.TrimSpace(dataProcessed)
- if detectByHTML && svgTagRegex.Match(dataProcessed) ||
- detectByXML && svgTagInXMLRegex.Match(dataProcessed) {
+ if detectByHTML && vars.svgTagRegex.Match(dataProcessed) ||
+ detectByXML && vars.svgTagInXMLRegex.Match(dataProcessed) {
ct = MimeTypeImageSvg
}
}
if strings.HasPrefix(ct, "audio/") && bytes.HasPrefix(data, []byte("ID3")) {
// The MP3 detection is quite inaccurate, any content with "ID3" prefix will result in "audio/mpeg".
- // So remove the "ID3" prefix and detect again, if result is text, then it must be text content.
+ // So remove the "ID3" prefix and detect again, then if the result is "text", it must be text content.
// This works especially because audio files contain many unprintable/invalid characters like `0x00`
ct2 := http.DetectContentType(data[3:])
if strings.HasPrefix(ct2, "text/") {
@@ -155,15 +160,3 @@ func DetectContentType(data []byte) SniffedType {
}
return SniffedType{ct}
}
-
-// DetectContentTypeFromReader guesses the content type contained in the reader.
-func DetectContentTypeFromReader(r io.Reader) (SniffedType, error) {
- buf := make([]byte, sniffLen)
- n, err := util.ReadAtMost(r, buf)
- if err != nil {
- return SniffedType{}, fmt.Errorf("DetectContentTypeFromReader io error: %w", err)
- }
- buf = buf[:n]
-
- return DetectContentType(buf), nil
-}
diff --git a/modules/typesniffer/typesniffer_test.go b/modules/typesniffer/typesniffer_test.go
index 3e5db3308b..a0c824b912 100644
--- a/modules/typesniffer/typesniffer_test.go
+++ b/modules/typesniffer/typesniffer_test.go
@@ -4,7 +4,6 @@
package typesniffer
import (
- "bytes"
"encoding/base64"
"encoding/hex"
"strings"
@@ -17,7 +16,7 @@ func TestDetectContentTypeLongerThanSniffLen(t *testing.T) {
// Pre-condition: Shorter than sniffLen detects SVG.
assert.Equal(t, "image/svg+xml", DetectContentType([]byte(`<!-- Comment --><svg></svg>`)).contentType)
// Longer than sniffLen detects something else.
- assert.NotEqual(t, "image/svg+xml", DetectContentType([]byte(`<!-- `+strings.Repeat("x", sniffLen)+` --><svg></svg>`)).contentType)
+ assert.NotEqual(t, "image/svg+xml", DetectContentType([]byte(`<!-- `+strings.Repeat("x", SniffContentSize)+` --><svg></svg>`)).contentType)
}
func TestIsTextFile(t *testing.T) {
@@ -116,22 +115,13 @@ func TestIsAudio(t *testing.T) {
assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ..."+"🌛"[0:2])).IsText()) // test ID3 tag with incomplete UTF8 char
}
-func TestDetectContentTypeFromReader(t *testing.T) {
- mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl")
- st, err := DetectContentTypeFromReader(bytes.NewReader(mp3))
- assert.NoError(t, err)
- assert.True(t, st.IsAudio())
-}
-
func TestDetectContentTypeOgg(t *testing.T) {
oggAudio, _ := hex.DecodeString("4f67675300020000000000000000352f0000000000007dc39163011e01766f72626973000000000244ac0000000000000071020000000000b8014f6767530000")
- st, err := DetectContentTypeFromReader(bytes.NewReader(oggAudio))
- assert.NoError(t, err)
+ st := DetectContentType(oggAudio)
assert.True(t, st.IsAudio())
oggVideo, _ := hex.DecodeString("4f676753000200000000000000007d9747ef000000009b59daf3012a807468656f7261030201001e00110001e000010e00020000001e00000001000001000001")
- st, err = DetectContentTypeFromReader(bytes.NewReader(oggVideo))
- assert.NoError(t, err)
+ st = DetectContentType(oggVideo)
assert.True(t, st.IsVideo())
}
diff --git a/modules/updatechecker/update_checker.go b/modules/updatechecker/update_checker.go
index 3c1e05d060..f0686c0f78 100644
--- a/modules/updatechecker/update_checker.go
+++ b/modules/updatechecker/update_checker.go
@@ -34,7 +34,7 @@ func GiteaUpdateChecker(httpEndpoint string) error {
},
}
- req, err := http.NewRequest("GET", httpEndpoint, nil)
+ req, err := http.NewRequest(http.MethodGet, httpEndpoint, nil)
if err != nil {
return err
}
diff --git a/modules/user/user_test.go b/modules/user/user_test.go
index 372a675d34..d6b3911ca6 100644
--- a/modules/user/user_test.go
+++ b/modules/user/user_test.go
@@ -8,6 +8,9 @@ import (
"runtime"
"strings"
"testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func getWhoamiOutput() (string, error) {
@@ -20,24 +23,19 @@ func getWhoamiOutput() (string, error) {
func TestCurrentUsername(t *testing.T) {
user := CurrentUsername()
- if len(user) == 0 {
- t.Errorf("expected non-empty user, got: %s", user)
- }
+ require.NotEmpty(t, user)
+
// Windows whoami is weird, so just skip remaining tests
if runtime.GOOS == "windows" {
t.Skip("skipped test because of weird whoami on Windows")
}
whoami, err := getWhoamiOutput()
- if err != nil {
- t.Errorf("failed to run whoami to test current user: %f", err)
- }
+ require.NoError(t, err)
+
user = CurrentUsername()
- if user != whoami {
- t.Errorf("expected %s as user, got: %s", whoami, user)
- }
+ assert.Equal(t, whoami, user)
+
t.Setenv("USER", "spoofed")
user = CurrentUsername()
- if user != whoami {
- t.Errorf("expected %s as user, got: %s", whoami, user)
- }
+ assert.Equal(t, whoami, user)
}
diff --git a/modules/util/error.go b/modules/util/error.go
index 0f3597147c..6b2721618e 100644
--- a/modules/util/error.go
+++ b/modules/util/error.go
@@ -10,56 +10,88 @@ import (
// Common Errors forming the base of our error system
//
-// Many Errors returned by Gitea can be tested against these errors
-// using errors.Is.
+// Many Errors returned by Gitea can be tested against these errors using "errors.Is".
var (
- ErrInvalidArgument = errors.New("invalid argument")
- ErrPermissionDenied = errors.New("permission denied")
- ErrAlreadyExist = errors.New("resource already exists")
- ErrNotExist = errors.New("resource does not exist")
+ ErrInvalidArgument = errors.New("invalid argument") // also implies HTTP 400
+ ErrPermissionDenied = errors.New("permission denied") // also implies HTTP 403
+ ErrNotExist = errors.New("resource does not exist") // also implies HTTP 404
+ ErrAlreadyExist = errors.New("resource already exists") // also implies HTTP 409
+
+ // ErrUnprocessableContent implies HTTP 422, the syntax of the request content is correct,
+ // but the server is unable to process the contained instructions
+ ErrUnprocessableContent = errors.New("unprocessable content")
)
-// SilentWrap provides a simple wrapper for a wrapped error where the wrapped error message plays no part in the error message
+// errorWrapper provides a simple wrapper for a wrapped error where the wrapped error message plays no part in the error message
// Especially useful for "untyped" errors created with "errors.New(…)" that can be classified as 'invalid argument', 'permission denied', 'exists already', or 'does not exist'
-type SilentWrap struct {
+type errorWrapper struct {
Message string
Err error
}
// Error returns the message
-func (w SilentWrap) Error() string {
+func (w errorWrapper) Error() string {
return w.Message
}
// Unwrap returns the underlying error
-func (w SilentWrap) Unwrap() error {
+func (w errorWrapper) Unwrap() error {
return w.Err
}
-// NewSilentWrapErrorf returns an error that formats as the given text but unwraps as the provided error
-func NewSilentWrapErrorf(unwrap error, message string, args ...any) error {
+type LocaleWrapper struct {
+ err error
+ TrKey string
+ TrArgs []any
+}
+
+// Error returns the message
+func (w LocaleWrapper) Error() string {
+ return w.err.Error()
+}
+
+// Unwrap returns the underlying error
+func (w LocaleWrapper) Unwrap() error {
+ return w.err
+}
+
+// ErrorWrap returns an error that formats as the given text but unwraps as the provided error
+func ErrorWrap(unwrap error, message string, args ...any) error {
if len(args) == 0 {
- return SilentWrap{Message: message, Err: unwrap}
+ return errorWrapper{Message: message, Err: unwrap}
}
- return SilentWrap{Message: fmt.Sprintf(message, args...), Err: unwrap}
+ return errorWrapper{Message: fmt.Sprintf(message, args...), Err: unwrap}
}
// NewInvalidArgumentErrorf returns an error that formats as the given text but unwraps as an ErrInvalidArgument
func NewInvalidArgumentErrorf(message string, args ...any) error {
- return NewSilentWrapErrorf(ErrInvalidArgument, message, args...)
+ return ErrorWrap(ErrInvalidArgument, message, args...)
}
// NewPermissionDeniedErrorf returns an error that formats as the given text but unwraps as an ErrPermissionDenied
func NewPermissionDeniedErrorf(message string, args ...any) error {
- return NewSilentWrapErrorf(ErrPermissionDenied, message, args...)
+ return ErrorWrap(ErrPermissionDenied, message, args...)
}
// NewAlreadyExistErrorf returns an error that formats as the given text but unwraps as an ErrAlreadyExist
func NewAlreadyExistErrorf(message string, args ...any) error {
- return NewSilentWrapErrorf(ErrAlreadyExist, message, args...)
+ return ErrorWrap(ErrAlreadyExist, message, args...)
}
// NewNotExistErrorf returns an error that formats as the given text but unwraps as an ErrNotExist
func NewNotExistErrorf(message string, args ...any) error {
- return NewSilentWrapErrorf(ErrNotExist, message, args...)
+ return ErrorWrap(ErrNotExist, message, args...)
+}
+
+// ErrorWrapLocale wraps an err with a translation key and arguments
+func ErrorWrapLocale(err error, trKey string, trArgs ...any) error {
+ return LocaleWrapper{err: err, TrKey: trKey, TrArgs: trArgs}
+}
+
+func ErrorAsLocale(err error) *LocaleWrapper {
+ var e LocaleWrapper
+ if errors.As(err, &e) {
+ return &e
+ }
+ return nil
}
diff --git a/modules/util/filebuffer/file_backed_buffer.go b/modules/util/filebuffer/file_backed_buffer.go
index 739543e297..0731ba30c8 100644
--- a/modules/util/filebuffer/file_backed_buffer.go
+++ b/modules/util/filebuffer/file_backed_buffer.go
@@ -7,16 +7,10 @@ import (
"bytes"
"errors"
"io"
- "math"
"os"
)
-var (
- // ErrInvalidMemorySize occurs if the memory size is not in a valid range
- ErrInvalidMemorySize = errors.New("Memory size must be greater 0 and lower math.MaxInt32")
- // ErrWriteAfterRead occurs if Write is called after a read operation
- ErrWriteAfterRead = errors.New("Write is unsupported after a read operation")
-)
+var ErrWriteAfterRead = errors.New("write is unsupported after a read operation") // occurs if Write is called after a read operation
type readAtSeeker interface {
io.ReadSeeker
@@ -30,34 +24,17 @@ type FileBackedBuffer struct {
maxMemorySize int64
size int64
buffer bytes.Buffer
+ tempDir string
file *os.File
reader readAtSeeker
}
// New creates a file backed buffer with a specific maximum memory size
-func New(maxMemorySize int) (*FileBackedBuffer, error) {
- if maxMemorySize < 0 || maxMemorySize > math.MaxInt32 {
- return nil, ErrInvalidMemorySize
- }
-
+func New(maxMemorySize int, tempDir string) *FileBackedBuffer {
return &FileBackedBuffer{
maxMemorySize: int64(maxMemorySize),
- }, nil
-}
-
-// CreateFromReader creates a file backed buffer and copies the provided reader data into it.
-func CreateFromReader(r io.Reader, maxMemorySize int) (*FileBackedBuffer, error) {
- b, err := New(maxMemorySize)
- if err != nil {
- return nil, err
+ tempDir: tempDir,
}
-
- _, err = io.Copy(b, r)
- if err != nil {
- return nil, err
- }
-
- return b, nil
}
// Write implements io.Writer
@@ -73,7 +50,7 @@ func (b *FileBackedBuffer) Write(p []byte) (int, error) {
n, err = b.file.Write(p)
} else {
if b.size+int64(len(p)) > b.maxMemorySize {
- b.file, err = os.CreateTemp("", "gitea-buffer-")
+ b.file, err = os.CreateTemp(b.tempDir, "gitea-buffer-")
if err != nil {
return 0, err
}
@@ -148,7 +125,7 @@ func (b *FileBackedBuffer) Seek(offset int64, whence int) (int64, error) {
func (b *FileBackedBuffer) Close() error {
if b.file != nil {
err := b.file.Close()
- os.Remove(b.file.Name())
+ _ = os.Remove(b.file.Name())
b.file = nil
return err
}
diff --git a/modules/util/filebuffer/file_backed_buffer_test.go b/modules/util/filebuffer/file_backed_buffer_test.go
index 16d5a1965f..3f13c6ac7b 100644
--- a/modules/util/filebuffer/file_backed_buffer_test.go
+++ b/modules/util/filebuffer/file_backed_buffer_test.go
@@ -21,7 +21,8 @@ func TestFileBackedBuffer(t *testing.T) {
}
for _, c := range cases {
- buf, err := CreateFromReader(strings.NewReader(c.Data), c.MaxMemorySize)
+ buf := New(c.MaxMemorySize, t.TempDir())
+ _, err := io.Copy(buf, strings.NewReader(c.Data))
assert.NoError(t, err)
assert.EqualValues(t, len(c.Data), buf.Size())
diff --git a/modules/util/legacy_test.go b/modules/util/legacy_test.go
index e732094c29..565fb7f284 100644
--- a/modules/util/legacy_test.go
+++ b/modules/util/legacy_test.go
@@ -17,7 +17,7 @@ import (
func TestCopyFile(t *testing.T) {
testContent := []byte("hello")
- tmpDir := os.TempDir()
+ tmpDir := t.TempDir()
now := time.Now()
srcFile := fmt.Sprintf("%s/copy-test-%d-src.txt", tmpDir, now.UnixMicro())
dstFile := fmt.Sprintf("%s/copy-test-%d-dst.txt", tmpDir, now.UnixMicro())
diff --git a/modules/util/map.go b/modules/util/map.go
new file mode 100644
index 0000000000..f307faad1f
--- /dev/null
+++ b/modules/util/map.go
@@ -0,0 +1,13 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package util
+
+func GetMapValueOrDefault[T any](m map[string]any, key string, defaultValue T) T {
+ if value, ok := m[key]; ok {
+ if v, ok := value.(T); ok {
+ return v
+ }
+ }
+ return defaultValue
+}
diff --git a/modules/util/map_test.go b/modules/util/map_test.go
new file mode 100644
index 0000000000..1a141cec88
--- /dev/null
+++ b/modules/util/map_test.go
@@ -0,0 +1,26 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package util
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGetMapValueOrDefault(t *testing.T) {
+ testMap := map[string]any{
+ "key1": "value1",
+ "key2": 42,
+ "key3": nil,
+ }
+
+ assert.Equal(t, "value1", GetMapValueOrDefault(testMap, "key1", "default"))
+ assert.Equal(t, 42, GetMapValueOrDefault(testMap, "key2", 0))
+
+ assert.Equal(t, "default", GetMapValueOrDefault(testMap, "key4", "default"))
+ assert.Equal(t, 100, GetMapValueOrDefault(testMap, "key5", 100))
+
+ assert.Equal(t, "default", GetMapValueOrDefault(testMap, "key3", "default"))
+}
diff --git a/modules/util/paginate_test.go b/modules/util/paginate_test.go
index 6e69dd19cc..3dc5095071 100644
--- a/modules/util/paginate_test.go
+++ b/modules/util/paginate_test.go
@@ -13,23 +13,23 @@ func TestPaginateSlice(t *testing.T) {
stringSlice := []string{"a", "b", "c", "d", "e"}
result, ok := PaginateSlice(stringSlice, 1, 2).([]string)
assert.True(t, ok)
- assert.EqualValues(t, []string{"a", "b"}, result)
+ assert.Equal(t, []string{"a", "b"}, result)
result, ok = PaginateSlice(stringSlice, 100, 2).([]string)
assert.True(t, ok)
- assert.EqualValues(t, []string{}, result)
+ assert.Equal(t, []string{}, result)
result, ok = PaginateSlice(stringSlice, 3, 2).([]string)
assert.True(t, ok)
- assert.EqualValues(t, []string{"e"}, result)
+ assert.Equal(t, []string{"e"}, result)
result, ok = PaginateSlice(stringSlice, 1, 0).([]string)
assert.True(t, ok)
- assert.EqualValues(t, []string{"a", "b", "c", "d", "e"}, result)
+ assert.Equal(t, []string{"a", "b", "c", "d", "e"}, result)
result, ok = PaginateSlice(stringSlice, 1, -1).([]string)
assert.True(t, ok)
- assert.EqualValues(t, []string{"a", "b", "c", "d", "e"}, result)
+ assert.Equal(t, []string{"a", "b", "c", "d", "e"}, result)
type Test struct {
Val int
@@ -38,9 +38,9 @@ func TestPaginateSlice(t *testing.T) {
testVar := []*Test{{Val: 2}, {Val: 3}, {Val: 4}}
testVar, ok = PaginateSlice(testVar, 1, 50).([]*Test)
assert.True(t, ok)
- assert.EqualValues(t, []*Test{{Val: 2}, {Val: 3}, {Val: 4}}, testVar)
+ assert.Equal(t, []*Test{{Val: 2}, {Val: 3}, {Val: 4}}, testVar)
testVar, ok = PaginateSlice(testVar, 2, 2).([]*Test)
assert.True(t, ok)
- assert.EqualValues(t, []*Test{{Val: 4}}, testVar)
+ assert.Equal(t, []*Test{{Val: 4}}, testVar)
}
diff --git a/modules/util/path.go b/modules/util/path.go
index d9f17bd124..0e56348978 100644
--- a/modules/util/path.go
+++ b/modules/util/path.go
@@ -36,9 +36,10 @@ func PathJoinRel(elem ...string) string {
elems[i] = path.Clean("/" + e)
}
p := path.Join(elems...)
- if p == "" {
+ switch p {
+ case "":
return ""
- } else if p == "/" {
+ case "/":
return "."
}
return p[1:]
diff --git a/modules/util/remove.go b/modules/util/remove.go
index d1e38faf5f..3db0b5a796 100644
--- a/modules/util/remove.go
+++ b/modules/util/remove.go
@@ -15,7 +15,7 @@ const windowsSharingViolationError syscall.Errno = 32
// Remove removes the named file or (empty) directory with at most 5 attempts.
func Remove(name string) error {
var err error
- for i := 0; i < 5; i++ {
+ for range 5 {
err = os.Remove(name)
if err == nil {
break
@@ -44,7 +44,7 @@ func Remove(name string) error {
// RemoveAll removes the named file or (empty) directory with at most 5 attempts.
func RemoveAll(name string) error {
var err error
- for i := 0; i < 5; i++ {
+ for range 5 {
err = os.RemoveAll(name)
if err == nil {
break
@@ -73,7 +73,7 @@ func RemoveAll(name string) error {
// Rename renames (moves) oldpath to newpath with at most 5 attempts.
func Rename(oldpath, newpath string) error {
var err error
- for i := 0; i < 5; i++ {
+ for i := range 5 {
err = os.Rename(oldpath, newpath)
if err == nil {
break
diff --git a/modules/util/rotatingfilewriter/writer_test.go b/modules/util/rotatingfilewriter/writer_test.go
index 88392797b3..f6ea1d50ae 100644
--- a/modules/util/rotatingfilewriter/writer_test.go
+++ b/modules/util/rotatingfilewriter/writer_test.go
@@ -23,7 +23,7 @@ func TestCompressOldFile(t *testing.T) {
ng, err := os.OpenFile(nonGzip, os.O_CREATE|os.O_WRONLY, 0o660)
assert.NoError(t, err)
- for i := 0; i < 999; i++ {
+ for range 999 {
f.WriteString("This is a test file\n")
ng.WriteString("This is a test file\n")
}
diff --git a/modules/util/runtime_test.go b/modules/util/runtime_test.go
index 20f9063b0b..01dd034cea 100644
--- a/modules/util/runtime_test.go
+++ b/modules/util/runtime_test.go
@@ -18,14 +18,14 @@ func TestCallerFuncName(t *testing.T) {
func BenchmarkCallerFuncName(b *testing.B) {
// BenchmarkCaller/sprintf-12 12744829 95.49 ns/op
b.Run("sprintf", func(b *testing.B) {
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
_ = fmt.Sprintf("aaaaaaaaaaaaaaaa %s %s %s", "bbbbbbbbbbbbbbbbbbb", b.Name(), "ccccccccccccccccccccc")
}
})
// BenchmarkCaller/caller-12 10625133 113.6 ns/op
// It is almost as fast as fmt.Sprintf
b.Run("caller", func(b *testing.B) {
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
CallerFuncName(1)
}
})
diff --git a/modules/util/sec_to_time.go b/modules/util/sec_to_time.go
index ad0fb1a68b..646f33c82a 100644
--- a/modules/util/sec_to_time.go
+++ b/modules/util/sec_to_time.go
@@ -8,61 +8,23 @@ import (
"strings"
)
-// SecToTime converts an amount of seconds to a human-readable string. E.g.
-// 66s -> 1 minute 6 seconds
-// 52410s -> 14 hours 33 minutes
-// 563418 -> 6 days 12 hours
-// 1563418 -> 2 weeks 4 days
-// 3937125s -> 1 month 2 weeks
-// 45677465s -> 1 year 6 months
-func SecToTime(durationVal any) string {
- duration, _ := ToInt64(durationVal)
+// SecToHours converts an amount of seconds to a human-readable hours string.
+// This is stable for planning and managing timesheets.
+// Here it only supports hours and minutes, because a work day could contain 6 or 7 or 8 hours.
+// If the duration is less than 1 minute, it will be shown as seconds.
+func SecToHours(durationVal any) string {
+ seconds, _ := ToInt64(durationVal)
+ hours := seconds / 3600
+ minutes := (seconds / 60) % 60
formattedTime := ""
-
- // The following four variables are calculated by taking
- // into account the previously calculated variables, this avoids
- // pitfalls when using remainders. As that could lead to incorrect
- // results when the calculated number equals the quotient number.
- remainingDays := duration / (60 * 60 * 24)
- years := remainingDays / 365
- remainingDays -= years * 365
- months := remainingDays * 12 / 365
- remainingDays -= months * 365 / 12
- weeks := remainingDays / 7
- remainingDays -= weeks * 7
- days := remainingDays
-
- // The following three variables are calculated without depending
- // on the previous calculated variables.
- hours := (duration / 3600) % 24
- minutes := (duration / 60) % 60
- seconds := duration % 60
-
- // Extract only the relevant information of the time
- // If the time is greater than a year, it makes no sense to display seconds.
- switch {
- case years > 0:
- formattedTime = formatTime(years, "year", formattedTime)
- formattedTime = formatTime(months, "month", formattedTime)
- case months > 0:
- formattedTime = formatTime(months, "month", formattedTime)
- formattedTime = formatTime(weeks, "week", formattedTime)
- case weeks > 0:
- formattedTime = formatTime(weeks, "week", formattedTime)
- formattedTime = formatTime(days, "day", formattedTime)
- case days > 0:
- formattedTime = formatTime(days, "day", formattedTime)
- formattedTime = formatTime(hours, "hour", formattedTime)
- case hours > 0:
- formattedTime = formatTime(hours, "hour", formattedTime)
- formattedTime = formatTime(minutes, "minute", formattedTime)
- default:
- formattedTime = formatTime(minutes, "minute", formattedTime)
- formattedTime = formatTime(seconds, "second", formattedTime)
- }
+ formattedTime = formatTime(hours, "hour", formattedTime)
+ formattedTime = formatTime(minutes, "minute", formattedTime)
// The formatTime() function always appends a space at the end. This will be trimmed
+ if formattedTime == "" && seconds > 0 {
+ formattedTime = formatTime(seconds, "second", "")
+ }
return strings.TrimRight(formattedTime, " ")
}
@@ -76,6 +38,5 @@ func formatTime(value int64, name, formattedTime string) string {
} else if value > 1 {
formattedTime = fmt.Sprintf("%s%d %ss ", formattedTime, value, name)
}
-
return formattedTime
}
diff --git a/modules/util/sec_to_time_test.go b/modules/util/sec_to_time_test.go
index 4d1213a52c..84e767c6e0 100644
--- a/modules/util/sec_to_time_test.go
+++ b/modules/util/sec_to_time_test.go
@@ -9,22 +9,20 @@ import (
"github.com/stretchr/testify/assert"
)
-func TestSecToTime(t *testing.T) {
+func TestSecToHours(t *testing.T) {
second := int64(1)
minute := 60 * second
hour := 60 * minute
day := 24 * hour
- year := 365 * day
- assert.Equal(t, "1 minute 6 seconds", SecToTime(minute+6*second))
- assert.Equal(t, "1 hour", SecToTime(hour))
- assert.Equal(t, "1 hour", SecToTime(hour+second))
- assert.Equal(t, "14 hours 33 minutes", SecToTime(14*hour+33*minute+30*second))
- assert.Equal(t, "6 days 12 hours", SecToTime(6*day+12*hour+30*minute+18*second))
- assert.Equal(t, "2 weeks 4 days", SecToTime((2*7+4)*day+2*hour+16*minute+58*second))
- assert.Equal(t, "4 weeks", SecToTime(4*7*day))
- assert.Equal(t, "4 weeks 1 day", SecToTime((4*7+1)*day))
- assert.Equal(t, "1 month 2 weeks", SecToTime((6*7+3)*day+13*hour+38*minute+45*second))
- assert.Equal(t, "11 months", SecToTime(year-25*day))
- assert.Equal(t, "1 year 5 months", SecToTime(year+163*day+10*hour+11*minute+5*second))
+ assert.Equal(t, "1 minute", SecToHours(minute+6*second))
+ assert.Equal(t, "1 hour", SecToHours(hour))
+ assert.Equal(t, "1 hour", SecToHours(hour+second))
+ assert.Equal(t, "14 hours 33 minutes", SecToHours(14*hour+33*minute+30*second))
+ assert.Equal(t, "156 hours 30 minutes", SecToHours(6*day+12*hour+30*minute+18*second))
+ assert.Equal(t, "98 hours 16 minutes", SecToHours(4*day+2*hour+16*minute+58*second))
+ assert.Equal(t, "672 hours", SecToHours(4*7*day))
+ assert.Equal(t, "1 second", SecToHours(1))
+ assert.Equal(t, "2 seconds", SecToHours(2))
+ assert.Empty(t, SecToHours(nil)) // old behavior, empty means no output
}
diff --git a/modules/util/shellquote_test.go b/modules/util/shellquote_test.go
index 969998c592..4ef5ce6980 100644
--- a/modules/util/shellquote_test.go
+++ b/modules/util/shellquote_test.go
@@ -3,7 +3,11 @@
package util
-import "testing"
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
func TestShellEscape(t *testing.T) {
tests := []struct {
@@ -83,9 +87,7 @@ func TestShellEscape(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- if got := ShellEscape(tt.toEscape); got != tt.want {
- t.Errorf("ShellEscape(%q):\nGot: %s\nWanted: %s", tt.toEscape, got, tt.want)
- }
+ assert.Equal(t, tt.want, ShellEscape(tt.toEscape))
})
}
}
diff --git a/modules/util/slice.go b/modules/util/slice.go
index 9c878c24be..da6886491e 100644
--- a/modules/util/slice.go
+++ b/modules/util/slice.go
@@ -71,3 +71,10 @@ func KeysOfMap[K comparable, V any](m map[K]V) []K {
}
return keys
}
+
+func SliceNilAsEmpty[T any](a []T) []T {
+ if a == nil {
+ return []T{}
+ }
+ return a
+}
diff --git a/modules/util/string.go b/modules/util/string.go
index 19cf75b8b3..b9b59df3ef 100644
--- a/modules/util/string.go
+++ b/modules/util/string.go
@@ -103,10 +103,31 @@ func UnsafeStringToBytes(s string) []byte {
func SplitTrimSpace(input, sep string) []string {
input = strings.TrimSpace(input)
var stringList []string
- for _, s := range strings.Split(input, sep) {
+ for s := range strings.SplitSeq(input, sep) {
if s = strings.TrimSpace(s); s != "" {
stringList = append(stringList, s)
}
}
return stringList
}
+
+func asciiLower(b byte) byte {
+ if 'A' <= b && b <= 'Z' {
+ return b + ('a' - 'A')
+ }
+ return b
+}
+
+// AsciiEqualFold is from Golang https://cs.opensource.google/go/go/+/refs/tags/go1.24.4:src/net/http/internal/ascii/print.go
+// ASCII only. In most cases for protocols, we should only use this but not [strings.EqualFold]
+func AsciiEqualFold(s, t string) bool { //nolint:revive // PascalCase
+ if len(s) != len(t) {
+ return false
+ }
+ for i := 0; i < len(s); i++ {
+ if asciiLower(s[i]) != asciiLower(t[i]) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/modules/util/truncate_test.go b/modules/util/truncate_test.go
index 8789c824f5..9f4ad7dc20 100644
--- a/modules/util/truncate_test.go
+++ b/modules/util/truncate_test.go
@@ -5,6 +5,7 @@ package util
import (
"fmt"
+ "strconv"
"strings"
"testing"
@@ -100,7 +101,7 @@ func TestEllipsisString(t *testing.T) {
{limit: 7, left: "\xef\x03\xfe\xef\x03\xfe", right: ""},
}
for _, c := range invalidCases {
- t.Run(fmt.Sprintf("%d", c.limit), func(t *testing.T) {
+ t.Run(strconv.Itoa(c.limit), func(t *testing.T) {
left, right := EllipsisDisplayStringX("\xef\x03\xfe\xef\x03\xfe", c.limit)
assert.Equal(t, c.left, left, "left")
assert.Equal(t, c.right, right, "right")
@@ -115,15 +116,15 @@ func TestEllipsisString(t *testing.T) {
}
func TestTruncateRunes(t *testing.T) {
- assert.Equal(t, "", TruncateRunes("", 0))
- assert.Equal(t, "", TruncateRunes("", 1))
+ assert.Empty(t, TruncateRunes("", 0))
+ assert.Empty(t, TruncateRunes("", 1))
- assert.Equal(t, "", TruncateRunes("ab", 0))
+ assert.Empty(t, TruncateRunes("ab", 0))
assert.Equal(t, "a", TruncateRunes("ab", 1))
assert.Equal(t, "ab", TruncateRunes("ab", 2))
assert.Equal(t, "ab", TruncateRunes("ab", 3))
- assert.Equal(t, "", TruncateRunes("测试", 0))
+ assert.Empty(t, TruncateRunes("测试", 0))
assert.Equal(t, "测", TruncateRunes("测试", 1))
assert.Equal(t, "测试", TruncateRunes("测试", 2))
assert.Equal(t, "测试", TruncateRunes("测试", 3))
diff --git a/modules/util/util.go b/modules/util/util.go
index 1fb4cb21cb..dd8e073888 100644
--- a/modules/util/util.go
+++ b/modules/util/util.go
@@ -11,21 +11,10 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/modules/optional"
-
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
-// OptionalBoolParse get the corresponding optional.Option[bool] of a string using strconv.ParseBool
-func OptionalBoolParse(s string) optional.Option[bool] {
- v, e := strconv.ParseBool(s)
- if e != nil {
- return optional.None[bool]()
- }
- return optional.Some(v)
-}
-
// IsEmptyString checks if the provided string is empty
func IsEmptyString(s string) bool {
return len(strings.TrimSpace(s)) == 0
@@ -230,6 +219,13 @@ func IfZero[T comparable](v, def T) T {
return v
}
+func IfEmpty[T any](v, def []T) []T {
+ if len(v) == 0 {
+ return def
+ }
+ return v
+}
+
// OptionalArg helps the "optional argument" in Golang:
//
// func foo(optArg ...int) { return OptionalArg(optArg) }
diff --git a/modules/util/util_test.go b/modules/util/util_test.go
index 52b05acc5b..fe4125cdb5 100644
--- a/modules/util/util_test.go
+++ b/modules/util/util_test.go
@@ -8,8 +8,6 @@ import (
"strings"
"testing"
- "code.gitea.io/gitea/modules/optional"
-
"github.com/stretchr/testify/assert"
)
@@ -175,19 +173,6 @@ func Test_RandomBytes(t *testing.T) {
assert.NotEqual(t, bytes3, bytes4)
}
-func TestOptionalBoolParse(t *testing.T) {
- assert.Equal(t, optional.None[bool](), OptionalBoolParse(""))
- assert.Equal(t, optional.None[bool](), OptionalBoolParse("x"))
-
- assert.Equal(t, optional.Some(false), OptionalBoolParse("0"))
- assert.Equal(t, optional.Some(false), OptionalBoolParse("f"))
- assert.Equal(t, optional.Some(false), OptionalBoolParse("False"))
-
- assert.Equal(t, optional.Some(true), OptionalBoolParse("1"))
- assert.Equal(t, optional.Some(true), OptionalBoolParse("t"))
- assert.Equal(t, optional.Some(true), OptionalBoolParse("True"))
-}
-
// Test case for any function which accepts and returns a single string.
type StringTest struct {
in, out string
@@ -215,7 +200,7 @@ func TestToUpperASCII(t *testing.T) {
func BenchmarkToUpper(b *testing.B) {
for _, tc := range upperTests {
b.Run(tc.in, func(b *testing.B) {
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
ToUpperASCII(tc.in)
}
})
diff --git a/modules/validation/binding_test.go b/modules/validation/binding_test.go
index 28d0f57b5c..0cd328f312 100644
--- a/modules/validation/binding_test.go
+++ b/modules/validation/binding_test.go
@@ -47,7 +47,7 @@ func performValidationTest(t *testing.T, testCase validationTestCase) {
assert.Equal(t, testCase.expectedErrors, actual)
})
- req, err := http.NewRequest("POST", testRoute, nil)
+ req, err := http.NewRequest(http.MethodPost, testRoute, nil)
if err != nil {
panic(err)
}
diff --git a/modules/validation/glob_pattern_test.go b/modules/validation/glob_pattern_test.go
index 1bf622e61d..7f3e609acf 100644
--- a/modules/validation/glob_pattern_test.go
+++ b/modules/validation/glob_pattern_test.go
@@ -19,39 +19,39 @@ func getGlobPatternErrorString(pattern string) string {
return ""
}
-var globValidationTestCases = []validationTestCase{
- {
- description: "Empty glob pattern",
- data: TestForm{
- GlobPattern: "",
- },
- expectedErrors: binding.Errors{},
- },
- {
- description: "Valid glob",
- data: TestForm{
- GlobPattern: "{master,release*}",
- },
- expectedErrors: binding.Errors{},
- },
+func Test_GlobPatternValidation(t *testing.T) {
+ AddBindingRules()
- {
- description: "Invalid glob",
- data: TestForm{
- GlobPattern: "[a-",
+ globValidationTestCases := []validationTestCase{
+ {
+ description: "Empty glob pattern",
+ data: TestForm{
+ GlobPattern: "",
+ },
+ expectedErrors: binding.Errors{},
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"GlobPattern"},
- Classification: ErrGlobPattern,
- Message: getGlobPatternErrorString("[a-"),
+ {
+ description: "Valid glob",
+ data: TestForm{
+ GlobPattern: "{master,release*}",
},
+ expectedErrors: binding.Errors{},
},
- },
-}
-func Test_GlobPatternValidation(t *testing.T) {
- AddBindingRules()
+ {
+ description: "Invalid glob",
+ data: TestForm{
+ GlobPattern: "[a-",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"GlobPattern"},
+ Classification: ErrGlobPattern,
+ Message: getGlobPatternErrorString("[a-"),
+ },
+ },
+ },
+ }
for _, testCase := range globValidationTestCases {
t.Run(testCase.description, func(t *testing.T) {
diff --git a/modules/validation/helpers.go b/modules/validation/helpers.go
index f6e00f3887..ba383ba195 100644
--- a/modules/validation/helpers.go
+++ b/modules/validation/helpers.go
@@ -7,14 +7,28 @@ import (
"net"
"net/url"
"regexp"
+ "slices"
"strings"
+ "sync"
"code.gitea.io/gitea/modules/setting"
"github.com/gobwas/glob"
)
-var externalTrackerRegex = regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`)
+type globalVarsStruct struct {
+ externalTrackerRegex *regexp.Regexp
+ validUsernamePattern *regexp.Regexp
+ invalidUsernamePattern *regexp.Regexp
+}
+
+var globalVars = sync.OnceValue(func() *globalVarsStruct {
+ return &globalVarsStruct{
+ externalTrackerRegex: regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`),
+ validUsernamePattern: regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`),
+ invalidUsernamePattern: regexp.MustCompile(`[-._]{2,}|[-._]$`), // No consecutive or trailing non-alphanumeric chars
+ }
+})
func isLoopbackIP(ip string) bool {
return net.ParseIP(ip).IsLoopback()
@@ -42,12 +56,7 @@ func IsValidSiteURL(uri string) bool {
return false
}
- for _, scheme := range setting.Service.ValidSiteURLSchemes {
- if scheme == u.Scheme {
- return true
- }
- }
- return false
+ return slices.Contains(setting.Service.ValidSiteURLSchemes, u.Scheme)
}
// IsEmailDomainListed checks whether the domain of an email address
@@ -105,9 +114,9 @@ func IsValidExternalTrackerURLFormat(uri string) bool {
if !IsValidExternalURL(uri) {
return false
}
-
+ vars := globalVars()
// check for typoed variables like /{index/ or /[repo}
- for _, match := range externalTrackerRegex.FindAllStringSubmatch(uri, -1) {
+ for _, match := range vars.externalTrackerRegex.FindAllStringSubmatch(uri, -1) {
if (match[1] == "{" || match[2] == "}") && (match[1] != "{" || match[2] != "}") {
return false
}
@@ -116,14 +125,10 @@ func IsValidExternalTrackerURLFormat(uri string) bool {
return true
}
-var (
- validUsernamePattern = regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`)
- invalidUsernamePattern = regexp.MustCompile(`[-._]{2,}|[-._]$`) // No consecutive or trailing non-alphanumeric chars
-)
-
// IsValidUsername checks if username is valid
func IsValidUsername(name string) bool {
// It is difficult to find a single pattern that is both readable and effective,
// but it's easier to use positive and negative checks.
- return validUsernamePattern.MatchString(name) && !invalidUsernamePattern.MatchString(name)
+ vars := globalVars()
+ return vars.validUsernamePattern.MatchString(name) && !vars.invalidUsernamePattern.MatchString(name)
}
diff --git a/modules/validation/helpers_test.go b/modules/validation/helpers_test.go
index 52f383f698..6a982965f6 100644
--- a/modules/validation/helpers_test.go
+++ b/modules/validation/helpers_test.go
@@ -7,6 +7,7 @@ import (
"testing"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
@@ -47,7 +48,7 @@ func Test_IsValidURL(t *testing.T) {
}
func Test_IsValidExternalURL(t *testing.T) {
- setting.AppURL = "https://try.gitea.io/"
+ defer test.MockVariableValue(&setting.AppURL, "https://try.gitea.io/")()
cases := []struct {
description string
@@ -89,7 +90,7 @@ func Test_IsValidExternalURL(t *testing.T) {
}
func Test_IsValidExternalTrackerURLFormat(t *testing.T) {
- setting.AppURL = "https://try.gitea.io/"
+ defer test.MockVariableValue(&setting.AppURL, "https://try.gitea.io/")()
cases := []struct {
description string
diff --git a/modules/validation/refname_test.go b/modules/validation/refname_test.go
index 3af7387c47..93703761cf 100644
--- a/modules/validation/refname_test.go
+++ b/modules/validation/refname_test.go
@@ -9,253 +9,252 @@ import (
"gitea.com/go-chi/binding"
)
-var gitRefNameValidationTestCases = []validationTestCase{
- {
- description: "Reference name contains only characters",
- data: TestForm{
- BranchName: "test",
- },
- expectedErrors: binding.Errors{},
- },
- {
- description: "Reference name contains single slash",
- data: TestForm{
- BranchName: "feature/test",
- },
- expectedErrors: binding.Errors{},
- },
- {
- description: "Reference name has allowed special characters",
- data: TestForm{
- BranchName: "debian/1%1.6.0-2",
- },
- expectedErrors: binding.Errors{},
- },
- {
- description: "Reference name contains backslash",
- data: TestForm{
- BranchName: "feature\\test",
- },
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+func Test_GitRefNameValidation(t *testing.T) {
+ AddBindingRules()
+ gitRefNameValidationTestCases := []validationTestCase{
+ {
+ description: "Reference name contains only characters",
+ data: TestForm{
+ BranchName: "test",
},
+ expectedErrors: binding.Errors{},
},
- },
- {
- description: "Reference name starts with dot",
- data: TestForm{
- BranchName: ".test",
- },
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ {
+ description: "Reference name contains single slash",
+ data: TestForm{
+ BranchName: "feature/test",
},
+ expectedErrors: binding.Errors{},
},
- },
- {
- description: "Reference name ends with dot",
- data: TestForm{
- BranchName: "test.",
- },
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ {
+ description: "Reference name has allowed special characters",
+ data: TestForm{
+ BranchName: "debian/1%1.6.0-2",
},
+ expectedErrors: binding.Errors{},
},
- },
- {
- description: "Reference name starts with slash",
- data: TestForm{
- BranchName: "/test",
- },
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ {
+ description: "Reference name contains backslash",
+ data: TestForm{
+ BranchName: "feature\\test",
},
- },
- },
- {
- description: "Reference name ends with slash",
- data: TestForm{
- BranchName: "test/",
- },
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
},
},
- },
- {
- description: "Reference name ends with .lock",
- data: TestForm{
- BranchName: "test.lock",
- },
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ {
+ description: "Reference name starts with dot",
+ data: TestForm{
+ BranchName: ".test",
},
- },
- },
- {
- description: "Reference name contains multiple consecutive dots",
- data: TestForm{
- BranchName: "te..st",
- },
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
},
},
- },
- {
- description: "Reference name contains multiple consecutive slashes",
- data: TestForm{
- BranchName: "te//st",
- },
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ {
+ description: "Reference name ends with dot",
+ data: TestForm{
+ BranchName: "test.",
},
- },
- },
- {
- description: "Reference name is single @",
- data: TestForm{
- BranchName: "@",
- },
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
},
},
- },
- {
- description: "Reference name has @{",
- data: TestForm{
- BranchName: "branch@{",
- },
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ {
+ description: "Reference name starts with slash",
+ data: TestForm{
+ BranchName: "/test",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
},
},
- },
- {
- description: "Reference name has unallowed special character ~",
- data: TestForm{
- BranchName: "~debian/1%1.6.0-2",
- },
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ {
+ description: "Reference name ends with slash",
+ data: TestForm{
+ BranchName: "test/",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
},
},
- },
- {
- description: "Reference name has unallowed special character *",
- data: TestForm{
- BranchName: "*debian/1%1.6.0-2",
+ {
+ description: "Reference name ends with .lock",
+ data: TestForm{
+ BranchName: "test.lock",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
+ },
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ {
+ description: "Reference name contains multiple consecutive dots",
+ data: TestForm{
+ BranchName: "te..st",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
},
},
- },
- {
- description: "Reference name has unallowed special character ?",
- data: TestForm{
- BranchName: "?debian/1%1.6.0-2",
+ {
+ description: "Reference name contains multiple consecutive slashes",
+ data: TestForm{
+ BranchName: "te//st",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
+ },
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ {
+ description: "Reference name is single @",
+ data: TestForm{
+ BranchName: "@",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
},
},
- },
- {
- description: "Reference name has unallowed special character ^",
- data: TestForm{
- BranchName: "^debian/1%1.6.0-2",
+ {
+ description: "Reference name has @{",
+ data: TestForm{
+ BranchName: "branch@{",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
+ },
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ {
+ description: "Reference name has unallowed special character ~",
+ data: TestForm{
+ BranchName: "~debian/1%1.6.0-2",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
},
},
- },
- {
- description: "Reference name has unallowed special character :",
- data: TestForm{
- BranchName: "debian:jessie",
+ {
+ description: "Reference name has unallowed special character *",
+ data: TestForm{
+ BranchName: "*debian/1%1.6.0-2",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
+ },
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ {
+ description: "Reference name has unallowed special character ?",
+ data: TestForm{
+ BranchName: "?debian/1%1.6.0-2",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
},
},
- },
- {
- description: "Reference name has unallowed special character (whitespace)",
- data: TestForm{
- BranchName: "debian jessie",
+ {
+ description: "Reference name has unallowed special character ^",
+ data: TestForm{
+ BranchName: "^debian/1%1.6.0-2",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
+ },
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ {
+ description: "Reference name has unallowed special character :",
+ data: TestForm{
+ BranchName: "debian:jessie",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
},
},
- },
- {
- description: "Reference name has unallowed special character [",
- data: TestForm{
- BranchName: "debian[jessie",
+ {
+ description: "Reference name has unallowed special character (whitespace)",
+ data: TestForm{
+ BranchName: "debian jessie",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
+ },
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"BranchName"},
- Classification: ErrGitRefName,
- Message: "GitRefName",
+ {
+ description: "Reference name has unallowed special character [",
+ data: TestForm{
+ BranchName: "debian[jessie",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"BranchName"},
+ Classification: ErrGitRefName,
+ Message: "GitRefName",
+ },
},
},
- },
-}
-
-func Test_GitRefNameValidation(t *testing.T) {
- AddBindingRules()
+ }
for _, testCase := range gitRefNameValidationTestCases {
t.Run(testCase.description, func(t *testing.T) {
diff --git a/modules/validation/regex_pattern_test.go b/modules/validation/regex_pattern_test.go
index efcb276734..80790a23b1 100644
--- a/modules/validation/regex_pattern_test.go
+++ b/modules/validation/regex_pattern_test.go
@@ -17,39 +17,39 @@ func getRegexPatternErrorString(pattern string) string {
return ""
}
-var regexValidationTestCases = []validationTestCase{
- {
- description: "Empty regex pattern",
- data: TestForm{
- RegexPattern: "",
- },
- expectedErrors: binding.Errors{},
- },
- {
- description: "Valid regex",
- data: TestForm{
- RegexPattern: `(\d{1,3})+`,
- },
- expectedErrors: binding.Errors{},
- },
+func Test_RegexPatternValidation(t *testing.T) {
+ AddBindingRules()
- {
- description: "Invalid regex",
- data: TestForm{
- RegexPattern: "[a-",
+ regexValidationTestCases := []validationTestCase{
+ {
+ description: "Empty regex pattern",
+ data: TestForm{
+ RegexPattern: "",
+ },
+ expectedErrors: binding.Errors{},
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"RegexPattern"},
- Classification: ErrRegexPattern,
- Message: getRegexPatternErrorString("[a-"),
+ {
+ description: "Valid regex",
+ data: TestForm{
+ RegexPattern: `(\d{1,3})+`,
},
+ expectedErrors: binding.Errors{},
},
- },
-}
-func Test_RegexPatternValidation(t *testing.T) {
- AddBindingRules()
+ {
+ description: "Invalid regex",
+ data: TestForm{
+ RegexPattern: "[a-",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"RegexPattern"},
+ Classification: ErrRegexPattern,
+ Message: getRegexPatternErrorString("[a-"),
+ },
+ },
+ },
+ }
for _, testCase := range regexValidationTestCases {
t.Run(testCase.description, func(t *testing.T) {
diff --git a/modules/validation/validurl_test.go b/modules/validation/validurl_test.go
index 39f7fa5d65..ce4898b271 100644
--- a/modules/validation/validurl_test.go
+++ b/modules/validation/validurl_test.go
@@ -9,98 +9,98 @@ import (
"gitea.com/go-chi/binding"
)
-var urlValidationTestCases = []validationTestCase{
- {
- description: "Empty URL",
- data: TestForm{
- URL: "",
- },
- expectedErrors: binding.Errors{},
- },
- {
- description: "URL without port",
- data: TestForm{
- URL: "http://test.lan/",
- },
- expectedErrors: binding.Errors{},
- },
- {
- description: "URL with port",
- data: TestForm{
- URL: "http://test.lan:3000/",
- },
- expectedErrors: binding.Errors{},
- },
- {
- description: "URL with IPv6 address without port",
- data: TestForm{
- URL: "http://[::1]/",
- },
- expectedErrors: binding.Errors{},
- },
- {
- description: "URL with IPv6 address with port",
- data: TestForm{
- URL: "http://[::1]:3000/",
+func Test_ValidURLValidation(t *testing.T) {
+ AddBindingRules()
+
+ urlValidationTestCases := []validationTestCase{
+ {
+ description: "Empty URL",
+ data: TestForm{
+ URL: "",
+ },
+ expectedErrors: binding.Errors{},
},
- expectedErrors: binding.Errors{},
- },
- {
- description: "Invalid URL",
- data: TestForm{
- URL: "http//test.lan/",
+ {
+ description: "URL without port",
+ data: TestForm{
+ URL: "http://test.lan/",
+ },
+ expectedErrors: binding.Errors{},
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"URL"},
- Classification: binding.ERR_URL,
- Message: "Url",
+ {
+ description: "URL with port",
+ data: TestForm{
+ URL: "http://test.lan:3000/",
},
+ expectedErrors: binding.Errors{},
},
- },
- {
- description: "Invalid schema",
- data: TestForm{
- URL: "ftp://test.lan/",
+ {
+ description: "URL with IPv6 address without port",
+ data: TestForm{
+ URL: "http://[::1]/",
+ },
+ expectedErrors: binding.Errors{},
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"URL"},
- Classification: binding.ERR_URL,
- Message: "Url",
+ {
+ description: "URL with IPv6 address with port",
+ data: TestForm{
+ URL: "http://[::1]:3000/",
},
+ expectedErrors: binding.Errors{},
},
- },
- {
- description: "Invalid port",
- data: TestForm{
- URL: "http://test.lan:3x4/",
+ {
+ description: "Invalid URL",
+ data: TestForm{
+ URL: "http//test.lan/",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"URL"},
+ Classification: binding.ERR_URL,
+ Message: "Url",
+ },
+ },
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"URL"},
- Classification: binding.ERR_URL,
- Message: "Url",
+ {
+ description: "Invalid schema",
+ data: TestForm{
+ URL: "ftp://test.lan/",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"URL"},
+ Classification: binding.ERR_URL,
+ Message: "Url",
+ },
},
},
- },
- {
- description: "Invalid port with IPv6 address",
- data: TestForm{
- URL: "http://[::1]:3x4/",
+ {
+ description: "Invalid port",
+ data: TestForm{
+ URL: "http://test.lan:3x4/",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"URL"},
+ Classification: binding.ERR_URL,
+ Message: "Url",
+ },
+ },
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"URL"},
- Classification: binding.ERR_URL,
- Message: "Url",
+ {
+ description: "Invalid port with IPv6 address",
+ data: TestForm{
+ URL: "http://[::1]:3x4/",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"URL"},
+ Classification: binding.ERR_URL,
+ Message: "Url",
+ },
},
},
- },
-}
-
-func Test_ValidURLValidation(t *testing.T) {
- AddBindingRules()
+ }
for _, testCase := range urlValidationTestCases {
t.Run(testCase.description, func(t *testing.T) {
diff --git a/modules/validation/validurllist_test.go b/modules/validation/validurllist_test.go
index c6f862a962..cccc570a1a 100644
--- a/modules/validation/validurllist_test.go
+++ b/modules/validation/validurllist_test.go
@@ -9,145 +9,145 @@ import (
"gitea.com/go-chi/binding"
)
-// This is a copy of all the URL tests cases, plus additional ones to
-// account for multiple URLs
-var urlListValidationTestCases = []validationTestCase{
- {
- description: "Empty URL",
- data: TestForm{
- URLs: "",
- },
- expectedErrors: binding.Errors{},
- },
- {
- description: "URL without port",
- data: TestForm{
- URLs: "http://test.lan/",
- },
- expectedErrors: binding.Errors{},
- },
- {
- description: "URL with port",
- data: TestForm{
- URLs: "http://test.lan:3000/",
- },
- expectedErrors: binding.Errors{},
- },
- {
- description: "URL with IPv6 address without port",
- data: TestForm{
- URLs: "http://[::1]/",
- },
- expectedErrors: binding.Errors{},
- },
- {
- description: "URL with IPv6 address with port",
- data: TestForm{
- URLs: "http://[::1]:3000/",
- },
- expectedErrors: binding.Errors{},
- },
- {
- description: "Invalid URL",
- data: TestForm{
- URLs: "http//test.lan/",
- },
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"URLs"},
- Classification: binding.ERR_URL,
- Message: "http//test.lan/",
+func Test_ValidURLListValidation(t *testing.T) {
+ AddBindingRules()
+
+ // This is a copy of all the URL tests cases, plus additional ones to
+ // account for multiple URLs
+ urlListValidationTestCases := []validationTestCase{
+ {
+ description: "Empty URL",
+ data: TestForm{
+ URLs: "",
},
+ expectedErrors: binding.Errors{},
},
- },
- {
- description: "Invalid schema",
- data: TestForm{
- URLs: "ftp://test.lan/",
+ {
+ description: "URL without port",
+ data: TestForm{
+ URLs: "http://test.lan/",
+ },
+ expectedErrors: binding.Errors{},
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"URLs"},
- Classification: binding.ERR_URL,
- Message: "ftp://test.lan/",
+ {
+ description: "URL with port",
+ data: TestForm{
+ URLs: "http://test.lan:3000/",
},
+ expectedErrors: binding.Errors{},
},
- },
- {
- description: "Invalid port",
- data: TestForm{
- URLs: "http://test.lan:3x4/",
+ {
+ description: "URL with IPv6 address without port",
+ data: TestForm{
+ URLs: "http://[::1]/",
+ },
+ expectedErrors: binding.Errors{},
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"URLs"},
- Classification: binding.ERR_URL,
- Message: "http://test.lan:3x4/",
+ {
+ description: "URL with IPv6 address with port",
+ data: TestForm{
+ URLs: "http://[::1]:3000/",
},
+ expectedErrors: binding.Errors{},
},
- },
- {
- description: "Invalid port with IPv6 address",
- data: TestForm{
- URLs: "http://[::1]:3x4/",
+ {
+ description: "Invalid URL",
+ data: TestForm{
+ URLs: "http//test.lan/",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"URLs"},
+ Classification: binding.ERR_URL,
+ Message: "http//test.lan/",
+ },
+ },
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"URLs"},
- Classification: binding.ERR_URL,
- Message: "http://[::1]:3x4/",
+ {
+ description: "Invalid schema",
+ data: TestForm{
+ URLs: "ftp://test.lan/",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"URLs"},
+ Classification: binding.ERR_URL,
+ Message: "ftp://test.lan/",
+ },
},
},
- },
- {
- description: "Multi URLs",
- data: TestForm{
- URLs: "http://test.lan:3000/\nhttp://test.local/",
+ {
+ description: "Invalid port",
+ data: TestForm{
+ URLs: "http://test.lan:3x4/",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"URLs"},
+ Classification: binding.ERR_URL,
+ Message: "http://test.lan:3x4/",
+ },
+ },
},
- expectedErrors: binding.Errors{},
- },
- {
- description: "Multi URLs with newline",
- data: TestForm{
- URLs: "http://test.lan:3000/\nhttp://test.local/\n",
+ {
+ description: "Invalid port with IPv6 address",
+ data: TestForm{
+ URLs: "http://[::1]:3x4/",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"URLs"},
+ Classification: binding.ERR_URL,
+ Message: "http://[::1]:3x4/",
+ },
+ },
},
- expectedErrors: binding.Errors{},
- },
- {
- description: "List with invalid entry",
- data: TestForm{
- URLs: "http://test.lan:3000/\nhttp://[::1]:3x4/",
+ {
+ description: "Multi URLs",
+ data: TestForm{
+ URLs: "http://test.lan:3000/\nhttp://test.local/",
+ },
+ expectedErrors: binding.Errors{},
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"URLs"},
- Classification: binding.ERR_URL,
- Message: "http://[::1]:3x4/",
+ {
+ description: "Multi URLs with newline",
+ data: TestForm{
+ URLs: "http://test.lan:3000/\nhttp://test.local/\n",
},
+ expectedErrors: binding.Errors{},
},
- },
- {
- description: "List with two invalid entries",
- data: TestForm{
- URLs: "ftp://test.lan:3000/\nhttp://[::1]:3x4/\n",
+ {
+ description: "List with invalid entry",
+ data: TestForm{
+ URLs: "http://test.lan:3000/\nhttp://[::1]:3x4/",
+ },
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"URLs"},
+ Classification: binding.ERR_URL,
+ Message: "http://[::1]:3x4/",
+ },
+ },
},
- expectedErrors: binding.Errors{
- binding.Error{
- FieldNames: []string{"URLs"},
- Classification: binding.ERR_URL,
- Message: "ftp://test.lan:3000/",
+ {
+ description: "List with two invalid entries",
+ data: TestForm{
+ URLs: "ftp://test.lan:3000/\nhttp://[::1]:3x4/\n",
},
- binding.Error{
- FieldNames: []string{"URLs"},
- Classification: binding.ERR_URL,
- Message: "http://[::1]:3x4/",
+ expectedErrors: binding.Errors{
+ binding.Error{
+ FieldNames: []string{"URLs"},
+ Classification: binding.ERR_URL,
+ Message: "ftp://test.lan:3000/",
+ },
+ binding.Error{
+ FieldNames: []string{"URLs"},
+ Classification: binding.ERR_URL,
+ Message: "http://[::1]:3x4/",
+ },
},
},
- },
-}
-
-func Test_ValidURLListValidation(t *testing.T) {
- AddBindingRules()
+ }
for _, testCase := range urlListValidationTestCases {
t.Run(testCase.description, func(t *testing.T) {
diff --git a/modules/web/handler.go b/modules/web/handler.go
index 9a3e4a7f17..42a649714d 100644
--- a/modules/web/handler.go
+++ b/modules/web/handler.go
@@ -121,7 +121,7 @@ func wrapHandlerProvider[T http.Handler](hp func(next http.Handler) T, funcInfo
return func(next http.Handler) http.Handler {
h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
- routing.UpdateFuncInfo(req.Context(), funcInfo)
+ defer routing.RecordFuncInfo(req.Context(), funcInfo)()
h.ServeHTTP(resp, req)
})
}
@@ -157,7 +157,7 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
return // it's doing pre-check, just return
}
- routing.UpdateFuncInfo(req.Context(), funcInfo)
+ defer routing.RecordFuncInfo(req.Context(), funcInfo)()
ret := fn.Call(argsIn)
// handle the return value (no-op at the moment)
diff --git a/modules/web/middleware/binding.go b/modules/web/middleware/binding.go
index 43e1bbc70e..ee4eca976e 100644
--- a/modules/web/middleware/binding.go
+++ b/modules/web/middleware/binding.go
@@ -50,7 +50,7 @@ func AssignForm(form any, data map[string]any) {
}
func getRuleBody(field reflect.StructField, prefix string) string {
- for _, rule := range strings.Split(field.Tag.Get("binding"), ";") {
+ for rule := range strings.SplitSeq(field.Tag.Get("binding"), ";") {
if strings.HasPrefix(rule, prefix) {
return rule[len(prefix) : len(rule)-1]
}
@@ -78,7 +78,7 @@ func GetInclude(field reflect.StructField) string {
return getRuleBody(field, "Include(")
}
-// Validate validate TODO:
+// Validate validate
func Validate(errs binding.Errors, data map[string]any, f Form, l translation.Locale) binding.Errors {
if errs.Len() == 0 {
return errs
diff --git a/modules/web/middleware/flash.go b/modules/web/middleware/flash.go
index 0caaa8c036..0e848c7902 100644
--- a/modules/web/middleware/flash.go
+++ b/modules/web/middleware/flash.go
@@ -6,6 +6,7 @@ package middleware
import (
"fmt"
"html/template"
+ "net/http"
"net/url"
"code.gitea.io/gitea/modules/reqctx"
@@ -65,3 +66,27 @@ func (f *Flash) Success(msg any, current ...bool) {
f.SuccessMsg = flashMsgStringOrHTML(msg)
f.set("success", f.SuccessMsg, current...)
}
+
+func ParseCookieFlashMessage(val string) *Flash {
+ if vals, _ := url.ParseQuery(val); len(vals) > 0 {
+ return &Flash{
+ Values: vals,
+ ErrorMsg: vals.Get("error"),
+ SuccessMsg: vals.Get("success"),
+ InfoMsg: vals.Get("info"),
+ WarningMsg: vals.Get("warning"),
+ }
+ }
+ return nil
+}
+
+func GetSiteCookieFlashMessage(dataStore reqctx.RequestDataStore, req *http.Request, cookieName string) (string, *Flash) {
+ // Get the last flash message from cookie
+ lastFlashCookie := GetSiteCookie(req, cookieName)
+ lastFlashMsg := ParseCookieFlashMessage(lastFlashCookie)
+ if lastFlashMsg != nil {
+ lastFlashMsg.DataStore = dataStore
+ return lastFlashCookie, lastFlashMsg
+ }
+ return lastFlashCookie, nil
+}
diff --git a/modules/web/middleware/request.go b/modules/web/middleware/request.go
deleted file mode 100644
index 0bb155df70..0000000000
--- a/modules/web/middleware/request.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2020 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package middleware
-
-import (
- "net/http"
- "strings"
-)
-
-// IsAPIPath returns true if the specified URL is an API path
-func IsAPIPath(req *http.Request) bool {
- return strings.HasPrefix(req.URL.Path, "/api/")
-}
diff --git a/modules/web/routemock_test.go b/modules/web/routemock_test.go
index 89cfaacdd1..a0949bf622 100644
--- a/modules/web/routemock_test.go
+++ b/modules/web/routemock_test.go
@@ -30,13 +30,13 @@ func TestRouteMock(t *testing.T) {
// normal request
recorder := httptest.NewRecorder()
- req, err := http.NewRequest("GET", "http://localhost:8000/foo", nil)
+ req, err := http.NewRequest(http.MethodGet, "http://localhost:8000/foo", nil)
assert.NoError(t, err)
r.ServeHTTP(recorder, req)
assert.Len(t, recorder.Header(), 3)
- assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1"))
- assert.EqualValues(t, "m2", recorder.Header().Get("X-Test-Middleware2"))
- assert.EqualValues(t, "h", recorder.Header().Get("X-Test-Handler"))
+ assert.Equal(t, "m1", recorder.Header().Get("X-Test-Middleware1"))
+ assert.Equal(t, "m2", recorder.Header().Get("X-Test-Middleware2"))
+ assert.Equal(t, "h", recorder.Header().Get("X-Test-Handler"))
RouteMockReset()
// mock at "mock-point"
@@ -45,12 +45,12 @@ func TestRouteMock(t *testing.T) {
resp.WriteHeader(http.StatusOK)
})
recorder = httptest.NewRecorder()
- req, err = http.NewRequest("GET", "http://localhost:8000/foo", nil)
+ req, err = http.NewRequest(http.MethodGet, "http://localhost:8000/foo", nil)
assert.NoError(t, err)
r.ServeHTTP(recorder, req)
assert.Len(t, recorder.Header(), 2)
- assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1"))
- assert.EqualValues(t, "a", recorder.Header().Get("X-Test-MockPoint"))
+ assert.Equal(t, "m1", recorder.Header().Get("X-Test-Middleware1"))
+ assert.Equal(t, "a", recorder.Header().Get("X-Test-MockPoint"))
RouteMockReset()
// mock at MockAfterMiddlewares
@@ -59,12 +59,12 @@ func TestRouteMock(t *testing.T) {
resp.WriteHeader(http.StatusOK)
})
recorder = httptest.NewRecorder()
- req, err = http.NewRequest("GET", "http://localhost:8000/foo", nil)
+ req, err = http.NewRequest(http.MethodGet, "http://localhost:8000/foo", nil)
assert.NoError(t, err)
r.ServeHTTP(recorder, req)
assert.Len(t, recorder.Header(), 3)
- assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1"))
- assert.EqualValues(t, "m2", recorder.Header().Get("X-Test-Middleware2"))
- assert.EqualValues(t, "b", recorder.Header().Get("X-Test-MockPoint"))
+ assert.Equal(t, "m1", recorder.Header().Get("X-Test-Middleware1"))
+ assert.Equal(t, "m2", recorder.Header().Get("X-Test-Middleware2"))
+ assert.Equal(t, "b", recorder.Header().Get("X-Test-MockPoint"))
RouteMockReset()
}
diff --git a/modules/web/router.go b/modules/web/router.go
index da06b955b1..5812ff69d4 100644
--- a/modules/web/router.go
+++ b/modules/web/router.go
@@ -125,8 +125,8 @@ func (r *Router) Methods(methods, pattern string, h ...any) {
middlewares, handlerFunc := wrapMiddlewareAndHandler(r.curMiddlewares, h)
fullPattern := r.getPattern(pattern)
if strings.Contains(methods, ",") {
- methods := strings.Split(methods, ",")
- for _, method := range methods {
+ methods := strings.SplitSeq(methods, ",")
+ for method := range methods {
r.chiRouter.With(middlewares...).Method(strings.TrimSpace(method), fullPattern, handlerFunc)
}
} else {
diff --git a/modules/web/router_path.go b/modules/web/router_path.go
index 39082c0724..64154c34a5 100644
--- a/modules/web/router_path.go
+++ b/modules/web/router_path.go
@@ -4,9 +4,9 @@
package web
import (
- "fmt"
"net/http"
"regexp"
+ "slices"
"strings"
"code.gitea.io/gitea/modules/container"
@@ -26,6 +26,7 @@ func (g *RouterPathGroup) ServeHTTP(resp http.ResponseWriter, req *http.Request)
path := chiCtx.URLParam(g.pathParam)
for _, m := range g.matchers {
if m.matchPath(chiCtx, path) {
+ chiCtx.RoutePatterns = append(chiCtx.RoutePatterns, m.pattern)
handler := m.handlerFunc
for i := len(m.middlewares) - 1; i >= 0; i-- {
handler = m.middlewares[i](handler).ServeHTTP
@@ -37,11 +38,22 @@ func (g *RouterPathGroup) ServeHTTP(resp http.ResponseWriter, req *http.Request)
g.r.chiRouter.NotFoundHandler().ServeHTTP(resp, req)
}
+type RouterPathGroupPattern struct {
+ pattern string
+ re *regexp.Regexp
+ params []routerPathParam
+ middlewares []any
+}
+
// MatchPath matches the request method, and uses regexp to match the path.
-// The pattern uses "<...>" to define path parameters, for example: "/<name>" (different from chi router)
-// It is only designed to resolve some special cases which chi router can't handle.
+// The pattern uses "<...>" to define path parameters, for example, "/<name>" (different from chi router)
+// It is only designed to resolve some special cases that chi router can't handle.
// For most cases, it shouldn't be used because it needs to iterate all rules to find the matched one (inefficient).
func (g *RouterPathGroup) MatchPath(methods, pattern string, h ...any) {
+ g.MatchPattern(methods, g.PatternRegexp(pattern), h...)
+}
+
+func (g *RouterPathGroup) MatchPattern(methods string, pattern *RouterPathGroupPattern, h ...any) {
g.matchers = append(g.matchers, newRouterPathMatcher(methods, pattern, h...))
}
@@ -52,6 +64,7 @@ type routerPathParam struct {
type routerPathMatcher struct {
methods container.Set[string]
+ pattern string
re *regexp.Regexp
params []routerPathParam
middlewares []func(http.Handler) http.Handler
@@ -89,25 +102,43 @@ func (p *routerPathMatcher) matchPath(chiCtx *chi.Context, path string) bool {
return true
}
-func newRouterPathMatcher(methods, pattern string, h ...any) *routerPathMatcher {
- middlewares, handlerFunc := wrapMiddlewareAndHandler(nil, h)
+func isValidMethod(name string) bool {
+ switch name {
+ case http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete, http.MethodHead, http.MethodOptions, http.MethodConnect, http.MethodTrace:
+ return true
+ }
+ return false
+}
+
+func newRouterPathMatcher(methods string, patternRegexp *RouterPathGroupPattern, h ...any) *routerPathMatcher {
+ middlewares, handlerFunc := wrapMiddlewareAndHandler(patternRegexp.middlewares, h)
p := &routerPathMatcher{methods: make(container.Set[string]), middlewares: middlewares, handlerFunc: handlerFunc}
- for _, method := range strings.Split(methods, ",") {
- p.methods.Add(strings.TrimSpace(method))
+ for method := range strings.SplitSeq(methods, ",") {
+ method = strings.TrimSpace(method)
+ if !isValidMethod(method) {
+ panic("invalid HTTP method: " + method)
+ }
+ p.methods.Add(method)
}
+ p.pattern, p.re, p.params = patternRegexp.pattern, patternRegexp.re, patternRegexp.params
+ return p
+}
+
+func patternRegexp(pattern string, h ...any) *RouterPathGroupPattern {
+ p := &RouterPathGroupPattern{middlewares: slices.Clone(h)}
re := []byte{'^'}
lastEnd := 0
for lastEnd < len(pattern) {
start := strings.IndexByte(pattern[lastEnd:], '<')
if start == -1 {
- re = append(re, pattern[lastEnd:]...)
+ re = append(re, regexp.QuoteMeta(pattern[lastEnd:])...)
break
}
end := strings.IndexByte(pattern[lastEnd+start:], '>')
if end == -1 {
- panic(fmt.Sprintf("invalid pattern: %s", pattern))
+ panic("invalid pattern: " + pattern)
}
- re = append(re, pattern[lastEnd:lastEnd+start]...)
+ re = append(re, regexp.QuoteMeta(pattern[lastEnd:lastEnd+start])...)
partName, partExp, _ := strings.Cut(pattern[lastEnd+start+1:lastEnd+start+end], ":")
lastEnd += start + end + 1
@@ -129,7 +160,10 @@ func newRouterPathMatcher(methods, pattern string, h ...any) *routerPathMatcher
p.params = append(p.params, param)
}
re = append(re, '$')
- reStr := string(re)
- p.re = regexp.MustCompile(reStr)
+ p.pattern, p.re = pattern, regexp.MustCompile(string(re))
return p
}
+
+func (g *RouterPathGroup) PatternRegexp(pattern string, h ...any) *RouterPathGroupPattern {
+ return patternRegexp(pattern, h...)
+}
diff --git a/modules/web/router_test.go b/modules/web/router_test.go
index 582980a27a..f216aa6180 100644
--- a/modules/web/router_test.go
+++ b/modules/web/router_test.go
@@ -34,7 +34,7 @@ func TestPathProcessor(t *testing.T) {
testProcess := func(pattern, uri string, expectedPathParams map[string]string) {
chiCtx := chi.NewRouteContext()
chiCtx.RouteMethod = "GET"
- p := newRouterPathMatcher("GET", pattern, http.NotFound)
+ p := newRouterPathMatcher("GET", patternRegexp(pattern), http.NotFound)
assert.True(t, p.matchPath(chiCtx, uri), "use pattern %s to process uri %s", pattern, uri)
assert.Equal(t, expectedPathParams, chiURLParamsToMap(chiCtx), "use pattern %s to process uri %s", pattern, uri)
}
@@ -51,23 +51,28 @@ func TestPathProcessor(t *testing.T) {
}
func TestRouter(t *testing.T) {
- buff := bytes.NewBufferString("")
+ buff := &bytes.Buffer{}
recorder := httptest.NewRecorder()
recorder.Body = buff
type resultStruct struct {
- method string
- pathParams map[string]string
- handlerMark string
+ method string
+ pathParams map[string]string
+ handlerMarks []string
+ chiRoutePattern *string
}
- var res resultStruct
+ var res resultStruct
h := func(optMark ...string) func(resp http.ResponseWriter, req *http.Request) {
mark := util.OptionalArg(optMark, "")
return func(resp http.ResponseWriter, req *http.Request) {
+ chiCtx := chi.RouteContext(req.Context())
res.method = req.Method
- res.pathParams = chiURLParamsToMap(chi.RouteContext(req.Context()))
- res.handlerMark = mark
+ res.pathParams = chiURLParamsToMap(chiCtx)
+ res.chiRoutePattern = util.ToPointer(chiCtx.RoutePattern())
+ if mark != "" {
+ res.handlerMarks = append(res.handlerMarks, mark)
+ }
}
}
@@ -77,6 +82,8 @@ func TestRouter(t *testing.T) {
if stop := req.FormValue("stop"); stop != "" && (mark == "" || mark == stop) {
h(stop)(resp, req)
resp.WriteHeader(http.StatusOK)
+ } else if mark != "" {
+ res.handlerMarks = append(res.handlerMarks, mark)
}
}
}
@@ -108,7 +115,7 @@ func TestRouter(t *testing.T) {
m.Delete("", h())
})
m.PathGroup("/*", func(g *RouterPathGroup) {
- g.MatchPath("GET", `/<dir:*>/<file:[a-z]{1,2}>`, stopMark("s2"), h("match-path"))
+ g.MatchPattern("GET", g.PatternRegexp(`/<dir:*>/<file:[a-z]{1,2}>`, stopMark("s2")), stopMark("s3"), h("match-path"))
}, stopMark("s1"))
})
})
@@ -121,36 +128,47 @@ func TestRouter(t *testing.T) {
req, err := http.NewRequest(methodPathFields[0], methodPathFields[1], nil)
assert.NoError(t, err)
r.ServeHTTP(recorder, req)
- assert.EqualValues(t, expected, res)
+ if expected.chiRoutePattern == nil {
+ res.chiRoutePattern = nil
+ }
+ assert.Equal(t, expected, res)
})
}
t.Run("RootRouter", func(t *testing.T) {
- testRoute(t, "GET /the-user/the-repo/other", resultStruct{method: "GET", handlerMark: "not-found:/"})
+ testRoute(t, "GET /the-user/the-repo/other", resultStruct{
+ method: "GET",
+ handlerMarks: []string{"not-found:/"},
+ chiRoutePattern: util.ToPointer(""),
+ })
testRoute(t, "GET /the-user/the-repo/pulls", resultStruct{
- method: "GET",
- pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "type": "pulls"},
- handlerMark: "list-issues-b",
+ method: "GET",
+ pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "type": "pulls"},
+ handlerMarks: []string{"list-issues-b"},
})
testRoute(t, "GET /the-user/the-repo/issues/123", resultStruct{
- method: "GET",
- pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "type": "issues", "index": "123"},
- handlerMark: "view-issue",
+ method: "GET",
+ pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "type": "issues", "index": "123"},
+ handlerMarks: []string{"view-issue"},
+ chiRoutePattern: util.ToPointer("/{username}/{reponame}/{type:issues|pulls}/{index}"),
})
testRoute(t, "GET /the-user/the-repo/issues/123?stop=hijack", resultStruct{
- method: "GET",
- pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "type": "issues", "index": "123"},
- handlerMark: "hijack",
+ method: "GET",
+ pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "type": "issues", "index": "123"},
+ handlerMarks: []string{"hijack"},
})
testRoute(t, "POST /the-user/the-repo/issues/123/update", resultStruct{
- method: "POST",
- pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "index": "123"},
- handlerMark: "update-issue",
+ method: "POST",
+ pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "index": "123"},
+ handlerMarks: []string{"update-issue"},
})
})
t.Run("Sub Router", func(t *testing.T) {
- testRoute(t, "GET /api/v1/other", resultStruct{method: "GET", handlerMark: "not-found:/api/v1"})
+ testRoute(t, "GET /api/v1/other", resultStruct{
+ method: "GET",
+ handlerMarks: []string{"not-found:/api/v1"},
+ })
testRoute(t, "GET /api/v1/repos/the-user/the-repo/branches", resultStruct{
method: "GET",
pathParams: map[string]string{"username": "the-user", "reponame": "the-repo"},
@@ -179,31 +197,38 @@ func TestRouter(t *testing.T) {
t.Run("MatchPath", func(t *testing.T) {
testRoute(t, "GET /api/v1/repos/the-user/the-repo/branches/d1/d2/fn", resultStruct{
- method: "GET",
- pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "*": "d1/d2/fn", "dir": "d1/d2", "file": "fn"},
- handlerMark: "match-path",
+ method: "GET",
+ pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "*": "d1/d2/fn", "dir": "d1/d2", "file": "fn"},
+ handlerMarks: []string{"s1", "s2", "s3", "match-path"},
})
testRoute(t, "GET /api/v1/repos/the-user/the-repo/branches/d1%2fd2/fn", resultStruct{
- method: "GET",
- pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "*": "d1%2fd2/fn", "dir": "d1%2fd2", "file": "fn"},
- handlerMark: "match-path",
+ method: "GET",
+ pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "*": "d1%2fd2/fn", "dir": "d1%2fd2", "file": "fn"},
+ handlerMarks: []string{"s1", "s2", "s3", "match-path"},
})
testRoute(t, "GET /api/v1/repos/the-user/the-repo/branches/d1/d2/000", resultStruct{
- method: "GET",
- pathParams: map[string]string{"reponame": "the-repo", "username": "the-user", "*": "d1/d2/000"},
- handlerMark: "not-found:/api/v1",
+ method: "GET",
+ pathParams: map[string]string{"reponame": "the-repo", "username": "the-user", "*": "d1/d2/000"},
+ handlerMarks: []string{"s1", "not-found:/api/v1"},
})
testRoute(t, "GET /api/v1/repos/the-user/the-repo/branches/d1/d2/fn?stop=s1", resultStruct{
- method: "GET",
- pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "*": "d1/d2/fn"},
- handlerMark: "s1",
+ method: "GET",
+ pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "*": "d1/d2/fn"},
+ handlerMarks: []string{"s1"},
})
testRoute(t, "GET /api/v1/repos/the-user/the-repo/branches/d1/d2/fn?stop=s2", resultStruct{
- method: "GET",
- pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "*": "d1/d2/fn", "dir": "d1/d2", "file": "fn"},
- handlerMark: "s2",
+ method: "GET",
+ pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "*": "d1/d2/fn", "dir": "d1/d2", "file": "fn"},
+ handlerMarks: []string{"s1", "s2"},
+ })
+
+ testRoute(t, "GET /api/v1/repos/the-user/the-repo/branches/d1/d2/fn?stop=s3", resultStruct{
+ method: "GET",
+ pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "*": "d1/d2/fn", "dir": "d1/d2", "file": "fn"},
+ handlerMarks: []string{"s1", "s2", "s3"},
+ chiRoutePattern: util.ToPointer("/api/v1/repos/{username}/{reponame}/branches/<dir:*>/<file:[a-z]{1,2}>"),
})
})
}
@@ -224,7 +249,7 @@ func TestRouteNormalizePath(t *testing.T) {
actualPaths.Path = req.URL.Path
})
- req, err := http.NewRequest("GET", reqPath, nil)
+ req, err := http.NewRequest(http.MethodGet, reqPath, nil)
assert.NoError(t, err)
r.ServeHTTP(recorder, req)
assert.Equal(t, expectedPaths, actualPaths, "req path = %q", reqPath)
diff --git a/modules/web/routing/context.go b/modules/web/routing/context.go
index c5e85a415b..d3eb98f83d 100644
--- a/modules/web/routing/context.go
+++ b/modules/web/routing/context.go
@@ -6,22 +6,29 @@ package routing
import (
"context"
"net/http"
+
+ "code.gitea.io/gitea/modules/gtprof"
+ "code.gitea.io/gitea/modules/reqctx"
)
type contextKeyType struct{}
var contextKey contextKeyType
-// UpdateFuncInfo updates a context's func info
-func UpdateFuncInfo(ctx context.Context, funcInfo *FuncInfo) {
- record, ok := ctx.Value(contextKey).(*requestRecord)
- if !ok {
- return
+// RecordFuncInfo records a func info into context
+func RecordFuncInfo(ctx context.Context, funcInfo *FuncInfo) (end func()) {
+ end = func() {}
+ if reqCtx := reqctx.FromContext(ctx); reqCtx != nil {
+ var traceSpan *gtprof.TraceSpan
+ traceSpan, end = gtprof.GetTracer().StartInContext(reqCtx, "http.func")
+ traceSpan.SetAttributeString("func", funcInfo.shortName)
}
-
- record.lock.Lock()
- record.funcInfo = funcInfo
- record.lock.Unlock()
+ if record, ok := ctx.Value(contextKey).(*requestRecord); ok {
+ record.lock.Lock()
+ record.funcInfo = funcInfo
+ record.lock.Unlock()
+ }
+ return end
}
// MarkLongPolling marks the request is a long-polling request, and the logger may output different message for it
diff --git a/modules/web/routing/funcinfo_test.go b/modules/web/routing/funcinfo_test.go
index 2ab5960373..974af58931 100644
--- a/modules/web/routing/funcinfo_test.go
+++ b/modules/web/routing/funcinfo_test.go
@@ -6,6 +6,8 @@ package routing
import (
"fmt"
"testing"
+
+ "github.com/stretchr/testify/assert"
)
func Test_shortenFilename(t *testing.T) {
@@ -37,9 +39,8 @@ func Test_shortenFilename(t *testing.T) {
}
for _, tt := range tests {
t.Run(fmt.Sprintf("shortenFilename('%s')", tt.filename), func(t *testing.T) {
- if gotShort := shortenFilename(tt.filename, tt.fallback); gotShort != tt.expected {
- t.Errorf("shortenFilename('%s'), expect '%s', but get '%s'", tt.filename, tt.expected, gotShort)
- }
+ gotShort := shortenFilename(tt.filename, tt.fallback)
+ assert.Equal(t, tt.expected, gotShort)
})
}
}
@@ -72,9 +73,8 @@ func Test_trimAnonymousFunctionSuffix(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- if got := trimAnonymousFunctionSuffix(tt.name); got != tt.want {
- t.Errorf("trimAnonymousFunctionSuffix() = %v, want %v", got, tt.want)
- }
+ got := trimAnonymousFunctionSuffix(tt.name)
+ assert.Equal(t, tt.want, got)
})
}
}
diff --git a/modules/web/routing/logger.go b/modules/web/routing/logger.go
index 5f3a7592af..3bca9b3420 100644
--- a/modules/web/routing/logger.go
+++ b/modules/web/routing/logger.go
@@ -35,6 +35,19 @@ var (
)
func logPrinter(logger log.Logger) func(trigger Event, record *requestRecord) {
+ const callerName = "HTTPRequest"
+ logTrace := func(fmt string, args ...any) {
+ logger.Log(2, &log.Event{Level: log.TRACE, Caller: callerName}, fmt, args...)
+ }
+ logInfo := func(fmt string, args ...any) {
+ logger.Log(2, &log.Event{Level: log.INFO, Caller: callerName}, fmt, args...)
+ }
+ logWarn := func(fmt string, args ...any) {
+ logger.Log(2, &log.Event{Level: log.WARN, Caller: callerName}, fmt, args...)
+ }
+ logError := func(fmt string, args ...any) {
+ logger.Log(2, &log.Event{Level: log.ERROR, Caller: callerName}, fmt, args...)
+ }
return func(trigger Event, record *requestRecord) {
if trigger == StartEvent {
if !logger.LevelEnabled(log.TRACE) {
@@ -44,7 +57,7 @@ func logPrinter(logger log.Logger) func(trigger Event, record *requestRecord) {
}
// when a request starts, we have no information about the handler function information, we only have the request path
req := record.request
- logger.Trace("router: %s %v %s for %s", startMessage, log.ColoredMethod(req.Method), req.RequestURI, req.RemoteAddr)
+ logTrace("router: %s %v %s for %s", startMessage, log.ColoredMethod(req.Method), req.RequestURI, req.RemoteAddr)
return
}
@@ -60,9 +73,9 @@ func logPrinter(logger log.Logger) func(trigger Event, record *requestRecord) {
if trigger == StillExecutingEvent {
message := slowMessage
- logf := logger.Warn
+ logf := logWarn
if isLongPolling {
- logf = logger.Info
+ logf = logInfo
message = pollingMessage
}
logf("router: %s %v %s for %s, elapsed %v @ %s",
@@ -75,7 +88,7 @@ func logPrinter(logger log.Logger) func(trigger Event, record *requestRecord) {
}
if panicErr != nil {
- logger.Warn("router: %s %v %s for %s, panic in %v @ %s, err=%v",
+ logWarn("router: %s %v %s for %s, panic in %v @ %s, err=%v",
failedMessage,
log.ColoredMethod(req.Method), req.RequestURI, req.RemoteAddr,
log.ColoredTime(time.Since(record.startTime)),
@@ -89,13 +102,16 @@ func logPrinter(logger log.Logger) func(trigger Event, record *requestRecord) {
if v, ok := record.responseWriter.(types.ResponseStatusProvider); ok {
status = v.WrittenStatus()
}
- logf := logger.Info
- if strings.HasPrefix(req.RequestURI, "/assets/") {
- logf = logger.Trace
+ logf := logInfo
+ // lower the log level for some specific requests, in most cases these logs are not useful
+ if strings.HasPrefix(req.RequestURI, "/assets/") /* static assets */ ||
+ req.RequestURI == "/user/events" /* Server-Sent Events (SSE) handler */ ||
+ req.RequestURI == "/api/actions/runner.v1.RunnerService/FetchTask" /* Actions Runner polling */ {
+ logf = logTrace
}
message := completedMessage
if isUnknownHandler {
- logf = logger.Error
+ logf = logError
message = unknownHandlerMessage
}
diff --git a/modules/webhook/events.go b/modules/webhook/events.go
new file mode 100644
index 0000000000..f4dfff0294
--- /dev/null
+++ b/modules/webhook/events.go
@@ -0,0 +1,20 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package webhook
+
+type HookEvents map[HookEventType]bool
+
+func (he HookEvents) Get(evt HookEventType) bool {
+ return he[evt]
+}
+
+// HookEvent represents events that will delivery hook.
+type HookEvent struct {
+ PushOnly bool `json:"push_only"`
+ SendEverything bool `json:"send_everything"`
+ ChooseEvents bool `json:"choose_events"`
+ BranchFilter string `json:"branch_filter"`
+
+ HookEvents `json:"events"`
+}
diff --git a/modules/webhook/structs.go b/modules/webhook/structs.go
deleted file mode 100644
index 927a91a74c..0000000000
--- a/modules/webhook/structs.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package webhook
-
-// HookEvents is a set of web hook events
-type HookEvents struct {
- Create bool `json:"create"`
- Delete bool `json:"delete"`
- Fork bool `json:"fork"`
- Issues bool `json:"issues"`
- IssueAssign bool `json:"issue_assign"`
- IssueLabel bool `json:"issue_label"`
- IssueMilestone bool `json:"issue_milestone"`
- IssueComment bool `json:"issue_comment"`
- Push bool `json:"push"`
- PullRequest bool `json:"pull_request"`
- PullRequestAssign bool `json:"pull_request_assign"`
- PullRequestLabel bool `json:"pull_request_label"`
- PullRequestMilestone bool `json:"pull_request_milestone"`
- PullRequestComment bool `json:"pull_request_comment"`
- PullRequestReview bool `json:"pull_request_review"`
- PullRequestSync bool `json:"pull_request_sync"`
- PullRequestReviewRequest bool `json:"pull_request_review_request"`
- Wiki bool `json:"wiki"`
- Repository bool `json:"repository"`
- Release bool `json:"release"`
- Package bool `json:"package"`
-}
-
-// HookEvent represents events that will delivery hook.
-type HookEvent struct {
- PushOnly bool `json:"push_only"`
- SendEverything bool `json:"send_everything"`
- ChooseEvents bool `json:"choose_events"`
- BranchFilter string `json:"branch_filter"`
-
- HookEvents `json:"events"`
-}
diff --git a/modules/webhook/type.go b/modules/webhook/type.go
index fbec889272..89c6a4bfe5 100644
--- a/modules/webhook/type.go
+++ b/modules/webhook/type.go
@@ -31,21 +31,51 @@ const (
HookEventRepository HookEventType = "repository"
HookEventRelease HookEventType = "release"
HookEventPackage HookEventType = "package"
- HookEventSchedule HookEventType = "schedule"
HookEventStatus HookEventType = "status"
+ // once a new event added here, please also added to AllEvents() function
+
+ // FIXME: This event should be a group of pull_request_review_xxx events
+ HookEventPullRequestReview HookEventType = "pull_request_review"
+ // Actions event only
+ HookEventSchedule HookEventType = "schedule"
+ HookEventWorkflowRun HookEventType = "workflow_run"
+ HookEventWorkflowJob HookEventType = "workflow_job"
)
+func AllEvents() []HookEventType {
+ return []HookEventType{
+ HookEventCreate,
+ HookEventDelete,
+ HookEventFork,
+ HookEventPush,
+ HookEventIssues,
+ HookEventIssueAssign,
+ HookEventIssueLabel,
+ HookEventIssueMilestone,
+ HookEventIssueComment,
+ HookEventPullRequest,
+ HookEventPullRequestAssign,
+ HookEventPullRequestLabel,
+ HookEventPullRequestMilestone,
+ HookEventPullRequestComment,
+ HookEventPullRequestReviewApproved,
+ HookEventPullRequestReviewRejected,
+ HookEventPullRequestReviewComment,
+ HookEventPullRequestSync,
+ HookEventPullRequestReviewRequest,
+ HookEventWiki,
+ HookEventRepository,
+ HookEventRelease,
+ HookEventPackage,
+ HookEventStatus,
+ HookEventWorkflowRun,
+ HookEventWorkflowJob,
+ }
+}
+
// Event returns the HookEventType as an event string
func (h HookEventType) Event() string {
switch h {
- case HookEventCreate:
- return "create"
- case HookEventDelete:
- return "delete"
- case HookEventFork:
- return "fork"
- case HookEventPush:
- return "push"
case HookEventIssues, HookEventIssueAssign, HookEventIssueLabel, HookEventIssueMilestone:
return "issues"
case HookEventPullRequest, HookEventPullRequestAssign, HookEventPullRequestLabel, HookEventPullRequestMilestone,
@@ -59,14 +89,13 @@ func (h HookEventType) Event() string {
return "pull_request_rejected"
case HookEventPullRequestReviewComment:
return "pull_request_comment"
- case HookEventWiki:
- return "wiki"
- case HookEventRepository:
- return "repository"
- case HookEventRelease:
- return "release"
+ default:
+ return string(h)
}
- return ""
+}
+
+func (h HookEventType) IsPullRequest() bool {
+ return h.Event() == "pull_request"
}
// HookType is the type of a webhook
diff --git a/modules/zstd/zstd_test.go b/modules/zstd/zstd_test.go
index c3ca8e78f7..7fd30484ca 100644
--- a/modules/zstd/zstd_test.go
+++ b/modules/zstd/zstd_test.go
@@ -16,7 +16,7 @@ import (
)
func TestWriterReader(t *testing.T) {
- testData := prepareTestData(t, 20_000_000)
+ testData := prepareTestData(t, 1_000_000)
result := bytes.NewBuffer(nil)
@@ -64,7 +64,7 @@ func TestWriterReader(t *testing.T) {
}
func TestSeekableWriterReader(t *testing.T) {
- testData := prepareTestData(t, 20_000_000)
+ testData := prepareTestData(t, 2_000_000)
result := bytes.NewBuffer(nil)
@@ -109,7 +109,7 @@ func TestSeekableWriterReader(t *testing.T) {
reader, err := NewSeekableReader(assertReader)
require.NoError(t, err)
- _, err = reader.Seek(10_000_000, io.SeekStart)
+ _, err = reader.Seek(1_000_000, io.SeekStart)
require.NoError(t, err)
data := make([]byte, 1000)
@@ -117,7 +117,7 @@ func TestSeekableWriterReader(t *testing.T) {
require.NoError(t, err)
require.NoError(t, reader.Close())
- assert.Equal(t, testData[10_000_000:10_000_000+1000], data)
+ assert.Equal(t, testData[1_000_000:1_000_000+1000], data)
// Should seek 3 times,
// the first two times are for getting the index,
diff --git a/options/fileicon/material-icon-rules.json b/options/fileicon/material-icon-rules.json
new file mode 100644
index 0000000000..caa068ff44
--- /dev/null
+++ b/options/fileicon/material-icon-rules.json
@@ -0,0 +1,10007 @@
+{
+ "folderNames": {
+ "rust": "folder-rust",
+ ".rust": "folder-rust",
+ "_rust": "folder-rust",
+ "__rust__": "folder-rust",
+ "bot": "folder-robot",
+ ".bot": "folder-robot",
+ "_bot": "folder-robot",
+ "__bot__": "folder-robot",
+ "bots": "folder-robot",
+ ".bots": "folder-robot",
+ "_bots": "folder-robot",
+ "__bots__": "folder-robot",
+ "robot": "folder-robot",
+ ".robot": "folder-robot",
+ "_robot": "folder-robot",
+ "__robot__": "folder-robot",
+ "robots": "folder-robot",
+ ".robots": "folder-robot",
+ "_robots": "folder-robot",
+ "__robots__": "folder-robot",
+ "src": "folder-src",
+ ".src": "folder-src",
+ "_src": "folder-src",
+ "__src__": "folder-src",
+ "srcs": "folder-src",
+ ".srcs": "folder-src",
+ "_srcs": "folder-src",
+ "__srcs__": "folder-src",
+ "source": "folder-src",
+ ".source": "folder-src",
+ "_source": "folder-src",
+ "__source__": "folder-src",
+ "sources": "folder-src",
+ ".sources": "folder-src",
+ "_sources": "folder-src",
+ "__sources__": "folder-src",
+ "code": "folder-src",
+ ".code": "folder-src",
+ "_code": "folder-src",
+ "__code__": "folder-src",
+ "dist": "folder-dist",
+ ".dist": "folder-dist",
+ "_dist": "folder-dist",
+ "__dist__": "folder-dist",
+ "out": "folder-dist",
+ ".out": "folder-dist",
+ "_out": "folder-dist",
+ "__out__": "folder-dist",
+ "output": "folder-dist",
+ ".output": "folder-dist",
+ "_output": "folder-dist",
+ "__output__": "folder-dist",
+ "build": "folder-dist",
+ ".build": "folder-dist",
+ "_build": "folder-dist",
+ "__build__": "folder-dist",
+ "builds": "folder-dist",
+ ".builds": "folder-dist",
+ "_builds": "folder-dist",
+ "__builds__": "folder-dist",
+ "release": "folder-dist",
+ ".release": "folder-dist",
+ "_release": "folder-dist",
+ "__release__": "folder-dist",
+ "bin": "folder-dist",
+ ".bin": "folder-dist",
+ "_bin": "folder-dist",
+ "__bin__": "folder-dist",
+ "distribution": "folder-dist",
+ ".distribution": "folder-dist",
+ "_distribution": "folder-dist",
+ "__distribution__": "folder-dist",
+ "css": "folder-css",
+ ".css": "folder-css",
+ "_css": "folder-css",
+ "__css__": "folder-css",
+ "stylesheet": "folder-css",
+ ".stylesheet": "folder-css",
+ "_stylesheet": "folder-css",
+ "__stylesheet__": "folder-css",
+ "stylesheets": "folder-css",
+ ".stylesheets": "folder-css",
+ "_stylesheets": "folder-css",
+ "__stylesheets__": "folder-css",
+ "style": "folder-css",
+ ".style": "folder-css",
+ "_style": "folder-css",
+ "__style__": "folder-css",
+ "styles": "folder-css",
+ ".styles": "folder-css",
+ "_styles": "folder-css",
+ "__styles__": "folder-css",
+ "sass": "folder-sass",
+ ".sass": "folder-sass",
+ "_sass": "folder-sass",
+ "__sass__": "folder-sass",
+ "scss": "folder-sass",
+ ".scss": "folder-sass",
+ "_scss": "folder-sass",
+ "__scss__": "folder-sass",
+ "tv": "folder-television",
+ ".tv": "folder-television",
+ "_tv": "folder-television",
+ "__tv__": "folder-television",
+ "television": "folder-television",
+ ".television": "folder-television",
+ "_television": "folder-television",
+ "__television__": "folder-television",
+ "desktop": "folder-desktop",
+ ".desktop": "folder-desktop",
+ "_desktop": "folder-desktop",
+ "__desktop__": "folder-desktop",
+ "display": "folder-desktop",
+ ".display": "folder-desktop",
+ "_display": "folder-desktop",
+ "__display__": "folder-desktop",
+ "console": "folder-console",
+ ".console": "folder-console",
+ "_console": "folder-console",
+ "__console__": "folder-console",
+ "images": "folder-images",
+ ".images": "folder-images",
+ "_images": "folder-images",
+ "__images__": "folder-images",
+ "image": "folder-images",
+ ".image": "folder-images",
+ "_image": "folder-images",
+ "__image__": "folder-images",
+ "imgs": "folder-images",
+ ".imgs": "folder-images",
+ "_imgs": "folder-images",
+ "__imgs__": "folder-images",
+ "img": "folder-images",
+ ".img": "folder-images",
+ "_img": "folder-images",
+ "__img__": "folder-images",
+ "icons": "folder-images",
+ ".icons": "folder-images",
+ "_icons": "folder-images",
+ "__icons__": "folder-images",
+ "icon": "folder-images",
+ ".icon": "folder-images",
+ "_icon": "folder-images",
+ "__icon__": "folder-images",
+ "icos": "folder-images",
+ ".icos": "folder-images",
+ "_icos": "folder-images",
+ "__icos__": "folder-images",
+ "ico": "folder-images",
+ ".ico": "folder-images",
+ "_ico": "folder-images",
+ "__ico__": "folder-images",
+ "figures": "folder-images",
+ ".figures": "folder-images",
+ "_figures": "folder-images",
+ "__figures__": "folder-images",
+ "figure": "folder-images",
+ ".figure": "folder-images",
+ "_figure": "folder-images",
+ "__figure__": "folder-images",
+ "figs": "folder-images",
+ ".figs": "folder-images",
+ "_figs": "folder-images",
+ "__figs__": "folder-images",
+ "fig": "folder-images",
+ ".fig": "folder-images",
+ "_fig": "folder-images",
+ "__fig__": "folder-images",
+ "screenshot": "folder-images",
+ ".screenshot": "folder-images",
+ "_screenshot": "folder-images",
+ "__screenshot__": "folder-images",
+ "screenshots": "folder-images",
+ ".screenshots": "folder-images",
+ "_screenshots": "folder-images",
+ "__screenshots__": "folder-images",
+ "screengrab": "folder-images",
+ ".screengrab": "folder-images",
+ "_screengrab": "folder-images",
+ "__screengrab__": "folder-images",
+ "screengrabs": "folder-images",
+ ".screengrabs": "folder-images",
+ "_screengrabs": "folder-images",
+ "__screengrabs__": "folder-images",
+ "pic": "folder-images",
+ ".pic": "folder-images",
+ "_pic": "folder-images",
+ "__pic__": "folder-images",
+ "pics": "folder-images",
+ ".pics": "folder-images",
+ "_pics": "folder-images",
+ "__pics__": "folder-images",
+ "picture": "folder-images",
+ ".picture": "folder-images",
+ "_picture": "folder-images",
+ "__picture__": "folder-images",
+ "pictures": "folder-images",
+ ".pictures": "folder-images",
+ "_pictures": "folder-images",
+ "__pictures__": "folder-images",
+ "photo": "folder-images",
+ ".photo": "folder-images",
+ "_photo": "folder-images",
+ "__photo__": "folder-images",
+ "photos": "folder-images",
+ ".photos": "folder-images",
+ "_photos": "folder-images",
+ "__photos__": "folder-images",
+ "photograph": "folder-images",
+ ".photograph": "folder-images",
+ "_photograph": "folder-images",
+ "__photograph__": "folder-images",
+ "photographs": "folder-images",
+ ".photographs": "folder-images",
+ "_photographs": "folder-images",
+ "__photographs__": "folder-images",
+ "script": "folder-scripts",
+ ".script": "folder-scripts",
+ "_script": "folder-scripts",
+ "__script__": "folder-scripts",
+ "scripts": "folder-scripts",
+ ".scripts": "folder-scripts",
+ "_scripts": "folder-scripts",
+ "__scripts__": "folder-scripts",
+ "scripting": "folder-scripts",
+ ".scripting": "folder-scripts",
+ "_scripting": "folder-scripts",
+ "__scripting__": "folder-scripts",
+ "node": "folder-node",
+ ".node": "folder-node",
+ "_node": "folder-node",
+ "__node__": "folder-node",
+ "nodejs": "folder-node",
+ ".nodejs": "folder-node",
+ "_nodejs": "folder-node",
+ "__nodejs__": "folder-node",
+ "node_modules": "folder-node",
+ ".node_modules": "folder-node",
+ "_node_modules": "folder-node",
+ "__node_modules__": "folder-node",
+ "js": "folder-javascript",
+ ".js": "folder-javascript",
+ "_js": "folder-javascript",
+ "__js__": "folder-javascript",
+ "javascript": "folder-javascript",
+ ".javascript": "folder-javascript",
+ "_javascript": "folder-javascript",
+ "__javascript__": "folder-javascript",
+ "javascripts": "folder-javascript",
+ ".javascripts": "folder-javascript",
+ "_javascripts": "folder-javascript",
+ "__javascripts__": "folder-javascript",
+ "json": "folder-json",
+ ".json": "folder-json",
+ "_json": "folder-json",
+ "__json__": "folder-json",
+ "jsons": "folder-json",
+ ".jsons": "folder-json",
+ "_jsons": "folder-json",
+ "__jsons__": "folder-json",
+ "font": "folder-font",
+ ".font": "folder-font",
+ "_font": "folder-font",
+ "__font__": "folder-font",
+ "fonts": "folder-font",
+ ".fonts": "folder-font",
+ "_fonts": "folder-font",
+ "__fonts__": "folder-font",
+ "bower_components": "folder-bower",
+ ".bower_components": "folder-bower",
+ "_bower_components": "folder-bower",
+ "__bower_components__": "folder-bower",
+ "test": "folder-test",
+ ".test": "folder-test",
+ "_test": "folder-test",
+ "__test__": "folder-test",
+ "tests": "folder-test",
+ ".tests": "folder-test",
+ "_tests": "folder-test",
+ "__tests__": "folder-test",
+ "testing": "folder-test",
+ ".testing": "folder-test",
+ "_testing": "folder-test",
+ "__testing__": "folder-test",
+ "snapshots": "folder-test",
+ ".snapshots": "folder-test",
+ "_snapshots": "folder-test",
+ "__snapshots__": "folder-test",
+ "spec": "folder-test",
+ ".spec": "folder-test",
+ "_spec": "folder-test",
+ "__spec__": "folder-test",
+ "specs": "folder-test",
+ ".specs": "folder-test",
+ "_specs": "folder-test",
+ "__specs__": "folder-test",
+ "directive": "folder-directive",
+ ".directive": "folder-directive",
+ "_directive": "folder-directive",
+ "__directive__": "folder-directive",
+ "directives": "folder-directive",
+ ".directives": "folder-directive",
+ "_directives": "folder-directive",
+ "__directives__": "folder-directive",
+ "jinja": "folder-jinja",
+ ".jinja": "folder-jinja",
+ "_jinja": "folder-jinja",
+ "__jinja__": "folder-jinja",
+ "jinja2": "folder-jinja",
+ ".jinja2": "folder-jinja",
+ "_jinja2": "folder-jinja",
+ "__jinja2__": "folder-jinja",
+ "j2": "folder-jinja",
+ ".j2": "folder-jinja",
+ "_j2": "folder-jinja",
+ "__j2__": "folder-jinja",
+ "markdown": "folder-markdown",
+ ".markdown": "folder-markdown",
+ "_markdown": "folder-markdown",
+ "__markdown__": "folder-markdown",
+ "md": "folder-markdown",
+ ".md": "folder-markdown",
+ "_md": "folder-markdown",
+ "__md__": "folder-markdown",
+ "pdm-plugins": "folder-pdm",
+ ".pdm-plugins": "folder-pdm",
+ "_pdm-plugins": "folder-pdm",
+ "__pdm-plugins__": "folder-pdm",
+ "pdm-build": "folder-pdm",
+ ".pdm-build": "folder-pdm",
+ "_pdm-build": "folder-pdm",
+ "__pdm-build__": "folder-pdm",
+ "php": "folder-php",
+ ".php": "folder-php",
+ "_php": "folder-php",
+ "__php__": "folder-php",
+ "phpmailer": "folder-phpmailer",
+ ".phpmailer": "folder-phpmailer",
+ "_phpmailer": "folder-phpmailer",
+ "__phpmailer__": "folder-phpmailer",
+ "sublime": "folder-sublime",
+ ".sublime": "folder-sublime",
+ "_sublime": "folder-sublime",
+ "__sublime__": "folder-sublime",
+ "doc": "folder-docs",
+ ".doc": "folder-docs",
+ "_doc": "folder-docs",
+ "__doc__": "folder-docs",
+ "docs": "folder-docs",
+ ".docs": "folder-docs",
+ "_docs": "folder-docs",
+ "__docs__": "folder-docs",
+ "document": "folder-docs",
+ ".document": "folder-docs",
+ "_document": "folder-docs",
+ "__document__": "folder-docs",
+ "documents": "folder-docs",
+ ".documents": "folder-docs",
+ "_documents": "folder-docs",
+ "__documents__": "folder-docs",
+ "documentation": "folder-docs",
+ ".documentation": "folder-docs",
+ "_documentation": "folder-docs",
+ "__documentation__": "folder-docs",
+ "post": "folder-docs",
+ ".post": "folder-docs",
+ "_post": "folder-docs",
+ "__post__": "folder-docs",
+ "posts": "folder-docs",
+ ".posts": "folder-docs",
+ "_posts": "folder-docs",
+ "__posts__": "folder-docs",
+ "article": "folder-docs",
+ ".article": "folder-docs",
+ "_article": "folder-docs",
+ "__article__": "folder-docs",
+ "articles": "folder-docs",
+ ".articles": "folder-docs",
+ "_articles": "folder-docs",
+ "__articles__": "folder-docs",
+ "wiki": "folder-docs",
+ ".wiki": "folder-docs",
+ "_wiki": "folder-docs",
+ "__wiki__": "folder-docs",
+ "news": "folder-docs",
+ ".news": "folder-docs",
+ "_news": "folder-docs",
+ "__news__": "folder-docs",
+ "github/workflows": "folder-gh-workflows",
+ ".github/workflows": "folder-gh-workflows",
+ "_github/workflows": "folder-gh-workflows",
+ "__github/workflows__": "folder-gh-workflows",
+ "git": "folder-git",
+ ".git": "folder-git",
+ "_git": "folder-git",
+ "__git__": "folder-git",
+ "patches": "folder-git",
+ ".patches": "folder-git",
+ "_patches": "folder-git",
+ "__patches__": "folder-git",
+ "githooks": "folder-git",
+ ".githooks": "folder-git",
+ "_githooks": "folder-git",
+ "__githooks__": "folder-git",
+ "submodules": "folder-git",
+ ".submodules": "folder-git",
+ "_submodules": "folder-git",
+ "__submodules__": "folder-git",
+ "github": "folder-github",
+ ".github": "folder-github",
+ "_github": "folder-github",
+ "__github__": "folder-github",
+ "gitea": "folder-gitea",
+ ".gitea": "folder-gitea",
+ "_gitea": "folder-gitea",
+ "__gitea__": "folder-gitea",
+ "gitlab": "folder-gitlab",
+ ".gitlab": "folder-gitlab",
+ "_gitlab": "folder-gitlab",
+ "__gitlab__": "folder-gitlab",
+ "forgejo": "folder-forgejo",
+ ".forgejo": "folder-forgejo",
+ "_forgejo": "folder-forgejo",
+ "__forgejo__": "folder-forgejo",
+ "vscode": "folder-vscode",
+ ".vscode": "folder-vscode",
+ "_vscode": "folder-vscode",
+ "__vscode__": "folder-vscode",
+ "vscode-test": "folder-vscode",
+ ".vscode-test": "folder-vscode",
+ "_vscode-test": "folder-vscode",
+ "__vscode-test__": "folder-vscode",
+ "view": "folder-views",
+ ".view": "folder-views",
+ "_view": "folder-views",
+ "__view__": "folder-views",
+ "views": "folder-views",
+ ".views": "folder-views",
+ "_views": "folder-views",
+ "__views__": "folder-views",
+ "screen": "folder-views",
+ ".screen": "folder-views",
+ "_screen": "folder-views",
+ "__screen__": "folder-views",
+ "screens": "folder-views",
+ ".screens": "folder-views",
+ "_screens": "folder-views",
+ "__screens__": "folder-views",
+ "page": "folder-views",
+ ".page": "folder-views",
+ "_page": "folder-views",
+ "__page__": "folder-views",
+ "pages": "folder-views",
+ ".pages": "folder-views",
+ "_pages": "folder-views",
+ "__pages__": "folder-views",
+ "public_html": "folder-views",
+ ".public_html": "folder-views",
+ "_public_html": "folder-views",
+ "__public_html__": "folder-views",
+ "html": "folder-views",
+ ".html": "folder-views",
+ "_html": "folder-views",
+ "__html__": "folder-views",
+ "vue": "folder-vue",
+ ".vue": "folder-vue",
+ "_vue": "folder-vue",
+ "__vue__": "folder-vue",
+ "vuepress": "folder-vuepress",
+ ".vuepress": "folder-vuepress",
+ "_vuepress": "folder-vuepress",
+ "__vuepress__": "folder-vuepress",
+ "expo": "folder-expo",
+ ".expo": "folder-expo",
+ "_expo": "folder-expo",
+ "__expo__": "folder-expo",
+ "expo-shared": "folder-expo",
+ ".expo-shared": "folder-expo",
+ "_expo-shared": "folder-expo",
+ "__expo-shared__": "folder-expo",
+ "cfg": "folder-config",
+ ".cfg": "folder-config",
+ "_cfg": "folder-config",
+ "__cfg__": "folder-config",
+ "cfgs": "folder-config",
+ ".cfgs": "folder-config",
+ "_cfgs": "folder-config",
+ "__cfgs__": "folder-config",
+ "conf": "folder-config",
+ ".conf": "folder-config",
+ "_conf": "folder-config",
+ "__conf__": "folder-config",
+ "confs": "folder-config",
+ ".confs": "folder-config",
+ "_confs": "folder-config",
+ "__confs__": "folder-config",
+ "config": "folder-config",
+ ".config": "folder-config",
+ "_config": "folder-config",
+ "__config__": "folder-config",
+ "configs": "folder-config",
+ ".configs": "folder-config",
+ "_configs": "folder-config",
+ "__configs__": "folder-config",
+ "configuration": "folder-config",
+ ".configuration": "folder-config",
+ "_configuration": "folder-config",
+ "__configuration__": "folder-config",
+ "configurations": "folder-config",
+ ".configurations": "folder-config",
+ "_configurations": "folder-config",
+ "__configurations__": "folder-config",
+ "setting": "folder-config",
+ ".setting": "folder-config",
+ "_setting": "folder-config",
+ "__setting__": "folder-config",
+ "settings": "folder-config",
+ ".settings": "folder-config",
+ "_settings": "folder-config",
+ "__settings__": "folder-config",
+ "META-INF": "folder-config",
+ ".META-INF": "folder-config",
+ "_META-INF": "folder-config",
+ "__META-INF__": "folder-config",
+ "option": "folder-config",
+ ".option": "folder-config",
+ "_option": "folder-config",
+ "__option__": "folder-config",
+ "options": "folder-config",
+ ".options": "folder-config",
+ "_options": "folder-config",
+ "__options__": "folder-config",
+ "pref": "folder-config",
+ ".pref": "folder-config",
+ "_pref": "folder-config",
+ "__pref__": "folder-config",
+ "prefs": "folder-config",
+ ".prefs": "folder-config",
+ "_prefs": "folder-config",
+ "__prefs__": "folder-config",
+ "preference": "folder-config",
+ ".preference": "folder-config",
+ "_preference": "folder-config",
+ "__preference__": "folder-config",
+ "preferences": "folder-config",
+ ".preferences": "folder-config",
+ "_preferences": "folder-config",
+ "__preferences__": "folder-config",
+ "i18n": "folder-i18n",
+ ".i18n": "folder-i18n",
+ "_i18n": "folder-i18n",
+ "__i18n__": "folder-i18n",
+ "internationalization": "folder-i18n",
+ ".internationalization": "folder-i18n",
+ "_internationalization": "folder-i18n",
+ "__internationalization__": "folder-i18n",
+ "lang": "folder-i18n",
+ ".lang": "folder-i18n",
+ "_lang": "folder-i18n",
+ "__lang__": "folder-i18n",
+ "langs": "folder-i18n",
+ ".langs": "folder-i18n",
+ "_langs": "folder-i18n",
+ "__langs__": "folder-i18n",
+ "language": "folder-i18n",
+ ".language": "folder-i18n",
+ "_language": "folder-i18n",
+ "__language__": "folder-i18n",
+ "languages": "folder-i18n",
+ ".languages": "folder-i18n",
+ "_languages": "folder-i18n",
+ "__languages__": "folder-i18n",
+ "locale": "folder-i18n",
+ ".locale": "folder-i18n",
+ "_locale": "folder-i18n",
+ "__locale__": "folder-i18n",
+ "locales": "folder-i18n",
+ ".locales": "folder-i18n",
+ "_locales": "folder-i18n",
+ "__locales__": "folder-i18n",
+ "l10n": "folder-i18n",
+ ".l10n": "folder-i18n",
+ "_l10n": "folder-i18n",
+ "__l10n__": "folder-i18n",
+ "localization": "folder-i18n",
+ ".localization": "folder-i18n",
+ "_localization": "folder-i18n",
+ "__localization__": "folder-i18n",
+ "translation": "folder-i18n",
+ ".translation": "folder-i18n",
+ "_translation": "folder-i18n",
+ "__translation__": "folder-i18n",
+ "translate": "folder-i18n",
+ ".translate": "folder-i18n",
+ "_translate": "folder-i18n",
+ "__translate__": "folder-i18n",
+ "translations": "folder-i18n",
+ ".translations": "folder-i18n",
+ "_translations": "folder-i18n",
+ "__translations__": "folder-i18n",
+ "tx": "folder-i18n",
+ ".tx": "folder-i18n",
+ "_tx": "folder-i18n",
+ "__tx__": "folder-i18n",
+ "components": "folder-components",
+ ".components": "folder-components",
+ "_components": "folder-components",
+ "__components__": "folder-components",
+ "widget": "folder-components",
+ ".widget": "folder-components",
+ "_widget": "folder-components",
+ "__widget__": "folder-components",
+ "widgets": "folder-components",
+ ".widgets": "folder-components",
+ "_widgets": "folder-components",
+ "__widgets__": "folder-components",
+ "fragments": "folder-components",
+ ".fragments": "folder-components",
+ "_fragments": "folder-components",
+ "__fragments__": "folder-components",
+ "verdaccio": "folder-verdaccio",
+ ".verdaccio": "folder-verdaccio",
+ "_verdaccio": "folder-verdaccio",
+ "__verdaccio__": "folder-verdaccio",
+ "aurelia_project": "folder-aurelia",
+ ".aurelia_project": "folder-aurelia",
+ "_aurelia_project": "folder-aurelia",
+ "__aurelia_project__": "folder-aurelia",
+ "resource": "folder-resource",
+ ".resource": "folder-resource",
+ "_resource": "folder-resource",
+ "__resource__": "folder-resource",
+ "resources": "folder-resource",
+ ".resources": "folder-resource",
+ "_resources": "folder-resource",
+ "__resources__": "folder-resource",
+ "res": "folder-resource",
+ ".res": "folder-resource",
+ "_res": "folder-resource",
+ "__res__": "folder-resource",
+ "asset": "folder-resource",
+ ".asset": "folder-resource",
+ "_asset": "folder-resource",
+ "__asset__": "folder-resource",
+ "assets": "folder-resource",
+ ".assets": "folder-resource",
+ "_assets": "folder-resource",
+ "__assets__": "folder-resource",
+ "static": "folder-resource",
+ ".static": "folder-resource",
+ "_static": "folder-resource",
+ "__static__": "folder-resource",
+ "report": "folder-resource",
+ ".report": "folder-resource",
+ "_report": "folder-resource",
+ "__report__": "folder-resource",
+ "reports": "folder-resource",
+ ".reports": "folder-resource",
+ "_reports": "folder-resource",
+ "__reports__": "folder-resource",
+ "lib": "folder-lib",
+ ".lib": "folder-lib",
+ "_lib": "folder-lib",
+ "__lib__": "folder-lib",
+ "libs": "folder-lib",
+ ".libs": "folder-lib",
+ "_libs": "folder-lib",
+ "__libs__": "folder-lib",
+ "library": "folder-lib",
+ ".library": "folder-lib",
+ "_library": "folder-lib",
+ "__library__": "folder-lib",
+ "libraries": "folder-lib",
+ ".libraries": "folder-lib",
+ "_libraries": "folder-lib",
+ "__libraries__": "folder-lib",
+ "vendor": "folder-lib",
+ ".vendor": "folder-lib",
+ "_vendor": "folder-lib",
+ "__vendor__": "folder-lib",
+ "vendors": "folder-lib",
+ ".vendors": "folder-lib",
+ "_vendors": "folder-lib",
+ "__vendors__": "folder-lib",
+ "third-party": "folder-lib",
+ ".third-party": "folder-lib",
+ "_third-party": "folder-lib",
+ "__third-party__": "folder-lib",
+ "lib64": "folder-lib",
+ ".lib64": "folder-lib",
+ "_lib64": "folder-lib",
+ "__lib64__": "folder-lib",
+ "themes": "folder-theme",
+ ".themes": "folder-theme",
+ "_themes": "folder-theme",
+ "__themes__": "folder-theme",
+ "theme": "folder-theme",
+ ".theme": "folder-theme",
+ "_theme": "folder-theme",
+ "__theme__": "folder-theme",
+ "color": "folder-theme",
+ ".color": "folder-theme",
+ "_color": "folder-theme",
+ "__color__": "folder-theme",
+ "colors": "folder-theme",
+ ".colors": "folder-theme",
+ "_colors": "folder-theme",
+ "__colors__": "folder-theme",
+ "design": "folder-theme",
+ ".design": "folder-theme",
+ "_design": "folder-theme",
+ "__design__": "folder-theme",
+ "designs": "folder-theme",
+ ".designs": "folder-theme",
+ "_designs": "folder-theme",
+ "__designs__": "folder-theme",
+ "palette": "folder-theme",
+ ".palette": "folder-theme",
+ "_palette": "folder-theme",
+ "__palette__": "folder-theme",
+ "palettes": "folder-theme",
+ ".palettes": "folder-theme",
+ "_palettes": "folder-theme",
+ "__palettes__": "folder-theme",
+ "webpack": "folder-webpack",
+ ".webpack": "folder-webpack",
+ "_webpack": "folder-webpack",
+ "__webpack__": "folder-webpack",
+ "global": "folder-global",
+ ".global": "folder-global",
+ "_global": "folder-global",
+ "__global__": "folder-global",
+ "public": "folder-public",
+ ".public": "folder-public",
+ "_public": "folder-public",
+ "__public__": "folder-public",
+ "www": "folder-public",
+ ".www": "folder-public",
+ "_www": "folder-public",
+ "__www__": "folder-public",
+ "wwwroot": "folder-public",
+ ".wwwroot": "folder-public",
+ "_wwwroot": "folder-public",
+ "__wwwroot__": "folder-public",
+ "web": "folder-public",
+ ".web": "folder-public",
+ "_web": "folder-public",
+ "__web__": "folder-public",
+ "website": "folder-public",
+ ".website": "folder-public",
+ "_website": "folder-public",
+ "__website__": "folder-public",
+ "websites": "folder-public",
+ ".websites": "folder-public",
+ "_websites": "folder-public",
+ "__websites__": "folder-public",
+ "site": "folder-public",
+ ".site": "folder-public",
+ "_site": "folder-public",
+ "__site__": "folder-public",
+ "browser": "folder-public",
+ ".browser": "folder-public",
+ "_browser": "folder-public",
+ "__browser__": "folder-public",
+ "browsers": "folder-public",
+ ".browsers": "folder-public",
+ "_browsers": "folder-public",
+ "__browsers__": "folder-public",
+ "inc": "folder-include",
+ ".inc": "folder-include",
+ "_inc": "folder-include",
+ "__inc__": "folder-include",
+ "include": "folder-include",
+ ".include": "folder-include",
+ "_include": "folder-include",
+ "__include__": "folder-include",
+ "includes": "folder-include",
+ ".includes": "folder-include",
+ "_includes": "folder-include",
+ "__includes__": "folder-include",
+ "partial": "folder-include",
+ ".partial": "folder-include",
+ "_partial": "folder-include",
+ "__partial__": "folder-include",
+ "partials": "folder-include",
+ ".partials": "folder-include",
+ "_partials": "folder-include",
+ "__partials__": "folder-include",
+ "inc64": "folder-include",
+ ".inc64": "folder-include",
+ "_inc64": "folder-include",
+ "__inc64__": "folder-include",
+ "docker": "folder-docker",
+ ".docker": "folder-docker",
+ "_docker": "folder-docker",
+ "__docker__": "folder-docker",
+ "dockerfiles": "folder-docker",
+ ".dockerfiles": "folder-docker",
+ "_dockerfiles": "folder-docker",
+ "__dockerfiles__": "folder-docker",
+ "dockerhub": "folder-docker",
+ ".dockerhub": "folder-docker",
+ "_dockerhub": "folder-docker",
+ "__dockerhub__": "folder-docker",
+ "astro": "folder-astro",
+ ".astro": "folder-astro",
+ "_astro": "folder-astro",
+ "__astro__": "folder-astro",
+ "db": "folder-database",
+ ".db": "folder-database",
+ "_db": "folder-database",
+ "__db__": "folder-database",
+ "data": "folder-database",
+ ".data": "folder-database",
+ "_data": "folder-database",
+ "__data__": "folder-database",
+ "database": "folder-database",
+ ".database": "folder-database",
+ "_database": "folder-database",
+ "__database__": "folder-database",
+ "databases": "folder-database",
+ ".databases": "folder-database",
+ "_databases": "folder-database",
+ "__databases__": "folder-database",
+ "sql": "folder-database",
+ ".sql": "folder-database",
+ "_sql": "folder-database",
+ "__sql__": "folder-database",
+ "log": "folder-log",
+ ".log": "folder-log",
+ "_log": "folder-log",
+ "__log__": "folder-log",
+ "logs": "folder-log",
+ ".logs": "folder-log",
+ "_logs": "folder-log",
+ "__logs__": "folder-log",
+ "logging": "folder-log",
+ ".logging": "folder-log",
+ "_logging": "folder-log",
+ "__logging__": "folder-log",
+ "target": "folder-target",
+ ".target": "folder-target",
+ "_target": "folder-target",
+ "__target__": "folder-target",
+ "temp": "folder-temp",
+ ".temp": "folder-temp",
+ "_temp": "folder-temp",
+ "__temp__": "folder-temp",
+ "tmp": "folder-temp",
+ ".tmp": "folder-temp",
+ "_tmp": "folder-temp",
+ "__tmp__": "folder-temp",
+ "cached": "folder-temp",
+ ".cached": "folder-temp",
+ "_cached": "folder-temp",
+ "__cached__": "folder-temp",
+ "cache": "folder-temp",
+ ".cache": "folder-temp",
+ "_cache": "folder-temp",
+ "__cache__": "folder-temp",
+ "aws": "folder-aws",
+ ".aws": "folder-aws",
+ "_aws": "folder-aws",
+ "__aws__": "folder-aws",
+ "azure": "folder-aws",
+ ".azure": "folder-aws",
+ "_azure": "folder-aws",
+ "__azure__": "folder-aws",
+ "gcp": "folder-aws",
+ ".gcp": "folder-aws",
+ "_gcp": "folder-aws",
+ "__gcp__": "folder-aws",
+ "aud": "folder-audio",
+ ".aud": "folder-audio",
+ "_aud": "folder-audio",
+ "__aud__": "folder-audio",
+ "auds": "folder-audio",
+ ".auds": "folder-audio",
+ "_auds": "folder-audio",
+ "__auds__": "folder-audio",
+ "audio": "folder-audio",
+ ".audio": "folder-audio",
+ "_audio": "folder-audio",
+ "__audio__": "folder-audio",
+ "audios": "folder-audio",
+ ".audios": "folder-audio",
+ "_audios": "folder-audio",
+ "__audios__": "folder-audio",
+ "music": "folder-audio",
+ ".music": "folder-audio",
+ "_music": "folder-audio",
+ "__music__": "folder-audio",
+ "song": "folder-audio",
+ ".song": "folder-audio",
+ "_song": "folder-audio",
+ "__song__": "folder-audio",
+ "songs": "folder-audio",
+ ".songs": "folder-audio",
+ "_songs": "folder-audio",
+ "__songs__": "folder-audio",
+ "sound": "folder-audio",
+ ".sound": "folder-audio",
+ "_sound": "folder-audio",
+ "__sound__": "folder-audio",
+ "sounds": "folder-audio",
+ ".sounds": "folder-audio",
+ "_sounds": "folder-audio",
+ "__sounds__": "folder-audio",
+ "voice": "folder-audio",
+ ".voice": "folder-audio",
+ "_voice": "folder-audio",
+ "__voice__": "folder-audio",
+ "voices": "folder-audio",
+ ".voices": "folder-audio",
+ "_voices": "folder-audio",
+ "__voices__": "folder-audio",
+ "recordings": "folder-audio",
+ ".recordings": "folder-audio",
+ "_recordings": "folder-audio",
+ "__recordings__": "folder-audio",
+ "vid": "folder-video",
+ ".vid": "folder-video",
+ "_vid": "folder-video",
+ "__vid__": "folder-video",
+ "vids": "folder-video",
+ ".vids": "folder-video",
+ "_vids": "folder-video",
+ "__vids__": "folder-video",
+ "video": "folder-video",
+ ".video": "folder-video",
+ "_video": "folder-video",
+ "__video__": "folder-video",
+ "videos": "folder-video",
+ ".videos": "folder-video",
+ "_videos": "folder-video",
+ "__videos__": "folder-video",
+ "movie": "folder-video",
+ ".movie": "folder-video",
+ "_movie": "folder-video",
+ "__movie__": "folder-video",
+ "movies": "folder-video",
+ ".movies": "folder-video",
+ "_movies": "folder-video",
+ "__movies__": "folder-video",
+ "media": "folder-video",
+ ".media": "folder-video",
+ "_media": "folder-video",
+ "__media__": "folder-video",
+ "kubernetes": "folder-kubernetes",
+ ".kubernetes": "folder-kubernetes",
+ "_kubernetes": "folder-kubernetes",
+ "__kubernetes__": "folder-kubernetes",
+ "k8s": "folder-kubernetes",
+ ".k8s": "folder-kubernetes",
+ "_k8s": "folder-kubernetes",
+ "__k8s__": "folder-kubernetes",
+ "import": "folder-import",
+ ".import": "folder-import",
+ "_import": "folder-import",
+ "__import__": "folder-import",
+ "imports": "folder-import",
+ ".imports": "folder-import",
+ "_imports": "folder-import",
+ "__imports__": "folder-import",
+ "imported": "folder-import",
+ ".imported": "folder-import",
+ "_imported": "folder-import",
+ "__imported__": "folder-import",
+ "export": "folder-export",
+ ".export": "folder-export",
+ "_export": "folder-export",
+ "__export__": "folder-export",
+ "exports": "folder-export",
+ ".exports": "folder-export",
+ "_exports": "folder-export",
+ "__exports__": "folder-export",
+ "exported": "folder-export",
+ ".exported": "folder-export",
+ "_exported": "folder-export",
+ "__exported__": "folder-export",
+ "wakatime": "folder-wakatime",
+ ".wakatime": "folder-wakatime",
+ "_wakatime": "folder-wakatime",
+ "__wakatime__": "folder-wakatime",
+ "circleci": "folder-circleci",
+ ".circleci": "folder-circleci",
+ "_circleci": "folder-circleci",
+ "__circleci__": "folder-circleci",
+ "wordpress-org": "folder-wordpress",
+ ".wordpress-org": "folder-wordpress",
+ "_wordpress-org": "folder-wordpress",
+ "__wordpress-org__": "folder-wordpress",
+ "wp-content": "folder-wordpress",
+ ".wp-content": "folder-wordpress",
+ "_wp-content": "folder-wordpress",
+ "__wp-content__": "folder-wordpress",
+ "gradle": "folder-gradle",
+ ".gradle": "folder-gradle",
+ "_gradle": "folder-gradle",
+ "__gradle__": "folder-gradle",
+ "coverage": "folder-coverage",
+ ".coverage": "folder-coverage",
+ "_coverage": "folder-coverage",
+ "__coverage__": "folder-coverage",
+ "nyc-output": "folder-coverage",
+ ".nyc-output": "folder-coverage",
+ "_nyc-output": "folder-coverage",
+ "__nyc-output__": "folder-coverage",
+ "nyc_output": "folder-coverage",
+ ".nyc_output": "folder-coverage",
+ "_nyc_output": "folder-coverage",
+ "__nyc_output__": "folder-coverage",
+ "e2e": "folder-coverage",
+ ".e2e": "folder-coverage",
+ "_e2e": "folder-coverage",
+ "__e2e__": "folder-coverage",
+ "it": "folder-coverage",
+ ".it": "folder-coverage",
+ "_it": "folder-coverage",
+ "__it__": "folder-coverage",
+ "integration-test": "folder-coverage",
+ ".integration-test": "folder-coverage",
+ "_integration-test": "folder-coverage",
+ "__integration-test__": "folder-coverage",
+ "integration-tests": "folder-coverage",
+ ".integration-tests": "folder-coverage",
+ "_integration-tests": "folder-coverage",
+ "__integration-tests__": "folder-coverage",
+ "class": "folder-class",
+ ".class": "folder-class",
+ "_class": "folder-class",
+ "__class__": "folder-class",
+ "classes": "folder-class",
+ ".classes": "folder-class",
+ "_classes": "folder-class",
+ "__classes__": "folder-class",
+ "model": "folder-class",
+ ".model": "folder-class",
+ "_model": "folder-class",
+ "__model__": "folder-class",
+ "models": "folder-class",
+ ".models": "folder-class",
+ "_models": "folder-class",
+ "__models__": "folder-class",
+ "schemas": "folder-class",
+ ".schemas": "folder-class",
+ "_schemas": "folder-class",
+ "__schemas__": "folder-class",
+ "schema": "folder-class",
+ ".schema": "folder-class",
+ "_schema": "folder-class",
+ "__schema__": "folder-class",
+ "other": "folder-other",
+ ".other": "folder-other",
+ "_other": "folder-other",
+ "__other__": "folder-other",
+ "others": "folder-other",
+ ".others": "folder-other",
+ "_others": "folder-other",
+ "__others__": "folder-other",
+ "misc": "folder-other",
+ ".misc": "folder-other",
+ "_misc": "folder-other",
+ "__misc__": "folder-other",
+ "miscellaneous": "folder-other",
+ ".miscellaneous": "folder-other",
+ "_miscellaneous": "folder-other",
+ "__miscellaneous__": "folder-other",
+ "extra": "folder-other",
+ ".extra": "folder-other",
+ "_extra": "folder-other",
+ "__extra__": "folder-other",
+ "extras": "folder-other",
+ ".extras": "folder-other",
+ "_extras": "folder-other",
+ "__extras__": "folder-other",
+ "etc": "folder-other",
+ ".etc": "folder-other",
+ "_etc": "folder-other",
+ "__etc__": "folder-other",
+ "lua": "folder-lua",
+ ".lua": "folder-lua",
+ "_lua": "folder-lua",
+ "__lua__": "folder-lua",
+ "turbo": "folder-turborepo",
+ ".turbo": "folder-turborepo",
+ "_turbo": "folder-turborepo",
+ "__turbo__": "folder-turborepo",
+ "typescript": "folder-typescript",
+ ".typescript": "folder-typescript",
+ "_typescript": "folder-typescript",
+ "__typescript__": "folder-typescript",
+ "ts": "folder-typescript",
+ ".ts": "folder-typescript",
+ "_ts": "folder-typescript",
+ "__ts__": "folder-typescript",
+ "typings": "folder-typescript",
+ ".typings": "folder-typescript",
+ "_typings": "folder-typescript",
+ "__typings__": "folder-typescript",
+ "@types": "folder-typescript",
+ ".@types": "folder-typescript",
+ "_@types": "folder-typescript",
+ "__@types__": "folder-typescript",
+ "types": "folder-typescript",
+ ".types": "folder-typescript",
+ "_types": "folder-typescript",
+ "__types__": "folder-typescript",
+ "graphql": "folder-graphql",
+ ".graphql": "folder-graphql",
+ "_graphql": "folder-graphql",
+ "__graphql__": "folder-graphql",
+ "gql": "folder-graphql",
+ ".gql": "folder-graphql",
+ "_gql": "folder-graphql",
+ "__gql__": "folder-graphql",
+ "routes": "folder-routes",
+ ".routes": "folder-routes",
+ "_routes": "folder-routes",
+ "__routes__": "folder-routes",
+ "router": "folder-routes",
+ ".router": "folder-routes",
+ "_router": "folder-routes",
+ "__router__": "folder-routes",
+ "routers": "folder-routes",
+ ".routers": "folder-routes",
+ "_routers": "folder-routes",
+ "__routers__": "folder-routes",
+ "navigation": "folder-routes",
+ ".navigation": "folder-routes",
+ "_navigation": "folder-routes",
+ "__navigation__": "folder-routes",
+ "navigations": "folder-routes",
+ ".navigations": "folder-routes",
+ "_navigations": "folder-routes",
+ "__navigations__": "folder-routes",
+ "routing": "folder-routes",
+ ".routing": "folder-routes",
+ "_routing": "folder-routes",
+ "__routing__": "folder-routes",
+ "ci": "folder-ci",
+ ".ci": "folder-ci",
+ "_ci": "folder-ci",
+ "__ci__": "folder-ci",
+ "benchmark": "folder-benchmark",
+ ".benchmark": "folder-benchmark",
+ "_benchmark": "folder-benchmark",
+ "__benchmark__": "folder-benchmark",
+ "benchmarks": "folder-benchmark",
+ ".benchmarks": "folder-benchmark",
+ "_benchmarks": "folder-benchmark",
+ "__benchmarks__": "folder-benchmark",
+ "bench": "folder-benchmark",
+ ".bench": "folder-benchmark",
+ "_bench": "folder-benchmark",
+ "__bench__": "folder-benchmark",
+ "performance": "folder-benchmark",
+ ".performance": "folder-benchmark",
+ "_performance": "folder-benchmark",
+ "__performance__": "folder-benchmark",
+ "perf": "folder-benchmark",
+ ".perf": "folder-benchmark",
+ "_perf": "folder-benchmark",
+ "__perf__": "folder-benchmark",
+ "profiling": "folder-benchmark",
+ ".profiling": "folder-benchmark",
+ "_profiling": "folder-benchmark",
+ "__profiling__": "folder-benchmark",
+ "measure": "folder-benchmark",
+ ".measure": "folder-benchmark",
+ "_measure": "folder-benchmark",
+ "__measure__": "folder-benchmark",
+ "measures": "folder-benchmark",
+ ".measures": "folder-benchmark",
+ "_measures": "folder-benchmark",
+ "__measures__": "folder-benchmark",
+ "measurement": "folder-benchmark",
+ ".measurement": "folder-benchmark",
+ "_measurement": "folder-benchmark",
+ "__measurement__": "folder-benchmark",
+ "messages": "folder-messages",
+ ".messages": "folder-messages",
+ "_messages": "folder-messages",
+ "__messages__": "folder-messages",
+ "messaging": "folder-messages",
+ ".messaging": "folder-messages",
+ "_messaging": "folder-messages",
+ "__messaging__": "folder-messages",
+ "forum": "folder-messages",
+ ".forum": "folder-messages",
+ "_forum": "folder-messages",
+ "__forum__": "folder-messages",
+ "chat": "folder-messages",
+ ".chat": "folder-messages",
+ "_chat": "folder-messages",
+ "__chat__": "folder-messages",
+ "chats": "folder-messages",
+ ".chats": "folder-messages",
+ "_chats": "folder-messages",
+ "__chats__": "folder-messages",
+ "conversation": "folder-messages",
+ ".conversation": "folder-messages",
+ "_conversation": "folder-messages",
+ "__conversation__": "folder-messages",
+ "conversations": "folder-messages",
+ ".conversations": "folder-messages",
+ "_conversations": "folder-messages",
+ "__conversations__": "folder-messages",
+ "dialog": "folder-messages",
+ ".dialog": "folder-messages",
+ "_dialog": "folder-messages",
+ "__dialog__": "folder-messages",
+ "dialogs": "folder-messages",
+ ".dialogs": "folder-messages",
+ "_dialogs": "folder-messages",
+ "__dialogs__": "folder-messages",
+ "less": "folder-less",
+ ".less": "folder-less",
+ "_less": "folder-less",
+ "__less__": "folder-less",
+ "gulp": "folder-gulp",
+ ".gulp": "folder-gulp",
+ "_gulp": "folder-gulp",
+ "__gulp__": "folder-gulp",
+ "gulp-tasks": "folder-gulp",
+ ".gulp-tasks": "folder-gulp",
+ "_gulp-tasks": "folder-gulp",
+ "__gulp-tasks__": "folder-gulp",
+ "gulpfile.js": "folder-gulp",
+ ".gulpfile.js": "folder-gulp",
+ "_gulpfile.js": "folder-gulp",
+ "__gulpfile.js__": "folder-gulp",
+ "gulpfile.mjs": "folder-gulp",
+ ".gulpfile.mjs": "folder-gulp",
+ "_gulpfile.mjs": "folder-gulp",
+ "__gulpfile.mjs__": "folder-gulp",
+ "gulpfile.ts": "folder-gulp",
+ ".gulpfile.ts": "folder-gulp",
+ "_gulpfile.ts": "folder-gulp",
+ "__gulpfile.ts__": "folder-gulp",
+ "gulpfile.babel.js": "folder-gulp",
+ ".gulpfile.babel.js": "folder-gulp",
+ "_gulpfile.babel.js": "folder-gulp",
+ "__gulpfile.babel.js__": "folder-gulp",
+ "python": "folder-python",
+ ".python": "folder-python",
+ "_python": "folder-python",
+ "__python__": "folder-python",
+ "pycache": "folder-python",
+ ".pycache": "folder-python",
+ "_pycache": "folder-python",
+ "__pycache__": "folder-python",
+ "pytest_cache": "folder-python",
+ ".pytest_cache": "folder-python",
+ "_pytest_cache": "folder-python",
+ "__pytest_cache__": "folder-python",
+ "sandbox": "folder-sandbox",
+ ".sandbox": "folder-sandbox",
+ "_sandbox": "folder-sandbox",
+ "__sandbox__": "folder-sandbox",
+ "sandboxes": "folder-sandbox",
+ ".sandboxes": "folder-sandbox",
+ "_sandboxes": "folder-sandbox",
+ "__sandboxes__": "folder-sandbox",
+ "playground": "folder-sandbox",
+ ".playground": "folder-sandbox",
+ "_playground": "folder-sandbox",
+ "__playground__": "folder-sandbox",
+ "playgrounds": "folder-sandbox",
+ ".playgrounds": "folder-sandbox",
+ "_playgrounds": "folder-sandbox",
+ "__playgrounds__": "folder-sandbox",
+ "scons": "folder-scons",
+ ".scons": "folder-scons",
+ "_scons": "folder-scons",
+ "__scons__": "folder-scons",
+ "sconf_temp": "folder-scons",
+ ".sconf_temp": "folder-scons",
+ "_sconf_temp": "folder-scons",
+ "__sconf_temp__": "folder-scons",
+ "scons_cache": "folder-scons",
+ ".scons_cache": "folder-scons",
+ "_scons_cache": "folder-scons",
+ "__scons_cache__": "folder-scons",
+ "mojo": "folder-mojo",
+ ".mojo": "folder-mojo",
+ "_mojo": "folder-mojo",
+ "__mojo__": "folder-mojo",
+ "moon": "folder-moon",
+ ".moon": "folder-moon",
+ "_moon": "folder-moon",
+ "__moon__": "folder-moon",
+ "debug": "folder-debug",
+ ".debug": "folder-debug",
+ "_debug": "folder-debug",
+ "__debug__": "folder-debug",
+ "debugger": "folder-debug",
+ ".debugger": "folder-debug",
+ "_debugger": "folder-debug",
+ "__debugger__": "folder-debug",
+ "debugging": "folder-debug",
+ ".debugging": "folder-debug",
+ "_debugging": "folder-debug",
+ "__debugging__": "folder-debug",
+ "fastlane": "folder-fastlane",
+ ".fastlane": "folder-fastlane",
+ "_fastlane": "folder-fastlane",
+ "__fastlane__": "folder-fastlane",
+ "plugin": "folder-plugin",
+ ".plugin": "folder-plugin",
+ "_plugin": "folder-plugin",
+ "__plugin__": "folder-plugin",
+ "plugins": "folder-plugin",
+ ".plugins": "folder-plugin",
+ "_plugins": "folder-plugin",
+ "__plugins__": "folder-plugin",
+ "mod": "folder-plugin",
+ ".mod": "folder-plugin",
+ "_mod": "folder-plugin",
+ "__mod__": "folder-plugin",
+ "mods": "folder-plugin",
+ ".mods": "folder-plugin",
+ "_mods": "folder-plugin",
+ "__mods__": "folder-plugin",
+ "modding": "folder-plugin",
+ ".modding": "folder-plugin",
+ "_modding": "folder-plugin",
+ "__modding__": "folder-plugin",
+ "extension": "folder-plugin",
+ ".extension": "folder-plugin",
+ "_extension": "folder-plugin",
+ "__extension__": "folder-plugin",
+ "extensions": "folder-plugin",
+ ".extensions": "folder-plugin",
+ "_extensions": "folder-plugin",
+ "__extensions__": "folder-plugin",
+ "addon": "folder-plugin",
+ ".addon": "folder-plugin",
+ "_addon": "folder-plugin",
+ "__addon__": "folder-plugin",
+ "addons": "folder-plugin",
+ ".addons": "folder-plugin",
+ "_addons": "folder-plugin",
+ "__addons__": "folder-plugin",
+ "addin": "folder-plugin",
+ ".addin": "folder-plugin",
+ "_addin": "folder-plugin",
+ "__addin__": "folder-plugin",
+ "addins": "folder-plugin",
+ ".addins": "folder-plugin",
+ "_addins": "folder-plugin",
+ "__addins__": "folder-plugin",
+ "module": "folder-plugin",
+ ".module": "folder-plugin",
+ "_module": "folder-plugin",
+ "__module__": "folder-plugin",
+ "modules": "folder-plugin",
+ ".modules": "folder-plugin",
+ "_modules": "folder-plugin",
+ "__modules__": "folder-plugin",
+ "middleware": "folder-middleware",
+ ".middleware": "folder-middleware",
+ "_middleware": "folder-middleware",
+ "__middleware__": "folder-middleware",
+ "middlewares": "folder-middleware",
+ ".middlewares": "folder-middleware",
+ "_middlewares": "folder-middleware",
+ "__middlewares__": "folder-middleware",
+ "controller": "folder-controller",
+ ".controller": "folder-controller",
+ "_controller": "folder-controller",
+ "__controller__": "folder-controller",
+ "controllers": "folder-controller",
+ ".controllers": "folder-controller",
+ "_controllers": "folder-controller",
+ "__controllers__": "folder-controller",
+ "controls": "folder-controller",
+ ".controls": "folder-controller",
+ "_controls": "folder-controller",
+ "__controls__": "folder-controller",
+ "service": "folder-controller",
+ ".service": "folder-controller",
+ "_service": "folder-controller",
+ "__service__": "folder-controller",
+ "services": "folder-controller",
+ ".services": "folder-controller",
+ "_services": "folder-controller",
+ "__services__": "folder-controller",
+ "provider": "folder-controller",
+ ".provider": "folder-controller",
+ "_provider": "folder-controller",
+ "__provider__": "folder-controller",
+ "providers": "folder-controller",
+ ".providers": "folder-controller",
+ "_providers": "folder-controller",
+ "__providers__": "folder-controller",
+ "handler": "folder-controller",
+ ".handler": "folder-controller",
+ "_handler": "folder-controller",
+ "__handler__": "folder-controller",
+ "handlers": "folder-controller",
+ ".handlers": "folder-controller",
+ "_handlers": "folder-controller",
+ "__handlers__": "folder-controller",
+ "ansible": "folder-ansible",
+ ".ansible": "folder-ansible",
+ "_ansible": "folder-ansible",
+ "__ansible__": "folder-ansible",
+ "server": "folder-server",
+ ".server": "folder-server",
+ "_server": "folder-server",
+ "__server__": "folder-server",
+ "servers": "folder-server",
+ ".servers": "folder-server",
+ "_servers": "folder-server",
+ "__servers__": "folder-server",
+ "backend": "folder-server",
+ ".backend": "folder-server",
+ "_backend": "folder-server",
+ "__backend__": "folder-server",
+ "backends": "folder-server",
+ ".backends": "folder-server",
+ "_backends": "folder-server",
+ "__backends__": "folder-server",
+ "client": "folder-client",
+ ".client": "folder-client",
+ "_client": "folder-client",
+ "__client__": "folder-client",
+ "clients": "folder-client",
+ ".clients": "folder-client",
+ "_clients": "folder-client",
+ "__clients__": "folder-client",
+ "frontend": "folder-client",
+ ".frontend": "folder-client",
+ "_frontend": "folder-client",
+ "__frontend__": "folder-client",
+ "frontends": "folder-client",
+ ".frontends": "folder-client",
+ "_frontends": "folder-client",
+ "__frontends__": "folder-client",
+ "pwa": "folder-client",
+ ".pwa": "folder-client",
+ "_pwa": "folder-client",
+ "__pwa__": "folder-client",
+ "spa": "folder-client",
+ ".spa": "folder-client",
+ "_spa": "folder-client",
+ "__spa__": "folder-client",
+ "tasks": "folder-tasks",
+ ".tasks": "folder-tasks",
+ "_tasks": "folder-tasks",
+ "__tasks__": "folder-tasks",
+ "tickets": "folder-tasks",
+ ".tickets": "folder-tasks",
+ "_tickets": "folder-tasks",
+ "__tickets__": "folder-tasks",
+ "android": "folder-android",
+ ".android": "folder-android",
+ "_android": "folder-android",
+ "__android__": "folder-android",
+ "ios": "folder-ios",
+ ".ios": "folder-ios",
+ "_ios": "folder-ios",
+ "__ios__": "folder-ios",
+ "presentation": "folder-ui",
+ ".presentation": "folder-ui",
+ "_presentation": "folder-ui",
+ "__presentation__": "folder-ui",
+ "gui": "folder-ui",
+ ".gui": "folder-ui",
+ "_gui": "folder-ui",
+ "__gui__": "folder-ui",
+ "ui": "folder-ui",
+ ".ui": "folder-ui",
+ "_ui": "folder-ui",
+ "__ui__": "folder-ui",
+ "ux": "folder-ui",
+ ".ux": "folder-ui",
+ "_ux": "folder-ui",
+ "__ux__": "folder-ui",
+ "uploads": "folder-upload",
+ ".uploads": "folder-upload",
+ "_uploads": "folder-upload",
+ "__uploads__": "folder-upload",
+ "upload": "folder-upload",
+ ".upload": "folder-upload",
+ "_upload": "folder-upload",
+ "__upload__": "folder-upload",
+ "downloads": "folder-download",
+ ".downloads": "folder-download",
+ "_downloads": "folder-download",
+ "__downloads__": "folder-download",
+ "download": "folder-download",
+ ".download": "folder-download",
+ "_download": "folder-download",
+ "__download__": "folder-download",
+ "downloader": "folder-download",
+ ".downloader": "folder-download",
+ "_downloader": "folder-download",
+ "__downloader__": "folder-download",
+ "downloaders": "folder-download",
+ ".downloaders": "folder-download",
+ "_downloaders": "folder-download",
+ "__downloaders__": "folder-download",
+ "tools": "folder-tools",
+ ".tools": "folder-tools",
+ "_tools": "folder-tools",
+ "__tools__": "folder-tools",
+ "toolkit": "folder-tools",
+ ".toolkit": "folder-tools",
+ "_toolkit": "folder-tools",
+ "__toolkit__": "folder-tools",
+ "toolkits": "folder-tools",
+ ".toolkits": "folder-tools",
+ "_toolkits": "folder-tools",
+ "__toolkits__": "folder-tools",
+ "toolbox": "folder-tools",
+ ".toolbox": "folder-tools",
+ "_toolbox": "folder-tools",
+ "__toolbox__": "folder-tools",
+ "toolboxes": "folder-tools",
+ ".toolboxes": "folder-tools",
+ "_toolboxes": "folder-tools",
+ "__toolboxes__": "folder-tools",
+ "tooling": "folder-tools",
+ ".tooling": "folder-tools",
+ "_tooling": "folder-tools",
+ "__tooling__": "folder-tools",
+ "devtools": "folder-tools",
+ ".devtools": "folder-tools",
+ "_devtools": "folder-tools",
+ "__devtools__": "folder-tools",
+ "kit": "folder-tools",
+ ".kit": "folder-tools",
+ "_kit": "folder-tools",
+ "__kit__": "folder-tools",
+ "kits": "folder-tools",
+ ".kits": "folder-tools",
+ "_kits": "folder-tools",
+ "__kits__": "folder-tools",
+ "helpers": "folder-helper",
+ ".helpers": "folder-helper",
+ "_helpers": "folder-helper",
+ "__helpers__": "folder-helper",
+ "helper": "folder-helper",
+ ".helper": "folder-helper",
+ "_helper": "folder-helper",
+ "__helper__": "folder-helper",
+ "serverless": "folder-serverless",
+ ".serverless": "folder-serverless",
+ "_serverless": "folder-serverless",
+ "__serverless__": "folder-serverless",
+ "api": "folder-api",
+ ".api": "folder-api",
+ "_api": "folder-api",
+ "__api__": "folder-api",
+ "apis": "folder-api",
+ ".apis": "folder-api",
+ "_apis": "folder-api",
+ "__apis__": "folder-api",
+ "restapi": "folder-api",
+ ".restapi": "folder-api",
+ "_restapi": "folder-api",
+ "__restapi__": "folder-api",
+ "app": "folder-app",
+ ".app": "folder-app",
+ "_app": "folder-app",
+ "__app__": "folder-app",
+ "apps": "folder-app",
+ ".apps": "folder-app",
+ "_apps": "folder-app",
+ "__apps__": "folder-app",
+ "application": "folder-app",
+ ".application": "folder-app",
+ "_application": "folder-app",
+ "__application__": "folder-app",
+ "applications": "folder-app",
+ ".applications": "folder-app",
+ "_applications": "folder-app",
+ "__applications__": "folder-app",
+ "apollo": "folder-apollo",
+ ".apollo": "folder-apollo",
+ "_apollo": "folder-apollo",
+ "__apollo__": "folder-apollo",
+ "apollo-client": "folder-apollo",
+ ".apollo-client": "folder-apollo",
+ "_apollo-client": "folder-apollo",
+ "__apollo-client__": "folder-apollo",
+ "apollo-cache": "folder-apollo",
+ ".apollo-cache": "folder-apollo",
+ "_apollo-cache": "folder-apollo",
+ "__apollo-cache__": "folder-apollo",
+ "apollo-config": "folder-apollo",
+ ".apollo-config": "folder-apollo",
+ "_apollo-config": "folder-apollo",
+ "__apollo-config__": "folder-apollo",
+ "arc": "folder-archive",
+ ".arc": "folder-archive",
+ "_arc": "folder-archive",
+ "__arc__": "folder-archive",
+ "arcs": "folder-archive",
+ ".arcs": "folder-archive",
+ "_arcs": "folder-archive",
+ "__arcs__": "folder-archive",
+ "archive": "folder-archive",
+ ".archive": "folder-archive",
+ "_archive": "folder-archive",
+ "__archive__": "folder-archive",
+ "archives": "folder-archive",
+ ".archives": "folder-archive",
+ "_archives": "folder-archive",
+ "__archives__": "folder-archive",
+ "archival": "folder-archive",
+ ".archival": "folder-archive",
+ "_archival": "folder-archive",
+ "__archival__": "folder-archive",
+ "bkp": "folder-backup",
+ ".bkp": "folder-backup",
+ "_bkp": "folder-backup",
+ "__bkp__": "folder-backup",
+ "bkps": "folder-backup",
+ ".bkps": "folder-backup",
+ "_bkps": "folder-backup",
+ "__bkps__": "folder-backup",
+ "bak": "folder-backup",
+ ".bak": "folder-backup",
+ "_bak": "folder-backup",
+ "__bak__": "folder-backup",
+ "baks": "folder-backup",
+ ".baks": "folder-backup",
+ "_baks": "folder-backup",
+ "__baks__": "folder-backup",
+ "backup": "folder-backup",
+ ".backup": "folder-backup",
+ "_backup": "folder-backup",
+ "__backup__": "folder-backup",
+ "backups": "folder-backup",
+ ".backups": "folder-backup",
+ "_backups": "folder-backup",
+ "__backups__": "folder-backup",
+ "back-up": "folder-backup",
+ ".back-up": "folder-backup",
+ "_back-up": "folder-backup",
+ "__back-up__": "folder-backup",
+ "back-ups": "folder-backup",
+ ".back-ups": "folder-backup",
+ "_back-ups": "folder-backup",
+ "__back-ups__": "folder-backup",
+ "history": "folder-backup",
+ ".history": "folder-backup",
+ "_history": "folder-backup",
+ "__history__": "folder-backup",
+ "histories": "folder-backup",
+ ".histories": "folder-backup",
+ "_histories": "folder-backup",
+ "__histories__": "folder-backup",
+ "batch": "folder-batch",
+ ".batch": "folder-batch",
+ "_batch": "folder-batch",
+ "__batch__": "folder-batch",
+ "batchs": "folder-batch",
+ ".batchs": "folder-batch",
+ "_batchs": "folder-batch",
+ "__batchs__": "folder-batch",
+ "batches": "folder-batch",
+ ".batches": "folder-batch",
+ "_batches": "folder-batch",
+ "__batches__": "folder-batch",
+ "buildkite": "folder-buildkite",
+ ".buildkite": "folder-buildkite",
+ "_buildkite": "folder-buildkite",
+ "__buildkite__": "folder-buildkite",
+ "cluster": "folder-cluster",
+ ".cluster": "folder-cluster",
+ "_cluster": "folder-cluster",
+ "__cluster__": "folder-cluster",
+ "clusters": "folder-cluster",
+ ".clusters": "folder-cluster",
+ "_clusters": "folder-cluster",
+ "__clusters__": "folder-cluster",
+ "command": "folder-command",
+ ".command": "folder-command",
+ "_command": "folder-command",
+ "__command__": "folder-command",
+ "commands": "folder-command",
+ ".commands": "folder-command",
+ "_commands": "folder-command",
+ "__commands__": "folder-command",
+ "commandline": "folder-command",
+ ".commandline": "folder-command",
+ "_commandline": "folder-command",
+ "__commandline__": "folder-command",
+ "cmd": "folder-command",
+ ".cmd": "folder-command",
+ "_cmd": "folder-command",
+ "__cmd__": "folder-command",
+ "cli": "folder-command",
+ ".cli": "folder-command",
+ "_cli": "folder-command",
+ "__cli__": "folder-command",
+ "clis": "folder-command",
+ ".clis": "folder-command",
+ "_clis": "folder-command",
+ "__clis__": "folder-command",
+ "constant": "folder-constant",
+ ".constant": "folder-constant",
+ "_constant": "folder-constant",
+ "__constant__": "folder-constant",
+ "constants": "folder-constant",
+ ".constants": "folder-constant",
+ "_constants": "folder-constant",
+ "__constants__": "folder-constant",
+ "const": "folder-constant",
+ ".const": "folder-constant",
+ "_const": "folder-constant",
+ "__const__": "folder-constant",
+ "consts": "folder-constant",
+ ".consts": "folder-constant",
+ "_consts": "folder-constant",
+ "__consts__": "folder-constant",
+ "container": "folder-container",
+ ".container": "folder-container",
+ "_container": "folder-container",
+ "__container__": "folder-container",
+ "containers": "folder-container",
+ ".containers": "folder-container",
+ "_containers": "folder-container",
+ "__containers__": "folder-container",
+ "devcontainer": "folder-container",
+ ".devcontainer": "folder-container",
+ "_devcontainer": "folder-container",
+ "__devcontainer__": "folder-container",
+ "content": "folder-content",
+ ".content": "folder-content",
+ "_content": "folder-content",
+ "__content__": "folder-content",
+ "contents": "folder-content",
+ ".contents": "folder-content",
+ "_contents": "folder-content",
+ "__contents__": "folder-content",
+ "context": "folder-context",
+ ".context": "folder-context",
+ "_context": "folder-context",
+ "__context__": "folder-context",
+ "contexts": "folder-context",
+ ".contexts": "folder-context",
+ "_contexts": "folder-context",
+ "__contexts__": "folder-context",
+ "core": "folder-core",
+ ".core": "folder-core",
+ "_core": "folder-core",
+ "__core__": "folder-core",
+ "delta": "folder-delta",
+ ".delta": "folder-delta",
+ "_delta": "folder-delta",
+ "__delta__": "folder-delta",
+ "deltas": "folder-delta",
+ ".deltas": "folder-delta",
+ "_deltas": "folder-delta",
+ "__deltas__": "folder-delta",
+ "changes": "folder-delta",
+ ".changes": "folder-delta",
+ "_changes": "folder-delta",
+ "__changes__": "folder-delta",
+ "dump": "folder-dump",
+ ".dump": "folder-dump",
+ "_dump": "folder-dump",
+ "__dump__": "folder-dump",
+ "dumps": "folder-dump",
+ ".dumps": "folder-dump",
+ "_dumps": "folder-dump",
+ "__dumps__": "folder-dump",
+ "demo": "folder-examples",
+ ".demo": "folder-examples",
+ "_demo": "folder-examples",
+ "__demo__": "folder-examples",
+ "demos": "folder-examples",
+ ".demos": "folder-examples",
+ "_demos": "folder-examples",
+ "__demos__": "folder-examples",
+ "example": "folder-examples",
+ ".example": "folder-examples",
+ "_example": "folder-examples",
+ "__example__": "folder-examples",
+ "examples": "folder-examples",
+ ".examples": "folder-examples",
+ "_examples": "folder-examples",
+ "__examples__": "folder-examples",
+ "sample": "folder-examples",
+ ".sample": "folder-examples",
+ "_sample": "folder-examples",
+ "__sample__": "folder-examples",
+ "samples": "folder-examples",
+ ".samples": "folder-examples",
+ "_samples": "folder-examples",
+ "__samples__": "folder-examples",
+ "sample-data": "folder-examples",
+ ".sample-data": "folder-examples",
+ "_sample-data": "folder-examples",
+ "__sample-data__": "folder-examples",
+ "env": "folder-environment",
+ ".env": "folder-environment",
+ "_env": "folder-environment",
+ "__env__": "folder-environment",
+ "envs": "folder-environment",
+ ".envs": "folder-environment",
+ "_envs": "folder-environment",
+ "__envs__": "folder-environment",
+ "environment": "folder-environment",
+ ".environment": "folder-environment",
+ "_environment": "folder-environment",
+ "__environment__": "folder-environment",
+ "environments": "folder-environment",
+ ".environments": "folder-environment",
+ "_environments": "folder-environment",
+ "__environments__": "folder-environment",
+ "venv": "folder-environment",
+ ".venv": "folder-environment",
+ "_venv": "folder-environment",
+ "__venv__": "folder-environment",
+ "func": "folder-functions",
+ ".func": "folder-functions",
+ "_func": "folder-functions",
+ "__func__": "folder-functions",
+ "funcs": "folder-functions",
+ ".funcs": "folder-functions",
+ "_funcs": "folder-functions",
+ "__funcs__": "folder-functions",
+ "function": "folder-functions",
+ ".function": "folder-functions",
+ "_function": "folder-functions",
+ "__function__": "folder-functions",
+ "functions": "folder-functions",
+ ".functions": "folder-functions",
+ "_functions": "folder-functions",
+ "__functions__": "folder-functions",
+ "lambda": "folder-functions",
+ ".lambda": "folder-functions",
+ "_lambda": "folder-functions",
+ "__lambda__": "folder-functions",
+ "lambdas": "folder-functions",
+ ".lambdas": "folder-functions",
+ "_lambdas": "folder-functions",
+ "__lambdas__": "folder-functions",
+ "logic": "folder-functions",
+ ".logic": "folder-functions",
+ "_logic": "folder-functions",
+ "__logic__": "folder-functions",
+ "math": "folder-functions",
+ ".math": "folder-functions",
+ "_math": "folder-functions",
+ "__math__": "folder-functions",
+ "maths": "folder-functions",
+ ".maths": "folder-functions",
+ "_maths": "folder-functions",
+ "__maths__": "folder-functions",
+ "calc": "folder-functions",
+ ".calc": "folder-functions",
+ "_calc": "folder-functions",
+ "__calc__": "folder-functions",
+ "calcs": "folder-functions",
+ ".calcs": "folder-functions",
+ "_calcs": "folder-functions",
+ "__calcs__": "folder-functions",
+ "calculation": "folder-functions",
+ ".calculation": "folder-functions",
+ "_calculation": "folder-functions",
+ "__calculation__": "folder-functions",
+ "calculations": "folder-functions",
+ ".calculations": "folder-functions",
+ "_calculations": "folder-functions",
+ "__calculations__": "folder-functions",
+ "generator": "folder-generator",
+ ".generator": "folder-generator",
+ "_generator": "folder-generator",
+ "__generator__": "folder-generator",
+ "generators": "folder-generator",
+ ".generators": "folder-generator",
+ "_generators": "folder-generator",
+ "__generators__": "folder-generator",
+ "generated": "folder-generator",
+ ".generated": "folder-generator",
+ "_generated": "folder-generator",
+ "__generated__": "folder-generator",
+ "cfn-gen": "folder-generator",
+ ".cfn-gen": "folder-generator",
+ "_cfn-gen": "folder-generator",
+ "__cfn-gen__": "folder-generator",
+ "gen": "folder-generator",
+ ".gen": "folder-generator",
+ "_gen": "folder-generator",
+ "__gen__": "folder-generator",
+ "gens": "folder-generator",
+ ".gens": "folder-generator",
+ "_gens": "folder-generator",
+ "__gens__": "folder-generator",
+ "auto": "folder-generator",
+ ".auto": "folder-generator",
+ "_auto": "folder-generator",
+ "__auto__": "folder-generator",
+ "hook": "folder-hook",
+ ".hook": "folder-hook",
+ "_hook": "folder-hook",
+ "__hook__": "folder-hook",
+ "hooks": "folder-hook",
+ ".hooks": "folder-hook",
+ "_hooks": "folder-hook",
+ "__hooks__": "folder-hook",
+ "trigger": "folder-trigger",
+ ".trigger": "folder-trigger",
+ "_trigger": "folder-trigger",
+ "__trigger__": "folder-trigger",
+ "triggers": "folder-trigger",
+ ".triggers": "folder-trigger",
+ "_triggers": "folder-trigger",
+ "__triggers__": "folder-trigger",
+ "job": "folder-job",
+ ".job": "folder-job",
+ "_job": "folder-job",
+ "__job__": "folder-job",
+ "jobs": "folder-job",
+ ".jobs": "folder-job",
+ "_jobs": "folder-job",
+ "__jobs__": "folder-job",
+ "key": "folder-keys",
+ ".key": "folder-keys",
+ "_key": "folder-keys",
+ "__key__": "folder-keys",
+ "keys": "folder-keys",
+ ".keys": "folder-keys",
+ "_keys": "folder-keys",
+ "__keys__": "folder-keys",
+ "token": "folder-keys",
+ ".token": "folder-keys",
+ "_token": "folder-keys",
+ "__token__": "folder-keys",
+ "tokens": "folder-keys",
+ ".tokens": "folder-keys",
+ "_tokens": "folder-keys",
+ "__tokens__": "folder-keys",
+ "jwt": "folder-keys",
+ ".jwt": "folder-keys",
+ "_jwt": "folder-keys",
+ "__jwt__": "folder-keys",
+ "secret": "folder-keys",
+ ".secret": "folder-keys",
+ "_secret": "folder-keys",
+ "__secret__": "folder-keys",
+ "secrets": "folder-keys",
+ ".secrets": "folder-keys",
+ "_secrets": "folder-keys",
+ "__secrets__": "folder-keys",
+ "layout": "folder-layout",
+ ".layout": "folder-layout",
+ "_layout": "folder-layout",
+ "__layout__": "folder-layout",
+ "layouts": "folder-layout",
+ ".layouts": "folder-layout",
+ "_layouts": "folder-layout",
+ "__layouts__": "folder-layout",
+ "mail": "folder-mail",
+ ".mail": "folder-mail",
+ "_mail": "folder-mail",
+ "__mail__": "folder-mail",
+ "mails": "folder-mail",
+ ".mails": "folder-mail",
+ "_mails": "folder-mail",
+ "__mails__": "folder-mail",
+ "email": "folder-mail",
+ ".email": "folder-mail",
+ "_email": "folder-mail",
+ "__email__": "folder-mail",
+ "emails": "folder-mail",
+ ".emails": "folder-mail",
+ "_emails": "folder-mail",
+ "__emails__": "folder-mail",
+ "smtp": "folder-mail",
+ ".smtp": "folder-mail",
+ "_smtp": "folder-mail",
+ "__smtp__": "folder-mail",
+ "mailers": "folder-mail",
+ ".mailers": "folder-mail",
+ "_mailers": "folder-mail",
+ "__mailers__": "folder-mail",
+ "mappings": "folder-mappings",
+ ".mappings": "folder-mappings",
+ "_mappings": "folder-mappings",
+ "__mappings__": "folder-mappings",
+ "mapping": "folder-mappings",
+ ".mapping": "folder-mappings",
+ "_mapping": "folder-mappings",
+ "__mapping__": "folder-mappings",
+ "meta": "folder-meta",
+ ".meta": "folder-meta",
+ "_meta": "folder-meta",
+ "__meta__": "folder-meta",
+ "changesets": "folder-changesets",
+ ".changesets": "folder-changesets",
+ "_changesets": "folder-changesets",
+ "__changesets__": "folder-changesets",
+ "changeset": "folder-changesets",
+ ".changeset": "folder-changesets",
+ "_changeset": "folder-changesets",
+ "__changeset__": "folder-changesets",
+ "package": "folder-packages",
+ ".package": "folder-packages",
+ "_package": "folder-packages",
+ "__package__": "folder-packages",
+ "packages": "folder-packages",
+ ".packages": "folder-packages",
+ "_packages": "folder-packages",
+ "__packages__": "folder-packages",
+ "pkg": "folder-packages",
+ ".pkg": "folder-packages",
+ "_pkg": "folder-packages",
+ "__pkg__": "folder-packages",
+ "pkgs": "folder-packages",
+ ".pkgs": "folder-packages",
+ "_pkgs": "folder-packages",
+ "__pkgs__": "folder-packages",
+ "serverpackages": "folder-packages",
+ ".serverpackages": "folder-packages",
+ "_serverpackages": "folder-packages",
+ "__serverpackages__": "folder-packages",
+ "devpackages": "folder-packages",
+ ".devpackages": "folder-packages",
+ "_devpackages": "folder-packages",
+ "__devpackages__": "folder-packages",
+ "dependencies": "folder-packages",
+ ".dependencies": "folder-packages",
+ "_dependencies": "folder-packages",
+ "__dependencies__": "folder-packages",
+ "shared": "folder-shared",
+ ".shared": "folder-shared",
+ "_shared": "folder-shared",
+ "__shared__": "folder-shared",
+ "common": "folder-shared",
+ ".common": "folder-shared",
+ "_common": "folder-shared",
+ "__common__": "folder-shared",
+ "glsl": "folder-shader",
+ ".glsl": "folder-shader",
+ "_glsl": "folder-shader",
+ "__glsl__": "folder-shader",
+ "hlsl": "folder-shader",
+ ".hlsl": "folder-shader",
+ "_hlsl": "folder-shader",
+ "__hlsl__": "folder-shader",
+ "shader": "folder-shader",
+ ".shader": "folder-shader",
+ "_shader": "folder-shader",
+ "__shader__": "folder-shader",
+ "shaders": "folder-shader",
+ ".shaders": "folder-shader",
+ "_shaders": "folder-shader",
+ "__shaders__": "folder-shader",
+ "stack": "folder-stack",
+ ".stack": "folder-stack",
+ "_stack": "folder-stack",
+ "__stack__": "folder-stack",
+ "stacks": "folder-stack",
+ ".stacks": "folder-stack",
+ "_stacks": "folder-stack",
+ "__stacks__": "folder-stack",
+ "template": "folder-template",
+ ".template": "folder-template",
+ "_template": "folder-template",
+ "__template__": "folder-template",
+ "templates": "folder-template",
+ ".templates": "folder-template",
+ "_templates": "folder-template",
+ "__templates__": "folder-template",
+ "github/ISSUE_TEMPLATE": "folder-template",
+ ".github/ISSUE_TEMPLATE": "folder-template",
+ "_github/ISSUE_TEMPLATE": "folder-template",
+ "__github/ISSUE_TEMPLATE__": "folder-template",
+ "github/PULL_REQUEST_TEMPLATE": "folder-template",
+ ".github/PULL_REQUEST_TEMPLATE": "folder-template",
+ "_github/PULL_REQUEST_TEMPLATE": "folder-template",
+ "__github/PULL_REQUEST_TEMPLATE__": "folder-template",
+ "util": "folder-utils",
+ ".util": "folder-utils",
+ "_util": "folder-utils",
+ "__util__": "folder-utils",
+ "utils": "folder-utils",
+ ".utils": "folder-utils",
+ "_utils": "folder-utils",
+ "__utils__": "folder-utils",
+ "utility": "folder-utils",
+ ".utility": "folder-utils",
+ "_utility": "folder-utils",
+ "__utility__": "folder-utils",
+ "utilities": "folder-utils",
+ ".utilities": "folder-utils",
+ "_utilities": "folder-utils",
+ "__utilities__": "folder-utils",
+ "supabase": "folder-supabase",
+ ".supabase": "folder-supabase",
+ "_supabase": "folder-supabase",
+ "__supabase__": "folder-supabase",
+ "private": "folder-private",
+ ".private": "folder-private",
+ "_private": "folder-private",
+ "__private__": "folder-private",
+ "linux": "folder-linux",
+ ".linux": "folder-linux",
+ "_linux": "folder-linux",
+ "__linux__": "folder-linux",
+ "linuxbsd": "folder-linux",
+ ".linuxbsd": "folder-linux",
+ "_linuxbsd": "folder-linux",
+ "__linuxbsd__": "folder-linux",
+ "unix": "folder-linux",
+ ".unix": "folder-linux",
+ "_unix": "folder-linux",
+ "__unix__": "folder-linux",
+ "windows": "folder-windows",
+ ".windows": "folder-windows",
+ "_windows": "folder-windows",
+ "__windows__": "folder-windows",
+ "win": "folder-windows",
+ ".win": "folder-windows",
+ "_win": "folder-windows",
+ "__win__": "folder-windows",
+ "win32": "folder-windows",
+ ".win32": "folder-windows",
+ "_win32": "folder-windows",
+ "__win32__": "folder-windows",
+ "macos": "folder-macos",
+ ".macos": "folder-macos",
+ "_macos": "folder-macos",
+ "__macos__": "folder-macos",
+ "mac": "folder-macos",
+ ".mac": "folder-macos",
+ "_mac": "folder-macos",
+ "__mac__": "folder-macos",
+ "osx": "folder-macos",
+ ".osx": "folder-macos",
+ "_osx": "folder-macos",
+ "__osx__": "folder-macos",
+ "DS_Store": "folder-macos",
+ ".DS_Store": "folder-macos",
+ "_DS_Store": "folder-macos",
+ "__DS_Store__": "folder-macos",
+ "iPhone": "folder-macos",
+ ".iPhone": "folder-macos",
+ "_iPhone": "folder-macos",
+ "__iPhone__": "folder-macos",
+ "iPad": "folder-macos",
+ ".iPad": "folder-macos",
+ "_iPad": "folder-macos",
+ "__iPad__": "folder-macos",
+ "iPod": "folder-macos",
+ ".iPod": "folder-macos",
+ "_iPod": "folder-macos",
+ "__iPod__": "folder-macos",
+ "macbook": "folder-macos",
+ ".macbook": "folder-macos",
+ "_macbook": "folder-macos",
+ "__macbook__": "folder-macos",
+ "macbook-air": "folder-macos",
+ ".macbook-air": "folder-macos",
+ "_macbook-air": "folder-macos",
+ "__macbook-air__": "folder-macos",
+ "macosx": "folder-macos",
+ ".macosx": "folder-macos",
+ "_macosx": "folder-macos",
+ "__macosx__": "folder-macos",
+ "apple": "folder-macos",
+ ".apple": "folder-macos",
+ "_apple": "folder-macos",
+ "__apple__": "folder-macos",
+ "error": "folder-error",
+ ".error": "folder-error",
+ "_error": "folder-error",
+ "__error__": "folder-error",
+ "errors": "folder-error",
+ ".errors": "folder-error",
+ "_errors": "folder-error",
+ "__errors__": "folder-error",
+ "err": "folder-error",
+ ".err": "folder-error",
+ "_err": "folder-error",
+ "__err__": "folder-error",
+ "errs": "folder-error",
+ ".errs": "folder-error",
+ "_errs": "folder-error",
+ "__errs__": "folder-error",
+ "crash": "folder-error",
+ ".crash": "folder-error",
+ "_crash": "folder-error",
+ "__crash__": "folder-error",
+ "crashes": "folder-error",
+ ".crashes": "folder-error",
+ "_crashes": "folder-error",
+ "__crashes__": "folder-error",
+ "event": "folder-event",
+ ".event": "folder-event",
+ "_event": "folder-event",
+ "__event__": "folder-event",
+ "events": "folder-event",
+ ".events": "folder-event",
+ "_events": "folder-event",
+ "__events__": "folder-event",
+ "auth": "folder-secure",
+ ".auth": "folder-secure",
+ "_auth": "folder-secure",
+ "__auth__": "folder-secure",
+ "authentication": "folder-secure",
+ ".authentication": "folder-secure",
+ "_authentication": "folder-secure",
+ "__authentication__": "folder-secure",
+ "secure": "folder-secure",
+ ".secure": "folder-secure",
+ "_secure": "folder-secure",
+ "__secure__": "folder-secure",
+ "security": "folder-secure",
+ ".security": "folder-secure",
+ "_security": "folder-secure",
+ "__security__": "folder-secure",
+ "cert": "folder-secure",
+ ".cert": "folder-secure",
+ "_cert": "folder-secure",
+ "__cert__": "folder-secure",
+ "certs": "folder-secure",
+ ".certs": "folder-secure",
+ "_certs": "folder-secure",
+ "__certs__": "folder-secure",
+ "certificate": "folder-secure",
+ ".certificate": "folder-secure",
+ "_certificate": "folder-secure",
+ "__certificate__": "folder-secure",
+ "certificates": "folder-secure",
+ ".certificates": "folder-secure",
+ "_certificates": "folder-secure",
+ "__certificates__": "folder-secure",
+ "ssl": "folder-secure",
+ ".ssl": "folder-secure",
+ "_ssl": "folder-secure",
+ "__ssl__": "folder-secure",
+ "cipher": "folder-secure",
+ ".cipher": "folder-secure",
+ "_cipher": "folder-secure",
+ "__cipher__": "folder-secure",
+ "cypher": "folder-secure",
+ ".cypher": "folder-secure",
+ "_cypher": "folder-secure",
+ "__cypher__": "folder-secure",
+ "tls": "folder-secure",
+ ".tls": "folder-secure",
+ "_tls": "folder-secure",
+ "__tls__": "folder-secure",
+ "custom": "folder-custom",
+ ".custom": "folder-custom",
+ "_custom": "folder-custom",
+ "__custom__": "folder-custom",
+ "customs": "folder-custom",
+ ".customs": "folder-custom",
+ "_customs": "folder-custom",
+ "__customs__": "folder-custom",
+ "draft": "folder-mock",
+ ".draft": "folder-mock",
+ "_draft": "folder-mock",
+ "__draft__": "folder-mock",
+ "drafts": "folder-mock",
+ ".drafts": "folder-mock",
+ "_drafts": "folder-mock",
+ "__drafts__": "folder-mock",
+ "mock": "folder-mock",
+ ".mock": "folder-mock",
+ "_mock": "folder-mock",
+ "__mock__": "folder-mock",
+ "mocks": "folder-mock",
+ ".mocks": "folder-mock",
+ "_mocks": "folder-mock",
+ "__mocks__": "folder-mock",
+ "fixture": "folder-mock",
+ ".fixture": "folder-mock",
+ "_fixture": "folder-mock",
+ "__fixture__": "folder-mock",
+ "fixtures": "folder-mock",
+ ".fixtures": "folder-mock",
+ "_fixtures": "folder-mock",
+ "__fixtures__": "folder-mock",
+ "concept": "folder-mock",
+ ".concept": "folder-mock",
+ "_concept": "folder-mock",
+ "__concept__": "folder-mock",
+ "concepts": "folder-mock",
+ ".concepts": "folder-mock",
+ "_concepts": "folder-mock",
+ "__concepts__": "folder-mock",
+ "sketch": "folder-mock",
+ ".sketch": "folder-mock",
+ "_sketch": "folder-mock",
+ "__sketch__": "folder-mock",
+ "sketches": "folder-mock",
+ ".sketches": "folder-mock",
+ "_sketches": "folder-mock",
+ "__sketches__": "folder-mock",
+ "syntax": "folder-syntax",
+ ".syntax": "folder-syntax",
+ "_syntax": "folder-syntax",
+ "__syntax__": "folder-syntax",
+ "syntaxes": "folder-syntax",
+ ".syntaxes": "folder-syntax",
+ "_syntaxes": "folder-syntax",
+ "__syntaxes__": "folder-syntax",
+ "spellcheck": "folder-syntax",
+ ".spellcheck": "folder-syntax",
+ "_spellcheck": "folder-syntax",
+ "__spellcheck__": "folder-syntax",
+ "spellcheckers": "folder-syntax",
+ ".spellcheckers": "folder-syntax",
+ "_spellcheckers": "folder-syntax",
+ "__spellcheckers__": "folder-syntax",
+ "vm": "folder-vm",
+ ".vm": "folder-vm",
+ "_vm": "folder-vm",
+ "__vm__": "folder-vm",
+ "vms": "folder-vm",
+ ".vms": "folder-vm",
+ "_vms": "folder-vm",
+ "__vms__": "folder-vm",
+ "stylus": "folder-stylus",
+ ".stylus": "folder-stylus",
+ "_stylus": "folder-stylus",
+ "__stylus__": "folder-stylus",
+ "flow-typed": "folder-flow",
+ ".flow-typed": "folder-flow",
+ "_flow-typed": "folder-flow",
+ "__flow-typed__": "folder-flow",
+ "rule": "folder-rules",
+ ".rule": "folder-rules",
+ "_rule": "folder-rules",
+ "__rule__": "folder-rules",
+ "rules": "folder-rules",
+ ".rules": "folder-rules",
+ "_rules": "folder-rules",
+ "__rules__": "folder-rules",
+ "validation": "folder-rules",
+ ".validation": "folder-rules",
+ "_validation": "folder-rules",
+ "__validation__": "folder-rules",
+ "validations": "folder-rules",
+ ".validations": "folder-rules",
+ "_validations": "folder-rules",
+ "__validations__": "folder-rules",
+ "validator": "folder-rules",
+ ".validator": "folder-rules",
+ "_validator": "folder-rules",
+ "__validator__": "folder-rules",
+ "validators": "folder-rules",
+ ".validators": "folder-rules",
+ "_validators": "folder-rules",
+ "__validators__": "folder-rules",
+ "review": "folder-review",
+ ".review": "folder-review",
+ "_review": "folder-review",
+ "__review__": "folder-review",
+ "reviews": "folder-review",
+ ".reviews": "folder-review",
+ "_reviews": "folder-review",
+ "__reviews__": "folder-review",
+ "revisal": "folder-review",
+ ".revisal": "folder-review",
+ "_revisal": "folder-review",
+ "__revisal__": "folder-review",
+ "revisals": "folder-review",
+ ".revisals": "folder-review",
+ "_revisals": "folder-review",
+ "__revisals__": "folder-review",
+ "reviewed": "folder-review",
+ ".reviewed": "folder-review",
+ "_reviewed": "folder-review",
+ "__reviewed__": "folder-review",
+ "preview": "folder-review",
+ ".preview": "folder-review",
+ "_preview": "folder-review",
+ "__preview__": "folder-review",
+ "previews": "folder-review",
+ ".previews": "folder-review",
+ "_previews": "folder-review",
+ "__previews__": "folder-review",
+ "anim": "folder-animation",
+ ".anim": "folder-animation",
+ "_anim": "folder-animation",
+ "__anim__": "folder-animation",
+ "anims": "folder-animation",
+ ".anims": "folder-animation",
+ "_anims": "folder-animation",
+ "__anims__": "folder-animation",
+ "animation": "folder-animation",
+ ".animation": "folder-animation",
+ "_animation": "folder-animation",
+ "__animation__": "folder-animation",
+ "animations": "folder-animation",
+ ".animations": "folder-animation",
+ "_animations": "folder-animation",
+ "__animations__": "folder-animation",
+ "animated": "folder-animation",
+ ".animated": "folder-animation",
+ "_animated": "folder-animation",
+ "__animated__": "folder-animation",
+ "motion": "folder-animation",
+ ".motion": "folder-animation",
+ "_motion": "folder-animation",
+ "__motion__": "folder-animation",
+ "motions": "folder-animation",
+ ".motions": "folder-animation",
+ "_motions": "folder-animation",
+ "__motions__": "folder-animation",
+ "transition": "folder-animation",
+ ".transition": "folder-animation",
+ "_transition": "folder-animation",
+ "__transition__": "folder-animation",
+ "transitions": "folder-animation",
+ ".transitions": "folder-animation",
+ "_transitions": "folder-animation",
+ "__transitions__": "folder-animation",
+ "easing": "folder-animation",
+ ".easing": "folder-animation",
+ "_easing": "folder-animation",
+ "__easing__": "folder-animation",
+ "easings": "folder-animation",
+ ".easings": "folder-animation",
+ "_easings": "folder-animation",
+ "__easings__": "folder-animation",
+ "guard": "folder-guard",
+ ".guard": "folder-guard",
+ "_guard": "folder-guard",
+ "__guard__": "folder-guard",
+ "guards": "folder-guard",
+ ".guards": "folder-guard",
+ "_guards": "folder-guard",
+ "__guards__": "folder-guard",
+ "prisma": "folder-prisma",
+ ".prisma": "folder-prisma",
+ "_prisma": "folder-prisma",
+ "__prisma__": "folder-prisma",
+ "prisma/schema": "folder-prisma",
+ ".prisma/schema": "folder-prisma",
+ "_prisma/schema": "folder-prisma",
+ "__prisma/schema__": "folder-prisma",
+ "pipe": "folder-pipe",
+ ".pipe": "folder-pipe",
+ "_pipe": "folder-pipe",
+ "__pipe__": "folder-pipe",
+ "pipes": "folder-pipe",
+ ".pipes": "folder-pipe",
+ "_pipes": "folder-pipe",
+ "__pipes__": "folder-pipe",
+ "pipeline": "folder-pipe",
+ ".pipeline": "folder-pipe",
+ "_pipeline": "folder-pipe",
+ "__pipeline__": "folder-pipe",
+ "pipelines": "folder-pipe",
+ ".pipelines": "folder-pipe",
+ "_pipelines": "folder-pipe",
+ "__pipelines__": "folder-pipe",
+ "svg": "folder-svg",
+ ".svg": "folder-svg",
+ "_svg": "folder-svg",
+ "__svg__": "folder-svg",
+ "svgs": "folder-svg",
+ ".svgs": "folder-svg",
+ "_svgs": "folder-svg",
+ "__svgs__": "folder-svg",
+ "nuxt": "folder-nuxt",
+ ".nuxt": "folder-nuxt",
+ "_nuxt": "folder-nuxt",
+ "__nuxt__": "folder-nuxt",
+ "terraform": "folder-terraform",
+ ".terraform": "folder-terraform",
+ "_terraform": "folder-terraform",
+ "__terraform__": "folder-terraform",
+ "mobile": "folder-mobile",
+ ".mobile": "folder-mobile",
+ "_mobile": "folder-mobile",
+ "__mobile__": "folder-mobile",
+ "mobiles": "folder-mobile",
+ ".mobiles": "folder-mobile",
+ "_mobiles": "folder-mobile",
+ "__mobiles__": "folder-mobile",
+ "portable": "folder-mobile",
+ ".portable": "folder-mobile",
+ "_portable": "folder-mobile",
+ "__portable__": "folder-mobile",
+ "portability": "folder-mobile",
+ ".portability": "folder-mobile",
+ "_portability": "folder-mobile",
+ "__portability__": "folder-mobile",
+ "phone": "folder-mobile",
+ ".phone": "folder-mobile",
+ "_phone": "folder-mobile",
+ "__phone__": "folder-mobile",
+ "phones": "folder-mobile",
+ ".phones": "folder-mobile",
+ "_phones": "folder-mobile",
+ "__phones__": "folder-mobile",
+ "stencil": "folder-stencil",
+ ".stencil": "folder-stencil",
+ "_stencil": "folder-stencil",
+ "__stencil__": "folder-stencil",
+ "firebase": "folder-firebase",
+ ".firebase": "folder-firebase",
+ "_firebase": "folder-firebase",
+ "__firebase__": "folder-firebase",
+ "firestore": "folder-firestore",
+ ".firestore": "folder-firestore",
+ "_firestore": "folder-firestore",
+ "__firestore__": "folder-firestore",
+ "cloud-firestore": "folder-firestore",
+ ".cloud-firestore": "folder-firestore",
+ "_cloud-firestore": "folder-firestore",
+ "__cloud-firestore__": "folder-firestore",
+ "firebase-firestore": "folder-firestore",
+ ".firebase-firestore": "folder-firestore",
+ "_firebase-firestore": "folder-firestore",
+ "__firebase-firestore__": "folder-firestore",
+ "cloud-functions": "folder-cloud-functions",
+ ".cloud-functions": "folder-cloud-functions",
+ "_cloud-functions": "folder-cloud-functions",
+ "__cloud-functions__": "folder-cloud-functions",
+ "cloudfunctions": "folder-cloud-functions",
+ ".cloudfunctions": "folder-cloud-functions",
+ "_cloudfunctions": "folder-cloud-functions",
+ "__cloudfunctions__": "folder-cloud-functions",
+ "firebase-cloud-functions": "folder-cloud-functions",
+ ".firebase-cloud-functions": "folder-cloud-functions",
+ "_firebase-cloud-functions": "folder-cloud-functions",
+ "__firebase-cloud-functions__": "folder-cloud-functions",
+ "firebase-cloudfunctions": "folder-cloud-functions",
+ ".firebase-cloudfunctions": "folder-cloud-functions",
+ "_firebase-cloudfunctions": "folder-cloud-functions",
+ "__firebase-cloudfunctions__": "folder-cloud-functions",
+ "svelte": "folder-svelte",
+ ".svelte": "folder-svelte",
+ "_svelte": "folder-svelte",
+ "__svelte__": "folder-svelte",
+ "svelte-kit": "folder-svelte",
+ ".svelte-kit": "folder-svelte",
+ "_svelte-kit": "folder-svelte",
+ "__svelte-kit__": "folder-svelte",
+ "update": "folder-update",
+ ".update": "folder-update",
+ "_update": "folder-update",
+ "__update__": "folder-update",
+ "updates": "folder-update",
+ ".updates": "folder-update",
+ "_updates": "folder-update",
+ "__updates__": "folder-update",
+ "upgrade": "folder-update",
+ ".upgrade": "folder-update",
+ "_upgrade": "folder-update",
+ "__upgrade__": "folder-update",
+ "upgrades": "folder-update",
+ ".upgrades": "folder-update",
+ "_upgrades": "folder-update",
+ "__upgrades__": "folder-update",
+ "idea": "folder-intellij",
+ ".idea": "folder-intellij",
+ "_idea": "folder-intellij",
+ "__idea__": "folder-intellij",
+ "azure-pipelines": "folder-azure-pipelines",
+ ".azure-pipelines": "folder-azure-pipelines",
+ "_azure-pipelines": "folder-azure-pipelines",
+ "__azure-pipelines__": "folder-azure-pipelines",
+ "azure-pipelines-ci": "folder-azure-pipelines",
+ ".azure-pipelines-ci": "folder-azure-pipelines",
+ "_azure-pipelines-ci": "folder-azure-pipelines",
+ "__azure-pipelines-ci__": "folder-azure-pipelines",
+ "mjml": "folder-mjml",
+ ".mjml": "folder-mjml",
+ "_mjml": "folder-mjml",
+ "__mjml__": "folder-mjml",
+ "admin": "folder-admin",
+ ".admin": "folder-admin",
+ "_admin": "folder-admin",
+ "__admin__": "folder-admin",
+ "admins": "folder-admin",
+ ".admins": "folder-admin",
+ "_admins": "folder-admin",
+ "__admins__": "folder-admin",
+ "manager": "folder-admin",
+ ".manager": "folder-admin",
+ "_manager": "folder-admin",
+ "__manager__": "folder-admin",
+ "managers": "folder-admin",
+ ".managers": "folder-admin",
+ "_managers": "folder-admin",
+ "__managers__": "folder-admin",
+ "moderator": "folder-admin",
+ ".moderator": "folder-admin",
+ "_moderator": "folder-admin",
+ "__moderator__": "folder-admin",
+ "moderators": "folder-admin",
+ ".moderators": "folder-admin",
+ "_moderators": "folder-admin",
+ "__moderators__": "folder-admin",
+ "jupyter": "folder-jupyter",
+ ".jupyter": "folder-jupyter",
+ "_jupyter": "folder-jupyter",
+ "__jupyter__": "folder-jupyter",
+ "notebook": "folder-jupyter",
+ ".notebook": "folder-jupyter",
+ "_notebook": "folder-jupyter",
+ "__notebook__": "folder-jupyter",
+ "notebooks": "folder-jupyter",
+ ".notebooks": "folder-jupyter",
+ "_notebooks": "folder-jupyter",
+ "__notebooks__": "folder-jupyter",
+ "ipynb": "folder-jupyter",
+ ".ipynb": "folder-jupyter",
+ "_ipynb": "folder-jupyter",
+ "__ipynb__": "folder-jupyter",
+ "scala": "folder-scala",
+ ".scala": "folder-scala",
+ "_scala": "folder-scala",
+ "__scala__": "folder-scala",
+ "connection": "folder-connection",
+ ".connection": "folder-connection",
+ "_connection": "folder-connection",
+ "__connection__": "folder-connection",
+ "connections": "folder-connection",
+ ".connections": "folder-connection",
+ "_connections": "folder-connection",
+ "__connections__": "folder-connection",
+ "integration": "folder-connection",
+ ".integration": "folder-connection",
+ "_integration": "folder-connection",
+ "__integration__": "folder-connection",
+ "integrations": "folder-connection",
+ ".integrations": "folder-connection",
+ "_integrations": "folder-connection",
+ "__integrations__": "folder-connection",
+ "remote": "folder-connection",
+ ".remote": "folder-connection",
+ "_remote": "folder-connection",
+ "__remote__": "folder-connection",
+ "remotes": "folder-connection",
+ ".remotes": "folder-connection",
+ "_remotes": "folder-connection",
+ "__remotes__": "folder-connection",
+ "quasar": "folder-quasar",
+ ".quasar": "folder-quasar",
+ "_quasar": "folder-quasar",
+ "__quasar__": "folder-quasar",
+ "next": "folder-next",
+ ".next": "folder-next",
+ "_next": "folder-next",
+ "__next__": "folder-next",
+ "cobol": "folder-cobol",
+ ".cobol": "folder-cobol",
+ "_cobol": "folder-cobol",
+ "__cobol__": "folder-cobol",
+ "yarn": "folder-yarn",
+ ".yarn": "folder-yarn",
+ "_yarn": "folder-yarn",
+ "__yarn__": "folder-yarn",
+ "husky": "folder-husky",
+ ".husky": "folder-husky",
+ "_husky": "folder-husky",
+ "__husky__": "folder-husky",
+ "storybook": "folder-storybook",
+ ".storybook": "folder-storybook",
+ "_storybook": "folder-storybook",
+ "__storybook__": "folder-storybook",
+ "stories": "folder-storybook",
+ ".stories": "folder-storybook",
+ "_stories": "folder-storybook",
+ "__stories__": "folder-storybook",
+ "base": "folder-base",
+ ".base": "folder-base",
+ "_base": "folder-base",
+ "__base__": "folder-base",
+ "bases": "folder-base",
+ ".bases": "folder-base",
+ "_bases": "folder-base",
+ "__bases__": "folder-base",
+ "cart": "folder-cart",
+ ".cart": "folder-cart",
+ "_cart": "folder-cart",
+ "__cart__": "folder-cart",
+ "shopping-cart": "folder-cart",
+ ".shopping-cart": "folder-cart",
+ "_shopping-cart": "folder-cart",
+ "__shopping-cart__": "folder-cart",
+ "shopping": "folder-cart",
+ ".shopping": "folder-cart",
+ "_shopping": "folder-cart",
+ "__shopping__": "folder-cart",
+ "shop": "folder-cart",
+ ".shop": "folder-cart",
+ "_shop": "folder-cart",
+ "__shop__": "folder-cart",
+ "home": "folder-home",
+ ".home": "folder-home",
+ "_home": "folder-home",
+ "__home__": "folder-home",
+ "start": "folder-home",
+ ".start": "folder-home",
+ "_start": "folder-home",
+ "__start__": "folder-home",
+ "main": "folder-home",
+ ".main": "folder-home",
+ "_main": "folder-home",
+ "__main__": "folder-home",
+ "landing": "folder-home",
+ ".landing": "folder-home",
+ "_landing": "folder-home",
+ "__landing__": "folder-home",
+ "project": "folder-project",
+ ".project": "folder-project",
+ "_project": "folder-project",
+ "__project__": "folder-project",
+ "projects": "folder-project",
+ ".projects": "folder-project",
+ "_projects": "folder-project",
+ "__projects__": "folder-project",
+ "interface": "folder-interface",
+ ".interface": "folder-interface",
+ "_interface": "folder-interface",
+ "__interface__": "folder-interface",
+ "interfaces": "folder-interface",
+ ".interfaces": "folder-interface",
+ "_interfaces": "folder-interface",
+ "__interfaces__": "folder-interface",
+ "netlify": "folder-netlify",
+ ".netlify": "folder-netlify",
+ "_netlify": "folder-netlify",
+ "__netlify__": "folder-netlify",
+ "enum": "folder-enum",
+ ".enum": "folder-enum",
+ "_enum": "folder-enum",
+ "__enum__": "folder-enum",
+ "enums": "folder-enum",
+ ".enums": "folder-enum",
+ "_enums": "folder-enum",
+ "__enums__": "folder-enum",
+ "pact": "folder-contract",
+ ".pact": "folder-contract",
+ "_pact": "folder-contract",
+ "__pact__": "folder-contract",
+ "pacts": "folder-contract",
+ ".pacts": "folder-contract",
+ "_pacts": "folder-contract",
+ "__pacts__": "folder-contract",
+ "contract": "folder-contract",
+ ".contract": "folder-contract",
+ "_contract": "folder-contract",
+ "__contract__": "folder-contract",
+ "contracts": "folder-contract",
+ ".contracts": "folder-contract",
+ "_contracts": "folder-contract",
+ "__contracts__": "folder-contract",
+ "contract-testing": "folder-contract",
+ ".contract-testing": "folder-contract",
+ "_contract-testing": "folder-contract",
+ "__contract-testing__": "folder-contract",
+ "contract-test": "folder-contract",
+ ".contract-test": "folder-contract",
+ "_contract-test": "folder-contract",
+ "__contract-test__": "folder-contract",
+ "contract-tests": "folder-contract",
+ ".contract-tests": "folder-contract",
+ "_contract-tests": "folder-contract",
+ "__contract-tests__": "folder-contract",
+ "helm": "folder-helm",
+ ".helm": "folder-helm",
+ "_helm": "folder-helm",
+ "__helm__": "folder-helm",
+ "helmchart": "folder-helm",
+ ".helmchart": "folder-helm",
+ "_helmchart": "folder-helm",
+ "__helmchart__": "folder-helm",
+ "helmcharts": "folder-helm",
+ ".helmcharts": "folder-helm",
+ "_helmcharts": "folder-helm",
+ "__helmcharts__": "folder-helm",
+ "queue": "folder-queue",
+ ".queue": "folder-queue",
+ "_queue": "folder-queue",
+ "__queue__": "folder-queue",
+ "queues": "folder-queue",
+ ".queues": "folder-queue",
+ "_queues": "folder-queue",
+ "__queues__": "folder-queue",
+ "bull": "folder-queue",
+ ".bull": "folder-queue",
+ "_bull": "folder-queue",
+ "__bull__": "folder-queue",
+ "mq": "folder-queue",
+ ".mq": "folder-queue",
+ "_mq": "folder-queue",
+ "__mq__": "folder-queue",
+ "vercel": "folder-vercel",
+ ".vercel": "folder-vercel",
+ "_vercel": "folder-vercel",
+ "__vercel__": "folder-vercel",
+ "now": "folder-vercel",
+ ".now": "folder-vercel",
+ "_now": "folder-vercel",
+ "__now__": "folder-vercel",
+ "cypress": "folder-cypress",
+ ".cypress": "folder-cypress",
+ "_cypress": "folder-cypress",
+ "__cypress__": "folder-cypress",
+ "decorator": "folder-decorators",
+ ".decorator": "folder-decorators",
+ "_decorator": "folder-decorators",
+ "__decorator__": "folder-decorators",
+ "decorators": "folder-decorators",
+ ".decorators": "folder-decorators",
+ "_decorators": "folder-decorators",
+ "__decorators__": "folder-decorators",
+ "java": "folder-java",
+ ".java": "folder-java",
+ "_java": "folder-java",
+ "__java__": "folder-java",
+ "resolver": "folder-resolver",
+ ".resolver": "folder-resolver",
+ "_resolver": "folder-resolver",
+ "__resolver__": "folder-resolver",
+ "resolvers": "folder-resolver",
+ ".resolvers": "folder-resolver",
+ "_resolvers": "folder-resolver",
+ "__resolvers__": "folder-resolver",
+ "angular": "folder-angular",
+ ".angular": "folder-angular",
+ "_angular": "folder-angular",
+ "__angular__": "folder-angular",
+ "unity": "folder-unity",
+ ".unity": "folder-unity",
+ "_unity": "folder-unity",
+ "__unity__": "folder-unity",
+ "pdf": "folder-pdf",
+ ".pdf": "folder-pdf",
+ "_pdf": "folder-pdf",
+ "__pdf__": "folder-pdf",
+ "pdfs": "folder-pdf",
+ ".pdfs": "folder-pdf",
+ "_pdfs": "folder-pdf",
+ "__pdfs__": "folder-pdf",
+ "protobuf": "folder-proto",
+ ".protobuf": "folder-proto",
+ "_protobuf": "folder-proto",
+ "__protobuf__": "folder-proto",
+ "protobufs": "folder-proto",
+ ".protobufs": "folder-proto",
+ "_protobufs": "folder-proto",
+ "__protobufs__": "folder-proto",
+ "proto": "folder-proto",
+ ".proto": "folder-proto",
+ "_proto": "folder-proto",
+ "protos": "folder-proto",
+ ".protos": "folder-proto",
+ "_protos": "folder-proto",
+ "__protos__": "folder-proto",
+ "plastic": "folder-plastic",
+ ".plastic": "folder-plastic",
+ "_plastic": "folder-plastic",
+ "__plastic__": "folder-plastic",
+ "gamemaker": "folder-gamemaker",
+ ".gamemaker": "folder-gamemaker",
+ "_gamemaker": "folder-gamemaker",
+ "__gamemaker__": "folder-gamemaker",
+ "gamemaker2": "folder-gamemaker",
+ ".gamemaker2": "folder-gamemaker",
+ "_gamemaker2": "folder-gamemaker",
+ "__gamemaker2__": "folder-gamemaker",
+ "hg": "folder-mercurial",
+ ".hg": "folder-mercurial",
+ "_hg": "folder-mercurial",
+ "__hg__": "folder-mercurial",
+ "hghooks": "folder-mercurial",
+ ".hghooks": "folder-mercurial",
+ "_hghooks": "folder-mercurial",
+ "__hghooks__": "folder-mercurial",
+ "hgext": "folder-mercurial",
+ ".hgext": "folder-mercurial",
+ "_hgext": "folder-mercurial",
+ "__hgext__": "folder-mercurial",
+ "godot": "folder-godot",
+ ".godot": "folder-godot",
+ "_godot": "folder-godot",
+ "__godot__": "folder-godot",
+ "godot-cpp": "folder-godot",
+ ".godot-cpp": "folder-godot",
+ "_godot-cpp": "folder-godot",
+ "__godot-cpp__": "folder-godot",
+ "lottie": "folder-lottie",
+ ".lottie": "folder-lottie",
+ "_lottie": "folder-lottie",
+ "__lottie__": "folder-lottie",
+ "lotties": "folder-lottie",
+ ".lotties": "folder-lottie",
+ "_lotties": "folder-lottie",
+ "__lotties__": "folder-lottie",
+ "lottiefiles": "folder-lottie",
+ ".lottiefiles": "folder-lottie",
+ "_lottiefiles": "folder-lottie",
+ "__lottiefiles__": "folder-lottie",
+ "taskfile": "folder-taskfile",
+ ".taskfile": "folder-taskfile",
+ "_taskfile": "folder-taskfile",
+ "__taskfile__": "folder-taskfile",
+ "taskfiles": "folder-taskfile",
+ ".taskfiles": "folder-taskfile",
+ "_taskfiles": "folder-taskfile",
+ "__taskfiles__": "folder-taskfile",
+ "drizzle": "folder-drizzle",
+ ".drizzle": "folder-drizzle",
+ "_drizzle": "folder-drizzle",
+ "__drizzle__": "folder-drizzle",
+ "cloudflare": "folder-cloudflare",
+ ".cloudflare": "folder-cloudflare",
+ "_cloudflare": "folder-cloudflare",
+ "__cloudflare__": "folder-cloudflare",
+ "seeds": "folder-seeders",
+ ".seeds": "folder-seeders",
+ "_seeds": "folder-seeders",
+ "__seeds__": "folder-seeders",
+ "seeders": "folder-seeders",
+ ".seeders": "folder-seeders",
+ "_seeders": "folder-seeders",
+ "__seeders__": "folder-seeders",
+ "seed": "folder-seeders",
+ ".seed": "folder-seeders",
+ "_seed": "folder-seeders",
+ "__seed__": "folder-seeders",
+ "seeding": "folder-seeders",
+ ".seeding": "folder-seeders",
+ "_seeding": "folder-seeders",
+ "__seeding__": "folder-seeders",
+ "store": "folder-store",
+ ".store": "folder-store",
+ "_store": "folder-store",
+ "__store__": "folder-store",
+ "stores": "folder-store",
+ ".stores": "folder-store",
+ "_stores": "folder-store",
+ "__stores__": "folder-store",
+ "bicep": "folder-bicep",
+ ".bicep": "folder-bicep",
+ "_bicep": "folder-bicep",
+ "__bicep__": "folder-bicep",
+ "snap": "folder-snapcraft",
+ ".snap": "folder-snapcraft",
+ "_snap": "folder-snapcraft",
+ "__snap__": "folder-snapcraft",
+ "snapcraft": "folder-snapcraft",
+ ".snapcraft": "folder-snapcraft",
+ "_snapcraft": "folder-snapcraft",
+ "__snapcraft__": "folder-snapcraft",
+ "dev": "folder-development",
+ ".dev": "folder-development",
+ "_dev": "folder-development",
+ "__dev__": "folder-development",
+ "development": "folder-development",
+ ".development": "folder-development",
+ "_development": "folder-development",
+ "__development__": "folder-development",
+ "flutter": "folder-flutter",
+ ".flutter": "folder-flutter",
+ "_flutter": "folder-flutter",
+ "__flutter__": "folder-flutter",
+ "snippet": "folder-snippet",
+ ".snippet": "folder-snippet",
+ "_snippet": "folder-snippet",
+ "__snippet__": "folder-snippet",
+ "snippets": "folder-snippet",
+ ".snippets": "folder-snippet",
+ "_snippets": "folder-snippet",
+ "__snippets__": "folder-snippet",
+ "element": "folder-element",
+ ".element": "folder-element",
+ "_element": "folder-element",
+ "__element__": "folder-element",
+ "elements": "folder-element",
+ ".elements": "folder-element",
+ "_elements": "folder-element",
+ "__elements__": "folder-element",
+ "src-tauri": "folder-src-tauri",
+ ".src-tauri": "folder-src-tauri",
+ "_src-tauri": "folder-src-tauri",
+ "__src-tauri__": "folder-src-tauri",
+ "favicon": "folder-favicon",
+ ".favicon": "folder-favicon",
+ "_favicon": "folder-favicon",
+ "__favicon__": "folder-favicon",
+ "favicons": "folder-favicon",
+ ".favicons": "folder-favicon",
+ "_favicons": "folder-favicon",
+ "__favicons__": "folder-favicon",
+ "lefthook": "folder-lefthook",
+ ".lefthook": "folder-lefthook",
+ "_lefthook": "folder-lefthook",
+ "__lefthook__": "folder-lefthook",
+ "lefthook-local": "folder-lefthook",
+ ".lefthook-local": "folder-lefthook",
+ "_lefthook-local": "folder-lefthook",
+ "__lefthook-local__": "folder-lefthook",
+ "bloc": "folder-bloc",
+ ".bloc": "folder-bloc",
+ "_bloc": "folder-bloc",
+ "__bloc__": "folder-bloc",
+ "cubit": "folder-bloc",
+ ".cubit": "folder-bloc",
+ "_cubit": "folder-bloc",
+ "__cubit__": "folder-bloc",
+ "blocs": "folder-bloc",
+ ".blocs": "folder-bloc",
+ "_blocs": "folder-bloc",
+ "__blocs__": "folder-bloc",
+ "cubits": "folder-bloc",
+ ".cubits": "folder-bloc",
+ "_cubits": "folder-bloc",
+ "__cubits__": "folder-bloc",
+ "powershell": "folder-powershell",
+ ".powershell": "folder-powershell",
+ "_powershell": "folder-powershell",
+ "__powershell__": "folder-powershell",
+ "ps": "folder-powershell",
+ ".ps": "folder-powershell",
+ "_ps": "folder-powershell",
+ "__ps__": "folder-powershell",
+ "ps1": "folder-powershell",
+ ".ps1": "folder-powershell",
+ "_ps1": "folder-powershell",
+ "__ps1__": "folder-powershell",
+ "repository": "folder-repository",
+ ".repository": "folder-repository",
+ "_repository": "folder-repository",
+ "__repository__": "folder-repository",
+ "repositories": "folder-repository",
+ ".repositories": "folder-repository",
+ "_repositories": "folder-repository",
+ "__repositories__": "folder-repository",
+ "repo": "folder-repository",
+ ".repo": "folder-repository",
+ "_repo": "folder-repository",
+ "__repo__": "folder-repository",
+ "repos": "folder-repository",
+ ".repos": "folder-repository",
+ "_repos": "folder-repository",
+ "__repos__": "folder-repository",
+ "luau": "folder-luau",
+ ".luau": "folder-luau",
+ "_luau": "folder-luau",
+ "__luau__": "folder-luau",
+ "obsidian": "folder-obsidian",
+ ".obsidian": "folder-obsidian",
+ "_obsidian": "folder-obsidian",
+ "__obsidian__": "folder-obsidian",
+ "trash": "folder-trash",
+ ".trash": "folder-trash",
+ "_trash": "folder-trash",
+ "__trash__": "folder-trash",
+ "cline_docs": "folder-cline",
+ ".cline_docs": "folder-cline",
+ "_cline_docs": "folder-cline",
+ "__cline_docs__": "folder-cline",
+ "liquibase": "folder-liquibase",
+ ".liquibase": "folder-liquibase",
+ "_liquibase": "folder-liquibase",
+ "__liquibase__": "folder-liquibase",
+ "dart": "folder-dart",
+ ".dart": "folder-dart",
+ "_dart": "folder-dart",
+ "__dart__": "folder-dart",
+ "dart_tool": "folder-dart",
+ ".dart_tool": "folder-dart",
+ "_dart_tool": "folder-dart",
+ "__dart_tool__": "folder-dart",
+ "dart_tools": "folder-dart",
+ ".dart_tools": "folder-dart",
+ "_dart_tools": "folder-dart",
+ "__dart_tools__": "folder-dart",
+ "zeabur": "folder-zeabur",
+ ".zeabur": "folder-zeabur",
+ "_zeabur": "folder-zeabur",
+ "__zeabur__": "folder-zeabur",
+ "kusto": "folder-kusto",
+ ".kusto": "folder-kusto",
+ "_kusto": "folder-kusto",
+ "__kusto__": "folder-kusto",
+ "kql": "folder-kusto",
+ ".kql": "folder-kusto",
+ "_kql": "folder-kusto",
+ "__kql__": "folder-kusto",
+ "policy": "folder-policy",
+ ".policy": "folder-policy",
+ "_policy": "folder-policy",
+ "__policy__": "folder-policy",
+ "policies": "folder-policy",
+ ".policies": "folder-policy",
+ "_policies": "folder-policy",
+ "__policies__": "folder-policy",
+ "attachment": "folder-attachment",
+ ".attachment": "folder-attachment",
+ "_attachment": "folder-attachment",
+ "__attachment__": "folder-attachment",
+ "attachments": "folder-attachment",
+ ".attachments": "folder-attachment",
+ "_attachments": "folder-attachment",
+ "__attachments__": "folder-attachment",
+ "bibliography": "folder-bibliography",
+ ".bibliography": "folder-bibliography",
+ "_bibliography": "folder-bibliography",
+ "__bibliography__": "folder-bibliography",
+ "bibliographies": "folder-bibliography",
+ ".bibliographies": "folder-bibliography",
+ "_bibliographies": "folder-bibliography",
+ "__bibliographies__": "folder-bibliography",
+ "book": "folder-bibliography",
+ ".book": "folder-bibliography",
+ "_book": "folder-bibliography",
+ "__book__": "folder-bibliography",
+ "books": "folder-bibliography",
+ ".books": "folder-bibliography",
+ "_books": "folder-bibliography",
+ "__books__": "folder-bibliography",
+ "link": "folder-link",
+ ".link": "folder-link",
+ "_link": "folder-link",
+ "__link__": "folder-link",
+ "links": "folder-link",
+ ".links": "folder-link",
+ "_links": "folder-link",
+ "__links__": "folder-link",
+ "pytorch": "folder-pytorch",
+ ".pytorch": "folder-pytorch",
+ "_pytorch": "folder-pytorch",
+ "__pytorch__": "folder-pytorch",
+ "torch": "folder-pytorch",
+ ".torch": "folder-pytorch",
+ "_torch": "folder-pytorch",
+ "__torch__": "folder-pytorch",
+ "blender": "folder-blender",
+ ".blender": "folder-blender",
+ "_blender": "folder-blender",
+ "__blender__": "folder-blender",
+ "blender-assets": "folder-blender",
+ ".blender-assets": "folder-blender",
+ "_blender-assets": "folder-blender",
+ "__blender-assets__": "folder-blender",
+ "blender-files": "folder-blender",
+ ".blender-files": "folder-blender",
+ "_blender-files": "folder-blender",
+ "__blender-files__": "folder-blender",
+ "blender-project": "folder-blender",
+ ".blender-project": "folder-blender",
+ "_blender-project": "folder-blender",
+ "__blender-project__": "folder-blender",
+ "blender-models": "folder-blender",
+ ".blender-models": "folder-blender",
+ "_blender-models": "folder-blender",
+ "__blender-models__": "folder-blender",
+ "meta-inf": "folder-config",
+ ".meta-inf": "folder-config",
+ "_meta-inf": "folder-config",
+ "__meta-inf__": "folder-config",
+ "github/issue_template": "folder-template",
+ ".github/issue_template": "folder-template",
+ "_github/issue_template": "folder-template",
+ "__github/issue_template__": "folder-template",
+ "github/pull_request_template": "folder-template",
+ ".github/pull_request_template": "folder-template",
+ "_github/pull_request_template": "folder-template",
+ "__github/pull_request_template__": "folder-template",
+ "ds_store": "folder-macos",
+ ".ds_store": "folder-macos",
+ "_ds_store": "folder-macos",
+ "__ds_store__": "folder-macos",
+ "iphone": "folder-macos",
+ ".iphone": "folder-macos",
+ "_iphone": "folder-macos",
+ "__iphone__": "folder-macos",
+ "ipad": "folder-macos",
+ ".ipad": "folder-macos",
+ "_ipad": "folder-macos",
+ "__ipad__": "folder-macos",
+ "ipod": "folder-macos",
+ ".ipod": "folder-macos",
+ "_ipod": "folder-macos",
+ "__ipod__": "folder-macos"
+ },
+ "folderNamesExpanded": {
+ "rust": "folder-rust-open",
+ ".rust": "folder-rust-open",
+ "_rust": "folder-rust-open",
+ "__rust__": "folder-rust-open",
+ "bot": "folder-robot-open",
+ ".bot": "folder-robot-open",
+ "_bot": "folder-robot-open",
+ "__bot__": "folder-robot-open",
+ "bots": "folder-robot-open",
+ ".bots": "folder-robot-open",
+ "_bots": "folder-robot-open",
+ "__bots__": "folder-robot-open",
+ "robot": "folder-robot-open",
+ ".robot": "folder-robot-open",
+ "_robot": "folder-robot-open",
+ "__robot__": "folder-robot-open",
+ "robots": "folder-robot-open",
+ ".robots": "folder-robot-open",
+ "_robots": "folder-robot-open",
+ "__robots__": "folder-robot-open",
+ "src": "folder-src-open",
+ ".src": "folder-src-open",
+ "_src": "folder-src-open",
+ "__src__": "folder-src-open",
+ "srcs": "folder-src-open",
+ ".srcs": "folder-src-open",
+ "_srcs": "folder-src-open",
+ "__srcs__": "folder-src-open",
+ "source": "folder-src-open",
+ ".source": "folder-src-open",
+ "_source": "folder-src-open",
+ "__source__": "folder-src-open",
+ "sources": "folder-src-open",
+ ".sources": "folder-src-open",
+ "_sources": "folder-src-open",
+ "__sources__": "folder-src-open",
+ "code": "folder-src-open",
+ ".code": "folder-src-open",
+ "_code": "folder-src-open",
+ "__code__": "folder-src-open",
+ "dist": "folder-dist-open",
+ ".dist": "folder-dist-open",
+ "_dist": "folder-dist-open",
+ "__dist__": "folder-dist-open",
+ "out": "folder-dist-open",
+ ".out": "folder-dist-open",
+ "_out": "folder-dist-open",
+ "__out__": "folder-dist-open",
+ "output": "folder-dist-open",
+ ".output": "folder-dist-open",
+ "_output": "folder-dist-open",
+ "__output__": "folder-dist-open",
+ "build": "folder-dist-open",
+ ".build": "folder-dist-open",
+ "_build": "folder-dist-open",
+ "__build__": "folder-dist-open",
+ "builds": "folder-dist-open",
+ ".builds": "folder-dist-open",
+ "_builds": "folder-dist-open",
+ "__builds__": "folder-dist-open",
+ "release": "folder-dist-open",
+ ".release": "folder-dist-open",
+ "_release": "folder-dist-open",
+ "__release__": "folder-dist-open",
+ "bin": "folder-dist-open",
+ ".bin": "folder-dist-open",
+ "_bin": "folder-dist-open",
+ "__bin__": "folder-dist-open",
+ "distribution": "folder-dist-open",
+ ".distribution": "folder-dist-open",
+ "_distribution": "folder-dist-open",
+ "__distribution__": "folder-dist-open",
+ "css": "folder-css-open",
+ ".css": "folder-css-open",
+ "_css": "folder-css-open",
+ "__css__": "folder-css-open",
+ "stylesheet": "folder-css-open",
+ ".stylesheet": "folder-css-open",
+ "_stylesheet": "folder-css-open",
+ "__stylesheet__": "folder-css-open",
+ "stylesheets": "folder-css-open",
+ ".stylesheets": "folder-css-open",
+ "_stylesheets": "folder-css-open",
+ "__stylesheets__": "folder-css-open",
+ "style": "folder-css-open",
+ ".style": "folder-css-open",
+ "_style": "folder-css-open",
+ "__style__": "folder-css-open",
+ "styles": "folder-css-open",
+ ".styles": "folder-css-open",
+ "_styles": "folder-css-open",
+ "__styles__": "folder-css-open",
+ "sass": "folder-sass-open",
+ ".sass": "folder-sass-open",
+ "_sass": "folder-sass-open",
+ "__sass__": "folder-sass-open",
+ "scss": "folder-sass-open",
+ ".scss": "folder-sass-open",
+ "_scss": "folder-sass-open",
+ "__scss__": "folder-sass-open",
+ "tv": "folder-television-open",
+ ".tv": "folder-television-open",
+ "_tv": "folder-television-open",
+ "__tv__": "folder-television-open",
+ "television": "folder-television-open",
+ ".television": "folder-television-open",
+ "_television": "folder-television-open",
+ "__television__": "folder-television-open",
+ "desktop": "folder-desktop-open",
+ ".desktop": "folder-desktop-open",
+ "_desktop": "folder-desktop-open",
+ "__desktop__": "folder-desktop-open",
+ "display": "folder-desktop-open",
+ ".display": "folder-desktop-open",
+ "_display": "folder-desktop-open",
+ "__display__": "folder-desktop-open",
+ "console": "folder-console-open",
+ ".console": "folder-console-open",
+ "_console": "folder-console-open",
+ "__console__": "folder-console-open",
+ "images": "folder-images-open",
+ ".images": "folder-images-open",
+ "_images": "folder-images-open",
+ "__images__": "folder-images-open",
+ "image": "folder-images-open",
+ ".image": "folder-images-open",
+ "_image": "folder-images-open",
+ "__image__": "folder-images-open",
+ "imgs": "folder-images-open",
+ ".imgs": "folder-images-open",
+ "_imgs": "folder-images-open",
+ "__imgs__": "folder-images-open",
+ "img": "folder-images-open",
+ ".img": "folder-images-open",
+ "_img": "folder-images-open",
+ "__img__": "folder-images-open",
+ "icons": "folder-images-open",
+ ".icons": "folder-images-open",
+ "_icons": "folder-images-open",
+ "__icons__": "folder-images-open",
+ "icon": "folder-images-open",
+ ".icon": "folder-images-open",
+ "_icon": "folder-images-open",
+ "__icon__": "folder-images-open",
+ "icos": "folder-images-open",
+ ".icos": "folder-images-open",
+ "_icos": "folder-images-open",
+ "__icos__": "folder-images-open",
+ "ico": "folder-images-open",
+ ".ico": "folder-images-open",
+ "_ico": "folder-images-open",
+ "__ico__": "folder-images-open",
+ "figures": "folder-images-open",
+ ".figures": "folder-images-open",
+ "_figures": "folder-images-open",
+ "__figures__": "folder-images-open",
+ "figure": "folder-images-open",
+ ".figure": "folder-images-open",
+ "_figure": "folder-images-open",
+ "__figure__": "folder-images-open",
+ "figs": "folder-images-open",
+ ".figs": "folder-images-open",
+ "_figs": "folder-images-open",
+ "__figs__": "folder-images-open",
+ "fig": "folder-images-open",
+ ".fig": "folder-images-open",
+ "_fig": "folder-images-open",
+ "__fig__": "folder-images-open",
+ "screenshot": "folder-images-open",
+ ".screenshot": "folder-images-open",
+ "_screenshot": "folder-images-open",
+ "__screenshot__": "folder-images-open",
+ "screenshots": "folder-images-open",
+ ".screenshots": "folder-images-open",
+ "_screenshots": "folder-images-open",
+ "__screenshots__": "folder-images-open",
+ "screengrab": "folder-images-open",
+ ".screengrab": "folder-images-open",
+ "_screengrab": "folder-images-open",
+ "__screengrab__": "folder-images-open",
+ "screengrabs": "folder-images-open",
+ ".screengrabs": "folder-images-open",
+ "_screengrabs": "folder-images-open",
+ "__screengrabs__": "folder-images-open",
+ "pic": "folder-images-open",
+ ".pic": "folder-images-open",
+ "_pic": "folder-images-open",
+ "__pic__": "folder-images-open",
+ "pics": "folder-images-open",
+ ".pics": "folder-images-open",
+ "_pics": "folder-images-open",
+ "__pics__": "folder-images-open",
+ "picture": "folder-images-open",
+ ".picture": "folder-images-open",
+ "_picture": "folder-images-open",
+ "__picture__": "folder-images-open",
+ "pictures": "folder-images-open",
+ ".pictures": "folder-images-open",
+ "_pictures": "folder-images-open",
+ "__pictures__": "folder-images-open",
+ "photo": "folder-images-open",
+ ".photo": "folder-images-open",
+ "_photo": "folder-images-open",
+ "__photo__": "folder-images-open",
+ "photos": "folder-images-open",
+ ".photos": "folder-images-open",
+ "_photos": "folder-images-open",
+ "__photos__": "folder-images-open",
+ "photograph": "folder-images-open",
+ ".photograph": "folder-images-open",
+ "_photograph": "folder-images-open",
+ "__photograph__": "folder-images-open",
+ "photographs": "folder-images-open",
+ ".photographs": "folder-images-open",
+ "_photographs": "folder-images-open",
+ "__photographs__": "folder-images-open",
+ "script": "folder-scripts-open",
+ ".script": "folder-scripts-open",
+ "_script": "folder-scripts-open",
+ "__script__": "folder-scripts-open",
+ "scripts": "folder-scripts-open",
+ ".scripts": "folder-scripts-open",
+ "_scripts": "folder-scripts-open",
+ "__scripts__": "folder-scripts-open",
+ "scripting": "folder-scripts-open",
+ ".scripting": "folder-scripts-open",
+ "_scripting": "folder-scripts-open",
+ "__scripting__": "folder-scripts-open",
+ "node": "folder-node-open",
+ ".node": "folder-node-open",
+ "_node": "folder-node-open",
+ "__node__": "folder-node-open",
+ "nodejs": "folder-node-open",
+ ".nodejs": "folder-node-open",
+ "_nodejs": "folder-node-open",
+ "__nodejs__": "folder-node-open",
+ "node_modules": "folder-node-open",
+ ".node_modules": "folder-node-open",
+ "_node_modules": "folder-node-open",
+ "__node_modules__": "folder-node-open",
+ "js": "folder-javascript-open",
+ ".js": "folder-javascript-open",
+ "_js": "folder-javascript-open",
+ "__js__": "folder-javascript-open",
+ "javascript": "folder-javascript-open",
+ ".javascript": "folder-javascript-open",
+ "_javascript": "folder-javascript-open",
+ "__javascript__": "folder-javascript-open",
+ "javascripts": "folder-javascript-open",
+ ".javascripts": "folder-javascript-open",
+ "_javascripts": "folder-javascript-open",
+ "__javascripts__": "folder-javascript-open",
+ "json": "folder-json-open",
+ ".json": "folder-json-open",
+ "_json": "folder-json-open",
+ "__json__": "folder-json-open",
+ "jsons": "folder-json-open",
+ ".jsons": "folder-json-open",
+ "_jsons": "folder-json-open",
+ "__jsons__": "folder-json-open",
+ "font": "folder-font-open",
+ ".font": "folder-font-open",
+ "_font": "folder-font-open",
+ "__font__": "folder-font-open",
+ "fonts": "folder-font-open",
+ ".fonts": "folder-font-open",
+ "_fonts": "folder-font-open",
+ "__fonts__": "folder-font-open",
+ "bower_components": "folder-bower-open",
+ ".bower_components": "folder-bower-open",
+ "_bower_components": "folder-bower-open",
+ "__bower_components__": "folder-bower-open",
+ "test": "folder-test-open",
+ ".test": "folder-test-open",
+ "_test": "folder-test-open",
+ "__test__": "folder-test-open",
+ "tests": "folder-test-open",
+ ".tests": "folder-test-open",
+ "_tests": "folder-test-open",
+ "__tests__": "folder-test-open",
+ "testing": "folder-test-open",
+ ".testing": "folder-test-open",
+ "_testing": "folder-test-open",
+ "__testing__": "folder-test-open",
+ "snapshots": "folder-test-open",
+ ".snapshots": "folder-test-open",
+ "_snapshots": "folder-test-open",
+ "__snapshots__": "folder-test-open",
+ "spec": "folder-test-open",
+ ".spec": "folder-test-open",
+ "_spec": "folder-test-open",
+ "__spec__": "folder-test-open",
+ "specs": "folder-test-open",
+ ".specs": "folder-test-open",
+ "_specs": "folder-test-open",
+ "__specs__": "folder-test-open",
+ "directive": "folder-directive-open",
+ ".directive": "folder-directive-open",
+ "_directive": "folder-directive-open",
+ "__directive__": "folder-directive-open",
+ "directives": "folder-directive-open",
+ ".directives": "folder-directive-open",
+ "_directives": "folder-directive-open",
+ "__directives__": "folder-directive-open",
+ "jinja": "folder-jinja-open",
+ ".jinja": "folder-jinja-open",
+ "_jinja": "folder-jinja-open",
+ "__jinja__": "folder-jinja-open",
+ "jinja2": "folder-jinja-open",
+ ".jinja2": "folder-jinja-open",
+ "_jinja2": "folder-jinja-open",
+ "__jinja2__": "folder-jinja-open",
+ "j2": "folder-jinja-open",
+ ".j2": "folder-jinja-open",
+ "_j2": "folder-jinja-open",
+ "__j2__": "folder-jinja-open",
+ "markdown": "folder-markdown-open",
+ ".markdown": "folder-markdown-open",
+ "_markdown": "folder-markdown-open",
+ "__markdown__": "folder-markdown-open",
+ "md": "folder-markdown-open",
+ ".md": "folder-markdown-open",
+ "_md": "folder-markdown-open",
+ "__md__": "folder-markdown-open",
+ "pdm-plugins": "folder-pdm-open",
+ ".pdm-plugins": "folder-pdm-open",
+ "_pdm-plugins": "folder-pdm-open",
+ "__pdm-plugins__": "folder-pdm-open",
+ "pdm-build": "folder-pdm-open",
+ ".pdm-build": "folder-pdm-open",
+ "_pdm-build": "folder-pdm-open",
+ "__pdm-build__": "folder-pdm-open",
+ "php": "folder-php-open",
+ ".php": "folder-php-open",
+ "_php": "folder-php-open",
+ "__php__": "folder-php-open",
+ "phpmailer": "folder-phpmailer-open",
+ ".phpmailer": "folder-phpmailer-open",
+ "_phpmailer": "folder-phpmailer-open",
+ "__phpmailer__": "folder-phpmailer-open",
+ "sublime": "folder-sublime-open",
+ ".sublime": "folder-sublime-open",
+ "_sublime": "folder-sublime-open",
+ "__sublime__": "folder-sublime-open",
+ "doc": "folder-docs-open",
+ ".doc": "folder-docs-open",
+ "_doc": "folder-docs-open",
+ "__doc__": "folder-docs-open",
+ "docs": "folder-docs-open",
+ ".docs": "folder-docs-open",
+ "_docs": "folder-docs-open",
+ "__docs__": "folder-docs-open",
+ "document": "folder-docs-open",
+ ".document": "folder-docs-open",
+ "_document": "folder-docs-open",
+ "__document__": "folder-docs-open",
+ "documents": "folder-docs-open",
+ ".documents": "folder-docs-open",
+ "_documents": "folder-docs-open",
+ "__documents__": "folder-docs-open",
+ "documentation": "folder-docs-open",
+ ".documentation": "folder-docs-open",
+ "_documentation": "folder-docs-open",
+ "__documentation__": "folder-docs-open",
+ "post": "folder-docs-open",
+ ".post": "folder-docs-open",
+ "_post": "folder-docs-open",
+ "__post__": "folder-docs-open",
+ "posts": "folder-docs-open",
+ ".posts": "folder-docs-open",
+ "_posts": "folder-docs-open",
+ "__posts__": "folder-docs-open",
+ "article": "folder-docs-open",
+ ".article": "folder-docs-open",
+ "_article": "folder-docs-open",
+ "__article__": "folder-docs-open",
+ "articles": "folder-docs-open",
+ ".articles": "folder-docs-open",
+ "_articles": "folder-docs-open",
+ "__articles__": "folder-docs-open",
+ "wiki": "folder-docs-open",
+ ".wiki": "folder-docs-open",
+ "_wiki": "folder-docs-open",
+ "__wiki__": "folder-docs-open",
+ "news": "folder-docs-open",
+ ".news": "folder-docs-open",
+ "_news": "folder-docs-open",
+ "__news__": "folder-docs-open",
+ "github/workflows": "folder-gh-workflows-open",
+ ".github/workflows": "folder-gh-workflows-open",
+ "_github/workflows": "folder-gh-workflows-open",
+ "__github/workflows__": "folder-gh-workflows-open",
+ "git": "folder-git-open",
+ ".git": "folder-git-open",
+ "_git": "folder-git-open",
+ "__git__": "folder-git-open",
+ "patches": "folder-git-open",
+ ".patches": "folder-git-open",
+ "_patches": "folder-git-open",
+ "__patches__": "folder-git-open",
+ "githooks": "folder-git-open",
+ ".githooks": "folder-git-open",
+ "_githooks": "folder-git-open",
+ "__githooks__": "folder-git-open",
+ "submodules": "folder-git-open",
+ ".submodules": "folder-git-open",
+ "_submodules": "folder-git-open",
+ "__submodules__": "folder-git-open",
+ "github": "folder-github-open",
+ ".github": "folder-github-open",
+ "_github": "folder-github-open",
+ "__github__": "folder-github-open",
+ "gitea": "folder-gitea-open",
+ ".gitea": "folder-gitea-open",
+ "_gitea": "folder-gitea-open",
+ "__gitea__": "folder-gitea-open",
+ "gitlab": "folder-gitlab-open",
+ ".gitlab": "folder-gitlab-open",
+ "_gitlab": "folder-gitlab-open",
+ "__gitlab__": "folder-gitlab-open",
+ "forgejo": "folder-forgejo-open",
+ ".forgejo": "folder-forgejo-open",
+ "_forgejo": "folder-forgejo-open",
+ "__forgejo__": "folder-forgejo-open",
+ "vscode": "folder-vscode-open",
+ ".vscode": "folder-vscode-open",
+ "_vscode": "folder-vscode-open",
+ "__vscode__": "folder-vscode-open",
+ "vscode-test": "folder-vscode-open",
+ ".vscode-test": "folder-vscode-open",
+ "_vscode-test": "folder-vscode-open",
+ "__vscode-test__": "folder-vscode-open",
+ "view": "folder-views-open",
+ ".view": "folder-views-open",
+ "_view": "folder-views-open",
+ "__view__": "folder-views-open",
+ "views": "folder-views-open",
+ ".views": "folder-views-open",
+ "_views": "folder-views-open",
+ "__views__": "folder-views-open",
+ "screen": "folder-views-open",
+ ".screen": "folder-views-open",
+ "_screen": "folder-views-open",
+ "__screen__": "folder-views-open",
+ "screens": "folder-views-open",
+ ".screens": "folder-views-open",
+ "_screens": "folder-views-open",
+ "__screens__": "folder-views-open",
+ "page": "folder-views-open",
+ ".page": "folder-views-open",
+ "_page": "folder-views-open",
+ "__page__": "folder-views-open",
+ "pages": "folder-views-open",
+ ".pages": "folder-views-open",
+ "_pages": "folder-views-open",
+ "__pages__": "folder-views-open",
+ "public_html": "folder-views-open",
+ ".public_html": "folder-views-open",
+ "_public_html": "folder-views-open",
+ "__public_html__": "folder-views-open",
+ "html": "folder-views-open",
+ ".html": "folder-views-open",
+ "_html": "folder-views-open",
+ "__html__": "folder-views-open",
+ "vue": "folder-vue-open",
+ ".vue": "folder-vue-open",
+ "_vue": "folder-vue-open",
+ "__vue__": "folder-vue-open",
+ "vuepress": "folder-vuepress-open",
+ ".vuepress": "folder-vuepress-open",
+ "_vuepress": "folder-vuepress-open",
+ "__vuepress__": "folder-vuepress-open",
+ "expo": "folder-expo-open",
+ ".expo": "folder-expo-open",
+ "_expo": "folder-expo-open",
+ "__expo__": "folder-expo-open",
+ "expo-shared": "folder-expo-open",
+ ".expo-shared": "folder-expo-open",
+ "_expo-shared": "folder-expo-open",
+ "__expo-shared__": "folder-expo-open",
+ "cfg": "folder-config-open",
+ ".cfg": "folder-config-open",
+ "_cfg": "folder-config-open",
+ "__cfg__": "folder-config-open",
+ "cfgs": "folder-config-open",
+ ".cfgs": "folder-config-open",
+ "_cfgs": "folder-config-open",
+ "__cfgs__": "folder-config-open",
+ "conf": "folder-config-open",
+ ".conf": "folder-config-open",
+ "_conf": "folder-config-open",
+ "__conf__": "folder-config-open",
+ "confs": "folder-config-open",
+ ".confs": "folder-config-open",
+ "_confs": "folder-config-open",
+ "__confs__": "folder-config-open",
+ "config": "folder-config-open",
+ ".config": "folder-config-open",
+ "_config": "folder-config-open",
+ "__config__": "folder-config-open",
+ "configs": "folder-config-open",
+ ".configs": "folder-config-open",
+ "_configs": "folder-config-open",
+ "__configs__": "folder-config-open",
+ "configuration": "folder-config-open",
+ ".configuration": "folder-config-open",
+ "_configuration": "folder-config-open",
+ "__configuration__": "folder-config-open",
+ "configurations": "folder-config-open",
+ ".configurations": "folder-config-open",
+ "_configurations": "folder-config-open",
+ "__configurations__": "folder-config-open",
+ "setting": "folder-config-open",
+ ".setting": "folder-config-open",
+ "_setting": "folder-config-open",
+ "__setting__": "folder-config-open",
+ "settings": "folder-config-open",
+ ".settings": "folder-config-open",
+ "_settings": "folder-config-open",
+ "__settings__": "folder-config-open",
+ "META-INF": "folder-config-open",
+ ".META-INF": "folder-config-open",
+ "_META-INF": "folder-config-open",
+ "__META-INF__": "folder-config-open",
+ "option": "folder-config-open",
+ ".option": "folder-config-open",
+ "_option": "folder-config-open",
+ "__option__": "folder-config-open",
+ "options": "folder-config-open",
+ ".options": "folder-config-open",
+ "_options": "folder-config-open",
+ "__options__": "folder-config-open",
+ "pref": "folder-config-open",
+ ".pref": "folder-config-open",
+ "_pref": "folder-config-open",
+ "__pref__": "folder-config-open",
+ "prefs": "folder-config-open",
+ ".prefs": "folder-config-open",
+ "_prefs": "folder-config-open",
+ "__prefs__": "folder-config-open",
+ "preference": "folder-config-open",
+ ".preference": "folder-config-open",
+ "_preference": "folder-config-open",
+ "__preference__": "folder-config-open",
+ "preferences": "folder-config-open",
+ ".preferences": "folder-config-open",
+ "_preferences": "folder-config-open",
+ "__preferences__": "folder-config-open",
+ "i18n": "folder-i18n-open",
+ ".i18n": "folder-i18n-open",
+ "_i18n": "folder-i18n-open",
+ "__i18n__": "folder-i18n-open",
+ "internationalization": "folder-i18n-open",
+ ".internationalization": "folder-i18n-open",
+ "_internationalization": "folder-i18n-open",
+ "__internationalization__": "folder-i18n-open",
+ "lang": "folder-i18n-open",
+ ".lang": "folder-i18n-open",
+ "_lang": "folder-i18n-open",
+ "__lang__": "folder-i18n-open",
+ "langs": "folder-i18n-open",
+ ".langs": "folder-i18n-open",
+ "_langs": "folder-i18n-open",
+ "__langs__": "folder-i18n-open",
+ "language": "folder-i18n-open",
+ ".language": "folder-i18n-open",
+ "_language": "folder-i18n-open",
+ "__language__": "folder-i18n-open",
+ "languages": "folder-i18n-open",
+ ".languages": "folder-i18n-open",
+ "_languages": "folder-i18n-open",
+ "__languages__": "folder-i18n-open",
+ "locale": "folder-i18n-open",
+ ".locale": "folder-i18n-open",
+ "_locale": "folder-i18n-open",
+ "__locale__": "folder-i18n-open",
+ "locales": "folder-i18n-open",
+ ".locales": "folder-i18n-open",
+ "_locales": "folder-i18n-open",
+ "__locales__": "folder-i18n-open",
+ "l10n": "folder-i18n-open",
+ ".l10n": "folder-i18n-open",
+ "_l10n": "folder-i18n-open",
+ "__l10n__": "folder-i18n-open",
+ "localization": "folder-i18n-open",
+ ".localization": "folder-i18n-open",
+ "_localization": "folder-i18n-open",
+ "__localization__": "folder-i18n-open",
+ "translation": "folder-i18n-open",
+ ".translation": "folder-i18n-open",
+ "_translation": "folder-i18n-open",
+ "__translation__": "folder-i18n-open",
+ "translate": "folder-i18n-open",
+ ".translate": "folder-i18n-open",
+ "_translate": "folder-i18n-open",
+ "__translate__": "folder-i18n-open",
+ "translations": "folder-i18n-open",
+ ".translations": "folder-i18n-open",
+ "_translations": "folder-i18n-open",
+ "__translations__": "folder-i18n-open",
+ "tx": "folder-i18n-open",
+ ".tx": "folder-i18n-open",
+ "_tx": "folder-i18n-open",
+ "__tx__": "folder-i18n-open",
+ "components": "folder-components-open",
+ ".components": "folder-components-open",
+ "_components": "folder-components-open",
+ "__components__": "folder-components-open",
+ "widget": "folder-components-open",
+ ".widget": "folder-components-open",
+ "_widget": "folder-components-open",
+ "__widget__": "folder-components-open",
+ "widgets": "folder-components-open",
+ ".widgets": "folder-components-open",
+ "_widgets": "folder-components-open",
+ "__widgets__": "folder-components-open",
+ "fragments": "folder-components-open",
+ ".fragments": "folder-components-open",
+ "_fragments": "folder-components-open",
+ "__fragments__": "folder-components-open",
+ "verdaccio": "folder-verdaccio-open",
+ ".verdaccio": "folder-verdaccio-open",
+ "_verdaccio": "folder-verdaccio-open",
+ "__verdaccio__": "folder-verdaccio-open",
+ "aurelia_project": "folder-aurelia-open",
+ ".aurelia_project": "folder-aurelia-open",
+ "_aurelia_project": "folder-aurelia-open",
+ "__aurelia_project__": "folder-aurelia-open",
+ "resource": "folder-resource-open",
+ ".resource": "folder-resource-open",
+ "_resource": "folder-resource-open",
+ "__resource__": "folder-resource-open",
+ "resources": "folder-resource-open",
+ ".resources": "folder-resource-open",
+ "_resources": "folder-resource-open",
+ "__resources__": "folder-resource-open",
+ "res": "folder-resource-open",
+ ".res": "folder-resource-open",
+ "_res": "folder-resource-open",
+ "__res__": "folder-resource-open",
+ "asset": "folder-resource-open",
+ ".asset": "folder-resource-open",
+ "_asset": "folder-resource-open",
+ "__asset__": "folder-resource-open",
+ "assets": "folder-resource-open",
+ ".assets": "folder-resource-open",
+ "_assets": "folder-resource-open",
+ "__assets__": "folder-resource-open",
+ "static": "folder-resource-open",
+ ".static": "folder-resource-open",
+ "_static": "folder-resource-open",
+ "__static__": "folder-resource-open",
+ "report": "folder-resource-open",
+ ".report": "folder-resource-open",
+ "_report": "folder-resource-open",
+ "__report__": "folder-resource-open",
+ "reports": "folder-resource-open",
+ ".reports": "folder-resource-open",
+ "_reports": "folder-resource-open",
+ "__reports__": "folder-resource-open",
+ "lib": "folder-lib-open",
+ ".lib": "folder-lib-open",
+ "_lib": "folder-lib-open",
+ "__lib__": "folder-lib-open",
+ "libs": "folder-lib-open",
+ ".libs": "folder-lib-open",
+ "_libs": "folder-lib-open",
+ "__libs__": "folder-lib-open",
+ "library": "folder-lib-open",
+ ".library": "folder-lib-open",
+ "_library": "folder-lib-open",
+ "__library__": "folder-lib-open",
+ "libraries": "folder-lib-open",
+ ".libraries": "folder-lib-open",
+ "_libraries": "folder-lib-open",
+ "__libraries__": "folder-lib-open",
+ "vendor": "folder-lib-open",
+ ".vendor": "folder-lib-open",
+ "_vendor": "folder-lib-open",
+ "__vendor__": "folder-lib-open",
+ "vendors": "folder-lib-open",
+ ".vendors": "folder-lib-open",
+ "_vendors": "folder-lib-open",
+ "__vendors__": "folder-lib-open",
+ "third-party": "folder-lib-open",
+ ".third-party": "folder-lib-open",
+ "_third-party": "folder-lib-open",
+ "__third-party__": "folder-lib-open",
+ "lib64": "folder-lib-open",
+ ".lib64": "folder-lib-open",
+ "_lib64": "folder-lib-open",
+ "__lib64__": "folder-lib-open",
+ "themes": "folder-theme-open",
+ ".themes": "folder-theme-open",
+ "_themes": "folder-theme-open",
+ "__themes__": "folder-theme-open",
+ "theme": "folder-theme-open",
+ ".theme": "folder-theme-open",
+ "_theme": "folder-theme-open",
+ "__theme__": "folder-theme-open",
+ "color": "folder-theme-open",
+ ".color": "folder-theme-open",
+ "_color": "folder-theme-open",
+ "__color__": "folder-theme-open",
+ "colors": "folder-theme-open",
+ ".colors": "folder-theme-open",
+ "_colors": "folder-theme-open",
+ "__colors__": "folder-theme-open",
+ "design": "folder-theme-open",
+ ".design": "folder-theme-open",
+ "_design": "folder-theme-open",
+ "__design__": "folder-theme-open",
+ "designs": "folder-theme-open",
+ ".designs": "folder-theme-open",
+ "_designs": "folder-theme-open",
+ "__designs__": "folder-theme-open",
+ "palette": "folder-theme-open",
+ ".palette": "folder-theme-open",
+ "_palette": "folder-theme-open",
+ "__palette__": "folder-theme-open",
+ "palettes": "folder-theme-open",
+ ".palettes": "folder-theme-open",
+ "_palettes": "folder-theme-open",
+ "__palettes__": "folder-theme-open",
+ "webpack": "folder-webpack-open",
+ ".webpack": "folder-webpack-open",
+ "_webpack": "folder-webpack-open",
+ "__webpack__": "folder-webpack-open",
+ "global": "folder-global-open",
+ ".global": "folder-global-open",
+ "_global": "folder-global-open",
+ "__global__": "folder-global-open",
+ "public": "folder-public-open",
+ ".public": "folder-public-open",
+ "_public": "folder-public-open",
+ "__public__": "folder-public-open",
+ "www": "folder-public-open",
+ ".www": "folder-public-open",
+ "_www": "folder-public-open",
+ "__www__": "folder-public-open",
+ "wwwroot": "folder-public-open",
+ ".wwwroot": "folder-public-open",
+ "_wwwroot": "folder-public-open",
+ "__wwwroot__": "folder-public-open",
+ "web": "folder-public-open",
+ ".web": "folder-public-open",
+ "_web": "folder-public-open",
+ "__web__": "folder-public-open",
+ "website": "folder-public-open",
+ ".website": "folder-public-open",
+ "_website": "folder-public-open",
+ "__website__": "folder-public-open",
+ "websites": "folder-public-open",
+ ".websites": "folder-public-open",
+ "_websites": "folder-public-open",
+ "__websites__": "folder-public-open",
+ "site": "folder-public-open",
+ ".site": "folder-public-open",
+ "_site": "folder-public-open",
+ "__site__": "folder-public-open",
+ "browser": "folder-public-open",
+ ".browser": "folder-public-open",
+ "_browser": "folder-public-open",
+ "__browser__": "folder-public-open",
+ "browsers": "folder-public-open",
+ ".browsers": "folder-public-open",
+ "_browsers": "folder-public-open",
+ "__browsers__": "folder-public-open",
+ "inc": "folder-include-open",
+ ".inc": "folder-include-open",
+ "_inc": "folder-include-open",
+ "__inc__": "folder-include-open",
+ "include": "folder-include-open",
+ ".include": "folder-include-open",
+ "_include": "folder-include-open",
+ "__include__": "folder-include-open",
+ "includes": "folder-include-open",
+ ".includes": "folder-include-open",
+ "_includes": "folder-include-open",
+ "__includes__": "folder-include-open",
+ "partial": "folder-include-open",
+ ".partial": "folder-include-open",
+ "_partial": "folder-include-open",
+ "__partial__": "folder-include-open",
+ "partials": "folder-include-open",
+ ".partials": "folder-include-open",
+ "_partials": "folder-include-open",
+ "__partials__": "folder-include-open",
+ "inc64": "folder-include-open",
+ ".inc64": "folder-include-open",
+ "_inc64": "folder-include-open",
+ "__inc64__": "folder-include-open",
+ "docker": "folder-docker-open",
+ ".docker": "folder-docker-open",
+ "_docker": "folder-docker-open",
+ "__docker__": "folder-docker-open",
+ "dockerfiles": "folder-docker-open",
+ ".dockerfiles": "folder-docker-open",
+ "_dockerfiles": "folder-docker-open",
+ "__dockerfiles__": "folder-docker-open",
+ "dockerhub": "folder-docker-open",
+ ".dockerhub": "folder-docker-open",
+ "_dockerhub": "folder-docker-open",
+ "__dockerhub__": "folder-docker-open",
+ "astro": "folder-astro-open",
+ ".astro": "folder-astro-open",
+ "_astro": "folder-astro-open",
+ "__astro__": "folder-astro-open",
+ "db": "folder-database-open",
+ ".db": "folder-database-open",
+ "_db": "folder-database-open",
+ "__db__": "folder-database-open",
+ "data": "folder-database-open",
+ ".data": "folder-database-open",
+ "_data": "folder-database-open",
+ "__data__": "folder-database-open",
+ "database": "folder-database-open",
+ ".database": "folder-database-open",
+ "_database": "folder-database-open",
+ "__database__": "folder-database-open",
+ "databases": "folder-database-open",
+ ".databases": "folder-database-open",
+ "_databases": "folder-database-open",
+ "__databases__": "folder-database-open",
+ "sql": "folder-database-open",
+ ".sql": "folder-database-open",
+ "_sql": "folder-database-open",
+ "__sql__": "folder-database-open",
+ "log": "folder-log-open",
+ ".log": "folder-log-open",
+ "_log": "folder-log-open",
+ "__log__": "folder-log-open",
+ "logs": "folder-log-open",
+ ".logs": "folder-log-open",
+ "_logs": "folder-log-open",
+ "__logs__": "folder-log-open",
+ "logging": "folder-log-open",
+ ".logging": "folder-log-open",
+ "_logging": "folder-log-open",
+ "__logging__": "folder-log-open",
+ "target": "folder-target-open",
+ ".target": "folder-target-open",
+ "_target": "folder-target-open",
+ "__target__": "folder-target-open",
+ "temp": "folder-temp-open",
+ ".temp": "folder-temp-open",
+ "_temp": "folder-temp-open",
+ "__temp__": "folder-temp-open",
+ "tmp": "folder-temp-open",
+ ".tmp": "folder-temp-open",
+ "_tmp": "folder-temp-open",
+ "__tmp__": "folder-temp-open",
+ "cached": "folder-temp-open",
+ ".cached": "folder-temp-open",
+ "_cached": "folder-temp-open",
+ "__cached__": "folder-temp-open",
+ "cache": "folder-temp-open",
+ ".cache": "folder-temp-open",
+ "_cache": "folder-temp-open",
+ "__cache__": "folder-temp-open",
+ "aws": "folder-aws-open",
+ ".aws": "folder-aws-open",
+ "_aws": "folder-aws-open",
+ "__aws__": "folder-aws-open",
+ "azure": "folder-aws-open",
+ ".azure": "folder-aws-open",
+ "_azure": "folder-aws-open",
+ "__azure__": "folder-aws-open",
+ "gcp": "folder-aws-open",
+ ".gcp": "folder-aws-open",
+ "_gcp": "folder-aws-open",
+ "__gcp__": "folder-aws-open",
+ "aud": "folder-audio-open",
+ ".aud": "folder-audio-open",
+ "_aud": "folder-audio-open",
+ "__aud__": "folder-audio-open",
+ "auds": "folder-audio-open",
+ ".auds": "folder-audio-open",
+ "_auds": "folder-audio-open",
+ "__auds__": "folder-audio-open",
+ "audio": "folder-audio-open",
+ ".audio": "folder-audio-open",
+ "_audio": "folder-audio-open",
+ "__audio__": "folder-audio-open",
+ "audios": "folder-audio-open",
+ ".audios": "folder-audio-open",
+ "_audios": "folder-audio-open",
+ "__audios__": "folder-audio-open",
+ "music": "folder-audio-open",
+ ".music": "folder-audio-open",
+ "_music": "folder-audio-open",
+ "__music__": "folder-audio-open",
+ "song": "folder-audio-open",
+ ".song": "folder-audio-open",
+ "_song": "folder-audio-open",
+ "__song__": "folder-audio-open",
+ "songs": "folder-audio-open",
+ ".songs": "folder-audio-open",
+ "_songs": "folder-audio-open",
+ "__songs__": "folder-audio-open",
+ "sound": "folder-audio-open",
+ ".sound": "folder-audio-open",
+ "_sound": "folder-audio-open",
+ "__sound__": "folder-audio-open",
+ "sounds": "folder-audio-open",
+ ".sounds": "folder-audio-open",
+ "_sounds": "folder-audio-open",
+ "__sounds__": "folder-audio-open",
+ "voice": "folder-audio-open",
+ ".voice": "folder-audio-open",
+ "_voice": "folder-audio-open",
+ "__voice__": "folder-audio-open",
+ "voices": "folder-audio-open",
+ ".voices": "folder-audio-open",
+ "_voices": "folder-audio-open",
+ "__voices__": "folder-audio-open",
+ "recordings": "folder-audio-open",
+ ".recordings": "folder-audio-open",
+ "_recordings": "folder-audio-open",
+ "__recordings__": "folder-audio-open",
+ "vid": "folder-video-open",
+ ".vid": "folder-video-open",
+ "_vid": "folder-video-open",
+ "__vid__": "folder-video-open",
+ "vids": "folder-video-open",
+ ".vids": "folder-video-open",
+ "_vids": "folder-video-open",
+ "__vids__": "folder-video-open",
+ "video": "folder-video-open",
+ ".video": "folder-video-open",
+ "_video": "folder-video-open",
+ "__video__": "folder-video-open",
+ "videos": "folder-video-open",
+ ".videos": "folder-video-open",
+ "_videos": "folder-video-open",
+ "__videos__": "folder-video-open",
+ "movie": "folder-video-open",
+ ".movie": "folder-video-open",
+ "_movie": "folder-video-open",
+ "__movie__": "folder-video-open",
+ "movies": "folder-video-open",
+ ".movies": "folder-video-open",
+ "_movies": "folder-video-open",
+ "__movies__": "folder-video-open",
+ "media": "folder-video-open",
+ ".media": "folder-video-open",
+ "_media": "folder-video-open",
+ "__media__": "folder-video-open",
+ "kubernetes": "folder-kubernetes-open",
+ ".kubernetes": "folder-kubernetes-open",
+ "_kubernetes": "folder-kubernetes-open",
+ "__kubernetes__": "folder-kubernetes-open",
+ "k8s": "folder-kubernetes-open",
+ ".k8s": "folder-kubernetes-open",
+ "_k8s": "folder-kubernetes-open",
+ "__k8s__": "folder-kubernetes-open",
+ "import": "folder-import-open",
+ ".import": "folder-import-open",
+ "_import": "folder-import-open",
+ "__import__": "folder-import-open",
+ "imports": "folder-import-open",
+ ".imports": "folder-import-open",
+ "_imports": "folder-import-open",
+ "__imports__": "folder-import-open",
+ "imported": "folder-import-open",
+ ".imported": "folder-import-open",
+ "_imported": "folder-import-open",
+ "__imported__": "folder-import-open",
+ "export": "folder-export-open",
+ ".export": "folder-export-open",
+ "_export": "folder-export-open",
+ "__export__": "folder-export-open",
+ "exports": "folder-export-open",
+ ".exports": "folder-export-open",
+ "_exports": "folder-export-open",
+ "__exports__": "folder-export-open",
+ "exported": "folder-export-open",
+ ".exported": "folder-export-open",
+ "_exported": "folder-export-open",
+ "__exported__": "folder-export-open",
+ "wakatime": "folder-wakatime-open",
+ ".wakatime": "folder-wakatime-open",
+ "_wakatime": "folder-wakatime-open",
+ "__wakatime__": "folder-wakatime-open",
+ "circleci": "folder-circleci-open",
+ ".circleci": "folder-circleci-open",
+ "_circleci": "folder-circleci-open",
+ "__circleci__": "folder-circleci-open",
+ "wordpress-org": "folder-wordpress-open",
+ ".wordpress-org": "folder-wordpress-open",
+ "_wordpress-org": "folder-wordpress-open",
+ "__wordpress-org__": "folder-wordpress-open",
+ "wp-content": "folder-wordpress-open",
+ ".wp-content": "folder-wordpress-open",
+ "_wp-content": "folder-wordpress-open",
+ "__wp-content__": "folder-wordpress-open",
+ "gradle": "folder-gradle-open",
+ ".gradle": "folder-gradle-open",
+ "_gradle": "folder-gradle-open",
+ "__gradle__": "folder-gradle-open",
+ "coverage": "folder-coverage-open",
+ ".coverage": "folder-coverage-open",
+ "_coverage": "folder-coverage-open",
+ "__coverage__": "folder-coverage-open",
+ "nyc-output": "folder-coverage-open",
+ ".nyc-output": "folder-coverage-open",
+ "_nyc-output": "folder-coverage-open",
+ "__nyc-output__": "folder-coverage-open",
+ "nyc_output": "folder-coverage-open",
+ ".nyc_output": "folder-coverage-open",
+ "_nyc_output": "folder-coverage-open",
+ "__nyc_output__": "folder-coverage-open",
+ "e2e": "folder-coverage-open",
+ ".e2e": "folder-coverage-open",
+ "_e2e": "folder-coverage-open",
+ "__e2e__": "folder-coverage-open",
+ "it": "folder-coverage-open",
+ ".it": "folder-coverage-open",
+ "_it": "folder-coverage-open",
+ "__it__": "folder-coverage-open",
+ "integration-test": "folder-coverage-open",
+ ".integration-test": "folder-coverage-open",
+ "_integration-test": "folder-coverage-open",
+ "__integration-test__": "folder-coverage-open",
+ "integration-tests": "folder-coverage-open",
+ ".integration-tests": "folder-coverage-open",
+ "_integration-tests": "folder-coverage-open",
+ "__integration-tests__": "folder-coverage-open",
+ "class": "folder-class-open",
+ ".class": "folder-class-open",
+ "_class": "folder-class-open",
+ "__class__": "folder-class-open",
+ "classes": "folder-class-open",
+ ".classes": "folder-class-open",
+ "_classes": "folder-class-open",
+ "__classes__": "folder-class-open",
+ "model": "folder-class-open",
+ ".model": "folder-class-open",
+ "_model": "folder-class-open",
+ "__model__": "folder-class-open",
+ "models": "folder-class-open",
+ ".models": "folder-class-open",
+ "_models": "folder-class-open",
+ "__models__": "folder-class-open",
+ "schemas": "folder-class-open",
+ ".schemas": "folder-class-open",
+ "_schemas": "folder-class-open",
+ "__schemas__": "folder-class-open",
+ "schema": "folder-class-open",
+ ".schema": "folder-class-open",
+ "_schema": "folder-class-open",
+ "__schema__": "folder-class-open",
+ "other": "folder-other-open",
+ ".other": "folder-other-open",
+ "_other": "folder-other-open",
+ "__other__": "folder-other-open",
+ "others": "folder-other-open",
+ ".others": "folder-other-open",
+ "_others": "folder-other-open",
+ "__others__": "folder-other-open",
+ "misc": "folder-other-open",
+ ".misc": "folder-other-open",
+ "_misc": "folder-other-open",
+ "__misc__": "folder-other-open",
+ "miscellaneous": "folder-other-open",
+ ".miscellaneous": "folder-other-open",
+ "_miscellaneous": "folder-other-open",
+ "__miscellaneous__": "folder-other-open",
+ "extra": "folder-other-open",
+ ".extra": "folder-other-open",
+ "_extra": "folder-other-open",
+ "__extra__": "folder-other-open",
+ "extras": "folder-other-open",
+ ".extras": "folder-other-open",
+ "_extras": "folder-other-open",
+ "__extras__": "folder-other-open",
+ "etc": "folder-other-open",
+ ".etc": "folder-other-open",
+ "_etc": "folder-other-open",
+ "__etc__": "folder-other-open",
+ "lua": "folder-lua-open",
+ ".lua": "folder-lua-open",
+ "_lua": "folder-lua-open",
+ "__lua__": "folder-lua-open",
+ "turbo": "folder-turborepo-open",
+ ".turbo": "folder-turborepo-open",
+ "_turbo": "folder-turborepo-open",
+ "__turbo__": "folder-turborepo-open",
+ "typescript": "folder-typescript-open",
+ ".typescript": "folder-typescript-open",
+ "_typescript": "folder-typescript-open",
+ "__typescript__": "folder-typescript-open",
+ "ts": "folder-typescript-open",
+ ".ts": "folder-typescript-open",
+ "_ts": "folder-typescript-open",
+ "__ts__": "folder-typescript-open",
+ "typings": "folder-typescript-open",
+ ".typings": "folder-typescript-open",
+ "_typings": "folder-typescript-open",
+ "__typings__": "folder-typescript-open",
+ "@types": "folder-typescript-open",
+ ".@types": "folder-typescript-open",
+ "_@types": "folder-typescript-open",
+ "__@types__": "folder-typescript-open",
+ "types": "folder-typescript-open",
+ ".types": "folder-typescript-open",
+ "_types": "folder-typescript-open",
+ "__types__": "folder-typescript-open",
+ "graphql": "folder-graphql-open",
+ ".graphql": "folder-graphql-open",
+ "_graphql": "folder-graphql-open",
+ "__graphql__": "folder-graphql-open",
+ "gql": "folder-graphql-open",
+ ".gql": "folder-graphql-open",
+ "_gql": "folder-graphql-open",
+ "__gql__": "folder-graphql-open",
+ "routes": "folder-routes-open",
+ ".routes": "folder-routes-open",
+ "_routes": "folder-routes-open",
+ "__routes__": "folder-routes-open",
+ "router": "folder-routes-open",
+ ".router": "folder-routes-open",
+ "_router": "folder-routes-open",
+ "__router__": "folder-routes-open",
+ "routers": "folder-routes-open",
+ ".routers": "folder-routes-open",
+ "_routers": "folder-routes-open",
+ "__routers__": "folder-routes-open",
+ "navigation": "folder-routes-open",
+ ".navigation": "folder-routes-open",
+ "_navigation": "folder-routes-open",
+ "__navigation__": "folder-routes-open",
+ "navigations": "folder-routes-open",
+ ".navigations": "folder-routes-open",
+ "_navigations": "folder-routes-open",
+ "__navigations__": "folder-routes-open",
+ "routing": "folder-routes-open",
+ ".routing": "folder-routes-open",
+ "_routing": "folder-routes-open",
+ "__routing__": "folder-routes-open",
+ "ci": "folder-ci-open",
+ ".ci": "folder-ci-open",
+ "_ci": "folder-ci-open",
+ "__ci__": "folder-ci-open",
+ "benchmark": "folder-benchmark-open",
+ ".benchmark": "folder-benchmark-open",
+ "_benchmark": "folder-benchmark-open",
+ "__benchmark__": "folder-benchmark-open",
+ "benchmarks": "folder-benchmark-open",
+ ".benchmarks": "folder-benchmark-open",
+ "_benchmarks": "folder-benchmark-open",
+ "__benchmarks__": "folder-benchmark-open",
+ "bench": "folder-benchmark-open",
+ ".bench": "folder-benchmark-open",
+ "_bench": "folder-benchmark-open",
+ "__bench__": "folder-benchmark-open",
+ "performance": "folder-benchmark-open",
+ ".performance": "folder-benchmark-open",
+ "_performance": "folder-benchmark-open",
+ "__performance__": "folder-benchmark-open",
+ "perf": "folder-benchmark-open",
+ ".perf": "folder-benchmark-open",
+ "_perf": "folder-benchmark-open",
+ "__perf__": "folder-benchmark-open",
+ "profiling": "folder-benchmark-open",
+ ".profiling": "folder-benchmark-open",
+ "_profiling": "folder-benchmark-open",
+ "__profiling__": "folder-benchmark-open",
+ "measure": "folder-benchmark-open",
+ ".measure": "folder-benchmark-open",
+ "_measure": "folder-benchmark-open",
+ "__measure__": "folder-benchmark-open",
+ "measures": "folder-benchmark-open",
+ ".measures": "folder-benchmark-open",
+ "_measures": "folder-benchmark-open",
+ "__measures__": "folder-benchmark-open",
+ "measurement": "folder-benchmark-open",
+ ".measurement": "folder-benchmark-open",
+ "_measurement": "folder-benchmark-open",
+ "__measurement__": "folder-benchmark-open",
+ "messages": "folder-messages-open",
+ ".messages": "folder-messages-open",
+ "_messages": "folder-messages-open",
+ "__messages__": "folder-messages-open",
+ "messaging": "folder-messages-open",
+ ".messaging": "folder-messages-open",
+ "_messaging": "folder-messages-open",
+ "__messaging__": "folder-messages-open",
+ "forum": "folder-messages-open",
+ ".forum": "folder-messages-open",
+ "_forum": "folder-messages-open",
+ "__forum__": "folder-messages-open",
+ "chat": "folder-messages-open",
+ ".chat": "folder-messages-open",
+ "_chat": "folder-messages-open",
+ "__chat__": "folder-messages-open",
+ "chats": "folder-messages-open",
+ ".chats": "folder-messages-open",
+ "_chats": "folder-messages-open",
+ "__chats__": "folder-messages-open",
+ "conversation": "folder-messages-open",
+ ".conversation": "folder-messages-open",
+ "_conversation": "folder-messages-open",
+ "__conversation__": "folder-messages-open",
+ "conversations": "folder-messages-open",
+ ".conversations": "folder-messages-open",
+ "_conversations": "folder-messages-open",
+ "__conversations__": "folder-messages-open",
+ "dialog": "folder-messages-open",
+ ".dialog": "folder-messages-open",
+ "_dialog": "folder-messages-open",
+ "__dialog__": "folder-messages-open",
+ "dialogs": "folder-messages-open",
+ ".dialogs": "folder-messages-open",
+ "_dialogs": "folder-messages-open",
+ "__dialogs__": "folder-messages-open",
+ "less": "folder-less-open",
+ ".less": "folder-less-open",
+ "_less": "folder-less-open",
+ "__less__": "folder-less-open",
+ "gulp": "folder-gulp-open",
+ ".gulp": "folder-gulp-open",
+ "_gulp": "folder-gulp-open",
+ "__gulp__": "folder-gulp-open",
+ "gulp-tasks": "folder-gulp-open",
+ ".gulp-tasks": "folder-gulp-open",
+ "_gulp-tasks": "folder-gulp-open",
+ "__gulp-tasks__": "folder-gulp-open",
+ "gulpfile.js": "folder-gulp-open",
+ ".gulpfile.js": "folder-gulp-open",
+ "_gulpfile.js": "folder-gulp-open",
+ "__gulpfile.js__": "folder-gulp-open",
+ "gulpfile.mjs": "folder-gulp-open",
+ ".gulpfile.mjs": "folder-gulp-open",
+ "_gulpfile.mjs": "folder-gulp-open",
+ "__gulpfile.mjs__": "folder-gulp-open",
+ "gulpfile.ts": "folder-gulp-open",
+ ".gulpfile.ts": "folder-gulp-open",
+ "_gulpfile.ts": "folder-gulp-open",
+ "__gulpfile.ts__": "folder-gulp-open",
+ "gulpfile.babel.js": "folder-gulp-open",
+ ".gulpfile.babel.js": "folder-gulp-open",
+ "_gulpfile.babel.js": "folder-gulp-open",
+ "__gulpfile.babel.js__": "folder-gulp-open",
+ "python": "folder-python-open",
+ ".python": "folder-python-open",
+ "_python": "folder-python-open",
+ "__python__": "folder-python-open",
+ "pycache": "folder-python-open",
+ ".pycache": "folder-python-open",
+ "_pycache": "folder-python-open",
+ "__pycache__": "folder-python-open",
+ "pytest_cache": "folder-python-open",
+ ".pytest_cache": "folder-python-open",
+ "_pytest_cache": "folder-python-open",
+ "__pytest_cache__": "folder-python-open",
+ "sandbox": "folder-sandbox-open",
+ ".sandbox": "folder-sandbox-open",
+ "_sandbox": "folder-sandbox-open",
+ "__sandbox__": "folder-sandbox-open",
+ "sandboxes": "folder-sandbox-open",
+ ".sandboxes": "folder-sandbox-open",
+ "_sandboxes": "folder-sandbox-open",
+ "__sandboxes__": "folder-sandbox-open",
+ "playground": "folder-sandbox-open",
+ ".playground": "folder-sandbox-open",
+ "_playground": "folder-sandbox-open",
+ "__playground__": "folder-sandbox-open",
+ "playgrounds": "folder-sandbox-open",
+ ".playgrounds": "folder-sandbox-open",
+ "_playgrounds": "folder-sandbox-open",
+ "__playgrounds__": "folder-sandbox-open",
+ "scons": "folder-scons-open",
+ ".scons": "folder-scons-open",
+ "_scons": "folder-scons-open",
+ "__scons__": "folder-scons-open",
+ "sconf_temp": "folder-scons-open",
+ ".sconf_temp": "folder-scons-open",
+ "_sconf_temp": "folder-scons-open",
+ "__sconf_temp__": "folder-scons-open",
+ "scons_cache": "folder-scons-open",
+ ".scons_cache": "folder-scons-open",
+ "_scons_cache": "folder-scons-open",
+ "__scons_cache__": "folder-scons-open",
+ "mojo": "folder-mojo-open",
+ ".mojo": "folder-mojo-open",
+ "_mojo": "folder-mojo-open",
+ "__mojo__": "folder-mojo-open",
+ "moon": "folder-moon-open",
+ ".moon": "folder-moon-open",
+ "_moon": "folder-moon-open",
+ "__moon__": "folder-moon-open",
+ "debug": "folder-debug-open",
+ ".debug": "folder-debug-open",
+ "_debug": "folder-debug-open",
+ "__debug__": "folder-debug-open",
+ "debugger": "folder-debug-open",
+ ".debugger": "folder-debug-open",
+ "_debugger": "folder-debug-open",
+ "__debugger__": "folder-debug-open",
+ "debugging": "folder-debug-open",
+ ".debugging": "folder-debug-open",
+ "_debugging": "folder-debug-open",
+ "__debugging__": "folder-debug-open",
+ "fastlane": "folder-fastlane-open",
+ ".fastlane": "folder-fastlane-open",
+ "_fastlane": "folder-fastlane-open",
+ "__fastlane__": "folder-fastlane-open",
+ "plugin": "folder-plugin-open",
+ ".plugin": "folder-plugin-open",
+ "_plugin": "folder-plugin-open",
+ "__plugin__": "folder-plugin-open",
+ "plugins": "folder-plugin-open",
+ ".plugins": "folder-plugin-open",
+ "_plugins": "folder-plugin-open",
+ "__plugins__": "folder-plugin-open",
+ "mod": "folder-plugin-open",
+ ".mod": "folder-plugin-open",
+ "_mod": "folder-plugin-open",
+ "__mod__": "folder-plugin-open",
+ "mods": "folder-plugin-open",
+ ".mods": "folder-plugin-open",
+ "_mods": "folder-plugin-open",
+ "__mods__": "folder-plugin-open",
+ "modding": "folder-plugin-open",
+ ".modding": "folder-plugin-open",
+ "_modding": "folder-plugin-open",
+ "__modding__": "folder-plugin-open",
+ "extension": "folder-plugin-open",
+ ".extension": "folder-plugin-open",
+ "_extension": "folder-plugin-open",
+ "__extension__": "folder-plugin-open",
+ "extensions": "folder-plugin-open",
+ ".extensions": "folder-plugin-open",
+ "_extensions": "folder-plugin-open",
+ "__extensions__": "folder-plugin-open",
+ "addon": "folder-plugin-open",
+ ".addon": "folder-plugin-open",
+ "_addon": "folder-plugin-open",
+ "__addon__": "folder-plugin-open",
+ "addons": "folder-plugin-open",
+ ".addons": "folder-plugin-open",
+ "_addons": "folder-plugin-open",
+ "__addons__": "folder-plugin-open",
+ "addin": "folder-plugin-open",
+ ".addin": "folder-plugin-open",
+ "_addin": "folder-plugin-open",
+ "__addin__": "folder-plugin-open",
+ "addins": "folder-plugin-open",
+ ".addins": "folder-plugin-open",
+ "_addins": "folder-plugin-open",
+ "__addins__": "folder-plugin-open",
+ "module": "folder-plugin-open",
+ ".module": "folder-plugin-open",
+ "_module": "folder-plugin-open",
+ "__module__": "folder-plugin-open",
+ "modules": "folder-plugin-open",
+ ".modules": "folder-plugin-open",
+ "_modules": "folder-plugin-open",
+ "__modules__": "folder-plugin-open",
+ "middleware": "folder-middleware-open",
+ ".middleware": "folder-middleware-open",
+ "_middleware": "folder-middleware-open",
+ "__middleware__": "folder-middleware-open",
+ "middlewares": "folder-middleware-open",
+ ".middlewares": "folder-middleware-open",
+ "_middlewares": "folder-middleware-open",
+ "__middlewares__": "folder-middleware-open",
+ "controller": "folder-controller-open",
+ ".controller": "folder-controller-open",
+ "_controller": "folder-controller-open",
+ "__controller__": "folder-controller-open",
+ "controllers": "folder-controller-open",
+ ".controllers": "folder-controller-open",
+ "_controllers": "folder-controller-open",
+ "__controllers__": "folder-controller-open",
+ "controls": "folder-controller-open",
+ ".controls": "folder-controller-open",
+ "_controls": "folder-controller-open",
+ "__controls__": "folder-controller-open",
+ "service": "folder-controller-open",
+ ".service": "folder-controller-open",
+ "_service": "folder-controller-open",
+ "__service__": "folder-controller-open",
+ "services": "folder-controller-open",
+ ".services": "folder-controller-open",
+ "_services": "folder-controller-open",
+ "__services__": "folder-controller-open",
+ "provider": "folder-controller-open",
+ ".provider": "folder-controller-open",
+ "_provider": "folder-controller-open",
+ "__provider__": "folder-controller-open",
+ "providers": "folder-controller-open",
+ ".providers": "folder-controller-open",
+ "_providers": "folder-controller-open",
+ "__providers__": "folder-controller-open",
+ "handler": "folder-controller-open",
+ ".handler": "folder-controller-open",
+ "_handler": "folder-controller-open",
+ "__handler__": "folder-controller-open",
+ "handlers": "folder-controller-open",
+ ".handlers": "folder-controller-open",
+ "_handlers": "folder-controller-open",
+ "__handlers__": "folder-controller-open",
+ "ansible": "folder-ansible-open",
+ ".ansible": "folder-ansible-open",
+ "_ansible": "folder-ansible-open",
+ "__ansible__": "folder-ansible-open",
+ "server": "folder-server-open",
+ ".server": "folder-server-open",
+ "_server": "folder-server-open",
+ "__server__": "folder-server-open",
+ "servers": "folder-server-open",
+ ".servers": "folder-server-open",
+ "_servers": "folder-server-open",
+ "__servers__": "folder-server-open",
+ "backend": "folder-server-open",
+ ".backend": "folder-server-open",
+ "_backend": "folder-server-open",
+ "__backend__": "folder-server-open",
+ "backends": "folder-server-open",
+ ".backends": "folder-server-open",
+ "_backends": "folder-server-open",
+ "__backends__": "folder-server-open",
+ "client": "folder-client-open",
+ ".client": "folder-client-open",
+ "_client": "folder-client-open",
+ "__client__": "folder-client-open",
+ "clients": "folder-client-open",
+ ".clients": "folder-client-open",
+ "_clients": "folder-client-open",
+ "__clients__": "folder-client-open",
+ "frontend": "folder-client-open",
+ ".frontend": "folder-client-open",
+ "_frontend": "folder-client-open",
+ "__frontend__": "folder-client-open",
+ "frontends": "folder-client-open",
+ ".frontends": "folder-client-open",
+ "_frontends": "folder-client-open",
+ "__frontends__": "folder-client-open",
+ "pwa": "folder-client-open",
+ ".pwa": "folder-client-open",
+ "_pwa": "folder-client-open",
+ "__pwa__": "folder-client-open",
+ "spa": "folder-client-open",
+ ".spa": "folder-client-open",
+ "_spa": "folder-client-open",
+ "__spa__": "folder-client-open",
+ "tasks": "folder-tasks-open",
+ ".tasks": "folder-tasks-open",
+ "_tasks": "folder-tasks-open",
+ "__tasks__": "folder-tasks-open",
+ "tickets": "folder-tasks-open",
+ ".tickets": "folder-tasks-open",
+ "_tickets": "folder-tasks-open",
+ "__tickets__": "folder-tasks-open",
+ "android": "folder-android-open",
+ ".android": "folder-android-open",
+ "_android": "folder-android-open",
+ "__android__": "folder-android-open",
+ "ios": "folder-ios-open",
+ ".ios": "folder-ios-open",
+ "_ios": "folder-ios-open",
+ "__ios__": "folder-ios-open",
+ "presentation": "folder-ui-open",
+ ".presentation": "folder-ui-open",
+ "_presentation": "folder-ui-open",
+ "__presentation__": "folder-ui-open",
+ "gui": "folder-ui-open",
+ ".gui": "folder-ui-open",
+ "_gui": "folder-ui-open",
+ "__gui__": "folder-ui-open",
+ "ui": "folder-ui-open",
+ ".ui": "folder-ui-open",
+ "_ui": "folder-ui-open",
+ "__ui__": "folder-ui-open",
+ "ux": "folder-ui-open",
+ ".ux": "folder-ui-open",
+ "_ux": "folder-ui-open",
+ "__ux__": "folder-ui-open",
+ "uploads": "folder-upload-open",
+ ".uploads": "folder-upload-open",
+ "_uploads": "folder-upload-open",
+ "__uploads__": "folder-upload-open",
+ "upload": "folder-upload-open",
+ ".upload": "folder-upload-open",
+ "_upload": "folder-upload-open",
+ "__upload__": "folder-upload-open",
+ "downloads": "folder-download-open",
+ ".downloads": "folder-download-open",
+ "_downloads": "folder-download-open",
+ "__downloads__": "folder-download-open",
+ "download": "folder-download-open",
+ ".download": "folder-download-open",
+ "_download": "folder-download-open",
+ "__download__": "folder-download-open",
+ "downloader": "folder-download-open",
+ ".downloader": "folder-download-open",
+ "_downloader": "folder-download-open",
+ "__downloader__": "folder-download-open",
+ "downloaders": "folder-download-open",
+ ".downloaders": "folder-download-open",
+ "_downloaders": "folder-download-open",
+ "__downloaders__": "folder-download-open",
+ "tools": "folder-tools-open",
+ ".tools": "folder-tools-open",
+ "_tools": "folder-tools-open",
+ "__tools__": "folder-tools-open",
+ "toolkit": "folder-tools-open",
+ ".toolkit": "folder-tools-open",
+ "_toolkit": "folder-tools-open",
+ "__toolkit__": "folder-tools-open",
+ "toolkits": "folder-tools-open",
+ ".toolkits": "folder-tools-open",
+ "_toolkits": "folder-tools-open",
+ "__toolkits__": "folder-tools-open",
+ "toolbox": "folder-tools-open",
+ ".toolbox": "folder-tools-open",
+ "_toolbox": "folder-tools-open",
+ "__toolbox__": "folder-tools-open",
+ "toolboxes": "folder-tools-open",
+ ".toolboxes": "folder-tools-open",
+ "_toolboxes": "folder-tools-open",
+ "__toolboxes__": "folder-tools-open",
+ "tooling": "folder-tools-open",
+ ".tooling": "folder-tools-open",
+ "_tooling": "folder-tools-open",
+ "__tooling__": "folder-tools-open",
+ "devtools": "folder-tools-open",
+ ".devtools": "folder-tools-open",
+ "_devtools": "folder-tools-open",
+ "__devtools__": "folder-tools-open",
+ "kit": "folder-tools-open",
+ ".kit": "folder-tools-open",
+ "_kit": "folder-tools-open",
+ "__kit__": "folder-tools-open",
+ "kits": "folder-tools-open",
+ ".kits": "folder-tools-open",
+ "_kits": "folder-tools-open",
+ "__kits__": "folder-tools-open",
+ "helpers": "folder-helper-open",
+ ".helpers": "folder-helper-open",
+ "_helpers": "folder-helper-open",
+ "__helpers__": "folder-helper-open",
+ "helper": "folder-helper-open",
+ ".helper": "folder-helper-open",
+ "_helper": "folder-helper-open",
+ "__helper__": "folder-helper-open",
+ "serverless": "folder-serverless-open",
+ ".serverless": "folder-serverless-open",
+ "_serverless": "folder-serverless-open",
+ "__serverless__": "folder-serverless-open",
+ "api": "folder-api-open",
+ ".api": "folder-api-open",
+ "_api": "folder-api-open",
+ "__api__": "folder-api-open",
+ "apis": "folder-api-open",
+ ".apis": "folder-api-open",
+ "_apis": "folder-api-open",
+ "__apis__": "folder-api-open",
+ "restapi": "folder-api-open",
+ ".restapi": "folder-api-open",
+ "_restapi": "folder-api-open",
+ "__restapi__": "folder-api-open",
+ "app": "folder-app-open",
+ ".app": "folder-app-open",
+ "_app": "folder-app-open",
+ "__app__": "folder-app-open",
+ "apps": "folder-app-open",
+ ".apps": "folder-app-open",
+ "_apps": "folder-app-open",
+ "__apps__": "folder-app-open",
+ "application": "folder-app-open",
+ ".application": "folder-app-open",
+ "_application": "folder-app-open",
+ "__application__": "folder-app-open",
+ "applications": "folder-app-open",
+ ".applications": "folder-app-open",
+ "_applications": "folder-app-open",
+ "__applications__": "folder-app-open",
+ "apollo": "folder-apollo-open",
+ ".apollo": "folder-apollo-open",
+ "_apollo": "folder-apollo-open",
+ "__apollo__": "folder-apollo-open",
+ "apollo-client": "folder-apollo-open",
+ ".apollo-client": "folder-apollo-open",
+ "_apollo-client": "folder-apollo-open",
+ "__apollo-client__": "folder-apollo-open",
+ "apollo-cache": "folder-apollo-open",
+ ".apollo-cache": "folder-apollo-open",
+ "_apollo-cache": "folder-apollo-open",
+ "__apollo-cache__": "folder-apollo-open",
+ "apollo-config": "folder-apollo-open",
+ ".apollo-config": "folder-apollo-open",
+ "_apollo-config": "folder-apollo-open",
+ "__apollo-config__": "folder-apollo-open",
+ "arc": "folder-archive-open",
+ ".arc": "folder-archive-open",
+ "_arc": "folder-archive-open",
+ "__arc__": "folder-archive-open",
+ "arcs": "folder-archive-open",
+ ".arcs": "folder-archive-open",
+ "_arcs": "folder-archive-open",
+ "__arcs__": "folder-archive-open",
+ "archive": "folder-archive-open",
+ ".archive": "folder-archive-open",
+ "_archive": "folder-archive-open",
+ "__archive__": "folder-archive-open",
+ "archives": "folder-archive-open",
+ ".archives": "folder-archive-open",
+ "_archives": "folder-archive-open",
+ "__archives__": "folder-archive-open",
+ "archival": "folder-archive-open",
+ ".archival": "folder-archive-open",
+ "_archival": "folder-archive-open",
+ "__archival__": "folder-archive-open",
+ "bkp": "folder-backup-open",
+ ".bkp": "folder-backup-open",
+ "_bkp": "folder-backup-open",
+ "__bkp__": "folder-backup-open",
+ "bkps": "folder-backup-open",
+ ".bkps": "folder-backup-open",
+ "_bkps": "folder-backup-open",
+ "__bkps__": "folder-backup-open",
+ "bak": "folder-backup-open",
+ ".bak": "folder-backup-open",
+ "_bak": "folder-backup-open",
+ "__bak__": "folder-backup-open",
+ "baks": "folder-backup-open",
+ ".baks": "folder-backup-open",
+ "_baks": "folder-backup-open",
+ "__baks__": "folder-backup-open",
+ "backup": "folder-backup-open",
+ ".backup": "folder-backup-open",
+ "_backup": "folder-backup-open",
+ "__backup__": "folder-backup-open",
+ "backups": "folder-backup-open",
+ ".backups": "folder-backup-open",
+ "_backups": "folder-backup-open",
+ "__backups__": "folder-backup-open",
+ "back-up": "folder-backup-open",
+ ".back-up": "folder-backup-open",
+ "_back-up": "folder-backup-open",
+ "__back-up__": "folder-backup-open",
+ "back-ups": "folder-backup-open",
+ ".back-ups": "folder-backup-open",
+ "_back-ups": "folder-backup-open",
+ "__back-ups__": "folder-backup-open",
+ "history": "folder-backup-open",
+ ".history": "folder-backup-open",
+ "_history": "folder-backup-open",
+ "__history__": "folder-backup-open",
+ "histories": "folder-backup-open",
+ ".histories": "folder-backup-open",
+ "_histories": "folder-backup-open",
+ "__histories__": "folder-backup-open",
+ "batch": "folder-batch-open",
+ ".batch": "folder-batch-open",
+ "_batch": "folder-batch-open",
+ "__batch__": "folder-batch-open",
+ "batchs": "folder-batch-open",
+ ".batchs": "folder-batch-open",
+ "_batchs": "folder-batch-open",
+ "__batchs__": "folder-batch-open",
+ "batches": "folder-batch-open",
+ ".batches": "folder-batch-open",
+ "_batches": "folder-batch-open",
+ "__batches__": "folder-batch-open",
+ "buildkite": "folder-buildkite-open",
+ ".buildkite": "folder-buildkite-open",
+ "_buildkite": "folder-buildkite-open",
+ "__buildkite__": "folder-buildkite-open",
+ "cluster": "folder-cluster-open",
+ ".cluster": "folder-cluster-open",
+ "_cluster": "folder-cluster-open",
+ "__cluster__": "folder-cluster-open",
+ "clusters": "folder-cluster-open",
+ ".clusters": "folder-cluster-open",
+ "_clusters": "folder-cluster-open",
+ "__clusters__": "folder-cluster-open",
+ "command": "folder-command-open",
+ ".command": "folder-command-open",
+ "_command": "folder-command-open",
+ "__command__": "folder-command-open",
+ "commands": "folder-command-open",
+ ".commands": "folder-command-open",
+ "_commands": "folder-command-open",
+ "__commands__": "folder-command-open",
+ "commandline": "folder-command-open",
+ ".commandline": "folder-command-open",
+ "_commandline": "folder-command-open",
+ "__commandline__": "folder-command-open",
+ "cmd": "folder-command-open",
+ ".cmd": "folder-command-open",
+ "_cmd": "folder-command-open",
+ "__cmd__": "folder-command-open",
+ "cli": "folder-command-open",
+ ".cli": "folder-command-open",
+ "_cli": "folder-command-open",
+ "__cli__": "folder-command-open",
+ "clis": "folder-command-open",
+ ".clis": "folder-command-open",
+ "_clis": "folder-command-open",
+ "__clis__": "folder-command-open",
+ "constant": "folder-constant-open",
+ ".constant": "folder-constant-open",
+ "_constant": "folder-constant-open",
+ "__constant__": "folder-constant-open",
+ "constants": "folder-constant-open",
+ ".constants": "folder-constant-open",
+ "_constants": "folder-constant-open",
+ "__constants__": "folder-constant-open",
+ "const": "folder-constant-open",
+ ".const": "folder-constant-open",
+ "_const": "folder-constant-open",
+ "__const__": "folder-constant-open",
+ "consts": "folder-constant-open",
+ ".consts": "folder-constant-open",
+ "_consts": "folder-constant-open",
+ "__consts__": "folder-constant-open",
+ "container": "folder-container-open",
+ ".container": "folder-container-open",
+ "_container": "folder-container-open",
+ "__container__": "folder-container-open",
+ "containers": "folder-container-open",
+ ".containers": "folder-container-open",
+ "_containers": "folder-container-open",
+ "__containers__": "folder-container-open",
+ "devcontainer": "folder-container-open",
+ ".devcontainer": "folder-container-open",
+ "_devcontainer": "folder-container-open",
+ "__devcontainer__": "folder-container-open",
+ "content": "folder-content-open",
+ ".content": "folder-content-open",
+ "_content": "folder-content-open",
+ "__content__": "folder-content-open",
+ "contents": "folder-content-open",
+ ".contents": "folder-content-open",
+ "_contents": "folder-content-open",
+ "__contents__": "folder-content-open",
+ "context": "folder-context-open",
+ ".context": "folder-context-open",
+ "_context": "folder-context-open",
+ "__context__": "folder-context-open",
+ "contexts": "folder-context-open",
+ ".contexts": "folder-context-open",
+ "_contexts": "folder-context-open",
+ "__contexts__": "folder-context-open",
+ "core": "folder-core-open",
+ ".core": "folder-core-open",
+ "_core": "folder-core-open",
+ "__core__": "folder-core-open",
+ "delta": "folder-delta-open",
+ ".delta": "folder-delta-open",
+ "_delta": "folder-delta-open",
+ "__delta__": "folder-delta-open",
+ "deltas": "folder-delta-open",
+ ".deltas": "folder-delta-open",
+ "_deltas": "folder-delta-open",
+ "__deltas__": "folder-delta-open",
+ "changes": "folder-delta-open",
+ ".changes": "folder-delta-open",
+ "_changes": "folder-delta-open",
+ "__changes__": "folder-delta-open",
+ "dump": "folder-dump-open",
+ ".dump": "folder-dump-open",
+ "_dump": "folder-dump-open",
+ "__dump__": "folder-dump-open",
+ "dumps": "folder-dump-open",
+ ".dumps": "folder-dump-open",
+ "_dumps": "folder-dump-open",
+ "__dumps__": "folder-dump-open",
+ "demo": "folder-examples-open",
+ ".demo": "folder-examples-open",
+ "_demo": "folder-examples-open",
+ "__demo__": "folder-examples-open",
+ "demos": "folder-examples-open",
+ ".demos": "folder-examples-open",
+ "_demos": "folder-examples-open",
+ "__demos__": "folder-examples-open",
+ "example": "folder-examples-open",
+ ".example": "folder-examples-open",
+ "_example": "folder-examples-open",
+ "__example__": "folder-examples-open",
+ "examples": "folder-examples-open",
+ ".examples": "folder-examples-open",
+ "_examples": "folder-examples-open",
+ "__examples__": "folder-examples-open",
+ "sample": "folder-examples-open",
+ ".sample": "folder-examples-open",
+ "_sample": "folder-examples-open",
+ "__sample__": "folder-examples-open",
+ "samples": "folder-examples-open",
+ ".samples": "folder-examples-open",
+ "_samples": "folder-examples-open",
+ "__samples__": "folder-examples-open",
+ "sample-data": "folder-examples-open",
+ ".sample-data": "folder-examples-open",
+ "_sample-data": "folder-examples-open",
+ "__sample-data__": "folder-examples-open",
+ "env": "folder-environment-open",
+ ".env": "folder-environment-open",
+ "_env": "folder-environment-open",
+ "__env__": "folder-environment-open",
+ "envs": "folder-environment-open",
+ ".envs": "folder-environment-open",
+ "_envs": "folder-environment-open",
+ "__envs__": "folder-environment-open",
+ "environment": "folder-environment-open",
+ ".environment": "folder-environment-open",
+ "_environment": "folder-environment-open",
+ "__environment__": "folder-environment-open",
+ "environments": "folder-environment-open",
+ ".environments": "folder-environment-open",
+ "_environments": "folder-environment-open",
+ "__environments__": "folder-environment-open",
+ "venv": "folder-environment-open",
+ ".venv": "folder-environment-open",
+ "_venv": "folder-environment-open",
+ "__venv__": "folder-environment-open",
+ "func": "folder-functions-open",
+ ".func": "folder-functions-open",
+ "_func": "folder-functions-open",
+ "__func__": "folder-functions-open",
+ "funcs": "folder-functions-open",
+ ".funcs": "folder-functions-open",
+ "_funcs": "folder-functions-open",
+ "__funcs__": "folder-functions-open",
+ "function": "folder-functions-open",
+ ".function": "folder-functions-open",
+ "_function": "folder-functions-open",
+ "__function__": "folder-functions-open",
+ "functions": "folder-functions-open",
+ ".functions": "folder-functions-open",
+ "_functions": "folder-functions-open",
+ "__functions__": "folder-functions-open",
+ "lambda": "folder-functions-open",
+ ".lambda": "folder-functions-open",
+ "_lambda": "folder-functions-open",
+ "__lambda__": "folder-functions-open",
+ "lambdas": "folder-functions-open",
+ ".lambdas": "folder-functions-open",
+ "_lambdas": "folder-functions-open",
+ "__lambdas__": "folder-functions-open",
+ "logic": "folder-functions-open",
+ ".logic": "folder-functions-open",
+ "_logic": "folder-functions-open",
+ "__logic__": "folder-functions-open",
+ "math": "folder-functions-open",
+ ".math": "folder-functions-open",
+ "_math": "folder-functions-open",
+ "__math__": "folder-functions-open",
+ "maths": "folder-functions-open",
+ ".maths": "folder-functions-open",
+ "_maths": "folder-functions-open",
+ "__maths__": "folder-functions-open",
+ "calc": "folder-functions-open",
+ ".calc": "folder-functions-open",
+ "_calc": "folder-functions-open",
+ "__calc__": "folder-functions-open",
+ "calcs": "folder-functions-open",
+ ".calcs": "folder-functions-open",
+ "_calcs": "folder-functions-open",
+ "__calcs__": "folder-functions-open",
+ "calculation": "folder-functions-open",
+ ".calculation": "folder-functions-open",
+ "_calculation": "folder-functions-open",
+ "__calculation__": "folder-functions-open",
+ "calculations": "folder-functions-open",
+ ".calculations": "folder-functions-open",
+ "_calculations": "folder-functions-open",
+ "__calculations__": "folder-functions-open",
+ "generator": "folder-generator-open",
+ ".generator": "folder-generator-open",
+ "_generator": "folder-generator-open",
+ "__generator__": "folder-generator-open",
+ "generators": "folder-generator-open",
+ ".generators": "folder-generator-open",
+ "_generators": "folder-generator-open",
+ "__generators__": "folder-generator-open",
+ "generated": "folder-generator-open",
+ ".generated": "folder-generator-open",
+ "_generated": "folder-generator-open",
+ "__generated__": "folder-generator-open",
+ "cfn-gen": "folder-generator-open",
+ ".cfn-gen": "folder-generator-open",
+ "_cfn-gen": "folder-generator-open",
+ "__cfn-gen__": "folder-generator-open",
+ "gen": "folder-generator-open",
+ ".gen": "folder-generator-open",
+ "_gen": "folder-generator-open",
+ "__gen__": "folder-generator-open",
+ "gens": "folder-generator-open",
+ ".gens": "folder-generator-open",
+ "_gens": "folder-generator-open",
+ "__gens__": "folder-generator-open",
+ "auto": "folder-generator-open",
+ ".auto": "folder-generator-open",
+ "_auto": "folder-generator-open",
+ "__auto__": "folder-generator-open",
+ "hook": "folder-hook-open",
+ ".hook": "folder-hook-open",
+ "_hook": "folder-hook-open",
+ "__hook__": "folder-hook-open",
+ "hooks": "folder-hook-open",
+ ".hooks": "folder-hook-open",
+ "_hooks": "folder-hook-open",
+ "__hooks__": "folder-hook-open",
+ "trigger": "folder-trigger-open",
+ ".trigger": "folder-trigger-open",
+ "_trigger": "folder-trigger-open",
+ "__trigger__": "folder-trigger-open",
+ "triggers": "folder-trigger-open",
+ ".triggers": "folder-trigger-open",
+ "_triggers": "folder-trigger-open",
+ "__triggers__": "folder-trigger-open",
+ "job": "folder-job-open",
+ ".job": "folder-job-open",
+ "_job": "folder-job-open",
+ "__job__": "folder-job-open",
+ "jobs": "folder-job-open",
+ ".jobs": "folder-job-open",
+ "_jobs": "folder-job-open",
+ "__jobs__": "folder-job-open",
+ "key": "folder-keys-open",
+ ".key": "folder-keys-open",
+ "_key": "folder-keys-open",
+ "__key__": "folder-keys-open",
+ "keys": "folder-keys-open",
+ ".keys": "folder-keys-open",
+ "_keys": "folder-keys-open",
+ "__keys__": "folder-keys-open",
+ "token": "folder-keys-open",
+ ".token": "folder-keys-open",
+ "_token": "folder-keys-open",
+ "__token__": "folder-keys-open",
+ "tokens": "folder-keys-open",
+ ".tokens": "folder-keys-open",
+ "_tokens": "folder-keys-open",
+ "__tokens__": "folder-keys-open",
+ "jwt": "folder-keys-open",
+ ".jwt": "folder-keys-open",
+ "_jwt": "folder-keys-open",
+ "__jwt__": "folder-keys-open",
+ "secret": "folder-keys-open",
+ ".secret": "folder-keys-open",
+ "_secret": "folder-keys-open",
+ "__secret__": "folder-keys-open",
+ "secrets": "folder-keys-open",
+ ".secrets": "folder-keys-open",
+ "_secrets": "folder-keys-open",
+ "__secrets__": "folder-keys-open",
+ "layout": "folder-layout-open",
+ ".layout": "folder-layout-open",
+ "_layout": "folder-layout-open",
+ "__layout__": "folder-layout-open",
+ "layouts": "folder-layout-open",
+ ".layouts": "folder-layout-open",
+ "_layouts": "folder-layout-open",
+ "__layouts__": "folder-layout-open",
+ "mail": "folder-mail-open",
+ ".mail": "folder-mail-open",
+ "_mail": "folder-mail-open",
+ "__mail__": "folder-mail-open",
+ "mails": "folder-mail-open",
+ ".mails": "folder-mail-open",
+ "_mails": "folder-mail-open",
+ "__mails__": "folder-mail-open",
+ "email": "folder-mail-open",
+ ".email": "folder-mail-open",
+ "_email": "folder-mail-open",
+ "__email__": "folder-mail-open",
+ "emails": "folder-mail-open",
+ ".emails": "folder-mail-open",
+ "_emails": "folder-mail-open",
+ "__emails__": "folder-mail-open",
+ "smtp": "folder-mail-open",
+ ".smtp": "folder-mail-open",
+ "_smtp": "folder-mail-open",
+ "__smtp__": "folder-mail-open",
+ "mailers": "folder-mail-open",
+ ".mailers": "folder-mail-open",
+ "_mailers": "folder-mail-open",
+ "__mailers__": "folder-mail-open",
+ "mappings": "folder-mappings-open",
+ ".mappings": "folder-mappings-open",
+ "_mappings": "folder-mappings-open",
+ "__mappings__": "folder-mappings-open",
+ "mapping": "folder-mappings-open",
+ ".mapping": "folder-mappings-open",
+ "_mapping": "folder-mappings-open",
+ "__mapping__": "folder-mappings-open",
+ "meta": "folder-meta-open",
+ ".meta": "folder-meta-open",
+ "_meta": "folder-meta-open",
+ "__meta__": "folder-meta-open",
+ "changesets": "folder-changesets-open",
+ ".changesets": "folder-changesets-open",
+ "_changesets": "folder-changesets-open",
+ "__changesets__": "folder-changesets-open",
+ "changeset": "folder-changesets-open",
+ ".changeset": "folder-changesets-open",
+ "_changeset": "folder-changesets-open",
+ "__changeset__": "folder-changesets-open",
+ "package": "folder-packages-open",
+ ".package": "folder-packages-open",
+ "_package": "folder-packages-open",
+ "__package__": "folder-packages-open",
+ "packages": "folder-packages-open",
+ ".packages": "folder-packages-open",
+ "_packages": "folder-packages-open",
+ "__packages__": "folder-packages-open",
+ "pkg": "folder-packages-open",
+ ".pkg": "folder-packages-open",
+ "_pkg": "folder-packages-open",
+ "__pkg__": "folder-packages-open",
+ "pkgs": "folder-packages-open",
+ ".pkgs": "folder-packages-open",
+ "_pkgs": "folder-packages-open",
+ "__pkgs__": "folder-packages-open",
+ "serverpackages": "folder-packages-open",
+ ".serverpackages": "folder-packages-open",
+ "_serverpackages": "folder-packages-open",
+ "__serverpackages__": "folder-packages-open",
+ "devpackages": "folder-packages-open",
+ ".devpackages": "folder-packages-open",
+ "_devpackages": "folder-packages-open",
+ "__devpackages__": "folder-packages-open",
+ "dependencies": "folder-packages-open",
+ ".dependencies": "folder-packages-open",
+ "_dependencies": "folder-packages-open",
+ "__dependencies__": "folder-packages-open",
+ "shared": "folder-shared-open",
+ ".shared": "folder-shared-open",
+ "_shared": "folder-shared-open",
+ "__shared__": "folder-shared-open",
+ "common": "folder-shared-open",
+ ".common": "folder-shared-open",
+ "_common": "folder-shared-open",
+ "__common__": "folder-shared-open",
+ "glsl": "folder-shader-open",
+ ".glsl": "folder-shader-open",
+ "_glsl": "folder-shader-open",
+ "__glsl__": "folder-shader-open",
+ "hlsl": "folder-shader-open",
+ ".hlsl": "folder-shader-open",
+ "_hlsl": "folder-shader-open",
+ "__hlsl__": "folder-shader-open",
+ "shader": "folder-shader-open",
+ ".shader": "folder-shader-open",
+ "_shader": "folder-shader-open",
+ "__shader__": "folder-shader-open",
+ "shaders": "folder-shader-open",
+ ".shaders": "folder-shader-open",
+ "_shaders": "folder-shader-open",
+ "__shaders__": "folder-shader-open",
+ "stack": "folder-stack-open",
+ ".stack": "folder-stack-open",
+ "_stack": "folder-stack-open",
+ "__stack__": "folder-stack-open",
+ "stacks": "folder-stack-open",
+ ".stacks": "folder-stack-open",
+ "_stacks": "folder-stack-open",
+ "__stacks__": "folder-stack-open",
+ "template": "folder-template-open",
+ ".template": "folder-template-open",
+ "_template": "folder-template-open",
+ "__template__": "folder-template-open",
+ "templates": "folder-template-open",
+ ".templates": "folder-template-open",
+ "_templates": "folder-template-open",
+ "__templates__": "folder-template-open",
+ "github/ISSUE_TEMPLATE": "folder-template-open",
+ ".github/ISSUE_TEMPLATE": "folder-template-open",
+ "_github/ISSUE_TEMPLATE": "folder-template-open",
+ "__github/ISSUE_TEMPLATE__": "folder-template-open",
+ "github/PULL_REQUEST_TEMPLATE": "folder-template-open",
+ ".github/PULL_REQUEST_TEMPLATE": "folder-template-open",
+ "_github/PULL_REQUEST_TEMPLATE": "folder-template-open",
+ "__github/PULL_REQUEST_TEMPLATE__": "folder-template-open",
+ "util": "folder-utils-open",
+ ".util": "folder-utils-open",
+ "_util": "folder-utils-open",
+ "__util__": "folder-utils-open",
+ "utils": "folder-utils-open",
+ ".utils": "folder-utils-open",
+ "_utils": "folder-utils-open",
+ "__utils__": "folder-utils-open",
+ "utility": "folder-utils-open",
+ ".utility": "folder-utils-open",
+ "_utility": "folder-utils-open",
+ "__utility__": "folder-utils-open",
+ "utilities": "folder-utils-open",
+ ".utilities": "folder-utils-open",
+ "_utilities": "folder-utils-open",
+ "__utilities__": "folder-utils-open",
+ "supabase": "folder-supabase-open",
+ ".supabase": "folder-supabase-open",
+ "_supabase": "folder-supabase-open",
+ "__supabase__": "folder-supabase-open",
+ "private": "folder-private-open",
+ ".private": "folder-private-open",
+ "_private": "folder-private-open",
+ "__private__": "folder-private-open",
+ "linux": "folder-linux-open",
+ ".linux": "folder-linux-open",
+ "_linux": "folder-linux-open",
+ "__linux__": "folder-linux-open",
+ "linuxbsd": "folder-linux-open",
+ ".linuxbsd": "folder-linux-open",
+ "_linuxbsd": "folder-linux-open",
+ "__linuxbsd__": "folder-linux-open",
+ "unix": "folder-linux-open",
+ ".unix": "folder-linux-open",
+ "_unix": "folder-linux-open",
+ "__unix__": "folder-linux-open",
+ "windows": "folder-windows-open",
+ ".windows": "folder-windows-open",
+ "_windows": "folder-windows-open",
+ "__windows__": "folder-windows-open",
+ "win": "folder-windows-open",
+ ".win": "folder-windows-open",
+ "_win": "folder-windows-open",
+ "__win__": "folder-windows-open",
+ "win32": "folder-windows-open",
+ ".win32": "folder-windows-open",
+ "_win32": "folder-windows-open",
+ "__win32__": "folder-windows-open",
+ "macos": "folder-macos-open",
+ ".macos": "folder-macos-open",
+ "_macos": "folder-macos-open",
+ "__macos__": "folder-macos-open",
+ "mac": "folder-macos-open",
+ ".mac": "folder-macos-open",
+ "_mac": "folder-macos-open",
+ "__mac__": "folder-macos-open",
+ "osx": "folder-macos-open",
+ ".osx": "folder-macos-open",
+ "_osx": "folder-macos-open",
+ "__osx__": "folder-macos-open",
+ "DS_Store": "folder-macos-open",
+ ".DS_Store": "folder-macos-open",
+ "_DS_Store": "folder-macos-open",
+ "__DS_Store__": "folder-macos-open",
+ "iPhone": "folder-macos-open",
+ ".iPhone": "folder-macos-open",
+ "_iPhone": "folder-macos-open",
+ "__iPhone__": "folder-macos-open",
+ "iPad": "folder-macos-open",
+ ".iPad": "folder-macos-open",
+ "_iPad": "folder-macos-open",
+ "__iPad__": "folder-macos-open",
+ "iPod": "folder-macos-open",
+ ".iPod": "folder-macos-open",
+ "_iPod": "folder-macos-open",
+ "__iPod__": "folder-macos-open",
+ "macbook": "folder-macos-open",
+ ".macbook": "folder-macos-open",
+ "_macbook": "folder-macos-open",
+ "__macbook__": "folder-macos-open",
+ "macbook-air": "folder-macos-open",
+ ".macbook-air": "folder-macos-open",
+ "_macbook-air": "folder-macos-open",
+ "__macbook-air__": "folder-macos-open",
+ "macosx": "folder-macos-open",
+ ".macosx": "folder-macos-open",
+ "_macosx": "folder-macos-open",
+ "__macosx__": "folder-macos-open",
+ "apple": "folder-macos-open",
+ ".apple": "folder-macos-open",
+ "_apple": "folder-macos-open",
+ "__apple__": "folder-macos-open",
+ "error": "folder-error-open",
+ ".error": "folder-error-open",
+ "_error": "folder-error-open",
+ "__error__": "folder-error-open",
+ "errors": "folder-error-open",
+ ".errors": "folder-error-open",
+ "_errors": "folder-error-open",
+ "__errors__": "folder-error-open",
+ "err": "folder-error-open",
+ ".err": "folder-error-open",
+ "_err": "folder-error-open",
+ "__err__": "folder-error-open",
+ "errs": "folder-error-open",
+ ".errs": "folder-error-open",
+ "_errs": "folder-error-open",
+ "__errs__": "folder-error-open",
+ "crash": "folder-error-open",
+ ".crash": "folder-error-open",
+ "_crash": "folder-error-open",
+ "__crash__": "folder-error-open",
+ "crashes": "folder-error-open",
+ ".crashes": "folder-error-open",
+ "_crashes": "folder-error-open",
+ "__crashes__": "folder-error-open",
+ "event": "folder-event-open",
+ ".event": "folder-event-open",
+ "_event": "folder-event-open",
+ "__event__": "folder-event-open",
+ "events": "folder-event-open",
+ ".events": "folder-event-open",
+ "_events": "folder-event-open",
+ "__events__": "folder-event-open",
+ "auth": "folder-secure-open",
+ ".auth": "folder-secure-open",
+ "_auth": "folder-secure-open",
+ "__auth__": "folder-secure-open",
+ "authentication": "folder-secure-open",
+ ".authentication": "folder-secure-open",
+ "_authentication": "folder-secure-open",
+ "__authentication__": "folder-secure-open",
+ "secure": "folder-secure-open",
+ ".secure": "folder-secure-open",
+ "_secure": "folder-secure-open",
+ "__secure__": "folder-secure-open",
+ "security": "folder-secure-open",
+ ".security": "folder-secure-open",
+ "_security": "folder-secure-open",
+ "__security__": "folder-secure-open",
+ "cert": "folder-secure-open",
+ ".cert": "folder-secure-open",
+ "_cert": "folder-secure-open",
+ "__cert__": "folder-secure-open",
+ "certs": "folder-secure-open",
+ ".certs": "folder-secure-open",
+ "_certs": "folder-secure-open",
+ "__certs__": "folder-secure-open",
+ "certificate": "folder-secure-open",
+ ".certificate": "folder-secure-open",
+ "_certificate": "folder-secure-open",
+ "__certificate__": "folder-secure-open",
+ "certificates": "folder-secure-open",
+ ".certificates": "folder-secure-open",
+ "_certificates": "folder-secure-open",
+ "__certificates__": "folder-secure-open",
+ "ssl": "folder-secure-open",
+ ".ssl": "folder-secure-open",
+ "_ssl": "folder-secure-open",
+ "__ssl__": "folder-secure-open",
+ "cipher": "folder-secure-open",
+ ".cipher": "folder-secure-open",
+ "_cipher": "folder-secure-open",
+ "__cipher__": "folder-secure-open",
+ "cypher": "folder-secure-open",
+ ".cypher": "folder-secure-open",
+ "_cypher": "folder-secure-open",
+ "__cypher__": "folder-secure-open",
+ "tls": "folder-secure-open",
+ ".tls": "folder-secure-open",
+ "_tls": "folder-secure-open",
+ "__tls__": "folder-secure-open",
+ "custom": "folder-custom-open",
+ ".custom": "folder-custom-open",
+ "_custom": "folder-custom-open",
+ "__custom__": "folder-custom-open",
+ "customs": "folder-custom-open",
+ ".customs": "folder-custom-open",
+ "_customs": "folder-custom-open",
+ "__customs__": "folder-custom-open",
+ "draft": "folder-mock-open",
+ ".draft": "folder-mock-open",
+ "_draft": "folder-mock-open",
+ "__draft__": "folder-mock-open",
+ "drafts": "folder-mock-open",
+ ".drafts": "folder-mock-open",
+ "_drafts": "folder-mock-open",
+ "__drafts__": "folder-mock-open",
+ "mock": "folder-mock-open",
+ ".mock": "folder-mock-open",
+ "_mock": "folder-mock-open",
+ "__mock__": "folder-mock-open",
+ "mocks": "folder-mock-open",
+ ".mocks": "folder-mock-open",
+ "_mocks": "folder-mock-open",
+ "__mocks__": "folder-mock-open",
+ "fixture": "folder-mock-open",
+ ".fixture": "folder-mock-open",
+ "_fixture": "folder-mock-open",
+ "__fixture__": "folder-mock-open",
+ "fixtures": "folder-mock-open",
+ ".fixtures": "folder-mock-open",
+ "_fixtures": "folder-mock-open",
+ "__fixtures__": "folder-mock-open",
+ "concept": "folder-mock-open",
+ ".concept": "folder-mock-open",
+ "_concept": "folder-mock-open",
+ "__concept__": "folder-mock-open",
+ "concepts": "folder-mock-open",
+ ".concepts": "folder-mock-open",
+ "_concepts": "folder-mock-open",
+ "__concepts__": "folder-mock-open",
+ "sketch": "folder-mock-open",
+ ".sketch": "folder-mock-open",
+ "_sketch": "folder-mock-open",
+ "__sketch__": "folder-mock-open",
+ "sketches": "folder-mock-open",
+ ".sketches": "folder-mock-open",
+ "_sketches": "folder-mock-open",
+ "__sketches__": "folder-mock-open",
+ "syntax": "folder-syntax-open",
+ ".syntax": "folder-syntax-open",
+ "_syntax": "folder-syntax-open",
+ "__syntax__": "folder-syntax-open",
+ "syntaxes": "folder-syntax-open",
+ ".syntaxes": "folder-syntax-open",
+ "_syntaxes": "folder-syntax-open",
+ "__syntaxes__": "folder-syntax-open",
+ "spellcheck": "folder-syntax-open",
+ ".spellcheck": "folder-syntax-open",
+ "_spellcheck": "folder-syntax-open",
+ "__spellcheck__": "folder-syntax-open",
+ "spellcheckers": "folder-syntax-open",
+ ".spellcheckers": "folder-syntax-open",
+ "_spellcheckers": "folder-syntax-open",
+ "__spellcheckers__": "folder-syntax-open",
+ "vm": "folder-vm-open",
+ ".vm": "folder-vm-open",
+ "_vm": "folder-vm-open",
+ "__vm__": "folder-vm-open",
+ "vms": "folder-vm-open",
+ ".vms": "folder-vm-open",
+ "_vms": "folder-vm-open",
+ "__vms__": "folder-vm-open",
+ "stylus": "folder-stylus-open",
+ ".stylus": "folder-stylus-open",
+ "_stylus": "folder-stylus-open",
+ "__stylus__": "folder-stylus-open",
+ "flow-typed": "folder-flow-open",
+ ".flow-typed": "folder-flow-open",
+ "_flow-typed": "folder-flow-open",
+ "__flow-typed__": "folder-flow-open",
+ "rule": "folder-rules-open",
+ ".rule": "folder-rules-open",
+ "_rule": "folder-rules-open",
+ "__rule__": "folder-rules-open",
+ "rules": "folder-rules-open",
+ ".rules": "folder-rules-open",
+ "_rules": "folder-rules-open",
+ "__rules__": "folder-rules-open",
+ "validation": "folder-rules-open",
+ ".validation": "folder-rules-open",
+ "_validation": "folder-rules-open",
+ "__validation__": "folder-rules-open",
+ "validations": "folder-rules-open",
+ ".validations": "folder-rules-open",
+ "_validations": "folder-rules-open",
+ "__validations__": "folder-rules-open",
+ "validator": "folder-rules-open",
+ ".validator": "folder-rules-open",
+ "_validator": "folder-rules-open",
+ "__validator__": "folder-rules-open",
+ "validators": "folder-rules-open",
+ ".validators": "folder-rules-open",
+ "_validators": "folder-rules-open",
+ "__validators__": "folder-rules-open",
+ "review": "folder-review-open",
+ ".review": "folder-review-open",
+ "_review": "folder-review-open",
+ "__review__": "folder-review-open",
+ "reviews": "folder-review-open",
+ ".reviews": "folder-review-open",
+ "_reviews": "folder-review-open",
+ "__reviews__": "folder-review-open",
+ "revisal": "folder-review-open",
+ ".revisal": "folder-review-open",
+ "_revisal": "folder-review-open",
+ "__revisal__": "folder-review-open",
+ "revisals": "folder-review-open",
+ ".revisals": "folder-review-open",
+ "_revisals": "folder-review-open",
+ "__revisals__": "folder-review-open",
+ "reviewed": "folder-review-open",
+ ".reviewed": "folder-review-open",
+ "_reviewed": "folder-review-open",
+ "__reviewed__": "folder-review-open",
+ "preview": "folder-review-open",
+ ".preview": "folder-review-open",
+ "_preview": "folder-review-open",
+ "__preview__": "folder-review-open",
+ "previews": "folder-review-open",
+ ".previews": "folder-review-open",
+ "_previews": "folder-review-open",
+ "__previews__": "folder-review-open",
+ "anim": "folder-animation-open",
+ ".anim": "folder-animation-open",
+ "_anim": "folder-animation-open",
+ "__anim__": "folder-animation-open",
+ "anims": "folder-animation-open",
+ ".anims": "folder-animation-open",
+ "_anims": "folder-animation-open",
+ "__anims__": "folder-animation-open",
+ "animation": "folder-animation-open",
+ ".animation": "folder-animation-open",
+ "_animation": "folder-animation-open",
+ "__animation__": "folder-animation-open",
+ "animations": "folder-animation-open",
+ ".animations": "folder-animation-open",
+ "_animations": "folder-animation-open",
+ "__animations__": "folder-animation-open",
+ "animated": "folder-animation-open",
+ ".animated": "folder-animation-open",
+ "_animated": "folder-animation-open",
+ "__animated__": "folder-animation-open",
+ "motion": "folder-animation-open",
+ ".motion": "folder-animation-open",
+ "_motion": "folder-animation-open",
+ "__motion__": "folder-animation-open",
+ "motions": "folder-animation-open",
+ ".motions": "folder-animation-open",
+ "_motions": "folder-animation-open",
+ "__motions__": "folder-animation-open",
+ "transition": "folder-animation-open",
+ ".transition": "folder-animation-open",
+ "_transition": "folder-animation-open",
+ "__transition__": "folder-animation-open",
+ "transitions": "folder-animation-open",
+ ".transitions": "folder-animation-open",
+ "_transitions": "folder-animation-open",
+ "__transitions__": "folder-animation-open",
+ "easing": "folder-animation-open",
+ ".easing": "folder-animation-open",
+ "_easing": "folder-animation-open",
+ "__easing__": "folder-animation-open",
+ "easings": "folder-animation-open",
+ ".easings": "folder-animation-open",
+ "_easings": "folder-animation-open",
+ "__easings__": "folder-animation-open",
+ "guard": "folder-guard-open",
+ ".guard": "folder-guard-open",
+ "_guard": "folder-guard-open",
+ "__guard__": "folder-guard-open",
+ "guards": "folder-guard-open",
+ ".guards": "folder-guard-open",
+ "_guards": "folder-guard-open",
+ "__guards__": "folder-guard-open",
+ "prisma": "folder-prisma-open",
+ ".prisma": "folder-prisma-open",
+ "_prisma": "folder-prisma-open",
+ "__prisma__": "folder-prisma-open",
+ "prisma/schema": "folder-prisma-open",
+ ".prisma/schema": "folder-prisma-open",
+ "_prisma/schema": "folder-prisma-open",
+ "__prisma/schema__": "folder-prisma-open",
+ "pipe": "folder-pipe-open",
+ ".pipe": "folder-pipe-open",
+ "_pipe": "folder-pipe-open",
+ "__pipe__": "folder-pipe-open",
+ "pipes": "folder-pipe-open",
+ ".pipes": "folder-pipe-open",
+ "_pipes": "folder-pipe-open",
+ "__pipes__": "folder-pipe-open",
+ "pipeline": "folder-pipe-open",
+ ".pipeline": "folder-pipe-open",
+ "_pipeline": "folder-pipe-open",
+ "__pipeline__": "folder-pipe-open",
+ "pipelines": "folder-pipe-open",
+ ".pipelines": "folder-pipe-open",
+ "_pipelines": "folder-pipe-open",
+ "__pipelines__": "folder-pipe-open",
+ "svg": "folder-svg-open",
+ ".svg": "folder-svg-open",
+ "_svg": "folder-svg-open",
+ "__svg__": "folder-svg-open",
+ "svgs": "folder-svg-open",
+ ".svgs": "folder-svg-open",
+ "_svgs": "folder-svg-open",
+ "__svgs__": "folder-svg-open",
+ "nuxt": "folder-nuxt-open",
+ ".nuxt": "folder-nuxt-open",
+ "_nuxt": "folder-nuxt-open",
+ "__nuxt__": "folder-nuxt-open",
+ "terraform": "folder-terraform-open",
+ ".terraform": "folder-terraform-open",
+ "_terraform": "folder-terraform-open",
+ "__terraform__": "folder-terraform-open",
+ "mobile": "folder-mobile-open",
+ ".mobile": "folder-mobile-open",
+ "_mobile": "folder-mobile-open",
+ "__mobile__": "folder-mobile-open",
+ "mobiles": "folder-mobile-open",
+ ".mobiles": "folder-mobile-open",
+ "_mobiles": "folder-mobile-open",
+ "__mobiles__": "folder-mobile-open",
+ "portable": "folder-mobile-open",
+ ".portable": "folder-mobile-open",
+ "_portable": "folder-mobile-open",
+ "__portable__": "folder-mobile-open",
+ "portability": "folder-mobile-open",
+ ".portability": "folder-mobile-open",
+ "_portability": "folder-mobile-open",
+ "__portability__": "folder-mobile-open",
+ "phone": "folder-mobile-open",
+ ".phone": "folder-mobile-open",
+ "_phone": "folder-mobile-open",
+ "__phone__": "folder-mobile-open",
+ "phones": "folder-mobile-open",
+ ".phones": "folder-mobile-open",
+ "_phones": "folder-mobile-open",
+ "__phones__": "folder-mobile-open",
+ "stencil": "folder-stencil-open",
+ ".stencil": "folder-stencil-open",
+ "_stencil": "folder-stencil-open",
+ "__stencil__": "folder-stencil-open",
+ "firebase": "folder-firebase-open",
+ ".firebase": "folder-firebase-open",
+ "_firebase": "folder-firebase-open",
+ "__firebase__": "folder-firebase-open",
+ "firestore": "folder-firestore-open",
+ ".firestore": "folder-firestore-open",
+ "_firestore": "folder-firestore-open",
+ "__firestore__": "folder-firestore-open",
+ "cloud-firestore": "folder-firestore-open",
+ ".cloud-firestore": "folder-firestore-open",
+ "_cloud-firestore": "folder-firestore-open",
+ "__cloud-firestore__": "folder-firestore-open",
+ "firebase-firestore": "folder-firestore-open",
+ ".firebase-firestore": "folder-firestore-open",
+ "_firebase-firestore": "folder-firestore-open",
+ "__firebase-firestore__": "folder-firestore-open",
+ "cloud-functions": "folder-cloud-functions-open",
+ ".cloud-functions": "folder-cloud-functions-open",
+ "_cloud-functions": "folder-cloud-functions-open",
+ "__cloud-functions__": "folder-cloud-functions-open",
+ "cloudfunctions": "folder-cloud-functions-open",
+ ".cloudfunctions": "folder-cloud-functions-open",
+ "_cloudfunctions": "folder-cloud-functions-open",
+ "__cloudfunctions__": "folder-cloud-functions-open",
+ "firebase-cloud-functions": "folder-cloud-functions-open",
+ ".firebase-cloud-functions": "folder-cloud-functions-open",
+ "_firebase-cloud-functions": "folder-cloud-functions-open",
+ "__firebase-cloud-functions__": "folder-cloud-functions-open",
+ "firebase-cloudfunctions": "folder-cloud-functions-open",
+ ".firebase-cloudfunctions": "folder-cloud-functions-open",
+ "_firebase-cloudfunctions": "folder-cloud-functions-open",
+ "__firebase-cloudfunctions__": "folder-cloud-functions-open",
+ "svelte": "folder-svelte-open",
+ ".svelte": "folder-svelte-open",
+ "_svelte": "folder-svelte-open",
+ "__svelte__": "folder-svelte-open",
+ "svelte-kit": "folder-svelte-open",
+ ".svelte-kit": "folder-svelte-open",
+ "_svelte-kit": "folder-svelte-open",
+ "__svelte-kit__": "folder-svelte-open",
+ "update": "folder-update-open",
+ ".update": "folder-update-open",
+ "_update": "folder-update-open",
+ "__update__": "folder-update-open",
+ "updates": "folder-update-open",
+ ".updates": "folder-update-open",
+ "_updates": "folder-update-open",
+ "__updates__": "folder-update-open",
+ "upgrade": "folder-update-open",
+ ".upgrade": "folder-update-open",
+ "_upgrade": "folder-update-open",
+ "__upgrade__": "folder-update-open",
+ "upgrades": "folder-update-open",
+ ".upgrades": "folder-update-open",
+ "_upgrades": "folder-update-open",
+ "__upgrades__": "folder-update-open",
+ "idea": "folder-intellij-open",
+ ".idea": "folder-intellij-open",
+ "_idea": "folder-intellij-open",
+ "__idea__": "folder-intellij-open",
+ "azure-pipelines": "folder-azure-pipelines-open",
+ ".azure-pipelines": "folder-azure-pipelines-open",
+ "_azure-pipelines": "folder-azure-pipelines-open",
+ "__azure-pipelines__": "folder-azure-pipelines-open",
+ "azure-pipelines-ci": "folder-azure-pipelines-open",
+ ".azure-pipelines-ci": "folder-azure-pipelines-open",
+ "_azure-pipelines-ci": "folder-azure-pipelines-open",
+ "__azure-pipelines-ci__": "folder-azure-pipelines-open",
+ "mjml": "folder-mjml-open",
+ ".mjml": "folder-mjml-open",
+ "_mjml": "folder-mjml-open",
+ "__mjml__": "folder-mjml-open",
+ "admin": "folder-admin-open",
+ ".admin": "folder-admin-open",
+ "_admin": "folder-admin-open",
+ "__admin__": "folder-admin-open",
+ "admins": "folder-admin-open",
+ ".admins": "folder-admin-open",
+ "_admins": "folder-admin-open",
+ "__admins__": "folder-admin-open",
+ "manager": "folder-admin-open",
+ ".manager": "folder-admin-open",
+ "_manager": "folder-admin-open",
+ "__manager__": "folder-admin-open",
+ "managers": "folder-admin-open",
+ ".managers": "folder-admin-open",
+ "_managers": "folder-admin-open",
+ "__managers__": "folder-admin-open",
+ "moderator": "folder-admin-open",
+ ".moderator": "folder-admin-open",
+ "_moderator": "folder-admin-open",
+ "__moderator__": "folder-admin-open",
+ "moderators": "folder-admin-open",
+ ".moderators": "folder-admin-open",
+ "_moderators": "folder-admin-open",
+ "__moderators__": "folder-admin-open",
+ "jupyter": "folder-jupyter-open",
+ ".jupyter": "folder-jupyter-open",
+ "_jupyter": "folder-jupyter-open",
+ "__jupyter__": "folder-jupyter-open",
+ "notebook": "folder-jupyter-open",
+ ".notebook": "folder-jupyter-open",
+ "_notebook": "folder-jupyter-open",
+ "__notebook__": "folder-jupyter-open",
+ "notebooks": "folder-jupyter-open",
+ ".notebooks": "folder-jupyter-open",
+ "_notebooks": "folder-jupyter-open",
+ "__notebooks__": "folder-jupyter-open",
+ "ipynb": "folder-jupyter-open",
+ ".ipynb": "folder-jupyter-open",
+ "_ipynb": "folder-jupyter-open",
+ "__ipynb__": "folder-jupyter-open",
+ "scala": "folder-scala-open",
+ ".scala": "folder-scala-open",
+ "_scala": "folder-scala-open",
+ "__scala__": "folder-scala-open",
+ "connection": "folder-connection-open",
+ ".connection": "folder-connection-open",
+ "_connection": "folder-connection-open",
+ "__connection__": "folder-connection-open",
+ "connections": "folder-connection-open",
+ ".connections": "folder-connection-open",
+ "_connections": "folder-connection-open",
+ "__connections__": "folder-connection-open",
+ "integration": "folder-connection-open",
+ ".integration": "folder-connection-open",
+ "_integration": "folder-connection-open",
+ "__integration__": "folder-connection-open",
+ "integrations": "folder-connection-open",
+ ".integrations": "folder-connection-open",
+ "_integrations": "folder-connection-open",
+ "__integrations__": "folder-connection-open",
+ "remote": "folder-connection-open",
+ ".remote": "folder-connection-open",
+ "_remote": "folder-connection-open",
+ "__remote__": "folder-connection-open",
+ "remotes": "folder-connection-open",
+ ".remotes": "folder-connection-open",
+ "_remotes": "folder-connection-open",
+ "__remotes__": "folder-connection-open",
+ "quasar": "folder-quasar-open",
+ ".quasar": "folder-quasar-open",
+ "_quasar": "folder-quasar-open",
+ "__quasar__": "folder-quasar-open",
+ "next": "folder-next-open",
+ ".next": "folder-next-open",
+ "_next": "folder-next-open",
+ "__next__": "folder-next-open",
+ "cobol": "folder-cobol-open",
+ ".cobol": "folder-cobol-open",
+ "_cobol": "folder-cobol-open",
+ "__cobol__": "folder-cobol-open",
+ "yarn": "folder-yarn-open",
+ ".yarn": "folder-yarn-open",
+ "_yarn": "folder-yarn-open",
+ "__yarn__": "folder-yarn-open",
+ "husky": "folder-husky-open",
+ ".husky": "folder-husky-open",
+ "_husky": "folder-husky-open",
+ "__husky__": "folder-husky-open",
+ "storybook": "folder-storybook-open",
+ ".storybook": "folder-storybook-open",
+ "_storybook": "folder-storybook-open",
+ "__storybook__": "folder-storybook-open",
+ "stories": "folder-storybook-open",
+ ".stories": "folder-storybook-open",
+ "_stories": "folder-storybook-open",
+ "__stories__": "folder-storybook-open",
+ "base": "folder-base-open",
+ ".base": "folder-base-open",
+ "_base": "folder-base-open",
+ "__base__": "folder-base-open",
+ "bases": "folder-base-open",
+ ".bases": "folder-base-open",
+ "_bases": "folder-base-open",
+ "__bases__": "folder-base-open",
+ "cart": "folder-cart-open",
+ ".cart": "folder-cart-open",
+ "_cart": "folder-cart-open",
+ "__cart__": "folder-cart-open",
+ "shopping-cart": "folder-cart-open",
+ ".shopping-cart": "folder-cart-open",
+ "_shopping-cart": "folder-cart-open",
+ "__shopping-cart__": "folder-cart-open",
+ "shopping": "folder-cart-open",
+ ".shopping": "folder-cart-open",
+ "_shopping": "folder-cart-open",
+ "__shopping__": "folder-cart-open",
+ "shop": "folder-cart-open",
+ ".shop": "folder-cart-open",
+ "_shop": "folder-cart-open",
+ "__shop__": "folder-cart-open",
+ "home": "folder-home-open",
+ ".home": "folder-home-open",
+ "_home": "folder-home-open",
+ "__home__": "folder-home-open",
+ "start": "folder-home-open",
+ ".start": "folder-home-open",
+ "_start": "folder-home-open",
+ "__start__": "folder-home-open",
+ "main": "folder-home-open",
+ ".main": "folder-home-open",
+ "_main": "folder-home-open",
+ "__main__": "folder-home-open",
+ "landing": "folder-home-open",
+ ".landing": "folder-home-open",
+ "_landing": "folder-home-open",
+ "__landing__": "folder-home-open",
+ "project": "folder-project-open",
+ ".project": "folder-project-open",
+ "_project": "folder-project-open",
+ "__project__": "folder-project-open",
+ "projects": "folder-project-open",
+ ".projects": "folder-project-open",
+ "_projects": "folder-project-open",
+ "__projects__": "folder-project-open",
+ "interface": "folder-interface-open",
+ ".interface": "folder-interface-open",
+ "_interface": "folder-interface-open",
+ "__interface__": "folder-interface-open",
+ "interfaces": "folder-interface-open",
+ ".interfaces": "folder-interface-open",
+ "_interfaces": "folder-interface-open",
+ "__interfaces__": "folder-interface-open",
+ "netlify": "folder-netlify-open",
+ ".netlify": "folder-netlify-open",
+ "_netlify": "folder-netlify-open",
+ "__netlify__": "folder-netlify-open",
+ "enum": "folder-enum-open",
+ ".enum": "folder-enum-open",
+ "_enum": "folder-enum-open",
+ "__enum__": "folder-enum-open",
+ "enums": "folder-enum-open",
+ ".enums": "folder-enum-open",
+ "_enums": "folder-enum-open",
+ "__enums__": "folder-enum-open",
+ "pact": "folder-contract-open",
+ ".pact": "folder-contract-open",
+ "_pact": "folder-contract-open",
+ "__pact__": "folder-contract-open",
+ "pacts": "folder-contract-open",
+ ".pacts": "folder-contract-open",
+ "_pacts": "folder-contract-open",
+ "__pacts__": "folder-contract-open",
+ "contract": "folder-contract-open",
+ ".contract": "folder-contract-open",
+ "_contract": "folder-contract-open",
+ "__contract__": "folder-contract-open",
+ "contracts": "folder-contract-open",
+ ".contracts": "folder-contract-open",
+ "_contracts": "folder-contract-open",
+ "__contracts__": "folder-contract-open",
+ "contract-testing": "folder-contract-open",
+ ".contract-testing": "folder-contract-open",
+ "_contract-testing": "folder-contract-open",
+ "__contract-testing__": "folder-contract-open",
+ "contract-test": "folder-contract-open",
+ ".contract-test": "folder-contract-open",
+ "_contract-test": "folder-contract-open",
+ "__contract-test__": "folder-contract-open",
+ "contract-tests": "folder-contract-open",
+ ".contract-tests": "folder-contract-open",
+ "_contract-tests": "folder-contract-open",
+ "__contract-tests__": "folder-contract-open",
+ "helm": "folder-helm-open",
+ ".helm": "folder-helm-open",
+ "_helm": "folder-helm-open",
+ "__helm__": "folder-helm-open",
+ "helmchart": "folder-helm-open",
+ ".helmchart": "folder-helm-open",
+ "_helmchart": "folder-helm-open",
+ "__helmchart__": "folder-helm-open",
+ "helmcharts": "folder-helm-open",
+ ".helmcharts": "folder-helm-open",
+ "_helmcharts": "folder-helm-open",
+ "__helmcharts__": "folder-helm-open",
+ "queue": "folder-queue-open",
+ ".queue": "folder-queue-open",
+ "_queue": "folder-queue-open",
+ "__queue__": "folder-queue-open",
+ "queues": "folder-queue-open",
+ ".queues": "folder-queue-open",
+ "_queues": "folder-queue-open",
+ "__queues__": "folder-queue-open",
+ "bull": "folder-queue-open",
+ ".bull": "folder-queue-open",
+ "_bull": "folder-queue-open",
+ "__bull__": "folder-queue-open",
+ "mq": "folder-queue-open",
+ ".mq": "folder-queue-open",
+ "_mq": "folder-queue-open",
+ "__mq__": "folder-queue-open",
+ "vercel": "folder-vercel-open",
+ ".vercel": "folder-vercel-open",
+ "_vercel": "folder-vercel-open",
+ "__vercel__": "folder-vercel-open",
+ "now": "folder-vercel-open",
+ ".now": "folder-vercel-open",
+ "_now": "folder-vercel-open",
+ "__now__": "folder-vercel-open",
+ "cypress": "folder-cypress-open",
+ ".cypress": "folder-cypress-open",
+ "_cypress": "folder-cypress-open",
+ "__cypress__": "folder-cypress-open",
+ "decorator": "folder-decorators-open",
+ ".decorator": "folder-decorators-open",
+ "_decorator": "folder-decorators-open",
+ "__decorator__": "folder-decorators-open",
+ "decorators": "folder-decorators-open",
+ ".decorators": "folder-decorators-open",
+ "_decorators": "folder-decorators-open",
+ "__decorators__": "folder-decorators-open",
+ "java": "folder-java-open",
+ ".java": "folder-java-open",
+ "_java": "folder-java-open",
+ "__java__": "folder-java-open",
+ "resolver": "folder-resolver-open",
+ ".resolver": "folder-resolver-open",
+ "_resolver": "folder-resolver-open",
+ "__resolver__": "folder-resolver-open",
+ "resolvers": "folder-resolver-open",
+ ".resolvers": "folder-resolver-open",
+ "_resolvers": "folder-resolver-open",
+ "__resolvers__": "folder-resolver-open",
+ "angular": "folder-angular-open",
+ ".angular": "folder-angular-open",
+ "_angular": "folder-angular-open",
+ "__angular__": "folder-angular-open",
+ "unity": "folder-unity-open",
+ ".unity": "folder-unity-open",
+ "_unity": "folder-unity-open",
+ "__unity__": "folder-unity-open",
+ "pdf": "folder-pdf-open",
+ ".pdf": "folder-pdf-open",
+ "_pdf": "folder-pdf-open",
+ "__pdf__": "folder-pdf-open",
+ "pdfs": "folder-pdf-open",
+ ".pdfs": "folder-pdf-open",
+ "_pdfs": "folder-pdf-open",
+ "__pdfs__": "folder-pdf-open",
+ "protobuf": "folder-proto-open",
+ ".protobuf": "folder-proto-open",
+ "_protobuf": "folder-proto-open",
+ "__protobuf__": "folder-proto-open",
+ "protobufs": "folder-proto-open",
+ ".protobufs": "folder-proto-open",
+ "_protobufs": "folder-proto-open",
+ "__protobufs__": "folder-proto-open",
+ "proto": "folder-proto-open",
+ ".proto": "folder-proto-open",
+ "_proto": "folder-proto-open",
+ "protos": "folder-proto-open",
+ ".protos": "folder-proto-open",
+ "_protos": "folder-proto-open",
+ "__protos__": "folder-proto-open",
+ "plastic": "folder-plastic-open",
+ ".plastic": "folder-plastic-open",
+ "_plastic": "folder-plastic-open",
+ "__plastic__": "folder-plastic-open",
+ "gamemaker": "folder-gamemaker-open",
+ ".gamemaker": "folder-gamemaker-open",
+ "_gamemaker": "folder-gamemaker-open",
+ "__gamemaker__": "folder-gamemaker-open",
+ "gamemaker2": "folder-gamemaker-open",
+ ".gamemaker2": "folder-gamemaker-open",
+ "_gamemaker2": "folder-gamemaker-open",
+ "__gamemaker2__": "folder-gamemaker-open",
+ "hg": "folder-mercurial-open",
+ ".hg": "folder-mercurial-open",
+ "_hg": "folder-mercurial-open",
+ "__hg__": "folder-mercurial-open",
+ "hghooks": "folder-mercurial-open",
+ ".hghooks": "folder-mercurial-open",
+ "_hghooks": "folder-mercurial-open",
+ "__hghooks__": "folder-mercurial-open",
+ "hgext": "folder-mercurial-open",
+ ".hgext": "folder-mercurial-open",
+ "_hgext": "folder-mercurial-open",
+ "__hgext__": "folder-mercurial-open",
+ "godot": "folder-godot-open",
+ ".godot": "folder-godot-open",
+ "_godot": "folder-godot-open",
+ "__godot__": "folder-godot-open",
+ "godot-cpp": "folder-godot-open",
+ ".godot-cpp": "folder-godot-open",
+ "_godot-cpp": "folder-godot-open",
+ "__godot-cpp__": "folder-godot-open",
+ "lottie": "folder-lottie-open",
+ ".lottie": "folder-lottie-open",
+ "_lottie": "folder-lottie-open",
+ "__lottie__": "folder-lottie-open",
+ "lotties": "folder-lottie-open",
+ ".lotties": "folder-lottie-open",
+ "_lotties": "folder-lottie-open",
+ "__lotties__": "folder-lottie-open",
+ "lottiefiles": "folder-lottie-open",
+ ".lottiefiles": "folder-lottie-open",
+ "_lottiefiles": "folder-lottie-open",
+ "__lottiefiles__": "folder-lottie-open",
+ "taskfile": "folder-taskfile-open",
+ ".taskfile": "folder-taskfile-open",
+ "_taskfile": "folder-taskfile-open",
+ "__taskfile__": "folder-taskfile-open",
+ "taskfiles": "folder-taskfile-open",
+ ".taskfiles": "folder-taskfile-open",
+ "_taskfiles": "folder-taskfile-open",
+ "__taskfiles__": "folder-taskfile-open",
+ "drizzle": "folder-drizzle-open",
+ ".drizzle": "folder-drizzle-open",
+ "_drizzle": "folder-drizzle-open",
+ "__drizzle__": "folder-drizzle-open",
+ "cloudflare": "folder-cloudflare-open",
+ ".cloudflare": "folder-cloudflare-open",
+ "_cloudflare": "folder-cloudflare-open",
+ "__cloudflare__": "folder-cloudflare-open",
+ "seeds": "folder-seeders-open",
+ ".seeds": "folder-seeders-open",
+ "_seeds": "folder-seeders-open",
+ "__seeds__": "folder-seeders-open",
+ "seeders": "folder-seeders-open",
+ ".seeders": "folder-seeders-open",
+ "_seeders": "folder-seeders-open",
+ "__seeders__": "folder-seeders-open",
+ "seed": "folder-seeders-open",
+ ".seed": "folder-seeders-open",
+ "_seed": "folder-seeders-open",
+ "__seed__": "folder-seeders-open",
+ "seeding": "folder-seeders-open",
+ ".seeding": "folder-seeders-open",
+ "_seeding": "folder-seeders-open",
+ "__seeding__": "folder-seeders-open",
+ "store": "folder-store-open",
+ ".store": "folder-store-open",
+ "_store": "folder-store-open",
+ "__store__": "folder-store-open",
+ "stores": "folder-store-open",
+ ".stores": "folder-store-open",
+ "_stores": "folder-store-open",
+ "__stores__": "folder-store-open",
+ "bicep": "folder-bicep-open",
+ ".bicep": "folder-bicep-open",
+ "_bicep": "folder-bicep-open",
+ "__bicep__": "folder-bicep-open",
+ "snap": "folder-snapcraft-open",
+ ".snap": "folder-snapcraft-open",
+ "_snap": "folder-snapcraft-open",
+ "__snap__": "folder-snapcraft-open",
+ "snapcraft": "folder-snapcraft-open",
+ ".snapcraft": "folder-snapcraft-open",
+ "_snapcraft": "folder-snapcraft-open",
+ "__snapcraft__": "folder-snapcraft-open",
+ "dev": "folder-development-open",
+ ".dev": "folder-development-open",
+ "_dev": "folder-development-open",
+ "__dev__": "folder-development-open",
+ "development": "folder-development-open",
+ ".development": "folder-development-open",
+ "_development": "folder-development-open",
+ "__development__": "folder-development-open",
+ "flutter": "folder-flutter-open",
+ ".flutter": "folder-flutter-open",
+ "_flutter": "folder-flutter-open",
+ "__flutter__": "folder-flutter-open",
+ "snippet": "folder-snippet-open",
+ ".snippet": "folder-snippet-open",
+ "_snippet": "folder-snippet-open",
+ "__snippet__": "folder-snippet-open",
+ "snippets": "folder-snippet-open",
+ ".snippets": "folder-snippet-open",
+ "_snippets": "folder-snippet-open",
+ "__snippets__": "folder-snippet-open",
+ "element": "folder-element-open",
+ ".element": "folder-element-open",
+ "_element": "folder-element-open",
+ "__element__": "folder-element-open",
+ "elements": "folder-element-open",
+ ".elements": "folder-element-open",
+ "_elements": "folder-element-open",
+ "__elements__": "folder-element-open",
+ "src-tauri": "folder-src-tauri-open",
+ ".src-tauri": "folder-src-tauri-open",
+ "_src-tauri": "folder-src-tauri-open",
+ "__src-tauri__": "folder-src-tauri-open",
+ "favicon": "folder-favicon-open",
+ ".favicon": "folder-favicon-open",
+ "_favicon": "folder-favicon-open",
+ "__favicon__": "folder-favicon-open",
+ "favicons": "folder-favicon-open",
+ ".favicons": "folder-favicon-open",
+ "_favicons": "folder-favicon-open",
+ "__favicons__": "folder-favicon-open",
+ "lefthook": "folder-lefthook-open",
+ ".lefthook": "folder-lefthook-open",
+ "_lefthook": "folder-lefthook-open",
+ "__lefthook__": "folder-lefthook-open",
+ "lefthook-local": "folder-lefthook-open",
+ ".lefthook-local": "folder-lefthook-open",
+ "_lefthook-local": "folder-lefthook-open",
+ "__lefthook-local__": "folder-lefthook-open",
+ "bloc": "folder-bloc-open",
+ ".bloc": "folder-bloc-open",
+ "_bloc": "folder-bloc-open",
+ "__bloc__": "folder-bloc-open",
+ "cubit": "folder-bloc-open",
+ ".cubit": "folder-bloc-open",
+ "_cubit": "folder-bloc-open",
+ "__cubit__": "folder-bloc-open",
+ "blocs": "folder-bloc-open",
+ ".blocs": "folder-bloc-open",
+ "_blocs": "folder-bloc-open",
+ "__blocs__": "folder-bloc-open",
+ "cubits": "folder-bloc-open",
+ ".cubits": "folder-bloc-open",
+ "_cubits": "folder-bloc-open",
+ "__cubits__": "folder-bloc-open",
+ "powershell": "folder-powershell-open",
+ ".powershell": "folder-powershell-open",
+ "_powershell": "folder-powershell-open",
+ "__powershell__": "folder-powershell-open",
+ "ps": "folder-powershell-open",
+ ".ps": "folder-powershell-open",
+ "_ps": "folder-powershell-open",
+ "__ps__": "folder-powershell-open",
+ "ps1": "folder-powershell-open",
+ ".ps1": "folder-powershell-open",
+ "_ps1": "folder-powershell-open",
+ "__ps1__": "folder-powershell-open",
+ "repository": "folder-repository-open",
+ ".repository": "folder-repository-open",
+ "_repository": "folder-repository-open",
+ "__repository__": "folder-repository-open",
+ "repositories": "folder-repository-open",
+ ".repositories": "folder-repository-open",
+ "_repositories": "folder-repository-open",
+ "__repositories__": "folder-repository-open",
+ "repo": "folder-repository-open",
+ ".repo": "folder-repository-open",
+ "_repo": "folder-repository-open",
+ "__repo__": "folder-repository-open",
+ "repos": "folder-repository-open",
+ ".repos": "folder-repository-open",
+ "_repos": "folder-repository-open",
+ "__repos__": "folder-repository-open",
+ "luau": "folder-luau-open",
+ ".luau": "folder-luau-open",
+ "_luau": "folder-luau-open",
+ "__luau__": "folder-luau-open",
+ "obsidian": "folder-obsidian-open",
+ ".obsidian": "folder-obsidian-open",
+ "_obsidian": "folder-obsidian-open",
+ "__obsidian__": "folder-obsidian-open",
+ "trash": "folder-trash-open",
+ ".trash": "folder-trash-open",
+ "_trash": "folder-trash-open",
+ "__trash__": "folder-trash-open",
+ "cline_docs": "folder-cline-open",
+ ".cline_docs": "folder-cline-open",
+ "_cline_docs": "folder-cline-open",
+ "__cline_docs__": "folder-cline-open",
+ "liquibase": "folder-liquibase-open",
+ ".liquibase": "folder-liquibase-open",
+ "_liquibase": "folder-liquibase-open",
+ "__liquibase__": "folder-liquibase-open",
+ "dart": "folder-dart-open",
+ ".dart": "folder-dart-open",
+ "_dart": "folder-dart-open",
+ "__dart__": "folder-dart-open",
+ "dart_tool": "folder-dart-open",
+ ".dart_tool": "folder-dart-open",
+ "_dart_tool": "folder-dart-open",
+ "__dart_tool__": "folder-dart-open",
+ "dart_tools": "folder-dart-open",
+ ".dart_tools": "folder-dart-open",
+ "_dart_tools": "folder-dart-open",
+ "__dart_tools__": "folder-dart-open",
+ "zeabur": "folder-zeabur-open",
+ ".zeabur": "folder-zeabur-open",
+ "_zeabur": "folder-zeabur-open",
+ "__zeabur__": "folder-zeabur-open",
+ "kusto": "folder-kusto-open",
+ ".kusto": "folder-kusto-open",
+ "_kusto": "folder-kusto-open",
+ "__kusto__": "folder-kusto-open",
+ "kql": "folder-kusto-open",
+ ".kql": "folder-kusto-open",
+ "_kql": "folder-kusto-open",
+ "__kql__": "folder-kusto-open",
+ "policy": "folder-policy-open",
+ ".policy": "folder-policy-open",
+ "_policy": "folder-policy-open",
+ "__policy__": "folder-policy-open",
+ "policies": "folder-policy-open",
+ ".policies": "folder-policy-open",
+ "_policies": "folder-policy-open",
+ "__policies__": "folder-policy-open",
+ "attachment": "folder-attachment-open",
+ ".attachment": "folder-attachment-open",
+ "_attachment": "folder-attachment-open",
+ "__attachment__": "folder-attachment-open",
+ "attachments": "folder-attachment-open",
+ ".attachments": "folder-attachment-open",
+ "_attachments": "folder-attachment-open",
+ "__attachments__": "folder-attachment-open",
+ "bibliography": "folder-bibliography-open",
+ ".bibliography": "folder-bibliography-open",
+ "_bibliography": "folder-bibliography-open",
+ "__bibliography__": "folder-bibliography-open",
+ "bibliographies": "folder-bibliography-open",
+ ".bibliographies": "folder-bibliography-open",
+ "_bibliographies": "folder-bibliography-open",
+ "__bibliographies__": "folder-bibliography-open",
+ "book": "folder-bibliography-open",
+ ".book": "folder-bibliography-open",
+ "_book": "folder-bibliography-open",
+ "__book__": "folder-bibliography-open",
+ "books": "folder-bibliography-open",
+ ".books": "folder-bibliography-open",
+ "_books": "folder-bibliography-open",
+ "__books__": "folder-bibliography-open",
+ "link": "folder-link-open",
+ ".link": "folder-link-open",
+ "_link": "folder-link-open",
+ "__link__": "folder-link-open",
+ "links": "folder-link-open",
+ ".links": "folder-link-open",
+ "_links": "folder-link-open",
+ "__links__": "folder-link-open",
+ "pytorch": "folder-pytorch-open",
+ ".pytorch": "folder-pytorch-open",
+ "_pytorch": "folder-pytorch-open",
+ "__pytorch__": "folder-pytorch-open",
+ "torch": "folder-pytorch-open",
+ ".torch": "folder-pytorch-open",
+ "_torch": "folder-pytorch-open",
+ "__torch__": "folder-pytorch-open",
+ "blender": "folder-blender-open",
+ ".blender": "folder-blender-open",
+ "_blender": "folder-blender-open",
+ "__blender__": "folder-blender-open",
+ "blender-assets": "folder-blender-open",
+ ".blender-assets": "folder-blender-open",
+ "_blender-assets": "folder-blender-open",
+ "__blender-assets__": "folder-blender-open",
+ "blender-files": "folder-blender-open",
+ ".blender-files": "folder-blender-open",
+ "_blender-files": "folder-blender-open",
+ "__blender-files__": "folder-blender-open",
+ "blender-project": "folder-blender-open",
+ ".blender-project": "folder-blender-open",
+ "_blender-project": "folder-blender-open",
+ "__blender-project__": "folder-blender-open",
+ "blender-models": "folder-blender-open",
+ ".blender-models": "folder-blender-open",
+ "_blender-models": "folder-blender-open",
+ "__blender-models__": "folder-blender-open"
+ },
+ "rootFolderNames": {},
+ "rootFolderNamesExpanded": {},
+ "fileExtensions": {
+ "60": "slint",
+ "htm": "html",
+ "xhtml": "html",
+ "html_vm": "html",
+ "asp": "html",
+ "jade": "pug",
+ "pug": "pug",
+ "md": "markdown",
+ "markdown": "markdown",
+ "rst": "markdown",
+ "blink": "blink",
+ "css": "css",
+ "scss": "sass",
+ "sass": "sass",
+ "less": "less",
+ "json": "json",
+ "jsonc": "json",
+ "tsbuildinfo": "json",
+ "json5": "json",
+ "jsonl": "json",
+ "ndjson": "json",
+ "hjson": "hjson",
+ "jinja": "jinja",
+ "jinja2": "jinja",
+ "j2": "jinja",
+ "jinja-html": "jinja",
+ "proto": "proto",
+ "sublime-project": "sublime",
+ "sublime-workspace": "sublime",
+ "slx": "simulink",
+ "tw": "twine",
+ "twee": "twine",
+ "yml.dist": "yaml",
+ "yaml.dist": "yaml",
+ "YAML-tmLanguage": "yaml",
+ "xml": "xml",
+ "plist": "xml",
+ "xsd": "xml",
+ "dtd": "xml",
+ "xsl": "xml",
+ "xslt": "xml",
+ "resx": "xml",
+ "iml": "xml",
+ "xquery": "xml",
+ "tmLanguage": "xml",
+ "manifest": "xml",
+ "project": "xml",
+ "xml.dist": "xml",
+ "xml.dist.sample": "xml",
+ "dmn": "xml",
+ "jrxml": "xml",
+ "xmp": "xml",
+ "toml": "toml",
+ "png": "image",
+ "jpeg": "image",
+ "jpg": "image",
+ "gif": "image",
+ "ico": "image",
+ "tif": "image",
+ "tiff": "image",
+ "ami": "image",
+ "apx": "image",
+ "avif": "image",
+ "bmp": "image",
+ "bpg": "image",
+ "brk": "image",
+ "cur": "image",
+ "dds": "image",
+ "exr": "image",
+ "fpx": "image",
+ "gbr": "image",
+ "img": "image",
+ "jbig2": "image",
+ "jb2": "image",
+ "jng": "image",
+ "jxr": "image",
+ "pgf": "image",
+ "pic": "image",
+ "raw": "image",
+ "webp": "image",
+ "eps": "image",
+ "afphoto": "image",
+ "ase": "image",
+ "aseprite": "image",
+ "clip": "image",
+ "cpt": "image",
+ "heif": "image",
+ "heic": "image",
+ "kra": "image",
+ "mdp": "image",
+ "ora": "image",
+ "pdn": "image",
+ "reb": "image",
+ "sai": "image",
+ "tga": "image",
+ "xcf": "image",
+ "jfif": "image",
+ "ppm": "image",
+ "pbm": "image",
+ "pgm": "image",
+ "pnm": "image",
+ "icns": "image",
+ "3fr": "image",
+ "ari": "image",
+ "arw": "image",
+ "bay": "image",
+ "braw": "image",
+ "crw": "image",
+ "cr2": "image",
+ "cr3": "image",
+ "cap": "image",
+ "data": "image",
+ "dcs": "image",
+ "dcr": "image",
+ "dng": "image",
+ "drf": "image",
+ "eip": "image",
+ "erf": "image",
+ "fff": "image",
+ "gpr": "image",
+ "iiq": "image",
+ "k25": "image",
+ "kdc": "image",
+ "mdc": "image",
+ "mef": "image",
+ "mos": "image",
+ "mrw": "image",
+ "nef": "image",
+ "nrw": "image",
+ "obm": "image",
+ "orf": "image",
+ "pef": "image",
+ "ptx": "image",
+ "pxn": "image",
+ "r3d": "image",
+ "raf": "image",
+ "rwl": "image",
+ "rw2": "image",
+ "rwz": "image",
+ "sr2": "image",
+ "srf": "image",
+ "srw": "image",
+ "x3f": "image",
+ "pal": "palette",
+ "gpl": "palette",
+ "act": "palette",
+ "esx": "javascript",
+ "mjs": "javascript",
+ "jsx": "react",
+ "tsx": "react_ts",
+ "routing.ts": "routing",
+ "routing.tsx": "routing",
+ "routing.js": "routing",
+ "routing.jsx": "routing",
+ "route.ts": "routing",
+ "route.tsx": "routing",
+ "route.js": "routing",
+ "route.jsx": "routing",
+ "routes.ts": "routing",
+ "routes.tsx": "routing",
+ "routes.js": "routing",
+ "routes.jsx": "routing",
+ "ini": "settings",
+ "dlc": "settings",
+ "config": "settings",
+ "conf": "settings",
+ "properties": "settings",
+ "prop": "settings",
+ "settings": "settings",
+ "option": "settings",
+ "props": "settings",
+ "prefs": "settings",
+ "sln.dotsettings": "settings",
+ "sln.dotsettings.user": "settings",
+ "cfg": "settings",
+ "cnf": "settings",
+ "tool-versions": "settings",
+ "d.ts": "typescript-def",
+ "d.cts": "typescript-def",
+ "d.mts": "typescript-def",
+ "d.ets": "typescript-def",
+ "mdoc": "markdoc",
+ "markdoc": "markdoc",
+ "markdoc.md": "markdoc",
+ "marko": "markojs",
+ "astro": "astro",
+ "pdf": "pdf",
+ "xlsx": "table",
+ "xlsm": "table",
+ "xls": "table",
+ "csv": "table",
+ "tsv": "table",
+ "psv": "table",
+ "ods": "table",
+ "vscodeignore": "vscode",
+ "vsixmanifest": "vscode",
+ "vsix": "vscode",
+ "code-workplace": "vscode",
+ "code-workspace": "vscode",
+ "code-profile": "vscode",
+ "code-snippets": "vscode",
+ "csproj": "visualstudio",
+ "ruleset": "visualstudio",
+ "sln": "visualstudio",
+ "slnx": "visualstudio",
+ "suo": "visualstudio",
+ "vb": "visualstudio",
+ "vbs": "visualstudio",
+ "vcxitems": "visualstudio",
+ "vcxitems.filters": "visualstudio",
+ "vcxproj": "visualstudio",
+ "vcxproj.filters": "visualstudio",
+ "pdb": "database",
+ "sql": "database",
+ "pks": "database",
+ "pkb": "database",
+ "accdb": "database",
+ "mdb": "database",
+ "sqlite": "database",
+ "sqlite3": "database",
+ "pgsql": "database",
+ "postgres": "database",
+ "plpgsql": "database",
+ "psql": "database",
+ "db": "database",
+ "db3": "database",
+ "dblite": "database",
+ "dblite3": "database",
+ "debugsymbols": "database",
+ "odb": "database",
+ "kql": "kusto",
+ "cs": "csharp",
+ "csx": "csharp",
+ "csharp": "csharp",
+ "qs": "qsharp",
+ "zip": "zip",
+ "z": "zip",
+ "tar": "zip",
+ "gz": "zip",
+ "xz": "zip",
+ "lz": "zip",
+ "liz": "zip",
+ "lzma": "zip",
+ "lzma2": "zip",
+ "lz4": "zip",
+ "lz5": "zip",
+ "lzh": "zip",
+ "lha": "zip",
+ "br": "zip",
+ "bz2": "zip",
+ "bzip2": "zip",
+ "gzip": "zip",
+ "brotli": "zip",
+ "7z": "zip",
+ "001": "zip",
+ "rar": "zip",
+ "far": "zip",
+ "tz": "zip",
+ "taz": "zip",
+ "tlz": "zip",
+ "txz": "zip",
+ "tgz": "zip",
+ "tpz": "zip",
+ "tbz": "zip",
+ "tbz2": "zip",
+ "zst": "zip",
+ "zstd": "zip",
+ "tzst": "zip",
+ "tzstd": "zip",
+ "cab": "zip",
+ "cpio": "zip",
+ "rpm": "zip",
+ "deb": "zip",
+ "arj": "zip",
+ "wim": "zip",
+ "swm": "zip",
+ "esd": "zip",
+ "fat": "zip",
+ "xar": "zip",
+ "ntfs": "zip",
+ "hfs": "zip",
+ "squashfs": "zip",
+ "apfs": "zip",
+ "vala": "vala",
+ "zig": "zig",
+ "zon": "zig",
+ "exe": "exe",
+ "msi": "exe",
+ "dat": "hex",
+ "bin": "hex",
+ "hex": "hex",
+ "java": "java",
+ "jsp": "java",
+ "jar": "jar",
+ "class": "javaclass",
+ "c3": "c3",
+ "c": "c",
+ "i": "c",
+ "mi": "c",
+ "h": "h",
+ "cc": "cpp",
+ "cpp": "cpp",
+ "cxx": "cpp",
+ "c++": "cpp",
+ "cp": "cpp",
+ "mii": "cpp",
+ "ii": "cpp",
+ "hh": "hpp",
+ "hpp": "hpp",
+ "hxx": "hpp",
+ "h++": "hpp",
+ "hp": "hpp",
+ "tcc": "hpp",
+ "inl": "hpp",
+ "rc": "rc",
+ "go": "go",
+ "py": "python",
+ "pyc": "python-misc",
+ "whl": "python-misc",
+ "egg": "python-misc",
+ "url": "url",
+ "sh": "console",
+ "ksh": "console",
+ "csh": "console",
+ "tcsh": "console",
+ "zsh": "console",
+ "bash": "console",
+ "bat": "console",
+ "cmd": "console",
+ "awk": "console",
+ "fish": "console",
+ "exp": "console",
+ "nu": "console",
+ "xsh": "console",
+ "ps1": "powershell",
+ "psm1": "powershell",
+ "psd1": "powershell",
+ "ps1xml": "powershell",
+ "psc1": "powershell",
+ "pssc": "powershell",
+ "excalidraw.json": "excalidraw",
+ "excalidraw.svg": "excalidraw",
+ "excalidraw.png": "excalidraw",
+ "gradle": "gradle",
+ "doc": "word",
+ "docx": "word",
+ "rtf": "word",
+ "odt": "word",
+ "cer": "certificate",
+ "cert": "certificate",
+ "crt": "certificate",
+ "pub": "key",
+ "key": "key",
+ "pem": "key",
+ "asc": "key",
+ "gpg": "key",
+ "passwd": "key",
+ "shasum": "key",
+ "sha256": "key",
+ "sha256sum": "key",
+ "sha256sums": "key",
+ "woff": "font",
+ "woff2": "font",
+ "ttf": "font",
+ "eot": "font",
+ "suit": "font",
+ "otf": "font",
+ "bmap": "font",
+ "fnt": "font",
+ "odttf": "font",
+ "ttc": "font",
+ "font": "font",
+ "fonts": "font",
+ "sui": "font",
+ "ntf": "font",
+ "mrf": "font",
+ "lib": "lib",
+ "a": "lib",
+ "bib": "bibliography",
+ "bst": "bibtex-style",
+ "dll": "dll",
+ "ilk": "dll",
+ "so": "dll",
+ "rb": "ruby",
+ "erb": "ruby",
+ "rbs": "ruby",
+ "fs": "fsharp",
+ "fsx": "fsharp",
+ "fsi": "fsharp",
+ "fsproj": "fsharp",
+ "swift": "swift",
+ "ino": "arduino",
+ "dockerignore": "docker",
+ "dockerfile": "docker",
+ "docker-compose.yml": "docker",
+ "docker-compose.yaml": "docker",
+ "containerignore": "docker",
+ "containerfile": "docker",
+ "compose.yaml": "docker",
+ "compose.yml": "docker",
+ "bbx": "bbx",
+ "cbx": "cbx",
+ "lbx": "lbx",
+ "tex": "tex",
+ "sty": "sty",
+ "ltx": "ltx",
+ "dtx": "dtx",
+ "pptx": "powerpoint",
+ "ppt": "powerpoint",
+ "pptm": "powerpoint",
+ "potx": "powerpoint",
+ "potm": "powerpoint",
+ "ppsx": "powerpoint",
+ "ppsm": "powerpoint",
+ "pps": "powerpoint",
+ "ppam": "powerpoint",
+ "ppa": "powerpoint",
+ "odp": "powerpoint",
+ "webm": "video",
+ "mkv": "video",
+ "flv": "video",
+ "vob": "video",
+ "ogv": "video",
+ "ogg": "video",
+ "gifv": "video",
+ "avi": "video",
+ "mov": "video",
+ "qt": "video",
+ "wmv": "video",
+ "yuv": "video",
+ "rm": "video",
+ "rmvb": "video",
+ "mp4": "video",
+ "m4v": "video",
+ "mpg": "video",
+ "mp2": "video",
+ "mpeg": "video",
+ "mpe": "video",
+ "mpv": "video",
+ "m2v": "video",
+ "vdi": "virtual",
+ "vbox": "virtual",
+ "vbox-prev": "virtual",
+ "ved": "vedic",
+ "veda": "vedic",
+ "vedic": "vedic",
+ "edb": "email",
+ "eml": "email",
+ "emlx": "email",
+ "ics": "email",
+ "mbox": "email",
+ "msg": "email",
+ "oft": "email",
+ "olm": "email",
+ "ost": "email",
+ "p7s": "email",
+ "pst": "email",
+ "rpmsg": "email",
+ "tnef": "email",
+ "8svx": "audio",
+ "aa": "audio",
+ "aac": "audio",
+ "aax": "audio",
+ "ac3": "audio",
+ "aif": "audio",
+ "aiff": "audio",
+ "alac": "audio",
+ "amr": "audio",
+ "ape": "audio",
+ "caf": "audio",
+ "cda": "audio",
+ "cdr": "audio",
+ "dss": "audio",
+ "ec3": "audio",
+ "efs": "audio",
+ "enc": "audio",
+ "flac": "audio",
+ "flp": "audio",
+ "gp": "audio",
+ "gsm": "audio",
+ "it": "audio",
+ "m3u": "audio",
+ "m3u8": "audio",
+ "m4a": "audio",
+ "m4b": "audio",
+ "m4p": "audio",
+ "m4r": "audio",
+ "mid": "audio",
+ "mka": "audio",
+ "mmf": "audio",
+ "mod": "audio",
+ "mp3": "audio",
+ "mpc": "audio",
+ "mscz": "audio",
+ "mtm": "audio",
+ "mui": "audio",
+ "musx": "audio",
+ "mxl": "audio",
+ "nsa": "audio",
+ "opus": "audio",
+ "pkf": "audio",
+ "qcp": "audio",
+ "ra": "audio",
+ "rf64": "audio",
+ "rip": "audio",
+ "sdt": "audio",
+ "sesx": "audio",
+ "sf2": "audio",
+ "stap": "audio",
+ "tg": "audio",
+ "voc": "audio",
+ "vqf": "audio",
+ "wav": "audio",
+ "weba": "audio",
+ "wfp": "audio",
+ "wma": "audio",
+ "wpl": "audio",
+ "wproj": "audio",
+ "wv": "audio",
+ "coffee": "coffee",
+ "cson": "coffee",
+ "iced": "coffee",
+ "txt": "document",
+ "lrc": "lyric",
+ "graphql": "graphql",
+ "gql": "graphql",
+ "rs": "rust",
+ "ron": "rust",
+ "raml": "raml",
+ "xaml": "xaml",
+ "hs": "haskell",
+ "lhs": "haskell",
+ "kt": "kotlin",
+ "kts": "kotlin",
+ "mist.js": "mist",
+ "mist.ts": "mist",
+ "mist.jsx": "mist",
+ "mist.tsx": "mist",
+ "otne": "otne",
+ "patch": "git",
+ "lua": "lua",
+ "clj": "clojure",
+ "cljs": "clojure",
+ "cljc": "clojure",
+ "groovy": "groovy",
+ "r": "r",
+ "rmd": "r",
+ "dart": "dart",
+ "freezed.dart": "dart_generated",
+ "g.dart": "dart_generated",
+ "as": "actionscript",
+ "mxml": "mxml",
+ "ahk": "autohotkey",
+ "swf": "flash",
+ "swc": "adobe-swc",
+ "swcrc": "swc",
+ "cmake": "cmake",
+ "asm": "assembly",
+ "a51": "assembly",
+ "inc": "assembly",
+ "nasm": "assembly",
+ "s": "assembly",
+ "ms": "assembly",
+ "agc": "assembly",
+ "ags": "assembly",
+ "aea": "assembly",
+ "argus": "assembly",
+ "mitigus": "assembly",
+ "binsource": "assembly",
+ "vue": "vue",
+ "ml": "ocaml",
+ "mli": "ocaml",
+ "cmx": "ocaml",
+ "odin": "odin",
+ "js.map": "javascript-map",
+ "mjs.map": "javascript-map",
+ "cjs.map": "javascript-map",
+ "css.map": "css-map",
+ "lock": "lock",
+ "hbs": "handlebars",
+ "mustache": "handlebars",
+ "pm": "perl",
+ "raku": "perl",
+ "hx": "haxe",
+ "spec.ts": "test-ts",
+ "spec.cts": "test-ts",
+ "spec.mts": "test-ts",
+ "cy.ts": "test-ts",
+ "e2e-spec.ts": "test-ts",
+ "e2e-spec.cts": "test-ts",
+ "e2e-spec.mts": "test-ts",
+ "test.ts": "test-ts",
+ "test.cts": "test-ts",
+ "test.mts": "test-ts",
+ "ts.snap": "test-ts",
+ "spec-d.ts": "test-ts",
+ "test-d.ts": "test-ts",
+ "spec.tsx": "test-jsx",
+ "test.tsx": "test-jsx",
+ "tsx.snap": "test-jsx",
+ "spec.jsx": "test-jsx",
+ "test.jsx": "test-jsx",
+ "jsx.snap": "test-jsx",
+ "cy.jsx": "test-jsx",
+ "cy.tsx": "test-jsx",
+ "spec-d.tsx": "test-jsx",
+ "test-d.tsx": "test-jsx",
+ "spec.js": "test-js",
+ "spec.cjs": "test-js",
+ "spec.mjs": "test-js",
+ "e2e-spec.js": "test-js",
+ "e2e-spec.cjs": "test-js",
+ "e2e-spec.mjs": "test-js",
+ "test.js": "test-js",
+ "test.cjs": "test-js",
+ "test.mjs": "test-js",
+ "js.snap": "test-js",
+ "cy.js": "test-js",
+ "module.ts": "angular",
+ "module.js": "angular",
+ "ng-template": "angular",
+ "component.ts": "angular-component",
+ "component.js": "angular-component",
+ "guard.ts": "angular-guard",
+ "guard.js": "angular-guard",
+ "service.ts": "angular-service",
+ "service.js": "angular-service",
+ "pipe.ts": "angular-pipe",
+ "pipe.js": "angular-pipe",
+ "filter.js": "angular-pipe",
+ "directive.ts": "angular-directive",
+ "directive.js": "angular-directive",
+ "resolver.ts": "angular-resolver",
+ "resolver.js": "angular-resolver",
+ "interceptor.ts": "angular-interceptor",
+ "interceptor.js": "angular-interceptor",
+ "pp": "puppet",
+ "ex": "elixir",
+ "exs": "elixir",
+ "eex": "elixir",
+ "leex": "elixir",
+ "heex": "elixir",
+ "ls": "livescript",
+ "erl": "erlang",
+ "twig": "twig",
+ "jl": "julia",
+ "elm": "elm",
+ "pure": "purescript",
+ "purs": "purescript",
+ "tpl": "smarty",
+ "styl": "stylus",
+ "re": "reason",
+ "rei": "reason",
+ "cmj": "bucklescript",
+ "merlin": "merlin",
+ "vhd": "verilog",
+ "vhdx": "verilog",
+ "sv": "verilog",
+ "svh": "verilog",
+ "vhdl": "verilog",
+ "nb": "mathematica",
+ "wl": "wolframlanguage",
+ "wls": "wolframlanguage",
+ "njk": "nunjucks",
+ "nunjucks": "nunjucks",
+ "robot": "robot",
+ "sol": "solidity",
+ "au3": "autoit",
+ "haml": "haml",
+ "yang": "yang",
+ "mjml": "mjml",
+ "tf": "terraform",
+ "tf.json": "terraform",
+ "tfvars": "terraform",
+ "tfstate": "terraform",
+ "tfbackend": "terraform",
+ "blade.php": "laravel",
+ "inky.php": "laravel",
+ "applescript": "applescript",
+ "ipa": "applescript",
+ "cake": "cake",
+ "feature": "cucumber",
+ "features": "cucumber",
+ "nim": "nim",
+ "nimble": "nim",
+ "apib": "apiblueprint",
+ "apiblueprint": "apiblueprint",
+ "riot": "riot",
+ "tag": "riot",
+ "vfl": "vfl",
+ "kl": "kl",
+ "pcss": "postcss",
+ "sss": "postcss",
+ "todo": "todo",
+ "cfml": "coldfusion",
+ "cfc": "coldfusion",
+ "lucee": "coldfusion",
+ "cfm": "coldfusion",
+ "cabal": "cabal",
+ "nix": "nix",
+ "slim": "slim",
+ "http": "http",
+ "rest": "http",
+ "rql": "restql",
+ "restql": "restql",
+ "kv": "kivy",
+ "graphcool": "graphcool",
+ "sbt": "sbt",
+ "apk": "android",
+ "smali": "android",
+ "dex": "android",
+ "env": "tune",
+ "gitlab-ci.yml": "gitlab",
+ "jenkinsfile": "jenkins",
+ "jenkins": "jenkins",
+ "fig": "figma",
+ "huff": "huff",
+ "cr": "crystal",
+ "ecr": "crystal",
+ "drone.yml": "drone",
+ "cu": "cuda",
+ "cuh": "cuda",
+ "log": "log",
+ "def": "dotjs",
+ "dot": "dotjs",
+ "jst": "dotjs",
+ "ejs": "ejs",
+ ".wakatime-project": "wakatime",
+ "pde": "processing",
+ "stories.js": "storybook",
+ "stories.jsx": "storybook",
+ "stories.mdx": "storybook",
+ "story.js": "storybook",
+ "story.jsx": "storybook",
+ "stories.ts": "storybook",
+ "stories.tsx": "storybook",
+ "story.ts": "storybook",
+ "story.tsx": "storybook",
+ "stories.svelte": "storybook",
+ "story.mdx": "storybook",
+ "wpy": "wepy",
+ "hcl": "hcl",
+ "san": "san",
+ "quokka.js": "quokka",
+ "quokka.ts": "quokka",
+ "quokka.jsx": "quokka",
+ "quokka.tsx": "quokka",
+ "djt": "django",
+ "red": "red",
+ "mk": "makefile",
+ "fxp": "foxpro",
+ "prg": "foxpro",
+ "pot": "i18n",
+ "po": "i18n",
+ "mo": "i18n",
+ "lang": "i18n",
+ "xlf": "i18n",
+ "wat": "webassembly",
+ "wasm": "webassembly",
+ "ipynb": "jupyter",
+ "d": "d",
+ "mdx": "mdx",
+ "svx": "mdsvex",
+ "bal": "ballerina",
+ "balx": "ballerina",
+ "rkt": "racket",
+ "bzl": "bazel",
+ "bazel": "bazel",
+ "mint": "mint",
+ "vm": "velocity",
+ "fhtml": "velocity",
+ "vtl": "velocity",
+ "gd": "godot",
+ "godot": "godot-assets",
+ "tres": "godot-assets",
+ "tscn": "godot-assets",
+ "gdns": "godot-assets",
+ "gdnlib": "godot-assets",
+ "gdshader": "godot-assets",
+ "gdshaderinc": "godot-assets",
+ "gdextension": "godot-assets",
+ "azure-pipelines.yml": "azure-pipelines",
+ "azure-pipelines.yaml": "azure-pipelines",
+ "azure-pipelines-main.yml": "azure-pipelines",
+ "azure-pipelines-main.yaml": "azure-pipelines",
+ "azcli": "azure",
+ "vagrantfile": "vagrant",
+ "prisma": "prisma",
+ "cshtml": "razor",
+ "vbhtml": "razor",
+ "abc": "abc",
+ "ad": "asciidoc",
+ "adoc": "asciidoc",
+ "asciidoc": "asciidoc",
+ "edge": "edge",
+ "ss": "scheme",
+ "scm": "scheme",
+ "lisp": "lisp",
+ "lsp": "lisp",
+ "cl": "lisp",
+ "fast": "lisp",
+ "stl": "3d",
+ "stp": "3d",
+ "step": "3d",
+ "obj": "3d",
+ "o": "3d",
+ "ac": "3d",
+ "dxf": "3d",
+ "fbx": "3d",
+ "mesh": "3d",
+ "mqo": "3d",
+ "pmd": "3d",
+ "pmx": "3d",
+ "skp": "3d",
+ "vac": "3d",
+ "vdp": "3d",
+ "vox": "3d",
+ "gltf": "3d",
+ "glb": "3d",
+ "3ds": "3d",
+ "dae": "3d",
+ "ply": "3d",
+ "wrl": "3d",
+ "usd": "3d",
+ "usdz": "3d",
+ "svg": "svg",
+ "ai": "adobe-illustrator",
+ "ait": "adobe-illustrator",
+ "psd": "adobe-photoshop",
+ "psb": "adobe-photoshop",
+ "psdt": "adobe-photoshop",
+ "svelte": "svelte",
+ "svelte.js": "svelte_js",
+ "svelte.ts": "svelte_ts",
+ "vimrc": "vim",
+ "gvimrc": "vim",
+ "exrc": "vim",
+ "vim": "vim",
+ "viminfo": "vim",
+ "moon": "moonscript",
+ "prw": "advpl",
+ "prx": "advpl",
+ "ptm": "advpl-ptm",
+ "tlpp": "advpl-tlpp",
+ "ch": "advpl-include",
+ "iso": "disc",
+ "vmdk": "disc",
+ "hdd": "disc",
+ "qcow": "disc",
+ "qcow2": "disc",
+ "qed": "disc",
+ "dmg": "disc",
+ "f": "fortran",
+ "f77": "fortran",
+ "f90": "fortran",
+ "f95": "fortran",
+ "f03": "fortran",
+ "f08": "fortran",
+ "tcl": "tcl",
+ "do": "tcl",
+ "liquid": "liquid",
+ "p": "prolog",
+ "pro": "prolog",
+ "pl": "prolog",
+ "coco": "coconut",
+ "sketch": "sketch",
+ "pwn": "pawn",
+ "amx": "pawn",
+ "4th": "forth",
+ "fth": "forth",
+ "frt": "forth",
+ "iuml": "uml",
+ "pu": "uml",
+ "puml": "uml",
+ "plantuml": "uml",
+ "wsd": "uml",
+ "wrap": "meson",
+ "dhall": "dhall",
+ "dhallb": "dhall",
+ "sml": "sml",
+ "mlton": "sml",
+ "mlb": "sml",
+ "sig": "sml",
+ "fun": "sml",
+ "cm": "sml",
+ "lex": "sml",
+ "use": "sml",
+ "grm": "sml",
+ "opam": "opam",
+ "imba": "imba",
+ "drawio": "drawio",
+ "dio": "drawio",
+ "pas": "pascal",
+ "unity": "unity",
+ "unitypackage": "unity",
+ "sas": "sas",
+ "sas7bdat": "sas",
+ "sashdat": "sas",
+ "astore": "sas",
+ "ast": "sas",
+ "sast": "sas",
+ "nupkg": "nuget",
+ "command": "command",
+ "dsc": "denizenscript",
+ "code-search": "search",
+ "nginx": "nginx",
+ "nginxconf": "nginx",
+ "nginxconfig": "nginx",
+ "mcfunction": "minecraft",
+ "mcmeta": "minecraft",
+ "mcr": "minecraft",
+ "mca": "minecraft",
+ "mcgame": "minecraft",
+ "mclevel": "minecraft",
+ "mcworld": "minecraft",
+ "mine": "minecraft",
+ "mus": "minecraft",
+ "mcstructure": "minecraft",
+ "mcpack": "minecraft",
+ "mcaddon": "minecraft",
+ "mctemplate": "minecraft",
+ "mcproject": "minecraft",
+ "res": "rescript",
+ "resi": "rescript-interface",
+ "duc": "duc",
+ "b": "brainfuck",
+ "bf": "brainfuck",
+ "bicep": "bicep",
+ "cob": "cobol",
+ "cbl": "cobol",
+ "gr": "grain",
+ "lol": "lolcode",
+ "idr": "idris",
+ "ibc": "idris",
+ "pipeline": "pipeline",
+ "rego": "opa",
+ "windi": "windicss",
+ "scala": "scala",
+ "sc": "scala",
+ "ly": "lilypond",
+ "v": "vlang",
+ "pgn": "chess",
+ "fen": "chess",
+ "gmi": "gemini",
+ "gemini": "gemini",
+ "tsconfig.json": "tsconfig",
+ "tauri": "tauri",
+ "jsconfig.json": "jsconfig",
+ "ada": "ada",
+ "adb": "ada",
+ "ads": "ada",
+ "ali": "ada",
+ "horusec-config.json": "horusec",
+ "pdm.lock": "pdm",
+ "pdm.toml": "pdm",
+ "coarc": "coala",
+ "coafile": "coala",
+ "bubble": "dinophp",
+ "html.bubble": "dinophp",
+ "php.bubble": "dinophp",
+ "tl": "teal",
+ "template": "template",
+ "glsl": "shader",
+ "vert": "shader",
+ "tesc": "shader",
+ "tese": "shader",
+ "geom": "shader",
+ "frag": "shader",
+ "comp": "shader",
+ "vert.glsl": "shader",
+ "tesc.glsl": "shader",
+ "tese.glsl": "shader",
+ "geom.glsl": "shader",
+ "frag.glsl": "shader",
+ "comp.glsl": "shader",
+ "vertex.glsl": "shader",
+ "geometry.glsl": "shader",
+ "fragment.glsl": "shader",
+ "compute.glsl": "shader",
+ "ts.glsl": "shader",
+ "gs.glsl": "shader",
+ "vs.glsl": "shader",
+ "fs.glsl": "shader",
+ "shader": "shader",
+ "vertexshader": "shader",
+ "fragmentshader": "shader",
+ "geometryshader": "shader",
+ "computeshader": "shader",
+ "hlsl": "shader",
+ "pixel.hlsl": "shader",
+ "geometry.hlsl": "shader",
+ "compute.hlsl": "shader",
+ "tessellation.hlsl": "shader",
+ "px.hlsl": "shader",
+ "geom.hlsl": "shader",
+ "comp.hlsl": "shader",
+ "tess.hlsl": "shader",
+ "wgsl": "shader",
+ "sy": "siyuan",
+ "ndst.yml": "ndst",
+ "ndst.yaml": "ndst",
+ "ndst.json": "ndst",
+ "tobi": "tobi",
+ "gleam": "gleam",
+ "steadybit.yml": "steadybit",
+ "steadybit.yaml": "steadybit",
+ "capnp": "capnp",
+ "tree": "tree",
+ "cdc": "cadence",
+ "openapi.json": "openapi",
+ "openapi.yml": "openapi",
+ "openapi.yaml": "openapi",
+ "swagger.json": "swagger",
+ "swagger.yml": "swagger",
+ "swagger.yaml": "swagger",
+ "g4": "antlr",
+ "st.css": "stylable",
+ "pine": "pinejs",
+ "taskfile.yml": "taskfile",
+ "taskfile.yaml": "taskfile",
+ "gml": "gamemaker",
+ "yy": "gamemaker",
+ "yyp": "gamemaker",
+ "yyz": "gamemaker",
+ "tldr": "tldraw",
+ "typ": "typst",
+ "mmd": "mermaid",
+ "mermaid": "mermaid",
+ "mojo": "mojo",
+ "🔥": "mojo",
+ "rbxl": "roblox",
+ "rbxlx": "roblox",
+ "rbxm": "roblox",
+ "rbxmx": "roblox",
+ "luau": "luau",
+ "rbxmk.lua": "rbxmk",
+ "rbxmk.luau": "rbxmk",
+ "spwn": "spwn",
+ "templ": "templ",
+ "crx": "chrome",
+ "stan": "stan",
+ "abap": "abap",
+ "acds": "abap",
+ "asddls": "abap",
+ "lottie": "lottie",
+ "gs": "apps-script",
+ "garden.yml": "garden",
+ "garden.yaml": "garden",
+ "pkl": "pkl",
+ "k": "kcl",
+ "sigstore.json": "verified",
+ "bru": "bruno",
+ "cairo": "cairo",
+ "alloy": "grafana-alloy",
+ "ftl": "freemarker",
+ "ц": "tsil",
+ "tape": "tape",
+ "hurl": "hurl",
+ "cds": "cds",
+ "slint": "slint",
+ "sw": "sway",
+ "zeabur": "zeabur",
+ "bench.ts": "bench-ts",
+ "bench.cts": "bench-ts",
+ "bench.mts": "bench-ts",
+ "bench.jsx": "bench-jsx",
+ "bench.tsx": "bench-jsx",
+ "bench.js": "bench-js",
+ "bench.cjs": "bench-js",
+ "bench.mjs": "bench-js",
+ "controller.js": "controller",
+ "controller.ts": "controller",
+ ".ncurc.json": "dependencies-update",
+ ".ncurc.yml": "dependencies-update",
+ ".ncurc.js": "dependencies-update",
+ "srt": "subtitles",
+ "ssa": "subtitles",
+ "ttml": "subtitles",
+ "sbv": "subtitles",
+ "dfxp": "subtitles",
+ "vtt": "subtitles",
+ "sub": "subtitles",
+ "ass": "subtitles",
+ "beancount": "beancount",
+ "bean": "beancount",
+ "epub": "epub",
+ "reg": "regedit",
+ "gnu": "gnuplot",
+ "smk": "snakemake",
+ "snakemake": "snakemake",
+ "cpn": "coloredpetrinets",
+ "pnml": "coloredpetrinets",
+ "pt": "pytorch",
+ "pth": "pytorch",
+ "pwf": "pytorch",
+ "blend": "blender",
+ "blend1": "blender",
+ "blend2": "blender",
+ "yaml-tmlanguage": "yaml",
+ "tmlanguage": "xml",
+ "cljx": "clojure",
+ "clojure": "clojure",
+ "edn": "clojure",
+ "cppm": "cpp",
+ "ccm": "cpp",
+ "cxxm": "cpp",
+ "c++m": "cpp",
+ "ipp": "cpp",
+ "ixx": "cpp",
+ "tpp": "cpp",
+ "txx": "cpp",
+ "hpp.in": "cpp",
+ "h.in": "cpp",
+ "diff": "diff",
+ "rej": "diff",
+ "fsscript": "fsharp",
+ "gitignore_global": "ignore",
+ "gitignore": "ignore",
+ "git-blame-ignore-revs": "ignore",
+ "gvy": "groovy",
+ "nf": "groovy",
+ "handlebars": "handlebars",
+ "hjs": "handlebars",
+ "hlsli": "hlsl",
+ "fx": "hlsl",
+ "fxh": "hlsl",
+ "vsh": "hlsl",
+ "psh": "hlsl",
+ "cginc": "hlsl",
+ "compute": "hlsl",
+ "html": "html",
+ "shtml": "html",
+ "xht": "html",
+ "aspx": "html",
+ "jshtm": "html",
+ "volt": "html",
+ "rhtml": "html",
+ "directory": "properties",
+ "gitattributes": "properties",
+ "gitconfig": "properties",
+ "gitmodules": "properties",
+ "editorconfig": "properties",
+ "repo": "properties",
+ "jav": "java",
+ "js": "javascript",
+ "es6": "javascript",
+ "cjs": "javascript",
+ "pac": "javascript",
+ "bowerrc": "json",
+ "jscsrc": "json",
+ "webmanifest": "json",
+ "ts.map": "json",
+ "har": "json",
+ "jslintrc": "json",
+ "jsonld": "json",
+ "geojson": "json",
+ "vuerc": "json",
+ "eslintrc": "jsonc",
+ "eslintrc.json": "jsonc",
+ "jsfmtrc": "jsonc",
+ "jshintrc": "jsonc",
+ "hintrc": "jsonc",
+ "babelrc": "jsonc",
+ "jmd": "juliamarkdown",
+ "cls": "tex",
+ "ctx": "latex",
+ "mak": "makefile",
+ "mkd": "markdown",
+ "mdwn": "markdown",
+ "mdown": "markdown",
+ "markdn": "markdown",
+ "mdtxt": "markdown",
+ "mdtext": "markdown",
+ "workbook": "markdown",
+ "npmignore": "ignore",
+ "npmrc": "properties",
+ "m": "objective-c",
+ "mm": "objective-cpp",
+ "pod": "perl",
+ "t": "perl",
+ "psgi": "perl",
+ "rakumod": "raku",
+ "rakutest": "raku",
+ "rakudoc": "raku",
+ "nqp": "raku",
+ "p6": "raku",
+ "pl6": "raku",
+ "pm6": "raku",
+ "php": "php",
+ "php4": "php",
+ "php5": "php",
+ "phtml": "php",
+ "ctp": "php",
+ "psrc": "powershell",
+ "rpy": "python",
+ "pyw": "python",
+ "cpy": "python",
+ "gyp": "python",
+ "gypi": "python",
+ "pyi": "python",
+ "ipy": "python",
+ "pyt": "python",
+ "rhistory": "r",
+ "rprofile": "r",
+ "rt": "r",
+ "razor": "razor",
+ "rbx": "ruby",
+ "rjs": "ruby",
+ "gemspec": "ruby",
+ "rake": "ruby",
+ "ru": "ruby",
+ "podspec": "ruby",
+ "rbi": "ruby",
+ "bashrc": "shellscript",
+ "bash_aliases": "shellscript",
+ "bash_profile": "shellscript",
+ "bash_login": "shellscript",
+ "ebuild": "shellscript",
+ "eclass": "shellscript",
+ "profile": "shellscript",
+ "bash_logout": "shellscript",
+ "xprofile": "shellscript",
+ "xsession": "shellscript",
+ "xsessionrc": "shellscript",
+ "zshrc": "shellscript",
+ "zprofile": "shellscript",
+ "zlogin": "shellscript",
+ "zlogout": "shellscript",
+ "zshenv": "shellscript",
+ "zsh-theme": "shellscript",
+ "cshrc": "shellscript",
+ "tcshrc": "shellscript",
+ "yashrc": "shellscript",
+ "yash_profile": "shellscript",
+ "dsql": "sql",
+ "ts": "typescript",
+ "cts": "typescript",
+ "mts": "typescript",
+ "brs": "vb",
+ "bas": "vb",
+ "vba": "vb",
+ "ascx": "xml",
+ "atom": "xml",
+ "axml": "xml",
+ "axaml": "xml",
+ "bpmn": "xml",
+ "csl": "xml",
+ "csproj.user": "xml",
+ "dita": "xml",
+ "ditamap": "xml",
+ "ent": "xml",
+ "dtml": "xml",
+ "fxml": "xml",
+ "isml": "xml",
+ "jmx": "xml",
+ "launch": "xml",
+ "menu": "xml",
+ "nuspec": "xml",
+ "opml": "xml",
+ "owl": "xml",
+ "proj": "xml",
+ "publishsettings": "xml",
+ "pubxml": "xml",
+ "pubxml.user": "xml",
+ "rdf": "xml",
+ "rng": "xml",
+ "rss": "xml",
+ "shproj": "xml",
+ "storyboard": "xml",
+ "targets": "xml",
+ "tld": "xml",
+ "tmx": "xml",
+ "vbproj": "xml",
+ "vbproj.user": "xml",
+ "wsdl": "xml",
+ "wxi": "xml",
+ "wxl": "xml",
+ "wxs": "xml",
+ "xbl": "xml",
+ "xib": "xml",
+ "xliff": "xml",
+ "xpdl": "xml",
+ "xul": "xml",
+ "xoml": "xml",
+ "yaml": "yaml",
+ "yml": "yaml",
+ "eyaml": "yaml",
+ "eyml": "yaml",
+ "cff": "yaml",
+ "yaml-tmpreferences": "yaml",
+ "yaml-tmtheme": "yaml",
+ "winget": "yaml"
+ },
+ "fileNames": {
+ ".pug-lintrc": "pug",
+ ".pug-lintrc.js": "pug",
+ ".pug-lintrc.json": "pug",
+ "justfile": "just",
+ ".justfile": "just",
+ ".jscsrc": "json",
+ ".jshintrc": "json",
+ "composer.lock": "json",
+ ".jsbeautifyrc": "json",
+ ".esformatter": "json",
+ "cdp.pid": "json",
+ ".lintstagedrc": "json",
+ ".whitesource": "json",
+ "playwright.config.js": "playwright",
+ "playwright.config.mjs": "playwright",
+ "playwright.config.ts": "playwright",
+ "playwright.config.base.js": "playwright",
+ "playwright.config.base.mjs": "playwright",
+ "playwright.config.base.ts": "playwright",
+ "playwright-ct.config.js": "playwright",
+ "playwright-ct.config.mjs": "playwright",
+ "playwright-ct.config.ts": "playwright",
+ ".htaccess": "xml",
+ ".release-it.json": "rocket",
+ ".release-it.ts": "rocket",
+ ".release-it.js": "rocket",
+ ".release-it.cjs": "rocket",
+ ".release-it.yaml": "rocket",
+ ".release-it.yml": "rocket",
+ ".release-it.toml": "rocket",
+ "release.toml": "rocket",
+ "release-plz.toml": "rocket",
+ ".release-plz.toml": "rocket",
+ "router.js": "routing",
+ "router.jsx": "routing",
+ "router.ts": "routing",
+ "router.tsx": "routing",
+ "route.js": "routing",
+ "route.jsx": "routing",
+ "route.ts": "routing",
+ "route.tsx": "routing",
+ "routes.js": "routing",
+ "routes.jsx": "routing",
+ "routes.ts": "routing",
+ "routes.tsx": "routing",
+ ".jshintignore": "settings",
+ ".buildignore": "settings",
+ ".mrconfig": "settings",
+ ".yardopts": "settings",
+ "manifest.mf": "settings",
+ ".clang-format": "settings",
+ ".clang-format-ignore": "settings",
+ ".clang-tidy": "settings",
+ ".conf": "settings",
+ "compile_flags.txt": "settings",
+ "markdoc.config.js": "markdoc-config",
+ "markdoc.config.mjs": "markdoc-config",
+ "markdoc.config.cjs": "markdoc-config",
+ "markdoc.config.ts": "markdoc-config",
+ "markdoc.config.mts": "markdoc-config",
+ "markdoc.config.cts": "markdoc-config",
+ "astro.config.js": "astro-config",
+ "astro.config.mjs": "astro-config",
+ "astro.config.cjs": "astro-config",
+ "astro.config.ts": "astro-config",
+ "astro.config.cts": "astro-config",
+ "astro.config.mts": "astro-config",
+ "go.mod": "go-mod",
+ "go.sum": "go-mod",
+ "go.work": "go-mod",
+ "go.work.sum": "go-mod",
+ "requirements.txt": "python-misc",
+ "pipfile": "python-misc",
+ ".python-version": "python-misc",
+ "manifest.in": "python-misc",
+ "pylintrc": "python-misc",
+ ".pylintrc": "python-misc",
+ "pyproject.toml": "python-misc",
+ "py.typed": "python-misc",
+ ".coveragerc": "python-misc",
+ ".coverage": "python-misc",
+ ".scrapy": "python-misc",
+ "celerybeat-schedule": "python-misc",
+ "celerybeat.pid": "python-misc",
+ "ruff.toml": "ruff",
+ ".ruff.toml": "ruff",
+ "uv.toml": "uv",
+ ".uv.toml": "uv",
+ "sconstruct": "scons",
+ "sconscript": "scons",
+ "scsub": "scons",
+ "commit-msg": "console",
+ "pre-commit": "console",
+ "pre-push": "console",
+ "post-merge": "console",
+ "excalidraw.json": "excalidraw",
+ "excalidraw.svg": "excalidraw",
+ "excalidraw.png": "excalidraw",
+ "gradle.properties": "gradle",
+ "gradlew": "gradle",
+ "gradle-wrapper.properties": "gradle",
+ "copying": "certificate",
+ "copying.md": "certificate",
+ "copying.rst": "certificate",
+ "copying.txt": "certificate",
+ "copyright": "certificate",
+ "copyright.md": "certificate",
+ "copyright.rst": "certificate",
+ "copyright.txt": "certificate",
+ "license": "certificate",
+ "license-agpl": "certificate",
+ "license-apache": "certificate",
+ "license-bsd": "certificate",
+ "license-mit": "certificate",
+ "license-gpl": "certificate",
+ "license-lgpl": "certificate",
+ "license.md": "certificate",
+ "license.rst": "certificate",
+ "license.txt": "certificate",
+ "licence": "certificate",
+ "licence-agpl": "certificate",
+ "licence-apache": "certificate",
+ "licence-bsd": "certificate",
+ "licence-mit": "certificate",
+ "licence-gpl": "certificate",
+ "licence-lgpl": "certificate",
+ "licence.md": "certificate",
+ "licence.rst": "certificate",
+ "licence.txt": "certificate",
+ "unlicense": "certificate",
+ "unlicense.txt": "certificate",
+ ".htpasswd": "key",
+ "sha256sums": "key",
+ ".secrets": "key",
+ "keystatic.config.tsx": "keystatic",
+ "keystatic.config.ts": "keystatic",
+ "keystatic.config.jsx": "keystatic",
+ "keystatic.config.js": "keystatic",
+ ".ruby-version": "ruby",
+ "gemfile": "gemfile",
+ ".rubocop.yml": "rubocop",
+ ".rubocop-todo.yml": "rubocop",
+ ".rubocop_todo.yml": "rubocop",
+ ".rspec": "rspec",
+ "dockerfile": "docker",
+ "dockerfile.prod": "docker",
+ "dockerfile.production": "docker",
+ "dockerfile.alpha": "docker",
+ "dockerfile.beta": "docker",
+ "dockerfile.stage": "docker",
+ "dockerfile.staging": "docker",
+ "dockerfile.dev": "docker",
+ "dockerfile.development": "docker",
+ "dockerfile.local": "docker",
+ "dockerfile.test": "docker",
+ "dockerfile.testing": "docker",
+ "dockerfile.ci": "docker",
+ "dockerfile.web": "docker",
+ "dockerfile.windows": "docker",
+ "dockerfile.worker": "docker",
+ "docker-compose.yml": "docker",
+ "docker-compose.override.yml": "docker",
+ "docker-compose.prod.yml": "docker",
+ "docker-compose.production.yml": "docker",
+ "docker-compose.alpha.yml": "docker",
+ "docker-compose.beta.yml": "docker",
+ "docker-compose.stage.yml": "docker",
+ "docker-compose.staging.yml": "docker",
+ "docker-compose.dev.yml": "docker",
+ "docker-compose.development.yml": "docker",
+ "docker-compose.local.yml": "docker",
+ "docker-compose.test.yml": "docker",
+ "docker-compose.testing.yml": "docker",
+ "docker-compose.ci.yml": "docker",
+ "docker-compose.web.yml": "docker",
+ "docker-compose.worker.yml": "docker",
+ "docker-compose.yaml": "docker",
+ "docker-compose.override.yaml": "docker",
+ "docker-compose.prod.yaml": "docker",
+ "docker-compose.production.yaml": "docker",
+ "docker-compose.alpha.yaml": "docker",
+ "docker-compose.beta.yaml": "docker",
+ "docker-compose.stage.yaml": "docker",
+ "docker-compose.staging.yaml": "docker",
+ "docker-compose.dev.yaml": "docker",
+ "docker-compose.development.yaml": "docker",
+ "docker-compose.local.yaml": "docker",
+ "docker-compose.test.yaml": "docker",
+ "docker-compose.testing.yaml": "docker",
+ "docker-compose.ci.yaml": "docker",
+ "docker-compose.web.yaml": "docker",
+ "docker-compose.worker.yaml": "docker",
+ "containerfile": "docker",
+ "containerfile.prod": "docker",
+ "containerfile.production": "docker",
+ "containerfile.alpha": "docker",
+ "containerfile.beta": "docker",
+ "containerfile.stage": "docker",
+ "containerfile.staging": "docker",
+ "containerfile.dev": "docker",
+ "containerfile.development": "docker",
+ "containerfile.local": "docker",
+ "containerfile.test": "docker",
+ "containerfile.testing": "docker",
+ "containerfile.ci": "docker",
+ "containerfile.web": "docker",
+ "containerfile.worker": "docker",
+ "compose.yaml": "docker",
+ "compose.override.yaml": "docker",
+ "compose.prod.yaml": "docker",
+ "compose.production.yaml": "docker",
+ "compose.alpha.yaml": "docker",
+ "compose.beta.yaml": "docker",
+ "compose.stage.yaml": "docker",
+ "compose.staging.yaml": "docker",
+ "compose.dev.yaml": "docker",
+ "compose.development.yaml": "docker",
+ "compose.local.yaml": "docker",
+ "compose.test.yaml": "docker",
+ "compose.testing.yaml": "docker",
+ "compose.ci.yaml": "docker",
+ "compose.web.yaml": "docker",
+ "compose.worker.yaml": "docker",
+ "compose.yml": "docker",
+ "compose.override.yml": "docker",
+ "compose.prod.yml": "docker",
+ "compose.production.yml": "docker",
+ "compose.alpha.yml": "docker",
+ "compose.beta.yml": "docker",
+ "compose.stage.yml": "docker",
+ "compose.staging.yml": "docker",
+ "compose.dev.yml": "docker",
+ "compose.development.yml": "docker",
+ "compose.local.yml": "docker",
+ "compose.test.yml": "docker",
+ "compose.testing.yml": "docker",
+ "compose.ci.yml": "docker",
+ "compose.web.yml": "docker",
+ "compose.worker.yml": "docker",
+ ".latexmkrc": "latexmk",
+ "latexmkrc": "latexmk",
+ ".mailmap": "email",
+ ".graphqlrc": "graphql",
+ ".graphqlrc.json": "graphql",
+ ".graphqlrc.jsonc": "graphql",
+ ".graphqlrc.json5": "graphql",
+ ".graphqlrc.yaml": "graphql",
+ ".graphqlrc.yml": "graphql",
+ ".graphqlrc.toml": "graphql",
+ ".graphqlrc.js": "graphql",
+ ".graphqlrc.mjs": "graphql",
+ ".graphqlrc.cjs": "graphql",
+ ".graphqlrc.ts": "graphql",
+ ".graphqlrc.mts": "graphql",
+ ".graphqlrc.cts": "graphql",
+ ".config/graphqlrc": "graphql",
+ ".config/graphqlrc.json": "graphql",
+ ".config/graphqlrc.jsonc": "graphql",
+ ".config/graphqlrc.json5": "graphql",
+ ".config/graphqlrc.yaml": "graphql",
+ ".config/graphqlrc.yml": "graphql",
+ ".config/graphqlrc.toml": "graphql",
+ ".config/graphqlrc.js": "graphql",
+ ".config/graphqlrc.mjs": "graphql",
+ ".config/graphqlrc.cjs": "graphql",
+ ".config/graphqlrc.ts": "graphql",
+ ".config/graphqlrc.mts": "graphql",
+ ".config/graphqlrc.cts": "graphql",
+ "graphql.config.json": "graphql",
+ "graphql.config.jsonc": "graphql",
+ "graphql.config.json5": "graphql",
+ "graphql.config.yaml": "graphql",
+ "graphql.config.yml": "graphql",
+ "graphql.config.toml": "graphql",
+ "graphql.config.js": "graphql",
+ "graphql.config.mjs": "graphql",
+ "graphql.config.cjs": "graphql",
+ "graphql.config.ts": "graphql",
+ "graphql.config.mts": "graphql",
+ "graphql.config.cts": "graphql",
+ ".graphqlconfig": "graphql",
+ ".git": "git",
+ ".gitignore": "git",
+ ".gitmessage": "git",
+ ".gitignore-global": "git",
+ ".gitignore_global": "git",
+ ".gitattributes": "git",
+ ".gitattributes-global": "git",
+ ".gitattributes_global": "git",
+ ".gitconfig": "git",
+ ".gitmodules": "git",
+ ".gitkeep": "git",
+ ".keep": "git",
+ ".gitpreserve": "git",
+ ".gitinclude": "git",
+ ".git-blame-ignore": "git",
+ ".git-blame-ignore-revs": "git",
+ ".git-for-windows-updater": "git",
+ "git-history": "git",
+ ".luacheckrc": "lua",
+ ".Rhistory": "r",
+ ".pubignore": "dart",
+ "cmakelists.txt": "cmake",
+ "cmakecache.txt": "cmake",
+ "CMakePresets.json": "cmake",
+ "semgrep.yml": "semgrep",
+ ".semgrepignore": "semgrep",
+ "vue.config.js": "vue-config",
+ "vue.config.ts": "vue-config",
+ "vetur.config.js": "vue-config",
+ "vetur.config.ts": "vue-config",
+ "volar.config.js": "vue-config",
+ "nuxt.config.js": "nuxt",
+ "nuxt.config.ts": "nuxt",
+ ".nuxtignore": "nuxt",
+ ".nuxtrc": "nuxt",
+ "harmonix.config.js": "harmonix",
+ "harmonix.config.ts": "harmonix",
+ "security.md": "lock",
+ "security.txt": "lock",
+ "security": "lock",
+ "angular-cli.json": "angular",
+ ".angular-cli.json": "angular",
+ "angular.json": "angular",
+ "ng-package.json": "angular",
+ ".mjmlconfig": "mjml",
+ "vercel.json": "vercel",
+ ".vercelignore": "vercel",
+ "now.json": "vercel",
+ ".nowignore": "vercel",
+ "liara.json": "liara",
+ ".liaraignore": "liara",
+ "verdaccio.yml": "verdaccio",
+ "payload.config.js": "payload",
+ "payload.config.mjs": "payload",
+ "payload.config.ts": "payload",
+ "payload.config.mts": "payload",
+ "next.config.js": "next",
+ "next.config.mjs": "next",
+ "next.config.ts": "next",
+ "next.config.mts": "next",
+ ".remarkrc": "remark",
+ ".remarkrc.cjs": "remark",
+ ".remarkrc.js": "remark",
+ ".remarkrc.json": "remark",
+ ".remarkrc.mjs": "remark",
+ ".remarkrc.yaml": "remark",
+ ".remarkrc.yml": "remark",
+ ".remarkignore": "remark",
+ "remix.config.js": "remix",
+ "remix.config.ts": "remix",
+ "artisan": "laravel",
+ ".vfl": "vfl",
+ ".kl": "kl",
+ ".postcssrc": "postcss",
+ ".postcssrc.json": "postcss",
+ ".postcssrc.jsonc": "postcss",
+ ".postcssrc.json5": "postcss",
+ ".postcssrc.yaml": "postcss",
+ ".postcssrc.yml": "postcss",
+ ".postcssrc.toml": "postcss",
+ ".postcssrc.js": "postcss",
+ ".postcssrc.mjs": "postcss",
+ ".postcssrc.cjs": "postcss",
+ ".postcssrc.ts": "postcss",
+ ".postcssrc.mts": "postcss",
+ ".postcssrc.cts": "postcss",
+ ".config/postcssrc": "postcss",
+ ".config/postcssrc.json": "postcss",
+ ".config/postcssrc.jsonc": "postcss",
+ ".config/postcssrc.json5": "postcss",
+ ".config/postcssrc.yaml": "postcss",
+ ".config/postcssrc.yml": "postcss",
+ ".config/postcssrc.toml": "postcss",
+ ".config/postcssrc.js": "postcss",
+ ".config/postcssrc.mjs": "postcss",
+ ".config/postcssrc.cjs": "postcss",
+ ".config/postcssrc.ts": "postcss",
+ ".config/postcssrc.mts": "postcss",
+ ".config/postcssrc.cts": "postcss",
+ "postcss.config.json": "postcss",
+ "postcss.config.jsonc": "postcss",
+ "postcss.config.json5": "postcss",
+ "postcss.config.yaml": "postcss",
+ "postcss.config.yml": "postcss",
+ "postcss.config.toml": "postcss",
+ "postcss.config.js": "postcss",
+ "postcss.config.mjs": "postcss",
+ "postcss.config.cjs": "postcss",
+ "postcss.config.ts": "postcss",
+ "postcss.config.mts": "postcss",
+ "postcss.config.cts": "postcss",
+ ".posthtmlrc": "posthtml",
+ ".posthtmlrc.json": "posthtml",
+ ".posthtmlrc.jsonc": "posthtml",
+ ".posthtmlrc.json5": "posthtml",
+ ".posthtmlrc.yaml": "posthtml",
+ ".posthtmlrc.yml": "posthtml",
+ ".posthtmlrc.toml": "posthtml",
+ ".posthtmlrc.js": "posthtml",
+ ".posthtmlrc.mjs": "posthtml",
+ ".posthtmlrc.cjs": "posthtml",
+ ".posthtmlrc.ts": "posthtml",
+ ".posthtmlrc.mts": "posthtml",
+ ".posthtmlrc.cts": "posthtml",
+ ".config/posthtmlrc": "posthtml",
+ ".config/posthtmlrc.json": "posthtml",
+ ".config/posthtmlrc.jsonc": "posthtml",
+ ".config/posthtmlrc.json5": "posthtml",
+ ".config/posthtmlrc.yaml": "posthtml",
+ ".config/posthtmlrc.yml": "posthtml",
+ ".config/posthtmlrc.toml": "posthtml",
+ ".config/posthtmlrc.js": "posthtml",
+ ".config/posthtmlrc.mjs": "posthtml",
+ ".config/posthtmlrc.cjs": "posthtml",
+ ".config/posthtmlrc.ts": "posthtml",
+ ".config/posthtmlrc.mts": "posthtml",
+ ".config/posthtmlrc.cts": "posthtml",
+ "posthtml.config.json": "posthtml",
+ "posthtml.config.jsonc": "posthtml",
+ "posthtml.config.json5": "posthtml",
+ "posthtml.config.yaml": "posthtml",
+ "posthtml.config.yml": "posthtml",
+ "posthtml.config.toml": "posthtml",
+ "posthtml.config.js": "posthtml",
+ "posthtml.config.mjs": "posthtml",
+ "posthtml.config.cjs": "posthtml",
+ "posthtml.config.ts": "posthtml",
+ "posthtml.config.mts": "posthtml",
+ "posthtml.config.cts": "posthtml",
+ "todo.md": "todo",
+ "todos.md": "todo",
+ "cabal.project": "cabal",
+ "cabal.project.freeze": "cabal",
+ "cabal.project.local": "cabal",
+ "CNAME": "http",
+ "project.graphcool": "graphcool",
+ "webpack.base.js": "webpack",
+ "webpack.base.mjs": "webpack",
+ "webpack.base.cjs": "webpack",
+ "webpack.base.ts": "webpack",
+ "webpack.base.mts": "webpack",
+ "webpack.base.cts": "webpack",
+ "webpack.client.js": "webpack",
+ "webpack.client.mjs": "webpack",
+ "webpack.client.cjs": "webpack",
+ "webpack.client.ts": "webpack",
+ "webpack.client.mts": "webpack",
+ "webpack.client.cts": "webpack",
+ "webpack.common.js": "webpack",
+ "webpack.common.mjs": "webpack",
+ "webpack.common.cjs": "webpack",
+ "webpack.common.ts": "webpack",
+ "webpack.common.mts": "webpack",
+ "webpack.common.cts": "webpack",
+ "webpack.config.babel.js": "webpack",
+ "webpack.config.babel.mjs": "webpack",
+ "webpack.config.babel.cjs": "webpack",
+ "webpack.config.babel.ts": "webpack",
+ "webpack.config.babel.mts": "webpack",
+ "webpack.config.babel.cts": "webpack",
+ "webpack.config.base.babel.js": "webpack",
+ "webpack.config.base.babel.mjs": "webpack",
+ "webpack.config.base.babel.cjs": "webpack",
+ "webpack.config.base.babel.ts": "webpack",
+ "webpack.config.base.babel.mts": "webpack",
+ "webpack.config.base.babel.cts": "webpack",
+ "webpack.config.base.js": "webpack",
+ "webpack.config.base.mjs": "webpack",
+ "webpack.config.base.cjs": "webpack",
+ "webpack.config.base.ts": "webpack",
+ "webpack.config.base.mts": "webpack",
+ "webpack.config.base.cts": "webpack",
+ "webpack.config.client.js": "webpack",
+ "webpack.config.client.mjs": "webpack",
+ "webpack.config.client.cjs": "webpack",
+ "webpack.config.client.ts": "webpack",
+ "webpack.config.client.mts": "webpack",
+ "webpack.config.client.cts": "webpack",
+ "webpack.config.common.babel.js": "webpack",
+ "webpack.config.common.babel.mjs": "webpack",
+ "webpack.config.common.babel.cjs": "webpack",
+ "webpack.config.common.babel.ts": "webpack",
+ "webpack.config.common.babel.mts": "webpack",
+ "webpack.config.common.babel.cts": "webpack",
+ "webpack.config.common.js": "webpack",
+ "webpack.config.common.mjs": "webpack",
+ "webpack.config.common.cjs": "webpack",
+ "webpack.config.common.ts": "webpack",
+ "webpack.config.common.mts": "webpack",
+ "webpack.config.common.cts": "webpack",
+ "webpack.config.dev.babel.js": "webpack",
+ "webpack.config.dev.babel.mjs": "webpack",
+ "webpack.config.dev.babel.cjs": "webpack",
+ "webpack.config.dev.babel.ts": "webpack",
+ "webpack.config.dev.babel.mts": "webpack",
+ "webpack.config.dev.babel.cts": "webpack",
+ "webpack.config.dev.js": "webpack",
+ "webpack.config.dev.mjs": "webpack",
+ "webpack.config.dev.cjs": "webpack",
+ "webpack.config.dev.ts": "webpack",
+ "webpack.config.dev.mts": "webpack",
+ "webpack.config.dev.cts": "webpack",
+ "webpack.config.main.js": "webpack",
+ "webpack.config.main.mjs": "webpack",
+ "webpack.config.main.cjs": "webpack",
+ "webpack.config.main.ts": "webpack",
+ "webpack.config.main.mts": "webpack",
+ "webpack.config.main.cts": "webpack",
+ "webpack.config.prod.babel.js": "webpack",
+ "webpack.config.prod.babel.mjs": "webpack",
+ "webpack.config.prod.babel.cjs": "webpack",
+ "webpack.config.prod.babel.ts": "webpack",
+ "webpack.config.prod.babel.mts": "webpack",
+ "webpack.config.prod.babel.cts": "webpack",
+ "webpack.config.prod.js": "webpack",
+ "webpack.config.prod.mjs": "webpack",
+ "webpack.config.prod.cjs": "webpack",
+ "webpack.config.prod.ts": "webpack",
+ "webpack.config.prod.mts": "webpack",
+ "webpack.config.prod.cts": "webpack",
+ "webpack.config.production.babel.js": "webpack",
+ "webpack.config.production.babel.mjs": "webpack",
+ "webpack.config.production.babel.cjs": "webpack",
+ "webpack.config.production.babel.ts": "webpack",
+ "webpack.config.production.babel.mts": "webpack",
+ "webpack.config.production.babel.cts": "webpack",
+ "webpack.config.production.js": "webpack",
+ "webpack.config.production.mjs": "webpack",
+ "webpack.config.production.cjs": "webpack",
+ "webpack.config.production.ts": "webpack",
+ "webpack.config.production.mts": "webpack",
+ "webpack.config.production.cts": "webpack",
+ "webpack.config.renderer.js": "webpack",
+ "webpack.config.renderer.mjs": "webpack",
+ "webpack.config.renderer.cjs": "webpack",
+ "webpack.config.renderer.ts": "webpack",
+ "webpack.config.renderer.mts": "webpack",
+ "webpack.config.renderer.cts": "webpack",
+ "webpack.config.server.js": "webpack",
+ "webpack.config.server.mjs": "webpack",
+ "webpack.config.server.cjs": "webpack",
+ "webpack.config.server.ts": "webpack",
+ "webpack.config.server.mts": "webpack",
+ "webpack.config.server.cts": "webpack",
+ "webpack.config.staging.babel.js": "webpack",
+ "webpack.config.staging.babel.mjs": "webpack",
+ "webpack.config.staging.babel.cjs": "webpack",
+ "webpack.config.staging.babel.ts": "webpack",
+ "webpack.config.staging.babel.mts": "webpack",
+ "webpack.config.staging.babel.cts": "webpack",
+ "webpack.config.staging.js": "webpack",
+ "webpack.config.staging.mjs": "webpack",
+ "webpack.config.staging.cjs": "webpack",
+ "webpack.config.staging.ts": "webpack",
+ "webpack.config.staging.mts": "webpack",
+ "webpack.config.staging.cts": "webpack",
+ "webpack.config.test.js": "webpack",
+ "webpack.config.test.mjs": "webpack",
+ "webpack.config.test.cjs": "webpack",
+ "webpack.config.test.ts": "webpack",
+ "webpack.config.test.mts": "webpack",
+ "webpack.config.test.cts": "webpack",
+ "webpack.config.vendor.production.js": "webpack",
+ "webpack.config.vendor.production.mjs": "webpack",
+ "webpack.config.vendor.production.cjs": "webpack",
+ "webpack.config.vendor.production.ts": "webpack",
+ "webpack.config.vendor.production.mts": "webpack",
+ "webpack.config.vendor.production.cts": "webpack",
+ "webpack.config.vendor.js": "webpack",
+ "webpack.config.vendor.mjs": "webpack",
+ "webpack.config.vendor.cjs": "webpack",
+ "webpack.config.vendor.ts": "webpack",
+ "webpack.config.vendor.mts": "webpack",
+ "webpack.config.vendor.cts": "webpack",
+ "webpack.config.js": "webpack",
+ "webpack.config.mjs": "webpack",
+ "webpack.config.cjs": "webpack",
+ "webpack.config.ts": "webpack",
+ "webpack.config.mts": "webpack",
+ "webpack.config.cts": "webpack",
+ "webpack.dev.js": "webpack",
+ "webpack.dev.mjs": "webpack",
+ "webpack.dev.cjs": "webpack",
+ "webpack.dev.ts": "webpack",
+ "webpack.dev.mts": "webpack",
+ "webpack.dev.cts": "webpack",
+ "webpack.development.js": "webpack",
+ "webpack.development.mjs": "webpack",
+ "webpack.development.cjs": "webpack",
+ "webpack.development.ts": "webpack",
+ "webpack.development.mts": "webpack",
+ "webpack.development.cts": "webpack",
+ "webpack.dist.js": "webpack",
+ "webpack.dist.mjs": "webpack",
+ "webpack.dist.cjs": "webpack",
+ "webpack.dist.ts": "webpack",
+ "webpack.dist.mts": "webpack",
+ "webpack.dist.cts": "webpack",
+ "webpack.mix.js": "webpack",
+ "webpack.mix.mjs": "webpack",
+ "webpack.mix.cjs": "webpack",
+ "webpack.mix.ts": "webpack",
+ "webpack.mix.mts": "webpack",
+ "webpack.mix.cts": "webpack",
+ "webpack.prod.config.js": "webpack",
+ "webpack.prod.config.mjs": "webpack",
+ "webpack.prod.config.cjs": "webpack",
+ "webpack.prod.config.ts": "webpack",
+ "webpack.prod.config.mts": "webpack",
+ "webpack.prod.config.cts": "webpack",
+ "webpack.prod.js": "webpack",
+ "webpack.prod.mjs": "webpack",
+ "webpack.prod.cjs": "webpack",
+ "webpack.prod.ts": "webpack",
+ "webpack.prod.mts": "webpack",
+ "webpack.prod.cts": "webpack",
+ "webpack.production.js": "webpack",
+ "webpack.production.mjs": "webpack",
+ "webpack.production.cjs": "webpack",
+ "webpack.production.ts": "webpack",
+ "webpack.production.mts": "webpack",
+ "webpack.production.cts": "webpack",
+ "webpack.server.js": "webpack",
+ "webpack.server.mjs": "webpack",
+ "webpack.server.cjs": "webpack",
+ "webpack.server.ts": "webpack",
+ "webpack.server.mts": "webpack",
+ "webpack.server.cts": "webpack",
+ "webpack.test.js": "webpack",
+ "webpack.test.mjs": "webpack",
+ "webpack.test.cjs": "webpack",
+ "webpack.test.ts": "webpack",
+ "webpack.test.mts": "webpack",
+ "webpack.test.cts": "webpack",
+ "webpack.js": "webpack",
+ "webpack.mjs": "webpack",
+ "webpack.cjs": "webpack",
+ "webpack.ts": "webpack",
+ "webpack.mts": "webpack",
+ "webpack.cts": "webpack",
+ "webpackfile.js": "webpack",
+ "webpackfile.mjs": "webpack",
+ "webpackfile.cjs": "webpack",
+ "webpackfile.ts": "webpack",
+ "webpackfile.mts": "webpack",
+ "webpackfile.cts": "webpack",
+ "webpack.config.coffee": "webpack",
+ "ionic.config.json": "ionic",
+ ".io-config.json": "ionic",
+ "gulpfile.js": "gulp",
+ "gulpfile.mjs": "gulp",
+ "gulpfile.ts": "gulp",
+ "gulpfile.cts": "gulp",
+ "gulpfile.mts": "gulp",
+ "gulpfile.babel.js": "gulp",
+ "package.json": "nodejs",
+ "package-lock.json": "nodejs",
+ ".nvmrc": "nodejs",
+ ".esmrc": "nodejs",
+ ".node-version": "nodejs",
+ ".npmignore": "npm",
+ ".npmrc": "npm",
+ ".yarnrc": "yarn",
+ "yarn.lock": "yarn",
+ ".yarnclean": "yarn",
+ ".yarn-integrity": "yarn",
+ "yarn-error.log": "yarn",
+ ".yarnrc.yml": "yarn",
+ ".yarnrc.yaml": "yarn",
+ "androidmanifest.xml": "android",
+ ".env.defaults": "tune",
+ ".env.example": "tune",
+ ".env.sample": "tune",
+ ".env.template": "tune",
+ ".env.schema": "tune",
+ ".env.local": "tune",
+ ".env.dev": "tune",
+ ".env.development": "tune",
+ ".env.alpha": "tune",
+ ".env.e2e": "tune",
+ ".env.qa": "tune",
+ ".env.dist": "tune",
+ ".env.prod": "tune",
+ ".env.production": "tune",
+ ".env.stg": "tune",
+ ".env.stage": "tune",
+ ".env.staging": "tune",
+ ".env.preview": "tune",
+ ".env.test": "tune",
+ ".env.testing": "tune",
+ ".env.dev.local": "tune",
+ ".env.development.local": "tune",
+ ".env.qa.local": "tune",
+ ".env.prod.local": "tune",
+ ".env.production.local": "tune",
+ ".env.stg.local": "tune",
+ ".env.staging.local": "tune",
+ ".env.test.local": "tune",
+ ".env.uat": "tune",
+ ".vars": "tune",
+ ".dev.vars": "tune",
+ "turbo.json": "turborepo",
+ "turbo.jsonc": "turborepo",
+ ".babelrc": "babel",
+ ".babelrc.json": "babel",
+ ".babelrc.jsonc": "babel",
+ ".babelrc.json5": "babel",
+ ".babelrc.yaml": "babel",
+ ".babelrc.yml": "babel",
+ ".babelrc.toml": "babel",
+ ".babelrc.js": "babel",
+ ".babelrc.mjs": "babel",
+ ".babelrc.cjs": "babel",
+ ".babelrc.ts": "babel",
+ ".babelrc.mts": "babel",
+ ".babelrc.cts": "babel",
+ ".config/babelrc": "babel",
+ ".config/babelrc.json": "babel",
+ ".config/babelrc.jsonc": "babel",
+ ".config/babelrc.json5": "babel",
+ ".config/babelrc.yaml": "babel",
+ ".config/babelrc.yml": "babel",
+ ".config/babelrc.toml": "babel",
+ ".config/babelrc.js": "babel",
+ ".config/babelrc.mjs": "babel",
+ ".config/babelrc.cjs": "babel",
+ ".config/babelrc.ts": "babel",
+ ".config/babelrc.mts": "babel",
+ ".config/babelrc.cts": "babel",
+ "babel.config.json": "babel",
+ "babel.config.jsonc": "babel",
+ "babel.config.json5": "babel",
+ "babel.config.yaml": "babel",
+ "babel.config.yml": "babel",
+ "babel.config.toml": "babel",
+ "babel.config.js": "babel",
+ "babel.config.mjs": "babel",
+ "babel.config.cjs": "babel",
+ "babel.config.ts": "babel",
+ "babel.config.mts": "babel",
+ "babel.config.cts": "babel",
+ ".babel-plugin-macrosrc": "babel",
+ ".babel-plugin-macrosrc.json": "babel",
+ ".babel-plugin-macrosrc.jsonc": "babel",
+ ".babel-plugin-macrosrc.json5": "babel",
+ ".babel-plugin-macrosrc.yaml": "babel",
+ ".babel-plugin-macrosrc.yml": "babel",
+ ".babel-plugin-macrosrc.toml": "babel",
+ ".babel-plugin-macrosrc.js": "babel",
+ ".babel-plugin-macrosrc.mjs": "babel",
+ ".babel-plugin-macrosrc.cjs": "babel",
+ ".babel-plugin-macrosrc.ts": "babel",
+ ".babel-plugin-macrosrc.mts": "babel",
+ ".babel-plugin-macrosrc.cts": "babel",
+ ".config/babel-plugin-macrosrc": "babel",
+ ".config/babel-plugin-macrosrc.json": "babel",
+ ".config/babel-plugin-macrosrc.jsonc": "babel",
+ ".config/babel-plugin-macrosrc.json5": "babel",
+ ".config/babel-plugin-macrosrc.yaml": "babel",
+ ".config/babel-plugin-macrosrc.yml": "babel",
+ ".config/babel-plugin-macrosrc.toml": "babel",
+ ".config/babel-plugin-macrosrc.js": "babel",
+ ".config/babel-plugin-macrosrc.mjs": "babel",
+ ".config/babel-plugin-macrosrc.cjs": "babel",
+ ".config/babel-plugin-macrosrc.ts": "babel",
+ ".config/babel-plugin-macrosrc.mts": "babel",
+ ".config/babel-plugin-macrosrc.cts": "babel",
+ "babel-plugin-macros.config.json": "babel",
+ "babel-plugin-macros.config.jsonc": "babel",
+ "babel-plugin-macros.config.json5": "babel",
+ "babel-plugin-macros.config.yaml": "babel",
+ "babel-plugin-macros.config.yml": "babel",
+ "babel-plugin-macros.config.toml": "babel",
+ "babel-plugin-macros.config.js": "babel",
+ "babel-plugin-macros.config.mjs": "babel",
+ "babel-plugin-macros.config.cjs": "babel",
+ "babel-plugin-macros.config.ts": "babel",
+ "babel-plugin-macros.config.mts": "babel",
+ "babel-plugin-macros.config.cts": "babel",
+ "babel-transform.js": "babel",
+ "blitz.config.js": "blitz",
+ "blitz.config.ts": "blitz",
+ ".blitz.config.compiled.js": "blitz",
+ "contributing.md": "contributing",
+ "contributing.rst": "contributing",
+ "contributing.txt": "contributing",
+ "contributing": "contributing",
+ "readme.md": "readme",
+ "readme.rst": "readme",
+ "readme.txt": "readme",
+ "readme": "readme",
+ "changelog": "changelog",
+ "changelog.md": "changelog",
+ "changelog.rst": "changelog",
+ "changelog.txt": "changelog",
+ "changes": "changelog",
+ "changes.md": "changelog",
+ "changes.rst": "changelog",
+ "changes.txt": "changelog",
+ "architecture.md": "architecture",
+ "architecture.rst": "architecture",
+ "architecture.txt": "architecture",
+ "architecture": "architecture",
+ "credits.md": "credits",
+ "credits.rst": "credits",
+ "credits.txt": "credits",
+ "credits": "credits",
+ "authors.md": "authors",
+ "authors.rst": "authors",
+ "authors.txt": "authors",
+ "authors": "authors",
+ "contributors.md": "authors",
+ "contributors.rst": "authors",
+ "contributors.txt": "authors",
+ "contributors": "authors",
+ ".flowconfig": "flow",
+ "favicon.ico": "favicon",
+ "karma.conf.js": "karma",
+ "karma.conf.ts": "karma",
+ "karma.conf.coffee": "karma",
+ "karma.config.js": "karma",
+ "karma.config.ts": "karma",
+ "karma-main.js": "karma",
+ "karma-main.ts": "karma",
+ ".bithoundrc": "bithound",
+ "svgo.config.js": "svgo",
+ "svgo.config.cjs": "svgo",
+ "svgo.config.mjs": "svgo",
+ ".appveyor.yml": "appveyor",
+ "appveyor.yml": "appveyor",
+ ".travis.yml": "travis",
+ ".codecov.yml": "codecov",
+ "codecov.yml": "codecov",
+ ".codecov.yaml": "codecov",
+ "codecov.yaml": "codecov",
+ "sonar-project.properties": "sonarcloud",
+ ".sonarcloud.properties": "sonarcloud",
+ "sonarcloud.yaml": "sonarcloud",
+ "SonarQube.Analysis.xml": "sonarcloud",
+ "protractor.conf.js": "protractor",
+ "protractor.conf.ts": "protractor",
+ "protractor.conf.coffee": "protractor",
+ "protractor.config.js": "protractor",
+ "protractor.config.ts": "protractor",
+ "fuse.js": "fusebox",
+ "procfile": "heroku",
+ "procfile.windows": "heroku",
+ ".editorconfig": "editorconfig",
+ ".editorconfig-checker.json": "editorconfig",
+ ".ecrc": "editorconfig",
+ ".bowerrc": "bower",
+ "bower.json": "bower",
+ ".eslintrc": "eslint",
+ ".eslintrc.json": "eslint",
+ ".eslintrc.jsonc": "eslint",
+ ".eslintrc.json5": "eslint",
+ ".eslintrc.yaml": "eslint",
+ ".eslintrc.yml": "eslint",
+ ".eslintrc.toml": "eslint",
+ ".eslintrc.js": "eslint",
+ ".eslintrc.mjs": "eslint",
+ ".eslintrc.cjs": "eslint",
+ ".eslintrc.ts": "eslint",
+ ".eslintrc.mts": "eslint",
+ ".eslintrc.cts": "eslint",
+ ".config/eslintrc": "eslint",
+ ".config/eslintrc.json": "eslint",
+ ".config/eslintrc.jsonc": "eslint",
+ ".config/eslintrc.json5": "eslint",
+ ".config/eslintrc.yaml": "eslint",
+ ".config/eslintrc.yml": "eslint",
+ ".config/eslintrc.toml": "eslint",
+ ".config/eslintrc.js": "eslint",
+ ".config/eslintrc.mjs": "eslint",
+ ".config/eslintrc.cjs": "eslint",
+ ".config/eslintrc.ts": "eslint",
+ ".config/eslintrc.mts": "eslint",
+ ".config/eslintrc.cts": "eslint",
+ "eslint.config.json": "eslint",
+ "eslint.config.jsonc": "eslint",
+ "eslint.config.json5": "eslint",
+ "eslint.config.yaml": "eslint",
+ "eslint.config.yml": "eslint",
+ "eslint.config.toml": "eslint",
+ "eslint.config.js": "eslint",
+ "eslint.config.mjs": "eslint",
+ "eslint.config.cjs": "eslint",
+ "eslint.config.ts": "eslint",
+ "eslint.config.mts": "eslint",
+ "eslint.config.cts": "eslint",
+ ".eslintrc-md.js": "eslint",
+ ".eslintrc-jsdoc.js": "eslint",
+ ".eslintrc.base.json": "eslint",
+ ".eslintignore": "eslint",
+ ".eslintcache": "eslint",
+ "code_of_conduct.md": "conduct",
+ "code_of_conduct.txt": "conduct",
+ "code_of_conduct": "conduct",
+ ".watchmanconfig": "watchman",
+ "aurelia.json": "aurelia",
+ ".autorc": "auto",
+ "auto.config.js": "auto",
+ "auto.config.ts": "auto",
+ "auto-config.json": "auto",
+ "auto-config.yaml": "auto",
+ "auto-config.yml": "auto",
+ "auto-config.ts": "auto",
+ "auto-config.js": "auto",
+ "mocha.opts": "mocha",
+ ".mocharc.yml": "mocha",
+ ".mocharc.yaml": "mocha",
+ ".mocharc.js": "mocha",
+ ".mocharc.cjs": "mocha",
+ ".mocharc.json": "mocha",
+ ".mocharc.jsonc": "mocha",
+ "jenkinsfile": "jenkins",
+ "firebase.json": "firebase",
+ ".firebaserc": "firebase",
+ "firestore.rules": "firebase",
+ "firestore.indexes.json": "firebase",
+ "rollup.config.js": "rollup",
+ "rollup.config.mjs": "rollup",
+ "rollup.config.ts": "rollup",
+ "rollup-config.js": "rollup",
+ "rollup-config.mjs": "rollup",
+ "rollup-config.ts": "rollup",
+ "rollup.config.common.js": "rollup",
+ "rollup.config.common.mjs": "rollup",
+ "rollup.config.common.ts": "rollup",
+ "rollup.config.base.js": "rollup",
+ "rollup.config.base.mjs": "rollup",
+ "rollup.config.base.ts": "rollup",
+ "rollup.config.prod.js": "rollup",
+ "rollup.config.prod.mjs": "rollup",
+ "rollup.config.prod.ts": "rollup",
+ "rollup.config.dev.js": "rollup",
+ "rollup.config.dev.mjs": "rollup",
+ "rollup.config.dev.ts": "rollup",
+ "rollup.config.prod.vendor.js": "rollup",
+ "rollup.config.prod.vendor.mjs": "rollup",
+ "rollup.config.prod.vendor.ts": "rollup",
+ ".hhconfig": "hack",
+ "hardhat.config.js": "hardhat",
+ "hardhat.config.ts": "hardhat",
+ ".stylelintrc": "stylelint",
+ ".stylelintrc.json": "stylelint",
+ ".stylelintrc.jsonc": "stylelint",
+ ".stylelintrc.json5": "stylelint",
+ ".stylelintrc.yaml": "stylelint",
+ ".stylelintrc.yml": "stylelint",
+ ".stylelintrc.toml": "stylelint",
+ ".stylelintrc.js": "stylelint",
+ ".stylelintrc.mjs": "stylelint",
+ ".stylelintrc.cjs": "stylelint",
+ ".stylelintrc.ts": "stylelint",
+ ".stylelintrc.mts": "stylelint",
+ ".stylelintrc.cts": "stylelint",
+ ".config/stylelintrc": "stylelint",
+ ".config/stylelintrc.json": "stylelint",
+ ".config/stylelintrc.jsonc": "stylelint",
+ ".config/stylelintrc.json5": "stylelint",
+ ".config/stylelintrc.yaml": "stylelint",
+ ".config/stylelintrc.yml": "stylelint",
+ ".config/stylelintrc.toml": "stylelint",
+ ".config/stylelintrc.js": "stylelint",
+ ".config/stylelintrc.mjs": "stylelint",
+ ".config/stylelintrc.cjs": "stylelint",
+ ".config/stylelintrc.ts": "stylelint",
+ ".config/stylelintrc.mts": "stylelint",
+ ".config/stylelintrc.cts": "stylelint",
+ "stylelint.config.json": "stylelint",
+ "stylelint.config.jsonc": "stylelint",
+ "stylelint.config.json5": "stylelint",
+ "stylelint.config.yaml": "stylelint",
+ "stylelint.config.yml": "stylelint",
+ "stylelint.config.toml": "stylelint",
+ "stylelint.config.js": "stylelint",
+ "stylelint.config.mjs": "stylelint",
+ "stylelint.config.cjs": "stylelint",
+ "stylelint.config.ts": "stylelint",
+ "stylelint.config.mts": "stylelint",
+ "stylelint.config.cts": "stylelint",
+ ".stylelintignore": "stylelint",
+ ".stylelintcache": "stylelint",
+ ".codeclimate.yml": "code-climate",
+ ".prettierrc": "prettier",
+ ".prettierrc.json": "prettier",
+ ".prettierrc.jsonc": "prettier",
+ ".prettierrc.json5": "prettier",
+ ".prettierrc.yaml": "prettier",
+ ".prettierrc.yml": "prettier",
+ ".prettierrc.toml": "prettier",
+ ".prettierrc.js": "prettier",
+ ".prettierrc.mjs": "prettier",
+ ".prettierrc.cjs": "prettier",
+ ".prettierrc.ts": "prettier",
+ ".prettierrc.mts": "prettier",
+ ".prettierrc.cts": "prettier",
+ ".config/prettierrc": "prettier",
+ ".config/prettierrc.json": "prettier",
+ ".config/prettierrc.jsonc": "prettier",
+ ".config/prettierrc.json5": "prettier",
+ ".config/prettierrc.yaml": "prettier",
+ ".config/prettierrc.yml": "prettier",
+ ".config/prettierrc.toml": "prettier",
+ ".config/prettierrc.js": "prettier",
+ ".config/prettierrc.mjs": "prettier",
+ ".config/prettierrc.cjs": "prettier",
+ ".config/prettierrc.ts": "prettier",
+ ".config/prettierrc.mts": "prettier",
+ ".config/prettierrc.cts": "prettier",
+ "prettier.config.json": "prettier",
+ "prettier.config.jsonc": "prettier",
+ "prettier.config.json5": "prettier",
+ "prettier.config.yaml": "prettier",
+ "prettier.config.yml": "prettier",
+ "prettier.config.toml": "prettier",
+ "prettier.config.js": "prettier",
+ "prettier.config.mjs": "prettier",
+ "prettier.config.cjs": "prettier",
+ "prettier.config.ts": "prettier",
+ "prettier.config.mts": "prettier",
+ "prettier.config.cts": "prettier",
+ ".prettierignore": "prettier",
+ ".renovaterc": "renovate",
+ ".renovaterc.json": "renovate",
+ "renovate-config.json": "renovate",
+ "renovate.json": "renovate",
+ "renovate.json5": "renovate",
+ "apollo.config.js": "apollo",
+ "nodemon.json": "nodemon",
+ "nodemon-debug.json": "nodemon",
+ ".hintrc": "webhint",
+ "browserslist": "browserlist",
+ ".browserslistrc": "browserlist",
+ ".snyk": "snyk",
+ ".drone.yml": "drone",
+ ".sequelizerc": "sequelize",
+ "gatsby-config.js": "gatsby",
+ "gatsby-config.mjs": "gatsby",
+ "gatsby-config.ts": "gatsby",
+ "gatsby-node.js": "gatsby",
+ "gatsby-node.mjs": "gatsby",
+ "gatsby-node.ts": "gatsby",
+ "gatsby-browser.js": "gatsby",
+ "gatsby-browser.tsx": "gatsby",
+ "gatsby-ssr.js": "gatsby",
+ "gatsby-ssr.tsx": "gatsby",
+ ".wakatime-project": "wakatime",
+ "circle.yml": "circleci",
+ ".cfignore": "cloudfoundry",
+ "gruntfile.js": "grunt",
+ "gruntfile.ts": "grunt",
+ "gruntfile.cjs": "grunt",
+ "gruntfile.cts": "grunt",
+ "gruntfile.coffee": "grunt",
+ "gruntfile.babel.js": "grunt",
+ "gruntfile.babel.ts": "grunt",
+ "gruntfile.babel.coffee": "grunt",
+ "jest.config.js": "jest",
+ "jest.config.cjs": "jest",
+ "jest.config.mjs": "jest",
+ "jest.config.ts": "jest",
+ "jest.config.cts": "jest",
+ "jest.config.mts": "jest",
+ "jest.config.json": "jest",
+ "jest.e2e.config.js": "jest",
+ "jest.e2e.config.cjs": "jest",
+ "jest.e2e.config.mjs": "jest",
+ "jest.e2e.config.ts": "jest",
+ "jest.e2e.config.cts": "jest",
+ "jest.e2e.config.mts": "jest",
+ "jest.e2e.config.json": "jest",
+ "jest.e2e.json": "jest",
+ "jest-unit.config.js": "jest",
+ "jest-e2e.config.js": "jest",
+ "jest-e2e.config.cjs": "jest",
+ "jest-e2e.config.mjs": "jest",
+ "jest-e2e.config.ts": "jest",
+ "jest-e2e.config.cts": "jest",
+ "jest-e2e.config.mts": "jest",
+ "jest-e2e.config.json": "jest",
+ "jest-e2e.json": "jest",
+ "jest-github-actions-reporter.js": "jest",
+ "jest.setup.js": "jest",
+ "jest.setup.ts": "jest",
+ "jest.json": "jest",
+ ".jestrc": "jest",
+ ".jestrc.js": "jest",
+ ".jestrc.json": "jest",
+ "jest.teardown.js": "jest",
+ "jest-preset.json": "jest",
+ "jest-preset.js": "jest",
+ "jest-preset.cjs": "jest",
+ "jest-preset.mjs": "jest",
+ "jest.preset.js": "jest",
+ "jest.preset.mjs": "jest",
+ "jest.preset.cjs": "jest",
+ "jest.preset.json": "jest",
+ "fastfile": "fastlane",
+ "appfile": "fastlane",
+ ".helmignore": "helm",
+ "wallaby.js": "wallaby",
+ "wallaby.conf.js": "wallaby",
+ "stencil.config.js": "stencil",
+ "stencil.config.ts": "stencil",
+ "makefile": "makefile",
+ "gnumakefile": "makefile",
+ "kbuild": "makefile",
+ ".releaserc": "semantic-release",
+ ".releaserc.json": "semantic-release",
+ ".releaserc.jsonc": "semantic-release",
+ ".releaserc.json5": "semantic-release",
+ ".releaserc.yaml": "semantic-release",
+ ".releaserc.yml": "semantic-release",
+ ".releaserc.toml": "semantic-release",
+ ".releaserc.js": "semantic-release",
+ ".releaserc.mjs": "semantic-release",
+ ".releaserc.cjs": "semantic-release",
+ ".releaserc.ts": "semantic-release",
+ ".releaserc.mts": "semantic-release",
+ ".releaserc.cts": "semantic-release",
+ ".config/releaserc": "semantic-release",
+ ".config/releaserc.json": "semantic-release",
+ ".config/releaserc.jsonc": "semantic-release",
+ ".config/releaserc.json5": "semantic-release",
+ ".config/releaserc.yaml": "semantic-release",
+ ".config/releaserc.yml": "semantic-release",
+ ".config/releaserc.toml": "semantic-release",
+ ".config/releaserc.js": "semantic-release",
+ ".config/releaserc.mjs": "semantic-release",
+ ".config/releaserc.cjs": "semantic-release",
+ ".config/releaserc.ts": "semantic-release",
+ ".config/releaserc.mts": "semantic-release",
+ ".config/releaserc.cts": "semantic-release",
+ "release.config.json": "semantic-release",
+ "release.config.jsonc": "semantic-release",
+ "release.config.json5": "semantic-release",
+ "release.config.yaml": "semantic-release",
+ "release.config.yml": "semantic-release",
+ "release.config.toml": "semantic-release",
+ "release.config.js": "semantic-release",
+ "release.config.mjs": "semantic-release",
+ "release.config.cjs": "semantic-release",
+ "release.config.ts": "semantic-release",
+ "release.config.mts": "semantic-release",
+ "release.config.cts": "semantic-release",
+ "bitbucket-pipelines.yaml": "bitbucket",
+ "bitbucket-pipelines.yml": "bitbucket",
+ ".bazelignore": "bazel",
+ ".bazelrc": "bazel",
+ ".bazelversion": "bazel",
+ ".gdignore": "godot-assets",
+ "._sc_": "godot-assets",
+ "_sc_": "godot-assets",
+ "azure-pipelines.yml": "azure-pipelines",
+ "azure-pipelines.yaml": "azure-pipelines",
+ "azure-pipelines-main.yml": "azure-pipelines",
+ "azure-pipelines-main.yaml": "azure-pipelines",
+ "vagrantfile": "vagrant",
+ "prisma.yml": "prisma",
+ "prisma.config.ts": "prisma",
+ ".nycrc": "istanbul",
+ ".nycrc.json": "istanbul",
+ ".nycrc.yaml": "istanbul",
+ ".nycrc.yml": "istanbul",
+ "nyc.config.js": "istanbul",
+ "nyc.config.cjs": "istanbul",
+ ".istanbul.yml": "istanbul",
+ "tailwind.js": "tailwindcss",
+ "tailwind.ts": "tailwindcss",
+ "tailwind.config.js": "tailwindcss",
+ "tailwind.config.cjs": "tailwindcss",
+ "tailwind.config.mjs": "tailwindcss",
+ "tailwind.config.ts": "tailwindcss",
+ "tailwind.config.cts": "tailwindcss",
+ "tailwind.config.mts": "tailwindcss",
+ "buildkite.yml": "buildkite",
+ "buildkite.yaml": "buildkite",
+ "netlify.json": "netlify",
+ "netlify.yml": "netlify",
+ "netlify.yaml": "netlify",
+ "netlify.toml": "netlify",
+ "svelte.config.js": "svelte",
+ "svelte.config.mjs": "svelte",
+ "svelte.config.cjs": "svelte",
+ "svelte.config.ts": "svelte",
+ "svelte.config.mts": "svelte",
+ "svelte.config.cts": "svelte",
+ "nest-cli.json": "nest",
+ ".nest-cli.json": "nest",
+ "nestconfig.json": "nest",
+ ".nestconfig.json": "nest",
+ "moon.yml": "moon",
+ ".percy.yml": "percy",
+ ".gitpod.yml": "gitpod",
+ ".stackblitzrc": "stackblitz",
+ "codeowners": "codeowners",
+ "OWNERS": "codeowners",
+ ".gcloudignore": "gcp",
+ "amplify.yml": "amplify",
+ ".huskyrc": "husky",
+ ".huskyrc.json": "husky",
+ ".huskyrc.jsonc": "husky",
+ ".huskyrc.json5": "husky",
+ ".huskyrc.yaml": "husky",
+ ".huskyrc.yml": "husky",
+ ".huskyrc.toml": "husky",
+ ".huskyrc.js": "husky",
+ ".huskyrc.mjs": "husky",
+ ".huskyrc.cjs": "husky",
+ ".huskyrc.ts": "husky",
+ ".huskyrc.mts": "husky",
+ ".huskyrc.cts": "husky",
+ ".config/huskyrc": "husky",
+ ".config/huskyrc.json": "husky",
+ ".config/huskyrc.jsonc": "husky",
+ ".config/huskyrc.json5": "husky",
+ ".config/huskyrc.yaml": "husky",
+ ".config/huskyrc.yml": "husky",
+ ".config/huskyrc.toml": "husky",
+ ".config/huskyrc.js": "husky",
+ ".config/huskyrc.mjs": "husky",
+ ".config/huskyrc.cjs": "husky",
+ ".config/huskyrc.ts": "husky",
+ ".config/huskyrc.mts": "husky",
+ ".config/huskyrc.cts": "husky",
+ "husky.config.json": "husky",
+ "husky.config.jsonc": "husky",
+ "husky.config.json5": "husky",
+ "husky.config.yaml": "husky",
+ "husky.config.yml": "husky",
+ "husky.config.toml": "husky",
+ "husky.config.js": "husky",
+ "husky.config.mjs": "husky",
+ "husky.config.cjs": "husky",
+ "husky.config.ts": "husky",
+ "husky.config.mts": "husky",
+ "husky.config.cts": "husky",
+ "tiltfile": "tilt",
+ "capacitor.config.json": "capacitor",
+ "capacitor.config.ts": "capacitor",
+ ".adonisrc.json": "adonis",
+ "ace": "adonis",
+ "meson.build": "meson",
+ "meson_options.txt": "meson",
+ ".czrc": "commitizen",
+ ".cz.json": "commitizen",
+ ".cz.toml": "commitizen",
+ ".cz.yaml": "commitizen",
+ ".cz.yml": "commitizen",
+ "cz.json": "commitizen",
+ "cz.toml": "commitizen",
+ "cz.yaml": "commitizen",
+ "cz.yml": "commitizen",
+ ".commitlintrc": "commitlint",
+ ".commitlintrc.json": "commitlint",
+ ".commitlintrc.jsonc": "commitlint",
+ ".commitlintrc.json5": "commitlint",
+ ".commitlintrc.yaml": "commitlint",
+ ".commitlintrc.yml": "commitlint",
+ ".commitlintrc.toml": "commitlint",
+ ".commitlintrc.js": "commitlint",
+ ".commitlintrc.mjs": "commitlint",
+ ".commitlintrc.cjs": "commitlint",
+ ".commitlintrc.ts": "commitlint",
+ ".commitlintrc.mts": "commitlint",
+ ".commitlintrc.cts": "commitlint",
+ ".config/commitlintrc": "commitlint",
+ ".config/commitlintrc.json": "commitlint",
+ ".config/commitlintrc.jsonc": "commitlint",
+ ".config/commitlintrc.json5": "commitlint",
+ ".config/commitlintrc.yaml": "commitlint",
+ ".config/commitlintrc.yml": "commitlint",
+ ".config/commitlintrc.toml": "commitlint",
+ ".config/commitlintrc.js": "commitlint",
+ ".config/commitlintrc.mjs": "commitlint",
+ ".config/commitlintrc.cjs": "commitlint",
+ ".config/commitlintrc.ts": "commitlint",
+ ".config/commitlintrc.mts": "commitlint",
+ ".config/commitlintrc.cts": "commitlint",
+ "commitlint.config.json": "commitlint",
+ "commitlint.config.jsonc": "commitlint",
+ "commitlint.config.json5": "commitlint",
+ "commitlint.config.yaml": "commitlint",
+ "commitlint.config.yml": "commitlint",
+ "commitlint.config.toml": "commitlint",
+ "commitlint.config.js": "commitlint",
+ "commitlint.config.mjs": "commitlint",
+ "commitlint.config.cjs": "commitlint",
+ "commitlint.config.ts": "commitlint",
+ "commitlint.config.mts": "commitlint",
+ "commitlint.config.cts": "commitlint",
+ ".commitlint.yaml": "commitlint",
+ ".commitlint.yml": "commitlint",
+ ".buckconfig": "buck",
+ "nx.json": "nx",
+ ".nxignore": "nx",
+ "dune": "dune",
+ "dune-project": "dune",
+ "dune-workspace": "dune",
+ "dune-workspace.dev": "dune",
+ "roadmap.md": "roadmap",
+ "roadmap.txt": "roadmap",
+ "timeline.md": "roadmap",
+ "timeline.txt": "roadmap",
+ "milestones.md": "roadmap",
+ "milestones.txt": "roadmap",
+ "nuget.config": "nuget",
+ ".nuspec": "nuget",
+ "nuget.exe": "nuget",
+ "stryker.conf.json": "stryker",
+ "stryker.conf.js": "stryker",
+ "stryker.conf.cjs": "stryker",
+ "stryker.conf.mjs": "stryker",
+ ".stryker.conf.json": "stryker",
+ ".stryker.conf.js": "stryker",
+ ".stryker.conf.cjs": "stryker",
+ ".stryker.conf.mjs": "stryker",
+ "stryker.config.json": "stryker",
+ "stryker.config.js": "stryker",
+ "stryker.config.mjs": "stryker",
+ "stryker.config.cjs": "stryker",
+ ".stryker.config.json": "stryker",
+ ".stryker.config.js": "stryker",
+ ".stryker.config.mjs": "stryker",
+ ".stryker.config.cjs": "stryker",
+ ".modernizrrc": "modernizr",
+ ".modernizrrc.js": "modernizr",
+ ".modernizrrc.json": "modernizr",
+ ".slugignore": "slug",
+ "stitches.config.js": "stitches",
+ "stitches.config.ts": "stitches",
+ "nginx.conf": "nginx",
+ ".mcattributes": "minecraft",
+ ".mcdefinitions": "minecraft",
+ ".mcignore": "minecraft",
+ ".replit": "replit",
+ "duc.fbs": "duc",
+ "snowpack.config.js": "snowpack",
+ "snowpack.config.cjs": "snowpack",
+ "snowpack.config.mjs": "snowpack",
+ "snowpack.config.ts": "snowpack",
+ "snowpack.config.cts": "snowpack",
+ "snowpack.config.mts": "snowpack",
+ "snowpack.deps.json": "snowpack",
+ "snowpack.config.json": "snowpack",
+ "quasar.conf.js": "quasar",
+ "quasar.config.js": "quasar",
+ "quasar.conf.ts": "quasar",
+ "quasar.config.ts": "quasar",
+ "quasar.config.cjs": "quasar",
+ "dependabot.yml": "dependabot",
+ "dependabot.yaml": "dependabot",
+ "vite.config.js": "vite",
+ "vite.config.mjs": "vite",
+ "vite.config.cjs": "vite",
+ "vite.config.ts": "vite",
+ "vite.config.mts": "vite",
+ "vite.config.cts": "vite",
+ "vitest.workspace.js": "vitest",
+ "vitest.workspace.mjs": "vitest",
+ "vitest.workspace.cjs": "vitest",
+ "vitest.workspace.ts": "vitest",
+ "vitest.workspace.mts": "vitest",
+ "vitest.workspace.cts": "vitest",
+ "vitest.config.js": "vitest",
+ "vitest.config.mjs": "vitest",
+ "vitest.config.cjs": "vitest",
+ "vitest.config.ts": "vitest",
+ "vitest.config.mts": "vitest",
+ "vitest.config.cts": "vitest",
+ "velite.config.js": "velite",
+ "velite.config.mjs": "velite",
+ "velite.config.cjs": "velite",
+ "velite.config.ts": "velite",
+ "velite.config.mts": "velite",
+ "velite.config.cts": "velite",
+ "lerna.json": "lerna",
+ "windi.config.js": "windicss",
+ "windi.config.cjs": "windicss",
+ "windi.config.ts": "windicss",
+ "windi.config.cts": "windicss",
+ "windi.config.json": "windicss",
+ ".textlintrc": "textlint",
+ ".textlintrc.js": "textlint",
+ ".textlintrc.cjs": "textlint",
+ ".textlintrc.json": "textlint",
+ ".textlintrc.yml": "textlint",
+ ".textlintrc.yaml": "textlint",
+ ".textlintignore": "textlint",
+ "vpkg.json": "vlang",
+ "v.mod": "vlang",
+ "sentry.client.config.js": "sentry",
+ "sentry.client.config.mjs": "sentry",
+ "sentry.client.config.cjs": "sentry",
+ "sentry.client.config.ts": "sentry",
+ "sentry.client.config.mts": "sentry",
+ "sentry.client.config.cts": "sentry",
+ "sentry.server.config.js": "sentry",
+ "sentry.server.config.mjs": "sentry",
+ "sentry.server.config.cjs": "sentry",
+ "sentry.server.config.ts": "sentry",
+ "sentry.server.config.mts": "sentry",
+ "sentry.server.config.cts": "sentry",
+ "sentry.edge.config.js": "sentry",
+ "sentry.edge.config.mjs": "sentry",
+ "sentry.edge.config.cjs": "sentry",
+ "sentry.edge.config.ts": "sentry",
+ "sentry.edge.config.mts": "sentry",
+ "sentry.edge.config.cts": "sentry",
+ ".sentryclirc": "sentry",
+ "contentlayer.config.js": "contentlayer",
+ "contentlayer.config.mjs": "contentlayer",
+ "contentlayer.config.cjs": "contentlayer",
+ "contentlayer.config.ts": "contentlayer",
+ "contentlayer.config.mts": "contentlayer",
+ "contentlayer.config.cts": "contentlayer",
+ ".phpunit.result.cache": "phpunit",
+ ".phpunit-watcher.yml": "phpunit",
+ "phpunit.xml": "phpunit",
+ "phpunit.xml.dist": "phpunit",
+ "phpunit-watcher.yml": "phpunit",
+ "phpunit-watcher.yml.dist": "phpunit",
+ ".php_cs": "php-cs-fixer",
+ ".php_cs.dist": "php-cs-fixer",
+ ".php_cs.php": "php-cs-fixer",
+ ".php_cs.dist.php": "php-cs-fixer",
+ ".php-cs-fixer.php": "php-cs-fixer",
+ ".php-cs-fixer.dist.php": "php-cs-fixer",
+ "robots.txt": "robots",
+ "tsconfig.json": "tsconfig",
+ "tsconfig.app.json": "tsconfig",
+ "tsconfig.editor.json": "tsconfig",
+ "tsconfig.spec.json": "tsconfig",
+ "tsconfig.base.json": "tsconfig",
+ "tsconfig.build.json": "tsconfig",
+ "tsconfig.eslint.json": "tsconfig",
+ "tsconfig.lib.json": "tsconfig",
+ "tsconfig.lib.prod.json": "tsconfig",
+ "tsconfig.node.json": "tsconfig",
+ "tsconfig.test.json": "tsconfig",
+ "tsconfig.e2e.json": "tsconfig",
+ "tsconfig.web.json": "tsconfig",
+ "tsconfig.webworker.json": "tsconfig",
+ "tsconfig.worker.json": "tsconfig",
+ "tsconfig.config.json": "tsconfig",
+ "tsconfig.vitest.json": "tsconfig",
+ "tsconfig.cjs.json": "tsconfig",
+ "tsconfig.esm.json": "tsconfig",
+ "tsconfig.mjs.json": "tsconfig",
+ "tsconfig.doc.json": "tsconfig",
+ "tsconfig.paths.json": "tsconfig",
+ "tsconfig.main.json": "tsconfig",
+ "tsconfig.renderer.json": "tsconfig",
+ "tsconfig.server.json": "tsconfig",
+ "tsconfig.client.json": "tsconfig",
+ "tsconfig.declaration.json": "tsconfig",
+ "tauri.conf.json": "tauri",
+ "tauri.config.json": "tauri",
+ "tauri.linux.conf.json": "tauri",
+ "tauri.windows.conf.json": "tauri",
+ "tauri.macos.conf.json": "tauri",
+ ".taurignore": "tauri",
+ "jsconfig.json": "jsconfig",
+ "maven.config": "maven",
+ "jvm.config": "maven",
+ "pom.xml": "maven",
+ "serverless.yml": "serverless",
+ "serverless.yaml": "serverless",
+ "serverless.json": "serverless",
+ "serverless.js": "serverless",
+ "serverless.ts": "serverless",
+ "supabase.js": "supabase",
+ "supabase.ts": "supabase",
+ "supabase.py": "supabase",
+ ".ember-cli": "ember",
+ ".ember-cli.js": "ember",
+ "ember-cli-builds.js": "ember",
+ "horusec-config.json": "horusec",
+ "poetry.lock": "poetry",
+ "pdm.lock": "pdm",
+ "pdm.toml": "pdm",
+ ".pdm-python": "pdm",
+ ".parcelrc": "parcel",
+ ".astylerc": "astyle",
+ ".lighthouserc.js": "lighthouse",
+ "lighthouserc.js": "lighthouse",
+ ".lighthouserc.cjs": "lighthouse",
+ "lighthouserc.cjs": "lighthouse",
+ ".lighthouserc.json": "lighthouse",
+ "lighthouserc.json": "lighthouse",
+ ".lighthouserc.yml": "lighthouse",
+ "lighthouserc.yml": "lighthouse",
+ ".lighthouserc.yaml": "lighthouse",
+ "lighthouserc.yaml": "lighthouse",
+ ".svgrrc": "svgr",
+ ".svgrrc.json": "svgr",
+ ".svgrrc.jsonc": "svgr",
+ ".svgrrc.json5": "svgr",
+ ".svgrrc.yaml": "svgr",
+ ".svgrrc.yml": "svgr",
+ ".svgrrc.toml": "svgr",
+ ".svgrrc.js": "svgr",
+ ".svgrrc.mjs": "svgr",
+ ".svgrrc.cjs": "svgr",
+ ".svgrrc.ts": "svgr",
+ ".svgrrc.mts": "svgr",
+ ".svgrrc.cts": "svgr",
+ ".config/svgrrc": "svgr",
+ ".config/svgrrc.json": "svgr",
+ ".config/svgrrc.jsonc": "svgr",
+ ".config/svgrrc.json5": "svgr",
+ ".config/svgrrc.yaml": "svgr",
+ ".config/svgrrc.yml": "svgr",
+ ".config/svgrrc.toml": "svgr",
+ ".config/svgrrc.js": "svgr",
+ ".config/svgrrc.mjs": "svgr",
+ ".config/svgrrc.cjs": "svgr",
+ ".config/svgrrc.ts": "svgr",
+ ".config/svgrrc.mts": "svgr",
+ ".config/svgrrc.cts": "svgr",
+ "svgr.config.json": "svgr",
+ "svgr.config.jsonc": "svgr",
+ "svgr.config.json5": "svgr",
+ "svgr.config.yaml": "svgr",
+ "svgr.config.yml": "svgr",
+ "svgr.config.toml": "svgr",
+ "svgr.config.js": "svgr",
+ "svgr.config.mjs": "svgr",
+ "svgr.config.cjs": "svgr",
+ "svgr.config.ts": "svgr",
+ "svgr.config.mts": "svgr",
+ "svgr.config.cts": "svgr",
+ "rome.json": "rome",
+ "cypress.config.js": "cypress",
+ "cypress.config.mjs": "cypress",
+ "cypress.config.cjs": "cypress",
+ "cypress.config.ts": "cypress",
+ "cypress.config.mts": "cypress",
+ "cypress.config.cts": "cypress",
+ "cypress.json": "cypress",
+ "cypress.env.json": "cypress",
+ "plopfile.js": "plop",
+ "plopfile.cjs": "plop",
+ "plopfile.mjs": "plop",
+ "plopfile.ts": "plop",
+ ".tobimake": "tobimake",
+ "gleam.toml": "gleam",
+ "pnpm-lock.yaml": "pnpm",
+ "pnpm-workspace.yaml": "pnpm",
+ ".pnpmfile.cjs": "pnpm",
+ "gridsome.config.js": "gridsome",
+ "gridsome.server.js": "gridsome",
+ ".steadybit.yml": "steadybit",
+ "steadybit.yml": "steadybit",
+ ".steadybit.yaml": "steadybit",
+ "steadybit.yaml": "steadybit",
+ "Caddyfile": "caddy",
+ "openapi.json": "openapi",
+ "openapi.yml": "openapi",
+ "openapi.yaml": "openapi",
+ "swagger.json": "swagger",
+ "swagger.yml": "swagger",
+ "swagger.yaml": "swagger",
+ "bun.lockb": "bun",
+ "bunfig.toml": "bun",
+ ".bun-version": "bun",
+ "bun.lock": "bun",
+ ".nano-staged.js": "nano-staged",
+ "nano-staged.js": "nano-staged",
+ ".nano-staged.cjs": "nano-staged",
+ "nano-staged.cjs": "nano-staged",
+ ".nano-staged.mjs": "nano-staged",
+ "nano-staged.mjs": "nano-staged",
+ ".nano-staged.json": "nano-staged",
+ "nano-staged.json": "nano-staged",
+ ".nanostagedrc": "nano-staged",
+ "knip.json": "knip",
+ "knip.jsonc": "knip",
+ ".knip.json": "knip",
+ ".knip.jsonc": "knip",
+ "knip.ts": "knip",
+ "knip.js": "knip",
+ "knip.config.ts": "knip",
+ "knip.config.js": "knip",
+ "taskfile.yml": "taskfile",
+ "taskfile.yaml": "taskfile",
+ "taskfile.dist.yml": "taskfile",
+ "taskfile.dist.yaml": "taskfile",
+ ".taskrc.yml": "taskfile",
+ ".taskrc.yaml": "taskfile",
+ ".cracorc": "craco",
+ ".cracorc.json": "craco",
+ ".cracorc.jsonc": "craco",
+ ".cracorc.json5": "craco",
+ ".cracorc.yaml": "craco",
+ ".cracorc.yml": "craco",
+ ".cracorc.toml": "craco",
+ ".cracorc.js": "craco",
+ ".cracorc.mjs": "craco",
+ ".cracorc.cjs": "craco",
+ ".cracorc.ts": "craco",
+ ".cracorc.mts": "craco",
+ ".cracorc.cts": "craco",
+ ".config/cracorc": "craco",
+ ".config/cracorc.json": "craco",
+ ".config/cracorc.jsonc": "craco",
+ ".config/cracorc.json5": "craco",
+ ".config/cracorc.yaml": "craco",
+ ".config/cracorc.yml": "craco",
+ ".config/cracorc.toml": "craco",
+ ".config/cracorc.js": "craco",
+ ".config/cracorc.mjs": "craco",
+ ".config/cracorc.cjs": "craco",
+ ".config/cracorc.ts": "craco",
+ ".config/cracorc.mts": "craco",
+ ".config/cracorc.cts": "craco",
+ "craco.config.json": "craco",
+ "craco.config.jsonc": "craco",
+ "craco.config.json5": "craco",
+ "craco.config.yaml": "craco",
+ "craco.config.yml": "craco",
+ "craco.config.toml": "craco",
+ "craco.config.js": "craco",
+ "craco.config.mjs": "craco",
+ "craco.config.cjs": "craco",
+ "craco.config.ts": "craco",
+ "craco.config.mts": "craco",
+ "craco.config.cts": "craco",
+ ".hg": "mercurial",
+ ".hgignore": "mercurial",
+ ".hgflow": "mercurial",
+ ".hgtags": "mercurial",
+ ".hgrc": "mercurial",
+ "hgrc": "mercurial",
+ "mercurial.ini": "mercurial",
+ "deno.json": "deno",
+ "deno.jsonc": "deno",
+ "deno.lock": "deno",
+ "plastic.branchexplorer": "plastic",
+ "plastic.selector": "plastic",
+ "plastic.wktree": "plastic",
+ "plastic.workspace": "plastic",
+ "plastic.workspaces": "plastic",
+ "typst.toml": "typst",
+ "uno.config.js": "unocss",
+ "uno.config.mjs": "unocss",
+ "uno.config.ts": "unocss",
+ "uno.config.mts": "unocss",
+ "unocss.config.js": "unocss",
+ "unocss.config.mjs": "unocss",
+ "unocss.config.ts": "unocss",
+ "unocss.config.mts": "unocss",
+ ".mincloudrc": "ifanr-cloud",
+ "concourse.yml": "concourse",
+ ".syncpackrc": "syncpack",
+ ".syncpackrc.json": "syncpack",
+ ".syncpackrc.jsonc": "syncpack",
+ ".syncpackrc.json5": "syncpack",
+ ".syncpackrc.yaml": "syncpack",
+ ".syncpackrc.yml": "syncpack",
+ ".syncpackrc.toml": "syncpack",
+ ".syncpackrc.js": "syncpack",
+ ".syncpackrc.mjs": "syncpack",
+ ".syncpackrc.cjs": "syncpack",
+ ".syncpackrc.ts": "syncpack",
+ ".syncpackrc.mts": "syncpack",
+ ".syncpackrc.cts": "syncpack",
+ ".config/syncpackrc": "syncpack",
+ ".config/syncpackrc.json": "syncpack",
+ ".config/syncpackrc.jsonc": "syncpack",
+ ".config/syncpackrc.json5": "syncpack",
+ ".config/syncpackrc.yaml": "syncpack",
+ ".config/syncpackrc.yml": "syncpack",
+ ".config/syncpackrc.toml": "syncpack",
+ ".config/syncpackrc.js": "syncpack",
+ ".config/syncpackrc.mjs": "syncpack",
+ ".config/syncpackrc.cjs": "syncpack",
+ ".config/syncpackrc.ts": "syncpack",
+ ".config/syncpackrc.mts": "syncpack",
+ ".config/syncpackrc.cts": "syncpack",
+ "syncpack.config.json": "syncpack",
+ "syncpack.config.jsonc": "syncpack",
+ "syncpack.config.json5": "syncpack",
+ "syncpack.config.yaml": "syncpack",
+ "syncpack.config.yml": "syncpack",
+ "syncpack.config.toml": "syncpack",
+ "syncpack.config.js": "syncpack",
+ "syncpack.config.mjs": "syncpack",
+ "syncpack.config.cjs": "syncpack",
+ "syncpack.config.ts": "syncpack",
+ "syncpack.config.mts": "syncpack",
+ "syncpack.config.cts": "syncpack",
+ "werf.yaml": "werf",
+ "werf.yml": "werf",
+ "werf-giterminism.yaml": "werf",
+ "werf-giterminism.yml": "werf",
+ ".luaurc": "luau",
+ "wally.toml": "wally",
+ "panda.config.js": "panda",
+ "panda.config.mjs": "panda",
+ "panda.config.cjs": "panda",
+ "panda.config.ts": "panda",
+ "panda.config.mts": "panda",
+ "panda.config.cts": "panda",
+ "biome.json": "biome",
+ "biome.jsonc": "biome",
+ "esbuild.js": "esbuild",
+ "esbuild.mjs": "esbuild",
+ "esbuild.cjs": "esbuild",
+ "esbuild.ts": "esbuild",
+ "esbuild.mts": "esbuild",
+ "esbuild.cts": "esbuild",
+ "esbuild.config.js": "esbuild",
+ "esbuild.config.mjs": "esbuild",
+ "esbuild.config.cjs": "esbuild",
+ "esbuild.config.ts": "esbuild",
+ "esbuild.config.mts": "esbuild",
+ "esbuild.config.cts": "esbuild",
+ "drizzle.config.ts": "drizzle",
+ "drizzle.config.js": "drizzle",
+ "drizzle.config.json": "drizzle",
+ ".puppeteerrc": "puppeteer",
+ ".puppeteerrc.json": "puppeteer",
+ ".puppeteerrc.jsonc": "puppeteer",
+ ".puppeteerrc.json5": "puppeteer",
+ ".puppeteerrc.yaml": "puppeteer",
+ ".puppeteerrc.yml": "puppeteer",
+ ".puppeteerrc.toml": "puppeteer",
+ ".puppeteerrc.js": "puppeteer",
+ ".puppeteerrc.mjs": "puppeteer",
+ ".puppeteerrc.cjs": "puppeteer",
+ ".puppeteerrc.ts": "puppeteer",
+ ".puppeteerrc.mts": "puppeteer",
+ ".puppeteerrc.cts": "puppeteer",
+ ".config/puppeteerrc": "puppeteer",
+ ".config/puppeteerrc.json": "puppeteer",
+ ".config/puppeteerrc.jsonc": "puppeteer",
+ ".config/puppeteerrc.json5": "puppeteer",
+ ".config/puppeteerrc.yaml": "puppeteer",
+ ".config/puppeteerrc.yml": "puppeteer",
+ ".config/puppeteerrc.toml": "puppeteer",
+ ".config/puppeteerrc.js": "puppeteer",
+ ".config/puppeteerrc.mjs": "puppeteer",
+ ".config/puppeteerrc.cjs": "puppeteer",
+ ".config/puppeteerrc.ts": "puppeteer",
+ ".config/puppeteerrc.mts": "puppeteer",
+ ".config/puppeteerrc.cts": "puppeteer",
+ "puppeteer.config.json": "puppeteer",
+ "puppeteer.config.jsonc": "puppeteer",
+ "puppeteer.config.json5": "puppeteer",
+ "puppeteer.config.yaml": "puppeteer",
+ "puppeteer.config.yml": "puppeteer",
+ "puppeteer.config.toml": "puppeteer",
+ "puppeteer.config.js": "puppeteer",
+ "puppeteer.config.mjs": "puppeteer",
+ "puppeteer.config.cjs": "puppeteer",
+ "puppeteer.config.ts": "puppeteer",
+ "puppeteer.config.mts": "puppeteer",
+ "puppeteer.config.cts": "puppeteer",
+ "garden.yml": "garden",
+ "garden.yaml": "garden",
+ "project.garden.yml": "garden",
+ "project.garden.yaml": "garden",
+ ".gardenignore": "garden",
+ "PklProject": "pkl",
+ "PklProject.deps.json": "pkl",
+ "k8s.yml": "kubernetes",
+ "k8s.yaml": "kubernetes",
+ "kubernetes.yml": "kubernetes",
+ "kubernetes.yaml": "kubernetes",
+ ".k8s.yml": "kubernetes",
+ ".k8s.yaml": "kubernetes",
+ "phpstan.neon": "phpstan",
+ "phpneon.neon.dist": "phpstan",
+ "screwdriver.yaml": "screwdriver",
+ "screwdriver.yml": "screwdriver",
+ "snapcraft.yaml": "snapcraft",
+ "snapcraft.yml": "snapcraft",
+ ".devcontainer/devcontainer.json": "container",
+ ".devcontainer/devcontainer-lock.json": "container",
+ "kcl.mod": "kcl",
+ "kcl.yaml": "kcl",
+ "kcl.yml": "kcl",
+ ".clangd": "clangd",
+ ".markdownlint.json": "markdownlint",
+ ".markdownlint.jsonc": "markdownlint",
+ ".markdownlint.yaml": "markdownlint",
+ ".markdownlint.yml": "markdownlint",
+ ".markdownlint-cli2.jsonc": "markdownlint",
+ ".markdownlint-cli2.yaml": "markdownlint",
+ ".markdownlint-cli2.cjs": "markdownlint",
+ ".markdownlint-cli2.mjs": "markdownlint",
+ ".markdownlintignore": "markdownlint",
+ "trigger.config.js": "trigger",
+ "trigger.config.mjs": "trigger",
+ "trigger.config.cjs": "trigger",
+ "trigger.config.ts": "trigger",
+ "trigger.config.mts": "trigger",
+ "trigger.config.cts": "trigger",
+ ".deepsource.toml": "deepsource",
+ "jsr.json": "jsr",
+ "jsr.jsonc": "jsr",
+ ".coderabbit.yml": "coderabbit-ai",
+ ".coderabbit.yaml": "coderabbit-ai",
+ ".aiexclude": "gemini-ai",
+ "taze.config.js": "taze",
+ "taze.config.mjs": "taze",
+ "taze.config.cjs": "taze",
+ "taze.config.ts": "taze",
+ "taze.config.mts": "taze",
+ "taze.config.cts": "taze",
+ ".tazerc": "taze",
+ ".tazerc.json": "taze",
+ "wxt.config.js": "wxt",
+ "wxt.config.mjs": "wxt",
+ "wxt.config.cjs": "wxt",
+ "wxt.config.ts": "wxt",
+ "wxt.config.mts": "wxt",
+ "wxt.config.cts": "wxt",
+ ".lefthook-local.json": "lefthook",
+ ".lefthook-local.toml": "lefthook",
+ ".lefthook-local.yaml": "lefthook",
+ ".lefthook-local.yml": "lefthook",
+ ".lefthook.json": "lefthook",
+ ".lefthook.toml": "lefthook",
+ ".lefthook.yaml": "lefthook",
+ ".lefthook.yml": "lefthook",
+ ".lefthookrc": "lefthook",
+ "lefthook-local.json": "lefthook",
+ "lefthook-local.toml": "lefthook",
+ "lefthook-local.yaml": "lefthook",
+ "lefthook-local.yml": "lefthook",
+ "lefthook.json": "lefthook",
+ "lefthook.toml": "lefthook",
+ "lefthook.yaml": "lefthook",
+ "lefthook.yml": "lefthook",
+ "lefthookrc": "lefthook",
+ ".github/labeler.yml": "label",
+ ".github/labeler.yaml": "label",
+ "zeabur.json": "zeabur",
+ "zeabur.jsonc": "zeabur",
+ "zeabur.json5": "zeabur",
+ "zeabur.yaml": "zeabur",
+ "zeabur.yml": "zeabur",
+ "zeabur.toml": "zeabur",
+ ".github/copilot-instructions.md": "copilot",
+ ".pre-commit-config.yaml": "pre-commit",
+ ".pre-commit-hooks.yaml": "pre-commit",
+ "histoire.config.js": "histoire",
+ "histoire.config.mjs": "histoire",
+ "histoire.config.cjs": "histoire",
+ "histoire.config.ts": "histoire",
+ "histoire.config.mts": "histoire",
+ "histoire.config.cts": "histoire",
+ ".histoire.js": "histoire",
+ ".histoire.mjs": "histoire",
+ ".histoire.cjs": "histoire",
+ ".histoire.ts": "histoire",
+ ".histoire.mts": "histoire",
+ ".histoire.cts": "histoire",
+ "install": "installation",
+ "installation": "installation",
+ ".github/FUNDING.yml": "github-sponsors",
+ "fabric.mod.json": "minecraft-fabric",
+ ".umirc.js": "umi",
+ ".umirc.mjs": "umi",
+ ".umirc.cjs": "umi",
+ ".umirc.ts": "umi",
+ ".umirc.mts": "umi",
+ ".umirc.cts": "umi",
+ "ecosystem.config.js": "pm2-ecosystem",
+ "ecosystem.config.mjs": "pm2-ecosystem",
+ "ecosystem.config.cjs": "pm2-ecosystem",
+ "ecosystem.config.ts": "pm2-ecosystem",
+ "ecosystem.config.mts": "pm2-ecosystem",
+ "ecosystem.config.cts": "pm2-ecosystem",
+ "hosts": "hosts",
+ "citation.cff": "citation",
+ "xmake.lua": "xmake",
+ "xmake": "xmake",
+ "wrangler.toml": "wrangler",
+ "wrangler.json": "wrangler",
+ "wrangler.jsonc": "wrangler",
+ ".clinerules": "cline",
+ ".packshiprc": "packship",
+ ".packshiprc.json": "packship",
+ ".packshiprc.js": "packship",
+ ".packshiprc.ts": "packship",
+ "packship.config.js": "packship",
+ "packship.config.ts": "packship",
+ "packship.config.mjs": "packship",
+ "packship.config.mts": "packship",
+ "packship.config.json": "packship",
+ "Snakefile": "snakemake",
+ ".hadolint.yaml": "hadolint",
+ ".hadolint.yml": "hadolint",
+ "hadolint.yaml": "hadolint",
+ "hadolint.yml": "hadolint",
+ ".rhistory": "r",
+ "cmakepresets.json": "cmake",
+ "cname": "http",
+ "sonarqube.analysis.xml": "sonarcloud",
+ "owners": "codeowners",
+ "caddyfile": "caddy",
+ "pklproject": "pkl",
+ "pklproject.deps.json": "pkl",
+ ".github/funding.yml": "github-sponsors",
+ "snakefile": "snakemake",
+ "language-configuration.json": "jsonc",
+ "icon-theme.json": "jsonc",
+ "color-theme.json": "jsonc",
+ "*.log.?": "log"
+ },
+ "languageIds": {
+ "git": "git",
+ "git-commit": "git",
+ "git-rebase": "git",
+ "ignore": "git",
+ "github-actions-workflow": "github-actions-workflow",
+ "yaml": "yaml",
+ "spring-boot-properties-yaml": "yaml",
+ "ansible": "yaml",
+ "ansible-jinja": "yaml",
+ "xml": "xml",
+ "xquery": "xml",
+ "xsl": "xml",
+ "matlab": "matlab",
+ "makefile": "settings",
+ "ini": "settings",
+ "properties": "settings",
+ "spring-boot-properties": "settings",
+ "toml": "toml",
+ "diff": "diff",
+ "json": "json",
+ "jsonc": "json",
+ "json5": "json",
+ "blink": "blink",
+ "java": "java",
+ "razor": "razor",
+ "aspnetcorerazor": "razor",
+ "python": "python",
+ "mojo": "mojo",
+ "javascript": "javascript",
+ "typescript": "typescript",
+ "ets": "typescript",
+ "scala": "scala",
+ "handlebars": "handlebars",
+ "perl": "perl",
+ "perl6": "perl",
+ "haxe": "haxe",
+ "hxml": "haxe",
+ "puppet": "puppet",
+ "elixir": "elixir",
+ "livescript": "livescript",
+ "erlang": "erlang",
+ "twig": "twig",
+ "julia": "julia",
+ "elm": "elm",
+ "purescript": "purescript",
+ "stylus": "stylus",
+ "nunjucks": "nunjucks",
+ "pug": "pug",
+ "robotframework": "robot",
+ "sass": "sass",
+ "scss": "sass",
+ "less": "less",
+ "css": "css",
+ "testOutput": "visualstudio",
+ "vb": "visualstudio",
+ "ng-template": "angular",
+ "graphql": "graphql",
+ "solidity": "solidity",
+ "autoit": "autoit",
+ "haml": "haml",
+ "yang": "yang",
+ "terraform": "terraform",
+ "applescript": "applescript",
+ "cake": "cake",
+ "cucumber": "cucumber",
+ "nim": "nim",
+ "nimble": "nim",
+ "apiblueprint": "apiblueprint",
+ "riot": "riot",
+ "postcss": "postcss",
+ "lang-cfml": "coldfusion",
+ "haskell": "haskell",
+ "dhall": "dhall",
+ "cabal": "cabal",
+ "nix": "nix",
+ "ruby": "ruby",
+ "slim": "slim",
+ "php": "php",
+ "hack": "hack",
+ "javascriptreact": "react",
+ "mjml": "mjml",
+ "processing": "processing",
+ "hcl": "hcl",
+ "go": "go",
+ "django-html": "django",
+ "django-txt": "django",
+ "html": "html",
+ "gdscript": "godot",
+ "gdresource": "godot-assets",
+ "gdshader": "godot-assets",
+ "viml": "vim",
+ "prolog": "prolog",
+ "pawn": "pawn",
+ "reason": "reason",
+ "reason_lisp": "reason",
+ "sml": "sml",
+ "tex": "tex",
+ "latex": "latex",
+ "latex-expl3": "latex",
+ "latex-class": "latex-class",
+ "latex-package": "latex-package",
+ "context": "context",
+ "doctex": "doctex",
+ "doctex-installer": "doctex-installer",
+ "bibtex": "bibliography",
+ "bibtex-style": "bibtex-style",
+ "apex": "salesforce",
+ "sas": "sas",
+ "dockerfile": "docker",
+ "dockercompose": "docker",
+ "dockerbake": "docker",
+ "csv": "table",
+ "tsv": "table",
+ "psv": "table",
+ "csharp": "csharp",
+ "bat": "console",
+ "awk": "console",
+ "shellscript": "console",
+ "c": "c",
+ "cpp": "cpp",
+ "objective-c": "objective-c",
+ "objective-cpp": "objective-cpp",
+ "c3": "c3",
+ "coffeescript": "coffee",
+ "fsharp": "fsharp",
+ "editorconfig": "editorconfig",
+ "clojure": "clojure",
+ "groovy": "groovy",
+ "markdoc": "markdoc",
+ "markdown": "markdown",
+ "jinja": "jinja",
+ "proto": "proto",
+ "pip-requirements": "python-misc",
+ "vue": "vue",
+ "vue-postcss": "vue",
+ "vue-html": "vue",
+ "lua": "lua",
+ "log": "log",
+ "jupyter": "jupyter",
+ "plaintext": "document",
+ "pdf": "pdf",
+ "powershell": "powershell",
+ "jade": "pug",
+ "r": "r",
+ "rsweave": "r",
+ "rust": "rust",
+ "sql": "database",
+ "kql": "kusto",
+ "ssh_config": "lock",
+ "svg": "svg",
+ "swift": "swift",
+ "typescriptreact": "react_ts",
+ "search-result": "search",
+ "mcfunction": "minecraft",
+ "rescript": "rescript",
+ "otne": "otne",
+ "twee3": "twine",
+ "twee3-harlowe-3": "twine",
+ "twee3-chapbook-1": "twine",
+ "twee3-sugarcube-2": "twine",
+ "grain": "grain",
+ "lolcode": "lolcode",
+ "idris": "idris",
+ "pgn": "chess",
+ "gemini": "gemini",
+ "text-gemini": "gemini",
+ "v": "vlang",
+ "wolfram": "wolframlanguage",
+ "shaderlab": "shader",
+ "hlsl": "shader",
+ "glsl": "shader",
+ "wgsl": "shader",
+ "tree": "tree",
+ "svelte": "svelte",
+ "dart": "dart",
+ "cadence": "cadence",
+ "stylable": "stylable",
+ "hjson": "hjson",
+ "huff": "huff",
+ "cds": "cds",
+ "capnb": "cds",
+ "cds-markdown-injection": "cds",
+ "concourse-pipeline-yaml": "concourse",
+ "concourse-task-yaml": "concourse",
+ "systemd-conf": "systemd",
+ "systemd-unit-file": "systemd",
+ "slint": "slint",
+ "luau": "luau",
+ "hosts": "hosts",
+ "beancount": "beancount",
+ "ahk2": "ahk2",
+ "gnuplot": "gnuplot"
+ },
+ "light": {
+ "fileExtensions": {
+ "blink": "blink_light",
+ "jinja": "jinja_light",
+ "jinja2": "jinja_light",
+ "j2": "jinja_light",
+ "jinja-html": "jinja_light",
+ "toml": "toml_light",
+ "huff": "huff_light",
+ "cr": "crystal_light",
+ "ecr": "crystal_light",
+ "drone.yml": "drone_light",
+ ".wakatime-project": "wakatime_light",
+ "hcl": "hcl_light",
+ "ai": "adobe-illustrator_light",
+ "ait": "adobe-illustrator_light",
+ "psd": "adobe-photoshop_light",
+ "psb": "adobe-photoshop_light",
+ "psdt": "adobe-photoshop_light",
+ "iuml": "uml_light",
+ "pu": "uml_light",
+ "puml": "uml_light",
+ "plantuml": "uml_light",
+ "wsd": "uml_light",
+ "pgn": "chess_light",
+ "fen": "chess_light",
+ "openapi.json": "openapi_light",
+ "openapi.yml": "openapi_light",
+ "openapi.yaml": "openapi_light",
+ "tldr": "tldraw_light",
+ "zeabur": "zeabur_light"
+ },
+ "fileNames": {
+ "sconstruct": "scons_light",
+ "sconscript": "scons_light",
+ "scsub": "scons_light",
+ ".rubocop.yml": "rubocop_light",
+ ".rubocop-todo.yml": "rubocop_light",
+ ".rubocop_todo.yml": "rubocop_light",
+ "vercel.json": "vercel_light",
+ ".vercelignore": "vercel_light",
+ "now.json": "vercel_light",
+ ".nowignore": "vercel_light",
+ "payload.config.js": "payload_light",
+ "payload.config.mjs": "payload_light",
+ "payload.config.ts": "payload_light",
+ "payload.config.mts": "payload_light",
+ "next.config.js": "next_light",
+ "next.config.mjs": "next_light",
+ "next.config.ts": "next_light",
+ "next.config.mts": "next_light",
+ "remix.config.js": "remix_light",
+ "remix.config.ts": "remix_light",
+ "turbo.json": "turborepo_light",
+ "turbo.jsonc": "turborepo_light",
+ ".autorc": "auto_light",
+ "auto.config.js": "auto_light",
+ "auto.config.ts": "auto_light",
+ "auto-config.json": "auto_light",
+ "auto-config.yaml": "auto_light",
+ "auto-config.yml": "auto_light",
+ "auto-config.ts": "auto_light",
+ "auto-config.js": "auto_light",
+ ".stylelintrc": "stylelint_light",
+ ".stylelintrc.json": "stylelint_light",
+ ".stylelintrc.jsonc": "stylelint_light",
+ ".stylelintrc.json5": "stylelint_light",
+ ".stylelintrc.yaml": "stylelint_light",
+ ".stylelintrc.yml": "stylelint_light",
+ ".stylelintrc.toml": "stylelint_light",
+ ".stylelintrc.js": "stylelint_light",
+ ".stylelintrc.mjs": "stylelint_light",
+ ".stylelintrc.cjs": "stylelint_light",
+ ".stylelintrc.ts": "stylelint_light",
+ ".stylelintrc.mts": "stylelint_light",
+ ".stylelintrc.cts": "stylelint_light",
+ ".config/stylelintrc": "stylelint_light",
+ ".config/stylelintrc.json": "stylelint_light",
+ ".config/stylelintrc.jsonc": "stylelint_light",
+ ".config/stylelintrc.json5": "stylelint_light",
+ ".config/stylelintrc.yaml": "stylelint_light",
+ ".config/stylelintrc.yml": "stylelint_light",
+ ".config/stylelintrc.toml": "stylelint_light",
+ ".config/stylelintrc.js": "stylelint_light",
+ ".config/stylelintrc.mjs": "stylelint_light",
+ ".config/stylelintrc.cjs": "stylelint_light",
+ ".config/stylelintrc.ts": "stylelint_light",
+ ".config/stylelintrc.mts": "stylelint_light",
+ ".config/stylelintrc.cts": "stylelint_light",
+ "stylelint.config.json": "stylelint_light",
+ "stylelint.config.jsonc": "stylelint_light",
+ "stylelint.config.json5": "stylelint_light",
+ "stylelint.config.yaml": "stylelint_light",
+ "stylelint.config.yml": "stylelint_light",
+ "stylelint.config.toml": "stylelint_light",
+ "stylelint.config.js": "stylelint_light",
+ "stylelint.config.mjs": "stylelint_light",
+ "stylelint.config.cjs": "stylelint_light",
+ "stylelint.config.ts": "stylelint_light",
+ "stylelint.config.mts": "stylelint_light",
+ "stylelint.config.cts": "stylelint_light",
+ ".stylelintignore": "stylelint_light",
+ ".stylelintcache": "stylelint_light",
+ ".codeclimate.yml": "code-climate_light",
+ "browserslist": "browserlist_light",
+ ".browserslistrc": "browserlist_light",
+ ".drone.yml": "drone_light",
+ ".wakatime-project": "wakatime_light",
+ "circle.yml": "circleci_light",
+ ".releaserc": "semantic-release_light",
+ ".releaserc.json": "semantic-release_light",
+ ".releaserc.jsonc": "semantic-release_light",
+ ".releaserc.json5": "semantic-release_light",
+ ".releaserc.yaml": "semantic-release_light",
+ ".releaserc.yml": "semantic-release_light",
+ ".releaserc.toml": "semantic-release_light",
+ ".releaserc.js": "semantic-release_light",
+ ".releaserc.mjs": "semantic-release_light",
+ ".releaserc.cjs": "semantic-release_light",
+ ".releaserc.ts": "semantic-release_light",
+ ".releaserc.mts": "semantic-release_light",
+ ".releaserc.cts": "semantic-release_light",
+ ".config/releaserc": "semantic-release_light",
+ ".config/releaserc.json": "semantic-release_light",
+ ".config/releaserc.jsonc": "semantic-release_light",
+ ".config/releaserc.json5": "semantic-release_light",
+ ".config/releaserc.yaml": "semantic-release_light",
+ ".config/releaserc.yml": "semantic-release_light",
+ ".config/releaserc.toml": "semantic-release_light",
+ ".config/releaserc.js": "semantic-release_light",
+ ".config/releaserc.mjs": "semantic-release_light",
+ ".config/releaserc.cjs": "semantic-release_light",
+ ".config/releaserc.ts": "semantic-release_light",
+ ".config/releaserc.mts": "semantic-release_light",
+ ".config/releaserc.cts": "semantic-release_light",
+ "release.config.json": "semantic-release_light",
+ "release.config.jsonc": "semantic-release_light",
+ "release.config.json5": "semantic-release_light",
+ "release.config.yaml": "semantic-release_light",
+ "release.config.yml": "semantic-release_light",
+ "release.config.toml": "semantic-release_light",
+ "release.config.js": "semantic-release_light",
+ "release.config.mjs": "semantic-release_light",
+ "release.config.cjs": "semantic-release_light",
+ "release.config.ts": "semantic-release_light",
+ "release.config.mts": "semantic-release_light",
+ "release.config.cts": "semantic-release_light",
+ "netlify.json": "netlify_light",
+ "netlify.yml": "netlify_light",
+ "netlify.yaml": "netlify_light",
+ "netlify.toml": "netlify_light",
+ "stitches.config.js": "stitches_light",
+ "stitches.config.ts": "stitches_light",
+ "snowpack.config.js": "snowpack_light",
+ "snowpack.config.cjs": "snowpack_light",
+ "snowpack.config.mjs": "snowpack_light",
+ "snowpack.config.ts": "snowpack_light",
+ "snowpack.config.cts": "snowpack_light",
+ "snowpack.config.mts": "snowpack_light",
+ "snowpack.deps.json": "snowpack_light",
+ "snowpack.config.json": "snowpack_light",
+ "pnpm-lock.yaml": "pnpm_light",
+ "pnpm-workspace.yaml": "pnpm_light",
+ ".pnpmfile.cjs": "pnpm_light",
+ "openapi.json": "openapi_light",
+ "openapi.yml": "openapi_light",
+ "openapi.yaml": "openapi_light",
+ "bun.lockb": "bun_light",
+ "bunfig.toml": "bun_light",
+ ".bun-version": "bun_light",
+ "bun.lock": "bun_light",
+ ".nano-staged.js": "nano-staged_light",
+ "nano-staged.js": "nano-staged_light",
+ ".nano-staged.cjs": "nano-staged_light",
+ "nano-staged.cjs": "nano-staged_light",
+ ".nano-staged.mjs": "nano-staged_light",
+ "nano-staged.mjs": "nano-staged_light",
+ ".nano-staged.json": "nano-staged_light",
+ "nano-staged.json": "nano-staged_light",
+ ".nanostagedrc": "nano-staged_light",
+ "deno.json": "deno_light",
+ "deno.jsonc": "deno_light",
+ "deno.lock": "deno_light",
+ "jsr.json": "jsr_light",
+ "jsr.jsonc": "jsr_light",
+ "zeabur.json": "zeabur_light",
+ "zeabur.jsonc": "zeabur_light",
+ "zeabur.json5": "zeabur_light",
+ "zeabur.yaml": "zeabur_light",
+ "zeabur.yml": "zeabur_light",
+ "zeabur.toml": "zeabur_light",
+ ".github/copilot-instructions.md": "copilot_light",
+ "hosts": "hosts_light"
+ },
+ "languageIds": {
+ "toml": "toml_light",
+ "systemd-conf": "systemd_light",
+ "systemd-unit-file": "systemd_light"
+ },
+ "folderNames": {
+ "jinja": "folder-jinja_light",
+ ".jinja": "folder-jinja_light",
+ "_jinja": "folder-jinja_light",
+ "__jinja__": "folder-jinja_light",
+ "jinja2": "folder-jinja_light",
+ ".jinja2": "folder-jinja_light",
+ "_jinja2": "folder-jinja_light",
+ "__jinja2__": "folder-jinja_light",
+ "j2": "folder-jinja_light",
+ ".j2": "folder-jinja_light",
+ "_j2": "folder-jinja_light",
+ "__j2__": "folder-jinja_light",
+ "idea": "folder-intellij_light",
+ ".idea": "folder-intellij_light",
+ "_idea": "folder-intellij_light",
+ "__idea__": "folder-intellij_light"
+ },
+ "folderNamesExpanded": {
+ "jinja": "folder-jinja-open_light",
+ ".jinja": "folder-jinja-open_light",
+ "_jinja": "folder-jinja-open_light",
+ "__jinja__": "folder-jinja-open_light",
+ "jinja2": "folder-jinja-open_light",
+ ".jinja2": "folder-jinja-open_light",
+ "_jinja2": "folder-jinja-open_light",
+ "__jinja2__": "folder-jinja-open_light",
+ "j2": "folder-jinja-open_light",
+ ".j2": "folder-jinja-open_light",
+ "_j2": "folder-jinja-open_light",
+ "__j2__": "folder-jinja-open_light",
+ "idea": "folder-intellij-open_light",
+ ".idea": "folder-intellij-open_light",
+ "_idea": "folder-intellij-open_light",
+ "__idea__": "folder-intellij-open_light"
+ },
+ "rootFolderNames": {},
+ "rootFolderNamesExpanded": {}
+ },
+ "highContrast": {
+ "fileExtensions": {},
+ "fileNames": {}
+ },
+ "file": "file",
+ "hidesExplorerArrows": false,
+ "folder": "folder",
+ "folderExpanded": "folder-open",
+ "rootFolder": "folder-root",
+ "rootFolderExpanded": "folder-root-open"
+} \ No newline at end of file
diff --git a/options/fileicon/material-icon-svgs.json b/options/fileicon/material-icon-svgs.json
new file mode 100644
index 0000000000..326e0a1b91
--- /dev/null
+++ b/options/fileicon/material-icon-svgs.json
@@ -0,0 +1,1122 @@
+{
+ "3d": "<svg viewBox='0 0 24 24'><path fill='#29b6f6' d='M21 16.5c0 .38-.21.71-.53.88l-7.9 4.44c-.16.12-.36.18-.57.18s-.41-.06-.57-.18l-7.9-4.44A.99.99 0 0 1 3 16.5v-9c0-.38.21-.71.53-.88l7.9-4.44c.16-.12.36-.18.57-.18s.41.06.57.18l7.9 4.44c.32.17.53.5.53.88zM12 4.15 6.04 7.5 12 10.85l5.96-3.35zM5 15.91l6 3.38v-6.71L5 9.21zm14 0v-6.7l-6 3.37v6.71z'/></svg>",
+ "abap": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M2 10v12h14l14-12'/></svg>",
+ "abc": "<svg viewBox='0 0 24 24'><path fill='#ff5722' d='M13.295 11.033V7.65l2.126-2.136c.774-.763.919-1.981.377-2.929a2.38 2.38 0 0 0-2.068-1.217c-.203 0-.435.029-.619.087-1.044.28-1.749 1.246-1.749 2.33v3.13L8.327 9.98a5.75 5.75 0 0 0-1.208 6.214 5.62 5.62 0 0 0 4.243 3.432v.59a.5.5 0 0 1-.483.482h-1.45v1.934h1.45a2.43 2.43 0 0 0 2.416-2.417v-.483c1.962 0 4.02-1.856 4.02-4.591 0-2.223-1.855-4.108-4.02-4.108m0-7.249c0-.222.106-.396.31-.454a.47.47 0 0 1 .54.222.48.48 0 0 1-.077.59l-.773.83V3.785m-1.933 7.732c-.938.619-1.643 1.682-1.894 2.668l1.894.503v2.948a3.73 3.73 0 0 1-2.484-2.185 3.8 3.8 0 0 1 .802-4.098l1.682-1.769zm1.933 6.283v-4.89c1.13 0 2.107 1.062 2.107 2.232 0 1.691-1.227 2.658-2.107 2.658'/></svg>",
+ "actionscript": "<svg viewBox='0 -960 960 960'><path fill='#f44336' d='M560-160v-80h120q17 0 28.5-11.5T720-280v-80q0-38 22-69t58-44v-14q-36-13-58-44t-22-69v-80q0-17-11.5-28.5T680-720H560v-80h120q50 0 85 35t35 85v80q0 17 11.5 28.5T840-560h40v160h-40q-17 0-28.5 11.5T800-360v80q0 50-35 85t-85 35zm-280 0q-50 0-85-35t-35-85v-80q0-17-11.5-28.5T120-400H80v-160h40q17 0 28.5-11.5T160-600v-80q0-50 35-85t85-35h120v80H280q-17 0-28.5 11.5T240-680v80q0 38-22 69t-58 44v14q36 13 58 44t22 69v80q0 17 11.5 28.5T280-240h120v80z'/><path fill='#f44336' d='M360-600h80v40h-80zm80 240h40v-200h-40v80h-80v-80h-40v200h40v-80h80zm200-200v-40H530a10 10 0 0 0-10 10v100a10 10 0 0 0 10 10h70v80h-80v40h110a10 10 0 0 0 10-10v-140a10 10 0 0 0-10-10h-70v-40z'/></svg>",
+ "ada": "<svg viewBox='0 0 24 24'><path fill='#0277bd' d='m2 12 2.9-1.07c.25-1.1.87-1.73.87-1.73a3.996 3.996 0 0 1 5.65 0l1.41 1.41 6.31-6.7c.95 3.81 0 7.62-2.33 10.69L22 19.62s-8.47 1.9-13.4-1.95c-2.63-2.06-3.22-3.26-3.59-4.52zm5.04.21c.37.37.98.37 1.35 0s.37-.97 0-1.34a.96.96 0 0 0-1.35 0c-.37.37-.37.97 0 1.34'/></svg>",
+ "adobe-illustrator": "<svg viewBox='0 0 32 32'><rect width='28' height='28' x='2' y='2' fill='#5d4037' rx='4'/><path fill='#ffb74d' d='M20.988 9.999a.96.96 0 0 1-.687-.269 1 1 0 0 1-.263-.704.9.9 0 0 1 .278-.681 1 1 0 0 1 .687-.268.93.93 0 0 1 .703.268 1.046 1.046 0 0 1-.015 1.385.9.9 0 0 1-.703.268M20 12h2v10h-2zm-5.63-1.98-.01-.02h-2.08a.12.12 0 0 0-.1.13 4.5 4.5 0 0 1-.06.74c-.05.13-.08.26-.12.37l-.27.78L8 22h2.14l.75-2h5.24l.79 2h2.16zM11.64 18l1.8-4.84.01.04.02.04L14.95 17l.39 1z'/></svg>",
+ "adobe-illustrator_light": "<svg viewBox='0 0 32 32'><rect width='28' height='28' x='2' y='2' fill='#795548' rx='4'/><path fill='#ffb74d' d='M20.988 9.999a.96.96 0 0 1-.687-.269 1 1 0 0 1-.263-.704.9.9 0 0 1 .278-.681 1 1 0 0 1 .687-.268.93.93 0 0 1 .703.268 1.046 1.046 0 0 1-.015 1.385.9.9 0 0 1-.703.268M20 12h2v10h-2zm-5.63-1.98-.01-.02h-2.08a.12.12 0 0 0-.1.13 4.5 4.5 0 0 1-.06.74c-.05.13-.08.26-.12.37l-.27.78L8 22h2.14l.75-2h5.24l.79 2h2.16zM11.64 18l1.8-4.84.01.04.02.04L14.95 17l.39 1z'/></svg>",
+ "adobe-photoshop": "<svg viewBox='0 0 32 32'><rect width='28' height='28' x='2' y='2' fill='#37474f' rx='4'/><path fill='#64b5f6' d='M23.744 14.716a3.7 3.7 0 0 0-1.066-.408 5.4 5.4 0 0 0-1.245-.157 2.1 2.1 0 0 0-.666.085.57.57 0 0 0-.345.24.7.7 0 0 0-.089.324.56.56 0 0 0 .111.313 1.3 1.3 0 0 0 .378.324q.386.217.79.397a7.8 7.8 0 0 1 1.71.877 2.7 2.7 0 0 1 .878.998 2.8 2.8 0 0 1 .256 1.238 2.96 2.96 0 0 1-.434 1.599 2.83 2.83 0 0 1-1.244 1.07 4.75 4.75 0 0 1-2.011.384 7 7 0 0 1-1.511-.156 4.2 4.2 0 0 1-1.134-.385.24.24 0 0 1-.122-.228v-2.092a.14.14 0 0 1 .044-.108c.034-.024.067-.012.1.012a4.6 4.6 0 0 0 1.378.59 4.8 4.8 0 0 0 1.311.18 2 2 0 0 0 .923-.169.56.56 0 0 0 .3-.505.65.65 0 0 0-.267-.48 4.6 4.6 0 0 0-1.089-.565 6.6 6.6 0 0 1-1.578-.866 3 3 0 0 1-.844-1.021 2.76 2.76 0 0 1-.256-1.226 3 3 0 0 1 .378-1.455 2.8 2.8 0 0 1 1.167-1.105A4 4 0 0 1 21.533 12a9 9 0 0 1 1.378.108 3.7 3.7 0 0 1 .956.277.2.2 0 0 1 .11.108.7.7 0 0 1 .023.144v1.96a.15.15 0 0 1-.056.12.28.28 0 0 1-.2 0M12.38 10H9.99v-.03h-2v12h2V18h2.39A3.62 3.62 0 0 0 16 14.38v-.76A3.62 3.62 0 0 0 12.38 10M14 14.38A1.626 1.626 0 0 1 12.38 16H9.99v-4h2.39A1.626 1.626 0 0 1 14 13.62Z'/></svg>",
+ "adobe-photoshop_light": "<svg viewBox='0 0 32 32'><rect width='28' height='28' x='2' y='2' fill='#455a64' rx='4'/><path fill='#64b5f6' d='M23.744 14.716a3.7 3.7 0 0 0-1.066-.408 5.4 5.4 0 0 0-1.245-.157 2.1 2.1 0 0 0-.666.085.57.57 0 0 0-.345.24.7.7 0 0 0-.089.324.56.56 0 0 0 .111.313 1.3 1.3 0 0 0 .378.324q.386.217.79.397a7.8 7.8 0 0 1 1.71.877 2.7 2.7 0 0 1 .878.998 2.8 2.8 0 0 1 .256 1.238 2.96 2.96 0 0 1-.434 1.599 2.83 2.83 0 0 1-1.244 1.07 4.75 4.75 0 0 1-2.011.384 7 7 0 0 1-1.511-.156 4.2 4.2 0 0 1-1.134-.385.24.24 0 0 1-.122-.228v-2.092a.14.14 0 0 1 .044-.108c.034-.024.067-.012.1.012a4.6 4.6 0 0 0 1.378.59 4.8 4.8 0 0 0 1.311.18 2 2 0 0 0 .923-.169.56.56 0 0 0 .3-.505.65.65 0 0 0-.267-.48 4.6 4.6 0 0 0-1.089-.565 6.6 6.6 0 0 1-1.578-.866 3 3 0 0 1-.844-1.021 2.76 2.76 0 0 1-.256-1.226 3 3 0 0 1 .378-1.455 2.8 2.8 0 0 1 1.167-1.105A4 4 0 0 1 21.533 12a9 9 0 0 1 1.378.108 3.7 3.7 0 0 1 .956.277.2.2 0 0 1 .11.108.7.7 0 0 1 .023.144v1.96a.15.15 0 0 1-.056.12.28.28 0 0 1-.2 0M12.38 10H9.99v-.03h-2v12h2V18h2.39A3.62 3.62 0 0 0 16 14.38v-.76A3.62 3.62 0 0 0 12.38 10M14 14.38A1.626 1.626 0 0 1 12.38 16H9.99v-4h2.39A1.626 1.626 0 0 1 14 13.62Z'/></svg>",
+ "adobe-swc": "<svg viewBox='0 0 32 32'><path fill='#e53935' d='M4 5v22a1 1 0 0 0 1 1h22a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1m20 7c-2.926 0-4.21.722-5.012 2H22v4h-4.582C16.34 20.857 14.393 24 8 24v-4c4.559 0 5.14-1.744 6.103-4.632C15.139 12.258 16.559 8 24 8Z'/></svg>",
+ "adonis": "<svg viewBox='0 0 180 180'><path fill='#7c4dff' d='m79.579 25.741-66.481 115.15h63.305l11.218-19.433H47.613L79.804 65.7l20.005 34.649 11.423-19.783zm42.118 50.221-45.203 78.297h90.408z' paint-order='fill markers stroke'/></svg>",
+ "advpl-include.clone": "<svg viewBox='0 0 16 16'><path fill='#00bcd4' fill-rule='evenodd' d='M6.752 1.158C2.234 1.96-.271 6.943 1.758 11.09c2.537 5.185 10.047 5.142 12.511-.07C16.69 5.9 12.321.17 6.752 1.159m.587 2.335c2.576.517 5.233 1.323 5.326 1.615.26.808.256 4.849-.004 5.34-.066.125-1.209-.012-2.08-.247l-.351-.094-.001-.437c-.005-1.308-.138-2.547-.29-2.7-.176-.176-1.312-.545-3.052-.99L5.78 5.697l-.014-.267c-.033-.6.117-1.95.232-2.093.063-.079.315-.05 1.34.157M4.029 5.39c.5.066 1.083.178 1.492.289l.178.048.03.984c.058 1.844.117 2.13.475 2.29.448.2 2.083.679 3.62 1.061l.34.084-.01.653c-.012.735-.083 1.393-.175 1.617l-.062.15-.261-.03c-.976-.113-4.175-.896-5.567-1.362-.611-.205-.759-.284-.811-.435-.23-.66-.23-4.905 0-5.337.054-.1.08-.1.75-.012'/></svg>",
+ "advpl-ptm.clone": "<svg viewBox='0 0 16 16'><path fill='#ef5350' fill-rule='evenodd' d='M6.752 1.158C2.234 1.96-.271 6.943 1.758 11.09c2.537 5.185 10.047 5.142 12.511-.07C16.69 5.9 12.321.17 6.752 1.159m.587 2.335c2.576.517 5.233 1.323 5.326 1.615.26.808.256 4.849-.004 5.34-.066.125-1.209-.012-2.08-.247l-.351-.094-.001-.437c-.005-1.308-.138-2.547-.29-2.7-.176-.176-1.312-.545-3.052-.99L5.78 5.697l-.014-.267c-.033-.6.117-1.95.232-2.093.063-.079.315-.05 1.34.157M4.029 5.39c.5.066 1.083.178 1.492.289l.178.048.03.984c.058 1.844.117 2.13.475 2.29.448.2 2.083.679 3.62 1.061l.34.084-.01.653c-.012.735-.083 1.393-.175 1.617l-.062.15-.261-.03c-.976-.113-4.175-.896-5.567-1.362-.611-.205-.759-.284-.811-.435-.23-.66-.23-4.905 0-5.337.054-.1.08-.1.75-.012'/></svg>",
+ "advpl-tlpp.clone": "<svg viewBox='0 0 16 16'><path fill='#fbc02d' fill-rule='evenodd' d='M6.752 1.158C2.234 1.96-.271 6.943 1.758 11.09c2.537 5.185 10.047 5.142 12.511-.07C16.69 5.9 12.321.17 6.752 1.159m.587 2.335c2.576.517 5.233 1.323 5.326 1.615.26.808.256 4.849-.004 5.34-.066.125-1.209-.012-2.08-.247l-.351-.094-.001-.437c-.005-1.308-.138-2.547-.29-2.7-.176-.176-1.312-.545-3.052-.99L5.78 5.697l-.014-.267c-.033-.6.117-1.95.232-2.093.063-.079.315-.05 1.34.157M4.029 5.39c.5.066 1.083.178 1.492.289l.178.048.03.984c.058 1.844.117 2.13.475 2.29.448.2 2.083.679 3.62 1.061l.34.084-.01.653c-.012.735-.083 1.393-.175 1.617l-.062.15-.261-.03c-.976-.113-4.175-.896-5.567-1.362-.611-.205-.759-.284-.811-.435-.23-.66-.23-4.905 0-5.337.054-.1.08-.1.75-.012'/></svg>",
+ "advpl": "<svg viewBox='0 0 16 16'><path fill='#7986cb' fill-rule='evenodd' d='M6.752 1.158C2.234 1.96-.271 6.943 1.758 11.09c2.537 5.185 10.047 5.142 12.511-.07C16.69 5.9 12.321.17 6.752 1.159m.587 2.335c2.576.517 5.233 1.323 5.326 1.615.26.808.256 4.849-.004 5.34-.066.125-1.209-.012-2.08-.247l-.351-.094-.001-.437c-.005-1.308-.138-2.547-.29-2.7-.176-.176-1.312-.545-3.052-.99L5.78 5.697l-.014-.267c-.033-.6.117-1.95.232-2.093.063-.079.315-.05 1.34.157M4.029 5.39c.5.066 1.083.178 1.492.289l.178.048.03.984c.058 1.844.117 2.13.475 2.29.448.2 2.083.679 3.62 1.061l.34.084-.01.653c-.012.735-.083 1.393-.175 1.617l-.062.15-.261-.03c-.976-.113-4.175-.896-5.567-1.362-.611-.205-.759-.284-.811-.435-.23-.66-.23-4.905 0-5.337.054-.1.08-.1.75-.012'/></svg>",
+ "ahk2.clone": "<svg viewBox='0 0 32 32'><path fill='#afb42b' d='M25.333 4H6.667A2.657 2.657 0 0 0 4 6.667v18.666A2.667 2.667 0 0 0 6.667 28h18.666A2.667 2.667 0 0 0 28 25.333V6.667A2.667 2.667 0 0 0 25.333 4m-2.495 6.22a4 4 0 0 0-.163 1.01q0 .266-.058.83a9 9 0 0 0-.04.719c0 .584-.031 1.443-.092 2.55q-.074 1.253-.088 2.502c0 .412.032 1.057.097 1.91.067.865.1 1.534.1 1.988a1.62 1.62 0 0 1-.505 1.197 1.65 1.65 0 0 1-1.225.475 1.92 1.92 0 0 1-1.233-.466 1.51 1.51 0 0 1-.554-1.19q0-.644-.06-1.934-.047-.99-.056-1.979 0-.198.003-.376c-.805.065-1.766.198-2.867.398q-1.522.277-3.045.562-.032.61-.11 1.65a30 30 0 0 0-.087 2.017 1.62 1.62 0 0 1-.506 1.192 1.73 1.73 0 0 1-1.224.474l-.048.001a1.7 1.7 0 0 1-1.157-.479 1.62 1.62 0 0 1-.502-1.2c0-.615.05-1.513.155-2.738.104-1.182.157-2.077.157-2.661q0-1.15.057-3.46.054-2.302.053-3.442a1.62 1.62 0 0 1 .508-1.196 1.68 1.68 0 0 1 1.222-.478 1.7 1.7 0 0 1 1.206.484 1.63 1.63 0 0 1 .5 1.19q0 .687-.055 2.07-.036 1.01-.044 2.023.001.23-.065.905a7 7 0 0 0-.022.251l2.825-.532a28 28 0 0 1 3.086-.395q.037-.83.095-2.76a4.8 4.8 0 0 1 .466-1.778c.421-.926.957-1.395 1.591-1.395a1.75 1.75 0 0 1 1.166.434l.003.002a1.58 1.58 0 0 1 .566 1.228 1.5 1.5 0 0 1-.05.397'/></svg>",
+ "amplify": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='M14 10 5 28h12l-2-4h-4l3-6 5 10h4zm1-2 2-4 12 24h-4l-8-16z'/></svg>",
+ "android": "<svg viewBox='0 0 32 32'><rect width='4' height='10' x='2' y='12' fill='#8bc34a' rx='2'/><rect width='4' height='10' x='26' y='12' fill='#8bc34a' rx='2'/><path fill='#8bc34a' d='M8 12h16v12H8zm2 12h4v4a2 2 0 0 1-2 2 2 2 0 0 1-2-2zm8 0h4v4a2 2 0 0 1-2 2 2 2 0 0 1-2-2zm3.545-19.759 2.12-2.12A1 1 0 0 0 22.251.707l-2.326 2.326a7.97 7.97 0 0 0-7.85 0L9.75.707a1 1 0 1 0-1.414 1.414l2.12 2.12A7.97 7.97 0 0 0 8 10h16a7.97 7.97 0 0 0-2.455-5.759M14 8h-2V6h2Zm6 0h-2V6h2Z'/></svg>",
+ "angular-component.clone": "<svg viewBox='0 0 24 24'><path fill='#1976d2' d='M9.87 2.5 3.022 5.666l.645 10.178zm4.26 0 6.202 13.344.645-10.178zM12 7.563l-2.451 5.964h4.906zm-3.73 8.959-.954 2.308L12 21.5l4.683-2.67-.953-2.308z'/></svg>",
+ "angular-directive.clone": "<svg viewBox='0 0 24 24'><path fill='#ab47bc' d='M9.87 2.5 3.022 5.666l.645 10.178zm4.26 0 6.202 13.344.645-10.178zM12 7.563l-2.451 5.964h4.906zm-3.73 8.959-.954 2.308L12 21.5l4.683-2.67-.953-2.308z'/></svg>",
+ "angular-guard.clone": "<svg viewBox='0 0 24 24'><path fill='#43a047' d='M9.87 2.5 3.022 5.666l.645 10.178zm4.26 0 6.202 13.344.645-10.178zM12 7.563l-2.451 5.964h4.906zm-3.73 8.959-.954 2.308L12 21.5l4.683-2.67-.953-2.308z'/></svg>",
+ "angular-interceptor.clone": "<svg viewBox='0 0 24 24'><path fill='#ff9800' d='M9.87 2.5 3.022 5.666l.645 10.178zm4.26 0 6.202 13.344.645-10.178zM12 7.563l-2.451 5.964h4.906zm-3.73 8.959-.954 2.308L12 21.5l4.683-2.67-.953-2.308z'/></svg>",
+ "angular-pipe.clone": "<svg viewBox='0 0 24 24'><path fill='#00897b' d='M9.87 2.5 3.022 5.666l.645 10.178zm4.26 0 6.202 13.344.645-10.178zM12 7.563l-2.451 5.964h4.906zm-3.73 8.959-.954 2.308L12 21.5l4.683-2.67-.953-2.308z'/></svg>",
+ "angular-resolver.clone": "<svg viewBox='0 0 24 24'><path fill='#43a047' d='M9.87 2.5 3.022 5.666l.645 10.178zm4.26 0 6.202 13.344.645-10.178zM12 7.563l-2.451 5.964h4.906zm-3.73 8.959-.954 2.308L12 21.5l4.683-2.67-.953-2.308z'/></svg>",
+ "angular-service.clone": "<svg viewBox='0 0 24 24'><path fill='#ffca28' d='M9.87 2.5 3.022 5.666l.645 10.178zm4.26 0 6.202 13.344.645-10.178zM12 7.563l-2.451 5.964h4.906zm-3.73 8.959-.954 2.308L12 21.5l4.683-2.67-.953-2.308z'/></svg>",
+ "angular": "<svg viewBox='0 0 24 24'><path fill='#e53935' d='M9.87 2.5 3.022 5.666l.645 10.178zm4.26 0 6.202 13.344.645-10.178zM12 7.563l-2.451 5.964h4.906zm-3.73 8.959-.954 2.308L12 21.5l4.683-2.67-.953-2.308z'/></svg>",
+ "antlr": "<svg viewBox='0 0 24 24'><path fill='#f44336' d='M10.355 1.614a10.469 10.483 0 0 1 11.813 7.792 10.327 10.34 0 0 1-1.565 8.673 10.583 10.597 0 0 1-14.819 2.428 10.416 10.43 0 0 1-4.222-7.14 10.641 10.656 0 0 1 .999-5.994 10.498 10.512 0 0 1 7.795-5.76m.27 3.825c-.949 2.08-1.9 4.16-2.83 6.25-.479 1.345-1.127 2.615-1.716 3.915-.174.408-.468.853-.287 1.312a1.088 1.09 0 0 0 1.575.556c.458-.261.566-.828.778-1.272.952-2.405 2.13-4.708 3.11-7.104a7.356 7.366 0 0 1 .776-1.6c.568 1.406 1.186 2.791 1.773 4.19a14.819 14.839 0 0 1 .969 2.197c-1.51-.015-3.02-.004-4.531-.01 2.073 1.233 4.202 2.379 6.305 3.562a1.094 1.094 0 0 0 1.698-1.036c-.425-1.15-1.014-2.237-1.5-3.364-.917-2.393-2.076-4.685-3.097-7.036a2.685 2.689 0 0 0-.738-1.163 1.564 1.566 0 0 0-2.285.602z'/></svg>",
+ "apiblueprint": "<svg viewBox='0 0 32 32'><rect width='12' height='12' x='10' y='2' fill='#42a5f5' rx='6'/><rect width='12' height='12' x='18' y='18' fill='#42a5f5' rx='6'/><rect width='12' height='12' x='2' y='18' fill='#42a5f5' rx='6'/><path fill='none' stroke='#42a5f5' stroke-miterlimit='10' stroke-width='3' d='m16 8 8 16M16 8 8 24'/></svg>",
+ "apollo": "<svg viewBox='0 0 32 32'><path fill='#7e57c2' d='M31.93 14.457a.51.51 0 0 0-.506-.457h-2.01a.497.497 0 0 0-.491.559l.014.134c.616 6.284-4.097 12.817-10.29 14.044A13.009 13.009 0 1 1 24.3 6h4.19a16.013 16.013 0 1 0 3.44 8.457'/><circle cx='24.533' cy='4.267' r='4.267' fill='#7e57c2'/><path fill='#7e57c2' d='M17 8h-3L8 24h3z'/><path fill='#7e57c2' d='M15 8h3l6 16h-3zm2.88 13H12v-3h4.75z'/></svg>",
+ "applescript": "<svg viewBox='0 0 32 32'><path fill='#78909c' d='M25.425 26.498c-1.162 1.736-2.394 3.43-4.27 3.458-1.875.042-2.477-1.106-4.605-1.106-2.142 0-2.8 1.078-4.578 1.148-1.834.07-3.22-1.848-4.396-3.542C5.183 23 3.35 16.63 5.813 12.346a6.84 6.84 0 0 1 5.767-3.514c1.792-.028 3.5 1.217 4.606 1.217 1.092 0 3.164-1.497 5.334-1.273a6.5 6.5 0 0 1 5.095 2.771 6.38 6.38 0 0 0-3.01 5.334 6.18 6.18 0 0 0 3.752 5.656 15.5 15.5 0 0 1-1.932 3.961M17.432 4.1A6.36 6.36 0 0 1 21.548 2a6.13 6.13 0 0 1-1.456 4.466 5.11 5.11 0 0 1-4.13 1.988 5.98 5.98 0 0 1 1.47-4.354'/></svg>",
+ "apps-script": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='M6.053 20.055H21.21a3.01 3.01 0 0 1 3.049 2.966 3.01 3.01 0 0 1-3.049 2.966H6.053a3.01 3.01 0 0 1-3.049-2.966 3.01 3.01 0 0 1 3.049-2.966'/><path fill='#ffc107' d='M19.44 25.433 7.179 16.765a2.914 2.914 0 0 1-.674-4.143 3.104 3.104 0 0 1 4.258-.656l12.263 8.668a2.914 2.914 0 0 1 .674 4.143 3.104 3.104 0 0 1-4.258.656Z'/><path fill='#43a047' d='m19.489 8.05 4.683 14.026a2.95 2.95 0 0 1-1.957 3.737 3.067 3.067 0 0 1-3.841-1.904L13.69 9.884a2.95 2.95 0 0 1 1.957-3.738 3.067 3.067 0 0 1 3.842 1.905Z'/><path fill='#448aff' d='M18.363 22.076 23.047 8.05a3.067 3.067 0 0 1 3.841-1.904 2.95 2.95 0 0 1 1.958 3.737L24.162 23.91a3.067 3.067 0 0 1-3.842 1.904 2.95 2.95 0 0 1-1.957-3.737Z'/></svg>",
+ "appveyor": "<svg preserveAspectRatio='xMidYMid' viewBox='0 0 256 256'><path fill='#00b8d4' fill-rule='evenodd' d='M127.646 17.356c61.588 0 110.999 49.414 110.999 110.29a110.64 110.64 0 0 1-110.999 110.999c-60.873 0-110.29-49.414-110.29-110.999 0-60.873 49.414-110.29 110.29-110.29m27.213 131.77c-12.174 15.756-34.375 18.62-49.414 6.446-15.039-11.459-17.187-33.66-5.013-49.414 12.891-15.039 35.091-17.904 50.131-6.445 15.039 12.174 17.187 34.375 4.297 49.414zm-58.723 72.331 42.252-40.82c-15.756 3.58-32.227.716-45.117-10.026-15.039-11.459-21.484-30.795-19.336-48.699L35.98 163.45s-5.013-9.31-6.446-26.498l66.602-52.278c20.052-14.323 47.266-15.04 66.602 0 21.484 17.187 25.781 48.698 10.027 72.33l-48.699 69.466c-7.161 0-21.484-2.149-27.93-5.013'/></svg>",
+ "architecture": "<svg fill='none' viewBox='0 0 24 24'><path fill='#66bb6a' d='M6.278 22 6 19.556l3.167-8.723a4.37 4.37 0 0 0 1.944 1.056l-3.055 8.389zm11.666 0-1.777-1.722-3.056-8.39q.556-.138 1.042-.402a4.4 4.4 0 0 0 .903-.653l3.166 8.723zm-5.833-11.111q-1.389 0-2.361-.972-.972-.973-.972-2.361 0-1.084.624-1.932.626-.846 1.598-1.18V2h2.222v2.444a3.27 3.27 0 0 1 1.598 1.18q.624.849.624 1.932 0 1.389-.972 2.36-.972.973-2.36.973Zm0-2.222q.473 0 .792-.32t.32-.791q0-.473-.32-.793a1.08 1.08 0 0 0-.792-.319q-.472 0-.791.32t-.32.792.32.79q.319.32.791.32Z'/></svg>",
+ "arduino": "<svg viewBox='0 0 32 32'><path fill='#0097a7' d='M2 14h10v2H2zm22-4h2v10h-2z'/><path fill='#0097a7' d='M20 14h10v2H20z'/><path fill='none' stroke='#0097a7' stroke-width='2' d='M2 5h4a10 10 0 0 1 10 10 10 10 0 0 0 10 10h4'/><path fill='#0097a7' d='M11.644 22A8.95 8.95 0 0 1 6 24H2v2h4a10.98 10.98 0 0 0 8.479-4ZM26 4a10.98 10.98 0 0 0-8.479 4h2.835A8.95 8.95 0 0 1 26 6h4V4Z'/></svg>",
+ "asciidoc": "<svg viewBox='0 0 32 32'><path fill='#0097a7' d='M4 18V8l5.39 10Zm0 4v3.67A2.33 2.33 0 0 0 6.33 28h8.9l-3.496-6Zm12.444 0 3.177 5.444A11.88 11.88 0 0 0 26.448 22Zm11.419-4A15 15 0 0 0 28 16 12 12 0 0 0 16 4L6 3.995q-.08 0-.158.005L14 18Z'/></svg>",
+ "assembly": "<svg viewBox='0 0 32 32'><path fill='#ff6e40' d='M8 6V2H4a2 2 0 0 0-2 2v24a2 2 0 0 0 2 2h4v-4H4V6Zm16-4v4h4v20h-4v4h4a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2Zm-4 4h-2a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2m-2 6V8h2v4Zm-4 6h-2a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2m-2 6v-4h2v4Zm0-18c0 2 0 2-2 2v2h2v4h2V6Zm8 12c0 2 0 2-2 2v2h2v4h2v-8Z'/></svg>",
+ "astro-config": "<svg viewBox='0 0 32 32'><path fill='#757575' d='M15 2H6a2.006 2.006 0 0 0-2 2v22a2.006 2.006 0 0 0 2 2h6v-4H6v-2h6v-2H6v-2h6v-2H6v-2h6v-2h2V4l8 8h2v-1Z'/><path fill='#7c4dff' d='M12 12v18h18V12Zm10 16c-.9 0-2.025-1.267-2.025-3.005-.914 0-.975.464-.975 1.005-.881-.213-1-1.15-1-2h6c0 1.919-2 1.787-2 4m2.542-6a2.5 2.5 0 0 1-2.308-1.641l-.946-2.42a.305.305 0 0 0-.576 0l-.946 2.42A2.5 2.5 0 0 1 17.458 22H16l2.965-7.59a.63.63 0 0 1 .577-.41h2.916a.63.63 0 0 1 .577.41L26 22Z'/></svg>",
+ "astro": "<svg viewBox='0 0 32 32'><path fill='#7c4dff' d='M12.106 25.849c-1.262-1.156-1.63-3.586-1.105-5.346a5.18 5.18 0 0 0 3.484 1.66 9.68 9.68 0 0 0 5.882-.734c.215-.106.413-.247.648-.39a3.5 3.5 0 0 1 .16 1.555 4.26 4.26 0 0 1-1.798 3.021c-.404.3-.832.569-1.25.852a2.613 2.613 0 0 0-1.15 3.372l.048.161a3.4 3.4 0 0 1-1.5-1.285 3.6 3.6 0 0 1-.578-1.962 9 9 0 0 0-.05-1.037c-.114-.831-.504-1.204-1.238-1.225a1.45 1.45 0 0 0-1.507 1.18c-.012.056-.028.112-.046.178M4.901 20a17.75 17.75 0 0 1 7.4-2l2.913-8.38a.765.765 0 0 1 1.527 0L19.7 18a14.24 14.24 0 0 1 7.399 2S20.704 2.877 20.692 2.842C20.51 2.33 20.202 2 19.787 2h-7.619c-.415 0-.71.33-.904.842z'/></svg>",
+ "astyle": "<svg viewBox='0 0 24 24'><path fill='#ef5350' d='M8.203 5.447 5.83 6.777l1.329-2.374-1.33-2.374 2.374 1.33 2.374-1.33-1.33 2.374 1.33 2.374zm11.394 9.305 2.374-1.329-1.33 2.374 1.33 2.373-2.374-1.329-2.374 1.33 1.33-2.374-1.33-2.374zm2.374-12.724-1.33 2.374 1.33 2.374-2.374-1.33-2.374 1.33 1.33-2.374-1.33-2.374 2.374 1.33zm-8.223 10.236 2.317-2.316-2.013-2.013-2.317 2.317zm.978-5.212 2.222 2.222c.37.35.37.968 0 1.338L5.867 21.694c-.37.37-.987.37-1.339 0l-2.222-2.221c-.37-.352-.37-.969 0-1.34l11.081-11.08c.37-.37.988-.37 1.34 0z'/></svg>",
+ "audio": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M16 2a14 14 0 1 0 14 14A14 14 0 0 0 16 2m6 10h-4v8a4 4 0 1 1-4-4 3.96 3.96 0 0 1 2 .555V8h6Z'/></svg>",
+ "aurelia": "<svg xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 24 24'><defs><linearGradient xlink:href='#a' id='i' x1='-31.824' x2='19.682' y1='-11.741' y2='35.548' gradientTransform='scale(.95818 1.0436)' gradientUnits='userSpaceOnUse'/><linearGradient id='a' x1='-3.881' x2='2.377' y1='-1.442' y2='4.304'><stop offset='0' stop-color='#ba68c8'/><stop offset='1' stop-color='#7e57c2'/></linearGradient><linearGradient xlink:href='#b' id='j' x1='12.022' x2='-15.716' y1='13.922' y2='-23.952' gradientTransform='scale(.96226 1.0392)' gradientUnits='userSpaceOnUse'/><linearGradient id='b' x1='.729' x2='-.971' y1='.844' y2='-1.477'><stop offset='0' stop-color='#5e35b1'/><stop offset='.14' stop-color='#8e24aa'/><stop offset='.29' stop-color='#ad1457'/><stop offset='.84' stop-color='#c2185b'/><stop offset='1' stop-color='#ec407a'/></linearGradient><linearGradient xlink:href='#c' id='k' x1='-23.39' x2='23.931' y1='-57.289' y2='8.573' gradientTransform='scale(1.0429 .95884)' gradientUnits='userSpaceOnUse'/><linearGradient id='c' x1='-2.839' x2='2.875' y1='-6.936' y2='1.017'><stop offset='0' stop-color='#ba68c8'/><stop offset='1' stop-color='#7e57c2'/></linearGradient><linearGradient xlink:href='#d' id='l' x1='-53.331' x2='6.771' y1='-30.517' y2='18.785' gradientTransform='scale(.99898 1.001)' gradientUnits='userSpaceOnUse'/><linearGradient id='d' x1='-8.212' x2='1.02' y1='-4.691' y2='2.882'><stop offset='0' stop-color='#ba68c8'/><stop offset='1' stop-color='#7e57c2'/></linearGradient><linearGradient xlink:href='#e' id='m' x1='-14.029' x2='41.998' y1='-23.111' y2='26.259' gradientTransform='scale(1.0003 .99965)' gradientUnits='userSpaceOnUse'/><linearGradient id='e' x1='-1.404' x2='4.19' y1='-2.309' y2='2.62'><stop offset='0' stop-color='#ba68c8'/><stop offset='1' stop-color='#7e57c2'/></linearGradient><linearGradient xlink:href='#f' id='n' x1='31.177' x2='3.37' y1='41.442' y2='3.402' gradientTransform='scale(.96254 1.0389)' gradientUnits='userSpaceOnUse'/><linearGradient id='f' x1='1.911' x2='.204' y1='2.539' y2='.204'><stop offset='0' stop-color='#7e57c2'/><stop offset='.14' stop-color='#7b1fa2'/><stop offset='.29' stop-color='#ad1457'/><stop offset='.84' stop-color='#c2185b'/><stop offset='1' stop-color='#ec407a'/></linearGradient><linearGradient xlink:href='#g' id='o' x1='-31.905' x2='19.599' y1='-14.258' y2='42.767' gradientTransform='scale(.95823 1.0436)' gradientUnits='userSpaceOnUse'/><linearGradient id='g' x1='-3.881' x2='2.377' y1='-1.738' y2='5.19'><stop offset='0' stop-color='#ba68c8'/><stop offset='1' stop-color='#7e57c2'/></linearGradient><linearGradient xlink:href='#h' id='p' x1='4.301' x2='34.534' y1='34.41' y2='4.514' gradientTransform='scale(1.002 .99796)' gradientUnits='userSpaceOnUse'/><linearGradient id='h' x1='.112' x2='.901' y1='.897' y2='.116'><stop offset='0' stop-color='#7e57c2'/><stop offset='.14' stop-color='#8e24aa'/><stop offset='.53' stop-color='#c2185b'/><stop offset='.79' stop-color='#c2185b'/><stop offset='1' stop-color='#ec407a'/></linearGradient></defs><g stroke-linejoin='round' stroke-miterlimit='1.414' clip-rule='evenodd'><path fill='url(#i)' d='M8.002 6.127 4.117 8.719.116 2.723 4 .13z' transform='translate(11.282 3.07)scale(.47102)'/><path fill='url(#j)' d='m9.179 1.887 6.637 9.946-7.906 5.276-6.637-9.946L.115 5.43 8.02.153z' transform='translate(12.215 13.552)scale(.47102)'/><path fill='url(#k)' d='m7.3 1.88 1.462 2.189-6.018 4.015L.124 4.16l1.315-.877L6.143.144z' transform='translate(8.41 16.686)scale(.47102)'/><path fill='url(#l)' d='M2.328 1.146 4.016.02l2.619 3.925L2.75 6.537l-1.46-2.19 2.197-1.466zm-1.04 3.201L.132 2.612l2.197-1.466 1.158 1.735z' transform='translate(16.99 11.686)scale(.47102)'/><path fill='url(#m)' d='m5.346 9.155-1.315.877L.03 4.035 6.047.019l2.805 4.204L4.15 7.36l4.703-3.138 1.197 1.793z' transform='translate(2.738 8.18)scale(.47102)'/><path fill='url(#n)' d='m14.533 9.934 1.197 1.793-7.907 5.276-1.196-1.793L.052 5.358 7.958.082z' transform='translate(4.753 2.36)scale(.47102)'/><path fill='url(#o)' d='M6.235 7.177 4.038 8.643 2.84 6.849.036 2.646 3.92.053 7.923 6.05z' transform='translate(11.32 3.106)scale(.47102)'/><path fill='#673ab7' d='m9.632 19.05-.545-.818 2.215-1.478.546.817zm7.965-5.315-.545-.817 1.035-.691.545.817z'/><path fill='#7e57c2' d='m5.256 12.492-.564-.845 2.216-1.478.563.845zm7.965-5.315-.564-.845 1.035-.69.564.844z'/><path fill='#880e4f' d='m16.538 14.441-3.724 2.485-.545-.817 3.724-2.485z'/><path fill='#ad1457' d='m11.598 7.039.564.844-3.724 2.485-.564-.844z'/><path fill='#ab47bc' d='m4.2 6.363.703 1.054-1.053.702-.703-1.053z'/><path fill='#7e57c2' d='m7.996 18.99.703 1.054-1.054.703-.702-1.054z'/><path fill='url(#p)' d='M8.372 38.294.017 29.876 29.749.08l8.636 8.201z' transform='rotate(11.282 -5.61 25.53)scale(.47102)'/></g></svg>",
+ "authors": "<svg viewBox='0 0 24 24'><path fill='#f44336' d='M15.787 13.71c-.275 0-.587 0-.918.047 1.098.796 1.865 1.847 1.865 3.267v2.367h5.68v-2.367c0-2.206-4.42-3.314-6.627-3.314m-7.575 0c-2.206 0-6.628 1.108-6.628 3.314v2.367H14.84v-2.367c0-2.206-4.421-3.314-6.628-3.314m0-1.894a2.84 2.84 0 0 0 2.841-2.84 2.84 2.84 0 0 0-2.84-2.84 2.84 2.84 0 0 0-2.841 2.84 2.84 2.84 0 0 0 2.84 2.84m7.575 0a2.84 2.84 0 0 0 2.84-2.84 2.84 2.84 0 0 0-2.84-2.84 2.84 2.84 0 0 0-2.84 2.84 2.84 2.84 0 0 0 2.84 2.84'/></svg>",
+ "auto": "<svg viewBox='0 0 24 24'><path fill='#ffc400' d='M8.48 4.17c.334.574 1.047.798 1.696.636A7.5 7.5 0 0 1 12 4.583c.62 0 1.223.075 1.799.217.647.159 1.357-.065 1.691-.64.39-.668.116-1.532-.63-1.751A10.1 10.1 0 0 0 12 2c-1.006 0-1.977.146-2.894.419-.743.22-1.015 1.083-.627 1.75Z'/><path fill='#ad1457' d='M5.039 4.772c.564-.535 1.458-.34 1.848.331.333.572.176 1.292-.284 1.769a7.4 7.4 0 0 0-1.456 2.17c-.242.552-.762.958-1.367.958-.854 0-1.496-.781-1.191-1.572a10 10 0 0 1 2.45-3.656'/><path fill='#cfd8dc' d='M3.197 12c.718 0 1.32.583 1.444 1.286.613 3.483 3.675 6.13 7.359 6.13s6.746-2.647 7.359-6.13c.124-.703.726-1.286 1.444-1.286.719 0 1.279.581 1.187 1.289C21.353 18.203 17.123 22 12 22s-9.353-3.797-9.99-8.711C1.918 12.58 2.478 12 3.197 12'/><path fill='#ff5252' d='M20.203 9.958c.857 0 1.5-.786 1.19-1.578a10 10 0 0 0-2.458-3.632c-.564-.533-1.455-.336-1.845.333-.333.573-.174 1.295.289 1.772a7.4 7.4 0 0 1 1.459 2.155c.243.548.762.95 1.365.95'/><path fill='#cfd8dc' d='M7.133 9.32c-.442-.488.053-1.262.657-1.027l4.912 1.91c1.114.434 1.538 1.84.862 2.855a1.785 1.785 0 0 1-2.83.222l-3.6-3.96Z'/></svg>",
+ "auto_light": "<svg viewBox='0 0 24 24'><path fill='#ffc400' d='M8.48 4.17c.334.574 1.047.798 1.696.636A7.5 7.5 0 0 1 12 4.583c.62 0 1.223.075 1.799.217.647.159 1.357-.065 1.691-.64.39-.668.116-1.532-.63-1.751A10.1 10.1 0 0 0 12 2c-1.006 0-1.977.146-2.894.419-.743.22-1.015 1.083-.627 1.75Z'/><path fill='#ad1457' d='M5.039 4.772c.564-.535 1.458-.34 1.848.331.333.572.176 1.292-.284 1.769a7.4 7.4 0 0 0-1.456 2.17c-.242.552-.762.958-1.367.958-.854 0-1.496-.781-1.191-1.572a10 10 0 0 1 2.45-3.656'/><path fill='#546e7a' d='M3.197 12c.718 0 1.32.583 1.444 1.286.613 3.483 3.675 6.13 7.359 6.13s6.746-2.647 7.359-6.13c.124-.703.726-1.286 1.444-1.286.719 0 1.279.581 1.187 1.289C21.353 18.203 17.123 22 12 22s-9.353-3.797-9.99-8.711C1.918 12.58 2.478 12 3.197 12'/><path fill='#ff5252' d='M20.203 9.958c.857 0 1.5-.786 1.19-1.578a10 10 0 0 0-2.458-3.632c-.564-.533-1.455-.336-1.845.333-.333.573-.174 1.295.289 1.772a7.4 7.4 0 0 1 1.459 2.155c.243.548.762.95 1.365.95'/><path fill='#546e7a' d='M7.133 9.32c-.442-.488.053-1.262.657-1.027l4.912 1.91c1.114.434 1.538 1.84.862 2.855a1.785 1.785 0 0 1-2.83.222l-3.6-3.96Z'/></svg>",
+ "autohotkey": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='M25.333 4H6.667A2.657 2.657 0 0 0 4 6.667v18.666A2.667 2.667 0 0 0 6.667 28h18.666A2.667 2.667 0 0 0 28 25.333V6.667A2.667 2.667 0 0 0 25.333 4m-2.495 6.22a4 4 0 0 0-.163 1.01q0 .266-.058.83a9 9 0 0 0-.04.719c0 .584-.031 1.443-.092 2.55q-.074 1.253-.088 2.502c0 .412.032 1.057.097 1.91.067.865.1 1.534.1 1.988a1.62 1.62 0 0 1-.505 1.197 1.65 1.65 0 0 1-1.225.475 1.92 1.92 0 0 1-1.233-.466 1.51 1.51 0 0 1-.554-1.19q0-.644-.06-1.934-.047-.99-.056-1.979 0-.198.003-.376c-.805.065-1.766.198-2.867.398q-1.522.277-3.045.562-.032.61-.11 1.65a30 30 0 0 0-.087 2.017 1.62 1.62 0 0 1-.506 1.192 1.73 1.73 0 0 1-1.224.474l-.048.001a1.7 1.7 0 0 1-1.157-.479 1.62 1.62 0 0 1-.502-1.2c0-.615.05-1.513.155-2.738.104-1.182.157-2.077.157-2.661q0-1.15.057-3.46.054-2.302.053-3.442a1.62 1.62 0 0 1 .508-1.196 1.68 1.68 0 0 1 1.222-.478 1.7 1.7 0 0 1 1.206.484 1.63 1.63 0 0 1 .5 1.19q0 .687-.055 2.07-.036 1.01-.044 2.023.001.23-.065.905a7 7 0 0 0-.022.251l2.825-.532a28 28 0 0 1 3.086-.395q.037-.83.095-2.76a4.8 4.8 0 0 1 .466-1.778c.421-.926.957-1.395 1.591-1.395a1.75 1.75 0 0 1 1.166.434l.003.002a1.58 1.58 0 0 1 .566 1.228 1.5 1.5 0 0 1-.05.397'/></svg>",
+ "autoit": "<svg viewBox='0 0 24 24'><path fill='#1976d2' d='M12.002 2a10 10 0 0 0-10 10 10 10 0 0 0 10 10 10 10 0 0 0 10-10 10 10 0 0 0-10-10m.139 4.419q.642 0 1.07.294.431.294.731.731l5.71 8.262H9.026l1.707-2.35h3.15q.443 0 .77.028a11 11 0 0 1-.443-.62q-.253-.376-.485-.704l-1.64-2.417-4.29 6.063H4.45l5.86-8.262q.285-.396.723-.71.437-.315 1.108-.315'/></svg>",
+ "azure-pipelines": "<svg viewBox='0 0 32 32'><path fill='#64b5f6' d='M3.98 22.01h1.803v4.208H9.99v1.803H3.98Z'/><path fill='#1565c0' d='M3.98 10.991v5.51l3.505 3.61 1.503-1.606 4.508 4.508-1.502 1.502 3.506 3.506h5.51a1 1 0 0 0 1.001-1.002v-8.014L12.995 9.99H4.982a1 1 0 0 0-1.003 1.001Z'/><path fill='#1e88e5' d='M8.317 18.44a1 1 0 0 1-.125-1.265L16.407 4.87a2 2 0 0 1 1.666-.891h8.946A1 1 0 0 1 28.02 4.98v8.946a2 2 0 0 1-.891 1.667l-12.305 8.215a1 1 0 0 1-1.265-.126Z'/><path fill='#64b5f6' d='m8.976 21.542 7.648-7.648 1.48 1.481-7.647 7.648Z'/><path fill='#42a5f5' d='m11.68 21.801-1.481-1.48 6.426-6.427 1.48 1.481Z'/><path fill='#90caf9' d='M22.011 12.995a3.006 3.006 0 0 0 .096-6.011h-.096a3.006 3.006 0 0 0 0 6.01Z'/></svg>",
+ "azure": "<svg viewBox='0 0 32 32'><path fill='#01579b' d='M12.001 4h7.102l-7.372 23.181a1.14 1.14 0 0 1-1.073.819H5.13A1.166 1.166 0 0 1 4 26.801a1.3 1.3 0 0 1 .06-.385l6.87-21.599A1.14 1.14 0 0 1 12.001 4'/><path fill='#1976d2' d='M22.32 20H11.06a.537.537 0 0 0-.522.55.57.57 0 0 0 .166.408l7.236 6.716a1.1 1.1 0 0 0 .775.325h6.376Z'/><path fill='#29b6f6' d='M21.071 4.816A1.14 1.14 0 0 0 20.001 4h-7.915a1.14 1.14 0 0 1 1.072.815l6.868 21.599a1.22 1.22 0 0 1-.71 1.52 1.1 1.1 0 0 1-.362.064h7.915A1.166 1.166 0 0 0 28 26.8a1.3 1.3 0 0 0-.06-.385L21.072 4.817Z'/></svg>",
+ "babel": "<svg viewBox='0 0 24 24'><path fill='#fdd835' d='M18.23 11.21q-.045-.24-1.32-1.65c-.02-.19.29-.45.9-.8l1.74-1.55c.39-.5.62-1.28.69-2.38l-.02-.26c-.07-.78-.63-1.4-1.69-1.89-.63-.42-1.76-.65-3.38-.68-1.35.11-3.11.59-5.28 1.43-.6.43-1.28.86-2.04 1.28l.01.14.21-.08c.08-.01.13.03.14.11l.13-.07.07-.01.01.06c0 .07-.47.44-1.76 1.35l-.06.12c-.31.02-.61.25-.91.67l.08.12.25-.09.18.24c.32-.33.66-.62 1.03-.87.19.05.29.11.44.16 1.02-.75 2.03-1.3 3.04-1.64l.01.14c-.2.27-.32.42-.38.42l.1.23c.01.19-2.55 7-6.66 14.44l.08.19c.35-.08.58-.17.75-.26l.01.13.4-.03-.67 1.76.14.06c.57-.64 1-1.29 1.3-1.88 1.67-.49 2.94-.97 3.82-1.44.88-.08 1.56-.31 2.02-.7l.92-.47c1.27-.98 2.22-1.67 2.87-2.08 1.33-.98 2.2-1.93 2.6-2.85zm-3.46 2.31L13 14.91c-1.29.85-2 1.3-2.09 1.3-2.07 1.13-3.36 1.72-3.86 1.76l-.05.01c.04-.23.96-2.12 2.75-5.67.78-.06 2.02-.43 3.71-1.1l.41-.03c.85-.08 1.49.09 1.91.49l.03.26c-.31.9-.67 1.44-1.04 1.59m1.09-5.78q-.27.33-1.5 1.11c-.27.03-1.27.42-3.01 1.18l-.28-.05-.01-.12c-.02-.25.09-.57.34-.95.13-.7.28-1.12.44-1.2l1.45-3.28c-.02-.22.29-.35.93-.46l.21-.02.01.18 1.16-.16c1.15-.1 1.75.14 1.8.7l.13-.02-.03-.32.15-.02c.35.19.52.4.54.68.02.18-.08.41-.29.68-.09.01-.14-.06-.15-.18l-.14.01-.03.4c-.58.87-1.01 1.31-1.27 1.34z'/></svg>",
+ "ballerina": "<svg viewBox='0 0 32 32'><path fill='#00bfa5' d='m14 12-6-2V2h6Zm-6 0 4 2.058L8 16Zm0 18V18l6-2v4l-2 10Zm10-18 6-2V2h-6Zm6 0-4 2.058L24 16Zm0 18V18l-6-2v4l2 10Z'/></svg>",
+ "bazel": "<svg viewBox='0 0 512 512'><path fill='#81c784' d='m153.491 50.983 102.508 102.508-102.508 102.508L50.983 153.491z'/><path fill='#43a047' d='M50.983 153.491v102.508l102.508 102.508V255.999z'/><path fill='#81c784' d='m358.507 50.983 102.508 102.508-102.508 102.508-102.508-102.508z'/><path fill='#43a047' d='M461.015 153.491v102.508L358.507 358.507V255.999zm-205.016 0 102.508 102.508-102.508 102.508-102.508-102.508z'/><path fill='#2e7d32' d='M255.999 358.507v102.508L153.491 358.507V255.999z'/><path fill='#1b5e20' d='m255.999 358.507 102.508-102.508v102.508L255.999 461.015z'/></svg>",
+ "bbx": "<svg viewBox='0 0 1024 1024'><path fill='#c62828' d='M128 704v128c0 70.692 57.308 128 128 128h608c17.728 0 32-14.272 32-32V704z'/><path fill='#ffe082' d='M704 704v192h128V704z'/><path fill='#fff8e1' d='M192 704v96c0 53.184 42.816 96 96 96h544a96 96 0 0 1-96-96 96 96 0 0 1 96-96z'/><path fill='#ff1744' d='M320 832h192v192l-96-96-96 96z'/><path fill='#f44336' d='M256 64c-70.692 0-128 57.308-128 128v640c0 11.088 1.557 21.787 4.207 32.047C146.767 807.565 197.672 768.07 256 768h608c17.728 0 32-14.272 32-32V96c0-17.728-14.272-32-32-32z'/><path fill='#ffeb3b' d='M256 192c-70.912 0-128 57.088-128 128v64c0-70.912 57.088-128 128-128h448v320H256c-70.912 0-128 57.088-128 128v64c0-70.912 57.088-128 128-128h512V192z'/></svg>",
+ "beancount": "<svg viewBox='0 0 32 32'><path fill='#e64a19' d='M26.471 5.736c7.383 3.577 2.04 13.636-5.547 17.984-5.998 3.44-18.128 5.76-18.877-2.22-.738-7.863 7.61-6.698 11.575-8.67 4.032-2.003 6.854-9.998 12.85-7.093zm-11.684 8.89c-1.167.438-3.695.194-3.479 2.094.215 1.932 3.483.908 5.243.097 1.788-.82 3.415-2.475 2.27-3.496-1.424-1.268-2.421.698-4.034 1.305'/></svg>",
+ "bench-js": "<svg viewBox='0 0 16 16'><path fill='#ffca28' d='M6.915 9.906q.42.413 1.084.404t.98-.472l3.92-5.775-5.88 3.85q-.472.309-.498.945t.394 1.048M7.999 2q1.033 0 1.987.284.953.283 1.793.85l-1.33.825q-.577-.292-1.198-.438-.622-.146-1.252-.146-2.327 0-3.963 1.607T2.4 8.875q0 .722.201 1.427.201.704.569 1.323h9.659q.402-.653.586-1.358t.184-1.46q0-.62-.149-1.204t-.446-1.134l.84-1.307q.525.808.831 1.72.306.91.324 1.89.017.98-.228 1.873-.245.894-.717 1.702-.193.31-.525.481-.333.172-.7.172h-9.66q-.367 0-.7-.172t-.524-.481q-.455-.774-.7-1.642T1 8.875q0-1.427.551-2.673T3.056 4.02q.954-.937 2.231-1.479Q6.565 2 8 2zm.123 5.38'/></svg>",
+ "bench-jsx": "<svg viewBox='0 0 16 16'><path fill='#00bcd4' d='M6.915 9.906q.42.413 1.084.404t.98-.472l3.92-5.775-5.88 3.85q-.472.309-.498.945t.394 1.048M7.999 2q1.033 0 1.987.284.953.283 1.793.85l-1.33.825q-.577-.292-1.198-.438-.622-.146-1.252-.146-2.327 0-3.963 1.607T2.4 8.875q0 .722.201 1.427.201.704.569 1.323h9.659q.402-.653.586-1.358t.184-1.46q0-.62-.149-1.204t-.446-1.134l.84-1.307q.525.808.831 1.72.306.91.324 1.89.017.98-.228 1.873-.245.894-.717 1.702-.193.31-.525.481-.333.172-.7.172h-9.66q-.367 0-.7-.172t-.524-.481q-.455-.774-.7-1.642T1 8.875q0-1.427.551-2.673T3.056 4.02q.954-.937 2.231-1.479Q6.565 2 8 2zm.123 5.38'/></svg>",
+ "bench-ts": "<svg viewBox='0 0 16 16'><path fill='#0288d1' d='M6.915 9.906q.42.413 1.084.404t.98-.472l3.92-5.775-5.88 3.85q-.472.309-.498.945t.394 1.048M7.999 2q1.033 0 1.987.284.953.283 1.793.85l-1.33.825q-.577-.292-1.198-.438-.622-.146-1.252-.146-2.327 0-3.963 1.607T2.4 8.875q0 .722.201 1.427.201.704.569 1.323h9.659q.402-.653.586-1.358t.184-1.46q0-.62-.149-1.204t-.446-1.134l.84-1.307q.525.808.831 1.72.306.91.324 1.89.017.98-.228 1.873-.245.894-.717 1.702-.193.31-.525.481-.333.172-.7.172h-9.66q-.367 0-.7-.172t-.524-.481q-.455-.774-.7-1.642T1 8.875q0-1.427.551-2.673T3.056 4.02q.954-.937 2.231-1.479Q6.565 2 8 2zm.123 5.38'/></svg>",
+ "bibliography": "<svg viewBox='0 0 1024 1024'><path fill='#795548' d='M96 832h832c17.728 0 32 14.272 32 32v64c0 17.728-14.272 32-32 32H96c-17.728 0-32-14.272-32-32v-64c0-17.728 14.272-32 32-32'/><path fill='#4caf50' d='M160 192h64c17.728 0 32 14.272 32 32v512c0 17.728-14.272 32-32 32h-64c-17.728 0-32-14.272-32-32V224c0-17.728 14.272-32 32-32'/><path fill='#f44336' d='M512 96c0-17.728-14.272-32-32-32H352c-17.728 0-32 14.272-32 32v640c0 17.728 14.272 32 32 32h128c17.728 0 32-14.272 32-32z'/><path fill='#2196f3' d='m530.161 158.902 57.333-27.693a31.804 31.804 0 0 1 42.634 14.936l262.693 548.17c7.66 15.984.977 35.057-14.982 42.766l-57.333 27.693a31.804 31.804 0 0 1-42.634-14.936L515.18 201.668c-7.66-15.983-.977-35.057 14.982-42.766z'/><path fill='#ffeb3b' d='M320 192v64h192v-64zm0 384v64h192v-64z'/></svg>",
+ "bibtex-style": "<svg viewBox='0 0 1024 1024'><path fill='#795548' d='M96 832h832c17.728 0 32 14.272 32 32v64c0 17.728-14.272 32-32 32H96c-17.728 0-32-14.272-32-32v-64c0-17.728 14.272-32 32-32'/><path fill='#4caf50' d='M160 192h64c17.728 0 32 14.272 32 32v512c0 17.728-14.272 32-32 32h-64c-17.728 0-32-14.272-32-32V224c0-17.728 14.272-32 32-32'/><path fill='#f44336' d='M512 96c0-17.728-14.272-32-32-32H352c-17.728 0-32 14.272-32 32v640c0 17.728 14.272 32 32 32h128c17.728 0 32-14.272 32-32z'/><path fill='#ffeb3b' d='M320 192v64h192v-64zm0 384v64h192v-64z'/><path fill='#bbdefb' d='M608 320h256c17.728 0 32 14.272 32 32v384c0 17.728-14.272 32-32 32H608c-17.728 0-32-14.272-32-32V352c0-17.728 14.272-32 32-32'/><path fill='#2196f3' d='M608 320c-17.673 0-32 14.327-32 32v352c35.346 0 64-28.654 64-64v-32a32 32 0 0 1 32-32c17.673 0 32-14.327 32-32v-64a32 32 0 0 1 32-32c17.673 0 32-14.327 32-32v-96z'/><path d='M745.606 339.205 924.74 473.693a15.965 15.965 0 0 1 3.19 22.401 15.965 15.965 0 0 1-22.403 3.19l-179.133-134.49a15.965 15.965 0 0 1-3.19-22.401 15.965 15.965 0 0 1 22.402-3.19z'/></svg>",
+ "bicep": "<svg viewBox='0 0 24 24'><path fill='#fbc02d' d='M3 18S4.15 6.885 7 3l5 1-1 3H9v7h1c1.9-2.915 5.783-3.98 8.157-2.915 4.475 1.915 2.998 5.967.148 7.905C16.025 20.548 10.113 23.05 3 18'/></svg>",
+ "biome": "<svg viewBox='0 0 74 74'><path fill='#42a5f5' d='M37 9 22.745 33.69a32.2 32.2 0 0 1 16.869-.584l4.818 1.137-4.533 19.22-4.825-1.137c-5.93-1.399-11.628 1.716-14.036 6.685l-4.46-2.158c3.404-7.029 11.425-11.285 19.637-9.347l2.259-9.58A27.23 27.23 0 0 0 5 64.424l64 .001z'/></svg>",
+ "bitbucket": "<svg viewBox='0 0 24 24'><defs><linearGradient id='a' x1='64.01' x2='32.99' y1='65.26' y2='89.48' gradientUnits='userSpaceOnUse'><stop offset='.18' stop-color='#1565c0'/><stop offset='1' stop-color='#1e88e5'/></linearGradient></defs><path fill='#1e88e5' d='M2.985 3.333a.618.618 0 0 0-.617.716l2.621 15.914a.84.84 0 0 0 .822.701h12.576a.62.62 0 0 0 .618-.519l2.627-16.09a.618.618 0 0 0-.617-.716zm11.039 11.501H10.01L8.923 9.16h6.074z'/><path fill='url(#a)' d='M59.67 60.12H40.9L37.75 78.5h-13L9.4 96.73a2.7 2.7 0 0 0 1.75.66h40.74a2 2 0 0 0 2-1.68z' transform='translate(2.368 -9.404)scale(.30877)'/></svg>",
+ "bithound": "<svg fill-opacity='.05' viewBox='0 0 400 400'><g fill='#e53935' fill-opacity='1'><path d='M350.738 186.163c-1.32-13.024-4.224-26.312-8.36-38.72-11.88-35.464-33.968-71.808-61.864-96.888-1.232-1.056-5.896-3.872-7.656-2.904-4.576 2.552 4.048 20.064 5.104 23.232 6.512 19.36 10.648 39.864 5.984 60.104-6.248 26.752-26.752 45.496-54.12 47.784-15.048 1.232-30.184-.44-45.232 1.32-22.528 2.64-45.496 10.384-59.84 28.864-1.672 2.112-3.168 4.488-4.576 6.952h-.352c-5.544.616-11.088-1.76-13.816-3.256-.704-.44-1.408-.792-1.936-1.056-16.72-9.24-29.04-29.92-36.608-46.992-3.432-7.92-6.336-16.192-8.184-24.552-.88-3.784-.968-7.744-1.144-11.616-.088-2.376.264-5.72-1.056-7.832-2.904-4.576-6.6-.176-7.216 3.52-.968 6.072-1.848 12.056-1.584 18.216.44 10.384 3.344 20.68 7.04 30.36 5.456 14.256 13.112 27.368 23.056 39.072 4.136 4.84 8.536 9.328 13.288 13.464 4.224 3.784 9.592 6.776 12.76 11.616 3.696 5.544 4.312 12.408 3.96 18.832-.88 16.984-1.408 32.912 3.432 49.456 4.224 14.696 9.504 29.744 18.304 42.328 4.4 6.248 9.856 12.848 15.84 17.512 4.048 3.168 11.704 3.52 7.304-8.096-9.768-25.784-10.648-52.536 4.576-76.648 12.76-20.064 35.288-37.928 60.72-34.76 37.4 4.664 63.448 38.984 61.6 75.68-.528 10.296-.88 19.096-4.136 28.776-1.32 3.872-2.288 8.8-1.32 12.848 1.584 6.864 9.24 4.312 12.584-.176 9.064-12.32 18.568-24.288 27.104-36.96 27.808-41.536 41.36-89.584 36.344-139.48'/><path d='M141.21 85.051c.616 2.024 1.232 4.224 1.672 6.6.088.968.352 2.024.88 2.992 2.288 5.984 7.832 9.24 13.024 12.32 3.168 1.936 8.888 3.784 12.408 5.192 4.576 1.848 14.432-.528 19.096-.88 10.736-.88 20.68-4.664 30.536 1.056-50.512 59.224-2.816 72.424 34.144 43.912 42.24-32.56 2.464-109.384 2.464-109.384s-.88-5.984-16.896-9.504a52 52 0 0 0-4.488-2.112c-15.84-7.304-30.096 4.664-41.536 14.432-3.344 2.816-6.6 5.632-10.12 8.272-4.752 3.52-9.856 6.424-15.224 8.976-5.632 2.64-12.32 5.632-18.568 5.896-.88 0-2.552.176-4.312.528-2.728.264-4.136.968-4.752 2.2-1.056.88-1.76 2.112-1.584 3.696.176 2.2 1.232 4.048 2.376 5.456.352.088.616.264.88.352'/></g></svg>",
+ "blender": "<svg viewBox='0 0 32 32'><g fill='#ff9800' fill-rule='evenodd'><path d='M14.004 19a5 5 0 0 1 4.993-5.001 5 5 0 0 1 5.008 4.985A5 5 0 0 1 19.028 24a5 5 0 0 1-5.024-4.97z' paint-order='stroke fill markers'/><path d='m23.148 7.152-4.355-4.451s-1.488-1.534-3.022-.046-.046 3.022-.046 3.022l.165.264c.693.69 1.412 1.368 2.105 2.058h-12s-2 0-2 2 2 2 2 2h4l-6 6-1.332 1.41s-1.503 1.503 0 3.007c1.503 1.503 3.006 0 3.006 0l2.327-2.417.063.19q.06.555.173 1.09a11.02 11.02 0 0 0 8.599 8.508 11 11 0 0 0 2.216.213c6.068-.028 10.967-4.965 10.949-11.034 0-5.452-4-8.967-6.848-11.815zm-4.162 4.847a7 7 0 1 1-6.99 7.044V19a7 7 0 0 1 6.99-7'/></g></svg>",
+ "blink": "<svg viewBox='0 0 256 256'><path fill='#f9a825' d='M130.974 23.383c57.809 1.624 103.262 49.782 101.638 107.591s-49.782 103.262-107.59 101.639C67.303 230.989 21.85 183.01 23.383 125.293c1.533-57.81 49.602-103.443 107.41-101.91zm-.541 10.823c-51.766-1.353-94.875 39.59-96.137 91.447-1.353 51.766 39.59 94.875 91.357 96.137 51.766 1.353 94.875-39.59 96.137-91.357 1.443-51.856-39.5-94.875-91.357-96.227.09 0 0 0 0 0'/><path fill='#f9a825' d='M137.9 93.403c-4.149 3.968-2.525 12.806 3.878 19.57s15.241 8.838 19.209 4.78 2.706-12.987-3.788-19.751-15.422-8.748-19.57-4.78zm52.217-25.162c8.207 8.568 14.43 18.758 18.398 29.851 0 0 2.706 7.395-5.862 7.395H181.73s-6.674-.54-6.944 5.14c-.451 7.035-.27 12.988-.27 12.988.27 4.058 1.803 8.026 4.328 11.183l21.554 22.727a9.184 9.184 0 0 1 1.082 12.355c-10.912 18.578-28.408 32.286-49.06 38.509-6.314 1.894-6.945-3.247-6.765-6.764 0-2.255 2.616-52.397 2.616-52.397.721-5.411-.992-10.912-4.78-14.881-6.764-7.215-11.003-11.814-11.003-11.814s-4.78-4.78-11.634-11.904c-3.788-3.878-9.018-5.862-14.43-5.501H54.027c-3.427 0-8.658-.812-6.403-7.035 7.305-20.292 21.915-37.066 41.124-46.896 3.878-2.705 9.199-1.984 12.265 1.714l21.554 22.727c3.066 2.705 6.944 4.329 11.003 4.78 0 0 5.862.45 12.987.36 5.591 0 5.501-6.764 5.501-6.764s.902-13.888 1.173-20.923c-.27-2.976 1.894-5.591 4.78-5.862.991-.09 1.893.09 2.795.451 11.003 4.69 21.013 11.634 29.22 20.292z'/></svg>",
+ "blink_light": "<svg viewBox='0 0 256 256'><circle cx='128' cy='128' r='97.4' fill='#37474f'/><path fill='#f9a825' d='M130.974 23.383c57.809 1.624 103.262 49.782 101.638 107.591s-49.782 103.262-107.59 101.639C67.303 230.989 21.85 183.01 23.383 125.293c1.533-57.81 49.602-103.443 107.41-101.91zm-.541 10.823c-51.766-1.353-94.875 39.59-96.137 91.447-1.353 51.766 39.59 94.875 91.357 96.137 51.766 1.353 94.875-39.59 96.137-91.357 1.443-51.856-39.5-94.875-91.357-96.227.09 0 0 0 0 0'/><path fill='#f9a825' d='M137.9 93.403c-4.149 3.968-2.525 12.806 3.878 19.57s15.241 8.838 19.209 4.78 2.706-12.987-3.788-19.751-15.422-8.748-19.57-4.78zm52.217-25.162c8.207 8.568 14.43 18.758 18.398 29.851 0 0 2.706 7.395-5.862 7.395H181.73s-6.674-.54-6.944 5.14c-.451 7.035-.27 12.988-.27 12.988.27 4.058 1.803 8.026 4.328 11.183l21.554 22.727a9.184 9.184 0 0 1 1.082 12.355c-10.912 18.578-28.408 32.286-49.06 38.509-6.314 1.894-6.945-3.247-6.765-6.764 0-2.255 2.616-52.397 2.616-52.397.721-5.411-.992-10.912-4.78-14.881-6.764-7.215-11.003-11.814-11.003-11.814s-4.78-4.78-11.634-11.904c-3.788-3.878-9.018-5.862-14.43-5.501H54.027c-3.427 0-8.658-.812-6.403-7.035 7.305-20.292 21.915-37.066 41.124-46.896 3.878-2.705 9.199-1.984 12.265 1.714l21.554 22.727c3.066 2.705 6.944 4.329 11.003 4.78 0 0 5.862.45 12.987.36 5.591 0 5.501-6.764 5.501-6.764s.902-13.888 1.173-20.923c-.27-2.976 1.894-5.591 4.78-5.862.991-.09 1.893.09 2.795.451 11.003 4.69 21.013 11.634 29.22 20.292z'/></svg>",
+ "blitz": "<svg viewBox='0 0 24 24'><path fill='#7c4dff' d='M8.613 11.997c1.333 0 2.588.621 3.389 1.677l3.454 4.552a.28.28 0 0 1 .025.297l-1.991 3.825a.284.284 0 0 1-.477.04l-7.901-10.39zm2.375-10.385 7.9 10.39h-3.5a4.25 4.25 0 0 1-3.39-1.676L8.546 5.774a.28.28 0 0 1-.025-.297l1.99-3.825a.284.284 0 0 1 .478-.04z'/></svg>",
+ "bower": "<svg viewBox='0 0 400 400'><path fill='#5d4037' d='M376.834 196.261c-18.912-18.172-113.486-29.517-143.327-32.819a88 88 0 0 0 3.692-10.58c4.068-1.78 8.46-3.438 13-4.822.553 1.632 3.159 7.885 4.644 10.853 60.004 1.655 63.085-44.591 65.525-57.26 2.387-12.389 2.265-24.359 22.847-46.241-30.663-8.936-74.759 13.85-89.53 47.762-5.55-2.08-11.114-3.615-16.615-4.565-3.943-15.905-24.474-60.215-78.352-60.215-68.215 0-142.567 56.276-142.567 151.542 0 80.078 54.672 150.258 85.559 150.258 13.49 0 25.094-10.103 27.818-19.158 2.284 6.209 9.292 25.51 11.593 30.424 3.402 7.267 19.134 13.554 26.018 6.014 8.852 4.917 25.095 7.88 33.947-5.235 17.049 3.606 32.12-6.56 32.45-18.691 8.365-.447 12.469-12.193 10.642-21.547-1.346-6.887-15.732-31.599-21.343-40.13 11.108 9.035 39.243 11.593 42.66.006 17.909 14.057 45.817 6.679 48.03-4.753 21.761 5.654 46.72-6.764 42.621-21.803 34.958-2.418 30.483-39.611 20.675-49.037z'/><path fill='#03a9f4' d='M279.494 116.935c7.529-14.938 16.99-31.25 28.94-41.34-13.153 5.3-26.139 21.146-33.817 38.083a118 118 0 0 0-11.893-6.646c10.71-22.862 35.598-41.955 63.025-43.447-18.37 16.662-11.85 51.29-26.954 69.623-4.322-4.342-14.247-12.72-19.301-16.273m-11.876 24.326c.008-.572.222-4.981.624-6.994-1.054-.248-7.601-1.529-11.015-1.449-.249 4.288 1.802 11.581 3.828 15.972 13.956-.292 24.036-4.472 29.969-8.314-5.051-2.354-13.67-4.448-20.224-5.7-.732 1.513-2.531 5.368-3.182 6.485'/><g stroke-width='.973' transform='translate(10.989 32.73)scale(.81733)'><path fill='#4caf50' d='M250.54 277.39c.004.024.015.057.018.082-2.165-4.657-4.463-10.314-7.208-17.708 10.688 15.557 44.184 7.533 42.427-6.407 16.395 12.336 50.143-2.055 42.471-19.353 16.423 7.653 35.168-7.745 30.964-14.455 28 5.4 54.832 10.783 63.256 12.938-5.595 9.123-18.339 15.566-37.549 11.089 10.38 14.14-9.773 31.105-37.844 21.76 6.18 13.883-18.814 26.38-47.22 11.91.361 13.889-35.24 15.488-49.315.143zm55.543-70.194c32.497 2.495 86.238 7.34 119.51 11.997-2.102-10.828-7.844-13.921-25.905-18.772-19.425 2.072-68.706 6.913-93.604 6.776z'/><path fill='#ffca28' d='M285.78 253.36c16.395 12.336 50.143-2.055 42.471-19.353 16.423 7.653 35.168-7.745 30.964-14.455-33.103-6.383-67.84-12.788-75.719-13.908 4.78.254 12.702.797 22.59 1.556 24.899.137 74.18-4.704 93.604-6.775-31.452-7.975-95.666-19.613-140.01-22.48-2.055 3.003-5.833 8.097-12.413 13.51-19.403 41.053-54.557 68.34-93.454 68.34-11.335 0-24.018-1.912-38.233-6.456-8.865 9.497-46.661 16.694-77.329 1.641 24.326 56.961 80.74 94.984 143.19 94.984 52.591 0 75.912-53.704 70.808-67.914-1.238-3.45-6.145-14.889-8.891-22.283 10.689 15.556 44.185 7.532 42.429-6.408z'/><path fill='#e0e0e0' d='M253.91 145.27c4.644-2.526 20.69-12.253 35.981-15.908a68 68 0 0 1-.536-5.12c-10.032 2.403-28.945 10.51-39.784-.661 22.866 6.9 34.283-6.149 51.09-6.149 10.014 0 24.305 2.798 35.57 7.22-9.061-8.37-38.772-33.63-75.558-33.717-8.213 9.957-17.09 31.526-6.764 54.334z'/><path fill='#f4511e' d='M115.58 253.33c14.215 4.544 26.898 6.457 38.233 6.457 38.896 0 74.05-27.29 93.454-68.341-14.351 11.978-39.291 22.228-78.241 22.228 34.694-7.866 64.56-25.156 79.753-50.427-10.68-16.998-22.263-54.603 7.07-84.33-4.512-14.497-26.475-52.766-75.095-52.766-84.85 0-155.17 71.001-155.17 166.15 0 22.525 4.547 43.65 12.67 62.664 30.666 15.054 68.462 7.858 77.327-1.64z'/><path fill='#ffca28' d='M141.03 108.45c0 21.644 17.546 39.191 39.19 39.191s39.192-17.548 39.192-39.191-17.548-39.191-39.192-39.191-39.19 17.547-39.19 39.191'/><path fill='#5d4037' d='M156.76 108.45c0 12.958 10.507 23.463 23.463 23.463 12.96 0 23.464-10.506 23.464-23.463 0-12.959-10.504-23.464-23.464-23.464-12.957 0-23.463 10.506-23.463 23.464'/><ellipse cx='180.22' cy='98.044' fill='#fafafa' rx='13.673' ry='8.501'/></g></svg>",
+ "brainfuck": "<svg viewBox='0 0 24 24'><path fill='#ff4081' d='M21.085 13.343a4.35 4.35 0 0 1-1.812 3.788l.738 1.429c.22.431.25.94.058 1.39a1.68 1.68 0 0 1-1.017.959l-.758.24a1.62 1.62 0 0 1-1.784-.527l-2.032-2.398a5.1 5.1 0 0 1-2.34-1.055 5 5 0 0 1-1.438.22 4.2 4.2 0 0 1-2.398-.757 5 5 0 0 1-1.553.211 5.6 5.6 0 0 1-2.206-.431 3.94 3.94 0 0 1-2.33-3.462c-.077-.69.038-1.39.336-2.024a3.3 3.3 0 0 1-.068-2.234 4.3 4.3 0 0 1 1.86-2.148c.557-1.62 2.12-2.704 3.837-2.589a4.404 4.404 0 0 1 5.59-.355 5 5 0 0 1 1.247-.163A4.16 4.16 0 0 1 18.37 5.01a4.61 4.61 0 0 1 3.433 4.286 5.05 5.05 0 0 1-.825 3.002c.067.345.106.69.106 1.045m-4.795-1.352c.547.067.978.48.978 1.026a.96.96 0 0 1-.959.959h-.604a4.97 4.97 0 0 1-1.553 2.196c.24.086.489.134.738.201 4.92-.067 4.344-3.068 4.344-3.116a2.486 2.486 0 0 0-2.58-2.388.96.96 0 0 1-.958-.959.96.96 0 0 1 .958-.959c1.18.029 2.312.47 3.194 1.247a5 5 0 0 0 .076-.854c-.057-1.189-.594-2.224-2.752-2.426-1.198-2.838-4.22-1.266-4.22-.383-.028.22.202.69.24.719a.96.96 0 0 1 .96.959.96.96 0 0 1-.96.959 2.25 2.25 0 0 1-1.37-.537c-.461.297-.988.48-1.535.537-.547.048-.997-.336-1.026-.863a.93.93 0 0 1 .844-1.055c.153-.02.901-.134.901-.739 0-.632.24-1.237.652-1.716-.882-.24-1.831.077-2.79 1.237-1.765-.278-2.484-.038-3.011 1.832-.911.45-1.39.767-1.602 1.726a5.65 5.65 0 0 1 3.088.24.97.97 0 0 1 .566 1.236.96.96 0 0 1-1.237.566 2.93 2.93 0 0 0-2.206-.057c-.307.259-.307.796-.307 1.218 0 .71.355 1.37.96 1.754a3.5 3.5 0 0 0 1.64.384 6 6 0 0 1-.375-.777.995.995 0 0 1 1.88-.652c.383 1.093 1.361 1.841 2.512 1.966a3.59 3.59 0 0 0 3.06-2.043c.22-1.323 1.284-1.438 2.454-1.438m1.918 7.163-.595-1.246-.68.153.958 1.199zm-4.46-8.256a.96.96 0 0 0-.872-.988 2.56 2.56 0 0 0-1.85.643 2.85 2.85 0 0 0-.806 2.1.96.96 0 0 0 .959.959.95.95 0 0 0 .959-.96c0-.258.067-.517.22-.728a.64.64 0 0 1 .413-.144c.527.029.978-.364.978-.882z'/></svg>",
+ "browserlist": "<svg viewBox='0 0 140 140'><path fill='#ffca28' d='M70 10.172A59.83 59.83 0 0 0 10.172 70 59.83 59.83 0 0 0 70 129.828 59.83 59.83 0 0 0 129.828 70 59.83 59.83 0 0 0 70 10.172m-4.836 8.785C65.66 23 66.516 26.28 67.736 29.18c4.779-4.287 10.265-7.546 16.041-9.02-.981 3.938-1.357 7.295-1.261 10.43 6.026-2.314 12.349-3.404 18.3-2.706-3.182 2.413-5.482 4.717-7.128 7.015-2.201 12.074 6.858 20.43 14.779 24.551a5.13 5.13 0 0 1 5.183-3.888 5.128 5.128 0 0 1 3.7 8.435V64c-.487 1.055-2.002 2.343-3.496 3.219-4.076 2.39-11.173 5.736-20.915 7.39.045 1.214.077 2.453.077 3.747 0 4.817-.485 8.291-1.385 10.699-3.3 13.313-12.648 26.76-24.695 31.95.357-4.082.197-7.484-.402-10.591-5.582 3.219-11.646 5.278-17.623 5.52h-.002c1.785-3.662 2.855-6.878 3.412-9.976-6.347.996-12.727.742-18.377-1.17 2.93-2.732 5.054-5.314 6.673-7.96-6.292-1.344-12.169-3.87-16.766-7.686 3.822-1.544 6.795-3.239 9.3-5.197-5.426-3.517-10.034-7.998-12.972-13.23 4.012-.07 7.321-.568 10.3-1.453-3.786-5.215-6.468-11.032-7.333-16.951 3.861 1.405 7.196 2.133 10.36 2.355-1.662-6.22-2.081-12.605-.768-18.436 3.03 2.634 5.824 4.48 8.63 5.815.677-6.406 2.576-12.52 5.893-17.496 1.926 3.622 3.914 6.392 6.111 8.672 2.93-5.754 6.9-10.798 11.791-14.262zM91.63 38.514c-2.395 5.514-1.665 11.297-.555 18.732a2.138 2.138 0 0 0 .28-4.178 3.419 3.419 0 1 1 .092 6.704c.574 3.882 1.157 8.18 1.421 13.125a67 67 0 0 0 3.25-.649c6.616-1.487 12.258-3.801 16.871-6.506.45-.264.884-.563 1.276-.867.366-.557.333-.957.035-1.285-4.831-1.245-10.891-4.53-15.258-8.795-4.764-4.653-7.427-10.164-7.412-16.281'/></svg>",
+ "browserlist_light": "<svg viewBox='0 0 140 140'><g stroke-width='.855' transform='translate(10.508 10.205)'><circle cx='59.492' cy='59.795' r='59.828' fill='#ffca28'/><path fill='#37474f' d='M54.656 8.752c-4.89 3.464-8.862 8.508-11.791 14.262-2.198-2.28-4.185-5.05-6.111-8.672-3.318 4.976-5.216 11.09-5.893 17.496-2.807-1.335-5.6-3.18-8.63-5.814-1.314 5.83-.895 12.216.767 18.436-3.164-.223-6.498-.95-10.36-2.356.865 5.92 3.548 11.737 7.333 16.951-2.978.885-6.287 1.383-10.3 1.453 2.939 5.233 7.547 9.714 12.972 13.23-2.505 1.959-5.478 3.654-9.299 5.198 4.596 3.815 10.474 6.341 16.766 7.685-1.62 2.647-3.743 5.228-6.674 7.96 5.65 1.912 12.03 2.166 18.377 1.17-.556 3.098-1.626 6.314-3.412 9.975h.002c5.977-.24 12.042-2.3 17.623-5.52.6 3.108.76 6.51.402 10.593 12.047-5.19 21.395-18.638 24.695-31.951.9-2.408 1.385-5.881 1.385-10.7 0-1.293-.031-2.531-.076-3.745 9.742-1.655 16.839-5.001 20.914-7.39 1.494-.877 3.01-2.165 3.496-3.22v-.002a5.128 5.128 0 0 0-3.7-8.435 5.13 5.13 0 0 0-5.183 3.889c-7.92-4.122-16.98-12.477-14.779-24.551 1.646-2.299 3.947-4.603 7.13-7.016-5.952-.698-12.276.392-18.302 2.707-.095-3.135.28-6.492 1.262-10.43-5.776 1.473-11.262 4.733-16.041 9.02-1.22-2.902-2.076-6.18-2.572-10.223zm26.465 19.557c-.015 6.117 2.648 11.628 7.412 16.281 4.366 4.265 10.426 7.55 15.258 8.795.298.328.331.728-.035 1.285-.392.304-.825.603-1.276.867-4.612 2.704-10.256 5.019-16.87 6.506q-1.607.361-3.25.649c-.265-4.945-.848-9.243-1.422-13.125a3.419 3.419 0 1 0-.092-6.703 2.138 2.138 0 0 1-.28 4.177c-1.11-7.435-1.84-13.218.555-18.732' data-mit-no-recolor='true'/></g></svg>",
+ "bruno": "<svg viewBox='0 0 32 32'><path fill='#212121' d='M26 10H6v14h6v2h8v-2h6z'/><path fill='#ffab40' d='M27.897 9.794a6.86 6.86 0 0 0-4.35-3.55A7.97 7.97 0 0 0 18 4h-4a7.97 7.97 0 0 0-5.548 2.244 6.86 6.86 0 0 0-4.35 3.55A19.9 19.9 0 0 0 2 18.702V20a4 4 0 0 0 4 4v-8h2v6a4 4 0 0 0 4 4v-2a2 2 0 0 1-2-2v-2h2v2h2v-2h4v2h2v-2h2v2a2 2 0 0 1-2 2v2a4 4 0 0 0 4-4v-6h2v8a4 4 0 0 0 4-4v-1.298a19.9 19.9 0 0 0-2.103-8.908M10 14v-2h2v2Zm4 4v-2h4v2Zm8-4h-2v-2h2Z'/><path fill='#f44336' d='M16 24a6.8 6.8 0 0 1-2 .996V28a2 2 0 0 0 4 0v-3.004A6.8 6.8 0 0 1 16 24'/></svg>",
+ "buck": "<svg viewBox='0 0 24 24'><g fill='#0277bd'><path d='M7.488 2.94h-1.51v4.273L7.82 9.054H5.61L3.197 6.642V2.94H1.595v4.383l3.278 3.278h4.42l1.584 1.584H6.64l3.573 3.683-5.23 5.194h2.062l5.23-5.267-1.989-1.99H20.86v.664l-4.567 2.725-3.831 3.868h2.173l2.615-2.652 5.157-3.094v-3.13h-4.862l-1.658-1.658 1.879-1.879V4.45H16.2v3.5l-1.528 1.51-1.363-1.428V4.45h-1.473v4.273l3.445 3.462h-2.102L7.488 6.55z'/><path d='M15.48 14.763h-2.062l.995 1.068z'/></g></svg>",
+ "bucklescript": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='M12 18h4v2h-4z'/><path fill='#26a69a' d='M4 4v24h24V4Zm14 15.5a.5.5 0 0 1-.5.5H16v2h1.5a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5H16v1.5a.5.5 0 0 1-.5.5h-5a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 .5.5V18h1.5a.5.5 0 0 1 .5.5Zm8-2a.5.5 0 0 1-.5.5H22v2h2a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2h-3.5a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5H24v-2h-2a2 2 0 0 1-2-2v-2a2 2 0 0 1 2-2h3.5a.5.5 0 0 1 .5.5Z'/><path fill='#26a69a' d='M12 22h4v2h-4z'/></svg>",
+ "buildkite": "<svg viewBox='0 0 32 32'><path fill='#00e676' d='m12 22-8-4V8l8 4zm8-14v10h4l4-4'/><path fill='#00c853' d='m12 22 8-4V8l-8 4zm8 6 8-4V14l-8 4z'/></svg>",
+ "bun": "<svg viewBox='0 0 32 32'><path fill='#fff8e1' d='M30 17.045a9.8 9.8 0 0 0-.32-2.306l-.004.034a11.2 11.2 0 0 0-5.762-6.786c-3.495-1.89-5.243-3.326-6.8-3.811h.003c-1.95-.695-3.949.82-5.825 1.927-4.52 2.481-9.573 5.45-9.28 11.417.008-.029.017-.052.026-.08a9.97 9.97 0 0 0 3.934 7.257l-.01-.006C13.747 31.473 30.05 27.292 30 17.045'/><path fill='#37474f' d='M19.855 20.236A.8.8 0 0 0 19.26 20h-6.514a.8.8 0 0 0-.596.236.51.51 0 0 0-.137.463 4.37 4.37 0 0 0 1.641 2.339 4.2 4.2 0 0 0 2.349.926 4.2 4.2 0 0 0 2.343-.926 4.37 4.37 0 0 0 1.642-2.339.5.5 0 0 0-.132-.463Z'/><ellipse cx='22.5' cy='18.5' fill='#f8bbd0' rx='2.5' ry='1.5'/><ellipse cx='9.5' cy='18.5' fill='#f8bbd0' rx='2.5' ry='1.5'/><circle cx='10' cy='16' r='2' fill='#37474f'/><circle cx='22' cy='16' r='2' fill='#37474f'/><path fill='#455a64' d='M9.996 18A2 2 0 1 0 8 15.996V16a2 2 0 0 0 1.996 2'/><circle cx='9' cy='15' r='1' fill='#fafafa'/><circle cx='21' cy='15' r='1' fill='#fafafa'/></svg>",
+ "bun_light": "<svg viewBox='0 0 32 32'><path fill='#fff8e1' d='M15.696 27.002a13.73 13.73 0 0 1-9.071-3.062 8.86 8.86 0 0 1-3.6-6.505c-.252-5.091 3.813-7.747 8.748-10.455.28-.165.537-.322.793-.48a7.8 7.8 0 0 1 3.52-1.5 2 2 0 0 1 .695.118 14.8 14.8 0 0 1 2.95 1.576c.972.6 2.182 1.348 3.707 2.173a10.14 10.14 0 0 1 5.274 6.147A8.8 8.8 0 0 1 29 17.035a8.15 8.15 0 0 1-2.525 5.959 15.6 15.6 0 0 1-10.778 4.008Z'/><path fill='#37474f' d='M16.087 6a1 1 0 0 1 .358.06l.038.013.037.012a14.5 14.5 0 0 1 2.684 1.46 72 72 0 0 0 3.767 2.205 9.17 9.17 0 0 1 4.767 5.493A8 8 0 0 1 28 17.055a7.18 7.18 0 0 1-2.234 5.233 14.6 14.6 0 0 1-10.07 3.714 12.74 12.74 0 0 1-8.415-2.816l-.027-.024-.029-.023a7.98 7.98 0 0 1-3.202-5.758c-.223-4.516 3.431-6.89 8.231-9.525l.027-.015.027-.015q.389-.231.783-.474A7.4 7.4 0 0 1 16.087 6m0-2c-1.618 0-3.248 1.19-4.795 2.103-4.52 2.481-9.56 5.41-9.267 11.376a9.9 9.9 0 0 0 3.942 7.215 14.77 14.77 0 0 0 9.73 3.308c7.122 0 14.335-4.134 14.303-10.957a9.6 9.6 0 0 0-.322-2.29 11.16 11.16 0 0 0-5.764-6.768c-3.495-1.89-5.242-3.326-6.798-3.811A3 3 0 0 0 16.086 4Z'/><path fill='#37474f' d='M19.855 20.236A.8.8 0 0 0 19.26 20h-6.514a.8.8 0 0 0-.596.236.51.51 0 0 0-.137.463 4.37 4.37 0 0 0 1.641 2.339 4.2 4.2 0 0 0 2.349.926 4.2 4.2 0 0 0 2.343-.926 4.37 4.37 0 0 0 1.642-2.339.5.5 0 0 0-.132-.463Z'/><ellipse cx='22.5' cy='18.5' fill='#f8bbd0' rx='2.5' ry='1.5'/><ellipse cx='9.5' cy='18.5' fill='#f8bbd0' rx='2.5' ry='1.5'/><circle cx='10' cy='16' r='2' fill='#37474f'/><circle cx='22' cy='16' r='2' fill='#37474f'/><path fill='#455a64' d='M9.996 18A2 2 0 1 0 8 15.996V16a2 2 0 0 0 1.996 2'/><circle cx='9' cy='15' r='1' fill='#fafafa'/><circle cx='21' cy='15' r='1' fill='#fafafa'/></svg>",
+ "c": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M19.563 22A5.57 5.57 0 0 1 14 16.437v-2.873A5.57 5.57 0 0 1 19.563 8H24V2h-4.437A11.563 11.563 0 0 0 8 13.563v2.873A11.564 11.564 0 0 0 19.563 28H24v-6Z'/></svg>",
+ "c3": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M11.563 22A5.57 5.57 0 0 1 6 16.437v-2.873A5.57 5.57 0 0 1 11.563 8H14V2h-2.437A11.563 11.563 0 0 0 0 13.563v2.873A11.564 11.564 0 0 0 11.563 28H14v-6Zm20.129-4.131A5.17 5.17 0 0 0 28 14c3.39-.894 4.268-5.241 2.792-8.108-3.305-6.304-14.028-4.545-15.407 2.381l4.89 1.255a3.17 3.17 0 0 1 3.04-2.754 3 3 0 0 1 1.814.702c1.19.911 1.249 3.785-.353 4.232A9 9 0 0 1 22 12h-2v4h2c1.7.107 3.362.577 4.23 2.313a3.4 3.4 0 0 1 .377 1.636 3.25 3.25 0 0 1-.297 1.464c-.919 1.985-3.984 2.166-5.407.749a4.43 4.43 0 0 1-1.285-2.143l-4.89 1.429c2.447 10.449 19.993 7.76 16.964-3.58'/></svg>",
+ "cabal": "<svg viewBox='0 0 300 300'><g transform='translate(0 -822.52)'><rect width='107.25' height='156.59' x='405.55' y='967.22' fill='#0097a7' rx='12.306' ry='12.31' transform='matrix(-.98339 .18149 .60192 .79856 0 0)'/><rect width='108.34' height='123.15' x='-1156.5' y='1461.9' fill='#3f51b5' rx='10.69' ry='12.31' transform='matrix(-.98528 .17093 -.59175 .80612 0 0)'/><path fill='#3f51b5' d='M52.112 965.158c-1.343 3.515-26.292 23.248-25.744 27.277.548 4.03 29.812 16.023 32.04 19.027s10.545 41.668 13.603 42.5 18.828-31.274 21.548-32.932 32.808 2.503 34.15-1.01c1.343-3.515-18.174-35.352-18.721-39.381s9.732-40.12 7.502-43.125-30.06 9.427-33.118 8.594-26.793-27.3-29.514-25.643-.405 41.177-1.747 44.693z'/></g></svg>",
+ "caddy": "<svg viewBox='0 0 32 32'><path fill='#4fc3f7' d='M20 22v-3.53q-.008-.155-.011-.31-.003-.436-.041-.87a5.3 5.3 0 0 0-.18-.994 2.9 2.9 0 0 0-1.026-1.563A4.42 4.42 0 0 0 16.017 14a4.5 4.5 0 0 0-2.762.74 2.9 2.9 0 0 0-1.05 1.57 5 5 0 0 0-.186 1.073q-.029.448-.014.897l.004.191v3.53Z'/><path fill='#4fc3f7' d='M29 19c0-7.409-5.268-13-13-13S3 11.591 3 19c-.003 2.317 0 5 1 7.026v-.84c.001-1.673 2.264-3.002 4-3.186v-4.438C8 12.38 10.388 9.931 16 10c5.612-.07 8 2.38 8 7.562V22c1.736.184 3.999 1.513 4 3.187v.839C29 24 29.003 21.317 29 19'/></svg>",
+ "cadence": "<svg viewBox='0 0 32 32'><path fill='#00e676' d='M14 12h6v-1a1 1 0 0 1 1-1h7V4h-6a8 8 0 0 0-8 8m6 0h6v6h-6zm-6 6v2.65A1.35 1.35 0 0 1 12.65 22h-1.3A1.35 1.35 0 0 1 10 20.65v-1.3A1.35 1.35 0 0 1 11.35 18zv-6h-2.65A7.35 7.35 0 0 0 4 19.35v1.3A7.35 7.35 0 0 0 11.35 28h1.3A7.35 7.35 0 0 0 20 20.65V18Z'/></svg>",
+ "cairo": "<svg viewBox='0 0 24 24'><path fill='#f44336' d='M13.15 7.455a1.94 1.94 0 0 1-1.938-1.94c0-1.07.87-1.94 1.939-1.94 1.07 0 1.94.87 1.94 1.94s-.87 1.94-1.94 1.94M12 2C6.477 2 2 6.477 2 12c0 2.348.811 4.506 2.166 6.212 1.092-1.532 2.258-2.977 3.721-4.18.042-.035.143-.11.272-.203a2.92 2.92 0 0 0 1.15-1.876v-.012c.37-2.438 1.371-3.279 4.152-3.279q.37.001.786.02c1.423.067 2.243.473 2.34.685a.57.57 0 0 1 .027.363l-.111-.015c-.878-.109-2.231.16-2.419 1.117-.105.544.02 1.143.072 1.693.054.567.104 1.139.099 1.711-.003.044-.035.266-.005.29-1.514-1.449-5.014.37-6.116 1.17q.17-.058.34-.122c1.05-.357 4.24-1.314 5.47-.256 1.043 1.277.104 3.634-.673 4.802a9.7 9.7 0 0 1-1.64 1.87q.184.009.369.01c5.523 0 10-4.477 10-10S17.523 2 12 2'/></svg>",
+ "cake": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='M16 10a2.847 2.847 0 0 0 3-2.663v-.003a2.32 2.32 0 0 0-.435-1.372L16 2l-2.565 3.96A2.33 2.33 0 0 0 13 7.331 2.847 2.847 0 0 0 15.998 10zm6.134 13.376L20.708 22l-1.44 1.375a4.917 4.917 0 0 1-6.52 0L11.334 22l-1.466 1.375A4.79 4.79 0 0 1 4 23.871V29a1 1 0 0 0 1 1h22a1 1 0 0 0 1-1v-5.129a4.79 4.79 0 0 1-5.866-.497M24 14h-6.667v-2h-2.666v2H8a4.145 4.145 0 0 0-4 4.09v1.415A2.56 2.56 0 0 0 6.614 22a2.53 2.53 0 0 0 1.84-.726l2.88-2.71 2.813 2.71a2.764 2.764 0 0 0 3.693 0l2.826-2.71 2.868 2.71A2.649 2.649 0 0 0 28 19.505V18.09A4.145 4.145 0 0 0 24 14'/></svg>",
+ "capacitor": "<svg viewBox='0 0 24 24'><path fill='#4fc3f7' d='m19.081 2.35-4.795 4.795L9.62 2.482 7.05 5.05l4.664 4.665 2.57 2.57 4.705 4.705 2.57-2.57-4.705-4.705 4.795-4.797zM5.052 7.05l-2.57 2.57 4.665 4.664L2.35 19.08l2.57 2.57 4.796-4.796 4.704 4.705 2.57-2.57-7.274-7.274z' paint-order='fill markers stroke'/></svg>",
+ "capnp": "<svg viewBox='0 0 24 24'><path fill='#c62828' d='M17 3V2h4v8h-4c-.085-2.088-.445-4.042-3-4-2.917 0-5 2.51-5 5 0 3 .495 6.981 4.67 6.981 2.906-.26 2.99-2.705 3.33-4.981h4c0 5.806-3.314 9.052-9 9-6.154-.073-8.915-4.685-9-10-.128-6.14 4.568-9.2 10.414-9.65 1.301-.028 2.466 0 3.586.65'/></svg>",
+ "cbx": "<svg viewBox='0 0 1024 1024'><path fill='#1565c0' d='M128 704v128c0 70.692 57.308 128 128 128h608c17.728 0 32-14.272 32-32V704z'/><path fill='#ffe082' d='M704 704v192h128V704z'/><path fill='#fff8e1' d='M192 704v96c0 53.184 42.816 96 96 96h544a96 96 0 0 1-96-96 96 96 0 0 1 96-96z'/><path fill='#ff1744' d='M320 832h192v192l-96-96-96 96z'/><path fill='#2196f3' d='M256 64c-70.692 0-128 57.308-128 128v640c0 11.088 1.557 21.787 4.207 32.047C146.767 807.565 197.672 768.07 256 768h608c17.728 0 32-14.272 32-32V96c0-17.728-14.272-32-32-32z'/><path fill='#e3f2fd' d='M384 192c-70.692 0-128 57.308-128 128 .171 67.295 52.422 122.965 119.57 127.396L256 640h80l156.748-252.488h-.146A128 128 0 0 0 512 320c0-70.692-57.308-128-128-128m320 0c-70.692 0-128 57.308-128 128 .171 67.295 52.422 122.965 119.57 127.396L576 640h80l156.748-252.488h-.146A128 128 0 0 0 832 320c0-70.692-57.308-128-128-128'/></svg>",
+ "cds": "<svg viewBox='0 0 16 16'><g fill='#0288d1'><rect width='4' height='1' x='7' y='9' ry='.5'/><rect width='3' height='1' x='8' y='11' ry='.5'/><rect width='4' height='1' x='7' y='13' ry='.5'/><path d='m5 9-1 1 1.5 1.5L4 13l1 1 2.5-2.5z'/><path d='M6 2a3 3 0 0 0-2.598 1.5 3 3 0 0 0-.187 2.607 3 3 0 0 0-1.514.965 3 3 0 0 0-.42 3.196A3 3 0 0 0 4 12v-1a2 2 0 0 1-2-2 2 2 0 0 1 2-2 2 2 0 0 1 .515.076l.159-.591A2 2 0 0 1 4 5a2 2 0 0 1 2-2 2 2 0 0 1 2 2l.594.594A2 2 0 0 1 10 5a2 2 0 0 1 2 2 2 2 0 0 1 2 2 2 2 0 0 1-2 2v1a3 3 0 0 0 2.898-2.223A3 3 0 0 0 13.5 6.402a3 3 0 0 0-.63-.267 3 3 0 0 0-1.722-1.906 3 3 0 0 0-2.252-.014 3 3 0 0 0-2.119-2.113A3 3 0 0 0 6 2'/></g></svg>",
+ "certificate": "<svg viewBox='0 0 32 32'><path fill='#ff5722' d='M4 6v14a2 2 0 0 0 2 2h12v6l3-2 3 2v-6h4a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2m2 0h8v2H6Zm0 4h6v2H6Zm0 4h8v2H6Zm10 6H6v-2h10Zm8-6v4l-3-2-3 2v-4l-4-2 4-2V6l3 2 3-2v4.2l4 1.8Z'/></svg>",
+ "changelog": "<svg fill='none' viewBox='0 0 24 24'><path d='M0 0h24v24H0z'/><path fill='#8bc34a' d='M13 3a9 9 0 0 0-9 9H1l4 4 4-4H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.95 8.95 0 0 0 13 21a9 9 0 0 0 0-18m-1 5v5l4.25 2.52.77-1.28-3.52-2.09V8z'/></svg>",
+ "chess": "<svg viewBox='0 0 32 32'><path fill='#cfd8dc' d='M6 26h20v4H6zm16.5-13a5.49 5.49 0 0 0-4.5 2.344V10h4V6h-4V2h-4v4h-4v4h4v5.344a5.498 5.498 0 1 0-5 8.63V24h14v-.025A5.499 5.499 0 0 0 22.5 13'/></svg>",
+ "chess_light": "<svg viewBox='0 0 32 32'><path fill='#455a64' d='M6 26h20v4H6zm16.5-13a5.49 5.49 0 0 0-4.5 2.344V10h4V6h-4V2h-4v4h-4v4h4v5.344a5.498 5.498 0 1 0-5 8.63V24h14v-.025A5.499 5.499 0 0 0 22.5 13'/></svg>",
+ "chrome": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='M16 2a14 14 0 1 0 14 14A14 14 0 0 0 16 2m0 3a11 11 0 0 1 9.208 5H16a6 6 0 0 0-5.74 4.253L7.27 9.334A10.98 10.98 0 0 1 16 5m4 11a4 4 0 1 1-4-4 4.005 4.005 0 0 1 4 4M5 16a10.9 10.9 0 0 1 1.094-4.75l4.838 7.959.003-.002a5.96 5.96 0 0 0 6.16 2.689l-2.996 4.928A11.01 11.01 0 0 1 5 16m11.343 10.983 4.878-8.026-.003-.002A5.97 5.97 0 0 0 20.463 12h5.773a10.966 10.966 0 0 1-9.893 14.983'/></svg>",
+ "circleci": "<svg viewBox='0 0 32 32'><circle cx='16' cy='16' r='4' fill='#fafafa'/><path fill='#fafafa' d='M17.73 2.104a14 14 0 0 0-14.927 9.234.504.504 0 0 0 .48.662h5.525a.49.49 0 0 0 .416-.235 8 8 0 1 1 0 8.47A.49.49 0 0 0 8.81 20H3.28a.503.503 0 0 0-.479.66 14 14 0 1 0 14.93-18.556Z'/></svg>",
+ "circleci_light": "<svg viewBox='0 0 32 32'><circle cx='16' cy='16' r='4' fill='#424242'/><path fill='#424242' d='M17.73 2.104a14 14 0 0 0-14.927 9.234.504.504 0 0 0 .48.662h5.525a.49.49 0 0 0 .416-.235 8 8 0 1 1 0 8.47A.49.49 0 0 0 8.81 20H3.28a.503.503 0 0 0-.479.66 14 14 0 1 0 14.93-18.556Z'/></svg>",
+ "citation": "<svg viewBox='0 0 1024 1024'><path fill='#1e88e5' d='M256 192c-106.039 0-192 85.961-192 192 .189 103.43 82.273 188.122 185.646 191.545L128 832h128l173.473-365.715C441.746 440.412 447.323 412.44 448 384c0-106.039-85.961-192-192-192m512 0c-106.039 0-192 85.961-192 192 .189 103.43 82.273 188.122 185.646 191.545L640 832h128l173.473-365.715C953.746 440.412 959.323 412.44 960 384c0-106.039-85.961-192-192-192'/></svg>",
+ "clangd": "<svg viewBox='0 0 16 16'><path fill='#4caf50' d='M10 4H7.5C4.75 4 2 5.379 2 9.5c0 4.12 2.75 5.51 5.53 5.5H10v-3H7.667C7.665 11.973 5 12.289 5 9.478 5 6.672 7.395 7.028 7.52 7H10z'/><path fill='#2979ff' d='M10 1v6H7.52C7.452 7.03 5 6.659 5 9.478 5 12.295 7.618 11.97 7.668 12H13V1h-2.725z'/></svg>",
+ "cline": "<svg viewBox='0 0 16 16'><path fill='#42a5f5' d='M8 1a2 2 0 0 0-2 2H5C3.338 3 2 4.338 2 6v1L1 9l1 2v1c0 1.662 1.338 3 3 3h6c1.662 0 3-1.338 3-3v-1l1-2-1-2V6c0-1.662-1.338-3-3-3h-1a2 2 0 0 0-2-2M6 7c.554 0 1 .446 1 1v2c0 .554-.446 1-1 1s-1-.446-1-1V8c0-.554.446-1 1-1m4 0c.554 0 1 .446 1 1v2c0 .554-.446 1-1 1s-1-.446-1-1V8c0-.554.446-1 1-1'/></svg>",
+ "clojure": "<svg viewBox='0 0 256 256'><path fill='#64dd17' d='M123.456 129.975a507 507 0 0 0-3.54 7.846c-4.406 9.981-9.284 22.127-11.066 29.908-.64 2.77-1.037 6.205-1.03 10.013 0 1.506.081 3.09.21 4.702a58.1 58.1 0 0 0 19.98 3.559 58.2 58.2 0 0 0 18.29-2.98c-1.352-1.237-2.642-2.554-3.816-4.038-7.796-9.942-12.146-24.512-19.028-49.01m-28.784-49.39C79.782 91.08 70.039 108.387 70.002 128c.037 19.32 9.487 36.403 24.002 46.94 3.56-14.83 12.485-28.41 25.868-55.63a219 219 0 0 0-2.714-7.083c-3.708-9.3-9.059-20.102-13.834-24.993-2.435-2.555-5.389-4.763-8.652-6.648'/><path fill='#7cb342' d='M178.532 194.535c-7.683-.963-14.023-2.124-19.57-4.081a69.4 69.4 0 0 1-30.958 7.249c-38.491 0-69.693-31.198-69.698-69.7 0-20.891 9.203-39.62 23.764-52.392-3.895-.94-7.956-1.49-12.104-1.482-20.45.193-42.037 11.51-51.025 42.075-.84 4.45-.64 7.813-.64 11.8 0 60.591 49.12 109.715 109.705 109.715 37.104 0 69.882-18.437 89.732-46.633-10.736 2.675-21.06 3.955-29.902 3.982-3.314 0-6.425-.177-9.305-.53'/><path fill='#29b6f6' d='M157.922 173.271c.678.336 2.213.884 4.35 1.49 14.375-10.553 23.717-27.552 23.754-46.764h-.005c-.055-32.03-25.974-57.945-58.011-58.009a58.2 58.2 0 0 0-18.213 2.961c11.779 13.426 17.443 32.613 22.922 53.6l.01.025c.01.017 1.752 5.828 4.743 13.538 2.97 7.7 7.203 17.231 11.818 24.178 3.03 4.655 6.363 8 8.632 8.981'/><path fill='#1e88e5' d='M128.009 18.29c-36.746 0-69.25 18.089-89.16 45.826 10.361-6.49 20.941-8.83 30.174-8.747 12.753.037 22.779 3.991 27.589 6.696a51 51 0 0 1 3.345 2.131 69.4 69.4 0 0 1 28.049-5.894c38.496.004 69.703 31.202 69.709 69.698h-.006c0 19.409-7.938 36.957-20.736 49.594 3.142.352 6.492.571 9.912.554 12.15.006 25.284-2.675 35.13-10.956 6.42-5.408 11.798-13.327 14.78-25.199.584-4.586.92-9.247.92-13.991 0-60.588-49.116-109.715-109.705-109.715'/></svg>",
+ "cloudfoundry": "<svg viewBox='0 0 24 24'><path fill='#0288d1' d='M14.13 10.1c.6-.5.98-1.19.97-1.96-.01-1.47-1.4-2.66-3.1-2.66S8.91 6.66 8.9 8.13c-.01.77.37 1.47.97 1.96.75.61 1.36 1.84 1.4 3.18.04 1.49-.06 3.22-.12 4.1-.8.11-1.36.38-1.36.69 0 .41.99.89 2.22.89s2.22-.48 2.22-.89c0-.31-.56-.58-1.36-.69-.06-.88-.16-2.6-.12-4.1.04-1.34.65-2.57 1.4-3.18z'/><path fill='#78909c' d='M17.89 18.77a2 2 0 0 1 .37-.11 2.3 2.3 0 0 1 .43-.05l-.02-.12c0-.07-.04-.16-.07-.26l-.05-.13c-.02-.04-.04-.08-.06-.11l-.06-.11a4 4 0 0 1-.44-.1 3 3 0 0 1-.4-.14c-.22-.09-.43-.2-.54-.32a.41.41 0 0 1-.16-.34.3.3 0 0 1 .07-.15.5.5 0 0 1 .16-.12l-.13-.07c-.04-.02-.08-.05-.14-.07-.05-.03-.11-.05-.17-.08-.11-.05-.22-.11-.32-.14l-.15-.06a3 3 0 0 1-.34.02c-.12 0-.25 0-.37-.01-.24-.02-.49-.05-.7-.12a2 2 0 0 1-.28-.1 1.3 1.3 0 0 1-.21-.11c-.11-.08-.19-.16-.19-.25l-.17-.03c-.1-.02-.33-.05-.47-.07 0 .02-.02.05-.02.09l-.04.17c-.07.35.13.7.47.8.83.24 1.46.64 1.64 1.13.37.99-1.18 1.96-3.52 1.96-2.35 0-3.89-.97-3.53-1.96.18-.48.78-.87 1.59-1.11a.72.72 0 0 0 .49-.84l-.05-.24-.31.04c-.14.02-.27.04-.37.06l-.17.03c.01.08-.05.17-.16.25a1.4 1.4 0 0 1-.2.12 2 2 0 0 1-.27.1 3 3 0 0 1-.33.08 3 3 0 0 1-.36.05c-.25.02-.5.02-.73 0l-.04.02c-.02.01-.07.03-.11.05l-.31.15c-.11.05-.21.12-.29.16l-.1.05-.03.02c.16.07.24.17.26.28.04.11-.01.23-.12.35a1.5 1.5 0 0 1-.22.18 2 2 0 0 1-.32.17 3.4 3.4 0 0 1-.83.26.5.5 0 0 0-.05.11l-.09.25-.04.26a.4.4 0 0 0-.01.12c.32 0 .61.06.83.14.25.08.4.21.52.36.09.16.13.33.05.53-.06.19-.22.41-.47.6l.04.03.12.08c.1.07.23.17.38.26l.44.24c.06.03.12.06.16.07l.06.03c.36-.15.75-.23 1.14-.28a3.3 3.3 0 0 1 .57-.01c.19.01.38.02.55.07.18.05.35.09.5.16a3 3 0 0 1 .42.24c.26.17.45.42.54.68l.33.01c.2.01.46.02.73.01l.72-.03.32-.03a1 1 0 0 1 .17-.38 1 1 0 0 1 .14-.16 1.6 1.6 0 0 1 .55-.39 2.3 2.3 0 0 1 .48-.17c.18-.04.35-.07.54-.08q.285-.015.57 0a4 4 0 0 1 1.15.24l.2-.11c.11-.07.28-.15.41-.25s.27-.19.35-.26l.13-.13c-.27-.19-.43-.39-.52-.58a.64.64 0 0 1-.06-.27.4.4 0 0 1 .06-.25c.07-.16.25-.27.45-.37M9.95 13.9s.35-1.32-1.17-3.03l-.05-.05a4.2 4.2 0 0 1-.98-2.7c0-2.32 1.89-4.2 4.2-4.2h.1a4.2 4.2 0 0 1 4.2 4.2 4.2 4.2 0 0 1-.99 2.7c-.01.01-.02.03-.05.05-1.34 1.35-1.17 3.03-1.17 3.03a6.14 6.14 0 0 0 4.13-5.75c.02-3.24-2.6-6-5.83-6.15h-.7c-3.23.16-5.85 2.92-5.83 6.15.01 2.59 1.67 4.9 4.13 5.75z'/></svg>",
+ "cmake": "<svg viewBox='0 0 24 24'><path fill='#1e88e5' d='M11.94 2.984 2.928 21.017l9.875-8.47z'/><path fill='#e53935' d='m11.958 2.982.002.29 1.312 14.499-.002.006.023.26 7.363 2.978h.415l-.158-.31-.114-.228h-.001l-8.84-17.494z'/><path fill='#7cb342' d='m8.558 16.13-5.627 4.884h17.743v-.016z'/></svg>",
+ "coala": "<svg viewBox='0 0 24 24'><path xmlns='http://www.w3.org/2000/svg' fill='#90a4ae' d='M22 8.95c0-2.59-1.74-3.63-3.89-3.63-.9 0-1.83.2-2.6.57-1-.69-2.17-1.09-3.51-1.09s-2.51.41-3.51 1.09c-.78-.38-1.7-.57-2.6-.57C3.74 5.32 2 6.35 2 8.95c0 2.14 1.18 3.56 2.8 4-.02.3-.04.6-.04.89 0 3.18 2.51 4.26 4.77 4.63.61.45 1.49.73 2.47.73s1.86-.28 2.47-.73c2.26-.36 4.77-1.44 4.77-4.63 0-.3-.01-.6-.04-.89 1.62-.44 2.8-1.87 2.8-4'/><path xmlns='http://www.w3.org/2000/svg' fill='#f8bbd0' d='M7.31 6.9c-.18-.02-.35-.03-.53-.03-1.72 0-3.11.83-3.11 2.9 0 1.2.47 2.12 1.19 2.68.26-2.11 1.11-4.12 2.45-5.55m9.91-.03c-.18 0-.35.01-.53.03 1.34 1.43 2.19 3.44 2.45 5.55.72-.56 1.19-1.48 1.19-2.68 0-2.07-1.39-2.9-3.11-2.9'/><path xmlns='http://www.w3.org/2000/svg' fill='#263238' d='M14.07 15.21c0 1.86-.96 2.33-2.07 2.33s-2.07-.47-2.07-2.33.96-3.36 2.07-3.36 2.07 1.51 2.07 3.36M9.5 11.75a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0m7.5 0a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0'/></svg>",
+ "cobol": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M12 0h8v4h-8z'/><path fill='#0288d1' d='M16 5A11 11 0 1 1 5 16 11.01 11.01 0 0 1 16 5m0-3a14 14 0 1 0 14 14A14 14 0 0 0 16 2'/><path fill='#0288d1' d='M32 12v8h-4v-8zm-1.858 12.485-5.657 5.657-2.313-2.313 5.657-5.657zM7.514 30.143l-5.657-5.657 2.814-2.814 5.657 5.657zM12 28h8v4h-8zm15.329-17.672L21.672 4.67l2.814-2.814 5.657 5.657zM3 12v8H0v-8zm7.328-7.329L4.67 10.328 1.857 7.514l5.657-5.657zM20 10h-4a6 6 0 0 0 0 12h4v-4h-4a2 2 0 0 1 0-4h4z'/></svg>",
+ "coconut": "<svg viewBox='0 0 24 24'><path fill='#8d6e63' d='M8.042 3.036a7 7 0 0 0-.737.237c-.527.22-.983.492-1.385.824a3 3 0 0 1-.275.21c-.126.056-.7.655-1.007 1.052-1.732 2.247-2.442 5.513-1.86 8.565.076.396.339 1.362.465 1.706.764 2.085 2.136 3.719 3.928 4.673 1.412.753 3.084 1.06 4.822.887.348-.034.524-.063 1.085-.165.233-.043 1.381-.385 1.659-.495 2.392-.946 4.365-2.582 5.572-4.618.506-.855.796-1.669 1.029-2.882.37-1.923-.502-4.262-2.324-6.229-2.643-2.854-6.788-4.459-10.18-3.942a8 8 0 0 0-.793.177zm.42 1.507c.28-.076.575-.139.813-.167a8 8 0 0 1 1.994.024c3.131.42 6.308 2.488 7.806 5.077.498.861.788 1.79.803 2.578.012.635-.129 1.18-.431 1.679a2.6 2.6 0 0 1-.549.647c-.613.538-1.455.876-2.501 1.005-.548.067-.92.206-1.23.46-.068.056-.642.588-1.279 1.182-1.248 1.166-1.237 1.157-1.42 1.104a.4.4 0 0 1-.15-.087c-.13-.122-.126-.16.079-1.224.105-.543.188-1.053.187-1.132a1.16 1.16 0 0 0-.297-.75c-.113-.125-.3-.244-.547-.35-2.359-1.007-4.343-2.776-5.295-4.722a5.2 5.2 0 0 1-.412-1.088c-.29-1.061-.22-1.913.219-2.683.322-.565.777-.964 1.487-1.302.178-.084.443-.175.723-.251'/></svg>",
+ "code-climate": "<svg viewBox='0 0 300 300'><path fill='#eee' d='m196.19 75.562-51.846 51.561 30.766 30.766 21.08-21.08 59.252 59.537 30.481-30.766zm-61.246 60.961-30.481-30.481-78.053 78.053-11.964 11.964 30.766 30.766 11.964-12.249 39.596-39.312 7.691-7.691 30.481 30.48 28.772 28.773 30.766-30.766-28.772-28.772z'/></svg>",
+ "code-climate_light": "<svg viewBox='0 0 300 300'><path fill='#455a64' d='m196.19 75.562-51.846 51.561 30.766 30.766 21.08-21.08 59.252 59.537 30.481-30.766zm-61.246 60.961-30.481-30.481-78.053 78.053-11.964 11.964 30.766 30.766 11.964-12.249 39.596-39.312 7.691-7.691 30.481 30.48 28.772 28.773 30.766-30.766-28.772-28.772z'/></svg>",
+ "codecov": "<svg viewBox='0 0 24 24'><path fill='#ec407a' d='M12.006 2.375c-5.528.004-10.028 4.471-10.032 9.959v.025l1.706.995.023-.016a4.9 4.9 0 0 1 3.641-.773 4.75 4.75 0 0 1 2.398 1.193l.293.273.166-.363c.16-.35.346-.68.55-.98a8 8 0 0 1 .278-.372l.172-.215-.211-.176a7 7 0 0 0-3.249-1.516 7.16 7.16 0 0 0-3.359.196c.812-3.556 3.939-6.036 7.631-6.039a7.78 7.78 0 0 1 5.516 2.267 7.7 7.7 0 0 1 2.095 3.759 7.2 7.2 0 0 0-2.09-.317h-.127a7 7 0 0 0-.829.061l-.034.005a6 6 0 0 0-.327.05 7 7 0 0 0-.47.101l-.115.03q-.202.055-.403.12l-.025.008a7 7 0 0 0-.878.367l-.023.012a7 7 0 0 0-.392.214l-.03.018a6.8 6.8 0 0 0-1.77 1.516l-.063.076a7 7 0 0 0-.557.799l-.05.087a7 7 0 0 0-.195.36l-.014.025a7 7 0 0 0-.367.888l-.015.044a6.9 6.9 0 0 0-.343 2.264l.001.094a10 10 0 0 0 .018.33l.014.155.021.19.005.034.011.086q.022.158.052.316c.202 1.057.706 2.115 1.458 3.058l.034.042.035-.041c.3-.355 1.044-1.479 1.107-2.154l.001-.012-.006-.011a4.7 4.7 0 0 1-.535-2.169c0-2.52 1.982-4.613 4.51-4.764l.165-.006a4.96 4.96 0 0 1 2.9.856l.023.016 1.684-.979.022-.013v-.025a9.84 9.84 0 0 0-2.934-7.039 10 10 0 0 0-7.087-2.91'/></svg>",
+ "codeowners": "<svg viewBox='0 0 24 24'><path fill='#afb42b' d='m20.35 12.25 1.4 1.41-6.53 6.59-3.47-3.5 1.4-1.41 2.07 2.08zm-11.1 4.5 3 3h-10v-2c0-2.21 3.58-4 8-4l1.89.11zm1-13a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4'/></svg>",
+ "coderabbit-ai": "<svg viewBox='0 0 16 16'><path fill='#f4511e' d='M15 8.913A6.85 6.85 0 0 1 12.74 14h-1.68c.035-.162-.052-.274-.165-.35-.236-.162-.385-.431-.35-.71.093-.735.545-1.674 2.194-2.536 1.115-.588 1.32-1.8 1.398-1.978.113-.294.082-.543-.154-.781-.427-.431-.889-.822-1.449-1.075-.786-.365-1.588-.345-2.384-.051-.139.05-.108-.056-.118-.101a5.3 5.3 0 0 0-.545-1.481c-.519-.923-1.243-1.603-2.322-1.831-.16-.036-.324-.046-.488-.066-.077-.01-.118.01-.098.101.144.68.345 1.334.781 1.892.2.259.473.431.74.609.283.192.58.37.848.588.252.213.457.461.555.817C9.467 7.01 9.45 7 9.44 6.986c-.822-1.289-2.43-1.857-4.048-1.39-.15.04-.129.091-.057.198.468.71 1.11 1.217 1.87 1.608.565.289 1.151.527 1.788.608.288.036.15.228.195.457.062.355.211.466.16.436-.977-.386-1.788-.538-2.461-.538-2.78 0-3.232 2.617-3.211 2.648-.041-.016-.71-.259-.858.4-.154.67.755 1.106.755 1.106.118-.791.837-.964.925-.984-.072.04-.54.31-.684 1.045-.123.65.396 1.222.591 1.42h-1.15A6.87 6.87 0 0 1 1 8.913C1 5.093 4.129 2 7.997 2 11.867 2 15 5.094 15 8.913M9.914 14h-.74a.27.27 0 0 0 .072-.142c.062-.355-.247-.426-.247-.426H7.294s.678-.03 1.3-.284c.616-.259 1.114-.71 1.202-.832a1.93 1.93 0 0 0-.031 1.517.32.32 0 0 0 .149.167'/></svg>",
+ "coffee": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='M4 26h24v2H4zM28 4H7a1 1 0 0 0-1 1v13a4 4 0 0 0 4 4h10a4 4 0 0 0 4-4v-4h4a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2m0 8h-4V6h4Z'/></svg>",
+ "coldfusion": "<svg viewBox='0 0 32 32'><path fill='#0d3858' stroke='#4dd0e1' stroke-width='2' d='M3.009 3.009h25.983v25.983H3.009z'/><path fill='#4dd0e1' d='M24 9.5v-1a.5.5 0 0 0-.5-.5H22a2 2 0 0 0-2 2v2h-1.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5H20v7.5a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5V14h1.5a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5H22v-2h1.5a.5.5 0 0 0 .5-.5M12 20a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h3.5a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5H12a4 4 0 0 0-4 4v4a4 4 0 0 0 4 4h3.5a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5Z'/></svg>",
+ "coloredpetrinets": "<svg viewBox='0 0 16 16'><rect width='3' height='1' x='9' y='7.5' fill='#b0bec5' rx='0' ry='.032'/><rect width='3' height='1' x='4' y='7.5' fill='#b0bec5' rx='0' ry='.032'/><circle cx='3' cy='8' r='2' fill='#4caf50'/><circle cx='13' cy='8' r='2' fill='#ff9800'/><rect width='4' height='4' x='6' y='6' fill='#00bcd4' rx='.59'/></svg>",
+ "command": "<svg viewBox='0 0 32 32'><path fill='#90a4ae' d='M24 18h-3v-4h3a6 6 0 1 0-6-6v3h-4V8a6 6 0 1 0-6 6h3v4H8a6 6 0 1 0 6 6v-3h4v3a6 6 0 1 0 6-6M21 8a3 3 0 1 1 3 3h-3ZM11 24a3 3 0 1 1-3-3h3Zm0-13H8a3 3 0 1 1 3-3Zm7 7h-4v-4h4Zm6 9a3.003 3.003 0 0 1-3-3v-3h3a3 3 0 0 1 0 6'/></svg>",
+ "commitizen": "<svg viewBox='0 0 32 32'><path fill='#64b5f6' d='M29.422 17.4 17.4 29.422a1.986 1.986 0 0 1-2.8 0L2.578 17.4a1.986 1.986 0 0 1 0-2.8L14.6 2.578a1.986 1.986 0 0 1 2.8 0l8.01 8.012L23 13a2 2 0 0 0-.74-.14A2.13 2.13 0 0 0 20.37 14H12a2.08 2.08 0 0 0-1.86-1.14 2.14 2.14 0 0 0 0 4.28A2.08 2.08 0 0 0 12 16h8l-3.82 3.83h-.01a1.9 1.9 0 0 0-.63-.1 2.135 2.135 0 1 0 2.14 2.13 1.8 1.8 0 0 0-.1-.61l4.17-4.17a2 2 0 0 0 .51.06A2.14 2.14 0 0 0 24.4 15a2 2 0 0 0-.06-.51l2.49-2.48 2.592 2.59a1.986 1.986 0 0 1 0 2.8'/></svg>",
+ "commitlint": "<svg viewBox='0 0 24 24'><path fill='#009688' d='M2.47 2.922V8.37h1.813V2.922zm12.708 1.816a7.27 7.27 0 0 0-6.946 5.127L6.1 12l2.133 2.133c.916 2.969 3.677 5.13 6.945 5.13 4.013 0 7.262-3.25 7.262-7.263s-3.25-7.262-7.262-7.262m2.942 3.703 1.342 1.63-5.49 5.488-3.179-3.467 1.34-1.34 1.838 1.838zM3.377 10.184c-.998 0-1.816.817-1.816 1.816a1.817 1.817 0 1 0 1.816-1.816M2.47 15.63v5.448h1.814V15.63z'/></svg>",
+ "concourse": "<svg stroke-linejoin='round' stroke-miterlimit='2' clip-rule='evenodd' viewBox='0 0 24 24'><clipPath id='a'><path d='M.913 1h22.173v22H.913z'/></clipPath><g fill='#2196f3' clip-path='url(#a)' transform='translate(1.036 1.05)scale(.9137)'><path d='M13.555 14.025a1.541 1.541 0 1 0-.255-3.071 1.541 1.541 0 0 0 .255 3.071m1.796-.022a3.3 3.3 0 0 1-1.203.839l-.004.01c.348 1.34.894 2.62 1.618 3.799a5.9 5.9 0 0 1-2.035.644l-.101.013-.119.009-.24.018c-.147 0-.292.014-.446.005a6.1 6.1 0 0 1-1.782-.304 6.3 6.3 0 0 1-1.986-1.067c-.156-.118-.278-.237-.384-.329a12 12 0 0 1-.315-.312s.094.13.271.353c.093.107.2.243.341.383a6.4 6.4 0 0 0 1.125.939q.375.249.781.444a6.8 6.8 0 0 0 1.883.59c.166.032.345.043.518.064l.241.015.12.007.137.002a6.9 6.9 0 0 0 2.646-.499q.303.412.636.801l-.03.019-.265.164-.037.023-.009.006-.003.001.047-.023-.005.003-.019.01-.081.04-.169.086-.089.046-.046.023-.023.012-.009.004.03-.012-.012.005-.197.087-.207.09-.026.012-.004.002.021-.008-.007.004-.014.005-.054.021-.112.044a8.1 8.1 0 0 1-3.175.535l-.141-.005-.159-.012-.319-.026c-.198-.031-.396-.045-.6-.089a8.3 8.3 0 0 1-2.306-.781 6 6 0 0 1-.507-.281c-.165-.094-.318-.2-.468-.302a8.3 8.3 0 0 1-1.427-1.243c-.181-.187-.317-.369-.438-.513-.117-.147-.198-.27-.258-.351l-.091-.125.078.134c.052.086.121.218.223.377.105.156.224.353.387.561a8.8 8.8 0 0 0 1.328 1.418c.144.121.291.247.453.362a9 9 0 0 0 1.619.963q.606.277 1.246.462c.211.069.44.112.661.168l.316.058.157.029.175.025a9 9 0 0 0 1.304.081 9.2 9.2 0 0 0 2.316-.323l.116-.033.056-.015.079-.025.216-.07.204-.066.222-.085.177-.069.084-.033.021-.007.005-.002.102-.051.281-.136.35-.17q.356.343.738.66s1.212 1.249 1.88.735c.628-.482-.095-1.996-.095-1.996a14.03 14.03 0 0 0-5.192-6.847m-4.23-2.052-.007-.009a14 14 0 0 0-4.053-.57 5.9 5.9 0 0 1 .537-2.155l.043-.093.055-.106.112-.213c.078-.125.143-.254.232-.38a6.1 6.1 0 0 1 1.203-1.351 6.3 6.3 0 0 1 1.956-1.118c.183-.07.348-.111.483-.152.272-.069.432-.102.432-.102s-.161.01-.444.042c-.139.023-.312.041-.506.086a6.4 6.4 0 0 0-1.392.458 6.75 6.75 0 0 0-2.288 1.711c-.116.124-.22.27-.329.406l-.14.196-.069.098-.075.116a7 7 0 0 0-.995 2.591l-.004.024a15 15 0 0 0-1 .128l.001-.174.001-.312v-.056l-.005.052.001-.006.002-.022.008-.089.016-.19.009-.099.005-.051.002-.026v-.006l.001-.003c.001-.009-.011.063-.006.031l.002-.013.031-.213.033-.223.004-.029v-.004l-.004.021.001-.007.003-.014.011-.058.022-.117a8.1 8.1 0 0 1 1.228-2.977l.079-.117.095-.128.19-.258c.132-.151.248-.312.394-.46A8.3 8.3 0 0 1 8.88 4.468q.251-.155.507-.282c.167-.091.337-.164.503-.237a8.3 8.3 0 0 1 1.81-.552c.255-.054.482-.073.668-.099.186-.022.334-.025.434-.034l.155-.01-.155-.006c-.1-.001-.249-.013-.437-.011-.189.007-.419.003-.682.032a8 8 0 0 0-.881.117 9 9 0 0 0-1.025.257c-.179.059-.364.117-.547.193q-.279.104-.558.24A8.8 8.8 0 0 0 6.504 5.51c-.171.143-.329.315-.493.472l-.216.237-.108.119-.114.135a9 9 0 0 0-.76 1.063 9.2 9.2 0 0 0-.952 2.136l-.034.115-.017.056-.021.081-.055.219-.052.208-.045.234-.036.187-.017.088-.004.022-.001.005-.006.06-.001.011-.005.043-.033.309-.059.552q-.483.13-.959.296s-1.695.396-1.599 1.233c.091.787 1.761.947 1.761.947a14.03 14.03 0 0 0 8.543-.923 3.3 3.3 0 0 1-.1-1.464m11.935-.909a9 9 0 0 0-.209-1.313c-.044-.218-.12-.438-.179-.658l-.105-.304-.052-.151-.064-.165a9.1 9.1 0 0 0-1.99-3.033l-.127-.127-.061-.056-.166-.153-.158-.146-.183-.151-.147-.121-.069-.057-.017-.014-.005-.004-.049-.034-.045-.03-.256-.177-.344-.238q.134-.53.225-1.071c.091-.541.473-1.675-.306-1.996-.732-.302-1.68 1.083-1.68 1.083a14.02 14.02 0 0 0-3.325 7.923c.485.104.939.316 1.329.621l.011-.002a14 14 0 0 0 2.411-3.181c.621.39 1.163.892 1.6 1.48l.06.082.067.099.134.2c.072.128.155.247.223.386.287.534.493 1.108.611 1.702a6.3 6.3 0 0 1 .046 2.253c-.026.194-.069.359-.097.497-.07.272-.118.427-.118.427s.068-.146.175-.409c.047-.134.114-.294.166-.486q.099-.32.162-.65a6.8 6.8 0 0 0-.025-2.649 7 7 0 0 0-.281-.97c-.053-.161-.132-.322-.198-.483l-.106-.217-.053-.107-.066-.121a6.8 6.8 0 0 0-1.773-2.091q.215-.478.393-.971l.053.029.274.15.037.021.012.006-.043-.029.005.003.093.062.158.106.083.055.043.028.03.02-.025-.02.01.008.172.129.181.135.023.018.003.002-.016-.014.017.014.046.037.093.075a8.1 8.1 0 0 1 2.027 2.503l.065.125.201.435c.07.188.156.367.217.566.268.771.421 1.577.453 2.392q.016.295.005.58c0 .19-.018.375-.032.556a8.5 8.5 0 0 1-.382 1.853c-.074.25-.166.458-.232.634-.07.174-.138.305-.178.398l-.065.14.079-.133c.049-.087.13-.212.218-.379.085-.168.198-.369.299-.613q.181-.407.318-.83.163-.504.264-1.023c.034-.186.072-.376.092-.573a9 9 0 0 0 .043-1.883'/></g></svg>",
+ "conduct": "<svg viewBox='0 0 24 24'><path fill='#cddc39' d='m10 17-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9m-6-6a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1s-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2'/></svg>",
+ "console": "<svg viewBox='0 0 24 24'><path fill='#ff7043' d='M20 19V7H4v12zm0-16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zm-7 14v-2h5v2zm-3.42-4L5.57 9H8.4l3.3 3.3c.39.39.39 1.03 0 1.42L8.42 17H5.59z'/></svg>",
+ "container.clone": "<svg viewBox='0 0 24 24'><path fill='#00b0ff' d='M21 16.5c0 .38-.21.71-.53.88l-7.9 4.44c-.16.12-.36.18-.57.18s-.41-.06-.57-.18l-7.9-4.44A.99.99 0 0 1 3 16.5v-9c0-.38.21-.71.53-.88l7.9-4.44c.16-.12.36-.18.57-.18s.41.06.57.18l7.9 4.44c.32.17.53.5.53.88zM12 4.15 6.04 7.5 12 10.85l5.96-3.35zM5 15.91l6 3.38v-6.71L5 9.21zm14 0v-6.7l-6 3.37v6.71z'/></svg>",
+ "contentlayer": "<svg fill='none' viewBox='0 0 16 16'><path fill='#651fff' fill-rule='evenodd' stroke='#651fff' stroke-width='1.073' d='M-2.482.404A1.93 1.93 0 0 1-.16.427l6.967 5.356a1.93 1.93 0 0 1 0 3.058L4.15 10.883l2.7 2.171c.983.79.956 2.294-.053 3.048l-7.152 5.344a1.93 1.93 0 0 1-2.439-.106l-5.596-4.996-.782-.672c-3.492-3-3.062-8.526.845-10.951zm5.6 9.65L-.13 7.444a1.93 1.93 0 0 0-2.384-.026l-2.403 1.848a1.93 1.93 0 0 0 0 3.058l2.42 1.86a1.93 1.93 0 0 0 2.352 0l3.246-2.494 2.944 2.366a.643.643 0 0 1-.018 1.016l-7.152 5.344a.64.64 0 0 1-.813-.035l-5.6-5-.796-.684c-2.839-2.439-2.482-6.935.705-8.896l.023-.014 5.888-4.349a.64.64 0 0 1 .774.008l6.967 5.356a.643.643 0 0 1 0 1.02zm-1.049.807-2.998 2.304a.64.64 0 0 1-.783 0l-2.421-1.86a.643.643 0 0 1 0-1.02l2.403-1.848a.64.64 0 0 1 .795.009z' clip-rule='evenodd' transform='matrix(.5949 0 0 .61208 9.182 1.311)'/></svg>",
+ "context": "<svg viewBox='0 0 16 16'><path fill='#ffd600' d='M7 6h2c.554 0 1 .446 1 1v2c0 .554-.446 1-1 1H7c-.554 0-1-.446-1-1V7c0-.554.446-1 1-1'/><path fill='#aeea00' d='M7 0h2c.554 0 1 .446 1 1v3c0 .554-.446 1-1 1H7c-.554 0-1-.446-1-1V1c0-.554.446-1 1-1'/><path fill='#f57f17' d='M12 3h2c.554 0 1 .446 1 1v3c0 .554-.446 1-1 1h-2c-.554 0-1-.446-1-1V4c0-.554.446-1 1-1'/><path fill='#8e24aa' d='M12 9h2c.554 0 1 .446 1 1v3c0 .554-.446 1-1 1h-2c-.554 0-1-.446-1-1v-3c0-.554.446-1 1-1'/><path fill='#4db6ac' d='M7 11h2c.554 0 1 .446 1 1v3c0 .554-.446 1-1 1H7c-.554 0-1-.446-1-1v-3c0-.554.446-1 1-1'/><path fill='#e53935' d='M2 9h2c.554 0 1 .446 1 1v3c0 .554-.446 1-1 1H2c-.554 0-1-.446-1-1v-3c0-.554.446-1 1-1'/><path fill='#1565c0' d='M2 3h2c.554 0 1 .446 1 1v3c0 .554-.446 1-1 1H2c-.554 0-1-.446-1-1V4c0-.554.446-1 1-1'/></svg>",
+ "contributing": "<svg viewBox='0 0 24 24'><path fill='#ffca28' d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1s-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2'/></svg>",
+ "controller": "<svg viewBox='0 0 16 16'><path fill='#ffc107' d='M8.002 10.45A2.45 2.45 0 0 1 5.552 8a2.45 2.45 0 0 1 2.45-2.45A2.45 2.45 0 0 1 10.452 8a2.45 2.45 0 0 1-2.45 2.45m5.2-1.771c.029-.224.05-.448.05-.679a6 6 0 0 0-.05-.7l1.478-1.141a.35.35 0 0 0 .084-.448l-1.4-2.422a.344.344 0 0 0-.427-.154l-1.743.7a5 5 0 0 0-1.183-.686l-.26-1.855A.354.354 0 0 0 9.402 1h-2.8a.354.354 0 0 0-.35.294l-.258 1.855a5 5 0 0 0-1.183.686l-1.743-.7a.344.344 0 0 0-.427.154l-1.4 2.422a.345.345 0 0 0 .084.448L2.8 7.3a6 6 0 0 0-.05.7c0 .231.022.455.05.679L1.324 9.841a.345.345 0 0 0-.084.448l1.4 2.422c.084.154.273.21.427.154l1.743-.707c.364.28.742.518 1.183.693l.259 1.855a.354.354 0 0 0 .35.294h2.8a.354.354 0 0 0 .35-.294l.259-1.855a5 5 0 0 0 1.183-.693l1.743.707c.154.056.343 0 .427-.154l1.4-2.422a.35.35 0 0 0-.084-.448z'/></svg>",
+ "copilot": "<svg viewBox='0 0 16 16'><path fill='#fafafa' d='M7.998 14C4.006 14 1.11 11.457 1 10.728v-1.7c.074-.549.592-1.472 1.39-1.803l.031-.19a4 4 0 0 1 .11-.534c-.176-.444-.222-.946-.222-1.446 0-.759.112-1.544.606-2.168.507-.64 1.308-.98 2.384-1.1 1.055-.117 1.98.03 2.576.667q.066.07.121.144a2 2 0 0 1 .126-.144c.596-.638 1.52-.784 2.576-.667 1.076.12 1.877.46 2.383 1.1.495.624.607 1.41.607 2.168 0 .5-.047 1.002-.223 1.446.058.199.086.374.11.534l.033.19c.808.336 1.332 1.284 1.392 1.829v1.634C15 11.356 12.068 14 7.998 14m0-1.296c1.995 0 4.011-.969 4.377-1.25V7.738l-.02-.1c-.429.182-.94.253-1.511.253-1.003 0-1.802-.285-2.371-.865A2.82 2.812 0 0 1 8 6.38a2.835 2.828 0 0 1-.476.648c-.569.58-1.368.865-2.371.865-.57 0-1.082-.07-1.511-.254l-.02.101v3.714c.366.282 2.381 1.25 4.376 1.25M6.917 3.347c-.17-.18-.558-.36-1.472-.259-.892.099-1.294.353-1.499.611-.216.272-.323.689-.323 1.356 0 .693.113 1.022.27 1.197.142.158.454.33 1.262.33.746 0 1.171-.204 1.433-.47.275-.282.46-.722.54-1.356.102-.816-.033-1.218-.211-1.409m3.635-.259c-.913-.101-1.302.08-1.47.26-.179.19-.315.592-.212 1.408.08.634.265 1.074.54 1.356.262.266.687.47 1.434.47.807 0 1.12-.172 1.262-.33.156-.175.269-.504.269-1.197 0-.667-.108-1.084-.324-1.356-.204-.258-.606-.512-1.499-.61Z'/><path fill='#fafafa' d='M6.469 8.765a.656.655 0 0 1 .656.654v1.31a.656.655 0 0 1-1.313 0V9.42a.656.655 0 0 1 .657-.654Zm3.718.654v1.31a.656.655 0 0 1-1.312 0V9.42a.656.655 0 0 1 1.313 0z'/></svg>",
+ "copilot_light": "<svg viewBox='0 0 16 16'><path fill='#212121' d='M7.998 14C4.006 14 1.11 11.457 1 10.728v-1.7c.074-.549.592-1.472 1.39-1.803l.031-.19a4 4 0 0 1 .11-.534c-.176-.444-.222-.946-.222-1.446 0-.759.112-1.544.606-2.168.507-.64 1.308-.98 2.384-1.1 1.055-.117 1.98.03 2.576.667q.066.07.121.144a2 2 0 0 1 .126-.144c.596-.638 1.52-.784 2.576-.667 1.076.12 1.877.46 2.383 1.1.495.624.607 1.41.607 2.168 0 .5-.047 1.002-.223 1.446.058.199.086.374.11.534l.033.19c.808.336 1.332 1.284 1.392 1.829v1.634C15 11.356 12.068 14 7.998 14m0-1.296c1.995 0 4.011-.969 4.377-1.25V7.738l-.02-.1c-.429.182-.94.253-1.511.253-1.003 0-1.802-.285-2.371-.865A2.82 2.812 0 0 1 8 6.38a2.835 2.828 0 0 1-.476.648c-.569.58-1.368.865-2.371.865-.57 0-1.082-.07-1.511-.254l-.02.101v3.714c.366.282 2.381 1.25 4.376 1.25M6.917 3.347c-.17-.18-.558-.36-1.472-.259-.892.099-1.294.353-1.499.611-.216.272-.323.689-.323 1.356 0 .693.113 1.022.27 1.197.142.158.454.33 1.262.33.746 0 1.171-.204 1.433-.47.275-.282.46-.722.54-1.356.102-.816-.033-1.218-.211-1.409m3.635-.259c-.913-.101-1.302.08-1.47.26-.179.19-.315.592-.212 1.408.08.634.265 1.074.54 1.356.262.266.687.47 1.434.47.807 0 1.12-.172 1.262-.33.156-.175.269-.504.269-1.197 0-.667-.108-1.084-.324-1.356-.204-.258-.606-.512-1.499-.61Z'/><path fill='#212121' d='M6.469 8.765a.656.655 0 0 1 .656.654v1.31a.656.655 0 0 1-1.313 0V9.42a.656.655 0 0 1 .657-.654Zm3.718.654v1.31a.656.655 0 0 1-1.312 0V9.42a.656.655 0 0 1 1.313 0z'/></svg>",
+ "cpp": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M28 14v-4h-2v4h-6v-4h-2v4h-4v2h4v4h2v-4h6v4h2v-4h4v-2z'/><path fill='#0288d1' d='M13.563 22A5.57 5.57 0 0 1 8 16.437v-2.873A5.57 5.57 0 0 1 13.563 8H18V2h-4.437A11.563 11.563 0 0 0 2 13.563v2.873A11.564 11.564 0 0 0 13.563 28H18v-6Z'/></svg>",
+ "craco": "<svg viewBox='0 0 32 32'><path fill='#00bfa5' d='M16 8a8 8 0 1 1-8 8 8.01 8.01 0 0 1 8-8m0-4a12 12 0 1 0 12 12A12 12 0 0 0 16 4'/><path fill='#00bfa5' d='m11.315 19.758-.755-.754 1.5-1.502L13.557 16l-1.499-1.502-1.499-1.502.754-.755.755-.754 2.304 2.226c1.267 1.225 2.303 2.254 2.303 2.287s-1.036 1.062-2.303 2.286l-2.304 2.227Zm5.323 0-.754-.754 1.499-1.502L18.882 16l-1.5-1.502-1.498-1.502.754-.755.755-.754 2.303 2.226C20.963 14.938 22 15.967 22 16s-1.037 1.062-2.304 2.286l-2.303 2.227ZM14 26h4v4h-4zm16-12v4h-4v-4zm-2.686 10.486-2.828 2.828-2.9-2.9 2.829-2.828zm-16.9-16.901-2.829 2.829-2.9-2.9 2.829-2.828zM7.586 21.586l2.828 2.828-2.9 2.9-2.828-2.829zm16.899-16.9 2.829 2.829-2.9 2.9-2.828-2.829zM6 14v4H2v-4zm8-12h4v4h-4z'/></svg>",
+ "credits": "<svg viewBox='0 0 32 32'><path fill='#9ccc65' d='M4 2h24v4H4zm6 6h12v4H10zm-6 6h24v4H4zm6 6h12v4H10zm-6 6h24v4H4z'/></svg>",
+ "crystal": "<svg viewBox='0 0 200 200'><path fill='#cfd8dc' d='m179.363 121.67-57.623 57.507c-.23.23-.576.346-.806.23l-78.713-21.09c-.346-.115-.577-.345-.577-.576L20.44 79.144c-.115-.345 0-.576.23-.806L78.294 20.83c.23-.23.576-.346.807-.23l78.713 21.09c.345.114.576.345.576.575l21.09 78.597c.23.346.115.577-.115.807zm-77.215-62.58-77.33 20.63c-.115 0-.23.23-.115.345l56.586 56.47c.115.115.346.115.346-.115l20.744-77.215c.115 0-.115-.23-.23-.116z'/></svg>",
+ "crystal_light": "<svg viewBox='0 0 200 200'><path fill='#37474f' d='m179.363 121.67-57.623 57.507c-.23.23-.576.346-.806.23l-78.713-21.09c-.346-.115-.577-.345-.577-.576L20.44 79.144c-.115-.345 0-.576.23-.806L78.294 20.83c.23-.23.576-.346.807-.23l78.713 21.09c.345.114.576.345.576.575l21.09 78.597c.23.346.115.577-.115.807zm-77.215-62.58-77.33 20.63c-.115 0-.23.23-.115.345l56.586 56.47c.115.115.346.115.346-.115l20.744-77.215c.115 0-.115-.23-.23-.116z'/></svg>",
+ "csharp": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M30 14v-2h-2V8h-2v4h-2V8h-2v4h-2v2h2v2h-2v2h2v4h2v-4h2v4h2v-4h2v-2h-2v-2Zm-4 2h-2v-2h2Zm-12.437 6A5.57 5.57 0 0 1 8 16.437v-2.873A5.57 5.57 0 0 1 13.563 8H18V2h-4.437A11.563 11.563 0 0 0 2 13.563v2.873A11.564 11.564 0 0 0 13.563 28H18v-6Z'/></svg>",
+ "css-map": "<svg viewBox='0 0 32 32'><path fill='#7e57c2' d='M26 11.998v2h2v14H14v-2h-2v4L30 30V11.998Z'/><path fill='#7e57c2' d='M16 14h-2v-2h-2v2c0 .193 0 .703 1.254 1.033A3.345 3.345 0 0 1 16 18h2v2h2v-2c0-.388-.562-.851-1.254-1.034C16.356 16.34 16 14.84 16 14m-3.254 2.966C10.356 16.34 10 14.84 10 14H8v-2H6v8h2v-2h4v2h2v-2c0-.388-.562-.851-1.254-1.034'/><path fill='#7e57c2' d='M2 2v21.998h22V2Zm20 12h-2v-2h-2v2c0 .193 0 .703 1.254 1.033A3.345 3.345 0 0 1 22 18v2a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2 2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2 2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2 2 2 0 0 1 2-2h2a2 2 0 0 1 2 2 2 2 0 0 1 2-2h2a2 2 0 0 1 2 2Z'/></svg>",
+ "css": "<svg viewBox='0 0 32 32'><path fill='#7e57c2' d='M20 18h-2v-2h-2v2c0 .193 0 .703 1.254 1.033A3.345 3.345 0 0 1 20 22h2v2h2v-2c0-.388-.562-.851-1.254-1.034C20.356 20.34 20 18.84 20 18m-3.254 2.966C14.356 20.34 14 18.84 14 18h-2v-2h-2v8h2v-2h4v2h2v-2c0-.388-.562-.851-1.254-1.034'/><path fill='#7e57c2' d='M24 4H4v20a4 4 0 0 0 4 4h16.16A3.84 3.84 0 0 0 28 24.16V8a4 4 0 0 0-4-4m2 14h-2v-2h-2v2c0 .193 0 .703 1.254 1.033A3.345 3.345 0 0 1 26 22v2a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2 2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2 2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2 2 2 0 0 1 2-2h2a2 2 0 0 1 2 2 2 2 0 0 1 2-2h2a2 2 0 0 1 2 2Z'/></svg>",
+ "cucumber": "<svg fill-rule='evenodd' viewBox='0 0 33 33'><path fill='#4caf50' d='M16.633 2.088c-7.028 0-12.714 5.686-12.714 12.714 0 6.187 4.435 11.327 10.288 12.471v3.64c7.609-1.148 14.346-7.187 14.848-15.117.303-4.772-2.076-9.644-6.09-12.01a10.6 10.6 0 0 0-1.455-.728l-.243-.097c-.223-.083-.448-.175-.68-.243a12.6 12.6 0 0 0-3.954-.63m2.62 4.707a1.39 1.39 0 0 0-1.213.485c-.233.31-.379.611-.534.922-.466 1.087-.31 2.252.388 3.106 1.087-.233 2.01-.927 2.475-2.014a2.45 2.45 0 0 0 .243-1.02c.048-.824-.634-1.405-1.359-1.48zm-5.654.073c-.708.067-1.382.63-1.382 1.407 0 .31.087.708.243 1.019.466 1.087 1.46 1.78 2.546 2.014.621-.854.782-2.019.316-3.106-.155-.31-.3-.616-.534-.85a1.36 1.36 0 0 0-1.188-.484zM9.79 10.603c-1.224.063-1.77 1.602-.752 2.402.31.233.612.403.922.558 1.087.466 2.344.306 3.275-.315-.233-1.01-1.023-1.936-2.11-2.402-.388-.155-.703-.243-1.092-.243-.087-.01-.161-.004-.243 0m11.961 4.707a3.55 3.55 0 0 0-2.013.583c.233 1.009 1.023 1.935 2.11 2.401.389.155.705.243 1.093.243 1.397.078 2.08-1.65.994-2.426-.31-.233-.611-.379-.922-.534a3.4 3.4 0 0 0-1.262-.267m-10.603.073a3.4 3.4 0 0 0-1.261.267c-.389.155-.69.325-.923.558-1.009.854-.33 2.48 1.068 2.402a2.5 2.5 0 0 0 1.092-.243c1.087-.466 1.859-1.392 2.014-2.401a3.47 3.47 0 0 0-1.99-.583m3.931 2.378c-1.087.233-2.009.927-2.475 2.014-.155.31-.243.684-.243.994-.077 1.32 1.724 2.03 2.5 1.02.233-.31.378-.612.534-.923.466-1.009.306-2.174-.316-3.105m2.887.073c-.621.854-.781 2.018-.315 3.105.155.311.3.616.534.85.854.93 2.65.242 2.572-.923 0-.31-.088-.708-.243-1.019-.466-1.087-1.46-1.78-2.547-2.013z'/></svg>",
+ "cuda": "<svg viewBox='0 0 32 32'><path fill='#7cb342' d='M12.496 10.16c-.184 0-.314.01-.496.022V12a7 7 0 0 1 .991-.062 7.34 7.34 0 0 1 5.335 2.457l-2.72 2.156c-1.213-1.922-1.568-2.767-3.606-3v5.468a4.8 4.8 0 0 0 1.486.234c3.969 0 7.667-4.847 7.667-4.847s-3.427-4.402-8.657-4.246m-9.222 4.468A12.46 12.46 0 0 1 12 10.184V8.715c-6.407.489-12 5.602-12 5.602s3.202 8.56 12 9.337v-1.641c-6.454-.756-8.726-7.385-8.726-7.385'/><path fill='#7cb342' d='M12 13.54V12a11.17 11.17 0 0 0-6.3 2.828s1.424 4.791 6.3 5.614v-1.423a6.48 6.48 0 0 1-3.72-3.913A5.04 5.04 0 0 1 12 13.54m0-7.566v2.74l.496-.032c7.267-.234 12.014 5.624 12.014 5.624s-5.442 6.247-11.107 6.247A8.4 8.4 0 0 1 12 20.431V22a11 11 0 0 0 1.19.108c5.276 0 9.058-2.478 12.757-5.479.612.467 3.12 1.59 3.64 2.079-3.51 2.779-11.696 5.013-16.337 5.013a12 12 0 0 1-1.25-.066V26h20V6Z'/></svg>",
+ "cypress": "<svg viewBox='0 0 24 24'><path fill='#00bfa5' d='M11.998 2A9.993 9.993 0 0 0 2 12a9.993 9.993 0 0 0 10 10c5.528 0 10-4.473 10-10-.001-5.527-4.51-10-10.002-10m-4.69 12.146c.327.436.763.618 1.381.618.292 0 .583-.037.837-.146.255-.108.546-.255.908-.473l1.019 1.454c-.836.692-1.782 1.018-2.873 1.018-.873 0-1.6-.182-2.254-.545a3.66 3.66 0 0 1-1.454-1.599c-.327-.691-.509-1.491-.509-2.437 0-.908.182-1.745.508-2.436a3.85 3.85 0 0 1 1.457-1.672c.617-.4 1.38-.582 2.217-.582.583 0 1.128.072 1.564.254.49.19.944.46 1.345.8l-1.018 1.382a4 4 0 0 0-.836-.474c-.254-.108-.582-.145-.873-.145-1.236 0-1.854.945-1.854 2.872-.036.983.146 1.673.437 2.11zm10 2.254c-.363 1.128-.909 1.964-1.673 2.582-.763.619-1.782.946-3.054 1.055l-.254-1.708c.836-.11 1.454-.292 1.854-.583.145-.108.437-.436.437-.436l-3.019-9.673h2.508l1.746 7.236 1.855-7.236h2.436z'/></svg>",
+ "d": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='M21.805 8.063a5 5 0 0 0-3.502.727A10.95 10.95 0 0 0 11 6H2.5a.5.5 0 0 0-.5.5v21a.5.5 0 0 0 .5.5H11a10.995 10.995 0 0 0 10.954-10.096 4.998 4.998 0 0 0-.149-9.841M11 24H6V10h5a7 7 0 0 1 0 14'/><circle cx='28' cy='7' r='1.5' fill='#f44336'/></svg>",
+ "dart": "<svg viewBox='0 0 32 32'><path fill='#4fc3f7' d='M16.83 2a1.3 1.3 0 0 0-.916.377l-.013.01L7.323 7.34l8.556 8.55v.005l10.283 10.277 1.96-3.529-7.068-16.96-3.299-3.297A1.3 1.3 0 0 0 16.828 2Z'/><path fill='#01579b' d='m7.343 7.32-4.955 8.565-.01.013a1.297 1.297 0 0 0 .004 1.835l.005.005 4.106 4.107 16.064 6.314 3.632-2.015-.098-.098-.025.002L15.995 15.97h-.012z'/><path fill='#01579b' d='m7.321 7.324 8.753 8.755h.013L26.16 26.156l3.835-.73L30 14.089l-4.049-3.965a6.5 6.5 0 0 0-3.618-1.612l.002-.043L7.323 7.325Z'/><path fill='#64b5f6' d='m7.332 7.335 8.758 8.75v.013l10.079 10.071L25.436 30H14.09l-3.967-4.048a6.5 6.5 0 0 1-1.611-3.618l-.045.004Z'/></svg>",
+ "dart_generated": "<svg viewBox='0 0 32 32'><path fill='#90a4ae' d='M16.83 2a1.3 1.3 0 0 0-.916.377l-.013.01L7.323 7.34l8.556 8.55v.005l10.283 10.277 1.96-3.529-7.068-16.96-3.299-3.297A1.3 1.3 0 0 0 16.828 2Z'/><path fill='#455a64' d='m7.343 7.32-4.955 8.565-.01.013a1.297 1.297 0 0 0 .004 1.835l.005.005 4.106 4.107 16.064 6.314 3.632-2.015-.098-.098-.025.002L15.995 15.97h-.012z'/><path fill='#455a64' d='m7.321 7.324 8.753 8.755h.013L26.16 26.156l3.835-.73L30 14.089l-4.049-3.965a6.5 6.5 0 0 0-3.618-1.612l.002-.043L7.323 7.325Z'/><path fill='#90a4ae' d='m7.332 7.335 8.758 8.75v.013l10.079 10.071L25.436 30H14.09l-3.967-4.048a6.5 6.5 0 0 1-1.611-3.618l-.045.004Z'/></svg>",
+ "database": "<svg viewBox='0 0 32 32'><path fill='#ffca28' d='M16 24c-5.525 0-10-.9-10-2v4c0 1.1 4.475 2 10 2s10-.9 10-2v-4c0 1.1-4.475 2-10 2m0-8c-5.525 0-10-.9-10-2v4c0 1.1 4.475 2 10 2s10-.9 10-2v-4c0 1.1-4.475 2-10 2m0-12C10.477 4 6 4.895 6 6v4c0 1.1 4.475 2 10 2s10-.9 10-2V6c0-1.105-4.477-2-10-2'/></svg>",
+ "deepsource": "<svg viewBox='0 0 16 16'><path fill='#1de9b6' d='M2 2h9a1 1 0 0 1 1 .992A1 1 0 0 1 11 4H2z'/><path fill='#f44336' d='M2 12h11a1 1 0 0 1 1 1 1 1 0 0 1-1 1H2z'/><path fill='#ffb300' d='M2 9h7a1 1 0 0 0 1-1 1 1 0 0 0-1-1H2z'/></svg>",
+ "denizenscript": "<svg viewBox='0 0 32 32'><path fill='#ffd54f' d='M6 4h8.804a17 17 0 0 1 4.54.459 8 8 0 0 1 3.597 2.21 10.5 10.5 0 0 1 2.278 3.887A17.8 17.8 0 0 1 26 16.23a15.8 15.8 0 0 1-.733 5.108 10.6 10.6 0 0 1-2.554 4.24 8.45 8.45 0 0 1-3.385 1.915 14.5 14.5 0 0 1-4.264.508H6Zm4 4.06v15.896h4.413a13 13 0 0 0 2.913-.228 4.45 4.45 0 0 0 1.945-1 5.1 5.1 0 0 0 1.261-2.316 15.8 15.8 0 0 0 .488-4.395 14.5 14.5 0 0 0-.488-4.274 5.5 5.5 0 0 0-1.367-2.324 4.57 4.57 0 0 0-2.23-1.13 21.7 21.7 0 0 0-3.954-.229Z' style='isolation:isolate'/></svg>",
+ "deno": "<svg viewBox='0 0 32 32'><path fill='#cfd8dc' d='M3.069 10.688C3.069 5.873 7.859 2 14 2a11.9 11.9 0 0 1 7.49 2.378 10.64 10.64 0 0 1 3.593 5.236l.015.049.017.057.034.108.048.198.134.463.14.529.238.875.38 1.386.613 2.28.692 2.593 1.116 4.168.42 1.571-.09.1A18.98 18.98 0 0 1 17.337 30l-.04-.273-.074-.545-.066-.395-.076-.52-.097-.634-.042-.25-.091-.602-.057-.356-.074-.462-.076-.444-.074-.432-.074-.422-.066-.413-.074-.395-.068-.38-.048-.281-.057-.271-.034-.173-.066-.35-.05-.246-.057-.305-.049-.215-.042-.205-.043-.2-.023-.132-.059-.248-.042-.181-.04-.182-.032-.114-.042-.167-.032-.157-.042-.156-.043-.148-.023-.091-.042-.142-.032-.13-.025-.092-.034-.084-.023-.072-.034-.116-.025-.085-.017-.049q-.067-.196-.148-.386l-.026-.051.19-.495-.75.026-.207.008c-6.82.14-11.222-2.759-11.222-7.301Zm14.345-4.101a2 2 0 1 0 0 2.827 2 2 0 0 0 0-2.827'/><path fill='#cfd8dc' d='M3.069 10.688c.95-12.027 21.388-11.423 22.64 1.205 1.027 3.74 2.21 8.244 3.222 11.998A18.98 18.98 0 0 1 17.337 30c-.407-2.79-.84-5.602-1.41-8.364a27 27 0 0 0-.505-2.123c-.104-.536-.523-1.043-.173-1.56-6.665.529-12.374-2.428-12.18-7.267Zm14.345-4.101c-1.807-1.861-4.689 1.02-2.827 2.828 1.807 1.86 4.689-1.021 2.827-2.828'/></svg>",
+ "deno_light": "<svg viewBox='0 0 32 32'><path fill='#455a64' d='M3.07 10.688C4.02-1.34 24.46-.735 25.713 11.893c1.027 3.74 2.21 8.244 3.222 11.998A18.98 18.98 0 0 1 17.339 30c-.407-2.79-.839-5.602-1.41-8.364a27 27 0 0 0-.505-2.123c-.103-.536-.522-1.043-.173-1.56-6.665.529-12.374-2.428-12.18-7.267Zm14.347-4.101c-1.808-1.861-4.69 1.02-2.828 2.828 1.808 1.86 4.69-1.021 2.828-2.828'/></svg>",
+ "dependabot": "<svg viewBox='0 0 32 32'><path fill='#448aff' d='M29.5 16H28v-4a2 2 0 0 0-2-2h-6V2.5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5H18v4H6a2 2 0 0 0-2 2v4H2.5a.5.5 0 0 0-.5.5v7a.5.5 0 0 0 .5.5H4v2a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2v-2h1.5a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.5-.5m-15.533 2.647-3.106 3.106a.6.6 0 0 1-.84 0l-1.867-1.866a.6.6 0 0 1 0-.84l.627-.64a.6.6 0 0 1 .848-.005l.005.005.8.8 2.053-2.04a.6.6 0 0 1 .84 0l.64.64a.58.58 0 0 1 0 .84m9.88 0-3.106 3.106a.6.6 0 0 1-.84 0l-1.867-1.866a.6.6 0 0 1 0-.84l.627-.64a.6.6 0 0 1 .84 0l.813.8 2.053-2.04a.6.6 0 0 1 .84 0l.64.64a.604.604 0 0 1 0 .84'/></svg>",
+ "dependencies-update": "<svg fill='none' viewBox='0 0 16 16'><path fill='#8bc34a' d='m10.484 3.635-2.5 2.546-.875-.891 1-1.018H8q-1.563 0-2.656 1.121Q4.249 6.515 4.25 8.122a3.5 3.5 0 0 0 .375 1.59l-.937.955a5.156 5.25 0 0 1-.516-1.24A4.81 4.897 0 0 1 3 8.121Q3 5.99 4.453 4.494T8 2.999h.11l-1-1.018.874-.891zm-4.968 8.747 2.5-2.546.875.891-1 1.018H8q1.563 0 2.656-1.12 1.095-1.123 1.094-2.73a3.5 3.5 0 0 0-.375-1.59l.938-.955q.343.604.515 1.24.172.638.172 1.305 0 2.131-1.453 3.628Q10.094 13.018 8 13.018h-.11l1 1.018-.874.891z'/></svg>",
+ "dhall": "<svg viewBox='0 0 24 24'><path fill='#78909c' d='M15.81 2.853 21.148 8.2l-1.54 1.518-5.326-5.327 1.528-1.54M2.853 20.373l6.995-6.962a1.04 1.04 0 0 1 .247-1.033c.42-.42 1.109-.42 1.528 0 .42.43.42 1.108 0 1.528-.28.28-.7.355-1.033.248l-6.962 6.995 11.418-3.82 3.799-6.845-5.317-5.327-6.855 3.799z'/></svg>",
+ "diff": "<svg fill='none' viewBox='0 0 24 24'><path d='M0 0h24v24H0z'/><path fill='#42a5f5' d='M18 23H4c-1.1 0-2-.9-2-2V7h2v14h14zM14.5 7V5h-2v2h-2v2h2v2h2V9h2V7zm2 6h-6v2h6zM15 1H8c-1.1 0-1.99.9-1.99 2L6 17c0 1.1.89 2 1.99 2H19c1.1 0 2-.9 2-2V7zm4 16H8V3h6.17L19 7.83z'/></svg>",
+ "dinophp": "<svg viewBox='0 0 1424.1 1368'><g fill='#ff5252' stroke-width='1.05'><path d='M1101.8 130.51c-12.441.289-30.445 1.731-55.957 5.878-32.232 5.25-56.168 11.548-61.523 12.914-39.16 10.394-67.506 22.153-93.018 32.757-14.383 5.984-33.282 13.859-56.589 25.932l-.734.42c-11.129 5.774-19.738 10.394-24.357 12.914l-2.414 1.258c-16.693 9.03-32.862 15.958-49.765 24.777 0 0-16.167 8.505-39.999 24.043-6.929 4.515-12.914 9.028-12.914 9.028a141 141 0 0 0-4.621 3.57c-5.67 4.514-10.917 9.554-15.641 15.118-4.515 5.25-11.025 12.81-15.33 23.518-7.034 17.113-4.723 32.86-3.673 40 1.47 9.133 4.513 28.66 19.841 41.68 11.758 10.182 25.618 11.864 33.807 12.704 50.079 4.62 98.163-5.354 129.03-9.344 41.47-5.249 73.071-2.31 98.269 0 45.984 4.305 96.587 9.028 148.87 42.1 12.388 7.873 45.67 30.446 74.436 72.021 12.598 18.163 31.811 45.985 39.16 85.985 7.244 39.895-.84 72.755 7.77 74.96 4.094 1.05 8.714-5.354 17.848-17.848a208.5 208.5 0 0 0 28.346-54.173c2.625-7.664 5.25-19.109 10.5-42.1 5.774-24.988 8.607-37.48 10.496-49.344 1.575-10.183 3.465-24.987 4.095-63.937.63-43.674.944-65.513-1.575-89.029-3.465-32.02-10.498-63.516-20.997-93.858-11.34-32.02-22.992-64.252-51.76-94.699-20.681-21.942-38.95-31.076-45.353-34.015-18.163-8.503-33.595-10.708-43.673-12.178-3.255-.472-10.133-1.338-22.574-1.05zm-100.67 82.398c12.105.117 20.275 2.825 22.244 9.046 5.25 16.588-23.937 48.503-74.436 64.46-50.499 15.854-117.38 7.034-122.62-9.554s41.47-31.39 91.969-47.243c31.562-9.908 62.671-16.905 82.847-16.709z'/><path d='M925.47 442.64c1.785-5.354 27.401-1.47 37.27 0 27.926 4.2 55.433 8.399 88.189 25.932 23.517 12.493 59.107 36.85 85.774 80.105 5.25 8.399 30.026 49.869 33.176 110.03 2.94 54.908-13.858 95.748-22.677 116.53-37.06 87.349-106.04 132.7-139.42 154.02-22.887 14.593-81.575 48.714-161.05 57.953-40.735 4.724-94.698 3.884-96.798-6.72-.63-3.359 3.99-7.348 11.024-13.437 3.255-2.835 11.968-9.869 26.982-13.753 8.399-2.205 12.178-1.68 32.651-2.1 19.632-.42 29.396-.63 35.905-1.47 14.593-2.1 28.976-5.984 42.52-11.758 9.764-4.094 17.323-7.139 26.457-13.753a107.1 107.1 0 0 0 26.982-28.87c4.094-6.195 7.874-11.969 11.024-20.473 2.625-6.929 3.045-10.814 6.93-16.588 3.674-5.459 6.718-7.139 11.128-11.339 3.465-3.254 6.09-6.404 17.848-26.142 8.504-14.278 12.703-21.417 15.328-26.457 12.178-23.622 16.693-45.459 19.212-57.848 8.924-43.989 4.62-80.945 2.415-99.527-3.99-32.86-7.874-65.512-28.346-101.94-13.543-24.147-31.076-55.328-64.777-76.85-6.614-4.514-19.003-11.549-17.743-15.538z'/><path d='M843.89 890.41s6.614-13.963 21.522-55.958c5.67-15.853 8.924-24.987 10.394-38.74 1.89-17.428-.21-31.286-4.41-58.687-1.05-7.244-2.624-14.383-4.829-21.417-1.575-5.04-3.044-8.294-7.664-18.898-12.073-27.192-14.803-32.336-20.052-38.635-8.084-9.659-13.018-11.024-14.803-11.339-7.979-1.575-17.848 2.205-20.787 8.924-2.624 5.984 1.05 12.388 5.04 20.052 0 0 5.774 11.234 19.108 46.089 7.139 18.793 7.769 34.646 8.189 47.559 1.574 46.719-17.953 82.099-24.462 93.438-17.848 31.286-40 49.134-56.273 61.942-34.121 26.772-70.761 41.47-89.448 48.924-29.921 11.968-44.724 14.173-57.428 30.76-9.87 12.914-11.758 26.038-15.013 48.19 0 0-5.46 38.005 9.343 82.099 2.73 8.294 5.88 15.433 12.913 20.262 10.08 6.824 22.572 4.62 32.756 2.834 13.858-2.414 26.982-8.084 39.265-13.333 9.764-4.199 16.588-7.769 24.672-5.354 3.465 1.05 6.72 3.045 11.34 7.77 10.603 11.023 16.272 24.356 19.841 37.584 5.985 21.837 9.24 34.121 2.415 40.84-7.349 7.244-23.937 5.67-34.016-.42-6.929-4.2-7.244-8.504-15.013-11.339-5.984-2.205-10.499-1.365-20.997-.42-8.714.735-17.533.84-26.247 1.26-22.887 1.155-34.33 1.68-44.094 1.26-13.438-.63-19.842-1.995-23.832 2.415-4.514 4.934-1.575 12.074-7.244 16.588-3.36 2.624-7.454 2.73-12.178 2.834-5.46.105-11.758-1.155-15.328-3.254-8.714-5.04-6.824-17.953-9.344-47.35-1.575-19.002-2.624-15.222-6.929-51.337-.84-7.98-2.1-15.853-3.884-23.622-1.68-7.454-2.835-10.08-4.62-18.058-1.575-7.034-2.834-14.278-3.674-21.417-1.26-10.29-4.305-29.081-11.34-65.932 0 0-13.752-59.002-4.409-78.53 3.045-6.3 7.664-8.084 7.664-8.084 1.26-.525 2.31-.525 6.93-.42 7.453.315 11.128.42 16.587 1.26 7.245 1.05 6.825 1.784 9.66 1.574 4.724-.315 9.133-2.204 12.492-5.354 5.46-5.354 4.935-13.438 5.145-20.262.104-3.15.21-.735 11.024-41.575 8.818-32.966 7.979-31.706 10.499-39.37 4.934-14.698 9.973-25.512 15.328-36.64 19.318-39.79 28.87-59.632 39.685-77.27 21.627-35.38 42.1-57.953 67.61-86.194 25.723-28.45 48.085-52.913 83.78-78.11 38.005-26.772 60.682-32.966 78.53-34.75 11.234-1.156 27.61-2.836 45.669 4.408 29.71 11.968 42.834 39.895 55.013 65.512 19.527 41.155 21.417 77.795 22.257 97.952 1.574 37.375-4.62 64.987-8.084 80.105-10.92 47.454-30.131 81.05-41.47 85.354-1.26.525-7.14 2.415-9.344 7.454-1.575 3.675-.525 7.245-.21 8.714 2.94 13.018-10.92 30.446-13.753 34.016-12.808 16.063-29.606 20.577-38.845 23.097-2.205.63-33.491 8.714-39.265-1.574-2.205-3.99.525-8.714 4.62-17.428z'/><path d='M596.23 710.77c.105 4.095-.105 8.084-.42 12.178-1.785 21.207-8.819 33.806-17.953 54.803-9.974 22.992-15.853 36.43-20.787 55.853-3.15 12.388-7.244 24.462-9.554 37.06-2.73 14.908-2.624 21.627-8.189 25.092-6.089 3.78-10.814-1.68-29.396-2.1-9.658-.21-16.903 1.05-24.252 5.88-6.824 4.514-10.709 9.868-11.863 12.703a34 34 0 0 0-2.73 14.067c1.366 53.753.84 61.417-1.364 64.777-3.57 5.46-15.328 8.294-33.176 14.803-6.93 2.52-24.987 8.924-59.107 15.118-20.262 3.675-35.59 6.51-57.218 7.14-14.908.63-29.711 0-44.514-1.995-17.113-2.31-41.155-5.564-66.351-19.108-10.604-5.67-47.454-25.512-64.777-69.081-11.968-30.131-8.714-56.798-7.034-69.816a148 148 0 0 1 22.677-61.732c8.084-12.283 23.307-34.541 52.808-47.244 15.748-6.72 32.86-9.659 49.974-8.504 36.535 2.73 77.69 25.512 86.614 58.582 1.05 3.78 5.984 24.042-4.62 38.845-1.574 2.205-6.194 8.714-11.339 8.084-5.354-.63-7.139-8.294-14.593-22.152-6.194-11.549-9.343-17.323-14.803-22.677-14.488-14.068-34.54-14.068-36.64-14.068-21.207.315-34.75 13.858-40.42 19.422-4.304 4.304-21.522 21.837-20.997 49.134.105 3.465.735 25.827 17.848 43.674 18.268 19.213 43.15 20.472 57.218 20.997 32.231 1.26 58.162-10.919 85.774-26.982a334 334 0 0 0 8.084-4.83c48.4-30.235 88.19-64.566 112.34-87.558 16.588-15.853 21.522-21.942 41.89-40.42 15.853-14.383 18.268-15.643 27.296-25.092 7.98-8.4 13.543-15.223 24.252-23.832 4.83-3.99 9.974-7.664 15.328-11.024zm286.82 313.43c1.995-1.47 6.194-1.47 15.013-1.575 6.194-.105-1.155 0 12.808-.105 9.659-.105 11.129.105 16.693 0 2.625-.105 3.36-.105 8.399-.315 3.36-.105 8.084-.21 11.339-.21 1.575 0 2.835 1.26 2.835 2.73v.105c-.105 6.09.21 12.283-.105 19.212a129 129 0 0 0 0 9.029 86 86 0 0 1 0 7.349c-.315 6.614-.42 23.202 4.094 47.874 1.155 6.3 2.205 11.444 6.51 15.958 6.298 6.614 15.537 7.139 21.521 7.559 12.493.84 22.572-3.045 34.121-7.56 15.433-6.088 18.478-10.078 26.247-8.923 2.835.42 9.239 1.785 17.218 10.814 5.88 6.614 8.61 12.913 13.858 25.302 2.835 6.404 5.145 12.913 7.034 19.738 3.36 12.388 4.935 18.583 2.94 22.677-5.67 11.338-37.06 4.724-43.254.84-2.835-1.785-6.09-4.2-6.09-4.2-1.89-1.364-3.464-2.624-4.514-3.464-3.884-3.045-6.719-5.144-7.664-5.984-.525-.42-1.155-.84-1.68-1.26-.314-.21-.63-.42-1.05-.63-1.364-.735-2.73-1.365-4.199-1.785-1.89-.735-3.884-1.365-5.984-1.68-1.575-.314-1.89-.105-9.449-.21h-4.724c-1.05 0-2.31.105-6.614.63-4.2.525-6.51.735-8.504 1.05l-6.615 1.155s-9.868 2.1-18.163 3.255c-3.57.525-4.094.42-7.139.945-4.41.84-6.614 1.154-7.559 1.89-4.41 3.359 1.68 12.177-1.574 18.582-4.725 8.924-26.877 11.13-35.695 1.575-3.465-3.674-3.885-8.189-4.41-12.703-2.415-20.577-5.984-40.945-7.874-61.522-.84-9.449-1.68-18.268-3.674-30.236-1.05-6.51-3.045-25.197-7.14-62.467-.524-3.674-1.05-10.394 3.045-13.438z'/></g></svg>",
+ "disc": "<svg viewBox='0 0 32 32'><path fill='#b0bec5' d='M16 12a4 4 0 1 1-4 4 4.005 4.005 0 0 1 4-4m0-10a14 14 0 1 0 14 14A14 14 0 0 0 16 2'/></svg>",
+ "django": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='M22 2h4v4h-4zm0 8v12.13A3.88 3.88 0 0 1 18.13 26H18v4h.13A7.866 7.866 0 0 0 26 22.13V10Zm-8-8h4v20h-4z'/><path fill='#43a047' d='M11.838 12A2.165 2.165 0 0 1 14 14.162v4.955l-.77.257a5.03 5.03 0 0 1-2.812.108A3.19 3.19 0 0 1 8 16.384v-.547A3.84 3.84 0 0 1 11.838 12m0-4A7.84 7.84 0 0 0 4 15.837v.547a7.19 7.19 0 0 0 5.448 6.978 9.03 9.03 0 0 0 5.047-.194L18 22v-7.838A6.16 6.16 0 0 0 11.838 8'/></svg>",
+ "dll": "<svg viewBox='0 0 24 24'><path fill='#42a5f5' d='M6 2a2 2 0 0 0-2 2v16c0 1.11.89 2 2 2h6v-2H6V4h7v5h5v3h2V8l-6-6m4 12a.26.26 0 0 0-.26.21l-.19 1.32c-.3.13-.59.29-.85.47l-1.24-.5c-.11 0-.24 0-.31.13l-1 1.73c-.06.11-.04.24.06.32l1.06.82a4.2 4.2 0 0 0 0 1l-1.06.82a.26.26 0 0 0-.06.32l1 1.73c.06.13.19.13.31.13l1.24-.5c.26.18.54.35.85.47l.19 1.32c.02.12.12.21.26.21h2c.11 0 .22-.09.24-.21l.19-1.32c.3-.13.57-.29.84-.47l1.23.5c.13 0 .26 0 .33-.13l1-1.73a.26.26 0 0 0-.06-.32l-1.07-.82c.02-.17.04-.33.04-.5s-.01-.33-.04-.5l1.06-.82a.26.26 0 0 0 .06-.32l-1-1.73c-.06-.13-.19-.13-.32-.13l-1.23.5c-.27-.18-.54-.35-.85-.47l-.19-1.32A.236.236 0 0 0 20 14m-1 3.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5c-.84 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5'/></svg>",
+ "docker": "<svg viewBox='0 0 24 24'><path fill='#0288d1' d='M21.81 10.25c-.06-.04-.56-.43-1.64-.43-.28 0-.56.03-.84.08-.21-1.4-1.38-2.11-1.43-2.14l-.29-.17-.18.27c-.24.36-.43.77-.51 1.19-.2.8-.08 1.56.33 2.21-.49.28-1.29.35-1.46.35H2.62c-.34 0-.62.28-.62.63 0 1.15.18 2.3.58 3.38.45 1.19 1.13 2.07 2 2.61.98.6 2.59.94 4.42.94.79 0 1.61-.07 2.42-.22 1.12-.2 2.2-.59 3.19-1.16A8.3 8.3 0 0 0 16.78 16c1.05-1.17 1.67-2.5 2.12-3.65h.19c1.14 0 1.85-.46 2.24-.85.26-.24.45-.53.59-.87l.08-.24zm-17.96.99h1.76c.08 0 .16-.07.16-.16V9.5c0-.08-.07-.16-.16-.16H3.85c-.09 0-.16.07-.16.16v1.58c.01.09.07.16.16.16m2.43 0h1.76c.08 0 .16-.07.16-.16V9.5c0-.08-.07-.16-.16-.16H6.28c-.09 0-.16.07-.16.16v1.58c.01.09.07.16.16.16m2.47 0h1.75c.1 0 .17-.07.17-.16V9.5c0-.08-.06-.16-.17-.16H8.75c-.08 0-.15.07-.15.16v1.58c0 .09.06.16.15.16m2.44 0h1.77c.08 0 .15-.07.15-.16V9.5c0-.08-.06-.16-.15-.16h-1.77c-.08 0-.15.07-.15.16v1.58c0 .09.07.16.15.16M6.28 9h1.76c.08 0 .16-.09.16-.18V7.25c0-.09-.07-.16-.16-.16H6.28c-.09 0-.16.06-.16.16v1.57c.01.09.07.18.16.18m2.47 0h1.75c.1 0 .17-.09.17-.18V7.25c0-.09-.06-.16-.17-.16H8.75c-.08 0-.15.06-.15.16v1.57c0 .09.06.18.15.18m2.44 0h1.77c.08 0 .15-.09.15-.18V7.25c0-.09-.07-.16-.15-.16h-1.77c-.08 0-.15.06-.15.16v1.57c0 .09.07.18.15.18m0-2.28h1.77c.08 0 .15-.07.15-.16V5c0-.1-.07-.17-.15-.17h-1.77c-.08 0-.15.06-.15.17v1.56c0 .08.07.16.15.16m2.46 4.52h1.76c.09 0 .16-.07.16-.16V9.5c0-.08-.07-.16-.16-.16h-1.76c-.08 0-.15.07-.15.16v1.58c0 .09.07.16.15.16'/></svg>",
+ "doctex-installer": "<svg viewBox='0 0 16 16'><path fill='#f57f17' d='M1.25 3 1 5h.5c.25-1.25.25-1.5.988-1.5h.262a.25.25 0 0 1 .25.25v5.5c0 .138 0 .25-.5.25H2v.5h3v-.5h-.5c-.5 0-.5-.112-.5-.25v-5.5a.25.25 0 0 1 .25-.25h.25c.75 0 .75.25 1 1.5H6l-.25-2zM6 12v2h9v-2h-.5v.5c0 .5-.112.5-.25.5h-7.5c-.138 0-.25 0-.25-.5V12zm4-10v8c-.5-.75-2-1.75-2.75-2L7 8.5c1.5.5 2.75 2 3.5 3.5.75-1.5 2-3 3.5-3.5l-.25-.5C13 8.25 11.5 9.25 11 10V2z'/></svg>",
+ "doctex.clone": "<svg viewBox='0 0 1024 1024'><path fill='#f57f17' d='M80 192 64 320h32c16-80 16-96 63.242-96H176c8.837 0 16 7.163 16 16v352c0 8.837 0 16-32 16h-32v32h192v-32h-32c-32 0-32-7.163-32-16V240c0-8.837 7.163-16 16-16h16c48 0 48 16 64 96h32l-16-128zm560 0v32c16 0 45.713 0 52.57 16L776 434.666 708.57 592c-6.857 16-52.57 16-68.57 16v32h128v-32s-34.285 0-27.428-16L792 472l51.428 120c3.103 7.24-1.52 16-11.428 16v32h128v-32c-16 0-45.713 0-52.57-16L824 397.334 891.43 240c6.857-16 52.57-16 68.57-16v-32H832v32s34.285 0 27.428 16L808 360l-51.428-120c-3.103-7.24 1.52-16 11.428-16v-32zM320 384v32h32c32 0 32 7.163 32 16v352c0 8.837 0 16-32 16h-32v32h304l16-128h-32c-16 80-16 96-64 96h-64c-32 0-32-7.163-32-16V624h80c8.837 0 16 0 16 32v16h32V544h-32v16c0 32-7.163 32-16 32h-80V432c0-8.837 0-16 32-16h64c48 0 48 16 64 96h32l-16-128z'/></svg>",
+ "document": "<svg fill='none' viewBox='0 0 24 24'><path d='M0 0h24v24H0z'/><path fill='#42a5f5' d='M8 16h8v2H8zm0-4h8v2H8zm6-10H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8zm4 18H6V4h7v5h5z'/></svg>",
+ "dotjs": "<svg viewBox='0 0 400 400'><g fill='#2196f3' fill-opacity='.604' transform='translate(-6.66 100.49)'><ellipse cx='37.18' cy='-256.97' rx='110.14' ry='139.47' transform='matrix(-.3005 .95378 -.96071 -.27755 0 0)'/><ellipse cx='38.835' cy='-197.03' rx='110.14' ry='139.47' transform='matrix(-.3005 .95378 -.96071 -.27755 0 0)'/><ellipse cx='-224.78' cy='-5.066' rx='110.14' ry='139.47' transform='matrix(-.95378 -.3005 .27755 -.96071 0 0)'/><ellipse cx='-228.55' cy='-60.291' rx='110.14' ry='139.47' transform='matrix(-.95378 -.3005 .27755 -.96071 0 0)'/></g></svg>",
+ "drawio": "<svg viewBox='0 0 32 32'><path fill='#fb8c00' d='m25.329 20-7.001-8H20V4h-8v8h1.672l-7.001 8H4v8h8v-8H9.328L16 12.376 22.672 20H20v8h8v-8z'/></svg>",
+ "drizzle": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='m5.22 23.118 3.647-6.593a1.712 1.712 0 1 0-2.996-1.657L2.224 21.46a1.712 1.712 0 0 0 2.996 1.658m12.02 0 3.648-6.593a1.712 1.712 0 1 0-2.996-1.657l-3.648 6.592a1.712 1.712 0 0 0 2.996 1.658m-3.378-5.96 3.88-6.588a1.706 1.706 0 0 0-2.94-1.73l-3.88 6.588a1.706 1.706 0 0 0 2.94 1.73m12.028 0 3.88-6.588a1.706 1.706 0 0 0-2.94-1.73l-3.88 6.588a1.706 1.706 0 0 0 2.94 1.73'/></svg>",
+ "drone": "<svg viewBox='0 0 230 230'><path fill='#cfd8dc' d='m57.01 36.707-.835.834 34.036 34.035c-4.813 7.514-7.584 16.742-7.584 27.239 0 29.18 21.422 48.557 48.557 48.557 10.14 0 19.48-2.706 27.205-7.618l34.21 34.21c-17.726 23.411-45.89 38.151-77.601 38.151-53.627 0-97.114-42.154-97.114-97.114 0-32.685 15.38-60.84 39.125-78.293zm16.188-9.611c12.66-5.927 26.836-9.21 41.799-9.21 53.626 0 97.114 42.155 97.114 97.114 0 15.117-3.29 29.265-9.176 41.833l-30.78-30.78c4.812-7.514 7.584-16.742 7.584-27.239 0-29.18-21.422-48.557-48.557-48.557-10.14 0-19.48 2.706-27.205 7.617zm57.985 100.853c-16.281 0-29.134-11.626-29.134-29.135 0-17.508 12.853-29.134 29.134-29.134s29.134 11.626 29.134 29.134-12.853 29.135-29.134 29.135'/></svg>",
+ "drone_light": "<svg viewBox='0 0 230 230'><path fill='#546e7a' d='m57.011 36.707-.834.834 34.036 34.035c-4.813 7.514-7.584 16.742-7.584 27.239 0 29.18 21.422 48.557 48.557 48.557 10.14 0 19.48-2.706 27.205-7.618l34.21 34.21c-17.726 23.411-45.89 38.151-77.601 38.151-53.627 0-97.114-42.154-97.114-97.114 0-32.685 15.38-60.84 39.125-78.293zm16.19-9.611c12.66-5.927 26.835-9.21 41.798-9.21 53.626 0 97.114 42.155 97.114 97.114 0 15.117-3.29 29.265-9.176 41.833l-30.78-30.78c4.813-7.514 7.584-16.742 7.584-27.239 0-29.18-21.422-48.557-48.557-48.557-10.14 0-19.48 2.706-27.205 7.617zm57.984 100.853c-16.281 0-29.134-11.626-29.134-29.135s12.853-29.134 29.134-29.134 29.134 11.626 29.134 29.134-12.853 29.135-29.134 29.135'/></svg>",
+ "dtx.clone": "<svg viewBox='0 0 1024 1024'><path fill='#f57f17' d='M80 192 64 320h32c16-80 16-96 63.242-96H176c8.837 0 16 7.163 16 16v352c0 8.837 0 16-32 16h-32v32h192v-32h-32c-32 0-32-7.163-32-16V240c0-8.837 7.163-16 16-16h16c48 0 48 16 64 96h32l-16-128zm560 0v32c16 0 45.713 0 52.57 16L776 434.666 708.57 592c-6.857 16-52.57 16-68.57 16v32h128v-32s-34.285 0-27.428-16L792 472l51.428 120c3.103 7.24-1.52 16-11.428 16v32h128v-32c-16 0-45.713 0-52.57-16L824 397.334 891.43 240c6.857-16 52.57-16 68.57-16v-32H832v32s34.285 0 27.428 16L808 360l-51.428-120c-3.103-7.24 1.52-16 11.428-16v-32zM320 384v32h32c32 0 32 7.163 32 16v352c0 8.837 0 16-32 16h-32v32h304l16-128h-32c-16 80-16 96-64 96h-64c-32 0-32-7.163-32-16V624h80c8.837 0 16 0 16 32v16h32V544h-32v16c0 32-7.163 32-16 32h-80V432c0-8.837 0-16 32-16h64c48 0 48 16 64 96h32l-16-128z'/></svg>",
+ "duc": "<svg viewBox='0 0 16 16'><path fill='#ff5252' d='M12.564 8.49c.06-.232.124-.46.156-.696.096-.719-.02-1.408-.268-2.08a1 1 0 0 0-.077-.162c-.124-.193-.343-.2-.47-.01a1.3 1.3 0 0 0-.128.277c-.238.653-.553 1.26-1.049 1.758a4.6 4.6 0 0 1-.863.678c.057-.23.161-.42.248-.605.34-.721.513-1.483.619-2.264.088-.658.102-1.32-.072-1.964-.317-1.174-1.01-2.022-2.23-2.32-1.232-.301-2.266.07-3.093 1.026-.227.262-.436.541-.663.804-.377.44-.842.758-1.382.97-.163.065-.328.058-.494.06-.144 0-.29.001-.425.057-.411.168-.503.68-.172 1.03.316.334.732.465 1.176.536.54.088 1.041-.087 1.55-.216.48-.123.96-.253 1.439-.38.086-.022.197-.062.25.008.047.062-.023.158-.062.231-.117.218-.301.369-.502.504-.511.344-1.056.639-1.535 1.031-.914.751-1.528 1.684-1.673 2.877-.169 1.384.26 2.593 1.173 3.627.6.68 1.332 1.196 2.202 1.467 1.677.523 3.243.282 4.656-.784 1.448-1.092 2.2-2.583 2.438-4.356a3.5 3.5 0 0 0 .016-.934c-.051-.381-.274-.478-.585-.255q-.065.046-.134.088c-.008.005-.021-.001-.046-.004Z'/></svg>",
+ "dune": "<svg viewBox='0 0 24 24'><path fill='#f57c00' d='m14 6-3.75 5 2.85 3.8-1.6 1.2C9.81 13.75 7 10 7 10l-6 8h22z'/></svg>",
+ "edge": "<svg viewBox='0 0 24 24'><path fill='#ef6c00' d='M12 15c.81 0 1.5-.3 2.11-.89.59-.61.89-1.3.89-2.11s-.3-1.5-.89-2.11C13.5 9.3 12.81 9 12 9s-1.5.3-2.11.89C9.3 10.5 9 11.19 9 12s.3 1.5.89 2.11c.61.59 1.3.89 2.11.89m0-13c2.75 0 5.1 1 7.05 2.95S22 9.25 22 12v1.45c0 1-.35 1.85-1 2.55-.7.67-1.5 1-2.5 1-1.2 0-2.19-.5-2.94-1.5-1 1-2.18 1.5-3.56 1.5-1.37 0-2.55-.5-3.54-1.46C7.5 14.55 7 13.38 7 12c0-1.37.5-2.55 1.46-3.54C9.45 7.5 10.63 7 12 7c1.38 0 2.55.5 3.54 1.46C16.5 9.45 17 10.63 17 12v1.45c0 .41.16.77.46 1.08s.65.47 1.04.47c.42 0 .77-.16 1.07-.47s.43-.67.43-1.08V12c0-2.19-.77-4.07-2.35-5.65S14.19 4 12 4s-4.07.77-5.65 2.35S4 9.81 4 12s.77 4.07 2.35 5.65S9.81 20 12 20h5v2h-5c-2.75 0-5.1-1-7.05-2.95S2 14.75 2 12s1-5.1 2.95-7.05S9.25 2 12 2'/></svg>",
+ "editorconfig": "<svg clip-rule='evenodd' image-rendering='optimizeQuality' shape-rendering='geometricPrecision' text-rendering='geometricPrecision' viewBox='0 0 3473 3473'><path fill='#ede7f6' d='M989.342 1977.409c41.146-26.835 75.137-93.922 54.564-141.33-56.353 24.151-53.67 79.61-54.564 141.33m636.877 153.851c44.724-14.311 87.66-64.402 63.509-116.283-34.886 24.151-57.248 57.247-63.51 116.284z'/><g fill='#fafafa'><path d='M374.827 2871.899c0 56.352 14.312 117.178 53.67 138.645 144.907 81.4 652.977 17.89 825.614-20.573 90.343-20.573 163.692-87.66 248.668-124.334 191.421-83.187 330.067-150.274 483.025-262.085 110.916-81.399 287.131-310.388 305.915-447.245l-151.169-33.991c-3.578 153.852-38.463 188.737-175.32 224.517-92.132 25.046-271.925 30.413-365.846 14.312-124.334-20.574-180.687-85.871-237.04-160.114-109.128-144.907 24.151-245.985-148.485-255.824-181.582 222.728-501.81 62.614-642.244 40.252-59.93 86.765-200.366 650.294-198.577 779.1 86.766-29.517 141.33 2.684 219.15 33.097 275.503 106.444 34.885 200.366-75.137 172.636-75.137-17.89-98.394-67.086-142.224-98.393m360.48-1285.383c111.81 21.468 211.1 67.982 305.915 115.39 154.747 76.926 182.476 66.192 196.788 173.53 1.789 19.68-1.789 30.413 54.564 48.303 94.816 29.518-54.564-23.257 199.471-22.362 151.169.894 497.337 61.72 609.148 132.384 46.513 29.519 37.568 67.087 194.999 62.615-1.79-185.16-50.986-461.557-123.44-631.51-88.554-205.733-205.733-237.04-444.561-313.966-139.54-44.725-549.217-93.922-676.235-15.207-118.967 74.243-141.33 162.798-252.246 318.439-32.202 45.619-43.83 80.504-64.403 132.384'/><path d='M1720.14 1966.675c89.45 36.674-4.472 273.714-128.806 216.466-40.252-113.6 55.458-178.003 81.398-228.99-53.67-8.05-206.627-32.2-252.246-15.206-59.036 22.363-72.454 148.486-42.041 207.522 143.118 280.87 775.523 220.94 708.436 2.684-26.835-88.555-51.88-102.867-142.224-133.28-72.454-24.15-144.907-49.196-224.517-49.196m-1124.374-31.307c71.56 68.875 233.462 79.61 338.117 84.976 13.418-138.646 25.046-242.407 135.963-234.356 54.564 74.242 25.94 161.902-31.307 218.255 97.5-.894 153.852-74.242 139.54-180.687-82.293-59.036-331.856-177.109-457.084-194.104-34.885 37.569-120.756 243.301-125.229 305.916'/></g><path d='M427.602 2820.913c59.036-5.367 212.889 39.357 225.412 89.449-95.71 11.628-217.361 2.683-225.412-89.45zm-52.775 50.986c43.83 31.307 67.087 80.504 142.224 98.393 110.022 27.73 350.64-66.192 75.137-172.636-77.82-30.413-132.384-62.614-219.15-33.096-1.789-128.807 138.646-692.336 198.577-779.101 140.435 22.362 460.662 182.476 642.244-40.252 172.636 9.84 39.357 110.917 148.485 255.824 56.353 74.243 112.706 139.54 237.04 160.114 93.921 16.1 273.714 10.734 365.846-14.312 136.857-35.78 171.742-70.665 175.32-224.517l151.17 33.99c-18.785 136.858-195 365.847-305.916 447.246-152.958 111.81-291.604 178.898-483.025 262.085-84.976 36.674-158.325 103.761-248.668 124.334-172.637 38.463-680.707 101.972-825.614 20.574-39.358-21.468-53.67-82.294-53.67-138.646M1626.22 2131.26c6.261-59.037 28.623-92.133 63.508-116.284 24.152 51.88-18.784 101.972-63.508 116.284m93.921-164.586c79.61 0 152.063 25.045 224.517 49.197 90.344 30.412 115.39 44.724 142.224 133.279 67.087 218.255-565.318 278.186-708.436-2.684-30.413-59.036-16.995-185.16 42.041-207.522 45.619-16.995 198.577 7.156 252.246 15.207-25.94 50.986-121.65 115.389-81.398 228.99 124.334 57.247 218.255-179.793 128.806-216.467m-730.798 10.734c.894-61.72-1.79-117.179 54.564-141.33 20.573 47.408-13.418 114.495-54.564 141.33m-393.576-42.041c4.473-62.615 90.344-268.347 125.229-305.916 125.228 16.995 374.791 135.068 457.084 194.104 14.312 106.445-42.04 179.793-139.54 180.687 57.247-56.353 85.87-144.013 31.307-218.255-110.917-8.05-122.545 95.71-135.963 234.356-104.655-5.367-266.558-16.1-338.117-84.976m-89.449-71.56c-33.096-91.238-33.096-233.462 107.339-245.09l-71.56 199.471c-18.783 42.936-18.783 33.096-35.779 45.62zm228.99-277.292c20.573-51.88 32.201-86.765 64.403-132.384 110.917-155.641 133.279-244.196 252.246-318.439 127.018-78.715 536.694-29.518 676.235 15.207 238.828 76.926 356.007 108.233 444.561 313.966 72.454 169.953 121.65 446.35 123.44 631.51-157.43 4.472-148.486-33.096-195-62.615-111.81-70.664-457.978-131.49-609.147-132.384-254.035-.895-104.655 51.88-199.471 22.362-56.353-17.89-52.775-28.624-54.564-48.302-14.312-107.34-42.041-96.605-196.788-173.531-94.816-47.408-194.104-93.922-305.915-115.39m1583.247-43.83c-16.995-56.352 14.312-52.775 68.876-91.238 31.307-22.362 56.353-45.619 94.816-67.086 144.013-80.504 412.36-93.922 526.854 1.789 46.514 38.463 122.545 113.6 110.917 211.994-24.151 195.893-158.325 303.232-268.347 392.68-111.811 91.239-297.865 185.16-490.18 122.546-16.101-39.358-3.578-288.92-22.363-381.053-16.995-82.293-8.05-91.238 39.358-140.435 139.54-144.907 441.878-250.457 613.62-126.123 72.454 53.67 51.88 74.243 89.449 115.39 46.513-50.092-40.252-218.256-360.48-207.522-217.36 7.156-311.282 177.109-402.52 169.058m-1302.377-508.964c4.472-124.335 118.967-381.948 233.461-471.397 138.646-107.338 283.554-208.416 496.442-87.66 52.775 29.519 50.092 44.725 55.459 118.073 4.472 70.665-1.79 96.605-19.679 153.852-141.33 456.19-259.402 194.105-712.014 302.338 16.995-148.485 145.802-280.87 217.361-349.746 122.545-118.967 211.1-195.893 395.365-170.847 50.986 84.976 56.352 138.646-5.367 237.934-82.293 132.385-102.867 124.334-90.344 214.678 64.403-16.101 84.082-78.715 113.6-141.33 179.793-375.686-81.398-421.305-241.512-352.429-107.339 45.62-298.76 256.719-361.374 383.736-12.523 25.046-25.94 57.248-37.568 84.977zm708.436 18.784c18.784-111.811 129.7-139.54 129.7-483.92 0-148.485-182.475-281.764-421.304-182.475-204.838 84.082-236.145 148.485-345.273 313.071-102.867 155.642-99.289 326.49-187.843 470.502-25.94 41.147-49.197 55.458-77.82 96.605-20.574 30.413-35.78 68.876-56.354 104.655-42.04 68.876-84.976 118.968-118.967 201.26-107.339 2.684-197.682 4.473-208.416 115.39-14.312 152.063 57.247 189.632 57.247 246.879-.894 61.72-251.351 684.285-181.581 1055.498 19.679 101.972 86.765 102.867 194.104 115.39 258.508 31.307 593.942 20.573 825.614-72.454l420.41-201.26c106.445-59.931 285.343-173.532 364.953-256.72 56.353-58.141 85.87-107.338 134.173-176.214 66.192-96.605 67.981-94.816 82.293-226.306 87.66 16.101 251.352 54.564 305.916 101.972-6.262 61.72-36.674 32.202-36.674 87.66 34.885.895 93.027-42.935 107.339-91.238-36.675-53.67-75.138-44.724-127.913-87.66 42.042-33.096 118.073-48.302 176.215-72.453 125.229-51.88 339.012-209.311 391.787-352.43 42.04-115.389 10.734-307.704-57.248-382.841-71.559-78.715-237.934-118.967-373.897-118.967-161.902 0-329.172 116.283-459.767 166.375-50.092-43.83-53.67-93.922-90.344-142.224-42.04-57.248-315.755-200.366-446.35-228.095'/><path fill='#efebe9' d='M2318.554 1542.686c91.238 8.05 185.16-161.902 402.52-169.058 320.228-10.734 406.993 157.43 360.48 207.521-37.569-41.146-16.995-61.72-89.45-115.389-171.741-124.334-474.079-18.784-613.62 126.123-47.407 49.197-56.352 58.142-39.357 140.435 18.785 92.133 6.262 341.695 22.362 381.053 192.316 62.614 378.37-31.307 490.181-122.545 110.022-89.45 244.196-196.788 268.347-392.681 11.628-98.394-64.403-173.531-110.917-211.994-114.494-95.71-382.841-82.293-526.854-1.79-38.463 21.468-63.51 44.725-94.816 67.087-54.564 38.464-85.871 34.886-68.876 91.238m-1302.377-508.964 43.83-77.821c11.628-27.73 25.045-59.93 37.568-84.977 62.614-127.017 254.035-338.117 361.374-383.736 160.114-68.876 421.305-23.257 241.512 352.43-29.518 62.614-49.197 125.228-113.6 141.329-12.523-90.344 8.05-82.293 90.344-214.678 61.72-99.288 56.353-152.958 5.367-237.934-184.265-25.046-272.82 51.88-395.365 170.847-71.56 68.876-200.366 201.26-217.361 349.746 452.612-108.233 570.685 153.852 712.014-302.338 17.89-57.247 24.151-83.187 19.679-153.852-5.367-73.348-2.684-88.554-55.459-118.073-212.888-120.756-357.796-19.678-496.442 87.66-114.494 89.45-228.989 347.062-233.461 471.397'/><path fill='#eee' d='M506.317 1863.808c16.996-12.523 16.996-2.683 35.78-45.619l71.559-199.47c-140.435 11.627-140.435 153.851-107.339 245.09z'/><path fill='#efebe9' d='M653.014 2910.362c-12.523-50.092-166.376-94.816-225.412-89.45 8.05 92.133 129.701 101.078 225.412 89.45'/></svg>",
+ "ejs": "<svg fill='none' viewBox='0 0 24 24'><path fill='#ffca28' d='M8.046 4.862.908 12l7.138 7.138 2.71-2.691L6.308 12l4.446-4.447z'/><ellipse cx='14.543' cy='7.812' stroke='#ffca28' stroke-width='1.455' rx='2.101' ry='2.798'/><path fill='#ffca28' d='m20.616 4.152 1.47.69-7.783 15.005-1.47-.69z'/><ellipse cx='20.35' cy='16.198' stroke='#ffca28' stroke-width='1.455' rx='2.101' ry='2.798'/></svg>",
+ "elixir": "<svg viewBox='0 0 24 24'><path fill='#9575cd' d='M12.173 22.681c-3.86 0-6.99-3.64-6.99-8.13 0-3.678 2.773-8.172 4.916-10.91 1.014-1.296 2.93-2.322 2.93-2.322s-.982 5.239 1.683 7.319c2.366 1.847 4.106 4.25 4.106 6.363 0 4.232-2.784 7.68-6.645 7.68'/></svg>",
+ "elm": "<svg viewBox='0 0 323 323'><path fill='#ffb300' d='m106.716 99.763 54.785 54.782 54.779-54.782z'/><path fill='#64dd17' d='M96.881 89.93H216.83l-55.18-55.184H41.7zm131.546 11.593 59.705 59.704L228.16 221.2l-59.705-59.704z'/><path fill='#00b8d4' d='m175.552 34.746 112.703 112.695V34.746z'/><path fill='#455a64' d='m34.746 281.3 119.8-119.8-119.8-119.8z'/><path fill='#ffb300' d='m288.255 175.01-53.148 53.149 53.148 53.14z'/><path fill='#00b8d4' d='M281.3 288.254 161.5 168.455l-119.8 119.8z'/></svg>",
+ "email": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='M28 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2m0 6-12 6-12-6V8l12 6 12-6Z'/></svg>",
+ "ember": "<svg viewBox='0 0 24 24'><path fill='#ff5722' d='M12.78 4c1.79-.03 3.18.35 4.12 1.99 2.36 5.88-6.08 8.92-6.42 9.04h-.01s-.25 1.6 2.17 1.54c2.98 0 6.1-2.31 7.3-3.3.29-.23.71-.21.97.06l.89.93c.25.27.26.69.02.97-.77.87-2.59 2.65-5.33 3.79 0 0-4.57 2.13-7.65.12-1.83-1.2-2.34-2.64-2.55-4.13 0 0-2.23-.12-3.66-.67-1.44-.57.01-2.26.01-2.26s.44-.71 1.28 0c.83.71 2.4.39 2.4.39.14-1.11.37-2.57 1.06-4.11C8.81 5.14 11 4.04 12.78 4m1.05 3.24c-.94-.91-3.67.91-3.78 5.09 0 0 .81.24 2.58-.98 1.79-1.23 2.13-3.19 1.19-4.11z'/></svg>",
+ "epub": "<svg viewBox='0 0 16 16'><path fill='#8bc34a' d='M8 12.401 3.601 8 8 3.601l1.468 1.466L6.534 8 8 9.467l4.4-4.4-3.833-3.832a.8.8 0 0 0-1.133 0L1.235 7.434a.8.8 0 0 0 0 1.133l6.199 6.199a.8.8 0 0 0 1.133 0l6.199-6.199a.803.803 0 0 0 0-1.133l-.9-.899z'/></svg>",
+ "erlang": "<svg viewBox='0 0 30 30'><path fill='#f44336' d='M5.207 4.33q-.072.075-.143.153Q1.5 8.476 1.5 15.33c0 4.418 1.155 7.862 3.459 10.34h19.415c2.553-1.152 4.127-3.43 4.127-3.43l-3.147-2.52L23.9 21.1c-.867.773-.845.931-2.315 1.78-1.495.674-3.04.966-4.634.966-2.515 0-4.423-.909-5.723-2.059-1.286-1.15-1.985-4.511-2.096-6.68l17.458.067-.183-1.472s-.847-7.129-2.541-9.372zm8.76.846c1.565 0 3.22.535 3.961 1.471.74.937.931 1.667.973 3.524H9.11c.112-1.955.436-2.81 1.373-3.698.936-.887 2.03-1.297 3.484-1.297'/></svg>",
+ "esbuild": "<svg viewBox='0 0 24 24'><path fill='#ffca28' d='M12 2.042A9.957 9.957 0 0 0 2.043 12 9.957 9.957 0 0 0 12 21.957 9.957 9.957 0 0 0 21.957 12 9.957 9.957 0 0 0 12 2.043zM7.617 6.425 13.192 12l-5.575 5.575-1.69-1.69L9.814 12 5.926 8.115zm5.975 0L19.166 12l-5.574 5.575-1.69-1.69L15.787 12l-3.885-3.885z'/></svg>",
+ "eslint": "<svg viewBox='0 0 32 32'><path fill='#3f51b5' d='M22.713 4H9.287a.5.5 0 0 0-.432.248l-6.708 11.5a.5.5 0 0 0 0 .504l6.708 11.5a.5.5 0 0 0 .432.248h13.426a.5.5 0 0 0 .432-.248l6.708-11.5a.5.5 0 0 0 0-.504l-6.708-11.5A.5.5 0 0 0 22.713 4m-6.937 20.888-7.5-3.75A.5.5 0 0 1 8 20.691v-9.382a.5.5 0 0 1 .276-.447l7.5-3.75a.5.5 0 0 1 .448 0l7.5 3.75a.5.5 0 0 1 .276.447v9.382a.5.5 0 0 1-.276.447l-7.5 3.75a.5.5 0 0 1-.448 0'/><path fill='#7986cb' d='M22 19.441v-6.882a.5.5 0 0 0-.276-.447l-5.5-2.75a.5.5 0 0 0-.448 0l-5.5 2.75a.5.5 0 0 0-.276.447v6.882a.5.5 0 0 0 .276.447l5.5 2.75a.5.5 0 0 0 .448 0l5.5-2.75a.5.5 0 0 0 .276-.447'/></svg>",
+ "excalidraw": "<svg viewBox='0 0 16 16'><path fill='#5c6bc0' d='m15 1-5 1-9 10 3 3 10-9zm-4 3h1v1h-1zm1 5-3 3 4 3 2-2zM7 4 4 7 2 5 1 1l4 1z'/></svg>",
+ "exe": "<svg viewBox='0 0 32 32'><path fill='#e64a19' d='M28 4H4a2 2 0 0 0-2 2v20a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2m0 22H4V10h24Z'/></svg>",
+ "fastlane": "<svg preserveAspectRatio='xMidYMid' viewBox='0 0 300 300'><path fill='#2979ff' d='M242.745 89.48c-11.223 0-21.398 4.463-28.867 11.7l-47.366-33.917c.295-1.238.469-2.524.469-3.854 0-9.167-7.432-16.6-16.6-16.6s-16.601 7.433-16.601 16.6c0 9.169 7.433 16.6 16.6 16.6 3.21 0 6.197-.927 8.738-2.504L217.1 119.7c4.52-9.428 14.49-16.77 25.645-16.77 15.492 0 28.051 12.558 28.051 28.05 0 12.38-8.02 22.887-19.148 26.608l3.806 12.91c16.703-5.368 28.79-21.03 28.79-39.518 0-22.92-18.579-41.5-41.5-41.5'/><path fill='#e64a19' d='M109.689 49.166c-3.389 10.669-2.22 21.69 2.405 30.977l-46.546 34.784a16.6 16.6 0 0 0-3.523-1.609c-8.716-2.768-18.026 2.053-20.794 10.768s2.053 18.026 10.767 20.794c8.716 2.769 18.026-2.052 20.795-10.767a16.46 16.46 0 0 0 .257-9.062l57.623-42.379c-7.598-7.144-11.567-18.84-8.2-29.444 4.68-14.727 20.411-22.874 35.139-18.195 11.768 3.738 19.334 14.535 19.513 26.238l13.421.28c-.059-17.5-11.299-33.721-28.873-39.304-21.788-6.921-45.063 5.13-51.984 26.92'/><path fill='#00bcd4' d='M32.81 161.347a41.37 41.37 0 0 0 30.043 7.612l18.362 54.878a16.3 16.3 0 0 0-2.621 2.8c-5.338 7.316-3.686 17.611 3.692 22.994 7.377 5.383 17.685 3.815 23.023-3.501 5.34-7.316 3.687-17.61-3.69-22.994a16.53 16.53 0 0 0-8.489-3.13l-22.086-67.718c-9.128 4.87-21.425 4.875-30.402-1.674-12.465-9.097-15.258-26.492-6.237-38.855 7.21-9.88 19.78-13.556 30.9-9.993l4.456-12.536c-16.566-5.525-35.414-.121-46.179 14.631-13.346 18.291-9.214 44.029 9.229 57.486'/><path fill='#8bc34a' d='M245.283 225.838c-3.42-10.583-10.75-18.811-19.884-23.64l17.72-55.05a16.6 16.6 0 0 0 3.796-.739c8.69-2.808 13.47-12.093 10.679-20.737-2.793-8.646-12.102-13.378-20.793-10.57-8.69 2.806-13.472 12.09-10.679 20.736a16.3 16.3 0 0 0 5.036 7.472l-22.334 67.6c10.315 1.374 20.312 8.527 23.71 19.046 4.72 14.61-3.36 30.298-18.044 35.042-11.735 3.791-24.138-.554-31.055-9.908l-11.078 7.543c10.176 14.106 28.706 20.71 46.23 15.048 21.726-7.019 33.678-30.23 26.696-51.843'/><path fill='#a0f' d='M116.724 270.244c9.003-6.587 14.547-16.139 16.291-26.33l57.906-.59a16.5 16.5 0 0 0 1.887 3.366c5.382 7.355 15.706 8.955 23.061 3.574s8.955-15.705 3.575-23.06c-5.382-7.356-15.706-8.956-23.061-3.575a16.4 16.4 0 0 0-5.54 7.137l-71.283.182c1.908 10.217-1.78 21.958-10.73 28.506-12.428 9.093-29.875 6.39-38.968-6.039-7.266-9.932-6.999-23.068-.257-32.585l-10.631-8.123c-10.249 14.11-10.752 33.77.098 48.601 13.453 18.389 39.265 22.389 57.652 8.936'/></svg>",
+ "favicon": "<svg viewBox='0 0 32 32'><path fill='#ffd54f' d='m16 24 10 6-4-10 8-8-10-.032L16 2l-4 10H2l8 8-4 10Z'/></svg>",
+ "figma": "<svg viewBox='0 0 32 32'><path fill='#f4511e' d='M12 4h4v8h-4a4 4 0 0 1-4-4 4 4 0 0 1 4-4'/><path fill='#ff8a65' d='M20 12h-4V4h4a4 4 0 0 1 4 4 4 4 0 0 1-4 4'/><rect width='8' height='8' x='16' y='12' fill='#29b6f6' rx='4' transform='rotate(180 20 16)'/><path fill='#7c4dff' d='M12 12h4v8h-4a4 4 0 0 1-4-4 4 4 0 0 1 4-4'/><path fill='#00e676' d='M12 20h4v4a4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4'/></svg>",
+ "file": "<svg viewBox='0 0 16 16'><path fill='#90a4ae' d='M8.668 6h3.664L8.668 2.332zM4 1.332h5.332l4 4v8c0 .738-.594 1.336-1.332 1.336H4a1.33 1.33 0 0 1-1.332-1.336V2.668A1.33 1.33 0 0 1 4 1.332m3.332 1.336H4v10.664h8v-6H7.332z'/></svg>",
+ "firebase": "<svg viewBox='0 0 24 24'><path fill='#ff9100' d='M18.217 8.974c-.45-.623-1.482-1.904-3.07-3.808-.689-.825-1.28-1.526-1.57-1.87l-.408-.48-.173-.205-.094-.11-.018-.027-.008-.004-.4-.47-.509.407a11.1 11.1 0 0 0-3.069 3.866 9.5 9.5 0 0 0-.87 2.647q-.06.303-.1.615a9 9 0 0 0-.577-.03 6.2 6.2 0 0 0-1.901.229l-.265.074-.136.238a8 8 0 0 0-1.044 3.68 8 8 0 0 0 5.006 7.697l.197.079.06.02h.002a8 8 0 0 0 2.452.473q.143.005.286.005a7.9 7.9 0 0 0 3.076-.618l.007.003.261-.12a7.99 7.99 0 0 0 4.643-6.981 8.5 8.5 0 0 0-1.778-5.31M9.837 19.82l-.192-.074-.051-.02a6.31 6.31 0 0 1-3.897-6.048 6.2 6.2 0 0 1 .697-2.667 4.6 4.6 0 0 1 .759-.103l.065-.002a8 8 0 0 1 .378 0c.108.005.215.021.322.034a13 13 0 0 0 .918 4.007 10.1 10.1 0 0 0 2.474 3.61 6.4 6.4 0 0 1-1.473 1.263m.351-5.486a11.4 11.4 0 0 1-.767-3.125 4.6 4.6 0 0 1 .95.461 4.73 4.73 0 0 1 1.94 2.884 5 5 0 0 1 .12.649 4.2 4.2 0 0 1-.288 2.023 8.3 8.3 0 0 1-1.955-2.892m1.741 5.858a8 8 0 0 0 .553-.62c.233.177.485.332.73.495a6.3 6.3 0 0 1-1.283.125m5.432-2.97a6.34 6.34 0 0 1-2.212 2.138 12.4 12.4 0 0 1-1.851-1.15 5.84 5.84 0 0 0 .309-3.998 6.02 6.02 0 0 0-2.504-3.664 6.1 6.1 0 0 0-1.679-.74 8 8 0 0 1 .064-.496 9 9 0 0 1 .465-1.598q.117-.298.253-.584l.004-.007c.14-.282.296-.567.481-.872l.073-.12h-.002a9.2 9.2 0 0 1 1.534-1.824l.227.269c.53.628 1.03 1.222 1.483 1.765 1.02 1.223 2.342 2.828 2.852 3.536a6.8 6.8 0 0 1 1.446 4.242 6.3 6.3 0 0 1-.943 3.104'/></svg>",
+ "flash": "<svg viewBox='0 0 24 24'><path fill='#e53935' d='M20.314 2c-2.957 0-5.341 1.104-7.122 3.252-1.427 1.752-2.354 3.93-3.164 6.034-1.663 4.283-2.781 6.741-6.342 6.741V22c2.958 0 5.342-1.03 7.122-3.194 1.133-1.383 1.957-3.135 2.634-4.827h4.665v-3.973h-3.061c1.207-2.575 2.546-3.973 5.268-3.973z'/></svg>",
+ "flow": "<svg viewBox='0 0 300 300'><path fill='#fbc02d' fill-opacity='.976' d='m38.75 33.427 77.461 77.47H54.436l61.145 61.16H38.437l93.462 93.478v-77.158l.01-.01v-77.47h-.01V66.982h46.691l20.394 20.393H153.57v76.531h22.05l24.474 24.473h-15.806l-.01-.01v.01h-31.665l-.01-.01v.01h-.313l.313.313v77.148h109.149l-39.2-39.2v-15.806l8.465 8.466v-77.37h-15.682l.017-38.191 30.09 30.086V56.362h-64.874l-22.94-22.934H113.71z'/></svg>",
+ "folder-admin-open": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#cfd8dc' d='m25 10-7 3.273v4.908c0 4.542 2.986 8.788 7 9.819 4.014-1.031 7-5.277 7-9.82v-4.907zm0 3.273a2.457 2.457 0 1 1-2.333 2.454A2.396 2.396 0 0 1 25 13.273m3.99 9.817A7.6 7.6 0 0 1 25 26.298a7.6 7.6 0 0 1-3.99-3.208 8.4 8.4 0 0 1-.677-1.25c0-1.352 2.108-2.456 4.667-2.456s4.666 1.08 4.666 2.455a8.3 8.3 0 0 1-.676 1.251'/></svg>",
+ "folder-admin": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#cfd8dc' d='m25 10-7 3.273v4.908c0 4.542 2.986 8.788 7 9.819 4.014-1.031 7-5.277 7-9.82v-4.907zm0 3.273a2.457 2.457 0 1 1-2.333 2.454A2.396 2.396 0 0 1 25 13.273m3.99 9.817A7.6 7.6 0 0 1 25 26.298a7.6 7.6 0 0 1-3.99-3.208 8.4 8.4 0 0 1-.677-1.25c0-1.352 2.108-2.456 4.667-2.456s4.666 1.08 4.666 2.455a8.3 8.3 0 0 1-.676 1.251'/></svg>",
+ "folder-android-open": "<svg viewBox='0 0 32 32'><path fill='#8bc34a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#dcedc8' d='M27.943 19.471 32 15.414 30.586 14l-4.333 4.333a11.01 11.01 0 0 0-10.505 0L11.414 14 10 15.414l4.057 4.057A10.98 10.98 0 0 0 10 28h22a10.98 10.98 0 0 0-4.057-8.529M18 26h-4v-4h4Zm10 0h-4v-4h4Z'/></svg>",
+ "folder-android": "<svg viewBox='0 0 32 32'><path fill='#8bc34a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#dcedc8' d='M27.943 19.471 32 15.414 30.586 14l-4.333 4.333a11.01 11.01 0 0 0-10.505 0L11.414 14 10 15.414l4.057 4.057A10.98 10.98 0 0 0 10 28h22a10.98 10.98 0 0 0-4.057-8.529M18 26h-4v-4h4Zm10 0h-4v-4h4Z'/></svg>",
+ "folder-angular-open": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='m22 12-6 2v10Zm4 0 6 12V14Zm-.408 8L24 16.034 22.408 20zm-4.789 4L20 26l4 2 4-2-.803-2z'/></svg>",
+ "folder-angular": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='m22 12-6 2v10Zm4 0 6 12V14Zm-.408 8L24 16.034 22.408 20zm-4.789 4L20 26l4 2 4-2-.803-2z'/></svg>",
+ "folder-animation-open": "<svg viewBox='0 0 32 32'><path fill='#ec407a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f8bbd0' d='M25 14a7 7 0 0 0-2 .29 7.04 7.04 0 0 0-4 0 7 7 0 0 0-2-.29 7 7 0 0 0 0 14 7 7 0 0 0 2-.29 7.04 7.04 0 0 0 4 0 7 7 0 0 0 2 .29 7 7 0 0 0 0-14m-13 7a5 5 0 0 1 4.01-4.9 6.98 6.98 0 0 0 0 9.8A5 5 0 0 1 12 21m8.01 4.9a4.999 4.999 0 0 1 0-9.8 6.98 6.98 0 0 0 0 9.8M23 16.41a5.011 5.011 0 0 1 0 9.18 5.011 5.011 0 0 1 0-9.18m2.99 9.49a6.98 6.98 0 0 0 0-9.8 4.999 4.999 0 0 1 0 9.8'/></svg>",
+ "folder-animation": "<svg viewBox='0 0 32 32'><path fill='#ec407a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#f8bbd0' d='M25 14a7 7 0 0 0-2 .29 7.04 7.04 0 0 0-4 0 7 7 0 0 0-2-.29 7 7 0 0 0 0 14 7 7 0 0 0 2-.29 7.04 7.04 0 0 0 4 0 7 7 0 0 0 2 .29 7 7 0 0 0 0-14m-13 7a5 5 0 0 1 4.01-4.9 6.98 6.98 0 0 0 0 9.8A5 5 0 0 1 12 21m8.01 4.9a4.999 4.999 0 0 1 0-9.8 6.98 6.98 0 0 0 0 9.8M23 16.41a5.011 5.011 0 0 1 0 9.18 5.011 5.011 0 0 1 0-9.18m2.99 9.49a6.98 6.98 0 0 0 0-9.8 4.999 4.999 0 0 1 0 9.8'/></svg>",
+ "folder-ansible-open": "<svg viewBox='0 0 32 32'><path fill='#616161' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#9e9e9e' d='M32 21a9 9 0 1 1-9-9 9.043 9.043 0 0 1 9 9'/><path fill='#fafafa' d='m27.929 24.628-4-10a1 1 0 0 0-.93-.628h-.006a1 1 0 0 0-.927.641L18 26h2l1.24-3.638 5.205 3.47a1 1 0 0 0 1.484-1.204m-5.954-4.18 1.043-2.71 1.858 4.644Z'/></svg>",
+ "folder-ansible": "<svg viewBox='0 0 32 32'><path fill='#616161' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#9e9e9e' d='M32 21a9 9 0 1 1-9-9 9.043 9.043 0 0 1 9 9'/><path fill='#fafafa' d='m27.929 24.628-4-10a1 1 0 0 0-.93-.628h-.006a1 1 0 0 0-.927.641L18 26h2l1.24-3.638 5.205 3.47a1 1 0 0 0 1.484-1.204m-5.954-4.18 1.043-2.71 1.858 4.644Z'/></svg>",
+ "folder-api-open": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fffde7' d='M20 18h-4v2h6v-6h-2zm8 0v-4h-2v6h6v-2zm-12 8h4v4h2v-6h-6zm10 0v4h2v-4h4v-2h-6z'/></svg>",
+ "folder-api": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fffde7' d='M20 18h-4v2h6v-6h-2zm8 0v-4h-2v6h6v-2zm-12 8h4v4h2v-6h-6zm10 0v4h2v-4h4v-2h-6z'/></svg>",
+ "folder-apollo-open": "<svg viewBox='0 0 32 32'><path fill='#7e57c2' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#d1c4e9' d='M25 28h3l-4-12h-4l-4 12h3l.667-2h3.01L22 24h-1.667L22 19z'/><path fill='#d1c4e9' d='M28 12a2 2 0 0 0-.416.045 10.996 10.996 0 0 0-17.102 13.473 1.003 1.003 0 0 0 1.72-1.034A8.986 8.986 0 0 1 26.1 13.406 2 2 0 0 0 26 14a2 2 0 1 0 2-2'/></svg>",
+ "folder-apollo": "<svg viewBox='0 0 32 32'><path fill='#7e57c2' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#d1c4e9' d='M25 28h3l-4-12h-4l-4 12h3l.667-2h3.01L22 24h-1.667L22 19z'/><path fill='#d1c4e9' d='M28 12a2 2 0 0 0-.416.045 10.996 10.996 0 0 0-17.102 13.473 1.003 1.003 0 0 0 1.72-1.034A8.986 8.986 0 0 1 26.1 13.406 2 2 0 0 0 26 14a2 2 0 1 0 2-2'/></svg>",
+ "folder-app-open": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M16 12h4v4h-4zm6 0h4v4h-4zm6 0h4v4h-4zm-12 6h4v4h-4zm6 0h4v4h-4zm6 0h4v4h-4zm-12 6h4v4h-4zm6 0h4v4h-4zm6 0h4v4h-4z'/></svg>",
+ "folder-app": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M16 12h4v4h-4zm6 0h4v4h-4zm6 0h4v4h-4zm-12 6h4v4h-4zm6 0h4v4h-4zm6 0h4v4h-4zm-12 6h4v4h-4zm6 0h4v4h-4zm6 0h4v4h-4z'/></svg>",
+ "folder-archive-open": "<svg viewBox='0 0 1024 1024'><path fill='#ffa726' d='M926.912 384H302.144a64 64 0 0 0-60.736 43.776L128 768V320h768a64 64 0 0 0-64-64H483.968a64 64 0 0 1-40.96-14.848l-41.216-34.304A64 64 0 0 0 360.832 192H128a64 64 0 0 0-64 64v512a64 64 0 0 0 64 64h704l153.792-358.784A64 64 0 0 0 926.912 384'/><path fill='#ffe0b2' d='M512 320c-35.456 0-64 28.544-64 64v64c0 35.456 28.544 64 64 64v320c0 35.456 28.544 64 64 64h320c35.456 0 64-28.544 64-64V512c35.456 0 64-28.544 64-64v-64c0-35.456-28.544-64-64-64zm0 64h448v64H512zm128 128h192v64H640z'/></svg>",
+ "folder-archive": "<svg viewBox='0 0 1024 1024'><path fill='#ffa726' d='m443.008 241.152-41.216-34.304A64 64 0 0 0 360.832 192H128a64 64 0 0 0-64 64v512a64 64 0 0 0 64 64h768a64 64 0 0 0 64-64V320a64 64 0 0 0-64-64H483.968a64 64 0 0 1-40.96-14.848'/><path fill='#ffe0b2' d='M512 320c-35.456 0-64 28.544-64 64v64c0 35.456 28.544 64 64 64v320c0 35.456 28.544 64 64 64h320c35.456 0 64-28.544 64-64V512c35.456 0 64-28.544 64-64v-64c0-35.456-28.544-64-64-64zm0 64h448v64H512zm128 128h192v64H640z'/></svg>",
+ "folder-astro-open": "<svg viewBox='0 0 32 32'><path fill='#7c4dff' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.367L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#d1c4e9' d='M19.333 27c0-.81.082-1.508 1.3-1.508 0 2.608 1.5 4.508 2.7 4.508 0-3.319 2.667-3.122 2.667-6h-8c0 1.275.158 2.681 1.333 3m5.923-16.385L30 22h-2.333a4 4 0 0 1-3.693-2.462l-1.512-3.63a.5.5 0 0 0-.924 0l-1.512 3.63A4 4 0 0 1 16.333 22H14l4.744-11.385a1 1 0 0 1 .923-.615h4.666a1 1 0 0 1 .923.615'/></svg>",
+ "folder-astro": "<svg viewBox='0 0 32 32'><path fill='#7c4dff' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#d1c4e9' d='M19.333 27c0-.81.082-1.508 1.3-1.508 0 2.608 1.5 4.508 2.7 4.508 0-3.319 2.667-3.122 2.667-6h-8c0 1.275.158 2.681 1.333 3m5.923-16.385L30 22h-2.333a4 4 0 0 1-3.693-2.462l-1.512-3.63a.5.5 0 0 0-.924 0l-1.512 3.63A4 4 0 0 1 16.333 22H14l4.744-11.385a1 1 0 0 1 .923-.615h4.666a1 1 0 0 1 .923.615'/></svg>",
+ "folder-attachment-open": "<svg viewBox='0 0 1024 1024'><path fill='#9c27b0' d='M128 192a64 64 0 0 0-64 64v512a64 64 0 0 0 64 64h704l153.791-358.783A64 64 0 0 0 926.912 384H302.145a64 64 0 0 0-60.737 43.775L128 768V320h768a64 64 0 0 0-64-64H483.969a64 64 0 0 1-40.961-14.848l-41.215-34.304A64 64 0 0 0 360.832 192z'/><path fill='#e1bee7' d='M990.922 446.852c.156 8.69-1.383 17.621-5.131 26.365l-26.271 61.287c.306 3.125.48 6.29.48 9.496 0 53.02-42.98 96-96 96H544c-17.673 0-32-14.327-32-32s14.327-32 32-32h320v-64H544c-53.02 0-96 42.98-96 96s42.98 96 96 96h320c88.366 0 160-71.634 160-160 0-36.586-12.416-70.195-33.078-97.148'/></svg>",
+ "folder-attachment": "<svg viewBox='0 0 1024 1024'><path fill='#9c27b0' d='m443.008 241.152-41.216-34.304A64 64 0 0 0 360.832 192H128a64 64 0 0 0-64 64v512a64 64 0 0 0 64 64h768a64 64 0 0 0 64-64V320a64 64 0 0 0-64-64H483.968a64 64 0 0 1-40.96-14.848'/><path fill='#e1bee7' d='M960 416.219V544c0 53.02-42.98 96-96 96H544c-17.673 0-32-14.327-32-32s14.327-32 32-32h320v-64H544c-53.02 0-96 42.98-96 96s42.98 96 96 96h320c88.366 0 160-71.634 160-160 0-52.295-25.203-98.585-64-127.781'/></svg>",
+ "folder-audio-open": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M31.5 12h-5a.5.5 0 0 0-.5.5v8.055a3.9 3.9 0 0 0-3.232-.357 3.999 3.999 0 0 0 1.856 7.755A4.1 4.1 0 0 0 28 23.847V16h3.5a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5'/></svg>",
+ "folder-audio": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M31.5 12h-5a.5.5 0 0 0-.5.5v8.055a3.9 3.9 0 0 0-3.232-.357 3.999 3.999 0 0 0 1.856 7.755A4.1 4.1 0 0 0 28 23.847V16h3.5a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5'/></svg>",
+ "folder-aurelia-open": "<svg xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 32 32'><defs><linearGradient id='a' x1='1997.25' x2='2067.763' y1='2029.643' y2='2094.383' gradientTransform='matrix(.3031 0 0 .33012 -592.7 -666.868)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ba68c8'/><stop offset='1' stop-color='#7e57c2'/></linearGradient><linearGradient id='b' x1='2037.862' x2='1999.816' y1='2094.543' y2='2042.593' gradientTransform='matrix(.30439 0 0 .32873 -592.019 -659.828)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#7e57c2'/><stop offset='.14' stop-color='#7b1fa2'/><stop offset='.29' stop-color='#ad1457'/><stop offset='.84' stop-color='#c2185b'/><stop offset='1' stop-color='#ec407a'/></linearGradient><linearGradient xlink:href='#a' id='c' x1='1810.238' x2='1876.912' y1='2182.482' y2='2275.279' gradientTransform='matrix(.3299 0 0 .30331 -593.502 -657.724)'/><linearGradient xlink:href='#a' id='d' x1='1884.666' x2='1966.686' y1='2101.188' y2='2168.469' gradientTransform='matrix(.31601 0 0 .31665 -588.323 -661.081)'/><linearGradient xlink:href='#a' id='e' x1='1908.618' x2='1985.086' y1='2130.411' y2='2197.794' gradientTransform='matrix(.31642 0 0 .31622 -597.877 -663.436)'/><linearGradient xlink:href='#b' id='f' x1='2058.454' x2='2020.313' y1='2123.223' y2='2071.047' gradientTransform='matrix(.30448 0 0 .32864 -597.026 -667.345)'/><linearGradient xlink:href='#a' id='g' x1='1999.965' x2='2070.546' y1='2025.692' y2='2103.839' gradientTransform='matrix(.30312 0 0 .33012 -592.673 -666.844)'/><linearGradient id='h' x1='2320.079' x2='2361.512' y1='1768.801' y2='1727.83' gradientTransform='rotate(11.284 3068.743 -3352.564)scale(.31697 .3157)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#7e57c2'/><stop offset='.14' stop-color='#7b1fa2'/><stop offset='.53' stop-color='#ad1457'/><stop offset='.79' stop-color='#c2185b'/><stop offset='1' stop-color='#ec407a'/></linearGradient></defs><path fill='#f06292' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='url(#a)' d='m25.787 15.333-1.671 1.144-1.72-2.646 1.67-1.144Z'/><path fill='url(#b)' d='M27.145 23.283 30 27.672 26.6 30l-2.855-4.389-.498-.765 3.4-2.328Z'/><path fill='url(#c)' d='m22.864 26.215.628.966-2.588 1.772-1.127-1.732.566-.387 2.023-1.385Z'/><path fill='url(#d)' d='m28.56 21.208.727-.497 1.126 1.732-1.671 1.144-.628-.967.945-.647Zm-.447 1.412-.497-.765.945-.647.498.765Z'/><path fill='url(#e)' d='m16.844 21.457-.565.387-1.72-2.647 2.587-1.772 1.206 1.855-2.022 1.385 2.023-1.385.515.791Z'/><path fill='url(#f)' d='m22.635 16.348.515.79-3.4 2.329-.515-.791-2.828-4.348 3.4-2.328Z'/><path fill='url(#g)' d='m25.062 15.83-.945.647-.515-.792-1.206-1.855 1.67-1.144 1.722 2.646Z'/><path fill='#673ab7' d='m20.84 27.6-.497-.765 2.023-1.386.497.766Zm7.273-4.98-.498-.764.945-.647.498.765Z'/><path fill='#ab47bc' d='m16.844 21.457-.515-.791 2.023-1.385.515.79Z'/><path fill='#7e57c2' d='m24.117 16.477-.514-.791.945-.648.514.791Z'/><path fill='#880e4f' d='m27.145 23.283-3.4 2.328-.497-.766 3.399-2.328Z'/><path fill='#ad1457' d='m22.635 16.347.515.792-3.4 2.329-.515-.793Z'/><path fill='#ab47bc' d='m15.88 15.715.642.986-.962.659-.642-.987Z'/><path fill='#7e57c2' d='m19.346 27.545.642.987-.962.659-.641-.988Z'/><path fill='url(#h)' d='M16.815 28.539 14 24.175l15.048-10.328L32 18.142Z'/></svg>",
+ "folder-aurelia": "<svg xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 32 32'><defs><linearGradient id='a' x1='1900.681' x2='1971.195' y1='2029.643' y2='2094.383' gradientTransform='matrix(.3031 0 0 .33012 -563.43 -666.868)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ba68c8'/><stop offset='1' stop-color='#7e57c2'/></linearGradient><linearGradient id='b' x1='1941.881' x2='1903.835' y1='2094.543' y2='2042.593' gradientTransform='matrix(.30439 0 0 .32873 -562.803 -659.828)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#7e57c2'/><stop offset='.14' stop-color='#7b1fa2'/><stop offset='.29' stop-color='#ad1457'/><stop offset='.84' stop-color='#c2185b'/><stop offset='1' stop-color='#ec407a'/></linearGradient><linearGradient xlink:href='#a' id='c' x1='1724.927' x2='1791.601' y1='2182.482' y2='2275.279' gradientTransform='matrix(.3299 0 0 .30331 -565.358 -657.724)'/><linearGradient xlink:href='#a' id='d' x1='1793.759' x2='1875.779' y1='2101.188' y2='2168.469' gradientTransform='matrix(.31601 0 0 .31665 -559.595 -661.081)'/><linearGradient xlink:href='#a' id='e' x1='1817.883' x2='1894.351' y1='2130.411' y2='2197.794' gradientTransform='matrix(.31642 0 0 .31622 -569.167 -663.436)'/><linearGradient xlink:href='#b' id='f' x1='1962.514' x2='1924.373' y1='2123.223' y2='2071.047' gradientTransform='matrix(.30448 0 0 .32864 -567.814 -667.345)'/><linearGradient xlink:href='#a' id='g' x1='1903.406' x2='1973.987' y1='2025.692' y2='2103.839' gradientTransform='matrix(.30312 0 0 .33012 -563.404 -666.844)'/><linearGradient id='h' x1='2232.134' x2='2273.567' y1='1794.832' y2='1753.862' gradientTransform='rotate(11.284 3096.4 -3207.367)scale(.31697 .3157)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#7e57c2'/><stop offset='.14' stop-color='#7b1fa2'/><stop offset='.53' stop-color='#ad1457'/><stop offset='.79' stop-color='#c2185b'/><stop offset='1' stop-color='#ec407a'/></linearGradient></defs><path fill='#f06292' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='url(#a)' d='m25.787 15.333-1.671 1.144-1.72-2.646 1.67-1.144Z'/><path fill='url(#b)' d='M27.145 23.283 30 27.672 26.6 30l-2.855-4.389-.498-.765 3.4-2.328Z'/><path fill='url(#c)' d='m22.864 26.215.628.966-2.588 1.772-1.127-1.732.566-.387 2.023-1.385Z'/><path fill='url(#d)' d='m28.56 21.208.727-.497 1.126 1.732-1.671 1.144-.628-.967.945-.647Zm-.447 1.412-.497-.765.945-.647.498.765Z'/><path fill='url(#e)' d='m16.844 21.457-.565.387-1.72-2.647 2.587-1.772 1.206 1.855-2.022 1.385 2.023-1.385.515.791Z'/><path fill='url(#f)' d='m22.635 16.348.515.79-3.4 2.329-.515-.791-2.828-4.348 3.4-2.328Z'/><path fill='url(#g)' d='m25.062 15.83-.945.647-.515-.792-1.206-1.855 1.67-1.144 1.722 2.646Z'/><path fill='#673ab7' d='m20.84 27.6-.497-.765 2.023-1.386.497.766Zm7.273-4.98-.498-.764.945-.647.498.765Z'/><path fill='#ab47bc' d='m16.844 21.457-.515-.791 2.023-1.385.515.79Z'/><path fill='#7e57c2' d='m24.117 16.477-.514-.791.945-.648.514.791Z'/><path fill='#880e4f' d='m27.145 23.283-3.4 2.328-.497-.766 3.399-2.328Z'/><path fill='#ad1457' d='m22.635 16.347.515.792-3.4 2.329-.515-.793Z'/><path fill='#ab47bc' d='m15.88 15.715.642.986-.962.659-.642-.987Z'/><path fill='#7e57c2' d='m19.346 27.545.642.987-.962.659-.641-.988Z'/><path fill='url(#h)' d='M16.815 28.539 14 24.175l15.048-10.328L32 18.142Z'/></svg>",
+ "folder-aws-open": "<svg viewBox='0 0 32 32'><path fill='#ffb300' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffecb3' d='M27.881 19.23a6.591 6.591 0 0 0-12.308-1.76 5.278 5.278 0 0 0 .572 10.525h11.428a4.388 4.388 0 0 0 .308-8.766Z'/></svg>",
+ "folder-aws": "<svg viewBox='0 0 32 32'><path fill='#ffb300' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffecb3' d='M27.881 19.23a6.591 6.591 0 0 0-12.308-1.76 5.278 5.278 0 0 0 .572 10.525h11.428a4.388 4.388 0 0 0 .308-8.766Z'/></svg>",
+ "folder-azure-pipelines-open": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='m28 22 3.724-1.862a.5.5 0 0 0 .276-.447V12.5a.5.5 0 0 0-.5-.5h-7.191a.5.5 0 0 0-.447.276L22 16h-5.5a.5.5 0 0 0-.5.5V20l1.172 1.172 1.414-1.415L20 21.172l-1.414 1.414 2.828 2.828L22.828 24l1.415 1.414-1.415 1.414L24 28h3.5a.5.5 0 0 0 .5-.5Zm0-4a2 2 0 1 1 2-2 2 2 0 0 1-2 2M16 28v-4h-2v6h6v-2z'/></svg>",
+ "folder-azure-pipelines": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='m28 22 3.724-1.862a.5.5 0 0 0 .276-.447V12.5a.5.5 0 0 0-.5-.5h-7.191a.5.5 0 0 0-.447.276L22 16h-5.5a.5.5 0 0 0-.5.5V20l1.172 1.172 1.414-1.415L20 21.172l-1.414 1.414 2.828 2.828L22.828 24l1.415 1.414-1.415 1.414L24 28h3.5a.5.5 0 0 0 .5-.5Zm0-4a2 2 0 1 1 2-2 2 2 0 0 1-2 2M16 28v-4h-2v6h6v-2z'/></svg>",
+ "folder-backup-open": "<svg viewBox='0 0 32 32'><path fill='#8d6e63' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#d7ccc8' d='M25.375 24.781 20 20.48V14h2v5.52l4.625 3.699z'/><path fill='#d7ccc8' d='M22 30a10 10 0 1 1 10-10 10.01 10.01 0 0 1-10 10m0-18a8 8 0 1 0 8 8 8.01 8.01 0 0 0-8-8'/></svg>",
+ "folder-backup": "<svg viewBox='0 0 32 32'><path fill='#8d6e63' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#d7ccc8' d='M25.375 24.781 20 20.48V14h2v5.52l4.625 3.699z'/><path fill='#d7ccc8' d='M22 30a10 10 0 1 1 10-10 10.01 10.01 0 0 1-10 10m0-18a8 8 0 1 0 8 8 8.01 8.01 0 0 0-8-8'/></svg>",
+ "folder-base-open": "<svg viewBox='0 0 32 32'><path fill='#8d6e63' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><rect width='18' height='6' x='14' y='22' fill='#d7ccc8' rx='1'/></svg>",
+ "folder-base": "<svg viewBox='0 0 32 32'><path fill='#8d6e63' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><rect width='18' height='6' x='14' y='22' fill='#d7ccc8' rx='1'/></svg>",
+ "folder-batch-open": "<svg viewBox='0 0 32 32'><path fill='#616161' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bdbdbd' d='M16 14h12v2H16zm0 4h12v2H16zm0 4h8v2h-8zm10 0v6l6-3z'/></svg>",
+ "folder-batch": "<svg viewBox='0 0 32 32'><path fill='#616161' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bdbdbd' d='M16 14h12v2H16zm0 4h12v2H16zm0 4h8v2h-8zm10 0v6l6-3z'/></svg>",
+ "folder-benchmark-open": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M20 12a9.99 9.99 0 0 0-7.99 16h2.71A7.993 7.993 0 0 1 20 14a8 8 0 0 1 1.69.18c.73-.44 1.51-.9 2.28-1.35A9.8 9.8 0 0 0 20 12m9.12 5.92c-.41.73-.86 1.52-1.32 2.33A7.8 7.8 0 0 1 28 22a7.97 7.97 0 0 1-2.72 6h2.71A9.93 9.93 0 0 0 30 22a9.8 9.8 0 0 0-.88-4.08'/><path fill='#bbdefb' d='M17.172 19.172c1.562-1.563 11.313-5.657 11.313-5.657s-4.094 9.751-5.657 11.313a4 4 0 0 1-5.656-5.656'/></svg>",
+ "folder-benchmark": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M20 12a9.99 9.99 0 0 0-7.99 16h2.71A7.993 7.993 0 0 1 20 14a8 8 0 0 1 1.69.18c.73-.44 1.51-.9 2.28-1.35A9.8 9.8 0 0 0 20 12m9.12 5.92c-.41.73-.86 1.52-1.32 2.33A7.8 7.8 0 0 1 28 22a7.97 7.97 0 0 1-2.72 6h2.71A9.93 9.93 0 0 0 30 22a9.8 9.8 0 0 0-.88-4.08'/><path fill='#bbdefb' d='M17.172 19.172c1.562-1.563 11.313-5.657 11.313-5.657s-4.094 9.751-5.657 11.313a4 4 0 0 1-5.656-5.656'/></svg>",
+ "folder-bibliography-open": "<svg viewBox='0 0 1024 1024'><path fill='#a1887f' d='M926.912 384H302.144a64 64 0 0 0-60.736 43.776L128 768V320h768a64 64 0 0 0-64-64H483.968a64 64 0 0 1-40.96-14.848l-41.216-34.304A64 64 0 0 0 360.832 192H128a64 64 0 0 0-64 64v512a64 64 0 0 0 64 64h704l153.792-358.784A64 64 0 0 0 926.912 384'/><path fill='#8d6e63' d='M576 704v128c0 35.456 28.544 64 64 64h352c17.728 0 32-14.272 32-32V704z'/><path fill='#ffe082' d='M896 768v64h64v-64z'/><path fill='#fff8e1' d='M640 768c0 35.456 28.544 64 64 64h256c-35.346 0-64-28.654-64-64z'/><path fill='#ff1744' d='M704 800h64v160h-64z'/><path fill='#d7ccc8' d='M640 320c-35.456 0-64 28.544-64 64v448c0-35.346 28.654-64 64-64h352c17.728 0 32-14.272 32-32V352c0-17.728-14.272-32-32-32z'/><path fill='#5d4037' d='M640 384v32h32c32 0 32 7.163 32 16v224c0 8.837 0 16-32 16h-32v32h192c70.692 0 128-39.399 128-88-.014-40.302-39.848-75.446-96.688-85.305C884.346 514.563 895.986 493.665 896 472c0-48.601-57.308-88-128-88zm143.873 33.736C811.541 423.914 832 445.866 832 472c0 30.928-28.654 56-64 56v-96c0-8.837 7.266-16.186 15.873-14.264M768 560h64c35.346 0 64 25.072 64 56s-28.654 56-64 56h-48a16 16 0 0 1-16-16z'/></svg>",
+ "folder-bibliography": "<svg viewBox='0 0 1024 1024'><path fill='#a1887f' d='m443.008 241.152-41.216-34.304A64 64 0 0 0 360.832 192H128a64 64 0 0 0-64 64v512a64 64 0 0 0 64 64h768a64 64 0 0 0 64-64V320a64 64 0 0 0-64-64H483.968a64 64 0 0 1-40.96-14.848'/><path fill='#8d6e63' d='M576 704v128c0 35.456 28.544 64 64 64h352c17.728 0 32-14.272 32-32V704z'/><path fill='#ffe082' d='M896 768v64h64v-64z'/><path fill='#fff8e1' d='M640 768c0 35.456 28.544 64 64 64h256c-35.346 0-64-28.654-64-64z'/><path fill='#ff1744' d='M704 800h64v160h-64z'/><path fill='#d7ccc8' d='M640 320c-35.456 0-64 28.544-64 64v448c0-35.346 28.654-64 64-64h352c17.728 0 32-14.272 32-32V352c0-17.728-14.272-32-32-32z'/><path fill='#5d4037' d='M640 384v32h32c32 0 32 7.163 32 16v224c0 8.837 0 16-32 16h-32v32h192c70.692 0 128-39.399 128-88-.014-40.302-39.848-75.446-96.688-85.305C884.346 514.563 895.986 493.665 896 472c0-48.601-57.308-88-128-88zm143.873 33.736C811.541 423.914 832 445.866 832 472c0 30.928-28.654 56-64 56v-96c0-8.837 7.266-16.186 15.873-14.264M768 560h64c35.346 0 64 25.072 64 56s-28.654 56-64 56h-48a16 16 0 0 1-16-16z'/></svg>",
+ "folder-bicep-open": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fff9c4' d='M15 25s1.15-11.115 4-15l4 1-1 3h-2v7h2c1.9-2.915 5.381-4.255 7.755-3.19 3.134 1.453 2.85 5.831 0 7.769C27.475 27.137 20.699 28.885 15 25'/></svg>",
+ "folder-bicep": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fff9c4' d='M15 25s1.15-11.115 4-15l4 1-1 3h-2v7h2c1.9-2.915 5.381-4.255 7.755-3.19 3.134 1.453 2.85 5.831 0 7.769C27.475 27.137 20.699 28.885 15 25'/></svg>",
+ "folder-blender-open": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><g fill='#ffe0b2' fill-rule='evenodd'><path d='M19.716 20.926a3.215 3.215 0 0 1 3.21-3.216 3.215 3.215 0 0 1 3.22 3.206 3.215 3.215 0 0 1-3.2 3.224 3.215 3.215 0 0 1-3.23-3.195z' paint-order='stroke fill markers'/><path d='m25.595 13.309-2.8-2.862s-.957-.987-1.943-.03-.03 1.943-.03 1.943l.107.17c.445.444.907.88 1.353 1.324h-7.715s-1.286 0-1.286 1.285c0 1.286 1.286 1.286 1.286 1.286h2.572l-3.858 3.858-.857.907s-.967.966 0 1.933c.966.966 1.933 0 1.933 0l1.496-1.554.04.122q.039.357.112.701a7.08 7.08 0 0 0 5.528 5.47q.692.14 1.425.137a7.073 7.073 0 0 0 7.04-7.094c0-3.505-2.572-5.766-4.403-7.597zm-2.676 3.116a4.5 4.5 0 1 1-4.494 4.529v-.028a4.5 4.5 0 0 1 4.494-4.5'/></g></svg>",
+ "folder-blender": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><g fill='#ffe0b2' fill-rule='evenodd'><path d='M19.716 20.926a3.215 3.215 0 0 1 3.21-3.216 3.215 3.215 0 0 1 3.22 3.206 3.215 3.215 0 0 1-3.2 3.224 3.215 3.215 0 0 1-3.23-3.195z' paint-order='stroke fill markers'/><path d='m25.595 13.309-2.8-2.862s-.957-.987-1.943-.03-.03 1.943-.03 1.943l.107.17c.445.444.907.88 1.353 1.324h-7.715s-1.286 0-1.286 1.285c0 1.286 1.286 1.286 1.286 1.286h2.572l-3.858 3.858-.857.907s-.967.966 0 1.933c.966.966 1.933 0 1.933 0l1.496-1.554.04.122q.039.357.112.701a7.08 7.08 0 0 0 5.528 5.47q.692.14 1.425.137a7.073 7.073 0 0 0 7.04-7.094c0-3.505-2.572-5.766-4.403-7.597zm-2.676 3.116a4.5 4.5 0 1 1-4.494 4.529v-.028a4.5 4.5 0 0 1 4.494-4.5'/></g></svg>",
+ "folder-bloc-open": "<svg fill='none' viewBox='0 0 32 32'><path fill='#26a69a' d='M29 12H9.4c-.9 0-1.6.6-1.9 1.4L4 24V10h24c0-1.1-.9-2-2-2H15.1c-.5 0-.9-.2-1.3-.5l-1.3-1.1c-.3-.2-.8-.4-1.2-.4H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h22l4.8-11.2c.4-1 0-2.2-1-2.6-.3-.1-.6-.2-.8-.2'/><path fill='#b2dfdb' d='m25 12 7 4.198v8.103L25 28.5l-7-4.212V16.21z'/></svg>",
+ "folder-bloc": "<svg fill='none' viewBox='0 0 32 32'><path fill='#26a69a' d='m13.8 7.5-1.3-1.1c-.3-.2-.8-.4-1.2-.4H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h24c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2H15.1c-.5 0-.9-.1-1.3-.5'/><path fill='#b2dfdb' d='m25 12 7 4.198v8.103L25 28.5l-7-4.212V16.21z'/></svg>",
+ "folder-bower-open": "<svg viewBox='0 0 32 32'><path fill='#8d6e63' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#5d4037' d='M31.598 20.789c-1.028-1.012-6.166-1.644-7.786-1.827a5 5 0 0 0 .2-.589 6 6 0 0 1 .707-.269c.03.092.171.439.251.605a3.165 3.165 0 0 0 3.533-2.78 3 3 0 0 0 .027-.407 4 4 0 0 1 1.241-2.574c-1.666-.498-4.062.77-4.864 2.657a5 5 0 0 0-.902-.253A4.396 4.396 0 0 0 19.75 12a8.07 8.07 0 0 0-7.746 8.364l.002.07c0 4.46 2.97 8.365 4.647 8.365a1.65 1.65 0 0 0 1.511-1.067c.124.346.505 1.42.63 1.694.184.404 1.04.755 1.414.335a1.355 1.355 0 0 0 1.843-.292 1.36 1.36 0 0 0 1.724-.89 1 1 0 0 0 .039-.149c.454-.026.678-.68.578-1.2a13 13 0 0 0-1.159-2.234c.604.503 2.132.645 2.317 0 .973.782 2.488.372 2.61-.264 1.18.314 2.536-.376 2.314-1.213a1.63 1.63 0 0 0 1.525-1.719 1.66 1.66 0 0 0-.401-1.011'/><path fill='#03a9f4' d='M26.252 16.374a7.4 7.4 0 0 1 1.57-2.302 4.14 4.14 0 0 0-1.836 2.12 6 6 0 0 0-.646-.37 4.2 4.2 0 0 1 3.424-2.419c-.999.928-.644 2.855-1.464 3.876a11 11 0 0 0-1.048-.906Zm-.646 1.354a4 4 0 0 1 .035-.39 3.4 3.4 0 0 0-.6-.082 2.4 2.4 0 0 0 .208.889 3.05 3.05 0 0 0 1.629-.462 5.5 5.5 0 0 0-1.099-.318c-.04.085-.137.3-.173.362Z'/><path fill='#4caf50' d='M22.847 24.304v.003a11 11 0 0 1-.32-.805c.475.708 1.962.343 1.884-.292.728.562 2.226-.093 1.885-.88.73.348 1.561-.352 1.375-.657 1.243.245 2.434.49 2.808.588a1.43 1.43 0 0 1-1.667.505c.46.643-.434 1.414-1.68.99.274.63-.835 1.2-2.096.541.016.632-1.565.705-2.19.007Zm2.465-3.193c1.443.113 3.828.334 5.305.545-.093-.492-.348-.633-1.15-.854-.862.095-3.05.315-4.155.309'/><path fill='#ffca28' d='M24.41 23.21c.729.562 2.227-.093 1.886-.88.73.348 1.561-.352 1.375-.657-1.47-.29-3.012-.582-3.361-.633a53 53 0 0 1 1.002.07c1.106.007 3.293-.213 4.156-.307a41 41 0 0 0-6.216-1.023 3 3 0 0 1-.55.615 4.82 4.82 0 0 1-4.15 3.108 5.5 5.5 0 0 1-1.697-.293 3.54 3.54 0 0 1-3.432.074 6.94 6.94 0 0 0 6.356 4.321c2.335 0 3.37-2.443 3.143-3.09-.055-.156-.272-.677-.394-1.013.474.708 1.961.343 1.883-.291Z'/><path fill='#e0e0e0' d='M22.996 18.294a6.7 6.7 0 0 1 1.597-.724q-.016-.116-.023-.233c-.446.11-1.285.478-1.766-.03 1.015.314 1.521-.28 2.268-.28a4.7 4.7 0 0 1 1.579.329 5.27 5.27 0 0 0-3.355-1.534 2.45 2.45 0 0 0-.3 2.472'/><path fill='#f4511e' d='M16.855 23.21a5.5 5.5 0 0 0 1.698.293 4.82 4.82 0 0 0 4.148-3.109 5.25 5.25 0 0 1-3.473 1.011 5.42 5.42 0 0 0 3.54-2.294 3.14 3.14 0 0 1 .314-3.836 3.52 3.52 0 0 0-3.333-2.4 7.174 7.174 0 0 0-6.893 7.431l.004.127a7.4 7.4 0 0 0 .563 2.85 3.54 3.54 0 0 0 3.432-.074Z'/><path fill='#ffca28' d='M17.985 16.619a1.74 1.74 0 1 0 1.74-1.783 1.76 1.76 0 0 0-1.74 1.783'/><path fill='#5d4037' d='M18.683 16.619a1.042 1.042 0 1 0 1.042-1.068 1.055 1.055 0 0 0-1.042 1.068'/><ellipse cx='19.725' cy='16.145' fill='#fafafa' rx='.607' ry='.387'/></svg>",
+ "folder-bower": "<svg viewBox='0 0 32 32'><path fill='#8d6e63' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#5d4037' d='M31.598 20.789c-1.028-1.012-6.166-1.644-7.786-1.827a5 5 0 0 0 .2-.589 6 6 0 0 1 .707-.269c.03.092.171.439.251.605a3.165 3.165 0 0 0 3.533-2.78 3 3 0 0 0 .027-.407 4 4 0 0 1 1.241-2.574c-1.666-.498-4.062.77-4.864 2.657a5 5 0 0 0-.902-.253A4.396 4.396 0 0 0 19.75 12a8.07 8.07 0 0 0-7.746 8.364l.002.07c0 4.46 2.97 8.365 4.647 8.365a1.65 1.65 0 0 0 1.511-1.067c.124.346.505 1.42.63 1.694.184.404 1.04.755 1.414.335a1.355 1.355 0 0 0 1.843-.292 1.36 1.36 0 0 0 1.724-.89 1 1 0 0 0 .039-.149c.454-.026.678-.68.578-1.2a13 13 0 0 0-1.159-2.234c.604.503 2.132.645 2.317 0 .973.782 2.488.372 2.61-.264 1.18.314 2.536-.376 2.314-1.213a1.63 1.63 0 0 0 1.525-1.719 1.66 1.66 0 0 0-.401-1.011'/><path fill='#03a9f4' d='M26.252 16.374a7.4 7.4 0 0 1 1.57-2.302 4.14 4.14 0 0 0-1.836 2.12 6 6 0 0 0-.646-.37 4.2 4.2 0 0 1 3.424-2.419c-.999.928-.644 2.855-1.464 3.876a11 11 0 0 0-1.048-.906Zm-.646 1.354a4 4 0 0 1 .035-.39 3.4 3.4 0 0 0-.6-.082 2.4 2.4 0 0 0 .208.889 3.05 3.05 0 0 0 1.629-.462 5.5 5.5 0 0 0-1.099-.318c-.04.085-.137.3-.173.362Z'/><path fill='#4caf50' d='M22.847 24.304v.003a11 11 0 0 1-.32-.805c.475.708 1.962.343 1.884-.292.728.562 2.226-.093 1.885-.88.73.348 1.561-.352 1.375-.657 1.243.245 2.434.49 2.808.588a1.43 1.43 0 0 1-1.667.505c.46.643-.434 1.414-1.68.99.274.63-.835 1.2-2.096.541.016.632-1.565.705-2.19.007Zm2.465-3.193c1.443.113 3.828.334 5.305.545-.093-.492-.348-.633-1.15-.854-.862.095-3.05.315-4.155.309'/><path fill='#ffca28' d='M24.41 23.21c.729.562 2.227-.093 1.886-.88.73.348 1.561-.352 1.375-.657-1.47-.29-3.012-.582-3.361-.633a53 53 0 0 1 1.002.07c1.106.007 3.293-.213 4.156-.307a41 41 0 0 0-6.216-1.023 3 3 0 0 1-.55.615 4.82 4.82 0 0 1-4.15 3.108 5.5 5.5 0 0 1-1.697-.293 3.54 3.54 0 0 1-3.432.074 6.94 6.94 0 0 0 6.356 4.321c2.335 0 3.37-2.443 3.143-3.09-.055-.156-.272-.677-.394-1.013.474.708 1.961.343 1.883-.291Z'/><path fill='#e0e0e0' d='M22.996 18.294a6.7 6.7 0 0 1 1.597-.724q-.016-.116-.023-.233c-.446.11-1.285.478-1.766-.03 1.015.314 1.521-.28 2.268-.28a4.7 4.7 0 0 1 1.579.329 5.27 5.27 0 0 0-3.355-1.534 2.45 2.45 0 0 0-.3 2.472'/><path fill='#f4511e' d='M16.855 23.21a5.5 5.5 0 0 0 1.698.293 4.82 4.82 0 0 0 4.148-3.109 5.25 5.25 0 0 1-3.473 1.011 5.42 5.42 0 0 0 3.54-2.294 3.14 3.14 0 0 1 .314-3.836 3.52 3.52 0 0 0-3.333-2.4 7.174 7.174 0 0 0-6.893 7.431l.004.127a7.4 7.4 0 0 0 .563 2.85 3.54 3.54 0 0 0 3.432-.074Z'/><path fill='#ffca28' d='M17.985 16.619a1.74 1.74 0 1 0 1.74-1.783 1.76 1.76 0 0 0-1.74 1.783'/><path fill='#5d4037' d='M18.683 16.619a1.042 1.042 0 1 0 1.042-1.068 1.055 1.055 0 0 0-1.042 1.068'/><ellipse cx='19.725' cy='16.145' fill='#fafafa' rx='.607' ry='.387'/></svg>",
+ "folder-buildkite-open": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c8e6c9' d='m20 24-6-2v-6l6 2zm10-2h-4v-6l6 4z'/><path fill='#a5d6a7' d='m20 24 6-2v-6l-6 2zm6 4 6-2v-6l-6 2z'/></svg>",
+ "folder-buildkite": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c8e6c9' d='m20 24-6-2v-6l6 2zm10-2h-4v-6l6 4z'/><path fill='#a5d6a7' d='m20 24 6-2v-6l-6 2zm6 4 6-2v-6l-6 2z'/></svg>",
+ "folder-cart-open": "<svg viewBox='0 0 32 32'><path fill='#009688' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><circle cx='20' cy='26' r='2' fill='#b2dfdb'/><circle cx='28' cy='26' r='2' fill='#b2dfdb'/><path fill='#b2dfdb' d='M30.613 12H18.22l-.4-2H14v2h2.18l1.84 9.196A1 1 0 0 0 19 22h11v-2H19.82l-.4-2H30l1.561-4.684A1 1 0 0 0 30.613 12'/></svg>",
+ "folder-cart": "<svg viewBox='0 0 32 32'><path fill='#009688' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><circle cx='20' cy='26' r='2' fill='#b2dfdb'/><circle cx='28' cy='26' r='2' fill='#b2dfdb'/><path fill='#b2dfdb' d='M30.613 12H18.22l-.4-2H14v2h2.18l1.84 9.196A1 1 0 0 0 19 22h11v-2H19.82l-.4-2H30l1.561-4.684A1 1 0 0 0 30.613 12'/></svg>",
+ "folder-changesets-open": "<svg viewBox='0 0 32 32'><path fill='#2196f3' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M14.003 14a6.68 6.68 0 0 0 6.335 6.98h1.9a6.62 6.62 0 0 0-6.238 7h1.172A6.36 6.36 0 0 0 23 23.73a6.36 6.36 0 0 0 5.828 4.25H30a6.62 6.62 0 0 0-6.239-7h1.901A6.68 6.68 0 0 0 31.997 14h-3.169A6.34 6.34 0 0 0 23 18.23 6.34 6.34 0 0 0 17.172 14ZM23 18.234a7.7 7.7 0 0 1 0 5.493 7.7 7.7 0 0 1 0-5.493'/></svg>",
+ "folder-changesets": "<svg viewBox='0 0 32 32'><path fill='#2196f3' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M14.003 14a6.68 6.68 0 0 0 6.335 6.98h1.9a6.62 6.62 0 0 0-6.238 7h1.172A6.36 6.36 0 0 0 23 23.73a6.36 6.36 0 0 0 5.828 4.25H30a6.62 6.62 0 0 0-6.239-7h1.901A6.68 6.68 0 0 0 31.997 14h-3.169A6.34 6.34 0 0 0 23 18.23 6.34 6.34 0 0 0 17.172 14ZM23 18.234a7.7 7.7 0 0 1 0 5.493 7.7 7.7 0 0 1 0-5.493'/></svg>",
+ "folder-ci-open": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M23.998 12.978v2.986l4-3.981-4-3.983v2.986A7.98 7.98 0 0 0 16 18.95v.002a7.9 7.9 0 0 0 1.239 4.24l1.46-1.455a5.86 5.86 0 0 1-.7-2.785 5.987 5.987 0 0 1 6-5.974m6.759 1.732-1.46 1.454a5.968 5.968 0 0 1-5.3 8.76v-2.985l-4 3.983 4 3.981v-2.986a7.98 7.98 0 0 0 7.999-7.964v-.001a7.87 7.87 0 0 0-1.24-4.24Z'/></svg>",
+ "folder-ci": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M23.998 12.978v2.986l4-3.981-4-3.983v2.986A7.98 7.98 0 0 0 16 18.95v.002a7.9 7.9 0 0 0 1.239 4.24l1.46-1.455a5.86 5.86 0 0 1-.7-2.785 5.987 5.987 0 0 1 6-5.974m6.759 1.732-1.46 1.454a5.968 5.968 0 0 1-5.3 8.76v-2.985l-4 3.983 4 3.981v-2.986a7.98 7.98 0 0 0 7.999-7.964v-.001a7.87 7.87 0 0 0-1.24-4.24Z'/></svg>",
+ "folder-circleci-open": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#eceff1' d='M22.954 10a9 9 0 0 0-8.555 6.367.5.5 0 0 0 .486.633h3.23a.5.5 0 0 0 .442-.28 5 5 0 1 1-.001 4.557.5.5 0 0 0-.439-.277h-3.232a.5.5 0 0 0-.486.633 8.997 8.997 0 0 0 17.597-2.892A9.103 9.103 0 0 0 22.954 10'/><circle cx='23' cy='19' r='2' fill='#eceff1'/></svg>",
+ "folder-circleci": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#eceff1' d='M22.954 10a9 9 0 0 0-8.555 6.367.5.5 0 0 0 .486.633h3.23a.5.5 0 0 0 .442-.28 5 5 0 1 1-.001 4.557.5.5 0 0 0-.439-.277h-3.232a.5.5 0 0 0-.486.633 8.997 8.997 0 0 0 17.597-2.892A9.103 9.103 0 0 0 22.954 10'/><circle cx='23' cy='19' r='2' fill='#eceff1'/></svg>",
+ "folder-class-open": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M16 12v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V12a2 2 0 0 0-2-2H18a2 2 0 0 0-2 2m14 14H18v-4h12Zm0-6H18v-4h12Z'/></svg>",
+ "folder-class": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M16 12v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V12a2 2 0 0 0-2-2H18a2 2 0 0 0-2 2m14 14H18v-4h12Zm0-6H18v-4h12Z'/></svg>",
+ "folder-client-open": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M15 12a1 1 0 0 0-1 1v10.994h-4v4h12v-4h-6V14h14v-2.006Z'/><path fill='#b3e5fc' d='M31 16h-6a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V17a1 1 0 0 0-1-1m-1 8h-4v-6h4Z'/></svg>",
+ "folder-client": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M15 12a1 1 0 0 0-1 1v10.994h-4v4h12v-4h-6V14h14v-2.006Z'/><path fill='#b3e5fc' d='M31 16h-6a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V17a1 1 0 0 0-1-1m-1 8h-4v-6h4Z'/></svg>",
+ "folder-cline-open": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M23 12a2 2 0 0 0-2 2h-1c-2.216 0-4 1.784-4 4l-2 3 2 3c0 2.216 1.784 4 4 4h6c2.216 0 4-1.784 4-4l2-3-2-3c0-2.216-1.784-4-4-4h-1a2 2 0 0 0-2-2m-2 6c.554 0 1 .446 1 1v4c0 .554-.446 1-1 1s-1-.446-1-1v-4c0-.554.446-1 1-1m4 0c.554 0 1 .446 1 1v4c0 .554-.446 1-1 1s-1-.446-1-1v-4c0-.554.446-1 1-1'/></svg>",
+ "folder-cline": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M23 12a2 2 0 0 0-2 2h-1c-2.216 0-4 1.784-4 4l-2 3 2 3c0 2.216 1.784 4 4 4h6c2.216 0 4-1.784 4-4l2-3-2-3c0-2.216-1.784-4-4-4h-1a2 2 0 0 0-2-2m-2 6c.554 0 1 .446 1 1v4c0 .554-.446 1-1 1s-1-.446-1-1v-4c0-.554.446-1 1-1m4 0c.554 0 1 .446 1 1v4c0 .554-.446 1-1 1s-1-.446-1-1v-4c0-.554.446-1 1-1'/></svg>",
+ "folder-cloud-functions-open": "<svg xmlns:xlink='http://www.w3.org/1999/xlink' fill-rule='evenodd' stroke-linejoin='round' stroke-miterlimit='2' clip-rule='evenodd' viewBox='0 0 32 32'><defs><path id='a' fill='#bbdefb' d='m26 14 2-2 4 4-2 2z'/></defs><path fill='#2196f3' fill-rule='nonzero' d='M28.967 12H9.442c-.859 0-1.627.553-1.898 1.368L4 24V10h24c0-1.097-.903-2-2-2H15.124c-.468 0-.921-.164-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4c-1.097 0-2 .903-2 2v16c0 1.097.903 2 2 2h22l4.805-11.212c.107-.249.162-.517.162-.788 0-1.097-.903-2-2-2'/><path fill='#bbdefb' d='M21.982 17.988h2.037v1.996h-2.037zm-2.983.021h2.037v1.996h-2.037zm5.998 0h1.996v1.996h-1.996zM29 14h3v10h-3z'/><use xlink:href='#a' transform='translate(0 -2)'/><use xlink:href='#a' transform='matrix(1 0 0 -1 0 40)'/><path fill='#bbdefb' d='M14 14h3v10h-3z'/><use xlink:href='#a' transform='matrix(-1 0 0 1 46 -2)'/><use xlink:href='#a' transform='rotate(180 23 20)'/></svg>",
+ "folder-cloud-functions": "<svg xmlns:xlink='http://www.w3.org/1999/xlink' fill-rule='evenodd' stroke-linejoin='round' stroke-miterlimit='2' clip-rule='evenodd' viewBox='0 0 32 32'><defs><path id='a' fill='#bbdefb' d='m26 14 2-2 4 4-2 2z'/></defs><path fill='#2196f3' fill-rule='nonzero' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4c-1.097 0-2 .903-2 2v16c0 1.097.903 2 2 2h24c1.097 0 2-.903 2-2V10c0-1.097-.903-2-2-2H15.124c-.468 0-.921-.164-1.28-.464'/><path fill='#bbdefb' d='M21.982 17.988h2.037v1.996h-2.037zm-2.983.021h2.037v1.996h-2.037zm5.998 0h1.996v1.996h-1.996zM29 14h3v10h-3z'/><use xlink:href='#a' transform='translate(0 -2)'/><use xlink:href='#a' transform='matrix(1 0 0 -1 0 40)'/><path fill='#bbdefb' d='M14 14h3v10h-3z'/><use xlink:href='#a' transform='matrix(-1 0 0 1 46 -2)'/><use xlink:href='#a' transform='rotate(180 23 20)'/></svg>",
+ "folder-cloudflare-open": "<svg viewBox='0 0 32 32'><path fill='#ff8a65' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#efebe9' d='M27.881 19.229a6.591 6.591 0 0 0-12.308-1.759 5.278 5.278 0 0 0 .572 10.524h11.428a4.388 4.388 0 0 0 .308-8.765'/></svg>",
+ "folder-cloudflare": "<svg viewBox='0 0 32 32'><path fill='#ff8a65' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#efebe9' d='M27.881 19.229a6.591 6.591 0 0 0-12.308-1.759 5.278 5.278 0 0 0 .572 10.524h11.428a4.388 4.388 0 0 0 .308-8.765'/></svg>",
+ "folder-cluster-open": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><circle cx='21' cy='15' r='3' fill='#b2dfdb'/><circle cx='17' cy='23' r='3' fill='#b2dfdb'/><circle cx='27' cy='27' r='3' fill='#b2dfdb'/></svg>",
+ "folder-cluster": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><circle cx='21' cy='15' r='3' fill='#b2dfdb'/><circle cx='17' cy='23' r='3' fill='#b2dfdb'/><circle cx='27' cy='27' r='3' fill='#b2dfdb'/></svg>",
+ "folder-cobol-open": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M23.448 10.106a10 10 0 0 0-2.896 0l-.634 1.653a8.5 8.5 0 0 0-2.273.942l-1.618-.72a10 10 0 0 0-2.047 2.046l.721 1.618a8.5 8.5 0 0 0-.942 2.273l-1.653.634a10 10 0 0 0 0 2.896l1.653.634a8.5 8.5 0 0 0 .942 2.273l-.72 1.618a10 10 0 0 0 2.046 2.047l1.618-.721a8.5 8.5 0 0 0 2.273.942l.634 1.653a10 10 0 0 0 2.896 0l.634-1.653a8.5 8.5 0 0 0 2.273-.942l1.618.72a10 10 0 0 0 2.047-2.046l-.721-1.618a8.5 8.5 0 0 0 .942-2.273l1.653-.634a10 10 0 0 0 0-2.896l-1.653-.634a8.5 8.5 0 0 0-.942-2.273l.72-1.618a10 10 0 0 0-2.046-2.047l-1.618.721a8.5 8.5 0 0 0-2.273-.942ZM22 13.592A6.408 6.408 0 1 1 15.592 20 6.41 6.41 0 0 1 22 13.592'/><path fill='#b3e5fc' d='M24.607 22.602a3.62 3.62 0 1 1-.006-5.118l.006.006-1.28 1.278a1.81 1.81 0 1 0-.004 2.56l.004-.004Z'/></svg>",
+ "folder-cobol": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M23.448 10.106a10 10 0 0 0-2.896 0l-.634 1.653a8.5 8.5 0 0 0-2.273.942l-1.618-.72a10 10 0 0 0-2.047 2.046l.721 1.618a8.5 8.5 0 0 0-.942 2.273l-1.653.634a10 10 0 0 0 0 2.896l1.653.634a8.5 8.5 0 0 0 .942 2.273l-.72 1.618a10 10 0 0 0 2.046 2.047l1.618-.721a8.5 8.5 0 0 0 2.273.942l.634 1.653a10 10 0 0 0 2.896 0l.634-1.653a8.5 8.5 0 0 0 2.273-.942l1.618.72a10 10 0 0 0 2.047-2.046l-.721-1.618a8.5 8.5 0 0 0 .942-2.273l1.653-.634a10 10 0 0 0 0-2.896l-1.653-.634a8.5 8.5 0 0 0-.942-2.273l.72-1.618a10 10 0 0 0-2.046-2.047l-1.618.721a8.5 8.5 0 0 0-2.273-.942ZM22 13.592A6.408 6.408 0 1 1 15.592 20 6.41 6.41 0 0 1 22 13.592'/><path fill='#b3e5fc' d='M24.607 22.602a3.62 3.62 0 1 1-.006-5.118l.006.006-1.28 1.278a1.81 1.81 0 1 0-.004 2.56l.004-.004Z'/></svg>",
+ "folder-command-open": "<svg viewBox='0 0 32 32'><path fill='#03a9f4' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M28 28a2 2 0 0 0 2-2V16a2 2 0 0 0-2-2H12a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2H8v2h24v-2Zm0-2h-8v-2h8Zm-16-8.264 1.014-1.724L20.037 20v2l-7.023 4L12 24.275l5.558-3.27Z'/></svg>",
+ "folder-command": "<svg viewBox='0 0 32 32'><path fill='#03a9f4' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M28 28a2 2 0 0 0 2-2V16a2 2 0 0 0-2-2H12a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2H8v2h24v-2Zm0-2h-8v-2h8Zm-16-8.264 1.014-1.724L20.037 20v2l-7.023 4L12 24.275l5.558-3.27Z'/></svg>",
+ "folder-components-open": "<svg viewBox='0 0 32 32'><path fill='#c0ca33' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f0f4c3' d='M12 20h8v8h-8zm10 0h8v8h-8zM12 10h8v8h-8zm8.343 4L26 8.343 31.657 14 26 19.657z'/></svg>",
+ "folder-components": "<svg viewBox='0 0 32 32'><path fill='#c0ca33' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#f0f4c3' d='M12 20h8v8h-8zm10 0h8v8h-8zM12 10h8v8h-8zm8.343 4L26 8.343 31.657 14 26 19.657z'/></svg>",
+ "folder-config-open": "<svg viewBox='0 0 32 32'><path fill='#00acc1' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#80deea' d='M23.001 24.15A3.195 3.195 0 0 1 19.762 21a3.24 3.24 0 1 1 3.239 3.15m6.875-2.277a7 7 0 0 0 .064-.874 8 8 0 0 0-.064-.9l1.951-1.467a.446.446 0 0 0 .113-.576l-1.853-3.112a.46.46 0 0 0-.564-.199l-2.302.9a6.8 6.8 0 0 0-1.565-.882l-.342-2.385A.464.464 0 0 0 24.85 12h-3.7a.464.464 0 0 0-.463.378l-.341 2.385a6.8 6.8 0 0 0-1.563.881l-2.304-.899a.46.46 0 0 0-.564.198l-1.851 3.113a.436.436 0 0 0 .112.576l1.95 1.468a8 8 0 0 0-.064.9 7 7 0 0 0 .064.873l-1.95 1.493a.436.436 0 0 0-.112.576l1.85 3.115a.47.47 0 0 0 .565.198l2.304-.91a6.4 6.4 0 0 0 1.563.892l.342 2.385a.464.464 0 0 0 .463.378h3.7a.464.464 0 0 0 .464-.378l.34-2.385a6.8 6.8 0 0 0 1.566-.891l2.302.909a.475.475 0 0 0 .566-.198l1.85-3.115a.446.446 0 0 0-.112-.576Z'/></svg>",
+ "folder-config": "<svg viewBox='0 0 32 32'><path fill='#00acc1' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#80deea' d='M23.001 24.15A3.195 3.195 0 0 1 19.762 21a3.24 3.24 0 1 1 3.239 3.15m6.875-2.277a7 7 0 0 0 .064-.874 8 8 0 0 0-.064-.9l1.951-1.467a.446.446 0 0 0 .113-.576l-1.853-3.112a.46.46 0 0 0-.564-.199l-2.302.9a6.8 6.8 0 0 0-1.565-.882l-.342-2.385A.464.464 0 0 0 24.85 12h-3.7a.464.464 0 0 0-.463.378l-.341 2.385a6.8 6.8 0 0 0-1.563.881l-2.304-.899a.46.46 0 0 0-.564.198l-1.851 3.113a.436.436 0 0 0 .112.576l1.95 1.468a8 8 0 0 0-.064.9 7 7 0 0 0 .064.873l-1.95 1.493a.436.436 0 0 0-.112.576l1.85 3.115a.47.47 0 0 0 .565.198l2.304-.91a6.4 6.4 0 0 0 1.563.892l.342 2.385a.464.464 0 0 0 .463.378h3.7a.464.464 0 0 0 .464-.378l.34-2.385a6.8 6.8 0 0 0 1.566-.891l2.302.909a.475.475 0 0 0 .566-.198l1.85-3.115a.446.446 0 0 0-.112-.576Z'/></svg>",
+ "folder-connection-open": "<svg viewBox='0 0 32 32'><path fill='#00acc1' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2ebf2' d='M30.868 15.821a1.81 1.81 0 0 1 .064 2.566q-.031.033-.064.064l-2.642 2.63-7.358-7.325 2.642-2.63a1.83 1.83 0 0 1 2.577-.063q.033.031.064.064l1.698 1.69L30.679 10 32 11.314l-2.83 2.818zm-5.471 5.445-1.322-1.314-2.64 2.63-1.981-1.974 2.64-2.627-1.32-1.316-2.642 2.63-1.415-1.314-2.64 2.627a1.81 1.81 0 0 0-.064 2.566q.03.033.064.064l1.697 1.69L12 28.683 13.322 30l3.774-3.756 1.697 1.69a1.83 1.83 0 0 0 2.578.063q.033-.031.064-.064l2.64-2.628-1.32-1.315Z'/></svg>",
+ "folder-connection": "<svg viewBox='0 0 32 32'><path fill='#00acc1' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2ebf2' d='M30.868 15.821a1.81 1.81 0 0 1 .064 2.566q-.031.033-.064.064l-2.642 2.63-7.358-7.325 2.642-2.63a1.83 1.83 0 0 1 2.577-.063q.033.031.064.064l1.698 1.69L30.679 10 32 11.314l-2.83 2.818zm-5.471 5.445-1.322-1.314-2.64 2.63-1.981-1.974 2.64-2.627-1.32-1.316-2.642 2.63-1.415-1.314-2.64 2.627a1.81 1.81 0 0 0-.064 2.566q.03.033.064.064l1.697 1.69L12 28.683 13.322 30l3.774-3.756 1.697 1.69a1.83 1.83 0 0 0 2.578.063q.033-.031.064-.064l2.64-2.628-1.32-1.315Z'/></svg>",
+ "folder-console-open": "<svg viewBox='0 0 32 32'><path fill='#8bc34a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#dcedc8' d='M26 14H16a4.533 4.533 0 0 0-4.498 3.971l-1.483 9.276A2.45 2.45 0 0 0 12.45 30a2.45 2.45 0 0 0 1.96-.98L17 26h8l2.59 3.02a2.45 2.45 0 0 0 1.96.98 2.45 2.45 0 0 0 2.43-2.753l-1.482-9.276A4.533 4.533 0 0 0 26 14m-1 2a1 1 0 1 1-1 1 1 1 0 0 1 1-1m-5 4h-2v2h-2v-2h-2v-2h2v-2h2v2h2Zm3 0a1 1 0 1 1 1-1 1 1 0 0 1-1 1m2 2a1 1 0 1 1 1-1 1 1 0 0 1-1 1m2-2a1 1 0 1 1 1-1 1 1 0 0 1-1 1'/></svg>",
+ "folder-console": "<svg viewBox='0 0 32 32'><path fill='#8bc34a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#dcedc8' d='M26 14H16a4.533 4.533 0 0 0-4.498 3.971l-1.483 9.276A2.45 2.45 0 0 0 12.45 30a2.45 2.45 0 0 0 1.96-.98L17 26h8l2.59 3.02a2.45 2.45 0 0 0 1.96.98 2.45 2.45 0 0 0 2.43-2.753l-1.482-9.276A4.533 4.533 0 0 0 26 14m-1 2a1 1 0 1 1-1 1 1 1 0 0 1 1-1m-5 4h-2v2h-2v-2h-2v-2h2v-2h2v2h2Zm3 0a1 1 0 1 1 1-1 1 1 0 0 1-1 1m2 2a1 1 0 1 1 1-1 1 1 0 0 1-1 1m2-2a1 1 0 1 1 1-1 1 1 0 0 1-1 1'/></svg>",
+ "folder-constant-open": "<svg viewBox='0 0 32 32'><path fill='#607d8b' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#cfd8dc' d='M16 16h4v12h-4zm6 0h4v12h-4zm6 0h4v12h-4z'/></svg>",
+ "folder-constant": "<svg viewBox='0 0 32 32'><path fill='#607d8b' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#cfd8dc' d='M16 16h4v12h-4zm6 0h4v12h-4zm6 0h4v12h-4z'/></svg>",
+ "folder-container-open": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='m31.532 14.153-8.085-4.048a1 1 0 0 0-.894 0l-8.085 4.048A1 1 0 0 0 14 15v10a1 1 0 0 0 .553.895l8.046 4.01a.9.9 0 0 0 .802 0l8.046-4.01A1 1 0 0 0 32 25V15a1 1 0 0 0-.468-.847M22 27.382l-6-3v-7.79l6 2.887Zm1-9.64-5.73-2.759L23 12.118l5.73 2.865Zm7 6.64-6 3v-7.903l6-2.888Z'/></svg>",
+ "folder-container": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='m31.532 14.153-8.085-4.048a1 1 0 0 0-.894 0l-8.085 4.048A1 1 0 0 0 14 15v10a1 1 0 0 0 .553.895l8.046 4.01a.9.9 0 0 0 .802 0l8.046-4.01A1 1 0 0 0 32 25V15a1 1 0 0 0-.468-.847M22 27.382l-6-3v-7.79l6 2.887Zm1-9.64-5.73-2.759L23 12.118l5.73 2.865Zm7 6.64-6 3v-7.903l6-2.888Z'/></svg>",
+ "folder-content-open": "<svg viewBox='0 0 32 32'><path fill='#00bcd4' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2ebf2' d='M22 18h6v2h-6zm0 4h6v2h-6z'/><path fill='#b2ebf2' d='M10 15v12a1 1 0 0 0 1 1h20a1 1 0 0 0 1-1V15a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1m20 11H20V16h10Z'/></svg>",
+ "folder-content": "<svg viewBox='0 0 32 32'><path fill='#00bcd4' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2ebf2' d='M22 18h6v2h-6zm0 4h6v2h-6z'/><path fill='#b2ebf2' d='M10 15v12a1 1 0 0 0 1 1h20a1 1 0 0 0 1-1V15a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1m20 11H20V16h10Z'/></svg>",
+ "folder-context-open": "<svg viewBox='0 0 32 32'><path fill='#00897b' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2dfdb' d='M30 22v-2a2 2 0 0 0-2-2h-6v-2h2v-6h-6v6h2v2h-6a2 2 0 0 0-2 2v2h-2v6h6v-6h-2v-2h6v2h-2v6h6v-6h-2v-2h6v2h-2v6h6v-6Z'/></svg>",
+ "folder-context": "<svg viewBox='0 0 32 32'><path fill='#00897b' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2dfdb' d='M30 22v-2a2 2 0 0 0-2-2h-6v-2h2v-6h-6v6h2v2h-6a2 2 0 0 0-2 2v2h-2v6h6v-6h-2v-2h6v2h-2v6h6v-6h-2v-2h6v2h-2v6h6v-6Z'/></svg>",
+ "folder-contract-open": "<svg viewBox='0 0 32 32'><path fill='#448aff' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M24.368 20.368a.947.947 0 0 0-.044 1.339l.044.044a.98.98 0 0 0 1.382 0 4.87 4.87 0 0 0 0-6.883l-3.446-3.447a4.867 4.867 0 0 0-6.883 6.882l1.449 1.452a6.7 6.7 0 0 1 .39-2.356l-.457-.468a2.903 2.903 0 0 1-.022-4.105l.022-.023a2.903 2.903 0 0 1 4.106-.02l.021.02 3.438 3.437a2.903 2.903 0 0 1 .022 4.106zm-2.746-4.128a.98.98 0 0 0-1.383 0 4.87 4.87 0 0 0 0 6.883l3.448 3.447a4.867 4.867 0 0 0 6.892-6.873l-.01-.01-1.45-1.451a6.8 6.8 0 0 1-.39 2.366l.458.456a2.903 2.903 0 0 1 .024 4.105l-.024.025a2.903 2.903 0 0 1-4.106.022l-.022-.022-3.437-3.437a2.903 2.903 0 0 1-.023-4.106l.023-.022a.947.947 0 0 0 .043-1.34q-.02-.022-.043-.043'/></svg>",
+ "folder-contract": "<svg viewBox='0 0 32 32'><path fill='#448aff' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M24.368 20.368a.947.947 0 0 0-.044 1.339l.044.044a.98.98 0 0 0 1.382 0 4.87 4.87 0 0 0 0-6.883l-3.446-3.447a4.867 4.867 0 0 0-6.883 6.882l1.449 1.452a6.7 6.7 0 0 1 .39-2.356l-.457-.468a2.903 2.903 0 0 1-.022-4.105l.022-.023a2.903 2.903 0 0 1 4.106-.02l.021.02 3.438 3.437a2.903 2.903 0 0 1 .022 4.106zm-2.746-4.128a.98.98 0 0 0-1.383 0 4.87 4.87 0 0 0 0 6.883l3.448 3.447a4.867 4.867 0 0 0 6.892-6.873l-.01-.01-1.45-1.451a6.8 6.8 0 0 1-.39 2.366l.458.456a2.903 2.903 0 0 1 .024 4.105l-.024.025a2.903 2.903 0 0 1-4.106.022l-.022-.022-3.437-3.437a2.903 2.903 0 0 1-.023-4.106l.023-.022a.947.947 0 0 0 .043-1.34q-.02-.022-.043-.043'/></svg>",
+ "folder-controller-open": "<svg viewBox='0 0 32 32'><path fill='#ffc107' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fff9c4' d='M23.057 23.715a3.077 3.077 0 0 1-3.077-3.078 3.077 3.077 0 1 1 3.077 3.078m6.532-2.224a7 7 0 0 0 .062-.854 8 8 0 0 0-.062-.878l1.855-1.434a.444.444 0 0 0 .107-.562l-1.76-3.04a.43.43 0 0 0-.536-.195l-2.188.88a6.4 6.4 0 0 0-1.487-.863l-.325-2.329a.444.444 0 0 0-.44-.37h-3.516a.444.444 0 0 0-.44.37l-.324 2.33a6.4 6.4 0 0 0-1.486.86l-2.189-.878a.43.43 0 0 0-.536.193l-1.759 3.042a.433.433 0 0 0 .107.562l1.853 1.434a8 8 0 0 0-.061.88 7 7 0 0 0 .061.852l-1.853 1.458a.433.433 0 0 0-.107.563l1.759 3.043a.44.44 0 0 0 .536.193l2.19-.888a6 6 0 0 0 1.485.87l.325 2.33a.444.444 0 0 0 .44.37h3.516a.444.444 0 0 0 .44-.37l.324-2.33a6.4 6.4 0 0 0 1.487-.87l2.188.888a.445.445 0 0 0 .537-.193l1.757-3.043a.444.444 0 0 0-.105-.563Z'/></svg>",
+ "folder-controller": "<svg viewBox='0 0 32 32'><path fill='#ffc107' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fff9c4' d='M23.001 24.15A3.195 3.195 0 0 1 19.762 21a3.24 3.24 0 1 1 3.239 3.15m6.875-2.277a7 7 0 0 0 .064-.874 8 8 0 0 0-.064-.9l1.951-1.467a.446.446 0 0 0 .113-.576l-1.853-3.112a.46.46 0 0 0-.564-.199l-2.302.9a6.8 6.8 0 0 0-1.565-.882l-.342-2.385A.464.464 0 0 0 24.85 12h-3.7a.464.464 0 0 0-.463.378l-.341 2.385a6.8 6.8 0 0 0-1.563.881l-2.304-.899a.46.46 0 0 0-.564.198l-1.851 3.113a.436.436 0 0 0 .112.576l1.95 1.468a8 8 0 0 0-.064.9 7 7 0 0 0 .064.873l-1.95 1.493a.436.436 0 0 0-.112.576l1.85 3.115a.47.47 0 0 0 .565.198l2.304-.91a6.4 6.4 0 0 0 1.563.892l.342 2.385a.464.464 0 0 0 .463.378h3.7a.464.464 0 0 0 .464-.378l.34-2.385a6.8 6.8 0 0 0 1.566-.891l2.302.909a.475.475 0 0 0 .566-.198l1.85-3.115a.446.446 0 0 0-.112-.576Z'/></svg>",
+ "folder-core-open": "<svg viewBox='0 0 32 32'><path fill='#1976d2' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M18 14v10h10V14Zm8 8h-6v-6h6ZM14 12h2v-2a2 2 0 0 0-2 2m4-2h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm4 0v2h2a2 2 0 0 0-2-2m0 4h2v2h-2zm0 4h2v2h-2zm0 4h2v2h-2zm0 6a2 2 0 0 0 2-2h-2Zm-4-2h2v2h-2zm-4 0h2v2h-2zm-4 0h2v2h-2zm-2 2v-2h-2a2 2 0 0 0 2 2m-2-6h2v2h-2zm0-4h2v2h-2zm0-4h2v2h-2z'/></svg>",
+ "folder-core": "<svg viewBox='0 0 32 32'><path fill='#1976d2' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M18 14v10h10V14Zm8 8h-6v-6h6ZM14 12h2v-2a2 2 0 0 0-2 2m4-2h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm4 0v2h2a2 2 0 0 0-2-2m0 4h2v2h-2zm0 4h2v2h-2zm0 4h2v2h-2zm0 6a2 2 0 0 0 2-2h-2Zm-4-2h2v2h-2zm-4 0h2v2h-2zm-4 0h2v2h-2zm-2 2v-2h-2a2 2 0 0 0 2 2m-2-6h2v2h-2zm0-4h2v2h-2zm0-4h2v2h-2z'/></svg>",
+ "folder-coverage-open": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2dfdb' d='m23.444 23.265-3.11-3.156 1.095-1.112 2.015 2.035 5.127-5.2 1.096 1.12M25 10l-7 4v4.53A9.8 9.8 0 0 0 25 28a9.8 9.8 0 0 0 7-9.47V14Z'/></svg>",
+ "folder-coverage": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2dfdb' d='m23.444 23.265-3.11-3.156 1.095-1.112 2.015 2.035 5.127-5.2 1.096 1.12M25 10l-7 4v4.53A9.8 9.8 0 0 0 25 28a9.8 9.8 0 0 0 7-9.47V14Z'/></svg>",
+ "folder-css-open": "<svg xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 32 32'><defs><path id='a' fill='#d1c4e9' d='M14 20v-2h-2v8h2v-2h2v2a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2Zm10 0v-2a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2v2c0 .839.357 2.34 2.746 2.966C21.437 23.15 22 23.612 22 24v2h-2v-2h-2v2a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2v-2a3.345 3.345 0 0 0-2.746-2.967C20 20.703 20 20.193 20 20v-2h2v2Zm8 0v-2a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2v2c0 .839.357 2.34 2.746 2.966C29.437 23.15 30 23.612 30 24v2h-2v-2h-2v2a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2v-2a3.345 3.345 0 0 0-2.746-2.967C28 20.703 28 20.193 28 20v-2h2v2Z'/></defs><path fill='#7e57c2' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><use xlink:href='#a'/><use xlink:href='#a'/></svg>",
+ "folder-css": "<svg xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 32 32'><defs><path id='a' fill='#d1c4e9' d='M14 20v-2h-2v8h2v-2h2v2a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2Zm10 0v-2a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2v2c0 .839.357 2.34 2.746 2.966C21.438 23.15 22 23.612 22 24v2h-2v-2h-2v2a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2v-2a3.345 3.345 0 0 0-2.746-2.967C20 20.703 20 20.193 20 20v-2h2v2Zm8 0v-2a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2v2c0 .839.357 2.34 2.746 2.966C29.438 23.15 30 23.612 30 24v2h-2v-2h-2v2a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2v-2a3.345 3.345 0 0 0-2.746-2.967C28 20.703 28 20.193 28 20v-2h2v2Z'/></defs><path fill='#7e57c2' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><use xlink:href='#a'/><use xlink:href='#a'/></svg>",
+ "folder-custom-open": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffccbc' d='M26.017 24 22 28h10v-4zm-2.724-9.293L14 24v4h4l9.293-9.293a1 1 0 0 0 0-1.414l-2.586-2.586a1 1 0 0 0-1.414 0'/></svg>",
+ "folder-custom": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffccbc' d='M26.017 24 22 28h10v-4zm-2.724-9.293L14 24v4h4l9.293-9.293a1 1 0 0 0 0-1.414l-2.586-2.586a1 1 0 0 0-1.414 0'/></svg>",
+ "folder-cypress-open": "<svg viewBox='0 0 32 32'><path fill='#009688' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2dfdb' d='M22.999 10A8.994 8.994 0 0 0 14 18.99V19a8.994 8.994 0 0 0 8.988 9H23a8.994 8.994 0 0 0 9-8.988V19a9.017 9.017 0 0 0-9.001-9m-4.222 10.931a1.41 1.41 0 0 0 1.242.557 1.9 1.9 0 0 0 .755-.133 6.4 6.4 0 0 0 .817-.425l.916 1.31a3.9 3.9 0 0 1-2.585.916 4.05 4.05 0 0 1-2.028-.49 3.3 3.3 0 0 1-1.31-1.44 5.04 5.04 0 0 1-.457-2.194 5.2 5.2 0 0 1 .456-2.193 3.46 3.46 0 0 1 1.312-1.503 3.6 3.6 0 0 1 1.996-.525 3.7 3.7 0 0 1 1.407.23 4.2 4.2 0 0 1 1.21.72l-.915 1.243a3.6 3.6 0 0 0-.754-.427 2.1 2.1 0 0 0-.785-.131c-1.112 0-1.669.852-1.669 2.585a3.24 3.24 0 0 0 .393 1.899Zm9 2.03a4.7 4.7 0 0 1-1.505 2.323 4.9 4.9 0 0 1-2.75.95l-.228-1.537a3.74 3.74 0 0 0 1.67-.526 4 4 0 0 0 .391-.391l-2.716-8.707h2.257l1.572 6.513 1.67-6.513h2.193Z'/></svg>",
+ "folder-cypress": "<svg viewBox='0 0 32 32'><path fill='#009688' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2dfdb' d='M22.999 10A8.994 8.994 0 0 0 14 18.99V19a8.994 8.994 0 0 0 8.988 9H23a8.994 8.994 0 0 0 9-8.988V19a9.017 9.017 0 0 0-9.001-9m-4.222 10.931a1.41 1.41 0 0 0 1.242.557 1.9 1.9 0 0 0 .755-.133 6.4 6.4 0 0 0 .817-.425l.916 1.31a3.9 3.9 0 0 1-2.585.916 4.05 4.05 0 0 1-2.028-.49 3.3 3.3 0 0 1-1.31-1.44 5.04 5.04 0 0 1-.457-2.194 5.2 5.2 0 0 1 .456-2.193 3.46 3.46 0 0 1 1.312-1.503 3.6 3.6 0 0 1 1.996-.525 3.7 3.7 0 0 1 1.407.23 4.2 4.2 0 0 1 1.21.72l-.915 1.243a3.6 3.6 0 0 0-.754-.427 2.1 2.1 0 0 0-.785-.131c-1.112 0-1.669.852-1.669 2.585a3.24 3.24 0 0 0 .393 1.899Zm9 2.03a4.7 4.7 0 0 1-1.505 2.323 4.9 4.9 0 0 1-2.75.95l-.228-1.537a3.74 3.74 0 0 0 1.67-.526 4 4 0 0 0 .391-.391l-2.716-8.707h2.257l1.572 6.513 1.67-6.513h2.193Z'/></svg>",
+ "folder-dart-open": "<svg viewBox='0 0 32 32'><path fill='#2196f3' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M23.037 14.036 15.999 16l-1.948 7.036c-.147.533.058 1.273.458 1.654a486 486 0 0 1 5.49 5.31H26v-4h4v-6l-5.306-5.513c-.383-.397-1.125-.599-1.657-.45z'/></svg>",
+ "folder-dart": "<svg viewBox='0 0 32 32'><path fill='#2196f3' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M23.037 14.036 15.999 16l-1.948 7.036c-.147.533.058 1.273.458 1.654a486 486 0 0 1 5.49 5.31H26v-4h4v-6l-5.306-5.513c-.383-.397-1.125-.599-1.657-.45z'/></svg>",
+ "folder-database-open": "<svg viewBox='0 0 32 32'><path fill='#ffca28' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><ellipse cx='24' cy='14' fill='#ffecb3' rx='8' ry='4'/><path fill='#ffecb3' d='M24 26c-4.418 0-8-1.79-8-4v4c0 2.21 3.582 4 8 4s8-1.79 8-4v-4c0 2.21-3.582 4-8 4'/><path fill='#ffecb3' d='M24 20c-4.418 0-8-1.79-8-4v4c0 2.21 3.582 4 8 4s8-1.79 8-4v-4c0 2.21-3.582 4-8 4'/></svg>",
+ "folder-database": "<svg viewBox='0 0 32 32'><path fill='#ffca28' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><ellipse cx='24' cy='14' fill='#ffecb3' rx='8' ry='4'/><path fill='#ffecb3' d='M24 26c-4.418 0-8-1.79-8-4v4c0 2.21 3.582 4 8 4s8-1.79 8-4v-4c0 2.21-3.582 4-8 4'/><path fill='#ffecb3' d='M24 20c-4.418 0-8-1.79-8-4v4c0 2.21 3.582 4 8 4s8-1.79 8-4v-4c0 2.21-3.582 4-8 4'/></svg>",
+ "folder-debug-open": "<svg viewBox='0 0 32 32'><path fill='#f9a825' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fff9c4' d='M32 18v-2h-2.347a6 6 0 0 0-1.717-2.522L30 11.414 28.586 10l-2.412 2.412a5.94 5.94 0 0 0-4.347 0L19.414 10 18 11.414l2.064 2.064A6 6 0 0 0 18.347 16H16v2h2v2h-2v2h2v2h-2v2h2.349a5.992 5.992 0 0 0 11.302 0H32v-2h-2v-2h2v-2h-2v-2Zm-6 8h-4v-2h4Zm0-6h-4v-2h4Z'/></svg>",
+ "folder-debug": "<svg viewBox='0 0 32 32'><path fill='#f9a825' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fff9c4' d='M32 18v-2h-2.347a6 6 0 0 0-1.717-2.522L30 11.414 28.586 10l-2.412 2.412a5.94 5.94 0 0 0-4.347 0L19.414 10 18 11.414l2.064 2.064A6 6 0 0 0 18.347 16H16v2h2v2h-2v2h2v2h-2v2h2.349a5.992 5.992 0 0 0 11.302 0H32v-2h-2v-2h2v-2h-2v-2Zm-6 8h-4v-2h4Zm0-6h-4v-2h4Z'/></svg>",
+ "folder-decorators-open": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='M24.667 27.333h-20A2.667 2.667 0 0 1 2 24.667v-16A2.657 2.657 0 0 1 4.648 6h8.019l2.666 2.667h9.334a2.68 2.68 0 0 1 2.666 2.666H4.667v13.334L7.52 14h22.76l-3.04 11.333a2.67 2.67 0 0 1-2.573 2'/><path fill='#e1bee7' d='M23.66 30a7.8 7.8 0 0 1-3.737-.929 7.06 7.06 0 0 1-2.81-2.784 9.2 9.2 0 0 1-1.07-4.655 18.3 18.3 0 0 1 .863-5.874 12.6 12.6 0 0 1 2.349-4.267 10.1 10.1 0 0 1 3.392-2.604A9.3 9.3 0 0 1 26.607 8a5.22 5.22 0 0 1 4.101 1.455A5.64 5.64 0 0 1 32 13.347a5.4 5.4 0 0 1-.069.832q-.07.443-.153.914l-1.611 7.97h-2.308l-.029-1.006h-.11c-.464.258-.96.665-1.488.96a3.96 3.96 0 0 1-1.96.444 3.03 3.03 0 0 1-2.098-.818 2.79 2.79 0 0 1-.904-2.175 4.34 4.34 0 0 1 1.877-3.781 13 13 0 0 1 5.907-1.76 6 6 0 0 0 .167-1.22 3.94 3.94 0 0 0-.611-2.258 2.71 2.71 0 0 0-2.39-.9 4.9 4.9 0 0 0-2.42.692 7.7 7.7 0 0 0-2.266 2.051 10.9 10.9 0 0 0-1.682 3.367 15.3 15.3 0 0 0-.638 4.641 7.05 7.05 0 0 0 .721 3.366 5 5 0 0 0 1.864 2.009 4.67 4.67 0 0 0 2.39.665 4.4 4.4 0 0 0 1.668-.29 6.2 6.2 0 0 0 1.418-.818l1.279 2.272a7.3 7.3 0 0 1-2.21 1.122A9 9 0 0 1 23.66 30m2.018-9.056a2.8 2.8 0 0 0 1.04-.21 4.8 4.8 0 0 0 1.04-.574l.56-2.742a6.8 6.8 0 0 0-2.99.951 1.87 1.87 0 0 0-.772 1.512 1 1 0 0 0 .31.783 1.16 1.16 0 0 0 .812.28'/></svg>",
+ "folder-decorators": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#e1bee7' d='M23.66 30a7.8 7.8 0 0 1-3.737-.929 7.06 7.06 0 0 1-2.81-2.784 9.2 9.2 0 0 1-1.07-4.655 18.3 18.3 0 0 1 .863-5.874 12.6 12.6 0 0 1 2.349-4.267 10.1 10.1 0 0 1 3.392-2.604A9.3 9.3 0 0 1 26.607 8a5.22 5.22 0 0 1 4.101 1.455A5.64 5.64 0 0 1 32 13.347a5.4 5.4 0 0 1-.069.832q-.07.443-.153.914l-1.611 7.97h-2.308l-.029-1.006h-.11c-.464.258-.96.665-1.488.96a3.96 3.96 0 0 1-1.96.444 3.03 3.03 0 0 1-2.098-.818 2.79 2.79 0 0 1-.904-2.175 4.34 4.34 0 0 1 1.877-3.781 13 13 0 0 1 5.907-1.76 6 6 0 0 0 .167-1.22 3.94 3.94 0 0 0-.611-2.258 2.71 2.71 0 0 0-2.39-.9 4.9 4.9 0 0 0-2.42.692 7.7 7.7 0 0 0-2.266 2.051 10.9 10.9 0 0 0-1.682 3.367 15.3 15.3 0 0 0-.638 4.641 7.05 7.05 0 0 0 .721 3.366 5 5 0 0 0 1.864 2.009 4.67 4.67 0 0 0 2.39.665 4.4 4.4 0 0 0 1.668-.29 6.2 6.2 0 0 0 1.418-.818l1.279 2.272a7.3 7.3 0 0 1-2.21 1.122A9 9 0 0 1 23.66 30m2.018-9.056a2.8 2.8 0 0 0 1.04-.21 4.8 4.8 0 0 0 1.04-.574l.56-2.742a6.8 6.8 0 0 0-2.99.951 1.87 1.87 0 0 0-.772 1.512 1 1 0 0 0 .31.783 1.16 1.16 0 0 0 .812.28'/></svg>",
+ "folder-delta-open": "<svg viewBox='0 0 32 32'><path fill='#ec407a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f8bbd0' d='M23 17.699 28.337 26H17.663zM23 14l-9 14h18z'/></svg>",
+ "folder-delta": "<svg viewBox='0 0 32 32'><path fill='#ec407a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#f8bbd0' d='M23 17.699 28.337 26H17.663zM23 14l-9 14h18z'/></svg>",
+ "folder-desktop-open": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M30 12H14a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h6v2h-2v2h8v-2h-2v-2h6a2 2 0 0 0 2-2V14a2 2 0 0 0-2-2m0 12H14V14h16Z'/></svg>",
+ "folder-desktop": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M30 12H14a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h6v2h-2v2h8v-2h-2v-2h6a2 2 0 0 0 2-2V14a2 2 0 0 0-2-2m0 12H14V14h16Z'/></svg>",
+ "folder-development-open.clone": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M18.473 30a1 1 0 0 1-.238-.028 1.137 1.137 0 0 1-.828-1.323L20.5 12.905a1.13 1.13 0 0 1 .507-.744 1.06 1.06 0 0 1 .8-.134 1.14 1.14 0 0 1 .828 1.324l-3.101 15.744a1.12 1.12 0 0 1-.504.743 1.06 1.06 0 0 1-.557.162m6.2-2h-.077a1.08 1.08 0 0 1-.762-.412 1.164 1.164 0 0 1 .113-1.548l5.319-4.967-5.296-4.623a1.165 1.165 0 0 1-.162-1.544 1.08 1.08 0 0 1 .754-.437 1.06 1.06 0 0 1 .81.258l6.244 5.455a1.156 1.156 0 0 1 .003 1.723l-6.218 5.808a1.07 1.07 0 0 1-.729.289Zm-9.31 0a1.07 1.07 0 0 1-.728-.292l-6.226-5.811a1.16 1.16 0 0 1-.01-1.692l.02-.018 6.246-5.454a1.03 1.03 0 0 1 .8-.26 1.08 1.08 0 0 1 .76.436 1.165 1.165 0 0 1-.16 1.547l-5.294 4.62 5.32 4.964a1.156 1.156 0 0 1 .112 1.548 1.07 1.07 0 0 1-.762.412Z'/></svg>",
+ "folder-development.clone": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M18.435 30a1 1 0 0 1-.238-.028 1.137 1.137 0 0 1-.828-1.323l3.093-15.744a1.13 1.13 0 0 1 .507-.744 1.06 1.06 0 0 1 .8-.134 1.14 1.14 0 0 1 .828 1.324l-3.1 15.744a1.12 1.12 0 0 1-.505.743 1.06 1.06 0 0 1-.557.162m6.2-2h-.077a1.08 1.08 0 0 1-.762-.412 1.164 1.164 0 0 1 .113-1.548l5.32-4.967-5.297-4.623a1.165 1.165 0 0 1-.162-1.544 1.08 1.08 0 0 1 .754-.437 1.06 1.06 0 0 1 .81.258l6.244 5.455a1.156 1.156 0 0 1 .004 1.723l-6.22 5.808a1.07 1.07 0 0 1-.728.289Zm-9.31 0a1.07 1.07 0 0 1-.728-.292l-6.225-5.811a1.16 1.16 0 0 1-.01-1.692l.02-.018 6.246-5.454a1.03 1.03 0 0 1 .8-.26 1.08 1.08 0 0 1 .758.436 1.165 1.165 0 0 1-.16 1.547l-5.293 4.62 5.32 4.964a1.156 1.156 0 0 1 .112 1.548 1.07 1.07 0 0 1-.762.412Z'/></svg>",
+ "folder-directive-open": "<svg viewBox='0 0 16 16'><path fill='#f44336' d='M14.484 6H4.72a1 1 0 0 0-.949.684L2 12V5h12a1 1 0 0 0-1-1H7.562a1 1 0 0 1-.64-.232l-.644-.536A1 1 0 0 0 5.638 3H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h11l2.403-5.606A1 1 0 0 0 14.483 6'/><g fill='#ffcdd2'><path d='m11.5 6.001-1.5 2h3z'/><path d='M11 7v2h1V7zm-1 3H8v1.001h2z'/><path d='m9 9-2 1.5L9 12zm2 3h1v2h-1Z'/><path d='m10 13 1.5 2 1.5-2Zm2.715-3v1.001H15v-1Z'/><path d='m14 9 2 1.5-2 1.5Z'/></g></svg>",
+ "folder-directive": "<svg viewBox='0 0 16 16'><path fill='#f44336' d='m6.922 3.768-.644-.536A1 1 0 0 0 5.638 3H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H7.562a1 1 0 0 1-.64-.232'/><g fill='#ffcdd2'><path d='m11.5 6.001-1.5 2h3z'/><path d='M11 7v2h1V7zm-1 3H8v1.001h2z'/><path d='m9 9-2 1.5L9 12zm2 3h1v2h-1Z'/><path d='m10 13 1.5 2 1.5-2Zm3-3v1.001h2v-1Z'/><path d='m14 9 2 1.5-2 1.5Z'/></g></svg>",
+ "folder-dist-open": "<svg viewBox='0 0 32 32'><path fill='#e57373' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M30 14h-4v-2l-2-2h-4l-2 2v2h-4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V16a2 2 0 0 0-2-2m-10 0v-2h4v2Z'/></svg>",
+ "folder-dist": "<svg viewBox='0 0 32 32'><path fill='#e57373' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M30 14h-4v-2l-2-2h-4l-2 2v2h-4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V16a2 2 0 0 0-2-2m-10 0v-2h4v2Z'/></svg>",
+ "folder-docker-open": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M14 16h4v4h-4zm-6 0h4v4H8zm12 0h4v4h-4zm0-6h4v4h-4z'/><path fill='#b3e5fc' d='M31.667 19.81a3.65 3.65 0 0 0-3.067-.377 4.6 4.6 0 0 0-1.667-2.945l-.333-.302-.267.378a4.44 4.44 0 0 0-.667 2.793 5 5 0 0 0 .233 1.101l-.62 1.283A5.1 5.1 0 0 1 23.6 22H6a15.3 15.3 0 0 0 .865 4.757l.267.528v.076a8.22 8.22 0 0 0 7.6 4.38c6 0 10.934-2.945 13.268-9.288a3.605 3.605 0 0 0 3.8-2.039l.2-.377ZM12 28a2 2 0 1 1 2-2 2 2 0 0 1-2 2'/></svg>",
+ "folder-docker": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M14 16h4v4h-4zm-6 0h4v4H8zm12 0h4v4h-4zm0-6h4v4h-4z'/><path fill='#b3e5fc' d='M31.667 19.81a3.65 3.65 0 0 0-3.067-.377 4.6 4.6 0 0 0-1.667-2.945l-.333-.302-.267.378a4.44 4.44 0 0 0-.667 2.793 5 5 0 0 0 .233 1.101l-.62 1.283A5.1 5.1 0 0 1 23.6 22H6a15.3 15.3 0 0 0 .865 4.757l.267.528v.076a8.22 8.22 0 0 0 7.6 4.38c6 0 10.934-2.945 13.268-9.288a3.605 3.605 0 0 0 3.8-2.039l.2-.377ZM12 28a2 2 0 1 1 2-2 2 2 0 0 1-2 2'/></svg>",
+ "folder-docs-open": "<svg viewBox='0 0 32 32'><path fill='#0277bd' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M24 10h-7a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V16Zm0 16h-6v-2h6Zm4-4H18v-2h10Zm-4.828-5.172V12L28 16.828Z'/></svg>",
+ "folder-docs": "<svg viewBox='0 0 32 32'><path fill='#0277bd' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M24 10h-7a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V16Zm0 16h-6v-2h6Zm4-4H18v-2h10Zm-4.828-5.172V12L28 16.828Z'/></svg>",
+ "folder-download-open": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c8e6c9' d='M26 10v6h4l-7 8-7-8h4v-6zm4 16v2H16v-2Z'/></svg>",
+ "folder-download": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c8e6c9' d='M26 10v6h4l-7 8-7-8h4v-6zm4 16v2H16v-2Z'/></svg>",
+ "folder-drizzle-open": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c8e6c9' d='m10.752 27.217 3.127-5.764a1.51 1.51 0 0 0-.573-2.034 1.45 1.45 0 0 0-1.995.586l-3.127 5.763a1.51 1.51 0 0 0 .573 2.034 1.44 1.44 0 0 0 .71.187 1.46 1.46 0 0 0 1.285-.772m10.305 0 3.127-5.764a1.51 1.51 0 0 0-.574-2.034 1.45 1.45 0 0 0-1.995.586l-3.127 5.763a1.51 1.51 0 0 0 .574 2.034 1.44 1.44 0 0 0 .709.187 1.46 1.46 0 0 0 1.286-.772m-2.896-5.21 3.326-5.76a1.507 1.507 0 0 0-.518-2.04 1.447 1.447 0 0 0-2.002.527l-3.326 5.76a1.51 1.51 0 0 0 .519 2.041 1.43 1.43 0 0 0 .74.206 1.46 1.46 0 0 0 1.261-.734m10.31 0 3.327-5.76a1.507 1.507 0 0 0-.518-2.04 1.447 1.447 0 0 0-2.002.527l-3.326 5.76a1.51 1.51 0 0 0 .518 2.041 1.43 1.43 0 0 0 .74.206 1.46 1.46 0 0 0 1.262-.734Z'/></svg>",
+ "folder-drizzle": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c8e6c9' d='m10.752 27.217 3.127-5.764a1.51 1.51 0 0 0-.573-2.034 1.45 1.45 0 0 0-1.995.586l-3.127 5.763a1.51 1.51 0 0 0 .573 2.034 1.44 1.44 0 0 0 .71.187 1.46 1.46 0 0 0 1.285-.772m10.305 0 3.127-5.764a1.51 1.51 0 0 0-.574-2.034 1.45 1.45 0 0 0-1.995.586l-3.127 5.763a1.51 1.51 0 0 0 .574 2.034 1.44 1.44 0 0 0 .709.187 1.46 1.46 0 0 0 1.286-.772m-2.896-5.21 3.326-5.76a1.507 1.507 0 0 0-.518-2.04 1.447 1.447 0 0 0-2.002.527l-3.326 5.76a1.51 1.51 0 0 0 .519 2.041 1.43 1.43 0 0 0 .74.206 1.46 1.46 0 0 0 1.261-.734m10.31 0 3.327-5.76a1.507 1.507 0 0 0-.518-2.04 1.447 1.447 0 0 0-2.002.527l-3.326 5.76a1.51 1.51 0 0 0 .518 2.041 1.43 1.43 0 0 0 .74.206 1.46 1.46 0 0 0 1.262-.734Z'/></svg>",
+ "folder-dump-open": "<svg viewBox='0 0 32 32'><path fill='#757575' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bdbdbd' d='M26 12H15a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1V16Zm-10 2h8v4h-8Zm6 12a3 3 0 1 1 3-3 2.996 2.996 0 0 1-3 3'/></svg>",
+ "folder-dump": "<svg viewBox='0 0 32 32'><path fill='#757575' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bdbdbd' d='M26 12H15a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1V16Zm-10 2h8v4h-8Zm6 12a3 3 0 1 1 3-3 2.996 2.996 0 0 1-3 3'/></svg>",
+ "folder-element-open": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='M29 12H9.4a2 2 0 0 0-1.9 1.4L4 24V10h24a2 2 0 0 0-2-2H15.1a2 2 0 0 1-1.3-.5l-1.2-1a2 2 0 0 0-1.3-.5H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.8-11.2A2 2 0 0 0 29 12'/><path fill='#e1bee7' d='m21.15 25.74-8.3-5.2A1.8 1.8 0 0 1 12 19v-.98l9.15 5.72a1.6 1.6 0 0 0 1.7 0L32 18v1a1.8 1.8 0 0 1-.85 1.53l-8.3 5.21a1.6 1.6 0 0 1-1.7 0m0 4-8.3-5.2A1.8 1.8 0 0 1 12 23v-.98l9.15 5.72a1.6 1.6 0 0 0 1.7 0L32 22v1a1.8 1.8 0 0 1-.85 1.53l-8.3 5.21a1.6 1.6 0 0 1-1.7 0m-.08-19.46L12 16l9.07 5.73c.56.36 1.29.36 1.85 0L31.99 16l-9.07-5.72a1.68 1.68 0 0 0-1.85 0'/></svg>",
+ "folder-element": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='m13.84 7.54-1.28-1.08A2 2 0 0 0 11.28 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.12a2 2 0 0 1-1.28-.46'/><path fill='#e1bee7' d='m21.15 25.74-8.3-5.2A1.8 1.8 0 0 1 12 19v-.98l9.15 5.72a1.6 1.6 0 0 0 1.7 0L32 18v1a1.8 1.8 0 0 1-.85 1.53l-8.3 5.21a1.6 1.6 0 0 1-1.7 0m0 4-8.3-5.2A1.8 1.8 0 0 1 12 23v-.98l9.15 5.72a1.6 1.6 0 0 0 1.7 0L32 22v1a1.8 1.8 0 0 1-.85 1.53l-8.3 5.21a1.6 1.6 0 0 1-1.7 0m-.08-19.46L12 16l9.07 5.72c.56.37 1.29.37 1.85 0L31.99 16l-9.07-5.72a1.68 1.68 0 0 0-1.85 0'/></svg>",
+ "folder-enum-open": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2dfdb' d='M16 12v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V12a2 2 0 0 0-2-2H18a2 2 0 0 0-2 2m14 14H18v-4h12Zm0-6H18v-4h12Z'/></svg>",
+ "folder-enum": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2dfdb' d='M16 12v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V12a2 2 0 0 0-2-2H18a2 2 0 0 0-2 2m14 14H18v-4h12Zm0-6H18v-4h12Z'/></svg>",
+ "folder-environment-open": "<svg viewBox='0 0 32 32'><path fill='#66bb6a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c8e6c9' d='m23 16-3.749 5 2.85 3.798L20.5 26l-4.499-6L10 28h22Z'/></svg>",
+ "folder-environment": "<svg viewBox='0 0 32 32'><path fill='#66bb6a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c8e6c9' d='m23 16-3.749 5 2.85 3.798L20.5 26l-4.499-6L10 28h22Z'/></svg>",
+ "folder-error-open": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M25 14a7 7 0 1 0 7 7 7 7 0 0 0-7-7m1 2v6h-2v-6Zm-2 10v-2h2v2Z'/></svg>",
+ "folder-error": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M25 14a7 7 0 1 0 7 7 7 7 0 0 0-7-7m1 2v6h-2v-6Zm-2 10v-2h2v2Z'/></svg>",
+ "folder-event-open": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fff9c4' d='M24 30a2 2 0 0 0 2-2h-4a2 2 0 0 0 2 2m6-8v-5a5 5 0 0 0-4-4.9V12a2 2 0 0 0-4 0v.1a5 5 0 0 0-4 4.9v5l-2 2v2h16v-2Z'/></svg>",
+ "folder-event": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fff9c4' d='M24 30a2 2 0 0 0 2-2h-4a2 2 0 0 0 2 2m6-8v-5a5 5 0 0 0-4-4.9V12a2 2 0 0 0-4 0v.1a5 5 0 0 0-4 4.9v5l-2 2v2h16v-2Z'/></svg>",
+ "folder-examples-open": "<svg viewBox='0 0 32 32'><path fill='#009688' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2dfdb' d='M16 14v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V14a2 2 0 0 0-2-2H18a2 2 0 0 0-2 2m2 0h2a2 2 0 0 1-2 2Zm0 4a4 4 0 0 0 4-4h2a6.005 6.005 0 0 1-6 6Zm0 8 4-4 1.6 1.6L26 20l4 6Z'/></svg>",
+ "folder-examples": "<svg viewBox='0 0 32 32'><path fill='#009688' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2dfdb' d='M16 14v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V14a2 2 0 0 0-2-2H18a2 2 0 0 0-2 2m2 0h2a2 2 0 0 1-2 2Zm0 4a4 4 0 0 0 4-4h2a6.005 6.005 0 0 1-6 6Zm0 8 4-4 1.6 1.6L26 20l4 6Z'/></svg>",
+ "folder-expo-open": "<svg viewBox='0 0 32 32'><path fill='#1976d2' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M25.182 13.148c-.663-1.013-.82-1.148-2.17-1.148h-.032c-1.35 0-1.499.135-2.17 1.148C20.187 14.1 14 25.473 14 25.79a2.5 2.5 0 0 0 .545 1.513c.434.626 1.183.974 1.728.42.37-.373 4.34-7.24 6.257-9.837a.575.575 0 0 1 .94 0c1.916 2.597 5.887 9.464 6.257 9.837.545.554 1.294.204 1.728-.42A2.5 2.5 0 0 0 32 25.79c-.008-.317-6.195-11.699-6.818-12.642'/></svg>",
+ "folder-expo": "<svg viewBox='0 0 32 32'><path fill='#1976d2' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M25.182 13.148c-.663-1.013-.82-1.148-2.17-1.148h-.032c-1.35 0-1.499.135-2.17 1.148C20.187 14.1 14 25.473 14 25.79a2.5 2.5 0 0 0 .545 1.513c.434.626 1.183.974 1.728.42.37-.373 4.34-7.24 6.257-9.837a.575.575 0 0 1 .94 0c1.916 2.597 5.887 9.464 6.257 9.837.545.554 1.294.204 1.728-.42A2.5 2.5 0 0 0 32 25.79c-.008-.317-6.195-11.699-6.818-12.642'/></svg>",
+ "folder-export-open": "<svg viewBox='0 0 32 32'><path fill='#ff8a65' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#efebe9' fill-opacity='.949' d='M25 14a7 7 0 1 0 7 7 7 7 0 0 0-7-7m-5 8v-2h10v2Z'/></svg>",
+ "folder-export": "<svg viewBox='0 0 32 32'><path fill='#ff8a65' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#efebe9' fill-opacity='.949' d='M25 14a7 7 0 1 0 7 7 7 7 0 0 0-7-7m-5 8v-2h10v2Z'/></svg>",
+ "folder-fastlane-open": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#e3f2fd' d='m15.508 21.618 1.272 3.936a1.456 1.456 0 0 0-.06 1.922 1.35 1.35 0 0 0 .937.486l.1.003a1.33 1.33 0 0 0 .894-.346 1.4 1.4 0 0 0 .207-.231 1.446 1.446 0 0 0-.307-1.972 1.36 1.36 0 0 0-.597-.256l-1.586-5.037a.16.16 0 0 0-.092-.101.15.15 0 0 0-.134.007 1.92 1.92 0 0 1-2.059-.113 1.995 1.995 0 0 1-.423-2.72 1.84 1.84 0 0 1 2.088-.697.16.16 0 0 0 .199-.102l.325-.95a.17.17 0 0 0-.007-.128.16.16 0 0 0-.093-.084 3 3 0 0 0-.978-.167h-.039A3.215 3.215 0 0 0 12 18.296a3.3 3.3 0 0 0 1.321 2.698 3.08 3.08 0 0 0 2.187.624'/><path fill='#e3f2fd' d='M15.802 17.146a1.35 1.35 0 0 0-1.787.535 1.45 1.45 0 0 0-.157 1.074 1.4 1.4 0 0 0 .622.875 1.33 1.33 0 0 0 .706.203 1.36 1.36 0 0 0 1.176-.685 1.47 1.47 0 0 0 .177-.971l4.14-3.149a.17.17 0 0 0 .065-.122.17.17 0 0 0-.05-.13 2.09 2.09 0 0 1-.556-2.063 1.883 1.883 0 0 1 2.383-1.262 1.95 1.95 0 0 1 1.31 1.823.16.16 0 0 0 .155.161l.983.02a.2.2 0 0 0 .114-.047.17.17 0 0 0 .048-.117 3.34 3.34 0 0 0-.945-2.333A3.12 3.12 0 0 0 21.939 10a4 4 0 0 0-.293.014 3.14 3.14 0 0 0-2.162 1.182 3.4 3.4 0 0 0-.457 3.457Zm10.842 10.755a1.4 1.4 0 0 0 .736-.77 1.45 1.45 0 0 0-.005-1.083 1.38 1.38 0 0 0-.744-.763 1.3 1.3 0 0 0-1.047.005 1.4 1.4 0 0 0-.693.672l-5.12.014a.16.16 0 0 0-.123.06.17.17 0 0 0-.033.135 2.08 2.08 0 0 1-.724 2 1.82 1.82 0 0 1-1.4.355 1.86 1.86 0 0 1-1.233-.773 2 2 0 0 1-.016-2.286.17.17 0 0 0-.033-.226l-.778-.613a.15.15 0 0 0-.12-.032.16.16 0 0 0-.105.065 3.36 3.36 0 0 0-.575 2.45 3.3 3.3 0 0 0 1.266 2.153 3.1 3.1 0 0 0 1.874.634 3.15 3.15 0 0 0 2.572-1.349 3.4 3.4 0 0 0 .545-1.264l4.01-.04a1.35 1.35 0 0 0 1.746.656'/><path fill='#e3f2fd' d='m27.718 23.882 1.228-3.945a1.34 1.34 0 0 0 .824-.473 1.44 1.44 0 0 0 .328-1.026 1.366 1.366 0 0 0-2.729-.013 1.5 1.5 0 0 0 .06.553 1.4 1.4 0 0 0 .336.564l-1.603 5.027a.17.17 0 0 0 .016.14.16.16 0 0 0 .115.075 1.952 1.952 0 0 1 .385 3.782 1.84 1.84 0 0 1-2.097-.692.156.156 0 0 0-.217-.037l-.811.572a.16.16 0 0 0-.067.108.17.17 0 0 0 .028.124A3.15 3.15 0 0 0 26.097 30a3.1 3.1 0 0 0 1.871-.63 3.36 3.36 0 0 0 1.165-3.667 3.23 3.23 0 0 0-1.415-1.82Z'/><path fill='#e3f2fd' d='M31.845 17.545a3.15 3.15 0 0 0-5.177-1.459l-3.28-2.432a1.46 1.46 0 0 0-.193-.969 1.37 1.37 0 0 0-.857-.637 1.3 1.3 0 0 0-.308-.038h-.004a1.425 1.425 0 0 0-.003 2.847h.005a1.3 1.3 0 0 0 .631-.16l4.165 3.137a.16.16 0 0 0 .133.027.16.16 0 0 0 .104-.089 1.98 1.98 0 0 1 1.735-1.178h.001a1.933 1.933 0 0 1 1.897 1.963 1.96 1.96 0 0 1-1.296 1.863.166.166 0 0 0-.102.203l.28.98a.16.16 0 0 0 .079.098.15.15 0 0 0 .123.011 3.2 3.2 0 0 0 1.867-1.64 3.4 3.4 0 0 0 .2-2.527'/></svg>",
+ "folder-fastlane": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#e3f2fd' d='m15.508 21.618 1.272 3.936a1.456 1.456 0 0 0-.06 1.922 1.35 1.35 0 0 0 .937.486l.1.003a1.33 1.33 0 0 0 .894-.346 1.4 1.4 0 0 0 .207-.231 1.446 1.446 0 0 0-.307-1.972 1.36 1.36 0 0 0-.597-.256l-1.586-5.037a.16.16 0 0 0-.092-.101.15.15 0 0 0-.134.007 1.92 1.92 0 0 1-2.059-.113 1.995 1.995 0 0 1-.423-2.72 1.84 1.84 0 0 1 2.088-.697.16.16 0 0 0 .199-.102l.325-.95a.17.17 0 0 0-.007-.128.16.16 0 0 0-.093-.084 3 3 0 0 0-.978-.167h-.039A3.215 3.215 0 0 0 12 18.296a3.3 3.3 0 0 0 1.321 2.698 3.08 3.08 0 0 0 2.187.624'/><path fill='#e3f2fd' d='M15.802 17.146a1.35 1.35 0 0 0-1.787.535 1.45 1.45 0 0 0-.157 1.074 1.4 1.4 0 0 0 .622.875 1.33 1.33 0 0 0 .706.203 1.36 1.36 0 0 0 1.176-.685 1.47 1.47 0 0 0 .177-.971l4.14-3.149a.17.17 0 0 0 .065-.122.17.17 0 0 0-.05-.13 2.09 2.09 0 0 1-.556-2.063 1.883 1.883 0 0 1 2.383-1.262 1.95 1.95 0 0 1 1.31 1.823.16.16 0 0 0 .155.161l.983.02a.2.2 0 0 0 .114-.047.17.17 0 0 0 .048-.117 3.34 3.34 0 0 0-.945-2.333A3.12 3.12 0 0 0 21.939 10a4 4 0 0 0-.293.014 3.14 3.14 0 0 0-2.162 1.182 3.4 3.4 0 0 0-.457 3.457Zm10.842 10.755a1.4 1.4 0 0 0 .736-.77 1.45 1.45 0 0 0-.005-1.083 1.38 1.38 0 0 0-.744-.763 1.3 1.3 0 0 0-1.047.005 1.4 1.4 0 0 0-.693.672l-5.12.014a.16.16 0 0 0-.123.06.17.17 0 0 0-.033.135 2.08 2.08 0 0 1-.724 2 1.82 1.82 0 0 1-1.4.355 1.86 1.86 0 0 1-1.233-.773 2 2 0 0 1-.016-2.286.17.17 0 0 0-.033-.226l-.778-.613a.15.15 0 0 0-.12-.032.16.16 0 0 0-.105.065 3.36 3.36 0 0 0-.575 2.45 3.3 3.3 0 0 0 1.266 2.153 3.1 3.1 0 0 0 1.874.634 3.15 3.15 0 0 0 2.572-1.349 3.4 3.4 0 0 0 .545-1.264l4.01-.04a1.35 1.35 0 0 0 1.746.656'/><path fill='#e3f2fd' d='m27.718 23.882 1.228-3.945a1.34 1.34 0 0 0 .824-.473 1.44 1.44 0 0 0 .328-1.026 1.366 1.366 0 0 0-2.729-.013 1.5 1.5 0 0 0 .06.553 1.4 1.4 0 0 0 .336.564l-1.603 5.027a.17.17 0 0 0 .016.14.16.16 0 0 0 .115.075 1.952 1.952 0 0 1 .385 3.782 1.84 1.84 0 0 1-2.097-.692.156.156 0 0 0-.217-.037l-.811.572a.16.16 0 0 0-.067.108.17.17 0 0 0 .028.124A3.15 3.15 0 0 0 26.097 30a3.1 3.1 0 0 0 1.871-.63 3.36 3.36 0 0 0 1.165-3.667 3.23 3.23 0 0 0-1.415-1.82Z'/><path fill='#e3f2fd' d='M31.845 17.545a3.15 3.15 0 0 0-5.177-1.459l-3.28-2.432a1.46 1.46 0 0 0-.193-.969 1.37 1.37 0 0 0-.857-.637 1.3 1.3 0 0 0-.308-.038h-.004a1.425 1.425 0 0 0-.003 2.847h.005a1.3 1.3 0 0 0 .631-.16l4.165 3.137a.16.16 0 0 0 .133.027.16.16 0 0 0 .104-.089 1.98 1.98 0 0 1 1.735-1.178h.001a1.933 1.933 0 0 1 1.897 1.963 1.96 1.96 0 0 1-1.296 1.863.166.166 0 0 0-.102.203l.28.98a.16.16 0 0 0 .079.098.15.15 0 0 0 .123.011 3.2 3.2 0 0 0 1.867-1.64 3.4 3.4 0 0 0 .2-2.527'/></svg>",
+ "folder-favicon-open": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124c-.468 0-.921-.164-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fffde7' d='m24 24 6 4-2-6 4-4h-6l-1.999-6L22 18h-6l4 4-2 6z'/></svg>",
+ "folder-favicon": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fffde7' d='m24 24 6 4-2-6 4-4h-6l-1.999-6L22 18h-6l4 4-2 6z'/></svg>",
+ "folder-firebase-open": "<svg viewBox='0 0 32 32'><path fill='#ff9100' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffe0b2' d='M24 28.014s-4.584.213-7-4.014c-.66-1.156-1.006-2.805-1-4 .012-2.264.962-3.881 1.038-4 .117-.181 2.954-.867 4.962 0s3.979 3.215 4 6-2.275 4.565-2.691 4.881.691 1.133.691 1.133M22 25.5c2.051-1.646 1.875-2.063 2-3.5s-1.007-3.071-2-4-2.934-.65-3.5-.5c-2.418 5.405 3.5 8 3.5 8'/><path fill='#ffe0b2' d='m24 28.014 2.527-.941s-1.988-1.265-2.909-2.168C21.381 22.71 20.021 20.085 20 16s4-8 4-8 8.063 6.276 8 12c-.644 8.183-8 8.014-8 8.014m4-3.023c1.044-1.135 1.95-2.042 2-4.991.075-4.381-6-9.5-6-9.5s-1.856 2.393-2 5.5c-.338 7.273 6 8.991 6 8.991'/><path fill='#ffe0b2' d='M22.34 25.64s3.453-.086 5.66-.649c3.451-.879-1.022 2.191-1.022 2.191L24 28.015l-1.313-.536z'/></svg>",
+ "folder-firebase": "<svg fill-rule='evenodd' stroke-linejoin='round' stroke-miterlimit='2' clip-rule='evenodd' viewBox='0 0 32 32'><path fill='#ff9100' fill-rule='nonzero' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4c-1.097 0-2 .903-2 2v16c0 1.097.903 2 2 2h24c1.097 0 2-.903 2-2V10c0-1.097-.903-2-2-2H15.124c-.468 0-.921-.164-1.28-.464'/><path fill='#ffe0b2' d='M24 28.014s-4.584.213-7-4.014c-.66-1.156-1.006-2.805-1-4 .012-2.264.962-3.881 1.038-4 .117-.181 2.954-.867 4.962 0s3.979 3.215 4 6-2.275 4.565-2.691 4.881.691 1.133.691 1.133M22 25.5c2.051-1.646 1.875-2.063 2-3.5s-1.007-3.071-2-4-2.934-.65-3.5-.5c-2.418 5.405 3.5 8 3.5 8'/><path fill='#ffe0b2' d='m24 28.014 2.527-.941s-1.988-1.265-2.909-2.168C21.381 22.71 20.021 20.085 20 16s4-8 4-8 8.063 6.276 8 12c-.644 8.183-8 8.014-8 8.014m4-3.023c1.044-1.135 1.95-2.042 2-4.991.075-4.381-6-9.5-6-9.5s-1.856 2.393-2 5.5c-.338 7.273 6 8.991 6 8.991'/><path fill='#ffe0b2' d='M22.34 25.64s3.453-.086 5.66-.649c3.451-.879-1.022 2.191-1.022 2.191L24 28.015l-1.313-.536z'/></svg>",
+ "folder-firestore-open": "<svg fill-rule='evenodd' stroke-linejoin='round' stroke-miterlimit='2' clip-rule='evenodd' viewBox='0 0 32 32'><path fill='#2196f3' fill-rule='nonzero' d='M28.967 12H9.442c-.859 0-1.627.553-1.898 1.368L4 24V10h24c0-1.097-.903-2-2-2H15.124c-.468 0-.921-.164-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4c-1.097 0-2 .903-2 2v16c0 1.097.903 2 2 2h22l4.805-11.212c.107-.249.162-.517.162-.788 0-1.097-.903-2-2-2'/><path fill='#bbdefb' d='m24 10-8 4v4l8-4 8 4v-4z'/><path fill='#bbdefb' d='M16 20v4l8-4 8 4v-4l-8-4z'/><path fill='#bbdefb' d='m24 24 3-1 4 2-7 3z'/></svg>",
+ "folder-firestore": "<svg fill-rule='evenodd' stroke-linejoin='round' stroke-miterlimit='2' clip-rule='evenodd' viewBox='0 0 32 32'><path fill='#2196f3' fill-rule='nonzero' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4c-1.097 0-2 .903-2 2v16c0 1.097.903 2 2 2h24c1.097 0 2-.903 2-2V10c0-1.097-.903-2-2-2H15.124c-.468 0-.921-.164-1.28-.464'/><path fill='#bbdefb' d='m24 10-8 4v4l8-4 8 4v-4z'/><path fill='#bbdefb' d='M16 20v4l8-4 8 4v-4l-8-4z'/><path fill='#bbdefb' d='m24 24 3-1 4 2-7 3z'/></svg>",
+ "folder-flow-open": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fbc02d' fill-opacity='.976' d='m12 10 6.71 6h-4.725l4.672 4h-6.676L20 28.025V12h3.548l1.584 2H22v6h1.98l1.979 2h-3.984l.025.025V28h10l-4-3.033v-1.221l.657.655L28 18h-2v-2.01l4 1.997v-5.99l-6-.999L21.923 10Z'/></svg>",
+ "folder-flow": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fbc02d' fill-opacity='.976' d='m12 10 6.71 6h-4.725l4.672 4h-6.676L20 28.025V12h3.548l1.584 2H22v6h1.98l1.979 2h-3.984l.025.025V28h10l-4-3.033v-1.221l.657.655L28 18h-2v-2.01l4 1.997v-5.99l-6-.999L21.923 10Z'/></svg>",
+ "folder-flutter-open": "<svg viewBox='0 0 32 32'><path fill='#03a9f4' d='M29 12H9.4a2 2 0 0 0-1.9 1.4L4 24V10h24a2 2 0 0 0-2-2H15.1a2 2 0 0 1-1.3-.5l-1.2-1a2 2 0 0 0-1.3-.5H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.8-11.2A2 2 0 0 0 29 12'/><path fill='#b3e5fc' d='m20 10-8 8 4 4 12-12zm4 8-6 6 6 6h8l-6-6 6-6z'/></svg>",
+ "folder-flutter": "<svg viewBox='0 0 32 32'><path fill='#03a9f4' d='m13.8 7.5-1.2-1a2 2 0 0 0-1.3-.5H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.1a2 2 0 0 1-1.3-.5'/><path fill='#b3e5fc' d='m20 10-8 8 4 4 12-12zm4 8-6 6 6 6h8l-6-6 6-6z'/></svg>",
+ "folder-font-open": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M24.077 12h-2.154L16 28h2.423L20 24h6l1.577 4H30Zm-3.64 10L23 14.764 25.552 22Z'/></svg>",
+ "folder-font": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M24.077 12h-2.154L16 28h2.423L20 24h6l1.577 4H30Zm-3.64 10L23 14.764 25.552 22Z'/></svg>",
+ "folder-forgejo-open": "<svg viewBox='0 0 32 32'><path fill='#757575' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><g fill='none' transform='translate(14.53 10.455)scale(.08531)'><path stroke='#ff6d00' stroke-width='25' d='M58 168V70a50 50 0 0 1 50-50h20' class='prefix__prefix__orange'/><path stroke='#d50000' stroke-width='25' d='M58 168v-30a50 50 0 0 1 50-50h20' class='prefix__prefix__red'/><circle cx='142' cy='20' r='18' stroke='#ff6d00' stroke-width='15' class='prefix__prefix__orange'/><circle cx='142' cy='88' r='18' stroke='#d50000' stroke-width='15' class='prefix__prefix__red'/><circle cx='58' cy='180' r='18' stroke='#d50000' stroke-width='15' class='prefix__prefix__red'/></g></svg>",
+ "folder-forgejo": "<svg viewBox='0 0 32 32'><path fill='#757575' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><g fill='none' transform='translate(14.53 10.455)scale(.08531)'><path stroke='#ff6d00' stroke-width='25' d='M58 168V70a50 50 0 0 1 50-50h20' class='prefix__prefix__orange'/><path stroke='#d50000' stroke-width='25' d='M58 168v-30a50 50 0 0 1 50-50h20' class='prefix__prefix__red'/><circle cx='142' cy='20' r='18' stroke='#ff6d00' stroke-width='15' class='prefix__prefix__orange'/><circle cx='142' cy='88' r='18' stroke='#d50000' stroke-width='15' class='prefix__prefix__red'/><circle cx='58' cy='180' r='18' stroke='#d50000' stroke-width='15' class='prefix__prefix__red'/></g></svg>",
+ "folder-functions-open": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M24 16h-2.982l.14-1.676.002-.01a1.945 1.945 0 0 1 3.848-.485l1.475-1.687a3.9 3.9 0 0 0-3.01-2.126 4.143 4.143 0 0 0-4.263 4.105L19.048 16H16v2h2.87l-.529 5.874a2.05 2.05 0 0 1-1.348 1.776l-.026.009a1.92 1.92 0 0 1-2.451-1.465l-1.477 1.687a3.91 3.91 0 0 0 2.99 2.1 4.13 4.13 0 0 0 4.274-4.08L20.839 18H24Zm8 4.929-1.414-1.414-2.829 2.828-2.828-2.828-1.414 1.414 2.828 2.828-2.828 2.829L24.929 28l2.828-2.828L30.586 28 32 26.586l-2.828-2.829z'/></svg>",
+ "folder-functions": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M24 16h-2.982l.14-1.676.002-.01a1.945 1.945 0 0 1 3.848-.485l1.475-1.687a3.9 3.9 0 0 0-3.01-2.126 4.143 4.143 0 0 0-4.263 4.105L19.048 16H16v2h2.87l-.529 5.874a2.05 2.05 0 0 1-1.348 1.776l-.026.009a1.92 1.92 0 0 1-2.451-1.465l-1.477 1.687a3.91 3.91 0 0 0 2.99 2.1 4.13 4.13 0 0 0 4.274-4.08L20.839 18H24Zm8 4.929-1.414-1.414-2.829 2.828-2.828-2.828-1.414 1.414 2.828 2.828-2.828 2.829L24.929 28l2.828-2.828L30.586 28 32 26.586l-2.828-2.829z'/></svg>",
+ "folder-gamemaker-open": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2dfdb' d='m32 20-9.03-9.03L13.942 20l9.03 9.03 3.765-3.766.007-5.264Zm-9.513 2.526L19.96 20l3.01-3.01L25.98 20h-3.494Z'/></svg>",
+ "folder-gamemaker": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2dfdb' d='m32 20-9.03-9.03L13.942 20l9.03 9.03 3.765-3.766.007-5.264Zm-9.513 2.526L19.96 20l3.01-3.01L25.98 20h-3.494Z'/></svg>",
+ "folder-generator-open": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M23.998 12.978v2.986l4-3.981-4-3.983v2.986A7.98 7.98 0 0 0 16 18.95v.002a7.9 7.9 0 0 0 1.239 4.24l1.46-1.455a5.86 5.86 0 0 1-.7-2.785 5.987 5.987 0 0 1 6-5.974m6.759 1.732-1.46 1.454a5.968 5.968 0 0 1-5.3 8.76v-2.985l-4 3.983 4 3.981v-2.986a7.98 7.98 0 0 0 7.999-7.964v-.001a7.87 7.87 0 0 0-1.24-4.24Z'/></svg>",
+ "folder-generator": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M23.998 12.978v2.986l4-3.981-4-3.983v2.986A7.98 7.98 0 0 0 16 18.95v.002a7.9 7.9 0 0 0 1.239 4.24l1.46-1.455a5.86 5.86 0 0 1-.7-2.785 5.987 5.987 0 0 1 6-5.974m6.759 1.732-1.46 1.454a5.968 5.968 0 0 1-5.3 8.76v-2.985l-4 3.983 4 3.981v-2.986a7.98 7.98 0 0 0 7.999-7.964v-.001a7.87 7.87 0 0 0-1.24-4.24Z'/></svg>",
+ "folder-gh-workflows-open": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#eceff1' d='M24 22v2h-2a2 2 0 0 1-2-2v-4h2v-6h-6v6h2v4a4.004 4.004 0 0 0 4 4h2v2h6v-6Zm-6-6v-2h2v2Zm10 10h-2v-2h2Z'/></svg>",
+ "folder-gh-workflows": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#eceff1' d='M24 22v2h-2a2 2 0 0 1-2-2v-4h2v-6h-6v6h2v4a4.004 4.004 0 0 0 4 4h2v2h6v-6Zm-6-6v-2h2v2Zm10 10h-2v-2h2Z'/></svg>",
+ "folder-git-open": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffccbc' d='m12.593 18.589 5.784-5.794 1.692 1.7a1.98 1.98 0 0 0 .93 2.233v5.544a1.99 1.99 0 0 0-1 1.731 2.002 2.002 0 0 0 4.003 0A1.99 1.99 0 0 0 23 22.272v-4.864l2.071 2.092a1.2 1.2 0 0 0-.07.5 2.002 2.002 0 1 0 2.002-2.002 1.2 1.2 0 0 0-.5.07l-2.573-2.571a1.98 1.98 0 0 0-1.15-2.342 2.1 2.1 0 0 0-1.281-.09l-1.702-1.692.791-.78a1.975 1.975 0 0 1 2.822 0l7.996 7.996a1.975 1.975 0 0 1 0 2.822l-7.996 7.996a1.975 1.975 0 0 1-2.822 0l-7.996-7.996a1.975 1.975 0 0 1 0-2.822Z'/></svg>",
+ "folder-git": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffccbc' d='m12.593 18.589 5.784-5.794 1.692 1.7a1.98 1.98 0 0 0 .93 2.233v5.544a1.99 1.99 0 0 0-1 1.731 2.002 2.002 0 0 0 4.003 0A1.99 1.99 0 0 0 23 22.272v-4.864l2.071 2.092a1.2 1.2 0 0 0-.07.5 2.002 2.002 0 1 0 2.002-2.002 1.2 1.2 0 0 0-.5.07l-2.573-2.571a1.98 1.98 0 0 0-1.15-2.342 2.1 2.1 0 0 0-1.281-.09l-1.702-1.692.791-.78a1.975 1.975 0 0 1 2.822 0l7.996 7.996a1.975 1.975 0 0 1 0 2.822l-7.996 7.996a1.975 1.975 0 0 1-2.822 0l-7.996-7.996a1.975 1.975 0 0 1 0-2.822Z'/></svg>",
+ "folder-gitea-open": "<svg viewBox='0 0 32 32'><path fill='#689f38' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#dcedc8' d='M24 14v4h1.5a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5h-5a.5.5 0 0 1-.5-.5v-5a.5.5 0 0 1 .5-.5H22v-4H12a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h3.202l.3 2A4.55 4.55 0 0 0 20 28h6a4.55 4.55 0 0 0 4.497-4L32 14Zm-12 6v-4h2.3l.602 4Z'/></svg>",
+ "folder-gitea": "<svg viewBox='0 0 32 32'><path fill='#689f38' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#dcedc8' d='M24 14v4h1.5a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5h-5a.5.5 0 0 1-.5-.5v-5a.5.5 0 0 1 .5-.5H22v-4H12a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h3.202l.3 2A4.55 4.55 0 0 0 20 28h6a4.55 4.55 0 0 0 4.497-4L32 14Zm-12 6v-4h2.3l.602 4Z'/></svg>",
+ "folder-github-open": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#eceff1' d='M23 10a9.03 9.03 0 0 0-9 9.063 9.08 9.08 0 0 0 6.157 8.609c.45.072.593-.21.593-.453v-1.532c-2.493.544-3.024-1.214-3.024-1.214a2.42 2.42 0 0 0-.998-1.333c-.82-.561.062-.544.062-.544a1.9 1.9 0 0 1 1.377.933 1.925 1.925 0 0 0 2.62.754 1.96 1.96 0 0 1 .566-1.215c-1.998-.227-4.094-1.007-4.094-4.459a3.52 3.52 0 0 1 .927-2.456 3.26 3.26 0 0 1 .09-2.392s.754-.245 2.474.924a8.6 8.6 0 0 1 4.5 0c1.718-1.169 2.476-.924 2.476-.924a3.26 3.26 0 0 1 .088 2.392 3.52 3.52 0 0 1 .927 2.456c0 3.462-2.105 4.223-4.112 4.45a2.17 2.17 0 0 1 .622 1.676v2.484c0 .244.143.533.602.453A9.08 9.08 0 0 0 23 10'/></svg>",
+ "folder-github": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#eceff1' d='M23 10a9.03 9.03 0 0 0-9 9.063 9.08 9.08 0 0 0 6.157 8.609c.45.072.593-.21.593-.453v-1.532c-2.493.544-3.024-1.214-3.024-1.214a2.42 2.42 0 0 0-.998-1.333c-.82-.561.062-.544.062-.544a1.9 1.9 0 0 1 1.377.933 1.925 1.925 0 0 0 2.62.754 1.96 1.96 0 0 1 .566-1.215c-1.998-.227-4.094-1.007-4.094-4.459a3.52 3.52 0 0 1 .927-2.456 3.26 3.26 0 0 1 .09-2.392s.754-.245 2.474.924a8.6 8.6 0 0 1 4.5 0c1.718-1.169 2.476-.924 2.476-.924a3.26 3.26 0 0 1 .088 2.392 3.52 3.52 0 0 1 .927 2.456c0 3.462-2.105 4.223-4.112 4.45a2.17 2.17 0 0 1 .622 1.676v2.484c0 .244.143.533.602.453A9.08 9.08 0 0 0 23 10'/></svg>",
+ "folder-gitlab-open": "<svg viewBox='0 0 32 32'><path fill='#757575' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><g data-mit-no-recolor='true'><path fill='#e53935' d='m31.35 17.703-.022-.063-2.339-6.097a.6.6 0 0 0-.24-.291.617.617 0 0 0-.928.353l-1.577 4.831h-6.39l-1.58-4.83a.62.62 0 0 0-.926-.354.6.6 0 0 0-.24.29l-2.337 6.1-.024.06a4.34 4.34 0 0 0 1.44 5.017l.009.006.02.015 3.556 2.667 1.764 1.333 1.072.81a.724.724 0 0 0 .873 0l1.072-.811 1.764-1.334 3.583-2.68.01-.008a4.34 4.34 0 0 0 1.44-5.014'/><path fill='#ef6c00' d='m31.35 17.703-.022-.063a7.9 7.9 0 0 0-3.143 1.413l-5.133 3.883 3.268 2.47 3.581-2.68.011-.009a4.34 4.34 0 0 0 1.44-5.014Z'/><path fill='#f9a825' d='m19.772 25.407 1.764 1.333 1.072.81a.724.724 0 0 0 .873 0l1.072-.81 1.766-1.333-3.27-2.471Z'/><path fill='#ef6c00' d='M17.912 19.053a7.9 7.9 0 0 0-3.141-1.412l-.024.062a4.34 4.34 0 0 0 1.44 5.016l.009.006.02.016 3.556 2.666 3.27-2.471Z'/></g></svg>",
+ "folder-gitlab": "<svg viewBox='0 0 32 32'><path fill='#757575' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><g data-mit-no-recolor='true'><path fill='#e53935' d='m31.35 17.703-.022-.063-2.339-6.097a.6.6 0 0 0-.24-.291.617.617 0 0 0-.928.353l-1.577 4.831h-6.39l-1.58-4.83a.62.62 0 0 0-.926-.354.6.6 0 0 0-.24.29l-2.337 6.1-.024.06a4.34 4.34 0 0 0 1.44 5.017l.009.006.02.015 3.556 2.667 1.764 1.333 1.072.81a.724.724 0 0 0 .873 0l1.072-.811 1.764-1.334 3.583-2.68.01-.008a4.34 4.34 0 0 0 1.44-5.014'/><path fill='#ef6c00' d='m31.35 17.703-.022-.063a7.9 7.9 0 0 0-3.143 1.413l-5.133 3.883 3.268 2.47 3.581-2.68.011-.009a4.34 4.34 0 0 0 1.44-5.014Z'/><path fill='#f9a825' d='m19.772 25.405 1.764 1.334 1.072.81a.724.724 0 0 0 .873 0l1.072-.81 1.766-1.334-3.27-2.469Z'/><path fill='#ef6c00' d='M17.912 19.053a7.9 7.9 0 0 0-3.141-1.412l-.024.062a4.34 4.34 0 0 0 1.44 5.016l.009.006.02.016 3.556 2.666 3.27-2.471Z'/></g></svg>",
+ "folder-global-open": "<svg viewBox='0 0 32 32'><path fill='#5c6bc0' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c5cae9' d='M22 10a10 10 0 1 0 10 10 10 10 0 0 0-10-10m8 10a8 8 0 0 1-.263 2H26l-2-2v-2h-4v-4h4v2h2v-2.918A8 8 0 0 1 30 20m-16 0a8 8 0 0 1 .123-1.347L18 20h4l2 2-2 2v4a8.01 8.01 0 0 1-8-8'/><path fill='#c5cae9' d='M26 17h2v1h-2z'/></svg>",
+ "folder-global": "<svg viewBox='0 0 32 32'><path fill='#5c6bc0' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c5cae9' d='M22 10a10 10 0 1 0 10 10 10 10 0 0 0-10-10m8 10a8 8 0 0 1-.263 2H26l-2-2v-2h-4v-4h4v2h2v-2.918A8 8 0 0 1 30 20m-16 0a8 8 0 0 1 .123-1.347L18 20h4l2 2-2 2v4a8.01 8.01 0 0 1-8-8'/><path fill='#c5cae9' d='M26 17h2v1h-2z'/></svg>",
+ "folder-godot-open": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M19.966 10a11.7 11.7 0 0 0-2.665.871 18 18 0 0 0 .166 2.093 11 11 0 0 0-.983.61 9 9 0 0 0-.914.717 18 18 0 0 0-1.779-.989A13 13 0 0 0 12 15.544a18 18 0 0 0 1.21 1.665h.011v5.056l.029.001 3.247.299a.34.34 0 0 1 .316.31l.1 1.367 2.833.192.195-1.262a.345.345 0 0 1 .346-.285h3.426a.346.346 0 0 1 .346.285l.195 1.262 2.833-.192.1-1.367a.345.345 0 0 1 .316-.31l3.246-.299.028-.001v-.404h.002v-4.65h.011A18 18 0 0 0 32 15.545a13 13 0 0 0-1.791-2.242 18 18 0 0 0-1.779.987 9 9 0 0 0-.914-.717 11 11 0 0 0-.982-.61 19 19 0 0 0 .166-2.093 11.8 11.8 0 0 0-2.666-.87 18 18 0 0 0-.99 1.84 7 7 0 0 0-1.037-.08h-.014a7 7 0 0 0-1.037.08 18 18 0 0 0-.99-1.84m-2.569 7.396a1.87 1.87 0 1 1 .11 3.736h-.004a1.87 1.87 0 1 1-.106-3.736m9.206 0a1.87 1.87 0 1 1 .026 3.736h-.025a1.87 1.87 0 1 1-.024-3.736zM22 18.488a.593.593 0 0 1 .63.547v1.72a.626.626 0 0 1-.69.543.62.62 0 0 1-.57-.544v-1.72a.59.59 0 0 1 .63-.546'/><path fill='#bbdefb' d='m27.863 23.203-.1 1.376a.34.34 0 0 1-.324.31l-3.459.234-.026.001a.345.345 0 0 1-.345-.285l-.198-1.284H20.59l-.199 1.284a.345.345 0 0 1-.371.284l-3.459-.236a.345.345 0 0 1-.324-.31l-.1-1.375-2.92-.269.005.693c0 2.944 3.912 4.358 8.772 4.374h.011c4.86-.016 8.772-1.43 8.772-4.374l.005-.693Z'/></svg>",
+ "folder-godot": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M19.966 10a11.7 11.7 0 0 0-2.665.871 18 18 0 0 0 .166 2.093 11 11 0 0 0-.983.61 9 9 0 0 0-.914.717 18 18 0 0 0-1.779-.989A13 13 0 0 0 12 15.544a18 18 0 0 0 1.21 1.665h.011v5.056l.029.001 3.247.299a.34.34 0 0 1 .316.31l.1 1.367 2.833.192.195-1.262a.345.345 0 0 1 .346-.285h3.426a.346.346 0 0 1 .346.285l.195 1.262 2.833-.192.1-1.367a.345.345 0 0 1 .316-.31l3.246-.299.028-.001v-.404h.002v-4.65h.011A18 18 0 0 0 32 15.545a13 13 0 0 0-1.791-2.242 18 18 0 0 0-1.779.987 9 9 0 0 0-.914-.717 11 11 0 0 0-.982-.61 19 19 0 0 0 .166-2.093 11.8 11.8 0 0 0-2.666-.87 18 18 0 0 0-.99 1.84 7 7 0 0 0-1.037-.08h-.014a7 7 0 0 0-1.037.08 18 18 0 0 0-.99-1.84m-2.569 7.396a1.87 1.87 0 1 1 .11 3.736h-.004a1.87 1.87 0 1 1-.106-3.736m9.206 0a1.87 1.87 0 1 1 .026 3.736h-.025a1.87 1.87 0 1 1-.024-3.736zM22 18.488a.593.593 0 0 1 .63.547v1.72a.626.626 0 0 1-.69.543.62.62 0 0 1-.57-.544v-1.72a.59.59 0 0 1 .63-.546'/><path fill='#bbdefb' d='m27.863 23.203-.1 1.376a.34.34 0 0 1-.324.31l-3.459.234-.026.001a.345.345 0 0 1-.345-.285l-.198-1.284H20.59l-.199 1.284a.345.345 0 0 1-.371.284l-3.459-.236a.345.345 0 0 1-.324-.31l-.1-1.375-2.92-.269.005.693c0 2.944 3.912 4.358 8.772 4.374h.011c4.86-.016 8.772-1.43 8.772-4.374l.005-.693Z'/></svg>",
+ "folder-gradle-open": "<svg viewBox='0 0 32 32'><path fill='#0097a7' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2ebf2' d='M31.4 13.692a3.3 3.3 0 0 0-2.869-1.691c-1.097-.022-1.998.592-1.828.996a6 6 0 0 0 .369.726c.183.269.51.061.624 0a1.84 1.84 0 0 1 1.124-.194 1.68 1.68 0 0 1 1.276.98c.832 1.618-1.736 4.945-4.95 2.641a8.34 8.34 0 0 0-7.754-1.077c-1.414.465-2.065.932-1.505 2.012a21 21 0 0 0 1.243 2.232c1.17 1.93 3.733-.888 3.733-.888-1.908 2.846-3.544 2.159-4.172 1.164a16 16 0 0 1-1.004-1.93C10.854 20.386 12.161 28 12.161 28h2.4c.611-2.803 2.8-2.699 3.174 0h1.831c1.621-5.475 5.727 0 5.727 0h2.387c-.67-3.732 1.342-4.907 2.61-7.095 1.268-2.19 2.469-4.868 1.11-7.213m-6.158 7.21a1.28 1.28 0 0 1-.845-1.589q.015-.05.034-.099s1.103.36 2.593.852a1.43 1.43 0 0 1-1.782.836'/></svg>",
+ "folder-gradle": "<svg viewBox='0 0 32 32'><path fill='#0097a7' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2ebf2' d='M31.4 13.692a3.3 3.3 0 0 0-2.869-1.691c-1.097-.022-1.998.592-1.828.996a6 6 0 0 0 .369.726c.183.269.51.061.624 0a1.84 1.84 0 0 1 1.124-.194 1.68 1.68 0 0 1 1.276.98c.832 1.618-1.736 4.945-4.95 2.641a8.34 8.34 0 0 0-7.754-1.077c-1.414.465-2.065.932-1.505 2.012a21 21 0 0 0 1.243 2.232c1.17 1.93 3.733-.888 3.733-.888-1.908 2.846-3.544 2.159-4.172 1.164a16 16 0 0 1-1.004-1.93C10.854 20.386 12.161 28 12.161 28h2.4c.611-2.803 2.8-2.699 3.174 0h1.831c1.621-5.475 5.727 0 5.727 0h2.387c-.67-3.732 1.342-4.907 2.61-7.095 1.268-2.19 2.469-4.868 1.11-7.213m-6.158 7.21a1.28 1.28 0 0 1-.845-1.589q.015-.05.034-.099s1.103.36 2.593.852a1.43 1.43 0 0 1-1.782.836'/></svg>",
+ "folder-graphql-open": "<svg viewBox='0 0 32 32'><path fill='#ec407a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f8bbd0' d='M22.995 10A1.99 1.99 0 0 0 21 11.984a2 2 0 0 0 .036.37l-3.734 2.14a2 2 0 0 0-.308-.221h-.003a2.01 2.01 0 0 0-2.72.761 1.948 1.948 0 0 0 1.07 2.825v4.277a1.975 1.975 0 0 0-1.228 2.52 2.007 2.007 0 0 0 2.884 1.063 2 2 0 0 0 .303-.218l3.736 2.135a1.998 1.998 0 1 0 3.96.376 2 2 0 0 0-.05-.431l3.7-2.12a2.015 2.015 0 0 0 2.828-.128 2 2 0 0 0 .251-.34h.003v-.002a1.97 1.97 0 0 0-.73-2.704l-.002-.001a2 2 0 0 0-.341-.15v-4.28a1.974 1.974 0 0 0 1.236-2.516 2.006 2.006 0 0 0-2.886-1.067H29a2 2 0 0 0-.31.22l-3.732-2.132a1.985 1.985 0 0 0-1.586-2.324 2 2 0 0 0-.376-.036Zm-1.802 3.78-4.534 7.777v-3.713a1.934 1.934 0 0 0 1.288-2.208Zm3.616.005 3.234 1.854a1.94 1.94 0 0 0 1.292 2.205v3.707Zm-2.178.14a1.9 1.9 0 0 0 .734 0l5.12 8.787a1.85 1.85 0 0 0-.374.639H17.882a2 2 0 0 0-.153-.337l-.002-.003a2 2 0 0 0-.217-.296ZM18.472 24.66h9.144l-3.293 1.883a1.974 1.974 0 0 0-2.618-.03l-3.233-1.85Z'/></svg>",
+ "folder-graphql": "<svg viewBox='0 0 32 32'><path fill='#ec407a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#f8bbd0' d='M22.995 10A1.99 1.99 0 0 0 21 11.984a2 2 0 0 0 .036.37l-3.734 2.14a2 2 0 0 0-.308-.221h-.003a2.01 2.01 0 0 0-2.72.761 1.948 1.948 0 0 0 1.07 2.825v4.277a1.975 1.975 0 0 0-1.228 2.52 2.007 2.007 0 0 0 2.884 1.063 2 2 0 0 0 .303-.218l3.736 2.135a1.998 1.998 0 1 0 3.96.376 2 2 0 0 0-.05-.431l3.7-2.12a2.015 2.015 0 0 0 2.828-.128 2 2 0 0 0 .251-.34h.003v-.002a1.97 1.97 0 0 0-.73-2.704l-.002-.001a2 2 0 0 0-.341-.15v-4.28a1.974 1.974 0 0 0 1.236-2.516 2.006 2.006 0 0 0-2.886-1.067H29a2 2 0 0 0-.31.22l-3.732-2.132a1.985 1.985 0 0 0-1.586-2.324 2 2 0 0 0-.376-.036Zm-1.802 3.78-4.534 7.777v-3.713a1.934 1.934 0 0 0 1.288-2.208Zm3.616.005 3.234 1.854a1.94 1.94 0 0 0 1.292 2.205v3.707Zm-2.178.14a1.9 1.9 0 0 0 .734 0l5.12 8.787a1.85 1.85 0 0 0-.374.639H17.882a2 2 0 0 0-.153-.337l-.002-.003a2 2 0 0 0-.217-.296ZM18.472 24.66h9.144l-3.293 1.883a1.974 1.974 0 0 0-2.618-.03l-3.233-1.85Z'/></svg>",
+ "folder-guard-open": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c8e6c9' d='m25 9.962-7 3.273v4.908c0 4.542 2.986 8.788 7 9.82 4.014-1.032 7-5.278 7-9.82v-4.908ZM28 20h-6v-2h6Z'/></svg>",
+ "folder-guard": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c8e6c9' d='m25 9.962-7 3.273v4.908c0 4.542 2.986 8.788 7 9.82 4.014-1.032 7-5.278 7-9.82v-4.908ZM28 20h-6v-2h6Z'/></svg>",
+ "folder-gulp-open": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M24 16v-2.968l2.563-1.306A1 1 0 0 0 27 10.381a1 1 0 0 0-1.345-.437L22 11.806V16h-4v2h1l1 10h6l1-10h1v-2Z'/></svg>",
+ "folder-gulp": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M24 16v-2.968l2.563-1.306A1 1 0 0 0 27 10.381a1 1 0 0 0-1.345-.437L22 11.806V16h-4v2h1l1 10h6l1-10h1v-2Z'/></svg>",
+ "folder-helm-open": "<svg viewBox='0 0 32 32'><path fill='#00acc1' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2ebf2' d='M23.239 27.996c-.13-.09-.284-.39-.372-.724-.078-.296-.108-1.078-.056-1.462.02-.143.03-.264.025-.27a2 2 0 0 0-.27-.042c-.82-.099-1.694-.378-2.36-.752a2 2 0 0 0-.294-.144c-.02 0-.092.106-.16.236-.313.604-.861 1.204-1.278 1.4-.152.07-.355.088-.448.038-.142-.076-.183-.376-.099-.708.058-.227.29-.706.478-.985.083-.124.248-.333.366-.466l.213-.24-.111-.107c-.294-.28-.846-.93-.846-.995 0-.023.753-.546.808-.56.028-.008.095.058.187.184.197.272.726.795 1.038 1.026 2.372 1.764 5.716 1.32 7.517-.996.095-.123.188-.224.207-.224.036 0 .78.506.807.548.023.037-.303.46-.58.752-.133.139-.24.264-.24.278s.103.134.23.267c.402.424.797 1.086.907 1.518.084.332.043.632-.099.708a.5.5 0 0 1-.18.029c-.433 0-1.15-.696-1.577-1.527-.082-.16-.161-.292-.176-.292a1 1 0 0 0-.186.102c-.62.397-1.544.74-2.344.868a2 2 0 0 0-.34.074c-.022.017-.02.097.01.277.052.32.032 1.083-.037 1.392-.07.318-.179.565-.313.711-.154.168-.273.193-.427.086m-8.513-6.49c-.008-.02-.01-1.076-.007-2.347l.008-2.31h1.21l.008.85c.006.637.017.857.043.874.02.012.323.023.674.023.497 0 .645-.008.67-.038q.03-.038.039-.874l.008-.834h1.21v4.677h-1.21l-.008-.893c-.005-.6-.018-.905-.04-.93-.025-.03-.173-.04-.673-.04s-.649.01-.674.04c-.021.025-.034.33-.04.93l-.007.893-.599.008c-.463.006-.602 0-.612-.03zm4.9 0c-.008-.02-.01-1.076-.006-2.347l.007-2.31h2.998l.008.513.008.512-.887.007-.888.008-.008.344c-.006.256.002.35.03.368.02.013.364.023.765.024h.728l-.008.513-.008.512-1.498.03v.808l.922.014.922.014v1.011l-1.535.007c-1.214.006-1.54 0-1.55-.029zm4.035 0c-.007-.02-.01-1.076-.006-2.347l.007-2.31h1.211l.015 1.82.014 1.818 1.758.03v1.01l-1.492.007c-1.179.006-1.496 0-1.506-.029zm3.834 0c-.007-.02-.01-1.076-.006-2.346l.007-2.31.634-.007c.35-.004.649.002.666.014.043.029.683 1.773.824 2.245.147.491.136.462.17.429.015-.016.083-.21.15-.431.15-.488.74-2.178.78-2.23.02-.027.176-.034.664-.028l.638.008v4.677l-.54.008-.54.008-.019-.077c-.04-.158.014-1.83.074-2.307.094-.75.08-.762-.143-.115-.241.7-.72 1.987-.766 2.057-.04.06-.063.065-.34.065-.205 0-.308-.011-.327-.036-.036-.046-.624-1.647-.785-2.136-.114-.345-.175-.47-.173-.349 0 .03.027.267.059.529.06.49.112 2.136.073 2.292l-.019.075h-.534c-.4 0-.537-.009-.547-.036zm.381-5.333a5.7 5.7 0 0 0-.64-.807c-.78-.82-1.716-1.337-2.867-1.583-.33-.071-.417-.078-1.024-.079-.714-.002-.87.016-1.424.161a5.3 5.3 0 0 0-2.035 1.037c-.29.237-.753.725-.95 1.003-.122.17-.184.234-.216.222-.105-.04-.808-.513-.808-.543.001-.069.475-.663.797-1l.332-.347-.22-.238c-.402-.436-.787-1.086-.894-1.508-.085-.332-.043-.632.099-.708a.5.5 0 0 1 .18-.03c.433.002 1.15.697 1.577 1.528.082.16.16.291.172.291s.125-.058.25-.13c.644-.367 1.677-.694 2.381-.754.122-.01.236-.028.255-.04.025-.015.021-.093-.016-.315-.068-.41-.047-1.213.041-1.547.14-.528.364-.824.592-.781.213.04.42.387.527.886.07.325.072 1.241.003 1.541-.025.111-.038.211-.027.222s.148.04.307.065c.918.145 1.615.401 2.469.91.058.034.113.05.121.036.01-.014.088-.169.175-.344.338-.676.882-1.29 1.328-1.5.152-.07.354-.088.448-.038.142.076.183.376.1.708-.063.243-.3.721-.512 1.032-.1.146-.3.386-.448.534l-.267.27.263.28c.266.282.735.882.821 1.048.04.075.04.096.008.126-.066.06-.775.477-.812.477-.02 0-.058-.038-.086-.085'/></svg>",
+ "folder-helm": "<svg viewBox='0 0 32 32'><path fill='#00acc1' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2ebf2' d='M23.24 27.996c-.13-.09-.284-.39-.372-.724-.078-.296-.108-1.078-.056-1.462.02-.143.03-.264.025-.27a2 2 0 0 0-.27-.042c-.82-.099-1.694-.378-2.36-.752a2 2 0 0 0-.294-.144c-.02 0-.092.106-.16.236-.313.604-.861 1.204-1.278 1.4-.152.07-.355.088-.448.038-.142-.076-.183-.376-.099-.708.058-.227.29-.706.478-.985.083-.124.248-.333.366-.466l.213-.24-.111-.107c-.294-.28-.846-.93-.846-.995 0-.023.753-.546.808-.56.028-.008.095.058.187.184.197.272.726.795 1.038 1.026 2.372 1.764 5.716 1.32 7.517-.996.095-.123.188-.224.207-.224.036 0 .78.506.807.548.023.037-.303.46-.58.752-.133.139-.24.264-.24.278s.103.134.23.267c.402.424.797 1.086.907 1.518.084.332.043.632-.099.708a.5.5 0 0 1-.18.029c-.433 0-1.15-.696-1.577-1.527-.082-.16-.161-.292-.176-.292a1 1 0 0 0-.186.102c-.62.397-1.544.74-2.344.868a2 2 0 0 0-.34.074c-.022.017-.02.097.01.277.052.32.032 1.083-.037 1.392-.07.318-.179.565-.313.711-.154.168-.273.193-.427.086m-8.513-6.49c-.008-.02-.01-1.076-.007-2.347l.008-2.31h1.21l.008.85c.006.637.017.857.043.874.02.012.323.023.674.023.497 0 .645-.008.67-.038q.03-.038.039-.874l.008-.834h1.21v4.677h-1.21l-.008-.893c-.005-.6-.018-.905-.04-.93-.025-.03-.173-.04-.673-.04s-.649.01-.674.04c-.021.025-.034.33-.04.93l-.007.893-.599.008c-.463.006-.602 0-.612-.03zm4.9 0c-.008-.02-.01-1.076-.006-2.347l.007-2.31h2.998l.008.513.008.512-.887.007-.888.008-.008.344c-.006.256.002.35.03.368.02.013.364.023.765.024h.728l-.008.513-.008.512-1.498.03v.808l.922.014.922.014v1.011l-1.535.007c-1.214.006-1.54 0-1.55-.029zm4.035 0c-.007-.02-.01-1.076-.006-2.347l.007-2.31h1.211l.015 1.82.014 1.818 1.758.03v1.01l-1.492.007c-1.179.006-1.496 0-1.507-.029zm3.834 0c-.007-.02-.01-1.076-.006-2.346l.007-2.31.634-.007c.35-.004.649.002.666.014.043.029.683 1.773.824 2.245.147.491.136.462.17.429.015-.016.083-.21.15-.431.15-.488.74-2.178.78-2.23.02-.027.176-.034.664-.028l.638.008v4.677l-.54.008-.54.008-.019-.077c-.04-.158.014-1.83.074-2.307.094-.75.08-.762-.143-.115-.241.7-.72 1.987-.766 2.057-.04.06-.063.065-.34.065-.205 0-.308-.011-.327-.036-.036-.046-.624-1.647-.785-2.136-.114-.345-.175-.47-.173-.349 0 .03.027.267.059.529.06.49.112 2.136.073 2.292l-.019.075h-.534c-.4 0-.537-.009-.547-.036zm.381-5.333a5.7 5.7 0 0 0-.64-.807c-.78-.82-1.716-1.337-2.867-1.583-.33-.071-.417-.078-1.024-.079-.714-.002-.87.016-1.424.161a5.3 5.3 0 0 0-2.035 1.037c-.29.237-.753.725-.95 1.003-.122.17-.184.234-.216.222-.105-.04-.808-.513-.808-.543.001-.069.475-.663.797-1l.332-.347-.22-.238c-.402-.436-.787-1.086-.894-1.508-.085-.332-.043-.632.099-.708a.5.5 0 0 1 .18-.03c.433.002 1.15.697 1.577 1.528.082.16.16.291.172.291s.125-.058.25-.13c.644-.367 1.677-.694 2.381-.754.122-.01.236-.028.255-.04.025-.015.021-.093-.016-.315-.068-.41-.047-1.213.041-1.547.14-.528.364-.824.592-.781.213.04.42.387.527.886.07.325.072 1.241.003 1.541-.025.111-.038.211-.027.222s.148.04.307.065c.918.145 1.615.401 2.469.91.058.034.113.05.121.036.01-.014.088-.169.175-.344.338-.676.882-1.29 1.328-1.5.152-.07.354-.088.448-.038.142.076.183.376.1.708-.063.243-.3.721-.512 1.032-.1.146-.3.386-.448.534l-.267.27.263.28c.266.282.735.882.821 1.048.04.075.04.096.008.126-.066.06-.775.477-.812.477-.02 0-.058-.038-.086-.085'/></svg>",
+ "folder-helper-open": "<svg viewBox='0 0 32 32'><path fill='#afb42b' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f0f4c3' d='M28.178 12a1.57 1.57 0 0 0-1.138.467l-4.62 4.691 4.493 4.555 4.628-4.684a1.646 1.646 0 0 0 0-2.28l-2.259-2.282A1.54 1.54 0 0 0 28.178 12m-6.521 5.924-4.739 4.803a1.635 1.635 0 0 0 .008 2.291l.008.008C15.963 26.017 14.978 27.01 14 28h4.5l.684-.693a1.58 1.58 0 0 0 2.234-.016l4.732-4.803'/></svg>",
+ "folder-helper": "<svg viewBox='0 0 32 32'><path fill='#afb42b' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#f0f4c3' d='M28.178 12a1.57 1.57 0 0 0-1.138.467l-4.62 4.691 4.493 4.555 4.628-4.684a1.646 1.646 0 0 0 0-2.28l-2.259-2.282A1.54 1.54 0 0 0 28.178 12m-6.521 5.924-4.739 4.803a1.635 1.635 0 0 0 .008 2.291l.008.008C15.963 26.017 14.978 27.01 14 28h4.5l.684-.693a1.58 1.58 0 0 0 2.234-.016l4.732-4.803'/></svg>",
+ "folder-home-open": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M20 12 8 22h4v8h6v-6h4v6h6v-8h4z'/></svg>",
+ "folder-home": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M20 12 8 22h4v8h6v-6h4v6h6v-8h4z'/></svg>",
+ "folder-hook-open": "<svg viewBox='0 0 1024 1024'><path fill='#7e57c2' d='M926.944 384h-624.8a64 64 0 0 0-60.736 43.776L128 768V320h768a64 64 0 0 0-64-64H483.968a64 64 0 0 1-40.96-14.848l-41.216-34.304A64 64 0 0 0 360.832 192H128a64 64 0 0 0-64 64v512a64 64 0 0 0 64 64h704l153.76-358.784A64 64 0 0 0 926.944 384'/><path fill='#d1c4e9' d='M800 320c-53.02 0-96 42.98-96 96 .104 40.593 25.729 76.733 64 90.264V768c0 35.346-28.654 64-64 64s-64-28.654-64-64v-64h64L576 576v192c0 70.692 57.308 128 128 128s128-57.308 128-128V506.264c38.271-13.531 63.896-49.671 64-90.264 0-53.02-42.98-96-96-96m0 64c17.673 0 32 14.327 32 32s-14.327 32-32 32-32-14.327-32-32 14.327-32 32-32'/></svg>",
+ "folder-hook": "<svg viewBox='0 0 1024 1024'><path fill='#7e57c2' d='m443.008 241.152-41.216-34.304A64 64 0 0 0 360.832 192H128a64 64 0 0 0-64 64v512a64 64 0 0 0 64 64h768a64 64 0 0 0 64-64V320a64 64 0 0 0-64-64H483.968a64 64 0 0 1-40.96-14.848'/><path fill='#d1c4e9' d='M800 320c-53.02 0-96 42.98-96 96 .104 40.593 25.729 76.733 64 90.264V768c0 35.346-28.654 64-64 64s-64-28.654-64-64v-64h64L576 576v192c0 70.692 57.308 128 128 128s128-57.308 128-128V506.264c38.271-13.531 63.896-49.671 64-90.264 0-53.02-42.98-96-96-96m0 64c17.673 0 32 14.327 32 32s-14.327 32-32 32-32-14.327-32-32 14.327-32 32-32'/></svg>",
+ "folder-husky-open": "<svg viewBox='0 0 32 32'><path fill='#607d8b' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#cfd8dc' d='M24.942 12.076c.872.35 1.217 1.731.761 3.095-.452 1.357-1.518 2.184-2.395 1.84-.869-.34-1.22-1.725-.771-3.093.444-1.36 1.523-2.179 2.405-1.842m4.879 2.832c.738.602.566 1.947-.371 3.023-.961 1.07-2.321 1.46-3.057.87-.74-.595-.561-1.937.388-3.005.948-1.078 2.308-1.468 3.04-.888m-10.343-1.795c.97.116 1.68 1.34 1.62 2.724-.104 1.386-.935 2.421-1.9 2.31-.963-.111-1.668-1.326-1.588-2.716s.928-2.425 1.868-2.319m12.285 7.131c.561.765.094 2.021-1.064 2.785s-2.555.76-3.133-.026c-.578-.782-.102-2.024 1.04-2.808 1.163-.742 2.571-.738 3.157.05m-5.388 6.733a2.14 2.14 0 0 1-1.984 1.017c-1.545-.147-2.323-2.153-3.68-2.94-1.358-.79-3.515-.422-4.367-1.731a2.41 2.41 0 0 1 .065-2.586c.711-.952 2.249-.792 3.4-1.12 1.519-.409 3.245-1.831 4.617-1.033 1.366.79 1.06 3 1.41 4.53.277 1.278 1.134 2.718.539 3.863'/></svg>",
+ "folder-husky": "<svg viewBox='0 0 32 32'><path fill='#607d8b' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#cfd8dc' d='M24.942 12.076c.872.35 1.217 1.731.761 3.095-.452 1.357-1.518 2.184-2.395 1.84-.869-.34-1.22-1.725-.771-3.093.444-1.36 1.523-2.179 2.405-1.842m4.879 2.832c.738.602.566 1.947-.371 3.023-.961 1.07-2.321 1.46-3.057.87-.74-.595-.561-1.937.388-3.005.948-1.078 2.308-1.468 3.04-.888m-10.343-1.795c.97.116 1.68 1.34 1.62 2.724-.104 1.386-.935 2.421-1.9 2.31-.963-.111-1.668-1.326-1.588-2.716s.928-2.425 1.868-2.319m12.285 7.131c.561.765.094 2.021-1.064 2.785s-2.555.76-3.133-.026c-.578-.782-.102-2.024 1.04-2.808 1.163-.742 2.571-.738 3.157.05m-5.388 6.733a2.14 2.14 0 0 1-1.984 1.017c-1.545-.147-2.323-2.153-3.68-2.94-1.358-.79-3.515-.422-4.367-1.731a2.41 2.41 0 0 1 .065-2.586c.711-.952 2.249-.792 3.4-1.12 1.519-.409 3.245-1.831 4.617-1.033 1.366.79 1.06 3 1.41 4.53.277 1.278 1.134 2.718.539 3.863'/></svg>",
+ "folder-i18n-open": "<svg viewBox='0 0 32 32'><path fill='#5c6bc0' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c5cae9' d='m22.79 23.762-2.308-2.259.027-.026a15.7 15.7 0 0 0 3.373-5.877h2.663v-1.8h-6.363V12h-1.819v1.8H12v1.8h10.155a14.2 14.2 0 0 1-2.882 4.814 14 14 0 0 1-2.1-3.014h-1.819a15.8 15.8 0 0 0 2.71 4.103l-4.629 4.518 1.292 1.278 4.545-4.5 2.828 2.799zm5.12-4.562h-1.82L22 30h1.818l1.017-2.7h4.32l1.025 2.699H32zm-2.384 6.3L27 21.602l1.473 3.897Z'/></svg>",
+ "folder-i18n": "<svg viewBox='0 0 32 32'><path fill='#5c6bc0' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c5cae9' d='m22.79 23.762-2.308-2.259.027-.026a15.7 15.7 0 0 0 3.373-5.877h2.663v-1.8h-6.363V12h-1.819v1.8H12v1.8h10.155a14.2 14.2 0 0 1-2.882 4.814 14 14 0 0 1-2.1-3.014h-1.819a15.8 15.8 0 0 0 2.71 4.103l-4.629 4.518 1.292 1.278 4.545-4.5 2.828 2.799zm5.12-4.562h-1.82L22 30h1.818l1.017-2.7h4.32l1.025 2.699H32zm-2.384 6.3L27 21.602l1.473 3.897Z'/></svg>",
+ "folder-images-open": "<svg viewBox='0 0 32 32'><path fill='#009688' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2dfdb' d='M24 10h-7a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V16Zm-4 6a2 2 0 1 1-2 2 2.01 2.01 0 0 1 2-2m8 10H18l4-4 2 2 4-4Zm-4.828-9.172V12L28 16.828Z'/></svg>",
+ "folder-images": "<svg viewBox='0 0 32 32'><path fill='#009688' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2dfdb' d='M24 10h-7a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V16Zm-4 6a2 2 0 1 1-2 2 2.01 2.01 0 0 1 2-2m8 10H18l4-4 2 2 4-4Zm-4.828-9.172V12L28 16.828Z'/></svg>",
+ "folder-import-open": "<svg viewBox='0 0 32 32'><path fill='#afb42b' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f0f4c3' d='M25 14a7 7 0 1 0 7 7 7 7 0 0 0-7-7m1 8v4h-2v-4h-4v-2h4v-4h2v4h4v2Z'/></svg>",
+ "folder-import": "<svg viewBox='0 0 32 32'><path fill='#afb42b' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#f0f4c3' d='M25 14a7 7 0 1 0 7 7 7 7 0 0 0-7-7m1 8v4h-2v-4h-4v-2h4v-4h2v4h4v2Z'/></svg>",
+ "folder-include-open": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M25 14a7 7 0 1 0 7 7 7 7 0 0 0-7-7m1 8v4h-2v-4h-4v-2h4v-4h2v4h4v2Z'/></svg>",
+ "folder-include": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M25 14a7 7 0 1 0 7 7 7 7 0 0 0-7-7m1 8v4h-2v-4h-4v-2h4v-4h2v4h4v2Z'/></svg>",
+ "folder-intellij-open": "<svg viewBox='0 0 32 32'><defs data-mit-no-recolor='true'><linearGradient id='a' x1='.445' x2='104.977' y1='3272.835' y2='3209.742' gradientTransform='translate(18.126 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#fdd835'/><stop offset='1' stop-color='#f57c00'/></linearGradient><linearGradient id='b' x1='22.55' x2='117.962' y1='3121.343' y2='3204.873' gradientTransform='translate(18.126 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ef5350'/><stop offset='.57' stop-color='#ff6e40'/><stop offset='1' stop-color='#f57c00'/></linearGradient><linearGradient id='c' x1='28.608' x2='-27.937' y1='3197.064' y2='3161.75' gradientTransform='translate(18.126 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#8e24aa'/><stop offset='.385' stop-color='#ab47bc'/><stop offset='.765' stop-color='#ec407a'/><stop offset='.957' stop-color='#ec407a'/></linearGradient><linearGradient id='d' x1='27.588' x2='-27.616' y1='3117.085' y2='3162.678' gradientTransform='translate(18.126 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ef5350'/><stop offset='.364' stop-color='#ec407a'/><stop offset='1' stop-color='#ec407a'/></linearGradient></defs><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='url(#a)' d='M30.93 22.519a.68.68 0 0 0 .22-.47.69.69 0 0 0-.647-.72.72.72 0 0 0-.485.161l-12.314 6.745a1.44 1.44 0 0 0-.69.602 1.48 1.48 0 0 0 .506 2.03l.022.013a1.51 1.51 0 0 0 1.573-.03c.03-.029.073-.043.103-.073l11.461-8.053a2 2 0 0 0 .25-.205Z'/><path fill='url(#b)' d='m30.959 21.534-9.376-9.199a1.133 1.133 0 1 0-1.66 1.543 2 2 0 0 0 .176.147l9.904 8.48a.76.76 0 0 0 .441.19.69.69 0 0 0 .72-.646.73.73 0 0 0-.205-.515'/><path fill='url(#c)' d='M21.892 20.711c-.015 0-5.79-4.555-5.907-4.628l-.265-.133a1.644 1.644 0 0 0-1.44 2.94 1.3 1.3 0 0 0 .294.131c.059.03 6.671 2.763 6.671 2.763a.63.63 0 0 0 .647-1.073'/><path fill='url(#d)' d='M20.746 11.968a1.2 1.2 0 0 0-.676.22l-5.849 3.939c-.014.014-.03.014-.03.029h-.014a1.638 1.638 0 0 0 .397 2.865 1.61 1.61 0 0 0 1.528-.205 1.4 1.4 0 0 0 .265-.235l5.084-4.585a1.132 1.132 0 0 0-.705-2.028'/></svg>",
+ "folder-intellij-open_light": "<svg viewBox='0 0 32 32'><defs data-mit-no-recolor='true'><linearGradient id='a' x1='.445' x2='104.977' y1='3611.926' y2='3548.833' gradientTransform='translate(18.126 -368.395)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#fdd835'/><stop offset='1' stop-color='#f57c00'/></linearGradient><linearGradient id='b' x1='22.55' x2='117.962' y1='3460.434' y2='3543.963' gradientTransform='translate(18.126 -368.395)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ef5350'/><stop offset='.57' stop-color='#ff6e40'/><stop offset='1' stop-color='#f57c00'/></linearGradient><linearGradient id='c' x1='28.608' x2='-27.937' y1='3536.154' y2='3500.841' gradientTransform='translate(18.126 -368.395)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#8e24aa'/><stop offset='.385' stop-color='#ab47bc'/><stop offset='.765' stop-color='#ec407a'/><stop offset='.957' stop-color='#ec407a'/></linearGradient><linearGradient id='d' x1='27.588' x2='-27.616' y1='3456.176' y2='3501.769' gradientTransform='translate(18.126 -368.395)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ef5350'/><stop offset='.364' stop-color='#ec407a'/><stop offset='1' stop-color='#ec407a'/></linearGradient></defs><path fill='#b0bec5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='url(#a)' d='M30.93 22.519a.68.68 0 0 0 .22-.47.69.69 0 0 0-.647-.72.72.72 0 0 0-.485.161l-12.314 6.745a1.44 1.44 0 0 0-.69.602 1.48 1.48 0 0 0 .506 2.03l.022.013a1.51 1.51 0 0 0 1.573-.03c.03-.029.073-.043.103-.073l11.461-8.053a2 2 0 0 0 .25-.205Z'/><path fill='url(#b)' d='m30.959 21.534-9.376-9.199a1.133 1.133 0 1 0-1.66 1.543 2 2 0 0 0 .176.147l9.904 8.48a.76.76 0 0 0 .441.19.69.69 0 0 0 .72-.646.73.73 0 0 0-.205-.515'/><path fill='url(#c)' d='M21.892 20.711c-.015 0-5.79-4.555-5.907-4.628l-.265-.133a1.644 1.644 0 0 0-1.44 2.94 1.3 1.3 0 0 0 .294.131c.059.03 6.671 2.763 6.671 2.763a.63.63 0 0 0 .647-1.073'/><path fill='url(#d)' d='M20.746 11.968a1.2 1.2 0 0 0-.676.22l-5.849 3.939c-.014.014-.03.014-.03.029h-.014a1.638 1.638 0 0 0 .397 2.865 1.61 1.61 0 0 0 1.528-.205 1.4 1.4 0 0 0 .265-.235l5.084-4.585a1.132 1.132 0 0 0-.705-2.028'/></svg>",
+ "folder-intellij": "<svg viewBox='0 0 32 32'><defs data-mit-no-recolor='true'><linearGradient id='a' x1='-338.646' x2='-234.114' y1='3272.835' y2='3209.742' gradientTransform='translate(55.497 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#fdd835'/><stop offset='1' stop-color='#f57c00'/></linearGradient><linearGradient id='b' x1='-316.541' x2='-221.129' y1='3121.343' y2='3204.873' gradientTransform='translate(55.497 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ef5350'/><stop offset='.57' stop-color='#ff6e40'/><stop offset='1' stop-color='#f57c00'/></linearGradient><linearGradient id='c' x1='-310.483' x2='-367.028' y1='3197.064' y2='3161.75' gradientTransform='translate(55.497 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#8e24aa'/><stop offset='.385' stop-color='#ab47bc'/><stop offset='.765' stop-color='#ec407a'/><stop offset='.957' stop-color='#ec407a'/></linearGradient><linearGradient id='d' x1='-311.503' x2='-366.707' y1='3117.085' y2='3162.678' gradientTransform='translate(55.497 -331.024)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ef5350'/><stop offset='.364' stop-color='#ec407a'/><stop offset='1' stop-color='#ec407a'/></linearGradient></defs><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='url(#a)' d='M30.93 22.519a.68.68 0 0 0 .22-.47.69.69 0 0 0-.647-.72.72.72 0 0 0-.485.161l-12.314 6.745a1.44 1.44 0 0 0-.69.602 1.48 1.48 0 0 0 .506 2.03l.022.013a1.51 1.51 0 0 0 1.573-.03c.03-.029.073-.043.103-.073l11.461-8.053a2 2 0 0 0 .25-.205Z'/><path fill='url(#b)' d='m30.959 21.534-9.376-9.199a1.133 1.133 0 1 0-1.66 1.543 2 2 0 0 0 .176.147l9.904 8.48a.76.76 0 0 0 .441.19.69.69 0 0 0 .72-.646.73.73 0 0 0-.205-.515'/><path fill='url(#c)' d='M21.892 20.711c-.015 0-5.79-4.555-5.907-4.628l-.265-.133a1.644 1.644 0 0 0-1.44 2.94 1.3 1.3 0 0 0 .294.131c.059.03 6.671 2.763 6.671 2.763a.63.63 0 0 0 .647-1.073'/><path fill='url(#d)' d='M20.746 11.968a1.2 1.2 0 0 0-.676.22l-5.849 3.939c-.014.014-.03.014-.03.029h-.014a1.638 1.638 0 0 0 .397 2.865 1.61 1.61 0 0 0 1.528-.205 1.4 1.4 0 0 0 .265-.235l5.084-4.585a1.132 1.132 0 0 0-.705-2.028'/></svg>",
+ "folder-intellij_light": "<svg viewBox='0 0 32 32'><defs data-mit-no-recolor='true'><linearGradient id='a' x1='-338.646' x2='-234.114' y1='3611.926' y2='3548.833' gradientTransform='translate(55.497 -368.395)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#fdd835'/><stop offset='1' stop-color='#f57c00'/></linearGradient><linearGradient id='b' x1='-316.541' x2='-221.129' y1='3460.434' y2='3543.963' gradientTransform='translate(55.497 -368.395)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ef5350'/><stop offset='.57' stop-color='#ff6e40'/><stop offset='1' stop-color='#f57c00'/></linearGradient><linearGradient id='c' x1='-310.483' x2='-367.028' y1='3536.154' y2='3500.841' gradientTransform='translate(55.497 -368.395)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#8e24aa'/><stop offset='.385' stop-color='#ab47bc'/><stop offset='.765' stop-color='#ec407a'/><stop offset='.957' stop-color='#ec407a'/></linearGradient><linearGradient id='d' x1='-311.503' x2='-366.707' y1='3456.176' y2='3501.769' gradientTransform='translate(55.497 -368.395)scale(.11021)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ef5350'/><stop offset='.364' stop-color='#ec407a'/><stop offset='1' stop-color='#ec407a'/></linearGradient></defs><path fill='#b0bec5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='url(#a)' d='M30.93 22.519a.68.68 0 0 0 .22-.47.69.69 0 0 0-.647-.72.72.72 0 0 0-.485.161l-12.314 6.745a1.44 1.44 0 0 0-.69.602 1.48 1.48 0 0 0 .506 2.03l.022.013a1.51 1.51 0 0 0 1.573-.03c.03-.029.073-.043.103-.073l11.461-8.053a2 2 0 0 0 .25-.205Z'/><path fill='url(#b)' d='m30.959 21.534-9.376-9.199a1.133 1.133 0 1 0-1.66 1.543 2 2 0 0 0 .176.147l9.904 8.48a.76.76 0 0 0 .441.19.69.69 0 0 0 .72-.646.73.73 0 0 0-.205-.515'/><path fill='url(#c)' d='M21.892 20.711c-.015 0-5.79-4.555-5.907-4.628l-.265-.133a1.644 1.644 0 0 0-1.44 2.94 1.3 1.3 0 0 0 .294.131c.059.03 6.671 2.763 6.671 2.763a.63.63 0 0 0 .647-1.073'/><path fill='url(#d)' d='M20.746 11.968a1.2 1.2 0 0 0-.676.22l-5.849 3.939c-.014.014-.03.014-.03.029h-.014a1.638 1.638 0 0 0 .397 2.865 1.61 1.61 0 0 0 1.528-.205 1.4 1.4 0 0 0 .265-.235l5.084-4.585a1.132 1.132 0 0 0-.705-2.028'/></svg>",
+ "folder-interface-open": "<svg viewBox='0 0 32 32'><path fill='#1976d2' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M16 12v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V12a2 2 0 0 0-2-2H18a2 2 0 0 0-2 2m14 14H18v-4h12Zm0-6H18v-4h12Z'/></svg>",
+ "folder-interface": "<svg viewBox='0 0 32 32'><path fill='#1976d2' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M16 12v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V12a2 2 0 0 0-2-2H18a2 2 0 0 0-2 2m14 14H18v-4h12Zm0-6H18v-4h12Z'/></svg>",
+ "folder-ios-open": "<svg viewBox='0 0 32 32'><path fill='#78909c' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#cfd8dc' d='M12 18h2v10h-2zm0-4h2v2h-2zm4 1v12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V15a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1m6 11h-4V16h4Zm10-10v-2h-5a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h3v4h-4v2h5a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1h-3v-4Z'/></svg>",
+ "folder-ios": "<svg viewBox='0 0 32 32'><path fill='#78909c' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#cfd8dc' d='M12 18h2v10h-2zm0-4h2v2h-2zm4 1v12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V15a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1m6 11h-4V16h4Zm10-10v-2h-5a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h3v4h-4v2h5a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1h-3v-4Z'/></svg>",
+ "folder-java-open": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M14 26h16v2H14zm17-14H16v8a4 4 0 0 0 4 4h4a4 4 0 0 0 4-4h3a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1m-1 6h-2v-4h2Z'/></svg>",
+ "folder-java": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M14 26h16v2H14zm17-14H16v8a4 4 0 0 0 4 4h4a4 4 0 0 0 4-4h3a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1m-1 6h-2v-4h2Z'/></svg>",
+ "folder-javascript-open": "<svg viewBox='0 0 32 32'><path fill='#ffca28' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffecb3' d='M24 19.06a1.33 1.33 0 0 0 .3 1.04 2.5 2.5 0 0 0 .61.28c.54.18 1.33.37 2.09.62 2.64.88 2.96 2.32 2.99 3.49.01.16.01.31.01.46V25c0 1.06-.46 2.79-3.44 2.98-.13.01-.25.01-.37.01A1 1 0 0 1 26 28h-4v-1.76l.24-.24H26a2 2 0 0 0 .25-.01h.17c.18-.01.33-.03.47-.04a2 2 0 0 0 .27-.06c.07-.02.13-.04.19-.06a.04.04 0 0 0 .03-.01c.49-.18.59-.45.61-.66A1 1 0 0 0 28 25c0-.32-.68-1.23-3-2-2.74-.91-2.98-2.42-2.99-3.61a.6.6 0 0 1-.01-.13V19a2.85 2.85 0 0 1 .45-1.59c.04-.06.07-.11.11-.16.01-.01.01-.02.02-.03a1 1 0 0 1 .18-.2A4.3 4.3 0 0 1 25.91 16H30v2h-4c-.13 0-.26 0-.39.01-1.18.06-1.49.4-1.58.7a.13.13 0 0 0-.01.06A1 1 0 0 0 24 19ZM16 28a3.64 3.64 0 0 1-4-4h2c0 1.44.56 2 2 2s2-.56 2-2v-8h2v8a3.64 3.64 0 0 1-4 4'/></svg>",
+ "folder-javascript": "<svg viewBox='0 0 32 32'><path fill='#ffca28' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffecb3' d='M24 19.06a1.33 1.33 0 0 0 .3 1.04 2.5 2.5 0 0 0 .61.28c.54.18 1.33.37 2.09.62 2.64.88 2.96 2.32 2.99 3.49.01.16.01.31.01.46V25c0 1.06-.46 2.79-3.44 2.98-.13.01-.25.01-.37.01A1 1 0 0 1 26 28h-4v-1.76l.24-.24H26a2 2 0 0 0 .25-.01h.17c.18-.01.33-.03.47-.04a2 2 0 0 0 .27-.06c.07-.02.13-.04.19-.06a.04.04 0 0 0 .03-.01c.49-.18.59-.45.61-.66A1 1 0 0 0 28 25c0-.32-.68-1.23-3-2-2.74-.91-2.98-2.42-2.99-3.61a.6.6 0 0 1-.01-.13V19a2.85 2.85 0 0 1 .45-1.59c.04-.06.07-.11.11-.16.01-.01.01-.02.02-.03a1 1 0 0 1 .18-.2A4.3 4.3 0 0 1 25.91 16H30v2h-4c-.13 0-.26 0-.39.01-1.18.06-1.49.4-1.58.7a.13.13 0 0 0-.01.06A1 1 0 0 0 24 19ZM16 28a3.64 3.64 0 0 1-4-4h2c0 1.44.56 2 2 2s2-.56 2-2v-8h2v8a3.64 3.64 0 0 1-4 4'/></svg>",
+ "folder-jinja-open": "<svg viewBox='0 0 32 32'><path fill='#e0e0e0' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#424242' d='M32 17v-3a41.3 41.3 0 0 1-9 1 41.3 41.3 0 0 1-9-1v3a36 36 0 0 0 3.938.681L17.728 20H14v2h3.545L17 28h3v-6h6v6h3l-.545-6H32v-2h-3.727l-.211-2.319A36 36 0 0 0 32 17m-6 3h-2v-2h-2v2h-2v-2.116c.938.07 1.945.116 3 .116s2.062-.046 3-.116Z'/></svg>",
+ "folder-jinja-open_light": "<svg viewBox='0 0 32 32'><path fill='#616161' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f5f5f5' d='M32 17v-3a41.3 41.3 0 0 1-9 1 41.3 41.3 0 0 1-9-1v3a36 36 0 0 0 3.938.681L17.728 20H14v2h3.545L17 28h3v-6h6v6h3l-.545-6H32v-2h-3.727l-.211-2.319A36 36 0 0 0 32 17m-6 3h-2v-2h-2v2h-2v-2.116c.938.07 1.945.116 3 .116s2.062-.046 3-.116Z'/></svg>",
+ "folder-jinja": "<svg viewBox='0 0 32 32'><path fill='#e0e0e0' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#424242' d='M32 17v-3a41.3 41.3 0 0 1-9 1 41.3 41.3 0 0 1-9-1v3a36 36 0 0 0 3.938.681L17.728 20H14v2h3.545L17 28h3v-6h6v6h3l-.545-6H32v-2h-3.727l-.211-2.319A36 36 0 0 0 32 17m-6 3h-2v-2h-2v2h-2v-2.116c.938.07 1.945.116 3 .116s2.062-.046 3-.116Z'/></svg>",
+ "folder-jinja_light": "<svg viewBox='0 0 32 32'><path fill='#616161' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#f5f5f5' d='M32 17v-3a41.3 41.3 0 0 1-9 1 41.3 41.3 0 0 1-9-1v3a36 36 0 0 0 3.938.681L17.728 20H14v2h3.545L17 28h3v-6h6v6h3l-.545-6H32v-2h-3.727l-.211-2.319A36 36 0 0 0 32 17m-6 3h-2v-2h-2v2h-2v-2.116c.938.07 1.945.116 3 .116s2.062-.046 3-.116Z'/></svg>",
+ "folder-job-open": "<svg viewBox='0 0 32 32'><path fill='#ffa000' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffecb3' d='M30 14h-4v-2l-2-2h-4l-2 2v2h-4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V16a2 2 0 0 0-2-2m-10-2h4v2h-4Zm6 10-7.023 4-1.014-1.725 5.558-3.27-5.558-3.269 1.014-1.724L26 20Z'/></svg>",
+ "folder-job": "<svg viewBox='0 0 32 32'><path fill='#ffa000' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffecb3' d='M30 14h-4v-2l-2-2h-4l-2 2v2h-4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V16a2 2 0 0 0-2-2m-10-2h4v2h-4Zm6 10-7.023 4-1.014-1.725 5.558-3.27-5.558-3.269 1.014-1.724L26 20Z'/></svg>",
+ "folder-json-open": "<svg viewBox='0 0 32 32'><path fill='#f9a825' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fffde7' d='M24 28v-2h3q.425 0 .713-.288Q28 25.425 28 25v-2q0-.95.55-1.725t1.45-1.1v-.35q-.9-.325-1.45-1.1T28 17v-2q0-.425-.287-.712Q27.424 14 27 14h-3v-2h3q1.25 0 2.125.875T30 15v2q0 .425.287.712.288.288.713.288h1v4h-1q-.425 0-.713.288Q30 22.575 30 23v2q0 1.25-.875 2.125T27 28zm-7 0q-1.25 0-2.125-.875T14 25v-2q0-.425-.287-.712Q13.425 22 13 22h-1v-4h1q.425 0 .713-.288Q14 17.425 14 17v-2q0-1.25.875-2.125T17 12h3v2h-3q-.425 0-.713.288Q16 14.575 16 15v2q0 .95-.55 1.725t-1.45 1.1v.35q.9.325 1.45 1.1T16 23v2q0 .425.287.712.288.288.713.288h3v2z'/></svg>",
+ "folder-json": "<svg viewBox='0 0 32 32'><path fill='#f9a825' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fffde7' d='M24 28v-2h3q.425 0 .713-.287T28 25v-2q0-.95.55-1.725t1.45-1.1v-.35q-.9-.325-1.45-1.1T28 17v-2q0-.425-.287-.713T27 14h-3v-2h3q1.25 0 2.125.875T30 15v2q0 .425.287.712T31 18h1v4h-1q-.425 0-.713.287T30 23v2q0 1.25-.875 2.125T27 28zm-7 0q-1.25 0-2.125-.875T14 25v-2q0-.425-.287-.713T13 22h-1v-4h1q.425 0 .713-.287T14 17v-2q0-1.25.875-2.125T17 12h3v2h-3q-.425 0-.713.287T16 15v2q0 .95-.55 1.725t-1.45 1.1v.35q.9.325 1.45 1.1T16 23v2q0 .425.287.713T17 26h3v2z'/></svg>",
+ "folder-jupyter-open": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><g fill='#ffe0b2' data-mit-no-recolor='true' transform='matrix(.7 0 0 .7 10 10)'><path d='M6.2 18a22.7 22.7 0 0 0 9.8 2 22.7 22.7 0 0 0 9.8-2 10.002 10.002 0 0 1-19.6 0m0-4a22.7 22.7 0 0 1 9.8-2 22.7 22.7 0 0 1 9.8 2 10.002 10.002 0 0 0-19.6 0'/><circle cx='27' cy='5' r='3'/><circle cx='5' cy='27' r='3'/><circle cx='5' cy='5' r='3'/></g></svg>",
+ "folder-jupyter": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><g fill='#ffe0b2' data-mit-no-recolor='true' transform='matrix(.7 0 0 .7 10 10)'><path d='M6.2 18a22.7 22.7 0 0 0 9.8 2 22.7 22.7 0 0 0 9.8-2 10.002 10.002 0 0 1-19.6 0m0-4a22.7 22.7 0 0 1 9.8-2 22.7 22.7 0 0 1 9.8 2 10.002 10.002 0 0 0-19.6 0'/><circle cx='27' cy='5' r='3'/><circle cx='5' cy='27' r='3'/><circle cx='5' cy='5' r='3'/></g></svg>",
+ "folder-keys-open": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2dfdb' d='M21.651 20a6 6 0 1 0 0 4H26v4h4v-4h2v-4ZM16 24a2 2 0 1 1 2-2 2 2 0 0 1-2 2'/></svg>",
+ "folder-keys": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2dfdb' d='M21.651 20a6 6 0 1 0 0 4H26v4h4v-4h2v-4ZM16 24a2 2 0 1 1 2-2 2 2 0 0 1-2 2'/></svg>",
+ "folder-kubernetes-open": "<svg viewBox='0 0 32 32'><path fill='#448aff' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M22.069 10.463a.6.6 0 0 0-.116.003.59.59 0 0 0-.517.635v.16a3.6 3.6 0 0 0 .08.543 5.2 5.2 0 0 1 .065 1.018.6.6 0 0 1-.186.305l-.013.238a7 7 0 0 0-1.031.157 7.27 7.27 0 0 0-3.706 2.117l-.196-.145a.52.52 0 0 1-.346-.039 5.4 5.4 0 0 1-.765-.69 5 5 0 0 0-.372-.395l-.12-.093a.75.75 0 0 0-.397-.158.6.6 0 0 0-.463.197.61.61 0 0 0 .148.834l.013.013c.026.027.079.067.106.093a4 4 0 0 0 .475.277 5.4 5.4 0 0 1 .848.597.6.6 0 0 1 .106.33l.183.158a7.22 7.22 0 0 0-1.137 5.121l-.25.065a.8.8 0 0 1-.254.253 4.4 4.4 0 0 1-1.018.158 2 2 0 0 0-.543.051l-.144.029h-.013v.013c-.04 0-.08.013-.106.013a.57.57 0 1 0 .339 1.086l.004-.001a1 1 0 0 0 .174-.041 2.7 2.7 0 0 0 .488-.197 7 7 0 0 1 1.018-.292.5.5 0 0 1 .305.119l.263-.039a7.43 7.43 0 0 0 3.27 4.088l-.094.238a.7.7 0 0 1 .042.33 4.2 4.2 0 0 1-.517.913c-.106.159-.199.304-.318.462a.17.17 0 0 1-.052.148c-.013.04-.04.066-.054.106a.57.57 0 0 0 1.072.382c.027-.04.051-.132.078-.132a5 5 0 0 0 .16-.53 5 5 0 0 1 .437-1.017.45.45 0 0 1 .25-.12l.132-.237a7.4 7.4 0 0 0 5.254.013l.105.212a.5.5 0 0 1 .277.183 6 6 0 0 1 .398.954c.04.172.094.344.16.542.027 0 .051.08.078.12.013.039.028.065.041.105a.568.568 0 0 0 .984-.569l-.007-.012a1 1 0 0 1-.052-.16c-.106-.146-.212-.305-.318-.45a7.4 7.4 0 0 1-.501-.9.44.44 0 0 1 .039-.343 1 1 0 0 1-.093-.225 7.44 7.44 0 0 0 3.268-4.113c.08.013.158.025.251.038a.33.33 0 0 1 .305-.106 4.7 4.7 0 0 1 1.018.28 2.6 2.6 0 0 0 .475.196.7.7 0 0 0 .187.028v.013c0 .013.053.013.093.026a.585.585 0 0 0 .635-.491.57.57 0 0 0-.483-.645l-.008-.001a.34.34 0 0 1-.157-.067h-.543a6.6 6.6 0 0 1-1.018-.186.55.55 0 0 1-.253-.238l-.251-.064a7.2 7.2 0 0 0-1.165-5.109l.211-.184a.4.4 0 0 1 .106-.317 5 5 0 0 1 .848-.597 3.3 3.3 0 0 0 .462-.277 1 1 0 0 0 .12-.093c.039-.026.08-.053.08-.08a.556.556 0 0 0-.78-.793c-.04 0-.093.054-.133.08a10 10 0 0 0-.372.395 4.8 4.8 0 0 1-.767.69.5.5 0 0 1-.344.039l-.212.158a7.37 7.37 0 0 0-4.708-2.274l-.013-.253a.45.45 0 0 1-.186-.29 5.2 5.2 0 0 1 .065-1.018 3.6 3.6 0 0 0 .08-.543v-.292a.57.57 0 0 0-.504-.506m-.778 4.408-.16 2.977h-.013a.5.5 0 0 1-.106.28.5.5 0 0 1-.687.118h-.013l-2.434-1.734a5.75 5.75 0 0 1 2.803-1.535c.212-.04.412-.08.61-.106m1.427 0a5.9 5.9 0 0 1 3.4 1.641l-2.421 1.734h-.013a.6.6 0 0 1-.277.067.494.494 0 0 1-.516-.47v-.008Zm-5.727 2.713 2.223 2.024v.013a.34.34 0 0 1 .144.253.483.483 0 0 1-.323.602l-.02.005v.013l-2.858.822a5.9 5.9 0 0 1 .834-3.732m10.013.04a6.18 6.18 0 0 1 .86 3.679l-2.87-.822-.013-.013a.47.47 0 0 1-.238-.157.49.49 0 0 1 .042-.694l.01-.01-.013-.038Zm-5.462 2.144h.912l.568.7-.199.886-.819.398-.819-.398-.212-.886Zm-2.132 2.447h.106a.55.55 0 0 1 .504.382.5.5 0 0 1-.052.28v.038l-1.127 2.74a5.84 5.84 0 0 1-2.366-2.952Zm4.87 0h.319l2.948.475a5.85 5.85 0 0 1-2.367 2.977l-1.14-2.79a.53.53 0 0 1 .24-.662m-2.327 1.199a.51.51 0 0 1 .488.256h.013l1.442 2.607a5 5 0 0 1-.569.157 5.9 5.9 0 0 1-3.214-.157l1.442-2.607h.012c.04-.093.107-.133.2-.2a.5.5 0 0 1 .186-.056'/></svg>",
+ "folder-kubernetes": "<svg viewBox='0 0 32 32'><path fill='#448aff' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M22.069 10.463a.6.6 0 0 0-.116.003.59.59 0 0 0-.517.635v.16a3.6 3.6 0 0 0 .08.543 5.2 5.2 0 0 1 .065 1.018.6.6 0 0 1-.186.305l-.013.238a7 7 0 0 0-1.031.157 7.27 7.27 0 0 0-3.706 2.117l-.196-.145a.52.52 0 0 1-.346-.039 5.4 5.4 0 0 1-.765-.69 5 5 0 0 0-.372-.395l-.12-.093a.75.75 0 0 0-.397-.158.6.6 0 0 0-.463.197.61.61 0 0 0 .148.834l.013.013c.026.027.079.067.106.093a4 4 0 0 0 .475.277 5.4 5.4 0 0 1 .848.597.6.6 0 0 1 .106.33l.183.158a7.22 7.22 0 0 0-1.137 5.121l-.25.065a.8.8 0 0 1-.254.253 4.4 4.4 0 0 1-1.018.158 2 2 0 0 0-.543.051l-.144.029h-.013v.013c-.04 0-.08.013-.106.013a.57.57 0 1 0 .339 1.086l.004-.001a1 1 0 0 0 .174-.041 2.7 2.7 0 0 0 .488-.197 7 7 0 0 1 1.018-.292.5.5 0 0 1 .305.119l.263-.039a7.43 7.43 0 0 0 3.27 4.088l-.094.238a.7.7 0 0 1 .042.33 4.2 4.2 0 0 1-.517.913c-.106.159-.199.304-.318.462a.17.17 0 0 1-.052.148c-.013.04-.04.066-.054.106a.57.57 0 0 0 1.072.382c.027-.04.051-.132.078-.132a5 5 0 0 0 .16-.53 5 5 0 0 1 .437-1.017.45.45 0 0 1 .25-.12l.132-.237a7.4 7.4 0 0 0 5.254.013l.105.212a.5.5 0 0 1 .277.183 6 6 0 0 1 .398.954c.04.172.094.344.16.542.027 0 .051.08.078.12.013.039.028.065.041.105a.568.568 0 0 0 .984-.569l-.007-.012a1 1 0 0 1-.052-.16c-.106-.146-.212-.305-.318-.45a7.4 7.4 0 0 1-.501-.9.44.44 0 0 1 .039-.343 1 1 0 0 1-.093-.225 7.44 7.44 0 0 0 3.268-4.113c.08.013.158.025.251.038a.33.33 0 0 1 .305-.106 4.7 4.7 0 0 1 1.018.28 2.6 2.6 0 0 0 .475.196.7.7 0 0 0 .187.028v.013c0 .013.053.013.093.026a.585.585 0 0 0 .635-.491.57.57 0 0 0-.483-.645l-.008-.001a.34.34 0 0 1-.157-.067h-.543a6.6 6.6 0 0 1-1.018-.186.55.55 0 0 1-.253-.238l-.251-.064a7.2 7.2 0 0 0-1.165-5.109l.211-.184a.4.4 0 0 1 .106-.317 5 5 0 0 1 .848-.597 3.3 3.3 0 0 0 .462-.277 1 1 0 0 0 .12-.093c.039-.026.08-.053.08-.08a.556.556 0 0 0-.78-.793c-.04 0-.093.054-.133.08a10 10 0 0 0-.372.395 4.8 4.8 0 0 1-.767.69.5.5 0 0 1-.344.039l-.212.158a7.37 7.37 0 0 0-4.708-2.274l-.013-.253a.45.45 0 0 1-.186-.29 5.2 5.2 0 0 1 .065-1.018 3.6 3.6 0 0 0 .08-.543v-.292a.57.57 0 0 0-.504-.506m-.778 4.408-.16 2.977h-.013a.5.5 0 0 1-.106.28.5.5 0 0 1-.687.118h-.013l-2.434-1.734a5.75 5.75 0 0 1 2.803-1.535c.212-.04.412-.08.61-.106m1.427 0a5.9 5.9 0 0 1 3.4 1.641l-2.421 1.734h-.013a.6.6 0 0 1-.277.067.494.494 0 0 1-.516-.47v-.008Zm-5.727 2.713 2.223 2.024v.013a.34.34 0 0 1 .144.253.483.483 0 0 1-.323.602l-.02.005v.013l-2.858.822a5.9 5.9 0 0 1 .834-3.732m10.013.04a6.18 6.18 0 0 1 .86 3.679l-2.87-.822-.013-.013a.47.47 0 0 1-.238-.157.49.49 0 0 1 .042-.694l.01-.01-.013-.038Zm-5.462 2.144h.912l.568.7-.199.886-.819.398-.819-.398-.212-.886Zm-2.132 2.447h.106a.55.55 0 0 1 .504.382.5.5 0 0 1-.052.28v.038l-1.127 2.74a5.84 5.84 0 0 1-2.366-2.952Zm4.87 0h.319l2.948.475a5.85 5.85 0 0 1-2.367 2.977l-1.14-2.79a.53.53 0 0 1 .24-.662m-2.327 1.199a.51.51 0 0 1 .488.256h.013l1.442 2.607a5 5 0 0 1-.569.157 5.9 5.9 0 0 1-3.214-.157l1.442-2.607h.012c.04-.093.107-.133.2-.2a.5.5 0 0 1 .186-.056'/></svg>",
+ "folder-kusto-open": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M25 10c-3.878 0-7 3.122-7 7v7h7c3.878 0 7-3.122 7-7s-3.122-7-7-7m3 4h2v6h-2zm-8 2h2v4h-2zm4 2h2v2h-2zm-8 2-3.996.02v2.322L12 24h4zm-4 6v4h4v-4zm6 0v4h4v-4z'/></svg>",
+ "folder-kusto": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M25 10c-3.878 0-7 3.122-7 7v7h7c3.878 0 7-3.122 7-7s-3.122-7-7-7m3 4h2v6h-2zm-8 2h2v4h-2zm4 2h2v2h-2zm-8 2-3.996.02v2.322L12 24h4zm-4 6v4h4v-4zm6 0v4h4v-4z'/></svg>",
+ "folder-layout-open": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M10 16h6v14h-6zm8 8h6v6h-6zm8 0h6v6h-6zm-8-8h14v6H18z'/></svg>",
+ "folder-layout": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M10 16h6v14h-6zm8 8h6v6h-6zm8 0h6v6h-6zm-8-8h14v6H18z'/></svg>",
+ "folder-lefthook-open": "<svg viewBox='0 0 32 32'><path fill='#607d8b' d='M28.97 11.998H9.444a2 2 0 0 0-1.898 1.368L4.001 24V9.998h24.003a2 2 0 0 0-2-2H15.125a2 2 0 0 1-1.28-.464L12.557 6.46a2 2 0 0 0-1.28-.464H4.002a2 2 0 0 0-2.001 2V24A2 2 0 0 0 4 26h22.003l4.806-11.214a2 2 0 0 0-1.838-2.788z'/><path fill='#f44336' d='M14 20v6h-4zm4.026-5.342-2.385.795a1.494 1.494 0 0 0-.867 2.094l.534 1.068 4.696-1.624c.014-.293-.004-.602-.004-.91a1.496 1.496 0 0 0-1.974-1.423m12.886 5.502-5.546-5.18C24.272 13.999 23.85 14 22 14v3.012a3.36 3.36 0 0 1-1.301 2.787L24 24l5.876 1.676c.606-.698.85-1.005 1.38-1.595a2.583 2.583 0 0 0-.344-3.921'/><path fill='#b71c1c' d='m10 26 4-2 4 2-4 2zm10.699-6.2a20 20 0 0 1-2.463 1.314 3.5 3.5 0 0 1-2.236.302v1.339l8.98 4.888a3.22 3.22 0 0 0 4.054-1c.333-.384.505-.582.842-.967zm-5.127-1.89 3.756-1.408a.5.5 0 0 1 .675.492 1.48 1.48 0 0 1-.832 1.42l-1.83.915c-1.399.7-2.717-1.063-1.769-1.419'/></svg>",
+ "folder-lefthook": "<svg viewBox='0 0 32 32'><path fill='#607d8b' d='m13.845 7.533-1.288-1.072a2 2 0 0 0-1.28-.464H4a2 2 0 0 0-2 2v16.002a2 2 0 0 0 2 2h24.003a2 2 0 0 0 2-2V9.997a2 2 0 0 0-2-2H15.125a2 2 0 0 1-1.28-.464'/><path fill='#f44336' d='M14 20v6h-4zm4.026-5.342-2.385.795a1.494 1.494 0 0 0-.867 2.094l.534 1.068 4.696-1.624c.014-.293-.004-.602-.004-.91a1.496 1.496 0 0 0-1.974-1.423m12.886 5.502-5.546-5.18C24.272 13.999 23.85 14 22 14v3.012a3.36 3.36 0 0 1-1.301 2.787L24 24l5.876 1.676c.606-.698.85-1.005 1.38-1.595a2.583 2.583 0 0 0-.344-3.921'/><path fill='#b71c1c' d='m10 26 4-2 4 2-4 2zm10.699-6.2a20 20 0 0 1-2.463 1.314 3.5 3.5 0 0 1-2.236.302v1.339l8.98 4.888a3.22 3.22 0 0 0 4.054-1c.333-.384.505-.582.842-.967zm-5.127-1.89 3.756-1.408a.5.5 0 0 1 .675.492 1.48 1.48 0 0 1-.832 1.42l-1.83.915c-1.399.7-2.717-1.063-1.769-1.419'/></svg>",
+ "folder-less-open": "<svg viewBox='0 0 32 32'><path fill='#0277bd' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M20 21a1 1 0 0 0-1-1 1 1 0 0 0 1-1v-5h2v-2h-2a2 2 0 0 0-2 2v4a1 1 0 0 1-1 1h-1v2h1a1 1 0 0 1 1 1v4a2 2 0 0 0 2 2h2v-2h-2Zm11-2a1 1 0 0 1-1-1v-4a2 2 0 0 0-2-2h-2v2h2v5a1 1 0 0 0 1 1 1 1 0 0 0-1 1v5h-2v2h2a2 2 0 0 0 2-2v-4a1 1 0 0 1 1-1h1v-2Z'/></svg>",
+ "folder-less": "<svg viewBox='0 0 32 32'><path fill='#0277bd' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M20 21a1 1 0 0 0-1-1 1 1 0 0 0 1-1v-5h2v-2h-2a2 2 0 0 0-2 2v4a1 1 0 0 1-1 1h-1v2h1a1 1 0 0 1 1 1v4a2 2 0 0 0 2 2h2v-2h-2Zm11-2a1 1 0 0 1-1-1v-4a2 2 0 0 0-2-2h-2v2h2v5a1 1 0 0 0 1 1 1 1 0 0 0-1 1v5h-2v2h2a2 2 0 0 0 2-2v-4a1 1 0 0 1 1-1h1v-2Z'/></svg>",
+ "folder-lib-open": "<svg viewBox='0 0 32 32'><path fill='#c0ca33' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f0f4c3' d='M23 16a3 3 0 0 0 .003-6H23a3 3 0 0 0-3 2.999V13a3 3 0 0 0 2.999 3zm0 3.973c-2.225-2.078-5.955-3.978-9-3.973v10c3.19 0 6.85 2.004 9 4 2.225-2.078 5.955-4.005 9-4V16c-3.045-.005-6.775 1.895-9 3.973'/></svg>",
+ "folder-lib": "<svg viewBox='0 0 32 32'><path fill='#c0ca33' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#f0f4c3' d='M22.931 16a3 3 0 0 0 .003-6h-.003a3 3 0 0 0-3 2.999V13a3 3 0 0 0 2.999 3zm0 3.973c-2.225-2.078-5.955-3.978-9-3.973v10c3.19 0 6.85 2.004 9 4 2.226-2.078 5.955-4.005 9-4V16c-3.044-.005-6.774 1.895-9 3.973'/></svg>",
+ "folder-link-open": "<svg viewBox='0 0 1024 1024'><path fill='#7e57c2' d='M926.912 384H302.144a64 64 0 0 0-60.736 43.776L128 768V320h768a64 64 0 0 0-64-64H483.968a64 64 0 0 1-40.96-14.848l-41.216-34.304A64 64 0 0 0 360.832 192H128a64 64 0 0 0-64 64v512a64 64 0 0 0 64 64h704l153.792-358.784A64 64 0 0 0 926.912 384'/><path fill='#d1c4e9' d='M736 320c-53.02 0-96 42.98-96 96 .104 40.593 25.729 76.733 64 90.264V576h-32c-17.673 0-32 14.327-32 32s14.327 32 32 32h32v190.547C595.489 821.653 512 768.467 512 704h64L448 576v128c0 106.039 128.942 192 288 192s288-85.961 288-192V576L896 704h64c0 64.467-83.489 117.653-192 126.547V640h32c17.673 0 32-14.327 32-32s-14.327-32-32-32h-32v-69.736c38.271-13.531 63.896-49.671 64-90.264 0-53.02-42.98-96-96-96m0 64c17.673 0 32 14.327 32 32s-14.327 32-32 32-32-14.327-32-32 14.327-32 32-32'/></svg>",
+ "folder-link": "<svg viewBox='0 0 1024 1024'><path fill='#7e57c2' d='m443.008 241.152-41.216-34.304A64 64 0 0 0 360.832 192H128a64 64 0 0 0-64 64v512a64 64 0 0 0 64 64h768a64 64 0 0 0 64-64V320a64 64 0 0 0-64-64H483.968a64 64 0 0 1-40.96-14.848'/><path fill='#d1c4e9' d='M736 320c-53.02 0-96 42.98-96 96 .104 40.593 25.729 76.733 64 90.264V576h-32c-17.673 0-32 14.327-32 32s14.327 32 32 32h32v190.547C595.489 821.653 512 768.467 512 704h64L448 576v128c0 106.039 128.942 192 288 192s288-85.961 288-192V576L896 704h64c0 64.467-83.489 117.653-192 126.547V640h32c17.673 0 32-14.327 32-32s-14.327-32-32-32h-32v-69.736c38.271-13.531 63.896-49.671 64-90.264 0-53.02-42.98-96-96-96m0 64c17.673 0 32 14.327 32 32s-14.327 32-32 32-32-14.327-32-32 14.327-32 32-32'/></svg>",
+ "folder-linux-open": "<svg viewBox='0 0 32 32'><path fill='#f9a825' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffecb3' d='M24.62 16.35c-.42.28-1.75 1.04-1.95 1.19a.825.825 0 0 1-1.14-.01c-.2-.16-1.53-.92-1.95-1.19-.48-.309-.45-.699.08-.919a6.16 6.16 0 0 1 4.91.03c.49.21.51.6.05.9Zm7.218 7.279A19.1 19.1 0 0 0 28 17.971a4.3 4.3 0 0 1-1.06-1.88c-.1-.33-.17-.67-.24-1.01a11.3 11.3 0 0 0-.7-2.609 4.06 4.06 0 0 0-3.839-2.47 4.2 4.2 0 0 0-3.95 2.4 6 6 0 0 0-.46 1.34c-.17.76-.32 1.55-.5 2.319a3.4 3.4 0 0 1-.959 1.71 19.5 19.5 0 0 0-3.88 5.348 6 6 0 0 0-.37.88c-.19.66.29 1.12.99.96.44-.09.88-.18 1.3-.31.41-.15.57-.05.67.35a6.73 6.73 0 0 0 4.24 4.498c4.119 1.56 8.928-.66 9.968-4.578.07-.27.17-.37.47-.27.46.14.93.24 1.4.35a.724.724 0 0 0 .92-.64 1.44 1.44 0 0 0-.16-.73Z'/></svg>",
+ "folder-linux": "<svg viewBox='0 0 32 32'><path fill='#f9a825' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffecb3' d='M24.62 16.35c-.42.28-1.75 1.04-1.95 1.19a.825.825 0 0 1-1.14-.01c-.2-.16-1.53-.92-1.95-1.19-.48-.309-.45-.699.08-.919a6.16 6.16 0 0 1 4.91.03c.49.21.51.6.05.9Zm7.218 7.279A19.1 19.1 0 0 0 28 17.971a4.3 4.3 0 0 1-1.06-1.88c-.1-.33-.17-.67-.24-1.01a11.3 11.3 0 0 0-.7-2.609 4.06 4.06 0 0 0-3.839-2.47 4.2 4.2 0 0 0-3.95 2.4 6 6 0 0 0-.46 1.34c-.17.76-.32 1.55-.5 2.319a3.4 3.4 0 0 1-.959 1.71 19.5 19.5 0 0 0-3.88 5.348 6 6 0 0 0-.37.88c-.19.66.29 1.12.99.96.44-.09.88-.18 1.3-.31.41-.15.57-.05.67.35a6.73 6.73 0 0 0 4.24 4.498c4.119 1.56 8.928-.66 9.968-4.578.07-.27.17-.37.47-.27.46.14.93.24 1.4.35a.724.724 0 0 0 .92-.64 1.44 1.44 0 0 0-.16-.73Z'/></svg>",
+ "folder-liquibase-open": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M23 12c-3.865 0-6.998 1.343-6.998 3s3.133 3 6.999 3S30 16.657 30 15s-3.134-3-7-3m7 5c-.222.2-.438.417-.703.582-.84.466-1.767.724-2.7.957-1.64.41-3.07.673-5.645 1.363-1.232.33-3.29 1.07-4.304 2.494-.686.961-.644 2.116-.646 2.604.438-.29.91-.82 3.26-1.51a61 61 0 0 1 3.145-.777c.785-.175 1.57-.329 2.354-.516 2.853-.697 3.631-1.07 4.325-1.539.608-.413.916-.91.914-1.658 0 0-.024-1.342 0-2m0 4.453c-1.39.78-2.246 1.215-4.325 1.682-1.767.4-3.53.81-5.295 1.22-1.188.282-3.555.975-4.18 2.145-.286.536-.295 1.164.169 1.643.05.05.56.524.738.586 0 0 .218-.106.836-.393 1.16-.536 2.396-.858 3.64-1.162 1.824-.425 3.659-.818 5.448-1.383.82-.252 1.605-.582 2.26-1.13C29.755 24.271 30 24 30 23Zm0 4.147s-.505.507-2.014 1.076C24.213 27.979 22.03 28 19.027 29.15c-.15.058-.388.147-.532.211.124.05.717.236.711.229.96.243 2.365.416 3.795.41.534-.013 4.286.128 6.163-1.39.384-.34.833-.61.836-1.61Z'/></svg>",
+ "folder-liquibase": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M23 12a7 3 0 0 0-7 3 7 3 0 0 0 7 3 7 3 0 0 0 7-3 7 3 0 0 0-7-3m7 5c-.222.2-.438.417-.703.582-.84.466-1.767.724-2.701.957-1.639.41-3.069.673-5.645 1.363-1.232.33-3.29 1.07-4.305 2.494-.686.961-.644 2.116-.646 2.604.438-.29.91-.82 3.26-1.51a61 61 0 0 1 3.146-.777c.785-.175 1.57-.329 2.354-.516 2.854-.697 3.632-1.07 4.326-1.539.608-.413.916-.91.914-1.658 0 0-.024-1.342 0-2m0 4.453c-1.39.78-2.246 1.215-4.326 1.682-1.767.4-3.53.81-5.295 1.22-1.188.282-3.556.975-4.18 2.145-.287.536-.296 1.164.168 1.643.05.05.56.524.738.586 0 0 .218-.106.836-.393 1.16-.536 2.397-.858 3.64-1.162 1.825-.425 3.66-.818 5.45-1.383.819-.252 1.605-.582 2.26-1.13C29.755 24.271 30 24 30 23zm0 4.147s-.505.507-2.014 1.076c-3.774 1.303-5.957 1.324-8.96 2.474-.15.058-.388.147-.532.211.124.05.717.236.711.229.96.243 2.365.416 3.795.41.534-.013 4.287.128 6.164-1.39.384-.34.833-.61.836-1.61z'/></svg>",
+ "folder-log-open": "<svg viewBox='0 0 32 32'><path fill='#afb42b' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f0f4c3' d='M30.337 14H17.663A1.663 1.663 0 0 0 16 15.663v10.674A1.663 1.663 0 0 0 17.663 28h12.674A1.663 1.663 0 0 0 32 26.337V15.663A1.663 1.663 0 0 0 30.337 14M26 26h-6v-2h6Zm2-4h-8v-2h8Zm0-4h-8v-2h8Z'/></svg>",
+ "folder-log": "<svg viewBox='0 0 32 32'><path fill='#afb42b' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#afb42b' d='M20 14h8v2h-8zm0 4h8v2h-8zm0 4h6v2h-6z'/><path fill='#f0f4c3' d='M30.337 14H17.663A1.663 1.663 0 0 0 16 15.663v10.674A1.663 1.663 0 0 0 17.663 28h12.674A1.663 1.663 0 0 0 32 26.337V15.663A1.663 1.663 0 0 0 30.337 14M26 26h-6v-2h6Zm2-4h-8v-2h8Zm0-4h-8v-2h8Z'/></svg>",
+ "folder-lottie-open": "<svg viewBox='0 0 32 32'><path fill='#00bfa5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#a7ffeb' d='M28 10H18a4 4 0 0 0-4 4v10a4 4 0 0 0 4 4h10a4 4 0 0 0 4-4V14a4 4 0 0 0-4-4m0 5.563a.48.48 0 0 1-.437.464c-1.541.201-2.457 1.49-3.715 3.503-1.233 1.971-2.619 4.19-5.323 4.446a.495.495 0 0 1-.525-.501v-1.038a.48.48 0 0 1 .437-.465c1.541-.2 2.457-1.489 3.715-3.502 1.233-1.971 2.619-4.19 5.323-4.446a.495.495 0 0 1 .525.501Z'/></svg>",
+ "folder-lottie": "<svg viewBox='0 0 32 32'><path fill='#00bfa5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#a7ffeb' d='M28 10H18a4 4 0 0 0-4 4v10a4 4 0 0 0 4 4h10a4 4 0 0 0 4-4V14a4 4 0 0 0-4-4m0 5.563a.48.48 0 0 1-.437.464c-1.541.201-2.457 1.49-3.715 3.503-1.233 1.971-2.619 4.19-5.323 4.446a.495.495 0 0 1-.525-.501v-1.038a.48.48 0 0 1 .437-.465c1.541-.2 2.457-1.489 3.715-3.502 1.233-1.971 2.619-4.19 5.323-4.446a.495.495 0 0 1 .525.501Z'/></svg>",
+ "folder-lua-open": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><circle cx='29' cy='13' r='3' fill='#b3e5fc'/><path fill='#b3e5fc' d='M19.703 14.594a7.703 7.703 0 1 0 7.703 7.703 7.703 7.703 0 0 0-7.703-7.703M21 24a3 3 0 1 1 3-3 3 3 0 0 1-3 3'/></svg>",
+ "folder-lua": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><circle cx='29' cy='13' r='3' fill='#b3e5fc'/><path fill='#b3e5fc' d='M19.703 14.594a7.703 7.703 0 1 0 7.703 7.703 7.703 7.703 0 0 0-7.703-7.703M21 24a3 3 0 1 1 3-3 3 3 0 0 1-3 3'/></svg>",
+ "folder-luau-open": "<svg fill='none' viewBox='0 0 32 32'><path fill='#42a5f5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' fill-rule='evenodd' d='M18.381 12 31 15.381 27.619 28 15 24.619zm8.095 3.86 2.524.676-.676 2.524-2.524-.677z' clip-rule='evenodd'/></svg>",
+ "folder-luau": "<svg fill='none' viewBox='0 0 32 32'><path fill='#42a5f5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' fill-rule='evenodd' d='M18.381 12 31 15.381 27.619 28 15 24.619zm8.095 3.86 2.524.676-.676 2.524-2.524-.677z' clip-rule='evenodd'/></svg>",
+ "folder-macos-open": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#cfd8dc' d='M30.64 27.499c-.82 1.24-1.687 2.45-3.008 2.47-1.322.03-1.746-.79-3.245-.79-1.508 0-1.972.77-3.224.82-1.292.05-2.268-1.32-3.096-2.53-1.687-2.47-2.979-7.02-1.243-10.08a4.81 4.81 0 0 1 4.063-2.51c1.262-.02 2.465.87 3.244.87.77 0 2.229-1.07 3.757-.91a4.56 4.56 0 0 1 3.59 1.98 4.57 4.57 0 0 0-2.12 3.81A4.41 4.41 0 0 0 32 24.67a11.1 11.1 0 0 1-1.36 2.83Zm-5.632-16a4.46 4.46 0 0 1 2.9-1.499 4.42 4.42 0 0 1-1.026 3.19 3.58 3.58 0 0 1-2.91 1.42 4.3 4.3 0 0 1 1.036-3.11Z'/></svg>",
+ "folder-macos": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#cfd8dc' d='M30.64 27.499c-.82 1.24-1.687 2.45-3.008 2.47-1.322.03-1.746-.79-3.245-.79-1.508 0-1.972.77-3.224.82-1.292.05-2.268-1.32-3.096-2.53-1.687-2.47-2.979-7.02-1.243-10.08a4.81 4.81 0 0 1 4.063-2.51c1.262-.02 2.465.87 3.244.87.77 0 2.229-1.07 3.757-.91a4.56 4.56 0 0 1 3.59 1.98 4.57 4.57 0 0 0-2.12 3.81A4.41 4.41 0 0 0 32 24.67a11.1 11.1 0 0 1-1.36 2.83Zm-5.632-16a4.46 4.46 0 0 1 2.9-1.499 4.42 4.42 0 0 1-1.026 3.19 3.58 3.58 0 0 1-2.91 1.42 4.3 4.3 0 0 1 1.036-3.11Z'/></svg>",
+ "folder-mail-open": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M14 16v10a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V16a2 2 0 0 0-2-2H16a2 2 0 0 0-2 2m16 2-7 4-7-4v-2l7 4 7-4Z'/></svg>",
+ "folder-mail": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M14 16v10a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V16a2 2 0 0 0-2-2H16a2 2 0 0 0-2 2m16 2-7 4-7-4v-2l7 4 7-4Z'/></svg>",
+ "folder-mappings-open": "<svg viewBox='0 0 32 32'><path fill='#afb42b' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f0f4c3' d='M18 16h2v2h-2zm4 0h6v2h-6zm-4 4h2v2h-2zm4 0h6v2h-6zm-4 4h2v2h-2zm4 0h6v2h-6z'/><path fill='#f0f4c3' d='M14 12.5v17a.5.5 0 0 0 .5.5h17a.5.5 0 0 0 .5-.5v-17a.5.5 0 0 0-.5-.5h-17a.5.5 0 0 0-.5.5M30 28H16V14h14Z'/></svg>",
+ "folder-mappings": "<svg viewBox='0 0 32 32'><path fill='#afb42b' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#f0f4c3' d='M18 16h2v2h-2zm4 0h6v2h-6zm-4 4h2v2h-2zm4 0h6v2h-6zm-4 4h2v2h-2zm4 0h6v2h-6z'/><path fill='#f0f4c3' d='M14 12.5v17a.5.5 0 0 0 .5.5h17a.5.5 0 0 0 .5-.5v-17a.5.5 0 0 0-.5-.5h-17a.5.5 0 0 0-.5.5M30 28H16V14h14Z'/></svg>",
+ "folder-markdown-open": "<svg viewBox='0 0 32 32'><path fill='#0277bd' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M22 18v10h-4v-6l-2 2-2-2v6h-4V18h4l2 2 2-2zm10 6-4 5-4-5h2v-6h4v6z'/></svg>",
+ "folder-markdown": "<svg viewBox='0 0 32 32'><path fill='#0277bd' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M22 18v10h-4v-6l-2 2-2-2v6h-4V18h4l2 2 2-2zm10 6-4 5-4-5h2v-6h4v6z'/></svg>",
+ "folder-mercurial-open": "<svg viewBox='0 0 32 32'><path fill='#78909c' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#cfd8dc' d='M31.983 20.728c.31-5.49-3.708-11.404-9.888-10.665a6.1 6.1 0 0 0-5.15 3.379c-1.237 2.64.102 6.125 4.12 7.286 2.472.74 2.987 1.795 2.678 3.062-.31 1.162-1.133 2.43-1.34 3.485a3 3 0 0 0-.102.95c.103 2.324 4.738 3.274 8.343-2.956a9.35 9.35 0 0 0 1.34-4.54Z'/><circle cx='16' cy='26' r='4' fill='#cfd8dc'/><circle cx='12' cy='18' r='2' fill='#cfd8dc'/></svg>",
+ "folder-mercurial": "<svg viewBox='0 0 32 32'><path fill='#78909c' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#cfd8dc' d='M31.983 20.728c.31-5.49-3.708-11.404-9.888-10.665a6.1 6.1 0 0 0-5.15 3.379c-1.237 2.64.102 6.125 4.12 7.286 2.472.74 2.987 1.795 2.678 3.062-.31 1.162-1.133 2.43-1.34 3.485a3 3 0 0 0-.102.95c.103 2.324 4.738 3.274 8.343-2.956a9.35 9.35 0 0 0 1.34-4.54Z'/><circle cx='16' cy='26' r='4' fill='#cfd8dc'/><circle cx='12' cy='18' r='2' fill='#cfd8dc'/></svg>",
+ "folder-messages-open": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fff9c4' d='M31 12H15a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h3v3a1 1 0 0 0 1 1h.697a1 1 0 0 0 .555-.168L26 26h5a1 1 0 0 0 1-1V13a1 1 0 0 0-1-1m-15 6h8v2h-8Zm10 6H16v-2h10Zm4-8H16v-2h14Z'/></svg>",
+ "folder-messages": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fff9c4' d='M31 12H15a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h3v3a1 1 0 0 0 1 1h.697a1 1 0 0 0 .555-.168L26 26h5a1 1 0 0 0 1-1V13a1 1 0 0 0-1-1m-15 6h8v2h-8Zm10 6H16v-2h10Zm4-8H16v-2h14Z'/></svg>",
+ "folder-meta-open": "<svg viewBox='0 0 32 32'><path fill='#7cb342' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#dcedc8' d='M16 16h-2v13a1 1 0 0 0 1 1h13v-2H16Z'/><path fill='#dcedc8' d='M31 12H19a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V13a1 1 0 0 0-1-1m-5 12h-6v-2h6Zm4-4H20v-2h10Zm0-4H20v-2h10Z'/></svg>",
+ "folder-meta": "<svg viewBox='0 0 32 32'><path fill='#7cb342' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#dcedc8' d='M16 16h-2v13a1 1 0 0 0 1 1h13v-2H16Z'/><path fill='#dcedc8' d='M31 12H19a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V13a1 1 0 0 0-1-1m-5 12h-6v-2h6Zm4-4H20v-2h10Zm0-4H20v-2h10Z'/></svg>",
+ "folder-middleware-open": "<svg viewBox='0 0 32 32'><path fill='#5c6bc0' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c5cae9' d='M30.107 20H32v-4a2 2 0 0 0-2-2h-4v-2a2 2 0 0 0-4 0v2h-4a2 2 0 0 0-2 2v4h-2a2 2 0 0 0 0 4h2v4a2 2 0 0 0 2 2h4v-1.893a2.074 2.074 0 0 1 1.664-2.08A2 2 0 0 1 26 28v2h4a2 2 0 0 0 2-2v-4h-2a2 2 0 0 1-1.973-2.336A2.074 2.074 0 0 1 30.107 20'/></svg>",
+ "folder-middleware": "<svg viewBox='0 0 32 32'><path fill='#5c6bc0' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c5cae9' d='M30.107 20H32v-4a2 2 0 0 0-2-2h-4v-2a2 2 0 0 0-4 0v2h-4a2 2 0 0 0-2 2v4h-2a2 2 0 0 0 0 4h2v4a2 2 0 0 0 2 2h4v-1.893a2.074 2.074 0 0 1 1.664-2.08A2 2 0 0 1 26 28v2h4a2 2 0 0 0 2-2v-4h-2a2 2 0 0 1-1.973-2.336A2.074 2.074 0 0 1 30.107 20'/></svg>",
+ "folder-mjml-open": "<svg viewBox='0 0 32 32'><path fill='#ff5722' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><rect width='12' height='4' x='14' y='24' fill='#ffccbc' rx='2'/><rect width='12' height='4' x='20' y='18' fill='#ffccbc' rx='2'/><rect width='12' height='4' x='14' y='12' fill='#ffccbc' rx='2'/><circle cx='30' cy='26' r='2' fill='#ffccbc'/><circle cx='16' cy='20' r='2' fill='#ffccbc'/><circle cx='30' cy='14' r='2' fill='#ffccbc'/></svg>",
+ "folder-mjml": "<svg viewBox='0 0 32 32'><path fill='#ff5722' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><rect width='12' height='4' x='14' y='24' fill='#ffccbc' rx='2'/><rect width='12' height='4' x='20' y='18' fill='#ffccbc' rx='2'/><rect width='12' height='4' x='14' y='12' fill='#ffccbc' rx='2'/><circle cx='30' cy='26' r='2' fill='#ffccbc'/><circle cx='16' cy='20' r='2' fill='#ffccbc'/><circle cx='30' cy='14' r='2' fill='#ffccbc'/></svg>",
+ "folder-mobile-open": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M12 14v10a2 2 0 0 0 2 2h8v-2h-6V14h12v2h4v-2a2 2 0 0 0-2-2H14a2 2 0 0 0-2 2'/><path fill='#ffcdd2' d='M24 19v8a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-8a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1m6 7h-4v-6h4Z'/></svg>",
+ "folder-mobile": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M12 14v10a2 2 0 0 0 2 2h8v-2h-6V14h12v2h4v-2a2 2 0 0 0-2-2H14a2 2 0 0 0-2 2'/><path fill='#ffcdd2' d='M24 19v8a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-8a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1m6 7h-4v-6h4Z'/></svg>",
+ "folder-mock-open": "<svg viewBox='0 0 32 32'><path fill='#8d6e63' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#d7ccc8' d='M16 24.667V28h3.333l9.83-9.83-3.333-3.333Zm15.74-9.074a.885.885 0 0 0 .001-1.252v-.001l-2.08-2.08a.885.885 0 0 0-1.253-.001l-1.627 1.627 3.333 3.333Z'/></svg>",
+ "folder-mock": "<svg viewBox='0 0 32 32'><path fill='#8d6e63' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#d7ccc8' d='M16 24.667V28h3.333l9.83-9.83-3.333-3.333Zm15.74-9.074a.885.885 0 0 0 .001-1.252v-.001l-2.08-2.08a.885.885 0 0 0-1.253-.001l-1.627 1.627 3.333 3.333Z'/></svg>",
+ "folder-mojo-open": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffccbc' d='M30.495 17.1a9 9 0 0 0-.88-.913c-.765-.663-1.638-1.14-2.365-1.84A5.4 5.4 0 0 1 26.253 8a7.5 7.5 0 0 0-2.845 1.462 8.59 8.59 0 0 0-2.743 9.877.8.8 0 0 1 .094.364.62.62 0 0 1-.4.556.72.72 0 0 1-.757-.135 5.42 5.42 0 0 1-.785-5.87 7.8 7.8 0 0 0-2.802 6.469 8 8 0 0 0 .335 1.669 7.2 7.2 0 0 0 .808 1.918 8.02 8.02 0 0 0 5.675 3.566 8.8 8.8 0 0 0 6.934-1.769 6.44 6.44 0 0 0 1.746-7.324l-.145-.285a12 12 0 0 0-.88-1.398m-3.61 6.99a4 4 0 0 1-1.258.662 3.33 3.33 0 0 1-3.318-.913 3.13 3.13 0 0 0 2.415-2.275 6.2 6.2 0 0 0-.32-2.467 4 4 0 0 1 .19-2.282 7 7 0 0 0 .727 1.17c.873 1.112 2.256 1.597 2.554 3.109a1.7 1.7 0 0 1 .073.47 3.34 3.34 0 0 1-1.063 2.525Z'/></svg>",
+ "folder-mojo": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffccbc' d='M30.495 17.1a9 9 0 0 0-.88-.913c-.765-.663-1.638-1.14-2.365-1.84A5.4 5.4 0 0 1 26.253 8a7.5 7.5 0 0 0-2.845 1.462 8.59 8.59 0 0 0-2.743 9.877.8.8 0 0 1 .094.364.62.62 0 0 1-.4.556.72.72 0 0 1-.757-.135 5.42 5.42 0 0 1-.785-5.87 7.8 7.8 0 0 0-2.802 6.469 8 8 0 0 0 .335 1.669 7.2 7.2 0 0 0 .808 1.918 8.02 8.02 0 0 0 5.675 3.566 8.8 8.8 0 0 0 6.934-1.769 6.44 6.44 0 0 0 1.746-7.324l-.145-.285a12 12 0 0 0-.88-1.398m-3.61 6.99a4 4 0 0 1-1.258.662 3.33 3.33 0 0 1-3.318-.913 3.13 3.13 0 0 0 2.415-2.275 6.2 6.2 0 0 0-.32-2.467 4 4 0 0 1 .19-2.282 7 7 0 0 0 .727 1.17c.873 1.112 2.256 1.597 2.554 3.109a1.7 1.7 0 0 1 .073.47 3.34 3.34 0 0 1-1.063 2.525Z'/></svg>",
+ "folder-moon-open": "<svg viewBox='0 0 32 32'><path fill='#7e57c2' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#d1c4e9' d='M32 14a3.995 3.995 0 0 0-7.64-1.64A7.7 7.7 0 0 0 22 12a8 8 0 1 0 8 8 7.7 7.7 0 0 0-.36-2.36A3.99 3.99 0 0 0 32 14m-8 8a4 4 0 0 1 0-8 4 4 0 0 0 4 4 4 4 0 0 1-4 4'/></svg>",
+ "folder-moon": "<svg viewBox='0 0 32 32'><path fill='#7e57c2' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#d1c4e9' d='M32 14a3.995 3.995 0 0 0-7.64-1.64A7.7 7.7 0 0 0 22 12a8 8 0 1 0 8 8 7.7 7.7 0 0 0-.36-2.36A3.99 3.99 0 0 0 32 14m-8 8a4 4 0 0 1 0-8 4 4 0 0 0 4 4 4 4 0 0 1-4 4'/></svg>",
+ "folder-netlify-open": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#a7ffeb' d='M22 16h-4v6h2v-4h1.5a.5.5 0 0 1 .5.5V22h2v-4a2 2 0 0 0-2-2'/><rect width='6' height='2' x='26' y='18' fill='#a7ffeb' rx='.5'/><rect width='2' height='6' x='20' y='8' fill='#a7ffeb' rx='.5'/><rect width='6' height='2' x='10' y='18' fill='#a7ffeb' rx='.5'/><rect width='2' height='6' x='20' y='24' fill='#a7ffeb' rx='.5'/><path fill='#a7ffeb' d='m13 12.172 1.414-1.414 2.828 2.828L15.828 15zM15.828 23l1.414 1.414-2.828 2.828L13 25.828z'/></svg>",
+ "folder-netlify": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#a7ffeb' d='M22 16h-4v6h2v-4h1.5a.5.5 0 0 1 .5.5V22h2v-4a2 2 0 0 0-2-2'/><rect width='6' height='2' x='26' y='18' fill='#a7ffeb' rx='.5'/><rect width='2' height='6' x='20' y='8' fill='#a7ffeb' rx='.5'/><rect width='6' height='2' x='10' y='18' fill='#a7ffeb' rx='.5'/><rect width='2' height='6' x='20' y='24' fill='#a7ffeb' rx='.5'/><path fill='#a7ffeb' d='m13 12.172 1.414-1.414 2.828 2.828L15.828 15zM15.828 23l1.414 1.414-2.828 2.828L13 25.828z'/></svg>",
+ "folder-next-open": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#cfd8dc' d='M24 12a8 8 0 1 0 3.969 14.94L22 19v4.5a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-7a.5.5 0 0 1 .5-.5h1.232a.5.5 0 0 1 .416.223l6.736 10.103A7.993 7.993 0 0 0 24 12m4 8h-2v-3.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5Z'/></svg>",
+ "folder-next": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#cfd8dc' d='M24 12a8 8 0 1 0 3.969 14.94L22 19v4.5a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-7a.5.5 0 0 1 .5-.5h1.232a.5.5 0 0 1 .416.223l6.736 10.103A7.993 7.993 0 0 0 24 12m4 8h-2v-3.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5Z'/></svg>",
+ "folder-ngrx-actions-open.clone": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#e1bee7' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
+ "folder-ngrx-actions.clone": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#e1bee7' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
+ "folder-ngrx-effects-open.clone": "<svg viewBox='0 0 32 32'><path fill='#00bcd4' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2ebf2' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
+ "folder-ngrx-effects.clone": "<svg viewBox='0 0 32 32'><path fill='#00bcd4' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2ebf2' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
+ "folder-ngrx-entities-open.clone": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffecb3' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
+ "folder-ngrx-entities.clone": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffecb3' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
+ "folder-ngrx-reducer-open.clone": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
+ "folder-ngrx-reducer.clone": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
+ "folder-ngrx-selectors-open.clone": "<svg viewBox='0 0 32 32'><path fill='#ff6e40' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffccbc' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
+ "folder-ngrx-selectors.clone": "<svg viewBox='0 0 32 32'><path fill='#ff6e40' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffccbc' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
+ "folder-ngrx-state-open.clone": "<svg viewBox='0 0 32 32'><path fill='#9e9d24' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f0f4c3' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
+ "folder-ngrx-state.clone": "<svg viewBox='0 0 32 32'><path fill='#9e9d24' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#f0f4c3' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
+ "folder-ngrx-store-open": "<svg viewBox='0 0 32 32'><path fill='#8bc34a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#dcedc8' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
+ "folder-ngrx-store": "<svg viewBox='0 0 32 32'><path fill='#8bc34a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#dcedc8' d='m23 9-9 3 1 12 8 4 8-4 1-12Zm-1.869 2.785a2.3 2.3 0 0 1 1.124.324 5.3 5.3 0 0 0 1.214.305 6.63 6.63 0 0 1 4.433 2.834c.448.875.356 1.348-.33 1.7-.59.303-1.799.157-3.554-.432l-1.481-.497-.527.199a3.53 3.53 0 0 0-1.84 1.73 2.9 2.9 0 0 0-.218 1.622 2.9 2.9 0 0 0 .41 1.645c.35.613 1.15 1.395 1.287 1.259.038-.038-.044-.287-.182-.553a1.1 1.1 0 0 1-.178-.595c.038-.061.4.165.802.504a5.6 5.6 0 0 0 2.898 1.333c.787.081.967-.064.377-.307a1.8 1.8 0 0 1-.547-.363c-.23-.252-.243-.244.738-.462a4.6 4.6 0 0 0 1.887-.996c.023-.073-.173-.102-.495-.077-.292.023-.53-.006-.53-.067a3 3 0 0 1 .53-.656 4.93 4.93 0 0 0 1.585-3.596l.08-1.114.258.53a3.2 3.2 0 0 1 .133 2.148c-.168.605-.056.672.253.152.382-.644.505-.543.438.364a3.95 3.95 0 0 1-1.183 2.561c-.627.68-.551.803.207.34.731-.449.83-.379.453.325a6.08 6.08 0 0 1-3.782 2.831 6.2 6.2 0 0 1-2.487.16 7.33 7.33 0 0 1-5.44-3.849 13 13 0 0 0-.836-1.437c-.403-.542-.436-.785-.166-1.197a.92.92 0 0 0 .111-.73c-.257-1.451-.248-1.496.337-2.088.512-.513.543-.581.543-1.182 0-.52.052-.69.29-.925a1 1 0 0 1 .561-.291 2.88 2.88 0 0 0 1.624-.865 1.67 1.67 0 0 1 1.203-.587'/></svg>",
+ "folder-node-open": "<svg viewBox='0 0 32 32'><path fill='#8bc34a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#dcedc8' d='m25 12-7 4.072v7.854L25 28l7-4.074v-7.854Z'/></svg>",
+ "folder-node": "<svg viewBox='0 0 32 32'><path fill='#8bc34a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#dcedc8' d='m25 12-7 4.072v7.854L25 28l7-4.074v-7.854Z'/></svg>",
+ "folder-nuxt-open": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#00e676' d='M22.498 27.998h6.927a1.56 1.56 0 0 0 1.127-.617 1.3 1.3 0 0 0 .188-.631 1.26 1.26 0 0 0-.188-.618l-4.685-8.053a1.14 1.14 0 0 0-.443-.443 1.5 1.5 0 0 0-.67-.188 1.29 1.29 0 0 0-1.074.63l-1.182 2.054-2.376-3.986a1.3 1.3 0 0 0-.43-.497 1.52 1.52 0 0 0-1.247 0 1.5 1.5 0 0 0-.51.497l-5.799 9.986a1.2 1.2 0 0 0-.134.618 1.24 1.24 0 0 0 .134.63 1.3 1.3 0 0 0 .497.43 1.3 1.3 0 0 0 .63.188h4.363a4.26 4.26 0 0 0 3.88-2.241l2.12-3.692 1.114-1.933 3.436 5.866h-4.564Zm-4.9-2h-3.04l4.533-7.8 2.28 3.893-1.52 2.667a2.34 2.34 0 0 1-2.267 1.24Z'/></svg>",
+ "folder-nuxt": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#00e676' d='M22.498 27.998h6.927a1.56 1.56 0 0 0 1.127-.617 1.3 1.3 0 0 0 .188-.631 1.26 1.26 0 0 0-.188-.618l-4.685-8.053a1.14 1.14 0 0 0-.443-.443 1.5 1.5 0 0 0-.67-.188 1.29 1.29 0 0 0-1.074.63l-1.182 2.054-2.376-3.986a1.3 1.3 0 0 0-.43-.497 1.52 1.52 0 0 0-1.247 0 1.5 1.5 0 0 0-.51.497l-5.799 9.986a1.2 1.2 0 0 0-.134.618 1.24 1.24 0 0 0 .134.63 1.3 1.3 0 0 0 .497.43 1.3 1.3 0 0 0 .63.188h4.363a4.26 4.26 0 0 0 3.88-2.241l2.12-3.692 1.114-1.933 3.436 5.866h-4.564Zm-4.9-2h-3.04l4.533-7.8 2.28 3.893-1.52 2.667a2.34 2.34 0 0 1-2.267 1.24Z'/></svg>",
+ "folder-obsidian-open": "<svg fill-rule='evenodd' clip-rule='evenodd' image-rendering='optimizeQuality' shape-rendering='geometricPrecision' text-rendering='geometricPrecision' viewBox='0 0 512 512'><path fill='#673ab7' d='M463.47 192H151.06c-13.77 0-26 8.82-30.35 21.89L64 384V160h384c0-17.67-14.33-32-32-32H241.98a32 32 0 0 1-20.48-7.42l-20.6-17.15c-5.75-4.8-13-7.43-20.48-7.43H64c-17.67 0-32 14.33-32 32v256c0 17.67 14.33 32 32 32h352l76.88-179.39c1.7-3.98 2.59-8.28 2.59-12.61 0-17.67-14.33-32-32-32'/><g fill='#d1c4e9'><path d='M336.2 318.24c8.07-1.51 12.6-2.02 21.66-2.02-34.18-89.72 48.95-139.27 18.63-155.11-17-8.88-52.32 37.77-72.93 56.26l-10.67 37.41c19.77 16.2 36.25 39.63 43.31 63.46m75.04 128.91c13.05 3.85 26.66-5.92 28.52-19.42 1.35-9.81 3.51-20.65 8.24-30.94-2.66-7.51-25.72-71.18-104.74-56.39 7.6 31.4-4.15 64.54-22.83 91.02 33.14.31 59.29 6.45 90.81 15.73'/><path d='M478.76 346.86c7.02-12.43-16.61-22.28-28.74-50.78-10.52-24.69 4.93-53.82-8.18-66.23l-40.17-38.02c-14.09 38.27-40.29 56.91-17.12 123.38 37.13 6.98 67.48 27.2 77.55 58.42 0 0 13.67-21.49 16.66-26.77m-221.26 5.78c-8.21 18.67 17.96 36.81 43.46 63.29 29-40.73 24.17-88.95-15.12-127.91z'/></g></svg>",
+ "folder-obsidian": "<svg fill-rule='evenodd' clip-rule='evenodd' image-rendering='optimizeQuality' shape-rendering='geometricPrecision' text-rendering='geometricPrecision' viewBox='0 0 512 512'><path fill='#673ab7' d='m221.5 120.58-20.6-17.16A32 32 0 0 0 180.42 96H64c-17.67 0-32 14.33-32 32v256c0 17.67 14.33 32 32 32h384c17.67 0 32-14.33 32-32V160c0-17.67-14.33-32-32-32H241.98a32 32 0 0 1-20.48-7.42'/><g fill='#d1c4e9'><path d='M336.2 318.24c8.07-1.51 12.6-2.02 21.66-2.02-34.18-89.72 48.95-139.27 18.63-155.11-17-8.88-52.32 37.77-72.93 56.26l-10.67 37.41c19.77 16.2 36.25 39.63 43.31 63.46m75.04 128.91c13.05 3.85 26.66-5.92 28.52-19.42 1.35-9.81 3.51-20.65 8.24-30.94-2.66-7.51-25.72-71.18-104.74-56.39 7.6 31.4-4.15 64.54-22.83 91.02 33.14.31 59.29 6.45 90.81 15.73'/><path d='M478.76 346.86c7.02-12.43-16.61-22.28-28.74-50.78-10.52-24.69 4.93-53.82-8.18-66.23l-40.17-38.02c-14.09 38.27-40.29 56.91-17.12 123.38 37.13 6.98 67.48 27.2 77.55 58.42 0 0 13.67-21.49 16.66-26.77m-221.26 5.78c-8.21 18.67 17.96 36.81 43.46 63.29 29-40.73 24.17-88.95-15.12-127.91z'/></g></svg>",
+ "folder-open": "<svg viewBox='0 0 16 16'><path fill='#90a4ae' d='M14.483 6H4.721a1 1 0 0 0-.949.684L2 12V5h12a1 1 0 0 0-1-1H7.562a1 1 0 0 1-.64-.232l-.644-.536A1 1 0 0 0 5.638 3H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h11l2.403-5.606A1 1 0 0 0 14.483 6'/></svg>",
+ "folder-other-open": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffccbc' d='M22 10a10 10 0 1 0 10 10 10 10 0 0 0-10-10m-6 12.125a2 2 0 1 1 2-2 2 2 0 0 1-2 2m6 0a2 2 0 1 1 2-2 2 2 0 0 1-2 2m6 0a2 2 0 1 1 2-2 2 2 0 0 1-2 2'/></svg>",
+ "folder-other": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffccbc' d='M22 10a10 10 0 1 0 10 10 10 10 0 0 0-10-10m-6 12.125a2 2 0 1 1 2-2 2 2 0 0 1-2 2m6 0a2 2 0 1 1 2-2 2 2 0 0 1-2 2m6 0a2 2 0 1 1 2-2 2 2 0 0 1-2 2'/></svg>",
+ "folder-packages-open": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M31.2 12.933 29.6 10.8A2 2 0 0 0 28 10h-8a2 2 0 0 0-1.6.8l-1.6 2.133a4 4 0 0 0-.8 2.4V26a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V15.333a4 4 0 0 0-.8-2.4M20 12h8l1.5 2h-11Zm6 10v4h-4v-4h-4l6-6 6 6Z'/></svg>",
+ "folder-packages": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M31.2 12.933 29.6 10.8A2 2 0 0 0 28 10h-8a2 2 0 0 0-1.6.8l-1.6 2.133a4 4 0 0 0-.8 2.4V26a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V15.333a4 4 0 0 0-.8-2.4M20 12h8l1.5 2h-11Zm6 10v4h-4v-4h-4l6-6 6 6Z'/></svg>",
+ "folder-pdf-open": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M22.433 17.937a14.7 14.7 0 0 1-1.015 2.407 9 9 0 0 0-.494 1.036l.109-.041a18.3 18.3 0 0 1 3.342-.924q-.218-.151-.42-.324a6.25 6.25 0 0 1-1.522-2.154m6.474 3.812a1.14 1.14 0 0 1-.9.299 7.2 7.2 0 0 1-2.985-.739 20 20 0 0 0-4.047.75l-.184.07c-1.243 2.123-2.162 3.07-2.974 3.07a1 1 0 0 1-.44-.104l-.48-.315-.023-.053a.83.83 0 0 1-.053-.538 3.8 3.8 0 0 1 1.883-2.118 5.5 5.5 0 0 1 .89-.49c.296-.522.616-1.128.952-1.804a17.3 17.3 0 0 0 1.087-2.924l-.005-.012a4.94 4.94 0 0 1-.219-3.265c.11-.386.42-.776.794-.776h.237a.85.85 0 0 1 .608.246c.659.659.357 2.267.022 3.595l-.035.141a5.8 5.8 0 0 0 1.596 2.556 8 8 0 0 0 .862.586 12 12 0 0 1 1.298-.074c1.24 0 1.986.224 2.277.686a.8.8 0 0 1 .124.551.96.96 0 0 1-.285.662M30 10H16a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V12a2 2 0 0 0-2-2m-1.486 11.043c-.112-.106-.522-.356-1.918-.356a.26.26 0 0 0-.23.1 5.4 5.4 0 0 0 1.902.512 1.3 1.3 0 0 0 .196-.015l.034-.006c.048-.014.08-.03.09-.13a1 1 0 0 0-.074-.105m-9.185 1.455a4 4 0 0 0-.475.314 3.66 3.66 0 0 0-1.215 1.692c.455-.156 1.043-.812 1.692-2.006Zm3.016-6.906.056-.037c.073-.323.12-.601.16-.823l.03-.162c.096-.541.085-.853-.098-1.096l-.147-.05a1 1 0 0 0-.067.118 3.65 3.65 0 0 0 .067 2.05Z'/></svg>",
+ "folder-pdf": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M22.433 17.937a14.7 14.7 0 0 1-1.015 2.407 9 9 0 0 0-.494 1.036l.109-.041a18.3 18.3 0 0 1 3.342-.924q-.218-.151-.42-.324a6.25 6.25 0 0 1-1.522-2.154m6.474 3.812a1.14 1.14 0 0 1-.9.299 7.2 7.2 0 0 1-2.985-.739 20 20 0 0 0-4.047.75l-.184.07c-1.243 2.123-2.162 3.07-2.974 3.07a1 1 0 0 1-.44-.104l-.48-.315-.023-.053a.83.83 0 0 1-.053-.538 3.8 3.8 0 0 1 1.883-2.118 5.5 5.5 0 0 1 .89-.49c.296-.522.616-1.128.952-1.804a17.3 17.3 0 0 0 1.087-2.924l-.005-.012a4.94 4.94 0 0 1-.219-3.265c.11-.386.42-.776.794-.776h.237a.85.85 0 0 1 .608.246c.659.659.357 2.267.022 3.595l-.035.141a5.8 5.8 0 0 0 1.596 2.556 8 8 0 0 0 .862.586 12 12 0 0 1 1.298-.074c1.24 0 1.986.224 2.277.686a.8.8 0 0 1 .124.551.96.96 0 0 1-.285.662M30 10H16a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V12a2 2 0 0 0-2-2m-1.486 11.043c-.112-.106-.522-.356-1.918-.356a.26.26 0 0 0-.23.1 5.4 5.4 0 0 0 1.902.512 1.3 1.3 0 0 0 .196-.015l.034-.006c.048-.014.08-.03.09-.13a1 1 0 0 0-.074-.105m-9.185 1.455a4 4 0 0 0-.475.314 3.66 3.66 0 0 0-1.215 1.692c.455-.156 1.043-.812 1.692-2.006Zm3.016-6.906.056-.037c.073-.323.12-.601.16-.823l.03-.162c.096-.541.085-.853-.098-1.096l-.147-.05a1 1 0 0 0-.067.118 3.65 3.65 0 0 0 .067 2.05Z'/></svg>",
+ "folder-pdm-open": "<svg viewBox='0 0 32 32'><path fill='#9575cd' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#d1c4e9' d='m23.51 10.276-7 3.937a1 1 0 0 0-.51.872v7.83a1 1 0 0 0 .51.872l7 3.937a1 1 0 0 0 .98 0l7-3.937a1 1 0 0 0 .51-.872v-7.83a1 1 0 0 0-.51-.872l-7-3.937a1 1 0 0 0-.98 0m-5.255 5.25 5.5-3.098a.5.5 0 0 1 .49 0L26 13.42V14l-8 4v-2.038a.5.5 0 0 1 .255-.435ZM26 16v5.52l-5.24-2.9Zm-2.246 9.572-5.5-3.099a.5.5 0 0 1-.254-.435V20l.59-.29 8.1 4.48-2.444 1.381a.5.5 0 0 1-.492 0ZM30 22.038a.5.5 0 0 1-.255.435l-1.005.567-.74-.41v-8.09l1.746.986a.5.5 0 0 1 .254.436Z'/></svg>",
+ "folder-pdm": "<svg viewBox='0 0 32 32'><path fill='#9575cd' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#d1c4e9' d='m23.51 10.276-7 3.937a1 1 0 0 0-.51.872v7.83a1 1 0 0 0 .51.872l7 3.937a1 1 0 0 0 .98 0l7-3.937a1 1 0 0 0 .51-.872v-7.83a1 1 0 0 0-.51-.872l-7-3.937a1 1 0 0 0-.98 0m-5.255 5.25 5.5-3.098a.5.5 0 0 1 .49 0L26 13.42V14l-8 4v-2.038a.5.5 0 0 1 .255-.435ZM26 16v5.52l-5.24-2.9Zm-2.246 9.572-5.5-3.099a.5.5 0 0 1-.254-.435V20l.59-.29 8.1 4.48-2.444 1.381a.5.5 0 0 1-.492 0ZM30 22.038a.5.5 0 0 1-.255.435l-1.005.567-.74-.41v-8.09l1.746.986a.5.5 0 0 1 .254.436Z'/></svg>",
+ "folder-php-open": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M12 18H8.5a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5V24h2a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2m0 3.5a.5.5 0 0 1-.5.5H10v-2h1.5a.5.5 0 0 1 .5.5ZM28 18h-3.5a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5V24h2a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2m0 3.5a.5.5 0 0 1-.5.5H26v-2h1.5a.5.5 0 0 1 .5.5ZM20 18h-2v-3.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5V20h1.5a.5.5 0 0 1 .5.5v3a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5V20a2 2 0 0 0-2-2'/></svg>",
+ "folder-php": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M12 18H8.5a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5V24h2a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2m0 3.5a.5.5 0 0 1-.5.5H10v-2h1.5a.5.5 0 0 1 .5.5ZM28 18h-3.5a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5V24h2a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2m0 3.5a.5.5 0 0 1-.5.5H26v-2h1.5a.5.5 0 0 1 .5.5ZM20 18h-2v-3.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5V20h1.5a.5.5 0 0 1 .5.5v3a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5V20a2 2 0 0 0-2-2'/></svg>",
+ "folder-phpmailer-open": "<svg viewBox='0 0 32 32'><path fill='#616161' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#eee' d='M14 14h-4v14h2v-6h2a4 4 0 0 0 0-8m0 6h-2v-4h2a2 2 0 0 1 0 4'/><path fill='#ffd180' d='M20 17v11h12V17l-6 6z'/><path fill='#ffd180' d='M32 14H20l6 6z'/></svg>",
+ "folder-phpmailer": "<svg viewBox='0 0 32 32'><path fill='#616161' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#eee' d='M14 14h-4v14h2v-6h2a4 4 0 0 0 0-8m0 6h-2v-4h2a2 2 0 0 1 0 4'/><path fill='#ffd180' d='M20 17v11h12V17l-6 6z'/><path fill='#ffd180' d='M32 14H20l6 6z'/></svg>",
+ "folder-pipe-open": "<svg viewBox='0 0 32 32'><path fill='#00897b' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2dfdb' d='M30 22v2h-6v-2h2v-2h-2v-4a2 2 0 0 0-2-2h-6v-2h-2v8h2v-2h4v2h-2v2h2v4a2 2 0 0 0 2 2h8v2h2v-8Z'/></svg>",
+ "folder-pipe": "<svg viewBox='0 0 32 32'><path fill='#00897b' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2dfdb' d='M30 22v2h-6v-2h2v-2h-2v-4a2 2 0 0 0-2-2h-6v-2h-2v8h2v-2h4v2h-2v2h2v4a2 2 0 0 0 2 2h8v2h2v-8Z'/></svg>",
+ "folder-plastic-open": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fffde7' d='m30.973 14.255-6.955-3.984a2.05 2.05 0 0 0-2.033 0l-6.955 3.984A2.05 2.05 0 0 0 14 16.032v7.94a1.93 1.93 0 0 0 1.016 1.708l.984.56v-9.306a1.7 1.7 0 0 1 .14-.58 1.64 1.64 0 0 1 .689-.798l5.398-3.092a1.59 1.59 0 0 1 1.576 0l5.398 3.092a1.6 1.6 0 0 1 .749.983 1.6 1.6 0 0 1 .05.395v6.138a1.58 1.58 0 0 1-.797 1.375l-5.406 3.096a1.6 1.6 0 0 1-.797.21v2.246a2.06 2.06 0 0 0 1.02-.27l6.95-3.982A2.05 2.05 0 0 0 32 23.97v-7.938a2 2 0 0 0-.076-.548 2.03 2.03 0 0 0-.95-1.229Z'/><path fill='#fffde7' d='m23.539 25.412 3.89-2.228a1.14 1.14 0 0 0 .57-.985v-4.402a1.14 1.14 0 0 0-.572-.988l-3.862-2.211a1.14 1.14 0 0 0-1.13 0l-3.862 2.211a1.15 1.15 0 0 0-.512.618 1.2 1.2 0 0 0-.061.37l-.014 9.578L20 28.505v-4.468l2.402 1.375a1.15 1.15 0 0 0 1.137 0m-3.2-3.5a.68.68 0 0 1-.339-.585v-2.649a.7.7 0 0 1 .04-.232.68.68 0 0 1 .304-.36l2.321-1.329a.68.68 0 0 1 .671 0l2.322 1.33a.68.68 0 0 1 .328.45 1 1 0 0 1 .014.141v2.65a.68.68 0 0 1-.34.585l-2.322 1.329a.7.7 0 0 1-.339.09.7.7 0 0 1-.339-.089Z'/></svg>",
+ "folder-plastic": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fffde7' d='m30.973 14.255-6.955-3.984a2.05 2.05 0 0 0-2.033 0l-6.955 3.984A2.05 2.05 0 0 0 14 16.032v7.94a1.93 1.93 0 0 0 1.016 1.708l.984.56v-9.306a1.7 1.7 0 0 1 .14-.58 1.64 1.64 0 0 1 .689-.798l5.398-3.092a1.59 1.59 0 0 1 1.576 0l5.398 3.092a1.6 1.6 0 0 1 .749.983 1.6 1.6 0 0 1 .05.395v6.138a1.58 1.58 0 0 1-.797 1.375l-5.406 3.096a1.6 1.6 0 0 1-.797.21v2.246a2.06 2.06 0 0 0 1.02-.27l6.95-3.982A2.05 2.05 0 0 0 32 23.97v-7.938a2 2 0 0 0-.076-.548 2.03 2.03 0 0 0-.95-1.229Z'/><path fill='#fffde7' d='m23.539 25.412 3.89-2.228a1.14 1.14 0 0 0 .57-.985v-4.402a1.14 1.14 0 0 0-.572-.988l-3.862-2.211a1.14 1.14 0 0 0-1.13 0l-3.862 2.211a1.15 1.15 0 0 0-.512.618 1.2 1.2 0 0 0-.061.37l-.014 9.578L20 28.505v-4.468l2.402 1.375a1.15 1.15 0 0 0 1.137 0m-3.2-3.5a.68.68 0 0 1-.339-.585v-2.649a.7.7 0 0 1 .04-.232.68.68 0 0 1 .304-.36l2.321-1.329a.68.68 0 0 1 .671 0l2.322 1.33a.68.68 0 0 1 .328.45 1 1 0 0 1 .014.141v2.65a.68.68 0 0 1-.34.585l-2.322 1.329a.7.7 0 0 1-.339.09.7.7 0 0 1-.339-.089Z'/></svg>",
+ "folder-plugin-open": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M30.107 20H32v-4a2 2 0 0 0-2-2h-4v-2a2 2 0 0 0-4 0v2h-4a2 2 0 0 0-2 2v4h-2a2 2 0 0 0 0 4h2v4a2 2 0 0 0 2 2h4v-1.893a2.074 2.074 0 0 1 1.664-2.08A2 2 0 0 1 26 28v2h4a2 2 0 0 0 2-2v-4h-2a2 2 0 0 1-1.973-2.336A2.074 2.074 0 0 1 30.107 20'/></svg>",
+ "folder-plugin": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M30.107 20H32v-4a2 2 0 0 0-2-2h-4v-2a2 2 0 0 0-4 0v2h-4a2 2 0 0 0-2 2v4h-2a2 2 0 0 0 0 4h2v4a2 2 0 0 0 2 2h4v-1.893a2.074 2.074 0 0 1 1.664-2.08A2 2 0 0 1 26 28v2h4a2 2 0 0 0 2-2v-4h-2a2 2 0 0 1-1.973-2.336A2.074 2.074 0 0 1 30.107 20'/></svg>",
+ "folder-policy-open": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#90caf9' d='m21.972 21.668.697.696a1.004 1.004 0 0 0 1.414 0l5.626-5.623a1.003 1.003 0 0 0 0-1.413l-.697-.707a1.004 1.004 0 0 0-1.414 0l-.707.707-1.404-1.404a1.003 1.003 0 0 0 0-1.413.99.99 0 0 0-1.404 0l-1.414-1.423.707-.687a.99.99 0 0 0 0-1.403l-.707-.707a.99.99 0 0 0-1.404 0l-5.636 5.633a.99.99 0 0 0 0 1.404l.707.706a.99.99 0 0 0 1.404 0l.717-.706 1.394 1.413-9.56 9.555a1.003 1.003 0 0 0 0 1.413.99.99 0 0 0 1.404 0l9.57-9.565 1.404 1.404-.697.706a.985.985 0 0 0 0 1.414M24 26h6a2 2 0 0 1 2 2v2H22v-2a2 2 0 0 1 2-2'/></svg>",
+ "folder-policy": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#90caf9' d='m21.972 21.668.697.696a1.004 1.004 0 0 0 1.414 0l5.626-5.623a1.003 1.003 0 0 0 0-1.413l-.697-.707a1.004 1.004 0 0 0-1.414 0l-.707.707-1.404-1.404a1.003 1.003 0 0 0 0-1.413.99.99 0 0 0-1.404 0l-1.414-1.423.707-.687a.99.99 0 0 0 0-1.403l-.707-.707a.99.99 0 0 0-1.404 0l-5.636 5.633a.99.99 0 0 0 0 1.404l.707.706a.99.99 0 0 0 1.404 0l.717-.706 1.394 1.413-9.56 9.555a1.003 1.003 0 0 0 0 1.413.99.99 0 0 0 1.404 0l9.57-9.565 1.404 1.404-.697.706a.985.985 0 0 0 0 1.414M24 26h6a2 2 0 0 1 2 2v2H22v-2a2 2 0 0 1 2-2'/></svg>",
+ "folder-powershell-open": "<svg viewBox='0 0 32 32'><path fill='#03a9f4' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M31.25 14.034a1 1 0 0 0-.285-.034H14.496a1.03 1.03 0 0 0-.996.731l-3.461 12A1.007 1.007 0 0 0 11.035 28h16.469a1.03 1.03 0 0 0 .996-.731l3.461-12a1.007 1.007 0 0 0-.71-1.235ZM15.001 26a1 1 0 0 1-.556-1.832l4.986-3.323-3.138-3.138a1 1 0 0 1 1.414-1.414l4 4a1 1 0 0 1-.152 1.54l-6 4A1 1 0 0 1 15 26ZM26 26h-4a1 1 0 0 1 0-2h4a1 1 0 0 1 0 2'/></svg>",
+ "folder-powershell": "<svg viewBox='0 0 32 32'><path fill='#03a9f4' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M31.25 14.034a1 1 0 0 0-.285-.034H14.496a1.03 1.03 0 0 0-.996.731l-3.461 12A1.007 1.007 0 0 0 11.035 28h16.469a1.03 1.03 0 0 0 .996-.731l3.461-12a1.007 1.007 0 0 0-.71-1.235ZM15.001 26a1 1 0 0 1-.556-1.832l4.986-3.323-3.138-3.138a1 1 0 0 1 1.414-1.414l4 4a1 1 0 0 1-.152 1.54l-6 4A1 1 0 0 1 15 26ZM26 26h-4a1 1 0 0 1 0-2h4a1 1 0 0 1 0 2'/></svg>",
+ "folder-prisma-open": "<svg viewBox='0 0 32 32'><path fill='#00bfa5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#a7ffeb' d='m30.209 26.275-9.76 2.39a.42.42 0 0 1-.51-.224.3.3 0 0 1-.012-.165l3.486-13.827a.35.35 0 0 1 .412-.21.34.34 0 0 1 .221.15l6.457 11.352a.362.362 0 0 1-.218.51zm1.672-.564-7.475-13.144a1.335 1.335 0 0 0-1.647-.453 1.2 1.2 0 0 0-.468.357l-8.106 10.873a.87.87 0 0 0 .014 1.092l3.964 5.083a1.41 1.41 0 0 0 1.432.435l11.503-2.816a1.22 1.22 0 0 0 .79-.567.86.86 0 0 0-.007-.86'/></svg>",
+ "folder-prisma": "<svg viewBox='0 0 32 32'><path fill='#00bfa5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#a7ffeb' d='m30.209 26.275-9.76 2.39a.42.42 0 0 1-.51-.224.3.3 0 0 1-.012-.165l3.486-13.827a.35.35 0 0 1 .412-.21.34.34 0 0 1 .221.15l6.457 11.352a.362.362 0 0 1-.218.51zm1.672-.564-7.475-13.144a1.335 1.335 0 0 0-1.647-.453 1.2 1.2 0 0 0-.468.357l-8.106 10.873a.87.87 0 0 0 .014 1.092l3.964 5.083a1.41 1.41 0 0 0 1.432.435l11.503-2.816a1.22 1.22 0 0 0 .79-.567.86.86 0 0 0-.007-.86'/></svg>",
+ "folder-private-open": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M24 14a8 8 0 1 0 8 8 8 8 0 0 0-8-8m6 8a5.96 5.96 0 0 1-1.115 3.471l-8.356-8.356A5.99 5.99 0 0 1 30 22m-12 0a5.96 5.96 0 0 1 1.115-3.471l8.356 8.356A5.99 5.99 0 0 1 18 22'/></svg>",
+ "folder-private": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M24 14a8 8 0 1 0 8 8 8 8 0 0 0-8-8m6 8a5.96 5.96 0 0 1-1.115 3.471l-8.356-8.356A5.99 5.99 0 0 1 30 22m-12 0a5.96 5.96 0 0 1 1.115-3.471l8.356 8.356A5.99 5.99 0 0 1 18 22'/></svg>",
+ "folder-project-open": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M27.354 20.871 32 25.343l-2.74 2.624-4.613-4.471v-.737l1.947-1.888zm.751-2.023-.8-.768-3.953 3.839v1.577L18.706 28 16 25.343l4.612-4.472h1.626l.644-.624-3.17-3.08h-1.071l-2.32-2.271 2.162-2.096 2.311 2.24v1.048l3.21 3.072 2.194-2.128-.791-.808 1.072-1.049h-2.196l-.536-.52L26.48 12l.545.527v2.129l1.081-1.057 2.707 2.625a2.22 2.22 0 0 1 0 3.184l-1.627-1.609Z'/></svg>",
+ "folder-project": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M27.354 20.871 32 25.343l-2.74 2.624-4.613-4.471v-.737l1.947-1.888zm.751-2.023-.8-.768-3.953 3.839v1.577L18.706 28 16 25.343l4.612-4.472h1.626l.644-.624-3.17-3.08h-1.071l-2.32-2.271 2.162-2.096 2.311 2.24v1.048l3.21 3.072 2.194-2.128-.791-.808 1.072-1.049h-2.196l-.536-.52L26.48 12l.545.527v2.129l1.081-1.057 2.707 2.625a2.22 2.22 0 0 1 0 3.184l-1.627-1.609Z'/></svg>",
+ "folder-proto-open": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffccbc' d='m22 12-10 6v4l10 6 10-6v-4Zm0 12-6.667-4L22 16l6.667 4Z'/></svg>",
+ "folder-proto": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffccbc' d='m22 12-10 6v4l10 6 10-6v-4Zm0 12-6.667-4L22 16l6.667 4Z'/></svg>",
+ "folder-public-open": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M22 10a10 10 0 1 0 10 10 10 10 0 0 0-10-10m6.918 6H25.96a15.8 15.8 0 0 0-1.342-3.54 8.04 8.04 0 0 1 4.3 3.54M22 12a14.1 14.1 0 0 1 1.89 4h-3.78A14.1 14.1 0 0 1 22 12m-2.618.46A15.8 15.8 0 0 0 18.04 16h-2.958a8.04 8.04 0 0 1 4.3-3.54M14.263 22a7.7 7.7 0 0 1 0-4h3.407a15.5 15.5 0 0 0 0 4Zm.82 2h2.957a15.8 15.8 0 0 0 1.342 3.54 8.04 8.04 0 0 1-4.3-3.54ZM22 28a14.1 14.1 0 0 1-1.89-4h3.78A14.1 14.1 0 0 1 22 28m2.31-6h-4.62a13.4 13.4 0 0 1 0-4h4.62a13.4 13.4 0 0 1 0 4m.308 5.54A15.8 15.8 0 0 0 25.96 24h2.958a8.04 8.04 0 0 1-4.3 3.54M29.737 22H26.33a15.5 15.5 0 0 0 0-4h3.407a7.7 7.7 0 0 1 0 4'/></svg>",
+ "folder-public": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M22 10a10 10 0 1 0 10 10 10 10 0 0 0-10-10m6.918 6H25.96a15.8 15.8 0 0 0-1.342-3.54 8.04 8.04 0 0 1 4.3 3.54M22 12a14.1 14.1 0 0 1 1.89 4h-3.78A14.1 14.1 0 0 1 22 12m-2.618.46A15.8 15.8 0 0 0 18.04 16h-2.958a8.04 8.04 0 0 1 4.3-3.54M14.263 22a7.7 7.7 0 0 1 0-4h3.407a15.5 15.5 0 0 0 0 4Zm.82 2h2.957a15.8 15.8 0 0 0 1.342 3.54 8.04 8.04 0 0 1-4.3-3.54ZM22 28a14.1 14.1 0 0 1-1.89-4h3.78A14.1 14.1 0 0 1 22 28m2.31-6h-4.62a13.4 13.4 0 0 1 0-4h4.62a13.4 13.4 0 0 1 0 4m.308 5.54A15.8 15.8 0 0 0 25.96 24h2.958a8.04 8.04 0 0 1-4.3 3.54M29.737 22H26.33a15.5 15.5 0 0 0 0-4h3.407a7.7 7.7 0 0 1 0 4'/></svg>",
+ "folder-python-open": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#0277bd' d='M21.123 10a2.574 2.574 0 0 0-2.574 2.574v1.512h3.86c.352 0 .64.513.64.864h-6.426a2.574 2.574 0 0 0-2.574 2.574v3.404A2.57 2.57 0 0 0 16.62 23.5h1.065v-2.412a2.565 2.565 0 0 1 2.556-2.574h4.734a2.565 2.565 0 0 0 2.574-2.556v-3.384A2.574 2.574 0 0 0 24.975 10zm-.648 1.449c.36 0 .648.109.648.64s-.288.8-.648.8c-.351 0-.64-.27-.64-.8s.289-.64.64-.64'/><path fill='#fdd835' d='M28.412 14.5v2.412a2.565 2.565 0 0 1-2.556 2.574h-4.733a2.565 2.565 0 0 0-2.574 2.556v3.382A2.574 2.574 0 0 0 21.12 28h3.854a2.574 2.574 0 0 0 2.574-2.574v-1.513h-3.862c-.351 0-.638-.512-.638-.863h6.426a2.574 2.574 0 0 0 2.574-2.574v-3.403a2.574 2.574 0 0 0-2.574-2.573Zm-8.675 4.063-.004.003q.017-.003.034-.003Zm5.886 6.547c.35 0 .639.27.639.801a.64.64 0 0 1-.64.64c-.36 0-.647-.109-.647-.64s.288-.8.648-.8Z'/></svg>",
+ "folder-python": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#0277bd' d='M21.123 10a2.574 2.574 0 0 0-2.574 2.574v1.512h3.86c.352 0 .64.513.64.864h-6.426a2.574 2.574 0 0 0-2.574 2.574v3.404A2.57 2.57 0 0 0 16.62 23.5h1.065v-2.412a2.565 2.565 0 0 1 2.556-2.574h4.734a2.565 2.565 0 0 0 2.574-2.556v-3.384A2.574 2.574 0 0 0 24.975 10zm-.648 1.449c.36 0 .648.109.648.64s-.288.8-.648.8c-.351 0-.64-.27-.64-.8s.289-.64.64-.64'/><path fill='#fdd835' d='M28.412 14.5v2.412a2.565 2.565 0 0 1-2.556 2.574h-4.733a2.565 2.565 0 0 0-2.574 2.556v3.382A2.574 2.574 0 0 0 21.12 28h3.854a2.574 2.574 0 0 0 2.574-2.574v-1.513h-3.862c-.351 0-.638-.512-.638-.863h6.426a2.574 2.574 0 0 0 2.574-2.574v-3.403a2.574 2.574 0 0 0-2.574-2.573Zm-8.675 4.063-.004.003q.017-.003.034-.003Zm5.886 6.547c.35 0 .639.27.639.801a.64.64 0 0 1-.64.64c-.36 0-.647-.109-.647-.64s.288-.8.648-.8Z'/></svg>",
+ "folder-pytorch-open": "<svg viewBox='0 0 32 32'><path fill='#f4511e' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffab91' d='M23.334 14.011a1.334 1.334 0 0 1 1.333-1.335 1.335 1.335 0 1 1-1.333 1.335m6.497 5.29a7.5 7.5 0 0 0-1.449-3.066 8 8 0 0 0-.734-.814l-1.883 1.892a6 6 0 0 1 .498.543 4.9 4.9 0 0 1 .961 2.01 5 5 0 0 1 .11.822 5.334 5.334 0 0 1-10.667.016v-.016a3.86 3.86 0 0 1 .463-1.845 5 5 0 0 1 .852-1.267c.12-.12 1.209-1.166 2.493-2.394l1.537-1.473L22 10l-3.372 3.255a337 337 0 0 0-2.584 2.486 7.7 7.7 0 0 0-1.327 1.965A6.54 6.54 0 0 0 14 20.688a8 8 0 1 0 16-.02 8 8 0 0 0-.17-1.366'/></svg>",
+ "folder-pytorch": "<svg viewBox='0 0 32 32'><path fill='#f4511e' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffab91' d='M23.334 14.011a1.334 1.334 0 0 1 1.333-1.335 1.335 1.335 0 1 1-1.333 1.335m6.497 5.29a7.5 7.5 0 0 0-1.449-3.066 8 8 0 0 0-.734-.814l-1.883 1.892a6 6 0 0 1 .498.543 4.9 4.9 0 0 1 .961 2.01 5 5 0 0 1 .11.822 5.334 5.334 0 0 1-10.667.016v-.016a3.86 3.86 0 0 1 .463-1.845 5 5 0 0 1 .852-1.267c.12-.12 1.209-1.166 2.493-2.394l1.537-1.473L22 10l-3.372 3.255a337 337 0 0 0-2.584 2.486 7.7 7.7 0 0 0-1.327 1.965A6.54 6.54 0 0 0 14 20.688a8 8 0 1 0 16-.02 8 8 0 0 0-.17-1.366'/></svg>",
+ "folder-quasar-open": "<svg viewBox='0 0 32 32'><path fill='#1976d2' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M24.026 20A2.028 2.028 0 1 1 22 18.048 1.99 1.99 0 0 1 24.026 20m6.967-5.002a10 10 0 0 0-1.59-2.002L27.06 14.3a7.9 7.9 0 0 0-2.445-1.365 9.3 9.3 0 0 0-1.893 2.6 11.74 11.74 0 0 1 7.8 2.618l1.473-.819A9.8 9.8 0 0 0 30.993 15Zm0 10.002A9.8 9.8 0 0 0 32 22.67l-2.342-1.303a7.2 7.2 0 0 0 .005-2.72 10 10 0 0 0-3.285-.278 10.7 10.7 0 0 1 1.545 7.812l1.473.82A10 10 0 0 0 30.993 25m-8.992 5a10.8 10.8 0 0 0 2.597-.326v-2.603a7.9 7.9 0 0 0 2.451-1.357 9.1 9.1 0 0 0-1.392-2.88 11.36 11.36 0 0 1-6.255 5.196v1.64a10.8 10.8 0 0 0 2.599.33m-8.994-5a10 10 0 0 0 1.592 2.004L16.94 25.7a7.8 7.8 0 0 0 2.447 1.365 9.3 9.3 0 0 0 1.891-2.6 11.75 11.75 0 0 1-7.8-2.618l-1.471.819a9.8 9.8 0 0 0 1 2.333Zm0-10A9.8 9.8 0 0 0 12 17.33l2.343 1.303a7.2 7.2 0 0 0-.005 2.72 10 10 0 0 0 3.286.278 10.7 10.7 0 0 1-1.545-7.814l-1.475-.82a10 10 0 0 0-1.597 2.005Zm8.992-5a10.8 10.8 0 0 0-2.597.326v2.603a7.9 7.9 0 0 0-2.45 1.357 9.1 9.1 0 0 0 1.393 2.88A11.35 11.35 0 0 1 24.6 11.97v-1.64A10.8 10.8 0 0 0 22 10Z'/></svg>",
+ "folder-quasar": "<svg viewBox='0 0 32 32'><path fill='#1976d2' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M24.026 20A2.028 2.028 0 1 1 22 18.048 1.99 1.99 0 0 1 24.026 20m6.967-5.002a10 10 0 0 0-1.59-2.002L27.06 14.3a7.9 7.9 0 0 0-2.445-1.365 9.3 9.3 0 0 0-1.893 2.6 11.74 11.74 0 0 1 7.8 2.618l1.473-.819A9.8 9.8 0 0 0 30.993 15Zm0 10.002A9.8 9.8 0 0 0 32 22.67l-2.342-1.303a7.2 7.2 0 0 0 .005-2.72 10 10 0 0 0-3.285-.278 10.7 10.7 0 0 1 1.545 7.812l1.473.82A10 10 0 0 0 30.993 25m-8.992 5a10.8 10.8 0 0 0 2.597-.326v-2.603a7.9 7.9 0 0 0 2.451-1.357 9.1 9.1 0 0 0-1.392-2.88 11.36 11.36 0 0 1-6.255 5.196v1.64a10.8 10.8 0 0 0 2.599.33m-8.994-5a10 10 0 0 0 1.592 2.004L16.94 25.7a7.8 7.8 0 0 0 2.447 1.365 9.3 9.3 0 0 0 1.891-2.6 11.75 11.75 0 0 1-7.8-2.618l-1.471.819a9.8 9.8 0 0 0 1 2.333Zm0-10A9.8 9.8 0 0 0 12 17.33l2.343 1.303a7.2 7.2 0 0 0-.005 2.72 10 10 0 0 0 3.286.278 10.7 10.7 0 0 1-1.545-7.814l-1.475-.82a10 10 0 0 0-1.597 2.005Zm8.992-5a10.8 10.8 0 0 0-2.597.326v2.603a7.9 7.9 0 0 0-2.45 1.357 9.1 9.1 0 0 0 1.393 2.88A11.35 11.35 0 0 1 24.6 11.97v-1.64A10.8 10.8 0 0 0 22 10Z'/></svg>",
+ "folder-queue-open": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M24 16v-2h-3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3v-2h-2v-8Zm8-2v-2h-5a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h5v-2h-4V14Zm-16 2h2v8h-2z'/></svg>",
+ "folder-queue": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M24 16v-2h-3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3v-2h-2v-8Zm8-2v-2h-5a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h5v-2h-4V14Zm-16 2h2v8h-2z'/></svg>",
+ "folder-react-components-open": "<svg viewBox='0 0 16 16'><path fill='#00bcd4' d='M14.484 6H4.72a1 1 0 0 0-.949.684L2 12V5h13a1 1 0 0 0-1-1H7.562a1 1 0 0 1-.64-.232l-.644-.536A1 1 0 0 0 5.638 3H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h11l2.403-5.606A1 1 0 0 0 14.483 6'/><g fill='#b2ebf2'><path d='M10.5 8.399c2.924 0 4.714 1.037 4.714 1.6s-1.79 1.602-4.714 1.602S5.785 10.564 5.785 10s1.79-1.601 4.715-1.601m0-.8c-3.038 0-5.5 1.075-5.5 2.4s2.462 2.402 5.5 2.402S16 11.326 16 10s-2.463-2.401-5.5-2.401'/><path d='M10.5 9.2a.786.8 0 1 0 .785.8.786.8 0 0 0-.785-.8'/><path d='M8.322 5.8c.793 0 2.333 1.272 3.538 3.4 1.463 2.58 1.476 4.677.997 4.959a.354.36 0 0 1-.18.04c-.792 0-2.333-1.271-3.538-3.399-1.463-2.58-1.476-4.677-.997-4.96a.354.36 0 0 1 .18-.04m0-.8a1.128 1.149 0 0 0-.572.147c-1.128.663-.81 3.374.708 6.054C9.748 13.478 11.491 15 12.678 15a1.128 1.149 0 0 0 .572-.148c1.127-.663.81-3.373-.71-6.053C11.25 6.522 9.509 5 8.323 5Z'/><path d='M12.677 5.8a.354.36 0 0 1 .18.04c.48.283.466 2.38-.997 4.96-1.206 2.128-2.746 3.4-3.538 3.4a.354.36 0 0 1-.18-.04c-.48-.284-.466-2.38.997-4.96 1.206-2.128 2.746-3.4 3.538-3.4m0-.8c-1.186 0-2.929 1.522-4.22 3.8-1.517 2.68-1.835 5.39-.707 6.052a1.128 1.149 0 0 0 .572.148c1.186 0 2.929-1.523 4.22-3.8 1.517-2.68 1.835-5.39.708-6.052A1.128 1.149 0 0 0 12.677 5'/></g></svg>",
+ "folder-react-components": "<svg viewBox='0 0 16 16'><path fill='#00bcd4' d='m6.922 3.768-.644-.536A1 1 0 0 0 5.638 3H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H7.562a1 1 0 0 1-.64-.232'/><g fill='#b2ebf2'><path d='M10.5 8.399c2.924 0 4.714 1.037 4.714 1.6s-1.79 1.602-4.714 1.602S5.785 10.564 5.785 10s1.79-1.601 4.715-1.601m0-.8c-3.038 0-5.5 1.075-5.5 2.4s2.462 2.402 5.5 2.402S16 11.326 16 10s-2.463-2.401-5.5-2.401'/><path d='M10.5 9.2a.786.8 0 1 0 .785.8.786.8 0 0 0-.785-.8'/><path d='M8.322 5.8c.793 0 2.333 1.272 3.538 3.4 1.463 2.58 1.476 4.677.997 4.959a.354.36 0 0 1-.18.04c-.792 0-2.333-1.271-3.538-3.399-1.463-2.58-1.476-4.677-.997-4.96a.354.36 0 0 1 .18-.04m0-.8a1.128 1.149 0 0 0-.572.147c-1.128.663-.81 3.374.708 6.054C9.748 13.478 11.491 15 12.678 15a1.128 1.149 0 0 0 .572-.148c1.127-.663.81-3.373-.71-6.053C11.25 6.522 9.509 5 8.323 5Z'/><path d='M12.677 5.8a.354.36 0 0 1 .18.04c.48.283.466 2.38-.997 4.96-1.206 2.128-2.746 3.4-3.538 3.4a.354.36 0 0 1-.18-.04c-.48-.284-.466-2.38.997-4.96 1.206-2.128 2.746-3.4 3.538-3.4m0-.8c-1.186 0-2.929 1.522-4.22 3.8-1.517 2.68-1.835 5.39-.707 6.052a1.128 1.149 0 0 0 .572.148c1.186 0 2.929-1.523 4.22-3.8 1.517-2.68 1.835-5.39.708-6.052A1.128 1.149 0 0 0 12.677 5'/></g></svg>",
+ "folder-redux-actions-open.clone": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f3e5f5' stroke='#f3e5f5' stroke-linejoin='round' stroke-width='.293' d='M25.948 24.114a1.65 1.65 0 0 0 .97-.6 1.8 1.8 0 0 0 .381-1.274 1.72 1.72 0 0 0-1.69-1.596h-.06a1.724 1.724 0 0 0-1.61 1.814 1.85 1.85 0 0 0 .34.985 8.85 8.85 0 0 1-3.863 3.799 6.15 6.15 0 0 1-3.876.771 3.13 3.13 0 0 1-2.32-1.411 3.67 3.67 0 0 1-.18-3.738 5.8 5.8 0 0 1 1.605-1.986.3.3 0 0 0 .098-.313 14 14 0 0 1-.315-1.298.29.29 0 0 0-.172-.213.28.28 0 0 0-.272.036c-3.731 2.836-3.326 6.763-2.188 8.579a5.36 5.36 0 0 0 4.294 2.229q.125 0 .24-.005h.04a6 6 0 0 0 1.5-.188 9.88 9.88 0 0 0 7.078-5.591Z'/><path fill='#f3e5f5' stroke='#f3e5f5' stroke-linejoin='round' stroke-width='.293' d='M30.327 20.428a10.12 10.12 0 0 0-7.774-3.69q-.133 0-.265.003h-.234a1.61 1.61 0 0 0-1.377-.78h-.053a1.62 1.62 0 0 0-1.175.535 1.806 1.806 0 0 0 .039 2.466 1.67 1.67 0 0 0 1.19.494h.064a1.68 1.68 0 0 0 1.375-.886h.27a8.83 8.83 0 0 1 5.126 1.646 6.6 6.6 0 0 1 2.522 3.202 3.48 3.48 0 0 1-.046 2.831 3.39 3.39 0 0 1-3.137 1.97 5.8 5.8 0 0 1-2.32-.522.27.27 0 0 0-.304.054 14 14 0 0 1-1.088.912.294.294 0 0 0 .039.495 7.7 7.7 0 0 0 3.313.84l.192.002a5.66 5.66 0 0 0 4.886-2.948 6.39 6.39 0 0 0-1.243-6.624Z'/><path fill='#f3e5f5' stroke='#f3e5f5' stroke-linejoin='round' stroke-width='.293' d='m17.249 24.295.123-.01-.123.02a1.705 1.705 0 0 0 1.67 1.682h.053a1.715 1.715 0 0 0 1.64-1.778 1.78 1.78 0 0 0-.507-1.224 1.6 1.6 0 0 0-1.187-.493h-.076a9.6 9.6 0 0 1-1.154-5.448 6.83 6.83 0 0 1 1.39-3.853 3.97 3.97 0 0 1 2.842-1.363h.055c2.438 0 3.415 3.34 3.477 4.491a.29.29 0 0 0 .216.265c.299.073.822.246 1.213.384a.27.27 0 0 0 .266-.048.3.3 0 0 0 .105-.247C26.928 12.088 24.204 10 21.804 10a5.96 5.96 0 0 0-5.36 4.39 11.38 11.38 0 0 0 .936 9.155 1.5 1.5 0 0 0-.131.75Z'/></svg>",
+ "folder-redux-actions.clone": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#f3e5f5' stroke='#f3e5f5' stroke-linejoin='round' stroke-width='.293' d='M25.948 24.114a1.65 1.65 0 0 0 .97-.6 1.8 1.8 0 0 0 .381-1.274 1.72 1.72 0 0 0-1.69-1.596h-.06a1.724 1.724 0 0 0-1.61 1.814 1.85 1.85 0 0 0 .34.985 8.85 8.85 0 0 1-3.863 3.799 6.15 6.15 0 0 1-3.876.771 3.13 3.13 0 0 1-2.32-1.411 3.67 3.67 0 0 1-.18-3.738 5.8 5.8 0 0 1 1.605-1.986.3.3 0 0 0 .098-.313 14 14 0 0 1-.315-1.298.29.29 0 0 0-.172-.213.28.28 0 0 0-.272.036c-3.731 2.836-3.326 6.763-2.188 8.579a5.36 5.36 0 0 0 4.294 2.229q.125 0 .24-.005h.04a6 6 0 0 0 1.5-.188 9.88 9.88 0 0 0 7.078-5.591Z'/><path fill='#f3e5f5' stroke='#f3e5f5' stroke-linejoin='round' stroke-width='.293' d='M30.327 20.428a10.12 10.12 0 0 0-7.774-3.69q-.133 0-.265.003h-.234a1.61 1.61 0 0 0-1.377-.78h-.053a1.62 1.62 0 0 0-1.175.535 1.806 1.806 0 0 0 .039 2.466 1.67 1.67 0 0 0 1.19.494h.064a1.68 1.68 0 0 0 1.375-.886h.27a8.83 8.83 0 0 1 5.126 1.646 6.6 6.6 0 0 1 2.522 3.202 3.48 3.48 0 0 1-.046 2.831 3.39 3.39 0 0 1-3.137 1.97 5.8 5.8 0 0 1-2.32-.522.27.27 0 0 0-.304.054 14 14 0 0 1-1.088.912.294.294 0 0 0 .039.495 7.7 7.7 0 0 0 3.313.84l.192.002a5.66 5.66 0 0 0 4.886-2.948 6.39 6.39 0 0 0-1.243-6.624Z'/><path fill='#f3e5f5' stroke='#f3e5f5' stroke-linejoin='round' stroke-width='.293' d='m17.249 24.295.123-.01-.123.02a1.705 1.705 0 0 0 1.67 1.682h.053a1.715 1.715 0 0 0 1.64-1.778 1.78 1.78 0 0 0-.507-1.224 1.6 1.6 0 0 0-1.187-.493h-.076a9.6 9.6 0 0 1-1.154-5.448 6.83 6.83 0 0 1 1.39-3.853 3.97 3.97 0 0 1 2.842-1.363h.055c2.438 0 3.415 3.34 3.477 4.491a.29.29 0 0 0 .216.265c.299.073.822.246 1.213.384a.27.27 0 0 0 .266-.048.3.3 0 0 0 .105-.247C26.928 12.088 24.204 10 21.804 10a5.96 5.96 0 0 0-5.36 4.39 11.38 11.38 0 0 0 .936 9.155 1.5 1.5 0 0 0-.131.75Z'/></svg>",
+ "folder-redux-reducer-open": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' stroke='#ffcdd2' stroke-linejoin='round' stroke-width='.293' d='M25.948 24.114a1.65 1.65 0 0 0 .97-.6 1.8 1.8 0 0 0 .381-1.274 1.72 1.72 0 0 0-1.69-1.596h-.06a1.724 1.724 0 0 0-1.61 1.814 1.85 1.85 0 0 0 .34.985 8.85 8.85 0 0 1-3.863 3.799 6.15 6.15 0 0 1-3.876.771 3.13 3.13 0 0 1-2.32-1.411 3.67 3.67 0 0 1-.18-3.738 5.8 5.8 0 0 1 1.605-1.986.3.3 0 0 0 .098-.313 14 14 0 0 1-.315-1.298.29.29 0 0 0-.172-.213.28.28 0 0 0-.272.036c-3.731 2.836-3.326 6.763-2.188 8.579a5.36 5.36 0 0 0 4.294 2.229q.125 0 .24-.005h.04a6 6 0 0 0 1.5-.188 9.88 9.88 0 0 0 7.078-5.591Z'/><path fill='#ffcdd2' stroke='#ffcdd2' stroke-linejoin='round' stroke-width='.293' d='M30.327 20.428a10.12 10.12 0 0 0-7.774-3.69q-.133 0-.265.003h-.234a1.61 1.61 0 0 0-1.377-.78h-.053a1.62 1.62 0 0 0-1.175.535 1.806 1.806 0 0 0 .039 2.466 1.67 1.67 0 0 0 1.19.494h.064a1.68 1.68 0 0 0 1.375-.886h.27a8.83 8.83 0 0 1 5.126 1.646 6.6 6.6 0 0 1 2.522 3.202 3.48 3.48 0 0 1-.046 2.831 3.39 3.39 0 0 1-3.137 1.97 5.8 5.8 0 0 1-2.32-.522.27.27 0 0 0-.304.054 14 14 0 0 1-1.088.912.294.294 0 0 0 .039.495 7.7 7.7 0 0 0 3.313.84l.192.002a5.66 5.66 0 0 0 4.886-2.948 6.39 6.39 0 0 0-1.243-6.624Z'/><path fill='#ffcdd2' stroke='#ffcdd2' stroke-linejoin='round' stroke-width='.293' d='m17.249 24.295.123-.01-.123.02a1.705 1.705 0 0 0 1.67 1.682h.053a1.715 1.715 0 0 0 1.64-1.778 1.78 1.78 0 0 0-.507-1.224 1.6 1.6 0 0 0-1.187-.493h-.076a9.6 9.6 0 0 1-1.154-5.448 6.83 6.83 0 0 1 1.39-3.853 3.97 3.97 0 0 1 2.842-1.363h.055c2.438 0 3.415 3.34 3.477 4.491a.29.29 0 0 0 .216.265c.299.073.822.246 1.213.384a.27.27 0 0 0 .266-.048.3.3 0 0 0 .105-.247C26.928 12.088 24.204 10 21.804 10a5.96 5.96 0 0 0-5.36 4.39 11.38 11.38 0 0 0 .936 9.155 1.5 1.5 0 0 0-.131.75Z'/></svg>",
+ "folder-redux-reducer": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' stroke='#ffcdd2' stroke-linejoin='round' stroke-width='.293' d='M25.948 24.114a1.65 1.65 0 0 0 .97-.6 1.8 1.8 0 0 0 .381-1.274 1.72 1.72 0 0 0-1.69-1.596h-.06a1.724 1.724 0 0 0-1.61 1.814 1.85 1.85 0 0 0 .34.985 8.85 8.85 0 0 1-3.863 3.799 6.15 6.15 0 0 1-3.876.771 3.13 3.13 0 0 1-2.32-1.411 3.67 3.67 0 0 1-.18-3.738 5.8 5.8 0 0 1 1.605-1.986.3.3 0 0 0 .098-.313 14 14 0 0 1-.315-1.298.29.29 0 0 0-.172-.213.28.28 0 0 0-.272.036c-3.731 2.836-3.326 6.763-2.188 8.579a5.36 5.36 0 0 0 4.294 2.229q.125 0 .24-.005h.04a6 6 0 0 0 1.5-.188 9.88 9.88 0 0 0 7.078-5.591Z'/><path fill='#ffcdd2' stroke='#ffcdd2' stroke-linejoin='round' stroke-width='.293' d='M30.327 20.428a10.12 10.12 0 0 0-7.774-3.69q-.133 0-.265.003h-.234a1.61 1.61 0 0 0-1.377-.78h-.053a1.62 1.62 0 0 0-1.175.535 1.806 1.806 0 0 0 .039 2.466 1.67 1.67 0 0 0 1.19.494h.064a1.68 1.68 0 0 0 1.375-.886h.27a8.83 8.83 0 0 1 5.126 1.646 6.6 6.6 0 0 1 2.522 3.202 3.48 3.48 0 0 1-.046 2.831 3.39 3.39 0 0 1-3.137 1.97 5.8 5.8 0 0 1-2.32-.522.27.27 0 0 0-.304.054 14 14 0 0 1-1.088.912.294.294 0 0 0 .039.495 7.7 7.7 0 0 0 3.313.84l.192.002a5.66 5.66 0 0 0 4.886-2.948 6.39 6.39 0 0 0-1.243-6.624Z'/><path fill='#ffcdd2' stroke='#ffcdd2' stroke-linejoin='round' stroke-width='.293' d='m17.249 24.295.123-.01-.123.02a1.705 1.705 0 0 0 1.67 1.682h.053a1.715 1.715 0 0 0 1.64-1.778 1.78 1.78 0 0 0-.507-1.224 1.6 1.6 0 0 0-1.187-.493h-.076a9.6 9.6 0 0 1-1.154-5.448 6.83 6.83 0 0 1 1.39-3.853 3.97 3.97 0 0 1 2.842-1.363h.055c2.438 0 3.415 3.34 3.477 4.491a.29.29 0 0 0 .216.265c.299.073.822.246 1.213.384a.27.27 0 0 0 .266-.048.3.3 0 0 0 .105-.247C26.928 12.088 24.204 10 21.804 10a5.96 5.96 0 0 0-5.36 4.39 11.38 11.38 0 0 0 .936 9.155 1.5 1.5 0 0 0-.131.75Z'/></svg>",
+ "folder-redux-selector-open.clone": "<svg viewBox='0 0 32 32'><path fill='#ff6e40' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffccbc' stroke='#ffccbc' stroke-linejoin='round' stroke-width='.293' d='M25.948 24.114a1.65 1.65 0 0 0 .97-.6 1.8 1.8 0 0 0 .381-1.274 1.72 1.72 0 0 0-1.69-1.596h-.06a1.724 1.724 0 0 0-1.61 1.814 1.85 1.85 0 0 0 .34.985 8.85 8.85 0 0 1-3.863 3.799 6.15 6.15 0 0 1-3.876.771 3.13 3.13 0 0 1-2.32-1.411 3.67 3.67 0 0 1-.18-3.738 5.8 5.8 0 0 1 1.605-1.986.3.3 0 0 0 .098-.313 14 14 0 0 1-.315-1.298.29.29 0 0 0-.172-.213.28.28 0 0 0-.272.036c-3.731 2.836-3.326 6.763-2.188 8.579a5.36 5.36 0 0 0 4.294 2.229q.125 0 .24-.005h.04a6 6 0 0 0 1.5-.188 9.88 9.88 0 0 0 7.078-5.591Z'/><path fill='#ffccbc' stroke='#ffccbc' stroke-linejoin='round' stroke-width='.293' d='M30.327 20.428a10.12 10.12 0 0 0-7.774-3.69q-.133 0-.265.003h-.234a1.61 1.61 0 0 0-1.377-.78h-.053a1.62 1.62 0 0 0-1.175.535 1.806 1.806 0 0 0 .039 2.466 1.67 1.67 0 0 0 1.19.494h.064a1.68 1.68 0 0 0 1.375-.886h.27a8.83 8.83 0 0 1 5.126 1.646 6.6 6.6 0 0 1 2.522 3.202 3.48 3.48 0 0 1-.046 2.831 3.39 3.39 0 0 1-3.137 1.97 5.8 5.8 0 0 1-2.32-.522.27.27 0 0 0-.304.054 14 14 0 0 1-1.088.912.294.294 0 0 0 .039.495 7.7 7.7 0 0 0 3.313.84l.192.002a5.66 5.66 0 0 0 4.886-2.948 6.39 6.39 0 0 0-1.243-6.624Z'/><path fill='#ffccbc' stroke='#ffccbc' stroke-linejoin='round' stroke-width='.293' d='m17.249 24.295.123-.01-.123.02a1.705 1.705 0 0 0 1.67 1.682h.053a1.715 1.715 0 0 0 1.64-1.778 1.78 1.78 0 0 0-.507-1.224 1.6 1.6 0 0 0-1.187-.493h-.076a9.6 9.6 0 0 1-1.154-5.448 6.83 6.83 0 0 1 1.39-3.853 3.97 3.97 0 0 1 2.842-1.363h.055c2.438 0 3.415 3.34 3.477 4.491a.29.29 0 0 0 .216.265c.299.073.822.246 1.213.384a.27.27 0 0 0 .266-.048.3.3 0 0 0 .105-.247C26.928 12.088 24.204 10 21.804 10a5.96 5.96 0 0 0-5.36 4.39 11.38 11.38 0 0 0 .936 9.155 1.5 1.5 0 0 0-.131.75Z'/></svg>",
+ "folder-redux-selector.clone": "<svg viewBox='0 0 32 32'><path fill='#ff6e40' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffccbc' stroke='#ffccbc' stroke-linejoin='round' stroke-width='.293' d='M25.948 24.114a1.65 1.65 0 0 0 .97-.6 1.8 1.8 0 0 0 .381-1.274 1.72 1.72 0 0 0-1.69-1.596h-.06a1.724 1.724 0 0 0-1.61 1.814 1.85 1.85 0 0 0 .34.985 8.85 8.85 0 0 1-3.863 3.799 6.15 6.15 0 0 1-3.876.771 3.13 3.13 0 0 1-2.32-1.411 3.67 3.67 0 0 1-.18-3.738 5.8 5.8 0 0 1 1.605-1.986.3.3 0 0 0 .098-.313 14 14 0 0 1-.315-1.298.29.29 0 0 0-.172-.213.28.28 0 0 0-.272.036c-3.731 2.836-3.326 6.763-2.188 8.579a5.36 5.36 0 0 0 4.294 2.229q.125 0 .24-.005h.04a6 6 0 0 0 1.5-.188 9.88 9.88 0 0 0 7.078-5.591Z'/><path fill='#ffccbc' stroke='#ffccbc' stroke-linejoin='round' stroke-width='.293' d='M30.327 20.428a10.12 10.12 0 0 0-7.774-3.69q-.133 0-.265.003h-.234a1.61 1.61 0 0 0-1.377-.78h-.053a1.62 1.62 0 0 0-1.175.535 1.806 1.806 0 0 0 .039 2.466 1.67 1.67 0 0 0 1.19.494h.064a1.68 1.68 0 0 0 1.375-.886h.27a8.83 8.83 0 0 1 5.126 1.646 6.6 6.6 0 0 1 2.522 3.202 3.48 3.48 0 0 1-.046 2.831 3.39 3.39 0 0 1-3.137 1.97 5.8 5.8 0 0 1-2.32-.522.27.27 0 0 0-.304.054 14 14 0 0 1-1.088.912.294.294 0 0 0 .039.495 7.7 7.7 0 0 0 3.313.84l.192.002a5.66 5.66 0 0 0 4.886-2.948 6.39 6.39 0 0 0-1.243-6.624Z'/><path fill='#ffccbc' stroke='#ffccbc' stroke-linejoin='round' stroke-width='.293' d='m17.249 24.295.123-.01-.123.02a1.705 1.705 0 0 0 1.67 1.682h.053a1.715 1.715 0 0 0 1.64-1.778 1.78 1.78 0 0 0-.507-1.224 1.6 1.6 0 0 0-1.187-.493h-.076a9.6 9.6 0 0 1-1.154-5.448 6.83 6.83 0 0 1 1.39-3.853 3.97 3.97 0 0 1 2.842-1.363h.055c2.438 0 3.415 3.34 3.477 4.491a.29.29 0 0 0 .216.265c.299.073.822.246 1.213.384a.27.27 0 0 0 .266-.048.3.3 0 0 0 .105-.247C26.928 12.088 24.204 10 21.804 10a5.96 5.96 0 0 0-5.36 4.39 11.38 11.38 0 0 0 .936 9.155 1.5 1.5 0 0 0-.131.75Z'/></svg>",
+ "folder-redux-store-open.clone": "<svg viewBox='0 0 32 32'><path fill='#8bc34a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#dcedc8' stroke='#dcedc8' stroke-linejoin='round' stroke-width='.293' d='M25.948 24.114a1.65 1.65 0 0 0 .97-.6 1.8 1.8 0 0 0 .381-1.274 1.72 1.72 0 0 0-1.69-1.596h-.06a1.724 1.724 0 0 0-1.61 1.814 1.85 1.85 0 0 0 .34.985 8.85 8.85 0 0 1-3.863 3.799 6.15 6.15 0 0 1-3.876.771 3.13 3.13 0 0 1-2.32-1.411 3.67 3.67 0 0 1-.18-3.738 5.8 5.8 0 0 1 1.605-1.986.3.3 0 0 0 .098-.313 14 14 0 0 1-.315-1.298.29.29 0 0 0-.172-.213.28.28 0 0 0-.272.036c-3.731 2.836-3.326 6.763-2.188 8.579a5.36 5.36 0 0 0 4.294 2.229q.125 0 .24-.005h.04a6 6 0 0 0 1.5-.188 9.88 9.88 0 0 0 7.078-5.591Z'/><path fill='#dcedc8' stroke='#dcedc8' stroke-linejoin='round' stroke-width='.293' d='M30.327 20.428a10.12 10.12 0 0 0-7.774-3.69q-.133 0-.265.003h-.234a1.61 1.61 0 0 0-1.377-.78h-.053a1.62 1.62 0 0 0-1.175.535 1.806 1.806 0 0 0 .039 2.466 1.67 1.67 0 0 0 1.19.494h.064a1.68 1.68 0 0 0 1.375-.886h.27a8.83 8.83 0 0 1 5.126 1.646 6.6 6.6 0 0 1 2.522 3.202 3.48 3.48 0 0 1-.046 2.831 3.39 3.39 0 0 1-3.137 1.97 5.8 5.8 0 0 1-2.32-.522.27.27 0 0 0-.304.054 14 14 0 0 1-1.088.912.294.294 0 0 0 .039.495 7.7 7.7 0 0 0 3.313.84l.192.002a5.66 5.66 0 0 0 4.886-2.948 6.39 6.39 0 0 0-1.243-6.624Z'/><path fill='#dcedc8' stroke='#dcedc8' stroke-linejoin='round' stroke-width='.293' d='m17.249 24.295.123-.01-.123.02a1.705 1.705 0 0 0 1.67 1.682h.053a1.715 1.715 0 0 0 1.64-1.778 1.78 1.78 0 0 0-.507-1.224 1.6 1.6 0 0 0-1.187-.493h-.076a9.6 9.6 0 0 1-1.154-5.448 6.83 6.83 0 0 1 1.39-3.853 3.97 3.97 0 0 1 2.842-1.363h.055c2.438 0 3.415 3.34 3.477 4.491a.29.29 0 0 0 .216.265c.299.073.822.246 1.213.384a.27.27 0 0 0 .266-.048.3.3 0 0 0 .105-.247C26.928 12.088 24.204 10 21.804 10a5.96 5.96 0 0 0-5.36 4.39 11.38 11.38 0 0 0 .936 9.155 1.5 1.5 0 0 0-.131.75Z'/></svg>",
+ "folder-redux-store.clone": "<svg viewBox='0 0 32 32'><path fill='#8bc34a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#dcedc8' stroke='#dcedc8' stroke-linejoin='round' stroke-width='.293' d='M25.948 24.114a1.65 1.65 0 0 0 .97-.6 1.8 1.8 0 0 0 .381-1.274 1.72 1.72 0 0 0-1.69-1.596h-.06a1.724 1.724 0 0 0-1.61 1.814 1.85 1.85 0 0 0 .34.985 8.85 8.85 0 0 1-3.863 3.799 6.15 6.15 0 0 1-3.876.771 3.13 3.13 0 0 1-2.32-1.411 3.67 3.67 0 0 1-.18-3.738 5.8 5.8 0 0 1 1.605-1.986.3.3 0 0 0 .098-.313 14 14 0 0 1-.315-1.298.29.29 0 0 0-.172-.213.28.28 0 0 0-.272.036c-3.731 2.836-3.326 6.763-2.188 8.579a5.36 5.36 0 0 0 4.294 2.229q.125 0 .24-.005h.04a6 6 0 0 0 1.5-.188 9.88 9.88 0 0 0 7.078-5.591Z'/><path fill='#dcedc8' stroke='#dcedc8' stroke-linejoin='round' stroke-width='.293' d='M30.327 20.428a10.12 10.12 0 0 0-7.774-3.69q-.133 0-.265.003h-.234a1.61 1.61 0 0 0-1.377-.78h-.053a1.62 1.62 0 0 0-1.175.535 1.806 1.806 0 0 0 .039 2.466 1.67 1.67 0 0 0 1.19.494h.064a1.68 1.68 0 0 0 1.375-.886h.27a8.83 8.83 0 0 1 5.126 1.646 6.6 6.6 0 0 1 2.522 3.202 3.48 3.48 0 0 1-.046 2.831 3.39 3.39 0 0 1-3.137 1.97 5.8 5.8 0 0 1-2.32-.522.27.27 0 0 0-.304.054 14 14 0 0 1-1.088.912.294.294 0 0 0 .039.495 7.7 7.7 0 0 0 3.313.84l.192.002a5.66 5.66 0 0 0 4.886-2.948 6.39 6.39 0 0 0-1.243-6.624Z'/><path fill='#dcedc8' stroke='#dcedc8' stroke-linejoin='round' stroke-width='.293' d='m17.249 24.295.123-.01-.123.02a1.705 1.705 0 0 0 1.67 1.682h.053a1.715 1.715 0 0 0 1.64-1.778 1.78 1.78 0 0 0-.507-1.224 1.6 1.6 0 0 0-1.187-.493h-.076a9.6 9.6 0 0 1-1.154-5.448 6.83 6.83 0 0 1 1.39-3.853 3.97 3.97 0 0 1 2.842-1.363h.055c2.438 0 3.415 3.34 3.477 4.491a.29.29 0 0 0 .216.265c.299.073.822.246 1.213.384a.27.27 0 0 0 .266-.048.3.3 0 0 0 .105-.247C26.928 12.088 24.204 10 21.804 10a5.96 5.96 0 0 0-5.36 4.39 11.38 11.38 0 0 0 .936 9.155 1.5 1.5 0 0 0-.131.75Z'/></svg>",
+ "folder-repository-open": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c8e6c9' d='M20 10a2 2 0 0 0-1.6.8l-1.6 2.134a4 4 0 0 0-.8 2.398V26a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V15.332a4 4 0 0 0-.8-2.398L29.6 10.8A2 2 0 0 0 28 10zm0 2h8l1.5 2h-11zm2 4h4v4h4l-6 6-6-6h4z'/></svg>",
+ "folder-repository": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c8e6c9' d='M20 10a2 2 0 0 0-1.6.8l-1.6 2.134a4 4 0 0 0-.8 2.398V26a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V15.332a4 4 0 0 0-.8-2.398L29.6 10.8A2 2 0 0 0 28 10zm0 2h8l1.5 2h-11zm2 4h4v4h4l-6 6-6-6h4z'/></svg>",
+ "folder-resolver-open": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c8e6c9' d='M29.216 14 20.6 22.159l-3.816-3.614L14 21.183 20.6 28 32 17.205Z'/></svg>",
+ "folder-resolver": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c8e6c9' d='M29.216 14 20.6 22.159l-3.816-3.614L14 21.183 20.6 28 32 17.205Z'/></svg>",
+ "folder-resource-open": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fff9c4' d='M16 16h-2v13a1 1 0 0 0 1 1h13v-2H16Z'/><path fill='#fff9c4' d='M31 12H19a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V13a1 1 0 0 0-1-1m-5 12h-6v-2h6Zm4-4H20v-2h10Zm0-4H20v-2h10Z'/></svg>",
+ "folder-resource": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fff9c4' d='M16 16h-2v13a1 1 0 0 0 1 1h13v-2H16Z'/><path fill='#fff9c4' d='M31 12H19a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V13a1 1 0 0 0-1-1m-5 12h-6v-2h6Zm4-4H20v-2h10Zm0-4H20v-2h10Z'/></svg>",
+ "folder-review-open": "<svg viewBox='0 0 32 32'><path fill='#2196f3' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><circle cx='21' cy='21' r='3' fill='#bbdefb'/><path fill='#bbdefb' d='M21 14c-4.66 0-9.35 2.91-11 7 1.65 4.09 6.34 7 11 7s9.35-2.91 11-7c-1.65-4.09-6.34-7-11-7m0 12a5 5 0 1 1 5-5 5 5 0 0 1-5 5'/></svg>",
+ "folder-review": "<svg viewBox='0 0 32 32'><path fill='#2196f3' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><circle cx='21' cy='21' r='3' fill='#bbdefb'/><path fill='#bbdefb' d='M21 14c-4.66 0-9.35 2.91-11 7 1.65 4.09 6.34 7 11 7s9.35-2.91 11-7c-1.65-4.09-6.34-7-11-7m0 12a5 5 0 1 1 5-5 5 5 0 0 1-5 5'/></svg>",
+ "folder-robot-open": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M10.5 26H12v-6h-1.5a.5.5 0 0 0-.5.5v5a.5.5 0 0 0 .5.5M30 20v6h1.5a.5.5 0 0 0 .5-.5v-5a.5.5 0 0 0-.5-.5Zm-8.5-8h-1a.5.5 0 0 0-.5.5V16h-5.5a.5.5 0 0 0-.5.5v11a.5.5 0 0 0 .5.5h13a.5.5 0 0 0 .5-.5v-11a.5.5 0 0 0-.5-.5H22v-3.5a.5.5 0 0 0-.5-.5M18 18a2 2 0 1 1-2 2 2 2 0 0 1 2-2m8 8H16v-2h10Zm-2-8a2 2 0 1 1-2 2 2 2 0 0 1 2-2'/></svg>",
+ "folder-robot": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M10.5 26H12v-6h-1.5a.5.5 0 0 0-.5.5v5a.5.5 0 0 0 .5.5M30 20v6h1.5a.5.5 0 0 0 .5-.5v-5a.5.5 0 0 0-.5-.5Zm-8.5-8h-1a.5.5 0 0 0-.5.5V16h-5.5a.5.5 0 0 0-.5.5v11a.5.5 0 0 0 .5.5h13a.5.5 0 0 0 .5-.5v-11a.5.5 0 0 0-.5-.5H22v-3.5a.5.5 0 0 0-.5-.5M18 18a2 2 0 1 1-2 2 2 2 0 0 1 2-2m8 8H16v-2h10Zm-2-8a2 2 0 1 1-2 2 2 2 0 0 1 2-2'/></svg>",
+ "folder-root-open": "<svg viewBox='0 0 16 16'><circle cx='8' cy='8' r='6' fill='none' stroke='#90a4ae' stroke-width='2'/></svg>",
+ "folder-root": "<svg viewBox='0 0 16 16'><circle cx='8' cy='8' r='6' fill='none' stroke='#90a4ae' stroke-width='2'/><circle cx='8' cy='8' r='3' fill='#90a4ae'/></svg>",
+ "folder-routes-open": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c8e6c9' d='M17.414 14.586 20 12h-8v8l2.586-2.586 4.91 4.91A1.7 1.7 0 0 1 20 23.541V28h4v-4.459a5.68 5.68 0 0 0-1.676-4.045ZM29.36 12l-5.61 4.93.57.57a5.6 5.6 0 0 1 1.56 2.89L32 15.01Z'/></svg>",
+ "folder-routes": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c8e6c9' d='M17.414 14.586 20 12h-8v8l2.586-2.586 4.91 4.91A1.7 1.7 0 0 1 20 23.541V28h4v-4.459a5.68 5.68 0 0 0-1.676-4.045ZM29.36 12l-5.61 4.93.57.57a5.6 5.6 0 0 1 1.56 2.89L32 15.01Z'/></svg>",
+ "folder-rules-open": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M30 14h-2a3 3 0 0 0-6 0h-2a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V16a2 2 0 0 0-2-2m-5-1a1 1 0 1 1-1 1 1.003 1.003 0 0 1 1-1m-1.547 11.597-3.093-3.093 1.09-1.09 2.003 1.995 5.096-5.096 1.091 1.099Z'/></svg>",
+ "folder-rules": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M30 14h-2a3 3 0 0 0-6 0h-2a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V16a2 2 0 0 0-2-2m-5-1a1 1 0 1 1-1 1 1.003 1.003 0 0 1 1-1m-1.547 11.597-3.093-3.093 1.09-1.09 2.003 1.995 5.096-5.096 1.091 1.099Z'/></svg>",
+ "folder-rust-open": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffccbc' d='M30 24v1a1 1 0 0 1-2 0v-1a2 2 0 0 0-2-2 3 3 0 0 0 2.996-3.16A3.115 3.114 0 0 0 25.83 16H14v2h2v8h-2v2h8v-2h-2v-2h4c.82.819.297 2.308 1.179 3.37a1.89 1.89 0 0 0 1.46.63H32v-4zm-6-4h-4v-2h4a1 1 0 0 1 0 2'/></svg>",
+ "folder-rust": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffccbc' d='M30 24v1a1 1 0 0 1-2 0v-1a2 2 0 0 0-2-2 3 3 0 0 0 2.996-3.16A3.115 3.114 0 0 0 25.83 16H14v2h2v8h-2v2h8v-2h-2v-2h4c.82.819.297 2.308 1.179 3.37a1.89 1.89 0 0 0 1.46.63H32v-4zm-6-4h-4v-2h4a1 1 0 0 1 0 2'/></svg>",
+ "folder-sandbox-open": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='M28.965 12.001H9.44a2 2 0 0 0-1.898 1.368L3.998 24.001v-14h24a2 2 0 0 0-2-2H15.122a2 2 0 0 1-1.28-.464l-1.288-1.072a2 2 0 0 0-1.28-.464H3.998a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212a2 2 0 0 0-1.838-2.788'/><path fill='#bbdefb' d='M21 20h-6a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1m7-8a4 4 0 1 0 4 4 4 4 0 0 0-4-4m.707 17.707 3-3a1 1 0 0 0 0-1.414l-3-3a1 1 0 0 0-1.414 0l-3 3a1 1 0 0 0 0 1.414l3 3a1 1 0 0 0 1.414 0m-11.581-7.193-2.999 6A1 1 0 0 0 15.001 30H21a1 1 0 0 0 .874-1.486l-2.999-6a1 1 0 0 0-1.748 0'/></svg>",
+ "folder-sandbox": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M21 20h-6a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1m7-8a4 4 0 1 0 4 4 4 4 0 0 0-4-4m.707 17.707 3-3a1 1 0 0 0 0-1.414l-3-3a1 1 0 0 0-1.414 0l-3 3a1 1 0 0 0 0 1.414l3 3a1 1 0 0 0 1.414 0m-11.581-7.193-2.999 6A1 1 0 0 0 15.001 30H21a1 1 0 0 0 .874-1.486l-2.999-6a1 1 0 0 0-1.748 0'/></svg>",
+ "folder-sass-open": "<svg viewBox='0 0 32 32'><path fill='#f06292' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fce4ec' d='M31.897 12.592a3 3 0 0 0-1.53-1.912 7.95 7.95 0 0 0-6.348-.05 17.4 17.4 0 0 0-5.864 3.557c-1.83 1.81-2.288 3.496-2.124 4.39.346 1.89 2.181 3.227 3.658 4.3.314.23.618.45.876.657-.92.513-2.916 1.749-3.483 3.074a2.9 2.9 0 0 0-.074 2.347 1.57 1.57 0 0 0 .874.903 3.5 3.5 0 0 0 .986.142 4.14 4.14 0 0 0 3.438-2.025 5.03 5.03 0 0 0 .55-3.886 4.5 4.5 0 0 1 1.46-.034 2.64 2.64 0 0 1 1.927.96 1.44 1.44 0 0 1 .304.968 1.2 1.2 0 0 1-.55.805c-.159.104-.356.233-.31.504.028.151.13.393.532.313a1.99 1.99 0 0 0 1.392-1.841 2.9 2.9 0 0 0-.801-2.051 3.9 3.9 0 0 0-2.897-1.135 6.5 6.5 0 0 0-1.813.226 13 13 0 0 0-1.498-1.346c-1.165-.947-2.265-1.842-2.2-3.125.085-1.654 1.672-3.306 4.716-4.909 2.7-1.422 4.894-1.47 6.04-1.041a1.44 1.44 0 0 1 .858.674 2.23 2.23 0 0 1-.257 1.866 6.57 6.57 0 0 1-5.023 3.105 2.56 2.56 0 0 1-2.225-.565c-.189-.219-.37-.423-.65-.263-.332.196-.175.625-.123.768a2.6 2.6 0 0 0 1.578 1.342 7.32 7.32 0 0 0 4.752-.482c2.631-1.078 4.384-3.933 3.83-6.236ZM21.301 26.118a3 3 0 0 1-.13.345 3.4 3.4 0 0 1-.517.795c-.648.743-1.499.978-1.776.808a.27.27 0 0 1-.088-.187 2.5 2.5 0 0 1 .742-1.704 7.8 7.8 0 0 1 1.865-1.445 3.05 3.05 0 0 1-.096 1.388'/></svg>",
+ "folder-sass": "<svg viewBox='0 0 32 32'><path fill='#f06292' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fce4ec' d='M31.897 12.592a3 3 0 0 0-1.53-1.912 7.95 7.95 0 0 0-6.348-.05 17.4 17.4 0 0 0-5.864 3.557c-1.83 1.81-2.288 3.496-2.124 4.39.346 1.89 2.181 3.227 3.658 4.3.314.23.618.45.876.657-.92.513-2.916 1.749-3.483 3.074a2.9 2.9 0 0 0-.074 2.347 1.57 1.57 0 0 0 .874.903 3.5 3.5 0 0 0 .986.142 4.14 4.14 0 0 0 3.438-2.025 5.03 5.03 0 0 0 .55-3.886 4.5 4.5 0 0 1 1.46-.034 2.64 2.64 0 0 1 1.927.96 1.44 1.44 0 0 1 .304.968 1.2 1.2 0 0 1-.55.805c-.159.104-.356.233-.31.504.028.151.13.393.532.313a1.99 1.99 0 0 0 1.392-1.841 2.9 2.9 0 0 0-.801-2.051 3.9 3.9 0 0 0-2.897-1.135 6.5 6.5 0 0 0-1.813.226 13 13 0 0 0-1.498-1.346c-1.165-.947-2.265-1.842-2.2-3.125.085-1.654 1.672-3.306 4.716-4.909 2.7-1.422 4.894-1.47 6.04-1.041a1.44 1.44 0 0 1 .858.674 2.23 2.23 0 0 1-.257 1.866 6.57 6.57 0 0 1-5.023 3.105 2.56 2.56 0 0 1-2.225-.565c-.189-.219-.37-.423-.65-.263-.332.196-.175.625-.123.768a2.6 2.6 0 0 0 1.578 1.342 7.32 7.32 0 0 0 4.752-.482c2.631-1.078 4.384-3.933 3.83-6.236ZM21.301 26.118a3 3 0 0 1-.13.345 3.4 3.4 0 0 1-.517.795c-.648.743-1.499.978-1.776.808a.27.27 0 0 1-.088-.187 2.5 2.5 0 0 1 .742-1.704 7.8 7.8 0 0 1 1.865-1.445 3.05 3.05 0 0 1-.096 1.388'/></svg>",
+ "folder-scala-open": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M18 26v4c2.281-.781 8.713-1.025 10-2v-4c-1.287.975-7.719 1.219-10 2m0-6v4c2.281-.781 8.713-1.025 10-2v-4c-1.287.975-7.719 1.219-10 2m0-6v4c2.281-.781 8.713-1.025 10-2v-4c-1.287.975-7.719 1.219-10 2'/></svg>",
+ "folder-scala": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M18 26v4c2.281-.781 8.713-1.025 10-2v-4c-1.287.975-7.719 1.219-10 2m0-6v4c2.281-.781 8.713-1.025 10-2v-4c-1.287.975-7.719 1.219-10 2m0-6v4c2.281-.781 8.713-1.025 10-2v-4c-1.287.975-7.719 1.219-10 2'/></svg>",
+ "folder-scons-open": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M12 24h8v4h-8Zm12 0h8v4h-8Zm-12-6h4v4h-4Zm16 0h4v4h-4Zm-10-8h8v4h-8Zm4 14-4-4h2v-4h4v4h2Z'/></svg>",
+ "folder-scons": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124c-.468 0-.921-.164-1.28-.464'/><path fill='#ffcdd2' d='M12 24h8v4h-8Zm12 0h8v4h-8Zm-12-6h4v4h-4Zm16 0h4v4h-4Zm-10-8h8v4h-8Zm4 14-4-4h2v-4h4v4h2Z'/></svg>",
+ "folder-scripts-open": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#cfd8dc' d='M28 12h-6a4 4 0 0 0-4 4v8h2v-8h8v9.893a2.074 2.074 0 0 1-1.664 2.08A2 2 0 0 1 24 26h-8a4 4 0 0 0 4 4h6a4 4 0 0 0 4-4V16h2a4 4 0 0 0-4-4'/></svg>",
+ "folder-scripts": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#cfd8dc' d='M28 12h-6a4 4 0 0 0-4 4v8h2v-8h8v9.893a2.074 2.074 0 0 1-1.664 2.08A2 2 0 0 1 24 26h-8a4 4 0 0 0 4 4h6a4 4 0 0 0 4-4V16h2a4 4 0 0 0-4-4'/></svg>",
+ "folder-secure-open": "<svg viewBox='0 0 32 32'><path fill='#f9a825' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fff9c4' d='M28 16v-3.828a4.116 4.116 0 0 0-3.607-4.153A4 4 0 0 0 20 12v4h-2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2Zm-4 8a2 2 0 1 1 2-2 2 2 0 0 1-2 2m2-8h-4v-4a2 2 0 0 1 4 0Z'/></svg>",
+ "folder-secure": "<svg viewBox='0 0 32 32'><path fill='#f9a825' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fff9c4' d='M28 16v-3.828a4.116 4.116 0 0 0-3.607-4.153A4 4 0 0 0 20 12v4h-2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2Zm-4 8a2 2 0 1 1 2-2 2 2 0 0 1-2 2m2-8h-4v-4a2 2 0 0 1 4 0Z'/></svg>",
+ "folder-seeders-open": "<svg clip-rule='evenodd' image-rendering='optimizeQuality' shape-rendering='geometricPrecision' text-rendering='geometricPrecision' viewBox='0 0 3200 3200'><path fill='#43a047' d='M2340.5 1199.5c-18.05 5.55-36.72 11.21-56 17a565 565 0 0 0-62 21q-441.75 165.75-603 609c-2.04 4.44-3.7 9.11-5 14-1.4 3.2-2.4 6.53-3 10-3.64 7.59-6.31 15.59-8 24-2.11 4-3.44 8.34-4 13-.61.89-.94 1.89-1 3-1.78 3-2.78 6.34-3 10-.67 2.67-1.33 5.33-2 8-1.57 1.6-2.24 3.6-2 6-.92 2.92-1.58 5.92-2 9-1.9 2.2-2.9 4.86-3 8-.33 2-.67 4-1 6-1.42 2.01-2.09 4.35-2 7v1c-.62.11-1.12.44-1.5 1a103 103 0 0 0-3.5 17c-21.96 92.4-35.63 186.07-41 281a821 821 0 0 0-4 73v2c-1.16 46.16-1.33 92.5-.5 139 .89 16.16 1.73 32.16 2.5 48v11c.18 7.19.85 14.19 2 21 .47 13.68 1.13 27.34 2 41-386.33.17-772.67 0-1159-.5q-122.657-16.17-169.5-130.5a230.2 230.2 0 0 1-11-49c-.667-546.67-.667-1093.3 0-1640 9.834-76.145 49.334-130.64 118.5-163.5q29.688-12.892 62-16 387.03-1.49 774 1c40.68 5.845 76.34 22.178 107 49 45.37 38.377 91.04 76.377 137 114 33.84 22.572 71.17 34.238 112 35l1108 1q101.4 12.037 154.5 98.5c17.74 31.305 26.58 64.972 26.5 101h-2400c-.167 465.67 0 931.33.5 1397l357-1071c28.15-65.48 76.984-106.31 146.5-122.5a307 307 0 0 1 20-3c472.33-.5 944.67-.67 1417-.5' opacity='.999'/><path fill='#a5d6a7' d='M3071.5 1301.5c-1.15 5.48-1.32 11.15-.5 17 .28.92.78 1.58 1.5 2v4c-.17 6.34 0 12.68.5 19q.345 1.86 1.5 3v5c-.32 9.22.34 18.22 2 27v10c.15 12.67.49 25.34 1 38-.17 19 0 38 .5 57 .28-.92.78-1.58 1.5-2q.585 196.035-43.5 387c-27.87 114.37-71.2 222.37-130 324-21.76 34.53-44.76 68.2-69 101a1351 1351 0 0 1-82.5 92.5c-16.67 15-33.33 30-50 45a2628 2628 0 0 1-49 38.5c-73.84 51.95-153.18 95.28-238 130-60.65 24.38-122.99 43.88-187 58.5-113.01 24.48-227.35 38.15-343 41-68.76 1.79-137.42.13-206-5-45.06-3.26-90.06-7.1-135-11.5-3.11-27.6-5.45-55.26-7-83-.87-13.66-1.53-27.32-2-41 .24-7.23-.43-14.23-2-21v-11c-1.58-62.32-2.25-124.66-2-187v-2c.82-2.47 1.32-5.14 1.5-8 1.01-21.66 1.84-43.33 2.5-65 5.37-94.93 19.04-188.6 41-281 1.96-5.81 3.62-11.81 5-18v-1c1.42-2.01 2.09-4.35 2-7 .33-2 .67-4 1-6 1.6-2.35 2.6-5.01 3-8 .42-3.08 1.08-6.08 2-9 1.57-1.6 2.24-3.6 2-6 .67-2.67 1.33-5.33 2-8 1.78-3 2.78-6.34 3-10 .06-1.11.39-2.11 1-3 2.11-4 3.44-8.34 4-13a169 169 0 0 0 8-24c.6-3.47 1.6-6.8 3-10 2.82-4.13 4.49-8.8 5-14q161.25-443.25 603-609c20.86-6.62 41.53-13.62 62-21 19.28-5.79 37.95-11.45 56-17 29.89-8.48 60.22-15.65 91-21.5 191.75-33.99 384.08-37.66 577-11 14.64 2.55 29.3 4.88 44 7 1.2.9 2.03 2.07 2.5 3.5 6.67 41.19 12.17 82.52 16.5 124'/><path fill='#43a047' d='M3071.5 1303.5c19.44 34.7 27.61 72.03 24.5 112-1.77 22.43-7.27 43.77-16.5 64 .23-17.69-.1-35.35-1-53 .06-12.86-.61-25.53-2-38v-10c-.22-9.18-.88-18.18-2-27v-5c-.13-7.53-.79-14.86-2-22v-4c-.26-5.67-.59-11.34-1-17m-398 192h1c17.51-.33 34.84 0 52 1-1.33.67-2.67 1.33-4 2-3.65.77-6.98 2.1-10 4a950 950 0 0 0-105 50.5c-121.59 68.07-232.26 150.91-332 248.5a178.3 178.3 0 0 0-23 22c-96.64 96.09-181.81 201.09-255.5 315a2166 2166 0 0 0-119.5 210c-1.41.47-2.07 1.47-2 3v1q-1.17-19.245 0-39v-2c1.45-5.77 2.11-11.77 2-18 .24-7.06.91-14.06 2-21 1.75-6.43 2.75-13.09 3-20v-1c2.07-8.76 3.4-17.76 4-27v-2c1.72-3.34 2.38-7.01 2-11 2.57-6.3 3.9-12.97 4-20 .03-4.14.7-8.14 2-12 1.42-2.01 2.09-4.35 2-7v-2c2.54-3.85 3.54-8.19 3-13 17.15-76.63 42.32-150.3 75.5-221 51.76-109.33 124.93-201.83 219.5-277.5 91.03-69.67 193.03-116.5 306-140.5 4.81.54 9.15-.46 13-3 9.03-.41 17.7-2.07 26-5 8.58-.59 16.91-1.92 25-4 6.91-.25 13.57-1.25 20-3 6.26-1.24 12.59-1.91 19-2 14.8-1.38 29.47-2.72 44-4h2c8.18-.28 16.18-.94 24-2'/></svg>",
+ "folder-seeders": "<svg clip-rule='evenodd' image-rendering='optimizeQuality' shape-rendering='geometricPrecision' text-rendering='geometricPrecision' viewBox='0 0 3200 3200'><path fill='#43a047' d='M2999.5 1165.5c-30.43-3.18-61.1-6.35-92-9.5-167.75-14.9-334.08-5.57-499 28a1455 1455 0 0 0-207 61c-79.51 31.74-154.18 71.74-224 120a896 896 0 0 0-55 43c-1 .33-2 .67-3 1-46.92 39.92-90.09 83.42-129.5 130.5a1986 1986 0 0 1-45 60c-61.77 91.89-109.44 190.89-143 297a1660.6 1660.6 0 0 0-53 246c-14.98 118.89-19.98 238.22-15 358 1.34 33.03 3.17 66.03 5.5 99-386.33.17-772.67 0-1159-.5q-122.657-16.17-169.5-130.5a230.2 230.2 0 0 1-11-49c-.667-546.67-.667-1093.3 0-1640 9.834-76.145 49.334-130.64 118.5-163.5q29.688-12.892 62-16c255.33-.667 510.67-.667 766 0 43.68 4.875 82.01 21.541 115 50 45.37 38.377 91.04 76.377 137 114 28.54 18.847 59.87 30.181 94 34q662.985 1.986 1326 2c77.31 10.044 132.48 50.211 165.5 120.5a220.4 220.4 0 0 1 15 60c.5 61.67.67 123.33.5 185'/><path fill='#a5d6a7' d='M2999.5 1165.5c17.69 2.74 35.36 5.91 53 9.5 1.08.67 1.75 1.67 2 3 28.18 168.19 32.68 337.02 13.5 506.5-11.97 104.19-34.8 205.86-68.5 305-33.03 92.41-76.87 179.07-131.5 260a1694 1694 0 0 1-77 97 2240 2240 0 0 1-80.5 78.5c-46.48 38.13-95.48 72.8-147 104a1457 1457 0 0 1-145 70.5c-24.7 9.85-49.7 19.01-75 27.5-149 44.89-301.33 68.89-457 72-64.06 1.52-128.06.19-192-4-49.39-3.53-98.73-7.7-148-12.5a1745 1745 0 0 1-7-83 3304 3304 0 0 1-5.5-99c-4.98-119.78.02-239.11 15-358a1660.6 1660.6 0 0 1 53-246c33.56-106.11 81.23-205.11 143-297q23.07-29.58 45-60c39.41-47.08 82.58-90.58 129.5-130.5 1-.33 2-.67 3-1a896 896 0 0 1 55-43c69.82-48.26 144.49-88.26 224-120a1455 1455 0 0 1 207-61c164.92-33.57 331.25-42.9 499-28 30.9 3.15 61.57 6.32 92 9.5' opacity='.999'/><path fill='#43a047' d='M2709.5 1494.5c7.67-.17 15.34 0 23 .5a825 825 0 0 0-130 61c-97.27 54.78-187.6 119.12-271 193-15.7 14.7-31.7 29.04-48 43a4541 4541 0 0 0-111.5 115.5 2730 2730 0 0 1-57 68c-93.1 116.73-172.93 242.4-239.5 377-.5 1.02-1.17 1.19-2 .5 6.63-168.23 50.47-326.06 131.5-473.5 116.36-197.54 287.53-319.37 513.5-365.5 24.5-4.98 49.17-8.98 74-12q58.635-5.25 117-7.5'/><path fill='#43a047' d='M2999.5 1989.5c.17 143 0 286-.5 429-8.62 70.79-43.78 123.62-105.5 158.5q-31.365 15.615-66 21c-136.31 1.48-272.65 1.98-409 1.5 49.63-20.82 97.96-44.32 145-70.5 51.52-31.2 100.52-65.87 147-104a2240 2240 0 0 0 80.5-78.5c26.87-31.42 52.54-63.76 77-97 54.63-80.93 98.47-167.59 131.5-260' opacity='.999'/></svg>",
+ "folder-server-open": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fffde7' d='M14 15v4a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1H15a1 1 0 0 0-1 1m6 3h-4v-2h4Zm4 0h-2v-2h2Zm-10 5v4a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1H15a1 1 0 0 0-1 1m6 3h-4v-2h4Zm4 0h-2v-2h2Z'/></svg>",
+ "folder-server": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fffde7' d='M14 15v4a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1H15a1 1 0 0 0-1 1m6 3h-4v-2h4Zm4 0h-2v-2h2Zm-10 5v4a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1H15a1 1 0 0 0-1 1m6 3h-4v-2h4Zm4 0h-2v-2h2Z'/></svg>",
+ "folder-serverless-open": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M14 14h6l-.8 3H14zm18 0v3h-9.8l.8-3zm-18 6h4.4l-.8 3H14zm18 0v3H20.6l.8-3zm-18 6h2.8l-.8 3h-2zm18 0v3H19l.8-3z'/></svg>",
+ "folder-serverless": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M14 14h6l-.8 3H14zm18 0v3h-9.8l.8-3zm-18 6h4.4l-.8 3H14zm18 0v3H20.6l.8-3zm-18 6h2.8l-.8 3h-2zm18 0v3H19l.8-3z'/></svg>",
+ "folder-shader-open": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#e1bee7' d='M21.58 25.999A3.9 3.9 0 0 1 17.79 30a4.007 4.007 0 0 1 0-8.003A3.9 3.9 0 0 1 21.58 26Zm4.704-9.47a3.966 3.966 0 0 0-3.257-4.494 3.833 3.833 0 0 0-4.255 3.439 4.026 4.026 0 0 0 2.457 4.285c2.905 1.205 3.59 2.423 3.226 5.71a3.967 3.967 0 0 0 3.254 4.495 3.83 3.83 0 0 0 4.257-3.435 4.03 4.03 0 0 0-2.456-4.288c-2.905-1.207-3.59-2.427-3.225-5.713Z'/></svg>",
+ "folder-shader": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#e1bee7' d='M21.58 25.999A3.9 3.9 0 0 1 17.79 30a4.007 4.007 0 0 1 0-8.003A3.9 3.9 0 0 1 21.58 26Zm4.704-9.47a3.966 3.966 0 0 0-3.257-4.494 3.833 3.833 0 0 0-4.255 3.439 4.026 4.026 0 0 0 2.457 4.285c2.905 1.205 3.59 2.423 3.226 5.71a3.967 3.967 0 0 0 3.254 4.495 3.83 3.83 0 0 0 4.257-3.435 4.03 4.03 0 0 0-2.456-4.288c-2.905-1.207-3.59-2.427-3.225-5.713Z'/></svg>",
+ "folder-shared-open": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#e1bee7' d='M28 26a2 2 0 0 0 2-2v-6a2 2 0 0 0-2-2H18a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h-4v2h18v-2Zm-5-6v-2l4 2.798L23 24v-2a4.12 4.12 0 0 0-4 2c.448-2.003.888-3.595 4-4'/></svg>",
+ "folder-shared": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#e1bee7' d='M28 26a2 2 0 0 0 2-2v-6a2 2 0 0 0-2-2H18a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h-4v2h18v-2Zm-5-6v-2l4 2.798L23 24v-2a4.12 4.12 0 0 0-4 2c.448-2.003.888-3.595 4-4'/></svg>",
+ "folder-snapcraft-open": "<svg viewBox='0 0 16 16'><path fill='#66bb6a' d='M14.033 6H4.597a.97.97 0 0 0-.918.684L2 12V5h11.566c0-.552-.433-1-.967-1H7.343a.95.95 0 0 1-.619-.232l-.622-.536A.95.95 0 0 0 5.483 3H1.967C1.433 3 1 3.448 1 4v8c0 .552.433 1 .967 1h10.632l2.323-5.606c.273-.66-.195-1.394-.889-1.394'/><path fill='#dcedc8' d='m12.538 7.077 2.077 1.038-2.077 2.077zM8.385 14l3.807-3.462-1.73-1.73zM7 5l5.192 5.192V7.077zm8.654 2.077-3.116-.346L16 8.46z'/></svg>",
+ "folder-snapcraft": "<svg viewBox='0 0 16 16'><path fill='#66bb6a' d='m6.922 3.768-.644-.536A1 1 0 0 0 5.638 3H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H7.562a1 1 0 0 1-.64-.232'/><path fill='#dcedc8' d='m12.538 7.077 2.077 1.038-2.077 2.077zM8.385 14l3.807-3.462-1.73-1.73zM7 5l5.192 5.192V7.077zm8.654 2.077-3.116-.346L16 8.46z'/></svg>",
+ "folder-snippet-open": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='M29 12H9.4a2 2 0 0 0-1.9 1.4L4 24V10h24a2 2 0 0 0-2-2H15.1a2 2 0 0 1-1.3-.5l-1.2-1a2 2 0 0 0-1.3-.5H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.8-11.2A2 2 0 0 0 29 12'/><path fill='#ffe0b2' d='M20 10s-2 1.86-2 4 6 8 6 8l-.89.88a3 3 0 0 0-5.1 2A3.01 3.01 0 0 0 20.87 28a3.02 3.02 0 0 0 3.11-3.24L26 23.5l.25.31A3.02 3.02 0 0 0 28.88 28 3.01 3.01 0 0 0 32 25.12a3.01 3.01 0 0 0-4.38-2.78zm10 0-4 8 2 2s4-3.94 4-6-2-4-2-4m-9.06 14h.1c.51.02.9.4.95.89v.2a.98.98 0 0 1-1.03.91.99.99 0 0 1-.96-1.04.98.98 0 0 1 .94-.96m8 0h.1c.56.02.98.48.96 1.04a.98.98 0 0 1-1.04.96.98.98 0 0 1-.96-1.04.98.98 0 0 1 .94-.96' style='-inkscape-stroke:none'/></svg>",
+ "folder-snippet": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='m13.84 7.54-1.28-1.08A2 2 0 0 0 11.28 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.12a2 2 0 0 1-1.28-.46'/><path fill='#ffe0b2' d='M20 10s-2 1.86-2 4 6 8 6 8l-.89.88a3 3 0 0 0-5.1 2A3.01 3.01 0 0 0 20.87 28a3.02 3.02 0 0 0 3.11-3.24L26 23.5l.25.31A3.02 3.02 0 0 0 28.88 28 3.01 3.01 0 0 0 32 25.12a3.01 3.01 0 0 0-4.38-2.78zm10 0-4 8 2 2s4-3.94 4-6-2-4-2-4m-9.06 14h.1c.51.02.9.4.95.89v.2a.98.98 0 0 1-1.03.91.99.99 0 0 1-.96-1.04.98.98 0 0 1 .94-.96m8 0h.1c.56.02.98.48.96 1.04a.98.98 0 0 1-1.04.96.98.98 0 0 1-.96-1.04.98.98 0 0 1 .94-.96' style='-inkscape-stroke:none'/></svg>",
+ "folder-src-open": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c8e6c9' d='M18.473 30a1 1 0 0 1-.238-.028 1.137 1.137 0 0 1-.828-1.323L20.5 12.905a1.13 1.13 0 0 1 .507-.744 1.06 1.06 0 0 1 .8-.134 1.14 1.14 0 0 1 .828 1.324l-3.101 15.744a1.12 1.12 0 0 1-.504.743 1.06 1.06 0 0 1-.557.162m6.2-2h-.077a1.08 1.08 0 0 1-.762-.412 1.164 1.164 0 0 1 .113-1.548l5.319-4.967-5.296-4.623a1.165 1.165 0 0 1-.162-1.544 1.08 1.08 0 0 1 .754-.437 1.06 1.06 0 0 1 .81.258l6.244 5.455a1.156 1.156 0 0 1 .003 1.723l-6.218 5.808a1.07 1.07 0 0 1-.729.289Zm-9.31 0a1.07 1.07 0 0 1-.728-.292l-6.226-5.811a1.16 1.16 0 0 1-.01-1.692l.02-.018 6.246-5.454a1.03 1.03 0 0 1 .8-.26 1.08 1.08 0 0 1 .76.436 1.165 1.165 0 0 1-.16 1.547l-5.294 4.62 5.32 4.964a1.156 1.156 0 0 1 .112 1.548 1.07 1.07 0 0 1-.762.412Z'/></svg>",
+ "folder-src-tauri-open": "<svg viewBox='0 0 32 32'><path fill='#455a64' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124c-.468 0-.921-.164-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffca28' d='M27.163 16.551c0 1.319-1.428 2.143-2.57 1.483a1.6 1.6 0 0 1-.354-.271 1.714 1.714 0 1 1 2.924-1.212'/><path fill='#26c6da' d='M22.568 19.744a1.713 1.713 0 1 0 0 3.426 1.713 1.713 0 0 0 0-3.426'/><path fill='#ffca28' d='M29 22.041a6.6 6.6 0 0 1-2.259.918 4.6 4.6 0 0 0 .226-2.07 4.596 4.596 0 0 0 .521-8.455 4.594 4.594 0 0 0-5.768 1.44 7.6 7.6 0 0 0-2.507.731 6.54 6.54 0 0 1 7.351-4.511A6.542 6.542 0 0 1 29 22.041m-9.71-6.245 1.605.195a4.7 4.7 0 0 1 .202-.911 6.5 6.5 0 0 0-1.807.716' clip-rule='evenodd'/><path fill='#26c6da' d='M19.011 15.967a6.5 6.5 0 0 1 2.273-.926 4.6 4.6 0 0 0-.257 2.079 4.6 4.6 0 0 0-2.859 3.005c-.892 2.969 1.35 5.951 4.449 5.916a4.6 4.6 0 0 0 3.682-1.914 7.7 7.7 0 0 0 2.506-.724 6.54 6.54 0 0 1-10.2 3.266c-3.612-2.742-3.405-8.24.406-10.702m9.708 6.245-.03.016z' clip-rule='evenodd'/></svg>",
+ "folder-src-tauri": "<svg viewBox='0 0 32 32'><path fill='#455a64' d='m13.843 7.536-1.288-1.072A2 2 0 0 0 11.275 6H3.999a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.123a2 2 0 0 1-1.28-.464'/><path fill='#ffca28' d='M27.157 16.551c0 1.319-1.43 2.143-2.573 1.483a1.7 1.7 0 0 1-.355-.271c-.934-.933-.506-2.525.768-2.867a1.716 1.716 0 0 1 2.16 1.655'/><path fill='#26c6da' d='M22.556 19.744c-.947 0-1.715.767-1.715 1.712 0 .946.768 1.713 1.715 1.713.949 0 1.717-.767 1.717-1.713 0-.945-.768-1.712-1.717-1.712'/><path fill='#ffca28' d='M28.996 22.041a6.6 6.6 0 0 1-2.261.918 4.6 4.6 0 0 0 .226-2.07 4.593 4.593 0 0 0 .521-8.455 4.6 4.6 0 0 0-5.775 1.44 7.6 7.6 0 0 0-2.51.731 6.55 6.55 0 0 1 12.798 2.191 6.54 6.54 0 0 1-2.999 5.245m-9.722-6.245 1.607.195a4.6 4.6 0 0 1 .203-.911 6.6 6.6 0 0 0-1.81.716' clip-rule='evenodd'/><path fill='#26c6da' d='M18.995 15.967a6.6 6.6 0 0 1 2.276-.926 4.6 4.6 0 0 0-.257 2.079 4.6 4.6 0 0 0-2.863 3.005c-.893 2.969 1.352 5.951 4.454 5.916a4.6 4.6 0 0 0 3.687-1.914 7.7 7.7 0 0 0 2.509-.724 6.553 6.553 0 0 1-10.212 3.266c-3.617-2.742-3.41-8.24.406-10.702m9.72 6.245-.03.016z' clip-rule='evenodd'/></svg>",
+ "folder-src": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c8e6c9' d='M18.435 30a1 1 0 0 1-.238-.028 1.137 1.137 0 0 1-.828-1.323l3.093-15.744a1.13 1.13 0 0 1 .507-.744 1.06 1.06 0 0 1 .8-.134 1.14 1.14 0 0 1 .828 1.324l-3.1 15.744a1.12 1.12 0 0 1-.505.743 1.06 1.06 0 0 1-.557.162m6.2-2h-.077a1.08 1.08 0 0 1-.762-.412 1.164 1.164 0 0 1 .113-1.548l5.32-4.967-5.297-4.623a1.165 1.165 0 0 1-.162-1.544 1.08 1.08 0 0 1 .754-.437 1.06 1.06 0 0 1 .81.258l6.244 5.455a1.156 1.156 0 0 1 .004 1.723l-6.22 5.808a1.07 1.07 0 0 1-.728.289Zm-9.31 0a1.07 1.07 0 0 1-.728-.292l-6.225-5.811a1.16 1.16 0 0 1-.01-1.692l.02-.018 6.246-5.454a1.03 1.03 0 0 1 .8-.26 1.08 1.08 0 0 1 .758.436 1.165 1.165 0 0 1-.16 1.547l-5.293 4.62 5.32 4.964a1.156 1.156 0 0 1 .112 1.548 1.07 1.07 0 0 1-.762.412Z'/></svg>",
+ "folder-stack-open": "<svg viewBox='0 0 32 32'><path fill='#ffa726' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffe0b2' d='M16 16.006h4v4h-4zm0 6h4v4h-4zm6-2h4v6h-4zm0-4h4v2h-4z'/><path fill='#ffe0b2' d='M32 16.006v-2h-2v-1a1 1 0 0 0-1-1H13a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1v-1h2v-2h-2v-4h2v-2h-2v-4Zm-4 12H14v-14h14Z'/></svg>",
+ "folder-stack": "<svg viewBox='0 0 32 32'><path fill='#ffa726' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffe0b2' d='M16 16.006h4v4h-4zm0 6h4v4h-4zm6-2h4v6h-4zm0-4h4v2h-4z'/><path fill='#ffe0b2' d='M32 16.006v-2h-2v-1a1 1 0 0 0-1-1H13a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1v-1h2v-2h-2v-4h2v-2h-2v-4Zm-4 12H14v-14h14Z'/></svg>",
+ "folder-stencil-open": "<svg viewBox='0 0 32 32'><path fill='#448aff' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='m32 18-4 4H12l4-4zm-4-6-4 4h-6l4-4zm-2 12-4 4h-6l4-4z'/></svg>",
+ "folder-stencil": "<svg viewBox='0 0 32 32'><path fill='#448aff' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='m32 18-4 4H12l4-4zm-4-6-4 4h-6l4-4zm-2 12-4 4h-6l4-4z'/></svg>",
+ "folder-store-open": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffccbc' d='M22 26h-6v-6h6m10 0v-2l-2-4H14l-2 4v2h2v8h10v-8h4v8h2v-8m0-10H14v2h16z'/></svg>",
+ "folder-store": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffccbc' d='M22 26h-6v-6h6m10 0v-2l-2-4H14l-2 4v2h2v8h10v-8h4v8h2v-8m0-10H14v2h16z'/></svg>",
+ "folder-storybook-open": "<svg viewBox='0 0 32 32'><path fill='#ff4081' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='M23.506 15.814c0 .543 3.755.286 4.262-.1 0-3.743-2.044-5.714-5.784-5.714-3.726 0-5.814 2-5.814 5 0 5.214 7.147 5.314 7.147 8.157a1.147 1.147 0 0 1-1.275 1.286c-1.146 0-1.595-.572-1.537-2.529 0-.428-4.35-.557-4.48 0C15.692 26.63 18.678 28 22.1 28c3.29 0 5.9-1.743 5.9-4.886 0-5.6-7.248-5.443-7.248-8.214a1.193 1.193 0 0 1 1.348-1.286c.522 0 1.478.1 1.391 2.2ZM6 10.492l.192-4.673h3.654L10 10.34a.288.288 0 0 1-.462.23L8.135 9.454l-1.673 1.27A.288.288 0 0 1 6 10.491Z'/></svg>",
+ "folder-storybook": "<svg viewBox='0 0 32 32'><path fill='#ff4081' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='M23.506 15.814c0 .543 3.755.286 4.262-.1 0-3.743-2.044-5.714-5.784-5.714-3.726 0-5.814 2-5.814 5 0 5.214 7.147 5.314 7.147 8.157a1.147 1.147 0 0 1-1.275 1.286c-1.146 0-1.595-.572-1.537-2.529 0-.428-4.35-.557-4.48 0C15.692 26.63 18.678 28 22.1 28c3.29 0 5.9-1.743 5.9-4.886 0-5.6-7.248-5.443-7.248-8.214a1.193 1.193 0 0 1 1.348-1.286c.522 0 1.478.1 1.391 2.2ZM6 10.492l.192-4.673h3.654L10 10.34a.288.288 0 0 1-.462.23L8.135 9.454l-1.673 1.27A.288.288 0 0 1 6 10.491Z'/></svg>",
+ "folder-stylus-open": "<svg viewBox='0 0 32 32'><path fill='#c0ca33' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f0f4c3' d='M27.644 24.544c1.961-2.364 2.191-4.804.674-9.335-.962-2.867-2.556-5.074-1.384-6.855 1.248-1.898 3.902-.058 1.69 2.48l.44.307c2.652.311 3.96-3.35 1.98-4.394-5.225-2.75-9.797 2.536-7.782 8.654.867 2.597 2.078 5.347 1.097 7.536a4.85 4.85 0 0 1-3.574 3.02c-2.286.114-.766-5.17 1.864-6.487.23-.115.558-.27.25-.658a5.744 5.744 0 0 0-6.244 3.253c-3.191 6.14 6.05 8.405 10.989 2.479'/></svg>",
+ "folder-stylus": "<svg viewBox='0 0 32 32'><path fill='#c0ca33' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#f0f4c3' d='M27.644 24.544c1.961-2.364 2.191-4.804.674-9.335-.962-2.867-2.556-5.074-1.384-6.855 1.248-1.898 3.902-.058 1.69 2.48l.44.307c2.652.311 3.96-3.35 1.98-4.394-5.225-2.75-9.797 2.536-7.782 8.654.867 2.597 2.078 5.347 1.097 7.536a4.85 4.85 0 0 1-3.574 3.02c-2.286.114-.766-5.17 1.864-6.487.23-.115.558-.27.25-.658a5.744 5.744 0 0 0-6.244 3.253c-3.191 6.14 6.05 8.405 10.989 2.479'/></svg>",
+ "folder-sublime-open": "<svg viewBox='0 0 32 32'><path fill='#616161' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffa726' d='M32 15.833v-2.946a.5.5 0 0 0-.658-.474l-11 3.666a.5.5 0 0 0-.342.475v3.279a.5.5 0 0 0 .342.474L26 22.193l-5.658 1.886a.5.5 0 0 0-.342.475V27.5a.5.5 0 0 0 .658.474l11-3.667a.5.5 0 0 0 .342-.474v-3.28a.5.5 0 0 0-.342-.474L26 18.193l5.658-1.886a.5.5 0 0 0 .342-.474' data-mit-no-recolor='true'/></svg>",
+ "folder-sublime": "<svg viewBox='0 0 32 32'><path fill='#616161' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffa726' d='M32 15.833v-2.946a.5.5 0 0 0-.658-.474l-11 3.666a.5.5 0 0 0-.342.475v3.279a.5.5 0 0 0 .342.474L26 22.193l-5.658 1.886a.5.5 0 0 0-.342.475V27.5a.5.5 0 0 0 .658.474l11-3.667a.5.5 0 0 0 .342-.474v-3.28a.5.5 0 0 0-.342-.474L26 18.193l5.658-1.886a.5.5 0 0 0 .342-.474' data-mit-no-recolor='true'/></svg>",
+ "folder-supabase-open": "<svg viewBox='0 0 32 32'><path fill='#66bb6a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c8e6c9' d='M30 18h-6v-6.499a.5.5 0 0 0-.9-.3l-6.3 8.4a1.5 1.5 0 0 0 1.2 2.4h6V28.5a.5.5 0 0 0 .9.3l6.3-8.4A1.5 1.5 0 0 0 30 18'/></svg>",
+ "folder-supabase": "<svg viewBox='0 0 32 32'><path fill='#66bb6a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c8e6c9' d='M30 18h-6v-6.499a.5.5 0 0 0-.9-.3l-6.3 8.4a1.5 1.5 0 0 0 1.2 2.4h6V28.5a.5.5 0 0 0 .9.3l6.3-8.4A1.5 1.5 0 0 0 30 18'/></svg>",
+ "folder-svelte-open": "<svg viewBox='0 0 32 32'><path fill='#ff5722' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffccbc' d='M25.983 8a5.36 5.36 0 0 0-2.865.89l-4.477 2.971a5.36 5.36 0 0 0-2.321 3.575 5.84 5.84 0 0 0 .532 3.618 5.5 5.5 0 0 0-.767 1.998 5.88 5.88 0 0 0 .934 4.311 5.73 5.73 0 0 0 7.862 1.758l4.479-2.96a5.37 5.37 0 0 0 2.32-3.573 5.84 5.84 0 0 0-.534-3.616 5.5 5.5 0 0 0 .77-2 5.9 5.9 0 0 0-.936-4.31v-.014a5.91 5.91 0 0 0-4.997-2.647Zm.393 2.283a3.606 3.606 0 0 1 3.323 4.183 4 4 0 0 1-.106.433l-.086.269-.228-.18a5.7 5.7 0 0 0-1.752-.911l-.172-.05.016-.18a1.08 1.08 0 0 0-.182-.693 1.05 1.05 0 0 0-1.143-.432 1 1 0 0 0-.275.126l-4.48 2.965a.98.98 0 0 0-.422.65 1.08 1.08 0 0 0 .172.78 1.05 1.05 0 0 0 1.142.445 1 1 0 0 0 .275-.125l1.717-1.133a3.1 3.1 0 0 1 .91-.417 3.48 3.48 0 0 1 3.817 1.473 3.54 3.54 0 0 1 .563 2.592 3.22 3.22 0 0 1-1.395 2.156l-4.48 2.97a3.2 3.2 0 0 1-.91.416 3.49 3.49 0 0 1-3.819-1.475 3.53 3.53 0 0 1-.561-2.59 3 3 0 0 1 .106-.432l.085-.268.23.179a5.7 5.7 0 0 0 1.746.905l.172.05-.015.18a1.1 1.1 0 0 0 .187.688 1.05 1.05 0 0 0 1.15.443 1 1 0 0 0 .274-.125l4.472-2.97a.97.97 0 0 0 .42-.651 1.06 1.06 0 0 0-.172-.782 1.05 1.05 0 0 0-1.15-.442 1 1 0 0 0-.275.125l-1.717 1.135a3.2 3.2 0 0 1-.907.415 3.49 3.49 0 0 1-3.813-1.473 3.54 3.54 0 0 1-.557-2.592 3.22 3.22 0 0 1 1.395-2.156l4.485-2.97a3.2 3.2 0 0 1 .903-.415 3.4 3.4 0 0 1 1.057-.114Z'/></svg>",
+ "folder-svelte": "<svg viewBox='0 0 32 32'><path fill='#ff5722' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffccbc' d='M25.983 8a5.36 5.36 0 0 0-2.865.89l-4.477 2.971a5.36 5.36 0 0 0-2.321 3.575 5.84 5.84 0 0 0 .532 3.618 5.5 5.5 0 0 0-.767 1.998 5.88 5.88 0 0 0 .934 4.311 5.73 5.73 0 0 0 7.862 1.758l4.479-2.96a5.37 5.37 0 0 0 2.32-3.573 5.84 5.84 0 0 0-.534-3.616 5.5 5.5 0 0 0 .77-2 5.9 5.9 0 0 0-.936-4.31v-.014a5.91 5.91 0 0 0-4.997-2.647Zm.393 2.283a3.606 3.606 0 0 1 3.323 4.183 4 4 0 0 1-.106.433l-.086.269-.228-.18a5.7 5.7 0 0 0-1.752-.911l-.172-.05.016-.18a1.08 1.08 0 0 0-.182-.693 1.05 1.05 0 0 0-1.143-.432 1 1 0 0 0-.275.126l-4.48 2.965a.98.98 0 0 0-.422.65 1.08 1.08 0 0 0 .172.78 1.05 1.05 0 0 0 1.142.445 1 1 0 0 0 .275-.125l1.717-1.133a3.1 3.1 0 0 1 .91-.417 3.48 3.48 0 0 1 3.817 1.473 3.54 3.54 0 0 1 .563 2.592 3.22 3.22 0 0 1-1.395 2.156l-4.48 2.97a3.2 3.2 0 0 1-.91.416 3.49 3.49 0 0 1-3.819-1.475 3.53 3.53 0 0 1-.561-2.59 3 3 0 0 1 .106-.432l.085-.268.23.179a5.7 5.7 0 0 0 1.746.905l.172.05-.015.18a1.1 1.1 0 0 0 .187.688 1.05 1.05 0 0 0 1.15.443 1 1 0 0 0 .274-.125l4.472-2.97a.97.97 0 0 0 .42-.651 1.06 1.06 0 0 0-.172-.782 1.05 1.05 0 0 0-1.15-.442 1 1 0 0 0-.275.125l-1.717 1.135a3.2 3.2 0 0 1-.907.415 3.49 3.49 0 0 1-3.813-1.473 3.54 3.54 0 0 1-.557-2.592 3.22 3.22 0 0 1 1.395-2.156l4.485-2.97a3.2 3.2 0 0 1 .903-.415 3.4 3.4 0 0 1 1.057-.114Z'/></svg>",
+ "folder-svg-open": "<svg viewBox='0 0 32 32'><path fill='#ffb300' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fff9c4' d='M29 17a1.99 1.99 0 0 0-1.723 1h-3.863l3.074-3.074a2.035 2.035 0 1 0-1.414-1.414L22 16.586v-3.863a2 2 0 1 0-2 0v3.863l-3.074-3.075a2.034 2.034 0 1 0-1.414 1.415L18.586 18h-3.863a2 2 0 1 0 0 2h3.863l-3.075 3.074a2.035 2.035 0 1 0 1.415 1.415L20 21.414v3.863a2 2 0 1 0 2 0v-3.863l3.074 3.074a2.035 2.035 0 1 0 1.415-1.414L23.414 20h3.863A1.997 1.997 0 1 0 29 17'/></svg>",
+ "folder-svg": "<svg viewBox='0 0 32 32'><path fill='#ffb300' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fff9c4' d='M29 17a1.99 1.99 0 0 0-1.723 1h-3.863l3.074-3.074a2.035 2.035 0 1 0-1.414-1.414L22 16.586v-3.863a2 2 0 1 0-2 0v3.863l-3.074-3.075a2.034 2.034 0 1 0-1.414 1.415L18.586 18h-3.863a2 2 0 1 0 0 2h3.863l-3.075 3.074a2.035 2.035 0 1 0 1.415 1.415L20 21.414v3.863a2 2 0 1 0 2 0v-3.863l3.074 3.074a2.035 2.035 0 1 0 1.415-1.414L23.414 20h3.863A1.997 1.997 0 1 0 29 17'/></svg>",
+ "folder-syntax-open": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffcdd2' d='m31.87 19.917-1.307-1.378a.477.477 0 0 0-.691-.001l-7.156 7.538-3.057-3.23a.48.48 0 0 0-.345-.148h-.001a.48.48 0 0 0-.345.148l-1.31 1.377a.477.477 0 0 0 0 .657l4.721 4.972a.477.477 0 0 0 .691 0l8.8-9.28a.476.476 0 0 0 0-.655'/><path fill='#ffcdd2' d='M21.292 23.336a.48.48 0 0 0 .448.314h1.94a.476.476 0 0 0 .446-.642l-4.74-12.698a.48.48 0 0 0-.446-.31h-1.724a.48.48 0 0 0-.447.31l-4.74 12.698a.476.476 0 0 0 .447.642h1.94a.48.48 0 0 0 .448-.317l.926-2.612h4.558Zm-1.97-5.523h-2.49l1.245-3.495Z'/></svg>",
+ "folder-syntax": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffcdd2' d='m31.87 19.917-1.307-1.378a.477.477 0 0 0-.691-.001l-7.156 7.538-3.057-3.23a.48.48 0 0 0-.345-.148h-.001a.48.48 0 0 0-.345.148l-1.31 1.377a.477.477 0 0 0 0 .657l4.721 4.972a.477.477 0 0 0 .691 0l8.8-9.28a.476.476 0 0 0 0-.655'/><path fill='#ffcdd2' d='M21.292 23.336a.48.48 0 0 0 .448.314h1.94a.476.476 0 0 0 .446-.642l-4.74-12.698a.48.48 0 0 0-.446-.31h-1.724a.48.48 0 0 0-.447.31l-4.74 12.698a.476.476 0 0 0 .447.642h1.94a.48.48 0 0 0 .448-.317l.926-2.612h4.558Zm-1.97-5.523h-2.49l1.245-3.495Z'/></svg>",
+ "folder-target-open": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#cfd8dc' d='m31.304 16.347-1.584 1.584a8 8 0 1 1-5.657-5.657l1.584-1.583a10 10 0 1 0 5.657 5.656'/><path fill='#cfd8dc' d='m22.437 16.022.22-1.987a6 6 0 1 0 5.302 5.304l-1.987.219a4 4 0 1 1-3.535-3.536'/><path fill='#cfd8dc' d='m24.827 14.34-.707 2.12-1.61 1.611a2 2 0 0 0-.99-.01q-.037.01-.072.022a2 2 0 0 0-.367.146q-.054.027-.107.057a2 2 0 1 0 2.735 2.735q.03-.053.056-.108a2 2 0 0 0 .147-.366l.021-.072a2 2 0 0 0-.01-.99l1.611-1.61 2.121-.707 4.243-4.243H29.07v-2.828Z'/></svg>",
+ "folder-target": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#cfd8dc' d='m31.304 16.347-1.584 1.584a8 8 0 1 1-5.657-5.657l1.584-1.583a10 10 0 1 0 5.657 5.656'/><path fill='#cfd8dc' d='m22.437 16.022.22-1.987a6 6 0 1 0 5.302 5.304l-1.987.219a4 4 0 1 1-3.535-3.536'/><path fill='#cfd8dc' d='m24.827 14.34-.707 2.12-1.61 1.611a2 2 0 0 0-.99-.01q-.037.01-.072.022a2 2 0 0 0-.367.146q-.054.027-.107.057a2 2 0 1 0 2.735 2.735q.03-.053.056-.108a2 2 0 0 0 .147-.366l.021-.072a2 2 0 0 0-.01-.99l1.611-1.61 2.121-.707 4.243-4.243H29.07v-2.828Z'/></svg>",
+ "folder-taskfile-open": "<svg viewBox='0 0 32 32'><path fill='#4db6ac' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2dfdb' d='m25 12-7 4.072v7.854L25 28l7-4.074v-7.854Z'/></svg>",
+ "folder-taskfile": "<svg viewBox='0 0 32 32'><path fill='#4db6ac' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2dfdb' d='m25 12-7 4.072v7.854L25 28l7-4.074v-7.854Z'/></svg>",
+ "folder-tasks-open": "<svg viewBox='0 0 32 32'><path fill='#5c6bc0' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c5cae9' d='M23.08 24 20 21.108l1.378-1.465 1.718 1.612L26.638 18 28 19.48z'/><path fill='#c5cae9' d='M30 12v-2h-2v2h-8v-2h-2v2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V14a2 2 0 0 0-2-2m0 14H18V16h12Z'/></svg>",
+ "folder-tasks": "<svg viewBox='0 0 32 32'><path fill='#5c6bc0' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c5cae9' d='M23.08 24 20 21.108l1.378-1.465 1.718 1.612L26.638 18 28 19.48z'/><path fill='#c5cae9' d='M30 12v-2h-2v2h-8v-2h-2v2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V14a2 2 0 0 0-2-2m0 14H18V16h12Z'/></svg>",
+ "folder-television-open": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fff9c4' d='M30 12H14a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h4v2h8v-2h4a2 2 0 0 0 2-2V14a2 2 0 0 0-2-2m0 12H14V14h16Z'/></svg>",
+ "folder-television": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fff9c4' d='M30 12H14a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h4v2h8v-2h4a2 2 0 0 0 2-2V14a2 2 0 0 0-2-2m0 12H14V14h16Z'/></svg>",
+ "folder-temp-open": "<svg viewBox='0 0 32 32'><path fill='#0097a7' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2ebf2' d='M25.375 24.781 20 20.48V14h2v5.52l4.625 3.699z'/><path fill='#b2ebf2' d='M22 30a10 10 0 1 1 10-10 10.01 10.01 0 0 1-10 10m0-18a8 8 0 1 0 8 8 8.01 8.01 0 0 0-8-8'/></svg>",
+ "folder-temp": "<svg viewBox='0 0 32 32'><path fill='#0097a7' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2ebf2' d='M25.375 24.781 20 20.48V14h2v5.52l4.625 3.699z'/><path fill='#b2ebf2' d='M22 30a10 10 0 1 1 10-10 10.01 10.01 0 0 1-10 10m0-18a8 8 0 1 0 8 8 8.01 8.01 0 0 0-8-8'/></svg>",
+ "folder-template-open": "<svg viewBox='0 0 32 32'><path fill='#8d6e63' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#d7ccc8' d='M12 26h8v2h-8zm0-4h8v2h-8zm0-4h8v2h-8zm0-4h8v2h-8zm10 0h8v14h-8z'/></svg>",
+ "folder-template": "<svg viewBox='0 0 32 32'><path fill='#8d6e63' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#d7ccc8' d='M12 26h8v2h-8zm0-4h8v2h-8zm0-4h8v2h-8zm0-4h8v2h-8zm10 0h8v14h-8z'/></svg>",
+ "folder-terraform-open": "<svg viewBox='0 0 32 32'><path fill='#5c6bc0' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c5cae9' d='M27.03 22.993 32 19.84v-6.316l-4.97 3.16m-5.516-3.16 4.97 3.16v6.31l-4.969-3.157M16 16.313l4.97 3.156v-6.313L16 10m5.516 16.844L26.486 30v-6.313l-4.97-3.156'/></svg>",
+ "folder-terraform": "<svg viewBox='0 0 32 32'><path fill='#5c6bc0' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c5cae9' d='M27.03 22.993 32 19.84v-6.316l-4.97 3.16m-5.516-3.16 4.97 3.16v6.31l-4.969-3.157M16 16.313l4.97 3.156v-6.313L16 10m5.516 16.844L26.486 30v-6.313l-4.97-3.156'/></svg>",
+ "folder-test-open": "<svg viewBox='0 0 32 32'><path fill='#00bfa5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#a7ffeb' d='M16 12v2h2v12a4 4 0 0 0 8 0V14h2v-2Zm5 14a1 1 0 1 1 1-1 1 1 0 0 1-1 1m2-4a1 1 0 1 1 1-1 1 1 0 0 1-1 1m1-4h-4v-4h4Z'/></svg>",
+ "folder-test": "<svg viewBox='0 0 32 32'><path fill='#00bfa5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#a7ffeb' d='M16 12v2h2v12a4 4 0 0 0 8 0V14h2v-2Zm5 14a1 1 0 1 1 1-1 1 1 0 0 1-1 1m2-4a1 1 0 1 1 1-1 1 1 0 0 1-1 1m1-4h-4v-4h4Z'/></svg>",
+ "folder-theme-open": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M21.998 10C16 10 12 14 12 20a10 10 0 0 0 10 10c.92 0 2 0 2-2 0-.436-.569-.785-.964-1.18A2.37 2.37 0 0 1 22 25c0-1 1-1 2-1h4c4 0 4-4 4-6 0-4-4-8-10.002-8M16 20a2 2 0 1 1 2-2 2 2 0 0 1-2 2m6-4a2 2 0 1 1 2-2 2 2 0 0 1-2 2m6 4a2 2 0 1 1 2-2 2 2 0 0 1-2 2'/></svg>",
+ "folder-theme": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M21.998 10C16 10 12 14 12 20a10 10 0 0 0 10 10c.92 0 2 0 2-2 0-.436-.569-.785-.964-1.18A2.37 2.37 0 0 1 22 25c0-1 1-1 2-1h4c4 0 4-4 4-6 0-4-4-8-10.002-8M16 20a2 2 0 1 1 2-2 2 2 0 0 1-2 2m6-4a2 2 0 1 1 2-2 2 2 0 0 1-2 2m6 4a2 2 0 1 1 2-2 2 2 0 0 1-2 2'/></svg>",
+ "folder-tools-open": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M24.363 19.012 13.364 30 12 28.638l10.988-11zm4.365-2.815.574-.576-.77-.77.624-.623-1.384-1.383-.623.624-.77-.77-.574.575A20.5 20.5 0 0 0 20.155 10l-.81 1.744a24.5 24.5 0 0 1 4.736 3.253l-.488.488 2.923 2.923.488-.488a24.5 24.5 0 0 1 3.252 4.736L32 21.848a20.5 20.5 0 0 0-3.272-5.651'/></svg>",
+ "folder-tools": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='M24.363 19.012 13.364 30 12 28.638l10.988-11zm4.365-2.815.574-.576-.77-.77.624-.623-1.384-1.383-.623.624-.77-.77-.574.575A20.5 20.5 0 0 0 20.155 10l-.81 1.744a24.5 24.5 0 0 1 4.736 3.253l-.488.488 2.923 2.923.488-.488a24.5 24.5 0 0 1 3.252 4.736L32 21.848a20.5 20.5 0 0 0-3.272-5.651'/></svg>",
+ "folder-trash-open": "<svg fill-rule='evenodd' clip-rule='evenodd' image-rendering='optimizeQuality' shape-rendering='geometricPrecision' text-rendering='geometricPrecision' viewBox='0 0 512 512'><path fill='#f44336' d='M463.47 192H151.06c-13.77 0-26 8.82-30.35 21.89L64 384V160h384c0-17.67-14.33-32-32-32H241.98a32 32 0 0 1-20.48-7.42l-20.6-17.15c-5.75-4.8-13-7.43-20.48-7.43H64c-17.67 0-32 14.33-32 32v256c0 17.67 14.33 32 32 32h352l76.88-179.39c1.7-3.98 2.59-8.28 2.59-12.61 0-17.67-14.33-32-32-32'/><path fill='#ffcdd2' d='M320 160v32h-96v32h32v192c0 17.63 14.38 32 32 32h160c17.63 0 32-14.37 32-32V224h32v-32h-96v-32zm0 96v128h32V256zm64 0v128h32V256z'/></svg>",
+ "folder-trash": "<svg fill-rule='evenodd' clip-rule='evenodd' image-rendering='optimizeQuality' shape-rendering='geometricPrecision' text-rendering='geometricPrecision' viewBox='0 0 512 512'><path fill='#f44336' d='m221.5 120.58-20.6-17.16A32 32 0 0 0 180.42 96H64c-17.67 0-32 14.33-32 32v256c0 17.67 14.33 32 32 32h384c17.67 0 32-14.33 32-32V160c0-17.67-14.33-32-32-32H241.98a32 32 0 0 1-20.48-7.42'/><path fill='#ffcdd2' d='M320 160v32h-96v32h32v192c0 17.63 14.38 32 32 32h160c17.63 0 32-14.37 32-32V224h32v-32h-96v-32zm0 96v128h32V256zm64 0v128h32V256z'/></svg>",
+ "folder-trigger-open": "<svg viewBox='0 0 1024 1024'><path fill='#ffc107' d='M926.944 384h-624.8a64 64 0 0 0-60.736 43.776L128 768V320h768a64 64 0 0 0-64-64H483.968a64 64 0 0 1-40.96-14.848l-41.216-34.304A64 64 0 0 0 360.832 192H128a64 64 0 0 0-64 64v512a64 64 0 0 0 64 64h704l153.76-358.784A64 64 0 0 0 926.944 384'/><path fill='#ffecb3' d='m640 896 64-256h-64a28.252 32.205 0 0 1-20.876-53.906L832 320l-64 256h64a28.252 32.205 0 0 1 20.876 53.906z'/></svg>",
+ "folder-trigger": "<svg viewBox='0 0 1024 1024'><path fill='#ffc107' d='m443.008 241.152-41.216-34.304A64 64 0 0 0 360.832 192H128a64 64 0 0 0-64 64v512a64 64 0 0 0 64 64h768a64 64 0 0 0 64-64V320a64 64 0 0 0-64-64H483.968a64 64 0 0 1-40.96-14.848'/><path fill='#ffecb3' d='m640 896 64-256h-64a28.252 32.205 0 0 1-20.876-53.906L832 320l-64 256h64a28.252 32.205 0 0 1 20.876 53.906z'/></svg>",
+ "folder-turborepo-open": "<svg viewBox='0 0 32 32'><defs><linearGradient id='a' x1='30.58' x2='17.816' y1='13.808' y2='26.573' gradientUnits='userSpaceOnUse'><stop offset='.15' stop-color='#2196f3'/><stop offset='.85' stop-color='#f50057'/></linearGradient></defs><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#cfd8dc' d='M23 16a3 3 0 1 1-3 3 3 3 0 0 1 3-3m0-2a5 5 0 1 0 5 5 5 5 0 0 0-5-5'/><path fill='url(#a)' d='M16 20a7.1 7.1 0 0 0 1.5 3.41l-1.421 1.426A9.05 9.05 0 0 1 14 20Zm15.944-2.003A9.015 9.015 0 0 0 24 10v2a7.085 7.085 0 0 1 0 14v2a9.03 9.03 0 0 0 7.944-10.003m-14.414 8.23A9.07 9.07 0 0 0 22 28v-2a7.1 7.1 0 0 1-3.03-1.218Z'/></svg>",
+ "folder-turborepo": "<svg viewBox='0 0 32 32'><defs><linearGradient id='a' x1='30.58' x2='17.816' y1='13.808' y2='26.573' gradientUnits='userSpaceOnUse'><stop offset='.15' stop-color='#2196f3'/><stop offset='.85' stop-color='#f50057'/></linearGradient></defs><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#cfd8dc' d='M23 16a3 3 0 1 1-3 3 3 3 0 0 1 3-3m0-2a5 5 0 1 0 5 5 5 5 0 0 0-5-5'/><path fill='url(#a)' d='M16 20a7.1 7.1 0 0 0 1.5 3.41l-1.421 1.426A9.05 9.05 0 0 1 14 20Zm15.944-2.003A9.015 9.015 0 0 0 24 10v2a7.085 7.085 0 0 1 0 14v2a9.03 9.03 0 0 0 7.944-10.003m-14.414 8.23A9.07 9.07 0 0 0 22 28v-2a7.1 7.1 0 0 1-3.03-1.218Z'/></svg>",
+ "folder-typescript-open": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#90caf9' d='M24 19.06a1.33 1.33 0 0 0 .3 1.04 2.5 2.5 0 0 0 .61.28c.54.18 1.33.37 2.09.62 2.64.88 2.96 2.32 2.99 3.49.01.16.01.31.01.46V25c0 1.06-.46 2.79-3.44 2.98-.13.01-.25.01-.37.01A1 1 0 0 1 26 28h-4v-1.76l.24-.24H26a2 2 0 0 0 .25-.01h.17c.18-.01.33-.03.47-.04a2 2 0 0 0 .27-.06c.07-.02.13-.04.19-.06a.04.04 0 0 0 .03-.01c.49-.18.59-.45.61-.66A1 1 0 0 0 28 25c0-.32-.68-1.23-3-2-2.74-.91-2.98-2.42-2.99-3.61a.6.6 0 0 1-.01-.13V19a2.85 2.85 0 0 1 .45-1.59c.04-.06.07-.11.11-.16.01-.01.01-.02.02-.03a1 1 0 0 1 .18-.2A4.3 4.3 0 0 1 25.91 16H30v2h-4c-.13 0-.26 0-.39.01-1.18.06-1.49.4-1.58.7a.13.13 0 0 0-.01.06A1 1 0 0 0 24 19ZM18 28h-2V18h-4v-2h10v2h-4Z'/></svg>",
+ "folder-typescript": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#90caf9' d='M24 19.06a1.33 1.33 0 0 0 .3 1.04 2.5 2.5 0 0 0 .61.28c.54.18 1.33.37 2.09.62 2.64.88 2.96 2.32 2.99 3.49.01.16.01.31.01.46V25c0 1.06-.46 2.79-3.44 2.98-.13.01-.25.01-.37.01A1 1 0 0 1 26 28h-4v-1.76l.24-.24H26a2 2 0 0 0 .25-.01h.17c.18-.01.33-.03.47-.04a2 2 0 0 0 .27-.06c.07-.02.13-.04.19-.06a.04.04 0 0 0 .03-.01c.49-.18.59-.45.61-.66A1 1 0 0 0 28 25c0-.32-.68-1.23-3-2-2.74-.91-2.98-2.42-2.99-3.61a.6.6 0 0 1-.01-.13V19a2.85 2.85 0 0 1 .45-1.59c.04-.06.07-.11.11-.16.01-.01.01-.02.02-.03a1 1 0 0 1 .18-.2A4.3 4.3 0 0 1 25.91 16H30v2h-4c-.13 0-.26 0-.39.01-1.18.06-1.49.4-1.58.7a.13.13 0 0 0-.01.06A1 1 0 0 0 24 19ZM18 28h-2V18h-4v-2h10v2h-4Z'/></svg>",
+ "folder-ui-open": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#e1bee7' d='M16 14h16v6H16zm0 8h6v6h-6zm8 0h8v6h-8z'/></svg>",
+ "folder-ui": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#e1bee7' d='M16 14h16v6H16zm0 8h6v6h-6zm8 0h8v6h-8z'/></svg>",
+ "folder-unity-open": "<svg viewBox='0 0 32 32'><path fill='#2196f3' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='M23 15.25 18.5 13l3-1.5v-3L14 13v7.75l3-1.5v-4.5L21.5 17v6.75L17 22l-3 1.5 9 4.5 9-4.5-3-1.5-4.5 2.25V17.5l4.5-2.25v4l3 1.5V13l-7.5-4.5v3l3 1.5z'/></svg>",
+ "folder-unity": "<svg viewBox='0 0 32 32'><path fill='#2196f3' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124c-.468 0-.921-.164-1.28-.464'/><path fill='#bbdefb' d='M23 15.25 18.5 13l3-1.5v-3L14 13v7.75l3-1.5v-4.5L21.5 17v6.75L17 22l-3 1.5 9 4.5 9-4.5-3-1.5-4.5 2.25V17.5l4.5-2.25v4l3 1.5V13l-7.5-4.5v3l3 1.5z'/></svg>",
+ "folder-update-open": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#c8e6c9' d='M20 14v6.48l5.38 4.3 1.24-1.56-4.62-3.7V14z'/><path fill='#c8e6c9' d='m32 10.162-2.898 2.821A9.984 9.984 0 1 0 31.8 22h-2.05a8.034 8.034 0 1 1-2.082-7.62L24.975 17H32Z'/></svg>",
+ "folder-update": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#c8e6c9' d='M20 14v6.48l5.38 4.3 1.24-1.56-4.62-3.7V14z'/><path fill='#c8e6c9' d='m32 10.162-2.898 2.821A9.984 9.984 0 1 0 31.8 22h-2.05a8.034 8.034 0 1 1-2.082-7.62L24.975 17H32Z'/></svg>",
+ "folder-upload-open": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffccbc' d='M20 24v-6h-4l7-8 7 8h-4v6zm-4 4v-2h14v2Z'/></svg>",
+ "folder-upload": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffccbc' d='M20 24v-6h-4l7-8 7 8h-4v6zm-4 4v-2h14v2Z'/></svg>",
+ "folder-utils-open": "<svg viewBox='0 0 32 32'><path fill='#7cb342' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#dcedc8' d='M31 12H19a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V13a1 1 0 0 0-1-1m-1 8h-4v4h-2v-4h-4v-2h4v-4h2v4h4Z'/><path fill='#dcedc8' d='M16 28V16h-2v13a1 1 0 0 0 1 1h13v-2Z'/></svg>",
+ "folder-utils": "<svg viewBox='0 0 32 32'><path fill='#7cb342' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#dcedc8' d='M31 12H19a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V13a1 1 0 0 0-1-1m-1 8h-4v4h-2v-4h-4v-2h4v-4h2v4h4Z'/><path fill='#dcedc8' d='M16 28V16h-2v13a1 1 0 0 0 1 1h13v-2Z'/></svg>",
+ "folder-vercel-open": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#cfd8dc' d='m22 12 10 16H12Z'/></svg>",
+ "folder-vercel": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#cfd8dc' d='m22 12 10 16H12Z'/></svg>",
+ "folder-verdaccio-open": "<svg viewBox='0 0 32 32'><path fill='#00897b' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b2dfdb' d='m14 12.001 7.765 16h2.536l4.852-10h-4.337l-1.78 4L18.339 12Z'/><path fill='#ffcdd2' d='M25.764 12.001v.927h1.732l-.285.584h-2.833v.922h2.377l-.338.696h-3.394v.871h7.04L32 12.016h-.765V12Z'/></svg>",
+ "folder-verdaccio": "<svg viewBox='0 0 32 32'><path fill='#00897b' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b2dfdb' d='m14 12.001 7.765 16h2.536l4.852-10h-4.337l-1.78 4L18.339 12Z'/><path fill='#ffcdd2' d='M25.764 12.001v.927h1.732l-.285.584h-2.833v.922h2.377l-.338.696h-3.394v.871h7.04L32 12.016h-.765V12Z'/></svg>",
+ "folder-video-open": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffe0b2' d='m28 14 2 4h-2l-2-4h-2l2 4h-2l-2-4h-2l2 4h-2l-2-4h-2a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V14.5a.5.5 0 0 0-.5-.5Z'/></svg>",
+ "folder-video": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffe0b2' d='m28 14 2 4h-2l-2-4h-2l2 4h-2l-2-4h-2l2 4h-2l-2-4h-2a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V14.5a.5.5 0 0 0-.5-.5Z'/></svg>",
+ "folder-views-open": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#ffccbc' d='m14 12 2 16 7 2 7-2 2-16Zm8 5.899L18.66 20 22 22.102V24l-5.235-3.386v-1.227L22 16Zm7.235 2.728L24 24v-1.898L27.363 20 24 17.899V16l5.235 3.373Z'/></svg>",
+ "folder-views": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#ffccbc' d='m14 12 2 16 7 2 7-2 2-16Zm8 5.899L18.66 20 22 22.102V24l-5.235-3.386v-1.227L22 16Zm7.235 2.728L24 24v-1.898L27.363 20 24 17.899V16l5.235 3.373Z'/></svg>",
+ "folder-vm-open": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M30 26v-9.5a.5.5 0 0 0-.5-.5h-13a.5.5 0 0 0-.5.5V26h-1.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h17a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5Zm-12-8h10v6H18Z'/></svg>",
+ "folder-vm": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M30 26v-9.5a.5.5 0 0 0-.5-.5h-13a.5.5 0 0 0-.5.5V26h-1.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h17a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5Zm-12-8h10v6H18Z'/></svg>",
+ "folder-vscode-open": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#bbdefb' d='m28.145 10-7.903 7.267-4.417-3.329L14 15.001l4.353 3.998L14 23.001l1.825 1.065 4.417-3.329L28.145 28 32 26.127V11.874ZM28 14.78v8.441l-5.603-4.22Z'/></svg>",
+ "folder-vscode": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#bbdefb' d='m28.145 10-7.903 7.267-4.417-3.329L14 15.001l4.353 3.998L14 23.001l1.825 1.065 4.417-3.329L28.145 28 32 26.127V11.874ZM28 14.78v8.441l-5.603-4.22Z'/></svg>",
+ "folder-vue-directives-open": "<svg viewBox='0 0 32 32'><path fill='#00838f' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#41b883' d='m12 12 10 16 10-15.923V12h-3.889l-6.053 9.641-6.026-9.64Z'/><path fill='#35495e' d='m16.03 12 6.027 9.642L28.11 12h-3.647l-2.383 3.795L19.708 12Z'/></svg>",
+ "folder-vue-directives": "<svg viewBox='0 0 32 32'><path fill='#00838f' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#41b883' d='m12 12 10 16 10-15.923V12h-3.889l-6.053 9.641-6.026-9.64Z'/><path fill='#35495e' d='m16.03 12 6.027 9.642L28.11 12h-3.647l-2.383 3.795L19.708 12Z'/></svg>",
+ "folder-vue-open": "<svg viewBox='0 0 32 32'><path fill='#009688' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#41b883' d='m12 12 10 16 10-15.923V12h-3.889l-6.053 9.641-6.026-9.64Z'/><path fill='#35495e' d='m16.03 12 6.027 9.642L28.11 12h-3.647l-2.383 3.795L19.708 12Z'/></svg>",
+ "folder-vue": "<svg viewBox='0 0 32 32'><path fill='#009688' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#41b883' d='m12 12 10 16 10-15.923V12h-3.889l-6.053 9.641-6.026-9.64Z'/><path fill='#35495e' d='m16.03 12 6.027 9.642L28.11 12h-3.647l-2.383 3.795L19.708 12Z'/></svg>",
+ "folder-vuepress-open": "<svg viewBox='0 0 32 32'><path fill='#41b883' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#e8f5e9' d='M18.4 12a2.23 2.23 0 0 0-2.4 2v12a2.23 2.23 0 0 0 2.4 2h11.2a2.23 2.23 0 0 0 2.4-2V14a2.23 2.23 0 0 0-2.4-2Z'/><path fill='#41b883' d='m18 16 6 10 6-9.952V16h-2.333l-3.632 6.026L20.42 16Z'/><path fill='#35495e' d='m20.418 16 3.616 6.026L27.667 16h-2.188l-1.43 2.372L22.625 16Z'/></svg>",
+ "folder-vuepress": "<svg viewBox='0 0 32 32'><path fill='#41b883' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#e8f5e9' d='M18.4 12a2.23 2.23 0 0 0-2.4 2v12a2.23 2.23 0 0 0 2.4 2h11.2a2.23 2.23 0 0 0 2.4-2V14a2.23 2.23 0 0 0-2.4-2Z'/><path fill='#41b883' d='m18 16 6 10 6-9.952V16h-2.333l-3.632 6.026L20.42 16Z'/><path fill='#35495e' d='m20.418 16 3.616 6.026L27.667 16h-2.188l-1.43 2.372L22.625 16Z'/></svg>",
+ "folder-vuex-store-open": "<svg viewBox='0 0 32 32'><path fill='#009688' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><g data-mit-no-recolor='true'><path fill='#41b883' d='m14 29.989 7.2-14.38 1.8 3.508v3.688l-3.577 7.184ZM32 30l-7.2-14.38-1.8 3.508v3.688L26.566 30Z'/><path fill='#35495e' d='m14 12 4.5 9 2.7-5.391L19.4 12Zm18 0-4.5 9-2.7-5.391L26.6 12Z'/></g></svg>",
+ "folder-vuex-store": "<svg viewBox='0 0 32 32'><path fill='#009688' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><g data-mit-no-recolor='true'><path fill='#41b883' d='m14 29.989 7.2-14.38 1.8 3.508v3.688l-3.577 7.184ZM32 30l-7.2-14.38-1.8 3.508v3.688L26.566 30Z'/><path fill='#35495e' d='m14 12 4.5 9 2.7-5.391L19.4 12Zm18 0-4.5 9-2.7-5.391L26.6 12Z'/></g></svg>",
+ "folder-wakatime-open": "<svg viewBox='0 0 32 32'><path fill='#455a64' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#f5f5f5' d='M31.578 14.516A1.62 1.62 0 0 0 30.442 14a1.5 1.5 0 0 0-1.273.728l-6.255 8.823-.82-1.324a1.54 1.54 0 0 0-1.31-.756 1.52 1.52 0 0 0-1.331.827l-.783 1.253-3.795-5.52a1.54 1.54 0 0 0-1.352-.8 1.6 1.6 0 0 0-1.521 1.644 1.67 1.67 0 0 0 .366 1.066l5.026 7.205a1.506 1.506 0 0 0 2.686.058l.717-1.136.698 1.103a2 2 0 0 0 .178.266 2 2 0 0 0 .13.141l.106.092a2 2 0 0 0 .227.15l.1.05a1.4 1.4 0 0 0 .455.122l.123.008h.029a1.53 1.53 0 0 0 1.204-.617l7.61-10.702a1.7 1.7 0 0 0 .341-1.007 1.6 1.6 0 0 0-.42-1.158m-9.212 12.82'/></svg>",
+ "folder-wakatime": "<svg viewBox='0 0 32 32'><path fill='#455a64' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#f5f5f5' d='M31.578 14.516A1.62 1.62 0 0 0 30.442 14a1.5 1.5 0 0 0-1.273.728l-6.255 8.823-.82-1.324a1.54 1.54 0 0 0-1.31-.756 1.52 1.52 0 0 0-1.331.827l-.783 1.253-3.795-5.52a1.54 1.54 0 0 0-1.352-.8 1.6 1.6 0 0 0-1.521 1.644 1.67 1.67 0 0 0 .366 1.066l5.026 7.205a1.506 1.506 0 0 0 2.686.058l.717-1.136.698 1.103a2 2 0 0 0 .178.266 2 2 0 0 0 .13.141l.106.092a2 2 0 0 0 .227.15l.1.05a1.4 1.4 0 0 0 .455.122l.123.008h.029a1.53 1.53 0 0 0 1.204-.617l7.61-10.702a1.7 1.7 0 0 0 .341-1.007 1.6 1.6 0 0 0-.42-1.158m-9.212 12.82'/></svg>",
+ "folder-webpack-open": "<svg viewBox='0 0 32 32'><path fill='#03a9f4' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#fafafa' d='m30.992 14.263-7-4a2 2 0 0 0-1.984 0l-7 4A2 2 0 0 0 14 16v8.65a2 2 0 0 0 1.025 1.746l6 3.35A2 2 0 0 0 23 29.73a2 2 0 0 0 1.975.016l6-3.35A2 2 0 0 0 32 24.65V16a2 2 0 0 0-1.008-1.737'/><path fill='#0277bd' d='M30 24.65 24 28v-6l6-3.35zM23 12l-7 4 7 4 7-4zm-7 12.65L22 28v-6l-6-3.35z'/></svg>",
+ "folder-webpack": "<svg viewBox='0 0 32 32'><path fill='#03a9f4' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#fafafa' d='m30.992 14.263-7-4a2 2 0 0 0-1.984 0l-7 4A2 2 0 0 0 14 16v8.65a2 2 0 0 0 1.025 1.746l6 3.35A2 2 0 0 0 23 29.73a2 2 0 0 0 1.975.016l6-3.35A2 2 0 0 0 32 24.65V16a2 2 0 0 0-1.008-1.737'/><path fill='#0277bd' d='M30 24.65 24 28v-6l6-3.35zM23 12l-7 4 7 4 7-4zm-7 12.65L22 28v-6l-6-3.35z'/></svg>",
+ "folder-windows-open": "<svg viewBox='0 0 32 32'><path fill='#2196f3' d='M24.667 27.333h-20A2.667 2.667 0 0 1 2 24.667v-16A2.657 2.657 0 0 1 4.648 6h8.019l2.666 2.667h9.334a2.68 2.68 0 0 1 2.666 2.666H4.667v13.334L7.52 14h22.76l-3.04 11.333a2.67 2.67 0 0 1-2.573 2'/><path fill='#bbdefb' d='M14 12h8v8h-8zm10 0h8v8h-8zm0 10h8v8h-8zm-10 0h8v8h-8z'/></svg>",
+ "folder-windows": "<svg viewBox='0 0 32 32'><path fill='#2196f3' d='M12.667 6h-8A2.657 2.657 0 0 0 2 8.648v16.019a2.68 2.68 0 0 0 2.667 2.666H26a2.68 2.68 0 0 0 2.667-2.666V11.333A2.667 2.667 0 0 0 26 8.667H15.333z'/><path fill='#bbdefb' d='M14 12h8v8h-8zm10 0h8v8h-8zm0 10h8v8h-8zm-10 0h8v8h-8z'/></svg>",
+ "folder-wordpress-open": "<svg viewBox='0 0 32 32'><path fill='#0277bd' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#e1f5fe' d='M22 8a10 10 0 0 0-8.356 4.51l.642.013c1.049 0 2.669-.125 2.669-.125a.413.413 0 0 1 .07.824l-1.155.119 3.648 10.803 2.188-6.56-1.559-4.243-1.061-.12a.414.414 0 0 1 .07-.823l2.632.125c1.049 0 2.67-.125 2.67-.125a.413.413 0 0 1 .062.824l-1.143.119 3.612 10.72 1.002-3.332a12.7 12.7 0 0 0 .757-3.228 5.2 5.2 0 0 0-.83-2.764 4.67 4.67 0 0 1-.978-2.34 1.73 1.73 0 0 1 1.681-1.771h.127A10 10 0 0 0 22.001 8Zm8.777 5.201.07 1.037a9.5 9.5 0 0 1-.771 3.576l-3.053 8.822a10 10 0 0 0 3.754-13.435m-17.916.724A10.2 10.2 0 0 0 12 18.003 9.98 9.98 0 0 0 17.64 27Zm9.315 4.952-2.996 8.72a10.06 10.06 0 0 0 6.144-.164l-.073-.142Z'/></svg>",
+ "folder-wordpress": "<svg viewBox='0 0 32 32'><path fill='#0277bd' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#e1f5fe' d='M22 8a10 10 0 0 0-8.356 4.51l.642.013c1.049 0 2.669-.125 2.669-.125a.413.413 0 0 1 .07.824l-1.155.119 3.648 10.803 2.188-6.56-1.559-4.243-1.061-.12a.414.414 0 0 1 .07-.823l2.632.125c1.049 0 2.67-.125 2.67-.125a.413.413 0 0 1 .062.824l-1.143.119 3.612 10.72 1.002-3.332a12.7 12.7 0 0 0 .757-3.228 5.2 5.2 0 0 0-.83-2.764 4.67 4.67 0 0 1-.978-2.34 1.73 1.73 0 0 1 1.681-1.771h.127A10 10 0 0 0 22.001 8Zm8.777 5.201.07 1.037a9.5 9.5 0 0 1-.771 3.576l-3.053 8.822a10 10 0 0 0 3.754-13.435m-17.916.724A10.2 10.2 0 0 0 12 18.003 9.98 9.98 0 0 0 17.64 27Zm9.315 4.952-2.996 8.72a10.06 10.06 0 0 0 6.144-.164l-.073-.142Z'/></svg>",
+ "folder-yarn-open": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M28.967 12H9.442a2 2 0 0 0-1.898 1.368L4 24V10h24a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464l-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h22l4.805-11.212A2 2 0 0 0 28.967 12'/><path fill='#b3e5fc' d='M31.445 24.006a7.2 7.2 0 0 0-2.736 1.301 16.2 16.2 0 0 1-4.038 1.886 1.1 1.1 0 0 1-.68.394 42 42 0 0 1-4.455.413c-.805.006-1.296-.212-1.434-.554a1.14 1.14 0 0 1 .58-1.474l.02-.008a2.5 2.5 0 0 1-.357-.27c-.118-.122-.243-.368-.28-.277-.156.392-.237 1.352-.654 1.784a2.04 2.04 0 0 1-2.3.052c-.704-.386.05-1.295.05-1.295a.497.497 0 0 1-.679-.23l-.007-.015a3.56 3.56 0 0 1-.46-2.106 3.92 3.92 0 0 1 1.221-2.08 6.85 6.85 0 0 1 .455-3.144 7.4 7.4 0 0 1 2.187-2.614s-1.34-1.527-.84-2.912c.322-.903.453-.895.56-.935a2.5 2.5 0 0 0 1.003-.61 3.58 3.58 0 0 1 3.046-1.213s.8-2.53 1.546-2.035a13.3 13.3 0 0 1 1.06 2.062s.885-.535.985-.336a8.35 8.35 0 0 1 .361 4.382 10.1 10.1 0 0 1-1.795 3.863 7.9 7.9 0 0 1 1.808 2.778 8.4 8.4 0 0 1 .181 3.722l.024.044a4.44 4.44 0 0 0 2.343-.934 5.77 5.77 0 0 1 2.954-1.147.75.75 0 0 1 .873.62.775.775 0 0 1-.542.888'/></svg>",
+ "folder-yarn": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='m13.844 7.536-1.288-1.072A2 2 0 0 0 11.276 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H15.124a2 2 0 0 1-1.28-.464'/><path fill='#b3e5fc' d='M31.445 24.006a7.2 7.2 0 0 0-2.736 1.301 16.2 16.2 0 0 1-4.038 1.886 1.1 1.1 0 0 1-.68.394 42 42 0 0 1-4.455.413c-.805.006-1.296-.212-1.434-.554a1.14 1.14 0 0 1 .58-1.474l.02-.008a2.5 2.5 0 0 1-.357-.27c-.118-.122-.243-.368-.28-.277-.156.392-.237 1.352-.654 1.784a2.04 2.04 0 0 1-2.3.052c-.704-.386.05-1.295.05-1.295a.497.497 0 0 1-.679-.23l-.007-.015a3.56 3.56 0 0 1-.46-2.106 3.92 3.92 0 0 1 1.221-2.08 6.85 6.85 0 0 1 .455-3.144 7.4 7.4 0 0 1 2.187-2.614s-1.34-1.527-.84-2.912c.322-.903.453-.895.56-.935a2.5 2.5 0 0 0 1.003-.61 3.58 3.58 0 0 1 3.046-1.213s.8-2.53 1.546-2.035a13.3 13.3 0 0 1 1.06 2.062s.885-.535.985-.336a8.35 8.35 0 0 1 .361 4.382 10.1 10.1 0 0 1-1.795 3.863 7.9 7.9 0 0 1 1.808 2.778 8.4 8.4 0 0 1 .181 3.722l.024.044a4.44 4.44 0 0 0 2.343-.934 5.77 5.77 0 0 1 2.954-1.147.75.75 0 0 1 .873.62.775.775 0 0 1-.542.888'/></svg>",
+ "folder-zeabur-open": "<svg fill='none' viewBox='0 0 32 32'><path fill='#7e57c2' d='M29 12H9.4c-.9 0-1.6.6-1.9 1.4L4 24V10h24c0-1.1-.9-2-2-2H15.1c-.5 0-.9-.2-1.3-.5l-1.3-1.1c-.3-.2-.8-.4-1.2-.4H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h22l4.8-11.2c.4-1 0-2.2-1-2.6-.3-.1-.6-.2-.8-.2'/><g fill='#d1c4e9' clip-path='url(#a)'><path d='M20 24h12v6H12v-6h6l8-4H12v-6h20v6z'/><path d='M26 14H12v6h14zm6 10H20v6h12z'/></g><defs><clipPath id='a'><path d='M12 14h20v16H12z'/></clipPath></defs></svg>",
+ "folder-zeabur": "<svg fill='none' viewBox='0 0 32 32'><path fill='#7e57c2' d='m13.8 7.5-1.3-1.1c-.3-.2-.8-.4-1.2-.4H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h24c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2H15.1c-.5 0-.9-.1-1.3-.5'/><g fill='#d1c4e9' clip-path='url(#a)'><path d='M20 24h12v6H12v-6h6l8-4H12v-6h20v6z'/><path d='M26 14H12v6h14zm6 10H20v6h12z'/></g><defs><clipPath id='a'><path d='M12 14h20v16H12z'/></clipPath></defs></svg>",
+ "folder": "<svg viewBox='0 0 16 16'><path fill='#90a4ae' d='m6.922 3.768-.644-.536A1 1 0 0 0 5.638 3H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H7.562a1 1 0 0 1-.64-.232'/></svg>",
+ "font": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='M24 28h4L18 4h-4L4 28h4l8-19.422'/><path fill='#f44336' d='M8 20h16v4H8z'/></svg>",
+ "forth": "<svg viewBox='0 0 67.733 67.733'><path fill='#ef5350' d='M10.321 12.006c-.21 0-.38.173-.38.39v12.63c0 .215.17.389.38.389h16.925c.21 0 .38-.174.38-.39v-12.63a.384.384 0 0 0-.38-.389zm30.167 0c-.21 0-.38.173-.38.39v12.63c0 .215.17.389.38.389h16.925c.21 0 .38-.174.38-.39v-12.63a.384.384 0 0 0-.38-.389zM10.321 34.328c-.21 0-.38.173-.38.39v12.63c0 .215.17.389.38.389h16.925c.21 0 .38-.174.38-.39v-12.63a.384.384 0 0 0-.38-.389zm30.167 0c-.21 0-.38.173-.38.39v12.63c0 .215.17.389.38.389h4.053v4.351H40.51a.374.374 0 0 0-.375.375v2.89c0 .207.167.374.375.374h8.303a.373.373 0 0 0 .374-.374v-4.135h3.798a.374.374 0 0 0 .374-.375v-3.106h4.054c.21 0 .38-.174.38-.39v-12.63a.384.384 0 0 0-.38-.389z'/></svg>",
+ "fortran": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='M6 4v2h3a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6v2h12v-2h-3a1 1 0 0 1-1-1v-9h4a2 2 0 0 1 2 2v2h2V10h-2v2a2 2 0 0 1-2 2h-4V6h6a4 4 0 0 1 4 4h2V4Z'/></svg>",
+ "foxpro": "<svg viewBox='0 0 300 300'><path fill='#fbc02d' d='M110.978 292.583c1.71-.48 4.858-.93 6.994-.996l3.886-.123v-5.072c0-9.684-6.355-24.885-13.844-33.115-1.22-1.34-5.476-4.083-9.459-6.096-7.06-3.568-7.46-3.643-16.061-3.032-4.85.345-11.618 1.392-15.038 2.325-21.15 5.777-53.825-10.127-53.871-26.223-.017-5.957 2.584-8.327 11.094-10.108 10.248-2.144 17.288-5.479 18.06-8.554 1.18-4.697-.304-19.367-2.339-23.14-1.434-2.66-1.925-5.792-1.94-12.369-.018-8.404.16-9.092 3.878-15.025 2.143-3.42 6.093-8.94 8.779-12.265 4.553-5.639 4.908-6.515 5.266-12.953.414-7.455-2.387-20.66-6.188-29.184-4.29-9.62-3.664-35.68 1.334-55.392C56.631 21.138 66.772 4.993 73.058 6.99c2.083.66 2.215 1.013 9.388 24.968 2.121 7.084 2.404 7.46 8.731 11.62 10.456 6.875 20.048 18.022 29.738 34.563l2.684 4.582 10.203 1.289c17.3 2.184 37.568 10.032 48.636 18.83 2.26 1.796 4.824 3.043 5.7 2.771.875-.271 6.923-2.912 13.438-5.867 14.079-6.386 28.26-11.15 47.56-15.972 20.388-5.097 31.764-7.167 34.968-6.363 2.781.699 2.796.75 2.153 7.662-.73 7.857-5.042 21.461-9.758 30.793-7.233 14.308-25.004 34-41.889 46.417-7.607 5.594-7.995 6.08-8.621 10.787-5.233 39.421-15.87 61.176-43.496 88.956-12.947 13.02-13.966 13.797-25.405 19.401-15.592 7.64-23.663 9.762-44.04 11.572-4.036.36-4.494.268-2.072-.414zm-80.394-56.332a167 167 0 0 1-2.505-1.814c-1.917-1.406-1.965-1.384-.65.294.783.997 1.91 1.813 2.504 1.813s.888-.132.65-.293z'/><path fill='#ff9800' d='M110.978 292.583c1.71-.48 4.858-.93 6.994-.996l3.886-.123-.012-4.922c-.015-5.735-1.855-13.217-5.005-20.336-1.63-3.684-1.805-4.689-.653-3.733 2.945 2.445 2.772.137-.327-4.355-3.607-5.228-8.862-9.639-16.88-14.163-3.157-1.782-6.76-4.412-8.006-5.844-1.367-1.574-3.365-2.604-5.048-2.604-2.704 0-2.657.1 1.547 3.33 2.383 1.832 4.333 3.63 4.333 3.992s-4.08.951-9.067 1.305c-4.987.353-11.864 1.407-15.284 2.34-21.15 5.777-53.825-10.127-53.871-26.223-.018-5.957 2.584-8.327 11.094-10.107 10.282-2.151 17.288-5.477 18.066-8.578 1.24-4.94-.43-20.227-2.787-25.52-1.773-3.981-2.17-6.518-1.862-11.917.432-7.593 3.474-13.514 13.02-25.34 4.553-5.639 4.91-6.514 5.267-12.953.413-7.454-2.387-20.66-6.188-29.184-4.29-9.618-3.664-35.68 1.334-55.392C56.631 21.138 66.772 4.992 73.058 6.988c2.083.661 2.215 1.014 9.388 24.968 2.121 7.085 2.404 7.46 8.731 11.62 10.456 6.876 20.048 18.022 29.738 34.563l2.684 4.583 10.203 1.289c17.3 2.184 37.568 10.032 48.636 18.83 2.26 1.796 4.824 3.043 5.7 2.771.875-.271 6.922-2.912 13.438-5.867 14.079-6.386 28.26-11.15 47.56-15.972 20.388-5.097 31.764-7.167 34.968-6.363 2.781.699 2.795.75 2.153 7.662-.73 7.857-5.042 21.461-9.758 30.793-7.233 14.308-25.004 34-41.889 46.417-7.608 5.594-7.995 6.08-8.621 10.787-5.233 39.421-15.87 61.176-43.496 88.956-12.947 13.02-13.966 13.797-25.405 19.401-15.592 7.64-23.663 9.762-44.04 11.572-4.036.36-4.494.267-2.072-.414zm28.487-17.757c15.214-5.868 29.597-17.34 38.77-30.924 10.817-16.02 18.632-40.542 21.438-67.272l1.34-12.765 7.065-6.263c7.63-6.765 16.248-18.903 22.119-31.154 4.509-9.41 4.622-12.622.517-14.743-6.472-3.347-27.43-1.059-46.262 5.05-12.977 4.211-14.76 4.4-19.647 2.08l-3.479-1.65-7.558 5.011c-4.158 2.756-12.657 10.028-18.889 16.158-11.187 11.007-14.057 12.906-14.057 9.305 0-2.563 14.333-18.344 24.633-27.122 6.74-5.744 9.286-7.307 11.917-7.32 2.425-.011 2.932-.277 1.846-.964-.823-.52-3.643-.694-6.265-.383-4.02.477-5.899.003-11.973-3.008-3.964-1.966-12.103-5.63-18.087-8.143-9.855-4.14-11.67-5.35-19.27-12.867C99.007 83.29 92.3 75.125 88.715 69.711c-7.619-11.512-9.882-14.176-13.738-16.182-6.709-3.49-11.336 2.036-14.805 17.687-2.129 9.603-1.636 29.433 1.083 43.55 2.508 13.023 1.12 18.641-7.308 29.553-9.286 12.025-10.76 19.67-6.29 32.617 3.345 9.682 4.876 19.774 3.542 23.34-2.335 6.24-12.264 13.471-18.5 13.471-3.035 0-3.19.184-2.564 3.04.794 3.61-2.412 13.54-4.373 13.54-1.97 0-1.581 1.74.91 4.06 3.543 3.301 11.06 6.389 16.814 6.907 4.561.412 4.949.314 2.725-.687a979 979 0 0 1-5.699-2.599l-3.109-1.433 4.145.762c6.893 1.266 21.308.708 29.22-1.132 4.101-.953 10.03-1.733 13.172-1.733 10.801 0 24.919 7.327 33.195 17.229 4.183 5.005 10.903 18.998 10.926 22.751.026 4.412.858 4.44 11.402.374zm-5.69-42.91c-34.094-5.987-38.188-6.821-39.716-8.089-2.84-2.356 2.65-2.362 31.777-.034 15.761 1.26 28.936 1.963 29.277 1.563.34-.401 1.119-5.78 1.728-11.953.61-6.174 1.39-11.504 1.733-11.847 1.709-1.709 2.777 2.714 3.803 15.74 1.267 16.085 1.015 16.916-5.357 17.66-1.957.229-12.417-1.14-23.245-3.04m-52.977-29.777c-.839-1.356 2.065-3.717 11.39-9.264 9.722-5.78 21.588-9.929 32.597-11.395 13.411-1.786 17.117.44 6.12 3.676-4.205 1.238-6 2.454-8.009 5.428-3.307 4.9-9.59 9.453-14.248 10.327-2.002.375-6.759.093-10.572-.626-6.389-1.205-7.31-1.14-11.762.83-3.246 1.436-5.054 1.771-5.515 1.025zM61.8 181.891c-4.124-5.852-5.081-11.666-2.813-17.094 1.092-2.615 1.411-5.572 1.014-9.413-.62-6.007.614-8.12 3.393-5.814.85.706 3.211 4.26 5.244 7.896 4.35 7.777 4.959 15.03 2.008 23.912-2.29 6.894-4.267 7.008-8.846.513'/><path fill='#263238' d='M110.978 292.583c1.71-.48 4.858-.93 6.994-.996 2.347-.075 3.89-.637 3.897-1.418.004-.73 5.324-3.067 12.175-5.35 15.388-5.127 25.225-9.652 34.145-15.702 29.01-19.678 47.927-56.35 47.958-92.981.01-10.453.32-10.95 10.844-17.31 8.456-5.11 30.613-27.397 30.613-30.793 0-.536 1.457-3.175 3.238-5.863 3.99-6.023 10.38-23.325 10.995-29.775.435-4.56.323-4.8-2.485-5.336-10.334-1.974-40.272 5.589-66.668 16.842-19.344 8.248-18.662 8.18-28.102 2.792-15.56-8.878-31.568-15.114-45.666-17.789-4.657-.883-8.536-1.716-8.618-1.852-1.217-1.964-13.342-18.003-16.245-21.488-2.138-2.565-7.377-7.672-11.643-11.352-8.3-7.155-11.524-12.045-15.023-22.776-2.646-8.118-4.738-11.482-7.106-11.42-.985.026-3.746 2.288-6.138 5.024-4.88 5.587-7.616 14.008-9.749 30-1.675 12.57-.592 40.068 2.189 55.6 3.13 17.474 1.72 23.614-7.368 32.123l-4.426 4.145 4.343-5.33c6.243-7.661 7.43-10.45 7.434-17.466.004-6.504-3.948-23.123-6.818-28.673-2.286-4.42-3.133-24.55-1.54-36.61 3.76-28.47 16.268-54.564 24.849-51.842 2.083.662 2.216 1.014 9.388 24.969 2.122 7.084 2.404 7.46 8.732 11.62 10.456 6.875 20.048 18.022 29.738 34.563l2.684 4.582 10.161 1.284c17.57 2.218 38.61 10.584 50.486 20.072l2.808 2.243 9.398-4.367c5.169-2.402 13.464-5.935 18.435-7.852 18.978-7.321 63.593-18.103 69.142-16.71 2.872.72 2.875.729 2.228 7.681-.73 7.856-5.041 21.46-9.758 30.793-7.232 14.308-25.004 34-41.889 46.417-7.607 5.593-7.995 6.08-8.62 10.787-5.233 39.421-15.871 61.176-43.497 88.956-12.947 13.02-13.966 13.797-25.405 19.401-15.592 7.64-23.663 9.76-44.04 11.572-4.036.358-4.494.267-2.072-.415zm-65.8-46.306c-8.26-2.214-20.64-8.61-24.89-12.861-7.241-7.241-9.021-15.757-4.205-20.116 1.073-.972 5.017-2.408 8.763-3.191 8.78-1.837 14.8-4.553 19.146-8.637 1.913-1.798 4.127-3.27 4.922-3.27 5.032 0-6.224 11.61-13.063 13.474-2.565.699-5.241 1.434-5.947 1.634-.94.266-1.012 1.077-.266 3.039 1.193 3.138-.806 8.86-4.458 12.76l-2.282 2.437 3.109 2.521c11.045 8.96 23.647 10.678 44.586 6.077 12.895-2.833 13.257-2.856 16.62-1.046 6.163 3.32 5.233 4.346-4.562 5.039-4.937.349-11.542 1.342-14.677 2.207-7.077 1.954-15.352 1.93-22.797-.067zm103.622-13.43c-.855-.19-11.94-2.02-24.634-4.066-24.527-3.953-26.442-4.385-25.583-5.776.577-.932 1.04-.91 34.537 1.674 11.609.897 21.688 1.405 22.398 1.133.84-.321 1.55-4.145 2.031-10.932.721-10.171 2.027-14.003 3.137-9.206.302 1.305.987 7.236 1.523 13.18 1.229 13.66.8 14.62-6.452 14.461-2.973-.066-6.103-.277-6.958-.468zm-62.842-35.035c9.795-8.936 32.919-17.021 46.583-16.29 2.632.141 1.297 1.805-2.125 2.647-1.572.387-5.686 3.662-9.142 7.277-7.841 8.202-11.972 9.648-21.914 7.672-5.395-1.073-7.267-1.07-9.948.007-6.85 2.753-7.6 2.468-3.454-1.313m-23.513-17.967c-4.163-6.559-4.619-9.794-2.197-15.588 1.148-2.75 1.403-5.397.904-9.395-.626-5.014-.51-5.53 1.183-5.204 1.033.2 3.52 3.16 5.527 6.578 4.576 7.796 5.448 15.96 2.535 23.746-1.048 2.802-2.411 5.265-3.03 5.47-.62.207-2.834-2.317-4.922-5.607m59.413-32.9c0-2.015 12.835-16.484 20.322-22.909 9.177-7.875 12.004-9.768 14.593-9.768 4.217 0 2.596 2.78-4.605 7.892-3.848 2.73-11.858 9.766-17.803 15.634-5.944 5.869-11.19 10.67-11.657 10.67s-.85-.684-.85-1.52z'/></svg>",
+ "freemarker": "<svg viewBox='0 0 16 16'><path fill='#2196f3' d='m12.5 11 .75.5L15 8l-1.75-3.5-.75.5L14 8zM6 4h1v2h2V4h1v2h1.5v1H10v2h1.5v1H10v2H9v-2H7v2H6v-2H4.5V9H6V7H4.5V6H6zm1 5h2V7H7zM3.5 5l-.75-.5L1 8l1.75 3.5.75-.5L2 8z'/></svg>",
+ "fsharp": "<svg viewBox='0 0 500 500'><path fill='#0288d1' d='m236.249 36.066-213.94 213.94 213.94 213.94v-84.36l-129.7-129.7 129.7-129.7z'/><path fill='#0288d1' d='m236.249 156.017-93.622 93.62 93.622 93.622z'/><path fill='#00b8d4' d='m263.759 36.047 213.94 213.94-213.94 213.94v-84.36l129.7-129.7-129.7-129.7z'/></svg>",
+ "fusebox": "<svg viewBox='0 0 152.99 160.01'><g data-name='Layer 2'><g data-name='Fuse Box'><path fill='#fafafa' d='m76.995 12.087 64.783 21.76-6.72 76.61-57.094 37.236-60.916-38.345-4.975-75.178z' data-mit-no-recolor='true'/><path fill='#424242' d='m77.982 149.831-62.688-39.444-5.124-77.518 66.817-22.694 66.729 22.406-6.93 78.906zM18.794 108.31l59.153 37.235 55.382-36.127 6.52-74.306-62.845-21.105-63.028 21.437z' data-mit-no-recolor='true'/><path fill='#0277bd' d='M76.856 140.055V64.012l58.42-26.028-5.229 68.754z'/><path fill='#424242' d='M76.856 140.055 24.179 107.42l-5.603-69.487 58.28 26.08z' data-mit-no-recolor='true'/><path fill='#fafafa' d='m32.498 56.2.47 13.905L65.57 85.973l.105 12.822-32.296-15.894 1.432 32.383-12.71-7.856-4.407-70.308 59.17 26.901v13.494z' data-mit-no-recolor='true'/><path fill='#fafafa' d='M128.799 89.404c-.07 11.451-5.525 23.209-14.638 28.908l-37.358 24.38V64.038L113.7 47.594c4.504-2.103 8.3-1.248 10.823 2.715 1.658 2.618 2.523 6.31 2.496 10.657a31.15 31.15 0 0 1-3.954 15.546c3.745 1.745 5.814 5.717 5.735 12.892zm-11.268-23.576c0-4.05-1.746-6.049-4.295-4.757L87.923 73.71l-.07 15.022 25.697-13.608c2.182-1.152 3.98-5.159 3.98-9.296zm1.876 28.14c0-4.983-2.112-7.192-5.717-5.175l-25.836 14.463v17.09l25.61-15.24c3.866-2.304 5.926-6.345 5.943-11.137z' data-mit-no-recolor='true'/><path fill='#424242' d='m76.856 17.874-58.21 20.031 95.812 2.078-73.337 8.039 35.735 15.99 18.12-8.073-24.204-1.196 43.45-7.385 21.054-9.374z' data-mit-no-recolor='true'/></g></g></svg>",
+ "gamemaker": "<svg viewBox='0 0 16 16'><path fill='#26a69a' d='M8 1.422 14.578 8h-3.759v3.853c-.94.846-1.88 1.785-2.819 2.725L1.422 8zM5.275 8 8 10.725V8h2.725A37 37 0 0 0 8 5.275 37 37 0 0 0 5.275 8'/></svg>",
+ "garden": "<svg viewBox='0 0 16 16'><g stroke-width='.752'><path fill='#80cbc4' d='M14 14V2h-1.716v12z'/><path fill='#80deea' d='M10.58 14V2H8.865v12z'/><path fill='#26a69a' d='M10.58 8.98V2H8.865v6.98z'/><path fill='#ff80ab' d='M10.608 10.034v-1.65H8.844v1.65z'/><path fill='#1565c0' d='M7.145 14V6.377H5.452V14z'/><path fill='#43a047' d='M5.451 14V2H3.716v12z'/><path fill='#4db6ac' d='M3.716 14V2H2v12z'/><path fill='#0288d1' d='M7.148 6.477V2H5.45v4.477z'/><path fill='#ff80ab' d='M7.145 8.162V6.504H5.452v1.658z'/><path fill='#4caf50' d='M12.296 14V2h-1.715v12z'/><path fill='#00bfa5' d='M8.864 14V2H7.148v12z'/></g></svg>",
+ "gatsby": "<svg viewBox='0 0 28 28'><path fill='#fafafa' d='M23.835 14h-6.259v1.788h4.292c-.626 2.682-2.593 4.918-5.186 5.812L6.4 11.318c1.073-3.13 4.113-5.365 7.6-5.365 2.682 0 5.096 1.342 6.616 3.398l1.341-1.162C20.17 5.775 17.308 4.165 14 4.165c-4.65 0-8.583 3.308-9.566 7.69l11.801 11.801c4.292-1.073 7.6-5.007 7.6-9.656m-19.67.09c0 2.503.984 4.917 2.861 6.794s4.381 2.861 6.795 2.861z'/><path fill='#6a1b9a' d='M14 1.483C7.116 1.483 1.483 7.116 1.483 14S7.116 26.517 14 26.517 26.517 20.884 26.517 14 20.884 1.483 14 1.483m-6.974 19.49c-1.877-1.877-2.86-4.38-2.86-6.794l9.745 9.656c-2.504-.09-5.007-.984-6.885-2.861zm9.12 2.594L4.433 11.854C5.417 7.474 9.351 4.165 14 4.165c3.308 0 6.17 1.61 7.957 4.024l-1.34 1.162C19.096 7.294 16.681 5.953 14 5.953c-3.487 0-6.437 2.236-7.6 5.365L16.682 21.6c2.593-.894 4.56-3.13 5.186-5.812h-4.292V14h6.259c0 4.65-3.308 8.583-7.69 9.567z'/></svg>",
+ "gcp": "<svg viewBox='0 0 300 300'><path fill='#f44336' d='M184.351 103.816h7.786l22.191-22.191 1.09-9.421a99.743 99.743 0 0 0-162.266 48.664 12.07 12.07 0 0 1 7.786-.467l44.382-7.32s2.258-3.737 3.426-3.503a55.36 55.36 0 0 1 75.76-5.762z'/><path fill='#448aff' d='M245.94 120.868a100 100 0 0 0-30.132-48.587l-31.146 31.146a55.36 55.36 0 0 1 20.323 43.914v5.529a27.72 27.72 0 1 1 0 55.438h-55.439l-5.528 5.606v33.248l5.528 5.528h55.439a72.101 72.101 0 0 0 40.956-131.822z'/><path fill='#43a047' d='M94.03 252.379h55.438v-44.382H94.03a27.6 27.6 0 0 1-11.446-2.492l-7.786 2.414-22.347 22.19-1.947 7.787a71.7 71.7 0 0 0 43.526 14.483'/><path fill='#ffc107' d='M94.03 108.41a72.101 72.101 0 0 0-43.526 129.252l32.158-32.157a27.72 27.72 0 1 1 36.673-36.673l32.158-32.158A72.02 72.02 0 0 0 94.03 108.41'/></svg>",
+ "gemfile": "<svg viewBox='0 0 32 32'><path fill='#e53935' d='M21.184 10.016V10H10.881l.016.033-.016-.017L8 14l8.032 10L24 14z'/><path fill='#e53935' d='m16 3.455 11 6.286v12.518l-11 6.286-11-6.286V9.741zM16 0 2 8v16l14 8 14-8V8z'/></svg>",
+ "gemini-ai": "<svg viewBox='0 0 16 16'><path fill='#448aff' d='M15 8.014A7.457 7.457 0 0 0 8.014 15h-.028A7.456 7.456 0 0 0 1 8.014v-.028A7.456 7.456 0 0 0 7.986 1h.028A7.457 7.457 0 0 0 15 7.986z'/></svg>",
+ "gemini": "<svg viewBox='0 0 32 32'><path fill='#81c784' d='M16 2a14 14 0 1 0 14 14A14 14 0 0 0 16 2m11.3 10h-5.64a22.5 22.5 0 0 0-2.705-7.616A12.03 12.03 0 0 1 27.3 12M20 16c0 .693-.037 1.357-.094 2h-7.811A22 22 0 0 1 12 16c0-.693.037-1.357.094-2h7.811c.058.643.095 1.307.095 2m-4 12c-.115 0-.226-.014-.34-.017A20.4 20.4 0 0 1 12.368 20h7.264a20.4 20.4 0 0 1-3.292 7.983c-.114.003-.225.017-.34.017m-3.632-16a20.4 20.4 0 0 1 3.292-7.983c.114-.003.225-.017.34-.017s.226.014.34.017A20.4 20.4 0 0 1 19.632 12Zm.683-7.618A22.4 22.4 0 0 0 10.339 12H4.7a12.03 12.03 0 0 1 8.35-7.618ZM4.18 14h5.91c-.052.647-.091 1.307-.091 2s.039 1.353.091 2H4.18a11.2 11.2 0 0 1 0-4m.52 6h5.638a22.4 22.4 0 0 0 2.712 7.618A12.03 12.03 0 0 1 4.7 20m14.255 7.616A22.5 22.5 0 0 0 21.661 20H27.3a12.03 12.03 0 0 1-8.344 7.616ZM27.819 18h-5.91c.052-.647.091-1.307.091-2s-.039-1.353-.091-2h5.91a11.2 11.2 0 0 1 0 4'/></svg>",
+ "git": "<svg viewBox='0 0 32 32'><path fill='#e64a19' d='M13.172 2.828 11.78 4.22l1.91 1.91 2 2A2.986 2.986 0 0 1 20 10.81a3.25 3.25 0 0 1-.31 1.31l2.06 2a2.68 2.68 0 0 1 3.37.57 2.86 2.86 0 0 1 .88 2.117 3.02 3.02 0 0 1-.856 2.109A2.9 2.9 0 0 1 23 19.81a2.93 2.93 0 0 1-2.13-.87 2.694 2.694 0 0 1-.56-3.38l-2-2.06a3 3 0 0 1-.31.12V20a3 3 0 0 1 1.44 1.09 2.92 2.92 0 0 1 .56 1.72 2.88 2.88 0 0 1-.878 2.128 2.98 2.98 0 0 1-2.048.871 2.981 2.981 0 0 1-2.514-4.719A3 3 0 0 1 16 20v-6.38a2.96 2.96 0 0 1-1.44-1.09 2.9 2.9 0 0 1-.56-1.72 2.9 2.9 0 0 1 .31-1.31l-3.9-3.9-7.579 7.572a4 4 0 0 0-.001 5.658l10.342 10.342a4 4 0 0 0 5.656 0l10.344-10.344a4 4 0 0 0 0-5.656L18.828 2.828a4 4 0 0 0-5.656 0'/></svg>",
+ "github-actions-workflow": "<svg viewBox='0 0 32 32'><path fill='#78909c' d='M26 18h-6a2 2 0 0 0-2 2v2h-6a2 2 0 0 1-2-2v-6h2a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2v6a4 4 0 0 0 4 4h6v2a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-6a2 2 0 0 0-2-2M6.5 12a.5.5 0 0 1-.5-.5v-5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5ZM26 25.5a.5.5 0 0 1-.5.5h-5a.5.5 0 0 1-.5-.5v-5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 .5.5Z'/></svg>",
+ "github-sponsors": "<svg fill='#f06292' viewBox='0 0 16 16'><path d='M7.984 14q-.217 0-.409-.077a1.3 1.3 0 0 1-.369-.236l-.73-.671Q4.24 11.012 2.62 9.18 1 7.35 1 5.43q0-1.453.994-2.441.995-.99 2.445-.989.85 0 1.784.436C6.845 2.727 7.438 3.142 8 4c.59-.858 1.189-1.273 1.798-1.564Q10.711 2 11.561 2q1.45 0 2.445.989.994.988.994 2.44 0 1.967-1.7 3.823A59 59 0 0 1 9.526 13l-.747.687a1.15 1.15 0 0 1-.794.313z'/></svg>",
+ "gitlab": "<svg viewBox='0 0 32 32'><path fill='#e53935' d='m29.532 13.083-.037-.105-3.811-10.322a1 1 0 0 0-.392-.49.985.985 0 0 0-1.39.316 1 1 0 0 0-.122.28L21.208 10H10.792L8.22 2.762a1.004 1.004 0 0 0-1.246-.721 1 1 0 0 0-.266.124 1 1 0 0 0-.392.491L2.507 12.98l-.04.103a7.52 7.52 0 0 0 2.348 8.491l.015.012.032.026 5.797 4.511 2.876 2.257 1.747 1.372a1.146 1.146 0 0 0 1.424 0l1.747-1.372 2.876-2.257 5.838-4.537.016-.013a7.52 7.52 0 0 0 2.35-8.49Z'/><path fill='#ef6c00' d='m29.532 13.083-.037-.105a12.6 12.6 0 0 0-5.123 2.394l-8.367 6.57 5.327 4.181 5.839-4.537.016-.013a7.52 7.52 0 0 0 2.345-8.49'/><path fill='#f9a825' d='m10.659 26.123 2.876 2.257 1.747 1.372a1.146 1.146 0 0 0 1.424 0l1.747-1.372 2.876-2.257L16 21.943Z'/><path fill='#ef6c00' d='M7.628 15.371a12.6 12.6 0 0 0-5.12-2.39l-.04.102a7.52 7.52 0 0 0 2.347 8.491l.015.012.032.026 5.797 4.511 5.331-4.18Z'/></svg>",
+ "gitpod": "<svg viewBox='0 0 32 32'><path fill='#ffa726' d='M18.258 4.156a2.6 2.6 0 0 1-.951 3.538l-.016.01-7.757 4.428a.66.66 0 0 0-.331.57v6.953a.66.66 0 0 0 .331.57l6.14 3.507a.66.66 0 0 0 .652 0l6.14-3.506a.66.66 0 0 0 .331-.571v-4.323l-5.52 3.11a2.6 2.6 0 0 1-2.57-4.518l.014-.009 7.898-4.452A3.61 3.61 0 0 1 28 12.603v7.58a4.95 4.95 0 0 1-2.495 4.295l-7.048 4.024a4.95 4.95 0 0 1-4.914 0l-7.048-4.024A4.95 4.95 0 0 1 4 20.182v-8.007A4.95 4.95 0 0 1 6.495 7.88l8.214-4.692a2.603 2.603 0 0 1 3.55.968Z'/></svg>",
+ "gleam": "<svg viewBox='0 0 24 24'><path fill='#ea80fc' d='m12.717 2.53-.027.005-.438.092-.22.334.04.41-.187.307.041.052s.063.077.031.196c-.016.06-.114.318-.205.543s-.176.43-.176.43l-.017.042.021.038a.55.55 0 0 0 .026.517c.057.1.035.2-.006.287a.6.6 0 0 1-.1.147l-.013.015-.147.432-.023.182.029.066-.057.045-.304.945-.036.283-.097.399-.196.56-.05.293-.082.342-.118.27-.084.156-.152.46-.111.49.492.349.85-.123.584-.766.28-.771.182-.852.112-.63.066-.339.174-.806.053-.473.127-.39.021-.329.082-.576.037-.476.084-.274.055-.625-.031-.146.119-.368-.078-.093.03-.17-.144-.31zM2.059 5.442l-.04.018-.575.227-.258.283.893 1.142.326.032.267.453.457.172.069.16.357.285.932.678.664.433 1.312 1.055.112.209.365.201.467.399.588.517.101.223.625.043.656-.85-.006-.605-1.738-1.395-1.27-.922-1.245-.838-.954-.515-.056-.14-1.065-.628-.406-.324zm17.912 5.528-.152.134-.233-.062-.959.14-.47-.109.02.131c-.116-.065-.24-.049-.343-.035-.105.014-.187.019-.232-.006-.124-.069-.261-.034-.361.004-.07.026-.09.043-.12.06l-.263-.15-.373.166-.4-.04-.42-.018-.45.08-.252-.063-.617.067-.326-.084-.04.048s-.027.036-.062.063a.2.2 0 0 1-.047.025c-.01.003-.004.002-.004.002-.087-.05-.178-.04-.253-.031-.075.008-.135.021-.135.021l-.008.004-.248.118-.795.132-.021.028s-.12.143-.227.304a1.5 1.5 0 0 0-.133.246.4.4 0 0 0-.031.124.17.17 0 0 0 .053.132c-.002-.001.01.02.011.065a1 1 0 0 1-.021.164 2 2 0 0 1-.068.226l-.026.063.461.367.47.063.29-.094.95-.174.185-.076.107.037.588-.053.19.106.124-.108.116.037.918-.138.402.125-.027-.147c.04.018.076.027.12.03q.08.004.182-.002c.134-.008.293-.028.448-.05.295-.04.548-.089.57-.093l.19.066.853-.127.527-.156.363.117.407-.058.224-.215.54.135-.018-.135s-.003-.062.027-.106c.015-.021.034-.04.084-.054a.6.6 0 0 1 .25.01c.276.058.45.023.557-.043a.3.3 0 0 0 .107-.106.3.3 0 0 0 .03-.066l.01-.043-.122-.158.053-.19-.338-.45-.576-.11-.358.005-.039-.062-.047-.002s-.247-.022-.449.045c-.107.036-.234-.072-.234-.072l-.139.127zM9.03 13.117l-.033.018-.658.35-.23.22-.173.356.203.572.319.295.306.465-3.802 3.158-.12.17-.798.615-.328.334-.559.422-.09.135-.152.445.385.531.459-.31.207.093.166-.199.101.047.729-.572.238-.133.287-.068.344-.266-.04-.05.206-.07.416-.359.273-.076.328-.224.141-.268.031-.021.112.048.52-.546.54-.292 1.102-.923.457 1.283.252.375.146.629.213.082-.012.033.112.75.193.18-.08.326.316.164a.25.25 0 0 0 .016.156c.026.067.072.147.148.254.164.23.369.29.522.277a.6.6 0 0 0 .262-.09l.023-.015.186-.445-.256-1.391-.225-1.092-.275-.592-.194-.552v-.002c0-.002-.051-.155-.105-.336a4 4 0 0 1-.12-.487 1.1 1.1 0 0 0-.103-.373l-.002-.003-.142-.301.19-.178.558-.648.08-.729-.688-.762-.484.196-.379.406-.582-.867z'/></svg>",
+ "gnuplot": "<svg viewBox='0 -16 16 16'><path fill='#1e88e5' d='M2-1c-.5 0-1-.5-1-1v-12c0-.5.5-1 1-1h12c.5 0 1 .5 1 1v12c0 .5-.5 1-1 1zm0-2v1h12v-7L9-4 6-7zm0-2 4-4 3 3 5-5v-3H2z'/></svg>",
+ "go-mod": "<svg viewBox='0 0 32 32'><path fill='#ec407a' d='M2 12h4v2H2zm-2 4h6v2H0zm4 4h2v2H4zm16.954-5H14v3h3.239a4.42 4.42 0 0 1-3.531 2 2.65 2.65 0 0 1-2.053-.858 2.86 2.86 0 0 1-.628-2.28A4.515 4.515 0 0 1 15.292 13a2.73 2.73 0 0 1 1.749.584l2.962-1.185A5.6 5.6 0 0 0 15.292 10a7.526 7.526 0 0 0-7.243 6.5 5.614 5.614 0 0 0 5.659 6.5 7.526 7.526 0 0 0 7.243-6.5 6.4 6.4 0 0 0 .003-1.5'/><path fill='#ec407a' d='M26.292 10a7.526 7.526 0 0 0-7.243 6.5 5.614 5.614 0 0 0 5.659 6.5 7.526 7.526 0 0 0 7.243-6.5 5.614 5.614 0 0 0-5.659-6.5m2.681 6.137A4.515 4.515 0 0 1 24.708 20a2.65 2.65 0 0 1-2.053-.858 2.86 2.86 0 0 1-.628-2.28A4.515 4.515 0 0 1 26.292 13a2.65 2.65 0 0 1 2.053.858 2.86 2.86 0 0 1 .628 2.28Z'/></svg>",
+ "go": "<svg viewBox='0 0 32 32'><path fill='#00acc1' d='M2 12h4v2H2zm-2 4h6v2H0zm4 4h2v2H4zm16.954-5H14v3h3.239a4.42 4.42 0 0 1-3.531 2 2.65 2.65 0 0 1-2.053-.858 2.86 2.86 0 0 1-.628-2.28A4.515 4.515 0 0 1 15.292 13a2.73 2.73 0 0 1 1.749.584l2.962-1.185A5.6 5.6 0 0 0 15.292 10a7.526 7.526 0 0 0-7.243 6.5 5.614 5.614 0 0 0 5.659 6.5 7.526 7.526 0 0 0 7.243-6.5 6.4 6.4 0 0 0 .003-1.5'/><path fill='#00acc1' d='M26.292 10a7.526 7.526 0 0 0-7.243 6.5 5.614 5.614 0 0 0 5.659 6.5 7.526 7.526 0 0 0 7.243-6.5 5.614 5.614 0 0 0-5.659-6.5m2.681 6.137A4.515 4.515 0 0 1 24.708 20a2.65 2.65 0 0 1-2.053-.858 2.86 2.86 0 0 1-.628-2.28A4.515 4.515 0 0 1 26.292 13a2.65 2.65 0 0 1 2.053.858 2.86 2.86 0 0 1 .628 2.28Z'/></svg>",
+ "go_gopher": "<svg viewBox='0 0 24 24'><g fill='#4dd0e1'><path d='M10.575 1.695c-2.634 0-4.756 2.453-4.756 5.502v4.6l-.027-.003v4.71c0 3.05 2.122 5.502 4.756 5.502h2.287c2.634 0 4.756-2.453 4.756-5.502v-4.6l.027.003v-4.71c0-3.049-2.122-5.502-4.756-5.502z'/><rect width='2.289' height='3.335' x='-1.177' y='6.093' ry='1.125' transform='matrix(.48489 -.87457 .85979 .51065 0 0)'/><rect width='2.297' height='3.39' x='10.261' y='-15.076' ry='1.143' transform='matrix(.44646 .8948 -.89204 .45195 0 0)'/></g><g data-mit-no-recolor='true' transform='translate(.282 -.134)'><circle cx='9.267' cy='5.13' r='2.054' fill='#fafafa' stroke='#616161' stroke-width='.1'/><circle cx='14.214' cy='5.116' r='2.054' fill='#fafafa' stroke='#616161' stroke-width='.1'/><ellipse cx='8.039' cy='5.051' fill='#212121' rx='.792' ry='.901'/><path fill='#fafafa' stroke='#fafafa' stroke-width='.155' d='m11.792 9.556.763.138a.403.689 0 0 1 .008.138.403.689 0 0 1-.403.69.403.689 0 0 1-.403-.69.403.689 0 0 1 .035-.276z'/><ellipse cx='8.51' cy='5.365' fill='#fafafa' rx='.138' ry='.166'/><ellipse cx='12.945' cy='5.189' fill='#212121' rx='.792' ry='.901'/><ellipse cx='13.414' cy='5.446' fill='#fafafa' rx='.138' ry='.166'/><ellipse cx='-12.982' cy='-3.409' fill='#ffe0b2' rx='.708' ry='1.026' transform='rotate(-129.403)'/><path fill='#fafafa' stroke='#fafafa' stroke-width='.153' d='m11.772 9.553-.757.135a.4.672 0 0 0-.008.134.4.672 0 0 0 .4.673.4.672 0 0 0 .4-.672.4.672 0 0 0-.035-.27z'/><g fill='#ffe0b2'><ellipse cx='1.841' cy='-21.563' rx='.707' ry='1.026' transform='rotate(-50.597)scale(1 -1)'/><ellipse cx='-17.281' cy='-21.784' rx='.864' ry='1.27' transform='matrix(.3054 -.95222 -.97065 -.24051 0 0)'/><ellipse cx='22.885' cy='2.587' rx='.864' ry='1.27' transform='matrix(.22652 .97401 .95652 -.29167 0 0)'/><path stroke='#546e7a' stroke-width='.1' d='M10.708 8.392a.594.594 0 0 0-.594.597v.115c0 .331.265.598.594.598h.386a.973.772 0 0 1 .697-.235.973.772 0 0 1 .698.235h.334c.33 0 .595-.267.595-.598V8.99a.595.595 0 0 0-.595-.597h-2.115z'/></g><ellipse cx='11.734' cy='8.203' fill='#212121' stroke='#fafafa' stroke-width='.162' rx='1.208' ry='.68'/></g></svg>",
+ "godot-assets": "<svg viewBox='0 0 32 32'><path fill='#66bb6a' d='m31.75 10.745-2.5-4.33a.5.5 0 0 0-.683-.183l-2.793 1.612A10 10 0 0 0 24 6.838V2.5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0-.5.5V6h-4V2.5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0-.5.5v4.338a10 10 0 0 0-1.725.97l-2.73-1.576a.5.5 0 0 0-.683.183l-2.5 4.33a.5.5 0 0 0 .183.683L2.6 12.614A10 10 0 0 0 2 16v3l5 1 1 4h4l1-4h6l1 4h4l1-4 5-1v-3a10 10 0 0 0-.58-3.332l2.147-1.24a.5.5 0 0 0 .183-.683M9 18a3 3 0 1 1 3-3 3 3 0 0 1-3 3m9-2h-4v-2h4Zm5 2a3 3 0 1 1 3-3 3 3 0 0 1-3 3'/><path fill='#66bb6a' d='m26 22-1 4h-6l-1-4h-4l-1 4H7l-1-4-4-1v3a4 4 0 0 0 4 4h20a4 4 0 0 0 4-4v-3Z'/></svg>",
+ "godot": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='m26 22-1 4h-6l-1-4h-4l-1 4H7l-1-4-4-1v3a4 4 0 0 0 4 4h20a4 4 0 0 0 4-4v-3Z'/><path fill='#42a5f5' d='m31.75 10.745-2.5-4.33a.5.5 0 0 0-.683-.183l-2.793 1.612A10 10 0 0 0 24 6.838V2.5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0-.5.5V6h-4V2.5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0-.5.5v4.338a10 10 0 0 0-1.725.97l-2.73-1.576a.5.5 0 0 0-.683.183l-2.5 4.33a.5.5 0 0 0 .183.683L2.6 12.614A10 10 0 0 0 2 16v3l5 1 1 4h4l1-4h6l1 4h4l1-4 5-1v-3a10 10 0 0 0-.58-3.332l2.147-1.24a.5.5 0 0 0 .183-.683M9 18a3 3 0 1 1 3-3 3 3 0 0 1-3 3m9-2h-4v-2h4Zm5 2a3 3 0 1 1 3-3 3 3 0 0 1-3 3'/></svg>",
+ "gradle": "<svg viewBox='0 0 32 32'><path fill='#0097a7' d='M16 10v2h6c-2 0-3-2-6-2'/><path fill='#0097a7' d='M26 4h-2a4 4 0 0 0-4 4h4a1 1 0 0 1 2 0v4H16v-2h-5.317A2.683 2.683 0 0 0 8 12.683v2.634A2.683 2.683 0 0 0 10.683 18H16v2h-5.98A4.02 4.02 0 0 1 6 16v-2c-2 0-4 4-4 8 0 5 1 6 2 6h4v-4h4v4h4v-4h4v4h4v-6a2 2 0 0 0 2-2v-2a4 4 0 0 0 4-4V8a4 4 0 0 0-4-4m-4 12h-2a2 2 0 0 1-2-2h6a2 2 0 0 1-2 2'/></svg>",
+ "grafana-alloy": "<svg viewBox='0 0 24 24'><path fill='#ff6f00' d='M20.173 8.483a14.7 14.7 0 0 0-3.287-3.92l-.025-.02a13 13 0 0 0-.784-.603C14.28 2.67 12.317 2 10.394 2 7.953 2 5.741 3.302 4.167 5.668 1.952 8.994 1.48 13.656 2.99 17.269c1.134 2.712 4.077 4.47 7.873 4.706q.415.024.833.025c1.757 0 3.531-.338 5.073-.975 1.962-.81 3.463-2.048 4.342-3.583 1.304-2.28.945-5.712-.938-8.96zm-8.871.508c.863 0 1.723.354 2.341 1.048.558.625.839 1.43.79 2.266a3.1 3.1 0 0 1-1.007 2.128l-.072.064a3.14 3.14 0 0 1-3.725.28 4.4 4.4 0 0 1-.745-.67 3 3 0 0 1-.17-.214 3.1 3.1 0 0 1-.416-.874l-.016-.057-.002-.007c-.277-1.08.04-2.339.905-3.138l.066-.061a3.12 3.12 0 0 1 2.05-.764zm-.908-5.84c1.683 0 3.418.598 5.018 1.73q.367.26.72.553l.386.348c2.95 2.744 3.873 5.42 3.642 8.189-.151 1.818-1.31 3.27-2.97 4.394-1.58 1.07-4 1.377-5.727 1.192-1.697-.182-3.456-.866-4.592-2.404-.939-1.273-1.218-2.64-1.091-4.107.127-1.459.712-2.823 1.662-3.772.533-.533 1.442-1.202 2.894-1.324-.68.156-1.33.48-1.887.976a4.29 4.29 0 0 0-1.378 3.869c.093.636.33 1.248.713 1.778a4.3 4.3 0 0 0 1.252 1.191c1.66 1.121 3.728 1.033 5.747-.306 1.1-.73 1.844-1.994 2.04-3.471.238-1.788-.336-3.623-1.575-5.033-1.347-1.533-3.212-2.44-5.116-2.49-1.77-.046-3.409.652-4.737 2.017-.407.417-.777.87-1.107 1.349q.358-.801.838-1.523C6.48 4.272 8.35 3.152 10.394 3.152z'/></svg>",
+ "grain": "<svg viewBox='0 0 32 32'><path fill='#ffa726' d='m16 2-4.97 9.782L16 16l5.094-4.218Z'/><path fill='#f57f17' d='M2 6.848V18.64L16 30l7.228-5.844Z'/><path fill='#f57c00' d='M30 18.645V6.71L17.334 17.235l7.284 5.808Z'/></svg>",
+ "graphcool": "<svg preserveAspectRatio='xMidYMid' viewBox='0 0 300 300'><path fill='#4caf50' stroke='#4caf50' stroke-width='7.884' d='M246.886 107.727c-12.237-6.892-27.616 2.1-30.081 3.646l-52.834 29.965c-7.8-6.196-18.914-5.933-26.412.625-7.499 6.558-9.24 17.537-4.14 26.094 5.102 8.556 15.588 12.246 24.923 8.768s14.852-13.129 13.111-22.937l52.688-29.9.321-.196c3.464-2.188 11.5-5.462 15.256-3.34 2.706 1.524 4.252 6.629 4.376 14.148h-.066v66.092a17.31 17.31 0 0 1-8.635 14.95l-75.739 43.755a17.31 17.31 0 0 1-17.261 0l-75.74-43.756a17.31 17.31 0 0 1-8.634-14.95V113.22c.01-6.165 3.3-11.86 8.634-14.95l68.549-39.562c6.522 7.482 17.451 9.25 26 4.206s12.283-15.468 8.886-24.794-12.962-14.904-22.751-13.27c-9.79 1.636-17.022 10.02-17.204 19.944L59.397 85.632a31.93 31.93 0 0 0-15.978 27.588v87.454a31.93 31.93 0 0 0 15.927 27.602l75.74 43.755a31.93 31.93 0 0 0 31.846 0l75.74-43.755a31.93 31.93 0 0 0 15.927-27.58V137.12h.05c.373-14.913-3.616-24.794-11.762-29.389z'/></svg>",
+ "graphql": "<svg viewBox='0 0 32 32'><path fill='#ec407a' d='M6 20h20v2H6z'/><circle cx='7' cy='21' r='3' fill='#ec407a'/><circle cx='16' cy='27' r='3' fill='#ec407a'/><circle cx='25' cy='21' r='3' fill='#ec407a'/><path fill='#ec407a' d='M6 10h20v2H6z'/><circle cx='7' cy='11' r='3' fill='#ec407a'/><circle cx='16' cy='5' r='3' fill='#ec407a'/><circle cx='25' cy='11' r='3' fill='#ec407a'/><path fill='#ec407a' d='M6 12h2v10H6zm18-2h2v12h-2z'/><path fill='#ec407a' d='m5.014 19.41 11.674 6.866L15.674 28 4 21.134z'/><path fill='#ec407a' d='M26.688 21.724 15.014 28.59 14 26.866 25.674 20zM5.124 10.382l11.415-7.29 1.077 1.686L6.2 12.068z'/><path fill='#ec407a' d='m25.798 12.067-11.415-7.29 1.077-1.685 11.415 7.29zM6.2 19.932l11.416 7.29-1.077 1.686-11.415-7.29z'/><path fill='#ec407a' d='m26.875 21.619-11.415 7.29-1.077-1.687 11.415-7.289zM5.877 22.6 16.04 3.686l1.762.946L7.638 23.546z'/><path fill='#ec407a' d='M24.361 23.545 14.197 4.633l1.761-.947 10.165 18.913z'/></svg>",
+ "gridsome": "<svg viewBox='0 0 32 32'><circle cx='16' cy='16' r='2' fill='#00bfa5'/><path fill='#00bfa5' d='M27.96 14H22v4h3.798A9.998 9.998 0 1 1 18 6.202V2.159A13.994 13.994 0 1 0 30 16v-.02A2.02 2.02 0 0 0 27.96 14'/></svg>",
+ "groovy": "<svg viewBox='0 0 32 32'><path fill='#26c6da' d='M19.322 2a6.5 6.5 0 0 1 4.352 1.419 4.55 4.55 0 0 1 1.685 3.662 5.82 5.82 0 0 1-1.886 4.275 6.04 6.04 0 0 1-4.34 1.846 4.15 4.15 0 0 1-2.385-.649 1.91 1.91 0 0 1-.936-1.603 1.6 1.6 0 0 1 .356-1.024 1.1 1.1 0 0 1 .861-.447q.469 0 .468.504a.79.79 0 0 0 .358.693 1.43 1.43 0 0 0 .826.245 3.1 3.1 0 0 0 2.39-1.573 5.66 5.66 0 0 0 1.154-3.39 2.64 2.64 0 0 0-.891-2.064 3.28 3.28 0 0 0-2.293-.812 6.18 6.18 0 0 0-4.086 1.736 12.9 12.9 0 0 0-3.215 4.557 13.4 13.4 0 0 0-1.233 5.36 5.86 5.86 0 0 0 1.091 3.723 3.53 3.53 0 0 0 2.905 1.372q3.058 0 5.848-4.002l2.935-.388q.546-.07.545.246a8 8 0 0 1-.423 1.24q-.421 1.097-1.152 3.668A12.7 12.7 0 0 0 26 17.72v1.66a14.2 14.2 0 0 1-4.055 2.57 10.38 10.38 0 0 1-2.764 5.931 6.7 6.7 0 0 1-4.806 2.11 3.3 3.3 0 0 1-2.012-.55 1.8 1.8 0 0 1-.718-1.514q0-2.685 5.634-5.212.532-1.766 1.152-3.507a8.6 8.6 0 0 1-2.853 2.323 7.4 7.4 0 0 1-3.48 1.01 5.46 5.46 0 0 1-4.366-2.093A8.1 8.1 0 0 1 6 15.122a11.6 11.6 0 0 1 1.966-6.426 14.7 14.7 0 0 1 5.162-4.862A12.44 12.44 0 0 1 19.322 2m-2.407 22.17q-4.055 1.875-4.054 3.695a.87.87 0 0 0 .999.97q1.964 0 3.055-4.665'/></svg>",
+ "grunt": "<svg viewBox='0 0 300 300'><path fill='#f9a825' d='M61.045 79.22s4.718 18.193 5.39 24.259c.675 6.064-12.128 19.54-12.128 26.279s2.691 18.87 17.518 24.935 16.847 21.563 16.847 31c0 9.433-5.39 27.626-5.39 27.626s-3.368 45.148 14.825 53.234 14.151 8.086 18.192 8.086c4.037 0 33.688 12.136 51.21 7.416 17.523-4.711 22.234-10.105 22.234-10.105s17.523 1.348 22.908-14.151c5.394-15.492 8.757-47.84 6.735-62.665s-4.72-34.362 7.408-41.105c12.129-6.734 24.931-14.825 22.908-21.56S236.9 116.297 236.9 116.297s-2.697-16.84-.683-20.885c2.023-4.038 14.151-14.152 7.409-21.56-6.734-7.409-18.871-7.409-18.871-7.409l-15.491 1.34s-26.27-23.59-60.641-22.242c-34.346 1.348-55.914 22.234-55.914 22.234s-11.454-4.044-21.562 0c-10.114 4.045-12.128 0-10.106 11.454z'/><g fill='#e78724'><path d='M122.623 179.552s-13.345 20.404-23.133 34.884c-1.514 2.24 9.788 2.395 9.788 2.395l13.346-37.28zm6.353-42.303s-4.022 10.035-6.226 15.355c-2.392 5.783-11.511 8.66-11.511 8.66s4.728-4.958 4.728-17.519c0-5.262 6.802-7.359 6.802-7.359l6.208.872z'/><path d='M124.909 142.92s-8.242 9.81-23.544 4.317c-15.306-5.493-16.874-25.117-16.874-30.611s29.04 5.494 32.964 7.849 11.378 11.772 7.454 18.445M67.104 38.09s14.126-2.75 23.544 4.707c9.422 7.456 12.165 13.344 12.165 13.344L86.939 68.724s-7.67-3.557-10.026-13.761c-2.353-10.203-.783-14.911-9.81-16.873z'/><path d='M120.79 114.585c-6.084-8.634-16.107-12.4-24.11-13.025-13.646-1.066-20.125 3.758-20.125 3.758s22.349.946 28.79 9.407c11.992 15.746 29.487 17.025 32.284 13.567-3.235.161-6.793-1.442-10.602-5.043-2.38-2.253-4.309-5.924-6.239-8.664zm-37.73 30.528s-24.795-8.525-20.084-17.156c4.71-8.635.962-13.34.962-13.34s-13.847 10.337-10.32 21.324c2.17 6.742 5.808 11.626 25.783 20.984 9.58 17.793 1.978 49.829 2.434 49.977.759.246 13.452-6.274 12.585-36.246-.263-9.209-.995-21.765-11.355-25.555zm32.495-91.666c-10.398 5.283-24.642 12.638-24.642 12.638l5.606 2.804c-.534.868-.793 1.385-.793 1.385l5.748 2.616s14.044 10.176 22.653 16.717c-2.87-8.835-11.298-22.717-11.298-22.717s-2.393-6.378 2.713-13.444zm34.54 215.317c-31.958 0-42.783-16.223-42.783-16.223l4.995 12.161c1.888 5.263 22.027 21.634 37.74 21.634'/><path d='M110.815 241.575s2.94 17.97 14.713 17.97c10.004 0 12.952-15.179 12.952-18.316l-27.666.346zm-19.593-27.1c1.046-2.88 4.709-14.522 5.885-25.116 1.406-12.69.897-31.249.897-31.249s-3.141 12.166-5.896 14.132c-2.746 1.96-7.853 15.302-7.853 15.302s-2.738 19.862-4.037 26.538c3.602.722 6.628-1.4 10.986.393zm-28.43-78.732s6.668-2.356 8.634-16.874c1.96-14.521-2.357-27.079-2.357-27.079s.392 20.014-1.961 23.94c-2.357 3.922-4.317 5.1-7.065 10.204-2.749 5.103 1.176 8.239 2.749 9.81z'/></g><path fill='#ffcc80' d='m70.755 41.524-1.361-.864c-4.015.137 1.362.864 1.362.864z'/><path fill='#f9a825' d='M120.987 49.146s-6.278-1.046-9.419-5.754c-3.14-4.71-12.034-14.13-26.213-14.235-14.176-.107-28.204 3.768-28.204 3.768l12.244 7.73c1.296-.042 3.56-.025 7.305.098 15.366.51 17.732 15.705 17.732 15.705l54.162-7.457'/><path fill='#fafafa' d='M126.216 133.094s-7.326 7.326-18.576 4.97c-11.251-2.354-18.055-7.325-18.578-15.96-.523-8.634 2.357-12.034 2.357-12.034s12.564 3.93 18.583 9.16c6.019 5.238 10.467 13.608 16.223 13.871z'/><path fill='#bdbdbd' d='M117.352 130.48s-6.353 2.902-13.455.537c-9.297-3.1-9.917-12.77-9.917-12.77s-5.702 16.737 10.287 19.959c12.825 2.583 20.41-4.848 20.41-4.848z'/><g fill='#4e342e'><path d='M121.866 45.596c-.684-14.354 11.083-25.787 15.16-26.272.225 2.432-.227 11.919 3.848 14.836 1.187-7.746 5.2-16.29 21.045-21.889-3.394 9-.453 16.54.947 20.034 10.82-9.82 18.11-8.198 18.11-8.198s-7.518 12.973-4.577 21.486c-21.721-1.704-54.427 2.307-54.533.003M75.817 67.459c-5.182-4.3-1.32-8.29-1.729-13.175-.222-2.599-.427-5.052-1.285-6.406-4.77-7.53-16.018-9.815-16.133-9.839l-5.279-1.034 3.495-4.09c.452-.53 14.809-14.225 37.026-7.055 15.541 5.013 27.044 22.007 27.472 22.695l-7.787 3.663c-.09-.145-9.184-16.119-22.513-20.417-10.772-3.478-20.705-.17-24.964 1.998 4.522 1.806 10.434 5.094 14.003 10.72 1.686 2.655 1.965 6.002 2.237 9.24.361 4.288.698 8.336 3.856 10.957l-8.387 2.743zm-13.842 62.765s6.53-8.097 8.464-23.852c.76-6.206-.59-18.446-2.749-23.155.392 11.578-2.55 23.155-2.55 23.155s-1.982 21.497-3.158 23.852z'/><path d='M96.41 59.281c-.004-.016-2.096-9.158-7.894-13.773-8.182-6.507-18.17-3.045-18.267-3.015l-1.447-4.481c.485-.156 11.906-3.754 22.423 3.642 10.582 7.439 11.857 14.181 11.964 14.926l-6.776 2.701zm-13.923 65.136s-3.426-4.904-2.81-10.297c.307-2.664 1.541-5.397 3.745-6.367 4.094-1.793 5.912 2.845 5.912 2.845s-5.764 1.422-6.842 13.822z'/><path d='M120.099 131.893c.143.268.216.417.216.417s-10.685 8.019-21.634-.118c-5.863-4.358-5.308-15.138-3.513-19.71 4.67 1.061 8.63 2.969 11.941 5.197-4.183-3.733-7.846-6.48-7.836-6.545l-4.755-1.349-9.242-3.165c-.119.707-3.49 16.51 6.24 27.274 4.314 4.777 24.533 15.467 34.133-2.812l-5.55.83z'/><path d='M119.095 133.07c6.016 9.155 21.194 3.923 20.933-5.757 0 0-10.142 5.598-19.101-4.708-5.232-6.01-21.996-22.604-44.727-17.086 15.698 3.453 36.48 17.793 42.895 27.562z'/><path d='M74.077 158.905c1.447.836 6.55 6.121 6.553 14.027 0 3.964-.395 23.879-2.357 36.044 2.952 1.918 5.008 2.325 7.946 4.28 1.472-14.09 2.526-34.512 2.034-42.756-.559-9.398-5.591-15.705-10.253-18.394a187 187 0 0 0-4.523-2.498c-7.992-4.318-16.256-8.786-16.256-16.414 0-5.967 2.319-7.833 4.777-9.812l.264-.214c1.628-1.317 3.478-2.81 4.571-5.262 2.985-6.704 3.47-23.78 0-28.116-1.726-2.162-6.364-9.43-5.032-14.307.51-1.866 1.686-3.248 3.593-4.226 2.023-1.036 4.753-1.563 8.116-1.563 6.603 0 13.93 2.007 17.703 3.207 13.502 4.276 29.79 16.61 34.461 20.294l.23.172 2.985 2.08-1.866-3.132c-.115-.198-11.948-19.66-33.113-26.798-4.12-1.39-12.202-3.716-20.153-3.716-4.728 0-8.75.822-11.931 2.45-3.873 1.982-6.496 5.147-7.59 9.144-2.672 9.8 6.036 20.712 6.332 21.066 1.02 1.809 1.447 14.965-.897 20.236-.32.723-1.2 1.439-2.327 2.343-.345.28-.74.576-1.159.888-3.305 2.492-8.305 6.266-8.305 14.957 0 11.89 12.063 18.345 21.749 23.525 1.595.855 3.108 1.66 4.481 2.45z'/><path d='M75.817 67.459c-5.182-4.3-1.32-8.29-1.729-13.175-.222-2.599-.427-5.052-1.285-6.406-4.77-7.53-16.018-9.815-16.133-9.839l-5.279-1.034 3.495-4.09c.452-.53 14.809-14.225 37.026-7.055 15.541 5.013 27.044 22.007 27.472 22.695l-7.787 3.663c-.09-.145-9.184-16.119-22.513-20.417-10.772-3.478-20.705-.17-24.964 1.998 4.522 1.806 10.434 5.094 14.003 10.72 1.686 2.655 1.965 6.002 2.237 9.24.361 4.288.698 8.336 3.856 10.957l-8.387 2.743z'/><path d='m75.649 68.34-.335-.278c-3.843-3.182-3.161-6.331-2.501-9.365.3-1.398.617-2.854.488-4.342-.21-2.5-.41-4.86-1.164-6.052-4.539-7.17-15.516-9.464-15.623-9.489l-6.595-1.299 4.375-5.106c.082-.09 9.3-9.398 24.906-9.398 4.26 0 8.617.707 12.95 2.113 16.216 5.23 27.793 22.85 27.9 23.023l.469.748-9.218 4.342-.37-.584c-.987-1.686-9.727-16.1-22.094-20.088-3.034-.987-6.233-1.48-9.505-1.48-5.92 0-10.723 1.62-13.28 2.697 4.276 1.924 9.448 5.164 12.729 10.336 1.784 2.82 2.072 6.257 2.351 9.58.346 4.135.675 8.025 3.577 10.434l1.094.904-10.147 3.33z'/><path d='M96.41 59.281c-.004-.016-2.096-9.158-7.894-13.773-8.182-6.507-18.17-3.045-18.267-3.015l-1.447-4.481c.485-.156 11.906-3.754 22.423 3.642 10.582 7.439 11.857 14.181 11.964 14.926l-6.776 2.701z'/><path d='m95.842 60.35-.204-.9c-.021-.087-2.072-8.916-7.62-13.326-3.16-2.516-6.972-3.79-11.322-3.79-3.396 0-5.92.814-6.195.904l-.75.255-1.94-5.978.747-.238c.338-.107 3.364-1.053 7.656-1.053 5.657 0 11.002 1.661 15.466 4.802 10.854 7.63 12.178 14.678 12.293 15.459l.099.616-8.206 3.273zM76.696 40.762c4.648 0 8.905 1.43 12.301 4.131 4.926 3.923 7.242 10.846 7.943 13.33l5.337-2.128c-.483-1.885-2.61-7.54-11.512-13.798-4.257-2.993-9.156-4.509-14.562-4.509-2.967 0-5.27.472-6.41.76l.962 2.977a23.3 23.3 0 0 1 5.941-.763m48.339 214.47c-2.32 0-4.686-.84-6.326-2.26-1.256-1.083-5.501-4.984-7.374-9.761-.699-1.787-.543-3.215.472-4.245.699-.704 2.15-1.547 5.094-1.547l2.095-.003c2.5-.003 6.701-.003 10.295-.003h3.445c2.245 0 3.848.485 4.769 1.43 1.184 1.218.806 2.772.477 3.586-.576 1.43-2.96 6.085-7.006 10.402-1.447 1.554-3.552 2.409-5.928 2.409zm2.43-121.628-8.371-.539s2.14 3.773 3.526 8.652c1.565 5.517-1.431 13.584-1.431 13.584s11.162-12.638 6.276-21.7zm-10.452 143.73c-29.797-2.29-40.28-30.446-40.28-36.31l7.781.003h-3.89 3.89c.04 2.54 7.958 26.625 33.096 28.557l-.596 7.762z'/></g><path fill='#fafafa' d='M108.644 226.897c-.465-2.691-.513-5.243.055-7.751-2.265-1.702-6.328-2.335-9.377-3.33-2.5-1.579-15.253 1.611-24.512-9.588-11.602-14.028-9.16-22.406-9.16-22.406-2.976 3.305-5.854 2.039-10.393 17.448s2.13 26.148 14.562 36.928c5.87 5.09 14.176 7.186 21.313 7.992-2.393-2.401 4.087-2.648 8.551-4.317-.361-3.61.239-7.104 2.928-9.925 1.62-1.702 3.708-2.713 5.87-3.527.124-.28.223-.535.338-.806a7 7 0 0 1-.19-.756z'/><path fill='#bdbdbd' d='M99.632 241.805c-.257-.757-.452-1.53-.643-2.305-.206-.847-1.234-3.25.033-.507a10.2 10.2 0 0 1-.696-2.155c-20.129-.026-37.643-13.846-34.255-42.905-2.632-3.499-6.324 7.055-6.324 7.055S47.675 230.25 73.7 241.59c8.675 3.782 16.339 6.494 18.148 5.4 3.765.765 7.17-1.86 8.444-3.588-.238-.53-.452-1.06-.641-1.599z'/><path fill='#4e342e' d='M108.283 222.68c.03-2.454.532-4.83 1.718-7.196.082-.164.179-.32.267-.478-7.745-.066-16.667-.809-24.264-3.848-8.346-3.341-18.79-14.768-20.59-28.194 0 0-15.721 12.135-11.1 38.108 4.333 24.39 24.741 32.342 48.02 33.687.016-.158-2.615-5.592-1.785-8.918-13.213.962-27.998-4.144-35.127-15.886-6.528-10.738-4.695-29.083-2.014-31.772 6.553 20.664 30.111 24.865 44.887 24.504z'/><path fill='#4e342e' d='M110.717 207.887c-4.189 12.292-12.745 22.24-12.745 22.24s11.77-.543 12.419-2.624c.481-1.537 10.627-25.284 14.521-53.11-3.663 6.02-13.837 32.43-14.195 33.483z'/><g fill='#e78724'><path d='M177.394 179.552s13.34 20.404 23.132 34.884c1.515 2.24-9.791 2.395-9.791 2.395zm-6.356-42.303s4.024 10.035 6.224 15.355c2.393 5.783 11.512 8.66 11.512 8.66s-4.728-4.958-4.728-17.519c0-5.262-6.802-7.359-6.802-7.359l-6.2.872z'/><path d='M175.1 142.92s8.241 9.81 23.547 4.317 16.875-25.117 16.875-30.611-29.04 5.494-32.965 7.849-11.38 11.772-7.457 18.445m57.804-104.83s-14.126-2.75-23.546 4.707c-9.423 7.456-12.17 13.344-12.17 13.344l15.87 12.582s7.672-3.556 10.024-13.76c2.351-10.203.78-14.911 9.81-16.873z'/><path d='M179.227 114.585c6.083-8.634 16.103-12.4 24.109-13.025 13.647-1.066 20.122 3.758 20.122 3.758s-22.349.946-28.791 9.407c-11.993 15.746-29.481 17.025-32.279 13.567 3.232.161 6.792-1.442 10.6-5.043 2.375-2.253 4.308-5.924 6.232-8.664zm37.727 30.528s24.795-8.525 20.086-17.157c-4.709-8.642-.964-13.345-.964-13.345s13.85 10.336 10.318 21.321c-2.169 6.743-5.806 11.627-25.782 20.984-9.587 17.794-1.981 49.83-2.433 49.977-.765.247-13.453-6.273-12.59-36.245.264-9.2.996-21.765 11.356-25.556zm-32.492-91.666a5329 5329 0 0 1 24.64 12.638l-5.6 2.804c.535.868.798 1.385.798 1.385l-5.747 2.616S184.509 83.066 175.9 89.607c2.87-8.835 11.29-22.717 11.29-22.717s2.392-6.378-2.714-13.444zm-34.54 215.317c31.953 0 42.778-16.223 42.778-16.223l-4.991 12.161c-1.888 5.263-22.028 21.634-37.739 21.634'/><path d='M189.201 241.575s-2.943 17.97-14.713 17.97c-10.003 0-12.95-15.179-12.95-18.316zm19.592-27.1c-1.046-2.88-4.708-14.522-5.887-25.116-1.411-12.69-.894-31.249-.894-31.249s3.141 12.166 5.884 14.132c2.75 1.96 7.85 15.302 7.85 15.302s2.733 19.862 4.033 26.538c-3.599.722-6.627-1.4-10.985.393zm28.434-78.732s-6.673-2.356-8.634-16.874c-1.962-14.521 2.355-27.079 2.355-27.079s-.394 20.014 1.957 23.94c2.352 3.924 4.317 5.1 7.063 10.204 2.747 5.102-1.184 8.239-2.746 9.81z'/></g><path fill='#ffcc80' d='m229.254 41.524 1.365-.864c4.015.137-1.365.864-1.365.864'/><path fill='#f9a825' d='M179.03 49.146s6.28-1.046 9.418-5.754c3.141-4.71 12.038-14.13 26.216-14.235 14.178-.107 28.204 3.768 28.204 3.768l-12.244 7.73c-1.296-.042-3.56-.025-7.308.098-15.364.51-17.73 15.705-17.73 15.705l-54.154-7.457'/><path fill='#fafafa' d='M173.8 133.094s7.327 7.326 18.575 4.97c11.252-2.354 18.055-7.325 18.578-15.96.522-8.634-2.354-12.034-2.354-12.034s-12.56 3.93-18.577 9.16-10.464 13.608-16.222 13.871z'/><path fill='#bdbdbd' d='M182.664 130.48s6.356 2.902 13.456.537c9.3-3.1 9.918-12.77 9.918-12.77s5.7 16.737-10.29 19.959c-12.822 2.583-20.408-4.848-20.408-4.848z'/><g fill='#4e342e'><path d='M224.197 67.459c5.182-4.3 1.322-8.29 1.731-13.175.22-2.599.425-5.052 1.283-6.406 4.77-7.53 16.02-9.815 16.135-9.839l5.28-1.034-3.495-4.09c-.453-.53-14.81-14.225-37.027-7.055-15.54 5.013-27.044 22.007-27.471 22.695l7.786 3.663c.09-.145 9.185-16.119 22.522-20.417 10.772-3.478 20.705-.17 24.956 1.998-4.523 1.806-10.435 5.094-14.003 10.72-1.686 2.655-1.966 6.002-2.237 9.24-.362 4.288-.707 8.336-3.865 10.957l8.38 2.743zm13.838 62.765s-6.526-8.097-8.46-23.852c-.76-6.206.587-18.446 2.746-23.155-.393 11.578 2.553 23.155 2.553 23.155s1.985 21.497 3.161 23.852'/><path d='M203.607 59.281c.004-.016 2.097-9.158 7.896-13.773 8.18-6.507 18.17-3.045 18.263-3.015l1.455-4.481c-.485-.156-11.901-3.754-22.426 3.642-10.59 7.439-11.857 14.181-11.972 14.926l6.775 2.701zm13.924 65.136s3.425-4.904 2.806-10.297c-.304-2.664-1.538-5.397-3.741-6.367-4.095-1.793-5.91 2.845-5.91 2.845s5.763 1.422 6.845 13.822z'/><path d='M179.918 131.893c-.142.268-.216.417-.216.417s10.68 8.019 21.63-.118c5.866-4.358 5.313-15.138 3.512-19.71-4.666 1.061-8.626 2.969-11.94 5.197 4.186-3.733 7.848-6.48 7.835-6.545l4.759-1.349 9.234-3.165c.115.707 3.486 16.51-6.25 27.274-4.316 4.777-24.536 15.467-34.132-2.812l5.55.83z'/><path d='M180.913 133.07c-6.016 9.155-21.19 3.923-20.927-5.757 0 0 10.139 5.598 19.098-4.708 5.231-6.01 21.997-22.604 44.729-17.086-15.697 3.453-36.482 17.793-42.9 27.562z'/><path d='M225.94 158.905c-1.447.836-6.554 6.121-6.554 14.027-.003 3.964.393 23.879 2.357 36.044-2.952 1.918-5.008 2.325-7.946 4.28-1.472-14.09-2.53-34.512-2.035-42.756.562-9.398 5.592-15.705 10.257-18.394 1.461-.84 2.99-1.667 4.531-2.498 7.992-4.318 16.256-8.786 16.256-16.414 0-5.967-2.322-7.833-4.777-9.812l-.263-.214c-1.628-1.317-3.477-2.81-4.566-5.262-2.985-6.704-3.479-23.78-.009-28.116 1.727-2.162 6.365-9.43 5.033-14.307-.51-1.866-1.686-3.248-3.594-4.226-2.022-1.036-4.752-1.563-8.115-1.563-6.603 0-13.93 2.007-17.704 3.207-13.501 4.276-29.79 16.61-34.469 20.294-.131.098-.214.164-.23.172l-2.993 2.08 1.866-3.132c.116-.198 11.948-19.66 33.113-26.798 4.12-1.39 12.202-3.716 20.153-3.716 4.728 0 8.741.822 11.923 2.45 3.873 1.982 6.496 5.147 7.59 9.144 2.672 9.8-6.027 20.712-6.332 21.066-1.02 1.809-1.447 14.965.897 20.236.32.723 1.208 1.439 2.327 2.343q.53.42 1.159.888c3.305 2.492 8.305 6.266 8.305 14.957 0 11.89-12.055 18.345-21.74 23.525-1.588.855-3.109 1.66-4.474 2.45z'/><path d='M211.559 66.06c-1.918-3.249-6.822-9.52-18.125-15.032-12.613-6.147-27.212-9.267-43.396-9.275v-.003h-.058v.008c-16.182.008-30.785 3.133-43.399 9.275-11.297 5.518-16.198 11.783-18.122 15.031l-.822 1.094 9.677 2.153.31-.386.163-.357c.148-.642 1.447-4.095 12.238-9.357 11.536-5.626 24.989-8.478 39.987-8.486 14.998.006 28.45 2.859 39.986 8.486 10.788 5.262 12.095 8.715 12.243 9.355l.157.356.304.387 9.678-2.155-.822-1.088z'/><path d='M224.197 67.459c5.182-4.3 1.322-8.29 1.731-13.175.22-2.599.425-5.052 1.283-6.406 4.77-7.53 16.02-9.815 16.135-9.839l5.28-1.034-3.495-4.09c-.453-.53-14.81-14.225-37.027-7.055-15.54 5.013-27.044 22.007-27.471 22.695l7.786 3.663c.09-.145 9.185-16.119 22.522-20.417 10.772-3.478 20.705-.17 24.956 1.998-4.523 1.806-10.435 5.094-14.003 10.72-1.686 2.655-1.966 6.002-2.237 9.24-.362 4.288-.707 8.336-3.865 10.957l8.38 2.743z'/><path d='m214.223 65.017 1.09-.908c2.903-2.408 3.228-6.296 3.578-10.43.282-3.33.572-6.767 2.354-9.587 3.274-5.172 8.458-8.404 12.728-10.336-2.557-1.086-7.36-2.697-13.284-2.697-3.273 0-6.465.493-9.502 1.472-12.367 3.988-21.104 18.394-22.094 20.087l-.37.584-9.218-4.333.469-.748c.11-.173 11.676-17.794 27.895-23.024 4.342-1.406 8.7-2.113 12.959-2.113 15.615 0 24.832 9.308 24.906 9.399l4.375 5.114-6.595 1.291c-.107.017-11.084 2.319-15.623 9.489-.748 1.192-.946 3.552-1.16 6.052-.123 1.488.19 2.935.494 4.341.658 3.034 1.34 6.184-2.5 9.366l-.329.271-10.146-3.33z'/><path d='M203.607 59.281c.004-.016 2.097-9.158 7.896-13.773 8.18-6.507 18.17-3.045 18.263-3.015l1.455-4.481c-.485-.156-11.901-3.754-22.426 3.642-10.59 7.439-11.857 14.181-11.972 14.926l6.775 2.701z'/><path d='m195.968 57.08.09-.615c.116-.778 1.442-7.828 12.29-15.454 4.465-3.136 9.818-4.795 15.467-4.795 4.292 0 7.318.946 7.655 1.053l.749.246-1.94 5.978-.75-.247c-.279-.09-2.803-.904-6.19-.904-4.35 0-8.158 1.283-11.315 3.799-5.542 4.415-7.598 13.238-7.614 13.329l-.198.904-8.206-3.273zm33.294-15.557.966-2.975c-1.14-.29-3.447-.76-6.414-.76-5.405 0-10.305 1.518-14.562 4.51-8.903 6.257-11.03 11.912-11.513 13.797l5.342 2.127c.699-2.483 3.013-9.408 7.94-13.329 3.404-2.702 7.655-4.13 12.3-4.13 2.673 0 4.827.46 5.946.76zm-60.209 211.304c-4.043-4.323-6.435-8.975-7.013-10.404-.33-.814-.708-2.365.479-3.585.924-.943 2.526-1.427 4.766-1.427h3.453c3.594 0 7.795 0 10.295.008l2.097.008c2.943 0 4.399.847 5.09 1.546 1.02 1.028 1.175 2.459.476 4.251-1.874 4.777-6.117 8.683-7.375 9.76-1.645 1.423-4.005 2.262-6.323 2.262-2.377 0-4.482-.856-5.929-2.401zm3.5-119.227 8.367-.539s-2.138 3.773-3.523 8.652c-1.57 5.517 1.43 13.584 1.43 13.584s-11.166-12.639-6.281-21.7z'/></g><path fill='#e78724' d='M150.004 103.715c-5.336 0-12.293-.146-17.512-3.898 0 0 6.834 11.982 17.503 11.982l.008-1.516.01 1.516c10.67 0 17.504-11.98 17.504-11.98-5.22 3.75-12.182 3.897-17.513 3.897z'/><path fill='#4e342e' d='M164.698 103.657c-5.353 3.24-10.132 4.362-14.691 4.462-4.56-.1-9.339-1.222-14.692-4.461 0 0 7.795 9.513 14.653 9.587v.009h.038c.012 0 .024.008.038.008 6.858-.074 14.661-9.58 14.661-9.58zm18.3 173.686-.598-7.758c25.137-1.932 33.055-26.019 33.096-28.554h7.787c.008 5.863-10.484 34.022-40.274 36.311z'/><path fill='#fafafa' d='M191.372 226.897c.467-2.691.512-5.243-.055-7.751 2.268-1.702 6.328-2.335 9.377-3.33 2.493-1.579 15.255 1.611 24.512-9.588 11.596-14.028 9.151-22.406 9.151-22.406 2.977 3.305 5.856 2.039 10.392 17.448 4.539 15.41-2.134 26.148-14.564 36.928-5.87 5.09-14.174 7.186-21.314 7.992 2.392-2.401-4.079-2.648-8.544-4.317.362-3.61-.238-7.104-2.927-9.925-1.612-1.702-3.7-2.713-5.863-3.527-.123-.28-.222-.535-.337-.806.074-.247.14-.493.19-.756z'/><path fill='#bdbdbd' d='M200.376 241.805c.261-.757.452-1.53.644-2.305.21-.847 1.238-3.25-.03-.507a10.2 10.2 0 0 0 .695-2.155c20.13-.026 37.643-13.846 34.257-42.905 2.635-3.499 6.328 7.055 6.328 7.055s10.078 29.262-15.95 40.603c-8.672 3.782-16.335 6.494-18.144 5.4-3.77.765-7.17-1.86-8.442-3.588.238-.53.46-1.06.65-1.599z'/><g fill='#4e342e'><path d='M191.734 222.68c-.028-2.454-.532-4.83-1.715-7.196-.083-.164-.18-.32-.268-.478 7.746-.066 16.668-.809 24.265-3.848 8.35-3.341 18.795-14.768 20.59-28.194 0 0 15.726 12.135 11.105 38.108-4.341 24.39-24.747 32.342-48.023 33.687-.013-.158 2.619-5.592 1.787-8.918 13.213.962 27.998-4.144 35.135-15.886 6.529-10.738 4.695-29.083 2.014-31.772-6.545 20.664-30.11 24.865-44.887 24.504z'/><path d='M201.132 228.797c-2.78-3.006-7.893-6.59-19.682-6.59l-31.394-.019h-.074c-9.045.007-31.402.02-31.402.02-11.783 0-16.898 3.584-19.677 6.589-4.054 4.374-4.687 10.461-1.883 18.069 7.45 20.249 17.925 40.821 52.97 40.85v.003h.041c.017 0 .025.008.041.008 35.045-.024 45.52-20.597 52.97-40.85 2.804-7.606 2.171-13.69-1.883-18.065zm-5.456 16.649c-3.55 10.168-13.09 34.78-45.669 34.823-32.58-.043-42.117-24.654-45.668-34.823-1.963-5.64-1.814-9.816.46-12.41 1.555-1.772 5.123-3.889 13.255-3.889l8.497-.002c6.268-.006 14.138-.013 23.459-.017 9.316.004 17.19.01 23.457.017l8.493.003c8.135 0 11.703 2.113 13.257 3.88 2.272 2.591 2.423 6.768.459 12.409z'/><path d='M189.292 207.887c4.188 12.292 12.743 22.24 12.743 22.24s-11.766-.543-12.416-2.624c-.477-1.537-10.624-25.284-14.513-53.11 3.667 6.02 13.839 32.43 14.2 33.483z'/></g></svg>",
+ "gulp": "<svg viewBox='0 0 32 32'><path fill='#e53935' d='M17.5 12V7.75l3.4-2.55a1.5 1.5 0 0 0-1.8-2.4l-4.6 3.45V12H8v2h2l1.38 16h9.255L22 14h2v-2Z'/></svg>",
+ "h": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M18.5 11a5.49 5.49 0 0 0-4.5 2.344V4H8v24h6V17a2 2 0 0 1 4 0v11h6V16.5a5.5 5.5 0 0 0-5.5-5.5'/></svg>",
+ "hack": "<svg viewBox='0 0 32 32'><path fill='#607d8b' d='m14 9-8 8V9l8-8zm12 12L16 31v-8l10-10z'/><path fill='#ffa000' d='m6 20 8-8v8'/><path fill='#607d8b' d='m6 30 8-8H6'/><path fill='#eceff1' d='m16 20 10-10H16'/></svg>",
+ "hadolint": "<svg viewBox='0 0 1024 1024'><path fill='#1976d2' d='M128 704h768c35.456 0 64 28.544 64 64v128c0 35.456-28.544 64-64 64H128c-35.456 0-64-28.544-64-64V768c0-35.456 28.544-64 64-64'/><path fill='#e0e0e0' d='M160 768h704c17.728 0 32 14.272 32 32s-14.272 32-32 32H160c-17.728 0-32-14.272-32-32s14.272-32 32-32'/><path fill='#1a237e' d='M160 64c-53.02 0-96 42.98-96 96v320c0 160 128 352 288 352h320c160 0 288-256 288-512V128c0-35.346-28.654-64-64-64s-64 28.654-64 64 28.654 64 64 64v128h-64c-64-64-128-128-192-128v-32c0-53.02-42.98-96-96-96s-96 42.98-96 96v32H256v-32c0-53.02-42.98-96-96-96'/><path fill='#fafafa' d='M224 320c-53.02 0-96 42.98-96 96s42.98 96 96 96 96-42.98 96-96-42.98-96-96-96m256 0c-53.02 0-96 42.98-96 96s42.98 96 96 96 96-42.98 96-96-42.98-96-96-96'/><path d='M224 384a32 32 0 0 0-32 32 32 32 0 0 0 32 32 32 32 0 0 0 32-32 32 32 0 0 0-32-32m256 0a32 32 0 0 0-32 32 32 32 0 0 0 32 32 32 32 0 0 0 32-32 32 32 0 0 0-32-32'/><path fill='#ad1457' d='M320 512h64c17.728 0 32 14.272 32 32s-14.272 32-32 32h-64c-17.728 0-32-14.272-32-32s14.272-32 32-32'/><path fill='#757575' d='M352 592c-8.864 0-16 7.136-16 16v6.762l-61.281 35.383A15.964 15.964 0 0 0 268.86 672a15.966 15.966 0 0 0 21.858 5.855L352 642.475l61.281 35.38A15.966 15.966 0 0 0 435.14 672a15.964 15.964 0 0 0-5.858-21.855L368 614.762V608c0-8.864-7.136-16-16-16m-80.422-368.008c-2.578.07-5.18.768-7.578 2.153A15.964 15.964 0 0 0 258.145 248l32 55.426A15.964 15.964 0 0 0 312 309.28a15.964 15.964 0 0 0 5.855-21.855l-32-55.426a15.96 15.96 0 0 0-14.277-8.008zm160.844 0A15.96 15.96 0 0 0 418.145 232l-32 55.426A15.964 15.964 0 0 0 392 309.28a15.964 15.964 0 0 0 21.855-5.855l32-55.426A15.964 15.964 0 0 0 440 226.145a16 16 0 0 0-7.578-2.153M160 128c-17.728 0-32 14.272-32 32v64c0 17.728 14.272 32 32 32s32-14.272 32-32v-64c0-17.728-14.272-32-32-32m384 0c-17.728 0-32 14.272-32 32v64c0 17.728 14.272 32 32 32s32-14.272 32-32v-64c0-17.728-14.272-32-32-32'/></svg>",
+ "haml": "<svg viewBox='0 0 300 300'><path fill='#f4511e' d='M75.766 33.97c-12.149-.304-27.153 6.049-27.153 6.049l49.613 100.64-26.308 112.47c24.012 20.305 50.496 10.593 50.496 10.593l12.187-87.03c1.54 1.458 3.172 2.794 4.818 4.028 5.289 3.746 11.018 6.609 16.746 8.813 5.73 2.203 11.68 3.966 17.63 5.288 3.967.882 7.711 1.543 11.677 1.984-1.763 3.966-2.864 8.152-2.643 12.78 0 .44.22.88.661 1.1h.22c4.186 2.204 8.593 3.97 13.44 5.071 4.628.881 9.697 1.323 14.545.662 5.068-.661 10.136-2.645 14.103-5.95s6.831-7.714 8.594-12.34l.22-.221v-.219l.663-5.949v-.222c2.203-1.322 4.406-2.644 6.61-4.406 2.644-2.204 5.068-4.629 6.83-7.714 1.764-3.085 2.865-6.831 2.644-10.577-.22-3.525-1.544-7.049-3.086-10.134-1.543-3.085-3.525-5.951-5.728-8.596-4.408-5.068-9.696-9.253-15.425-12.559-5.51-3.525-11.68-6.392-17.85-8.375l-2.424-.662-1.98-.66c-1.322-.44-2.426-1.101-3.527-1.542-2.204-1.322-3.748-2.645-4.85-4.408-2.203-3.305-2.423-8.371-1.321-13.66.22-1.322.662-2.645 1.103-3.967s.88-2.645 1.321-4.188a27 27 0 0 0 .603-4.44l29.451-25.77c-2.295-8.474-27.722-17.303-27.722-17.303l-75.57 64.269-43.61-82.277c-1.566-.353-3.245-.532-4.98-.575zm108.6 73.763c-.452 2.355-.652 4.836-.652 7.32.22 3.746 1.323 7.936 3.746 11.462s5.728 6.169 9.034 7.711a30 30 0 0 0 5.07 1.984l2.645.66 2.202.44c5.73 1.543 11.237 3.746 16.305 6.611s9.916 6.39 13.883 10.357 7.052 9.035 7.493 14.103c.22 3.526-.222 6.17-1.324 8.373-1.101 2.424-2.864 4.627-4.847 6.61-.881.882-1.983 1.762-2.864 2.423q0-2.974-.66-5.948c-.22-1.102-.442-1.983-.882-3.085-.44-.881-.88-1.982-1.982-2.643-.22 0-.443-.001-.443.219-1.322 3.305-3.525 6.171-5.948 8.154s-5.51 2.864-8.594 3.085c-3.085.22-6.392-.44-9.697-1.322-3.306-.881-6.609-1.985-9.914-3.086l-.443-.221c-.661-.22-1.539.002-1.98.443-1.762 2.204-3.086 4.184-4.628 6.388a59 59 0 0 0-2.864 5.07c-3.967-1.543-7.715-2.866-11.68-4.408-5.51-2.204-11.02-4.406-16.087-7.05-5.289-2.424-10.354-5.29-14.761-8.596-3.178-2.383-6.114-4.886-8.255-7.747l2.424-17.318z'/></svg>",
+ "handlebars": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='M12.023 12a4 4 0 0 0-3.94 3.182 1 1 0 0 1-.972.818H5.229C4.446 16 4 15.552 4 15v-1H3a1 1 0 0 0-1 1v1c0 3.866 3.134 6 7 6 3.425 0 6.275-1.675 6.881-4.745.545-2.764-1.041-5.24-3.858-5.255'/><path fill='#ff7043' d='M29 14h-1v1c0 .552-.446 1-1.229 1H24.89a1 1 0 0 1-.973-.818A4 4 0 0 0 19.977 12c-2.817.016-4.403 2.491-3.858 5.255C16.725 20.325 19.575 22 23 22c3.866 0 7-2.134 7-6v-1a1 1 0 0 0-1-1'/></svg>",
+ "hardhat": "<svg viewBox='0 0 24 24'><path fill='#ffd600' d='M9.87 12.15 9 6.46a9.9 9.9 0 0 1 6 0l-.87 5.69c-.07.49-.5.85-1 .85h-2.27a1 1 0 0 1-.99-.85M22 16c0-.79-.47-1.5-1.2-1.83A9.08 9.08 0 0 0 17 8.5l-1.76 4.84c-.14.4-.52.66-.94.66H9.7c-.42 0-.8-.26-.94-.66L7 8.5a9.1 9.1 0 0 0-3.8 5.66C2.47 14.5 2 15.2 2 16l6.45 1.84c.36.1.73.16 1.1.16h4.88c.37 0 .74-.06 1.1-.16z'/></svg>",
+ "harmonix": "<svg viewBox='0 0 32 32'><path fill='#536dfe' d='M27 13 16 2 5 13l8 8-6 6 3 3 6-6 6 6 3-3-6-6zm-17 0 6-6 6 6-6 6z'/></svg>",
+ "haskell": "<svg viewBox='0 0 300 300'><g stroke-width='2.422'><path fill='#ef5350' d='m23.928 240.5 59.94-89.852-59.94-89.855h44.955l59.94 89.855-59.94 89.852z'/><path fill='#ffa726' d='m83.869 240.5 59.94-89.852-59.94-89.855h44.955l119.88 179.71h-44.95l-37.46-56.156-37.468 56.156z'/><path fill='#ffee58' d='m228.72 188.08-19.98-29.953h69.93v29.956h-49.95zm-29.97-44.924-19.98-29.953h99.901v29.953z'/></g></svg>",
+ "haxe": "<svg viewBox='0 0 210 210'><path fill='#fb8c00' d='m41.559 104.988 63.43-63.43 63.432 63.43-63.431 63.431z'/><path fill='#ffb300' d='M41.578 105.037 29.973 61.726 18.368 18.415l43.31 11.605 43.312 11.605-31.706 31.706z'/><path fill='#ffca28' d='M104.735 41.555 61.545 30.01 18.367 18.413l22.927.185 23.228.294 20.263 11.36z'/><path fill='#ffea00' d='m18.368 18.417 11.597 43.177 11.544 43.19-11.303-19.948-11.36-20.263-.294-23.229z'/><path fill='#ef6c00' d='m104.999 41.579 43.31-11.605 43.312-11.605-11.605 43.311-11.605 43.311-31.706-31.706z'/><path fill='#e64a19' d='m168.49 104.735 11.545-43.19 11.598-43.177-.185 22.927-.294 23.228-11.36 20.264z'/><path fill='#ffca28' d='m191.628 18.365-43.176 11.597-43.19 11.544 19.948-11.303 20.263-11.36 23.228-.293z'/><path fill='#ef6c00' d='m168.419 104.987 11.605 43.311 11.605 43.311-43.311-11.605-43.311-11.605 31.706-31.706z'/><path fill='#fb8c00' d='m105.261 168.47 43.19 11.544 43.177 11.597-22.927-.185-23.229-.294-20.263-11.36z'/><path fill='#e64a19' d='m191.631 191.617-11.597-43.177-11.545-43.19 11.304 19.948 11.36 20.263.293 23.229z'/><path fill='#ffa000' d='m104.99 168.422-43.31 11.605-43.311 11.605 11.605-43.311 11.605-43.311 31.706 31.706z'/><path fill='#ffea00' d='m41.51 105.27-11.545 43.19-11.597 43.176.185-22.927.294-23.228 11.36-20.264z'/><path fill='#ef6c00' d='m18.368 191.63 43.176-11.598 43.19-11.544-19.947 11.303-20.264 11.36-23.228.293z'/></svg>",
+ "hcl": "<svg viewBox='0 0 32 32'><path fill='#eceff1' d='M18 1.2V14h-4v-4l-4 2v16.37l4 2.43V18h4v4l4-2V3.63z'/><path fill='#eceff1' d='M14 1.2 2 8.49v15.02l4 2.43v-15.2l8-4.86zm12 4.86v15.2l-8 4.86v4.68l12-7.29V8.49z'/></svg>",
+ "hcl_light": "<svg viewBox='0 0 32 32'><path fill='#455a64' d='M18 1.2V14h-4v-4l-4 2v16.37l4 2.43V18h4v4l4-2V3.63z'/><path fill='#455a64' d='M14 1.2 2 8.49v15.02l4 2.43v-15.2l8-4.86zm12 4.86v15.2l-8 4.86v4.68l12-7.29V8.49z'/></svg>",
+ "helm": "<svg data-name='Layer 1' viewBox='0 0 300 300'><path fill='#00acc1' d='M148.014 286.552c-1.986-1.376-4.331-5.94-5.67-11.019-1.19-4.517-1.64-16.429-.845-22.27.297-2.175.467-4.026.374-4.116-.088-.092-1.934-.38-4.102-.64-12.481-1.506-25.802-5.747-35.943-11.447-2.15-1.21-4.167-2.199-4.477-2.199-.312 0-1.407 1.62-2.436 3.6-4.777 9.198-13.122 18.332-19.465 21.305-2.315 1.086-5.402 1.35-6.829.585-2.152-1.152-2.79-5.72-1.503-10.777.879-3.463 4.416-10.749 7.283-15.005 1.267-1.88 3.768-5.068 5.562-7.086l3.254-3.667-1.697-1.623c-4.472-4.273-12.874-14.16-12.874-15.152 0-.354 11.463-8.317 12.298-8.543.424-.114 1.451.901 2.843 2.814 3.012 4.14 11.067 12.105 15.807 15.627 36.132 26.861 87.06 20.116 114.48-15.165 1.456-1.873 2.874-3.408 3.15-3.408.55 0 11.897 7.695 12.29 8.332.356.574-4.603 7-8.838 11.459-2.011 2.117-3.656 4.021-3.651 4.23 0 .211 1.582 2.045 3.508 4.078 6.121 6.453 12.145 16.528 13.818 23.109 1.284 5.056.646 9.625-1.504 10.777-.466.25-1.697.45-2.74.448-6.595-.015-17.532-10.605-24.022-23.26-1.253-2.44-2.46-4.437-2.687-4.437s-1.5.7-2.83 1.55c-9.43 6.044-23.51 11.257-35.69 13.213-2.59.418-4.922.924-5.182 1.124-.336.261-.294 1.482.15 4.22.791 4.893.486 16.505-.556 21.201-1.078 4.848-2.73 8.605-4.771 10.834-2.344 2.564-4.166 2.93-6.508 1.308zm-129.646-98.84c-.116-.308-.163-16.387-.099-35.734l.112-35.176h18.437l.116 12.949c.088 9.689.255 13.037.66 13.294.299.19 4.914.348 10.256.352 7.565.004 9.82-.123 10.199-.58.323-.392.525-4.815.602-13.301l.114-12.714h18.437v71.231H58.766l-.117-13.593c-.077-9.135-.275-13.785-.602-14.18-.379-.458-2.645-.587-10.256-.587s-9.876.13-10.256.587c-.326.395-.524 5.045-.603 14.18l-.116 13.593-9.12.119c-7.057.092-9.167-.007-9.33-.44zm74.624-.004c-.117-.306-.16-16.383-.097-35.73l.112-35.176h45.654l.121 7.798.119 7.798-13.508.116-13.51.117-.123 5.241c-.092 3.898.02 5.334.44 5.6.308.198 5.556.36 11.656.365l11.094.006-.119 7.805-.12 7.805-11.413.22-11.412.22v12.311l14.046.22 14.048.22v15.39l-23.39.111c-18.476.09-23.431 0-23.598-.44zm61.457 0c-.117-.306-.16-16.383-.097-35.73l.112-35.176h18.437l.22 27.7.22 27.702 13.388.22 13.39.22v15.39l-22.729.113c-17.95.088-22.776-.004-22.94-.44zm58.38-.005c-.114-.303-.158-16.378-.096-35.725l.114-35.176 9.658-.11c5.312-.057 9.876.042 10.142.22.65.442 10.399 26.997 12.551 34.186 2.236 7.48 2.062 7.036 2.57 6.53.238-.238 1.275-3.188 2.307-6.56 2.273-7.425 11.26-33.167 11.854-33.945.325-.427 2.69-.528 10.13-.44l9.707.119v71.231l-8.22.121-8.22.119-.29-1.165c-.607-2.417.206-27.862 1.12-35.132 1.44-11.435 1.223-11.613-2.172-1.76-3.671 10.661-10.968 30.259-11.665 31.33-.598.923-.958.989-5.188.989-3.115 0-4.68-.172-4.975-.55-.543-.695-9.505-25.085-11.954-32.531-1.728-5.255-2.666-7.145-2.636-5.307.007.44.411 4.06.897 8.05.908 7.476 1.713 32.53 1.12 34.9l-.29 1.154h-8.127c-6.098 0-8.178-.137-8.336-.55zm5.807-81.212c-1.728-2.933-6.079-8.407-9.761-12.281-11.855-12.477-26.13-20.363-43.656-24.118-5.02-1.075-6.351-1.176-15.585-1.196-10.872-.024-13.266.247-21.686 2.452-11.397 2.985-21.79 8.282-30.992 15.796-4.424 3.612-11.47 11.043-14.48 15.27-1.853 2.604-2.787 3.567-3.275 3.382-1.599-.607-12.312-7.811-12.308-8.273.016-1.042 7.233-10.091 12.13-15.211l5.063-5.294-3.349-3.632c-6.12-6.64-11.988-16.529-13.624-22.961-1.284-5.057-.646-9.625 1.506-10.78.464-.248 1.697-.45 2.74-.448 6.595.015 17.53 10.608 24.023 23.262 1.25 2.44 2.429 4.435 2.616 4.435.191 0 1.906-.89 3.814-1.979 9.805-5.595 25.545-10.584 36.26-11.487 1.849-.156 3.595-.427 3.878-.603.39-.241.33-1.42-.235-4.808-1.042-6.243-.717-18.472.624-23.552 2.126-8.051 5.54-12.56 9.014-11.907 3.243.609 6.387 5.898 8.025 13.498 1.066 4.958 1.092 18.903.044 23.472-.387 1.692-.576 3.214-.418 3.38.158.168 2.264.617 4.676.995 13.991 2.198 24.601 6.105 37.603 13.837.886.528 1.717.78 1.851.565s1.333-2.574 2.665-5.241c5.144-10.294 13.426-19.648 20.215-22.832 2.317-1.086 5.402-1.35 6.829-.584 2.154 1.154 2.792 5.72 1.506 10.777-.94 3.693-4.549 10.983-7.783 15.719-1.51 2.212-4.577 5.872-6.815 8.134l-4.068 4.112 4.002 4.256c4.047 4.302 11.194 13.426 12.503 15.961.593 1.145.613 1.466.123 1.917-1.005.926-11.8 7.264-12.371 7.264-.297 0-.884-.583-1.304-1.297'/></svg>",
+ "heroku": "<svg viewBox='0 0 32 32'><path fill='#7e57c2' d='M28 2H6a2 2 0 0 0-2 2v24a2 2 0 0 0 2 2h22a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2M10 26v-6l4 3Zm14 0h-4v-6.45a2.55 2.55 0 0 0-.95-1.987 2.75 2.75 0 0 0-2.278-.478L10 18.44V6h4v7.56l2.16-.43A6.558 6.558 0 0 1 24 19.55Zm0-16h-4V6h4Z'/></svg>",
+ "hex": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='M4 8v16a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2m4 14V10h4v12Zm11.999-6L18 18.001 21 21l-3 3.001L19.999 26l3.003-3 3 2.999L28 24l-3-2.999 3-3L26.001 16l-3 3z'/></svg>",
+ "histoire": "<svg clip-rule='evenodd' viewBox='0 0 16 16'><path fill='#1de9b6' d='M8.814 1.004a.19.19 0 0 0-.162.09l-5.61 7.529A.247.247 0 0 0 3.25 9H7v5.817c-.001.204.266.25.373.076l5.592-7.567c.087-.14.025-.326-.14-.326H9V1.194a.19.19 0 0 0-.185-.19z'/></svg>",
+ "hjson": "<svg viewBox='0 0 24 24'><path fill='#78909c' d='M11.652 3.92a1.1 1.1 0 0 0-.626-.014 1.11 1.11 0 0 0-.786 1.359 1.11 1.11 0 0 0 1.362.788 1.11 1.11 0 0 0 .786-1.363 1.11 1.11 0 0 0-.736-.771zm-3.6-.229a1 1 0 0 0-.246-.027h-.002a418 418 0 0 1-3.02-.096.84.84 0 0 0-.38.089.69.69 0 0 0-.339.506v.001a.68.68 0 0 0 .168.553.9.9 0 0 0 .41.235l.002.001c1.275.388 2.552.766 3.923 1.179l.496.15-.133-.5c-.16-.6-.314-1.098-.423-1.592a.67.67 0 0 0-.292-.434.6.6 0 0 0-.164-.065m2.613 3.73a.56.56 0 0 0-.274.003l-.362.097a.554.554 0 0 0-.392.678l1.105 4.123a.55.55 0 0 0 .677.39l.364-.098a.55.55 0 0 0 .392-.676L11.07 7.816a.55.55 0 0 0-.404-.396z'/><path fill='#8bc34a' d='m21.578 9.77-2.203.59.59 2.203-5.508 1.476a2.28 2.28 0 0 0-1.612 2.793 2.28 2.28 0 0 0-2.79-1.61l-5.508 1.475-.59-2.205-2.203.59.59 2.205A2.28 2.28 0 0 0 5.138 18.9l4.406-1.18a2.28 2.28 0 0 1 2.792 1.61l.295 1.102 2.203-.59-.295-1.103a2.28 2.28 0 0 1 1.613-2.79l4.405-1.18a2.28 2.28 0 0 0 1.612-2.794v-.002z'/></svg>",
+ "horusec": "<svg viewBox='0 0 32 32'><g fill='#e64a19'><path d='M25.407 10.455a4 4 0 0 0-.389.516c.01 1.577.032 4.648.043 5.085.062 2.661-.873 4.94-2.86 6.964-.275.28-.56.54-.854.8.271-.24-.633.616-2.117 1.602a20.666 22.983 0 0 1-3.327 1.685c-2.465-.983-4.509-2.322-6.24-4.087q-.47-.48-.861-.979l-.01-.014a8.884 9.88 0 0 1-.92-1.393l.005-.004-.389-.592a4.43 4.43 0 0 1-2.728-1.911q.119.79.344 1.545c.287.9.383 1.081.88 2.09.548 1 1.261 1.95 2.149 2.854 2.044 2.084 4.463 3.636 7.396 4.743l.374.141.374-.141c2.994-1.131 5.433-2.683 7.454-4.743 2.437-2.484 3.584-5.301 3.505-8.613-.007-.312-.013-4.686-.018-8.503a9.9 11.01 0 0 1-.366.87c-.332.698-.844 1.406-1.445 2.085'/><path d='M27.236 2.5v1.506l.002.096v.51l.002 1.735-.01.035c-.128.39-.249.762-.27.82l-.047.126a14 14 0 0 1-.522 1.243l-.039.078c-.32.622-.786 1.24-1.304 1.782l-.06.066a3.409 3.791 0 0 1-.12.129l-.067.07c-.603.624-1.25 1.198-2.018 1.622l-.127.07c-.3.168-.598.337-.907.483l-.024.01c-.3.143-.324.144-.325.46v9.542l-.038.03a23.674 26.328 0 0 1-2.07 1.587v-9.865c-.004-.139-.017-.281-.028-.48l-.042.017c-.15.066-.264.106-.368.165l-.772.44q-.772.443-1.545.882l-.791.447c-.793.445-1.587.888-2.376 1.338l-.007.004c-.205.116-1.294.742-2.643 1.517l-.002-.009-.072.046a4 4 0 0 1-.688.334l-.007.003h-.003l-.072.025a3.9 3.9 0 0 1-1.213.21h-.16a4.298 4.78 0 0 1-.643-.072l-.052-.011-.051-.013-.085-.02a4.832 5.374 0 0 1-.418-.13l-.05-.018-.051-.02v.001l-.046-.017c-1.415-.572-2.347-1.915-2.347-3.52v-.068l.006-.136v-.065h.005l.004-.055a3.82 3.82 0 0 1 2.178-3.123l.062-.03a4.24 4.716 0 0 1 1.551-.342h.025l.025-.001h.06a3.87 3.87 0 0 1 3.06 1.577l.03.033q.099.116.162.256l.033.053.031.055-.018.009.012.038a.872.97 0 0 1 .038.284v.03a1.08 1.08 0 0 1-1.084 1.044h-.038a1.12 1.246 0 0 1-.868-.483l-.011.004-.024-.035a1.711 1.903 0 0 0-1.072-.686l-.05-.01a1.872 2.082 0 0 0-.261-.02h-.039a1.68 1.68 0 0 0-1.625 1.402l-.007.054a1.34 1.49 0 0 0-.01.111l-.002.187.002.037a1.68 1.68 0 0 0 1.68 1.551h.057a1.924 2.14 0 0 0 .483-.088l.007-.003.007-.003c.306-.13 6.299-3.466 6.657-3.668l-.009-.004c-.195-.084-1.85-.595-2.524-.94l-.098-.049a9.6 9.6 0 0 1-2.503-1.852l-.076-.081a8.893 9.89 0 0 1-1.543-2.134l-.012-.023c-.095-.166-.22-.244-.434-.241h-.018l-.646.002H7.34l-.033.001h-.38v1.633a1.06 1.06 0 0 1-1.066 1.027h-.063a1.06 1.06 0 0 1-1.035-1.058V2.5h22.474zM25.07 4.645H6.927v1.609h.032v.003h3.059c.314.002.314.02.444.35l.03.078c.585 1.452 1.463 2.676 2.773 3.589l.088.06a8.5 8.5 0 0 0 1.858.925l.086.031c.93.324 1.88.419 2.852.37l.096-.005a7.613 8.466 0 0 0 1.883-.383l.108-.04c.502-.183.98-.4 1.439-.673l.085-.051a7.459 8.295 0 0 0 1.517-1.254l.063-.066a6.026 6.702 0 0 0 1.14-1.636l.06-.122c.176-.364.35-.722.526-1.12v-.027l.006-1.638zM17.664 6.33h.261c2.086.003 4.094.023 4.267.065l-.02.07a3.847 4.278 0 0 1-.926 1.643l-.045.051a4.74 4.74 0 0 1-3.276 1.596l-.07.004c-2.32.128-4.408-1.53-4.77-3.364.13-.038 1.865-.059 3.786-.063h.33l.197-.001z'/></g></svg>",
+ "hosts": "<svg viewBox='0 0 16 16'><path fill='#cfd8dc' d='m14 6-3-3v2H7v2h4v2M5 7l-3 3 3 3v-2h4V9H5z'/></svg>",
+ "hosts_light": "<svg viewBox='0 0 16 16'><path fill='#455a64' d='m14 6-3-3v2H7v2h4v2M5 7l-3 3 3 3v-2h4V9H5z'/></svg>",
+ "hpp": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M28 6V2h-2v4h-6V2h-2v4h-4v2h4v4h2V8h6v4h2V8h4V6zm-15.5 5A5.49 5.49 0 0 0 8 13.344V4H2v24h6V17a2 2 0 0 1 4 0v11h6V16.5a5.5 5.5 0 0 0-5.5-5.5'/></svg>",
+ "html": "<svg viewBox='0 0 32 32'><path fill='#e65100' d='m4 4 2 22 10 2 10-2 2-22Zm19.72 7H11.28l.29 3h11.86l-.802 9.335L15.99 25l-6.635-1.646L8.93 19h3.02l.19 2 3.86.77 3.84-.77.29-4H8.84L8 8h16Z'/></svg>",
+ "http": "<svg viewBox='0 0 32 32'><path fill='#e53935' d='M16 2a14 14 0 1 0 14 14A14 14 0 0 0 16 2m11.3 10h-5.64a22.5 22.5 0 0 0-2.705-7.616A12.03 12.03 0 0 1 27.3 12M20 16c0 .693-.037 1.357-.094 2h-7.811A22 22 0 0 1 12 16c0-.693.037-1.357.094-2h7.811c.058.643.095 1.307.095 2m-4 12c-.115 0-.226-.014-.34-.017A20.4 20.4 0 0 1 12.368 20h7.264a20.4 20.4 0 0 1-3.292 7.983c-.114.003-.225.017-.34.017m-3.632-16a20.4 20.4 0 0 1 3.292-7.983c.114-.003.225-.017.34-.017s.226.014.34.017A20.4 20.4 0 0 1 19.632 12Zm.683-7.618A22.4 22.4 0 0 0 10.339 12H4.7a12.03 12.03 0 0 1 8.35-7.618ZM4.18 14h5.91c-.052.647-.091 1.307-.091 2s.039 1.353.091 2H4.18a11.2 11.2 0 0 1 0-4m.52 6h5.638a22.4 22.4 0 0 0 2.712 7.618A12.03 12.03 0 0 1 4.7 20m14.255 7.616A22.5 22.5 0 0 0 21.661 20H27.3a12.03 12.03 0 0 1-8.344 7.616ZM27.819 18h-5.91c.052-.647.091-1.307.091-2s-.039-1.353-.091-2h5.91a11.2 11.2 0 0 1 0 4'/></svg>",
+ "huff": "<svg viewBox='0 0 32 32'><rect width='16' height='2' x='8' y='28' fill='#cfd8dc' rx='.5'/><path fill='#cfd8dc' d='M12 23v1h-1a1 1 0 0 0-1 1v1h12v-1a1 1 0 0 0-1-1h-1v-1a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1m11.916-11.126L20 6V4h2a9.24 9.24 0 0 0-6.176-1.999 8.063 8.063 0 0 0-7.822 8.2 12.3 12.3 0 0 0 .63 3.696L10 18v1a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1v-.586a1 1 0 0 0-.293-.707L16 12h2l2.874 1.916a.5.5 0 0 0 .277.084H23.5a.5.5 0 0 0 .5-.5v-1.349a.5.5 0 0 0-.084-.277M20 10h-1a1 1 0 0 1-1-1V8h1a1 1 0 0 1 1 1Z'/></svg>",
+ "huff_light": "<svg viewBox='0 0 32 32'><rect width='16' height='2' x='8' y='28' fill='#607d8b' rx='.5'/><path fill='#607d8b' d='M12 23v1h-1a1 1 0 0 0-1 1v1h12v-1a1 1 0 0 0-1-1h-1v-1a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1m11.916-11.126L20 6V4h2a9.24 9.24 0 0 0-6.176-1.999 8.063 8.063 0 0 0-7.822 8.2 12.3 12.3 0 0 0 .63 3.696L10 18v1a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1v-.586a1 1 0 0 0-.293-.707L16 12h2l2.874 1.916a.5.5 0 0 0 .277.084H23.5a.5.5 0 0 0 .5-.5v-1.349a.5.5 0 0 0-.084-.277M20 10h-1a1 1 0 0 1-1-1V8h1a1 1 0 0 1 1 1Z'/></svg>",
+ "hurl": "<svg viewBox='0 0 16 16'><path fill='#ec407a' d='M11 2v2H2v2h9v2l4-3zM5 8l-4 3 4 3v-2h9v-2H5z'/></svg>",
+ "husky": "<svg viewBox='0 0 24 24'><path fill='#90a4ae' d='M13.083 2.937c1.003.4 1.4 1.98.875 3.539-.519 1.552-1.745 2.497-2.752 2.104-1-.389-1.404-1.972-.888-3.536.512-1.557 1.753-2.493 2.765-2.107m5.61 3.239c.848.688.65 2.227-.426 3.457-1.105 1.225-2.67 1.67-3.514.995-.853-.68-.647-2.215.445-3.436 1.09-1.234 2.654-1.68 3.495-1.016M6.801 4.122c1.117.132 1.93 1.533 1.863 3.116-.12 1.584-1.075 2.769-2.184 2.641-1.108-.127-1.918-1.515-1.827-3.105.092-1.59 1.068-2.773 2.148-2.652m14.124 8.155c.646.875.11 2.312-1.222 3.186s-2.938.867-3.603-.03c-.664-.896-.116-2.316 1.196-3.212 1.338-.849 2.957-.845 3.63.056m-6.195 7.702c-.394.756-1.43 1.245-2.282 1.162-1.776-.168-2.67-2.462-4.23-3.364-1.562-.901-4.043-.482-5.02-1.977-.564-.838-.516-2.17.075-2.959.816-1.088 2.583-.904 3.909-1.28 1.745-.467 3.73-2.093 5.308-1.183 1.569.906 1.218 3.434 1.62 5.184.319 1.46 1.304 3.106.62 4.417'/></svg>",
+ "i18n": "<svg viewBox='0 0 32 32'><path fill='#7986cb' d='M24 14h-2l-6 14h3l.857-2h6.286L27 28h3Zm-2.856 9L23 18.67 24.856 23ZM12 6V4h-2v2H2v2h11.959a13.4 13.4 0 0 1-2.876 7.07A41 41 0 0 1 8.786 12H6.408a42 42 0 0 0 3.404 4.685 64 64 0 0 1-5.49 5.579l1.355 1.472a68 68 0 0 0 5.454-5.523 49 49 0 0 0 3.279 3.342l1.42-1.42a50 50 0 0 1-3.415-3.498A15.34 15.34 0 0 0 15.97 8H20V6Z'/></svg>",
+ "idris": "<svg viewBox='0 0 200 200'><g fill='#f44336'><path d='M103.07 189.45c-7.937-2.666-8.004-2.718-9.763-7.517-.974-2.657-2.302-9.039-2.952-14.18-.997-7.897-.931-10.755.422-18.392 2.778-15.674 9.623-27.676 27.672-48.52 20.87-24.103 24.51-32.428 20.238-46.29-3.656-11.866-13.458-22.97-33.613-38.078-12.915-9.68-13.458-10.303-5.013-5.736 22.554 12.196 40.365 27.413 46.375 39.621 4.182 8.495 5.13 17.92 2.664 26.458-2.622 9.076-7.402 17.316-21.141 36.448-18.697 26.035-24.133 38.641-23.165 53.723.411 6.41 1.147 8.82 5.004 16.396 5.204 10.22 5.321 10.114-6.728 6.066zm-16.791-83.413c-4.28-13.575-13.604-22.609-29.213-28.302-4.127-1.505-7.504-2.968-7.504-3.25 0-1.027 18.939.848 22.98 2.274 10.092 3.56 15.043 10.936 15.799 23.537.643 10.726.039 12.409-2.063 5.742zm16.539-21.356C96.771 68.28 86.659 59 69.062 53.7c-9.46-2.849-9.054-3.744 1.347-2.967 20.536 1.534 29.413 7.298 33.642 21.846.953 3.279 1.488 8.121 1.31 11.864l-.299 6.323zm13.371-24.882c-5.02-11.521-12.108-17.947-25.873-23.454-5.394-2.158-6.8-3.084-5.48-3.608 2.477-.983 14.766.98 19.144 3.058 4.914 2.332 11.082 9.558 13.024 15.26 1.553 4.56 3.246 15.212 2.416 15.212-.228 0-1.682-2.91-3.232-6.467z' style='font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;isolation:auto;mix-blend-mode:normal;shape-margin:0;shape-padding:0;solid-color:#000;text-decoration-color:#000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal' transform='translate(0 .001)'/><path d='M94.58 7.496c-.085-.009-.186-.028-.357.057s-.319.377-.303.54c.03.328.15.375.258.499.434.497 1.427 1.302 3.168 2.654 1.74 1.352 4.196 3.206 7.425 5.627 20.123 15.084 29.828 26.115 33.436 37.824 2.116 6.866 2.271 12.23-.671 18.96s-9.042 14.816-19.467 26.856c-18.07 20.87-24.992 32.99-27.787 48.76-1.357 7.655-1.426 10.628-.426 18.541.653 5.175 1.966 11.528 2.978 14.291.88 2.401 1.386 3.746 2.73 4.781 1.346 1.036 3.376 1.704 7.345 3.037 3.011 1.012 5.263 1.78 6.898 2.207.818.214 1.479.346 2.031.373s1.038-.04 1.399-.365c.36-.325.449-.832.39-1.336s-.244-1.077-.53-1.779c-.57-1.404-1.556-3.311-2.857-5.865-3.85-7.56-4.541-9.822-4.95-16.201-.48-7.473.617-14.292 4.167-22.525s9.565-17.87 18.904-30.875c13.747-19.142 18.572-27.446 21.217-36.6 2.504-8.666 1.536-18.22-2.697-26.818-6.11-12.412-23.974-27.614-46.584-39.84-2.114-1.143-3.663-1.961-4.625-2.406-.48-.223-.753-.36-1.092-.397m2.588 2.3c.832.43 1.427.72 2.652 1.382 22.498 12.165 40.254 27.397 46.164 39.402 4.13 8.39 5.063 17.686 2.633 26.098-2.6 8.998-7.337 17.174-21.068 36.295-9.358 13.03-15.41 22.713-19.01 31.062s-4.736 15.376-4.248 22.986c.413 6.44 1.193 9 5.059 16.59 1.301 2.556 2.282 4.465 2.82 5.789.269.662.423 1.18.463 1.52.04.339-.014.429-.067.476-.052.047-.251.129-.68.107-.427-.02-1.041-.134-1.826-.34-1.569-.41-3.82-1.175-6.834-2.187-3.967-1.333-5.94-2.024-7.052-2.88s-1.522-1.937-2.4-4.335c-.936-2.553-2.281-8.963-2.926-14.072-.995-7.878-.933-10.622.418-18.24 2.76-15.578 9.53-27.46 27.559-48.28 10.444-12.063 16.6-20.188 19.627-27.112s2.866-12.656.71-19.652c-3.703-12.022-13.603-23.199-33.79-38.33-3.228-2.42-5.683-4.273-7.413-5.617-.452-.351-.435-.374-.79-.66zm-8.889 22.212c-1.594-.08-2.825-.056-3.629.264-.202.08-.382.172-.539.334s-.27.44-.23.689c.078.499.43.742.9 1.05.94.62 2.64 1.381 5.346 2.464 13.695 5.48 20.624 11.764 25.602 23.189a101 101 0 0 0 2.121 4.591c.296.592.55 1.073.754 1.419.101.172.188.31.273.423.043.057.083.108.143.164s.119.17.398.17a.65.65 0 0 0 .59-.43 1.6 1.6 0 0 0 .106-.488c.035-.352.028-.8-.006-1.35-.068-1.097-.252-2.58-.51-4.206-.517-3.254-1.326-7.059-2.123-9.399-2.005-5.885-8.154-13.117-13.283-15.55-2.298-1.091-6.463-2.087-10.42-2.72-1.979-.315-3.898-.535-5.492-.615zm-.05.998c1.542.077 3.434.294 5.384.605 3.901.623 8.07 1.648 10.15 2.635 4.699 2.23 10.886 9.451 12.766 14.97.757 2.219 1.57 6.02 2.08 9.232.255 1.606.436 3.071.5 4.113.007.12-.002.165.002.273-.123-.231-.22-.396-.367-.691-.58-1.16-1.328-2.771-2.1-4.543-5.06-11.617-12.31-18.186-26.144-23.721-2.688-1.075-4.388-1.854-5.17-2.37-.302-.198-.324-.264-.356-.284.023-.012.015-.012.045-.024.434-.172 1.666-.272 3.21-.195m-23.76 16.96c-.692.01-1.23.047-1.643.12-.207.037-.381.08-.545.152-.164.073-.346.177-.453.413-.107.235-.034.526.074.687s.24.268.396.377c.313.218.743.429 1.325.672 1.162.486 2.918 1.076 5.29 1.79 17.495 5.27 27.427 14.388 33.433 30.677l2.244 6.084.969-.149.298-6.324c.18-3.81-.354-8.668-1.33-12.025-2.136-7.347-5.49-12.562-10.914-16.084s-12.863-5.35-23.172-6.12c-2.607-.196-4.59-.288-5.972-.268zm.015 1c1.324-.018 3.29.07 5.883.265 10.227.764 17.496 2.584 22.7 5.964 5.206 3.38 8.406 8.323 10.499 15.523.93 3.2 1.467 8.025 1.293 11.7l-.184 3.865-1.392-3.776c-6.089-16.513-16.38-25.956-34.08-31.287-2.357-.71-4.099-1.299-5.196-1.758-.39-.163-.592-.283-.81-.406.317-.044.712-.082 1.287-.09m-11.889 22.74c-.93-.027-1.696-.022-2.258.025a3.5 3.5 0 0 0-.71.116 1 1 0 0 0-.311.148.65.65 0 0 0-.258.49c0 .267.116.349.18.414.063.066.121.109.187.155.132.091.292.183.492.289.402.21.96.47 1.647.767 1.373.595 3.255 1.338 5.326 2.094 15.509 5.656 24.672 14.547 28.908 27.984.53 1.68.956 2.83 1.34 3.518.192.343.317.624.738.752a.76.76 0 0 0 .656-.155 1 1 0 0 0 .274-.445c.227-.632.276-1.624.275-3.203 0-1.58-.085-3.702-.246-6.389-.382-6.36-1.825-11.449-4.482-15.408s-6.525-6.762-11.648-8.57c-1.094-.386-3.031-.763-5.387-1.125s-5.108-.699-7.736-.96-5.128-.443-6.987-.497m-.03 1c1.82.053 4.307.236 6.919.494 2.612.259 5.35.593 7.681.951 2.332.359 4.28.753 5.207 1.08 4.968 1.753 8.62 4.417 11.15 8.186s3.94 8.667 4.315 14.908q.241 4.013.244 6.33c0 1.235-.09 1.828-.174 2.291-.296-.583-.677-1.54-1.156-3.059-4.323-13.713-13.809-22.894-29.518-28.623-2.056-.75-3.927-1.489-5.273-2.072-.431-.187-.7-.322-1.01-.47.46-.019.93-.036 1.615-.016' style='font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;isolation:auto;mix-blend-mode:normal;shape-margin:0;shape-padding:0;solid-color:#000;text-decoration-color:#000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal' transform='translate(0 .001)'/></g></svg>",
+ "ifanr-cloud": "<svg viewBox='0 0 256 256'><path fill='#0288d1' d='M126.9 41.6c30.2 0 56.1 18.5 67.1 44.8 29.2 6.3 51.1 32.2 51.1 63.3 0 35.7-29 64.7-64.7 64.7H60.7c-27.5 0-49.7-22.3-49.7-49.7 0-25.3 18.8-46.1 43.2-49.3v-.8c0-40.3 32.6-73 72.7-73m2.6 49c-3.5 0-6.3 2.8-6.3 6.4v86.7c0 3.5 2.8 6.4 6.3 6.4s6.3-2.8 6.3-6.4V97c0-3.5-2.8-6.4-6.3-6.4m-41 25.7c-.2 10.4-1.6 21-3.7 30.4-.9 3.9-1.8 7.4-2.8 10.1-.4 1.2-.8 2.2-1.1 3-.1.3-.3.6-.4.7-2.2 2.7-1.7 6.7 1 8.9s6.7 1.8 8.9-1c4.8-6 10.3-30.4 10.7-51.9.1-3.5-2.7-6.4-6.2-6.5s-6.3 2.8-6.4 6.3m75.3-6.3h-.1c-3.5.1-6.3 3-6.2 6.5.4 21.5 5.9 45.9 10.7 51.9 2.2 2.7 6.2 3.2 8.9 1s3.1-6.2 1-8.9c-.1-.2-.2-.4-.4-.7-.3-.7-.7-1.8-1.1-3-.9-2.7-1.9-6.2-2.8-10.1-2.1-9.4-3.5-20-3.7-30.4-.1-3.5-3-6.3-6.5-6.2z'/></svg>",
+ "image": "<svg viewBox='0 0 16 16'><path fill='#26a69a' d='M8.5 6h4l-4-4zM3.875 1H9.5l4 4v8.6c0 .773-.616 1.4-1.375 1.4h-8.25c-.76 0-1.375-.627-1.375-1.4V2.4c0-.777.612-1.4 1.375-1.4M4 13.6h8V8l-2.625 2.8L8 9.4zm1.25-7.7c-.76 0-1.375.627-1.375 1.4s.616 1.4 1.375 1.4c.76 0 1.375-.627 1.375-1.4S6.009 5.9 5.25 5.9'/></svg>",
+ "imba": "<svg stroke-linejoin='round' stroke-miterlimit='1.414' clip-rule='evenodd' viewBox='0 0 201 201'><path fill='#ffc400' d='M161.96 61.952c-3.043 13.905-32.633 79.576-36.431 94.457-2.698 10.575 11.229 23.851 13.555 15.159 6.84-25.548 37.32-86.251 39.023-98.893 1.468-10.897-14.66-17.516-16.147-10.723m-37.128 48.192a4.97 4.97 0 0 1 5.726 6.91c.023.012.021.015.021.016a19.04 19.04 0 0 1-13.667 10.676c-3.4.645-7.236 1.182-11.504 1.588-15.316 1.453-31.743-17.007-20.624-16.49 16.552.77 29.747-.447 40.047-2.7zm16.256-17.347a13.36 13.36 0 0 1-9.677 8.152c-20.232 4.242-49.32 2.59-63.662-.888-13.94-3.38-23.102-23.665-14.05-20.64 21.019 7.024 60.118 9.347 82.248 6.838a4.808 4.808 0 0 1 5.133 6.523c.011.004.011.004.008.015m8.398-23.8a11.39 11.39 0 0 1-9.973 8.037c-40.633 2.924-92.83-6.466-107.91-22.019C20.273 43.326 21 20.85 27.442 27.992c24.417 27.072 84.437 34.865 117.12 34.521a5.022 5.022 0 0 1 4.92 6.481q.003.002.001.003z'/></svg>",
+ "installation": "<svg viewBox='0 0 16 16'><path fill='#ff5722' d='M12 7h-2V2H6v5H4l4 4zm-9 5.5V14h10v-1.5z'/></svg>",
+ "ionic": "<svg viewBox='0 0 512 512'><g fill='#448aff'><path d='M423.59 132.8a31.86 31.86 0 0 0 5.408-17.804c0-17.675-14.33-32-32-32a31.85 31.85 0 0 0-17.805 5.409c-34.486-25.394-77.085-40.409-123.2-40.409-114.88 0-208 93.125-208 208 0 114.88 93.125 208 208 208 114.87 0 208-93.123 208-208 0-46.111-15.016-88.71-40.408-123.2zm-31.762 259.03c-17.646 17.646-38.191 31.499-61.064 41.174-23.672 10.012-48.826 15.089-74.766 15.089s-51.095-5.077-74.767-15.089c-22.873-9.675-43.417-23.527-61.064-41.174s-31.5-38.191-41.174-61.064c-10.013-23.672-15.09-48.828-15.09-74.768s5.077-51.095 15.089-74.767c9.674-22.873 23.527-43.417 41.174-61.064s38.191-31.5 61.064-41.174c23.673-10.013 48.828-15.09 74.768-15.09s51.094 5.077 74.766 15.089a191.2 191.2 0 0 1 37.802 21.327 31.85 31.85 0 0 0-3.568 14.679c0 17.675 14.327 32 32 32 5.293 0 10.28-1.293 14.678-3.568a191 191 0 0 1 21.327 37.801c10.013 23.672 15.09 48.827 15.09 74.767s-5.077 51.096-15.09 74.768c-9.675 22.873-23.527 43.418-41.175 61.064'/><circle cx='256' cy='256' r='96'/></g></svg>",
+ "istanbul": "<svg viewBox='0 0 32 32'><path fill='#fdd835' d='M30 13v-3h-2v3h-1v.5a.5.5 0 0 0 .5.5h.5v10h-1v-2h-1v-8h-1v6h-2v-3h-1v1h-1v-1h1a6.35 6.35 0 0 0-6-4 6.35 6.35 0 0 0-6 4h1v1h-1v-1H9v3H7v-6H6v8H5v2H4V14h.5a.5.5 0 0 0 .5-.5V13H4v-3H2v3H1v.5a.5.5 0 0 0 .5.5H2v12h8v-6h2v6h8v-6h2v6h8V14h.5a.5.5 0 0 0 .5-.5V13ZM14 24h-1v-2h1Zm3 0h-2v-2a1 1 0 0 1 2 0Zm2 0h-1v-2h1Zm-1-7v1h-1v-1Zm-3 0v1h-1v-1Zm-3 0h1v1h-1Zm7 1v-1h1v1ZM3 6 2 9h2zm26 0-1 3h2z'/><path fill='#fdd835' d='m16 10-1 2h2zm9.5 1-.5 2h1zm-19 0L6 13h1z'/></svg>",
+ "jar": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='M22 10h2v4h-2z'/><path fill='#f44336' d='M28 2H4a2 2 0 0 0-2 2v24a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2m-2 12a2 2 0 0 1-2 2h-2v4a4 4 0 0 1-4 4h-8a4 4 0 0 1-4-4V8h18a2 2 0 0 1 2 2Z'/></svg>",
+ "java": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='M4 26h24v2H4zM28 4H7a1 1 0 0 0-1 1v13a4 4 0 0 0 4 4h10a4 4 0 0 0 4-4v-4h4a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2m0 8h-4V6h4Z'/></svg>",
+ "javaclass": "<svg viewBox='0 0 32 32'><path fill='#1e88e5' d='M4 26h24v2H4zM28 4H7a1 1 0 0 0-1 1v13a4 4 0 0 0 4 4h10a4 4 0 0 0 4-4v-4h4a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2m0 8h-4V6h4Z'/></svg>",
+ "javascript-map": "<svg viewBox='0 0 16 16'><g fill='#ffca28'><path d='M12 5v1h1v7H6v-1H5l-.069 2 9.069.001V5z'/><path d='M2 2v9h9V2zm3 3h1v4a1.003 1.003 0 0 1-1 1H4a1.003 1.003 0 0 1-1-1V8h1v1h1zm3 0h2v1H8v1h1a1.003 1.003 0 0 1 1 1v1a1.003 1.003 0 0 1-1 1H7V9h2V8H8a1.003 1.003 0 0 1-1-1V6a1.003 1.003 0 0 1 1-1'/></g></svg>",
+ "javascript": "<svg viewBox='0 0 16 16'><path fill='#ffca28' d='M2 2v12h12V2zm6 6h1v4a1.003 1.003 0 0 1-1 1H7a1.003 1.003 0 0 1-1-1v-1h1v1h1zm3 0h2v1h-2v1h1a1.003 1.003 0 0 1 1 1v1a1.003 1.003 0 0 1-1 1h-2v-1h2v-1h-1a1.003 1.003 0 0 1-1-1V9a1.003 1.003 0 0 1 1-1'/></svg>",
+ "jenkins": "<svg viewBox='0 0 180 180'><defs><clipPath id='a'><path fill='#37474f' d='M.899 144.42h144.42V0H.899z'/></clipPath></defs><g clip-path='url(#a)' transform='matrix(1.0691 0 0 -1.0691 9.4 166.143)'><g fill-rule='evenodd'><path fill='#f0d6b7' d='m107.96 30.661-12.506-1.876-16.883-1.876-10.943-.312-10.629.312-8.13 2.502-7.19 7.815-5.628 15.945-1.25 3.44-7.504 2.5-4.377 7.191-3.126 10.317 3.44 9.067 8.128 2.814 6.565-3.127 3.127-6.878 3.752.626 1.25 1.563-1.25 7.19-.313 9.068 1.876 12.505-.074 7.143 5.701 9.114 10.005 7.19 17.508 7.504 19.383-2.814 16.883-12.193 7.817-12.505 5.002-9.067 1.25-22.51-3.752-19.384-6.877-17.195-6.566-9.066'/><path fill='#335061' d='m97.334-23.425-44.709-1.876v-7.503l3.752-26.262-1.876-2.19-31.264 10.63-2.19 3.752-3.126 35.328-7.19 21.26-1.563 5.002 25.01 17.195 7.818 3.127 6.877-8.441 5.94-5.315 6.88-2.188 3.125-.938L68.57 1.899l2.814-3.44 7.19 2.502-5.002-9.693 27.2-12.818-3.439-1.876'/><path fill='#6d6b6d' d='m23.238 85.687 8.128 2.814 6.566-3.127 3.127-6.878 3.751.626.938 3.751-1.876 7.19 1.876 17.197-1.563 9.379 5.627 6.565 12.193 9.692-3.44 4.69-17.194-8.442-7.191-5.627-4.064-8.754-6.253-8.442-1.876-10.005z'/><path fill='#dcd9d8' d='M36.055 115.07s4.69 11.567 23.448 17.195c18.759 5.628.938 4.065.938 4.065l-20.321-7.817-7.817-7.816-3.438-6.253zm-9.379-27.195s-6.566 21.886 18.446 25.012l-.938 3.752-17.195-4.065-5.003-16.257 1.251-10.63z'/></g><g fill='#f7e4cd'><path fill-rule='evenodd' d='m36.681 58.799 4.094 3.966s1.847-.214 2.16-2.402c.312-2.19 1.25-21.886 14.693-32.516 1.227-.97-10.004 1.564-10.004 1.564L37.62 45.042m56.589 19.697s.729 9.477 3.28 8.748c2.553-.729 2.553-3.28 2.553-3.28s-6.198-4.01-5.833-5.468'/><path d='M120.16 99.442s-5.153-1.088-5.628-5.628c-.474-4.54 5.628-.938 6.566-.625m-38.771 5.94s-6.879-.938-6.879-5.314c0-4.378 7.817-4.065 10.005-2.19'/><g fill-rule='evenodd'><path d='M39.807 78.808s-11.881 7.191-13.131.312c-1.25-6.877-4.065-11.88 1.876-19.07l-4.064 1.25-3.752 9.691-1.25 9.38 7.19 7.504 8.129-.626 4.69-3.751zm5.628 19.696s5.315 27.512 32.203 32.827c22.136 4.375 33.765-.938 38.142-5.94 0 0-19.696 23.447-38.455 16.257-18.759-7.191-32.514-20.322-32.202-28.762.532-14.377.313-14.382.313-14.382m72.534 23.766s-9.066.312-9.38-7.817c0 0 0-1.25.625-2.5 0 0 7.192 8.129 11.568 3.751'/><path d='M78.268 111.1s-1.56 12.477-12.199 5.223c-6.878-4.69-6.252-11.255-5.002-12.505s.91-3.77 1.862-2.04c.952 1.728.638 7.356 4.078 8.918 3.439 1.564 9.077 3.31 11.26.404'/></g></g><path fill='#49728b' fill-rule='evenodd' d='M48.874 26.597 19.486 13.466s12.193-48.46 5.94-63.467l-4.377 1.563-.313 18.446-8.128 35.015-3.44 9.692 30.639 20.633 9.067-8.753M51.896-.206l4.17-5.087v-18.76h-5.003s-.625 13.132-.625 14.696c0 1.563.624 7.19.624 7.19M52-26.866l-14.069-.625 4.065-2.813L52-31.868'/><g fill-rule='evenodd'><path fill='#335061' d='m100.15-23.739 11.567.313 2.814-28.764-11.881-1.563z'/><path fill='#335061' d='m103.27-23.739 17.508.938s7.19 18.133 7.19 19.07c0 .939 6.253 26.263 6.253 26.263l-14.069 14.694-2.813 2.501-7.504-7.503V3.148z'/><path fill='#49728b' d='m111.09-21.55-10.942-2.188 1.563-8.755c4.064-1.876 10.943 3.127 10.943 3.127M111.4 33.162l21.885-16.257.626 7.503-16.57 15.32-5.94-6.566'/><path fill='#fafafa' d='m62.85-85.332-6.473 26.266-3.22 19.38-.531 14.385 29.296 1.56 18.226.003-1.658-32.83 2.814-25.324-.312-4.69-23.76-1.876z'/><path fill='#dcd9d8' d='M96.083-23.426s-1.563-32.515 3.127-55.65c0 0-9.38-5.94-23.136-7.503l26.262.938 3.126 1.875-3.752 51.273-.938 10.944'/><path fill='#fafafa' d='m115.06-49.691 12.193 3.44 23.135 1.25 3.44 10.629-6.254 18.446-7.19.938-10.005-3.127-9.599-4.686-5.095.935-3.972-1.56'/><path fill='#dcd9d8' d='M114.84-43.435s8.128 3.751 9.38 3.438L120.78-22.8l4.065 1.563s2.814-16.257 2.814-18.133c0 0 17.507-.938 19.07-.938 0 0 3.752 7.191 2.814 14.694l3.44-10.005.312-5.628-5.002-7.503-5.627-1.25-9.38.312-3.126 4.064-10.943-1.563-3.44-1.25'/></g><path fill='#fafafa' d='M102.56-21.241 95.682-3.733l-7.19 10.317s1.562 4.377 3.75 4.377h7.192l6.878-2.501-.625-11.568-3.127-18.134'/><path fill='#dcd9d8' fill-rule='evenodd' d='M103.9-15.297S95.145 1.585 95.145 4.086c0 0 1.563 3.752 3.752 2.814s6.879-3.439 6.879-3.439v5.94l-10.63 2.19-7.19-.939 12.193-28.763 2.5-.313'/><path fill='#fafafa' d='m65.664 25.968-8.661.942-8.13 2.501v-2.814l3.972-4.38 12.506-5.627'/><path fill='#dcd9d8' fill-rule='evenodd' d='M51.689 25.031s9.693-4.065 12.819-3.127l.311-3.748-8.752 1.872-5.316 3.752z'/><path fill='#d33833' fill-rule='evenodd' d='M115.03 9.897c-5.305.156-10.098.786-14.294 1.97.285 1.72-.249 3.408.18 4.647 1.17.843 3.13.83 4.898 1.027-1.529.752-3.677 1.049-5.44.615-.042 1.194-.578 1.934-.902 2.868 2.982 1.064 10.024 8.044 13.984 5.732 1.887-1.099 2.689-7.377 2.835-10.43.122-2.533-.23-5.088-1.261-6.43'/><path fill='none' stroke='#d33833' stroke-width='2' d='M115.03 9.897c-5.305.156-10.098.786-14.294 1.97.285 1.72-.249 3.408.18 4.647 1.17.843 3.13.83 4.898 1.027-1.529.752-3.677 1.049-5.44.615-.042 1.194-.578 1.934-.902 2.868 2.982 1.064 10.024 8.044 13.984 5.732 1.887-1.099 2.689-7.377 2.835-10.43.122-2.533-.23-5.088-1.261-6.43z'/><path fill='#d33833' fill-rule='evenodd' d='M89.66 18.569q-.021-.603-.047-1.21c-1.656-1.089-4.33-1.076-6.148-1.99 2.68-.117 4.79-.763 6.614-1.672l-.118-3.033c-3.036-2.078-5.81-5.173-9.384-7.122-1.69-.922-7.622-3.294-9.42-2.875-1.017.236-1.109 1.499-1.516 2.689-.866 2.548-2.861 6.605-3.035 10.44-.222 4.846-.71 12.967 4.51 11.969 4.213-.804 9.113-2.745 12.375-4.527 1.993-1.09 3.146-2.436 6.17-2.669'/><path fill='none' stroke='#d33833' stroke-width='2' d='M89.66 18.569q-.021-.603-.047-1.21c-1.656-1.089-4.33-1.076-6.148-1.99 2.68-.117 4.79-.763 6.614-1.672l-.118-3.033c-3.036-2.078-5.81-5.173-9.384-7.122-1.69-.922-7.622-3.294-9.42-2.875-1.017.236-1.109 1.499-1.516 2.689-.866 2.548-2.861 6.605-3.035 10.44-.222 4.846-.71 12.967 4.51 11.969 4.213-.804 9.113-2.745 12.375-4.527 1.993-1.09 3.146-2.436 6.17-2.669z'/><path fill='#d33833' fill-rule='evenodd' d='M92.675 12.788c-.463 2.64-.999 3.393-.792 5.695 7.04 4.693 8.361-8.061.792-5.695'/><path fill='none' stroke='#d33833' stroke-width='2' d='M92.675 12.788c-.463 2.64-.999 3.393-.792 5.695 7.04 4.693 8.361-8.061.792-5.695z'/><g fill-rule='evenodd'><path fill='#ef3d3a' d='M102.87 10.649s-2.19 3.127-.626 4.065 3.127 0 4.065 1.563 0 2.501.313 4.377 1.877 2.189 3.44 2.501c1.562.313 5.94.938 6.565-.625l-1.876 5.627-3.752 1.25-11.88-6.877-.626-3.44v-6.877M70.041.331c-.376 4.88-.773 9.752-1.215 14.626-.662 7.279 1.748 6.009 8.057 6.009.964 0 5.933-1.15 6.289-1.876 1.705-3.483-2.851-2.709 1.964-5.335 4.065-2.216 11.246 1.346 9.603 6.273-.919 1.095-4.789.341-6.176 1.06l-7.327 3.8c-3.108 1.612-10.29 3.962-13.603 1.709-8.395-5.71.53-19.974 3.524-25.93'/><path fill='#231f20' d='M78.268 111.1c-8.521 1.985-12.755-3.566-15.338-9.323-2.306.559-1.389 3.695-.806 5.294 1.525 4.194 7.672 9.778 12.694 9.02 2.161-.325 5.086-2.301 3.45-4.99m41.522-9.701.404-.016c1.926-4 3.593-8.238 6.022-11.769-1.628-3.79-12.322-7.144-12.157-.338 2.313 1.01 6.305.206 8.356 1.497-1.186 3.254-2.897 6.024-2.625 10.626m-37.16-.11c1.827-3.35 2.422-6.868 5.019-9.4 1.17-1.14 3.444-2.529 2.316-5.698-.263-.747-2.189-2.414-3.3-2.741-4.06-1.2-13.521-.248-10.317 4.814 3.358-.157 7.871-2.18 10.38.257-1.927 3.081-5.363 9.177-4.098 12.768m35.63-34.037c-6.113-3.927-12.93-8.197-22.947-7.207-2.14 1.86-2.956 6.002-.877 8.737 1.082-1.861.402-5.284 3.419-5.799 5.684-.972 12.299 3.477 16.387 5.032 2.535 4.275-.219 5.847-2.503 8.597-4.675 5.636-10.947 12.622-10.72 21.06 1.89 1.37 2.053-2.092 2.325-2.722 2.44-5.714 8.585-13.021 13.07-17.912 1.1-1.205 2.914-2.36 3.115-3.157.582-2.315-1.513-5.09-1.27-6.63m-80.591 4.135c-1.916 1.094-2.372 5.91-4.622 6.048-3.215.195-2.629-6.25-2.616-10.018-2.213 2.009-2.602 8.194-.976 11.37-1.853.91-2.68-1.003-3.708-1.677 1.32 9.595 14.036 4.45 11.922-5.723m84.482-8.13c-2.846-5.417-6.871-11.382-15.222-11.555-.17 1.75-.3 4.411.009 5.464 6.384.614 10.325 3.863 15.212 6.091m-40-3.512c5.326-2.8 15.114-3.102 22.353-2.89.388-1.586.379-3.545.394-5.48-9.305-.463-20.307 1.84-22.747 8.37m-1.013-5.222c3.683-9.247 16.341-8.182 27.016-7.927-.47-1.2-1.489-2.62-2.755-3.132-3.42-1.392-12.855-2.448-17.604.074-3.011 1.601-4.946 5.219-6.596 7.34-.797 1.024-4.765 3.64-.06 3.645'/><path fill='#81b0c4' d='M117.82 3.516c-4.322-7.402-8.457-15.005-13.585-21.534 2.15 6.32 3.07 16.9 3.394 24.965 4.498 2.105 8.349-.474 10.191-3.43'/><path fill='#231f20' d='M141.07-23.089c-4.839-.969-8.239-5.671-12.959-5.37 2.594 3.658 7.14 5.2 12.959 5.37m2.14-7.572c-3.944-.417-8.576-1.055-12.577-.726 1.894 2.892 9.19 1.894 12.577.726m1.37-6.529c-4.433-.096-9.942-.008-14.155.346 2.492 2.677 11.28.993 14.155-.346'/><path fill='#dcd9d8' d='M109.48-55.057c.636-5.567 2.843-11.207 2.566-17.304-2.45-.827-3.858-1.55-7.142-1.545-.232 5.181-.925 13.102-.718 18.041 1.615-.107 3.997 1.154 5.294.808'/><path fill='#f0d6b7' d='M102.33 26.985c-2.226-1.453-4.121-3.267-6.259-4.818-4.74-.235-7.327.328-10.81 3.05.057.219.407.121.42.39 5.075-2.262 11.524.92 16.648 1.378'/><path fill='#81b0c4' d='M75.694-7.603c1.394 6.04 6.857 9.17 11.817 12.497 5.12-6.498 8.234-14.855 11.663-22.92-8.102 2.443-16.38 6.406-23.481 10.423'/><path fill='#231f20' d='M104.18-55.865c-.207-4.94.486-12.86.718-18.041 3.283-.004 4.691.718 7.142 1.545.276 6.096-1.93 11.737-2.566 17.304-1.298.346-3.679-.914-5.294-.808m-51.13 28.09c2.165-19.906 5.301-36.639 11.054-54.266 12.766-3.876 28.157-4.214 39.441-.716-2.072 9.948-1.167 22.06-2.378 32.677-.912 7.98-.447 16.009-1.698 24.15-13.673 2.844-33 .665-46.418-1.845zm49.651 1.72c-.115-8.549.383-16.982 1.036-25.542 3.282.493 5.51.822 8.56 1.49-.99 8.241-.869 17.514-2.886 24.804-2.332-.023-4.385.027-6.71-.752m16.653 1.378c-1.558.357-3.372.014-4.86-.015.7-6.969 2.397-14.659 2.995-21.974 2.342-.073 3.593 1.032 5.52 1.403.102 6.421-.562 15.268-3.655 20.586m25.215-23.038c4.882 1.186 7.952 7.165 6.586 13.305-.916 4.127-2.548 11.898-4.295 14.538-1.29 1.953-4.79 4.51-7.584 2.72-4.545-2.91-12.552-3.755-15.867-7.278 1.662-5.534 2.178-13.135 2.864-20.146 5.678-.354 12.665 1.562 17.387-.471-3.297-1.068-7.575-1.077-10.423-2.633 2.328-1.125 7.778-.897 11.332-.035M99.17-18.025c-3.43 8.063-6.543 16.42-11.663 22.918-4.96-3.327-10.423-6.456-11.817-12.497 7.1-4.017 15.379-7.98 23.481-10.422zm8.453 24.971c-.325-8.065-1.245-18.644-3.395-24.965 5.128 6.53 9.263 14.132 13.585 21.534-1.842 2.957-5.693 5.536-10.19 3.431m-9.582 3.405c-1.943.21-3.592-2.233-6.117-1.177-.58-.64-1.105-1.333-1.695-1.958 5.579-6.723 8.114-16.262 12.423-24.163 2.312 7.59 2.045 15.904 2.555 24.188-3.177-.201-4.94 2.873-7.166 3.11m-6.161 8.132c-.208-2.303.328-3.056.791-5.695 7.57-2.367 6.248 10.388-.791 5.695m-8.394 2.755c-3.261 1.782-8.161 3.723-12.374 4.527-5.222.999-4.732-7.123-4.51-11.968.173-3.836 2.168-7.893 3.035-10.441.406-1.19.498-2.453 1.515-2.69 1.798-.418 7.73 1.954 9.42 2.875 3.575 1.95 6.348 5.045 9.384 7.123l.119 3.032c-1.826.91-3.935 1.555-6.615 1.673 1.818.914 4.492.901 6.148 1.989q.025.609.047 1.21c-3.024.234-4.176 1.58-6.17 2.67zm-31.152 5.659c-2.707-2.748 7.592-6.494 10.871-6.696-.018 1.739.991 3.378.788 4.626-3.895.684-9.013.232-11.66 2.07zm33.345-1.29c-.013-.27-.363-.172-.42-.39 3.482-2.722 6.07-3.285 10.81-3.05 2.137 1.551 4.033 3.365 6.259 4.818-5.124-.458-11.574-3.64-16.648-1.379zm30.606-9.282c-.146 3.053-.948 9.332-2.835 10.431-3.961 2.312-11.002-4.668-13.984-5.732.324-.934.86-1.674.901-2.868 1.764.434 3.912.137 5.44-.615-1.767-.198-3.727-.185-4.897-1.027-.429-1.239.105-2.927-.18-4.647 4.196-1.184 8.989-1.814 14.294-1.97 1.032 1.341 1.383 3.896 1.261 6.429zM47.777 24.24c-.85.606-6.6 8.087-7.388 7.777-10.405-4.103-20.134-11.199-28.828-17.91 8.29-17.787 11.635-39.579 12.227-60.582 9.496-4.441 17.836-10.844 30.722-11.512-1.491 10.55-2.852 19.962-3.699 29.895-3.237 1.365-7.882-.062-10.913.423-.025 3.651 4.628 1.6 5.015 4.054.292 1.858-2.56 1.998-1.631 4.923 2.368-.861 3.612-2.763 6.138-3.477 2.309 5.05-.032 13.985.3 18.205.064.792.397 4.39 2.172 3.759 1.57-.559-.09-9.569.082-13.563.157-3.68-.444-7.242 1.046-9.552a356 356 0 0 0 38.576 3.16c-2.964 1.272-6.485 2.475-10.345 4.651-2.093 1.18-8.69 3.635-9.293 5.622-.964 3.167 2.528 4.855 3.125 7.57-6.285-3.428-7.511 3.286-8.998 8.042-1.347 4.308-2.114 7.526-2.445 10.01-5.414 2.581-11.203 5.195-15.863 8.505m63.009 6.872c8.67 4.204 10.232-15.711 6.834-22.127.525-1.914 2.331-2.646 3.069-4.366-4.838-8.667-10.211-16.756-15.148-25.32 3.672 2.286 8.917.409 13.238 2.12 1.58.624 2.722 4.24 3.918 7.133 3.29 7.958 6.743 17.99 8.28 25.586.346 1.73 1.292 5.5 1.08 7.04-.378 2.758-4.12 4.803-6.022 6.508-3.506 3.15-5.714 5.921-9.371 8.866-1.483-2.189-4.666-3.66-5.878-5.44M27.95 107.99c-4.13-4.545-3.266-13.062-2.766-19.121 7.467 4.697 17.377-.372 17.284-8.36 3.565.094 1.332 4.452.687 7.259-2.107 9.169 3.55 19.13.256 27.516-6.395-.485-11.649-3.097-15.46-7.294zm29.558 26.38c-9.352-2.65-21.337-9.446-25.18-17.847 2.976.432 5.041 1.933 7.977 2.119 1.11.072 2.563-.466 3.838-.148 2.54.63 4.685 6.327 6.602 8.447 1.868 2.07 4.114 2.954 5.651 4.841.988.477 2.448.444 2.504 1.927-.428.457-.879.806-1.392.66zm48.681-2.493c-9.707 5.477-26.136 9.596-36.462 4.449-8.331-4.155-19.593-11.027-23.433-19.737 3.587-8.405-1.062-16.106-1.36-24.64-.157-4.54 2.139-8.504 2.315-13.446-1.228-2.025-4.978-2.275-7.574-2.136-.873 4.372-2.403 9.287-6.906 9.78-6.371.697-11.03-4.576-11.319-10.085-.342-6.48 4.978-17.22 12.517-16.475 2.913.287 3.629 3.207 6.802 3.177 1.72-3.432-2.653-4.51-3.103-6.964-.117-.634.363-3.112.642-4.274 1.37-5.658 4.422-12.982 7.427-17.29 3.814-5.464 11.307-6.288 19.37-6.823 1.44 3.101 6.743 2.846 10.2 2.035-4.143 1.64-7.993 5.617-11.185 9.137-3.665 4.039-7.378 8.371-7.566 13.65 6.927-9.61 12.65-18.003 25.246-22.23 9.53-3.196 20.662 1.465 27.986 6.608 3.039 2.137 4.853 5.529 7.013 8.634 8.082 11.626 11.854 28.219 11.024 44.303-.342 6.633-.327 13.244-2.552 17.706-2.326 4.666-10.193 8.84-14.8 4.62-.853 4.537 3.83 7.344 9.331 5.71-3.922 5.063-8.039 11.145-13.614 14.29zm18.084-149.66c7.585 3.77 21.757 10.149 26.512-.014 1.755-3.746 3.814-10.079 4.723-13.946 1.284-5.456-1.392-16.923-7-18.754-4.953-1.617-10.733-1.518-16.7-.32-.702.585-1.484 1.603-2.03 2.665-4.261.165-8.25-.229-11.615-1.98.319-3.15-1.812-3.656-3.81-4.305-1.48-5.872 2.963-13.541 1.9-18.896-.76-3.815-5.453-4.405-8.902-5.118-.113-2.12.15-3.89.386-5.683-.789-2.907-4.327-4.561-7.679-4.967-11.029-1.326-27.775-1.922-38.384 1.893-2.96 7.261-5.292 16.093-7.758 24.384-10.346-1.105-18.715 4.464-26.603 8.113-2.731 1.266-6.51 1.964-7.53 4.138-.99 2.105-.584 6.14-.83 9.95-.625 9.733-1.16 19.12-3.73 29.086-1.154 4.472-3.165 8.418-4.568 12.727C9.358 5.184 7.092 10.12 6.5 14.1c-.877 5.903 4.681 6.232 8.235 8.79 5.494 3.954 9.806 6.142 15.756 9.711 1.762 1.057 7.077 3.733 7.681 4.966 1.202 2.443-2.062 5.888-2.935 7.803-1.38 3.03-2.1 5.602-2.298 8.59-4.992.789-8.775 3.76-11.06 7.109-3.781 5.543-6.403 15.798-3.132 23.599.257.614 1.536 1.822 1.725 2.765.372 1.858-.7 4.329-.768 6.305-.343 10.14 1.716 18.875 8.541 21.932 2.771 11.038 12.688 14.71 22.032 20.195 3.493 2.05 7.343 3.36 11.32 4.824 14.263 5.25 36.15 4.261 47.987-4.692 5.02-3.797 13.044-11.813 15.914-17.617 7.58-15.323 7.042-40.931 1.74-59.571-.712-2.503-1.746-6.181-3.19-9.187-1.006-2.1-4.134-6.3-3.754-8.153.391-1.916 7.132-7.034 8.577-8.428 2.603-2.51 7.548-5.843 7.948-9.012.43-3.372-1.485-7.984-2.456-11.238-3.245-10.858-6.412-20.895-10.091-30.576'/><path fill='#f7e4cd' d='M73.674 57.38c.411.548 2.674 1.38 5.84-.144 0 0-3.752-.626-3.44-6.881l-1.564.313s-1.615 5.672-.836 6.712'/><path fill='#1d1919' d='M101.09 3.617a1.72 1.72 0 1 0-3.44.001 1.72 1.72 0 0 0 3.44-.001m1.72-7.972a1.72 1.72 0 1 0-3.44 0 1.72 1.72 0 0 0 3.44 0'/></g></g></svg>",
+ "jest": "<svg viewBox='0 0 32 32'><path fill='#f4511e' d='m21.032 8-1.878 4L20 13.998h2L22.928 12z'/><path fill='#f4511e' d='m14 2 2 8h2l3.032-6L24 10h2l2-8zm14 18h-2a4.34 4.34 0 0 1-4-4h-2a4.17 4.17 0 0 1-4.23 3.87c-1.522 2.38-5.155 4.283-7.77 5.148A4.724 4.724 0 0 1 5 20H4c-4.718 7.978 3.064 13.219 10.955 7.895C18.85 24.497 29.658 27.487 28 20'/><circle cx='7' cy='15' r='3' fill='#f4511e'/><circle cx='27' cy='15' r='3' fill='#f4511e'/><circle cx='16' cy='16' r='2' fill='#f4511e'/></svg>",
+ "jinja": "<svg viewBox='0 0 32 32'><path fill='#bdbdbd' d='M30 8V4a94.4 94.4 0 0 1-14 1A94.4 94.4 0 0 1 2 4v4s1.482.247 3.95.495L5.4 14H2v2h3.2L4 28h6V16h12v12h6l-1.2-12H30v-2h-3.4l-.55-5.505C28.517 8.247 30 8 30 8m-20 6V8.817c1.235.074 2.576.13 4 .16V14Zm12 0h-4V8.977a104 104 0 0 0 4-.16Z'/></svg>",
+ "jinja_light": "<svg viewBox='0 0 32 32'><path fill='#616161' d='M30 8V4a94.4 94.4 0 0 1-14 1A94.4 94.4 0 0 1 2 4v4s1.482.247 3.95.495L5.4 14H2v2h3.2L4 28h6V16h12v12h6l-1.2-12H30v-2h-3.4l-.55-5.505C28.517 8.247 30 8 30 8m-20 6V8.817c1.235.074 2.576.13 4 .16V14Zm12 0h-4V8.977a104 104 0 0 0 4-.16Z'/></svg>",
+ "jsconfig": "<svg viewBox='0 0 32 32'><path fill='#757575' d='M15 2H6a2.006 2.006 0 0 0-2 2v22a2.006 2.006 0 0 0 2 2h6v-4H6v-2h6v-2H6v-2h6v-2H6v-2h6v-2h2V4l8 8h2v-1Z' data-mit-no-recolor='true'/><path fill='#ffca28' d='M12 12v18h18V12zm8 12a2.006 2.006 0 0 1-2 2h-2a2.006 2.006 0 0 1-2-2v-2h2v2h2v-8h2zm8-6h-4v2h2a2.006 2.006 0 0 1 2 2v2a2.006 2.006 0 0 1-2 2h-4v-2h4v-2h-2a2.006 2.006 0 0 1-2-2v-2a2.006 2.006 0 0 1 2-2h4z'/></svg>",
+ "json": "<svg viewBox='0 -960 960 960'><path fill='#f9a825' d='M560-160v-80h120q17 0 28.5-11.5T720-280v-80q0-38 22-69t58-44v-14q-36-13-58-44t-22-69v-80q0-17-11.5-28.5T680-720H560v-80h120q50 0 85 35t35 85v80q0 17 11.5 28.5T840-560h40v160h-40q-17 0-28.5 11.5T800-360v80q0 50-35 85t-85 35zm-280 0q-50 0-85-35t-35-85v-80q0-17-11.5-28.5T120-400H80v-160h40q17 0 28.5-11.5T160-600v-80q0-50 35-85t85-35h120v80H280q-17 0-28.5 11.5T240-680v80q0 38-22 69t-58 44v14q36 13 58 44t22 69v80q0 17 11.5 28.5T280-240h120v80z'/></svg>",
+ "jsr": "<svg viewBox='0 0 16 16'><path fill='#fdd835' d='M2 7h1v2h1V5h1v5H2m4-5h4v1H7v1.5h3V11H6v-1h3V8.5H6M11 6h3v2.5h-1V7h-1v4h-1'/></svg>",
+ "jsr_light": "<svg viewBox='0 0 16 16'><path fill='#37474f' d='M1 6h2V4h8v1h4v5h-2v2H5v-1H1'/><path fill='#fdd835' d='M2 7h1v2h1V5h1v5H2m4-5h4v1H7v1.5h3V11H6v-1h3V8.5H6M11 6h3v2.5h-1V7h-1v4h-1'/></svg>",
+ "julia": "<svg viewBox='0 0 50 50'><g transform='translate(.21 -247.01)'><circle cx='13.497' cy='281.63' r='9.555' fill='#c62828'/><circle cx='36.081' cy='281.63' r='9.555' fill='#7e57c2'/><circle cx='24.722' cy='262.39' r='9.555' fill='#388e3c'/></g></svg>",
+ "jupyter": "<svg viewBox='0 0 32 32'><path fill='#f57c00' d='M6.2 18a22.7 22.7 0 0 0 9.8 2 22.7 22.7 0 0 0 9.8-2 10.002 10.002 0 0 1-19.6 0m19.6-4a22.7 22.7 0 0 0-9.8-2 22.7 22.7 0 0 0-9.8 2 10.002 10.002 0 0 1 19.6 0'/><circle cx='27' cy='5' r='3' fill='#757575'/><circle cx='5' cy='27' r='3' fill='#9e9e9e'/><circle cx='5' cy='5' r='3' fill='#616161'/></svg>",
+ "just": "<svg fill='none' viewBox='0 0 16 16'><path fill='#ef5350' d='M8 1a7 7 0 0 0-7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0-7-7m0 1a2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2m0 8a2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2'/></svg>",
+ "karma": "<svg viewBox='0 0 64 64'><path fill='#009688' d='m37.275 55.618-20.29-26.686 9.53-7.247 20.29 26.686h.003l5.527 7.247z'/><path fill='#4db6ac' d='M34.4 8.378 23.638 22.533V8.403H11.665V22.22l7.84 33.234h4.132V42.308l.003.003 20.29-26.686-.008-.006 5.504-7.24H34.558v.12z'/></svg>",
+ "kcl": "<svg viewBox='0 0 24 24'><g stroke-width='.948'><path fill='#9ccc65' d='m2 3 5.087 1.007L9 12l-5-1Z'/><path fill='#e91e63' d='m3.957 11.996 5.087 1.007 1.912 7.993-5-1z'/><path fill='#26c6da' d='m10 13 5-2 7 5-5 2z'/><path fill='#ffcc80' d='m10 12 7-3 1-5-7 3z'/></g></svg>",
+ "key": "<svg viewBox='0 0 32 32'><path fill='#26a69a' d='M30 14H17.738a8 8 0 1 0 0 4H24v4h4v-4h2Zm-20 5a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3'/></svg>",
+ "keystatic": "<svg viewBox='0 0 32 32'><defs><linearGradient id='a' x1='385.222' x2='405.918' y1='482.514' y2='477.914' gradientTransform='matrix(.75 0 0 .75 -284.775 -343.25)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#78909c' stop-opacity='.2'/><stop offset='1' stop-color='#78909c'/></linearGradient></defs><path fill='#78909c' d='m17.714 9-3.428 14-1.715 7L28 14ZM4 18 19.429 2l-1.715 7Z'/><path fill='url(#a)' d='M17.714 9 4 18l10.286 5Z'/></svg>",
+ "kivy": "<svg viewBox='0 0 24 24'><path fill='#90a4ae' d='M1.123 4.881v8.456l3.643-3.643a.825.825 0 0 0 0-1.17zm4.45 14.066v-8.456L1.93 14.134a.825.825 0 0 0 0 1.17zM22.952 8.25 12.848 9.316l-.033.034 4.518 4.518zM6.595 4.64s.044 6.245-.006 8.414c-.037 1.619.703 2.106 1.41 2.848 1.018 1.067 3.022 2.968 3.022 2.968.396.39 1.025.393 1.418 0l3.485-3.485a1 1 0 0 0 0-1.417z'/></svg>",
+ "kl": "<svg viewBox='0 0 24 24'><path fill='#29b6f6' d='M12 1.703c-.25-.002-.5.11-.729.338a5356 5356 0 0 0-9.226 9.235c-.144.144-.229.347-.341.522v.41c.16.223.294.474.485.666a3260 3260 0 0 0 8.936 8.937c.193.192.443.325.666.486h.41c.205-.142.436-.256.609-.429q4.569-4.56 9.133-9.126c.47-.47.472-1.005.006-1.472L12.73 2.052c-.23-.23-.48-.346-.731-.349zM10.938 6.25l1.386.832q1.052.635 2.109 1.262l.04.026.016.013.017.013c.061.056.089.122.088.224a510 510 0 0 0 0 3.793.5.5 0 0 1-.007.094c-.015.07-.054.104-.142.11l-.044.001-.136-.003c-.323-.005-.648 0-.998 0v-2.543l.004-.668c0-.146-.039-.23-.17-.307-.893-.528-1.78-1.067-2.67-1.6-.051-.03-.101-.065-.173-.112l.001-.002-.001-.001zm.362 3.39c.068-.003.119.042.173.138q.128.22.264.439l.015.025q.145.231.292.47l-1.915 1.176c-.337.208-.673.418-1.014.618-.113.066-.154.143-.154.277.01.977.01 1.954.014 2.932v.253H7.664c-.004-.054-.014-.112-.014-.17-.005-1.25-.006-2.502-.015-3.752 0-.14.045-.22.164-.293a467 467 0 0 0 3.353-2.055l.016-.009.032-.018.016-.007.033-.013.012-.004.028-.005.01-.002zm5.677 3.125.314.54.346.595v.001c-.158.094-.298.177-.438.258l-3.097 1.798c-.106.062-.189.072-.3.01l-.893-.495-1.524-.843-.895-.493c-.035-.02-.068-.044-.129-.085h.001v-.001l.137-.25.495-.902 1.446.795c.442.244.886.484 1.323.735.121.069.212.071.334 0 .894-.526 1.792-1.044 2.689-1.563.057-.034.118-.062.191-.1'/></svg>",
+ "knip": "<svg viewBox='0 0 24 24'><path fill='#ef6c00' d='m18.957 2.998-5.985 5.984 1.995 1.995 6.983-6.982v-.998m-9.975 9.477a.5.5 0 1 1 0-.998.5.5 0 0 1 0 .998M5.99 19.955a1.995 1.995 0 1 1 0-3.99 1.995 1.995 0 0 1 0 3.99m0-11.97a1.995 1.995 0 1 1 0-3.99 1.995 1.995 0 0 1 0 3.99m3.63-.36a3.9 3.9 0 0 0 .36-1.635 3.99 3.99 0 1 0-3.99 3.99c.589 0 1.137-.13 1.636-.36l2.354 2.355-2.354 2.354a3.9 3.9 0 0 0-1.636-.359 3.99 3.99 0 1 0 3.99 3.99c0-.588-.13-1.137-.36-1.636l2.355-2.354 6.982 6.982h2.993v-.997z'/></svg>",
+ "kotlin": "<svg viewBox='0 0 24 24'><defs><linearGradient id='a' x1='1.725' x2='22.185' y1='22.67' y2='1.982' gradientTransform='translate(1.306 1.129)scale(.89324)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#7c4dff'/><stop offset='.5' stop-color='#d500f9'/><stop offset='1' stop-color='#ef5350'/></linearGradient></defs><path fill='url(#a)' d='M2.975 2.976v18.048h18.05v-.03l-4.478-4.511-4.48-4.515 4.48-4.515 4.443-4.477z'/></svg>",
+ "kubernetes": "<svg viewBox='0 0 24 24'><path fill='#448aff' d='M12.074 1.424a.638.638 0 0 0-.686.691v.173c.015.2.044.402.087.588.058.358.085.73.07 1.102a.65.65 0 0 1-.201.33l-.014.258a7 7 0 0 0-1.117.17 7.9 7.9 0 0 0-4.012 2.292l-.213-.157a.56.56 0 0 1-.374-.042 6 6 0 0 1-.829-.747c-.129-.143-.26-.299-.403-.428l-.128-.1a.8.8 0 0 0-.431-.171c-.2 0-.372.07-.501.212-.2.301-.127.675.16.904l.013.014c.03.029.087.072.115.1q.258.172.515.3c.33.186.631.403.918.647a.63.63 0 0 1 .114.358l.2.17a7.82 7.82 0 0 0-1.232 5.546l-.271.07a.84.84 0 0 1-.275.274c-.358.086-.73.17-1.102.17-.186 0-.387 0-.588.057l-.156.03h-.014v.015c-.043 0-.086.014-.115.014a.62.62 0 0 0-.4.789.625.625 0 0 0 .772.386 1 1 0 0 0 .188-.045c.186-.057.37-.127.528-.213a7.4 7.4 0 0 1 1.103-.316c.114 0 .244.057.33.129l.285-.042a8.04 8.04 0 0 0 3.54 4.426l-.1.258a.8.8 0 0 1 .044.358c-.143.344-.33.687-.56.987-.114.172-.215.33-.344.501 0 .043.001.117-.056.16-.014.043-.044.072-.059.114a.615.615 0 0 0 .372.787.62.62 0 0 0 .79-.372c.028-.043.055-.143.083-.143.072-.2.131-.387.174-.574a5.4 5.4 0 0 1 .473-1.102.5.5 0 0 1 .271-.129l.143-.257c1.82.702 3.84.701 5.688.014l.115.23a.53.53 0 0 1 .3.198c.186.33.301.674.43 1.032.043.187.102.373.174.588.028 0 .055.086.084.129.014.043.03.071.044.114a.61.61 0 0 0 .845.216.614.614 0 0 0 .213-.845c-.014-.057-.056-.13-.056-.174-.115-.157-.23-.329-.344-.486-.215-.316-.371-.63-.543-.974a.48.48 0 0 1 .042-.372 1.2 1.2 0 0 1-.1-.244c1.661-1.002 2.951-2.577 3.539-4.454.086.014.17.028.271.042.086-.115.201-.115.33-.115.387.057.73.16 1.103.302q.235.128.514.213c.058.014.116.03.202.03v.015c0 .014.058.013.1.028.344.043.617-.202.689-.532a.617.617 0 0 0-.532-.7c-.057-.014-.127-.03-.17-.072h-.588a7 7 0 0 1-1.102-.202.6.6 0 0 1-.274-.257l-.272-.07a7.8 7.8 0 0 0-1.262-5.531l.23-.2a.44.44 0 0 1 .114-.343c.273-.244.589-.46.918-.647a3.6 3.6 0 0 0 .5-.3 1 1 0 0 0 .13-.1c.043-.028.086-.058.086-.087.258-.243.273-.63 0-.859-.214-.257-.601-.257-.845 0-.043 0-.1.059-.142.087a11 11 0 0 0-.403.428c-.244.272-.53.532-.831.747a.55.55 0 0 1-.372.042l-.23.171a7.98 7.98 0 0 0-5.098-2.462l-.014-.274a.5.5 0 0 1-.201-.314 5.6 5.6 0 0 1 .07-1.102 4 4 0 0 0 .087-.588v-.316a.62.62 0 0 0-.546-.548m-.842 4.773-.174 3.223h-.014a.56.56 0 0 1-.114.302.543.543 0 0 1-.745.13h-.014L7.536 7.973a6.23 6.23 0 0 1 3.035-1.662c.23-.043.446-.086.66-.115zm1.544 0a6.38 6.38 0 0 1 3.682 1.777L13.837 9.85h-.014a.66.66 0 0 1-.3.073.535.535 0 0 1-.56-.518zm-6.2 2.938 2.406 2.19v.015c.086.071.157.16.157.274a.523.523 0 0 1-.372.657v.014l-3.095.89a6.4 6.4 0 0 1 .904-4.04m10.842.042c.73 1.189 1.032 2.595.932 3.984l-3.109-.89-.014-.014a.5.5 0 0 1-.257-.17.53.53 0 0 1 .056-.761l-.014-.042zm-5.915 2.322h.988l.615.758-.215.96-.887.431-.887-.43-.23-.96zm-2.308 2.65h.115c.243 0 .46.17.545.414 0 .1.001.23-.056.302v.042l-1.22 2.966a6.33 6.33 0 0 1-2.563-3.195zm5.274 0h.344l3.193.515a6.34 6.34 0 0 1-2.563 3.223l-1.234-3.022c-.115-.258.002-.558.26-.716m-2.521 1.298a.55.55 0 0 1 .529.277h.014l1.561 2.823a5 5 0 0 1-.615.171 6.4 6.4 0 0 1-3.481-.17l1.561-2.824h.014c.043-.1.115-.144.216-.215a.5.5 0 0 1 .201-.062'/></svg>",
+ "kusto": "<svg viewBox='0 0 16 16'><path fill='#1e88e5' d='M10 1C7.23 1 5 3.23 5 6v5h5c2.77 0 5-2.23 5-5s-2.23-5-5-5m1.5 3H13v5h-1.5zm-5 1H8v4H6.5zM9 6h1.5v3H9zM1 8v3h3V8zm0 4v2.498a.5.5 0 0 0 .502.502H4v-3zm4 0v3h3v-3z'/></svg>",
+ "label": "<svg viewBox='0 0 16 16'><path fill='#ffb300' d='m14.709 8.558-7.27-7.247a1 1 0 0 0-.893-.297l-4 .731c-.399.074-.714.38-.8.777l-.732 4.024c-.055.328.057.662.297.892l7.247 7.27c.393.39 1.025.39 1.417 0l4.734-4.733a1.006 1.006 0 0 0 0-1.417m-8.981-2.8c-1.434 1.554-3.65-.764-2.117-2.214 1.411-1.378 3.467.704 2.15 2.178z'/></svg>",
+ "laravel": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='M31.963 9.12c-.008-.03-.023-.056-.034-.085a1 1 0 0 0-.07-.156 2 2 0 0 0-.162-.205 1 1 0 0 0-.088-.072 1 1 0 0 0-.083-.068l-.044-.02-.035-.024-6-3a1 1 0 0 0-.894 0l-6 3-.035.024-.044.02a1 1 0 0 0-.083.068.7.7 0 0 0-.187.191 1 1 0 0 0-.064.086 1 1 0 0 0-.069.156c-.01.029-.026.055-.034.085a1 1 0 0 0-.037.265v5.382l-4 2V5.385a1 1 0 0 0-.037-.265c-.008-.03-.023-.056-.034-.085a1 1 0 0 0-.07-.156 1 1 0 0 0-.063-.086.7.7 0 0 0-.187-.191 1 1 0 0 0-.083-.068l-.044-.02-.035-.024-6-3a1 1 0 0 0-.894 0l-6 3-.035.024-.044.02a1 1 0 0 0-.083.068 1 1 0 0 0-.088.072 1 1 0 0 0-.1.119 1 1 0 0 0-.063.086 1 1 0 0 0-.069.156c-.01.029-.026.055-.034.085A1 1 0 0 0 0 5.385v19a1 1 0 0 0 .553.894l6 3 6 3c.014.007.03.005.046.011a.9.9 0 0 0 .802 0c.015-.006.032-.004.046-.01l12-6a1 1 0 0 0 .553-.895v-5.382l5.447-2.724a1 1 0 0 0 .553-.894v-6a1 1 0 0 0-.037-.265M9.236 21.385l4.211-2.106h.001L19 16.503l3.764 1.882L13 23.267ZM24 13.003v3.764l-4-2v-3.764Zm1-5.5 3.764 1.882L25 11.267l-3.764-1.882ZM8 19.767V9.003l4-2v10.764ZM7 3.503l3.764 1.882L7 7.267 3.236 5.385Zm-5 3.5 4 2v16.764l-4-2Zm6 16 4 2v3.764l-4-2Zm16 .764-10 5v-3.764l10-5Zm6-9-4 2v-3.764l4-2Z'/></svg>",
+ "latex-class.clone": "<svg viewBox='0 0 1024 1024'><path fill='#e040fb' d='M80 192 64 320h32c16-80 16-96 63.242-96H176c8.837 0 16 7.163 16 16v352c0 8.837 0 16-32 16h-32v32h192v-32h-32c-32 0-32-7.163-32-16V240c0-8.837 7.163-16 16-16h16c48 0 48 16 64 96h32l-16-128zm560 0v32c16 0 45.713 0 52.57 16L776 434.666 708.57 592c-6.857 16-52.57 16-68.57 16v32h128v-32s-34.285 0-27.428-16L792 472l51.428 120c3.103 7.24-1.52 16-11.428 16v32h128v-32c-16 0-45.713 0-52.57-16L824 397.334 891.43 240c6.857-16 52.57-16 68.57-16v-32H832v32s34.285 0 27.428 16L808 360l-51.428-120c-3.103-7.24 1.52-16 11.428-16v-32zM320 384v32h32c32 0 32 7.163 32 16v352c0 8.837 0 16-32 16h-32v32h304l16-128h-32c-16 80-16 96-64 96h-64c-32 0-32-7.163-32-16V624h80c8.837 0 16 0 16 32v16h32V544h-32v16c0 32-7.163 32-16 32h-80V432c0-8.837 0-16 32-16h64c48 0 48 16 64 96h32l-16-128z'/></svg>",
+ "latex-package.clone": "<svg viewBox='0 0 1024 1024'><path fill='#b388ff' d='M80 192 64 320h32c16-80 16-96 63.242-96H176c8.837 0 16 7.163 16 16v352c0 8.837 0 16-32 16h-32v32h192v-32h-32c-32 0-32-7.163-32-16V240c0-8.837 7.163-16 16-16h16c48 0 48 16 64 96h32l-16-128zm560 0v32c16 0 45.713 0 52.57 16L776 434.666 708.57 592c-6.857 16-52.57 16-68.57 16v32h128v-32s-34.285 0-27.428-16L792 472l51.428 120c3.103 7.24-1.52 16-11.428 16v32h128v-32c-16 0-45.713 0-52.57-16L824 397.334 891.43 240c6.857-16 52.57-16 68.57-16v-32H832v32s34.285 0 27.428 16L808 360l-51.428-120c-3.103-7.24 1.52-16 11.428-16v-32zM320 384v32h32c32 0 32 7.163 32 16v352c0 8.837 0 16-32 16h-32v32h304l16-128h-32c-16 80-16 96-64 96h-64c-32 0-32-7.163-32-16V624h80c8.837 0 16 0 16 32v16h32V544h-32v16c0 32-7.163 32-16 32h-80V432c0-8.837 0-16 32-16h64c48 0 48 16 64 96h32l-16-128z'/></svg>",
+ "latex.clone": "<svg viewBox='0 0 1024 1024'><path fill='#00bfa5' d='M80 192 64 320h32c16-80 16-96 63.242-96H176c8.837 0 16 7.163 16 16v352c0 8.837 0 16-32 16h-32v32h192v-32h-32c-32 0-32-7.163-32-16V240c0-8.837 7.163-16 16-16h16c48 0 48 16 64 96h32l-16-128zm560 0v32c16 0 45.713 0 52.57 16L776 434.666 708.57 592c-6.857 16-52.57 16-68.57 16v32h128v-32s-34.285 0-27.428-16L792 472l51.428 120c3.103 7.24-1.52 16-11.428 16v32h128v-32c-16 0-45.713 0-52.57-16L824 397.334 891.43 240c6.857-16 52.57-16 68.57-16v-32H832v32s34.285 0 27.428 16L808 360l-51.428-120c-3.103-7.24 1.52-16 11.428-16v-32zM320 384v32h32c32 0 32 7.163 32 16v352c0 8.837 0 16-32 16h-32v32h304l16-128h-32c-16 80-16 96-64 96h-64c-32 0-32-7.163-32-16V624h80c8.837 0 16 0 16 32v16h32V544h-32v16c0 32-7.163 32-16 32h-80V432c0-8.837 0-16 32-16h64c48 0 48 16 64 96h32l-16-128z'/></svg>",
+ "latexmk": "<svg viewBox='0 0 1024 1024'><path fill='#00bfa5' d='M1024 128q-384 0-576 384L256 896l64-64 64-96c64 0 192-64 224-160-64 16-96-32-96-64 224 0 272-96 352-224 37.924-60.678 80-128 160-160M80 192 64 320h32c16-80 16-96 63.242-96H176c8.837 0 16 7.163 16 16v352c0 8.837 0 16-32 16h-32v32h192v-32h-32c-32 0-32-7.163-32-16V240c0-8.837 7.163-16 16-16h16c48 0 48 16 64 96h32l-16-128z'/></svg>",
+ "lbx": "<svg viewBox='0 0 1024 1024'><path fill='#2e7d32' d='M128 704v128c0 70.692 57.308 128 128 128h608c17.728 0 32-14.272 32-32V704z'/><path fill='#ffe082' d='M704 704v192h128V704z'/><path fill='#fff8e1' d='M192 704v96c0 53.184 42.816 96 96 96h544a96 96 0 0 1-96-96 96 96 0 0 1 96-96z'/><path fill='#ff1744' d='M320 832h192v192l-96-96-96 96z'/><path fill='#4caf50' d='M256 64c-70.692 0-128 57.308-128 128v640c0 11.088 1.557 21.787 4.207 32.047C146.767 807.565 197.672 768.07 256 768h608c17.728 0 32-14.272 32-32V96c0-17.728-14.272-32-32-32z'/><path fill='#e8f5e9' d='M448 128v64H256v64h256c0 33.778-26.676 86.11-73.947 144.959-7.257 9.034-15.068 18.276-23.123 27.611-4.479-5.242-8.838-10.461-13.002-15.634C370.55 373.95 352 336 352 320h-64c0 48 29.45 90.049 64.072 133.064 6.302 7.83 12.933 15.627 19.68 23.39-35.13 37.553-74.249 76.79-114.379 116.919l45.254 45.254c38.924-38.924 77.278-77.307 112.568-114.883 17.182 17.87 34.328 35.033 50.178 50.883l45.254-45.254c-16.799-16.799-34.744-34.801-52.309-53.17 10.301-11.813 20.31-23.56 29.63-35.162C538.675 377.889 576 318.222 576 256h128v-64H512v-64zm192 192L512 704h64l21.334-64h149.332L768 704h64L704 320zm32 96 53.334 160H618.666z'/></svg>",
+ "lefthook": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='M6 16v6H2zm5.106-6.537-3.317 1.775a2.22 2.22 0 0 0-.895 2.873l.333.71L14 11.571v-.193a2.006 2.006 0 0 0-2.894-1.915m18.82 7.545a2 2 0 0 0-.393-.744l-7.89-8.883a2.76 2.76 0 0 0-3.138-.384L16 8v4.559a3.97 3.97 0 0 1-1.566 3.18L16 20l8.457 2.204 4.624-2.979a2 2 0 0 0 .845-2.217'/><path fill='#b71c1c' fill-rule='evenodd' d='m2 22 4-2 4 2-4 2zm12.434-6.262a6 6 0 0 1-1.194.695l-2.544 1.136A6.55 6.55 0 0 1 8 18v.764l9.71 4.855a4.05 4.05 0 0 0 2.343.366 7.8 7.8 0 0 0 2.667-.82 24 24 0 0 0 1.737-.96zm-6.97-1.635 5.829-2.937a.5.5 0 0 1 .712.475c.007.417-.005.871-.005 1.153a2.1 2.1 0 0 1-1.367 2.03l-2.987 1.067c-1.629.581-3.103-1.324-2.182-1.788'/></svg>",
+ "lerna": "<svg viewBox='0 0 24 24'><path fill='#448aff' d='M7.57 8.21c-.41.08-.99.06-1.26.06-.4 0-1.27-.48-1.75-.98-.25-.26-.71-.82-1.01-1.25-.31-.43-.62-.78-.71-.78-.17 0-.28.6-.29 1.6 0 .12-.01.22-.01.31l.01.03-.14 8.47c.27.07.54.13.82.19.58.12 1.06.22 1.08.23.01.02-.19.34-.45.71-.95 1.35-1.14 2-.6 2 .17 0 .53-.07.82-.17.33-.1.93-.16 1.58-.16 1.34 0 2.68.33 4.37 1.08 1.61.71 2.14.88 4 1.25.85.18 1.59.37 1.64.41.05.05-.07.22-.28.38s-.38.33-.38.37.22.04.51-.01c.27-.04.89-.14 1.36-.19 1.43-.18 2.45-.82 2.47-1.56.04-.94-.25-3.03-.43-3.14-.02-.01-.38.11-.46.24-.07.13-.16.33-.25.56-.18.46-.15.56-1.57.66-1.03.05-2.02-.19-2.82-.65-.67-.39-1.66-.74-2.18-1.54-.42-.65-.86-1.72-.78-1.87.03-.04.17-.03.32.02.39.15.96.38 2.73.15 1.54-.2 3-.04 3.39.16s.92.47.94.99c.01.52.33.63.36.63.17-.03.59-1.42.83-1.83.2-.32.31-.5 1.1-1.04.62-.43 1.13-.83 1.11-.9-.01-.06-.36-.47-.77-.92-.65-.7-.83-.82-1.42-1.02-2.05-.67-4.23-2.12-5.36-3.54-1.25-1.58-1.6-1.94-2.5-2.53-1.01-.67-2.32-1.24-3.3-1.43-1-.19-.72.13-.29.8 1.53 1.75 1.49 1.81 1.49 2.97-.16.9-1 1.03-1.92 1.24m3.17 1.03c.17 0 .58.11 1.29.35 1.74.58 1.84.85 2.41 1.85.13.22.22.41.21.42s-.17.05-.35.09c-.45.09-1.16.09-1.58 0-.76-.16-.72-.19-1.11-.57-.16-.16-.35-.42-.41-.56-.04-.1-.03-.12.19-.3.05-.04.1-.11.1-.15 0-.07-.22-.37-.34-.47-.13-.11-.49-.54-.49-.59 0-.03.03-.05.1-.05z'/></svg>",
+ "less": "<svg viewBox='0 0 24 24'><path fill='#0277bd' d='M8 3a2 2 0 0 0-2 2v4a2 2 0 0 1-2 2H3v2h1a2 2 0 0 1 2 2v4a2 2 0 0 0 2 2h2v-2H8v-5a2 2 0 0 0-2-2 2 2 0 0 0 2-2V5h2V3m6 0a2 2 0 0 1 2 2v4a2 2 0 0 0 2 2h1v2h-1a2 2 0 0 0-2 2v4a2 2 0 0 1-2 2h-2v-2h2v-5a2 2 0 0 1 2-2 2 2 0 0 1-2-2V5h-2V3z'/></svg>",
+ "liara": "<svg viewBox='0 0 16 16'><defs><linearGradient id='a' x1='11.328' x2='90.301' y1='8.203' y2='10.761' gradientTransform='matrix(.15939 0 0 .16254 -.134 -.857)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#69f0ae'/><stop offset='1' stop-color='#4fc3f7'/></linearGradient><linearGradient id='b' x1='11.328' x2='90.301' y1='8.203' y2='10.761' gradientTransform='matrix(.15939 0 0 .16253 .195 -.856)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#69f0ae'/><stop offset='1' stop-color='#4fc3f7'/></linearGradient><linearGradient id='c' x1='11.328' x2='90.301' y1='8.203' y2='10.761' gradientTransform='matrix(.15784 0 0 .16493 .15 -.355)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#69f0ae'/><stop offset='1' stop-color='#4fc3f7'/></linearGradient></defs><path fill='url(#a)' d='M8.5 8.811c0-.53.368-1.174.82-1.44l3.86-2.257c.452-.265.82-.052.82.479v4.596c0 .527-.368 1.17-.82 1.436l-3.86 2.261c-.452.265-.82.051-.82-.479zm0 0'/><path fill='url(#b)' d='M2 5.593c0-.53.368-.745.82-.479l3.86 2.258c.452.264.82.909.82 1.44v4.595c0 .53-.368.744-.82.48l-3.86-2.262c-.452-.264-.82-.909-.82-1.44zm0 0'/><path fill='url(#c)' d='M3.336 4.467c-.448-.27-.448-.706 0-.972l3.821-2.293c.447-.27 1.173-.27 1.62 0l3.888 2.332c.447.268.447.702 0 .971L8.843 6.799c-.447.268-1.173.268-1.624 0zm0 0'/></svg>",
+ "lib": "<svg fill='none' viewBox='0 0 24 24'><path d='M0 0h24v24H0z'/><path fill='#8bc34a' d='M4 6H2v14c0 1.1.9 2 2 2h14v-2H4zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2m0 14H8V4h12zM10 9h8v2h-8zm0 3h4v2h-4zm0-6h8v2h-8z'/></svg>",
+ "lighthouse": "<svg viewBox='0 0 24 24'><path fill='#f4511e' d='M8.852 10.182V8.364h.851V4.727h-.851v-.909L12.258 2l3.407 1.818v.91h-.852v3.636h.852v1.818h-1.073L9.226 13.49l.477-3.31h-.851zm4.258-1.818V4.727h-1.703v3.637zM8 22l.034-.218L15.792 17l.443 3.073L13.11 22zm.894-6.21L15.077 12l.443 3.064-7.154 4.409z'/></svg>",
+ "lilypond": "<svg viewBox='0 0 32 32'><path fill='#66bb6a' d='M10 8v11.023A4.986 4.986 0 1 0 11.9 24h.1V11.6l16-3.2v8.623A4.986 4.986 0 1 0 29.9 22h.1V4Z'/></svg>",
+ "liquid": "<svg viewBox='0 0 24 24'><path fill='#29b6f6' d='M12 21.669a6.927 6.927 0 0 1-6.927-6.927C5.073 10.124 12 2.33 12 2.33s6.927 7.793 6.927 12.41A6.927 6.927 0 0 1 12 21.67z'/></svg>",
+ "lisp": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M16 2a14 14 0 1 0 14 14A14.003 14.003 0 0 0 16 2m8.93 20.43a11 11 0 0 1-7.19 4.43 6.094 6.094 0 0 1-4.79-5.9v-.05a5 5 0 0 1 .04-.66 7.95 7.95 0 0 1 2.3-4.95 5.99 5.99 0 0 0-2.23-9.9 11.004 11.004 0 0 1 11.87 17.03'/></svg>",
+ "livescript": "<svg viewBox='0 0 32 32'><path fill='#0277bd' d='M4 2h4v28H4z'/><path fill='#0277bd' d='M2 24h28v4H2zm8-20h2v18h-2zm2 16h16v2H12zm2-4h14v2H14zm0-12h2v12h-2zm4 8h10v2H18zm0-8h2v8h-2zm4 4h6v2h-6zm0-4h2v4h-2z'/><path fill='#0277bd' d='M6 24.586 23.271 7.315l1.414 1.414L7.415 26z'/></svg>",
+ "lock": "<svg viewBox='0 0 32 32'><path fill='#ffd54f' d='M25 12h-3V8a6 6 0 0 0-12 0v4H7a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h18a1 1 0 0 0 1-1V13a1 1 0 0 0-1-1M14 8a2 2 0 0 1 4 0v4h-4Zm2 17a4 4 0 1 1 4-4 4 4 0 0 1-4 4'/></svg>",
+ "log": "<svg fill='none' viewBox='0 0 24 24'><path d='M0 0h24v24H0z'/><path fill='#afb42b' d='M19 5v9h-5v5H5V5zm0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h10l6-6V5c0-1.1-.9-2-2-2m-7 11H7v-2h5zm5-4H7V8h10z'/></svg>",
+ "lolcode": "<svg viewBox='0 0 24 24'><path fill='#ef5350' d='m12 7.687-1.35.091c-.872-1.035-3.318-3.643-5.754-3.643 0 0-1.999 3.004-.04 7.013-.559.842-.904 1.278-.975 2.283l-1.958.294.213.995 1.786-.264.142.72-1.593.954.477.904 1.471-.904c1.167 2.477 4.12 3.735 7.581 3.735 3.46 0 6.414-1.258 7.58-3.735l1.472.904.477-.904-1.593-.953.142-.721 1.786.264.213-.995-1.958-.294c-.071-1.005-.416-1.441-.975-2.283 1.959-4.009-.04-7.013-.04-7.013-2.436 0-4.881 2.608-5.754 3.643zm-3.044 3.044a1.015 1.015 0 0 1 1.014 1.015 1.015 1.015 0 0 1-1.014 1.015 1.015 1.015 0 0 1-1.015-1.015 1.015 1.015 0 0 1 1.015-1.015m6.089 0a1.015 1.015 0 0 1 1.014 1.015 1.015 1.015 0 0 1-1.014 1.015 1.015 1.015 0 0 1-1.015-1.015 1.015 1.015 0 0 1 1.015-1.015m-4.06 3.045h2.03l-.71 1.41c.203.65.77 1.127 1.471 1.127a1.52 1.52 0 0 0 1.522-1.522h.508a2.03 2.03 0 0 1-2.03 2.03c-.761 0-1.42-.416-1.776-1.015a2.07 2.07 0 0 1-1.776 1.015 2.03 2.03 0 0 1-2.03-2.03h.508a1.52 1.52 0 0 0 1.522 1.522c.7 0 1.268-.477 1.471-1.126z'/></svg>",
+ "lottie": "<svg viewBox='0 0 32 32'><path fill='#00bfa5' d='M2 4v24a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2m20.237 8.11c-2.974.426-3.518 2.058-4.34 4.523-.92 2.764-2.145 6.436-7.635 7.217a1.996 1.996 0 1 1-.499-3.96c2.974-.426 3.518-2.058 4.34-4.523.92-2.764 2.145-6.436 7.635-7.217a1.996 1.996 0 1 1 .499 3.96'/></svg>",
+ "ltx.clone": "<svg viewBox='0 0 1024 1024'><path fill='#00bfa5' d='M80 192 64 320h32c16-80 16-96 63.242-96H176c8.837 0 16 7.163 16 16v352c0 8.837 0 16-32 16h-32v32h192v-32h-32c-32 0-32-7.163-32-16V240c0-8.837 7.163-16 16-16h16c48 0 48 16 64 96h32l-16-128zm560 0v32c16 0 45.713 0 52.57 16L776 434.666 708.57 592c-6.857 16-52.57 16-68.57 16v32h128v-32s-34.285 0-27.428-16L792 472l51.428 120c3.103 7.24-1.52 16-11.428 16v32h128v-32c-16 0-45.713 0-52.57-16L824 397.334 891.43 240c6.857-16 52.57-16 68.57-16v-32H832v32s34.285 0 27.428 16L808 360l-51.428-120c-3.103-7.24 1.52-16 11.428-16v-32zM320 384v32h32c32 0 32 7.163 32 16v352c0 8.837 0 16-32 16h-32v32h304l16-128h-32c-16 80-16 96-64 96h-64c-32 0-32-7.163-32-16V624h80c8.837 0 16 0 16 32v16h32V544h-32v16c0 32-7.163 32-16 32h-80V432c0-8.837 0-16 32-16h64c48 0 48 16 64 96h32l-16-128z'/></svg>",
+ "lua": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='M30 6a3.86 3.86 0 0 1-1.167 2.833 4.024 4.024 0 0 1-5.666 0A3.86 3.86 0 0 1 22 6a3.86 3.86 0 0 1 1.167-2.833 4.024 4.024 0 0 1 5.666 0A3.86 3.86 0 0 1 30 6m-9.208 5.208A10.6 10.6 0 0 0 13 8a10.6 10.6 0 0 0-7.792 3.208A10.6 10.6 0 0 0 2 19a10.6 10.6 0 0 0 3.208 7.792A10.6 10.6 0 0 0 13 30a10.6 10.6 0 0 0 7.792-3.208A10.6 10.6 0 0 0 24 19a10.6 10.6 0 0 0-3.208-7.792m-1.959 7.625a4.024 4.024 0 0 1-5.666 0 4.024 4.024 0 0 1 0-5.666 4.024 4.024 0 0 1 5.666 0 4.024 4.024 0 0 1 0 5.666'/></svg>",
+ "luau": "<svg fill='none' viewBox='0 0 24 24'><path fill='#03a9f4' d='M22.495 6.331 6.33 2 2 18.164l16.164 4.33z'/><path fill='#fafafa' d='M19.933 7.81 16.7 6.944l-.866 3.233 3.233.866z'/></svg>",
+ "lyric": "<svg viewBox='0 0 32 32'><path fill='#50c5ef' d='M26 7.7c0-.1-.1-.2-.2-.3l-5.2-5.2c-.1-.1-.3-.2-.4-.2h-.1q-.15 0 0 0H7.7c-1 .1-1.7.8-1.7 1.8v24.5c0 1 .8 1.7 1.7 1.7h16.6c1 0 1.7-.8 1.7-1.7zq0 .15 0 0M22 12h-4v8c0 2.2-1.8 4-4 4s-4-1.8-4-4 1.8-4 4-4c.7 0 1.4.2 2 .6V8h6z'/></svg>",
+ "makefile": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='m29.5 24.02-1.6-.92a4.4 4.4 0 0 0 .09-.9A1.3 1.3 0 0 0 28 22a5.6 5.6 0 0 0-.1-1.1l1.6-.92a.493.493 0 0 0 .18-.68l-1.5-2.6a.45.45 0 0 0-.18-.18V6.01a2.006 2.006 0 0 0-2-2H4a2.006 2.006 0 0 0-2 2V22a2.006 2.006 0 0 0 2 2h10.53l-.03.02a.493.493 0 0 0-.18.68l1.5 2.6a.493.493 0 0 0 .68.18l1.6-.92a5.9 5.9 0 0 0 1.9 1.09v1.85a.495.495 0 0 0 .5.5h3a.495.495 0 0 0 .5-.5v-1.85a5.9 5.9 0 0 0 1.9-1.09l1.6.92a.493.493 0 0 0 .68-.18l1.5-2.6a.493.493 0 0 0-.18-.68M24 22.01a1.99 1.99 0 0 1-.88 1.65l-.18.11a2.04 2.04 0 0 1-1.88 0l-.18-.11a1.99 1.99 0 0 1-.88-1.65V22a2 2 0 0 1 .88-1.66l.18-.11a2.04 2.04 0 0 1 1.88 0l.18.11A2 2 0 0 1 24 22Zm2-4.63-.1.06a5.9 5.9 0 0 0-1.9-1.09V14.5a.495.495 0 0 0-.5-.5h-3a.495.495 0 0 0-.5.5v1.85a5.9 5.9 0 0 0-1.9 1.09l-1.6-.92a.493.493 0 0 0-.68.18l-1.5 2.6a.493.493 0 0 0 .18.68l1.6.92A5.6 5.6 0 0 0 16 22v.01L4 22V10.01h22Z'/></svg>",
+ "markdoc-config": "<svg viewBox='0 0 32 32'><path fill='#757575' d='M15 2H6a2.006 2.006 0 0 0-2 2v22a2.006 2.006 0 0 0 2 2h6v-4H6v-2h6v-2H6v-2h6v-2H6v-2h6v-2h2V4l8 8h2v-1Z'/><path fill='#ffb300' d='M12 12v18h18V12Zm10 14h-2v-6l-2 2-2-2v6h-2V16h2l2 2 2-2h2Zm6 2h-4V14h4Z'/></svg>",
+ "markdoc": "<svg viewBox='0 0 32 32'><path fill='#78909c' d='m14 10-4 3.5L6 10H4v12h4v-6l2 2 2-2v6h4V10Z'/><rect width='6' height='16' x='22' y='8' fill='#ffb300' rx='.5'/></svg>",
+ "markdown": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='m14 10-4 3.5L6 10H4v12h4v-6l2 2 2-2v6h4V10zm12 6v-6h-4v6h-4l6 8 6-8z'/></svg>",
+ "markdownlint": "<svg viewBox='0 0 16 16'><path fill='#42a5f5' d='M6 5 4 6.75 2 5H1v6h2V8l1 1 1-1v3h2V5zm4.73 3.975L10 8H8l2 3h2l3-6h-2z'/></svg>",
+ "markojs": "<svg viewBox='0 0 32 32'><path fill='#2196f3' d='M5.287 8.001c-.873 1.333-1.76 2.663-2.634 3.995C1.779 13.32.909 14.665.035 15.998L.038 16l-.003.002c.874 1.333 1.744 2.678 2.618 4 .873 1.332 1.76 2.665 2.634 4h4.44c-.885-1.333-1.757-2.667-2.632-4C6.225 18.67 5.353 17.33 4.481 16c.872-1.33 1.744-2.674 2.614-4.004q1.31-2.001 2.632-3.995Z'/><path fill='#26a69a' d='m5.283 8.001-.002.001c.873 1.333 1.746 2.68 2.618 4 .874 1.333 1.76 2.665 2.632 3.998h4.442a703 703 0 0 1-2.632-3.998c-.871-1.33-1.742-2.67-2.614-4.002Z'/><path fill='#8bc34a' d='m20.222 8.001.003.001c-.874 1.333-1.747 2.68-2.62 4-.874 1.333-1.759 2.665-2.632 3.998h-4.44q1.322-1.995 2.63-3.998c.872-1.33 1.743-2.67 2.615-4.002Z'/><path fill='#ffc107' d='M15.783 8.001q1.323 1.994 2.632 3.995c.871 1.33 1.743 2.674 2.615 4.004-.872 1.33-1.744 2.671-2.615 4.001Q17.106 22.004 15.783 24h4.44c.874-1.334 1.759-2.666 2.632-3.999.874-1.32 1.747-2.665 2.622-3.997L25.474 16l.003-.002c-.874-1.333-1.747-2.68-2.622-4.002-.873-1.332-1.758-2.664-2.632-3.996Z'/><path fill='#f44336' d='M22.271 8.001q1.323 1.994 2.632 3.995c.871 1.33 1.74 2.674 2.614 4.004-.874 1.33-1.743 2.671-2.614 4.001-.873 1.333-1.746 2.666-2.632 4h4.44c.874-1.332 1.759-2.666 2.632-3.999.874-1.322 1.747-2.667 2.622-4L31.962 16l.003-.002c-.874-1.333-1.747-2.68-2.622-4.002-.873-1.332-1.758-2.664-2.632-3.996Z'/></svg>",
+ "mathematica": "<svg viewBox='0 0 23 24'><path fill='#f44336' stroke='#f44336' stroke-linejoin='round' stroke-width='.775' d='m11.498 1.525-.073.025-.46.794-.454.763-1.218 2.089h-.017L5.42 3.5l-.1-.047h-.018v.092l.025.163v.086l.132 1.227v.082l.032.252v.082l.22 2.137v.075l.018.082v.06l-2.349.506-.04.015-.456.1-.025.01h-.043l-1.095.245-.04.007-.17.035v.082l.018.01 1.859 2.086.052.053.115.132.804.908v.006l-.053.05-.22.256-2.564 2.876-.01.007v.082l.071.006.295.074 1.697.367v.006l2.14.471h.014v.047l-.035.252v.08l-.047.412v.082l-.035.245v.082l-.046.412v.08l-.05.409v.08l-.036.245v.082l-.046.412v.082l-.05.407v.082l-.032.248v.082l-.05.407v.104h.037l3.642-1.6.294-.135h.018l.177.312.539.912.015.032.854 1.465.16.262.404.694.007.022h.092l.005-.022.017-.025.56-.946.015-.043.599-1.032.316-.54.645-1.09.05.012 3.905 1.722h.035v-.085l-.138-1.32v-.082l-.032-.245V18.7l-.035-.244v-.085l-.033-.245v-.08l-.032-.245v-.082l-.032-.245v-.085l-.035-.244v-.082l-.032-.245v-.082l-.033-.245v-.085l-.025-.17v-.052l1.632-.355.043-.007.458-.107h.028v-.01l.23-.05.03-.01h.042l.382-.09.025-.01h.043l.194-.05h.033l1.015-.23.07-.007v-.065l-.015-.013-1.19-1.342-.028-.027-.197-.22-1.428-1.604v-.006l.295-.324.4-.457 2.148-2.408.015-.01v-.065l-.035-.007-1.288-.28-.372-.085-.047-.01-2.481-.544v-.045l.432-4.264v-.02h-.042l-.302.134-.01.015h-.025L14.01 5.06l-.297.135h-.015L11.67 1.712l-.099-.145-.014-.045zm-.002 1.113 1.366 2.324.34.591-.008.025-1.18 1.512-.518.66-.011-.011-.258-.334-.04-.05L9.79 5.568l.03-.063 1.378-2.366.287-.49zm4.91 2.04-.008.024-.169.226-.537.066zm-9.818.004.052.02.677.299H6.82l-.224-.299zM16.933 5l-.123 1.248-.113-.927.225-.308zm-9.26.157.053.023.705.31-.758-.175zm7.387.115.02.169-1.317.403.002-.003.16-.072 1.015-.444zM9.655 6.39l.944 1.204v.01l-1.13-.403zm3.55.171.209.683-.233.083-.09.022-.7.255.006-.022.777-.981zm-5 .836.986.356.898.312.048.021 1.053.372.012 3.086-.362-.117-.67-.224-.081-.038-.736-.245-.769-.256-.291-.1-.01-.254-.033-1.196-.01-.287-.014-.893-.014-.298zm6.583 0-.012.228-.028.9-.007.302-.032 1.475-.01.262-.337.118-.734.244-.77.257-.713.244-.354.117.01-3.086 1.632-.577zm.584.437.09.735.79-.096-.915 1.302-.017.006.01-.183.018-.878zm-9.451.536.152.22 1.448 2.05-2.608.967-.05.015L2.892 9.41l-.28-.312.003-.01.114-.018.425-.1.14-.022.336-.077.042-.01zm11.146.003 3.284.713.03.01-.023.026-1.954 2.191-.276.312-.093-.035-2.563-.95.474-.682.153-.215zm-10.295.15h.861l.035 1.258-.013-.006-.763-1.078zm1.358 2.624.152.06.77.252.713.245.745.248.49.167-.065.092-1.723 2.333-1.015-.301-.082-.018-.035-.015-1.901-.56.937-1.22.982-1.277zm6.73 0 .033.006 1.787 2.328.132.17-.127.035-.033.015-2.195.641-.106.033-.564.17-.017-.003-1.054-1.44-.174-.24-.546-.726-.008-.017.47-.16.768-.255.714-.244.769-.252zm-7.765.305-.008.02-.404.524-.291-.292.657-.245zm8.802 0 .042.008.579.212.713.27-.66.394-.375-.48-.03-.042-.262-.341zm-10.843.75-.67.668.355-.397.206-.23zm12.91.016.068.025.045.043.554.627.043.042.203.229-.255.134zm-6.473.265.022.015 1.38 1.872.032.05.343.466.008.03-.088.118-.422.628-.047.075-.245.343-.97 1.43-.013.007-1.18-1.72-.095-.16-.494-.709-.007-.036 1.617-2.192.007-.01zm7.827 1.194.566.633.063.082-.273-.092-.036-.013zm-15.785.148.298.299-.637.218-.153.05.038-.057zm13.225.47-.855.449.346.659-.185-.057-.27-.088-1.092-.349.012-.01zm-9.687.255 1.222.356-.006.008-.458.145-.443.134-.032.01-.49.157zm-2.765.049.318.319 2.007.517-.567.18-.055.005-2.103-.47-.744-.156.007-.006zm14.966.205.548.187v.003l-.457.1-.043.014-1.07.23zM9.04 15.31l.007.227.01.347.025 1.362.025.691-.007.255-.24.107-2.863 1.256.033-.372.032-.255.017-.227.031-.257.037-.407.045-.419.018-.23.032-.252.032-.411.05-.415.013-.14 1.455-.456.003-.015.302-.098zm4.909 0 1.245.39v.014l.312.1 1.145.361.022.23.031.255.043.409.04.419.017.23.032.252.032.411.043.325.077.849-.077-.04-3.025-1.323-.005-.304.06-2.369zm-4.295.616.014.008.068.107.599.874-.639.532-.035-1.439zm3.67 0h.008l-.005.06-.02.678-.004.214-.48-.222zm-2.888 3.605.763.916.001.37-.017-.007-.025-.05-.464-.79-.012-.018zm1.53.61.184.083-.343.586-.018.007.002-.531z'/></svg>",
+ "matlab": "<svg viewBox='0 0 720 720'><path fill='#4db6ac' d='M209.25 329.98 52.37 387.636l121.32 85.822 96.752-95.805-61.197-47.674z'/><path fill='#00897b' d='M480.19 71.446c-13.123 1.784-9.565 1.013-28.4 16.091-18.009 14.417-69.925 100.35-97.674 129.26-24.688 25.721-34.46 12.199-60.102 33.661-25.68 21.494-65.273 64.464-65.273 64.464l63.978 47.319 101.43-139.48c23.948-32.932 23.693-37.266 36.743-71.821 6.385-16.906 17.76-29.899 27.756-45.808 12.488-19.874 30.186-34.855 21.543-33.68z'/><path fill='#ffb74d' d='M478.21 69.796c-31.267-.188-62.068 137.25-115.56 242.69-54.543 107.52-162.24 176.82-162.24 176.82 18.157 8.243 34.682 4.91 54.236 23.395 13.375 16.164 52.091 95.975 75.174 146.12 0 0 18.965-10.297 42.994-27.694 24.03-17.398 53.124-41.897 73.384-70.301 26.884-37.692 47.897-61.042 65.703-75.271s32.404-19.336 46.459-20.54c50.237-4.305 124.58 85.792 124.58 85.792S527.27 70.097 478.2 69.797z'/></svg>",
+ "maven": "<svg viewBox='0 0 24 24'><defs><linearGradient id='a' x1='.125' x2='0' y1='1' y2='0'><stop offset='0%' stop-color='#ab47bc'/><stop offset='37.5%' stop-color='#ab47bc'/><stop offset='37.501%' stop-color='#d32f2f'/><stop offset='54.25%' stop-color='#d32f2f'/><stop offset='54.251%' stop-color='#ff7043'/><stop offset='69.75%' stop-color='#ff7043'/><stop offset='69.751%' stop-color='#ffa726'/><stop offset='100%' stop-color='#ffa726'/></linearGradient></defs><path fill='url(#a)' d='M22 2s-7.64-.37-13.66 7.88C3.72 16.21 2 22 2 22l1.94-1c1.44-2.5 2.19-3.53 3.6-5 2.53.74 5.17.65 7.46-2-2-.56-3.6-.43-5.96-.19C11.69 12 13.5 11.6 16 12l1-2c-1.8-.34-3-.37-4.78.04C14.19 8.65 15.56 7.87 18 8l1.11-1.73c-1.53-.06-2.4-.02-4.19.3 1.61-1.46 3.08-2.12 5.22-2.25 0 0 1.05-1.89 1.86-2.32'/></svg>",
+ "mdsvex": "<svg viewBox='0 0 32 32'><path fill='#ff5722' d='m14 10-4 3.5L6 10H4v12h4v-6l2 2 2-2v6h4V10zm12 6v-6h-4v6h-4l6 8 6-8z'/></svg>",
+ "mdx": "<svg viewBox='0 0 32 32'><path fill='#ffca28' d='m14 10-4 3.5L6 10H4v12h4v-6l2 2 2-2v6h4V10zm12 6v-6h-4v6h-4l6 8 6-8z'/></svg>",
+ "mercurial": "<svg viewBox='0 0 24 24'><g fill='#78909c'><path d='M21.29 12.66c.287-4.983-3.45-10.35-9.202-9.68-2.588.288-4.121 1.63-4.792 3.067-1.15 2.397.095 5.56 3.834 6.614 2.3.67 2.78 1.63 2.492 2.78-.288 1.054-1.055 2.204-1.246 3.163-.096.287-.096.575-.096.862.096 2.109 4.409 2.972 7.764-2.684.766-1.15 1.15-2.587 1.246-4.121zM6.433 11.51v-.383c0-.096 0-.191-.096-.287-.192-.959-.958-1.534-1.917-1.438s-1.63.863-1.725 1.821v.48c.096 1.054 1.054 1.82 2.013 1.725 1.054-.192 1.725-.959 1.725-1.917z'/><path d='M10.65 16.59c-.383-1.533-1.917-2.491-3.45-2.012-1.246.287-2.013 1.342-2.109 2.588-.096.383 0 .767.096 1.15.383 1.534 2.013 2.492 3.546 2.013 1.342-.383 2.205-1.63 2.109-3.067-.096-.192-.096-.384-.192-.671z'/></g></svg>",
+ "merlin": "<svg viewBox='0 0 281.25 281.25'><path fill='#42a5f5' d='M57.857 40.232h37.088l46.022 140.044 46.7-140.044h36.546l33.57 200.781h-36.547l-21.387-126.796-42.367 126.796h-33.435L82.222 114.217 60.428 241.013H23.476z' aria-label='M'/></svg>",
+ "mermaid": "<svg stroke-linejoin='round' stroke-miterlimit='2' clip-rule='evenodd' viewBox='0 0 64 64'><path fill='#ff4081' d='M56.954 11.425C45.907 10.952 35.763 17.749 32 28.146 28.236 17.749 18.093 10.952 7.046 11.425a25.46 25.46 0 0 0 11.073 22.072 13.66 13.66 0 0 1 5.92 11.286v7.815h15.924v-7.815a13.66 13.66 0 0 1 5.92-11.286 25.44 25.44 0 0 0 11.072-22.072z'/></svg>",
+ "meson": "<svg viewBox='0 0 18 18'><path fill='#3f51b5' d='M9.06 1.534c-4.627 2.64-7.5 7.588-7.526 12.962 4.84 2.668 10.525 2.577 14.93.028q0-.02.002-.039C16.46 9.14 13.637 4.203 9.06 1.533zm2.49 5.722c.687.47.585 1.605.474 2.382-.14.856-.952 2.262-2.006 3.18.136.198.216.442.215.701-.654.334-1.302-.042-2.086-.721s-1.63-2.039-1.91-3.437a1.12 1.12 0 0 1-.723-.169c.057-.783.88-1.249 1.61-1.524.728-.275 2.607-.368 3.932.108.104-.219.275-.403.495-.52z'/></svg>",
+ "minecraft-fabric": "<svg fill-rule='evenodd' stroke-linejoin='round' stroke-miterlimit='1.414' clip-rule='evenodd' viewBox='0 0 16 16'><path fill='#38342a' d='M8 1v1H7v2H6v1H5v1H4v1H3v1H2v1H1v2h1v1h1v1h1v1h1v1h2v-1h1v-2h1v-1h1v-1h1V9h2V8h1V6h-1V5h-1V4h-1V3h-1V2H9V1z'/><path fill='#dbd0b4' d='M8 2v1h1v1h1v1h1v1h1V5h-1V4h-1V3H9V2zM7 4v2H5v1H4v1H3v2h1v1h1v1h2v-2H6V9h2v1h1V8H8V7h2V6H9V5H8V4z'/><path fill='#38342a' d='M8 4v1h1v1h1v1h1V6h-1V5H9V4z'/><path fill='#bcb29c' d='M9 4v1h1V4zm1 1v1h1V5zm1 1v1h1V6zm0 1h-1v1H9v2h1V9h1zm-2 3H7v2h1v-1h1zm-2 2H6v1h1z'/><path fill='#807a6d' d='M12 7v1h1V7z'/><path fill='#aea694' d='M2 9v1h1v1h1v1h1v1h1v-1H5v-1H4v-1H3V9z'/><path fill='#9a927e' d='M2 10v1h1v-1zm1 1v1h1v-1zm1 1v1h1v-1zm1 1v1h1v-1z'/><path fill='#c6bca5' d='M8 3v1h1V3zM6 5v1h1V5zm1 1v1h1V6zm1 1v1h2V7zM5 8v1h1V8zm1 1v1h2V9z'/></svg>",
+ "minecraft": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='M4 4v24h24V4Zm20 10h-6v2h4v8h-4v-4h-4v4h-4v-8h4v-2H8V8h6v6h4V8h6Z'/></svg>",
+ "mint": "<svg viewBox='0 0 256 256'><path fill='#43a047' d='M128 18.753c-2.84 10.841-27.094 17.766-28.772 31.512l-2.738-8.22C82.21 60.232 83.473 69.446 83.473 69.446l-1.2-6.593c-10.422 17.426-8.215 25.963-7.012 28.566l.505.892s-.225-.287-.505-.892l-2.748-4.846c-10.824 29.941-1.371 39.133-1.371 39.133l-5.822-6.764c-2.573 22.94 4.11 32.368 4.11 32.368l-4.196-3.94c-.534 15.582 9.677 24.833 9.677 24.833l-6.73-4.615c12.951 56.58 38.666 58.104 41.982 58.061l-10.25.331c6.836 7.318 15.562 10.156 21.464 11.267 3.218-17.067 6.176-58.395 6.623-166.69.447 108.29 3.407 149.62 6.625 166.69 5.903-1.112 14.629-3.951 21.464-11.267l-10.248-.33c3.32.042 29.03-1.488 41.98-58.062l-6.73 4.616s10.211-9.25 9.677-24.833l-4.196 3.94s6.683-9.427 4.11-32.368l-5.822 6.764s9.453-9.192-1.371-39.133l-2.746 4.84c-.281.609-.508.895-.508.895l.508-.894c1.204-2.609 3.403-11.145-7.015-28.561l-1.199 6.593s1.264-9.215-13.017-27.402l-2.74 8.221c-1.677-13.745-25.93-20.67-28.771-31.51z'/></svg>",
+ "mist.clone": "<svg viewBox='0 0 24 24'><path fill='#2196f3' d='M12 21.669a6.927 6.927 0 0 1-6.927-6.927C5.073 10.124 12 2.33 12 2.33s6.927 7.793 6.927 12.41A6.927 6.927 0 0 1 12 21.67z'/></svg>",
+ "mjml": "<svg viewBox='0 0 120 120'><g transform='translate(9.943 14.253)scale(.8026)'><path fill='#ff5722' d='M14.5 0h57.3c8 0 14.5 6.5 14.5 14.5S79.8 29 71.8 29H14.5C6.5 29 0 22.5 0 14.5S6.5 0 14.5 0'/><ellipse cx='109.2' cy='14.5' fill='#ff5722' rx='14.8' ry='14.5'/><path fill='#ff5722' d='M52.6 43.3h56.6c8-.6 14.9 5.5 15.5 13.5s-5.5 14.9-13.5 15.5H52.6c-8 .6-14.9-5.5-15.5-13.5s5.5-14.9 13.5-15.5H52z'/><path fill='#ff1744' d='M14.8 43c8.2 0 14.8 6.6 14.8 14.8S23 72.6 14.8 72.6C6.6 72.5 0 65.9 0 57.8 0 49.6 6.6 43 14.8 43'/><path fill='#ff5722' d='M14.5 85h57.3c8 0 14.5 6.5 14.5 14.5S79.8 114 71.8 114H14.5C6.5 114 0 107.5 0 99.5S6.5 85 14.5 85'/><ellipse cx='109.2' cy='99.5' fill='#ff5722' rx='14.8' ry='14.5'/></g></svg>",
+ "mocha": "<svg viewBox='0 0 32 32'><path fill='#a1887f' d='M22 14c-.002 7.41-.07 10.857-2.486 14h-7.028C10.07 24.857 10.002 21.41 10 14zm.823-2H9.177A1.266 1.266 0 0 0 8 13.342C8 22 8 26 11.546 30h8.908C24 26 24 22 24 13.342A1.266 1.266 0 0 0 22.823 12m-4.82-9.998c1.15.46 2 2.075 2 3.998 0 1.925-.851 3.54-2.003 4V7.998c-1.15-.46-2-2.074-2-3.998s.851-3.54 2.003-4ZM13 6.004A2.11 2.11 0 0 1 14 8a2.1 2.1 0 0 1-1 2V9a2.11 2.11 0 0 1-1-1.998 2.1 2.1 0 0 1 1-1.998Z'/><path fill='#a1887f' d='M16 20h-3c0 2 1 4 1 6h4c0-2 1-4 1-6Z'/></svg>",
+ "modernizr": "<svg viewBox='0 0 32 32'><path fill='#e91e63' d='M10 10v4H6v4H2v4h12V10zm8 0v12h12a12 12 0 0 0-12-12'/></svg>",
+ "mojo": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='M23.5 10.12a9.2 9.2 0 0 1-1.73-1.04.04.04 0 0 0-.02-.01 9.5 9.5 0 0 1-3.74-7.23 9.98 9.98 0 0 0-5.83 7.27A10 10 0 0 0 12 11a9.92 9.92 0 0 0 2.76 6.88l-1.72 1.72A10.98 10.98 0 0 1 10 12a10 10 0 0 1 .12-1.58 10.997 10.997 0 1 0 17.76 10.16A10 10 0 0 0 28 19v-.01a10.97 10.97 0 0 0-4.5-8.87M22 24h-4c4-2 4.37-8.007 0-10h4a5.667 5.667 0 0 1 0 10'/></svg>",
+ "moon": "<svg viewBox='0 0 32 32'><path fill='#7e57c2' d='M30 7.5a5.499 5.499 0 0 0-10.772-1.562 12.53 12.53 0 1 0 6.834 6.834A5.5 5.5 0 0 0 30 7.5M19.5 18a5.498 5.498 0 0 1-.476-10.976A6 6 0 0 0 19 7.5a5.5 5.5 0 0 0 5.5 5.5q.24-.002.476-.024A5.5 5.5 0 0 1 19.5 18'/></svg>",
+ "moonscript": "<svg viewBox='0 0 24 24'><path fill='#fbc02d' d='m18.121 4.581-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94 3.19-.09 1.06-3 1.06 3zm3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98-1.64-1.25 2.06-.05.69-1.95.69 1.95zm-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27-3.91 3.93-10.24 3.93-14.14 0-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31'/></svg>",
+ "mxml": "<svg viewBox='0 0 24 24'><path fill='#ffa726' d='M13 9h5.5L13 3.5zM6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4c0-1.11.89-2 2-2m.12 13.5 3.74 3.74 1.42-1.41-2.33-2.33 2.33-2.33-1.42-1.41zm11.16 0-3.74-3.74-1.42 1.41 2.33 2.33-2.33 2.33 1.42 1.41z'/></svg>",
+ "nano-staged": "<svg viewBox='0 0 24 24'><path fill='#b0bec5' d='M12 1.481A10.495 10.495 0 0 0 1.48 11.999c0 5.828 4.69 10.52 10.518 10.52S22.52 17.826 22.52 12A10.496 10.496 0 0 0 12 1.481M7.952 6.899a.2.2 0 0 1 .11.028l5.661 3.27a.21.21 0 0 0 .212 0l2.002-1.157a.21.21 0 0 1 .317.18v7.67a.21.21 0 0 1-.317.183l-5.662-3.27a.21.21 0 0 0-.21 0l-2.004 1.159a.21.21 0 0 1-.314-.183V7.11c0-.121.097-.208.205-.21z'/></svg>",
+ "nano-staged_light": "<svg viewBox='0 0 24 24'><path fill='#546e7a' d='M11.999 1.481A10.495 10.495 0 0 0 1.48 11.999c0 5.828 4.69 10.52 10.518 10.52 5.827 0 10.52-4.693 10.52-10.52a10.496 10.496 0 0 0-10.52-10.518zM7.953 6.899a.2.2 0 0 1 .109.028l5.662 3.27a.21.21 0 0 0 .212 0l2.002-1.157a.21.21 0 0 1 .317.18v7.67a.21.21 0 0 1-.317.183l-5.662-3.27a.21.21 0 0 0-.21 0l-2.004 1.16a.21.21 0 0 1-.315-.184V7.11a.21.21 0 0 1 .206-.21z'/></svg>",
+ "ndst": "<svg viewBox='0 0 32 32'><path fill='#0097a7' d='M8.2 12.2c-.8-.1-1.3.3-1.4 1-.1.9.3 1.3 1.1 1.4s1.2-.3 1.4-1c.1-.7-.3-1.3-1.1-1.4'/><path fill='#0097a7' d='M16 2c-1.4 0-2.8.2-4.1.6.2.7.5 1.3.9 1.9.9 1.5 2.3 2.2 4 2.3.8.1 1.6.1 2.4.1 2.1 0 3.8 2.4 2.9 4.6-.5 1.2-1.1 1.9-2.4 2.1-1.6.2-2.8-.3-3.6-1.7-.1-.3-.3-.6-.4-.9-.2-.6-.4-1.3-.7-1.9-.8-2.3-3.4-3.6-5.7-3.1-.8.1-1.8-.1-2.5-.5C3.8 8.1 2 11.8 2 16c0 7.7 6.3 14 14 14s14-6.3 14-14S23.7 2 16 2m5.8 21.3c-.5 1.2-1.1 1.9-2.4 2.1-1.6.2-2.8-.3-3.6-1.7-.1-.3-.3-.6-.4-.9-.2-.6-.4-1.3-.7-1.9-.9-2.4-3.5-3.6-5.8-3.2-1.3.2-3.2-.5-3.8-1.6-.8-1.3-.7-3.1.1-4.1.5-.6 1.3-1 2.1-1.2h.9c1.5.2 2.4.9 2.9 2.4.3 1.1.7 2.1 1.3 3.1.9 1.5 2.3 2.2 4 2.3.8.1 1.6.1 2.4.1 2.1 0 3.8 2.5 3 4.6'/><path fill='#0097a7' d='M18.4 21.8c-.6.1-.8.6-.8 1.1s.4 1 .7 1.3c.5.3 1.1.3 1.5-.3.1-.1.4-1 .2-1.3-.2-.5-1.4-.8-1.6-.8m1.7-9.7c.1-.1.4-1 .2-1.3-.2-.4-1.4-.7-1.6-.7-.6.1-.8.6-.8 1.1s.4 1 .7 1.3c.5.3 1.2.3 1.5-.4'/></svg>",
+ "nest-controller.clone": "<svg viewBox='0 0 300 300'><path fill='#0288d1' d='M172.382 24.41c-1.793 0-3.5.426-5.036.938 3.329 2.22 5.121 5.121 6.06 8.45.085.427.17.768.256 1.195s.17.768.17 1.195c.257 5.719-1.536 6.401-2.73 9.815-1.878 4.353-1.366 9.048.938 12.803.17.427.427.94.768 1.451-2.475-16.473 11.267-18.948 13.827-24.07.171-4.523-3.499-7.51-6.401-9.558-2.987-1.708-5.548-2.22-7.852-2.22zm20.655 3.67c-.256 1.536-.086 1.11-.17 1.877-.086.512-.086 1.195-.172 1.707-.17.512-.256 1.024-.426 1.537l-.512 1.536c-.257.512-.427.939-.683 1.536l-.512.768c-.171.171-.256.427-.427.598-.342.427-.683.939-.939 1.28-.427.427-.683.854-1.195 1.195v.085c-.427.342-.768.683-1.195 1.025-1.366 1.024-2.902 1.792-4.353 2.816-.427.342-.939.598-1.28.94-.427.34-.854.682-1.195 1.023l-1.195 1.195c-.341.427-.683.854-.939 1.28-.341.427-.683.94-.939 1.366-.256.512-.426.94-.683 1.537-.17.512-.426.938-.512 1.536-.17.597-.341 1.11-.426 1.622-.086.256-.086.597-.171.853s-.085.512-.17.768c0 .512-.086 1.11-.086 1.622 0 .427 0 .768.085 1.195 0 .512.086 1.024.17 1.622.086.512.172 1.024.342 1.536l.512 1.536c.171.342.342.683.427.94l-14.936-5.805c-2.561-.683-5.036-1.365-7.511-1.963-1.366-.341-2.732-.683-4.097-.939a137 137 0 0 0-11.864-1.792c-.17 0-.17-.086-.342-.086-3.926-.426-7.767-.597-11.608-.597-2.901 0-5.718.17-8.535.341-3.926.256-7.937.769-11.864 1.451-.939.171-1.963.342-2.902.513-2.048.426-3.926.853-5.889 1.28-.939.256-1.963.512-2.902.768-.939.427-1.878.854-2.816 1.195a23 23 0 0 1-2.134.939c-.171.085-.256.085-.342.17a34 34 0 0 0-1.792.94c-.17.085-.341.17-.427.17-.683.341-1.45.683-2.048 1.024-.427.171-.94.427-1.28.683-.171.17-.427.256-.598.342-.597.34-1.195.682-1.707.938-.598.342-1.11.683-1.536.94-.427.34-.94.597-1.28.938-.086.085-.171.085-.171.17a6.5 6.5 0 0 0-1.195.94l-.171.17c-.341.256-.683.513-.939.769-.17.085-.256.17-.427.256-.341.256-.682.597-.939.853-.085.17-.17.17-.256.256-.426.427-.768.683-1.195 1.11-.085 0-.085.085-.17.17-.427.342-.768.683-1.195 1.11-.085.085-.085.17-.17.17-.342.342-.684.684-.94 1.025-.17.17-.341.256-.426.427-.342.427-.683.768-1.11 1.195-.085.17-.17.17-.256.341-.512.512-.939 1.024-1.536 1.536l-.171.171c-1.024 1.11-2.134 2.22-3.329 3.158-1.195 1.024-2.39 2.049-3.67 2.902-1.28.939-2.56 1.707-3.926 2.475-1.28.683-2.646 1.366-4.097 1.963a36 36 0 0 1-4.268 1.537c-2.731.597-5.548 1.707-7.937 1.878-.513 0-1.11.17-1.622.17-.598.17-1.11.256-1.622.427s-1.024.427-1.536.597-1.024.427-1.536.683c-.427.342-.94.598-1.451.94-.427.34-.94.682-1.28 1.109-.428.341-.94.768-1.281 1.195-.427.426-.768.853-1.11 1.28-.341.512-.682.939-.938 1.536-.342.427-.683.94-.94 1.537s-.511 1.11-.682 1.707c-.17.512-.427 1.11-.598 1.707-.17.512-.256 1.024-.341 1.536 0 .085-.085.17-.085.17-.171.598-.171 1.366-.171 1.793-.085.427-.17.854-.17 1.28 0 .256 0 .598.085.854.085.427.17.853.256 1.195.17.427.256.768.426 1.195v.085c.171.427.427.768.683 1.195s.512.768.854 1.195c.341.341.683.683 1.11 1.024.426.427.767.683 1.194 1.024 1.537 1.366 1.963 1.793 3.926 2.902l1.025.512c.085 0 .17.086.17.086 0 .17 0 .17.086.341.085.512.17 1.024.341 1.537q.256.896.512 1.536c.256.64.342.768.512 1.195.086.17.171.256.171.341.256.512.512.94.768 1.451.342.427.683.94.94 1.366.34.427.682.853 1.109 1.195.426.427.768.683 1.195 1.11 0 0 .085.085.17.085.427.341.768.683 1.195.939.427.341.94.597 1.451.853.427.256.94.512 1.537.683.426.17.853.341 1.28.427.085.085.17.085.256.17.256.086.597.171.853.171-.17 3.5-.256 6.828.256 8.023.598 1.28 3.415-2.646 6.316-7.255-.426 4.524-.682 9.73 0 11.352s4.61-3.414 8.024-9.047c46.09-10.67 88.168 21.167 92.606 66.233-.853-6.999-9.474-10.925-13.485-9.986-1.963 4.78-5.292 11.01-10.584 14.851.427-4.268.256-8.706-.683-12.974-1.45 5.975-4.267 11.608-8.023 16.388-6.145.427-12.376-2.56-15.62-6.999-.255-.17-.34-.597-.511-.853-.17-.427-.427-.94-.512-1.366-.171-.427-.342-.939-.427-1.366s-.085-.938-.085-1.45v-.94c.085-.426.17-.938.341-1.365s.256-.939.427-1.366c.256-.426.426-.939.768-1.365 1.11-3.158 1.11-5.634-.939-7.17-.427-.256-.768-.427-1.195-.683-.256-.085-.597-.17-.853-.256-.171-.085-.342-.17-.513-.17-.426-.171-.938-.257-1.365-.342-.427-.17-.939-.17-1.366-.17-.427-.086-.939-.171-1.45-.171-.342 0-.684.085-.94.085-.512 0-.939.085-1.45.17-.427.086-.94.171-1.366.257-.427.17-.94.256-1.366.427s-.853.426-1.28.597-.768.427-1.195.683c-15.193 9.9-6.145 33.031 4.267 39.689-3.926.682-7.852 1.536-8.961 2.39l-.171.17a56 56 0 0 0 8.791 4.353c4.182 1.366 8.62 2.56 10.498 3.158v.085c5.378 1.11 10.84 1.537 16.388 1.195 28.764-2.048 52.406-23.898 56.674-52.833.17.598.256 1.11.426 1.708.171 1.194.427 2.39.598 3.67v.085c.17.597.17 1.195.256 1.707v.256c.085.598.17 1.195.17 1.707.086.683.171 1.451.171 2.134v1.024c0 .342.086.683.086 1.024 0 .427-.086.769-.086 1.195v.94c0 .426-.085.853-.085 1.28 0 .256 0 .512-.085.853 0 .427-.086.939-.086 1.451-.085.17-.085.427-.085.597-.085.513-.17.94-.17 1.537 0 .17 0 .427-.086.597-.085.683-.17 1.195-.256 1.878v.17c-.17.598-.256 1.196-.427 1.793v.17c-.17.598-.256 1.196-.427 1.793 0 .086-.085.17-.085.256-.17.598-.256 1.195-.427 1.793v.17c-.17.683-.427 1.195-.512 1.793-.085.085-.085.17-.085.17-.17.683-.427 1.28-.598 1.964-.256.682-.426 1.194-.683 1.877s-.426 1.28-.682 1.878c-.256.683-.512 1.195-.768 1.878h-.086c-.256.597-.512 1.195-.853 1.792-.086.17-.171.342-.171.427-.085.085-.085.17-.17.17-4.268 8.536-10.5 15.961-18.266 21.85-.512.342-1.024.684-1.536 1.11-.171.171-.342.171-.427.342a8.5 8.5 0 0 1-1.451.939l.17.426h.086c.939-.17 1.792-.256 2.731-.426h.085c1.708-.256 3.415-.598 5.036-.94.427-.085.94-.17 1.451-.34.342-.086.598-.171.94-.171.426-.086.938-.171 1.365-.256.427-.171.768-.171 1.195-.342 6.486-1.536 12.802-3.67 18.862-6.23-10.327 14.082-24.154 25.52-40.371 32.945a109 109 0 0 0 22.191-3.84c26.204-7.768 48.224-25.35 61.454-49.078-2.646 15.022-8.62 29.361-17.497 41.823 6.316-4.183 12.12-8.962 17.326-14.425 14.595-15.193 24.155-34.568 27.398-55.308 2.22 10.242 2.902 20.911 1.878 31.324 46.944-65.465 3.926-133.405-14.083-151.244-.085-.17-.17-.17-.17-.341-.086.085-.086.085-.086.17 0-.085 0-.085-.086-.17 0 .768-.085 1.536-.17 2.304-.17 1.537-.427 2.902-.683 4.353-.341 1.451-.683 2.902-1.11 4.268s-.938 2.817-1.536 4.182c-.597 1.28-1.195 2.646-1.963 3.926a53 53 0 0 1-2.305 3.67c-.853 1.196-1.792 2.39-2.645 3.5-.94 1.195-2.049 2.22-3.073 3.243-.683.598-1.195 1.11-1.878 1.622-.512.427-.939.854-1.536 1.28-1.195.94-2.305 1.793-3.67 2.56-1.195.77-2.56 1.537-3.841 2.22-1.366.683-2.731 1.195-4.097 1.792-1.366.513-2.817.94-4.268 1.366s-2.902.683-4.353.939a42 42 0 0 1-4.438.512c-1.024.085-2.048.17-3.158.17-1.536 0-2.987-.17-4.438-.255-1.537-.171-2.988-.342-4.439-.683-1.536-.256-2.902-.683-4.353-1.11h-.085c1.451-.17 2.902-.256 4.268-.512 1.536-.256 2.902-.597 4.353-.939 1.45-.426 2.902-.853 4.267-1.365 1.451-.512 2.817-1.195 4.097-1.793 1.366-.682 2.56-1.365 3.926-2.133 1.195-.854 2.476-1.707 3.67-2.561 1.195-.939 2.305-1.878 3.33-2.902 1.109-.939 2.048-2.048 3.072-3.158.939-1.195 1.878-2.305 2.731-3.5.17-.17.256-.426.427-.682a39 39 0 0 0 1.878-3.158c.682-1.28 1.365-2.56 1.963-3.926s1.11-2.732 1.536-4.183c.427-1.365.768-2.816 1.11-4.267.256-1.537.512-2.902.682-4.353.171-1.537.257-2.988.257-4.439 0-1.024-.086-2.048-.171-3.158-.17-1.536-.342-2.902-.512-4.353-.256-1.536-.598-2.902-.94-4.352-.426-1.366-.938-2.817-1.45-4.183s-1.195-2.731-1.793-4.011c-.682-1.28-1.45-2.56-2.219-3.841l-2.56-3.585c-.94-1.11-1.963-2.22-2.988-3.329-.512-.512-1.11-1.11-1.707-1.621-2.902-2.305-5.974-4.439-9.047-6.402-.427-.256-.853-.426-1.28-.683-2.049-1.536-4.012-2.219-6.06-2.902z'/></svg>",
+ "nest-decorator.clone": "<svg viewBox='0 0 300 300'><path fill='#ab47bc' d='M172.382 24.41c-1.793 0-3.5.426-5.036.938 3.329 2.22 5.121 5.121 6.06 8.45.085.427.17.768.256 1.195s.17.768.17 1.195c.257 5.719-1.536 6.401-2.73 9.815-1.878 4.353-1.366 9.048.938 12.803.17.427.427.94.768 1.451-2.475-16.473 11.267-18.948 13.827-24.07.171-4.523-3.499-7.51-6.401-9.558-2.987-1.708-5.548-2.22-7.852-2.22zm20.655 3.67c-.256 1.536-.086 1.11-.17 1.877-.086.512-.086 1.195-.172 1.707-.17.512-.256 1.024-.426 1.537l-.512 1.536c-.257.512-.427.939-.683 1.536l-.512.768c-.171.171-.256.427-.427.598-.342.427-.683.939-.939 1.28-.427.427-.683.854-1.195 1.195v.085c-.427.342-.768.683-1.195 1.025-1.366 1.024-2.902 1.792-4.353 2.816-.427.342-.939.598-1.28.94-.427.34-.854.682-1.195 1.023l-1.195 1.195c-.341.427-.683.854-.939 1.28-.341.427-.683.94-.939 1.366-.256.512-.426.94-.683 1.537-.17.512-.426.938-.512 1.536-.17.597-.341 1.11-.426 1.622-.086.256-.086.597-.171.853s-.085.512-.17.768c0 .512-.086 1.11-.086 1.622 0 .427 0 .768.085 1.195 0 .512.086 1.024.17 1.622.086.512.172 1.024.342 1.536l.512 1.536c.171.342.342.683.427.94l-14.936-5.805c-2.561-.683-5.036-1.365-7.511-1.963-1.366-.341-2.732-.683-4.097-.939a137 137 0 0 0-11.864-1.792c-.17 0-.17-.086-.342-.086-3.926-.426-7.767-.597-11.608-.597-2.901 0-5.718.17-8.535.341-3.926.256-7.937.769-11.864 1.451-.939.171-1.963.342-2.902.513-2.048.426-3.926.853-5.889 1.28-.939.256-1.963.512-2.902.768-.939.427-1.878.854-2.816 1.195a23 23 0 0 1-2.134.939c-.171.085-.256.085-.342.17a34 34 0 0 0-1.792.94c-.17.085-.341.17-.427.17-.683.341-1.45.683-2.048 1.024-.427.171-.94.427-1.28.683-.171.17-.427.256-.598.342-.597.34-1.195.682-1.707.938-.598.342-1.11.683-1.536.94-.427.34-.94.597-1.28.938-.086.085-.171.085-.171.17a6.5 6.5 0 0 0-1.195.94l-.171.17c-.341.256-.683.513-.939.769-.17.085-.256.17-.427.256-.341.256-.682.597-.939.853-.085.17-.17.17-.256.256-.426.427-.768.683-1.195 1.11-.085 0-.085.085-.17.17-.427.342-.768.683-1.195 1.11-.085.085-.085.17-.17.17-.342.342-.684.684-.94 1.025-.17.17-.341.256-.426.427-.342.427-.683.768-1.11 1.195-.085.17-.17.17-.256.341-.512.512-.939 1.024-1.536 1.536l-.171.171c-1.024 1.11-2.134 2.22-3.329 3.158-1.195 1.024-2.39 2.049-3.67 2.902-1.28.939-2.56 1.707-3.926 2.475-1.28.683-2.646 1.366-4.097 1.963a36 36 0 0 1-4.268 1.537c-2.731.597-5.548 1.707-7.937 1.878-.513 0-1.11.17-1.622.17-.598.17-1.11.256-1.622.427s-1.024.427-1.536.597-1.024.427-1.536.683c-.427.342-.94.598-1.451.94-.427.34-.94.682-1.28 1.109-.428.341-.94.768-1.281 1.195-.427.426-.768.853-1.11 1.28-.341.512-.682.939-.938 1.536-.342.427-.683.94-.94 1.537s-.511 1.11-.682 1.707c-.17.512-.427 1.11-.598 1.707-.17.512-.256 1.024-.341 1.536 0 .085-.085.17-.085.17-.171.598-.171 1.366-.171 1.793-.085.427-.17.854-.17 1.28 0 .256 0 .598.085.854.085.427.17.853.256 1.195.17.427.256.768.426 1.195v.085c.171.427.427.768.683 1.195s.512.768.854 1.195c.341.341.683.683 1.11 1.024.426.427.767.683 1.194 1.024 1.537 1.366 1.963 1.793 3.926 2.902l1.025.512c.085 0 .17.086.17.086 0 .17 0 .17.086.341.085.512.17 1.024.341 1.537q.256.896.512 1.536c.256.64.342.768.512 1.195.086.17.171.256.171.341.256.512.512.94.768 1.451.342.427.683.94.94 1.366.34.427.682.853 1.109 1.195.426.427.768.683 1.195 1.11 0 0 .085.085.17.085.427.341.768.683 1.195.939.427.341.94.597 1.451.853.427.256.94.512 1.537.683.426.17.853.341 1.28.427.085.085.17.085.256.17.256.086.597.171.853.171-.17 3.5-.256 6.828.256 8.023.598 1.28 3.415-2.646 6.316-7.255-.426 4.524-.682 9.73 0 11.352s4.61-3.414 8.024-9.047c46.09-10.67 88.168 21.167 92.606 66.233-.853-6.999-9.474-10.925-13.485-9.986-1.963 4.78-5.292 11.01-10.584 14.851.427-4.268.256-8.706-.683-12.974-1.45 5.975-4.267 11.608-8.023 16.388-6.145.427-12.376-2.56-15.62-6.999-.255-.17-.34-.597-.511-.853-.17-.427-.427-.94-.512-1.366-.171-.427-.342-.939-.427-1.366s-.085-.938-.085-1.45v-.94c.085-.426.17-.938.341-1.365s.256-.939.427-1.366c.256-.426.426-.939.768-1.365 1.11-3.158 1.11-5.634-.939-7.17-.427-.256-.768-.427-1.195-.683-.256-.085-.597-.17-.853-.256-.171-.085-.342-.17-.513-.17-.426-.171-.938-.257-1.365-.342-.427-.17-.939-.17-1.366-.17-.427-.086-.939-.171-1.45-.171-.342 0-.684.085-.94.085-.512 0-.939.085-1.45.17-.427.086-.94.171-1.366.257-.427.17-.94.256-1.366.427s-.853.426-1.28.597-.768.427-1.195.683c-15.193 9.9-6.145 33.031 4.267 39.689-3.926.682-7.852 1.536-8.961 2.39l-.171.17a56 56 0 0 0 8.791 4.353c4.182 1.366 8.62 2.56 10.498 3.158v.085c5.378 1.11 10.84 1.537 16.388 1.195 28.764-2.048 52.406-23.898 56.674-52.833.17.598.256 1.11.426 1.708.171 1.194.427 2.39.598 3.67v.085c.17.597.17 1.195.256 1.707v.256c.085.598.17 1.195.17 1.707.086.683.171 1.451.171 2.134v1.024c0 .342.086.683.086 1.024 0 .427-.086.769-.086 1.195v.94c0 .426-.085.853-.085 1.28 0 .256 0 .512-.085.853 0 .427-.086.939-.086 1.451-.085.17-.085.427-.085.597-.085.513-.17.94-.17 1.537 0 .17 0 .427-.086.597-.085.683-.17 1.195-.256 1.878v.17c-.17.598-.256 1.196-.427 1.793v.17c-.17.598-.256 1.196-.427 1.793 0 .086-.085.17-.085.256-.17.598-.256 1.195-.427 1.793v.17c-.17.683-.427 1.195-.512 1.793-.085.085-.085.17-.085.17-.17.683-.427 1.28-.598 1.964-.256.682-.426 1.194-.683 1.877s-.426 1.28-.682 1.878c-.256.683-.512 1.195-.768 1.878h-.086c-.256.597-.512 1.195-.853 1.792-.086.17-.171.342-.171.427-.085.085-.085.17-.17.17-4.268 8.536-10.5 15.961-18.266 21.85-.512.342-1.024.684-1.536 1.11-.171.171-.342.171-.427.342a8.5 8.5 0 0 1-1.451.939l.17.426h.086c.939-.17 1.792-.256 2.731-.426h.085c1.708-.256 3.415-.598 5.036-.94.427-.085.94-.17 1.451-.34.342-.086.598-.171.94-.171.426-.086.938-.171 1.365-.256.427-.171.768-.171 1.195-.342 6.486-1.536 12.802-3.67 18.862-6.23-10.327 14.082-24.154 25.52-40.371 32.945a109 109 0 0 0 22.191-3.84c26.204-7.768 48.224-25.35 61.454-49.078-2.646 15.022-8.62 29.361-17.497 41.823 6.316-4.183 12.12-8.962 17.326-14.425 14.595-15.193 24.155-34.568 27.398-55.308 2.22 10.242 2.902 20.911 1.878 31.324 46.944-65.465 3.926-133.405-14.083-151.244-.085-.17-.17-.17-.17-.341-.086.085-.086.085-.086.17 0-.085 0-.085-.086-.17 0 .768-.085 1.536-.17 2.304-.17 1.537-.427 2.902-.683 4.353-.341 1.451-.683 2.902-1.11 4.268s-.938 2.817-1.536 4.182c-.597 1.28-1.195 2.646-1.963 3.926a53 53 0 0 1-2.305 3.67c-.853 1.196-1.792 2.39-2.645 3.5-.94 1.195-2.049 2.22-3.073 3.243-.683.598-1.195 1.11-1.878 1.622-.512.427-.939.854-1.536 1.28-1.195.94-2.305 1.793-3.67 2.56-1.195.77-2.56 1.537-3.841 2.22-1.366.683-2.731 1.195-4.097 1.792-1.366.513-2.817.94-4.268 1.366s-2.902.683-4.353.939a42 42 0 0 1-4.438.512c-1.024.085-2.048.17-3.158.17-1.536 0-2.987-.17-4.438-.255-1.537-.171-2.988-.342-4.439-.683-1.536-.256-2.902-.683-4.353-1.11h-.085c1.451-.17 2.902-.256 4.268-.512 1.536-.256 2.902-.597 4.353-.939 1.45-.426 2.902-.853 4.267-1.365 1.451-.512 2.817-1.195 4.097-1.793 1.366-.682 2.56-1.365 3.926-2.133 1.195-.854 2.476-1.707 3.67-2.561 1.195-.939 2.305-1.878 3.33-2.902 1.109-.939 2.048-2.048 3.072-3.158.939-1.195 1.878-2.305 2.731-3.5.17-.17.256-.426.427-.682a39 39 0 0 0 1.878-3.158c.682-1.28 1.365-2.56 1.963-3.926s1.11-2.732 1.536-4.183c.427-1.365.768-2.816 1.11-4.267.256-1.537.512-2.902.682-4.353.171-1.537.257-2.988.257-4.439 0-1.024-.086-2.048-.171-3.158-.17-1.536-.342-2.902-.512-4.353-.256-1.536-.598-2.902-.94-4.352-.426-1.366-.938-2.817-1.45-4.183s-1.195-2.731-1.793-4.011c-.682-1.28-1.45-2.56-2.219-3.841l-2.56-3.585c-.94-1.11-1.963-2.22-2.988-3.329-.512-.512-1.11-1.11-1.707-1.621-2.902-2.305-5.974-4.439-9.047-6.402-.427-.256-.853-.426-1.28-.683-2.049-1.536-4.012-2.219-6.06-2.902z'/></svg>",
+ "nest-filter.clone": "<svg viewBox='0 0 300 300'><path fill='#ff7043' d='M172.382 24.41c-1.793 0-3.5.426-5.036.938 3.329 2.22 5.121 5.121 6.06 8.45.085.427.17.768.256 1.195s.17.768.17 1.195c.257 5.719-1.536 6.401-2.73 9.815-1.878 4.353-1.366 9.048.938 12.803.17.427.427.94.768 1.451-2.475-16.473 11.267-18.948 13.827-24.07.171-4.523-3.499-7.51-6.401-9.558-2.987-1.708-5.548-2.22-7.852-2.22zm20.655 3.67c-.256 1.536-.086 1.11-.17 1.877-.086.512-.086 1.195-.172 1.707-.17.512-.256 1.024-.426 1.537l-.512 1.536c-.257.512-.427.939-.683 1.536l-.512.768c-.171.171-.256.427-.427.598-.342.427-.683.939-.939 1.28-.427.427-.683.854-1.195 1.195v.085c-.427.342-.768.683-1.195 1.025-1.366 1.024-2.902 1.792-4.353 2.816-.427.342-.939.598-1.28.94-.427.34-.854.682-1.195 1.023l-1.195 1.195c-.341.427-.683.854-.939 1.28-.341.427-.683.94-.939 1.366-.256.512-.426.94-.683 1.537-.17.512-.426.938-.512 1.536-.17.597-.341 1.11-.426 1.622-.086.256-.086.597-.171.853s-.085.512-.17.768c0 .512-.086 1.11-.086 1.622 0 .427 0 .768.085 1.195 0 .512.086 1.024.17 1.622.086.512.172 1.024.342 1.536l.512 1.536c.171.342.342.683.427.94l-14.936-5.805c-2.561-.683-5.036-1.365-7.511-1.963-1.366-.341-2.732-.683-4.097-.939a137 137 0 0 0-11.864-1.792c-.17 0-.17-.086-.342-.086-3.926-.426-7.767-.597-11.608-.597-2.901 0-5.718.17-8.535.341-3.926.256-7.937.769-11.864 1.451-.939.171-1.963.342-2.902.513-2.048.426-3.926.853-5.889 1.28-.939.256-1.963.512-2.902.768-.939.427-1.878.854-2.816 1.195a23 23 0 0 1-2.134.939c-.171.085-.256.085-.342.17a34 34 0 0 0-1.792.94c-.17.085-.341.17-.427.17-.683.341-1.45.683-2.048 1.024-.427.171-.94.427-1.28.683-.171.17-.427.256-.598.342-.597.34-1.195.682-1.707.938-.598.342-1.11.683-1.536.94-.427.34-.94.597-1.28.938-.086.085-.171.085-.171.17a6.5 6.5 0 0 0-1.195.94l-.171.17c-.341.256-.683.513-.939.769-.17.085-.256.17-.427.256-.341.256-.682.597-.939.853-.085.17-.17.17-.256.256-.426.427-.768.683-1.195 1.11-.085 0-.085.085-.17.17-.427.342-.768.683-1.195 1.11-.085.085-.085.17-.17.17-.342.342-.684.684-.94 1.025-.17.17-.341.256-.426.427-.342.427-.683.768-1.11 1.195-.085.17-.17.17-.256.341-.512.512-.939 1.024-1.536 1.536l-.171.171c-1.024 1.11-2.134 2.22-3.329 3.158-1.195 1.024-2.39 2.049-3.67 2.902-1.28.939-2.56 1.707-3.926 2.475-1.28.683-2.646 1.366-4.097 1.963a36 36 0 0 1-4.268 1.537c-2.731.597-5.548 1.707-7.937 1.878-.513 0-1.11.17-1.622.17-.598.17-1.11.256-1.622.427s-1.024.427-1.536.597-1.024.427-1.536.683c-.427.342-.94.598-1.451.94-.427.34-.94.682-1.28 1.109-.428.341-.94.768-1.281 1.195-.427.426-.768.853-1.11 1.28-.341.512-.682.939-.938 1.536-.342.427-.683.94-.94 1.537s-.511 1.11-.682 1.707c-.17.512-.427 1.11-.598 1.707-.17.512-.256 1.024-.341 1.536 0 .085-.085.17-.085.17-.171.598-.171 1.366-.171 1.793-.085.427-.17.854-.17 1.28 0 .256 0 .598.085.854.085.427.17.853.256 1.195.17.427.256.768.426 1.195v.085c.171.427.427.768.683 1.195s.512.768.854 1.195c.341.341.683.683 1.11 1.024.426.427.767.683 1.194 1.024 1.537 1.366 1.963 1.793 3.926 2.902l1.025.512c.085 0 .17.086.17.086 0 .17 0 .17.086.341.085.512.17 1.024.341 1.537q.256.896.512 1.536c.256.64.342.768.512 1.195.086.17.171.256.171.341.256.512.512.94.768 1.451.342.427.683.94.94 1.366.34.427.682.853 1.109 1.195.426.427.768.683 1.195 1.11 0 0 .085.085.17.085.427.341.768.683 1.195.939.427.341.94.597 1.451.853.427.256.94.512 1.537.683.426.17.853.341 1.28.427.085.085.17.085.256.17.256.086.597.171.853.171-.17 3.5-.256 6.828.256 8.023.598 1.28 3.415-2.646 6.316-7.255-.426 4.524-.682 9.73 0 11.352s4.61-3.414 8.024-9.047c46.09-10.67 88.168 21.167 92.606 66.233-.853-6.999-9.474-10.925-13.485-9.986-1.963 4.78-5.292 11.01-10.584 14.851.427-4.268.256-8.706-.683-12.974-1.45 5.975-4.267 11.608-8.023 16.388-6.145.427-12.376-2.56-15.62-6.999-.255-.17-.34-.597-.511-.853-.17-.427-.427-.94-.512-1.366-.171-.427-.342-.939-.427-1.366s-.085-.938-.085-1.45v-.94c.085-.426.17-.938.341-1.365s.256-.939.427-1.366c.256-.426.426-.939.768-1.365 1.11-3.158 1.11-5.634-.939-7.17-.427-.256-.768-.427-1.195-.683-.256-.085-.597-.17-.853-.256-.171-.085-.342-.17-.513-.17-.426-.171-.938-.257-1.365-.342-.427-.17-.939-.17-1.366-.17-.427-.086-.939-.171-1.45-.171-.342 0-.684.085-.94.085-.512 0-.939.085-1.45.17-.427.086-.94.171-1.366.257-.427.17-.94.256-1.366.427s-.853.426-1.28.597-.768.427-1.195.683c-15.193 9.9-6.145 33.031 4.267 39.689-3.926.682-7.852 1.536-8.961 2.39l-.171.17a56 56 0 0 0 8.791 4.353c4.182 1.366 8.62 2.56 10.498 3.158v.085c5.378 1.11 10.84 1.537 16.388 1.195 28.764-2.048 52.406-23.898 56.674-52.833.17.598.256 1.11.426 1.708.171 1.194.427 2.39.598 3.67v.085c.17.597.17 1.195.256 1.707v.256c.085.598.17 1.195.17 1.707.086.683.171 1.451.171 2.134v1.024c0 .342.086.683.086 1.024 0 .427-.086.769-.086 1.195v.94c0 .426-.085.853-.085 1.28 0 .256 0 .512-.085.853 0 .427-.086.939-.086 1.451-.085.17-.085.427-.085.597-.085.513-.17.94-.17 1.537 0 .17 0 .427-.086.597-.085.683-.17 1.195-.256 1.878v.17c-.17.598-.256 1.196-.427 1.793v.17c-.17.598-.256 1.196-.427 1.793 0 .086-.085.17-.085.256-.17.598-.256 1.195-.427 1.793v.17c-.17.683-.427 1.195-.512 1.793-.085.085-.085.17-.085.17-.17.683-.427 1.28-.598 1.964-.256.682-.426 1.194-.683 1.877s-.426 1.28-.682 1.878c-.256.683-.512 1.195-.768 1.878h-.086c-.256.597-.512 1.195-.853 1.792-.086.17-.171.342-.171.427-.085.085-.085.17-.17.17-4.268 8.536-10.5 15.961-18.266 21.85-.512.342-1.024.684-1.536 1.11-.171.171-.342.171-.427.342a8.5 8.5 0 0 1-1.451.939l.17.426h.086c.939-.17 1.792-.256 2.731-.426h.085c1.708-.256 3.415-.598 5.036-.94.427-.085.94-.17 1.451-.34.342-.086.598-.171.94-.171.426-.086.938-.171 1.365-.256.427-.171.768-.171 1.195-.342 6.486-1.536 12.802-3.67 18.862-6.23-10.327 14.082-24.154 25.52-40.371 32.945a109 109 0 0 0 22.191-3.84c26.204-7.768 48.224-25.35 61.454-49.078-2.646 15.022-8.62 29.361-17.497 41.823 6.316-4.183 12.12-8.962 17.326-14.425 14.595-15.193 24.155-34.568 27.398-55.308 2.22 10.242 2.902 20.911 1.878 31.324 46.944-65.465 3.926-133.405-14.083-151.244-.085-.17-.17-.17-.17-.341-.086.085-.086.085-.086.17 0-.085 0-.085-.086-.17 0 .768-.085 1.536-.17 2.304-.17 1.537-.427 2.902-.683 4.353-.341 1.451-.683 2.902-1.11 4.268s-.938 2.817-1.536 4.182c-.597 1.28-1.195 2.646-1.963 3.926a53 53 0 0 1-2.305 3.67c-.853 1.196-1.792 2.39-2.645 3.5-.94 1.195-2.049 2.22-3.073 3.243-.683.598-1.195 1.11-1.878 1.622-.512.427-.939.854-1.536 1.28-1.195.94-2.305 1.793-3.67 2.56-1.195.77-2.56 1.537-3.841 2.22-1.366.683-2.731 1.195-4.097 1.792-1.366.513-2.817.94-4.268 1.366s-2.902.683-4.353.939a42 42 0 0 1-4.438.512c-1.024.085-2.048.17-3.158.17-1.536 0-2.987-.17-4.438-.255-1.537-.171-2.988-.342-4.439-.683-1.536-.256-2.902-.683-4.353-1.11h-.085c1.451-.17 2.902-.256 4.268-.512 1.536-.256 2.902-.597 4.353-.939 1.45-.426 2.902-.853 4.267-1.365 1.451-.512 2.817-1.195 4.097-1.793 1.366-.682 2.56-1.365 3.926-2.133 1.195-.854 2.476-1.707 3.67-2.561 1.195-.939 2.305-1.878 3.33-2.902 1.109-.939 2.048-2.048 3.072-3.158.939-1.195 1.878-2.305 2.731-3.5.17-.17.256-.426.427-.682a39 39 0 0 0 1.878-3.158c.682-1.28 1.365-2.56 1.963-3.926s1.11-2.732 1.536-4.183c.427-1.365.768-2.816 1.11-4.267.256-1.537.512-2.902.682-4.353.171-1.537.257-2.988.257-4.439 0-1.024-.086-2.048-.171-3.158-.17-1.536-.342-2.902-.512-4.353-.256-1.536-.598-2.902-.94-4.352-.426-1.366-.938-2.817-1.45-4.183s-1.195-2.731-1.793-4.011c-.682-1.28-1.45-2.56-2.219-3.841l-2.56-3.585c-.94-1.11-1.963-2.22-2.988-3.329-.512-.512-1.11-1.11-1.707-1.621-2.902-2.305-5.974-4.439-9.047-6.402-.427-.256-.853-.426-1.28-.683-2.049-1.536-4.012-2.219-6.06-2.902z'/></svg>",
+ "nest-gateway.clone": "<svg viewBox='0 0 300 300'><path fill='#afb42b' d='M172.382 24.41c-1.793 0-3.5.426-5.036.938 3.329 2.22 5.121 5.121 6.06 8.45.085.427.17.768.256 1.195s.17.768.17 1.195c.257 5.719-1.536 6.401-2.73 9.815-1.878 4.353-1.366 9.048.938 12.803.17.427.427.94.768 1.451-2.475-16.473 11.267-18.948 13.827-24.07.171-4.523-3.499-7.51-6.401-9.558-2.987-1.708-5.548-2.22-7.852-2.22zm20.655 3.67c-.256 1.536-.086 1.11-.17 1.877-.086.512-.086 1.195-.172 1.707-.17.512-.256 1.024-.426 1.537l-.512 1.536c-.257.512-.427.939-.683 1.536l-.512.768c-.171.171-.256.427-.427.598-.342.427-.683.939-.939 1.28-.427.427-.683.854-1.195 1.195v.085c-.427.342-.768.683-1.195 1.025-1.366 1.024-2.902 1.792-4.353 2.816-.427.342-.939.598-1.28.94-.427.34-.854.682-1.195 1.023l-1.195 1.195c-.341.427-.683.854-.939 1.28-.341.427-.683.94-.939 1.366-.256.512-.426.94-.683 1.537-.17.512-.426.938-.512 1.536-.17.597-.341 1.11-.426 1.622-.086.256-.086.597-.171.853s-.085.512-.17.768c0 .512-.086 1.11-.086 1.622 0 .427 0 .768.085 1.195 0 .512.086 1.024.17 1.622.086.512.172 1.024.342 1.536l.512 1.536c.171.342.342.683.427.94l-14.936-5.805c-2.561-.683-5.036-1.365-7.511-1.963-1.366-.341-2.732-.683-4.097-.939a137 137 0 0 0-11.864-1.792c-.17 0-.17-.086-.342-.086-3.926-.426-7.767-.597-11.608-.597-2.901 0-5.718.17-8.535.341-3.926.256-7.937.769-11.864 1.451-.939.171-1.963.342-2.902.513-2.048.426-3.926.853-5.889 1.28-.939.256-1.963.512-2.902.768-.939.427-1.878.854-2.816 1.195a23 23 0 0 1-2.134.939c-.171.085-.256.085-.342.17a34 34 0 0 0-1.792.94c-.17.085-.341.17-.427.17-.683.341-1.45.683-2.048 1.024-.427.171-.94.427-1.28.683-.171.17-.427.256-.598.342-.597.34-1.195.682-1.707.938-.598.342-1.11.683-1.536.94-.427.34-.94.597-1.28.938-.086.085-.171.085-.171.17a6.5 6.5 0 0 0-1.195.94l-.171.17c-.341.256-.683.513-.939.769-.17.085-.256.17-.427.256-.341.256-.682.597-.939.853-.085.17-.17.17-.256.256-.426.427-.768.683-1.195 1.11-.085 0-.085.085-.17.17-.427.342-.768.683-1.195 1.11-.085.085-.085.17-.17.17-.342.342-.684.684-.94 1.025-.17.17-.341.256-.426.427-.342.427-.683.768-1.11 1.195-.085.17-.17.17-.256.341-.512.512-.939 1.024-1.536 1.536l-.171.171c-1.024 1.11-2.134 2.22-3.329 3.158-1.195 1.024-2.39 2.049-3.67 2.902-1.28.939-2.56 1.707-3.926 2.475-1.28.683-2.646 1.366-4.097 1.963a36 36 0 0 1-4.268 1.537c-2.731.597-5.548 1.707-7.937 1.878-.513 0-1.11.17-1.622.17-.598.17-1.11.256-1.622.427s-1.024.427-1.536.597-1.024.427-1.536.683c-.427.342-.94.598-1.451.94-.427.34-.94.682-1.28 1.109-.428.341-.94.768-1.281 1.195-.427.426-.768.853-1.11 1.28-.341.512-.682.939-.938 1.536-.342.427-.683.94-.94 1.537s-.511 1.11-.682 1.707c-.17.512-.427 1.11-.598 1.707-.17.512-.256 1.024-.341 1.536 0 .085-.085.17-.085.17-.171.598-.171 1.366-.171 1.793-.085.427-.17.854-.17 1.28 0 .256 0 .598.085.854.085.427.17.853.256 1.195.17.427.256.768.426 1.195v.085c.171.427.427.768.683 1.195s.512.768.854 1.195c.341.341.683.683 1.11 1.024.426.427.767.683 1.194 1.024 1.537 1.366 1.963 1.793 3.926 2.902l1.025.512c.085 0 .17.086.17.086 0 .17 0 .17.086.341.085.512.17 1.024.341 1.537q.256.896.512 1.536c.256.64.342.768.512 1.195.086.17.171.256.171.341.256.512.512.94.768 1.451.342.427.683.94.94 1.366.34.427.682.853 1.109 1.195.426.427.768.683 1.195 1.11 0 0 .085.085.17.085.427.341.768.683 1.195.939.427.341.94.597 1.451.853.427.256.94.512 1.537.683.426.17.853.341 1.28.427.085.085.17.085.256.17.256.086.597.171.853.171-.17 3.5-.256 6.828.256 8.023.598 1.28 3.415-2.646 6.316-7.255-.426 4.524-.682 9.73 0 11.352s4.61-3.414 8.024-9.047c46.09-10.67 88.168 21.167 92.606 66.233-.853-6.999-9.474-10.925-13.485-9.986-1.963 4.78-5.292 11.01-10.584 14.851.427-4.268.256-8.706-.683-12.974-1.45 5.975-4.267 11.608-8.023 16.388-6.145.427-12.376-2.56-15.62-6.999-.255-.17-.34-.597-.511-.853-.17-.427-.427-.94-.512-1.366-.171-.427-.342-.939-.427-1.366s-.085-.938-.085-1.45v-.94c.085-.426.17-.938.341-1.365s.256-.939.427-1.366c.256-.426.426-.939.768-1.365 1.11-3.158 1.11-5.634-.939-7.17-.427-.256-.768-.427-1.195-.683-.256-.085-.597-.17-.853-.256-.171-.085-.342-.17-.513-.17-.426-.171-.938-.257-1.365-.342-.427-.17-.939-.17-1.366-.17-.427-.086-.939-.171-1.45-.171-.342 0-.684.085-.94.085-.512 0-.939.085-1.45.17-.427.086-.94.171-1.366.257-.427.17-.94.256-1.366.427s-.853.426-1.28.597-.768.427-1.195.683c-15.193 9.9-6.145 33.031 4.267 39.689-3.926.682-7.852 1.536-8.961 2.39l-.171.17a56 56 0 0 0 8.791 4.353c4.182 1.366 8.62 2.56 10.498 3.158v.085c5.378 1.11 10.84 1.537 16.388 1.195 28.764-2.048 52.406-23.898 56.674-52.833.17.598.256 1.11.426 1.708.171 1.194.427 2.39.598 3.67v.085c.17.597.17 1.195.256 1.707v.256c.085.598.17 1.195.17 1.707.086.683.171 1.451.171 2.134v1.024c0 .342.086.683.086 1.024 0 .427-.086.769-.086 1.195v.94c0 .426-.085.853-.085 1.28 0 .256 0 .512-.085.853 0 .427-.086.939-.086 1.451-.085.17-.085.427-.085.597-.085.513-.17.94-.17 1.537 0 .17 0 .427-.086.597-.085.683-.17 1.195-.256 1.878v.17c-.17.598-.256 1.196-.427 1.793v.17c-.17.598-.256 1.196-.427 1.793 0 .086-.085.17-.085.256-.17.598-.256 1.195-.427 1.793v.17c-.17.683-.427 1.195-.512 1.793-.085.085-.085.17-.085.17-.17.683-.427 1.28-.598 1.964-.256.682-.426 1.194-.683 1.877s-.426 1.28-.682 1.878c-.256.683-.512 1.195-.768 1.878h-.086c-.256.597-.512 1.195-.853 1.792-.086.17-.171.342-.171.427-.085.085-.085.17-.17.17-4.268 8.536-10.5 15.961-18.266 21.85-.512.342-1.024.684-1.536 1.11-.171.171-.342.171-.427.342a8.5 8.5 0 0 1-1.451.939l.17.426h.086c.939-.17 1.792-.256 2.731-.426h.085c1.708-.256 3.415-.598 5.036-.94.427-.085.94-.17 1.451-.34.342-.086.598-.171.94-.171.426-.086.938-.171 1.365-.256.427-.171.768-.171 1.195-.342 6.486-1.536 12.802-3.67 18.862-6.23-10.327 14.082-24.154 25.52-40.371 32.945a109 109 0 0 0 22.191-3.84c26.204-7.768 48.224-25.35 61.454-49.078-2.646 15.022-8.62 29.361-17.497 41.823 6.316-4.183 12.12-8.962 17.326-14.425 14.595-15.193 24.155-34.568 27.398-55.308 2.22 10.242 2.902 20.911 1.878 31.324 46.944-65.465 3.926-133.405-14.083-151.244-.085-.17-.17-.17-.17-.341-.086.085-.086.085-.086.17 0-.085 0-.085-.086-.17 0 .768-.085 1.536-.17 2.304-.17 1.537-.427 2.902-.683 4.353-.341 1.451-.683 2.902-1.11 4.268s-.938 2.817-1.536 4.182c-.597 1.28-1.195 2.646-1.963 3.926a53 53 0 0 1-2.305 3.67c-.853 1.196-1.792 2.39-2.645 3.5-.94 1.195-2.049 2.22-3.073 3.243-.683.598-1.195 1.11-1.878 1.622-.512.427-.939.854-1.536 1.28-1.195.94-2.305 1.793-3.67 2.56-1.195.77-2.56 1.537-3.841 2.22-1.366.683-2.731 1.195-4.097 1.792-1.366.513-2.817.94-4.268 1.366s-2.902.683-4.353.939a42 42 0 0 1-4.438.512c-1.024.085-2.048.17-3.158.17-1.536 0-2.987-.17-4.438-.255-1.537-.171-2.988-.342-4.439-.683-1.536-.256-2.902-.683-4.353-1.11h-.085c1.451-.17 2.902-.256 4.268-.512 1.536-.256 2.902-.597 4.353-.939 1.45-.426 2.902-.853 4.267-1.365 1.451-.512 2.817-1.195 4.097-1.793 1.366-.682 2.56-1.365 3.926-2.133 1.195-.854 2.476-1.707 3.67-2.561 1.195-.939 2.305-1.878 3.33-2.902 1.109-.939 2.048-2.048 3.072-3.158.939-1.195 1.878-2.305 2.731-3.5.17-.17.256-.426.427-.682a39 39 0 0 0 1.878-3.158c.682-1.28 1.365-2.56 1.963-3.926s1.11-2.732 1.536-4.183c.427-1.365.768-2.816 1.11-4.267.256-1.537.512-2.902.682-4.353.171-1.537.257-2.988.257-4.439 0-1.024-.086-2.048-.171-3.158-.17-1.536-.342-2.902-.512-4.353-.256-1.536-.598-2.902-.94-4.352-.426-1.366-.938-2.817-1.45-4.183s-1.195-2.731-1.793-4.011c-.682-1.28-1.45-2.56-2.219-3.841l-2.56-3.585c-.94-1.11-1.963-2.22-2.988-3.329-.512-.512-1.11-1.11-1.707-1.621-2.902-2.305-5.974-4.439-9.047-6.402-.427-.256-.853-.426-1.28-.683-2.049-1.536-4.012-2.219-6.06-2.902z'/></svg>",
+ "nest-guard.clone": "<svg viewBox='0 0 300 300'><path fill='#43a047' d='M172.382 24.41c-1.793 0-3.5.426-5.036.938 3.329 2.22 5.121 5.121 6.06 8.45.085.427.17.768.256 1.195s.17.768.17 1.195c.257 5.719-1.536 6.401-2.73 9.815-1.878 4.353-1.366 9.048.938 12.803.17.427.427.94.768 1.451-2.475-16.473 11.267-18.948 13.827-24.07.171-4.523-3.499-7.51-6.401-9.558-2.987-1.708-5.548-2.22-7.852-2.22zm20.655 3.67c-.256 1.536-.086 1.11-.17 1.877-.086.512-.086 1.195-.172 1.707-.17.512-.256 1.024-.426 1.537l-.512 1.536c-.257.512-.427.939-.683 1.536l-.512.768c-.171.171-.256.427-.427.598-.342.427-.683.939-.939 1.28-.427.427-.683.854-1.195 1.195v.085c-.427.342-.768.683-1.195 1.025-1.366 1.024-2.902 1.792-4.353 2.816-.427.342-.939.598-1.28.94-.427.34-.854.682-1.195 1.023l-1.195 1.195c-.341.427-.683.854-.939 1.28-.341.427-.683.94-.939 1.366-.256.512-.426.94-.683 1.537-.17.512-.426.938-.512 1.536-.17.597-.341 1.11-.426 1.622-.086.256-.086.597-.171.853s-.085.512-.17.768c0 .512-.086 1.11-.086 1.622 0 .427 0 .768.085 1.195 0 .512.086 1.024.17 1.622.086.512.172 1.024.342 1.536l.512 1.536c.171.342.342.683.427.94l-14.936-5.805c-2.561-.683-5.036-1.365-7.511-1.963-1.366-.341-2.732-.683-4.097-.939a137 137 0 0 0-11.864-1.792c-.17 0-.17-.086-.342-.086-3.926-.426-7.767-.597-11.608-.597-2.901 0-5.718.17-8.535.341-3.926.256-7.937.769-11.864 1.451-.939.171-1.963.342-2.902.513-2.048.426-3.926.853-5.889 1.28-.939.256-1.963.512-2.902.768-.939.427-1.878.854-2.816 1.195a23 23 0 0 1-2.134.939c-.171.085-.256.085-.342.17a34 34 0 0 0-1.792.94c-.17.085-.341.17-.427.17-.683.341-1.45.683-2.048 1.024-.427.171-.94.427-1.28.683-.171.17-.427.256-.598.342-.597.34-1.195.682-1.707.938-.598.342-1.11.683-1.536.94-.427.34-.94.597-1.28.938-.086.085-.171.085-.171.17a6.5 6.5 0 0 0-1.195.94l-.171.17c-.341.256-.683.513-.939.769-.17.085-.256.17-.427.256-.341.256-.682.597-.939.853-.085.17-.17.17-.256.256-.426.427-.768.683-1.195 1.11-.085 0-.085.085-.17.17-.427.342-.768.683-1.195 1.11-.085.085-.085.17-.17.17-.342.342-.684.684-.94 1.025-.17.17-.341.256-.426.427-.342.427-.683.768-1.11 1.195-.085.17-.17.17-.256.341-.512.512-.939 1.024-1.536 1.536l-.171.171c-1.024 1.11-2.134 2.22-3.329 3.158-1.195 1.024-2.39 2.049-3.67 2.902-1.28.939-2.56 1.707-3.926 2.475-1.28.683-2.646 1.366-4.097 1.963a36 36 0 0 1-4.268 1.537c-2.731.597-5.548 1.707-7.937 1.878-.513 0-1.11.17-1.622.17-.598.17-1.11.256-1.622.427s-1.024.427-1.536.597-1.024.427-1.536.683c-.427.342-.94.598-1.451.94-.427.34-.94.682-1.28 1.109-.428.341-.94.768-1.281 1.195-.427.426-.768.853-1.11 1.28-.341.512-.682.939-.938 1.536-.342.427-.683.94-.94 1.537s-.511 1.11-.682 1.707c-.17.512-.427 1.11-.598 1.707-.17.512-.256 1.024-.341 1.536 0 .085-.085.17-.085.17-.171.598-.171 1.366-.171 1.793-.085.427-.17.854-.17 1.28 0 .256 0 .598.085.854.085.427.17.853.256 1.195.17.427.256.768.426 1.195v.085c.171.427.427.768.683 1.195s.512.768.854 1.195c.341.341.683.683 1.11 1.024.426.427.767.683 1.194 1.024 1.537 1.366 1.963 1.793 3.926 2.902l1.025.512c.085 0 .17.086.17.086 0 .17 0 .17.086.341.085.512.17 1.024.341 1.537q.256.896.512 1.536c.256.64.342.768.512 1.195.086.17.171.256.171.341.256.512.512.94.768 1.451.342.427.683.94.94 1.366.34.427.682.853 1.109 1.195.426.427.768.683 1.195 1.11 0 0 .085.085.17.085.427.341.768.683 1.195.939.427.341.94.597 1.451.853.427.256.94.512 1.537.683.426.17.853.341 1.28.427.085.085.17.085.256.17.256.086.597.171.853.171-.17 3.5-.256 6.828.256 8.023.598 1.28 3.415-2.646 6.316-7.255-.426 4.524-.682 9.73 0 11.352s4.61-3.414 8.024-9.047c46.09-10.67 88.168 21.167 92.606 66.233-.853-6.999-9.474-10.925-13.485-9.986-1.963 4.78-5.292 11.01-10.584 14.851.427-4.268.256-8.706-.683-12.974-1.45 5.975-4.267 11.608-8.023 16.388-6.145.427-12.376-2.56-15.62-6.999-.255-.17-.34-.597-.511-.853-.17-.427-.427-.94-.512-1.366-.171-.427-.342-.939-.427-1.366s-.085-.938-.085-1.45v-.94c.085-.426.17-.938.341-1.365s.256-.939.427-1.366c.256-.426.426-.939.768-1.365 1.11-3.158 1.11-5.634-.939-7.17-.427-.256-.768-.427-1.195-.683-.256-.085-.597-.17-.853-.256-.171-.085-.342-.17-.513-.17-.426-.171-.938-.257-1.365-.342-.427-.17-.939-.17-1.366-.17-.427-.086-.939-.171-1.45-.171-.342 0-.684.085-.94.085-.512 0-.939.085-1.45.17-.427.086-.94.171-1.366.257-.427.17-.94.256-1.366.427s-.853.426-1.28.597-.768.427-1.195.683c-15.193 9.9-6.145 33.031 4.267 39.689-3.926.682-7.852 1.536-8.961 2.39l-.171.17a56 56 0 0 0 8.791 4.353c4.182 1.366 8.62 2.56 10.498 3.158v.085c5.378 1.11 10.84 1.537 16.388 1.195 28.764-2.048 52.406-23.898 56.674-52.833.17.598.256 1.11.426 1.708.171 1.194.427 2.39.598 3.67v.085c.17.597.17 1.195.256 1.707v.256c.085.598.17 1.195.17 1.707.086.683.171 1.451.171 2.134v1.024c0 .342.086.683.086 1.024 0 .427-.086.769-.086 1.195v.94c0 .426-.085.853-.085 1.28 0 .256 0 .512-.085.853 0 .427-.086.939-.086 1.451-.085.17-.085.427-.085.597-.085.513-.17.94-.17 1.537 0 .17 0 .427-.086.597-.085.683-.17 1.195-.256 1.878v.17c-.17.598-.256 1.196-.427 1.793v.17c-.17.598-.256 1.196-.427 1.793 0 .086-.085.17-.085.256-.17.598-.256 1.195-.427 1.793v.17c-.17.683-.427 1.195-.512 1.793-.085.085-.085.17-.085.17-.17.683-.427 1.28-.598 1.964-.256.682-.426 1.194-.683 1.877s-.426 1.28-.682 1.878c-.256.683-.512 1.195-.768 1.878h-.086c-.256.597-.512 1.195-.853 1.792-.086.17-.171.342-.171.427-.085.085-.085.17-.17.17-4.268 8.536-10.5 15.961-18.266 21.85-.512.342-1.024.684-1.536 1.11-.171.171-.342.171-.427.342a8.5 8.5 0 0 1-1.451.939l.17.426h.086c.939-.17 1.792-.256 2.731-.426h.085c1.708-.256 3.415-.598 5.036-.94.427-.085.94-.17 1.451-.34.342-.086.598-.171.94-.171.426-.086.938-.171 1.365-.256.427-.171.768-.171 1.195-.342 6.486-1.536 12.802-3.67 18.862-6.23-10.327 14.082-24.154 25.52-40.371 32.945a109 109 0 0 0 22.191-3.84c26.204-7.768 48.224-25.35 61.454-49.078-2.646 15.022-8.62 29.361-17.497 41.823 6.316-4.183 12.12-8.962 17.326-14.425 14.595-15.193 24.155-34.568 27.398-55.308 2.22 10.242 2.902 20.911 1.878 31.324 46.944-65.465 3.926-133.405-14.083-151.244-.085-.17-.17-.17-.17-.341-.086.085-.086.085-.086.17 0-.085 0-.085-.086-.17 0 .768-.085 1.536-.17 2.304-.17 1.537-.427 2.902-.683 4.353-.341 1.451-.683 2.902-1.11 4.268s-.938 2.817-1.536 4.182c-.597 1.28-1.195 2.646-1.963 3.926a53 53 0 0 1-2.305 3.67c-.853 1.196-1.792 2.39-2.645 3.5-.94 1.195-2.049 2.22-3.073 3.243-.683.598-1.195 1.11-1.878 1.622-.512.427-.939.854-1.536 1.28-1.195.94-2.305 1.793-3.67 2.56-1.195.77-2.56 1.537-3.841 2.22-1.366.683-2.731 1.195-4.097 1.792-1.366.513-2.817.94-4.268 1.366s-2.902.683-4.353.939a42 42 0 0 1-4.438.512c-1.024.085-2.048.17-3.158.17-1.536 0-2.987-.17-4.438-.255-1.537-.171-2.988-.342-4.439-.683-1.536-.256-2.902-.683-4.353-1.11h-.085c1.451-.17 2.902-.256 4.268-.512 1.536-.256 2.902-.597 4.353-.939 1.45-.426 2.902-.853 4.267-1.365 1.451-.512 2.817-1.195 4.097-1.793 1.366-.682 2.56-1.365 3.926-2.133 1.195-.854 2.476-1.707 3.67-2.561 1.195-.939 2.305-1.878 3.33-2.902 1.109-.939 2.048-2.048 3.072-3.158.939-1.195 1.878-2.305 2.731-3.5.17-.17.256-.426.427-.682a39 39 0 0 0 1.878-3.158c.682-1.28 1.365-2.56 1.963-3.926s1.11-2.732 1.536-4.183c.427-1.365.768-2.816 1.11-4.267.256-1.537.512-2.902.682-4.353.171-1.537.257-2.988.257-4.439 0-1.024-.086-2.048-.171-3.158-.17-1.536-.342-2.902-.512-4.353-.256-1.536-.598-2.902-.94-4.352-.426-1.366-.938-2.817-1.45-4.183s-1.195-2.731-1.793-4.011c-.682-1.28-1.45-2.56-2.219-3.841l-2.56-3.585c-.94-1.11-1.963-2.22-2.988-3.329-.512-.512-1.11-1.11-1.707-1.621-2.902-2.305-5.974-4.439-9.047-6.402-.427-.256-.853-.426-1.28-.683-2.049-1.536-4.012-2.219-6.06-2.902z'/></svg>",
+ "nest-interceptor.clone": "<svg viewBox='0 0 300 300'><path fill='#ff9800' d='M172.382 24.41c-1.793 0-3.5.426-5.036.938 3.329 2.22 5.121 5.121 6.06 8.45.085.427.17.768.256 1.195s.17.768.17 1.195c.257 5.719-1.536 6.401-2.73 9.815-1.878 4.353-1.366 9.048.938 12.803.17.427.427.94.768 1.451-2.475-16.473 11.267-18.948 13.827-24.07.171-4.523-3.499-7.51-6.401-9.558-2.987-1.708-5.548-2.22-7.852-2.22zm20.655 3.67c-.256 1.536-.086 1.11-.17 1.877-.086.512-.086 1.195-.172 1.707-.17.512-.256 1.024-.426 1.537l-.512 1.536c-.257.512-.427.939-.683 1.536l-.512.768c-.171.171-.256.427-.427.598-.342.427-.683.939-.939 1.28-.427.427-.683.854-1.195 1.195v.085c-.427.342-.768.683-1.195 1.025-1.366 1.024-2.902 1.792-4.353 2.816-.427.342-.939.598-1.28.94-.427.34-.854.682-1.195 1.023l-1.195 1.195c-.341.427-.683.854-.939 1.28-.341.427-.683.94-.939 1.366-.256.512-.426.94-.683 1.537-.17.512-.426.938-.512 1.536-.17.597-.341 1.11-.426 1.622-.086.256-.086.597-.171.853s-.085.512-.17.768c0 .512-.086 1.11-.086 1.622 0 .427 0 .768.085 1.195 0 .512.086 1.024.17 1.622.086.512.172 1.024.342 1.536l.512 1.536c.171.342.342.683.427.94l-14.936-5.805c-2.561-.683-5.036-1.365-7.511-1.963-1.366-.341-2.732-.683-4.097-.939a137 137 0 0 0-11.864-1.792c-.17 0-.17-.086-.342-.086-3.926-.426-7.767-.597-11.608-.597-2.901 0-5.718.17-8.535.341-3.926.256-7.937.769-11.864 1.451-.939.171-1.963.342-2.902.513-2.048.426-3.926.853-5.889 1.28-.939.256-1.963.512-2.902.768-.939.427-1.878.854-2.816 1.195a23 23 0 0 1-2.134.939c-.171.085-.256.085-.342.17a34 34 0 0 0-1.792.94c-.17.085-.341.17-.427.17-.683.341-1.45.683-2.048 1.024-.427.171-.94.427-1.28.683-.171.17-.427.256-.598.342-.597.34-1.195.682-1.707.938-.598.342-1.11.683-1.536.94-.427.34-.94.597-1.28.938-.086.085-.171.085-.171.17a6.5 6.5 0 0 0-1.195.94l-.171.17c-.341.256-.683.513-.939.769-.17.085-.256.17-.427.256-.341.256-.682.597-.939.853-.085.17-.17.17-.256.256-.426.427-.768.683-1.195 1.11-.085 0-.085.085-.17.17-.427.342-.768.683-1.195 1.11-.085.085-.085.17-.17.17-.342.342-.684.684-.94 1.025-.17.17-.341.256-.426.427-.342.427-.683.768-1.11 1.195-.085.17-.17.17-.256.341-.512.512-.939 1.024-1.536 1.536l-.171.171c-1.024 1.11-2.134 2.22-3.329 3.158-1.195 1.024-2.39 2.049-3.67 2.902-1.28.939-2.56 1.707-3.926 2.475-1.28.683-2.646 1.366-4.097 1.963a36 36 0 0 1-4.268 1.537c-2.731.597-5.548 1.707-7.937 1.878-.513 0-1.11.17-1.622.17-.598.17-1.11.256-1.622.427s-1.024.427-1.536.597-1.024.427-1.536.683c-.427.342-.94.598-1.451.94-.427.34-.94.682-1.28 1.109-.428.341-.94.768-1.281 1.195-.427.426-.768.853-1.11 1.28-.341.512-.682.939-.938 1.536-.342.427-.683.94-.94 1.537s-.511 1.11-.682 1.707c-.17.512-.427 1.11-.598 1.707-.17.512-.256 1.024-.341 1.536 0 .085-.085.17-.085.17-.171.598-.171 1.366-.171 1.793-.085.427-.17.854-.17 1.28 0 .256 0 .598.085.854.085.427.17.853.256 1.195.17.427.256.768.426 1.195v.085c.171.427.427.768.683 1.195s.512.768.854 1.195c.341.341.683.683 1.11 1.024.426.427.767.683 1.194 1.024 1.537 1.366 1.963 1.793 3.926 2.902l1.025.512c.085 0 .17.086.17.086 0 .17 0 .17.086.341.085.512.17 1.024.341 1.537q.256.896.512 1.536c.256.64.342.768.512 1.195.086.17.171.256.171.341.256.512.512.94.768 1.451.342.427.683.94.94 1.366.34.427.682.853 1.109 1.195.426.427.768.683 1.195 1.11 0 0 .085.085.17.085.427.341.768.683 1.195.939.427.341.94.597 1.451.853.427.256.94.512 1.537.683.426.17.853.341 1.28.427.085.085.17.085.256.17.256.086.597.171.853.171-.17 3.5-.256 6.828.256 8.023.598 1.28 3.415-2.646 6.316-7.255-.426 4.524-.682 9.73 0 11.352s4.61-3.414 8.024-9.047c46.09-10.67 88.168 21.167 92.606 66.233-.853-6.999-9.474-10.925-13.485-9.986-1.963 4.78-5.292 11.01-10.584 14.851.427-4.268.256-8.706-.683-12.974-1.45 5.975-4.267 11.608-8.023 16.388-6.145.427-12.376-2.56-15.62-6.999-.255-.17-.34-.597-.511-.853-.17-.427-.427-.94-.512-1.366-.171-.427-.342-.939-.427-1.366s-.085-.938-.085-1.45v-.94c.085-.426.17-.938.341-1.365s.256-.939.427-1.366c.256-.426.426-.939.768-1.365 1.11-3.158 1.11-5.634-.939-7.17-.427-.256-.768-.427-1.195-.683-.256-.085-.597-.17-.853-.256-.171-.085-.342-.17-.513-.17-.426-.171-.938-.257-1.365-.342-.427-.17-.939-.17-1.366-.17-.427-.086-.939-.171-1.45-.171-.342 0-.684.085-.94.085-.512 0-.939.085-1.45.17-.427.086-.94.171-1.366.257-.427.17-.94.256-1.366.427s-.853.426-1.28.597-.768.427-1.195.683c-15.193 9.9-6.145 33.031 4.267 39.689-3.926.682-7.852 1.536-8.961 2.39l-.171.17a56 56 0 0 0 8.791 4.353c4.182 1.366 8.62 2.56 10.498 3.158v.085c5.378 1.11 10.84 1.537 16.388 1.195 28.764-2.048 52.406-23.898 56.674-52.833.17.598.256 1.11.426 1.708.171 1.194.427 2.39.598 3.67v.085c.17.597.17 1.195.256 1.707v.256c.085.598.17 1.195.17 1.707.086.683.171 1.451.171 2.134v1.024c0 .342.086.683.086 1.024 0 .427-.086.769-.086 1.195v.94c0 .426-.085.853-.085 1.28 0 .256 0 .512-.085.853 0 .427-.086.939-.086 1.451-.085.17-.085.427-.085.597-.085.513-.17.94-.17 1.537 0 .17 0 .427-.086.597-.085.683-.17 1.195-.256 1.878v.17c-.17.598-.256 1.196-.427 1.793v.17c-.17.598-.256 1.196-.427 1.793 0 .086-.085.17-.085.256-.17.598-.256 1.195-.427 1.793v.17c-.17.683-.427 1.195-.512 1.793-.085.085-.085.17-.085.17-.17.683-.427 1.28-.598 1.964-.256.682-.426 1.194-.683 1.877s-.426 1.28-.682 1.878c-.256.683-.512 1.195-.768 1.878h-.086c-.256.597-.512 1.195-.853 1.792-.086.17-.171.342-.171.427-.085.085-.085.17-.17.17-4.268 8.536-10.5 15.961-18.266 21.85-.512.342-1.024.684-1.536 1.11-.171.171-.342.171-.427.342a8.5 8.5 0 0 1-1.451.939l.17.426h.086c.939-.17 1.792-.256 2.731-.426h.085c1.708-.256 3.415-.598 5.036-.94.427-.085.94-.17 1.451-.34.342-.086.598-.171.94-.171.426-.086.938-.171 1.365-.256.427-.171.768-.171 1.195-.342 6.486-1.536 12.802-3.67 18.862-6.23-10.327 14.082-24.154 25.52-40.371 32.945a109 109 0 0 0 22.191-3.84c26.204-7.768 48.224-25.35 61.454-49.078-2.646 15.022-8.62 29.361-17.497 41.823 6.316-4.183 12.12-8.962 17.326-14.425 14.595-15.193 24.155-34.568 27.398-55.308 2.22 10.242 2.902 20.911 1.878 31.324 46.944-65.465 3.926-133.405-14.083-151.244-.085-.17-.17-.17-.17-.341-.086.085-.086.085-.086.17 0-.085 0-.085-.086-.17 0 .768-.085 1.536-.17 2.304-.17 1.537-.427 2.902-.683 4.353-.341 1.451-.683 2.902-1.11 4.268s-.938 2.817-1.536 4.182c-.597 1.28-1.195 2.646-1.963 3.926a53 53 0 0 1-2.305 3.67c-.853 1.196-1.792 2.39-2.645 3.5-.94 1.195-2.049 2.22-3.073 3.243-.683.598-1.195 1.11-1.878 1.622-.512.427-.939.854-1.536 1.28-1.195.94-2.305 1.793-3.67 2.56-1.195.77-2.56 1.537-3.841 2.22-1.366.683-2.731 1.195-4.097 1.792-1.366.513-2.817.94-4.268 1.366s-2.902.683-4.353.939a42 42 0 0 1-4.438.512c-1.024.085-2.048.17-3.158.17-1.536 0-2.987-.17-4.438-.255-1.537-.171-2.988-.342-4.439-.683-1.536-.256-2.902-.683-4.353-1.11h-.085c1.451-.17 2.902-.256 4.268-.512 1.536-.256 2.902-.597 4.353-.939 1.45-.426 2.902-.853 4.267-1.365 1.451-.512 2.817-1.195 4.097-1.793 1.366-.682 2.56-1.365 3.926-2.133 1.195-.854 2.476-1.707 3.67-2.561 1.195-.939 2.305-1.878 3.33-2.902 1.109-.939 2.048-2.048 3.072-3.158.939-1.195 1.878-2.305 2.731-3.5.17-.17.256-.426.427-.682a39 39 0 0 0 1.878-3.158c.682-1.28 1.365-2.56 1.963-3.926s1.11-2.732 1.536-4.183c.427-1.365.768-2.816 1.11-4.267.256-1.537.512-2.902.682-4.353.171-1.537.257-2.988.257-4.439 0-1.024-.086-2.048-.171-3.158-.17-1.536-.342-2.902-.512-4.353-.256-1.536-.598-2.902-.94-4.352-.426-1.366-.938-2.817-1.45-4.183s-1.195-2.731-1.793-4.011c-.682-1.28-1.45-2.56-2.219-3.841l-2.56-3.585c-.94-1.11-1.963-2.22-2.988-3.329-.512-.512-1.11-1.11-1.707-1.621-2.902-2.305-5.974-4.439-9.047-6.402-.427-.256-.853-.426-1.28-.683-2.049-1.536-4.012-2.219-6.06-2.902z'/></svg>",
+ "nest-middleware.clone": "<svg viewBox='0 0 300 300'><path fill='#5c6bc0' d='M172.382 24.41c-1.793 0-3.5.426-5.036.938 3.329 2.22 5.121 5.121 6.06 8.45.085.427.17.768.256 1.195s.17.768.17 1.195c.257 5.719-1.536 6.401-2.73 9.815-1.878 4.353-1.366 9.048.938 12.803.17.427.427.94.768 1.451-2.475-16.473 11.267-18.948 13.827-24.07.171-4.523-3.499-7.51-6.401-9.558-2.987-1.708-5.548-2.22-7.852-2.22zm20.655 3.67c-.256 1.536-.086 1.11-.17 1.877-.086.512-.086 1.195-.172 1.707-.17.512-.256 1.024-.426 1.537l-.512 1.536c-.257.512-.427.939-.683 1.536l-.512.768c-.171.171-.256.427-.427.598-.342.427-.683.939-.939 1.28-.427.427-.683.854-1.195 1.195v.085c-.427.342-.768.683-1.195 1.025-1.366 1.024-2.902 1.792-4.353 2.816-.427.342-.939.598-1.28.94-.427.34-.854.682-1.195 1.023l-1.195 1.195c-.341.427-.683.854-.939 1.28-.341.427-.683.94-.939 1.366-.256.512-.426.94-.683 1.537-.17.512-.426.938-.512 1.536-.17.597-.341 1.11-.426 1.622-.086.256-.086.597-.171.853s-.085.512-.17.768c0 .512-.086 1.11-.086 1.622 0 .427 0 .768.085 1.195 0 .512.086 1.024.17 1.622.086.512.172 1.024.342 1.536l.512 1.536c.171.342.342.683.427.94l-14.936-5.805c-2.561-.683-5.036-1.365-7.511-1.963-1.366-.341-2.732-.683-4.097-.939a137 137 0 0 0-11.864-1.792c-.17 0-.17-.086-.342-.086-3.926-.426-7.767-.597-11.608-.597-2.901 0-5.718.17-8.535.341-3.926.256-7.937.769-11.864 1.451-.939.171-1.963.342-2.902.513-2.048.426-3.926.853-5.889 1.28-.939.256-1.963.512-2.902.768-.939.427-1.878.854-2.816 1.195a23 23 0 0 1-2.134.939c-.171.085-.256.085-.342.17a34 34 0 0 0-1.792.94c-.17.085-.341.17-.427.17-.683.341-1.45.683-2.048 1.024-.427.171-.94.427-1.28.683-.171.17-.427.256-.598.342-.597.34-1.195.682-1.707.938-.598.342-1.11.683-1.536.94-.427.34-.94.597-1.28.938-.086.085-.171.085-.171.17a6.5 6.5 0 0 0-1.195.94l-.171.17c-.341.256-.683.513-.939.769-.17.085-.256.17-.427.256-.341.256-.682.597-.939.853-.085.17-.17.17-.256.256-.426.427-.768.683-1.195 1.11-.085 0-.085.085-.17.17-.427.342-.768.683-1.195 1.11-.085.085-.085.17-.17.17-.342.342-.684.684-.94 1.025-.17.17-.341.256-.426.427-.342.427-.683.768-1.11 1.195-.085.17-.17.17-.256.341-.512.512-.939 1.024-1.536 1.536l-.171.171c-1.024 1.11-2.134 2.22-3.329 3.158-1.195 1.024-2.39 2.049-3.67 2.902-1.28.939-2.56 1.707-3.926 2.475-1.28.683-2.646 1.366-4.097 1.963a36 36 0 0 1-4.268 1.537c-2.731.597-5.548 1.707-7.937 1.878-.513 0-1.11.17-1.622.17-.598.17-1.11.256-1.622.427s-1.024.427-1.536.597-1.024.427-1.536.683c-.427.342-.94.598-1.451.94-.427.34-.94.682-1.28 1.109-.428.341-.94.768-1.281 1.195-.427.426-.768.853-1.11 1.28-.341.512-.682.939-.938 1.536-.342.427-.683.94-.94 1.537s-.511 1.11-.682 1.707c-.17.512-.427 1.11-.598 1.707-.17.512-.256 1.024-.341 1.536 0 .085-.085.17-.085.17-.171.598-.171 1.366-.171 1.793-.085.427-.17.854-.17 1.28 0 .256 0 .598.085.854.085.427.17.853.256 1.195.17.427.256.768.426 1.195v.085c.171.427.427.768.683 1.195s.512.768.854 1.195c.341.341.683.683 1.11 1.024.426.427.767.683 1.194 1.024 1.537 1.366 1.963 1.793 3.926 2.902l1.025.512c.085 0 .17.086.17.086 0 .17 0 .17.086.341.085.512.17 1.024.341 1.537q.256.896.512 1.536c.256.64.342.768.512 1.195.086.17.171.256.171.341.256.512.512.94.768 1.451.342.427.683.94.94 1.366.34.427.682.853 1.109 1.195.426.427.768.683 1.195 1.11 0 0 .085.085.17.085.427.341.768.683 1.195.939.427.341.94.597 1.451.853.427.256.94.512 1.537.683.426.17.853.341 1.28.427.085.085.17.085.256.17.256.086.597.171.853.171-.17 3.5-.256 6.828.256 8.023.598 1.28 3.415-2.646 6.316-7.255-.426 4.524-.682 9.73 0 11.352s4.61-3.414 8.024-9.047c46.09-10.67 88.168 21.167 92.606 66.233-.853-6.999-9.474-10.925-13.485-9.986-1.963 4.78-5.292 11.01-10.584 14.851.427-4.268.256-8.706-.683-12.974-1.45 5.975-4.267 11.608-8.023 16.388-6.145.427-12.376-2.56-15.62-6.999-.255-.17-.34-.597-.511-.853-.17-.427-.427-.94-.512-1.366-.171-.427-.342-.939-.427-1.366s-.085-.938-.085-1.45v-.94c.085-.426.17-.938.341-1.365s.256-.939.427-1.366c.256-.426.426-.939.768-1.365 1.11-3.158 1.11-5.634-.939-7.17-.427-.256-.768-.427-1.195-.683-.256-.085-.597-.17-.853-.256-.171-.085-.342-.17-.513-.17-.426-.171-.938-.257-1.365-.342-.427-.17-.939-.17-1.366-.17-.427-.086-.939-.171-1.45-.171-.342 0-.684.085-.94.085-.512 0-.939.085-1.45.17-.427.086-.94.171-1.366.257-.427.17-.94.256-1.366.427s-.853.426-1.28.597-.768.427-1.195.683c-15.193 9.9-6.145 33.031 4.267 39.689-3.926.682-7.852 1.536-8.961 2.39l-.171.17a56 56 0 0 0 8.791 4.353c4.182 1.366 8.62 2.56 10.498 3.158v.085c5.378 1.11 10.84 1.537 16.388 1.195 28.764-2.048 52.406-23.898 56.674-52.833.17.598.256 1.11.426 1.708.171 1.194.427 2.39.598 3.67v.085c.17.597.17 1.195.256 1.707v.256c.085.598.17 1.195.17 1.707.086.683.171 1.451.171 2.134v1.024c0 .342.086.683.086 1.024 0 .427-.086.769-.086 1.195v.94c0 .426-.085.853-.085 1.28 0 .256 0 .512-.085.853 0 .427-.086.939-.086 1.451-.085.17-.085.427-.085.597-.085.513-.17.94-.17 1.537 0 .17 0 .427-.086.597-.085.683-.17 1.195-.256 1.878v.17c-.17.598-.256 1.196-.427 1.793v.17c-.17.598-.256 1.196-.427 1.793 0 .086-.085.17-.085.256-.17.598-.256 1.195-.427 1.793v.17c-.17.683-.427 1.195-.512 1.793-.085.085-.085.17-.085.17-.17.683-.427 1.28-.598 1.964-.256.682-.426 1.194-.683 1.877s-.426 1.28-.682 1.878c-.256.683-.512 1.195-.768 1.878h-.086c-.256.597-.512 1.195-.853 1.792-.086.17-.171.342-.171.427-.085.085-.085.17-.17.17-4.268 8.536-10.5 15.961-18.266 21.85-.512.342-1.024.684-1.536 1.11-.171.171-.342.171-.427.342a8.5 8.5 0 0 1-1.451.939l.17.426h.086c.939-.17 1.792-.256 2.731-.426h.085c1.708-.256 3.415-.598 5.036-.94.427-.085.94-.17 1.451-.34.342-.086.598-.171.94-.171.426-.086.938-.171 1.365-.256.427-.171.768-.171 1.195-.342 6.486-1.536 12.802-3.67 18.862-6.23-10.327 14.082-24.154 25.52-40.371 32.945a109 109 0 0 0 22.191-3.84c26.204-7.768 48.224-25.35 61.454-49.078-2.646 15.022-8.62 29.361-17.497 41.823 6.316-4.183 12.12-8.962 17.326-14.425 14.595-15.193 24.155-34.568 27.398-55.308 2.22 10.242 2.902 20.911 1.878 31.324 46.944-65.465 3.926-133.405-14.083-151.244-.085-.17-.17-.17-.17-.341-.086.085-.086.085-.086.17 0-.085 0-.085-.086-.17 0 .768-.085 1.536-.17 2.304-.17 1.537-.427 2.902-.683 4.353-.341 1.451-.683 2.902-1.11 4.268s-.938 2.817-1.536 4.182c-.597 1.28-1.195 2.646-1.963 3.926a53 53 0 0 1-2.305 3.67c-.853 1.196-1.792 2.39-2.645 3.5-.94 1.195-2.049 2.22-3.073 3.243-.683.598-1.195 1.11-1.878 1.622-.512.427-.939.854-1.536 1.28-1.195.94-2.305 1.793-3.67 2.56-1.195.77-2.56 1.537-3.841 2.22-1.366.683-2.731 1.195-4.097 1.792-1.366.513-2.817.94-4.268 1.366s-2.902.683-4.353.939a42 42 0 0 1-4.438.512c-1.024.085-2.048.17-3.158.17-1.536 0-2.987-.17-4.438-.255-1.537-.171-2.988-.342-4.439-.683-1.536-.256-2.902-.683-4.353-1.11h-.085c1.451-.17 2.902-.256 4.268-.512 1.536-.256 2.902-.597 4.353-.939 1.45-.426 2.902-.853 4.267-1.365 1.451-.512 2.817-1.195 4.097-1.793 1.366-.682 2.56-1.365 3.926-2.133 1.195-.854 2.476-1.707 3.67-2.561 1.195-.939 2.305-1.878 3.33-2.902 1.109-.939 2.048-2.048 3.072-3.158.939-1.195 1.878-2.305 2.731-3.5.17-.17.256-.426.427-.682a39 39 0 0 0 1.878-3.158c.682-1.28 1.365-2.56 1.963-3.926s1.11-2.732 1.536-4.183c.427-1.365.768-2.816 1.11-4.267.256-1.537.512-2.902.682-4.353.171-1.537.257-2.988.257-4.439 0-1.024-.086-2.048-.171-3.158-.17-1.536-.342-2.902-.512-4.353-.256-1.536-.598-2.902-.94-4.352-.426-1.366-.938-2.817-1.45-4.183s-1.195-2.731-1.793-4.011c-.682-1.28-1.45-2.56-2.219-3.841l-2.56-3.585c-.94-1.11-1.963-2.22-2.988-3.329-.512-.512-1.11-1.11-1.707-1.621-2.902-2.305-5.974-4.439-9.047-6.402-.427-.256-.853-.426-1.28-.683-2.049-1.536-4.012-2.219-6.06-2.902z'/></svg>",
+ "nest-module.clone": "<svg viewBox='0 0 300 300'><path fill='#e53935' d='M172.382 24.41c-1.793 0-3.5.426-5.036.938 3.329 2.22 5.121 5.121 6.06 8.45.085.427.17.768.256 1.195s.17.768.17 1.195c.257 5.719-1.536 6.401-2.73 9.815-1.878 4.353-1.366 9.048.938 12.803.17.427.427.94.768 1.451-2.475-16.473 11.267-18.948 13.827-24.07.171-4.523-3.499-7.51-6.401-9.558-2.987-1.708-5.548-2.22-7.852-2.22zm20.655 3.67c-.256 1.536-.086 1.11-.17 1.877-.086.512-.086 1.195-.172 1.707-.17.512-.256 1.024-.426 1.537l-.512 1.536c-.257.512-.427.939-.683 1.536l-.512.768c-.171.171-.256.427-.427.598-.342.427-.683.939-.939 1.28-.427.427-.683.854-1.195 1.195v.085c-.427.342-.768.683-1.195 1.025-1.366 1.024-2.902 1.792-4.353 2.816-.427.342-.939.598-1.28.94-.427.34-.854.682-1.195 1.023l-1.195 1.195c-.341.427-.683.854-.939 1.28-.341.427-.683.94-.939 1.366-.256.512-.426.94-.683 1.537-.17.512-.426.938-.512 1.536-.17.597-.341 1.11-.426 1.622-.086.256-.086.597-.171.853s-.085.512-.17.768c0 .512-.086 1.11-.086 1.622 0 .427 0 .768.085 1.195 0 .512.086 1.024.17 1.622.086.512.172 1.024.342 1.536l.512 1.536c.171.342.342.683.427.94l-14.936-5.805c-2.561-.683-5.036-1.365-7.511-1.963-1.366-.341-2.732-.683-4.097-.939a137 137 0 0 0-11.864-1.792c-.17 0-.17-.086-.342-.086-3.926-.426-7.767-.597-11.608-.597-2.901 0-5.718.17-8.535.341-3.926.256-7.937.769-11.864 1.451-.939.171-1.963.342-2.902.513-2.048.426-3.926.853-5.889 1.28-.939.256-1.963.512-2.902.768-.939.427-1.878.854-2.816 1.195a23 23 0 0 1-2.134.939c-.171.085-.256.085-.342.17a34 34 0 0 0-1.792.94c-.17.085-.341.17-.427.17-.683.341-1.45.683-2.048 1.024-.427.171-.94.427-1.28.683-.171.17-.427.256-.598.342-.597.34-1.195.682-1.707.938-.598.342-1.11.683-1.536.94-.427.34-.94.597-1.28.938-.086.085-.171.085-.171.17a6.5 6.5 0 0 0-1.195.94l-.171.17c-.341.256-.683.513-.939.769-.17.085-.256.17-.427.256-.341.256-.682.597-.939.853-.085.17-.17.17-.256.256-.426.427-.768.683-1.195 1.11-.085 0-.085.085-.17.17-.427.342-.768.683-1.195 1.11-.085.085-.085.17-.17.17-.342.342-.684.684-.94 1.025-.17.17-.341.256-.426.427-.342.427-.683.768-1.11 1.195-.085.17-.17.17-.256.341-.512.512-.939 1.024-1.536 1.536l-.171.171c-1.024 1.11-2.134 2.22-3.329 3.158-1.195 1.024-2.39 2.049-3.67 2.902-1.28.939-2.56 1.707-3.926 2.475-1.28.683-2.646 1.366-4.097 1.963a36 36 0 0 1-4.268 1.537c-2.731.597-5.548 1.707-7.937 1.878-.513 0-1.11.17-1.622.17-.598.17-1.11.256-1.622.427s-1.024.427-1.536.597-1.024.427-1.536.683c-.427.342-.94.598-1.451.94-.427.34-.94.682-1.28 1.109-.428.341-.94.768-1.281 1.195-.427.426-.768.853-1.11 1.28-.341.512-.682.939-.938 1.536-.342.427-.683.94-.94 1.537s-.511 1.11-.682 1.707c-.17.512-.427 1.11-.598 1.707-.17.512-.256 1.024-.341 1.536 0 .085-.085.17-.085.17-.171.598-.171 1.366-.171 1.793-.085.427-.17.854-.17 1.28 0 .256 0 .598.085.854.085.427.17.853.256 1.195.17.427.256.768.426 1.195v.085c.171.427.427.768.683 1.195s.512.768.854 1.195c.341.341.683.683 1.11 1.024.426.427.767.683 1.194 1.024 1.537 1.366 1.963 1.793 3.926 2.902l1.025.512c.085 0 .17.086.17.086 0 .17 0 .17.086.341.085.512.17 1.024.341 1.537q.256.896.512 1.536c.256.64.342.768.512 1.195.086.17.171.256.171.341.256.512.512.94.768 1.451.342.427.683.94.94 1.366.34.427.682.853 1.109 1.195.426.427.768.683 1.195 1.11 0 0 .085.085.17.085.427.341.768.683 1.195.939.427.341.94.597 1.451.853.427.256.94.512 1.537.683.426.17.853.341 1.28.427.085.085.17.085.256.17.256.086.597.171.853.171-.17 3.5-.256 6.828.256 8.023.598 1.28 3.415-2.646 6.316-7.255-.426 4.524-.682 9.73 0 11.352s4.61-3.414 8.024-9.047c46.09-10.67 88.168 21.167 92.606 66.233-.853-6.999-9.474-10.925-13.485-9.986-1.963 4.78-5.292 11.01-10.584 14.851.427-4.268.256-8.706-.683-12.974-1.45 5.975-4.267 11.608-8.023 16.388-6.145.427-12.376-2.56-15.62-6.999-.255-.17-.34-.597-.511-.853-.17-.427-.427-.94-.512-1.366-.171-.427-.342-.939-.427-1.366s-.085-.938-.085-1.45v-.94c.085-.426.17-.938.341-1.365s.256-.939.427-1.366c.256-.426.426-.939.768-1.365 1.11-3.158 1.11-5.634-.939-7.17-.427-.256-.768-.427-1.195-.683-.256-.085-.597-.17-.853-.256-.171-.085-.342-.17-.513-.17-.426-.171-.938-.257-1.365-.342-.427-.17-.939-.17-1.366-.17-.427-.086-.939-.171-1.45-.171-.342 0-.684.085-.94.085-.512 0-.939.085-1.45.17-.427.086-.94.171-1.366.257-.427.17-.94.256-1.366.427s-.853.426-1.28.597-.768.427-1.195.683c-15.193 9.9-6.145 33.031 4.267 39.689-3.926.682-7.852 1.536-8.961 2.39l-.171.17a56 56 0 0 0 8.791 4.353c4.182 1.366 8.62 2.56 10.498 3.158v.085c5.378 1.11 10.84 1.537 16.388 1.195 28.764-2.048 52.406-23.898 56.674-52.833.17.598.256 1.11.426 1.708.171 1.194.427 2.39.598 3.67v.085c.17.597.17 1.195.256 1.707v.256c.085.598.17 1.195.17 1.707.086.683.171 1.451.171 2.134v1.024c0 .342.086.683.086 1.024 0 .427-.086.769-.086 1.195v.94c0 .426-.085.853-.085 1.28 0 .256 0 .512-.085.853 0 .427-.086.939-.086 1.451-.085.17-.085.427-.085.597-.085.513-.17.94-.17 1.537 0 .17 0 .427-.086.597-.085.683-.17 1.195-.256 1.878v.17c-.17.598-.256 1.196-.427 1.793v.17c-.17.598-.256 1.196-.427 1.793 0 .086-.085.17-.085.256-.17.598-.256 1.195-.427 1.793v.17c-.17.683-.427 1.195-.512 1.793-.085.085-.085.17-.085.17-.17.683-.427 1.28-.598 1.964-.256.682-.426 1.194-.683 1.877s-.426 1.28-.682 1.878c-.256.683-.512 1.195-.768 1.878h-.086c-.256.597-.512 1.195-.853 1.792-.086.17-.171.342-.171.427-.085.085-.085.17-.17.17-4.268 8.536-10.5 15.961-18.266 21.85-.512.342-1.024.684-1.536 1.11-.171.171-.342.171-.427.342a8.5 8.5 0 0 1-1.451.939l.17.426h.086c.939-.17 1.792-.256 2.731-.426h.085c1.708-.256 3.415-.598 5.036-.94.427-.085.94-.17 1.451-.34.342-.086.598-.171.94-.171.426-.086.938-.171 1.365-.256.427-.171.768-.171 1.195-.342 6.486-1.536 12.802-3.67 18.862-6.23-10.327 14.082-24.154 25.52-40.371 32.945a109 109 0 0 0 22.191-3.84c26.204-7.768 48.224-25.35 61.454-49.078-2.646 15.022-8.62 29.361-17.497 41.823 6.316-4.183 12.12-8.962 17.326-14.425 14.595-15.193 24.155-34.568 27.398-55.308 2.22 10.242 2.902 20.911 1.878 31.324 46.944-65.465 3.926-133.405-14.083-151.244-.085-.17-.17-.17-.17-.341-.086.085-.086.085-.086.17 0-.085 0-.085-.086-.17 0 .768-.085 1.536-.17 2.304-.17 1.537-.427 2.902-.683 4.353-.341 1.451-.683 2.902-1.11 4.268s-.938 2.817-1.536 4.182c-.597 1.28-1.195 2.646-1.963 3.926a53 53 0 0 1-2.305 3.67c-.853 1.196-1.792 2.39-2.645 3.5-.94 1.195-2.049 2.22-3.073 3.243-.683.598-1.195 1.11-1.878 1.622-.512.427-.939.854-1.536 1.28-1.195.94-2.305 1.793-3.67 2.56-1.195.77-2.56 1.537-3.841 2.22-1.366.683-2.731 1.195-4.097 1.792-1.366.513-2.817.94-4.268 1.366s-2.902.683-4.353.939a42 42 0 0 1-4.438.512c-1.024.085-2.048.17-3.158.17-1.536 0-2.987-.17-4.438-.255-1.537-.171-2.988-.342-4.439-.683-1.536-.256-2.902-.683-4.353-1.11h-.085c1.451-.17 2.902-.256 4.268-.512 1.536-.256 2.902-.597 4.353-.939 1.45-.426 2.902-.853 4.267-1.365 1.451-.512 2.817-1.195 4.097-1.793 1.366-.682 2.56-1.365 3.926-2.133 1.195-.854 2.476-1.707 3.67-2.561 1.195-.939 2.305-1.878 3.33-2.902 1.109-.939 2.048-2.048 3.072-3.158.939-1.195 1.878-2.305 2.731-3.5.17-.17.256-.426.427-.682a39 39 0 0 0 1.878-3.158c.682-1.28 1.365-2.56 1.963-3.926s1.11-2.732 1.536-4.183c.427-1.365.768-2.816 1.11-4.267.256-1.537.512-2.902.682-4.353.171-1.537.257-2.988.257-4.439 0-1.024-.086-2.048-.171-3.158-.17-1.536-.342-2.902-.512-4.353-.256-1.536-.598-2.902-.94-4.352-.426-1.366-.938-2.817-1.45-4.183s-1.195-2.731-1.793-4.011c-.682-1.28-1.45-2.56-2.219-3.841l-2.56-3.585c-.94-1.11-1.963-2.22-2.988-3.329-.512-.512-1.11-1.11-1.707-1.621-2.902-2.305-5.974-4.439-9.047-6.402-.427-.256-.853-.426-1.28-.683-2.049-1.536-4.012-2.219-6.06-2.902z'/></svg>",
+ "nest-pipe.clone": "<svg viewBox='0 0 300 300'><path fill='#00897b' d='M172.382 24.41c-1.793 0-3.5.426-5.036.938 3.329 2.22 5.121 5.121 6.06 8.45.085.427.17.768.256 1.195s.17.768.17 1.195c.257 5.719-1.536 6.401-2.73 9.815-1.878 4.353-1.366 9.048.938 12.803.17.427.427.94.768 1.451-2.475-16.473 11.267-18.948 13.827-24.07.171-4.523-3.499-7.51-6.401-9.558-2.987-1.708-5.548-2.22-7.852-2.22zm20.655 3.67c-.256 1.536-.086 1.11-.17 1.877-.086.512-.086 1.195-.172 1.707-.17.512-.256 1.024-.426 1.537l-.512 1.536c-.257.512-.427.939-.683 1.536l-.512.768c-.171.171-.256.427-.427.598-.342.427-.683.939-.939 1.28-.427.427-.683.854-1.195 1.195v.085c-.427.342-.768.683-1.195 1.025-1.366 1.024-2.902 1.792-4.353 2.816-.427.342-.939.598-1.28.94-.427.34-.854.682-1.195 1.023l-1.195 1.195c-.341.427-.683.854-.939 1.28-.341.427-.683.94-.939 1.366-.256.512-.426.94-.683 1.537-.17.512-.426.938-.512 1.536-.17.597-.341 1.11-.426 1.622-.086.256-.086.597-.171.853s-.085.512-.17.768c0 .512-.086 1.11-.086 1.622 0 .427 0 .768.085 1.195 0 .512.086 1.024.17 1.622.086.512.172 1.024.342 1.536l.512 1.536c.171.342.342.683.427.94l-14.936-5.805c-2.561-.683-5.036-1.365-7.511-1.963-1.366-.341-2.732-.683-4.097-.939a137 137 0 0 0-11.864-1.792c-.17 0-.17-.086-.342-.086-3.926-.426-7.767-.597-11.608-.597-2.901 0-5.718.17-8.535.341-3.926.256-7.937.769-11.864 1.451-.939.171-1.963.342-2.902.513-2.048.426-3.926.853-5.889 1.28-.939.256-1.963.512-2.902.768-.939.427-1.878.854-2.816 1.195a23 23 0 0 1-2.134.939c-.171.085-.256.085-.342.17a34 34 0 0 0-1.792.94c-.17.085-.341.17-.427.17-.683.341-1.45.683-2.048 1.024-.427.171-.94.427-1.28.683-.171.17-.427.256-.598.342-.597.34-1.195.682-1.707.938-.598.342-1.11.683-1.536.94-.427.34-.94.597-1.28.938-.086.085-.171.085-.171.17a6.5 6.5 0 0 0-1.195.94l-.171.17c-.341.256-.683.513-.939.769-.17.085-.256.17-.427.256-.341.256-.682.597-.939.853-.085.17-.17.17-.256.256-.426.427-.768.683-1.195 1.11-.085 0-.085.085-.17.17-.427.342-.768.683-1.195 1.11-.085.085-.085.17-.17.17-.342.342-.684.684-.94 1.025-.17.17-.341.256-.426.427-.342.427-.683.768-1.11 1.195-.085.17-.17.17-.256.341-.512.512-.939 1.024-1.536 1.536l-.171.171c-1.024 1.11-2.134 2.22-3.329 3.158-1.195 1.024-2.39 2.049-3.67 2.902-1.28.939-2.56 1.707-3.926 2.475-1.28.683-2.646 1.366-4.097 1.963a36 36 0 0 1-4.268 1.537c-2.731.597-5.548 1.707-7.937 1.878-.513 0-1.11.17-1.622.17-.598.17-1.11.256-1.622.427s-1.024.427-1.536.597-1.024.427-1.536.683c-.427.342-.94.598-1.451.94-.427.34-.94.682-1.28 1.109-.428.341-.94.768-1.281 1.195-.427.426-.768.853-1.11 1.28-.341.512-.682.939-.938 1.536-.342.427-.683.94-.94 1.537s-.511 1.11-.682 1.707c-.17.512-.427 1.11-.598 1.707-.17.512-.256 1.024-.341 1.536 0 .085-.085.17-.085.17-.171.598-.171 1.366-.171 1.793-.085.427-.17.854-.17 1.28 0 .256 0 .598.085.854.085.427.17.853.256 1.195.17.427.256.768.426 1.195v.085c.171.427.427.768.683 1.195s.512.768.854 1.195c.341.341.683.683 1.11 1.024.426.427.767.683 1.194 1.024 1.537 1.366 1.963 1.793 3.926 2.902l1.025.512c.085 0 .17.086.17.086 0 .17 0 .17.086.341.085.512.17 1.024.341 1.537q.256.896.512 1.536c.256.64.342.768.512 1.195.086.17.171.256.171.341.256.512.512.94.768 1.451.342.427.683.94.94 1.366.34.427.682.853 1.109 1.195.426.427.768.683 1.195 1.11 0 0 .085.085.17.085.427.341.768.683 1.195.939.427.341.94.597 1.451.853.427.256.94.512 1.537.683.426.17.853.341 1.28.427.085.085.17.085.256.17.256.086.597.171.853.171-.17 3.5-.256 6.828.256 8.023.598 1.28 3.415-2.646 6.316-7.255-.426 4.524-.682 9.73 0 11.352s4.61-3.414 8.024-9.047c46.09-10.67 88.168 21.167 92.606 66.233-.853-6.999-9.474-10.925-13.485-9.986-1.963 4.78-5.292 11.01-10.584 14.851.427-4.268.256-8.706-.683-12.974-1.45 5.975-4.267 11.608-8.023 16.388-6.145.427-12.376-2.56-15.62-6.999-.255-.17-.34-.597-.511-.853-.17-.427-.427-.94-.512-1.366-.171-.427-.342-.939-.427-1.366s-.085-.938-.085-1.45v-.94c.085-.426.17-.938.341-1.365s.256-.939.427-1.366c.256-.426.426-.939.768-1.365 1.11-3.158 1.11-5.634-.939-7.17-.427-.256-.768-.427-1.195-.683-.256-.085-.597-.17-.853-.256-.171-.085-.342-.17-.513-.17-.426-.171-.938-.257-1.365-.342-.427-.17-.939-.17-1.366-.17-.427-.086-.939-.171-1.45-.171-.342 0-.684.085-.94.085-.512 0-.939.085-1.45.17-.427.086-.94.171-1.366.257-.427.17-.94.256-1.366.427s-.853.426-1.28.597-.768.427-1.195.683c-15.193 9.9-6.145 33.031 4.267 39.689-3.926.682-7.852 1.536-8.961 2.39l-.171.17a56 56 0 0 0 8.791 4.353c4.182 1.366 8.62 2.56 10.498 3.158v.085c5.378 1.11 10.84 1.537 16.388 1.195 28.764-2.048 52.406-23.898 56.674-52.833.17.598.256 1.11.426 1.708.171 1.194.427 2.39.598 3.67v.085c.17.597.17 1.195.256 1.707v.256c.085.598.17 1.195.17 1.707.086.683.171 1.451.171 2.134v1.024c0 .342.086.683.086 1.024 0 .427-.086.769-.086 1.195v.94c0 .426-.085.853-.085 1.28 0 .256 0 .512-.085.853 0 .427-.086.939-.086 1.451-.085.17-.085.427-.085.597-.085.513-.17.94-.17 1.537 0 .17 0 .427-.086.597-.085.683-.17 1.195-.256 1.878v.17c-.17.598-.256 1.196-.427 1.793v.17c-.17.598-.256 1.196-.427 1.793 0 .086-.085.17-.085.256-.17.598-.256 1.195-.427 1.793v.17c-.17.683-.427 1.195-.512 1.793-.085.085-.085.17-.085.17-.17.683-.427 1.28-.598 1.964-.256.682-.426 1.194-.683 1.877s-.426 1.28-.682 1.878c-.256.683-.512 1.195-.768 1.878h-.086c-.256.597-.512 1.195-.853 1.792-.086.17-.171.342-.171.427-.085.085-.085.17-.17.17-4.268 8.536-10.5 15.961-18.266 21.85-.512.342-1.024.684-1.536 1.11-.171.171-.342.171-.427.342a8.5 8.5 0 0 1-1.451.939l.17.426h.086c.939-.17 1.792-.256 2.731-.426h.085c1.708-.256 3.415-.598 5.036-.94.427-.085.94-.17 1.451-.34.342-.086.598-.171.94-.171.426-.086.938-.171 1.365-.256.427-.171.768-.171 1.195-.342 6.486-1.536 12.802-3.67 18.862-6.23-10.327 14.082-24.154 25.52-40.371 32.945a109 109 0 0 0 22.191-3.84c26.204-7.768 48.224-25.35 61.454-49.078-2.646 15.022-8.62 29.361-17.497 41.823 6.316-4.183 12.12-8.962 17.326-14.425 14.595-15.193 24.155-34.568 27.398-55.308 2.22 10.242 2.902 20.911 1.878 31.324 46.944-65.465 3.926-133.405-14.083-151.244-.085-.17-.17-.17-.17-.341-.086.085-.086.085-.086.17 0-.085 0-.085-.086-.17 0 .768-.085 1.536-.17 2.304-.17 1.537-.427 2.902-.683 4.353-.341 1.451-.683 2.902-1.11 4.268s-.938 2.817-1.536 4.182c-.597 1.28-1.195 2.646-1.963 3.926a53 53 0 0 1-2.305 3.67c-.853 1.196-1.792 2.39-2.645 3.5-.94 1.195-2.049 2.22-3.073 3.243-.683.598-1.195 1.11-1.878 1.622-.512.427-.939.854-1.536 1.28-1.195.94-2.305 1.793-3.67 2.56-1.195.77-2.56 1.537-3.841 2.22-1.366.683-2.731 1.195-4.097 1.792-1.366.513-2.817.94-4.268 1.366s-2.902.683-4.353.939a42 42 0 0 1-4.438.512c-1.024.085-2.048.17-3.158.17-1.536 0-2.987-.17-4.438-.255-1.537-.171-2.988-.342-4.439-.683-1.536-.256-2.902-.683-4.353-1.11h-.085c1.451-.17 2.902-.256 4.268-.512 1.536-.256 2.902-.597 4.353-.939 1.45-.426 2.902-.853 4.267-1.365 1.451-.512 2.817-1.195 4.097-1.793 1.366-.682 2.56-1.365 3.926-2.133 1.195-.854 2.476-1.707 3.67-2.561 1.195-.939 2.305-1.878 3.33-2.902 1.109-.939 2.048-2.048 3.072-3.158.939-1.195 1.878-2.305 2.731-3.5.17-.17.256-.426.427-.682a39 39 0 0 0 1.878-3.158c.682-1.28 1.365-2.56 1.963-3.926s1.11-2.732 1.536-4.183c.427-1.365.768-2.816 1.11-4.267.256-1.537.512-2.902.682-4.353.171-1.537.257-2.988.257-4.439 0-1.024-.086-2.048-.171-3.158-.17-1.536-.342-2.902-.512-4.353-.256-1.536-.598-2.902-.94-4.352-.426-1.366-.938-2.817-1.45-4.183s-1.195-2.731-1.793-4.011c-.682-1.28-1.45-2.56-2.219-3.841l-2.56-3.585c-.94-1.11-1.963-2.22-2.988-3.329-.512-.512-1.11-1.11-1.707-1.621-2.902-2.305-5.974-4.439-9.047-6.402-.427-.256-.853-.426-1.28-.683-2.049-1.536-4.012-2.219-6.06-2.902z'/></svg>",
+ "nest-resolver.clone": "<svg viewBox='0 0 300 300'><path fill='#ec407a' d='M172.382 24.41c-1.793 0-3.5.426-5.036.938 3.329 2.22 5.121 5.121 6.06 8.45.085.427.17.768.256 1.195s.17.768.17 1.195c.257 5.719-1.536 6.401-2.73 9.815-1.878 4.353-1.366 9.048.938 12.803.17.427.427.94.768 1.451-2.475-16.473 11.267-18.948 13.827-24.07.171-4.523-3.499-7.51-6.401-9.558-2.987-1.708-5.548-2.22-7.852-2.22zm20.655 3.67c-.256 1.536-.086 1.11-.17 1.877-.086.512-.086 1.195-.172 1.707-.17.512-.256 1.024-.426 1.537l-.512 1.536c-.257.512-.427.939-.683 1.536l-.512.768c-.171.171-.256.427-.427.598-.342.427-.683.939-.939 1.28-.427.427-.683.854-1.195 1.195v.085c-.427.342-.768.683-1.195 1.025-1.366 1.024-2.902 1.792-4.353 2.816-.427.342-.939.598-1.28.94-.427.34-.854.682-1.195 1.023l-1.195 1.195c-.341.427-.683.854-.939 1.28-.341.427-.683.94-.939 1.366-.256.512-.426.94-.683 1.537-.17.512-.426.938-.512 1.536-.17.597-.341 1.11-.426 1.622-.086.256-.086.597-.171.853s-.085.512-.17.768c0 .512-.086 1.11-.086 1.622 0 .427 0 .768.085 1.195 0 .512.086 1.024.17 1.622.086.512.172 1.024.342 1.536l.512 1.536c.171.342.342.683.427.94l-14.936-5.805c-2.561-.683-5.036-1.365-7.511-1.963-1.366-.341-2.732-.683-4.097-.939a137 137 0 0 0-11.864-1.792c-.17 0-.17-.086-.342-.086-3.926-.426-7.767-.597-11.608-.597-2.901 0-5.718.17-8.535.341-3.926.256-7.937.769-11.864 1.451-.939.171-1.963.342-2.902.513-2.048.426-3.926.853-5.889 1.28-.939.256-1.963.512-2.902.768-.939.427-1.878.854-2.816 1.195a23 23 0 0 1-2.134.939c-.171.085-.256.085-.342.17a34 34 0 0 0-1.792.94c-.17.085-.341.17-.427.17-.683.341-1.45.683-2.048 1.024-.427.171-.94.427-1.28.683-.171.17-.427.256-.598.342-.597.34-1.195.682-1.707.938-.598.342-1.11.683-1.536.94-.427.34-.94.597-1.28.938-.086.085-.171.085-.171.17a6.5 6.5 0 0 0-1.195.94l-.171.17c-.341.256-.683.513-.939.769-.17.085-.256.17-.427.256-.341.256-.682.597-.939.853-.085.17-.17.17-.256.256-.426.427-.768.683-1.195 1.11-.085 0-.085.085-.17.17-.427.342-.768.683-1.195 1.11-.085.085-.085.17-.17.17-.342.342-.684.684-.94 1.025-.17.17-.341.256-.426.427-.342.427-.683.768-1.11 1.195-.085.17-.17.17-.256.341-.512.512-.939 1.024-1.536 1.536l-.171.171c-1.024 1.11-2.134 2.22-3.329 3.158-1.195 1.024-2.39 2.049-3.67 2.902-1.28.939-2.56 1.707-3.926 2.475-1.28.683-2.646 1.366-4.097 1.963a36 36 0 0 1-4.268 1.537c-2.731.597-5.548 1.707-7.937 1.878-.513 0-1.11.17-1.622.17-.598.17-1.11.256-1.622.427s-1.024.427-1.536.597-1.024.427-1.536.683c-.427.342-.94.598-1.451.94-.427.34-.94.682-1.28 1.109-.428.341-.94.768-1.281 1.195-.427.426-.768.853-1.11 1.28-.341.512-.682.939-.938 1.536-.342.427-.683.94-.94 1.537s-.511 1.11-.682 1.707c-.17.512-.427 1.11-.598 1.707-.17.512-.256 1.024-.341 1.536 0 .085-.085.17-.085.17-.171.598-.171 1.366-.171 1.793-.085.427-.17.854-.17 1.28 0 .256 0 .598.085.854.085.427.17.853.256 1.195.17.427.256.768.426 1.195v.085c.171.427.427.768.683 1.195s.512.768.854 1.195c.341.341.683.683 1.11 1.024.426.427.767.683 1.194 1.024 1.537 1.366 1.963 1.793 3.926 2.902l1.025.512c.085 0 .17.086.17.086 0 .17 0 .17.086.341.085.512.17 1.024.341 1.537q.256.896.512 1.536c.256.64.342.768.512 1.195.086.17.171.256.171.341.256.512.512.94.768 1.451.342.427.683.94.94 1.366.34.427.682.853 1.109 1.195.426.427.768.683 1.195 1.11 0 0 .085.085.17.085.427.341.768.683 1.195.939.427.341.94.597 1.451.853.427.256.94.512 1.537.683.426.17.853.341 1.28.427.085.085.17.085.256.17.256.086.597.171.853.171-.17 3.5-.256 6.828.256 8.023.598 1.28 3.415-2.646 6.316-7.255-.426 4.524-.682 9.73 0 11.352s4.61-3.414 8.024-9.047c46.09-10.67 88.168 21.167 92.606 66.233-.853-6.999-9.474-10.925-13.485-9.986-1.963 4.78-5.292 11.01-10.584 14.851.427-4.268.256-8.706-.683-12.974-1.45 5.975-4.267 11.608-8.023 16.388-6.145.427-12.376-2.56-15.62-6.999-.255-.17-.34-.597-.511-.853-.17-.427-.427-.94-.512-1.366-.171-.427-.342-.939-.427-1.366s-.085-.938-.085-1.45v-.94c.085-.426.17-.938.341-1.365s.256-.939.427-1.366c.256-.426.426-.939.768-1.365 1.11-3.158 1.11-5.634-.939-7.17-.427-.256-.768-.427-1.195-.683-.256-.085-.597-.17-.853-.256-.171-.085-.342-.17-.513-.17-.426-.171-.938-.257-1.365-.342-.427-.17-.939-.17-1.366-.17-.427-.086-.939-.171-1.45-.171-.342 0-.684.085-.94.085-.512 0-.939.085-1.45.17-.427.086-.94.171-1.366.257-.427.17-.94.256-1.366.427s-.853.426-1.28.597-.768.427-1.195.683c-15.193 9.9-6.145 33.031 4.267 39.689-3.926.682-7.852 1.536-8.961 2.39l-.171.17a56 56 0 0 0 8.791 4.353c4.182 1.366 8.62 2.56 10.498 3.158v.085c5.378 1.11 10.84 1.537 16.388 1.195 28.764-2.048 52.406-23.898 56.674-52.833.17.598.256 1.11.426 1.708.171 1.194.427 2.39.598 3.67v.085c.17.597.17 1.195.256 1.707v.256c.085.598.17 1.195.17 1.707.086.683.171 1.451.171 2.134v1.024c0 .342.086.683.086 1.024 0 .427-.086.769-.086 1.195v.94c0 .426-.085.853-.085 1.28 0 .256 0 .512-.085.853 0 .427-.086.939-.086 1.451-.085.17-.085.427-.085.597-.085.513-.17.94-.17 1.537 0 .17 0 .427-.086.597-.085.683-.17 1.195-.256 1.878v.17c-.17.598-.256 1.196-.427 1.793v.17c-.17.598-.256 1.196-.427 1.793 0 .086-.085.17-.085.256-.17.598-.256 1.195-.427 1.793v.17c-.17.683-.427 1.195-.512 1.793-.085.085-.085.17-.085.17-.17.683-.427 1.28-.598 1.964-.256.682-.426 1.194-.683 1.877s-.426 1.28-.682 1.878c-.256.683-.512 1.195-.768 1.878h-.086c-.256.597-.512 1.195-.853 1.792-.086.17-.171.342-.171.427-.085.085-.085.17-.17.17-4.268 8.536-10.5 15.961-18.266 21.85-.512.342-1.024.684-1.536 1.11-.171.171-.342.171-.427.342a8.5 8.5 0 0 1-1.451.939l.17.426h.086c.939-.17 1.792-.256 2.731-.426h.085c1.708-.256 3.415-.598 5.036-.94.427-.085.94-.17 1.451-.34.342-.086.598-.171.94-.171.426-.086.938-.171 1.365-.256.427-.171.768-.171 1.195-.342 6.486-1.536 12.802-3.67 18.862-6.23-10.327 14.082-24.154 25.52-40.371 32.945a109 109 0 0 0 22.191-3.84c26.204-7.768 48.224-25.35 61.454-49.078-2.646 15.022-8.62 29.361-17.497 41.823 6.316-4.183 12.12-8.962 17.326-14.425 14.595-15.193 24.155-34.568 27.398-55.308 2.22 10.242 2.902 20.911 1.878 31.324 46.944-65.465 3.926-133.405-14.083-151.244-.085-.17-.17-.17-.17-.341-.086.085-.086.085-.086.17 0-.085 0-.085-.086-.17 0 .768-.085 1.536-.17 2.304-.17 1.537-.427 2.902-.683 4.353-.341 1.451-.683 2.902-1.11 4.268s-.938 2.817-1.536 4.182c-.597 1.28-1.195 2.646-1.963 3.926a53 53 0 0 1-2.305 3.67c-.853 1.196-1.792 2.39-2.645 3.5-.94 1.195-2.049 2.22-3.073 3.243-.683.598-1.195 1.11-1.878 1.622-.512.427-.939.854-1.536 1.28-1.195.94-2.305 1.793-3.67 2.56-1.195.77-2.56 1.537-3.841 2.22-1.366.683-2.731 1.195-4.097 1.792-1.366.513-2.817.94-4.268 1.366s-2.902.683-4.353.939a42 42 0 0 1-4.438.512c-1.024.085-2.048.17-3.158.17-1.536 0-2.987-.17-4.438-.255-1.537-.171-2.988-.342-4.439-.683-1.536-.256-2.902-.683-4.353-1.11h-.085c1.451-.17 2.902-.256 4.268-.512 1.536-.256 2.902-.597 4.353-.939 1.45-.426 2.902-.853 4.267-1.365 1.451-.512 2.817-1.195 4.097-1.793 1.366-.682 2.56-1.365 3.926-2.133 1.195-.854 2.476-1.707 3.67-2.561 1.195-.939 2.305-1.878 3.33-2.902 1.109-.939 2.048-2.048 3.072-3.158.939-1.195 1.878-2.305 2.731-3.5.17-.17.256-.426.427-.682a39 39 0 0 0 1.878-3.158c.682-1.28 1.365-2.56 1.963-3.926s1.11-2.732 1.536-4.183c.427-1.365.768-2.816 1.11-4.267.256-1.537.512-2.902.682-4.353.171-1.537.257-2.988.257-4.439 0-1.024-.086-2.048-.171-3.158-.17-1.536-.342-2.902-.512-4.353-.256-1.536-.598-2.902-.94-4.352-.426-1.366-.938-2.817-1.45-4.183s-1.195-2.731-1.793-4.011c-.682-1.28-1.45-2.56-2.219-3.841l-2.56-3.585c-.94-1.11-1.963-2.22-2.988-3.329-.512-.512-1.11-1.11-1.707-1.621-2.902-2.305-5.974-4.439-9.047-6.402-.427-.256-.853-.426-1.28-.683-2.049-1.536-4.012-2.219-6.06-2.902z'/></svg>",
+ "nest-service.clone": "<svg viewBox='0 0 300 300'><path fill='#ffca28' d='M172.382 24.41c-1.793 0-3.5.426-5.036.938 3.329 2.22 5.121 5.121 6.06 8.45.085.427.17.768.256 1.195s.17.768.17 1.195c.257 5.719-1.536 6.401-2.73 9.815-1.878 4.353-1.366 9.048.938 12.803.17.427.427.94.768 1.451-2.475-16.473 11.267-18.948 13.827-24.07.171-4.523-3.499-7.51-6.401-9.558-2.987-1.708-5.548-2.22-7.852-2.22zm20.655 3.67c-.256 1.536-.086 1.11-.17 1.877-.086.512-.086 1.195-.172 1.707-.17.512-.256 1.024-.426 1.537l-.512 1.536c-.257.512-.427.939-.683 1.536l-.512.768c-.171.171-.256.427-.427.598-.342.427-.683.939-.939 1.28-.427.427-.683.854-1.195 1.195v.085c-.427.342-.768.683-1.195 1.025-1.366 1.024-2.902 1.792-4.353 2.816-.427.342-.939.598-1.28.94-.427.34-.854.682-1.195 1.023l-1.195 1.195c-.341.427-.683.854-.939 1.28-.341.427-.683.94-.939 1.366-.256.512-.426.94-.683 1.537-.17.512-.426.938-.512 1.536-.17.597-.341 1.11-.426 1.622-.086.256-.086.597-.171.853s-.085.512-.17.768c0 .512-.086 1.11-.086 1.622 0 .427 0 .768.085 1.195 0 .512.086 1.024.17 1.622.086.512.172 1.024.342 1.536l.512 1.536c.171.342.342.683.427.94l-14.936-5.805c-2.561-.683-5.036-1.365-7.511-1.963-1.366-.341-2.732-.683-4.097-.939a137 137 0 0 0-11.864-1.792c-.17 0-.17-.086-.342-.086-3.926-.426-7.767-.597-11.608-.597-2.901 0-5.718.17-8.535.341-3.926.256-7.937.769-11.864 1.451-.939.171-1.963.342-2.902.513-2.048.426-3.926.853-5.889 1.28-.939.256-1.963.512-2.902.768-.939.427-1.878.854-2.816 1.195a23 23 0 0 1-2.134.939c-.171.085-.256.085-.342.17a34 34 0 0 0-1.792.94c-.17.085-.341.17-.427.17-.683.341-1.45.683-2.048 1.024-.427.171-.94.427-1.28.683-.171.17-.427.256-.598.342-.597.34-1.195.682-1.707.938-.598.342-1.11.683-1.536.94-.427.34-.94.597-1.28.938-.086.085-.171.085-.171.17a6.5 6.5 0 0 0-1.195.94l-.171.17c-.341.256-.683.513-.939.769-.17.085-.256.17-.427.256-.341.256-.682.597-.939.853-.085.17-.17.17-.256.256-.426.427-.768.683-1.195 1.11-.085 0-.085.085-.17.17-.427.342-.768.683-1.195 1.11-.085.085-.085.17-.17.17-.342.342-.684.684-.94 1.025-.17.17-.341.256-.426.427-.342.427-.683.768-1.11 1.195-.085.17-.17.17-.256.341-.512.512-.939 1.024-1.536 1.536l-.171.171c-1.024 1.11-2.134 2.22-3.329 3.158-1.195 1.024-2.39 2.049-3.67 2.902-1.28.939-2.56 1.707-3.926 2.475-1.28.683-2.646 1.366-4.097 1.963a36 36 0 0 1-4.268 1.537c-2.731.597-5.548 1.707-7.937 1.878-.513 0-1.11.17-1.622.17-.598.17-1.11.256-1.622.427s-1.024.427-1.536.597-1.024.427-1.536.683c-.427.342-.94.598-1.451.94-.427.34-.94.682-1.28 1.109-.428.341-.94.768-1.281 1.195-.427.426-.768.853-1.11 1.28-.341.512-.682.939-.938 1.536-.342.427-.683.94-.94 1.537s-.511 1.11-.682 1.707c-.17.512-.427 1.11-.598 1.707-.17.512-.256 1.024-.341 1.536 0 .085-.085.17-.085.17-.171.598-.171 1.366-.171 1.793-.085.427-.17.854-.17 1.28 0 .256 0 .598.085.854.085.427.17.853.256 1.195.17.427.256.768.426 1.195v.085c.171.427.427.768.683 1.195s.512.768.854 1.195c.341.341.683.683 1.11 1.024.426.427.767.683 1.194 1.024 1.537 1.366 1.963 1.793 3.926 2.902l1.025.512c.085 0 .17.086.17.086 0 .17 0 .17.086.341.085.512.17 1.024.341 1.537q.256.896.512 1.536c.256.64.342.768.512 1.195.086.17.171.256.171.341.256.512.512.94.768 1.451.342.427.683.94.94 1.366.34.427.682.853 1.109 1.195.426.427.768.683 1.195 1.11 0 0 .085.085.17.085.427.341.768.683 1.195.939.427.341.94.597 1.451.853.427.256.94.512 1.537.683.426.17.853.341 1.28.427.085.085.17.085.256.17.256.086.597.171.853.171-.17 3.5-.256 6.828.256 8.023.598 1.28 3.415-2.646 6.316-7.255-.426 4.524-.682 9.73 0 11.352s4.61-3.414 8.024-9.047c46.09-10.67 88.168 21.167 92.606 66.233-.853-6.999-9.474-10.925-13.485-9.986-1.963 4.78-5.292 11.01-10.584 14.851.427-4.268.256-8.706-.683-12.974-1.45 5.975-4.267 11.608-8.023 16.388-6.145.427-12.376-2.56-15.62-6.999-.255-.17-.34-.597-.511-.853-.17-.427-.427-.94-.512-1.366-.171-.427-.342-.939-.427-1.366s-.085-.938-.085-1.45v-.94c.085-.426.17-.938.341-1.365s.256-.939.427-1.366c.256-.426.426-.939.768-1.365 1.11-3.158 1.11-5.634-.939-7.17-.427-.256-.768-.427-1.195-.683-.256-.085-.597-.17-.853-.256-.171-.085-.342-.17-.513-.17-.426-.171-.938-.257-1.365-.342-.427-.17-.939-.17-1.366-.17-.427-.086-.939-.171-1.45-.171-.342 0-.684.085-.94.085-.512 0-.939.085-1.45.17-.427.086-.94.171-1.366.257-.427.17-.94.256-1.366.427s-.853.426-1.28.597-.768.427-1.195.683c-15.193 9.9-6.145 33.031 4.267 39.689-3.926.682-7.852 1.536-8.961 2.39l-.171.17a56 56 0 0 0 8.791 4.353c4.182 1.366 8.62 2.56 10.498 3.158v.085c5.378 1.11 10.84 1.537 16.388 1.195 28.764-2.048 52.406-23.898 56.674-52.833.17.598.256 1.11.426 1.708.171 1.194.427 2.39.598 3.67v.085c.17.597.17 1.195.256 1.707v.256c.085.598.17 1.195.17 1.707.086.683.171 1.451.171 2.134v1.024c0 .342.086.683.086 1.024 0 .427-.086.769-.086 1.195v.94c0 .426-.085.853-.085 1.28 0 .256 0 .512-.085.853 0 .427-.086.939-.086 1.451-.085.17-.085.427-.085.597-.085.513-.17.94-.17 1.537 0 .17 0 .427-.086.597-.085.683-.17 1.195-.256 1.878v.17c-.17.598-.256 1.196-.427 1.793v.17c-.17.598-.256 1.196-.427 1.793 0 .086-.085.17-.085.256-.17.598-.256 1.195-.427 1.793v.17c-.17.683-.427 1.195-.512 1.793-.085.085-.085.17-.085.17-.17.683-.427 1.28-.598 1.964-.256.682-.426 1.194-.683 1.877s-.426 1.28-.682 1.878c-.256.683-.512 1.195-.768 1.878h-.086c-.256.597-.512 1.195-.853 1.792-.086.17-.171.342-.171.427-.085.085-.085.17-.17.17-4.268 8.536-10.5 15.961-18.266 21.85-.512.342-1.024.684-1.536 1.11-.171.171-.342.171-.427.342a8.5 8.5 0 0 1-1.451.939l.17.426h.086c.939-.17 1.792-.256 2.731-.426h.085c1.708-.256 3.415-.598 5.036-.94.427-.085.94-.17 1.451-.34.342-.086.598-.171.94-.171.426-.086.938-.171 1.365-.256.427-.171.768-.171 1.195-.342 6.486-1.536 12.802-3.67 18.862-6.23-10.327 14.082-24.154 25.52-40.371 32.945a109 109 0 0 0 22.191-3.84c26.204-7.768 48.224-25.35 61.454-49.078-2.646 15.022-8.62 29.361-17.497 41.823 6.316-4.183 12.12-8.962 17.326-14.425 14.595-15.193 24.155-34.568 27.398-55.308 2.22 10.242 2.902 20.911 1.878 31.324 46.944-65.465 3.926-133.405-14.083-151.244-.085-.17-.17-.17-.17-.341-.086.085-.086.085-.086.17 0-.085 0-.085-.086-.17 0 .768-.085 1.536-.17 2.304-.17 1.537-.427 2.902-.683 4.353-.341 1.451-.683 2.902-1.11 4.268s-.938 2.817-1.536 4.182c-.597 1.28-1.195 2.646-1.963 3.926a53 53 0 0 1-2.305 3.67c-.853 1.196-1.792 2.39-2.645 3.5-.94 1.195-2.049 2.22-3.073 3.243-.683.598-1.195 1.11-1.878 1.622-.512.427-.939.854-1.536 1.28-1.195.94-2.305 1.793-3.67 2.56-1.195.77-2.56 1.537-3.841 2.22-1.366.683-2.731 1.195-4.097 1.792-1.366.513-2.817.94-4.268 1.366s-2.902.683-4.353.939a42 42 0 0 1-4.438.512c-1.024.085-2.048.17-3.158.17-1.536 0-2.987-.17-4.438-.255-1.537-.171-2.988-.342-4.439-.683-1.536-.256-2.902-.683-4.353-1.11h-.085c1.451-.17 2.902-.256 4.268-.512 1.536-.256 2.902-.597 4.353-.939 1.45-.426 2.902-.853 4.267-1.365 1.451-.512 2.817-1.195 4.097-1.793 1.366-.682 2.56-1.365 3.926-2.133 1.195-.854 2.476-1.707 3.67-2.561 1.195-.939 2.305-1.878 3.33-2.902 1.109-.939 2.048-2.048 3.072-3.158.939-1.195 1.878-2.305 2.731-3.5.17-.17.256-.426.427-.682a39 39 0 0 0 1.878-3.158c.682-1.28 1.365-2.56 1.963-3.926s1.11-2.732 1.536-4.183c.427-1.365.768-2.816 1.11-4.267.256-1.537.512-2.902.682-4.353.171-1.537.257-2.988.257-4.439 0-1.024-.086-2.048-.171-3.158-.17-1.536-.342-2.902-.512-4.353-.256-1.536-.598-2.902-.94-4.352-.426-1.366-.938-2.817-1.45-4.183s-1.195-2.731-1.793-4.011c-.682-1.28-1.45-2.56-2.219-3.841l-2.56-3.585c-.94-1.11-1.963-2.22-2.988-3.329-.512-.512-1.11-1.11-1.707-1.621-2.902-2.305-5.974-4.439-9.047-6.402-.427-.256-.853-.426-1.28-.683-2.049-1.536-4.012-2.219-6.06-2.902z'/></svg>",
+ "nest": "<svg viewBox='0 0 300 300'><path fill='#ff1744' d='M172.382 24.41c-1.793 0-3.5.426-5.036.938 3.329 2.22 5.121 5.121 6.06 8.45.085.427.17.768.256 1.195s.17.768.17 1.195c.257 5.719-1.536 6.401-2.73 9.815-1.878 4.353-1.366 9.048.938 12.803.17.427.427.94.768 1.451-2.475-16.473 11.267-18.948 13.827-24.07.171-4.523-3.499-7.51-6.401-9.558-2.987-1.708-5.548-2.22-7.852-2.22zm20.655 3.67c-.256 1.536-.086 1.11-.17 1.877-.086.512-.086 1.195-.172 1.707-.17.512-.256 1.024-.426 1.537l-.512 1.536c-.257.512-.427.939-.683 1.536l-.512.768c-.171.171-.256.427-.427.598-.342.427-.683.939-.939 1.28-.427.427-.683.854-1.195 1.195v.085c-.427.342-.768.683-1.195 1.025-1.366 1.024-2.902 1.792-4.353 2.816-.427.342-.939.598-1.28.94-.427.34-.854.682-1.195 1.023l-1.195 1.195c-.341.427-.683.854-.939 1.28-.341.427-.683.94-.939 1.366-.256.512-.426.94-.683 1.537-.17.512-.426.938-.512 1.536-.17.597-.341 1.11-.426 1.622-.086.256-.086.597-.171.853s-.085.512-.17.768c0 .512-.086 1.11-.086 1.622 0 .427 0 .768.085 1.195 0 .512.086 1.024.17 1.622.086.512.172 1.024.342 1.536l.512 1.536c.171.342.342.683.427.94l-14.936-5.805c-2.561-.683-5.036-1.365-7.511-1.963-1.366-.341-2.732-.683-4.097-.939a137 137 0 0 0-11.864-1.792c-.17 0-.17-.086-.342-.086-3.926-.426-7.767-.597-11.608-.597-2.901 0-5.718.17-8.535.341-3.926.256-7.937.769-11.864 1.451-.939.171-1.963.342-2.902.513-2.048.426-3.926.853-5.889 1.28-.939.256-1.963.512-2.902.768-.939.427-1.878.854-2.816 1.195a23 23 0 0 1-2.134.939c-.171.085-.256.085-.342.17a34 34 0 0 0-1.792.94c-.17.085-.341.17-.427.17-.683.341-1.45.683-2.048 1.024-.427.171-.94.427-1.28.683-.171.17-.427.256-.598.342-.597.34-1.195.682-1.707.938-.598.342-1.11.683-1.536.94-.427.34-.94.597-1.28.938-.086.085-.171.085-.171.17a6.5 6.5 0 0 0-1.195.94l-.171.17c-.341.256-.683.513-.939.769-.17.085-.256.17-.427.256-.341.256-.682.597-.939.853-.085.17-.17.17-.256.256-.426.427-.768.683-1.195 1.11-.085 0-.085.085-.17.17-.427.342-.768.683-1.195 1.11-.085.085-.085.17-.17.17-.342.342-.684.684-.94 1.025-.17.17-.341.256-.426.427-.342.427-.683.768-1.11 1.195-.085.17-.17.17-.256.341-.512.512-.939 1.024-1.536 1.536l-.171.171c-1.024 1.11-2.134 2.22-3.329 3.158-1.195 1.024-2.39 2.049-3.67 2.902-1.28.939-2.56 1.707-3.926 2.475-1.28.683-2.646 1.366-4.097 1.963a36 36 0 0 1-4.268 1.537c-2.731.597-5.548 1.707-7.937 1.878-.513 0-1.11.17-1.622.17-.598.17-1.11.256-1.622.427s-1.024.427-1.536.597-1.024.427-1.536.683c-.427.342-.94.598-1.451.94-.427.34-.94.682-1.28 1.109-.428.341-.94.768-1.281 1.195-.427.426-.768.853-1.11 1.28-.341.512-.682.939-.938 1.536-.342.427-.683.94-.94 1.537s-.511 1.11-.682 1.707c-.17.512-.427 1.11-.598 1.707-.17.512-.256 1.024-.341 1.536 0 .085-.085.17-.085.17-.171.598-.171 1.366-.171 1.793-.085.427-.17.854-.17 1.28 0 .256 0 .598.085.854.085.427.17.853.256 1.195.17.427.256.768.426 1.195v.085c.171.427.427.768.683 1.195s.512.768.854 1.195c.341.341.683.683 1.11 1.024.426.427.767.683 1.194 1.024 1.537 1.366 1.963 1.793 3.926 2.902l1.025.512c.085 0 .17.086.17.086 0 .17 0 .17.086.341.085.512.17 1.024.341 1.537q.256.896.512 1.536c.256.64.342.768.512 1.195.086.17.171.256.171.341.256.512.512.94.768 1.451.342.427.683.94.94 1.366.34.427.682.853 1.109 1.195.426.427.768.683 1.195 1.11 0 0 .085.085.17.085.427.341.768.683 1.195.939.427.341.94.597 1.451.853.427.256.94.512 1.537.683.426.17.853.341 1.28.427.085.085.17.085.256.17.256.086.597.171.853.171-.17 3.5-.256 6.828.256 8.023.598 1.28 3.415-2.646 6.316-7.255-.426 4.524-.682 9.73 0 11.352s4.61-3.414 8.024-9.047c46.09-10.67 88.168 21.167 92.606 66.233-.853-6.999-9.474-10.925-13.485-9.986-1.963 4.78-5.292 11.01-10.584 14.851.427-4.268.256-8.706-.683-12.974-1.45 5.975-4.267 11.608-8.023 16.388-6.145.427-12.376-2.56-15.62-6.999-.255-.17-.34-.597-.511-.853-.17-.427-.427-.94-.512-1.366-.171-.427-.342-.939-.427-1.366s-.085-.938-.085-1.45v-.94c.085-.426.17-.938.341-1.365s.256-.939.427-1.366c.256-.426.426-.939.768-1.365 1.11-3.158 1.11-5.634-.939-7.17-.427-.256-.768-.427-1.195-.683-.256-.085-.597-.17-.853-.256-.171-.085-.342-.17-.513-.17-.426-.171-.938-.257-1.365-.342-.427-.17-.939-.17-1.366-.17-.427-.086-.939-.171-1.45-.171-.342 0-.684.085-.94.085-.512 0-.939.085-1.45.17-.427.086-.94.171-1.366.257-.427.17-.94.256-1.366.427s-.853.426-1.28.597-.768.427-1.195.683c-15.193 9.9-6.145 33.031 4.267 39.689-3.926.682-7.852 1.536-8.961 2.39l-.171.17a56 56 0 0 0 8.791 4.353c4.182 1.366 8.62 2.56 10.498 3.158v.085c5.378 1.11 10.84 1.537 16.388 1.195 28.764-2.048 52.406-23.898 56.674-52.833.17.598.256 1.11.426 1.708.171 1.194.427 2.39.598 3.67v.085c.17.597.17 1.195.256 1.707v.256c.085.598.17 1.195.17 1.707.086.683.171 1.451.171 2.134v1.024c0 .342.086.683.086 1.024 0 .427-.086.769-.086 1.195v.94c0 .426-.085.853-.085 1.28 0 .256 0 .512-.085.853 0 .427-.086.939-.086 1.451-.085.17-.085.427-.085.597-.085.513-.17.94-.17 1.537 0 .17 0 .427-.086.597-.085.683-.17 1.195-.256 1.878v.17c-.17.598-.256 1.196-.427 1.793v.17c-.17.598-.256 1.196-.427 1.793 0 .086-.085.17-.085.256-.17.598-.256 1.195-.427 1.793v.17c-.17.683-.427 1.195-.512 1.793-.085.085-.085.17-.085.17-.17.683-.427 1.28-.598 1.964-.256.682-.426 1.194-.683 1.877s-.426 1.28-.682 1.878c-.256.683-.512 1.195-.768 1.878h-.086c-.256.597-.512 1.195-.853 1.792-.086.17-.171.342-.171.427-.085.085-.085.17-.17.17-4.268 8.536-10.5 15.961-18.266 21.85-.512.342-1.024.684-1.536 1.11-.171.171-.342.171-.427.342a8.5 8.5 0 0 1-1.451.939l.17.426h.086c.939-.17 1.792-.256 2.731-.426h.085c1.708-.256 3.415-.598 5.036-.94.427-.085.94-.17 1.451-.34.342-.086.598-.171.94-.171.426-.086.938-.171 1.365-.256.427-.171.768-.171 1.195-.342 6.486-1.536 12.802-3.67 18.862-6.23-10.327 14.082-24.154 25.52-40.371 32.945a109 109 0 0 0 22.191-3.84c26.204-7.768 48.224-25.35 61.454-49.078-2.646 15.022-8.62 29.361-17.497 41.823 6.316-4.183 12.12-8.962 17.326-14.425 14.595-15.193 24.155-34.568 27.398-55.308 2.22 10.242 2.902 20.911 1.878 31.324 46.944-65.465 3.926-133.405-14.083-151.244-.085-.17-.17-.17-.17-.341-.086.085-.086.085-.086.17 0-.085 0-.085-.086-.17 0 .768-.085 1.536-.17 2.304-.17 1.537-.427 2.902-.683 4.353-.341 1.451-.683 2.902-1.11 4.268s-.938 2.817-1.536 4.182c-.597 1.28-1.195 2.646-1.963 3.926a53 53 0 0 1-2.305 3.67c-.853 1.196-1.792 2.39-2.645 3.5-.94 1.195-2.049 2.22-3.073 3.243-.683.598-1.195 1.11-1.878 1.622-.512.427-.939.854-1.536 1.28-1.195.94-2.305 1.793-3.67 2.56-1.195.77-2.56 1.537-3.841 2.22-1.366.683-2.731 1.195-4.097 1.792-1.366.513-2.817.94-4.268 1.366s-2.902.683-4.353.939a42 42 0 0 1-4.438.512c-1.024.085-2.048.17-3.158.17-1.536 0-2.987-.17-4.438-.255-1.537-.171-2.988-.342-4.439-.683-1.536-.256-2.902-.683-4.353-1.11h-.085c1.451-.17 2.902-.256 4.268-.512 1.536-.256 2.902-.597 4.353-.939 1.45-.426 2.902-.853 4.267-1.365 1.451-.512 2.817-1.195 4.097-1.793 1.366-.682 2.56-1.365 3.926-2.133 1.195-.854 2.476-1.707 3.67-2.561 1.195-.939 2.305-1.878 3.33-2.902 1.109-.939 2.048-2.048 3.072-3.158.939-1.195 1.878-2.305 2.731-3.5.17-.17.256-.426.427-.682a39 39 0 0 0 1.878-3.158c.682-1.28 1.365-2.56 1.963-3.926s1.11-2.732 1.536-4.183c.427-1.365.768-2.816 1.11-4.267.256-1.537.512-2.902.682-4.353.171-1.537.257-2.988.257-4.439 0-1.024-.086-2.048-.171-3.158-.17-1.536-.342-2.902-.512-4.353-.256-1.536-.598-2.902-.94-4.352-.426-1.366-.938-2.817-1.45-4.183s-1.195-2.731-1.793-4.011c-.682-1.28-1.45-2.56-2.219-3.841l-2.56-3.585c-.94-1.11-1.963-2.22-2.988-3.329-.512-.512-1.11-1.11-1.707-1.621-2.902-2.305-5.974-4.439-9.047-6.402-.427-.256-.853-.426-1.28-.683-2.049-1.536-4.012-2.219-6.06-2.902z'/></svg>",
+ "netlify": "<svg viewBox='0 0 24 24'><path fill='#00bfa5' d='m11.25 2.232-.127.127v4.567l.127.129h1.526l.126-.13V2.36l-.126-.127H11.25zM6.69 5.455 5.643 6.5v.21l1.6 1.6h1.108l.148-.148V7.055l-1.6-1.6zM1.026 11.11l-.127.127v1.528l.127.127h6.09l.127-.127v-1.528l-.127-.127zm15.858 0-.127.127v1.528l.127.127h6.09l.127-.127v-1.528l-.127-.127zm-9.64 4.58-1.6 1.6v.21l1.045 1.046h.21l1.6-1.6v-1.109l-.148-.146zm4.005 1.256-.127.13v4.566l.127.127h1.526l.126-.127v-4.567l-.126-.129z'/><path fill='#cfd8dc' d='M14.855 15.172h-1.523l-.127-.127V11.48c0-.634-.249-1.125-1.013-1.142-.394-.01-.844 0-1.325.019l-.072.074v4.61l-.127.128H9.146l-.128-.127V8.956l.128-.127h3.425a2.41 2.41 0 0 1 2.41 2.41v3.806z'/></svg>",
+ "netlify_light": "<svg viewBox='0 0 24 24'><path fill='#00bfa5' d='m11.25 2.232-.127.127v4.567l.127.129h1.526l.126-.13V2.36l-.126-.127H11.25zM6.69 5.455 5.643 6.5v.21l1.6 1.6h1.108l.148-.148V7.055l-1.6-1.6zM1.026 11.11l-.127.127v1.528l.127.127h6.09l.127-.127v-1.528l-.127-.127zm15.858 0-.127.127v1.528l.127.127h6.09l.127-.127v-1.528l-.127-.127zm-9.64 4.58-1.6 1.6v.21l1.045 1.046h.21l1.6-1.6v-1.109l-.148-.146zm4.005 1.256-.127.13v4.566l.127.127h1.526l.126-.127v-4.567l-.126-.129z'/><path fill='#00897b' d='M14.855 15.172h-1.523l-.127-.127V11.48c0-.634-.249-1.125-1.013-1.142-.394-.01-.844 0-1.325.019l-.072.074v4.61l-.127.128H9.146l-.128-.127V8.956l.128-.127h3.425a2.41 2.41 0 0 1 2.41 2.41v3.806z'/></svg>",
+ "next": "<svg viewBox='0 0 32 32'><path fill='#cfd8dc' d='M16 2a14 14 0 1 0 5.816 26.723L12 14v9a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1h2.434a1 1 0 0 1 .857.486l11.491 19.15A14 14 0 0 0 16 2m8 16h-4V9a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1Z'/></svg>",
+ "next_light": "<svg viewBox='0 0 32 32'><path fill='#455a64' d='M16 2a14 14 0 1 0 5.816 26.723L12 14v9a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1h2.434a1 1 0 0 1 .857.486l11.491 19.15A14 14 0 0 0 16 2m8 16h-4V9a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1Z'/></svg>",
+ "nginx": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='M16 0 2 8v16l14 8 14-8V8Zm8 23a1 1 0 0 1-1 1h-2.52a1 1 0 0 1-.78-.375L12 14v9a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1h2.52a1 1 0 0 1 .78.375L20 18V9a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1Z'/></svg>",
+ "ngrx-actions": "<svg viewBox='0 0 300 300'><path fill='#ab47bc' d='M150 27.324 35.85 68.006l17.303 151.09 96.843 53.586 96.843-53.586 17.303-151.09zm-23.719 38.349c4.346-.075 9.04 1.316 14.265 4.131 2.3 1.24 9.235 2.994 15.407 3.889 21.936 3.18 47.975 19.934 56.21 36.186 5.667 11.183 4.508 17.209-4.18 21.702-7.492 3.874-22.822 2-45.08-5.517l-18.785-6.343-6.683 2.552c-9.683 3.698-19.366 12.877-23.33 22.09-2.858 6.645-3.293 9.768-2.77 20.705.523 10.955 1.315 14.12 5.2 20.997 4.423 7.829 14.576 17.818 16.331 16.064.473-.473-.574-3.648-2.308-7.048-1.735-3.4-2.744-6.825-2.26-7.606.482-.781 5.054 2.123 10.157 6.44 11.35 9.6 24.608 15.74 36.77 17.01 9.985 1.045 12.266-.814 4.787-3.912-2.41-.998-5.544-3.088-6.95-4.641-2.907-3.212-3.072-3.12 9.356-5.906 7.736-1.733 23.026-9.849 23.937-12.71.29-.91-2.195-1.296-6.27-.972-3.706.295-6.732-.087-6.732-.85 0-.76 3.032-4.523 6.732-8.385 13.883-14.489 18.62-25.32 20.098-45.906l1.02-14.217 3.257 6.756c3.601 7.452 4.265 18.202 1.701 27.437-2.141 7.711-.712 8.564 3.208 1.92 4.845-8.212 6.39-6.905 5.54 4.666-.924 12.587-5.243 22.017-14.993 32.686-7.95 8.699-7.001 10.254 2.624 4.326 9.273-5.711 10.511-4.815 5.736 4.155-9.031 16.964-28.122 31.35-47.948 36.161-12.016 2.917-20.537 3.461-31.544 2.018-28.78-3.775-56.001-23.157-68.993-49.114-3.378-6.748-8.154-14.994-10.62-18.348-5.092-6.924-5.529-10.038-2.09-15.286 1.715-2.618 2.116-5.307 1.41-9.308-3.273-18.531-3.167-19.11 4.276-26.659 6.468-6.56 6.878-7.44 6.878-15.092 0-6.637.671-8.813 3.67-11.811 2.02-2.02 5.23-3.7 7.12-3.718 5.49-.05 14.97-5.135 20.584-11.033 4.687-4.927 9.674-7.417 15.262-7.51z'/></svg>",
+ "ngrx-effects": "<svg viewBox='0 0 300 300'><path fill='#26c6da' d='M150 27.324 35.85 68.006l17.303 151.09 96.843 53.586 96.843-53.586 17.303-151.09zm-23.719 38.349c4.346-.075 9.04 1.316 14.265 4.131 2.3 1.24 9.235 2.994 15.407 3.889 21.936 3.18 47.975 19.934 56.21 36.186 5.667 11.183 4.508 17.209-4.18 21.702-7.492 3.874-22.822 2-45.08-5.517l-18.785-6.343-6.683 2.552c-9.683 3.698-19.366 12.877-23.33 22.09-2.858 6.645-3.293 9.768-2.77 20.705.523 10.955 1.315 14.12 5.2 20.997 4.423 7.829 14.576 17.818 16.331 16.064.473-.473-.574-3.648-2.308-7.048-1.735-3.4-2.744-6.825-2.26-7.606.482-.781 5.054 2.123 10.157 6.44 11.35 9.6 24.608 15.74 36.77 17.01 9.985 1.045 12.266-.814 4.787-3.912-2.41-.998-5.544-3.088-6.95-4.641-2.907-3.212-3.072-3.12 9.356-5.906 7.736-1.733 23.026-9.849 23.937-12.71.29-.91-2.195-1.296-6.27-.972-3.706.295-6.732-.087-6.732-.85 0-.76 3.032-4.523 6.732-8.385 13.883-14.489 18.62-25.32 20.098-45.906l1.02-14.217 3.257 6.756c3.601 7.452 4.265 18.202 1.701 27.437-2.141 7.711-.712 8.564 3.208 1.92 4.845-8.212 6.39-6.905 5.54 4.666-.924 12.587-5.243 22.017-14.993 32.686-7.95 8.699-7.001 10.254 2.624 4.326 9.273-5.711 10.511-4.815 5.736 4.155-9.031 16.964-28.122 31.35-47.948 36.161-12.016 2.917-20.537 3.461-31.544 2.018-28.78-3.775-56.001-23.157-68.993-49.114-3.378-6.748-8.154-14.994-10.62-18.348-5.092-6.924-5.529-10.038-2.09-15.286 1.715-2.618 2.116-5.307 1.41-9.308-3.273-18.531-3.167-19.11 4.276-26.659 6.468-6.56 6.878-7.44 6.878-15.092 0-6.637.671-8.813 3.67-11.811 2.02-2.02 5.23-3.7 7.12-3.718 5.49-.05 14.97-5.135 20.584-11.033 4.687-4.927 9.674-7.417 15.262-7.51z'/></svg>",
+ "ngrx-entity": "<svg viewBox='0 0 300 300'><path fill='#fbc02d' d='M150 27.324 35.85 68.006l17.303 151.09 96.843 53.586 96.843-53.586 17.303-151.09zm-23.719 38.349c4.346-.075 9.04 1.316 14.265 4.131 2.3 1.24 9.235 2.994 15.407 3.889 21.936 3.18 47.975 19.934 56.21 36.186 5.667 11.183 4.508 17.209-4.18 21.702-7.492 3.874-22.822 2-45.08-5.517l-18.785-6.343-6.683 2.552c-9.683 3.698-19.366 12.877-23.33 22.09-2.858 6.645-3.293 9.768-2.77 20.705.523 10.955 1.315 14.12 5.2 20.997 4.423 7.829 14.576 17.818 16.331 16.064.473-.473-.574-3.648-2.308-7.048-1.735-3.4-2.744-6.825-2.26-7.606.482-.781 5.054 2.123 10.157 6.44 11.35 9.6 24.608 15.74 36.77 17.01 9.985 1.045 12.266-.814 4.787-3.912-2.41-.998-5.544-3.088-6.95-4.641-2.907-3.212-3.072-3.12 9.356-5.906 7.736-1.733 23.026-9.849 23.937-12.71.29-.91-2.195-1.296-6.27-.972-3.706.295-6.732-.087-6.732-.85 0-.76 3.032-4.523 6.732-8.385 13.883-14.489 18.62-25.32 20.098-45.906l1.02-14.217 3.257 6.756c3.601 7.452 4.265 18.202 1.701 27.437-2.141 7.711-.712 8.564 3.208 1.92 4.845-8.212 6.39-6.905 5.54 4.666-.924 12.587-5.243 22.017-14.993 32.686-7.95 8.699-7.001 10.254 2.624 4.326 9.273-5.711 10.511-4.815 5.736 4.155-9.031 16.964-28.122 31.35-47.948 36.161-12.016 2.917-20.537 3.461-31.544 2.018-28.78-3.775-56.001-23.157-68.993-49.114-3.378-6.748-8.154-14.994-10.62-18.348-5.092-6.924-5.529-10.038-2.09-15.286 1.715-2.618 2.116-5.307 1.41-9.308-3.273-18.531-3.167-19.11 4.276-26.659 6.468-6.56 6.878-7.44 6.878-15.092 0-6.637.671-8.813 3.67-11.811 2.02-2.02 5.23-3.7 7.12-3.718 5.49-.05 14.97-5.135 20.584-11.033 4.687-4.927 9.674-7.417 15.262-7.51z'/></svg>",
+ "ngrx-reducer": "<svg viewBox='0 0 300 300'><path fill='#e53935' d='M150 27.324 35.85 68.006l17.303 151.09 96.843 53.586 96.843-53.586 17.303-151.09zm-23.719 38.349c4.346-.075 9.04 1.316 14.265 4.131 2.3 1.24 9.235 2.994 15.407 3.889 21.936 3.18 47.975 19.934 56.21 36.186 5.667 11.183 4.508 17.209-4.18 21.702-7.492 3.874-22.822 2-45.08-5.517l-18.785-6.343-6.683 2.552c-9.683 3.698-19.366 12.877-23.33 22.09-2.858 6.645-3.293 9.768-2.77 20.705.523 10.955 1.315 14.12 5.2 20.997 4.423 7.829 14.576 17.818 16.331 16.064.473-.473-.574-3.648-2.308-7.048-1.735-3.4-2.744-6.825-2.26-7.606.482-.781 5.054 2.123 10.157 6.44 11.35 9.6 24.608 15.74 36.77 17.01 9.985 1.045 12.266-.814 4.787-3.912-2.41-.998-5.544-3.088-6.95-4.641-2.907-3.212-3.072-3.12 9.356-5.906 7.736-1.733 23.026-9.849 23.937-12.71.29-.91-2.195-1.296-6.27-.972-3.706.295-6.732-.087-6.732-.85 0-.76 3.032-4.523 6.732-8.385 13.883-14.489 18.62-25.32 20.098-45.906l1.02-14.217 3.257 6.756c3.601 7.452 4.265 18.202 1.701 27.437-2.141 7.711-.712 8.564 3.208 1.92 4.845-8.212 6.39-6.905 5.54 4.666-.924 12.587-5.243 22.017-14.993 32.686-7.95 8.699-7.001 10.254 2.624 4.326 9.273-5.711 10.511-4.815 5.736 4.155-9.031 16.964-28.122 31.35-47.948 36.161-12.016 2.917-20.537 3.461-31.544 2.018-28.78-3.775-56.001-23.157-68.993-49.114-3.378-6.748-8.154-14.994-10.62-18.348-5.092-6.924-5.529-10.038-2.09-15.286 1.715-2.618 2.116-5.307 1.41-9.308-3.273-18.531-3.167-19.11 4.276-26.659 6.468-6.56 6.878-7.44 6.878-15.092 0-6.637.671-8.813 3.67-11.811 2.02-2.02 5.23-3.7 7.12-3.718 5.49-.05 14.97-5.135 20.584-11.033 4.687-4.927 9.674-7.417 15.262-7.51z'/></svg>",
+ "ngrx-selectors": "<svg viewBox='0 0 300 300'><path fill='#ff6e40' d='M150 27.324 35.85 68.006l17.303 151.09 96.843 53.586 96.843-53.586 17.303-151.09zm-23.719 38.349c4.346-.075 9.04 1.316 14.265 4.131 2.3 1.24 9.235 2.994 15.407 3.889 21.936 3.18 47.975 19.934 56.21 36.186 5.667 11.183 4.508 17.209-4.18 21.702-7.492 3.874-22.822 2-45.08-5.517l-18.785-6.343-6.683 2.552c-9.683 3.698-19.366 12.877-23.33 22.09-2.858 6.645-3.293 9.768-2.77 20.705.523 10.955 1.315 14.12 5.2 20.997 4.423 7.829 14.576 17.818 16.331 16.064.473-.473-.574-3.648-2.308-7.048-1.735-3.4-2.744-6.825-2.26-7.606.482-.781 5.054 2.123 10.157 6.44 11.35 9.6 24.608 15.74 36.77 17.01 9.985 1.045 12.266-.814 4.787-3.912-2.41-.998-5.544-3.088-6.95-4.641-2.907-3.212-3.072-3.12 9.356-5.906 7.736-1.733 23.026-9.849 23.937-12.71.29-.91-2.195-1.296-6.27-.972-3.706.295-6.732-.087-6.732-.85 0-.76 3.032-4.523 6.732-8.385 13.883-14.489 18.62-25.32 20.098-45.906l1.02-14.217 3.257 6.756c3.601 7.452 4.265 18.202 1.701 27.437-2.141 7.711-.712 8.564 3.208 1.92 4.845-8.212 6.39-6.905 5.54 4.666-.924 12.587-5.243 22.017-14.993 32.686-7.95 8.699-7.001 10.254 2.624 4.326 9.273-5.711 10.511-4.815 5.736 4.155-9.031 16.964-28.122 31.35-47.948 36.161-12.016 2.917-20.537 3.461-31.544 2.018-28.78-3.775-56.001-23.157-68.993-49.114-3.378-6.748-8.154-14.994-10.62-18.348-5.092-6.924-5.529-10.038-2.09-15.286 1.715-2.618 2.116-5.307 1.41-9.308-3.273-18.531-3.167-19.11 4.276-26.659 6.468-6.56 6.878-7.44 6.878-15.092 0-6.637.671-8.813 3.67-11.811 2.02-2.02 5.23-3.7 7.12-3.718 5.49-.05 14.97-5.135 20.584-11.033 4.687-4.927 9.674-7.417 15.262-7.51z'/></svg>",
+ "ngrx-state": "<svg viewBox='0 0 300 300'><path fill='#9ccc65' d='M150 27.324 35.85 68.006l17.303 151.09 96.843 53.586 96.843-53.586 17.303-151.09zm-23.719 38.349c4.346-.075 9.04 1.316 14.265 4.131 2.3 1.24 9.235 2.994 15.407 3.889 21.936 3.18 47.975 19.934 56.21 36.186 5.667 11.183 4.508 17.209-4.18 21.702-7.492 3.874-22.822 2-45.08-5.517l-18.785-6.343-6.683 2.552c-9.683 3.698-19.366 12.877-23.33 22.09-2.858 6.645-3.293 9.768-2.77 20.705.523 10.955 1.315 14.12 5.2 20.997 4.423 7.829 14.576 17.818 16.331 16.064.473-.473-.574-3.648-2.308-7.048-1.735-3.4-2.744-6.825-2.26-7.606.482-.781 5.054 2.123 10.157 6.44 11.35 9.6 24.608 15.74 36.77 17.01 9.985 1.045 12.266-.814 4.787-3.912-2.41-.998-5.544-3.088-6.95-4.641-2.907-3.212-3.072-3.12 9.356-5.906 7.736-1.733 23.026-9.849 23.937-12.71.29-.91-2.195-1.296-6.27-.972-3.706.295-6.732-.087-6.732-.85 0-.76 3.032-4.523 6.732-8.385 13.883-14.489 18.62-25.32 20.098-45.906l1.02-14.217 3.257 6.756c3.601 7.452 4.265 18.202 1.701 27.437-2.141 7.711-.712 8.564 3.208 1.92 4.845-8.212 6.39-6.905 5.54 4.666-.924 12.587-5.243 22.017-14.993 32.686-7.95 8.699-7.001 10.254 2.624 4.326 9.273-5.711 10.511-4.815 5.736 4.155-9.031 16.964-28.122 31.35-47.948 36.161-12.016 2.917-20.537 3.461-31.544 2.018-28.78-3.775-56.001-23.157-68.993-49.114-3.378-6.748-8.154-14.994-10.62-18.348-5.092-6.924-5.529-10.038-2.09-15.286 1.715-2.618 2.116-5.307 1.41-9.308-3.273-18.531-3.167-19.11 4.276-26.659 6.468-6.56 6.878-7.44 6.878-15.092 0-6.637.671-8.813 3.67-11.811 2.02-2.02 5.23-3.7 7.12-3.718 5.49-.05 14.97-5.135 20.584-11.033 4.687-4.927 9.674-7.417 15.262-7.51z'/></svg>",
+ "nim": "<svg viewBox='0 0 32 32'><path fill='#ffca28' d='M6 24h20v2a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2zM30 6l-9 9-5-11-5 11-9-9 4 14h20z'/></svg>",
+ "nix": "<svg viewBox='0 0 500 500'><g stroke-width='.395'><path fill='#1976d2' d='M133.347 451.499c0-.295-2.752-5.283-6.116-11.084s-6.116-10.776-6.116-11.055 9.514-16.889 21.143-36.912c11.629-20.022 21.323-36.798 21.542-37.279.346-.76-1.608-4.363-14.896-27.466-8.412-14.625-15.294-26.785-15.294-27.023 0-.5 24.46-43.501 25.206-44.31.414-.45.592-.384 1.078.395.32.513 16.876 29.256 36.791 63.87 62.62 108.85 74.852 130.01 75.41 130.46.3.242.544.554.544.694s-11.836.21-26.302.154c-23.023-.09-26.313-.175-26.393-.694-.11-.714-27.662-48.825-28.86-50.392-.746-.978-.906-1.035-1.426-.51-.688.696-28.954 49.323-29.49 50.733l-.364.96h-13.23c-10.895 0-13.228-.095-13.228-.538zm167.58-125.61c-.134-.216 1.189-2.863 2.939-5.882 6.924-11.944 84.29-145.75 96.49-166.88 7.143-12.371 13.143-22.465 13.334-22.433.362.062 25.86 43.105 25.86 43.655 0 .174-6.761 11.952-15.025 26.173-8.46 14.557-14.932 26.104-14.81 26.421.185.483 4.563.564 30.213.564h29.996l.957 1.48c.527.814 3.296 5.547 6.155 10.518s5.45 9.29 5.757 9.597c.705.705.703.724-.16 1.572-.396.388-3.36 5.323-6.588 10.965-3.228 5.643-6.056 10.387-6.285 10.543s-19.695.171-43.256.034l-42.84-.249-.803 1.15c-.442.632-7.505 12.736-15.696 26.897l-14.892 25.747h-15.486c-8.518 0-20.015.116-25.551.259-6.55.168-10.15.121-10.308-.135zm-133.75-157.86c-56.373-.055-102.5-.182-102.5-.282s5.617-10.132 12.481-22.294L89.64 123.34h30.332c27.113 0 30.332-.065 30.332-.611 0-.336-6.659-12.228-14.797-26.427s-14.797-25.917-14.797-26.04 2.682-4.853 5.96-10.51 6.003-10.578 6.056-10.934c.086-.586 1.375-.648 13.572-.648 7.412 0 13.463.143 13.446.317-.018.174.22.707.53 1.184.31.476 9.763 16.937 21.007 36.578 11.244 19.64 20.71 36.022 21.036 36.4.554.647 2.549.691 31.428.691h30.837l12.896 22.145c7.093 12.18 12.8 22.301 12.682 22.492-.117.19-4.776.303-10.352.249-5.575-.054-56.26-.143-112.63-.198z'/><path fill='#64b5f6' d='M23.046 238.939c-6.098 10.563-6.69 11.711-6.224 12.078.282.224 3.18 5.044 6.44 10.712s6.016 10.355 6.123 10.417c.106.061 13.585.153 29.95.204 16.367.052 29.994.23 30.285.399.473.273-1.08 3.094-14.637 26.574l-15.166 26.269 12.907 21.865c7.1 12.026 12.982 21.906 13.068 21.956s23.257-39.831 51.492-88.624c11.352-19.617 21.214-36.64 30.37-52.442 23.308-40.452 30.68-53.468 30.73-54.132-1.096-.11-6.141-.187-13.006-.216-3.945-.01-7.82-.02-12.75-.002l-25.341.092-15.42 26.706c-14.256 24.693-15.445 26.663-16.278 26.86l-.023.037c-.012.003-1.622-.001-1.826 0-4.29.062-20.453.063-40.226-.01-22.632-.082-41.615-.125-42.183-.096-.567.03-1.147-.03-1.29-.132-.141-.102-3.29 5.066-6.996 11.485zm205.16-190.3c-.123.149 5.62 10.392 12.761 22.763 12.2 21.131 89.393 155.03 96.276 167 1.503 2.613 2.92 4.803 3.443 5.348.9-1.249 3.532-5.63 7.954-13.219a1343 1343 0 0 1 10.05-17.76l6.606-11.443c.691-1.403.753-1.818.652-2.117-.161-.48-6.903-12.332-14.982-26.337-8.078-14.005-14.824-25.849-14.99-26.32a.73.73 0 0 1-.01-.366l-.426-.913 21.636-36.976c3.69-6.307 6.425-11.042 9.471-16.29 9.158-15.948 12.036-21.189 11.895-21.55-.126-.324-2.7-4.83-5.72-10.017-3.021-5.185-5.845-10.148-6.275-11.026-.483-.987-.734-1.364-1.1-1.456-.054.014-.083.018-.144.035-.42.112-5.455.195-11.19.185s-11.22.024-12.187.073l-1.76.089-14.998 25.978c-12.824 22.212-15.084 25.964-15.595 25.883-.024-.004-.15-.189-.235-.301-.109.066-.2.09-.271.05-.256-.148-7.144-11.902-15.306-26.119L279.4 48.817c-.116-.186-.444-.744-.458-.752-.476-.275-50.502.287-50.737.57zm-18.646 283.09c-.047.109-.026.262.043.48.328 1.05 25.338 43.735 25.772 43.985.206.119 14.178.239 31.05.266 26.65.044 30.749.152 31.234.832.307.43 9.987 17.214 21.513 37.296s21.152 36.627 21.394 36.767 5.926.243 12.633.23c6.705-.013 12.4.099 12.657.246.131.076.381-.141.851-.795l6.008-10.406c5.234-9.065 6.62-11.684 6.294-11.888-.575-.36-15.597-26.643-23.859-41.482-3.09-5.45-5.37-9.516-5.44-9.774-.196-.712-.066-.822 1.155-.98 1.956-.252 57.397-.057 58.071.205.237.092.79-.569 2.593-3.497 1.866-3.067 5.03-8.524 11.001-18.866 7.22-12.505 13.043-22.784 12.941-22.843s-.77-.051-1.489.016l-.046.001c-4.451.204-33.918.203-149.74.025-38.96-.06-69.786-.09-71.912-.072-1.12.01-2.095.076-2.66.172a.3.3 0 0 0-.062.083z'/></g></svg>",
+ "nodejs": "<svg viewBox='0 0 32 32'><path fill='#8bc34a' d='M16 20.003v2h4a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2h-2v-2h4v-2h-4a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h2v2Z'/><path fill='#8bc34a' d='m16 3.003-12 7v14l4 2h6v-13.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v11.5H8l-2-1.034V11.15l10-5.833 10 5.833v11.703l-10 5.833-1.745-1.022L13 29.253l3 1.75 12-7v-14Z'/></svg>",
+ "nodejs_alt": "<svg viewBox='0 0 32 32'><path fill='#388e3c' d='M15.354 2.831 4.647 8.861A1.25 1.25 0 0 0 4 9.953V22.03a1.26 1.26 0 0 0 .646 1.095l10.709 6.039a1.32 1.32 0 0 0 1.294 0l10.705-6.038A1.26 1.26 0 0 0 28 22.03V9.96a1.25 1.25 0 0 0-.647-1.093L16.65 2.836a1.32 1.32 0 0 0-1.294 0Z'/><path fill='#4caf50' d='M4.305 22.784a1.3 1.3 0 0 0 .381.328l9.185 5.18 1.53.862a1.3 1.3 0 0 0 .745.166 1.4 1.4 0 0 0 .254-.046L27.693 9.082a1.3 1.3 0 0 0-.294-.234L20.38 4.894l-3.705-2.082a1.3 1.3 0 0 0-.335-.13Z'/><path fill='#66bb6a' d='M27.693 22.784a1.3 1.3 0 0 1-.38.328l-9.185 5.18-1.53.862a1.3 1.3 0 0 1-.745.166 1.4 1.4 0 0 1-.254-.046L4.305 9.08a1.3 1.3 0 0 1 .295-.234l7.018-3.952 3.705-2.082a1.3 1.3 0 0 1 .335-.13Z'/></svg>",
+ "nodemon": "<svg viewBox='0 0 32 32'><path fill='#8bc34a' d='m27.354 8.517-10.708-6.34A1.27 1.27 0 0 0 15.999 2a1.27 1.27 0 0 0-.647.178L4.647 8.516A1.33 1.33 0 0 0 4 9.664v12.678a1.33 1.33 0 0 0 .647 1.147l10.705 6.333a1.26 1.26 0 0 0 1.293 0l10.708-6.333A1.33 1.33 0 0 0 28 22.341V9.664a1.33 1.33 0 0 0-.646-1.147M24 23.307a.5.5 0 0 1-.658.474l-5-1.667A.5.5 0 0 1 18 21.64V15.6a.5.5 0 0 0-.223-.415l-1.5-1a.5.5 0 0 0-.554 0l-1.5 1A.5.5 0 0 0 14 15.6v6.039a.5.5 0 0 1-.342.474l-5 1.667A.5.5 0 0 1 8 23.306V12.31a.5.5 0 0 1 .276-.447l1.942-.971A2 2 0 0 1 10 10V6l3.333 3.333 2.443-1.221a.5.5 0 0 1 .448 0l2.443 1.221L22 6v4a2 2 0 0 1-.218.89l1.942.972a.5.5 0 0 1 .276.447Z'/></svg>",
+ "npm": "<svg viewBox='0 0 32 32'><path fill='#e53935' d='M4 4v24h24V4Zm20 20h-4V12h-4v12H8V8h16Z'/></svg>",
+ "nuget": "<svg viewBox='0 0 32 32'><circle cx='5' cy='5' r='3' fill='#0288d1'/><path fill='#0288d1' d='M8 14v10a6 6 0 0 0 6 6h10a6 6 0 0 0 6-6V14a6 6 0 0 0-6-6H14a6 6 0 0 0-6 6m7 4a3 3 0 1 1 3-3 3 3 0 0 1-3 3m7 8a4 4 0 1 1 4-4 4 4 0 0 1-4 4'/></svg>",
+ "nunjucks": "<svg viewBox='0 0 32 32'><path fill='#388e3c' d='M12 4v12L8 4H4v24h4V18l4 10h4V4zm12 6v14h-2v-4h-4v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V10Z'/></svg>",
+ "nuxt": "<svg viewBox='0 0 32 32'><path fill='#00e676' d='m30.27 23-6.93-12a1.98 1.98 0 0 0-1.73-1 1.96 1.96 0 0 0-1.73 1l-2.27 3.93-3.88-6.71a1.996 1.996 0 0 0-3.46 0L1.73 23a2 2 0 0 0 1.73 3h8.915a6 6 0 0 0 5.197-3.001L21.61 16l3.46 6h-2.31l-2.31 4h8.09a2 2 0 0 0 1.73-3m-17.896-1H6.93L12 13.22l3.3 5.71-1.193 2.069A2 2 0 0 1 12.374 22'/></svg>",
+ "nx": "<svg class='h-8 w-8' viewBox='0 0 24 24'><path fill='#039be5' d='m12 13.862-2.749 4.434-4.61-7.537v7.891H1.36V5.35h3.28l4.611 7.892V9.695l2.66 4.168zm.532-5.054V5.35h-3.28v3.458zm4.966 3.635a1.77 1.77 0 0 0-1.774 1.153 1.77 1.77 0 0 1 2.128-.62c.355.177.887.354 1.153.265a1.86 1.86 0 0 0-1.507-.798m3.014.887c-.354 0-.709-.177-.975-.532l-.177-.266a1.9 1.9 0 0 0-.444-.532 1.77 1.77 0 0 0-1.064-.266 2.22 2.22 0 0 0-2.04 1.33 2.04 2.04 0 0 1 3.548.355.71.71 0 0 0 .798.266c.443 0 .354.354 1.064.443v-.089c0-.354-.266-.443-.71-.62zm1.774 1.153a.62.62 0 0 0 .354-.532c0-2.66-2.128-4.877-4.788-4.877a4.79 4.79 0 0 0-3.99 2.128l-1.33-2.128h-3.28l3.103 4.788L9.34 18.65h3.192l1.241-2.216 1.42 2.128h3.102l-2.748-4.434a.6.6 0 0 1 0-.266 2.394 2.394 0 0 1 2.305-2.394c1.33 0 1.507.798 1.773 1.153.621.71 1.774.443 1.774 1.33a.62.62 0 0 0 .887.532m.354.177c-.177.266-.532.266-.709.532-.089.266.089.355.089.355s.354.177.532-.266v-.621z'/></svg>",
+ "objective-c": "<svg viewBox='0 0 32 32'><path fill='#ffab40' d='M19.563 22A5.57 5.57 0 0 1 14 16.437v-2.873A5.57 5.57 0 0 1 19.563 8H24V2h-4.437A11.563 11.563 0 0 0 8 13.563v2.873A11.564 11.564 0 0 0 19.563 28H24v-6Z'/></svg>",
+ "objective-cpp": "<svg viewBox='0 0 32 32'><path fill='#ffab40' d='M28 14v-4h-2v4h-6v-4h-2v4h-4v2h4v4h2v-4h6v4h2v-4h4v-2z'/><path fill='#ffab40' d='M13.563 22A5.57 5.57 0 0 1 8 16.437v-2.873A5.57 5.57 0 0 1 13.563 8H18V2h-4.437A11.563 11.563 0 0 0 2 13.563v2.873A11.564 11.564 0 0 0 13.563 28H18v-6Z'/></svg>",
+ "ocaml": "<svg viewBox='0 0 24 24'><path d='m12.019 15.021.003-.008c-.005-.021-.006-.026-.003.008'/><path fill='#ff9800' d='M4.51 3.273a2.523 2.523 0 0 0-2.524 2.523V11.3c.361-.13.88-.898 1.043-1.085.285-.327.337-.743.478-1.006C3.83 8.612 3.886 8.2 4.62 8.2c.342 0 .478.08.71.39.16.216.438.615.568.882.15.307.396.724.503.808q.122.095.233.137c.119.044.218-.037.297-.1.102-.082.145-.247.24-.467.135-.317.283-.697.367-.83.146-.23.195-.501.352-.633.232-.195.535-.208.618-.225.466-.092.677.225.907.43.15.133.355.403.5.765.114.283.26.544.32.707.059.158.203.41.289.713.077.275.286.486.365.616 0 0 .121.34.858.65.16.067.482.176.674.246.32.116.63.101 1.025.054.281 0 .434-.408.562-.734.075-.193.148-.745.197-.902.048-.153-.064-.27.031-.405.112-.156.178-.164.242-.368.138-.436.936-.458 1.384-.458.374 0 .327.363.96.239.364-.072.714.046 1.1.149.324.086.63.184.812.398.119.139.412.834.113.863.029.035.05.099.104.134-.067.262-.357.075-.518.041-.217-.045-.37.007-.583.101-.363.162-.894.143-1.21.407-.27.223-.269.721-.394 1 0 0-.348.895-1.106 1.443-.194.14-.574.477-1.4.605a5.3 5.3 0 0 1-1.1.043c-.186-.009-.362-.018-.549-.02-.11-.002-.48-.013-.461.022l-.041.103.024.138c.015.083.019.149.022.225.006.157-.013.32-.005.478.017.328.138.627.154.958.017.368.199.758.375 1.059.067.114.169.128.213.269.052.161.003.333.028.505.1.668.292 1.366.592 1.97l.008.014c.371-.062.743-.196 1.226-.267.885-.132 2.115-.064 2.906-.138 2-.188 3.085.82 4.882.407V5.796a2.523 2.523 0 0 0-2.523-2.523zm-.907 11.144q-.022 0-.046.003c-.159.025-.313.08-.412.24-.08.13-.108.355-.164.505-.064.175-.176.338-.274.505-.18.305-.504.581-.644.879-.028.06-.053.13-.076.2v3.402c.163.028.333.062.524.113 1.407.375 1.75.407 3.13.25l.13-.018c.105-.22.187-.968.255-1.2.054-.178.127-.32.155-.5.026-.173-.003-.337-.017-.493-.04-.393.285-.533.44-.87.14-.304.22-.651.336-.963.11-.298.284-.721.579-.872-.036-.041-.617-.06-.772-.076a5 5 0 0 1-.5-.07c-.314-.064-.656-.126-.965-.2a10 10 0 0 1-.947-.328c-.298-.138-.503-.497-.732-.507m5.737.83c-.74.149-.97.876-1.32 1.451-.192.319-.396.59-.548.928-.14.312-.128.657-.368.924a2.55 2.55 0 0 0-.528.922c-.023.067-.088.776-.158.943l1.101-.078c1.026.07.73.464 2.332.378l2.529-.078a7 7 0 0 0-.228-.588c-.07-.147-.16-.434-.218-.56a3.5 3.5 0 0 0-.309-.526c-.184-.215-.227-.23-.28-.503-.095-.473-.344-1.33-.637-1.923-.151-.306-.403-.562-.634-.784-.2-.195-.655-.522-.734-.505z'/></svg>",
+ "odin": "<svg stroke-linejoin='round' stroke-miterlimit='2' clip-rule='evenodd' viewBox='0 0 260 260'><path fill='#448aff' d='M73.123 212.264a99 99 0 0 1-6.865-5.18l98.605-170.789s4.059 1.506 7.412 3.113c4.226 2.026 7.726 4 7.726 4 47.796 27.596 64.198 88.805 36.602 136.6-27.595 47.798-88.804 64.198-136.6 36.603 0 0-3.347-1.924-6.879-4.346zm98.545-154.422L88.336 202.177c39.831 22.997 90.838 9.329 113.834-30.5 22.995-39.832 9.33-90.838-30.5-113.835zM47.944 187.19c-3.35-4.852-4.543-7.18-4.543-7.18-17.227-29.922-18.49-67.972 0-100s52.075-49.958 86.603-50c0 0 4.661-.011 8.543.36 5.847.56 9.735 1.315 9.735 1.315L53.986 195.011s-2.237-2.316-6.04-7.82zm72.084-139.903c-25.13 3.054-48.573 17.467-62.193 41.058-13.62 23.589-14.378 51.098-4.459 74.39z'/></svg>",
+ "opa": "<svg viewBox='0 0 32 32'><path fill='#bdbdbd' d='M29.12 10.065c0-2.5-4.686-8.122-4.998-8.434 1.124 5.057.984 4.302 1.172 5.917a2.44 2.44 0 0 1-.1 1.261c-.78 1.802-3.572 2.817-3.572 2.817l4.373 3.749s3.125-3.124 3.125-5.31m-26.24 0c0-2.5 4.686-8.122 4.998-8.434-1.124 5.057-.984 4.302-1.172 5.917a2.44 2.44 0 0 0 .1 1.261c.78 1.802 3.572 2.817 3.572 2.817l-4.373 3.749S2.88 12.25 2.88 10.065'/><path fill='#546e7a' d='M16 8c4.897 0 8.452 3.007 9.996 7.375V22s-3.124 2.122-4.998 3.371A31.7 31.7 0 0 0 16.312 30H16Z'/><path fill='#78909c' d='M16 8c-4.897 0-8.452 3.007-9.996 7.375V22s3.124 2.122 4.998 3.371A31.7 31.7 0 0 1 15.688 30H16Z'/><circle cx='16' cy='16' r='2' fill='#fafafa'/></svg>",
+ "opam": "<svg viewBox='0 0 24 24'><path fill='#ff9800' d='M2.222 3.198v5.667c.255-.092.621-.635.736-.766.2-.231.237-.525.337-.71.228-.422.267-.712.786-.712.241 0 .337.055.5.275.114.152.31.434.402.622.106.217.279.511.355.57a.6.6 0 0 0 .164.097c.084.032.154-.026.21-.07.072-.058.103-.175.169-.33.096-.224.2-.492.26-.586.102-.162.137-.354.248-.447.164-.137.377-.147.436-.158.329-.065.478.158.64.303.106.094.25.285.354.54.08.2.182.384.225.5.041.11.143.289.204.502.055.194.202.343.258.435 0 0 .085.24.605.459.113.047.34.124.476.174.226.082.445.071.723.038.199 0 .307-.288.397-.518.053-.136.105-.526.14-.637.033-.108-.046-.191.021-.286.079-.11.126-.116.171-.26.098-.307.66-.323.977-.323.264 0 .23.256.678.169.256-.05.503.033.776.105.229.06.444.13.573.281.084.098.291.588.08.61.02.024.035.069.073.093-.047.185-.252.053-.366.03-.153-.032-.261.005-.41.07-.257.115-.632.102-.856.288-.19.157-.189.51-.277.706 0 0-.246.632-.781 1.018-.137.1-.405.337-.989.427a3.7 3.7 0 0 1-.775.031c-.132-.006-.256-.013-.388-.015-.077 0-.338-.009-.325.016l-.03.073.017.097c.011.059.014.105.016.16.004.11-.009.225-.003.337.011.231.097.442.108.676.012.26.14.535.265.747.047.081.12.09.15.19.037.114.003.235.02.357a4.7 4.7 0 0 0 .423 1.4c.263-.044.525-.138.866-.189.624-.092 1.493-.045 2.05-.097 1.413-.133 2.179.58 3.447.288V3.198zm1.141 7.866-.033.002c-.112.018-.22.056-.29.17-.057.091-.076.25-.116.356-.046.123-.124.239-.194.356-.127.216-.355.41-.454.62q-.03.067-.054.142v2.401c.115.02.235.044.37.08.993.265 1.236.287 2.21.176l.091-.012c.075-.155.132-.684.18-.847.038-.126.09-.226.11-.354.018-.121-.002-.237-.013-.348-.027-.276.202-.375.311-.613.099-.215.156-.46.237-.68.078-.21.2-.509.409-.615-.025-.03-.435-.043-.544-.054a4 4 0 0 1-.354-.05c-.221-.045-.463-.088-.68-.14a7 7 0 0 1-.67-.233c-.21-.097-.354-.35-.516-.357m4.05.586c-.523.105-.686.618-.932 1.024-.136.225-.28.416-.387.655-.1.22-.09.464-.26.652a1.8 1.8 0 0 0-.372.65c-.016.048-.062.549-.112.667l.777-.055c.724.05.515.327 1.646.266l1.785-.055a5 5 0 0 0-.161-.415c-.05-.103-.113-.306-.154-.395a2.4 2.4 0 0 0-.218-.37c-.13-.153-.16-.164-.198-.356a6.4 6.4 0 0 0-.449-1.358c-.107-.215-.285-.397-.447-.553-.142-.137-.463-.368-.518-.357m10.365-4.848v2h2v10h-10v-2h-2v4h14v-14z'/></svg>",
+ "openapi": "<svg viewBox='0 0 24 24'><path fill='#8bc34a' d='M10.86 4.29h-.36a9 9 0 0 0-1.11.12h-.03l-.24.05-.09.02-.15.03h-.03a9 9 0 0 0-2.12.8l-.13.07-.16.09-.11.06h-.01l-.03.03.09.15 2.63 4.36.15-.09a4 4 0 0 1 .16-.08 3.6 3.6 0 0 1 1.18-.33 4 4 0 0 1 .36-.02V4.3zM5.98 5.75 5.61 6a9 9 0 0 0-.76.63l3.74 3.74.12-.1-.01-.02zm8.47 7.4-.01.17-.01.18a3.57 3.57 0 0 1-.8 1.92l-.11.13c-.04.04-.08.1-.13.13l3.73 3.73.24-.26a9 9 0 0 0 .76-.94l.03-.03.15-.23.03-.06a8.83 8.83 0 0 0 1.37-4.39v-.18l.01-.18h-5.26zM2 13.5v.08l.01.15.02.23V14l.02.19v.02l.03.2c.06.42.15.84.27 1.25l.07.2v.01l.06.19.02.04.05.15.03.07.04.12.04.1.04.09.05.12.04.07.06.14.1.2.01.03.1.17.02.04 4.5-2.71.03-.01a3.6 3.6 0 0 1-.33-1.18zM7.78 15l-4.51 2.7.21.34.01.01.01.02.02.03.24.34h.01v.01l.11.15.02.02.12.14.02.03.11.13.05.06.1.1.05.06.02.02.07.08.03.03.12.13 3.73-3.73a4 4 0 0 1-.13-.13l-.11-.13-.1-.13-.1-.14-.1-.15zm4.93 1.22-.16.08a3.6 3.6 0 0 1-1.7.43 3.6 3.6 0 0 1-1.03-.15l-.16-.05a3 3 0 0 1-.17-.07L7.62 21l-.07.18-.07.15h.02l.01.01.14.05.17.07c.03 0 .05.02.08.03a9 9 0 0 0 1.8.43l.16.02.14.02h.04a9 9 0 0 0 .38.03h.44a9 9 0 0 0 1.47-.11h.02l.15-.04.1-.01.23-.05a9 9 0 0 0 2.15-.8l.14-.08.15-.08.1-.06h.01l.01-.01.04-.02-.1-.15-.09-.16z'/><path fill='#689f38' d='M11.21 4.3v5.27a3.7 3.7 0 0 1 .8.17l3.89-3.88a9 9 0 0 0-.44-.29h-.02l-.14-.1-.09-.04-.08-.05-.14-.07-.02-.02a9 9 0 0 0-.95-.42l-.03-.01-.21-.08A9 9 0 0 0 12 4.36l-.08-.01h-.07l-.14-.02h-.05l-.17-.02h-.06l-.15-.01zM4.6 6.87 4.47 7l-.12.13a9 9 0 0 0-.75.93l-.04.05a6 6 0 0 0-.15.23l-.03.04a8.83 8.83 0 0 0-1.37 4.4v.17l-.01.18h5.26l.01-.18.01-.17a3.57 3.57 0 0 1 .8-1.92c.03-.05.08-.09.12-.13.04-.05.07-.1.12-.14L4.6 6.88zM18.14 8.1l-3.89 3.89c.1.26.15.52.18.8h5.28v-.08l-.02-.14v-.07l-.01-.17v-.04l-.03-.2v-.01l-.03-.21a9 9 0 0 0-.27-1.24v-.01l-.06-.2-.07-.19-.01-.04a6 6 0 0 0-.08-.22l-.05-.12-.08-.2-.05-.11-.1-.21-.02-.04-.08-.17V9.1l-.1-.17v-.01a9 9 0 0 0-.5-.82zm-5.01 7.82-.13.1.01.01 2.72 4.5.37-.25a9 9 0 0 0 .76-.63l-3.72-3.71zm-4.55 0-.01.02-3.72 3.71.16.16c.02 0 .04.02.06.04l.13.1.03.03.15.12.01.02.16.12a9 9 0 0 0 .7.47l.03.02a9 9 0 0 0 .45.25l.02.01a8 8 0 0 0 .4.2l.14-.32 1.87-4.54v-.02a3.6 3.6 0 0 1-.58-.39'/><path fill='#e0e0e0' d='M19.53 2a2.46 2.46 0 0 0-1.74.72 2.47 2.47 0 0 0-.47 2.84l-5.37 5.37a2.47 2.47 0 1 0 1.12 1.12l5.37-5.37a2.47 2.47 0 0 0 2.84-3.96A2.46 2.46 0 0 0 19.53 2'/></svg>",
+ "openapi_light": "<svg viewBox='0 0 24 24'><path fill='#8bc34a' d='M10.86 4.29h-.36a9 9 0 0 0-1.11.12h-.03l-.24.05-.09.02-.15.03h-.03a9 9 0 0 0-2.12.8l-.13.07-.16.09-.11.06h-.01l-.03.03.09.15 2.63 4.36.15-.09a4 4 0 0 1 .16-.08 3.6 3.6 0 0 1 1.18-.33 4 4 0 0 1 .36-.02V4.3zM5.98 5.75 5.61 6a9 9 0 0 0-.76.63l3.72 3.72.02.02.12-.1-.01-.02zm8.47 7.4-.01.17-.01.18a3.57 3.57 0 0 1-.8 1.92l-.11.13c-.04.04-.08.1-.13.13l3.73 3.73.12-.13.12-.13a9 9 0 0 0 .76-.94l.03-.03.15-.23.03-.06a8.83 8.83 0 0 0 1.37-4.39v-.18l.01-.18h-5.26zM2 13.5v.08l.01.15.02.23V14l.02.19v.02l.03.2c.06.42.15.84.27 1.25l.07.2v.01l.06.19.02.04.05.15.03.07.04.12.04.1.04.09.05.12.04.07.06.14.02.04.08.16.01.03.1.17.02.04 4.5-2.71.03-.01a3.6 3.6 0 0 1-.33-1.18zM7.78 15l-4.51 2.7.21.34.01.01.01.02.02.03.24.34h.01v.01l.11.15.02.02.12.14.02.03.11.13.05.06.1.1.05.06.02.02.07.08.03.03.12.13 3.73-3.73a4 4 0 0 1-.13-.13l-.11-.13-.1-.13-.1-.14-.1-.15zm4.93 1.22-.16.08a3.6 3.6 0 0 1-1.7.43 3.6 3.6 0 0 1-1.03-.15l-.16-.05a3 3 0 0 1-.17-.07L7.62 21l-.07.18-.07.15h.02l.01.01.14.05.17.07c.03 0 .05.02.08.03a9 9 0 0 0 1.8.43l.08.01.08.01.14.02h.04a9 9 0 0 0 .38.03h.44a9 9 0 0 0 1.47-.11h.02l.15-.04.1-.01.23-.05a9 9 0 0 0 2.15-.8l.14-.08.15-.08.1-.06h.01l.01-.01.04-.02-.1-.15-.09-.16z'/><path fill='#689f38' d='M11.21 4.3v5.27a3.7 3.7 0 0 1 .8.17l3.89-3.88a9 9 0 0 0-.44-.29h-.02l-.14-.1-.09-.04-.08-.05-.14-.07-.02-.02a9 9 0 0 0-.95-.42l-.03-.01-.21-.08A9 9 0 0 0 12 4.36l-.08-.01h-.07l-.14-.02h-.05l-.17-.02h-.06l-.15-.01zM4.6 6.87 4.47 7l-.12.13a9 9 0 0 0-.75.93l-.04.05a6 6 0 0 0-.15.23l-.03.04a8.83 8.83 0 0 0-1.37 4.4v.17l-.01.18h5.26l.01-.18.01-.17a3.57 3.57 0 0 1 .8-1.92c.03-.05.08-.09.12-.13.04-.05.07-.1.12-.14L4.6 6.88zM18.14 8.1l-3.89 3.89c.1.26.15.52.18.8h5.28v-.08l-.02-.14v-.07l-.01-.17v-.04l-.03-.2v-.01l-.03-.21a9 9 0 0 0-.27-1.24v-.01l-.06-.2-.07-.19-.01-.04a6 6 0 0 0-.08-.22l-.05-.12-.04-.1-.04-.1-.05-.11-.1-.21-.02-.04-.08-.17V9.1l-.1-.17v-.01a9 9 0 0 0-.5-.82zm-5.01 7.82-.13.1.01.01 2.72 4.5.37-.25a9 9 0 0 0 .76-.63l-3.72-3.71zm-4.55 0-.01.02-3.72 3.71.06.06.1.1c.02 0 .04.02.06.04l.13.1.03.03.15.12.01.02.16.12a9 9 0 0 0 .7.47l.03.02a9 9 0 0 0 .45.25l.02.01a8 8 0 0 0 .4.2l.14-.32 1.87-4.54v-.02a3.6 3.6 0 0 1-.58-.39'/><path fill='#424242' d='M19.53 2a2.46 2.46 0 0 0-1.74.72 2.47 2.47 0 0 0-.47 2.84l-5.37 5.37a2.47 2.47 0 1 0 1.12 1.12l5.37-5.37a2.47 2.47 0 0 0 2.84-3.96A2.46 2.46 0 0 0 19.53 2' data-mit-no-recolor='true'/></svg>",
+ "otne": "<svg viewBox='0 0 1024 1024'><g fill='#00c853'><path d='M512 254.16A257.84 257.84 0 0 0 254.16 512 257.84 257.84 0 0 0 512 769.841a257.84 257.84 0 0 0 257.841-257.84 257.84 257.84 0 0 0-257.84-257.842zM941.74 512A429.74 429.74 0 0 1 512 941.74 429.74 429.74 0 0 1 82.262 512 429.74 429.74 0 0 1 512 82.262 429.74 429.74 0 0 1 941.74 512'/><path d='M695.945 450.836h-92.08l-.005 122.318h92.084zm-122.854-122.78H450.92v367.89h122.17zm-152.942 122.78h-92.084v122.318h92.08z'/></g></svg>",
+ "packship": "<svg viewBox='0 0 16 16'><path fill='#9575cd' d='M2.997 14.967c-.344-.086-.654-.345-.765-.64-.136-.36-.157-.283 1.119-4.186s1.181-3.58 1.01-3.467c-.741.491-1.703.082-1.95-.83-.175-.648.027-1.006.899-1.587 1.274-.85 1.979-1.476 2.603-2.312.492-.659.682-.798 1.194-.873.623-.09 1.204.138 1.418.56.075.147.02.153.417-.053 4.991-2.592 6.93 4.168 2.114 7.369-1.361.904-3.966 1.598-4.84 1.29l-.128-.045-.144.439c-.08.241-.378 1.16-.663 2.042-.622 1.929-.645 1.972-1.152 2.21-.228.106-.854.153-1.132.083m.729-.772c.272-.068.224.052 1.27-3.18 1.267-3.918 1.463-4.385 2.38-5.681 2.03-2.868 4.835-3.397 4.577-.864-.218 2.145-2.177 3.72-5 4.02-.493.052-.577.117-.647.496-.1.542.18.631 1.35.431 3.043-.52 5.106-2.398 5.391-4.906.365-3.207-3.167-3.65-5.56-.698-.289.356-.294.418.059-.698.392-1.241.389-1.266-.198-1.266-.306 0-.397.047-.604.31-.072.09-.243.31-.38.486-.58.745-1.443 1.49-2.632 2.275-.652.43-.708.542-.468.922.157.247.337.32.56.226.238-.099 1.576-1.11 2.002-1.51.124-.118.226-.205.226-.193s-.7 2.17-1.555 4.795a462 462 0 0 0-1.556 4.82c0 .198.407.31.785.215m3.962-6.61c2.008-.431 3.407-1.742 3.486-3.266.031-.597-.03-.691-.436-.666-1.12.068-3.05 2.1-3.756 3.956l-.034.087.218-.022a6 6 0 0 0 .522-.088'/></svg>",
+ "palette": "<svg viewBox='0 0 16 16'><path fill='#4fc3f7' d='M12.278 8a1.167 1.167 0 0 1-1.167-1.167 1.167 1.167 0 0 1 1.167-1.166 1.167 1.167 0 0 1 1.166 1.166A1.167 1.167 0 0 1 12.278 8M9.944 4.889a1.167 1.167 0 0 1-1.166-1.167 1.167 1.167 0 0 1 1.166-1.166 1.167 1.167 0 0 1 1.167 1.166A1.167 1.167 0 0 1 9.944 4.89m-3.888 0a1.167 1.167 0 0 1-1.167-1.167 1.167 1.167 0 0 1 1.167-1.166 1.167 1.167 0 0 1 1.166 1.166A1.167 1.167 0 0 1 6.056 4.89M3.722 8a1.167 1.167 0 0 1-1.166-1.167 1.167 1.167 0 0 1 1.166-1.166A1.167 1.167 0 0 1 4.89 6.833 1.167 1.167 0 0 1 3.722 8M8 1a7 7 0 0 0-7 7 7 7 0 0 0 7 7 1.167 1.167 0 0 0 1.167-1.167c0-.303-.117-.575-.304-.777a1.2 1.2 0 0 1-.295-.778 1.167 1.167 0 0 1 1.166-1.167h1.377A3.89 3.89 0 0 0 15 7.222C15 3.784 11.866 1 8 1'/></svg>",
+ "panda": "<svg viewBox='0 0 24 24'><path fill='#ffd740' d='M4.524 20.862c-.258-.317-.958-2.683-1.319-4.451-1.238-6.075.1-10.397 3.824-12.354 1.596-.838 2.918-1.114 5.37-1.118 3.212-.007 5.102.617 6.808 2.244 2.52 2.403 2.735 6.732.459 9.222-1.267 1.387-4.598 2.82-6.551 2.82h-.593l-.408-1.239c-.224-.68-.456-1.502-.516-1.825l-.108-.586.656.088c.777.104 1.89-.27 2.365-.798.998-1.102.824-3.595-.302-4.333-1.063-.697-3.124-.653-4.166.089-1.888 1.345-1.382 6.248 1.172 11.343.248.495.406.944.351.999-.054.055-1.624.1-3.49.1-2.519 0-3.431-.052-3.552-.2z'/></svg>",
+ "parcel": "<svg viewBox='0 0 24 24'><path fill='#ffb74d' d='M2.007 10.96a.985.985 0 0 1-.37-1.37L3.137 7c.11-.2.28-.34.47-.42l7.83-4.4c.16-.12.36-.18.57-.18s.41.06.57.18l7.9 4.44c.19.1.35.26.44.46l1.45 2.52c.28.48.11 1.09-.36 1.36l-1 .58v4.96c0 .38-.21.71-.53.88l-7.9 4.44c-.16.12-.36.18-.57.18s-.41-.06-.57-.18l-7.9-4.44a.99.99 0 0 1-.53-.88v-5.54c-.3.17-.68.18-1 0m10-6.81v6.7l5.96-3.35zm-7 11.76 6 3.38v-6.71l-6-3.37zm14 0v-3.22l-5 2.9c-.33.18-.7.17-1 .01v3.69zm-5.15-2.55 6.28-3.63-.58-1.01-6.28 3.63z'/></svg>",
+ "pascal": "<svg viewBox='0 0 24 24'><path fill='#0288d1' d='m8.863 14.765-1.158 6.597H3.937L7.191 2.637l6.559.013q3.035 0 4.77 1.685 1.737 1.685 1.518 4.398-.205 2.752-2.302 4.398-2.083 1.646-5.324 1.646zm.527-3.125 3.138.026q1.517 0 2.52-.785 1.003-.784 1.196-2.122t-.437-2.135q-.617-.797-1.839-.848l-3.55-.013z' aria-label='P'/></svg>",
+ "pawn": "<svg viewBox='0 0 32 32'><path fill='#ef6c00' d='M6 28h20v2H6zm8-18h4l4 14H10z'/><path fill='#ef6c00' d='M10 12h12v2H10z'/><circle cx='16' cy='7' r='4' fill='#ef6c00'/></svg>",
+ "payload": "<svg viewBox='0 0 24 24'><path fill='#cfd8dc' d='m11.617 2 9.163 5.508v10.455l-6.9 3.991V11.5L4.706 6zm-.703 11.216v8.159L4 17.215z'/></svg>",
+ "payload_light": "<svg viewBox='0 0 24 24'><path fill='#455a64' d='m11.617 2 9.163 5.508v10.455l-6.9 3.991V11.5L4.706 6zm-.703 11.216v8.159L4 17.215z'/></svg>",
+ "pdf": "<svg viewBox='0 0 24 24'><path fill='#ef5350' d='M13 9h5.5L13 3.5zM6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2m4.93 10.44c.41.9.93 1.64 1.53 2.15l.41.32c-.87.16-2.07.44-3.34.93l-.11.04.5-1.04c.45-.87.78-1.66 1.01-2.4m6.48 3.81c.18-.18.27-.41.28-.66.03-.2-.02-.39-.12-.55-.29-.47-1.04-.69-2.28-.69l-1.29.07-.87-.58c-.63-.52-1.2-1.43-1.6-2.56l.04-.14c.33-1.33.64-2.94-.02-3.6a.85.85 0 0 0-.61-.24h-.24c-.37 0-.7.39-.79.77-.37 1.33-.15 2.06.22 3.27v.01c-.25.88-.57 1.9-1.08 2.93l-.96 1.8-.89.49c-1.2.75-1.77 1.59-1.88 2.12-.04.19-.02.36.05.54l.03.05.48.31.44.11c.81 0 1.73-.95 2.97-3.07l.18-.07c1.03-.33 2.31-.56 4.03-.75 1.03.51 2.24.74 3 .74.44 0 .74-.11.91-.3m-.41-.71.09.11c-.01.1-.04.11-.09.13h-.04l-.19.02c-.46 0-1.17-.19-1.9-.51.09-.1.13-.1.23-.1 1.4 0 1.8.25 1.9.35M7.83 17c-.65 1.19-1.24 1.85-1.69 2 .05-.38.5-1.04 1.21-1.69zm3.02-6.91c-.23-.9-.24-1.63-.07-2.05l.07-.12.15.05c.17.24.19.56.09 1.1l-.03.16-.16.82z'/></svg>",
+ "pdm": "<svg viewBox='0 0 24 24'><path fill='#9575cd' d='m16.145 6.113 2.757 1.59a.54.54 0 0 1 .27.469v7.656a.54.54 0 0 1-.27.469l-1.675.965-.239-.137-.843-.488ZM5.973 13.25l9.101 5.254-2.804 1.621a.53.53 0 0 1-.54 0l-6.632-3.828a.54.54 0 0 1-.27-.469v-1.914l.067-.039Zm2.156-1.242 5.863-3.387v6.774Zm5.863-5.871-9.164 5.289V8.172a.54.54 0 0 1 .27-.469l6.632-3.828a.53.53 0 0 1 .54 0l1.722.996Zm-3.34-4.125a2.68 2.68 0 0 1 2.696 0L19.98 5.84a2.69 2.69 0 0 1 1.344 2.332v7.656c0 .961-.512 1.852-1.344 2.332l-6.632 3.828a2.68 2.68 0 0 1-2.696 0L4.02 18.16a2.69 2.69 0 0 1-1.344-2.332V8.172c0-.965.511-1.852 1.344-2.332Zm0 0'/></svg>",
+ "percy": "<svg viewBox='0 0 24 24'><path fill='#ba68c8' d='M22.847 7.51a.74.74 0 0 0-.706-.527.8.8 0 0 0-.288.051 3.2 3.2 0 0 1-1.064.21c-.914-.672-3.131-2.293-4.448-3.204 0 0 .178.531.418 1.521 0 0-2.086-1.193-3.473-2.106 0 0 .506 1.05.543 1.388 0 0-1.663-.309-4.092-.864 0 0 1.796 1.017 2.232 1.62 0 0-2.18-.182-4.747-.369 0 0 1.291.664 1.918 1.193 0 0-2.336.294-4.112.348 0 0 1.298 1.025 1.82 1.519 0 0-2.23.814-4.904 2.266 0 0 1.394.09 2.394.42 0 0-1.232 1.022-3.441 3.376 0 0 1.392-.026 2.517-.155 0 0-.902 1.31-2.038 3.672 0 0 .758-.449 1.487-.604 0 0 .03 2.674 1.533 3.128l.002-.002a.7.7 0 0 0 .205.034 1 1 0 0 0 .23-.029c.481-.124.852-.575 1.282-1.098.142-.173.288-.35.443-.523.171-.226.394-.461.66-.666.549-.423 1.305-.734 2.218-.575.99.12 1.589 1.093 2.072 1.878.37.6.662 1.074 1.08 1.126q.046.006.088.006c.641.011.88-.71 1.181-1.621l.062-.186.009.018c.224-.74.649-1.535 1.164-2.25.698-.968 1.59-1.828 2.438-2.22l.003.005c.46-.248.945-.481 1.422-.711h.001c1.483-.715 2.884-1.39 3.599-2.438.358-.524.542-1.171.548-1.924a6 6 0 0 0-.256-1.706zm-6.19 7.882c-.428.378-.848.85-1.218 1.364-.61.846-1.064 1.776-1.208 2.552.403.668.77 1 1.117 1.011h.011q.481.008.637-.606c.077-.305.1-.725.127-1.211.048-.886.11-2.027.534-3.11m-9.87 3.82-.005-.013a3.1 3.1 0 0 1 .697-.751c.442-.34 1.022-.588 1.716-.516a18 18 0 0 1-.274.647c-.342.763-.748 1.55-1.173 1.607l-.076.005c-.331-.006-.621-.326-.885-.98zm8.512-12.44-1.831-1.18L16.5 6.697zm-5.1.437 2.919.776 1.286-.389zm5.203 1.512.634-.448-2.43.352zm-4.15-.071-1.015.633-2.775-.304zm3.103 1.678 1.027-.533-3.79.33zm-2.813.564-.73 1.116-3.563.779zm-4.447.618-2.768 1.124 3.414-2.283zm-1.527 1.378-.13 1.17-1.611 1.887z' clip-rule='evenodd'/></svg>",
+ "perl": "<svg viewBox='0 0 24 24'><path fill='#ba68c8' d='M11.057 2.981c.537.735.028 1.653.141 2.472a3.42 3.42 0 0 1-1.03 2.415c-1.414 1.625-3.165 3.038-4.097 5.03a5.28 5.28 0 0 0 1.412 5.847c.706.735 1.54 1.342 2.472 1.738.17.805-1.088.184-1.455 0A6.7 6.7 0 0 1 4.361 16.4a5.44 5.44 0 0 1 .904-5.368c1.272-1.61 3.136-2.543 4.662-3.857.565-.55 1.003-1.3.932-2.119.156-.678-.254-1.469.212-2.09zm-.07 18.929c-.17.198-.467.325-.495.24-.042-.085.212-.127.381-.325.17-.183.127-.522.24-.522.1 0 .043.395-.14.607zm2.16 0c.17.198.453.31.495.24.028-.085-.212-.141-.395-.339-.156-.184-.113-.523-.24-.523-.085 0-.029.41.14.608zm-1.03.48c-.1 0-.071-.296-.071-.65 0-.367-.028-.663.07-.663.085 0 .057.296.057.663 0 .354.014.65-.057.65m-.495-20.765c.34.24.254 2.077.254 3.136 0 1.653.184 3.376-.805 4.916-.96 1.497-2.048 3.108-1.95 4.972.1 1.837.99 3.504 2.148 5.043.664.876-.353.509-.876.085a7.2 7.2 0 0 1-2.755-5.664c.142-1.907 1.597-3.348 2.628-4.803.805-1.13 1.186-1.879 1.215-3.645.028-1.412-.142-3.531.042-3.983.014-.043.07-.1.099-.057m.537 2.232c-.085 0-.043.396.028.72.424 2.26-.198 4.52-.749 6.682a12.77 12.77 0 0 0 .283 7.826c.607 1.568 1.71.791 2.161 1.568.34.593 1.272.198 1.978-.141 2.232-1.102 4.012-3.108 4.11-5.566.029-.494 0-.989-.07-1.497-.283-1.837-1.78-3.065-3.15-4.083-1.215-.89-2.74-1.483-3.659-2.613-.523-.65-.297-1.638-.381-2.458-.043-.452-.255-.042-.382-.268-.084-.127-.14-.17-.17-.17zm.72 3.616c.057 0 .17.071.325.226a20 20 0 0 0 2.161 1.921c1.272.961 2.43 2.091 2.967 3.504.339.875.339 1.836.226 2.74-.184 1.384-1.187 2.444-2.119 3.404-.339.354-1.06.791-1.074.678-.084-.367.763-1.172 1.159-1.695A5.93 5.93 0 0 0 16 10.962c-1.102-1.214-2.317-1.907-2.995-3.08-.14-.253-.183-.409-.113-.409z'/></svg>",
+ "php-cs-fixer": "<svg viewBox='0 0 24 24'><path fill='#ff7043' d='M22 2c-2.023.139-16.234 1.492-17.227 11.234a43 43 0 0 0-.234 3.135l4.313-4.308a1.5 1.5 0 0 1-.952.339 1.5 1.5 0 0 1-1.5-1.5 1.5 1.5 0 0 1 1.5-1.5 1.5 1.5 0 0 1 1.5 1.5 1.5 1.5 0 0 1-.349.963l2.476-2.474a.625.625 0 1 1 .885.884l-2.525 2.522A1.5 1.5 0 0 1 10.9 12.4a1.5 1.5 0 0 1 1.5 1.5 1.5 1.5 0 0 1-1.5 1.5 1.5 1.5 0 0 1-1.5-1.5 1.5 1.5 0 0 1 .379-.998L2.275 20.4a.937.937 0 1 0 1.327 1.325l2.232-2.229a44 44 0 0 0 4.92-.287c2.089-.213 3.791-1.033 5.18-2.207h-3.946l5.735-1.91a15.5 15.5 0 0 0 1.189-1.84h-3.17l4.162-2.078C21.543 7.191 21.929 3.025 22 2'/></svg>",
+ "php": "<svg viewBox='0 0 24 24'><path fill='#1e88e5' d='M12 18.08c-6.63 0-12-2.72-12-6.08s5.37-6.08 12-6.08S24 8.64 24 12s-5.37 6.08-12 6.08m-5.19-7.95c.54 0 .91.1 1.09.31.18.2.22.56.13 1.03-.1.53-.29.87-.58 1.09q-.42.33-1.29.33h-.87l.53-2.76zm-3.5 5.55h1.44l.34-1.75h1.23c.54 0 .98-.06 1.33-.17.35-.12.67-.31.96-.58.24-.22.43-.46.58-.73.15-.26.26-.56.31-.88.16-.78.05-1.39-.33-1.82-.39-.44-.99-.65-1.82-.65H4.59zm7.25-8.33-1.28 6.58h1.42l.74-3.77h1.14c.36 0 .6.06.71.18s.13.34.07.66l-.57 2.93h1.45l.59-3.07c.13-.62.03-1.07-.27-1.36-.3-.27-.85-.4-1.65-.4h-1.27L12 7.35zM18 10.13c.55 0 .91.1 1.09.31.18.2.22.56.13 1.03-.1.53-.29.87-.57 1.09-.29.22-.72.33-1.3.33h-.85l.5-2.76zm-3.5 5.55h1.44l.34-1.75h1.22c.55 0 1-.06 1.35-.17.35-.12.65-.31.95-.58.24-.22.44-.46.58-.73.15-.26.26-.56.32-.88.15-.78.04-1.39-.34-1.82-.36-.44-.99-.65-1.82-.65h-2.75z'/></svg>",
+ "php_elephant": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M28 10a4 4 0 0 0-4-4h-6v6a6 6 0 0 1-6 6h-2v2h2v6h4v-6h8v6h4V16h2v-4a2 2 0 0 0-2-2'/><path fill='#0288d1' d='M12 4H8v2a6 6 0 0 0-6 6v6a2 2 0 0 0 2 2v2H2.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5H6a2 2 0 0 0 2-2v-8h4a4 4 0 0 0 4-4V8a4 4 0 0 0-4-4M6 14H4v-2h2Z'/></svg>",
+ "php_elephant_pink": "<svg viewBox='0 0 32 32'><path fill='#ec407a' d='M28 10a4 4 0 0 0-4-4h-6v6a6 6 0 0 1-6 6h-2v2h2v6h4v-6h8v6h4V16h2v-4a2 2 0 0 0-2-2'/><path fill='#ec407a' d='M12 4H8v2a6 6 0 0 0-6 6v6a2 2 0 0 0 2 2v2H2.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5H6a2 2 0 0 0 2-2v-8h4a4 4 0 0 0 4-4V8a4 4 0 0 0-4-4M6 14H4v-2h2Z'/></svg>",
+ "phpstan": "<svg viewBox='0 0 32 32'><path fill='#263238' d='M6.405 24.174 5.77 25.68c-.135.532.019 1.045.547 1.269.597.255 1.146-.02 1.316-.348l.218-.52z'/><path fill='#5c6bc0' d='M16.08 4.6s2.434-.1 4.47 1.499a.41.41 0 0 0 .536 0 .384.384 0 0 0 .029-.538c-.102-.124-.55-.574-.904-.593 0 0 .703-.962 2.531-.962 2.323 0 7.258 3.375 7.258 9.696 0 6.074-3.977 7.383-5.058 7.383-1.645 0-3.35-1.697-3.35-2.782a4.9 4.9 0 0 0 1.542-1.4 4.9 4.9 0 0 0 .82-1.92s.088-2.59 0-4.786a.46.46 0 0 0-.399-.374.41.41 0 0 0-.42.408c0 .245.108 1.763.027 4.486-.044 1.466-1.432 2.612-2.085 2.87q.319-.897.528-1.827c.05-.238 0-.405-.16-.472a.35.35 0 0 0-.477.203c-.106.215-3.885 12.498-9.11 12.498-4.416 0-6.961-6.3-6.961-7.745s.921-2.127 1.734-2.127 1.922.817 2.057 1.077-.582.883-.582.883l-.625-.505a.385.385 0 0 0-.489.015.363.363 0 0 0-.01.51c.16.16 4.933 4.006 4.933 4.006a.445.445 0 0 0 .568.037.376.376 0 0 0 .017-.529l-1.344-1.083 1.431-4.03s-2.197-.982-3.632.102c0 0-.757-1.261-2.26-1.261a2.5 2.5 0 0 0-1.738.665 2.54 2.54 0 0 0-.808 1.687S2 17.6 2 13.735C2 9.358 5.467 4 9.487 4a3.92 3.92 0 0 1 2.488.89l-.888.674a.407.407 0 0 0-.03.557.38.38 0 0 0 .508.052c.14-.103 1.571-1.574 4.515-1.574'/><path fill='#263238' d='m9.853 21.495 1.273-2.884q.765.143 1.543.16c1.97 0 5.76-1.602 5.76-5.94 0-4.337-3.687-6.043-5.95-6.043-3.059 0-6.045 2.304-6.045 5.85 0 3.694 2.695 5.14 2.695 5.14l-1.023 2.309zm9.907-8.49a.32.32 0 0 1-.269-.15.32.32 0 0 1-.018-.309c.018-.035.414-.849 1.244-.849s1.225.77 1.241.801a.33.33 0 0 1-.035.36.318.318 0 0 1-.531-.068c-.01-.018-.242-.452-.675-.452s-.666.48-.67.48a.32.32 0 0 1-.286.187'/><path fill='#d7ccc8' d='M12.425 16.696c2.117 0 3.833-1.728 3.833-3.86s-1.716-3.86-3.833-3.86-3.832 1.729-3.832 3.86c0 2.132 1.716 3.86 3.832 3.86'/><path fill='#263238' d='M12.425 14.828a1.985 1.985 0 0 0 1.978-1.992c0-1.1-.886-1.991-1.978-1.991a1.985 1.985 0 0 0-1.977 1.991c0 1.1.885 1.992 1.977 1.992'/></svg>",
+ "phpunit": "<svg viewBox='0 0 24 24'><path fill='#5c6bc0' d='M21.46 10.086c-.224.59-1.012.848-1.541.269-1.668-1.925-4.936-1.945-5.845.963-.694 2.092 1.035 4.28 4.093 4.003.671-.053 1.145.548.934 1.21l-1.521 4.157c-.386 1.023-1.376 1.574-2.547 1.191L3.384 17.631c-1.049-.37-1.624-1.513-1.28-2.54L6.419 3.298c.465-1.052 1.268-1.528 2.547-1.191l11.649 4.246c1.051.373 1.625 1.515 1.281 2.541zm-2.223 2.074c0-1.571-1.713-2.559-3.075-1.773s-1.363 2.76 0 3.546 3.075-.202 3.075-1.773'/></svg>",
+ "pinejs": "<svg viewBox='0 0 24 24'><path fill='#66bb6a' d='M12.038 3.115c-.134.002-.271.083-.426.24-.467.478-6.764 9.388-6.764 9.572 0 .264.458.677.75.677.154 0 .724-.24 1.268-.535.712-.386 1.012-.635 1.075-.892.254-1.04.873-1.57 1.827-1.57.73 0 1.494.723 1.6 1.517.073.542.174.66 1.259 1.463.649.48 1.244.874 1.323.874.08 0 .328-.096.553-.214.545-.284.886-.27 1.482.063.273.152.556.28.627.285.22.014 2.389-1.307 2.463-1.5.038-.1 0-.311-.083-.47-.119-.22-5.243-7.495-6.42-9.114-.195-.267-.362-.397-.534-.396'/><path fill='#388e3c' d='m8.392 13.584-3.472 1.97-1.534 2.13c-1.614 2.241-1.82 2.643-1.534 2.988.153.184 1.488.212 10.068.212 5.44 0 9.994-.039 10.118-.086.124-.048.226-.23.226-.407 0-.341-3.33-5.189-3.648-5.31-.383-.148-1.491.674-1.841 1.364-.392.773-.883 1.085-1.674 1.064-.698-.018-1.306-.55-1.574-1.378-.154-.478-.397-.725-1.457-1.485-.862-.618-1.345-.88-1.499-.811-.502.223-1.274.217-1.72-.014z'/></svg>",
+ "pipeline": "<svg viewBox='0 0 32 32'><path fill='#f57f17' d='M20 24h-8.1a5 5 0 0 0-.732-1.754l11.078-11.078A4.997 4.997 0 1 0 20.1 6h-8.202a5 5 0 1 0 0 2H20.1a5 5 0 0 0 .73 1.754L9.755 20.832A4.997 4.997 0 1 0 11.9 26H20v4h10V20H20ZM7 10a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3m18-6a3 3 0 1 1-3 3 3.003 3.003 0 0 1 3-3M7 28a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3m15-6h6v6h-6Z'/></svg>",
+ "pkl": "<svg viewBox='0 0 100 100'><path fill='#689f38' d='m72.152 24.129 2.128-11.606a44 44 0 0 0-10.43-5.107l-7.857 8.803a33.2 33.2 0 0 0-11.588-.07l-7.761-8.908a43.7 43.7 0 0 0-10.502 4.975l1.98 11.632a33.3 33.3 0 0 0-7.288 9.022l-11.79.508A43.7 43.7 0 0 0 6.39 44.686l10.336 5.703a33.3 33.3 0 0 0 2.505 11.317l-6.955 9.538a44 44 0 0 0 7.183 9.127l10.905-4.52a33.3 33.3 0 0 0 10.415 5.098l3.118 11.387c3.854.543 7.76.56 11.615.07l3.259-11.343a33 33 0 0 0 10.476-4.966l10.844 4.66a43.5 43.5 0 0 0 7.296-9.04l-6.832-9.626a33.4 33.4 0 0 0 2.654-11.282l10.406-5.57A43.8 43.8 0 0 0 91.1 33.894l-11.781-.657a33.2 33.2 0 0 0-7.174-9.109z'/><circle cx='50.674' cy='48.601' r='27.582' fill='#dcedc8'/><g fill='#aed581'><path d='M32.215 41.215c-7.461.184-6.563 10.815-1.04 20.367 7.2 12.455 21.742 16.617 18.265-9.17a7.28 7.28 0 0 0-3.022-4.976c-6.412-4.515-11.053-6.299-14.203-6.221m16.953-16 .008.008c-14.382.009-25.26 10.52-1.19 20.4a7.26 7.26 0 0 0 5.815-.131c23.974-11.098 11.063-20.286-4.633-20.277'/><path d='M69.127 40.814c-3.315-.001-8.1 1.984-14.537 6.955a7.23 7.23 0 0 0-2.795 5.106c-2.374 26.312 12.036 19.727 19.875 6.132v-.01c4.938-8.568 4.75-18.181-2.543-18.183'/></g></svg>",
+ "plastic": "<svg viewBox='0 0 16 16'><g fill='#ff9800'><path d='M13.175 4.272 8.66 1.687a1.33 1.33 0 0 0-1.32 0L2.826 4.272a1.33 1.33 0 0 0-.668 1.153v5.151c0 .477.246.873.66 1.11l.64.364.019-6.043c0-.128.028-.26.071-.376.086-.21.243-.4.446-.516L7.49 3.113a1.03 1.03 0 0 1 1.022 0l3.496 2.002a1.03 1.03 0 0 1 .517.892v3.989c0 .369-.198.71-.517.893l-3.496 2a1 1 0 0 1-.515.136v1.464c.229 0 .459-.059.662-.175l4.515-2.586c.413-.236.668-.676.668-1.153v-5.15a1.32 1.32 0 0 0-.667-1.153'/><path d='m8.367 11.506 2.507-1.436a.74.74 0 0 0 .37-.64V6.57a.75.75 0 0 0-.37-.64L8.367 4.495a.74.74 0 0 0-.734 0L5.127 5.93a.75.75 0 0 0-.372.64l-.01 6.207 1.339.75V10.62l1.55.887a.73.73 0 0 0 .733 0zM6.295 9.229a.44.44 0 0 1-.22-.38V7.151a.44.44 0 0 1 .22-.38l1.489-.852a.44.44 0 0 1 .435 0l1.489.852a.44.44 0 0 1 .22.38V8.85a.44.44 0 0 1-.221.38l-1.489.852a.44.44 0 0 1-.435.001z'/></g></svg>",
+ "playwright": "<svg viewBox='0 0 24 24'><path fill='#ef5350' d='M9.708 15.968v-1.429l-3.97 1.125s.294-1.703 2.364-2.291a3.45 3.45 0 0 1 1.605-.091v-5.86h1.988a12 12 0 0 0-.601-1.541c-.291-.591-.589-.2-1.266.367-.477.398-1.682 1.248-3.495 1.737s-3.278.359-3.89.253c-.867-.15-1.321-.341-1.278.32.037.58.175 1.483.492 2.673.688 2.58 2.957 7.55 7.245 6.395 1.12-.302 1.91-.898 2.459-1.66H9.708zm-6.404-4.701 3.047-.803s-.09 1.173-1.232 1.474-1.816-.671-1.816-.671z'/><path fill='#4caf50' d='M21.178 7.49c-.792.14-2.694.312-5.042-.318-2.35-.63-3.908-1.729-4.526-2.246-.876-.733-1.26-1.244-1.64-.473-.335.68-.763 1.786-1.178 3.337-.898 3.354-1.57 10.432 3.985 11.921s8.512-4.978 9.41-8.333c.416-1.548.597-2.72.647-3.477.058-.857-.53-.608-1.656-.41zm-11.162 2.776s.875-1.363 2.36-.94c1.486.422 1.6 2.065 1.6 2.065zm3.624 6.11c-2.611-.765-3.014-2.848-3.014-2.848l7.016 1.962s-1.416 1.64-4.002.886m2.482-4.28s.874-1.362 2.358-.938 1.602 2.065 1.602 2.065z'/></svg>",
+ "plop": "<svg viewBox='0 0 250 250'><path fill='#00bfa5' d='M60.095 155.16c2.82 36.962 27.499 69.519 64.554 69.541h.702c37.056-.022 61.735-32.579 64.554-69.541q.12-1.556.12-3.197v-.044c-.008-16.32-8.724-38.965-25.764-65.777a5571 5571 0 0 0-26.79-41.696 5949 5949 0 0 0-8.84-13.598l-2.479-3.796-.653-1-.499-.76-.498.76-.654 1-2.478 3.797a5896 5896 0 0 0-8.842 13.597A5572 5572 0 0 0 85.74 86.142c-17.047 26.825-25.764 49.479-25.764 65.799q0 1.653.12 3.22zm31.661-2.755c1.444 18.924 14.084 35.594 33.065 35.605h.359c18.98-.012 31.62-16.681 33.065-35.605q.06-.796.061-1.637v-.023c-.004-8.355-4.468-19.95-13.197-33.678a2858 2858 0 0 0-13.72-21.348 3425 3425 0 0 0-5.8-8.906l-.589-.901-.59.901a2608 2608 0 0 0-5.798 8.906 2858 2858 0 0 0-13.72 21.348c-8.732 13.734-13.197 25.333-13.197 33.69q0 .846.061 1.647z' clip-rule='evenodd'/></svg>",
+ "pm2-ecosystem": "<svg viewBox='0 0 32 32'><defs><linearGradient id='a' x1='30.625' x2='18.735' y1='16' y2='16' gradientTransform='matrix(1 0 0 -1 0 32)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#536dfe'/><stop offset='1' stop-color='#42a5f5'/></linearGradient><linearGradient id='b' x1='14.849' x2='12.074' y1='17.238' y2='22.863' gradientTransform='matrix(1 0 0 -1 0 32)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#6200ea'/><stop offset='1' stop-color='#a0f'/></linearGradient><linearGradient id='c' x1='14.412' x2='10.362' y1='14.78' y2='4.655' gradientTransform='matrix(1 0 0 -1 0 32)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#9c27b0'/><stop offset='1' stop-color='#ec407a'/></linearGradient></defs><path fill='url(#a)' d='M21.23 18.477a4.26 4.26 0 0 0 0-4.954L18 9l1.88-1.88 4.447 6.662a3.98 3.98 0 0 1 0 4.436L19.88 24.88 18 23Zm7.93-5.25L23 4l-1.678 1.678 4.67 6.993a5.98 5.98 0 0 1 0 6.658l-4.67 6.993L23 28l6.16-9.226a4.995 4.995 0 0 0 0-5.548'/><path fill='url(#b)' d='m3.2 12 2.4-4H10a4 4 0 0 0 4-4h4.43l-2.642 4.527A7.02 7.02 0 0 1 9.742 12Zm17.545-8-3.23 5.535A9.03 9.03 0 0 1 9.743 14H2v2h14l7-12Z'/><path fill='url(#c)' d='M18.43 28H14a4 4 0 0 0-4-4H5.6l-2.4-4h6.542a7.02 7.02 0 0 1 6.047 3.473Zm-.913-5.535L20.745 28H23l-7-12H2v2h7.742a9.03 9.03 0 0 1 7.775 4.465'/></svg>",
+ "pnpm": "<svg viewBox='0 0 32 32'><path fill='#e0e0e0' d='M2 22h8v8H2zm10 0h8v8h-8zm10 0h8v8h-8zM12 12h8v8h-8z'/><path fill='#ffb300' d='M2 2h8v8H2zm10 0h8v8h-8zm10 0h8v8h-8zm0 10h8v8h-8z'/></svg>",
+ "pnpm_light": "<svg viewBox='0 0 32 32'><path fill='#757575' d='M2 22h8v8H2zm10 0h8v8h-8zm10 0h8v8h-8zM12 12h8v8h-8z'/><path fill='#ffb300' d='M2 2h8v8H2zm10 0h8v8h-8zm10 0h8v8h-8zm0 10h8v8h-8z'/></svg>",
+ "poetry": "<svg viewBox='0 0 32 32'><path fill='#3f51b5' d='M20.137 17.834A18.52 18.52 0 0 1 6 24l5 6a25.1 25.1 0 0 0 13-8Z'/><path fill='#1976d2' d='M6 2v22a18.52 18.52 0 0 0 14.137-6.166Z'/><path fill='#29b6f6' d='m6 2 14.137 15.834A23.7 23.7 0 0 0 26 2Z'/></svg>",
+ "postcss": "<svg viewBox='0 0 32 32'><path fill='#e53935' d='M20 12v8h-8v-8zm2-2H10v12h12z'/><path fill='#e53935' d='M16 5.488 26.159 20H5.84zM16 2 2 22h28z'/><path fill='#e53935' d='M16 13a3 3 0 1 1-3 3 3.003 3.003 0 0 1 3-3m0-2a5 5 0 1 0 5 5 5 5 0 0 0-5-5'/><path fill='#e53935' d='M16 4A12 12 0 1 1 4 16 12.014 12.014 0 0 1 16 4m0-2a14 14 0 1 0 14 14A14 14 0 0 0 16 2'/></svg>",
+ "posthtml": "<svg viewBox='0 0 24 24'><g fill='#f57f17'><path d='M6.176 16.747c1.271 0 2.471-.494 3.327-1.35l6.203-5.506a2.97 2.97 0 0 1 2.118-.873c1.65 0 3 1.332 3 2.982s-1.35 2.982-3 2.982a3 3 0 0 1-2.153-.908l-.997-.883-1.35 1.183 1.129.979c.9.9 2.1 1.394 3.37 1.394 2.63 0 4.765-2.135 4.765-4.747s-2.135-4.747-4.764-4.747c-1.271 0-2.471.494-3.327 1.35l-6.203 5.506a2.97 2.97 0 0 1-2.117.873c-1.65 0-3-1.332-3-2.982s1.35-2.982 3-2.982c.794 0 1.552.308 2.152.908l1.024.892 1.323-1.183-1.129-.988a4.75 4.75 0 0 0-3.37-1.385c-2.63 0-4.765 2.126-4.765 4.738a4.74 4.74 0 0 0 4.764 4.747' style='font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;isolation:auto;mix-blend-mode:normal;shape-margin:0;shape-padding:0;solid-color:#000;text-decoration-color:#000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal'/><path d='M17.824 6.963c-1.345 0-2.619.52-3.531 1.432l-.002.001-6.197 5.5-.006.006a2.67 2.67 0 0 1-1.912.79c-1.494 0-2.71-1.2-2.71-2.692s1.216-2.691 2.71-2.691c.718 0 1.398.275 1.947.824l.008.006 1.224 1.066 1.76-1.572-1.363-1.194H9.75a5.05 5.05 0 0 0-3.574-1.468C3.389 6.97 1.12 9.229 1.12 12c0 2.803 2.27 5.037 5.055 5.037 1.345 0 2.618-.52 3.53-1.432l6.2-5.502.006-.006a2.67 2.67 0 0 1 1.912-.789c1.493 0 2.709 1.2 2.709 2.692s-1.216 2.691-2.709 2.691c-.728 0-1.398-.274-1.947-.824l-.008-.006-1.195-1.058-1.793 1.572 1.367 1.183a5.03 5.03 0 0 0 3.576 1.479c2.787 0 5.055-2.267 5.055-5.037s-2.268-5.037-5.055-5.037zm0 .58c2.472 0 4.473 2.004 4.473 4.457s-2.001 4.457-4.473 4.457a4.45 4.45 0 0 1-3.166-1.31l-.006-.006-.887-.768.907-.795.793.701.002.002a3.3 3.3 0 0 0 2.357.992c1.807 0 3.291-1.464 3.291-3.273s-1.484-3.273-3.291-3.273c-.877 0-1.704.34-2.322.957l-.002.002-6.198 5.5-.005.006c-.8.799-1.926 1.265-3.122 1.265A4.444 4.444 0 0 1 1.703 12c0-2.453 2-4.447 4.472-4.447 1.205 0 2.32.462 3.166 1.3l.008.006.887.778-.887.793-.814-.71a3.32 3.32 0 0 0-2.36-.993c-1.806 0-3.29 1.464-3.29 3.273s1.484 3.273 3.29 3.273a3.27 3.27 0 0 0 2.323-.957l6.199-5.502.006-.005c.8-.8 1.926-1.266 3.12-1.266z' style='font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;isolation:auto;mix-blend-mode:normal;shape-margin:0;shape-padding:0;solid-color:#000;text-decoration-color:#000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal'/></g></svg>",
+ "powerpoint": "<svg viewBox='0 0 24 24'><path fill='#e64a19' d='M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2m7 1.5V9h5.5zM8 11v2h1v6H8v1h4v-1h-1v-2h2a3 3 0 0 0 3-3 3 3 0 0 0-3-3zm5 2a1 1 0 0 1 1 1 1 1 0 0 1-1 1h-2v-2z'/></svg>",
+ "powershell": "<svg viewBox='0 0 32 32'><path fill='#03a9f4' d='M29.07 6H7.677A1.535 1.535 0 0 0 6.24 7.113l-4.2 17.774A.852.852 0 0 0 2.93 26h21.393a1.535 1.535 0 0 0 1.436-1.113L29.96 7.112A.852.852 0 0 0 29.07 6M8.626 23.797a1.4 1.4 0 0 1-1.814-.31l-.007-.009a1.075 1.075 0 0 1 .315-1.599l9.6-6.061-6.102-5.852-.01-.01a1.068 1.068 0 0 1 .084-1.625l.037-.03a1.38 1.38 0 0 1 1.8.07l7.233 6.957a1.1 1.1 0 0 1 .236.739 1.08 1.08 0 0 1-.412.79c-.074.04-.146.119-10.951 6.935ZM24 22.94A1.135 1.135 0 0 1 22.803 24h-5.634a1.061 1.061 0 1 1 .001-2.112h5.633A1.134 1.134 0 0 1 24 22.938Z'/></svg>",
+ "pre-commit": "<svg viewBox='0 0 2000 2000'><defs><clipPath id='a' clipPathUnits='userSpaceOnUse'><path d='M0 1500h1500V0H0z'/></clipPath></defs><g clip-path='url(#a)' transform='matrix(1.33333 0 0 -1.33333 0 2000)'><path fill='#ffb74d' d='M665.147 130.852 130.853 665.147c-46.863 46.862-46.863 122.842 0 169.705l534.294 534.295c46.863 46.864 122.843 46.864 169.706 0l534.294-534.294c46.863-46.863 46.863-122.843 0-169.706L834.853 130.852c-46.863-46.862-122.843-46.862-169.706 0'/><path fill='none' stroke='#212121' stroke-miterlimit='10' stroke-width='34' d='M687.774 233.226 233.225 687.775c-34.366 34.366-34.366 90.085 0 124.45l454.55 454.55c34.365 34.366 90.084 34.366 124.45 0l454.55-454.55c34.365-34.365 34.365-90.084 0-124.45l-454.55-454.55c-34.366-34.365-90.085-34.365-124.45 0z'/><path fill='#212121' d='M784.672 763.286c12.096 0 23.74.893 34.943 2.688 11.194 1.785 21.053 5.26 29.569 10.416 8.504 5.145 15.34 12.432 20.496 21.84 5.144 9.408 7.726 21.724 7.726 36.96 0 15.225-2.582 27.552-7.726 36.96-5.156 9.408-11.992 16.684-20.496 21.84-8.516 5.145-18.375 8.62-29.57 10.416-11.202 1.785-22.846 2.688-34.942 2.688h-81.985V763.286zm28.895 225.792q45.013 0 76.609-13.104c21.05-8.736 38.187-20.275 51.406-34.608 13.209-14.343 22.85-30.692 28.897-49.056 6.048-18.375 9.072-37.412 9.072-57.12 0-19.268-3.024-38.2-9.072-56.784-6.047-18.596-15.688-35.06-28.897-49.392-13.22-14.343-30.355-25.872-51.406-34.608q-31.596-13.104-76.61-13.104h-110.88V509.27H597.184v479.808z'/></g></svg>",
+ "prettier": "<svg viewBox='0 0 16 16'><path fill='#f44336' d='M2 8h4v1H2zm0 6h4v1H2zm9-10h3v1h-3zM2 2h3v1H2z'/><path fill='#f9a825' d='M9 2h3v1H9zm1 4h4v1h-4zm-5 6h1v1H5zm-3-2h6v1H2z'/><path fill='#26a69a' d='M2 12h3v1H2zm7-4h5v1H9zM2 4h4v1H2zm3-2h4v1H5z'/><path fill='#ba68c8' d='M2 6h3v1H2zm7-2h2v1H9zm-1 6h4v1H8z'/></svg>",
+ "prisma": "<svg viewBox='0 0 32 32'><path fill='#00bfa5' d='m27.777 22.617-.459-.946L18.43 3.26a2.25 2.25 0 0 0-1.914-1.256A2 2 0 0 0 16.379 2a2.23 2.23 0 0 0-1.891 1.042L4.348 19.056a2.2 2.2 0 0 0 .025 2.417l4.957 7.488A2.34 2.34 0 0 0 11.29 30a2.4 2.4 0 0 0 .655-.092l14.387-4.149a2.32 2.32 0 0 0 1.458-1.234 2.21 2.21 0 0 0-.013-1.908m-3.538.604-11.268 3.25 4.075-19.033 7.568 15.671-.376.098Z'/></svg>",
+ "processing": "<svg fill='none' viewBox='0 0 32 32'><path stroke='#536dfe' stroke-width='8' d='M10 26c16 0 16-20 0-20'/></svg>",
+ "prolog": "<svg viewBox='0 0 24 24'><path fill='#ef5350' d='M12 15.385a5.1 5.1 0 0 0 1.862 1.693L12 18.94l-1.862-1.862A5.04 5.04 0 0 0 12 15.385m4.232-4.063a1.693 1.693 0 0 0-1.693 1.693 1.693 1.693 0 0 0 1.693 1.693 1.693 1.693 0 0 0 1.693-1.693c0-.94-.762-1.693-1.693-1.693m-8.464 0a1.693 1.693 0 0 0-1.693 1.693 1.693 1.693 0 0 0 1.693 1.693 1.693 1.693 0 0 0 1.693-1.693c0-.94-.762-1.693-1.693-1.693m8.464-2.116a3.385 3.385 0 0 1 3.385 3.386 3.385 3.385 0 0 1-3.385 3.385 3.385 3.385 0 0 1-3.386-3.385 3.385 3.385 0 0 1 3.386-3.386m-8.464 0a3.385 3.385 0 0 1 3.386 3.386 3.385 3.385 0 0 1-3.386 3.385 3.385 3.385 0 0 1-3.385-3.385 3.385 3.385 0 0 1 3.385-3.386M3.74 2.69c1.49 3.132.415 5.468-.584 7.787a5.1 5.1 0 0 0-.465 2.116 5.08 5.08 0 0 0 5.078 5.078 6 6 0 0 0 .533-.042l2.506 2.505L12 21.31l1.194-1.177 2.505-2.505c.178.025.355.034.533.042a5.08 5.08 0 0 0 5.078-5.078 5.1 5.1 0 0 0-.465-2.116c-.999-2.319-2.074-4.655-.584-7.787-2.235 1.744-5.417 3.123-8.26 3.132-2.845-.008-6.027-1.388-8.261-3.132z'/></svg>",
+ "proto": "<svg viewBox='0 0 32 32'><path fill='#00bfa5' d='M16 27 2 19v-5l14 8z'/><path fill='#ffeb3b' d='m30 14-14 8v5l14-8z'/><path fill='#ff5722' d='M16 6 2 14v5l14-8z'/><path fill='#00e676' d='m30 19-14-8V6l14 8z'/><path fill='#03a9f4' d='M16 27 2 19v-5l14 8z'/></svg>",
+ "protractor": "<svg viewBox='0 0 80 80'><defs><clipPath id='a'><path fill='#424242' d='M-2.983 69.251h69.412V2.143H-2.983z'/></clipPath></defs><g clip-path='url(#a)' transform='matrix(1.1304 0 0 -1.1304 5.714 82.137)'><g stroke-width='.1'><path fill='#e53935' d='M61.216 37.276C61.216 20.217 47.39 6.39 30.33 6.39S-.554 20.218-.554 37.276 13.27 68.161 30.33 68.161c17.059 0 30.885-13.827 30.885-30.885'/><path fill='#d32f2f' d='m46.274 52.172-10.504.096-6.142 6.142-7.21-4.789 1.245-1.243-2.92.026-9.913-16.682H6.94l2.44-2.44-2.458-4.137L29.67 6.398q.33-.007.66-.008c17.042 0 30.858 13.806 30.885 30.841L46.273 52.173'/><path fill='#f5f5f5' d='M15.114 35.02c0 8.404 6.813 15.214 15.217 15.214s15.214-6.81 15.214-15.215zm34.206-.702v1.404h4.401a23.3 23.3 0 0 1-6.353 15.342l-3.289-3.29-.992.995 3.287 3.289a23.3 23.3 0 0 1-15.34 6.352l-.002-4.4h-1.404v4.4a23.3 23.3 0 0 1-15.34-6.352l3.288-3.29-.995-.991-3.288 3.287A23.32 23.32 0 0 1 6.94 35.722h4.4V34.32H6.921v-5.151H53.74v5.15h-4.42'/></g></g></svg>",
+ "pug": "<svg viewBox='0 0 128 128'><g transform='translate(-.25 -1.71)'><path fill='#ffe0b2' d='M107.4 50.9c-.2-4.4.4-8.3-1.6-11.6-4.8-8.2-16.8-13-40.8-13v.7h-.5.5v-.7c-24 0-36.6 4.8-41.4 13.1-1.9 3.4-1.7 7.2-2 11.6-.2 3.5-1.8 7.2-1.1 11.2.8 5.2 1.1 10.4 1.9 15.2.6 3.9 6 7.2 6.5 10.9 1.4 10.2 12 14.9 36 14.9v.8h-.6.7v-.8c24 0 34.2-4.7 35.5-14.9.5-3.8 5.5-7 6.1-10.9.8-4.8 1.1-10 1.9-15.2.7-4-.9-7.8-1.1-11.3'/><path fill='#ffe0b2' d='M64.6 54.5c4.3.1 7.3 2.8 10.1 5.3 3.3 2.9 8.9 4.9 11.2 7.4s5.3 5 6.4 8.9 1.4 8.9 1.4 10.2.7 1 2.7 0c4.7-2.3 9.9-8.5 9.9-8.5-.6 3.9-5.7 7.4-6.2 11.1C98.9 99.1 89 104 64.5 104h-.1.6'/><path fill='#ffe0b2' d='M80.4 46.7c.9 3.1 4.1 13.6-2.1 10.1 0 0 2.6 1.5 4.2 7.2 1.7 5.7 5.8 6.4 5.8 6.4s6.7 1.3 11.7-3c4.2-3.6 4.9-10 3.1-14.9-1.8-4.8-5-6.3-9.7-7.3-4.7-1.1-14.1-2-13 1.5'/><circle cx='92.3' cy='58.1' r='8.8'/><circle cx='90' cy='54.2' r='2.3' fill='#fafafa'/><path fill='#ffe0b2' d='M78.9 57.7s7.9 5.4 12.2 10.7 4.2 6.3 4.2 6.3l-3.1 1.4s-4.4-8.3-9.8-11.4c-5.5-3.1-6.1-5.7-6.1-5.7zm-14-3.2c-4.3.1-7.5 2.8-10.4 5.3-3.3 2.9-9.1 4.9-11.4 7.4s-5.4 5-6.5 8.9-1.5 8.9-1.5 10.2.2 1.4-2.7 0c-4.7-2.2-9.9-8.5-9.9-8.5.6 3.9 5.7 7.4 6.2 11.1C30.1 99.1 40 104 64.5 104h.5'/><path fill='#4e342e' d='M88.1 71.4C83.3 65.5 75.6 60 64.9 60h-.1c-10.7 0-18.4 5.5-23.2 11.4-5 6.1-4.6 8.5-4.6 14.3 0 21 7.4 15 12.3 17.6 5 2.5 10.2 1.7 15.5 1.7h.1c5.4 0 10.5.7 15.5-1.8 4.9-2.5 12.3 3.7 12.3-17.3.1-5.8.4-8.4-4.6-14.5'/><path fill='#3e2723' d='M64.4 65.2s-.7 9.7-2.1 11.6l2.6-.6z'/><path fill='#3e2723' d='M65.1 65.2s.7 9.7 2.1 11.6l-2.6-.6z'/><path fill='#4e342e' d='M56.7 62.9c-1-2.3 2.6-6 8.3-6.1 5.7 0 9.3 3.7 8.3 6.1S68.7 66 65 66.1c-3.6-.1-7.3-.8-8.3-3.2'/><path d='M65 65.2c0-.4 3.4-.5 5.2-1.7 0 0-3.7 1.2-4.5.7-.8-.4-1-1.6-1-1.6s-.3 1.2-.9 1.6c-.7.4-4.9-.7-4.9-.7s5.6 1.4 5.6 1.7-.1 1.3-.1 2c0 2.5 0 8.7.4 9.2.6.9.4-6.7.4-9.2-.1-.8-.1-1.6-.2-2'/><path fill='#795548' d='M65.2 78.6c1.7 0 4.7 1.2 7.4 3.1-2.6-2.9-5.7-4.9-7.4-4.9-1.8 0-5.6 2.2-8.3 5.4 2.8-2.2 6.4-3.6 8.3-3.6'/><g fill='#3e2723'><path d='M64.5 96.3c-3.8 0-7.5-1.2-10.9-2.1-.7-.2-1.4.3-2.1.1-6.3-2-11.4-5.4-14.5-9.7v1c0 21 7.4 15.1 12.3 17.6 5 2.5 10.2 1.7 15.5 1.7h.1c5.4 0 10.5.7 15.5-1.8 4.9-2.5 12.3 3.6 12.3-17.4 0-.8 0-1.6.1-2.3-2.9 4.7-8.2 8.4-14.8 10.6-.6.2-2-.3-2.6-.2-3.6 1.2-6.8 2.5-10.9 2.5'/><path d='M55 85s-2.5 7.5-.8 10.8l-2.3-1s1.7-7.6 3.1-9.8m19.8 0s2.5 7.5.8 10.8l2.3-1s-1.8-7.6-3.1-9.8'/></g><path fill='#ffe0b2' d='M48.6 46.7c-.9 3.1-4.1 13.6 2.1 10.1 0 0-2.6 1.5-4.2 7.2s-5.8 6.4-5.8 6.4-6.7 1.3-11.7-3c-4.2-3.6-4.9-10-3.1-14.9s5-6.3 9.7-7.3c4.7-1.1 14-2 13 1.5'/><path d='M64.9 76.8c2.7 0 11.1 5.8 11.2 12.9v-.4c0-7.4-6.8-13.3-11.2-13.3s-11.2 6-11.2 13.3v.4c.1-7.1 8.5-12.9 11.2-12.9'/><g fill='#3e2723'><ellipse cx='66.7' cy='61.5' rx='.8' ry='1.5' transform='rotate(-14.465 66.71 61.469)'/><ellipse cx='62.4' cy='61.5' rx='.8' ry='1.5' transform='rotate(17.235 62.372 61.463)'/></g><circle cx='37.2' cy='58.1' r='8.8'/><circle cx='39.5' cy='54.2' r='2.3' fill='#fafafa'/><path fill='#795548' d='M67.5 58.2c0-.1-2.3 1-2.9 1.1-.6-.1-2.9-1.2-2.9-1.1z'/><path fill='#ffe0b2' d='M50 57.7s-7.9 5.4-12.2 10.7-4.2 6.3-4.2 6.3l3.1 1.4s4.4-8.3 9.8-11.4 6.1-5.7 6.1-5.7z'/><path fill='#ffe0b2' d='M32.7 41.7S30 49.1 24 52.2c0 0 9.4-1.1 8.7-10.5m63.1 0s2.7 7.4 8.7 10.5c0 0-9.4-1.1-8.7-10.5M78.7 55.5s-5.9-6.2-13.8-6.4h.2c-8 .2-13.8 6.4-13.8 6.4 6.9-4.8 12.8-4.7 13.8-4.7-.1 0 6.7-.1 13.6 4.7m-6.9-13s-3-4.2-7-4.3h.2c-3 .1-6.9 4.3-6.9 4.3 3.4-3.3 6.9-3.2 6.9-3.2s3.3-.1 6.8 3.2M37.2 73.2s-4.7 2.3-8.1.9H29c-3-1.7-4.5-6.8-4.5-6.8s3 9 12.7 5.9m54.8 0s4.7 2.3 8.1.9c4-1.7 4.6-6.8 4.6-6.8s-3 9-12.7 5.9'/><path fill='#ffe0b2' d='M42.6 41.2c2.6-.5 6.9-.6 10.3.5 4.3 1.5.8 7 1.7 7.3s2.1-3.8 10.1-3.4c8.1.4 9 4 10.1 3.4s-1.1-10 11-7.8c0 0-12.7-3.4-12.1 5.8 0 0-7.3-5.6-17.5-.6.1 0 2.7-8.6-13.6-5.2m44.3 0c.2 0 .3.1.4.1s-.1-.1-.4-.1M39.1 28.9S28.3 42.5 26.7 47.7c-1.6 5.3-2.8 27-4.2 30.1l-5-21.4 9.2-22.3zm50.8 0s10.8 13.6 12.4 18.8c1.6 5.3 2.8 27 4.2 30.1l5-21.4-9.2-22.3z'/><path fill='#4e342e' d='M89.4 28.9s11.6 9.7 15 20.9 2 24.8 4.6 26.5c3.7 2.4 7.9-11.9 9.3-13.4 2.2-2.4 9.5-8.5 10-9.6s-14.8-17.8-21.5-21.1c-8.1-3.8-18.1-4.1-17.4-3.3'/><path fill='#3e2723' d='M99.3 34.9s13.7 17.5 13.5 39.3l5.5-11.2c-.1 0-4.9-14.3-19-28.1'/><path fill='#4e342e' d='M39.1 28.9s-11.6 9.7-15 20.9-2 24.8-4.6 26.5c-3.7 2.4-7.9-11.9-9.3-13.4C8 60.5.7 54.4.2 53.3S15 35.5 21.7 32.2c8.1-3.8 18.1-4.1 17.4-3.3'/><path fill='#3e2723' d='M29.2 34.9S15.5 52.4 15.7 74.2L10.3 63s4.8-14.3 18.9-28.1'/><path fill='#ffe0b2' d='M21.8 74.6s1 5.4 2.6 7.1.5-1.3.5-1.3-1.7-.9-1.4-7.8-1.7 2-1.7 2m85.3 0s-1 5.4-2.6 7.1-.5-1.3-.5-1.3 1.7-.9 1.4-7.8 1.7 2 1.7 2'/><g fill='#3e2723'><circle cx='54.5' cy='70.5' r='.8'/><circle cx='49.9' cy='75.3' r='.8'/><circle cx='48.4' cy='70.5' r='.8'/></g><g fill='#3e2723'><circle cx='74' cy='70.5' r='.8'/><circle cx='78.6' cy='75.3' r='.8'/><circle cx='80.1' cy='70.5' r='.8'/></g></g></svg>",
+ "puppet": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='M6 2h8v8H6zm12 10h8v8h-8zM6 22h8v8H6z'/><path fill='#fbc02d' d='m7.888 6.192 1.92-2.305 14.304 11.921-1.92 2.305z'/><path fill='#fbc02d' d='m7.888 25.808 14.303-11.92 1.921 2.304-14.303 11.92z'/></svg>",
+ "puppeteer": "<svg viewBox='0 0 24 24'><path fill='#00bfa5' d='M2.822 19.073q-.159 0-.159-.292l-.013-2.138q0-.16.013-.173.066-.2 3.307-2.298 3.24-2.085 3.254-2.152-.173-.239-3.413-2.35-3.148-2.033-3.148-2.232V5.233q0-.305.213-.305h1.912q.252 0 3.56 2.497 3.32 2.497 3.586 2.497.292 0 3.639-2.484t3.546-2.484h2.045q.186 0 .186.36 0 .37-.026 1.115t-.027 1.115q0 .067-.61.492-1.966 1.248-5.871 3.772-.093.066-.2.226.094.186 3.414 2.297 3.267 2.085 3.267 2.271v2.099q0 .345-.186.345h-2.045q-.332 0-3.626-2.484-3.294-2.47-3.466-2.47-.213 0-3.613 2.484-3.387 2.497-3.626 2.497z' aria-label='X'/></svg>",
+ "purescript": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='m31.447 12.569-6.035-6.038-1.627 1.628 5.22 5.223-5.22 5.223 1.627 1.628 6.035-6.036a1.15 1.15 0 0 0 0-1.628M8.216 13.928l-1.628-1.629L.55 18.335a1.155 1.155 0 0 0 0 1.628L6.588 26l1.628-1.628-5.223-5.222ZM10 22h12l2 2H12z'/><path fill='#42a5f5' d='M22 18H10l2-2h12zm0-6H10l-2-2h12z'/></svg>",
+ "python-misc": "<svg viewBox='0 0 32 32'><path fill='#757575' d='M15 2H6a2.006 2.006 0 0 0-2 2v22a2.006 2.006 0 0 0 2 2h16a2 2 0 0 0 2-2V11Zm3 22H6v-2h12Zm0-4H6v-2h12Zm0-4H6v-2h12Zm-4-4V4l8 8Z'/><path fill='#fbc02d' d='M30.714 16H28v5h-9v7.714A1.286 1.286 0 0 0 20.286 30h6.428A1.286 1.286 0 0 0 28 28.714V26h-6v-1h8.714A1.286 1.286 0 0 0 32 23.714v-6.428A1.286 1.286 0 0 0 30.714 16M24 28h3v1h-3Z' style='isolation:isolate'/><path fill='#0288d1' d='M25.714 12h-6.428A1.286 1.286 0 0 0 18 13.286V16h6v1h-8.714A1.286 1.286 0 0 0 14 18.286v6.428A1.286 1.286 0 0 0 15.286 26H18v-6h9v-6.714A1.286 1.286 0 0 0 25.714 12M22 14h-3v-1h3Z' style='isolation:isolate'/></svg>",
+ "python": "<svg viewBox='0 0 24 24'><path fill='#0288d1' d='M9.86 2A2.86 2.86 0 0 0 7 4.86v1.68h4.29c.39 0 .71.57.71.96H4.86A2.86 2.86 0 0 0 2 10.36v3.781a2.86 2.86 0 0 0 2.86 2.86h1.18v-2.68a2.85 2.85 0 0 1 2.85-2.86h5.25c1.58 0 2.86-1.271 2.86-2.851V4.86A2.86 2.86 0 0 0 14.14 2zm-.72 1.61c.4 0 .72.12.72.71s-.32.891-.72.891c-.39 0-.71-.3-.71-.89s.32-.711.71-.711'/><path fill='#fdd835' d='M17.959 7v2.68a2.85 2.85 0 0 1-2.85 2.859H9.86A2.85 2.85 0 0 0 7 15.389v3.75a2.86 2.86 0 0 0 2.86 2.86h4.28A2.86 2.86 0 0 0 17 19.14v-1.68h-4.291c-.39 0-.709-.57-.709-.96h7.14A2.86 2.86 0 0 0 22 13.64V9.86A2.86 2.86 0 0 0 19.14 7zM8.32 11.513l-.004.004.038-.004zm6.54 7.276c.39 0 .71.3.71.89a.71.71 0 0 1-.71.71c-.4 0-.72-.12-.72-.71s.32-.89.72-.89'/></svg>",
+ "pytorch": "<svg viewBox='0 0 32 32'><circle cx='20' cy='8' r='2' fill='#f4511e'/><path fill='#f4511e' d='M25.573 11.335a11.4 11.4 0 0 0-1.1-1.219l-2.825 2.832a9 9 0 0 1 .746.812 7.36 7.36 0 0 1 1.443 3.011 8 8 0 0 1 .164 1.23A8 8 0 0 1 8 18a5.76 5.76 0 0 1 .695-2.762 7.4 7.4 0 0 1 1.277-1.896c.18-.18 1.814-1.746 3.74-3.584L16 7.553V2l-5.057 4.873c-1.112 1.06-3.713 3.545-3.877 3.722a11.5 11.5 0 0 0-1.99 2.942A9.8 9.8 0 0 0 4 18a12 12 0 1 0 24 0 12.6 12.6 0 0 0-.254-2.074 11.26 11.26 0 0 0-2.173-4.591'/></svg>",
+ "qsharp": "<svg viewBox='0 0 24 24'><path fill='#fbc02d' d='M11.938 16.414c.9-1.101 1.468-2.936 1.468-4.735 0-1.963-.697-3.854-1.872-5.12-1.156-1.248-2.66-1.853-4.57-1.853s-3.413.605-4.569 1.853C1.22 7.825.523 9.716.523 11.716s.697 3.89 1.872 5.157c1.156 1.248 2.68 1.853 4.57 1.853 1.376 0 2.404-.275 3.468-.917l1.578 1.486 1.395-1.486zm-3.395-3.212-1.396 1.487 1.413 1.34c-.422.22-1.027.348-1.615.348-2.202 0-3.67-1.853-3.67-4.66 0-2.809 1.468-4.662 3.689-4.662 2.239 0 3.689 1.835 3.689 4.68 0 1.1-.202 2.092-.606 2.9z' aria-label='Q'/><path fill='#fbc02d' d='m17.589 4.728-.611 4h-1.5l-.34 2h1.5l-.32 2h-1.5l-.34 2h1.5l-.61 4h2l.61-4h1l-.61 4h2l.61-4h1.5l.34-2h-1.5l.32-2h1.5l.34-2h-1.5l.611-4h-2l-.611 4h-1l.611-4zm1.049 6h1l-.32 2h-1z'/></svg>",
+ "quasar": "<svg viewBox='0 0 50.843 50.843'><path fill='#1976d2' d='M29.585 25.422a4.16 4.16 0 0 1-4.16 4.159 4.16 4.16 0 0 1-4.159-4.16 4.16 4.16 0 0 1 4.16-4.159 4.16 4.16 0 0 1 4.159 4.16M43.888 14.76a21.3 21.3 0 0 0-3.267-4.272l-4.808 2.776a16 16 0 0 0-5.02-2.91c-1.642 1.664-2.946 3.523-3.886 5.545 5.352-.364 10.88 1.573 16.012 5.582l3.026-1.747a21.3 21.3 0 0 0-2.057-4.974m.001 21.32a21.3 21.3 0 0 0 2.066-4.966l-4.808-2.776c.359-1.938.356-3.904.01-5.803-2.262-.59-4.524-.79-6.745-.593 2.992 4.454 4.078 10.21 3.172 16.659l3.026 1.747a21.3 21.3 0 0 0 3.28-4.269zM25.427 46.74a21.3 21.3 0 0 0 5.333-.694v-5.552a16 16 0 0 0 5.03-2.893c-.62-2.253-1.578-4.312-2.859-6.137-2.36 4.817-6.802 8.636-12.84 11.076v3.494a21.3 21.3 0 0 0 5.336.706M6.963 36.082a21.3 21.3 0 0 0 3.267 4.272l4.809-2.777a16 16 0 0 0 5.02 2.91c1.642-1.664 2.946-3.523 3.886-5.544-5.353.364-10.88-1.573-16.012-5.583l-3.027 1.747a21.3 21.3 0 0 0 2.057 4.975m-.001-21.32a21.3 21.3 0 0 0-2.066 4.966l4.809 2.776a16 16 0 0 0-.01 5.802c2.262.59 4.524.79 6.744.593-2.991-4.453-4.078-10.209-3.171-16.658l-3.026-1.747a21.3 21.3 0 0 0-3.28 4.269zm18.462-10.66a21.3 21.3 0 0 0-5.333.694v5.552a16 16 0 0 0-5.03 2.893c.62 2.253 1.578 4.312 2.86 6.137 2.36-4.818 6.802-8.636 12.84-11.076V4.808a21.3 21.3 0 0 0-5.337-.706'/></svg>",
+ "quokka": "<svg viewBox='0 0 16 16'><path fill='#ff6d00' d='M8 2v6H2v6h12V2z' paint-order='fill markers stroke'/></svg>",
+ "qwik": "<svg viewBox='0 0 24 24'><path fill='#29b6f6' d='m19.26 22.182-3.657-3.636-.056.008v-.04l-7.776-7.678 1.916-1.85-1.126-6.46-5.341 6.62c-.91.917-1.078 2.408-.423 3.508l3.337 5.534a2.8 2.8 0 0 0 2.435 1.356l1.653-.016z'/><path fill='#b388ff' d='m21.255 9.018-.734-1.356-.383-.693-.152-.272-.016.016-2.012-3.484a2.82 2.82 0 0 0-2.467-1.411l-1.765.047-5.261.016A2.82 2.82 0 0 0 6.054 3.27L2.852 9.616 8.577 2.51l7.505 8.245-1.334 1.347.799 6.451.008-.016v.016h-.016l.016.016.623.606 3.025 2.958c.128.12.336-.024.248-.175l-1.868-3.676 3.257-6.02.104-.12c.04-.048.08-.096.112-.143.638-.87.726-2.034.2-2.983z'/><path fill='#eceff1' d='M16.106 10.724 8.576 2.52l1.07 6.427-1.916 1.858 7.8 7.742-.702-6.426z'/></svg>",
+ "r": "<svg viewBox='0 0 24 24'><path fill='#1976d2' d='M11.956 4.05c-5.694 0-10.354 3.106-10.354 6.947 0 3.396 3.686 6.212 8.531 6.813v2.205h3.53V17.82c.88-.093 1.699-.259 2.475-.497l1.43 2.692h3.996l-2.402-4.048c1.936-1.263 3.147-3.034 3.147-4.97 0-3.841-4.659-6.947-10.354-6.947m1.584 2.712c4.349 0 7.558 1.45 7.558 4.753 0 1.77-.952 3.013-2.505 3.779a1 1 0 0 1-.228-.156c-.373-.165-.994-.352-.994-.352s3.085-.227 3.085-3.302-3.23-3.127-3.23-3.127h-7.092v7.413c-2.64-.766-4.462-2.392-4.462-4.255 0-2.63 3.52-4.753 7.868-4.753m.156 4.12h2.143s.983-.05.983.974c0 1.004-.983 1.004-.983 1.004h-2.143v-1.977m-.031 4.566h.952c.186 0 .28.052.445.207.135.103.28.3.404.476-.57.073-1.17.104-1.801.104z'/></svg>",
+ "racket": "<svg viewBox='0 0 511.875 511.824'><path fill='#0288d1' d='M416.17 381.6c27.189-34.614 43.405-78.256 43.405-125.685 0-112.466-91.168-203.634-203.634-203.634-24.464 0-47.92 4.319-69.65 12.228 82.671 43.366 192.023 184.854 229.88 317.097z'/><path fill='#e53935' d='M226.77 182.174c-31.766-34.221-67.34-61.4-105.015-79.425-42.569 37.324-69.453 92.102-69.453 153.162 0 51.344 19.009 98.24 50.365 134.06 27.641-83.049 79.607-163.09 124.108-207.8zm37.526 46.176c-44.085 47.512-88.014 130.681-103.913 207.415 28.498 15.172 61.02 23.783 95.561 23.783 35.508 0 68.888-9.097 97.951-25.074-16.752-77.405-48.375-148.294-89.591-206.127z'/></svg>",
+ "raml": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='M6 18.087a2 2 0 0 0-2-2 2 2 0 0 0 2-2v-6h4v-4H6a2 2 0 0 0-2 2v8H2v4h1.5a.5.5 0 0 1 .5.5v7.5a2 2 0 0 0 2 2h4v-4H6Zm22-3.999v-8a2 2 0 0 0-2-2h-4v4h4v6a2 2 0 0 0 2 2 2 2 0 0 0-2 2v6h-4v4h4a2 2 0 0 0 2-2v-8h2v-4Z'/><rect width='4' height='4' x='10' y='14.088' fill='#42a5f5' rx='.5'/><rect width='4' height='4' x='18' y='14.088' fill='#42a5f5' rx='.5'/></svg>",
+ "razor": "<svg viewBox='0 0 24 24'><path fill='#42a5f5' d='M12 15c.81 0 1.5-.3 2.11-.89.59-.61.89-1.3.89-2.11s-.3-1.5-.89-2.11C13.5 9.3 12.81 9 12 9s-1.5.3-2.11.89C9.3 10.5 9 11.19 9 12s.3 1.5.89 2.11c.61.59 1.3.89 2.11.89m0-13c2.75 0 5.1 1 7.05 2.95S22 9.25 22 12v1.45c0 1-.35 1.85-1 2.55-.7.67-1.5 1-2.5 1-1.2 0-2.19-.5-2.94-1.5-1 1-2.18 1.5-3.56 1.5-1.37 0-2.55-.5-3.54-1.46C7.5 14.55 7 13.38 7 12c0-1.37.5-2.55 1.46-3.54C9.45 7.5 10.63 7 12 7c1.38 0 2.55.5 3.54 1.46C16.5 9.45 17 10.63 17 12v1.45c0 .41.16.77.46 1.08s.65.47 1.04.47c.42 0 .77-.16 1.07-.47s.43-.67.43-1.08V12c0-2.19-.77-4.07-2.35-5.65S14.19 4 12 4s-4.07.77-5.65 2.35S4 9.81 4 12s.77 4.07 2.35 5.65S9.81 20 12 20h5v2h-5c-2.75 0-5.1-1-7.05-2.95S2 14.75 2 12s1-5.1 2.95-7.05S9.25 2 12 2'/></svg>",
+ "rbxmk": "<svg fill='none' viewBox='0 0 24 24'><path fill='#00c853' fill-rule='evenodd' d='m6.331 2 16.164 4.331-2.454 9.158-2.25-.603.94-3.508-.002-.001-1.35-.362-2.129-.57-1.354-.363-.364 1.356-.577 2.153-2.125-.57.94-3.509-1.354-.362-2.128-.57-1.35-.363-.94 3.507-2.118-.568-.364 1.357 2.118.568 1.35.362h.001l.94-3.509 2.128.57-.577 2.155-.363 1.354 1.354.363 2.125.57 1.355.363.94-3.507 2.13.57-.578 2.152-.363 1.355 1.353.362 2.249.603-1.514 5.65L2 18.165z' clip-rule='evenodd'/></svg>",
+ "rc": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M4 28V8H2v21.5a.5.5 0 0 0 .5.5H24v-2Z'/><path fill='#0288d1' d='M29.5 2h-21a.5.5 0 0 0-.5.5v21a.5.5 0 0 0 .5.5h21a.5.5 0 0 0 .5-.5v-21a.5.5 0 0 0-.5-.5M26 8.04a3.4 3.4 0 0 0-.56-.04H21a5 5 0 0 0 0 10h4.44a3.4 3.4 0 0 0 .56-.04V22h-5a9 9 0 0 1 0-18h5Z'/></svg>",
+ "react": "<svg viewBox='0 0 32 32'><path fill='#00bcd4' d='M16 12c7.444 0 12 2.59 12 4s-4.556 4-12 4-12-2.59-12-4 4.556-4 12-4m0-2c-7.732 0-14 2.686-14 6s6.268 6 14 6 14-2.686 14-6-6.268-6-14-6'/><path fill='#00bcd4' d='M16 14a2 2 0 1 0 2 2 2 2 0 0 0-2-2'/><path fill='#00bcd4' d='M10.458 5.507c2.017 0 5.937 3.177 9.006 8.493 3.722 6.447 3.757 11.687 2.536 12.392a.9.9 0 0 1-.457.1c-2.017 0-5.938-3.176-9.007-8.492C8.814 11.553 8.779 6.313 10 5.608a.9.9 0 0 1 .458-.1m-.001-2A2.87 2.87 0 0 0 9 3.875C6.13 5.532 6.938 12.304 10.804 19c3.284 5.69 7.72 9.493 10.74 9.493A2.87 2.87 0 0 0 23 28.124c2.87-1.656 2.062-8.428-1.804-15.124-3.284-5.69-7.72-9.493-10.74-9.493Z'/><path fill='#00bcd4' d='M21.543 5.507a.9.9 0 0 1 .457.1c1.221.706 1.186 5.946-2.536 12.393-3.07 5.316-6.99 8.493-9.007 8.493a.9.9 0 0 1-.457-.1C8.779 25.686 8.814 20.446 12.536 14c3.07-5.316 6.99-8.493 9.007-8.493m0-2c-3.02 0-7.455 3.804-10.74 9.493C6.939 19.696 6.13 26.468 9 28.124a2.87 2.87 0 0 0 1.457.369c3.02 0 7.455-3.804 10.74-9.493C25.061 12.304 25.87 5.532 23 3.876a2.87 2.87 0 0 0-1.457-.369'/></svg>",
+ "react_ts": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M16 12c7.444 0 12 2.59 12 4s-4.556 4-12 4-12-2.59-12-4 4.556-4 12-4m0-2c-7.732 0-14 2.686-14 6s6.268 6 14 6 14-2.686 14-6-6.268-6-14-6'/><path fill='#0288d1' d='M16 14a2 2 0 1 0 2 2 2 2 0 0 0-2-2'/><path fill='#0288d1' d='M10.458 5.507c2.017 0 5.937 3.177 9.006 8.493 3.722 6.447 3.757 11.687 2.536 12.392a.9.9 0 0 1-.457.1c-2.017 0-5.938-3.176-9.007-8.492C8.814 11.553 8.779 6.313 10 5.608a.9.9 0 0 1 .458-.1m-.001-2A2.87 2.87 0 0 0 9 3.875C6.13 5.532 6.938 12.304 10.804 19c3.284 5.69 7.72 9.493 10.74 9.493A2.87 2.87 0 0 0 23 28.124c2.87-1.656 2.062-8.428-1.804-15.124-3.284-5.69-7.72-9.493-10.74-9.493Z'/><path fill='#0288d1' d='M21.543 5.507a.9.9 0 0 1 .457.1c1.221.706 1.186 5.946-2.536 12.393-3.07 5.316-6.99 8.493-9.007 8.493a.9.9 0 0 1-.457-.1C8.779 25.686 8.814 20.446 12.536 14c3.07-5.316 6.99-8.493 9.007-8.493m0-2c-3.02 0-7.455 3.804-10.74 9.493C6.939 19.696 6.13 26.468 9 28.124a2.87 2.87 0 0 0 1.457.369c3.02 0 7.455-3.804 10.74-9.493C25.061 12.304 25.87 5.532 23 3.876a2.87 2.87 0 0 0-1.457-.369'/></svg>",
+ "readme": "<svg fill='none' viewBox='0 0 16 16'><path d='M0 0h24v24H0z'/><path fill='#42a5f5' d='M8 1C4.136 1 1 4.136 1 8s3.136 7 7 7 7-3.136 7-7-3.136-7-7-7m1 11H7V7.5h2zm0-6H7V4h2z'/></svg>",
+ "reason": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='M4 4v24h24V4Zm14 15.5a.5.5 0 0 1-.5.5H16v2h1a1 1 0 0 1 1 1v3h-2v-4h-4v4h-1a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v1h1.5a.5.5 0 0 1 .5.5Zm8-1.5h-4v2h4v2h-4v2h4v2h-5a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1h5Z'/><path fill='#f44336' d='M12 18h4v2h-4z'/></svg>",
+ "red": "<svg viewBox='0 0 200 200'><path fill='#fbc02d' d='M100 60.234V10.322L74.999 47.28z'/><path fill='#b71c1c' d='m100 125.001 57.34-29.893-25.002-36.958L100 75z'/><path fill='#f9a825' d='M100 10.322v49.912l25.001-12.954z'/><path fill='#b71c1c' d='M100 139.766v49.912l89.678-46.65-25.001-36.959z'/><path fill='#e53935' d='M100 139.766 35.323 106.07l-25.001 36.958L100 189.678zm0-14.765V75L67.662 58.15 42.66 95.108z'/></svg>",
+ "redux-action": "<svg stroke-linejoin='round' stroke-miterlimit='1.414' clip-rule='evenodd' viewBox='0 0 24 24'><g fill='#ab47bc' stroke='#ab47bc' stroke-miterlimit='4' stroke-width='.435'><path d='M15.878 15.787a1.437 1.437 0 0 0-.154-2.866h-.05a1.43 1.43 0 0 0-1.382 1.484c.025.384.179.717.41.947-.87 1.714-2.201 2.968-4.197 4.017-1.356.717-2.764.972-4.17.793-1.152-.153-2.048-.665-2.61-1.51-.82-1.253-.896-2.61-.205-3.965.486-.973 1.253-1.69 1.74-2.047a14 14 0 0 1-.333-1.305c-3.71 2.686-3.326 6.32-2.2 8.034.844 1.28 2.558 2.073 4.451 2.073a6.3 6.3 0 0 0 1.536-.18c3.275-.64 5.757-2.584 7.164-5.475z'/><path d='M20.381 12.614c-1.944-2.277-4.81-3.53-8.085-3.53h-.41a1.41 1.41 0 0 0-1.253-.769h-.051A1.43 1.43 0 0 0 9.2 9.8a1.437 1.437 0 0 0 1.433 1.381h.05a1.44 1.44 0 0 0 1.255-.87h.46c1.945 0 3.787.563 5.45 1.663 1.28.845 2.2 1.945 2.712 3.276.435 1.074.41 2.123-.05 3.019-.717 1.356-1.92 2.098-3.506 2.098a6.5 6.5 0 0 1-2.508-.537c-.281.256-.793.665-1.151.92 1.1.513 2.226.794 3.3.794 2.457 0 4.274-1.356 4.965-2.712.742-1.484.69-4.043-1.229-6.218z'/><path d='M7.383 16.222a1.437 1.437 0 0 0 1.433 1.382h.051a1.43 1.43 0 0 0 1.382-1.484 1.437 1.437 0 0 0-1.433-1.382h-.051c-.051 0-.128 0-.18.025-1.048-1.74-1.483-3.633-1.33-5.68.103-1.535.614-2.866 1.51-3.966.742-.947 2.175-1.407 3.147-1.433 2.712-.05 3.864 3.327 3.94 4.683.333.076.896.256 1.28.383-.307-4.145-2.866-6.294-5.322-6.294-2.303 0-4.427 1.663-5.271 4.12-1.177 3.275-.41 6.422 1.023 8.904a1.15 1.15 0 0 0-.179.742z'/></g></svg>",
+ "redux-reducer": "<svg stroke-linejoin='round' stroke-miterlimit='1.414' clip-rule='evenodd' viewBox='0 0 24 24'><g fill='#ef5350' stroke='#ef5350' stroke-miterlimit='4' stroke-width='.435'><path d='M15.878 15.787a1.437 1.437 0 0 0-.154-2.866h-.05a1.43 1.43 0 0 0-1.382 1.484c.025.384.179.717.41.947-.87 1.714-2.201 2.968-4.197 4.017-1.356.717-2.764.972-4.17.793-1.152-.153-2.048-.665-2.61-1.51-.82-1.253-.896-2.61-.205-3.965.486-.973 1.253-1.69 1.74-2.047a14 14 0 0 1-.333-1.305c-3.71 2.686-3.326 6.32-2.2 8.034.844 1.28 2.558 2.073 4.451 2.073a6.3 6.3 0 0 0 1.536-.18c3.275-.64 5.757-2.584 7.164-5.475z'/><path d='M20.381 12.614c-1.944-2.277-4.81-3.53-8.085-3.53h-.41a1.41 1.41 0 0 0-1.253-.769h-.051A1.43 1.43 0 0 0 9.2 9.8a1.437 1.437 0 0 0 1.433 1.381h.05a1.44 1.44 0 0 0 1.255-.87h.46c1.945 0 3.787.563 5.45 1.663 1.28.845 2.2 1.945 2.712 3.276.435 1.074.41 2.123-.05 3.019-.717 1.356-1.92 2.098-3.506 2.098a6.5 6.5 0 0 1-2.508-.537c-.281.256-.793.665-1.151.92 1.1.513 2.226.794 3.3.794 2.457 0 4.274-1.356 4.965-2.712.742-1.484.69-4.043-1.229-6.218z'/><path d='M7.383 16.222a1.437 1.437 0 0 0 1.433 1.382h.051a1.43 1.43 0 0 0 1.382-1.484 1.437 1.437 0 0 0-1.433-1.382h-.051c-.051 0-.128 0-.18.025-1.048-1.74-1.483-3.633-1.33-5.68.103-1.535.614-2.866 1.51-3.966.742-.947 2.175-1.407 3.147-1.433 2.712-.05 3.864 3.327 3.94 4.683.333.076.896.256 1.28.383-.307-4.145-2.866-6.294-5.322-6.294-2.303 0-4.427 1.663-5.271 4.12-1.177 3.275-.41 6.422 1.023 8.904a1.15 1.15 0 0 0-.179.742z'/></g></svg>",
+ "redux-selector": "<svg stroke-linejoin='round' stroke-miterlimit='1.414' clip-rule='evenodd' viewBox='0 0 24 24'><g fill='#ff6e40' stroke='#ff6e40' stroke-miterlimit='4' stroke-width='.435'><path d='M15.878 15.787a1.437 1.437 0 0 0-.154-2.866h-.05a1.43 1.43 0 0 0-1.382 1.484c.025.384.179.717.41.947-.87 1.714-2.201 2.968-4.197 4.017-1.356.717-2.764.972-4.17.793-1.152-.153-2.048-.665-2.61-1.51-.82-1.253-.896-2.61-.205-3.965.486-.973 1.253-1.69 1.74-2.047a14 14 0 0 1-.333-1.305c-3.71 2.686-3.326 6.32-2.2 8.034.844 1.28 2.558 2.073 4.451 2.073a6.3 6.3 0 0 0 1.536-.18c3.275-.64 5.757-2.584 7.164-5.475z'/><path d='M20.381 12.614c-1.944-2.277-4.81-3.53-8.085-3.53h-.41a1.41 1.41 0 0 0-1.253-.769h-.051A1.43 1.43 0 0 0 9.2 9.8a1.437 1.437 0 0 0 1.433 1.381h.05a1.44 1.44 0 0 0 1.255-.87h.46c1.945 0 3.787.563 5.45 1.663 1.28.845 2.2 1.945 2.712 3.276.435 1.074.41 2.123-.05 3.019-.717 1.356-1.92 2.098-3.506 2.098a6.5 6.5 0 0 1-2.508-.537c-.281.256-.793.665-1.151.92 1.1.513 2.226.794 3.3.794 2.457 0 4.274-1.356 4.965-2.712.742-1.484.69-4.043-1.229-6.218z'/><path d='M7.383 16.222a1.437 1.437 0 0 0 1.433 1.382h.051a1.43 1.43 0 0 0 1.382-1.484 1.437 1.437 0 0 0-1.433-1.382h-.051c-.051 0-.128 0-.18.025-1.048-1.74-1.483-3.633-1.33-5.68.103-1.535.614-2.866 1.51-3.966.742-.947 2.175-1.407 3.147-1.433 2.712-.05 3.864 3.327 3.94 4.683.333.076.896.256 1.28.383-.307-4.145-2.866-6.294-5.322-6.294-2.303 0-4.427 1.663-5.271 4.12-1.177 3.275-.41 6.422 1.023 8.904a1.15 1.15 0 0 0-.179.742z'/></g></svg>",
+ "redux-store": "<svg stroke-linejoin='round' stroke-miterlimit='1.414' clip-rule='evenodd' viewBox='0 0 24 24'><g fill='#8bc34a' stroke='#8bc34a' stroke-miterlimit='4' stroke-width='.435'><path d='M15.878 15.787a1.437 1.437 0 0 0-.154-2.866h-.05a1.43 1.43 0 0 0-1.382 1.484c.025.384.179.717.41.947-.87 1.714-2.201 2.968-4.197 4.017-1.356.717-2.764.972-4.17.793-1.152-.153-2.048-.665-2.61-1.51-.82-1.253-.896-2.61-.205-3.965.486-.973 1.253-1.69 1.74-2.047a14 14 0 0 1-.333-1.305c-3.71 2.686-3.326 6.32-2.2 8.034.844 1.28 2.558 2.073 4.451 2.073a6.3 6.3 0 0 0 1.536-.18c3.275-.64 5.757-2.584 7.164-5.475z'/><path d='M20.381 12.614c-1.944-2.277-4.81-3.53-8.085-3.53h-.41a1.41 1.41 0 0 0-1.253-.769h-.051A1.43 1.43 0 0 0 9.2 9.8a1.437 1.437 0 0 0 1.433 1.381h.05a1.44 1.44 0 0 0 1.255-.87h.46c1.945 0 3.787.563 5.45 1.663 1.28.845 2.2 1.945 2.712 3.276.435 1.074.41 2.123-.05 3.019-.717 1.356-1.92 2.098-3.506 2.098a6.5 6.5 0 0 1-2.508-.537c-.281.256-.793.665-1.151.92 1.1.513 2.226.794 3.3.794 2.457 0 4.274-1.356 4.965-2.712.742-1.484.69-4.043-1.229-6.218z'/><path d='M7.383 16.222a1.437 1.437 0 0 0 1.433 1.382h.051a1.43 1.43 0 0 0 1.382-1.484 1.437 1.437 0 0 0-1.433-1.382h-.051c-.051 0-.128 0-.18.025-1.048-1.74-1.483-3.633-1.33-5.68.103-1.535.614-2.866 1.51-3.966.742-.947 2.175-1.407 3.147-1.433 2.712-.05 3.864 3.327 3.94 4.683.333.076.896.256 1.28.383-.307-4.145-2.866-6.294-5.322-6.294-2.303 0-4.427 1.663-5.271 4.12-1.177 3.275-.41 6.422 1.023 8.904a1.15 1.15 0 0 0-.179.742z'/></g></svg>",
+ "regedit": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='M10 23v4a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1m-6-8v4a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1m0-8v4a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1m13 15h-4a1 1 0 0 0-1 1v4a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1m-5-7v4a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1m.41-7.632 1.637 3.65a1 1 0 0 0 1.321.503l3.65-1.637a1 1 0 0 0 .503-1.322l-1.637-3.65a1 1 0 0 0-1.322-.503l-3.65 1.638a1 1 0 0 0-.503 1.321M25 22h-4a1 1 0 0 0-1 1v4a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1m-2.256-9.318-2.195 3.344a1 1 0 0 0 .287 1.384l3.344 2.195a1 1 0 0 0 1.385-.287l2.195-3.344a1 1 0 0 0-.287-1.384l-3.344-2.195a1 1 0 0 0-1.385.287M22 5v4a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1'/></svg>",
+ "remark": "<svg viewBox='0 0 16 16'><path fill='#ef5350' d='M5.445 2.975C3.724 2.989 3.04 3.93 3 4V3H1v10h2V7c.647-.844 1.609-1.724 3-1V3a5 5 0 0 0-.555-.025M10.748 3q-1.319 0-2.352.623-1.025.616-1.588 1.766-.56 1.14-.56 2.593v.25q0 2.166 1.328 3.467Q8.913 13.001 11.033 13q1.204 0 2.193-.455.99-.464 1.569-1.283l-1.266-1.416q-.837 1.078-2.326 1.078-.963 0-1.596-.57C9.191 9.974 9.083 9.63 9 9h6V7.785q0-2.29-1.14-3.537Q12.726 3 10.747 3zm-.008 2.086q.82 0 1.266.473.444.463.463 1.318L12.48 7H9c.089-.642.177-1.054.492-1.398q.48-.517 1.248-.516'/></svg>",
+ "remix": "<svg viewBox='0 0 32 32'><path fill='#b0bec5' d='M28 12v-2a8 8 0 0 0-8-8H4v6h12.83a3.114 3.114 0 0 1 3.166 2.839A3 3 0 0 1 17 14H4v6h12a4 4 0 0 1 4 4v6h8v-5a7 7 0 0 0-7-7h1a6 6 0 0 0 6-6M12 26H4v4h10v-2a2 2 0 0 0-2-2'/></svg>",
+ "remix_light": "<svg viewBox='0 0 32 32'><path fill='#455a64' d='M28 12v-2a8 8 0 0 0-8-8H4v6h12.83a3.114 3.114 0 0 1 3.166 2.839A3 3 0 0 1 17 14H4v6h12a4 4 0 0 1 4 4v6h8v-5a7 7 0 0 0-7-7h1a6 6 0 0 0 6-6M12 26H4v4h10v-2a2 2 0 0 0-2-2'/></svg>",
+ "renovate": "<svg viewBox='0 0 24 24'><path fill='#ffb300' d='m13.061 3.722-.707-.707a1 1 0 0 0-1.414 0L2.454 11.5a1 1 0 0 0 0 1.414l2.829 2.829a1 1 0 0 0 1.414 0l8.485-8.486a1 1 0 0 0 0-1.414l-.707-.707.707-.707 2.829 2.828-7.071 7.071 7.778 7.779a1 1 0 0 0 1.414 0l1.414-1.415a1 1 0 0 0 0-1.414l-6.364-6.364 5.657-5.657L15.182 1.6z'/></svg>",
+ "replit": "<svg viewBox='0 0 32 32'><path fill='#ff6d00' d='M8 4h8v8H8a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2m8 8h8a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-8zm-8 8h8v8H8a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2'/></svg>",
+ "rescript-interface": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M16 21.862A2.14 2.14 0 0 1 13.862 24h-1.724A2.14 2.14 0 0 1 10 21.862V10.138A2.14 2.14 0 0 1 12.138 8H16ZM21 14a3 3 0 1 1 3-3 3 3 0 0 1-3 3'/><path fill='#ef5350' d='M24 4a4.005 4.005 0 0 1 4 4v16a4.005 4.005 0 0 1-4 4H8a4.005 4.005 0 0 1-4-4V8a4.005 4.005 0 0 1 4-4zm0-2H8a6 6 0 0 0-6 6v16a6 6 0 0 0 6 6h16a6 6 0 0 0 6-6V8a6 6 0 0 0-6-6'/></svg>",
+ "rescript": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M24 2H8a6 6 0 0 0-6 6v16a6 6 0 0 0 6 6h16a6 6 0 0 0 6-6V8a6 6 0 0 0-6-6m-8 19.862A2.14 2.14 0 0 1 13.862 24h-1.724A2.14 2.14 0 0 1 10 21.862V10.138A2.14 2.14 0 0 1 12.138 8H16ZM21 14a3 3 0 1 1 3-3 3 3 0 0 1-3 3'/></svg>",
+ "restql": "<svg viewBox='0 0 300 300'><path fill='#64ffda' d='M266.377 94.665c-1.327-8.361-6.357-15.836-13.806-20.138L164.349 23.59c-4.303-2.483-9.203-3.795-14.169-3.795s-9.865 1.312-14.166 3.793L47.793 74.524c-8.742 5.05-14.171 14.452-14.171 24.542v101.866c0 10.09 5.429 19.493 14.168 24.54l88.221 50.935c4.305 2.485 9.205 3.796 14.17 3.796s9.863-1.311 14.166-3.795l88.22-50.933c7.453-4.306 12.485-11.78 13.81-20.142zm-22.465 115.817-88.219 50.931a11.02 11.02 0 0 1-11.028 0l-88.219-50.931a11.03 11.03 0 0 1-5.513-9.55V99.066a11.03 11.03 0 0 1 5.513-9.55l88.219-50.934c1.705-.986 3.61-1.477 5.514-1.477s3.808.491 5.514 1.477l88.219 50.934a11.03 11.03 0 0 1 5.513 9.55v101.866a11.03 11.03 0 0 1-5.513 9.55'/><path fill='#1a237e' d='M249.429 99.066a11.03 11.03 0 0 0-5.513-9.55l-88.219-50.934c-1.705-.986-3.61-1.477-5.514-1.477s-3.809.491-5.514 1.477L56.45 89.516a11.03 11.03 0 0 0-5.513 9.55v101.866a11.03 11.03 0 0 0 5.513 9.55l88.219 50.931a11.02 11.02 0 0 0 11.028 0l88.219-50.931a11.03 11.03 0 0 0 5.513-9.55zm-63.612 43.093q.245-.14.497-.203c.06-.016.12-.013.178-.024.104-.017.207-.04.31-.04l.02.002c.028.001.052.006.08.008q.19.011.372.054.067.019.136.041.156.051.304.13.058.026.114.058c.128.077.25.166.358.27q.013.018.028.033.143.146.256.32.035.06.068.122.08.146.135.312.024.066.043.134c.038.157.067.319.067.491v22.048l-4.459 2.575a2.1 2.1 0 0 0-.922 1.129 2.1 2.1 0 0 0-.134.699v5.148l-19.094 11.023-.014.006q-.238.133-.481.198c-.061.017-.123.015-.184.025-.1.016-.204.04-.306.04l-.019-.002-.08-.008a2 2 0 0 1-.371-.056q-.069-.018-.136-.041a2 2 0 0 1-.304-.128c-.04-.02-.077-.037-.114-.06a2 2 0 0 1-.358-.269c-.012-.01-.02-.026-.031-.037a2 2 0 0 1-.252-.315c-.026-.04-.046-.082-.07-.123a2 2 0 0 1-.132-.311 2 2 0 0 1-.045-.138 2 2 0 0 1-.066-.487v-27.278a1.97 1.97 0 0 1 .988-1.707zm-36.622-80.724a1.97 1.97 0 0 1 1.975 0l62.217 35.923c1.316.759 1.316 2.658 0 3.417l-23.62 13.639a1.98 1.98 0 0 1-1.976 0l-21.07-12.165 2.35-1.354c1.407-.814 1.407-2.845 0-3.657l-17.834-10.296c-.325-.19-.69-.282-1.054-.282s-.728.093-1.054.282l-4.459 2.574-19.094-11.023c-1.316-.76-1.316-2.657 0-3.418zm-37.61 22.02c.342 0 .682.089.988.264l21.069 12.163-2.35 1.356c-1.406.812-1.406 2.843 0 3.657l2.35 1.354-21.067 12.165a1.98 1.98 0 0 1-1.975 0l-23.621-13.64c-1.317-.758-1.317-2.657 0-3.416l23.62-13.639c.307-.175.647-.264.987-.264zM98.583 209.04a2 2 0 0 1-.984-.267l-23.62-13.637a1.97 1.97 0 0 1-.988-1.708v-71.843c0-1.153.944-1.977 1.975-1.977.33 0 .667.084.985.268l23.621 13.636c.611.354.988 1.004.988 1.71v24.328l-2.35-1.356c-.34-.197-.7-.285-1.05-.285a2.11 2.11 0 0 0-2.114 2.113v20.592c0 .755.4 1.452 1.054 1.829l4.458 2.576v22.047c.002 1.15-.944 1.974-1.975 1.974m40.572 20.306a1.975 1.975 0 0 1-2.96 1.71l-23.62-13.637a1.98 1.98 0 0 1-.988-1.71v-24.327l2.348 1.355c.34.197.7.287 1.051.287 1.103 0 2.114-.882 2.114-2.116v-2.71l21.068 12.163c.61.353.987 1.003.987 1.71zm0-44.57c0 .172-.027.334-.066.489q-.02.07-.044.137-.053.165-.134.312c-.024.04-.044.083-.068.122q-.114.174-.256.32c-.011.01-.02.024-.029.034a2 2 0 0 1-.359.27c-.035.022-.075.039-.111.06a2.2 2.2 0 0 1-.441.168q-.185.047-.378.056c-.025 0-.05.008-.075.008l-.02.001c-.105 0-.211-.024-.317-.04-.059-.01-.115-.008-.17-.024a2 2 0 0 1-.498-.203l-19.09-11.023v-5.147a2.1 2.1 0 0 0-1.056-1.827l-4.456-2.573v-22.043c0-.173.027-.335.067-.491q.02-.069.043-.136a2 2 0 0 1 .135-.312c.023-.04.043-.084.07-.124q.11-.172.253-.318c.011-.01.02-.024.029-.034q.163-.155.358-.27.056-.032.114-.059a2 2 0 0 1 .818-.226c.025 0 .048-.007.074-.007l.02-.001c.107 0 .214.025.322.04.054.01.11.008.166.024q.252.063.498.203l23.614 13.633a1.98 1.98 0 0 1 .988 1.712zm-13.58-61.717a1.97 1.97 0 0 1-.74-.748 2 2 0 0 1 0-1.922 1.96 1.96 0 0 1 .74-.748l19.095-11.023 4.457 2.573a2.12 2.12 0 0 0 2.11 0l4.458-2.575 19.095 11.025c1.317.759 1.317 2.658 0 3.418l-23.62 13.637a1.97 1.97 0 0 1-1.975 0zm101.8 70.367a1.97 1.97 0 0 1-.987 1.709l-62.217 35.921a1.95 1.95 0 0 1-.985.267c-1.03 0-1.976-.825-1.976-1.977V202.07c0-.706.377-1.356.989-1.71l21.066-12.161v2.71c0 1.232 1.011 2.115 2.113 2.115.353 0 .711-.09 1.053-.286l17.832-10.297a2.11 2.11 0 0 0 1.055-1.828v-5.147l19.095-11.024c.318-.184.655-.267.985-.267 1.03 0 1.976.824 1.976 1.976v27.275zm0-44.565c0 .706-.375 1.356-.987 1.708l-21.068 12.164v-2.71c0-1.233-1.012-2.114-2.114-2.114a2.07 2.07 0 0 0-1.052.285l-2.349 1.355v-24.327c0-.706.377-1.356.99-1.71l23.62-13.636a1.96 1.96 0 0 1 .984-.268c1.031 0 1.976.824 1.976 1.977z'/><path fill='#64ffda' d='m138.166 200.367-21.069-12.163v2.711c0 1.233-1.01 2.115-2.113 2.115-.353 0-.712-.09-1.052-.287l-2.347-1.354v24.327c0 .706.375 1.356.987 1.71l23.62 13.637c.318.184.656.267.986.267a1.975 1.975 0 0 0 1.975-1.977v-27.276a1.98 1.98 0 0 0-.987-1.71m23.617-44.247a2 2 0 0 1 .412-.316l23.62-13.637-23.62 13.637a2 2 0 0 0-.412.316m-48.219-14.226-.02.001c.112-.001.226.02.34.04-.107-.017-.214-.041-.32-.041m22.624 44.599-19.09-11.031zm.985.259.02-.002c-.111.002-.226-.021-.337-.04.106.017.212.042.317.042m49.632-44.86c-.102 0-.205.025-.31.04.11-.02.22-.04.329-.038zm38.589-22.284c-.328 0-.665.084-.985.268l-23.62 13.636a1.98 1.98 0 0 0-.989 1.71v24.327l2.35-1.355c.34-.197.7-.287 1.05-.285 1.103 0 2.115.88 2.115 2.113v2.711l21.068-12.164c.61-.352.987-1.002.987-1.708v-27.276a1.975 1.975 0 0 0-1.976-1.977m-62.212 67.144c.101 0 .204-.025.306-.04-.108.018-.216.038-.323.038zm62.212-22.58a2 2 0 0 0-.985.267l-19.094 11.024v5.147a2.11 2.11 0 0 1-1.056 1.829l-17.832 10.296c-.342.196-.7.286-1.053.286a2.113 2.113 0 0 1-2.112-2.115v-2.71l-21.067 12.162a1.98 1.98 0 0 0-.989 1.71v27.276c0 1.152.945 1.976 1.976 1.976.33 0 .667-.084.985-.267l62.217-35.921c.61-.352.988-1.004.988-1.709v-27.276c-.002-1.149-.947-1.975-1.978-1.975M125.57 75.074c-1.315.76-1.315 2.658 0 3.418l19.095 11.023 4.458-2.575c.327-.189.691-.281 1.055-.281s.729.092 1.054.281l17.833 10.297c1.408.812 1.408 2.843 0 3.656l-2.349 1.355 21.07 12.164a1.98 1.98 0 0 0 1.975 0l23.62-13.639c1.316-.759 1.316-2.657 0-3.416l-62.216-35.923a1.97 1.97 0 0 0-1.975 0zM86.976 97.358c-1.317.759-1.317 2.658 0 3.417l23.621 13.639a1.98 1.98 0 0 0 1.975 0l21.067-12.165-2.35-1.354c-1.406-.814-1.406-2.845 0-3.657l2.35-1.356L112.57 83.72a1.98 1.98 0 0 0-1.974 0zm13.58 109.702v-22.048l-4.459-2.576a2.11 2.11 0 0 1-1.054-1.828v-20.592a2.11 2.11 0 0 1 2.114-2.114c.352 0 .711.088 1.051.286l2.35 1.356v-24.329c0-.706-.376-1.356-.988-1.71l-23.621-13.634a1.95 1.95 0 0 0-.985-.268 1.975 1.975 0 0 0-1.975 1.976v71.842c0 .704.375 1.356.987 1.708l23.62 13.638c.319.183.656.266.985.266 1.03.001 1.975-.823 1.975-1.974zm88.221-41.147-4.458 2.575zv-22.047zm-.245-22.983'/><path fill='#f44336' d='M161.783 156.12a1.97 1.97 0 0 0-.577 1.391v27.277l.001.002c0 .172.028.332.066.487q.02.068.045.138.053.162.133.311c.023.04.043.083.07.123q.11.172.25.315c.013.012.021.027.032.037q.164.156.358.27.055.031.114.059a2 2 0 0 0 .81.224l.081.009c.108 0 .215-.022.324-.039.061-.009.122-.008.183-.024q.244-.064.482-.199l.014-.005 19.094-11.023v-5.148c0-.245.054-.477.133-.7a2.1 2.1 0 0 1 .923-1.129l4.458-2.574v-22.048c0-.173-.028-.334-.066-.49q-.02-.068-.044-.136a1.7 1.7 0 0 0-.135-.311q-.032-.063-.068-.122a2 2 0 0 0-.255-.32l-.029-.033a2 2 0 0 0-.358-.27q-.055-.032-.114-.059a2 2 0 0 0-.44-.17 2 2 0 0 0-.37-.054l-.08-.009c-.11 0-.22.02-.33.039-.058.01-.118.008-.178.024a2 2 0 0 0-.497.203l-23.62 13.637a2 2 0 0 0-.41.317m-23.619-.326L114.55 142.16a2 2 0 0 0-.498-.202c-.056-.017-.112-.014-.166-.024-.114-.02-.228-.04-.341-.039-.026 0-.05.007-.075.007q-.195.011-.378.056c-.047.012-.089.027-.135.04a2 2 0 0 0-.305.13q-.058.027-.114.058a2 2 0 0 0-.358.27l-.028.035a2 2 0 0 0-.255.318q-.036.061-.069.124-.08.15-.135.311a2 2 0 0 0-.11.627v22.044l4.456 2.573a2.1 2.1 0 0 1 1.056 1.827v5.147l19.09 11.023q.245.139.498.203c.056.016.112.013.17.023.112.019.226.04.337.04.026 0 .05-.007.075-.009q.195-.01.378-.056c.047-.01.089-.025.135-.039q.157-.052.306-.13c.037-.02.076-.036.112-.058a2 2 0 0 0 .359-.27l.028-.034q.143-.146.256-.32l.068-.122q.08-.148.135-.312a2 2 0 0 0 .11-.627v-27.268a1.98 1.98 0 0 0-.988-1.712m13.002-19.098 23.62-13.637c1.317-.76 1.317-2.66 0-3.418l-19.094-11.025-4.459 2.575a2.12 2.12 0 0 1-2.11 0l-4.457-2.573-19.095 11.023a1.96 1.96 0 0 0-.74.748 2 2 0 0 0 0 1.922c.165.296.413.558.74.748l23.622 13.637a1.97 1.97 0 0 0 1.973 0'/></svg>",
+ "riot": "<svg viewBox='0 0 32 32'><path fill='#e53935' d='M20 4H4.5a.5.5 0 0 0-.5.5V22a6 6 0 0 0 6 6V10.5a.5.5 0 0 1 .5-.5H20a2 2 0 0 1 2 2v1.5a.5.5 0 0 0 .5.5h5a.5.5 0 0 0 .5-.5V12a8 8 0 0 0-8-8'/><path fill='#e53935' d='M24 16H14a6 6 0 0 0 6 6h1a1 1 0 0 1 1 1v5h2a4 4 0 0 0 4-4v-4a4 4 0 0 0-4-4'/></svg>",
+ "roadmap": "<svg viewBox='0 0 24 24'><path fill='#26a69a' d='M2 2h2v18h18v2H2zm5 8h10v3H7zm4 5h10v3H11zM6 4h16v4h-2V6H8v2H6z'/></svg>",
+ "roblox": "<svg viewBox='0 0 500 500'><path fill='#42a5f5' d='m127.87 38.084 334.05 89.432-36.055 135.03-199.37-53.377-10.251 38.177-134.68-36.056zm244.26 423.83L38.08 372.482l36.056-135.03 199.01 53.377 10.251-38.176 135.03 36.055z' clip-rule='evenodd'/></svg>",
+ "robot": "<svg viewBox='0 0 32 32'><path fill='#00bfa5' d='M25.172 6 28 8.828v14.344L25.172 26H6.828L4 23.172V8.828L6.828 6zM26 4H6L2 8v16l4 4h20l4-4V8z'/><path fill='#00bfa5' d='M8 20h16v2H8zm0-6v2h2v-2a2 2 0 0 1 2-2 2 2 0 0 1 2 2v2h2v-2a4 4 0 0 0-4-4 4 4 0 0 0-4 4m9.876.268 5.196-3 1 1.732-5.196 3z'/></svg>",
+ "robots": "<svg viewBox='0 0 32 32'><path fill='#ff5252' d='M28.586 18H28a8 8 0 0 0-8-8h-2V8.445a4 4 0 1 0-4 0V10h-2a8 8 0 0 0-8 8h-.586A1.414 1.414 0 0 0 2 19.414v3.172A1.414 1.414 0 0 0 3.414 24H4v1a3 3 0 0 0 3 3h18a3 3 0 0 0 3-3v-1h.586A1.414 1.414 0 0 0 30 22.586v-3.172A1.414 1.414 0 0 0 28.586 18M11 22a3 3 0 1 1 3-3 3 3 0 0 1-3 3m10 0a3 3 0 1 1 3-3 3 3 0 0 1-3 3'/></svg>",
+ "rocket": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M12 26c-1.71 1.905-7.64 2.149-7.93 1.937l-.004.003-.002-.005-.005-.002.004-.003C3.85 27.64 4.1 21.715 6 20ZM29.64 7.908c-.655 2.69-1.681 5.64-5.64 10.092l-.789 4.749a4 4 0 0 1-1.116 2.171l-4.863 4.868A.72.72 0 0 1 16 29.277v-6.23L9 16H2.723a.721.721 0 0 1-.511-1.232L7.08 9.905A4 4 0 0 1 9.25 8.79L14 8c4.453-3.959 7.402-4.985 10.092-5.64a11.1 11.1 0 0 1 3.642-.329 3.1 3.1 0 0 1 1.72.515 3.1 3.1 0 0 1 .515 1.72 11.1 11.1 0 0 1-.33 3.642ZM26 10a4 4 0 1 0-4 4 4 4 0 0 0 4-4'/></svg>",
+ "rojo": "<svg fill='none' viewBox='0 0 24 24'><path fill='#e53935' d='M9.586 2h.077l-.077.135v.29c.598-.116.908-.27.908-.425l.425.058A36 36 0 0 1 12.869 2h.077c.927 0 2.009.502 3.284 1.526.27.444.424.83.502 1.197v.695c-.155.966-.425 1.7-.773 2.183q-.319.26-1.39 1.738h-.078v-.135l.194-.29h-.116a17.6 17.6 0 0 1-5.601 3.226 6.2 6.2 0 0 1-1.545.212c.656.772 1.873 1.796 3.63 3.07.174.097 1.295.812 3.361 2.183 1.719 1.062 3.766 2.221 6.161 3.496v.077h-.29c-.463-.174-.695-.309-.695-.367l-.135.077h-.135c-.058 0-.078-.019-.078-.077-.173.039-.309.213-.424.502.29.078.424.174.424.27-.115.097-.193.155-.27.155l-.348-.077v.077c0 .078.058.135.194.135v.213h-.058c-.097 0-1.16-.618-3.148-1.835-1.41-.715-3.149-1.815-5.176-3.283-.676-.31-1.912-1.217-3.728-2.723h-.193c-.27.405-.618 1.41-1.062 3.013-.097.367-.56.907-1.39 1.602h-.155l-.135-.212v-.077c0-.097.077-.31.212-.618v-.077h-.077c0 .135-.29.347-.83.637H3v-.135c1.642-4.867 2.781-8.035 3.438-9.522q1.68-3.852 1.68-4.403c-.251 0-.811.309-1.68.965h-.135l.058-.135v-.135c-.252 0-.831.406-1.739 1.255h-.077v-.077l.908-1.043.154-.212v-.077h-.154l-.83.695c-.097 0-.136-.039-.136-.116A11.1 11.1 0 0 1 7.693 3.12c1.178-.309 1.758-.656 1.758-1.062zm6.373 1.95v.078c0 .077.058.135.135.135v-.077c0-.077-.038-.135-.135-.135M7.77 10.673h.058c.85-.174 1.275-.31 1.275-.425-.058-.097-.077-.155-.077-.213C10.803 9.436 12 8.837 12.599 8.22c.058 0 .425-.386 1.12-1.198h.077V7.1c-.096.154-.154.27-.154.347h.077c.599-.463.908-.965.908-1.525v-.425c-.136 0-.213-.058-.213-.155q.348-.057.348-.347 0-.348-1.39-.696c-.754-.193-1.565-.27-2.453-.27-.445 0-.889.811-1.333 2.453-.193.193-.792 1.583-1.816 4.19m.83-5.949V4.8c.078 0 .136-.077.213-.212v-.077c-.058 0-.135.077-.212.212m.078 1.333h.077c.136-.058.213-.193.213-.425-.077 0-.174.135-.29.425m6.586.27v.155c.058 0 .135-.078.193-.213v-.077H15.4c-.096.02-.135.058-.135.135m-7.57 4.693v.078h.134a.5.5 0 0 1 .213-.078v.078c1.14-.213 1.815-.445 2.047-.696h-.077c-1.024.232-1.796.444-2.318.618m-.271.696h.058l1.274-.27-.077-.136c-.83.097-1.255.251-1.255.406m2.78 4.345v.135c.735.56 1.179.85 1.334.85-.174-.135-.27-.251-.27-.348zm2.048 1.545c.039.174.174.27.406.27h.077v-.077c-.116 0-.251-.058-.425-.193zm4.539 2.51q.55.495 1.68.986l.077-.135c-.58-.348-1.12-.638-1.603-.85zm3.862 1.121h.213c.077.02.135.058.135.135-.097 0-.135.078-.135.213h-.155l.078-.136v-.077h-.136z'/></svg>",
+ "rollup": "<svg viewBox='100 100 800 800'><path fill='#f44336' d='M733.79 394.71c0 77.407-42.308 144.79-104.67 180.51-3.76 3.134-5.954 8.148-3.76 12.849l106.87 211.22c2.82 6.581-1.568 14.103-8.776 14.103h-408.35l2.194-1.254c15.356-8.774 121.91-219.06 225.95-318.72 104.05-99.658 117.21-66.439 59.857-174.87 0 0 44.188 86.182 6.581 92.763-29.459 5.328-97.15-60.17-72.08-119.09 25.071-57.664 123.79-46.695 169.23.314 17.236 30.085 26.952 64.872 26.952 102.17m-385.47 140.71c-41.367 76.154-67.692 131.62-82.108 170.48v-509.57c0-5.328 4.388-9.715 9.715-9.715h252.91c73.333 1.253 137.58 40.114 173.62 98.718-26.325-32.906-67.692-51.71-108.43-51.71-77.407 0-96.837 28.206-245.7 301.79z'/></svg>",
+ "rome": "<svg viewBox='0 0 32 32'><path fill='#546e7a' d='M9.875 5.409A14.02 14.02 0 0 0 2.01 17.48a.51.51 0 0 0 .508.519H5.52a.495.495 0 0 0 .491-.481 10.01 10.01 0 0 1 5.273-8.337.494.494 0 0 0 .243-.592l-.958-2.882a.505.505 0 0 0-.694-.3Zm11.556.299-.958 2.882a.494.494 0 0 0 .243.592 10.01 10.01 0 0 1 5.273 8.337.495.495 0 0 0 .49.481h3.004a.51.51 0 0 0 .508-.519A14.02 14.02 0 0 0 22.125 5.41a.505.505 0 0 0-.694.299ZM26 20.5v9a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5m-24 0v9a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5'/><path fill='#ffc400' d='M16.13 10h-.26A7.87 7.87 0 0 0 8 17.87V29.5a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5V17.87A3.88 3.88 0 0 1 15.87 14h.26A3.88 3.88 0 0 1 20 17.87V29.5a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5V17.87A7.87 7.87 0 0 0 16.13 10m1.51-2h-3.28a.5.5 0 0 1-.474-.342l-1.667-5A.5.5 0 0 1 12.694 2h6.612a.5.5 0 0 1 .475.658l-1.667 5A.5.5 0 0 1 17.64 8'/></svg>",
+ "routing": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='M18 14v-2h8l2-3-2-3h-8V4l-2-2-2 2v6H6l-2 3 2 3h8v10a4 4 0 0 0-4 4h12a4 4 0 0 0-4-4v-6h8l2-3-2-3Z'/></svg>",
+ "rspec": "<svg viewBox='0 0 128 128'><path fill='#ff1744' d='M50.633 39.468h-2.267L33.824 54.661l.423 1.03 8.624 1.563-8.624-1.563 3.05 7.437 26.265 31.47 26.262-31.47 3.051-7.437-8.625 1.563-.096-.53-12.888.016 12.887-.017L76.49 39.5v-.032H54.147l1.71 17.27v.002h-.001v-.002l-1.709-17.27zm12.93 55.13L46.738 64.322 63.562 94.6Zm0 0L52.256 64.752l-.172-.457.172.457 11.305 29.847ZM84.18 57.267l-3.533 7.186zm-3.535 7.186-.13.262zm-5.61-.157-.171.457.172-.457ZM71.05 57.43l-3.638 9.198-.192-.024.192.025zm-11.34 9.2-2.316-5.858 2.315 5.856.196-.025-.196.026Zm3.852 27.97 3.655-27.997-3.654 27.996Zm-20.62-37.334 3.532 7.187zm35.813-17.797 14.543 15.193-.423 1.03-8.625 1.563-.095-.53L76.489 39.5v-.032zM56.073 56.74l7.49.012z' clip-rule='evenodd'/><path fill='#80deea' d='M120 64a55.9 55.9 0 0 1-3.96 20.723l-.013-.005-8.327 1.656-5.206-7.77A41 41 0 0 0 105.16 64c0-21.382-16.301-38.955-37.155-40.968l.005-.061 5.632-7.6-4.258-7.115C97.787 10.963 120 34.89 120 64'/><path fill='#4dd0e1' d='m106.557 89.43 8.141-1.618C105.749 106.834 86.412 120 64 120c-22.475 0-41.858-13.239-50.773-32.347l.012-.005 4.506-7.198 9.225 1.543h.003C33.646 95.71 47.72 105.16 64 105.16c16.457 0 30.657-9.658 37.243-23.613l.049.023'/><path fill='#26c6da' d='M64.808 22.84v.009q-.402-.01-.808-.01c-22.732 0-41.16 18.428-41.16 41.161 0 5.317 1.01 10.395 2.843 15.062l-.049.02-9.33-1.56-4.407 7.037A55.8 55.8 0 0 1 8 64C8 33.072 33.072 8 64 8q1.014 0 2.019.035l4.36 7.289'/></svg>",
+ "rubocop": "<svg viewBox='0 0 32 32'><path fill='#b0bec5' d='M22 24a2 2 0 0 1-2 2h-2l-1-2h-2l-1 2h-2a2 2 0 0 1-2-2v-2H8v2a4 4 0 0 0 4 4h8a4 4 0 0 0 4-4v-2h-2Z'/><path fill='#b0bec5' d='M20 24v-2h-8v2h1v-1h6v1zm6.5-10H26a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2h-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5H6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2h.5a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5'/><path fill='#e53935' d='M8 15h16v2H8z' data-mit-no-recolor='true'/><path fill='#b0bec5' d='M23.738 10a7.99 7.99 0 0 0-15.476 0Z'/></svg>",
+ "rubocop_light": "<svg viewBox='0 0 32 32'><path fill='#455a64' d='M22 24a2 2 0 0 1-2 2h-2l-1-2h-2l-1 2h-2a2 2 0 0 1-2-2v-2H8v2a4 4 0 0 0 4 4h8a4 4 0 0 0 4-4v-2h-2Z'/><path fill='#455a64' d='M20 24v-2h-8v2h1v-1h6v1zm6.5-10H26a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2h-.5a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5H6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2h.5a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5'/><path fill='#e53935' d='M8 15h16v2H8z' data-mit-no-recolor='true'/><path fill='#455a64' d='M23.738 10a7.99 7.99 0 0 0-15.476 0Z'/></svg>",
+ "ruby": "<svg viewBox='0 0 24 24'><path fill='#f44336' d='M18.041 3.177c2.24.382 2.879 1.919 2.843 3.527V6.67l-1.013 13.266-13.132.897h.008c-1.093-.044-3.518-.151-3.634-3.545l1.217-2.222 2.462 5.74 2.097-6.77-.045.009.018-.018 6.85 2.186L13.945 9.3l6.53-.409-5.144-4.212 2.71-1.51v.009M3.113 17.252v.017zM6.916 6.874c2.63-2.622 6.033-4.168 7.34-2.844 1.297 1.306-.072 4.523-2.702 7.135-2.666 2.613-6.015 4.248-7.322 2.933-1.306-1.324.036-4.612 2.675-7.224z'/></svg>",
+ "ruff": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='M26 16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H4v24h10v-8h2v8h12V18h-6v-2Zm-8-2h-6v-2h6Z'/></svg>",
+ "rust": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='m30 12-4-2V6h-4l-2-4-4 2-4-2-2 4H6v4l-4 2 2 4-2 4 4 2v4h4l2 4 4-2 4 2 2-4h4v-4l4-2-2-4ZM6 16a9.9 9.9 0 0 1 .842-4H10v8H6.842A9.9 9.9 0 0 1 6 16m10 10a9.98 9.98 0 0 1-7.978-4H16v-2h-2v-2h4c.819.819.297 2.308 1.179 3.37a1.89 1.89 0 0 0 1.46.63h3.34A9.98 9.98 0 0 1 16 26m-2-12v-2h4a1 1 0 0 1 0 2Zm11.158 6H24a2.006 2.006 0 0 1-2-2 2 2 0 0 0-2-2 3 3 0 0 0 3-3q0-.08-.004-.161A3.115 3.115 0 0 0 19.83 10H8.022a9.986 9.986 0 0 1 17.136 10'/></svg>",
+ "salesforce": "<svg viewBox='0 0 24 24'><path fill='#039be5' d='M18.206 6.522c-.681 0-1.275.204-1.858.399-.681-1.177-1.956-1.956-3.328-1.956-1.07 0-2.043.487-2.734 1.168-.779-.973-1.956-1.664-3.318-1.664-2.267 0-4.213 1.858-4.213 4.126 0 .574.204 1.157.399 1.741a3.58 3.58 0 0 0-1.859 3.124c0 1.946 1.567 3.62 3.523 3.62.292 0 .583 0 .778-.098.39 1.469 1.858 2.55 3.62 2.55 1.654 0 3.026-.984 3.512-2.356.496.205.983.4 1.47.4 1.274 0 2.442-.71 3.025-1.762.302.078.613.078.886.078 2.54 0 4.592-2.034 4.592-4.67.098-2.627-1.946-4.7-4.495-4.7'/></svg>",
+ "san": "<svg viewBox='0 0 32 32'><path fill='#01579b' d='M28 17.898 4 23.316V30l24-5.418Zm0-10.623L4 12.694v6.683l24-5.418Z'/><path fill='#b3e5fc' d='M28 13.926 4 8.684V2l24 5.242Zm0 10.623L4 19.307v-6.684l24 5.242Z'/></svg>",
+ "sas": "<svg viewBox='0 0 6.35 6.35'><path fill='#039be5' d='M3.16.546a1.9 1.9 0 0 0-.667.13c-.378.157-.678.4-.846.74-.255.452-.296 1.032.04 1.536.31.474.72.908 1.148 1.407.382.139.572-.272.515-.42-.276-.35-.57-.695-.804-1.06a1.1 1.1 0 0 1-.07-.906c.075-.198.503-.84 1.372-.836.345.04.658.059 1.086.53a1.6 1.6 0 0 0-.536-.753A2.05 2.05 0 0 0 3.159.546zm.245 1.4c-.277.002-.406.227-.348.437.253.365.578.748.869 1.122.316.609.092 1.077-.512 1.472-.82.458-1.576.116-2.026-.298.234.489.423.636.471.679.31.27.81.527 1.62.423.456-.102 1.06-.256 1.396-1.097.093-.265.125-.54.03-.843-.09-.338-.274-.58-.466-.829-.3-.351-.77-.979-.906-1.053a.6.6 0 0 0-.128-.013'/></svg>",
+ "sass": "<svg viewBox='0 0 32 32'><path fill='#ec407a' d='M27.837 5.673a4.33 4.33 0 0 0-2.293-2.701c-2.362-1.261-6.11-1.298-9.548-.092a26.3 26.3 0 0 0-8.76 4.966c-2.752 2.542-3.438 4.925-3.189 6.194.523 2.668 3.274 4.539 5.485 6.042.418.284.822.559 1.175.816-1.429.76-4.261 2.444-5.088 4.248a3.88 3.88 0 0 0-.118 3.332A2.37 2.37 0 0 0 6.869 29.8a5.6 5.6 0 0 0 1.49.2 6.35 6.35 0 0 0 5.19-2.856 6.74 6.74 0 0 0 .864-5.382 7.3 7.3 0 0 1 2.044-.03 3.92 3.92 0 0 1 2.816 1.311 1.82 1.82 0 0 1 .423 1.262 1.55 1.55 0 0 1-.772 1.05c-.234.14-.586.355-.504.803.036.194.198.633.894.512a2.93 2.93 0 0 0 2.145-2.651 4 4 0 0 0-1.197-2.904 5.94 5.94 0 0 0-4.396-1.626 10.6 10.6 0 0 0-2.672.304 20 20 0 0 0-2.203-1.846c-1.712-1.3-3.33-2.529-3.235-4.26.125-2.263 2.468-4.532 6.964-6.744 4.016-1.976 7.254-2.037 8.944-1.438a2 2 0 0 1 1.204.883 2.77 2.77 0 0 1-.36 2.47 9.71 9.71 0 0 1-7.425 4.304 3.86 3.86 0 0 1-3.238-.757c-.278-.302-.593-.645-1.074-.383q-.565.31-.225 1.189a3.9 3.9 0 0 0 2.407 1.92 11.7 11.7 0 0 0 7.128-.671c3.527-1.35 6.681-5.202 5.756-8.787M11.895 24.475a4 4 0 0 1-.192.468 4.5 4.5 0 0 1-.753 1.081 2.83 2.83 0 0 1-2.533 1.107c-.056-.032-.078-.146-.085-.193a3.28 3.28 0 0 1 1.076-2.284 11.3 11.3 0 0 1 2.644-1.933 3.85 3.85 0 0 1-.157 1.754'/></svg>",
+ "sbt": "<svg viewBox='0 0 300 300'><path fill='#0277bd' d='M105.46 209.517c-7.875 0-13.452-7.521-13.452-15.37v-.327c0-7.848 5.578-13.735 13.452-13.735h164.05c1.476-4.905 2.625-11.446 3.281-17.986h-137.81c-7.875 0-14.273-6.05-14.273-13.898s6.398-13.898 14.273-13.898h137.31c-.82-6.54-1.969-13.081-3.773-17.986h-104.01c-7.875 0-14.273-6.05-14.273-13.898s6.398-13.898 14.273-13.898h91.87c-21.327-37.607-60.864-61.315-106.14-61.315-67.918 0-123.04 54.448-123.04 122.3 0 67.856 55.122 123.28 123.04 123.28 46.59 0 87.112-25.507 107.95-63.114h-152.73z'/></svg>",
+ "scala": "<svg viewBox='0 0 32 32'><path fill='#f44336' d='m6.457 9.894 12.523 5.163-.456 1.211L6 11.105Zm7.02-3.091L26 11.966l-.457 1.21L13.02 8.015ZM6.465 18.885l12.524 5.163-.457 1.21L6.01 20.097Zm7.007-3.086 12.524 5.163-.456 1.21-12.524-5.162Z'/><path fill='#f44336' d='M6 24.07V30l19.997-3.106V20.96zM6 5.11v5.99l20-3.11V2zm0 9.96v5.03l20-3.11v-5.03z'/></svg>",
+ "scheme": "<svg viewBox='0 0 24 24'><path fill='#f44336' d='M5.11 21.186 9.887 7.303 8.945 5.11H7.407V2.813h2.296c.483 0 .896.299 1.068.724l6.58 15.353h1.539v2.296h-2.297a1.14 1.14 0 0 1-1.068-.735L11.231 10.45 7.544 21.186z'/></svg>",
+ "scons": "<svg viewBox='0 0 16 16'><path fill='#c62828' d='M1 12h6v3H1Zm8 0h6v3H9ZM1 8h3v3H1Zm11 0h3v3h-3ZM5 1h6v3H5Z'/><path fill='#b0bec5' d='M8 11 6 8h1V5h2v3h1Z'/></svg>",
+ "scons_light": "<svg viewBox='0 0 16 16'><path fill='#c62828' d='M1 12h6v3H1Zm8 0h6v3H9ZM1 8h3v3H1Zm11 0h3v3h-3ZM5 1h6v3H5Z'/><path fill='#455a64' d='M8 11 6 8h1V5h2v3h1Z'/></svg>",
+ "screwdriver": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M12 2A10 10 0 0 0 2 12a10 10 0 0 0 8.185 9.815A10 10 0 0 0 20 30a10 10 0 0 0 10-10 10 10 0 0 0-8.17-9.83A10 10 0 0 0 12 2m0 4a6 6 0 0 1 5.654 4H10v7.652A6 6 0 0 1 6 12a6 6 0 0 1 6-6m2 8h4v4h-4zm8 .346A6 6 0 0 1 26 20a6 6 0 0 1-6 6 6 6 0 0 1-5.652-4H22z'/></svg>",
+ "search": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='M17 17a4 4 0 1 1-4 4 4.005 4.005 0 0 1 4-4m0-3a7 7 0 1 0 7 7 7 7 0 0 0-7-7'/><path fill='#42a5f5' d='m19.586 26.414 2.828-2.828L26 27.17 23.17 30zM10 26H6V4h9.172L22 10.828V12h2v-2l-8-8H6a2 2 0 0 0-2 2v22a2 2 0 0 0 2 2h4Z'/><path fill='#42a5f5' d='M22 12h-8V4h2l6 6zm0 0h2v2h-2z'/></svg>",
+ "semantic-release": "<svg viewBox='0 0 300 300'><path fill='#f5f5f5' d='M93.656 252.93c-30.929-17.946-57.303-33.656-58.608-34.91-2.197-2.111-2.372-7.168-2.352-68.012.01-36.152.469-66.413 1.017-67.248.84-1.278 114.93-68.32 116.26-68.32 1.335 0 115.42 67.042 116.26 68.32 1.697 2.584 1.253 132.52-.461 134.58-1.178 1.42-50.022 30.2-110.42 65.063l-5.466 3.155zm74.879 11.529c9.928-5.689 19.12-11.554 20.425-13.033 5.167-5.855 2.22-22.428-7.921-44.553-3.434-7.491-5.89-13.974-5.458-14.406 2.166-2.165 30 21.345 35.714 30.166 8.795 13.576 5.942 13.273 24.626 2.61 9.088-5.185 17.723-10.52 19.191-11.854 2.44-2.22 2.668-4.272 2.668-24.017 0-24.644.382-23.74-12.846-30.433-7.331-3.71-27.076-7.453-39.62-7.51-3.127-.015-6.045-.611-6.486-1.326-1.018-1.646 3.35-3.864 20.305-10.314 10.685-4.064 16.148-5.32 25.765-5.926l12.204-.768V87.052l-16.926-9.863c-9.31-5.425-18.078-9.863-19.485-9.863-4.587 0-18.396 9.537-24.283 16.77-3.164 3.888-8.499 11.144-11.855 16.125-3.357 4.982-6.648 9.076-7.314 9.1-1.699.06.055-16.855 3.282-31.654 1.676-7.688 4.235-14.848 6.755-18.906 2.231-3.592 4.057-7.32 4.057-8.283 0-1.574-38.81-25.189-41.396-25.189-1.803 0-38.26 21.675-40.016 23.79-1.114 1.343-1.456 5.6-1.056 13.167.52 9.845 1.444 13.13 7.728 27.456 3.926 8.95 7.463 17.035 7.86 17.968 1.606 3.762-2.84 1.171-15.601-9.094-15.36-12.355-19.89-17.167-24.644-26.181-1.863-3.533-4-6.376-4.746-6.318-.747.057-9.292 4.806-18.987 10.552l-17.63 10.448v47.348l7.44 4.445c9.78 5.844 13.866 6.917 33.242 8.725 8.95.835 16.8 1.982 17.445 2.548 1.506 1.321-3.334 3.76-20.158 10.16-10.685 4.064-16.148 5.32-25.765 5.926l-12.204.769-.372 21.952c-.44 25.928-2.375 22.64 21.282 36.139l16.252 9.273 7.144-3.85c9.02-4.859 15.585-11.571 25.982-26.562 4.526-6.526 8.717-11.865 9.314-11.865 1.68 0 1.312 5.155-1.614 22.579-2.03 12.092-3.88 18.4-7.474 25.496-4.046 7.985-4.493 9.712-2.918 11.287 1.926 1.926 37.46 23.046 39.11 23.245.513.062 9.056-4.542 18.985-10.231zm-28.054-89.313c-7.395-2.593-15.058-11.41-16.73-19.25-4.594-21.54 17.346-39.532 37.071-30.4 17.296 8.008 21.43 29.242 8.34 42.832-8.106 8.416-17.651 10.686-28.68 6.818zm16.04-7.965c2.435-1.017 6.077-3.81 8.093-6.206 7.074-8.407 3.726-22.934-6.392-27.735-12.968-6.154-26.556 2.647-26.556 17.2 0 7.107 4.494 13.649 11.459 16.683 5.324 2.319 7.957 2.33 13.396.058'/></svg>",
+ "semantic-release_light": "<svg viewBox='0 0 300 300'><path fill='#455a64' d='M93.656 252.93c-30.929-17.946-57.303-33.656-58.608-34.91-2.197-2.111-2.372-7.168-2.352-68.012.01-36.152.469-66.413 1.017-67.248.84-1.278 114.93-68.32 116.26-68.32 1.335 0 115.42 67.042 116.26 68.32 1.697 2.584 1.253 132.52-.461 134.58-1.178 1.42-50.022 30.2-110.42 65.063l-5.466 3.155zm74.879 11.529c9.928-5.689 19.12-11.554 20.425-13.033 5.167-5.855 2.22-22.428-7.921-44.553-3.434-7.491-5.89-13.974-5.458-14.406 2.166-2.165 30 21.345 35.714 30.166 8.795 13.576 5.942 13.273 24.626 2.61 9.088-5.185 17.723-10.52 19.191-11.854 2.44-2.22 2.668-4.272 2.668-24.017 0-24.644.382-23.74-12.846-30.433-7.331-3.71-27.076-7.453-39.62-7.51-3.127-.015-6.045-.611-6.486-1.326-1.018-1.646 3.35-3.864 20.305-10.314 10.685-4.064 16.148-5.32 25.765-5.926l12.204-.768V87.052l-16.926-9.863c-9.31-5.425-18.078-9.863-19.485-9.863-4.587 0-18.396 9.537-24.283 16.77-3.164 3.888-8.499 11.144-11.855 16.125-3.357 4.982-6.648 9.076-7.314 9.1-1.699.06.055-16.855 3.282-31.654 1.676-7.688 4.235-14.848 6.755-18.906 2.231-3.592 4.057-7.32 4.057-8.283 0-1.574-38.81-25.189-41.396-25.189-1.803 0-38.26 21.675-40.016 23.79-1.114 1.343-1.456 5.6-1.056 13.167.52 9.845 1.444 13.13 7.728 27.456 3.926 8.95 7.463 17.035 7.86 17.968 1.606 3.762-2.84 1.171-15.601-9.094-15.36-12.355-19.89-17.167-24.644-26.181-1.863-3.533-4-6.376-4.746-6.318-.747.057-9.292 4.806-18.987 10.552l-17.63 10.448v47.348l7.44 4.445c9.78 5.844 13.866 6.917 33.242 8.725 8.95.835 16.8 1.982 17.445 2.548 1.506 1.321-3.334 3.76-20.158 10.16-10.685 4.064-16.148 5.32-25.765 5.926l-12.204.769-.372 21.952c-.44 25.928-2.375 22.64 21.282 36.139l16.252 9.273 7.144-3.85c9.02-4.859 15.585-11.571 25.982-26.562 4.526-6.526 8.717-11.865 9.314-11.865 1.68 0 1.312 5.155-1.614 22.579-2.03 12.092-3.88 18.4-7.474 25.496-4.046 7.985-4.493 9.712-2.918 11.287 1.926 1.926 37.46 23.046 39.11 23.245.513.062 9.056-4.542 18.985-10.231zm-28.054-89.313c-7.395-2.593-15.058-11.41-16.73-19.25-4.594-21.54 17.346-39.532 37.071-30.4 17.296 8.008 21.43 29.242 8.34 42.832-8.106 8.416-17.651 10.686-28.68 6.818zm16.04-7.965c2.435-1.017 6.077-3.81 8.093-6.206 7.074-8.407 3.726-22.934-6.392-27.735-12.968-6.154-26.556 2.647-26.556 17.2 0 7.107 4.494 13.649 11.459 16.683 5.324 2.319 7.957 2.33 13.396.058'/></svg>",
+ "semgrep": "<svg viewBox='0 0 24 24'><path fill='#00bfa5' d='M5.918 8.101a3.898 3.898 0 1 0 3.041 6.336l.004-.005.002.001q.055-.069.107-.142a2.7 2.7 0 0 0 .36-.602l.029-.064.03-.067h.13a2 2 0 0 1-.117.348 7 7 0 0 1-.38.705l-.007.01A3.9 3.9 0 0 0 12 15.898a3.9 3.9 0 0 0 2.883-1.277l-.006-.01a7 7 0 0 1-.383-.705 2 2 0 0 1-.117-.348h.13q.035.079.071.155a2.7 2.7 0 0 0 .348.578l.023.03.008.01q.038.052.078.1v.003l.002.002a3.898 3.898 0 1 0 .149-5.047c.238.385.416.677.537.974h-.137c-.238-.388-.319-.543-.545-.802a3.89 3.89 0 0 0-3.04-1.46 3.89 3.89 0 0 0-3.042 1.46c-.226.26-.309.414-.547.802h-.135c.121-.297.297-.589.536-.974a3.9 3.9 0 0 0-2.895-1.287zm0 1.715a2.184 2.184 0 1 1 0 4.368 2.184 2.184 0 0 1 0-4.368m6.082 0a2.184 2.184 0 1 1 0 4.368 2.184 2.184 0 0 1 0-4.368m6.082 0c1.206 0 2.182.978 2.182 2.184a2.182 2.182 0 1 1-4.366 0c0-1.206.978-2.184 2.184-2.184'/></svg>",
+ "sentry": "<svg viewBox='0 0 200 200'><path fill='#f06292' d='M181.58 148.3c3.8 6.649 4.206 13.637 1.018 19.2s-9.43 8.684-17.03 8.684h-14.45c.203-2.714.271-5.428.271-8.141 0-3.053-.136-6.106-.34-9.091h9.499c2.307 0 4.206-1.9 4.206-4.207 0-.678-.203-1.424-.475-2.035L103.694 47.35c-.746-1.357-2.17-2.171-3.663-2.171s-2.85.746-3.596 2.035l-9.634 16.69c29.241 22.05 48.848 56.175 51.561 94.914.204 2.985.34 6.038.34 9.091 0 2.714-.136 5.428-.272 8.142H91.278q.407-4.071.407-8.142c0-3.053-.203-6.106-.542-9.09-2.307-21.304-12.755-40.232-28.155-53.53l-6.65 11.533c11.602 10.787 19.54 25.51 21.71 42.063.408 2.985.611 6.038.611 9.091 0 2.782-.203 5.496-.475 8.142H34.425c-7.666 0-13.908-3.189-17.029-8.684-3.188-5.496-2.781-12.551 1.018-19.2l8.956-15.401a52.4 52.4 0 0 1 13.704 10.652l-5.36 9.09c-.34.611-.475 1.29-.543 2.036 0 2.307 1.9 4.206 4.206 4.206h21.235a52.26 52.26 0 0 0-13.162-26.595c-3.934-4.274-8.616-7.87-13.704-10.65L57.424 80.39c5.02 2.782 9.838 6.038 14.247 9.702 20.421 16.554 34.193 41.046 36.704 68.726h12.687c-2.578-32.362-18.86-60.924-43.013-79.852-4.545-3.528-9.362-6.784-14.383-9.566l20.217-35.143c3.8-6.649 9.702-10.448 16.011-10.448 6.378 0 12.212 3.8 16.011 10.448z'/></svg>",
+ "sequelize": "<svg preserveAspectRatio='xMidYMid' viewBox='0 0 250 250'><g stroke-width='.725'><path fill='#01579b' d='M190.945 86.173v77.236l-65.63 38.829-.593.54v28.867l.593.56 92.43-53.39V71.194l-.873-.215-26.058 14.568.132.629'/><path fill='#0288d1' d='m59.477 164.043 65.84 38.196v29.966l-93.063-53.601v-107.2l.955-.144 25.983 15.106.285.865v76.814'/><path fill='#4fc3f7' d='M59.477 87.223 32.254 71.396l92.852-53.601 92.64 53.39-26.8 14.983-65.84-37.563-65.63 38.618'/><path fill='#283593' d='m124.186 155.203-.713-.727v-29.002l.713-.369.173-.715 24.864-14.504.76.17v29.884l-25.798 15.263'/><path fill='#0277bd' d='M97.959 140.873v-31.029l.72-.035 25.292 14.718.216.58v30.098z'/><path fill='#29b6f6' d='m123.756 94.583-25.798 15.264 26.228 15.263 25.798-15.049z'/><path fill='#01579b' d='m92.083 174.123-.713-.727v-29.003l.713-.368.173-.715 24.863-14.504.761.17v29.882l-25.798 15.264'/><path fill='#0288d1' d='M65.855 159.793v-31.03l.72-.036 25.292 14.72.215.58v30.098z'/><path fill='#4fc3f7' d='m91.653 113.503-25.799 15.263 26.229 15.264 25.798-15.049-26.228-15.479'/><path fill='#01579b' d='m158.585 174.843-.712-.727v-29.003l.713-.368.173-.715 24.864-14.504.76.17v29.882z'/><path fill='#0288d1' d='M132.356 160.513v-31.03l.72-.035 25.292 14.72.216.58v30.097z'/><path fill='#4fc3f7' d='m158.155 114.213-25.798 15.263 26.228 15.264 25.798-15.049z'/><path fill='#01579b' d='m126.476 193.763-.713-.727v-29.003l.713-.368.173-.716 24.864-14.503.76.17V178.5l-25.798 15.264'/><path fill='#0288d1' d='M100.256 179.433v-31.03l.72-.035 25.293 14.718.214.58v30.099l-26.228-14.332'/><path fill='#4fc3f7' d='m126.046 133.133-25.799 15.264 26.229 15.264 25.798-15.049z'/><path fill='#01579b' d='m124.186 114.073-.713-.728V84.343l.713-.368.173-.715 24.864-14.503.76.17v29.882l-25.798 15.264'/><path fill='#0288d1' d='M97.959 99.743v-31.03l.72-.035 25.292 14.718.216.58v30.099z'/><path fill='#4fc3f7' d='M123.756 53.443 97.958 68.707l26.228 15.264 25.798-15.049z'/><path fill='#01579b' d='m92.083 132.993-.713-.727v-29.002l.713-.369.173-.715 24.863-14.504.761.17v29.884l-25.798 15.263'/><path fill='#0288d1' d='M65.855 118.663V87.634l.72-.036 25.292 14.72.215.58v30.097z'/><path fill='#4fc3f7' d='m91.653 72.363-25.8 15.264 26.229 15.263 25.798-15.049z'/><path fill='#01579b' d='m158.585 133.713-.712-.727v-29.002l.713-.369.173-.715 24.864-14.504.76.17v29.884z'/><path fill='#0288d1' d='M132.356 119.373V88.344l.72-.035 25.292 14.718.216.58v30.098z'/><path fill='#4fc3f7' d='m158.155 73.083-25.798 15.264 26.228 15.263 25.798-15.049z'/><path fill='#01579b' d='m126.476 152.623-.713-.727v-29.003l.713-.368.173-.715 24.864-14.504.76.17v29.882l-25.798 15.264'/><path fill='#0288d1' d='M100.256 138.293v-31.03l.72-.036 25.293 14.72.214.58v30.098l-26.228-14.332'/><path fill='#4fc3f7' d='m126.046 92.003-25.799 15.264 26.229 15.264 25.798-15.049z'/></g></svg>",
+ "serverless": "<svg viewBox='0 0 32 32'><path fill='#ef5350' d='M12.897 6H2v4h9.613zm4.201 0-1.284 4H30V6zm-2.568 8-1.283 4H30v-4zm-4.2 0H2v4h7.046zm1.633 8-1.283 4H30v-4zm-4.201 0H2v4h4.479z'/></svg>",
+ "settings": "<svg fill='none' viewBox='0 0 24 24'><path d='M0 0h24v24H0z'/><path fill='#42a5f5' d='M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46a.5.5 0 0 0-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65A.49.49 0 0 0 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1a.6.6 0 0 0-.18-.03c-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46a.5.5 0 0 0 .61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1q.09.03.18.03c.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64zm-1.98-1.71c.04.31.05.52.05.73s-.02.43-.05.73l-.14 1.13.89.7 1.08.84-.7 1.21-1.27-.51-1.04-.42-.9.68c-.43.32-.84.56-1.25.73l-1.06.43-.16 1.13-.2 1.35h-1.4l-.19-1.35-.16-1.13-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7-1.06.43-1.27.51-.7-1.21 1.08-.84.89-.7-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13-.89-.7-1.08-.84.7-1.21 1.27.51 1.04.42.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43.16-1.13.2-1.35h1.39l.19 1.35.16 1.13 1.06.43c.43.18.83.41 1.23.71l.91.7 1.06-.43 1.27-.51.7 1.21-1.07.85-.89.7zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4m0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2'/></svg>",
+ "shader": "<svg viewBox='0 0 24 24'><path fill='#ab47bc' d='M10.469 17.102a4.083 4.083 0 1 1-8.166 0 4.083 4.083 0 0 1 8.166 0m5.069-9.666a4.083 4.083 0 1 0-5.446 3.296c3.13 1.231 3.867 2.474 3.475 5.828a4.083 4.083 0 1 0 5.446-3.295c-3.13-1.232-3.869-2.475-3.475-5.829'/></svg>",
+ "silverstripe": "<svg viewBox='0 0 24 24'><path fill='#0288d1' d='M10.097 15.432a3.513 3.513 0 0 0 .914-4.882l-2.896 1.982a1.76 1.76 0 0 1-2.44-.46 1.76 1.76 0 0 1 .46-2.443L8.78 7.82l1.697-1.164a3.513 3.513 0 0 0 .915-4.883L8.496 3.759 4.152 6.728l-.005.006a5.266 5.266 0 0 0-1.368 7.324c1.635 2.402 4.917 3.015 7.318 1.374m3.81-6.864-.022.016za3.513 3.513 0 0 0-.914 4.882l2.895-1.982a1.75 1.75 0 0 1 2.442.46 1.756 1.756 0 0 1-.454 2.443l-2.646 1.81-1.702 1.164a3.513 3.513 0 0 0-.915 4.883l2.896-1.982 4.343-2.974a5.27 5.27 0 0 0 1.374-7.324 5.25 5.25 0 0 0-7.319-1.38'/></svg>",
+ "simulink": "<svg viewBox='0 0 4.233 4.233'><path fill='#9e9e9e' d='M.53 1.323v.264h2.38v.794h.265V1.323z'/><path fill='#ff6e40' d='M2.381 2.381h1.323v1.323H2.381z'/><path fill='#64b5f6' d='m2.381 1.455-1.587.926V.53z'/></svg>",
+ "siyuan": "<svg viewBox='0 0 32 32'><path fill='#e53935' d='M2 11.976 10 4v16l-8 8Z'/><path fill='#455a64' d='M30 11.976 22 4v15.99L30 28ZM10 4l6 6v16l-6-6Z'/><path fill='#e53935' d='m22 4-6 6v16l6-6.01Z'/></svg>",
+ "sketch": "<svg viewBox='0 0 24 24'><path fill='#ffc107' d='M15.705 9.221h2.779l-4.632 6.484m-3.705-6.484h3.705L12 16.631m-6.484-7.41h2.779l1.852 6.484M14.78 4.59h1.852l1.853 2.779h-2.78m-4.63-2.779h1.852l.926 2.779h-3.705M7.368 4.59h1.853l-.926 2.78h-2.78m.927-4.631L2.737 8.294 12 21.263l9.262-12.968-3.705-5.557z'/></svg>",
+ "slim": "<svg viewBox='0 0 32 32'><path fill='#f57f17' d='M23 2H9a7 7 0 0 0-7 7v14a7 7 0 0 0 7 7h14a7 7 0 0 0 7-7V9a7 7 0 0 0-7-7m-5 14-4-6v6H6a10 10 0 0 1 20 0Z'/></svg>",
+ "slint": "<svg viewBox='0 0 16 16'><path fill='#2979ff' d='M12 1 3 7l5 2-2-2Z'/><path fill='#2979ff' d='m4 15 9-6-5-2 2 2Z'/></svg>",
+ "slug": "<svg viewBox='0 0 24 24'><path fill='#f9a825' d='m9.164 21.221-.983-.056c-3.402-.19-5.654-.714-6.125-1.427-.136-.205-.146-.515-.022-.681.115-.154.377-.28.744-.355l.313-.064.373.122c1.568.517 2.903.589 4.875.263.82-.135 1.26-.29 1.736-.613.183-.124.26-.152.413-.152.62-.002 1.581-.168 2.066-.357 1.392-.544 2.655-2.023 2.979-3.49.159-.717.072-1.83-.211-2.693l-.13-.397.028-.747c.023-.606.047-.818.126-1.123.29-1.108.991-1.878 1.957-2.145.374-.103 1.17-.093 1.589.02a3.34 3.34 0 0 1 1.595.941c.505.548.707 1.025.735 1.738.021.55-.034.809-.283 1.316-.211.43-.542.833-.909 1.107-.15.113-.302.23-.336.26-.052.046-.067.192-.087.837-.014.43-.053.986-.086 1.235-.458 3.354-2.234 5.421-5.307 6.174-1.023.25-1.37.283-3.183.292-.908.005-1.748.002-1.867-.005m-3.967-2.877-.45-.07-.3-.322c-1.514-1.613-2.085-4.002-1.43-5.98.646-1.953 2.32-3.39 4.496-3.86.557-.121 1.912-.133 2.437-.023a6.7 6.7 0 0 1 1.631.56c1.828.904 2.982 2.495 3.205 4.421.111.955-.13 1.793-.763 2.651-.712.966-1.618 1.525-2.681 1.654l-.233.028.267-.277c.475-.495.596-.93.596-2.15 0-.683-.011-.831-.087-1.134-.182-.721-.476-1.239-.967-1.705-.643-.611-1.44-.923-2.349-.92-1.295.005-2.196.828-2.196 2.006 0 .398.069.695.24 1.033.135.269.446.62.68.769.368.233.984.35 1.195.225a.387.387 0 0 0 .096-.61c-.09-.101-.15-.126-.364-.154-.337-.043-.437-.086-.642-.275-.26-.239-.395-.57-.397-.964-.002-.269.014-.348.11-.543.133-.27.396-.497.703-.603.327-.114.98-.096 1.386.038.854.281 1.456.918 1.699 1.797.054.196.07.401.07.903 0 .57-.011.683-.092.941-.103.327-.31.758-.488 1.019-.295.429-.853.927-1.283 1.144-.691.348-2.98.573-4.09.4zM19.199 6.578l-.244-.078.14-.4c.537-1.544 1.334-2.521 2.057-2.521.567 0 1.043.588.862 1.067-.045.12-.244.345-.37.419-.044.025-.31.054-.597.065-.497.019-.524.024-.688.139-.221.154-.503.575-.69 1.03-.081.2-.166.361-.188.36a3 3 0 0 1-.282-.081m-2.185-.06c-.068-.115-.127-.837-.126-1.545.001-.639.015-.863.07-1.08.194-.765.529-1.121 1.055-1.121.588 0 .932.42.782.957-.072.258-.183.365-.591.568-.312.155-.394.216-.496.369-.176.262-.228.59-.2 1.253l.023.538-.165.027c-.09.014-.202.035-.249.047s-.092.006-.103-.012z'/></svg>",
+ "smarty": "<svg viewBox='0 0 32 32'><path fill='#ffca28' d='M16 5a7 7 0 0 1 4.198 12.601l-1.198.9V21h-6v-2.498l-1.198-.9a7 7 0 0 1 3.362-12.554A7 7 0 0 1 16 5m0-3a10 10 0 0 0-1.176.067A10 10 0 0 0 10 20.001V22a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-2a10 10 0 0 0-6-18m-4 24h8v2a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2z'/></svg>",
+ "sml": "<svg viewBox='0 0 24 24'><path fill='#f44336' d='M4.747 6.562h6.437c1.202 0 2.176.974 2.176 2.176v8.702h-2.176V8.738H9.008v7.614H6.833V8.738H4.746v8.702H2.481V8.738a2.176 2.176 0 0 1 2.266-2.176m10.244 0h2.176v8.702h4.352v2.176H14.99z'/></svg>",
+ "snakemake": "<svg viewBox='0 0 16 16'><path fill='#00897b' d='M6.635 11.014c1.237 0 2.473-.006 3.71.002 1.273.009 2.127 1.282 1.76 2.608-.208.754-.864 1.353-1.586 1.36-1.711.019-3.426.07-5.133-.02-1.558-.084-2.674-1.028-3.369-2.538-.313-.681.121-1.403.828-1.408 1.263-.008 2.527-.002 3.79-.002z'/><path fill='#00897b' d='M8.19 6.022c.744 0 1.489-.003 2.233 0 2.266.01 4.026 1.934 4.061 4.41.018 1.236-.35 2.298-1.118 3.198-.061.072-.157.11-.236.163-.013-.097-.047-.196-.036-.29.217-1.871-1.058-3.473-2.82-3.498-1.506-.022-3.013-.002-4.52-.01-.88-.004-1.587-.639-1.771-1.569-.243-1.227.607-2.392 1.759-2.402.816-.007 1.632-.001 2.448-.002'/><path fill='#00897b' d='M11.242 3.292c.296-.006.518-.264.507-.589-.011-.312-.237-.536-.532-.525-.282.01-.512.27-.504.567.007.29.261.553.529.547m-8.3 5.473c-.457-.45-.784-1.005-1.007-1.627-.9-2.52.466-5.374 2.874-5.996a4 4 0 0 1 .985-.114c1.534-.012 3.067-.02 4.6-.001 1.697.02 2.933.888 3.721 2.516.342.706-.071 1.449-.806 1.456-1.058.011-2.116.003-3.174.003-1.417 0-2.833-.006-4.25.001-1.442.008-2.65 1.145-2.85 2.691-.034.257.008.525.01.788.002.088-.01.177-.017.266z'/></svg>",
+ "snapcraft": "<svg viewBox='0 0 1.28 1.28'><path fill='#81c784' d='M.76.36 1 .48.76.72zm-.48.8.44-.4-.2-.2zM.12.12l.6.6V.36z'/><path fill='#ff6e40' d='M1.12.36.76.32l.4.2z'/></svg>",
+ "snowpack": "<svg viewBox='-30 -94 700 700'><path fill='#cfd8dc' d='M600.53 440.27 344.04 41.29a28.5 28.5 0 0 0-48.092 0L39.458 440.27a28.499 28.499 0 0 0 24.046 43.639h512.98a28.499 28.499 0 0 0 24.046-43.639M320 108.97l75.7 118.45H320l-56.998 56.998-33.842-33.842z'/></svg>",
+ "snowpack_light": "<svg viewBox='-30 -94 700 700'><path fill='#607d8b' d='M600.53 440.27 344.04 41.29a28.5 28.5 0 0 0-48.092 0L39.458 440.27a28.499 28.499 0 0 0 24.046 43.639h512.98a28.499 28.499 0 0 0 24.046-43.639M320 108.97l75.7 118.45H320l-56.998 56.998-33.842-33.842z'/></svg>",
+ "snyk": "<svg viewBox='0 0 32 32'><path fill='#607d8b' d='M25.338 20.622c-.337-.62.142-2.09.142-2.09-2.745-2.478-3.432-7.645-3.432-7.645-.512 1.628-1.626 6.428-1.626 6.428a16 16 0 0 0-4.38-.674h-.017q-.192 0-.38.007v11.351h10.323c.034-2.228-.63-7.377-.63-7.377M21.288 24h-3.205a4.2 4.2 0 0 1 2.926-1.409c1.695 0 .428 1.265.278 1.409Z'/><path fill='#90a4ae' d='M11.666 17.316s-1.115-4.8-1.627-6.43c0 .004-.687 5.169-3.432 7.646 0 0 .48 1.47.142 2.09 0 0-.665 5.15-.63 7.377H16V16.64a15.3 15.3 0 0 0-4.334.676M10.77 24c-.149-.144-1.416-1.409.278-1.409A4.2 4.2 0 0 1 13.975 24Z'/><path fill='#455a64' d='M21.555 25.209H18v.033a2.196 2.196 0 0 0 2.333 1.977 2.196 2.196 0 0 0 2.333-1.977v-.033Z'/><path fill='#fafafa' d='M18.888 25.209v.033a1.344 1.344 0 0 0 2.667 0v-.033Z'/><path fill='#37474f' d='M20.633 25.209h-.625l-.016.003c.077.015.12.17.103.272a.346.346 0 0 1-.351.27.56.56 0 0 0 .494.401.58.58 0 0 0 .668-.439c.033-.209-.081-.507-.273-.507'/><path fill='#455a64' d='M12.972 25.209H9.417v.033a2.196 2.196 0 0 0 2.333 1.977 2.196 2.196 0 0 0 2.334-1.977v-.033Z'/><path fill='#fafafa' d='M10.306 25.209v.033a1.26 1.26 0 0 0 1.333 1.176 1.26 1.26 0 0 0 1.333-1.176v-.033Z'/><path fill='#37474f' d='M12.051 25.209h-.626l-.016.003c.077.015.12.17.104.272a.346.346 0 0 1-.352.27.565.565 0 0 0 .494.401.58.58 0 0 0 .668-.439c.033-.209-.08-.507-.272-.507'/><path fill='#607d8b' d='m9.775 4-1.3 2.098C8.017 6.846 4 13.468 4 15.86V16l1.84 4.31A54.4 54.4 0 0 0 5.288 28h1.775a56 56 0 0 1 .588-7.7l.024-.184-.333-.788a10.7 10.7 0 0 0 2.5-3.887l.053.337 1.817 2.624.71-.268a11.8 11.8 0 0 1 3.603-.687 12.1 12.1 0 0 1 3.623.688l.708.266 1.814-2.624.072-.438a10.9 10.9 0 0 0 2.498 3.95l-.349.826.024.185a56.5 56.5 0 0 1 .593 7.7h1.757a55 55 0 0 0-.572-7.688L28 16.001v-.14c0-2.393-3.986-9.015-4.441-9.763L22.284 4 20.46 15.237l-.843 1.22a13.3 13.3 0 0 0-3.592-.575 13.2 13.2 0 0 0-3.578.575l-.842-1.22-.641-3.939.002-.019-.005-.001Zm-.98 5.013.374 2.29a14.9 14.9 0 0 1-2.538 6.335l-.805-1.905c.101-1.241 1.569-4.215 2.97-6.72Zm14.476 0c1.4 2.505 2.869 5.479 2.97 6.72l-.786 1.86a15.3 15.3 0 0 1-2.528-6.468Z'/></svg>",
+ "solidity": "<svg viewBox='0 0 24 24'><g fill='#0288d1'><path d='m5.747 14.046 6.254 8.61 6.252-8.61-6.254 3.807z'/><path d='M11.999 1.343 5.747 11.83l6.252 3.807 6.253-3.807z'/></g></svg>",
+ "sonarcloud": "<svg viewBox='0 0 24 24'><path fill='#ef6c00' d='M11.985 2.949c-3.269 0-5.909 2.745-5.936 6.12-2.332.834-4.022 3.116-4.022 5.813 0 3.392 2.663 6.169 5.948 6.169 1.513-.003 2.943-.625 4.025-1.675 1.081 1.052 2.513 1.673 4.026 1.675 3.278 0 5.947-2.77 5.947-6.17v-.001c0-1.145-.314-2.26-.891-3.237a8.3 8.3 0 0 0-1.192-1.379l-.089-.081a5 5 0 0 0-.163-.14l-.02-.016-.037-.03a5.7 5.7 0 0 0-1.666-.945c-.036-3.36-2.669-6.103-5.93-6.103m.007 1.937c2.242 0 4.072 1.894 4.072 4.238v.002a4.32 4.32 0 0 1-1.717 3.46h-.002a.985.985 0 0 0-.218 1.33l.002.002c.179.262.47.41.766.41a.9.9 0 0 0 .546-.182c1.04-.78 1.769-1.882 2.16-3.115a4.24 4.24 0 0 1 2.51 3.855c-.006 2.337-1.836 4.234-4.085 4.234-2.24 0-4.07-1.895-4.071-4.238v-.002a.954.954 0 0 0-.932-.964h-.007a.95.95 0 0 0-.936.966v.002c0 1.08.317 2.077.788 2.961a3.97 3.97 0 0 1-2.894 1.28c-2.242 0-4.075-1.897-4.075-4.24 0-2.341 1.833-4.238 4.075-4.238.487 0 .957.09 1.412.258l.007.004.016.004.005.002.008.004c.07.025.154.061.23.098.08.04.156.09.155.09a.913.913 0 0 0 1.32-.11.98.98 0 0 0-.102-1.347l-.002-.002c-.362-.318-.864-.504-.994-.552h-.002a5.8 5.8 0 0 0-2.047-.374h-.01c.206-2.15 1.91-3.836 4.023-3.836z'/></svg>",
+ "spwn": "<svg viewBox='0 0 24 24'><g transform='translate(13.512 10.42)scale(.06153)'><circle cx='30.125' cy='-29' r='110.12' fill='#e040fb'/><circle cx='-79.266' cy='80.375' r='110.12' fill='#00bfa5'/><path fill='#f5f5f5' d='m30.875-29.75-55.437-.001-55.437.001 27.624 27.624-57.45 57.45 56.063 56.063 57.45-57.45 27.188 27.188V25.686z' data-mit-no-recolor='true'/></g></svg>",
+ "stackblitz": "<svg viewBox='0 0 16 16'><path fill='#2196f3' d='m5 15 8-8H9l2-6-8 8h4z'/></svg>",
+ "stan": "<svg stroke-linejoin='round' stroke-miterlimit='1.414' clip-rule='evenodd' viewBox='0 0 500 500'><path fill='#e53935' d='M249.98 46C137.31 46 46 137.353 46 250.02c0 53.209 20.377 101.65 53.74 137.96 24.726-7.494 49.697-14.465 70.033-19.775 44.666-11.665 113.39-29.452 150.29-51.658-12.161-7.795-33.029-16.267-82.541-31.932-49.553-15.678-104.51-32.254-100.85-49.371 0 0 .132-.478.55-1.277-.11.177-.282.343-.386.521 8.18-20.906 32.893-75.656 32.893-75.656 17.725-29.087 96.193-49.896 150.93-65.023 12.767-3.528 28.016-7.86 44.062-12.508-32.692-22.279-72.2-35.303-114.75-35.303zm181.68 111.15c-36.813 5.94-152.95 27.021-182.71 33.736-12.109 2.732-28.745 6.302-45.57 10.84 23.794 11.702 31.215 17.554 64.1 29.701 32.943 12.169 82.342 39.919 76.84 61.621 0 0-.237.628-.633 1.51-4.22 11.592-17.59 41.406-17.59 41.406-18.634 18.179-103.32 38.34-159.08 49.072-17.787 3.423-38.089 7.38-58.674 11.742 36.688 35.408 86.612 57.22 141.63 57.22 112.67 0 204.02-91.313 204.02-203.98 0-33.447-8.07-65.012-22.338-92.868zM170.2 212.377c-.545.214-1.078.431-1.615.648.522-.215 1.075-.432 1.615-.648m-5.75 2.408q-2.135.93-4.166 1.893c1.29-.62 2.731-1.257 4.166-1.893m-5.098 2.352c-4.528 2.193-8.595 4.514-12.092 6.97 3.069-2.132 7-4.474 12.092-6.97m-13.928 8.297c-.576.44-1.178.875-1.714 1.324.517-.425 1.119-.876 1.714-1.324'/></svg>",
+ "steadybit": "<svg viewBox='0 0 24 24'><path fill='#e53935' d='M19.2 10.7c-.2 0-.3 0-.5.1L16.3 7c.6-.5 1.1-1.3 1.1-2.2 0-1.6-1.3-3-3-3s-3 1.3-3 3c0 .3 0 .5.1.8L7 7.9c-.5-.6-1.3-1-2.2-1-1.6 0-3 1.3-3 3s1.3 3 3 3c.2 0 .4 0 .6-.1l2.3 4.3c-.6.5-.9 1.3-.9 2.1 0 1.6 1.3 3 3 3s3-1.3 3-3c0-.2 0-.4-.1-.6l4.2-3.1c.5.7 1.4 1.1 2.3 1.1 1.6 0 3-1.3 3-3s-1.4-2.9-3-2.9m-4.8-6.9c.6 0 1 .5 1 1 0 .6-.5 1-1 1s-1-.5-1-1c0-.6.4-1 1-1m-9.6 7.1c-.6 0-1-.5-1-1 0-.6.5-1 1-1s1 .5 1 1c.1.6-.4 1-1 1m2 1.2c.6-.5 1-1.3 1-2.2 0-.3 0-.5-.1-.7l4.5-2.4c.2.2.4.4.7.5l-2.8 9h-.3c-.3 0-.6.1-.8.1zm3 8.2c-.6 0-1-.5-1-1 0-.6.5-1 1-1s1 .5 1 1-.4 1-1 1m6.4-6.6c0 .2 0 .3.1.5l-4.2 3.1-.6-.6 2.9-9h.1c.2 0 .4 0 .6-.1l2.3 3.7c-.7.6-1.2 1.5-1.2 2.4m3 1c-.6 0-1-.5-1-1 0-.6.5-1 1-1s1 .5 1 1c0 .6-.5 1-1 1'/></svg>",
+ "stencil": "<svg viewBox='0 0 32 32'><path fill='#448aff' d='m8 12-6 8h22l6-8zm6.5-8L10 10h11l4.5-6zm3 24 4.5-6H11l-4.5 6z'/></svg>",
+ "stitches": "<svg viewBox='0 0 64 64'><g fill='#cfd8dc' clip-rule='evenodd'><path d='M32 8.812C19.193 8.812 8.81 19.193 8.81 32S19.193 55.189 32 55.189 55.188 44.807 55.188 32 44.807 8.812 32 8.812M5.27 32C5.27 17.238 17.239 5.27 32 5.27S58.73 17.239 58.73 32 46.761 58.73 32 58.73 5.27 46.761 5.27 32'/><path d='M57.179 37.624 24.384 56.558l-.886-1.533L56.294 36.09zM40.826 9.3 8.031 28.236l-.885-1.533L39.941 7.767zm6.527 25.024a.887.887 0 0 1-.324 1.21L17.214 52.747a.885.885 0 0 1-.885-1.534L46.143 34a.885.885 0 0 1 1.21.324m.967-22.422c.245.424.1.965-.323 1.21L18.183 30.325a.885.885 0 0 1-.886-1.534l29.814-17.213a.885.885 0 0 1 1.21.324z'/><path d='M23.944 25.844a.885.885 0 0 1 1.239-.184L41.15 37.499a.885.885 0 1 1-1.054 1.422L24.128 27.08a.885.885 0 0 1-.184-1.238zm5.963-3.442a.885.885 0 0 1 1.238-.184l15.967 11.838a.885.885 0 0 1-1.054 1.422L30.09 23.64a.885.885 0 0 1-.183-1.239zM17.02 29.043a.885.885 0 0 1 1.235-.205l16.92 12.094a.885.885 0 1 1-1.03 1.44l-16.92-12.094a.885.885 0 0 1-.205-1.235'/></g></svg>",
+ "stitches_light": "<svg viewBox='0 0 64 64'><g fill='#546e7a' clip-rule='evenodd'><path d='M32 8.812C19.193 8.812 8.81 19.193 8.81 32S19.193 55.189 32 55.189 55.188 44.807 55.188 32 44.807 8.812 32 8.812M5.27 32C5.27 17.238 17.239 5.27 32 5.27S58.73 17.239 58.73 32 46.761 58.73 32 58.73 5.27 46.761 5.27 32'/><path d='M57.179 37.624 24.384 56.558l-.886-1.533L56.294 36.09zM40.826 9.3 8.031 28.236l-.885-1.533L39.941 7.767zm6.527 25.024a.887.887 0 0 1-.324 1.21L17.214 52.747a.885.885 0 0 1-.885-1.534L46.143 34a.885.885 0 0 1 1.21.324m.967-22.422c.245.424.1.965-.323 1.21L18.183 30.325a.885.885 0 0 1-.886-1.534l29.814-17.213a.885.885 0 0 1 1.21.324z'/><path d='M23.944 25.844a.885.885 0 0 1 1.239-.184L41.15 37.499a.885.885 0 1 1-1.054 1.422L24.128 27.08a.885.885 0 0 1-.184-1.238zm5.963-3.442a.885.885 0 0 1 1.238-.184l15.967 11.838a.885.885 0 0 1-1.054 1.422L30.09 23.64a.885.885 0 0 1-.183-1.239zM17.02 29.043a.885.885 0 0 1 1.235-.205l16.92 12.094a.885.885 0 1 1-1.03 1.44l-16.92-12.094a.885.885 0 0 1-.205-1.235'/></g></svg>",
+ "storybook": "<svg viewBox='0 0 32 32'><path fill='#ff4081' d='m24.95 28.948-18-.9a1 1 0 0 1-.95-1V6.906a1 1 0 0 1 .9-.995l18-1.905A1 1 0 0 1 26 5v22.949a1 1 0 0 1-1.05.998Z'/><path fill='#fafafa' d='m20 8.52.19-4.242 3.649-.275.16 4.37a.28.28 0 0 1-.276.283.3.3 0 0 1-.188-.063L22.123 7.52l-1.668 1.23a.29.29 0 0 1-.398-.055A.27.27 0 0 1 20 8.52m-2.128 6.647c0 .487 3.448.25 3.912-.094 0-3.324-1.87-5.073-5.298-5.073-3.421 0-5.345 1.774-5.345 4.436 0 4.642 6.561 4.735 6.561 7.266a1.022 1.022 0 0 1-1.164 1.13c-1.047 0-1.459-.512-1.413-2.242 0-.375-3.984-.494-4.101 0-.308 4.198 2.426 5.41 5.56 5.41C19.619 26 22 24.45 22 21.658c0-4.973-6.653-4.842-6.653-7.31a1.08 1.08 0 0 1 1.243-1.13c.478 0 1.354.08 1.282 1.949'/></svg>",
+ "stryker": "<svg viewBox='0 0 24 24'><g fill='#ef5350'><path d='M11.095 2.498v1.404a8.14 8.14 0 0 0-7.194 7.193H2.499v1.81h1.404a8.14 8.14 0 0 0 7.193 7.194v1.403h1.81v-1.403a8.14 8.14 0 0 0 7.193-7.194h1.404v-1.81h-1.404a8.14 8.14 0 0 0-7.193-7.193V2.498zm0 3.231v1.294h1.81V5.729a6.315 6.315 0 0 1 5.366 5.366h-1.294v1.81h1.294a6.315 6.315 0 0 1-5.366 5.366v-1.294h-1.81v1.294a6.315 6.315 0 0 1-5.366-5.366h1.294v-1.81H5.729a6.315 6.315 0 0 1 5.366-5.366' style='font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;font-variation-settings:normal;inline-size:0;isolation:auto;mix-blend-mode:normal;shape-margin:0;shape-padding:0;text-decoration-color:#000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal'/><path d='M10.945 2.348V3.79a8.26 8.26 0 0 0-7.154 7.154H2.347v2.11h1.444a8.26 8.26 0 0 0 7.154 7.154v1.443h2.11V20.21a8.26 8.26 0 0 0 7.154-7.154h1.443v-2.11H20.21a8.26 8.26 0 0 0-7.155-7.154V2.348zm.299.3h1.512v1.39l.132.013a7.99 7.99 0 0 1 7.06 7.06l.015.133h1.388v1.512h-1.388l-.014.133a7.99 7.99 0 0 1-7.06 7.06l-.133.014v1.389h-1.512v-1.39l-.133-.013a7.99 7.99 0 0 1-7.06-7.06l-.014-.133H2.648v-1.512h1.389l.014-.133a7.99 7.99 0 0 1 7.06-7.06l.133-.014zm0 2.909-.17.023a6.466 6.466 0 0 0-5.494 5.494l-.024.17h1.317v1.512H5.556l.024.17a6.466 6.466 0 0 0 5.494 5.494l.17.023v-1.316h1.512v1.316l.17-.023a6.466 6.466 0 0 0 5.494-5.494l.023-.17h-1.316v-1.512h1.316l-.023-.17a6.466 6.466 0 0 0-5.495-5.494l-.17-.023v1.316h-1.511zm-.299.373v1.244h2.11V5.93a6.13 6.13 0 0 1 5.015 5.015h-1.244v2.11h1.244a6.13 6.13 0 0 1-5.016 5.015v-1.244h-2.109v1.244a6.13 6.13 0 0 1-5.016-5.015h1.244v-2.11H5.93a6.13 6.13 0 0 1 5.016-5.015z'/></g></svg>",
+ "sty.clone": "<svg viewBox='0 0 1024 1024'><path fill='#b388ff' d='M80 192 64 320h32c16-80 16-96 63.242-96H176c8.837 0 16 7.163 16 16v352c0 8.837 0 16-32 16h-32v32h192v-32h-32c-32 0-32-7.163-32-16V240c0-8.837 7.163-16 16-16h16c48 0 48 16 64 96h32l-16-128zm560 0v32c16 0 45.713 0 52.57 16L776 434.666 708.57 592c-6.857 16-52.57 16-68.57 16v32h128v-32s-34.285 0-27.428-16L792 472l51.428 120c3.103 7.24-1.52 16-11.428 16v32h128v-32c-16 0-45.713 0-52.57-16L824 397.334 891.43 240c6.857-16 52.57-16 68.57-16v-32H832v32s34.285 0 27.428 16L808 360l-51.428-120c-3.103-7.24 1.52-16 11.428-16v-32zM320 384v32h32c32 0 32 7.163 32 16v352c0 8.837 0 16-32 16h-32v32h304l16-128h-32c-16 80-16 96-64 96h-64c-32 0-32-7.163-32-16V624h80c8.837 0 16 0 16 32v16h32V544h-32v16c0 32-7.163 32-16 32h-80V432c0-8.837 0-16 32-16h64c48 0 48 16 64 96h32l-16-128z'/></svg>",
+ "stylable": "<svg viewBox='0 0 30 30'><g stroke-width='.247'><path fill='#66bb6a' d='M10.942 3.615c-1.944.827-6.386 5.085-6.401 8.44.026 3.467 4.457 7.612 6.401 8.439.446.19 1.137.451 1.868.451.666 0 1.23-.328 1.803-1.019.965-1.16.85-2.828-.436-3.775-1.539-1.133-2.798-2.149-2.824-4.097.026-1.947 1.285-3.307 2.824-4.44 1.285-.946 1.536-2.382.571-3.543-.574-.69-1.272-.908-1.938-.908-.73 0-1.422.262-1.868.452'/><path fill='#01579b' d='M12.711 8.934q-.532.61-.984 1.23c-.502.64-.934 1.314-1.284 1.992l-.004.004-.015.032-.026.052c-.44.874-.746 1.754-.892 2.582l-.002.01c-.568 2.648.237 4.923 2.625 6.025.944.329 1.753-.052 2.49-.89.853-.97.958-2.896-.38-3.82-1.878-1.296-2.836-2.532-2.825-4.116.011-1.57.887-2.627 1.297-3.101'/><path fill='#ff1744' d='M17.23 26.837c-.663 0-1.358-.218-1.929-.908-.96-1.16-.709-2.595.57-3.541 1.531-1.132 2.783-2.49 2.809-4.437-.026-1.947-1.28-3.24-2.809-4.377-1.13-.84-1.53-2.442-.57-3.602.571-.69 1.266-.908 1.93-.908.726 0 1.414.262 1.858.452 1.934.826 6.336 4.958 6.37 8.435-.01 3.277-4.436 7.608-6.37 8.435-.444.189-1.132.451-1.859.451'/><path fill='#b71c1c' d='M17.219 9.02c-.717-.011-1.419.324-1.975.953-.857.97-.713 2.711.633 3.636 1.887 1.296 2.787 2.782 2.776 4.366-.013 1.795-1.161 2.919-1.448 3.269a8.7 8.7 0 0 0 1.7-1.656 1.3 1.3 0 0 1-.106.172 8.3 8.3 0 0 0 1.248-2.026c.026-.056.058-.128.09-.207l.025-.06a9.3 9.3 0 0 0 .516-1.75c.135-.655.207-1.352.119-1.936q-.007-.089-.016-.176c-.158-1.946-1.09-3.65-2.845-4.457a2.3 2.3 0 0 0-.717-.128' style='mix-blend-mode:multiply'/></g></svg>",
+ "stylelint": "<svg viewBox='0 0 412 395'><g fill='#cfd8dc' transform='translate(31.478 29.499)scale(.84775)'><path d='M208.8 393.05c45.057-161.12 43.75-161.85 76.32-276.73l7.832 4.523c4.255 2.458 7.738.448 7.738-4.455V61.602c8.643-30.27 15.416-53.66 17.4-60.693h35.287l58.618 54.304-38.498 33.27 29.11 31.473-191.86 273.09c-.938 1.542-2.244 1.19-1.947 0zm20.96-347.28c1.733 0 3.148.958 3.148 2.147v28.077c0 1.186-1.415 2.15-3.147 2.15h-47.396c-1.742 0-3.153-.96-3.153-2.15V47.917c0-1.185 1.41-2.147 3.153-2.147h47.396z'/><path d='m288.26 14.688-52.14 30.1c.605.92.973 1.98.973 3.136v28.078c0 1.457-.565 2.77-1.496 3.83l52.663 30.402c3.59 2.073 6.535.377 6.535-3.764V18.456c0-4.145-2.944-5.836-6.535-3.768M175.02 76V47.923c0-1.15.368-2.21.966-3.13l-52.14-30.105c-3.588-2.068-6.53-.376-6.53 3.768v88.013c0 4.14 2.938 5.84 6.53 3.76l52.66-30.405c-.926-1.06-1.487-2.37-1.487-3.827z'/><path d='M201.25 393.05h1.947c-45.05-161.12-43.753-161.85-76.32-276.73l-7.833 4.523c-4.253 2.458-7.737.448-7.737-4.455V61.602C102.662 31.332 95.892 7.942 93.902.909H58.619L.002 55.213l38.494 33.27-29.11 31.473z'/><circle cx='204.57' cy='122.54' r='14.231'/><circle cx='204.57' cy='207.16' r='14.231'/><circle cx='204.57' cy='291.78' r='14.23'/></g></svg>",
+ "stylelint_light": "<svg viewBox='0 0 412 395'><g fill='#546e7a' transform='translate(31.478 29.499)scale(.84775)'><path d='M208.8 393.05c45.057-161.12 43.75-161.85 76.32-276.73l7.832 4.523c4.255 2.458 7.738.448 7.738-4.455V61.602c8.643-30.27 15.416-53.66 17.4-60.693h35.287l58.618 54.304-38.498 33.27 29.11 31.473-191.86 273.09c-.938 1.542-2.244 1.19-1.947 0zm20.96-347.28c1.733 0 3.148.958 3.148 2.147v28.077c0 1.186-1.415 2.15-3.147 2.15h-47.396c-1.742 0-3.153-.96-3.153-2.15V47.917c0-1.185 1.41-2.147 3.153-2.147h47.396z'/><path d='m288.26 14.688-52.14 30.1c.605.92.973 1.98.973 3.136v28.078c0 1.457-.565 2.77-1.496 3.83l52.663 30.402c3.59 2.073 6.535.377 6.535-3.764V18.456c0-4.145-2.944-5.836-6.535-3.768M175.02 76V47.923c0-1.15.368-2.21.966-3.13l-52.14-30.105c-3.588-2.068-6.53-.376-6.53 3.768v88.013c0 4.14 2.938 5.84 6.53 3.76l52.66-30.405c-.926-1.06-1.487-2.37-1.487-3.827z'/><path d='M201.25 393.05h1.947c-45.05-161.12-43.753-161.85-76.32-276.73l-7.833 4.523c-4.253 2.458-7.737.448-7.737-4.455V61.602C102.662 31.332 95.892 7.942 93.902.909H58.619L.002 55.213l38.494 33.27-29.11 31.473z'/><circle cx='204.57' cy='122.54' r='14.231'/><circle cx='204.57' cy='207.16' r='14.231'/><circle cx='204.57' cy='291.78' r='14.23'/></g></svg>",
+ "stylus": "<svg viewBox='0 0 200 200'><path fill='#c0ca33' d='M127.22 155.91c14.639-17.51 16.362-35.594 5.023-69.178-7.176-21.241-19.089-37.603-10.334-50.807 9.33-14.065 29.135-.43 12.63 18.371l3.301 2.296c19.806 2.297 29.566-24.83 14.783-32.58-39.038-20.38-73.197 18.802-58.127 64.155 6.459 19.232 15.501 39.613 8.181 55.831-6.315 13.922-18.515 22.103-26.695 22.39-17.079.862-5.74-38.32 13.922-48.08 1.722-.86 4.162-2.009 1.866-4.88-24.255-2.726-38.464 8.468-46.645 24.113-23.825 45.497 45.21 62.289 82.096 18.37z'/></svg>",
+ "sublime": "<svg viewBox='0 0 32 32'><path fill='#ffb74d' d='m14.576 18.857-8.088-2.425a.6.6 0 0 1-.41-.516L6 10.169a.57.57 0 0 1 .4-.514l19.06-5.637a.29.29 0 0 1 .408.275v5.744a.59.59 0 0 1-.406.52l-7.864 2.424 7.994 2.307a.58.58 0 0 1 .408.513v5.664a.59.59 0 0 1-.402.521l-19.11 5.993a.285.285 0 0 1-.406-.265l-.04-5.913a.58.58 0 0 1 .403-.514Z'/></svg>",
+ "subtitles": "<svg fill='#ff9800' viewBox='0 0 32 32'><path d='M4.625 26q-1.083 0-1.854-.735C2 24.53 2 24.187 2 23.5V8.49q0-1.032.771-1.76A2.6 2.6 0 0 1 4.625 6h22.75q1.083 0 1.854.735C30 7.47 30 7.813 30 8.5v15.01q0 1.032-.771 1.76a2.6 2.6 0 0 1-1.854.73zM6 22h13v-4H6zm15 0h5v-4h-5zM6 16h5v-4H6zm7 0h13v-4H13z'/></svg>",
+ "supabase": "<svg viewBox='0 0 32 32'><path fill='#66bb6a' d='M29.92 12H16V2.85a1 1 0 0 0-1.78-.624L1.3 18.376A1 1 0 0 0 2.08 20H16v9.15a1 1 0 0 0 1.78.624l12.92-16.15A1 1 0 0 0 29.92 12'/></svg>",
+ "svelte": "<svg viewBox='0 0 300 300'><path fill='#ff5722' d='M175.94 24.328c-13.037.252-26.009 3.872-37.471 11.174L79.912 72.818a67.13 67.13 0 0 0-30.355 44.906 70.8 70.8 0 0 0 6.959 45.445 67.2 67.2 0 0 0-10.035 25.102 71.54 71.54 0 0 0 12.236 54.156c23.351 33.41 69.468 43.311 102.81 22.07l58.559-37.158a67.36 67.36 0 0 0 30.355-44.906 70.77 70.77 0 0 0-6.982-45.422 67.65 67.65 0 0 0 10.059-25.102 71.63 71.63 0 0 0-12.236-54.156v-.18c-15.324-21.925-40.453-33.727-65.342-33.246zm5.137 28.68a46.5 46.5 0 0 1 36.09 19.969 42.98 42.98 0 0 1 7.365 32.557 45 45 0 0 1-1.393 5.455l-1.123 3.37-2.986-2.247a75.9 75.9 0 0 0-22.902-11.45l-2.244-.651.201-2.246a13.16 13.16 0 0 0-2.379-8.711 13.99 13.99 0 0 0-14.953-5.412 12.8 12.8 0 0 0-3.594 1.572l-58.578 37.25a12.24 12.24 0 0 0-5.502 8.15 13.1 13.1 0 0 0 2.246 9.834 14.03 14.03 0 0 0 14.93 5.569 13.5 13.5 0 0 0 3.594-1.573l22.453-14.234a41.8 41.8 0 0 1 11.898-5.232 46.48 46.48 0 0 1 49.914 18.502 43.02 43.02 0 0 1 7.363 32.557 40.42 40.42 0 0 1-18.254 27.078l-58.58 37.316a43 43 0 0 1-11.898 5.23A46.545 46.545 0 0 1 82.81 227.14a42.98 42.98 0 0 1-7.341-32.557 38 38 0 0 1 1.39-5.41l1.102-3.37 3.008 2.246a75.9 75.9 0 0 0 22.836 11.361l2.244.65-.201 2.247a13.25 13.25 0 0 0 2.447 8.644 14.03 14.03 0 0 0 15.043 5.569 13.1 13.1 0 0 0 3.592-1.573l58.467-37.316a12.17 12.17 0 0 0 5.502-8.173 12.96 12.96 0 0 0-2.246-9.811 14.03 14.03 0 0 0-15.043-5.568 12.8 12.8 0 0 0-3.592 1.57l-22.453 14.258a42.9 42.9 0 0 1-11.877 5.209 46.52 46.52 0 0 1-49.846-18.5 43.02 43.02 0 0 1-7.297-32.557A40.42 40.42 0 0 1 96.798 96.98l58.646-37.316a42.8 42.8 0 0 1 11.811-5.21 46.5 46.5 0 0 1 13.822-1.444z'/></svg>",
+ "svelte_js.clone": "<svg viewBox='0 0 300 300'><path fill='#ffca28' d='M175.94 24.328c-13.037.252-26.009 3.872-37.471 11.174L79.912 72.818a67.13 67.13 0 0 0-30.355 44.906 70.8 70.8 0 0 0 6.959 45.445 67.2 67.2 0 0 0-10.035 25.102 71.54 71.54 0 0 0 12.236 54.156c23.351 33.41 69.468 43.311 102.81 22.07l58.559-37.158a67.36 67.36 0 0 0 30.355-44.906 70.77 70.77 0 0 0-6.982-45.422 67.65 67.65 0 0 0 10.059-25.102 71.63 71.63 0 0 0-12.236-54.156v-.18c-15.324-21.925-40.453-33.727-65.342-33.246zm5.137 28.68a46.5 46.5 0 0 1 36.09 19.969 42.98 42.98 0 0 1 7.365 32.557 45 45 0 0 1-1.393 5.455l-1.123 3.37-2.986-2.247a75.9 75.9 0 0 0-22.902-11.45l-2.244-.651.201-2.246a13.16 13.16 0 0 0-2.379-8.711 13.99 13.99 0 0 0-14.953-5.412 12.8 12.8 0 0 0-3.594 1.572l-58.578 37.25a12.24 12.24 0 0 0-5.502 8.15 13.1 13.1 0 0 0 2.246 9.834 14.03 14.03 0 0 0 14.93 5.569 13.5 13.5 0 0 0 3.594-1.573l22.453-14.234a41.8 41.8 0 0 1 11.898-5.232 46.48 46.48 0 0 1 49.914 18.502 43.02 43.02 0 0 1 7.363 32.557 40.42 40.42 0 0 1-18.254 27.078l-58.58 37.316a43 43 0 0 1-11.898 5.23A46.545 46.545 0 0 1 82.81 227.14a42.98 42.98 0 0 1-7.341-32.557 38 38 0 0 1 1.39-5.41l1.102-3.37 3.008 2.246a75.9 75.9 0 0 0 22.836 11.361l2.244.65-.201 2.247a13.25 13.25 0 0 0 2.447 8.644 14.03 14.03 0 0 0 15.043 5.569 13.1 13.1 0 0 0 3.592-1.573l58.467-37.316a12.17 12.17 0 0 0 5.502-8.173 12.96 12.96 0 0 0-2.246-9.811 14.03 14.03 0 0 0-15.043-5.568 12.8 12.8 0 0 0-3.592 1.57l-22.453 14.258a42.9 42.9 0 0 1-11.877 5.209 46.52 46.52 0 0 1-49.846-18.5 43.02 43.02 0 0 1-7.297-32.557A40.42 40.42 0 0 1 96.798 96.98l58.646-37.316a42.8 42.8 0 0 1 11.811-5.21 46.5 46.5 0 0 1 13.822-1.444z'/></svg>",
+ "svelte_ts.clone": "<svg viewBox='0 0 300 300'><path fill='#0288d1' d='M175.94 24.328c-13.037.252-26.009 3.872-37.471 11.174L79.912 72.818a67.13 67.13 0 0 0-30.355 44.906 70.8 70.8 0 0 0 6.959 45.445 67.2 67.2 0 0 0-10.035 25.102 71.54 71.54 0 0 0 12.236 54.156c23.351 33.41 69.468 43.311 102.81 22.07l58.559-37.158a67.36 67.36 0 0 0 30.355-44.906 70.77 70.77 0 0 0-6.982-45.422 67.65 67.65 0 0 0 10.059-25.102 71.63 71.63 0 0 0-12.236-54.156v-.18c-15.324-21.925-40.453-33.727-65.342-33.246zm5.137 28.68a46.5 46.5 0 0 1 36.09 19.969 42.98 42.98 0 0 1 7.365 32.557 45 45 0 0 1-1.393 5.455l-1.123 3.37-2.986-2.247a75.9 75.9 0 0 0-22.902-11.45l-2.244-.651.201-2.246a13.16 13.16 0 0 0-2.379-8.711 13.99 13.99 0 0 0-14.953-5.412 12.8 12.8 0 0 0-3.594 1.572l-58.578 37.25a12.24 12.24 0 0 0-5.502 8.15 13.1 13.1 0 0 0 2.246 9.834 14.03 14.03 0 0 0 14.93 5.569 13.5 13.5 0 0 0 3.594-1.573l22.453-14.234a41.8 41.8 0 0 1 11.898-5.232 46.48 46.48 0 0 1 49.914 18.502 43.02 43.02 0 0 1 7.363 32.557 40.42 40.42 0 0 1-18.254 27.078l-58.58 37.316a43 43 0 0 1-11.898 5.23A46.545 46.545 0 0 1 82.81 227.14a42.98 42.98 0 0 1-7.341-32.557 38 38 0 0 1 1.39-5.41l1.102-3.37 3.008 2.246a75.9 75.9 0 0 0 22.836 11.361l2.244.65-.201 2.247a13.25 13.25 0 0 0 2.447 8.644 14.03 14.03 0 0 0 15.043 5.569 13.1 13.1 0 0 0 3.592-1.573l58.467-37.316a12.17 12.17 0 0 0 5.502-8.173 12.96 12.96 0 0 0-2.246-9.811 14.03 14.03 0 0 0-15.043-5.568 12.8 12.8 0 0 0-3.592 1.57l-22.453 14.258a42.9 42.9 0 0 1-11.877 5.209 46.52 46.52 0 0 1-49.846-18.5 43.02 43.02 0 0 1-7.297-32.557A40.42 40.42 0 0 1 96.798 96.98l58.646-37.316a42.8 42.8 0 0 1 11.811-5.21 46.5 46.5 0 0 1 13.822-1.444z'/></svg>",
+ "svg": "<svg viewBox='0 0 32 32'><path fill='#ffb300' d='M29.168 14.03a2.7 2.7 0 0 0-1.968-.83 2.51 2.51 0 0 0-1.929.8h-4.443l3.078-3.078a2.835 2.835 0 0 0 2.857-2.842 2.6 2.6 0 0 0-.831-1.969 2.82 2.82 0 0 0-2.014-.788 2.67 2.67 0 0 0-1.968.788 2.36 2.36 0 0 0-.812 1.922L18 11.17V6.726a2.51 2.51 0 0 0 .8-1.929 2.7 2.7 0 0 0-.832-1.968 2.745 2.745 0 0 0-3.936 0 2.7 2.7 0 0 0-.832 1.968 2.51 2.51 0 0 0 .8 1.93v4.443l-3.138-3.138a2.36 2.36 0 0 0-.812-1.922 2.66 2.66 0 0 0-1.968-.788 2.83 2.83 0 0 0-2.014.788 2.6 2.6 0 0 0-.831 1.969 2.74 2.74 0 0 0 .831 2.013 2.8 2.8 0 0 0 2.026.829l3.078 3.078H6.729a2.51 2.51 0 0 0-1.929-.8 2.7 2.7 0 0 0-1.968.831 2.745 2.745 0 0 0 0 3.937 2.7 2.7 0 0 0 1.968.832 2.51 2.51 0 0 0 1.929-.8h4.443l-3.078 3.077a2.835 2.835 0 0 0-2.857 2.842 2.6 2.6 0 0 0 .831 1.969 2.82 2.82 0 0 0 2.014.788 2.67 2.67 0 0 0 1.968-.788 2.36 2.36 0 0 0 .812-1.922L14 20.827v4.444a2.51 2.51 0 0 0-.8 1.929 2.784 2.784 0 0 0 4.768 1.968A2.7 2.7 0 0 0 18.8 27.2a2.51 2.51 0 0 0-.8-1.929v-4.444l3.138 3.138a2.36 2.36 0 0 0 .812 1.922 2.66 2.66 0 0 0 1.968.788 2.83 2.83 0 0 0 2.014-.788 2.6 2.6 0 0 0 .831-1.969 2.74 2.74 0 0 0-.831-2.013 2.8 2.8 0 0 0-2.026-.829L20.828 18h4.443a2.51 2.51 0 0 0 1.93.8 2.784 2.784 0 0 0 1.967-4.769Z'/></svg>",
+ "svgo": "<svg viewBox='0 0 100 100'><path fill='#0288d1' d='M42.951 7.647c-.659 2.54-1.157 5.016-1.581 7.52a36.3 36.3 0 0 0-10.041 3.98c-2.023-1.43-4.084-2.814-6.268-4.084l-9.994 9.928a83 83 0 0 0 3.84 5.929 36.5 36.5 0 0 0-4.668 10.568c-2.202.395-4.385.847-6.587 1.43v14.107c2.051.546 4.084.97 6.135 1.336a36.4 36.4 0 0 0 4.527 11.5 70 70 0 0 0-3.237 5.091l9.985 9.985c1.693-.978 3.303-2.032 4.874-3.115a36.2 36.2 0 0 0 11.792 4.913 74 74 0 0 0 1.233 5.618h14.173c.499-1.92.904-3.84 1.261-5.75a36.5 36.5 0 0 0 11.443-4.988c1.694 1.148 3.388 2.259 5.138 3.303l9.976-9.994a82.09 82.081 0 0 0-3.548-5.486 36.5 36.5 0 0 0 4.235-11.011c2.24-.414 4.47-.857 6.71-1.45V42.908c-2.362-.63-4.725-1.11-7.143-1.515a36.8 36.8 0 0 0-4.357-10.069 86 86 0 0 0 4.14-6.342l-10.013-9.966c-2.268 1.336-4.423 2.776-6.53 4.281a36 36 0 0 0-9.76-4.027c-.423-2.541-.931-5.082-1.609-7.623zm6.7 16.544h.076a26.4 26.4 0 0 1 9.137 1.619c.669.244 1.318.508 1.967.846a26.8 26.8 0 0 1 12.855 12.545c.254.518.49 1.045.716 1.572a26.6 26.6 0 0 1 .498 18.925c-.103.301-.207.593-.32.847a26.85 26.85 0 0 1-13.947 14.653l-.178.122a26.73 26.73 0 0 1-20.986.198.1.1 0 0 0 0-.047A26.83 26.83 0 0 1 24.9 60.517l-.132-.358a26.66 26.66 0 0 1 .527-19.913c.15-.357.31-.715.49-1.063a26.9 26.9 0 0 1 13.456-12.846 26 26 0 0 1 1.779-.687 26.7 26.7 0 0 1 8.63-1.459zm8.686 6.474a3.68 3.68 0 0 0-3.52 4.894 3.6 3.6 0 0 0 .914 1.402l-3.388 5.873a7.44 7.44 0 0 0-3.341-.612 7.33 7.33 0 0 0-6.098 3.943l-7.265-2.851v-.076a3.388 3.388 0 1 0-.64 1.657l7.265 2.851a7.34 7.34 0 0 0 2.475 7.491l-3.388 5.289a4.4 4.4 0 0 0-1.92-.32 4.517 4.517 0 1 0 3.388 1.233l3.388-5.27a7.34 7.34 0 0 0 8.526-1.544l4.235 3.162a5.364 5.364 0 1 0 10.004 1.402 5.364 5.364 0 0 0-9.006-2.757L55.74 53.29a7.31 7.31 0 0 0-1.929-9.57l3.388-5.873a3.68 3.68 0 1 0 1.148-7.18z'/></svg>",
+ "svgr": "<svg viewBox='0 0 32 32'><path fill='#ffb300' d='M16 12c7.444 0 12 2.59 12 4s-4.556 4-12 4-12-2.59-12-4 4.556-4 12-4m0-2c-7.732 0-14 2.686-14 6s6.268 6 14 6 14-2.686 14-6-6.268-6-14-6'/><path fill='#ffb300' d='M16 14a2 2 0 1 0 2 2 2 2 0 0 0-2-2'/><path fill='#ffb300' d='M10.458 5.507c2.017 0 5.937 3.177 9.006 8.493 3.722 6.447 3.757 11.687 2.536 12.392a.9.9 0 0 1-.457.1c-2.017 0-5.938-3.176-9.007-8.492C8.814 11.553 8.779 6.313 10 5.608a.9.9 0 0 1 .458-.1m-.001-2A2.87 2.87 0 0 0 9 3.875C6.13 5.532 6.938 12.304 10.804 19c3.284 5.69 7.72 9.493 10.74 9.493A2.87 2.87 0 0 0 23 28.124c2.87-1.656 2.062-8.428-1.804-15.124-3.284-5.69-7.72-9.493-10.74-9.493Z'/><path fill='#ffb300' d='M21.543 5.507a.9.9 0 0 1 .457.1c1.221.706 1.186 5.946-2.536 12.393-3.07 5.316-6.99 8.493-9.007 8.493a.9.9 0 0 1-.457-.1C8.779 25.686 8.814 20.446 12.536 14c3.07-5.316 6.99-8.493 9.007-8.493m0-2c-3.02 0-7.455 3.804-10.74 9.493C6.939 19.696 6.13 26.468 9 28.124a2.87 2.87 0 0 0 1.457.369c3.02 0 7.455-3.804 10.74-9.493C25.061 12.304 25.87 5.532 23 3.876a2.87 2.87 0 0 0-1.457-.369'/></svg>",
+ "swagger": "<svg preserveAspectRatio='xMidYMid' viewBox='0 0 256 256'><path fill='#43a047' d='M128.963 17.002a112 112 0 0 0-11.422.51 110 110 0 0 0-11.09 1.642 110 110 0 0 0-25.906 8.668 111.6 111.6 0 0 0-14.024 8.022 113 113 0 0 0-16.535 13.564A113.3 113.3 0 0 0 33.41 70.29a112 112 0 0 0-7.43 14.26 110 110 0 0 0-5.318 15.305 108.7 108.7 0 0 0-3.66 27.277 112 112 0 0 0 .521 11.53 109.3 109.3 0 0 0 8.21 32.204A111.3 111.3 0 0 0 42.546 198.4a113 113 0 0 0 15.129 15.073c2.747 2.264 5.6 4.398 8.547 6.394a113 113 0 0 0 13.867 8.022 110.7 110.7 0 0 0 20.101 7.486 108.6 108.6 0 0 0 16.004 2.984c3.626.398 7.294.614 10.99.639a110 110 0 0 0 22.471-2.162c3.636-.733 7.21-1.646 10.713-2.729a111 111 0 0 0 24.781-11.146 113 113 0 0 0 21.174-16.555 113 113 0 0 0 7.252-7.924 112 112 0 0 0 12.086-17.744 110 110 0 0 0 4.717-9.697 108 108 0 0 0 3.75-10.166 106.8 106.8 0 0 0 4.37-21.498 107 107 0 0 0 .498-11.207 107.6 107.6 0 0 0-2.115-22.271 107.5 107.5 0 0 0-6.42-20.772 110 110 0 0 0-7.344-14.328c-1.854-3.048-3.853-6-5.985-8.85a113 113 0 0 0-18.336-19.263 113 113 0 0 0-17.631-12.008 112 112 0 0 0-9.635-4.713 110.5 110.5 0 0 0-15.302-5.305 109 109 0 0 0-27.266-3.658zm-39.49 45.004c1.55.014 3.15.049 4.847.049v14.09c-1.397.099-2.677.301-3.949.263-8.58-.263-9.024 2.661-9.65 9.764-.391 4.454.15 8.984-.155 13.453-.317 4.447-.912 8.87-1.78 13.244-1.239 6.339-5.135 11.054-10.536 15.055 10.484 6.822 11.677 17.423 12.357 28.187.367 5.785.199 11.61.786 17.366.457 4.467 2.195 5.608 6.808 5.744 1.902.056 3.806.013 6 .013v13.787c-13.635 2.306-24.867-1.566-27.623-13.091a76.5 76.5 0 0 1-1.736-12.887c-.293-4.592.215-9.235-.135-13.818-.97-12.612-2.604-16.918-14.707-17.514v-15.695a23 23 0 0 1 2.633-.454c6.635-.326 9.432-2.36 10.916-8.896a74.6 74.6 0 0 0 1.193-11.123c.526-7.217.34-14.55 1.543-21.65 1.738-10.268 8.112-15.257 18.64-15.815 1.499-.08 2.998-.086 4.548-.072m81.525.088c11.709.34 19.4 6.373 20.26 19.207.444 6.633.378 13.297.803 19.93a55 55 0 0 0 1.613 10.693c1.458 5.447 4.297 7.361 10.031 7.623.94.043 1.874.203 3.162.346v15.691q-1.046.344-2.137.512c-7.684.478-11.186 3.631-11.962 11.336-.496 4.918-.455 9.892-.795 14.828a126 126 0 0 1-1.477 16.18c-1.96 9.703-8.02 14.543-18.03 15.134-3.221.19-6.465.03-9.939.03v-14.026c1.87-.116 3.52-.275 5.174-.314 5.98-.143 8.096-2.072 8.389-8.012.324-6.525.464-13.057.756-19.584.423-9.434 3.008-17.862 11.797-23.746-5.03-3.585-9.068-7.928-10.114-13.783-1.265-7.098-1.672-14.35-2.353-21.547-.337-3.598-.32-7.228-.672-10.822-.379-3.881-3.044-5.224-6.576-5.311-2.024-.049-4.056-.01-6.643-.01V62.754c3.096-.514 6.011-.739 8.713-.66m-73.96 56.695h.161a9.08 9.08 0 0 1 8.928 9.227 8.884 8.884 0 0 1-9.396 8.852 9.045 9.045 0 1 1 .307-18.078zm31.235 0c5.483-.042 9.124 3.51 9.153 8.93.03 5.565-3.423 9.127-8.87 9.15-5.539.025-9.186-3.48-9.216-8.867a8.67 8.67 0 0 1 8.934-9.213zm31.807 0c5.045.025 9.52 4.29 9.248 9.168-.284 5.29-4.906 9.683-9.46 8.916h-.07a9.134 9.134 0 0 1-9.146-9.123 9.277 9.277 0 0 1 9.428-8.96z'/></svg>",
+ "sway": "<svg viewBox='0 0 32 32'><path fill='#00e676' d='M4 6v22h19a2.4 2.4 157.5 0 0 1.707-.707l2.586-2.586A2.4 2.4 112.5 0 0 28 23V4H6a2 2 135 0 0-2 2'/><path fill='#263238' d='m18 8-6.69 7.244c-.171.186-.44.506-.656.633-.224.131-.457.157-.681.06-.23-.1-.432-.47-.517-.708-.602-1.696-1.04-3.489-1.415-5.268C7.814 8.88 8.543 8 9.647 8zm-8 15.436c-.8.855-1.997.755-2-.416V19c-.019-.47.53-.987 1-1h6zM16.045 16h-2.82L20 8.765c.373-.42 1.438-.76 2-.765h2l-6.486 7.33a2 2 0 0 1-1.469.67'/></svg>",
+ "swc": "<svg fill='none' viewBox='0 0 16 16'><path fill='#ffca28' d='m2.327 5.5-.07.01a1 1 0 0 0-.15.023 1 1 0 0 0-.158.044l-.098.04a1.4 1.4 0 0 0-.564.456l-.07.096c-.052.07-.164.348-.185.461-.038.2-.042.421-.012.569.05.246.123.411.263.602.035.048.288.316.563.595.358.365.503.523.52.559a.266.266 0 0 1-.134.346c-.041.018-.14.022-.367.023-.145 0-.288.006-.317.012a.58.58 0 0 0-.453.552.6.6 0 0 0 .259.514c.117.082.202.097.55.097.26 0 .41-.007.52-.032l.064-.013c.106-.02.414-.165.465-.22a1.6 1.6 0 0 0 .318-.323 1.6 1.6 0 0 0 .264-.683c.01-.084.01-.298.001-.347-.003-.017-.011-.064-.02-.106a1.5 1.5 0 0 0-.266-.592c-.037-.048-.29-.314-.563-.593-.542-.554-.543-.554-.53-.673.022-.184.198-.29.359-.215.038.016.466.448 1.721 1.726.918.938 1.689 1.72 1.711 1.738q.065.052.134.098c.494.33 1.132.31 1.614-.046.076-.056.223-.206.282-.285l.041-.056.135.136c.119.119.15.149.206.194.007.005.045.033.087.06.467.316 1.124.31 1.593-.017.082-.057.11-.08.197-.166a1.5 1.5 0 0 0 .398-.778c.018-.091.02-.408.005-.485a1.6 1.6 0 0 0-.262-.618 12 12 0 0 0-.553-.588 20 20 0 0 1-.519-.54.3.3 0 0 1-.02-.23.3.3 0 0 1 .116-.14c.033-.019.078-.021.388-.025l.35-.003-.01.064c-.011.075-.01.358 0 .416l.014.078c.016.082.058.21.101.304.108.236.094.22 1.29 1.442a53 53 0 0 0 1.204 1.21c.201.16.384.243.634.289a1.6 1.6 0 0 0 .388.012 1.42 1.42 0 0 0 1.233-1.26c.029-.281-.042-.476-.222-.62a.56.56 0 0 0-.635-.04.6.6 0 0 0-.29.495c-.009.18-.144.286-.31.244-.056-.013-.098-.055-1.13-1.105-.59-.6-1.086-1.115-1.103-1.142a.27.27 0 0 1 .047-.315c.064-.062.103-.07.355-.07.283 0 .365-.017.487-.102a.592.592 0 0 0-.06-1.014c-.12-.063-.104-.062-1.425-.063-1.31 0-1.376.002-1.526.043l-.065.016a2 2 0 0 0-.223.09c-.156.08-.366.255-.478.396-.077.1-.18.297-.225.429-.146.433-.086.888.165 1.271.06.092.165.206.588.64.283.29.524.542.535.562.042.084.02.214-.05.291-.058.065-.095.079-.2.079-.077 0-.103-.005-.135-.026a167 167 0 0 1-1.826-1.845c-1.345-1.371-1.803-1.83-1.854-1.862a.58.58 0 0 0-.432-.067.61.61 0 0 0-.429.455.63.63 0 0 0 .092.449c.017.026.573.6 1.235 1.275 1.096 1.12 1.205 1.232 1.218 1.281a.28.28 0 0 1-.049.254c-.058.082-.212.114-.31.062-.022-.013-.803-.8-1.735-1.748-1.344-1.37-1.712-1.738-1.785-1.787a1.4 1.4 0 0 0-.775-.257c-.063 0-.116-.002-.117 0z'/></svg>",
+ "swift": "<svg viewBox='0 0 24 24'><path fill='#ff6e40' d='M17.087 19.721c-2.36 1.36-5.59 1.5-8.86.1a13.8 13.8 0 0 1-6.23-5.32c.67.55 1.46 1 2.3 1.4 3.37 1.57 6.73 1.46 9.1 0-3.37-2.59-6.24-5.96-8.37-8.71-.45-.45-.78-1.01-1.12-1.51 8.28 6.05 7.92 7.59 2.41-1.01 4.89 4.94 9.43 7.74 9.43 7.74.16.09.25.16.36.22.1-.25.19-.51.26-.78.79-2.85-.11-6.12-2.08-8.81 4.55 2.75 7.25 7.91 6.12 12.24-.03.11-.06.22-.05.39 2.24 2.83 1.64 5.78 1.35 5.22-1.21-2.39-3.48-1.65-4.62-1.17'/></svg>",
+ "syncpack": "<svg viewBox='0 0 100 100'><path fill='#8bc34a' d='M46.677 7.772a38.8 38.8 0 0 1 10.83.571 45 45 0 0 1 7.226 2.046 53 53 0 0 1 4.89 2.263c3.022 1.662 5.888 3.666 8.338 6.106a51 51 0 0 1 3.572 3.893 47 47 0 0 1 3.354 4.953 38.2 38.2 0 0 1 3.935 9.47 20 20 0 0 1 .862 5.18c-.478-.353-.852-.82-1.257-1.256-2.523-2.845-4.942-5.794-7.413-8.701-1.111-1.267-2.18-2.596-3.395-3.769-.956.83-1.662 1.89-2.482 2.845-2.326 2.783-4.651 5.565-7.008 8.317a10.2 10.2 0 0 0-1.392-2.866 22.4 22.4 0 0 0-3.177-3.883 18.75 18.75 0 0 0-12.21-5.244 18.9 18.9 0 0 0-5.607.468 16.9 16.9 0 0 0-4.496 1.599 18.2 18.2 0 0 0-6.822 5.939 15.3 15.3 0 0 0-2.284 4.506c3.24.01 6.48-.02 9.719 0-.924 1.318-1.994 2.523-3.011 3.769-2.607 3.115-5.275 6.209-7.891 9.345a100 100 0 0 1-3.136 3.81c-1.734 2.18-3.572 4.278-5.4 6.375a9 9 0 0 1-.83-.727c-1.267-1.266-2.44-2.616-3.624-3.955-3.83-4.413-7.538-8.93-11.286-13.405-1.017-1.204-2.014-2.43-3.084-3.572-.415-.467-.81-.945-1.142-1.474 2.856 0 5.71-.042 8.556-.083a36.1 36.1 0 0 1 3.956-11.889 42 42 0 0 1 5.243-7.527 39.3 39.3 0 0 1 6.126-5.514 40.6 40.6 0 0 1 20.35-7.58z'/><path fill='#2e7d32' d='M77.567 36.502c.727.55 1.318 1.246 1.952 1.89 2.544 2.762 4.953 5.648 7.372 8.514 2.637 3.136 5.243 6.302 7.89 9.428.904 1.11 1.974 2.087 2.763 3.291-2.856.02-5.71.042-8.566.093a36.3 36.3 0 0 1-5.752 14.827c-.758 1.163-1.558 2.305-2.44 3.385A40.48 40.48 0 0 1 48.13 92.342a45 45 0 0 1-7.715-1.142 50 50 0 0 1-5.534-1.745 45 45 0 0 1-6.157-3.063 35 35 0 0 1-6.956-5.42c-1.143-1.142-2.254-2.325-3.281-3.582a47 47 0 0 1-3.354-4.932 38.3 38.3 0 0 1-3.998-9.635c-.446-1.661-.83-3.343-.861-5.067.415.239.706.633 1.038.976 2.73 3.053 5.316 6.23 7.995 9.345.997 1.142 1.941 2.346 3.052 3.385.841-.727 1.485-1.62 2.18-2.471a829 829 0 0 1 7.31-8.691c.291 1.11.893 2.108 1.506 3.063a19.74 19.74 0 0 0 7.278 6.749 18 18 0 0 0 4.89 1.734c1.911.405 3.884.612 5.826.436 1.35-.104 2.689-.384 3.997-.685a19.1 19.1 0 0 0 7.912-4.569 20.4 20.4 0 0 0 3.188-4.08c.591-.997 1.1-2.046 1.412-3.157H58.14c.665-.976 1.433-1.869 2.18-2.762 2.617-3.167 5.296-6.271 7.933-9.417 1.205-1.391 2.285-2.887 3.52-4.267.976-1.163 1.91-2.378 2.907-3.53.956-1.122 1.942-2.212 2.908-3.323z'/></svg>",
+ "systemd": "<svg viewBox='0 0 16 16'><path fill='#00e676' d='m9 8 3-2v4z'/><circle cx='6' cy='8' r='2' fill='#00e676'/><path fill='#eceff1' d='M3 5H1v6h2v-1H2V6h1zm10 0h2v6h-2v-1h1V6h-1z'/></svg>",
+ "systemd_light": "<svg viewBox='0 0 16 16'><path fill='#00c853' d='m9 8 3-2v4z'/><circle cx='6' cy='8' r='2' fill='#00c853'/><path fill='#455a64' d='M3 5H1v6h2v-1H2V6h1zm10 0h2v6h-2v-1h1V6h-1z'/></svg>",
+ "table": "<svg viewBox='0 0 24 24'><path fill='#8bc34a' d='M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2m7 1.5V9h5.5zm4 7.5h-4v2h1l-2 1.67L10 13h1v-2H7v2h1l3 2.5L8 18H7v2h4v-2h-1l2-1.67L14 18h-1v2h4v-2h-1l-3-2.5 3-2.5h1z'/></svg>",
+ "tailwindcss": "<svg viewBox='0 0 32 32'><path fill='#4db6ac' d='M23.5 12H8c.89-2.3 4.02-4 7.75-4s6.86 1.7 7.75 4M14 12h15.5c-.89 2.3-4.02 4-7.75 4s-6.86-1.7-7.75-4m3.5 8H2c.89-2.3 4.02-4 7.75-4s6.86 1.7 7.75 4M8 20h15.5c-.89 2.3-4.02 4-7.75 4S8.89 22.3 8 20'/></svg>",
+ "tape.clone": "<svg viewBox='0 0 32 32'><path fill='#ba68c8' d='m24 6 2 6h-4l-2-6h-3l2 6h-4l-2-6h-3l2 6H8L6 6H5a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h22a3 3 0 0 0 3-3V6Z'/></svg>",
+ "taskfile": "<svg viewBox='0 0 32 32'><path fill='#4db6ac' d='M4 9v14l12 6V15z'/><path fill='#b2dfdb' d='M16 3 4 9l12 6 12-6z'/><path fill='#80cbc4' d='M16 15v14l12-6V9z'/></svg>",
+ "tauri": "<svg viewBox='0 0 256 256'><path fill='#ffca28' d='M165.09 99.183a20.153 20.153 0 0 1-34.401 14.25 20.152 20.152 0 1 1 34.4-14.25z'/><path fill='#26c6da' d='M111.04 136.74c-11.13 0-20.152 9.022-20.152 20.151 0 11.13 9.022 20.152 20.152 20.152 11.129 0 20.152-9.022 20.152-20.152 0-11.129-9.023-20.15-20.152-20.15z'/><path fill='#ffca28' d='M186.7 163.76a77 77 0 0 1-26.564 10.81 54.04 54.04 0 0 0 2.657-24.366 54.05 54.05 0 0 0 33.856-35.246 54.04 54.04 0 0 0-27.73-64.21 54.044 54.044 0 0 0-67.863 16.927 89.8 89.8 0 0 0-29.495 8.61 76.94 76.94 0 0 1 86.49-53.058 76.94 76.94 0 0 1 63.888 78.829 76.94 76.94 0 0 1-35.24 61.706zM72.48 90.298l18.869 2.29a54 54 0 0 1 2.381-10.717 77 77 0 0 0-21.25 8.427' clip-rule='evenodd'/><path fill='#26c6da' d='M69.182 92.314a76.9 76.9 0 0 1 26.747-10.9 53.95 53.95 0 0 0-3.023 24.457 54.05 54.05 0 0 0-33.64 35.358 54.04 54.04 0 0 0 52.354 69.6 54.04 54.04 0 0 0 43.298-22.52 89.8 89.8 0 0 0 29.495-8.518 76.94 76.94 0 0 1-50.002 50.423 76.94 76.94 0 0 1-69.992-11.992A76.944 76.944 0 0 1 69.18 92.315zm114.22 73.462-.366.183z' clip-rule='evenodd'/></svg>",
+ "taze": "<svg viewBox='0 0 16 16'><path fill='#00c853' d='M9.196 10.555a.465.465 0 0 0-.654-.14l-2.417 1.62 1.038-2.108a.5.5 0 0 0-.195-.657.47.47 0 0 0-.643.209l-1.049 2.123-.165-2.95a.477.477 0 0 0-.497-.454.48.48 0 0 0-.444.508l.093 1.665c.039.698-.103 1.4-.41 2.023l-.473.962a.66.66 0 0 0 .283.877l1.355.698c.312.16.697.035.858-.29l.473-.961a3.9 3.9 0 0 1 1.345-1.54l1.365-.917a.484.484 0 0 0 .137-.668'/><path fill='#00695c' d='M12.9 6.175c.454-1.85-.385-3.832-2.12-4.73-1.735-.892-3.792-.398-4.977 1.072a3.066 3.066 0 0 0-3.47 1.654c-.77 1.56-.157 3.459 1.37 4.246a3.05 3.05 0 0 0 1.666.324 1.96 1.96 0 0 0 1.048 1.575 1.88 1.88 0 0 0 1.857-.075c.288.489.712.902 1.248 1.176 1.526.788 3.383.16 4.153-1.4a3.234 3.234 0 0 0-.775-3.842'/></svg>",
+ "tcl": "<svg viewBox='0 0 24 24'><path fill='#ef5350' d='M21.492 2.51S14.24 2.157 8.526 9.988c-4.385 6.008-6.018 11.504-6.018 11.504l1.842-.95c1.366-2.372 2.078-3.35 3.417-4.745 2.401.702 4.907.617 7.08-1.899-1.898-.53-3.416-.408-5.657-.18C11.706 12 13.424 11.62 15.797 12l.949-1.898c-1.709-.323-2.848-.352-4.537.038 1.87-1.32 3.17-2.06 5.486-1.937l1.148-1.832c-1.48-.104-2.372.057-4.072.475C16.3 5.46 17.695 4.834 19.726 4.71c0 0 .997-1.793 1.766-2.202z'/></svg>",
+ "teal": "<svg viewBox='0 0 32 32'><path fill='#00acc1' d='M16 2a14 14 0 1 0 14 14A14 14 0 0 0 16 2m8 14h-8V8h8Z'/></svg>",
+ "templ": "<svg viewBox='0 0 32 32'><path fill='#ffd54f' d='M8.125 24.313 1 16l7.125-8.313 3 2.625L6.25 16l4.875 5.688ZM24 7.687 31.125 16 24 24.313l-3-2.625L25.875 16 21 10.312ZM16 4l-4 24h4l4-24z'/></svg>",
+ "template": "<svg viewBox='0 0 24 24'><path fill='#90a4ae' d='M13 9h1v2h-3V7h2zm5.5 0-2.12-2.12 1.25-1.25L20 8v2h-2v1h-3V9zM13 3.5V2h-1v2h1v2h-2V4H9V2H8v2H6v1H4V4c0-1.11.89-2 2-2h8l2.36 2.36-1.25 1.25zM20 20a2 2 0 0 1-2 2h-2v-2h2v-1h2zm-2-5h2v3h-2zm-6 7v-2h3v2zm-4 0v-2h3v2zm-2 0a2 2 0 0 1-2-2v-2h2v2h1v2zm-2-8h2v3H4zm0-4h2v3H4zm14 1h2v3h-2zM4 6h2v3H4z'/></svg>",
+ "terraform": "<svg viewBox='0 0 32 32'><path fill='#5c6bc0' d='m2 10 8 4V6L2 2zm10 5 8 4v-8l-8-4zm0 11 8 4v-8l-8-4zm10-14v8l8-4V8z'/></svg>",
+ "test-js": "<svg viewBox='0 0 32 32'><path fill='#ffca28' d='M20 4v2h-2v4.531l.264.461 7.473 13.078a2 2 0 0 1 .263.992V26a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2v-.938a2 2 0 0 1 .264-.992l7.473-13.078.263-.46V6h-2V4zm0-2h-8a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2v2L4.527 23.078A4 4 0 0 0 4 25.062V26a4 4 0 0 0 4 4h16a4 4 0 0 0 4-4v-.938a4 4 0 0 0-.527-1.984L20 10V8a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2'/><circle cx='17' cy='17' r='1' fill='#ffca28'/><path fill='#ffca28' d='M19.72 20.715a1 1 0 0 0-1.134-.318 5 5 0 0 1-1.18.262 3.95 3.95 0 0 1-1.862-.292 2.74 2.74 0 0 0-3.371.489 2 2 0 0 0-.237.35L10 24h12Z'/></svg>",
+ "test-jsx": "<svg viewBox='0 0 32 32'><path fill='#00bcd4' d='M20 4v2h-2v4.531l.264.461 7.473 13.078a2 2 0 0 1 .263.992V26a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2v-.938a2 2 0 0 1 .264-.992l7.473-13.078.263-.46V6h-2V4zm0-2h-8a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2v2L4.527 23.078A4 4 0 0 0 4 25.062V26a4 4 0 0 0 4 4h16a4 4 0 0 0 4-4v-.938a4 4 0 0 0-.527-1.984L20 10V8a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2'/><circle cx='17' cy='17' r='1' fill='#00bcd4'/><path fill='#00bcd4' d='M19.72 20.715a1 1 0 0 0-1.134-.318 5 5 0 0 1-1.18.262 3.95 3.95 0 0 1-1.862-.292 2.74 2.74 0 0 0-3.371.489 2 2 0 0 0-.237.35L10 24h12Z'/></svg>",
+ "test-ts": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M20 4v2h-2v4.531l.264.461 7.473 13.078a2 2 0 0 1 .263.992V26a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2v-.938a2 2 0 0 1 .264-.992l7.473-13.078.263-.46V6h-2V4zm0-2h-8a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2v2L4.527 23.078A4 4 0 0 0 4 25.062V26a4 4 0 0 0 4 4h16a4 4 0 0 0 4-4v-.938a4 4 0 0 0-.527-1.984L20 10V8a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2'/><circle cx='17' cy='17' r='1' fill='#0288d1'/><path fill='#0288d1' d='M19.72 20.715a1 1 0 0 0-1.134-.318 5 5 0 0 1-1.18.262 3.95 3.95 0 0 1-1.862-.292 2.74 2.74 0 0 0-3.371.489 2 2 0 0 0-.237.35L10 24h12Z'/></svg>",
+ "tex": "<svg viewBox='0 0 1024 1024'><path fill='#2196f3' d='M80 192 64 320h32c16-80 16-96 63.242-96H176c8.837 0 16 7.163 16 16v352c0 8.837 0 16-32 16h-32v32h192v-32h-32c-32 0-32-7.163-32-16V240c0-8.837 7.163-16 16-16h16c48 0 48 16 64 96h32l-16-128zm560 0v32c16 0 45.713 0 52.57 16L776 434.666 708.57 592c-6.857 16-52.57 16-68.57 16v32h128v-32s-34.285 0-27.428-16L792 472l51.428 120c3.103 7.24-1.52 16-11.428 16v32h128v-32c-16 0-45.713 0-52.57-16L824 397.334 891.43 240c6.857-16 52.57-16 68.57-16v-32H832v32s34.285 0 27.428 16L808 360l-51.428-120c-3.103-7.24 1.52-16 11.428-16v-32zM320 384v32h32c32 0 32 7.163 32 16v352c0 8.837 0 16-32 16h-32v32h304l16-128h-32c-16 80-16 96-64 96h-64c-32 0-32-7.163-32-16V624h80c8.837 0 16 0 16 32v16h32V544h-32v16c0 32-7.163 32-16 32h-80V432c0-8.837 0-16 32-16h64c48 0 48 16 64 96h32l-16-128z'/></svg>",
+ "textlint": "<svg viewBox='0 0 32 32'><path fill='#f06292' d='M10 22V8H4v20h24v-6z'/><path fill='#00e5ff' d='M14 8h4v20h-4z'/><path fill='#00e5ff' d='M4 4h24v6H4z'/></svg>",
+ "tilt": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='M14 5v22a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V13a1 1 0 0 1 1-1h4a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H15a1 1 0 0 0-1 1M4.47 7.706l5.694-3.559A1.2 1.2 0 0 1 12 5.165V11a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V8.554a1 1 0 0 1 .47-.848'/></svg>",
+ "tldraw": "<svg viewBox='0 0 24 24'><path fill='#b0bec5' d='M14.69 5.75q0 1.058-.808 1.796c-.808.738-1.194.738-1.966.738q-1.195 0-2.002-.738-.808-.737-.808-1.797 0-1.058.808-1.796c.808-.738 1.206-.738 2.002-.738q1.159 0 1.966.738.808.738.808 1.796zm-5.619 8.883q0-1.059.808-1.797.842-.77 2.037-.77a2.82 2.82 0 0 1 1.966.77q.843.738.984 1.668.28 1.733-.703 3.434-.948 1.7-2.74 2.598-.982.514-1.615-.032-.596-.513.352-1.219.525-.353.878-.898.351-.546.456-1.123.036-.257-.246-.257-.702-.032-1.44-.706a2.17 2.17 0 0 1-.737-1.668'/></svg>",
+ "tldraw_light": "<svg viewBox='0 0 24 24'><path fill='#455a64' d='M14.69 5.75q0 1.058-.808 1.796c-.808.738-1.194.738-1.966.738q-1.195 0-2.002-.738-.808-.737-.808-1.797 0-1.058.808-1.796c.808-.738 1.206-.738 2.002-.738q1.159 0 1.966.738.808.738.808 1.796zm-5.619 8.883q0-1.059.808-1.797.842-.77 2.037-.77a2.82 2.82 0 0 1 1.966.77q.843.738.984 1.668.28 1.733-.703 3.434-.948 1.7-2.74 2.598-.982.514-1.615-.032-.596-.513.352-1.219.525-.353.878-.898.351-.546.456-1.123.036-.257-.246-.257-.702-.032-1.44-.706a2.17 2.17 0 0 1-.737-1.668'/></svg>",
+ "tobi": "<svg viewBox='0 0 32 32'><path fill='#c2185b' d='M2 2v28h28V2Zm16 16h-4v10h-2V18H8v-2h10Zm10 10h-8V16h2v10h6Z'/></svg>",
+ "tobimake": "<svg viewBox='0 0 32 32'><path fill='#757575' d='M15 2H6a2.006 2.006 0 0 0-2 2v22a2.006 2.006 0 0 0 2 2h6v-4H6v-2h6v-2H6v-2h6v-2H6v-2h6v-2h2V4l8 8h2v-1Z'/><path fill='#c2185b' d='M12 12v18h18V12Zm8 6h-2v8h-2v-8h-2v-2h6Zm8 8h-6V16h2v8h4Z'/></svg>",
+ "todo": "<svg viewBox='0 0 32 32'><path fill='#7cb342' d='M16 2a14 14 0 1 0 14 14A14 14 0 0 0 16 2m-1.667 22.143L6.92 16.73l3.293-3.293 4.12 4.107 7.455-7.44 3.294 3.293Z'/></svg>",
+ "toml": "<svg viewBox='0 0 16 16'><path fill='#cfd8dc' d='M4 6V4h8v2H9v7H7V6z'/><path fill='#ef5350' d='M4 1v1H2v12h2v1H1V1zm8 0v1h2v12h-2v1h3V1z'/></svg>",
+ "toml_light": "<svg viewBox='0 0 16 16'><path fill='#455a64' d='M4 6V4h8v2H9v7H7V6z'/><path fill='#ef5350' d='M4 1v1H2v12h2v1H1V1zm8 0v1h2v12h-2v1h3V1z'/></svg>",
+ "travis": "<svg viewBox='0 0 200 200'><path fill='#cb3349' d='M53.341 89.17s-27.564 19.4-28.641 26.791l2.068-.425S59.776 93.66 85.942 90.991l.592-3.321zm43.27-2.773-21.712 15.4 1.174.941c.885-.714 38.567-12.222 38.567-12.222l7.944-4.981c-5.33.664-25.973.862-25.973.862m18.869 18.627c15.417 0 38.527-15.1 38.527-15.1l-7.47-1.422c-.37.37-12.047-.409-12.047-.409l-5.544-1.54-13.243 15.091-1.078 2.619c.952-.387.854.76.854.76zm50.313 48.152-4.763 1.34-22.006-.475-13.766-10.705-16.64 4.163-19.244-1.665-10.777 11.776-20.22 6.04-10.097-3.13-.509-.438 4.532 11.097s10.265 11.005 15.999 12.299c5.733 1.294 16.09-.093 23.859-1.296 7.767-1.202 13.963-3.976 16.46-8.506 2.496-4.533 2.866-5.827 2.866-5.827s7.4 10.45 13.78 11.653c6.38 1.202 25.338-5.273 25.338-5.273s11.56-3.143 13.593-7.12c2.035-3.976 7.4-17.015 7.4-17.015zM50.4 162.216l-.32-.147c-.758-.655-1.378-1.19-1.783-1.538z'/><path fill='#f4edae' d='M184.483 74.565a43 43 0 0 0-.186-1.78 24 24 0 0 0-.19-1.26c-1.588-1.337-3.394-2.526-5.273-3.607-2.036-1.19-4.166-2.255-6.341-3.244-2.16-1.02-4.378-1.941-6.609-2.841q-1.672-.68-3.364-1.309c-1.124-.438-2.259-.846-3.397-1.26 2.389.395 4.753.937 7.1 1.541 1.526.395 3.044.837 4.552 1.309-10.728-28.04-36.38-46.301-64.386-46.301-28.01 0-53.659 18.26-64.385 46.3a97 97 0 0 1 4.55-1.308c2.35-.604 4.716-1.146 7.103-1.542-1.142.415-2.274.823-3.402 1.261q-1.685.63-3.364 1.309c-2.231.9-4.443 1.822-6.608 2.84-2.17.99-4.302 2.056-6.339 3.245-1.879 1.08-3.682 2.27-5.277 3.606-.066.405-.133.843-.186 1.262-.071.59-.137 1.185-.186 1.78a46 46 0 0 0-.156 3.597 52.5 52.5 0 0 0 .443 7.183c.327 2.385.765 4.754 1.412 7.043.32 1.147.68 2.279 1.099 3.373a29 29 0 0 0 1.042 2.374l.153.291c.176.09.361.18.537.266l1.067.509c.594.27 1.417.66 2.097.966a2 2 0 0 0 .11-.101l-1.98-7.541c.42-.115 4.179-1.085 10.658-2.318a32 32 0 0 1-5.91-1.723c-.7-.295-1.388-.618-2.045-.988-.657-.377-1.304-.804-1.804-1.371 7.118 2.322 21.826 1.27 34.9-.08 11.957-1.233 23.942-2.032 35.99-2.056 12.048.024 24.037.823 35.99 2.055 13.08 1.351 27.787 2.403 34.904.08-.5.568-1.145.996-1.802 1.372a20 20 0 0 1-2.045.988 32 32 0 0 1-4.34 1.385q-.328.077-.657.147c7.104 1.318 11.24 2.39 11.682 2.508l-1.894 7.184c.38-.172.765-.338 1.142-.509l1.065-.51q.266-.129.538-.265l.147-.29q.194-.372.371-.766c.24-.523.468-1.062.677-1.608a37 37 0 0 0 1.1-3.373c.647-2.29 1.084-4.659 1.412-7.043.306-2.379.466-4.782.444-7.184-.01-1.201-.053-2.4-.159-3.596'/><path fill='#e6ccad' d='M114.47 174.284c-.943.142-1.885.262-2.823.332-.604.03-1.243.09-1.823.09l-.215.005a516 516 0 0 0 1.7-3.844c.853.957 1.938 2.141 3.16 3.417zm2.046 2.091c2.926 2.898 6.553 4.558 10.544 4.853-6.704 2.723-13.023 3.774-17.89 4.101-4.853.325-9.772-.247-14.593-1.568.404-.073.7-.125 1.057-.168.396-.048 9.04-1.175 12.937-6.97l.366.009.957-.008c.647 0 1.235-.053 1.854-.077a39 39 0 0 0 4.321-.623q.22.226.447.45z'/><path fill='#656c67' d='M124.807 89.611c-.28.366-.595.77-.928 1.19-1.713 2.165-4.088 4.934-6.904 7.642a327 327 0 0 0-10.102-.157c-5.722 0-11.21.152-16.42.405 7.362-3.055 16.206-6.04 25.785-7.832 2.797-.53 5.66-.953 8.569-1.248m-43.085 3.297c-2.627 1.846-5.595 4.202-8.473 7.06a319 319 0 0 0-19.104 2.49c8.155-3.75 17.462-7.2 27.577-9.55m90.05 15.216-2.998 20.975-14.36 10.058-37.454-4.258-5.639-18.567a1.23 1.23 0 0 0-.981-.86c-1.388-.225-2.616-.339-3.639-.339-1.027 0-2.25.114-3.645.339a1.24 1.24 0 0 0-.98.86l-5.5 18.114-37.245 8.289-14.759-10.34-2.908-23.537q.95-.537 1.927-1.07 1.372-.296 3.079-.624l2.74 22.21c.043.347.234.656.52.856l10.152 7.115a1.26 1.26 0 0 0 .976.188l30.622-6.813a1.23 1.23 0 0 0 .909-.842l5.514-18.158c1.479-.38 5.268-1.266 8.597-1.266 3.321 0 7.113.885 8.593 1.266l5.514 18.157c.143.472.553.808 1.042.866l30.622 3.478c.297.033.6-.044.844-.214l10.159-7.109c.274-.195.46-.501.507-.838l2.713-18.974c2.038.393 3.741.75 5.078 1.038m3.782-13.155-2.636 10c-5.39-1.188-17.234-3.52-33.354-5.09 5.267-2.246 10.63-5.063 15.53-8.584 9.592 1.356 16.635 2.807 20.46 3.674'/><path fill='#e5caa3' d='M67.567 126.256c2.088-1.128 4.448-.96 8.14-.956.397.01.805.005 1.232-.004.363-.004.728-.01 1.114-.02 3.806-.037 6.97.325 6.915-5.652-.058-5.975-2.712-10.805-6.514-10.767-3.806.033-7.489 4.929-7.261 10.9.062 1.556.29 2.679.657 3.488-3.317.831-4.231 2.883-4.283 3.011m45.758-24.58a41 41 0 0 1-4.65 3.288l-5.4 3.26a46 46 0 0 0-6.494 1.3 1.25 1.25 0 0 0-.838.827l-5.5 18.123-29.395 6.537-9.259-6.485-2.693-21.82c5.258-.966 12.29-2.084 20.736-3.03a50 50 0 0 0-2.617 3.415l-5.029 7.218 7.322-4.881c.11-.076 4.772-3.144 12.4-6.875a334 334 0 0 1 31.417-.877m32.192 22.135c-.037-.141-.837-2.545-4.378-2.123.409-.888.59-2.087.462-3.73-.468-5.961-3.936-10.7-7.738-10.568-3.802.135-6.238 5.077-6.028 11.053.21 5.972 3.402 5.93 7.204 5.796 5.11-.095 7.918-1.665 10.478-.428m18.761-17.17-2.65 18.551-9.336 6.537-29.356-3.336-5.477-18.038a1.25 1.25 0 0 0-.837-.827c-.182-.053-3.816-1.09-7.67-1.424l.704-.047c.609-.043 10.734-1.285 22.825-5.453 13.388 1.028 24.322 2.675 31.797 4.036z'/><path fill='#c7b39a' d='M67.567 126.256c2.088-1.128 4.448-.96 8.14-.956.397.01.805.005 1.232-.004.363-.004.728-.01 1.114-.02 3.806-.037 6.97.325 6.915-5.652-.058-5.975-2.712-10.805-6.514-10.767-3.806.033-7.489 4.929-7.261 10.9.062 1.556.29 2.679.657 3.488-3.317.831-4.231 2.883-4.283 3.011m45.758-24.58a41 41 0 0 1-4.65 3.288l-5.4 3.26a46 46 0 0 0-6.494 1.3 1.25 1.25 0 0 0-.838.827l-5.5 18.123-29.395 6.537-9.259-6.485-2.693-21.82c5.258-.966 12.29-2.084 20.736-3.03a50 50 0 0 0-2.617 3.415l-5.029 7.218 7.322-4.881c.11-.076 4.772-3.144 12.4-6.875a334 334 0 0 1 31.417-.877m32.192 22.135c-.037-.141-.837-2.545-4.378-2.123.409-.888.59-2.087.462-3.73-.468-5.961-3.936-10.7-7.738-10.568-3.802.135-6.238 5.077-6.028 11.053.21 5.972 3.402 5.93 7.204 5.796 5.11-.095 7.918-1.665 10.478-.428m18.761-17.17-2.65 18.551-9.336 6.537-29.356-3.336-5.477-18.038a1.25 1.25 0 0 0-.837-.827c-.182-.053-3.816-1.09-7.67-1.424l.704-.047c.609-.043 10.734-1.285 22.825-5.453 13.388 1.028 24.322 2.675 31.797 4.036z'/><g fill='#e6ccad'><path d='M177.087 114.794c.45.371.807 1.332.404 5.809-.495 5.481-2.8 15.102-4.226 16.653-1.575.305-4.877-.057-6.89-.527.348-1.381.447-2.043.728-3.455l3.521-2.463c.28-.195.466-.501.514-.834l2.46-17.243c1.494.808 3.03 1.683 3.489 2.06M47.279 137.302q.265.938.553 1.86c-1.895.557-5.754 1.09-7.499.785-1.446-1.457-4.144-11.485-4.734-16.81-.481-4.315-.144-5.248.295-5.611.51-.428 2.416-1.513 4.068-2.398l2.232 18.114c.042.348.227.656.519.856zm71.23.248-.134 1.156s-4.058 3.763-10.59 4.605c-6.533.842-11.667-3.173-11.667-3.173.214.698.519 2.274 1.998 3.62a47 47 0 0 0-5.592-.348c-.712 0-1.432.02-2.127.054-4.02.21-9.676 6.289-13.654 12.413-1.951.618-8.05 2.554-15.111 4.64-6.59-9.132-9.831-18.463-10.16-19.428l.005-.005c-.133-.372-.267-.744-.39-1.124l1.694 1.186v.004l4.186 2.931 1.4.975c.209.148.456.224.703.224q.135 0 .27-.027l38.475-8.566c.431-.094.78-.419.908-.841l5.486-18.062a19 19 0 0 1 2.493-.195c.71 0 1.547.067 2.494.195l5.646 18.6c.143.47.552.814 1.037.865z'/><path d='m162.427 136.558 1.242-.872a77 77 0 0 1-.662 2.566c-1.726 5.534-3.765 10.43-6.028 14.754-.5.034-1.028.054-1.594.054-1.747 0-3.531-.148-5.506-.33-3.426-.314-8.864-.936-10.581-1.136-1.48-1.18-6.542-5.202-10.754-8.517-.166-.133-.308-.257-.447-.372-.742-.627-1.585-1.34-3.293-1.34-1.55 0-3.816.565-9.29 2.222 2.844-2.127 2.863-4.872 2.863-4.872l.134-1.156 36.094 4.1c.294.034.599-.042.846-.213l3.627-2.54z'/></g><path fill='#ebd599' d='M135.862 60.616c.11 3.21.029 9.03-1.466 14.478a1.2 1.2 0 0 0 0 .624 234 234 0 0 0-5.71-.524c2.136-4.477 2.602-9.244 2.703-10.923 1.64-1.167 3.274-2.566 4.473-3.655M81.4 64.27c.098 1.68.564 6.437 2.697 10.91q-2.856.228-5.705.528a1.2 1.2 0 0 0 0-.615c-1.494-5.438-1.57-11.262-1.465-14.478 1.198 1.09 2.825 2.489 4.472 3.655z'/><path fill='#2d3136' d='M145.517 123.811c-2.56-1.236-5.367.334-10.477.43-3.802.133-6.995.174-7.204-5.797-.21-5.976 2.226-10.918 6.028-11.053 3.802-.133 7.27 4.606 7.738 10.569.128 1.642-.054 2.84-.462 3.729 3.54-.423 4.34 1.98 4.377 2.122m-11.948-8.75a1.95 1.95 0 0 0 1.908-1.98 1.937 1.937 0 0 0-1.98-1.906 1.944 1.944 0 0 0 .072 3.887z'/><circle cx='131.439' cy='115.529' r='1.943' fill='#edf6fa' transform='rotate(-1.049)'/><path fill='#2d3136' d='M84.967 119.622c.056 5.976-3.108 5.615-6.914 5.652-.386.01-.752.014-1.115.02q-.64.017-1.231.003c-3.692-.004-6.053-.172-8.14.956.05-.128.965-2.18 4.282-3.012-.366-.81-.596-1.932-.657-3.487-.228-5.971 3.455-10.868 7.261-10.9 3.802-.038 6.457 4.792 6.514 10.767zm-5.876-3.126a1.944 1.944 0 1 0-.109-3.887 1.944 1.944 0 0 0 .109 3.887'/><circle cx='76.07' cy='116.551' r='1.944' fill='#edf6fa' transform='rotate(-1.473)'/><path fill='#ebd599' fill-opacity='.8' d='M55.92 79.056s.59-7.7 1.797-10.65c.622-1.52 4.426-2.489 7.331-3.18 2.904-.692 7.537-2.075 8.437 2.005s2.35 7.053 3.318 8.367l-1.244-16.803-19.985 4.84-1.315 14.869.623 1.175 1.037-.623m101.131 0s-.59-7.7-1.797-10.65c-.623-1.52-4.427-2.489-7.33-3.18-2.906-.692-7.538-2.075-8.438 2.005-.898 4.08-2.351 7.053-3.32 8.367l1.246-16.803 19.985 4.84 1.314 14.869-.623 1.175z' opacity='.8'/><path fill='#ebd599' fill-opacity='.75' d='M128.793 28.763s-.53 40.295-9.942 40.295H94.379c-9.413 0-10.173-40.277-10.173-40.277l-4.624 13.675.076 18.764 3.283 2.61 1.493 8.058 2.088 4.7 40.288.075 1.864-3.358 1.567-5.67.225-3.58 4.45-12.704zm29.647 37.572s1.938 13.9 7.064 16.407l-7.178-2.165zm-104.1 0s-1.938 13.9-7.065 16.407l7.179-2.165z' opacity='.75'/><path fill='#2d3136' d='M159.823 80.513c2.44.8 4.894 1.646 7.118 2.946-1.19-.472-2.418-.809-3.655-1.114a62 62 0 0 0-3.721-.818 116 116 0 0 0-7.528-1.176 169 169 0 0 0-7.57-.818c-2.531-.214-5.057-.428-7.594-.576-10.139-.624-20.306-.78-30.48-.79-10.172.005-20.34.196-30.48.795-2.535.147-5.062.362-7.593.576s-5.058.486-7.57.818c-2.522.305-5.03.705-7.527 1.175q-1.871.35-3.726.814c-1.232.305-2.465.642-3.655 1.114 2.223-1.299 4.677-2.15 7.123-2.95.205-.067.414-.124.623-.19-.014-.053-.029-.1-.038-.157-.043-.281-.947-6.962.628-16.516.076-.457.404-.828.852-.952l2.584-.736c.561-20.042 8.815-29.467 9.073-29.758-7.375 11.867-7.308 25.942-7.18 29.22l16.057-4.565.038-.008q.1-.022.2-.029c.018 0 .043-.005.066-.005.082-.004.173.005.263.024.014 0 .028.01.047.014.067.014.129.037.199.066.029.015.047.03.076.044q.078.044.157.1c.014.009.03.013.043.028l.028.029c.015.014.033.024.048.039.01.014.438.436 1.118 1.07-.161-5.29.09-20.469 7.319-33.049-.153.441-5.887 17.21-3.911 36.004a30 30 0 0 0 1.974 1.46c.318.22.514.58.518.966.006.062.143 6.053 2.789 11.149q.054.115.09.237c6.58-.447 13.18-.628 19.764-.632 6.587.004 13.18.194 19.76.647.024-.086.053-.17.096-.252 2.645-5.096 2.784-11.087 2.784-11.15.009-.385.204-.746.523-.965a32 32 0 0 0 1.97-1.46c1.979-18.794-3.756-35.563-3.907-36.004 7.222 12.58 7.48 27.758 7.318 33.05a55 55 0 0 0 1.117-1.071c.014-.015.035-.024.047-.04.01-.008.016-.018.03-.028.014-.014.029-.02.043-.029l.157-.104.071-.039c.072-.029.138-.052.21-.07.014 0 .029-.01.043-.01.085-.019.177-.028.262-.024.024 0 .043.005.062.005.066.005.138.014.205.029.01.005.024.005.037.009l16.058 4.563c.13-3.277.196-17.353-7.178-29.219.256.29 8.512 9.717 9.068 29.758l2.588.736c.442.124.775.495.852.952 1.574 9.554.666 16.235.627 16.516q-.015.085-.037.161c.212.062.417.123.627.19m-2.92-.857c.123-1.07.698-6.917-.524-14.882l-18.177-5.167c.163 3.078.235 9.754-1.512 16.12a1.4 1.4 0 0 1-.09.229c.18.019.362.033.537.057 5.11.585 10.197 1.403 15.226 2.507 1.517.352 3.035.729 4.539 1.136zm-22.508-4.563c1.495-5.447 1.576-11.267 1.466-14.478-1.199 1.09-2.832 2.488-4.473 3.654-.1 1.68-.567 6.447-2.703 10.923 1.903.154 3.807.325 5.71.524a1.2 1.2 0 0 1 0-.623m-50.296.087c-2.133-4.473-2.599-9.232-2.698-10.91-1.646-1.166-3.274-2.565-4.473-3.654-.105 3.216-.028 9.04 1.466 14.478.057.21.051.42 0 .615q2.85-.301 5.705-.529m-7.913.765a1.5 1.5 0 0 1-.087-.218c-1.75-6.367-1.673-13.042-1.516-16.121l-18.171 5.167c-1.223 7.956-.652 13.803-.53 14.879 1.51-.41 3.022-.786 4.546-1.138 2.511-.556 5.043-1.022 7.579-1.45 2.541-.424 5.09-.747 7.64-1.057.182-.024.358-.037.54-.062z'/><path fill='#2d3136' d='M125.542 29.29v15.081h-10.69v-4.406h-2.41v20.357h3.64v11.382H97.385V60.322h3.625V39.966h-2.401v4.406H87.925V29.29zm-3.212 11.877v-8.671h-31.2v8.671h4.266v-4.406h8.826v26.773h-3.632V68.5h12.282v-4.966h-3.632V36.761h8.826v4.406z'/><path fill='#cb3349' d='M104.22 36.761h-8.823v4.406h-4.266v-8.672h31.205v8.672h-4.27v-4.406h-8.823v26.773h3.632V68.5H100.59v-4.966h3.632z'/><path fill='#656c67' d='M38.206 94.969c1.558-.354 3.663-.804 6.246-1.302a93 93 0 0 0-5.512 4.072z'/><g fill='#c7b39a'><path d='M95.266 136.301s.436 2.804-3.488 4.003c-3.922 1.2-28.773 8.606-31.607 8.205s-10.026-5.203-10.026-5.203l-1.745-6.204 10.644 6.636zm22.26-.541s-.314 2.82 3.659 3.85c3.972 1.028 29.12 7.347 31.933 6.824s9.79-5.635 9.79-5.635l1.474-6.275-9.784 5.836z'/><path d='M36.184 131.146s3.443 6.887 9.005 4.502l3.973 1.457-.397 2.915-8.343 1.058-2.25-1.323zm140.965-1.896s-5.628 7.188-11.19 4.805l-1.788 1.153.397 2.913 8.343 1.06 2.25-1.324zM90.1 183.355s20.4 10.285 43.498-3.71l-7.417-.506s-16.186 6.406-29 2.866z'/></g><path fill='#2d3136' d='m110.785 164.408 1.412 1.682c.032.04 3.168 3.773 7.003 7.575 2.55 2.527 5.63 3.806 9.162 3.806a16 16 0 0 0 3.751-.463c1.187-.289 2.477-.596 3.797-.912 4.77-1.14 10.171-2.435 12.79-3.169a35 35 0 0 1 1.705-.418c3.485-.805 8.263-1.916 10.795-6.988 1.904-3.806 4.47-8.746 6.375-12.367-1.65.761-3.38 1.586-4.609 2.233-1.894.994-4.374 1.476-7.58 1.476-1.907 0-3.78-.16-5.851-.344-4.141-.381-11.149-1.206-11.22-1.216l-.537-.06-.424-.338c-.063-.046-6.203-4.939-11.166-8.842q-.302-.247-.55-.454c-.523-.446-.523-.446-.835-.446-1.591 0-6.218 1.435-16.353 4.577l-.623.198-.61-.23c-.07-.023-6.72-2.49-14.688-2.49-.652 0-1.302.017-1.935.05-.798.041-5.278 2.169-11.006 11.232l-.371.582-.657.21c-.095.034-9.954 3.216-20.41 6.206l-.679.193-.642-.298c-.068-.028-4.16-1.926-8.053-3.716 2.123 3.137 4.838 7.05 7.227 10.118 4.173 5.374 9.433 10.34 19.518 10.34 1.265 0 2.609-.077 3.989-.234 10.258-1.16 12.9-1.627 14.321-1.875.481-.082.862-.152 1.349-.21.077-.01 8.478-1.097 10.835-6.384 2.43-5.433 2.921-6.728 2.94-6.777l.857-2.288s.106-.593.28-.777q-.001.005.005.004l.017.014c.27.194.67.8.67.8zM46.895 61.832c-2.23.898-4.444 1.825-6.609 2.842-2.169.992-4.301 2.056-6.337 3.242-1.88 1.083-3.683 2.27-5.274 3.611a34 34 0 0 0-.187 1.26 46 46 0 0 0-.344 5.376 53 53 0 0 0 .445 7.185c.33 2.385.765 4.756 1.412 7.044a37 37 0 0 0 1.1 3.37c.207.551.436 1.087.676 1.61.118.26.242.518.365.765l.153.29c.178.091.362.182.536.265l1.068.508c.596.276 1.417.666 2.096.968.04-.032.078-.069.11-.1l-1.977-7.54c.418-.114 4.179-1.086 10.658-2.32a30 30 0 0 1-1.572-.335 32.5 32.5 0 0 1-4.339-1.385 20 20 0 0 1-2.045-.99c-.656-.377-1.303-.803-1.803-1.371 7.118 2.324 21.824 1.27 34.9-.078 11.958-1.234 23.94-2.031 35.988-2.06 12.046.028 24.038.825 35.99 2.06 13.083 1.348 27.79 2.403 34.905.078-.5.568-1.145.994-1.801 1.37-.655.372-1.348.693-2.046.991a33 33 0 0 1-4.343 1.385q-.325.076-.656.147c7.104 1.316 11.24 2.389 11.68 2.508l-1.894 7.182c.381-.17.766-.334 1.141-.509l1.07-.508a21 21 0 0 0 .536-.266l.147-.289a18 18 0 0 0 .371-.765q.361-.785.68-1.61c.417-1.09.778-2.224 1.095-3.37.647-2.288 1.087-4.66 1.412-7.044.307-2.38.468-4.783.445-7.186a47 47 0 0 0-.156-3.595 46 46 0 0 0-.188-1.78 26 26 0 0 0-.19-1.26c-1.591-1.34-3.393-2.527-5.273-3.61-2.037-1.188-4.167-2.251-6.342-3.243-2.16-1.017-4.378-1.944-6.608-2.842a126 126 0 0 0-3.367-1.308c-1.122-.435-2.26-.846-3.397-1.26 2.388.394 4.756.939 7.098 1.54 1.527.395 3.05.84 4.554 1.312-10.727-28.043-36.38-46.302-64.384-46.302-28.011 0-53.658 18.26-64.384 46.302a96 96 0 0 1 4.549-1.312c2.348-.6 4.714-1.146 7.104-1.54-1.143.413-2.275.825-3.403 1.26-1.123.417-2.246.857-3.365 1.307m141.223 16.224c.129 2.581.06 5.167-.229 7.736a49 49 0 0 1-1.434 7.622 40 40 0 0 1-1.244 3.719 34 34 0 0 1-.788 1.834c-.143.302-.293.6-.463.908l-.252.454a8 8 0 0 1-.308.508l-.26.405-.409.21c-.436.225-.793.394-1.189.578l-1.15.519c-.77.33-1.545.64-2.32.943-.541.212-1.019.405-1.472.597l-1.28 4.86a94 94 0 0 0-1.136-.279l-.07.509c1.413.738 4.187 2.234 5.132 3.014 1.903 1.572 2.037 4.063 1.615 8.717-.216 2.39-1.009 7.039-2.109 11.31-1.9 7.35-2.908 8.213-4.21 8.546-.66.17-1.678.34-2.665.34-2.007 0-4.741.055-6.58-.39-.472 1.673-1.82 6.035-4.49 11.483.134-.065.266-.12.386-.184 3.292-1.729 9.62-4.485 9.887-4.605l4.999-2.172-2.56 4.814c-.06.105-5.549 10.429-8.91 17.17-3.34 6.677-9.598 8.13-13.345 8.992-.564.133-1.1.257-1.535.376-2.689.756-8.132 2.054-12.932 3.21-.057.013-.115.028-.174.036a56.7 56.7 0 0 1-9.704 4.793c-12.905 4.888-26.506 4.614-38.837.082-2.003.258-4.637.573-8.144.972a40 40 0 0 1-4.415.257c-11.75 0-17.977-5.966-22.522-11.808-4.865-6.256-10.817-15.542-11.07-15.931l-3.45-5.383 5.832 2.623c2.792 1.257 11.094 5.1 13.469 6.2q.287-.082.569-.165c-3.948-5.63-7.186-12.05-9.474-19.163-1.944.586-4.94 1.04-7.269 1.04-.851 0-1.614-.063-2.2-.201-1.303-.298-2.789-1.554-4.811-8.676-1.174-4.135-2.05-8.65-2.306-10.973-.505-4.536-.413-6.966 1.472-8.535 1.137-.95 4.97-2.963 5.764-3.374l-.17-1.4c-12.18 7.157-19.48 13.482-19.626 13.611l-8.71 7.644 6.205-9.786c4.91-7.755 10.583-14.024 15.802-18.853-.216-.087-.417-.174-.629-.26l-1.15-.519a31 31 0 0 1-1.187-.578l-.409-.21-.261-.405c-.147-.22-.21-.344-.307-.508l-.257-.454c-.16-.307-.317-.606-.46-.908q-.433-.914-.787-1.834a40 40 0 0 1-1.244-3.719 48.5 48.5 0 0 1-1.435-7.622 48.4 48.4 0 0 1-.23-7.736q.105-1.935.372-3.857c.092-.642.188-1.284.317-1.922.133-.65.262-1.256.459-1.967l.137-.5.358-.28c2.032-1.597 4.223-2.802 6.452-3.87a70 70 0 0 1 5.59-2.318C43.622 48.733 52.674 35.78 64.57 26.75c12.3-9.332 26.762-14.266 41.827-14.266s29.527 4.934 41.825 14.267c11.897 9.029 20.949 21.984 26.21 36.59a69 69 0 0 1 5.594 2.32c2.228 1.069 4.42 2.274 6.451 3.87l.358.281.138.5c.201.711.33 1.317.458 1.967.13.638.23 1.28.317 1.922.18 1.278.308 2.566.37 3.855m-10.626 42.546c.404-4.475.052-5.438-.403-5.805-.454-.381-1.99-1.251-3.485-2.063l-2.462 17.24a1.23 1.23 0 0 1-.514.835l-3.522 2.464c-.28 1.41-.38 2.077-.724 3.457 2.013.468 5.314.83 6.887.527 1.428-1.548 3.733-11.17 4.223-16.655m-4.571-15.633 2.637-10c-3.825-.867-10.869-2.317-20.462-3.674-4.899 3.521-10.26 6.337-15.528 8.584 16.12 1.569 27.965 3.903 33.353 5.09m-4.146 24.127 2.999-20.972c-1.334-.289-3.04-.646-5.077-1.036l-2.71 18.976c-.05.335-.234.642-.509.835l-10.162 7.107a1.23 1.23 0 0 1-.839.215l-30.623-3.48a1.23 1.23 0 0 1-1.042-.862l-5.516-18.159c-1.477-.382-5.27-1.267-8.589-1.267-3.334 0-7.122.885-8.599 1.267l-5.517 18.16a1.23 1.23 0 0 1-.908.838l-30.624 6.814a1.3 1.3 0 0 1-.27.029c-.248 0-.496-.074-.702-.222l-10.158-7.112a1.24 1.24 0 0 1-.518-.857l-2.738-22.21c-1.137.221-2.164.432-3.082.624q-.976.537-1.925 1.074l2.906 23.534 14.763 10.342 37.245-8.286 5.498-18.114a1.23 1.23 0 0 1 .982-.863c1.394-.224 2.613-.339 3.646-.339 1.02 0 2.246.115 3.636.339.467.073.847.408.981.863l5.64 18.563 37.453 4.259zm-7.148-3.907 2.65-18.556c-7.475-1.36-18.407-3.006-31.797-4.034-12.089 4.17-22.214 5.41-22.824 5.452l-.706.05c3.856.332 7.489 1.368 7.671 1.422.4.12.712.426.836.827l5.48 18.04 29.354 3.334zm2.04 10.492-1.244.87-3.347 2.35-3.628 2.54c-.247.17-.55.248-.845.215l-36.092-4.104-2.628-.299a1.23 1.23 0 0 1-1.037-.865l-5.649-18.6a19 19 0 0 0-2.489-.194q-1.076 0-2.495.194l-5.485 18.063a1.23 1.23 0 0 1-.908.838l-38.475 8.568a1.4 1.4 0 0 1-.27.026c-.247 0-.495-.073-.707-.224l-1.397-.973-4.187-2.929v-.01l-1.692-1.182c.12.38.256.752.391 1.123l-.005.005c.326.967 3.568 10.295 10.157 19.424a774 774 0 0 0 15.11-4.636c3.975-6.127 9.634-12.203 13.657-12.413a45 45 0 0 1 2.127-.054c1.968 0 3.848.141 5.59.348-1.48-1.344-1.784-2.922-1.999-3.619 0 0 5.132 4.013 11.666 3.175 6.536-.844 10.594-4.61 10.594-4.61s-.02 2.748-2.867 4.875c5.476-1.656 7.741-2.224 9.291-2.224 1.71 0 2.554.715 3.293 1.345.142.114.285.237.449.371 4.21 3.315 9.272 7.337 10.754 8.516 1.715.201 7.153.82 10.58 1.137 1.975.178 3.76.33 5.506.33.565 0 1.093-.024 1.592-.057 2.266-4.322 4.303-9.222 6.03-14.75.232-.851.458-1.709.659-2.57M150.35 89.98q-.014-.009-.028-.01a489 489 0 0 0-8.704-.577 803 803 0 0 0-12.106-.592 65 65 0 0 1-1.394 1.97c-2.196 2.973-6.553 8.425-12.14 13.048 7.908-1.417 21.622-4.925 33.487-13.203.297-.21.592-.43.885-.636m-26.464.82c.334-.417.645-.82.924-1.187a99 99 0 0 0-8.565 1.247c-9.58 1.793-18.426 4.774-25.786 7.828a340 340 0 0 1 16.423-.403c3.462 0 6.828.06 10.101.156 2.816-2.707 5.192-5.476 6.903-7.64zm3.177 90.423c-3.989-.294-7.616-1.953-10.544-4.852l-.444-.453a39 39 0 0 1-4.324.623c-.618.023-1.206.077-1.852.077l-.96.01-.365-.01c-3.894 5.793-12.542 6.921-12.937 6.972-.357.04-.65.09-1.055.164 4.82 1.321 9.74 1.894 14.592 1.568 4.865-.325 11.185-1.38 17.889-4.1zm-15.413-6.61a40 40 0 0 0 2.82-.334 123 123 0 0 1-3.16-3.417c-.417.959-.97 2.22-1.7 3.844h.214c.583 0 1.221-.063 1.826-.092zm-2.97-69.651a42 42 0 0 0 4.649-3.289c-2.115-.036-4.26-.063-6.447-.063-8.98 0-17.367.367-24.97.94-7.627 3.728-12.29 6.795-12.4 6.873l-7.323 4.885 5.03-7.219a47 47 0 0 1 2.619-3.416c-8.447.944-15.481 2.063-20.737 3.03l2.691 21.82 9.26 6.485 29.394-6.536 5.502-18.127a1.24 1.24 0 0 1 .556-.702 1 1 0 0 1 .279-.124c.17-.046 3.109-.885 6.498-1.298zm3.38-16.643q-3.072-.048-6.142-.055h-.004q-4.505.007-9.025.097l-5.553 2.631c-.005.005-.037.02-.078.043-.835.407-8.127 4.086-14.987 10.648 6.15-3.233 15.188-7.456 25.823-10.731a128 128 0 0 1 9.966-2.633M73.248 99.962c2.88-2.857 5.85-5.214 8.474-7.057-10.111 2.348-19.42 5.796-27.573 9.547a320 320 0 0 1 19.099-2.49m1.912-8.813a132 132 0 0 1 10.464-2.476c-5.136.187-10.275.426-15.406.715-2.982.165-5.949.367-8.916.591a138 138 0 0 0-4.435.394c-.422.041-.835.087-1.256.142-3.87 2.315-18.715 11.634-28.905 23.818 9.903-6.881 27.153-17.222 48.454-23.184m-27.327 48.008a61 61 0 0 1-.55-1.862l-4.572-3.2a1.23 1.23 0 0 1-.52-.858l-2.228-18.114c-1.651.885-3.558 1.971-4.068 2.398-.44.363-.775 1.294-.297 5.61.592 5.323 3.288 15.353 4.737 16.81 1.743.308 5.605-.228 7.498-.784m-3.379-45.495c-2.582.5-4.688.949-6.246 1.302l.735 2.77a92 92 0 0 1 5.511-4.072'/></svg>",
+ "tree": "<svg viewBox='0 0 32 32'><path fill='#7cb342' d='M4 4h10v6H4zm16 10h10v6H20zm0 8h10v6H20zm-4-4v-2h-6v-4H8v14h8v-2h-6v-6z'/></svg>",
+ "trigger": "<svg fill='none' viewBox='0 0 32 32'><path fill='#4caf50' fill-rule='evenodd' d='M11.158 13.51 16 5l12 21.09H4l4.842-8.51 3.425 2.007-1.416 2.49h10.298L16 13.027l-1.417 2.49z' clip-rule='evenodd'/></svg>",
+ "tsconfig": "<svg viewBox='0 0 32 32'><path fill='#757575' d='M15 2H6a2.006 2.006 0 0 0-2 2v22a2.006 2.006 0 0 0 2 2h6v-4H6v-2h6v-2H6v-2h6v-2H6v-2h6v-2h2V4l8 8h2v-1Z' data-mit-no-recolor='true'/><path fill='#0288d1' d='M12 12v18h18V12Zm8 6h-2v8h-2v-8h-2v-2h6Zm8 0h-4v2h2a2.006 2.006 0 0 1 2 2v2a2.006 2.006 0 0 1-2 2h-4v-2h4v-2h-2a2.006 2.006 0 0 1-2-2v-2a2.006 2.006 0 0 1 2-2h4Z'/></svg>",
+ "tsil": "<svg viewBox='0 0 16 16'><path fill='#795548' d='M14 13.3a.7.7 0 0 1-.7.7H8V8h6z'/><path fill='#ffe57f' d='M14 8H8V2h5.3a.7.7 0 0 1 .7.7z'/><path fill='#ffab40' d='M8 8H2V2.7a.7.7 0 0 1 .7-.7H8z'/><path fill='#212121' d='M8 14H2.7a.7.7 0 0 1-.7-.7V8h6z'/></svg>",
+ "tune": "<svg viewBox='0 0 32 32'><path fill='#fbc02d' d='M12 10h10v2H12z'/><path fill='#fbc02d' d='M16 4h2v8h-2zm4 18h10v2H20zm4 2h2v4h-2zm0-20h2v14h-2zM2 18h10v2H2z'/><path fill='#fbc02d' d='M6 18h2v10H6zM6 4h2v10H6zm10 12h2v12h-2z'/></svg>",
+ "turborepo": "<svg viewBox='0 0 32 32'><defs><linearGradient id='a' x1='27.349' x2='7.613' y1='26.455' y2='6.719' gradientTransform='matrix(1 0 0 -1 0 34)' gradientUnits='userSpaceOnUse'><stop offset='.15' stop-color='#2196f3'/><stop offset='.85' stop-color='#f50057'/></linearGradient></defs><path fill='#cfd8dc' d='M16 8a8 8 0 1 0 8 8 8 8 0 0 0-8-8m0 12a4 4 0 1 1 4-4 4 4 0 0 1-4 4'/><path fill='url(#a)' d='M4.281 23.647A13.9 13.9 0 0 1 2 16h4a9.95 9.95 0 0 0 1.192 4.736ZM14 29.84v-4.042a9.9 9.9 0 0 1-3.892-1.732l-2.854 2.855A13.9 13.9 0 0 0 14 29.84M16 2v4a10 10 0 0 1 2 19.8v4.04A13.992 13.992 0 0 0 16 2'/></svg>",
+ "turborepo_light": "<svg viewBox='0 0 32 32'><defs><linearGradient id='a' x1='27.349' x2='7.613' y1='26.455' y2='6.719' gradientTransform='matrix(1 0 0 -1 0 34)' gradientUnits='userSpaceOnUse'><stop offset='.15' stop-color='#2196f3'/><stop offset='.85' stop-color='#f50057'/></linearGradient></defs><path fill='#455a64' d='M16 8a8 8 0 1 0 8 8 8 8 0 0 0-8-8m0 12a4 4 0 1 1 4-4 4 4 0 0 1-4 4'/><path fill='url(#a)' d='M4.281 23.647A13.9 13.9 0 0 1 2 16h4a9.95 9.95 0 0 0 1.192 4.736ZM14 29.84v-4.042a9.9 9.9 0 0 1-3.892-1.732l-2.854 2.855A13.9 13.9 0 0 0 14 29.84M16 2v4a10 10 0 0 1 2 19.8v4.04A13.992 13.992 0 0 0 16 2'/></svg>",
+ "twig": "<svg viewBox='0 0 50 50'><path fill='#8bc34a' d='M9.727 47.556c-.125-.223-.297-2.168-.183-2.087.034.025.171.267.304.537s.282.487.332.482c.123-.011.075-1.196-.1-2.454-.331-2.397-1.176-4.434-2.358-5.69-.2-.212-.344-.4-.319-.418.093-.067 1.327.842 1.842 1.358.293.293.735.825.981 1.182.328.474.465.618.51.533.078-.147-.21-9.903-.376-12.7-.074-1.256.063-1.023.61 1.034 1.064 4.007 1.858 7.922 2.342 11.55.086.637.173 1.173.195 1.19s.092.002.157-.033c.888-.484 1.524-.667 2.55-.736.727-.049.945.062.35.177-1.15.223-1.99 1.013-2.344 2.201-.315 1.061-.327 2.707-.024 3.435.152.365.037.425-1.067.56-.716.088-.977.095-1.202.037-.356-.093-1.118-.098-1.195-.009-.031.037-.243.066-.47.066-.38 0-.423-.017-.534-.215zm1.974-3.232c.152-.205.072-.412-.204-.522-.225-.091-.263-.089-.437.024-.21.137-.252.43-.08.554.18.13.607.096.72-.056zm1.248.085a.8.8 0 0 0 .214-.202c.241-.33-.352-.622-.745-.366-.406.264.08.785.531.568m2.288 3.094c-.033-.038.117-.387.334-.774.216-.388.411-.666.433-.619.07.153-.201 1.28-.33 1.373-.15.107-.354.116-.437.02m-7.036-.41c-.29-.344-.221-.434.14-.183.176.124.321.264.321.311 0 .164-.279.086-.46-.129zm8.649-.146c0-.053.102-.18.227-.282.25-.204.312-.113.143.208-.095.18-.37.235-.37.074m8.065-.827c-.243-.025-.48-.088-.527-.14-.11-.125-.114-3.044-.004-3.044.045 0 .132.15.193.331q.189.57.31.124c.094-.337.065-3.471-.039-4.296-.449-3.55-1.865-6.124-4.342-7.89-1.086-.774-2.653-1.437-4.047-1.712-.764-.15-.522-.224.598-.182 2.364.09 4.167.707 5.847 2.002a11 11 0 0 1 2.32 2.501c.453.682.64.854.64.584 0-.07.063-.882.139-1.805.679-8.26 2.396-15.1 4.984-19.86 1.86-3.422 5.108-6.817 7.885-8.244 1.397-.717 2.539-.988 4.02-.952.933.023 1.01.036 1.77.308a6.8 6.8 0 0 1 1.363.661c.612.407 1.309 1.004 1.234 1.058-.025.018-.342-.165-.704-.407-2.657-1.771-5.062-1.52-7.12.742-1.108 1.22-2.652 3.53-3.634 5.443-2.828 5.503-4.541 11.464-5.291 18.413-.163 1.509-.282 3.76-.195 3.703.032-.022.266-.52.518-1.108 1.597-3.723 3.578-6.428 5.79-7.908.672-.45 1.612-.904 1.714-.83.023.016-.17.22-.43.453-1.958 1.755-3.25 3.76-4.233 6.573-.938 2.68-1.366 5.588-1.369 9.299 0 1.742.189 4.385.367 5.102.125.505.08.546-.585.546-.55 0-2.306.138-3.417.27-.413.05-.816.04-1.608-.036-.58-.056-1.13-.119-1.219-.14-.164-.037-.18-.014-.198.302-.012.186-.1.203-.73.139m2.507-6.725c.294-.11.375-.22.375-.517 0-.63-1.309-.706-1.524-.088-.074.212.13.51.42.616.297.109.413.107.73-.011zm2.369-.052c.277-.222.318-.364.174-.611-.4-.69-1.755-.307-1.428.405.121.265.299.35.738.353.227.001.387-.044.516-.147m3.011 6.681c-.027-.05.088-.267.256-.483.879-1.135 1.22-1.545 1.285-1.545.039 0 .055.037.035.083l-.423.963c-.213.484-.445.925-.519.977-.169.122-.57.125-.634.005m2.446-.596c0-.12.853-.683.896-.59.018.04-.056.21-.166.377-.168.258-.238.304-.464.304-.164 0-.266-.035-.266-.09zm-13.04-.124c-.176-.159-.493-.656-.461-.725.018-.038.248.1.512.309s.456.405.428.438c-.076.088-.372.074-.479-.022'/></svg>",
+ "twine": "<svg viewBox='0 0 24 24'><path fill='#1e88e5' d='M4.229 3.119h6.657v17.755H4.23z'/><path fill='#69f0ae' d='M4.229 17.545c0-12.207 15.535-12.207 15.535-12.207v6.658s-8.877 0-8.877 5.549v3.329H4.229z'/></svg>",
+ "typescript-def": "<svg viewBox='0 0 16 16'><g fill='#0288d1'><path d='M2 2v12h12V2zm1 1h10v10H3z'/><path d='M5 7v1h1v4h1V8h1V7zm5 0a1.003 1.003 0 0 0-1 1v1a1.003 1.003 0 0 0 1 1h1v1H9v1h2a1.003 1.003 0 0 0 1-1v-1a1.003 1.003 0 0 0-1-1h-1V8h2V7z'/></g></svg>",
+ "typescript": "<svg viewBox='0 0 16 16'><path fill='#0288d1' d='M2 2v12h12V2zm4 6h3v1H8v4H7V9H6zm5 0h2v1h-2v1h1a1.003 1.003 0 0 1 1 1v1a1.003 1.003 0 0 1-1 1h-2v-1h2v-1h-1a1.003 1.003 0 0 1-1-1V9a1.003 1.003 0 0 1 1-1'/></svg>",
+ "typst": "<svg viewBox='0 0 32 32'><path fill='#0097a7' d='M21.98 24.84A10.8 10.8 0 0 1 19.72 26a4.3 4.3 0 0 1-1.46.3 2.4 2.4 0 0 1-1.93-.64c-.36-.42-.33-1.25-.33-2.48V14h4v-4h-4V2l-6 2v6H8v4h2v9.91q0 3.405 1.59 4.77A6.64 6.64 0 0 0 15.94 30a4 4 0 0 0 .47-.03c1.54-.15 3.4-1.08 5.59-2.77.31-.24.63-.49.95-.76Z'/></svg>",
+ "umi": "<svg viewBox='0 0 16 16'><path fill='#455a64' d='M1 8a7 7 0 0 0 4 6.316V15h6v-.684A7 7 0 0 0 15 8z'/><path fill='#f5f5f5' d='M8 3a5 5 0 0 0-5 5h10a5 5 0 0 0-5-5'/><path fill='#2196f3' d='M2.092 9A6 6 0 0 0 8 14a6 6 0 0 0 5.908-5z'/><g fill='#455a64'><g stroke-width='0'><circle cx='5.5' cy='6.5' r='.5'/><circle cx='10.5' cy='6.5' r='.5'/><circle cx='6.5' cy='5.5' r='.5'/></g><path d='M8 2a6 6 0 0 0-6 6h1a5 5 0 0 1 5-5 5 5 0 0 1 5 5h1a6 6 0 0 0-6-6'/></g></svg>",
+ "uml": "<svg viewBox='0 0 100 100'><path fill='#b39ddb' d='M87 76.652 53.84 93.907l-.038-41.04 13.9-7.15v29.622l19.224-9.85z'/><path fill='#fbc02d' d='m38.693 89.604 8.576 4.303V52.743l-13.027-6.29-4.126 19.643-4.16-23.69L13 36.077V77.28l8.54 4.378V56.826l4.669 26.817 7.599 3.863 4.885-22.293z'/><path fill='#f06292' d='m45.237 6.093-9.775 8.755s19.072 9.931 21.39 11.105c2.317 1.173 5.615 3.43 2.05 6.771s-7.487 2.89-10.16 1.535a21830 21830 0 0 1-22.458-11.466l-10.07 8.667S35.642 41.48 38.85 43.196c3.208 1.715 15.15 5.958 26.47-2.98 11.318-8.937 9.714-12.188 9.714-12.82s-.267-3.972-2.228-6.048c-1.96-2.077-7.664-5.056-10.07-6.32S45.239 6.092 45.239 6.092z'/></svg>",
+ "uml_light": "<svg viewBox='0 0 100 100'><path fill='#9575cd' d='M67.702 45.716V75.34l19.224-9.85L87 76.653 53.84 93.907l-.038-41.04z'/><path fill='#f9a825' d='m30.116 66.096-4.16-23.69L13 36.077V77.28l8.54 4.378V56.826l4.669 26.817 7.599 3.863 4.885-22.293v24.391l8.576 4.303V52.743l-13.027-6.29z'/><path fill='#ec407a' d='m45.237 6.093-9.775 8.755s19.072 9.931 21.39 11.105c2.317 1.174 5.615 3.43 2.05 6.772-3.565 3.34-7.487 2.889-10.16 1.535a21830 21830 0 0 1-22.458-11.468l-10.07 8.667S35.641 41.482 38.85 43.196c3.208 1.716 15.15 5.959 26.47-2.979 11.318-8.938 9.714-12.188 9.714-12.82s-.267-3.972-2.228-6.049c-1.96-2.076-7.664-5.056-10.07-6.32S45.239 6.093 45.239 6.093z'/></svg>",
+ "unity": "<svg viewBox='0 0 16 16'><path fill='#42a5f5' d='M8 6.5 5 5l2-1V2L2 5v5l2-1V6.5L7 8v4.5L4 11l-2 1 6 3 6-3-2-1-3 1.5V8l3-1.5V9l2 1V5L9 2v2l2 1Z'/></svg>",
+ "unocss": "<svg viewBox='0 0 32 32'><circle cx='24' cy='24' r='6' fill='#78909c'/><path fill='#546e7a' d='M2 18v6a6 6 0 0 0 12 0v-6Z'/><path fill='#b0bec5' d='M30 14V8a6 6 0 0 0-12 0v6Z'/></svg>",
+ "url": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='M10 14h12v4H10z'/><path fill='#42a5f5' d='M12 22H9.562A5.57 5.57 0 0 1 4 16.438v-.876A5.57 5.57 0 0 1 9.562 10H12V6H9.562A9.56 9.56 0 0 0 0 15.562v.876A9.56 9.56 0 0 0 9.562 26H12ZM22.438 6H20v4h2.438A5.57 5.57 0 0 1 28 15.562v.876A5.57 5.57 0 0 1 22.438 22H20v4h2.438A9.56 9.56 0 0 0 32 16.438v-.876A9.56 9.56 0 0 0 22.438 6'/></svg>",
+ "uv": "<svg viewBox='0 0 16 16'><path fill='#e040fb' d='M2 2v11c0 .5.5 1 1 1h8c.5 0 1-.5 1-1h1v1h1V2H8v8H7V2z'/></svg>",
+ "vagrant": "<svg viewBox='0 0 140.625 140.625'><path fill='#1565c0' d='m70.315 132.051 23.269-13.42 36.445-89.24V18.084l-27.142 15.791v9.539L81.16 90.26l-10.846 7.494zM59.449 92.32l10.866-5.365V73.322L54.028 35.326v-10.75l-.112-.064-16.174 9.362v9.539z'/><path fill='#2979ff' d='M86.597 24.463v10.862L70.312 73.32v12.697l-10.862 6.3-21.708-48.904V33.86l16.285-9.38L26.88 8.577l-16.286 9.506v11.644l36.654 89.018 23.064 13.302V98.615l10.847-6.3-.128-.08 21.852-48.824v-9.554l27.148-15.775-16.286-9.507-27.131 15.886z'/></svg>",
+ "vala": "<svg viewBox='0 0 64 64'><defs><linearGradient id='c' x1='25.058' x2='25.058' y1='47.028' y2='39.999' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#212121' stop-opacity='0'/><stop offset='.5' stop-color='#212121'/><stop offset='1' stop-color='#212121' stop-opacity='0'/></linearGradient><linearGradient id='e' x1='24' x2='24' y1='5' y2='43' gradientTransform='matrix(1.4324 0 0 1.4363 134.03 -5.86)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#fafafa'/><stop offset='.063' stop-color='#fafafa' stop-opacity='.235'/><stop offset='.951' stop-color='#fafafa' stop-opacity='.157'/><stop offset='1' stop-color='#fafafa' stop-opacity='.392'/></linearGradient><linearGradient id='d' x1='31.293' x2='31.293' y1='5.008' y2='59.329' gradientTransform='translate(136.41 -3.39)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ba68c8'/><stop offset='1' stop-color='#673ab7'/></linearGradient><radialGradient id='a' cx='4.993' cy='43.5' r='2.5' gradientTransform='matrix(2.0038 0 0 1.4 27.988 -17.4)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#212121'/><stop offset='1' stop-color='#212121' stop-opacity='0'/></radialGradient><radialGradient id='b' cx='4.993' cy='43.5' r='2.5' gradientTransform='matrix(2.0038 0 0 1.4 -20.012 -104.4)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#212121'/><stop offset='1' stop-color='#212121' stop-opacity='0'/></radialGradient></defs><g opacity='.6'><path fill='url(#a)' d='M38 40h5v7h-5z' transform='matrix(1.579 0 0 .71429 130.515 24.54)'/><path fill='url(#b)' d='M-10-47h5v7h-5z' transform='matrix(-1.579 0 0 -.71429 130.515 24.54)'/><path fill='url(#c)' d='M10 40h28v7H10z' transform='matrix(1.579 0 0 .71429 130.515 24.54)'/></g><rect width='55' height='55' x='140.91' y='1.11' fill='url(#d)' rx='3' ry='3'/><rect width='53' height='53.142' x='141.91' y='2.039' stroke='url(#e)' stroke-linecap='round' stroke-linejoin='round' opacity='.3' rx='2' ry='2'/><rect width='55' height='55' x='140.91' y='1.11' stroke='#4a148c' stroke-linecap='round' stroke-linejoin='round' opacity='.5' rx='3' ry='3'/><path fill='#9575cd' d='m26.357 57.882-1.111-47.15q-4.854 1.82-7.583 5.694-2.698 3.877-2.698 9.64 0 1.314.136 2.157.167.809.336 1.314.169.472.305.742.167.27.167.472-1.786 0-3.167-.336-1.383-.372-2.327-1.147-.91-.773-1.415-2.055-.473-1.28-.473-3.167 0-2.292.976-4.516 1.011-2.223 2.73-4.213 1.753-1.987 4.08-3.673 2.36-1.685 5.021-2.899 2.695-1.247 5.594-1.92 2.932-.71 5.831-.71.775 0 1.416.034.673.033 1.346.1l.608 42.465L50.654 6.45h4.819L36.298 57.883h-9.943z'/></svg>",
+ "vedic": "<svg viewBox='0 0 288 288'><svg viewBox='0 -15 356 400'><path fill='#ff3d00' d='M90.457 353.95c-38.66-13.815-66.73-48.192-77.845-95.332-5.044-21.395-6.47-56.748-2.288-56.748 1.389 0 5.1 9.7 8.245 21.557 6.884 25.945 18.625 50.342 29.967 62.267 18.839 19.808 65.5 27.566 92.385 15.36 20.943-9.509 29.436-32.108 20.329-54.095-7.038-16.99-23.003-22.67-52.742-18.767 0 0-18.225-19.618-24.032-54.457l18.681 1.694c22.5 2.04 39.488-2.933 48.305-14.142 8.286-10.533 8.107-14.607-1.114-25.325-13.304-15.468-37.193-11.55-85.561 14.033l-24.405-40.91 10.231-7.804c25.64-19.557 70.16-29.334 95.497-20.972 23.078 7.617 40.017 37.839 35.492 63.324-3.059 17.23-16.874 41.362-27.548 48.12l-9.205 5.829 12.715 5.733c21.606 9.743 34.797 2.295 50.556-28.547 21.81-42.681 35.954-53.73 68.847-53.777 15.315-.023 20.766 1.584 31.936 9.412 27.88 19.537 43.06 59.994 39.725 105.87-4.223 58.101-31.744 93.343-72.894 93.343-22.583 0-37.14-7.92-48.727-26.514-10.177-16.333-14.764-48.68-8.919-62.908 2.804-6.827 3.31-7.058 3.494-1.597.337 10.04 11.76 26.358 22.246 31.781 25.73 13.306 62.667-3.411 77.28-34.975 11.095-23.964 5.143-70.186-10.087-78.337-3.186-1.706-11.06-3.101-17.497-3.101-13.682 0-24.427 9.837-39.491 36.153-5.209 9.098-13.974 20.854-19.478 26.123-8.944 8.562-12.137 9.581-30.024 9.581h-20.017l6.47 14.372c9.261 20.57 8.823 53.993-.974 74.34-8.657 17.979-28.674 36.18-44.676 40.626-15.578 4.328-40.946 3.768-54.877-1.21m75.377-278.026c-8.855-15.11-14.304-43.318-8.369-43.318 6.973 15.126 21.265 28.621 36.57 38.037 27.486 13.306 55.358 7.936 85.807-16.535 6.704-5.387 12.64-9.195 13.192-8.462 7.436 11.538 20.297 20.967 24.548 34.375 0 5.658-24.353 21.94-43.57 29.13-47.63 13.72-80.046 8.292-108.178-33.227m24.002-34.927 31.01-34.383 34.46 31.9c-11.787 9.709-20.296 24.775-33.762 32.416-10.64-7.844-26.52-17.092-31.708-29.933' class='colorff4500 svgShape'/></svg></svg>",
+ "velite": "<svg fill='none' viewBox='0 0 16 16'><path fill='#43a047' d='m5.767 7.155.918 2.46L5 13 1 3.014c.444 0 .866.107 1.267.221 1.433.408 2.594 1.594 3.5 3.92'/><path fill='#26a69a' d='M15 3.014c-1.357-.423-2.581.024-3.602.943L6.75 8.285C4.833 10.077 3.69 9.871 3 8l2 5z'/></svg>",
+ "velocity": "<svg viewBox='0 0 300 300'><path fill='#0288d1' d='M150 61.553A88.446 88.446 0 0 0 61.553 150 88.446 88.446 0 0 0 150 238.446 88.446 88.446 0 0 0 238.446 150 88.446 88.446 0 0 0 150 61.553m.011 25.082a63.353 63.353 0 0 1 63.353 63.353 63.353 63.353 0 0 1-63.353 63.353 63.353 63.353 0 0 1-63.353-63.353 63.353 63.353 0 0 1 63.353-63.353' paint-order='fill markers stroke'/><path fill='#0288d1' d='M45.008 193.096 12.213 225.89l32.795 32.795V238.44h104.99v-25.098H45.008zM74.088 12.21 41.293 45.006h20.246v104.99h25.098V45.007h20.246zm180.901 29.093V61.55h-104.99v25.097h104.99v20.246L287.784 74.1zM213.32 149.998V254.99h-20.245l32.794 32.795 32.795-32.795h-20.246V150z'/></svg>",
+ "vercel": "<svg viewBox='0 0 32 32'><path fill='#cfd8dc' d='m16 6 12 20H4Z'/></svg>",
+ "vercel_light": "<svg viewBox='0 0 32 32'><path fill='#455a64' d='m16 6 12 20H4Z'/></svg>",
+ "verdaccio": "<svg viewBox='0 0 24 24'><path fill='#00897b' d='M18.2 10.237h-4.448l-1.827 3.654-4.812-9.624H2.665l7.96 15.92h2.6z' clip-rule='evenodd'/><path fill='#e57373' d='M14.845 3.813v.7h1.767l-.416.825h-2.773v.7h2.42l-.546 1.085h-3.264v.7h3.526l3.766.017 2.01-4.018-1.1-.003v-.006z'/></svg>",
+ "verified": "<svg viewBox='0 0 24 24'><path fill='#8bc34a' d='M9 3 8 6H4l1 4-3 2 3 2-1 4h4l1 3 3-2 3 2 1-3h4l-1-4 3-2-3-2 1-4h-4l-1-3-3 2zm7 5 1 1-7 7-3-3 1-1 2 2z'/></svg>",
+ "verilog": "<svg viewBox='0 0 32 32'><path fill='#ff7043' d='M21.833 8A2.17 2.17 0 0 1 24 10.167v11.666A2.17 2.17 0 0 1 21.833 24H10.167A2.17 2.17 0 0 1 8 21.833V10.167A2.17 2.17 0 0 1 10.167 8zm0-2H10.167A4.167 4.167 0 0 0 6 10.167v11.666A4.167 4.167 0 0 0 10.167 26h11.666A4.167 4.167 0 0 0 26 21.833V10.167A4.167 4.167 0 0 0 21.833 6'/><path fill='#ff7043' d='M18 14v4h-4v-4zm2-2h-8v8h8zM2 12h4v2H2zm0 6h4v2H2zm24-6h4v2h-4zm0 6h4v2h-4zm-8 8h2v4h-2zm-6 0h2v4h-2zm6-24h2v4h-2zm-6 0h2v4h-2z'/></svg>",
+ "vfl": "<svg viewBox='0 0 24 24'><defs><radialGradient id='a' cx='205.45' cy='208.29' r='225.35' gradientTransform='matrix(.04556 0 0 .0456 2.888 2.88)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#ffd600'/><stop offset='.35' stop-color='#f9a825'/><stop offset='1' stop-color='#f4511e'/></radialGradient></defs><g stroke-width='.046'><path fill='#f4511e' d='M19.97 3H4.03A1.03 1.03 0 0 0 3 4.03v4.136c1.548-1.19 3.563-1.958 5.948-1.958 5.107.004 8.35 3.575 8.348 8.082 0 3.13-1.46 5.485-3.745 6.71h6.419A1.03 1.03 0 0 0 21 19.967V4.031a1.03 1.03 0 0 0-1.03-1.03z'/><path fill='url(#a)' d='M3 17.722v2.247A1.03 1.03 0 0 0 4.03 21h1.837C4.474 20.21 3.49 19 3 17.722'/><path fill='#f4511e' d='M8.948 8.23C6.362 8.142 4.35 9.09 3 10.496v3.162c.918-2.653 3.447-3.87 5.565-3.849 2.647.027 4.689 2.025 4.7 4.284.012 2.158-.892 3.748-3.33 4.14-1.33.213-3.41-.568-3.318-2.578.046-1.037.854-1.622 1.777-1.58-.904 1.213.293 2.102 1.139 1.92 1.048-.223 1.475-1.155 1.475-1.877 0-.762-.717-1.994-2.498-1.952-2.204.053-3.59 1.64-3.638 3.603-.056 2.468 2.254 4.091 4.623 4.12 3.478.046 5.542-2.24 5.538-5.585-.005-3.03-2.434-5.946-6.085-6.072z'/></g></svg>",
+ "video": "<svg viewBox='0 0 32 32'><path fill='#ff9800' d='m24 6 2 6h-4l-2-6h-3l2 6h-4l-2-6h-3l2 6H8L6 6H5a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h22a3 3 0 0 0 3-3V6Z'/></svg>",
+ "vim": "<svg viewBox='0 0 32 32'><path fill='#43a047' d='M22.19 4H16v4h2.19L12 14.19V8h2V4H2v4h2v20h4v-.01l.01.01L28 8V4z'/></svg>",
+ "virtual": "<svg viewBox='0 0 32 32'><path fill='#039be5' d='M28 24V6H4v18H2v2h28v-2Zm-8 0h-8v-2h8Zm6-4H6V8h20Z'/></svg>",
+ "visualstudio": "<svg viewBox='0 0 32 32'><path fill='#ab47bc' d='m22 11.8-5.7 4.584L22 20.8zM7.24 23.68 4 21.64v-10.8l3.6-1.2 5.16 3.996L23.2 4 28 7v18.6L22 28l-9.192-8.808zm.36-5.28 2.232-2.064L7.6 14.2Z'/></svg>",
+ "vite": "<svg viewBox='0 0 32 32'><path fill='#ffab00' d='M10 2v16h4v12l9-16h-6l5-12Z'/></svg>",
+ "vitest": "<svg viewBox='0 0 32 32'><path fill='#689f38' d='M16.094 30.074a1.4 1.4 0 0 1-1.003-.416l-6.622-6.622a1.42 1.42 0 0 1 2.006-2.006l5.62 5.618 12.24-12.24a1.419 1.419 0 0 1 2.007 2.006L17.098 29.658a1.4 1.4 0 0 1-1.004.416'/><path fill='#689f38' fill-opacity='.502' d='M16.089 30.074a1.4 1.4 0 0 0 1.003-.416l6.622-6.622a1.42 1.42 0 0 0-2.006-2.006l-5.62 5.618-12.24-12.24a1.42 1.42 0 0 0-2.007 2.006l13.244 13.244a1.4 1.4 0 0 0 1.004.416'/><path fill='#ffca28' d='M24 10h-6V2l-8 12h6v8z'/></svg>",
+ "vlang": "<svg style='isolation:isolate' viewBox='0 0 500 500'><path fill='#546e7a' d='m311.64 433.372 130.885-363.97c2.22-6.173-1.28-10.674-7.809-10.044l-102.93 9.915c-6.529.63-13.582 6.17-15.739 12.363L194.901 429.48c-2.158 6.194 1.416 11.223 7.975 11.223h100.191c3.28 0 6.843-2.505 7.953-5.592z'/><path fill='#039be5' d='m65.278 59.359 102.93 9.915c6.529.63 13.59 6.167 15.757 12.358l123.714 353.456c1.083 3.097-.7 5.608-3.98 5.608H202.877c-6.56 0-13.688-5.01-15.907-11.183L57.472 69.398c-2.22-6.173 1.28-10.674 7.809-10.044z'/></svg>",
+ "vscode": "<svg viewBox='0 0 16 16'><path fill='#2196f3' d='M11.5 11.19V4.8L7.3 7.99M1.17 6.07a.6.6 0 0 1-.01-.81L2 4.48c.14-.13.48-.18.73 0l2.39 1.83 5.55-5.09c.22-.22.61-.32 1.05-.08l2.8 1.34c.25.15.49.38.49.81v9.49c0 .28-.2.58-.42.7l-3.08 1.48c-.22.09-.64 0-.79-.14L5.11 9.69l-2.38 1.83c-.27.18-.6.13-.74 0l-.84-.77c-.22-.23-.2-.61.04-.84l2.1-1.9'/></svg>",
+ "vue-config": "<svg viewBox='0 0 32 32'><path fill='#757575' d='M15 2H6a2.006 2.006 0 0 0-2 2v22a2.006 2.006 0 0 0 2 2h16a2 2 0 0 0 2-2V11Zm3 22H6v-2h12Zm0-4H6v-2h12Zm0-4H6v-2h12Zm-4-4V4l8 8Z' data-mit-no-recolor='true'/><path fill='#41b883' d='m14 16 8 14.093 8-14.024V16h-3.11l-4.843 8.49L17.225 16Z'/><path fill='#35495e' d='m17.225 16 4.821 8.492 4.844-8.491h-2.918l-1.906 3.342-1.9-3.343Z'/></svg>",
+ "vue": "<svg viewBox='0 0 24 24'><path fill='#41b883' d='M1.791 3.851 12 21.471 22.209 3.936V3.85H18.24l-6.18 10.616L5.906 3.851z'/><path fill='#35495e' d='m5.907 3.851 6.152 10.617L18.24 3.851h-3.723L12.084 8.03 9.66 3.85z'/></svg>",
+ "vuex-store": "<svg viewBox='0 0 16 16'><path fill='#41b883' d='M1.646 14.41 6.729 4.157l1.27 2.501v2.63l-2.525 5.124zm12.708.009L9.27 4.164 8 6.665v2.63l2.517 5.124z'/><path fill='#35495e' d='M1.646 1.582 4.823 8l1.906-3.844-1.27-2.574zm12.708 0L11.177 8 9.27 4.156l1.27-2.574z'/></svg>",
+ "wakatime": "<svg fill='none' viewBox='0 0 340 340'><path stroke='#f5f5f5' stroke-width='33.39' d='M170 44.788c-69.154 0-125.212 56.058-125.212 125.212s56.058 125.213 125.213 125.213S295.212 239.155 295.212 170 239.155 44.788 170 44.788z'/><path fill='#f5f5f5' d='M186.846 206.343c-1.205 1.588-3.011 2.61-5.035 2.61a6 6 0 0 1-.591-.034 7 7 0 0 1-.7-.109 6.7 6.7 0 0 1-1.15-.385 8 8 0 0 1-.547-.28 6.6 6.6 0 0 1-.856-.591 7 7 0 0 1-.42-.367 8 8 0 0 1-.586-.64 7.5 7.5 0 0 1-.754-1.144l-7.378-11.854-7.374 11.854c-1.157 2.107-3.249 3.55-5.652 3.55-2.412 0-4.514-1.454-5.636-3.607l-32.252-46.985c-1.06-1.278-1.712-2.973-1.712-4.844 0-3.96 2.911-7.173 6.501-7.173 2.324 0 4.358 1.35 5.508 3.375l27.224 40.228 7.663-12.477c1.104-2.224 3.248-3.734 5.71-3.734 2.252 0 4.238 1.266 5.404 3.188l7.903 12.972 42.712-61.15c1.16-1.967 3.164-3.269 5.45-3.269 3.59 0 6.5 3.212 6.5 7.172 0 1.73-.553 3.317-1.478 4.555z'/></svg>",
+ "wakatime_light": "<svg fill='none' viewBox='0 0 340 340'><path stroke='#455a64' stroke-width='33.39' d='M170 44.788c-69.154 0-125.212 56.058-125.212 125.212s56.058 125.213 125.213 125.213S295.212 239.155 295.212 170 239.155 44.788 170 44.788z'/><path fill='#455a64' d='M186.846 206.343c-1.205 1.588-3.011 2.61-5.035 2.61a6 6 0 0 1-.591-.034 7 7 0 0 1-.7-.109 6.7 6.7 0 0 1-1.15-.385 8 8 0 0 1-.547-.28 6.6 6.6 0 0 1-.856-.591 7 7 0 0 1-.42-.367 8 8 0 0 1-.586-.64 7.5 7.5 0 0 1-.754-1.144l-7.378-11.854-7.374 11.854c-1.157 2.107-3.249 3.55-5.652 3.55-2.412 0-4.514-1.454-5.636-3.607l-32.252-46.985c-1.06-1.278-1.712-2.973-1.712-4.844 0-3.96 2.911-7.173 6.501-7.173 2.324 0 4.358 1.35 5.508 3.375l27.224 40.228 7.663-12.477c1.104-2.224 3.248-3.734 5.71-3.734 2.252 0 4.238 1.266 5.404 3.188l7.903 12.972 42.712-61.15c1.16-1.967 3.164-3.269 5.45-3.269 3.59 0 6.5 3.212 6.5 7.172 0 1.73-.553 3.317-1.478 4.555z'/></svg>",
+ "wallaby": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='M16 2v14H2v14h28V2z'/></svg>",
+ "wally": "<svg viewBox='0 0 32 32'><path fill='#e65100' d='M8.454 3.084c-.897-.112-1.438.473-2.502 2.43-1.457 2.682-3.888 1.135-1.765 5.275a4.7 4.7 0 0 0 .759 1.122l5.749-9.219c-.84-.614-1.513.448-2.241.392'/><path fill='#ffcc80' d='m28.565 29.985-5.982-8.28a1.67 1.67 0 0 0-.57-1.362c-1.794-1.789-2.69-3.51-1.905-6.975a3.94 3.94 0 0 0-1.326-3.766 29 29 0 0 0 .206-3.22s-.673-2.907-2.354-3.13c0 0-4.426-2.236-7.956.279S7.95 5.71 5.82 8.897a4.5 4.5 0 0 0-.444.867 7 7 0 0 0-1.237.307c-1.4.56-1.344 2.07.56 3.635a1 1 0 0 0 .29.148 8.2 8.2 0 0 1-.29 2.198c-.595 1.674.853 3.91 3.024 1.789a7 7 0 0 0 1.648.942 8.1 8.1 0 0 1 .707 2.522l-.558 8.68Z'/><path fill='#3e2723' d='M8.651 9.017a.803.803 0 0 1-.807-.805v-1.88a.807.807 0 0 1 1.615 0v1.88a.803.803 0 0 1-.808.805m5.314 0a.814.814 0 0 1-.813-.811V6.339a.814.814 0 0 1 1.627 0v1.948a.8.8 0 0 1-.814.73'/><path fill='#e65100' d='M17.082 14.15a6.06 6.06 0 0 1-4.818 4.528 4.75 4.75 0 0 1-5.828-3.13 3.8 3.8 0 0 0 2.999 3.679c.84 1.302.355 3.547.307 4.874a5.4 5.4 0 0 1-.061.84l-1.08 5.044 5.421.015c-.724-.397-1.02-2.476-1.14-3.901l-.079-1.405a1.415 1.415 0 0 1 .974-1.432c2.796-1.81-.672-1.789-.672-2.292a3.6 3.6 0 0 1 1.068-2.195c2.535-1.273 3.114-4.013 2.91-4.624'/><path fill='#e65100' d='M20.518 17.614c.588-.523 1.012-2.457 1.492-3.362.785-1.48 4.797-2.989 3.136-5.446-1.55-2.292-2.24-1.658-3.136-3.28-.84-1.452-1.073-1.763-3.154-1.763a7.7 7.7 0 0 1-2.296-1.124A3.07 3.07 0 0 0 14.531 2c-3.98 0-4.9-.091-5.853 1.195 0 0 4.429-1.841 8.025.533 1.213 1.634 2.441 4.61 1.612 9.64a8.8 8.8 0 0 0 .093 3.228 1.5 1.5 0 0 0 .096.466s4.621 9.882 9.222 12.922H29s-5.867-7.368-8.482-12.371M5.035 16.05a2.496 2.496 0 0 0-1.176-3.24 3 3 0 0 1 .56 3.185c-.56 1.118 0 3.242 2.073 2.627 0 0-2.41.112-1.457-2.571'/><path fill='#b71c1c' d='M9.736 23.933a9.63 9.63 0 0 0 8.41-.28c3.195-1.733 3.788-3.732 3.451-4.123a2.42 2.42 0 0 1 2.053 1.036c.56.839-.46 3.645-5.167 5.602-4.427 1.788-7.848.974-8.632.191-.729-.782-.115-2.426-.115-2.426m-6.55-13.192c-.616.615.393 2.46 1.625 3.018 1.401.615 3.418-1.788 3.026-2.459-.448-.894-3.362-1.844-4.65-.559'/></svg>",
+ "watchman": "<svg viewBox='0 0 420 419'><circle cx='210' cy='209.5' r='188.15' fill='#fafafa' paint-order='stroke fill markers'/><path fill='#304ffe' d='M191.07 397.1c-35.512-4.049-66.779-16.485-95.318-37.913-22.723-17.061-44.027-43.274-56.077-68.997l-3.932-8.393 25.692-26.37c14.13-14.505 27.522-28.059 29.758-30.12l4.067-3.748 12.5 11.364c16.495 14.996 26.818 23.219 41.05 32.697 14.94 9.95 23.867 14.578 35.877 18.597 22.823 7.637 42.099 5.991 66.082-5.642 17.83-8.65 44.399-28.24 66.179-48.797l8.878-8.38 29.147 29.137c16.03 16.025 29.147 29.84 29.147 30.7s-1.6 4.912-3.554 9.005c-18.398 38.533-49.46 69.834-87.797 88.466-17.732 8.619-33.936 13.787-53.563 17.084-10.16 1.708-38.005 2.465-48.137 1.31zm6.3-123.69c-17.457-3.809-39.276-16.397-63.835-36.829-13.001-10.816-26.615-23.771-26.615-25.327 0-1.265 16.792-17.101 29.7-28.009 20.328-17.179 36.936-27.484 53.753-33.355 6.275-2.19 8.25-2.443 19.147-2.443 10.892 0 12.873.253 19.136 2.44 22.614 7.894 50.68 27.157 79.189 54.35 3.836 3.659 6.975 7.021 6.975 7.472 0 1.1-21.726 20.758-32.4 29.316-19.403 15.557-41.794 28.276-56.169 31.907-8.267 2.088-20.566 2.29-28.881.477zm37.472-20.429c14.201-7.184 18.451-9.747 19.779-11.925 1.556-2.552 1.692-4.934 1.692-29.774 0-24.91-.132-27.216-1.705-29.795-1.34-2.2-5.607-4.783-20.022-12.124-18.175-9.256-18.368-9.329-24.812-9.355-6.44-.026-6.632.044-22.95 8.376-10.12 5.168-17.582 9.58-19.38 11.462l-2.925 3.06.003 27.237c.003 25.164.134 27.452 1.712 30.04 1.301 2.134 5.186 4.602 16.288 10.35 20.15 10.433 22.925 11.538 29.04 11.571 4.82.026 6.487-.627 23.28-9.123m-40.872-12.388c-7.315-3.808-13.914-7.538-14.665-8.29-1.181-1.181-1.28-4.249-.736-22.667l.63-21.3 13.888-7.202c8.096-4.199 14.986-7.202 16.52-7.202 2.953 0 28.618 12.505 31.424 15.311 1.68 1.682 1.788 3.033 1.788 22.477v20.69l-3.46 2.183c-1.902 1.202-8.376 4.65-14.386 7.662-8.03 4.025-11.825 5.448-14.315 5.37-2.329-.075-7.546-2.273-16.688-7.032M29.5 263.432c-1.475-3.866-4.192-15.727-5.967-26.05-2.405-13.986-2.19-43.337.424-58.05 2.41-13.562 4.216-20.627 5.613-21.959.772-.736 7.977 6.088 27.938 26.459 14.794 15.098 26.902 27.655 26.906 27.906s-12.122 12.581-26.948 27.4L30.51 266.082zm333.59-24.851c-14.17-14.479-25.763-26.679-25.763-27.11 0-1.574 51.776-53.76 53.1-53.521 1.493.27 3.338 6.773 5.975 21.06 2.353 12.751 2.343 48.232-.017 61.072-2.37 12.886-5.358 24.1-6.527 24.49-.553.184-12.599-11.512-26.768-25.991M64.75 170.903c-26.893-26.902-29.95-30.252-29.354-32.175 1.335-4.308 8.33-18.529 12.565-25.546 28.808-47.732 75.832-79.809 131.42-89.647 15.231-2.696 45.201-2.944 59.361-.492 41.827 7.244 76.236 24.833 104.91 53.625 15.582 15.647 27.713 32.758 36.971 52.151 6.183 12.95 8.732 8.549-24.429 42.185-15.984 16.213-29.386 29.61-29.782 29.768-.396.16-5.695-4.244-11.775-9.786-32.548-29.67-61.86-48.734-85.116-55.361-7.771-2.215-9.327-2.357-22.05-2.009-12.468.34-14.427.627-21.965 3.212-23.775 8.152-49.675 26.108-80.457 55.78-4.75 4.579-9.006 8.325-9.457 8.325s-14.329-13.513-30.839-30.029z'/></svg>",
+ "webassembly": "<svg viewBox='0 0 32 32'><path fill='#7c4dff' d='M22 18h4v4h-4z'/><path fill='#7c4dff' d='M20 2a4 4 0 0 1-8 0H2v28h28V2Zm-2 24h-2v2h-4v-2h-2v2H6v-2H4V16h2v10h4V16h2v10h4V16h2Zm10 2h-2v-4h-4v4h-2V18h2v-2h4v2h2Z'/></svg>",
+ "webhint": "<svg viewBox='0 0 120 120'><path fill='#1e88e5' d='M115.45 23.033S97.961 33.27 97.534 33.412c-.427.284-.852.57-1.137.854-1.422 1.421-1.848 3.41-1.422 5.26.285.852.711 1.849 1.422 2.56.711.71 1.564 1.137 2.559 1.422 1.848.426 3.84 0 5.262-1.422q.638-.64.851-1.28l.143-.427 2.56-4.692zm-39.102 9.242c-27.441 0-31.99 13.08-31.99 29.29 0 3.838.569 7.962-1.99 11.942-3.84 5.972-8.957 5.828-10.236 5.828-1.706 0-7.962-.993-8.246-2.841h.994c6.682 0 11.658-5.404 11.658-12.655v-2.56h-5.686c-4.123 0-7.82 1.849-10.238 5.12-2.417-3.271-6.113-5.12-10.236-5.12h-5.83v2.56c0 7.11 5.688 12.795 12.797 12.795h1.848c0 4.124 5.687 20.332 47.63 20.332 16.352 0 40.665-2.843 40.665-33.697 0-5.829-1.848-11.23-4.691-15.78-.996.284-1.992.568-3.13.568a8.92 8.92 0 0 1-8.956-8.957q0-1.493.425-2.986c-4.265-2.702-8.53-3.838-14.787-3.838z'/></svg>",
+ "webpack": "<svg viewBox='0 0 24 24'><path fill='#fafafa' fill-opacity='.785' d='m19.376 15.988-7.708 4.45-7.709-4.45v-8.9l7.709-4.451 7.708 4.45z'/><path fill='#90caf9' d='M12.286 1.98c-.21 0-.41.059-.57.179l-7.9 4.44c-.32.17-.53.5-.53.88v9c0 .38.21.711.53.881l7.9 4.44c.16.12.36.18.57.18s.41-.06.57-.18l7.9-4.44c.32-.17.53-.5.53-.88v-9c0-.38-.21-.712-.53-.882l-7.9-4.44a.95.95 0 0 0-.57-.179zm0 2.15 7 3.94v2.103h-.016v5.177h.016v.54l-7 3.939-7-3.94V8.07zm0 2.08-4.9 2.83 4.9 2.83 4.9-2.83zm-5 5.08v3.58l4 2.309v-3.58l-4-2.31zm10 0-4 2.308v3.58l4-2.308z'/><path fill='#0277bd' d='m12.286 6.21-4.9 2.83 4.9 2.83 4.9-2.83zm-5 5.08v3.58l4 2.309v-3.58l-4-2.31zm10 0-4 2.308v3.58l4-2.308z'/></svg>",
+ "wepy": "<svg viewBox='0 0 32 32'><path fill='#4caf50' d='M16 2A14 14 0 0 0 2 16v12a2 2 0 0 0 2 2h12a14 14 0 0 0 0-28m0 24a10 10 0 1 1 10-10 10.01 10.01 0 0 1-10 10'/></svg>",
+ "werf": "<svg viewBox='0 0 100 111'><path fill='#1e88e5' d='m74.849 59.509 3.307 9.697 5.768-9.697zm-.601-20.967v17.932h9.788zm-3.095 10.125V34.182h-4.938zm-10.77-31.582 4.795 14.06h5.032l-8.166-14.959-1.236.668-.425.228zm-6.74 78.095h7.34l-2.77-5.008-.917.976a71.108 69.794 0 0 1-3.653 4.031zm-3.594-4.567.125-29.212a48.51 47.615 0 0 0-3.263 2.064c.125 3.608.715 15.891 3.137 27.147zm-8.374-23.137c.112 2.31.811 10.604 5.165 19.634-1.57-8.843-2.14-17.601-2.34-21.906a62.074 60.926 0 0 0-2.825 2.272m-2.68 27.704h7.64l-.584-.672a69.587 68.302 0 0 1-4.088-4.69l-2.967 5.36zm-2.442-22.688c.275 1.63 2.424 7.773 7.763 15.086-3.516-7.485-4.631-14.336-4.98-17.987a46.725 45.861 0 0 0-2.784 2.901zm3.86-15.062v6.948a46.951 46.083 0 0 1 3.884-2.96c3.425-2.488 4.818-3 5.698-3.055h-.006v-.933zm-24.358 2.08 6.167 10.364 4.244-10.364zm11.154-16.456L16.532 56.473h10.677zm3.097-3.889v10.974l7.509-18.34-1.004-.812-6.507 8.178zM80.198 71.8h6.218l1.548 1.52v23.377l-1.548 1.522H13.563L12.016 96.7V73.32l1.547-1.52h6.22l-7.76-13.04.115-1.701 23.21-29.167 2.11-.229.038-.052 10.428 7.677-1.857 2.431-5.638-4.153L24.199 73.2l12.145 20.407 3.595-6.496c-6.09-8.683-6.534-14.664-6.556-15l.363-1.07a62.861 61.699 0 0 1 3.57-3.827v-11.3l1.55-1.519h22.247l1.548 1.52V66.86a62.175 61.026 0 0 1 3.932 4.182l.361 1.067c-.019.343-.473 6.45-6.756 15.288l3.438 6.21 12.444-20.91-18.45-54.12-5.06 2.74-1.493-2.66 10.841-5.872 2.113.614 23.954 43.878'/></svg>",
+ "windicss": "<svg fill='none' viewBox='0 0 32 32'><path stroke='#42a5f5' stroke-miterlimit='3.339' stroke-width='4' d='M22 12a4 4 0 1 1 4 4H2m14 10a4 4 0 1 0 4-4H10M8 6a4 4 0 1 1 4 4H2'/><path fill='#42a5f5' d='M2 20h4v4H2z'/></svg>",
+ "wolframlanguage": "<svg viewBox='0 0 24 24'><g transform='translate(-.009 -.001)scale(.12121)'><circle cx='99.197' cy='98.946' r='83.28' fill='#212121'/><path fill='#e53935' d='M182.53 98.828a83.4 83.4 0 0 1-39.14 70.722.06.06 0 0 1-.037.019l-28.62-35.664 23.71 2.611s11.385 1.178 13.979 0c2.373-.938 15.174-18.963 15.174-18.963s-36.75-23.23-49.312-36.032c1.435-21.575-1.655-50.269-1.655-50.03-9.252 9.234-10.43 10.668-19.681 19.202-4.028-13.04-5.923-17.546-9.95-30.587-12.104 9.95-21.337 26.799-27.977 46.48a79 79 0 0 0-4.23 5.095 110 110 0 0 0-2.668 3.66 115 115 0 0 0-5.131 8 173 173 0 0 0-3.403 6.052c-7.707 14.476-14.034 31.067-19.515 46.002a1 1 0 0 1-.092-.184C8.993 104.299 14.48 67.36 37.804 42.138c23.325-25.223 59.722-33.574 91.71-21.045 31.988 12.53 53.029 43.382 53.017 77.736z'/><path fill='#fafafa' d='M101.45 69.178s-1.416-8.295-2.373-11.367c6.401-6.18 7.357-7.118 13.52-13.041.477 11.845.238 18.007-.479 32.482-3.55-3.568-10.668-8.075-10.668-8.075zm-27.737 40.778s-6.64-4.028-11.624-4.727c1.435-3.33 5.224-7.597 6.18-8.774-1.913.7-15.652 6.861-17.087 12.084a75 75 0 0 1 11.385 3.79 36 36 0 0 0-8.773 20.158s21.814-3.329 38.184-1.195c.283.168.608.25.938.239l8.534.239 27.11 45.137.222.35c-.037.018-.056.036-.074.036-51.133 18.485-88.085-15.542-95.975-27.442q.05-.153.074-.312c7.1-30.018 15.855-65.94 29.999-76.552 7.357-12.82 9.49-31.783 22.752-41.734 3.329 9.95 8.553 30.588 12.103 40.538 15.653 15.653 39.36 35.094 55.234 43.15 1.655.956 3.789 7.596 3.789 7.596l-6.401 8.056-68.275-6.879a55 55 0 0 0-4.58-.184 87 87 0 0 0-14.144 1.361c3.31-8.295 10.429-14.935 10.429-14.935m22.053-8.774c3.789-.46 7.817.957 12.323 3.569 4.267-1.196 4.745-1.435 9.013-2.612-5.463-4.028-11.385-8.295-19.442-7.118a47 47 0 0 0-1.895 6.161z'/></g></svg>",
+ "word": "<svg viewBox='0 0 24 24'><path fill='#01579b' d='M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2m7 1.5V9h5.5zM7 13l1.5 7h2l1.5-3 1.5 3h2l1.5-7h1v-2h-4v2h1l-.9 4.2L13 15h-2l-1.1 2.2L9 13h1v-2H6v2z'/></svg>",
+ "wrangler": "<svg viewBox='0 0 32 32'><path fill='#f57f17' d='M22 20H10.5a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5H22v-6.449A5.95 5.95 0 0 0 18 10h-2a5.98 5.98 0 0 0-4.463 2H10a4 4 0 0 0-4 4 4 4 0 0 0-4 4v1.5a.5.5 0 0 0 .5.5H22Z'/><path fill='#ffab40' d='M24 14v4h1.5a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5H24v2h5.5a.5.5 0 0 0 .5-.5V20a6 6 0 0 0-6-6'/></svg>",
+ "wxt": "<svg viewBox='0 0 16 16'><path fill='#00c853' d='M14 8.9c.117 1.136-.854 2.043-2 2.1v2c0 .663-.337 1-1 1H8v-1c0-1.52-2-1.34-2 0v1H3c-.663 0-1-.337-1-1v-3h1s1-.1 1-1-1-1-1-1H2V5c0-.663.337-1 1-1h2c.025-1.06.885-1.995 2-2 1.123-.005 1.996.93 2 2h2c.663 0 1 .337 1 1v2c1.082.067 2.117.798 2 2m-3 1h1c.497 0 1-.503 1-1s-.503-1-1-1h-1V5H8V4c0-.497-.503-1-1-1s-1 .503-1 1v1H3v2c1.148.341 1.98.744 2 2 .02 1.226-.707 1.666-2 2v2h2c.156-1.452 1.055-1.948 2-2 1-.056 2.098.695 2 2h2z'/></svg>",
+ "xaml": "<svg viewBox='0 0 32 32'><path fill='#42a5f5' d='m32 16-5.387 9.333L24.307 24l4.613-8-4.613-8 2.306-1.333z'/><path fill='#42a5f5' d='m25.24 16-4.627 8h-9.226L6.76 16l4.627-8h9.226z'/><path fill='#42a5f5' d='m0 16 5.387-9.333L7.693 8 3.08 16l4.613 8-2.306 1.333z'/></svg>",
+ "xmake": "<svg viewBox='0 0 16 16'><circle cx='8' cy='8' r='7' fill='#e0f2f1'/><path fill='#e0f2f1' d='M11.759 2.944a6.3 6.3 0 0 0-8.932 1.462l3.281 2.023z'/><path fill='#8bc34a' d='M1.796 9.088 6.107 6.43l-3.28-2.025A6.27 6.27 0 0 0 1.7 8a6.4 6.4 0 0 0 .096 1.088'/><path fill='#4db6ac' d='M13.536 11.01a6.3 6.3 0 0 0-1.777-8.066l-5.65 3.485z'/><path fill='#009688' d='M1.796 9.088a6.3 6.3 0 0 0 11.74 1.922L6.108 6.428z'/></svg>",
+ "xml": "<svg viewBox='0 0 24 24'><path fill='#8bc34a' d='M13 9h5.5L13 3.5zM6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4c0-1.11.89-2 2-2m.12 13.5 3.74 3.74 1.42-1.41-2.33-2.33 2.33-2.33-1.42-1.41zm11.16 0-3.74-3.74-1.42 1.41 2.33 2.33-2.33 2.33 1.42 1.41z'/></svg>",
+ "yaml": "<svg viewBox='0 0 24 24'><path fill='#ff5252' d='M13 9h5.5L13 3.5zM6 2h8l6 6v12c0 1.1-.9 2-2 2H6c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2m12 16v-2H9v2zm-4-4v-2H6v2z'/></svg>",
+ "yang": "<svg viewBox='0 0 24 24'><path fill='#42a5f5' d='M12 2a10 10 0 0 1 10 10 10 10 0 0 1-10 10A10 10 0 0 1 2 12 10 10 0 0 1 12 2m0 2a8 8 0 0 0-8 8 8 8 0 0 0 8 8 4 4 0 0 1-4-4 4 4 0 0 1 4-4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 2.5A1.5 1.5 0 0 1 13.5 8 1.5 1.5 0 0 1 12 9.5 1.5 1.5 0 0 1 10.5 8 1.5 1.5 0 0 1 12 6.5m0 8a1.5 1.5 0 0 0-1.5 1.5 1.5 1.5 0 0 0 1.5 1.5 1.5 1.5 0 0 0 1.5-1.5 1.5 1.5 0 0 0-1.5-1.5'/></svg>",
+ "yarn": "<svg viewBox='0 0 32 32'><path fill='#0288d1' d='M27.575 23.967a9.9 9.9 0 0 0-3.751 1.726 22.6 22.6 0 0 1-5.537 2.504 1.55 1.55 0 0 1-.931.52 59 59 0 0 1-6.11.548c-1.102.008-1.777-.282-1.965-.735a1.49 1.49 0 0 1 .82-1.965 3.6 3.6 0 0 1-.486-.359c-.163-.162-.334-.487-.385-.367-.213.52-.324 1.794-.897 2.366-.786.795-2.273.53-3.153.069-.965-.513.069-1.718.069-1.718a.69.69 0 0 1-.94-.324 4.6 4.6 0 0 1-.632-2.794 5.2 5.2 0 0 1 1.674-2.76 8.84 8.84 0 0 1 .624-4.17 9.9 9.9 0 0 1 3-3.469S7.136 11.015 7.82 9.177c.444-1.196.623-1.187.769-1.239a3.44 3.44 0 0 0 1.375-.811 4.99 4.99 0 0 1 4.178-1.607s1.094-3.357 2.12-2.7a17.4 17.4 0 0 1 1.452 2.735s1.213-.71 1.35-.445a10.74 10.74 0 0 1 .495 5.81 13.3 13.3 0 0 1-2.46 5.127c-.129.214 1.47.889 2.477 3.683.932 2.554.103 4.699.248 4.938.026.043.034.06.034.06s1.068.085 3.213-1.24a8.05 8.05 0 0 1 4.05-1.52 1.026 1.026 0 0 1 .453 2Z'/></svg>",
+ "zeabur": "<svg viewBox='0 0 32 32'><path fill='#cfd8dc' d='m14 20 4 4-4 4H2v-8h8l10-8-6-4 6-4h10v8Z'/><path fill='#651fff' d='M20 4H2v8h18Z'/><path fill='#ff3d00' d='M30 20H14v8h16Z'/></svg>",
+ "zeabur_light": "<svg viewBox='0 0 32 32'><path fill='#263238' d='m14 20 4 4-4 4H2v-8h8l10-8-6-4 6-4h10v8Z'/><path fill='#651fff' d='M20 4H2v8h18Z'/><path fill='#ff3d00' d='M30 20H14v8h16Z'/></svg>",
+ "zig": "<svg viewBox='0 0 32 32'><path fill='#f9a825' d='M2 8h6v4H2zm8 0h12v4H10zm0 12h12v4H10zm14 0h2v4h-2zM8 20l-3 4H2V12h4v8zm14-8h-6l-6 8h6z'/><path fill='#f9a825' d='M16 20h-6l-6 8m12-16h6l6-8m2 4v16h-4V12h-2l3-4z'/></svg>",
+ "zip": "<svg viewBox='0 0 24 24'><path fill='#afb42b' d='M14 17h-2v-2h-2v-2h2v2h2m0-6h-2v2h2v2h-2v-2h-2V9h2V7h-2V5h2v2h2m5-4H5c-1.11 0-2 .89-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2'/></svg>"
+} \ No newline at end of file
diff --git a/options/gitignore/Flutter b/options/gitignore/Flutter
new file mode 100644
index 0000000000..39b8814aec
--- /dev/null
+++ b/options/gitignore/Flutter
@@ -0,0 +1,119 @@
+# Miscellaneous
+*.class
+*.lock
+*.log
+*.pyc
+*.swp
+.buildlog/
+.history
+
+
+
+# Flutter repo-specific
+/bin/cache/
+/bin/internal/bootstrap.bat
+/bin/internal/bootstrap.sh
+/bin/mingit/
+/dev/benchmarks/mega_gallery/
+/dev/bots/.recipe_deps
+/dev/bots/android_tools/
+/dev/devicelab/ABresults*.json
+/dev/docs/doc/
+/dev/docs/flutter.docs.zip
+/dev/docs/lib/
+/dev/docs/pubspec.yaml
+/dev/integration_tests/**/xcuserdata
+/dev/integration_tests/**/Pods
+/packages/flutter/coverage/
+version
+analysis_benchmark.json
+
+# packages file containing multi-root paths
+.packages.generated
+
+# Flutter/Dart/Pub related
+**/doc/api/
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+**/generated_plugin_registrant.dart
+.packages
+.pub-preload-cache/
+.pub/
+build/
+flutter_*.png
+linked_*.ds
+unlinked.ds
+unlinked_spec.ds
+
+# Android related
+**/android/**/gradle-wrapper.jar
+.gradle/
+**/android/captures/
+**/android/gradlew
+**/android/gradlew.bat
+**/android/local.properties
+**/android/**/GeneratedPluginRegistrant.java
+**/android/key.properties
+*.jks
+
+# iOS/XCode related
+**/ios/**/*.mode1v3
+**/ios/**/*.mode2v3
+**/ios/**/*.moved-aside
+**/ios/**/*.pbxuser
+**/ios/**/*.perspectivev3
+**/ios/**/*sync/
+**/ios/**/.sconsign.dblite
+**/ios/**/.tags*
+**/ios/**/.vagrant/
+**/ios/**/DerivedData/
+**/ios/**/Icon?
+**/ios/**/Pods/
+**/ios/**/.symlinks/
+**/ios/**/profile
+**/ios/**/xcuserdata
+**/ios/.generated/
+**/ios/Flutter/.last_build_id
+**/ios/Flutter/App.framework
+**/ios/Flutter/Flutter.framework
+**/ios/Flutter/Flutter.podspec
+**/ios/Flutter/Generated.xcconfig
+**/ios/Flutter/ephemeral
+**/ios/Flutter/app.flx
+**/ios/Flutter/app.zip
+**/ios/Flutter/flutter_assets/
+**/ios/Flutter/flutter_export_environment.sh
+**/ios/ServiceDefinitions.json
+**/ios/Runner/GeneratedPluginRegistrant.*
+
+# macOS
+**/Flutter/ephemeral/
+**/Pods/
+**/macos/Flutter/GeneratedPluginRegistrant.swift
+**/macos/Flutter/ephemeral
+**/xcuserdata/
+
+# Windows
+**/windows/flutter/generated_plugin_registrant.cc
+**/windows/flutter/generated_plugin_registrant.h
+**/windows/flutter/generated_plugins.cmake
+
+# Linux
+**/linux/flutter/generated_plugin_registrant.cc
+**/linux/flutter/generated_plugin_registrant.h
+**/linux/flutter/generated_plugins.cmake
+
+# Coverage
+coverage/
+
+# Symbols
+app.*.symbols
+
+# Exceptions to above rules.
+!**/ios/**/default.mode1v3
+!**/ios/**/default.mode2v3
+!**/ios/**/default.pbxuser
+!**/ios/**/default.perspectivev3
+!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
+!/dev/ci/**/Gemfile.lock \ No newline at end of file
diff --git a/options/gitignore/Nix b/options/gitignore/Nix
index 1fd04ef1f6..912e6700f4 100644
--- a/options/gitignore/Nix
+++ b/options/gitignore/Nix
@@ -1,3 +1,6 @@
# Ignore build outputs from performing a nix-build or `nix build` command
result
result-*
+
+# Ignore automatically generated direnv output
+.direnv
diff --git a/options/gitignore/Node b/options/gitignore/Node
index c6bba59138..1170717c14 100644
--- a/options/gitignore/Node
+++ b/options/gitignore/Node
@@ -104,6 +104,12 @@ dist
.temp
.cache
+# vitepress build output
+**/.vitepress/dist
+
+# vitepress cache directory
+**/.vitepress/cache
+
# Docusaurus cache and generated files
.docusaurus
diff --git a/options/gitignore/NotesAndCoreConfiguration b/options/gitignore/NotesAndCoreConfiguration
new file mode 100644
index 0000000000..4eff01dae1
--- /dev/null
+++ b/options/gitignore/NotesAndCoreConfiguration
@@ -0,0 +1,16 @@
+# Excludes Obsidian workspace cache and plugins. All notes and core obsidian
+# configuration files are tracked by Git.
+
+# The current application UI state (DOM layout, recently-opened files, etc.) is
+# stored in these files (separate for desktop and mobile) so you can resume
+# your session seamlessly after a restart. If you want to track UI state, use
+# the Workspaces core plugin instead of relying on these files.
+.obsidian/workspace.json
+.obsidian/workspace-mobile.json
+
+# Obsidian plugins are stored under .obsidian/plugins/$plugin_name. They
+# contain metadata (manifest.json), application code (main.js), stylesheets
+# (styles.css), and user-configuration data (data.json).
+# We want to exclude all plugin-related files, so we can exclude everything
+# under this directory.
+.obsidian/plugins/**/*
diff --git a/options/gitignore/NotesAndExtendedConfiguration b/options/gitignore/NotesAndExtendedConfiguration
new file mode 100644
index 0000000000..3e0804f299
--- /dev/null
+++ b/options/gitignore/NotesAndExtendedConfiguration
@@ -0,0 +1,38 @@
+# Excludes Obsidian workspace cache and plugin code, but retains plugin
+# configuration. All notes and user-controlled configuration files are tracked
+# by Git.
+#
+# !!! WARNING !!!
+#
+# Community plugins may store sensitive secrets in their data.json files. By
+# including these files, those secrets may be tracked in your Git repository.
+#
+# To ignore configurations for specific plugins, add a line like this after the
+# contents of this file (order is important):
+# .obsidian/plugins/{{plugin_name}}/data.json
+#
+# Alternatively, ensure that you are treating your entire Git repository as
+# sensitive data, since it may contain secrets, or may have contained them in
+# past commits. Understand your threat profile, and make the decision
+# appropriate for yourself. If in doubt, err on the side of not including
+# plugin configuration. Use one of the alternative gitignore files instead:
+# * NotesOnly.gitignore
+# * NotesAndCoreConfiguration.gitignore
+
+# The current application UI state (DOM layout, recently-opened files, etc.) is
+# stored in these files (separate for desktop and mobile) so you can resume
+# your session seamlessly after a restart. If you want to track UI state, use
+# the Workspaces core plugin instead of relying on these files.
+.obsidian/workspace.json
+.obsidian/workspace-mobile.json
+
+# Obsidian plugins are stored under .obsidian/plugins/$plugin_name. They
+# contain metadata (manifest.json), application code (main.js), stylesheets
+# (styles.css), and user-configuration data (data.json).
+# We only want to track data.json, so we:
+# 1. exclude everything under the plugins directory recursively,
+# 2. unignore the plugin directories themselves, which then allows us to
+# 3. unignore the data.json files
+.obsidian/plugins/**/*
+!.obsidian/plugins/*/
+!.obsidian/plugins/*/data.json
diff --git a/options/gitignore/NotesOnly b/options/gitignore/NotesOnly
new file mode 100644
index 0000000000..2b3b76ee0e
--- /dev/null
+++ b/options/gitignore/NotesOnly
@@ -0,0 +1,4 @@
+# Excludes all Obsidian-related configuration. All notes are tracked by Git.
+
+# All Obsidian configuration and runtime state is stored here
+.obsidian/**/*
diff --git a/options/gitignore/Processing b/options/gitignore/Processing
index 942ebbccb5..2d243c96bd 100644
--- a/options/gitignore/Processing
+++ b/options/gitignore/Processing
@@ -2,6 +2,7 @@
applet
application.linux-arm64
application.linux-armv6hf
+application.linux-riscv64
application.linux32
application.linux64
application.windows32
diff --git a/options/gitignore/Python b/options/gitignore/Python
index 15201acc11..0a197900e2 100644
--- a/options/gitignore/Python
+++ b/options/gitignore/Python
@@ -167,5 +167,8 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
+# Ruff stuff:
+.ruff_cache/
+
# PyPI configuration file
.pypirc
diff --git a/options/gitignore/Rust b/options/gitignore/Rust
index d01bd1a990..0104787a73 100644
--- a/options/gitignore/Rust
+++ b/options/gitignore/Rust
@@ -3,10 +3,6 @@
debug/
target/
-# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
-# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
-Cargo.lock
-
# These are backup files generated by rustfmt
**/*.rs.bk
diff --git a/options/label/Advanced.yaml b/options/label/Advanced.yaml
index b1ecdd6d93..860645d5d5 100644
--- a/options/label/Advanced.yaml
+++ b/options/label/Advanced.yaml
@@ -22,49 +22,60 @@ labels:
description: Breaking change that won't be backward compatible
- name: "Reviewed/Duplicate"
exclusive: true
+ exclusive_order: 2
color: 616161
description: This issue or pull request already exists
- name: "Reviewed/Invalid"
exclusive: true
+ exclusive_order: 3
color: 546e7a
description: Invalid issue
- name: "Reviewed/Confirmed"
exclusive: true
+ exclusive_order: 1
color: 795548
description: Issue has been confirmed
- name: "Reviewed/Won't Fix"
exclusive: true
+ exclusive_order: 3
color: eeeeee
description: This issue won't be fixed
- name: "Status/Need More Info"
exclusive: true
+ exclusive_order: 2
color: 424242
description: Feedback is required to reproduce issue or to continue work
- name: "Status/Blocked"
exclusive: true
+ exclusive_order: 1
color: 880e4f
description: Something is blocking this issue or pull request
- name: "Status/Abandoned"
exclusive: true
+ exclusive_order: 3
color: "222222"
description: Somebody has started to work on this but abandoned work
- name: "Priority/Critical"
exclusive: true
+ exclusive_order: 1
color: b71c1c
description: The priority is critical
priority: critical
- name: "Priority/High"
exclusive: true
+ exclusive_order: 2
color: d32f2f
description: The priority is high
priority: high
- name: "Priority/Medium"
exclusive: true
+ exclusive_order: 3
color: e64a19
description: The priority is medium
priority: medium
- name: "Priority/Low"
exclusive: true
+ exclusive_order: 4
color: 4caf50
description: The priority is low
priority: low
diff --git a/options/license/389-exception b/options/license/389-exception
deleted file mode 100644
index fe5bff900a..0000000000
--- a/options/license/389-exception
+++ /dev/null
@@ -1,7 +0,0 @@
-This Program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License.
-
-This Program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with this Program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
-In addition, as a special exception, Red Hat, Inc. gives You the additional right to link the code of this Program with code not covered under the GNU General Public License ("Non-GPL Code") and to distribute linked combinations including the two, subject to the limitations in this paragraph. Non-GPL Code permitted under this exception must only link to the code of this Program through those well defined interfaces identified in the file named EXCEPTION found in the source code files (the "Approved Interfaces"). The files of Non-GPL Code may instantiate templates or use macros or inline functions from the Approved Interfaces without causing the resulting work to be covered by the GNU General Public License. Only Red Hat, Inc. may make changes or additions to the list of Approved Interfaces. You must obey the GNU General Public License in all respects for all of the Program code and other code used in conjunction with the Program except the Non-GPL Code covered by this exception. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to provide this exception without modification, you must delete this exception statement from your version and license this file solely under the GPL without exception.
diff --git a/options/license/3D-Slicer-1.0 b/options/license/3D-Slicer-1.0
deleted file mode 100644
index 38bd5230c6..0000000000
--- a/options/license/3D-Slicer-1.0
+++ /dev/null
@@ -1,190 +0,0 @@
-3D Slicer Contribution and Software License Agreement ("Agreement")
-Version 1.0 (December 20, 2005)
-
-This Agreement covers contributions to and downloads from the 3D
-Slicer project ("Slicer") maintained by The Brigham and Women's
-Hospital, Inc. ("Brigham"). Part A of this Agreement applies to
-contributions of software and/or data to Slicer (including making
-revisions of or additions to code and/or data already in Slicer). Part
-B of this Agreement applies to downloads of software and/or data from
-Slicer. Part C of this Agreement applies to all transactions with
-Slicer. If you distribute Software (as defined below) downloaded from
-Slicer, all of the paragraphs of Part B of this Agreement must be
-included with and apply to such Software.
-
-Your contribution of software and/or data to Slicer (including prior
-to the date of the first publication of this Agreement, each a
-"Contribution") and/or downloading, copying, modifying, displaying,
-distributing or use of any software and/or data from Slicer
-(collectively, the "Software") constitutes acceptance of all of the
-terms and conditions of this Agreement. If you do not agree to such
-terms and conditions, you have no right to contribute your
-Contribution, or to download, copy, modify, display, distribute or use
-the Software.
-
-PART A. CONTRIBUTION AGREEMENT - License to Brigham with Right to
-Sublicense ("Contribution Agreement").
-
-1. As used in this Contribution Agreement, "you" means the individual
- contributing the Contribution to Slicer and the institution or
- entity which employs or is otherwise affiliated with such
- individual in connection with such Contribution.
-
-2. This Contribution Agreement applies to all Contributions made to
- Slicer, including without limitation Contributions made prior to
- the date of first publication of this Agreement. If at any time you
- make a Contribution to Slicer, you represent that (i) you are
- legally authorized and entitled to make such Contribution and to
- grant all licenses granted in this Contribution Agreement with
- respect to such Contribution; (ii) if your Contribution includes
- any patient data, all such data is de-identified in accordance with
- U.S. confidentiality and security laws and requirements, including
- but not limited to the Health Insurance Portability and
- Accountability Act (HIPAA) and its regulations, and your disclosure
- of such data for the purposes contemplated by this Agreement is
- properly authorized and in compliance with all applicable laws and
- regulations; and (iii) you have preserved in the Contribution all
- applicable attributions, copyright notices and licenses for any
- third party software or data included in the Contribution.
-
-3. Except for the licenses granted in this Agreement, you reserve all
- right, title and interest in your Contribution.
-
-4. You hereby grant to Brigham, with the right to sublicense, a
- perpetual, worldwide, non-exclusive, no charge, royalty-free,
- irrevocable license to use, reproduce, make derivative works of,
- display and distribute the Contribution. If your Contribution is
- protected by patent, you hereby grant to Brigham, with the right to
- sublicense, a perpetual, worldwide, non-exclusive, no-charge,
- royalty-free, irrevocable license under your interest in patent
- rights covering the Contribution, to make, have made, use, sell and
- otherwise transfer your Contribution, alone or in combination with
- any other code.
-
-5. You acknowledge and agree that Brigham may incorporate your
- Contribution into Slicer and may make Slicer available to members
- of the public on an open source basis under terms substantially in
- accordance with the Software License set forth in Part B of this
- Agreement. You further acknowledge and agree that Brigham shall
- have no liability arising in connection with claims resulting from
- your breach of any of the terms of this Agreement.
-
-6. YOU WARRANT THAT TO THE BEST OF YOUR KNOWLEDGE YOUR CONTRIBUTION
- DOES NOT CONTAIN ANY CODE THAT REQUIRES OR PRESCRIBES AN "OPEN
- SOURCE LICENSE" FOR DERIVATIVE WORKS (by way of non-limiting
- example, the GNU General Public License or other so-called
- "reciprocal" license that requires any derived work to be licensed
- under the GNU General Public License or other "open source
- license").
-
-PART B. DOWNLOADING AGREEMENT - License from Brigham with Right to
-Sublicense ("Software License").
-
-1. As used in this Software License, "you" means the individual
- downloading and/or using, reproducing, modifying, displaying and/or
- distributing the Software and the institution or entity which
- employs or is otherwise affiliated with such individual in
- connection therewith. The Brigham and Women's Hospital,
- Inc. ("Brigham") hereby grants you, with right to sublicense, with
- respect to Brigham's rights in the software, and data, if any,
- which is the subject of this Software License (collectively, the
- "Software"), a royalty-free, non-exclusive license to use,
- reproduce, make derivative works of, display and distribute the
- Software, provided that:
-
-(a) you accept and adhere to all of the terms and conditions of this
-Software License;
-
-(b) in connection with any copy of or sublicense of all or any portion
-of the Software, all of the terms and conditions in this Software
-License shall appear in and shall apply to such copy and such
-sublicense, including without limitation all source and executable
-forms and on any user documentation, prefaced with the following
-words: "All or portions of this licensed product (such portions are
-the "Software") have been obtained under license from The Brigham and
-Women's Hospital, Inc. and are subject to the following terms and
-conditions:"
-
-(c) you preserve and maintain all applicable attributions, copyright
-notices and licenses included in or applicable to the Software;
-
-(d) modified versions of the Software must be clearly identified and
-marked as such, and must not be misrepresented as being the original
-Software; and
-
-(e) you consider making, but are under no obligation to make, the
-source code of any of your modifications to the Software freely
-available to others on an open source basis.
-
-2. The license granted in this Software License includes without
- limitation the right to (i) incorporate the Software into
- proprietary programs (subject to any restrictions applicable to
- such programs), (ii) add your own copyright statement to your
- modifications of the Software, and (iii) provide additional or
- different license terms and conditions in your sublicenses of
- modifications of the Software; provided that in each case your use,
- reproduction or distribution of such modifications otherwise
- complies with the conditions stated in this Software License.
-
-3. This Software License does not grant any rights with respect to
- third party software, except those rights that Brigham has been
- authorized by a third party to grant to you, and accordingly you
- are solely responsible for (i) obtaining any permissions from third
- parties that you need to use, reproduce, make derivative works of,
- display and distribute the Software, and (ii) informing your
- sublicensees, including without limitation your end-users, of their
- obligations to secure any such required permissions.
-
-4. The Software has been designed for research purposes only and has
- not been reviewed or approved by the Food and Drug Administration
- or by any other agency. YOU ACKNOWLEDGE AND AGREE THAT CLINICAL
- APPLICATIONS ARE NEITHER RECOMMENDED NOR ADVISED. Any
- commercialization of the Software is at the sole risk of the party
- or parties engaged in such commercialization. You further agree to
- use, reproduce, make derivative works of, display and distribute
- the Software in compliance with all applicable governmental laws,
- regulations and orders, including without limitation those relating
- to export and import control.
-
-5. The Software is provided "AS IS" and neither Brigham nor any
- contributor to the software (each a "Contributor") shall have any
- obligation to provide maintenance, support, updates, enhancements
- or modifications thereto. BRIGHAM AND ALL CONTRIBUTORS SPECIFICALLY
- DISCLAIM ALL EXPRESS AND IMPLIED WARRANTIES OF ANY KIND INCLUDING,
- BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR
- A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
- BRIGHAM OR ANY CONTRIBUTOR BE LIABLE TO ANY PARTY FOR DIRECT,
- INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY ARISING IN ANY WAY
- RELATED TO THE SOFTWARE, EVEN IF BRIGHAM OR ANY CONTRIBUTOR HAS
- BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. TO THE MAXIMUM
- EXTENT NOT PROHIBITED BY LAW OR REGULATION, YOU FURTHER ASSUME ALL
- LIABILITY FOR YOUR USE, REPRODUCTION, MAKING OF DERIVATIVE WORKS,
- DISPLAY, LICENSE OR DISTRIBUTION OF THE SOFTWARE AND AGREE TO
- INDEMNIFY AND HOLD HARMLESS BRIGHAM AND ALL CONTRIBUTORS FROM AND
- AGAINST ANY AND ALL CLAIMS, SUITS, ACTIONS, DEMANDS AND JUDGMENTS
- ARISING THEREFROM.
-
-6. None of the names, logos or trademarks of Brigham or any of
- Brigham's affiliates or any of the Contributors, or any funding
- agency, may be used to endorse or promote products produced in
- whole or in part by operation of the Software or derived from or
- based on the Software without specific prior written permission
- from the applicable party.
-
-7. Any use, reproduction or distribution of the Software which is not
- in accordance with this Software License shall automatically revoke
- all rights granted to you under this Software License and render
- Paragraphs 1 and 2 of this Software License null and void.
-
-8. This Software License does not grant any rights in or to any
- intellectual property owned by Brigham or any Contributor except
- those rights expressly granted hereunder.
-
-PART C. MISCELLANEOUS
-
-This Agreement shall be governed by and construed in accordance with
-the laws of The Commonwealth of Massachusetts without regard to
-principles of conflicts of law. This Agreement shall supercede and
-replace any license terms that you may have agreed to previously with
-respect to Slicer.
diff --git a/options/license/AAL b/options/license/AAL
deleted file mode 100644
index 11ee9d9d89..0000000000
--- a/options/license/AAL
+++ /dev/null
@@ -1,23 +0,0 @@
-Attribution Assurance License
-
-Copyright (c) 2002 by AUTHOR PROFESSIONAL IDENTIFICATION * URL "PROMOTIONAL SLOGAN FOR AUTHOR'S PROFESSIONAL PRACTICE"
-
-All Rights Reserved
-
-ATTRIBUTION ASSURANCE LICENSE (adapted from the original BSD license)
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the conditions below are met. These conditions require a modest attribution to <AUTHOR> (the "Author"), who hopes that its promotional value may help justify the thousands of dollars in otherwise billable time invested in writing this and other freely available, open-source software.
-
-1. Redistributions of source code, in whole or part and with or without modification (the "Code"), must prominently display this GPG-signed text in verifiable form.
-
-2. Redistributions of the Code in binary form must be accompanied by this GPG-signed text in any documentation and, each time the resulting executable program or a program dependent thereon is launched, a prominent display (e.g., splash screen or banner text) of the Author's attribution information, which includes:
-
- (a) Name ("AUTHOR"),
- (b) Professional identification ("PROFESSIONAL IDENTIFICATION"), and
- (c) URL ("URL").
-
-3. Neither the name nor any trademark of the Author may be used to endorse or promote products derived from this software without specific prior written permission.
-
-4. Users are entirely responsible, to the exclusion of the Author and any other persons, for compliance with (1) regulations set by owners or administrators of employed equipment, (2) licensing terms of any other software, and (3) local regulations regarding use, including those regarding import, export, and use of encryption software.
-
-THIS FREE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR ANY CONTRIBUTOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, EFFECTS OF UNAUTHORIZED OR MALICIOUS NETWORK ACCESS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/ADSL b/options/license/ADSL
deleted file mode 100644
index dc509208cc..0000000000
--- a/options/license/ADSL
+++ /dev/null
@@ -1 +0,0 @@
-This software code is made available "AS IS" without warranties of any kind. You may copy, display, modify and redistribute the software code either by itself or as incorporated into your code; provided that you do not remove any proprietary notices. Your use of this software code is at your own risk and you waive any claim against Amazon Digital Services, Inc. or its affiliates with respect to your use of this software code. (c) 2006 Amazon Digital Services, Inc. or its affiliates.
diff --git a/options/license/AFL-1.1 b/options/license/AFL-1.1
deleted file mode 100644
index 446c0aca44..0000000000
--- a/options/license/AFL-1.1
+++ /dev/null
@@ -1,27 +0,0 @@
-Academic Free License
-Version 1.1
-
-The Academic Free License applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
-
- "Licensed under the Academic Free License version 1.1."
-
-Grant of License. Licensor hereby grants to any person obtaining a copy of the Original Work ("You") a world-wide, royalty-free, non-exclusive, perpetual, non-sublicenseable license
-
-(1) to use, copy, modify, merge, publish, perform, distribute and/or sell copies of the Original Work and derivative works thereof, and
-
-(2) under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, to make, use, sell and offer for sale the Original Work and derivative works thereof, subject to the following conditions.
-
- Right of Attribution. Redistributions of the Original Work must reproduce all copyright notices in the Original Work as furnished by the Licensor, both in the Original Work itself and in any documentation and/or other materials provided with the distribution of the Original Work in executable form.
-
- Exclusions from License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior written permission of the Licensor.
-
-WARRANTY AND DISCLAIMERS. LICENSOR WARRANTS THAT THE COPYRIGHT IN AND TO THE ORIGINAL WORK IS OWNED BY THE LICENSOR OR THAT THE ORIGINAL WORK IS DISTRIBUTED BY LICENSOR UNDER A VALID CURRENT LICENSE FROM THE COPYRIGHT OWNER. EXCEPT AS EXPRESSLY STATED IN THE IMMEDIATELY PRECEEDING SENTENCE, THE ORIGINAL WORK IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, THE WARRANTY OF NON-INFRINGEMENT AND WARRANTIES THAT THE ORIGINAL WORK IS MERCHANTABLE OR FIT FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO LICENSE TO ORIGINAL WORK IS GRANTED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE LICENSOR BE LIABLE TO ANY PERSON FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER ARISING AS A RESULT OF THIS LICENSE OR THE USE OF THE ORIGINAL WORK INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PERSON SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-License to Source Code. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to access and modify the Original Work. Licensor hereby agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work, and by publishing the address of that information repository in a notice immediately following the copyright notice that applies to the Original Work.
-
-Mutual Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License if You file a lawsuit in any court alleging that any OSI Certified open source software that is licensed under any license containing this "Mutual Termination for Patent Action" clause infringes any patent claims that are essential to use that software.
-
-This license is Copyright (C) 2002 Lawrence E. Rosen. All rights reserved.
-Permission is hereby granted to copy and distribute this license without modification. This license may not be modified without the express written permission of its copyright owner.
diff --git a/options/license/AFL-1.2 b/options/license/AFL-1.2
deleted file mode 100644
index b8009f87c6..0000000000
--- a/options/license/AFL-1.2
+++ /dev/null
@@ -1,28 +0,0 @@
-Academic Free License
-Version 1.2
-
-This Academic Free License applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the
-following notice immediately following the copyright notice for the Original Work:
-
- Licensed under the Academic Free License version 1.2
-
-Grant of License. Licensor hereby grants to any person obtaining a copy of the Original Work ("You") a world-wide, royalty-free, non-exclusive, perpetual, non-sublicenseable license (1) to use, copy, modify, merge, publish, perform, distribute and/or sell copies of the Original Work and derivative works thereof, and (2) under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, to make, use, sell and offer for sale the Original Work and derivative works thereof, subject to the
-following conditions.
-
-Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
-
-Exclusions from License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior written permission of the Licensor.
-
-Warranty and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work is owned by the Licensor or that the Original Work is distributed by Licensor under a valid current license from the copyright owner. Except as expressly stated in the immediately proceeding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to Original Work is granted hereunder except under this disclaimer.
-
-Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to any person for any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to liability for death or personal injury resulting from Licensor's negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.
-
-License to Source Code. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available
-documentation describing how to modify the Original Work. Licensor hereby agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work, and by publishing the address of that information repository in a notice immediately following the copyright notice that applies to the Original Work.
-
-Mutual Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License if You file a lawsuit in any court alleging that any OSI Certified open source software that is licensed under any license containing this "Mutual Termination for Patent Action" clause infringes any patent claims that are essential to use that software.
-
-Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
-
-This license is Copyright (C) 2002 Lawrence E. Rosen. All rights reserved.
-Permission is hereby granted to copy and distribute this license without modification. This license may not be modified without the express written permission of its copyright owner.
diff --git a/options/license/AFL-2.0 b/options/license/AFL-2.0
deleted file mode 100644
index f3bd601b34..0000000000
--- a/options/license/AFL-2.0
+++ /dev/null
@@ -1,45 +0,0 @@
-The Academic Free License
-v. 2.0
-
-This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
-
- Licensed under the Academic Free License version 2.0
-
-1) Grant of Copyright License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license to do the following:
-
- a) to reproduce the Original Work in copies;
- b) to prepare derivative works ("Derivative Works") based upon the Original Work;
- c) to distribute copies of the Original Work and Derivative Works to the public;
- d) to perform the Original Work publicly; and
- e) to display the Original Work publicly.
-
-2) Grant of Patent License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, to make, use, sell and offer for sale the Original Work and Derivative Works.
-
-3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor hereby agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work, and by publishing the address of that information repository in a notice immediately following the copyright notice that applies to the Original Work.
-
-4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior written permission of the Licensor. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor except as expressly stated herein. No patent license is granted to make, use, sell or offer to sell embodiments of any patent claims other than the licensed claims defined in Section 2. No right is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any Original Work that Licensor otherwise would have a right to license.
-
-5) This section intentionally omitted.
-
-6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
-
-7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately proceeding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to Original Work is granted hereunder except under this disclaimer.
-
-8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to any person for any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to liability for death or personal injury resulting from Licensor's negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.
-
-9) Acceptance and Termination. If You distribute copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. Nothing else but this License (or another written agreement between Licensor and You) grants You permission to create Derivative Works based upon the Original Work or to exercise any of the rights granted in Section 1 herein, and any attempt to do so except under the terms of this License (or another written agreement between Licensor and You) is expressly prohibited by U.S. copyright law, the equivalent laws of other countries, and by international treaty. Therefore, by exercising any of the rights granted to You in Section 1 herein, You indicate Your acceptance of this License and all of its terms and conditions.
-
-10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, for patent infringement (i) against Licensor with respect to a patent applicable to software or (ii) against any entity with respect to a patent applicable to the Original Work (but excluding combinations of the Original Work with other software or hardware).
-
-11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of the U.S. Copyright Act, 17 U.S.C. ¤ 101 et seq., the equivalent laws of other countries, and international treaty. This section shall survive the termination of this License.
-
-12) Attorneys Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
-
-13) Miscellaneous. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
-
-14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
-
-This license is Copyright (C) 2003 Lawrence E. Rosen. All rights reserved.
-Permission is hereby granted to copy and distribute this license without modification. This license may not be modified without the express written permission of its copyright owner.
diff --git a/options/license/AFL-2.1 b/options/license/AFL-2.1
deleted file mode 100644
index 011d6d489b..0000000000
--- a/options/license/AFL-2.1
+++ /dev/null
@@ -1,45 +0,0 @@
-The Academic Free License
-v.2.1
-
-This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
-
- Licensed under the Academic Free License version 2.1
-
-1) Grant of Copyright License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license to do the following:
-
- a) to reproduce the Original Work in copies;
- b) to prepare derivative works ("Derivative Works") based upon the Original Work;
- c) to distribute copies of the Original Work and Derivative Works to the public;
- d) to perform the Original Work publicly; and
- e) to display the Original Work publicly.
-
-2) Grant of Patent License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, to make, use, sell and offer for sale the Original Work and Derivative Works.
-
-3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor hereby agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work, and by publishing the address of that information repository in a notice immediately following the copyright notice that applies to the Original Work.
-
-4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior written permission of the Licensor. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor except as expressly stated herein. No patent license is granted to make, use, sell or offer to sell embodiments of any patent claims other than the licensed claims defined in Section 2. No right is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any Original Work that Licensor otherwise would have a right to license.
-
-5) This section intentionally omitted.
-
-6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
-
-7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately proceeding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to Original Work is granted hereunder except under this disclaimer.
-
-8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to any person for any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to liability for death or personal injury resulting from Licensor's negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.
-
- 9) Acceptance and Termination. If You distribute copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. Nothing else but this License (or another written agreement between Licensor and You) grants You permission to create Derivative Works based upon the Original Work or to exercise any of the rights granted in Section 1 herein, and any attempt to do so except under the terms of this License (or another written agreement between Licensor and You) is expressly prohibited by U.S. copyright law, the equivalent laws of other countries, and by international treaty. Therefore, by exercising any of the rights granted to You in Section 1 herein, You indicate Your acceptance of this License and all of its terms and conditions.
-
-10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
-
-11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et seq., the equivalent laws of other countries, and international treaty. This section shall survive the termination of this License.
-
-12) Attorneys Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
-
-13) Miscellaneous. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
-
-14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
-
-This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved.
-Permission is hereby granted to copy and distribute this license without modification. This license may not be modified without the express written permission of its copyright owner.
diff --git a/options/license/AFL-3.0 b/options/license/AFL-3.0
deleted file mode 100644
index e1b7792ae6..0000000000
--- a/options/license/AFL-3.0
+++ /dev/null
@@ -1,43 +0,0 @@
-Academic Free License (“AFLâ€) v. 3.0
-
-This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
-
- Licensed under the Academic Free License version 3.0
-
-1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
-
- a) to reproduce the Original Work in copies, either alone or as part of a collective work;
- b) to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
- c) to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor’s reserved rights and remedies, in this Academic Free License;
- d) to perform the Original Work publicly; and
- e) to display the Original Work publicly.
-
-2) Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
-
-3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
-
- 4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor’s trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
-
-5) External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
-
-6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
-
-7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
-
-8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
-
-9) Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including “fair use†or “fair dealingâ€). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
-
-10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
-
-11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
-
-12) Attorneys’ Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
-
-13) Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
-
-14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
-
-16) Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
diff --git a/options/license/AGPL-1.0-only b/options/license/AGPL-1.0-only
deleted file mode 100644
index a00f82e601..0000000000
--- a/options/license/AGPL-1.0-only
+++ /dev/null
@@ -1,86 +0,0 @@
-AFFERO GENERAL PUBLIC LICENSE
-Version 1, March 2002 Copyright © 2002 Affero Inc. 510 Third Street - Suite 225, San Francisco, CA 94107, USA
-
-This license is a modified version of the GNU General Public License copyright (C) 1989, 1991 Free Software Foundation, Inc. made with their permission. Section 2(d) has been added to cover use of software over a computer network.
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-Preamble
-
-The licenses for most software are designed to take away your freedom to share and change it. By contrast, the Affero General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This Public License applies to most of Affero's software and to any other program whose authors commit to using it. (Some other Affero software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. This General Public License is designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
-
-To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
-
-For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
-
-We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
-
-Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
-
-Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
-
-The precise terms and conditions for copying, distribution and modification follow.
-
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this Affero General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
-
-1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
-
-2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
- b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
- c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
- d) If the Program as you received it is intended to interact with users through a computer network and if, in the version you received, any user interacting with the Program was given the opportunity to request transmission to that user of the Program's complete source code, you must not remove that facility from your modified version of the Program or work based on the Program, and must offer an equivalent opportunity for all users interacting with your Program through a computer network to request immediate transmission by HTTP of the complete source code of your modified version or other derivative work.
-
-These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
-
-3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
- a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
- b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
- c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
-
-If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
-
-4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
-
-6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
-
-7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
-
-This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
-
-8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
-
-9. Affero Inc. may publish revised and/or new versions of the Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by Affero, Inc. If the Program does not specify a version number of this License, you may choose any version ever published by Affero, Inc.
-
-You may also choose to redistribute modified versions of this program under any version of the Free Software Foundation's GNU General Public License version 3 or higher, so long as that version of the GNU GPL includes terms and conditions substantially equivalent to those of this license.
-
-10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by Affero, Inc., write to us; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
-
-NO WARRANTY
-
-11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/options/license/AGPL-1.0-or-later b/options/license/AGPL-1.0-or-later
deleted file mode 100644
index a00f82e601..0000000000
--- a/options/license/AGPL-1.0-or-later
+++ /dev/null
@@ -1,86 +0,0 @@
-AFFERO GENERAL PUBLIC LICENSE
-Version 1, March 2002 Copyright © 2002 Affero Inc. 510 Third Street - Suite 225, San Francisco, CA 94107, USA
-
-This license is a modified version of the GNU General Public License copyright (C) 1989, 1991 Free Software Foundation, Inc. made with their permission. Section 2(d) has been added to cover use of software over a computer network.
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-Preamble
-
-The licenses for most software are designed to take away your freedom to share and change it. By contrast, the Affero General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This Public License applies to most of Affero's software and to any other program whose authors commit to using it. (Some other Affero software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. This General Public License is designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
-
-To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
-
-For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
-
-We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
-
-Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
-
-Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
-
-The precise terms and conditions for copying, distribution and modification follow.
-
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this Affero General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
-
-1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
-
-2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
- b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
- c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
- d) If the Program as you received it is intended to interact with users through a computer network and if, in the version you received, any user interacting with the Program was given the opportunity to request transmission to that user of the Program's complete source code, you must not remove that facility from your modified version of the Program or work based on the Program, and must offer an equivalent opportunity for all users interacting with your Program through a computer network to request immediate transmission by HTTP of the complete source code of your modified version or other derivative work.
-
-These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
-
-3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
- a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
- b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
- c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
-
-If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
-
-4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
-
-6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
-
-7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
-
-This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
-
-8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
-
-9. Affero Inc. may publish revised and/or new versions of the Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by Affero, Inc. If the Program does not specify a version number of this License, you may choose any version ever published by Affero, Inc.
-
-You may also choose to redistribute modified versions of this program under any version of the Free Software Foundation's GNU General Public License version 3 or higher, so long as that version of the GNU GPL includes terms and conditions substantially equivalent to those of this license.
-
-10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by Affero, Inc., write to us; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
-
-NO WARRANTY
-
-11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/options/license/AGPL-3.0-only b/options/license/AGPL-3.0
index 0c97efd25b..0c97efd25b 100644
--- a/options/license/AGPL-3.0-only
+++ b/options/license/AGPL-3.0
diff --git a/options/license/AGPL-3.0-or-later b/options/license/AGPL-3.0-or-later
deleted file mode 100644
index 0c97efd25b..0000000000
--- a/options/license/AGPL-3.0-or-later
+++ /dev/null
@@ -1,235 +0,0 @@
-GNU AFFERO GENERAL PUBLIC LICENSE
-Version 3, 19 November 2007
-
-Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
- Preamble
-
-The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software.
-
-The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users.
-
-When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.
-
-Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software.
-
-A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public.
-
-The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version.
-
-An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license.
-
-The precise terms and conditions for copying, distribution and modification follow.
-
- TERMS AND CONDITIONS
-
-0. Definitions.
-
-"This License" refers to version 3 of the GNU Affero General Public License.
-
-"Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
-
-"The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations.
-
-To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work.
-
-A "covered work" means either the unmodified Program or a work based on the Program.
-
-To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.
-
-To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.
-
-An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
-
-1. Source Code.
-The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work.
-
-A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.
-
-The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.
-
-The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
-The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
-
-The Corresponding Source for a work in source code form is that same work.
-
-2. Basic Permissions.
-All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
-
-You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
-
-Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
-
-3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.
-
-When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.
-
-4. Conveying Verbatim Copies.
-You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.
-
-You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
-
-5. Conveying Modified Source Versions.
-You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
-
-A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.
-
-6. Conveying Non-Source Forms.
-You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
-
- d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
-
-A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.
-
-A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.
-
-"Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.
-
-If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).
-
-The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.
-
-Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.
-
-7. Additional Terms.
-"Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.
-
-When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.
-
-Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
-
-All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.
-
-If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
-
-Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.
-
-8. Termination.
-
-You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).
-
-However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
-
-Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.
-
-9. Acceptance Not Required for Having Copies.
-
-You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.
-
-10. Automatic Licensing of Downstream Recipients.
-
-Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.
-
-An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.
-
-You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
-
-11. Patents.
-
-A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version".
-
-A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
-
-Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.
-
-In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.
-
-If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.
-
-If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.
-
-A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
-
-Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.
-
-12. No Surrender of Others' Freedom.
-
-If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
-
-13. Remote Network Interaction; Use with the GNU General Public License.
-
-Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph.
-
-Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License.
-
-14. Revised Versions of this License.
-
-The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation.
-
-If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.
-
-Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.
-
-15. Disclaimer of Warranty.
-
-THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-16. Limitation of Liability.
-
-IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-17. Interpretation of Sections 15 and 16.
-
-If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
-
-END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
-If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
-
-To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements.
-
-You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see <http://www.gnu.org/licenses/>.
diff --git a/options/license/AMD-newlib b/options/license/AMD-newlib
deleted file mode 100644
index 1b2f1abd6f..0000000000
--- a/options/license/AMD-newlib
+++ /dev/null
@@ -1,11 +0,0 @@
-Copyright 1989, 1990 Advanced Micro Devices, Inc.
-
-This software is the property of Advanced Micro Devices, Inc (AMD) which
-specifically grants the user the right to modify, use and distribute this
-software provided this notice is not removed or altered. All other rights
-are reserved by AMD.
-
-AMD MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH REGARD TO THIS
-SOFTWARE. IN NO EVENT SHALL AMD BE LIABLE FOR INCIDENTAL OR CONSEQUENTIAL
-DAMAGES IN CONNECTION WITH OR ARISING FROM THE FURNISHING, PERFORMANCE, OR
-USE OF THIS SOFTWARE.
diff --git a/options/license/AMDPLPA b/options/license/AMDPLPA
deleted file mode 100644
index a58a8525f8..0000000000
--- a/options/license/AMDPLPA
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2006, 2007 Advanced Micro Devices, Inc.
-All rights reserved.
-
-Redistribution and use in any form of this material and any product thereof including software in source or binary forms, along with any related documentation, with or without modification ("this material"), is permitted provided that the following conditions are met:
-
- Redistributions of source code of any software must retain the above copyright notice and all terms of this license as part of the code.
-
- Redistributions in binary form of any software must reproduce the above copyright notice and all terms of this license in any related documentation and/or other materials.
-
- Neither the names nor trademarks of Advanced Micro Devices, Inc. or any copyright holders or contributors may be used to endorse or promote products derived from this material without specific prior written permission.
-
- Notice about U.S. Government restricted rights: This material is provided with "RESTRICTED RIGHTS." Use, duplication or disclosure by the U.S. Government is subject to the full extent of restrictions set forth in FAR52.227 and DFARS252.227 et seq., or any successor or applicable regulations. Use of this material by the U.S. Government constitutes acknowledgment of the proprietary rights of Advanced Micro Devices, Inc. and any copyright holders and contributors.
-
- ANY BREACH OF ANY TERM OF THIS LICENSE SHALL RESULT IN THE IMMEDIATE REVOCATION OF ALL RIGHTS TO REDISTRIBUTE, ACCESS OR USE THIS MATERIAL.
-
-THIS MATERIAL IS PROVIDED BY ADVANCED MICRO DEVICES, INC. AND ANY COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" IN ITS CURRENT CONDITION AND WITHOUT ANY REPRESENTATIONS, GUARANTEE, OR WARRANTY OF ANY KIND OR IN ANY WAY RELATED TO SUPPORT, INDEMNITY, ERROR FREE OR UNINTERRUPTED OPERATION, OR THAT IT IS FREE FROM DEFECTS OR VIRUSES. ALL OBLIGATIONS ARE HEREBY DISCLAIMED - WHETHER EXPRESS, IMPLIED, OR STATUTORY - INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OPERABILITY, QUALITY OF SERVICE, OR NON-INFRINGEMENT. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. OR ANY COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, PUNITIVE, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, REVENUE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED OR BASED ON ANY THEORY OF LIABILITY ARISING IN ANY WAY RELATED TO THIS MATERIAL, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE ENTIRE AND AGGREGATE LIABILITY OF ADVANCED MICRO DEVICES, INC. AND ANY COPYRIGHT HOLDERS AND CONTRIBUTORS SHALL NOT EXCEED TEN DOLLARS (US $10.00). ANYONE REDISTRIBUTING OR ACCESSING OR USING THIS MATERIAL ACCEPTS THIS ALLOCATION OF RISK AND AGREES TO RELEASE ADVANCED MICRO DEVICES, INC. AND ANY COPYRIGHT HOLDERS AND CONTRIBUTORS FROM ANY AND ALL LIABILITIES, OBLIGATIONS, CLAIMS, OR DEMANDS IN EXCESS OF TEN DOLLARS (US $10.00). THE FOREGOING ARE ESSENTIAL TERMS OF THIS LICENSE AND, IF ANY OF THESE TERMS ARE CONSTRUED AS UNENFORCEABLE, FAIL IN ESSENTIAL PURPOSE, OR BECOME VOID OR DETRIMENTAL TO ADVANCED MICRO DEVICES, INC. OR ANY COPYRIGHT HOLDERS OR CONTRIBUTORS FOR ANY REASON, THEN ALL RIGHTS TO REDISTRIBUTE, ACCESS OR USE THIS MATERIAL SHALL TERMINATE IMMEDIATELY. MOREOVER, THE FOREGOING SHALL SURVIVE ANY EXPIRATION OR TERMINATION OF THIS LICENSE OR ANY AGREEMENT OR ACCESS OR USE RELATED TO THIS MATERIAL.
-
-NOTICE IS HEREBY PROVIDED, AND BY REDISTRIBUTING OR ACCESSING OR USING THIS MATERIAL SUCH NOTICE IS ACKNOWLEDGED, THAT THIS MATERIAL MAY BE SUBJECT TO RESTRICTIONS UNDER THE LAWS AND REGULATIONS OF THE UNITED STATES OR OTHER COUNTRIES, WHICH INCLUDE BUT ARE NOT LIMITED TO, U.S. EXPORT CONTROL LAWS SUCH AS THE EXPORT ADMINISTRATION REGULATIONS AND NATIONAL SECURITY CONTROLS AS DEFINED THEREUNDER, AS WELL AS STATE DEPARTMENT CONTROLS UNDER THE U.S. MUNITIONS LIST. THIS MATERIAL MAY NOT BE USED, RELEASED, TRANSFERRED, IMPORTED, EXPORTED AND/OR RE- EXPORTED IN ANY MANNER PROHIBITED UNDER ANY APPLICABLE LAWS, INCLUDING U.S. EXPORT CONTROL LAWS REGARDING SPECIFICALLY DESIGNATED PERSONS, COUNTRIES AND NATIONALS OF COUNTRIES SUBJECT TO NATIONAL SECURITY CONTROLS. MOREOVER, THE FOREGOING SHALL SURVIVE ANY EXPIRATION OR TERMINATION OF ANY LICENSE OR AGREEMENT OR ACCESS OR USE RELATED TO THIS MATERIAL.
-
-This license forms the entire agreement regarding the subject matter hereof and supersedes all proposals and prior discussions and writings between the parties with respect thereto. This license does not affect any ownership, rights, title, or interest in, or relating to, this material. No terms of this license can be modified or waived, and no breach of this license can be excused, unless done so in a writing signed by all affected parties. Each term of this license is separately enforceable. If any term of this license is determined to be or becomes unenforceable or illegal, such term shall be reformed to the minimum extent necessary in order for this license to remain in effect in accordance with its terms as modified by such reformation. This license shall be governed by and construed in accordance with the laws of the State of Texas without regard to rules on conflicts of law of any state or jurisdiction or the United Nations Convention on the International Sale of Goods. All disputes arising out of this license shall be subject to the jurisdiction of the federal and state courts in Austin, Texas, and all defenses are hereby waived concerning personal jurisdiction and venue of these courts.
diff --git a/options/license/AML b/options/license/AML
deleted file mode 100644
index a9d91ffc8e..0000000000
--- a/options/license/AML
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright: Copyright (c) 2006 by Apple Computer, Inc., All Rights Reserved.
-
-IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this Apple software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this Apple software.
-
-In consideration of your agreement to abide by the following terms, and subject to these terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in this original Apple software (the "Apple Software"), to use, reproduce, modify and redistribute the Apple Software, with or without modifications, in source and/or binary forms; provided that if you redistribute the Apple Software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the Apple Software. Neither the name, trademarks, service marks or logos of Apple Computer, Inc. may be used to endorse or promote products derived from the Apple Software without specific prior written permission from Apple. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Apple herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated.
-
-The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
-
-IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/AML-glslang b/options/license/AML-glslang
deleted file mode 100644
index 2a24aeac22..0000000000
--- a/options/license/AML-glslang
+++ /dev/null
@@ -1,41 +0,0 @@
-Copyright (c) 2002, NVIDIA Corporation.
-
-NVIDIA Corporation("NVIDIA") supplies this software to you in
-consideration of your agreement to the following terms, and your use,
-installation, modification or redistribution of this NVIDIA software
-constitutes acceptance of these terms. If you do not agree with these
-terms, please do not use, install, modify or redistribute this NVIDIA
-software.
-
-In consideration of your agreement to abide by the following terms, and
-subject to these terms, NVIDIA grants you a personal, non-exclusive
-license, under NVIDIA's copyrights in this original NVIDIA software (the
-"NVIDIA Software"), to use, reproduce, modify and redistribute the
-NVIDIA Software, with or without modifications, in source and/or binary
-forms; provided that if you redistribute the NVIDIA Software, you must
-retain the copyright notice of NVIDIA, this notice and the following
-text and disclaimers in all such redistributions of the NVIDIA Software.
-Neither the name, trademarks, service marks nor logos of NVIDIA
-Corporation may be used to endorse or promote products derived from the
-NVIDIA Software without specific prior written permission from NVIDIA.
-Except as expressly stated in this notice, no other rights or licenses
-express or implied, are granted by NVIDIA herein, including but not
-limited to any patent rights that may be infringed by your derivative
-works or by other works in which the NVIDIA Software may be
-incorporated. No hardware is licensed hereunder.
-
-THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
-WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
-INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE,
-NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
-ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER
-PRODUCTS.
-
-IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT,
-INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY
-OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE
-NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT,
-TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
-NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/AMPAS b/options/license/AMPAS
deleted file mode 100644
index 0fc771d755..0000000000
--- a/options/license/AMPAS
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright (c) 2006 Academy of Motion Picture Arts and Sciences ("A.M.P.A.S."). Portions contributed by others as indicated. All rights reserved.
-
-A world-wide, royalty-free, non-exclusive right to distribute, copy, modify, create derivatives, and use, in source and binary forms, is hereby granted, subject to acceptance of this license. Performance of any of the aforementioned acts indicates acceptance to be bound by the following terms and conditions:
-
- * Redistributions of source code must retain the above copyright notice, this list of conditions and the Disclaimer of Warranty.
-
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the Disclaimer of Warranty in the documentation and/or other materials provided with the distribution.
-
- * Nothing in this license shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of A.M.P.A.S. or any contributors, except as expressly stated herein, and neither the name of A.M.P.A.S. nor of any other contributors to this software, may be used to endorse or promote products derived from this software without specific prior written permission of A.M.P.A.S. or contributor, as appropriate.
-
-This license shall be governed by the laws of the State of California, and subject to the jurisdiction of the courts therein.
-
-Disclaimer of Warranty: THIS SOFTWARE IS PROVIDED BY A.M.P.A.S. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL A.M.P.A.S., ANY CONTRIBUTORS OR DISTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/ANTLR-PD b/options/license/ANTLR-PD
deleted file mode 100644
index d75060dac1..0000000000
--- a/options/license/ANTLR-PD
+++ /dev/null
@@ -1,5 +0,0 @@
-ANTLR 2 License
-
-We reserve no legal rights to the ANTLR--it is fully in the public domain. An individual or company may do whatever they wish with source code distributed with ANTLR or the code generated by ANTLR, including the incorporation of ANTLR, or its output, into commerical software.
-
-We encourage users to develop software with ANTLR. However, we do ask that credit is given to us for developing ANTLR. By "credit", we mean that if you use ANTLR or incorporate any source code into one of your programs (commercial product, research project, or otherwise) that you acknowledge this fact somewhere in the documentation, research report, etc... If you like ANTLR and have developed a nice tool with the output, please mention that you developed it using ANTLR. In addition, we ask that the headers remain intact in our source code. As long as these guidelines are kept, we expect to continue enhancing this system and expect to make other tools available as they are completed.
diff --git a/options/license/ANTLR-PD-fallback b/options/license/ANTLR-PD-fallback
deleted file mode 100644
index 12bfe73738..0000000000
--- a/options/license/ANTLR-PD-fallback
+++ /dev/null
@@ -1,7 +0,0 @@
-ANTLR 2 License
-
-We reserve no legal rights to the ANTLR--it is fully in the public domain. An individual or company may do whatever they wish with source code distributed with ANTLR or the code generated by ANTLR, including the incorporation of ANTLR, or its output, into commerical software.
-
-We encourage users to develop software with ANTLR. However, we do ask that credit is given to us for developing ANTLR. By "credit", we mean that if you use ANTLR or incorporate any source code into one of your programs (commercial product, research project, or otherwise) that you acknowledge this fact somewhere in the documentation, research report, etc... If you like ANTLR and have developed a nice tool with the output, please mention that you developed it using ANTLR. In addition, we ask that the headers remain intact in our source code. As long as these guidelines are kept, we expect to continue enhancing this system and expect to make other tools available as they are completed.
-
-In countries where the Public Domain status of the work may not be valid, the author grants a copyright licence to the general public to deal in the work without restriction and permission to sublicence derivates under the terms of any (OSI approved) Open Source licence.
diff --git a/options/license/APAFML b/options/license/APAFML
deleted file mode 100644
index a7824e26ca..0000000000
--- a/options/license/APAFML
+++ /dev/null
@@ -1,3 +0,0 @@
-Copyright (c) 1985, 1987, 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
-
-This file and the 14 PostScript(R) AFM files it accompanies may be used, copied, and distributed for any purpose and without charge, with or without modification, provided that all copyright notices are retained; that the AFM files are not distributed without this file; that all modifications to this file or any of the AFM files are prominently noted in the modified file(s); and that this paragraph is not modified. Adobe Systems has no responsibility or obligation to support the use of the AFM files.
diff --git a/options/license/APL-1.0 b/options/license/APL-1.0
deleted file mode 100644
index 0748f90cd9..0000000000
--- a/options/license/APL-1.0
+++ /dev/null
@@ -1,295 +0,0 @@
-ADAPTIVE PUBLIC LICENSE
-Version 1.0
-
-THE LICENSED WORK IS PROVIDED UNDER THE TERMS OF THIS ADAPTIVE PUBLIC LICENSE ("LICENSE"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE LICENSED WORK CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS LICENSE AND ITS TERMS, WHETHER OR NOT SUCH RECIPIENT READS THE TERMS OF THIS LICENSE. "LICENSED WORK" AND "RECIPIENT" ARE DEFINED BELOW.
-
-IMPORTANT NOTE: This License is "adaptive", and the generic version or another version of an Adaptive Public License should not be relied upon to determine your rights and obligations under this License. You must read the specific Adaptive Public License that you receive with the Licensed Work, as certain terms are defined at the outset by the Initial Contributor.
-
-See Section 2.2 below, Exhibit A attached, and any Suppfile.txt accompanying this License to determine the specific adaptive features applicable to this License. For example, without limiting the foregoing, (a) for selected choice of law and jurisdiction see Part 3 of Exhibit A; (b) for the selected definition of Third Party see Part 4 of Exhibit A; and (c) for selected patent licensing terms (if any) see Section 2.2 below and Part 6 of Exhibit A.
-
-1. DEFINITIONS.
-
-1.1. "CONTRIBUTION" means:
-
- (a) In the case of the Initial Contributor, the Initial Work distributed under this License by the Initial Contributor; and
- (b) In the case of each Subsequent Contributor, the Subsequent Work originating from and distributed by such Subsequent Contributor.
-
-1.2. "DESIGNATED WEB SITE" means the web site having the URL identified in Part 1 of Exhibit A, which URL may be changed by the Initial Contributor by posting on the current Designated Web Site the new URL for at least sixty (60) days.
-
-1.3. "DISTRIBUTOR" means any Person that distributes the Licensed Work or any portion thereof to at least one Third Party.
-
-1.4. "ELECTRONIC DISTRIBUTION MECHANISM" means any mechanism generally accepted in the software development community for the electronic transfer of data.
-
-1.5. "EXECUTABLE" means the Licensed Work in any form other than Source Code.
-
-1.6. "GOVERNING JURISDICTION" means the state, province or other legal jurisdiction identified in Part 3 of Exhibit A.
-
-1.7. "INDEPENDENT MODULE" means a separate module of software and/or data that is not a derivative work of or copied from the Licensed Work or any portion thereof. In addition, a module does not qualify as an Independent Module but instead forms part of the Licensed Work if the module: (a) is embedded in the Licensed Work; (b) is included by reference in the Licensed Work other than by a function call or a class reference; or (c) must be included or contained, in whole or in part, within a file directory or subdirectory actually containing files making up the Licensed Work.
-
-1.8. "INITIAL CONTRIBUTOR" means the Person or entity identified as the Initial Contributor in the notice required by Part 1 of Exhibit A.
-
-1.9. "INITIAL WORK" means the initial Source Code, object code (if any) and documentation for the computer program identified in Part 2 of Exhibit A, as such Source Code, object code and documentation is distributed under this License by the Initial Contributor.
-
-1.10. "LARGER WORK" means a work that combines the Licensed Work or portions thereof with code not governed by this License.
-
-1.11. "LICENSED WORK" means the Initial Work and/or any Subsequent Work, in each case including portions thereof.
-
-1.12. "LICENSE NOTICE" has the meaning assigned in Part 5 of Exhibit A.
-
-1.13. "MODIFICATION" or "MODIFICATIONS" means any change to and/or addition to the Licensed Work.
-
-1.14. "PERSON" means an individual or other legal entity, including a corporation, partnership or other body.
-
-1.15. "RECIPIENT" means any Person who receives or obtains the Licensed Work under this License (by way of example, without limiting the foregoing, any Subsequent Contributor or Distributor).
-
-1.16. "SOURCE CODE" means the source code for a computer program, including the source code for all modules and components of the computer program, plus any associated interface definition files, and scripts used to control compilation and installation of an executable.
-
-1.17. "SUBSEQUENT CONTRIBUTOR" means any Person that makes or contributes to the making of any Subsequent Work and that distributes that Subsequent Work to at least one Third Party.
-
-1.18. "SUBSEQUENT WORK" means a work that has resulted or arises from changes to and/or additions to:
-
- (a) the Initial Work;
- (b) any other Subsequent Work; or
- (c) to any combination of the Initial Work and any such other Subsequent Work;
-where such changes and/or additions originate from a Subsequent Contributor. A Subsequent Work will "originate" from a Subsequent Contributor if the Subsequent Work was a result of efforts by such Subsequent Contributor (or anyone acting on such Subsequent Contributor's behalf, such as, a contractor or other entity that is engaged by or under the direction of the Subsequent Contributor). For greater certainty, a Subsequent Work expressly excludes and shall not capture within its meaning any Independent Module.
-
-1.19. "SUPPLEMENT FILE" means a file distributed with the Licensed Work having a file name "suppfile.txt".
-
-1.20. "THIRD PARTY" has the meaning assigned in Part 4 of Exhibit A.
-
-2. LICENSE.
-
-2.1. COPYRIGHT LICENSE FROM INITIAL AND SUBSEQUENT CONTRIBUTORS.
-
- (a) Subject to the terms of this License, the Initial Contributor hereby grants each Recipient a world-wide, royalty-free, non-exclusive copyright license to:
-
- (i) reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Initial Work; and
- (ii) reproduce, publicly display, publicly perform, distribute, and sublicense any derivative works (if any) prepared by Recipient;
-in Source Code and Executable form, either with other Modifications, on an unmodified basis, or as part of a Larger Work.
-
- (b) Subject to the terms of this License, each Subsequent Contributor hereby grants each Recipient a world-wide, royalty-free, non-exclusive copyright license to:
-
- (i) reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Subsequent Work of such Subsequent Contributor; and
- (ii) reproduce, publicly display, publicly perform, distribute, and sublicense any derivative works (if any) prepared by Recipient;
-in Source Code and Executable form, either with other Modifications, on an unmodified basis, or as part of a Larger Work.
-
-2.2. PATENT LICENSE FROM INITIAL AND SUBSEQUENT CONTRIBUTORS.
-
- (a) This License does not include or grant any patent license whatsoever from the Initial Contributor, Subsequent Contributor, or any Distributor unless, at the time the Initial Work is first distributed or made available under this License (as the case may be), the Initial Contributor has selected pursuant to Part 6 of Exhibit A the patent terms in paragraphs A, B, C, D and E from Part 6 of Exhibit A. If this is not done then the Initial Work and any other Subsequent Work is made available under the License without any patent license (the "PATENTS-EXCLUDED LICENSE").
- (b) However, the Initial Contributor may subsequently distribute or make available (as the case may be) future copies of: (1) the Initial Work; or (2) any Licensed Work distributed by the Initial Contributor which includes the Initial Work (or any portion thereof) and/or any Modification made by the Initial Contributor; available under a License which includes a patent license (the "PATENTS-INCLUDED LICENSE") by selecting pursuant to Part 6 of Exhibit A the patent terms in paragraphs A, B, C, D and E from Part 6 of Exhibit A, when the Initial Contributor distributes or makes available (as the case may be) such future copies under this License.
- (c) If any Recipient receives or obtains one or more copies of the Initial Work or any other portion of the Licensed Work under the Patents-Included License, then all licensing of such copies under this License shall include the terms in paragraphs A, B, C, D and E from Part 6 of Exhibit A and that Recipient shall not be able to rely upon the Patents-Excluded License for any such copies. However, all Recipients that receive one or more copies of the Initial Work or any other portion of the Licensed Work under a copy of the License which includes the Patents-Excluded License shall have no patent license with respect to such copies received under the Patents-Excluded License and availability and distribution of such copies, including Modifications made by such Recipient to such copies, shall be under a copy of the License without any patent license.
- (d) Where a Recipient uses in combination or combines any copy of the Licensed Work (or portion thereof) licensed under a copy of the License having a Patents-Excluded License with any copy of the Licensed Work (or portion thereof) licensed under a copy of the License having a Patents-Included License, the combination (and any portion thereof) shall, from the first time such Recipient uses, makes available or distributes the combination (as the case may be), be subject to only the terms of the License having the Patents-Included License which shall include the terms in paragraphs A, B, C, D and E from Part 6 of Exhibit A.
-
-2.3. ACKNOWLEDGEMENT AND DISCLAIMER.
-Recipient understands and agrees that although Initial Contributor and each Subsequent Contributor grants the licenses to its Contributions set forth herein, no representation, warranty, guarantee or assurance is provided by any Initial Contributor, Subsequent Contributor, or Distributor that the Licensed Work does not infringe the patent or other intellectual property rights of any other entity. Initial Contributor, Subsequent Contributor, and each Distributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise, in relation to the Licensed Works. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, without limiting the foregoing disclaimers, if a third party patent license is required to allow Recipient to distribute the Licensed Work, it is Recipient's responsibility to acquire that license before distributing the Licensed Work.
-
-2.4. RESERVATION.
-Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Initial Contributor, Subsequent Contributor, or Distributor except as expressly stated herein.
-
-3. DISTRIBUTION OBLIGATIONS.
-
-3.1. DISTRIBUTION GENERALLY.
-
- (a) A Subsequent Contributor shall make that Subsequent Contributor's Subsequent Work(s) available to the public via an Electronic Distribution Mechanism for a period of at least twelve (12) months. The aforesaid twelve (12) month period shall begin within a reasonable time after the creation of the Subsequent Work and no later than sixty (60) days after first distribution of that Subsequent Contributor's Subsequent Work.
- (b) All Distributors must distribute the Licensed Work in accordance with the terms of the License, and must include a copy of this License (including without limitation Exhibit A and the accompanying Supplement File) with each copy of the Licensed Work distributed. In particular, this License must be prominently distributed with the Licensed Work in a file called "license.txt." In addition, the License Notice in Part 5 of Exhibit A must be included at the beginning of all Source Code files, and viewable to a user in any executable such that the License Notice is reasonably brought to the attention of any party using the Licensed Work.
-
-3.2. EXECUTABLE DISTRIBUTIONS OF THE LICENSED WORK.
-A Distributor may choose to distribute the Licensed Work, or any portion thereof, in Executable form (an "EXECUTABLE DISTRIBUTION") to any third party, under the terms of Section 2 of this License, provided the Executable Distribution is made available under and accompanied by a copy of this License, AND provided at least ONE of the following conditions is fulfilled:
-
- (a) The Executable Distribution must be accompanied by the Source Code for the Licensed Work making up the Executable Distribution, and the Source Code must be distributed on the same media as the Executable Distribution or using an Electronic Distribution Mechanism; or
- (b) The Executable Distribution must be accompanied with a written offer, valid for at least thirty six (36) months, to give any third party under the terms of this License, for a charge no more than the cost of physically performing source distribution, a complete machine-readable copy of the Source Code for the Licensed Work making up the Executable Distribution, to be available and distributed using an Electronic Distribution Mechanism, and such Executable Distribution must remain available in Source Code form to any third party via the Electronic Distribution Mechanism (or any replacement Electronic Distribution Mechanism the particular Distributor may reasonably need to turn to as a substitute) for said at least thirty six (36) months.
-
-For greater certainty, the above-noted requirements apply to any Licensed Work or portion thereof distributed to any third party in Executable form, whether such distribution is made alone, in combination with a Larger Work or Independent Modules, or in some other combination.
-
-3.3. SOURCE CODE DISTRIBUTIONS.
-When a Distributor makes the Licensed Work, or any portion thereof, available to any Person in Source Code form, it must be made available under this License and a copy of this License must be included with each copy of the Source Code, situated so that the copy of the License is conspicuously brought to the attention of that Person. For greater clarification, this Section 3.3 applies to all distribution of the Licensed Work in any Source Code form. A Distributor may charge a fee for the physical act of transferring a copy, which charge shall be no more than the cost of physically performing source distribution.
-
-3.4. REQUIRED NOTICES IN SOURCE CODE.
-Each Subsequent Contributor must ensure that the notice set out in Part 5 of Exhibit A is included in each file of the Source Code for each Subsequent Work originating from that particular Subsequent Contributor, if such notice is not already included in each such file. If it is not possible to put such notice in a particular Source Code file due to its structure, then the Subsequent Contributor must include such notice in a location (such as a relevant directory in which the file is stored) where a user would be likely to look for such a notice.
-
-3.5. NO DISTRIBUTION REQUIREMENTS FOR INTERNALLY USED MODIFICATIONS.
-Notwithstanding Sections 3.2, 3.3 and 3.4, Recipient may, internally within its own corporation or organization use the Licensed Work, including the Initial Work and Subsequent Works, and make Modifications for internal use within Recipient's own corporation or organization (collectively, "INTERNAL USE MODIFICATIONS"). The Recipient shall have no obligation to distribute, in either Source Code or Executable form, any such Internal Use Modifications made by Recipient in the course of such internal use, except where required below in this Section 3.5. All Internal Use Modifications distributed to any Person, whether or not a Third Party, shall be distributed pursuant to and be accompanied by the terms of this License. If the Recipient chooses to distribute any such Internal Use Modifications to any Third Party, then the Recipient shall be deemed a Subsequent Contributor, and any such Internal Use Modifications distributed to any Third Party shall be deemed a Subsequent Work originating from that Subsequent Contributor, and shall from the first such instance become part of the Licensed Work that must thereafter be distributed and made available to third parties in accordance with the terms of Sections 3.1 to 3.4 inclusive.
-
-3.6. INDEPENDENT MODULES.
-This License shall not apply to Independent Modules of any Initial Contributor, Subsequent Contributor, Distributor or any Recipient, and such Independent Modules may be licensed or made available under one or more separate license agreements.
-
-3.7. LARGER WORKS.
-Any Distributor or Recipient may create or contribute to a Larger Work by combining any of the Licensed Work with other code not governed by the terms of this License, and may distribute the Larger Work as one or more products. However, in any such case, Distributor or Recipient (as the case may be) must make sure that the requirements of this License are fulfilled for the Licensed Work portion of the Larger Work.
-
-3.8. DESCRIPTION OF DISTRIBUTED MODIFICATIONS.
-
- (a) Each Subsequent Contributor (including the Initial Contributor where the Initial Contributor also qualifies as a Subsequent Contributor) must cause each Subsequent Work created or contributed to by that Subsequent Contributor to contain a file documenting the changes, in accordance with the requirements of Part 1 of the Supplement File, that such Subsequent Contributor made in the creation or contribution to that Subsequent Work. If no Supplement File exists or no requirements are set out in Part 1 of the Supplement File, then there are no requirements for Subsequent Contributors to document changes that they make resulting in Subsequent Works.
- (b) The Initial Contributor may at any time introduce requirements or add to or change earlier requirements (in each case, the "EARLIER DESCRIPTION REQUIREMENTS") for documenting changes resulting in Subsequent Works by revising Part 1 of each copy of the Supplement File distributed by the Initial Contributor with future copies of the Licensed Work so that Part 1 then contains new requirements (the "NEW DESCRIPTION REQUIREMENTS") for documenting such changes.
- (c) Any Recipient receiving at any time any copy of an Initial Work or any Subsequent Work under a copy of this License (in each case, an "Earlier LICENSED COPY") having the Earlier Description Requirements may choose, with respect to each such Earlier Licensed Copy, to comply with the Earlier Description Requirements or the New Description Requirements. Where a Recipient chooses to comply with the New Description Requirements, that Recipient will, when thereafter distributing any copies of any such Earlier Licensed Copy, include a Supplement File having a section entitled Part 1 that contains a copy of the New Description Requirements.
- (d) For greater certainty, the intent of Part 1 of the Supplement File is to provide a mechanism (if any) by which Subsequent Contributors must document changes that they make to the Licensed Work resulting in Subsequent Works. Part 1 of any Supplement File shall not be used to increase or reduce the scope of the license granted in Article 2 of this License or in any other way increase or decrease the rights and obligations of any Recipient, and shall at no time serve as the basis for terminating the License. Further, a Recipient can be required to correct and change its documentation procedures to comply with Part 1 of the Supplement File, but cannot be penalised with damages. Part 1 of any Supplement File is only binding on each Recipient of any Licensed Work to the extent Part 1 sets out the requirements for documenting changes to the Initial Work or any Subsequent Work.
- (e) An example of a set of requirements for documenting changes and contributions made by Subsequent Contributor is set out in Part 7 of Exhibit A of this License. Part 7 is a sample only and is not binding on Recipients, unless (subject to the earlier paragraphs of this Section 3.8) those are the requirements that the Initial Contributor includes in Part 1 of the Supplement File with the copies of the Initial Work distributed under this License.
-
-3.9. USE OF DISTRIBUTOR NAME.
-The name of a Distributor may not be used by any other Distributor to endorse or promote the Licensed Work or products derived from the Licensed Work, without prior written permission.
-
-3.10. LIMITED RECOGNITION OF INITIAL CONTRIBUTOR.
-
- (a) As a modest attribution to the Initial Contributor, in the hope that its promotional value may help justify the time, money and effort invested in writing the Initial Work, the Initial Contributor may include in Part 2 of the Supplement File a requirement that each time an executable program resulting from the Initial Work or any Subsequent Work, or a program dependent thereon, is launched or run, a prominent display of the Initial Contributor's attribution information must occur (the "ATTRIBUTION INFORMATION"). The Attribution Information must be included at the beginning of each Source Code file. For greater certainty, the Initial Contributor may specify in the Supplement File that the above attribution requirement only applies to an executable program resulting from the Initial Work or any Subsequent Work, but not a program dependent thereon. The intent is to provide for reasonably modest attribution, therefore the Initial Contributor may not require Recipients to display, at any time, more than the following Attribution Information: (a) a copyright notice including the name of the Initial Contributor; (b) a word or one phrase (not exceeding 10 words); (c) one digital image or graphic provided with the Initial Work; and (d) a URL (collectively, the "ATTRIBUTION LIMITS").
- (b) If no Supplement File exists, or no Attribution Information is set out in Part 2 of the Supplement File, then there are no requirements for Recipients to display any Attribution Information of the Initial Contributor.
- (c) Each Recipient acknowledges that all trademarks, service marks and/or trade names contained within Part 2 of the Supplement File distributed with the Licensed Work are the exclusive property of the Initial Contributor and may only be used with the permission of the Initial Contributor, or under circumstances otherwise permitted by law, or as expressly set out in this License.
-3.11. For greater certainty, any description or attribution provisions contained within a Supplement File may only be used to specify the nature of the description or attribution requirements, as the case may be. Any provision in a Supplement File that otherwise purports to modify, vary, nullify or amend any right, obligation or representation contained herein shall be deemed void to that extent, and shall be of no force or effect.
-
-4. COMMERCIAL USE AND INDEMNITY.
-
-4.1. COMMERCIAL SERVICES.
-A Recipient ("COMMERCIAL RECIPIENT") may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations (collectively, "SERVICES") to one or more other Recipients or Distributors. However, such Commercial Recipient may do so only on that Commercial Recipient's own behalf, and not on behalf of any other Distributor or Recipient, and Commercial Recipient must make it clear than any such warranty, support, indemnity or liability obligation(s) is/are offered by Commercial Recipient alone. At no time may Commercial Recipient use any Services to deny any party the Licensed Work in Source Code or Executable form when so required under any of the other terms of this License. For greater certainty, this Section 4.1 does not diminish any of the other terms of this License, including without limitation the obligation of the Commercial Recipient as a Distributor, when distributing any of the Licensed Work in Source Code or Executable form, to make such distribution royalty-free (subject to the right to charge a fee of no more than the cost of physically performing Source Code or Executable distribution (as the case may be)).
-
-4.2. INDEMNITY.
-Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this License is intended to facilitate the commercial use of the Licensed Work, the Distributor who includes any of the Licensed Work in a commercial product offering should do so in a manner which does not create potential liability for other Distributors. Therefore, if a Distributor includes the Licensed Work in a commercial product offering or offers any Services, such Distributor ("COMMERCIAL DISTRIBUTOR") hereby agrees to defend and indemnify every other Distributor or Subsequent Contributor (in each case an "INDEMNIFIED PARTY") against any losses, damages and costs (collectively "LOSSES") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Party to the extent caused by the acts or omissions of such Commercial Distributor in connection with its distribution of any of the Licensed Work in a commercial product offering or in connection with any Services. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Party must: (a) promptly notify the Commercial Distributor in writing of such claim; and (b) allow the Commercial Distributor to control, and co-operate with the Commercial Distributor in, the defense and any related settlement negotiations. The Indemnified Party may participate in any such claim at its own expense.
-
-5. VERSIONS OF THE LICENSE.
-
-5.1. NEW VERSIONS.
-The Initial Contributor may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
-5.2. EFFECT OF NEW VERSIONS.
-Once the Licensed Work or any portion thereof has been published by Initial Contributor under a particular version of the License, Recipient may choose to continue to use it under the terms of that version. However, if a Recipient chooses to use the Licensed Work under the terms of any subsequent version of the License published by the Initial Contributor, then from the date of making this choice, the Recipient must comply with the terms of that subsequent version with respect to all further reproduction, preparation of derivative works, public display of, public performance of, distribution and sublicensing by the Recipient in connection with the Licensed Work. No one other than the Initial Contributor has the right to modify the terms applicable to the Licensed Work
-
-6. DISCLAIMER OF WARRANTY.
-
-6.1. GENERAL DISCLAIMER.
-EXCEPT AS EXPRESSLY SET FORTH IN THIS LICENSE, THE LICENSED WORK IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT ANY REPRESENTATION, WARRANTY, GUARANTEE, ASSURANCE OR CONDITION OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LICENSED WORK IS WITH RECIPIENT. SHOULD ANY LICENSED WORK PROVE DEFECTIVE IN ANY RESPECT, RECIPIENT (NOT THE INITIAL CONTRIBUTOR OR ANY SUBSEQUENT CONTRIBUTOR) ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS CLAUSE CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY LICENSED WORK IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS LICENSE INCLUDING WITHOUT LIMITATION THIS DISCLAIMER.
-
-6.2. RESPONSIBILITY OF RECIPIENTS.
-Each Recipient is solely responsible for determining the appropriateness of using and distributing the Licensed Work and assumes all risks associated with its exercise of rights under this License, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
-
-7. TERMINATION.
-
-7.1. This License shall continue until terminated in accordance with the express terms herein.
-
-7.2. Recipient may choose to terminate this License automatically at any time.
-
-7.3. This License, including without limitation the rights granted hereunder to a particular Recipient, will terminate automatically if such Recipient is in material breach of any of the terms of this License and fails to cure such breach within sixty (60) days of becoming aware of the breach. Without limiting the foregoing, any material breach by such Recipient of any term of any other License under which such Recipient is granted any rights to the Licensed Work shall constitute a material breach of this License.
-
-7.4. Upon termination of this License by or with respect to a particular Recipient for any reason, all rights granted hereunder and under any other License to that Recipient shall terminate. However, all sublicenses to the Licensed Work which were previously properly granted by such Recipient under a copy of this License (in each case, an "Other License" and in plural, "Other Licenses") shall survive any such termination of this License, including without limitation the rights and obligations under such Other Licenses as set out in their respective Sections 2, 3, 4, 5, 6, 7 and 8, mutatis mutandis, for so long as the respective sublicensees (i.e. other Recipients) remain in compliance with the terms of the copy of this License under which such sublicensees received rights to the Licensed Work. Any termination of such Other Licenses shall be pursuant to their respective Section 7, mutatis mutandis. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-7.5. Upon any termination of this License by or with respect to a particular Recipient, Sections 4.1, 4.2, 6.1, 6.2, 7.4, 7.5, 8.1, and 8.2, together with all provisions of this License necessary for the interpretation and enforcement of same, shall expressly survive such termination.
-
-8. LIMITATION OF LIABILITY.
-
-8.1. IN NO EVENT SHALL ANY OF INITIAL CONTRIBUTOR, ITS SUBSIDIARIES, OR AFFILIATES, OR ANY OF ITS OR THEIR RESPECTIVE OFFICERS, DIRECTORS, EMPLOYEES, AND/OR AGENTS (AS THE CASE MAY BE), HAVE ANY LIABILITY FOR ANY DIRECT DAMAGES, INDIRECT DAMAGES, PUNITIVE DAMAGES, INCIDENTAL DAMAGES, SPECIAL DAMAGES, EXEMPLARY DAMAGES, CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHATSOEVER (INCLUDING WITHOUT LIMITATION LOSS OF USE, DATA OR PROFITS, OR ANY OTHER LOSS ARISING OUT OF OR IN ANY WAY RELATED TO THE USE, INABILITY TO USE, UNAUTHORIZED USE, PERFORMANCE, OR NON-PERFORMANCE OF THE LICENSED WORK OR ANY PART THEREOF OR THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, OR THAT RESULT FROM ERRORS, DEFECTS, OMISSIONS, DELAYS IN OPERATION OR TRANSMISSION, OR ANY OTHER FAILURE OF PERFORMANCE), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) IN RELATION TO OR ARISING IN ANY WAY OUT OF THIS LICENSE OR THE USE OR DISTRIBUTION OF THE LICENSED WORK OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. THIS CLAUSE CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY LICENSED WORK IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS LICENSE INCLUDING WITHOUT LIMITATION THE LIMITATIONS SET FORTH IN THIS SECTION 8.1.
-
-8.2. EXCEPT AS EXPRESSLY SET FORTH IN THIS LICENSE, EACH RECIPIENT SHALL NOT HAVE ANY LIABILITY FOR ANY EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE LICENSED WORK OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION.
-
-9. GOVERNING LAW AND LEGAL ACTION.
-
-9.1. This License shall be governed by and construed in accordance with the laws of the Governing Jurisdiction assigned in Part 3 of Exhibit A, without regard to its conflict of law provisions. No party may bring a legal action under this License more than one year after the cause of the action arose. Each party waives its rights (if any) to a jury trial in any litigation arising under this License. Note that if the Governing Jurisdiction is not assigned in Part 3 of Exhibit A, then the Governing Jurisdiction shall be the State of New York.
-
-9.2. The courts of the Governing Jurisdiction shall have jurisdiction, but not exclusive jurisdiction, to entertain and determine all disputes and claims, whether for specific performance, injunction, damages or otherwise, both at law and in equity, arising out of or in any way relating to this License, including without limitation, the legality, validity, existence and enforceability of this License. Each party to this License hereby irrevocably attorns to and accepts the jurisdiction of the courts of the Governing Jurisdiction for such purposes.
-
-9.3. Except as expressly set forth elsewhere herein, in the event of any action or proceeding brought by any party against another under this License the prevailing party shall be entitled to recover all costs and expenses including the fees of its attorneys in such action or proceeding in such amount as the court may adjudge reasonable.
-
-10. MISCELLANEOUS.
-
-10.1. The obligations imposed by this License are for the benefit of the Initial Contributor and any Recipient, and each Recipient acknowledges and agrees that the Initial Contributor and/or any other Recipient may enforce the terms and conditions of this License against any Recipient.
-
-10.2. This License represents the complete agreement concerning subject matter hereof, and supersedes and cancels all previous oral and written communications, representations, agreements and understandings between the parties with respect to the subject matter hereof.
-
-10.3. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded.
-
-10.4. The language in all parts of this License shall be in all cases construed simply according to its fair meaning, and not strictly for or against any of the parties hereto. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-10.5. If any provision of this License is invalid or unenforceable under the laws of the Governing Jurisdiction, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
-10.6. The paragraph headings of this License are for reference and convenience only and are not a part of this License, and they shall have no effect upon the construction or interpretation of any part hereof.
-
-10.7. Each of the terms "including", "include" and "includes", when used in this License, is not limiting whether or not non-limiting language (such as "without limitation" or "but not limited to" or words of similar import) is used with reference thereto.
-
-10.8. The parties hereto acknowledge they have expressly required that this License and notices relating thereto be drafted in the English language.
-
-//***THE LICENSE TERMS END HERE (OTHER THAN AS SET OUT IN EXHIBIT A).***//
-
-EXHIBIT A (to the Adaptive Public License)
-
-PART 1: INITIAL CONTRIBUTOR AND DESIGNATED WEB SITE
-
-The Initial Contributor is:
-____________________________________________________
-
-[Enter full name of Initial Contributor]
-
-Address of Initial Contributor:
-________________________________________________
-
-________________________________________________
-
-________________________________________________
-
-[Enter address above]
-
-The Designated Web Site is:
-__________________________________________________
-
-[Enter URL for Designated Web Site of Initial Contributor]
-
-NOTE: The Initial Contributor is to complete this Part 1, along with Parts 2, 3, and 5, and, if applicable, Parts 4 and 6.
-
-PART 2: INITIAL WORK
-
-The Initial Work comprises the computer program(s) distributed by the Initial Contributor having the following title(s): _______________________________________________.
-
-The date on which the Initial Work was first available under this License: _________________
-
-PART 3: GOVERNING JURISDICTION
-
-For the purposes of this License, the Governing Jurisdiction is _________________________________________________. [Initial Contributor to Enter Governing Jurisdiction here]
-
-PART 4: THIRD PARTIES
-
-For the purposes of this License, "Third Party" has the definition set forth below in the ONE paragraph selected by the Initial Contributor from paragraphs A, B, C, D and E when the Initial Work is distributed or otherwise made available by the Initial Contributor. To select one of the following paragraphs, the Initial Contributor must place an "X" or "x" in the selection box alongside the one respective paragraph selected.
-SELECTION
-
-BOX PARAGRAPH
-[ ] A. "THIRD PARTY" means any third party.
-
-
-[ ] B. "THIRD PARTY" means any third party except for any of the following: (a) a wholly owned subsidiary of the Subsequent Contributor in question; (b) a legal entity (the "PARENT") that wholly owns the Subsequent Contributor in question; or (c) a wholly owned subsidiary of the wholly owned subsidiary in (a) or of the Parent in (b).
-
-
-[ ] C. "THIRD PARTY" means any third party except for any of the following: (a) any Person directly or indirectly owning a majority of the voting interest in the Subsequent Contributor or (b) any Person in which the Subsequent Contributor directly or indirectly owns a majority voting interest.
-
-
-[ ] D. "THIRD PARTY" means any third party except for any Person directly or indirectly controlled by the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise.
-
-
-[ ] E. "THIRD PARTY" means any third party except for any Person directly or indirectly controlling, controlled by, or under common control with the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise.
-The default definition of "THIRD PARTY" is the definition set forth in paragraph A, if NONE OR MORE THAN ONE of paragraphs A, B, C, D or E in this Part 4 are selected by the Initial Contributor.
-
-PART 5: NOTICE
-
-THE LICENSED WORK IS PROVIDED UNDER THE TERMS OF THE ADAPTIVE PUBLIC LICENSE ("LICENSE") AS FIRST COMPLETED BY: ______________________ [Insert the name of the Initial Contributor here]. ANY USE, PUBLIC DISPLAY, PUBLIC PERFORMANCE, REPRODUCTION OR DISTRIBUTION OF, OR PREPARATION OF DERIVATIVE WORKS BASED ON, THE LICENSED WORK CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS LICENSE AND ITS TERMS, WHETHER OR NOT SUCH RECIPIENT READS THE TERMS OF THE LICENSE. "LICENSED WORK" AND "RECIPIENT" ARE DEFINED IN THE LICENSE. A COPY OF THE LICENSE IS LOCATED IN THE TEXT FILE ENTITLED "LICENSE.TXT" ACCOMPANYING THE CONTENTS OF THIS FILE. IF A COPY OF THE LICENSE DOES NOT ACCOMPANY THIS FILE, A COPY OF THE LICENSE MAY ALSO BE OBTAINED AT THE FOLLOWING WEB SITE: ___________________________________________________[Insert Initial Contributor's Designated Web Site here]
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-PART 6: PATENT LICENSING TERMS
-
-For the purposes of this License, paragraphs A, B, C, D and E of this Part 6 of Exhibit A are only incorporated and form part of the terms of the License if the Initial Contributor places an "X" or "x" in the selection box alongside the YES answer to the question immediately below.
-
-Is this a Patents-Included License pursuant to Section 2.2 of the License?
-YES [ ]
-NO [ ]
-
-By default, if YES is not selected by the Initial Contributor, the answer is NO.
-
-A. For the purposes of the paragraphs in this Part 6 of Exhibit A, "LICENSABLE" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights granted herein.
-
-B. The Initial Contributor hereby grants all Recipients a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims, under patent claim(s) Licensable by the Initial Contributor that are or would be infringed by the making, using, selling, offering for sale, having made, importing, exporting, transfer or disposal of such Initial Work or any portion thereof. Notwithstanding the foregoing, no patent license is granted under this Paragraph B by the Initial Contributor: (1) for any code that the Initial Contributor deletes from the Initial Work (or any portion thereof) distributed by the Initial Contributor prior to such distribution; (2) for any Modifications made to the Initial Work (or any portion thereof) by any other Person; or (3) separate from the Initial Work (or portions thereof) distributed or made available by the Initial Contributor.
-
-C. Effective upon distribution by a Subsequent Contributor to a Third Party of any Modifications made by that Subsequent Contributor, such Subsequent Contributor hereby grants all Recipients a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims, under patent claim(s) Licensable by such Subsequent Contributor that are or would be infringed by the making, using, selling, offering for sale, having made, importing, exporting, transfer or disposal of any such Modifications made by that Subsequent Contributor alone and/or in combination with its Subsequent Work (or portions of such combination) to make, use, sell, offer for sale, have made, import, export, transfer and otherwise dispose of:
-(1) Modifications made by that Subsequent Contributor (or portions thereof); and
-(2) the combination of Modifications made by that Subsequent Contributor with its Subsequent Work (or portions of such combination);
-(collectively and in each case, the "SUBSEQUENT CONTRIBUTOR VERSION").
-Notwithstanding the foregoing, no patent license is granted under this Paragraph C by such Subsequent Contributor: (1) for any code that such Subsequent Contributor deletes from the Subsequent Contributor Version (or any portion thereof) distributed by the Subsequent Contributor prior to such distribution; (2) for any Modifications made to the Subsequent Contributor Version (or any portion thereof) by any other Person; or (3) separate from the Subsequent Contributor Version (or portions thereof) distributed or made available by the Subsequent Contributor.
-
-D. Effective upon distribution of any Licensed Work by a Distributor to a Third Party, such Distributor hereby grants all Recipients a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims, under patent claim(s) Licensable by such Distributor that are or would be infringed by the making, using, selling, offering for sale, having made, importing, exporting, transfer or disposal of any such Licensed Work distributed by such Distributor, to make, use, sell, offer for sale, have made, import, export, transfer and otherwise dispose of such Licensed Work or portions thereof (collectively and in each case, the "DISTRIBUTOR VERSION"). Notwithstanding the foregoing, no patent license is granted under this Paragraph D by such Distributor: (1) for any code that such Distributor deletes from the Distributor Version (or any portion thereof) distributed by the Distributor prior to such distribution; (2) for any Modifications made to the Distributor Version (or any portion thereof) by any other Person; or (3) separate from the Distributor Version (or portions thereof) distributed or made available by the Distributor.
-
-E. If Recipient institutes patent litigation against another Recipient (a "USER") with respect to a patent applicable to a computer program or software (including a cross-claim or counterclaim in a lawsuit, and whether or not any of the patent claims are directed to a system, method, process, apparatus, device, product, article of manufacture or any other form of patent claim), then any patent or copyright license granted by that User to such Recipient under this License or any other copy of this License shall terminate. The termination shall be effective ninety (90) days after notice of termination from User to Recipient, unless the Recipient withdraws the patent litigation claim before the end of the ninety (90) day period. To be effective, any such notice of license termination must include a specific list of applicable patents and/or a copy of the copyrighted work of User that User alleges will be infringed by Recipient upon License termination. License termination is only effective with respect to patents and/or copyrights for which proper notice has been given.
-
-PART 7: SAMPLE REQUIREMENTS FOR THE DESCRIPTION OF DISTRIBUTED MODIFICATIONS
-
-Each Subsequent Contributor (including the Initial Contributor where the Initial Contributor qualifies as a Subsequent Contributor) is invited (but not required) to cause each Subsequent Work created or contributed to by that Subsequent Contributor to contain a file documenting the changes such Subsequent Contributor made to create that Subsequent Work and the date of any change. //***EXHIBIT A ENDS HERE.***//
diff --git a/options/license/APSL-1.0 b/options/license/APSL-1.0
deleted file mode 100644
index 4ad8d14b7e..0000000000
--- a/options/license/APSL-1.0
+++ /dev/null
@@ -1,109 +0,0 @@
-APPLE PUBLIC SOURCE LICENSE
-Version 1.0 - March 16, 1999
-
-Please read this License carefully before downloading this software. By downloading and using this software, you are agreeing to be bound by the terms of this License. If you do not or cannot agree to the terms of this License, please do not download or use the software.
-
-1. General; Definitions. This License applies to any program or other work which Apple Computer, Inc. ("Apple") publicly announces as subject to this Apple Public Source License and which contains a notice placed by Apple identifying such program or work as "Original Code" and stating that it is subject to the terms of this Apple Public Source License version 1.0 (or subsequent version thereof), as it may be revised from time to time by Apple ("License"). As used in this License:
-
- 1.1 "Applicable Patents" mean: (a) in the case where Apple is the grantor of rights, (i) patents or patent applications that are now or hereafter acquired, owned by or assigned to Apple and (ii) whose claims cover subject matter contained in the Original Code, but only to the extent necessary to use, reproduce and/or distribute the Original Code without infringement; and (b) in the case where You are the grantor of rights, (i) patents and patent applications that are now or hereafter acquired, owned by or assigned to You and (ii) whose claims cover subject matter in Your Modifications, taken alone or in combination with Original Code.
-
- 1.2 "Covered Code" means the Original Code, Modifications, the combination of Original Code and any Modifications, and/or any respective portions thereof.
-
- 1.3 "Deploy" means to use, sublicense or distribute Covered Code other than for Your internal research and development (R&D), and includes without limitation, any and all internal use or distribution of Covered Code within Your business or organization except for R&D use, as well as direct or indirect sublicensing or distribution of Covered Code by You to any third party in any form or manner.
-
- 1.4 "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.5 "Modifications" mean any addition to, deletion from, and/or change to, the substance and/or structure of Covered Code. When code is released as a series of files, a Modification is: (a) any addition to or deletion from the contents of a file containing Covered Code; and/or (b) any new file or other representation of computer program statements that contains any part of Covered Code.
-
- 1.6 "Original Code" means the Source Code of a program or other work as originally made available by Apple under this License, including the Source Code of any updates or upgrades to such programs or works made available by Apple under this License, and that has been expressly identified by Apple as such in the header file(s) of such work.
-
- 1.7 "Source Code" means the human readable form of a program or other work that is suitable for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an executable (object code).
-
- 1.8 "You" or "Your" means an individual or a legal entity exercising rights under this License. For legal entities, "You" or "Your" includes any entity which controls, is controlled by, or is under common control with, You, where "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
-2. Permitted Uses; Conditions & Restrictions. Subject to the terms and conditions of this License, Apple hereby grants You, effective on the date You accept this License and download the Original Code, a world-wide, royalty-free, non-exclusive license, to the extent of Apple's Applicable Patents and copyrights covering the Original Code, to do the following:
-
- 2.1 You may use, copy, modify and distribute Original Code, with or without Modifications, solely for Your internal research and development, provided that You must in each instance:
-
- (a) retain and reproduce in all copies of Original Code the copyright and other proprietary notices and disclaimers of Apple as they appear in the Original Code, and keep intact all notices in the Original Code that refer to this License;
-
- (b) include a copy of this License with every copy of Source Code of Covered Code and documentation You distribute, and You may not offer or impose any terms on such Source Code that alter or restrict this License or the recipients' rights hereunder, except as permitted under Section 6; and
-
- (c) completely and accurately document all Modifications that you have made and the date of each such Modification, designate the version of the Original Code you used, prominently include a file carrying such information with the Modifications, and duplicate the notice in Exhibit A in each file of the Source Code of all such Modifications.
-
- 2.2 You may Deploy Covered Code, provided that You must in each instance:
-
- (a) satisfy all the conditions of Section 2.1 with respect to the Source Code of the Covered Code;
-
- (b) make all Your Deployed Modifications publicly available in Source Code form via electronic distribution (e.g. download from a web site) under the terms of this License and subject to the license grants set forth in Section 3 below, and any additional terms You may choose to offer under Section 6. You must continue to make the Source Code of Your Deployed Modifications available for as long as you Deploy the Covered Code or twelve (12) months from the date of initial Deployment, whichever is longer;
-
- (c) must notify Apple and other third parties of how to obtain Your Deployed Modifications by filling out and submitting the required information found at http://www.apple.com/publicsource/modifications.html; and
-
- (d) if you Deploy Covered Code in object code, executable form only, include a prominent notice, in the code itself as well as in related documentation, stating that Source Code of the Covered Code is available under the terms of this License with information on how and where to obtain such Source Code.
-
-3. Your Grants. In consideration of, and as a condition to, the licenses granted to You under this License:
-
- (a) You hereby grant to Apple and all third parties a non-exclusive, royalty-free license, under Your Applicable Patents and other intellectual property rights owned or controlled by You, to use, reproduce, modify, distribute and Deploy Your Modifications of the same scope and extent as Apple's licenses under Sections 2.1 and 2.2; and
-
- (b) You hereby grant to Apple and its subsidiaries a non-exclusive, worldwide, royalty-free, perpetual and irrevocable license, under Your Applicable Patents and other intellectual property rights owned or controlled by You, to use, reproduce, execute, compile, display, perform, modify or have modified (for Apple and/or its subsidiaries), sublicense and distribute Your Modifications, in any form, through multiple tiers of distribution.
-
-4. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In each such instance, You must make sure the requirements of this License are fulfilled for the Covered Code or any portion thereof.
-
-5. Limitations on Patent License. Except as expressly stated in Section 2, no other patent rights, express or implied, are granted by Apple herein. Modifications and/or Larger Works may require additional patent licenses from Apple which Apple may grant in its sole discretion.
-
-6. Additional Terms. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations and/or other rights consistent with the scope of the license granted herein ("Additional Terms") to one or more recipients of Covered Code. However, You may do so only on Your own behalf and as Your sole responsibility, and not on behalf of Apple. You must obtain the recipient's agreement that any such Additional Terms are offered by You alone, and You hereby agree to indemnify, defend and hold Apple harmless for any liability incurred by or claims asserted against Apple by reason of any such Additional Terms.
-
-7. Versions of the License. Apple may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Once Original Code has been published under a particular version of this License, You may continue to use it under the terms of that version. You may also choose to use such Original Code under the terms of any subsequent version of this License published by Apple. No one other than Apple has the right to modify the terms applicable to Covered Code created under this License.
-
-8. NO WARRANTY OR SUPPORT. The Original Code may contain in whole or in part pre-release, untested, or not fully tested works. The Original Code may contain errors that could cause failures or loss of data, and may be incomplete or contain inaccuracies. You expressly acknowledge and agree that use of the Original Code, or any portion thereof, is at Your sole and entire risk. THE ORIGINAL CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND APPLE'S LICENSOR(S) (FOR THE PURPOSES OF SECTIONS 8 AND 9, APPLE AND APPLE'S LICENSOR(S) ARE COLLECTIVELY REFERRED TO AS "APPLE") EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY OR SATISFACTORY QUALITY AND FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. APPLE DOES NOT WARRANT THAT THE FUNCTIONS CONTAINED IN THE ORIGINAL CODE WILL MEET YOUR REQUIREMENTS, OR THAT THE OPERATION OF THE ORIGINAL CODE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT DEFECTS IN THE ORIGINAL CODE WILL BE CORRECTED. NO ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE OR AN APPLE AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY OR IN ANY WAY INCREASE THE SCOPE OF THIS WARRANTY. You acknowledge that the Original Code is not intended for use in the operation of nuclear facilities, aircraft navigation, communication systems, or air traffic control machines in which case the failure of the Original Code could lead to death, personal injury, or severe physical or environmental damage.
-
-9. Liability.
-
- 9.1 Infringement. If any of the Original Code becomes the subject ofa claim of infringement ("Affected Original Code"), Apple may, at its sole discretion and option: (a) attempt to procure the rights necessary for You to continue using the Affected Original Code; (b) modify the Affected Original Code so that it is no longer infringing; or (c) terminate Your rights to use the Affected Original Code, effective immediately upon Apple's posting of a notice to such effect on the Apple web site that is used for implementation of this License.
-
- 9.2 LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES SHALL APPLE BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE ORIGINAL CODE, OR ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY REMEDY. In no event shall Apple's total liability to You for all damages under this License exceed the amount of fifty dollars ($50.00).
-
-10. Trademarks. This License does not grant any rights to use the trademarks or trade names "Apple", "Apple Computer", "Mac OS X", "Mac OS X Server" or any other trademarks or trade names belonging to Apple (collectively "Apple Marks") and no Apple Marks may be used to endorse or promote products derived from the Original Code
-other than as permitted by and in strict compliance at all times with Apple's third party trademark usage guidelines which are posted at http://www.apple.com/legal/guidelinesfor3rdparties.html.
-
-11. Ownership. Apple retains all rights, title and interest in and to the Original Code and any Modifications made by or on behalf of Apple ("Apple Modifications"), and such Apple Modifications will not be automatically subject to this License. Apple may, at its sole discretion, choose to license such Apple Modifications under this License, or on different terms from those contained in this License or may choose not to license them at all. Apple's development, use, reproduction, modification, sublicensing and distribution of Covered Code will not be subject to this License.
-
-12. Termination.
-
- 12.1 Termination. This License and the rights granted hereunder will terminate:
-
- (a) automatically without notice from Apple if You fail to comply with any term(s) of this License and fail to cure such breach within 30 days of becoming aware of such breach;
-
- (b) immediately in the event of the circumstances described in Sections 9.1 and/or 13.6(b); or
-
- (c) automatically without notice from Apple if You, at any time during the term of this License, commence an action for patent infringement against Apple.
-
- 12.2 Effect of Termination. Upon termination, You agree to immediately stop any further use, reproduction, modification and distribution of the Covered Code, or Affected Original Code in the case of termination under Section 9.1, and to destroy all copies of the Covered Code or Affected Original Code (in the case of
-termination under Section 9.1) that are in your possession or control. All sublicenses to the Covered Code which have been properly granted prior to termination shall survive any termination of this License. Provisions which, by their nature, should remain in effect beyond the termination of this License shall survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. Neither party will be liable to the other for compensation, indemnity or damages of any sort solely as a result of terminating this License in accordance with its terms, and termination of this License will be without prejudice to any other right or remedy of either party.
-
-13. Miscellaneous.
-
- 13.1 Export Law Assurances. You may not use or otherwise export or re-export the Original Code except as authorized by United States law and the laws of the jurisdiction in which the Original Code was obtained. In particular, but without limitation, the Original Code may not be exported or re-exported (a) into (or to a national or resident of) any U.S. embargoed country or (b) to anyone on the U.S. Treasury Department's list of Specially Designated Nationals or the U.S. Department of Commerce's Table of Denial Orders. By using the Original Code, You represent and warrant that You are not located in, under control of, or a national or resident of any such country or on any such list.
-
- 13.2 Government End Users. The Covered Code is a "commercial item" as defined in FAR 2.101. Government software and technical data rights in the Covered Code include only those rights customarily provided to the public as defined in this License. This customary commercial license in technical data and software is provided in
-accordance with FAR 12.211 (Technical Data) and 12.212 (Computer Software) and, for Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 (Rights in Commercial Computer Software or Computer Software Documentation). Accordingly, all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
- 13.3 Relationship of Parties. This License will not be construed as creating an agency, partnership, joint venture or any other form of legal association between You and Apple, and You will not represent to the contrary, whether expressly, by implication, appearance or otherwise.
-
- 13.4 Independent Development. Nothing in this License will impair Apple's right to acquire, license, develop, have others develop for it, market and/or distribute technology or products that perform the same or similar functions as, or otherwise compete with, Modifications, Larger Works, technology or products that You may develop, produce, market or distribute.
-
- 13.5 Waiver; Construction. Failure by Apple to enforce any provision of this License will not be deemed a waiver of future enforcement of that or any other provision. Any law or regulation which provides that the language of a contract shall be construed against the drafter will not apply to this License.
-
- 13.6 Severability. (a) If for any reason a court of competent jurisdiction finds any provision of this License, or portion thereof, to be unenforceable, that provision of the License will be enforced to the maximum extent permissible so as to effect the economic benefits and intent of the parties, and the remainder of this License will continue in full force and effect. (b) Notwithstanding the foregoing, if applicable law prohibits or restricts You from fully and/or specifically complying with Sections 2 and/or 3 or prevents the enforceability of either of those Sections, this License will immediately terminate and You must immediately discontinue any use of the Covered Code and destroy all copies of it that are in your possession or control.
-
- 13.7 Dispute Resolution. Any litigation or other dispute resolution between You and Apple relating to this License shall take place in the Northern District of California, and You and Apple hereby consent to the personal jurisdiction of, and venue in, the state and federal courts within that District with respect to this License. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded.
-
- 13.8 Entire Agreement; Governing Law. This License constitutes the entire agreement between the parties with respect to the subject matter hereof. This License shall be governed by the laws of the United States and the State of California, except that body of California law concerning conflicts of law.
-
- Where You are located in the province of Quebec, Canada, the following clause applies: The parties hereby confirm that they have requested that this License and all related documents be drafted in English. Les parties ont exige que le present contrat et tous les documents connexes soient rediges en anglais.
-
-EXHIBIT A.
-
-"Portions Copyright (c) 1999 Apple Computer, Inc. All Rights Reserved. This file contains Original Code and/or Modifications of Original Code as defined in and that are subject to the Apple Public Source License Version 1.0 (the 'License'). You may not use this file except in compliance with the License. Please obtain a copy of the License at http://www.apple.com/publicsource and read it before using this file.
-
-The Original Code and all software distributed under the License are distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the License for the specific language governing rights and limitations under the License."
diff --git a/options/license/APSL-1.1 b/options/license/APSL-1.1
deleted file mode 100644
index f85f9b3aaa..0000000000
--- a/options/license/APSL-1.1
+++ /dev/null
@@ -1,108 +0,0 @@
-APPLE PUBLIC SOURCE LICENSE
-Version 1.1 - April 19,1999
-
-Please read this License carefully before downloading this software.
-By downloading and using this software, you are agreeing to be bound by the terms of this License. If you do not or cannot agree to the terms of this License, please do not download or use the software.
-
-1. General; Definitions. This License applies to any program or other work which Apple Computer, Inc. ("Apple") publicly announces as subject to this Apple Public Source License and which contains a notice placed by Apple identifying such program or work as "Original Code" and stating that it is subject to the terms of this Apple Public Source License version 1.1 (or subsequent version thereof), as it may be revised from time to time by Apple ("License"). As used in this License:
-
- 1.1 "Affected Original Code" means only those specific portions of Original Code that allegedly infringe upon any party's intellectual property rights or are otherwise the subject of a claim of infringement.
-
- 1.2 "Applicable Patent Rights" mean: (a) in the case where Apple is the grantor of rights, (i) claims of patents that are now or hereafter acquired, owned by or assigned to Apple and (ii) that cover subject matter contained in the Original Code, but only to the extent necessary to use, reproduce and/or distribute the Original Code without infringement; and (b) in the case where You are the grantor of rights, (i) claims of patents that are now or hereafter acquired, owned by or assigned to You and (ii) that cover subject matter in Your Modifications, taken alone or in combination with Original Code.
-
- 1.3 "Covered Code" means the Original Code, Modifications, the combination of Original Code and any Modifications, and/or any respective portions thereof.
-
- 1.4 "Deploy" means to use, sublicense or distribute Covered Code other than for Your internal research and development (R&D), and includes without limitation, any and all internal use or distribution of Covered Code within Your business or organization except for R&D use, as well as direct or indirect sublicensing or distribution of Covered Code by You to any third party in any form or manner.
-
- 1.5 "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.6 "Modifications" mean any addition to, deletion from, and/or change to, the substance and/or structure of Covered Code. When code is released as a series of files, a Modification is: (a) any addition to or deletion from the contents of a file containing Covered Code; and/or (b) any new file or other representation of computer program statements that contains any part of Covered Code.
-
- 1.7 "Original Code" means (a) the Source Code of a program or other work as originally made available by Apple under this License, including the Source Code of any updates or upgrades to such programs or works made available by Apple under this License, and that has been expressly identified by Apple as such in the header file(s) of such work; and (b) the object code compiled from such Source Code and originally made available by Apple under this License.
-
- 1.8 "Source Code" means the human readable form of a program or other work that is suitable for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an executable (object code).
-
- 1.9 "You" or "Your" means an individual or a legal entity exercising rights under this License. For legal entities, "You" or "Your" includes any entity which controls, is controlled by, or is under common control with, You, where "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
-2. Permitted Uses; Conditions & Restrictions. Subject to the terms and conditions of this License, Apple hereby grants You, effective on the date You accept this License and download the Original Code, a world-wide, royalty-free, non- exclusive license, to the extent of Apple's Applicable Patent Rights and copyrights covering the Original Code, to do the following:
-
- 2.1 You may use, copy, modify and distribute Original Code, with or without Modifications, solely for Your internal research and development, provided that You must in each instance:
-
- (a) retain and reproduce in all copies of Original Code the copyright and other proprietary notices and disclaimers of Apple as they appear in the Original Code, and keep intact all notices in the Original Code that refer to this License;
-
- (b) include a copy of this License with every copy of Source Code of Covered Code and documentation You distribute, and You may not offer or impose any terms on such Source Code that alter or restrict this License or the recipients' rights hereunder, except as permitted under Section 6; and
-
- (c) completely and accurately document all Modifications that you have made and the date of each such Modification, designate the version of the Original Code you used, prominently include a file carrying such information with the Modifications, and duplicate the notice in Exhibit A in each file of the Source Code of all such Modifications.
-
- 2.2 You may Deploy Covered Code, provided that You must in each instance:
-
- (a) satisfy all the conditions of Section 2.1 with respect to the Source Code of the Covered Code;
-
- (b) make all Your Deployed Modifications publicly available in Source Code form via electronic distribution (e.g. download from a web site) under the terms of this License and subject to the license grants set forth in Section 3 below, and any additional terms You may choose to offer under Section 6. You must continue to make the Source Code of Your Deployed Modifications available for as long as you Deploy the Covered Code or twelve (12) months from the date of initial Deployment, whichever is longer;
-
- (c) if You Deploy Covered Code containing Modifications made by You, inform others of how to obtain those Modifications by filling out and submitting the information found at http://www.apple.com/publicsource/modifications.html, if available; and
-
- (d) if You Deploy Covered Code in object code, executable form only, include a prominent notice, in the code itself as well as in related documentation, stating that Source Code of the Covered Code is available under the terms of this License with information on how and where to obtain such Source Code.
-
-3. Your Grants. In consideration of, and as a condition to, the licenses granted to You under this License:
-
- (a) You hereby grant to Apple and all third parties a non-exclusive, royalty-free license, under Your Applicable Patent Rights and other intellectual property rights owned or controlled by You, to use, reproduce, modify, distribute and Deploy Your Modifications of the same scope and extent as Apple's licenses under Sections 2.1 and 2.2; and
-
- (b) You hereby grant to Apple and its subsidiaries a non-exclusive, worldwide, royalty-free, perpetual and irrevocable license, under Your Applicable Patent Rights and other intellectual property rights owned or controlled by You, to use, reproduce, execute, compile, display, perform, modify or have modified (for Apple and/or its subsidiaries), sublicense and distribute Your Modifications, in any form, through multiple tiers of distribution.
-
-4. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In each such instance, You must make sure the requirements of this License are fulfilled for the Covered Code or any portion thereof.
-
-5. Limitations on Patent License. Except as expressly stated in Section 2, no other patent rights, express or implied, are granted by Apple herein. Modifications and/or Larger Works may require additional patent licenses from Apple which Apple may grant in its sole discretion.
-
-6. Additional Terms. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations and/or other rights consistent with the scope of the license granted herein ("Additional Terms") to one or more recipients of Covered Code. However, You may do so only on Your own behalf and as Your sole responsibility, and not on behalf of Apple. You must obtain the recipient's agreement that any such Additional Terms are offered by You alone, and You hereby agree to indemnify, defend and hold Apple harmless for any liability incurred by or claims asserted against Apple by reason of any such Additional Terms.
-
-7. Versions of the License. Apple may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Once Original Code has been published under a particular version of this License, You may continue to use it under the terms of that version. You may also choose to use such Original Code under the terms of any subsequent version of this License published by Apple. No one other than Apple has the right to modify the terms applicable to Covered Code created under this License.
-
-8. NO WARRANTY OR SUPPORT. The Original Code may contain in whole or in part pre-release, untested, or not fully tested works. The Original Code may contain errors that could cause failures or loss of data, and may be incomplete or contain inaccuracies. You expressly acknowledge and agree that use of the Original Code, or any portion thereof, is at Your sole and entire risk. THE ORIGINAL CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND APPLE'S LICENSOR(S) (FOR THE PURPOSES OF SECTIONS 8 AND 9, APPLE AND APPLE'S LICENSOR(S) ARE COLLECTIVELY REFERRED TO AS "APPLE") EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY OR SATISFACTORY QUALITY AND FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. APPLE DOES NOT WARRANT THAT THE FUNCTIONS CONTAINED IN THE ORIGINAL CODE WILL MEET YOUR REQUIREMENTS, OR THAT THE OPERATION OF THE ORIGINAL CODE WILL BE UNINTERRUPTED OR ERROR- FREE, OR THAT DEFECTS IN THE ORIGINAL CODE WILL BE CORRECTED. NO ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE OR AN APPLE AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY OR IN ANY WAY INCREASE THE SCOPE OF THIS WARRANTY. You acknowledge that the Original Code is not intended for use in the operation of nuclear facilities, aircraft navigation, communication systems, or air traffic control machines in which case the failure of the Original Code could lead to death, personal injury, or severe physical or environmental damage.
-
-9. Liability.
-
- 9.1 Infringement. If any portion of, or functionality implemented by, the Original Code becomes the subject of a claim of infringement, Apple may, at its option: (a) attempt to procure the rights necessary for Apple and You to continue using the Affected Original Code; (b) modify the Affected Original Code so that it is no longer infringing; or (c) suspend Your rights to use, reproduce, modify, sublicense and distribute the Affected Original Code until a final determination of the claim is made by a court or governmental administrative agency of competent jurisdiction and Apple lifts the suspension as set forth below. Such suspension of rights will be effective immediately upon Apple's posting of a notice to such effect on the Apple web site that is used for implementation of this License. Upon such final determination being made, if Apple is legally able, without the payment of a fee or royalty, to resume use, reproduction, modification, sublicensing and distribution of the Affected Original Code, Apple will lift the suspension of rights to the Affected Original Code by posting a notice to such effect on the Apple web site that is used for implementation of this License. If Apple suspends Your rights to Affected Original Code, nothing in this License shall be construed to restrict You, at Your option and subject to applicable law, from replacing the Affected Original Code with non-infringing code or independently negotiating for necessary rights from such third party.
-
- 9.2 LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES SHALL APPLE BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE ORIGINAL CODE, OR ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY REMEDY. In no event shall Apple's total liability to You for all damages under this License exceed the amount of fifty dollars ($50.00).
-
-10. Trademarks. This License does not grant any rights to use the trademarks or trade names "Apple", "Apple Computer", "Mac OS X", "Mac OS X Server" or any other trademarks or trade names belonging to Apple (collectively "Apple Marks") and no Apple Marks may be used to endorse or promote products derived from the Original Code other than as permitted by and in strict compliance at all times with Apple's third party trademark usage guidelines which are posted at http://www.apple.com/legal/guidelinesfor3rdparties.html.
-
-11. Ownership. Apple retains all rights, title and interest in and to the Original Code and any Modifications made by or on behalf of Apple ("Apple Modifications"), and such Apple Modifications will not be automatically subject to this License. Apple may, at its sole discretion, choose to license such Apple Modifications under this License, or on different terms from those contained in this License or may choose not to license them at all. Apple's development, use, reproduction, modification, sublicensing and distribution of Covered Code will not be subject to this License.
-
-12. Termination.
-
- 12.1 Termination. This License and the rights granted hereunder will terminate:
-
- (a) automatically without notice from Apple if You fail to comply with any term(s) of this License and fail to cure such breach within 30 days of becoming aware of such breach;
-
- (b) immediately in the event of the circumstances described in Section 13.5(b); or
-
- (c) automatically without notice from Apple if You, at any time during the term of this License, commence an action for patent infringement against Apple.
-
- 12.2 Effect of Termination. Upon termination, You agree to immediately stop any further use, reproduction, modification, sublicensing and distribution of the Covered Code and to destroy all copies of the Covered Code that are in your possession or control. All sublicenses to the Covered Code which have been properly granted prior to termination shall survive any termination of this License. Provisions which, by their nature, should remain in effect beyond the termination of this License shall survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. Neither party will be liable to the other for compensation, indemnity or damages of any sort solely as a result of terminating this License in accordance with its terms, and termination of this License will be without prejudice to any other right or remedy of either party.
-
-13. Miscellaneous.
-
- 13.1 Government End Users. The Covered Code is a "commercial item" as defined in FAR 2.101. Government software and technical data rights in the Covered Code include only those rights customarily provided to the public as defined in this License. This customary commercial license in technical data and software is provided in accordance with FAR 12.211 (Technical Data) and 12.212 (Computer Software) and, for Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 (Rights in Commercial Computer Software or Computer Software Documentation). Accordingly, all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
- 13.2 Relationship of Parties. This License will not be construed as creating an agency, partnership, joint venture or any other form of legal association between You and Apple, and You will not represent to the contrary, whether expressly, by implication, appearance or otherwise.
-
- 13.3 Independent Development. Nothing in this License will impair Apple's right to acquire, license, develop, have others develop for it, market and/or distribute technology or products that perform the same or similar functions as, or otherwise compete with, Modifications, Larger Works, technology or products that You may
-develop, produce, market or distribute.
-
- 13.4 Waiver; Construction. Failure by Apple to enforce any provision of this License will not be deemed a waiver of future enforcement of that or any other provision. Any law or regulation which provides that the language of a contract shall be construed against the drafter will not apply to this License.
-
- 13.5 Severability. (a) If for any reason a court of competent jurisdiction finds any provision of this License, or portion thereof, to be unenforceable, that provision of the License will be enforced to the maximum extent permissible so as to effect the economic benefits and intent of the parties, and the remainder of this License will continue in full force and effect. (b) Notwithstanding the foregoing, if applicable law prohibits or restricts You from fully and/or specifically complying with Sections 2 and/or 3 or prevents the enforceability of either of those Sections, this License will immediately terminate and You must immediately discontinue any use of the Covered Code and destroy all copies of it that are in your possession or control.
-
- 13.6 Dispute Resolution. Any litigation or other dispute resolution between You and Apple relating to this License shall take place in the Northern District of California, and You and Apple hereby consent to the personal jurisdiction of, and venue in, the state and federal courts within that District with respect to this License. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded.
-
- 13.7 Entire Agreement; Governing Law. This License constitutes the entire agreement between the parties with respect to the subject matter hereof. This License shall be governed by the laws of the United States and the State of California, except that body of California law concerning conflicts of law.
-
- Where You are located in the province of Quebec, Canada, the following clause applies: The parties hereby confirm that they have requested that this License and all related documents be drafted in English. Les parties ont exige que le present contrat et tous les documents connexes soient rediges en anglais.
-
-EXHIBIT A.
-
-"Portions Copyright (c) 1999-2000 Apple Computer, Inc. All Rights Reserved. This file contains Original Code and/or Modifications of Original Code as defined in and that are subject to the Apple Public Source License Version 1.1 (the "License"). You may not use this file except in compliance with the License. Please obtain a copy of the License at http://www.apple.com/publicsource and read it before using this file.
-
-The Original Code and all software distributed under the License are distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the License for the specific language governing rights and limitations under the License."
diff --git a/options/license/APSL-1.2 b/options/license/APSL-1.2
deleted file mode 100644
index f0c8c3ae97..0000000000
--- a/options/license/APSL-1.2
+++ /dev/null
@@ -1,103 +0,0 @@
-Apple Public Source License Ver. 1.2
-
-1. General; Definitions. This License applies to any program or other work which Apple Computer, Inc. ("Apple") makes publicly available and which contains a notice placed by Apple identifying such program or work as "Original Code" and stating that it is subject to the terms of this Apple Public Source License version 1.2 (or subsequent version thereof) ("License"). As used in this License:
-
- 1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is the grantor of rights, (i) claims of patents that are now or hereafter acquired, owned by or assigned to Apple and (ii) that cover subject matter contained in the Original Code, but only to the extent necessary to use, reproduce and/or distribute the Original Code without infringement; and (b) in the case where You are the grantor of rights, (i) claims of patents that are now or hereafter acquired, owned by or assigned to You and (ii) that cover subject matter in Your Modifications, taken alone or in combination with Original Code.
-
- 1.2 "Contributor" means any person or entity that creates or contributes to the creation of Modifications.
-
- 1.3 "Covered Code" means the Original Code, Modifications, the combination of Original Code and any Modifications, and/or any respective portions thereof.
-
- 1.4 "Deploy" means to use, sublicense or distribute Covered Code other than for Your internal research and development (R&D) and/or Personal Use, and includes without limitation, any and all internal use or distribution of Covered Code within Your business or organization except for R&D use and/or Personal Use, as well as direct or indirect sublicensing or distribution of Covered Code by You to any third party in any form or manner.
-
- 1.5 "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.6 "Modifications" mean any addition to, deletion from, and/or change to, the substance and/or structure of the Original Code, any previous Modifications, the combination of Original Code and any previous Modifications, and/or any respective portions thereof. When code is released as a series of files, a Modification is: (a) any addition to or deletion from the contents of a file containing Covered Code; and/or (b) any new file or other representation of computer program statements that contains any part of Covered Code.
-
- 1.7 "Original Code" means (a) the Source Code of a program or other work as originally made available by Apple under this License, including the Source Code of any updates or upgrades to such programs or works made available by Apple under this License, and that has been expressly identified by Apple as such in the header file(s) of such work; and (b) the object code compiled from such Source Code and originally made available by Apple under this License.
-
- 1.8 "Personal Use" means use of Covered Code by an individual solely for his or her personal, private and non-commercial purposes. An individual's use of Covered Code in his or her capacity as an officer, employee, member, independent contractor or agent of a corporation, business or organization (commercial or non-commercial) does not qualify as Personal Use.
-
- 1.9 "Source Code" means the human readable form of a program or other work that is suitable for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an executable (object code).
-
- 1.10 "You" or "Your" means an individual or a legal entity exercising rights under this License. For legal entities, "You" or "Your" includes any entity which controls, is controlled by, or is under common control with, You, where "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
-2. Permitted Uses; Conditions & Restrictions.Subject to the terms and conditions of this License, Apple hereby grants You, effective on the date You accept this License and download the Original Code, a world-wide, royalty-free, non-exclusive license, to the extent of Apple's Applicable Patent Rights and copyrights covering the Original Code, to do the following:
-
- 2.1 You may use, reproduce, display, perform, modify and distribute Original Code, with or without Modifications, solely for Your internal research and development and/or Personal Use, provided that in each instance:
-
- (a) You must retain and reproduce in all copies of Original Code the copyright and other proprietary notices and disclaimers of Apple as they appear in the Original Code, and keep intact all notices in the Original Code that refer to this License; and
-
- (b) You must include a copy of this License with every copy of Source Code of Covered Code and documentation You distribute, and You may not offer or impose any terms on such Source Code that alter or restrict this License or the recipients' rights hereunder, except as permitted under Section 6.
-
- 2.2 You may use, reproduce, display, perform, modify and Deploy Covered Code, provided that in each instance:
-
- (a) You must satisfy all the conditions of Section 2.1 with respect to the Source Code of the Covered Code;
-
- (b) You must duplicate, to the extent it does not already exist, the notice in Exhibit A in each file of the Source Code of all Your Modifications, and cause the modified files to carry prominent notices stating that You changed the files and the date of any change;
-
- (c) You must make Source Code of all Your Deployed Modifications publicly available under the terms of this License, including the license grants set forth in Section 3 below, for as long as you Deploy the Covered Code or twelve (12) months from the date of initial Deployment, whichever is longer. You should preferably distribute the Source Code of Your Deployed Modifications electronically (e.g. download from a web site); and
-
- (d) if You Deploy Covered Code in object code, executable form only, You must include a prominent notice, in the code itself as well as in related documentation, stating that Source Code of the Covered Code is available under the terms of this License with information on how and where to obtain such Source Code.
-
- 2.3 You expressly acknowledge and agree that although Apple and each Contributor grants the licenses to their respective portions of the Covered Code set forth herein, no assurances are provided by Apple or any Contributor that the Covered Code does not infringe the patent or other intellectual property rights of any other entity. Apple and each Contributor disclaim any liability to You for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, You hereby assume sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow You to distribute the Covered Code, it is Your responsibility to acquire that license before distributing the Covered Code.
-
-3. Your Grants. In consideration of, and as a condition to, the licenses granted to You under this License:
-
- (a) You hereby grant to Apple and all third parties a non-exclusive, royalty-free license, under Your Applicable Patent Rights and other intellectual property rights (other than patent) owned or controlled by You, to use, reproduce, display, perform, modify, distribute and Deploy Your Modifications of the same scope and extent as Apple's licenses under Sections 2.1 and 2.2; and
-
- (b) You hereby grant to Apple and its subsidiaries a non-exclusive, worldwide, royalty-free, perpetual and irrevocable license, under Your Applicable Patent Rights and other intellectual property rights (other than patent) owned or controlled by You, to use, reproduce, display, perform, modify or have modified (for Apple and/or its subsidiaries), sublicense and distribute Your Modifications, in any form, through multiple tiers of distribution.
-
-4. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In each such instance, You must make sure the requirements of this License are fulfilled for the Covered Code or any portion thereof.
-
-5. Limitations on Patent License. Except as expressly stated in Section 2, no other patent rights, express or implied, are granted by Apple herein. Modifications and/or Larger Works may require additional patent licenses from Apple which Apple may grant in its sole discretion.
-
-6. Additional Terms. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations and/or other rights consistent with the scope of the license granted herein ("Additional Terms") to one or more recipients of Covered Code. However, You may do so only on Your own behalf and as Your sole responsibility, and not on behalf of Apple or any Contributor. You must obtain the recipient's agreement that any such Additional Terms are offered by You alone, and You hereby agree to indemnify, defend and hold Apple and every Contributor harmless for any liability incurred by or claims asserted against Apple or such Contributor by reason of any such Additional Terms.
-
-7. Versions of the License. Apple may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Once Original Code has been published under a particular version of this License, You may continue to use it under the terms of that version. You may also choose to use such Original Code under the terms of any subsequent version of this License published by Apple. No one other than Apple has the right to modify the terms applicable to Covered Code created under this License.
-
-8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in part pre-release, untested, or not fully tested works. The Covered Code may contain errors that could cause failures or loss of data, and may be incomplete or contain inaccuracies. You expressly acknowledge and agree that use of the Covered Code, or any portion thereof, is at Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. You acknowledge that the Covered Code is not intended for use in the operation of nuclear facilities, aircraft navigation, communication systems, or air traffic control machines in which case the failure of the Covered Code could lead to death, personal injury, or severe physical or environmental damage.
-
-9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU. In no event shall Apple's total liability to You for all damages (other than as may be required by applicable law) under this License exceed the amount of fifty dollars ($50.00).
-
-10. Trademarks. This License does not grant any rights to use the trademarks or trade names "Apple", "Apple Computer", "Mac OS X", "Mac OS X Server", "QuickTime", "QuickTime Streaming Server" or any other trademarks or trade names belonging to Apple (collectively "Apple Marks") or to any trademark or trade name belonging to any Contributor. No Apple Marks may be used to endorse or promote products derived from the Original Code other than as permitted by and in strict compliance at all times with Apple's third party trademark usage guidelines which are posted at http://www.apple.com/legal/guidelinesfor3rdparties.html.
-
-11. Ownership. Subject to the licenses granted under this License, each Contributor retains all rights, title and interest in and to any Modifications made by such Contributor. Apple retains all rights, title and interest in and to the Original Code and any Modifications made by or on behalf of Apple ("Apple Modifications"), and such Apple Modifications will not be automatically subject to this License. Apple may, at its sole discretion, choose to license such Apple Modifications under this License, or on different terms from those contained in this License or may choose not to license them at all.
-
-12. Termination.
-
- 12.1 Termination. This License and the rights granted hereunder will terminate:
-
- (a) automatically without notice from Apple if You fail to comply with any term(s) of this License and fail to cure such breach within 30 days of becoming aware of such breach;
-
- (b) immediately in the event of the circumstances described in Section 13.5(b); or
-
- (c) automatically without notice from Apple if You, at any time during the term of this License, commence an action for patent infringement against Apple.
-
- 12.2 Effect of Termination. Upon termination, You agree to immediately stop any further use, reproduction, modification, sublicensing and distribution of the Covered Code and to destroy all copies of the Covered Code that are in your possession or control. All sublicenses to the Covered Code which have been properly granted prior to termination shall survive any termination of this License. Provisions which, by their nature, should remain in effect beyond the termination of this License shall survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. No party will be liable to any other for compensation, indemnity or damages of any sort solely as a result of terminating this License in accordance with its terms, and termination of this License will be without prejudice to any other right or remedy of any party.
-
-13. Miscellaneous.
-
- 13.1 Government End Users. The Covered Code is a "commercial item" as defined in FAR 2.101. Government software and technical data rights in the Covered Code include only those rights customarily provided to the public as defined in this License. This customary commercial license in technical data and software is provided in accordance with FAR 12.211 (Technical Data) and 12.212 (Computer Software) and, for Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 (Rights in Commercial Computer Software or Computer Software Documentation). Accordingly, all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
- 13.2 Relationship of Parties. This License will not be construed as creating an agency, partnership, joint venture or any other form of legal association between or amongYou, Apple or any Contributor, and You will not represent to the contrary, whether expressly, by implication, appearance or otherwise.
-
- 13.3 Independent Development. Nothing in this License will impair Apple's right to acquire, license, develop, have others develop for it, market and/or distribute technology or products that perform the same or similar functions as, or otherwise compete with, Modifications, Larger Works, technology or products that You may develop, produce, market or distribute.
-
- 13.4 Waiver; Construction. Failure by Apple or any Contributor to enforce any provision of this License will not be deemed a waiver of future enforcement of that or any other provision. Any law or regulation which provides that the language of a contract shall be construed against the drafter will not apply to this License.
-
- 13.5 Severability. (a) If for any reason a court of competent jurisdiction finds any provision of this License, or portion thereof, to be unenforceable, that provision of the License will be enforced to the maximum extent permissible so as to effect the economic benefits and intent of the parties, and the remainder of this License will continue in full force and effect. (b) Notwithstanding the foregoing, if applicable law prohibits or restricts You from fully and/or specifically complying with Sections 2 and/or 3 or prevents the enforceability of either of those Sections, this License will immediately terminate and You must immediately discontinue any use of the Covered Code and destroy all copies of it that are in your possession or control.
-
- 13.6 Dispute Resolution. Any litigation or other dispute resolution between You and Apple relating to this License shall take place in the Northern District of California, and You and Apple hereby consent to the personal jurisdiction of, and venue in, the state and federal courts within that District with respect to this License. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded.
-
- 13.7 Entire Agreement; Governing Law. This License constitutes the entire agreement between the parties with respect to the subject matter hereof. This License shall be governed by the laws of the United States and the State of California, except that body of California law concerning conflicts of law.
-
- Where You are located in the province of Quebec, Canada, the following clause applies: The parties hereby confirm that they have requested that this License and all related documents be drafted in English. Les parties ont exigé que le présent contrat et tous les documents connexes soient rédigés en anglais.
-
-EXHIBIT A.
-
-"Portions Copyright (c) 1999-2001 Apple Computer, Inc. All Rights Reserved.
-
-This file contains Original Code and/or Modifications of Original Code as defined in and that are subject to the Apple Public Source License Version 1.2 (the 'License'). You may not use this file except in compliance with the License. Please obtain a copy of the License at http://www.apple.com/publicsource and read it before using this file.
-
-The Original Code and all software distributed under the License are distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the specific language governing rights and limitations under the License."
diff --git a/options/license/APSL-2.0 b/options/license/APSL-2.0
deleted file mode 100644
index 2d2d2cf9ba..0000000000
--- a/options/license/APSL-2.0
+++ /dev/null
@@ -1,102 +0,0 @@
-APPLE PUBLIC SOURCE LICENSE
-Version 2.0 - August 6, 2003
-
-Please read this License carefully before downloading this software. By downloading or using this software, you are agreeing to be bound by the terms of this License. If you do not or cannot agree to the terms of this License, please do not download or use the software.
-
-Apple Note: In January 2007, Apple changed its corporate name from "Apple Computer, Inc." to "Apple Inc." This change has been reflected below and copyright years updated, but no other changes have been made to the APSL 2.0.
-
-1. General; Definitions. This License applies to any program or other work which Apple Inc. ("Apple") makes publicly available and which contains a notice placed by Apple identifying such program or work as "Original Code" and stating that it is subject to the terms of this Apple Public Source License version 2.0 ("License"). As used in this License:
-
- 1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is the grantor of rights, (i) claims of patents that are now or hereafter acquired, owned by or assigned to Apple and (ii) that cover subject matter contained in the Original Code, but only to the extent necessary to use, reproduce and/or distribute the Original Code without infringement; and (b) in the case where You are the grantor of rights, (i) claims of patents that are now or hereafter acquired, owned by or assigned to You and (ii) that cover subject matter in Your Modifications, taken alone or in combination with Original Code.
-
- 1.2 "Contributor" means any person or entity that creates or contributes to the creation of Modifications.
-
- 1.3 "Covered Code" means the Original Code, Modifications, the combination of Original Code and any Modifications, and/or any respective portions thereof.
-
- 1.4 "Externally Deploy" means: (a) to sublicense, distribute or otherwise make Covered Code available, directly or indirectly, to anyone other than You; and/or (b) to use Covered Code, alone or as part of a Larger Work, in any way to provide a service, including but not limited to delivery of content, through electronic communication with a client other than You.
-
- 1.5 "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.6 "Modifications" mean any addition to, deletion from, and/or change to, the substance and/or structure of the Original Code, any previous Modifications, the combination of Original Code and any previous Modifications, and/or any respective portions thereof. When code is released as a series of files, a Modification is: (a) any addition to or deletion from the contents of a file containing Covered Code; and/or (b) any new file or other representation of computer program statements that contains any part of Covered Code.
-
- 1.7 "Original Code" means (a) the Source Code of a program or other work as originally made available by Apple under this License, including the Source Code of any updates or upgrades to such programs or works made available by Apple under this License, and that has been expressly identified by Apple as such in the header file(s) of such work; and (b) the object code compiled from such Source Code and originally made available by Apple under this License
-
- 1.8 "Source Code" means the human readable form of a program or other work that is suitable for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an executable (object code).
-
- 1.9 "You" or "Your" means an individual or a legal entity exercising rights under this License. For legal entities, "You" or "Your" includes any entity which controls, is controlled by, or is under common control with, You, where "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
-2. Permitted Uses; Conditions & Restrictions. Subject to the terms and conditions of this License, Apple hereby grants You, effective on the date You accept this License and download the Original Code, a world-wide, royalty-free, non-exclusive license, to the extent of Apple's Applicable Patent Rights and copyrights covering the Original Code, to do the following:
-
- 2.1 Unmodified Code. You may use, reproduce, display, perform, internally distribute within Your organization, and Externally Deploy verbatim, unmodified copies of the Original Code, for commercial or non-commercial purposes, provided that in each instance:
-
- (a) You must retain and reproduce in all copies of Original Code the copyright and other proprietary notices and disclaimers of Apple as they appear in the Original Code, and keep intact all notices in the Original Code that refer to this License; and
-
- (b) You must include a copy of this License with every copy of Source Code of Covered Code and documentation You distribute or Externally Deploy, and You may not offer or impose any terms on such Source Code that alter or restrict this License or the recipients' rights hereunder, except as permitted under Section 6.
-
- 2.2 Modified Code. You may modify Covered Code and use, reproduce, display, perform, internally distribute within Your organization, and Externally Deploy Your Modifications and Covered Code, for commercial or non-commercial purposes, provided that in each instance You also meet all of these conditions:
-
- (a) You must satisfy all the conditions of Section 2.1 with respect to the Source Code of the Covered Code;
-
- (b) You must duplicate, to the extent it does not already exist, the notice in Exhibit A in each file of the Source Code of all Your Modifications, and cause the modified files to carry prominent notices stating that You changed the files and the date of any change; and
-
- (c) If You Externally Deploy Your Modifications, You must make Source Code of all Your Externally Deployed Modifications either available to those to whom You have Externally Deployed Your Modifications, or publicly available. Source Code of Your Externally Deployed Modifications must be released under the terms set forth in this License, including the license grants set forth in Section 3 below, for as long as you Externally Deploy the Covered Code or twelve (12) months from the date of initial External Deployment, whichever is longer. You should preferably distribute the Source Code of Your Externally Deployed Modifications electronically (e.g. download from a web site).
-
- 2.3 Distribution of Executable Versions. In addition, if You Externally Deploy Covered Code (Original Code and/or Modifications) in object code, executable form only, You must include a prominent notice, in the code itself as well as in related documentation, stating that Source Code of the Covered Code is available under the terms of this License with information on how and where to obtain such Source Code.
-
- 2.4 Third Party Rights. You expressly acknowledge and agree that although Apple and each Contributor grants the licenses to their respective portions of the Covered Code set forth herein, no assurances are provided by Apple or any Contributor that the Covered Code does not infringe the patent or other intellectual property rights of any other entity. Apple and each Contributor disclaim any liability to You for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, You hereby assume sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow You to distribute the Covered Code, it is Your responsibility to acquire that license before distributing the Covered Code.
-
-3. Your Grants. In consideration of, and as a condition to, the licenses granted to You under this License, You hereby grant to any person or entity receiving or distributing Covered Code under this License a non-exclusive, royalty-free, perpetual, irrevocable license, under Your Applicable Patent Rights and other intellectual property rights (other than patent) owned or controlled by You, to use, reproduce, display, perform, modify, sublicense, distribute and Externally Deploy Your Modifications of the same scope and extent as Apple's licenses under Sections 2.1 and 2.2 above.
-
-4. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In each such instance, You must make sure the requirements of this License are fulfilled for the Covered Code or any portion thereof.
-
-5. Limitations on Patent License. Except as expressly stated in Section 2, no other patent rights, express or implied, are granted by Apple herein. Modifications and/or Larger Works may require additional patent licenses from Apple which Apple may grant in its sole discretion.
-
-6. Additional Terms. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations and/or other rights consistent with the scope of the license granted herein ("Additional Terms") to one or more recipients of Covered Code. However, You may do so only on Your own behalf and as Your sole responsibility, and not on behalf of Apple or any Contributor. You must obtain the recipient's agreement that any such Additional Terms are offered by You alone, and You hereby agree to indemnify, defend and hold Apple and every Contributor harmless for any liability incurred by or claims asserted against Apple or such Contributor by reason of any such Additional Terms.
-
-7. Versions of the License. Apple may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Once Original Code has been published under a particular version of this License, You may continue to use it under the terms of that version. You may also choose to use such Original Code under the terms of any subsequent version of this License published by Apple. No one other than Apple has the right to modify the terms applicable to Covered Code created under this License.
-
-8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in part pre-release, untested, or not fully tested works. The Covered Code may contain errors that could cause failures or loss of data, and may be incomplete or contain inaccuracies. You expressly acknowledge and agree that use of the Covered Code, or any portion thereof, is at Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. You acknowledge that the Covered Code is not intended for use in the operation of nuclear facilities, aircraft navigation, communication systems, or air traffic control machines in which case the failure of the Covered Code could lead to death, personal injury, or severe physical or environmental damage.
-
-9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU. In no event shall Apple's total liability to You for all damages (other than as may be required by applicable law) under this License exceed the amount of fifty dollars ($50.00).
-
-10. Trademarks. This License does not grant any rights to use the trademarks or trade names "Apple", "Mac", "Mac OS", "QuickTime", "QuickTime Streaming Server" or any other trademarks, service marks, logos or trade names belonging to Apple (collectively "Apple Marks") or to any trademark, service mark, logo or trade name belonging to any Contributor. You agree not to use any Apple Marks in or as part of the name of products derived from the Original Code or to endorse or promote products derived from the Original Code other than as expressly permitted by and in strict compliance at all times with Apple's third party trademark usage guidelines which are posted at http://www.apple.com/legal/guidelinesfor3rdparties.html.
-
-11. Ownership. Subject to the licenses granted under this License, each Contributor retains all rights, title and interest in and to any Modifications made by such Contributor. Apple retains all rights, title and interest in and to the Original Code and any Modifications made by or on behalf of Apple ("Apple Modifications"), and such Apple Modifications will not be automatically subject to this License. Apple may, at its sole discretion, choose to license such Apple Modifications under this License, or on different terms from those contained in this License or may choose not to license them at all.
-
-12. Termination.
-
- 12.1 Termination. This License and the rights granted hereunder will terminate:
-
- (a) automatically without notice from Apple if You fail to comply with any term(s) of this License and fail to cure such breach within 30 days of becoming aware of such breach;
-
- (b) immediately in the event of the circumstances described in Section 13.5(b); or
-
- (c) automatically without notice from Apple if You, at any time during the term of this License, commence an action for patent infringement against Apple; provided that Apple did not first commence an action for patent infringement against You in that instance.
-
- 12.2 Effect of Termination. Upon termination, You agree to immediately stop any further use, reproduction, modification, sublicensing and distribution of the Covered Code. All sublicenses to the Covered Code which have been properly granted prior to termination shall survive any termination of this License. Provisions which, by their nature, should remain in effect beyond the termination of this License shall survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. No party will be liable to any other for compensation, indemnity or damages of any sort solely as a result of terminating this License in accordance with its terms, and termination of this License will be without prejudice to any other right or remedy of any party.
-
-13. Miscellaneous.
-
- 13.1 Government End Users. The Covered Code is a "commercial item" as defined in FAR 2.101. Government software and technical data rights in the Covered Code include only those rights customarily provided to the public as defined in this License. This customary commercial license in technical data and software is provided in accordance with FAR 12.211 (Technical Data) and 12.212 (Computer Software) and, for Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 (Rights in Commercial Computer Software or Computer Software Documentation). Accordingly, all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
- 13.2 Relationship of Parties. This License will not be construed as creating an agency, partnership, joint venture or any other form of legal association between or among You, Apple or any Contributor, and You will not represent to the contrary, whether expressly, by implication, appearance or otherwise.
-
- 13.3 Independent Development. Nothing in this License will impair Apple's right to acquire, license, develop, have others develop for it, market and/or distribute technology or products that perform the same or similar functions as, or otherwise compete with, Modifications, Larger Works, technology or products that You may develop, produce, market or distribute.
-
- 13.4 Waiver; Construction. Failure by Apple or any Contributor to enforce any provision of this License will not be deemed a waiver of future enforcement of that or any other provision. Any law or regulation which provides that the language of a contract shall be construed against the drafter will not apply to this License.
-
- 13.5 Severability. (a) If for any reason a court of competent jurisdiction finds any provision of this License, or portion thereof, to be unenforceable, that provision of the License will be enforced to the maximum extent permissible so as to effect the economic benefits and intent of the parties, and the remainder of this License will continue in full force and effect. (b) Notwithstanding the foregoing, if applicable law prohibits or restricts You from fully and/or specifically complying with Sections 2 and/or 3 or prevents the enforceability of either of those Sections, this License will immediately terminate and You must immediately discontinue any use of the Covered Code and destroy all copies of it that are in your possession or control.
-
- 13.6 Dispute Resolution. Any litigation or other dispute resolution between You and Apple relating to this License shall take place in the Northern District of California, and You and Apple hereby consent to the personal jurisdiction of, and venue in, the state and federal courts within that District with respect to this License. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded.
-
- 13.7 Entire Agreement; Governing Law. This License constitutes the entire agreement between the parties with respect to the subject matter hereof. This License shall be governed by the laws of the United States and the State of California, except that body of California law concerning conflicts of law.
-
- Where You are located in the province of Quebec, Canada, the following clause applies: The parties hereby confirm that they have requested that this License and all related documents be drafted in English. Les parties ont exigé que le présent contrat et tous les documents connexes soient rédigés en anglais.
-
-EXHIBIT A.
-
-"Portions Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
-
-This file contains Original Code and/or Modifications of Original Code as defined in and that are subject to the Apple Public Source License Version 2.0 (the 'License'). You may not use this file except in compliance with the License. Please obtain a copy of the License at http://www.opensource.apple.com/apsl/ and read it before using this file.
-
-The Original Code and all software distributed under the License are distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the specific language governing rights and limitations under the License."
diff --git a/options/license/ASWF-Digital-Assets-1.0 b/options/license/ASWF-Digital-Assets-1.0
deleted file mode 100644
index 27e45b19c9..0000000000
--- a/options/license/ASWF-Digital-Assets-1.0
+++ /dev/null
@@ -1,17 +0,0 @@
-ASWF Digital Assets License v1.0
-
-License for <Asset Name> (the "Asset Name").
-
-<Asset Name> Copyright <Year> <Asset Owner>. All rights reserved.
-
-Redistribution and use of these digital assets, with or without modification, solely for education, training, research, software and hardware development, performance benchmarking (including publication of benchmark results and permitting reproducibility of the benchmark results by third parties), or software and hardware product demonstrations, are permitted provided that the following conditions are met:
-
-1. Redistributions of these digital assets or any part of them must include the above copyright notice, this list of conditions and the disclaimer below.
-
-2. Publications showing images derived from these digital assets must include the above copyright notice.
-
-3. The names of copyright holder or the names of its contributors may NOT be used to promote or to imply endorsement, sponsorship, or affiliation with products developed or tested utilizing these digital assets or benchmarking results obtained from these digital assets, without prior written permission from copyright holder.
-
-4. The assets and their output may only be referred to as the Asset Name listed above, and your use of the Asset Name shall be solely to identify the digital assets. Other than as expressly permitted by this License, you may NOT use any trade names, trademarks, service marks, or product names of the copyright holder for any purpose.
-
-DISCLAIMER: THESE DIGITAL ASSETS ARE PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THESE DIGITAL ASSETS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/ASWF-Digital-Assets-1.1 b/options/license/ASWF-Digital-Assets-1.1
deleted file mode 100644
index 566604a100..0000000000
--- a/options/license/ASWF-Digital-Assets-1.1
+++ /dev/null
@@ -1,17 +0,0 @@
-ASWF Digital Assets License v1.1
-
-License for <Asset Name> (the "Asset Name").
-
-<Asset Name> Copyright <Year> <Asset Owner>. All rights reserved.
-
-Redistribution and use of these digital assets, with or without modification, solely for education, training, research, software and hardware development, performance benchmarking (including publication of benchmark results and permitting reproducibility of the benchmark results by third parties), or software and hardware product demonstrations, are permitted provided that the following conditions are met:
-
-1. Redistributions of these digital assets or any part of them must include the above copyright notice, this list of conditions and the disclaimer below, and if applicable, a description of how the redistributed versions of the digital assets differ from the originals.
-
-2. Publications showing images derived from these digital assets must include the above copyright notice.
-
-3. The names of copyright holder or the names of its contributors may NOT be used to promote or to imply endorsement, sponsorship, or affiliation with products developed or tested utilizing these digital assets or benchmarking results obtained from these digital assets, without prior written permission from copyright holder.
-
-4. The assets and their output may only be referred to as the Asset Name listed above, and your use of the Asset Name shall be solely to identify the digital assets. Other than as expressly permitted by this License, you may NOT use any trade names, trademarks, service marks, or product names of the copyright holder for any purpose.
-
-DISCLAIMER: THESE DIGITAL ASSETS ARE PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THESE DIGITAL ASSETS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/Abstyles b/options/license/Abstyles
deleted file mode 100644
index 96027770ec..0000000000
--- a/options/license/Abstyles
+++ /dev/null
@@ -1,11 +0,0 @@
-This is APREAMBL.TEX, version 1.10e, written by Hans-Hermann Bode
-(HHBODE@DOSUNI1.BITNET), for the BibTeX `adaptable' family, version 1.10.
-See the file APREAMBL.DOC for a detailed documentation.
-
-This program is distributed WITHOUT ANY WARRANTY, express or implied.
-
-Copyright (C) 1991, 1992 Hans-Hermann Bode
-
-Permission is granted to make and distribute verbatim copies of this document provided that the copyright notice and this permission notice are preserved on all copies.
-
-Permission is granted to copy and distribute modified versions of this document under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.
diff --git a/options/license/AdaCore-doc b/options/license/AdaCore-doc
deleted file mode 100644
index 0a1dab1b2c..0000000000
--- a/options/license/AdaCore-doc
+++ /dev/null
@@ -1 +0,0 @@
-This document may be copied, in whole or in part, in any form or by any means, as is or with alterations, provided that (1) alterations are clearly marked as alterations and (2) this copyright notice is included unmodified in any copy.
diff --git a/options/license/Adobe-2006 b/options/license/Adobe-2006
deleted file mode 100644
index d6fb2634a1..0000000000
--- a/options/license/Adobe-2006
+++ /dev/null
@@ -1,12 +0,0 @@
-Adobe Systems Incorporated(r) Source Code License Agreement
-Copyright(c) 2006 Adobe Systems Incorporated. All rights reserved.
-
-Please read this Source Code License Agreement carefully before using the source code.
-
-Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license, to reproduce, prepare derivative works of, publicly display, publicly perform, and distribute this source code and such derivative works in source or object code form without any attribution requirements.
-
-The name "Adobe Systems Incorporated" must not be used to endorse or promote products derived from the source code without prior written permission.
-
-You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and against any loss, damage, claims or lawsuits, including attorney's fees that arise or result from your use or distribution of the source code.
-
-THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOURCE CODE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/Adobe-Display-PostScript b/options/license/Adobe-Display-PostScript
deleted file mode 100644
index 6df57d3c80..0000000000
--- a/options/license/Adobe-Display-PostScript
+++ /dev/null
@@ -1,30 +0,0 @@
-(c)Copyright 1988,1991 Adobe Systems Incorporated.
-All rights reserved.
-
-Permission to use, copy, modify, distribute, and sublicense this software and its
-documentation for any purpose and without fee is hereby granted, provided that
-the above copyright notices appear in all copies and that both those copyright
-notices and this permission notice appear in supporting documentation and that
-the name of Adobe Systems Incorporated not be used in advertising or publicity
-pertaining to distribution of the software without specific, written prior
-permission. No trademark license to use the Adobe trademarks is hereby
-granted. If the Adobe trademark "Display PostScript"(tm) is used to describe
-this software, its functionality or for any other purpose, such use shall be
-limited to a statement that this software works in conjunction with the Display
-PostScript system. Proper trademark attribution to reflect Adobe's ownership
-of the trademark shall be given whenever any such reference to the Display
-PostScript system is made.
-
-ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR ANY
-PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. ADOBE
-DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
-WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-
-INFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL ADOBE BE LIABLE TO YOU
-OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
-DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,NEGLIGENCE, STRICT
-LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-PERFORMANCE OF THIS SOFTWARE. ADOBE WILL NOT PROVIDE ANY TRAINING OR OTHER
-SUPPORT FOR THE SOFTWARE.
-
-Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems
-Incorporated which may be registered in certain jurisdictions.
diff --git a/options/license/Adobe-Glyph b/options/license/Adobe-Glyph
deleted file mode 100644
index 609651d82e..0000000000
--- a/options/license/Adobe-Glyph
+++ /dev/null
@@ -1,10 +0,0 @@
-Copyright (c) 1997,1998,2002,2007 Adobe Systems Incorporated
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this documentation file to use, copy, publish, distribute, sublicense, and/or sell copies of the documentation, and to permit others to do the same, provided that:
-
- - No modification, editing or other alteration of this document is allowed; and
- - The above copyright notice and this permission notice shall be included in all copies of the documentation.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this documentation file, to create their own derivative works from the content of this document to use, copy, publish, distribute, sublicense, and/or sell the derivative works, and to permit others to do the same, provided that the derived work is not represented as being a copy or version of this document.
-
-Adobe shall not be liable to any party for any loss of revenue or profit or for indirect, incidental, special, consequential, or other similar damages, whether based on tort (including without limitation negligence or strict liability), contract or other legal or equitable grounds even if Adobe has been advised or had reason to know of the possibility of such damages. The Adobe materials are provided on an "AS IS" basis.Ê Adobe specifically disclaims all express, statutory, or implied warranties relating to the Adobe materials, including but not limited to those concerning merchantability or fitness for a particular purpose or non-infringement of any third party rights regarding the Adobe materials.
diff --git a/options/license/Adobe-Utopia b/options/license/Adobe-Utopia
deleted file mode 100644
index 4aa04503b5..0000000000
--- a/options/license/Adobe-Utopia
+++ /dev/null
@@ -1,12 +0,0 @@
-Permission to use, reproduce, display and distribute the listed typefaces
-is hereby granted, provided that the Adobe Copyright notice appears in all
-whole and partial copies of the software and that the following trademark
-symbol and attribution appear in all unmodified copies of the software:
-
-The Adobe typefaces (Type 1 font program, bitmaps and Adobe Font Metric
-files) donated are:
-
- Utopia Regular
- Utopia Italic
- Utopia Bold
- Utopia Bold Italic
diff --git a/options/license/Afmparse b/options/license/Afmparse
deleted file mode 100644
index 7c6d37ca65..0000000000
--- a/options/license/Afmparse
+++ /dev/null
@@ -1,10 +0,0 @@
-(C) 1988, 1989 by Adobe Systems Incorporated. All rights reserved.
-
-This file may be freely copied and redistributed as long as:
-
- 1) This entire notice continues to be included in the file,
- 2) If the file has been modified in any way, a notice of such modification is conspicuously indicated.
-
-PostScript, Display PostScript,and Adobe are registered trademarks of Adobe Systems Incorporated.
-
-THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
diff --git a/options/license/Aladdin b/options/license/Aladdin
deleted file mode 100644
index 5d71ff8ec5..0000000000
--- a/options/license/Aladdin
+++ /dev/null
@@ -1,62 +0,0 @@
-Aladdin Free Public License
-(Version 8, November 18, 1999)
-
-Copyright (C) 1994, 1995, 1997, 1998, 1999 Aladdin Enterprises, Menlo Park, California, U.S.A. All rights reserved.
-
-NOTE: This License is not the same as any of the GNU Licenses published by the Free Software Foundation. Its terms are substantially different from those of the GNU Licenses. If you are familiar with the GNU Licenses, please read this license with extra care.
-
-Aladdin Enterprises hereby grants to anyone the permission to apply this License to their own work, as long as the entire License (including the above notices and this paragraph) is copied with no changes, additions, or deletions except for changing the first paragraph of Section 0 to include a suitable description of the work to which the license is being applied and of the person or entity that holds the copyright in the work, and, if the License is being applied to a work created in a country other than the United States, replacing the first paragraph of Section 6 with an appropriate reference to the laws of the appropriate country.
-
-0. Subject Matter
-This License applies to the computer program known as "Aladdin Ghostscript." The "Program", below, refers to such program. The Program is a copyrighted work whose copyright is held by Aladdin Enterprises (the "Licensor"). Please note that Aladdin Ghostscript is neither the program known as "GNU Ghostscript" nor the version of Ghostscript available for commercial licensing from Artifex Software Inc.
-
-A "work based on the Program" means either the Program or any derivative work of the Program, as defined in the United States Copyright Act of 1976, such as a translation or a modification.
-
-BY MODIFYING OR DISTRIBUTING THE PROGRAM (OR ANY WORK BASED ON THE PROGRAM), YOU INDICATE YOUR ACCEPTANCE OF THIS LICENSE TO DO SO, AND ALL ITS TERMS AND CONDITIONS FOR COPYING, DISTRIBUTING OR MODIFYING THE PROGRAM OR WORKS BASED ON IT. NOTHING OTHER THAN THIS LICENSE GRANTS YOU PERMISSION TO MODIFY OR DISTRIBUTE THE PROGRAM OR ITS DERIVATIVE WORKS. THESE ACTIONS ARE PROHIBITED BY LAW. IF YOU DO NOT ACCEPT THESE TERMS AND CONDITIONS, DO NOT MODIFY OR DISTRIBUTE THE PROGRAM.
-
-1. Licenses.
-Licensor hereby grants you the following rights, provided that you comply with all of the restrictions set forth in this License and provided, further, that you distribute an unmodified copy of this License with the Program:
-
- (a) You may copy and distribute literal (i.e., verbatim) copies of the Program's source code as you receive it throughout the world, in any medium.
- (b) You may modify the Program, create works based on the Program and distribute copies of such throughout the world, in any medium.
-
-2. Restrictions.
-This license is subject to the following restrictions:
-
- (a) Distribution of the Program or any work based on the Program by a commercial organization to any third party is prohibited if any payment is made in connection with such distribution, whether directly (as in payment for a copy of the Program) or indirectly (as in payment for some service related to the Program, or payment for some product or service that includes a copy of the Program "without charge"; these are only examples, and not an exhaustive enumeration of prohibited activities). The following methods of distribution involving payment shall not in and of themselves be a violation of this restriction:
-
- (i) Posting the Program on a public access information storage and retrieval service for which a fee is received for retrieving information (such as an on-line service), provided that the fee is not content-dependent (i.e., the fee would be the same for retrieving the same volume of information consisting of random data) and that access to the service and to the Program is available independent of any other product or service. An example of a service that does not fall under this section is an on-line service that is operated by a company and that is only available to customers of that company. (This is not an exhaustive enumeration.)
- (ii) Distributing the Program on removable computer-readable media, provided that the files containing the Program are reproduced entirely and verbatim on such media, that all information on such media be redistributable for non-commercial purposes without charge, and that such media are distributed by themselves (except for accompanying documentation) independent of any other product or service. Examples of such media include CD-ROM, magnetic tape, and optical storage media. (This is not intended to be an exhaustive list.) An example of a distribution that does not fall under this section is a CD-ROM included in a book or magazine. (This is not an exhaustive enumeration.)
-
- (b) Activities other than copying, distribution and modification of the Program are not subject to this License and they are outside its scope. Functional use (running) of the Program is not restricted, and any output produced through the use of the Program is subject to this license only if its contents constitute a work based on the Program (independent of having been made by running the Program).
-
- (c) You must meet all of the following conditions with respect to any work that you distribute or publish that in whole or in part contains or is derived from the Program or any part thereof ("the Work"):
-
- (i) If you have modified the Program, you must cause the Work to carry prominent notices stating that you have modified the Program's files and the date of any change. In each source file that you have modified, you must include a prominent notice that you have modified the file, including your name, your e-mail address (if any), and the date and purpose of the change;
- (ii) You must cause the Work to be licensed as a whole and at no charge to all third parties under the terms of this License;
- (iii) If the Work normally reads commands interactively when run, you must cause it, at each time the Work commences operation, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty). Such notice must also state that users may redistribute the Work only under the conditions of this License and tell the user how to view the copy of this License included with the Work. (Exceptions: if the Program is interactive but normally prints or displays such an announcement only at the request of a user, such as in an "About box", the Work is required to print or display the notice only under the same circumstances; if the Program itself is interactive but does not normally print such an announcement, the Work is not required to print an announcement.);
- (iv) You must accompany the Work with the complete corresponding machine-readable source code, delivered on a medium customarily used for software interchange. The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable code. If you distribute with the Work any component that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, you must also distribute the source code of that component if you have it and are allowed to do so;
- (v) If you distribute any written or printed material at all with the Work, such material must include either a written copy of this License, or a prominent written indication that the Work is covered by this License and written instructions for printing and/or displaying the copy of the License on the distribution medium;
- (vi) You may not impose any further restrictions on the recipient's exercise of the rights granted herein.
-
-If distribution of executable or object code is made by offering the equivalent ability to copy from a designated place, then offering equivalent ability to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source code along with the object code.
-
-3. Reservation of Rights.
-No rights are granted to the Program except as expressly set forth herein. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-4. Other Restrictions.
-If the distribution and/or use of the Program is restricted in certain countries for any reason, Licensor may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
-
-5. Limitations.
-THE PROGRAM IS PROVIDED TO YOU "AS IS," WITHOUT WARRANTY. THERE IS NO WARRANTY FOR THE PROGRAM, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL LICENSOR, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-6. General.
-This License is governed by the laws of the State of California, U.S.A., excluding choice of law rules.
-
-If any part of this License is found to be in conflict with the law, that part shall be interpreted in its broadest meaning consistent with the law, and no other parts of the License shall be affected.
-
-For United States Government users, the Program is provided with RESTRICTED RIGHTS. If you are a unit or agency of the United States Government or are acquiring the Program for any such unit or agency, the following apply:
-
-If the unit or agency is the Department of Defense ("DOD"), the Program and its documentation are classified as "commercial computer software" and "commercial computer software documentation" respectively and, pursuant to DFAR Section 227.7202, the Government is acquiring the Program and its documentation in accordance with the terms of this License. If the unit or agency is other than DOD, the Program and its documentation are classified as "commercial computer software" and "commercial computer software documentation" respectively and, pursuant to FAR Section 12.212, the Government is acquiring the Program and its documentation in accordance with the terms of this License.
diff --git a/options/license/Apache-1.0 b/options/license/Apache-1.0
deleted file mode 100644
index 383e7b920a..0000000000
--- a/options/license/Apache-1.0
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 1995-1999 The Apache Group. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. All advertising materials mentioning features or use of this software must display the following acknowledgment: "This product includes software developed by the Apache Group for use in the Apache HTTP server project (http://www.apache.org/)."
-
-4. The names "Apache" and "Apache Software Foundation" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact apache@apache.org.
-
-5. Products derived from this software may not be called "Apache" nor may "Apache" appear in their name, without prior written permission of the Apache Group.
-
-6. Redistributions of any form whatsoever must retain the following acknowledgment:
-"This product includes software developed by the Apache Group for use in the Apache HTTP server project (http://www.apache.org/)."
-
-THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- This software consists of voluntary contributions made by many individuals on behalf of the Apache Group and was originally based on public domain software written at the National Center for Supercomputing Applications, University of Illinois, Urbana-Champaign. For more information on the Apache Group and the Apache HTTP server project, please see <http://www.apache.org/>.
diff --git a/options/license/Apache-1.1 b/options/license/Apache-1.1
deleted file mode 100644
index b2e3bf4d89..0000000000
--- a/options/license/Apache-1.1
+++ /dev/null
@@ -1,21 +0,0 @@
-The Apache Software License, Version 1.1
-
-Copyright (c) 2000 The Apache Software Foundation. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment:
-"This product includes software developed by the Apache Software Foundation (http://www.apache.org/)."
-Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear.
-
-4. The names "Apache" and "Apache Software Foundation" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact apache@apache.org.
-
-5. Products derived from this software may not be called "Apache" nor may "Apache" appear in their name, without prior written permission of the Apache Software Foundation.
-
-THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- This software consists of voluntary contributions made by many individuals on behalf of the Apache Software Foundation. For more information on the Apache Software Foundation, please see <http://www.apache.org/>. Portions of this software are based upon public domain software originally written at the National Center for Supercomputing Applications, University of Illinois, Urbana-Champaign.
diff --git a/options/license/App-s2p b/options/license/App-s2p
deleted file mode 100644
index b19eabf068..0000000000
--- a/options/license/App-s2p
+++ /dev/null
@@ -1,5 +0,0 @@
-COPYRIGHT and LICENSE
-
-This program is free and open software. You may use, modify,
-distribute, and sell this program (and any modified variants) in any
-way you wish, provided you do not restrict others from doing the same.
diff --git a/options/license/Arphic-1999 b/options/license/Arphic-1999
deleted file mode 100644
index c1aba41d3f..0000000000
--- a/options/license/Arphic-1999
+++ /dev/null
@@ -1,58 +0,0 @@
-ARPHIC PUBLIC LICENSE
-
-Copyright (C) 1999 Arphic Technology Co., Ltd.
-11Fl. No.168, Yung Chi Rd., Taipei, 110 Taiwan
-All rights reserved except as specified below.
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is forbidden.
-
-Preamble
-
- The licenses for most software are designed to take away your freedom to share and change it. By contrast, the ARPHIC PUBLIC LICENSE specifically permits and encourages you to use this software, provided that you give the recipients all the rights that we gave you and make sure they can get the modifications of this software.
-
-Legal Terms
-
-0. Definitions:
- Throughout this License, "Font" means the TrueType fonts "AR PL Mingti2L Big5", "AR PL KaitiM Big5" (BIG-5 character set) and "AR PL SungtiL GB", "AR PL KaitiM GB" (GB character set) which are originally distributed by Arphic, and the derivatives of those fonts created through any modification including modifying glyph, reordering glyph, converting format, changing font name, or adding/deleting some characters in/from glyph table.
-
- "PL" means "Public License".
-
- "Copyright Holder" means whoever is named in the copyright or copyrights for the Font.
-
- "You" means the licensee, or person copying, redistributing or modifying the Font.
-
- "Freely Available" means that you have the freedom to copy or modify the Font as well as redistribute copies of the Font under the same conditions you received, not price. If you wish, you can charge for this service.
-
-1. Copying & Distribution
- You may copy and distribute verbatim copies of this Font in any medium, without restriction, provided that you retain this license file (ARPHICPL.TXT) unaltered in all copies.
-
-2. Modification
- You may otherwise modify your copy of this Font in any way, including modifying glyph, reordering glyph, converting format, changing font name, or adding/deleting some characters in/from glyph table, and copy and distribute such modifications under the terms of Section 1 above, provided that the following conditions are met:
-
- a) You must insert a prominent notice in each modified file stating how and when you changed that file.
-
- b) You must make such modifications Freely Available as a whole to all third parties under the terms of this License, such as by offering access to copy the modifications from a designated place, or distributing the modifications on a medium customarily used for software interchange.
-
- c) If the modified fonts normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the Font under these conditions, and telling the user how to view a copy of this License.
-
- These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Font, and can be reasonably considered independent and separate works in themselves, then this License and its terms, do not apply to those sections when you distribute them as separate works. Therefore, mere aggregation of another work not based on the Font with the Font on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
-
-3. Condition Subsequent
- You may not copy, modify, sublicense, or distribute the Font except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Font will automatically retroactively void your rights under this License. However, parties who have received copies or rights from you under this License will keep their licenses valid so long as such parties remain in full compliance.
-
-4. Acceptance
- You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to copy, modify, sublicense or distribute the Font. These actions are prohibited by law if you do not accept this License. Therefore, by copying, modifying, sublicensing or distributing the Font, you indicate your acceptance of this License and all its terms and conditions.
-
-5. Automatic Receipt
- Each time you redistribute the Font, the recipient automatically receives a license from the original licensor to copy, distribute or modify the Font subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
-
-6. Contradiction
- If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Font at all. For example, if a patent license would not permit royalty-free redistribution of the Font by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Font.
-
- If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
-
-7. NO WARRANTY
- BECAUSE THE FONT IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE FONT, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS OR OTHER PARTIES PROVIDE THE FONT "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE FONT IS WITH YOU. SHOULD THE FONT PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-8. DAMAGES WAIVER
- UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, IN NO EVENT WILL ANY COPYRIGHTT HOLDERS, OR OTHER PARTIES WHO MAY COPY, MODIFY OR REDISTRIBUTE THE FONT AS PERMITTED ABOVE, BE LIABLE TO YOU FOR ANY DIRECT, INDIRECT, CONSEQUENTIAL, INCIDENTAL, SPECIAL OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE FONT (INCLUDING BUT NOT LIMITED TO PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION), EVEN IF SUCH HOLDERS OR OTHER PARTIES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/options/license/Artistic-1.0 b/options/license/Artistic-1.0
deleted file mode 100644
index 9298f435eb..0000000000
--- a/options/license/Artistic-1.0
+++ /dev/null
@@ -1,49 +0,0 @@
-The Artistic License
-
-Preamble
-
-The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications.
-
-Definitions:
-
- "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification.
-
- "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder.
-
- "Copyright Holder" is whoever is named in the copyright or copyrights for the package.
-
- "You" is you, if you're thinking about copying or distributing this Package.
-
- "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.)
-
- "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it.
-
-1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers.
-
-2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version.
-
-3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following:
-
- a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package.
- b) use the modified Package only within your corporation or organization.
- c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version.
- d) make other distribution arrangements with the Copyright Holder.
-
-4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following:
-
- a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version.
- b) accompany the distribution with the machine-readable source of the Package with your modifications.
- c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version.
- d) make other distribution arrangements with the Copyright Holder.
-
-5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own.
-
-6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package.
-
-7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package.
-
-8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission.
-
-9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-The End
diff --git a/options/license/Artistic-1.0-Perl b/options/license/Artistic-1.0-Perl
deleted file mode 100644
index dd45f4cd89..0000000000
--- a/options/license/Artistic-1.0-Perl
+++ /dev/null
@@ -1,51 +0,0 @@
-The "Artistic License"
-
-Preamble
-
-The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications.
-
-Definitions:
-
- "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification.
-
- "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder as specified below.
-
- "Copyright Holder" is whoever is named in the copyright or copyrights for the package.
-
- "You" is you, if you're thinking about copying or distributing this Package.
-
- "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.)
-
- "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it.
-
-1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers.
-
-2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version.
-
-3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following:
-
- a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package.
- b) use the modified Package only within your corporation or organization.
- c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version.
- d) make other distribution arrangements with the Copyright Holder.
-
-4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following:
-
- a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version.
- b) accompany the distribution with the machine-readable source of the Package with your modifications.
- c) give non-standard executables non-standard names, and clearly document the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version.
- d) make other distribution arrangements with the Copyright Holder.
-
-5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded.
-
-6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whoever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package.
-
-7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language.
-
-8. Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package.
-
-9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission.
-
-10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-The End
diff --git a/options/license/Artistic-1.0-cl8 b/options/license/Artistic-1.0-cl8
deleted file mode 100644
index 6f26f833d0..0000000000
--- a/options/license/Artistic-1.0-cl8
+++ /dev/null
@@ -1,51 +0,0 @@
-The Artistic License
-
-Preamble
-
-The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications.
-
-Definitions:
-
- "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification.
-
- "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder.
-
- "Copyright Holder" is whoever is named in the copyright or copyrights for the package.
-
- "You" is you, if you're thinking about copying or distributing this Package.
-
- "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.)
-
- "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it.
-
-1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers.
-
-2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version.
-
-3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following:
-
- a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package.
- b) use the modified Package only within your corporation or organization.
- c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version.
- d) make other distribution arrangements with the Copyright Holder.
-
-4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following:
-
- a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version.
- b) accompany the distribution with the machine-readable source of the Package with your modifications.
- c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version.
- d) make other distribution arrangements with the Copyright Holder.
-
-5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own.
-
-6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package.
-
-7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package.
-
-8.Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package.
-
-9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission.
-
-10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-The End
diff --git a/options/license/Artistic-2.0 b/options/license/Artistic-2.0
deleted file mode 100644
index eb2e968eda..0000000000
--- a/options/license/Artistic-2.0
+++ /dev/null
@@ -1,85 +0,0 @@
-The Artistic License 2.0
-
-Copyright (c) 2000-2006, The Perl Foundation.
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-Preamble
-
-This license establishes the terms under which a given free software Package may be copied, modified, distributed, and/or redistributed. The intent is that the Copyright Holder maintains some artistic control over the development of that Package while still keeping the Package available as open source and free software.
-
-You are always permitted to make arrangements wholly outside of this license directly with the Copyright Holder of a given Package. If the terms of this license do not permit the full use that you propose to make of the Package, you should contact the Copyright Holder and seek a different licensing arrangement.
-
-Definitions
-
- "Copyright Holder" means the individual(s) or organization(s) named in the copyright notice for the entire Package.
-
- "Contributor" means any party that has contributed code or other material to the Package, in accordance with the Copyright Holder's procedures.
-
- "You" and "your" means any person who would like to copy, distribute, or modify the Package.
-
- "Package" means the collection of files distributed by the Copyright Holder, and derivatives of that collection and/or of those files. A given Package may consist of either the Standard Version, or a Modified Version.
-
- "Distribute" means providing a copy of the Package or making it accessible to anyone else, or in the case of a company or organization, to others outside of your company or organization.
-
- "Distributor Fee" means any fee that you charge for Distributing this Package or providing support for this Package to another party. It does not mean licensing fees.
-
- "Standard Version" refers to the Package if it has not been modified, or has been modified only in ways explicitly requested by the Copyright Holder.
-
- "Modified Version" means the Package, if it has been changed, and such changes were not explicitly requested by the Copyright Holder.
-
- "Original License" means this Artistic License as Distributed with the Standard Version of the Package, in its current version or as it may be modified by The Perl Foundation in the future.
-
- "Source" form means the source code, documentation source, and configuration files for the Package.
-
- "Compiled" form means the compiled bytecode, object code, binary, or any other form resulting from mechanical transformation or translation of the Source form.
-
-Permission for Use and Modification Without Distribution
-
-(1) You are permitted to use the Standard Version and create and use Modified Versions for any purpose without restriction, provided that you do not Distribute the Modified Version.
-
-Permissions for Redistribution of the Standard Version
-
-(2) You may Distribute verbatim copies of the Source form of the Standard Version of this Package in any medium without restriction, either gratis or for a Distributor Fee, provided that you duplicate all of the original copyright notices and associated disclaimers. At your discretion, such verbatim copies may or may not include a Compiled form of the Package.
-
-(3) You may apply any bug fixes, portability changes, and other modifications made available from the Copyright Holder. The resulting Package will still be considered the Standard Version, and as such will be subject to the Original License.
-
-Distribution of Modified Versions of the Package as Source
-
-(4) You may Distribute your Modified Version as Source (either gratis or for a Distributor Fee, and with or without a Compiled form of the Modified Version) provided that you clearly document how it differs from the Standard Version, including, but not limited to, documenting any non-standard features, executables, or modules, and provided that you do at least ONE of the following:
-
- (a) make the Modified Version available to the Copyright Holder of the Standard Version, under the Original License, so that the Copyright Holder may include your modifications in the Standard Version.
- (b) ensure that installation of your Modified Version does not prevent the user installing or running the Standard Version. In addition, the Modified Version must bear a name that is different from the name of the Standard Version.
- (c) allow anyone who receives a copy of the Modified Version to make the Source form of the Modified Version available to others under
-
- (i) the Original License or
- (ii) a license that permits the licensee to freely copy, modify and redistribute the Modified Version using the same licensing terms that apply to the copy that the licensee received, and requires that the Source form of the Modified Version, and of any works derived from it, be made freely available in that license fees are prohibited but Distributor Fees are allowed.
-
-Distribution of Compiled Forms of the Standard Version or Modified Versions without the Source
-
-(5) You may Distribute Compiled forms of the Standard Version without the Source, provided that you include complete instructions on how to get the Source of the Standard Version. Such instructions must be valid at the time of your distribution. If these instructions, at any time while you are carrying out such distribution, become invalid, you must provide new instructions on demand or cease further distribution. If you provide valid instructions or cease distribution within thirty days after you become aware that the instructions are invalid, then you do not forfeit any of your rights under this license.
-
-(6) You may Distribute a Modified Version in Compiled form without the Source, provided that you comply with Section 4 with respect to the Source of the Modified Version.
-
-Aggregating or Linking the Package
-
-(7) You may aggregate the Package (either the Standard Version or Modified Version) with other packages and Distribute the resulting aggregation provided that you do not charge a licensing fee for the Package. Distributor Fees are permitted, and licensing fees for other components in the aggregation are permitted. The terms of this license apply to the use and Distribution of the Standard or Modified Versions as included in the aggregation.
-
-(8) You are permitted to link Modified and Standard Versions with other works, to embed the Package in a larger work of your own, or to build stand-alone binary or bytecode versions of applications that include the Package, and Distribute the result without restriction, provided the result does not expose a direct interface to the Package.
-
-Items That are Not Considered Part of a Modified Version
-
-(9) Works (including, but not limited to, modules and scripts) that merely extend or make use of the Package, do not, by themselves, cause the Package to be a Modified Version. In addition, such works are not considered parts of the Package itself, and are not subject to the terms of this license.
-
-General Provisions
-
-(10) Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license.
-
-(11) If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license.
-
-(12) This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder.
-
-(13) This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed.
-
-(14) Disclaimer of Warranty:
-THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/Asterisk-exception b/options/license/Asterisk-exception
deleted file mode 100644
index 88253f12d3..0000000000
--- a/options/license/Asterisk-exception
+++ /dev/null
@@ -1,5 +0,0 @@
-In addition, when this program is distributed with Asterisk in any
-form that would qualify as a 'combined work' or as a 'derivative work'
-(but not mere aggregation), you can redistribute and/or modify the
-combination under the terms of the license provided with that copy
-of Asterisk, instead of the license terms granted here.
diff --git a/options/license/Asterisk-linking-protocols-exception b/options/license/Asterisk-linking-protocols-exception
deleted file mode 100644
index 6705829f47..0000000000
--- a/options/license/Asterisk-linking-protocols-exception
+++ /dev/null
@@ -1,13 +0,0 @@
-Specific permission is also granted to link Asterisk with OpenSSL, OpenH323
-UniMRCP, and/or the UW IMAP Toolkit and distribute the resulting binary files.
-
-In addition, Asterisk implements several management/control protocols.
-This includes the Asterisk Manager Interface (AMI), the Asterisk Gateway
-Interface (AGI), and the Asterisk REST Interface (ARI). It is our belief
-that applications using these protocols to manage or control an Asterisk
-instance do not have to be licensed under the GPL or a compatible license,
-as we believe these protocols do not create a 'derivative work' as referred
-to in the GPL. However, should any court or other judiciary body find that
-these protocols do fall under the terms of the GPL, then we hereby grant you a
-license to use these protocols in combination with Asterisk in external
-applications licensed under any license you wish.
diff --git a/options/license/Autoconf-exception-2.0 b/options/license/Autoconf-exception-2.0
deleted file mode 100644
index 00cddeba4d..0000000000
--- a/options/license/Autoconf-exception-2.0
+++ /dev/null
@@ -1,5 +0,0 @@
-As a special exception, the Free Software Foundation gives unlimited permission to copy, distribute and modify the configure scripts that are the output of Autoconf. You need not follow the terms of the GNU General Public License when using or distributing such scripts, even though portions of the text of Autoconf appear in them. The GNU General Public License (GPL) does govern all other use of the material that constitutes the Autoconf program.
-
-Certain portions of the Autoconf source text are designed to be copied (in certain cases, depending on the input) into the output of Autoconf. We call these the "data" portions. The rest of the Autoconf source text consists of comments plus executable code that decides which of the data portions to output in any given case. We call these comments and executable code the "non-data" portions. Autoconf never copies any of the non-data portions into its output.
-
-This special exception to the GPL applies to versions of Autoconf released by the Free Software Foundation. When you make and distribute a modified version of Autoconf, you may extend this special exception to the GPL to apply to your modified version as well, *unless* your modified version has the potential to copy into its output some of the text that was the non-data portion of the version that you started with. (In other words, unless your change moves or copies text from the non-data portions to the data portions.) If your modification has such potential, you must delete any notice of this special exception to the GPL from your modified version.
diff --git a/options/license/Autoconf-exception-3.0 b/options/license/Autoconf-exception-3.0
deleted file mode 100644
index f212f9c7bc..0000000000
--- a/options/license/Autoconf-exception-3.0
+++ /dev/null
@@ -1,26 +0,0 @@
-AUTOCONF CONFIGURE SCRIPT EXCEPTION
-
-Version 3.0, 18 August 2009
-Copyright © 2009 Free Software Foundation, Inc. <http://fsf.org/>
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-This Exception is an additional permission under section 7 of the GNU General Public License, version 3 ("GPLv3"). It applies to a given file that bears a notice placed by the copyright holder of the file stating that the file is governed by GPLv3 along with this Exception.
-
-The purpose of this Exception is to allow distribution of Autoconf's typical output under terms of the recipient's choice (including proprietary).
-
-0. Definitions.
-
-"Covered Code" is the source or object code of a version of Autoconf that is a covered work under this License.
-
-"Normally Copied Code" for a version of Autoconf means all parts of its Covered Code which that version can copy from its code (i.e., not from its input file) into its minimally verbose, non-debugging and non-tracing output.
-
-"Ineligible Code" is Covered Code that is not Normally Copied Code.
-
-1. Grant of Additional Permission.
-
-You have permission to propagate output of Autoconf, even if such propagation would otherwise violate the terms of GPLv3. However, if by modifying Autoconf you cause any Ineligible Code of the version you received to become Normally Copied Code of your modified version, then you void this Exception for the resulting covered work. If you convey that resulting covered work, you must remove this Exception in accordance with the second paragraph of Section 7 of GPLv3.
-
-2. No Weakening of Autoconf Copyleft.
-
-The availability of this Exception does not imply any general presumption that third-party software is unaffected by the copyleft requirements of the license of Autoconf.
diff --git a/options/license/Autoconf-exception-generic b/options/license/Autoconf-exception-generic
deleted file mode 100644
index b39f827673..0000000000
--- a/options/license/Autoconf-exception-generic
+++ /dev/null
@@ -1,4 +0,0 @@
-As a special exception to the GNU General Public License,
-if you distribute this file as part of a program that contains
-a configuration script generated by Autoconf, you may include
-it under the same distribution terms that you use for the rest of that program.
diff --git a/options/license/Autoconf-exception-generic-3.0 b/options/license/Autoconf-exception-generic-3.0
deleted file mode 100644
index 2d3036772c..0000000000
--- a/options/license/Autoconf-exception-generic-3.0
+++ /dev/null
@@ -1,6 +0,0 @@
-As a special exception to the GNU General Public License, if you
-distribute this file as part of a program that contains a
-configuration script generated by Autoconf, you may include it under
-the same distribution terms that you use for the rest of that
-program. This Exception is an additional permission under section 7
-of the GNU General Public License, version 3 ("GPLv3").
diff --git a/options/license/Autoconf-exception-macro b/options/license/Autoconf-exception-macro
deleted file mode 100644
index 8b5b4677f3..0000000000
--- a/options/license/Autoconf-exception-macro
+++ /dev/null
@@ -1,12 +0,0 @@
-As a special exception, the respective Autoconf Macro's copyright owner
-gives unlimited permission to copy, distribute and modify the configure
-scripts that are the output of Autoconf when processing the Macro. You
-need not follow the terms of the GNU General Public License when using
-or distributing such scripts, even though portions of the text of the
-Macro appear in them. The GNU General Public License (GPL) does govern
-all other use of the material that constitutes the Autoconf Macro.
-
-This special exception to the GPL applies to versions of the Autoconf
-Macro released by the Autoconf Archive. When you make and distribute a
-modified version of the Autoconf Macro, you may extend this special
-exception to the GPL to apply to your modified version as well.
diff --git a/options/license/BSD-1-Clause b/options/license/BSD-1-Clause
deleted file mode 100644
index 4005b63def..0000000000
--- a/options/license/BSD-1-Clause
+++ /dev/null
@@ -1,7 +0,0 @@
-Copyright (c) <year> <owner>. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/BSD-2-Clause-Darwin b/options/license/BSD-2-Clause-Darwin
deleted file mode 100644
index d582399763..0000000000
--- a/options/license/BSD-2-Clause-Darwin
+++ /dev/null
@@ -1,28 +0,0 @@
-Copyright (c) Ian F. Darwin 1986, 1987, 1989, 1990, 1991, 1992, 1994, 1995.
-Software written by Ian F. Darwin and others;
-maintained 1994- Christos Zoulas.
-
-This software is not subject to any export provision of the United States
-Department of Commerce, and may be exported to any country or planet.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice immediately at the beginning of the file, without modification,
- this list of conditions, and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
diff --git a/options/license/BSD-2-Clause-Patent b/options/license/BSD-2-Clause-Patent
deleted file mode 100644
index 31de6e498e..0000000000
--- a/options/license/BSD-2-Clause-Patent
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) <YEAR> <COPYRIGHT HOLDERS>
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-Subject to the terms and conditions of this license, each copyright holder and contributor hereby grants to those receiving rights under this license a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except for failure to satisfy the conditions of this license) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer this software, where such license applies only to those patent claims, already acquired or hereafter acquired, licensable by such copyright holder or contributor that are necessarily infringed by:
-
-(a) their Contribution(s) (the licensed copyrights of copyright holders and non-copyrightable additions of contributors, in source or binary form) alone; or
-
-(b) combination of their Contribution(s) with the work of authorship to which such Contribution(s) was added by such copyright holder or contributor, if, at the time the Contribution is added, such addition causes such combination to be necessarily infringed. The patent license shall not apply to any other combinations which include the Contribution.
-
-Except as expressly stated above, no rights or licenses from any copyright holder or contributor is granted under this license, whether expressly, by implication, estoppel or otherwise.
-
-DISCLAIMER
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/BSD-2-Clause-Views b/options/license/BSD-2-Clause-Views
deleted file mode 100644
index be605e314d..0000000000
--- a/options/license/BSD-2-Clause-Views
+++ /dev/null
@@ -1,11 +0,0 @@
-Copyright (c) <year> <owner> All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the copyright holders or contributors.
diff --git a/options/license/BSD-2-Clause-first-lines b/options/license/BSD-2-Clause-first-lines
deleted file mode 100644
index 3774caf24a..0000000000
--- a/options/license/BSD-2-Clause-first-lines
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright (C) 2006,2007,2009 NTT (Nippon Telegraph and Telephone
-Corporation). All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above
- copyright notice, this list of conditions and the following
- disclaimer as the first lines of this file unmodified.
-
-2. Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY NTT "AS IS" AND ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL NTT BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
-OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/BSD-3-Clause-Attribution b/options/license/BSD-3-Clause-Attribution
deleted file mode 100644
index 6dcab5eff0..0000000000
--- a/options/license/BSD-3-Clause-Attribution
+++ /dev/null
@@ -1,11 +0,0 @@
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
- 4. Redistributions of any form whatsoever must retain the following acknowledgment: 'This product includes software developed by the "Universidad de Palermo, Argentina" (http://www.palermo.edu/).'
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/BSD-3-Clause-HP b/options/license/BSD-3-Clause-HP
deleted file mode 100644
index e16195729a..0000000000
--- a/options/license/BSD-3-Clause-HP
+++ /dev/null
@@ -1,23 +0,0 @@
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the HP nor the names of its
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-NOT LIMITED TO, PATENT INFRINGEMENT; PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
-IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/BSD-3-Clause-LBNL b/options/license/BSD-3-Clause-LBNL
deleted file mode 100644
index ab94601aed..0000000000
--- a/options/license/BSD-3-Clause-LBNL
+++ /dev/null
@@ -1,12 +0,0 @@
-Copyright (c) 2003, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-(1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-(2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-(3) Neither the name of the University of California, Lawrence Berkeley National Laboratory, U.S. Dept. of Energy nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-You are under no obligation whatsoever to provide any bug fixes, patches, or upgrades to the features, functionality or performance of the source code ("Enhancements") to anyone; however, if you choose to make your Enhancements available either publicly, or directly to Lawrence Berkeley National Laboratory, without imposing a separate written license agreement for such Enhancements, then you hereby grant the following license: a non-exclusive, royalty-free perpetual license to install, use, modify, prepare derivative works, incorporate into other computer software, distribute, and sublicense such Enhancements or derivative works thereof, in binary and source code form.
diff --git a/options/license/BSD-3-Clause-Modification b/options/license/BSD-3-Clause-Modification
deleted file mode 100644
index 4e337d7dbd..0000000000
--- a/options/license/BSD-3-Clause-Modification
+++ /dev/null
@@ -1,35 +0,0 @@
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-1. Redistributions in source code must retain the accompanying
- copyright notice, this list of conditions, and the following
- disclaimer.
-
-2. Redistributions in binary form must reproduce the accompanying
- copyright notice, this list of conditions, and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
-3. Names of the copyright holders must not be used to endorse or
- promote products derived from this software without prior
- written permission from the copyright holders.
-
-4. If any files are modified, you must cause the modified files to
- carry prominent notices stating that you changed the files and
- the date of any change.
-
-Disclaimer
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND
- ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
diff --git a/options/license/BSD-3-Clause-No-Military-License b/options/license/BSD-3-Clause-No-Military-License
deleted file mode 100644
index e06aa93b51..0000000000
--- a/options/license/BSD-3-Clause-No-Military-License
+++ /dev/null
@@ -1,16 +0,0 @@
-Copyright (c) year copyright holder. All Rights Reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1.
-Redistribution of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2.
-Redistribution in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3.
-Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT DESIGNED, LICENSED OR INTENDED FOR USE IN THE DESIGN, CONSTRUCTION, OPERATION OR MAINTENANCE OF ANY MILITARY FACILITY.
diff --git a/options/license/BSD-3-Clause-No-Nuclear-License b/options/license/BSD-3-Clause-No-Nuclear-License
deleted file mode 100644
index b37aa3058a..0000000000
--- a/options/license/BSD-3-Clause-No-Nuclear-License
+++ /dev/null
@@ -1,14 +0,0 @@
-
-Copyright 1994-2009 Sun Microsystems, Inc. All Rights Reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- * Redistribution of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
- * Redistribution in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- * Neither the name of Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-You acknowledge that this software is not designed, licensed or intended for use in the design, construction, operation or maintenance of any nuclear facility.
diff --git a/options/license/BSD-3-Clause-No-Nuclear-License-2014 b/options/license/BSD-3-Clause-No-Nuclear-License-2014
deleted file mode 100644
index 315c6d64c7..0000000000
--- a/options/license/BSD-3-Clause-No-Nuclear-License-2014
+++ /dev/null
@@ -1,16 +0,0 @@
-
-Copyright © 2008, 2014 Oracle and/or its affiliates. All rights reserved.
-
-Use is subject to license terms.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- * Neither the name of Oracle Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-You acknowledge that this software is not designed, licensed or intended for use in the design, construction, operation or maintenance of any nuclear facility.
diff --git a/options/license/BSD-3-Clause-No-Nuclear-Warranty b/options/license/BSD-3-Clause-No-Nuclear-Warranty
deleted file mode 100644
index 17457e6769..0000000000
--- a/options/license/BSD-3-Clause-No-Nuclear-Warranty
+++ /dev/null
@@ -1,14 +0,0 @@
-
-Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- - Redistribution of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
- - Redistribution in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- - Neither the name of Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-You acknowledge that this software is not designed or intended for use in the design, construction, operation or maintenance of any nuclear facility.
diff --git a/options/license/BSD-3-Clause-Open-MPI b/options/license/BSD-3-Clause-Open-MPI
deleted file mode 100644
index 166a95b130..0000000000
--- a/options/license/BSD-3-Clause-Open-MPI
+++ /dev/null
@@ -1,34 +0,0 @@
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-- Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer listed
- in this license in the documentation and/or other materials
- provided with the distribution.
-
-- Neither the name of the copyright holders nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-The copyright holders provide no reassurances that the source code
-provided does not infringe any patent, copyright, or any other
-intellectual property rights of third parties. The copyright holders
-disclaim any liability to any recipient for claims brought against
-recipient by any third party for infringement of that parties
-intellectual property rights.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/BSD-3-Clause-Sun b/options/license/BSD-3-Clause-Sun
deleted file mode 100644
index 1d86449d90..0000000000
--- a/options/license/BSD-3-Clause-Sun
+++ /dev/null
@@ -1,29 +0,0 @@
-Copyright (c) 2001-2013 Oracle and/or its affiliates. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-- Redistribution in binary form must reproduct the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-Neither the name of Sun Microsystems, Inc. or the names of
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-This software is provided "AS IS," without a warranty of any kind. ALL
-EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
-INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
-PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND
-ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES OR LIABILITIES
-SUFFERED BY LICENSEE AS A RESULT OF OR RELATING TO USE, MODIFICATION
-OR DISTRIBUTION OF THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
-SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
-OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
-PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
-LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE,
-EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/options/license/BSD-3-Clause-acpica b/options/license/BSD-3-Clause-acpica
deleted file mode 100644
index 9fb56c585a..0000000000
--- a/options/license/BSD-3-Clause-acpica
+++ /dev/null
@@ -1,26 +0,0 @@
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions, and the following disclaimer,
- without modification.
-2. Redistributions in binary form must reproduce at minimum a disclaimer
- substantially similar to the "NO WARRANTY" disclaimer below
- ("Disclaimer") and any redistribution must be conditioned upon
- including a substantially similar Disclaimer requirement for further
- binary redistribution.
-3. Neither the names of the above-listed copyright holders nor the names
- of any contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/BSD-3-Clause-flex b/options/license/BSD-3-Clause-flex
deleted file mode 100644
index 684b011026..0000000000
--- a/options/license/BSD-3-Clause-flex
+++ /dev/null
@@ -1,42 +0,0 @@
-Flex carries the copyright used for BSD software, slightly modified
-because it originated at the Lawrence Berkeley (not Livermore!) Laboratory,
-which operates under a contract with the Department of Energy:
-
-Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The Flex Project.
-
-Copyright (c) 1990, 1997 The Regents of the University of California.
-All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-Vern Paxson.
-
-The United States Government has rights in this work pursuant
-to contract no. DE-AC03-76SF00098 between the United States
-Department of Energy and the University of California.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-Neither the name of the University nor the names of its contributors
-may be used to endorse or promote products derived from this software
-without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.
-
-This basically says "do whatever you please with this software except
-remove this notice or take advantage of the University's (or the flex
-authors') name".
-
-Note that the "flex.skl" scanner skeleton carries no copyright notice.
-You are free to do whatever you please with scanners generated using flex;
-for them, you are not even bound by the above copyright.
diff --git a/options/license/BSD-4-Clause b/options/license/BSD-4-Clause
deleted file mode 100644
index 1e67e146f8..0000000000
--- a/options/license/BSD-4-Clause
+++ /dev/null
@@ -1,14 +0,0 @@
-Copyright (c) <year> <owner>. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. All advertising materials mentioning features or use of this software must display the following acknowledgement:
-This product includes software developed by the organization.
-
-4. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/BSD-4-Clause-Shortened b/options/license/BSD-4-Clause-Shortened
deleted file mode 100644
index 6812783d5f..0000000000
--- a/options/license/BSD-4-Clause-Shortened
+++ /dev/null
@@ -1,13 +0,0 @@
-License: BSD-4-Clause-Shortened
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that:
-
-(1) source code distributions retain the above copyright notice and this paragraph in its entirety,
-(2) distributions including binary code include the above copyright notice and this paragraph in its entirety in the documentation or other materials provided with the distribution, and
-(3) all advertising materials mentioning features or use of this software display the following acknowledgement:
-
-"This product includes software developed by the University of California, Lawrence Berkeley Laboratory and its contributors.''
-
-Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/options/license/BSD-4-Clause-UC b/options/license/BSD-4-Clause-UC
deleted file mode 100644
index 69edbe3242..0000000000
--- a/options/license/BSD-4-Clause-UC
+++ /dev/null
@@ -1,15 +0,0 @@
-BSD-4-Clause (University of California-Specific)
-
-Copyright [various years] The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors.
-
-4. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/BSD-4.3RENO b/options/license/BSD-4.3RENO
deleted file mode 100644
index c05b03cc0f..0000000000
--- a/options/license/BSD-4.3RENO
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright (c) 1987 Regents of the University of California.
-All rights reserved.
-
-Redistribution and use in source and binary forms are permitted
-provided that this notice is preserved and that due credit is given
-to the University of California at Berkeley. The name of the University
-may not be used to endorse or promote products derived from this
-software without specific written prior permission. This software
-is provided ``as is'' without express or implied warranty.
diff --git a/options/license/BSD-4.3TAHOE b/options/license/BSD-4.3TAHOE
deleted file mode 100644
index 413098d93c..0000000000
--- a/options/license/BSD-4.3TAHOE
+++ /dev/null
@@ -1,11 +0,0 @@
-Copyright (c) 1987 Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms are permitted provided
-that the above copyright notice and this paragraph are duplicated in all
-such forms and that any documentation, advertising materials, and other
-materials related to such distribution and use acknowledge that the software
-was developed by the University of California, Berkeley. The name of the
-University may not be used to endorse or promote products derived from this
-software without specific prior written permission. THIS SOFTWARE IS PROVIDED
-``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
-LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/options/license/BSD-Advertising-Acknowledgement b/options/license/BSD-Advertising-Acknowledgement
deleted file mode 100644
index cedb72e677..0000000000
--- a/options/license/BSD-Advertising-Acknowledgement
+++ /dev/null
@@ -1,37 +0,0 @@
-Copyright (c) 2001 David Giffin.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
-notice, this list of conditions and the following disclaimer in
-the documentation and/or other materials provided with the
-distribution.
-
-3. All advertising materials mentioning features or use of this
-software must display the following acknowledgment:
-"This product includes software developed by
-David Giffin <david@giffin.org>."
-
-4. Redistributions of any form whatsoever must retain the following
-acknowledgment:
-"This product includes software developed by
-David Giffin <david@giffin.org>."
-
-THIS SOFTWARE IS PROVIDED BY DAVID GIFFIN ``AS IS'' AND ANY
-EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID GIFFIN OR
-ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
-OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/BSD-Attribution-HPND-disclaimer b/options/license/BSD-Attribution-HPND-disclaimer
deleted file mode 100644
index 1272e1fe26..0000000000
--- a/options/license/BSD-Attribution-HPND-disclaimer
+++ /dev/null
@@ -1,37 +0,0 @@
-Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
-3. The name "Carnegie Mellon University" must not be used to
- endorse or promote products derived from this software without
- prior written permission. For permission or any other legal
- details, please contact
- Office of Technology Transfer
- Carnegie Mellon University
- 5000 Forbes Avenue
- Pittsburgh, PA 15213-3890
- (412) 268-4387, fax: (412) 268-7395
- tech-transfer@andrew.cmu.edu
-
-4. Redistributions of any form whatsoever must retain the following
- acknowledgment:
- "This product includes software developed by Computing Services
- at Carnegie Mellon University (http://www.cmu.edu/computing/)."
-
-CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
-THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
-FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
-AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
-OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/options/license/BSD-Inferno-Nettverk b/options/license/BSD-Inferno-Nettverk
deleted file mode 100644
index d10fe158a1..0000000000
--- a/options/license/BSD-Inferno-Nettverk
+++ /dev/null
@@ -1,41 +0,0 @@
- Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
- 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016
- 2017, 2018, 2019, 2020
- Inferno Nettverk A/S, Norway. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. The above copyright notice, this list of conditions and the following
- disclaimer must appear in all copies of the software, derivative works
- or modified versions, and any portions thereof, aswell as in all
- supporting documentation.
- 2. All advertising materials mentioning features or use of this software
- must display the following acknowledgement:
- This product includes software developed by
- Inferno Nettverk A/S, Norway.
- 3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- Inferno Nettverk A/S requests users of this software to return to
-
- Software Distribution Coordinator or sdc@inet.no
- Inferno Nettverk A/S
- Oslo Research Park
- Gaustadalléen 21
- NO-0349 Oslo
- Norway
-
- any improvements or extensions that they make and grant Inferno Nettverk A/S
- the rights to redistribute these changes.
diff --git a/options/license/BSD-Protection b/options/license/BSD-Protection
deleted file mode 100644
index 73820813ff..0000000000
--- a/options/license/BSD-Protection
+++ /dev/null
@@ -1,53 +0,0 @@
-BSD Protection License
-February 2002
-
-Preamble
---------
-
-The Berkeley Software Distribution ("BSD") license has proven very effective over the years at allowing for a wide spread of work throughout both commercial and non-commercial products. For programmers whose primary intention is to improve the general quality of available software, it is arguable that there is no better license than the BSD license, as it permits improvements to be used wherever they will help, without idealogical or metallic constraint.
-
-This is of particular value to those who produce reference implementations of proposed standards: The case of TCP/IP clearly illustrates that freely and universally available implementations leads the rapid acceptance of standards -- often even being used instead of a de jure standard (eg, OSI network models).
-
-With the rapid proliferation of software licensed under the GNU General Public License, however, the continued success of this role is called into question. Given that the inclusion of a few lines of "GPL-tainted" work into a larger body of work will result in restricted distribution -- and given that further work will likely build upon the "tainted" portions, making them difficult to remove at a future date -- there are inevitable circumstances where authors would, in order to protect their goal of providing for the widespread usage of their work, wish to guard against such "GPL-taint".
-
-In addition, one can imagine that companies which operate by producing and selling (possibly closed-source) code would wish to protect themselves against the rise of a GPL-licensed competitor. While under existing licenses this would mean not releasing their code under any form of open license, if a license existed under which they could incorporate any improvements back into their own (commercial) products then they might be far more willing to provide for non-closed distribution.
-
-For the above reasons, we put forth this "BSD Protection License": A license designed to retain the freedom granted by the BSD license to use licensed works in a wide variety of settings, both non-commercial and commercial, while protecting the work from having future contributors restrict that freedom.
-
-The precise terms and conditions for copying, distribution, and modification follow.
-
-BSD PROTECTION LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION, AND MODIFICATION
-----------------------------------------------------------------
-
-0. Definitions.
-
- a) "Program", below, refers to any program or work distributed under the terms of this license.
- b) A "work based on the Program", below, refers to either the Program or any derivative work under copyright law.
- c) "Modification", below, refers to the act of creating derivative works.
- d) "You", below, refers to each licensee.
-
-1. Scope.
-This license governs the copying, distribution, and modification of the Program. Other activities are outside the scope of this license; The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program.
-
-2. Verbatim copies.
-You may copy and distribute verbatim copies of the Program as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
-
-3. Modification and redistribution under closed license.
-You may modify your copy or copies of the Program, and distribute the resulting derivative works, provided that you meet the following conditions:
-
- a) The copyright notice and disclaimer on the Program must be reproduced and included in the source code, documentation, and/or other materials provided in a manner in which such notices are normally distributed.
- b) The derivative work must be clearly identified as such, in order that it may not be confused with the original work.
- c) The license under which the derivative work is distributed must expressly prohibit the distribution of further derivative works.
-
-4. Modification and redistribution under open license.
-You may modify your copy or copies of the Program, and distribute the resulting derivative works, provided that you meet the following conditions:
-
- a) The copyright notice and disclaimer on the Program must be reproduced and included in the source code, documentation, and/or other materials provided in a manner in which such notices are normally distributed.
- b) You must clearly indicate the nature and date of any changes made to the Program. The full details need not necessarily be included in the individual modified files, provided that each modified file is clearly marked as such and instructions are included on where the full details of the modifications may be found.
- c) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
-
-5. Implied acceptance.
-You may not copy or distribute the Program or any derivative works except as expressly provided under this license. Consequently, any such action will be taken as implied acceptance of the terms of this license.
-
-6. NO WARRANTY.
-THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT, EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/options/license/BSD-Source-Code b/options/license/BSD-Source-Code
deleted file mode 100644
index c41fc42733..0000000000
--- a/options/license/BSD-Source-Code
+++ /dev/null
@@ -1,10 +0,0 @@
-Copyright (c) 2011, Deusty, LLC
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
- * Neither the name of Deusty nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of Deusty, LLC.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/BSD-Source-beginning-file b/options/license/BSD-Source-beginning-file
deleted file mode 100644
index 6265f97608..0000000000
--- a/options/license/BSD-Source-beginning-file
+++ /dev/null
@@ -1,23 +0,0 @@
-Copyright (c) 1997 Justin T. Gibbs.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions, and the following disclaimer,
- without modification, immediately at the beginning of the file.
-2. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
diff --git a/options/license/BSD-Systemics b/options/license/BSD-Systemics
deleted file mode 100644
index 6ca8a26c33..0000000000
--- a/options/license/BSD-Systemics
+++ /dev/null
@@ -1,39 +0,0 @@
-Copyright (C) 1995, 1996 Systemics Ltd (http://www.systemics.com/)
-All rights reserved.
-
-This library and applications are FREE FOR COMMERCIAL AND NON-COMMERCIAL USE
-as long as the following conditions are adhered to.
-
-Copyright remains with Systemics Ltd, and as such any Copyright notices
-in the code are not to be removed. If this code is used in a product,
-Systemics should be given attribution as the author of the parts used.
-This can be in the form of a textual message at program startup or
-in documentation (online or textual) provided with the package.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. All advertising materials mentioning features or use of this software
- must display the following acknowledgement:
- This product includes software developed by Systemics Ltd (http://www.systemics.com/)
-
-THIS SOFTWARE IS PROVIDED BY SYSTEMICS LTD ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
-The licence and distribution terms for any publically available version or
-derivative of this code cannot be changed. i.e. this code cannot simply be
-copied and put under another distribution licence [including the GNU Public Licence.]
diff --git a/options/license/BSD-Systemics-W3Works b/options/license/BSD-Systemics-W3Works
deleted file mode 100644
index 73428e86ca..0000000000
--- a/options/license/BSD-Systemics-W3Works
+++ /dev/null
@@ -1,62 +0,0 @@
-Copyright (C) 1995, 1996 Systemics Ltd (http://www.systemics.com/)
-
-Modifications and Current Implimentation Copyright (C) 2000 W3Works, LLC.
-
-All rights reserved.
-
-Current implimentation contains modifications made by W3Works, LLC. The
-modifications remain copyright of W3Works, LLC and attribution for these
-modification should be made to W3Works, LLC. These modifications and
-this copyright must remain with this package.
-
-Additions to the Restrictions set out below are:
-1. All advertising materials mentioning features or use of this software
- must display the following acknowledgement:
- This product inculdes software developed by W3Works, LLC (http://www.w3works.com)
-
- NO ADDITIONAL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE EXTENDED BY THIS DISTRIBUTION.
-
- Any subsequent derrivations of this package must retainl this copyright.
-
-
-Original Copyright Below
-
-This library and applications are FREE FOR COMMERCIAL AND NON-COMMERCIAL USE
-as long as the following conditions are adhered to.
-
-Copyright remains with Systemics Ltd, and as such any Copyright notices
-in the code are not to be removed. If this code is used in a product,
-Systemics should be given attribution as the author of the parts used.
-This can be in the form of a textual message at program startup or
-in documentation (online or textual) provided with the package.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. All advertising materials mentioning features or use of this software
- must display the following acknowledgement:
- This product includes software developed by Systemics Ltd (http://www.systemics.com/)
-
- THIS SOFTWARE IS PROVIDED BY SYSTEMICS LTD ``AS IS'' AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
- The licence and distribution terms for any publically available version or
- derivative of this code cannot be changed. i.e. this code cannot simply be
- copied and put under another distribution licence
- [including the GNU Public Licence.]
diff --git a/options/license/BUSL-1.1 b/options/license/BUSL-1.1
deleted file mode 100644
index 2ef98f1be3..0000000000
--- a/options/license/BUSL-1.1
+++ /dev/null
@@ -1,71 +0,0 @@
-Business Source License 1.1
-
-License text copyright © 2017 MariaDB Corporation Ab, All Rights Reserved.
-"Business Source License" is a trademark of MariaDB Corporation Ab.
-
-Terms
-
-The Licensor hereby grants you the right to copy, modify, create derivative
-works, redistribute, and make non-production use of the Licensed Work. The
-Licensor may make an Additional Use Grant, above, permitting limited
-production use.
-
-Effective on the Change Date, or the fourth anniversary of the first publicly
-available distribution of a specific version of the Licensed Work under this
-License, whichever comes first, the Licensor hereby grants you rights under
-the terms of the Change License, and the rights granted in the paragraph
-above terminate.
-
-If your use of the Licensed Work does not comply with the requirements
-currently in effect as described in this License, you must purchase a
-commercial license from the Licensor, its affiliated entities, or authorized
-resellers, or you must refrain from using the Licensed Work.
-
-All copies of the original and modified Licensed Work, and derivative works
-of the Licensed Work, are subject to this License. This License applies
-separately for each version of the Licensed Work and the Change Date may vary
-for each version of the Licensed Work released by Licensor.
-
-You must conspicuously display this License on each original or modified copy
-of the Licensed Work. If you receive the Licensed Work in original or
-modified form from a third party, the terms and conditions set forth in this
-License apply to your use of that work.
-
-Any use of the Licensed Work in violation of this License will automatically
-terminate your rights under this License for the current and all other
-versions of the Licensed Work.
-
-This License does not grant you any right in any trademark or logo of
-Licensor or its affiliates (provided that you may use a trademark or logo of
-Licensor as expressly required by this License).
-
-TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
-AN “AS IS†BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
-EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
-TITLE.
-
-MariaDB hereby grants you permission to use this License’s text to license
-your works, and to refer to it using the trademark “Business Source Licenseâ€,
-as long as you comply with the Covenants of Licensor below.
-
-Covenants of Licensor
-
-In consideration of the right to use this License’s text and the “Business
-Source License†name and trademark, Licensor covenants to MariaDB, and to all
-other recipients of the licensed work to be provided by Licensor:
-
-1. To specify as the Change License the GPL Version 2.0 or any later version,
- or a license that is compatible with GPL Version 2.0 or a later version,
- where “compatible†means that software provided under the Change License can
- be included in a program with software provided under GPL Version 2.0 or a
- later version. Licensor may specify additional Change Licenses without
- limitation.
-
-2. To either: (a) specify an additional grant of rights to use that does not
- impose any additional restriction on the right granted in this License, as
- the Additional Use Grant; or (b) insert the text “Noneâ€.
-
-3. To specify a Change Date.
-
-4. Not to modify this License in any other way.
diff --git a/options/license/Baekmuk b/options/license/Baekmuk
deleted file mode 100644
index b86efc04a0..0000000000
--- a/options/license/Baekmuk
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright (c) 1986-2002 Kim Jeong-Hwan All rights reserved.
-
-Permission to use, copy, modify and distribute this font
-is hereby granted, provided that both the copyright notice
-and this permission notice appear in all copies of the
-font, derivative works or modified versions, and that the
-following acknowledgement appear in supporting documentation:
-Baekmuk Batang, Baekmuk Dotum, Baekmuk Gulim, and Baekmuk
-Headline are registered trademarks owned by Kim Jeong-Hwan.
diff --git a/options/license/Bahyph b/options/license/Bahyph
deleted file mode 100644
index a42e875ac3..0000000000
--- a/options/license/Bahyph
+++ /dev/null
@@ -1,11 +0,0 @@
-COPYRIGHT NOTICE
-
-These patterns and the generating sh script are Copyright (c) GMV 1991
-
-These patterns were developed for internal GMV use and are made public in the hope that they will benefit others. Also, spreading these patterns throughout the Spanish-language TeX community is expected to provide back-benefits to GMV in that it can help keeping GMV in the mainstream of spanish users.
-
-However, this is given for free and WITHOUT ANY WARRANTY. Under no circumstances can Julio Sanchez, GMV, Jos'e A. Ma~nas or any agents or representatives thereof be held responsible for any errors in this software nor for any damages derived from its use, even in case any of the above has been notified of the possibility of such damages. If any such situation arises, you responsible for repair. Use of this software is an explicit acceptance of these conditions.
-
-You can use this software for any purpose. You cannot delete this copyright notice. If you change this software, you must include comments explaining who, when and why. You are kindly requested to send any changes to tex@gmv.es. If you change the generating script, you must include code in it such that any output is clearly labeled as generated by a modified script. Despite the lack of warranty, we would like to hear about any problem you find. Please report problems to tex@gmv.es.
-
-END OF COPYRIGHT NOTICE
diff --git a/options/license/Barr b/options/license/Barr
deleted file mode 100644
index 07f32df0ed..0000000000
--- a/options/license/Barr
+++ /dev/null
@@ -1 +0,0 @@
-This is a package of commutative diagram macros built on top of Xy-pic by Michael Barr (email: barr@barrs.org). Its use is unrestricted. It may be freely distributed, unchanged, for non-commercial or commercial use. If changed, it must be renamed. Inclusion in a commercial software package is also permitted, but I would appreciate receiving a free copy for my personal examination and use. There are no guarantees that this package is good for anything. I have tested it with LaTeX 2e, LaTeX 2.09 and Plain TeX. Although I know of no reason it will not work with AMSTeX, I have not tested it.
diff --git a/options/license/Beerware b/options/license/Beerware
deleted file mode 100644
index c7ffc1a04e..0000000000
--- a/options/license/Beerware
+++ /dev/null
@@ -1 +0,0 @@
-"THE BEER-WARE LICENSE" (Revision 42): <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp
diff --git a/options/license/Bison-exception-1.24 b/options/license/Bison-exception-1.24
deleted file mode 100644
index 7f3c3009ee..0000000000
--- a/options/license/Bison-exception-1.24
+++ /dev/null
@@ -1,4 +0,0 @@
-As a special exception, when this file is copied by Bison into a
-Bison output file, you may use that output file without restriction.
-This special exception was added by the Free Software Foundation
-in version 1.24 of Bison.
diff --git a/options/license/Bison-exception-2.2 b/options/license/Bison-exception-2.2
deleted file mode 100644
index 91140decee..0000000000
--- a/options/license/Bison-exception-2.2
+++ /dev/null
@@ -1,5 +0,0 @@
-Bison Exception
-
-As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception.
-
-This special exception was added by the Free Software Foundation in version 2.2 of Bison.
diff --git a/options/license/BitTorrent-1.0 b/options/license/BitTorrent-1.0
deleted file mode 100644
index 16e3b9a647..0000000000
--- a/options/license/BitTorrent-1.0
+++ /dev/null
@@ -1,330 +0,0 @@
-BitTorrent Open Source License
-
-Version 1.0
-
-This BitTorrent Open Source License (the "License") applies to the BitTorrent client and related software products as
-well as any updates or maintenance releases of that software ("BitTorrent Products") that are distributed by
-BitTorrent, Inc. ("Licensor"). Any BitTorrent Product licensed pursuant to this License is a Licensed Product.
-Licensed Product, in its entirety, is protected by U.S. copyright law. This License identifies the terms under which
-you may use, copy, distribute or modify Licensed Product.
-
-Preamble
-
-This Preamble is intended to describe, in plain English, the nature and scope of this License. However, this
-Preamble is not a part of this license. The legal effect of this License is dependent only upon the terms of the
-License and not this Preamble.
-
-This License complies with the Open Source Definition and is derived from the Jabber Open Source License 1.0 (the
-"JOSL"), which has been approved by Open Source Initiative. Sections 4(c) and 4(f)(iii) from the JOSL have been
-dropped.
-
-This License provides that:
-
-1. You may use, sell or give away the Licensed Product, alone or as a component of an aggregate software
-distribution containing programs from several different sources. No royalty or other fee is required.
-
-2. Both Source Code and executable versions of the Licensed Product, including Modifications made by previous
-Contributors, are available for your use. (The terms "Licensed Product," "Modifications," "Contributors" and "Source
-Code" are defined in the License.)
-
-3. You are allowed to make Modifications to the Licensed Product, and you can create Derivative Works from it.
-(The term "Derivative Works" is defined in the License.)
-
-4. By accepting the Licensed Product under the provisions of this License, you agree that any Modifications you
-make to the Licensed Product and then distribute are governed by the provisions of this License. In particular, you
-must make the Source Code of your Modifications available to others.
-
-5. You may use the Licensed Product for any purpose, but the Licensor is not providing you any warranty
-whatsoever, nor is the Licensor accepting any liability in the event that the Licensed Product doesn't work properly
-or causes you any injury or damages.
-
-6. If you sublicense the Licensed Product or Derivative Works, you may charge fees for warranty or support, or
-for accepting indemnity or liability obligations to your customers. You cannot charge for the Source Code.
-
-7. If you assert any patent claims against the Licensor relating to the Licensed Product, or if you breach any
-terms of the License, your rights to the Licensed Product under this License automatically terminate.
-
-You may use this License to distribute your own Derivative Works, in which case the provisions of this License will
-apply to your Derivative Works just as they do to the original Licensed Product.
-
-Alternatively, you may distribute your Derivative Works under any other OSI-approved Open Source license, or under a
-proprietary license of your choice. If you use any license other than this License, however, you must continue to
-fulfill the requirements of this License (including the provisions relating to publishing the Source Code) for those
-portions of your Derivative Works that consist of the Licensed Product, including the files containing Modifications.
-
-New versions of this License may be published from time to time. You may choose to continue to use the license
-terms in this version of the License or those from the new version. However, only the Licensor has the right to
-change the License terms as they apply to the Licensed Product.
-
-This License relies on precise definitions for certain terms. Those terms are defined when they are first used, and
-the definitions are repeated for your convenience in a Glossary at the end of the License.
-
-
-License Terms
-
-1. Grant of License From Licensor. Licensor hereby grants you a world-wide, royalty-free, non-exclusive
-license, subject to third party intellectual property claims, to do the following:
-
-a. Use, reproduce, modify, display, perform, sublicense and distribute any Modifications created by such
-Contributor or portions thereof, in both Source Code or as an executable program, either on an unmodified basis or as
-part of Derivative Works.
-
-b. Under claims of patents now or hereafter owned or controlled by Contributor, to make, use, sell, offer for
-sale, have made, and/or otherwise dispose of Modifications or portions thereof, but solely to the extent that any
-such claim is necessary to enable you to make, use, sell, offer for sale, have made, and/or otherwise dispose of
-Modifications or portions thereof or Derivative Works thereof.
-
-
-2. Grant of License to Modifications From Contributor. "Modifications" means any additions to or deletions from the
-substance or structure of (i) a file containing Licensed Product, or (ii) any new file that contains any part of
-Licensed Product. Hereinafter in this License, the term "Licensed Product" shall include all previous Modifications
-that you receive from any Contributor. By application of the provisions in Section 4(a) below, each person or entity
-who created or contributed to the creation of, and distributed, a Modification (a "Contributor") hereby grants you a
-world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims, to do the
-following:
-
- 1. Use, reproduce, modify, display, perform, sublicense and distribute any Modifications created by such
-Contributor or portions thereof, in both Source Code or as an executable program, either on an unmodified basis or as
-part of Derivative Works.
-
- 2. Under claims of patents now or hereafter owned or controlled by Contributor, to make, use, sell, offer for
-sale, have made, and/or otherwise dispose of Modifications or portions thereof, but solely to the extent that any
-such claim is necessary to enable you to make, use, sell, offer for sale, have made, and/or otherwise dispose of
-Modifications or portions thereof or Derivative Works thereof.
-
-
-3. Exclusions From License Grant. Nothing in this License shall be deemed to grant any rights to trademarks,
-copyrights, patents, trade secrets or any other intellectual property of Licensor or any Contributor except as
-expressly stated herein. No patent license is granted separate from the Licensed Product, for code that you delete
-from the Licensed Product, or for combinations of the Licensed Product with other software or hardware. No right is
-granted to the trademarks of Licensor or any Contributor even if such marks are included in the Licensed Product.
-Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this
-License any code that Licensor otherwise would have a right to license.
-
-
-4. Your Obligations Regarding Distribution.
-
-a. Application of This License to Your Modifications. As an express condition for your use of the Licensed
-Product, you hereby agree that any Modifications that you create or to which you contribute, and which you
-distribute, are governed by the terms of this License including, without limitation, Section 2. Any Modifications
-that you create or to which you contribute may be distributed only under the terms of this License or a future
-version of this License released under Section 7. You must include a copy of this License with every copy of the
-Modifications you distribute. You agree not to offer or impose any terms on any Source Code or executable version of
-the Licensed Product or Modifications that alter or restrict the applicable version of this License or the
-recipients' rights hereunder. However, you may include an additional document offering the additional rights
-described in Section 4(d).
-
-b. Availability of Source Code. You must make available, under the terms of this License, the Source Code of
-the Licensed Product and any Modifications that you distribute, either on the same media as you distribute any
-executable or other form of the Licensed Product, or via a mechanism generally accepted in the software development
-community for the electronic transfer of data (an "Electronic Distribution Mechanism"). The Source Code for any
-version of Licensed Product or Modifications that you distribute must remain available for at least twelve (12)
-months after the date it initially became available, or at least six (6) months after a subsequent version of said
-Licensed Product or Modifications has been made available. You are responsible for ensuring that the Source Code
-version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
-c. Intellectual Property Matters.
-
- i. Third Party Claims. If you have knowledge that a license to a third
-party's intellectual property right is required to exercise the rights granted by this License, you must include a
-text file with the Source Code distribution titled "LEGAL" that describes the claim and the party making the claim in
-sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after you make any
-Modifications available as described in Section 4(b), you shall promptly modify the LEGAL file in all copies you make
-available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups)
-reasonably calculated to inform those who received the Licensed Product from you that new knowledge has been
-obtained.
-
- ii. Contributor APIs. If your Modifications include an application
-programming interface ("API") and you have knowledge of patent licenses that are reasonably necessary to implement
-that API, you must also include this information in the LEGAL file.
-
- iii. Representations. You represent that, except as disclosed pursuant to
-4(c)(i) above, you believe that any Modifications you distribute are your original creations and that you have
-sufficient rights to grant the rights conveyed by this License.
-
-d. Required Notices. You must duplicate this License in any documentation you provide along with the Source
-Code of any Modifications you create or to which you contribute, and which you distribute, wherever you describe
-recipients' rights relating to Licensed Product. You must duplicate the notice contained in Exhibit A (the "Notice")
-in each file of the Source Code of any copy you distribute of the Licensed Product. If you created a Modification,
-you may add your name as a Contributor to the Notice. If it is not possible to put the Notice in a particular Source
-Code file due to its structure, then you must include such Notice in a location (such as a relevant directory file)
-where a user would be likely to look for such a notice. You may choose to offer, and charge a fee for, warranty,
-support, indemnity or liability obligations to one or more recipients of Licensed Product. However, you may do so
-only on your own behalf, and not on behalf of the Licensor or any Contributor. You must make it clear that any such
-warranty, support, indemnity or liability obligation is offered by you alone, and you hereby agree to indemnify the
-Licensor and every Contributor for any liability incurred by the Licensor or such Contributor as a result of
-warranty, support, indemnity or liability terms you offer.
-
-e. Distribution of Executable Versions. You may distribute Licensed Product as an executable program under a
-license of your choice that may contain terms different from this License provided (i) you have satisfied the
-requirements of Sections 4(a) through 4(d) for that distribution, (ii) you include a conspicuous notice in the
-executable version, related documentation and collateral materials stating that the Source Code version of the
-Licensed Product is available under the terms of this License, including a description of how and where you have
-fulfilled the obligations of Section 4(b), and (iii) you make it clear that any terms that differ from this License
-are offered by you alone, not by Licensor or any Contributor. You hereby agree to indemnify the Licensor and every
-Contributor for any liability incurred by Licensor or such Contributor as a result of any terms you offer.
-
-f. Distribution of Derivative Works. You may create Derivative Works (e.g., combinations of some or all of the
-Licensed Product with other code) and distribute the Derivative Works as products under any other license you select,
-with the proviso that the requirements of this License are fulfilled for those portions of the Derivative Works that
-consist of the Licensed Product or any Modifications thereto.
-
-
-5. Inability to Comply Due to Statute or Regulation. If it is impossible for you to comply with any of the
-terms of this License with respect to some or all of the Licensed Product due to statute, judicial order, or
-regulation, then you must (i) comply with the terms of this License to the maximum extent possible, (ii) cite the
-statute or regulation that prohibits you from adhering to the License, and (iii) describe the limitations and the
-code they affect. Such description must be included in the LEGAL file described in Section 4(d), and must be included
-with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such
-description must be sufficiently detailed for a recipient of ordinary skill at computer programming to be able to
-understand it.
-
-
-6. Application of This License. This License applies to code to which Licensor or Contributor has attached the
-Notice in Exhibit A, which is incorporated herein by this reference.
-
-
-7. Versions of This License.
-
-a. New Versions. Licensor may publish from time to time revised and/or new versions of the License.
-
-b. Effect of New Versions. Once Licensed Product has been published under a particular version of the License,
-you may always continue to use it under the terms of that version. You may also choose to use such Licensed Product
-under the terms of any subsequent version of the License published by Licensor. No one other than Licensor has the
-right to modify the terms applicable to Licensed Product created under this License.
-
-c. Derivative Works of this License. If you create or use a modified version of this License, which you may do
-only in order to apply it to software that is not already a Licensed Product under this License, you must rename your
-license so that it is not confusingly similar to this License, and must make it clear that your license contains
-terms that differ from this License. In so naming your license, you may not use any trademark of Licensor or any
-Contributor.
-
-
-8. Disclaimer of Warranty. LICENSED PRODUCT IS PROVIDED UNDER THIS LICENSE ON AN AS IS BASIS, WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE LICENSED PRODUCT IS FREE
-OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND
-PERFORMANCE OF THE LICENSED PRODUCT IS WITH YOU. SHOULD LICENSED PRODUCT PROVE DEFECTIVE IN ANY RESPECT, YOU (AND
-NOT THE LICENSOR OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS
-DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF LICENSED PRODUCT IS AUTHORIZED
-HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-
-9. Termination.
-
-a. Automatic Termination Upon Breach. This license and the rights granted hereunder will terminate
-automatically if you fail to comply with the terms herein and fail to cure such breach within thirty (30) days of
-becoming aware of the breach. All sublicenses to the Licensed Product that are properly granted shall survive any
-termination of this license. Provisions that, by their nature, must remain in effect beyond the termination of this
-License, shall survive.
-
-b. Termination Upon Assertion of Patent Infringement. If you initiate litigation by asserting a patent
-infringement claim (excluding declaratory judgment actions) against Licensor or a Contributor (Licensor or
-Contributor against whom you file such an action is referred to herein as Respondent) alleging that Licensed Product
-directly or indirectly infringes any patent, then any and all rights granted by such Respondent to you under Sections
-1 or 2 of this License shall terminate prospectively upon sixty (60) days notice from Respondent (the "Notice
-Period") unless within that Notice Period you either agree in writing (i) to pay Respondent a mutually agreeable
-reasonably royalty for your past or future use of Licensed Product made by such Respondent, or (ii) withdraw your
-litigation claim with respect to Licensed Product against such Respondent. If within said Notice Period a reasonable
-royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not
-withdrawn, the rights granted by Licensor to you under Sections 1 and 2 automatically terminate at the expiration of
-said Notice Period.
-
-c. Reasonable Value of This License. If you assert a patent infringement claim against Respondent alleging
-that Licensed Product directly or indirectly infringes any patent where such claim is resolved (such as by license or
-settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses
-granted by said Respondent under Sections 1 and 2 shall be taken into account in determining the amount or value of
-any payment or license.
-
-d. No Retroactive Effect of Termination. In the event of termination under Sections 9(a) or 9(b) above, all
-end user license agreements (excluding licenses to distributors and resellers) that have been validly granted by you
-or any distributor hereunder prior to termination shall survive termination.
-
-
-10. Limitation of Liability. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE),
-CONTRACT, OR OTHERWISE, SHALL THE LICENSOR, ANY CONTRIBUTOR, OR ANY DISTRIBUTOR OF LICENSED PRODUCT, OR ANY SUPPLIER
-OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF
-ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
-MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE
-POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
-RESULTING FROM SUCH PARTYS NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO
-NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY
-NOT APPLY TO YOU.
-
-
-11. Responsibility for Claims. As between Licensor and Contributors, each party is responsible for claims and
-damages arising, directly or indirectly, out of its utilization of rights under this License. You agree to work with
-Licensor and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or
-shall be deemed to constitute any admission of liability.
-
-
-12. U.S. Government End Users. The Licensed Product is a commercial item, as that term is defined in 48 C.F.R.
-2.101 (Oct. 1995), consisting of commercial computer software and commercial computer software documentation, as such
-terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through
-227.7202-4 (June 1995), all U.S. Government End Users acquire Licensed Product with only those rights set forth
-herein.
-
-
-13. Miscellaneous. This License represents the complete agreement concerning the subject matter hereof. If any
-provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary
-to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable
-law, if any, provides otherwise), excluding its conflict-of-law provisions. You expressly agree that any litigation
-relating to this license shall be subject to the jurisdiction of the Federal Courts of the Northern District of
-California or the Superior Court of the County of Santa Clara, California (as appropriate), with venue lying in Santa
-Clara County, California, with the losing party responsible for costs including, without limitation, court costs and
-reasonable attorneys fees and expenses. The application of the United Nations Convention on Contracts for the
-International Sale of Goods is expressly excluded. You and Licensor expressly waive any rights to a jury trial in
-any litigation concerning Licensed Product or this License. Any law or regulation that provides that the language of
-a contract shall be construed against the drafter shall not apply to this License.
-
-
-14. Definition of You in This License. You throughout this License, whether in upper or lower case, means an
-individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a
-future version of this License issued under Section 7. For legal entities, you includes any entity that controls, is
-controlled by, or is under common control with you. For purposes of this definition, control means (i) the power,
-direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii)
-ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-
-15. Glossary. All defined terms in this License that are used in more than one Section of this License are repeated
-here, in alphabetical order, for the convenience of the reader. The Section of this License in which each defined
-term is first used is shown in parentheses.
-
-Contributor: Each person or entity who created or contributed to the creation of, and distributed, a Modification.
-(See Section 2)
-
-Derivative Works: That term as used in this License is defined under U.S. copyright law. (See Section 1(b))
-
-License: This BitTorrent Open Source License. (See first paragraph of License)
-
-Licensed Product: Any BitTorrent Product licensed pursuant to this License. The term "Licensed Product" includes
-all previous Modifications from any Contributor that you receive. (See first paragraph of License and Section 2)
-
-Licensor: BitTorrent, Inc. (See first paragraph of License)
-
-Modifications: Any additions to or deletions from the substance or structure of (i) a file containing Licensed
-Product, or (ii) any new file that contains any part of Licensed Product. (See Section 2)
-
-Notice: The notice contained in Exhibit A. (See Section 4(e))
-
-Source Code: The preferred form for making modifications to the Licensed Product, including all modules contained
-therein, plus any associated interface definition files, scripts used to control compilation and installation of an
-executable program, or a list of differential comparisons against the Source Code of the Licensed Product. (See
-Section 1(a))
-
-You: This term is defined in Section 14 of this License.
-
-
-EXHIBIT A
-
-The Notice below must appear in each file of the Source Code of any copy you distribute of the Licensed Product or
-any hereto. Contributors to any Modifications may add their own copyright notices to identify their own
-contributions.
-
-License:
-
-The contents of this file are subject to the BitTorrent Open Source License Version 1.0 (the License). You may not
-copy or use this file, in either source code or executable form, except in compliance with the License. You may
-obtain a copy of the License at http://www.bittorrent.com/license/.
-
-Software distributed under the License is distributed on an AS IS basis, WITHOUT WARRANTY OF ANY KIND, either express
-or implied. See the License for the specific language governing rights and limitations under the License.
diff --git a/options/license/BitTorrent-1.1 b/options/license/BitTorrent-1.1
deleted file mode 100644
index fa5bd9df24..0000000000
--- a/options/license/BitTorrent-1.1
+++ /dev/null
@@ -1,137 +0,0 @@
-BitTorrent Open Source License
-Version 1.1
-
-This BitTorrent Open Source License (the "License") applies to the BitTorrent client and related software products as well as any updates or maintenance releases of that software ("BitTorrent Products") that are distributed by BitTorrent, Inc. ("Licensor"). Any BitTorrent Product licensed pursuant to this License is a Licensed Product. Licensed Product, in its entirety, is protected by U.S. copyright law. This License identifies the terms under which you may use, copy, distribute or modify Licensed Product.
-
-Preamble
-
-This Preamble is intended to describe, in plain English, the nature and scope of this License. However, this Preamble is not a part of this license. The legal effect of this License is dependent only upon the terms of the License and not this Preamble.
-
-This License complies with the Open Source Definition and is derived from the Jabber Open Source License 1.0 (the "JOSL"), which has been approved by Open Source Initiative. Sections 4(c) and 4(f)(iii) from the JOSL have been deleted.
-
-This License provides that:
-
-1. You may use or give away the Licensed Product, alone or as a component of an aggregate software distribution containing programs from several different sources. No royalty or other fee is required.
-
-2. Both Source Code and executable versions of the Licensed Product, including Modifications made by previous Contributors, are available for your use. (The terms "Licensed Product," "Modifications," "Contributors" and "Source Code" are defined in the License.)
-
-3. You are allowed to make Modifications to the Licensed Product, and you can create Derivative Works from it. (The term "Derivative Works" is defined in the License.)
-
-4. By accepting the Licensed Product under the provisions of this License, you agree that any Modifications you make to the Licensed Product and then distribute are governed by the provisions of this License. In particular, you must make the Source Code of your Modifications available to others free of charge and without a royalty.
-
-5. You may sell, accept donations or otherwise receive compensation for executable versions of a Licensed Product, without paying a royalty or other fee to the Licensor or any Contributor, provided that such executable versions contain your or another Contributor's material Modifications. For the avoidance of doubt, to the extent your executable version of a Licensed Product does not contain your or another Contributor's material Modifications, you may not sell, accept donations or otherwise receive compensation for such executable.
-
-You may use the Licensed Product for any purpose, but the Licensor is not providing you any warranty whatsoever, nor is the Licensor accepting any liability in the event that the Licensed Product doesn't work properly or causes you any injury or damages.
-
-6. If you sublicense the Licensed Product or Derivative Works, you may charge fees for warranty or support, or for accepting indemnity or liability obligations to your customers. You cannot charge for, sell, accept donations or otherwise receive compensation for the Source Code.
-
-7. If you assert any patent claims against the Licensor relating to the Licensed Product, or if you breach any terms of the License, your rights to the Licensed Product under this License automatically terminate.
-You may use this License to distribute your own Derivative Works, in which case the provisions of this License will apply to your Derivative Works just as they do to the original Licensed Product.
-
-Alternatively, you may distribute your Derivative Works under any other OSI-approved Open Source license, or under a proprietary license of your choice. If you use any license other than this License, however, you must continue to fulfill the requirements of this License (including the provisions relating to publishing the Source Code) for those portions of your Derivative Works that consist of the Licensed Product, including the files containing
-Modifications.
-
-New versions of this License may be published from time to time in connection with new versions of a Licensed Product or otherwise. You may choose to continue to use the license terms in this version of the License for the Licensed Product that was originally licensed hereunder, however, the new versions of this License will at all times apply to new versions of the Licensed Product released by Licensor after the release of the new version of this License. Only the Licensor has the right to change the License terms as they apply to the Licensed Product.
-
-This License relies on precise definitions for certain terms. Those terms are defined when they are first used, and the definitions are repeated for your convenience in a Glossary at the end of the License.
-
-License Terms
-
-1. Grant of License From Licensor. Subject to the terms and conditions of this License, Licensor hereby grants you a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims, to do the following:
-
- a. Use, reproduce, modify, display, perform, sublicense and distribute any Modifications created by a Contributor or portions thereof, in both Source Code or as an executable program, either on an unmodified basis or as part of Derivative Works.
- b. Under claims of patents now or hereafter owned or controlled by Contributor, to make, use, sell, offer for sale, have made, and/or otherwise dispose of Modifications or portions thereof, but solely to the extent that any such claim is necessary to enable you to make, use, sell, offer for sale, have made, and/or otherwise dispose of Modifications or portions thereof or Derivative Works thereof.
-
-2. Grant of License to Modifications From Contributor. "Modifications" means any additions to or deletions from the substance or structure of (i) a file containing a Licensed Product, or (ii) any new file that contains any part of a Licensed Product. Hereinafter in this License, the term "Licensed Product" shall include all previous Modifications that you receive from any Contributor. Subject to the terms and conditions of this License, By application of the provisions in Section 4(a) below, each person or entity who created or contributed to the creation of, and distributed, a Modification (a "Contributor") hereby grants you a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims, to do the following:
-
- a. Use, reproduce, modify, display, perform, sublicense and distribute any Modifications created by such Contributor or portions thereof, in both Source Code or as an executable program, either on an unmodified basis or as part of Derivative Works.
-
- b. Under claims of patents now or hereafter owned or controlled by Contributor, to make, use, sell, offer for sale, have made, and/or otherwise dispose of Modifications or portions thereof, but solely to the extent that any such claim is necessary to enable you to make, use, sell, offer for sale, have made, and/or otherwise dispose of Modifications or portions thereof or Derivative Works thereof.
-
-3. Exclusions From License Grant. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor or any Contributor except as expressly stated herein. No patent license is granted separate from the Licensed Product, for code that you delete from the Licensed Product, or for combinations of the Licensed Product with other software or hardware. No right is granted to the trademarks of Licensor or any Contributor even if such marks are included in the Licensed Product. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any code that Licensor otherwise would have a right to license. As an express condition for your use of the Licensed Product, you hereby agree that you will not, without the prior written consent of Licensor, use any trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor or any Contributor except as expressly stated herein. For the avoidance of doubt and without limiting the foregoing, you hereby agree that you will not use or display any trademark of Licensor or any Contributor in any domain name, directory filepath, advertisement, link or other reference to you in any manner or in any media.
-
-4. Your Obligations Regarding Distribution.
-
- a. Application of This License to Your Modifications. As an express condition for your use of the Licensed Product, you hereby agree that any Modifications that you create or to which you contribute, and which you distribute, are governed by the terms of this License including, without limitation, Section 2. Any Modifications that you create or to which you contribute may be distributed only under the terms of this License or a future version of this License released under Section 7. You must include a copy of this License with every copy of the Modifications you distribute. You agree not to offer or impose any terms on any Source Code or executable version of the Licensed Product or Modifications that alter or restrict the applicable version of this License or the recipients' rights hereunder. However, you may include an additional document offering the additional rights described in Section 4(d).
-
- b. Availability of Source Code. You must make available, without charge, under the terms of this License, the Source Code of the Licensed Product and any Modifications that you distribute, either on the same media as you distribute any executable or other form of the Licensed Product, or via a mechanism generally accepted in the software development community for the electronic transfer of data (an "Electronic Distribution Mechanism"). The Source Code for any version of Licensed Product or Modifications that you distribute must remain available for as long as any executable or other form of the Licensed Product is distributed by you. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
- c. Intellectual Property Matters.
-
- i. Third Party Claims. If you have knowledge that a license to a third party's intellectual property right is required to exercise the rights granted by this License, you must include a text file with the Source Code distribution titled "LEGAL" that describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after you make any Modifications available as described in Section 4(b), you shall promptly modify the LEGAL file in all copies you make available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Licensed Product from you that new knowledge has been obtained.
- ii. Contributor APIs. If your Modifications include an application programming interface ("API") and you have knowledge of patent licenses that are reasonably necessary to implement that API, you must also include this information in the LEGAL file.
- iii. Representations. You represent that, except as disclosed pursuant to 4(c)(i) above, you believe that any Modifications you distribute are your original creations and that you have sufficient rights to grant the rights conveyed by this License.
-
- d. Required Notices. You must duplicate this License in any documentation you provide along with the Source Code of any Modifications you create or to which you contribute, and which you distribute, wherever you describe recipients' rights relating to Licensed Product. You must duplicate the notice contained in Exhibit A (the "Notice") in each file of the Source Code of any copy you distribute of the Licensed Product. If you created a Modification, you may add your name as a Contributor to the Notice. If it is not possible to put the Notice in a particular Source Code file due to its structure, then you must include such Notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice. You may choose to offer, and charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Licensed Product. However, you may do so only on your own behalf, and not on behalf of the Licensor or any Contributor. You must make it clear that any such warranty, support, indemnity or liability obligation is offered by you alone, and you hereby agree to indemnify the Licensor and every Contributor for any liability incurred by the Licensor or such Contributor as a result of warranty, support, indemnity or liability terms you offer.
-
- e. Distribution of Executable Versions. You may distribute Licensed Product as an executable program under a license of your choice that may contain terms different from this License provided (i) you have satisfied the requirements of Sections 4(a) through 4(d) for that distribution, (ii) you include a conspicuous notice in the executable version, related documentation and collateral materials stating that the Source Code version of the Licensed Product is available under the terms of this License, including a description of how and where you have fulfilled the obligations of Section 4(b), and (iii) you make it clear that any terms that differ from this License are offered by you alone, not by Licensor or any Contributor. You hereby agree to indemnify the Licensor and every Contributor for any liability incurred by Licensor or such Contributor as a result of any terms you offer.
-
- f. Distribution of Derivative Works. You may create Derivative Works (e.g., combinations of some or all of the Licensed Product with other code) and distribute the Derivative Works as products under any other license you select, with the proviso that the requirements of this License are fulfilled for those portions of the Derivative Works that consist of the Licensed Product or any Modifications thereto.
-
- g. Compensation for Distribution of Executable Versions of Licensed Products, Modifications or Derivative Works. Notwithstanding any provision of this License to the contrary, by distributing, selling, licensing, sublicensing or otherwise making available any Licensed Product, or Modification or Derivative Work thereof, you and Licensor hereby acknowledge and agree that you may sell, license or sublicense for a fee, accept donations or otherwise receive compensation for executable versions of a Licensed Product, without paying a royalty or other fee to the Licensor or any other Contributor, provided that such executable versions (i) contain your or another Contributor's material Modifications, or (ii) are otherwise material Derivative Works. For purposes of this License, an executable version of the Licensed Product will be deemed to contain a material Modification, or will otherwise be deemed a material Derivative Work, if (a) the Licensed Product is modified with your own or a third party's software programs or other code, and/or the Licensed Product is combined with a number of your own or a third party's software programs or code, respectively, and (b) such software programs or code add or contribute material value, functionality or features to the License Product. For the avoidance of doubt, to the extent your executable version of a Licensed Product does not contain your or another Contributor's material Modifications or is otherwise not a material Derivative Work, in each case as contemplated herein, you may not sell, license or sublicense for a fee, accept donations or otherwise receive compensation for such executable. Additionally, without limitation of the foregoing and notwithstanding any provision of this License to the contrary, you cannot charge for, sell, license or sublicense for a fee, accept donations or otherwise receive compensation for the Source Code.
-
-5. Inability to Comply Due to Statute or Regulation. If it is impossible for you to comply with any of the terms of this License with respect to some or all of the Licensed Product due to statute, judicial order, or regulation, then you must (i) comply with the terms of this License to the maximum extent possible, (ii) cite the statute or regulation that prohibits you from adhering to the License, and (iii) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 4(d), and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill at computer programming to be able to understand it.
-
-6. Application of This License. This License applies to code to which Licensor or Contributor has attached the Notice in Exhibit A, which is incorporated herein by this reference.
-
-7. Versions of This License.
-
- a. New Versions. Licensor may publish from time to time revised and/or new versions of the License.
-
- b. Effect of New Versions. Once Licensed Product has been published under a particular version of the License, you may always continue to use it under the terms of that version, provided that any such license be in full force and effect at the time, and has not been revoked or otherwise terminated. You may also choose to use such Licensed Product under the terms of any subsequent version (but not any prior version) of the License published by Licensor. No one other than Licensor has the right to modify the terms applicable to Licensed Product created under this License.
-
- c. Derivative Works of this License. If you create or use a modified version of this License, which you may do only in order to apply it to software that is not already a Licensed Product under this License, you must rename your license so that it is not confusingly similar to this License, and must make it clear that your license contains terms that differ from this License. In so naming your license, you may not use any trademark of Licensor or any Contributor.
-
-8. Disclaimer of Warranty. LICENSED PRODUCT IS PROVIDED UNDER THIS LICENSE ON AN AS IS BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE LICENSED PRODUCT IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LICENSED PRODUCT IS WITH YOU. SHOULD LICENSED PRODUCT PROVE DEFECTIVE IN ANY RESPECT, YOU (AND NOT THE LICENSOR OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF LICENSED PRODUCT IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-9. Termination.
-
- a. Automatic Termination Upon Breach. This license and the rights granted hereunder will terminate automatically if you fail to comply with the terms herein and fail to cure such breach within ten (10) days of being notified of the breach by the Licensor. For purposes of this provision, proof of delivery via email to the address listed in the 'WHOIS' database of the registrar for any website through which you distribute or market any Licensed Product, or to any alternate email address which you designate in writing to the Licensor, shall constitute sufficient notification. All sublicenses to the Licensed Product that are properly granted shall survive any termination of this license so long as they continue to complye with the terms of this License. Provisions that, by their nature, must remain in effect beyond the termination of this License, shall survive.
-
- b. Termination Upon Assertion of Patent Infringement. If you initiate litigation by asserting a patent infringement claim (excluding declaratory judgment actions) against Licensor or a Contributor (Licensor or Contributor against whom you file such an action is referred to herein as Respondent) alleging that Licensed Product directly or indirectly infringes any patent, then any and all rights granted by such Respondent to you under Sections 1 or 2 of this License shall terminate prospectively upon sixty (60) days notice from Respondent (the "Notice Period") unless within that Notice Period you either agree in writing (i) to pay Respondent a mutually agreeable reasonably royalty for your past or future use of Licensed Product made by such Respondent, or (ii) withdraw your litigation claim with respect to Licensed Product against such Respondent. If within said Notice Period a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Licensor to you under Sections 1 and 2 automatically terminate at the expiration of said Notice Period.
-
- c. Reasonable Value of This License. If you assert a patent infringement claim against Respondent alleging that Licensed Product directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by said Respondent under Sections 1 and 2 shall be taken into account in determining the amount or value of any payment or license.
-
- d. No Retroactive Effect of Termination. In the event of termination under Sections 9(a) or 9(b) above, all end user license agreements (excluding licenses to distributors and resellers) that have been validly granted by you or any distributor hereunder prior to termination shall survive termination.
-
-10. Limitation of Liability. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE LICENSOR, ANY CONTRIBUTOR, OR ANY DISTRIBUTOR OF LICENSED PRODUCT, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTYS NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-11. Responsibility for Claims. As between Licensor and Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License. You agree to work with Licensor and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-12. U.S. Government End Users. The Licensed Product is a commercial item, as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of commercial computer software and commercial computer software documentation, as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Licensed Product with only those rights set forth herein.
-
-13. Miscellaneous. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. You expressly agree that in any litigation relating to this license the losing party shall be responsible for costs including, without limitation, court costs and reasonable attorneys fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation that provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-14. Definition of You in This License. You throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 7. For legal entities, you includes any entity that controls, is controlled by, is under common control with, or affiliated with, you. For purposes of this definition, control means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. You are responsible for advising any affiliated entity of the terms of this License, and that any rights or privileges derived from or obtained by way of this License are subject to the restrictions outlined herein.
-
-15. Glossary. All defined terms in this License that are used in more than one Section of this License are repeated here, in alphabetical order, for the convenience of the reader. The Section of this License in which each defined term is first used is shown in parentheses.
-
-Contributor: Each person or entity who created or contributed to the creation of, and distributed, a Modification. (See Section 2)
-
-Derivative Works: That term as used in this License is defined under U.S. copyright law. (See Section 1(b))
-
-License: This BitTorrent Open Source License. (See first paragraph of License)
-
-Licensed Product: Any BitTorrent Product licensed pursuant to this License. The term "Licensed Product" includes all previous Modifications from any Contributor that you receive. (See first paragraph of License and Section 2)
-
-Licensor: BitTorrent, Inc. (See first paragraph of License)
-
-Modifications: Any additions to or deletions from the substance or structure of (i) a file containing Licensed
-Product, or (ii) any new file that contains any part of Licensed Product. (See Section 2)
-
-Notice: The notice contained in Exhibit A. (See Section 4(e))
-
-Source Code: The preferred form for making modifications to the Licensed Product, including all modules contained therein, plus any associated interface definition files, scripts used to control compilation and installation of an executable program, or a list of differential comparisons against the Source Code of the Licensed Product. (See Section 1(a))
-
-You: This term is defined in Section 14 of this License.
-
-EXHIBIT A
-
-The Notice below must appear in each file of the Source Code of any copy you distribute of the Licensed Product or any hereto. Contributors to any Modifications may add their own copyright notices to identify their own contributions.
-
-License:
-The contents of this file are subject to the BitTorrent Open Source License Version 1.0 (the License). You may not copy or use this file, in either source code or executable form, except in compliance with the License. You may obtain a copy of the License at http://www.bittorrent.com/license/.
-
-Software distributed under the License is distributed on an AS IS basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-BitTorrent, Inc.
diff --git a/options/license/Bitstream-Charter b/options/license/Bitstream-Charter
deleted file mode 100644
index 7a0cf97a0c..0000000000
--- a/options/license/Bitstream-Charter
+++ /dev/null
@@ -1,9 +0,0 @@
-(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/Bitstream-Vera b/options/license/Bitstream-Vera
deleted file mode 100644
index f353aa2d04..0000000000
--- a/options/license/Bitstream-Vera
+++ /dev/null
@@ -1,15 +0,0 @@
-Copyright Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions:
-
-The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces.
-
-The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera".
-
-This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names.
-
-The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself.
-
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
-
-Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.
diff --git a/options/license/BlueOak-1.0.0 b/options/license/BlueOak-1.0.0
deleted file mode 100644
index c5402b9577..0000000000
--- a/options/license/BlueOak-1.0.0
+++ /dev/null
@@ -1,55 +0,0 @@
-# Blue Oak Model License
-
-Version 1.0.0
-
-## Purpose
-
-This license gives everyone as much permission to work with
-this software as possible, while protecting contributors
-from liability.
-
-## Acceptance
-
-In order to receive this license, you must agree to its
-rules. The rules of this license are both obligations
-under that agreement and conditions to your license.
-You must not do anything with this software that triggers
-a rule that you cannot or will not follow.
-
-## Copyright
-
-Each contributor licenses you to do everything with this
-software that would otherwise infringe that contributor's
-copyright in it.
-
-## Notices
-
-You must ensure that everyone who gets a copy of
-any part of this software from you, with or without
-changes, also gets the text of this license or a link to
-<https://blueoakcouncil.org/license/1.0.0>.
-
-## Excuse
-
-If anyone notifies you in writing that you have not
-complied with [Notices](#notices), you can keep your
-license by taking all practical steps to comply within 30
-days after the notice. If you do not do so, your license
-ends immediately.
-
-## Patent
-
-Each contributor licenses you to do everything with this
-software that would otherwise infringe any patent claims
-they can license or become able to license.
-
-## Reliability
-
-No contributor can revoke this license.
-
-## No Liability
-
-***As far as the law allows, this software comes as is,
-without any warranty or condition, and no contributor
-will be liable to anyone for any damages related to this
-software or this license, under any kind of legal claim.***
diff --git a/options/license/Boehm-GC b/options/license/Boehm-GC
deleted file mode 100644
index 95427c0b59..0000000000
--- a/options/license/Boehm-GC
+++ /dev/null
@@ -1,12 +0,0 @@
-Copyright (c) ...
-
-THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
-OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
-
-Permission is hereby granted to use or copy this program
-for any purpose, provided the above notices are retained on all copies.
-Permission to modify the code and to distribute modified code is granted,
-provided the above notices are retained, and a notice that the code was
-modified is included with the above copyright notice.
-
-A few files have other copyright holders.
diff --git a/options/license/Boehm-GC-without-fee b/options/license/Boehm-GC-without-fee
deleted file mode 100644
index 354d47017e..0000000000
--- a/options/license/Boehm-GC-without-fee
+++ /dev/null
@@ -1,14 +0,0 @@
-Copyright (c) 2000
-SWsoft company
-
-Modifications copyright (c) 2001, 2013. Oracle and/or its affiliates.
-All rights reserved.
-
-This material is provided "as is", with absolutely no warranty expressed
-or implied. Any use is at your own risk.
-
-Permission to use or copy this software for any purpose is hereby granted
-without fee, provided the above notices are retained on all copies.
-Permission to modify the code and to distribute modified code is granted,
-provided the above notices are retained, and a notice that the code was
-modified is included with the above copyright notice.
diff --git a/options/license/Bootloader-exception b/options/license/Bootloader-exception
deleted file mode 100644
index c557826705..0000000000
--- a/options/license/Bootloader-exception
+++ /dev/null
@@ -1,10 +0,0 @@
-Bootloader Exception
---------------------
-
-In addition to the permissions in the GNU General Public License, the
-authors give you unlimited permission to link or embed compiled bootloader
-and related files into combinations with other programs, and to distribute
-those combinations without any restriction coming from the use of those
-files. (The General Public License restrictions do apply in other respects;
-for example, they cover modification of the files, and distribution when
-not linked into a combine executable.)
diff --git a/options/license/Borceux b/options/license/Borceux
deleted file mode 100644
index 4856e78859..0000000000
--- a/options/license/Borceux
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright 1993 Francis Borceux
-You may freely use, modify, and/or distribute each of the files in this package without limitation. The package consists of the following files:
-
-README
-compatibility/OldDiagram
-compatibility/OldMaxiDiagram
-compatibility/OldMicroDiagram
-compatibility/OldMiniDiagram
-compatibility/OldMultipleArrows
-diagram/Diagram
-diagram/MaxiDiagram
-diagram/MicroDiagram
-diagram/MiniDiagram
-diagram/MultipleArrows
-user-guides/Diagram_Mode_d_Emploi
-user-guides/Diagram_Read_Me
-
-Of course no support is guaranteed, but the author will attempt to assist with problems. Current email address:
-francis dot borceux at uclouvain dot be.
diff --git a/options/license/Brian-Gladman-2-Clause b/options/license/Brian-Gladman-2-Clause
deleted file mode 100644
index 7276f63e9e..0000000000
--- a/options/license/Brian-Gladman-2-Clause
+++ /dev/null
@@ -1,17 +0,0 @@
-Copyright (C) 1998-2013, Brian Gladman, Worcester, UK. All
- rights reserved.
-
-The redistribution and use of this software (with or without
-changes) is allowed without the payment of fees or royalties
-provided that:
-
- source code distributions include the above copyright notice,
- this list of conditions and the following disclaimer;
-
- binary distributions include the above copyright notice, this
- list of conditions and the following disclaimer in their
- documentation.
-
-This software is provided 'as is' with no explicit or implied
-warranties in respect of its operation, including, but not limited
-to, correctness and fitness for purpose.
diff --git a/options/license/Brian-Gladman-3-Clause b/options/license/Brian-Gladman-3-Clause
deleted file mode 100644
index 984c95e3e8..0000000000
--- a/options/license/Brian-Gladman-3-Clause
+++ /dev/null
@@ -1,26 +0,0 @@
-Copyright (c) 2003, Dr Brian Gladman, Worcester, UK. All rights reserved.
-
-LICENSE TERMS
-
-The free distribution and use of this software in both source and binary
-form is allowed (with or without changes) provided that:
-
- 1. distributions of this source code include the above copyright
- notice, this list of conditions and the following disclaimer;
-
- 2. distributions in binary form include the above copyright
- notice, this list of conditions and the following disclaimer
- in the documentation and/or other associated materials;
-
- 3. the copyright holder's name is not used to endorse products
- built using this software without specific written permission.
-
-ALTERNATIVELY, provided that this notice is retained in full, this product
-may be distributed under the terms of the GNU General Public License (GPL),
-in which case the provisions of the GPL apply INSTEAD OF those given above.
-
-DISCLAIMER
-
-This software is provided 'as is' with no explicit or implied warranties
-in respect of its properties, including, but not limited to, correctness
-and/or fitness for purpose.
diff --git a/options/license/C-UDA-1.0 b/options/license/C-UDA-1.0
deleted file mode 100644
index 9f7c57df5a..0000000000
--- a/options/license/C-UDA-1.0
+++ /dev/null
@@ -1,47 +0,0 @@
-Computational Use of Data Agreement v1.0
-
-This is the Computational Use of Data Agreement, Version 1.0 (the “C-UDAâ€). Capitalized terms are defined in Section 5. Data Provider and you agree as follows:
-
-1. Provision of the Data
-
-1.1. You may use, modify, and distribute the Data made available to you by the Data Provider under this C-UDA for Computational Use if you follow the C-UDA's terms.
-
-1.2. Data Provider will not sue you or any Downstream Recipient for any claim arising out of the use, modification, or distribution of the Data provided you meet the terms of the C-UDA.
-
-1.3 This C-UDA does not restrict your use, modification, or distribution of any portions of the Data that are in the public domain or that may be used, modified, or distributed under any other legal exception or limitation.
-
-2. Restrictions
-
-2.1 You agree that you will use the Data solely for Computational Use.
-
-2.2 The C-UDA does not impose any restriction with respect to the use, modification, or distribution of Results.
-
-3. Redistribution of Data
-
-3.1. You may redistribute the Data, so long as:
-
-3.1.1. You include with any Data you redistribute all credit or attribution information that you received with the Data, and your terms require any Downstream Recipient to do the same; and
-
-3.1.2. You bind each recipient to whom you redistribute the Data to the terms of the C-UDA.
-
-4. No Warranty, Limitation of Liability
-
-4.1. Data Provider does not represent or warrant that it has any rights whatsoever in the Data.
-
-4.2. THE DATA IS PROVIDED ON AN “AS IS†BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
-
-4.3. NEITHER DATA PROVIDER NOR ANY UPSTREAM DATA PROVIDER SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE DATA OR RESULTS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-5. Definitions
-
-5.1. “Computational Use†means activities necessary to enable the use of Data (alone or along with other material) for analysis by a computer.
-
-5.2.“Data†means the material you receive under the C-UDA in modified or unmodified form, but not including Results.
-
-5.3. “Data Provider†means the source from which you receive the Data and with whom you enter into the C-UDA.
-
-5.4. “Downstream Recipient†means any person or persons who receives the Data directly or indirectly from you in accordance with the C-UDA.
-
-5.5. “Result†means anything that you develop or improve from your use of Data that does not include more than a de minimis portion of the Data on which the use is based. Results may include de minimis portions of the Data necessary to report on or explain use that has been conducted with the Data, such as figures in scientific papers, but do not include more. Artificial intelligence models trained on Data (and which do not include more than a de minimis portion of Data) are Results.
-
-5.6. “Upstream Data Providers†means the source or sources from which the Data Provider directly or indirectly received, under the terms of the C-UDA, material that is included in the Data.
diff --git a/options/license/CAL-1.0 b/options/license/CAL-1.0
deleted file mode 100644
index 4cebc6d54d..0000000000
--- a/options/license/CAL-1.0
+++ /dev/null
@@ -1,354 +0,0 @@
-# The Cryptographic Autonomy License, v. 1.0
-
-*This Cryptographic Autonomy License (the "License") applies to any
-Work whose owner has marked it with any of the following notices, or a
-similar demonstration of intent:*
-
-SPDX-License-Identifier: CAL-1.0
-Licensed under the Cryptographic Autonomy License version 1.0
-
-*or*
-
-SPDX-License-Identifier: CAL-1.0-Combined-Work-Exception
-Licensed under the Cryptographic Autonomy License version 1.0, with
-Combined Work Exception
-
-______________________________________________________________________
-
-## 1. Purpose
-
-This License gives You unlimited permission to use and modify the
-software to which it applies (the "Work"), either as-is or in modified
-form, for Your private purposes, while protecting the owners and
-contributors to the software from liability.
-
-This License also strives to protect the freedom and autonomy of third
-parties who receive the Work from you. If any non-affiliated third
-party receives any part, aspect, or element of the Work from You, this
-License requires that You provide that third party all the permissions
-and materials needed to independently use and modify the Work without
-that third party having a loss of data or capability due to your
-actions.
-
-The full permissions, conditions, and other terms are laid out below.
-
-## 2. Receiving a License
-
-In order to receive this License, You must agree to its rules. The
-rules of this License are both obligations of Your agreement with the
-Licensor and conditions to your License. You must not do anything with
-the Work that triggers a rule You cannot or will not follow.
-
-### 2.1. Application
-
-The terms of this License apply to the Work as you receive it from
-Licensor, as well as to any modifications, elaborations, or
-implementations created by You that contain any licensable portion of
-the Work (a "Modified Work"). Unless specified, any reference to the
-Work also applies to a Modified Work.
-
-### 2.2. Offer and Acceptance
-
-This License is automatically offered to every person and
-organization. You show that you accept this License and agree to its
-conditions by taking any action with the Work that, absent this
-License, would infringe any intellectual property right held by
-Licensor.
-
-### 2.3. Compliance and Remedies
-
-Any failure to act according to the terms and conditions of this
-License places Your use of the Work outside the scope of the License
-and infringes the intellectual property rights of the Licensor. In the
-event of infringement, the terms and conditions of this License may be
-enforced by Licensor under the intellectual property laws of any
-jurisdiction to which You are subject. You also agree that either the
-Licensor or a Recipient (as an intended third-party beneficiary) may
-enforce the terms and conditions of this License against You via
-specific performance.
-
-## 3. Permissions
-### 3.1. Permissions Granted
-
-Conditioned on compliance with section 4, and subject to the
-limitations of section 3.2, Licensor grants You the world-wide,
-royalty-free, non-exclusive permission to:
-
-+ a) Take any action with the Work that would infringe the non-patent
-intellectual property laws of any jurisdiction to which You are
-subject; and
-
-+ b) claims that Licensor can license or becomes able to
-license, to the extent that those claims are embodied in the Work as
-distributed by Licensor. ### 3.2. Limitations on Permissions Granted
-
-The following limitations apply to the permissions granted in section
-3.1:
-
-+ a) Licensor does not grant any patent license for claims that are
-only infringed due to modification of the Work as provided by
-Licensor, or the combination of the Work as provided by Licensor,
-directly or indirectly, with any other component, including other
-software or hardware.
-
-+ b) Licensor does not grant any license to the trademarks, service
-marks, or logos of Licensor, except to the extent necessary to comply
-with the attribution conditions in section 4.1 of this License.
-
-## 4. Conditions
-
-If You exercise any permission granted by this License, such that the
-Work, or any part, aspect, or element of the Work, is distributed,
-communicated, made available, or made perceptible to a non-Affiliate
-third party (a "Recipient"), either via physical delivery or via a
-network connection to the Recipient, You must comply with the
-following conditions:
-
-### 4.1. Provide Access to Source Code
-
-Subject to the exception in section 4.4, You must provide to each
-Recipient a copy of, or no-charge unrestricted network access to, the
-Source Code corresponding to the Work ("Access").
-
-The "Source Code" of the Work means the form of the Work preferred for
-making modifications, including any comments, configuration
-information, documentation, help materials, installation instructions,
-cryptographic seeds or keys, and any information reasonably necessary
-for the Recipient to independently compile and use the Source Code and
-to have full access to the functionality contained in the Work.
-
-#### 4.1.1. Providing Network Access to the Source Code
-
-Network Access to the Notices and Source Code may be provided by You
-or by a third party, such as a public software repository, and must
-persist during the same period in which You exercise any of the
-permissions granted to You under this License and for at least one
-year thereafter.
-
-#### 4.1.2. Source Code for a Modified Work
-
-Subject to the exception in section 4.5, You must provide to each
-Recipient of a Modified Work Access to Source Code corresponding to
-those portions of the Work remaining in the Modified Work as well as
-the modifications used by You to create the Modified Work. The Source
-Code corresponding to the modifications in the Modified Work must be
-provided to the Recipient either a) under this License, or b) under a
-Compatible Open Source License.
-
-A “Compatible Open Source License†means a license accepted by the Open Source
-Initiative that allows object code created using both Source Code provided under
-this License and Source Code provided under the other open source license to be
-distributed together as a single work.
-
-#### 4.1.3. Coordinated Disclosure of Security Vulnerabilities
-
-You may delay providing the Source Code corresponding to a particular
-modification of the Work for up to ninety (90) days (the "Embargo
-Period") if:
-
-+ a) the modification is intended to address a newly-identified
-vulnerability or a security flaw in the Work,
-
-+ b) disclosure of the vulnerability or security flaw before the end
-of the Embargo Period would put the data, identity, or autonomy of one
-or more Recipients of the Work at significant risk,
-
-+ c) You are participating in a coordinated disclosure of the
-vulnerability or security flaw with one or more additional Licensees,
-and
-
-+ d) Access to the Source Code pertaining to the modification is
-provided to all Recipients at the end of the Embargo Period.
-
-### 4.2. Maintain User Autonomy
-
-In addition to providing each Recipient the opportunity to have Access
-to the Source Code, You cannot use the permissions given under this
-License to interfere with a Recipient's ability to fully use an
-independent copy of the Work generated from the Source Code You
-provide with the Recipient's own User Data.
-
-"User Data" means any data that is an input to or an output from the
-Work, where the presence of the data is necessary for substantially
-identical use of the Work in an equivalent context chosen by the
-Recipient, and where the Recipient has an existing ownership interest,
-an existing right to possess, or where the data has been generated by,
-for, or has been assigned to the Recipient.
-
-#### 4.2.1. No Withholding User Data
-
-Throughout any period in which You exercise any of the permissions
-granted to You under this License, You must also provide to any
-Recipient to whom you provide services via the Work, a no-charge copy,
-provided in a commonly used electronic form, of the Recipient's User
-Data in your possession, to the extent that such User Data is
-available to You for use in conjunction with the Work.
-
-#### 4.2.2. No Technical Measures that Limit Access
-
-You may not, by means of the use cryptographic methods applied to
-anything provided to the Recipient, by possession or control of
-cryptographic keys, seeds, hashes, by any other technological
-protection measures, or by any other method, limit a Recipient's
-ability to access any functionality present in Recipient's independent
-copy of the Work, or to deny a Recipient full control of the
-Recipient's User Data.
-
-#### 4.2.3. No Legal or Contractual Measures that Limit Access
-
-You may not contractually restrict a Recipient's ability to
-independently exercise the permissions granted under this License. You
-waive any legal power to forbid circumvention of technical protection
-measures that include use of the Work, and You waive any claim that
-the capabilities of the Work were limited or modified as a means of
-enforcing the legal rights of third parties against Recipients.
-
-### 4.3. Provide Notices and Attribution
-
-You must retain all licensing, authorship, or attribution notices
-contained in the Source Code (the "Notices"), and provide all such
-Notices to each Recipient, together with a statement acknowledging the
-use of the Work. Notices may be provided directly to a Recipient or
-via an easy-to-find hyperlink to an Internet location also providing
-Access to Source Code.
-
-### 4.4. Scope of Conditions in this License
-
-You are required to uphold the conditions of this License only
-relative to those who are Recipients of the Work from You. Other than
-providing Recipients with the applicable Notices, Access to Source
-Code, and a copy of and full control of their User Data, nothing in
-this License requires You to provide processing services to or engage
-in network interactions with anyone.
-
-### 4.5. Combined Work Exception
-
-As an exception to condition that You provide Recipients Access to
-Source Code, any Source Code files marked by the Licensor as having
-the "Combined Work Exception," or any object code exclusively
-resulting from Source Code files so marked, may be combined with other
-Software into a "Larger Work." So long as you comply with the
-requirements to provide Recipients the applicable Notices and Access
-to the Source Code provided to You by Licensor, and you provide
-Recipients access to their User Data and do not limit Recipient's
-ability to independently work with their User Data, any other Software
-in the Larger Work as well as the Larger Work as a whole may be
-licensed under the terms of your choice.
-
-## 5. Term and Termination
-
-The term of this License begins when You receive the Work, and
-continues until terminated for any of the reasons described herein, or
-until all Licensor's intellectual property rights in the Software
-expire, whichever comes first ("Term"). This License cannot be
-revoked, only terminated for the reasons listed below.
-
-### 5.1. Effect of Termination
-
-If this License is terminated for any reason, all permissions granted
-to You under Section 3 by any Licensor automatically terminate. You
-will immediately cease exercising any permissions granted in this
-License relative to the Work, including as part of any Modified Work.
-
-### 5.2. Termination for Non-Compliance; Reinstatement
-
-This License terminates automatically if You fail to comply with any
-of the conditions in section 4. As a special exception to termination
-for non-compliance, Your permissions for the Work under this License
-will automatically be reinstated if You come into compliance with all
-the conditions in section 2 within sixty (60) days of being notified
-by Licensor or an intended third-party beneficiary of Your
-noncompliance. You are eligible for reinstatement of permissions for
-the Work one time only, and only for the sixty days immediately after
-becoming aware of noncompliance. Loss of permissions granted for the
-Work under this License due to either a) sustained noncompliance
-lasting more than sixty days or b) subsequent termination for
-noncompliance after reinstatement, is permanent, unless rights are
-specifically restored by Licensor in writing.
-
-### 5.3. Termination Due to Litigation
-
-If You initiate litigation against Licensor, or any Recipient of the
-Work, either direct or indirect, asserting that the Work directly or
-indirectly infringes any patent, then all permissions granted to You
-by this License shall terminate. In the event of termination due to
-litigation, all permissions validly granted by You under this License,
-directly or indirectly, shall survive termination. Administrative
-review procedures, declaratory judgment actions, counterclaims in
-response to patent litigation, and enforcement actions against former
-Licensees terminated under this section do not cause termination due
-to litigation.
-
-## 6. Disclaimer of Warranty and Limit on Liability
-
-As far as the law allows, the Work comes AS-IS, without any warranty
-of any kind, and no Licensor or contributor will be liable to anyone
-for any damages related to this software or this license, under any
-kind of legal claim, or for any type of damages, including indirect,
-special, incidental, or consequential damages of any type arising as a
-result of this License or the use of the Work including, without
-limitation, damages for loss of goodwill, work stoppage, computer
-failure or malfunction, loss of profits, revenue, or any and all other
-commercial damages or losses.
-
-## 7. Other Provisions
-### 7.1. Affiliates
-
-An "Affiliate" means any other entity that, directly or indirectly
-through one or more intermediaries, controls, is controlled by, or is
-under common control with, the Licensee. Employees of a Licensee and
-natural persons acting as contractors exclusively providing services
-to Licensee are also Affiliates.
-
-### 7.2. Choice of Jurisdiction and Governing Law
-
-A Licensor may require that any action or suit by a Licensee relating
-to a Work provided by Licensor under this License may be brought only
-in the courts of a particular jurisdiction and under the laws of a
-particular jurisdiction (excluding its conflict-of-law provisions), if
-Licensor provides conspicuous notice of the particular jurisdiction to
-all Licensees.
-
-### 7.3. No Sublicensing
-
-This License is not sublicensable. Each time You provide the Work or a
-Modified Work to a Recipient, the Recipient automatically receives a
-license under the terms described in this License. You may not impose
-any further reservations, conditions, or other provisions on any
-Recipients' exercise of the permissions granted herein.
-
-### 7.4. Attorneys' Fees
-
-In any action to enforce the terms of this License, or seeking damages
-relating thereto, including by an intended third-party beneficiary,
-the prevailing party shall be entitled to recover its costs and
-expenses, including, without limitation, reasonable attorneys' fees
-and costs incurred in connection with such action, including any
-appeal of such action. A "prevailing party" is the party that
-achieves, or avoids, compliance with this License, including through
-settlement. This section shall survive the termination of this
-License.
-
-### 7.5. No Waiver
-
-Any failure by Licensor to enforce any provision of this License will
-not constitute a present or future waiver of such provision nor limit
-Licensor's ability to enforce such provision at a later time.
-
-### 7.6. Severability
-
-If any provision of this License is held to be unenforceable, such
-provision shall be reformed only to the extent necessary to make it
-enforceable. Any invalid or unenforceable portion will be interpreted
-to the effect and intent of the original portion. If such a
-construction is not possible, the invalid or unenforceable portion
-will be severed from this License but the rest of this License will
-remain in full force and effect.
-
-### 7.7. License for the Text of this License
-
-The text of this license is released under the Creative Commons
-Attribution-ShareAlike 4.0 International License, with the caveat that
-any modifications of this license may not use the name "Cryptographic
-Autonomy License" or any name confusingly similar thereto to describe
-any derived work of this License.
diff --git a/options/license/CAL-1.0-Combined-Work-Exception b/options/license/CAL-1.0-Combined-Work-Exception
deleted file mode 100644
index 4cebc6d54d..0000000000
--- a/options/license/CAL-1.0-Combined-Work-Exception
+++ /dev/null
@@ -1,354 +0,0 @@
-# The Cryptographic Autonomy License, v. 1.0
-
-*This Cryptographic Autonomy License (the "License") applies to any
-Work whose owner has marked it with any of the following notices, or a
-similar demonstration of intent:*
-
-SPDX-License-Identifier: CAL-1.0
-Licensed under the Cryptographic Autonomy License version 1.0
-
-*or*
-
-SPDX-License-Identifier: CAL-1.0-Combined-Work-Exception
-Licensed under the Cryptographic Autonomy License version 1.0, with
-Combined Work Exception
-
-______________________________________________________________________
-
-## 1. Purpose
-
-This License gives You unlimited permission to use and modify the
-software to which it applies (the "Work"), either as-is or in modified
-form, for Your private purposes, while protecting the owners and
-contributors to the software from liability.
-
-This License also strives to protect the freedom and autonomy of third
-parties who receive the Work from you. If any non-affiliated third
-party receives any part, aspect, or element of the Work from You, this
-License requires that You provide that third party all the permissions
-and materials needed to independently use and modify the Work without
-that third party having a loss of data or capability due to your
-actions.
-
-The full permissions, conditions, and other terms are laid out below.
-
-## 2. Receiving a License
-
-In order to receive this License, You must agree to its rules. The
-rules of this License are both obligations of Your agreement with the
-Licensor and conditions to your License. You must not do anything with
-the Work that triggers a rule You cannot or will not follow.
-
-### 2.1. Application
-
-The terms of this License apply to the Work as you receive it from
-Licensor, as well as to any modifications, elaborations, or
-implementations created by You that contain any licensable portion of
-the Work (a "Modified Work"). Unless specified, any reference to the
-Work also applies to a Modified Work.
-
-### 2.2. Offer and Acceptance
-
-This License is automatically offered to every person and
-organization. You show that you accept this License and agree to its
-conditions by taking any action with the Work that, absent this
-License, would infringe any intellectual property right held by
-Licensor.
-
-### 2.3. Compliance and Remedies
-
-Any failure to act according to the terms and conditions of this
-License places Your use of the Work outside the scope of the License
-and infringes the intellectual property rights of the Licensor. In the
-event of infringement, the terms and conditions of this License may be
-enforced by Licensor under the intellectual property laws of any
-jurisdiction to which You are subject. You also agree that either the
-Licensor or a Recipient (as an intended third-party beneficiary) may
-enforce the terms and conditions of this License against You via
-specific performance.
-
-## 3. Permissions
-### 3.1. Permissions Granted
-
-Conditioned on compliance with section 4, and subject to the
-limitations of section 3.2, Licensor grants You the world-wide,
-royalty-free, non-exclusive permission to:
-
-+ a) Take any action with the Work that would infringe the non-patent
-intellectual property laws of any jurisdiction to which You are
-subject; and
-
-+ b) claims that Licensor can license or becomes able to
-license, to the extent that those claims are embodied in the Work as
-distributed by Licensor. ### 3.2. Limitations on Permissions Granted
-
-The following limitations apply to the permissions granted in section
-3.1:
-
-+ a) Licensor does not grant any patent license for claims that are
-only infringed due to modification of the Work as provided by
-Licensor, or the combination of the Work as provided by Licensor,
-directly or indirectly, with any other component, including other
-software or hardware.
-
-+ b) Licensor does not grant any license to the trademarks, service
-marks, or logos of Licensor, except to the extent necessary to comply
-with the attribution conditions in section 4.1 of this License.
-
-## 4. Conditions
-
-If You exercise any permission granted by this License, such that the
-Work, or any part, aspect, or element of the Work, is distributed,
-communicated, made available, or made perceptible to a non-Affiliate
-third party (a "Recipient"), either via physical delivery or via a
-network connection to the Recipient, You must comply with the
-following conditions:
-
-### 4.1. Provide Access to Source Code
-
-Subject to the exception in section 4.4, You must provide to each
-Recipient a copy of, or no-charge unrestricted network access to, the
-Source Code corresponding to the Work ("Access").
-
-The "Source Code" of the Work means the form of the Work preferred for
-making modifications, including any comments, configuration
-information, documentation, help materials, installation instructions,
-cryptographic seeds or keys, and any information reasonably necessary
-for the Recipient to independently compile and use the Source Code and
-to have full access to the functionality contained in the Work.
-
-#### 4.1.1. Providing Network Access to the Source Code
-
-Network Access to the Notices and Source Code may be provided by You
-or by a third party, such as a public software repository, and must
-persist during the same period in which You exercise any of the
-permissions granted to You under this License and for at least one
-year thereafter.
-
-#### 4.1.2. Source Code for a Modified Work
-
-Subject to the exception in section 4.5, You must provide to each
-Recipient of a Modified Work Access to Source Code corresponding to
-those portions of the Work remaining in the Modified Work as well as
-the modifications used by You to create the Modified Work. The Source
-Code corresponding to the modifications in the Modified Work must be
-provided to the Recipient either a) under this License, or b) under a
-Compatible Open Source License.
-
-A “Compatible Open Source License†means a license accepted by the Open Source
-Initiative that allows object code created using both Source Code provided under
-this License and Source Code provided under the other open source license to be
-distributed together as a single work.
-
-#### 4.1.3. Coordinated Disclosure of Security Vulnerabilities
-
-You may delay providing the Source Code corresponding to a particular
-modification of the Work for up to ninety (90) days (the "Embargo
-Period") if:
-
-+ a) the modification is intended to address a newly-identified
-vulnerability or a security flaw in the Work,
-
-+ b) disclosure of the vulnerability or security flaw before the end
-of the Embargo Period would put the data, identity, or autonomy of one
-or more Recipients of the Work at significant risk,
-
-+ c) You are participating in a coordinated disclosure of the
-vulnerability or security flaw with one or more additional Licensees,
-and
-
-+ d) Access to the Source Code pertaining to the modification is
-provided to all Recipients at the end of the Embargo Period.
-
-### 4.2. Maintain User Autonomy
-
-In addition to providing each Recipient the opportunity to have Access
-to the Source Code, You cannot use the permissions given under this
-License to interfere with a Recipient's ability to fully use an
-independent copy of the Work generated from the Source Code You
-provide with the Recipient's own User Data.
-
-"User Data" means any data that is an input to or an output from the
-Work, where the presence of the data is necessary for substantially
-identical use of the Work in an equivalent context chosen by the
-Recipient, and where the Recipient has an existing ownership interest,
-an existing right to possess, or where the data has been generated by,
-for, or has been assigned to the Recipient.
-
-#### 4.2.1. No Withholding User Data
-
-Throughout any period in which You exercise any of the permissions
-granted to You under this License, You must also provide to any
-Recipient to whom you provide services via the Work, a no-charge copy,
-provided in a commonly used electronic form, of the Recipient's User
-Data in your possession, to the extent that such User Data is
-available to You for use in conjunction with the Work.
-
-#### 4.2.2. No Technical Measures that Limit Access
-
-You may not, by means of the use cryptographic methods applied to
-anything provided to the Recipient, by possession or control of
-cryptographic keys, seeds, hashes, by any other technological
-protection measures, or by any other method, limit a Recipient's
-ability to access any functionality present in Recipient's independent
-copy of the Work, or to deny a Recipient full control of the
-Recipient's User Data.
-
-#### 4.2.3. No Legal or Contractual Measures that Limit Access
-
-You may not contractually restrict a Recipient's ability to
-independently exercise the permissions granted under this License. You
-waive any legal power to forbid circumvention of technical protection
-measures that include use of the Work, and You waive any claim that
-the capabilities of the Work were limited or modified as a means of
-enforcing the legal rights of third parties against Recipients.
-
-### 4.3. Provide Notices and Attribution
-
-You must retain all licensing, authorship, or attribution notices
-contained in the Source Code (the "Notices"), and provide all such
-Notices to each Recipient, together with a statement acknowledging the
-use of the Work. Notices may be provided directly to a Recipient or
-via an easy-to-find hyperlink to an Internet location also providing
-Access to Source Code.
-
-### 4.4. Scope of Conditions in this License
-
-You are required to uphold the conditions of this License only
-relative to those who are Recipients of the Work from You. Other than
-providing Recipients with the applicable Notices, Access to Source
-Code, and a copy of and full control of their User Data, nothing in
-this License requires You to provide processing services to or engage
-in network interactions with anyone.
-
-### 4.5. Combined Work Exception
-
-As an exception to condition that You provide Recipients Access to
-Source Code, any Source Code files marked by the Licensor as having
-the "Combined Work Exception," or any object code exclusively
-resulting from Source Code files so marked, may be combined with other
-Software into a "Larger Work." So long as you comply with the
-requirements to provide Recipients the applicable Notices and Access
-to the Source Code provided to You by Licensor, and you provide
-Recipients access to their User Data and do not limit Recipient's
-ability to independently work with their User Data, any other Software
-in the Larger Work as well as the Larger Work as a whole may be
-licensed under the terms of your choice.
-
-## 5. Term and Termination
-
-The term of this License begins when You receive the Work, and
-continues until terminated for any of the reasons described herein, or
-until all Licensor's intellectual property rights in the Software
-expire, whichever comes first ("Term"). This License cannot be
-revoked, only terminated for the reasons listed below.
-
-### 5.1. Effect of Termination
-
-If this License is terminated for any reason, all permissions granted
-to You under Section 3 by any Licensor automatically terminate. You
-will immediately cease exercising any permissions granted in this
-License relative to the Work, including as part of any Modified Work.
-
-### 5.2. Termination for Non-Compliance; Reinstatement
-
-This License terminates automatically if You fail to comply with any
-of the conditions in section 4. As a special exception to termination
-for non-compliance, Your permissions for the Work under this License
-will automatically be reinstated if You come into compliance with all
-the conditions in section 2 within sixty (60) days of being notified
-by Licensor or an intended third-party beneficiary of Your
-noncompliance. You are eligible for reinstatement of permissions for
-the Work one time only, and only for the sixty days immediately after
-becoming aware of noncompliance. Loss of permissions granted for the
-Work under this License due to either a) sustained noncompliance
-lasting more than sixty days or b) subsequent termination for
-noncompliance after reinstatement, is permanent, unless rights are
-specifically restored by Licensor in writing.
-
-### 5.3. Termination Due to Litigation
-
-If You initiate litigation against Licensor, or any Recipient of the
-Work, either direct or indirect, asserting that the Work directly or
-indirectly infringes any patent, then all permissions granted to You
-by this License shall terminate. In the event of termination due to
-litigation, all permissions validly granted by You under this License,
-directly or indirectly, shall survive termination. Administrative
-review procedures, declaratory judgment actions, counterclaims in
-response to patent litigation, and enforcement actions against former
-Licensees terminated under this section do not cause termination due
-to litigation.
-
-## 6. Disclaimer of Warranty and Limit on Liability
-
-As far as the law allows, the Work comes AS-IS, without any warranty
-of any kind, and no Licensor or contributor will be liable to anyone
-for any damages related to this software or this license, under any
-kind of legal claim, or for any type of damages, including indirect,
-special, incidental, or consequential damages of any type arising as a
-result of this License or the use of the Work including, without
-limitation, damages for loss of goodwill, work stoppage, computer
-failure or malfunction, loss of profits, revenue, or any and all other
-commercial damages or losses.
-
-## 7. Other Provisions
-### 7.1. Affiliates
-
-An "Affiliate" means any other entity that, directly or indirectly
-through one or more intermediaries, controls, is controlled by, or is
-under common control with, the Licensee. Employees of a Licensee and
-natural persons acting as contractors exclusively providing services
-to Licensee are also Affiliates.
-
-### 7.2. Choice of Jurisdiction and Governing Law
-
-A Licensor may require that any action or suit by a Licensee relating
-to a Work provided by Licensor under this License may be brought only
-in the courts of a particular jurisdiction and under the laws of a
-particular jurisdiction (excluding its conflict-of-law provisions), if
-Licensor provides conspicuous notice of the particular jurisdiction to
-all Licensees.
-
-### 7.3. No Sublicensing
-
-This License is not sublicensable. Each time You provide the Work or a
-Modified Work to a Recipient, the Recipient automatically receives a
-license under the terms described in this License. You may not impose
-any further reservations, conditions, or other provisions on any
-Recipients' exercise of the permissions granted herein.
-
-### 7.4. Attorneys' Fees
-
-In any action to enforce the terms of this License, or seeking damages
-relating thereto, including by an intended third-party beneficiary,
-the prevailing party shall be entitled to recover its costs and
-expenses, including, without limitation, reasonable attorneys' fees
-and costs incurred in connection with such action, including any
-appeal of such action. A "prevailing party" is the party that
-achieves, or avoids, compliance with this License, including through
-settlement. This section shall survive the termination of this
-License.
-
-### 7.5. No Waiver
-
-Any failure by Licensor to enforce any provision of this License will
-not constitute a present or future waiver of such provision nor limit
-Licensor's ability to enforce such provision at a later time.
-
-### 7.6. Severability
-
-If any provision of this License is held to be unenforceable, such
-provision shall be reformed only to the extent necessary to make it
-enforceable. Any invalid or unenforceable portion will be interpreted
-to the effect and intent of the original portion. If such a
-construction is not possible, the invalid or unenforceable portion
-will be severed from this License but the rest of this License will
-remain in full force and effect.
-
-### 7.7. License for the Text of this License
-
-The text of this license is released under the Creative Commons
-Attribution-ShareAlike 4.0 International License, with the caveat that
-any modifications of this license may not use the name "Cryptographic
-Autonomy License" or any name confusingly similar thereto to describe
-any derived work of this License.
diff --git a/options/license/CATOSL-1.1 b/options/license/CATOSL-1.1
deleted file mode 100644
index 4ba00492bb..0000000000
--- a/options/license/CATOSL-1.1
+++ /dev/null
@@ -1,114 +0,0 @@
-Computer Associates Trusted Open Source License
-Version 1.1
-
-PLEASE READ THIS DOCUMENT CAREFULLY AND IN ITS ENTIRETY. THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMPUTER ASSOCIATES TRUSTED OPEN SOURCE LICENSE ("LICENSE"). ANY USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES THE RECIPIENT'S ACCEPTANCE OF THIS LICENSE.
-
-License Background
-
-Computer Associates International, Inc. (CA) believes in open source. We believe that the open source development approach can take appropriate software programs to unprecedented levels of quality, growth, and innovation. To demonstrate our continuing commitment to open source, we are releasing the Program (as defined below) under this License.
-
-This License is intended to permit contributors and recipients of the Program to use the Program, including its source code, freely and without many of the concerns of some other open source licenses. Although we expect the underlying Program, and Contributions (as defined below) made to such Program, to remain open, this License is designed to permit you to maintain your own software programs free of this License unless you choose to do so. Thus, only your Contributions to the Program must be distributed under the terms of this License.
-
-The provisions that follow set forth the terms and conditions under which you may use the Program.
-
-1. DEFINITIONS
-
-1.1 Contribution means (a) in the case of CA, the Original Program; and (b) in the case of each Contributor (including CA), changes and additions to the Program, where such changes and/or additions to the Program originate from and are distributed by that particular Contributor to unaffiliated third parties. A Contribution originates from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributors behalf. Contributions do not include additions to the Program which: (x) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (y) are not derivative works of the Program.
-
-1.2 Contributor means CA and any other person or entity that distributes the Program.
-
-1.3 Contributor Version means as to a Contributor, that version of the Program that includes the Contributors Contribution but not any Contributions made to the Program thereafter.
-
-1.4 Larger Work means a work that combines the Program or portions thereof with code not governed by the terms of this License.
-
-1.5 Licensed Patents mean patents licensable by a Contributor that are infringed by the use or sale of its Contribution alone or when combined with the Program.
-
-1.6 Original Program means the original version of the software to which this License is attached and as released by CA, including source code, object code and documentation, if any.
-
-1.7 Program means the Original Program and Contributions.
-
-1.8 Recipient means anyone who modifies, copies, uses or distributes the Program.
-
-2. GRANT OF RIGHTS
-
-2.1 Subject to the terms of this License, each Contributor hereby grants Recipient an irrevocable, non-exclusive, worldwide, royalty-free license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. For the avoidance of doubt, the license provided in this Section 2.1 shall not include a license to any Licensed Patents of a Contributor.
-
-2.2 Subject to the terms of this License, each Contributor hereby grants Recipient an irrevocable, non-exclusive, worldwide, royalty-free license to the Licensed Patents to the extent necessary to make, use, sell, offer to sell and import the Contribution of such Contributor, if any, in source code and object code form. The license granted in this Section 2.2 shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes the Licensed Patents to be infringed by such combination. Notwithstanding the foregoing, no license is granted under this Section 2.2: (a) for any code or works that do not include the Contributor Version, as it exists and is used in accordance with the terms hereof; (b) for infringements caused by: (i) third party modifications of the Contributor Version; or (ii) the combination of Contributions made by each such Contributor with other software (except as part of the Contributor Version) or other devices; or (c) with respect to Licensed Patents infringed by the Program in the absence of Contributions made by that Contributor.
-
-2.3 Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, except as provided in Section 2.4, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other person or entity. Each Contributor disclaims any liability to Recipient for claims brought by any other person or entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any.
-
-2.4 Each Contributor represents and warrants that it has all right, title and interest in the copyrights in its Contributions, and has the right to grant the copyright licenses set forth in this License.
-
-3. DISTRIBUTION REQUIREMENTS
-
-3.1 If the Program is distributed in object code form, then a prominent notice must be included in the code itself as well as in any related documentation, stating that the source code for the Program is available from the Contributor with information on how and where to obtain the source code. A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
-
- * it complies with the terms and conditions of this License; and
- * its license agreement:
- * effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose, to the maximum extent permitted by applicable law;
- * effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits, to the maximum extent permitted by applicable law;
- * states that any provisions which are inconsistent with this License are offered by that Contributor alone and not by any other party; and
- * states that source code for the Program is available from such Contributor at the cost of distribution, and informs licensees how to obtain it in a reasonable manner.
-
-3.2 When the Program is made available in source code form:
-
- * it must be made available under this License; and
- * a copy of this License must be included with each copy of the Program.
-
-3.3 This License is intended to facilitate the commercial distribution of the Program by any Contributor. However, Contributors may only charge Recipients a one-time, upfront fee for the distribution of the Program. Contributors may not charge Recipients any recurring charge, license fee, or any ongoing royalty for the Recipients exercise of its rights under this License to the Program. Contributors shall make the source code for the Contributor Version they distribute available at a cost, if any, equal to the cost to the Contributor to physically copy and distribute the work. It is not the intent of this License to prohibit a Contributor from charging fees for any service or maintenance that a Contributor may charge to a Recipient, so long as such fees are not an attempt to circumvent the foregoing restrictions on charging royalties or other recurring fees for the Program itself.
-
-3.4 A Contributor may create a Larger Work by combining the Program with other software code not governed by the terms of this License, and distribute the Larger Work as a single product. In such a case, the Contributor must make sure that the requirements of this License are fulfilled for the Program. Any Contributor who includes the Program in a commercial product offering, including as part of a Larger Work, may subject itself, but not any other Contributor, to additional contractual commitments, including, but not limited to, performance warranties and non-infringement representations on suchContributors behalf. No Contributor may create any additional liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor (Commercial Contributor) hereby agrees to defend and indemnify every other Contributor (Indemnified Contributor) who made Contributions to the Program distributed by the Commercial Contributor against any losses, damages and costs (collectively Losses) arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions, including any additional contractual commitments, of such Commercial Contributor in connection with its distribution of the Program. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement.
-
-3.5 If Contributor has knowledge that a license under a third partys intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must (a) include a text file with the Program source code distribution titled ../IP_ISSUES, and (b) notify CA in writing at Computer Associates International, Inc., One Computer Associates Plaza, Islandia, New York 11749, Attn: Open Source Group or by email at opensource@ca.com, both describing the claim and the party making the claim in sufficient detail that a Recipient and CA will know whom to contact with regard to such matter. If Contributor obtains such knowledge after the Contribution is made available, Contributor shall also promptly modify the IP_ISSUES file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Program that such new knowledge has been obtained.
-
-3.6 Recipient shall not remove, obscure, or modify any CA or other Contributor copyright or patent proprietary notices appearing in the Program, whether in the source code, object code or in any documentation. In addition to the obligations set forth in Section 4, each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
-
-4. CONTRIBUTION RESTRICTIONS
-
-4.1 Each Contributor must cause the Program to which the Contributor provides a Contribution to contain a file documenting the changes the Contributor made to create its version of the Program and the date of any change. Each Contributor must also include a prominent statement that the Contribution is derived, directly or indirectly, from the Program distributed by a prior Contributor, including the name of the prior Contributor from which such Contribution was derived, in (a) the Program source code, and (b) in any notice in an executable version or related documentation in which the Contributor describes the origin or ownership of the Program.
-
-5. NO WARRANTY
-
-5.1 EXCEPT AS EXPRESSLY SET FORTH IN THIS LICENSE, THE PROGRAM IS PROVIDED AS IS AND IN ITS PRESENT STATE AND CONDITION. NO WARRANTY, REPRESENTATION, CONDITION, UNDERTAKING OR TERM, EXPRESS OR IMPLIED, STATUTORY OR OTHERWISE, AS TO THE CONDITION, QUALITY, DURABILITY, PERFORMANCE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE OR USE OF THE PROGRAM IS GIVEN OR ASSUMED BY ANY CONTRIBUTOR AND ALL SUCH WARRANTIES, REPRESENTATIONS, CONDITIONS, UNDERTAKINGS AND TERMS ARE HEREBY EXCLUDED TO THE FULLEST EXTENT PERMITTED BY LAW.
-
-5.2 Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this License, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
-
-5.3 Each Recipient acknowledges that the Program is not intended for use in the operation of nuclear facilities, aircraft navigation, communication systems, or air traffic control machines in which case the failure of the Program could lead to death, personal injury, or severe physical or environmental damage.
-
-6. DISCLAIMER OF LIABILITY
-
-6.1 EXCEPT AS EXPRESSLY SET FORTH IN THIS LICENSE, AND TO THE EXTENT PERMITTED BY LAW, NO CONTRIBUTOR SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. TRADEMARKS AND BRANDING
-
-7.1 This License does not grant any Recipient or any third party any rights to use the trademarks or trade names now or subsequently posted at http://www.ca.com/catrdmrk.htm, or any other trademarks, service marks, logos or trade names belonging to CA (collectively CA Marks) or to any trademark, service mark, logo or trade name belonging to any Contributor. Recipient agrees not to use any CA Marks in or as part of the name of products derived from the Original Program or to endorse or promote products derived from the Original Program.
-
-7.2 Subject to Section 7.1, Recipients may distribute the Program under trademarks, logos, and product names belonging to the Recipient provided that all copyright and other attribution notices remain in the Program.
-
-8. PATENT LITIGATION
-
-8.1 If Recipient institutes patent litigation against any person or entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipients patent(s), then such Recipients rights granted under Section 2.2 shall terminate as of the date such litigation is filed.
-
-9. OWNERSHIP
-
-9.1 Subject to the licenses granted under this License in Sections 2.1 and 2.2 above, each Contributor retains all rights, title and interest in and to any Contributions made by such Contributor. CA retains all rights, title and interest in and to the Original Program and any Contributions made by or on behalf of CA (CA Contributions), and such CA Contributions will not be automatically subject to this License. CA may, at its sole discretion, choose to license such CA Contributions under this License, or on different terms from those contained in this License or may choose not to license them at all.
-
-10. TERMINATION
-
-10.1 All of Recipients rights under this License shall terminate if it fails to comply with any of the material terms or conditions of this License and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If Recipients rights under this License terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipients obligations under this License and any licenses granted by Recipient as a Contributor relating to the Program shall continue and survive termination.
-
-11. GENERAL
-
-11.1 If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
-11.2 CA may publish new versions (including revisions) of this License from time to time. Each new version of the License will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the License under which it was received. In addition, after a new version of the License is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. No one other than CA has the right to modify this License.
-
-11.3 If it is impossible for Recipient to comply with any of the terms of this License with respect to some or all of the Program due to statute, judicial order, or regulation, then Recipient must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the IP_ISSUES file described in Section 3.5 and must be included with all distributions of the Program source code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a Recipient of ordinary skill to be able to understand it.
-
-11.4 This License is governed by the laws of the State of New York. No Recipient will bring a legal action under this License more than one year after the cause of action arose. Each Recipient waives its rights to a jury trial in any resulting litigation. Any litigation or other dispute resolution between a Recipient and CA relating to this License shall take place in the State of New York, and Recipient and CA hereby consent to the personal jurisdiction of, and venue in, the state and federal courts within that district with respect to this License. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded.
-
-11.5 Where Recipient is located in the province of Quebec, Canada, the following clause applies: The parties hereby confirm that they have requested that this License and all related documents be drafted in English. Les parties contractantes confirment qu'elles ont exige que le present contrat et tous les documents associes soient rediges en anglais.
-
-11.6 The Program is subject to all export and import laws, restrictions and regulations of the country in which Recipient receives the Program. Recipient is solely responsible for complying with and ensuring that Recipient does not export, re-export, or import the Program in violation of such laws, restrictions or regulations, or without any necessary licenses and authorizations.
-
-11.7 This License constitutes the entire agreement between the parties with respect to the subject matter hereof.
diff --git a/options/license/CC-BY-1.0 b/options/license/CC-BY-1.0
deleted file mode 100644
index 42a5f3f71b..0000000000
--- a/options/license/CC-BY-1.0
+++ /dev/null
@@ -1,80 +0,0 @@
-Creative Commons Attribution 1.0
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DRAFT LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to create and reproduce Derivative Works;
-
- c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
- d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works;
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any reference to such Licensor or the Original Author, as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any reference to such Licensor or the Original Author, as requested.
-
- b. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and give the Original Author credit reasonable to the medium or means You are utilizing by conveying the name (or pseudonym if applicable) of the Original Author if supplied; the title of the Work if supplied; in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
-5. Representations, Warranties and Disclaimer
-
- a. By offering the Work for public release under this License, Licensor represents and warrants that, to the best of Licensor's knowledge after reasonable inquiry:
-
- i. Licensor has secured all rights in the Work necessary to grant the license rights hereunder and to permit the lawful exercise of the rights granted hereunder without You having any obligation to pay any royalties, compulsory license fees, residuals or any other payments;
-
- ii. The Work does not infringe the copyright, trademark, publicity rights, common law rights or any other right of any third party or constitute defamation, invasion of privacy or other tortious injury to any third party.
-
- b. EXCEPT AS EXPRESSLY STATED IN THIS LICENSE OR OTHERWISE AGREED IN WRITING OR REQUIRED BY APPLICABLE LAW, THE WORK IS LICENSED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES REGARDING THE CONTENTS OR ACCURACY OF THE WORK.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, AND EXCEPT FOR DAMAGES ARISING FROM LIABILITY TO A THIRD PARTY RESULTING FROM BREACH OF THE WARRANTIES IN SECTION 5, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-2.0 b/options/license/CC-BY-2.0
deleted file mode 100644
index 86f93505c5..0000000000
--- a/options/license/CC-BY-2.0
+++ /dev/null
@@ -1,81 +0,0 @@
-Creative Commons Attribution 2.0
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to create and reproduce Derivative Works;
-
- c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
- d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
-
- e. For the avoidance of doubt, where the work is a musical composition:
-
- i. Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
-
- ii. Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
-
- f. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any reference to such Licensor or the Original Author, as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any reference to such Licensor or the Original Author, as requested.
-
- b. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and give the Original Author credit reasonable to the medium or means You are utilizing by conveying the name (or pseudonym if applicable) of the Original Author if supplied; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-2.5 b/options/license/CC-BY-2.5
deleted file mode 100644
index c20d60f8ab..0000000000
--- a/options/license/CC-BY-2.5
+++ /dev/null
@@ -1,81 +0,0 @@
-Creative Commons Attribution 2.5
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to create and reproduce Derivative Works;
-
- c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
- d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
-
- e. For the avoidance of doubt, where the work is a musical composition:
-
- i. Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
-
- ii. Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
-
- f. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(b), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(b), as requested.
-
- b. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-2.5-AU b/options/license/CC-BY-2.5-AU
deleted file mode 100644
index 23b8800919..0000000000
--- a/options/license/CC-BY-2.5-AU
+++ /dev/null
@@ -1,112 +0,0 @@
-Creative Commons Attribution 2.5 Australia
-
-CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENCE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-Licence
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENCE ("CCPL" OR "LICENCE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORISED UNDER THIS LICENCE AND/OR APPLICABLE LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENCE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
- 1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopaedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this Licence.
-
- b. "Derivative Work" means a work that reproduces a substantial part of the Work, or of the Work and other pre-existing works protected by copyright, or that is an adaptation of a Work that is a literary, dramatic, musical or artistic work. Derivative Works include a translation, musical arrangement, dramatisation, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which a work may be adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this Licence. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this Licence.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this Licence.
-
- d. "Moral rights law" means laws under which an individual who creates a work protected by copyright has rights of integrity of authorship of the work, rights of attribution of authorship of the work, rights not to have authorship of the work falsely attributed, or rights of a similar or analogous nature in the work anywhere in the world.
-
- e. "Original Author" means the individual or entity who created the Work.
-
- f. "Work" means the work or other subject-matter protected by copyright that is offered under the terms of this Licence, which may include (without limitation) a literary, dramatic, musical or artistic work, a sound recording or cinematograph film, a published edition of a literary, dramatic, musical or artistic work or a television or sound broadcast.
-
- g. "You" means an individual or entity exercising rights under this Licence who has not previously violated the terms of this Licence with respect to the Work, or who has received express permission from the Licensor to exercise rights under this Licence despite a previous violation.
-
- h. "Licence Elements" means the following high-level licence attributes as selected by Licensor and indicated in the title of this Licence: Attribution, NonCommercial, NoDerivatives, ShareAlike.
-
-2. Fair Dealing and Other Rights. Nothing in this Licence excludes or modifies, or is intended to exclude or modify, (including by reducing, limiting, or restricting) the rights of You or others to use the Work arising from fair dealings or other limitations on the rights of the copyright owner or the Original Author under copyright law, moral rights law or other applicable laws.
-
-3. Licence Grant. Subject to the terms and conditions of this Licence, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) licence to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to create and reproduce Derivative Works;
-
- c. to publish, communicate to the public, distribute copies or records of, exhibit or display publicly, perform publicly and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
- d. to publish, communicate to the public, distribute copies or records of, exhibit or display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works;
-
- e. For the avoidance of doubt, where the Work is a musical composition:
-
- i. Performance Royalties Under Blanket Licences. Licensor will not collect, whether individually or via a performance rights society, royalties for Your communication to the public, broadcast, public performance or public digital performance (e.g. webcast) of the Work.
-
- ii. Mechanical Rights and Statutory Royalties. Licensor will not collect, whether individually or via a music rights agency, designated agent or a music publisher, royalties for any record You create from the Work ("cover version") and distribute, subject to the compulsory licence created by 17 USC Section 115 of the US Copyright Act (or an equivalent statutory licence under the Australian Copyright Act or in other jurisdictions).
-
-
- f. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor will not collect, whether individually or via a performance-rights society, royalties for Your public digital performance (e.g. webcast) of the Work, subject to the compulsory licence created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor under this Licence are hereby reserved.
-
-4. Restrictions. The licence granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may publish, communicate to the public, distribute, publicly exhibit or display, publicly perform, or publicly digitally perform the Work only under the terms of this Licence, and You must include a copy of, or the Uniform Resource Identifier for, this Licence with every copy or record of the Work You publish, communicate to the public, distribute, publicly exhibit or display, publicly perform or publicly digitally perform. You may not offer or impose any terms on the Work that exclude, alter or restrict the terms of this Licence or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this Licence and to the disclaimer of representations and warranties. You may not publish, communicate to the public, distribute, publicly exhibit or display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this Licence. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this Licence. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by Section 4(b), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by Section 4(b), as requested.
-
- b. If you publish, communicate to the public, distribute, publicly exhibit or display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work. You must also give clear and reasonably prominent credit to (i) the Original Author (by name or pseudonym if applicable), if the name or pseudonym is supplied; and (ii) if another party or parties (eg a sponsor institute, publishing entity or journal) is designated for attribution in the copyright notice, terms of service or other reasonable means associated with the Work, such party or parties. If applicable, that credit must be given in the particular way made known by the Original Author and otherwise as reasonable to the medium or means You are utilizing, by conveying the identity of the Original Author and the other designated party or parties (if applicable); the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
- c. False attribution prohibited. Except as otherwise agreed in writing by the Licensor, if You publish, communicate to the public, distribute, publicly exhibit or display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works in accordance with this Licence, You must not falsely attribute the Work to someone other than the Original Author.
-
- d. Prejudice to honour or reputation prohibited. Except as otherwise agreed in writing by the Licensor, if you publish, communicate to the public, distribute, publicly exhibit or display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must not do anything that results in a material distortion of, the mutilation of, or a material alteration to, the Work that is prejudicial to the Original Author's honour or reputation, and You must not do anything else in relation to the Work that is prejudicial to the Original Author's honour or reputation.
-
-5. Disclaimer.
-
-EXCEPT AS EXPRESSLY STATED IN THIS LICENCE OR OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, AND TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK "AS-IS" AND MAKES NO REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, ANY REPRESENTATIONS, WARRANTIES OR CONDITIONS REGARDING THE CONTENTS OR ACCURACY OF THE WORK, OR OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, THE ABSENCE OF LATENT OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE.
-
-6. Limitation on Liability.
-
-TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, AND EXCEPT FOR ANY LIABILITY ARISING FROM CONTRARY MUTUAL AGREEMENT AS REFERRED TO IN SECTION 5, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) FOR ANY LOSS OR DAMAGE WHATSOEVER, INCLUDING (WITHOUT LIMITATION) LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR CORRUPTION OF DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS, OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC LOSS; OR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THIS LICENCE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-If applicable legislation implies warranties or conditions, or imposes obligations or liability on the Licensor in respect of this Licence that cannot be wholly or partly excluded, restricted or modified, the Licensor's liability is limited, to the full extent permitted by the applicable legislation, at its option, to:
-
- a. in the case of goods, any one or more of the following:
-
- i. the replacement of the goods or the supply of equivalent goods;
-
- ii. the repair of the goods;
-
- iii. the payment of the cost of replacing the goods or of acquiring equivalent goods;
-
- iv. the payment of the cost of having the goods repaired; or
-
- b. in the case of services:
-
- i. the supplying of the services again; or
-
- ii. the payment of the cost of having the services supplied again.
-
-7. Termination.
-
- a. This Licence and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this Licence. Individuals or entities who have received Derivative Works or Collective Works from You under this Licence, however, will not have their licences terminated provided such individuals or entities remain in full compliance with those licences. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this Licence.
-
- b. Subject to the above terms and conditions, the licence granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different licence terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this Licence (or any other licence that has been, or is required to be, granted under the terms of this Licence), and this Licence will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous.
-
- a. Each time You publish, communicate to the public, distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a licence to the Work on the same terms and conditions as the licence granted to You under this Licence.
-
- b. Each time You publish, communicate to the public, distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a licence to the original Work on the same terms and conditions as the licence granted to You under this Licence.
-
- c. If any provision of this Licence is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Licence, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this Licence shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This Licence constitutes the entire agreement between the parties with respect to the Work licensed here. To the full extent permitted by applicable law, there are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This Licence may not be modified without the mutual written agreement of the Licensor and You.
-
- f. The construction, validity and performance of this Licence shall be governed by the laws in force in New South Wales, Australia.
-
-Creative Commons is not a party to this Licence, and, to the full extent permitted by applicable law, makes no representation or warranty whatsoever in connection with the Work. To the full extent permitted by applicable law, Creative Commons will not be liable to You or any party on any legal theory (including, without limitation, negligence) for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this licence. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-3.0 b/options/license/CC-BY-3.0
deleted file mode 100644
index 1a16e05564..0000000000
--- a/options/license/CC-BY-3.0
+++ /dev/null
@@ -1,319 +0,0 @@
-Creative Commons Legal Code
-
-Attribution 3.0 Unported
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
- LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
- ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
- INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
- REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
- DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
-COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
-COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
-AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
-TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
-BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
-CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
-CONDITIONS.
-
-1. Definitions
-
- a. "Adaptation" means a work based upon the Work, or upon the Work and
- other pre-existing works, such as a translation, adaptation,
- derivative work, arrangement of music or other alterations of a
- literary or artistic work, or phonogram or performance and includes
- cinematographic adaptations or any other form in which the Work may be
- recast, transformed, or adapted including in any form recognizably
- derived from the original, except that a work that constitutes a
- Collection will not be considered an Adaptation for the purpose of
- this License. For the avoidance of doubt, where the Work is a musical
- work, performance or phonogram, the synchronization of the Work in
- timed-relation with a moving image ("synching") will be considered an
- Adaptation for the purpose of this License.
- b. "Collection" means a collection of literary or artistic works, such as
- encyclopedias and anthologies, or performances, phonograms or
- broadcasts, or other works or subject matter other than works listed
- in Section 1(f) below, which, by reason of the selection and
- arrangement of their contents, constitute intellectual creations, in
- which the Work is included in its entirety in unmodified form along
- with one or more other contributions, each constituting separate and
- independent works in themselves, which together are assembled into a
- collective whole. A work that constitutes a Collection will not be
- considered an Adaptation (as defined above) for the purposes of this
- License.
- c. "Distribute" means to make available to the public the original and
- copies of the Work or Adaptation, as appropriate, through sale or
- other transfer of ownership.
- d. "Licensor" means the individual, individuals, entity or entities that
- offer(s) the Work under the terms of this License.
- e. "Original Author" means, in the case of a literary or artistic work,
- the individual, individuals, entity or entities who created the Work
- or if no individual or entity can be identified, the publisher; and in
- addition (i) in the case of a performance the actors, singers,
- musicians, dancers, and other persons who act, sing, deliver, declaim,
- play in, interpret or otherwise perform literary or artistic works or
- expressions of folklore; (ii) in the case of a phonogram the producer
- being the person or legal entity who first fixes the sounds of a
- performance or other sounds; and, (iii) in the case of broadcasts, the
- organization that transmits the broadcast.
- f. "Work" means the literary and/or artistic work offered under the terms
- of this License including without limitation any production in the
- literary, scientific and artistic domain, whatever may be the mode or
- form of its expression including digital form, such as a book,
- pamphlet and other writing; a lecture, address, sermon or other work
- of the same nature; a dramatic or dramatico-musical work; a
- choreographic work or entertainment in dumb show; a musical
- composition with or without words; a cinematographic work to which are
- assimilated works expressed by a process analogous to cinematography;
- a work of drawing, painting, architecture, sculpture, engraving or
- lithography; a photographic work to which are assimilated works
- expressed by a process analogous to photography; a work of applied
- art; an illustration, map, plan, sketch or three-dimensional work
- relative to geography, topography, architecture or science; a
- performance; a broadcast; a phonogram; a compilation of data to the
- extent it is protected as a copyrightable work; or a work performed by
- a variety or circus performer to the extent it is not otherwise
- considered a literary or artistic work.
- g. "You" means an individual or entity exercising rights under this
- License who has not previously violated the terms of this License with
- respect to the Work, or who has received express permission from the
- Licensor to exercise rights under this License despite a previous
- violation.
- h. "Publicly Perform" means to perform public recitations of the Work and
- to communicate to the public those public recitations, by any means or
- process, including by wire or wireless means or public digital
- performances; to make available to the public Works in such a way that
- members of the public may access these Works from a place and at a
- place individually chosen by them; to perform the Work to the public
- by any means or process and the communication to the public of the
- performances of the Work, including by public digital performance; to
- broadcast and rebroadcast the Work by any means including signs,
- sounds or images.
- i. "Reproduce" means to make copies of the Work by any means including
- without limitation by sound or visual recordings and the right of
- fixation and reproducing fixations of the Work, including storage of a
- protected performance or phonogram in digital form or other electronic
- medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce,
-limit, or restrict any uses free from copyright or rights arising from
-limitations or exceptions that are provided for in connection with the
-copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License,
-Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
-perpetual (for the duration of the applicable copyright) license to
-exercise the rights in the Work as stated below:
-
- a. to Reproduce the Work, to incorporate the Work into one or more
- Collections, and to Reproduce the Work as incorporated in the
- Collections;
- b. to create and Reproduce Adaptations provided that any such Adaptation,
- including any translation in any medium, takes reasonable steps to
- clearly label, demarcate or otherwise identify that changes were made
- to the original Work. For example, a translation could be marked "The
- original work was translated from English to Spanish," or a
- modification could indicate "The original work has been modified.";
- c. to Distribute and Publicly Perform the Work including as incorporated
- in Collections; and,
- d. to Distribute and Publicly Perform Adaptations.
- e. For the avoidance of doubt:
-
- i. Non-waivable Compulsory License Schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme cannot be waived, the Licensor
- reserves the exclusive right to collect such royalties for any
- exercise by You of the rights granted under this License;
- ii. Waivable Compulsory License Schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme can be waived, the Licensor waives the
- exclusive right to collect such royalties for any exercise by You
- of the rights granted under this License; and,
- iii. Voluntary License Schemes. The Licensor waives the right to
- collect royalties, whether individually or, in the event that the
- Licensor is a member of a collecting society that administers
- voluntary licensing schemes, via that society, from any exercise
- by You of the rights granted under this License.
-
-The above rights may be exercised in all media and formats whether now
-known or hereafter devised. The above rights include the right to make
-such modifications as are technically necessary to exercise the rights in
-other media and formats. Subject to Section 8(f), all rights not expressly
-granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made
-subject to and limited by the following restrictions:
-
- a. You may Distribute or Publicly Perform the Work only under the terms
- of this License. You must include a copy of, or the Uniform Resource
- Identifier (URI) for, this License with every copy of the Work You
- Distribute or Publicly Perform. You may not offer or impose any terms
- on the Work that restrict the terms of this License or the ability of
- the recipient of the Work to exercise the rights granted to that
- recipient under the terms of the License. You may not sublicense the
- Work. You must keep intact all notices that refer to this License and
- to the disclaimer of warranties with every copy of the Work You
- Distribute or Publicly Perform. When You Distribute or Publicly
- Perform the Work, You may not impose any effective technological
- measures on the Work that restrict the ability of a recipient of the
- Work from You to exercise the rights granted to that recipient under
- the terms of the License. This Section 4(a) applies to the Work as
- incorporated in a Collection, but this does not require the Collection
- apart from the Work itself to be made subject to the terms of this
- License. If You create a Collection, upon notice from any Licensor You
- must, to the extent practicable, remove from the Collection any credit
- as required by Section 4(b), as requested. If You create an
- Adaptation, upon notice from any Licensor You must, to the extent
- practicable, remove from the Adaptation any credit as required by
- Section 4(b), as requested.
- b. If You Distribute, or Publicly Perform the Work or any Adaptations or
- Collections, You must, unless a request has been made pursuant to
- Section 4(a), keep intact all copyright notices for the Work and
- provide, reasonable to the medium or means You are utilizing: (i) the
- name of the Original Author (or pseudonym, if applicable) if supplied,
- and/or if the Original Author and/or Licensor designate another party
- or parties (e.g., a sponsor institute, publishing entity, journal) for
- attribution ("Attribution Parties") in Licensor's copyright notice,
- terms of service or by other reasonable means, the name of such party
- or parties; (ii) the title of the Work if supplied; (iii) to the
- extent reasonably practicable, the URI, if any, that Licensor
- specifies to be associated with the Work, unless such URI does not
- refer to the copyright notice or licensing information for the Work;
- and (iv) , consistent with Section 3(b), in the case of an Adaptation,
- a credit identifying the use of the Work in the Adaptation (e.g.,
- "French translation of the Work by Original Author," or "Screenplay
- based on original Work by Original Author"). The credit required by
- this Section 4 (b) may be implemented in any reasonable manner;
- provided, however, that in the case of a Adaptation or Collection, at
- a minimum such credit will appear, if a credit for all contributing
- authors of the Adaptation or Collection appears, then as part of these
- credits and in a manner at least as prominent as the credits for the
- other contributing authors. For the avoidance of doubt, You may only
- use the credit required by this Section for the purpose of attribution
- in the manner set out above and, by exercising Your rights under this
- License, You may not implicitly or explicitly assert or imply any
- connection with, sponsorship or endorsement by the Original Author,
- Licensor and/or Attribution Parties, as appropriate, of You or Your
- use of the Work, without the separate, express prior written
- permission of the Original Author, Licensor and/or Attribution
- Parties.
- c. Except as otherwise agreed in writing by the Licensor or as may be
- otherwise permitted by applicable law, if You Reproduce, Distribute or
- Publicly Perform the Work either by itself or as part of any
- Adaptations or Collections, You must not distort, mutilate, modify or
- take other derogatory action in relation to the Work which would be
- prejudicial to the Original Author's honor or reputation. Licensor
- agrees that in those jurisdictions (e.g. Japan), in which any exercise
- of the right granted in Section 3(b) of this License (the right to
- make Adaptations) would be deemed to be a distortion, mutilation,
- modification or other derogatory action prejudicial to the Original
- Author's honor and reputation, the Licensor will waive or not assert,
- as appropriate, this Section, to the fullest extent permitted by the
- applicable national law, to enable You to reasonably exercise Your
- right under Section 3(b) of this License (right to make Adaptations)
- but not otherwise.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
-OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
-KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
-INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
-FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
-LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
-WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
-OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
-LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
-ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
-ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
-BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate
- automatically upon any breach by You of the terms of this License.
- Individuals or entities who have received Adaptations or Collections
- from You under this License, however, will not have their licenses
- terminated provided such individuals or entities remain in full
- compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
- survive any termination of this License.
- b. Subject to the above terms and conditions, the license granted here is
- perpetual (for the duration of the applicable copyright in the Work).
- Notwithstanding the above, Licensor reserves the right to release the
- Work under different license terms or to stop distributing the Work at
- any time; provided, however that any such election will not serve to
- withdraw this License (or any other license that has been, or is
- required to be, granted under the terms of this License), and this
- License will continue in full force and effect unless terminated as
- stated above.
-
-8. Miscellaneous
-
- a. Each time You Distribute or Publicly Perform the Work or a Collection,
- the Licensor offers to the recipient a license to the Work on the same
- terms and conditions as the license granted to You under this License.
- b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
- offers to the recipient a license to the original Work on the same
- terms and conditions as the license granted to You under this License.
- c. If any provision of this License is invalid or unenforceable under
- applicable law, it shall not affect the validity or enforceability of
- the remainder of the terms of this License, and without further action
- by the parties to this agreement, such provision shall be reformed to
- the minimum extent necessary to make such provision valid and
- enforceable.
- d. No term or provision of this License shall be deemed waived and no
- breach consented to unless such waiver or consent shall be in writing
- and signed by the party to be charged with such waiver or consent.
- e. This License constitutes the entire agreement between the parties with
- respect to the Work licensed here. There are no understandings,
- agreements or representations with respect to the Work not specified
- here. Licensor shall not be bound by any additional provisions that
- may appear in any communication from You. This License may not be
- modified without the mutual written agreement of the Licensor and You.
- f. The rights granted under, and the subject matter referenced, in this
- License were drafted utilizing the terminology of the Berne Convention
- for the Protection of Literary and Artistic Works (as amended on
- September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
- Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
- and the Universal Copyright Convention (as revised on July 24, 1971).
- These rights and subject matter take effect in the relevant
- jurisdiction in which the License terms are sought to be enforced
- according to the corresponding provisions of the implementation of
- those treaty provisions in the applicable national law. If the
- standard suite of rights granted under applicable copyright law
- includes additional rights not granted under this License, such
- additional rights are deemed to be included in the License; this
- License is not intended to restrict the license of any rights under
- applicable law.
-
-
-Creative Commons Notice
-
- Creative Commons is not a party to this License, and makes no warranty
- whatsoever in connection with the Work. Creative Commons will not be
- liable to You or any party on any legal theory for any damages
- whatsoever, including without limitation any general, special,
- incidental or consequential damages arising in connection to this
- license. Notwithstanding the foregoing two (2) sentences, if Creative
- Commons has expressly identified itself as the Licensor hereunder, it
- shall have all rights and obligations of Licensor.
-
- Except for the limited purpose of indicating to the public that the
- Work is licensed under the CCPL, Creative Commons does not authorize
- the use by either party of the trademark "Creative Commons" or any
- related trademark or logo of Creative Commons without the prior
- written consent of Creative Commons. Any permitted use will be in
- compliance with Creative Commons' then-current trademark usage
- guidelines, as may be published on its website or otherwise made
- available upon request from time to time. For the avoidance of doubt,
- this trademark restriction does not form part of this License.
-
- Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-3.0-AT b/options/license/CC-BY-3.0-AT
deleted file mode 100644
index b22fedef2d..0000000000
--- a/options/license/CC-BY-3.0-AT
+++ /dev/null
@@ -1,111 +0,0 @@
-CREATIVE COMMONS IST KEINE RECHTSANWALTSKANZLEI UND LEISTET KEINE RECHTSBERATUNG. DIE BEREITSTELLUNG DIESER LIZENZ FÜHRT ZU KEINEM MANDATSVERHÄLTNIS. CREATIVE COMMONS STELLT DIESE INFORMATIONEN OHNE GEWÄHR ZUR VERFÜGUNG. CREATIVE COMMONS ÜBERNIMMT KEINE GEWÄHRLEISTUNG FÜR DIE GELIEFERTEN INFORMATIONEN UND SCHLIEßT DIE HAFTUNG FÜR SCHÄDEN AUS, DIE SICH AUS DEREN GEBRAUCH ERGEBEN.
-
-Lizenz
-
-DER GEGENSTAND DIESER LIZENZ (WIE UNTER "SCHUTZGEGENSTAND" DEFINIERT) WIRD UNTER DEN BEDINGUNGEN DIESER CREATIVE COMMONS PUBLIC LICENSE ("CCPL", "LIZENZ" ODER "LIZENZVERTRAG") ZUR VERFÜGUNG GESTELLT. DER SCHUTZGEGENSTAND IST DURCH DAS URHEBERRECHT UND/ODER ANDERE GESETZE GESCHÜTZT. JEDE FORM DER NUTZUNG DES SCHUTZGEGENSTANDES, DIE NICHT AUFGRUND DIESER LIZENZ ODER DURCH GESETZE GESTATTET IST, IST UNZULÄSSIG.
-
-DURCH DIE AUSÜBUNG EINES DURCH DIESE LIZENZ GEWÄHRTEN RECHTS AN DEM SCHUTZGEGENSTAND ERKLÄREN SIE SICH MIT DEN LIZENZBEDINGUNGEN RECHTSVERBINDLICH EINVERSTANDEN. SOWEIT DIESE LIZENZ ALS LIZENZVERTRAG ANZUSEHEN IST, GEWÄHRT IHNEN DER LIZENZGEBER DIE IN DER LIZENZ GENANNTEN RECHTE UNENTGELTLICH UND IM AUSTAUSCH DAFÜR, DASS SIE DAS GEBUNDENSEIN AN DIE LIZENZBEDINGUNGEN AKZEPTIEREN.
-
-1. Definitionen
-
- a. Der Begriff "Bearbeitung" im Sinne dieser Lizenz bezeichnet das Ergebnis jeglicher Art von Veränderung des Schutzgegenstandes, solange dieses erkennbar vom Schutzgegenstand abgeleitet wurde. Dies kann insbesondere auch eine Umgestaltung, Änderung, Anpassung, Übersetzung oder Heranziehung des Schutzgegenstandes zur Vertonung von Laufbildern sein. Nicht als Bearbeitung des Schutzgegenstandes gelten seine Aufnahme in eine Sammlung oder ein Sammelwerk und die freie Nutzung des Schutzgegenstandes.
-
- b. Der Begriff "Sammelwerk" im Sinne dieser Lizenz meint eine Zusammenstellung von literarischen, künstlerischen oder wissenschaftlichen Inhalten zu einem einheitlichen Ganzen, sofern diese Zusammenstellung aufgrund von Auswahl und Anordnung der darin enthaltenen selbständigen Elemente eine eigentümliche geistige Schöpfung darstellt, unabhängig davon, ob die Elemente systematisch oder methodisch angelegt und dadurch einzeln zugänglich sind oder nicht.
-
- c. "Verbreiten" im Sinne dieser Lizenz bedeutet, den Schutzgegenstand oder Bearbeitungen im Original oder in Form von Vervielfältigungsstücken, mithin in körperlich fixierter Form der Öffentlichkeit zugänglich zu machen oder in Verkehr zu bringen.
-
- d. Der "Lizenzgeber" im Sinne dieser Lizenz ist diejenige natürliche oder juristische Person oder Gruppe, die den Schutzgegenstand unter den Bedingungen dieser Lizenz anbietet und insoweit als Rechteinhaberin auftritt.
-
- e. "Rechteinhaber" im Sinne dieser Lizenz ist der Urheber des Schutzgegenstandes oder jede andere natürliche oder juristische Person, die am Schutzgegenstand ein Immaterialgüterrecht erlangt hat, welches die in Abschnitt 3 genannten Handlungen erfasst und eine Erteilung, Übertragung oder Einräumung von Nutzungsbewilligungen bzw Nutzungsrechten an Dritte erlaubt.
-
- f. Der Begriff "Schutzgegenstand" bezeichnet in dieser Lizenz den literarischen, künstlerischen oder wissenschaftlichen Inhalt, der unter den Bedingungen dieser Lizenz angeboten wird. Das kann insbesondere eine eigentümliche geistige Schöpfung jeglicher Art oder ein Werk der kleinen Münze, ein nachgelassenes Werk oder auch ein Lichtbild oder anderes Objekt eines verwandten Schutzrechts sein, unabhängig von der Art seiner Fixierung und unabhängig davon, auf welche Weise jeweils eine Wahrnehmung erfolgen kann, gleichviel ob in analoger oder digitaler Form. Soweit Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen, unterfallen auch sie dem Begriff „Schutzgegenstand“ im Sinne dieser Lizenz.
-
- g. Mit "Sie" bzw. "Ihnen" ist die natürliche oder juristische Person gemeint, die in dieser Lizenz im Abschnitt 3 genannte Nutzungen des Schutzgegenstandes vornimmt und zuvor in Hinblick auf den Schutzgegenstand nicht gegen Bedingungen dieser Lizenz verstoßen oder aber die ausdrückliche Erlaubnis des Lizenzgebers erhalten hat, die durch diese Lizenz gewährte Nutzungsbewilligung trotz eines vorherigen Verstoßes auszuüben.
-
- h. Unter "Öffentlich Wiedergeben" im Sinne dieser Lizenz sind Wahrnehmbarmachungen des Schutzgegenstandes in unkörperlicher Form zu verstehen, die für eine Mehrzahl von Mitgliedern der Öffentlichkeit bestimmt sind und mittels öffentlicher Wiedergabe in Form von Vortrag, Aufführung, Vorführung, Darbietung, Sendung, Weitersendung oder zeit- und ortsunabhängiger Zurverfügungstellung erfolgen, unabhängig von den zum Einsatz kommenden Techniken und Verfahren, einschließlich drahtgebundener oder drahtloser Mittel und Einstellen in das Internet.
-
- i. "Vervielfältigen" im Sinne dieser Lizenz bedeutet, gleichviel in welchem Verfahren, auf welchem Träger, in welcher Menge und ob vorübergehend oder dauerhaft, Vervielfältigungsstücke des Schutzgegenstandes herzustellen, insbesondere durch Ton- oder Bildaufzeichnungen, und umfasst auch das erstmalige Festhalten des Schutzgegenstandes oder dessen Wahrnehmbarmachung auf Mitteln der wiederholbaren Wiedergabe sowie das Herstellen von Vervielfältigungsstücken dieser Festhaltung, sowie die Speicherung einer geschützten Darbietung oder eines Bild- und/oder Schallträgers in digitaler Form oder auf einem anderen elektronischen Medium.
-
-2. Beschränkungen der Verwertungsrechte
-
-Diese Lizenz ist in keiner Weise darauf gerichtet, Befugnisse zur Nutzung des Schutzgegenstandes zu vermindern, zu beschränken oder zu vereiteln, die sich aus den Beschränkungen der Verwertungsrechte, anderen Beschränkungen der Ausschließlichkeitsrechte des Rechtsinhabers oder anderen entsprechenden Rechtsnormen oder sich aus dem Fehlen eines immaterialgüterrechtlichen Schutzes ergeben.
-
-3. Lizenzierung
-
-Unter den Bedingungen dieser Lizenz erteilt Ihnen der Lizenzgeber - unbeschadet unverzichtbarer Rechte und vorbehaltlich des Abschnitts 3.e) - die vergütungsfreie, räumlich und zeitlich (für die Dauer des Urheberrechts oder verwandten Schutzrechts am Schutzgegenstand) unbeschränkte Nutzungsbewilligung, den Schutzgegenstand in der folgenden Art und Weise zu nutzen:
-
- a. Den Schutzgegenstand in beliebiger Form und Menge zu vervielfältigen, ihn in Sammelwerke zu integrieren und ihn als Teil solcher Sammelwerke zu vervielfältigen;
-
- b. Den Schutzgegenstand zu bearbeiten, einschließlich Übersetzungen unter Nutzung jedweder Medien anzufertigen, sofern deutlich erkennbar gemacht wird, dass es sich um eine Bearbeitung handelt;
-
- c. Den Schutzgegenstand, allein oder in Sammelwerke aufgenommen, öffentlich wiederzugeben und zu verbreiten; und
-
- d. Bearbeitungen des Schutzgegenstandes zu veröffentlichen, öffentlich wiederzugeben und zu verbreiten.
-
- e. Bezüglich der Vergütung für die Nutzung des Schutzgegenstandes gilt Folgendes:
-
- i. Unverzichtbare gesetzliche Vergütungsansprüche: Soweit unverzichtbare Vergütungsansprüche im Gegenzug für gesetzliche Lizenzen vorgesehen oder Pauschalabgabensysteme (zum Beispiel für Leermedien) vorhanden sind, behält sich der Lizenzgeber das ausschließliche Recht vor, die entsprechenden Vergütungsansprüche für jede Ausübung eines Rechts aus dieser Lizenz durch Sie geltend zu machen.
-
- ii. Vergütung bei Zwangslizenzen: Sofern Zwangslizenzen außerhalb dieser Lizenz vorgesehen sind und zustande kommen, verzichtet der Lizenzgeber für alle Fälle einer lizenzgerechten Nutzung des Schutzgegenstandes durch Sie auf jegliche Vergütung.
-
- iii. Vergütung in sonstigen Fällen: Bezüglich lizenzgerechter Nutzung des Schutzgegenstandes durch Sie, die nicht unter die beiden vorherigen Abschnitte (i) und (ii) fällt, verzichtet der Lizenzgeber auf jegliche Vergütung, unabhängig davon, ob eine Geltendmachung der Vergütungsansprüche durch ihn selbst oder nur durch eine Verwertungsgesellschaft möglich wäre.
-
-Die vorgenannte Nutzungsbewilligung wird für alle bekannten sowie alle noch nicht bekannten Nutzungsarten eingeräumt. Sie beinhaltet auch das Recht, solche Änderungen am Schutzgegenstand vorzunehmen, die für bestimmte nach dieser Lizenz zulässige Nutzungen technisch erforderlich sind. Alle sonstigen Rechte, die über diesen Abschnitt hinaus nicht ausdrücklich vom Lizenzgeber eingeräumt werden, bleiben diesem allein vorbehalten. Soweit Datenbanken oder Zusammenstellungen von Daten Schutzgegenstand dieser Lizenz oder Teil dessen sind und einen immaterialgüterrechtlichen Schutz eigener Art genießen, verzichtet der Lizenzgeber auf die Geltendmachung sämtlicher daraus resultierender Rechte.
-
-4. Bedingungen
-
-Die Erteilung der Nutzungsbewilligung gemäß Abschnitt 3 dieser Lizenz erfolgt ausdrücklich nur unter den folgenden Bedingungen:
-
- a. Sie dürfen den Schutzgegenstand ausschließlich unter den Bedingungen dieser Lizenz verbreiten oder öffentlich wiedergeben. Sie müssen dabei stets eine Kopie dieser Lizenz oder deren vollständige Internetadresse in Form des Uniform-Resource-Identifier (URI) beifügen. Sie dürfen keine Vertrags- oder Nutzungsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch diese Lizenz gewährten Rechte beschränken. Sie dürfen den Schutzgegenstand nicht unterlizenzieren. Bei jeder Kopie des Schutzgegenstandes, die Sie verbreiten oder öffentlich wiedergeben, müssen Sie alle Hinweise unverändert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Wenn Sie den Schutzgegenstand verbreiten oder öffentlich wiedergeben, dürfen Sie (in Bezug auf den Schutzgegenstand) keine technischen Maßnahmen ergreifen, die den Nutzer des Schutzgegenstandes in der Ausübung der ihm durch diese Lizenz gewährten Rechte behindern können. Dasselbe gilt auch für den Fall, dass der Schutzgegenstand einen Bestandteil eines Sammelwerkes bildet, was jedoch nicht bedeutet, dass das Sammelwerk insgesamt dieser Lizenz unterstellt werden muss. Sofern Sie ein Sammelwerk erstellen, müssen Sie - soweit dies praktikabel ist - auf die Mitteilung eines Lizenzgebers hin aus dem Sammelwerk die in Abschnitt 4.b) aufgezählten Hinweise entfernen. Wenn Sie eine Bearbeitung vornehmen, müssen Sie – soweit dies praktikabel ist – auf die Mitteilung eines Lizenzgebers hin von der Bearbeitung die in Abschnitt 4.b) aufgezählten Hinweise entfernen.
-
- b. Die Verbreitung und die öffentliche Wiedergabe des Schutzgegenstandes oder auf ihm aufbauender Inhalte oder ihn enthaltender Sammelwerke ist Ihnen nur unter der Bedingung gestattet, dass Sie, vorbehaltlich etwaiger Mitteilungen im Sinne von Abschnitt 4.a), alle dazu gehörenden Rechtevermerke unberührt lassen. Sie sind verpflichtet, die Urheberschaft oder die Rechteinhaberschaft in einer der Nutzung entsprechenden, angemessenen Form anzuerkennen, indem Sie selbst – soweit bekannt – Folgendes angeben:
-
- i. Den Namen (oder das Pseudonym, falls ein solches verwendet wird) Rechteinhabers, und/oder falls der Lizenzgeber im Rechtevermerk, in den Nutzungsbedingungen oder auf andere angemessene Weise eine Zuschreibung an Dritte vorgenommen hat (z.B. an eine Stiftung, ein Verlagshaus oder eine Zeitung) („Zuschreibungsempfänger“), Namen bzw. Bezeichnung dieses oder dieser Dritten;
-
- ii. den Titel des Inhaltes;
-
- iii. in einer praktikablen Form den Uniform-Resource-Identifier (URI, z.B. Internetadresse), den der Lizenzgeber zum Schutzgegenstand angegeben hat, es sei denn, dieser URI verweist nicht auf den Rechtevermerk oder die Lizenzinformationen zum Schutzgegenstand;
-
- iv. und im Falle einer Bearbeitung des Schutzgegenstandes in Übereinstimmung mit Abschnitt 3.b) einen Hinweis darauf, dass es sich um eine Bearbeitung handelt.
-
- Die nach diesem Abschnitt 4.b) erforderlichen Angaben können in jeder angemessenen Form gemacht werden; im Falle einer Bearbeitung des Schutzgegenstandes oder eines Sammelwerkes müssen diese Angaben das Minimum darstellen und bei gemeinsamer Nennung aller Beitragenden dergestalt erfolgen, dass sie zumindest ebenso hervorgehoben sind wie die Hinweise auf die übrigen Rechteinhaber. Die Angaben nach diesem Abschnitt dürfen Sie ausschließlich zur Angabe der Rechteinhaberschaft in der oben bezeichneten Weise verwenden. Durch die Ausübung Ihrer Rechte aus dieser Lizenz dürfen Sie ohne eine vorherige, separat und schriftlich vorliegende Zustimmung des Urhebers, des Lizenzgebers und/oder des Zuschreibungsempfängers weder implizit noch explizit irgendeine Verbindung mit dem oder eine Unterstützung oder Billigung durch den Urheber, den Lizenzgeber oder den Zuschreibungsempfänger andeuten oder erklären.
-
- c. Die oben unter 4.a) und b) genannten Einschränkungen gelten nicht für solche Teile des Schutzgegenstandes, die allein deshalb unter den Schutzgegenstandsbegriff fallen, weil sie als Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen.
-
- d. (Urheber)Persönlichkeitsrechte bleiben - soweit sie bestehen - von dieser Lizenz unberührt.
-
-5. Gewährleistung
-
-SOFERN KEINE ANDERS LAUTENDE, SCHRIFTLICHE VEREINBARUNG ZWISCHEN DEM LIZENZGEBER UND IHNEN GESCHLOSSEN WURDE UND SOWEIT MÄNGEL NICHT ARGLISTIG VERSCHWIEGEN WURDEN, BIETET DER LIZENZGEBER DEN SCHUTZGEGENSTAND UND DIE ERTEILUNG DER NUTZUNGSBEWILLIGUNG UNTER AUSSCHLUSS JEGLICHER GEWÄHRLEISTUNG AN UND ÜBERNIMMT WEDER AUSDRÜCKLICH NOCH KONKLUDENT GARANTIEN IRGENDEINER ART. DIES UMFASST INSBESONDERE DAS FREISEIN VON SACH- UND RECHTSMÄNGELN, UNABHÄNGIG VON DEREN ERKENNBARKEIT FÜR DEN LIZENZGEBER, DIE VERKEHRSFÄHIGKEIT DES SCHUTZGEGENSTANDES, SEINE VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK SOWIE DIE KORREKTHEIT VON BESCHREIBUNGEN.
-
-6. Haftungsbeschränkung
-
-ÜBER DIE IN ZIFFER 5 GENANNTE GEWÄHRLEISTUNG HINAUS HAFTET DER LIZENZGEBER IHNEN GEGENÜBER FÜR SCHÄDEN JEGLICHER ART NUR BEI GROBER FAHRLÄSSIGKEIT ODER VORSATZ, UND ÜBERNIMMT DARÜBER HINAUS KEINERLEI FREIWILLIGE HAFTUNG FÜR FOLGE- ODER ANDERE SCHÄDEN, AUCH WENN ER ÜBER DIE MÖGLICHKEIT IHRES EINTRITTS UNTERRICHTET WURDE.
-
-7. Erlöschen
-
- a. Diese Lizenz und die durch sie erteilte Nutzungsbewilligung erlöschen mit Wirkung für die Zukunft im Falle eines Verstoßes gegen die Lizenzbedingungen durch Sie, ohne dass es dazu der Kenntnis des Lizenzgebers vom Verstoß oder einer weiteren Handlung einer der Vertragsparteien bedarf. Mit natürlichen oder juristischen Personen, die Bearbeitungen des Schutzgegenstandes oder diesen enthaltende Sammelwerke sowie entsprechende Vervielfältigungsstücke unter den Bedingungen dieser Lizenz von Ihnen erhalten haben, bestehen nachträglich entstandene Lizenzbeziehungen jedoch solange weiter, wie die genannten Personen sich ihrerseits an sämtliche Lizenzbedingungen halten. Darüber hinaus gelten die Ziffern 1, 2, 5, 6, 7, und 8 auch nach einem Erlöschen dieser Lizenz fort.
-
- b. Vorbehaltlich der oben genannten Bedingungen gilt diese Lizenz unbefristet bis der rechtliche Schutz für den Schutzgegenstand ausläuft. Davon abgesehen behält der Lizenzgeber das Recht, den Schutzgegenstand unter anderen Lizenzbedingungen anzubieten oder die eigene Weitergabe des Schutzgegenstandes jederzeit einzustellen, solange die Ausübung dieses Rechts nicht einer Kündigung oder einem Widerruf dieser Lizenz (oder irgendeiner Weiterlizenzierung, die auf Grundlage dieser Lizenz bereits erfolgt ist bzw. zukünftig noch erfolgen muss) dient und diese Lizenz unter Berücksichtigung der oben zum Erlöschen genannten Bedingungen vollumfänglich wirksam bleibt.
-
-8. Sonstige Bestimmungen
-
- a. Jedes Mal wenn Sie den Schutzgegenstand für sich genommen oder als Teil eines Sammelwerkes verbreiten oder öffentlich wiedergeben, bietet der Lizenzgeber dem Empfänger eine Lizenz zu den gleichen Bedingungen und im gleichen Umfang an, wie Ihnen in Form dieser Lizenz.
-
- b. Jedes Mal wenn Sie eine Bearbeitung des Schutzgegenstandes verbreiten oder öffentlich wiedergeben, bietet der Lizenzgeber dem Empfänger eine Lizenz am ursprünglichen Schutzgegenstand zu den gleichen Bedingungen und im gleichen Umfang an, wie Ihnen in Form dieser Lizenz.
-
- c. Sollte eine Bestimmung dieser Lizenz unwirksam sein, so bleibt davon die Wirksamkeit der Lizenz im Übrigen unberührt.
-
- d. Keine Bestimmung dieser Lizenz soll als abbedungen und kein Verstoß gegen sie als zulässig gelten, solange die von dem Verzicht oder von dem Verstoß betroffene Seite nicht schriftlich zugestimmt hat.
-
- e. Diese Lizenz (zusammen mit in ihr ausdrücklich vorgesehenen Erlaubnissen, Mitteilungen und Zustimmungen, soweit diese tatsächlich vorliegen) stellt die vollständige Vereinbarung zwischen dem Lizenzgeber und Ihnen in Bezug auf den Schutzgegenstand dar. Es bestehen keine Abreden, Vereinbarungen oder Erklärungen in Bezug auf den Schutzgegenstand, die in dieser Lizenz nicht genannt sind. Rechtsgeschäftliche Änderungen des Verhältnisses zwischen dem Lizenzgeber und Ihnen sind nur über Modifikationen dieser Lizenz möglich. Der Lizenzgeber ist an etwaige zusätzliche, einseitig durch Sie übermittelte Bestimmungen nicht gebunden. Diese Lizenz kann nur durch schriftliche Vereinbarung zwischen Ihnen und dem Lizenzgeber modifiziert werden. Derlei Modifikationen wirken ausschließlich zwischen dem Lizenzgeber und Ihnen und wirken sich nicht auf die Dritten gemäß 8.a) und b) angebotenen Lizenzen aus.
-
- f. Sofern zwischen Ihnen und dem Lizenzgeber keine anderweitige Vereinbarung getroffen wurde und soweit Wahlfreiheit besteht, findet auf diesen Lizenzvertrag das Recht der Republik Österreich Anwendung.
-
-Creative Commons Notice
-
-Creative Commons ist nicht Partei dieser Lizenz und übernimmt keinerlei Gewähr oder dergleichen in Bezug auf den Schutzgegenstand. Creative Commons haftet Ihnen oder einer anderen Partei unter keinem rechtlichen Gesichtspunkt für irgendwelche Schäden, die - abstrakt oder konkret, zufällig oder vorhersehbar - im Zusammenhang mit dieser Lizenz entstehen. Unbeschadet der vorangegangen beiden Sätze, hat Creative Commons alle Rechte und Pflichten eines Lizenzgebers, wenn es sich ausdrücklich als Lizenzgeber im Sinne dieser Lizenz bezeichnet.
-
-Creative Commons gewährt den Parteien nur insoweit das Recht, das Logo und die Marke "Creative Commons" zu nutzen, als dies notwendig ist, um der Öffentlichkeit gegenüber kenntlich zu machen, dass der Schutzgegenstand unter einer CCPL steht. Ein darüber hinaus gehender Gebrauch der Marke "Creative Commons" oder einer verwandten Marke oder eines verwandten Logos bedarf der vorherigen schriftlichen Zustimmung von Creative Commons. Jeder erlaubte Gebrauch richtet sich nach der Creative Commons Marken-Nutzungs-Richtlinie in der jeweils aktuellen Fassung, die von Zeit zu Zeit auf der Website veröffentlicht oder auf andere Weise auf Anfrage zugänglich gemacht wird. Zur Klarstellung: Die genannten Einschränkungen der Markennutzung sind nicht Bestandteil dieser Lizenz.
-
-Creative Commons kann kontaktiert werden über https://creativecommons.org/.
diff --git a/options/license/CC-BY-3.0-AU b/options/license/CC-BY-3.0-AU
deleted file mode 100644
index c6cd440054..0000000000
--- a/options/license/CC-BY-3.0-AU
+++ /dev/null
@@ -1,136 +0,0 @@
-Creative Commons Attribution 3.0 Australia
-
-CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENCE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-Licence
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENCE ("LICENCE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORISED UNDER THIS LICENCE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENCE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collection" means the Work in its entirety in unmodified form along with one or more other separate and independent works, assembled into a collective whole. A Collection may, for example, include a periodical, encyclopedia or anthology. A Collection will not be considered a Derivative Work for the purposes of this Licence.
- b. "Derivative Work" means material in any form that is created by editing, modifying or adapting the Work, a substantial part of the Work, or the Work and other pre-existing works. Derivative Works may, for example, include a translation, adaptation, musical arrangement, dramatisation, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be transformed or adapted, except that a Collection will not be considered a Derivative Work for the purpose of this Licence. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this Licence.
- c. "Distribute" means to make available to the public by any means, including publication, electronic communication, or broadcast.
- d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this Licence.
- e. "Original Author" means the individual, individuals, entity or entities who created the Work.
- f. "Reproduce" means to make a copy of the Work in any material form (eg storage in digital form).
- g. "Work" means the material (including any work or other subject matter) protected by copyright which is offered under the terms of this Licence. This may include (without limitation) a literary, dramatic, musical or artistic work; a sound recording or cinematograph film; a published edition of a literary, dramatic, musical or artistic work; or a television or sound broadcast.
- h. "You" means an individual or entity exercising rights under this Licence who has not previously violated the terms of this Licence with respect to the Work, or who has received express permission from the Licensor to exercise rights under this Licence despite a previous violation.
-
-2. Fair Dealing and Other Rights
-
-Nothing in this Licence is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions under copyright law or any other applicable laws.
-
-3. Licence Grant
-
-3A Grant of Rights
-
-Provided that the terms set out in this Licence are satisfied, the Licensor grants to You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) licence to exercise the following rights:
- a. Reproduce the Work;
- b. incorporate the Work into one or more Collections;
- c. Reproduce the Work as incorporated in any Collection;
- d. create and Reproduce one or more Derivative Works; and
- e. Distribute and publicly perform the Work, a Derivative Work or the Work as incorporated in any Collection.
-
-3B Media and Formats
-
-The above rights may be exercised in any media or format whether now known or hereafter created. They include the right to make modifications that are technically necessary to exercise the rights in other media and formats.
-
-3C Other Rights Reserved
-
-All rights not expressly granted by the Licensor are reserved. The Licensor waives the right to collect royalties for any exercise by You of the rights granted under this Licence.
-
-4. Restrictions
-
-The licence granted above is limited by the following restrictions.
-
-4A Restrictions on Distribution and Public Performance of the Work
-
- a. You may Distribute and publicly perform the Work only under the terms of this Licence.
- b. You must include a copy of, or the Uniform Resource Identifier (such as a web link) for, this Licence with every copy of the Work You Distribute or publicly perform.
- c. You must not offer or impose any terms on the Work that restrict this Licence or the ability of a recipient of the Work from You to exercise the rights granted to them by this Licence.
- d. You are not granted the right to sublicense the Work. The rights of recipients of the Work from You are governed by clause 9.
- e. You must keep intact all notices that refer to this Licence and to the disclaimer of warranties with every copy of the Work You Distribute or publicly perform.
- f. When You Distribute or publicly perform the Work, You must not impose any technological measures on it that restrict the ability of a recipient of the Work from You to exercise the rights granted to them by this Licence.
- g. For the avoidance of doubt, while this clause 4A applies to the Work as incorporated into a Collection, it does not require other material within the Collection, or the Collection apart from the Work itself, to be made subject to this Licence.
-
-4B Attribution and Notice Requirements
-
- a. When You Distribute or publicly perform the Work or any Derivative Work or Collection You must keep intact all copyright notices for the Work.
- b. When You Distribute or publicly perform the Work or any Derivative Work or Collection You must provide, in a manner reasonable to the medium or means You are using:
- i. the name or pseudonym (if provided) of the Original Author and/or of any other party (such as a sponsor institute, publishing entity or journal) that the Original Author or Licensor has requested be attributed (such as in the copyright notice or terms of use). In this clause 4B these parties are referred to as "Attribution Parties";
- ii. the title of the Work (if provided); and
- iii. to the extent reasonably practicable, any Uniform Resource Identifier (such as a web link) that the Licensor specifies should be associated with the Work that refers to the copyright notice or licensing information for the Work.
- c. For any Derivative Work You Distribute or publicly perform, You must take reasonable steps to clearly identify that changes were made to the Work. For example, a translation could be marked "The original work was translated from English to Spanish".
- d. In the case of a Derivative Work or Collection, the above attribution should, at a minimum, appear as part of any credits for other contributing authors and be as prominent as the credits for those other authors.
- e. You must, to the extent practicable, remove the above attribution from any Collection or Derivative Work if requested to do so by the Licensor or Original Author.
- f. For the avoidance of doubt, You may only use the credit required by this clause 4B for the purpose of attribution in the manner set out above. By exercising Your rights under this Licence, You must not assert or imply:
- i. any connection between the Original Author, Licensor or any other Attribution Party and You or Your use of the Work; or
- ii. sponsorship or endorsement by the Original Author, Licensor or any other Attribution Party of You or Your use of the Work,
- without their separate, express prior written permission.
-
-4C Moral Rights
-
-Moral rights remain unaffected to the extent they are recognised and nonwaivable at law. In this clause 4C, "moral rights" means the personal rights granted by law to the Original Author of a copyright work. For example, Part IX of the Copyright Act 1968 (Cth) grants authors the right of integrity of authorship, the right of attribution of authorship, and the right not to have authorship falsely attributed.
-
-5. Representations, Warranties and Disclaimer
-
-Except as expressly stated in this Licence or otherwise agreed to by the parties in writing, and to the full extent permitted by applicable law, the Licensor offers the Work "as-is" and makes no representations, warranties or conditions of any kind concerning the Work, express, implied, statutory or otherwise. This includes, without limitation, any representations, warranties or conditions regarding:
- a. the contents or accuracy of the Work;
- i. title, merchantability, or fitness for a particular purpose;
- ii. non-infringement;
- iii. the absence of latent or other defects; or
- iv. the presence or absence of errors, whether or not discoverable.
- b. The Trade Practices Act 1974 (Cth), and the corresponding State and Territory fair trading legislation, imply certain warranties and conditions in certain circumstances, such as the right to supply or fitness for purpose of goods or services supplied to a consumer. Clause 5(a) cannot and is not intended to exclude, restrict or modify these warranties.
-
-6. Limit of Liability
-
- a. To the full extent permitted by applicable law, and except for any liability arising from contrary agreement, in no event will the Licensor be liable to You on any legal basis (including without limitation, negligence) for any loss or damage whatsoever, including (without limitation):
- i. loss of production or operation time, loss, damage or corruption of data or records; or
- ii. loss of anticipated savings, opportunity, revenue, profit or goodwill, or other economic loss; or
- iii. any special, incidental, consequential, punitive or exemplary damages arising out of or in connection with this Licence or the use of the Work, even if the Licensor has been advised of the possibility of such damages.
- b. If applicable legislation implies warranties or conditions, or imposes obligations or liability on the Licensor in respect of this Licence that cannot be wholly or partly excluded, restricted or modified, the Licensor’s liability is limited, to the full extent permitted by the applicable legislation, at its option, to:
- i. in the case of goods, any one or more of the following:
- * the replacement of the goods or the supply of equivalent goods;
- * the repair of the goods;
- * the payment of the cost of replacing the goods or of acquiring equivalent goods;
- * the payment of the cost of having the goods repaired; or
- ii. in the case of services:
- * the supplying of the services again; or
- * the payment of the cost of having the services supplied again.
- c. The Trade Practices Act 1974 (Cth), and the corresponding State and Territory fair trading legislation, restrict the limitation of liability in certain circumstances, such as a contract for the supply of goods or services of a kind ordinarily acquired for personal, domestic, or household use. Clauses 6(a) and 6(b) cannot and are not intended to apply in circumstances where it is prohibited by law.
-
-7. Termination
-
-This Licence and the rights granted to You under this Licence shall terminate automatically upon any breach by You of the terms of the Licence. Individuals or entities who have received a Derivative Work or a Collection from You pursuant to this Licence, however, will not have their licences terminated provided they remain in full compliance with those licences. Clauses 1, 2, 5, 6, 7, 8, 9, 10, 11, 12 and 13 shall survive any termination of this Licence.
-
-8. Licensor’s Rights Retained
-
-Subject to the above terms, the Licence granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding this, the Licensor reserves the right to release the Work under different licence terms or to stop distributing the Work at any time. However, any such release will not serve to withdraw this Licence (or any other licence that has been granted under the terms of this Licence), and this Licence will continue in full force and effect unless terminated as stated above.
-
-9. Licence Grant to Recipients of the Work from You
-
-Each time You Distribute or publicly perform the Work, a Derivative Work or a Collection the Licensor offers the recipient a licence to the Work on the same terms as are granted to You under this Licence.
-
-10. Severability
-
-If any provision of this Licence is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Licence. Without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
-11. Waivers and Consents
-
-No term of this Licence shall be deemed waived and no breach consented to unless such waiver or consent is in writing and signed by the relevant party.
-
-12. Entire Agreement
-
-This Licence constitutes the entire agreement between the parties. To the full extent permitted by law, there are no understandings, agreements or representations with respect to the Work not specified here. The Licensor shall not be bound by any additional provisions that may appear in any communication from You. This Licence may not be modified without the written agreement of the Licensor and You.
-
-13. Governing Law
-
-The construction, validity and performance of this Licence shall be governed by the laws in force in the Australian Capital Territory, Australia.
-
-Creative Commons Notice
-
-Creative Commons is not a party to this Licence, and, to the full extent permitted by applicable law, makes no representation or warranty whatsoever in connection with the Work. To the full extent permitted by applicable law, Creative Commons will not be liable to You or any party on any legal theory (including, without limitation, negligence) for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this licence. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. Except for the limited purpose of indicating to the public that the Work is licensed under the Licence, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons’ then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at https://creativecommons.org/ .
diff --git a/options/license/CC-BY-3.0-DE b/options/license/CC-BY-3.0-DE
deleted file mode 100644
index 239da95803..0000000000
--- a/options/license/CC-BY-3.0-DE
+++ /dev/null
@@ -1,108 +0,0 @@
-Creative Commons Namensnennung 3.0 Deutschland
-
- CREATIVE COMMONS IST KEINE RECHTSANWALTSKANZLEI UND LEISTET KEINE RECHTSBERATUNG. DIE BEREITSTELLUNG DIESER LIZENZ FÜHRT ZU KEINEM MANDATSVERHÄLTNIS. CREATIVE COMMONS STELLT DIESE INFORMATIONEN OHNE GEWÄHR ZUR VERFÜGUNG. CREATIVE COMMONS ÜBERNIMMT KEINE GEWÄHRLEISTUNG FÜR DIE GELIEFERTEN INFORMATIONEN UND SCHLIEßT DIE HAFTUNG FÜR SCHÄDEN AUS, DIE SICH AUS DEREN GEBRAUCH ERGEBEN.
-
-Lizenz
-
-DER GEGENSTAND DIESER LIZENZ (WIE UNTER "SCHUTZGEGENSTAND" DEFINIERT) WIRD UNTER DEN BEDINGUNGEN DIESER CREATIVE COMMONS PUBLIC LICENSE ("CCPL", "LIZENZ" ODER "LIZENZVERTRAG") ZUR VERFÜGUNG GESTELLT. DER SCHUTZGEGENSTAND IST DURCH DAS URHEBERRECHT UND/ODER ANDERE GESETZE GESCHÜTZT. JEDE FORM DER NUTZUNG DES SCHUTZGEGENSTANDES, DIE NICHT AUFGRUND DIESER LIZENZ ODER DURCH GESETZE GESTATTET IST, IST UNZULÄSSIG.
-
-DURCH DIE AUSÜBUNG EINES DURCH DIESE LIZENZ GEWÄHRTEN RECHTS AN DEM SCHUTZGEGENSTAND ERKLÄREN SIE SICH MIT DEN LIZENZBEDINGUNGEN RECHTSVERBINDLICH EINVERSTANDEN. SOWEIT DIESE LIZENZ ALS LIZENZVERTRAG ANZUSEHEN IST, GEWÄHRT IHNEN DER LIZENZGEBER DIE IN DER LIZENZ GENANNTEN RECHTE UNENTGELTLICH UND IM AUSTAUSCH DAFÜR, DASS SIE DAS GEBUNDENSEIN AN DIE LIZENZBEDINGUNGEN AKZEPTIEREN.
-
-1. Definitionen
-
- a. Der Begriff "Abwandlung" im Sinne dieser Lizenz bezeichnet das Ergebnis jeglicher Art von Veränderung des Schutzgegenstandes, solange die eigenpersönlichen Züge des Schutzgegenstandes darin nicht verblassen und daran eigene Schutzrechte entstehen. Das kann insbesondere eine Bearbeitung, Umgestaltung, Änderung, Anpassung, Übersetzung oder Heranziehung des Schutzgegenstandes zur Vertonung von Laufbildern sein. Nicht als Abwandlung des Schutzgegenstandes gelten seine Aufnahme in eine Sammlung oder ein Sammelwerk und die freie Benutzung des Schutzgegenstandes.
-
- b. Der Begriff "Sammelwerk" im Sinne dieser Lizenz meint eine Zusammenstellung von literarischen, künstlerischen oder wissenschaftlichen Inhalten, sofern diese Zusammenstellung aufgrund von Auswahl und Anordnung der darin enthaltenen selbständigen Elemente eine geistige Schöpfung darstellt, unabhängig davon, ob die Elemente systematisch oder methodisch angelegt und dadurch einzeln zugänglich sind oder nicht.
-
- c. "Verbreiten" im Sinne dieser Lizenz bedeutet, den Schutzgegenstand oder Abwandlungen im Original oder in Form von Vervielfältigungsstücken, mithin in körperlich fixierter Form der Öffentlichkeit anzubieten oder in Verkehr zu bringen.
-
- d. Der "Lizenzgeber" im Sinne dieser Lizenz ist diejenige natürliche oder juristische Person oder Gruppe, die den Schutzgegenstand unter den Bedingungen dieser Lizenz anbietet und insoweit als Rechteinhaberin auftritt.
-
- e. "Rechteinhaber" im Sinne dieser Lizenz ist der Urheber des Schutzgegenstandes oder jede andere natürliche oder juristische Person oder Gruppe von Personen, die am Schutzgegenstand ein Immaterialgüterrecht erlangt hat, welches die in Abschnitt 3 genannten Handlungen erfasst und bei dem eine Einräumung von Nutzungsrechten oder eine Weiterübertragung an Dritte möglich ist.
-
- f. Der Begriff "Schutzgegenstand" bezeichnet in dieser Lizenz den literarischen, künstlerischen oder wissenschaftlichen Inhalt, der unter den Bedingungen dieser Lizenz angeboten wird. Das kann insbesondere eine persönliche geistige Schöpfung jeglicher Art, ein Werk der kleinen Münze, ein nachgelassenes Werk oder auch ein Lichtbild oder anderes Objekt eines verwandten Schutzrechts sein, unabhängig von der Art seiner Fixierung und unabhängig davon, auf welche Weise jeweils eine Wahrnehmung erfolgen kann, gleichviel ob in analoger oder digitaler Form. Soweit Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen, unterfallen auch sie dem Begriff "Schutzgegenstand" im Sinne dieser Lizenz.
-
- g. Mit "Sie" bzw. "Ihnen" ist die natürliche oder juristische Person gemeint, die in dieser Lizenz im Abschnitt 3 genannte Nutzungen des Schutzgegenstandes vornimmt und zuvor in Hinblick auf den Schutzgegenstand nicht gegen Bedingungen dieser Lizenz verstoßen oder aber die ausdrückliche Erlaubnis des Lizenzgebers erhalten hat, die durch diese Lizenz gewährten Nutzungsrechte trotz eines vorherigen Verstoßes auszuüben.
-
- h. Unter "Öffentlich Zeigen" im Sinne dieser Lizenz sind Veröffentlichungen und Präsentationen des Schutzgegenstandes zu verstehen, die für eine Mehrzahl von Mitgliedern der Öffentlichkeit bestimmt sind und in unkörperlicher Form mittels öffentlicher Wiedergabe in Form von Vortrag, Aufführung, Vorführung, Darbietung, Sendung, Weitersendung, zeit- und ortsunabhängiger Zugänglichmachung oder in körperlicher Form mittels Ausstellung erfolgen, unabhängig von bestimmten Veranstaltungen und unabhängig von den zum Einsatz kommenden Techniken und Verfahren, einschließlich drahtgebundener oder drahtloser Mittel und Einstellen in das Internet.
-
- i. "Vervielfältigen" im Sinne dieser Lizenz bedeutet, mittels beliebiger Verfahren Vervielfältigungsstücke des Schutzgegenstandes herzustellen, insbesondere durch Ton- oder Bildaufzeichnungen, und umfasst auch den Vorgang, erstmals körperliche Fixierungen des Schutzgegenstandes sowie Vervielfältigungsstücke dieser Fixierungen anzufertigen, sowie die Übertragung des Schutzgegenstandes auf einen Bild- oder Tonträger oder auf ein anderes elektronisches Medium, gleichviel ob in digitaler oder analoger Form.
-
-2. Schranken des Immaterialgüterrechts. Diese Lizenz ist in keiner Weise darauf gerichtet, Befugnisse zur Nutzung des Schutzgegenstandes zu vermindern, zu beschränken oder zu vereiteln, die Ihnen aufgrund der Schranken des Urheberrechts oder anderer Rechtsnormen bereits ohne Weiteres zustehen oder sich aus dem Fehlen eines immaterialgüterrechtlichen Schutzes ergeben.
-
-3. Einräumung von Nutzungsrechten. Unter den Bedingungen dieser Lizenz räumt Ihnen der Lizenzgeber - unbeschadet unverzichtbarer Rechte und vorbehaltlich des Abschnitts 3.e) - das vergütungsfreie, räumlich und zeitlich (für die Dauer des Schutzrechts am Schutzgegenstand) unbeschränkte einfache Recht ein, den Schutzgegenstand auf die folgenden Arten und Weisen zu nutzen ("unentgeltlich eingeräumtes einfaches Nutzungsrecht für jedermann"):
-
- a. den Schutzgegenstand in beliebiger Form und Menge zu vervielfältigen, ihn in Sammelwerke zu integrieren und ihn als Teil solcher Sammelwerke zu vervielfältigen;
-
- b. Abwandlungen des Schutzgegenstandes anzufertigen, einschließlich Übersetzungen unter Nutzung jedweder Medien, sofern deutlich erkennbar gemacht wird, dass es sich um Abwandlungen handelt;
-
- c. den Schutzgegenstand, allein oder in Sammelwerke aufgenommen, öffentlich zu zeigen und zu verbreiten;
-
- d. Abwandlungen des Schutzgegenstandes zu veröffentlichen, öffentlich zu zeigen und zu verbreiten.
-
- e. Bezüglich Vergütung für die Nutzung des Schutzgegenstandes gilt Folgendes:
-
- i. Unverzichtbare gesetzliche Vergütungsansprüche: Soweit unverzichtbare Vergütungsansprüche im Gegenzug für gesetzliche Lizenzen vorgesehen oder Pauschalabgabensysteme (zum Beispiel für Leermedien) vorhanden sind, behält sich der Lizenzgeber das ausschließliche Recht vor, die entsprechende Vergütung einzuziehen für jede Ausübung eines Rechts aus dieser Lizenz durch Sie.
-
- ii. Vergütung bei Zwangslizenzen: Sofern Zwangslizenzen außerhalb dieser Lizenz vorgesehen sind und zustande kommen, verzichtet der Lizenzgeber für alle Fälle einer lizenzgerechten Nutzung des Schutzgegenstandes durch Sie auf jegliche Vergütung.
-
- iii. Vergütung in sonstigen Fällen: Bezüglich lizenzgerechter Nutzung des Schutzgegenstandes durch Sie, die nicht unter die beiden vorherigen Abschnitte (i) und (ii) fällt, verzichtet der Lizenzgeber auf jegliche Vergütung, unabhängig davon, ob eine Einziehung der Vergütung durch ihn selbst oder nur durch eine Verwertungsgesellschaft möglich wäre.
-
-Das vorgenannte Nutzungsrecht wird für alle bekannten sowie für alle noch nicht bekannten Nutzungsarten eingeräumt. Es beinhaltet auch das Recht, solche Änderungen am Schutzgegenstand vorzunehmen, die für bestimmte nach dieser Lizenz zulässige Nutzungen technisch erforderlich sind. Alle sonstigen Rechte, die über diesen Abschnitt hinaus nicht ausdrücklich durch den Lizenzgeber eingeräumt werden, bleiben diesem allein vorbehalten. Soweit Datenbanken oder Zusammenstellungen von Daten Schutzgegenstand dieser Lizenz oder Teil dessen sind und einen immaterialgüterrechtlichen Schutz eigener Art genießen, verzichtet der Lizenzgeber auf sämtliche aus diesem Schutz resultierenden Rechte.
-
-4. Bedingungen. Die Einräumung des Nutzungsrechts gemäß Abschnitt 3 dieser Lizenz erfolgt ausdrücklich nur unter den folgenden Bedingungen:
-
- a. Sie dürfen den Schutzgegenstand ausschließlich unter den Bedingungen dieser Lizenz verbreiten oder öffentlich zeigen. Sie müssen dabei stets eine Kopie dieser Lizenz oder deren vollständige Internetadresse in Form des Uniform-Resource-Identifier (URI) beifügen. Sie dürfen keine Vertrags- oder Nutzungsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch diese Lizenz gewährten Rechte beschränken. Sie dürfen den Schutzgegenstand nicht unterlizenzieren. Bei jeder Kopie des Schutzgegenstandes, die Sie verbreiten oder öffentlich zeigen, müssen Sie alle Hinweise unverändert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Wenn Sie den Schutzgegenstand verbreiten oder öffentlich zeigen, dürfen Sie (in Bezug auf den Schutzgegenstand) keine technischen Maßnahmen ergreifen, die den Nutzer des Schutzgegenstandes in der Ausübung der ihm durch diese Lizenz gewährten Rechte behindern können. Dieser Abschnitt 4.a) gilt auch für den Fall, dass der Schutzgegenstand einen Bestandteil eines Sammelwerkes bildet, was jedoch nicht bedeutet, dass das Sammelwerk insgesamt dieser Lizenz unterstellt werden muss. Sofern Sie ein Sammelwerk erstellen, müssen Sie auf die Mitteilung eines Lizenzgebers hin aus dem Sammelwerk die in Abschnitt 4.b) aufgezählten Hinweise entfernen. Wenn Sie eine Abwandlung vornehmen, müssen Sie auf die Mitteilung eines Lizenzgebers hin von der Abwandlung die in Abschnitt 4.b) aufgezählten Hinweise entfernen.
-
- b. Die Verbreitung und das öffentliche Zeigen des Schutzgegenstandes oder auf ihm aufbauender Abwandlungen oder ihn enthaltender Sammelwerke ist Ihnen nur unter der Bedingung gestattet, dass Sie, vorbehaltlich etwaiger Mitteilungen im Sinne von Abschnitt 4.a), alle dazu gehörenden Rechtevermerke unberührt lassen. Sie sind verpflichtet, die Rechteinhaberschaft in einer der Nutzung entsprechenden, angemessenen Form anzuerkennen, indem Sie - soweit bekannt - Folgendes angeben:
-
- i. Den Namen (oder das Pseudonym, falls ein solches verwendet wird) des Rechteinhabers und / oder, falls der Lizenzgeber im Rechtevermerk, in den Nutzungsbedingungen oder auf andere angemessene Weise eine Zuschreibung an Dritte vorgenommen hat (z.B. an eine Stiftung, ein Verlagshaus oder eine Zeitung) ("Zuschreibungsempfänger"), Namen bzw. Bezeichnung dieses oder dieser Dritten;
-
- ii. den Titel des Inhaltes;
-
- iii. in einer praktikablen Form den Uniform-Resource-Identifier (URI, z.B. Internetadresse), den der Lizenzgeber zum Schutzgegenstand angegeben hat, es sei denn, dieser URI verweist nicht auf den Rechtevermerk oder die Lizenzinformationen zum Schutzgegenstand;
-
- iv. und im Falle einer Abwandlung des Schutzgegenstandes in Übereinstimmung mit Abschnitt 3.b) einen Hinweis darauf, dass es sich um eine Abwandlung handelt.
-
- Die nach diesem Abschnitt 4.b) erforderlichen Angaben können in jeder angemessenen Form gemacht werden; im Falle einer Abwandlung des Schutzgegenstandes oder eines Sammelwerkes müssen diese Angaben das Minimum darstellen und bei gemeinsamer Nennung mehrerer Rechteinhaber dergestalt erfolgen, dass sie zumindest ebenso hervorgehoben sind wie die Hinweise auf die übrigen Rechteinhaber. Die Angaben nach diesem Abschnitt dürfen Sie ausschließlich zur Angabe der Rechteinhaberschaft in der oben bezeichneten Weise verwenden. Durch die Ausübung Ihrer Rechte aus dieser Lizenz dürfen Sie ohne eine vorherige, separat und schriftlich vorliegende Zustimmung des Lizenzgebers und / oder des Zuschreibungsempfängers weder explizit noch implizit irgendeine Verbindung zum Lizenzgeber oder Zuschreibungsempfänger und ebenso wenig eine Unterstützung oder Billigung durch ihn andeuten.
-
- c. Die oben unter 4.a) und b) genannten Einschränkungen gelten nicht für solche Teile des Schutzgegenstandes, die allein deshalb unter den Schutzgegenstandsbegriff fallen, weil sie als Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen.
-
- d. Persönlichkeitsrechte bleiben - soweit sie bestehen - von dieser Lizenz unberührt.
-
-5. Gewährleistung
-
-SOFERN KEINE ANDERS LAUTENDE, SCHRIFTLICHE VEREINBARUNG ZWISCHEN DEM LIZENZGEBER UND IHNEN GESCHLOSSEN WURDE UND SOWEIT MÄNGEL NICHT ARGLISTIG VERSCHWIEGEN WURDEN, BIETET DER LIZENZGEBER DEN SCHUTZGEGENSTAND UND DIE EINRÄUMUNG VON RECHTEN UNTER AUSSCHLUSS JEGLICHER GEWÄHRLEISTUNG AN UND ÜBERNIMMT WEDER AUSDRÜCKLICH NOCH KONKLUDENT GARANTIEN IRGENDEINER ART. DIES UMFASST INSBESONDERE DAS FREISEIN VON SACH- UND RECHTSMÄNGELN, UNABHÄNGIG VON DEREN ERKENNBARKEIT FÜR DEN LIZENZGEBER, DIE VERKEHRSFÄHIGKEIT DES SCHUTZGEGENSTANDES, SEINE VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK SOWIE DIE KORREKTHEIT VON BESCHREIBUNGEN. DIESE GEWÄHRLEISTUNGSBESCHRÄNKUNG GILT NICHT, SOWEIT MÄNGEL ZU SCHÄDEN DER IN ABSCHNITT 6 BEZEICHNETEN ART FÜHREN UND AUF SEITEN DES LIZENZGEBERS DAS JEWEILS GENANNTE VERSCHULDEN BZW. VERTRETENMÜSSEN EBENFALLS VORLIEGT.
-
-6. Haftungsbeschränkung
-
-DER LIZENZGEBER HAFTET IHNEN GEGENÜBER IN BEZUG AUF SCHÄDEN AUS DER VERLETZUNG DES LEBENS, DES KÖRPERS ODER DER GESUNDHEIT NUR, SOFERN IHM WENIGSTENS FAHRLÄSSIGKEIT VORZUWERFEN IST, FÜR SONSTIGE SCHÄDEN NUR BEI GROBER FAHRLÄSSIGKEIT ODER VORSATZ, UND ÜBERNIMMT DARÜBER HINAUS KEINERLEI FREIWILLIGE HAFTUNG.
-
-7. Erlöschen
-
- a. Diese Lizenz und die durch sie eingeräumten Nutzungsrechte erlöschen mit Wirkung für die Zukunft im Falle eines Verstoßes gegen die Lizenzbedingungen durch Sie, ohne dass es dazu der Kenntnis des Lizenzgebers vom Verstoß oder einer weiteren Handlung einer der Vertragsparteien bedarf. Mit natürlichen oder juristischen Personen, die Abwandlungen des Schutzgegenstandes oder diesen enthaltende Sammelwerke unter den Bedingungen dieser Lizenz von Ihnen erhalten haben, bestehen nachträglich entstandene Lizenzbeziehungen jedoch solange weiter, wie die genannten Personen sich ihrerseits an sämtliche Lizenzbedingungen halten. Darüber hinaus gelten die Ziffern 1, 2, 5, 6, 7, und 8 auch nach einem Erlöschen dieser Lizenz fort.
-
- b. Vorbehaltlich der oben genannten Bedingungen gilt diese Lizenz unbefristet bis der rechtliche Schutz für den Schutzgegenstand ausläuft. Davon abgesehen behält der Lizenzgeber das Recht, den Schutzgegenstand unter anderen Lizenzbedingungen anzubieten oder die eigene Weitergabe des Schutzgegenstandes jederzeit einzustellen, solange die Ausübung dieses Rechts nicht einer Kündigung oder einem Widerruf dieser Lizenz (oder irgendeiner Weiterlizenzierung, die auf Grundlage dieser Lizenz bereits erfolgt ist bzw. zukünftig noch erfolgen muss) dient und diese Lizenz unter Berücksichtigung der oben zum Erlöschen genannten Bedingungen vollumfänglich wirksam bleibt.
-
-8. Sonstige Bestimmungen
-
- a. Jedes Mal, wenn Sie den Schutzgegenstand für sich genommen oder als Teil eines Sammelwerkes verbreiten oder öffentlich zeigen, bietet der Lizenzgeber dem Empfänger eine Lizenz zu den gleichen Bedingungen und im gleichen Umfang an, wie Ihnen in Form dieser Lizenz.
-
- b. Jedes Mal, wenn Sie eine Abwandlung des Schutzgegenstandes verbreiten oder öffentlich zeigen, bietet der Lizenzgeber dem Empfänger eine Lizenz am ursprünglichen Schutzgegenstand zu den gleichen Bedingungen und im gleichen Umfang an, wie Ihnen in Form dieser Lizenz.
-
- c. Sollte eine Bestimmung dieser Lizenz unwirksam sein, so bleibt davon die Wirksamkeit der Lizenz im Übrigen davon unberührt.
-
- d. Keine Bestimmung dieser Lizenz soll als abbedungen und kein Verstoß gegen sie als zulässig gelten, solange die von dem Verzicht oder von dem Verstoß betroffene Seite nicht schriftlich zugestimmt hat.
-
- e. Diese Lizenz (zusammen mit in ihr ausdrücklich vorgesehenen Erlaubnissen, Mitteilungen und Zustimmungen, soweit diese tatsächlich vorliegen) stellt die vollständige Vereinbarung zwischen dem Lizenzgeber und Ihnen in Bezug auf den Schutzgegenstand dar. Es bestehen keine Abreden, Vereinbarungen oder Erklärungen in Bezug auf den Schutzgegenstand, die in dieser Lizenz nicht genannt sind. Rechtsgeschäftliche Änderungen des Verhältnisses zwischen dem Lizenzgeber und Ihnen sind nur über Modifikationen dieser Lizenz möglich. Der Lizenzgeber ist an etwaige zusätzliche, einseitig durch Sie übermittelte Bestimmungen nicht gebunden. Diese Lizenz kann nur durch schriftliche Vereinbarung zwischen Ihnen und dem Lizenzgeber modifiziert werden. Derlei Modifikationen wirken ausschließlich zwischen dem Lizenzgeber und Ihnen und wirken sich nicht auf die Dritten gemäß Ziffern 8.a) und b) angebotenen Lizenzen aus.
-
- f. Sofern zwischen Ihnen und dem Lizenzgeber keine anderweitige Vereinbarung getroffen wurde und soweit Wahlfreiheit besteht, findet auf diesen Lizenzvertrag das Recht der Bundesrepublik Deutschland Anwendung.
-
-
-Creative Commons Notice
-
-Creative Commons ist nicht Partei dieser Lizenz und übernimmt keinerlei Gewähr oder dergleichen in Bezug auf den Schutzgegenstand. Creative Commons haftet Ihnen oder einer anderen Partei unter keinem rechtlichen Gesichtspunkt für irgendwelche Schäden, die - abstrakt oder konkret, zufällig oder vorhersehbar - im Zusammenhang mit dieser Lizenz entstehen. Unbeschadet der vorangegangen beiden Sätze, hat Creative Commons alle Rechte und Pflichten eines Lizenzgebers, wenn es sich ausdrücklich als Lizenzgeber im Sinne dieser Lizenz bezeichnet.
-
-Creative Commons gewährt den Parteien nur insoweit das Recht, das Logo und die Marke "Creative Commons" zu nutzen, als dies notwendig ist, um der Öffentlichkeit gegenüber kenntlich zu machen, dass der Schutzgegenstand unter einer CCPL steht. Ein darüber hinaus gehender Gebrauch der Marke "Creative Commons" oder einer verwandten Marke oder eines verwandten Logos bedarf der vorherigen schriftlichen Zustimmung von Creative Commons. Jeder erlaubte Gebrauch richtet sich nach der Creative Commons Marken-Nutzungs-Richtlinie in der jeweils aktuellen Fassung, die von Zeit zu Zeit auf der Website veröffentlicht oder auf andere Weise auf Anfrage zugänglich gemacht wird. Zur Klarstellung: Die genannten Einschränkungen der Markennutzung sind nicht Bestandteil dieser Lizenz.
-
-Creative Commons kann kontaktiert werden über https://creativecommons.org/.
diff --git a/options/license/CC-BY-3.0-IGO b/options/license/CC-BY-3.0-IGO
deleted file mode 100644
index 13ab9536e1..0000000000
--- a/options/license/CC-BY-3.0-IGO
+++ /dev/null
@@ -1,101 +0,0 @@
-Creative Commons Attribution 3.0 IGO
-
-CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. THE LICENSOR IS NOT NECESSARILY AN INTERGOVERNMENTAL ORGANIZATION (IGO), AS DEFINED IN THE LICENSE BELOW.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("LICENSE"). THE LICENSOR (DEFINED BELOW) HOLDS COPYRIGHT AND OTHER RIGHTS IN THE WORK. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION FOR YOUR ACCEPTANCE AND AGREEMENT TO THE TERMS OF THE LICENSE.
-
-1. Definitions
-
- a. "IGO" means, solely and exclusively for purposes of this License, an organization established by a treaty or other instrument governed by international law and possessing its own international legal personality. Other organizations established to carry out activities across national borders and that accordingly enjoy immunity from legal process are also IGOs for the sole and exclusive purposes of this License. IGOs may include as members, in addition to states, other entities.
-
- b. "Work" means the literary and/or artistic work eligible for copyright protection, whatever may be the mode or form of its expression including digital form, and offered under the terms of this License. It is understood that a database, which by reason of the selection and arrangement of its contents constitutes an intellectual creation, is considered a Work.
-
- c. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License and may be, but is not necessarily, an IGO.
-
- d. "You" means an individual or entity exercising rights under this License.
-
- e. "Reproduce" means to make a copy of the Work in any manner or form, and by any means.
-
- f. "Distribute" means the activity of making publicly available the Work or Adaptation (or copies of the Work or Adaptation), as applicable, by sale, rental, public lending or any other known form of transfer of ownership or possession of the Work or copy of the Work.
-
- g. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
-
- h. "Adaptation" means a work derived from or based upon the Work, or upon the Work and other pre-existing works. Adaptations may include works such as translations, derivative works, or any alterations and arrangements of any kind involving the Work. For purposes of this License, where the Work is a musical work, performance, or phonogram, the synchronization of the Work in timed-relation with a moving image is an Adaptation. For the avoidance of doubt, including the Work in a Collection is not an Adaptation.
-
- i. "Collection" means a collection of literary or artistic works or other works or subject matter other than works listed in Section 1(b) which by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. For the avoidance of doubt, a Collection will not be considered as an Adaptation.
-
-2. Scope of this License. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright protection.
-
-3. License Grant. Subject to the terms and conditions of this License, the Licensor hereby grants You a worldwide, royalty-free, non-exclusive license to exercise the rights in the Work as follows:
-
- a. to Reproduce, Distribute and Publicly Perform the Work, to incorporate the Work into one or more Collections, and to Reproduce, Distribute and Publicly Perform the Work as incorporated in the Collections; and,
-
- b. to create, Reproduce, Distribute and Publicly Perform Adaptations, provided that You clearly label, demarcate or otherwise identify that changes were made to the original Work.
-
- c. For the avoidance of doubt:
-
- i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-
- ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
-
- iii. Voluntary License Schemes. To the extent possible, the Licensor waives the right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary licensing scheme.
-
-This License lasts for the duration of the term of the copyright in the Work licensed by the Licensor. The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by the Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work (see section 8(a)). You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from a Licensor You must, to the extent practicable, remove from the Collection any credit (inclusive of any logo, trademark, official mark or official emblem) as required by Section 4(b), as requested. If You create an Adaptation, upon notice from a Licensor You must, to the extent practicable, remove from the Adaptation any credit (inclusive of any logo, trademark, official mark or official emblem) as required by Section 4(b), as requested.
-
- b. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) any attributions that the Licensor indicates be associated with the Work as indicated in a copyright notice, (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that the Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, (iv) consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation. The credit required by this Section 4(b) may be implemented in any reasonable manner; provided, however, that in the case of an Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributors to the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Licensor or others designated for attribution, of You or Your use of the Work, without the separate, express prior written permission of the Licensor or such others.
-
- c. Except as otherwise agreed in writing by the Licensor, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the honor or reputation of the Licensor where moral rights apply.
-
-5. Representations, Warranties and Disclaimer
-
-THE LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE.
-
-6. Limitation on Liability
-
-IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. Subject to the terms and conditions set forth in this License, the license granted here lasts for the duration of the term of the copyright in the Work licensed by the Licensor as stated in Section 3. Notwithstanding the above, the Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated below.
-
- b. If You fail to comply with this License, then this License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. Notwithstanding the foregoing, this License reinstates automatically as of the date the violation is cured, provided it is cured within 30 days of You discovering the violation, or upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 7(b) does not affect any rights the Licensor may have to seek remedies for violations of this License by You.
-
-8. Miscellaneous
-
- a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You Distribute or Publicly Perform an Adaptation, the Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the Licensor.
-
- e. This License constitutes the entire agreement between You and the Licensor with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. The Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
- f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). Interpretation of the scope of the rights granted by the Licensor and the conditions imposed on You under this License, this License, and the rights and conditions set forth herein shall be made with reference to copyright as determined in accordance with general principles of international law, including the above mentioned conventions.
-
- g. Nothing in this License constitutes or may be interpreted as a limitation upon or waiver of any privileges and immunities that may apply to the Licensor or You, including immunity from the legal processes of any jurisdiction, national court or other authority.
-
- h. Where the Licensor is an IGO, any and all disputes arising under this License that cannot be settled amicably shall be resolved in accordance with the following procedure:
-
- i. Pursuant to a notice of mediation communicated by reasonable means by either You or the Licensor to the other, the dispute shall be submitted to non-binding mediation conducted in accordance with rules designated by the Licensor in the copyright notice published with the Work, or if none then in accordance with those communicated in the notice of mediation. The language used in the mediation proceedings shall be English unless otherwise agreed.
-
- ii. If any such dispute has not been settled within 45 days following the date on which the notice of mediation is provided, either You or the Licensor may, pursuant to a notice of arbitration communicated by reasonable means to the other, elect to have the dispute referred to and finally determined by arbitration. The arbitration shall be conducted in accordance with the rules designated by the Licensor in the copyright notice published with the Work, or if none then in accordance with the UNCITRAL Arbitration Rules as then in force. The arbitral tribunal shall consist of a sole arbitrator and the language of the proceedings shall be English unless otherwise agreed. The place of arbitration shall be where the Licensor has its headquarters. The arbitral proceedings shall be conducted remotely (e.g., via telephone conference or written submissions) whenever practicable.
-
- iii. Interpretation of this License in any dispute submitted to mediation or arbitration shall be as set forth in Section 8(f), above.
-
-Creative Commons Notice
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of the Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
-
-Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-3.0-NL b/options/license/CC-BY-3.0-NL
deleted file mode 100644
index 5789b8592d..0000000000
--- a/options/license/CC-BY-3.0-NL
+++ /dev/null
@@ -1,97 +0,0 @@
-Creative Commons Naamsvermelding 3.0
-
- CREATIVE COMMONS CORPORATION IS GEEN ADVOCATENPRAKTIJK EN VERLEENT GEEN JURIDISCHE DIENSTEN. DE VERSPREIDING VAN DEZE LICENTIE ROEPT GEEN JURIDISCHE RELATIE MET CREATIVE COMMONS IN HET LEVEN. CREATIVE COMMONS VERSPREIDT DEZE INFORMATIE 'AS-IS'. CREATIVE COMMONS STAAT NIET IN VOOR DE INHOUD VAN DE VERSTREKTE INFORMATIE EN SLUIT ALLE AANSPRAKELIJKHEID UIT VOOR ENIGERLEI SCHADE VOORTVLOEIEND UIT HET GEBRUIK VAN DEZE INFORMATIE INDIEN EN VOORZOVER DE WET NIET ANDERS BEPAALT.
-
-Licentie
-
-HET WERK (ALS HIERONDER OMSCHREVEN) WORDT TER BESCHIKKING GESTELD OVEREENKOMSTIG DE VOORWAARDEN VAN DEZE CREATIVE COMMONS PUBLIEKE LICENTIE ('CCPL' OF 'LICENTIE'). HET WERK WORDT BESCHERMD OP GROND VAN HET AUTEURSRECHT, NABURIGE RECHTEN, HET DATABANKENRECHT EN/OF ENIGE ANDERE TOEPASSELIJKE RECHTEN. MET UITZONDERING VAN HET IN DEZE LICENTIE OMSCHREVEN TOEGESTANE GEBRUIK VAN HET WERK IS ENIG ANDER GEBRUIK VAN HET WERK NIET TOEGESTAAN.
-
-DOOR HET UITOEFENEN VAN DE IN DEZE LICENTIE VERLEENDE RECHTEN MET BETREKKING TOT HET WERK AANVAARDT EN GAAT DE GEBRUIKER AKKOORD MET DE VOORWAARDEN VAN DEZE LICENTIE, MET DIEN VERSTANDE DAT (DE INHOUD VAN) DEZE LICENTIE OP VOORHAND VOLDOENDE DUIDELIJK KENBAAR DIENT TE ZIJN VOOR DE ONTVANGER VAN HET WERK.
-
-DE LICENTIEGEVER VERLEENT DE GEBRUIKER DE IN DEZE LICENTIE OMSCHREVEN RECHTEN MET INACHTNEMING VAN DE DESBETREFFENDE VOORWAARDEN.
-
-1. Definities
-
- a. 'Verzamelwerk' een werk waarin het Werk, in zijn geheel en in ongewijzigde vorm, samen met een of meer andere werken, die elk een afzonderlijk en zelfstandig werk vormen, tot een geheel is samengevoegd. Voorbeelden van een verzamelwerk zijn een tijdschrift, een bloemlezing of een encyclopedie. Een Verzamelwerk zal voor de toepassing van deze Licentie niet als een Afgeleid werk (als hieronder omschreven) worden beschouwd.
-
- b. 'Afgeleid werk' een werk dat is gebaseerd op het Werk of op het Werk en andere reeds bestaande werken. Voorbeelden van een Afgeleid werk zijn een vertaling, een muziekschikking (arrangement), een toneelbewerking, een literaire bewerking, een verfilming, een geluidsopname, een kunstreproductie, een verkorte versie, een samenvatting of enig andere bewerking van het Werk, met dien verstande dat een Verzamelwerk voor de toepassing van deze Licentie niet als een Afgeleid werk zal worden beschouwd.
-
- Indien het Werk een muziekwerk betreft, zal de synchronisatie van de tijdslijnen van het Werk en een bewegend beeld ('synching') voor de toepassing van deze Licentie als een Afgeleid Werk worden beschouwd.
-
- c. 'Licentiegever' de natuurlijke persoon/personen of rechtspersoon/rechtspersonen die het Werk volgens de voorwaarden van deze Licentie aanbiedt/aanbieden.
-
- d. 'Maker' de natuurlijke persoon/personen of rechtspersoon/personen die het oorspronkelijke werk gemaakt heeft/hebben. Voor de toepassing van deze Licentie wordt onder de Maker mede verstaan de uitvoerende kunstenaar, film- en fonogramproducent en omroeporganisaties in de zin van de Wet op de naburige rechten en de producent van een databank in de zin van de Databankenwet.
-
- e. 'Werk' het auteursrechtelijk beschermde werk dat volgens de voorwaarden van deze Licentie wordt aangeboden. Voor de toepassing van deze Licentie wordt onder het Werk mede verstaan het fonogram, de eerste vastlegging van een film en het (omroep)programma in de zin van de Wet op de naburige rechten en de databank in de zin van de Databankenwet, voor zover dit fonogram, deze eerste vastlegging van een film, dit (omroep)programma en deze databank beschermd wordt krachtens de toepasselijke wet in de jurisdictie van de Gebruiker.
-
- f. 'Gebruiker' de natuurlijke persoon of rechtspersoon die rechten ingevolge deze Licentie uitoefent en die de voorwaarden van deze Licentie met betrekking tot het Werk niet eerder geschonden heeft, of die van de Licentiegever uitdrukkelijke toestemming gekregen heeft om rechten ingevolge deze Licentie uit te oefenen ondanks een eerdere schending.
-
-2. Beperkingen van de uitsluitende rechten. Niets in deze Licentie strekt ertoe om de rechten te beperken die voortvloeien uit de beperkingen en uitputting van de uitsluitende rechten van de rechthebbende krachtens het auteursrecht, de naburige rechten, het databankenrecht of enige andere toepasselijke rechten.
-
-3. Licentieverlening. Met inachtneming van de voorwaarden van deze Licentie verleent de Licentiegever hierbij aan de Gebruiker een wereldwijde, niet-exclusieve licentie om de navolgende rechten met betrekking tot het Werk vrij van royalty's uit te oefenen voor de duur van de toepasselijke intellectuele eigendomsrechten:
-
- a. het reproduceren van het Werk, het opnemen van het Werk in een of meerdere Verzamelwerken, en het reproduceren van het in de Verzamelwerken opgenomen Werk;
-
- b. het maken en reproduceren van Afgeleide werken met dien verstande dat met betrekking tot het Afgeleide werk, met inbegrip van welke vertaling in welk medium dan ook, duidelijk wordt gemaakt dat er wijzigingen in het oorspronkelijke Werk zijn aangebracht. Bijvoorbeeld, aan een vertaling kan worden toegevoegd dat 'het oorspronkelijke Werk is van het Engels in het Spaans vertaald', of in geval van een verandering kan worden aangegeven dat 'het oorspronkelijke werk is veranderd';
-
- c. het verspreiden van exemplaren van het Werk, het in het openbaar tonen, op- en uitvoeren en het on-line beschikbaar stellen van het Werk, afzonderlijk en als deel van een Verzamelwerk;
-
- d. het verspreiden van exemplaren van Afgeleide werken, het in het openbaar te tonen, op- en uitvoeren en het on-line beschikbaar stellen van Afgeleide werken;
-
- e. het opvragen en hergebruiken van het Werk;
-
- f. Volledigheidshalve dient te worden vermeld dat:
-
- i. Niet voor afstand vatbare heffingsregelingen. in het geval van niet voor afstand vatbare heffingsregelingen (bijvoorbeeld met betrekking tot thuiskopieën) de Licentiegever zich het recht voorbehoudt om dergelijke heffingen te innen (al dan niet door middel van een auteursrechtenorganisatie) bij zowel commercieel als niet-commercieel gebruik van het Werk;
-
- ii. Voor afstand vatbare heffingsregeling. in het geval van voor afstand vatbare heffingsregelingen (bijvoorbeeld met betrekking tot leenrechten) de Licentiegever afstand doet van het recht om dergelijke heffingen te innen bij zowel commercieel als niet-commercieel gebruik van het Werk;
-
- iii. Collectief rechtenbeheer. de Licentiegever afstand doet van het recht om vergoedingen te innen (zelfstandig of, indien de Licentiegever lid is van een auteursrechtenorganisatie, door middel van die organisatie) bij zowel commercieel als niet-commercieel gebruik van het Werk.
-
-De Gebruiker mag deze rechten uitoefenen met behulp van alle thans bekende media, dragers en formats. De Gebruiker is tevens gerechtigd om technische wijzigingen aan te brengen die noodzakelijk zijn om de rechten met behulp van andere media, dragers en formats uit te oefenen. Alle niet uitdrukkelijk verleende rechten zijn hierbij voorbehouden aan de Licentiegever, met inbegrip van maar niet beperkt tot de rechten die in artikel 4(d) worden genoemd. Voor zover de Licentiegever op basis van het nationale recht ter implementatie van de Europese Databankenrichtlijn over uitsluitende rechten beschickt doet de Licentiegever afstand van deze rechten.
-
-4. Beperkingen. De in artikel 3 verleende Licentie is uitdrukkelijk gebonden aan de volgende beperkingen:
-
- a. De Gebruiker mag het Werk uitsluitend verspreiden, in het openbaar tonen, op- of uitvoeren of on-line beschikbaar stellen met inachtneming van de voorwaarden van deze Licentie, en de Gebruiker dient een exemplaar van, of de Uniform Resource Identifier voor, deze Licentie toe te voegen aan elk exemplaar van het Werk dat de Gebruiker verspreidt, in het openbaar toont, op- of uitvoert, of on-line beschikbaar stelt. Het is de Gebruiker niet toegestaan om het Werk onder enige afwijkende voorwaarden aan te bieden waardoor de voorwaarden van deze Licentie dan wel de mogelijkheid van de ontvangers van het Werk om de rechten krachtens deze Licentie uit te oefenen worden beperkt. Het is de Gebruiker niet toegestaan om het Werk in sublicentie te geven. De Gebruiker dient alle vermeldingen die verwijzen naar deze Licentie dan wel naar de uitsluiting van garantie te laten staan. Het is de Gebruiker niet toegestaan om het Werk te verspreiden, in het openbaar te tonen, op- of uit te voeren of on-line beschikbaar te stellen met toepassing van technologische voorzieningen waardoor de voorwaarden van deze Licentie dan wel de mogelijkheid van de ontvangers van het Werk om de rechten krachtens deze Licentie uit te oefenen worden beperkt. Het voorgaande is tevens van toepassing op het Werk dat deel uitmaakt van een Verzamelwerk, maar dat houdt niet in dat het Verzamelwerk, afgezien van het Werk zelf, gebonden is aan de voorwaarden van deze Licentie. Indien de Gebruiker een Verzamelwerk maakt, dient deze, op verzoek van welke Licentiegever ook, de op grond van artikel 4(b) vereiste naamsvermelding uit het Verzamelwerk te verwijderen, voor zover praktisch mogelijk, conform het verzoek. Indien de Gebruiker een Afgeleid werk maakt, dient hij, op verzoek van welke Licentiegever ook, de op grond van artikel 4(b) vereiste naamsvermelding uit het Afgeleide werk te verwijderen, voorzover praktisch mogelijk, conform het verzoek.
-
- b. Indien de Gebruiker het Werk, Afgeleide Werken of Verzamelwerken verspreidt, in het openbaar toont, op- of uitvoert of on-line beschikbaar stelt, dient de Gebruiker, tenzij er sprake is van een verzoek als vermeld in lid 4(a), alle auteursrechtvermeldingen met betrekking tot het Werk te laten staan. Tevens dient de Gebruiker, op een wijze die redelijk is in verhouding tot het gebruikte medium, de naam te vermelden van (i) de Maker (of zijn/haar pseudoniem indien van toepassing) indien deze wordt vermeld; en/of (ii) van (een) andere partij(en) (b.v. sponsor, uitgeverij, tijdschrift) indien de naamsvermelding van deze partij(en) ("Naamsvermeldingsgerechtigden") in de auteursrechtvermelding of algemene voorwaarden van de Licentiegever of op een andere redelijke wijze verplicht is gesteld door de Maker en/of de Licentiegever; de titel van het Werk indien deze wordt vermeld; voorzover redelijkerwijs toepasbaar de Uniform Resource Identifier, indien aanwezig, waarvan de Licentiegever heeft aangegeven dat deze bij het Werk hoort, tenzij de URI niet verwijst naar de auteursrechtvermeldingen of de licentie-informatie betreffende het Werk; in overeenstemming met artikel 3(b) in geval van een Afgeleid werk, door te verwijzen naar het gebruik van het Werk in het Afgeleide werk (bijvoorbeeld: 'De Franse vertaling van het Werk van de Maker' of 'Scenario gebaseerd op het Werk van de Maker'). De Gebruiker dient op redelijke wijze aan de in dit artikel genoemde vereisten te voldoen; echter, met dien verstande dat, in geval van een Afgeleid werk of een Verzamelwerk, de naamsvermeldingen in ieder geval geplaatst dienen te worden, indien er een naamsvermelding van alle makers van het Afgeleide werk of het Verzamelwerk geplaatst wordt dan als deel van die naamsvermeldingen, en op een wijze die in ieder geval even duidelijk is als de naamsvermeldingen van de overige makers.
-
- Volledigheidshalve dient te worden vermeld dat de Gebruiker uitsluitend gebruik mag maken van de naamsvermelding op de in dit artikel omschreven wijze teneinde te voldoen aan de naamsvermeldingsverplichting en, door gebruikmaking van zijn rechten krachtens deze Licentie, is het de Gebruiker niet toegestaan om op enigerlei wijze de indruk te wekken dat er sprake is van enig verband met, sponsorschap van of goedkeuring van de (toepasselijke) Maker, Licentiegever c.q. Naamsvermeldingsgerechtigden van de Gebruiker of diens gebruik van het Werk, zonder de afzonderlijke, uitdrukkelijke, voorafgaande, schriftelijke toestemming van de Maker, Licentiegever c.q. Naamsvermeldingsgerechtigden.
-
- c. Volledigheidshalve dient te worden vermeld, dat de hierboven vermelde beperkingen (lid 4(a) en lid 4(b)) niet van toepassing zijn op die onderdelen van het Werk die geacht worden te vallen onder de definitie van het 'Werk' zoals vermeld in deze Licentie uitsluitend omdat zij voldoen aan de criteria van het sui generis databankenrecht krachtens het nationale recht ter implementatie van de Europese Databankenrichtlijn.
-
- d. De in artikel 3 verleende rechten moeten worden uitgeoefend met inachtneming van het morele recht van de Maker (en/of de uitvoerende kunstenaar) om zich te verzetten tegen elke misvorming, verminking of andere aantasting van het werk, welke nadeel zou kunnen toebrengen aan de eer of de naam van de Maker (en/of de uitvoerende kunstenaar) of aan zijn waarde in deze hoedanigheid, indien en voor zover de Maker (en/of de uitvoerende kunstenaar) op grond van een op hem van toepassing zijnde wettelijke bepaling geen afstand kan doen van dat morele recht.
-
-5. Garantie en vrijwaring.
-
-TENZIJ ANDERS SCHRIFTELIJK IS OVEREENGEKOMEN DOOR DE PARTIJEN, STELT DE LICENTIEGEVER HET WERK BESCHIKBAAR OP 'AS-IS' BASIS, ZONDER ENIGE GARANTIE, HETZIJ DIRECT, INDIRECT OF ANDERSZINS, MET BETREKKING TOT HET WERK, MET INBEGRIP VAN, MAAR NIET BEPERKT TOT GARANTIES MET BETREKKING TOT DE EIGENDOMSTITEL, DE VERKOOPBAARHEID, DE GESCHIKTHEID VOOR BEPAALDE DOELEINDEN, MOGELIJKE INBREUK, DE AFWEZIGHEID VAN LATENTE OF ANDERE TEKORTKOMINGEN, DE JUISTHEID OF DE AAN- OF AFWEZIGHEID VAN FOUTEN, ONGEACHT DE OPSPOORBAARHEID DAARVAN, INDIEN EN VOORZOVER DE WET NIET ANDERS BEPAALT.
-
-6. Beperking van de aansprakelijkheid.
-
-DE LICENTIEGEVER AANVAARDT GEEN ENKELE AANSPRAKELIJKHEID JEGENS DE GEBRUIKER VOOR ENIGE BIJZONDERE OF INCIDENTELE SCHADE OF GEVOLGSCHADE VOORTVLOEIEND UIT DEZE LICENTIE OF HET GEBRUIK VAN HET WERK, ZELFS NIET INDIEN DE LICENTIEGEVER OP DE HOOGTE IS GESTELD VAN HET RISICO VAN DERGELIJKE SCHADE, INDIEN EN VOORZOVER DE WET NIET ANDERS BEPAALT.
-
-7. Beëindiging
-
- a. Deze Licentie en de daarin verleende rechten vervallen automatisch op het moment dat de Gebruiker in strijd handelt met de voorwaarden van deze Licentie. De licenties van natuurlijke personen of rechtspersonen die Verzamelwerken hebben ontvangen van de Gebruiker krachtens deze Licentie blijven echter in stand zolang dergelijke natuurlijke personen of rechtspersonen zich houden aan de voorwaarden van die licenties. Na de beëindiging van deze Licentie blijven artikelen 1, 2, 5, 6, 7 en 8 onverminderd van kracht.
-
- b. Met inachtneming van de hierboven vermelde voorwaarden wordt de Licentie verleend voor de duur van de toepasselijke intellectuele eigendomsrechten op het Werk. De Licentiegever behoudt zich desalniettemin te allen tijde het recht voor om het Werk volgens gewijzigde licentievoorwaarden te verspreiden of om het Werk niet langer te verspreiden; met dien verstande dat een dergelijk besluit niet de intrekking van deze Licentie (of enig andere licentie die volgens de voorwaarden van deze Licentie (verplicht) is verleend) tot gevolg heeft, en deze Licentie onverminderd van kracht blijft tenzij zij op de in lid a omschreven wijze wordt beëindigd.
-
-8. Diversen
-
- a. Elke keer dat de Gebruiker het Werk of een Verzamelwerk verspreidt of on-line beschikbaar stelt, biedt de Licentiegever de ontvanger een licentie op het Werk aan volgens de algemene voorwaarden van deze Licentie.
-
- b. Elke keer dat de Gebruiker een Afgeleid werk verspreidt of on-line beschikbaar stelt, biedt de Licentiegever de ontvanger een licentie op het oorspronkelijke werk aan volgens de algemene voorwaarden van deze Licentie.
-
- c. Indien enige bepaling van deze Licentie nietig of niet rechtens afdwingbaar is, zullen de overige voorwaarden van deze Licentie volledig van kracht blijven. De nietige of niet-afdwingbare bepaling zal, zonder tussenkomst van de partijen, worden vervangen door een geldige en afdwingbare bepaling waarbij het doel en de strekking van de oorspronkelijke bepaling zoveel mogelijk in acht worden genomen.
-
- d. Een verklaring van afstand van in deze Licentie verleende rechten of een wijziging van de voorwaarden van deze Licentie dient schriftelijk te geschieden en getekend te zijn door de partij die verantwoordelijk is voor de verklaring van afstand respectievelijk de partij wiens toestemming voor de wijziging is vereist.
-
- f. Deze Licentie bevat de volledige overeenkomst tussen de partijen met betrekking tot het in licentie gegeven Werk. Er zijn geen andere afspraken gemaakt met betrekking tot het Werk. De Licentiegever is niet gebonden aan enige aanvullende bepalingen die worden vermeld in mededelingen van de Gebruiker. Deze licentie kan uitsluitend worden gewijzigd met de wederzijdse, schriftelijke instemming van de Licentiegever en de Gebruiker.
-
-Aansprakelijkheid en merkrechten van Creative Commons
-
-Creative Commons is geen partij bij deze Licentie en stelt geen enkele garantie met betrekking tot het Werk. Creative Commons kan op geen enkele wijze aansprakelijk worden gehouden jegens de Gebruiker of derden voor enigerlei schade met inbegrip van, maar niet beperkt tot enige algemene, bijzondere, incidentele of gevolgschade voortvloeiend uit deze Licentie. Onverminderd het bepaalde in de twee (2) voorgaande volzinnen is Creative Commons gebonden aan alle rechten en verplichtingen van de Licentiegever indien Creative Commons zichzelf uitdrukkelijk kenbaar gemaakt heeft als de Licentiegever krachtens deze Licentie.
-
-Met uitzondering van het beperkte doel om iedereen erop te wijzen dat het Werk in licentie is gegeven krachtens de CCPL, geeft Creative Commons aan geen van de partijen toestemming om gebruik te maken van de merknaam 'Creative Commons', enige daarmee verband houdende merknamen dan wel het logo van Creative Commons gebruiken zonder de voorafgaande schriftelijke toestemming van Creative Commons. Het geoorloofde gebruik dient in overeenstemming te zijn met de alsdan geldende richtlijnen betreffende het gebruik van merknamen van Creative Commons zoals die bekend worden gemaakt op de website of anderszins van tijd tot tijd, desgevraagd, ter beschikking worden gesteld. Volledigheidshalve dient te worden vermeld dat deze merkrechtelijke beperking geen deel uitmaakt van de Licentie.
-
-U kunt contact opnemen met Creative Commons via de website: https://creativecommons.org/.
diff --git a/options/license/CC-BY-3.0-US b/options/license/CC-BY-3.0-US
deleted file mode 100644
index c35a2b1a11..0000000000
--- a/options/license/CC-BY-3.0-US
+++ /dev/null
@@ -1,83 +0,0 @@
-Creative Commons Attribution 3.0 United States
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with one or more other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual, individuals, entity or entities that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual, individuals, entity or entities who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to create and reproduce Derivative Works provided that any such Derivative Work, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";;
-
- c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
- d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
-
- e. For the avoidance of doubt, where the Work is a musical composition:
-
- i. Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or, in the event that Licensor is a member of a performance rights society (e.g. ASCAP, BMI, SESAC), via that society, royalties for the public performance or public digital performance (e.g. webcast) of the Work.
-
- ii. Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
-
- f. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of a recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. When You distribute, publicly display, publicly perform, or publicly digitally perform the Work, You may not impose any technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by Section 4(b), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by Section 4(b), as requested.
-
- b. If You distribute, publicly display, publicly perform, or publicly digitally perform the Work (as defined in Section 1 above) or any Derivative Works (as defined in Section 1 above) or Collective Works (as defined in Section 1 above), You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, consistent with Section 3(b) in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(b) may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear, if a credit for all contributing authors of the Derivative Work or Collective Work appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND ONLY TO THE EXTENT OF ANY RIGHTS HELD IN THE LICENSED WORK BY THE LICENSOR. THE LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MARKETABILITY, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works (as defined in Section 1 above) or Collective Works (as defined in Section 1 above) from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work (as defined in Section 1 above) or a Collective Work (as defined in Section 1 above), the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons Notice
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of the License.
-
-Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-1.0 b/options/license/CC-BY-NC-1.0
deleted file mode 100644
index 1cc211eb90..0000000000
--- a/options/license/CC-BY-NC-1.0
+++ /dev/null
@@ -1,75 +0,0 @@
-Creative Commons Attribution-NonCommercial 1.0
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DRAFT LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to create and reproduce Derivative Works;
-
- c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
- d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works;
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any reference to such Licensor or the Original Author, as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any reference to such Licensor or the Original Author, as requested.
-
- b. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
- c. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and give the Original Author credit reasonable to the medium or means You are utilizing by conveying the name (or pseudonym if applicable) of the Original Author if supplied; the title of the Work if supplied; in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
-5. Representations, Warranties and Disclaimer
-
-By offering the Work for public release under this License, Licensor represents and warrants that, to the best of Licensor's knowledge after reasonable inquiry: Licensor has secured all rights in the Work necessary to grant the license rights hereunder and to permit the lawful exercise of the rights granted hereunder without You having any obligation to pay any royalties, compulsory license fees, residuals or any other payments; The Work does not infringe the copyright, trademark, publicity rights, common law rights or any other right of any third party or constitute defamation, invasion of privacy or other tortious injury to any third party. EXCEPT AS EXPRESSLY STATED IN THIS LICENSE OR OTHERWISE AGREED IN WRITING OR REQUIRED BY APPLICABLE LAW, THE WORK IS LICENSED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES REGARDING THE CONTENTS OR ACCURACY OF THE WORK.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, AND EXCEPT FOR DAMAGES ARISING FROM LIABILITY TO A THIRD PARTY RESULTING FROM BREACH OF THE WARRANTIES IN SECTION 5, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-2.0 b/options/license/CC-BY-NC-2.0
deleted file mode 100644
index 3732ddfc9e..0000000000
--- a/options/license/CC-BY-NC-2.0
+++ /dev/null
@@ -1,81 +0,0 @@
-Creative Commons Attribution-NonCommercial 2.0
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to create and reproduce Derivative Works;
-
- c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
- d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works;
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Sections 4(d) and 4(e).
-
-4. Restrictions.The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any reference to such Licensor or the Original Author, as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any reference to such Licensor or the Original Author, as requested.
-
- b. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
- c. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and give the Original Author credit reasonable to the medium or means You are utilizing by conveying the name (or pseudonym if applicable) of the Original Author if supplied; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
- d. For the avoidance of doubt, where the Work is a musical composition:
-
- i. Performance Royalties Under Blanket Licenses. Licensor reserves the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work if that performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
- ii. Mechanical Rights and Statutory Royalties. Licensor reserves the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions), if Your distribution of such cover version is primarily intended for or directed toward commercial advantage or private monetary compensation. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor reserves the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions), if Your public digital performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-2.5 b/options/license/CC-BY-NC-2.5
deleted file mode 100644
index d0259b391f..0000000000
--- a/options/license/CC-BY-NC-2.5
+++ /dev/null
@@ -1,81 +0,0 @@
-Creative Commons Attribution-NonCommercial 2.5
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License. "Original Author" means the individual or entity who created the Work.
-
- d. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- e. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to create and reproduce Derivative Works;
-
- c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
- d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works;
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Sections 4(d) and 4(e).
-
-4. Restrictions.The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder.
-
- b. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(c), as requested. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
- c. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
- d. For the avoidance of doubt, where the Work is a musical composition:
-
- i. Performance Royalties Under Blanket Licenses. Licensor reserves the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work if that performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
- ii. Mechanical Rights and Statutory Royalties. Licensor reserves the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions), if Your distribution of such cover version is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
- e. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor reserves the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions), if Your public digital performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-3.0 b/options/license/CC-BY-NC-3.0
deleted file mode 100644
index 197ec4de65..0000000000
--- a/options/license/CC-BY-NC-3.0
+++ /dev/null
@@ -1,334 +0,0 @@
-Creative Commons Legal Code
-
-Attribution-NonCommercial 3.0 Unported
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
- LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
- ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
- INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
- REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
- DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
-COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
-COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
-AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
-TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
-BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
-CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
-CONDITIONS.
-
-1. Definitions
-
- a. "Adaptation" means a work based upon the Work, or upon the Work and
- other pre-existing works, such as a translation, adaptation,
- derivative work, arrangement of music or other alterations of a
- literary or artistic work, or phonogram or performance and includes
- cinematographic adaptations or any other form in which the Work may be
- recast, transformed, or adapted including in any form recognizably
- derived from the original, except that a work that constitutes a
- Collection will not be considered an Adaptation for the purpose of
- this License. For the avoidance of doubt, where the Work is a musical
- work, performance or phonogram, the synchronization of the Work in
- timed-relation with a moving image ("synching") will be considered an
- Adaptation for the purpose of this License.
- b. "Collection" means a collection of literary or artistic works, such as
- encyclopedias and anthologies, or performances, phonograms or
- broadcasts, or other works or subject matter other than works listed
- in Section 1(f) below, which, by reason of the selection and
- arrangement of their contents, constitute intellectual creations, in
- which the Work is included in its entirety in unmodified form along
- with one or more other contributions, each constituting separate and
- independent works in themselves, which together are assembled into a
- collective whole. A work that constitutes a Collection will not be
- considered an Adaptation (as defined above) for the purposes of this
- License.
- c. "Distribute" means to make available to the public the original and
- copies of the Work or Adaptation, as appropriate, through sale or
- other transfer of ownership.
- d. "Licensor" means the individual, individuals, entity or entities that
- offer(s) the Work under the terms of this License.
- e. "Original Author" means, in the case of a literary or artistic work,
- the individual, individuals, entity or entities who created the Work
- or if no individual or entity can be identified, the publisher; and in
- addition (i) in the case of a performance the actors, singers,
- musicians, dancers, and other persons who act, sing, deliver, declaim,
- play in, interpret or otherwise perform literary or artistic works or
- expressions of folklore; (ii) in the case of a phonogram the producer
- being the person or legal entity who first fixes the sounds of a
- performance or other sounds; and, (iii) in the case of broadcasts, the
- organization that transmits the broadcast.
- f. "Work" means the literary and/or artistic work offered under the terms
- of this License including without limitation any production in the
- literary, scientific and artistic domain, whatever may be the mode or
- form of its expression including digital form, such as a book,
- pamphlet and other writing; a lecture, address, sermon or other work
- of the same nature; a dramatic or dramatico-musical work; a
- choreographic work or entertainment in dumb show; a musical
- composition with or without words; a cinematographic work to which are
- assimilated works expressed by a process analogous to cinematography;
- a work of drawing, painting, architecture, sculpture, engraving or
- lithography; a photographic work to which are assimilated works
- expressed by a process analogous to photography; a work of applied
- art; an illustration, map, plan, sketch or three-dimensional work
- relative to geography, topography, architecture or science; a
- performance; a broadcast; a phonogram; a compilation of data to the
- extent it is protected as a copyrightable work; or a work performed by
- a variety or circus performer to the extent it is not otherwise
- considered a literary or artistic work.
- g. "You" means an individual or entity exercising rights under this
- License who has not previously violated the terms of this License with
- respect to the Work, or who has received express permission from the
- Licensor to exercise rights under this License despite a previous
- violation.
- h. "Publicly Perform" means to perform public recitations of the Work and
- to communicate to the public those public recitations, by any means or
- process, including by wire or wireless means or public digital
- performances; to make available to the public Works in such a way that
- members of the public may access these Works from a place and at a
- place individually chosen by them; to perform the Work to the public
- by any means or process and the communication to the public of the
- performances of the Work, including by public digital performance; to
- broadcast and rebroadcast the Work by any means including signs,
- sounds or images.
- i. "Reproduce" means to make copies of the Work by any means including
- without limitation by sound or visual recordings and the right of
- fixation and reproducing fixations of the Work, including storage of a
- protected performance or phonogram in digital form or other electronic
- medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce,
-limit, or restrict any uses free from copyright or rights arising from
-limitations or exceptions that are provided for in connection with the
-copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License,
-Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
-perpetual (for the duration of the applicable copyright) license to
-exercise the rights in the Work as stated below:
-
- a. to Reproduce the Work, to incorporate the Work into one or more
- Collections, and to Reproduce the Work as incorporated in the
- Collections;
- b. to create and Reproduce Adaptations provided that any such Adaptation,
- including any translation in any medium, takes reasonable steps to
- clearly label, demarcate or otherwise identify that changes were made
- to the original Work. For example, a translation could be marked "The
- original work was translated from English to Spanish," or a
- modification could indicate "The original work has been modified.";
- c. to Distribute and Publicly Perform the Work including as incorporated
- in Collections; and,
- d. to Distribute and Publicly Perform Adaptations.
-
-The above rights may be exercised in all media and formats whether now
-known or hereafter devised. The above rights include the right to make
-such modifications as are technically necessary to exercise the rights in
-other media and formats. Subject to Section 8(f), all rights not expressly
-granted by Licensor are hereby reserved, including but not limited to the
-rights set forth in Section 4(d).
-
-4. Restrictions. The license granted in Section 3 above is expressly made
-subject to and limited by the following restrictions:
-
- a. You may Distribute or Publicly Perform the Work only under the terms
- of this License. You must include a copy of, or the Uniform Resource
- Identifier (URI) for, this License with every copy of the Work You
- Distribute or Publicly Perform. You may not offer or impose any terms
- on the Work that restrict the terms of this License or the ability of
- the recipient of the Work to exercise the rights granted to that
- recipient under the terms of the License. You may not sublicense the
- Work. You must keep intact all notices that refer to this License and
- to the disclaimer of warranties with every copy of the Work You
- Distribute or Publicly Perform. When You Distribute or Publicly
- Perform the Work, You may not impose any effective technological
- measures on the Work that restrict the ability of a recipient of the
- Work from You to exercise the rights granted to that recipient under
- the terms of the License. This Section 4(a) applies to the Work as
- incorporated in a Collection, but this does not require the Collection
- apart from the Work itself to be made subject to the terms of this
- License. If You create a Collection, upon notice from any Licensor You
- must, to the extent practicable, remove from the Collection any credit
- as required by Section 4(c), as requested. If You create an
- Adaptation, upon notice from any Licensor You must, to the extent
- practicable, remove from the Adaptation any credit as required by
- Section 4(c), as requested.
- b. You may not exercise any of the rights granted to You in Section 3
- above in any manner that is primarily intended for or directed toward
- commercial advantage or private monetary compensation. The exchange of
- the Work for other copyrighted works by means of digital file-sharing
- or otherwise shall not be considered to be intended for or directed
- toward commercial advantage or private monetary compensation, provided
- there is no payment of any monetary compensation in connection with
- the exchange of copyrighted works.
- c. If You Distribute, or Publicly Perform the Work or any Adaptations or
- Collections, You must, unless a request has been made pursuant to
- Section 4(a), keep intact all copyright notices for the Work and
- provide, reasonable to the medium or means You are utilizing: (i) the
- name of the Original Author (or pseudonym, if applicable) if supplied,
- and/or if the Original Author and/or Licensor designate another party
- or parties (e.g., a sponsor institute, publishing entity, journal) for
- attribution ("Attribution Parties") in Licensor's copyright notice,
- terms of service or by other reasonable means, the name of such party
- or parties; (ii) the title of the Work if supplied; (iii) to the
- extent reasonably practicable, the URI, if any, that Licensor
- specifies to be associated with the Work, unless such URI does not
- refer to the copyright notice or licensing information for the Work;
- and, (iv) consistent with Section 3(b), in the case of an Adaptation,
- a credit identifying the use of the Work in the Adaptation (e.g.,
- "French translation of the Work by Original Author," or "Screenplay
- based on original Work by Original Author"). The credit required by
- this Section 4(c) may be implemented in any reasonable manner;
- provided, however, that in the case of a Adaptation or Collection, at
- a minimum such credit will appear, if a credit for all contributing
- authors of the Adaptation or Collection appears, then as part of these
- credits and in a manner at least as prominent as the credits for the
- other contributing authors. For the avoidance of doubt, You may only
- use the credit required by this Section for the purpose of attribution
- in the manner set out above and, by exercising Your rights under this
- License, You may not implicitly or explicitly assert or imply any
- connection with, sponsorship or endorsement by the Original Author,
- Licensor and/or Attribution Parties, as appropriate, of You or Your
- use of the Work, without the separate, express prior written
- permission of the Original Author, Licensor and/or Attribution
- Parties.
- d. For the avoidance of doubt:
-
- i. Non-waivable Compulsory License Schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme cannot be waived, the Licensor
- reserves the exclusive right to collect such royalties for any
- exercise by You of the rights granted under this License;
- ii. Waivable Compulsory License Schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme can be waived, the Licensor reserves
- the exclusive right to collect such royalties for any exercise by
- You of the rights granted under this License if Your exercise of
- such rights is for a purpose or use which is otherwise than
- noncommercial as permitted under Section 4(b) and otherwise waives
- the right to collect royalties through any statutory or compulsory
- licensing scheme; and,
- iii. Voluntary License Schemes. The Licensor reserves the right to
- collect royalties, whether individually or, in the event that the
- Licensor is a member of a collecting society that administers
- voluntary licensing schemes, via that society, from any exercise
- by You of the rights granted under this License that is for a
- purpose or use which is otherwise than noncommercial as permitted
- under Section 4(c).
- e. Except as otherwise agreed in writing by the Licensor or as may be
- otherwise permitted by applicable law, if You Reproduce, Distribute or
- Publicly Perform the Work either by itself or as part of any
- Adaptations or Collections, You must not distort, mutilate, modify or
- take other derogatory action in relation to the Work which would be
- prejudicial to the Original Author's honor or reputation. Licensor
- agrees that in those jurisdictions (e.g. Japan), in which any exercise
- of the right granted in Section 3(b) of this License (the right to
- make Adaptations) would be deemed to be a distortion, mutilation,
- modification or other derogatory action prejudicial to the Original
- Author's honor and reputation, the Licensor will waive or not assert,
- as appropriate, this Section, to the fullest extent permitted by the
- applicable national law, to enable You to reasonably exercise Your
- right under Section 3(b) of this License (right to make Adaptations)
- but not otherwise.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
-OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
-KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
-INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
-FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
-LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
-WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
-OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
-LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
-ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
-ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
-BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate
- automatically upon any breach by You of the terms of this License.
- Individuals or entities who have received Adaptations or Collections
- from You under this License, however, will not have their licenses
- terminated provided such individuals or entities remain in full
- compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
- survive any termination of this License.
- b. Subject to the above terms and conditions, the license granted here is
- perpetual (for the duration of the applicable copyright in the Work).
- Notwithstanding the above, Licensor reserves the right to release the
- Work under different license terms or to stop distributing the Work at
- any time; provided, however that any such election will not serve to
- withdraw this License (or any other license that has been, or is
- required to be, granted under the terms of this License), and this
- License will continue in full force and effect unless terminated as
- stated above.
-
-8. Miscellaneous
-
- a. Each time You Distribute or Publicly Perform the Work or a Collection,
- the Licensor offers to the recipient a license to the Work on the same
- terms and conditions as the license granted to You under this License.
- b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
- offers to the recipient a license to the original Work on the same
- terms and conditions as the license granted to You under this License.
- c. If any provision of this License is invalid or unenforceable under
- applicable law, it shall not affect the validity or enforceability of
- the remainder of the terms of this License, and without further action
- by the parties to this agreement, such provision shall be reformed to
- the minimum extent necessary to make such provision valid and
- enforceable.
- d. No term or provision of this License shall be deemed waived and no
- breach consented to unless such waiver or consent shall be in writing
- and signed by the party to be charged with such waiver or consent.
- e. This License constitutes the entire agreement between the parties with
- respect to the Work licensed here. There are no understandings,
- agreements or representations with respect to the Work not specified
- here. Licensor shall not be bound by any additional provisions that
- may appear in any communication from You. This License may not be
- modified without the mutual written agreement of the Licensor and You.
- f. The rights granted under, and the subject matter referenced, in this
- License were drafted utilizing the terminology of the Berne Convention
- for the Protection of Literary and Artistic Works (as amended on
- September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
- Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
- and the Universal Copyright Convention (as revised on July 24, 1971).
- These rights and subject matter take effect in the relevant
- jurisdiction in which the License terms are sought to be enforced
- according to the corresponding provisions of the implementation of
- those treaty provisions in the applicable national law. If the
- standard suite of rights granted under applicable copyright law
- includes additional rights not granted under this License, such
- additional rights are deemed to be included in the License; this
- License is not intended to restrict the license of any rights under
- applicable law.
-
-
-Creative Commons Notice
-
- Creative Commons is not a party to this License, and makes no warranty
- whatsoever in connection with the Work. Creative Commons will not be
- liable to You or any party on any legal theory for any damages
- whatsoever, including without limitation any general, special,
- incidental or consequential damages arising in connection to this
- license. Notwithstanding the foregoing two (2) sentences, if Creative
- Commons has expressly identified itself as the Licensor hereunder, it
- shall have all rights and obligations of Licensor.
-
- Except for the limited purpose of indicating to the public that the
- Work is licensed under the CCPL, Creative Commons does not authorize
- the use by either party of the trademark "Creative Commons" or any
- related trademark or logo of Creative Commons without the prior
- written consent of Creative Commons. Any permitted use will be in
- compliance with Creative Commons' then-current trademark usage
- guidelines, as may be published on its website or otherwise made
- available upon request from time to time. For the avoidance of doubt,
- this trademark restriction does not form part of the License.
-
- Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-3.0-DE b/options/license/CC-BY-NC-3.0-DE
deleted file mode 100644
index 5d11815286..0000000000
--- a/options/license/CC-BY-NC-3.0-DE
+++ /dev/null
@@ -1,110 +0,0 @@
-Creative Commons Namensnennung - Keine kommerzielle Nutzung 3.0 Deutschland
-
- CREATIVE COMMONS IST KEINE RECHTSANWALTSKANZLEI UND LEISTET KEINE RECHTSBERATUNG. DIE BEREITSTELLUNG DIESER LIZENZ FÜHRT ZU KEINEM MANDATSVERHÄLTNIS. CREATIVE COMMONS STELLT DIESE INFORMATIONEN OHNE GEWÄHR ZUR VERFÜGUNG. CREATIVE COMMONS ÜBERNIMMT KEINE GEWÄHRLEISTUNG FÜR DIE GELIEFERTEN INFORMATIONEN UND SCHLIEßT DIE HAFTUNG FÜR SCHÄDEN AUS, DIE SICH AUS DEREN GEBRAUCH ERGEBEN.
-
-Lizenz
-
-DER GEGENSTAND DIESER LIZENZ (WIE UNTER "SCHUTZGEGENSTAND" DEFINIERT) WIRD UNTER DEN BEDINGUNGEN DIESER CREATIVE COMMONS PUBLIC LICENSE ("CCPL", "LIZENZ" ODER "LIZENZVERTRAG") ZUR VERFÜGUNG GESTELLT. DER SCHUTZGEGENSTAND IST DURCH DAS URHEBERRECHT UND/ODER ANDERE GESETZE GESCHÜTZT. JEDE FORM DER NUTZUNG DES SCHUTZGEGENSTANDES, DIE NICHT AUFGRUND DIESER LIZENZ ODER DURCH GESETZE GESTATTET IST, IST UNZULÄSSIG.
-
-DURCH DIE AUSÜBUNG EINES DURCH DIESE LIZENZ GEWÄHRTEN RECHTS AN DEM SCHUTZGEGENSTAND ERKLÄREN SIE SICH MIT DEN LIZENZBEDINGUNGEN RECHTSVERBINDLICH EINVERSTANDEN. SOWEIT DIESE LIZENZ ALS LIZENZVERTRAG ANZUSEHEN IST, GEWÄHRT IHNEN DER LIZENZGEBER DIE IN DER LIZENZ GENANNTEN RECHTE UNENTGELTLICH UND IM AUSTAUSCH DAFÜR, DASS SIE DAS GEBUNDENSEIN AN DIE LIZENZBEDINGUNGEN AKZEPTIEREN.
-
-1. Definitionen
-
- a. Der Begriff "Abwandlung" im Sinne dieser Lizenz bezeichnet das Ergebnis jeglicher Art von Veränderung des Schutzgegenstandes, solange die eigenpersönlichen Züge des Schutzgegenstandes darin nicht verblassen und daran eigene Schutzrechte entstehen. Das kann insbesondere eine Bearbeitung, Umgestaltung, Änderung, Anpassung, Übersetzung oder Heranziehung des Schutzgegenstandes zur Vertonung von Laufbildern sein. Nicht als Abwandlung des Schutzgegenstandes gelten seine Aufnahme in eine Sammlung oder ein Sammelwerk und die freie Benutzung des Schutzgegenstandes.
-
- b. Der Begriff "Sammelwerk" im Sinne dieser Lizenz meint eine Zusammenstellung von literarischen, künstlerischen oder wissenschaftlichen Inhalten, sofern diese Zusammenstellung aufgrund von Auswahl und Anordnung der darin enthaltenen selbständigen Elemente eine geistige Schöpfung darstellt, unabhängig davon, ob die Elemente systematisch oder methodisch angelegt und dadurch einzeln zugänglich sind oder nicht.
-
- c. "Verbreiten" im Sinne dieser Lizenz bedeutet, den Schutzgegenstand oder Abwandlungen im Original oder in Form von Vervielfältigungsstücken, mithin in körperlich fixierter Form der Öffentlichkeit anzubieten oder in Verkehr zu bringen.
-
- d. Der "Lizenzgeber" im Sinne dieser Lizenz ist diejenige natürliche oder juristische Person oder Gruppe, die den Schutzgegenstand unter den Bedingungen dieser Lizenz anbietet und insoweit als Rechteinhaberin auftritt.
-
- e. "Rechteinhaber" im Sinne dieser Lizenz ist der Urheber des Schutzgegenstandes oder jede andere natürliche oder juristische Person oder Gruppe von Personen, die am Schutzgegenstand ein Immaterialgüterrecht erlangt hat, welches die in Abschnitt 3 genannten Handlungen erfasst und bei dem eine Einräumung von Nutzungsrechten oder eine Weiterübertragung an Dritte möglich ist.
-
- f. Der Begriff "Schutzgegenstand" bezeichnet in dieser Lizenz den literarischen, künstlerischen oder wissenschaftlichen Inhalt, der unter den Bedingungen dieser Lizenz angeboten wird. Das kann insbesondere eine persönliche geistige Schöpfung jeglicher Art, ein Werk der kleinen Münze, ein nachgelassenes Werk oder auch ein Lichtbild oder anderes Objekt eines verwandten Schutzrechts sein, unabhängig von der Art seiner Fixierung und unabhängig davon, auf welche Weise jeweils eine Wahrnehmung erfolgen kann, gleichviel ob in analoger oder digitaler Form. Soweit Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen, unterfallen auch sie dem Begriff "Schutzgegenstand" im Sinne dieser Lizenz.
-
- g. Mit "Sie" bzw. "Ihne*" ist die natürliche oder juristische Person gemeint, die in dieser Lizenz im Abschnitt 3 genannte Nutzungen des Schutzgegenstandes vornimmt und zuvor in Hinblick auf den Schutzgegenstand nicht gegen Bedingungen dieser Lizenz verstoßen oder aber die ausdrückliche Erlaubnis des Lizenzgebers erhalten hat, die durch diese Lizenz gewährten Nutzungsrechte trotz eines vorherigen Verstoßes auszuüben.
-
- h. Unter "Öffentlich Zeigen" im Sinne dieser Lizenz sind Veröffentlichungen und Präsentationen des Schutzgegenstandes zu verstehen, die für eine Mehrzahl von Mitgliedern der Öffentlichkeit bestimmt sind und in unkörperlicher Form mittels öffentlicher Wiedergabe in Form von Vortrag, Aufführung, Vorführung, Darbietung, Sendung, Weitersendung, zeit- und ortsunabhängiger Zugänglichmachung oder in körperlicher Form mittels Ausstellung erfolgen, unabhängig von bestimmten Veranstaltungen und unabhängig von den zum Einsatz kommenden Techniken und Verfahren, einschließlich drahtgebundener oder drahtloser Mittel und Einstellen in das Internet.
-
- i. "Vervielfältigen" im Sinne dieser Lizenz bedeutet, mittels beliebiger Verfahren Vervielfältigungsstücke des Schutzgegenstandes herzustellen, insbesondere durch Ton- oder Bildaufzeichnungen, und umfasst auch den Vorgang, erstmals körperliche Fixierungen des Schutzgegenstandes sowie Vervielfältigungsstücke dieser Fixierungen anzufertigen, sowie die Übertragung des Schutzgegenstandes auf einen Bild- oder Tonträger oder auf ein anderes elektronisches Medium, gleichviel ob in digitaler oder analoger Form.
-
-2. Schranken des Immaterialgüterrechts. Diese Lizenz ist in keiner Weise darauf gerichtet, Befugnisse zur Nutzung des Schutzgegenstandes zu vermindern, zu beschränken oder zu vereiteln, die Ihnen aufgrund der Schranken des Urheberrechts oder anderer Rechtsnormen bereits ohne Weiteres zustehen oder sich aus dem Fehlen eines immaterialgüterrechtlichen Schutzes ergeben.
-
-3. Einräumung von Nutzungsrechten. Unter den Bedingungen dieser Lizenz räumt Ihnen der Lizenzgeber - unbeschadet unverzichtbarer Rechte und vorbehaltlich des Abschnitts 4.e) - das vergütungsfreie, räumlich und zeitlich (für die Dauer des Schutzrechts am Schutzgegenstand) unbeschränkte einfache Recht ein, den Schutzgegenstand auf die folgenden Arten und Weisen zu nutzen ("unentgeltlich eingeräumtes einfaches Nutzungsrecht für jedermann"):
-
- a. Den Schutzgegenstand in beliebiger Form und Menge zu vervielfältigen, ihn in Sammelwerke zu integrieren und ihn als Teil solcher Sammelwerke zu vervielfältigen;
-
- b. Abwandlungen des Schutzgegenstandes anzufertigen, einschließlich Übersetzungen unter Nutzung jedweder Medien, sofern deutlich erkennbar gemacht wird, dass es sich um Abwandlungen handelt;
-
- c. den Schutzgegenstand, allein oder in Sammelwerke aufgenommen, öffentlich zu zeigen und zu verbreiten;
-
- d. Abwandlungen des Schutzgegenstandes zu veröffentlichen, öffentlich zu zeigen und zu verbreiten.
-
-Das vorgenannte Nutzungsrecht wird für alle bekannten sowie für alle noch nicht bekannten Nutzungsarten eingeräumt. Es beinhaltet auch das Recht, solche Änderungen am Schutzgegenstand vorzunehmen, die für bestimmte nach dieser Lizenz zulässige Nutzungen technisch erforderlich sind. Alle sonstigen Rechte, die über diesen Abschnitt hinaus nicht ausdrücklich durch den Lizenzgeber eingeräumt werden, bleiben diesem allein vorbehalten. Soweit Datenbanken oder Zusammenstellungen von Daten Schutzgegenstand dieser Lizenz oder Teil dessen sind und einen immaterialgüterrechtlichen Schutz eigener Art genießen, verzichtet der Lizenzgeber auf sämtliche aus diesem Schutz resultierenden Rechte.
-
-4. Bedingungen. Die Einräumung des Nutzungsrechts gemäß Abschnitt 3 dieser Lizenz erfolgt ausdrücklich nur unter den folgenden Bedingungen:
-
- a. Sie dürfen den Schutzgegenstand ausschließlich unter den Bedingungen dieser Lizenz verbreiten oder öffentlich zeigen. Sie müssen dabei stets eine Kopie dieser Lizenz oder deren vollständige Internetadresse in Form des Uniform-Resource-Identifier (URI) beifügen. Sie dürfen keine Vertrags- oder Nutzungsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch diese Lizenz gewährten Rechte beschränken. Sie dürfen den Schutzgegenstand nicht unterlizenzieren. Bei jeder Kopie des Schutzgegenstandes, die Sie verbreiten oder öffentlich zeigen, müssen Sie alle Hinweise unverändert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Wenn Sie den Schutzgegenstand verbreiten oder öffentlich zeigen, dürfen Sie (in Bezug auf den Schutzgegenstand) keine technischen Maßnahmen ergreifen, die den Nutzer des Schutzgegenstandes in der Ausübung der ihm durch diese Lizenz gewährten Rechte behindern können. Dieser Abschnitt 4.a) gilt auch für den Fall, dass der Schutzgegenstand einen Bestandteil eines Sammelwerkes bildet, was jedoch nicht bedeutet, dass das Sammelwerk insgesamt dieser Lizenz unterstellt werden muss. Sofern Sie ein Sammelwerk erstellen, müssen Sie auf die Mitteilung eines Lizenzgebers hin aus dem Sammelwerk die in Abschnitt 4.c) aufgezählten Hinweise entfernen. Wenn Sie eine Abwandlung vornehmen, müssen Sie auf die Mitteilung eines Lizenzgebers hin von der Abwandlung die in Abschnitt 4.c) aufgezählten Hinweise entfernen.
-
- b. Die Rechteeinräumung gemäß Abschnitt 3 gilt nur für Handlungen, die nicht vorrangig auf einen geschäftlichen Vorteil oder eine geldwerte Vergütung gerichtet sind ("nicht-kommerzielle Nutzung", "Non-commercial-Option"). Wird Ihnen in Zusammenhang mit dem Schutzgegenstand dieser Lizenz ein anderer Schutzgegenstand überlassen, ohne dass eine vertragliche Verpflichtung hierzu besteht (etwa im Wege von File-Sharing), so wird dies nicht als auf geschäftlichen Vorteil oder geldwerte Vergütung gerichtet angesehen, wenn in Verbindung mit dem Austausch der Schutzgegenstände tatsächlich keine Zahlung oder geldwerte Vergütung geleistet wird.
-
- c. Die Verbreitung und das öffentliche Zeigen des Schutzgegenstandes oder auf ihm aufbauender Abwandlungen oder ihn enthaltender Sammelwerke ist Ihnen nur unter der Bedingung gestattet, dass Sie, vorbehaltlich etwaiger Mitteilungen im Sinne von Abschnitt 4.a), alle dazu gehörenden Rechtevermerke unberührt lassen. Sie sind verpflichtet, die Rechteinhaberschaft in einer der Nutzung entsprechenden, angemessenen Form anzuerkennen, indem Sie - soweit bekannt - Folgendes angeben:
-
- i. Den Namen (oder das Pseudonym, falls ein solches verwendet wird) des Rechteinhabers und / oder, falls der Lizenzgeber im Rechtevermerk, in den Nutzungsbedingungen oder auf andere angemessene Weise eine Zuschreibung an Dritte vorgenommen hat (z.B. an eine Stiftung, ein Verlagshaus oder eine Zeitung) ("Zuschreibungsempfänger"), Namen bzw. Bezeichnung dieses oder dieser Dritten;
-
- ii. den Titel des Inhaltes;
-
- iii. in einer praktikablen Form den Uniform-Resource-Identifier (URI, z.B. Internetadresse), den der Lizenzgeber zum Schutzgegenstand angegeben hat, es sei denn, dieser URI verweist nicht auf den Rechtevermerk oder die Lizenzinformationen zum Schutzgegenstand;
-
- iv. und im Falle einer Abwandlung des Schutzgegenstandes in Übereinstimmung mit Abschnitt 3.b) einen Hinweis darauf, dass es sich um eine Abwandlung handelt.
-
- Die nach diesem Abschnitt 4.c) erforderlichen Angaben können in jeder angemessenen Form gemacht werden; im Falle einer Abwandlung des Schutzgegenstandes oder eines Sammelwerkes müssen diese Angaben das Minimum darstellen und bei gemeinsamer Nennung mehrerer Rechteinhaber dergestalt erfolgen, dass sie zumindest ebenso hervorgehoben sind wie die Hinweise auf die übrigen Rechteinhaber. Die Angaben nach diesem Abschnitt dürfen Sie ausschließlich zur Angabe der Rechteinhaberschaft in der oben bezeichneten Weise verwenden. Durch die Ausübung Ihrer Rechte aus dieser Lizenz dürfen Sie ohne eine vorherige, separat und schriftlich vorliegende Zustimmung des Lizenzgebers und / oder des Zuschreibungsempfängers weder explizit noch implizit irgendeine Verbindung zum Lizenzgeber oder Zuschreibungsempfänger und ebenso wenig eine Unterstützung oder Billigung durch ihn andeuten.
-
- d. Die oben unter 4.a) bis c) genannten Einschränkungen gelten nicht für solche Teile des Schutzgegenstandes, die allein deshalb unter den Schutzgegenstandsbegriff fallen, weil sie als Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen.
-
- e. Bezüglich Vergütung für die Nutzung des Schutzgegenstandes gilt Folgendes:
-
- i. Unverzichtbare gesetzliche Vergütungsansprüche: Soweit unverzichtbare Vergütungsansprüche im Gegenzug für gesetzliche Lizenzen vorgesehen oder Pauschalabgabensysteme (zum Beispiel für Leermedien) vorhanden sind, behält sich der Lizenzgeber das ausschließliche Recht vor, die entsprechende Vergütung einzuziehen für jede Ausübung eines Rechts aus dieser Lizenz durch Sie.
-
- ii. Vergütung bei Zwangslizenzen: Sofern Zwangslizenzen außerhalb dieser Lizenz vorgesehen sind und zustande kommen, behält sich der Lizenzgeber das ausschließliche Recht auf Einziehung der entsprechenden Vergütung für den Fall vor, dass Sie eine Nutzung des Schutzgegenstandes für andere als die in Abschnitt 4.b) als nicht-kommerziell definierten Zwecke vornehmen, verzichtet für alle übrigen, lizenzgerechten Fälle von Nutzung jedoch auf jegliche Vergütung.
-
- iii. Vergütung in sonstigen Fällen: Bezüglich lizenzgerechter Nutzung des Schutzgegenstandes durch Sie, die nicht unter die beiden vorherigen Abschnitte (i) und (ii) fällt, verzichtet der Lizenzgeber auf jegliche Vergütung, unabhängig davon, ob eine Einziehung der Vergütung durch ihn selbst oder nur durch eine Verwertungsgesellschaft möglich wäre. Der Lizenzgeber behält sich jedoch das ausschließliche Recht auf Einziehung der entsprechenden Vergütung (durch ihn selbst oder eine Verwertungsgesellschaft) für den Fall vor, dass Sie eine Nutzung des Schutzgegenstandes für andere als die in Abschnitt 4.b) als nicht-kommerziell definierten Zwecke vornehmen.
-
- f. Persönlichkeitsrechte bleiben - soweit sie bestehen - von dieser Lizenz unberührt.
-
-5. Gewährleistung
-
-SOFERN KEINE ANDERS LAUTENDE, SCHRIFTLICHE VEREINBARUNG ZWISCHEN DEM LIZENZGEBER UND IHNEN GESCHLOSSEN WURDE UND SOWEIT MÄNGEL NICHT ARGLISTIG VERSCHWIEGEN WURDEN, BIETET DER LIZENZGEBER DEN SCHUTZGEGENSTAND UND DIE EINRÄUMUNG VON RECHTEN UNTER AUSSCHLUSS JEGLICHER GEWÄHRLEISTUNG AN UND ÜBERNIMMT WEDER AUSDRÜCKLICH NOCH KONKLUDENT GARANTIEN IRGENDEINER ART. DIES UMFASST INSBESONDERE DAS FREISEIN VON SACH- UND RECHTSMÄNGELN, UNABHÄNGIG VON DEREN ERKENNBARKEIT FÜR DEN LIZENZGEBER, DIE VERKEHRSFÄHIGKEIT DES SCHUTZGEGENSTANDES, SEINE VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK SOWIE DIE KORREKTHEIT VON BESCHREIBUNGEN. DIESE GEWÄHRLEISTUNGSBESCHRÄNKUNG GILT NICHT, SOWEIT MÄNGEL ZU SCHÄDEN DER IN ABSCHNITT 6 BEZEICHNETEN ART FÜHREN UND AUF SEITEN DES LIZENZGEBERS DAS JEWEILS GENANNTE VERSCHULDEN BZW. VERTRETENMÜSSEN EBENFALLS VORLIEGT.
-
-6. Haftungsbeschränkung
-
-DER LIZENZGEBER HAFTET IHNEN GEGENÜBER IN BEZUG AUF SCHÄDEN AUS DER VERLETZUNG DES LEBENS, DES KÖRPERS ODER DER GESUNDHEIT NUR, SOFERN IHM WENIGSTENS FAHRLÄSSIGKEIT VORZUWERFEN IST, FÜR SONSTIGE SCHÄDEN NUR BEI GROBER FAHRLÄSSIGKEIT ODER VORSATZ, UND ÜBERNIMMT DARÜBER HINAUS KEINERLEI FREIWILLIGE HAFTUNG.
-
-7. Erlöschen
-
- a. Diese Lizenz und die durch sie eingeräumten Nutzungsrechte erlöschen mit Wirkung für die Zukunft im Falle eines Verstoßes gegen die Lizenzbedingungen durch Sie, ohne dass es dazu der Kenntnis des Lizenzgebers vom Verstoß oder einer weiteren Handlung einer der Vertragsparteien bedarf. Mit natürlichen oder juristischen Personen, die Abwandlungen des Schutzgegenstandes oder diesen enthaltende Sammelwerke unter den Bedingungen dieser Lizenz von Ihnen erhalten haben, bestehen nachträglich entstandene Lizenzbeziehungen jedoch solange weiter, wie die genannten Personen sich ihrerseits an sämtliche Lizenzbedingungen halten. Darüber hinaus gelten die Ziffern 1, 2, 5, 6, 7, und 8 auch nach einem Erlöschen dieser Lizenz fort.
-
- b. Vorbehaltlich der oben genannten Bedingungen gilt diese Lizenz unbefristet bis der rechtliche Schutz für den Schutzgegenstand ausläuft. Davon abgesehen behält der Lizenzgeber das Recht, den Schutzgegenstand unter anderen Lizenzbedingungen anzubieten oder die eigene Weitergabe des Schutzgegenstandes jederzeit einzustellen, solange die Ausübung dieses Rechts nicht einer Kündigung oder einem Widerruf dieser Lizenz (oder irgendeiner Weiterlizenzierung, die auf Grundlage dieser Lizenz bereits erfolgt ist bzw. zukünftig noch erfolgen muss) dient und diese Lizenz unter Berücksichtigung der oben zum Erlöschen genannten Bedingungen vollumfänglich wirksam bleibt.
-
-8. Sonstige Bestimmungen
-
- a. Jedes Mal wenn Sie den Schutzgegenstand für sich genommen oder als Teil eines Sammelwerkes verbreiten oder öffentlich zeigen, bietet der Lizenzgeber dem Empfänger eine Lizenz zu den gleichen Bedingungen und im gleichen Umfang an, wie Ihnen in Form dieser Lizenz.
-
- b. Jedes Mal wenn Sie eine Abwandlung des Schutzgegenstandes verbreiten oder öffentlich zeigen, bietet der Lizenzgeber dem Empfänger eine Lizenz am ursprünglichen Schutzgegenstand zu den gleichen Bedingungen und im gleichen Umfang an, wie Ihnen in Form dieser Lizenz.
-
- c. Sollte eine Bestimmung dieser Lizenz unwirksam sein, so bleibt davon die Wirksamkeit der Lizenz im Übrigen unberührt.
-
- d. Keine Bestimmung dieser Lizenz soll als abbedungen und kein Verstoß gegen sie als zulässig gelten, solange die von dem Verzicht oder von dem Verstoß betroffene Seite nicht schriftlich zugestimmt hat.
-
- e. Diese Lizenz (zusammen mit in ihr ausdrücklich vorgesehenen Erlaubnissen, Mitteilungen und Zustimmungen, soweit diese tatsächlich vorliegen) stellt die vollständige Vereinbarung zwischen dem Lizenzgeber und Ihnen in Bezug auf den Schutzgegenstand dar. Es bestehen keine Abreden, Vereinbarungen oder Erklärungen in Bezug auf den Schutzgegenstand, die in dieser Lizenz nicht genannt sind. Rechtsgeschäftliche Änderungen des Verhältnisses zwischen dem Lizenzgeber und Ihnen sind nur über Modifikationen dieser Lizenz möglich. Der Lizenzgeber ist an etwaige zusätzliche, einseitig durch Sie übermittelte Bestimmungen nicht gebunden. Diese Lizenz kann nur durch schriftliche Vereinbarung zwischen Ihnen und dem Lizenzgeber modifiziert werden. Derlei Modifikationen wirken ausschließlich zwischen dem Lizenzgeber und Ihnen und wirken sich nicht auf die Dritten gemäß Ziffern 8.a) und b) angeboteten Lizenzen aus.
-
- f. Sofern zwischen Ihnen und dem Lizenzgeber keine anderweitige Vereinbarung getroffen wurde und soweit Wahlfreiheit besteht, findet auf diesen Lizenzvertrag das Recht der Bundesrepublik Deutschland Anwendung.
-
-
-Creative Commons Notice
-
-Creative Commons ist nicht Partei dieser Lizenz und übernimmt keinerlei Gewähr oder dergleichen in Bezug auf den Schutzgegenstand. Creative Commons haftet Ihnen oder einer anderen Partei unter keinem rechtlichen Gesichtspunkt für irgendwelche Schäden, die - abstrakt oder konkret, zufällig oder vorhersehbar - im Zusammenhang mit dieser Lizenz entstehen. Unbeschadet der vorangegangen beiden Sätze, hat Creative Commons alle Rechte und Pflichten eines Lizenzgebers, wenn es sich ausdrücklich als Lizenzgeber im Sinne dieser Lizenz bezeichnet.
-
-Creative Commons gewährt den Parteien nur insoweit das Recht, das Logo und die Marke "Creative Commons" zu nutzen, als dies notwendig ist, um der Öffentlichkeit gegenüber kenntlich zu machen, dass der Schutzgegenstand unter einer CCPL steht. Ein darüber hinaus gehender Gebrauch der Marke "Creative Commons" oder einer verwandten Marke oder eines verwandten Logos bedarf der vorherigen schriftlichen Zustimmung von Creative Commons. Jeder erlaubte Gebrauch richtet sich nach der Creative Commons Marken-Nutzungs-Richtlinie in der jeweils aktuellen Fassung, die von Zeit zu Zeit auf der Website veröffentlicht oder auf andere Weise auf Anfrage zugänglich gemacht wird. Zur Klarstellung: Die genannten Einschränkungen der Markennutzung sind nicht Bestandteil dieser Lizenz.
-
-Creative Commons kann kontaktiert werden über https://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-4.0 b/options/license/CC-BY-NC-4.0
deleted file mode 100644
index 340cf0c959..0000000000
--- a/options/license/CC-BY-NC-4.0
+++ /dev/null
@@ -1,158 +0,0 @@
-Creative Commons Attribution-NonCommercial 4.0 International
-
- Creative Commons Corporation (“Creative Commonsâ€) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is†basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible.
-
-Using Creative Commons Public Licenses
-
-Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses.
-
-Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors.
-
-Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public.
-
-Creative Commons Attribution-NonCommercial 4.0 International Public License
-
-By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
-
-Section 1 – Definitions.
-
- a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
-
- b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
-
- c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
-
- d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
-
- e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
-
- f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
-
- g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
-
- h. Licensor means the individual(s) or entity(ies) granting rights under this Public License.
-
- i. NonCommercial means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.
-
- j. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
-
- k. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
-
- l. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
-
-Section 2 – Scope.
-
- a. License grant.
-
- 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
-
- A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and
-
- B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only.
-
- 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
-
- 3. Term. The term of this Public License is specified in Section 6(a).
-
- 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
-
- 5. Downstream recipients.
-
- A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
-
- B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
-
- 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
-
- b. Other rights.
-
- 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
-
- 2. Patent and trademark rights are not licensed under this Public License.
-
- 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.
-
-Section 3 – License Conditions.
-
-Your exercise of the Licensed Rights is expressly made subject to the following conditions.
-
- a. Attribution.
-
- 1. If You Share the Licensed Material (including in modified form), You must:
-
- A. retain the following if it is supplied by the Licensor with the Licensed Material:
-
- i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
-
- ii. a copyright notice;
-
- iii. a notice that refers to this Public License;
-
- iv. a notice that refers to the disclaimer of warranties;
-
- v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
-
- B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
-
- C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
-
- 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
-
- 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
-
- 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License.
-
-Section 4 – Sui Generis Database Rights.
-
-Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
-
- a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only;
-
- b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and
-
- c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
-For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
-
-Section 5 – Disclaimer of Warranties and Limitation of Liability.
-
- a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
-
- b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
-
- c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
-
-Section 6 – Term and Termination.
-
- a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
-
- b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
-
- 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
-
- 2. upon express reinstatement by the Licensor.
-
- For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
-
- c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
-
- d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
-
-Section 7 – Other Terms and Conditions.
-
- a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
-
- b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
-
-Section 8 – Interpretation.
-
- a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
-
- b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
-
- c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
-
- d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
-
-Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.†Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons†or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.
-
-Creative Commons may be contacted at creativecommons.org.
diff --git a/options/license/CC-BY-NC-ND-1.0 b/options/license/CC-BY-NC-ND-1.0
deleted file mode 100644
index 91bde04a98..0000000000
--- a/options/license/CC-BY-NC-ND-1.0
+++ /dev/null
@@ -1,75 +0,0 @@
-Creative Commons Attribution-NoDerivs-NonCommercial 1.0
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DRAFT LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any reference to such Licensor or the Original Author, as requested.
-
- b. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
- c. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Collective Works, You must keep intact all copyright notices for the Work and give the Original Author credit reasonable to the medium or means You are utilizing by conveying the name (or pseudonym if applicable) of the Original Author if supplied; the title of the Work if supplied. Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
-5. Representations, Warranties and Disclaimer
-
- a. By offering the Work for public release under this License, Licensor represents and warrants that, to the best of Licensor's knowledge after reasonable inquiry:
-
- i. Licensor has secured all rights in the Work necessary to grant the license rights hereunder and to permit the lawful exercise of the rights granted hereunder without You having any obligation to pay any royalties, compulsory license fees, residuals or any other payments;
-
- ii. The Work does not infringe the copyright, trademark, publicity rights, common law rights or any other right of any third party or constitute defamation, invasion of privacy or other tortious injury to any third party.
-
- b. EXCEPT AS EXPRESSLY STATED IN THIS LICENSE OR OTHERWISE AGREED IN WRITING OR REQUIRED BY APPLICABLE LAW, THE WORK IS LICENSED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES REGARDING THE CONTENTS OR ACCURACY OF THE WORK.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, AND EXCEPT FOR DAMAGES ARISING FROM LIABILITY TO A THIRD PARTY RESULTING FROM BREACH OF THE WARRANTIES IN SECTION 5, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- c. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- d. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-ND-2.0 b/options/license/CC-BY-NC-ND-2.0
deleted file mode 100644
index fe0df20040..0000000000
--- a/options/license/CC-BY-NC-ND-2.0
+++ /dev/null
@@ -1,77 +0,0 @@
-Creative Commons Attribution-NonCommercial-NoDerivs 2.0
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Derivative Works. All rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Sections 4(d) and 4(e).
-
-4. Restrictions.The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any reference to such Licensor or the Original Author, as requested.
-
- b. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
- c. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work, You must keep intact all copyright notices for the Work and give the Original Author credit reasonable to the medium or means You are utilizing by conveying the name (or pseudonym if applicable) of the Original Author if supplied; the title of the Work if supplied; and to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
- d. For the avoidance of doubt, where the Work is a musical composition:
-
- i. Performance Royalties Under Blanket Licenses. Licensor reserves the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work if that performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
- ii. Mechanical Rights and Statutory Royalties. Licensor reserves the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions), if Your distribution of such cover version is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
- e. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor reserves the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions), if Your public digital performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- c. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- d. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-ND-2.5 b/options/license/CC-BY-NC-ND-2.5
deleted file mode 100644
index 13642c541b..0000000000
--- a/options/license/CC-BY-NC-ND-2.5
+++ /dev/null
@@ -1,77 +0,0 @@
-Creative Commons Attribution-NonCommercial-NoDerivs 2.5
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Derivative Works. All rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Sections 4(d) and 4(e).
-
-4. Restrictions.The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested.
-
- b. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
- c. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; and to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
- d. For the avoidance of doubt, where the Work is a musical composition:
-
- i. Performance Royalties Under Blanket Licenses. Licensor reserves the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work if that performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
- ii. Mechanical Rights and Statutory Royalties. Licensor reserves the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions), if Your distribution of such cover version is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
- e. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor reserves the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions), if Your public digital performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- c. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- d. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-ND-3.0 b/options/license/CC-BY-NC-ND-3.0
deleted file mode 100644
index 30b08e74db..0000000000
--- a/options/license/CC-BY-NC-ND-3.0
+++ /dev/null
@@ -1,308 +0,0 @@
-Creative Commons Legal Code
-
-Attribution-NonCommercial-NoDerivs 3.0 Unported
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
- LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
- ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
- INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
- REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
- DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
-COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
-COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
-AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
-TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
-BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
-CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
-CONDITIONS.
-
-1. Definitions
-
- a. "Adaptation" means a work based upon the Work, or upon the Work and
- other pre-existing works, such as a translation, adaptation,
- derivative work, arrangement of music or other alterations of a
- literary or artistic work, or phonogram or performance and includes
- cinematographic adaptations or any other form in which the Work may be
- recast, transformed, or adapted including in any form recognizably
- derived from the original, except that a work that constitutes a
- Collection will not be considered an Adaptation for the purpose of
- this License. For the avoidance of doubt, where the Work is a musical
- work, performance or phonogram, the synchronization of the Work in
- timed-relation with a moving image ("synching") will be considered an
- Adaptation for the purpose of this License.
- b. "Collection" means a collection of literary or artistic works, such as
- encyclopedias and anthologies, or performances, phonograms or
- broadcasts, or other works or subject matter other than works listed
- in Section 1(f) below, which, by reason of the selection and
- arrangement of their contents, constitute intellectual creations, in
- which the Work is included in its entirety in unmodified form along
- with one or more other contributions, each constituting separate and
- independent works in themselves, which together are assembled into a
- collective whole. A work that constitutes a Collection will not be
- considered an Adaptation (as defined above) for the purposes of this
- License.
- c. "Distribute" means to make available to the public the original and
- copies of the Work through sale or other transfer of ownership.
- d. "Licensor" means the individual, individuals, entity or entities that
- offer(s) the Work under the terms of this License.
- e. "Original Author" means, in the case of a literary or artistic work,
- the individual, individuals, entity or entities who created the Work
- or if no individual or entity can be identified, the publisher; and in
- addition (i) in the case of a performance the actors, singers,
- musicians, dancers, and other persons who act, sing, deliver, declaim,
- play in, interpret or otherwise perform literary or artistic works or
- expressions of folklore; (ii) in the case of a phonogram the producer
- being the person or legal entity who first fixes the sounds of a
- performance or other sounds; and, (iii) in the case of broadcasts, the
- organization that transmits the broadcast.
- f. "Work" means the literary and/or artistic work offered under the terms
- of this License including without limitation any production in the
- literary, scientific and artistic domain, whatever may be the mode or
- form of its expression including digital form, such as a book,
- pamphlet and other writing; a lecture, address, sermon or other work
- of the same nature; a dramatic or dramatico-musical work; a
- choreographic work or entertainment in dumb show; a musical
- composition with or without words; a cinematographic work to which are
- assimilated works expressed by a process analogous to cinematography;
- a work of drawing, painting, architecture, sculpture, engraving or
- lithography; a photographic work to which are assimilated works
- expressed by a process analogous to photography; a work of applied
- art; an illustration, map, plan, sketch or three-dimensional work
- relative to geography, topography, architecture or science; a
- performance; a broadcast; a phonogram; a compilation of data to the
- extent it is protected as a copyrightable work; or a work performed by
- a variety or circus performer to the extent it is not otherwise
- considered a literary or artistic work.
- g. "You" means an individual or entity exercising rights under this
- License who has not previously violated the terms of this License with
- respect to the Work, or who has received express permission from the
- Licensor to exercise rights under this License despite a previous
- violation.
- h. "Publicly Perform" means to perform public recitations of the Work and
- to communicate to the public those public recitations, by any means or
- process, including by wire or wireless means or public digital
- performances; to make available to the public Works in such a way that
- members of the public may access these Works from a place and at a
- place individually chosen by them; to perform the Work to the public
- by any means or process and the communication to the public of the
- performances of the Work, including by public digital performance; to
- broadcast and rebroadcast the Work by any means including signs,
- sounds or images.
- i. "Reproduce" means to make copies of the Work by any means including
- without limitation by sound or visual recordings and the right of
- fixation and reproducing fixations of the Work, including storage of a
- protected performance or phonogram in digital form or other electronic
- medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce,
-limit, or restrict any uses free from copyright or rights arising from
-limitations or exceptions that are provided for in connection with the
-copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License,
-Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
-perpetual (for the duration of the applicable copyright) license to
-exercise the rights in the Work as stated below:
-
- a. to Reproduce the Work, to incorporate the Work into one or more
- Collections, and to Reproduce the Work as incorporated in the
- Collections; and,
- b. to Distribute and Publicly Perform the Work including as incorporated
- in Collections.
-
-The above rights may be exercised in all media and formats whether now
-known or hereafter devised. The above rights include the right to make
-such modifications as are technically necessary to exercise the rights in
-other media and formats, but otherwise you have no rights to make
-Adaptations. Subject to 8(f), all rights not expressly granted by Licensor
-are hereby reserved, including but not limited to the rights set forth in
-Section 4(d).
-
-4. Restrictions. The license granted in Section 3 above is expressly made
-subject to and limited by the following restrictions:
-
- a. You may Distribute or Publicly Perform the Work only under the terms
- of this License. You must include a copy of, or the Uniform Resource
- Identifier (URI) for, this License with every copy of the Work You
- Distribute or Publicly Perform. You may not offer or impose any terms
- on the Work that restrict the terms of this License or the ability of
- the recipient of the Work to exercise the rights granted to that
- recipient under the terms of the License. You may not sublicense the
- Work. You must keep intact all notices that refer to this License and
- to the disclaimer of warranties with every copy of the Work You
- Distribute or Publicly Perform. When You Distribute or Publicly
- Perform the Work, You may not impose any effective technological
- measures on the Work that restrict the ability of a recipient of the
- Work from You to exercise the rights granted to that recipient under
- the terms of the License. This Section 4(a) applies to the Work as
- incorporated in a Collection, but this does not require the Collection
- apart from the Work itself to be made subject to the terms of this
- License. If You create a Collection, upon notice from any Licensor You
- must, to the extent practicable, remove from the Collection any credit
- as required by Section 4(c), as requested.
- b. You may not exercise any of the rights granted to You in Section 3
- above in any manner that is primarily intended for or directed toward
- commercial advantage or private monetary compensation. The exchange of
- the Work for other copyrighted works by means of digital file-sharing
- or otherwise shall not be considered to be intended for or directed
- toward commercial advantage or private monetary compensation, provided
- there is no payment of any monetary compensation in connection with
- the exchange of copyrighted works.
- c. If You Distribute, or Publicly Perform the Work or Collections, You
- must, unless a request has been made pursuant to Section 4(a), keep
- intact all copyright notices for the Work and provide, reasonable to
- the medium or means You are utilizing: (i) the name of the Original
- Author (or pseudonym, if applicable) if supplied, and/or if the
- Original Author and/or Licensor designate another party or parties
- (e.g., a sponsor institute, publishing entity, journal) for
- attribution ("Attribution Parties") in Licensor's copyright notice,
- terms of service or by other reasonable means, the name of such party
- or parties; (ii) the title of the Work if supplied; (iii) to the
- extent reasonably practicable, the URI, if any, that Licensor
- specifies to be associated with the Work, unless such URI does not
- refer to the copyright notice or licensing information for the Work.
- The credit required by this Section 4(c) may be implemented in any
- reasonable manner; provided, however, that in the case of a
- Collection, at a minimum such credit will appear, if a credit for all
- contributing authors of Collection appears, then as part of these
- credits and in a manner at least as prominent as the credits for the
- other contributing authors. For the avoidance of doubt, You may only
- use the credit required by this Section for the purpose of attribution
- in the manner set out above and, by exercising Your rights under this
- License, You may not implicitly or explicitly assert or imply any
- connection with, sponsorship or endorsement by the Original Author,
- Licensor and/or Attribution Parties, as appropriate, of You or Your
- use of the Work, without the separate, express prior written
- permission of the Original Author, Licensor and/or Attribution
- Parties.
- d. For the avoidance of doubt:
-
- i. Non-waivable Compulsory License Schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme cannot be waived, the Licensor
- reserves the exclusive right to collect such royalties for any
- exercise by You of the rights granted under this License;
- ii. Waivable Compulsory License Schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme can be waived, the Licensor reserves
- the exclusive right to collect such royalties for any exercise by
- You of the rights granted under this License if Your exercise of
- such rights is for a purpose or use which is otherwise than
- noncommercial as permitted under Section 4(b) and otherwise waives
- the right to collect royalties through any statutory or compulsory
- licensing scheme; and,
- iii. Voluntary License Schemes. The Licensor reserves the right to
- collect royalties, whether individually or, in the event that the
- Licensor is a member of a collecting society that administers
- voluntary licensing schemes, via that society, from any exercise
- by You of the rights granted under this License that is for a
- purpose or use which is otherwise than noncommercial as permitted
- under Section 4(b).
- e. Except as otherwise agreed in writing by the Licensor or as may be
- otherwise permitted by applicable law, if You Reproduce, Distribute or
- Publicly Perform the Work either by itself or as part of any
- Collections, You must not distort, mutilate, modify or take other
- derogatory action in relation to the Work which would be prejudicial
- to the Original Author's honor or reputation.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED BY THE PARTIES IN WRITING, LICENSOR
-OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
-KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
-INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
-FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
-LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
-WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
-OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
-LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
-ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
-ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
-BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate
- automatically upon any breach by You of the terms of this License.
- Individuals or entities who have received Collections from You under
- this License, however, will not have their licenses terminated
- provided such individuals or entities remain in full compliance with
- those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any
- termination of this License.
- b. Subject to the above terms and conditions, the license granted here is
- perpetual (for the duration of the applicable copyright in the Work).
- Notwithstanding the above, Licensor reserves the right to release the
- Work under different license terms or to stop distributing the Work at
- any time; provided, however that any such election will not serve to
- withdraw this License (or any other license that has been, or is
- required to be, granted under the terms of this License), and this
- License will continue in full force and effect unless terminated as
- stated above.
-
-8. Miscellaneous
-
- a. Each time You Distribute or Publicly Perform the Work or a Collection,
- the Licensor offers to the recipient a license to the Work on the same
- terms and conditions as the license granted to You under this License.
- b. If any provision of this License is invalid or unenforceable under
- applicable law, it shall not affect the validity or enforceability of
- the remainder of the terms of this License, and without further action
- by the parties to this agreement, such provision shall be reformed to
- the minimum extent necessary to make such provision valid and
- enforceable.
- c. No term or provision of this License shall be deemed waived and no
- breach consented to unless such waiver or consent shall be in writing
- and signed by the party to be charged with such waiver or consent.
- d. This License constitutes the entire agreement between the parties with
- respect to the Work licensed here. There are no understandings,
- agreements or representations with respect to the Work not specified
- here. Licensor shall not be bound by any additional provisions that
- may appear in any communication from You. This License may not be
- modified without the mutual written agreement of the Licensor and You.
- e. The rights granted under, and the subject matter referenced, in this
- License were drafted utilizing the terminology of the Berne Convention
- for the Protection of Literary and Artistic Works (as amended on
- September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
- Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
- and the Universal Copyright Convention (as revised on July 24, 1971).
- These rights and subject matter take effect in the relevant
- jurisdiction in which the License terms are sought to be enforced
- according to the corresponding provisions of the implementation of
- those treaty provisions in the applicable national law. If the
- standard suite of rights granted under applicable copyright law
- includes additional rights not granted under this License, such
- additional rights are deemed to be included in the License; this
- License is not intended to restrict the license of any rights under
- applicable law.
-
-
-Creative Commons Notice
-
- Creative Commons is not a party to this License, and makes no warranty
- whatsoever in connection with the Work. Creative Commons will not be
- liable to You or any party on any legal theory for any damages
- whatsoever, including without limitation any general, special,
- incidental or consequential damages arising in connection to this
- license. Notwithstanding the foregoing two (2) sentences, if Creative
- Commons has expressly identified itself as the Licensor hereunder, it
- shall have all rights and obligations of Licensor.
-
- Except for the limited purpose of indicating to the public that the
- Work is licensed under the CCPL, Creative Commons does not authorize
- the use by either party of the trademark "Creative Commons" or any
- related trademark or logo of Creative Commons without the prior
- written consent of Creative Commons. Any permitted use will be in
- compliance with Creative Commons' then-current trademark usage
- guidelines, as may be published on its website or otherwise made
- available upon request from time to time. For the avoidance of doubt,
- this trademark restriction does not form part of this License.
-
- Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-ND-3.0-DE b/options/license/CC-BY-NC-ND-3.0-DE
deleted file mode 100644
index 06d59d675a..0000000000
--- a/options/license/CC-BY-NC-ND-3.0-DE
+++ /dev/null
@@ -1,101 +0,0 @@
-Creative Commons Namensnennung - Keine kommerzielle Nutzung - Keine Bearbeitungen 3.0 Deutschland
-
- CREATIVE COMMONS IST KEINE RECHTSANWALTSKANZLEI UND LEISTET KEINE RECHTSBERATUNG. DIE BEREITSTELLUNG DIESER LIZENZ FÜHRT ZU KEINEM MANDATSVERHÄLTNIS. CREATIVE COMMONS STELLT DIESE INFORMATIONEN OHNE GEWÄHR ZUR VERFÜGUNG. CREATIVE COMMONS ÜBERNIMMT KEINE GEWÄHRLEISTUNG FÜR DIE GELIEFERTEN INFORMATIONEN UND SCHLIEßT DIE HAFTUNG FÜR SCHÄDEN AUS, DIE SICH AUS DEREN GEBRAUCH ERGEBEN.
-
-Lizenz
-
-DER GEGENSTAND DIESER LIZENZ (WIE UNTER "SCHUTZGEGENSTAND" DEFINIERT) WIRD UNTER DEN BEDINGUNGEN DIESER CREATIVE COMMONS PUBLIC LICENSE ("CCPL", "LIZENZ" ODER "LIZENZVERTRAG") ZUR VERFÜGUNG GESTELLT. DER SCHUTZGEGENSTAND IST DURCH DAS URHEBERRECHT UND/ODER ANDERE GESETZE GESCHÜTZT. JEDE FORM DER NUTZUNG DES SCHUTZGEGENSTANDES, DIE NICHT AUFGRUND DIESER LIZENZ ODER DURCH GESETZE GESTATTET IST, IST UNZULÄSSIG.
-
-DURCH DIE AUSÜBUNG EINES DURCH DIESE LIZENZ GEWÄHRTEN RECHTS AN DEM SCHUTZGEGENSTAND ERKLÄREN SIE SICH MIT DEN LIZENZBEDINGUNGEN RECHTSVERBINDLICH EINVERSTANDEN. SOWEIT DIESE LIZENZ ALS LIZENZVERTRAG ANZUSEHEN IST, GEWÄHRT IHNEN DER LIZENZGEBER DIE IN DER LIZENZ GENANNTEN RECHTE UNENTGELTLICH UND IM AUSTAUSCH DAFÜR, DASS SIE DAS GEBUNDENSEIN AN DIE LIZENZBEDINGUNGEN AKZEPTIEREN.
-
-1. Definitionen
-
- a. Der Begriff "Abwandlung" im Sinne dieser Lizenz bezeichnet das Ergebnis jeglicher Art von Veränderung des Schutzgegenstandes, solange die eigenpersönlichen Züge des Schutzgegenstandes darin nicht verblassen und daran eigene Schutzrechte entstehen. Das kann insbesondere eine Bearbeitung, Umgestaltung, Änderung, Anpassung, Übersetzung oder Heranziehung des Schutzgegenstandes zur Vertonung von Laufbildern sein. Nicht als Abwandlung des Schutzgegenstandes gelten seine Aufnahme in eine Sammlung oder ein Sammelwerk und die freie Benutzung des Schutzgegenstandes.
-
- b. Der Begriff "Sammelwerk" im Sinne dieser Lizenz meint eine Zusammenstellung von literarischen, künstlerischen oder wissenschaftlichen Inhalten, sofern diese Zusammenstellung aufgrund von Auswahl und Anordnung der darin enthaltenen selbständigen Elemente eine geistige Schöpfung darstellt, unabhängig davon, ob die Elemente systematisch oder methodisch angelegt und dadurch einzeln zugänglich sind oder nicht.
-
- c. "Verbreiten" im Sinne dieser Lizenz bedeutet, den Schutzgegenstand im Original oder in Form von Vervielfältigungsstücken, mithin in körperlich fixierter Form der Öffentlichkeit anzubieten oder in Verkehr zu bringen.
-
- d. Der "Lizenzgeber" im Sinne dieser Lizenz ist diejenige natürliche oder juristische Person oder Gruppe, die den Schutzgegenstand unter den Bedingungen dieser Lizenz anbietet und insoweit als Rechteinhaberin auftritt.
-
- e. "Rechteinhaber" im Sinne dieser Lizenz ist der Urheber des Schutzgegenstandes oder jede andere natürliche oder juristische Person oder Gruppe von Personen, die am Schutzgegenstand ein Immaterialgüterrecht erlangt hat, welches die in Abschnitt 3 genannten Handlungen erfasst und bei dem eine Einräumung von Nutzungsrechten oder eine Weiterübertragung an Dritte möglich ist.
-
- f. Der Begriff "Schutzgegenstand" bezeichnet in dieser Lizenz den literarischen, künstlerischen oder wissenschaftlichen Inhalt, der unter den Bedingungen dieser Lizenz angeboten wird. Das kann insbesondere eine persönliche geistige Schöpfung jeglicher Art, ein Werk der kleinen Münze, ein nachgelassenes Werk oder auch ein Lichtbild oder anderes Objekt eines verwandten Schutzrechts sein, unabhängig von der Art seiner Fixierung und unabhängig davon, auf welche Weise jeweils eine Wahrnehmung erfolgen kann, gleichviel ob in analoger oder digitaler Form. Soweit Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen, unterfallen auch sie dem Begriff "Schutzgegenstand" im Sinne dieser Lizenz.
-
- g. Mit "Sie" bzw. "Ihnen" ist die natürliche oder juristische Person gemeint, die in dieser Lizenz im Abschnitt 3 genannte Nutzungen des Schutzgegenstandes vornimmt und zuvor in Hinblick auf den Schutzgegenstand nicht gegen Bedingungen dieser Lizenz verstoßen oder aber die ausdrückliche Erlaubnis des Lizenzgebers erhalten hat, die durch diese Lizenz gewährten Nutzungsrechte trotz eines vorherigen Verstoßes auszuüben.
-
- h. Unter "Öffentlich Zeigen" im Sinne dieser Lizenz sind Veröffentlichungen und Präsentationen des Schutzgegenstandes zu verstehen, die für eine Mehrzahl von Mitgliedern der Öffentlichkeit bestimmt sind und in unkörperlicher Form mittels öffentlicher Wiedergabe in Form von Vortrag, Aufführung, Vorführung, Darbietung, Sendung, Weitersendung, zeit- und ortsunabhängiger Zugänglichmachung oder in körperlicher Form mittels Ausstellung erfolgen, unabhängig von bestimmten Veranstaltungen und unabhängig von den zum Einsatz kommenden Techniken und Verfahren, einschließlich drahtgebundener oder drahtloser Mittel und Einstellen in das Internet.
-
- i. "Vervielfältigen" im Sinne dieser Lizenz bedeutet, mittels beliebiger Verfahren Vervielfältigungsstücke des Schutzgegenstandes herzustellen, insbesondere durch Ton- oder Bildaufzeichnungen, und umfasst auch den Vorgang, erstmals körperliche Fixierungen des Schutzgegenstandes sowie Vervielfältigungsstücke dieser Fixierungen anzufertigen, sowie die Übertragung des Schutzgegenstandes auf einen Bild- oder Tonträger oder auf ein anderes elektronisches Medium, gleichviel ob in digitaler oder analoger Form.
-
-2. Schranken des Immaterialgüterrechts. Diese Lizenz ist in keiner Weise darauf gerichtet, Befugnisse zur Nutzung des Schutzgegenstandes zu vermindern, zu beschränken oder zu vereiteln, die Ihnen aufgrund der Schranken des Urheberrechts oder anderer Rechtsnormen bereits ohne Weiteres zustehen oder sich aus dem Fehlen eines immaterialgüterrechtlichen Schutzes ergeben.
-
-3. Einräumung von Nutzungsrechten. Unter den Bedingungen dieser Lizenz räumt Ihnen der Lizenzgeber - unbeschadet unverzichtbarer Rechte und vorbehaltlich des Abschnitts 4.e) - das vergütungsfreie, räumlich und zeitlich (für die Dauer des Schutzrechts am Schutzgegenstand) unbeschränkte einfache Recht ein, den Schutzgegenstand auf die folgenden Arten und Weisen zu nutzen ("unentgeltlich eingeräumtes einfaches Nutzungsrecht für jedermann"):
-
- a. den Schutzgegenstand in beliebiger Form und Menge zu vervielfältigen, ihn in Sammelwerke zu integrieren und ihn als Teil solcher Sammelwerke zu vervielfältigen;
-
- b. den Schutzgegenstand, allein oder in Sammelwerke aufgenommen, öffentlich zu zeigen und zu verbreiten.
-
-Das vorgenannte Nutzungsrecht wird für alle bekannten sowie für alle noch nicht bekannten Nutzungsarten eingeräumt. Es beinhaltet auch das Recht, solche Änderungen am Schutzgegenstand vorzunehmen, die für bestimmte nach dieser Lizenz zulässige Nutzungen technisch erforderlich sind. Weitergehende Änderungen oder Abwandlungen sind jedoch untersagt. Alle sonstigen Rechte, die über diesen Abschnitt hinaus nicht ausdrücklich durch den Lizenzgeber eingeräumt werden, bleiben diesem allein vorbehalten. Soweit Datenbanken oder Zusammenstellungen von Daten Schutzgegenstand dieser Lizenz oder Teil dessen sind und einen immaterialgüterrechtlichen Schutz eigener Art genießen, verzichtet der Lizenzgeber auf sämtliche aus diesem Schutz resultierenden Rechte.
-
-4. Bedingungen. Die Einräumung des Nutzungsrechts gemäß Abschnitt 3 dieser Lizenz erfolgt ausdrücklich nur unter den folgenden Bedingungen:
-
- a. Sie dürfen den Schutzgegenstand ausschließlich unter den Bedingungen dieser Lizenz verbreiten oder öffentlich zeigen. Sie müssen dabei stets eine Kopie dieser Lizenz oder deren vollständige Internetadresse in Form des Uniform-Resource-Identifier (URI) beifügen. Sie dürfen keine Vertrags- oder Nutzungsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch diese Lizenz gewährten Rechte beschränken. Sie dürfen den Schutzgegenstand nicht unterlizenzieren. Bei jeder Kopie des Schutzgegenstandes, die Sie verbreiten oder öffentlich zeigen, müssen Sie alle Hinweise unverändert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Wenn Sie den Schutzgegenstand verbreiten oder öffentlich zeigen, dürfen Sie (in Bezug auf den Schutzgegenstand) keine technischen Maßnahmen ergreifen, die den Nutzer des Schutzgegenstandes in der Ausübung der ihm durch diese Lizenz gewährten Rechte behindern können. Dieser Abschnitt 4.a) gilt auch für den Fall, dass der Schutzgegenstand einen Bestandteil eines Sammelwerkes bildet, was jedoch nicht bedeutet, dass das Sammelwerk insgesamt dieser Lizenz unterstellt werden muss. Sofern Sie ein Sammelwerk erstellen, müssen Sie auf die Mitteilung eines Lizenzgebers hin aus dem Sammelwerk die in Abschnitt 4.c) aufgezählten Hinweise entfernen.
-
- b. Die Rechteeinräumung gemäß Abschnitt 3 gilt nur für Handlungen, die nicht vorrangig auf einen geschäftlichen Vorteil oder eine geldwerte Vergütung gerichtet sind ("nicht-kommerzielle Nutzung", "Non-commercial-Option"). Wird Ihnen in Zusammenhang mit dem Schutzgegenstand dieser Lizenz ein anderer Schutzgegenstand überlassen, ohne dass eine vertragliche Verpflichtung hierzu besteht (etwa im Wege von File-Sharing), so wird dies nicht als auf geschäftlichen Vorteil oder geldwerte Vergütung gerichtet angesehen, wenn in Verbindung mit dem Austausch der Schutzgegenstände tatsächlich keine Zahlung oder geldwerte Vergütung geleistet wird.
-
- c. Die Verbreitung und das öffentliche Zeigen des Schutzgegenstandes oder ihn enthaltender Sammelwerke ist Ihnen nur unter der Bedingung gestattet, dass Sie, vorbehaltlich etwaiger Mitteilungen im Sinne von Abschnitt 4.a), alle dazu gehörenden Rechtevermerke unberührt lassen. Sie sind verpflichtet, die Rechteinhaberschaft in einer der Nutzung entsprechenden, angemessenen Form anzuerkennen, indem Sie - soweit bekannt - Folgendes angeben:
-
- i. Den Namen (oder das Pseudonym, falls ein solches verwendet wird) des Rechteinhabers und / oder, falls der Lizenzgeber im Rechtevermerk, in den Nutzungsbedingungen oder auf andere angemessene Weise eine Zuschreibung an Dritte vorgenommen hat (z.B. an eine Stiftung, ein Verlagshaus oder eine Zeitung) ("Zuschreibungsempfänger"), Namen bzw. Bezeichnung dieses oder dieser Dritten;
-
- ii. den Titel des Inhaltes;
-
- iii. in einer praktikablen Form den Uniform-Resource-Identifier (URI, z.B. Internetadresse), den der Lizenzgeber zum Schutzgegenstand angegeben hat, es sei denn, dieser URI verweist nicht auf den Rechtevermerk oder die Lizenzinformationen zum Schutzgegenstand.
-
- Die nach diesem Abschnitt 4.c) erforderlichen Angaben können in jeder angemessenen Form gemacht werden; im Falle eines Sammelwerkes müssen diese Angaben das Minimum darstellen und bei gemeinsamer Nennung mehrerer Rechteinhaber dergestalt erfolgen, dass sie zumindest ebenso hervorgehoben sind wie die Hinweise auf die übrigen Rechteinhaber. Die Angaben nach diesem Abschnitt dürfen Sie ausschließlich zur Angabe der Rechteinhaberschaft in der oben bezeichneten Weise verwenden. Durch die Ausübung Ihrer Rechte aus dieser Lizenz dürfen Sie ohne eine vorherige, separat und schriftlich vorliegende Zustimmung des Lizenzgebers und / oder des Zuschreibungsempfängers weder explizit noch implizit irgendeine Verbindung zum Lizenzgeber oder Zuschreibungsempfänger und ebenso wenig eine Unterstützung oder Billigung durch ihn andeuten.
-
- d. Die oben unter 4.a) bis c) genannten Einschränkungen gelten nicht für solche Teile des Schutzgegenstandes, die allein deshalb unter den Schutzgegenstandsbegriff fallen, weil sie als Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen.
-
- e. Bezüglich Vergütung für die Nutzung des Schutzgegenstandes gilt Folgendes:
-
- i. Unverzichtbare gesetzliche Vergütungsansprüche: Soweit unverzichtbare Vergütungsansprüche im Gegenzug für gesetzliche Lizenzen vorgesehen oder Pauschalabgabensysteme (zum Beispiel für Leermedien) vorhanden sind, behält sich der Lizenzgeber das ausschließliche Recht vor, die entsprechende Vergütung einzuziehen für jede Ausübung eines Rechts aus dieser Lizenz durch Sie.
-
- ii. Vergütung bei Zwangslizenzen: Sofern Zwangslizenzen außerhalb dieser Lizenz vorgesehen sind und zustande kommen, behält sich der Lizenzgeber das ausschließliche Recht auf Einziehung der entsprechenden Vergütung für den Fall vor, dass Sie eine Nutzung des Schutzgegenstandes für andere als die in Abschnitt 4.b) als nicht-kommerziell definierten Zwecke vornehmen, verzichtet für alle übrigen, lizenzgerechten Fälle von Nutzung jedoch auf jegliche Vergütung.
-
- iii. Vergütung in sonstigen Fällen: Bezüglich lizenzgerechter Nutzung des Schutzgegenstandes durch Sie, die nicht unter die beiden vorherigen Abschnitte (i) und (ii) fällt, verzichtet der Lizenzgeber auf jegliche Vergütung, unabhängig davon, ob eine Einziehung der Vergütung durch ihn selbst oder nur durch eine Verwertungsgesellschaft möglich wäre. Der Lizenzgeber behält sich jedoch das ausschließliche Recht auf Einziehung der entsprechenden Vergütung (durch ihn selbst oder eine Verwertungsgesellschaft) für den Fall vor, dass Sie eine Nutzung des Schutzgegenstandes für andere als die in Abschnitt 4.b) als nicht-kommerziell definierten Zwecke vornehmen.
-
- f. Persönlichkeitsrechte bleiben - soweit sie bestehen - von dieser Lizenz unberührt.
-
-5. Gewährleistung
-
-SOFERN KEINE ANDERS LAUTENDE, SCHRIFTLICHE VEREINBARUNG ZWISCHEN DEM LIZENZGEBER UND IHNEN GESCHLOSSEN WURDE UND SOWEIT MÄNGEL NICHT ARGLISTIG VERSCHWIEGEN WURDEN, BIETET DER LIZENZGEBER DEN SCHUTZGEGENSTAND UND DIE EINRÄUMUNG VON RECHTEN UNTER AUSSCHLUSS JEGLICHER GEWÄHRLEISTUNG AN UND ÜBERNIMMT WEDER AUSDRÜCKLICH NOCH KONKLUDENT GARANTIEN IRGENDEINER ART. DIES UMFASST INSBESONDERE DAS FREISEIN VON SACH- UND RECHTSMÄNGELN, UNABHÄNGIG VON DEREN ERKENNBARKEIT FÜR DEN LIZENZGEBER, DIE VERKEHRSFÄHIGKEIT DES SCHUTZGEGENSTANDES, SEINE VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK SOWIE DIE KORREKTHEIT VON BESCHREIBUNGEN. DIESE GEWÄHRLEISTUNGSBESCHRÄNKUNG GILT NICHT, SOWEIT MÄNGEL ZU SCHÄDEN DER IN ABSCHNITT 6 BEZEICHNETEN ART FÜHREN UND AUF SEITEN DES LIZENZGEBERS DAS JEWEILS GENANNTE VERSCHULDEN BZW. VERTRETENMÜSSEN EBENFALLS VORLIEGT.
-
-6. Haftungsbeschränkung
-
-DER LIZENZGEBER HAFTET IHNEN GEGENÜBER IN BEZUG AUF SCHÄDEN AUS DER VERLETZUNG DES LEBENS, DES KÖRPERS ODER DER GESUNDHEIT NUR, SOFERN IHM WENIGSTENS FAHRLÄSSIGKEIT VORZUWERFEN IST, FÜR SONSTIGE SCHÄDEN NUR BEI GROBER FAHRLÄSSIGKEIT ODER VORSATZ, UND ÜBERNIMMT DARÜBER HINAUS KEINERLEI FREIWILLIGE HAFTUNG.
-
-7. Erlöschen
-
- a. Diese Lizenz und die durch sie eingeräumten Nutzungsrechte erlöschen mit Wirkung für die Zukunft im Falle eines Verstoßes gegen die Lizenzbedingungen durch Sie, ohne dass es dazu der Kenntnis des Lizenzgebers vom Verstoß oder einer weiteren Handlung einer der Vertragsparteien bedarf. Mit natürlichen oder juristischen Personen, die den Schutzgegenstand enthaltende Sammelwerke unter den Bedingungen dieser Lizenz von Ihnen erhalten haben, bestehen nachträglich entstandene Lizenzbeziehungen jedoch solange weiter, wie die genannten Personen sich ihrerseits an sämtliche Lizenzbedingungen halten. Darüber hinaus gelten die Ziffern 1, 2, 5, 6, 7, und 8 auch nach einem Erlöschen dieser Lizenz fort.
-
- b. Vorbehaltlich der oben genannten Bedingungen gilt diese Lizenz unbefristet bis der rechtliche Schutz für den Schutzgegenstand ausläuft. Davon abgesehen behält der Lizenzgeber das Recht, den Schutzgegenstand unter anderen Lizenzbedingungen anzubieten oder die eigene Weitergabe des Schutzgegenstandes jederzeit einzustellen, solange die Ausübung dieses Rechts nicht einer Kündigung oder einem Widerruf dieser Lizenz (oder irgendeiner Weiterlizenzierung, die auf Grundlage dieser Lizenz bereits erfolgt ist bzw. zukünftig noch erfolgen muss) dient und diese Lizenz unter Berücksichtigung der oben zum Erlöschen genannten Bedingungen vollumfänglich wirksam bleibt.
-
-8. Sonstige Bestimmungen
-
- a. Jedes Mal wenn Sie den Schutzgegenstand für sich genommen oder als Teil eines Sammelwerkes verbreiten oder öffentlich zeigen, bietet der Lizenzgeber dem Empfänger eine Lizenz zu den gleichen Bedingungen und im gleichen Umfang an, wie Ihnen in Form dieser Lizenz.
-
- b. Sollte eine Bestimmung dieser Lizenz unwirksam sein, so bleibt davon die Wirksamkeit der Lizenz im Übrigen unberührt.
-
- c. Keine Bestimmung dieser Lizenz soll als abbedungen und kein Verstoß gegen sie als zulässig gelten, solange die von dem Verzicht oder von dem Verstoß betroffene Seite nicht schriftlich zugestimmt hat.
-
- d. Diese Lizenz (zusammen mit in ihr ausdrücklich vorgesehenen Erlaubnissen, Mitteilungen und Zustimmungen, soweit diese tatsächlich vorliegen) stellt die vollständige Vereinbarung zwischen dem Lizenzgeber und Ihnen in Bezug auf den Schutzgegenstand dar. Es bestehen keine Abreden, Vereinbarungen oder Erklärungen in Bezug auf den Schutzgegenstand, die in dieser Lizenz nicht genannt sind. Rechtsgeschäftliche Änderungen des Verhältnisses zwischen dem Lizenzgeber und Ihnen sind nur über Modifikationen dieser Lizenz möglich. Der Lizenzgeber ist an etwaige zusätzliche, einseitig durch Sie übermittelte Bestimmungen nicht gebunden. Diese Lizenz kann nur durch schriftliche Vereinbarung zwischen Ihnen und dem Lizenzgeber modifiziert werden. Derlei Modifikationen wirken ausschließlich zwischen dem Lizenzgeber und Ihnen und wirken sich nicht auf die Dritten gemäß Ziffern 8.a) angeboteten Lizenzen aus.
-
- e. Sofern zwischen Ihnen und dem Lizenzgeber keine anderweitige Vereinbarung getroffen wurde und soweit Wahlfreiheit besteht, findet auf diesen Lizenzvertrag das Recht der Bundesrepublik Deutschland Anwendung.
-
-Creative Commons Notice
-
-Creative Commons ist nicht Partei dieser Lizenz und übernimmt keinerlei Gewähr oder dergleichen in Bezug auf den Schutzgegenstand. Creative Commons haftet Ihnen oder einer anderen Partei unter keinem rechtlichen Gesichtspunkt für irgendwelche Schäden, die - abstrakt oder konkret, zufällig oder vorhersehbar - im Zusammenhang mit dieser Lizenz entstehen. Unbeschadet der vorangegangen beiden Sätze, hat Creative Commons alle Rechte und Pflichten eines Lizenzgebers, wenn es sich ausdrücklich als Lizenzgeber im Sinne dieser Lizenz bezeichnet.
-
-Creative Commons gewährt den Parteien nur insoweit das Recht, das Logo und die Marke "Creative Commons" zu nutzen, als dies notwendig ist, um der Öffentlichkeit gegenüber kenntlich zu machen, dass der Schutzgegenstand unter einer CCPL steht. Ein darüber hinaus gehender Gebrauch der Marke "Creative Commons" oder einer verwandten Marke oder eines verwandten Logos bedarf der vorherigen schriftlichen Zustimmung von Creative Commons. Jeder erlaubte Gebrauch richtet sich nach der Creative Commons Marken-Nutzungs-Richtlinie in der jeweils aktuellen Fassung, die von Zeit zu Zeit auf der Website veröffentlicht oder auf andere Weise auf Anfrage zugänglich gemacht wird. Zur Klarstellung: Die genannten Einschränkungen der Markennutzung sind nicht Bestandteil dieser Lizenz.
-
-Creative Commons kann kontaktiert werden über https://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-ND-3.0-IGO b/options/license/CC-BY-NC-ND-3.0-IGO
deleted file mode 100644
index c5b3226c18..0000000000
--- a/options/license/CC-BY-NC-ND-3.0-IGO
+++ /dev/null
@@ -1,99 +0,0 @@
-Attribution-NonCommercial-NoDerivs 3.0 IGO
-
-CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. THE LICENSOR IS NOT NECESSARILY AN INTERGOVERNMENTAL ORGANIZATION (IGO), AS DEFINED IN THE LICENSE BELOW.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("LICENSE"). THE LICENSOR (DEFINED BELOW) HOLDS COPYRIGHT AND OTHER RIGHTS IN THE WORK. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION FOR YOUR ACCEPTANCE AND AGREEMENT TO THE TERMS OF THE LICENSE.
-
-1. Definitions
-
- a. "IGO" means, solely and exclusively for purposes of this License, an organization established by a treaty or other instrument governed by international law and possessing its own international legal personality. Other organizations established to carry out activities across national borders and that accordingly enjoy immunity from legal process are also IGOs for the sole and exclusive purposes of this License. IGOs may include as members, in addition to states, other entities.
-
- b. "Work" means the literary and/or artistic work eligible for copyright protection, whatever may be the mode or form of its expression including digital form, and offered under the terms of this License. It is understood that a database, which by reason of the selection and arrangement of its contents constitutes an intellectual creation, is considered a Work.
-
- c. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License and may be, but is not necessarily, an IGO.
-
- d. "You" means an individual or entity exercising rights under this License.
-
- e. "Reproduce" means to make a copy of the Work in any manner or form, and by any means.
-
- f. "Distribute" means the activity of making publicly available the Work (or copies of the Work), as applicable, by sale, rental, public lending or any other known form of transfer of ownership or possession of the Work or copy of the Work.
-
- g. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
-
- h. "Adaptation" means a work derived from or based upon the Work, or upon the Work and other pre-existing works. Adaptations may include works such as translations, derivative works, or any alterations and arrangements of any kind involving the Work. For purposes of this License, where the Work is a musical work, performance, or phonogram, the synchronization of the Work in timed-relation with a moving image is an Adaptation. For the avoidance of doubt, including the Work in a Collection is not an Adaptation.
-
- i. "Collection" means a collection of literary or artistic works or other works or subject matter other than works listed in Section 1(b) which by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. For the avoidance of doubt, a Collection will not be considered as an Adaptation.
-
-2. Scope of this License. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright protection.
-
-3. License Grant. Subject to the terms and conditions of this License, the Licensor hereby grants You a worldwide, royalty-free, non-exclusive license to exercise the rights in the Work as follows:
-
- a. to Reproduce, Distribute and Publicly Perform the Work, to incorporate the Work into one or more Collections, and to Reproduce, Distribute and Publicly Perform the Work as incorporated in the Collections.
-
-This License lasts for the duration of the term of the copyright in the Work licensed by the Licensor. The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Adaptations. All rights not expressly granted by the Licensor are hereby reserved, including but not limited to the rights set forth in Section 4(d).
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work (see section 8(a)). You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from a Licensor You must, to the extent practicable, remove from the Collection any credit (inclusive of any logo, trademark, official mark or official emblem) as required by Section 4(c), as requested.
-
- b. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be primarily intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
- c. If You Distribute, or Publicly Perform the Work or any Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) any attributions that the Licensor indicates be associated with the Work as indicated in a copyright notice, (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that the Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Collection, at a minimum such credit will appear, if a credit for all contributors to the Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Licensor or others designated for attribution, of You or Your use of the Work, without the separate, express prior written permission of the Licensor or such others.
-
- d. For the avoidance of doubt:
-
- i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-
- ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and,
-
- iii. Voluntary License Schemes. To the extent possible, the Licensor waives the right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary licensing scheme. In all other cases the Licensor expressly reserves the right to collect such royalties.
-
- e. Except as otherwise agreed in writing by the Licensor, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the honor or reputation of the Licensor where moral rights apply.
-
-5. Representations, Warranties and Disclaimer
-
-THE LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE.
-
-6. Limitation on Liability
-
-IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. Subject to the terms and conditions set forth in this License, the license granted here lasts for the duration of the term of the copyright in the Work licensed by the Licensor as stated in Section 3. Notwithstanding the above, the Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated below.
-
- b. If You fail to comply with this License, then this License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. Notwithstanding the foregoing, this License reinstates automatically as of the date the violation is cured, provided it is cured within 30 days of You discovering the violation, or upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 7(b) does not affect any rights the Licensor may have to seek remedies for violations of this License by You.
-
-8. Miscellaneous
-
- a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. If any provision of this License is invalid or unenforceable, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- c. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the Licensor.
-
- d. This License constitutes the entire agreement between You and the Licensor with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. The Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
- e. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). Interpretation of the scope of the rights granted by the Licensor and the conditions imposed on You under this License, this License, and the rights and conditions set forth herein shall be made with reference to copyright as determined in accordance with general principles of international law, including the above mentioned conventions.
-
- f. Nothing in this License constitutes or may be interpreted as a limitation upon or waiver of any privileges and immunities that may apply to the Licensor or You, including immunity from the legal processes of any jurisdiction, national court or other authority.
-
- g. Where the Licensor is an IGO, any and all disputes arising under this License that cannot be settled amicably shall be resolved in accordance with the following procedure:
-
- i. Pursuant to a notice of mediation communicated by reasonable means by either You or the Licensor to the other, the dispute shall be submitted to non-binding mediation conducted in accordance with rules designated by the Licensor in the copyright notice published with the Work, or if none then in accordance with those communicated in the notice of mediation. The language used in the mediation proceedings shall be English unless otherwise agreed.
-
- ii. If any such dispute has not been settled within 45 days following the date on which the notice of mediation is provided, either You or the Licensor may, pursuant to a notice of arbitration communicated by reasonable means to the other, elect to have the dispute referred to and finally determined by arbitration. The arbitration shall be conducted in accordance with the rules designated by the Licensor in the copyright notice published with the Work, or if none then in accordance with the UNCITRAL Arbitration Rules as then in force. The arbitral tribunal shall consist of a sole arbitrator and the language of the proceedings shall be English unless otherwise agreed. The place of arbitration shall be where the Licensor has its headquarters. The arbitral proceedings shall be conducted remotely (e.g., via telephone conference or written submissions) whenever practicable.
-
- iii. Interpretation of this License in any dispute submitted to mediation or arbitration shall be as set forth in Section 8(e), above.
-
-Creative Commons Notice
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of the Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
-
-Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-ND-4.0 b/options/license/CC-BY-NC-ND-4.0
deleted file mode 100644
index 6f2a684c1a..0000000000
--- a/options/license/CC-BY-NC-ND-4.0
+++ /dev/null
@@ -1,155 +0,0 @@
-Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International
-
- Creative Commons Corporation (“Creative Commonsâ€) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is†basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible.
-
-Using Creative Commons Public Licenses
-
-Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses.
-
-Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors.
-
-Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public.
-
-Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License
-
-By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
-
-Section 1 – Definitions.
-
- a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
-
- b. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
-
- c. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
-
- d. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
-
- e. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
-
- f. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
-
- g. Licensor means the individual(s) or entity(ies) granting rights under this Public License.
-
- h. NonCommercial means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.
-
- i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
-
- j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
-
- k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
-
-Section 2 – Scope.
-
- a. License grant.
-
- 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
-
- A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and
-
- B. produce and reproduce, but not Share, Adapted Material for NonCommercial purposes only.
-
- 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
-
- 3. Term. The term of this Public License is specified in Section 6(a).
-
- 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
-
- 5. Downstream recipients.
- A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
-
- B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
-
- 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
-
- b. Other rights.
-
- 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
-
- 2. Patent and trademark rights are not licensed under this Public License.
-
- 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.
-
-Section 3 – License Conditions.
-
-Your exercise of the Licensed Rights is expressly made subject to the following conditions.
-
- a. Attribution.
-
- 1. If You Share the Licensed Material, You must:
-
- A. retain the following if it is supplied by the Licensor with the Licensed Material:
-
- i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
-
- ii. a copyright notice;
-
- iii. a notice that refers to this Public License;
-
- iv. a notice that refers to the disclaimer of warranties;
-
- v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
-
- B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
-
- C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
-
- For the avoidance of doubt, You do not have permission under this Public License to Share Adapted Material.
-
- 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
-
- 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
-
-Section 4 – Sui Generis Database Rights.
-
-Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
-
- a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only and provided You do not Share Adapted Material;
-
- b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and
-
- c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
-For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
-
-Section 5 – Disclaimer of Warranties and Limitation of Liability.
-
- a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
-
- b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
-
- c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
-
-Section 6 – Term and Termination.
-
- a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
-
- b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
-
- 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
-
- 2. upon express reinstatement by the Licensor.
-
- For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
-
- c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
-
- d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
-
-Section 7 – Other Terms and Conditions.
-
- a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
-
- b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
-
-Section 8 – Interpretation.
-
- a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
-
- b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
-
- c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
-
- d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
-
-Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.†Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons†or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.
-
-Creative Commons may be contacted at creativecommons.org.
diff --git a/options/license/CC-BY-NC-SA-1.0 b/options/license/CC-BY-NC-SA-1.0
deleted file mode 100644
index 0722e12319..0000000000
--- a/options/license/CC-BY-NC-SA-1.0
+++ /dev/null
@@ -1,83 +0,0 @@
-Creative Commons Attribution-NonCommercial-ShareAlike 1.0
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DRAFT LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to create and reproduce Derivative Works;
-
- c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
- d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works;
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any reference to such Licensor or the Original Author, as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any reference to such Licensor or the Original Author, as requested.
-
- b. You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
-
- c. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
- d. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and give the Original Author credit reasonable to the medium or means You are utilizing by conveying the name (or pseudonym if applicable) of the Original Author if supplied; the title of the Work if supplied; in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
-5. Representations, Warranties and Disclaimer
-
- a. By offering the Work for public release under this License, Licensor represents and warrants that, to the best of Licensor's knowledge after reasonable inquiry:
-
- i. Licensor has secured all rights in the Work necessary to grant the license rights hereunder and to permit the lawful exercise of the rights granted hereunder without You having any obligation to pay any royalties, compulsory license fees, residuals or any other payments;
-
- ii. The Work does not infringe the copyright, trademark, publicity rights, common law rights or any other right of any third party or constitute defamation, invasion of privacy or other tortious injury to any third party.
-
- b. EXCEPT AS EXPRESSLY STATED IN THIS LICENSE OR OTHERWISE AGREED IN WRITING OR REQUIRED BY APPLICABLE LAW, THE WORK IS LICENSED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES REGARDING THE CONTENTS OR ACCURACY OF THE WORK.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, AND EXCEPT FOR DAMAGES ARISING FROM LIABILITY TO A THIRD PARTY RESULTING FROM BREACH OF THE WARRANTIES IN SECTION 5, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-SA-2.0 b/options/license/CC-BY-NC-SA-2.0
deleted file mode 100644
index bb8d9012f3..0000000000
--- a/options/license/CC-BY-NC-SA-2.0
+++ /dev/null
@@ -1,87 +0,0 @@
-Creative Commons Attribution-NonCommercial-ShareAlike 2.0
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
- g. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, Noncommercial, ShareAlike.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to create and reproduce Derivative Works;
-
- c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
- d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works;
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Sections 4(e) and 4(f).
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any reference to such Licensor or the Original Author, as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any reference to such Licensor or the Original Author, as requested.
-
- b. You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-NonCommercial-ShareAlike 2.0 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
-
- c. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
- d. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and give the Original Author credit reasonable to the medium or means You are utilizing by conveying the name (or pseudonym if applicable) of the Original Author if supplied; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
- e. For the avoidance of doubt, where the Work is a musical composition:
-
- i. Performance Royalties Under Blanket Licenses. Licensor reserves the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work if that performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
- ii. Mechanical Rights and Statutory Royalties. Licensor reserves the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions), if Your distribution of such cover version is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
- f. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor reserves the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions), if Your public digital performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-SA-2.0-DE b/options/license/CC-BY-NC-SA-2.0-DE
deleted file mode 100644
index ba4b72ac30..0000000000
--- a/options/license/CC-BY-NC-SA-2.0-DE
+++ /dev/null
@@ -1,85 +0,0 @@
-Creative Commons Namensnennung — Nicht-kommerziell — Weitergabe unter gleichen Bedingungen 2.0
-
-CREATIVE COMMONS IST KEINE RECHTSANWALTSGESELLSCHAFT UND LEISTET KEINE RECHTSBERATUNG. DIE WEITERGABE DIESES LIZENZENTWURFES FÜHRT ZU KEINEM MANDATSVERHÄLTNIS. CREATIVE COMMONS ERBRINGT DIESE INFORMATIONEN OHNE GEWÄHR. CREATIVE COMMONS ÜBERNIMMT KEINE GEWÄHRLEISTUNG FÜR DIE GELIEFERTEN INFORMATIONEN UND SCHLIEßT DIE HAFTUNG FÜR SCHÄDEN AUS, DIE SICH AUS IHREM GEBRAUCH ERGEBEN.
-
-Lizenzvertrag
-
-DAS URHEBERRECHTLICH GESCHÜTZTE WERK ODER DER SONSTIGE SCHUTZGEGENSTAND (WIE UNTEN BESCHRIEBEN) WIRD UNTER DEN BEDINGUNGEN DIESER CREATIVE COMMONS PUBLIC LICENSE („CCPL“ ODER „LIZENZVERTRAG“) ZUR VERFÜGUNG GESTELLT. DER SCHUTZGEGENSTAND IST DURCH DAS URHEBERRECHT UND/ODER EINSCHLÄGIGE GESETZE GESCHÜTZT.
-
-DURCH DIE AUSÜBUNG EINES DURCH DIESEN LIZENZVERTRAG GEWÄHRTEN RECHTS AN DEM SCHUTZGEGENSTAND ERKLÄREN SIE SICH MIT DEN LIZENZBEDINGUNGEN RECHTSVERBINDLICH EINVERSTANDEN. DER LIZENZGEBER RÄUMT IHNEN DIE HIER BESCHRIEBENEN RECHTE UNTER DER VORAUSSETZUNGEIN, DASS SIE SICH MIT DIESEN VERTRAGSBEDINGUNGEN EINVERSTANDEN ERKLÄREN.
-
-1. Definitionen
-
- a. Unter einer „Bearbeitung“ wird eine Übersetzung oder andere Bearbeitung des Werkes verstanden, die Ihre persönliche geistige Schöpfung ist. Eine freie Benutzung des Werkes wird nicht als Bearbeitung angesehen.
-
- b. Unter den „Lizenzelementen“ werden die folgenden Lizenzcharakteristika verstanden, die vom Lizenzgeber ausgewählt und in der Bezeichnung der Lizenz genannt werden: „Namensnennung“, „Nicht-kommerziell“, „Weitergabe unter gleichen Bedingungen“.
-
- c. Unter dem „Lizenzgeber“ wird die natürliche oder juristische Person verstanden, die den Schutzgegenstand unter den Bedingungen dieser Lizenz anbietet.
-
- d. Unter einem „Sammelwerk“ wird eine Sammlung von Werken, Daten oder anderen unabhängigen Elementen verstanden, die aufgrund der Auswahl oder Anordnung der Elemente eine persönliche geistige Schöpfung ist. Darunter fallen auch solche Sammelwerke, deren Elemente systematisch oder methodisch angeordnet und einzeln mit Hilfe elektronischer Mittel oder auf andere Weise zugänglich sind (Datenbankwerke). Ein Sammelwerk wird im Zusammenhang mit dieser Lizenz nicht als Bearbeitung (wie oben beschrieben) angesehen.
-
- e. Mit „SIE“ und „Ihnen“ ist die natürliche oder juristische Person gemeint, die die durch diese Lizenz gewährten Nutzungsrechte ausübt und die zuvor die Bedingungen dieser Lizenz im Hinblick auf das Werk nicht verletzt hat, oder die die ausdrückliche Erlaubnis des Lizenzgebers erhalten hat, die durch diese Lizenz gewährten Nutzungsrechte trotz einer vorherigen Verletzung auszuüben.
-
- f. Unter dem „Schutzgegenstand“wird das Werk oder Sammelwerk oder das Schutzobjekt eines verwandten Schutzrechts, das Ihnen unter den Bedingungen dieser Lizenz angeboten wird, verstanden
-
- g. Unter dem „Urheber“ wird die natürliche Person verstanden, die das Werk geschaffen hat.
-
- h. Unter einem „verwandten Schutzrecht“ wird das Recht an einem anderen urheberrechtlichen Schutzgegenstand als einem Werk verstanden, zum Beispiel einer wissenschaftlichen Ausgabe, einem nachgelassenen Werk, einem Lichtbild, einer Datenbank, einem Tonträger, einer Funksendung, einem Laufbild oder einer Darbietung eines ausübenden Künstlers.
-
- i. Unter dem „Werk“ wird eine persönliche geistige Schöpfung verstanden, die Ihnen unter den Bedingungen dieser Lizenz angeboten wird.
-
-2. Schranken des Urheberrechts. Diese Lizenz lässt sämtliche Befugnisse unberührt, die sich aus den Schranken des Urheberrechts,aus dem Erschöpfungsgrundsatz oder anderen Beschränkungen der Ausschließlichkeitsrechte des Rechtsinhabers ergeben.
-
-3. Lizenzierung. Unter den Bedingungen dieses Lizenzvertrages räumt Ihnen der Lizenzgeber ein lizenzgebührenfreies, räumlich und zeitlich (für die Dauer des Urheberrechts oder verwandten Schutzrechts) unbeschränktes einfaches Nutzungsrecht ein, den Schutzgegenstand in der folgenden Art und Weise zu nutzen:
-
- a. den Schutzgegenstand in körperlicher Form zu verwerten, insbesondere zu vervielfältigen, zu verbreiten und auszustellen;
-
- b. den Schutzgegenstand in unkörperlicher Form öffentlich wiederzugeben, insbesondere vorzutragen, aufzuführen und vorzuführen, öffentlich zugänglich zu machen, zu senden, durch Bild- und Tonträger wiederzugeben sowie Funksendungen und öffentliche Zugänglichmachungen wiederzugeben;
-
- c. den Schutzgegenstand auf Bild- oder Tonträger aufzunehmen, Lichtbilder davon herzustellen, weiterzusenden und in dem in a. und b. genannten Umfang zu verwerten;
-
- d. den Schutzgegenstand zu bearbeiten oder in anderer Weise umzugestalten und die Bearbeitungen zu veröffentlichen und in dem in a. bis c. genannten Umfang zu verwerten;
-
-Die genannten Nutzungsrechte können für alle bekannten Nutzungsarten ausgeübt werden. Die genannten Nutzungsrechte beinhalten das Recht, solche Veränderungen an dem Werk vorzunehmen, die technisch erforderlich sind, um die Nutzungsrechte für alle Nutzungsarten wahrzunehmen. Insbesondere sind davon die Anpassung an andere Medien und auf andere Dateiformate umfasst.
-
-4. Beschränkungen. Die Einräumung der Nutzungsrechte gemäß Ziffer 3 erfolgt ausdrücklich nur unter den folgenden Bedingungen:
-
- a. Sie dürfen den Schutzgegenstand ausschließlich unter den Bedingungen dieser Lizenz vervielfältigen, verbreiten oder öffentlich wiedergeben, und Sie müssen stets eine Kopie oder die vollständige Internetadresse in Form des Uniform-Resource-Identifier (URI) dieser Lizenz beifügen, wenn Sie den Schutzgegenstandvervielfältigen, verbreiten oder öffentlich wiedergeben. Sie dürfen keine Vertragsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch sie gewährten Rechte ändern oder beschränken. Sie dürfen den Schutzgegenstand nicht unterlizenzieren. Sie müssen alle Hinweise unverändert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Sie dürfen den Schutzgegenstand mit keinen technischen Schutzmaßnahmen versehen, die den Zugang oder den Gebrauch des Schutzgegenstandes in einer Weise kontrollieren, die mit den Bedingungen dieser Lizenz im Widerspruch stehen. Die genannten Beschränkungen gelten auch für den Fall, dass der Schutzgegenstand einen Bestandteil eines Sammelwerkes bildet; sie verlangen aber nicht, dass das Sammelwerk insgesamt zum Gegenstand dieser Lizenz gemacht wird. Wenn Sie ein Sammelwerk erstellen, müssen Sie - soweit dies praktikabel ist - auf die Mitteilung eines Lizenzgebers oder Urhebers hin aus dem Sammelwerk jeglichen Hinweis auf diesen Lizenzgeber oder diesen Urheber entfernen. Wenn Sie den Schutzgegenstand bearbeiten, müssen Sie - soweit dies praktikabel ist- auf die Aufforderung eines Rechtsinhabers hin von der Bearbeitung jeglichen Hinweis auf diesen Rechtsinhaber entfernen.
-
- b. Sie dürfen eine Bearbeitung ausschließlich unter den Bedingungen dieser Lizenz, einer späteren Version dieser Lizenz mit denselben Lizenzelementen wie diese Lizenz oder einer Creative Commons iCommons Lizenz, die dieselben Lizenzelemente wie diese Lizenz enthält (z.B. Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 2.0 Japan), vervielfältigen, verbreiten oder öffentlich wiedergeben. Sie müssen stets eine Kopie oder die Internetadresse in Form des Uniform-Resource-Identifier (URI) dieser Lizenz oder einer anderen Lizenz der im vorhergehenden Satz beschriebenen Art beifügen, wenn Sie die Bearbeitung vervielfältigen, verbreiten oder öffentlich wiedergeben. Sie dürfen keine Vertragsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch sie gewährten Rechte ändern oder beschränken, und Sie müssen alle Hinweise unverändert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Sie dürfen eine Bearbeitung nicht mit technischen Schutzmaßnahmen versehen, die den Zugang oder den Gebrauch der Bearbeitung in einer Weise kontrollieren, die mit den Bedingungen dieser Lizenz im Widerspruch stehen. Die genannten Beschränkungen gelten auch für eine Bearbeitung als Bestandteil eines Sammelwerkes; sie erfordern aber nicht, dass das Sammelwerk insgesamt zum Gegenstand dieser Lizenz gemacht wird.
-
- c. Sie dürfen die in Ziffer 3 gewährten Nutzungsrechte in keiner Weise verwenden, die hauptsächlich auf einen geschäftlichen Vorteil oder eine vertraglich geschuldete geldwerte Vergütung abzielt oder darauf gerichtet ist. Erhalten Sie im Zusammenhang mit der Einräumung der Nutzungsrechte ebenfalls einen Schutzgegenstand, ohne dass eine vertragliche Verpflichtung hierzu besteht, so wird dies nicht als geschäftlicher Vorteil oder vertraglich geschuldete geldwerte Vergütung angesehen, wenn keine Zahlung oder geldwerte Vergütung in Verbindung mit dem Austausch der Schutzgegenstände geleistet wird (z.B. File-Sharing).
-
- d. Wenn Sie den Schutzgegenstand oder eine Bearbeitung oder ein Sammelwerk vervielfältigen, verbreiten oder öffentlich wiedergeben, müssen Sie alle Urhebervermerke für den Schutzgegenstand unverändert lassen und die Urheberschaft oder Rechtsinhaberschaft in einer der von Ihnen vorgenommenen Nutzung angemessenen Form anerkennen, indem Sie den Namen (oder das Pseudonym, falls ein solches verwendet wird) des Urhebers oder Rechteinhabers nennen, wenn dieser angegeben ist. Dies gilt auch für den Titel des Schutzgegenstandes, wenn dieser angeben ist, sowie - in einem vernünftigerweise durchführbaren Umfang - für die mit dem Schutzgegenstand zu verbindende Internetadresse in Form des Uniform-Resource-Identifier (URI), wie sie der Lizenzgeber angegeben hat, sofern dies geschehen ist, es sei denn, diese Internetadresse verweist nicht auf den Urhebervermerk oder die Lizenzinformationen zu dem Schutzgegenstand. Bei einer Bearbeitung ist ein Hinweis darauf aufzuführen, in welcher Form der Schutzgegenstand in die Bearbeitung eingegangen ist (z.B. „Französische Übersetzung des ... (Werk) durch ... (Urheber)“ oder „Das Drehbuch beruht auf dem Werk des ... (Urheber)“). Ein solcher Hinweis kann in jeder angemessenen Weise erfolgen, wobei jedoch bei einer Bearbeitung, einer Datenbank oder einem Sammelwerk der Hinweis zumindest an gleicher Stelle und in ebenso auffälliger Weise zu erfolgen hat wie vergleichbare Hinweise auf andere Rechtsinhaber.
-
- e. Obwohl die gemäss Ziffer 3 gewährten Nutzungsrechte in umfassender Weise ausgeübt werden dürfen, findet diese Erlaubnis ihre gesetzliche Grenze in den Persönlichkeitsrechten der Urheber und ausübenden Künstler, deren berechtigte geistige und persönliche Interessen bzw. deren Ansehen oder Ruf nicht dadurch gefährdet werden dürfen, dass ein Schutzgegenstand über das gesetzlich zulässige Maß hinaus beeinträchtigt wird.
-
-5. Gewährleistung. Sofern dies von den Vertragsparteien nicht anderweitig schriftlich vereinbart,, bietet der Lizenzgeber keine Gewährleistung für die erteilten Rechte, außer für den Fall, dass Mängel arglistig verschwiegen wurden. Für Mängel anderer Art, insbesondere bei der mangelhaften Lieferung von Verkörperungen des Schutzgegenstandes, richtet sich die Gewährleistung nach der Regelung, die die Person, die Ihnen den Schutzgegenstand zur Verfügung stellt, mit Ihnen außerhalb dieser Lizenz vereinbart, oder - wenn eine solche Regelung nicht getroffen wurde - nach den gesetzlichen Vorschriften.
-
-6. Haftung. Über die in Ziffer 5 genannte Gewährleistung hinaus haftet Ihnen der Lizenzgeber nur für Vorsatz und grobe Fahrlässigkeit.
-
-7. Vertragsende
-
- a. Dieser Lizenzvertrag und die durch ihn eingeräumten Nutzungsrechte enden automatisch bei jeder Verletzung der Vertragsbedingungen durch Sie. Für natürliche und juristische Personen, die von Ihnen eine Bearbeitung, eine Datenbank oder ein Sammelwerk unter diesen Lizenzbedingungen erhalten haben, gilt die Lizenz jedoch weiter, vorausgesetzt, diese natürlichen oder juristischen Personen erfüllen sämtliche Vertragsbedingungen. Die Ziffern 1, 2, 5, 6, 7 und 8 gelten bei einer Vertragsbeendigung fort.
-
- b. Unter den oben genannten Bedingungen erfolgt die Lizenz auf unbegrenzte Zeit (für die Dauer des Schutzrechts). Dennoch behält sich der Lizenzgeber das Recht vor, den Schutzgegenstand unter anderen Lizenzbedingungen zu nutzen oder die eigene Weitergabe des Schutzgegenstandes jederzeit zu beenden, vorausgesetzt, dass solche Handlungen nicht dem Widerruf dieser Lizenz dienen (oder jeder anderen Lizenzierung, die auf Grundlage dieser Lizenz erfolgt ist oder erfolgen muss) und diese Lizenz wirksam bleibt, bis Sie unter den oben genannten Voraussetzungen endet.
-
-8. Schlussbestimmungen
-
- a. Jedes Mal, wenn Sie den Schutzgegenstand vervielfältigen, verbreiten oder öffentlich wiedergeben, bietet der Lizenzgeber dem Erwerber eine Lizenz für den Schutzgegenstand unter denselben Vertragsbedingungen an, unter denen er Ihnen die Lizenz eingeräumt hat.
-
- b. Jedes Mal, wenn Sie eine Bearbeitung vervielfältigen, verbreiten oder öffentlich wiedergeben, bietet der Lizenzgeber dem Erwerber eine Lizenz für den ursprünglichen Schutzgegenstand unter denselben Vertragsbedingungen an, unter denen er Ihnen die Lizenz eingeräumt hat.
-
- c. Sollte eine Bestimmung dieses Lizenzvertrages unwirksam sein, so wird die Wirksamkeit der übrigen Lizenzbestimmungen dadurch nicht berührt, und an die Stelle der unwirksamen Bestimmung tritt eine Ersatzregelung, die dem mit der unwirksamen Bestimmung angestrebten Zweck am nächsten kommt.
-
- d. Nichts soll dahingehend ausgelegt werden, dass auf eine Bestimmung dieses Lizenzvertrages verzichtet oder einer Vertragsverletzung zugestimmt wird, so lange ein solcher Verzicht oder eine solche Zustimmung nicht schriftlich vorliegen und von der verzichtenden oder zustimmenden Vertragspartei unterschrieben sind
-
- e. Dieser Lizenzvertrag stellt die vollständige Vereinbarung zwischen den Vertragsparteien hinsichtlich des Schutzgegenstandes dar. Es gibt keine weiteren ergänzenden Vereinbarungen oder mündlichen Abreden im Hinblick auf den Schutzgegenstand. Der Lizenzgeber ist an keine zusätzlichen Abreden gebunden, die aus irgendeiner Absprache mit Ihnen entstehen könnten. Der Lizenzvertrag kann nicht ohne eine übereinstimmende schriftliche Vereinbarung zwischen dem Lizenzgeber und Ihnen abgeändert werden.
-
- f. Auf diesen Lizenzvertrag findet das Recht der Bundesrepublik Deutschland Anwendung.
-
-CREATIVE COMMONS IST KEINE VERTRAGSPARTEI DIESES LIZENZVERTRAGES UND ÜBERNIMMT KEINERLEI GEWÄHRLEISTUNG FÜR DAS WERK. CREATIVE COMMONS IST IHNEN ODER DRITTEN GEGENÜBER NICHT HAFTBAR FÜR SCHÄDEN JEDWEDER ART. UNGEACHTET DER VORSTEHENDEN ZWEI (2) SÄTZE HAT CREATIVE COMMONS ALL RECHTE UND PFLICHTEN EINES LIZENSGEBERS, WENN SICH CREATIVE COMMONS AUSDRÜCKLICH ALS LIZENZGEBER BEZEICHNET.
-
-AUSSER FÜR DEN BESCHRÄNKTEN ZWECK EINES HINWEISES AN DIE ÖFFENTLICHKEIT, DASS DAS WERK UNTER DER CCPL LIZENSIERT WIRD, DARF KENIE VERTRAGSPARTEI DIE MARKE “CREATIVE COMMONS†ODER EINE ÄHNLICHE MARKE ODER DAS LOGO VON CREATIVE COMMONS OHNE VORHERIGE GENEHMIGUNG VON CREATIVE COMMONS NUTZEN. JEDE GESTATTETE NUTZUNG HAT IN ÜBREEINSTIMMUNG MIT DEN JEWEILS GÜLTIGEN NUTZUNGSBEDINGUNGEN FÜR MARKEN VON CREATIVE COMMONS ZU ERFOLGEN, WIE SIE AUF DER WEBSITE ODER IN ANDERER WEISE AUF ANFRAGE VON ZEIT ZU ZEIT ZUGÄNGLICH GEMACHT WERDEN.
-
-CREATIVE COMMONS KANN UNTER https://creativecommons.org KONTAKTIERT WERDEN.
diff --git a/options/license/CC-BY-NC-SA-2.0-FR b/options/license/CC-BY-NC-SA-2.0-FR
deleted file mode 100644
index 9f7f9103ea..0000000000
--- a/options/license/CC-BY-NC-SA-2.0-FR
+++ /dev/null
@@ -1,93 +0,0 @@
-Creative Commons Paternité - Pas d'Utilisation Commerciale - Partage Des Conditions Initiales A l'Identique 2.0
-
- Creative Commons n'est pas un cabinet d'avocats et ne fournit pas de services de conseil juridique. La distribution de la présente version de ce contrat ne crée aucune relation juridique entre les parties au contrat présenté ci-après et Creative Commons. Creative Commons fournit cette offre de contrat-type en l'état, à seule fin d'information. Creative Commons ne saurait être tenu responsable des éventuels préjudices résultant du contenu ou de l'utilisation de ce contrat.
-
-Contrat
-
-L'Oeuvre (telle que définie ci-dessous) est mise à disposition selon les termes du présent contrat appelé Contrat Public Creative Commons (dénommé ici « CPCC » ou « Contrat »). L'Oeuvre est protégée par le droit de la propriété littéraire et artistique (droit d'auteur, droits voisins, droits des producteurs de bases de données) ou toute autre loi applicable. Toute utilisation de l'Oeuvre autrement qu'explicitement autorisée selon ce Contrat ou le droit applicable est interdite.
-
-L'exercice sur l'Oeuvre de tout droit proposé par le présent contrat vaut acceptation de celui-ci. Selon les termes et les obligations du présent contrat, la partie Offrante propose à la partie Acceptante l'exercice de certains droits présentés ci-après, et l'Acceptant en approuve les termes et conditions d'utilisation.
-
-1. Définitions
-
- a. « Oeuvre » : oeuvre de l'esprit protégeable par le droit de la propriété littéraire et artistique ou toute loi applicable et qui est mise à disposition selon les termes du présent Contrat.
-
- b. « Oeuvre dite Collective » : une oeuvre dans laquelle l'oeuvre, dans sa forme intégrale et non modifiée, est assemblée en un ensemble collectif avec d'autres contributions qui constituent en elles-mêmes des oeuvres séparées et indépendantes. Constituent notamment des Oeuvres dites Collectives les publications périodiques, les anthologies ou les encyclopédies. Aux termes de la présente autorisation, une oeuvre qui constitue une Oeuvre dite Collective ne sera pas considérée comme une Oeuvre dite Dérivée (telle que définie ci-après).
-
- c. « Oeuvre dite Dérivée » : une oeuvre créée soit à partir de l'Oeuvre seule, soit à partir de l'Oeuvre et d'autres oeuvres préexistantes. Constituent notamment des Oeuvres dites Dérivées les traductions, les arrangements musicaux, les adaptations théâtrales, littéraires ou cinématographiques, les enregistrements sonores, les reproductions par un art ou un procédé quelconque, les résumés, ou toute autre forme sous laquelle l'Oeuvre puisse être remaniée, modifiée, transformée ou adaptée, à l'exception d'une oeuvre qui constitue une Oeuvre dite Collective. Une Oeuvre dite Collective ne sera pas considérée comme une Oeuvre dite Dérivée aux termes du présent Contrat. Dans le cas où l'Oeuvre serait une composition musicale ou un enregistrement sonore, la synchronisation de l'oeuvre avec une image animée sera considérée comme une Oeuvre dite Dérivée pour les propos de ce Contrat.
-
- d. « Auteur original » : la ou les personnes physiques qui ont créé l'Oeuvre.
-
- e. « Offrant » : la ou les personne(s) physique(s) ou morale(s) qui proposent la mise à disposition de l'Oeuvre selon les termes du présent Contrat.
-
- f. « Acceptant » : la personne physique ou morale qui accepte le présent contrat et exerce des droits sans en avoir violé les termes au préalable ou qui a reçu l'autorisation expresse de l'Offrant d'exercer des droits dans le cadre du présent contrat malgré une précédente violation de ce contrat.
-
- g. « Options du Contrat » : les attributs génériques du Contrat tels qu'ils ont été choisis par l'Offrant et indiqués dans le titre de ce Contrat : Paternité - Pas d'Utilisation Commerciale - Partage Des Conditions Initiales A l'Identique.
-
-2. Exceptions aux droits exclusifs. Aucune disposition de ce contrat n'a pour intention de réduire, limiter ou restreindre les prérogatives issues des exceptions aux droits, de l'épuisement des droits ou d'autres limitations aux droits exclusifs des ayants droit selon le droit de la propriété littéraire et artistique ou les autres lois applicables.
-
-3. Autorisation. Soumis aux termes et conditions définis dans cette autorisation, et ceci pendant toute la durée de protection de l'Oeuvre par le droit de la propriété littéraire et artistique ou le droit applicable, l'Offrant accorde à l'Acceptant l'autorisation mondiale d'exercer à titre gratuit et non exclusif les droits suivants :
-
- a. reproduire l'Oeuvre, incorporer l'Oeuvre dans une ou plusieurs Oeuvres dites Collectives et reproduire l'Oeuvre telle qu'incorporée dans lesdites Oeuvres dites Collectives;
-
- b. créer et reproduire des Oeuvres dites Dérivées;
-
- c. distribuer des exemplaires ou enregistrements, présenter, représenter ou communiquer l'Oeuvre au public par tout procédé technique, y compris incorporée dans des Oeuvres Collectives;
-
- d. distribuer des exemplaires ou phonogrammes, présenter, représenter ou communiquer au public des Oeuvres dites Dérivées par tout procédé technique;
-
- e. lorsque l'Oeuvre est une base de données, extraire et réutiliser des parties substantielles de l'Oeuvre.
-
-Les droits mentionnés ci-dessus peuvent être exercés sur tous les supports, médias, procédés techniques et formats. Les droits ci-dessus incluent le droit d'effectuer les modifications nécessaires techniquement à l'exercice des droits dans d'autres formats et procédés techniques. L'exercice de tous les droits qui ne sont pas expressément autorisés par l'Offrant ou dont il n'aurait pas la gestion demeure réservé, notamment les mécanismes de gestion collective obligatoire applicables décrits à l'article 4(e).
-
-4. Restrictions. L'autorisation accordée par l'article 3 est expressément assujettie et limitée par le respect des restrictions suivantes :
-
-
- a. L'Acceptant peut reproduire, distribuer, représenter ou communiquer au public l'Oeuvre y compris par voie numérique uniquement selon les termes de ce Contrat. L'Acceptant doit inclure une copie ou l'adresse Internet (Identifiant Uniforme de Ressource) du présent Contrat à toute reproduction ou enregistrement de l'Oeuvre que l'Acceptant distribue, représente ou communique au public y compris par voie numérique. L'Acceptant ne peut pas offrir ou imposer de conditions d'utilisation de l'Oeuvre qui altèrent ou restreignent les termes du présent Contrat ou l'exercice des droits qui y sont accordés au bénéficiaire. L'Acceptant ne peut pas céder de droits sur l'Oeuvre. L'Acceptant doit conserver intactes toutes les informations qui renvoient à ce Contrat et à l'exonération de responsabilité. L'Acceptant ne peut pas reproduire, distribuer, représenter ou communiquer au public l'Oeuvre, y compris par voie numérique, en utilisant une mesure technique de contrôle d'accès ou de contrôle d'utilisation qui serait contradictoire avec les termes de cet Accord contractuel. Les mentions ci-dessus s'appliquent à l'Oeuvre telle qu'incorporée dans une Oeuvre dite Collective, mais, en dehors de l'Oeuvre en elle-même, ne soumettent pas l'Oeuvre dite Collective, aux termes du présent Contrat. Si l'Acceptant crée une Oeuvre dite Collective, à la demande de tout Offrant, il devra, dans la mesure du possible, retirer de l'Oeuvre dite Collective toute référence au dit Offrant, comme demandé. Si l'Acceptant crée une Oeuvre dite Collective, à la demande de tout Auteur, il devra, dans la mesure du possible, retirer de l'Oeuvre dite Collective toute référence au dit Auteur, comme demandé. Si l'Acceptant crée une Oeuvre dite Dérivée, à la demande de tout Offrant, il devra, dans la mesure du possible, retirer de l'Oeuvre dite Dérivée toute référence au dit Offrant, comme demandé. Si l'Acceptant crée une Oeuvre dite Dérivée, à la demande de tout Auteur, il devra, dans la mesure du possible, retirer de l'Oeuvre dite Dérivée toute référence au dit Auteur, comme demandé.
-
- b. L'Acceptant peut reproduire, distribuer, représenter ou communiquer au public une Oeuvre dite Dérivée y compris par voie numérique uniquement sous les termes de ce Contrat, ou d'une version ultérieure de ce Contrat comprenant les mêmes Options du Contrat que le présent Contrat, ou un Contrat Creative Commons iCommons comprenant les mêmes Options du Contrat que le présent Contrat (par exemple Paternité - Pas d'Utilisation Commerciale - Partage Des Conditions Initiales A l'Identique 2.0 Japon). L'Acceptant doit inclure une copie ou l'adresse Internet (Identifiant Uniforme de Ressource) du présent Contrat, ou d'un autre Contrat tel que décrit à la phrase précédente, à toute reproduction ou enregistrement de l'Oeuvre dite Dérivée que l'Acceptant distribue, représente ou communique au public y compris par voie numérique. L'Acceptant ne peut pas offrir ou imposer de conditions d'utilisation sur l'Oeuvre dite Dérivée qui altèrent ou restreignent les termes du présent Contrat ou l'exercice des droits qui y sont accordés au bénéficiaire, et doit conserver intactes toutes les informations qui renvoient à ce Contrat et à l'avertissement sur les garanties. L'Acceptant ne peut pas reproduire, distribuer, représenter ou communiquer au public y compris par voie numérique l'Oeuvre dite Dérivée en utilisant une mesure technique de contrôle d'accès ou de contrôle d'utilisation qui serait contradictoire avec les termes de cet Accord contractuel. Les mentions ci-dessus s'appliquent à l'Oeuvre dite Dérivée telle qu'incorporée dans une Oeuvre dite Collective, mais, en dehors de l'Oeuvre dite Dérivée en elle-même, ne soumettent pas l'Oeuvre Collective, aux termes du présent Contrat.
-
- c. L'Acceptant ne peut exercer aucun des droits conférés par l'article 3 avec l'intention ou l'objectif d'obtenir un profit commercial ou une compensation financière personnelle. L'échange de l'Oeuvre avec d'autres Oeuvres protégées par le droit de la propriété littéraire et artistique par le partage électronique de fichiers, ou par tout autre moyen, n'est pas considéré comme un échange avec l'intention ou l'objectif d'un profit commercial ou d'une compensation financière personnelle, dans la mesure où aucun paiement ou compensation financière n'intervient en relation avec l'échange d'Oeuvres protégées.
-
- d. Si l'Acceptant reproduit, distribue, représente ou communique au public, y compris par voie numérique, l'Oeuvre ou toute Oeuvre dite Dérivée ou toute Oeuvre dite Collective, il doit conserver intactes toutes les informations sur le régime des droits et en attribuer la paternité à l'Auteur Original, de manière raisonnable au regard au médium ou au moyen utilisé. Il doit communiquer le nom de l'Auteur Original ou son éventuel pseudonyme s'il est indiqué ; le titre de l'Oeuvre Originale s'il est indiqué ; dans la mesure du possible, l'adresse Internet ou Identifiant Uniforme de Ressource (URI), s'il existe, spécifié par l'Offrant comme associé à l'Oeuvre, à moins que cette adresse ne renvoie pas aux informations légales (paternité et conditions d'utilisation de l'Oeuvre). Dans le cas d'une Oeuvre dite Dérivée, il doit indiquer les éléments identifiant l'utilisation l'Oeuvre dans l'Oeuvre dite Dérivée par exemple « Traduction anglaise de l'Oeuvre par l'Auteur Original » ou « Scénario basé sur l'Oeuvre par l'Auteur Original ». Ces obligations d'attribution de paternité doivent être exécutées de manière raisonnable. Cependant, dans le cas d'une Oeuvre dite Dérivée ou d'une Oeuvre dite Collective, ces informations doivent, au minimum, apparaître à la place et de manière aussi visible que celles à laquelle apparaissent les informations de même nature.
-
- e. Dans le cas où une utilisation de l'Oeuvre serait soumise à un régime légal de gestion collective obligatoire, l'Offrant se réserve le droit exclusif de collecter ces redevances par l'intermédiaire de la société de perception et de répartition des droits compétente. Sont notamment concernés la radiodiffusion et la communication dans un lieu public de phonogrammes publiés à des fins de commerce, certains cas de retransmission par câble et satellite, la copie privée d'Oeuvres fixées sur phonogrammes ou vidéogrammes, la reproduction par reprographie.
-
-5. Garantie et exonération de responsabilité
-
-
- a. En mettant l'Oeuvre à la disposition du public selon les termes de ce Contrat, l'Offrant déclare de bonne foi qu'à sa connaissance et dans les limites d'une enquête raisonnable :
-
- i. L'Offrant a obtenu tous les droits sur l'Oeuvre nécessaires pour pouvoir autoriser l'exercice des droits accordés par le présent Contrat, et permettre la jouissance paisible et l'exercice licite de ces droits, ceci sans que l'Acceptant n'ait aucune obligation de verser de rémunération ou tout autre paiement ou droits, dans la limite des mécanismes de gestion collective obligatoire applicables décrits à l'article 4(e);
-
- ii. L'Oeuvre n'est constitutive ni d'une violation des droits de tiers, notamment du droit de la propriété littéraire et artistique, du droit des marques, du droit de l'information, du droit civil ou de tout autre droit, ni de diffamation, de violation de la vie privée ou de tout autre préjudice délictuel à l'égard de toute tierce partie.
-
- b. A l'exception des situations expressément mentionnées dans le présent Contrat ou dans un autre accord écrit, ou exigées par la loi applicable, l'Oeuvre est mise à disposition en l'état sans garantie d'aucune sorte, qu'elle soit expresse ou tacite, y compris à l'égard du contenu ou de l'exactitude de l'Oeuvre.
-
-6. Limitation de responsabilité. A l'exception des garanties d'ordre public imposées par la loi applicable et des réparations imposées par le régime de la responsabilité vis-à-vis d'un tiers en raison de la violation des garanties prévues par l'article 5 du présent contrat, l'Offrant ne sera en aucun cas tenu responsable vis-à-vis de l'Acceptant, sur la base d'aucune théorie légale ni en raison d'aucun préjudice direct, indirect, matériel ou moral, résultant de l'exécution du présent Contrat ou de l'utilisation de l'Oeuvre, y compris dans l'hypothèse où l'Offrant avait connaissance de la possible existence d'un tel préjudice.
-
-7. Résiliation
-
- a. Tout manquement aux termes du contrat par l'Acceptant entraîne la résiliation automatique du Contrat et la fin des droits qui en découlent. Cependant, le contrat conserve ses effets envers les personnes physiques ou morales qui ont reçu de la part de l'Acceptant, en exécution du présent contrat, la mise à disposition d'Oeuvres dites Dérivées, ou d'Oeuvres dites Collectives, ceci tant qu'elles respectent pleinement leurs obligations. Les sections 1, 2, 5, 6 et 7 du contrat continuent à s'appliquer après la résiliation de celui-ci.
-
- b. Dans les limites indiquées ci-dessus, le présent Contrat s'applique pendant toute la durée de protection de l'Oeuvre selon le droit applicable. Néanmoins, l'Offrant se réserve à tout moment le droit d'exploiter l'Oeuvre sous des conditions contractuelles différentes, ou d'en cesser la diffusion; cependant, le recours à cette option ne doit pas conduire à retirer les effets du présent Contrat (ou de tout contrat qui a été ou doit être accordé selon les termes de ce Contrat), et ce Contrat continuera à s'appliquer dans tous ses effets jusqu'à ce que sa résiliation intervienne dans les conditions décrites ci-dessus.
-
-8. Divers
-
- a. A chaque reproduction ou communication au public par voie numérique de l'Oeuvre ou d'une Oeuvre dite Collective par l'Acceptant, l'Offrant propose au bénéficiaire une offre de mise à disposition de l'Oeuvre dans des termes et conditions identiques à ceux accordés à la partie Acceptante dans le présent Contrat.
-
- b. A chaque reproduction ou communication au public par voie numérique d'une Oeuvre dite Dérivée par l'Acceptant, l'Offrant propose au bénéficiaire une offre de mise à disposition du bénéficiaire de l'Oeuvre originale dans des termes et conditions identiques à ceux accordés à la partie Acceptante dans le présent Contrat.
-
- c. La nullité ou l'inapplicabilité d'une quelconque disposition de ce Contrat au regard de la loi applicable n'affecte pas celle des autres dispositions qui resteront pleinement valides et applicables. Sans action additionnelle par les parties à cet accord, lesdites dispositions devront être interprétées dans la mesure minimum nécessaire à leur validité et leur applicabilité.
-
- d. Aucune limite, renonciation ou modification des termes ou dispositions du présent Contrat ne pourra être acceptée sans le consentement écrit et signé de la partie compétente.
-
- e. Ce Contrat constitue le seul accord entre les parties à propos de l'Oeuvre mise ici à disposition. Il n'existe aucun élément annexe, accord supplémentaire ou mandat portant sur cette Oeuvre en dehors des éléments mentionnés ici. L'Offrant ne sera tenu par aucune disposition supplémentaire qui pourrait apparaître dans une quelconque communication en provenance de l'Acceptant. Ce Contrat ne peut être modifié sans l'accord mutuel écrit de l'Offrant et de l'Acceptant.
-
- f. Le droit applicable est le droit français.
-
-Creative Commons n'est pas partie à ce Contrat et n'offre aucune forme de garantie relative à l'Oeuvre. Creative Commons décline toute responsabilité à l'égard de l'Acceptant ou de toute autre partie, quel que soit le fondement légal de cette responsabilité et quel que soit le préjudice subi, direct, indirect, matériel ou moral, qui surviendrait en rapport avec le présent Contrat. Cependant, si Creative Commons s'est expressément identifié comme Offrant pour mettre une Oeuvre à disposition selon les termes de ce Contrat, Creative Commons jouira de tous les droits et obligations d'un Offrant.
-
-A l'exception des fins limitées à informer le public que l'Oeuvre est mise à disposition sous CPCC, aucune des parties n'utilisera la marque « Creative Commons » ou toute autre indication ou logo afférent sans le consentement préalable écrit de Creative Commons. Toute utilisation autorisée devra être effectuée en conformité avec les lignes directrices de Creative Commons à jour au moment de l'utilisation, telles qu'elles sont disponibles sur son site Internet ou sur simple demande.
-
-Creative Commons peut être contacté à https://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-SA-2.0-UK b/options/license/CC-BY-NC-SA-2.0-UK
deleted file mode 100644
index 4025f2325f..0000000000
--- a/options/license/CC-BY-NC-SA-2.0-UK
+++ /dev/null
@@ -1,149 +0,0 @@
-Creative Commons Attribution - Non-Commercial - Share-Alike 2.0 England and Wales
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENCE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-Licence
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENCE ("CCPL" OR "LICENCE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENCE OR COPYRIGHT LAW IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENCE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-This Creative Commons England and Wales Public Licence enables You (all capitalised terms defined below) to view, edit, modify, translate and distribute Works worldwide, under the terms of this licence, provided that You credit the Original Author.
-
-'The Licensor' [one or more legally recognised persons or entities offering the Work under the terms and conditions of this Licence]
-
-and
-
-'You'
-
-agree as follows:
-
-1. Definitions
-
- a. "Attribution" means acknowledging all the parties who have contributed to and have rights in the Work or Collective Work under this Licence.
-
- b. "Collective Work" means the Work in its entirety in unmodified form along with a number of other separate and independent works, assembled into a collective whole.
-
- c. "Derivative Work" means any work created by the editing, modification, adaptation or translation of the Work in any media (however a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this Licence). For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this Licence.
-
- d. "Licence" means this Creative Commons England and Wales Public Licence agreement.
-
- e. "Licence Elements" means the following high-level licence attributes indicated in the title of this Licence: Attribution, Non-Commercial, Share-Alike.
-
- f. "Non-Commercial" means "not primarily intended for or directed towards commercial advantage or private monetary compensation". The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed towards commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
- g. "Original Author" means the individual (or entity) who created the Work.
-
- h. "Work" means the work protected by copyright which is offered under the terms of this Licence.
-
-For the purpose of this Licence, when not inconsistent with the context, words in the singular number include the plural number.
-
-2. Licence Terms
-
-2.1 The Licensor hereby grants to You a worldwide, royalty-free, non-exclusive, Licence for Non-Commercial use and for the duration of copyright in the Work.
-
-You may:
-
- • copy the Work;
-
- • create one or more Derivative Works;
-
- • incorporate the Work into one or more Collective Works;
-
- • copy Derivative Works or the Work as incorporated in any Collective Work; and
-
- • publish, distribute, archive, perform or otherwise disseminate the Work or the Work as incorporated in any Collective Work, to the public in any material form in any media whether now known or hereafter created.
-
-HOWEVER,
-
-You must not:
-
- • impose any terms on the use to be made of the Work, the Derivative Work or the Work as incorporated in a Collective Work that alter or restrict the terms of this Licence or any rights granted under it or has the effect or intent of restricting the ability to exercise those rights;
-
- • impose any digital rights management technology on the Work or the Work as incorporated in a Collective Work that alters or restricts the terms of this Licence or any rights granted under it or has the effect or intent of restricting the ability to exercise those rights;
-
- • sublicense the Work;
-
- • subject the Work to any derogatory treatment as defined in the Copyright, Designs and Patents Act 1988.
-
-FINALLY,
-
-You must:
-
- • make reference to this Licence (by Uniform Resource Identifier (URI), spoken word or as appropriate to the media used) on all copies of the Work and Collective Works published, distributed, performed or otherwise disseminated or made available to the public by You;
-
- • recognise the Licensor's / Original Author's right of attribution in any Work and Collective Work that You publish, distribute, perform or otherwise disseminate to the public and ensure that You credit the Licensor / Original Author as appropriate to the media used; and
-
- • to the extent reasonably practicable, keep intact all notices that refer to this Licence, in particular the URI, if any, that the Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work.
-
-Additional Provisions for third parties making use of the Work
-
-2.2. Further licence from the Licensor
-
-Each time You publish, distribute, perform or otherwise disseminate
-
- • the Work; or
-
- • any Derivative Work; or
-
- • the Work as incorporated in a Collective Work
-
-the Licensor agrees to offer to the relevant third party making use of the Work (in any of the alternatives set out above) a licence to use the Work on the same terms and conditions as granted to You hereunder.
-
-2.3. Further licence from You
-
-Each time You publish, distribute, perform or otherwise disseminate
-
- • a Derivative Work; or
-
- • a Derivative Work as incorporated in a Collective Work
-
-You agree to offer to the relevant third party making use of the Work (in either of the alternatives set out above) a licence to use the Derivative Work on any of the following premises:
-
- • a licence on the same terms and conditions as the licence granted to You hereunder; or
-
- • a later version of the licence granted to You hereunder; or
-
- • any other Creative Commons licence with the same Licence Elements.
-
-2.4. This Licence does not affect any rights that the User may have under any applicable law, including fair use, fair dealing or any other legally recognised limitation or exception to copyright infringement.
-
-2.5. All rights not expressly granted by the Licensor are hereby reserved, including but not limited to, the exclusive right to collect, whether individually or via a licensing body, such as a collecting society, royalties for any use of the Work which results in commercial advantage or private monetary compensation.
-
-3. Warranties and Disclaimer
-
-Except as required by law, the Work is licensed by the Licensor on an "as is" and "as available" basis and without any warranty of any kind, either express or implied.
-
-4. Limit of Liability
-
-Subject to any liability which may not be excluded or limited by law the Licensor shall not be liable and hereby expressly excludes all liability for loss or damage howsoever and whenever caused to You.
-
-5. Termination
-
-The rights granted to You under this Licence shall terminate automatically upon any breach by You of the terms of this Licence. Individuals or entities who have received Collective Works from You under this Licence, however, will not have their Licences terminated provided such individuals or entities remain in full compliance with those Licences.
-
-6. General
-
-6.1. The validity or enforceability of the remaining terms of this agreement is not affected by the holding of any provision of it to be invalid or unenforceable.
-
-6.2. This Licence constitutes the entire Licence Agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. The Licensor shall not be bound by any additional provisions that may appear in any communication in any form.
-
-6.3. A person who is not a party to this Licence shall have no rights under the Contracts (Rights of Third Parties) Act 1999 to enforce any of its terms.
-
-6.4. This Licence shall be governed by the law of England and Wales and the parties irrevocably submit to the exclusive jurisdiction of the Courts of England and Wales.
-
-7. On the role of Creative Commons
-
-7.1. Neither the Licensor nor the User may use the Creative Commons logo except to indicate that the Work is licensed under a Creative Commons Licence. Any permitted use has to be in compliance with the Creative Commons trade mark usage guidelines at the time of use of the Creative Commons trade mark. These guidelines may be found on the Creative Commons website or be otherwise available upon request from time to time.
-
-7.2. Creative Commons Corporation does not profit financially from its role in providing this Licence and will not investigate the claims of any Licensor or user of the Licence.
-
-7.3. One of the conditions that Creative Commons Corporation requires of the Licensor and You is an acknowledgement of its limited role and agreement by all who use the Licence that the Corporation is not responsible to anyone for the statements and actions of You or the Licensor or anyone else attempting to use or using this Licence.
-
-7.4. Creative Commons Corporation is not a party to this Licence, and makes no warranty whatsoever in connection to the Work or in connection to the Licence, and in all events is not liable for any loss or damage resulting from the Licensor's or Your reliance on this Licence or on its enforceability.
-
-7.5. USE OF THIS LICENCE MEANS THAT YOU AND THE LICENSOR EACH ACCEPTS THESE CONDITIONS IN SECTION 7.1, 7.2, 7.3, 7.4 AND EACH ACKNOWLEDGES CREATIVE COMMONS CORPORATION'S VERY LIMITED ROLE AS A FACILITATOR OF THE LICENCE FROM THE LICENSOR TO YOU.
-
- Creative Commons is not a party to this Licence, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this licence. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
- Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
- Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-SA-2.5 b/options/license/CC-BY-NC-SA-2.5
deleted file mode 100644
index e64b6d7c16..0000000000
--- a/options/license/CC-BY-NC-SA-2.5
+++ /dev/null
@@ -1,87 +0,0 @@
-Creative Commons Attribution-NonCommercial-ShareAlike 2.5
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
- g. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, Noncommercial, ShareAlike.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to create and reproduce Derivative Works;
-
- c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
- d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works;
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Sections 4(e) and 4(f).
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(d), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(d), as requested.
-
- b. You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-NonCommercial-ShareAlike 2.5 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
-
- c. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
- d. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
- e. For the avoidance of doubt, where the Work is a musical composition:
-
- i. Performance Royalties Under Blanket Licenses. Licensor reserves the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work if that performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
- ii. Mechanical Rights and Statutory Royalties. Licensor reserves the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions), if Your distribution of such cover version is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
- f. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor reserves the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions), if Your public digital performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-SA-3.0 b/options/license/CC-BY-NC-SA-3.0
deleted file mode 100644
index a50eacf98c..0000000000
--- a/options/license/CC-BY-NC-SA-3.0
+++ /dev/null
@@ -1,360 +0,0 @@
-Creative Commons Legal Code
-
-Attribution-NonCommercial-ShareAlike 3.0 Unported
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
- LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
- ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
- INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
- REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
- DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
-COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
-COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
-AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
-TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
-BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
-CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
-CONDITIONS.
-
-1. Definitions
-
- a. "Adaptation" means a work based upon the Work, or upon the Work and
- other pre-existing works, such as a translation, adaptation,
- derivative work, arrangement of music or other alterations of a
- literary or artistic work, or phonogram or performance and includes
- cinematographic adaptations or any other form in which the Work may be
- recast, transformed, or adapted including in any form recognizably
- derived from the original, except that a work that constitutes a
- Collection will not be considered an Adaptation for the purpose of
- this License. For the avoidance of doubt, where the Work is a musical
- work, performance or phonogram, the synchronization of the Work in
- timed-relation with a moving image ("synching") will be considered an
- Adaptation for the purpose of this License.
- b. "Collection" means a collection of literary or artistic works, such as
- encyclopedias and anthologies, or performances, phonograms or
- broadcasts, or other works or subject matter other than works listed
- in Section 1(g) below, which, by reason of the selection and
- arrangement of their contents, constitute intellectual creations, in
- which the Work is included in its entirety in unmodified form along
- with one or more other contributions, each constituting separate and
- independent works in themselves, which together are assembled into a
- collective whole. A work that constitutes a Collection will not be
- considered an Adaptation (as defined above) for the purposes of this
- License.
- c. "Distribute" means to make available to the public the original and
- copies of the Work or Adaptation, as appropriate, through sale or
- other transfer of ownership.
- d. "License Elements" means the following high-level license attributes
- as selected by Licensor and indicated in the title of this License:
- Attribution, Noncommercial, ShareAlike.
- e. "Licensor" means the individual, individuals, entity or entities that
- offer(s) the Work under the terms of this License.
- f. "Original Author" means, in the case of a literary or artistic work,
- the individual, individuals, entity or entities who created the Work
- or if no individual or entity can be identified, the publisher; and in
- addition (i) in the case of a performance the actors, singers,
- musicians, dancers, and other persons who act, sing, deliver, declaim,
- play in, interpret or otherwise perform literary or artistic works or
- expressions of folklore; (ii) in the case of a phonogram the producer
- being the person or legal entity who first fixes the sounds of a
- performance or other sounds; and, (iii) in the case of broadcasts, the
- organization that transmits the broadcast.
- g. "Work" means the literary and/or artistic work offered under the terms
- of this License including without limitation any production in the
- literary, scientific and artistic domain, whatever may be the mode or
- form of its expression including digital form, such as a book,
- pamphlet and other writing; a lecture, address, sermon or other work
- of the same nature; a dramatic or dramatico-musical work; a
- choreographic work or entertainment in dumb show; a musical
- composition with or without words; a cinematographic work to which are
- assimilated works expressed by a process analogous to cinematography;
- a work of drawing, painting, architecture, sculpture, engraving or
- lithography; a photographic work to which are assimilated works
- expressed by a process analogous to photography; a work of applied
- art; an illustration, map, plan, sketch or three-dimensional work
- relative to geography, topography, architecture or science; a
- performance; a broadcast; a phonogram; a compilation of data to the
- extent it is protected as a copyrightable work; or a work performed by
- a variety or circus performer to the extent it is not otherwise
- considered a literary or artistic work.
- h. "You" means an individual or entity exercising rights under this
- License who has not previously violated the terms of this License with
- respect to the Work, or who has received express permission from the
- Licensor to exercise rights under this License despite a previous
- violation.
- i. "Publicly Perform" means to perform public recitations of the Work and
- to communicate to the public those public recitations, by any means or
- process, including by wire or wireless means or public digital
- performances; to make available to the public Works in such a way that
- members of the public may access these Works from a place and at a
- place individually chosen by them; to perform the Work to the public
- by any means or process and the communication to the public of the
- performances of the Work, including by public digital performance; to
- broadcast and rebroadcast the Work by any means including signs,
- sounds or images.
- j. "Reproduce" means to make copies of the Work by any means including
- without limitation by sound or visual recordings and the right of
- fixation and reproducing fixations of the Work, including storage of a
- protected performance or phonogram in digital form or other electronic
- medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce,
-limit, or restrict any uses free from copyright or rights arising from
-limitations or exceptions that are provided for in connection with the
-copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License,
-Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
-perpetual (for the duration of the applicable copyright) license to
-exercise the rights in the Work as stated below:
-
- a. to Reproduce the Work, to incorporate the Work into one or more
- Collections, and to Reproduce the Work as incorporated in the
- Collections;
- b. to create and Reproduce Adaptations provided that any such Adaptation,
- including any translation in any medium, takes reasonable steps to
- clearly label, demarcate or otherwise identify that changes were made
- to the original Work. For example, a translation could be marked "The
- original work was translated from English to Spanish," or a
- modification could indicate "The original work has been modified.";
- c. to Distribute and Publicly Perform the Work including as incorporated
- in Collections; and,
- d. to Distribute and Publicly Perform Adaptations.
-
-The above rights may be exercised in all media and formats whether now
-known or hereafter devised. The above rights include the right to make
-such modifications as are technically necessary to exercise the rights in
-other media and formats. Subject to Section 8(f), all rights not expressly
-granted by Licensor are hereby reserved, including but not limited to the
-rights described in Section 4(e).
-
-4. Restrictions. The license granted in Section 3 above is expressly made
-subject to and limited by the following restrictions:
-
- a. You may Distribute or Publicly Perform the Work only under the terms
- of this License. You must include a copy of, or the Uniform Resource
- Identifier (URI) for, this License with every copy of the Work You
- Distribute or Publicly Perform. You may not offer or impose any terms
- on the Work that restrict the terms of this License or the ability of
- the recipient of the Work to exercise the rights granted to that
- recipient under the terms of the License. You may not sublicense the
- Work. You must keep intact all notices that refer to this License and
- to the disclaimer of warranties with every copy of the Work You
- Distribute or Publicly Perform. When You Distribute or Publicly
- Perform the Work, You may not impose any effective technological
- measures on the Work that restrict the ability of a recipient of the
- Work from You to exercise the rights granted to that recipient under
- the terms of the License. This Section 4(a) applies to the Work as
- incorporated in a Collection, but this does not require the Collection
- apart from the Work itself to be made subject to the terms of this
- License. If You create a Collection, upon notice from any Licensor You
- must, to the extent practicable, remove from the Collection any credit
- as required by Section 4(d), as requested. If You create an
- Adaptation, upon notice from any Licensor You must, to the extent
- practicable, remove from the Adaptation any credit as required by
- Section 4(d), as requested.
- b. You may Distribute or Publicly Perform an Adaptation only under: (i)
- the terms of this License; (ii) a later version of this License with
- the same License Elements as this License; (iii) a Creative Commons
- jurisdiction license (either this or a later license version) that
- contains the same License Elements as this License (e.g.,
- Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable License").
- You must include a copy of, or the URI, for Applicable License with
- every copy of each Adaptation You Distribute or Publicly Perform. You
- may not offer or impose any terms on the Adaptation that restrict the
- terms of the Applicable License or the ability of the recipient of the
- Adaptation to exercise the rights granted to that recipient under the
- terms of the Applicable License. You must keep intact all notices that
- refer to the Applicable License and to the disclaimer of warranties
- with every copy of the Work as included in the Adaptation You
- Distribute or Publicly Perform. When You Distribute or Publicly
- Perform the Adaptation, You may not impose any effective technological
- measures on the Adaptation that restrict the ability of a recipient of
- the Adaptation from You to exercise the rights granted to that
- recipient under the terms of the Applicable License. This Section 4(b)
- applies to the Adaptation as incorporated in a Collection, but this
- does not require the Collection apart from the Adaptation itself to be
- made subject to the terms of the Applicable License.
- c. You may not exercise any of the rights granted to You in Section 3
- above in any manner that is primarily intended for or directed toward
- commercial advantage or private monetary compensation. The exchange of
- the Work for other copyrighted works by means of digital file-sharing
- or otherwise shall not be considered to be intended for or directed
- toward commercial advantage or private monetary compensation, provided
- there is no payment of any monetary compensation in con-nection with
- the exchange of copyrighted works.
- d. If You Distribute, or Publicly Perform the Work or any Adaptations or
- Collections, You must, unless a request has been made pursuant to
- Section 4(a), keep intact all copyright notices for the Work and
- provide, reasonable to the medium or means You are utilizing: (i) the
- name of the Original Author (or pseudonym, if applicable) if supplied,
- and/or if the Original Author and/or Licensor designate another party
- or parties (e.g., a sponsor institute, publishing entity, journal) for
- attribution ("Attribution Parties") in Licensor's copyright notice,
- terms of service or by other reasonable means, the name of such party
- or parties; (ii) the title of the Work if supplied; (iii) to the
- extent reasonably practicable, the URI, if any, that Licensor
- specifies to be associated with the Work, unless such URI does not
- refer to the copyright notice or licensing information for the Work;
- and, (iv) consistent with Section 3(b), in the case of an Adaptation,
- a credit identifying the use of the Work in the Adaptation (e.g.,
- "French translation of the Work by Original Author," or "Screenplay
- based on original Work by Original Author"). The credit required by
- this Section 4(d) may be implemented in any reasonable manner;
- provided, however, that in the case of a Adaptation or Collection, at
- a minimum such credit will appear, if a credit for all contributing
- authors of the Adaptation or Collection appears, then as part of these
- credits and in a manner at least as prominent as the credits for the
- other contributing authors. For the avoidance of doubt, You may only
- use the credit required by this Section for the purpose of attribution
- in the manner set out above and, by exercising Your rights under this
- License, You may not implicitly or explicitly assert or imply any
- connection with, sponsorship or endorsement by the Original Author,
- Licensor and/or Attribution Parties, as appropriate, of You or Your
- use of the Work, without the separate, express prior written
- permission of the Original Author, Licensor and/or Attribution
- Parties.
- e. For the avoidance of doubt:
-
- i. Non-waivable Compulsory License Schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme cannot be waived, the Licensor
- reserves the exclusive right to collect such royalties for any
- exercise by You of the rights granted under this License;
- ii. Waivable Compulsory License Schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme can be waived, the Licensor reserves
- the exclusive right to collect such royalties for any exercise by
- You of the rights granted under this License if Your exercise of
- such rights is for a purpose or use which is otherwise than
- noncommercial as permitted under Section 4(c) and otherwise waives
- the right to collect royalties through any statutory or compulsory
- licensing scheme; and,
- iii. Voluntary License Schemes. The Licensor reserves the right to
- collect royalties, whether individually or, in the event that the
- Licensor is a member of a collecting society that administers
- voluntary licensing schemes, via that society, from any exercise
- by You of the rights granted under this License that is for a
- purpose or use which is otherwise than noncommercial as permitted
- under Section 4(c).
- f. Except as otherwise agreed in writing by the Licensor or as may be
- otherwise permitted by applicable law, if You Reproduce, Distribute or
- Publicly Perform the Work either by itself or as part of any
- Adaptations or Collections, You must not distort, mutilate, modify or
- take other derogatory action in relation to the Work which would be
- prejudicial to the Original Author's honor or reputation. Licensor
- agrees that in those jurisdictions (e.g. Japan), in which any exercise
- of the right granted in Section 3(b) of this License (the right to
- make Adaptations) would be deemed to be a distortion, mutilation,
- modification or other derogatory action prejudicial to the Original
- Author's honor and reputation, the Licensor will waive or not assert,
- as appropriate, this Section, to the fullest extent permitted by the
- applicable national law, to enable You to reasonably exercise Your
- right under Section 3(b) of this License (right to make Adaptations)
- but not otherwise.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE
-FULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS
-AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE
-WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT
-LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
-PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,
-ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT
-DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED
-WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
-LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
-ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
-ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
-BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate
- automatically upon any breach by You of the terms of this License.
- Individuals or entities who have received Adaptations or Collections
- from You under this License, however, will not have their licenses
- terminated provided such individuals or entities remain in full
- compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
- survive any termination of this License.
- b. Subject to the above terms and conditions, the license granted here is
- perpetual (for the duration of the applicable copyright in the Work).
- Notwithstanding the above, Licensor reserves the right to release the
- Work under different license terms or to stop distributing the Work at
- any time; provided, however that any such election will not serve to
- withdraw this License (or any other license that has been, or is
- required to be, granted under the terms of this License), and this
- License will continue in full force and effect unless terminated as
- stated above.
-
-8. Miscellaneous
-
- a. Each time You Distribute or Publicly Perform the Work or a Collection,
- the Licensor offers to the recipient a license to the Work on the same
- terms and conditions as the license granted to You under this License.
- b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
- offers to the recipient a license to the original Work on the same
- terms and conditions as the license granted to You under this License.
- c. If any provision of this License is invalid or unenforceable under
- applicable law, it shall not affect the validity or enforceability of
- the remainder of the terms of this License, and without further action
- by the parties to this agreement, such provision shall be reformed to
- the minimum extent necessary to make such provision valid and
- enforceable.
- d. No term or provision of this License shall be deemed waived and no
- breach consented to unless such waiver or consent shall be in writing
- and signed by the party to be charged with such waiver or consent.
- e. This License constitutes the entire agreement between the parties with
- respect to the Work licensed here. There are no understandings,
- agreements or representations with respect to the Work not specified
- here. Licensor shall not be bound by any additional provisions that
- may appear in any communication from You. This License may not be
- modified without the mutual written agreement of the Licensor and You.
- f. The rights granted under, and the subject matter referenced, in this
- License were drafted utilizing the terminology of the Berne Convention
- for the Protection of Literary and Artistic Works (as amended on
- September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
- Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
- and the Universal Copyright Convention (as revised on July 24, 1971).
- These rights and subject matter take effect in the relevant
- jurisdiction in which the License terms are sought to be enforced
- according to the corresponding provisions of the implementation of
- those treaty provisions in the applicable national law. If the
- standard suite of rights granted under applicable copyright law
- includes additional rights not granted under this License, such
- additional rights are deemed to be included in the License; this
- License is not intended to restrict the license of any rights under
- applicable law.
-
-
-Creative Commons Notice
-
- Creative Commons is not a party to this License, and makes no warranty
- whatsoever in connection with the Work. Creative Commons will not be
- liable to You or any party on any legal theory for any damages
- whatsoever, including without limitation any general, special,
- incidental or consequential damages arising in connection to this
- license. Notwithstanding the foregoing two (2) sentences, if Creative
- Commons has expressly identified itself as the Licensor hereunder, it
- shall have all rights and obligations of Licensor.
-
- Except for the limited purpose of indicating to the public that the
- Work is licensed under the CCPL, Creative Commons does not authorize
- the use by either party of the trademark "Creative Commons" or any
- related trademark or logo of Creative Commons without the prior
- written consent of Creative Commons. Any permitted use will be in
- compliance with Creative Commons' then-current trademark usage
- guidelines, as may be published on its website or otherwise made
- available upon request from time to time. For the avoidance of doubt,
- this trademark restriction does not form part of this License.
-
- Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-SA-3.0-DE b/options/license/CC-BY-NC-SA-3.0-DE
deleted file mode 100644
index ab3813ddba..0000000000
--- a/options/license/CC-BY-NC-SA-3.0-DE
+++ /dev/null
@@ -1,125 +0,0 @@
-Creative Commons Namensnennung - Keine kommerzielle Nutzung - Weitergabe unter gleichen Bedingungen 3.0 Deutschland
-
- CREATIVE COMMONS IST KEINE RECHTSANWALTSKANZLEI UND LEISTET KEINE RECHTSBERATUNG. DIE BEREITSTELLUNG DIESER LIZENZ FÜHRT ZU KEINEM MANDATSVERHÄLTNIS. CREATIVE COMMONS STELLT DIESE INFORMATIONEN OHNE GEWÄHR ZUR VERFÜGUNG. CREATIVE COMMONS ÜBERNIMMT KEINE GEWÄHRLEISTUNG FÜR DIE GELIEFERTEN INFORMATIONEN UND SCHLIEßT DIE HAFTUNG FÜR SCHÄDEN AUS, DIE SICH AUS DEREN GEBRAUCH ERGEBEN.
-
-Lizenz
-
-DER GEGENSTAND DIESER LIZENZ (WIE UNTER "SCHUTZGEGENSTAND" DEFINIERT) WIRD UNTER DEN BEDINGUNGEN DIESER CREATIVE COMMONS PUBLIC LICENSE ("CCPL", "LIZENZ" ODER "LIZENZVERTRAG") ZUR VERFÜGUNG GESTELLT. DER SCHUTZGEGENSTAND IST DURCH DAS URHEBERRECHT UND/ODER ANDERE GESETZE GESCHÜTZT. JEDE FORM DER NUTZUNG DES SCHUTZGEGENSTANDES, DIE NICHT AUFGRUND DIESER LIZENZ ODER DURCH GESETZE GESTATTET IST, IST UNZULÄSSIG.
-
-DURCH DIE AUSÜBUNG EINES DURCH DIESE LIZENZ GEWÄHRTEN RECHTS AN DEM SCHUTZGEGENSTAND ERKLÄREN SIE SICH MIT DEN LIZENZBEDINGUNGEN RECHTSVERBINDLICH EINVERSTANDEN. SOWEIT DIESE LIZENZ ALS LIZENZVERTRAG ANZUSEHEN IST, GEWÄHRT IHNEN DER LIZENZGEBER DIE IN DER LIZENZ GENANNTEN RECHTE UNENTGELTLICH UND IM AUSTAUSCH DAFÜR, DASS SIE DAS GEBUNDENSEIN AN DIE LIZENZBEDINGUNGEN AKZEPTIEREN.
-
-1. Definitionen
-
- a. Der Begriff "Abwandlung" im Sinne dieser Lizenz bezeichnet das Ergebnis jeglicher Art von Veränderung des Schutzgegenstandes, solange die eigenpersönlichen Züge des Schutzgegenstandes darin nicht verblassen und daran eigene Schutzrechte entstehen. Das kann insbesondere eine Bearbeitung, Umgestaltung, Änderung, Anpassung, Übersetzung oder Heranziehung des Schutzgegenstandes zur Vertonung von Laufbildern sein. Nicht als Abwandlung des Schutzgegenstandes gelten seine Aufnahme in eine Sammlung oder ein Sammelwerk und die freie Benutzung des Schutzgegenstandes.
-
- b. Der Begriff "Sammelwerk" im Sinne dieser Lizenz meint eine Zusammenstellung von literarischen, künstlerischen oder wissenschaftlichen Inhalten, sofern diese Zusammenstellung aufgrund von Auswahl und Anordnung der darin enthaltenen selbständigen Elemente eine geistige Schöpfung darstellt, unabhängig davon, ob die Elemente systematisch oder methodisch angelegt und dadurch einzeln zugänglich sind oder nicht.
-
- c. "Verbreiten" im Sinne dieser Lizenz bedeutet, den Schutzgegenstand oder Abwandlungen im Original oder in Form von Vervielfältigungsstücken, mithin in körperlich fixierter Form der Öffentlichkeit anzubieten oder in Verkehr zu bringen.
-
- d. Unter "Lizenzelementen" werden im Sinne dieser Lizenz die folgenden übergeordneten Lizenzcharakteristika verstanden, die vom Lizenzgeber ausgewählt wurden und in der Bezeichnung der Lizenz zum Ausdruck kommen: "Namensnennung", "Keine kommerzielle Nutzung", "Weitergabe unter gleichen Bedingungen".
-
- e. Der "Lizenzgeber" im Sinne dieser Lizenz ist diejenige natürliche oder juristische Person oder Gruppe, die den Schutzgegenstand unter den Bedingungen dieser Lizenz anbietet und insoweit als Rechteinhaberin auftritt.
-
- f. "Rechteinhaber" im Sinne dieser Lizenz ist der Urheber des Schutzgegenstandes oder jede andere natürliche oder juristische Person oder Gruppe von Personen, die am Schutzgegenstand ein Immaterialgüterrecht erlangt hat, welches die in Abschnitt 3 genannten Handlungen erfasst und bei dem eine Einräumung von Nutzungsrechten oder eine Weiterübertragung an Dritte möglich ist.
-
- g. Der Begriff "Schutzgegenstand" bezeichnet in dieser Lizenz den literarischen, künstlerischen oder wissenschaftlichen Inhalt, der unter den Bedingungen dieser Lizenz angeboten wird. Das kann insbesondere eine persönliche geistige Schöpfung jeglicher Art, ein Werk der kleinen Münze, ein nachgelassenes Werk oder auch ein Lichtbild oder anderes Objekt eines verwandten Schutzrechts sein, unabhängig von der Art seiner Fixierung und unabhängig davon, auf welche Weise jeweils eine Wahrnehmung erfolgen kann, gleichviel ob in analoger oder digitaler Form. Soweit Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen, unterfallen auch sie dem Begriff "Schutzgegenstand" im Sinne dieser Lizenz.
-
- h. Mit "Sie" bzw. "Ihnen" ist die natürliche oder juristische Person gemeint, die in dieser Lizenz im Abschnitt 3 genannte Nutzungen des Schutzgegenstandes vornimmt und zuvor in Hinblick auf den Schutzgegenstand nicht gegen Bedingungen dieser Lizenz verstoßen oder aber die ausdrückliche Erlaubnis des Lizenzgebers erhalten hat, die durch diese Lizenz gewährten Nutzungsrechte trotz eines vorherigen Verstoßes auszuüben.
-
- i. Unter "Öffentlich Zeigen" im Sinne dieser Lizenz sind Veröffentlichungen und Präsentationen des Schutzgegenstandes zu verstehen, die für eine Mehrzahl von Mitgliedern der Öffentlichkeit bestimmt sind und in unkörperlicher Form mittels öffentlicher Wiedergabe in Form von Vortrag, Aufführung, Vorführung, Darbietung, Sendung, Weitersendung, zeit- und ortsunabhängiger Zugänglichmachung oder in körperlicher Form mittels Ausstellung erfolgen, unabhängig von bestimmten Veranstaltungen und unabhängig von den zum Einsatz kommenden Techniken und Verfahren, einschließlich drahtgebundener oder drahtloser Mittel und Einstellen in das Internet.
-
- j. "Vervielfältigen" im Sinne dieser Lizenz bedeutet, mittels beliebiger Verfahren Vervielfältigungsstücke des Schutzgegenstandes herzustellen, insbesondere durch Ton- oder Bildaufzeichnungen, und umfasst auch den Vorgang, erstmals körperliche Fixierungen des Schutzgegenstandes sowie Vervielfältigungsstücke dieser Fixierungen anzufertigen, sowie die Übertragung des Schutzgegenstandes auf einen Bild- oder Tonträger oder auf ein anderes elektronisches Medium, gleichviel ob in digitaler oder analoger Form.
-
-2. Schranken des Immaterialgüterrechts. Diese Lizenz ist in keiner Weise darauf gerichtet, Befugnisse zur Nutzung des Schutzgegenstandes zu vermindern, zu beschränken oder zu vereiteln, die Ihnen aufgrund der Schranken des Urheberrechts oder anderer Rechtsnormen bereits ohne Weiteres zustehen oder sich aus dem Fehlen eines immaterialgüterrechtlichen Schutzes ergeben.
-
-3. Einräumung von Nutzungsrechten. Unter den Bedingungen dieser Lizenz räumt Ihnen der Lizenzgeber - unbeschadet unverzichtbarer Rechte und vorbehaltlich des Abschnitts 4.f) - das vergütungsfreie, räumlich und zeitlich (für die Dauer des Schutzrechts am Schutzgegenstand) unbeschränkte einfache Recht ein, den Schutzgegenstand auf die folgenden Arten und Weisen zu nutzen ("unentgeltlich eingeräumtes einfaches Nutzungsrecht für jedermann"):
-
- a. Den Schutzgegenstand in beliebiger Form und Menge zu vervielfältigen, ihn in Sammelwerke zu integrieren und ihn als Teil solcher Sammelwerke zu vervielfältigen;
-
- b. Abwandlungen des Schutzgegenstandes anzufertigen, einschließlich Übersetzungen unter Nutzung jedweder Medien, sofern deutlich erkennbar gemacht wird, dass es sich um Abwandlungen handelt;
-
- c. den Schutzgegenstand, allein oder in Sammelwerke aufgenommen, öffentlich zu zeigen und zu verbreiten;
-
- d. Abwandlungen des Schutzgegenstandes zu veröffentlichen, öffentlich zu zeigen und zu verbreiten.
-
-Das vorgenannte Nutzungsrecht wird für alle bekannten sowie für alle noch nicht bekannten Nutzungsarten eingeräumt. Es beinhaltet auch das Recht, solche Änderungen am Schutzgegenstand vorzunehmen, die für bestimmte nach dieser Lizenz zulässige Nutzungen technisch erforderlich sind. Alle sonstigen Rechte, die über diesen Abschnitt hinaus nicht ausdrücklich durch den Lizenzgeber eingeräumt werden, bleiben diesem allein vorbehalten. Soweit Datenbanken oder Zusammenstellungen von Daten Schutzgegenstand dieser Lizenz oder Teil dessen sind und einen immaterialgüterrechtlichen Schutz eigener Art genießen, verzichtet der Lizenzgeber auf sämtliche aus diesem Schutz resultierenden Rechte.
-
-4. Bedingungen. Die Einräumung des Nutzungsrechts gemäß Abschnitt 3 dieser Lizenz erfolgt ausdrücklich nur unter den folgenden Bedingungen:
-
- a. Sie dürfen den Schutzgegenstand ausschließlich unter den Bedingungen dieser Lizenz verbreiten oder öffentlich zeigen. Sie müssen dabei stets eine Kopie dieser Lizenz oder deren vollständige Internetadresse in Form des Uniform-Resource-Identifier (URI) beifügen. Sie dürfen keine Vertrags- oder Nutzungsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch diese Lizenz gewährten Rechte beschränken. Sie dürfen den Schutzgegenstand nicht unterlizenzieren. Bei jeder Kopie des Schutzgegenstandes, die Sie verbreiten oder öffentlich zeigen, müssen Sie alle Hinweise unverändert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Wenn Sie den Schutzgegenstand verbreiten oder öffentlich zeigen, dürfen Sie (in Bezug auf den Schutzgegenstand) keine technischen Maßnahmen ergreifen, die den Nutzer des Schutzgegenstandes in der Ausübung der ihm durch diese Lizenz gewährten Rechte behindern können. Dieser Abschnitt 4.a) gilt auch für den Fall, dass der Schutzgegenstand einen Bestandteil eines Sammelwerkes bildet, was jedoch nicht bedeutet, dass das Sammelwerk insgesamt dieser Lizenz unterstellt werden muss. Sofern Sie ein Sammelwerk erstellen, müssen Sie auf die Mitteilung eines Lizenzgebers hin aus dem Sammelwerk die in Abschnitt 4.d) aufgezählten Hinweise entfernen. Wenn Sie eine Abwandlung vornehmen, müssen Sie auf die Mitteilung eines Lizenzgebers hin von der Abwandlung die in Abschnitt 4.d) aufgezählten Hinweise entfernen.
-
- b. Sie dürfen eine Abwandlung ausschließlich unter den Bedingungen
-
- i. dieser Lizenz,
-
- ii. einer späteren Version dieser Lizenz mit denselben Lizenzelementen;
-
- iii. einer rechtsordnungsspezifischen Creative-Commons-Lizenz mit denselben Lizenzelementen ab Version 3.0 aufwärts (z.B. Namensnennung - Keine kommerzielle Nutzung - Weitergabe unter gleichen Bedingungen 3.0 US) oder
-
- iv. der Creative-Commons-Unported-Lizenz mit denselben Lizenzelementen ab Version 3.0 aufwärts
-
- verbreiten oder öffentlich zeigen ("Verwendbare Lizenz").
-
- Sie müssen stets eine Kopie der verwendbaren Lizenz oder deren vollständige Internetadresse in Form des Uniform-Resource-Identifier (URI) beifügen, wenn Sie die Abwandlung verbreiten oder öffentlich zeigen. Sie dürfen keine Vertrags- oder Nutzungsbedingungen anbieten oder fordern, die die Bedingungen der verwendbaren Lizenz oder die durch sie gewährten Rechte beschränken. Bei jeder Abwandlung, die Sie verbreiten oder öffentlich zeigen, müssen Sie alle Hinweise auf die verwendbare Lizenz und den Haftungsausschluss unverändert lassen. Wenn Sie die Abwandlung verbreiten oder öffentlich zeigen, dürfen Sie (in Bezug auf die Abwandlung) keine technischen Maßnahmen ergreifen, die den Nutzer der Abwandlung in der Ausübung der ihm durch die verwendbare Lizenz gewährten Rechte behindern können. Dieser Abschnitt 4.b) gilt auch für den Fall, dass die Abwandlung einen Bestandteil eines Sammelwerkes bildet, was jedoch nicht bedeutet, dass das Sammelwerk insgesamt der verwendbaren Lizenz unterstellt werden muss.
-
- c. Die Rechteeinräumung gemäß Abschnitt 3 gilt nur für Handlungen, die nicht vorrangig auf einen geschäftlichen Vorteil oder eine geldwerte Vergütung gerichtet sind ("nicht-kommerzielle Nutzung", "Non-commercial-Option"). Wird Ihnen in Zusammenhang mit dem Schutzgegenstand dieser Lizenz ein anderer Schutzgegenstand überlassen, ohne dass eine vertragliche Verpflichtung hierzu besteht (etwa im Wege von File-Sharing), so wird dies nicht als auf geschäftlichen Vorteil oder geldwerte Vergütung gerichtet angesehen, wenn in Verbindung mit dem Austausch der Schutzgegenstände tatsächlich keine Zahlung oder geldwerte Vergütung geleistet wird.
-
- d. Die Verbreitung und das öffentliche Zeigen des Schutzgegenstandes oder auf ihm aufbauender Abwandlungen oder ihn enthaltender Sammelwerke ist Ihnen nur unter der Bedingung gestattet, dass Sie, vorbehaltlich etwaiger Mitteilungen im Sinne von Abschnitt 4.a), alle dazu gehörenden Rechtevermerke unberührt lassen. Sie sind verpflichtet, die Rechteinhaberschaft in einer der Nutzung entsprechenden, angemessenen Form anzuerkennen, indem Sie - soweit bekannt - Folgendes angeben:
-
- i. Den Namen (oder das Pseudonym, falls ein solches verwendet wird) des Rechteinhabers und / oder, falls der Lizenzgeber im Rechtevermerk, in den Nutzungsbedingungen oder auf andere angemessene Weise eine Zuschreibung an Dritte vorgenommen hat (z.B. an eine Stiftung, ein Verlagshaus oder eine Zeitung) ("Zuschreibungsempfänger"), Namen bzw. Bezeichnung dieses oder dieser Dritten;
-
- ii. den Titel des Inhaltes;
-
- iii. in einer praktikablen Form den Uniform-Resource-Identifier (URI, z.B. Internetadresse), den der Lizenzgeber zum Schutzgegenstand angegeben hat, es sei denn, dieser URI verweist nicht auf den Rechtevermerk oder die Lizenzinformationen zum Schutzgegenstand;
-
- iv. und im Falle einer Abwandlung des Schutzgegenstandes in Übereinstimmung mit Abschnitt 3.b) einen Hinweis darauf, dass es sich um eine Abwandlung handelt.
-
- Die nach diesem Abschnitt 4.d) erforderlichen Angaben können in jeder angemessenen Form gemacht werden; im Falle einer Abwandlung des Schutzgegenstandes oder eines Sammelwerkes müssen diese Angaben das Minimum darstellen und bei gemeinsamer Nennung mehrerer Rechteinhaber dergestalt erfolgen, dass sie zumindest ebenso hervorgehoben sind wie die Hinweise auf die übrigen Rechteinhaber. Die Angaben nach diesem Abschnitt dürfen Sie ausschließlich zur Angabe der Rechteinhaberschaft in der oben bezeichneten Weise verwenden. Durch die Ausübung Ihrer Rechte aus dieser Lizenz dürfen Sie ohne eine vorherige, separat und schriftlich vorliegende Zustimmung des Lizenzgebers und / oder des Zuschreibungsempfängers weder explizit noch implizit irgendeine Verbindung zum Lizenzgeber oder Zuschreibungsempfänger und ebenso wenig eine Unterstützung oder Billigung durch ihn andeuten.
-
- e. Die oben unter 4.a) bis d) genannten Einschränkungen gelten nicht für solche Teile des Schutzgegenstandes, die allein deshalb unter den Schutzgegenstandsbegriff fallen, weil sie als Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen.
-
- f. Bezüglich Vergütung für die Nutzung des Schutzgegenstandes gilt Folgendes:
-
- i. Unverzichtbare gesetzliche Vergütungsansprüche: Soweit unverzichtbare Vergütungsansprüche im Gegenzug für gesetzliche Lizenzen vorgesehen oder Pauschalabgabensysteme (zum Beispiel für Leermedien) vorhanden sind, behält sich der Lizenzgeber das ausschließliche Recht vor, die entsprechende Vergütung einzuziehen für jede Ausübung eines Rechts aus dieser Lizenz durch Sie.
-
- ii. Vergütung bei Zwangslizenzen: Sofern Zwangslizenzen außerhalb dieser Lizenz vorgesehen sind und zustande kommen, behält sich der Lizenzgeber das ausschließliche Recht auf Einziehung der entsprechenden Vergütung für den Fall vor, dass Sie eine Nutzung des Schutzgegenstandes für andere als die in Abschnitt 4.c) als nicht-kommerziell definierten Zwecke vornehmen, verzichtet für alle übrigen, lizenzgerechten Fälle von Nutzung jedoch auf jegliche Vergütung.
-
- iii. Vergütung in sonstigen Fällen: Bezüglich lizenzgerechter Nutzung des Schutzgegenstandes durch Sie, die nicht unter die beiden vorherigen Abschnitte (i) und (ii) fällt, verzichtet der Lizenzgeber auf jegliche Vergütung, unabhängig davon, ob eine Einziehung der Vergütung durch ihn selbst oder nur durch eine Verwertungsgesellschaft möglich wäre. Der Lizenzgeber behält sich jedoch das ausschließliche Recht auf Einziehung der entsprechenden Vergütung (durch ihn selbst oder eine Verwertungsgesellschaft) für den Fall vor, dass Sie eine Nutzung des Schutzgegenstandes für andere als die in Abschnitt 4.c) als nicht-kommerziell definierten Zwecke vornehmen.
-
- g. Persönlichkeitsrechte bleiben - soweit sie bestehen - von dieser Lizenz unberührt.
-
-5. Gewährleistung
-
-SOFERN KEINE ANDERS LAUTENDE, SCHRIFTLICHE VEREINBARUNG ZWISCHEN DEM LIZENZGEBER UND IHNEN GESCHLOSSEN WURDE UND SOWEIT MÄNGEL NICHT ARGLISTIG VERSCHWIEGEN WURDEN, BIETET DER LIZENZGEBER DEN SCHUTZGEGENSTAND UND DIE EINRÄUMUNG VON RECHTEN UNTER AUSSCHLUSS JEGLICHER GEWÄHRLEISTUNG AN UND ÜBERNIMMT WEDER AUSDRÜCKLICH NOCH KONKLUDENT GARANTIEN IRGENDEINER ART. DIES UMFASST INSBESONDERE DAS FREISEIN VON SACH- UND RECHTSMÄNGELN, UNABHÄNGIG VON DEREN ERKENNBARKEIT FÜR DEN LIZENZGEBER, DIE VERKEHRSFÄHIGKEIT DES SCHUTZGEGENSTANDES, SEINE VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK SOWIE DIE KORREKTHEIT VON BESCHREIBUNGEN. DIESE GEWÄHRLEISTUNGSBESCHRÄNKUNG GILT NICHT, SOWEIT MÄNGEL ZU SCHÄDEN DER IN ABSCHNITT 6 BEZEICHNETEN ART FÜHREN UND AUF SEITEN DES LIZENZGEBERS DAS JEWEILS GENANNTE VERSCHULDEN BZW. VERTRETENMÜSSEN EBENFALLS VORLIEGT.
-
-6. Haftungsbeschränkung
-
-DER LIZENZGEBER HAFTET IHNEN GEGENÜBER IN BEZUG AUF SCHÄDEN AUS DER VERLETZUNG DES LEBENS, DES KÖRPERS ODER DER GESUNDHEIT NUR, SOFERN IHM WENIGSTENS FAHRLÄSSIGKEIT VORZUWERFEN IST, FÜR SONSTIGE SCHÄDEN NUR BEI GROBER FAHRLÄSSIGKEIT ODER VORSATZ, UND ÜBERNIMMT DARÜBER HINAUS KEINERLEI FREIWILLIGE HAFTUNG.
-
-7. Erlöschen
-
- a. Diese Lizenz und die durch sie eingeräumten Nutzungsrechte erlöschen mit Wirkung für die Zukunft im Falle eines Verstoßes gegen die Lizenzbedingungen durch Sie, ohne dass es dazu der Kenntnis des Lizenzgebers vom Verstoß oder einer weiteren Handlung einer der Vertragsparteien bedarf. Mit natürlichen oder juristischen Personen, die Abwandlungen des Schutzgegenstandes oder diesen enthaltende Sammelwerke unter den Bedingungen dieser Lizenz von Ihnen erhalten haben, bestehen nachträglich entstandene Lizenzbeziehungen jedoch solange weiter, wie die genannten Personen sich ihrerseits an sämtliche Lizenzbedingungen halten. Darüber hinaus gelten die Ziffern 1, 2, 5, 6, 7, und 8 auch nach einem Erlöschen dieser Lizenz fort.
-
- b. Vorbehaltlich der oben genannten Bedingungen gilt diese Lizenz unbefristet bis der rechtliche Schutz für den Schutzgegenstand ausläuft. Davon abgesehen behält der Lizenzgeber das Recht, den Schutzgegenstand unter anderen Lizenzbedingungen anzubieten oder die eigene Weitergabe des Schutzgegenstandes jederzeit einzustellen, solange die Ausübung dieses Rechts nicht einer Kündigung oder einem Widerruf dieser Lizenz (oder irgendeiner Weiterlizenzierung, die auf Grundlage dieser Lizenz bereits erfolgt ist bzw. zukünftig noch erfolgen muss) dient und diese Lizenz unter Berücksichtigung der oben zum Erlöschen genannten Bedingungen vollumfänglich wirksam bleibt.
-
-8. Sonstige Bestimmungen
-
- a. Jedes Mal wenn Sie den Schutzgegenstand für sich genommen oder als Teil eines Sammelwerkes verbreiten oder öffentlich zeigen, bietet der Lizenzgeber dem Empfänger eine Lizenz zu den gleichen Bedingungen und im gleichen Umfang an, wie Ihnen in Form dieser Lizenz.
-
- b. Jedes Mal wenn Sie eine Abwandlung des Schutzgegenstandes verbreiten oder öffentlich zeigen, bietet der Lizenzgeber dem Empfänger eine Lizenz am ursprünglichen Schutzgegenstand zu den gleichen Bedingungen und im gleichen Umfang an, wie Ihnen in Form dieser Lizenz.
-
- c. Sollte eine Bestimmung dieser Lizenz unwirksam sein, so bleibt davon die Wirksamkeit der Lizenz im Übrigen unberührt.
-
- d. Keine Bestimmung dieser Lizenz soll als abbedungen und kein Verstoß gegen sie als zulässig gelten, solange die von dem Verzicht oder von dem Verstoß betroffene Seite nicht schriftlich zugestimmt hat.
-
- e. Diese Lizenz (zusammen mit in ihr ausdrücklich vorgesehenen Erlaubnissen, Mitteilungen und Zustimmungen, soweit diese tatsächlich vorliegen) stellt die vollständige Vereinbarung zwischen dem Lizenzgeber und Ihnen in Bezug auf den Schutzgegenstand dar. Es bestehen keine Abreden, Vereinbarungen oder Erklärungen in Bezug auf den Schutzgegenstand, die in dieser Lizenz nicht genannt sind. Rechtsgeschäftliche Änderungen des Verhältnisses zwischen dem Lizenzgeber und Ihnen sind nur über Modifikationen dieser Lizenz möglich. Der Lizenzgeber ist an etwaige zusätzliche, einseitig durch Sie übermittelte Bestimmungen nicht gebunden. Diese Lizenz kann nur durch schriftliche Vereinbarung zwischen Ihnen und dem Lizenzgeber modifiziert werden. Derlei Modifikationen wirken ausschließlich zwischen dem Lizenzgeber und Ihnen und wirken sich nicht auf die Dritten gemäß Ziffern 8.a) und b) angeboteten Lizenzen aus.
-
- f. Sofern zwischen Ihnen und dem Lizenzgeber keine anderweitige Vereinbarung getroffen wurde und soweit Wahlfreiheit besteht, findet auf diesen Lizenzvertrag das Recht der Bundesrepublik Deutschland Anwendung.
-
-Creative Commons Notice
-
-Creative Commons ist nicht Partei dieser Lizenz und übernimmt keinerlei Gewähr oder dergleichen in Bezug auf den Schutzgegenstand. Creative Commons haftet Ihnen oder einer anderen Partei unter keinem rechtlichen Gesichtspunkt für irgendwelche Schäden, die - abstrakt oder konkret, zufällig oder vorhersehbar - im Zusammenhang mit dieser Lizenz entstehen. Unbeschadet der vorangegangen beiden Sätze, hat Creative Commons alle Rechte und Pflichten eines Lizenzgebers, wenn es sich ausdrücklich als Lizenzgeber im Sinne dieser Lizenz bezeichnet.
-
-Creative Commons gewährt den Parteien nur insoweit das Recht, das Logo und die Marke "Creative Commons" zu nutzen, als dies notwendig ist, um der Öffentlichkeit gegenüber kenntlich zu machen, dass der Schutzgegenstand unter einer CCPL steht. Ein darüber hinaus gehender Gebrauch der Marke "Creative Commons" oder einer verwandten Marke oder eines verwandten Logos bedarf der vorherigen schriftlichen Zustimmung von Creative Commons. Jeder erlaubte Gebrauch richtet sich nach der Creative Commons Marken-Nutzungs-Richtlinie in der jeweils aktuellen Fassung, die von Zeit zu Zeit auf der Website veröffentlicht oder auf andere Weise auf Anfrage zugänglich gemacht wird. Zur Klarstellung: Die genannten Einschränkungen der Markennutzung sind nicht Bestandteil dieser Lizenz.
-
-Creative Commons kann kontaktiert werden über https://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-SA-3.0-IGO b/options/license/CC-BY-NC-SA-3.0-IGO
deleted file mode 100644
index e03be0e88e..0000000000
--- a/options/license/CC-BY-NC-SA-3.0-IGO
+++ /dev/null
@@ -1,105 +0,0 @@
-Attribution-NonCommercial-ShareAlike 3.0 IGO
-
-CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. THE LICENSOR IS NOT NECESSARILY AN INTERGOVERNMENTAL ORGANIZATION (IGO), AS DEFINED IN THE LICENSE BELOW.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE (“LICENSEâ€). THE LICENSOR (DEFINED BELOW) HOLDS COPYRIGHT AND OTHER RIGHTS IN THE WORK. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION FOR YOUR ACCEPTANCE AND AGREEMENT TO THE TERMS OF THE LICENSE.
-
-1. Definitions
-
- a. "IGO" means, solely and exclusively for purposes of this License, an organization established by a treaty or other instrument governed by international law and possessing its own international legal personality. Other organizations established to carry out activities across national borders and that accordingly enjoy immunity from legal process are also IGOs for the sole and exclusive purposes of this License. IGOs may include as members, in addition to states, other entities.
-
- b. "Work" means the literary and/or artistic work eligible for copyright protection, whatever may be the mode or form of its expression including digital form, and offered under the terms of this License. It is understood that a database, which by reason of the selection and arrangement of its contents constitutes an intellectual creation, is considered a Work.
-
- c. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License and may be, but is not necessarily, an IGO.
-
- d. "You" means an individual or entity exercising rights under this License.
-
- e. "License Elements" means the following high-level license attributes as selected by the Licensor and indicated in the title of this License: Attribution, Noncommercial, ShareAlike.
-
- f. "Reproduce" means to make a copy of the Work in any manner or form, and by any means.
-
- g. "Distribute" means the activity of making publicly available the Work or Adaptation (or copies of the Work or Adaptation), as applicable, by sale, rental, public lending or any other known form of transfer of ownership or possession of the Work or copy of the Work.
-
- h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
-
- i. "Adaptation" means a work derived from or based upon the Work, or upon the Work and other pre-existing works. Adaptations may include works such as translations, derivative works, or any alterations and arrangements of any kind involving the Work. For purposes of this License, where the Work is a musical work, performance, or phonogram, the synchronization of the Work in timed-relation with a moving image is an Adaptation. For the avoidance of doubt, including the Work in a Collection is not an Adaptation.
-
- j. "Collection" means a collection of literary or artistic works or other works or subject matter other than works listed in Section 1(b) which by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. For the avoidance of doubt, a Collection will not be considered as an Adaptation.
-
-2. Scope of this License. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright protection.
-
-3. License Grant. Subject to the terms and conditions of this License, the Licensor hereby grants You a worldwide, royalty-free, non-exclusive license to exercise the rights in the Work as follows:
-
- a. to Reproduce, Distribute and Publicly Perform the Work, to incorporate the Work into one or more Collections, and to Reproduce, Distribute and Publicly Perform the Work as incorporated in the Collections; and,
-
- b. to create, Reproduce, Distribute and Publicly Perform Adaptations, provided that You clearly label, demarcate or otherwise identify that changes were made to the original Work.
-
-This License lasts for the duration of the term of the copyright in the Work licensed by the Licensor. The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by the Licensor are hereby reserved, including but not limited to the rights set forth in Section 4(e).
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work (see section 8(a)). You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from a Licensor You must, to the extent practicable, remove from the Collection any credit (inclusive of any logo, trademark, official mark or official emblem) as required by Section 4(d), as requested. If You create an Adaptation, upon notice from a Licensor You must, to the extent practicable, remove from the Adaptation any credit (inclusive of any logo, trademark, official mark or official emblem) as required by Section 4(d), as requested.
-
- b. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; or (iii) either the unported Creative Commons license or a ported Creative Commons license (either this or a later license version) containing the same License Elements (the “Applicable Licenseâ€). (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform. (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License. (III) You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform. (IV) When You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License.
-
- c. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be primarily intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
- d. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) any attributions that the Licensor indicates be associated with the Work as indicated in a copyright notice, (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that the Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, (iv) consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation. The credit required by this Section 4(d) may be implemented in any reasonable manner; provided, however, that in the case of an Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributors to the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Licensor or others designated for attribution, of You or Your use of the Work, without the separate, express prior written permission of the Licensor or such others.
-
- e. For the avoidance of doubt:
-
- i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-
- ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(c) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and,
-
- iii. Voluntary License Schemes. To the extent possible, the Licensor waives the right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary licensing scheme. In all other cases the Licensor expressly reserves the right to collect such royalties.
-
- f. Except as otherwise agreed in writing by the Licensor, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the honor or reputation of the Licensor where moral rights apply.
-
-5. Representations, Warranties and Disclaimer
-
-THE LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE.
-
-6. Limitation on Liability
-
-IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. Subject to the terms and conditions set forth in this License, the license granted here lasts for the duration of the term of the copyright in the Work licensed by the Licensor as stated in Section 3. Notwithstanding the above, the Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated below.
-
- b. If You fail to comply with this License, then this License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. Notwithstanding the foregoing, this License reinstates automatically as of the date the violation is cured, provided it is cured within 30 days of You discovering the violation, or upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 7(b) does not affect any rights the Licensor may have to seek remedies for violations of this License by You.
-
-8. Miscellaneous
-
- a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b Each time You Distribute or Publicly Perform an Adaptation, the Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the Licensor.
-
- e. This License constitutes the entire agreement between You and the Licensor with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. The Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
- f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). Interpretation of the scope of the rights granted by the Licensor and the conditions imposed on You under this License, this License, and the rights and conditions set forth herein shall be made with reference to copyright as determined in accordance with general principles of international law, including the above mentioned conventions.
-
- g. Nothing in this License constitutes or may be interpreted as a limitation upon or waiver of any privileges and immunities that may apply to the Licensor or You, including immunity from the legal processes of any jurisdiction, national court or other authority.
-
- h. Where the Licensor is an IGO, any and all disputes arising under this License that cannot be settled amicably shall be resolved in accordance with the following procedure:
-
- i. Pursuant to a notice of mediation communicated by reasonable means by either You or the Licensor to the other, the dispute shall be submitted to non-binding mediation conducted in accordance with rules designated by the Licensor in the copyright notice published with the Work, or if none then in accordance with those communicated in the notice of mediation. The language used in the mediation proceedings shall be English unless otherwise agreed.
-
- ii. If any such dispute has not been settled within 45 days following the date on which the notice of mediation is provided, either You or the Licensor may, pursuant to a notice of arbitration communicated by reasonable means to the other, elect to have the dispute referred to and finally determined by arbitration. The arbitration shall be conducted in accordance with the rules designated by the Licensor in the copyright notice published with the Work, or if none then in accordance with the UNCITRAL Arbitration Rules as then in force. The arbitral tribunal shall consist of a sole arbitrator and the language of the proceedings shall be English unless otherwise agreed. The place of arbitration shall be where the Licensor has its headquarters. The arbitral proceedings shall be conducted remotely (e.g., via telephone conference or written submissions) whenever practicable.
-
- iii. Interpretation of this License in any dispute submitted to mediation or arbitration shall be as set forth in Section 8(f), above.
-
-Creative Commons Notice
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of the Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
-
-Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-NC-SA-4.0 b/options/license/CC-BY-NC-SA-4.0
deleted file mode 100644
index baee873b67..0000000000
--- a/options/license/CC-BY-NC-SA-4.0
+++ /dev/null
@@ -1,170 +0,0 @@
-Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
-
- Creative Commons Corporation (“Creative Commonsâ€) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is†basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible.
-
-Using Creative Commons Public Licenses
-
-Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses.
-
-Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors.
-
-Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public.
-
-Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License
-
-By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
-
-Section 1 – Definitions.
-
- a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
-
- b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
-
- c. BY-NC-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License.
-
- d. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
-
- e. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
-
- f. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
-
- g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution, NonCommercial, and ShareAlike.
-
- h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
-
- i. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
-
- j. Licensor means the individual(s) or entity(ies) granting rights under this Public License.
-
- k. NonCommercial means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.
-
- l. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
-
- m. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
-
- n. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
-
-Section 2 – Scope.
-
- a. License grant.
-
- 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
-
- A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and
-
- B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only.
-
- 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
-
- 3. Term. The term of this Public License is specified in Section 6(a).
-
- 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
-
- 5. Downstream recipients.
-
- A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
-
- B. Additional offer from the Licensor – Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply.
-
- C. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
-
- 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
-
- b. Other rights.
-
- 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
-
- 2. Patent and trademark rights are not licensed under this Public License.
-
- 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.
-
-Section 3 – License Conditions.
-
-Your exercise of the Licensed Rights is expressly made subject to the following conditions.
-
- a. Attribution.
-
- 1. If You Share the Licensed Material (including in modified form), You must:
-
- A. retain the following if it is supplied by the Licensor with the Licensed Material:
-
- i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
-
- ii. a copyright notice;
-
- iii. a notice that refers to this Public License;
-
- iv. a notice that refers to the disclaimer of warranties;
-
- v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
-
- B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
-
- C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
-
- 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
-
- 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
-
- b. ShareAlike.In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply.
-
- 1. The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-NC-SA Compatible License.
-
- 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material.
-
- 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply.
-
-Section 4 – Sui Generis Database Rights.
-
-Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
-
- a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only;
-
- b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and
-
- c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
-For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
-
-Section 5 – Disclaimer of Warranties and Limitation of Liability.
-
- a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
-
- b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
-
- c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
-
-Section 6 – Term and Termination.
-
- a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
-
- b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
-
- 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
-
- 2. upon express reinstatement by the Licensor.
-
- For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
-
- c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
-
- d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
-
-Section 7 – Other Terms and Conditions.
-
- a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
-
- b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
-
-Section 8 – Interpretation.
-
- a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
-
- b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
-
- c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
-
- d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
-
-Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.†Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons†or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.
-
-Creative Commons may be contacted at creativecommons.org.
diff --git a/options/license/CC-BY-ND-1.0 b/options/license/CC-BY-ND-1.0
deleted file mode 100644
index 27972e4d93..0000000000
--- a/options/license/CC-BY-ND-1.0
+++ /dev/null
@@ -1,73 +0,0 @@
-Creative Commons Attribution-NoDerivs 1.0
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DRAFT LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any reference to such Licensor or the Original Author, as requested.
-
- b. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Collective Works, You must keep intact all copyright notices for the Work and give the Original Author credit reasonable to the medium or means You are utilizing by conveying the name (or pseudonym if applicable) of the Original Author if supplied; the title of the Work if supplied. Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
-5. Representations, Warranties and Disclaimer
-
- a. By offering the Work for public release under this License, Licensor represents and warrants that, to the best of Licensor's knowledge after reasonable inquiry:
-
- i. Licensor has secured all rights in the Work necessary to grant the license rights hereunder and to permit the lawful exercise of the rights granted hereunder without You having any obligation to pay any royalties, compulsory license fees, residuals or any other payments;
-
- ii. The Work does not infringe the copyright, trademark, publicity rights, common law rights or any other right of any third party or constitute defamation, invasion of privacy or other tortious injury to any third party.
-
- b. EXCEPT AS EXPRESSLY STATED IN THIS LICENSE OR OTHERWISE AGREED IN WRITING OR REQUIRED BY APPLICABLE LAW, THE WORK IS LICENSED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES REGARDING THE CONTENTS OR ACCURACY OF THE WORK.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, AND EXCEPT FOR DAMAGES ARISING FROM LIABILITY TO A THIRD PARTY RESULTING FROM BREACH OF THE WARRANTIES IN SECTION 5, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- c. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- d. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-ND-2.0 b/options/license/CC-BY-ND-2.0
deleted file mode 100644
index 3a0140868a..0000000000
--- a/options/license/CC-BY-ND-2.0
+++ /dev/null
@@ -1,75 +0,0 @@
-Creative Commons Attribution-NoDerivs 2.0
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works.
-
- c. For the avoidance of doubt, where the work is a musical composition:
-
- i. Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
-
- ii. Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
-
- d. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Derivative Works. All rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any reference to such Licensor or the Original Author, as requested.
-
- b. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or Collective Works, You must keep intact all copyright notices for the Work and give the Original Author credit reasonable to the medium or means You are utilizing by conveying the name (or pseudonym if applicable) of the Original Author if supplied; the title of the Work if supplied; and to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- c. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- d. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-ND-2.5 b/options/license/CC-BY-ND-2.5
deleted file mode 100644
index 2f6b29acfa..0000000000
--- a/options/license/CC-BY-ND-2.5
+++ /dev/null
@@ -1,75 +0,0 @@
-Creative Commons Attribution-NoDerivs 2.5
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works.
-
- c. For the avoidance of doubt, where the work is a musical composition:
-
- i. Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
-
- ii. Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
-
- d. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Derivative Works. All rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(b), as requested.
-
- b. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; and to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- c. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- d. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-ND-3.0 b/options/license/CC-BY-ND-3.0
deleted file mode 100644
index 2ec9718946..0000000000
--- a/options/license/CC-BY-ND-3.0
+++ /dev/null
@@ -1,293 +0,0 @@
-Creative Commons Legal Code
-
-Attribution-NoDerivs 3.0 Unported
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
- LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
- ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
- INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
- REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
- DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
-COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
-COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
-AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
-TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
-BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
-CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
-CONDITIONS.
-
-1. Definitions
-
- a. "Adaptation" means a work based upon the Work, or upon the Work and
- other pre-existing works, such as a translation, adaptation,
- derivative work, arrangement of music or other alterations of a
- literary or artistic work, or phonogram or performance and includes
- cinematographic adaptations or any other form in which the Work may be
- recast, transformed, or adapted including in any form recognizably
- derived from the original, except that a work that constitutes a
- Collection will not be considered an Adaptation for the purpose of
- this License. For the avoidance of doubt, where the Work is a musical
- work, performance or phonogram, the synchronization of the Work in
- timed-relation with a moving image ("synching") will be considered an
- Adaptation for the purpose of this License.
- b. "Collection" means a collection of literary or artistic works, such as
- encyclopedias and anthologies, or performances, phonograms or
- broadcasts, or other works or subject matter other than works listed
- in Section 1(f) below, which, by reason of the selection and
- arrangement of their contents, constitute intellectual creations, in
- which the Work is included in its entirety in unmodified form along
- with one or more other contributions, each constituting separate and
- independent works in themselves, which together are assembled into a
- collective whole. A work that constitutes a Collection will not be
- considered an Adaptation (as defined above) for the purposes of this
- License.
- c. "Distribute" means to make available to the public the original and
- copies of the Work through sale or other transfer of ownership.
- d. "Licensor" means the individual, individuals, entity or entities that
- offer(s) the Work under the terms of this License.
- e. "Original Author" means, in the case of a literary or artistic work,
- the individual, individuals, entity or entities who created the Work
- or if no individual or entity can be identified, the publisher; and in
- addition (i) in the case of a performance the actors, singers,
- musicians, dancers, and other persons who act, sing, deliver, declaim,
- play in, interpret or otherwise perform literary or artistic works or
- expressions of folklore; (ii) in the case of a phonogram the producer
- being the person or legal entity who first fixes the sounds of a
- performance or other sounds; and, (iii) in the case of broadcasts, the
- organization that transmits the broadcast.
- f. "Work" means the literary and/or artistic work offered under the terms
- of this License including without limitation any production in the
- literary, scientific and artistic domain, whatever may be the mode or
- form of its expression including digital form, such as a book,
- pamphlet and other writing; a lecture, address, sermon or other work
- of the same nature; a dramatic or dramatico-musical work; a
- choreographic work or entertainment in dumb show; a musical
- composition with or without words; a cinematographic work to which are
- assimilated works expressed by a process analogous to cinematography;
- a work of drawing, painting, architecture, sculpture, engraving or
- lithography; a photographic work to which are assimilated works
- expressed by a process analogous to photography; a work of applied
- art; an illustration, map, plan, sketch or three-dimensional work
- relative to geography, topography, architecture or science; a
- performance; a broadcast; a phonogram; a compilation of data to the
- extent it is protected as a copyrightable work; or a work performed by
- a variety or circus performer to the extent it is not otherwise
- considered a literary or artistic work.
- g. "You" means an individual or entity exercising rights under this
- License who has not previously violated the terms of this License with
- respect to the Work, or who has received express permission from the
- Licensor to exercise rights under this License despite a previous
- violation.
- h. "Publicly Perform" means to perform public recitations of the Work and
- to communicate to the public those public recitations, by any means or
- process, including by wire or wireless means or public digital
- performances; to make available to the public Works in such a way that
- members of the public may access these Works from a place and at a
- place individually chosen by them; to perform the Work to the public
- by any means or process and the communication to the public of the
- performances of the Work, including by public digital performance; to
- broadcast and rebroadcast the Work by any means including signs,
- sounds or images.
- i. "Reproduce" means to make copies of the Work by any means including
- without limitation by sound or visual recordings and the right of
- fixation and reproducing fixations of the Work, including storage of a
- protected performance or phonogram in digital form or other electronic
- medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce,
-limit, or restrict any uses free from copyright or rights arising from
-limitations or exceptions that are provided for in connection with the
-copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License,
-Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
-perpetual (for the duration of the applicable copyright) license to
-exercise the rights in the Work as stated below:
-
- a. to Reproduce the Work, to incorporate the Work into one or more
- Collections, and to Reproduce the Work as incorporated in the
- Collections; and,
- b. to Distribute and Publicly Perform the Work including as incorporated
- in Collections.
- c. For the avoidance of doubt:
-
- i. Non-waivable Compulsory License Schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme cannot be waived, the Licensor
- reserves the exclusive right to collect such royalties for any
- exercise by You of the rights granted under this License;
- ii. Waivable Compulsory License Schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme can be waived, the Licensor waives the
- exclusive right to collect such royalties for any exercise by You
- of the rights granted under this License; and,
- iii. Voluntary License Schemes. The Licensor waives the right to
- collect royalties, whether individually or, in the event that the
- Licensor is a member of a collecting society that administers
- voluntary licensing schemes, via that society, from any exercise
- by You of the rights granted under this License.
-
-The above rights may be exercised in all media and formats whether now
-known or hereafter devised. The above rights include the right to make
-such modifications as are technically necessary to exercise the rights in
-other media and formats, but otherwise you have no rights to make
-Adaptations. Subject to Section 8(f), all rights not expressly granted by
-Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made
-subject to and limited by the following restrictions:
-
- a. You may Distribute or Publicly Perform the Work only under the terms
- of this License. You must include a copy of, or the Uniform Resource
- Identifier (URI) for, this License with every copy of the Work You
- Distribute or Publicly Perform. You may not offer or impose any terms
- on the Work that restrict the terms of this License or the ability of
- the recipient of the Work to exercise the rights granted to that
- recipient under the terms of the License. You may not sublicense the
- Work. You must keep intact all notices that refer to this License and
- to the disclaimer of warranties with every copy of the Work You
- Distribute or Publicly Perform. When You Distribute or Publicly
- Perform the Work, You may not impose any effective technological
- measures on the Work that restrict the ability of a recipient of the
- Work from You to exercise the rights granted to that recipient under
- the terms of the License. This Section 4(a) applies to the Work as
- incorporated in a Collection, but this does not require the Collection
- apart from the Work itself to be made subject to the terms of this
- License. If You create a Collection, upon notice from any Licensor You
- must, to the extent practicable, remove from the Collection any credit
- as required by Section 4(b), as requested.
- b. If You Distribute, or Publicly Perform the Work or Collections, You
- must, unless a request has been made pursuant to Section 4(a), keep
- intact all copyright notices for the Work and provide, reasonable to
- the medium or means You are utilizing: (i) the name of the Original
- Author (or pseudonym, if applicable) if supplied, and/or if the
- Original Author and/or Licensor designate another party or parties
- (e.g., a sponsor institute, publishing entity, journal) for
- attribution ("Attribution Parties") in Licensor's copyright notice,
- terms of service or by other reasonable means, the name of such party
- or parties; (ii) the title of the Work if supplied; (iii) to the
- extent reasonably practicable, the URI, if any, that Licensor
- specifies to be associated with the Work, unless such URI does not
- refer to the copyright notice or licensing information for the Work.
- The credit required by this Section 4(b) may be implemented in any
- reasonable manner; provided, however, that in the case of a
- Collection, at a minimum such credit will appear, if a credit for all
- contributing authors of the Collection appears, then as part of these
- credits and in a manner at least as prominent as the credits for the
- other contributing authors. For the avoidance of doubt, You may only
- use the credit required by this Section for the purpose of attribution
- in the manner set out above and, by exercising Your rights under this
- License, You may not implicitly or explicitly assert or imply any
- connection with, sponsorship or endorsement by the Original Author,
- Licensor and/or Attribution Parties, as appropriate, of You or Your
- use of the Work, without the separate, express prior written
- permission of the Original Author, Licensor and/or Attribution
- Parties.
- c. Except as otherwise agreed in writing by the Licensor or as may be
- otherwise permitted by applicable law, if You Reproduce, Distribute or
- Publicly Perform the Work either by itself or as part of any
- Collections, You must not distort, mutilate, modify or take other
- derogatory action in relation to the Work which would be prejudicial
- to the Original Author's honor or reputation.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
-OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
-KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
-INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
-FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
-LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
-WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
-OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
-LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
-ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
-ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
-BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate
- automatically upon any breach by You of the terms of this License.
- Individuals or entities who have received Collections from You under
- this License, however, will not have their licenses terminated
- provided such individuals or entities remain in full compliance with
- those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any
- termination of this License.
- b. Subject to the above terms and conditions, the license granted here is
- perpetual (for the duration of the applicable copyright in the Work).
- Notwithstanding the above, Licensor reserves the right to release the
- Work under different license terms or to stop distributing the Work at
- any time; provided, however that any such election will not serve to
- withdraw this License (or any other license that has been, or is
- required to be, granted under the terms of this License), and this
- License will continue in full force and effect unless terminated as
- stated above.
-
-8. Miscellaneous
-
- a. Each time You Distribute or Publicly Perform the Work or a Collection,
- the Licensor offers to the recipient a license to the Work on the same
- terms and conditions as the license granted to You under this License.
- b. If any provision of this License is invalid or unenforceable under
- applicable law, it shall not affect the validity or enforceability of
- the remainder of the terms of this License, and without further action
- by the parties to this agreement, such provision shall be reformed to
- the minimum extent necessary to make such provision valid and
- enforceable.
- c. No term or provision of this License shall be deemed waived and no
- breach consented to unless such waiver or consent shall be in writing
- and signed by the party to be charged with such waiver or consent.
- d. This License constitutes the entire agreement between the parties with
- respect to the Work licensed here. There are no understandings,
- agreements or representations with respect to the Work not specified
- here. Licensor shall not be bound by any additional provisions that
- may appear in any communication from You. This License may not be
- modified without the mutual written agreement of the Licensor and You.
- e. The rights granted under, and the subject matter referenced, in this
- License were drafted utilizing the terminology of the Berne Convention
- for the Protection of Literary and Artistic Works (as amended on
- September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
- Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
- and the Universal Copyright Convention (as revised on July 24, 1971).
- These rights and subject matter take effect in the relevant
- jurisdiction in which the License terms are sought to be enforced
- according to the corresponding provisions of the implementation of
- those treaty provisions in the applicable national law. If the
- standard suite of rights granted under applicable copyright law
- includes additional rights not granted under this License, such
- additional rights are deemed to be included in the License; this
- License is not intended to restrict the license of any rights under
- applicable law.
-
-
-Creative Commons Notice
-
- Creative Commons is not a party to this License, and makes no warranty
- whatsoever in connection with the Work. Creative Commons will not be
- liable to You or any party on any legal theory for any damages
- whatsoever, including without limitation any general, special,
- incidental or consequential damages arising in connection to this
- license. Notwithstanding the foregoing two (2) sentences, if Creative
- Commons has expressly identified itself as the Licensor hereunder, it
- shall have all rights and obligations of Licensor.
-
- Except for the limited purpose of indicating to the public that the
- Work is licensed under the CCPL, Creative Commons does not authorize
- the use by either party of the trademark "Creative Commons" or any
- related trademark or logo of Creative Commons without the prior
- written consent of Creative Commons. Any permitted use will be in
- compliance with Creative Commons' then-current trademark usage
- guidelines, as may be published on its website or otherwise made
- available upon request from time to time. For the avoidance of doubt,
- this trademark restriction does not form part of this License.
-
- Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-ND-3.0-DE b/options/license/CC-BY-ND-3.0-DE
deleted file mode 100644
index 724e68ed1d..0000000000
--- a/options/license/CC-BY-ND-3.0-DE
+++ /dev/null
@@ -1,100 +0,0 @@
-Creative Commons Namensnennung - Keine Bearbeitungen 3.0 Deutschland
-
- CREATIVE COMMONS IST KEINE RECHTSANWALTSKANZLEI UND LEISTET KEINE RECHTSBERATUNG. DIE BEREITSTELLUNG DIESER LIZENZ FÜHRT ZU KEINEM MANDATSVERHÄLTNIS. CREATIVE COMMONS STELLT DIESE INFORMATIONEN OHNE GEWÄHR ZUR VERFÜGUNG. CREATIVE COMMONS ÜBERNIMMT KEINE GEWÄHRLEISTUNG FÜR DIE GELIEFERTEN INFORMATIONEN UND SCHLIEßT DIE HAFTUNG FÜR SCHÄDEN AUS, DIE SICH AUS DEREN GEBRAUCH ERGEBEN.
-
-Lizenz
-
-DER GEGENSTAND DIESER LIZENZ (WIE UNTER "SCHUTZGEGENSTAND" DEFINIERT) WIRD UNTER DEN BEDINGUNGEN DIESER CREATIVE COMMONS PUBLIC LICENSE ("CCPL", "LIZENZ" ODER "LIZENZVERTRAG") ZUR VERFÜGUNG GESTELLT. DER SCHUTZGEGENSTAND IST DURCH DAS URHEBERRECHT UND/ODER ANDERE GESETZE GESCHÜTZT. JEDE FORM DER NUTZUNG DES SCHUTZGEGENSTANDES, DIE NICHT AUFGRUND DIESER LIZENZ ODER DURCH GESETZE GESTATTET IST, IST UNZULÄSSIG.
-
-DURCH DIE AUSÜBUNG EINES DURCH DIESE LIZENZ GEWÄHRTEN RECHTS AN DEM SCHUTZGEGENSTAND ERKLÄREN SIE SICH MIT DEN LIZENZBEDINGUNGEN RECHTSVERBINDLICH EINVERSTANDEN. SOWEIT DIESE LIZENZ ALS LIZENZVERTRAG ANZUSEHEN IST, GEWÄHRT IHNEN DER LIZENZGEBER DIE IN DER LIZENZ GENANNTEN RECHTE UNENTGELTLICH UND IM AUSTAUSCH DAFÜR, DASS SIE DAS GEBUNDENSEIN AN DIE LIZENZBEDINGUNGEN AKZEPTIEREN.
-
-1. Definitionen
-
- a. Der Begriff "Abwandlung" im Sinne dieser Lizenz bezeichnet das Ergebnis jeglicher Art von Veränderung des Schutzgegenstandes, solange die eigenpersönlichen Züge des Schutzgegenstandes darin nicht verblassen und daran eigene Schutzrechte entstehen. Das kann insbesondere eine Bearbeitung, Umgestaltung, Änderung, Anpassung, Übersetzung oder Heranziehung des Schutzgegenstandes zur Vertonung von Laufbildern sein. Nicht als Abwandlung des Schutzgegenstandes gelten seine Aufnahme in eine Sammlung oder ein Sammelwerk und die freie Benutzung des Schutzgegenstandes.
-
- b. Der Begriff "Sammelwerk" im Sinne dieser Lizenz meint eine Zusammenstellung von literarischen, künstlerischen oder wissenschaftlichen Inhalten, sofern diese Zusammenstellung aufgrund von Auswahl und Anordnung der darin enthaltenen selbständigen Elemente eine geistige Schöpfung darstellt, unabhängig davon, ob die Elemente systematisch oder methodisch angelegt und dadurch einzeln zugänglich sind oder nicht.
-
- c. "Verbreiten" im Sinne dieser Lizenz bedeutet, den Schutzgegenstand im Original oder in Form von Vervielfältigungsstücken, mithin in körperlich fixierter Form der Öffentlichkeit anzubieten oder in Verkehr zu bringen.
-
- d. Der "Lizenzgeber" im Sinne dieser Lizenz ist diejenige natürliche oder juristische Person oder Gruppe, die den Schutzgegenstand unter den Bedingungen dieser Lizenz anbietet und insoweit als Rechteinhaberin auftritt.
-
- e. "Rechteinhaber" im Sinne dieser Lizenz ist der Urheber des Schutzgegenstandes oder jede andere natürliche oder juristische Person oder Gruppe von Personen, die am Schutzgegenstand ein Immaterialgüterrecht erlangt hat, welches die in Abschnitt 3 genannten Handlungen erfasst und bei dem eine Einräumung von Nutzungsrechten oder eine Weiterübertragung an Dritte möglich ist.
-
- f. Der Begriff "Schutzgegenstand" bezeichnet in dieser Lizenz den literarischen, künstlerischen oder wissenschaftlichen Inhalt, der unter den Bedingungen dieser Lizenz angeboten wird. Das kann insbesondere eine persönliche geistige Schöpfung jeglicher Art, ein Werk der kleinen Münze, ein nachgelassenes Werk oder auch ein Lichtbild oder anderes Objekt eines verwandten Schutzrechts sein, unabhängig von der Art seiner Fixierung und unabhängig davon, auf welche Weise jeweils eine Wahrnehmung erfolgen kann, gleichviel ob in analoger oder digitaler Form. Soweit Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen, unterfallen auch sie dem Begriff "Schutzgegenstand" im Sinne dieser Lizenz.
-
- g. Mit "Sie" bzw. "Ihnen" ist die natürliche oder juristische Person gemeint, die in dieser Lizenz im Abschnitt 3 genannte Nutzungen des Schutzgegenstandes vornimmt und zuvor in Hinblick auf den Schutzgegenstand nicht gegen Bedingungen dieser Lizenz verstoßen oder aber die ausdrückliche Erlaubnis des Lizenzgebers erhalten hat, die durch diese Lizenz gewährten Nutzungsrechte trotz eines vorherigen Verstoßes auszuüben.
-
- h. Unter "Öffentlich Zeigen" im Sinne dieser Lizenz sind Veröffentlichungen und Präsentationen des Schutzgegenstandes zu verstehen, die für eine Mehrzahl von Mitgliedern der Öffentlichkeit bestimmt sind und in unkörperlicher Form mittels öffentlicher Wiedergabe in Form von Vortrag, Aufführung, Vorführung, Darbietung, Sendung, Weitersendung, zeit- und ortsunabhängiger Zugänglichmachung oder in körperlicher Form mittels Ausstellung erfolgen, unabhängig von bestimmten Veranstaltungen und unabhängig von den zum Einsatz kommenden Techniken und Verfahren, einschließlich drahtgebundener oder drahtloser Mittel und Einstellen in das Internet.
-
- i. "Vervielfältigen" im Sinne dieser Lizenz bedeutet, mittels beliebiger Verfahren Vervielfältigungsstücke des Schutzgegenstandes herzustellen, insbesondere durch Ton- oder Bildaufzeichnungen, und umfasst auch den Vorgang, erstmals körperliche Fixierungen des Schutzgegenstandes sowie Vervielfältigungsstücke dieser Fixierungen anzufertigen, sowie die Übertragung des Schutzgegenstandes auf einen Bild- oder Tonträger oder auf ein anderes elektronisches Medium, gleichviel ob in digitaler oder analoger Form.
-
-2. Schranken des Immaterialgüterrechts. Diese Lizenz ist in keiner Weise darauf gerichtet, Befugnisse zur Nutzung des Schutzgegenstandes zu vermindern, zu beschränken oder zu vereiteln, die Ihnen aufgrund der Schranken des Urheberrechts oder anderer Rechtsnormen bereits ohne Weiteres zustehen oder sich aus dem Fehlen eines immaterialgüterrechtlichen Schutzes ergeben.
-
-3. Einräumung von Nutzungsrechten. Unter den Bedingungen dieser Lizenz räumt Ihnen der Lizenzgeber - unbeschadet unverzichtbarer Rechte und vorbehaltlich des Abschnitts 3.c) - das vergütungsfreie, räumlich und zeitlich (für die Dauer des Schutzrechts am Schutzgegenstand) unbeschränkte einfache Recht ein, den Schutzgegenstand auf die folgenden Arten und Weisen zu nutzen ("unentgeltlich eingeräumtes einfaches Nutzungsrecht für jedermann"):
-
- a. Den Schutzgegenstand in beliebiger Form und Menge zu vervielfältigen, ihn in Sammelwerke zu integrieren und ihn als Teil solcher Sammelwerke zu vervielfältigen;
-
- b. den Schutzgegenstand, allein oder in Sammelwerke aufgenommen, öffentlich zu zeigen und zu verbreiten.
-
- c. Bezüglich Vergütung für die Nutzung des Schutzgegenstandes gilt Folgendes:
-
- i. Unverzichtbare gesetzliche Vergütungsansprüche: Soweit unverzichtbare Vergütungsansprüche im Gegenzug für gesetzliche Lizenzen vorgesehen oder Pauschalabgabensysteme (zum Beispiel für Leermedien) vorhanden sind, behält sich der Lizenzgeber das ausschließliche Recht vor, die entsprechende Vergütung einzuziehen für jede Ausübung eines Rechts aus dieser Lizenz durch Sie.
-
- ii. Vergütung bei Zwangslizenzen: Sofern Zwangslizenzen außerhalb dieser Lizenz vorgesehen sind und zustande kommen, verzichtet der Lizenzgeber für alle Fälle einer lizenzgerechten Nutzung des Schutzgegenstandes durch Sie auf jegliche Vergütung.
-
- iii. Vergütung in sonstigen Fällen: Bezüglich lizenzgerechter Nutzung des Schutzgegenstandes durch Sie, die nicht unter die beiden vorherigen Abschnitte (i) und (ii) fällt, verzichtet der Lizenzgeber auf jegliche Vergütung, unabhängig davon, ob eine Einziehung der Vergütung durch ihn selbst oder nur durch eine Verwertungsgesellschaft möglich wäre.
-
-Das vorgenannte Nutzungsrecht wird für alle bekannten sowie für alle noch nicht bekannten Nutzungsarten eingeräumt. Es beinhaltet auch das Recht, solche Änderungen am Schutzgegenstand vorzunehmen, die für bestimmte nach dieser Lizenz zulässige Nutzungen technisch erforderlich sind. Weitergehende Änderungen oder Abwandlungen sind jedoch untersagt. Alle sonstigen Rechte, die über diesen Abschnitt hinaus nicht ausdrücklich durch den Lizenzgeber eingeräumt werden, bleiben diesem allein vorbehalten. Soweit Datenbanken oder Zusammenstellungen von Daten Schutzgegenstand dieser Lizenz oder Teil dessen sind und einen immaterialgüterrechtlichen Schutz eigener Art genießen, verzichtet der Lizenzgeber auf sämtliche aus diesem Schutz resultierenden Rechte.
-
-4. Bedingungen. Die Einräumung des Nutzungsrechts gemäß Abschnitt 3 dieser Lizenz erfolgt ausdrücklich nur unter den folgenden Bedingungen:
-
- a. Sie dürfen den Schutzgegenstand ausschließlich unter den Bedingungen dieser Lizenz verbreiten oder öffentlich zeigen. Sie müssen dabei stets eine Kopie dieser Lizenz oder deren vollständige Internetadresse in Form des Uniform-Resource-Identifier (URI) beifügen. Sie dürfen keine Vertrags- oder Nutzungsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch diese Lizenz gewährten Rechte beschränken. Sie dürfen den Schutzgegenstand nicht unterlizenzieren. Bei jeder Kopie des Schutzgegenstandes, die Sie verbreiten oder öffentlich zeigen, müssen Sie alle Hinweise unverändert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Wenn Sie den Schutzgegenstand verbreiten oder öffentlich zeigen, dürfen Sie (in Bezug auf den Schutzgegenstand) keine technischen Maßnahmen ergreifen, die den Nutzer des Schutzgegenstandes in der Ausübung der ihm durch diese Lizenz gewährten Rechte behindern können. Dieser Abschnitt 4.a) gilt auch für den Fall, dass der Schutzgegenstand einen Bestandteil eines Sammelwerkes bildet, was jedoch nicht bedeutet, dass das Sammelwerk insgesamt dieser Lizenz unterstellt werden muss. Sofern Sie ein Sammelwerk erstellen, müssen Sie auf die Mitteilung eines Lizenzgebers hin aus dem Sammelwerk die in Abschnitt 4.b) aufgezählten Hinweise entfernen.
-
- b. Die Verbreitung und das öffentliche Zeigen des Schutzgegenstandes oder ihn enthaltender Sammelwerke ist Ihnen nur unter der Bedingung gestattet, dass Sie, vorbehaltlich etwaiger Mitteilungen im Sinne von Abschnitt 4.a), alle dazu gehörenden Rechtevermerke unberührt lassen. Sie sind verpflichtet, die Rechteinhaberschaft in einer der Nutzung entsprechenden, angemessenen Form anzuerkennen, indem Sie - soweit bekannt - Folgendes angeben:
-
- i. Den Namen (oder das Pseudonym, falls ein solches verwendet wird) des Rechteinhabers und / oder, falls der Lizenzgeber im Rechtevermerk, in den Nutzungsbedingungen oder auf andere angemessene Weise eine Zuschreibung an Dritte vorgenommen hat (z.B. an eine Stiftung, ein Verlagshaus oder eine Zeitung) ("Zuschreibungsempfänger"), Namen bzw. Bezeichnung dieses oder dieser Dritten;
-
- ii. den Titel des Inhaltes;
-
- iii. in einer praktikablen Form den Uniform-Resource-Identifier (URI, z.B. Internetadresse), den der Lizenzgeber zum Schutzgegenstand angegeben hat, es sei denn, dieser URI verweist nicht auf den Rechtevermerk oder die Lizenzinformationen zum Schutzgegenstand.
-
- Die nach diesem Abschnitt 4.b) erforderlichen Angaben können in jeder angemessenen Form gemacht werden; im Falle eines Sammelwerkes müssen diese Angaben das Minimum darstellen und bei gemeinsamer Nennung mehrerer Rechteinhaber dergestalt erfolgen, dass sie zumindest ebenso hervorgehoben sind wie die Hinweise auf die übrigen Rechteinhaber. Die Angaben nach diesem Abschnitt dürfen Sie ausschließlich zur Angabe der Rechteinhaberschaft in der oben bezeichneten Weise verwenden. Durch die Ausübung Ihrer Rechte aus dieser Lizenz dürfen Sie ohne eine vorherige, separat und schriftlich vorliegende Zustimmung des Lizenzgebers und / oder des Zuschreibungsempfängers weder explizit noch implizit irgendeine Verbindung zum Lizenzgeber oder Zuschreibungsempfänger und ebenso wenig eine Unterstützung oder Billigung durch ihn andeuten.
-
- c. Die oben unter 4.a) und b) genannten Einschränkungen gelten nicht für solche Teile des Schutzgegenstandes, die allein deshalb unter den Schutzgegenstandsbegriff fallen, weil sie als Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen.
-
- d. Persönlichkeitsrechte bleiben - soweit sie bestehen - von dieser Lizenz unberührt.
-
-5. Gewährleistung
-
-SOFERN KEINE ANDERS LAUTENDE, SCHRIFTLICHE VEREINBARUNG ZWISCHEN DEM LIZENZGEBER UND IHNEN GESCHLOSSEN WURDE UND SOWEIT MÄNGEL NICHT ARGLISTIG VERSCHWIEGEN WURDEN, BIETET DER LIZENZGEBER DEN SCHUTZGEGENSTAND UND DIE EINRÄUMUNG VON RECHTEN UNTER AUSSCHLUSS JEGLICHER GEWÄHRLEISTUNG AN UND ÜBERNIMMT WEDER AUSDRÜCKLICH NOCH KONKLUDENT GARANTIEN IRGENDEINER ART. DIES UMFASST INSBESONDERE DAS FREISEIN VON SACH- UND RECHTSMÄNGELN, UNABHÄNGIG VON DEREN ERKENNBARKEIT FÜR DEN LIZENZGEBER, DIE VERKEHRSFÄHIGKEIT DES SCHUTZGEGENSTANDES, SEINE VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK SOWIE DIE KORREKTHEIT VON BESCHREIBUNGEN. DIESE GEWÄHRLEISTUNGSBESCHRÄNKUNG GILT NICHT, SOWEIT MÄNGEL ZU SCHÄDEN DER IN ABSCHNITT 6 BEZEICHNETEN ART FÜHREN UND AUF SEITEN DES LIZENZGEBERS DAS JEWEILS GENANNTE VERSCHULDEN BZW. VERTRETENMÜSSEN EBENFALLS VORLIEGT.
-
-6. Haftungsbeschränkung
-
-DER LIZENZGEBER HAFTET IHNEN GEGENÜBER IN BEZUG AUF SCHÄDEN AUS DER VERLETZUNG DES LEBENS, DES KÖRPERS ODER DER GESUNDHEIT NUR, SOFERN IHM WENIGSTENS FAHRLÄSSIGKEIT VORZUWERFEN IST, FÜR SONSTIGE SCHÄDEN NUR BEI GROBER FAHRLÄSSIGKEIT ODER VORSATZ, UND ÜBERNIMMT DARÜBER HINAUS KEINERLEI FREIWILLIGE HAFTUNG.
-
-7. Erlöschen
-
- a. Diese Lizenz und die durch sie eingeräumten Nutzungsrechte erlöschen mit Wirkung für die Zukunft im Falle eines Verstoßes gegen die Lizenzbedingungen durch Sie, ohne dass es dazu der Kenntnis des Lizenzgebers vom Verstoß oder einer weiteren Handlung einer der Vertragsparteien bedarf. Mit natürlichen oder juristischen Personen, die den Schutzgegenstand enthaltende Sammelwerke unter den Bedingungen dieser Lizenz von Ihnen erhalten haben, bestehen nachträglich entstandene Lizenzbeziehungen jedoch solange weiter, wie die genannten Personen sich ihrerseits an sämtliche Lizenzbedingungen halten. Darüber hinaus gelten die Ziffern 1, 2, 5, 6, 7, und 8 auch nach einem Erlöschen dieser Lizenz fort.
-
- b. Vorbehaltlich der oben genannten Bedingungen gilt diese Lizenz unbefristet bis der rechtliche Schutz für den Schutzgegenstand ausläuft. Davon abgesehen behält der Lizenzgeber das Recht, den Schutzgegenstand unter anderen Lizenzbedingungen anzubieten oder die eigene Weitergabe des Schutzgegenstandes jederzeit einzustellen, solange die Ausübung dieses Rechts nicht einer Kündigung oder einem Widerruf dieser Lizenz (oder irgendeiner Weiterlizenzierung, die auf Grundlage dieser Lizenz bereits erfolgt ist bzw. zukünftig noch erfolgen muss) dient und diese Lizenz unter Berücksichtigung der oben zum Erlöschen genannten Bedingungen vollumfänglich wirksam bleibt.
-
-8. Sonstige Bestimmungen
-
- a. Jedes Mal wenn Sie den Schutzgegenstand für sich genommen oder als Teil eines Sammelwerkes verbreiten oder öffentlich zeigen, bietet der Lizenzgeber dem Empfänger eine Lizenz zu den gleichen Bedingungen und im gleichen Umfang an, wie Ihnen in Form dieser Lizenz.
-
- b. Sollte eine Bestimmung dieser Lizenz unwirksam sein, so bleibt davon die Wirksamkeit der Lizenz im Übrigen unberührt.
-
- c. Keine Bestimmung dieser Lizenz soll als abbedungen und kein Verstoß gegen sie als zulässig gelten, solange die von dem Verzicht oder von dem Verstoß betroffene Seite nicht schriftlich zugestimmt hat.
-
- d. Diese Lizenz (zusammen mit in ihr ausdrücklich vorgesehenen Erlaubnissen, Mitteilungen und Zustimmungen, soweit diese tatsächlich vorliegen) stellt die vollständige Vereinbarung zwischen dem Lizenzgeber und Ihnen in Bezug auf den Schutzgegenstand dar. Es bestehen keine Abreden, Vereinbarungen oder Erklärungen in Bezug auf den Schutzgegenstand, die in dieser Lizenz nicht genannt sind. Rechtsgeschäftliche Änderungen des Verhältnisses zwischen dem Lizenzgeber und Ihnen sind nur über Modifikationen dieser Lizenz möglich. Der Lizenzgeber ist an etwaige zusätzliche, einseitig durch Sie übermittelte Bestimmungen nicht gebunden. Diese Lizenz kann nur durch schriftliche Vereinbarung zwischen Ihnen und dem Lizenzgeber modifiziert werden. Derlei Modifikationen wirken ausschließlich zwischen dem Lizenzgeber und Ihnen und wirken sich nicht auf die Dritten gemäß Ziffern 8.a) angeboteten Lizenzen aus.
-
- e. Sofern zwischen Ihnen und dem Lizenzgeber keine anderweitige Vereinbarung getroffen wurde und soweit Wahlfreiheit besteht, findet auf diesen Lizenzvertrag das Recht der Bundesrepublik Deutschland Anwendung.
-
-
-Creative Commons Notice
-
-Creative Commons ist nicht Partei dieser Lizenz und übernimmt keinerlei Gewähr oder dergleichen in Bezug auf den Schutzgegenstand. Creative Commons haftet Ihnen oder einer anderen Partei unter keinem rechtlichen Gesichtspunkt für irgendwelche Schäden, die - abstrakt oder konkret, zufällig oder vorhersehbar - im Zusammenhang mit dieser Lizenz entstehen. Unbeschadet der vorangegangen beiden Sätze, hat Creative Commons alle Rechte und Pflichten eines Lizenzgebers, wenn es sich ausdrücklich als Lizenzgeber im Sinne dieser Lizenz bezeichnet.
-
-Creative Commons gewährt den Parteien nur insoweit das Recht, das Logo und die Marke "Creative Commons" zu nutzen, als dies notwendig ist, um der Öffentlichkeit gegenüber kenntlich zu machen, dass der Schutzgegenstand unter einer CCPL steht. Ein darüber hinaus gehender Gebrauch der Marke "Creative Commons" oder einer verwandten Marke oder eines verwandten Logos bedarf der vorherigen schriftlichen Zustimmung von Creative Commons. Jeder erlaubte Gebrauch richtet sich nach der Creative Commons Marken-Nutzungs-Richtlinie in der jeweils aktuellen Fassung, die von Zeit zu Zeit auf der Website veröffentlicht oder auf andere Weise auf Anfrage zugänglich gemacht wird. Zur Klarstellung: Die genannten Einschränkungen der Markennutzung sind nicht Bestandteil dieser Lizenz.
-
-Creative Commons kann kontaktiert werden über https://creativecommons.org/.
diff --git a/options/license/CC-BY-ND-4.0 b/options/license/CC-BY-ND-4.0
deleted file mode 100644
index 09a21c7358..0000000000
--- a/options/license/CC-BY-ND-4.0
+++ /dev/null
@@ -1,154 +0,0 @@
-Creative Commons Attribution-NoDerivatives 4.0 International
-
- Creative Commons Corporation (“Creative Commonsâ€) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is†basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible.
-
-Using Creative Commons Public Licenses
-
-Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses.
-
-Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors.
-
-Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public.
-
-Creative Commons Attribution-NoDerivatives 4.0 International Public License
-
-By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NoDerivatives 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
-
-Section 1 – Definitions.
-
- a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
-
- b. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
-
- c. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
-
- d. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
-
- e. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
-
- f. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
-
- g. Licensor means the individual(s) or entity(ies) granting rights under this Public License.
-
- h. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
-
- i. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
-
- j. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
-
-Section 2 – Scope.
-
- a. License grant.
-
- 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
-
- A. reproduce and Share the Licensed Material, in whole or in part; and
-
- B. produce and reproduce, but not Share, Adapted Material.
-
- 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
-
- 3. Term. The term of this Public License is specified in Section 6(a).
-
- 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
-
- 5. Downstream recipients.
-
- A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
-
- B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
-
- 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
-
- b. Other rights.
-
- 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
-
- 2. Patent and trademark rights are not licensed under this Public License.
-
- 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties.
-
-Section 3 – License Conditions.
-
-Your exercise of the Licensed Rights is expressly made subject to the following conditions.
-
- a. Attribution.
-
- 1. If You Share the Licensed Material, You must:
-
- A. retain the following if it is supplied by the Licensor with the Licensed Material:
-
- i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
-
- ii. a copyright notice;
-
- iii. a notice that refers to this Public License;
-
- iv. a notice that refers to the disclaimer of warranties;
-
- v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
-
- B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
-
- C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
-
- 2. For the avoidance of doubt, You do not have permission under this Public License to Share Adapted Material.
-
- 3. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
-
- 4. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
-
-Section 4 – Sui Generis Database Rights.
-
-Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
-
- a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database, provided You do not Share Adapted Material;
-
- b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and
-
- c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
-For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
-
-Section 5 – Disclaimer of Warranties and Limitation of Liability.
-
- a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
-
- b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
-
- c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
-
-Section 6 – Term and Termination.
-
- a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
-
- b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
-
- 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
-
- 2. upon express reinstatement by the Licensor.
-
- c. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
-
- d. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
-
- e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
-
-Section 7 – Other Terms and Conditions.
-
- a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
-
- b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
-
-Section 8 – Interpretation.
-
- a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
-
- b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
-
- c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
-
- d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
-
-Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.†Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons†or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.
-
-Creative Commons may be contacted at creativecommons.org.
diff --git a/options/license/CC-BY-SA-1.0 b/options/license/CC-BY-SA-1.0
deleted file mode 100644
index 9930ef7b7e..0000000000
--- a/options/license/CC-BY-SA-1.0
+++ /dev/null
@@ -1,81 +0,0 @@
-Creative Commons Attribution-ShareAlike 1.0
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DRAFT LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to create and reproduce Derivative Works;
-
- c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
- d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works;
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any reference to such Licensor or the Original Author, as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any reference to such Licensor or the Original Author, as requested.
-
- b. You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
-
- c. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and give the Original Author credit reasonable to the medium or means You are utilizing by conveying the name (or pseudonym if applicable) of the Original Author if supplied; the title of the Work if supplied; in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
-5. Representations, Warranties and Disclaimer
-
- a. By offering the Work for public release under this License, Licensor represents and warrants that, to the best of Licensor's knowledge after reasonable inquiry:
-
- i. Licensor has secured all rights in the Work necessary to grant the license rights hereunder and to permit the lawful exercise of the rights granted hereunder without You having any obligation to pay any royalties, compulsory license fees, residuals or any other payments;
-
- ii. The Work does not infringe the copyright, trademark, publicity rights, common law rights or any other right of any third party or constitute defamation, invasion of privacy or other tortious injury to any third party.
-
- b. EXCEPT AS EXPRESSLY STATED IN THIS LICENSE OR OTHERWISE AGREED IN WRITING OR REQUIRED BY APPLICABLE LAW, THE WORK IS LICENSED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES REGARDING THE CONTENTS OR ACCURACY OF THE WORK.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, AND EXCEPT FOR DAMAGES ARISING FROM LIABILITY TO A THIRD PARTY RESULTING FROM BREACH OF THE WARRANTIES IN SECTION 5, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-SA-2.0 b/options/license/CC-BY-SA-2.0
deleted file mode 100644
index d1e03057f0..0000000000
--- a/options/license/CC-BY-SA-2.0
+++ /dev/null
@@ -1,85 +0,0 @@
-Creative Commons Attribution-ShareAlike 2.0
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
- g. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to create and reproduce Derivative Works;
-
- c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
- d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
-
- e. For the avoidance of doubt, where the work is a musical composition:
-
- i. Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
-
- ii. Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
-
- f. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any reference to such Licensor or the Original Author, as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any reference to such Licensor or the Original Author, as requested.
-
- b. You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.0 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
-
- c. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and give the Original Author credit reasonable to the medium or means You are utilizing by conveying the name (or pseudonym if applicable) of the Original Author if supplied; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-SA-2.0-UK b/options/license/CC-BY-SA-2.0-UK
deleted file mode 100644
index ea3df21954..0000000000
--- a/options/license/CC-BY-SA-2.0-UK
+++ /dev/null
@@ -1,147 +0,0 @@
-Creative Commons Attribution - Share-Alike 2.0 England and Wales
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENCE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-Licence
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENCE ("CCPL" OR "LICENCE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENCE OR COPYRIGHT LAW IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENCE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-This Creative Commons England and Wales Public Licence enables You (all capitalised terms defined below) to view, edit, modify, translate and distribute Works worldwide, under the terms of this licence, provided that You credit the Original Author.
-
-'The Licensor' [one or more legally recognised persons or entities offering the Work under the terms and conditions of this Licence]
-
-and
-
-'You'
-
-agree as follows:
-
-1. Definitions
-
- a. "Attribution" means acknowledging all the parties who have contributed to and have rights in the Work or Collective Work under this Licence.
-
- b. "Collective Work" means the Work in its entirety in unmodified form along with a number of other separate and independent works, assembled into a collective whole.
-
- c. "Derivative Work" means any work created by the editing, modification, adaptation or translation of the Work in any media (however a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this Licence). For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this Licence.
-
- d. "Licence" means this Creative Commons England and Wales Public Licence agreement.
-
- e. "Licence Elements" means the following high-level licence attributes indicated in the title of this Licence: Attribution, Share-Alike.
-
- f. "Original Author" means the individual (or entity) who created the Work.
-
- g. "Work" means the work protected by copyright which is offered under the terms of this Licence.
-
- h. For the purpose of this Licence, when not inconsistent with the context, words in the singular number include the plural number.
-
-2. Licence Terms
-
-2.1 The Licensor hereby grants to You a worldwide, royalty-free, non-exclusive, Licence for use and for the duration of copyright in the Work.
-
-You may:
-
- * copy the Work;
-
- * create one or more derivative Works;
-
- * incorporate the Work into one or more Collective Works;
-
- * copy Derivative Works or the Work as incorporated in any Collective Work; and
-
- * publish, distribute, archive, perform or otherwise disseminate the Work or the Work as incorporated in any Collective Work, to the public in any material form in any media whether now known or hereafter created.
-
-HOWEVER,
-
-You must not:
-
- * impose any terms on the use to be made of the Work, the Derivative Work or the Work as incorporated in a Collective Work that alter or restrict the terms of this Licence or any rights granted under it or has the effect or intent of restricting the ability to exercise those rights;
-
- * impose any digital rights management technology on the Work or the Work as incorporated in a Collective Work that alters or restricts the terms of this Licence or any rights granted under it or has the effect or intent of restricting the ability to exercise those rights;
-
- * sublicense the Work;
-
- * subject the Work to any derogatory treatment as defined in the Copyright, Designs and Patents Act 1988.
-
-FINALLY,
-
-You must:
-
- * make reference to this Licence (by Uniform Resource Identifier (URI), spoken word or as appropriate to the media used) on all copies of the Work and Collective Works published, distributed, performed or otherwise disseminated or made available to the public by You;
-
- * recognise the Licensor's / Original Author's right of attribution in any Work and Collective Work that You publish, distribute, perform or otherwise disseminate to the public and ensure that You credit the Licensor / Original Author as appropriate to the media used; and
-
- * to the extent reasonably practicable, keep intact all notices that refer to this Licence, in particular the URI, if any, that the Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work.
-
-Additional Provisions for third parties making use of the Work
-
-2.2. Further licence from the Licensor
-
-Each time You publish, distribute, perform or otherwise disseminate
-
- * the Work; or
-
- * any Derivative Work; or
-
- * the Work as incorporated in a Collective Work
-
-the Licensor agrees to offer to the relevant third party making use of the Work (in any of the alternatives set out above) a licence to use the Work on the same terms and conditions as granted to You hereunder.
-
-2.3. Further licence from You
-
-Each time You publish, distribute, perform or otherwise disseminate
-
- * a Derivative Work; or
-
- * a Derivative Work as incorporated in a Collective Work
-
-You agree to offer to the relevant third party making use of the Work (in either of the alternatives set out above) a licence to use the Derivative Work on any of the following premises:
-
- * a licence to the Derivative Work on the same terms and conditions as the licence granted to You hereunder; or
-
- * a later version of the licence granted to You hereunder; or
-
- * any other Creative Commons licence with the same Licence Elements.
-
-2.4. This Licence does not affect any rights that the User may have under any applicable law, including fair use, fair dealing or any other legally recognised limitation or exception to copyright infringement.
-
-2.5. All rights not expressly granted by the Licensor are hereby reserved, including but not limited to, the exclusive right to collect, whether individually or via a licensing body, such as a collecting society, royalties for any use of the Work which results in commercial advantage or private monetary compensation.
-
-3. Warranties and Disclaimer
-
-Except as required by law, the Work is licensed by the Licensor on an "as is" and "as available" basis and without any warranty of any kind, either express or implied.
-
-4. Limit of Liability
-
-Subject to any liability which may not be excluded or limited by law the Licensor shall not be liable and hereby expressly excludes all liability for loss or damage howsoever and whenever caused to You.
-
-5. Termination
-
-The rights granted to You under this Licence shall terminate automatically upon any breach by You of the terms of this Licence. Individuals or entities who have received Collective Works from You under this Licence, however, will not have their Licences terminated provided such individuals or entities remain in full compliance with those Licences.
-
-6. General
-
-6.1. The validity or enforceability of the remaining terms of this agreement is not affected by the holding of any provision of it to be invalid or unenforceable.
-
-6.2. This Licence constitutes the entire Licence Agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. The Licensor shall not be bound by any additional provisions that may appear in any communication in any form.
-
-6.3. A person who is not a party to this Licence shall have no rights under the Contracts (Rights of Third Parties) Act 1999 to enforce any of its terms.
-
-6.4. This Licence shall be governed by the law of England and Wales and the parties irrevocably submit to the exclusive jurisdiction of the Courts of England and Wales.
-
-7. On the role of Creative Commons
-
-7.1. Neither the Licensor nor the User may use the Creative Commons logo except to indicate that the Work is licensed under a Creative Commons Licence. Any permitted use has to be in compliance with the Creative Commons trade mark usage guidelines at the time of use of the Creative Commons trade mark. These guidelines may be found on the Creative Commons website or be otherwise available upon request from time to time.
-
-7.2. Creative Commons Corporation does not profit financially from its role in providing this Licence and will not investigate the claims of any Licensor or user of the Licence.
-
-7.3. One of the conditions that Creative Commons Corporation requires of the Licensor and You is an acknowledgement of its limited role and agreement by all who use the Licence that the Corporation is not responsible to anyone for the statements and actions of You or the Licensor or anyone else attempting to use or using this Licence.
-
-7.4. Creative Commons Corporation is not a party to this Licence, and makes no warranty whatsoever in connection to the Work or in connection to the Licence, and in all events is not liable for any loss or damage resulting from the Licensor's or Your reliance on this Licence or on its enforceability.
-
-7.5. USE OF THIS LICENCE MEANS THAT YOU AND THE LICENSOR EACH ACCEPTS THESE CONDITIONS IN SECTION 7.1, 7.2, 7.3, 7.4 AND EACH ACKNOWLEDGES CREATIVE COMMONS CORPORATION'S VERY LIMITED ROLE AS A FACILITATOR OF THE LICENCE FROM THE LICENSOR TO YOU.
-
-Creative Commons is not a party to this Licence, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this licence. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-SA-2.1-JP b/options/license/CC-BY-SA-2.1-JP
deleted file mode 100644
index 7971930e3f..0000000000
--- a/options/license/CC-BY-SA-2.1-JP
+++ /dev/null
@@ -1,83 +0,0 @@
-アトリビューション—シェアアライク 2.1
-(帰属—åŒä¸€æ¡ä»¶è¨±è«¾ï¼‰
-クリエイティブ・コモンズåŠã³ã‚¯ãƒªã‚¨ã‚¤ãƒ†ã‚£ãƒ–ãƒ»ã‚³ãƒ¢ãƒ³ã‚ºãƒ»ã‚¸ãƒ£ãƒ‘ãƒ³ã¯æ³•律事務所ã§ã¯ã‚りã¾ã›ã‚“。ã“ã®åˆ©ç”¨è¨±è«¾æ¡é …ã®é ’å¸ƒã¯æ³•的アドãƒã‚¤ã‚¹ãã®ä»–ã®æ³•律業務を行ã†ã‚‚ã®ã§ã¯ã‚りã¾ã›ã‚“。クリエイティブ・コモンズåŠã³ã‚¯ãƒªã‚¨ã‚¤ãƒ†ã‚£ãƒ–・コモンズ・ジャパンã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾ã®å½“事者ã§ã¯ãªãã€ã“ã“ã«æä¾›ã™ã‚‹æƒ…å ±åŠã³æœ¬ä½œå“ã«é–¢ã—ã„ã‹ãªã‚‹ä¿è¨¼ã‚‚行ã„ã¾ã›ã‚“。クリエイティブ・コモンズåŠã³ã‚¯ãƒªã‚¨ã‚¤ãƒ†ã‚£ãƒ–・コモンズ・ジャパンã¯ã€ã„ã‹ãªã‚‹æ³•令ã«åŸºã¥ã“ã†ã¨ã‚‚ã€ã‚ãªãŸåˆã¯ã„ã‹ãªã‚‹ç¬¬ä¸‰è€…ã®æå®³ï¼ˆã“ã®åˆ©ç”¨è¨±è«¾ã«é–¢é€£ã™ã‚‹é€šå¸¸æå®³ã€ç‰¹åˆ¥æå®³ã‚’å«ã¿ã¾ã™ãŒã“れらã«é™ã‚‰ã‚Œã¾ã›ã‚“)ã«ã¤ã„ã¦è²¬ä»»ã‚’è² ã„ã¾ã›ã‚“。
-
-利用許諾
-
-本作å“(下記ã«å®šç¾©ã™ã‚‹ï¼‰ã¯ã€ã“ã®ã‚¯ãƒªã‚¨ã‚¤ãƒ†ã‚£ãƒ–・コモンズ・パブリック・ライセンス日本版(以下「ã“ã®åˆ©ç”¨è¨±è«¾ã€ã¨ã„ã†ï¼‰ã®æ¡é …ã®ä¸‹ã§æä¾›ã•れる。本作å“ã¯ã€è‘—作権法åŠã³ï¼åˆã¯ä»–ã®é©ç”¨æ³•ã«ã‚ˆã£ã¦ä¿è­·ã•れる。本作å“ã‚’ã“ã®åˆ©ç”¨è¨±è«¾åˆã¯è‘—作権法ã®ä¸‹ã§æŽˆæ¨©ã•れãŸä»¥å¤–ã®æ–¹æ³•ã§ä½¿ç”¨ã™ã‚‹ã“ã¨ã‚’ç¦æ­¢ã™ã‚‹ã€‚
-
-許諾者ã¯ã€ã‹ã‹ã‚‹æ¡é …ã‚’ã‚ãªãŸãŒæ‰¿è«¾ã™ã‚‹ã“ã¨ã¨ã²ãã‹ãˆã«ã€ã“ã“ã«è¦å®šã•れる権利をã‚ãªãŸã«ä»˜ä¸Žã™ã‚‹ã€‚本作å“ã«é–¢ã—ã€ã“ã®åˆ©ç”¨è¨±è«¾ã®ä¸‹ã§èªã‚られるã„ãšã‚Œã‹ã®åˆ©ç”¨ã‚’行ã†ã“ã¨ã«ã‚ˆã‚Šã€ã‚ãªãŸã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾ï¼ˆæ¡é …ï¼‰ã«æ‹˜æŸã•れるã“ã¨ã‚’承諾ã—åŒæ„ã—ãŸã“ã¨ã¨ãªã‚‹ã€‚
-
-第1æ¡ å®šç¾©
-
-ã“ã®åˆ©ç”¨è¨±è«¾ä¸­ã®ç”¨èªžã‚’以下ã®ã‚ˆã†ã«å®šç¾©ã™ã‚‹ã€‚ãã®ä»–ã®ç”¨èªžã¯ã€è‘—作権法ãã®ä»–ã®æ³•令ã§å®šã‚ã‚‹æ„味をæŒã¤ã‚‚ã®ã¨ã™ã‚‹ã€‚
-
- a. 「二次的著作物ã€ã¨ã¯ã€è‘—作物を翻訳ã—ã€ç·¨æ›²ã—ã€è‹¥ã—ãã¯å¤‰å½¢ã—ã€ã¾ãŸã¯è„šè‰²ã—ã€æ˜ ç”»åŒ–ã—ã€ãã®ä»–翻案ã™ã‚‹ã“ã¨ã«ã‚ˆã‚Šå‰µä½œã—ãŸè‘—作物をã„ã†ã€‚ãŸã ã—ã€ç·¨é›†è‘—作物åˆã¯ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®è‘—作物(以下ã€ã“ã®äºŒã¤ã‚’ä½µã›ã¦ã€Œç·¨é›†è‘—作物等ã€ã¨ã„ã†ã€‚)を構æˆã™ã‚‹è‘—作物ã¯ã€äºŒæ¬¡çš„著作物ã¨ã¿ãªã•れãªã„。ã¾ãŸã€åŽŸè‘—ä½œè€…åŠã³å®Ÿæ¼”å®¶ã®å誉åˆã¯å£°æœ›ã‚’害ã™ã‚‹æ–¹æ³•ã§åŽŸè‘—ä½œç‰©ã‚’æ”¹ä½œã€å¤‰å½¢ã‚‚ã—ãã¯ç¿»æ¡ˆã—ã¦ç”Ÿã˜ã‚‹è‘—作物ã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾ã®ç›®çš„ã«ãŠã„ã¦ã¯ã€äºŒæ¬¡çš„著作物ã«å«ã¾ã‚Œãªã„。
- b. 「許諾者ã€ã¨ã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾ã®æ¡é …ã®ä¸‹ã§æœ¬ä½œå“ã‚’æä¾›ã™ã‚‹å€‹äººåˆã¯å›£ä½“ã‚’ã„ã†ã€‚
- c. 「ã‚ãªãŸã€ã¨ã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾ã«åŸºã¥ã権利を行使ã™ã‚‹å€‹äººåˆã¯å›£ä½“ã‚’ã„ã†ã€‚
- d. 「原著作者ã€ã¨ã¯ã€æœ¬ä½œå“ã«å«ã¾ã‚Œã‚‹è‘—作物を創作ã—ãŸå€‹äººåˆã¯å›£ä½“ã‚’ã„ã†ã€‚
- e. 「本作å“ã€ã¨ã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾ã®æ¡é …ã«åŸºã¥ã„ã¦åˆ©ç”¨ã™ã‚‹æ¨©åˆ©ãŒä»˜ä¸Žã•れる対象ãŸã‚‹ç„¡ä½“物をã„ã„ã€è‘—作物ã€å®Ÿæ¼”ã€ãƒ¬ã‚³ãƒ¼ãƒ‰ã€æ”¾é€ã«ã‹ã‹ã‚‹éŸ³åˆã¯å½±åƒã€ã‚‚ã—ãã¯æœ‰ç·šæ”¾é€ã«ã‹ã‹ã‚‹éŸ³åˆã¯å½±åƒã‚’ã™ã¹ã¦å«ã‚€ã‚‚ã®ã¨ã™ã‚‹ã€‚
- f. 「ライセンスè¦ç´ ã€ã¨ã¯ã€è¨±è«¾è€…ãŒé¸æŠžã—ã€ã“ã®åˆ©ç”¨è¨±è«¾ã«è¡¨ç¤ºã•れã¦ã„ã‚‹ã€ä»¥ä¸‹ã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹å±žæ€§ã‚’ã„ã†ï¼šå¸°å±žãƒ»åŒä¸€æ¡ä»¶è¨±è«¾
-
-第2æ¡ è‘—ä½œæ¨©ç­‰ã«å¯¾ã™ã‚‹åˆ¶é™
-
-ã“ã®åˆ©ç”¨è¨±è«¾ã«å«ã¾ã‚Œã‚‹ã„ã‹ãªã‚‹æ¡é …ã«ã‚ˆã£ã¦ã‚‚ã€è¨±è«¾è€…ã¯ã€ã‚ãªãŸãŒè‘—作権ã®åˆ¶é™ï¼ˆè‘—作権法第30æ¡ã€œ49æ¡ï¼‰ã€è‘—作者人格権ã«å¯¾ã™ã‚‹åˆ¶é™ï¼ˆè‘—作権法第18æ¡2項〜4é …ã€ç¬¬19æ¡2項〜4é …ã€ç¬¬20æ¡2項)ã€è‘—作隣接権ã«å¯¾ã™ã‚‹åˆ¶é™ï¼ˆè‘—作権法第102æ¡ï¼‰ãã®ä»–ã€è‘—作権法åˆã¯ãã®ä»–ã®é©ç”¨æ³•ã«åŸºã¥ã„ã¦èªã‚られるã“ã¨ã¨ãªã‚‹æœ¬ä½œå“ã®åˆ©ç”¨ã‚’ç¦æ­¢ã—ãªã„。
-
-第3æ¡ ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã®ä»˜ä¸Ž
-
-ã“ã®åˆ©ç”¨è¨±è«¾ã®æ¡é …ã«å¾“ã„ã€è¨±è«¾è€…ã¯ã‚ãªãŸã«ã€æœ¬ä½œå“ã«é–¢ã—ã€ã™ã¹ã¦ã®å›½ã§ã€ãƒ­ã‚¤ãƒ¤ãƒªãƒ†ã‚£ãƒ»ãƒ•リーã€éžæŽ’ä»–çš„ã§ã€ï¼ˆç¬¬7æ¡bã«å®šã‚る期間)継続的ãªä»¥ä¸‹ã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã‚’付与ã™ã‚‹ã€‚ãŸã ã—ã€ã‚ãªãŸãŒä»¥å‰ã«æœ¬ä½œå“ã«é–¢ã™ã‚‹ã“ã®åˆ©ç”¨è¨±è«¾ã®æ¡é …ã«é•åã—ãŸã“ã¨ãŒãªã„ã‹ã€ã‚ã‚‹ã„ã¯ã€ä»¥å‰ã«ã“ã®åˆ©ç”¨è¨±è«¾ã®æ¡é …ã«é•åã—ãŸãŒã“ã®åˆ©ç”¨è¨±è«¾ã«åŸºã¥ã権利を行使ã™ã‚‹ãŸã‚ã«è¨±è«¾è€…ã‹ã‚‰æ˜Žç¤ºçš„ãªè¨±å¯ã‚’å¾—ã¦ã„ã‚‹å ´åˆã«é™ã‚‹ã€‚
-
- a. 本作å“ã«å«ã¾ã‚Œã‚‹è‘—作物(以下「本著作物ã€ã¨ã„ã†ã€‚)を複製ã™ã‚‹ã“ã¨ï¼ˆç·¨é›†è‘—作物等ã«çµ„ã¿è¾¼ã¿è¤‡è£½ã™ã‚‹ã“ã¨ã‚’å«ã‚€ã€‚以下ã€åŒã˜ã€‚)ã€
- b. 本著作物を翻案ã—ã¦äºŒæ¬¡çš„著作物を創作ã—ã€è¤‡è£½ã™ã‚‹ã“ã¨ã€
- c. 本著作物åˆã¯ãã®äºŒæ¬¡çš„著作物ã®è¤‡è£½ç‰©ã‚’頒布ã™ã‚‹ã“ã¨ï¼ˆè­²æ¸¡ã¾ãŸã¯è²¸ä¸Žã«ã‚ˆã‚Šå…¬è¡†ã«æä¾›ã™ã‚‹ã“ã¨ã‚’å«ã‚€ã€‚以下åŒã˜ã€‚)ã€ä¸Šæ¼”ã™ã‚‹ã“ã¨ã€æ¼”å¥ã™ã‚‹ã“ã¨ã€ä¸Šæ˜ ã™ã‚‹ã“ã¨ã€å…¬è¡†é€ä¿¡ã‚’行ã†ã“ã¨ï¼ˆé€ä¿¡å¯èƒ½åŒ–ã‚’å«ã‚€ã€‚以下ã€åŒã˜ã€‚)ã€å…¬ã«å£è¿°ã™ã‚‹ã“ã¨ã€å…¬ã«å±•示ã™ã‚‹ã“ã¨ã€
- d. 本作å“ã«å«ã¾ã‚Œã‚‹å®Ÿæ¼”ã‚’ã€éŒ²éŸ³ãƒ»éŒ²ç”»ã™ã‚‹ã“ã¨ï¼ˆéŒ²éŸ³ï½¥éŒ²ç”»ç‰©ã‚’増製ã™ã‚‹ã“ã¨ã‚’å«ã‚€ï¼‰ã€éŒ²éŸ³ï½¥éŒ²ç”»ç‰©ã«ã‚ˆã‚Šé ’布ã™ã‚‹ã“ã¨ã€å…¬è¡†é€ä¿¡ã‚’行ã†ã“ã¨ã€
- e. 本作å“ã«å«ã¾ã‚Œã‚‹ãƒ¬ã‚³ãƒ¼ãƒ‰ã‚’ã€è¤‡è£½ã™ã‚‹ã“ã¨ã€é ’布ã™ã‚‹ã“ã¨ã€å…¬è¡†é€ä¿¡ã‚’行ã†ã“ã¨ã€
- f. 本作å“ã«å«ã¾ã‚Œã‚‹ã€æ”¾é€ã«ä¿‚る音åˆã¯å½±åƒã‚’ã€è¤‡è£½ã™ã‚‹ã“ã¨ã€ãã®æ”¾é€ã‚’å—ä¿¡ã—ã¦å†æ”¾é€ã™ã‚‹ã“ã¨åˆã¯æœ‰ç·šæ”¾é€ã™ã‚‹ã“ã¨ã€ãã®æ”¾é€åˆã¯ã“れをå—ä¿¡ã—ã¦è¡Œã†æœ‰ç·šæ”¾é€ã‚’å—ä¿¡ã—ã¦é€ä¿¡å¯èƒ½åŒ–ã™ã‚‹ã“ã¨ã€ãã®ãƒ†ãƒ¬ãƒ“ジョン放é€åˆã¯ã“れをå—ä¿¡ã—ã¦è¡Œã†æœ‰ç·šæ”¾é€ã‚’å—ä¿¡ã—ã¦ã€å½±åƒã‚’拡大ã™ã‚‹ç‰¹åˆ¥ã®è£…置を用ã„ã¦å…¬ã«ä¼é”ã™ã‚‹ã“ã¨ã€
- g. 本作å“ã«å«ã¾ã‚Œã‚‹ã€æœ‰ç·šæ”¾é€ã«ä¿‚る音åˆã¯å½±åƒã‚’ã€è¤‡è£½ã™ã‚‹ã“ã¨ã€ãã®æœ‰ç·šæ”¾é€ã‚’å—ä¿¡ã—ã¦æ”¾é€ã—ã€åˆã¯å†æœ‰ç·šæ”¾é€ã™ã‚‹ã“ã¨ã€ãã®æœ‰ç·šæ”¾é€ã‚’å—ä¿¡ã—ã¦é€ä¿¡å¯èƒ½åŒ–ã™ã‚‹ã“ã¨ã€ãã®æœ‰ç·šãƒ†ãƒ¬ãƒ“ジョン放é€ã‚’å—ä¿¡ã—ã¦ã€å½±åƒã‚’拡大ã™ã‚‹ç‰¹åˆ¥ã®è£…置を用ã„ã¦å…¬ã«ä¼é”ã™ã‚‹ã“ã¨ã€
-
-上記ã«å®šã‚ã‚‰ã‚ŒãŸæœ¬ä½œå“åˆã¯ãã®äºŒæ¬¡çš„著作物ã®åˆ©ç”¨ã¯ã€ç¾åœ¨åŠã³å°†æ¥ã®ã™ã¹ã¦ã®åª’体・形å¼ã§è¡Œã†ã“ã¨ãŒã§ãる。ã‚ãªãŸã¯ã€ä»–ã®åª’体åŠã³å½¢å¼ã§æœ¬ä½œå“åˆã¯ãã®äºŒæ¬¡çš„著作物を利用ã™ã‚‹ã®ã«æŠ€è¡“çš„ã«å¿…è¦ãªå¤‰æ›´ã‚’行ã†ã“ã¨ãŒã§ãã‚‹ã€‚è¨±è«¾è€…ã¯æœ¬ä½œå“åˆã¯ãã®äºŒæ¬¡çš„著作物ã«é–¢ã—ã¦ã€ã“ã®åˆ©ç”¨è¨±è«¾ã«å¾“ã£ãŸåˆ©ç”¨ã«ã¤ã„ã¦ã¯è‡ªå·±ãŒæœ‰ã™ã‚‹è‘—作者人格権åŠã³å®Ÿæ¼”家人格権を行使ã—ãªã„。許諾者ã«ã‚ˆã£ã¦æ˜Žç¤ºçš„ã«ä»˜ä¸Žã•れãªã„å…¨ã¦ã®æ¨©åˆ©ã¯ã€ç•™ä¿ã•れる。
-
-第4æ¡ å—領者ã¸ã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹æä¾›
-
-ã‚ãªãŸãŒæœ¬ä½œå“ã‚’ã“ã®åˆ©ç”¨è¨±è«¾ã«åŸºã¥ã„ã¦åˆ©ç”¨ã™ã‚‹åº¦æ¯Žã«ã€è¨±è«¾è€…ã¯æœ¬ä½œå“åˆã¯æœ¬ä½œå“ã®äºŒæ¬¡çš„著作物ã®å—領者ã«å¯¾ã—ã¦ã€ç›´æŽ¥ã€ã“ã®åˆ©ç”¨è¨±è«¾ã®ä¸‹ã§ã‚ãªãŸã«è¨±å¯ã•れãŸåˆ©ç”¨è¨±è«¾ã¨åŒã˜æ¡ä»¶ã®æœ¬ä½œå“ã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã‚’æä¾›ã™ã‚‹ã€‚
-
-第5æ¡ åˆ¶é™
-
-上記第3æ¡åŠã³ç¬¬4æ¡ã«ã‚ˆã‚Šä»˜ä¸Žã•れãŸãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã¯ã€ä»¥ä¸‹ã®åˆ¶é™ã«æ˜Žç¤ºçš„ã«å¾“ã„ã€åˆ¶ç´„ã•れる。
-
- a. ã‚ãªãŸã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾ã®æ¡é …ã«åŸºã¥ã„ã¦ã®ã¿ã€æœ¬ä½œå“を利用ã™ã‚‹ã“ã¨ãŒã§ãる。
- b. ã‚ãªãŸã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾åˆã¯ã“ã®åˆ©ç”¨è¨±è«¾ã¨åŒä¸€ã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹è¦ç´ ã‚’å«ã‚€ã»ã‹ã®ã‚¯ãƒªã‚¨ã‚¤ãƒ†ã‚£ãƒ–・コモンズ・ライセンス(例ãˆã°ã€ã“ã®åˆ©ç”¨è¨±è«¾ã®æ–°ã—ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã€åˆã¯ã“ã®åˆ©ç”¨è¨±è«¾ã¨åŒä¸€ã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹è¦ç´ ã®ä»–国ç±ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ãªã©ï¼‰ã«åŸºã¥ã„ã¦ã®ã¿ã€æœ¬ä½œå“ã®äºŒæ¬¡çš„著作物を利用ã™ã‚‹ã“ã¨ãŒã§ãる。
- c. ã‚ãªãŸã¯ã€æœ¬ä½œå“を利用ã™ã‚‹ã¨ãã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾ã®å†™ã—åˆã¯URI(Uniform Resource Identifier)を本作å“ã®è¤‡è£½ç‰©ã«æ·»ä»˜åˆã¯è¡¨ç¤ºã—ãªã‘れã°ãªã‚‰ãªã„。
- d. ã‚ãªãŸã¯ã€æœ¬ä½œå“ã®äºŒæ¬¡çš„著作物を利用ã™ã‚‹ã¨ãã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾åˆã¯ã“ã®åˆ©ç”¨è¨±è«¾ã¨åŒä¸€ã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹è¦ç´ ã‚’å«ã‚€ã»ã‹ã®ã‚¯ãƒªã‚¨ã‚¤ãƒ†ã‚£ãƒ–・コモンズ・ライセンスã®å†™ã—åˆã¯URIを本作å“ã®äºŒæ¬¡çš„著作物ã®è¤‡è£½ç‰©ã«æ·»ä»˜ã¾ãŸã¯è¡¨ç¤ºã—ãªã‘れã°ãªã‚‰ãªã„。
- e. ã‚ãªãŸã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾æ¡é …åŠã³ã“ã®åˆ©ç”¨è¨±è«¾ã«ã‚ˆã£ã¦ä»˜ä¸Žã•れる利用許諾å—é ˜è€…ã®æ¨©åˆ©ã®è¡Œä½¿ã‚’変更åˆã¯åˆ¶é™ã™ã‚‹ã‚ˆã†ãªã€æœ¬ä½œå“åˆã¯ãã®äºŒæ¬¡çš„著作物ã«ä¿‚ã‚‹æ¡ä»¶ã‚’ææ¡ˆã—ãŸã‚Šèª²ã—ãŸã‚Šã—ã¦ã¯ãªã‚‰ãªã„。
- f. ã‚ãªãŸã¯ã€æœ¬ä½œå“ã‚’å†åˆ©ç”¨è¨±è«¾ã™ã‚‹ã“ã¨ãŒã§ããªã„。
- g. ã‚ãªãŸã¯ã€æœ¬ä½œå“åˆã¯ãã®äºŒæ¬¡çš„著作物ã®åˆ©ç”¨ã«ã‚ãŸã£ã¦ã€ã“ã®åˆ©ç”¨è¨±è«¾åŠã³ãã®å…責æ¡é …ã«é–¢ã™ã‚‹æ³¨æ„書ãã®å†…容を変更ã›ãšã€è¦‹ã‚„ã™ã„態様ã§ãã®ã¾ã¾æŽ²è¼‰ã—ãªã‘れã°ãªã‚‰ãªã„。
- h. ã‚ãªãŸã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾æ¡é …ã¨çŸ›ç›¾ã™ã‚‹æ–¹æ³•ã§æœ¬è‘—作物ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹åˆã¯ä½¿ç”¨ã‚’コントロールã™ã‚‹ã‚ˆã†ãªæŠ€è¡“çš„ä¿è­·æ‰‹æ®µã‚’用ã„ã¦ã€æœ¬ä½œå“åˆã¯ãã®äºŒæ¬¡çš„著作物を利用ã—ã¦ã¯ãªã‚‰ãªã„。
- i. 本æ¡ã®åˆ¶é™ã¯ã€æœ¬ä½œå“åˆã¯ãã®äºŒæ¬¡çš„著作物ãŒç·¨é›†è‘—作物等ã«çµ„ã¿è¾¼ã¾ã‚ŒãŸå ´åˆã«ã‚‚ã€ãã®çµ„ã¿è¾¼ã¾ã‚ŒãŸä½œå“ã«é–¢ã—ã¦ã¯é©ç”¨ã•れる。ã—ã‹ã—ã€æœ¬ä½œå“åˆã¯ãã®äºŒæ¬¡çš„著作物ãŒçµ„ã¿è¾¼ã¾ã‚ŒãŸç·¨é›†è‘—作物等ãã®ã‚‚ã®ã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾ã®æ¡é …ã«å¾“ã†å¿…è¦ã¯ãªã„。
- j. ã‚ãªãŸã¯ã€æœ¬ä½œå“ã€ãã®äºŒæ¬¡çš„著作物åˆã¯æœ¬ä½œå“を組ã¿è¾¼ã‚“ã ç·¨é›†è‘—作物等を利用ã™ã‚‹å ´åˆã«ã¯ã€ï¼ˆ1)本作å“ã«ä¿‚ã‚‹ã™ã¹ã¦ã®è‘—作権表示をãã®ã¾ã¾ã«ã—ã¦ãŠã‹ãªã‘れã°ãªã‚‰ãšã€ï¼ˆ2)原著作者åŠã³å®Ÿæ¼”å®¶ã®ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆã‚’ã€åˆç†çš„ãªæ–¹å¼ã§ã€ï¼ˆã‚‚ã—示ã•れã¦ã„れã°åŽŸè‘—ä½œè€…åŠã³å®Ÿæ¼”å®¶ã®åå‰åˆã¯å¤‰åã‚’ä¼ãˆã‚‹ã“ã¨ã«ã‚ˆã‚Šã€ï¼‰è¡¨ç¤ºã—ãªã‘れã°ãªã‚‰ãšã€ï¼ˆ3)本作å“ã®ã‚¿ã‚¤ãƒˆãƒ«ãŒç¤ºã•れã¦ã„ã‚‹å ´åˆã«ã¯ã€ãã®ã‚¿ã‚¤ãƒˆãƒ«ã‚’表示ã—ãªã‘れã°ãªã‚‰ãšã€ï¼ˆ4ï¼‰è¨±è«¾è€…ãŒæœ¬ä½œå“ã«æ·»ä»˜ã™ã‚‹ã‚ˆã†æŒ‡å®šã—ãŸURIãŒã‚れã°ã€åˆç†çš„ã«å®Ÿè¡Œå¯èƒ½ãªç¯„囲ã§ã€ãã®URIを表示ã—ãªã‘れã°ãªã‚‰ãšï¼ˆãŸã ã—ã€ãã®URIãŒæœ¬ä½œå“ã®è‘—作権表示ã¾ãŸã¯ãƒ©ã‚¤ã‚»ãƒ³ã‚¹æƒ…報をå‚ç…§ã™ã‚‹ã‚‚ã®ã§ãªã„ã¨ãã¯ã“ã®é™ã‚Šã§ãªã„。)(5)二次的著作物ã®å ´åˆã«ã¯ã€å½“該二次的著作物中ã®åŽŸè‘—ä½œç‰©ã®åˆ©ç”¨ã‚’示ã™ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆã‚’表示ã—ãªã‘れã°ãªã‚‰ãªã„。ã“れらã®ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆã¯ã€åˆç†çš„ã§ã‚れã°ã©ã‚“ãªæ–¹æ³•ã§ã‚‚行ã†ã“ã¨ãŒã§ãる。ã—ã‹ã—ãªãŒã‚‰ã€äºŒæ¬¡çš„著作物åˆã¯ç·¨é›†è‘—作物等ã®å ´åˆã«ã¯ã€å°‘ãªãã¨ã‚‚ä»–ã®åŒæ§˜ã®è‘—作者ã®ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆãŒè¡¨ç¤ºã•れる箇所ã§å½“該クレジットを表示ã—ã€å°‘ãªãã¨ã‚‚ä»–ã®åŒæ§˜ã®è‘—作者ã®ã‚¯ãƒ¬ã‚¸ãƒƒãƒˆã¨åŒç¨‹åº¦ã«ç›®ç«‹ã¤æ–¹æ³•ã§ã‚ã‚‹ã“ã¨ã‚’è¦ã™ã‚‹ã€‚
- k. ã‚‚ã—ã€ã‚ãªãŸãŒã€æœ¬ä½œå“ã®äºŒæ¬¡çš„著作物ã€åˆã¯æœ¬ä½œå“ã‚‚ã—ãã¯ãã®äºŒæ¬¡çš„著作物を組ã¿è¾¼ã‚“ã ç·¨é›†è‘—作物等を創作ã—ãŸå ´åˆã€ã‚ãªãŸã¯ã€è¨±è«¾è€…ã‹ã‚‰ã®é€šçŸ¥ãŒã‚れã°ã€å®Ÿè¡Œå¯èƒ½ãªç¯„囲ã§ã€è¦æ±‚ã«å¿œã˜ã¦ã€äºŒæ¬¡çš„著作物åˆã¯ç·¨é›†è‘—作物等ã‹ã‚‰ã€è¨±è«¾è€…åˆã¯åŽŸè‘—ä½œè€…ã¸ã®è¨€åŠã‚’ã™ã¹ã¦é™¤åŽ»ã—ãªã‘れã°ãªã‚‰ãªã„。
-
-第6æ¡ è²¬ä»»åˆ¶é™
-
-ã“ã®åˆ©ç”¨è¨±è«¾ã®ä¸¡å½“äº‹è€…ãŒæ›¸é¢ã«ã¦åˆ¥é€”åˆæ„ã—ãªã„é™ã‚Šã€è¨±è«¾è€…ã¯æœ¬ä½œå“ã‚’ç¾çжã®ã¾ã¾æä¾›ã™ã‚‹ã‚‚ã®ã¨ã—ã€æ˜Žç¤ºãƒ»é»™ç¤ºã‚’å•ã‚ãšã€æœ¬ä½œå“ã«é–¢ã—ã¦ã„ã‹ãªã‚‹ä¿è¨¼ï¼ˆç‰¹å®šã®åˆ©ç”¨ç›®çš„ã¸ã®é©åˆæ€§ã€ç¬¬ä¸‰è€…ã®æ¨©åˆ©ã®éžä¾µå®³ã€æ¬ é™¥ã®ä¸å­˜åœ¨ã‚’å«ã‚€ãŒã€ã“れã«é™ã‚‰ã‚Œãªã„。)もã—ãªã„。
-
-ã“ã®åˆ©ç”¨è¨±è«¾åˆã¯ã“ã®åˆ©ç”¨è¨±è«¾ã«åŸºã¥ã本作å“ã®åˆ©ç”¨ã‹ã‚‰ç™ºç”Ÿã™ã‚‹ã€ã„ã‹ãªã‚‹æå®³ï¼ˆè¨±è«¾è€…ãŒã€æœ¬ä½œå“ã«ã‹ã‹ã‚‹è‘—作権ã€è‘—作隣接権ã€è‘—作者人格権ã€å®Ÿæ¼”家人格権ã€å•†æ¨™æ¨©ã€ãƒ‘ブリシティ権ã€ä¸æ­£ç«¶äº‰é˜²æ­¢æ³•ãã®ä»–関連法è¦ä¸Šä¿è­·ã•れる利益を有ã™ã‚‹è€…ã‹ã‚‰ã®è¨±è«¾ã‚’å¾—ã‚‹ã“ã¨ãªã本作å“ã®åˆ©ç”¨è¨±è«¾ã‚’行ã£ãŸã“ã¨ã«ã‚ˆã‚Šç™ºç”Ÿã™ã‚‹æå®³ã€ãƒ—ライãƒã‚·ãƒ¼ä¾µå®³åˆã¯å誉毀æã‹ã‚‰ç™ºç”Ÿã™ã‚‹æå®³ç­‰ã®é€šå¸¸æå®³ã€åŠã³ç‰¹åˆ¥æå®³ã‚’å«ã‚€ãŒã€ã“れã«é™ã‚‰ãªã„。)ã«ã¤ã„ã¦ã‚‚ã€è¨±è«¾è€…ã«æ•…æ„åˆã¯é‡å¤§ãªéŽå¤±ãŒã‚ã‚‹å ´åˆã‚’除ãã€è¨±è«¾è€…ãŒãã®ã‚ˆã†ãªæå®³ç™ºç”Ÿã®å¯èƒ½æ€§ã‚’知らã•れãŸã‹å¦ã‹ã‚’å•ã‚ãšã€è¨±è«¾è€…ã¯ã€ã‚ãªãŸã«å¯¾ã—ã€ã“れを賠償ã™ã‚‹è²¬ä»»ã‚’è² ã‚ãªã„。
-
-第7æ¡ çµ‚äº†
-
- a. ã“ã®åˆ©ç”¨è¨±è«¾ã¯ã€ã‚ãªãŸãŒã“ã®åˆ©ç”¨è¨±è«¾ã®æ¡é …ã«é•åã™ã‚‹ã¨è‡ªå‹•çš„ã«çµ‚了ã™ã‚‹ã€‚ã—ã‹ã—ã€æœ¬ä½œå“ã€ãã®äºŒæ¬¡çš„著作物åˆã¯ç·¨é›†è‘—作物等をã‚ãªãŸã‹ã‚‰ã“ã®åˆ©ç”¨è¨±è«¾ã«åŸºã¥ãå—é ˜ã—ãŸç¬¬ä¸‰è€…ã«å¯¾ã—ã¦ã¯ã€ãã®å—領者ãŒã“ã®åˆ©ç”¨è¨±è«¾ã‚’éµå®ˆã—ã¦ã„ã‚‹é™ã‚Šã€ã“ã®åˆ©ç”¨è¨±è«¾ã¯çµ‚了ã—ãªã„。第1æ¡ã€ç¬¬2æ¡ã€ç¬¬4æ¡ã‹ã‚‰ç¬¬9æ¡ã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾ãŒçµ‚了ã—ã¦ã‚‚ãªãŠæœ‰åйã«å­˜ç¶šã™ã‚‹ã€‚
- b. 上記aã«å®šã‚ã‚‹å ´åˆã‚’除ãã€ã“ã®åˆ©ç”¨è¨±è«¾ã«åŸºã¥ãライセンスã¯ã€æœ¬ä½œå“ã«å«ã¾ã‚Œã‚‹è‘—ä½œæ¨©æ³•ä¸Šã®æ¨©åˆ©ãŒå­˜ç¶šã™ã‚‹ã‹ãŽã‚Šç¶™ç¶šã™ã‚‹ã€‚
- c. 許諾者ã¯ã€ä¸Šè¨˜aãŠã‚ˆã³bã«é–¢ã‚らãšã€ã„ã¤ã§ã‚‚ã€æœ¬ä½œå“ã‚’ã“ã®åˆ©ç”¨è¨±è«¾ã«åŸºã¥ã„ã¦é ’布ã™ã‚‹ã“ã¨ã‚’å°†æ¥ã«å‘ã‹ã£ã¦ä¸­æ­¢ã™ã‚‹ã“ã¨ãŒã§ãる。ãŸã ã—ã€è¨±è«¾è€…ãŒã“ã®åˆ©ç”¨è¨±è«¾ã«åŸºã¥ã頒布を将æ¥ã«å‘ã‹ã£ã¦ä¸­æ­¢ã—ãŸå ´åˆã§ã‚‚ã€ã“ã®åˆ©ç”¨è¨±è«¾ã«åŸºã¥ã„ã¦ã™ã§ã«æœ¬ä½œå“ã‚’å—é ˜ã—ãŸåˆ©ç”¨è€…ã«å¯¾ã—ã¦ã¯ã€ã“ã®åˆ©ç”¨è¨±è«¾ã«åŸºã¥ã„ã¦éŽåŽ»åŠã³å°†æ¥ã«ä¸Žãˆã‚‰ã‚Œã‚‹ã„ã‹ãªã‚‹ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã‚‚終了ã™ã‚‹ã“ã¨ã¯ãªã„。ã¾ãŸã€ä¸Šè¨˜ã«ã‚ˆã£ã¦çµ‚了ã—ãªã„é™ã‚Šã€ã“ã®åˆ©ç”¨è¨±è«¾ã¯ã€å…¨é¢çš„ã«æœ‰åйãªã‚‚ã®ã¨ã—ã¦ç¶™ç¶šã™ã‚‹ã€‚
-
-第8æ¡ ãã®ä»–
-
- a. ã“ã®åˆ©ç”¨è¨±è«¾ã®ã„ãšã‚Œã‹ã®è¦å®šãŒã€é©ç”¨æ³•ã®ä¸‹ã§ç„¡åйåŠã³ï¼åˆã¯åŸ·è¡Œä¸èƒ½ã®å ´åˆã§ã‚ã£ã¦ã‚‚ã€ã“ã®åˆ©ç”¨è¨±è«¾ã®ä»–ã®æ¡é …ã®æœ‰åŠ¹æ€§åŠã³åŸ·è¡Œå¯èƒ½æ€§ã«ã¯å½±éŸ¿ã—ãªã„。
- b. ã“ã®åˆ©ç”¨è¨±è«¾ã®æ¡é …ã®å…¨éƒ¨åˆã¯ä¸€éƒ¨ã®æ”¾æ£„åˆã¯ãã®é•åã«é–¢ã™ã‚‹æ‰¿è«¾ã¯ã€ã“ã‚ŒãŒæ›¸é¢ã«ã•れã€å½“該放棄åˆã¯æ‰¿è«¾ã«è²¬ä»»ã‚’è² ã†å½“事者ã«ã‚ˆã‚‹ç½²ååˆã¯è¨˜å押å°ãŒãªã•れãªã„é™ã‚Šã€è¡Œã†ã“ã¨ãŒã§ããªã„。
- c. ã“ã®åˆ©ç”¨è¨±è«¾ã¯ã€å½“äº‹è€…ãŒæœ¬ä½œå“ã«é–¢ã—ã¦è¡Œã£ãŸæœ€çµ‚ã‹ã¤å”¯ä¸€ã®åˆæ„ã®å†…容ã§ã‚る。ã“ã®åˆ©ç”¨è¨±è«¾ã¯ã€è¨±è«¾è€…ã¨ã‚ãªãŸã¨ã®ç›¸äº’ã®æ›¸é¢ã«ã‚ˆã‚‹åˆæ„ãªã修正ã•れãªã„。
- d. ã“ã®åˆ©ç”¨è¨±è«¾ã¯æ—¥æœ¬èªžã«ã‚ˆã‚Šæä¾›ã•れる。ã“ã®åˆ©ç”¨è¨±è«¾ã®è‹±èªžãã®ä»–ã®è¨€èªžã¸ã®ç¿»è¨³ã¯å‚ç…§ã®ãŸã‚ã®ã‚‚ã®ã«éŽãŽãšã€ã“ã®åˆ©ç”¨è¨±è«¾ã®æ—¥æœ¬èªžç‰ˆã¨ç¿»è¨³ã¨ã®é–“ã«ä½•らã‹ã®é½Ÿé½¬ãŒã‚ã‚‹å ´åˆã«ã¯æ—¥æœ¬èªžç‰ˆãŒå„ªå…ˆã™ã‚‹ã€‚
-
-第9æ¡ æº–æ‹ æ³•
-
-ã“ã®åˆ©ç”¨è¨±è«¾ã¯ã€æ—¥æœ¬æ³•ã«åŸºã¥ã解釈ã•れる。
-
-本作å“ãŒã‚¯ãƒªã‚¨ã‚¤ãƒ†ã‚£ãƒ–・コモンズ・ライセンスã«åŸºã¥ã利用許諾ã•れãŸã“ã¨ã‚’公衆ã«ç¤ºã™ã¨ã„ã†é™å®šã•れãŸç›®çš„ã®å ´åˆã‚’除ãã€è¨±è«¾è€…も被許諾者もクリエイティブ・コモンズã®äº‹å‰ã®æ›¸é¢ã«ã‚ˆã‚‹åŒæ„ãªã—ã«ã€Œã‚¯ãƒªã‚¨ã‚¤ãƒ†ã‚£ãƒ–・コモンズã€ã®å•†æ¨™è‹¥ã—ãã¯é–¢é€£å•†æ¨™åˆã¯ã‚¯ãƒªã‚¨ã‚¤ãƒ†ã‚£ãƒ–・コモンズã®ãƒ­ã‚´ã‚’使用ã—ãªã„ã‚‚ã®ã¨ã—ã¾ã™ã€‚使用ãŒè¨±å¯ã•れãŸå ´åˆã¯ã‚¯ãƒªã‚¨ã‚¤ãƒ†ã‚£ãƒ–・コモンズãŠã‚ˆã³ã‚¯ãƒªã‚¨ã‚¤ãƒ†ã‚£ãƒ–・コモンズ・ジャパンã®ã‚¦ã‚§ãƒ–サイト上ã«å…¬è¡¨ã•れるã€åˆã¯ãã®ä»–éšæ™‚è¦æ±‚ã«å¾“ã„利用å¯èƒ½ã¨ãªã‚‹ã€ã‚¯ãƒªã‚¨ã‚¤ãƒ†ã‚£ãƒ–・コモンズã®å½“該時点ã«ãŠã‘る商標使用指é‡ã‚’éµå®ˆã™ã‚‹ã‚‚ã®ã¨ã—ã¾ã™ã€‚クリエイティブ・コモンズ㯠https://creativecommons.org/ã‹ã‚‰ã€ã‚¯ãƒªã‚¨ã‚¤ãƒ†ã‚£ãƒ–・コモンズ・ジャパンã¯http://www.creativecommons.jp/ã‹ã‚‰é€£çµ¡ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚
diff --git a/options/license/CC-BY-SA-2.5 b/options/license/CC-BY-SA-2.5
deleted file mode 100644
index 4a2ed20da9..0000000000
--- a/options/license/CC-BY-SA-2.5
+++ /dev/null
@@ -1,85 +0,0 @@
-Creative Commons Attribution-ShareAlike 2.5
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
-
- b. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
-
- c. "Licensor" means the individual or entity that offers the Work under the terms of this License.
-
- d. "Original Author" means the individual or entity who created the Work.
-
- e. "Work" means the copyrightable work of authorship offered under the terms of this License.
-
- f. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
- g. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
-
- b. to create and reproduce Derivative Works;
-
- c. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
-
- d. to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
-
- e. For the avoidance of doubt, where the work is a musical composition:
-
- i. Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
-
- ii. Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
-
- f. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).
-
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(c), as requested.
-
- b. You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.5 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
-
- c. If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CC-BY-SA-3.0 b/options/license/CC-BY-SA-3.0
deleted file mode 100644
index 604209a804..0000000000
--- a/options/license/CC-BY-SA-3.0
+++ /dev/null
@@ -1,359 +0,0 @@
-Creative Commons Legal Code
-
-Attribution-ShareAlike 3.0 Unported
-
- CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
- LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
- ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
- INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
- REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
- DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
-COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
-COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
-AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
-TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
-BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
-CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
-CONDITIONS.
-
-1. Definitions
-
- a. "Adaptation" means a work based upon the Work, or upon the Work and
- other pre-existing works, such as a translation, adaptation,
- derivative work, arrangement of music or other alterations of a
- literary or artistic work, or phonogram or performance and includes
- cinematographic adaptations or any other form in which the Work may be
- recast, transformed, or adapted including in any form recognizably
- derived from the original, except that a work that constitutes a
- Collection will not be considered an Adaptation for the purpose of
- this License. For the avoidance of doubt, where the Work is a musical
- work, performance or phonogram, the synchronization of the Work in
- timed-relation with a moving image ("synching") will be considered an
- Adaptation for the purpose of this License.
- b. "Collection" means a collection of literary or artistic works, such as
- encyclopedias and anthologies, or performances, phonograms or
- broadcasts, or other works or subject matter other than works listed
- in Section 1(f) below, which, by reason of the selection and
- arrangement of their contents, constitute intellectual creations, in
- which the Work is included in its entirety in unmodified form along
- with one or more other contributions, each constituting separate and
- independent works in themselves, which together are assembled into a
- collective whole. A work that constitutes a Collection will not be
- considered an Adaptation (as defined below) for the purposes of this
- License.
- c. "Creative Commons Compatible License" means a license that is listed
- at https://creativecommons.org/compatiblelicenses that has been
- approved by Creative Commons as being essentially equivalent to this
- License, including, at a minimum, because that license: (i) contains
- terms that have the same purpose, meaning and effect as the License
- Elements of this License; and, (ii) explicitly permits the relicensing
- of adaptations of works made available under that license under this
- License or a Creative Commons jurisdiction license with the same
- License Elements as this License.
- d. "Distribute" means to make available to the public the original and
- copies of the Work or Adaptation, as appropriate, through sale or
- other transfer of ownership.
- e. "License Elements" means the following high-level license attributes
- as selected by Licensor and indicated in the title of this License:
- Attribution, ShareAlike.
- f. "Licensor" means the individual, individuals, entity or entities that
- offer(s) the Work under the terms of this License.
- g. "Original Author" means, in the case of a literary or artistic work,
- the individual, individuals, entity or entities who created the Work
- or if no individual or entity can be identified, the publisher; and in
- addition (i) in the case of a performance the actors, singers,
- musicians, dancers, and other persons who act, sing, deliver, declaim,
- play in, interpret or otherwise perform literary or artistic works or
- expressions of folklore; (ii) in the case of a phonogram the producer
- being the person or legal entity who first fixes the sounds of a
- performance or other sounds; and, (iii) in the case of broadcasts, the
- organization that transmits the broadcast.
- h. "Work" means the literary and/or artistic work offered under the terms
- of this License including without limitation any production in the
- literary, scientific and artistic domain, whatever may be the mode or
- form of its expression including digital form, such as a book,
- pamphlet and other writing; a lecture, address, sermon or other work
- of the same nature; a dramatic or dramatico-musical work; a
- choreographic work or entertainment in dumb show; a musical
- composition with or without words; a cinematographic work to which are
- assimilated works expressed by a process analogous to cinematography;
- a work of drawing, painting, architecture, sculpture, engraving or
- lithography; a photographic work to which are assimilated works
- expressed by a process analogous to photography; a work of applied
- art; an illustration, map, plan, sketch or three-dimensional work
- relative to geography, topography, architecture or science; a
- performance; a broadcast; a phonogram; a compilation of data to the
- extent it is protected as a copyrightable work; or a work performed by
- a variety or circus performer to the extent it is not otherwise
- considered a literary or artistic work.
- i. "You" means an individual or entity exercising rights under this
- License who has not previously violated the terms of this License with
- respect to the Work, or who has received express permission from the
- Licensor to exercise rights under this License despite a previous
- violation.
- j. "Publicly Perform" means to perform public recitations of the Work and
- to communicate to the public those public recitations, by any means or
- process, including by wire or wireless means or public digital
- performances; to make available to the public Works in such a way that
- members of the public may access these Works from a place and at a
- place individually chosen by them; to perform the Work to the public
- by any means or process and the communication to the public of the
- performances of the Work, including by public digital performance; to
- broadcast and rebroadcast the Work by any means including signs,
- sounds or images.
- k. "Reproduce" means to make copies of the Work by any means including
- without limitation by sound or visual recordings and the right of
- fixation and reproducing fixations of the Work, including storage of a
- protected performance or phonogram in digital form or other electronic
- medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce,
-limit, or restrict any uses free from copyright or rights arising from
-limitations or exceptions that are provided for in connection with the
-copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License,
-Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
-perpetual (for the duration of the applicable copyright) license to
-exercise the rights in the Work as stated below:
-
- a. to Reproduce the Work, to incorporate the Work into one or more
- Collections, and to Reproduce the Work as incorporated in the
- Collections;
- b. to create and Reproduce Adaptations provided that any such Adaptation,
- including any translation in any medium, takes reasonable steps to
- clearly label, demarcate or otherwise identify that changes were made
- to the original Work. For example, a translation could be marked "The
- original work was translated from English to Spanish," or a
- modification could indicate "The original work has been modified.";
- c. to Distribute and Publicly Perform the Work including as incorporated
- in Collections; and,
- d. to Distribute and Publicly Perform Adaptations.
- e. For the avoidance of doubt:
-
- i. Non-waivable Compulsory License Schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme cannot be waived, the Licensor
- reserves the exclusive right to collect such royalties for any
- exercise by You of the rights granted under this License;
- ii. Waivable Compulsory License Schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme can be waived, the Licensor waives the
- exclusive right to collect such royalties for any exercise by You
- of the rights granted under this License; and,
- iii. Voluntary License Schemes. The Licensor waives the right to
- collect royalties, whether individually or, in the event that the
- Licensor is a member of a collecting society that administers
- voluntary licensing schemes, via that society, from any exercise
- by You of the rights granted under this License.
-
-The above rights may be exercised in all media and formats whether now
-known or hereafter devised. The above rights include the right to make
-such modifications as are technically necessary to exercise the rights in
-other media and formats. Subject to Section 8(f), all rights not expressly
-granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made
-subject to and limited by the following restrictions:
-
- a. You may Distribute or Publicly Perform the Work only under the terms
- of this License. You must include a copy of, or the Uniform Resource
- Identifier (URI) for, this License with every copy of the Work You
- Distribute or Publicly Perform. You may not offer or impose any terms
- on the Work that restrict the terms of this License or the ability of
- the recipient of the Work to exercise the rights granted to that
- recipient under the terms of the License. You may not sublicense the
- Work. You must keep intact all notices that refer to this License and
- to the disclaimer of warranties with every copy of the Work You
- Distribute or Publicly Perform. When You Distribute or Publicly
- Perform the Work, You may not impose any effective technological
- measures on the Work that restrict the ability of a recipient of the
- Work from You to exercise the rights granted to that recipient under
- the terms of the License. This Section 4(a) applies to the Work as
- incorporated in a Collection, but this does not require the Collection
- apart from the Work itself to be made subject to the terms of this
- License. If You create a Collection, upon notice from any Licensor You
- must, to the extent practicable, remove from the Collection any credit
- as required by Section 4(c), as requested. If You create an
- Adaptation, upon notice from any Licensor You must, to the extent
- practicable, remove from the Adaptation any credit as required by
- Section 4(c), as requested.
- b. You may Distribute or Publicly Perform an Adaptation only under the
- terms of: (i) this License; (ii) a later version of this License with
- the same License Elements as this License; (iii) a Creative Commons
- jurisdiction license (either this or a later license version) that
- contains the same License Elements as this License (e.g.,
- Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible
- License. If you license the Adaptation under one of the licenses
- mentioned in (iv), you must comply with the terms of that license. If
- you license the Adaptation under the terms of any of the licenses
- mentioned in (i), (ii) or (iii) (the "Applicable License"), you must
- comply with the terms of the Applicable License generally and the
- following provisions: (I) You must include a copy of, or the URI for,
- the Applicable License with every copy of each Adaptation You
- Distribute or Publicly Perform; (II) You may not offer or impose any
- terms on the Adaptation that restrict the terms of the Applicable
- License or the ability of the recipient of the Adaptation to exercise
- the rights granted to that recipient under the terms of the Applicable
- License; (III) You must keep intact all notices that refer to the
- Applicable License and to the disclaimer of warranties with every copy
- of the Work as included in the Adaptation You Distribute or Publicly
- Perform; (IV) when You Distribute or Publicly Perform the Adaptation,
- You may not impose any effective technological measures on the
- Adaptation that restrict the ability of a recipient of the Adaptation
- from You to exercise the rights granted to that recipient under the
- terms of the Applicable License. This Section 4(b) applies to the
- Adaptation as incorporated in a Collection, but this does not require
- the Collection apart from the Adaptation itself to be made subject to
- the terms of the Applicable License.
- c. If You Distribute, or Publicly Perform the Work or any Adaptations or
- Collections, You must, unless a request has been made pursuant to
- Section 4(a), keep intact all copyright notices for the Work and
- provide, reasonable to the medium or means You are utilizing: (i) the
- name of the Original Author (or pseudonym, if applicable) if supplied,
- and/or if the Original Author and/or Licensor designate another party
- or parties (e.g., a sponsor institute, publishing entity, journal) for
- attribution ("Attribution Parties") in Licensor's copyright notice,
- terms of service or by other reasonable means, the name of such party
- or parties; (ii) the title of the Work if supplied; (iii) to the
- extent reasonably practicable, the URI, if any, that Licensor
- specifies to be associated with the Work, unless such URI does not
- refer to the copyright notice or licensing information for the Work;
- and (iv) , consistent with Ssection 3(b), in the case of an
- Adaptation, a credit identifying the use of the Work in the Adaptation
- (e.g., "French translation of the Work by Original Author," or
- "Screenplay based on original Work by Original Author"). The credit
- required by this Section 4(c) may be implemented in any reasonable
- manner; provided, however, that in the case of a Adaptation or
- Collection, at a minimum such credit will appear, if a credit for all
- contributing authors of the Adaptation or Collection appears, then as
- part of these credits and in a manner at least as prominent as the
- credits for the other contributing authors. For the avoidance of
- doubt, You may only use the credit required by this Section for the
- purpose of attribution in the manner set out above and, by exercising
- Your rights under this License, You may not implicitly or explicitly
- assert or imply any connection with, sponsorship or endorsement by the
- Original Author, Licensor and/or Attribution Parties, as appropriate,
- of You or Your use of the Work, without the separate, express prior
- written permission of the Original Author, Licensor and/or Attribution
- Parties.
- d. Except as otherwise agreed in writing by the Licensor or as may be
- otherwise permitted by applicable law, if You Reproduce, Distribute or
- Publicly Perform the Work either by itself or as part of any
- Adaptations or Collections, You must not distort, mutilate, modify or
- take other derogatory action in relation to the Work which would be
- prejudicial to the Original Author's honor or reputation. Licensor
- agrees that in those jurisdictions (e.g. Japan), in which any exercise
- of the right granted in Section 3(b) of this License (the right to
- make Adaptations) would be deemed to be a distortion, mutilation,
- modification or other derogatory action prejudicial to the Original
- Author's honor and reputation, the Licensor will waive or not assert,
- as appropriate, this Section, to the fullest extent permitted by the
- applicable national law, to enable You to reasonably exercise Your
- right under Section 3(b) of this License (right to make Adaptations)
- but not otherwise.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
-OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
-KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
-INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
-FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
-LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
-WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
-OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
-LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
-ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
-ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
-BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate
- automatically upon any breach by You of the terms of this License.
- Individuals or entities who have received Adaptations or Collections
- from You under this License, however, will not have their licenses
- terminated provided such individuals or entities remain in full
- compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
- survive any termination of this License.
- b. Subject to the above terms and conditions, the license granted here is
- perpetual (for the duration of the applicable copyright in the Work).
- Notwithstanding the above, Licensor reserves the right to release the
- Work under different license terms or to stop distributing the Work at
- any time; provided, however that any such election will not serve to
- withdraw this License (or any other license that has been, or is
- required to be, granted under the terms of this License), and this
- License will continue in full force and effect unless terminated as
- stated above.
-
-8. Miscellaneous
-
- a. Each time You Distribute or Publicly Perform the Work or a Collection,
- the Licensor offers to the recipient a license to the Work on the same
- terms and conditions as the license granted to You under this License.
- b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
- offers to the recipient a license to the original Work on the same
- terms and conditions as the license granted to You under this License.
- c. If any provision of this License is invalid or unenforceable under
- applicable law, it shall not affect the validity or enforceability of
- the remainder of the terms of this License, and without further action
- by the parties to this agreement, such provision shall be reformed to
- the minimum extent necessary to make such provision valid and
- enforceable.
- d. No term or provision of this License shall be deemed waived and no
- breach consented to unless such waiver or consent shall be in writing
- and signed by the party to be charged with such waiver or consent.
- e. This License constitutes the entire agreement between the parties with
- respect to the Work licensed here. There are no understandings,
- agreements or representations with respect to the Work not specified
- here. Licensor shall not be bound by any additional provisions that
- may appear in any communication from You. This License may not be
- modified without the mutual written agreement of the Licensor and You.
- f. The rights granted under, and the subject matter referenced, in this
- License were drafted utilizing the terminology of the Berne Convention
- for the Protection of Literary and Artistic Works (as amended on
- September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
- Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
- and the Universal Copyright Convention (as revised on July 24, 1971).
- These rights and subject matter take effect in the relevant
- jurisdiction in which the License terms are sought to be enforced
- according to the corresponding provisions of the implementation of
- those treaty provisions in the applicable national law. If the
- standard suite of rights granted under applicable copyright law
- includes additional rights not granted under this License, such
- additional rights are deemed to be included in the License; this
- License is not intended to restrict the license of any rights under
- applicable law.
-
-
-Creative Commons Notice
-
- Creative Commons is not a party to this License, and makes no warranty
- whatsoever in connection with the Work. Creative Commons will not be
- liable to You or any party on any legal theory for any damages
- whatsoever, including without limitation any general, special,
- incidental or consequential damages arising in connection to this
- license. Notwithstanding the foregoing two (2) sentences, if Creative
- Commons has expressly identified itself as the Licensor hereunder, it
- shall have all rights and obligations of Licensor.
-
- Except for the limited purpose of indicating to the public that the
- Work is licensed under the CCPL, Creative Commons does not authorize
- the use by either party of the trademark "Creative Commons" or any
- related trademark or logo of Creative Commons without the prior
- written consent of Creative Commons. Any permitted use will be in
- compliance with Creative Commons' then-current trademark usage
- guidelines, as may be published on its website or otherwise made
- available upon request from time to time. For the avoidance of doubt,
- this trademark restriction does not form part of the License.
-
- Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-BY-SA-3.0-AT b/options/license/CC-BY-SA-3.0-AT
deleted file mode 100644
index 365b5f705d..0000000000
--- a/options/license/CC-BY-SA-3.0-AT
+++ /dev/null
@@ -1,139 +0,0 @@
-CREATIVE COMMONS IST KEINE RECHTSANWALTSKANZLEI UND LEISTET KEINE RECHTSBERATUNG. DIE BEREITSTELLUNG DIESER LIZENZ FÜHRT ZU KEINEM MANDATSVERHÄLTNIS. CREATIVE COMMONS STELLT DIESE INFORMATIONEN OHNE GEWÄHR ZUR VERFÜGUNG. CREATIVE COMMONS ÜBERNIMMT KEINE GEWÄHRLEISTUNG FÜR DIE GELIEFERTEN INFORMATIONEN UND SCHLIEßT DIE HAFTUNG FÜR SCHÄDEN AUS, DIE SICH AUS DEREN GEBRAUCH ERGEBEN.
-
-Lizenz
-
-DER GEGENSTAND DIESER LIZENZ (WIE UNTER „SCHUTZGEGENSTAND“ DEFINIERT) WIRD UNTER DEN BEDINGUNGEN DIESER CREATIVE COMMONS PUBLIC LICENSE ("CCPL", „LIZENZ“ ODER "LIZENZVERTRAG") ZUR VERFÜGUNG GESTELLT. DER SCHUTZGEGENSTAND IST DURCH DAS URHEBERRECHT UND/ODER ANDERE GESETZE GESCHÜTZT. JEDE FORM DER NUTZUNG DES SCHUTZGEGENSTANDES, DIE NICHT AUFGRUND DIESER LIZENZ ODER DURCH GESETZE GESTATTET IST, IST UNZULÄSSIG.
-
-DURCH DIE AUSÜBUNG EINES DURCH DIESE LIZENZ GEWÄHRTEN RECHTS AN DEM SCHUTZGEGENSTAND ERKLÄREN SIE SICH MIT DEN LIZENZBEDINGUNGEN RECHTSVERBINDLICH EINVERSTANDEN. SOWEIT DIESE LIZENZ ALS LIZENZVERTRAG ANZUSEHEN IST, GEWÄHRT IHNEN DER LIZENZGEBER DIE IN DER LIZENZ GENANNTEN RECHTE UNENTGELTLICH UND IM AUSTAUSCH DAFÜR, DASS SIE DAS GEBUNDENSEIN AN DIE LIZENZBEDINGUNGEN AKZEPTIEREN.
-
-1. Definitionen
-
- a. Der Begriff "Bearbeitung" im Sinne dieser Lizenz bezeichnet das Ergebnis jeglicher Art von Veränderung des Schutzgegenstandes, solange dieses erkennbar vom Schutzgegenstand abgeleitet wurde. Dies kann insbesondere auch eine Umgestaltung, Änderung, Anpassung, Übersetzung oder Heranziehung des Schutzgegenstandes zur Vertonung von Laufbildern sein. Nicht als Bearbeitung des Schutzgegenstandes gelten seine Aufnahme in eine Sammlung oder ein Sammelwerk und die freie Nutzung des Schutzgegenstandes.
-
- b. Der Begriff "Sammelwerk" im Sinne dieser Lizenz meint eine Zusammenstellung von literarischen, künstlerischen oder wissenschaftlichen Inhalten zu einem einheitlichen Ganzen, sofern diese Zusammenstellung aufgrund von Auswahl und Anordnung der darin enthaltenen selbständigen Elemente eine eigentümliche geistige Schöpfung darstellt, unabhängig davon, ob die Elemente systematisch oder methodisch angelegt und dadurch einzeln zugänglich sind oder nicht.
-
- c. "Verbreiten" im Sinne dieser Lizenz bedeutet, den Schutzgegenstand oder Bearbeitungen im Original oder in Form von Vervielfältigungsstücken, mithin in körperlich fixierter Form der Öffentlichkeit zugänglich zu machen oder in Verkehr zu bringen.
-
- d. Unter "Lizenzelementen" werden im Sinne dieser Lizenz die folgenden übergeordneten Lizenzcharakteristika verstanden, die vom Lizenzgeber ausgewählt wurden und in der Bezeichnung der Lizenz zum Ausdruck kommen: "Namensnennung", "Weitergabe unter gleichen Bedingungen".
-
- e. Der "Lizenzgeber" im Sinne dieser Lizenz ist diejenige natürliche oder juristische Person oder Gruppe, die den Schutzgegenstand unter den Bedingungen dieser Lizenz anbietet und insoweit als Rechteinhaberin auftritt.
-
- f. "Rechteinhaber" im Sinne dieser Lizenz ist der Urheber des Schutzgegenstandes oder jede andere natürliche oder juristische Person, die am Schutzgegenstand ein Immaterialgüterrecht erlangt hat, welches die in Abschnitt 3 genannten Handlungen erfasst und eine Erteilung, Übertragung oder Einräumung von Nutzungsbewilligungen bzw Nutzungsrechten an Dritte erlaubt.
-
- g. Der Begriff "Schutzgegenstand" bezeichnet in dieser Lizenz den literarischen, künstlerischen oder wissenschaftlichen Inhalt, der unter den Bedingungen dieser Lizenz angeboten wird. Das kann insbesondere eine eigentümliche geistige Schöpfung jeglicher Art oder ein Werk der kleinen Münze, ein nachgelassenes Werk oder auch ein Lichtbild oder anderes Objekt eines verwandten Schutzrechts sein, unabhängig von der Art seiner Fixierung und unabhängig davon, auf welche Weise jeweils eine Wahrnehmung erfolgen kann, gleichviel ob in analoger oder digitaler Form. Soweit Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen, unterfallen auch sie dem Begriff „Schutzgegenstand“ im Sinne dieser Lizenz.
-
- h. Mit "Sie" bzw. "Ihnen" ist die natürliche oder juristische Person gemeint, die in dieser Lizenz im Abschnitt 3 genannte Nutzungen des Schutzgegenstandes vornimmt und zuvor in Hinblick auf den Schutzgegenstand nicht gegen Bedingungen dieser Lizenz verstoßen oder aber die ausdrückliche Erlaubnis des Lizenzgebers erhalten hat, die durch diese Lizenz gewährte Nutzungsbewilligung trotz eines vorherigen Verstoßes auszuüben.
-
- i. Unter "Öffentlich Wiedergeben" im Sinne dieser Lizenz sind Wahrnehmbarmachungen des Schutzgegenstandes in unkörperlicher Form zu verstehen, die für eine Mehrzahl von Mitgliedern der Öffentlichkeit bestimmt sind und mittels öffentlicher Wiedergabe in Form von Vortrag, Aufführung, Vorführung, Darbietung, Sendung, Weitersendung oder zeit- und ortsunabhängiger Zurverfügungstellung erfolgen, unabhängig von den zum Einsatz kommenden Techniken und Verfahren, einschließlich drahtgebundener oder drahtloser Mittel und Einstellen in das Internet.
-
- j. "Vervielfältigen" im Sinne dieser Lizenz bedeutet, gleichviel in welchem Verfahren, auf welchem Träger, in welcher Menge und ob vorübergehend oder dauerhaft, Vervielfältigungsstücke des Schutzgegenstandes herzustellen, insbesondere durch Ton- oder Bildaufzeichnungen, und umfasst auch das erstmalige Festhalten des Schutzgegenstandes oder dessen Wahrnehmbarmachung auf Mitteln der wiederholbaren Wiedergabe sowie das Herstellen von Vervielfältigungsstücken dieser Festhaltung, sowie die Speicherung einer geschützten Darbietung oder eines Bild- und/oder Schallträgers in digitaler Form oder auf einem anderen elektronischen Medium.
-
- k. "Mit Creative Commons kompatible Lizenz" bezeichnet eine Lizenz, die unter https://creativecommons.org/compatiblelicenses aufgelistet ist und die durch Creative Commons als grundsätzlich zur vorliegenden Lizenz äquivalent akzeptiert wurde, da zumindest folgende Voraussetzungen erfüllt sind:
-
- Diese mit Creative Commons kompatible Lizenz
-
- i. enthält Bestimmungen, welche die gleichen Ziele verfolgen, die gleiche Bedeutung haben und die gleichen Wirkungen erzeugen wie die Lizenzelemente der vorliegenden Lizenz; und
-
- ii. erlaubt ausdrücklich das Lizenzieren von ihr unterstellten Abwandlungen unter vorliegender Lizenz, unter einer anderen rechtsordnungsspezifisch angepassten Creative-Commons-Lizenz mit denselben Lizenzelementen wie vorliegende Lizenz aufweist oder unter der entsprechenden Creative-Commons-Unported-Lizenz.
-
-2. Beschränkungen der Verwertungsrechte
-
-Diese Lizenz ist in keiner Weise darauf gerichtet, Befugnisse zur Nutzung des Schutzgegenstandes zu vermindern, zu beschränken oder zu vereiteln, die sich aus den Beschränkungen der Verwertungsrechte, anderen Beschränkungen der Ausschließlichkeitsrechte des Rechtsinhabers oder anderen entsprechenden Rechtsnormen oder sich aus dem Fehlen eines immaterialgüterrechtlichen Schutzes ergeben.
-
-3. Lizenzierung
-
-Unter den Bedingungen dieser Lizenz erteilt Ihnen der Lizenzgeber - unbeschadet unverzichtbarer Rechte und vorbehaltlich des Abschnitts 3.e) - die vergütungsfreie, räumlich und zeitlich (für die Dauer des Urheberrechts oder verwandten Schutzrechts am Schutzgegenstand) unbeschränkte Nutzungsbewilligung, den Schutzgegenstand in der folgenden Art und Weise zu nutzen:
-
- a. Den Schutzgegenstand in beliebiger Form und Menge zu vervielfältigen, ihn in Sammelwerke zu integrieren und ihn als Teil solcher Sammelwerke zu vervielfältigen;
-
- b. Den Schutzgegenstand zu bearbeiten, einschließlich Übersetzungen unter Nutzung jedweder Medien anzufertigen, sofern deutlich erkennbar gemacht wird, dass es sich um eine Bearbeitung handelt;
-
- c. Den Schutzgegenstand, allein oder in Sammelwerke aufgenommen, öffentlich wiederzugeben und zu verbreiten; und
-
- d. Bearbeitungen des Schutzgegenstandes zu veröffentlichen, öffentlich wiederzugeben und zu verbreiten.
-
- e. Bezüglich Vergütung für die Nutzung des Schutzgegenstandes gilt Folgendes:
-
- i. Unverzichtbare gesetzliche Vergütungsansprüche: Soweit unverzichtbare Vergütungsansprüche im Gegenzug für gesetzliche Lizenzen vorgesehen oder Pauschalabgabensysteme (zum Beispiel für Leermedien) vorhanden sind, behält sich der Lizenzgeber das ausschließliche Recht vor, die entsprechenden Vergütungsansprüche für jede Ausübung eines Rechts aus dieser Lizenz durch Sie geltend zu machen.
-
- ii. Vergütung bei Zwangslizenzen: Soweit Zwangslizenzen außerhalb dieser Lizenz vorgesehen sind und zustande kommen, verzichtet der Lizenzgeber für alle Fälle einer lizenzgerechten Nutzung des Schutzgegenstandes durch Sie auf jegliche Vergütung.
-
- iii. Vergütung in sonstigen Fällen: Bezüglich lizenzgerechter Nutzung des Schutzgegenstandes durch Sie, die nicht unter die beiden vorherigen Abschnitte (i) und (ii) fällt, verzichtet der Lizenzgeber auf jegliche Vergütung, unabhängig davon, ob eine Geltendmachung der Vergütungsansprüche durch ihn selbst oder nur durch eine Verwertungsgesellschaft möglich wäre.
-
-Die vorgenannte Nutzungsbewilligung wird für alle bekannten sowie alle noch nicht bekannten Nutzungsarten eingeräumt. Sie beinhaltet auch das Recht, solche Änderungen am Schutzgegenstand vorzunehmen, die für bestimmte nach dieser Lizenz zulässige Nutzungen technisch erforderlich sind. Alle sonstigen Rechte, die über diesen Abschnitt hinaus nicht ausdrücklich vom Lizenzgeber eingeräumt werden, bleiben diesem allein vorbehalten. Soweit Datenbanken oder Zusammenstellungen von Daten Schutzgegenstand dieser Lizenz oder Teil dessen sind und einen immaterialgüterrechtlichen Schutz eigener Art genießen, verzichtet der Lizenzgeber auf die Geltendmachung sämtlicher daraus resultierender Rechte.
-
-4. Bedingungen
-
-Die Erteilung der Nutzungsbewilligung gemäß Abschnitt 3 dieser Lizenz erfolgt ausdrücklich nur unter den folgenden Bedingungen:
-
- a. Sie dürfen den Schutzgegenstand ausschließlich unter den Bedingungen dieser Lizenz verbreiten oder öffentlich wiedergeben. Sie müssen dabei stets eine Kopie dieser Lizenz oder deren vollständige Internetadresse in Form des Uniform-Resource-Identifier (URI) beifügen. Sie dürfen keine Vertrags- oder Nutzungsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch diese Lizenz gewährten Rechte beschränken. Sie dürfen den Schutzgegenstand nicht unterlizenzieren. Bei jeder Kopie des Schutzgegenstandes, die Sie verbreiten oder öffentlich wiedergeben, müssen Sie alle Hinweise unverändert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Wenn Sie den Schutzgegenstand verbreiten oder öffentlich wiedergeben, dürfen Sie (in Bezug auf den Schutzgegenstand) keine technischen Maßnahmen ergreifen, die den Nutzer des Schutzgegenstandes in der Ausübung der ihm durch diese Lizenz gewährten Rechte behindern können. Dasselbe gilt auch für den Fall, dass der Schutzgegenstand einen Bestandteil eines Sammelwerkes bildet, was jedoch nicht bedeutet, dass das Sammelwerk insgesamt dieser Lizenz unterstellt werden muss. Sofern Sie ein Sammelwerk erstellen, müssen Sie - soweit dies praktikabel ist - auf die Mitteilung eines Lizenzgebers hin aus dem Sammelwerk die in Abschnitt 4.c) aufgezählten Hinweise entfernen. Wenn Sie eine Bearbeitung vornehmen, müssen Sie – soweit dies praktikabel ist – auf die Mitteilung eines Lizenzgebers hin von der Bearbeitung die in Abschnitt 4.c) aufgezählten Hinweise entfernen.
-
- b. Sie dürfen eine Bearbeitung ausschließlich unter den Bedingungen
-
- i. dieser Lizenz,
-
- ii. einer späteren Version dieser Lizenz mit denselben Lizenzelementen,
-
- iii. einer rechtsordnungsspezifischen Creative-Commons-Lizenz mit denselben Lizenzelementen ab Version 3.0 aufwärts (z.B. Namensnennung - Weitergabe unter gleichen Bedingungen 3.0 US),
-
- iv. der Creative-Commons-Unported-Lizenz mit denselben Lizenzelementen ab Version 3.0 aufwärts, oder
-
- v. einer mit Creative Commons kompatiblen Lizenz
-
- verbreiten oder öffentlich wiedergeben.
-
- Falls Sie die Bearbeitung gemäß Abschnitt b)(v) unter einer mit Creative Commons kompatiblen Lizenz lizenzieren, müssen Sie deren Lizenzbestimmungen Folge leisten.
-
- Falls Sie die Bearbeitung unter einer der unter b)(i)-(iv) genannten Lizenzen ("Verwendbare Lizenzen") lizenzieren, müssen Sie deren Lizenzbestimmungen sowie folgenden Bestimmungen Folge leisten: Sie müssen stets eine Kopie der verwendbaren Lizenz oder deren vollständige Internetadresse in Form des Uniform-Resource-Identifier (URI) beifügen, wenn Sie die Bearbeitung verbreiten oder öffentlich wiedergeben. Sie dürfen keine Vertrags- oder Nutzungsbedingungen anbieten oder fordern, die die Bedingungen der verwendbaren Lizenz oder die durch sie gewährten Rechte beschränken. Bei jeder Bearbeitung, die Sie verbreiten oder öffentlich wiedergeben, müssen Sie alle Hinweise auf die verwendbare Lizenz und den Haftungsausschluss unverändert lassen. Wenn Sie die Bearbeitung verbreiten oder öffentlich wiedergeben, dürfen Sie (in Bezug auf die Bearbeitung) keine technischen Maßnahmen ergreifen, die den Nutzer der Bearbeitung in der Ausübung der ihm durch die verwendbare Lizenz gewährten Rechte behindern können. Dieser Abschnitt 4.b) gilt auch für den Fall, dass die Bearbeitung einen Bestandteil eines Sammelwerkes bildet; dies bedeutet jedoch nicht, dass das Sammelwerk insgesamt der verwendbaren Lizenz unterstellt werden muss.
-
- c. Die Verbreitung und die öffentliche Wiedergabe des Schutzgegenstandes oder auf ihm aufbauender Inhalte oder ihn enthaltender Sammelwerke ist Ihnen nur unter der Bedingung gestattet, dass Sie, vorbehaltlich etwaiger Mitteilungen im Sinne von Abschnitt 4.a), alle dazu gehörenden Rechtevermerke unberührt lassen. Sie sind verpflichtet, die Urheberschaft oder die Rechteinhaberschaft in einer der Nutzung entsprechenden, angemessenen Form anzuerkennen, indem Sie selbst – soweit bekannt – Folgendes angeben:
-
- i. Den Namen (oder das Pseudonym, falls ein solches verwendet wird) des Rechteinhabers, und/oder falls der Lizenzgeber im Rechtevermerk, in den Nutzungsbedingungen oder auf andere angemessene Weise eine Zuschreibung an Dritte vorgenommen hat (z.B. an eine Stiftung, ein Verlagshaus oder eine Zeitung) („Zuschreibungsempfänger“), Namen bzw. Bezeichnung dieses oder dieser Dritten;
-
- ii. den Titel des Inhaltes;
-
- iii. in einer praktikablen Form den Uniform-Resource-Identifier (URI, z.B. Internetadresse), den der Lizenzgeber zum Schutzgegenstand angegeben hat, es sei denn, dieser URI verweist nicht auf den Rechtevermerk oder die Lizenzinformationen zum Schutzgegenstand;
-
- iv. und im Falle einer Bearbeitung des Schutzgegenstandes in Übereinstimmung mit Abschnitt 3.b) einen Hinweis darauf, dass es sich um eine Bearbeitung handelt.
-
- Die nach diesem Abschnitt 4.c) erforderlichen Angaben können in jeder angemessenen Form gemacht werden; im Falle einer Bearbeitung des Schutzgegenstandes oder eines Sammelwerkes müssen diese Angaben das Minimum darstellen und bei gemeinsamer Nennung aller Beitragenden dergestalt erfolgen, dass sie zumindest ebenso hervorgehoben sind wie die Hinweise auf die übrigen Rechteinhaber. Die Angaben nach diesem Abschnitt dürfen Sie ausschließlich zur Angabe der Rechteinhaberschaft in der oben bezeichneten Weise verwenden. Durch die Ausübung Ihrer Rechte aus dieser Lizenz dürfen Sie ohne eine vorherige, separat und schriftlich vorliegende Zustimmung des Urhebers, des Lizenzgebers und/oder des Zuschreibungsempfängers weder implizit noch explizit irgendeine Verbindung mit dem oder eine Unterstützung oder Billigung durch den Lizenzgeber oder den Zuschreibungsempfänger andeuten oder erklären.
-
- d. Die oben unter 4.a) bis c) genannten Einschränkungen gelten nicht für solche Teile des Schutzgegenstandes, die allein deshalb unter den Schutzgegenstandsbegriff fallen, weil sie als Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen.
-
- e. (Urheber)Persönlichkeitsrechte bleiben - soweit sie bestehen - von dieser Lizenz unberührt.
-
-5. Gewährleistung
-
-SOFERN KEINE ANDERS LAUTENDE, SCHRIFTLICHE VEREINBARUNG ZWISCHEN DEM LIZENZGEBER UND IHNEN GESCHLOSSEN WURDE UND SOWEIT MÄNGEL NICHT ARGLISTIG VERSCHWIEGEN WURDEN, BIETET DER LIZENZGEBER DEN SCHUTZGEGENSTAND UND DIE ERTEILUNG DER NUTZUNGSBEWILLIGUNG UNTER AUSSCHLUSS JEGLICHER GEWÄHRLEISTUNG AN UND ÜBERNIMMT WEDER AUSDRÜCKLICH NOCH KONKLUDENT GARANTIEN IRGENDEINER ART. DIES UMFASST INSBESONDERE DAS FREISEIN VON SACH- UND RECHTSMÄNGELN, UNABHÄNGIG VON DEREN ERKENNBARKEIT FÜR DEN LIZENZGEBER, DIE VERKEHRSFÄHIGKEIT DES SCHUTZGEGENSTANDES, SEINE VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK SOWIE DIE KORREKTHEIT VON BESCHREIBUNGEN.
-
-6. Haftungsbeschränkung
-
-ÜBER DIE IN ZIFFER 5 GENANNTE GEWÄHRLEISTUNG HINAUS HAFTET DER LIZENZGEBER IHNEN GEGENÜBER FÜR SCHÄDEN JEGLICHER ART NUR BEI GROBER FAHRLÄSSIGKEIT ODER VORSATZ, UND ÜBERNIMMT DARÜBER HINAUS KEINERLEI FREIWILLIGE HAFTUNG FÜR FOLGE- ODER ANDERE SCHÄDEN, AUCH WENN ER ÜBER DIE MÖGLICHKEIT IHRES EINTRITTS UNTERRICHTET WURDE.
-
-7. Erlöschen
-
- a. Diese Lizenz und die durch sie erteilte Nutzungsbewilligung erlöschen mit Wirkung für die Zukunft im Falle eines Verstoßes gegen die Lizenzbedingungen durch Sie, ohne dass es dazu der Kenntnis des Lizenzgebers vom Verstoß oder einer weiteren Handlung einer der Vertragsparteien bedarf. Mit natürlichen oder juristischen Personen, die Bearbeitungen des Schutzgegenstandes oder diesen enthaltende Sammelwerke sowie entsprechende Vervielfältigungsstücke unter den Bedingungen dieser Lizenz von Ihnen erhalten haben, bestehen nachträglich entstandene Lizenzbeziehungen jedoch solange weiter, wie die genannten Personen sich ihrerseits an sämtliche Lizenzbedingungen halten. Darüber hinaus gelten die Ziffern 1, 2, 5, 6, 7, und 8 auch nach einem Erlöschen dieser Lizenz fort.
-
- b. Vorbehaltlich der oben genannten Bedingungen gilt diese Lizenz unbefristet bis der rechtliche Schutz für den Schutzgegenstand ausläuft. Davon abgesehen behält der Lizenzgeber das Recht, den Schutzgegenstand unter anderen Lizenzbedingungen anzubieten oder die eigene Weitergabe des Schutzgegenstandes jederzeit einzustellen, solange die Ausübung dieses Rechts nicht einer Kündigung oder einem Widerruf dieser Lizenz (oder irgendeiner Weiterlizenzierung, die auf Grundlage dieser Lizenz bereits erfolgt ist bzw. zukünftig noch erfolgen muss) dient und diese Lizenz unter Berücksichtigung der oben zum Erlöschen genannten Bedingungen vollumfänglich wirksam bleibt.
-
-8. Sonstige Bestimmungen
-
- a. Jedes Mal wenn Sie den Schutzgegenstand für sich genommen oder als Teil eines Sammelwerkes verbreiten oder öffentlich wiedergeben, bietet der Lizenzgeber dem Empfänger eine Lizenz zu den gleichen Bedingungen und im gleichen Umfang an, wie Ihnen in Form dieser Lizenz.
-
- b. Jedes Mal wenn Sie eine Bearbeitung des Schutzgegenstandes verbreiten oder öffentlich wiedergeben, bietet der Lizenzgeber dem Empfänger eine Lizenz am ursprünglichen Schutzgegenstand zu den gleichen Bedingungen und im gleichen Umfang an, wie Ihnen in Form dieser Lizenz.
-
- c. Sollte eine Bestimmung dieser Lizenz unwirksam sein, so bleibt davon die Wirksamkeit der Lizenz im Übrigen unberührt.
-
- d. Keine Bestimmung dieser Lizenz soll als abbedungen und kein Verstoß gegen sie als zulässig gelten, solange die von dem Verzicht oder von dem Verstoß betroffene Seite nicht schriftlich zugestimmt hat.
-
- e. Diese Lizenz (zusammen mit in ihr ausdrücklich vorgesehenen Erlaubnissen, Mitteilungen und Zustimmungen, soweit diese tatsächlich vorliegen) stellt die vollständige Vereinbarung zwischen dem Lizenzgeber und Ihnen in Bezug auf den Schutzgegenstand dar. Es bestehen keine Abreden, Vereinbarungen oder Erklärungen in Bezug auf den Schutzgegenstand, die in dieser Lizenz nicht genannt sind. Rechtsgeschäftliche Änderungen des Verhältnisses zwischen dem Lizenzgeber und Ihnen sind nur über Modifikationen dieser Lizenz möglich. Der Lizenzgeber ist an etwaige zusätzliche, einseitig durch Sie übermittelte Bestimmungen nicht gebunden. Diese Lizenz kann nur durch schriftliche Vereinbarung zwischen Ihnen und dem Lizenzgeber modifiziert werden. Derlei Modifikationen wirken ausschließlich zwischen dem Lizenzgeber und Ihnen und wirken sich nicht auf die Dritten gemäß 8.a) und b) angebotenen Lizenzen aus.
-
- f. Sofern zwischen Ihnen und dem Lizenzgeber keine anderweitige Vereinbarung getroffen wurde und soweit Wahlfreiheit besteht, findet auf diesen Lizenzvertrag das Recht der Republik Österreich Anwendung.
-
-Creative Commons Notice
-
-Creative Commons ist nicht Partei dieser Lizenz und übernimmt keinerlei Gewähr oder dergleichen in Bezug auf den Schutzgegenstand. Creative Commons haftet Ihnen oder einer anderen Partei unter keinem rechtlichen Gesichtspunkt für irgendwelche Schäden, die - abstrakt oder konkret, zufällig oder vorhersehbar - im Zusammenhang mit dieser Lizenz entstehen. Unbeschadet der vorangegangen beiden Sätze, hat Creative Commons alle Rechte und Pflichten eines Lizenzgebers, wenn es sich ausdrücklich als Lizenzgeber im Sinne dieser Lizenz bezeichnet.
-
-Creative Commons gewährt den Parteien nur insoweit das Recht, das Logo und die Marke "Creative Commons" zu nutzen, als dies notwendig ist, um der Öffentlichkeit gegenüber kenntlich zu machen, dass der Schutzgegenstand unter einer CCPL steht. Ein darüber hinaus gehender Gebrauch der Marke "Creative Commons" oder einer verwandten Marke oder eines verwandten Logos bedarf der vorherigen schriftlichen Zustimmung von Creative Commons. Jeder erlaubte Gebrauch richtet sich nach der Creative Commons Marken-Nutzungs-Richtlinie in der jeweils aktuellen Fassung, die von Zeit zu Zeit auf der Website veröffentlicht oder auf andere Weise auf Anfrage zugänglich gemacht wird. Zur Klarstellung: Die genannten Einschränkungen der Markennutzung sind nicht Bestandteil dieser Lizenz.
-
-Creative Commons kann kontaktiert werden über https://creativecommons.org/.
diff --git a/options/license/CC-BY-SA-3.0-DE b/options/license/CC-BY-SA-3.0-DE
deleted file mode 100644
index 472c3663af..0000000000
--- a/options/license/CC-BY-SA-3.0-DE
+++ /dev/null
@@ -1,135 +0,0 @@
-Creative Commons Namensnennung - Weitergabe unter gleichen Bedingungen 3.0 Deutschland
-
- CREATIVE COMMONS IST KEINE RECHTSANWALTSKANZLEI UND LEISTET KEINE RECHTSBERATUNG. DIE BEREITSTELLUNG DIESER LIZENZ FÜHRT ZU KEINEM MANDATSVERHÄLTNIS. CREATIVE COMMONS STELLT DIESE INFORMATIONEN OHNE GEWÄHR ZUR VERFÜGUNG. CREATIVE COMMONS ÜBERNIMMT KEINE GEWÄHRLEISTUNG FÜR DIE GELIEFERTEN INFORMATIONEN UND SCHLIEßT DIE HAFTUNG FÜR SCHÄDEN AUS, DIE SICH AUS DEREN GEBRAUCH ERGEBEN.
-
-Lizenz
-
-DER GEGENSTAND DIESER LIZENZ (WIE UNTER "SCHUTZGEGENSTAND" DEFINIERT) WIRD UNTER DEN BEDINGUNGEN DIESER CREATIVE COMMONS PUBLIC LICENSE ("CCPL", "LIZENZ" ODER "LIZENZVERTRAG") ZUR VERFÜGUNG GESTELLT. DER SCHUTZGEGENSTAND IST DURCH DAS URHEBERRECHT UND/ODER ANDERE GESETZE GESCHÜTZT. JEDE FORM DER NUTZUNG DES SCHUTZGEGENSTANDES, DIE NICHT AUFGRUND DIESER LIZENZ ODER DURCH GESETZE GESTATTET IST, IST UNZULÄSSIG.
-
-DURCH DIE AUSÜBUNG EINES DURCH DIESE LIZENZ GEWÄHRTEN RECHTS AN DEM SCHUTZGEGENSTAND ERKLÄREN SIE SICH MIT DEN LIZENZBEDINGUNGEN RECHTSVERBINDLICH EINVERSTANDEN. SOWEIT DIESE LIZENZ ALS LIZENZVERTRAG ANZUSEHEN IST, GEWÄHRT IHNEN DER LIZENZGEBER DIE IN DER LIZENZ GENANNTEN RECHTE UNENTGELTLICH UND IM AUSTAUSCH DAFÜR, DASS SIE DAS GEBUNDENSEIN AN DIE LIZENZBEDINGUNGEN AKZEPTIEREN.
-
-1. Definitionen
-
- a. Der Begriff "Abwandlung" im Sinne dieser Lizenz bezeichnet das Ergebnis jeglicher Art von Veränderung des Schutzgegenstandes, solange die eigenpersönlichen Züge des Schutzgegenstandes darin nicht verblassen und daran eigene Schutzrechte entstehen. Das kann insbesondere eine Bearbeitung, Umgestaltung, Änderung, Anpassung, Übersetzung oder Heranziehung des Schutzgegenstandes zur Vertonung von Laufbildern sein. Nicht als Abwandlung des Schutzgegenstandes gelten seine Aufnahme in eine Sammlung oder ein Sammelwerk und die freie Benutzung des Schutzgegenstandes.
-
- b. Der Begriff "Sammelwerk" im Sinne dieser Lizenz meint eine Zusammenstellung von literarischen, künstlerischen oder wissenschaftlichen Inhalten, sofern diese Zusammenstellung aufgrund von Auswahl und Anordnung der darin enthaltenen selbständigen Elemente eine geistige Schöpfung darstellt, unabhängig davon, ob die Elemente systematisch oder methodisch angelegt und dadurch einzeln zugänglich sind oder nicht.
-
- c. "Verbreiten" im Sinne dieser Lizenz bedeutet, den Schutzgegenstand oder Abwandlungen im Original oder in Form von Vervielfältigungsstücken, mithin in körperlich fixierter Form der Öffentlichkeit anzubieten oder in Verkehr zu bringen.
-
- d. Unter "Lizenzelementen" werden im Sinne dieser Lizenz die folgenden übergeordneten Lizenzcharakteristika verstanden, die vom Lizenzgeber ausgewählt wurden und in der Bezeichnung der Lizenz zum Ausdruck kommen: "Namensnennung", "Weitergabe unter gleichen Bedingungen".
-
- e. Der "*Lizenzgeber*" im Sinne dieser Lizenz ist diejenige natürliche oder juristische Person oder Gruppe, die den Schutzgegenstand unter den Bedingungen dieser Lizenz anbietet und insoweit als Rechteinhaberin auftritt.
-
- f. "Rechteinhaber" im Sinne dieser Lizenz ist der Urheber des Schutzgegenstandes oder jede andere natürliche oder juristische Person oder Gruppe von Personen, die am Schutzgegenstand ein Immaterialgüterrecht erlangt hat, welches die in Abschnitt 3 genannten Handlungen erfasst und bei dem eine Einräumung von Nutzungsrechten oder eine Weiterübertragung an Dritte möglich ist.
-
- g. Der Begriff "Schutzgegenstand" bezeichnet in dieser Lizenz den literarischen, künstlerischen oder wissenschaftlichen Inhalt, der unter den Bedingungen dieser Lizenz angeboten wird. Das kann insbesondere eine persönliche geistige Schöpfung jeglicher Art, ein Werk der kleinen Münze, ein nachgelassenes Werk oder auch ein Lichtbild oder anderes Objekt eines verwandten Schutzrechts sein, unabhängig von der Art seiner Fixierung und unabhängig davon, auf welche Weise jeweils eine Wahrnehmung erfolgen kann, gleichviel ob in analoger oder digitaler Form. Soweit Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen, unterfallen auch sie dem Begriff "Schutzgegenstand" im Sinne dieser Lizenz.
-
- h. Mit "Sie" bzw. "Ihnen" ist die natürliche oder juristische Person gemeint, die in dieser Lizenz im Abschnitt 3 genannte Nutzungen des Schutzgegenstandes vornimmt und zuvor in Hinblick auf den Schutzgegenstand nicht gegen Bedingungen dieser Lizenz verstoßen oder aber die ausdrückliche Erlaubnis des Lizenzgebers erhalten hat, die durch diese Lizenz gewährten Nutzungsrechte trotz eines vorherigen Verstoßes auszuüben.
-
- i. Unter "Öffentlich Zeigen" im Sinne dieser Lizenz sind Veröffentlichungen und Präsentationen des Schutzgegenstandes zu verstehen, die für eine Mehrzahl von Mitgliedern der Öffentlichkeit bestimmt sind und in unkörperlicher Form mittels öffentlicher Wiedergabe in Form von Vortrag, Aufführung, Vorführung, Darbietung, Sendung, Weitersendung, zeit- und ortsunabhängiger Zugänglichmachung oder in körperlicher Form mittels Ausstellung erfolgen, unabhängig von bestimmten Veranstaltungen und unabhängig von den zum Einsatz kommenden Techniken und Verfahren, einschließlich drahtgebundener oder drahtloser Mittel und Einstellen in das Internet.
-
- j. "Vervielfältigen" im Sinne dieser Lizenz bedeutet, mittels beliebiger Verfahren Vervielfältigungsstücke des Schutzgegenstandes herzustellen, insbesondere durch Ton- oder Bildaufzeichnungen, und umfasst auch den Vorgang, erstmals körperliche Fixierungen des Schutzgegenstandes sowie Vervielfältigungsstücke dieser Fixierungen anzufertigen, sowie die Übertragung des Schutzgegenstandes auf einen Bild- oder Tonträger oder auf ein anderes elektronisches Medium, gleichviel ob in digitaler oder analoger Form.
-
- k. "Mit Creative Commons kompatible Lizenz" bezeichnet eine Lizenz, die unter https://creativecommons.org/compatiblelicenses aufgelistet ist und die durch Creative Commons als grundsätzlich zur vorliegenden Lizenz äquivalent akzeptiert wurde, da zumindest folgende Voraussetzungen erfüllt sind:
-
- Diese mit Creative Commons kompatible Lizenz
-
- i. enthält Bestimmungen, welche die gleichen Ziele verfolgen, die gleiche Bedeutung haben und die gleichen Wirkungen erzeugen wie die Lizenzelemente der vorliegenden Lizenz; und
-
- ii. erlaubt ausdrücklich das Lizenzieren von ihr unterstellten Abwandlungen unter vorliegender Lizenz, unter einer anderen rechtsordnungsspezifisch angepassten Creative-Commons-Lizenz mit denselben Lizenzelementen, wie sie die vorliegende Lizenz aufweist, oder unter der entsprechenden Creative-Commons-Unported-Lizenz.
-
-2. Schranken des Immaterialgüterrechts. Diese Lizenz ist in keiner Weise darauf gerichtet, Befugnisse zur Nutzung des Schutzgegenstandes zu vermindern, zu beschränken oder zu vereiteln, die Ihnen aufgrund der Schranken des Urheberrechts oder anderer Rechtsnormen bereits ohne Weiteres zustehen oder sich aus dem Fehlen eines immaterialgüterrechtlichen Schutzes ergeben.
-
-3. Einräumung von Nutzungsrechten. Unter den Bedingungen dieser Lizenz räumt Ihnen der Lizenzgeber - unbeschadet unverzichtbarer Rechte und vorbehaltlich des Abschnitts 3.e) - das vergütungsfreie, räumlich und zeitlich (für die Dauer des Schutzrechts am Schutzgegenstand) unbeschränkte einfache Recht ein, den Schutzgegenstand auf die folgenden Arten und Weisen zu nutzen ("unentgeltlich eingeräumtes einfaches Nutzungsrecht für jedermann"):
-
- a. Den Schutzgegenstand in beliebiger Form und Menge zu vervielfältigen, ihn in Sammelwerke zu integrieren und ihn als Teil solcher Sammelwerke zu vervielfältigen;
-
- b. Abwandlungen des Schutzgegenstandes anzufertigen, einschließlich Übersetzungen unter Nutzung jedweder Medien, sofern deutlich erkennbar gemacht wird, dass es sich um Abwandlungen handelt;
-
- c. den Schutzgegenstand, allein oder in Sammelwerke aufgenommen, öffentlich zu zeigen und zu verbreiten;
-
- d. Abwandlungen des Schutzgegenstandes zu veröffentlichen, öffentlich zu zeigen und zu verbreiten.
-
- e. Bezüglich Vergütung für die Nutzung des Schutzgegenstandes gilt Folgendes:
-
- i. Unverzichtbare gesetzliche Vergütungsansprüche: Soweit unverzichtbare Vergütungsansprüche im Gegenzug für gesetzliche Lizenzen vorgesehen oder Pauschalabgabensysteme (zum Beispiel für Leermedien) vorhanden sind, behält sich der Lizenzgeber das ausschließliche Recht vor, die entsprechende Vergütung einzuziehen für jede Ausübung eines Rechts aus dieser Lizenz durch Sie.
-
- ii. Vergütung bei Zwangslizenzen: Sofern Zwangslizenzen außerhalb dieser Lizenz vorgesehen sind und zustande kommen, verzichtet der Lizenzgeber für alle Fälle einer lizenzgerechten Nutzung des Schutzgegenstandes durch Sie auf jegliche Vergütung.
-
- iii. Vergütung in sonstigen Fällen: Bezüglich lizenzgerechter Nutzung des Schutzgegenstandes durch Sie, die nicht unter die beiden vorherigen Abschnitte (i) und (ii) fällt, verzichtet der Lizenzgeber auf jegliche Vergütung, unabhängig davon, ob eine Einziehung der Vergütung durch ihn selbst oder nur durch eine Verwertungsgesellschaft möglich wäre.
-
-Das vorgenannte Nutzungsrecht wird für alle bekannten sowie für alle noch nicht bekannten Nutzungsarten eingeräumt. Es beinhaltet auch das Recht, solche Änderungen am Schutzgegenstand vorzunehmen, die für bestimmte nach dieser Lizenz zulässige Nutzungen technisch erforderlich sind. Alle sonstigen Rechte, die über diesen Abschnitt hinaus nicht ausdrücklich durch den Lizenzgeber eingeräumt werden, bleiben diesem allein vorbehalten. Soweit Datenbanken oder Zusammenstellungen von Daten Schutzgegenstand dieser Lizenz oder Teil dessen sind und einen immaterialgüterrechtlichen Schutz eigener Art genießen, verzichtet der Lizenzgeber auf sämtliche aus diesem Schutz resultierenden Rechte.
-
-4. Bedingungen. Die Einräumung des Nutzungsrechts gemäß Abschnitt 3 dieser Lizenz erfolgt ausdrücklich nur unter den folgenden Bedingungen:
-
- a. Sie dürfen den Schutzgegenstand ausschließlich unter den Bedingungen dieser Lizenz verbreiten oder öffentlich zeigen. Sie müssen dabei stets eine Kopie dieser Lizenz oder deren vollständige Internetadresse in Form des Uniform-Resource-Identifier (URI) beifügen. Sie dürfen keine Vertrags- oder Nutzungsbedingungen anbieten oder fordern, die die Bedingungen dieser Lizenz oder die durch diese Lizenz gewährten Rechte beschränken. Sie dürfen den Schutzgegenstand nicht unterlizenzieren. Bei jeder Kopie des Schutzgegenstandes, die Sie verbreiten oder öffentlich zeigen, müssen Sie alle Hinweise unverändert lassen, die auf diese Lizenz und den Haftungsausschluss hinweisen. Wenn Sie den Schutzgegenstand verbreiten oder öffentlich zeigen, dürfen Sie (in Bezug auf den Schutzgegenstand) keine technischen Maßnahmen ergreifen, die den Nutzer des Schutzgegenstandes in der Ausübung der ihm durch diese Lizenz gewährten Rechte behindern können. Dieser Abschnitt 4.a) gilt auch für den Fall, dass der Schutzgegenstand einen Bestandteil eines Sammelwerkes bildet, was jedoch nicht bedeutet, dass das Sammelwerk insgesamt dieser Lizenz unterstellt werden muss. Sofern Sie ein Sammelwerk erstellen, müssen Sie auf die Mitteilung eines Lizenzgebers hin aus dem Sammelwerk die in Abschnitt 4.c) aufgezählten Hinweise entfernen. Wenn Sie eine Abwandlung vornehmen, müssen Sie auf die Mitteilung eines Lizenzgebers hin von der Abwandlung die in Abschnitt 4.c) aufgezählten Hinweise entfernen.
-
- b. Sie dürfen eine Abwandlung ausschließlich unter den Bedingungen
-
- i. dieser Lizenz,
-
- ii. einer späteren Version dieser Lizenz mit denselben Lizenzelementen,
-
- iii. einer rechtsordnungsspezifischen Creative-Commons-Lizenz mit denselben Lizenzelementen ab Version 3.0 aufwärts (z.B. Namensnennung - Weitergabe unter gleichen Bedingungen 3.0 US),
-
- iv. der Creative-Commons-Unported-Lizenz mit denselben Lizenzelementen ab Version 3.0 aufwärts, oder
-
- v. einer mit Creative Commons kompatiblen Lizenz
-
- verbreiten oder öffentlich zeigen.
-
- Falls Sie die Abwandlung gemäß Abschnitt (v) unter einer mit Creative Commons kompatiblen Lizenz lizenzieren, müssen Sie deren Lizenzbestimmungen Folge leisten.
-
- Falls Sie die Abwandlungen unter einer der unter (i)-(iv) genannten Lizenzen ("Verwendbare Lizenzen") lizenzieren, müssen Sie deren Lizenzbestimmungen sowie folgenden Bestimmungen Folge leisten: Sie müssen stets eine Kopie der verwendbaren Lizenz oder deren vollständige Internetadresse in Form des Uniform-Resource-Identifier (URI) beifügen, wenn Sie die Abwandlung verbreiten oder öffentlich zeigen. Sie dürfen keine Vertrags- oder Nutzungsbedingungen anbieten oder fordern, die die Bedingungen der verwendbaren Lizenz oder die durch sie gewährten Rechte beschränken. Bei jeder Abwandlung, die Sie verbreiten oder öffentlich zeigen, müssen Sie alle Hinweise auf die verwendbare Lizenz und den Haftungsausschluss unverändert lassen. Wenn Sie die Abwandlung verbreiten oder öffentlich zeigen, dürfen Sie (in Bezug auf die Abwandlung) keine technischen Maßnahmen ergreifen, die den Nutzer der Abwandlung in der Ausübung der ihm durch die verwendbare Lizenz gewährten Rechte behindern können. Dieser Abschnitt 4.b) gilt auch für den Fall, dass die Abwandlung einen Bestandteil eines Sammelwerkes bildet, was jedoch nicht bedeutet, dass das Sammelwerk insgesamt der verwendbaren Lizenz unterstellt werden muss.
-
- c. Die Verbreitung und das öffentliche Zeigen des Schutzgegenstandes oder auf ihm aufbauender Abwandlungen oder ihn enthaltender Sammelwerke ist Ihnen nur unter der Bedingung gestattet, dass Sie, vorbehaltlich etwaiger Mitteilungen im Sinne von Abschnitt 4.a), alle dazu gehörenden Rechtevermerke unberührt lassen. Sie sind verpflichtet, die Rechteinhaberschaft in einer der Nutzung entsprechenden, angemessenen Form anzuerkennen, indem Sie - soweit bekannt - Folgendes angeben:
-
- i. Den Namen (oder das Pseudonym, falls ein solches verwendet wird) des Rechteinhabers und / oder, falls der Lizenzgeber im Rechtevermerk, in den Nutzungsbedingungen oder auf andere angemessene Weise eine Zuschreibung an Dritte vorgenommen hat (z.B. an eine Stiftung, ein Verlagshaus oder eine Zeitung) ("Zuschreibungsempfänger"), Namen bzw. Bezeichnung dieses oder dieser Dritten;
-
- ii. den Titel des Inhaltes;
-
- iii. in einer praktikablen Form den Uniform-Resource-Identifier (URI, z.B. Internetadresse), den der Lizenzgeber zum Schutzgegenstand angegeben hat, es sei denn, dieser URI verweist nicht auf den Rechtevermerk oder die Lizenzinformationen zum Schutzgegenstand;
-
- iv. und im Falle einer Abwandlung des Schutzgegenstandes in Übereinstimmung mit Abschnitt 3.b) einen Hinweis darauf, dass es sich um eine Abwandlung handelt.
-
- Die nach diesem Abschnitt 4.c) erforderlichen Angaben können in jeder angemessenen Form gemacht werden; im Falle einer Abwandlung des Schutzgegenstandes oder eines Sammelwerkes müssen diese Angaben das Minimum darstellen und bei gemeinsamer Nennung mehrerer Rechteinhaber dergestalt erfolgen, dass sie zumindest ebenso hervorgehoben sind wie die Hinweise auf die übrigen Rechteinhaber. Die Angaben nach diesem Abschnitt dürfen Sie ausschließlich zur Angabe der Rechteinhaberschaft in der oben bezeichneten Weise verwenden. Durch die Ausübung Ihrer Rechte aus dieser Lizenz dürfen Sie ohne eine vorherige, separat und schriftlich vorliegende Zustimmung des Lizenzgebers und / oder des Zuschreibungsempfängers weder explizit noch implizit irgendeine Verbindung zum Lizenzgeber oder Zuschreibungsempfänger und ebenso wenig eine Unterstützung oder Billigung durch ihn andeuten.
-
- d. Die oben unter 4.a) bis c) genannten Einschränkungen gelten nicht für solche Teile des Schutzgegenstandes, die allein deshalb unter den Schutzgegenstandsbegriff fallen, weil sie als Datenbanken oder Zusammenstellungen von Daten einen immaterialgüterrechtlichen Schutz eigener Art genießen.
-
- e. Persönlichkeitsrechte bleiben - soweit sie bestehen - von dieser Lizenz unberührt.
-
-5. Gewährleistung
-
-SOFERN KEINE ANDERS LAUTENDE, SCHRIFTLICHE VEREINBARUNG ZWISCHEN DEM LIZENZGEBER UND IHNEN GESCHLOSSEN WURDE UND SOWEIT MÄNGEL NICHT ARGLISTIG VERSCHWIEGEN WURDEN, BIETET DER LIZENZGEBER DEN SCHUTZGEGENSTAND UND DIE EINRÄUMUNG VON RECHTEN UNTER AUSSCHLUSS JEGLICHER GEWÄHRLEISTUNG AN UND ÜBERNIMMT WEDER AUSDRÜCKLICH NOCH KONKLUDENT GARANTIEN IRGENDEINER ART. DIES UMFASST INSBESONDERE DAS FREISEIN VON SACH- UND RECHTSMÄNGELN, UNABHÄNGIG VON DEREN ERKENNBARKEIT FÜR DEN LIZENZGEBER, DIE VERKEHRSFÄHIGKEIT DES SCHUTZGEGENSTANDES, SEINE VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK SOWIE DIE KORREKTHEIT VON BESCHREIBUNGEN. DIESE GEWÄHRLEISTUNGSBESCHRÄNKUNG GILT NICHT, SOWEIT MÄNGEL ZU SCHÄDEN DER IN ABSCHNITT 6 BEZEICHNETEN ART FÜHREN UND AUF SEITEN DES LIZENZGEBERS DAS JEWEILS GENANNTE VERSCHULDEN BZW. VERTRETENMÜSSEN EBENFALLS VORLIEGT.
-
-6. Haftungsbeschränkung
-
-DER LIZENZGEBER HAFTET IHNEN GEGENÜBER IN BEZUG AUF SCHÄDEN AUS DER VERLETZUNG DES LEBENS, DES KÖRPERS ODER DER GESUNDHEIT NUR, SOFERN IHM WENIGSTENS FAHRLÄSSIGKEIT VORZUWERFEN IST, FÜR SONSTIGE SCHÄDEN NUR BEI GROBER FAHRLÄSSIGKEIT ODER VORSATZ, UND ÜBERNIMMT DARÜBER HINAUS KEINERLEI FREIWILLIGE HAFTUNG.
-
-7. Erlöschen
-
- a. Diese Lizenz und die durch sie eingeräumten Nutzungsrechte erlöschen mit Wirkung für die Zukunft im Falle eines Verstoßes gegen die Lizenzbedingungen durch Sie, ohne dass es dazu der Kenntnis des Lizenzgebers vom Verstoß oder einer weiteren Handlung einer der Vertragsparteien bedarf. Mit natürlichen oder juristischen Personen, die Abwandlungen des Schutzgegenstandes oder diesen enthaltende Sammelwerke unter den Bedingungen dieser Lizenz von Ihnen erhalten haben, bestehen nachträglich entstandene Lizenzbeziehungen jedoch solange weiter, wie die genannten Personen sich ihrerseits an sämtliche Lizenzbedingungen halten. Darüber hinaus gelten die Ziffern 1, 2, 5, 6, 7, und 8 auch nach einem Erlöschen dieser Lizenz fort.
-
- b. Vorbehaltlich der oben genannten Bedingungen gilt diese Lizenz unbefristet bis der rechtliche Schutz für den Schutzgegenstand ausläuft. Davon abgesehen behält der Lizenzgeber das Recht, den Schutzgegenstand unter anderen Lizenzbedingungen anzubieten oder die eigene Weitergabe des Schutzgegenstandes jederzeit einzustellen, solange die Ausübung dieses Rechts nicht einer Kündigung oder einem Widerruf dieser Lizenz (oder irgendeiner Weiterlizenzierung, die auf Grundlage dieser Lizenz bereits erfolgt ist bzw. zukünftig noch erfolgen muss) dient und diese Lizenz unter Berücksichtigung der oben zum Erlöschen genannten Bedingungen vollumfänglich wirksam bleibt.
-
-8. Sonstige Bestimmungen
-
- a. Jedes Mal wenn Sie den Schutzgegenstand für sich genommen oder als Teil eines Sammelwerkes verbreiten oder öffentlich zeigen, bietet der Lizenzgeber dem Empfänger eine Lizenz zu den gleichen Bedingungen und im gleichen Umfang an, wie Ihnen in Form dieser Lizenz.
-
- b. Jedes Mal wenn Sie eine Abwandlung des Schutzgegenstandes verbreiten oder öffentlich zeigen, bietet der Lizenzgeber dem Empfänger eine Lizenz am ursprünglichen Schutzgegenstand zu den gleichen Bedingungen und im gleichen Umfang an, wie Ihnen in Form dieser Lizenz.
-
- c. Sollte eine Bestimmung dieser Lizenz unwirksam sein, so bleibt davon die Wirksamkeit der Lizenz im Übrigen unberührt.
-
- d. Keine Bestimmung dieser Lizenz soll als abbedungen und kein Verstoß gegen sie als zulässig gelten, solange die von dem Verzicht oder von dem Verstoß betroffene Seite nicht schriftlich zugestimmt hat.
-
- e. Diese Lizenz (zusammen mit in ihr ausdrücklich vorgesehenen Erlaubnissen, Mitteilungen und Zustimmungen, soweit diese tatsächlich vorliegen) stellt die vollständige Vereinbarung zwischen dem Lizenzgeber und Ihnen in Bezug auf den Schutzgegenstand dar. Es bestehen keine Abreden, Vereinbarungen oder Erklärungen in Bezug auf den Schutzgegenstand, die in dieser Lizenz nicht genannt sind. Rechtsgeschäftliche Änderungen des Verhältnisses zwischen dem Lizenzgeber und Ihnen sind nur über Modifikationen dieser Lizenz möglich. Der Lizenzgeber ist an etwaige zusätzliche, einseitig durch Sie übermittelte Bestimmungen nicht gebunden. Diese Lizenz kann nur durch schriftliche Vereinbarung zwischen Ihnen und dem Lizenzgeber modifiziert werden. Derlei Modifikationen wirken ausschließlich zwischen dem Lizenzgeber und Ihnen und wirken sich nicht auf die Dritten gemäß Ziffern 8.a) und b) angeboteten Lizenzen aus.
-
- f. Sofern zwischen Ihnen und dem Lizenzgeber keine anderweitige Vereinbarung getroffen wurde und soweit Wahlfreiheit besteht, findet auf diesen Lizenzvertrag das Recht der Bundesrepublik Deutschland Anwendung.
-
-Creative Commons Notice
-
-Creative Commons ist nicht Partei dieser Lizenz und übernimmt keinerlei Gewähr oder dergleichen in Bezug auf den Schutzgegenstand. Creative Commons haftet Ihnen oder einer anderen Partei unter keinem rechtlichen Gesichtspunkt für irgendwelche Schäden, die - abstrakt oder konkret, zufällig oder vorhersehbar - im Zusammenhang mit dieser Lizenz entstehen. Unbeschadet der vorangegangen beiden Sätze, hat Creative Commons alle Rechte und Pflichten eines Lizenzgebers, wenn es sich ausdrücklich als Lizenzgeber im Sinne dieser Lizenz bezeichnet.
-
-Creative Commons gewährt den Parteien nur insoweit das Recht, das Logo und die Marke "Creative Commons" zu nutzen, als dies notwendig ist, um der Öffentlichkeit gegenüber kenntlich zu machen, dass der Schutzgegenstand unter einer CCPL steht. Ein darüber hinaus gehender Gebrauch der Marke "Creative Commons" oder einer verwandten Marke oder eines verwandten Logos bedarf der vorherigen schriftlichen Zustimmung von Creative Commons. Jeder erlaubte Gebrauch richtet sich nach der Creative Commons Marken-Nutzungs-Richtlinie in der jeweils aktuellen Fassung, die von Zeit zu Zeit auf der Website veröffentlicht oder auf andere Weise auf Anfrage zugänglich gemacht wird. Zur Klarstellung: Die genannten Einschränkungen der Markennutzung sind nicht Bestandteil dieser Lizenz.
-
-Creative Commons kann kontaktiert werden über https://creativecommons.org/.
diff --git a/options/license/CC-BY-SA-3.0-IGO b/options/license/CC-BY-SA-3.0-IGO
deleted file mode 100644
index 2b8b0f8f23..0000000000
--- a/options/license/CC-BY-SA-3.0-IGO
+++ /dev/null
@@ -1,107 +0,0 @@
-Creative Commons Attribution-ShareAlike 3.0 IGO
-
-CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. THE LICENSOR IS NOT NECESSARILY AN INTERGOVERNMENTAL ORGANIZATION (IGO), AS DEFINED IN THE LICENSE BELOW.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("LICENSE"). THE LICENSOR (DEFINED BELOW) HOLDS COPYRIGHT AND OTHER RIGHTS IN THE WORK. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION FOR YOUR ACCEPTANCE AND AGREEMENT TO THE TERMS OF THE LICENSE.
-
-1. Definitions
-
- a. "IGO" means, solely and exclusively for purposes of this License, an organization established by a treaty or other instrument governed by international law and possessing its own international legal personality. Other organizations established to carry out activities across national borders and that accordingly enjoy immunity from legal process are also IGOs for the sole and exclusive purposes of this License. IGOs may include as members, in addition to states, other entities.
-
- b. "Work" means the literary and/or artistic work eligible for copyright protection, whatever may be the mode or form of its expression including digital form, and offered under the terms of this License. It is understood that a database, which by reason of the selection and arrangement of its contents constitutes an intellectual creation, is considered a Work.
-
- c. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License and may be, but is not necessarily, an IGO.
-
- d. "You" means an individual or entity exercising rights under this License.
-
- e. "License Elements" means the following high-level license attributes as selected by the Licensor and indicated in the title of this License: Attribution, ShareAlike.
-
- f. "Reproduce" means to make a copy of the Work in any manner or form, and by any means.
-
- g. "Distribute" means the activity of making publicly available the Work or Adaptation (or copies of the Work or Adaptation), as applicable, by sale, rental, public lending or any other known form of transfer of ownership or possession of the Work or copy of the Work.
-
- h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
-
- i. "Adaptation" means a work derived from or based upon the Work, or upon the Work and other pre-existing works. Adaptations may include works such as translations, derivative works, or any alterations and arrangements of any kind involving the Work. For purposes of this License, where the Work is a musical work, performance, or phonogram, the synchronization of the Work in timed-relation with a moving image is an Adaptation. For the avoidance of doubt, including the Work in a Collection is not an Adaptation.
-
- j. "Collection" means a collection of literary or artistic works or other works or subject matter other than works listed in Section 1(b) which by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. For the avoidance of doubt, a Collection will not be considered as an Adaptation.
-
- k. "Creative Commons Compatible License" means a license that is listed at https://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License.
-
-2. Scope of this License. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright protection.
-
-3. License Grant. Subject to the terms and conditions of this License, the Licensor hereby grants You a worldwide, royalty-free, non-exclusive license to exercise the rights in the Work as follows:
-
- a. to Reproduce, Distribute and Publicly Perform the Work, to incorporate the Work into one or more Collections, and to Reproduce, Distribute and Publicly Perform the Work as incorporated in the Collections; and,
-
- b. to create, Reproduce, Distribute and Publicly Perform Adaptations, provided that You clearly label, demarcate or otherwise identify that changes were made to the original Work.
-
- c. For the avoidance of doubt:
-
- i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-
- ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
-
- ii. Voluntary License Schemes. To the extent possible, the Licensor waives the right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary licensing scheme.
-
-This License lasts for the duration of the term of the copyright in the Work licensed by the Licensor. The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by the Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work (see section 8(a)). You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from a Licensor You must, to the extent practicable, remove from the Collection any credit (inclusive of any logo, trademark, official mark or official emblem) as required by Section 4(c), as requested. If You create an Adaptation, upon notice from a Licensor You must, to the extent practicable, remove from the Adaptation any credit (inclusive of any logo, trademark, official mark or official emblem) as required by Section 4(c), as requested.
-
- b. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) either the unported Creative Commons license or a ported Creative Commons license (either this or a later license version) containing the same License Elements; or (iv) a Creative Commons Compatible License. If You license the Adaptation under one of the licenses mentioned in (iv), You must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), You must comply with terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform. (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License. (III) You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform. (IV) When You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License.
-
- c. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) any attributions that the Licensor indicates be associated with the Work as indicated in a copyright notice, (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that the Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, (iv) consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation. The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of an Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributors to the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Licensor or others designated for attribution, of You or Your use of the Work, without the separate, express prior written permission of the Licensor or such others.
-
- d. Except as otherwise agreed in writing by the Licensor, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the honor or reputation of the Licensor where moral rights apply.
-
-5. Representations, Warranties and Disclaimer
-
-THE LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE.
-
-6. Limitation on Liability
-
-IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. Subject to the terms and conditions set forth in this License, the license granted here lasts for the duration of the term of the copyright in the Work licensed by the Licensor as stated in Section 3. Notwithstanding the above, the Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated below.
-
- b. If You fail to comply with this License, then this License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. Notwithstanding the foregoing, this License reinstates automatically as of the date the violation is cured, provided it is cured within 30 days of You discovering the violation, or upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 7(b) does not affect any rights the Licensor may have to seek remedies for violations of this License by You.
-
-8. Miscellaneous
-
- a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You Distribute or Publicly Perform an Adaptation, the Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the Licensor.
-
- e. This License constitutes the entire agreement between You and the Licensor with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. The Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
- f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). Interpretation of the scope of the rights granted by the Licensor and the conditions imposed on You under this License, this License, and the rights and conditions set forth herein shall be made with reference to copyright as determined in accordance with general principles of international law, including the above mentioned conventions.
-
- g. Nothing in this License constitutes or may be interpreted as a limitation upon or waiver of any privileges and immunities that may apply to the Licensor or You, including immunity from the legal processes of any jurisdiction, national court or other authority.
-
- h. Where the Licensor is an IGO, any and all disputes arising under this License that cannot be settled amicably shall be resolved in accordance with the following procedure:
-
- i. Pursuant to a notice of mediation communicated by reasonable means by either You or the Licensor to the other, the dispute shall be submitted to non-binding mediation conducted in accordance with rules designated by the Licensor in the copyright notice published with the Work, or if none then in accordance with those communicated in the notice of mediation. The language used in the mediation proceedings shall be English unless otherwise agreed.
-
- ii. If any such dispute has not been settled within 45 days following the date on which the notice of mediation is provided, either You or the Licensor may, pursuant to a notice of arbitration communicated by reasonable means to the other, elect to have the dispute referred to and finally determined by arbitration. The arbitration shall be conducted in accordance with the rules designated by the Licensor in the copyright notice published with the Work, or if none then in accordance with the UNCITRAL Arbitration Rules as then in force. The arbitral tribunal shall consist of a sole arbitrator and the language of the proceedings shall be English unless otherwise agreed. The place of arbitration shall be where the Licensor has its headquarters. The arbitral proceedings shall be conducted remotely (e.g., via telephone conference or written submissions) whenever practicable.
-
- iii. Interpretation of this License in any dispute submitted to mediation or arbitration shall be as set forth in Section 8(f), above.
-
-Creative Commons Notice
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of the Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
-
-Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/CC-PDDC b/options/license/CC-PDDC
deleted file mode 100644
index b64dfd6b70..0000000000
--- a/options/license/CC-PDDC
+++ /dev/null
@@ -1,8 +0,0 @@
-
-The person or persons who have associated work with this document (the "Dedicator" or "Certifier") hereby either (a) certifies that, to the best of his knowledge, the work of authorship identified is in the public domain of the country from which the work is published, or (b) hereby dedicates whatever copyright the dedicators holds in the work of authorship identified below (the "Work") to the public domain. A certifier, moreover, dedicates any copyright interest he may have in the associated work, and for these purposes, is described as a "dedicator" below.
-
-A certifier has taken reasonable steps to verify the copyright status of this work. Certifier recognizes that his good faith efforts may not shield him from liability if in fact the work certified is not in the public domain.
-
-Dedicator makes this dedication for the benefit of the public at large and to the detriment of the Dedicator's heirs and successors. Dedicator intends this dedication to be an overt act of relinquishment in perpetuity of all present and future rights under copyright law, whether vested or contingent, in the Work. Dedicator understands that such relinquishment of all rights includes the relinquishment of all rights to enforce (by lawsuit or otherwise) those copyrights in the Work.
-
-Dedicator recognizes that, once placed in the public domain, the Work may be freely reproduced, distributed, transmitted, used, modified, built upon, or otherwise exploited by anyone for any purpose, commercial or non-commercial, and in any way, including by methods that have not yet been invented or conceived.
diff --git a/options/license/CC-PDM-1.0 b/options/license/CC-PDM-1.0
deleted file mode 100644
index 1dc4e63b87..0000000000
--- a/options/license/CC-PDM-1.0
+++ /dev/null
@@ -1,27 +0,0 @@
-No Copyright
-
-This work has been identified as being free of known restrictions under
-copyright law, including all related and neighboring rights.
-
-
-You can copy, modify, distribute and perform the work, even for commercial
-purposes, all without asking permission. See Other Information below.
-
-Other Information
-
-The work may not be free of known copyright restrictions in all jurisdictions .
-
-Persons may have other rights in or related to the work, such as patent or
-trademark rights, and others may have rights in how the work is used, such as
-publicity or privacy rights.
-
-In some jurisdictions moral rights of the author may persist beyond the term of
-copyright. These rights may include the right to be identified as the author
-and the right to object to derogatory treatments.
-
-Unless expressly stated otherwise, the person who identified the work makes no
-warranties about the work, and disclaims liability for all uses of the work, to
-the fullest extent permitted by applicable law.
-
-When using or citing the work, you should not imply endorsement by the author
-or the person who identified the work.
diff --git a/options/license/CC-SA-1.0 b/options/license/CC-SA-1.0
deleted file mode 100644
index 1a810feaec..0000000000
--- a/options/license/CC-SA-1.0
+++ /dev/null
@@ -1,198 +0,0 @@
- Creative Commons Legal Code
-
- ShareAlike 1.0
-
-CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL
-SERVICES. DISTRIBUTION OF THIS DRAFT LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT
-RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS.
-CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND
-DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
-COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
-COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
-AUTHORIZED UNDER THIS LICENSE IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE
-BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS
-CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
-CONDITIONS.
-
-1. Definitions
-
- a. "Collective Work" means a work, such as a periodical issue, anthology or
- encyclopedia, in which the Work in its entirety in unmodified form, along
- with a number of other contributions, constituting separate and independent
- works in themselves, are assembled into a collective whole. A work that
- constitutes a Collective Work will not be considered a Derivative Work (as
- defined below) for the purposes of this License.
- b. "Derivative Work" means a work based upon the Work or upon the Work and
- other pre-existing works, such as a translation, musical arrangement,
- dramatization, fictionalization, motion picture version, sound recording,
- art reproduction, abridgment, condensation, or any other form in which the
- Work may be recast, transformed, or adapted, except that a work that
- constitutes a Collective Work will not be considered a Derivative Work for
- the purpose of this License.
- c. "Licensor" means the individual or entity that offers the Work under the
- terms of this License.
- d. "Original Author" means the individual or entity who created the Work.
- e. "Work" means the copyrightable work of authorship offered under the terms
- of this License.
- f. "You" means an individual or entity exercising rights under this License
- who has not previously violated the terms of this License with respect to
- the Work, or who has received express permission from the Licensor to
- exercise rights under this License despite a previous violation.
-
-2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or
-restrict any rights arising from fair use, first sale or other limitations on
-the exclusive rights of the copyright owner under copyright law or other
-applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor
-hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the
-duration of the applicable copyright) license to exercise the rights in the
-Work as stated below:
-
- a. to reproduce the Work, to incorporate the Work into one or more Collective
- Works, and to reproduce the Work as incorporated in the Collective Works;
- b. to create and reproduce Derivative Works;
- c. to distribute copies or phonorecords of, display publicly, perform
- publicly, and perform publicly by means of a digital audio transmission the
- Work including as incorporated in Collective Works;
- d. to distribute copies or phonorecords of, display publicly, perform
- publicly, and perform publicly by means of a digital audio transmission
- Derivative Works;
-
-The above rights may be exercised in all media and formats whether now known or
-hereafter devised. The above rights include the right to make such
-modifications as are technically necessary to exercise the rights in other
-media and formats. All rights not expressly granted by Licensor are hereby
-reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made
-subject to and limited by the following restrictions:
-
- a. You may distribute, publicly display, publicly perform, or publicly
- digitally perform the Work only under the terms of this License, and You
- must include a copy of, or the Uniform Resource Identifier for, this
- License with every copy or phonorecord of the Work You distribute, publicly
- display, publicly perform, or publicly digitally perform. You may not offer
- or impose any terms on the Work that alter or restrict the terms of this
- License or the recipients' exercise of the rights granted hereunder. You
- may not sublicense the Work. You must keep intact all notices that refer to
- this License and to the disclaimer of warranties. You may not distribute,
- publicly display, publicly perform, or publicly digitally perform the Work
- with any technological measures that control access or use of the Work in a
- manner inconsistent with the terms of this License Agreement. The above
- applies to the Work as incorporated in a Collective Work, but this does not
- require the Collective Work apart from the Work itself to be made subject
- to the terms of this License. If You create a Collective Work, upon notice
- from any Licensor You must, to the extent practicable, remove from the
- Collective Work any reference to such Licensor or the Original Author, as
- requested. If You create a Derivative Work, upon notice from any Licensor
- You must, to the extent practicable, remove from the Derivative Work any
- reference to such Licensor or the Original Author, as requested.
- b. You may distribute, publicly display, publicly perform, or publicly
- digitally perform a Derivative Work only under the terms of this License,
- and You must include a copy of, or the Uniform Resource Identifier for,
- this License with every copy or phonorecord of each Derivative Work You
- distribute, publicly display, publicly perform, or publicly digitally
- perform. You may not offer or impose any terms on the Derivative Works that
- alter or restrict the terms of this License or the recipients' exercise of
- the rights granted hereunder, and You must keep intact all notices that
- refer to this License and to the disclaimer of warranties. You may not
- distribute, publicly display, publicly perform, or publicly digitally
- perform the Derivative Work with any technological measures that control
- access or use of the Work in a manner inconsistent with the terms of this
- License Agreement. The above applies to the Derivative Work as incorporated
- in a Collective Work, but this does not require the Collective Work apart
- from the Derivative Work itself to be made subject to the terms of this
- License.
-
-5. Representations, Warranties and Disclaimer
-
- a. By offering the Work for public release under this License, Licensor
- represents and warrants that, to the best of Licensor's knowledge after
- reasonable inquiry:
- i. Licensor has secured all rights in the Work necessary to grant the
- license rights hereunder and to permit the lawful exercise of the
- rights granted hereunder without You having any obligation to pay any
- royalties, compulsory license fees, residuals or any other payments;
- ii. The Work does not infringe the copyright, trademark, publicity rights,
- common law rights or any other right of any third party or constitute
- defamation, invasion of privacy or other tortious injury to any third
- party.
- b. EXCEPT AS EXPRESSLY STATED IN THIS LICENSE OR OTHERWISE AGREED IN WRITING
- OR REQUIRED BY APPLICABLE LAW, THE WORK IS LICENSED ON AN "AS IS" BASIS,
- WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING,
- WITHOUT LIMITATION, ANY WARRANTIES REGARDING THE CONTENTS OR ACCURACY OF
- THE WORK.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW,
-AND EXCEPT FOR DAMAGES ARISING FROM LIABILITY TO A THIRD PARTY RESULTING FROM
-BREACH OF THE WARRANTIES IN SECTION 5, IN NO EVENT WILL LICENSOR BE LIABLE TO
-YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR
-EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF
-LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
- a. This License and the rights granted hereunder will terminate automatically
- upon any breach by You of the terms of this License. Individuals or
- entities who have received Derivative Works or Collective Works from You
- under this License, however, will not have their licenses terminated
- provided such individuals or entities remain in full compliance with those
- licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of
- this License.
- b. Subject to the above terms and conditions, the license granted here is
- perpetual (for the duration of the applicable copyright in the Work).
- Notwithstanding the above, Licensor reserves the right to release the Work
- under different license terms or to stop distributing the Work at any time;
- provided, however that any such election will not serve to withdraw this
- License (or any other license that has been, or is required to be, granted
- under the terms of this License), and this License will continue in full
- force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
- a. Each time You distribute or publicly digitally perform the Work or a
- Collective Work, the Licensor offers to the recipient a license to the Work
- on the same terms and conditions as the license granted to You under this
- License.
- b. Each time You distribute or publicly digitally perform a Derivative Work,
- Licensor offers to the recipient a license to the original Work on the same
- terms and conditions as the license granted to You under this License.
- c. If any provision of this License is invalid or unenforceable under
- applicable law, it shall not affect the validity or enforceability of the
- remainder of the terms of this License, and without further action by the
- parties to this agreement, such provision shall be reformed to the minimum
- extent necessary to make such provision valid and enforceable.
- d. No term or provision of this License shall be deemed waived and no breach
- consented to unless such waiver or consent shall be in writing and signed
- by the party to be charged with such waiver or consent.
- e. This License constitutes the entire agreement between the parties with
- respect to the Work licensed here. There are no understandings, agreements
- or representations with respect to the Work not specified here. Licensor
- shall not be bound by any additional provisions that may appear in any
- communication from You. This License may not be modified without the mutual
- written agreement of the Licensor and You.
-
-Creative Commons is not a party to this License, and makes no warranty
-whatsoever in connection with the Work. Creative Commons will not be liable to
-You or any party on any legal theory for any damages whatsoever, including
-without limitation any general, special, incidental or consequential damages
-arising in connection to this license. Notwithstanding the foregoing two (2)
-sentences, if Creative Commons has expressly identified itself as the Licensor
-hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is
-licensed under the CCPL, neither party will use the trademark "Creative
-Commons" or any related trademark or logo of Creative Commons without the prior
-written consent of Creative Commons. Any permitted use will be in compliance
-with Creative Commons' then-current trademark usage guidelines, as may be
-published on its website or otherwise made available upon request from time to
-time.
-
-Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/options/license/CDDL-1.0 b/options/license/CDDL-1.0
deleted file mode 100644
index 2a5d7f18fc..0000000000
--- a/options/license/CDDL-1.0
+++ /dev/null
@@ -1,119 +0,0 @@
-COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)
-Version 1.0
-
-1. Definitions.
-
-1.1. “Contributor†means each individual or entity that creates or contributes to the creation of Modifications.
-
-1.2. “Contributor Version†means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor.
-
-1.3. “Covered Software†means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof.
-
-1.4. “Executable†means the Covered Software in any form other than Source Code.
-
-1.5. “Initial Developer†means the individual or entity that first makes Original Software available under this License.
-
-1.6. “Larger Work†means a work which combines Covered Software or portions thereof with code not governed by the terms of this License.
-
-1.7. “License†means this document.
-
-1.8. “Licensable†means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
-1.9. “Modifications†means the Source Code and Executable form of any of the following:
-
- A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications;
-
- B. Any new file that contains any part of the Original Software or previous Modification; or
-
- C. Any new file that is contributed or otherwise made available under the terms of this License.
-
-1.10. “Original Software†means the Source Code and Executable form of computer software code that is originally released under this License.
-
-1.11. “Patent Claims†means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
-1.12. “Source Code†means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code.
-
-1.13. “You†(or “Yourâ€) means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, “You†includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, “control†means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2. License Grants.
-
-2.1. The Initial Developer Grant.
-Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license:
-
- (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and
-
- (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof).
-
- (c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License.
-
- (d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices.
-
-2.2. Contributor Grant.
-Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:
-
- (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and
-
- (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
-
- (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party.
-
- (d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor.
-
-3. Distribution Obligations.
-
-3.1. Availability of Source Code.
-Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange.
-
-3.2. Modifications.
-The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License.
-
-3.3. Required Notices.
-You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer.
-
-3.4. Application of Additional Terms.
-You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients’ rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
-3.5. Distribution of Executable Versions.
-You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient’s rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
-3.6. Larger Works.
-You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software.
-
-4. Versions of the License.
-
-4.1. New Versions.
-Sun Microsystems, Inc. is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License.
-
-4.2. Effect of New Versions.
-You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward.
-
-4.3. Modified Versions.
-When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License.
-
-5. DISCLAIMER OF WARRANTY.
-
-COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN “AS IS†BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-6. TERMINATION.
-
-6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as “Participantâ€) alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant.
-
-6.3. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination.
-
-7. LIMITATION OF LIABILITY.
-
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY’S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-8. U.S. GOVERNMENT END USERS.
-
-The Covered Software is a “commercial item,†as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of “commercial computer software†(as that term is defined at 48 C.F.R. § 252.227-7014(a)(1)) and “commercial computer software documentation†as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License.
-
-9. MISCELLANEOUS.
-
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction’s conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys’ fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software.
-
-10. RESPONSIBILITY FOR CLAIMS.
-
-As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
diff --git a/options/license/CDDL-1.1 b/options/license/CDDL-1.1
deleted file mode 100644
index f5479ec406..0000000000
--- a/options/license/CDDL-1.1
+++ /dev/null
@@ -1,123 +0,0 @@
-COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)
-Version 1.1
-
-1. Definitions.
-
-1.1. “Contributor†means each individual or entity that creates or contributes to the creation of Modifications.
-
-1.2. “Contributor Version†means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor.
-
-1.3. “Covered Software†means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof.
-
-1.4. “Executable†means the Covered Software in any form other than Source Code.
-
-1.5. “Initial Developer†means the individual or entity that first makes Original Software available under this License.
-
-1.6. “Larger Work†means a work which combines Covered Software or portions thereof with code not governed by the terms of this License.
-
-1.7. “License†means this document.
-
-1.8. “Licensable†means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
-1.9. “Modifications†means the Source Code and Executable form of any of the following:
-
- A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications;
-
- B. Any new file that contains any part of the Original Software or previous Modification; or
-
- C. Any new file that is contributed or otherwise made available under the terms of this License.
-
-1.10. “Original Software†means the Source Code and Executable form of computer software code that is originally released under this License.
-
-1.11. “Patent Claims†means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
-1.12. “Source Code†means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code.
-
-1.13. “You†(or “Yourâ€) means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, “You†includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, “control†means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2. License Grants.
-
-2.1. The Initial Developer Grant.
-Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license:
-
- (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and
-
- (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof).
-
- (c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License.
-
- (d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices.
-
-2.2. Contributor Grant.
-Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:
-
- (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and
-
- (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
-
- (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party.
-
- (d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor.
-
-3. Distribution Obligations.
-
-3.1. Availability of Source Code.
-Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange.
-
-3.2. Modifications.
-The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License.
-
-3.3. Required Notices.
-You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer.
-
-3.4. Application of Additional Terms.
-You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients' rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
-3.5. Distribution of Executable Versions.
-You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient's rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
-3.6. Larger Works.
-You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software.
-
-4. Versions of the License.
-
-4.1. New Versions.
-Oracle is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License.
-
-4.2. Effect of New Versions.
-You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward.
-
-4.3. Modified Versions.
-When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License.
-
-5. DISCLAIMER OF WARRANTY.
-COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN “AS IS†BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-6. TERMINATION.
-
-6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as “Participantâ€) alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant.
-
-6.3. If You assert a patent infringement claim against Participant alleging that the Participant Software directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
-6.4. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination.
-
-7. LIMITATION OF LIABILITY.
-
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-8. U.S. GOVERNMENT END USERS.
-
-The Covered Software is a “commercial item,†as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of “commercial computer software†(as that term is defined at 48 C.F.R. § 252.227-7014(a)(1)) and “commercial computer software documentation†as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License.
-
-9. MISCELLANEOUS.
-
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction's conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software.
-
-10. RESPONSIBILITY FOR CLAIMS.
-
-As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)
-The code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California.
diff --git a/options/license/CDL-1.0 b/options/license/CDL-1.0
deleted file mode 100644
index e2990cde2d..0000000000
--- a/options/license/CDL-1.0
+++ /dev/null
@@ -1,53 +0,0 @@
-Common Documentation License
-
-Version 1.0 - February 16, 2001
-
-Copyright © 2001 Apple Computer, Inc.
-
-Permission is granted to copy and distribute verbatim copies of this License, but changing or adding to it in any way is not permitted.
-
-Please read this License carefully before downloading or using this material. By downloading or using this material, you are agreeing to be bound by the terms of this License. If you do not or cannot agree to the terms of this License, please do not download or use this material.
-
-0. Preamble. The Common Documentation License (CDL) provides a very simple and consistent license that allows relatively unrestricted use and redistribution of documents while still maintaining the author's credit and intent. To preserve simplicity, the License does not specify in detail how (e.g. font size) or where (e.g. title page, etc.) the author should be credited. To preserve consistency, changes to the CDL are not allowed and all derivatives of CDL documents are required to remain under the CDL. Together, these constraints enable third parties to easily and safely reuse CDL documents, making the CDL ideal for authors who desire a wide distribution of their work. However, this means the CDL does not allow authors to restrict precisely how their work is used or represented, making it inappropriate for those desiring more finely-grained control.
-
-1. General; Definitions. This License applies to any documentation, manual or other work that contains a notice placed by the Copyright Holder stating that it is subject to the terms of this Common Documentation License version 1.0 (or subsequent version thereof) ("License"). As used in this License:
-
-1.1 "Copyright Holder" means the original author(s) of the Document or other owner(s) of the copyright in the Document.
-
-1.2 "Document(s)" means any documentation, manual or other work that has been identified as being subject to the terms of this License.
-
-1.3 "Derivative Work" means a work which is based upon a pre-existing Document, such as a revision, modification, translation, abridgment, condensation, expansion, or any other form in which such pre-existing Document may be recast, transformed, or adapted.
-
-1.4 "You" or "Your" means an individual or a legal entity exercising rights under this License.
-
-2. Basic License. Subject to all the terms and conditions of this License, You may use, copy, modify, publicly display, distribute and publish the Document and your Derivative Works thereof, in any medium physical or electronic, commercially or non-commercially; provided that: (a) all copyright notices in the Document are preserved; (b) a copy of this License, or an incorporation of it by reference in proper form as indicated in Exhibit A below, is included in a conspicuous location in all copies such that it would be reasonably viewed by the recipient of the Document; and (c) You add no other terms or conditions to those of this License.
-
-3. Derivative Works. All Derivative Works are subject to the terms of this License. You may copy and distribute a Derivative Work of the Document under the conditions of Section 2 above, provided that You release the Derivative Work under the exact, verbatim terms of this License (i.e., the Derivative Work is licensed as a "Document" under the terms of this License). In addition, Derivative Works of Documents must meet the following requirements:
-
- (a) All copyright and license notices in the original Document must be preserved.
-
- (b) An appropriate copyright notice for your Derivative Work must be added adjacent to the other copyright notices.
-
- (c) A statement briefly summarizing how your Derivative Work is different from the original Document must be included in the same place as your copyright notice.
-
- (d) If it is not reasonably evident to a recipient of your Derivative Work that the Derivative Work is subject to the terms of this License, a statement indicating such fact must be included in the same place as your copyright notice.
-
-4. Compilation with Independent Works. You may compile or combine a Document or its Derivative Works with other separate and independent documents or works to create a compilation work ("Compilation"). If included in a Compilation, the Document or Derivative Work thereof must still be provided under the terms of this License, and the Compilation shall contain (a) a notice specifying the inclusion of the Document and/or Derivative Work and the fact that it is subject to the terms of this License, and (b) either a copy of the License or an incorporation by reference in proper form (as indicated in Exhibit A). Mere aggregation of a Document or Derivative Work with other documents or works on the same storage or distribution medium (e.g. a CD-ROM) will not cause this License to apply to those other works.
-
-5. NO WARRANTY. THE DOCUMENT IS PROVIDED 'AS IS' BASIS, WITHOUT WARRANTY OF ANY KIND, AND THE COPYRIGHT HOLDER EXPRESSLY DISCLAIMS ALL WARRANTIES AND/OR CONDITIONS WITH RESPECT TO THE DOCUMENT, EITHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND OF NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-
-6. LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE, REPRODUCTION, MODIFICATION, DISTRIBUTION AND/OR PUBLICATION OF THE DOCUMENT, OR ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY REMEDY.
-
-7. Trademarks. This License does not grant any rights to use any names, trademarks, service marks or logos of the Copyright Holder (collectively "Marks") and no such Marks may be used to endorse or promote works or products derived from the Document without the prior written permission of the Copyright Holder.
-
-8. Versions of the License. Apple Computer, Inc. ("Apple") may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Once a Document has been published under a particular version of this License, You may continue to use it under the terms of that version. You may also choose to use such Document under the terms of any subsequent version of this License published by Apple. No one other than Apple has the right to modify the terms applicable to Documents created under this License.
-
-9. Termination. This License and the rights granted hereunder will terminate automatically if You fail to comply with any of its terms. Upon termination, You must immediately stop any further reproduction, modification, public display, distr ibution and publication of the Document and Derivative Works. However, all sublicenses to the Document and Derivative Works which have been properly granted prior to termination shall survive any termination of this License. Provisions which, by their nat ure, must remain in effect beyond the termination of this License shall survive, including but not limited to Sections 5, 6, 7, 9 and 10.
-
-10. Waiver; Severability; Governing Law. Failure by the Copyright Holder to enforce any provision of this License will not be deemed a waiver of future enforcement of that or any other provision. If for any reason a court of competent jurisdiction finds any provision of this License, or portion thereof, to be unenforceable, that provision of the License will be enforced to the maximum extent permissible so as to effect the economic benefits and intent of the parties, and the remainder of this License will continue in full force and effect. This License shall be governed by the laws of the United States and the State of California, except that body of California law concerning conflicts of law.
-
-EXHIBIT A
-
-The proper form for an incorporation of this License by reference is as follows:
-
-"Copyright (c) [year] by [Copyright Holder's name]. This material has been released under and is subject to the terms of the Common Documentation License, v.1.0, the terms of which are hereby incorporated by reference. Please obtain a copy of the License at http://www.opensource.apple.com/cdl/ and read it before using this material. Your use of this material signifies your agreement to the terms of the License."
diff --git a/options/license/CDLA-Permissive-1.0 b/options/license/CDLA-Permissive-1.0
deleted file mode 100644
index 28249cf1f3..0000000000
--- a/options/license/CDLA-Permissive-1.0
+++ /dev/null
@@ -1,85 +0,0 @@
-Community Data License Agreement – Permissive – Version 1.0
-
-This is the Community Data License Agreement – Permissive, Version 1.0 (“Agreementâ€). Data is provided to You under this Agreement by each of the Data Providers. Your exercise of any of the rights and permissions granted below constitutes Your acceptance and agreement to be bound by the terms and conditions of this Agreement.
-
-The benefits that each Data Provider receives from making Data available and that You receive from Data or otherwise under these terms and conditions shall be deemed sufficient consideration for the formation of this Agreement. Accordingly, Data Provider(s) and You (the “Partiesâ€) agree as follows:
-
-Section 1. Definitions
-
-1.1 “Add†means to supplement Data with Your own or someone else’s Data, resulting in Your “Additions.†Additions do not include Results.
-
-1.2 “Computational Use†means Your analysis (through the use of computational devices or otherwise) or other interpretation of Data. By way of example and not limitation, “Computational Use†includes the application of any computational analytical technique, the purpose of which is the analysis of any Data in digital form to generate information about Data such as patterns, trends, correlations, inferences, insights and attributes.
-
-1.3 “Data†means the information (including copyrightable information, such as images or text), collectively or individually, whether created or gathered by a Data Provider or an Entity acting on its behalf, to which rights are granted under this Agreement.
-
-1.4 “Data Provider†means any Entity (including any employee or contractor of such Entity authorized to Publish Data on behalf of such Entity) that Publishes Data under this Agreement prior to Your Receiving it.
-
-1.5 “Enhanced Data†means the subset of Data that You Publish and that is composed of (a) Your Additions and/or (b) Modifications to Data You have received under this Agreement.
-
-1.6 “Entity†means any natural person or organization that exists under the laws of the jurisdiction in which it is organized, together with all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control†means (a) the power, directly or indirectly, to cause the direction or management of such entity, whether by contract or otherwise, (b) the ownership of more than fifty percent (50%) of the outstanding shares or securities, (c) the beneficial ownership of such entity or, (d) the ability to appoint, whether by agreement or right, the majority of directors of an Entity.
-
-1.7 “Modify†means to delete, erase, correct or re-arrange Data, resulting in “Modifications.†Modifications do not include Results.
-
-1.8 “Publish†means to make all or a subset of Data (including Your Enhanced Data) available in any manner which enables its Use, including by providing a copy on physical media or remote access. For any form of Entity, that is to make the Data available to any individual who is not employed by that Entity or engaged as a contractor or agent to perform work on that Entity’s behalf. A “Publication†occurs each time You Publish Data.
-
-1.9 “Receive†or “Receives†means to have been given access to Data, locally or remotely.
-
-1.10 “Results†means the outcomes or outputs that You obtain from Your Computational Use of Data. Results shall not include more than a de minimis portion of the Data on which the Computational Use is based.
-
-1.11 “Sui Generis Database Rights†means rights, other than copyright, resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other equivalent rights anywhere in the world.
-
-1.12 “Use†means using Data (including accessing, copying, studying, reviewing, adapting, analyzing, evaluating, or making Computational Use of it), either by machines or humans, or a combination of both.
-
-1.13 “You†or “Your†means any Entity that Receives Data under this Agreement.
-
-Section 2. Right and License to Use and to Publish
-
-2.1 Subject to the conditions set forth in Section 3 of this Agreement, Data Provider(s) hereby grant(s) to You a worldwide, non-exclusive, irrevocable (except as provided in Section 5) right to: (a) Use Data; and (b) Publish Data.
-
-2.2 To the extent that the Data or the coordination, selection or arrangement of Data is protected or protectable under copyright, Sui Generis Database Rights, or other law, Data Provider(s) further agree(s) that such Data or coordination, selection or arrangement is hereby licensed to You and to anyone else who Receives Data under this Agreement for Use and Publication, subject to the conditions set forth in Section 3 of this Agreement.
-
-2.3 Except for these rights and licenses expressly granted, no other intellectual property rights are granted or should be implied.
-
-Section 3. Conditions on Rights Granted
-
-3.1 If You Publish Data You Receive or Enhanced Data:
-
-(a) You may do so under a license of Your choice provided that You give anyone who Receives the Data from You the text of this Agreement, the name of this Agreement and/or a hyperlink or other method reasonably likely to provide a copy of the text of this Agreement; and
-
-(b) You must cause any Data files containing Enhanced Data to carry prominent notices that You have changed those files; and
-
-(c) If You Publish Data You Receive, You must preserve all credit or attribution to the Data Provider(s). Such retained credit or attribution includes any of the following to the extent they exist in Data as You have Received it: legal notices or metadata; identification of the Data Provider(s); or hyperlinks to Data to the extent it is practical to do so.
-
-3.2 You may provide additional or different license terms and conditions for use, reproduction, or distribution of that Enhanced Data, or for any combination of Data and Enhanced Data as a whole, provided that Your Use and Publication of that combined Data otherwise complies with the conditions stated in this License.
-
-3.3 You and each Data Provider agree that Enhanced Data shall not be considered a work of joint authorship by virtue of its relationship to Data licensed under this Agreement and shall not require either any obligation of accounting to or the consent of any Data Provider.
-
-3.4 This Agreement imposes no obligations or restrictions on Your Use or Publication of Results.
-
-Section 4. Data Provider(s)’ Representations
-
-4.1 Each Data Provider represents that the Data Provider has exercised reasonable care, to assure that: (a) the Data it Publishes was created or generated by it or was obtained from others with the right to Publish the Data under this Agreement; and (b) Publication of such Data does not violate any privacy or confidentiality obligation undertaken by the Data Provider.
-
-Section 5. Termination
-
-5.1 All of Your rights under this Agreement will terminate, and Your right to Receive, Use or Publish the Data will be revoked or modified if You materially fail to comply with the terms and conditions of this Agreement and You do not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If Your rights under this Agreement terminate, You agree to cease Receipt, Use and Publication of Data. However, Your obligations and any rights and permissions granted by You under this Agreement relating to Data that You Published prior to such termination will continue and survive.
-
-5.2 If You institute litigation against a Data Provider or anyone else who Receives the Data (including a cross-claim in a lawsuit) based on the Data, other than a claim asserting breach of this Agreement, then any rights previously granted to You to Receive, Use and Publish Data under this Agreement will terminate as of the date such litigation is filed.
-
-Section 6. Disclaimer of Warranties and Limitation of Liability
-
-6.1 EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE DATA (INCLUDING ENHANCED DATA) IS PROVIDED ON AN “AS IS†BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
-
-6.2 NEITHER YOU NOR ANY DATA PROVIDERS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE DATA OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-Section 7. Miscellaneous
-
-7.1 You agree that it is solely Your responsibility to comply with all applicable laws with regard to Your Use or Publication of Data, including any applicable privacy, data protection, security and export laws. You agree to take reasonable steps to assist a Data Provider fulfilling responsibilities to comply with applicable laws with regard to Use or Publication of Data Received hereunder.
-
-7.2 You and Data Provider(s), collectively and individually, waive and/or agree not to assert, to the extent permitted by law, any moral rights You or they hold in Data.
-
-7.3 This Agreement confers no rights or remedies upon any person or entity other than the Parties and their respective heirs, executors, successors and assigns.
-
-7.4 The Data Provider(s) reserve no right or expectation of privacy, data protection or confidentiality in any Data that they Publish under this Agreement. If You choose to Publish Data under this Agreement, You similarly do so with no reservation or expectation of any rights of privacy or confidentiality in that Data.
-
-7.5 The Community Data License Agreement workgroup under The Linux Foundation is the steward of this Agreement (“Stewardâ€). No one other than the Steward has the right to modify or publish new versions of this Agreement. Each version will be given a distinguishing version number. You may Use and Publish Data Received hereunder under the terms of the version of the Agreement under which You originally Received the Data, or under the terms of any subsequent version published by the Steward.
diff --git a/options/license/CDLA-Permissive-2.0 b/options/license/CDLA-Permissive-2.0
deleted file mode 100644
index cc0f954b59..0000000000
--- a/options/license/CDLA-Permissive-2.0
+++ /dev/null
@@ -1,35 +0,0 @@
-Community Data License Agreement - Permissive - Version 2.0
-
-This is the Community Data License Agreement - Permissive, Version 2.0 (the "agreement"). Data Provider(s) and Data Recipient(s) agree as follows:
-
-1. Provision of the Data
-
-1.1. A Data Recipient may use, modify, and share the Data made available by Data Provider(s) under this agreement if that Data Recipient follows the terms of this agreement.
-
-1.2. This agreement does not impose any restriction on a Data Recipient's use, modification, or sharing of any portions of the Data that are in the public domain or that may be used, modified, or shared under any other legal exception or limitation.
-
-2. Conditions for Sharing Data
-
-2.1. A Data Recipient may share Data, with or without modifications, so long as the Data Recipient makes available the text of this agreement with the shared Data.
-
-3. No Restrictions on Results
-
-3.1. This agreement does not impose any restriction or obligations with respect to the use, modification, or sharing of Results.
-
-4. No Warranty; Limitation of Liability
-
-4.1. All Data Recipients receive the Data subject to the following terms:
-
-THE DATA IS PROVIDED ON AN "AS IS" BASIS, WITHOUT REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
-
-NO DATA PROVIDER SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE DATA OR RESULTS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-5. Definitions
-
-5.1. "Data" means the material received by a Data Recipient under this agreement.
-
-5.2. "Data Provider" means any person who is the source of Data provided under this agreement and in reliance on a Data Recipient's agreement to its terms.
-
-5.3. "Data Recipient" means any person who receives Data directly or indirectly from a Data Provider and agrees to the terms of this agreement.
-
-5.4. "Results" means any outcome obtained by computational analysis of Data, including for example machine learning models and models' insights.
diff --git a/options/license/CDLA-Sharing-1.0 b/options/license/CDLA-Sharing-1.0
deleted file mode 100644
index dba8e2576e..0000000000
--- a/options/license/CDLA-Sharing-1.0
+++ /dev/null
@@ -1,89 +0,0 @@
-Community Data License Agreement – Sharing – Version 1.0
-
-This is the Community Data License Agreement – Sharing, Version 1.0 (“Agreementâ€). Data is provided to You under this Agreement by each of the Data Providers. Your exercise of any of the rights and permissions granted below constitutes Your acceptance and agreement to be bound by the terms and conditions of this Agreement.
-
-The benefits that each Data Provider receives from making Data available and that You receive from Data or otherwise under these terms and conditions shall be deemed sufficient consideration for the formation of this Agreement. Accordingly, Data Provider(s) and You (the “Partiesâ€) agree as follows:
-
-Section 1. Definitions
-
-1.1 “Add†means to supplement Data with Your own or someone else’s Data, resulting in Your “Additions.†Additions do not include Results.
-
-1.2 “Computational Use†means Your analysis (through the use of computational devices or otherwise) or other interpretation of Data. By way of example and not limitation, “Computational Use†includes the application of any computational analytical technique, the purpose of which is the analysis of any Data in digital form to generate information about Data such as patterns, trends, correlations, inferences, insights and attributes.
-
-1.3 “Data†means the information (including copyrightable information, such as images or text), collectively or individually, whether created or gathered by a Data Provider or an Entity acting on its behalf, to which rights are granted under this Agreement.
-
-1.4 “Data Provider†means any Entity (including any employee or contractor of such Entity authorized to Publish Data on behalf of such Entity) that Publishes Data under this Agreement prior to Your Receiving it.
-
-1.5 “Enhanced Data†means the subset of Data that You Publish and that is composed of (a) Your Additions and/or (b) Modifications to Data You have received under this Agreement.
-
-1.6 “Entity†means any natural person or organization that exists under the laws of the jurisdiction in which it is organized, together with all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control†means (a) the power, directly or indirectly, to cause the direction or management of such entity, whether by contract or otherwise, (b) the ownership of more than fifty percent (50%) of the outstanding shares or securities, (c) the beneficial ownership of such entity or, (d) the ability to appoint, whether by agreement or right, the majority of directors of an Entity.
-
-1.7 “Ledger†means a digital record of Data or grants of rights in Data governed by this Agreement, using any technology having functionality to record and store Data or grants, contributions, or licenses to Data governed by this Agreement.
-
-1.8 “Modify†means to delete, erase, correct or re-arrange Data, resulting in “Modifications.†Modifications do not include Results.
-
-1.9 “Publish†means to make all or a subset of Data (including Your Enhanced Data) available in any manner which enables its Use, including by providing a copy on physical media or remote access. For any form of Entity, that is to make the Data available to any individual who is not employed by that Entity or engaged as a contractor or agent to perform work on that Entity’s behalf. A “Publication†occurs each time You Publish Data.
-
-1.10 “Receive†or “Receives†means to have been given access to Data, locally or remotely.
-
-1.11 “Results†means the outcomes or outputs that You obtain from Your Computational Use of Data. Results shall not include more than a de minimis portion of the Data on which the Computational Use is based.
-
-1.12 “Sui Generis Database Rights†means rights, other than copyright, resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other equivalent rights anywhere in the world.
-
-1.13 “Use†means using Data (including accessing, copying, studying, reviewing, adapting, analyzing, evaluating, or making Computational Use of it), either by machines or humans, or a combination of both.
-
-1.14 “You†or “Your†means any Entity that Receives Data under this Agreement.
-
-Section 2. Right and License to Use and to Publish
-
-2.1 Subject to the conditions set forth in Section 3 of this Agreement, Data Provider(s) hereby grant(s) to You a worldwide, non-exclusive, irrevocable (except as provided in Section 5) right to: (a) Use Data; and (b) Publish Data.
-
-2.2 To the extent that the Data or the coordination, selection or arrangement of Data is protected or protectable under copyright, Sui Generis Database Rights, or other law, Data Provider(s) further agree(s) that such Data or coordination, selection or arrangement is hereby licensed to You and to anyone else who Receives Data under this Agreement for Use and Publication, subject to the conditions set forth in Section 3 of this Agreement.
-
-2.3 Except for these rights and licenses expressly granted, no other intellectual property rights are granted or should be implied.
-
-Section 3. Conditions on Rights Granted
-
-3.1 If You Publish Data You Receive or Enhanced Data:
-
-(a) The Data (including the Enhanced Data) must be Published under this Agreement in accordance with this Section 3; and
-
-(b) You must cause any Data files containing Enhanced Data to carry prominent notices that You have changed those files; and
-
-(c) If You Publish Data You Receive, You must preserve all credit or attribution to the Data Provider(s). Such retained credit or attribution includes any of the following to the extent they exist in Data as You have Received it: legal notices or metadata; identification of the Data Provider(s); or hyperlinks to Data to the extent it is practical to do so.
-
-3.2 You may not restrict or deter the ability of anyone who Receives the Data (a) to Publish the Data in a publicly-accessible manner or (b) if the project has designated a Ledger for recording Data or grants of rights in Data for purposes of this Agreement, to record the Data or grants of rights in Data in the Ledger.
-
-3.3 If You Publish Data You Receive, You must do so under an unmodified form of this Agreement and include the text of this Agreement, the name of this Agreement and/or a hyperlink or other method reasonably likely to provide a copy of the text of this Agreement. You may not modify this Agreement or impose any further restrictions on the exercise of the rights granted under this Agreement, including by adding any restriction on commercial or non-commercial Use of Data (including Your Enhanced Data) or by limiting permitted Use of such Data to any particular platform, technology or field of endeavor. Notices that purport to modify this Agreement shall be of no effect.
-
-3.4 You and each Data Provider agree that Enhanced Data shall not be considered a work of joint authorship by virtue of its relationship to Data licensed under this Agreement and shall not require either any obligation of accounting to or the consent of any Data Provider.
-
-3.5 This Agreement imposes no obligations or restrictions on Your Use or Publication of Results.
-
-Section 4. Data Provider(s)’ Representations
-
-4.1 Each Data Provider represents that the Data Provider has exercised reasonable care, to assure that: (a) the Data it Publishes was created or generated by it or was obtained from others with the right to Publish the Data under this Agreement; and (b) Publication of such Data does not violate any privacy or confidentiality obligation undertaken by the Data Provider.
-
-Section 5. Termination
-
-5.1 All of Your rights under this Agreement will terminate, and Your right to Receive, Use or Publish the Data will be revoked or modified if You materially fail to comply with the terms and conditions of this Agreement and You do not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If Your rights under this Agreement terminate, You agree to cease Receipt, Use and Publication of Data. However, Your obligations and any rights and permissions granted by You under this Agreement relating to Data that You Published prior to such termination will continue and survive.
-
-5.2 If You institute litigation against a Data Provider or anyone else who Receives the Data (including a cross-claim in a lawsuit) based on the Data, other than a claim asserting breach of this Agreement, then any rights previously granted to You to Receive, Use and Publish Data under this Agreement will terminate as of the date such litigation is filed.
-
-Section 6. Disclaimer of Warranties and Limitation of Liability
-
-6.1 EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE DATA (INCLUDING ENHANCED DATA) IS PROVIDED ON AN “AS IS†BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
-
-6.2 NEITHER YOU NOR ANY DATA PROVIDERS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE DATA OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-Section 7. Miscellaneous
-
-7.1 You agree that it is solely Your responsibility to comply with all applicable laws with regard to Your Use or Publication of Data, including any applicable privacy, data protection, security and export laws. You agree to take reasonable steps to assist a Data Provider fulfilling responsibilities to comply with applicable laws with regard to Use or Publication of Data Received hereunder.
-
-7.2 You and Data Provider(s), collectively and individually, waive and/or agree not to assert, to the extent permitted by law, any moral rights You or they hold in Data.
-
-7.3 This Agreement confers no rights or remedies upon any person or entity other than the Parties and their respective heirs, executors, successors and assigns.
-
-7.4 The Data Provider(s) reserve no right or expectation of privacy, data protection or confidentiality in any Data that they Publish under this Agreement. If You choose to Publish Data under this Agreement, You similarly do so with no reservation or expectation of any rights of privacy or confidentiality in that Data.
-
-7.5 The Community Data License Agreement workgroup under The Linux Foundation is the steward of this Agreement (“Stewardâ€). No one other than the Steward has the right to modify or publish new versions of this Agreement. Each version will be given a distinguishing version number. You may Use and Publish Data Received hereunder under the terms of the version of the Agreement under which You originally Received the Data, or under the terms of any subsequent version published by the Steward.
diff --git a/options/license/CECILL-1.0 b/options/license/CECILL-1.0
deleted file mode 100644
index f8df717732..0000000000
--- a/options/license/CECILL-1.0
+++ /dev/null
@@ -1,216 +0,0 @@
-CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL
-
-Avertissement
-
-Ce contrat est une licence de logiciel libre issue d’une concertation entre ses auteurs afin que le respect de deux grands principes préside à sa rédaction:
- • d’une part, sa conformité au droit français, tant au regard du droit de la responsabilité civile que du droit de la propriété intellectuelle et de la protection qu’il offre aux auteurs et titulaires des droits patrimoniaux sur un logiciel.
- • d’autre part, le respect des principes de diffusion des logiciels libres: accès au code source, droits étendus conférés aux utilisateurs.
-
-Les auteurs de la licence CeCILL1 sont:
-
-Commissariat à l’Energie Atomique – CEA, établissement public de caractère scientifique technique et industriel, dont le siège est situé 31-33 rue de la Fédération, 75752 PARIS cedex 15.
-
-Centre National de la Recherche Scientifique – CNRS, établissement public à caractère scientifique et technologique, dont le siège est situé 3 rue Michel-Ange 75794 Paris cedex 16.
-
-Institut National de Recherche en Informatique et en Automatique – INRIA, établissement public à caractère scientifique et technologique, dont le siège est situé Domaine de Voluceau, Rocquencourt, BP 105, 78153 Le Chesnay cedex.
-
-PREAMBULE
-
-Ce contrat est une licence de logiciel libre dont l'objectif est de conférer aux utilisateurs la liberté de modification et de redistribution du logiciel régi par cette licence dans le cadre d'un modèle de diffusion «open source».
-
-L'exercice de ces libertés est assorti de certains devoirs à la charge des utilisateurs afin de préserver ce statut au cours des redistributions ultérieures.
-
-L’accessibilité au code source et les droits de copie, de modification et de redistribution qui en découlent ont pour contrepartie de n’offrir aux utilisateurs qu’une garantie limitée et de ne faire peser sur l’auteur du logiciel, le titulaire des droits patrimoniaux et les concédants successifs qu’une responsabilité restreinte.
-
-A cet égard l’attention de l’utilisateur est attirée sur les risques associés au chargement, à l’utilisation, à la modification et/ou au développement et à la reproduction du logiciel par l’utilisateur étant donné sa spécificité de logiciel libre, qui peut le rendre complexe à manipuler et qui le réserve donc à des développeurs et des professionnels avertis possédant des connaissances informatiques approfondies. Les utilisateurs sont donc invités à charger et tester l’adéquation du Logiciel à leurs besoins dans des conditions permettant d'assurer la sécurité de leurs systèmes et ou de leurs données et, plus généralement, à l'utiliser et l'exploiter dans les même conditions de sécurité. Ce contrat peut être reproduit et diffusé librement, sous réserve de le conserver en l’état, sans ajout ni suppression de clauses.
-
-Ce contrat est susceptible de s’appliquer à tout logiciel dont le titulaire des droits patrimoniaux décide de soumettre l’exploitation aux dispositions qu’il contient.
-
-Article 1er - DEFINITIONS
-
-Dans ce contrat, les termes suivants, lorsqu’ils seront écrits avec une lettre capitale, auront la signification suivante:
-
-Contrat: désigne le présent contrat de licence, ses éventuelles versions postérieures et annexes.
-
-Logiciel: désigne le logiciel sous sa forme de Code Objet et/ou de Code Source et le cas échéant sa documentation, dans leur état au moment de l’acceptation du
-Contrat par le Licencié.
-
-Logiciel Initial: désigne le Logiciel sous sa forme de Code Source et de Code Objet et le cas échéant sa documentation, dans leur état au moment de leur première diffusion sous les termes du Contrat.
-
-Logiciel Modifié: désigne le Logiciel modifié par au moins une Contribution.
-
-Code Source: désigne l’ensemble des instructions et des lignes de programme du Logiciel et auquel l’accès est nécessaire en vue de modifier le Logiciel.
-
-Code Objet: désigne les fichiers binaires issus de la compilation du Code Source.
-
-Titulaire : désigne le détenteur des droits patrimoniaux d’auteur sur le Logiciel Initial.
-
-Licencié(s): désigne le ou les utilisateur(s) du Logiciel ayant accepté le Contrat.
-
-Contributeur: désigne le Licencié auteur d’au moins une Contribution.
-
-Concédant: désigne le Titulaire ou toute personne physique ou morale distribuant le Logiciel sous le Contrat.
-
-Contributions: désigne l’ensemble des modifications, corrections, traductions, adaptations et/ou nouvelles fonctionnalités intégrées dans le Logiciel par tout
-
-Contributeur, ainsi que les Modules Statiques.
-
-Module: désigne un ensemble de fichiers sources y compris leur documentation qui, une fois compilé sous forme exécutable, permet de réaliser des fonctionnalités ou
-services supplémentaires à ceux fournis par le Logiciel.
-
-Module Dynamique: désigne tout Module, créé par le Contributeur, indépendant du Logiciel, tel que ce Module et le Logiciel sont sous forme de deux exécutables indépendants qui s’exécutent dans un espace d’adressage indépendant, l’un appelant l’autre au moment de leur exécution.
-
-Module Statique: désigne tout Module créé par le Contributeur et lié au Logiciel par un lien statique rendant leur code objet dépendant l'un de l'autre. Ce Module et le Logiciel auquel il est lié, sont regroupés en un seul exécutable.
-
-Parties: désigne collectivement le Licencié et le Concédant.
-
-Ces termes s’entendent au singulier comme au pluriel.
-
-Article 2 - OBJET
-
-Le Contrat a pour objet la concession par le Concédant au Licencié d’une Licence non exclusive, transférable et mondiale du Logiciel telle que définie ci-après à l'article 5 pour toute la durée de protection des droits portant sur ce Logiciel.
-
-Article 3 - ACCEPTATION
-
-3.1. L’acceptation par le Licencié des termes du Contrat est réputée acquise du fait du premier des faits suivants:
- • (i) le chargement du Logiciel par tout moyen notamment par téléchargement à partir d’un serveur distant ou par chargement à partir d’un support physique;
- • (ii) le premier exercice par le Licencié de l’un quelconque des droits concédés par le Contrat.
-
-3.2. Un exemplaire du Contrat, contenant notamment un avertissement relatif aux spécificités du Logiciel, à la restriction de garantie et à la limitation à un usage par des utilisateurs expérimentés a été mis à disposition du Licencié préalablement à son acceptation telle que définie à l'article 3.1 ci dessus et le Licencié reconnaît en avoir pris connaissances.
-
-Article 4 - ENTREE EN VIGUEUR ET DUREE
-
-4.1.ENTREE EN VIGUEUR
-
-Le Contrat entre en vigueur à la date de son acceptation par le Licencié telle que définie en 3.1.
-
-4.2. DUREE
-
-Le Contrat produira ses effets pendant toute la durée légale de protection des droits patrimoniaux portant sur le Logiciel.
-
-Article 5 - ETENDUE DES DROITS CONCEDES
-
-Le Concédant concède au Licencié, qui accepte, les droits suivants sur le Logiciel pour toutes destinations et pour la durée du Contrat dans les conditions ci-après détaillées.
-
-Par ailleurs, le Concédant concède au Licencié à titre gracieux les droits d’exploitation du ou des brevets qu’il détient sur tout ou partie des inventions
-implémentées dans le Logiciel.
-
-5.1. DROITS D’UTILISATION
-
-Le Licencié est autorisé à utiliser le Logiciel, sans restriction quant aux domaines d’application, étant ci-après précisé que cela comporte:
- 1. la reproduction permanente ou provisoire du Logiciel en tout ou partie par tout moyen et sous toute forme.
- 2. le chargement, l’affichage, l’exécution, ou le stockage du Logiciel sur tout support.
- 3. la possibilité d’en observer, d’en étudier, ou d’en tester le fonctionnement afin de déterminer les idées et principes qui sont à la base de n’importe quel élément de ce Logiciel; et ceci, lorsque le Licencié effectue toute opération de chargement, d’affichage, d’exécution, de transmission ou de stockage du Logiciel qu’il est en droit d’effectuer en vertu du Contrat.
-
-5.2. DROIT D’APPORTER DES CONTRIBUTIONS
-
-Le droit d’apporter des Contributions comporte le droit de traduire, d’adapter, d’arranger ou d’apporter toute autre modification du Logiciel et le droit de reproduire le Logiciel en résultant.
-
-Le Licencié est autorisé à apporter toute Contribution au Logiciel sous réserve de mentionner, de façon explicite, son nom en tant qu’auteur de cette Contribution et la date de création de celle-ci.
-
-5.3. DROITS DE DISTRIBUTION ET DE DIFFUSION
-
-Le droit de distribution et de diffusion comporte notamment le droit de transmettre et de communiquer le Logiciel au public sur tout support et par tout moyen ainsi que le droit de mettre sur le marché à titre onéreux ou gratuit, un ou des exemplaires du Logiciel par tout procédé.
-
-Le Licencié est autorisé à redistribuer des copies du Logiciel, modifié ou non, à des tiers dans les conditions ci-après détaillées.
-
-5.3.1. REDISTRIBUTION DU LOGICIEL SANS MODIFICATION
-Le Licencié est autorisé à redistribuer des copies conformes du Logiciel, sous forme de Code Source ou de Code Objet, à condition que cette redistribution respecte les dispositions du Contrat dans leur totalité et soit accompagnée:
- 1. d’un exemplaire du Contrat,
- 2. d’un avertissement relatif à la restriction de garantie et de responsabilité du Concédant telle que prévue aux articles 8 et 9,
-et que, dans le cas où seul le Code Objet du Logiciel est redistribué, le Licencié permette aux futurs Licenciés d’accéder facilement au Code Source complet du Logiciel en indiquant les modalités d’accès, étant entendu que le coût additionnel d’acquisition du Code Source ne devra pas excéder le simple coût de transfert des données.
-
-5.3.2. REDISTRIBUTION DU LOGICIEL MODIFIE
-Lorsque le Licencié apporte une Contribution au Logiciel, les conditions de redistribution du Logiciel Modifié sont alors soumises à l’intégralité des dispositions du Contrat.
-Le Licencié est autorisé à redistribuer le Logiciel Modifié, sous forme de Code Source ou de Code Objet, à condition que cette redistribution respecte les dispositions du Contrat dans leur totalité et soit accompagnée:
- 1. d’un exemplaire du Contrat,
- 2. d’un avertissement relatif à la restriction de garantie et de responsabilité du concédant telle que prévue aux articles 8 et 9,
-et que, dans le cas où seul le Code Objet du Logiciel Modifié est redistribué, le Licencié permette aux futurs Licenciés d’accéder facilement au Code Source complet du Logiciel Modifié en indiquant les modalités d’accès, étant entendu que le coût additionnel d’acquisition du Code Source ne devra pas excéder le simple coût de transfert des données.
-
-5.3.3. REDISTRIBUTION DES MODULES DYNAMIQUES
-Lorsque le Licencié a développé un Module Dynamique les conditions du Contrat ne s’appliquent pas à ce Module Dynamique, qui peut être distribué sous un contrat de licence différent.
-
-5.3.4. COMPATIBILITE AVEC LA LICENCE GPL
-Dans le cas où le Logiciel, Modifié ou non, est intégré à un code soumis aux dispositions de la licence GPL, le Licencié est autorisé à redistribuer l’ensemble sous la licence GPL.
-Dans le cas où le Logiciel Modifié intègre un code soumis aux dispositions de la licence GPL, le Licencié est autorisé à redistribuer le Logiciel Modifié sous la licence GPL.
-
-Article 6 - PROPRIETE INTELLECTUELLE
-
-6.1. SUR LE LOGICIEL INITIAL
-Le Titulaire est détenteur des droits patrimoniaux sur le Logiciel Initial. Toute utilisation du Logiciel Initial est soumise au respect des conditions dans lesquelles le Titulaire a choisi de diffuser son œuvre et nul autre n’a la faculté de modifier les conditions de diffusion de ce Logiciel Initial.
-Le Titulaire s'engage à maintenir la diffusion du Logiciel initial sous les conditions du Contrat et ce, pour la durée visée à l'article 4.2.
-
-6.2. SUR LES CONTRIBUTIONS
-Les droits de propriété intellectuelle sur les Contributions sont attachés au titulaire de droits patrimoniaux désigné par la législation applicable.
-
-6.3. SUR LES MODULES DYNAMIQUES
-Le Licencié ayant développé un Module Dynamique est titulaire des droits de propriété intellectuelle sur ce Module Dynamique et reste libre du choix du contrat régissant sa diffusion.
-
-6.4. DISPOSITIONS COMMUNES
-
-6.4.1. Le Licencié s’engage expressément:
- 1. à ne pas supprimer ou modifier de quelque manière que ce soit les mentions de propriété intellectuelle apposées sur le Logiciel;
- 2. à reproduire à l’identique lesdites mentions de propriété intellectuelle sur les copies du Logiciel.
-6.4.2. Le Licencié s’engage à ne pas porter atteinte, directement ou indirectement, aux droits de propriété intellectuelle du Titulaire et/ou des Contributeurs et à prendre, le cas échéant, à l’égard de son personnel toutes les mesures nécessaires pour assurer le respect des dits droits de propriété intellectuelle du Titulaire et/ou des Contributeurs.
-
-Article 7 - SERVICES ASSOCIES
-
-7.1. Le Contrat n’oblige en aucun cas le Concédant à la réalisation de prestations d’assistance technique ou de maintenance du Logiciel.
-Cependant le Concédant reste libre de proposer ce type de services. Les termes et conditions d’une telle assistance technique et/ou d’une telle maintenance seront alors déterminés dans un acte séparé. Ces actes de maintenance et/ou assistance technique n’engageront que la seule responsabilité du Concédant qui les propose.
-
-7.2. De même, tout Concédant est libre de proposer, sous sa seule responsabilité, à ses licenciés une garantie, qui n’engagera que lui, lors de la redistribution du Logiciel et/ou du Logiciel Modifié et ce, dans les conditions qu’il souhaite. Cette garantie et les modalités financières de son application feront l’objet d’un acte séparé entre le Concédant et le Licencié.
-
-Article 8 - RESPONSABILITE
-
-8.1. Sous réserve des dispositions de l’article 8.2, si le Concédant n’exécute pas tout ou partie des obligations mises à sa charge par le Contrat, le Licencié a la faculté, sous réserve de prouver la faute du Concédant concerné, de solliciter la réparation du préjudice direct qu’il subit et dont il apportera la preuve.
-
-8.2. La responsabilité du Concédant est limitée aux engagements pris en application du Contrat et ne saurait être engagée en raison notamment:(i) des dommages dus à l’inexécution, totale ou partielle, de ses obligations par le Licencié, (ii) des dommages directs ou indirects découlant de l’utilisation ou des performances du Logiciel subis par le Licencié lorsqu’il s’agit d’un professionnel utilisant le Logiciel à des fins professionnelles et (iii) des dommages indirects découlant de l’utilisation ou des performances du Logiciel. Les Parties conviennent expressément que tout préjudice financier ou commercial (par exemple perte de données, perte de bénéfices, perte d’exploitation, perte de clientèle ou de commandes, manque à gagner, trouble commercial quelconque) ou toute action dirigée contre le Licencié par un tiers, constitue un dommage indirect et n’ouvre pas droit à réparation par le Concédant.
-
-Article 9 - GARANTIE
-
-9.1. Le Licencié reconnaît que l’état actuel des connaissances scientifiques et techniques au moment de la mise en circulation du Logiciel ne permet pas d’en tester et d’en vérifier toutes les utilisations ni de détecter l’existence d’éventuels défauts. L’attention du Licencié a été attirée sur ce point sur les risques associés au chargement, à l’utilisation, la modification et/ou au développement et à la reproduction du Logiciel qui sont réservés à des utilisateurs avertis.
-Il relève de la responsabilité du Licencié de contrôler, par tous moyens, l’adéquation du produit à ses besoins, son bon fonctionnement et de s'assurer qu’il ne causera pas de dommages aux personnes et aux biens.
-
-9.2. Le Concédant déclare de bonne foi être en droit de concéder l'ensemble des droits attachés au Logiciel (comprenant notamment les droits visés à l'article 5).
-
-9.3. Le Licencié reconnaît que le Logiciel est fourni «en l'état» par le Concédant sans autre garantie, expresse ou tacite, que celle prévue à l’article 9.2 et notamment sans aucune garantie sur sa valeur commerciale, son caractère sécurisé, innovant ou pertinent.
-En particulier, le Concédant ne garantit pas que le Logiciel est exempt d'erreur, qu’il fonctionnera sans interruption, qu’il sera compatible avec l’équipement du Licencié et sa configuration logicielle ni qu’il remplira les besoins du Licencié.
-
-9.4. Le Concédant ne garantit pas, de manière expresse ou tacite, que le Logiciel ne porte pas atteinte à un quelconque droit de propriété intellectuelle d’un tiers portant sur un brevet, un logiciel ou sur tout autre droit de propriété. Ainsi, le Concédant exclut toute garantie au profit du Licencié contre les actions en contrefaçon qui pourraient être diligentées au titre de l’utilisation, de la modification, et de la redistribution du Logiciel. Néanmoins, si de telles actions sont exercées contre le Licencié, le Concédant lui apportera son aide technique et juridique pour sa défense. Cette aide technique et juridique est déterminée au cas par cas entre le Concédant concerné et le Licencié dans le cadre d’un protocole d’accord. Le Concédant dégage toute responsabilité quant à l’utilisation de la dénomination du Logiciel par le Licencié. Aucune garantie n’est apportée quant à l’existence de droits antérieurs sur le nom du Logiciel et sur l’existence d’une marque.
-
-Article 10 - RESILIATION
-
-10.1. En cas de manquement par le Licencié aux obligations mises à sa charge par le Contrat, le Concédant pourra résilier de plein droit le Contrat trente (30) jours après notification adressée au Licencié et restée sans effet.
-10.2. Le Licencié dont le Contrat est résilié n’est plus autorisé à utiliser, modifier ou distribuer le Logiciel. Cependant, toutes les licences qu’il aura concédées antérieurement à la résiliation du Contrat resteront valides sous réserve qu’elles aient été effectuées en conformité avec le Contrat.
-
-Article 11 - DISPOSITIONS DIVERSES
-
-11.1. CAUSE EXTERIEURE
-Aucune des Parties ne sera responsable d’un retard ou d’une défaillance d’exécution du Contrat qui serait dû à un cas de force majeure, un cas fortuit ou une cause extérieure, telle que, notamment, le mauvais fonctionnement ou les interruptions du réseau électrique ou de télécommunication, la paralysie du réseau liée à une attaque informatique, l’intervention des autorités gouvernementales, les catastrophes naturelles, les dégâts des eaux, les tremblements de terre, le feu, les explosions, les grèves et les conflits sociaux, l’état de guerre…
-
-11.2. Le fait, par l’une ou l’autre des Parties, d’omettre en une ou plusieurs occasions de se prévaloir d’une ou plusieurs dispositions du Contrat, ne pourra en aucun cas impliquer renonciation par la Partie intéressée à s’en prévaloir ultérieurement.
-
-11.3. Le Contrat annule et remplace toute convention antérieure, écrite ou orale, entre les Parties sur le même objet et constitue l’accord entier entre les Parties sur cet objet. Aucune addition ou modification aux termes du Contrat n’aura d’effet à l’égard des Parties à moins d’être faite par écrit et signée par leurs représentants dûment habilités.
-
-11.4. Dans l’hypothèse où une ou plusieurs des dispositions du Contrat s’avèrerait contraire à une loi ou à un texte applicable, existants ou futurs, cette loi ou ce texte prévaudrait, et les Parties feraient les amendements nécessaires pour se conformer à cette loi ou à ce texte. Toutes les autres dispositions resteront en vigueur. De même, la nullité, pour quelque raison que ce soit, d’une des dispositions du Contrat ne saurait entraîner la nullité de l’ensemble du Contrat.
-
-11.5. LANGUE
-Le Contrat est rédigé en langue française et en langue anglaise. En cas de divergence d’interprétation, seule la version française fait foi.
-
-Article 12 - NOUVELLES VERSIONS DU CONTRAT
-
-12.1. Toute personne est autorisée à copier et distribuer des copies de ce Contrat.
-
-12.2. Afin d’en préserver la cohérence, le texte du Contrat est protégé et ne peut être modifié que par les auteurs de la licence, lesquels se réservent le droit de publier périodiquement des mises à jour ou de nouvelles versions du Contrat, qui possèderont chacune un numéro distinct. Ces versions ultérieures seront susceptibles de prendre en compte de nouvelles problématiques rencontrées par les logiciels libres.
-
-12.3. Tout Logiciel diffusé sous une version donnée du Contrat ne pourra faire l'objet d'une diffusion ultérieure que sous la même version du Contrat ou une version postérieure, sous réserve des dispositions de l'article 5.3.4.
-
-Article 13 - LOI APPLICABLE ET COMPETENCE TERRITORIALE
-
-13.1. Le Contrat est régi par la loi française. Les Parties conviennent de tenter de régler à l’amiable les différends ou litiges qui viendraient à se produire par suite ou à l’occasion du Contrat.
-
-13.2. A défaut d’accord amiable dans un délai de deux (2) mois à compter de leur survenance et sauf situation relevant d’une procédure d’urgence, les différends ou litiges seront portés par la Partie la plus diligente devant les Tribunaux compétents de Paris.
-
-1 Ce: CEA, C: CNRS, I: INRIA, LL: Logiciel Libre
-
-Version 1 du 21/06/2004
diff --git a/options/license/CECILL-1.1 b/options/license/CECILL-1.1
deleted file mode 100644
index 95bea5c9db..0000000000
--- a/options/license/CECILL-1.1
+++ /dev/null
@@ -1,229 +0,0 @@
- FREE SOFTWARE LICENSING AGREEMENT CeCILL
-
-Notice
-
-This Agreement is a free software license that is the result of discussions between its authors in order to ensure compliance with the two main principles guiding its drafting:
- - firstly, its conformity with French law, both as regards the law of torts and intellectual property law, and the protection that it offers to authors and the holders of economic rights over software.
- - secondly, compliance with the principles for the distribution of free software: access to source codes, extended user-rights.
-
-The following bodies are the authors of this license CeCILL (Ce : CEA, C : CNRS, I : INRIA, LL : Logiciel Libre):
-
-Commissariat à l'Energie Atomique - CEA, a public scientific, technical and industrial establishment, having its principal place of business at 31-33 rue de la Fédération, 75752 PARIS cedex 15, France.
-
-Centre National de la Recherche Scientifique - CNRS, a public scientific and technological establishment, having its principal place of business at 3 rue Michel-Ange 75794 Paris cedex 16, France.
-
-Institut National de Recherche en Informatique et en Automatique - INRIA, a public scientific and technological establishment, having its principal place of business at Domaine de Voluceau, Rocquencourt, BP 105, 78153 Le Chesnay cedex.
-
-PREAMBLE
-
-The purpose of this Free Software Licensing Agreement is to grant users the right to modify and redistribute the software governed by this license within the framework of an "open source" distribution model.
-
-The exercising of these rights is conditional upon certain obligations for users so as to ensure that this status is retained for subsequent redistribution operations.
-
-As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license, users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the successive licensors only have limited liability.
-
-In this respect, it is brought to the user's attention that the risks associated with loading, using, modifying and/or developing or reproducing the software by the user given its nature of Free Software, that may mean that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the Software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured and, more generally, to use and operate it in the same conditions of security. This Agreement may be freely reproduced and published, provided it is not altered, and that no Articles are either added or removed herefrom.
-
-This Agreement may apply to any or all software for which the holder of the economic rights decides to submit the operation thereof to its provisions.
-
-Article 1 - DEFINITIONS
-
-For the purposes of this Agreement, when the following expressions commence with a capital letter, they shall have the following meaning:
-
-Agreement: means this Licensing Agreement, and any or all of its subsequent versions.
-
-Software: means the software in its Object Code and/or Source Code form and, where applicable, its documentation, "as is" at the time when the Licensee accepts the Agreement.
-
-Initial Software: means the Software in its Source Code and/or Object Code form and, where applicable, its documentation, "as is" at the time when it is distributed for the first time under the terms and conditions of the Agreement.
-
-Modified Software: means the Software modified by at least one Contribution.
-
-Source Code: means all the Software's instructions and program lines to which access is required so as to modify the Software.
-
-Object Code: means the binary files originating from the compilation of the Source Code.
-
-Holder: means the holder of the economic rights over the Initial Software.
-
-Licensee(s): mean(s) the Software user(s) having accepted the Agreement.
-
-Contributor: means a Licensee having made at least one Contribution.
-
-Licensor: means the Holder, or any or all other individual or legal entity, that distributes the Software under the Agreement.
-
-Contributions: mean any or all modifications, corrections, translations, adaptations and/or new functionalities integrated into the Software by any or all Contributor, and the Static Modules.
-
-Module: means a set of sources files including their documentation that, once compiled in executable form, enables supplementary functionalities or services to be developed in addition to those offered by the Software.
-
-Dynamic Module: means any or all module, created by the Contributor, that is independent of the Software, so that this module and the Software are in two different executable forms that are run in separate address spaces, with one calling the other when they are run.
-
-Static Module: means any or all module, created by the Contributor and connected to the Software by a static link that makes their object codes interdependent. This module and the Software to which it is connected, are combined in a single executable.
-
-Parties: mean both the Licensee and the Licensor.
-
-These expressions may be used both in singular and plural form.
-
-Article 2 - PURPOSE
-
-The purpose of the Agreement is to enable the Licensor to grant the Licensee a free, non-exclusive, transferable and worldwide License for the Software as set forth in Article 5 hereinafter for the whole term of protection of the rights over said Software.
-
-Article 3 - ACCEPTANCE
-
-3.1. The Licensee shall be deemed as having accepted the terms and conditions of this Agreement by the occurrence of the first of the following events:
- (i) loading the Software by any or all means, notably, by downloading from a remote server, or by loading from a physical medium;
- (ii) the first time the Licensee exercises any of the rights granted hereunder.
-
-3.2. One copy of the Agreement, containing a notice relating to the specific nature of the Software, to the limited warranty, and to the limitation to use by experienced users has been provided to the Licensee prior to its acceptance as set forth in Article 3.1 hereinabove, and the Licensee hereby acknowledges that it is aware thereof.
-
-Article 4 - EFFECTIVE DATE AND TERM
-
-4.1. EFFECTIVE DATE
-
-The Agreement shall become effective on the date when it is accepted by the Licensee as set forth in Article 3.1.
-
-4.2. TERM
-
-The Agreement shall remain in force during the whole legal term of protection of the economic rights over the Software.
-
-Article 5 - SCOPE OF THE RIGHTS GRANTED ---------------------------------------
-
-The Licensor hereby grants to the Licensee, that accepts such, the following rights as regards the Software for any or all use, and for the term of the Agreement, on the basis of the terms and conditions set forth hereinafter.
-
-Otherwise, the Licensor grants to the Licensee free of charge exploitation rights on the patents he holds on whole or part of the inventions implemented in the Software.
-
-5.1. RIGHTS OF USE
-
-The Licensee is authorized to use the Software, unrestrictedly, as regards the fields of application, with it being hereinafter specified that this relates to:
- 1. permanent or temporary reproduction of all or part of the Software by any or all means and in any or all form.
- 2. loading, displaying, running, or storing the Software on any or all medium.
- 3. entitlement to observe, study or test the operation thereof so as to establish the ideas and principles that form the basis for any or all constituent elements of said Software. This shall apply when the Licensee carries out any or all loading, displaying, running, transmission or storage operation as regards the Software, that it is entitled to carry out hereunder.
-
-5.2. entitlement to make CONTRIBUTIONS
-
-The right to make Contributions includes the right to translate, adapt, arrange, or make any or all modification to the Software, and the right to reproduce the resulting Software.
-
-The Licensee is authorized to make any or all Contribution to the Software provided that it explicitly mentions its name as the author of said Contribution and the date of the development thereof.
-
-5.3. DISTRIBUTION AND PUBLICATION RIGHTS
-
-In particular, the right of distribution and publication includes the right to transmit and communicate the Software to the general public on any or all medium, and by any or all means, and the right to market, either in consideration of a fee, or free of charge, a copy or copies of the Software by means of any or all process. The Licensee is further authorized to redistribute copies of the modified or unmodified Software to third parties according to the terms and conditions set forth hereinafter.
-
-5.3.1. REDISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION
-
-The Licensee is authorized to redistribute true copies of the Software in Source Code or Object Code form, provided that said redistribution complies with all the provisions of the Agreement and is accompanied by:
-
- 1. a copy of the Agreement,
- 2. a notice relating to the limitation of both the Licensor's warranty and liability as set forth in Articles 8 and 9,
-
-and that, in the event that only the Software's Object Code is redistributed, the Licensee allows future Licensees unhindered access to the Software's full Source Code by providing them with the terms and conditions for access thereto, it being understood that the additional cost of acquiring the Source Code shall not exceed the cost of transferring the data.
-
-5.3.2. REDISTRIBUTION OF MODIFIED SOFTWARE
-
-When the Licensee makes a Contribution to the Software, the terms and conditions for the redistribution of the Modified Software shall then be subject to all the provisions hereof.
-
-The Licensee is authorized to redistribute the Modified Software, in Source Code or Object Code form, provided that said redistribution complies with all the provisions of the Agreement and is accompanied by:
-
- 1. a copy of the Agreement,
- 2. a notice relating to the limitation of both the Licensor's warranty and liability as set forth in Articles 8 and 9,
-
-and that, in the event that only the Modified Software's Object Code is redistributed, the Licensee allows future Licensees unhindered access to the Modified Software's full Source Code by providing them with the terms and conditions for access thereto, it being understood that the additional cost of acquiring the Source Code shall not exceed the cost of transferring the data.
-
-5.3.3. redistribution OF DYNAMIC MODULES
-
-When the Licensee has developed a Dynamic Module, the terms and conditions hereof do not apply to said Dynamic Module, that may be distributed under a separate Licensing Agreement.
-
-5.3.4. COMPATIBILITY WITH THE GPL LICENSE
-
-In the event that the Modified or unmodified Software is included in a code that is subject to the provisions of the GPL License, the Licensee is authorized to redistribute the whole under the GPL License.
-
-In the event that the Modified Software includes a code that is subject to the provisions of the GPL License, the Licensee is authorized to redistribute the Modified Software under the GPL License.
-
-Article 6 - INTELLECTUAL PROPERTY
-
-6.1. OVER THE INITIAL SOFTWARE
-
-The Holder owns the economic rights over the Initial Software. Any or all use of the Initial Software is subject to compliance with the terms and conditions under which the Holder has elected to distribute its work and no one shall be entitled to and it shall have sole entitlement to modify the terms and conditions for the distribution of said Initial Software.
-
-The Holder undertakes to maintain the distribution of the Initial Software under the conditions of the Agreement, for the duration set forth in article 4.2..
-
-6.2. OVER THE CONTRIBUTIONS
-
-The intellectual property rights over the Contributions belong to the holder of the economic rights as designated by effective legislation.
-
-6.3. OVER THE DYNAMIC MODULES
-
-The Licensee having developed a Dynamic Module is the holder of the intellectual property rights over said Dynamic Module and is free to choose the agreement that shall govern its distribution.
-
-6.4. JOINT PROVISIONS
-
-6.4.1. The Licensee expressly undertakes:
-
- 1. not to remove, or modify, in any or all manner, the intellectual property notices affixed to the Software;
- 2. to reproduce said notices, in an identical manner, in the copies of the Software.
-
-6.4.2. The Licensee undertakes not to directly or indirectly infringe the intellectual property rights of the Holder and/or Contributors and to take, where applicable, vis-à-vis its staff, any or all measures required to ensure respect for said intellectual property rights of the Holder and/or Contributors.
-
-Article 7 - RELATED SERVICES
-
-7.1. Under no circumstances shall the Agreement oblige the Licensor to provide technical assistance or maintenance services for the Software.
-
-However, the Licensor is entitled to offer this type of service. The terms and conditions of such technical assistance, and/or such maintenance, shall then be set forth in a separate instrument. Only the Licensor offering said maintenance and/or technical assistance services shall incur liability therefor.
-
-7.2. Similarly, any or all Licensor shall be entitled to offer to its Licensees, under its own responsibility, a warranty, that shall only be binding upon itself, for the redistribution of the Software and/or the Modified Software, under terms and conditions that it shall decide upon itself. Said warranty, and the financial terms and conditions of its application, shall be subject to a separate instrument executed between the Licensor and the Licensee.
-
-Article 8 - LIABILITY
-
-8.1. Subject to the provisions of Article 8.2, should the Licensor fail to fulfill all or part of its obligations hereunder, the Licensee shall be entitled to claim compensation for the direct loss suffered as a result of a fault on the part of the Licensor, subject to providing evidence of it.
-
-8.2. The Licensor's liability is limited to the commitments made under this Licensing Agreement and shall not be incurred as a result , in particular: (i) of loss due the Licensee's total or partial failure to fulfill its obligations, (ii) direct or consequential loss due to the Software's use or performance that is suffered by the Licensee, when the latter is a professional using said Software for professional purposes and (iii) consequential loss due to the Software's use or performance. The Parties expressly agree that any or all pecuniary or business loss (i.e. loss of data, loss of profits, operating loss, loss of customers or orders, opportunity cost, any disturbance to business activities) or any or all legal proceedings instituted against the Licensee by a third party, shall constitute consequential loss and shall not provide entitlement to any or all compensation from the Licensor.
-
-Article 9 - WARRANTY
-
-9.1. The Licensee acknowledges that the current situation as regards scientific and technical know-how at the time when the Software was distributed did not enable all possible uses to be tested and verified, nor for the presence of any or all faults to be detected. In this respect, the Licensee's attention has been drawn to the risks associated with loading, using, modifying and/or developing and reproducing the Software that are reserved for experienced users.
-
-The Licensee shall be responsible for verifying, by any or all means, the product's suitability for its requirements, its due and proper functioning, and for ensuring that it shall not cause damage to either persons or property.
-
-9.2. The Licensor hereby represents, in good faith, that it is entitled to grant all the rights on the Software (including in particular the rights set forth in Article 5 hereof over the Software).
-
-9.3. The Licensee acknowledges that the Software is supplied "as is" by the Licensor without any or all other express or tacit warranty, other than that provided for in Article 9.2 and, in particular, without any or all warranty as to its market value, its secured, innovative or relevant nature.
-
-Specifically, the Licensor does not warrant that the Software is free from any or all error, that it shall operate continuously, that it shall be compatible with the Licensee's own equipment and its software configuration, nor that it shall meet the Licensee's requirements.
-
-9.4. The Licensor does not either expressly or tacitly warrant that the Software does not infringe any or all third party intellectual right relating to a patent, software or to any or all other property right. Moreover, the Licensor shall not hold the Licensee harmless against any or all proceedings for infringement that may be instituted in respect of the use, modification and redistribution of the Software. Nevertheless, should such proceedings be instituted against the Licensee, the Licensor shall provide it with technical and legal assistance for its defense. Such technical and legal assistance shall be decided upon on a case-by-case basis between the relevant Licensor and the Licensee pursuant to a memorandum of understanding. The Licensor disclaims any or all liability as regards the Licensee's use of the Software's name. No warranty shall be provided as regards the existence of prior rights over the name of the Software and as regards the existence of a trademark.
-
-Article 10 - TERMINATION
-
-10.1. In the event of a breach by the Licensee of its obligations hereunder, the Licensor may automatically terminate this Agreement thirty (30) days after notice has been sent to the Licensee and has remained ineffective.
-
-10.2. The Licensee whose Agreement is terminated shall no longer be authorized to use, modify or distribute the Software. However, any or all licenses that it may have granted prior to termination of the Agreement shall remain valid subject to their having been granted in compliance with the terms and conditions hereof.
-
-Article 11 - MISCELLANEOUS PROVISIONS
-
-11.1. EXCUSABLE EVENTS
-
-Neither Party shall be liable for any or all delay, or failure to perform the Agreement, that may be attributable to an event of force majeure, an act of God or an outside cause, such as, notably, defective functioning, or interruptions affecting the electricity or telecommunications networks, blocking of the network following a virus attack, the intervention of the government authorities, natural disasters, water damage, earthquakes, fire, explosions, strikes and labor unrest, war, etc.
-
-11.2. The fact that either Party may fail, on one or several occasions, to invoke one or several of the provisions hereof, shall under no circumstances be interpreted as being a waiver by the interested Party of its entitlement to invoke said provision(s) subsequently.
-
-11.3. The Agreement cancels and replaces any or all previous agreement, whether written or oral, between the Parties and having the same purpose, and constitutes the entirety of the agreement between said Parties concerning said purpose. No supplement or modification to the terms and conditions hereof shall be effective as regards the Parties unless it is made in writing and signed by their duly authorized representatives.
-
-11.4. In the event that one or several of the provisions hereof were to conflict with a current or future applicable act or legislative text, said act or legislative text shall take precedence, and the Parties shall make the necessary amendments so as to be in compliance with said act or legislative text. All the other provisions shall remain effective. Similarly, the fact that a provision of the Agreement may be null and void, for any reason whatsoever, shall not cause the Agreement as a whole to be null and void.
-
-11.5. LANGUAGE
-
-The Agreement is drafted in both French and English. In the event of a conflict as regards construction, the French version shall be deemed authentic.
-
-Article 12 - NEW VERSIONS OF THE AGREEMENT
-
-12.1. Any or all person is authorized to duplicate and distribute copies of this Agreement.
-
-12.2. So as to ensure coherence, the wording of this Agreement is protected and may only be modified by the authors of the License, that reserve the right to periodically publish updates or new versions of the Agreement, each with a separate number. These subsequent versions may address new issues encountered by Free Software.
-
-12.3. Any or all Software distributed under a given version of the Agreement may only be subsequently distributed under the same version of the Agreement, or a subsequent version, subject to the provisions of article 5.3.4.
-
-Article 13 - GOVERNING LAW AND JURISDICTION
-
-13.1. The Agreement is governed by French law. The Parties agree to endeavor to settle the disagreements or disputes that may arise during the performance of the Agreement out-of-court.
-
-13.2. In the absence of an out-of-court settlement within two (2) months as from their occurrence, and unless emergency proceedings are necessary, the disagreements or disputes shall be referred to the Paris Courts having jurisdiction, by the first Party to take action.
-
- Version 1.1 of 10/26/2004
diff --git a/options/license/CECILL-2.0 b/options/license/CECILL-2.0
deleted file mode 100644
index f5985c07da..0000000000
--- a/options/license/CECILL-2.0
+++ /dev/null
@@ -1,506 +0,0 @@
-
-CeCILL FREE SOFTWARE LICENSE AGREEMENT
-
-
- Notice
-
-This Agreement is a Free Software license agreement that is the result
-of discussions between its authors in order to ensure compliance with
-the two main principles guiding its drafting:
-
- * firstly, compliance with the principles governing the distribution
- of Free Software: access to source code, broad rights granted to
- users,
- * secondly, the election of a governing law, French law, with which
- it is conformant, both as regards the law of torts and
- intellectual property law, and the protection that it offers to
- both authors and holders of the economic rights over software.
-
-The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre])
-license are:
-
-Commissariat à l'Energie Atomique - CEA, a public scientific, technical
-and industrial research establishment, having its principal place of
-business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France.
-
-Centre National de la Recherche Scientifique - CNRS, a public scientific
-and technological establishment, having its principal place of business
-at 3 rue Michel-Ange, 75794 Paris cedex 16, France.
-
-Institut National de Recherche en Informatique et en Automatique -
-INRIA, a public scientific and technological establishment, having its
-principal place of business at Domaine de Voluceau, Rocquencourt, BP
-105, 78153 Le Chesnay cedex, France.
-
-
- Preamble
-
-The purpose of this Free Software license agreement is to grant users
-the right to modify and redistribute the software governed by this
-license within the framework of an open source distribution model.
-
-The exercising of these rights is conditional upon certain obligations
-for users so as to preserve this status for all subsequent redistributions.
-
-In consideration of access to the source code and the rights to copy,
-modify and redistribute granted by the license, users are provided only
-with a limited warranty and the software's author, the holder of the
-economic rights, and the successive licensors only have limited liability.
-
-In this respect, the risks associated with loading, using, modifying
-and/or developing or reproducing the software by the user are brought to
-the user's attention, given its Free Software status, which may make it
-complicated to use, with the result that its use is reserved for
-developers and experienced professionals having in-depth computer
-knowledge. Users are therefore encouraged to load and test the
-suitability of the software as regards their requirements in conditions
-enabling the security of their systems and/or data to be ensured and,
-more generally, to use and operate it in the same conditions of
-security. This Agreement may be freely reproduced and published,
-provided it is not altered, and that no provisions are either added or
-removed herefrom.
-
-This Agreement may apply to any or all software for which the holder of
-the economic rights decides to submit the use thereof to its provisions.
-
-
- Article 1 - DEFINITIONS
-
-For the purpose of this Agreement, when the following expressions
-commence with a capital letter, they shall have the following meaning:
-
-Agreement: means this license agreement, and its possible subsequent
-versions and annexes.
-
-Software: means the software in its Object Code and/or Source Code form
-and, where applicable, its documentation, "as is" when the Licensee
-accepts the Agreement.
-
-Initial Software: means the Software in its Source Code and possibly its
-Object Code form and, where applicable, its documentation, "as is" when
-it is first distributed under the terms and conditions of the Agreement.
-
-Modified Software: means the Software modified by at least one
-Contribution.
-
-Source Code: means all the Software's instructions and program lines to
-which access is required so as to modify the Software.
-
-Object Code: means the binary files originating from the compilation of
-the Source Code.
-
-Holder: means the holder(s) of the economic rights over the Initial
-Software.
-
-Licensee: means the Software user(s) having accepted the Agreement.
-
-Contributor: means a Licensee having made at least one Contribution.
-
-Licensor: means the Holder, or any other individual or legal entity, who
-distributes the Software under the Agreement.
-
-Contribution: means any or all modifications, corrections, translations,
-adaptations and/or new functions integrated into the Software by any or
-all Contributors, as well as any or all Internal Modules.
-
-Module: means a set of sources files including their documentation that
-enables supplementary functions or services in addition to those offered
-by the Software.
-
-External Module: means any or all Modules, not derived from the
-Software, so that this Module and the Software run in separate address
-spaces, with one calling the other when they are run.
-
-Internal Module: means any or all Module, connected to the Software so
-that they both execute in the same address space.
-
-GNU GPL: means the GNU General Public License version 2 or any
-subsequent version, as published by the Free Software Foundation Inc.
-
-Parties: mean both the Licensee and the Licensor.
-
-These expressions may be used both in singular and plural form.
-
-
- Article 2 - PURPOSE
-
-The purpose of the Agreement is the grant by the Licensor to the
-Licensee of a non-exclusive, transferable and worldwide license for the
-Software as set forth in Article 5 hereinafter for the whole term of the
-protection granted by the rights over said Software.
-
-
- Article 3 - ACCEPTANCE
-
-3.1 The Licensee shall be deemed as having accepted the terms and
-conditions of this Agreement upon the occurrence of the first of the
-following events:
-
- * (i) loading the Software by any or all means, notably, by
- downloading from a remote server, or by loading from a physical
- medium;
- * (ii) the first time the Licensee exercises any of the rights
- granted hereunder.
-
-3.2 One copy of the Agreement, containing a notice relating to the
-characteristics of the Software, to the limited warranty, and to the
-fact that its use is restricted to experienced users has been provided
-to the Licensee prior to its acceptance as set forth in Article 3.1
-hereinabove, and the Licensee hereby acknowledges that it has read and
-understood it.
-
-
- Article 4 - EFFECTIVE DATE AND TERM
-
-
- 4.1 EFFECTIVE DATE
-
-The Agreement shall become effective on the date when it is accepted by
-the Licensee as set forth in Article 3.1.
-
-
- 4.2 TERM
-
-The Agreement shall remain in force for the entire legal term of
-protection of the economic rights over the Software.
-
-
- Article 5 - SCOPE OF RIGHTS GRANTED
-
-The Licensor hereby grants to the Licensee, who accepts, the following
-rights over the Software for any or all use, and for the term of the
-Agreement, on the basis of the terms and conditions set forth hereinafter.
-
-Besides, if the Licensor owns or comes to own one or more patents
-protecting all or part of the functions of the Software or of its
-components, the Licensor undertakes not to enforce the rights granted by
-these patents against successive Licensees using, exploiting or
-modifying the Software. If these patents are transferred, the Licensor
-undertakes to have the transferees subscribe to the obligations set
-forth in this paragraph.
-
-
- 5.1 RIGHT OF USE
-
-The Licensee is authorized to use the Software, without any limitation
-as to its fields of application, with it being hereinafter specified
-that this comprises:
-
- 1. permanent or temporary reproduction of all or part of the Software
- by any or all means and in any or all form.
-
- 2. loading, displaying, running, or storing the Software on any or
- all medium.
-
- 3. entitlement to observe, study or test its operation so as to
- determine the ideas and principles behind any or all constituent
- elements of said Software. This shall apply when the Licensee
- carries out any or all loading, displaying, running, transmission
- or storage operation as regards the Software, that it is entitled
- to carry out hereunder.
-
-
- 5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS
-
-The right to make Contributions includes the right to translate, adapt,
-arrange, or make any or all modifications to the Software, and the right
-to reproduce the resulting software.
-
-The Licensee is authorized to make any or all Contributions to the
-Software provided that it includes an explicit notice that it is the
-author of said Contribution and indicates the date of the creation thereof.
-
-
- 5.3 RIGHT OF DISTRIBUTION
-
-In particular, the right of distribution includes the right to publish,
-transmit and communicate the Software to the general public on any or
-all medium, and by any or all means, and the right to market, either in
-consideration of a fee, or free of charge, one or more copies of the
-Software by any means.
-
-The Licensee is further authorized to distribute copies of the modified
-or unmodified Software to third parties according to the terms and
-conditions set forth hereinafter.
-
-
- 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION
-
-The Licensee is authorized to distribute true copies of the Software in
-Source Code or Object Code form, provided that said distribution
-complies with all the provisions of the Agreement and is accompanied by:
-
- 1. a copy of the Agreement,
-
- 2. a notice relating to the limitation of both the Licensor's
- warranty and liability as set forth in Articles 8 and 9,
-
-and that, in the event that only the Object Code of the Software is
-redistributed, the Licensee allows future Licensees unhindered access to
-the full Source Code of the Software by indicating how to access it, it
-being understood that the additional cost of acquiring the Source Code
-shall not exceed the cost of transferring the data.
-
-
- 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE
-
-When the Licensee makes a Contribution to the Software, the terms and
-conditions for the distribution of the resulting Modified Software
-become subject to all the provisions of this Agreement.
-
-The Licensee is authorized to distribute the Modified Software, in
-source code or object code form, provided that said distribution
-complies with all the provisions of the Agreement and is accompanied by:
-
- 1. a copy of the Agreement,
-
- 2. a notice relating to the limitation of both the Licensor's
- warranty and liability as set forth in Articles 8 and 9,
-
-and that, in the event that only the object code of the Modified
-Software is redistributed, the Licensee allows future Licensees
-unhindered access to the full source code of the Modified Software by
-indicating how to access it, it being understood that the additional
-cost of acquiring the source code shall not exceed the cost of
-transferring the data.
-
-
- 5.3.3 DISTRIBUTION OF EXTERNAL MODULES
-
-When the Licensee has developed an External Module, the terms and
-conditions of this Agreement do not apply to said External Module, that
-may be distributed under a separate license agreement.
-
-
- 5.3.4 COMPATIBILITY WITH THE GNU GPL
-
-The Licensee can include a code that is subject to the provisions of one
-of the versions of the GNU GPL in the Modified or unmodified Software,
-and distribute that entire code under the terms of the same version of
-the GNU GPL.
-
-The Licensee can include the Modified or unmodified Software in a code
-that is subject to the provisions of one of the versions of the GNU GPL,
-and distribute that entire code under the terms of the same version of
-the GNU GPL.
-
-
- Article 6 - INTELLECTUAL PROPERTY
-
-
- 6.1 OVER THE INITIAL SOFTWARE
-
-The Holder owns the economic rights over the Initial Software. Any or
-all use of the Initial Software is subject to compliance with the terms
-and conditions under which the Holder has elected to distribute its work
-and no one shall be entitled to modify the terms and conditions for the
-distribution of said Initial Software.
-
-The Holder undertakes that the Initial Software will remain ruled at
-least by this Agreement, for the duration set forth in Article 4.2.
-
-
- 6.2 OVER THE CONTRIBUTIONS
-
-The Licensee who develops a Contribution is the owner of the
-intellectual property rights over this Contribution as defined by
-applicable law.
-
-
- 6.3 OVER THE EXTERNAL MODULES
-
-The Licensee who develops an External Module is the owner of the
-intellectual property rights over this External Module as defined by
-applicable law and is free to choose the type of agreement that shall
-govern its distribution.
-
-
- 6.4 JOINT PROVISIONS
-
-The Licensee expressly undertakes:
-
- 1. not to remove, or modify, in any manner, the intellectual property
- notices attached to the Software;
-
- 2. to reproduce said notices, in an identical manner, in the copies
- of the Software modified or not.
-
-The Licensee undertakes not to directly or indirectly infringe the
-intellectual property rights of the Holder and/or Contributors on the
-Software and to take, where applicable, vis-à-vis its staff, any and all
-measures required to ensure respect of said intellectual property rights
-of the Holder and/or Contributors.
-
-
- Article 7 - RELATED SERVICES
-
-7.1 Under no circumstances shall the Agreement oblige the Licensor to
-provide technical assistance or maintenance services for the Software.
-
-However, the Licensor is entitled to offer this type of services. The
-terms and conditions of such technical assistance, and/or such
-maintenance, shall be set forth in a separate instrument. Only the
-Licensor offering said maintenance and/or technical assistance services
-shall incur liability therefor.
-
-7.2 Similarly, any Licensor is entitled to offer to its licensees, under
-its sole responsibility, a warranty, that shall only be binding upon
-itself, for the redistribution of the Software and/or the Modified
-Software, under terms and conditions that it is free to decide. Said
-warranty, and the financial terms and conditions of its application,
-shall be subject of a separate instrument executed between the Licensor
-and the Licensee.
-
-
- Article 8 - LIABILITY
-
-8.1 Subject to the provisions of Article 8.2, the Licensee shall be
-entitled to claim compensation for any direct loss it may have suffered
-from the Software as a result of a fault on the part of the relevant
-Licensor, subject to providing evidence thereof.
-
-8.2 The Licensor's liability is limited to the commitments made under
-this Agreement and shall not be incurred as a result of in particular:
-(i) loss due the Licensee's total or partial failure to fulfill its
-obligations, (ii) direct or consequential loss that is suffered by the
-Licensee due to the use or performance of the Software, and (iii) more
-generally, any consequential loss. In particular the Parties expressly
-agree that any or all pecuniary or business loss (i.e. loss of data,
-loss of profits, operating loss, loss of customers or orders,
-opportunity cost, any disturbance to business activities) or any or all
-legal proceedings instituted against the Licensee by a third party,
-shall constitute consequential loss and shall not provide entitlement to
-any or all compensation from the Licensor.
-
-
- Article 9 - WARRANTY
-
-9.1 The Licensee acknowledges that the scientific and technical
-state-of-the-art when the Software was distributed did not enable all
-possible uses to be tested and verified, nor for the presence of
-possible defects to be detected. In this respect, the Licensee's
-attention has been drawn to the risks associated with loading, using,
-modifying and/or developing and reproducing the Software which are
-reserved for experienced users.
-
-The Licensee shall be responsible for verifying, by any or all means,
-the suitability of the product for its requirements, its good working
-order, and for ensuring that it shall not cause damage to either persons
-or properties.
-
-9.2 The Licensor hereby represents, in good faith, that it is entitled
-to grant all the rights over the Software (including in particular the
-rights set forth in Article 5).
-
-9.3 The Licensee acknowledges that the Software is supplied "as is" by
-the Licensor without any other express or tacit warranty, other than
-that provided for in Article 9.2 and, in particular, without any warranty
-as to its commercial value, its secured, safe, innovative or relevant
-nature.
-
-Specifically, the Licensor does not warrant that the Software is free
-from any error, that it will operate without interruption, that it will
-be compatible with the Licensee's own equipment and software
-configuration, nor that it will meet the Licensee's requirements.
-
-9.4 The Licensor does not either expressly or tacitly warrant that the
-Software does not infringe any third party intellectual property right
-relating to a patent, software or any other property right. Therefore,
-the Licensor disclaims any and all liability towards the Licensee
-arising out of any or all proceedings for infringement that may be
-instituted in respect of the use, modification and redistribution of the
-Software. Nevertheless, should such proceedings be instituted against
-the Licensee, the Licensor shall provide it with technical and legal
-assistance for its defense. Such technical and legal assistance shall be
-decided on a case-by-case basis between the relevant Licensor and the
-Licensee pursuant to a memorandum of understanding. The Licensor
-disclaims any and all liability as regards the Licensee's use of the
-name of the Software. No warranty is given as regards the existence of
-prior rights over the name of the Software or as regards the existence
-of a trademark.
-
-
- Article 10 - TERMINATION
-
-10.1 In the event of a breach by the Licensee of its obligations
-hereunder, the Licensor may automatically terminate this Agreement
-thirty (30) days after notice has been sent to the Licensee and has
-remained ineffective.
-
-10.2 A Licensee whose Agreement is terminated shall no longer be
-authorized to use, modify or distribute the Software. However, any
-licenses that it may have granted prior to termination of the Agreement
-shall remain valid subject to their having been granted in compliance
-with the terms and conditions hereof.
-
-
- Article 11 - MISCELLANEOUS
-
-
- 11.1 EXCUSABLE EVENTS
-
-Neither Party shall be liable for any or all delay, or failure to
-perform the Agreement, that may be attributable to an event of force
-majeure, an act of God or an outside cause, such as defective
-functioning or interruptions of the electricity or telecommunications
-networks, network paralysis following a virus attack, intervention by
-government authorities, natural disasters, water damage, earthquakes,
-fire, explosions, strikes and labor unrest, war, etc.
-
-11.2 Any failure by either Party, on one or more occasions, to invoke
-one or more of the provisions hereof, shall under no circumstances be
-interpreted as being a waiver by the interested Party of its right to
-invoke said provision(s) subsequently.
-
-11.3 The Agreement cancels and replaces any or all previous agreements,
-whether written or oral, between the Parties and having the same
-purpose, and constitutes the entirety of the agreement between said
-Parties concerning said purpose. No supplement or modification to the
-terms and conditions hereof shall be effective as between the Parties
-unless it is made in writing and signed by their duly authorized
-representatives.
-
-11.4 In the event that one or more of the provisions hereof were to
-conflict with a current or future applicable act or legislative text,
-said act or legislative text shall prevail, and the Parties shall make
-the necessary amendments so as to comply with said act or legislative
-text. All other provisions shall remain effective. Similarly, invalidity
-of a provision of the Agreement, for any reason whatsoever, shall not
-cause the Agreement as a whole to be invalid.
-
-
- 11.5 LANGUAGE
-
-The Agreement is drafted in both French and English and both versions
-are deemed authentic.
-
-
- Article 12 - NEW VERSIONS OF THE AGREEMENT
-
-12.1 Any person is authorized to duplicate and distribute copies of this
-Agreement.
-
-12.2 So as to ensure coherence, the wording of this Agreement is
-protected and may only be modified by the authors of the License, who
-reserve the right to periodically publish updates or new versions of the
-Agreement, each with a separate number. These subsequent versions may
-address new issues encountered by Free Software.
-
-12.3 Any Software distributed under a given version of the Agreement may
-only be subsequently distributed under the same version of the Agreement
-or a subsequent version, subject to the provisions of Article 5.3.4.
-
-
- Article 13 - GOVERNING LAW AND JURISDICTION
-
-13.1 The Agreement is governed by French law. The Parties agree to
-endeavor to seek an amicable solution to any disagreements or disputes
-that may arise during the performance of the Agreement.
-
-13.2 Failing an amicable solution within two (2) months as from their
-occurrence, and unless emergency proceedings are necessary, the
-disagreements or disputes shall be referred to the Paris Courts having
-jurisdiction, by the more diligent Party.
-
-
-Version 2.0 dated 2006-09-05.
diff --git a/options/license/CECILL-2.1 b/options/license/CECILL-2.1
deleted file mode 100644
index d290c0694a..0000000000
--- a/options/license/CECILL-2.1
+++ /dev/null
@@ -1,518 +0,0 @@
-
- CeCILL FREE SOFTWARE LICENSE AGREEMENT
-
-Version 2.1 dated 2013-06-21
-
-
- Notice
-
-This Agreement is a Free Software license agreement that is the result
-of discussions between its authors in order to ensure compliance with
-the two main principles guiding its drafting:
-
- * firstly, compliance with the principles governing the distribution
- of Free Software: access to source code, broad rights granted to users,
- * secondly, the election of a governing law, French law, with which it
- is conformant, both as regards the law of torts and intellectual
- property law, and the protection that it offers to both authors and
- holders of the economic rights over software.
-
-The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre])
-license are:
-
-Commissariat à l'énergie atomique et aux énergies alternatives - CEA, a
-public scientific, technical and industrial research establishment,
-having its principal place of business at 25 rue Leblanc, immeuble Le
-Ponant D, 75015 Paris, France.
-
-Centre National de la Recherche Scientifique - CNRS, a public scientific
-and technological establishment, having its principal place of business
-at 3 rue Michel-Ange, 75794 Paris cedex 16, France.
-
-Institut National de Recherche en Informatique et en Automatique -
-Inria, a public scientific and technological establishment, having its
-principal place of business at Domaine de Voluceau, Rocquencourt, BP
-105, 78153 Le Chesnay cedex, France.
-
-
- Preamble
-
-The purpose of this Free Software license agreement is to grant users
-the right to modify and redistribute the software governed by this
-license within the framework of an open source distribution model.
-
-The exercising of this right is conditional upon certain obligations for
-users so as to preserve this status for all subsequent redistributions.
-
-In consideration of access to the source code and the rights to copy,
-modify and redistribute granted by the license, users are provided only
-with a limited warranty and the software's author, the holder of the
-economic rights, and the successive licensors only have limited liability.
-
-In this respect, the risks associated with loading, using, modifying
-and/or developing or reproducing the software by the user are brought to
-the user's attention, given its Free Software status, which may make it
-complicated to use, with the result that its use is reserved for
-developers and experienced professionals having in-depth computer
-knowledge. Users are therefore encouraged to load and test the
-suitability of the software as regards their requirements in conditions
-enabling the security of their systems and/or data to be ensured and,
-more generally, to use and operate it in the same conditions of
-security. This Agreement may be freely reproduced and published,
-provided it is not altered, and that no provisions are either added or
-removed herefrom.
-
-This Agreement may apply to any or all software for which the holder of
-the economic rights decides to submit the use thereof to its provisions.
-
-Frequently asked questions can be found on the official website of the
-CeCILL licenses family (http://www.cecill.info/index.en.html) for any
-necessary clarification.
-
-
- Article 1 - DEFINITIONS
-
-For the purpose of this Agreement, when the following expressions
-commence with a capital letter, they shall have the following meaning:
-
-Agreement: means this license agreement, and its possible subsequent
-versions and annexes.
-
-Software: means the software in its Object Code and/or Source Code form
-and, where applicable, its documentation, "as is" when the Licensee
-accepts the Agreement.
-
-Initial Software: means the Software in its Source Code and possibly its
-Object Code form and, where applicable, its documentation, "as is" when
-it is first distributed under the terms and conditions of the Agreement.
-
-Modified Software: means the Software modified by at least one
-Contribution.
-
-Source Code: means all the Software's instructions and program lines to
-which access is required so as to modify the Software.
-
-Object Code: means the binary files originating from the compilation of
-the Source Code.
-
-Holder: means the holder(s) of the economic rights over the Initial
-Software.
-
-Licensee: means the Software user(s) having accepted the Agreement.
-
-Contributor: means a Licensee having made at least one Contribution.
-
-Licensor: means the Holder, or any other individual or legal entity, who
-distributes the Software under the Agreement.
-
-Contribution: means any or all modifications, corrections, translations,
-adaptations and/or new functions integrated into the Software by any or
-all Contributors, as well as any or all Internal Modules.
-
-Module: means a set of sources files including their documentation that
-enables supplementary functions or services in addition to those offered
-by the Software.
-
-External Module: means any or all Modules, not derived from the
-Software, so that this Module and the Software run in separate address
-spaces, with one calling the other when they are run.
-
-Internal Module: means any or all Module, connected to the Software so
-that they both execute in the same address space.
-
-GNU GPL: means the GNU General Public License version 2 or any
-subsequent version, as published by the Free Software Foundation Inc.
-
-GNU Affero GPL: means the GNU Affero General Public License version 3 or
-any subsequent version, as published by the Free Software Foundation Inc.
-
-EUPL: means the European Union Public License version 1.1 or any
-subsequent version, as published by the European Commission.
-
-Parties: mean both the Licensee and the Licensor.
-
-These expressions may be used both in singular and plural form.
-
-
- Article 2 - PURPOSE
-
-The purpose of the Agreement is the grant by the Licensor to the
-Licensee of a non-exclusive, transferable and worldwide license for the
-Software as set forth in Article 5 <#scope> hereinafter for the whole
-term of the protection granted by the rights over said Software.
-
-
- Article 3 - ACCEPTANCE
-
-3.1 The Licensee shall be deemed as having accepted the terms and
-conditions of this Agreement upon the occurrence of the first of the
-following events:
-
- * (i) loading the Software by any or all means, notably, by
- downloading from a remote server, or by loading from a physical medium;
- * (ii) the first time the Licensee exercises any of the rights granted
- hereunder.
-
-3.2 One copy of the Agreement, containing a notice relating to the
-characteristics of the Software, to the limited warranty, and to the
-fact that its use is restricted to experienced users has been provided
-to the Licensee prior to its acceptance as set forth in Article 3.1
-<#accepting> hereinabove, and the Licensee hereby acknowledges that it
-has read and understood it.
-
-
- Article 4 - EFFECTIVE DATE AND TERM
-
-
- 4.1 EFFECTIVE DATE
-
-The Agreement shall become effective on the date when it is accepted by
-the Licensee as set forth in Article 3.1 <#accepting>.
-
-
- 4.2 TERM
-
-The Agreement shall remain in force for the entire legal term of
-protection of the economic rights over the Software.
-
-
- Article 5 - SCOPE OF RIGHTS GRANTED
-
-The Licensor hereby grants to the Licensee, who accepts, the following
-rights over the Software for any or all use, and for the term of the
-Agreement, on the basis of the terms and conditions set forth hereinafter.
-
-Besides, if the Licensor owns or comes to own one or more patents
-protecting all or part of the functions of the Software or of its
-components, the Licensor undertakes not to enforce the rights granted by
-these patents against successive Licensees using, exploiting or
-modifying the Software. If these patents are transferred, the Licensor
-undertakes to have the transferees subscribe to the obligations set
-forth in this paragraph.
-
-
- 5.1 RIGHT OF USE
-
-The Licensee is authorized to use the Software, without any limitation
-as to its fields of application, with it being hereinafter specified
-that this comprises:
-
- 1. permanent or temporary reproduction of all or part of the Software
- by any or all means and in any or all form.
-
- 2. loading, displaying, running, or storing the Software on any or all
- medium.
-
- 3. entitlement to observe, study or test its operation so as to
- determine the ideas and principles behind any or all constituent
- elements of said Software. This shall apply when the Licensee
- carries out any or all loading, displaying, running, transmission or
- storage operation as regards the Software, that it is entitled to
- carry out hereunder.
-
-
- 5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS
-
-The right to make Contributions includes the right to translate, adapt,
-arrange, or make any or all modifications to the Software, and the right
-to reproduce the resulting software.
-
-The Licensee is authorized to make any or all Contributions to the
-Software provided that it includes an explicit notice that it is the
-author of said Contribution and indicates the date of the creation thereof.
-
-
- 5.3 RIGHT OF DISTRIBUTION
-
-In particular, the right of distribution includes the right to publish,
-transmit and communicate the Software to the general public on any or
-all medium, and by any or all means, and the right to market, either in
-consideration of a fee, or free of charge, one or more copies of the
-Software by any means.
-
-The Licensee is further authorized to distribute copies of the modified
-or unmodified Software to third parties according to the terms and
-conditions set forth hereinafter.
-
-
- 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION
-
-The Licensee is authorized to distribute true copies of the Software in
-Source Code or Object Code form, provided that said distribution
-complies with all the provisions of the Agreement and is accompanied by:
-
- 1. a copy of the Agreement,
-
- 2. a notice relating to the limitation of both the Licensor's warranty
- and liability as set forth in Articles 8 and 9,
-
-and that, in the event that only the Object Code of the Software is
-redistributed, the Licensee allows effective access to the full Source
-Code of the Software for a period of at least three years from the
-distribution of the Software, it being understood that the additional
-acquisition cost of the Source Code shall not exceed the cost of the
-data transfer.
-
-
- 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE
-
-When the Licensee makes a Contribution to the Software, the terms and
-conditions for the distribution of the resulting Modified Software
-become subject to all the provisions of this Agreement.
-
-The Licensee is authorized to distribute the Modified Software, in
-source code or object code form, provided that said distribution
-complies with all the provisions of the Agreement and is accompanied by:
-
- 1. a copy of the Agreement,
-
- 2. a notice relating to the limitation of both the Licensor's warranty
- and liability as set forth in Articles 8 and 9,
-
-and, in the event that only the object code of the Modified Software is
-redistributed,
-
- 3. a note stating the conditions of effective access to the full source
- code of the Modified Software for a period of at least three years
- from the distribution of the Modified Software, it being understood
- that the additional acquisition cost of the source code shall not
- exceed the cost of the data transfer.
-
-
- 5.3.3 DISTRIBUTION OF EXTERNAL MODULES
-
-When the Licensee has developed an External Module, the terms and
-conditions of this Agreement do not apply to said External Module, that
-may be distributed under a separate license agreement.
-
-
- 5.3.4 COMPATIBILITY WITH OTHER LICENSES
-
-The Licensee can include a code that is subject to the provisions of one
-of the versions of the GNU GPL, GNU Affero GPL and/or EUPL in the
-Modified or unmodified Software, and distribute that entire code under
-the terms of the same version of the GNU GPL, GNU Affero GPL and/or EUPL.
-
-The Licensee can include the Modified or unmodified Software in a code
-that is subject to the provisions of one of the versions of the GNU GPL,
-GNU Affero GPL and/or EUPL and distribute that entire code under the
-terms of the same version of the GNU GPL, GNU Affero GPL and/or EUPL.
-
-
- Article 6 - INTELLECTUAL PROPERTY
-
-
- 6.1 OVER THE INITIAL SOFTWARE
-
-The Holder owns the economic rights over the Initial Software. Any or
-all use of the Initial Software is subject to compliance with the terms
-and conditions under which the Holder has elected to distribute its work
-and no one shall be entitled to modify the terms and conditions for the
-distribution of said Initial Software.
-
-The Holder undertakes that the Initial Software will remain ruled at
-least by this Agreement, for the duration set forth in Article 4.2 <#term>.
-
-
- 6.2 OVER THE CONTRIBUTIONS
-
-The Licensee who develops a Contribution is the owner of the
-intellectual property rights over this Contribution as defined by
-applicable law.
-
-
- 6.3 OVER THE EXTERNAL MODULES
-
-The Licensee who develops an External Module is the owner of the
-intellectual property rights over this External Module as defined by
-applicable law and is free to choose the type of agreement that shall
-govern its distribution.
-
-
- 6.4 JOINT PROVISIONS
-
-The Licensee expressly undertakes:
-
- 1. not to remove, or modify, in any manner, the intellectual property
- notices attached to the Software;
-
- 2. to reproduce said notices, in an identical manner, in the copies of
- the Software modified or not.
-
-The Licensee undertakes not to directly or indirectly infringe the
-intellectual property rights on the Software of the Holder and/or
-Contributors, and to take, where applicable, vis-à-vis its staff, any
-and all measures required to ensure respect of said intellectual
-property rights of the Holder and/or Contributors.
-
-
- Article 7 - RELATED SERVICES
-
-7.1 Under no circumstances shall the Agreement oblige the Licensor to
-provide technical assistance or maintenance services for the Software.
-
-However, the Licensor is entitled to offer this type of services. The
-terms and conditions of such technical assistance, and/or such
-maintenance, shall be set forth in a separate instrument. Only the
-Licensor offering said maintenance and/or technical assistance services
-shall incur liability therefor.
-
-7.2 Similarly, any Licensor is entitled to offer to its licensees, under
-its sole responsibility, a warranty, that shall only be binding upon
-itself, for the redistribution of the Software and/or the Modified
-Software, under terms and conditions that it is free to decide. Said
-warranty, and the financial terms and conditions of its application,
-shall be subject of a separate instrument executed between the Licensor
-and the Licensee.
-
-
- Article 8 - LIABILITY
-
-8.1 Subject to the provisions of Article 8.2, the Licensee shall be
-entitled to claim compensation for any direct loss it may have suffered
-from the Software as a result of a fault on the part of the relevant
-Licensor, subject to providing evidence thereof.
-
-8.2 The Licensor's liability is limited to the commitments made under
-this Agreement and shall not be incurred as a result of in particular:
-(i) loss due the Licensee's total or partial failure to fulfill its
-obligations, (ii) direct or consequential loss that is suffered by the
-Licensee due to the use or performance of the Software, and (iii) more
-generally, any consequential loss. In particular the Parties expressly
-agree that any or all pecuniary or business loss (i.e. loss of data,
-loss of profits, operating loss, loss of customers or orders,
-opportunity cost, any disturbance to business activities) or any or all
-legal proceedings instituted against the Licensee by a third party,
-shall constitute consequential loss and shall not provide entitlement to
-any or all compensation from the Licensor.
-
-
- Article 9 - WARRANTY
-
-9.1 The Licensee acknowledges that the scientific and technical
-state-of-the-art when the Software was distributed did not enable all
-possible uses to be tested and verified, nor for the presence of
-possible defects to be detected. In this respect, the Licensee's
-attention has been drawn to the risks associated with loading, using,
-modifying and/or developing and reproducing the Software which are
-reserved for experienced users.
-
-The Licensee shall be responsible for verifying, by any or all means,
-the suitability of the product for its requirements, its good working
-order, and for ensuring that it shall not cause damage to either persons
-or properties.
-
-9.2 The Licensor hereby represents, in good faith, that it is entitled
-to grant all the rights over the Software (including in particular the
-rights set forth in Article 5 <#scope>).
-
-9.3 The Licensee acknowledges that the Software is supplied "as is" by
-the Licensor without any other express or tacit warranty, other than
-that provided for in Article 9.2 <#good-faith> and, in particular,
-without any warranty as to its commercial value, its secured, safe,
-innovative or relevant nature.
-
-Specifically, the Licensor does not warrant that the Software is free
-from any error, that it will operate without interruption, that it will
-be compatible with the Licensee's own equipment and software
-configuration, nor that it will meet the Licensee's requirements.
-
-9.4 The Licensor does not either expressly or tacitly warrant that the
-Software does not infringe any third party intellectual property right
-relating to a patent, software or any other property right. Therefore,
-the Licensor disclaims any and all liability towards the Licensee
-arising out of any or all proceedings for infringement that may be
-instituted in respect of the use, modification and redistribution of the
-Software. Nevertheless, should such proceedings be instituted against
-the Licensee, the Licensor shall provide it with technical and legal
-expertise for its defense. Such technical and legal expertise shall be
-decided on a case-by-case basis between the relevant Licensor and the
-Licensee pursuant to a memorandum of understanding. The Licensor
-disclaims any and all liability as regards the Licensee's use of the
-name of the Software. No warranty is given as regards the existence of
-prior rights over the name of the Software or as regards the existence
-of a trademark.
-
-
- Article 10 - TERMINATION
-
-10.1 In the event of a breach by the Licensee of its obligations
-hereunder, the Licensor may automatically terminate this Agreement
-thirty (30) days after notice has been sent to the Licensee and has
-remained ineffective.
-
-10.2 A Licensee whose Agreement is terminated shall no longer be
-authorized to use, modify or distribute the Software. However, any
-licenses that it may have granted prior to termination of the Agreement
-shall remain valid subject to their having been granted in compliance
-with the terms and conditions hereof.
-
-
- Article 11 - MISCELLANEOUS
-
-
- 11.1 EXCUSABLE EVENTS
-
-Neither Party shall be liable for any or all delay, or failure to
-perform the Agreement, that may be attributable to an event of force
-majeure, an act of God or an outside cause, such as defective
-functioning or interruptions of the electricity or telecommunications
-networks, network paralysis following a virus attack, intervention by
-government authorities, natural disasters, water damage, earthquakes,
-fire, explosions, strikes and labor unrest, war, etc.
-
-11.2 Any failure by either Party, on one or more occasions, to invoke
-one or more of the provisions hereof, shall under no circumstances be
-interpreted as being a waiver by the interested Party of its right to
-invoke said provision(s) subsequently.
-
-11.3 The Agreement cancels and replaces any or all previous agreements,
-whether written or oral, between the Parties and having the same
-purpose, and constitutes the entirety of the agreement between said
-Parties concerning said purpose. No supplement or modification to the
-terms and conditions hereof shall be effective as between the Parties
-unless it is made in writing and signed by their duly authorized
-representatives.
-
-11.4 In the event that one or more of the provisions hereof were to
-conflict with a current or future applicable act or legislative text,
-said act or legislative text shall prevail, and the Parties shall make
-the necessary amendments so as to comply with said act or legislative
-text. All other provisions shall remain effective. Similarly, invalidity
-of a provision of the Agreement, for any reason whatsoever, shall not
-cause the Agreement as a whole to be invalid.
-
-
- 11.5 LANGUAGE
-
-The Agreement is drafted in both French and English and both versions
-are deemed authentic.
-
-
- Article 12 - NEW VERSIONS OF THE AGREEMENT
-
-12.1 Any person is authorized to duplicate and distribute copies of this
-Agreement.
-
-12.2 So as to ensure coherence, the wording of this Agreement is
-protected and may only be modified by the authors of the License, who
-reserve the right to periodically publish updates or new versions of the
-Agreement, each with a separate number. These subsequent versions may
-address new issues encountered by Free Software.
-
-12.3 Any Software distributed under a given version of the Agreement may
-only be subsequently distributed under the same version of the Agreement
-or a subsequent version, subject to the provisions of Article 5.3.4
-<#compatibility>.
-
-
- Article 13 - GOVERNING LAW AND JURISDICTION
-
-13.1 The Agreement is governed by French law. The Parties agree to
-endeavor to seek an amicable solution to any disagreements or disputes
-that may arise during the performance of the Agreement.
-
-13.2 Failing an amicable solution within two (2) months as from their
-occurrence, and unless emergency proceedings are necessary, the
-disagreements or disputes shall be referred to the Paris Courts having
-jurisdiction, by the more diligent Party.
diff --git a/options/license/CECILL-B b/options/license/CECILL-B
deleted file mode 100644
index c02c70e620..0000000000
--- a/options/license/CECILL-B
+++ /dev/null
@@ -1,515 +0,0 @@
-
-CeCILL-B FREE SOFTWARE LICENSE AGREEMENT
-
-
- Notice
-
-This Agreement is a Free Software license agreement that is the result
-of discussions between its authors in order to ensure compliance with
-the two main principles guiding its drafting:
-
- * firstly, compliance with the principles governing the distribution
- of Free Software: access to source code, broad rights granted to
- users,
- * secondly, the election of a governing law, French law, with which
- it is conformant, both as regards the law of torts and
- intellectual property law, and the protection that it offers to
- both authors and holders of the economic rights over software.
-
-The authors of the CeCILL-B (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre])
-license are:
-
-Commissariat à l'Energie Atomique - CEA, a public scientific, technical
-and industrial research establishment, having its principal place of
-business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France.
-
-Centre National de la Recherche Scientifique - CNRS, a public scientific
-and technological establishment, having its principal place of business
-at 3 rue Michel-Ange, 75794 Paris cedex 16, France.
-
-Institut National de Recherche en Informatique et en Automatique -
-INRIA, a public scientific and technological establishment, having its
-principal place of business at Domaine de Voluceau, Rocquencourt, BP
-105, 78153 Le Chesnay cedex, France.
-
-
- Preamble
-
-This Agreement is an open source software license intended to give users
-significant freedom to modify and redistribute the software licensed
-hereunder.
-
-The exercising of this freedom is conditional upon a strong obligation
-of giving credits for everybody that distributes a software
-incorporating a software ruled by the current license so as all
-contributions to be properly identified and acknowledged.
-
-In consideration of access to the source code and the rights to copy,
-modify and redistribute granted by the license, users are provided only
-with a limited warranty and the software's author, the holder of the
-economic rights, and the successive licensors only have limited liability.
-
-In this respect, the risks associated with loading, using, modifying
-and/or developing or reproducing the software by the user are brought to
-the user's attention, given its Free Software status, which may make it
-complicated to use, with the result that its use is reserved for
-developers and experienced professionals having in-depth computer
-knowledge. Users are therefore encouraged to load and test the
-suitability of the software as regards their requirements in conditions
-enabling the security of their systems and/or data to be ensured and,
-more generally, to use and operate it in the same conditions of
-security. This Agreement may be freely reproduced and published,
-provided it is not altered, and that no provisions are either added or
-removed herefrom.
-
-This Agreement may apply to any or all software for which the holder of
-the economic rights decides to submit the use thereof to its provisions.
-
-
- Article 1 - DEFINITIONS
-
-For the purpose of this Agreement, when the following expressions
-commence with a capital letter, they shall have the following meaning:
-
-Agreement: means this license agreement, and its possible subsequent
-versions and annexes.
-
-Software: means the software in its Object Code and/or Source Code form
-and, where applicable, its documentation, "as is" when the Licensee
-accepts the Agreement.
-
-Initial Software: means the Software in its Source Code and possibly its
-Object Code form and, where applicable, its documentation, "as is" when
-it is first distributed under the terms and conditions of the Agreement.
-
-Modified Software: means the Software modified by at least one
-Contribution.
-
-Source Code: means all the Software's instructions and program lines to
-which access is required so as to modify the Software.
-
-Object Code: means the binary files originating from the compilation of
-the Source Code.
-
-Holder: means the holder(s) of the economic rights over the Initial
-Software.
-
-Licensee: means the Software user(s) having accepted the Agreement.
-
-Contributor: means a Licensee having made at least one Contribution.
-
-Licensor: means the Holder, or any other individual or legal entity, who
-distributes the Software under the Agreement.
-
-Contribution: means any or all modifications, corrections, translations,
-adaptations and/or new functions integrated into the Software by any or
-all Contributors, as well as any or all Internal Modules.
-
-Module: means a set of sources files including their documentation that
-enables supplementary functions or services in addition to those offered
-by the Software.
-
-External Module: means any or all Modules, not derived from the
-Software, so that this Module and the Software run in separate address
-spaces, with one calling the other when they are run.
-
-Internal Module: means any or all Module, connected to the Software so
-that they both execute in the same address space.
-
-Parties: mean both the Licensee and the Licensor.
-
-These expressions may be used both in singular and plural form.
-
-
- Article 2 - PURPOSE
-
-The purpose of the Agreement is the grant by the Licensor to the
-Licensee of a non-exclusive, transferable and worldwide license for the
-Software as set forth in Article 5 hereinafter for the whole term of the
-protection granted by the rights over said Software.
-
-
- Article 3 - ACCEPTANCE
-
-3.1 The Licensee shall be deemed as having accepted the terms and
-conditions of this Agreement upon the occurrence of the first of the
-following events:
-
- * (i) loading the Software by any or all means, notably, by
- downloading from a remote server, or by loading from a physical
- medium;
- * (ii) the first time the Licensee exercises any of the rights
- granted hereunder.
-
-3.2 One copy of the Agreement, containing a notice relating to the
-characteristics of the Software, to the limited warranty, and to the
-fact that its use is restricted to experienced users has been provided
-to the Licensee prior to its acceptance as set forth in Article 3.1
-hereinabove, and the Licensee hereby acknowledges that it has read and
-understood it.
-
-
- Article 4 - EFFECTIVE DATE AND TERM
-
-
- 4.1 EFFECTIVE DATE
-
-The Agreement shall become effective on the date when it is accepted by
-the Licensee as set forth in Article 3.1.
-
-
- 4.2 TERM
-
-The Agreement shall remain in force for the entire legal term of
-protection of the economic rights over the Software.
-
-
- Article 5 - SCOPE OF RIGHTS GRANTED
-
-The Licensor hereby grants to the Licensee, who accepts, the following
-rights over the Software for any or all use, and for the term of the
-Agreement, on the basis of the terms and conditions set forth hereinafter.
-
-Besides, if the Licensor owns or comes to own one or more patents
-protecting all or part of the functions of the Software or of its
-components, the Licensor undertakes not to enforce the rights granted by
-these patents against successive Licensees using, exploiting or
-modifying the Software. If these patents are transferred, the Licensor
-undertakes to have the transferees subscribe to the obligations set
-forth in this paragraph.
-
-
- 5.1 RIGHT OF USE
-
-The Licensee is authorized to use the Software, without any limitation
-as to its fields of application, with it being hereinafter specified
-that this comprises:
-
- 1. permanent or temporary reproduction of all or part of the Software
- by any or all means and in any or all form.
-
- 2. loading, displaying, running, or storing the Software on any or
- all medium.
-
- 3. entitlement to observe, study or test its operation so as to
- determine the ideas and principles behind any or all constituent
- elements of said Software. This shall apply when the Licensee
- carries out any or all loading, displaying, running, transmission
- or storage operation as regards the Software, that it is entitled
- to carry out hereunder.
-
-
- 5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS
-
-The right to make Contributions includes the right to translate, adapt,
-arrange, or make any or all modifications to the Software, and the right
-to reproduce the resulting software.
-
-The Licensee is authorized to make any or all Contributions to the
-Software provided that it includes an explicit notice that it is the
-author of said Contribution and indicates the date of the creation thereof.
-
-
- 5.3 RIGHT OF DISTRIBUTION
-
-In particular, the right of distribution includes the right to publish,
-transmit and communicate the Software to the general public on any or
-all medium, and by any or all means, and the right to market, either in
-consideration of a fee, or free of charge, one or more copies of the
-Software by any means.
-
-The Licensee is further authorized to distribute copies of the modified
-or unmodified Software to third parties according to the terms and
-conditions set forth hereinafter.
-
-
- 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION
-
-The Licensee is authorized to distribute true copies of the Software in
-Source Code or Object Code form, provided that said distribution
-complies with all the provisions of the Agreement and is accompanied by:
-
- 1. a copy of the Agreement,
-
- 2. a notice relating to the limitation of both the Licensor's
- warranty and liability as set forth in Articles 8 and 9,
-
-and that, in the event that only the Object Code of the Software is
-redistributed, the Licensee allows effective access to the full Source
-Code of the Software at a minimum during the entire period of its
-distribution of the Software, it being understood that the additional
-cost of acquiring the Source Code shall not exceed the cost of
-transferring the data.
-
-
- 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE
-
-If the Licensee makes any Contribution to the Software, the resulting
-Modified Software may be distributed under a license agreement other
-than this Agreement subject to compliance with the provisions of Article
-5.3.4.
-
-
- 5.3.3 DISTRIBUTION OF EXTERNAL MODULES
-
-When the Licensee has developed an External Module, the terms and
-conditions of this Agreement do not apply to said External Module, that
-may be distributed under a separate license agreement.
-
-
- 5.3.4 CREDITS
-
-Any Licensee who may distribute a Modified Software hereby expressly
-agrees to:
-
- 1. indicate in the related documentation that it is based on the
- Software licensed hereunder, and reproduce the intellectual
- property notice for the Software,
-
- 2. ensure that written indications of the Software intended use,
- intellectual property notice and license hereunder are included in
- easily accessible format from the Modified Software interface,
-
- 3. mention, on a freely accessible website describing the Modified
- Software, at least throughout the distribution term thereof, that
- it is based on the Software licensed hereunder, and reproduce the
- Software intellectual property notice,
-
- 4. where it is distributed to a third party that may distribute a
- Modified Software without having to make its source code
- available, make its best efforts to ensure that said third party
- agrees to comply with the obligations set forth in this Article .
-
-If the Software, whether or not modified, is distributed with an
-External Module designed for use in connection with the Software, the
-Licensee shall submit said External Module to the foregoing obligations.
-
-
- 5.3.5 COMPATIBILITY WITH THE CeCILL AND CeCILL-C LICENSES
-
-Where a Modified Software contains a Contribution subject to the CeCILL
-license, the provisions set forth in Article 5.3.4 shall be optional.
-
-A Modified Software may be distributed under the CeCILL-C license. In
-such a case the provisions set forth in Article 5.3.4 shall be optional.
-
-
- Article 6 - INTELLECTUAL PROPERTY
-
-
- 6.1 OVER THE INITIAL SOFTWARE
-
-The Holder owns the economic rights over the Initial Software. Any or
-all use of the Initial Software is subject to compliance with the terms
-and conditions under which the Holder has elected to distribute its work
-and no one shall be entitled to modify the terms and conditions for the
-distribution of said Initial Software.
-
-The Holder undertakes that the Initial Software will remain ruled at
-least by this Agreement, for the duration set forth in Article 4.2.
-
-
- 6.2 OVER THE CONTRIBUTIONS
-
-The Licensee who develops a Contribution is the owner of the
-intellectual property rights over this Contribution as defined by
-applicable law.
-
-
- 6.3 OVER THE EXTERNAL MODULES
-
-The Licensee who develops an External Module is the owner of the
-intellectual property rights over this External Module as defined by
-applicable law and is free to choose the type of agreement that shall
-govern its distribution.
-
-
- 6.4 JOINT PROVISIONS
-
-The Licensee expressly undertakes:
-
- 1. not to remove, or modify, in any manner, the intellectual property
- notices attached to the Software;
-
- 2. to reproduce said notices, in an identical manner, in the copies
- of the Software modified or not.
-
-The Licensee undertakes not to directly or indirectly infringe the
-intellectual property rights of the Holder and/or Contributors on the
-Software and to take, where applicable, vis-à-vis its staff, any and all
-measures required to ensure respect of said intellectual property rights
-of the Holder and/or Contributors.
-
-
- Article 7 - RELATED SERVICES
-
-7.1 Under no circumstances shall the Agreement oblige the Licensor to
-provide technical assistance or maintenance services for the Software.
-
-However, the Licensor is entitled to offer this type of services. The
-terms and conditions of such technical assistance, and/or such
-maintenance, shall be set forth in a separate instrument. Only the
-Licensor offering said maintenance and/or technical assistance services
-shall incur liability therefor.
-
-7.2 Similarly, any Licensor is entitled to offer to its licensees, under
-its sole responsibility, a warranty, that shall only be binding upon
-itself, for the redistribution of the Software and/or the Modified
-Software, under terms and conditions that it is free to decide. Said
-warranty, and the financial terms and conditions of its application,
-shall be subject of a separate instrument executed between the Licensor
-and the Licensee.
-
-
- Article 8 - LIABILITY
-
-8.1 Subject to the provisions of Article 8.2, the Licensee shall be
-entitled to claim compensation for any direct loss it may have suffered
-from the Software as a result of a fault on the part of the relevant
-Licensor, subject to providing evidence thereof.
-
-8.2 The Licensor's liability is limited to the commitments made under
-this Agreement and shall not be incurred as a result of in particular:
-(i) loss due the Licensee's total or partial failure to fulfill its
-obligations, (ii) direct or consequential loss that is suffered by the
-Licensee due to the use or performance of the Software, and (iii) more
-generally, any consequential loss. In particular the Parties expressly
-agree that any or all pecuniary or business loss (i.e. loss of data,
-loss of profits, operating loss, loss of customers or orders,
-opportunity cost, any disturbance to business activities) or any or all
-legal proceedings instituted against the Licensee by a third party,
-shall constitute consequential loss and shall not provide entitlement to
-any or all compensation from the Licensor.
-
-
- Article 9 - WARRANTY
-
-9.1 The Licensee acknowledges that the scientific and technical
-state-of-the-art when the Software was distributed did not enable all
-possible uses to be tested and verified, nor for the presence of
-possible defects to be detected. In this respect, the Licensee's
-attention has been drawn to the risks associated with loading, using,
-modifying and/or developing and reproducing the Software which are
-reserved for experienced users.
-
-The Licensee shall be responsible for verifying, by any or all means,
-the suitability of the product for its requirements, its good working
-order, and for ensuring that it shall not cause damage to either persons
-or properties.
-
-9.2 The Licensor hereby represents, in good faith, that it is entitled
-to grant all the rights over the Software (including in particular the
-rights set forth in Article 5).
-
-9.3 The Licensee acknowledges that the Software is supplied "as is" by
-the Licensor without any other express or tacit warranty, other than
-that provided for in Article 9.2 and, in particular, without any warranty
-as to its commercial value, its secured, safe, innovative or relevant
-nature.
-
-Specifically, the Licensor does not warrant that the Software is free
-from any error, that it will operate without interruption, that it will
-be compatible with the Licensee's own equipment and software
-configuration, nor that it will meet the Licensee's requirements.
-
-9.4 The Licensor does not either expressly or tacitly warrant that the
-Software does not infringe any third party intellectual property right
-relating to a patent, software or any other property right. Therefore,
-the Licensor disclaims any and all liability towards the Licensee
-arising out of any or all proceedings for infringement that may be
-instituted in respect of the use, modification and redistribution of the
-Software. Nevertheless, should such proceedings be instituted against
-the Licensee, the Licensor shall provide it with technical and legal
-assistance for its defense. Such technical and legal assistance shall be
-decided on a case-by-case basis between the relevant Licensor and the
-Licensee pursuant to a memorandum of understanding. The Licensor
-disclaims any and all liability as regards the Licensee's use of the
-name of the Software. No warranty is given as regards the existence of
-prior rights over the name of the Software or as regards the existence
-of a trademark.
-
-
- Article 10 - TERMINATION
-
-10.1 In the event of a breach by the Licensee of its obligations
-hereunder, the Licensor may automatically terminate this Agreement
-thirty (30) days after notice has been sent to the Licensee and has
-remained ineffective.
-
-10.2 A Licensee whose Agreement is terminated shall no longer be
-authorized to use, modify or distribute the Software. However, any
-licenses that it may have granted prior to termination of the Agreement
-shall remain valid subject to their having been granted in compliance
-with the terms and conditions hereof.
-
-
- Article 11 - MISCELLANEOUS
-
-
- 11.1 EXCUSABLE EVENTS
-
-Neither Party shall be liable for any or all delay, or failure to
-perform the Agreement, that may be attributable to an event of force
-majeure, an act of God or an outside cause, such as defective
-functioning or interruptions of the electricity or telecommunications
-networks, network paralysis following a virus attack, intervention by
-government authorities, natural disasters, water damage, earthquakes,
-fire, explosions, strikes and labor unrest, war, etc.
-
-11.2 Any failure by either Party, on one or more occasions, to invoke
-one or more of the provisions hereof, shall under no circumstances be
-interpreted as being a waiver by the interested Party of its right to
-invoke said provision(s) subsequently.
-
-11.3 The Agreement cancels and replaces any or all previous agreements,
-whether written or oral, between the Parties and having the same
-purpose, and constitutes the entirety of the agreement between said
-Parties concerning said purpose. No supplement or modification to the
-terms and conditions hereof shall be effective as between the Parties
-unless it is made in writing and signed by their duly authorized
-representatives.
-
-11.4 In the event that one or more of the provisions hereof were to
-conflict with a current or future applicable act or legislative text,
-said act or legislative text shall prevail, and the Parties shall make
-the necessary amendments so as to comply with said act or legislative
-text. All other provisions shall remain effective. Similarly, invalidity
-of a provision of the Agreement, for any reason whatsoever, shall not
-cause the Agreement as a whole to be invalid.
-
-
- 11.5 LANGUAGE
-
-The Agreement is drafted in both French and English and both versions
-are deemed authentic.
-
-
- Article 12 - NEW VERSIONS OF THE AGREEMENT
-
-12.1 Any person is authorized to duplicate and distribute copies of this
-Agreement.
-
-12.2 So as to ensure coherence, the wording of this Agreement is
-protected and may only be modified by the authors of the License, who
-reserve the right to periodically publish updates or new versions of the
-Agreement, each with a separate number. These subsequent versions may
-address new issues encountered by Free Software.
-
-12.3 Any Software distributed under a given version of the Agreement may
-only be subsequently distributed under the same version of the Agreement
-or a subsequent version.
-
-
- Article 13 - GOVERNING LAW AND JURISDICTION
-
-13.1 The Agreement is governed by French law. The Parties agree to
-endeavor to seek an amicable solution to any disagreements or disputes
-that may arise during the performance of the Agreement.
-
-13.2 Failing an amicable solution within two (2) months as from their
-occurrence, and unless emergency proceedings are necessary, the
-disagreements or disputes shall be referred to the Paris Courts having
-jurisdiction, by the more diligent Party.
-
-
-Version 1.0 dated 2006-09-05.
diff --git a/options/license/CECILL-C b/options/license/CECILL-C
deleted file mode 100644
index 2bb09b4b04..0000000000
--- a/options/license/CECILL-C
+++ /dev/null
@@ -1,517 +0,0 @@
-
-CeCILL-C FREE SOFTWARE LICENSE AGREEMENT
-
-
- Notice
-
-This Agreement is a Free Software license agreement that is the result
-of discussions between its authors in order to ensure compliance with
-the two main principles guiding its drafting:
-
- * firstly, compliance with the principles governing the distribution
- of Free Software: access to source code, broad rights granted to
- users,
- * secondly, the election of a governing law, French law, with which
- it is conformant, both as regards the law of torts and
- intellectual property law, and the protection that it offers to
- both authors and holders of the economic rights over software.
-
-The authors of the CeCILL-C (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre])
-license are:
-
-Commissariat à l'Energie Atomique - CEA, a public scientific, technical
-and industrial research establishment, having its principal place of
-business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France.
-
-Centre National de la Recherche Scientifique - CNRS, a public scientific
-and technological establishment, having its principal place of business
-at 3 rue Michel-Ange, 75794 Paris cedex 16, France.
-
-Institut National de Recherche en Informatique et en Automatique -
-INRIA, a public scientific and technological establishment, having its
-principal place of business at Domaine de Voluceau, Rocquencourt, BP
-105, 78153 Le Chesnay cedex, France.
-
-
- Preamble
-
-The purpose of this Free Software license agreement is to grant users
-the right to modify and re-use the software governed by this license.
-
-The exercising of this right is conditional upon the obligation to make
-available to the community the modifications made to the source code of
-the software so as to contribute to its evolution.
-
-In consideration of access to the source code and the rights to copy,
-modify and redistribute granted by the license, users are provided only
-with a limited warranty and the software's author, the holder of the
-economic rights, and the successive licensors only have limited liability.
-
-In this respect, the risks associated with loading, using, modifying
-and/or developing or reproducing the software by the user are brought to
-the user's attention, given its Free Software status, which may make it
-complicated to use, with the result that its use is reserved for
-developers and experienced professionals having in-depth computer
-knowledge. Users are therefore encouraged to load and test the
-suitability of the software as regards their requirements in conditions
-enabling the security of their systems and/or data to be ensured and,
-more generally, to use and operate it in the same conditions of
-security. This Agreement may be freely reproduced and published,
-provided it is not altered, and that no provisions are either added or
-removed herefrom.
-
-This Agreement may apply to any or all software for which the holder of
-the economic rights decides to submit the use thereof to its provisions.
-
-
- Article 1 - DEFINITIONS
-
-For the purpose of this Agreement, when the following expressions
-commence with a capital letter, they shall have the following meaning:
-
-Agreement: means this license agreement, and its possible subsequent
-versions and annexes.
-
-Software: means the software in its Object Code and/or Source Code form
-and, where applicable, its documentation, "as is" when the Licensee
-accepts the Agreement.
-
-Initial Software: means the Software in its Source Code and possibly its
-Object Code form and, where applicable, its documentation, "as is" when
-it is first distributed under the terms and conditions of the Agreement.
-
-Modified Software: means the Software modified by at least one
-Integrated Contribution.
-
-Source Code: means all the Software's instructions and program lines to
-which access is required so as to modify the Software.
-
-Object Code: means the binary files originating from the compilation of
-the Source Code.
-
-Holder: means the holder(s) of the economic rights over the Initial
-Software.
-
-Licensee: means the Software user(s) having accepted the Agreement.
-
-Contributor: means a Licensee having made at least one Integrated
-Contribution.
-
-Licensor: means the Holder, or any other individual or legal entity, who
-distributes the Software under the Agreement.
-
-Integrated Contribution: means any or all modifications, corrections,
-translations, adaptations and/or new functions integrated into the
-Source Code by any or all Contributors.
-
-Related Module: means a set of sources files including their
-documentation that, without modification to the Source Code, enables
-supplementary functions or services in addition to those offered by the
-Software.
-
-Derivative Software: means any combination of the Software, modified or
-not, and of a Related Module.
-
-Parties: mean both the Licensee and the Licensor.
-
-These expressions may be used both in singular and plural form.
-
-
- Article 2 - PURPOSE
-
-The purpose of the Agreement is the grant by the Licensor to the
-Licensee of a non-exclusive, transferable and worldwide license for the
-Software as set forth in Article 5 hereinafter for the whole term of the
-protection granted by the rights over said Software.
-
-
- Article 3 - ACCEPTANCE
-
-3.1 The Licensee shall be deemed as having accepted the terms and
-conditions of this Agreement upon the occurrence of the first of the
-following events:
-
- * (i) loading the Software by any or all means, notably, by
- downloading from a remote server, or by loading from a physical
- medium;
- * (ii) the first time the Licensee exercises any of the rights
- granted hereunder.
-
-3.2 One copy of the Agreement, containing a notice relating to the
-characteristics of the Software, to the limited warranty, and to the
-fact that its use is restricted to experienced users has been provided
-to the Licensee prior to its acceptance as set forth in Article 3.1
-hereinabove, and the Licensee hereby acknowledges that it has read and
-understood it.
-
-
- Article 4 - EFFECTIVE DATE AND TERM
-
-
- 4.1 EFFECTIVE DATE
-
-The Agreement shall become effective on the date when it is accepted by
-the Licensee as set forth in Article 3.1.
-
-
- 4.2 TERM
-
-The Agreement shall remain in force for the entire legal term of
-protection of the economic rights over the Software.
-
-
- Article 5 - SCOPE OF RIGHTS GRANTED
-
-The Licensor hereby grants to the Licensee, who accepts, the following
-rights over the Software for any or all use, and for the term of the
-Agreement, on the basis of the terms and conditions set forth hereinafter.
-
-Besides, if the Licensor owns or comes to own one or more patents
-protecting all or part of the functions of the Software or of its
-components, the Licensor undertakes not to enforce the rights granted by
-these patents against successive Licensees using, exploiting or
-modifying the Software. If these patents are transferred, the Licensor
-undertakes to have the transferees subscribe to the obligations set
-forth in this paragraph.
-
-
- 5.1 RIGHT OF USE
-
-The Licensee is authorized to use the Software, without any limitation
-as to its fields of application, with it being hereinafter specified
-that this comprises:
-
- 1. permanent or temporary reproduction of all or part of the Software
- by any or all means and in any or all form.
-
- 2. loading, displaying, running, or storing the Software on any or
- all medium.
-
- 3. entitlement to observe, study or test its operation so as to
- determine the ideas and principles behind any or all constituent
- elements of said Software. This shall apply when the Licensee
- carries out any or all loading, displaying, running, transmission
- or storage operation as regards the Software, that it is entitled
- to carry out hereunder.
-
-
- 5.2 RIGHT OF MODIFICATION
-
-The right of modification includes the right to translate, adapt,
-arrange, or make any or all modifications to the Software, and the right
-to reproduce the resulting software. It includes, in particular, the
-right to create a Derivative Software.
-
-The Licensee is authorized to make any or all modification to the
-Software provided that it includes an explicit notice that it is the
-author of said modification and indicates the date of the creation thereof.
-
-
- 5.3 RIGHT OF DISTRIBUTION
-
-In particular, the right of distribution includes the right to publish,
-transmit and communicate the Software to the general public on any or
-all medium, and by any or all means, and the right to market, either in
-consideration of a fee, or free of charge, one or more copies of the
-Software by any means.
-
-The Licensee is further authorized to distribute copies of the modified
-or unmodified Software to third parties according to the terms and
-conditions set forth hereinafter.
-
-
- 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION
-
-The Licensee is authorized to distribute true copies of the Software in
-Source Code or Object Code form, provided that said distribution
-complies with all the provisions of the Agreement and is accompanied by:
-
- 1. a copy of the Agreement,
-
- 2. a notice relating to the limitation of both the Licensor's
- warranty and liability as set forth in Articles 8 and 9,
-
-and that, in the event that only the Object Code of the Software is
-redistributed, the Licensee allows effective access to the full Source
-Code of the Software at a minimum during the entire period of its
-distribution of the Software, it being understood that the additional
-cost of acquiring the Source Code shall not exceed the cost of
-transferring the data.
-
-
- 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE
-
-When the Licensee makes an Integrated Contribution to the Software, the
-terms and conditions for the distribution of the resulting Modified
-Software become subject to all the provisions of this Agreement.
-
-The Licensee is authorized to distribute the Modified Software, in
-source code or object code form, provided that said distribution
-complies with all the provisions of the Agreement and is accompanied by:
-
- 1. a copy of the Agreement,
-
- 2. a notice relating to the limitation of both the Licensor's
- warranty and liability as set forth in Articles 8 and 9,
-
-and that, in the event that only the object code of the Modified
-Software is redistributed, the Licensee allows effective access to the
-full source code of the Modified Software at a minimum during the entire
-period of its distribution of the Modified Software, it being understood
-that the additional cost of acquiring the source code shall not exceed
-the cost of transferring the data.
-
-
- 5.3.3 DISTRIBUTION OF DERIVATIVE SOFTWARE
-
-When the Licensee creates Derivative Software, this Derivative Software
-may be distributed under a license agreement other than this Agreement,
-subject to compliance with the requirement to include a notice
-concerning the rights over the Software as defined in Article 6.4.
-In the event the creation of the Derivative Software required modification
-of the Source Code, the Licensee undertakes that:
-
- 1. the resulting Modified Software will be governed by this Agreement,
- 2. the Integrated Contributions in the resulting Modified Software
- will be clearly identified and documented,
- 3. the Licensee will allow effective access to the source code of the
- Modified Software, at a minimum during the entire period of
- distribution of the Derivative Software, such that such
- modifications may be carried over in a subsequent version of the
- Software; it being understood that the additional cost of
- purchasing the source code of the Modified Software shall not
- exceed the cost of transferring the data.
-
-
- 5.3.4 COMPATIBILITY WITH THE CeCILL LICENSE
-
-When a Modified Software contains an Integrated Contribution subject to
-the CeCILL license agreement, or when a Derivative Software contains a
-Related Module subject to the CeCILL license agreement, the provisions
-set forth in the third item of Article 6.4 are optional.
-
-
- Article 6 - INTELLECTUAL PROPERTY
-
-
- 6.1 OVER THE INITIAL SOFTWARE
-
-The Holder owns the economic rights over the Initial Software. Any or
-all use of the Initial Software is subject to compliance with the terms
-and conditions under which the Holder has elected to distribute its work
-and no one shall be entitled to modify the terms and conditions for the
-distribution of said Initial Software.
-
-The Holder undertakes that the Initial Software will remain ruled at
-least by this Agreement, for the duration set forth in Article 4.2.
-
-
- 6.2 OVER THE INTEGRATED CONTRIBUTIONS
-
-The Licensee who develops an Integrated Contribution is the owner of the
-intellectual property rights over this Contribution as defined by
-applicable law.
-
-
- 6.3 OVER THE RELATED MODULES
-
-The Licensee who develops a Related Module is the owner of the
-intellectual property rights over this Related Module as defined by
-applicable law and is free to choose the type of agreement that shall
-govern its distribution under the conditions defined in Article 5.3.3.
-
-
- 6.4 NOTICE OF RIGHTS
-
-The Licensee expressly undertakes:
-
- 1. not to remove, or modify, in any manner, the intellectual property
- notices attached to the Software;
-
- 2. to reproduce said notices, in an identical manner, in the copies
- of the Software modified or not;
-
- 3. to ensure that use of the Software, its intellectual property
- notices and the fact that it is governed by the Agreement is
- indicated in a text that is easily accessible, specifically from
- the interface of any Derivative Software.
-
-The Licensee undertakes not to directly or indirectly infringe the
-intellectual property rights of the Holder and/or Contributors on the
-Software and to take, where applicable, vis-à-vis its staff, any and all
-measures required to ensure respect of said intellectual property rights
-of the Holder and/or Contributors.
-
-
- Article 7 - RELATED SERVICES
-
-7.1 Under no circumstances shall the Agreement oblige the Licensor to
-provide technical assistance or maintenance services for the Software.
-
-However, the Licensor is entitled to offer this type of services. The
-terms and conditions of such technical assistance, and/or such
-maintenance, shall be set forth in a separate instrument. Only the
-Licensor offering said maintenance and/or technical assistance services
-shall incur liability therefor.
-
-7.2 Similarly, any Licensor is entitled to offer to its licensees, under
-its sole responsibility, a warranty, that shall only be binding upon
-itself, for the redistribution of the Software and/or the Modified
-Software, under terms and conditions that it is free to decide. Said
-warranty, and the financial terms and conditions of its application,
-shall be subject of a separate instrument executed between the Licensor
-and the Licensee.
-
-
- Article 8 - LIABILITY
-
-8.1 Subject to the provisions of Article 8.2, the Licensee shall be
-entitled to claim compensation for any direct loss it may have suffered
-from the Software as a result of a fault on the part of the relevant
-Licensor, subject to providing evidence thereof.
-
-8.2 The Licensor's liability is limited to the commitments made under
-this Agreement and shall not be incurred as a result of in particular:
-(i) loss due the Licensee's total or partial failure to fulfill its
-obligations, (ii) direct or consequential loss that is suffered by the
-Licensee due to the use or performance of the Software, and (iii) more
-generally, any consequential loss. In particular the Parties expressly
-agree that any or all pecuniary or business loss (i.e. loss of data,
-loss of profits, operating loss, loss of customers or orders,
-opportunity cost, any disturbance to business activities) or any or all
-legal proceedings instituted against the Licensee by a third party,
-shall constitute consequential loss and shall not provide entitlement to
-any or all compensation from the Licensor.
-
-
- Article 9 - WARRANTY
-
-9.1 The Licensee acknowledges that the scientific and technical
-state-of-the-art when the Software was distributed did not enable all
-possible uses to be tested and verified, nor for the presence of
-possible defects to be detected. In this respect, the Licensee's
-attention has been drawn to the risks associated with loading, using,
-modifying and/or developing and reproducing the Software which are
-reserved for experienced users.
-
-The Licensee shall be responsible for verifying, by any or all means,
-the suitability of the product for its requirements, its good working
-order, and for ensuring that it shall not cause damage to either persons
-or properties.
-
-9.2 The Licensor hereby represents, in good faith, that it is entitled
-to grant all the rights over the Software (including in particular the
-rights set forth in Article 5).
-
-9.3 The Licensee acknowledges that the Software is supplied "as is" by
-the Licensor without any other express or tacit warranty, other than
-that provided for in Article 9.2 and, in particular, without any warranty
-as to its commercial value, its secured, safe, innovative or relevant
-nature.
-
-Specifically, the Licensor does not warrant that the Software is free
-from any error, that it will operate without interruption, that it will
-be compatible with the Licensee's own equipment and software
-configuration, nor that it will meet the Licensee's requirements.
-
-9.4 The Licensor does not either expressly or tacitly warrant that the
-Software does not infringe any third party intellectual property right
-relating to a patent, software or any other property right. Therefore,
-the Licensor disclaims any and all liability towards the Licensee
-arising out of any or all proceedings for infringement that may be
-instituted in respect of the use, modification and redistribution of the
-Software. Nevertheless, should such proceedings be instituted against
-the Licensee, the Licensor shall provide it with technical and legal
-assistance for its defense. Such technical and legal assistance shall be
-decided on a case-by-case basis between the relevant Licensor and the
-Licensee pursuant to a memorandum of understanding. The Licensor
-disclaims any and all liability as regards the Licensee's use of the
-name of the Software. No warranty is given as regards the existence of
-prior rights over the name of the Software or as regards the existence
-of a trademark.
-
-
- Article 10 - TERMINATION
-
-10.1 In the event of a breach by the Licensee of its obligations
-hereunder, the Licensor may automatically terminate this Agreement
-thirty (30) days after notice has been sent to the Licensee and has
-remained ineffective.
-
-10.2 A Licensee whose Agreement is terminated shall no longer be
-authorized to use, modify or distribute the Software. However, any
-licenses that it may have granted prior to termination of the Agreement
-shall remain valid subject to their having been granted in compliance
-with the terms and conditions hereof.
-
-
- Article 11 - MISCELLANEOUS
-
-
- 11.1 EXCUSABLE EVENTS
-
-Neither Party shall be liable for any or all delay, or failure to
-perform the Agreement, that may be attributable to an event of force
-majeure, an act of God or an outside cause, such as defective
-functioning or interruptions of the electricity or telecommunications
-networks, network paralysis following a virus attack, intervention by
-government authorities, natural disasters, water damage, earthquakes,
-fire, explosions, strikes and labor unrest, war, etc.
-
-11.2 Any failure by either Party, on one or more occasions, to invoke
-one or more of the provisions hereof, shall under no circumstances be
-interpreted as being a waiver by the interested Party of its right to
-invoke said provision(s) subsequently.
-
-11.3 The Agreement cancels and replaces any or all previous agreements,
-whether written or oral, between the Parties and having the same
-purpose, and constitutes the entirety of the agreement between said
-Parties concerning said purpose. No supplement or modification to the
-terms and conditions hereof shall be effective as between the Parties
-unless it is made in writing and signed by their duly authorized
-representatives.
-
-11.4 In the event that one or more of the provisions hereof were to
-conflict with a current or future applicable act or legislative text,
-said act or legislative text shall prevail, and the Parties shall make
-the necessary amendments so as to comply with said act or legislative
-text. All other provisions shall remain effective. Similarly, invalidity
-of a provision of the Agreement, for any reason whatsoever, shall not
-cause the Agreement as a whole to be invalid.
-
-
- 11.5 LANGUAGE
-
-The Agreement is drafted in both French and English and both versions
-are deemed authentic.
-
-
- Article 12 - NEW VERSIONS OF THE AGREEMENT
-
-12.1 Any person is authorized to duplicate and distribute copies of this
-Agreement.
-
-12.2 So as to ensure coherence, the wording of this Agreement is
-protected and may only be modified by the authors of the License, who
-reserve the right to periodically publish updates or new versions of the
-Agreement, each with a separate number. These subsequent versions may
-address new issues encountered by Free Software.
-
-12.3 Any Software distributed under a given version of the Agreement may
-only be subsequently distributed under the same version of the Agreement
-or a subsequent version.
-
-
- Article 13 - GOVERNING LAW AND JURISDICTION
-
-13.1 The Agreement is governed by French law. The Parties agree to
-endeavor to seek an amicable solution to any disagreements or disputes
-that may arise during the performance of the Agreement.
-
-13.2 Failing an amicable solution within two (2) months as from their
-occurrence, and unless emergency proceedings are necessary, the
-disagreements or disputes shall be referred to the Paris Courts having
-jurisdiction, by the more diligent Party.
-
-
-Version 1.0 dated 2006-09-05.
diff --git a/options/license/CERN-OHL-1.1 b/options/license/CERN-OHL-1.1
deleted file mode 100644
index 9fcefc9568..0000000000
--- a/options/license/CERN-OHL-1.1
+++ /dev/null
@@ -1,47 +0,0 @@
-CERN OHL v1.1
-2011-07-08 - CERN, Geneva, Switzerland
-CERN Open Hardware Licence v1.1
-
-Preamble
-Through this CERN Open Hardware Licence ("CERN OHL") version 1.1, the Organization wishes to disseminate its hardware designs (as published on http://www.ohwr.org/) as widely as possible, and generally to foster collaboration among public research hardware designers. The CERN OHL is copyright of CERN. Anyone is welcome to use the CERN OHL, in unmodified form only, for the distribution of his own Open Hardware designs. Any other right is reserved.
-
-1. Definitions
-In this Licence, the following terms have the following meanings:
-“Licence†means this CERN OHL.
-“Documentation†means schematic diagrams, designs, circuit or circuit board layouts, mechanical drawings, flow charts and descriptive text, and other explanatory material that is explicitly stated as being made available under the conditions of this Licence. The Documentation may be in any medium, including but not limited to computer files and representations on paper, film, or any other media.
-“Product†means either an entire, or any part of a, device built using the Documentation or the modified Documentation.
-“Licensee†means any natural or legal person exercising rights under this Licence.
-“Licensor†means any natural or legal person that creates or modifies Documentation and subsequently communicates to the public and/ or distributes the resulting Documentation under the terms and conditions of this Licence.
-A Licensee may at the same time be a Licensor, and vice versa.
-
-2. Applicability
-2.1 This Licence governs the use, copying, modification, communication to the public and distribution of the Documentation, and the manufacture and distribution of Products. By exercising any right granted under this Licence, the Licensee irrevocably accepts these terms and conditions.
-2.2 This Licence is granted by the Licensor directly to the Licensee, and shall apply worldwide and without limitation in time. The Licensee may assign his licence rights or grant sub-licences.
-2.3 This Licence does not apply to software, firmware, or code loaded into programmable devices which may be used in conjunction with the Documentation, the modified Documentation or with Products. The use of such software, firmware, or code is subject to the applicable licence terms and conditions.
-
-3. Copying, modification, communication to the public and distribution of the Documentation
-3.1 The Licensee shall keep intact all copyright and trademarks notices and all notices that refer to this Licence and to the disclaimer of warranties that is included in the Documentation. He shall include a copy thereof in every copy of the documentation or, as the case may be, modified Documentation, that he communicates to the public or distributes.
-3.2 The Licensee may use, copy, communicate to the public and distribute verbatim copies of the Documentation, in any medium, subject to the requirements specified in section 3.1.
-3.3 The Licensee may modify the Documentation or any portion thereof. The Licensee may communicate to the public and distribute the modified Documentation (thereby in addition to being a Licensee also becoming a Licensor), always provided that he shall:
-a. comply with section 3.1;
-b. cause the modified Documentation to carry prominent notices stating that the Licensee has modified the Documentation, with the date and details of the modifications;
-c. license the modified Documentation under the terms and conditions of this Licence or, where applicable, a later version of this Licence as may be issued by CERN; and
-d. send a copy of the modified Documentation to all Licensors that contributed to the parts of the Documentation that were modified, as well as to any other Licensor who has requested to receive a copy of the modified Documentation and has provided a means of contact with the Documentation.
-3.4 The Licence includes a licence to those patents or registered designs that are held by the Licensor, to the extent necessary to make use of the rights granted under this Licence. The scope of this section 3.4 shall be strictly limited to the parts of the Documentation or modified Documentation created by the Licensor.
-
-4. Manufacture and distribution of Products
-4.1 The Licensee may manufacture or distribute Products always provided that the Licensee distributes to each recipient of such Products a copy of the Documentation or modified Documentation, as applicable, and complies with section 3.
-4.2 The Licensee is invited to inform in writing any Licensor who has indicated its wish to receive this information about the type, quantity and dates of production of Products the Licensee has (had) manufactured.
-
-5. Warranty and liability
-5.1 DISCLAIMER – The Documentation and any modified Documentation are provided "as is" and any express or implied warranties, including, but not limited to, implied warranties of merchantability, of satisfactory quality, and fitness for a particular purpose or use are disclaimed in respect of the Documentation, the modified Documentation or any Product. The Licensor makes no representation that the Documentation, modified Documentation, or any Product, does or will not infringe any patent, copyright, trade secret or other proprietary right. The entire risk as to the use, quality, and performance of a Product shall be with the Licensee and not the Licensor. This disclaimer of warranty is an essential part of this Licence and a condition for the grant of any rights granted under this Licence. The Licensee warrants that it does not act in a consumer capacity.
-5.2 LIMITATION OF LIABILITY – The Licensor shall have no liability for direct, indirect, special, incidental, consequential, exemplary, punitive or other damages of any character including, without limitation, procurement of substitute goods or services, loss of use, data or profits, or business interruption, however caused and on any
-theory of contract, warranty, tort (including negligence), product liability or otherwise, arising in any way in relation to the Documentation, modified Documentation and/or the use, manufacture or distribution of a Product, even if advised of the possibility of such damages, and the Licensee shall hold the Licensor(s) free and harmless
-from any liability, costs, damages, fees and expenses, including claims by third parties, in relation to such use.
-
-6. General
-6.1 The rights granted under this Licence do not imply or represent any transfer or assignment of intellectual property rights to the Licensee.
-6.2 The Licensee shall not use or make reference to any of the names, acronyms, images or logos under which the Licensor is known, save in so far as required to comply with section 3. Any such permitted use or reference shall be factual and shall in no event suggest any kind of endorsement by the Licensor or its personnel of the modified Documentation or any Product, or any kind of implication by the Licensor or its personnel in the preparation of the modified Documentation or Product.
-6.3 CERN may publish updated versions of this Licence which retain the same general provisions as this version, but differ in detail so far this is required and reasonable. New versions will be published with a unique version number.
-6.4 This Licence shall terminate with immediate effect, upon written notice and without involvement of a court if the Licensee fails to comply with any of its terms and conditions, or if the Licensee initiates legal action against Licensor in relation to this Licence. Section 5 shall continue to apply.
-6.5 Except as may be otherwise agreed with the Intergovernmental Organization, any dispute with respect to this Licence involving an Intergovernmental Organization shall, by virtue of the latter's Intergovernmental status, be settled by international arbitration. The arbitration proceedings shall be held at the place where the Intergovernmental Organization has its seat. The arbitral award shall be final and binding upon the parties, who hereby expressly agree to renounce any form of appeal or revision.
diff --git a/options/license/CERN-OHL-1.2 b/options/license/CERN-OHL-1.2
deleted file mode 100644
index def694c190..0000000000
--- a/options/license/CERN-OHL-1.2
+++ /dev/null
@@ -1,48 +0,0 @@
-CERN OHL v1.2
-2013-09-06 - CERN, Geneva, Switzerland
-CERN Open Hardware Licence v1.2
-
-Preamble
-Through this CERN Open Hardware Licence ("CERN OHL") version 1.2, CERN wishes to provide a tool to foster collaboration and sharing among hardware designers. The CERN OHL is copyright CERN. Anyone is welcome to use the CERN OHL, in unmodified form only, for the distribution of their own Open Hardware designs. Any other right is reserved. Release of hardware designs under the CERN OHL does not constitute an endorsement of the licensor or its designs nor does it imply any involvement by CERN in the development of such designs.
-
-1. Definitions
-In this Licence, the following terms have the following meanings:
-“Licence†means this CERN OHL.
-“Documentation†means schematic diagrams, designs, circuit or circuit board layouts, mechanical drawings, flow charts and descriptive text, and other explanatory material that is explicitly stated as being made available under the conditions of this Licence. The Documentation may be in any medium, including but not limited to computer files and representations on paper, film, or any other media.
-“Documentation Location†means a location where the Licensor has placed Documentation, and which he believes will be publicly accessible for at least three years from the first communication to the public or distribution of Documentation.
-“Product†means either an entire, or any part of a, device built using the Documentation or the modified Documentation.
-“Licensee†means any natural or legal person exercising rights under this Licence.
-“Licensor†means any natural or legal person that creates or modifies Documentation and subsequently communicates to the public and/ or distributes the resulting Documentation under the terms and conditions of this Licence.
-A Licensee may at the same time be a Licensor, and vice versa.
-Use of the masculine gender includes the feminine and neuter genders and is employed solely to facilitate reading.
-
-2. Applicability
-2.1. This Licence governs the use, copying, modification, communication to the public and distribution of the Documentation, and the manufacture and distribution of Products. By exercising any right granted under this Licence, the Licensee irrevocably accepts these terms and conditions.
-2.2. This Licence is granted by the Licensor directly to the Licensee, and shall apply worldwide and without limitation in time. The Licensee may assign his licence rights or grant sub-licences.
-2.3. This Licence does not extend to software, firmware, or code loaded into programmable devices which may be used in conjunction with the Documentation, the modified Documentation or with Products, unless such software, firmware, or code is explicitly expressed to be subject to this Licence. The use of such software, firmware, or code is otherwise subject to the applicable licence terms and conditions.
-
-3. Copying, modification, communication to the public and distribution of the Documentation
-3.1. The Licensee shall keep intact all copyright and trademarks notices, all notices referring to Documentation Location, and all notices that refer to this Licence and to the disclaimer of warranties that are included in the Documentation. He shall include a copy thereof in every copy of the Documentation or, as the case may be, modified Documentation, that he communicates to the public or distributes.
-3.2. The Licensee may copy, communicate to the public and distribute verbatim copies of the Documentation, in any medium, subject to the requirements specified in section 3.1.
-3.3. The Licensee may modify the Documentation or any portion thereof provided that upon modification of the Documentation, the Licensee shall make the modified Documentation available from a Documentation Location such that it can be easily located by an original Licensor once the Licensee communicates to the public or distributes the modified Documentation under section 3.4, and, where required by section 4.1, by a recipient of a Product. However, the Licensor shall not assert his rights under the foregoing proviso unless or until a Product is distributed.
-3.4. The Licensee may communicate to the public and distribute the modified Documentation (thereby in addition to being a Licensee also becoming a Licensor), always provided that he shall:
-a) comply with section 3.1;
-b) cause the modified Documentation to carry prominent notices stating that the Licensee has modified the Documentation, with the date and description of the modifications;
-c) cause the modified Documentation to carry a new Documentation Location notice if the original Documentation provided for one;
-d) make available the modified Documentation at the same level of abstraction as that of the Documentation, in the preferred format for making modifications to it (e.g. the native format of the CAD tool as applicable), and in the event that format is proprietary, in a format viewable with a tool licensed under an OSI-approved license if the proprietary tool can create it; and
-e) license the modified Documentation under the terms and conditions of this Licence or, where applicable, a later version of this Licence as may be issued by CERN.
-3.5. The Licence includes a non-exclusive licence to those patents or registered designs that are held by, under the control of, or sub-licensable by the Licensor, to the extent necessary to make use of the rights granted under this Licence. The scope of this section 3.5 shall be strictly limited to the parts of the Documentation or modified Documentation created by the Licensor.
-
-4. Manufacture and distribution of Products
-4.1. The Licensee may manufacture or distribute Products always provided that, where such manufacture or distribution requires a licence under this Licence the Licensee provides to each recipient of such Products an easy means of accessing a copy of the Documentation or modified Documentation, as applicable, as set out in section 3.
-4.2. The Licensee is invited to inform any Licensor who has indicated his wish to receive this information about the type, quantity and dates of production of Products the Licensee has (had) manufactured
-
-5. Warranty and liability
-5.1. DISCLAIMER – The Documentation and any modified Documentation are provided "as is" and any express or implied warranties, including, but not limited to, implied warranties of merchantability, of satisfactory quality, non-infringement of third party rights, and fitness for a particular purpose or use are disclaimed in respect of the Documentation, the modified Documentation or any Product. The Licensor makes no representation that the Documentation, modified Documentation, or any Product, does or will not infringe any patent, copyright, trade secret or other proprietary right. The entire risk as to the use, quality, and performance of a Product shall be with the Licensee and not the Licensor. This disclaimer of warranty is an essential part of this Licence and a condition for the grant of any rights granted under this Licence. The Licensee warrants that it does not act in a consumer capacity.
-5.2. LIMITATION OF LIABILITY – The Licensor shall have no liability for direct, indirect, special, incidental, consequential, exemplary, punitive or other damages of any character including, without limitation, procurement of substitute goods or services, loss of use, data or profits, or business interruption, however caused and on any theory of contract, warranty, tort (including negligence), product liability or otherwise, arising in any way in relation to the Documentation, modified Documentation and/or the use, manufacture or distribution of a Product, even if advised of the possibility of such damages, and the Licensee shall hold the Licensor(s) free and harmless from any liability, costs, damages, fees and expenses, including claims by third parties, in relation to such use.
-
-6. General
-6.1. Except for the rights explicitly granted hereunder, this Licence does not imply or represent any transfer or assignment of intellectual property rights to the Licensee.
-6.2. The Licensee shall not use or make reference to any of the names (including acronyms and abbreviations), images, or logos under which the Licensor is known, save in so far as required to comply with section 3. Any such permitted use or reference shall be factual and shall in no event suggest any kind of endorsement by the Licensor or its personnel of the modified Documentation or any Product, or any kind of implication by the Licensor or its personnel in the preparation of the modified Documentation or Product.
-6.3. CERN may publish updated versions of this Licence which retain the same general provisions as this version, but differ in detail so far this is required and reasonable. New versions will be published with a unique version number.
-6.4. This Licence shall terminate with immediate effect, upon written notice and without involvement of a court if the Licensee fails to comply with any of its terms and conditions, or if the Licensee initiates legal action against Licensor in relation to this Licence. Section 5 shall continue to apply.
diff --git a/options/license/CERN-OHL-P-2.0 b/options/license/CERN-OHL-P-2.0
deleted file mode 100644
index f19d2b7adc..0000000000
--- a/options/license/CERN-OHL-P-2.0
+++ /dev/null
@@ -1,199 +0,0 @@
-CERN Open Hardware Licence Version 2 - Permissive
-
-
-Preamble
-
-CERN has developed this licence to promote collaboration among
-hardware designers and to provide a legal tool which supports the
-freedom to use, study, modify, share and distribute hardware designs
-and products based on those designs. Version 2 of the CERN Open
-Hardware Licence comes in three variants: this licence, CERN-OHL-P
-(permissive); and two reciprocal licences: CERN- OHL-W (weakly
-reciprocal) and CERN-OHL-S (strongly reciprocal).
-
-The CERN-OHL-P is copyright CERN 2020. Anyone is welcome to use it, in
-unmodified form only.
-
-Use of this Licence does not imply any endorsement by CERN of any
-Licensor or their designs nor does it imply any involvement by CERN in
-their development.
-
-
-1 Definitions
-
- 1.1 'Licence' means this CERN-OHL-P.
-
- 1.2 'Source' means information such as design materials or digital
- code which can be applied to Make or test a Product or to
- prepare a Product for use, Conveyance or sale, regardless of its
- medium or how it is expressed. It may include Notices.
-
- 1.3 'Covered Source' means Source that is explicitly made available
- under this Licence.
-
- 1.4 'Product' means any device, component, work or physical object,
- whether in finished or intermediate form, arising from the use,
- application or processing of Covered Source.
-
- 1.5 'Make' means to create or configure something, whether by
- manufacture, assembly, compiling, loading or applying Covered
- Source or another Product or otherwise.
-
- 1.6 'Notice' means copyright, acknowledgement and trademark notices,
- references to the location of any Notices, modification notices
- (subsection 3.3(b)) and all notices that refer to this Licence
- and to the disclaimer of warranties that are included in the
- Covered Source.
-
- 1.7 'Licensee' or 'You' means any person exercising rights under
- this Licence.
-
- 1.8 'Licensor' means a person who creates Source or modifies Covered
- Source and subsequently Conveys the resulting Covered Source
- under the terms and conditions of this Licence. A person may be
- a Licensee and a Licensor at the same time.
-
- 1.9 'Convey' means to communicate to the public or distribute.
-
-
-2 Applicability
-
- 2.1 This Licence governs the use, copying, modification, Conveying
- of Covered Source and Products, and the Making of Products. By
- exercising any right granted under this Licence, You irrevocably
- accept these terms and conditions.
-
- 2.2 This Licence is granted by the Licensor directly to You, and
- shall apply worldwide and without limitation in time.
-
- 2.3 You shall not attempt to restrict by contract or otherwise the
- rights granted under this Licence to other Licensees.
-
- 2.4 This Licence is not intended to restrict fair use, fair dealing,
- or any other similar right.
-
-
-3 Copying, modifying and Conveying Covered Source
-
- 3.1 You may copy and Convey verbatim copies of Covered Source, in
- any medium, provided You retain all Notices.
-
- 3.2 You may modify Covered Source, other than Notices.
-
- You may only delete Notices if they are no longer applicable to
- the corresponding Covered Source as modified by You and You may
- add additional Notices applicable to Your modifications.
-
- 3.3 You may Convey modified Covered Source (with the effect that You
- shall also become a Licensor) provided that You:
-
- a) retain Notices as required in subsection 3.2; and
-
- b) add a Notice to the modified Covered Source stating that You
- have modified it, with the date and brief description of how
- You have modified it.
-
- 3.4 You may Convey Covered Source or modified Covered Source under
- licence terms which differ from the terms of this Licence
- provided that:
-
- a) You comply at all times with subsection 3.3; and
-
- b) You provide a copy of this Licence to anyone to whom You
- Convey Covered Source or modified Covered Source.
-
-
-4 Making and Conveying Products
-
-You may Make Products, and/or Convey them, provided that You ensure
-that the recipient of the Product has access to any Notices applicable
-to the Product.
-
-
-5 DISCLAIMER AND LIABILITY
-
- 5.1 DISCLAIMER OF WARRANTY -- The Covered Source and any Products
- are provided 'as is' and any express or implied warranties,
- including, but not limited to, implied warranties of
- merchantability, of satisfactory quality, non-infringement of
- third party rights, and fitness for a particular purpose or use
- are disclaimed in respect of any Source or Product to the
- maximum extent permitted by law. The Licensor makes no
- representation that any Source or Product does not or will not
- infringe any patent, copyright, trade secret or other
- proprietary right. The entire risk as to the use, quality, and
- performance of any Source or Product shall be with You and not
- the Licensor. This disclaimer of warranty is an essential part
- of this Licence and a condition for the grant of any rights
- granted under this Licence.
-
- 5.2 EXCLUSION AND LIMITATION OF LIABILITY -- The Licensor shall, to
- the maximum extent permitted by law, have no liability for
- direct, indirect, special, incidental, consequential, exemplary,
- punitive or other damages of any character including, without
- limitation, procurement of substitute goods or services, loss of
- use, data or profits, or business interruption, however caused
- and on any theory of contract, warranty, tort (including
- negligence), product liability or otherwise, arising in any way
- in relation to the Covered Source, modified Covered Source
- and/or the Making or Conveyance of a Product, even if advised of
- the possibility of such damages, and You shall hold the
- Licensor(s) free and harmless from any liability, costs,
- damages, fees and expenses, including claims by third parties,
- in relation to such use.
-
-
-6 Patents
-
- 6.1 Subject to the terms and conditions of this Licence, each
- Licensor hereby grants to You a perpetual, worldwide,
- non-exclusive, no-charge, royalty-free, irrevocable (except as
- stated in this section 6, or where terminated by the Licensor
- for cause) patent license to Make, have Made, use, offer to
- sell, sell, import, and otherwise transfer the Covered Source
- and Products, where such licence applies only to those patent
- claims licensable by such Licensor that are necessarily
- infringed by exercising rights under the Covered Source as
- Conveyed by that Licensor.
-
- 6.2 If You institute patent litigation against any entity (including
- a cross-claim or counterclaim in a lawsuit) alleging that the
- Covered Source or a Product constitutes direct or contributory
- patent infringement, or You seek any declaration that a patent
- licensed to You under this Licence is invalid or unenforceable
- then any rights granted to You under this Licence shall
- terminate as of the date such process is initiated.
-
-
-7 General
-
- 7.1 If any provisions of this Licence are or subsequently become
- invalid or unenforceable for any reason, the remaining
- provisions shall remain effective.
-
- 7.2 You shall not use any of the name (including acronyms and
- abbreviations), image, or logo by which the Licensor or CERN is
- known, except where needed to comply with section 3, or where
- the use is otherwise allowed by law. Any such permitted use
- shall be factual and shall not be made so as to suggest any kind
- of endorsement or implication of involvement by the Licensor or
- its personnel.
-
- 7.3 CERN may publish updated versions and variants of this Licence
- which it considers to be in the spirit of this version, but may
- differ in detail to address new problems or concerns. New
- versions will be published with a unique version number and a
- variant identifier specifying the variant. If the Licensor has
- specified that a given variant applies to the Covered Source
- without specifying a version, You may treat that Covered Source
- as being released under any version of the CERN-OHL with that
- variant. If no variant is specified, the Covered Source shall be
- treated as being released under CERN-OHL-S. The Licensor may
- also specify that the Covered Source is subject to a specific
- version of the CERN-OHL or any later version in which case You
- may apply this or any later version of CERN-OHL with the same
- variant identifier published by CERN.
-
- 7.4 This Licence shall not be enforceable except by a Licensor
- acting as such, and third party beneficiary rights are
- specifically excluded.
diff --git a/options/license/CERN-OHL-S-2.0 b/options/license/CERN-OHL-S-2.0
deleted file mode 100644
index 114486fd94..0000000000
--- a/options/license/CERN-OHL-S-2.0
+++ /dev/null
@@ -1,289 +0,0 @@
-CERN Open Hardware Licence Version 2 - Strongly Reciprocal
-
-
-Preamble
-
-CERN has developed this licence to promote collaboration among
-hardware designers and to provide a legal tool which supports the
-freedom to use, study, modify, share and distribute hardware designs
-and products based on those designs. Version 2 of the CERN Open
-Hardware Licence comes in three variants: CERN-OHL-P (permissive); and
-two reciprocal licences: CERN-OHL-W (weakly reciprocal) and this
-licence, CERN-OHL-S (strongly reciprocal).
-
-The CERN-OHL-S is copyright CERN 2020. Anyone is welcome to use it, in
-unmodified form only.
-
-Use of this Licence does not imply any endorsement by CERN of any
-Licensor or their designs nor does it imply any involvement by CERN in
-their development.
-
-
-1 Definitions
-
- 1.1 'Licence' means this CERN-OHL-S.
-
- 1.2 'Compatible Licence' means
-
- a) any earlier version of the CERN Open Hardware licence, or
-
- b) any version of the CERN-OHL-S, or
-
- c) any licence which permits You to treat the Source to which
- it applies as licensed under CERN-OHL-S provided that on
- Conveyance of any such Source, or any associated Product You
- treat the Source in question as being licensed under
- CERN-OHL-S.
-
- 1.3 'Source' means information such as design materials or digital
- code which can be applied to Make or test a Product or to
- prepare a Product for use, Conveyance or sale, regardless of its
- medium or how it is expressed. It may include Notices.
-
- 1.4 'Covered Source' means Source that is explicitly made available
- under this Licence.
-
- 1.5 'Product' means any device, component, work or physical object,
- whether in finished or intermediate form, arising from the use,
- application or processing of Covered Source.
-
- 1.6 'Make' means to create or configure something, whether by
- manufacture, assembly, compiling, loading or applying Covered
- Source or another Product or otherwise.
-
- 1.7 'Available Component' means any part, sub-assembly, library or
- code which:
-
- a) is licensed to You as Complete Source under a Compatible
- Licence; or
-
- b) is available, at the time a Product or the Source containing
- it is first Conveyed, to You and any other prospective
- licensees
-
- i) as a physical part with sufficient rights and
- information (including any configuration and
- programming files and information about its
- characteristics and interfaces) to enable it either to
- be Made itself, or to be sourced and used to Make the
- Product; or
- ii) as part of the normal distribution of a tool used to
- design or Make the Product.
-
- 1.8 'Complete Source' means the set of all Source necessary to Make
- a Product, in the preferred form for making modifications,
- including necessary installation and interfacing information
- both for the Product, and for any included Available Components.
- If the format is proprietary, it must also be made available in
- a format (if the proprietary tool can create it) which is
- viewable with a tool available to potential licensees and
- licensed under a licence approved by the Free Software
- Foundation or the Open Source Initiative. Complete Source need
- not include the Source of any Available Component, provided that
- You include in the Complete Source sufficient information to
- enable a recipient to Make or source and use the Available
- Component to Make the Product.
-
- 1.9 'Source Location' means a location where a Licensor has placed
- Covered Source, and which that Licensor reasonably believes will
- remain easily accessible for at least three years for anyone to
- obtain a digital copy.
-
- 1.10 'Notice' means copyright, acknowledgement and trademark notices,
- Source Location references, modification notices (subsection
- 3.3(b)) and all notices that refer to this Licence and to the
- disclaimer of warranties that are included in the Covered
- Source.
-
- 1.11 'Licensee' or 'You' means any person exercising rights under
- this Licence.
-
- 1.12 'Licensor' means a natural or legal person who creates or
- modifies Covered Source. A person may be a Licensee and a
- Licensor at the same time.
-
- 1.13 'Convey' means to communicate to the public or distribute.
-
-
-2 Applicability
-
- 2.1 This Licence governs the use, copying, modification, Conveying
- of Covered Source and Products, and the Making of Products. By
- exercising any right granted under this Licence, You irrevocably
- accept these terms and conditions.
-
- 2.2 This Licence is granted by the Licensor directly to You, and
- shall apply worldwide and without limitation in time.
-
- 2.3 You shall not attempt to restrict by contract or otherwise the
- rights granted under this Licence to other Licensees.
-
- 2.4 This Licence is not intended to restrict fair use, fair dealing,
- or any other similar right.
-
-
-3 Copying, modifying and Conveying Covered Source
-
- 3.1 You may copy and Convey verbatim copies of Covered Source, in
- any medium, provided You retain all Notices.
-
- 3.2 You may modify Covered Source, other than Notices, provided that
- You irrevocably undertake to make that modified Covered Source
- available from a Source Location should You Convey a Product in
- circumstances where the recipient does not otherwise receive a
- copy of the modified Covered Source. In each case subsection 3.3
- shall apply.
-
- You may only delete Notices if they are no longer applicable to
- the corresponding Covered Source as modified by You and You may
- add additional Notices applicable to Your modifications.
- Including Covered Source in a larger work is modifying the
- Covered Source, and the larger work becomes modified Covered
- Source.
-
- 3.3 You may Convey modified Covered Source (with the effect that You
- shall also become a Licensor) provided that You:
-
- a) retain Notices as required in subsection 3.2;
-
- b) add a Notice to the modified Covered Source stating that You
- have modified it, with the date and brief description of how
- You have modified it;
-
- c) add a Source Location Notice for the modified Covered Source
- if You Convey in circumstances where the recipient does not
- otherwise receive a copy of the modified Covered Source; and
-
- d) license the modified Covered Source under the terms and
- conditions of this Licence (or, as set out in subsection
- 8.3, a later version, if permitted by the licence of the
- original Covered Source). Such modified Covered Source must
- be licensed as a whole, but excluding Available Components
- contained in it, which remain licensed under their own
- applicable licences.
-
-
-4 Making and Conveying Products
-
-You may Make Products, and/or Convey them, provided that You either
-provide each recipient with a copy of the Complete Source or ensure
-that each recipient is notified of the Source Location of the Complete
-Source. That Complete Source is Covered Source, and You must
-accordingly satisfy Your obligations set out in subsection 3.3. If
-specified in a Notice, the Product must visibly and securely display
-the Source Location on it or its packaging or documentation in the
-manner specified in that Notice.
-
-
-5 Research and Development
-
-You may Convey Covered Source, modified Covered Source or Products to
-a legal entity carrying out development, testing or quality assurance
-work on Your behalf provided that the work is performed on terms which
-prevent the entity from both using the Source or Products for its own
-internal purposes and Conveying the Source or Products or any
-modifications to them to any person other than You. Any modifications
-made by the entity shall be deemed to be made by You pursuant to
-subsection 3.2.
-
-
-6 DISCLAIMER AND LIABILITY
-
- 6.1 DISCLAIMER OF WARRANTY -- The Covered Source and any Products
- are provided 'as is' and any express or implied warranties,
- including, but not limited to, implied warranties of
- merchantability, of satisfactory quality, non-infringement of
- third party rights, and fitness for a particular purpose or use
- are disclaimed in respect of any Source or Product to the
- maximum extent permitted by law. The Licensor makes no
- representation that any Source or Product does not or will not
- infringe any patent, copyright, trade secret or other
- proprietary right. The entire risk as to the use, quality, and
- performance of any Source or Product shall be with You and not
- the Licensor. This disclaimer of warranty is an essential part
- of this Licence and a condition for the grant of any rights
- granted under this Licence.
-
- 6.2 EXCLUSION AND LIMITATION OF LIABILITY -- The Licensor shall, to
- the maximum extent permitted by law, have no liability for
- direct, indirect, special, incidental, consequential, exemplary,
- punitive or other damages of any character including, without
- limitation, procurement of substitute goods or services, loss of
- use, data or profits, or business interruption, however caused
- and on any theory of contract, warranty, tort (including
- negligence), product liability or otherwise, arising in any way
- in relation to the Covered Source, modified Covered Source
- and/or the Making or Conveyance of a Product, even if advised of
- the possibility of such damages, and You shall hold the
- Licensor(s) free and harmless from any liability, costs,
- damages, fees and expenses, including claims by third parties,
- in relation to such use.
-
-
-7 Patents
-
- 7.1 Subject to the terms and conditions of this Licence, each
- Licensor hereby grants to You a perpetual, worldwide,
- non-exclusive, no-charge, royalty-free, irrevocable (except as
- stated in subsections 7.2 and 8.4) patent license to Make, have
- Made, use, offer to sell, sell, import, and otherwise transfer
- the Covered Source and Products, where such licence applies only
- to those patent claims licensable by such Licensor that are
- necessarily infringed by exercising rights under the Covered
- Source as Conveyed by that Licensor.
-
- 7.2 If You institute patent litigation against any entity (including
- a cross-claim or counterclaim in a lawsuit) alleging that the
- Covered Source or a Product constitutes direct or contributory
- patent infringement, or You seek any declaration that a patent
- licensed to You under this Licence is invalid or unenforceable
- then any rights granted to You under this Licence shall
- terminate as of the date such process is initiated.
-
-
-8 General
-
- 8.1 If any provisions of this Licence are or subsequently become
- invalid or unenforceable for any reason, the remaining
- provisions shall remain effective.
-
- 8.2 You shall not use any of the name (including acronyms and
- abbreviations), image, or logo by which the Licensor or CERN is
- known, except where needed to comply with section 3, or where
- the use is otherwise allowed by law. Any such permitted use
- shall be factual and shall not be made so as to suggest any kind
- of endorsement or implication of involvement by the Licensor or
- its personnel.
-
- 8.3 CERN may publish updated versions and variants of this Licence
- which it considers to be in the spirit of this version, but may
- differ in detail to address new problems or concerns. New
- versions will be published with a unique version number and a
- variant identifier specifying the variant. If the Licensor has
- specified that a given variant applies to the Covered Source
- without specifying a version, You may treat that Covered Source
- as being released under any version of the CERN-OHL with that
- variant. If no variant is specified, the Covered Source shall be
- treated as being released under CERN-OHL-S. The Licensor may
- also specify that the Covered Source is subject to a specific
- version of the CERN-OHL or any later version in which case You
- may apply this or any later version of CERN-OHL with the same
- variant identifier published by CERN.
-
- 8.4 This Licence shall terminate with immediate effect if You fail
- to comply with any of its terms and conditions.
-
- 8.5 However, if You cease all breaches of this Licence, then Your
- Licence from any Licensor is reinstated unless such Licensor has
- terminated this Licence by giving You, while You remain in
- breach, a notice specifying the breach and requiring You to cure
- it within 30 days, and You have failed to come into compliance
- in all material respects by the end of the 30 day period. Should
- You repeat the breach after receipt of a cure notice and
- subsequent reinstatement, this Licence will terminate
- immediately and permanently. Section 6 shall continue to apply
- after any termination.
-
- 8.6 This Licence shall not be enforceable except by a Licensor
- acting as such, and third party beneficiary rights are
- specifically excluded.
diff --git a/options/license/CERN-OHL-W-2.0 b/options/license/CERN-OHL-W-2.0
deleted file mode 100644
index 0f882860af..0000000000
--- a/options/license/CERN-OHL-W-2.0
+++ /dev/null
@@ -1,310 +0,0 @@
-CERN Open Hardware Licence Version 2 - Weakly Reciprocal
-
-Preamble
-
-CERN has developed this licence to promote collaboration among
-hardware designers and to provide a legal tool which supports the
-freedom to use, study, modify, share and distribute hardware designs
-and products based on those designs. Version 2 of the CERN Open
-Hardware Licence comes in three variants: CERN-OHL-P (permissive); and
-two reciprocal licences: this licence, CERN- OHL-W (weakly reciprocal)
-and CERN-OHL-S (strongly reciprocal).
-
-The CERN-OHL-W is copyright CERN 2020. Anyone is welcome to use it, in
-unmodified form only.
-
-Use of this Licence does not imply any endorsement by CERN of any
-Licensor or their designs nor does it imply any involvement by CERN in
-their development.
-
-
-1 Definitions
-
- 1.1 'Licence' means this CERN-OHL-W.
-
- 1.2 'Compatible Licence' means
-
- a) any earlier version of the CERN Open Hardware licence, or
-
- b) any version of the CERN-OHL-S or the CERN-OHL-W, or
-
- c) any licence which permits You to treat the Source to which
- it applies as licensed under CERN-OHL-S or CERN-OHL-W
- provided that on Conveyance of any such Source, or any
- associated Product You treat the Source in question as being
- licensed under CERN-OHL-S or CERN-OHL-W as appropriate.
-
- 1.3 'Source' means information such as design materials or digital
- code which can be applied to Make or test a Product or to
- prepare a Product for use, Conveyance or sale, regardless of its
- medium or how it is expressed. It may include Notices.
-
- 1.4 'Covered Source' means Source that is explicitly made available
- under this Licence.
-
- 1.5 'Product' means any device, component, work or physical object,
- whether in finished or intermediate form, arising from the use,
- application or processing of Covered Source.
-
- 1.6 'Make' means to create or configure something, whether by
- manufacture, assembly, compiling, loading or applying Covered
- Source or another Product or otherwise.
-
- 1.7 'Available Component' means any part, sub-assembly, library or
- code which:
-
- a) is licensed to You as Complete Source under a Compatible
- Licence; or
-
- b) is available, at the time a Product or the Source containing
- it is first Conveyed, to You and any other prospective
- licensees
-
- i) with sufficient rights and information (including any
- configuration and programming files and information
- about its characteristics and interfaces) to enable it
- either to be Made itself, or to be sourced and used to
- Make the Product; or
- ii) as part of the normal distribution of a tool used to
- design or Make the Product.
-
- 1.8 'External Material' means anything (including Source) which:
-
- a) is only combined with Covered Source in such a way that it
- interfaces with the Covered Source using a documented
- interface which is described in the Covered Source; and
-
- b) is not a derivative of or contains Covered Source, or, if it
- is, it is solely to the extent necessary to facilitate such
- interfacing.
-
- 1.9 'Complete Source' means the set of all Source necessary to Make
- a Product, in the preferred form for making modifications,
- including necessary installation and interfacing information
- both for the Product, and for any included Available Components.
- If the format is proprietary, it must also be made available in
- a format (if the proprietary tool can create it) which is
- viewable with a tool available to potential licensees and
- licensed under a licence approved by the Free Software
- Foundation or the Open Source Initiative. Complete Source need
- not include the Source of any Available Component, provided that
- You include in the Complete Source sufficient information to
- enable a recipient to Make or source and use the Available
- Component to Make the Product.
-
- 1.10 'Source Location' means a location where a Licensor has placed
- Covered Source, and which that Licensor reasonably believes will
- remain easily accessible for at least three years for anyone to
- obtain a digital copy.
-
- 1.11 'Notice' means copyright, acknowledgement and trademark notices,
- Source Location references, modification notices (subsection
- 3.3(b)) and all notices that refer to this Licence and to the
- disclaimer of warranties that are included in the Covered
- Source.
-
- 1.12 'Licensee' or 'You' means any person exercising rights under
- this Licence.
-
- 1.13 'Licensor' means a natural or legal person who creates or
- modifies Covered Source. A person may be a Licensee and a
- Licensor at the same time.
-
- 1.14 'Convey' means to communicate to the public or distribute.
-
-
-2 Applicability
-
- 2.1 This Licence governs the use, copying, modification, Conveying
- of Covered Source and Products, and the Making of Products. By
- exercising any right granted under this Licence, You irrevocably
- accept these terms and conditions.
-
- 2.2 This Licence is granted by the Licensor directly to You, and
- shall apply worldwide and without limitation in time.
-
- 2.3 You shall not attempt to restrict by contract or otherwise the
- rights granted under this Licence to other Licensees.
-
- 2.4 This Licence is not intended to restrict fair use, fair dealing,
- or any other similar right.
-
-
-3 Copying, modifying and Conveying Covered Source
-
- 3.1 You may copy and Convey verbatim copies of Covered Source, in
- any medium, provided You retain all Notices.
-
- 3.2 You may modify Covered Source, other than Notices, provided that
- You irrevocably undertake to make that modified Covered Source
- available from a Source Location should You Convey a Product in
- circumstances where the recipient does not otherwise receive a
- copy of the modified Covered Source. In each case subsection 3.3
- shall apply.
-
- You may only delete Notices if they are no longer applicable to
- the corresponding Covered Source as modified by You and You may
- add additional Notices applicable to Your modifications.
-
- 3.3 You may Convey modified Covered Source (with the effect that You
- shall also become a Licensor) provided that You:
-
- a) retain Notices as required in subsection 3.2;
-
- b) add a Notice to the modified Covered Source stating that You
- have modified it, with the date and brief description of how
- You have modified it;
-
- c) add a Source Location Notice for the modified Covered Source
- if You Convey in circumstances where the recipient does not
- otherwise receive a copy of the modified Covered Source; and
-
- d) license the modified Covered Source under the terms and
- conditions of this Licence (or, as set out in subsection
- 8.3, a later version, if permitted by the licence of the
- original Covered Source). Such modified Covered Source must
- be licensed as a whole, but excluding Available Components
- contained in it or External Material to which it is
- interfaced, which remain licensed under their own applicable
- licences.
-
-
-4 Making and Conveying Products
-
- 4.1 You may Make Products, and/or Convey them, provided that You
- either provide each recipient with a copy of the Complete Source
- or ensure that each recipient is notified of the Source Location
- of the Complete Source. That Complete Source includes Covered
- Source and You must accordingly satisfy Your obligations set out
- in subsection 3.3. If specified in a Notice, the Product must
- visibly and securely display the Source Location on it or its
- packaging or documentation in the manner specified in that
- Notice.
-
- 4.2 Where You Convey a Product which incorporates External Material,
- the Complete Source for that Product which You are required to
- provide under subsection 4.1 need not include any Source for the
- External Material.
-
- 4.3 You may license Products under terms of Your choice, provided
- that such terms do not restrict or attempt to restrict any
- recipients' rights under this Licence to the Covered Source.
-
-
-5 Research and Development
-
-You may Convey Covered Source, modified Covered Source or Products to
-a legal entity carrying out development, testing or quality assurance
-work on Your behalf provided that the work is performed on terms which
-prevent the entity from both using the Source or Products for its own
-internal purposes and Conveying the Source or Products or any
-modifications to them to any person other than You. Any modifications
-made by the entity shall be deemed to be made by You pursuant to
-subsection 3.2.
-
-
-6 DISCLAIMER AND LIABILITY
-
- 6.1 DISCLAIMER OF WARRANTY -- The Covered Source and any Products
- are provided 'as is' and any express or implied warranties,
- including, but not limited to, implied warranties of
- merchantability, of satisfactory quality, non-infringement of
- third party rights, and fitness for a particular purpose or use
- are disclaimed in respect of any Source or Product to the
- maximum extent permitted by law. The Licensor makes no
- representation that any Source or Product does not or will not
- infringe any patent, copyright, trade secret or other
- proprietary right. The entire risk as to the use, quality, and
- performance of any Source or Product shall be with You and not
- the Licensor. This disclaimer of warranty is an essential part
- of this Licence and a condition for the grant of any rights
- granted under this Licence.
-
- 6.2 EXCLUSION AND LIMITATION OF LIABILITY -- The Licensor shall, to
- the maximum extent permitted by law, have no liability for
- direct, indirect, special, incidental, consequential, exemplary,
- punitive or other damages of any character including, without
- limitation, procurement of substitute goods or services, loss of
- use, data or profits, or business interruption, however caused
- and on any theory of contract, warranty, tort (including
- negligence), product liability or otherwise, arising in any way
- in relation to the Covered Source, modified Covered Source
- and/or the Making or Conveyance of a Product, even if advised of
- the possibility of such damages, and You shall hold the
- Licensor(s) free and harmless from any liability, costs,
- damages, fees and expenses, including claims by third parties,
- in relation to such use.
-
-
-7 Patents
-
- 7.1 Subject to the terms and conditions of this Licence, each
- Licensor hereby grants to You a perpetual, worldwide,
- non-exclusive, no-charge, royalty-free, irrevocable (except as
- stated in subsections 7.2 and 8.4) patent license to Make, have
- Made, use, offer to sell, sell, import, and otherwise transfer
- the Covered Source and Products, where such licence applies only
- to those patent claims licensable by such Licensor that are
- necessarily infringed by exercising rights under the Covered
- Source as Conveyed by that Licensor.
-
- 7.2 If You institute patent litigation against any entity (including
- a cross-claim or counterclaim in a lawsuit) alleging that the
- Covered Source or a Product constitutes direct or contributory
- patent infringement, or You seek any declaration that a patent
- licensed to You under this Licence is invalid or unenforceable
- then any rights granted to You under this Licence shall
- terminate as of the date such process is initiated.
-
-
-8 General
-
- 8.1 If any provisions of this Licence are or subsequently become
- invalid or unenforceable for any reason, the remaining
- provisions shall remain effective.
-
- 8.2 You shall not use any of the name (including acronyms and
- abbreviations), image, or logo by which the Licensor or CERN is
- known, except where needed to comply with section 3, or where
- the use is otherwise allowed by law. Any such permitted use
- shall be factual and shall not be made so as to suggest any kind
- of endorsement or implication of involvement by the Licensor or
- its personnel.
-
- 8.3 CERN may publish updated versions and variants of this Licence
- which it considers to be in the spirit of this version, but may
- differ in detail to address new problems or concerns. New
- versions will be published with a unique version number and a
- variant identifier specifying the variant. If the Licensor has
- specified that a given variant applies to the Covered Source
- without specifying a version, You may treat that Covered Source
- as being released under any version of the CERN-OHL with that
- variant. If no variant is specified, the Covered Source shall be
- treated as being released under CERN-OHL-S. The Licensor may
- also specify that the Covered Source is subject to a specific
- version of the CERN-OHL or any later version in which case You
- may apply this or any later version of CERN-OHL with the same
- variant identifier published by CERN.
-
- You may treat Covered Source licensed under CERN-OHL-W as
- licensed under CERN-OHL-S if and only if all Available
- Components referenced in the Covered Source comply with the
- corresponding definition of Available Component for CERN-OHL-S.
-
- 8.4 This Licence shall terminate with immediate effect if You fail
- to comply with any of its terms and conditions.
-
- 8.5 However, if You cease all breaches of this Licence, then Your
- Licence from any Licensor is reinstated unless such Licensor has
- terminated this Licence by giving You, while You remain in
- breach, a notice specifying the breach and requiring You to cure
- it within 30 days, and You have failed to come into compliance
- in all material respects by the end of the 30 day period. Should
- You repeat the breach after receipt of a cure notice and
- subsequent reinstatement, this Licence will terminate
- immediately and permanently. Section 6 shall continue to apply
- after any termination.
-
- 8.6 This Licence shall not be enforceable except by a Licensor
- acting as such, and third party beneficiary rights are
- specifically excluded.
diff --git a/options/license/CFITSIO b/options/license/CFITSIO
deleted file mode 100644
index f2c5020572..0000000000
--- a/options/license/CFITSIO
+++ /dev/null
@@ -1,7 +0,0 @@
-Copyright (Unpublished-all rights reserved under the copyright laws of the United States), U.S. Government as represented by the Administrator of the National Aeronautics and Space Administration. No copyright is claimed in the United States under Title 17, U.S. Code.
-
-Permission to freely use, copy, modify, and distribute this software and its documentation without fee is hereby granted, provided that this copyright notice and disclaimer of warranty appears in all copies.
-
-DISCLAIMER:
-
-THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT SHALL NASA BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, CONTRACT, TORT , OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER."
diff --git a/options/license/CGAL-linking-exception b/options/license/CGAL-linking-exception
deleted file mode 100644
index c6dbd55ca6..0000000000
--- a/options/license/CGAL-linking-exception
+++ /dev/null
@@ -1,4 +0,0 @@
-As a special exception, you have permission to link this library
-with the CGAL library (http://www.cgal.org) and distribute executables,
-as long as you follow the requirements of the GNU GPL in regard to
-all of the software in the executable aside from CGAL.
diff --git a/options/license/CLISP-exception-2.0 b/options/license/CLISP-exception-2.0
deleted file mode 100644
index 9c981f9b97..0000000000
--- a/options/license/CLISP-exception-2.0
+++ /dev/null
@@ -1,15 +0,0 @@
-Summary:
-
-This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; see file GNU-GPL.
-
-This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-Note:
-
-This copyright does NOT cover user programs that run in CLISP and third-party packages not part of CLISP, if a) They only reference external symbols in CLISP's public packages that define API also provided by many other Common Lisp implementations (namely the packages COMMON-LISP, COMMON-LISP-USER, KEYWORD, CLOS, GRAY, EXT), i.e. if they don't rely on CLISP internals and would as well run in any other Common Lisp implementation. Or b) They only reference external symbols in CLISP's public packages that define API also provided by many other Common Lisp implementations (namely the packages COMMON-LISP, COMMON-LISP-USER, KEYWORD, CLOS, GRAY, EXT) and some external, not CLISP specific, symbols in third-party packages that are released with source code under a GPL compatible license and that run in a great number of Common Lisp implementations, i.e. if they rely on CLISP internals only to the extent needed for gaining some functionality also available in a great number of Common Lisp implementations. Such user programs are not covered by the term """"derived work"""" used in the GNU GPL. Neither is their compiled code, i.e. the result of compiling them by use of the function COMPILE-FILE. We refer to such user programs as """"independent work"""".
-
-You may copy and distribute memory image files generated by the function SAVEINITMEM, if it was generated only from CLISP and independent work, and provided that you accompany them, in the sense of section 3 of the GNU GPL, with the source code of CLISP - precisely the same CLISP version that was used to build the memory image -, the source or compiled code of the user programs needed to rebuild the memory image (source code for all the parts that are not independent work, see above), and a precise description how to rebuild the memory image from these.
-
-Foreign non-Lisp code that is linked with CLISP or loaded into CLISP through dynamic linking is not exempted from this copyright. I.e. such code, when distributed for use with CLISP, must be distributed under the GPL.
diff --git a/options/license/CMU-Mach b/options/license/CMU-Mach
deleted file mode 100644
index 1bb895d4ec..0000000000
--- a/options/license/CMU-Mach
+++ /dev/null
@@ -1,22 +0,0 @@
-Copyright (c) 1991,1990,1989 Carnegie Mellon University
-All Rights Reserved.
-
-Permission to use, copy, modify and distribute this software and its
-documentation is hereby granted, provided that both the copyright
-notice and this permission notice appear in all copies of the
-software, derivative works or modified versions, and any portions
-thereof, and that both notices appear in supporting documentation.
-
-CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
-CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
-ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
-
-Carnegie Mellon requests users of this software to return to
-
- Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
- School of Computer Science
- Carnegie Mellon University
- Pittsburgh PA 15213-3890
-
-any improvements or extensions that they make and grant Carnegie Mellon
-the rights to redistribute these changes.
diff --git a/options/license/CMU-Mach-nodoc b/options/license/CMU-Mach-nodoc
deleted file mode 100644
index c81d74fee7..0000000000
--- a/options/license/CMU-Mach-nodoc
+++ /dev/null
@@ -1,11 +0,0 @@
-Copyright (C) 2002 Naval Research Laboratory (NRL/CCS)
-
-Permission to use, copy, modify and distribute this software and
-its documentation is hereby granted, provided that both the
-copyright notice and this permission notice appear in all copies of
-the software, derivative works or modified versions, and any
-portions thereof.
-
-NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND
-DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
-RESULTING FROM THE USE OF THIS SOFTWARE.
diff --git a/options/license/CNRI-Jython b/options/license/CNRI-Jython
deleted file mode 100644
index 0bfec82d07..0000000000
--- a/options/license/CNRI-Jython
+++ /dev/null
@@ -1,12 +0,0 @@
-
-1. This LICENSE AGREEMENT is between the Corporation for National Research Initiatives, having an office at 1895 Preston White Drive, Reston, VA 20191 ("CNRI"), and the Individual or Organization ("Licensee") accessing and using JPython version 1.1.x in source or binary form and its associated documentation as provided herein ("Software").
-
-2. Subject to the terms and conditions of this License Agreement, CNRI hereby grants Licensee a non-exclusive, non-transferable, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use the Software alone or in any derivative version, provided, however, that CNRI's License Agreement and CNRI's notice of copyright, i.e., “Copyright (c) 1996-1999 Corporation for National Research Initiatives; All Rights Reserved†are both retained in the Software, alone or in any derivative version prepared by Licensee.
-Alternatively, in lieu of CNRI's License Agreement, Licensee may substitute the following text (omitting the quotes), provided, however, that such text is displayed prominently in the Software alone or in any derivative version prepared by Licensee: “JPython (Version 1.1.x) is made available subject to the terms and conditions in CNRI's License Agreement. This Agreement may be located on the Internet using the following unique, persistent identifier (known as a handle): 1895.22/1006. The License may also be obtained from a proxy server on the Web using the following URL: http://hdl.handle.net/1895.22/1006.â€
-3. In the event Licensee prepares a derivative work that is based on or incorporates the Software or any part thereof, and wants to make the derivative work available to the public as provided herein, then Licensee hereby agrees to indicate in any such work, in a prominently visible way, the nature of the modifications made to CNRI's Software.
-4. Licensee may not use CNRI trademarks or trade name, including JPython or CNRI, in a trademark sense to endorse or promote products or services of Licensee, or any third party. Licensee may use the mark JPython in connection with Licensee's derivative versions that are based on or incorporate the Software, but only in the form “JPython-based ___________________,†or equivalent.
-5. CNRI is making the Software available to Licensee on an “AS IS†basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
-6. CNRI SHALL NOT BE LIABLE TO LICENSEE OR OTHER USERS OF THE SOFTWARE FOR ANY INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. SOME STATES DO NOT ALLOW THE LIMITATION OR EXCLUSION OF LIABILITY SO THE ABOVE DISCLAIMER MAY NOT APPLY TO LICENSEE.
-7. This License Agreement may be terminated by CNRI (i) immediately upon written notice from CNRI of any material breach by the Licensee, if the nature of the breach is such that it cannot be promptly remedied; or (ii) sixty (60) days following notice from CNRI to Licensee of a material remediable breach, if Licensee has not remedied such breach within that sixty-day period.
-8. This License Agreement shall be governed by and interpreted in all respects by the law of the State of Virginia, excluding conflict of law provisions. Nothing in this Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between CNRI and Licensee.
-9. By clicking on the "ACCEPT" button where indicated, or by installing, copying or otherwise using the Software, Licensee agrees to be bound by the terms and conditions of this License Agreement.
diff --git a/options/license/CNRI-Python b/options/license/CNRI-Python
deleted file mode 100644
index 64f1298e95..0000000000
--- a/options/license/CNRI-Python
+++ /dev/null
@@ -1,25 +0,0 @@
-CNRI OPEN SOURCE LICENSE AGREEMENT
-
-IMPORTANT: PLEASE READ THE FOLLOWING AGREEMENT CAREFULLY.
-
-BY CLICKING ON "ACCEPT" WHERE INDICATED BELOW, OR BY COPYING, INSTALLING OR OTHERWISE USING PYTHON 1.6, beta 1 SOFTWARE, YOU ARE DEEMED TO HAVE AGREED TO THE TERMS AND CONDITIONS OF THIS LICENSE AGREEMENT.
-
-1. This LICENSE AGREEMENT is between the Corporation for National Research Initiatives, having an office at 1895 Preston White Drive, Reston, VA 20191 ("CNRI"), and the Individual or Organization ("Licensee") accessing and otherwise using Python 1.6, beta 1 software in source or binary form and its associated documentation, as released at the www.python.org Internet site on August 4, 2000 ("Python 1.6b1").
-
-2. Subject to the terms and conditions of this License Agreement, CNRI hereby grants Licensee a non-exclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python 1.6b1 alone or in any derivative version, provided, however, that CNRIs License Agreement is retained in Python 1.6b1, alone or in any derivative version prepared by Licensee.
-
-Alternately, in lieu of CNRIs License Agreement, Licensee may substitute the following text (omitting the quotes): "Python 1.6, beta 1, is made available subject to the terms and conditions in CNRIs License Agreement. This Agreement may be located on the Internet using the following unique, persistent identifier (known as a handle): 1895.22/1011. This Agreement may also be obtained from a proxy server on the Internet using the URL:http://hdl.handle.net/1895.22/1011".
-
-3. In the event Licensee prepares a derivative work that is based on or incorporates Python 1.6b1 or any part thereof, and wants to make the derivative work available to the public as provided herein, then Licensee hereby agrees to indicate in any such work the nature of the modifications made to Python 1.6b1.
-
-4. CNRI is making Python 1.6b1 available to Licensee on an "AS IS" basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6b1 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
-
-5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF USING, MODIFYING OR DISTRIBUTING PYTHON 1.6b1, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-6. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
-
-7. This License Agreement shall be governed by and interpreted in all respects by the law of the State of Virginia, excluding conflict of law provisions. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between CNRI and Licensee. This License Agreement does not grant permission to use CNRI trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party.
-
-8. By clicking on the "ACCEPT" button where indicated, or by copying, installing or otherwise using Python 1.6b1, Licensee agrees to be bound by the terms and conditions of this License Agreement.
-
-ACCEPT
diff --git a/options/license/CNRI-Python-GPL-Compatible b/options/license/CNRI-Python-GPL-Compatible
deleted file mode 100644
index 2754c70e89..0000000000
--- a/options/license/CNRI-Python-GPL-Compatible
+++ /dev/null
@@ -1,23 +0,0 @@
-CNRI OPEN SOURCE GPL-COMPATIBLE LICENSE AGREEMENT
-
-IMPORTANT: PLEASE READ THE FOLLOWING AGREEMENT CAREFULLY.
-
-BY CLICKING ON "ACCEPT" WHERE INDICATED BELOW, OR BY COPYING, INSTALLING OR OTHERWISE USING PYTHON 1.6.1 SOFTWARE, YOU ARE DEEMED TO HAVE AGREED TO THE TERMS AND CONDITIONS OF THIS LICENSE AGREEMENT.
-
-1. This LICENSE AGREEMENT is between the Corporation for National Research Initiatives, having an office at 1895 Preston White Drive, Reston, VA 20191 ("CNRI"), and the Individual or Organization ("Licensee") accessing and otherwise using Python 1.6.1 software in source or binary form and its associated documentation.
-
-2. Subject to the terms and conditions of this License Agreement, CNRI hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python 1.6.1 alone or in any derivative version, provided, however, that CNRI's License Agreement and CNRI's notice of copyright, i.e., "Copyright © 1995-2001 Corporation for National Research Initiatives; All Rights Reserved" are retained in Python 1.6.1 alone or in any derivative version prepared by Licensee. Alternately, in lieu of CNRI's License Agreement, Licensee may substitute the following text (omitting the quotes): "Python 1.6.1 is made available subject to the terms and conditions in CNRI's License Agreement. This Agreement together with Python 1.6.1 may be located on the Internet using the following unique, persistent identifier (known as a handle): 1895.22/1013. This Agreement may also be obtained from a proxy server on the Internet using the following URL: http://hdl.handle.net/1895.22/1013".
-
-3. In the event Licensee prepares a derivative work that is based on or incorporates Python 1.6.1 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python 1.6.1.
-
-4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
-
-5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-6. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
-
-7. This License Agreement shall be governed by the federal intellectual property law of the United States, including without limitation the federal copyright law, and, to the extent such U.S. federal law does not apply, by the law of the Commonwealth of Virginia, excluding Virginia's conflict of law provisions. Notwithstanding the foregoing, with regard to derivative works based on Python 1.6.1 that incorporate non-separable material that was previously distributed under the GNU General Public License (GPL), the law of the Commonwealth of Virginia shall govern this License Agreement only as to issues arising under or with respect to Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between CNRI and Licensee. This License Agreement does not grant permission to use CNRI trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party.
-
-8. By clicking on the "ACCEPT" button where indicated, or by copying, installing or otherwise using Python 1.6.1, Licensee agrees to be bound by the terms and conditions of this License Agreement.
-
-ACCEPT
diff --git a/options/license/COIL-1.0 b/options/license/COIL-1.0
deleted file mode 100644
index c24c539e31..0000000000
--- a/options/license/COIL-1.0
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyfree Open Innovation License
-
-This is version 1.0 of the Copyfree Open Innovation License.
-
-## Terms and Conditions
-
-Redistributions, modified or unmodified, in whole or in part, must retain
-applicable notices of copyright or other legal privilege, these conditions, and
-the following license terms and disclaimer. Subject to these conditions, each
-holder of copyright or other legal privileges, author or assembler, and
-contributor of this work, henceforth "licensor", hereby grants to any person
-who obtains a copy of this work in any form:
-
-1. Permission to reproduce, modify, distribute, publish, sell, sublicense, use,
-and/or otherwise deal in the licensed material without restriction.
-
-2. A perpetual, worldwide, non-exclusive, royalty-free, gratis, irrevocable
-patent license to make, have made, provide, transfer, import, use, and/or
-otherwise deal in the licensed material without restriction, for any and all
-patents held by such licensor and necessarily infringed by the form of the work
-upon distribution of that licensor's contribution to the work under the terms
-of this license.
-
-NO WARRANTY OF ANY KIND IS IMPLIED BY, OR SHOULD BE INFERRED FROM, THIS LICENSE
-OR THE ACT OF DISTRIBUTION UNDER THE TERMS OF THIS LICENSE, INCLUDING BUT NOT
-LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
-AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS, ASSEMBLERS, OR HOLDERS OF
-COPYRIGHT OR OTHER LEGAL PRIVILEGE BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
-LIABILITY, WHETHER IN ACTION OF CONTRACT, TORT, OR OTHERWISE ARISING FROM, OUT
-OF, OR IN CONNECTION WITH THE WORK OR THE USE OF OR OTHER DEALINGS IN THE WORK.
diff --git a/options/license/CPAL-1.0 b/options/license/CPAL-1.0
deleted file mode 100644
index 0ed5b69013..0000000000
--- a/options/license/CPAL-1.0
+++ /dev/null
@@ -1,172 +0,0 @@
-Common Public Attribution License Version 1.0 (CPAL)
-
-1. “Definitionsâ€
-
-1.0.1 “Commercial Use†means distribution or otherwise making the Covered Code available to a third party.
-
-1.1 “Contributor†means each entity that creates or contributes to the creation of Modifications.
-
-1.2 “Contributor Version†means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
-1.3 “Covered Code†means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
-1.4 “Electronic Distribution Mechanism†means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
-1.5 “Executable†means Covered Code in any form other than Source Code.
-
-1.6 “Initial Developer†means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
-1.7 “Larger Work†means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
-1.8 “License†means this document.
-
-1.8.1 “Licensable†means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
-1.9 “Modifications†means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
-
-A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
-
-B. Any new file that contains any part of the Original Code or previous Modifications.
-
-1.10 “Original Code†means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
-1.10.1 “Patent Claims†means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
-1.11 “Source Code†means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor’s choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
-1.12 “You†(or “Yourâ€) means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, “You†includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, “control†means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2. Source Code License.
-
-2.1 The Initial Developer Grant.
-The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and
-
- (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).
-
- (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.
-
- (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices.
-
-2.2 Contributor Grant.
-Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license
-
- (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and
-
- (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
-
- (c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first makes Commercial Use of the Covered Code.
-
- (d) Notwithstanding Section 2.2(b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor.
-
-3. Distribution Obligations.
-
-3.1 Application of License.
-The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients’ rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
-3.2 Availability of Source Code.
-Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
-3.3 Description of Modifications.
-You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
-3.4 Intellectual Property Matters
-
- (a) Third Party Claims. If Contributor has knowledge that a license under a third party’s intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled “LEGAL†which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
-
- (b) Contributor APIs. If Contributor’s Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file.
-
- (c) Representations. Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor’s Modifications are Contributor’s original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.
-
-3.5 Required Notices.
-You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients’ rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
-3.6 Distribution of Executable Versions.
-You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients’ rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient’s rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer, Original Developer or any Contributor. You hereby agree to indemnify the Initial Developer, Original Developer and every Contributor for any liability incurred by the Initial Developer, Original Developer or such Contributor as a result of any such terms You offer.
-
-3.7 Larger Works.
-You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Application of this License.
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code.
-
-6. Versions of the License.
-
-6.1 New Versions.
-Socialtext, Inc. (“Socialtextâ€) may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
-6.2 Effect of New Versions.
-Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Socialtext. No one other than Socialtext has the right to modify the terms applicable to Covered Code created under this License.
-
-6.3 Derivative Works.
-If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases “Socialtextâ€, “CPAL†or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the CPAL. (Filling in the name of the Initial Developer, Original Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
-7. DISCLAIMER OF WARRANTY.
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN “AS IS†BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER, ORIGINAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-
-8.1 This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-8.2 If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer, Original Developer or a Contributor (the Initial Developer, Original Developer or Contributor against whom You file such action is referred to as “Participantâ€) alleging that:
-
- (a) such Participant’s Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.
-
- (b) any software, hardware, or device, other than such Participant’s Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant.
-
-8.3 If You assert a patent infringement claim against Participant alleging that such Participant’s Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
-8.4 In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY.
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ORIGINAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY’S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS.
-The Covered Code is a “commercial item,†as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of “commercial computer software†and “commercial computer software documentation,†as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
-11. MISCELLANEOUS.
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys’ fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-12. RESPONSIBILITY FOR CLAIMS.
-As between Initial Developer, Original Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer, Original Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-13. MULTIPLE-LICENSED CODE.
-Initial Developer may designate portions of the Covered Code as Multiple-Licensed. Multiple-Licensed means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the CPAL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A.
-
-14. ADDITIONAL TERM: ATTRIBUTION
-
- (a) As a modest attribution to the organizer of the development of the Original Code (“Original Developerâ€), in the hope that its promotional value may help justify the time, money and effort invested in writing the Original Code, the Original Developer may include in Exhibit B (“Attribution Informationâ€) a requirement that each time an Executable and Source Code or a Larger Work is launched or initially run (which includes initiating a session), a prominent display of the Original Developer’s Attribution Information (as defined below) must occur on the graphic user interface employed by the end user to access such Covered Code (which may include display on a splash screen), if any. The size of the graphic image should be consistent with the size of the other elements of the Attribution Information. If the access by the end user to the Executable and Source Code does not create a graphic user interface for access to the Covered Code, this obligation shall not apply. If the Original Code displays such Attribution Information in a particular form (such as in the form of a splash screen, notice at login, an “about†display, or dedicated attribution area on user interface screens), continued use of such form for that Attribution Information is one way of meeting this requirement for notice.
-
- (b) Attribution information may only include a copyright notice, a brief phrase, graphic image and a URL (“Attribution Informationâ€) and is subject to the Attribution Limits as defined below. For these purposes, prominent shall mean display for sufficient duration to give reasonable notice to the user of the identity of the Original Developer and that if You include Attribution Information or similar information for other parties, You must ensure that the Attribution Information for the Original Developer shall be no less prominent than such Attribution Information or similar information for the other party. For greater certainty, the Original Developer may choose to specify in Exhibit B below that the above attribution requirement only applies to an Executable and Source Code resulting from the Original Code or any Modification, but not a Larger Work. The intent is to provide for reasonably modest attribution, therefore the Original Developer cannot require that You display, at any time, more than the following information as Attribution Information: (a) a copyright notice including the name of the Original Developer; (b) a word or one phrase (not exceeding 10 words); (c) one graphic image provided by the Original Developer; and (d) a URL (collectively, the “Attribution Limitsâ€).
-
- (c) If Exhibit B does not include any Attribution Information, then there are no requirements for You to display any Attribution Information of the Original Developer.
-
- (d) You acknowledge that all trademarks, service marks and/or trade names contained within the Attribution Information distributed with the Covered Code are the exclusive property of their owners and may only be used with the permission of their owners, or under circumstances otherwise permitted by law or as expressly set out in this License.
-
-15. ADDITIONAL TERM: NETWORK USE.
-The term “External Deployment†means the use, distribution, or communication of the Original Code or Modifications in any way such that the Original Code or Modifications may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Code or Modifications as a distribution under section 3.1 and make Source Code available under Section 3.2.
-
-EXHIBIT A. Common Public Attribution License Version 1.0.
-
-“The contents of this file are subject to the Common Public Attribution License Version 1.0 (the “Licenseâ€); you may not use this file except in compliance with the License. You may obtain a copy of the License at _____________. The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover use of software over a computer network and provide for limited attribution for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B.
-Software distributed under the License is distributed on an “AS IS†basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-The Original Code is______________________.
-The Original Developer is not the Initial Developer and is __________. If left blank, the Original Developer is the Initial Developer.
-The Initial Developer of the Original Code is ____________. All portions of the code written by ___________ are Copyright (c) _____. All Rights Reserved.
-Contributor ______________________.
-Alternatively, the contents of this file may be used under the terms of the _____ license (the [___] License), in which case the provisions of [______] License are applicable instead of those above.
-If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the CPAL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the CPAL or the [___] License.â€
-
-[NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.]
-
-EXHIBIT B. Attribution Information
-
-Attribution Copyright Notice: _______________________
-Attribution Phrase (not exceeding 10 words): _______________________
-Attribution URL: _______________________
-Graphic Image as provided in the Covered Code, if any.
-Display of Attribution Information is [required/not required] in Larger Works which are defined in the CPAL as a work which combines Covered Code or portions thereof with code not governed by the terms of the CPAL.
diff --git a/options/license/CPL-1.0 b/options/license/CPL-1.0
deleted file mode 100644
index e1d8293bf8..0000000000
--- a/options/license/CPL-1.0
+++ /dev/null
@@ -1,87 +0,0 @@
-Common Public License Version 1.0
-
-THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
-
-1. DEFINITIONS
-
-"Contribution" means:
-
- a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
-
- b) in the case of each subsequent Contributor:
-
- i) changes to the Program, and
-
- ii) additions to the Program;
-
-where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
-
-"Contributor" means any person or entity that distributes the Program.
-
-"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
-
-"Program" means the Contributions distributed in accordance with this Agreement.
-
-"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
-
-2. GRANT OF RIGHTS
-
- a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
-
- b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
-
- c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
-
- d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
-
-3. REQUIREMENTS
-
-A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
-
- a) it complies with the terms and conditions of this Agreement; and
-
- b) its license agreement:
-
- i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
-
- ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
-
- iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
-
- iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
-
-When the Program is made available in source code form:
-
- a) it must be made available under this Agreement; and
-
- b) a copy of this Agreement must be included with each copy of the Program.
-
-Contributors may not remove or alter any copyright notices contained within the Program.
-
-Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
-
-4. COMMERCIAL DISTRIBUTION
-
-Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
-
-For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
-
-5. NO WARRANTY
-
-EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
-
-6. DISCLAIMER OF LIABILITY
-
-EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. GENERAL
-
-If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
-If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
-
-All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
-
-Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
-
-This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
diff --git a/options/license/CPOL-1.02 b/options/license/CPOL-1.02
deleted file mode 100644
index 9857e0003b..0000000000
--- a/options/license/CPOL-1.02
+++ /dev/null
@@ -1,98 +0,0 @@
-The Code Project Open License (CPOL) 1.02
-
-Preamble
-
-This License governs Your use of the Work. This License is intended to allow developers to use the Source Code and Executable Files provided as part of the Work in any application in any form.
-
-The main points subject to the terms of the License are:
-- Source Code and Executable Files can be used in commercial applications;
-- Source Code and Executable Files can be redistributed; and
-- Source Code can be modified to create derivative works.
-- No claim of suitability, guarantee, or any warranty whatsoever is provided. The software is provided "as-is".
-- The Article accompanying the Work may not be distributed or republished without the Author's consent
-
-This License is entered between You, the individual or other entity reading or otherwise making use of the Work licensed pursuant to this License and the individual or other entity which offers the Work under the terms of this License ("Author").
-
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CODE PROJECT OPEN LICENSE ("LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HEREIN, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE AUTHOR GRANTS YOU THE RIGHTS CONTAINED HEREIN IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. IF YOU DO NOT AGREE TO ACCEPT AND BE BOUND BY THE TERMS OF THIS LICENSE, YOU CANNOT MAKE ANY USE OF THE WORK.
-
-1. Definitions.
-
- a. "Articles" means, collectively, all articles written by Author which describes how the Source Code and Executable Files for the Work may be used by a user.
-
- b. "Author" means the individual or entity that offers the Work under the terms of this License.
-
- c. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works.
-
- d. "Executable Files" refer to the executables, binary files, configuration and any required data files included in the Work.
-
- e. "Publisher" means the provider of the website, magazine, CD-ROM, DVD or other medium from or by which the Work is obtained by You.
-
- f. "Source Code" refers to the collection of source code and configuration files used to create the Executable Files.
-
- g. "Standard Version" refers to such a Work if it has not been modified, or has been modified in accordance with the consent of the Author, such consent being in the full discretion of the Author.
-
- h. "Work" refers to the collection of files distributed by the Publisher, including the Source Code, Executable Files, binaries, data files, documentation, whitepapers and the Articles.
-
- i. "You" is you, an individual or entity wishing to use the Work and exercise your rights under this License.
-
-2. Fair Use/Fair Use Rights. Nothing in this License is intended to reduce, limit, or restrict any rights arising from fair use, fair dealing, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, the Author hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. You may use the standard version of the Source Code or Executable Files in Your own applications.
-
- b. You may apply bug fixes, portability fixes and other modifications obtained from the Public Domain or from the Author. A Work modified in such a way shall still be considered the standard version and will be subject to this License.
-
- c. You may otherwise modify Your copy of this Work (excluding the Articles) in any way to create a Derivative Work, provided that You insert a prominent notice in each changed file stating how, when and where You changed that file.
-
- d. You may distribute the standard version of the Executable Files and Source Code or Derivative Work in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution.
-
- e. The Articles discussing the Work published in any form by the author may not be distributed or republished without the Author's consent. The author retains copyright to any such Articles. You may use the Executable Files and Source Code pursuant to this License but you may not repost or republish or otherwise distribute or make available the Articles, without the prior written consent of the Author.
-
-Any subroutines or modules supplied by You and linked into the Source Code or Executable Files of this Work shall not be considered part of this Work and will not be subject to the terms of this License.
-
-4. Patent License. Subject to the terms and conditions of this License, each Author hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, import, and otherwise transfer the Work.
-
-5. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You agree not to remove any of the original copyright, patent, trademark, and attribution notices and associated disclaimers that may appear in the Source Code or Executable Files.
-
- b. You agree not to advertise or in any way imply that this Work is a product of Your own.
-
- c. The name of the Author may not be used to endorse or promote products derived from the Work without the prior written consent of the Author.
-
- d. You agree not to sell, lease, or rent any part of the Work. This does not restrict you from including the Work or any part of the Work inside a larger software distribution that itself is being sold. The Work by itself, though, cannot be sold, leased or rented.
-
- e. You may distribute the Executable Files and Source Code only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy of the Executable Files or Source Code You distribute and ensure that anyone receiving such Executable Files and Source Code agrees that the terms of this License apply to such Executable Files and/or Source Code. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute the Executable Files or Source Code with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License.
-
- f. You agree not to use the Work for illegal, immoral or improper purposes, or on pages containing illegal, immoral or improper material. The Work is subject to applicable export laws. You agree to comply with all such laws and regulations that may apply to the Work after Your receipt of the Work.
-
-6. Representations, Warranties and Disclaimer. THIS WORK IS PROVIDED "AS IS", "WHERE IS" AND "AS AVAILABLE", WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES OR CONDITIONS OR GUARANTEES. YOU, THE USER, ASSUME ALL RISK IN ITS USE, INCLUDING COPYRIGHT INFRINGEMENT, PATENT INFRINGEMENT, SUITABILITY, ETC. AUTHOR EXPRESSLY DISCLAIMS ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES OR CONDITIONS, INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY OR FITNESS FOR A PARTICULAR PURPOSE, OR ANY WARRANTY OF TITLE OR NON-INFRINGEMENT, OR THAT THE WORK (OR ANY PORTION THEREOF) IS CORRECT, USEFUL, BUG-FREE OR FREE OF VIRUSES. YOU MUST PASS THIS DISCLAIMER ON WHENEVER YOU DISTRIBUTE THE WORK OR DERIVATIVE WORKS.
-
-7. Indemnity. You agree to defend, indemnify and hold harmless the Author and the Publisher from and against any claims, suits, losses, damages, liabilities, costs, and expenses (including reasonable legal or attorneys' fees) resulting from or relating to any use of the Work by You.
-
-8. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL THE AUTHOR OR THE PUBLISHER BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK OR OTHERWISE, EVEN IF THE AUTHOR OR THE PUBLISHER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-9. Termination.
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of any term of this License. Individuals or entities who have received Derivative Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 6, 7, 8, 9, 10 and 11 will survive any termination of this License.
-
- b. If You bring a copyright, trademark, patent or any other infringement claim against any contributor over infringements You claim are made by the Work, your License from such contributor to the Work ends automatically.
-
- c. Subject to the above terms and conditions, this License is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, the Author reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-10. Publisher. The parties hereby confirm that the Publisher shall not, under any circumstances, be responsible for and shall not have any liability in respect of the subject matter of this License. The Publisher makes no warranty whatsoever in connection with the Work and shall not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. The Publisher reserves the right to cease making the Work available to You at any time without notice
-
-11. Miscellaneous
-
- a. This License shall be governed by the laws of the location of the head office of the Author or if the Author is an individual, the laws of location of the principal place of residence of the Author.
-
- b. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this License, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- c. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- d. This License constitutes the entire agreement between the parties with respect to the Work licensed herein. There are no understandings, agreements or representations with respect to the Work not specified herein. The Author shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Author and You.
diff --git a/options/license/CUA-OPL-1.0 b/options/license/CUA-OPL-1.0
deleted file mode 100644
index 15e60b24ca..0000000000
--- a/options/license/CUA-OPL-1.0
+++ /dev/null
@@ -1,143 +0,0 @@
-CUA Office Public License Version 1.0
-
-1. Definitions.
-
-1.0.1. "Commercial Use" means distribution or otherwise making the Covered Code available to a third party.
-
-1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications.
-
-1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
-1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
-1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
-1.5. "Executable" means Covered Code in any form other than Source Code.
-
-1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
-1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
-1.8. "License" means this document.
-
-1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
-1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
-
- A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
-
- B. Any new file that contains any part of the Original Code or previous Modifications.
-
-1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
-1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
-1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
-1.12. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2. Source Code License.
-
-2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and
-
- (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).
-
- (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.
-
- (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices.
-
-2.2. Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license
-
- (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and
-
- (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
-
- (c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first makes Commercial Use of the Covered Code.
-
- (d) Notwithstanding Section 2.2(b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor.
-
-3. Distribution Obligations.
-
-3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
-3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
-3.3. Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
-3.4. Intellectual Property Matters
-
- (a) Third Party Claims. If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
-
- (b) Contributor APIs. If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file.
-
- (c) Representations. Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.
-
-3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
-3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
-3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Application of this License.
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code.
-
-6. Versions of the License.
-
-6.1. New Versions. CUA Office Project may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
-6.2. Effect of New Versions. Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by CUA Office Project. No one other than CUA Office Project has the right to modify the terms applicable to Covered Code created under this License.
-
-6.3. Derivative Works. If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases "CUA Office", "CUA", "CUAPL", or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the CUA Office Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
-7. DISCLAIMER OF WARRANTY.
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-
-8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-8.2. If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant") alleging that:
-
- (a) such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.
-
- (b) any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant.
-
-8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
-8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY.
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS.
-The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
-11. MISCELLANEOUS.
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-12. RESPONSIBILITY FOR CLAIMS.
-As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-13. MULTIPLE-LICENSED CODE.
-Initial Developer may designate portions of the Covered Code as "Multiple-Licensed". "Multiple-Licensed" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the NPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A.
-
-EXHIBIT A - CUA Office Public License.
-
-"The contents of this file are subject to the CUA Office Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://cuaoffice.sourceforge.net/
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-The Original Code is ______________________________________.
-
-The Initial Developer of the Original Code is ________________________. Portions created by ______________________ are Copyright (C) ______ _______________________. All Rights Reserved.
-
-Contributor(s): ______________________________________.
-
-Alternatively, the contents of this file may be used under the terms of the _____ license (the "[___] License"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the CUAPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the CUAPL or the [___] License."
-
-[NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.]
diff --git a/options/license/Caldera b/options/license/Caldera
deleted file mode 100644
index 752ccc1110..0000000000
--- a/options/license/Caldera
+++ /dev/null
@@ -1,25 +0,0 @@
-Caldera International, Inc. hereby grants a fee free license that includes the rights use, modify and distribute this named source code, including creating derived binary products created from the source code. The source code for which Caldera International, Inc. grants rights are limited to the following UNIX Operating Systems that operate on the 16-Bit PDP-11 CPU and early versions of the 32-Bit UNIX Operating System, with specific exclusion of UNIX System III and UNIX System V and successor operating systems:
-
- 32-bit 32V UNIX
- 16 bit UNIX Versions 1, 2, 3, 4, 5, 6, 7
-
-Caldera International, Inc. makes no guarantees or commitments that any source code is available from Caldera
-International, Inc.
-
-The following copyright notice applies to the source code files for which this license is granted.
-
-Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- Redistributions of source code and documentation must retain the above copyright notice, this list of conditions and the following disclaimer.
-
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- All advertising materials mentioning features or use of this software must display the following acknowledgement:
-This product includes software developed or owned by Caldera International, Inc.
-
- Neither the name of Caldera International, Inc. nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/Caldera-no-preamble b/options/license/Caldera-no-preamble
deleted file mode 100644
index f70f34b32b..0000000000
--- a/options/license/Caldera-no-preamble
+++ /dev/null
@@ -1,35 +0,0 @@
-Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-Redistributions of source code and documentation must retain the above
-copyright notice, this list of conditions and the following disclaimer.
-
-Redistributions in binary form must reproduce the above copyright
-notice, this list of conditions and the following disclaimer in the
-documentation and/or other materials provided with the distribution.
-
-All advertising materials mentioning features or use of this software
-must display the following acknowledgement:
-
- This product includes software developed or owned by Caldera
- International, Inc.
-
-Neither the name of Caldera International, Inc. nor the names of other
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
-INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
-NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
-INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/Catharon b/options/license/Catharon
deleted file mode 100644
index 8d0ac128bc..0000000000
--- a/options/license/Catharon
+++ /dev/null
@@ -1,121 +0,0 @@
- The Catharon Open Source LICENSE
- ----------------------------
-
- 2000-Jul-04
-
- Copyright (C) 2000 by Catharon Productions, Inc.
-
-
-
-Introduction
-============
-
- This license applies to source files distributed by Catharon
- Productions, Inc. in several archive packages. This license
- applies to all files found in such packages which do not fall
- under their own explicit license.
-
- This license was inspired by the BSD, Artistic, and IJG
- (Independent JPEG Group) licenses, which all encourage inclusion
- and use of free software in commercial and freeware products
- alike. As a consequence, its main points are that:
-
- o We don't promise that this software works. However, we are
- interested in any kind of bug reports. (`as is' distribution)
-
- o You can use this software for whatever you want, in parts or
- full form, without having to pay us. (`royalty-free' usage)
-
- o You may not pretend that you wrote this software. If you use
- it, or only parts of it, in a program, you must acknowledge
- somewhere in your documentation that you have used the
- Catharon Code. (`credits')
-
- We specifically permit and encourage the inclusion of this
- software, with or without modifications, in commercial products.
- We disclaim all warranties covering the packages distributed by
- Catharon Productions, Inc. and assume no liability related to
- their use.
-
-
-Legal Terms
-===========
-
-0. Definitions
---------------
-
- Throughout this license, the terms `Catharon Package', `package',
- and `Catharon Code' refer to the set of files originally
- distributed by Catharon Productions, Inc.
-
- `You' refers to the licensee, or person using the project, where
- `using' is a generic term including compiling the project's source
- code as well as linking it to form a `program' or `executable'.
- This program is referred to as `a program using one of the
- Catharon Packages'.
-
- This license applies to all files distributed in the original
- Catharon Package(s), including all source code, binaries and
- documentation, unless otherwise stated in the file in its
- original, unmodified form as distributed in the original archive.
- If you are unsure whether or not a particular file is covered by
- this license, you must contact us to verify this.
-
- The Catharon Packages are copyright (C) 2000 by Catharon
- Productions, Inc. All rights reserved except as specified below.
-
-1. No Warranty
---------------
-
- THE CATHARON PACKAGES ARE PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
- KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
- BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OF OR THE INABILITY TO
- USE THE CATHARON PACKAGE.
-
-2. Redistribution
------------------
-
- This license grants a worldwide, royalty-free, perpetual and
- irrevocable right and license to use, execute, perform, compile,
- display, copy, create derivative works of, distribute and
- sublicense the Catharon Packages (in both source and object code
- forms) and derivative works thereof for any purpose; and to
- authorize others to exercise some or all of the rights granted
- herein, subject to the following conditions:
-
- o Redistribution of source code must retain this license file
- (`license.txt') unaltered; any additions, deletions or changes
- to the original files must be clearly indicated in
- accompanying documentation. The copyright notices of the
- unaltered, original files must be preserved in all copies of
- source files.
-
- o Redistribution in binary form must provide a disclaimer that
- states that the software is based in part on the work of
- Catharon Productions, Inc. in the distribution documentation.
-
- These conditions apply to any software derived from or based on
- the Catharon Packages, not just the unmodified files. If you use
- our work, you must acknowledge us. However, no fee need be paid
- to us.
-
-3. Advertising
---------------
-
- Neither Catharon Productions, Inc. and contributors nor you shall
- use the name of the other for commercial, advertising, or
- promotional purposes without specific prior written permission.
-
- We suggest, but do not require, that you use the following phrase
- to refer to this software in your documentation: 'this software is
- based in part on the Catharon Typography Project'.
-
- As you have not signed this license, you are not required to
- accept it. However, as the Catharon Packages are copyrighted
- material, only this license, or another one contracted with the
- authors, grants you the right to use, distribute, and modify it.
- Therefore, by using, distributing, or modifying the Catharon
- Packages, you indicate that you understand and accept all the
- terms of this license.
diff --git a/options/license/ClArtistic b/options/license/ClArtistic
deleted file mode 100644
index 1d7a2c288c..0000000000
--- a/options/license/ClArtistic
+++ /dev/null
@@ -1,61 +0,0 @@
-The Clarified Artistic License
-
-Preamble
-
-The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications.
-
-Definitions:
-
- "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification.
-
- "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder as specified below.
-
- "Copyright Holder" is whoever is named in the copyright or copyrights for the package.
-
- "You" is you, if you're thinking about copying or distributing this Package.
-
- "Distribution fee" is a fee you charge for providing a copy of this Package to another party.
-
- "Freely Available" means that no fee is charged for the right to use the item, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it.
-
-1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers.
-
-2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain, or those made Freely Available, or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version.
-
-3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following:
-
- a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major network archive site allowing unrestricted access to them, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package.
-
- b) use the modified Package only within your corporation or organization.
-
- c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version.
-
- d) make other distribution arrangements with the Copyright Holder.
-
- e) permit and encourge anyone who receives a copy of the modified Package permission to make your modifications Freely Available in some specific way.
-
-4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following:
-
- a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version.
-
- b) accompany the distribution with the machine-readable source of the Package with your modifications.
-
- c) give non-standard executables non-standard names, and clearly document the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version.
-
- d) make other distribution arrangements with the Copyright Holder.
-
- e) offer the machine-readable source of the Package, with your modifications, by mail order.
-
-5. You may charge a distribution fee for any distribution of this Package. If you offer support for this Package, you may charge any fee you choose for that support. You may not charge a license fee for the right to use this Package itself. You may distribute this Package in aggregate with other (possibly commercial and possibly nonfree) programs as part of a larger (possibly commercial and possibly nonfree) software distribution, and charge license fees for other parts of that software distribution, provided that you do not advertise this Package as a product of your own. If the Package includes an interpreter, You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded.
-
-6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whoever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package.
-
-7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language.
-
-8. Aggregation of the Standard Version of the Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package.
-
-9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission.
-
-10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-The End
diff --git a/options/license/Classpath-exception-2.0 b/options/license/Classpath-exception-2.0
deleted file mode 100644
index 1a0045415c..0000000000
--- a/options/license/Classpath-exception-2.0
+++ /dev/null
@@ -1,3 +0,0 @@
-Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License cover the whole combination.
-
-As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version.
diff --git a/options/license/Clips b/options/license/Clips
deleted file mode 100644
index ff5afdd293..0000000000
--- a/options/license/Clips
+++ /dev/null
@@ -1,15 +0,0 @@
-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, and/or sell copies of the Software, and to permit persons
-to whom the Software is furnished to do so.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
-OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
-CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/options/license/Community-Spec-1.0 b/options/license/Community-Spec-1.0
deleted file mode 100644
index cdf7c64c07..0000000000
--- a/options/license/Community-Spec-1.0
+++ /dev/null
@@ -1,293 +0,0 @@
-Community Specification License 1.0
-
-The Purpose of this License. This License sets forth the terms under which
-1) Contributor will participate in and contribute to the development
-of specifications, standards, best practices, guidelines, and other
-similar materials under this Working Group, and 2) how the materials
-developed under this License may be used. It is not intended for source
-code. Capitalized terms are defined in the License’s last section.
-
-1. Copyright.
-
-1.1. Copyright License. Contributor grants everyone a non-sublicensable,
-perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-(except as expressly stated in this License) copyright license, without
-any obligation for accounting, to reproduce, prepare derivative works
-of, publicly display, publicly perform, and distribute any materials
-it submits to the full extent of its copyright interest in those
-materials. Contributor also acknowledges that the Working Group may
-exercise copyright rights in the Specification, including the rights to
-submit the Specification to another standards organization.
-
-1.2. Copyright Attribution. As a condition, anyone exercising this
-copyright license must include attribution to the Working Group in any
-derivative work based on materials developed by the Working Group.
-That attribution must include, at minimum, the material’s name,
-version number, and source from where the materials were retrieved.
-Attribution is not required for implementations of the Specification.
-
-2. Patents.
-
-2.1. Patent License.
-
-2.1.1. As a Result of Contributions.
-
-2.1.1.1. As a Result of Contributions to Draft Specifications.
-Contributor grants Licensee a non-sublicensable, perpetual, worldwide,
-non-exclusive, no-charge, royalty-free, irrevocable (except as
-expressly stated in this License) license to its Necessary Claims in 1)
-Contributor’s Contributions and 2) to the Draft Specification that
-is within Scope as of the date of that Contribution, in both cases for
-Licensee’s Implementation of the Draft Specification, except for those
-patent claims excluded by Contributor under Section 3.
-
-2.1.1.2. For Approved Specifications. Contributor grants Licensee a
-non-sublicensable, perpetual, worldwide, non-exclusive, no-charge,
-royalty-free, irrevocable (except as expressly stated in this License)
-license to its Necessary Claims included the Approved Specification
-that are within Scope for Licensee’s Implementation of the Approved
-Specification, except for those patent claims excluded by Contributor
-under Section 3.
-
-2.1.2. Patent Grant from Licensee. Licensee grants each other Licensee
-a non-sublicensable, perpetual, worldwide, non-exclusive, no-charge,
-royalty-free, irrevocable (except as expressly stated in this License)
-license to its Necessary Claims for its Implementation, except for those
-patent claims excluded under Section 3.
-
-2.1.3. Licensee Acceptance. The patent grants set forth in Section 2.1
-extend only to Licensees that have indicated their agreement to this
-License as follows:
-
-2.1.3.1. Source Code Distributions. For distribution in source code,
-by including this License in the root directory of the source code with
-the Implementation;
-
-2.1.3.2. Non-Source Code Distributions. For distribution in any form
-other than source code, by including this License in the documentation,
-legal notices, via notice in the software, and/or other written materials
-provided with the Implementation; or
-
-2.1.3.3. Via Notices.md. By issuing pull request or commit to the
-Specification’s repository’s Notices.md file by the Implementer’s
-authorized representative, including the Implementer’s name, authorized
-individual and system identifier, and Specification version.
-
-2.1.4. Defensive Termination. If any Licensee files or maintains a
-claim in a court asserting that a Necessary Claim is infringed by an
-Implementation, any licenses granted under this License to the Licensee
-are immediately terminated unless 1) that claim is directly in response
-to a claim against Licensee regarding an Implementation, or 2) that claim
-was brought to enforce the terms of this License, including intervention
-in a third-party action by a Licensee.
-
-2.1.5. Additional Conditions. This License is not an assurance (i)
-that any of Contributor’s copyrights or issued patent claims cover
-an Implementation of the Specification or are enforceable or (ii) that
-an Implementation of the Specification would not infringe intellectual
-property rights of any third party.
-
-2.2. Patent Licensing Commitment. In addition to the rights granted
-in Section 2.1, Contributor agrees to grant everyone a no charge,
-royalty-free license on reasonable and non-discriminatory terms
-to Contributor’s Necessary Claims that are within Scope for:
-1) Implementations of a Draft Specification, where such license
-applies only to those Necessary Claims infringed by implementing
-Contributor's Contribution(s) included in that Draft Specification,
-and 2) Implementations of the Approved Specification.
-
-This patent licensing commitment does not apply to those claims subject
-to Contributor’s Exclusion Notice under Section 3.
-
-2.3. Effect of Withdrawal. Contributor may withdraw from the Working Group
-by issuing a pull request or commit providing notice of withdrawal to
-the Working Group repository’s Notices.md file. All of Contributor’s
-existing commitments and obligations with respect to the Working Group
-up to the date of that withdrawal notice will remain in effect, but no
-new obligations will be incurred.
-
-2.4. Binding Encumbrance. This License is binding on any future owner,
-assignee, or party who has been given the right to enforce any Necessary
-Claims against third parties.
-
-3. Patent Exclusion.
-
-3.1. As a Result of Contributions. Contributor may exclude Necessary
-Claims from its licensing commitments incurred under Section 2.1.1
-by issuing an Exclusion Notice within 45 days of the date of that
-Contribution. Contributor may not issue an Exclusion Notice for any
-material that has been included in a Draft Deliverable for more than 45
-days prior to the date of that Contribution.
-
-3.2. As a Result of a Draft Specification Becoming an Approved
-Specification. Prior to the adoption of a Draft Specification as an
-Approved Specification, Contributor may exclude Necessary Claims from
-its licensing commitments under this Agreement by issuing an Exclusion
-Notice. Contributor may not issue an Exclusion Notice for patents that
-were eligible to have been excluded pursuant to Section 3.1.
-
-4. Source Code License. Any source code developed by the Working Group is
-solely subject the source code license included in the Working Group’s
-repository for that code. If no source code license is included, the
-source code will be subject to the MIT License.
-
-5. No Other Rights. Except as specifically set forth in this License, no
-other express or implied patent, trademark, copyright, or other rights are
-granted under this License, including by implication, waiver, or estoppel.
-
-6. Antitrust Compliance. Contributor acknowledge that it may compete
-with other participants in various lines of business and that it is
-therefore imperative that they and their respective representatives
-act in a manner that does not violate any applicable antitrust laws and
-regulations. This License does not restrict any Contributor from engaging
-in similar specification development projects. Each Contributor may
-design, develop, manufacture, acquire or market competitive deliverables,
-products, and services, and conduct its business, in whatever way it
-chooses. No Contributor is obligated to announce or market any products
-or services. Without limiting the generality of the foregoing, the
-Contributors agree not to have any discussion relating to any product
-pricing, methods or channels of product distribution, division of markets,
-allocation of customers or any other topic that should not be discussed
-among competitors under the auspices of the Working Group.
-
-7. Non-Circumvention. Contributor agrees that it will not intentionally
-take or willfully assist any third party to take any action for the
-purpose of circumventing any obligations under this License.
-
-8. Representations, Warranties and Disclaimers.
-
-8.1. Representations, Warranties and Disclaimers. Contributor and Licensee
-represents and warrants that 1) it is legally entitled to grant the
-rights set forth in this License and 2) it will not intentionally include
-any third party materials in any Contribution unless those materials are
-available under terms that do not conflict with this License. IN ALL OTHER
-RESPECTS ITS CONTRIBUTIONS ARE PROVIDED "AS IS." The entire risk as to
-implementing or otherwise using the Contribution or the Specification
-is assumed by the implementer and user. Except as stated herein,
-CONTRIBUTOR AND LICENSEE EXPRESSLY DISCLAIM ANY WARRANTIES (EXPRESS,
-IMPLIED, OR OTHERWISE), INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY,
-NON-INFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, CONDITIONS OF QUALITY,
-OR TITLE, RELATED TO THE CONTRIBUTION OR THE SPECIFICATION. IN NO EVENT
-WILL ANY PARTY BE LIABLE TO ANY OTHER PARTY FOR LOST PROFITS OR ANY
-FORM OF INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF
-ANY CHARACTER FROM ANY CAUSES OF ACTION OF ANY KIND WITH RESPECT TO
-THIS AGREEMENT, WHETHER BASED ON BREACH OF CONTRACT, TORT (INCLUDING
-NEGLIGENCE), OR OTHERWISE, AND WHETHER OR NOT THE OTHER PARTY HAS BEEN
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Any obligations regarding
-the transfer, successors in interest, or assignment of Necessary Claims
-will be satisfied if Contributor or Licensee notifies the transferee
-or assignee of any patent that it knows contains Necessary Claims or
-necessary claims under this License. Nothing in this License requires
-Contributor to undertake a patent search. If Contributor is 1) employed by
-or acting on behalf of an employer, 2) is making a Contribution under the
-direction or control of a third party, or 3) is making the Contribution
-as a consultant, contractor, or under another similar relationship with
-a third party, Contributor represents that they have been authorized by
-that party to enter into this License on its behalf.
-
-8.2. Distribution Disclaimer. Any distributions of technical
-information to third parties must include a notice materially similar
-to the following: “THESE MATERIALS ARE PROVIDED “AS IS.†The
-Contributors and Licensees expressly disclaim any warranties (express,
-implied, or otherwise), including implied warranties of merchantability,
-non-infringement, fitness for a particular purpose, or title, related to
-the materials. The entire risk as to implementing or otherwise using the
-materials is assumed by the implementer and user. IN NO EVENT WILL THE
-CONTRIBUTORS OR LICENSEES BE LIABLE TO ANY OTHER PARTY FOR LOST PROFITS
-OR ANY FORM OF INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
-OF ANY CHARACTER FROM ANY CAUSES OF ACTION OF ANY KIND WITH RESPECT TO
-THIS DELIVERABLE OR ITS GOVERNING AGREEMENT, WHETHER BASED ON BREACH OF
-CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, AND WHETHER OR NOT
-THE OTHER MEMBER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.â€
-
-9. Definitions.
-
-9.1. Affiliate. “Affiliate†means an entity that directly or
-indirectly Controls, is Controlled by, or is under common Control of
-that party.
-
-9.2. Approved Specification. “Approved Specification†means the final
-version and contents of any Draft Specification designated as an Approved
-Specification as set forth in the accompanying Governance.md file.
-
-9.3. Contribution. “Contribution†means any original work of
-authorship, including any modifications or additions to an existing
-work, that Contributor submits for inclusion in a Draft Specification,
-which is included in a Draft Specification or Approved Specification.
-
-9.4. Contributor. “Contributor†means any person or entity that has
-indicated its acceptance of the License 1) by making a Contribution to
-the Specification, or 2) by entering into the Community Specification
-Contributor License Agreement for the Specification. Contributor includes
-its Affiliates, assigns, agents, and successors in interest.
-
-9.5. Control. “Control†means direct or indirect control of more
-than 50% of the voting power to elect directors of that corporation,
-or for any other entity, the power to direct management of such entity.
-
-9.6. Draft Specification. “Draft Specification†means all versions
-of the material (except an Approved Specification) developed by this
-Working Group for the purpose of creating, commenting on, revising,
-updating, modifying, or adding to any document that is to be considered
-for inclusion in the Approved Specification.
-
-9.7. Exclusion Notice. “Exclusion Notice†means a written notice
-made by making a pull request or commit to the repository’s Notices.md
-file that identifies patents that Contributor is excluding from its
-patent licensing commitments under this License. The Exclusion Notice
-for issued patents and published applications must include the Draft
-Specification’s name, patent number(s) or title and application
-number(s), as the case may be, for each of the issued patent(s) or
-pending patent application(s) that the Contributor is excluding from the
-royalty-free licensing commitment set forth in this License. If an issued
-patent or pending patent application that may contain Necessary Claims
-is not set forth in the Exclusion Notice, those Necessary Claims shall
-continue to be subject to the licensing commitments under this License.
-The Exclusion Notice for unpublished patent applications must provide
-either: (i) the text of the filed application; or (ii) identification
-of the specific part(s) of the Draft Specification whose implementation
-makes the excluded claim a Necessary Claim. If (ii) is chosen, the
-effect of the exclusion will be limited to the identified part(s) of
-the Draft Specification.
-
-9.8. Implementation. “Implementation†means making, using, selling,
-offering for sale, importing or distributing any implementation of the
-Specification 1) only to the extent it implements the Specification and 2)
-so long as all required portions of the Specification are implemented.
-
-9.9. License. “License†means this Community Specification License.
-
-9.10. Licensee. “Licensee†means any person or entity that has
-indicated its acceptance of the License as set forth in Section 2.1.3.
-Licensee includes its Affiliates, assigns, agents, and successors in
-interest.
-
-9.11. Necessary Claims. “Necessary Claims†are those patent claims, if
-any, that a party owns or controls, including those claims later acquired,
-that are necessary to implement the required portions (including the
-required elements of optional portions) of the Specification that are
-described in detail and not merely referenced in the Specification.
-
-9.12. Specification. “Specification†means a Draft Specification
-or Approved Specification included in the Working Group’s repository
-subject to this License, and the version of the Specification implemented
-by the Licensee.
-
-9.13. Scope. “Scope†has the meaning as set forth in the accompanying
-Scope.md file included in this Specification’s repository. Changes
-to Scope do not apply retroactively. If no Scope is provided, each
-Contributor’s Necessary Claims are limited to that Contributor’s
-Contributions.
-
-9.14. Working Group. “Working Group†means this project to develop
-specifications, standards, best practices, guidelines, and other similar
-materials under this License.
-
-
-
-The text of this Community Specification License is Copyright 2020
-Joint Development Foundation and is licensed under the Creative
-Commons Attribution 4.0 International License available at
-https://creativecommons.org/licenses/by/4.0/.
-
-SPDX-License-Identifier: CC-BY-4.0
diff --git a/options/license/Condor-1.1 b/options/license/Condor-1.1
deleted file mode 100644
index b6af3571f3..0000000000
--- a/options/license/Condor-1.1
+++ /dev/null
@@ -1,38 +0,0 @@
-Condor Public License
-
-Version 1.1, October 30, 2003
-
-Copyright © 1990-2006 Condor Team, Computer Sciences Department, University of Wisconsin-Madison, Madison, WI. All Rights Reserved. For more information contact: Condor Team, Attention: Professor Miron Livny, Dept of Computer Sciences, 1210 W. Dayton St., Madison, WI 53706-1685, (608) 262-0856 or miron@cs.wisc.edu.
-
-This software referred to as the Condor® Version 6.x software ("Software") was developed by the Condor Project, Condor Team, Computer Sciences Department, University of Wisconsin-Madison, under the authority of the Board of Regents of the University of Wisconsin System and includes voluntary contributions made to the Condor Project ("Copyright Holders and Contributors and the University"). For more information on the Condor Project, please see http://www.condorproject.org/.
-
-Installation, use, reproduction, display, modification and redistribution of this Software, with or without modification, in source and binary forms, are permitted. Any exercise of rights under this license including sublicenses by you is subject to the following conditions:
-
-1. Redistributions of this Software, with or without modification, must reproduce this Condor Public License in: (1) the Software, and (2) any user documentation or other similar material which is provided with the Software.
-
-2. Any user documentation included with a redistribution must include the following notice:
-``This product includes software from the Condor® Project (http://www.condorproject.org/)"
-Alternatively, if that is where third-party acknowledgments normally appear, this acknowledgment must be reproduced in the Software itself.
-3. Any academic report, publication, or other academic disclosure of results obtained with this Software will acknowledge this Software's use by an appropriate citation.
-
-4. The name Condor® is a registered trademark of the University of Wisconsin-Madison. The trademark may not be used to endorse or promote software, or products derived therefrom, and, other than as required by section 2 and 3 above, it may not be affixed to modified redistributions of this Software without the prior written approval, obtainable via email to condor-admin@cs.wisc.edu.
-
-5. To the extent that patent claims licensable by the University of Wisconsin-Madison are necessarily infringed by the use or sale of the Software, you are granted a non-exclusive, worldwide, royalty- free perpetual license under such patent claims, with the rights for you to make, use, sell, offer to sell, import and otherwise transfer the Software in source code and object code form and derivative works. This patent license shall apply to the combination of the Software with other software if, at the time the Software is added by you, such addition of the Software causes such combination to be covered by such patent claims. This patent license shall not apply to any other combinations which include the Software. No hardware per se is licensed hereunder.If you or any subsequent sub-licensee (a ``Recipient") institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Software infringes such Recipient's patent(s), then such Recipient's rights granted (directly or indirectly) under the patent license above shall terminate as of the date such litigation is filed. All sublicenses to the Software which have been properly granted prior to termination shall survive any termination of said patent license, if not otherwise terminated pursuant to this section.
-
-6. DISCLAIMER
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AND THE UNIVERSITY "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, OF SATISFACTORY QUALITY, AND FITNESS FOR A PARTICULAR PURPOSE OR USE ARE DISCLAIMED. THE COPYRIGHT HOLDERS AND CONTRIBUTORS AND THE UNIVERSITY MAKE NO REPRESENTATION THAT THE SOFTWARE, MODIFICATIONS, ENHANCEMENTS OR DERIVATIVE WORKS THEREOF, WILL NOT INFRINGE ANY PATENT, COPYRIGHT, TRADEMARK, TRADE SECRET OR OTHER PROPRIETARY RIGHT.
-7. LIMITATION OF LIABILITY
-THE COPYRIGHT HOLDERS AND CONTRIBUTORS AND ANY OTHER OFFICER, AGENT, OR EMPLOYEE OF THE UNIVERSITY SHALL HAVE NO LIABILITY TO LICENSEE OR OTHER PERSONS FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF USE, DATA OR PROFITS, OR BUSINESS INTERRUPTION, HOWEVER CAUSED AND ON ANY THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCT LIABILITY OR OTHERWISE, ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-8. Certain uses and transfers of the Software or documentation, and/or items or software incorporating the Condor Software or documentation, may require a license under U.S. Export Control laws. Licensee represents and warrants that all uses and transfers of the Condor Software or documentation and/or any items or software incorporating Condor shall be in compliance with U.S. Export Control laws, and Licensee further understands that failure to comply with such export control laws may result in criminal liability to Licensee under U.S. laws.
-
-9. The Condor Team may publish revised and/or new versions of this Condor Public License (``this License") from time to time. Each version will be given a distinguishing version number. Once Software has been published under a particular version of this License, you may always continue to use it under the terms of that version. You may also choose to use such Software under the terms of any subsequent version of this License published by the Condor Team. No one other than the Condor Team has the right to modify the terms of this License.
-
-For more information:
-
-Condor Team
-Attention: Professor Miron Livny
-7367 Computer Sciences
-1210 W. Dayton St.
-Madison, WI 53706-1685
-miron@cs.wisc.edu
-http://pages.cs.wisc.edu/~miron/miron.html
diff --git a/options/license/Cornell-Lossless-JPEG b/options/license/Cornell-Lossless-JPEG
deleted file mode 100644
index 7d2d44394d..0000000000
--- a/options/license/Cornell-Lossless-JPEG
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 1993 Cornell University, Kongji Huang
-All rights reserved.
-
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose, without fee, and without written
-agreement is hereby granted, provided that the above copyright notice
-and the following two paragraphs appear in all copies of this
-software.
-
-IN NO EVENT SHALL THE CORNELL UNIVERSITY BE LIABLE TO ANY PARTY FOR
-DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF CORNELL
-UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-THE CORNELL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
-INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
-PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND CORNELL UNIVERSITY HAS
-NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
-OR MODIFICATIONS.
diff --git a/options/license/Cronyx b/options/license/Cronyx
deleted file mode 100644
index 10fa8e7067..0000000000
--- a/options/license/Cronyx
+++ /dev/null
@@ -1,11 +0,0 @@
-This package contains a set of Russian fonts for X11 Release 6.
-Copyright (C) 1994-1995 Cronyx Ltd.
-Changes Copyright (C) 1996 by Sergey Vovk
-Changes Copyright (C) 1999-2000 by Serge Winitzki
-Changes Copyright (C) 1996-2000 by Andrey A. Chernov, Moscow, Russia.
-
-This software may be used, modified, copied, distributed, and sold,
-in both source and binary form provided that the copyright
-and these terms are retained. Under no circumstances is the author
-responsible for the proper functioning of this software, nor does
-the author assume any responsibility for damages incurred with its use.
diff --git a/options/license/Crossword b/options/license/Crossword
deleted file mode 100644
index 35d95a79d7..0000000000
--- a/options/license/Crossword
+++ /dev/null
@@ -1,5 +0,0 @@
-Copyright (C) 1995-2009 Gerd Neugebauer
-
-cwpuzzle.dtx is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to anyone for the consequences of using it or for whether it serves any particular purpose or works at all, unless he says so in writing.
-
-Everyone is granted permission to copy, modify and redistribute cwpuzzle.dtx, provided this copyright notice is preserved and any modifications are indicated.
diff --git a/options/license/CrystalStacker b/options/license/CrystalStacker
deleted file mode 100644
index 506361a956..0000000000
--- a/options/license/CrystalStacker
+++ /dev/null
@@ -1,7 +0,0 @@
-Crystal Stacker is freeware. This means you can pass copies around freely provided you include this document in it's original form in your distribution. Please see the "Contacting Us" section of this document if you need to contact us for any reason.
-
-Disclaimer
-
-NewCreature Design makes no guarantees regarding the Crystal Stacker software. We are not responsible for damages caused by it, though the software is not known to cause any problems. If you have trouble with the software, see the "Contacting Us" section of this document.
-
-The source code is provided as-is and you may do with it whatsoever you please provided that you include this file in its unmodified form with any new distribution. NewCreature Design makes no gaurantees regarding the usability of the source but are willing to help with any problems you might run into. Please see the "Contacting Us" section of this document if you need to get in touch with us about any issues you have regarding the source.
diff --git a/options/license/Cube b/options/license/Cube
deleted file mode 100644
index 0a9ea66eb3..0000000000
--- a/options/license/Cube
+++ /dev/null
@@ -1,17 +0,0 @@
-Cube game engine source code, 20 dec 2003 release.
-
-Copyright (C) 2001-2003 Wouter van Oortmerssen.
-
-This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
-
- 3. This notice may not be removed or altered from any source distribution.
-
-additional clause specific to Cube:
-
- 4. Source versions may not be "relicensed" under a different license without my explicitly written permission.
diff --git a/options/license/D-FSL-1.0 b/options/license/D-FSL-1.0
deleted file mode 100644
index b64a259c47..0000000000
--- a/options/license/D-FSL-1.0
+++ /dev/null
@@ -1,147 +0,0 @@
-Deutsche Freie Software Lizenz
-
-(c) Ministerium für Wissenschaft und Forschung Nordrhein-Westfalen 2004
-
-Erstellt von Axel Metzger und Till Jaeger, Institut für Rechtsfragen der Freien und Open Source Software - (http://www.ifross.de).
-
-Präambel
-
-Software ist mehr als ein Wirtschaftsgut. Sie ist die technische Grundlage der Informationsgesellschaft. Die Frage der Teilhabe der Allgemeinheit ist deswegen von besonderer Bedeutung. Herkömmlich lizenzierte Programme werden nur im Object Code vertrieben, der Nutzer darf das Programm weder verändern noch weitergeben. Das Lizenzmodell der Freien Software (synonym "Open Source Software") gewährt Ihnen dagegen umfassende Freiheiten im Umgang mit dem Programm. Die Deutsche Freie Software Lizenz folgt diesem Lizenzmodell. Sie gewährt Ihnen das Recht, das Programm in umfassender Weise zu nutzen. Es ist Ihnen gestattet, das Programm nach Ihren Vorstellungen zu verändern, in veränderter oder unveränderter Form zu vervielfältigen, zu verbreiten und öffentlich zugänglich zu machen. Diese Rechte werden unentgeltlich eingeräumt.
-
-Die Deutsche Freie Software Lizenz verbindet die Rechtseinräumung allerdings mit Pflichten, die dem Zweck dienen, das freie Zirkulieren des Programms und aller veröffentlichten Fortentwicklungen zu sichern. Wenn Sie das Programm verbreiten oder öffentlich zugänglich machen, dann müssen Sie jedem, der das Programm von Ihnen erhält, eine Kopie dieser Lizenz mitliefern und den Zugriff auf den Source Code ermöglichen. Eine weitere Pflicht betrifft Fortentwicklungen des Programms. Änderungen am Programm, die Sie öffentlich verbreiten oder zugänglich machen, müssen nach den Bestimmungen dieser Lizenz frei gegeben werden.
-
-Die Deutsche Freie Software Lizenz nimmt auf die besonderen Anforderungen des deutschen und europäischen Rechts Rücksicht. Sie ist zweisprachig gestaltet und damit auch auf den internationalen Vertrieb ausgerichtet.
-
-§ 0 Definitionen
-
-Dokumentation: Die Beschreibung des Aufbaus und/oder der Struktur der Programmierung und/oder der Funktionalitäten des Programms, unabhängig davon, ob sie im Source Code oder gesondert vorgenommen wird.
-
-Lizenz: Die zwischen dem Lizenzgeber und Ihnen geschlossene Vereinbarung mit dem Inhalt der "Deutschen Freien Software Lizenz" bzw. das Angebot hierzu.
-
-Lizenznehmer: Jede natürliche oder juristische Person, die die Lizenz angenommen hat.
-
-Programm: Jedes Computerprogramm, das von den Rechtsinhabern nach den Bestimmungen dieser Lizenz verbreitet oder öffentlich zugänglich gemacht worden ist.
-
-Object Code: Die maschinenlesbare, übersetzte Form des Programms.
-
-Öffentlich: Nicht nur an einen bestimmten Personenkreis gerichtet, der persönlich oder durch die Zugehörigkeit zu einer juristischen Person oder einem öffentlichen Träger miteinander verbunden ist.
-
-Öffentlich zugänglich machen: Die öffentliche Weitergabe des Programms in unkörperlicher Form, insbesondere das Bereithalten zum Download in Datennetzen.
-
-Rechtsinhaber: Der bzw. die Urheber oder sonstigen Inhaber der ausschließlichen Nutzungsrechte an dem Programm.
-
-Source Code: Die für Menschen lesbare, in Programmiersprache dargestellte Form des Programms.
-
-Verändern: Jede Erweiterung, Kürzung und Bearbeitung des Programms, insbesondere Weiterentwicklungen.
-
-Verbreiten: Die öffentliche Weitergabe körperlicher Vervielfältigungsstücke, insbesondere auf Datenträgern oder in Verbindung mit Hardware.
-
-Vollständiger Source Code: Der Source Code in der für die Erstellung bzw. die Bearbeitung benutzten Form zusammen mit den zur Übersetzung und Installation erforderlichen Konfigurationsdateien und Software-Werkzeugen, sofern diese in der benötigten Form nicht allgemein gebräuchlich (z.B. Standard-Kompiler) oder für jedermann lizenzgebührenfrei im Internet abrufbar sind.
-
-§ 1 Rechte
-
-(1) Sie dürfen das Programm in unveränderter Form vervielfältigen, verbreiten und öffentlich zugänglich machen.
-
-(2) Sie dürfen das Programm verändern und entsprechend veränderte Versionen vervielfältigen, verbreiten und öffentlich zugänglich machen. Gestattet ist auch die Kombination des Programms oder Teilen hiervon mit anderen Programmen.
-
-(3) Sie erhalten die Rechte unentgeltlich.
-
-§ 2 Pflichten beim Vertrieb
-
-(1) Wenn Sie das Programm verbreiten oder öffentlich zugänglich machen, sei es in unveränderter oder veränderter Form, sei es in einer Kombination mit anderen Programmen oder in Verbindung mit Hardware, dann müssen sie mitliefern:
-1. alle Vermerke im Source Code und/oder Object Code, die auf diese Lizenz hinweisen;
-2. alle Vermerke im Source Code und/oder Object Code, die über die Urheber des Programms Auskunft geben;
-3. einen für den Empfänger deutlich wahrnehmbaren Hinweis auf diese Lizenz und die Internetadresse http://www.d-fsl.de;
-4. den vollständigen Text dieser Lizenz in deutlich wahrnehmbarer Weise.
-
-(2) Wenn bei der Installation des Programms und/oder beim Programmstart Lizenz- und/oder Vertragsbedingungen angezeigt werden, dann müssen
-1. diese Lizenz,
-2. ein Hinweis auf diese Lizenz und
-3. ein Hinweis auf den oder die Rechtsinhaber an den ersten unter dieser Lizenz nutzbaren Programmbestandteilen
-ebenfalls angezeigt werden.
-
-(3) Sie dürfen die Nutzung des Programms nicht von Pflichten oder Bedingungen abhängig machen, die nicht in dieser Lizenz vorgesehen sind.
-
-(4) Sofern Sie mit dem Programm eine Dokumentation erhalten haben, muss diese Dokumentation entsprechend mitgeliefert werden, es sei denn, die freie Mitlieferung der Dokumentation ist Ihnen aufgrund der Lizenz für die Dokumentation nicht gestattet.
-
-§ 3 Weitere Pflichten beim Vertrieb veränderter Versionen
-
-(1) Veränderte Versionen des Programms dürfen Sie nur unter den Bedingungen dieser Lizenz verbreiten oder öffentlich zugänglich machen, so dass Dritte das veränderte Programm insgesamt unter dieser Lizenz nutzen können.
-
-(2) Wird das Programm oder ein Teil hiervon mit einem anderen Programm kombiniert, gilt auch die Kombination insgesamt als eine veränderte Version des Programms, es sei denn, das andere Programm ist formal und inhaltlich eigenständig. Ein anderes Programm ist dann als eigenständig anzusehen, wenn es die folgenden Voraussetzungen alle erfüllt:
-1. Der Source Code der kombinierten Programme muss jeweils in eigenen Dateien vorhanden sein, die keine Bestandteile des anderen Teils enthalten, die über die zur Programmkombination üblichen und erforderlichen Informationen über den anderen Teil hinausgehen, wobei der Source Code des anderen Programms nicht mitgeliefert werden muss.
-2. Der mit dem Programm kombinierte Teil muss auch dann sinnvoll nutzbar sein, wenn er nicht mit dem Programm kombiniert wird, und zwar entweder alleine oder mit sonstigen Programmen. Was als "sinnvoll nutzbar" anzusehen ist, richtet sich nach der Auffassung der betroffenen Fachkreise. Zu den betroffenen Fachkreisen gehören alle Personen, die das Programm oder Programme mit vergleichbarer Funktionalität entwickeln, benutzen, verbreiten oder öffentlich zugänglich machen.
-
-(3) Wenn Sie das Programm oder einen Teil hiervon - verändert oder unverändert - zusammen mit einem anderen Programm verbreiten oder öffentlich zugänglich machen, das unter der GNU General Public License (GPL) lizenziert wird, darf das Programm auch unter den Bedingungen der GPL genutzt werden, sofern es mit dem anderen Programm ein "derivative work" im Sinne der GPL bildet. Dabei sollen die Hinweise auf diese Lizenz entfernt und durch einen Hinweis auf die GPL ersetzt werden. Ob bei der Zusammenstellung ein "derivate work" im Sinne der GPL entsteht, beurteilt sich nach Ziffer 2 b) der GPL. Diese Bestimmung lautet: "You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License." Die GPL kann abgerufen werden unter http://www.fsf.org/licenses/gpl.
-
-(4) Wenn Sie das Programm in einer veränderten Form verbreiten oder öffentlich zugänglich machen, müssen Sie im Source Code einen Hinweis mit den Änderungen aufnehmen und mit dem Datum der Änderung versehen. Der Hinweis muss erkennen lassen, welche Änderungen vorgenommen wurden und bestehende Vermerke, die über die Urheber des Programms Auskunft geben, übernehmen. Dies gilt unabhängig davon, ob Sie einen eigenen Urhebervermerk hinzufügen. Anstelle eines Hinweises im Source Code können Sie auch ein Versionskontrollsystem verwenden oder weiterführen, sofern dieses mitverbreitet wird oder öffentlich zugänglich ist.
-
-(5) Sie dürfen von Dritten für die Einräumung eines einfachen Nutzungsrechts an veränderten Versionen des Programms kein Entgelt verlangen.
-
-(6) Wenn Sie an der veränderten Version des Programms ein anderes Schutzrecht als ein Urheberrecht erwerben, insbesondere ein Patent oder Gebrauchsmuster, lizenzieren Sie dieses Schutzrecht für veränderte und unveränderte Versionen des Programms in dem Umfang, der erforderlich ist, um die Rechte aus dieser Lizenz wahrnehmen zu können.
-
-§ 4 Weitere Pflichten beim Vertrieb im Object Code
-
-(1) Wenn Sie das Programm nur im Object Code verbreiten, dann müssen Sie zusätzlich zu den in § 2 und § 3 geregelten Pflichten entweder
-1. den vollständigen Source Code im Internet öffentlich zugänglich machen und bei der Verbreitung des Object Codes deutlich auf die vollständige Internetadresse hinweisen, unter der der Source Code abgerufen werden kann oder
-2. den vollständigen Source Code auf einem hierfür üblichen Datenträger unter Beachtung der §§ 2 und 3 mitverbreiten.
-
-(2) Wenn Sie das Programm im Object Code öffentlich zugänglich machen, dann müssen Sie zusätzlich zu den in § 2 und § 3 geregelten Pflichten den vollständigen Source Code im Internet öffentlich zugänglich machen und dabei deutlich auf die vollständige Internetadresse hinweisen.
-
-(3) Sofern Sie mit dem Programm eine Dokumentation erhalten haben, muss diese Dokumentation entsprechend der Absätze 1 und 2 mitgeliefert werden, es sei denn, die freie Mitlieferung der Dokumentation ist Ihnen aufgrund der Lizenz für die Dokumentation nicht gestattet.
-
-§ 5 Vertragsschluss
-
-(1) Mit dieser Lizenz wird Ihnen und jeder anderen Person ein Angebot auf Abschluss eines Vertrages über die Nutzung des Programms unter den Bedingungen der Deutschen Freien Softwarelizenz unterbreitet.
-
-(2) Sie dürfen das Programm nach den jeweils anwendbaren gesetzlichen Vorschriften bestimmungsgemäß benutzen, ohne dass es der Annahme dieser Lizenz bedarf. Dieses Recht umfasst in der Europäischen Union und in den meisten anderen Rechtsordnungen insbesondere die folgenden Befugnisse:
-1. das Programm ablaufen zu lassen sowie die Erstellung von hierfür erforderlichen Vervielfältigungen im Haupt- und Arbeitsspeicher;
-2. das Erstellen einer Sicherungskopie;
-3. die Fehlerberichtigung;
-4. die Weitergabe einer rechtmäßig erworbenen körperlichen Kopie des Programms.
-
-(3) Sie erklären Ihre Zustimmung zum Abschluss dieser Lizenz, indem Sie das Programm verbreiten, öffentlich zugänglich machen, verändern oder in einer Weise vervielfältigen, die über die bestimmungsgemäße Nutzung im Sinne von Absatz 2 hinausgeht. Ab diesem Zeitpunkt ist diese Lizenz als rechtlich verbindlicher Vertrag zwischen den Rechtsinhabern und Ihnen geschlossen, ohne dass es eines Zugangs der Annahmeerklärung bei den Rechtsinhabern bedarf.
-
-(4) Sie und jeder andere Lizenznehmer erhalten die Rechte aus dieser Lizenz direkt von den Rechtsinhabern. Eine Unterlizenzierung oder Übertragung der Rechte ist nicht gestattet.
-
-§ 6 Beendigung der Rechte bei Zuwiderhandlung
-
-(1) Jede Verletzung Ihrer Verpflichtungen aus dieser Lizenz führt zu einer automatischen Beendigung Ihrer Rechte aus dieser Lizenz.
-
-(2) Die Rechte Dritter, die das Programm oder Rechte an dem Programm von Ihnen erhalten haben, bleiben hiervon unberührt.
-
-§ 7 Haftung und Gewährleistung
-
-(1) Für entgegenstehende Rechte Dritter haften die Rechtsinhaber nur, sofern sie Kenntnis von diesen Rechten hatten, ohne Sie zu informieren.
-
-(2) Die Haftung für Fehler und sonstige Mängel des Programms richtet sich nach den außerhalb dieser Lizenz getroffenen Vereinbarungen zwischen Ihnen und den Rechtsinhabern oder, wenn eine solche Vereinbarung nicht existiert, nach den gesetzlichen Regelungen.
-
-§ 8 Verträge mit Dritten
-
-(1) Diese Lizenz regelt nur die Beziehung zwischen Ihnen und den Rechtsinhabern. Sie ist nicht Bestandteil der Verträge zwischen Ihnen und Dritten.
-
-(2) Die Lizenz beschränkt Sie nicht in der Freiheit, mit Dritten, die von Ihnen Kopien des Programms erhalten oder Leistungen in Anspruch nehmen, die im Zusammenhang mit dem Programm stehen, Verträge beliebigen Inhalts zu schließen, sofern Sie dabei Ihren Verpflichtungen aus dieser Lizenz nachkommen und die Rechte der Dritten aus dieser Lizenz nicht beeinträchtigt werden. Insbesondere dürfen Sie für die Überlassung des Programms oder sonstige Leistungen ein Entgelt verlangen.
-
-(3) Diese Lizenz verpflichtet Sie nicht, das Programm an Dritte weiterzugeben. Es steht Ihnen frei zu entscheiden, wem Sie das Programm zugänglich machen. Sie dürfen aber die weitere Nutzung durch Dritte nicht durch den Einsatz technischer Schutzmaßnahmen, insbesondere durch den Einsatz von Kopierschutzvorrichtungen jeglicher Art, verhindern oder erschweren. Eine passwortgeschützte Zugangsbeschränkung oder die Nutzung in einem Intranet wird nicht als technische Schutzmaßnahme angesehen.
-
-§ 9 Text der Lizenz
-
-(1) Diese Lizenz ist in deutscher und englischer Sprache abgefasst. Beide Fassungen sind gleich verbindlich. Es wird unterstellt, dass die in der Lizenz verwandten Begriffe in beiden Fassungen dieselbe Bedeutung haben. Ergeben sich dennoch Unterschiede, so ist die Bedeutung maßgeblich, welche die Fassungen unter Berücksichtigung des Ziels und Zwecks der Lizenz am besten miteinander in Einklang bringt.
-
-(2) Der Lizenzrat der Deutschen Freien Software Lizenz kann mit verbindlicher Wirkung neue Versionen der Lizenz in Kraft setzen, soweit dies erforderlich und zumutbar ist. Neue Versionen der Lizenz werden auf der Internetseite http://www.d-fsl.de mit einer eindeutigen Versionsnummer veröffentlicht. Die neue Version der Lizenz erlangt für Sie verbindliche Wirkung, wenn Sie von deren Veröffentlichung Kenntnis genommen haben. Gesetzliche Rechtsbehelfe gegen die Änderung der Lizenz werden durch die vorstehenden Bestimmungen nicht beschränkt.
-
-(3) Sie dürfen diese Lizenz in unveränderter Form vervielfältigen, verbreiten und öffentlich zugänglich machen.
-
-§ 10 Anwendbares Recht
-
-Auf diese Lizenz findet deutsches Recht Anwendung.
-
-
-Anhang: Wie unterstellen Sie ein Programm der Deutschen Freien Software Lizenz?
-Um jedermann den Abschluss dieser Lizenz zu ermöglichen, wird empfohlen, das Programm mit folgendem Hinweis auf die Lizenz zu versehen:
-
-"Copyright (C) 20[jj] [Name des Rechtsinhabers].
-
-Dieses Programm kann durch jedermann gemäß den Bestimmungen der Deutschen Freien Software Lizenz genutzt werden.
-
-Die Lizenz kann unter http://www.d-fsl.de abgerufen werden."
diff --git a/options/license/DEC-3-Clause b/options/license/DEC-3-Clause
deleted file mode 100644
index 112edaa70d..0000000000
--- a/options/license/DEC-3-Clause
+++ /dev/null
@@ -1,28 +0,0 @@
-Copyright 1997 Digital Equipment Corporation.
-All rights reserved.
-
-This software is furnished under license and may be used and copied only in
-accordance with the following terms and conditions. Subject to these
-conditions, you may download, copy, install, use, modify and distribute
-this software in source and/or binary form. No title or ownership is
-transferred hereby.
-
-1) Any source code used, modified or distributed must reproduce and retain
- this copyright notice and list of conditions as they appear in the
- source file.
-
-2) No right is granted to use any trade name, trademark, or logo of Digital
- Equipment Corporation. Neither the "Digital Equipment Corporation"
- name nor any trademark or logo of Digital Equipment Corporation may be
- used to endorse or promote products derived from this software without
- the prior written permission of Digital Equipment Corporation.
-
-3) This software is provided "AS-IS" and any express or implied warranties,
- including but not limited to, any implied warranties of merchantability,
- fitness for a particular purpose, or non-infringement are disclaimed.
- In no event shall DIGITAL be liable for any damages whatsoever, and in
- particular, DIGITAL shall not be liable for special, indirect,
- consequential, or incidental damages or damages for lost profits, loss
- of revenue or loss of use, whether such damages arise in contract,
- negligence, tort, under statute, in equity, at law or otherwise, even
- if advised of the possibility of such damage.
diff --git a/options/license/DL-DE-BY-2.0 b/options/license/DL-DE-BY-2.0
deleted file mode 100644
index 20c3a19c2f..0000000000
--- a/options/license/DL-DE-BY-2.0
+++ /dev/null
@@ -1,45 +0,0 @@
-DL-DE->BY-2.0
-Datenlizenz Deutschland – Namensnennung – Version 2.0
-
-(1) Jede Nutzung ist unter den Bedingungen dieser „Datenlizenz Deutschland – Namensnennung – Version 2.0" zulässig.
-
-Die bereitgestellten Daten und Metadaten dürfen für die kommerzielle und nicht kommerzielle Nutzung insbesondere
-
- vervielfältigt, ausgedruckt, präsentiert, verändert, bearbeitet sowie an Dritte übermittelt werden;
- mit eigenen Daten und Daten Anderer zusammengeführt und zu selbständigen neuen Datensätzen verbunden werden;
- in interne und externe Geschäftsprozesse, Produkte und Anwendungen in öffentlichen und nicht öffentlichen elektronischen Netzwerken eingebunden werden.
-
-(2) Bei der Nutzung ist sicherzustellen, dass folgende Angaben als Quellenvermerk enthalten sind:
-
- Bezeichnung des Bereitstellers nach dessen Maßgabe,
- der Vermerk „Datenlizenz Deutschland – Namensnennung – Version 2.0" oder „dl-de/by-2-0" mit Verweis auf den Lizenztext unter www.govdata.de/dl-de/by-2-0 sowie
- einen Verweis auf den Datensatz (URI).
-
-Dies gilt nur soweit die datenhaltende Stelle die Angaben 1. bis 3. zum Quellenvermerk bereitstellt.
-
-(3) Veränderungen, Bearbeitungen, neue Gestaltungen oder sonstige Abwandlungen sind im Quellenvermerk mit dem Hinweis zu versehen, dass die Daten geändert wurden.
-
-
-Data licence Germany – attribution – version 2.0
-
-(1) Any use will be permitted provided it fulfils the requirements of this "Data licence Germany – attribution – Version 2.0".
-
-The data and meta-data provided may, for commercial and non-commercial use, in particular
-
- be copied, printed, presented, altered, processed and transmitted to third parties;
- be merged with own data and with the data of others and be combined to form new and independent datasets;
- be integrated in internal and external business processes, products and applications in public and non-public electronic networks.
-
-(2) The user must ensure that the source note contains the following information:
-
- the name of the provider,
- the annotation "Data licence Germany – attribution – Version 2.0" or "dl-de/by-2-0" referring to the licence text available at www.govdata.de/dl-de/by-2-0, and
- a reference to the dataset (URI).
-
-This applies only if the entity keeping the data provides the pieces of information 1-3 for the source note.
-
-(3) Changes, editing, new designs or other amendments must be marked as such in the source note.
-
-
-
-URL: http://www.govdata.de/dl-de/by-2-0
diff --git a/options/license/DL-DE-ZERO-2.0 b/options/license/DL-DE-ZERO-2.0
deleted file mode 100644
index 7daacde13d..0000000000
--- a/options/license/DL-DE-ZERO-2.0
+++ /dev/null
@@ -1,25 +0,0 @@
-DL-DE->Zero-2.0
-Datenlizenz Deutschland – Zero – Version 2.0
-
-Jede Nutzung ist ohne Einschränkungen oder Bedingungen zulässig.
-
-Die bereitgestellten Daten und Metadaten dürfen für die kommerzielle und nicht kommerzielle Nutzung insbesondere
-
- vervielfältigt, ausgedruckt, präsentiert, verändert, bearbeitet sowie an Dritte übermittelt werden;
- mit eigenen Daten und Daten Anderer zusammengeführt und zu selbständigen neuen Datensätzen verbunden werden;
- in interne und externe Geschäftsprozesse, Produkte und Anwendungen in öffentlichen und nicht öffentlichen elektronischen Netzwerken eingebunden werden.
-
-
-Data licence Germany – Zero – version 2.0
-
-Any use is permitted without restrictions or conditions.
-
-The data and meta-data provided may, for commercial and non-commercial use, in particular
-
- be copied, printed, presented, altered, processed and transmitted to third parties;
- be merged with own data and with the data of others and be combined to form new and independent datasets;
- be integrated in internal and external business processes, products and applications in public and non-public electronic networks.
-
-
-
-URL: https://www.govdata.de/dl-de/zero-2-0
diff --git a/options/license/DOC b/options/license/DOC
deleted file mode 100644
index 07a684f0d0..0000000000
--- a/options/license/DOC
+++ /dev/null
@@ -1,15 +0,0 @@
-Copyright and Licensing Information for ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM), and CoSMIC(TM)
-
-ACE(TM), TAO(TM), CIAO(TM), DAnCE>(TM), and CoSMIC(TM) (henceforth referred to as "DOC software") are copyrighted by Douglas C. Schmidt and his research group at Washington University, University of California, Irvine, and Vanderbilt University, Copyright (c) 1993-2009, all rights reserved. Since DOC software is open-source, freely available software, you are free to use, modify, copy, and distribute--perpetually and irrevocably--the DOC software source code and object code produced from the source, as well as copy and distribute modified versions of this software. You must, however, include this copyright statement along with any code built using DOC software that you release. No copyright statement needs to be provided if you just ship binary executables of your software products.
-
-You can use DOC software in commercial and/or binary software releases and are under no obligation to redistribute any of your source code that is built using DOC software. Note, however, that you may not misappropriate the DOC software code, such as copyrighting it yourself or claiming authorship of the DOC software code, in a way that will prevent DOC software from being distributed freely using an open-source development model. You needn't inform anyone that you're using DOC software in your software, though we encourage you to let us know so we can promote your project in the DOC software success stories.
-
-The ACE, TAO, CIAO, DAnCE, and CoSMIC web sites are maintained by the DOC Group at the Institute for Software Integrated Systems (ISIS) and the Center for Distributed Object Computing of Washington University, St. Louis for the development of open-source software as part of the open-source software community. Submissions are provided by the submitter ``as is'' with no warranties whatsoever, including any warranty of merchantability, noninfringement of third party intellectual property, or fitness for any particular purpose. In no event shall the submitter be liable for any direct, indirect, special, exemplary, punitive, or consequential damages, including without limitation, lost profits, even if advised of the possibility of such damages. Likewise, DOC software is provided as is with no warranties of any kind, including the warranties of design, merchantability, and fitness for a particular purpose, noninfringement, or arising from a course of dealing, usage or trade practice. Washington University, UC Irvine, Vanderbilt University, their employees, and students shall have no liability with respect to the infringement of copyrights, trade secrets or any patents by DOC software or any part thereof. Moreover, in no event will Washington University, UC Irvine, or Vanderbilt University, their employees, or students be liable for any lost revenue or profits or other special, indirect and consequential damages.
-
-DOC software is provided with no support and without any obligation on the part of Washington University, UC Irvine, Vanderbilt University, their employees, or students to assist in its use, correction, modification, or enhancement. A number of companies around the world provide commercial support for DOC software, however. DOC software is Y2K-compliant, as long as the underlying OS platform is Y2K-compliant. Likewise, DOC software is compliant with the new US daylight savings rule passed by Congress as "The Energy Policy Act of 2005," which established new daylight savings times (DST) rules for the United States that expand DST as of March 2007. Since DOC software obtains time/date and calendaring information from operating systems users will not be affected by the new DST rules as long as they upgrade their operating systems accordingly.
-
-The names ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM), CoSMIC(TM), Washington University, UC Irvine, and Vanderbilt University, may not be used to endorse or promote products or services derived from this source without express written permission from Washington University, UC Irvine, or Vanderbilt University. This license grants no permission to call products or services derived from this source ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM), or CoSMIC(TM), nor does it grant permission for the name Washington University, UC Irvine, or Vanderbilt University to appear in their names.
-
-If you have any suggestions, additions, comments, or questions, please let me know.
-
-Douglas C. Schmidt
diff --git a/options/license/DRL-1.0 b/options/license/DRL-1.0
deleted file mode 100644
index 8bcb7148c9..0000000000
--- a/options/license/DRL-1.0
+++ /dev/null
@@ -1,12 +0,0 @@
-Detection Rule License (DRL) 1.0
-Permission is hereby granted, free of charge, to any person obtaining a copy of this rule set and associated documentation files (the "Rules"), to deal in the Rules without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Rules, and to permit persons to whom the Rules are furnished to do so, subject to the following conditions:
-
-If you share the Rules (including in modified form), you must retain the following if it is supplied within the Rules:
-
-identification of the authors(s) ("author" field) of the Rule and any others designated to receive attribution, in any reasonable manner requested by the Rule author (including by pseudonym if designated).
-
-a URI or hyperlink to the Rule set or explicit Rule to the extent reasonably practicable
-
-indicate the Rules are licensed under this Detection Rule License, and include the text of, or the URI or hyperlink to, this Detection Rule License to the extent reasonably practicable
-
-THE RULES ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE RULES OR THE USE OR OTHER DEALINGS IN THE RULES.
diff --git a/options/license/DRL-1.1 b/options/license/DRL-1.1
deleted file mode 100644
index a6445601ff..0000000000
--- a/options/license/DRL-1.1
+++ /dev/null
@@ -1,17 +0,0 @@
-Detection Rule License (DRL) 1.1
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this rule set and associated documentation files (the "Rules"), to deal in the Rules without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Rules, and to permit persons to whom the Rules are furnished to do so, subject to the following conditions:
-
-If you share the Rules (including in modified form), you must retain the following if it is supplied within the Rules:
-
-identification of the authors(s) ("author" field) of the Rule and any others designated to receive attribution, in any reasonable manner requested by the Rule author (including by pseudonym if designated).
-
-a URI or hyperlink to the Rule set or explicit Rule to the extent reasonably practicable
-
-indicate the Rules are licensed under this Detection Rule License, and include the text of, or the URI or hyperlink to, this Detection Rule License to the extent reasonably practicable
-
-If you use the Rules (including in modified form) on data, messages based on matches with the Rules must retain the following if it is supplied within the Rules:
-
-identification of the authors(s) ("author" field) of the Rule and any others designated to receive attribution, in any reasonable manner requested by the Rule author (including by pseudonym if designated).
-
-THE RULES ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE RULES OR THE USE OR OTHER DEALINGS IN THE RULES.
diff --git a/options/license/DSDP b/options/license/DSDP
deleted file mode 100644
index 1c4d42f4b2..0000000000
--- a/options/license/DSDP
+++ /dev/null
@@ -1,18 +0,0 @@
-COPYRIGHT NOTIFICATION
-
-(C) COPYRIGHT 2004 UNIVERSITY OF CHICAGO
-
-This program discloses material protectable under copyright laws of the United States. Permission to copy and modify this software and its documentation is hereby granted, provided that this notice is retained thereon and on all copies or modifications. The University of Chicago makes no representations as to the suitability and operability of this software for any purpose. It is provided "as is"; without express or implied warranty. Permission is hereby granted to use, reproduce, prepare derivative works, and to redistribute to others, so long as this original copyright notice is retained. Any publication resulting from research that made use of this software should cite this document.
-
- This software was authored by:
-
- Steven J. Benson Mathematics and Computer Science Division Argonne National Laboratory Argonne IL 60439
-
- Yinyu Ye Department of Management Science and Engineering Stanford University Stanford, CA U.S.A
-
- Any questions or comments on the software may be directed to benson@mcs.anl.gov or yinyu-ye@stanford.edu
-
-Argonne National Laboratory with facilities in the states of Illinois and Idaho, is owned by The United States Government, and operated by the University of Chicago under provision of a contract with the Department of Energy.
-
-DISCLAIMER
-THIS PROGRAM WAS PREPARED AS AN ACCOUNT OF WORK SPONSORED BY AN AGENCY OF THE UNITED STATES GOVERNMENT. NEITHER THE UNITED STATES GOVERNMENT NOR ANY AGENCY THEREOF, NOR THE UNIVERSITY OF CHICAGO, NOR ANY OF THEIR EMPLOYEES OR OFFICERS, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS. REFERENCE HEREIN TO ANY SPECIFIC COMMERCIAL PRODUCT, PROCESS, OR SERVICE BY TRADE NAME, TRADEMARK, MANUFACTURER, OR OTHERWISE, DOES NOT NECESSARILY CONSTITUTE OR IMPLY ITS ENDORSEMENT, RECOMMENDATION, OR FAVORING BY THE UNITED STATES GOVERNMENT OR ANY AGENCY THEREOF. THE VIEW AND OPINIONS OF AUTHORS EXPRESSED HEREIN DO NOT NECESSARILY STATE OR REFLECT THOSE OF THE UNITED STATES GOVERNMENT OR ANY AGENCY THEREOF.
diff --git a/options/license/DigiRule-FOSS-exception b/options/license/DigiRule-FOSS-exception
deleted file mode 100644
index 2fa106b38e..0000000000
--- a/options/license/DigiRule-FOSS-exception
+++ /dev/null
@@ -1,54 +0,0 @@
-DigiRule Solutions’s FOSS License Exception Terms and Conditions
-
-1. Definitions.
-
-“Derivative Work†means a derivative work, as defined under applicable copyright law, formed entirely from the Program and one or more FOSS Applications.
-
-“FOSS Application†means a free and open source software application distributed subject to a license listed in the section below titled “FOSS License List.â€
-
-“FOSS Notice†means a notice placed by DigiRule Solutions in a copy of the Client Libraries stating that such copy of the Client Libraries may be distributed under DigiRule Solutions's or FOSS License Exception.
-
-“Independent Work†means portions of the Derivative Work that are not derived from the Program and can reasonably be considered independent and separate works.
-
-“Program†means a copy of DigiRule Solutions’s Client Libraries that contain a FOSS Notice.
-
-2. A FOSS application developer (“you†or “yourâ€) may distribute a Derivative Work provided that you and the Derivative Work meet all of the following conditions:
-
- 1. You obey the GPL in all respects for the Program and all portions (including modifications) of the Program included in the Derivative Work (provided that this condition does not apply to Independent Works);
-
- 2. The Derivative Work does not include any work licensed under the GPL other than the Program;
-
- 3. You distribute Independent Works subject to a license listed in the section below titled “FOSS License Listâ€;
-
- 4. You distribute Independent Works in object code or executable form with the complete corresponding machine-readable source code on the same medium and under the same FOSS license applying to the object code or executable forms;
-
- 5. All works that are aggregated with the Program or the Derivative Work on a medium or volume of storage are not derivative works of the Program, Derivative Work or FOSS Application, and must reasonably be considered independent and separate works.
-
-3. DigiRule Solutions reserves all rights not expressly granted in these terms and conditions. If all of the above conditions are not met, then this FOSS License Exception does not apply to you or your Derivative Work.
-
-FOSS License List
-License Name Version(s)/Copyright Date
-Release Early Certified Software
-Academic Free License 2.0
-Apache Software License 1.0/1.1/2.0
-Apple Public Source License 2.0
-Artistic license From Perl 5.8.0
-BSD license “July 22 1999â€
-Common Development and Distribution License (CDDL) 1.0
-Common Public License 1.0
-Eclipse Public License 1.0
-GNU Library or “Lesser†General Public License (LGPL) 2.0/2.1/3.0
-Jabber Open Source License 1.0
-MIT License (As listed in file MIT-License.txt) -
-Mozilla Public License (MPL) 1.0/1.1
-Open Software License 2.0
-OpenSSL license (with original SSLeay license) “2003†(“1998â€)
-PHP License 3.0/3.01
-Python license (CNRI Python License) -
-Python Software Foundation License 2.1.1
-Sleepycat License “1999â€
-University of Illinois/NCSA Open Source License -
-W3C License “2001â€
-X11 License “2001â€
-Zlib/libpng License -
-Zope Public License 2.0
diff --git a/options/license/DocBook-Schema b/options/license/DocBook-Schema
deleted file mode 100644
index 56203a0878..0000000000
--- a/options/license/DocBook-Schema
+++ /dev/null
@@ -1,22 +0,0 @@
-Copyright 1992-2011 HaL Computer Systems, Inc.,
-O'Reilly & Associates, Inc., ArborText, Inc., Fujitsu Software
-Corporation, Norman Walsh, Sun Microsystems, Inc., and the
-Organization for the Advancement of Structured Information
-Standards (OASIS).
-
-Permission to use, copy, modify and distribute the DocBook schema
-and its accompanying documentation for any purpose and without fee
-is hereby granted in perpetuity, provided that the above copyright
-notice and this paragraph appear in all copies. The copyright
-holders make no representation about the suitability of the schema
-for any purpose. It is provided "as is" without expressed or implied
-warranty.
-
-If you modify the DocBook schema in any way, label your schema as a
-variant of DocBook. See the reference documentation
-(http://docbook.org/tdg5/en/html/ch05.html#s-notdocbook)
-for more information.
-
-Please direct all questions, bug reports, or suggestions for changes
-to the docbook@lists.oasis-open.org mailing list. For more
-information, see http://www.oasis-open.org/docbook/.
diff --git a/options/license/DocBook-Stylesheet b/options/license/DocBook-Stylesheet
deleted file mode 100644
index e986ed4235..0000000000
--- a/options/license/DocBook-Stylesheet
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright 2005 Norman Walsh, Sun Microsystems,
-Inc., and the Organization for the Advancement
-of Structured Information Standards (OASIS).
-
-Release: $Id: db4-upgrade.xsl 8905 2010-09-12 11:47:07Z bobstayton $
-
-Permission to use, copy, modify and distribute this stylesheet
-and its accompanying documentation for any purpose and
-without fee is hereby granted in perpetuity, provided that
-the above copyright notice and this paragraph appear in
-all copies. The copyright holders make no representation
-about the suitability of the schema for any purpose. It
-is provided "as is" without expressed or implied warranty.
diff --git a/options/license/DocBook-XML b/options/license/DocBook-XML
deleted file mode 100644
index 9553feee6b..0000000000
--- a/options/license/DocBook-XML
+++ /dev/null
@@ -1,48 +0,0 @@
-Copyright
----------
-Copyright (C) 1999-2007 Norman Walsh
-Copyright (C) 2003 Jiří Kosek
-Copyright (C) 2004-2007 Steve Ball
-Copyright (C) 2005-2014 The DocBook Project
-Copyright (C) 2011-2012 O'Reilly Media
-
-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.
-
-Except as contained in this notice, the names of individuals
-credited with contribution to this software shall not be used in
-advertising or otherwise to promote the sale, use or other
-dealings in this Software without prior written authorization
-from the individuals in question.
-
-Any stylesheet derived from this Software that is publically
-distributed will be identified with a different name and the
-version strings in any derived Software will be changed so that
-no possibility of confusion between the derived package and this
-Software will exist.
-
-Warranty
---------
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL NORMAN WALSH OR ANY OTHER
-CONTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-Contacting the Author
----------------------
-The DocBook XSL stylesheets are maintained by Norman Walsh,
-<ndw@nwalsh.com>, and members of the DocBook Project,
-<docbook-developers@sf.net>
diff --git a/options/license/Dotseqn b/options/license/Dotseqn
deleted file mode 100644
index 9833407c06..0000000000
--- a/options/license/Dotseqn
+++ /dev/null
@@ -1,5 +0,0 @@
-Copyright (C) 1995 by Donald Arseneau
-
-This file may be freely transmitted and reproduced, but it may not be changed unless the name is changed also (except that you may freely change the paper-size option for \documentclass).
-
-This notice must be left intact.
diff --git a/options/license/ECL-1.0 b/options/license/ECL-1.0
deleted file mode 100644
index 08f1b6bd9c..0000000000
--- a/options/license/ECL-1.0
+++ /dev/null
@@ -1,23 +0,0 @@
-The Educational Community License
-
-This Educational Community License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
-
- Copyright (c) <year> <copyright holders>
-
- Licensed under the Educational Community License version 1.0
-
-This Original Work, including software, source code, documents, or other related items, is being provided by the copyright holder(s) subject to the terms of the Educational Community License. By obtaining, using and/or copying this Original Work, you agree that you have read, understand, and will comply with the following terms and conditions of the Educational Community License:
-
-Permission to use, copy, modify, merge, publish, distribute, and sublicense this Original Work and its documentation, with or without modification, for any purpose, and without fee or royalty to the copyright holder(s) is hereby granted, provided that you include the following on ALL copies of the Original Work or portions thereof, including modifications or derivatives, that you make:
-
- The full text of the Educational Community License in a location viewable to users of the redistributed or derivative work.
-
- Any pre-existing intellectual property disclaimers, notices, or terms and conditions.
-
- Notice of any changes or modifications to the Original Work, including the date the changes were made.
-
- Any modifications of the Original Work must be distributed in such a manner as to avoid any confusion with the Original Work of the copyright holders.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-The name and trademarks of copyright holder(s) may NOT be used in advertising or publicity pertaining to the Original or Derivative Works without specific, written prior permission. Title to copyright in the Original Work and any associated documentation will at all times remain with the copyright holders.
diff --git a/options/license/ECL-2.0 b/options/license/ECL-2.0
deleted file mode 100644
index eb04ec4147..0000000000
--- a/options/license/ECL-2.0
+++ /dev/null
@@ -1,98 +0,0 @@
-Educational Community License
-Version 2.0, April 2007
-
-http://www.osedu.org/licenses/
-
-The Educational Community License version 2.0 ("ECL") consists of the Apache 2.0 license, modified to change the scope of the patent grant in section 3 to be specific to the needs of the education communities using this license. The original Apache 2.0 license can be found at: http://www.apache.org/licenses/LICENSE-2.0
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-1. Definitions.
-
-"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
-
-"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
-
-"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
-
-"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
-
-"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
-
-"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
-
-"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
-
-"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
-
-"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
-
-2. Grant of Copyright License.
-
-Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
-
-3. Grant of Patent License.
-
-Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. Any patent license granted hereby with respect to contributions by an individual employed by an institution or organization is limited to patent claims where the individual that is the author of the Work is also the inventor of the patent claims licensed, and where the organization or institution has the right to grant such license under applicable grant and research funding agreements. No other express or implied licenses are granted.
-
-4. Redistribution.
-
-You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
-
- a. You must give any other recipients of the Work or Derivative Works a copy of this License; and
-
- b. You must cause any modified files to carry prominent notices stating that You changed the files; and
-
- c. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
-
- d. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
-
-You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
-
-5. Submission of Contributions.
-
-Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
-
-6. Trademarks.
-
-This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty.
-
-Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability.
-
-In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability.
-
-While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
-
-END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply the Educational Community License to your work
-
-To apply the Educational Community License to your work, attach
-the following boilerplate notice, with the fields enclosed by
-brackets "[]" replaced with your own identifying information.
-(Don't include the brackets!) The text should be enclosed in the
-appropriate comment syntax for the file format. We also recommend
-that a file or class name and description of purpose be included on
-the same "printed page" as the copyright notice for easier
-identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner] Licensed under the
- Educational Community License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may
- obtain a copy of the License at
-
- http://www.osedu.org/licenses/ECL-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an "AS IS"
- BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- or implied. See the License for the specific language governing
- permissions and limitations under the License.
diff --git a/options/license/EFL-1.0 b/options/license/EFL-1.0
deleted file mode 100644
index 278ab002d8..0000000000
--- a/options/license/EFL-1.0
+++ /dev/null
@@ -1,13 +0,0 @@
-Eiffel Forum License, version 1
-
-Permission is hereby granted to use, copy, modify and/or distribute this package, provided that:
-
- - copyright notices are retained unchanged
-
- - any distribution of this package, whether modified or not, includes this file
-
-Permission is hereby also granted to distribute binary programs which depend on this package, provided that:
-
- - if the binary program depends on a modified version of this package, you must publicly release the modified version of this package
-
-THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT WARRANTY. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE TO ANY PARTY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THIS PACKAGE.
diff --git a/options/license/EFL-2.0 b/options/license/EFL-2.0
deleted file mode 100644
index c241325012..0000000000
--- a/options/license/EFL-2.0
+++ /dev/null
@@ -1,9 +0,0 @@
-Eiffel Forum License, version 2
-
-1. Permission is hereby granted to use, copy, modify and/or distribute this package, provided that:
- - copyright notices are retained unchanged,
- - any distribution of this package, whether modified or not, includes this license text.
-
-2. Permission is hereby also granted to distribute binary programs which depend on this package. If the binary program depends on a modified version of this package, you are encouraged to publicly release the modified version of this package.
-
-THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT WARRANTY. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE TO ANY PARTY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THIS PACKAGE.
diff --git a/options/license/EPICS b/options/license/EPICS
deleted file mode 100644
index f2f6b0e7af..0000000000
--- a/options/license/EPICS
+++ /dev/null
@@ -1,32 +0,0 @@
-EPICS Open License Terms
-
-The following is the text of the EPICS Open software license agreement which now applies to EPICS Base and many of the unbundled EPICS extensions and support modules.
-
-Copyright © <YEAR> <HOLDERS>. All rights reserved.
-
-<PRODUCT> is distributed subject to the following license conditions:
-
-SOFTWARE LICENSE AGREEMENT
-
-Software: <PRODUCT>
-
-1. The "Software", below, refers to <PRODUCT> (in either source code, or binary form and accompanying documentation). Each licensee is addressed as "you" or "Licensee."
-
-2. The copyright holders shown above and their third-party licensors hereby grant Licensee a royalty-free nonexclusive license, subject to the limitations stated herein and U.S. Government license rights.
-
-3. You may modify and make a copy or copies of the Software for use within your organization, if you meet the following conditions:
-
-a. Copies in source code must include the copyright notice and this Software License Agreement.
-b. Copies in binary form must include the copyright notice and this Software License Agreement in the documentation and/or other materials provided with the copy.
-
-4. You may modify a copy or copies of the Software or any portion of it, thus forming a work based on the Software, and distribute copies of such work outside your organization, if you meet all of the following conditions:
-
-a. Copies in source code must include the copyright notice and this Software License Agreement;
-b. Copies in binary form must include the copyright notice and this Software License Agreement in the documentation and/or other materials provided with the copy;
-c. Modified copies and works based on the Software must carry prominent notices stating that you changed specified portions of the Software.
-
-5. Portions of the Software resulted from work developed under a U.S. Government contract and are subject to the following license: the Government is granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable worldwide license in this computer software to reproduce, prepare derivative works, and perform publicly and display publicly.
-
-6. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF THE SOFTWARE, (3) DO NOT REPTHAT USE OF THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL BE CORRECTED.
-
-7. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGES.
diff --git a/options/license/EUDatagrid b/options/license/EUDatagrid
deleted file mode 100644
index ca0ee0dcb2..0000000000
--- a/options/license/EUDatagrid
+++ /dev/null
@@ -1,24 +0,0 @@
-EU DataGrid Software License
-
-Copyright (c) 2001 EU DataGrid. All rights reserved.
-
-This software includes voluntary contributions made to the EU DataGrid. For more information on the EU DataGrid, please see http://www.eu-datagrid.org/.
-
-Installation, use, reproduction, display, modification and redistribution of this software, with or without modification, in source and binary forms, are permitted. Any exercise of rights under this license by you or your sub-licensees is subject to the following conditions:
-
-1. Redistributions of this software, with or without modification, must reproduce the above copyright notice and the above license statement as well as this list of conditions, in the software, the user documentation and any other materials provided with the software.
-
-2. The user documentation, if any, included with a redistribution, must include the following notice:
- "This product includes software developed by the EU DataGrid (http://www.eu-datagrid.org/)."
-
-Alternatively, if that is where third-party acknowledgments normally appear, this acknowledgment must be reproduced in the software itself.
-
-3. The names "EDG", "EDG Toolkit", “EU DataGrid†and "EU DataGrid Project" may not be used to endorse or promote software, or products derived therefrom, except with prior written permission by hep-project-grid-edg-license@cern.ch.
-
-4. You are under no obligation to provide anyone with any bug fixes, patches, upgrades or other modifications, enhancements or derivatives of the features,functionality or performance of this software that you may develop. However, if you publish or distribute your modifications, enhancements or derivative works without contemporaneously requiring users to enter into a separate written license agreement, then you are deemed to have granted participants in the EU DataGrid a worldwide, non-exclusive, royalty-free, perpetual license to install, use, reproduce, display, modify, redistribute and sub-license your modifications, enhancements or derivative works, whether in binary or source code form, under the license conditions stated in this list of conditions.
-
-5. DISCLAIMER
-THIS SOFTWARE IS PROVIDED BY THE EU DATAGRID AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, OF SATISFACTORY QUALITY, AND FITNESS FOR A PARTICULAR PURPOSE OR USE ARE DISCLAIMED. THE EU DATAGRID AND CONTRIBUTORS MAKE NO REPRESENTATION THAT THE SOFTWARE, MODIFICATIONS, ENHANCEMENTS OR DERIVATIVE WORKS THEREOF, WILL NOT INFRINGE ANY PATENT, COPYRIGHT, TRADE SECRET OR OTHER PROPRIETARY RIGHT.
-
-6. LIMITATION OF LIABILITY
-THE EU DATAGRID AND CONTRIBUTORS SHALL HAVE NO LIABILITY TO LICENSEE OR OTHER PERSONS FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF USE, DATA OR PROFITS, OR BUSINESS INTERRUPTION, HOWEVER CAUSED AND ON ANY THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCT LIABILITY OR OTHERWISE, ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/options/license/EUPL-1.0 b/options/license/EUPL-1.0
deleted file mode 100644
index cd43668842..0000000000
--- a/options/license/EUPL-1.0
+++ /dev/null
@@ -1,154 +0,0 @@
-European Union Public Licence V.1.0
-
-EUPL (c) the European Community 2007
-
-This European Union Public Licence (the “EUPLâ€) applies to the Work or Software (as defined below) which is provided under the terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such use is covered by a right of the copyright holder of the Work).
-
-The Original Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following notice immediately following the copyright notice for the Original Work:
-
- Licensed under the EUPL V.1.0
-
-or has expressed by any other mean his willingness to license under the EUPL.
-
-1. Definitions
-
-In this Licence, the following terms have the following meaning:
-
- − The Licence: this Licence.
-
- − The Original Work or the Software: the software distributed and/or communicated by the Licensor under this Licence, available as Source Code and also as Executable Code as the case may be.
-
- − Derivative Works: the works or software that could be created by the Licensee, based upon the Original Work or modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in the country mentioned in Article 15.
-
- − The Work: the Original Work and/or its Derivative Works.
-
- − The Source Code: the human-readable form of the Work which is the most convenient for people to study and modify.
-
- − The Executable Code: any code which has generally been compiled and which is meant to be interpreted by a computer as a program.
-
- − The Licensor: the natural or legal person that distributes and/or communicates the Work under the Licence.
-
- − Contributor(s): any natural or legal person who modifies the Work under the Licence, or otherwise contributes to the creation of a Derivative Work.
-
- − The Licensee or “Youâ€: any natural or legal person who makes any usage of the Software under the terms of the Licence. − Distribution and/or Communication: any act of selling, giving, lending, renting, distributing, communicating, transmitting, or otherwise making available, on-line or off-line, copies of the Work at the disposal of any other natural or legal person.
-
-2. Scope of the rights granted by the Licence
-
-The Licensor hereby grants You a world-wide, royalty-free, non-exclusive, sub-licensable licence to do the following, for the duration of copyright vested in the Original Work:
-
- − use the Work in any circumstance and for all usage,
-
- − reproduce the Work,
-
- − modify the Original Work, and make Derivative Works based upon the Work,
-
- − communicate to the public, including the right to make available or display the Work or copies thereof to the public and perform publicly, as the case may be, the Work,
-
- − distribute the Work or copies thereof,
-
- − lend and rent the Work or copies thereof,
-
- − sub-license rights in the Work or copies thereof.
-
-Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the applicable law permits so.
-
-In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed by law in order to make effective the licence of the economic rights here above listed.
-
-The Licensor grants to the Licensee royalty-free, non exclusive usage rights to any patents held by the Licensor, to the extent necessary to make use of the rights granted on the Work under this Licence.
-
-3. Communication of the Source Code
-
-The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as Executable Code, the Licensor provides in addition a machinereadable copy of the Source Code of the Work along with each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to distribute and/or communicate the Work.
-
-4. Limitations on copyright
-
-Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the exclusive rights of the rights owners in the Original Work or Software, of the exhaustion of those rights or of other applicable limitations thereto.
-
-5. Obligations of the Licensee
-
-The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those obligations are the following:
-
-Attribution right: the Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the Licence with every copy of the Work he/she distributes and/or communicates. The Licensee must cause any Derivative Work to carry prominent notices stating that the Work has been modified and the date of modification.
-
-Copyleft clause: If the Licensee distributes and/or communicates copies of the Original Works or Derivative Works based upon the Original Work, this Distribution and/or Communication will be done under the terms of this Licence. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the Work or Derivative Work that alter or restrict the terms of the Licence.
-
-Compatibility clause: If the Licensee Distributes and/or Communicates Derivative Works or copies thereof based upon both the Original Work and another work licensed under a Compatible Licence, this Distribution and/or Communication can be done under the terms of this Compatible Licence. For the sake of this clause, “Compatible Licence†refers to the licences listed in the appendix attached to this Licence. Should the Licensee’s obligations under the Compatible Licence conflict with his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail.
-
-Provision of Source Code: When distributing and/or communicating copies of the Work, the Licensee will provide a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available for as long as the Licensee continues to distribute and/or communicate the Work.
-
-Legal Protection: This Licence does not grant permission to use the trade names, trademarks, service marks, or names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the copyright notice.
-
-6. Chain of Authorship
-
-The original Licensor warrants that the copyright in the Original Work granted hereunder is owned by him/her or licensed to him/her and that he/she has the power and authority to grant the Licence.
-
-Each Contributor warrants that the copyright in the modifications he/she brings to the Work are owned by him/her or licensed to him/her and that he/she has the power and authority to grant the Licence.
-
-Each time You, as a Licensee, receive the Work, the original Licensor and subsequent Contributors grant You a licence to their contributions to the Work, under the terms of this Licence.
-
-7. Disclaimer of Warranty
-
-The Work is a work in progress, which is continuously improved by numerous contributors. It is not a finished work and may therefore contain defects or “bugs†inherent to this type of software development.
-
-For the above reason, the Work is provided under the Licence on an “as is†basis and without warranties of any kind concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this Licence.
-
-This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work.
-
-8. Disclaimer of Liability
-
-Except in the cases of wilful misconduct or damages directly caused to natural persons, the Licensor will in no event be liable for any direct or indirect, material or moral, damages of any kind, arising out of the Licence or of the use of the Work, including without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, loss of data or any commercial damage, even if the Licensor has been advised of the possibility of such damage. However, the Licensor will be liable under statutory product liability laws as far such laws apply to the Work.
-
-9. Additional agreements
-
-While distributing the Original Work or Derivative Works, You may choose to conclude an additional agreement to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or services consistent with this Licence. However, in accepting such obligations, You may act only on your own behalf and on your sole responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by the fact You have accepted any such warranty or additional liability.
-
-10. Acceptance of the Licence
-
-The provisions of this Licence can be accepted by clicking on an icon “I agree†placed under the bottom of a window displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms and conditions.
-
-Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution and/or Communication by You of the Work or copies thereof.
-
-11. Information to the public
-
-In case of any Distribution and/or Communication of the Work by means of electronic communication by You (for example, by offering to download the Work from a remote location) the distribution channel or media (for example, a website) must at least provide to the public the information requested by the applicable law regarding the identification and address of the Licensor, the Licence and the way it may be accessible, concluded, stored and reproduced by the Licensee.
-
-12. Termination of the Licence
-
-The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms of the Licence.
-
-Such a termination will not terminate the licences of any person who has received the Work from the Licensee under the Licence, provided such persons remain in full compliance with the Licence.
-
-13. Miscellaneous
-
-Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the Work licensed hereunder.
-
-If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or enforceability of the Licence as a whole. Such provision will be construed and/or reformed so as necessary to make it valid and enforceable.
-
-The European Commission may put into force translations and/or binding new versions of this Licence, so far this is required and reasonable. New versions of the Licence will be published with a unique version number. The new version of the Licence becomes binding for You as soon as You become aware of its publication.
-
-14. Jurisdiction
-
-Any litigation resulting from the interpretation of this License, arising between the European Commission, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice of the European Communities, as laid down in article 238 of the Treaty establishing the European Community.
-
-Any litigation arising between Parties, other than the European Commission, and resulting from the interpretation of this License, will be subject to the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business.
-
-15. Applicable Law
-
-This Licence shall be governed by the law of the European Union country where the Licensor resides or has his registered office.
-
-This licence shall be governed by the Belgian law if:
-
- − a litigation arises between the European Commission, as a Licensor, and any Licensee;
-
- − the Licensor, other than the European Commission, has no residence or registered office inside a European Union country.
-
-
-Appendix
-
-“Compatible Licences†according to article 5 EUPL are:
-
-− General Public License (GPL) v. 2
-− Open Software License (OSL) v. 2.1, v. 3.0
-− Common Public License v. 1.0
-− Eclipse Public License v. 1.0
-− Cecill v. 2.0
diff --git a/options/license/EUPL-1.1 b/options/license/EUPL-1.1
deleted file mode 100644
index 3e0d612be3..0000000000
--- a/options/license/EUPL-1.1
+++ /dev/null
@@ -1,157 +0,0 @@
-European Union Public Licence V. 1.1
-
-EUPL (c) the European Community 2007
-
-This European Union Public Licence (the "EUPL") applies to the Work or Software (as defined below) which is provided under the terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such use is covered by a right of the copyright holder of the Work).
-
-The Original Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following notice immediately following the copyright notice for the Original Work:
-
- Licensed under the EUPL V.1.1
-
-or has expressed by any other mean his willingness to license under the EUPL.
-
-1. Definitions
-
-In this Licence, the following terms have the following meaning:
-
- - The Licence: this Licence.
-
- - The Original Work or the Software: the software distributed and/or communicated by the Licensor under this Licence, available as Source Code and also as Executable Code as the case may be.
-
- - Derivative Works: the works or software that could be created by the Licensee, based upon the Original Work or modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in the country mentioned in Article 15.
-
- - The Work: the Original Work and/or its Derivative Works.
-
- - The Source Code: the human-readable form of the Work which is the most convenient for people to study and modify.
-
- - The Executable Code: any code which has generally been compiled and which is meant to be interpreted by a computer as a program.
-
- - The Licensor: the natural or legal person that distributes and/or communicates the Work under the Licence.
-
- - Contributor(s): any natural or legal person who modifies the Work under the Licence, or otherwise contributes to the creation of a Derivative Work.
-
- - The Licensee or "You": any natural or legal person who makes any usage of the Software under the terms of the Licence.
-
- - Distribution and/or Communication: any act of selling, giving, lending, renting, distributing, communicating, transmitting, or otherwise making available, on-line or off-line, copies of the Work or providing access to its essential functionalities at the disposal of any other natural or legal person.
-
-2. Scope of the rights granted by the Licence
-
-The Licensor hereby grants You a world-wide, royalty-free, non-exclusive, sublicensable licence to do the following, for the duration of copyright vested in the Original Work:
-
- - use the Work in any circumstance and for all usage,
-
- - reproduce the Work,
-
- - modify the Original Work, and make Derivative Works based upon the Work,
-
- - communicate to the public, including the right to make available or display the Work or copies thereof to the public and perform publicly, as the case may be, the Work,
-
- - distribute the Work or copies thereof,
-
- - lend and rent the Work or copies thereof,
-
- - sub-license rights in the Work or copies thereof.
-
-Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the applicable law permits so.
-
-In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed by law in order to make effective the licence of the economic rights here above listed.
-
-The Licensor grants to the Licensee royalty-free, non exclusive usage rights to any patents held by the Licensor, to the extent necessary to make use of the rights granted on the Work under this Licence.
-
-3. Communication of the Source Code
-
-The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to distribute and/or communicate the Work.
-
-4. Limitations on copyright
-
-Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the exclusive rights of the rights owners in the Original Work or Software, of the exhaustion of those rights or of other applicable limitations thereto.
-
-5. Obligations of the Licensee
-
-The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those obligations are the following:
-
-Attribution right: the Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the Licence with every copy of the Work he/she distributes and/or communicates. The Licensee must cause any Derivative Work to carry prominent notices stating that the Work has been modified and the date of modification.
-
-Copyleft clause: If the Licensee distributes and/or communicates copies of the Original Works or Derivative Works based upon the Original Work, this Distribution and/or Communication will be done under the terms of this Licence or of a later version of this Licence unless the Original Work is expressly distributed only under this version of the Licence. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the Work or Derivative Work that alter or restrict the terms of the Licence.
-
-Compatibility clause: If the Licensee Distributes and/or Communicates Derivative Works or copies thereof based upon both the Original Work and another work licensed under a Compatible Licence, this Distribution and/or Communication can be done under the terms of this Compatible Licence. For the sake of this clause, "Compatible Licence," refers to the licences listed in the appendix attached to this Licence. Should the Licensee's obligations under the Compatible Licence conflict with his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail.
-
-Provision of Source Code: When distributing and/or communicating copies of the Work, the Licensee will provide a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available for as long as the Licensee continues to distribute and/or communicate the Work.
-
-Legal Protection: This Licence does not grant permission to use the trade names, trademarks, service marks, or names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the copyright notice.
-
-6. Chain of Authorship
-
-The original Licensor warrants that the copyright in the Original Work granted hereunder is owned by him/her or licensed to him/her and that he/she has the power and authority to grant the Licence.
-
-Each Contributor warrants that the copyright in the modifications he/she brings to the Work are owned by him/her or licensed to him/her and that he/she has the power and authority to grant the Licence.
-
-Each time You accept the Licence, the original Licensor and subsequent Contributors grant You a licence to their contributions to the Work, under the terms of this Licence.
-
-7. Disclaimer of Warranty
-
-The Work is a work in progress, which is continuously improved by numerous contributors. It is not a finished work and may therefore contain defects or "bugs" inherent to this type of software development.
-
-For the above reason, the Work is provided under the Licence on an "as is" basis and without warranties of any kind concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this Licence.
-
-This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work.
-
-8. Disclaimer of Liability
-
-Except in the cases of wilful misconduct or damages directly caused to natural persons, the Licensor will in no event be liable for any direct or indirect, material or moral, damages of any kind, arising out of the Licence or of the use of the Work, including without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, loss of data or any commercial damage, even if the Licensor has been advised of the possibility of such damage. However, the Licensor will be liable under statutory product liability laws as far such laws apply to the Work.
-
-9. Additional agreements
-
-While distributing the Original Work or Derivative Works, You may choose to conclude an additional agreement to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or services consistent with this Licence. However, in accepting such obligations, You may act only on your own behalf and on your sole responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by the fact You have accepted any such warranty or additional liability.
-
-10. Acceptance of the Licence
-
-The provisions of this Licence can be accepted by clicking on an icon "I agree" placed under the bottom of a window displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms and conditions.
-
-Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution and/or Communication by You of the Work or copies thereof.
-
-11. Information to the public
-
-In case of any Distribution and/or Communication of the Work by means of electronic communication by You (for example, by offering to download the Work from a remote location) the distribution channel or media (for example, a website) must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence and the way it may be accessible, concluded, stored and reproduced by the Licensee.
-
-12. Termination of the Licence
-
-The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms of the Licence. Such a termination will not terminate the licences of any person who has received the Work from the Licensee under the Licence, provided such persons remain in full compliance with the Licence.
-
-13. Miscellaneous
-
-Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the Work licensed hereunder.
-
-If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or enforceability of the Licence as a whole. Such provision will be construed and/or reformed so as necessary to make it valid and enforceable.
-
-The European Commission may publish other linguistic versions and/or new versions of this Licence, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence. New versions of the Licence will be published with a unique version number.
-
-All linguistic versions of this Licence, approved by the European Commission, have identical value. Parties can take advantage of the linguistic version of their choice.
-
-14. Jurisdiction
-
-Any litigation resulting from the interpretation of this License, arising between the European Commission, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice of the European Communities, as laid down in article 238 of the Treaty establishing the European Community.
-
-Any litigation arising between Parties, other than the European Commission, and resulting from the interpretation of this License, will be subject to the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business.
-
-15. Applicable Law
-
-This Licence shall be governed by the law of the European Union country where the Licensor resides or has his registered office.
-
-This licence shall be governed by the Belgian law if:
-
- - a litigation arises between the European Commission, as a Licensor, and any Licensee;
-
- - the Licensor, other than the European Commission, has no residence or registered office inside a European Union country.
-
-
-
-Appendix
-
-"Compatible Licences" according to article 5 EUPL are:
-
- - GNU General Public License (GNU GPL) v. 2
- - Open Software License (OSL) v. 2.1, v. 3.0
- - Common Public License v. 1.0
- - Eclipse Public License v. 1.0
- - Cecill v. 2.0
diff --git a/options/license/Elastic-2.0 b/options/license/Elastic-2.0
deleted file mode 100644
index 9496955678..0000000000
--- a/options/license/Elastic-2.0
+++ /dev/null
@@ -1,93 +0,0 @@
-Elastic License 2.0
-
-URL: https://www.elastic.co/licensing/elastic-license
-
-Acceptance
-
-By using the software, you agree to all of the terms and conditions below.
-
-Copyright License
-
-The licensor grants you a non-exclusive, royalty-free, worldwide,
-non-sublicensable, non-transferable license to use, copy, distribute, make
-available, and prepare derivative works of the software, in each case subject to
-the limitations and conditions below.
-
-Limitations
-
-You may not provide the software to third parties as a hosted or managed
-service, where the service provides users with access to any substantial set of
-the features or functionality of the software.
-
-You may not move, change, disable, or circumvent the license key functionality
-in the software, and you may not remove or obscure any functionality in the
-software that is protected by the license key.
-
-You may not alter, remove, or obscure any licensing, copyright, or other notices
-of the licensor in the software. Any use of the licensor’s trademarks is subject
-to applicable law.
-
-Patents
-
-The licensor grants you a license, under any patent claims the licensor can
-license, or becomes able to license, to make, have made, use, sell, offer for
-sale, import and have imported the software, in each case subject to the
-limitations and conditions in this license. This license does not cover any
-patent claims that you cause to be infringed by modifications or additions to
-the software. If you or your company make any written claim that the software
-infringes or contributes to infringement of any patent, your patent license for
-the software granted under these terms ends immediately. If your company makes
-such a claim, your patent license ends immediately for work on behalf of your
-company.
-
-Notices
-
-You must ensure that anyone who gets a copy of any part of the software from you
-also gets a copy of these terms.
-
-If you modify the software, you must include in any modified copies of the
-software prominent notices stating that you have modified the software.
-
-## No Other Rights
-
-These terms do not imply any licenses other than those expressly granted in
-these terms.
-
-Termination
-
-If you use the software in violation of these terms, such use is not licensed,
-and your licenses will automatically terminate. If the licensor provides you
-with a notice of your violation, and you cease all violation of this license no
-later than 30 days after you receive that notice, your licenses will be
-reinstated retroactively. However, if you violate these terms after such
-reinstatement, any additional violation of these terms will cause your licenses
-to terminate automatically and permanently.
-
-No Liability
-
-As far as the law allows, the software comes as is, without any warranty or
-condition, and the licensor will not be liable to you for any damages arising
-out of these terms or the use or nature of the software, under any kind of
-legal claim.
-
-Definitions
-
-The licensor is the entity offering these terms, and the software is the
-software the licensor makes available under these terms, including any portion
-of it.
-
-you refers to the individual or entity agreeing to these terms.
-
-your company is any legal entity, sole proprietorship, or other kind of
-organization that you work for, plus all organizations that have control over,
-are under the control of, or are under common control with that
-organization. control means ownership of substantially all the assets of an
-entity, or the power to direct its management and policies by vote, contract, or
-otherwise. Control can be direct or indirect.
-
-your licenses are all the licenses granted to you for the software under
-these terms.
-
-use means anything you do with the software requiring one of your licenses.
-
-trademark means trademarks, service marks, and similar rights.
diff --git a/options/license/Entessa b/options/license/Entessa
deleted file mode 100644
index d434afe5b2..0000000000
--- a/options/license/Entessa
+++ /dev/null
@@ -1,22 +0,0 @@
-Entessa Public License Version. 1.0
-Copyright (c) 2003 Entessa, LLC. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment:
-
- "This product includes open source software developed by openSEAL (http://www.openseal.org/)."
-
-Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear.
-
-4. The names "openSEAL" and "Entessa" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact epl@entessa.com.
-
-5. Products derived from this software may not be called "openSEAL", nor may "openSEAL" appear in their name, without prior written permission of Entessa.
-
-THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ENTESSA, LLC, OPENSEAL OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-This software consists of voluntary contributions made by many individuals on behalf of openSEAL and was originally based on software contributed by Entessa, LLC, http://www.entessa.com. For more information on the openSEAL, please see <http://www.openseal.org/>.
diff --git a/options/license/ErlPL-1.1 b/options/license/ErlPL-1.1
deleted file mode 100644
index 88cd9569f4..0000000000
--- a/options/license/ErlPL-1.1
+++ /dev/null
@@ -1,93 +0,0 @@
-ERLANG PUBLIC LICENSE Version 1.1
-
-1. Definitions.
-
-1.1. ``Contributor'' means each entity that creates or contributes to the creation of Modifications.
-
-1.2. ``Contributor Version'' means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
-1.3. ``Covered Code'' means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
-1.4. ``Electronic Distribution Mechanism'' means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
-1.5. ``Executable'' means Covered Code in any form other than Source Code.
-
-1.6. ``Initial Developer'' means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
-1.7. ``Larger Work'' means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
-1.8. ``License'' means this document.
-
-1.9. ``Modifications'' means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
-
- A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
-
- B. Any new file that contains any part of the Original Code or previous Modifications.
-
-1.10. ``Original Code'' means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
-1.11. ``Source Code'' means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or a list of source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
-1.12. ``You'' means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities,``You'' includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, ``control'' means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
-2. Source Code License.
-
-2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, or as part of a Larger Work; and
-
- (b) under patents now or hereafter owned or controlled by Initial Developer, to make, have made, use and sell (``Utilize'') the Original Code (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Original Code (or portions thereof) and not to any greater extent that may be necessary to Utilize further Modifications or combinations.
-
-2.2. Contributor Grant. Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code or as part of a Larger Work; and
-
- (b) under patents now or hereafter owned or controlled by Contributor, to Utilize the Contributor Version (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Contributor Version (or portions thereof), and not to any greater extent that may be necessary to Utilize further Modifications or combinations.
-
-3. Distribution Obligations.
-
-3.1. Application of License. The Modifications which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
-3.2. Availability of Source Code. Any Modification which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
-3.3. Description of Modifications. You must cause all Covered Code to which you contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
-3.4. Intellectual Property Matters
-
- (a) Third Party Claims. If You have knowledge that a party claims an intellectual property right in particular functionality or code (or its utilization under this License), you must include a text file with the source code distribution titled ``LEGAL'' which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after You make Your Modification available as described in Section 3.2, You shall promptly modify the LEGAL file in all copies You make available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
-
- (b) Contributor APIs. If Your Modification is an application programming interface and You own or control patents which are reasonably necessary to implement that API, you must also include this information in the LEGAL file.
-
-3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code, and this License in any documentation for the Source Code, where You describe recipients' rights relating to Covered Code. If You created one or more Modification(s), You may add your name as a Contributor to the notice described in Exhibit A. If it is not possible to put such notice in a particular Source Code file due to its structure, then you must include such notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
-3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
-3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation. If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Application of this License.
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A, and to related Covered Code.
-
-6. CONNECTION TO MOZILLA PUBLIC LICENSE
-This Erlang License is a derivative work of the Mozilla Public License, Version 1.0. It contains terms which differ from the Mozilla Public License, Version 1.0.
-
-7. DISCLAIMER OF WARRANTY.
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN ``AS IS'' BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-9. DISCLAIMER OF LIABILITY
-Any utilization of Covered Code shall not cause the Initial Developer or any Contributor to be liable for any damages (neither direct nor indirect).
-
-10. MISCELLANEOUS
-This License represents the complete agreement concerning the subject matter hereof. If any provision is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be construed by and in accordance with the substantive laws of Sweden. Any dispute, controversy or claim arising out of or relating to this License, or the breach, termination or invalidity thereof, shall be subject to the exclusive jurisdiction of Swedish courts, with the Stockholm City Court as the first instance.
-
-EXHIBIT A.
-
-``The contents of this file are subject to the Erlang Public License, Version 1.1, (the "License"); you may not use this file except in compliance with the License. You should have received a copy of the Erlang Public License along with this software. If not, it can be retrieved via the world wide web at http://www.erlang.org/.
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-The Initial Developer of the Original Code is Ericsson Utvecklings AB. Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings AB. All Rights Reserved.''
diff --git a/options/license/Eurosym b/options/license/Eurosym
deleted file mode 100644
index f6c255327c..0000000000
--- a/options/license/Eurosym
+++ /dev/null
@@ -1,18 +0,0 @@
-Copyright (c) 1999-2002 Henrik Theiling
-Licence Version 2
-
-This software is provided 'as-is', without warranty of any kind, express or implied. In no event will the authors or copyright holders be held liable for any damages arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated.
-
- 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
-
- 3. You must not use any of the names of the authors or copyright holders of the original software for advertising or publicity pertaining to distribution without specific, written prior permission.
-
- 4. If you change this software and redistribute parts or all of it in any form, you must make the source code of the altered version of this software available.
-
- 5. This notice may not be removed or altered from any source distribution.
-
-This licence is governed by the Laws of Germany. Disputes shall be settled by Saarbruecken City Court.
diff --git a/options/license/FBM b/options/license/FBM
deleted file mode 100644
index 68d9149b90..0000000000
--- a/options/license/FBM
+++ /dev/null
@@ -1,6 +0,0 @@
-Portions of this code Copyright (C) 1989 by Michael Mauldin.
-Permission is granted to use this file in whole or in
-part for any purpose, educational, recreational or commercial,
-provided that this copyright notice is retained unchanged.
-This software is available to all free of charge by anonymous
-FTP and in the UUNET archives.
diff --git a/options/license/FDK-AAC b/options/license/FDK-AAC
deleted file mode 100644
index e506d69d5e..0000000000
--- a/options/license/FDK-AAC
+++ /dev/null
@@ -1,79 +0,0 @@
-Software License for The Fraunhofer FDK AAC Codec Library for Android
-
-© Copyright 1995 - 2012 Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V.
- All rights reserved.
-
-1. INTRODUCTION
-The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software that implements
-the MPEG Advanced Audio Coding ("AAC") encoding and decoding scheme for digital audio.
-This FDK AAC Codec software is intended to be used on a wide variety of Android devices.
-
-AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient general perceptual
-audio codecs. AAC-ELD is considered the best-performing full-bandwidth communications codec by
-independent studies and is widely deployed. AAC has been standardized by ISO and IEC as part
-of the MPEG specifications.
-
-Patent licenses for necessary patent claims for the FDK AAC Codec (including those of Fraunhofer)
-may be obtained through Via Licensing (www.vialicensing.com) or through the respective patent owners
-individually for the purpose of encoding or decoding bit streams in products that are compliant with
-the ISO/IEC MPEG audio standards. Please note that most manufacturers of Android devices already license
-these patent claims through Via Licensing or directly from the patent owners, and therefore FDK AAC Codec
-software may already be covered under those patent licenses when it is used for those licensed purposes only.
-
-Commercially-licensed AAC software libraries, including floating-point versions with enhanced sound quality,
-are also available from Fraunhofer. Users are encouraged to check the Fraunhofer website for additional
-applications information and documentation.
-
-2. COPYRIGHT LICENSE
-
-Redistribution and use in source and binary forms, with or without modification, are permitted without
-payment of copyright license fees provided that you satisfy the following conditions:
-
-You must retain the complete text of this software license in redistributions of the FDK AAC Codec or
-your modifications thereto in source code form.
-
-You must retain the complete text of this software license in the documentation and/or other materials
-provided with redistributions of the FDK AAC Codec or your modifications thereto in binary form.
-You must make available free of charge copies of the complete source code of the FDK AAC Codec and your
-modifications thereto to recipients of copies in binary form.
-
-The name of Fraunhofer may not be used to endorse or promote products derived from this library without
-prior written permission.
-
-You may not charge copyright license fees for anyone to use, copy or distribute the FDK AAC Codec
-software or your modifications thereto.
-
-Your modified versions of the FDK AAC Codec must carry prominent notices stating that you changed the software
-and the date of any change. For modified versions of the FDK AAC Codec, the term
-"Fraunhofer FDK AAC Codec Library for Android" must be replaced by the term
-"Third-Party Modified Version of the Fraunhofer FDK AAC Codec Library for Android."
-
-3. NO PATENT LICENSE
-
-NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without limitation the patents of Fraunhofer,
-ARE GRANTED BY THIS SOFTWARE LICENSE. Fraunhofer provides no warranty of patent non-infringement with
-respect to this software.
-
-You may use this FDK AAC Codec software or modifications thereto only for purposes that are authorized
-by appropriate patent licenses.
-
-4. DISCLAIMER
-
-This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright holders and contributors
-"AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, including but not limited to the implied warranties
-of merchantability and fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
-CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary, or consequential damages,
-including but not limited to procurement of substitute goods or services; loss of use, data, or profits,
-or business interruption, however caused and on any theory of liability, whether in contract, strict
-liability, or tort (including negligence), arising in any way out of the use of this software, even if
-advised of the possibility of such damage.
-
-5. CONTACT INFORMATION
-
-Fraunhofer Institute for Integrated Circuits IIS
-Attention: Audio and Multimedia Departments - FDK AAC LL
-Am Wolfsmantel 33
-91058 Erlangen, Germany
-
-www.iis.fraunhofer.de/amm
-amm-info@iis.fraunhofer.de
diff --git a/options/license/FLTK-exception b/options/license/FLTK-exception
deleted file mode 100644
index 836c954b33..0000000000
--- a/options/license/FLTK-exception
+++ /dev/null
@@ -1,17 +0,0 @@
-The FLTK library and included programs are provided under the terms of the GNU Library General Public License (LGPL) with the following exceptions:
-
-Modifications to the FLTK configure script, config header file, and makefiles by themselves to support a specific platform do not constitute a modified or derivative work.
-
-The authors do request that such modifications be contributed to the FLTK project - send all contributions to "fltk-bugs@fltk.org".
-
-Widgets that are subclassed from FLTK widgets do not constitute a derivative work.
-
-Static linking of applications and widgets to the FLTK library does not constitute a derivative work and does not require the author to provide source code for the application or widget, use the shared FLTK libraries, or link their applications or widgets against a user-supplied version of FLTK.
-
-If you link the application or widget to a modified version of FLTK, then the changes to FLTK must be provided under the terms of the LGPL in sections 1, 2, and 4.
-
-You do not have to provide a copy of the FLTK license with programs that are linked to the FLTK library, nor do you have to identify the FLTK license in your program or documentation as required by section 6 of the LGPL.
-
-However, programs must still identify their use of FLTK. The following example statement can be included in user documentation to satisfy this requirement:
-
-[program/widget] is based in part on the work of the FLTK project (http://www.fltk.org).
diff --git a/options/license/FSFAP b/options/license/FSFAP
deleted file mode 100644
index 32bc8a8898..0000000000
--- a/options/license/FSFAP
+++ /dev/null
@@ -1 +0,0 @@
-Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty.
diff --git a/options/license/FSFAP-no-warranty-disclaimer b/options/license/FSFAP-no-warranty-disclaimer
deleted file mode 100644
index 2cc8a93320..0000000000
--- a/options/license/FSFAP-no-warranty-disclaimer
+++ /dev/null
@@ -1,5 +0,0 @@
-Copyright (C) 2008 Micah J. Cowan
-
-Copying and distribution of this file, with or without modification,
-are permitted in any medium without royalty provided the copyright
-notice and this notice are preserved.
diff --git a/options/license/FSFUL b/options/license/FSFUL
deleted file mode 100644
index f976e3c9ac..0000000000
--- a/options/license/FSFUL
+++ /dev/null
@@ -1,3 +0,0 @@
-Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
-
-This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it.
diff --git a/options/license/FSFULLR b/options/license/FSFULLR
deleted file mode 100644
index 2acb219e0a..0000000000
--- a/options/license/FSFULLR
+++ /dev/null
@@ -1,3 +0,0 @@
-Copyright 1996-2006 Free Software Foundation, Inc.
-
-This file is free software; the Free Software Foundation gives unlimited permission to copy and/or distribute it, with or without modifications, as long as this notice is preserved.
diff --git a/options/license/FSFULLRWD b/options/license/FSFULLRWD
deleted file mode 100644
index 8dc0b2e5f0..0000000000
--- a/options/license/FSFULLRWD
+++ /dev/null
@@ -1,11 +0,0 @@
-Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
-
-This Makefile.in is free software; the Free Software Foundation
-gives unlimited permission to copy and/or distribute it,
-with or without modifications, as long as this notice is preserved.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.
diff --git a/options/license/FTL b/options/license/FTL
deleted file mode 100644
index a47d94d106..0000000000
--- a/options/license/FTL
+++ /dev/null
@@ -1,79 +0,0 @@
-The FreeType Project LICENSE
-
-2006-Jan-27
-
-Copyright 1996-2002, 2006 by David Turner, Robert Wilhelm, and Werner Lemberg
-
-Introduction
-
-The FreeType Project is distributed in several archive packages; some of them may contain, in addition to the FreeType font engine, various tools and contributions which rely on, or relate to, the FreeType Project.
-
-This license applies to all files found in such packages, and which do not fall under their own explicit license. The license affects thus the FreeType font engine, the test programs, documentation and makefiles, at the very least.
-
-This license was inspired by the BSD, Artistic, and IJG (Independent JPEG Group) licenses, which all encourage inclusion and use of free software in commercial and freeware products alike. As a consequence, its main points are that:
-
- o We don't promise that this software works. However, we will be interested in any kind of bug reports. (`as is' distribution)
-
- o You can use this software for whatever you want, in parts or full form, without having to pay us. (`royalty-free' usage)
-
- o You may not pretend that you wrote this software. If you use it, or only parts of it, in a program, you must acknowledge somewhere in your documentation that you have used the FreeType code. (`credits')
-
-We specifically permit and encourage the inclusion of this software, with or without modifications, in commercial products. We disclaim all warranties covering The FreeType Project and assume no liability related to The FreeType Project.
-
-Finally, many people asked us for a preferred form for a credit/disclaimer to use in compliance with this license. We thus encourage you to use the following text:
-
- """ Portions of this software are copyright © <year> The FreeType Project (www.freetype.org). All rights reserved. """
-
-Please replace <year> with the value from the FreeType version you actually use.
-
-Legal Terms
-
-0. Definitions
-
-Throughout this license, the terms `package', `FreeType Project', and `FreeType archive' refer to the set of files originally distributed by the authors (David Turner, Robert Wilhelm, and Werner Lemberg) as the `FreeType Project', be they named as alpha, beta or final release.
-
-`You' refers to the licensee, or person using the project, where `using' is a generic term including compiling the project's source code as well as linking it to form a `program' or `executable'. This program is referred to as `a program using the FreeType engine'.
-
-This license applies to all files distributed in the original FreeType Project, including all source code, binaries and documentation, unless otherwise stated in the file in its original, unmodified form as distributed in the original archive. If you are unsure whether or not a particular file is covered by this license, you must contact us to verify this.
-
-The FreeType Project is copyright (C) 1996-2000 by David Turner, Robert Wilhelm, and Werner Lemberg. All rights reserved except as specified below.
-
-1. No Warranty
-
-THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO USE, OF THE FREETYPE PROJECT.
-
-2. Redistribution
-
-This license grants a worldwide, royalty-free, perpetual and irrevocable right and license to use, execute, perform, compile, display, copy, create derivative works of, distribute and sublicense the FreeType Project (in both source and object code forms) and derivative works thereof for any purpose; and to authorize others to exercise some or all of the rights granted herein, subject to the following conditions:
-
- o Redistribution of source code must retain this license file (`FTL.TXT') unaltered; any additions, deletions or changes to the original files must be clearly indicated in accompanying documentation. The copyright notices of the unaltered, original files must be preserved in all copies of source files.
-
- o Redistribution in binary form must provide a disclaimer that states that the software is based in part of the work of the FreeType Team, in the distribution documentation. We also encourage you to put an URL to the FreeType web page in your documentation, though this isn't mandatory.
-
-These conditions apply to any software derived from or based on the FreeType Project, not just the unmodified files. If you use our work, you must acknowledge us. However, no fee need be paid to us.
-
-3. Advertising
-
-Neither the FreeType authors and contributors nor you shall use the name of the other for commercial, advertising, or promotional purposes without specific prior written permission.
-
-We suggest, but do not require, that you use one or more of the following phrases to refer to this software in your documentation or advertising materials: `FreeType Project', `FreeType Engine', `FreeType library', or `FreeType Distribution'.
-
-As you have not signed this license, you are not required to accept it. However, as the FreeType Project is copyrighted material, only this license, or another one contracted with the authors, grants you the right to use, distribute, and modify it. Therefore, by using, distributing, or modifying the FreeType Project, you indicate that you understand and accept all the terms of this license.
-
-4. Contacts
-
-There are two mailing lists related to FreeType:
-
- o freetype@nongnu.org
-
- Discusses general use and applications of FreeType, as well as future and wanted additions to the library and distribution. If you are looking for support, start in this list if you haven't found anything to help you in the documentation.
-
- o freetype-devel@nongnu.org
-
- Discusses bugs, as well as engine internals, design issues, specific licenses, porting, etc.
-
-Our home page can be found at
-
- http://www.freetype.org
-
---- end of FTL.TXT ---
diff --git a/options/license/Fair b/options/license/Fair
deleted file mode 100644
index 430fdb05f9..0000000000
--- a/options/license/Fair
+++ /dev/null
@@ -1,7 +0,0 @@
-Fair License
-
-<Copyright Information>
-
-Usage of the works is permitted provided that this instrument is retained with the works, so that any entity that uses the works is notified of this instrument.
-
-DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.
diff --git a/options/license/Fawkes-Runtime-exception b/options/license/Fawkes-Runtime-exception
deleted file mode 100644
index 0ec93c748b..0000000000
--- a/options/license/Fawkes-Runtime-exception
+++ /dev/null
@@ -1 +0,0 @@
-Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License cover the whole combination. As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. Additionally if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other files to produce an executable, this file does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU General Public License.
diff --git a/options/license/Ferguson-Twofish b/options/license/Ferguson-Twofish
deleted file mode 100644
index 43bb00c3ee..0000000000
--- a/options/license/Ferguson-Twofish
+++ /dev/null
@@ -1,15 +0,0 @@
- The author hereby grants a perpetual license to everybody to
- use this code for any purpose as long as the copyright message is included
- in the source code of this or any derived work.
-
- Yes, this means that you, your company, your club, and anyone else
- can use this code anywhere you want. You can change it and distribute it
- under the GPL, include it in your commercial product without releasing
- the source code, put it on the web, etc.
- The only thing you cannot do is remove my copyright message,
- or distribute any source code based on this implementation that does not
- include my copyright message.
-
- I appreciate a mention in the documentation or credits,
- but I understand if that is difficult to do.
- I also appreciate it if you tell me where and why you used my code.
diff --git a/options/license/Font-exception-2.0 b/options/license/Font-exception-2.0
deleted file mode 100644
index a78eeae73c..0000000000
--- a/options/license/Font-exception-2.0
+++ /dev/null
@@ -1 +0,0 @@
-As a special exception, if you create a document which uses this font, and embed this font or unaltered portions of this font into the document, this font does not by itself cause the resulting document to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the document might be covered by the GNU General Public License. If you modify this font, you may extend this exception to your version of the font, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version.
diff --git a/options/license/Frameworx-1.0 b/options/license/Frameworx-1.0
deleted file mode 100644
index da94f38707..0000000000
--- a/options/license/Frameworx-1.0
+++ /dev/null
@@ -1,69 +0,0 @@
-THE FRAMEWORX OPEN LICENSE 1.0
-
-This License Agreement, The Frameworx Open License 1.0, has been entered into between The Frameworx Company and you, the licensee hereunder, effective as of Your acceptance of the Frameworx Code Base or an Downstream Distribution (each as defined below).
-
-AGREEMENT BACKGROUND
-The Frameworx Company is committed to the belief that open source software results in better quality, greater technical and product innovation in the market place and a more empowered and productive developer and end-user community. Our objective is to ensure that the Frameworx Code Base, and the source code for improvements and innovations to it, remain free and open to the community.To further these beliefs and objectives, we are distributing the Frameworx Code Base, without royalties and in source code form, to the community pursuant to this License Agreement.
-
-AGREEMENT TERMS
-The Frameworx Company and You have agreed as follows:
-
-1.Definitions.The following terms have the following respective meanings:
-
- (a) Frameworx Code Base means the software developed by The Frameworx Company and made available under this License Agreement
-
- (b) Downstream Distribution means any direct or indirect release, distribution or remote availability of software (i) that directly or indirectly contains, or depends for its intended functioning on, the Frameworx Code Base or any portion or element thereof and (ii) in which rights to use and distribute such Frameworx Code Base software depend, directly or indirectly, on the License provided in Section 2 below.
-
- (c) "Source Code" to any software means the preferred form for making modifications to that software, including any associated documentation, interface definition files and compilation or installation scripts, or any version thereof that has been compressed or archived, and can be reconstituted, using an appropriate and generally available archival or compression technology.
-
- (d) Value-Added Services means any commercial or fee-based software-related service, including without limitation: system or application development or consulting; technical or end-user support or training; distribution maintenance, configuration or versioning; or outsourced, hosted or network-based application services.
-
-2. License Grant. Subject to the terms and conditions hereof, The Frameworx Company hereby grants You a non-exclusive license (the License), subject to third party intellectual property claims, and for no fee other than a nominal charge reflecting the costs of physical distribution, to:
-
- (a) use the Frameworx Code Base, in either Source Code or machine-readable form;
-
- (b) make modifications, additions and deletions to the content or structure of the Frameworx Code Base; or
-
- (c) create larger works or derivative works including the Frameworx Code Base or any portion or element thereof; and
-
- (d) release, distribute or make available, either generally or to any specific third-party, any of the foregoing in Source Code or binary form.
-
-3. License Conditions. The grant of the License under Section 1 hereof, and your exercise of all rights in connection with this License Agreement, will remain subject to the following terms and conditions, as well as to the other provisions hereof:
-
- (a) Complete Source Code for any Downstream Distribution directly or indirectly made by You that contains, or depends for its intended functionality on, the Frameworx Code Base, or any portion or element thereof, shall be made freely available to all users thereof on terms and conditions no more restrictive, and no less favorable for any user (including, without limitation, with regard to Source Code availability and royalty-free use) than those terms and conditions provided in this License Agreement.
-
- (b) Any Value-Added Services that you offer or provide, directly or indirectly, in relation to any Downstream Distribution shall be offered and provided on commercial terms that are reasonably commensurate to the fair market value of such Value-Added Services. In addition, the terms and conditions on which any such Value Added Services are so offered or provided shall be consistent with, and shall fully support, the intent and purpose of this License Agreement.
-
- (c) All Downstream Distributions shall:
-
- (i) include all portions and elements of the Frameworx Code Base required to build the Source Code of such Downstream Distribution into a fully functional machine-executable system, or additional build scripts or comparable software necessary and sufficient for such purposes;
-
- (ii) include, in each file containing any portion or element of the Frameworx Code Base, the following identifying legend: This file contains software that has been made available under The Frameworx Open License 1.0. Use and distribution hereof are subject to the restrictions set forth therein.
-
- (iii) include all other copyright notices, authorship credits, warranty disclaimers (including that provided in Section 6 below), legends, documentation, annotations and comments contained in the Frameworx Code Base as provided to You hereunder;
-
- (iv) contain an unaltered copy of the html file named frameworx_community_invitation.html included within the Frameworx Code Base that acknowledges new users and provides them with information on the Frameworx Code Base community;
-
- (v) contain an unaltered copy of the text file named the_frameworx_license.txt included within the Frameworx Code Base that includes a text copy of the form of this License Agreement; and
-
- (vi) prominently display to any viewer or user of the Source Code of such Open Downstream Distribution, in the place and manner normally used for such displays, the following legend:
-
-Source code licensed under from The Frameworx Company is contained herein, and such source code has been obtained either under The Frameworx Open License, or another license granted by The Frameworx Company. Use and distribution hereof is subject to the restrictions provided in the relevant such license and to the copyrights of the licensor thereunder. A copy of The Frameworx Open License is provided in a file named the_frameworx_license.txt and included herein, and may also be available for inspection at http://www.frameworx.com.
-
-4. Restrictions on Open Downstream Distributions. Each Downstream Distribution made by You, and by any party directly or indirectly obtaining rights to the Frameworx Code Base through You, shall be made subject to a license grant or agreement to the extent necessary so that each distributee under that Downstream Distribution will be subject to the same restrictions on re-distribution and use as are binding on You hereunder. You may satisfy this licensing requirement either by:
-
- (a) requiring as a condition to any Downstream Distribution made by you, or by any direct or indirect distributee of Your Downstream Distribution (or any portion or element thereof), that each distributee under the relevant Downstream Distribution obtain a direct license (on the same terms and conditions as those in this License Agreement) from The Frameworx Company; or
-
- (b) sub-licensing all (and not less than all) of Your rights and obligations hereunder to that distributee, including (without limitation) Your obligation to require distributees to be bound by license restrictions as contemplated by this Section 4 above.
-
-The Frameworx Company hereby grants to you all rights to sub-license your rights hereunder as necessary to fully effect the intent and purpose of this Section 4 above, provided, however, that your rights and obligations hereunder shall be unaffected by any such sublicensing. In addition, The Frameworx Company expressly retains all rights to take all appropriate action (including legal action) against any such direct or indirect sub-licensee to ensure its full compliance with the intent and purposes of this License Agreement.
-
-5. Intellectual Property. Except as expressly provided herein, this License Agreement preserves and respects Your and The Frameworx Companys respective intellectual property rights, including, in the case of The Frameworx Company, its copyrights and patent rights relating to the Frameworx Code Base.
-
-6. Warranty Disclaimer. THE SOFTWARE LICENSED HEREUNDER IS PROVIDED ``AS IS.'' ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT, ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OF THIS SOFTWARE, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING (BUT NOT LIMITED TO) PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-7. License Violation. The License, and all of your rights thereunder, shall be deemed automatically terminated and void as of any Downstream Distribution directly or indirectly made or facilitated by You that violates the provisions of this License Agreement, provided, however, that this License Agreement shall survive any such termination in order to remedy the effects of such violation. This License Agreement shall be binding on the legal successors and assigns of the parties hereto.
-
-Your agreement to the foregoing as of the date hereof has been evidenced by your acceptance of the relevant software distribution hereunder.
-
-(C) THE FRAMEWORX COMPANY 2003
diff --git a/options/license/FreeBSD-DOC b/options/license/FreeBSD-DOC
deleted file mode 100644
index 3023a2e948..0000000000
--- a/options/license/FreeBSD-DOC
+++ /dev/null
@@ -1,23 +0,0 @@
-The FreeBSD Documentation License
-
-Copyright 1994-2021 The FreeBSD Project. All rights reserved.
-
-Redistribution and use in source (SGML DocBook) and 'compiled' forms (SGML, HTML, PDF, PostScript, RTF and so forth) with or without modification, are permitted provided that the following conditions are met:
-
- 1. Redistributions of source code (SGML DocBook) must retain the above copyright notice, this list of conditions and the following disclaimer as the first lines of this file unmodified.
-
- 2. Redistributions in compiled form (transformed to other DTDs, converted to PDF, PostScript, RTF and other formats) must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-THIS DOCUMENTATION IS PROVIDED BY THE FREEBSD DOCUMENTATION PROJECT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD DOCUMENTATION PROJECT BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-Manual Pages
-
-Some FreeBSD manual pages contain text from the IEEE Std 1003.1, 2004 Edition, Standard for Information Technology — Portable Operating System Interface (POSIX®) specification. These manual pages are subject to the following terms:
-
- The Institute of Electrical and Electronics Engineers and The Open Group, have given us permission to reprint portions of their documentation.
-
- In the following statement, the phrase "this text" refers to portions of the system documentation.
-
- Portions of this text are reprinted and reproduced in electronic form in the FreeBSD manual pages, from IEEE Std 1003.1, 2004 Edition, Standard for Information Technology — Portable Operating System Interface (POSIX), The Open Group Base Specifications Issue 6, Copyright© 2001-2004 by the Institute of Electrical and Electronics Engineers, Inc and The Open Group. In the event of any discrepancy between these versions and the original IEEE and The Open Group Standard, the original IEEE and The Open Group Standard is the referee document. The original Standard can be obtained online at https://www.opengroup.org/membership/forums/platform/unix.
-
- This notice shall appear on any product containing this material.
diff --git a/options/license/FreeImage b/options/license/FreeImage
deleted file mode 100644
index 1b800d0628..0000000000
--- a/options/license/FreeImage
+++ /dev/null
@@ -1,117 +0,0 @@
-FreeImage Public License - Version 1.0
-
-1. Definitions.
-
- 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications.
-
- 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
- 1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
- 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
- 1.5. "Executable" means Covered Code in any form other than Source Code.
-
- 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
- 1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.8. "License" means this document.
-
- 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a
-Modification is:
-
- A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
- B. Any new file that contains any part of the Original Code or previous Modifications.
-
- 1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
- 1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or a list of source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
- 1.12. "You" means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
-2. Source Code License.
-
- 2.1. The Initial Developer Grant.
- The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, or as part of a Larger Work; and
-
- (b) under patents now or hereafter owned or controlled by Initial Developer, to make, have made, use and sell ("Utilize") the Original Code (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Original Code (or portions thereof) and not to any greater extent that may be necessary to Utilize further Modifications or combinations.
-
- 2.2. Contributor Grant.
- Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code or as part of a Larger Work; and
-
- (b) under patents now or hereafter owned or controlled by Contributor, to Utilize the Contributor Version (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Contributor Version (or portions thereof), and not to any greater extent that may be necessary to Utilize further Modifications or combinations.
-
-3. Distribution Obligations.
-
- 3.1. Application of License.
- The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
- 3.2. Availability of Source Code.
- Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
- 3.3. Description of Modifications.
- You must cause all Covered Code to which you contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
- 3.4. Intellectual Property Matters
-
- (a) Third Party Claims.
- If You have knowledge that a party claims an intellectual property right in particular functionality or code (or its utilization under this License), you must include a text file with the source code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after You make Your Modification available as described in Section 3.2, You shall promptly modify the LEGAL file in all copies You make available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
-
- (b) Contributor APIs.
- If Your Modification is an application programming interface and You own or control patents which are reasonably necessary to implement that API, you must also include this information in the LEGAL file.
-
- 3.5. Required Notices.
- You must duplicate the notice in Exhibit A in each file of the Source Code, and this License in any documentation for the Source Code, where You describe recipients' rights relating to Covered Code. If You created one or more Modification(s), You may add your name as a Contributor to the notice described in Exhibit A. If it is not possible to put such notice in a particular Source Code file due to its structure, then you must include such notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
- 3.6. Distribution of Executable Versions.
- You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You descr ibe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code under a license of Your choice, which may contain terms different from this License,provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
- 3.7. Larger Works.
- You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Application of this License.
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A, and to related Covered Code.
-
-6. Versions of the License.
-
- 6.1. New Versions.
- Floris van den Berg may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
- 6.2. Effect of New Versions.
- Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Floris van den Berg
-No one other than Floris van den Berg has the right to modify the terms applicable to Covered Code created under this License.
-
- 6.3. Derivative Works.
- If you create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), you must (a) rename Your license so that the phrases "FreeImage", `FreeImage Public License", "FIPL", or any confusingly similar phrase do not appear anywhere in your license and (b) otherwise make it clear that your version of the license contains terms which differ from the FreeImage Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
-7. DISCLAIMER OF WARRANTY.
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-9. LIMITATION OF LIABILITY.
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO YOU OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS.
-The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
-11. MISCELLANEOUS.
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by Dutch law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in, the The Netherlands: (a) unless otherwise agreed in writing, all disputes relating to this License (excepting any dispute relating to intellectual property rights) shall be subject to final and binding arbitration, with the losing party paying all costs of arbitration; (b) any arbitration relating to this Agreement shall be held in Almelo, The Netherlands; and (c) any litigation relating to this Agreement shall be subject to the jurisdiction of the court of Almelo, The Netherlands with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys fees and expenses. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-12. RESPONSIBILITY FOR CLAIMS.
-Except in cases where another Contributor has failed to comply with Section 3.4, You are responsible for damages arising, directly or indirectly, out of Your utilization of rights under this License, based on the number of copies of Covered Code you made available, the revenues you received from utilizing such rights, and other relevant factors. You agree to work with affected parties to distribute responsibility on an equitable basis.
-
-EXHIBIT A.
-
-"The contents of this file are subject to the FreeImage Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://home.wxs.nl/~flvdberg/freeimage-license.txt
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
diff --git a/options/license/Furuseth b/options/license/Furuseth
deleted file mode 100644
index 55feeef90b..0000000000
--- a/options/license/Furuseth
+++ /dev/null
@@ -1,13 +0,0 @@
-Portions Copyright 1999-2008 Howard Y.H. Chu.
-Portions Copyright 1999-2008 Symas Corporation.
-Portions Copyright 1998-2003 Hallvard B. Furuseth.
-Portions Copyright 2007-2011 Gavin Henry.
-Portions Copyright 2007-2011 Suretec Systems Ltd.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that this notice is preserved.
-The names of the copyright holders may not be used to endorse or
-promote products derived from this software without their specific
-prior written permission. This software is provided ``as is''
-without express or implied warranty.
diff --git a/options/license/GCC-exception-2.0 b/options/license/GCC-exception-2.0
deleted file mode 100644
index 642ecdd736..0000000000
--- a/options/license/GCC-exception-2.0
+++ /dev/null
@@ -1 +0,0 @@
-In addition to the permissions in the GNU General Public License, the Free Software Foundation gives you unlimited permission to link the compiled version of this file into combinations with other programs, and to distribute those combinations without any restriction coming from the use of this file. (The General Public License restrictions do apply in other respects; for example, they cover modification of the file, and distribution when not linked into a combine executable.)
diff --git a/options/license/GCC-exception-2.0-note b/options/license/GCC-exception-2.0-note
deleted file mode 100644
index 654099aac0..0000000000
--- a/options/license/GCC-exception-2.0-note
+++ /dev/null
@@ -1,16 +0,0 @@
- In addition to the permissions in the GNU Lesser General Public
- License, the Free Software Foundation gives you unlimited
- permission to link the compiled version of this file with other
- programs, and to distribute those programs without any restriction
- coming from the use of this file. (The GNU Lesser General Public
- License restrictions do apply in other respects; for example, they
- cover modification of the file, and distribution when not linked
- into another program.)
-
- Note that people who make modified versions of this file are not
- obligated to grant this special exception for their modified
- versions; it is their choice whether to do so. The GNU Lesser
- General Public License gives permission to release a modified
- version without this exception; this exception also makes it
- possible to release a modified version which carries forward this
- exception.
diff --git a/options/license/GCC-exception-3.1 b/options/license/GCC-exception-3.1
deleted file mode 100644
index 3d8345bec5..0000000000
--- a/options/license/GCC-exception-3.1
+++ /dev/null
@@ -1,33 +0,0 @@
-GCC RUNTIME LIBRARY EXCEPTION
-
-Version 3.1, 31 March 2009
-
-General information: http://www.gnu.org/licenses/gcc-exception.html
-Copyright (C) 2009 Free Software Foundation, Inc. <http://fsf.org/>
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-This GCC Runtime Library Exception ("Exception") is an additional permission under section 7 of the GNU General Public License, version 3 ("GPLv3"). It applies to a given file (the "Runtime Library") that bears a notice placed by the copyright holder of the file stating that the file is governed by GPLv3 along with this Exception.
-
-When you use GCC to compile a program, GCC may combine portions of certain GCC header files and runtime libraries with the compiled program. The purpose of this Exception is to allow compilation of non-GPL (including proprietary) programs to use, in this way, the header files and runtime libraries covered by this Exception.
-
-0. Definitions.
-
-A file is an "Independent Module" if it either requires the Runtime Library for execution after a Compilation Process, or makes use of an interface provided by the Runtime Library, but is not otherwise based on the Runtime Library.
-
-"GCC" means a version of the GNU Compiler Collection, with or without modifications, governed by version 3 (or a specified later version) of the GNU General Public License (GPL) with the option of using any subsequent versions published by the FSF.
-
-"GPL-compatible Software" is software whose conditions of propagation, modification and use would permit combination with GCC in accord with the license of GCC.
-
-"Target Code" refers to output from any compiler for a real or virtual target processor architecture, in executable form or suitable for input to an assembler, loader, linker and/or execution phase. Notwithstanding that, Target Code does not include data in any format that is used as a compiler intermediate representation, or used for producing a compiler intermediate representation.
-
-The "Compilation Process" transforms code entirely represented in non-intermediate languages designed for human-written code, and/or in Java Virtual Machine byte code, into Target Code. Thus, for example, use of source code generators and preprocessors need not be considered part of the Compilation Process, since the Compilation Process can be understood as starting with the output of the generators or preprocessors.
-
-A Compilation Process is "Eligible" if it is done using GCC, alone or with other GPL-compatible software, or if it is done without using any work based on GCC. For example, using non-GPL-compatible Software to optimize any GCC intermediate representations would not qualify as an Eligible Compilation Process.
-
-1. Grant of Additional Permission.
-
-You have permission to propagate a work of Target Code formed by combining the Runtime Library with Independent Modules, even if such propagation would otherwise violate the terms of GPLv3, provided that all Target Code was generated by Eligible Compilation Processes. You may then convey such a combination under terms of your choice, consistent with the licensing of the Independent Modules.
-
-2. No Weakening of GCC Copyleft.
-
-The availability of this Exception does not imply any general presumption that third-party software is unaffected by the copyleft requirements of the license of GCC.
diff --git a/options/license/GCR-docs b/options/license/GCR-docs
deleted file mode 100644
index d5c1293c96..0000000000
--- a/options/license/GCR-docs
+++ /dev/null
@@ -1,30 +0,0 @@
-This work may be reproduced and distributed in whole or in part, in
-any medium, physical or electronic, so as long as this copyright
-notice remains intact and unchanged on all copies. Commercial
-redistribution is permitted and encouraged, but you may not
-redistribute, in whole or in part, under terms more restrictive than
-those under which you received it. If you redistribute a modified or
-translated version of this work, you must also make the source code to
-the modified or translated version available in electronic form
-without charge. However, mere aggregation as part of a larger work
-shall not count as a modification for this purpose.
-
-All code examples in this work are placed into the public domain,
-and may be used, modified and redistributed without restriction.
-
-BECAUSE THIS WORK IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE WORK, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE WORK "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. SHOULD THE WORK PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY REPAIR OR CORRECTION.
-
-IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE WORK AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-WORK, EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
diff --git a/options/license/GD b/options/license/GD
deleted file mode 100644
index 534b5978c7..0000000000
--- a/options/license/GD
+++ /dev/null
@@ -1,24 +0,0 @@
-Credits and license terms
-
-In order to resolve any possible confusion regarding the authorship of gd, the following copyright statement covers all of the authors who have required such a statement.  If you are aware of any oversights in this copyright notice, please contact Pierre-A.  Joye who will be pleased to correct them.
-
- • Portions copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 by Cold Spring Harbor Laboratory.  Funded under Grant P41-RR02188 by the National Institutes of Health.
- • Portions copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 by Boutell.Com, Inc.
- • Portions relating to GD2 format copyright 1999, 2000, 2001, 2002, 2003, 2004 Philip Warner.
- • Portions relating to PNG copyright 1999, 2000, 2001, 2002, 2003, 2004 Greg Roelofs.
- • Portions relating to gdttf.c copyright 1999, 2000, 2001, 2002, 2003, 2004 John Ellson (ellson@graphviz.org).
- • Portions relating to gdft.c copyright 2001, 2002, 2003, 2004 John Ellson (ellson@graphviz.org).
- • Portions copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Pierre-Alain Joye (pierre@libgd.org).
- • Portions relating to JPEG and to color quantization copyright 2000, 2001, 2002, 2003, 2004, Doug Becker and copyright © 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Thomas G.  Lane.  This software is based in part on the work of the Independent JPEG Group.  See the file README-JPEG.TXT for more information.
- • Portions relating to GIF compression copyright 1989 by Jef Poskanzer and David Rowley, with modifications for thread safety by Thomas Boutell.
- • Portions relating to GIF decompression copyright 1990, 1991, 1993 by David Koblas, with modifications for thread safety by Thomas Boutell.
- • Portions relating to WBMP copyright 2000, 2001, 2002, 2003, 2004 Maurice Szmurlo and Johan Van den Brande.
- • Portions relating to GIF animations copyright 2004 Jaakko Hyvätti (jaakko.hyvatti@iki.fi)
-
-Permission has been granted to copy, distribute and modify gd in any context without fee, including a commercial application, provided that this notice is present in user-accessible supporting documentation.
-
-This does not affect your ownership of the derived work itself, and the intent is to assure proper credit for the authors of gd, not to interfere with your productive use of gd.  If you have questions, ask.  “Derived works†includes all programs that utilize the library.  Credit must be given in user-accessible documentation.
-
-This software is provided “AS IS.â€Â  The copyright holders disclaim all warranties, either express or implied, including but not limited to implied warranties of merchantability and fitness for a particular purpose, with respect to this code and accompanying documentation.
-
-Although their code does not appear in the current release, the authors wish to thank David Koblas, David Rowley, and Hutchison Avenue Software Corporation for their prior contributions.
diff --git a/options/license/GFDL-1.1-invariants-only b/options/license/GFDL-1.1-invariants-only
deleted file mode 100644
index e60192009a..0000000000
--- a/options/license/GFDL-1.1-invariants-only
+++ /dev/null
@@ -1,119 +0,0 @@
-GNU Free Documentation License
-Version 1.1, March 2000
-
-Copyright (C) 2000 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other written document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you".
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (For example, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modification by readers is not Transparent. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML designed for human modification. Opaque formats include PostScript, PDF, proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies of the Document numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a publicly-accessible computer-network location containing a complete Transparent copy of the Document, free of added material, which the general network-using public has access to download anonymously at no charge using public-standard network protocols. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has less than five).
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
- H. Include an unaltered copy of this License.
- I. Preserve the section entitled "History", and its title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. In any section entitled "Acknowledgements" or "Dedications", preserve the section's title, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section as "Endorsements" or to conflict in title with any Invariant Section.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections entitled "History" in the various original documents, forming one section entitled "History"; likewise combine any sections entitled "Acknowledgements", and any sections entitled "Dedications". You must delete all sections entitled "Endorsements."
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, does not as a whole count as a Modified Version of the Document, provided no compilation copyright is claimed for the compilation. Such a compilation is called an "aggregate", and this License does not apply to the other self-contained works thus compiled with the Document, on account of their being thus compiled, if they are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one quarter of the entire aggregate, the Document's Cover Texts may be placed on covers that surround only the Document within the aggregate. Otherwise they must appear on covers around the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License provided that you also include the original English version of this License. In case of a disagreement between the translation and the original English version of this License, the original English version will prevail.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have no Invariant Sections, write "with no Invariant Sections" instead of saying which ones are invariant. If you have no Front-Cover Texts, write "no Front-Cover Texts" instead of "Front-Cover Texts being LIST"; likewise for Back-Cover Texts.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.1-invariants-or-later b/options/license/GFDL-1.1-invariants-or-later
deleted file mode 100644
index e60192009a..0000000000
--- a/options/license/GFDL-1.1-invariants-or-later
+++ /dev/null
@@ -1,119 +0,0 @@
-GNU Free Documentation License
-Version 1.1, March 2000
-
-Copyright (C) 2000 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other written document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you".
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (For example, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modification by readers is not Transparent. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML designed for human modification. Opaque formats include PostScript, PDF, proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies of the Document numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a publicly-accessible computer-network location containing a complete Transparent copy of the Document, free of added material, which the general network-using public has access to download anonymously at no charge using public-standard network protocols. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has less than five).
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
- H. Include an unaltered copy of this License.
- I. Preserve the section entitled "History", and its title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. In any section entitled "Acknowledgements" or "Dedications", preserve the section's title, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section as "Endorsements" or to conflict in title with any Invariant Section.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections entitled "History" in the various original documents, forming one section entitled "History"; likewise combine any sections entitled "Acknowledgements", and any sections entitled "Dedications". You must delete all sections entitled "Endorsements."
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, does not as a whole count as a Modified Version of the Document, provided no compilation copyright is claimed for the compilation. Such a compilation is called an "aggregate", and this License does not apply to the other self-contained works thus compiled with the Document, on account of their being thus compiled, if they are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one quarter of the entire aggregate, the Document's Cover Texts may be placed on covers that surround only the Document within the aggregate. Otherwise they must appear on covers around the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License provided that you also include the original English version of this License. In case of a disagreement between the translation and the original English version of this License, the original English version will prevail.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have no Invariant Sections, write "with no Invariant Sections" instead of saying which ones are invariant. If you have no Front-Cover Texts, write "no Front-Cover Texts" instead of "Front-Cover Texts being LIST"; likewise for Back-Cover Texts.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.1-no-invariants-only b/options/license/GFDL-1.1-no-invariants-only
deleted file mode 100644
index e60192009a..0000000000
--- a/options/license/GFDL-1.1-no-invariants-only
+++ /dev/null
@@ -1,119 +0,0 @@
-GNU Free Documentation License
-Version 1.1, March 2000
-
-Copyright (C) 2000 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other written document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you".
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (For example, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modification by readers is not Transparent. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML designed for human modification. Opaque formats include PostScript, PDF, proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies of the Document numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a publicly-accessible computer-network location containing a complete Transparent copy of the Document, free of added material, which the general network-using public has access to download anonymously at no charge using public-standard network protocols. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has less than five).
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
- H. Include an unaltered copy of this License.
- I. Preserve the section entitled "History", and its title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. In any section entitled "Acknowledgements" or "Dedications", preserve the section's title, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section as "Endorsements" or to conflict in title with any Invariant Section.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections entitled "History" in the various original documents, forming one section entitled "History"; likewise combine any sections entitled "Acknowledgements", and any sections entitled "Dedications". You must delete all sections entitled "Endorsements."
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, does not as a whole count as a Modified Version of the Document, provided no compilation copyright is claimed for the compilation. Such a compilation is called an "aggregate", and this License does not apply to the other self-contained works thus compiled with the Document, on account of their being thus compiled, if they are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one quarter of the entire aggregate, the Document's Cover Texts may be placed on covers that surround only the Document within the aggregate. Otherwise they must appear on covers around the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License provided that you also include the original English version of this License. In case of a disagreement between the translation and the original English version of this License, the original English version will prevail.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have no Invariant Sections, write "with no Invariant Sections" instead of saying which ones are invariant. If you have no Front-Cover Texts, write "no Front-Cover Texts" instead of "Front-Cover Texts being LIST"; likewise for Back-Cover Texts.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.1-no-invariants-or-later b/options/license/GFDL-1.1-no-invariants-or-later
deleted file mode 100644
index e60192009a..0000000000
--- a/options/license/GFDL-1.1-no-invariants-or-later
+++ /dev/null
@@ -1,119 +0,0 @@
-GNU Free Documentation License
-Version 1.1, March 2000
-
-Copyright (C) 2000 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other written document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you".
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (For example, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modification by readers is not Transparent. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML designed for human modification. Opaque formats include PostScript, PDF, proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies of the Document numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a publicly-accessible computer-network location containing a complete Transparent copy of the Document, free of added material, which the general network-using public has access to download anonymously at no charge using public-standard network protocols. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has less than five).
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
- H. Include an unaltered copy of this License.
- I. Preserve the section entitled "History", and its title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. In any section entitled "Acknowledgements" or "Dedications", preserve the section's title, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section as "Endorsements" or to conflict in title with any Invariant Section.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections entitled "History" in the various original documents, forming one section entitled "History"; likewise combine any sections entitled "Acknowledgements", and any sections entitled "Dedications". You must delete all sections entitled "Endorsements."
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, does not as a whole count as a Modified Version of the Document, provided no compilation copyright is claimed for the compilation. Such a compilation is called an "aggregate", and this License does not apply to the other self-contained works thus compiled with the Document, on account of their being thus compiled, if they are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one quarter of the entire aggregate, the Document's Cover Texts may be placed on covers that surround only the Document within the aggregate. Otherwise they must appear on covers around the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License provided that you also include the original English version of this License. In case of a disagreement between the translation and the original English version of this License, the original English version will prevail.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have no Invariant Sections, write "with no Invariant Sections" instead of saying which ones are invariant. If you have no Front-Cover Texts, write "no Front-Cover Texts" instead of "Front-Cover Texts being LIST"; likewise for Back-Cover Texts.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.1-only b/options/license/GFDL-1.1-only
deleted file mode 100644
index e60192009a..0000000000
--- a/options/license/GFDL-1.1-only
+++ /dev/null
@@ -1,119 +0,0 @@
-GNU Free Documentation License
-Version 1.1, March 2000
-
-Copyright (C) 2000 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other written document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you".
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (For example, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modification by readers is not Transparent. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML designed for human modification. Opaque formats include PostScript, PDF, proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies of the Document numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a publicly-accessible computer-network location containing a complete Transparent copy of the Document, free of added material, which the general network-using public has access to download anonymously at no charge using public-standard network protocols. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has less than five).
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
- H. Include an unaltered copy of this License.
- I. Preserve the section entitled "History", and its title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. In any section entitled "Acknowledgements" or "Dedications", preserve the section's title, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section as "Endorsements" or to conflict in title with any Invariant Section.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections entitled "History" in the various original documents, forming one section entitled "History"; likewise combine any sections entitled "Acknowledgements", and any sections entitled "Dedications". You must delete all sections entitled "Endorsements."
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, does not as a whole count as a Modified Version of the Document, provided no compilation copyright is claimed for the compilation. Such a compilation is called an "aggregate", and this License does not apply to the other self-contained works thus compiled with the Document, on account of their being thus compiled, if they are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one quarter of the entire aggregate, the Document's Cover Texts may be placed on covers that surround only the Document within the aggregate. Otherwise they must appear on covers around the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License provided that you also include the original English version of this License. In case of a disagreement between the translation and the original English version of this License, the original English version will prevail.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have no Invariant Sections, write "with no Invariant Sections" instead of saying which ones are invariant. If you have no Front-Cover Texts, write "no Front-Cover Texts" instead of "Front-Cover Texts being LIST"; likewise for Back-Cover Texts.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.1-or-later b/options/license/GFDL-1.1-or-later
deleted file mode 100644
index e60192009a..0000000000
--- a/options/license/GFDL-1.1-or-later
+++ /dev/null
@@ -1,119 +0,0 @@
-GNU Free Documentation License
-Version 1.1, March 2000
-
-Copyright (C) 2000 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other written document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you".
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (For example, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modification by readers is not Transparent. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML designed for human modification. Opaque formats include PostScript, PDF, proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies of the Document numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a publicly-accessible computer-network location containing a complete Transparent copy of the Document, free of added material, which the general network-using public has access to download anonymously at no charge using public-standard network protocols. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has less than five).
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
- H. Include an unaltered copy of this License.
- I. Preserve the section entitled "History", and its title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. In any section entitled "Acknowledgements" or "Dedications", preserve the section's title, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section as "Endorsements" or to conflict in title with any Invariant Section.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections entitled "History" in the various original documents, forming one section entitled "History"; likewise combine any sections entitled "Acknowledgements", and any sections entitled "Dedications". You must delete all sections entitled "Endorsements."
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, does not as a whole count as a Modified Version of the Document, provided no compilation copyright is claimed for the compilation. Such a compilation is called an "aggregate", and this License does not apply to the other self-contained works thus compiled with the Document, on account of their being thus compiled, if they are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one quarter of the entire aggregate, the Document's Cover Texts may be placed on covers that surround only the Document within the aggregate. Otherwise they must appear on covers around the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License provided that you also include the original English version of this License. In case of a disagreement between the translation and the original English version of this License, the original English version will prevail.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have no Invariant Sections, write "with no Invariant Sections" instead of saying which ones are invariant. If you have no Front-Cover Texts, write "no Front-Cover Texts" instead of "Front-Cover Texts being LIST"; likewise for Back-Cover Texts.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.2-invariants-only b/options/license/GFDL-1.2-invariants-only
deleted file mode 100644
index 83c375aba1..0000000000
--- a/options/license/GFDL-1.2-invariants-only
+++ /dev/null
@@ -1,130 +0,0 @@
-GNU Free Documentation License
-Version 1.2, November 2002
-
-Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
-
-The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
- H. Include an unaltered copy of this License.
- I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
- O. Preserve any Warranty Disclaimers.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
-
-If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:
-
- with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
-
-If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.2-invariants-or-later b/options/license/GFDL-1.2-invariants-or-later
deleted file mode 100644
index 83c375aba1..0000000000
--- a/options/license/GFDL-1.2-invariants-or-later
+++ /dev/null
@@ -1,130 +0,0 @@
-GNU Free Documentation License
-Version 1.2, November 2002
-
-Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
-
-The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
- H. Include an unaltered copy of this License.
- I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
- O. Preserve any Warranty Disclaimers.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
-
-If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:
-
- with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
-
-If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.2-no-invariants-only b/options/license/GFDL-1.2-no-invariants-only
deleted file mode 100644
index 83c375aba1..0000000000
--- a/options/license/GFDL-1.2-no-invariants-only
+++ /dev/null
@@ -1,130 +0,0 @@
-GNU Free Documentation License
-Version 1.2, November 2002
-
-Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
-
-The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
- H. Include an unaltered copy of this License.
- I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
- O. Preserve any Warranty Disclaimers.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
-
-If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:
-
- with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
-
-If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.2-no-invariants-or-later b/options/license/GFDL-1.2-no-invariants-or-later
deleted file mode 100644
index 83c375aba1..0000000000
--- a/options/license/GFDL-1.2-no-invariants-or-later
+++ /dev/null
@@ -1,130 +0,0 @@
-GNU Free Documentation License
-Version 1.2, November 2002
-
-Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
-
-The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
- H. Include an unaltered copy of this License.
- I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
- O. Preserve any Warranty Disclaimers.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
-
-If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:
-
- with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
-
-If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.2-only b/options/license/GFDL-1.2-only
deleted file mode 100644
index 83c375aba1..0000000000
--- a/options/license/GFDL-1.2-only
+++ /dev/null
@@ -1,130 +0,0 @@
-GNU Free Documentation License
-Version 1.2, November 2002
-
-Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
-
-The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
- H. Include an unaltered copy of this License.
- I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
- O. Preserve any Warranty Disclaimers.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
-
-If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:
-
- with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
-
-If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.2-or-later b/options/license/GFDL-1.2-or-later
deleted file mode 100644
index 83c375aba1..0000000000
--- a/options/license/GFDL-1.2-or-later
+++ /dev/null
@@ -1,130 +0,0 @@
-GNU Free Documentation License
-Version 1.2, November 2002
-
-Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
-
-The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
- H. Include an unaltered copy of this License.
- I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
- O. Preserve any Warranty Disclaimers.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
-
-If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:
-
- with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
-
-If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.3-invariants-only b/options/license/GFDL-1.3-invariants-only
deleted file mode 100644
index b51dc2ab16..0000000000
--- a/options/license/GFDL-1.3-invariants-only
+++ /dev/null
@@ -1,149 +0,0 @@
-GNU Free Documentation License
-Version 1.3, 3 November 2008
-
-Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. <http://fsf.org/>
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-The "publisher" means any person or entity that distributes copies of the Document to the public.
-
-A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
-
-The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. H. Include an unaltered copy of this License.
- I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
- O. Preserve any Warranty Disclaimers.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
-
-If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License.
-
-However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
-
-Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Document.
-
-11. RELICENSING
-
-"Massive Multiauthor Collaboration Site" (or "MMC Site") means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A "Massive Multiauthor Collaboration" (or "MMC") contained in the site means any set of copyrightable works thus published on the MMC site.
-
-"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization.
-
-"Incorporate" means to publish or republish a Document, in whole or in part, as part of another Document.
-
-An MMC is "eligible for relicensing" if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008.
-
-The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:
-
- with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
-
-If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.3-invariants-or-later b/options/license/GFDL-1.3-invariants-or-later
deleted file mode 100644
index b51dc2ab16..0000000000
--- a/options/license/GFDL-1.3-invariants-or-later
+++ /dev/null
@@ -1,149 +0,0 @@
-GNU Free Documentation License
-Version 1.3, 3 November 2008
-
-Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. <http://fsf.org/>
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-The "publisher" means any person or entity that distributes copies of the Document to the public.
-
-A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
-
-The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. H. Include an unaltered copy of this License.
- I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
- O. Preserve any Warranty Disclaimers.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
-
-If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License.
-
-However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
-
-Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Document.
-
-11. RELICENSING
-
-"Massive Multiauthor Collaboration Site" (or "MMC Site") means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A "Massive Multiauthor Collaboration" (or "MMC") contained in the site means any set of copyrightable works thus published on the MMC site.
-
-"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization.
-
-"Incorporate" means to publish or republish a Document, in whole or in part, as part of another Document.
-
-An MMC is "eligible for relicensing" if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008.
-
-The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:
-
- with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
-
-If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.3-no-invariants-only b/options/license/GFDL-1.3-no-invariants-only
deleted file mode 100644
index b51dc2ab16..0000000000
--- a/options/license/GFDL-1.3-no-invariants-only
+++ /dev/null
@@ -1,149 +0,0 @@
-GNU Free Documentation License
-Version 1.3, 3 November 2008
-
-Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. <http://fsf.org/>
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-The "publisher" means any person or entity that distributes copies of the Document to the public.
-
-A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
-
-The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. H. Include an unaltered copy of this License.
- I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
- O. Preserve any Warranty Disclaimers.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
-
-If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License.
-
-However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
-
-Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Document.
-
-11. RELICENSING
-
-"Massive Multiauthor Collaboration Site" (or "MMC Site") means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A "Massive Multiauthor Collaboration" (or "MMC") contained in the site means any set of copyrightable works thus published on the MMC site.
-
-"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization.
-
-"Incorporate" means to publish or republish a Document, in whole or in part, as part of another Document.
-
-An MMC is "eligible for relicensing" if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008.
-
-The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:
-
- with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
-
-If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.3-no-invariants-or-later b/options/license/GFDL-1.3-no-invariants-or-later
deleted file mode 100644
index b51dc2ab16..0000000000
--- a/options/license/GFDL-1.3-no-invariants-or-later
+++ /dev/null
@@ -1,149 +0,0 @@
-GNU Free Documentation License
-Version 1.3, 3 November 2008
-
-Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. <http://fsf.org/>
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-The "publisher" means any person or entity that distributes copies of the Document to the public.
-
-A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
-
-The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. H. Include an unaltered copy of this License.
- I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
- O. Preserve any Warranty Disclaimers.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
-
-If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License.
-
-However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
-
-Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Document.
-
-11. RELICENSING
-
-"Massive Multiauthor Collaboration Site" (or "MMC Site") means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A "Massive Multiauthor Collaboration" (or "MMC") contained in the site means any set of copyrightable works thus published on the MMC site.
-
-"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization.
-
-"Incorporate" means to publish or republish a Document, in whole or in part, as part of another Document.
-
-An MMC is "eligible for relicensing" if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008.
-
-The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:
-
- with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
-
-If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.3-only b/options/license/GFDL-1.3-only
deleted file mode 100644
index b51dc2ab16..0000000000
--- a/options/license/GFDL-1.3-only
+++ /dev/null
@@ -1,149 +0,0 @@
-GNU Free Documentation License
-Version 1.3, 3 November 2008
-
-Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. <http://fsf.org/>
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-The "publisher" means any person or entity that distributes copies of the Document to the public.
-
-A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
-
-The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. H. Include an unaltered copy of this License.
- I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
- O. Preserve any Warranty Disclaimers.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
-
-If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License.
-
-However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
-
-Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Document.
-
-11. RELICENSING
-
-"Massive Multiauthor Collaboration Site" (or "MMC Site") means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A "Massive Multiauthor Collaboration" (or "MMC") contained in the site means any set of copyrightable works thus published on the MMC site.
-
-"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization.
-
-"Incorporate" means to publish or republish a Document, in whole or in part, as part of another Document.
-
-An MMC is "eligible for relicensing" if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008.
-
-The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:
-
- with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
-
-If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GFDL-1.3-or-later b/options/license/GFDL-1.3-or-later
deleted file mode 100644
index b51dc2ab16..0000000000
--- a/options/license/GFDL-1.3-or-later
+++ /dev/null
@@ -1,149 +0,0 @@
-GNU Free Documentation License
-Version 1.3, 3 November 2008
-
-Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. <http://fsf.org/>
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
-
-We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
-
-A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
-
-The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
-
-A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
-
-The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
-
-The "publisher" means any person or entity that distributes copies of the Document to the public.
-
-A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
-
-The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and you may publicly display copies.
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
-
-If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
-
-It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
-
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. H. Include an unaltered copy of this License.
- I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
- O. Preserve any Warranty Disclaimers.
-
-If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
-
-You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
-
-The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
-
-If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License.
-
-However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
-
-Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it.
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Document.
-
-11. RELICENSING
-
-"Massive Multiauthor Collaboration Site" (or "MMC Site") means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A "Massive Multiauthor Collaboration" (or "MMC") contained in the site means any set of copyrightable works thus published on the MMC site.
-
-"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization.
-
-"Incorporate" means to publish or republish a Document, in whole or in part, as part of another Document.
-
-An MMC is "eligible for relicensing" if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008.
-
-The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing.
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
-
- Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
-
-If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:
-
- with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
-
-If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
-
-If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
diff --git a/options/license/GL2PS b/options/license/GL2PS
deleted file mode 100644
index ee2af779d9..0000000000
--- a/options/license/GL2PS
+++ /dev/null
@@ -1,13 +0,0 @@
-GL2PS LICENSE Version 2, November 2003
-
-Copyright (C) 2003, Christophe Geuzaine
-
-Permission to use, copy, and distribute this software and its documentation for any purpose with or without fee is hereby granted, provided that the copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation.
-
-Permission to modify and distribute modified versions of this software is granted, provided that:
-
-1) the modifications are licensed under the same terms as this software;
-
-2) you make available the source code of any modifications that you distribute, either on the same media as you distribute any executable or other form of this software, or via a mechanism generally accepted in the software development community for the electronic transfer of data.
-
-This software is provided "as is" without express or implied warranty.
diff --git a/options/license/GLWTPL b/options/license/GLWTPL
deleted file mode 100644
index a0f7ec4883..0000000000
--- a/options/license/GLWTPL
+++ /dev/null
@@ -1,25 +0,0 @@
- GLWT(Good Luck With That) Public License
- Copyright (c) Everyone, except Author
-
-Everyone is permitted to copy, distribute, modify, merge, sell, publish,
-sublicense or whatever they want with this software but at their OWN RISK.
-
- Preamble
-
-The author has absolutely no clue what the code in this project does.
-It might just work or not, there is no third option.
-
-
- GOOD LUCK WITH THAT PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION, AND MODIFICATION
-
- 0. You just DO WHATEVER YOU WANT TO as long as you NEVER LEAVE A
-TRACE TO TRACK THE AUTHOR of the original product to blame for or hold
-responsible.
-
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-
-Good luck and Godspeed.
diff --git a/options/license/GNAT-exception b/options/license/GNAT-exception
deleted file mode 100644
index 2b5a96a62b..0000000000
--- a/options/license/GNAT-exception
+++ /dev/null
@@ -1,6 +0,0 @@
-As a special exception, if other files instantiate generics from this
-unit, or you link this unit with other files to produce an executable,
-this unit does not by itself cause the resulting executable to be
-covered by the GNU General Public License. This exception does not
-however invalidate any other reasons why the executable file might be
-covered by the GNU Public License.
diff --git a/options/license/GNOME-examples-exception b/options/license/GNOME-examples-exception
deleted file mode 100644
index 0f0cd53b50..0000000000
--- a/options/license/GNOME-examples-exception
+++ /dev/null
@@ -1 +0,0 @@
-As a special exception, the copyright holders give you permission to copy, modify, and distribute the example code contained in this document under the terms of your choosing, without restriction.
diff --git a/options/license/GNU-compiler-exception b/options/license/GNU-compiler-exception
deleted file mode 100644
index 684833ffb4..0000000000
--- a/options/license/GNU-compiler-exception
+++ /dev/null
@@ -1,6 +0,0 @@
-As a special exception, if you link this library with files
-compiled with a GNU compiler to produce an executable, this
-does not cause the resulting executable to be covered by
-the GNU General Public License. This exception does not
-however invalidate any other reasons why the executable
-file might be covered by the GNU General Public License.
diff --git a/options/license/GPL-1.0-only b/options/license/GPL-1.0-only
deleted file mode 100644
index b3a222308b..0000000000
--- a/options/license/GPL-1.0-only
+++ /dev/null
@@ -1,100 +0,0 @@
-GNU GENERAL PUBLIC LICENSE
-Version 1, February 1989
-
-Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-Preamble
-
-The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
-
-To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
-
-For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights.
-
-We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
-
-Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
-
-The precise terms and conditions for copying, distribution and modification follow.
-
-GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you".
-
-1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy.
-
-2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following:
-
- a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and
-
- b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option).
-
- c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License.
-
- d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
-
-Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms.
-
-3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following:
-
- a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or,
-
- b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or,
-
- c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.)
-
-Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system.
-
-4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance.
-
-5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions.
-
-6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein.
-
-7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation.
-
-8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
-
-NO WARRANTY
-
-9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-END OF TERMS AND CONDITIONS
-
-Appendix: How to Apply These Terms to Your New Programs
-
-If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
-
-To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.> Copyright (C) 19yy <name of author>
-
- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice
-
-That's all there is to it!
diff --git a/options/license/GPL-1.0-or-later b/options/license/GPL-1.0-or-later
deleted file mode 100644
index b3a222308b..0000000000
--- a/options/license/GPL-1.0-or-later
+++ /dev/null
@@ -1,100 +0,0 @@
-GNU GENERAL PUBLIC LICENSE
-Version 1, February 1989
-
-Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-Preamble
-
-The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
-
-To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
-
-For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights.
-
-We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
-
-Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
-
-The precise terms and conditions for copying, distribution and modification follow.
-
-GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you".
-
-1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy.
-
-2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following:
-
- a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and
-
- b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option).
-
- c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License.
-
- d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
-
-Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms.
-
-3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following:
-
- a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or,
-
- b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or,
-
- c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.)
-
-Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system.
-
-4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance.
-
-5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions.
-
-6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein.
-
-7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation.
-
-8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
-
-NO WARRANTY
-
-9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-END OF TERMS AND CONDITIONS
-
-Appendix: How to Apply These Terms to Your New Programs
-
-If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
-
-To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.> Copyright (C) 19yy <name of author>
-
- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice
-
-That's all there is to it!
diff --git a/options/license/GPL-2.0-only b/options/license/GPL-2.0
index 17cb286430..17cb286430 100644
--- a/options/license/GPL-2.0-only
+++ b/options/license/GPL-2.0
diff --git a/options/license/GPL-2.0-or-later b/options/license/GPL-2.0-or-later
deleted file mode 100644
index 17cb286430..0000000000
--- a/options/license/GPL-2.0-or-later
+++ /dev/null
@@ -1,117 +0,0 @@
-GNU GENERAL PUBLIC LICENSE
-Version 2, June 1991
-
-Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-Preamble
-
-The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
-
-To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
-
-For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
-
-We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
-
-Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
-
-Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
-
-The precise terms and conditions for copying, distribution and modification follow.
-
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
-
-1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
-
-2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
-
-3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
-
-If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
-
-4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
-
-6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
-
-7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
-
-This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
-
-8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
-
-9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
-
-10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
-
-NO WARRANTY
-
-11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-END OF TERMS AND CONDITIONS
-
-How to Apply These Terms to Your New Programs
-
-If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
-
-To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
-
- one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author
-
- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice
diff --git a/options/license/GPL-3.0-only b/options/license/GPL-3.0
index f6cdd22a6c..f6cdd22a6c 100644
--- a/options/license/GPL-3.0-only
+++ b/options/license/GPL-3.0
diff --git a/options/license/GPL-3.0-389-ds-base-exception b/options/license/GPL-3.0-389-ds-base-exception
deleted file mode 100644
index 52be470c10..0000000000
--- a/options/license/GPL-3.0-389-ds-base-exception
+++ /dev/null
@@ -1,10 +0,0 @@
-Additional permission under GPLv3 section 7:
-
-If you modify this Program, or any covered work, by
-linking or combining it with OpenSSL, or a modified
-version of OpenSSL licensed under the OpenSSL license
-(https://www.openssl.org/source/license.html), the licensors of this
-Program grant you additional permission to convey the resulting work.
-Corresponding Source for a non-source form of such a combination
-shall include the source code for the parts that are licensed
-under the OpenSSL license as well as that of the covered work.
diff --git a/options/license/GPL-3.0-interface-exception b/options/license/GPL-3.0-interface-exception
deleted file mode 100644
index a86a7fffd7..0000000000
--- a/options/license/GPL-3.0-interface-exception
+++ /dev/null
@@ -1,7 +0,0 @@
-Linking [name of library] statically or dynamically with other modules is making a combined work based on [name of library]. Thus, the terms and conditions of the GNU General Public License cover the whole combination.
-
-As a special exception, the copyright holders of [name of library] give you permission to combine [name of library] program with free software programs or libraries that are released under the GNU LGPL and with independent modules that communicate with [name of library] solely through the [name of library's interface] interface. You may copy and distribute such a system following the terms of the GNU GPL for [name of library] and the licenses of the other code concerned, provided that you include the source code of that other code when and as the GNU GPL requires distribution of source code and provided that you do not modify the [name of library's interface] interface.
-
-Note that people who make modified versions of [name of library] are not obligated to grant this special exception for their modified versions; it is their choice whether to do so. The GNU General Public License gives permission to release a modified version without this exception; this exception also makes it possible to release a modified version which carries forward this exception. If you modify the [name of library's interface] interface, this exception does not apply to your modified version of [name of library], and you must remove this exception when you distribute your modified version.
-
-This exception is an additional permission under section 7 of the GNU General Public License, version 3 ("GPLv3")
diff --git a/options/license/GPL-3.0-linking-exception b/options/license/GPL-3.0-linking-exception
deleted file mode 100644
index 56096c0706..0000000000
--- a/options/license/GPL-3.0-linking-exception
+++ /dev/null
@@ -1,3 +0,0 @@
-Additional permission under GNU GPL version 3 section 7
-
-If you modify this Program, or any covered work, by linking or combining it with [name of library] (or a modified version of that library), containing parts covered by the terms of [name of library's license], the licensors of this Program grant you additional permission to convey the resulting work.
diff --git a/options/license/GPL-3.0-linking-source-exception b/options/license/GPL-3.0-linking-source-exception
deleted file mode 100644
index 58a1d1b3a4..0000000000
--- a/options/license/GPL-3.0-linking-source-exception
+++ /dev/null
@@ -1,3 +0,0 @@
-Additional permission under GNU GPL version 3 section 7
-
-If you modify this Program, or any covered work, by linking or combining it with [name of library] (or a modified version of that library), containing parts covered by the terms of [name of library's license], the licensors of this Program grant you additional permission to convey the resulting work. Corresponding Source for a non-source form of such a combination shall include the source code for the parts of [name of library] used as well as that of the covered work.
diff --git a/options/license/GPL-3.0-or-later b/options/license/GPL-3.0-or-later
deleted file mode 100644
index f6cdd22a6c..0000000000
--- a/options/license/GPL-3.0-or-later
+++ /dev/null
@@ -1,232 +0,0 @@
-GNU GENERAL PUBLIC LICENSE
-Version 3, 29 June 2007
-
-Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/>
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-Preamble
-
-The GNU General Public License is a free, copyleft license for software and other kinds of works.
-
-The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.
-
-To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.
-
-For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
-
-Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.
-
-For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.
-
-Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.
-
-Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.
-
-The precise terms and conditions for copying, distribution and modification follow.
-
-TERMS AND CONDITIONS
-
-0. Definitions.
-
-“This License†refers to version 3 of the GNU General Public License.
-
-“Copyright†also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
-
-“The Program†refers to any copyrightable work licensed under this License. Each licensee is addressed as “youâ€. “Licensees†and “recipients†may be individuals or organizations.
-
-To “modify†a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version†of the earlier work or a work “based on†the earlier work.
-
-A “covered work†means either the unmodified Program or a work based on the Program.
-
-To “propagate†a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.
-
-To “convey†a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.
-
-An interactive user interface displays “Appropriate Legal Notices†to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
-
-1. Source Code.
-The “source code†for a work means the preferred form of the work for making modifications to it. “Object code†means any non-source form of a work.
-
-A “Standard Interface†means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.
-
-The “System Libraries†of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Componentâ€, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.
-
-The “Corresponding Source†for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.
-
-The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
-
-The Corresponding Source for a work in source code form is that same work.
-
-2. Basic Permissions.
-All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
-
-You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
-
-Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
-
-3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.
-
-When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.
-
-4. Conveying Verbatim Copies.
-You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.
-
-You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
-
-5. Conveying Modified Source Versions.
-You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all noticesâ€.
-
- c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
-
-A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate†if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.
-
-6. Conveying Non-Source Forms.
-You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
-
- d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
-
-A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.
-
-A “User Product†is either (1) a “consumer productâ€, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used†refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.
-
-“Installation Information†for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.
-
-If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).
-
-The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.
-
-Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.
-
-7. Additional Terms.
-“Additional permissions†are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.
-
-When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.
-
-Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
-
-All other non-permissive additional terms are considered “further restrictions†within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.
-
-If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
-
-Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.
-
-8. Termination.
-You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).
-
-However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
-
-Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.
-
-9. Acceptance Not Required for Having Copies.
-You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.
-
-10. Automatic Licensing of Downstream Recipients.
-Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.
-
-An “entity transaction†is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.
-
-You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
-
-11. Patents.
-A “contributor†is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor versionâ€.
-
-A contributor's “essential patent claims†are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control†includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
-
-Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.
-
-In the following three paragraphs, a “patent license†is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant†such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.
-
-If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying†means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.
-
-If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.
-
-A patent license is “discriminatory†if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
-
-Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.
-
-12. No Surrender of Others' Freedom.
-If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
-
-13. Use with the GNU Affero General Public License.
-Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.
-
-14. Revised Versions of this License.
-The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version†applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.
-
-If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.
-
-Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.
-
-15. Disclaimer of Warranty.
-THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS†WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-16. Limitation of Liability.
-IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-17. Interpretation of Sections 15 and 16.
-If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
-
-END OF TERMS AND CONDITIONS
-
-How to Apply These Terms to Your New Programs
-
-If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
-
-To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright†line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
-
- <program> Copyright (C) <year> <name of author>
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about boxâ€.
-
-You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer†for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <https://www.gnu.org/licenses/>.
-
-The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <https://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/options/license/GPL-CC-1.0 b/options/license/GPL-CC-1.0
deleted file mode 100644
index a687e0ddb6..0000000000
--- a/options/license/GPL-CC-1.0
+++ /dev/null
@@ -1,46 +0,0 @@
-GPL Cooperation Commitment
-Version 1.0
-
-Before filing or continuing to prosecute any legal proceeding or claim
-(other than a Defensive Action) arising from termination of a Covered
-License, we commit to extend to the person or entity ('you') accused
-of violating the Covered License the following provisions regarding
-cure and reinstatement, taken from GPL version 3. As used here, the
-term 'this License' refers to the specific Covered License being
-enforced.
-
- However, if you cease all violation of this License, then your
- license from a particular copyright holder is reinstated (a)
- provisionally, unless and until the copyright holder explicitly
- and finally terminates your license, and (b) permanently, if the
- copyright holder fails to notify you of the violation by some
- reasonable means prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
- reinstated permanently if the copyright holder notifies you of the
- violation by some reasonable means, this is the first time you
- have received notice of violation of this License (for any work)
- from that copyright holder, and you cure the violation prior to 30
- days after your receipt of the notice.
-
-We intend this Commitment to be irrevocable, and binding and
-enforceable against us and assignees of or successors to our
-copyrights.
-
-Definitions
-
-'Covered License' means the GNU General Public License, version 2
-(GPLv2), the GNU Lesser General Public License, version 2.1
-(LGPLv2.1), or the GNU Library General Public License, version 2
-(LGPLv2), all as published by the Free Software Foundation.
-
-'Defensive Action' means a legal proceeding or claim that We bring
-against you in response to a prior proceeding or claim initiated by
-you or your affiliate.
-
-'We' means each contributor to this repository as of the date of
-inclusion of this file, including subsidiaries of a corporate
-contributor.
-
-This work is available under a Creative Commons Attribution-ShareAlike
-4.0 International license (https://creativecommons.org/licenses/by-sa/4.0/).
diff --git a/options/license/GStreamer-exception-2005 b/options/license/GStreamer-exception-2005
deleted file mode 100644
index 95ff750da3..0000000000
--- a/options/license/GStreamer-exception-2005
+++ /dev/null
@@ -1 +0,0 @@
-The Totem project hereby grant permission for non-gpl compatible GStreamer plugins to be used and distributed together with GStreamer and Totem. This permission are above and beyond the permissions granted by the GPL license Totem is covered by.
diff --git a/options/license/GStreamer-exception-2008 b/options/license/GStreamer-exception-2008
deleted file mode 100644
index 28927e533e..0000000000
--- a/options/license/GStreamer-exception-2008
+++ /dev/null
@@ -1 +0,0 @@
-This project hereby grants permission for non-GPL compatible GStreamer plugins to be used and distributed together with GStreamer and this project. This permission is above and beyond the permissions granted by the GPL license by which this project is covered. If you modify this code, you may extend this exception to your version of the code, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version.
diff --git a/options/license/Giftware b/options/license/Giftware
deleted file mode 100644
index c22c5a6fb9..0000000000
--- a/options/license/Giftware
+++ /dev/null
@@ -1,9 +0,0 @@
-Allegro 4 (the giftware license)
-
-Allegro is gift-ware. It was created by a number of people working in cooperation, and is given to you freely as a gift. You may use, modify, redistribute, and generally hack it about in any way you like, and you do not have to give us anything in return.
-
-However, if you like this product you are encouraged to thank us by making a return gift to the Allegro community. This could be by writing an add-on package, providing a useful bug report, making an improvement to the library, or perhaps just releasing the sources of your program so that other people can learn from them. If you redistribute parts of this code or make a game using it, it would be nice if you mentioned Allegro somewhere in the credits, but you are not required to do this. We trust you not to abuse our generosity.
-
-By Shawn Hargreaves, 18 October 1998.
-
-DISCLAIMER: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/options/license/Glide b/options/license/Glide
deleted file mode 100644
index 23eae7c417..0000000000
--- a/options/license/Glide
+++ /dev/null
@@ -1,95 +0,0 @@
-3DFX GLIDE Source Code General Public License
-
-1. PREAMBLE
-
-This license is for software that provides a 3D graphics application program interface (API).The license is intended to offer terms similar to some standard General Public Licenses designed to foster open standards and unrestricted accessibility to source code. Some of these licenses require that, as a condition of the license of the software, any derivative works (that is, new software which is a work containing the original program or a portion of it) must be available for general use, without restriction other than for a minor transfer fee, and that the source code for such derivative works must likewise be made available. The only restriction is that such derivative works must be subject to the same General Public License terms as the original work.
-
-This 3dfx GLIDE Source Code General Public License differs from the standard licenses of this type in that it does not require the entire derivative work to be made available under the terms of this license nor is the recipient required to make available the source code for the entire derivative work. Rather, the license is limited to only the identifiable portion of the derivative work that is derived from the licensed software. The precise terms and conditions for copying, distribution and modification follow.
-
-2. DEFINITIONS
-
- 2.1 This License applies to any program (or other "work") which contains a notice placed by the copyright holder saying it may be distributed under the terms of this 3dfx GLIDE Source Code General Public License.
-
- 2.2 The term "Program" as used in this Agreement refers to 3DFX's GLIDE source code and object code and any Derivative Work.
-
- 2.3 "Derivative Work" means, for the purpose of the License, that portion of any work that contains the Program or the identifiable portion of a work that is derived from the Program, either verbatim or with modifications and/or translated into another language, and that performs 3D graphics API operations. It does not include any other portions of a work.
-
- 2.4 "Modifications of the Program" means any work, which includes a Derivative Work, and includes the whole of such work.
-
- 2.5 "License" means this 3dfx GLIDE Source Code General Public License.
-
- 2.6 The "Source Code" for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, any associated interface definition files, and the scripts used to control compilation and installation of the executable work.
-
- 2.7 "3dfx" means 3dfx Interactive, Inc.
-
-3. LICENSED ACTIVITIES
-
- 3.1 COPYING - You may copy and distribute verbatim copies of the Program's Source Code as you receive it, in any medium, subject to the provision of section 3.3 and provided also that:
-
- (a) you conspicuously and appropriately publish on each copy an appropriate copyright notice (3dfx Interactive, Inc. 1999), a notice that recipients who wish to copy, distribute or modify the Program can only do so subject to this License, and a disclaimer of warranty as set forth in section 5;
-
- (b) keep intact all the notices that refer to this License and to the absence of any warranty; and
-
- (c) do not make any use of the GLIDE trademark without the prior written permission of 3dfx, and
-
- (d) give all recipients of the Program a copy of this License along with the Program or instructions on how to easily receive a copy of this License.
-
- 3.2 MODIFICATION OF THE PROGRAM/DERIVATIVE WORKS - You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications subject to the provisions of section 3.3 and provided that you also meet all of the following conditions:
-
- (a) you conspicuously and appropriately publish on each copy of a Derivative Work an appropriate copyright notice, a notice that recipients who wish to copy, distribute or modify the Derivative Work can only do so subject to this License, and a disclaimer of warranty as set forth in section 5;
-
- (b) keep intact all the notices that refer to this License and to the absence of any warranty; and (c) give all recipients of the Derivative Work a copy of this License along with the Derivative Work or instructions on how to easily receive a copy of this License.
-
- (d) You must cause the modified files of the Derivative Work to carry prominent notices stating that you changed the files and the date of any change.
-
- (e) You must cause any Derivative Work that you distribute or publish to be licensed at no charge to all third parties under the terms of this License.
-
- (f) You do not make any use of the GLIDE trademark without the prior written permission of 3dfx.
-
- (g) If the Derivative Work normally reads commands interactively when run, you must cause it, when started running for such interactive use, to print or display an announcement as follows:
-
- "COPYRIGHT 3DFX INTERACTIVE, INC. 1999, ALL RIGHTS RESERVED THIS SOFTWARE IS FREE AND PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED. THERE IS NO RIGHT TO USE THE GLIDE TRADEMARK WITHOUT PRIOR WRITTEN PERMISSION OF 3DFX INTERACTIVE, INC. SEE THE 3DFX GLIDE GENERAL PUBLIC LICENSE FOR A FULL TEXT OF THE DISTRIBUTION AND NON-WARRANTY PROVISIONS (REQUEST COPY FROM INFO@3DFX.COM)."
-
- (h) The requirements of this section 3.2 do not apply to the modified work as a whole but only to the Derivative Work. It is not the intent of this License to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of Derivative Works.
-
- 3.3 DISTRIBUTION
-
- (a) All copies of the Program or Derivative Works which are distributed must include in the file headers the following language verbatim:
-
- "THIS SOFTWARE IS SUBJECT TO COPYRIGHT PROTECTION AND IS OFFERED ONLY PURSUANT TO THE 3DFX GLIDE GENERAL PUBLIC LICENSE. THERE IS NO RIGHT TO USE THE GLIDE TRADEMARK WITHOUT PRIOR WRITTEN PERMISSION OF 3DFX INTERACTIVE, INC. A COPY OF THIS LICENSE MAY BE OBTAINED FROM THE DISTRIBUTOR OR BY CONTACTING 3DFX INTERACTIVE INC (info@3dfx.com). THIS PROGRAM. IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED. SEE THE 3DFX GLIDE GENERAL PUBLIC LICENSE FOR A FULL TEXT OF THE NON-WARRANTY PROVISIONS.
-
- USE, DUPLICATION OR DISCLOSURE BY THE GOVERNMENT IS SUBJECT TO RESTRICTIONS AS SET FORTH IN SUBDIVISION (C)(1)(II) OF THE RIGHTS IN TECHNICAL DATA AND COMPUTER SOFTWARE CLAUSE AT DFARS 252.227-7013, AND/OR IN SIMILAR OR SUCCESSOR CLAUSES IN THE FAR, DOD OR NASA FAR SUPPLEMENT. UNPUBLISHED RIGHTS RESERVED UNDER THE COPYRIGHT LAWS OF THE UNITED STATES.
-
- COPYRIGHT 3DFX INTERACTIVE, INC. 1999, ALL RIGHTS RESERVED"
-
- (b) You may distribute the Program or a Derivative Work in object code or executable form under the terms of Sections 3.1 and 3.2 provided that you also do one of the following:
-
- (1) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 3.1 and 3.2; or,
-
- (2) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 3.1 and 3.2 on a medium customarily used for software interchange; or,
-
- (3) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection 3.3(b)(2) above.)
-
- (c) The source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable code.
-
- (d) If distribution of executable code or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
-
- (e) Each time you redistribute the Program or any Derivative Work, the recipient automatically receives a license from 3dfx and successor licensors to copy, distribute or modify the Program and Derivative Works subject to the terms and conditions of the License. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
-
- (f) You may not make any use of the GLIDE trademark without the prior written permission of 3dfx.
-
- (g) You may not copy, modify, sublicense, or distribute the Program or any Derivative Works except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program or any Derivative Works is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-4. MISCELLANEOUS
-
- 4.1 Acceptance of this License is voluntary. By using, modifying or distributing the Program or any Derivative Work, you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. Nothing else grants you permission to modify or distribute the Program or Derivative Works and doing so without acceptance of this License is in violation of the U.S. and international copyright laws.
-
- 4.2 If the distribution and/or use of the Program or Derivative Works is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
-
- 4.3 This License is to be construed according to the laws of the State of California and you consent to personal jurisdiction in the State of California in the event it is necessary to enforce the provisions of this License.
-
-5. NO WARRANTIES
-
- 5.1 TO THE EXTENT PERMITTED BY APPLICABLE LAW, THERE IS NO WARRANTY FOR THE PROGRAM. OR DERIVATIVE WORKS THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM AND ANY DERIVATIVE WORKS"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM AND ANY DERIVATIVE WORK IS WITH YOU. SHOULD THE PROGRAM OR ANY DERIVATIVE WORK PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 5.2 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL 3DFX INTERACTIVE, INC., OR ANY OTHER COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM OR DERIVATIVE WORKS AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM OR DERIVATIVE WORKS (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM OR DERIVATIVE WORKS TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/options/license/Glulxe b/options/license/Glulxe
deleted file mode 100644
index 8bbb720e65..0000000000
--- a/options/license/Glulxe
+++ /dev/null
@@ -1,3 +0,0 @@
-The source code in this package is copyright 1999-2010 by Andrew Plotkin.
-
-You may copy and distribute it freely, by any means and under any conditions, as long as the code and documentation is not changed. You may also incorporate this code into your own program and distribute that, or modify this code and use and distribute the modified version, as long as you retain a notice in your program or documentation which mentions my name and the URL shown above.
diff --git a/options/license/Gmsh-exception b/options/license/Gmsh-exception
deleted file mode 100644
index 6d28f704e4..0000000000
--- a/options/license/Gmsh-exception
+++ /dev/null
@@ -1,16 +0,0 @@
-The copyright holders of Gmsh give you permission to combine Gmsh
- with code included in the standard release of Netgen (from Joachim
- Sch"oberl), METIS (from George Karypis at the University of
- Minnesota), OpenCASCADE (from Open CASCADE S.A.S) and ParaView
- (from Kitware, Inc.) under their respective licenses. You may copy
- and distribute such a system following the terms of the GNU GPL for
- Gmsh and the licenses of the other code concerned, provided that
- you include the source code of that other code when and as the GNU
- GPL requires distribution of source code.
-
- Note that people who make modified versions of Gmsh are not
- obligated to grant this special exception for their modified
- versions; it is their choice whether to do so. The GNU General
- Public License gives permission to release a modified version
- without this exception; this exception also makes it possible to
- release a modified version which carries forward this exception.
diff --git a/options/license/Graphics-Gems b/options/license/Graphics-Gems
deleted file mode 100644
index ec28c46563..0000000000
--- a/options/license/Graphics-Gems
+++ /dev/null
@@ -1,5 +0,0 @@
-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/Gutmann b/options/license/Gutmann
deleted file mode 100644
index c33f4ee3a2..0000000000
--- a/options/license/Gutmann
+++ /dev/null
@@ -1,2 +0,0 @@
-You can use this code in whatever way you want, as long as you don't try
-to claim you wrote it.
diff --git a/options/license/HIDAPI b/options/license/HIDAPI
deleted file mode 100644
index e0b5d70c04..0000000000
--- a/options/license/HIDAPI
+++ /dev/null
@@ -1,2 +0,0 @@
-This software may be used by anyone for any reason so long
-as the copyright notice in the source files remains intact.
diff --git a/options/license/HP-1986 b/options/license/HP-1986
deleted file mode 100644
index 35844cb4d8..0000000000
--- a/options/license/HP-1986
+++ /dev/null
@@ -1,10 +0,0 @@
-(c) Copyright 1986 HEWLETT-PACKARD COMPANY
-
-To anyone who acknowledges that this file is provided "AS IS"
-without any express or implied warranty: permission to use, copy,
-modify, and distribute this file for any purpose is hereby granted
-without fee, provided that the above copyright notice and this notice
-appears in all copies, and that the name of Hewlett-Packard Company
-not be used in advertising or publicity pertaining to distribution
-of the software without specific, written prior permission. Hewlett-Packard
-Company makes no representations about the suitability of this software for any purpose.
diff --git a/options/license/HP-1989 b/options/license/HP-1989
deleted file mode 100644
index 7422055d95..0000000000
--- a/options/license/HP-1989
+++ /dev/null
@@ -1,16 +0,0 @@
-Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
-Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca.
-Digital Equipment Corporation, Maynard, Mass.
-Copyright (c) 1998 Microsoft.
-To anyone who acknowledges that this file is provided "AS IS"
-without any express or implied warranty: permission to use, copy,
-modify, and distribute this file for any purpose is hereby
-granted without fee, provided that the above copyright notices and
-this notice appears in all source code copies, and that none of
-the names of Open Software Foundation, Inc., Hewlett-Packard
-Company, Microsoft, or Digital Equipment Corporation be used in
-advertising or publicity pertaining to distribution of the software
-without specific, written prior permission. Neither Open Software
-Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital
-Equipment Corporation makes any representations about the
-suitability of this software for any purpose.
diff --git a/options/license/HPND b/options/license/HPND
deleted file mode 100644
index ff9ae1b713..0000000000
--- a/options/license/HPND
+++ /dev/null
@@ -1,7 +0,0 @@
-Historical Permission Notice and Disclaimer
-
-Copyright <year> <copyright holder>
-
-Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of <copyright holder> or <related entities> not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. <copyright holder> makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.
-
-<copyright holder> DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/options/license/HPND-DEC b/options/license/HPND-DEC
deleted file mode 100644
index d014f1fabc..0000000000
--- a/options/license/HPND-DEC
+++ /dev/null
@@ -1,22 +0,0 @@
-COPYRIGHT 1990
-DIGITAL EQUIPMENT CORPORATION
-MAYNARD, MASSACHUSETTS
-ALL RIGHTS RESERVED.
-
-THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
-DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE
-FOR ANY PURPOSE. IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED
-WARRANTY.
-
-IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT
-RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN
-ADDITION TO THAT SET FORTH ABOVE.
-
-Permission to use, copy, modify, and distribute this software and
-its documentation for any purpose and without fee is hereby granted,
-provided that the above copyright notice appear in all copies
-and that both that copyright notice and this permission notice appear in supporting
-documentation,
-and that the name of Digital Equipment Corporation not be
-used in advertising or publicity pertaining to distribution of the
-software without specific, written prior permission.
diff --git a/options/license/HPND-Fenneberg-Livingston b/options/license/HPND-Fenneberg-Livingston
deleted file mode 100644
index aaf524f3aa..0000000000
--- a/options/license/HPND-Fenneberg-Livingston
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright (C) 1995,1996,1997,1998 Lars Fenneberg <lf@elemental.net>
-
-Permission to use, copy, modify, and distribute this software for any
-purpose and without fee is hereby granted, provided that this copyright and
-permission notice appear on all copies and supporting documentation, the
-name of Lars Fenneberg not be used in advertising or publicity pertaining to
-distribution of the program without specific prior permission, and notice be
-given in supporting documentation that copying and distribution is by
-permission of Lars Fenneberg.
-
-Lars Fenneberg makes no representations about the suitability of this
-software for any purpose. It is provided "as is" without express or implied
-warranty.
diff --git a/options/license/HPND-INRIA-IMAG b/options/license/HPND-INRIA-IMAG
deleted file mode 100644
index 87d09d92cb..0000000000
--- a/options/license/HPND-INRIA-IMAG
+++ /dev/null
@@ -1,9 +0,0 @@
-This software is available with usual "research" terms with
-the aim of retain credits of the software. Permission to use,
-copy, modify and distribute this software for any purpose and
-without fee is hereby granted, provided that the above copyright
-notice and this permission notice appear in all copies, and
-the name of INRIA, IMAG, or any contributor not be used in
-advertising or publicity pertaining to this material without
-the prior explicit permission. The software is provided "as
-is" without any warranties, support or liabilities of any kind.
diff --git a/options/license/HPND-Intel b/options/license/HPND-Intel
deleted file mode 100644
index 98f0ceb4fd..0000000000
--- a/options/license/HPND-Intel
+++ /dev/null
@@ -1,25 +0,0 @@
-Copyright (c) 1993 Intel Corporation
-
-Intel hereby grants you permission to copy, modify, and distribute this
-software and its documentation. Intel grants this permission provided
-that the above copyright notice appears in all copies and that both the
-copyright notice and this permission notice appear in supporting
-documentation. In addition, Intel grants this permission provided that
-you prominently mark as "not part of the original" any modifications
-made to this software or documentation, and that the name of Intel
-Corporation not be used in advertising or publicity pertaining to
-distribution of the software or the documentation without specific,
-written prior permission.
-
-Intel Corporation provides this AS IS, WITHOUT ANY WARRANTY, EXPRESS OR
-IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY
-OR FITNESS FOR A PARTICULAR PURPOSE. Intel makes no guarantee or
-representations regarding the use of, or the results of the use of,
-the software and documentation in terms of correctness, accuracy,
-reliability, currentness, or otherwise; and you rely on the software,
-documentation and results solely at your own risk.
-
-IN NO EVENT SHALL INTEL BE LIABLE FOR ANY LOSS OF USE, LOSS OF BUSINESS,
-LOSS OF PROFITS, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES
-OF ANY KIND. IN NO EVENT SHALL INTEL'S TOTAL LIABILITY EXCEED THE SUM
-PAID TO INTEL FOR THE PRODUCT LICENSED HEREUNDER.
diff --git a/options/license/HPND-Kevlin-Henney b/options/license/HPND-Kevlin-Henney
deleted file mode 100644
index ddf9bd6dca..0000000000
--- a/options/license/HPND-Kevlin-Henney
+++ /dev/null
@@ -1,10 +0,0 @@
-Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved.
-
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose is hereby granted without fee, provided
-that this copyright and permissions notice appear in all copies and
-derivatives.
-
-This software is supplied "as is" without express or implied warranty.
-
-But that said, if there are any problems please get in touch.
diff --git a/options/license/HPND-MIT-disclaimer b/options/license/HPND-MIT-disclaimer
deleted file mode 100644
index bf035915cf..0000000000
--- a/options/license/HPND-MIT-disclaimer
+++ /dev/null
@@ -1,18 +0,0 @@
- LICENSE
- =======
-
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose and without fee is hereby granted, provided
-that the original copyright notices appear in all copies and that both
-copyright notice and this permission notice appear in supporting
-documentation, and that the name of the author not be used in advertising
-or publicity pertaining to distribution of the software without specific
-prior written permission.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
diff --git a/options/license/HPND-Markus-Kuhn b/options/license/HPND-Markus-Kuhn
deleted file mode 100644
index ca41db1618..0000000000
--- a/options/license/HPND-Markus-Kuhn
+++ /dev/null
@@ -1,3 +0,0 @@
-Permission to use, copy, modify, and distribute this software
-for any purpose and without fee is hereby granted. The author
-disclaims all warranties with regard to this software.
diff --git a/options/license/HPND-Netrek b/options/license/HPND-Netrek
deleted file mode 100644
index 5c3cb650f4..0000000000
--- a/options/license/HPND-Netrek
+++ /dev/null
@@ -1,10 +0,0 @@
-Copyright (C) 1995 S. M. Patel (smpatel@wam.umd.edu)
-
-Permission to use, copy, modify, and distribute this
-software and its documentation for any purpose and without
-fee is hereby granted, provided that the above copyright
-notice appear in all copies and that both that copyright
-notice and this permission notice appear in supporting
-documentation. No representations are made about the
-suitability of this software for any purpose. It is
-provided "as is" without express or implied warranty.
diff --git a/options/license/HPND-Pbmplus b/options/license/HPND-Pbmplus
deleted file mode 100644
index 5627d2646f..0000000000
--- a/options/license/HPND-Pbmplus
+++ /dev/null
@@ -1,8 +0,0 @@
-Copyright (C) 1991 by Jef Poskanzer.
-
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose and without fee is hereby granted, provided
-that the above copyright notice appear in all copies and that both that
-copyright notice and this permission notice appear in supporting
-documentation. This software is provided "as is" without express or
-implied warranty.
diff --git a/options/license/HPND-UC b/options/license/HPND-UC
deleted file mode 100644
index adfbd23862..0000000000
--- a/options/license/HPND-UC
+++ /dev/null
@@ -1,8 +0,0 @@
-Copyright 1989 Regents of the University of California
-
-Permission to use,
-copy, modify, and distribute this software and its documentation for any
-purpose and without fee is hereby granted, provided that the above
-copyright notice appear in all copies. The University of California makes
-no representations about the suitability of this software for any purpose.
-It is provided "as is" without express or implied warranty.
diff --git a/options/license/HPND-UC-export-US b/options/license/HPND-UC-export-US
deleted file mode 100644
index 015556c5f9..0000000000
--- a/options/license/HPND-UC-export-US
+++ /dev/null
@@ -1,10 +0,0 @@
-Copyright (C) 1985, 1990 Regents of the University of California.
-
-Permission to use, copy, modify, and distribute this
-software and its documentation for any purpose and without
-fee is hereby granted, provided that the above copyright
-notice appear in all copies. The University of California
-makes no representations about the suitability of this
-software for any purpose. It is provided "as is" without
-express or implied warranty. Export of this software outside
-of the United States of America may require an export license.
diff --git a/options/license/HPND-doc b/options/license/HPND-doc
deleted file mode 100644
index bd85a2816e..0000000000
--- a/options/license/HPND-doc
+++ /dev/null
@@ -1,8 +0,0 @@
-Copyright <year> <copyright holder>
-
-Permission to use, copy, modify, and distribute this documentation for
-any purpose and without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-<copyright holder> makes no representations about the suitability for
-any purpose of the information in this document. This documentation is
-provided ``as is'' without express or implied warranty.
diff --git a/options/license/HPND-doc-sell b/options/license/HPND-doc-sell
deleted file mode 100644
index ad4aed3e60..0000000000
--- a/options/license/HPND-doc-sell
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright <year> <copyright holder>
-
-Permission to use, copy, modify, distribute, and sell this
-documentation for any purpose is hereby granted without fee,
-provided that the above copyright notice and this permission
-notice appear in all copies. <copyright holder>
-makes no representations about the suitability for any purpose
-of the information in this document. This documentation
-is provided "as is" without express or implied warranty.
diff --git a/options/license/HPND-export-US b/options/license/HPND-export-US
deleted file mode 100644
index b0cd393969..0000000000
--- a/options/license/HPND-export-US
+++ /dev/null
@@ -1,5 +0,0 @@
-Copyright (C) 1990 by the Massachusetts Institute of Technology
-
-Export of this software from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting.
-
-WITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of M.I.T. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. M.I.T. makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.
diff --git a/options/license/HPND-export-US-acknowledgement b/options/license/HPND-export-US-acknowledgement
deleted file mode 100644
index 645df4c9aa..0000000000
--- a/options/license/HPND-export-US-acknowledgement
+++ /dev/null
@@ -1,22 +0,0 @@
-Copyright (C) 1994 by the University of Southern California
-
- EXPORT OF THIS SOFTWARE from the United States of America may
- require a specific license from the United States Government. It
- is the responsibility of any person or organization
- contemplating export to obtain such a license before exporting.
-
-WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute
-this software and its documentation in source and binary forms is
-hereby granted, provided that any documentation or other materials
-related to such distribution or use acknowledge that the software
-was developed by the University of Southern California.
-
-DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The
-University of Southern California MAKES NO REPRESENTATIONS OR
-WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not
-limitation, the University of Southern California MAKES NO
-REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
-PARTICULAR PURPOSE. The University of Southern California shall not
-be held liable for any liability nor for any direct, indirect, or
-consequential damages with respect to any claim by the user or
-distributor of the ksu software.
diff --git a/options/license/HPND-export-US-modify b/options/license/HPND-export-US-modify
deleted file mode 100644
index 3c62651c0f..0000000000
--- a/options/license/HPND-export-US-modify
+++ /dev/null
@@ -1,24 +0,0 @@
-Copyright (C) 1994 CyberSAFE Corporation.
-Copyright 1990,1991,2007,2008 by the Massachusetts
-Institute of Technology.
-All Rights Reserved.
-
-Export of this software from the United States of America may
-require a specific license from the United States Government. It
-is the responsibility of any person or organization
-contemplating export to obtain such a license before exporting.
-
-WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
-distribute this software and its documentation for any purpose and
-without fee is hereby granted, provided that the above copyright
-notice appear in all copies and that both that copyright notice and
-this permission notice appear in supporting documentation, and that
-the name of M.I.T. not be used in advertising or publicity
-pertaining to distribution of the software without specific,
-written prior permission. Furthermore if you modify this software
-you must label your software as modified software and not
-distribute it in such a fashion that it might be confused with the
-original M.I.T. software. Neither M.I.T., the Open Computing
-Security Group, nor CyberSAFE Corporation make any representations
-about the suitability of this software for any purpose. It is
-provided "as is" without express or implied warranty.
diff --git a/options/license/HPND-export2-US b/options/license/HPND-export2-US
deleted file mode 100644
index 1dda23a88c..0000000000
--- a/options/license/HPND-export2-US
+++ /dev/null
@@ -1,21 +0,0 @@
-Copyright 2004-2008 Apple Inc. All Rights Reserved.
-
- Export of this software from the United States of America may
- require a specific license from the United States Government.
- It is the responsibility of any person or organization
- contemplating export to obtain such a license before exporting.
-
-WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
-distribute this software and its documentation for any purpose and
-without fee is hereby granted, provided that the above copyright
-notice appear in all copies and that both that copyright notice and
-this permission notice appear in supporting documentation, and that
-the name of Apple Inc. not be used in advertising or publicity
-pertaining to distribution of the software without specific,
-written prior permission. Apple Inc. makes no representations
-about the suitability of this software for any purpose. It is
-provided "as is" without express or implied warranty.
-
-THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/options/license/HPND-merchantability-variant b/options/license/HPND-merchantability-variant
deleted file mode 100644
index 421b9ff96b..0000000000
--- a/options/license/HPND-merchantability-variant
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright (C) 2004 Christian Groessler <chris@groessler.org>
-
-Permission to use, copy, modify, and distribute this file
-for any purpose is hereby granted without fee, provided that
-the above copyright notice and this notice appears in all
-copies.
-
-This file is distributed WITHOUT ANY WARRANTY; without even the implied
-warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/options/license/HPND-sell-MIT-disclaimer-xserver b/options/license/HPND-sell-MIT-disclaimer-xserver
deleted file mode 100644
index e7bea21d16..0000000000
--- a/options/license/HPND-sell-MIT-disclaimer-xserver
+++ /dev/null
@@ -1,12 +0,0 @@
-Permission to use, copy, modify, distribute, and sell this software and its
-documentation for any purpose is hereby granted without fee, provided that
-this permission notice appear in supporting documentation. This permission
-notice shall be included in all copies or substantial portions of the
-Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
-AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/options/license/HPND-sell-regexpr b/options/license/HPND-sell-regexpr
deleted file mode 100644
index b0cd0fb112..0000000000
--- a/options/license/HPND-sell-regexpr
+++ /dev/null
@@ -1,9 +0,0 @@
-Author: Tatu Ylonen <ylo@ngs.fi>
-
-Copyright (c) 1991 Tatu Ylonen, Espoo, Finland
-
-Permission to use, copy, modify, distribute, and sell this software
-and its documentation for any purpose is hereby granted without
-fee, provided that the above copyright notice appear in all copies.
-This software is provided "as is" without express or implied
-warranty.
diff --git a/options/license/HPND-sell-variant b/options/license/HPND-sell-variant
deleted file mode 100644
index cac53b2373..0000000000
--- a/options/license/HPND-sell-variant
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright 1993 by OpenVision Technologies, Inc.
-
-Permission to use, copy, modify, distribute, and sell this software
-and its documentation for any purpose is hereby granted without fee,
-provided that the above copyright notice appears in all copies and
-that both that copyright notice and this permission notice appear in
-supporting documentation, and that the name of OpenVision not be used
-in advertising or publicity pertaining to distribution of the software
-without specific, written prior permission. OpenVision makes no
-representations about the suitability of this software for any
-purpose. It is provided "as is" without express or implied warranty.
-
-OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
-INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
-EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
-CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
-USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-PERFORMANCE OF THIS SOFTWARE.
diff --git a/options/license/HPND-sell-variant-MIT-disclaimer b/options/license/HPND-sell-variant-MIT-disclaimer
deleted file mode 100644
index d4464e0c35..0000000000
--- a/options/license/HPND-sell-variant-MIT-disclaimer
+++ /dev/null
@@ -1,20 +0,0 @@
-by Jim Knoble <jmknoble@pobox.com>
- Copyright (C) 1999,2000,2001 Jim Knoble
-
- Permission to use, copy, modify, distribute, and sell this software
- and its documentation for any purpose is hereby granted without fee,
- provided that the above copyright notice appear in all copies and
- that both that copyright notice and this permission notice appear in
- supporting documentation.
-
-+------------+
-| Disclaimer |
-+------------+
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- express or implied, including but not limited to the warranties of
- merchantability, fitness for a particular purpose and
- noninfringement. In no event shall the author(s) be liable for any
- claim, damages or other liability, whether in an action of contract,
- tort or otherwise, arising from, out of or in connection with the
- software or the use or other dealings in the software.
diff --git a/options/license/HPND-sell-variant-MIT-disclaimer-rev b/options/license/HPND-sell-variant-MIT-disclaimer-rev
deleted file mode 100644
index f68aff5c99..0000000000
--- a/options/license/HPND-sell-variant-MIT-disclaimer-rev
+++ /dev/null
@@ -1,15 +0,0 @@
-Disclaimer:
-
-The software is provided "as is", without warranty of any kind,
-express or implied, including but not limited to the warranties
-of merchantability, fitness for a particular purpose and
-noninfringement. In no event shall the author(s) be liable for
-any claim, damages or other liability, whether in an action of
-contract, tort or otherwise, arising from, out of or in connection
-with the software or the use or other dealings in the software.
-
-Permission to use, copy, modify, distribute, and sell this
-software and its documentation for any purpose is hereby
-granted without fee, provided that the above copyright notice
-appear in all copies and that both that copyright notice and
-this permission notice appear in supporting documentation.
diff --git a/options/license/HTMLTIDY b/options/license/HTMLTIDY
deleted file mode 100644
index 098dee8c05..0000000000
--- a/options/license/HTMLTIDY
+++ /dev/null
@@ -1,13 +0,0 @@
-HTML Tidy License
-
-This software and documentation is provided "as is," and the copyright holders and contributing author(s) make no representations or warranties, express or implied, including but not limited to, warranties of merchantability or fitness for any particular purpose or that the use of the software or documentation will not infringe any third party patents, copyrights, trademarks or other rights.
-
-The copyright holders and contributing author(s) will not be held liable for any direct, indirect, special or consequential damages arising out of any use of the software or documentation, even if advised of the possibility of such damage.
-
-Permission is hereby granted to use, copy, modify, and distribute this source code, or portions hereof, documentation and executables, for any purpose, without fee, subject to the following restrictions:
-
- 1. The origin of this source code must not be misrepresented.
- 2. Altered versions must be plainly marked as such and must not be misrepresented as being the original source.
- 3. This Copyright notice may not be removed or altered from any source or altered source distribution.
-
-The copyright holders and contributing author(s) specifically permit, without fee, and encourage the use of this source code as a component for supporting the Hypertext Markup Language in commercial products. If you use this source code in a product, acknowledgement is not required but would be appreciated.
diff --git a/options/license/HaskellReport b/options/license/HaskellReport
deleted file mode 100644
index 563b7056c1..0000000000
--- a/options/license/HaskellReport
+++ /dev/null
@@ -1,6 +0,0 @@
-Code derived from the document "Report on the Programming Language
-Haskell 2010", is distributed under the following license:
-
-Copyright (c) 2010 Simon Marlow
-
-The authors intend this Report to belong to the entire Haskell community, and so we grant permission to copy and distribute it for any purpose, provided that it is reproduced in its entirety, including this Notice. Modified versions of this Report may also be copied and distributed for any purpose, provided that the modified version is clearly presented as such, and that it does not claim to be a definition of the Haskell 2010 Language.
diff --git a/options/license/Hippocratic-2.1 b/options/license/Hippocratic-2.1
deleted file mode 100644
index 0395b52ddf..0000000000
--- a/options/license/Hippocratic-2.1
+++ /dev/null
@@ -1,33 +0,0 @@
-[SOFTWARE NAME] Copyright (YEAR) (COPYRIGHT HOLDER(S)/AUTHOR(S))(“Licensorâ€)
-
-Hippocratic License Version Number: 2.1.
-
-Purpose. The purpose of this License is for the Licensor named above to permit the Licensee (as defined below) broad permission, if consistent with Human Rights Laws and Human Rights Principles (as each is defined below), to use and work with the Software (as defined below) within the full scope of Licensor’s copyright and patent rights, if any, in the Software, while ensuring attribution and protecting the Licensor from liability.
-
-Permission and Conditions. The Licensor grants permission by this license (“Licenseâ€), free of charge, to the extent of Licensor’s rights under applicable copyright and patent law, to any person or entity (the “Licenseeâ€) obtaining a copy of this software and associated documentation files (the “Softwareâ€), to do everything with the Software that would otherwise infringe (i) the Licensor’s copyright in the Software or (ii) any patent claims to the Software that the Licensor can license or becomes able to license, subject to all of the following terms and conditions:
-
-* Acceptance. This License is automatically offered to every person and entity subject to its terms and conditions. Licensee accepts this License and agrees to its terms and conditions by taking any action with the Software that, absent this License, would infringe any intellectual property right held by Licensor.
-
-* Notice. Licensee must ensure that everyone who gets a copy of any part of this Software from Licensee, with or without changes, also receives the License and the above copyright notice (and if included by the Licensor, patent, trademark and attribution notice). Licensee must cause any modified versions of the Software to carry prominent notices stating that Licensee changed the Software. For clarity, although Licensee is free to create modifications of the Software and distribute only the modified portion created by Licensee with additional or different terms, the portion of the Software not modified must be distributed pursuant to this License. If anyone notifies Licensee in writing that Licensee has not complied with this Notice section, Licensee can keep this License by taking all practical steps to comply within 30 days after the notice. If Licensee does not do so, Licensee’s License (and all rights licensed hereunder) shall end immediately.
-
-* Compliance with Human Rights Principles and Human Rights Laws.
-
- 1. Human Rights Principles.
-
- (a) Licensee is advised to consult the articles of the United Nations Universal Declaration of Human Rights and the United Nations Global Compact that define recognized principles of international human rights (the “Human Rights Principlesâ€). Licensee shall use the Software in a manner consistent with Human Rights Principles.
-
- (b) Unless the Licensor and Licensee agree otherwise, any dispute, controversy, or claim arising out of or relating to (i) Section 1(a) regarding Human Rights Principles, including the breach of Section 1(a), termination of this License for breach of the Human Rights Principles, or invalidity of Section 1(a) or (ii) a determination of whether any Law is consistent or in conflict with Human Rights Principles pursuant to Section 2, below, shall be settled by arbitration in accordance with the Hague Rules on Business and Human Rights Arbitration (the “Rulesâ€); provided, however, that Licensee may elect not to participate in such arbitration, in which event this License (and all rights licensed hereunder) shall end immediately. The number of arbitrators shall be one unless the Rules require otherwise.
-
- Unless both the Licensor and Licensee agree to the contrary: (1) All documents and information concerning the arbitration shall be public and may be disclosed by any party; (2) The repository referred to under Article 43 of the Rules shall make available to the public in a timely manner all documents concerning the arbitration which are communicated to it, including all submissions of the parties, all evidence admitted into the record of the proceedings, all transcripts or other recordings of hearings and all orders, decisions and awards of the arbitral tribunal, subject only to the arbitral tribunal's powers to take such measures as may be necessary to safeguard the integrity of the arbitral process pursuant to Articles 18, 33, 41 and 42 of the Rules; and (3) Article 26(6) of the Rules shall not apply.
-
- 2. Human Rights Laws. The Software shall not be used by any person or entity for any systems, activities, or other uses that violate any Human Rights Laws. “Human Rights Laws†means any applicable laws, regulations, or rules (collectively, “Lawsâ€) that protect human, civil, labor, privacy, political, environmental, security, economic, due process, or similar rights; provided, however, that such Laws are consistent and not in conflict with Human Rights Principles (a dispute over the consistency or a conflict between Laws and Human Rights Principles shall be determined by arbitration as stated above). Where the Human Rights Laws of more than one jurisdiction are applicable or in conflict with respect to the use of the Software, the Human Rights Laws that are most protective of the individuals or groups harmed shall apply.
-
- 3. Indemnity. Licensee shall hold harmless and indemnify Licensor (and any other contributor) against all losses, damages, liabilities, deficiencies, claims, actions, judgments, settlements, interest, awards, penalties, fines, costs, or expenses of whatever kind, including Licensor’s reasonable attorneys’ fees, arising out of or relating to Licensee’s use of the Software in violation of Human Rights Laws or Human Rights Principles.
-
-* Failure to Comply. Any failure of Licensee to act according to the terms and conditions of this License is both a breach of the License and an infringement of the intellectual property rights of the Licensor (subject to exceptions under Laws, e.g., fair use). In the event of a breach or infringement, the terms and conditions of this License may be enforced by Licensor under the Laws of any jurisdiction to which Licensee is subject. Licensee also agrees that the Licensor may enforce the terms and conditions of this License against Licensee through specific performance (or similar remedy under Laws) to the extent permitted by Laws. For clarity, except in the event of a breach of this License, infringement, or as otherwise stated in this License, Licensor may not terminate this License with Licensee.
-
-* Enforceability and Interpretation. If any term or provision of this License is determined to be invalid, illegal, or unenforceable by a court of competent jurisdiction, then such invalidity, illegality, or unenforceability shall not affect any other term or provision of this License or invalidate or render unenforceable such term or provision in any other jurisdiction; provided, however, subject to a court modification pursuant to the immediately following sentence, if any term or provision of this License pertaining to Human Rights Laws or Human Rights Principles is deemed invalid, illegal, or unenforceable against Licensee by a court of competent jurisdiction, all rights in the Software granted to Licensee shall be deemed null and void as between Licensor and Licensee. Upon a determination that any term or provision is invalid, illegal, or unenforceable, to the extent permitted by Laws, the court may modify this License to affect the original purpose that the Software be used in compliance with Human Rights Principles and Human Rights Laws as closely as possible. The language in this License shall be interpreted as to its fair meaning and not strictly for or against any party.
-
-* Disclaimer. TO THE FULL EXTENT ALLOWED BY LAW, THIS SOFTWARE COMES “AS IS,†WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED, AND LICENSOR AND ANY OTHER CONTRIBUTOR SHALL NOT BE LIABLE TO ANYONE FOR ANY DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THIS LICENSE, UNDER ANY KIND OF LEGAL CLAIM.
-
-This Hippocratic License is an Ethical Source license (https://ethicalsource.dev) and is offered for use by licensors and licensees at their own risk, on an “AS IS†basis, and with no warranties express or implied, to the maximum extent permitted by Laws.
diff --git a/options/license/IBM-pibs b/options/license/IBM-pibs
deleted file mode 100644
index ee9c7be36d..0000000000
--- a/options/license/IBM-pibs
+++ /dev/null
@@ -1,8 +0,0 @@
-This source code has been made available to you by IBM on an AS-IS basis. Anyone receiving this source is licensed under IBM copyrights to use it in any way he or she deems fit, including copying it, modifying it, compiling it, and redistributing it either with or without modifications. No license under IBM patents or patent applications is to be implied by the copyright license.
-
-Any user of this software should understand that IBM cannot provide technical support for this software and will not be responsible for any consequences resulting from the use of this software.
-
-Any person who transfers this source code or any derivative work must include the IBM copyright notice, this paragraph, and the preceding two paragraphs in the transferred software.
-
-COPYRIGHT I B M CORPORATION 2002
-LICENSED MATERIAL - PROGRAM PROPERTY OF I B M
diff --git a/options/license/ICU b/options/license/ICU
deleted file mode 100644
index 883ab200fb..0000000000
--- a/options/license/ICU
+++ /dev/null
@@ -1,12 +0,0 @@
-ICU License - ICU 1.8.1 and later
-
-COPYRIGHT AND PERMISSION NOTICE
-
-Copyright (c) 1995-2014 International Business Machines Corporation and others
-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, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright notice(s) and this permission notice appear in all copies of the Software and that both the above copyright notice(s) and this permission notice appear in supporting documentation.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder.
diff --git a/options/license/IEC-Code-Components-EULA b/options/license/IEC-Code-Components-EULA
deleted file mode 100644
index b74269afd9..0000000000
--- a/options/license/IEC-Code-Components-EULA
+++ /dev/null
@@ -1,37 +0,0 @@
-IEC Code Components End-user licence agreement
-
-Code Components in IEC standards (International Standards, Technical Specifications or
-Technical Reports) which have been identified and approved for licensing, are licensed subject to
-the following conditions:
-
-- Redistributions of software must retain the Copyright Notice, this list of conditions and the
-disclaimer below (“Disclaimerâ€).
-- The software license extends to modifications permitted under the relevant IEC standard.
-- The software license extends to clarifications and corrections approved by IEC.
-- Neither the name of IEC, nor the names of specific contributors, may be used to endorse or
-promote products derived from this software without specific prior written permission. The
-relevant IEC standard may be referenced when claiming compliance with the relevant IEC
-standard.
-- The user of Code Components shall attribute each such Code Component to IEC and identify
-the IEC standard from which it is taken. Such attribution (e.g., “This code was derived from IEC
-[insert standard reference number:publication year] within modifications permitted in the
-relevant IEC standard. Please reproduce this note if possible.â€), may be placed in the code itself
-or any other reasonable location.
-
-Code Components means components included in IEC standards that are intended to be directly
-processed by a computer and also includes any text found between the markers <CODE
-BEGINS> and <CODE ENDS>, or otherwise clearly labeled in this standard as a Code
-Component.
-
-The Disclaimer is:
-EACH OF THE CODE COMPONENTS IS PROVIDED BY THE COPYRIGHT HOLDERS AND
-CONTRIBUTORS “AS IS†AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
-NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE CODE
-COMPONENTS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/IJG b/options/license/IJG
deleted file mode 100644
index 761071caa4..0000000000
--- a/options/license/IJG
+++ /dev/null
@@ -1,38 +0,0 @@
-Independent JPEG Group License
-
-LEGAL ISSUES
-
-In plain English:
-
-1. We don't promise that this software works. (But if you find any bugs, please let us know!)
-2. You can use this software for whatever you want. You don't have to pay us.
-3. You may not pretend that you wrote this software. If you use it in a program, you must acknowledge somewhere in your documentation that you've used the IJG code.
-
-In legalese:
-
-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-1998, 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.
-
-These conditions apply to any software derived from or based on the IJG code, not just to the unmodified library. If you use our work, you ought to acknowledge us.
-
-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.
-
-ansi2knr.c is included in this distribution by permission of L. Peter Deutsch, sole proprietor of its copyright holder, Aladdin Enterprises of Menlo Park, CA. ansi2knr.c is NOT covered by the above copyright and conditions, but instead by the usual distribution terms of the Free Software Foundation; principally, that you must include source code if you redistribute it. (See the file ansi2knr.c for full details.) However, since ansi2knr.c is not needed as part of any program generated from the IJG code, this does not limit you more than the foregoing paragraphs do.
-
-The Unix configuration script "configure" was produced with GNU Autoconf. It is copyright by the Free Software Foundation but is freely distributable. The same holds for its supporting scripts (config.guess, config.sub, ltconfig, ltmain.sh). Another support script, install-sh, is copyright by M.I.T. but is also freely distributable.
-
-It appears that the arithmetic coding option of the JPEG spec is covered by patents owned by IBM, AT&T, and Mitsubishi. Hence arithmetic coding cannot legally be used without obtaining one or more licenses. For this reason, support for arithmetic coding has been removed from the free JPEG software. (Since arithmetic coding provides only a marginal gain over the unpatented Huffman mode, it is unlikely that very many implementations will support it.) So far as we are aware, there are no patent restrictions on the remaining code.
-
-The IJG distribution formerly included code to read and write GIF files. To avoid entanglement with the Unisys LZW patent, GIF reading support has been removed altogether, and the GIF writer has been simplified to produce "uncompressed GIFs". This technique does not use the LZW algorithm; the resulting GIF files are larger than usual, but are readable by all standard GIF decoders.
-
-We are required to state that
- "The Graphics Interchange Format(c) is the Copyright property of CompuServe Incorporated. GIF(sm) is a Service Mark property of CompuServe Incorporated."
diff --git a/options/license/IJG-short b/options/license/IJG-short
deleted file mode 100644
index bbb0859d80..0000000000
--- a/options/license/IJG-short
+++ /dev/null
@@ -1,35 +0,0 @@
-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/IPA b/options/license/IPA
deleted file mode 100644
index cb77cc0f35..0000000000
--- a/options/license/IPA
+++ /dev/null
@@ -1,83 +0,0 @@
-IPA Font License Agreement v1.0
-
-The Licensor provides the Licensed Program (as defined in Article 1 below) under the terms of this license agreement ("Agreement"). Any use, reproduction or distribution of the Licensed Program, or any exercise of rights under this Agreement by a Recipient (as defined in Article 1 below) constitutes the Recipient's acceptance of this Agreement.
-
-Article 1 (Definitions)
-
-1. "Digital Font Program" shall mean a computer program containing, or used to render or display fonts.
-
-2. "Licensed Program" shall mean a Digital Font Program licensed by the Licensor under this Agreement.
-
-3. "Derived Program" shall mean a Digital Font Program created as a result of a modification, addition, deletion, replacement or any other adaptation to or of a part or all of the Licensed Program, and includes a case where a Digital Font Program newly created by retrieving font information from a part or all of the Licensed Program or Embedded Fonts from a Digital Document File with or without modification of the retrieved font information.
-
-4. "Digital Content" shall mean products provided to end users in the form of digital data, including video content, motion and/or still pictures, TV programs or other broadcasting content and products consisting of character text, pictures, photographic images, graphic symbols and/or the like.
-
-5. "Digital Document File" shall mean a PDF file or other Digital Content created by various software programs in which a part or all of the Licensed Program becomes embedded or contained in the file for the display of the font ("Embedded Fonts"). Embedded Fonts are used only in the display of characters in the particular Digital Document File within which they are embedded, and shall be distinguished from those in any Digital Font Program, which may be used for display of characters outside that particular Digital Document File.
-
-6. "Computer" shall include a server in this Agreement.
-
-7. "Reproduction and Other Exploitation" shall mean reproduction, transfer, distribution, lease, public transmission, presentation, exhibition, adaptation and any other exploitation.
-
-8. "Recipient" shall mean anyone who receives the Licensed Program under this Agreement, including one that receives the Licensed Program from a Recipient.
-
-Article 2 (Grant of License)
-
-The Licensor grants to the Recipient a license to use the Licensed Program in any and all countries in accordance with each of the provisions set forth in this Agreement. However, any and all rights underlying in the Licensed Program shall be held by the Licensor. In no sense is this Agreement intended to transfer any right relating to the Licensed Program held by the Licensor except as specifically set forth herein or any right relating to any trademark, trade name, or service mark to the Recipient.
-
-1. The Recipient may install the Licensed Program on any number of Computers and use the same in accordance with the provisions set forth in this Agreement.
-
-2. The Recipient may use the Licensed Program, with or without modification in printed materials or in Digital Content as an expression of character texts or the like.
-
-3. The Recipient may conduct Reproduction and Other Exploitation of the printed materials and Digital Content created in accordance with the preceding Paragraph, for commercial or non-commercial purposes and in any form of media including but not limited to broadcasting, communication and various recording media.
-
-4. If any Recipient extracts Embedded Fonts from a Digital Document File to create a Derived Program, such Derived Program shall be subject to the terms of this agreement.
-
-5. If any Recipient performs Reproduction or Other Exploitation of a Digital Document File in which Embedded Fonts of the Licensed Program are used only for rendering the Digital Content within such Digital Document File then such Recipient shall have no further obligations under this Agreement in relation to such actions.
-
-6. The Recipient may reproduce the Licensed Program as is without modification and transfer such copies, publicly transmit or otherwise redistribute the Licensed Program to a third party for commercial or non-commercial purposes ("Redistribute"), in accordance with the provisions set forth in Article 3 Paragraph 2.
-
-7. The Recipient may create, use, reproduce and/or Redistribute a Derived Program under the terms stated above for the Licensed Program: provided, that the Recipient shall follow the provisions set forth in Article 3 Paragraph 1 when Redistributing the Derived Program.
-
-Article 3 (Restriction)
-
-The license granted in the preceding Article shall be subject to the following restrictions:
-
-1. If a Derived Program is Redistributed pursuant to Paragraph 4 and 7 of the preceding Article, the following conditions must be met :
-
- (1) The following must be also Redistributed together with the Derived Program, or be made available online or by means of mailing mechanisms in exchange for a cost which does not exceed the total costs of postage, storage medium and handling fees:
-
- (a) a copy of the Derived Program; and
-
- (b) any additional file created by the font developing program in the course of creating the Derived Program that can be used for further modification of the Derived Program, if any.
-
- (2) It is required to also Redistribute means to enable recipients of the Derived Program to replace the Derived Program with the Licensed Program first released under this License (the "Original Program"). Such means may be to provide a difference file from the Original Program, or instructions setting out a method to replace the Derived Program with the Original Program.
-
- (3) The Recipient must license the Derived Program under the terms and conditions of this Agreement.
-
- (4) No one may use or include the name of the Licensed Program as a program name, font name or file name of the Derived Program.
-
- (5) Any material to be made available online or by means of mailing a medium to satisfy the requirements of this paragraph may be provided, verbatim, by any party wishing to do so.
-
-2. If the Recipient Redistributes the Licensed Program pursuant to Paragraph 6 of the preceding Article, the Recipient shall meet all of the following conditions:
-
- (1) The Recipient may not change the name of the Licensed Program.
-
- (2) The Recipient may not alter or otherwise modify the Licensed Program.
-
- (3) The Recipient must attach a copy of this Agreement to the Licensed Program.
-
-3. THIS LICENSED PROGRAM IS PROVIDED BY THE LICENSOR "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTY AS TO THE LICENSED PROGRAM OR ANY DERIVED PROGRAM, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXTENDED, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO; PROCUREMENT OF SUBSTITUTED GOODS OR SERVICE; DAMAGES ARISING FROM SYSTEM FAILURE; LOSS OR CORRUPTION OF EXISTING DATA OR PROGRAM; LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE INSTALLATION, USE, THE REPRODUCTION OR OTHER EXPLOITATION OF THE LICENSED PROGRAM OR ANY DERIVED PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-4. The Licensor is under no obligation to respond to any technical questions or inquiries, or provide any other user support in connection with the installation, use or the Reproduction and Other Exploitation of the Licensed Program or Derived Programs thereof.
-
-Article 4 (Termination of Agreement)
-
-1. The term of this Agreement shall begin from the time of receipt of the Licensed Program by the Recipient and shall continue as long as the Recipient retains any such Licensed Program in any way.
-
-2. Notwithstanding the provision set forth in the preceding Paragraph, in the event of the breach of any of the provisions set forth in this Agreement by the Recipient, this Agreement shall automatically terminate without any notice. In the case of such termination, the Recipient may not use or conduct Reproduction and Other Exploitation of the Licensed Program or a Derived Program: provided that such termination shall not affect any rights of any other Recipient receiving the Licensed Program or the Derived Program from such Recipient who breached this Agreement.
-
-Article 5 (Governing Law)
-
-1. IPA may publish revised and/or new versions of this License. In such an event, the Recipient may select either this Agreement or any subsequent version of the Agreement in using, conducting the Reproduction and Other Exploitation of, or Redistributing the Licensed Program or a Derived Program. Other matters not specified above shall be subject to the Copyright Law of Japan and other related laws and regulations of Japan.
-
-2. This Agreement shall be construed under the laws of Japan.
diff --git a/options/license/IPL-1.0 b/options/license/IPL-1.0
deleted file mode 100644
index 6a65f60521..0000000000
--- a/options/license/IPL-1.0
+++ /dev/null
@@ -1,215 +0,0 @@
-IBM Public License Version 1.0
-
-THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS IBM
-PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
-OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
-
-1. DEFINITIONS
-"Contribution" means:
-
-a. in the case of International Business Machines Corporation ("IBM"), the Original Program, and
-
-b. in the case of each Contributor,
- i. changes to the Program, and
- ii. additions to the Program;
-where such changes and/or additions to the Program originate from and
-are distributed by that particular Contributor. A Contribution
-'originates' from a Contributor if it was added to the Program by
-such Contributor itself or anyone acting on such Contributor's
-behalf. Contributions do not include additions to the Program which:
-(i) are separate modules of software distributed in conjunction with
-the Program under their own license agreement, and (ii) are not
-derivative works of the Program.
-
-"Contributor" means IBM and any other entity that distributes the Program.
-
-"Licensed Patents " mean patent claims licensable by a
-Contributor which are necessarily infringed by the use or sale of its
-Contribution alone or when combined with the Program.
-
-"Original Program" means the original version of the software
-accompanying this Agreement as released by IBM, including source
-code, object code and documentation, if any.
-
-"Program" means the Original Program and Contributions.
-
-"Recipient" means anyone who receives the Program under this
-Agreement, including all Contributors.
-
-2. GRANT OF RIGHTS
-a. Subject to the terms of this Agreement, each Contributor hereby
-grants Recipient a non-exclusive, worldwide, royalty-free copyright
-license to reproduce, prepare derivative works of, publicly display,
-publicly perform, distribute and sublicense the Contribution of such
-Contributor, if any, and such derivative works, in source code and
-object code form.
-
-b. Subject to the terms of this Agreement, each Contributor hereby
-grants Recipient a non-exclusive, worldwide, royalty-free patent
-license under Licensed Patents to make, use, sell, offer to sell,
-import and otherwise transfer the Contribution of such Contributor,
-if any, in source code and object code form. This patent license
-shall apply to the combination of the Contribution and the Program
-if, at the time the Contribution is added by the Contributor, such
-addition of the Contribution causes such combination to be covered by
-the Licensed Patents. The patent license shall not apply to any
-other combinations which include the Contribution. No hardware per
-se is licensed hereunder.
-
-c. Recipient understands that although each Contributor grants the
-licenses to its Contributions set forth herein, no assurances are
-provided by any Contributor that the Program does not infringe the
-patent or other intellectual property rights of any other entity.
-Each Contributor disclaims any liability to Recipient for claims
-brought by any other entity based on infringement of intellectual
-property rights or otherwise. As a condition to exercising the
-rights and licenses granted hereunder, each Recipient hereby assumes
-sole responsibility to secure any other intellectual property rights
-needed, if any. For example, if a third party patent license is
-required to allow Recipient to distribute the Program, it is
-Recipient's responsibility to acquire that license before
-distributing the Program.
-
-d. Each Contributor represents that to its knowledge it has
-sufficient copyright rights in its Contribution, if any, to grant the
-copyright license set forth in this Agreement.
-
-3. REQUIREMENTS
-A Contributor may choose to distribute
-the Program in object code form under its own license agreement,
-provided that:
-
-a. it complies with the terms and conditions of this Agreement; and
-b. its license agreement:
- i. effectively disclaims on behalf of all Contributors all warranties
- and conditions, express and implied, including warranties or
- conditions of title and non-infringement, and implied warranties or
- conditions of merchantability and fitness for a particular purpose;
- ii. effectively excludes on behalf of all Contributors all liability
- for damages, including direct, indirect, special, incidental and
- consequential damages, such as lost profits;
- iii. states that any provisions which differ from this Agreement are
- offered by that Contributor alone and not by any other party; and
- iv. states that source code for the Program is available from such
- Contributor, and informs licensees how to obtain it in a reasonable
- manner on or through a medium customarily used for software exchange.
-
-When the Program is made available in source code form:
-a. it must be made available under this Agreement; and
-b. a copy of this Agreement must be included with each copy of the
-Program.
-
-Each Contributor must include the following in a conspicuous location in the Program:
-
- Copyright (C) 1996, 1999 International Business Machines Corporation and others. All Rights Reserved.
-
-In addition, each Contributor must identify itself as the originator
-of its Contribution, if any, in a manner that reasonably allows
-subsequent Recipients to identify the originator of the Contribution.
-
-4. COMMERCIAL DISTRIBUTION
-Commercial distributors of software may accept certain
-responsibilities with respect to end users, business partners and the
-like. While this license is intended to facilitate the commercial
-use of the Program, the Contributor who includes the Program in a
-commercial product offering should do so in a manner which does not
-create potential liability for other Contributors. Therefore, if a
-Contributor includes the Program in a commercial product offering,
-such Contributor ("Commercial Contributor") hereby agrees to defend
-and indemnify every other Contributor ("Indemnified Contributor")
-against any losses, damages and costs (collectively "Losses") arising
-from claims, lawsuits and other legal actions brought by a third
-party against the Indemnified Contributor to the extent caused by the
-acts or omissions of such Commercial Contributor in connection with
-its distribution of the Program in a commercial product offering.
-The obligations in this section do not apply to any claims or Losses
-relating to any actual or alleged intellectual property infringement.
-In order to qualify, an Indemnified Contributor must: a) promptly
-notify the Commercial Contributor in writing of such claim, and b)
-allow the Commercial Contributor to control, and cooperate with the
-Commercial Contributor in, the defense and any related settlement
-negotiations. The Indemnified Contributor may participate in any
-such claim at its own expense.
-
-For example, a Contributor might include the Program in a commercial
-product offering, Product X. That Contributor is then a Commercial
-Contributor. If that Commercial Contributor then makes performance
-claims, or offers warranties related to Product X, those performance
-claims and warranties are such Commercial Contributor's
-responsibility alone. Under this section, the Commercial Contributor
-would have to defend claims against the other Contributors related to
-those performance claims and warranties, and if a court requires any
-other Contributor to pay any damages as a result, the Commercial
-Contributor must pay those damages.
-
-5. NO WARRANTY
-EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
-PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
-WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
-OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
-responsible for determining the appropriateness of using and
-distributing the Program and assumes all risks associated with its
-exercise of rights under this Agreement, including but not limited to
-the risks and costs of program errors, compliance with applicable
-laws, damage to or loss of data, programs or equipment, and
-unavailability or interruption of operations.
-
-6. DISCLAIMER OF LIABILITY
-EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
-NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS
-GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. GENERAL
-If any provision of this Agreement is invalid or unenforceable under
-applicable law, it shall not affect the validity or enforceability of
-the remainder of the terms of this Agreement, and without further
-action by the parties hereto, such provision shall be reformed to the
-minimum extent necessary to make such provision valid and enforceable.
-
-If Recipient institutes patent litigation against a Contributor with
-respect to a patent applicable to software (including a cross-claim
-or counterclaim in a lawsuit), then any patent licenses granted by
-that Contributor to such Recipient under this Agreement shall
-terminate as of the date such litigation is filed. In addition, if
-Recipient institutes patent litigation against any entity (including
-a cross-claim or counterclaim in a lawsuit) alleging that the Program
-itself (excluding combinations of the Program with other software or
-hardware) infringes such Recipient's patent(s), then such Recipient's
-rights granted under Section 2(b) shall terminate as of the date such
-litigation is filed.
-
-All Recipient's rights under this Agreement shall terminate if it
-fails to comply with any of the material terms or conditions of this
-Agreement and does not cure such failure in a reasonable period of
-time after becoming aware of such noncompliance. If all Recipient's
-rights under this Agreement terminate, Recipient agrees to cease use
-and distribution of the Program as soon as reasonably practicable.
-However, Recipient's obligations under this Agreement and any
-licenses granted by Recipient relating to the Program shall continue
-and survive.
-
-IBM may publish new versions (including revisions) of this Agreement
-from time to time. Each new version of the Agreement will be given a
-distinguishing version number. The Program (including Contributions)
-may always be distributed subject to the version of the Agreement
-under which it was received. In addition, after a new version of the
-Agreement is published, Contributor may elect to distribute the
-Program (including its Contributions) under the new version. No one
-other than IBM has the right to modify this Agreement. Except as
-expressly stated in Sections 2(a) and 2(b) above, Recipient receives
-no rights or licenses to the intellectual property of any Contributor
-under this Agreement, whether expressly, by implication, estoppel or
-otherwise. All rights in the Program not expressly granted under
-this Agreement are reserved.
-
-This Agreement is governed by the laws of the State of New York and
-the intellectual property laws of the United States of America. No
-party to this Agreement will bring a legal action under this
-Agreement more than one year after the cause of action arose. Each
-party waives its rights to a jury trial in any resulting litigation.
diff --git a/options/license/ISC-Veillard b/options/license/ISC-Veillard
deleted file mode 100644
index c3bd5455c9..0000000000
--- a/options/license/ISC-Veillard
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright (C) 2003-2012 Daniel Veillard.
-Permission to use, copy,
-modify, and distribute this software for any purpose with or
-without fee is hereby granted, provided that the above copyright
-notice and this permission notice appear in all copies. THIS
-SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS
-AND CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
diff --git a/options/license/ImageMagick b/options/license/ImageMagick
deleted file mode 100644
index e627fd7b42..0000000000
--- a/options/license/ImageMagick
+++ /dev/null
@@ -1,98 +0,0 @@
-Before we get to the text of the license, lets just review what the license says in simple terms:
-
-It allows you to:
-
- * freely download and use ImageMagick software, in whole or in part, for personal, company internal, or commercial purposes;
- * use ImageMagick software in packages or distributions that you create;
- * link against a library under a different license;
- * link code under a different license against a library under this license;
- * merge code into a work under a different license;
- * extend patent grants to any code using code under this license;
- * and extend patent protection.
-
-It forbids you to:
-
- * redistribute any piece of ImageMagick-originated software without proper attribution;
- * use any marks owned by ImageMagick Studio LLC in any way that might state or imply that ImageMagick Studio LLC endorses your distribution;
- * use any marks owned by ImageMagick Studio LLC in any way that might state or imply that you created the ImageMagick software in question.
-
-It requires you to:
-
- * include a copy of the license in any redistribution you may make that includes ImageMagick software;
- * provide clear attribution to ImageMagick Studio LLC for any distributions that include ImageMagick software.
-
-It does not require you to:
-
- * include the source of the ImageMagick software itself, or of any modifications you may have made to it, in any redistribution you may assemble that includes it;
- * submit changes that you make to the software back to the ImageMagick Studio LLC (though such feedback is encouraged).
-
-A few other clarifications include:
-
- * ImageMagick is freely available without charge;
- * you may include ImageMagick on a DVD as long as you comply with the terms of the license;
- * you can give modified code away for free or sell it under the terms of the ImageMagick license or distribute the result under a different license, but you need to acknowledge the use of the ImageMagick software;
- * the license is compatible with the GPL V3.
- * when exporting the ImageMagick software, review its export classification.
-
-Terms and Conditions for Use, Reproduction, and Distribution
-
-The legally binding and authoritative terms and conditions for use, reproduction, and distribution of ImageMagick follow:
-
-Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization dedicated to making software imaging solutions freely available.
-
-1. Definitions.
-
-License shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
-
-Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
-
-Legal Entity shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, control means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-You (or Your) shall mean an individual or Legal Entity exercising permissions granted by this License.
-
-Source form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
-
-Object form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
-
-Work shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
-
-Derivative Works shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
-
-Contribution shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as Not a Contribution.
-
-Contributor shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
-
-2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
-
-3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
-
-4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
-
- a. You must give any other recipients of the Work or Derivative Works a copy of this License; and
- b. You must cause any modified files to carry prominent notices stating that You changed the files; and
- c. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
- d. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
-
-You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
-
-5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
-
-6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
-
-How to Apply the License to your Work
-
-To apply the ImageMagick License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information (don't include the brackets). The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the ImageMagick License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
-
- http://www.imagemagick.org/script/license.php
-
- Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
diff --git a/options/license/Imlib2 b/options/license/Imlib2
deleted file mode 100644
index 9f9dfd2126..0000000000
--- a/options/license/Imlib2
+++ /dev/null
@@ -1,9 +0,0 @@
-Imlib2 License
-
-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 of the Software and its Copyright notices. In addition publicly documented acknowledgment must be given that this software has been used if no source code of this software is made available publicly. Making the source available publicly means including the source for this software with the distribution, or a method to get this software via some reasonable mechanism (electronic transfer via a network or media) as well as making an offer to supply the source on request. This Copyright notice serves as an offer to supply the source on on request as well. Instead of this, supplying acknowledgments of use of this software in either Copyright notices, Manuals, Publicity and Marketing documents or any documentation provided with any product containing this software. This License does not apply to any software that links to the libraries provided by this software (statically or dynamically), but only to the software provided.
-
-Please see the COPYING-PLAIN for a plain-english explanation of this notice and its intent.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/options/license/Independent-modules-exception b/options/license/Independent-modules-exception
deleted file mode 100644
index 8f66dba6ab..0000000000
--- a/options/license/Independent-modules-exception
+++ /dev/null
@@ -1,18 +0,0 @@
-This is the file COPYING.FPC, it applies to the Free Pascal Run-Time Library
-(RTL) and packages (packages) distributed by members of the Free Pascal
-Development Team.
-
-The source code of the Free Pascal Runtime Libraries and packages are
-distributed under the Library GNU General Public License
-(see the file COPYING) with the following modification:
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent modules,
-and to copy and distribute the resulting executable under terms of your choice,
-provided that you also meet, for each linked independent module, the terms
-and conditions of the license of that module. An independent module is a module
-which is not derived from or based on this library. If you modify this
-library, you may extend this exception to your version of the library, but you are
-not obligated to do so. If you do not wish to do so, delete this exception
-statement from your version.
diff --git a/options/license/Info-ZIP b/options/license/Info-ZIP
deleted file mode 100644
index 9067701bbb..0000000000
--- a/options/license/Info-ZIP
+++ /dev/null
@@ -1,16 +0,0 @@
-Info-ZIP License
-
-Copyright (c) 1990-2009 Info-ZIP. All rights reserved.
-
-For the purposes of this copyright and license, "Info-ZIP" is defined as the following set of individuals:
-
- Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, Jean-loup Gailly, Hunter Goatley, Ed Gordon, Ian Gorman, Chris Herborth, Dirk Haase, Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, Kai Uwe Rommel, Steve Salisbury, Dave Smith, Steven M. Schweda, Christian Spieler, Cosmin Truta, Antoine Verheijen, Paul von Behren, Rich Wales, Mike White.
-
-This software is provided "as is," without warranty of any kind, express or implied. In no event shall Info-ZIP or its contributors be held liable for any direct, indirect, incidental, special or consequential damages arising out of the use of or inability to use this software.
-
-Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the above disclaimer and the following restrictions:
-
- * Redistributions of source code (in whole or in part) must retain the above copyright notice, definition, disclaimer, and this list of conditions.
- * Redistributions in binary form (compiled executables and libraries) must reproduce the above copyright notice, definition, disclaimer, and this list of conditions in documentation and/or other materials provided with the distribution. Additional documentation is not needed for executables where a command line license option provides these and a note regarding this option is in the executable's startup banner. The sole exception to this condition is redistribution of a standard UnZipSFX binary (including SFXWiz) as part of a self-extracting archive; that is permitted without inclusion of this license, as long as the normal SFX banner has not been removed from the binary or disabled.
- * Altered versions--including, but not limited to, ports to new operating systems, existing ports with new graphical interfaces, versions with modified or added functionality, and dynamic, shared, or static library versions not from Info-ZIP--must be plainly marked as such and must not be misrepresented as being the original source or, if binaries, compiled from the original source. Such altered versions also must not be misrepresented as being Info-ZIP releases--including, but not limited to, labeling of the altered versions with the names "Info-ZIP" (or any variation thereof, including, but not limited to, different capitalizations), "Pocket UnZip," "WiZ" or "MacZip" without the explicit permission of Info-ZIP. Such altered versions are further prohibited from misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or the Info-ZIP URL(s), such as to imply Info-ZIP will provide support for the altered versions.
- * Info-ZIP retains the right to use the names "Info-ZIP," "Zip," "UnZip," "UnZipSFX," "WiZ," "Pocket UnZip," "Pocket Zip," and "MacZip" for its own source and binary releases.
diff --git a/options/license/Inner-Net-2.0 b/options/license/Inner-Net-2.0
deleted file mode 100644
index f8db440f2a..0000000000
--- a/options/license/Inner-Net-2.0
+++ /dev/null
@@ -1,34 +0,0 @@
-The Inner Net License, Version 2.00
-
-The author(s) grant permission for redistribution and use in source and
-binary forms, with or without modification, of the software and documentation
-provided that the following conditions are met:
-
-0. If you receive a version of the software that is specifically labelled
- as not being for redistribution (check the version message and/or README),
- you are not permitted to redistribute that version of the software in any
- way or form.
-1. All terms of the all other applicable copyrights and licenses must be
- followed.
-2. Redistributions of source code must retain the authors' copyright
- notice(s), this list of conditions, and the following disclaimer.
-3. Redistributions in binary form must reproduce the authors' copyright
- notice(s), this list of conditions, and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-4. [The copyright holder has authorized the removal of this clause.]
-5. Neither the name(s) of the author(s) nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-If these license terms cause you a real problem, contact the author.
diff --git a/options/license/InnoSetup b/options/license/InnoSetup
deleted file mode 100644
index 337584e6d1..0000000000
--- a/options/license/InnoSetup
+++ /dev/null
@@ -1,27 +0,0 @@
-Inno Setup License
-==================
-
-Except where otherwise noted, all of the documentation and software included in the Inno Setup
-package is copyrighted by Jordan Russell.
-
-Copyright (C) 1997-2024 Jordan Russell. All rights reserved.
-Portions Copyright (C) 2000-2024 Martijn Laan. All rights reserved.
-
-This software is provided "as-is," without any express or implied warranty. In no event shall the
-author be held liable for any damages arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose, including commercial
-applications, and to alter and redistribute it, provided that the following conditions are met:
-
-1. All redistributions of source code files must retain all copyright notices that are currently in
- place, and this list of conditions without modification.
-
-2. All redistributions in binary form must retain all occurrences of the above copyright notice and
- web site addresses that are currently in place (for example, in the About boxes).
-
-3. The origin of this software must not be misrepresented; you must not claim that you wrote the
- original software. If you use this software to distribute a product, an acknowledgment in the
- product documentation would be appreciated but is not required.
-
-4. Modified versions in source or binary form must be plainly marked as such, and must not be
- misrepresented as being the original software.
diff --git a/options/license/Intel b/options/license/Intel
deleted file mode 100644
index 5b949805f4..0000000000
--- a/options/license/Intel
+++ /dev/null
@@ -1,13 +0,0 @@
-Intel Open Source License
-
-Copyright (c) 1996-2000 Intel Corporation All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- • Neither the name of the Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-EXPORT LAWS: THIS LICENSE ADDS NO RESTRICTIONS TO THE EXPORT LAWS OF YOUR JURISDICTION. It is licensee's responsibility to comply with any export regulations applicable in licensee's jurisdiction. Under CURRENT (May 2000) U.S. export regulations this software is eligible for export from the U.S. and can be downloaded by or otherwise exported or reexported worldwide EXCEPT to U.S. embargoed destinations which include Cuba, Iraq, Libya, North Korea, Iran, Syria, Sudan, Afghanistan and any other country to which the U.S. has embargoed goods and services.
diff --git a/options/license/Intel-ACPI b/options/license/Intel-ACPI
deleted file mode 100644
index e5cc5fd190..0000000000
--- a/options/license/Intel-ACPI
+++ /dev/null
@@ -1,34 +0,0 @@
-ACPI - Software License Agreement
-Software License Agreement IMPORTANT - READ BEFORE COPYING, INSTALLING OR USING.
-
-Do not use or load this software and any associated materials (collectively, the "Software") until you have carefully read the following terms and conditions. By loading or using the Software, you agree to the terms of this Agreement. If you do not wish to so agree, do not install or use the Software.
-
-1. COPYRIGHT NOTICE Some or all of this work - Copyright © 1999-2005, Intel Corp. All rights reserved.
-
-2. LICENSE
-
- 2.1. This is your license from Intel Corp. under its intellectual property rights. You may have additional license terms from the party that provided you this software, covering your right to use that party's intellectual property rights.
-
- 2.2. Intel grants, free of charge, to any person ("Licensee") obtaining a copy of the source code appearing in this file ("Covered Code") an irrevocable, perpetual, worldwide license under Intel's copyrights in the base code distributed originally by Intel ("Original Intel Code") to copy, make derivatives, distribute, use and display any portion of the Covered Code in any form, with the right to sublicense such rights; and
-
- 2.3. Intel grants Licensee a non-exclusive and non-transferable patent license (with the right to sublicense), under only those claims of Intel patents that are infringed by the Original Intel Code, to make, use, sell, offer to sell, and import the Covered Code and derivative works thereof solely to the minimum extent necessary to exercise the above copyright license, and in no event shall the patent license extend to any additions to or modifications of the Original Intel Code. No other license or right is granted directly or by implication, estoppel or otherwise; The above copyright and patent license is granted only if the following conditions are met:
-
-3. CONDITIONS
-
- 3.1. Redistribution of Source with Rights to Further Distribute Source. Redistribution of source code of any substantial portion of the Covered Code or modification with rights to further distribute source must include the above Copyright Notice, the above License, this list of Conditions, and the following Disclaimer and Export Compliance provision. In addition, Licensee must cause all Covered Code to which Licensee contributes to contain a file documenting the changes Licensee made to create that Covered Code and the date of any change. Licensee must include in that file the documentation of any changes made by any predecessor Licensee. Licensee must include a prominent statement that the modification is derived, directly or indirectly, from Original Intel Code.
-
- 3.2. Redistribution of Source with no Rights to Further Distribute Source. Redistribution of source code of any substantial portion of the Covered Code or modification without rights to further distribute source must include the following Disclaimer and Export Compliance provision in the documentation and/or other materials provided with distribution. In addition, Licensee may not authorize further sublicense of source of any portion of the Covered Code, and must include terms to the effect that the license from Licensee to its licensee is limited to the intellectual property embodied in the software Licensee provides to its licensee, and not to intellectual property embodied in modifications its licensee may make.
-
- 3.3. Redistribution of Executable. Redistribution in executable form of any substantial portion of the Covered Code or modification must reproduce the above Copyright Notice, and the following Disclaimer and Export Compliance provision in the documentation and/or other materials provided with the distribution.
-
- 3.4. Intel retains all right, title, and interest in and to the Original Intel Code.
-
- 3.5. Neither the name Intel nor any other trademark owned or controlled by Intel shall be used in advertising or otherwise to promote the sale, use or other dealings in products derived from or relating to the Covered Code without prior written authorization from Intel.
-
-4. DISCLAIMER AND EXPORT COMPLIANCE
-
- 4.1. INTEL MAKES NO WARRANTY OF ANY KIND REGARDING ANY SOFTWARE PROVIDED HERE. ANY SOFTWARE ORIGINATING FROM INTEL OR DERIVED FROM INTEL SOFTWARE IS PROVIDED "AS IS," AND INTEL WILL NOT PROVIDE ANY SUPPORT, ASSISTANCE, INSTALLATION, TRAINING OR OTHER SERVICES. INTEL WILL NOT PROVIDE ANY UPDATES, ENHANCEMENTS OR EXTENSIONS. INTEL SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE.
-
- 4.2. IN NO EVENT SHALL INTEL HAVE ANY LIABILITY TO LICENSEE, ITS LICENSEES OR ANY OTHER THIRD PARTY, FOR ANY LOST PROFITS, LOST DATA, LOSS OF USE OR COSTS OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, UNDER ANY CAUSE OF ACTION OR THEORY OF LIABILITY, AND IRRESPECTIVE OF WHETHER INTEL HAS ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING THE FAILURE OF THE ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.
-
- 4.3. Licensee shall not export, either directly or indirectly, any of this software or system incorporating such software without first obtaining any required license or other approval from the U. S. Department of Commerce or any other agency or department of the United States Government. In the event Licensee exports any such software from the United States or re-exports any such software from a foreign destination, Licensee shall ensure that the distribution and export/re-export of the software is in compliance with all laws, regulations, orders, or other restrictions of the U.S. Export Administration Regulations. Licensee agrees that neither it nor any of its subsidiaries will export/re-export any technical data, process, software, or service, directly or indirectly, to any country for which the United States government or any agency thereof requires an export license, other governmental approval, or letter of assurance, without first obtaining such license, approval or letter.
diff --git a/options/license/Interbase-1.0 b/options/license/Interbase-1.0
deleted file mode 100644
index 5a73f24c31..0000000000
--- a/options/license/Interbase-1.0
+++ /dev/null
@@ -1,199 +0,0 @@
-INTERBASE PUBLIC LICENSE
-Version 1.0
-
-1. Definitions.
-
-1.0.1. "Commercial Use" means distribution or otherwise making the Covered Code available to a third party.
-
-1.1. ''Contributor'' means each entity that creates or contributes to the creation of Modifications.
-
-1.2. ''Contributor Version'' means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
-1.3. ''Covered Code'' means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
-1.4. ''Electronic Distribution Mechanism'' means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
-1.5. ''Executable'' means Covered Code in any form other than Source Code.
-
-1.6. ''Initial Developer'' means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
-1.7. ''Larger Work'' means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
-1.8. ''License'' means this document.
-
-1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
-1.9. ''Modifications'' means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
-
-A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
-
-B. Any new file that contains any part of the Original Code or previous Modifications.
-
-1.10. ''Original Code'' means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
-1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
-1.11. ''Source Code'' means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
-1.12. "You'' (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You'' includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control'' means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2. Source Code License.
-
-2.1. The Initial Developer Grant.
-
-The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and
-
- (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).
-
- (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.
-
- (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices.
-
-2.2. Contributor Grant.
-
-Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license
-
- (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and
-
- (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
-
- (c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first makes Commercial Use of the Covered Code.
-
- (d) Notwithstanding Section 2.2(b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor.
-
-3. Distribution Obligations.
-
-3.1. Application of License.
-
-The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
-3.2. Availability of Source Code.
-
-Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
-3.3. Description of Modifications.
-
-You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
-3.4. Intellectual Property Matters
-
- (a) Third Party Claims.
-
- If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL'' which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
-
- (b) Contributor APIs.
-
- If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file.
-
- (c) Representations.
-
- Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.
-
-3.5. Required Notices.
-
-You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
-3.6. Distribution of Executable Versions.
-
-You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
-3.7. Larger Works.
-
-You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Application of this License.
-
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code.
-
-6. Versions of the License.
-
-6.1. New Versions.
-
-Borland Software Corporation (''Interbase'') may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
-6.2. Effect of New Versions.
-
-Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Interbase. No one other than Interbase has the right to modify the terms applicable to Covered Code created under this License.
-
-6.3. Derivative Works.
-
-If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases ''Mozilla'', ''MOZILLAPL'', ''MOZPL'', ''Netscape'', "MPL", ''NPL", "Interbase", "ISC", "IB'' or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
-6.4 Origin of the Interbase Public License.
-
-The Interbase public license is based on the Mozilla Public License V 1.1 with the following changes:
-
-The license is published by Borland Software Corporation. Only Borland Software Corporation can modify the terms applicable to Covered Code.
-The license can be modified used for code which is not already governed by this license. Modified versions of the license must be renamed to avoid confusion with Netscape?s or Interbase Software?s license and must include a description of changes from the Interbase Public License.
-The name of the license in Exhibit A is the "Interbase Public License".
-The reference to an alternative license in Exhibit A has been removed.
-Amendments I, II, III, V, and VI have been deleted.
-Exhibit A, Netscape Public License has been deleted
-A new amendment (II) has been added, describing the required and restricted rights to use the trademarks of Borland Software Corporation
-7. DISCLAIMER OF WARRANTY.
-
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS'' BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-
-8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-8.2. If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant") alleging that:
-
- (a) such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.
-
- (b) any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant.
-
-8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
-8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY.
-
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS.
-
-The Covered Code is a ''commercial item,'' as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of ''commercial computer software'' and ''commercial computer software documentation,'' as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
-11. MISCELLANEOUS.
-
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-12. RESPONSIBILITY FOR CLAIMS.
-
-As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-13. MULTIPLE-LICENSED CODE.
-
-Initial Developer may designate portions of the Covered Code as "Multiple-Licensed". "Multiple-Licensed" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the NPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A.
-
-EXHIBIT A - InterBase Public License.
-
-``The contents of this file are subject to the Interbase Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.Interbase.com/IPL.html
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-The Original Code was created by InterBase Software Corp and its successors.
-
-Portions created by Borland/Inprise are Copyright (C) Borland/Inprise. All Rights Reserved.
-
-Contributor(s): ______________________________________.
-
-AMENDMENTS
-
-I. InterBase and logo. This License does not grant any rights to use the trademarks "Interbase'', "Java" or "JavaScript" even if such marks are included in the Original Code or Modifications.
-
-II. Trademark Usage.
-
-II.1. Advertising Materials. All advertising materials mentioning features or use of the covered Code must display the following acknowledgement: "This product includes software developed by Borland Software Corp. "
-
-II.2. Endorsements. The names "InterBase," "ISC," and "IB" must not be used to endorse or promote Contributor Versions or Larger Works without the prior written permission of Interbase.
-
-II.3. Product Names. Contributor Versions and Larger Works may not be called "InterBase" or "Interbase" nor may the word "InterBase" appear in their names without the prior written permission of Interbase.
diff --git a/options/license/JPL-image b/options/license/JPL-image
deleted file mode 100644
index 828b1b5424..0000000000
--- a/options/license/JPL-image
+++ /dev/null
@@ -1,21 +0,0 @@
-JPL Image Use Policy
-
-Unless otherwise noted, images and video on JPL public web sites (public sites ending with a jpl.nasa.gov address) may be used for any purpose without prior permission, subject to the special cases noted below. Publishers who wish to have authorization may print this page and retain it for their records; JPL does not issue image permissions on an image by image basis.
-
-By electing to download the material from this web site the user agrees:
-
-1. that Caltech makes no representations or warranties with respect to ownership of copyrights in the images, and does not represent others who may claim to be authors or owners of copyright of any of the images, and makes no warranties as to the quality of the images. Caltech shall not be responsible for any loss or expenses resulting from the use of the images, and you release and hold Caltech harmless from all liability arising from such use.
-2. to use a credit line in connection with images. Unless otherwise noted in the caption information for an image, the credit line should be "Courtesy NASA/JPL-Caltech."
-3. that the endorsement of any product or service by Caltech, JPL or NASA must not be claimed or implied.
-
-Special Cases:
-
-* Prior written approval must be obtained to use the NASA insignia logo (the blue "meatball" insignia), the NASA logotype (the red "worm" logo) and the NASA seal. These images may not be used by persons who are not NASA employees or on products (including Web pages) that are not NASA sponsored. In addition, no image may be used to explicitly or implicitly suggest endorsement by NASA, JPL or Caltech of commercial goods or services. Requests to use NASA logos may be directed to Bert Ulrich, Public Services Division, NASA Headquarters, Code POS, Washington, DC 20546, telephone (202) 358-1713, fax (202) 358-4331, email bert.ulrich@hq.nasa.gov.
-
-* Prior written approval must be obtained to use the JPL logo (stylized JPL letters in red or other colors). Requests to use the JPL logo may be directed to the Institutional Communications Office, email instcomm@jpl.nasa.gov.
-
-* If an image includes an identifiable person, using the image for commercial purposes may infringe that person's right of privacy or publicity, and permission should be obtained from the person. NASA and JPL generally do not permit likenesses of current employees to appear on commercial products. For more information, consult the NASA and JPL points of contact listed above.
-
-* JPL/Caltech contractors and vendors who wish to use JPL images in advertising or public relation materials should direct requests to the Institutional Communications Office, email instcomm@jpl.nasa.gov.
-
-* Some image and video materials on JPL public web sites are owned by organizations other than JPL or NASA. These owners have agreed to make their images and video available for journalistic, educational and personal uses, but restrictions are placed on commercial uses. To obtain permission for commercial use, contact the copyright owner listed in each image caption. Ownership of images and video by parties other than JPL and NASA is noted in the caption material with each image.
diff --git a/options/license/JPNIC b/options/license/JPNIC
deleted file mode 100644
index 6cc1d094c8..0000000000
--- a/options/license/JPNIC
+++ /dev/null
@@ -1,40 +0,0 @@
-Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved.
-
-By using this file, you agree to the terms and conditions set forth bellow.
-
- LICENSE TERMS AND CONDITIONS
-
-The following License Terms and Conditions apply, unless a different
-license is obtained from Japan Network Information Center ("JPNIC"),
-a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
-Chiyoda-ku, Tokyo 101-0047, Japan.
-
-1. Use, Modification and Redistribution (including distribution of any
- modified or derived work) in source and/or binary forms is permitted
- under this License Terms and Conditions.
-
-2. Redistribution of source code must retain the copyright notices as they
- appear in each source code file, this License Terms and Conditions.
-
-3. Redistribution in binary form must reproduce the Copyright Notice,
- this License Terms and Conditions, in the documentation and/or other
- materials provided with the distribution. For the purposes of binary
- distribution the "Copyright Notice" refers to the following language:
- "Copyright (c) 2000-2002 Japan Network Information Center. All rights
- reserved."
-
-4. The name of JPNIC may not be used to endorse or promote products
- derived from this Software without specific prior written approval of
- JPNIC.
-
-5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/options/license/JSON b/options/license/JSON
deleted file mode 100644
index e29500b0e2..0000000000
--- a/options/license/JSON
+++ /dev/null
@@ -1,11 +0,0 @@
-JSON License
-
-Copyright (c) 2002 JSON.org
-
-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 shall be used for Good, not Evil.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/options/license/Jam b/options/license/Jam
deleted file mode 100644
index 78d9abe6e5..0000000000
--- a/options/license/Jam
+++ /dev/null
@@ -1,5 +0,0 @@
-License is hereby granted to use this software and distribute it freely,
-as long as this copyright notice is retained and modifications are
-clearly marked.
-
-ALL WARRANTIES ARE HEREBY DISCLAIMED.
diff --git a/options/license/JasPer-2.0 b/options/license/JasPer-2.0
deleted file mode 100644
index 93d11287d4..0000000000
--- a/options/license/JasPer-2.0
+++ /dev/null
@@ -1,17 +0,0 @@
-JasPer License Version 2.0
-
-Copyright (c) 2001-2006 Michael David Adams
-Copyright (c) 1999-2000 Image Power, Inc.
-Copyright (c) 1999-2000 The University of British Columbia
-
-All rights reserved.
-
-Permission is hereby granted, free of charge, to any person (the "User") 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, 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:
-
-1. The above copyright notices and this permission notice (which includes the disclaimer below) shall be included in all copies or substantial portions of the Software.
-
-2. The name of a copyright holder shall not be used to endorse or promote products derived from the Software without specific prior written permission.
-
-THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF THE SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. THE SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
-"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO
-EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. NO ASSURANCES ARE PROVIDED BY THE COPYRIGHT HOLDERS THAT THE SOFTWARE DOES NOT INFRINGE THE PATENT OR OTHER INTELLECTUAL PROPERTY RIGHTS OF ANY OTHER ENTITY. EACH COPYRIGHT HOLDER DISCLAIMS ANY LIABILITY TO THE USER FOR CLAIMS BROUGHT BY ANY OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR OTHERWISE. AS A CONDITION TO EXERCISING THE RIGHTS GRANTED HEREUNDER, EACH USER HEREBY ASSUMES SOLE RESPONSIBILITY TO SECURE ANY OTHER INTELLECTUAL PROPERTY RIGHTS NEEDED, IF ANY. THE SOFTWARE IS NOT FAULT-TOLERANT AND IS NOT INTENDED FOR USE IN MISSION-CRITICAL SYSTEMS, SUCH AS THOSE USED IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL SYSTEMS, DIRECT LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE SOFTWARE OR SYSTEM COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). THE COPYRIGHT HOLDERS SPECIFICALLY DISCLAIM ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.
diff --git a/options/license/Kastrup b/options/license/Kastrup
deleted file mode 100644
index 46d1e9e0e4..0000000000
--- a/options/license/Kastrup
+++ /dev/null
@@ -1,3 +0,0 @@
-Copyright(c) 2001 by David Kastrup
-Any use of the code is permitted as long as this copyright
-notice is preserved in the code.
diff --git a/options/license/Kazlib b/options/license/Kazlib
deleted file mode 100644
index 714f2eabf9..0000000000
--- a/options/license/Kazlib
+++ /dev/null
@@ -1,4 +0,0 @@
-Copyright (C) 1999 Kaz Kylheku
-
-Free Software License:
-All rights are reserved by the author, with the following exceptions: Permission is granted to freely reproduce and distribute this software, possibly in exchange for a fee, provided that this copyright notice appears intact. Permission is also granted to adapt this software to produce derivative works, as long as the modified versions carry this copyright notice and additional notices stating that the work has been modified. This source code may be translated into executable form and incorporated into proprietary software; there is no requirement for such software to contain a copyright notice related to this source.
diff --git a/options/license/KiCad-libraries-exception b/options/license/KiCad-libraries-exception
deleted file mode 100644
index ae8854b119..0000000000
--- a/options/license/KiCad-libraries-exception
+++ /dev/null
@@ -1 +0,0 @@
-To the extent that the creation of electronic designs that use 'Licensed Material' can be considered to be 'Adapted Material', then the copyright holder waives article 3 of the license with respect to these designs and any generated files which use data provided as part of the 'Licensed Material'.
diff --git a/options/license/Knuth-CTAN b/options/license/Knuth-CTAN
deleted file mode 100644
index cd91789fd0..0000000000
--- a/options/license/Knuth-CTAN
+++ /dev/null
@@ -1,5 +0,0 @@
-This software is copyrighted. Unlimited copying and redistribution
-of this package and/or its individual files are permitted
-as long as there are no modifications. Modifications, and
-redistribution of modifications, are also permitted, but
-only if the resulting package and/or files are renamed.
diff --git a/options/license/LAL-1.2 b/options/license/LAL-1.2
deleted file mode 100644
index b8907ab6ff..0000000000
--- a/options/license/LAL-1.2
+++ /dev/null
@@ -1,67 +0,0 @@
-Licence Art Libre
-[ Copyleft Attitude ]
-
-Version 1.2
-
-Préambule :
-
-Avec cette Licence Art Libre, l’autorisation est donnée de copier, de diffuser et de transformer librement les oeuvres dans le respect des droits de l’auteur.
-
-Loin d’ignorer les droits de l’auteur, cette licence les reconnaît et les protège. Elle en reformule le principe en permettant au public de faire un usage créatif des oeuvres d’art.
-Alors que l’usage fait du droit de la propriété littéraire et artistique conduit à restreindre l’accès du public à l’oeuvre, la Licence Art Libre a pour but de le favoriser.
-L’intention est d’ouvrir l’accès et d’autoriser l’utilisation des ressources d’une oeuvre par le plus grand nombre. En avoir jouissance pour en multiplier les réjouissances, créer de nouvelles conditions de création pour amplifier les possibilités de création. Dans le respect des auteurs avec la reconnaissance et la défense de leur droit moral.
-
-En effet, avec la venue du numérique, l’invention de l’internet et des logiciels libres, un nouveau mode de création et de production est apparu. Il est aussi l’amplification de ce qui a été expérimenté par nombre d’artistes contemporains.
-Le savoir et la création sont des ressources qui doivent demeurer libres pour être encore véritablement du savoir et de la création. C’est à dire rester une recherche fondamentale qui ne soit pas directement liée à une application concrète. Créer c’est découvrir l’inconnu, c’est inventer le réel avant tout souci de réalisme.
-Ainsi, l’objet de l’art n’est pas confondu avec l’objet d’art fini et défini comme tel. C’est la raison essentielle de cette Licence Art Libre : promouvoir et protéger des pratiques artistiques libérées des seules règles de l’économie de marché.
-
-DÉFINITIONS
-
-– L’oeuvre :il s’agit d’une oeuvre commune qui comprend l’oeuvre originelle ainsi que toutes les contributions postérieures (les originaux conséquents et les copies). Elle est créée à l’initiative de l’auteur originel qui par cette licence définit les conditions selon lesquelles les contributions sont faites.
-
-– L’oeuvre originelle :c’est-à-dire l’oeuvre créée par l’initiateur de l’oeuvre commune dont les copies vont être modifiées par qui le souhaite.
-
-– Les oeuvres conséquentes :c’est-à-dire les propositions des auteurs qui contribuent à la formation de l’oeuvre en faisant usage des droits de reproduction, de diffusion et de modification que leur confère la licence.
-
-– Original (source ou ressource de l’oeuvre) :exemplaire daté de l’oeuvre, de sa définition, de sa partition ou de son programme que l’auteur présente comme référence pour toutes actualisations, interprétations, copies ou reproductions ultérieures.
-
-– Copie :toute reproduction d’un original au sens de cette licence.
-
-– Auteur de l’oeuvre originelle :c’est la personne qui a créé l’oeuvre à l’origine d’une arborescence de cette oeuvre modifiée. Par cette licence, l’auteur détermine les conditions dans lesquelles ce travail se fait.
-
-– Contributeur :toute personne qui contribue à la création de l’oeuvre. Il est l’auteur d’une oeuvre originale résultant de la modification d’une copie de l’oeuvre originelle ou de la modification d’une copie d’une oeuvre conséquente.
-
-1. OBJET
-Cette licence a pour objet de définir les conditions selon lesquelles vous pouvez jouir librement de cette oeuvre.
-
-2. L’ÉTENDUE DE LA JOUISSANCE
-Cette oeuvre est soumise au droit d’auteur, et l’auteur par cette licence vous indique quelles sont vos libertés pour la copier, la diffuser et la modifier:
-
-2.1 LA LIBERTÉ DE COPIER (OU DE REPRODUCTION)
-Vous avez la liberté de copier cette oeuvre pour un usage personnel, pour vos amis, ou toute autre personne et quelque soit la technique employée.
-
-2.2 LA LIBERTÉ DE DIFFUSER, D’INTERPRÉTER (OU DE REPRÉSENTATION)
-Vous pouvez diffuser librement les copies de ces oeuvres, modifiées ou non, quel que soit le support, quel que soit le lieu, à titre onéreux ou gratuit si vous respectez toutes les conditions suivantes:
- – joindre aux copies, cette licence à l’identique, ou indiquer précisément où se trouve la licence, – indiquer au destinataire le nom de l’auteur des originaux, – indiquer au destinataire où il pourra avoir accès aux originaux (originels et/ou conséquents). L’auteur de l’original pourra, s’il le souhaite, vous autoriser à diffuser l’original dans les mêmes conditions que les copies.
-
-2.3 LA LIBERTÉ DE MODIFIER
-Vous avez la liberté de modifier les copies des originaux (originels et conséquents), qui peuvent être partielles ou non, dans le respect des conditions prévues à l’article 2.2 en cas de diffusion (ou représentation) de la copie modifiée. L’auteur de l’original pourra, s’il le souhaite, vous autoriser à modifier l’original dans les mêmes conditions que les copies.
-
-3. L’INCORPORATION DE L’OEUVRE
-Tous les éléments de cette oeuvre doivent demeurer libres, c’est pourquoi il ne vous est pas permis d’intégrer les originaux (originels et conséquents) dans une autre oeuvre qui ne serait pas soumise à cette licence.
-
-4. VOS DROITS D’AUTEUR
-Cette licence n’a pas pour objet de nier vos droits d’auteur sur votre contribution. En choisissant de contribuer à l’évolution de cette oeuvre, vous acceptez seulement d’offrir aux autres les mêmes droits sur votre contribution que ceux qui vous ont été accordés par cette licence.
-
-5. LA DURÉE DE LA LICENCE
-Cette licence prend effet dès votre acceptation de ses dispositions. Le fait de copier, de diffuser, ou de modifier l’oeuvre constitue une acception tacite. Cette licence a pour durée la durée des droits d’auteur attachés à l’oeuvre. Si vous ne respectez pas les termes de cette licence, vous perdez automatiquement les droits qu’elle vous confère. Si le régime juridique auquel vous êtes soumis ne vous permet pas de respecter les termes de cette licence, vous ne pouvez pas vous prévaloir des libertés qu’elle confère.
-
-6. LES DIFFÉRENTES VERSIONS DE LA LICENCE
-Cette licence pourra être modifiée régulièrement, en vue de son amélioration, par ses auteurs (les acteurs du mouvement « copyleft attitude ») sous la forme de nouvelles versions numérotées.
-Vous avez toujours le choix entre vous contenter des dispositions contenues dans la version sous laquelle la copie vous a été communiquée ou alors, vous prévaloir des dispositions d’une des versions ultérieures.
-
-7. LES SOUS-LICENCES
-Les sous licences ne sont pas autorisées par la présente. Toute personne qui souhaite bénéficier des libertés qu’elle confère sera liée directement à l’auteur de l’oeuvre originelle.
-
-8. LA LOI APPLICABLE AU CONTRAT
-Cette licence est soumise au droit français.
diff --git a/options/license/LAL-1.3 b/options/license/LAL-1.3
deleted file mode 100644
index ca1a447346..0000000000
--- a/options/license/LAL-1.3
+++ /dev/null
@@ -1,88 +0,0 @@
-Licence Art Libre 1.3 (LAL 1.3)
-
-Préambule :
-
-Avec la Licence Art Libre, l’autorisation est donnée de copier, de diffuser et de transformer librement les œuvres dans le respect des droits de l’auteur.
-
-Loin d’ignorer ces droits, la Licence Art Libre les reconnaît et les protège. Elle en reformule l’exercice en permettant à tout un chacun de faire un usage créatif des productions de l’esprit quels que soient leur genre et leur forme d’expression.
-
-Si, en règle générale, l’application du droit d’auteur conduit à restreindre l’accès aux œuvres de l’esprit, la Licence Art Libre, au contraire, le favorise. L’intention est d’autoriser l’utilisation des ressources d’une œuvre ; créer de nouvelles conditions de création pour amplifier les possibilités de création. La Licence Art Libre permet d’avoir jouissance des œuvres tout en reconnaissant les droits et les responsabilités de chacun.
-
-Avec le développement du numérique, l’invention d’internet et des logiciels libres, les modalités de création ont évolué : les productions de l’esprit s’offrent naturellement à la circulation, à l’échange et aux transformations. Elles se prêtent favorablement à la réalisation d’œuvres communes que chacun peut augmenter pour l’avantage de tous.
-
-C’est la raison essentielle de la Licence Art Libre : promouvoir et protéger ces productions de l’esprit selon les principes du copyleft : liberté d’usage, de copie, de diffusion, de transformation et interdiction d’appropriation exclusive.
-
-Définitions :
-
-Nous désignons par « œuvre », autant l’œuvre initiale, les œuvres conséquentes, que l’œuvre commune telles que définies ci-après :
-
-L’œuvre commune :Il s’agit d’une œuvre qui comprend l’œuvre initiale ainsi que toutes les contributions postérieures (les originaux conséquents et les copies). Elle est créée à l’initiative de l’auteur initial qui par cette licence définit les conditions selon lesquelles les contributions sont faites.
-
-L’œuvre initiale :C’est-à-dire l’œuvre créée par l’initiateur de l’œuvre commune dont les copies vont être modifiées par qui le souhaite.
-
-Les œuvres conséquentes :C’est-à-dire les contributions des auteurs qui participent à la formation de l’œuvre commune en faisant usage des droits de reproduction, de diffusion et de modification que leur confère la licence.
-
-Originaux (sources ou ressources de l’œuvre) :Chaque exemplaire daté de l’œuvre initiale ou conséquente que leurs auteurs présentent comme référence pour toutes actualisations, interprétations, copies ou reproductions ultérieures.
-
-Copie :Toute reproduction d’un original au sens de cette licence.
-
-1- OBJET.
-Cette licence a pour objet de définir les conditions selon lesquelles vous pouvez jouir librement de l’œuvre.
-
-2. L’ÉTENDUE DE LA JOUISSANCE.
-Cette œuvre est soumise au droit d’auteur, et l’auteur par cette licence vous indique quelles sont vos libertés pour la copier, la diffuser et la modifier.
-
-2.1 LA LIBERTÉ DE COPIER (OU DE REPRODUCTION).
-Vous avez la liberté de copier cette œuvre pour vous, vos amis ou toute autre personne, quelle que soit la technique employée.
-
-2.2 LA LIBERTÉ DE DIFFUSER (INTERPRÉTER, REPRÉSENTER, DISTRIBUER).
-Vous pouvez diffuser librement les copies de ces œuvres, modifiées ou non, quel que soit le support, quel que soit le lieu, à titre onéreux ou gratuit, si vous respectez toutes les conditions suivantes :
-
- 1. joindre aux copies cette licence à l’identique ou indiquer précisément où se trouve la licence ;
- 2. indiquer au destinataire le nom de chaque auteur des originaux, y compris le vôtre si vous avez modifié l’œuvre ;
- 3. indiquer au destinataire où il pourrait avoir accès aux originaux (initiaux et/ou conséquents).
-
-Les auteurs des originaux pourront, s’ils le souhaitent, vous autoriser à diffuser l’original dans les mêmes conditions que les copies.
-
-2.3 LA LIBERTÉ DE MODIFIER.
-Vous avez la liberté de modifier les copies des originaux (initiaux et conséquents) dans le respect des conditions suivantes :
-
- 1. celles prévues à l’article 2.2 en cas de diffusion de la copie modifiée ;
- 2. indiquer qu’il s’agit d’une œuvre modifiée et, si possible, la nature de la modification ;
- 3. diffuser cette œuvre conséquente avec la même licence ou avec toute licence compatible ;
- 4. Les auteurs des originaux pourront, s’ils le souhaitent, vous autoriser à modifier l’original dans les mêmes conditions que les copies.
-
-3. DROITS CONNEXES.
-Les actes donnant lieu à des droits d’auteur ou des droits voisins ne doivent pas constituer un obstacle aux libertés conférées par cette licence. C’est pourquoi, par exemple, les interprétations doivent être soumises à la même licence ou une licence compatible. De même, l’intégration de l’œuvre à une base de données, une compilation ou une anthologie ne doit pas faire obstacle à la jouissance de l’œuvre telle que définie par cette licence.
-
-4. L’ INTÉGRATION DE L’ŒUVRE.
-Toute intégration de cette œuvre à un ensemble non soumis à la LAL doit assurer l’exercice des libertés conférées par cette licence.
-Si l’œuvre n’est plus accessible indépendamment de l’ensemble, alors l’intégration n’est possible qu’à condition que l’ensemble soit soumis à la LAL ou une licence compatible.
-
-5. CRITÈRES DE COMPATIBILITÉ.
-Une licence est compatible avec la LAL si et seulement si :
-
- 1. elle accorde l’autorisation de copier, diffuser et modifier des copies de l’œuvre, y compris à des fins lucratives, et sans autres restrictions que celles qu’impose le respect des autres critères de compatibilité ;
- 2. elle garantit la paternité de l’œuvre et l’accès aux versions antérieures de l’œuvre quand cet accès est possible ;
- 3. elle reconnaît la LAL également compatible (réciprocité) ;
- 4. elle impose que les modifications faites sur l’œuvre soient soumises à la même licence ou encore à une licence répondant aux critères de compatibilité posés par la LAL.
-
-6. VOS DROITS INTELLECTUELS.
-La LAL n’a pas pour objet de nier vos droits d’auteur sur votre contribution ni vos droits connexes. En choisissant de contribuer à l’évolution de cette œuvre commune, vous acceptez seulement d’offrir aux autres les mêmes autorisations sur votre contribution que celles qui vous ont été accordées par cette licence. Ces autorisations n’entraînent pas un dessaisissement de vos droits intellectuels.
-
-7. VOS RESPONSABILITÉS.
-La liberté de jouir de l’œuvre tel que permis par la LAL (liberté de copier, diffuser, modifier) implique pour chacun la responsabilité de ses propres faits.
-
-8. LA DURÉE DE LA LICENCE.
-Cette licence prend effet dès votre acceptation de ses dispositions. Le fait de copier, de diffuser, ou de modifier l’œuvre constitue une acceptation tacite.
-Cette licence a pour durée la durée des droits d’auteur attachés à l’œuvre. Si vous ne respectez pas les termes de cette licence, vous perdez automatiquement les droits qu’elle vous confère. Si le régime juridique auquel vous êtes soumis ne vous permet pas de respecter les termes de cette licence, vous ne pouvez pas vous prévaloir des libertés qu’elle confère.
-
-9. LES DIFFÉRENTES VERSIONS DE LA LICENCE.
-Cette licence pourra être modifiée régulièrement, en vue de son amélioration, par ses auteurs (les acteurs du mouvement Copyleft Attitude) sous la forme de nouvelles versions numérotées.
-Vous avez toujours le choix entre vous contenter des dispositions contenues dans la version de la LAL sous laquelle la copie vous a été communiquée ou alors, vous prévaloir des dispositions d’une des versions ultérieures.
-
-10. LES SOUS-LICENCES.
-Les sous-licences ne sont pas autorisées par la présente. Toute personne qui souhaite bénéficier des libertés qu’elle confère sera liée directement aux auteurs de l’œuvre commune.
-
-11. LE CONTEXTE JURIDIQUE.
-Cette licence est rédigée en référence au droit français et à la Convention de Berne relative au droit d’auteur.
diff --git a/options/license/LGPL-2.0-only b/options/license/LGPL-2.0-only
deleted file mode 100644
index 843b00b561..0000000000
--- a/options/license/LGPL-2.0-only
+++ /dev/null
@@ -1,175 +0,0 @@
-GNU LIBRARY GENERAL PUBLIC LICENSE
-
-Version 2, June 1991
-
-Copyright (C) 1991 Free Software Foundation, Inc.
-51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-[This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.]
-
-Preamble
-
-The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users.
-
-This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too.
-
-When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
-
-To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it.
-
-For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights.
-
-Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library.
-
-Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations.
-
-Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
-
-Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license.
-
-The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such.
-
-Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better.
-
-However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries.
-
-The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library.
-
-Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one.
-
-GNU LIBRARY GENERAL PUBLIC LICENSE
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you".
-
-A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.
-
-The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".)
-
-"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library.
-
-Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does.
-
-1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library.
-
-You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
-
-2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
-
- a) The modified work must itself be a software library.
-
- b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.
-
- c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.
-
- d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful.
-
-(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
-
-3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices.
-
-Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.
-
-This option is useful when you wish to copy part of the code of the Library into a program that is not a library.
-
-4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange.
-
-If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.
-
-5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.
-
-However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.
-
-When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law.
-
-If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.)
-
-Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.
-
-6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.
-
-You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:
-
- a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.)
-
- b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.
-
- c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.
-
- d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.
-
-For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
-
-It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.
-
-7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things:
-
- a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above.
-
- b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.
-
-8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.
-
-10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
-
-11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
-
-This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
-
-12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
-
-13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.
-
-14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
-
-NO WARRANTY
-
-15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-END OF TERMS AND CONDITIONS
-
-How to Apply These Terms to Your New Libraries
-
-If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License).
-
-To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
-
- one line to give the library's name and an idea of what it does.
- Copyright (C) year name of author
-
- This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names:
-
-Yoyodyne, Inc., hereby disclaims all copyright interest in
-the library `Frob' (a library for tweaking knobs) written
-by James Random Hacker.
-
-signature of Ty Coon, 1 April 1990
-Ty Coon, President of Vice
-
-That's all there is to it!
diff --git a/options/license/LGPL-2.0-or-later b/options/license/LGPL-2.0-or-later
deleted file mode 100644
index 843b00b561..0000000000
--- a/options/license/LGPL-2.0-or-later
+++ /dev/null
@@ -1,175 +0,0 @@
-GNU LIBRARY GENERAL PUBLIC LICENSE
-
-Version 2, June 1991
-
-Copyright (C) 1991 Free Software Foundation, Inc.
-51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-[This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.]
-
-Preamble
-
-The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users.
-
-This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too.
-
-When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
-
-To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it.
-
-For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights.
-
-Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library.
-
-Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations.
-
-Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
-
-Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license.
-
-The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such.
-
-Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better.
-
-However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries.
-
-The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library.
-
-Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one.
-
-GNU LIBRARY GENERAL PUBLIC LICENSE
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you".
-
-A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.
-
-The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".)
-
-"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library.
-
-Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does.
-
-1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library.
-
-You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
-
-2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
-
- a) The modified work must itself be a software library.
-
- b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.
-
- c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.
-
- d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful.
-
-(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
-
-3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices.
-
-Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.
-
-This option is useful when you wish to copy part of the code of the Library into a program that is not a library.
-
-4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange.
-
-If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.
-
-5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.
-
-However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.
-
-When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law.
-
-If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.)
-
-Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.
-
-6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.
-
-You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:
-
- a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.)
-
- b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.
-
- c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.
-
- d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.
-
-For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
-
-It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.
-
-7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things:
-
- a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above.
-
- b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.
-
-8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.
-
-10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
-
-11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
-
-This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
-
-12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
-
-13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.
-
-14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
-
-NO WARRANTY
-
-15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-END OF TERMS AND CONDITIONS
-
-How to Apply These Terms to Your New Libraries
-
-If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License).
-
-To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
-
- one line to give the library's name and an idea of what it does.
- Copyright (C) year name of author
-
- This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names:
-
-Yoyodyne, Inc., hereby disclaims all copyright interest in
-the library `Frob' (a library for tweaking knobs) written
-by James Random Hacker.
-
-signature of Ty Coon, 1 April 1990
-Ty Coon, President of Vice
-
-That's all there is to it!
diff --git a/options/license/LGPL-2.1-only b/options/license/LGPL-2.1
index c6487f4fdf..c6487f4fdf 100644
--- a/options/license/LGPL-2.1-only
+++ b/options/license/LGPL-2.1
diff --git a/options/license/LGPL-2.1-or-later b/options/license/LGPL-2.1-or-later
deleted file mode 100644
index c6487f4fdf..0000000000
--- a/options/license/LGPL-2.1-or-later
+++ /dev/null
@@ -1,176 +0,0 @@
-GNU LESSER GENERAL PUBLIC LICENSE
-
-Version 2.1, February 1999
-
-Copyright (C) 1991, 1999 Free Software Foundation, Inc.
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.]
-
-Preamble
-
-The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users.
-
-This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below.
-
-When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things.
-
-To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it.
-
-For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights.
-
-We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library.
-
-To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others.
-
-Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license.
-
-Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs.
-
-When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library.
-
-We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances.
-
-For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License.
-
-In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system.
-
-Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library.
-
-The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run.
-
-GNU LESSER GENERAL PUBLIC LICENSE
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you".
-
-A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.
-
-The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".)
-
-"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library.
-
-Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does.
-
-1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library.
-
-You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
-
-2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
-
- a) The modified work must itself be a software library.
-
- b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.
-
- c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.
-
- d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful.
-
-(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
-
-3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices.
-
-Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.
-
-This option is useful when you wish to copy part of the code of the Library into a program that is not a library.
-
-4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange.
-
-If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.
-
-5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.
-
-However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.
-
-When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law.
-
-If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.)
-
-Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.
-
-6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.
-
-You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:
-
- a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.)
-
- b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with.
-
- c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.
-
- d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.
-
- e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.
-
-For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
-
-It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.
-
-7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things:
-
- a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above.
-
- b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.
-
-8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.
-
-10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License.
-
-11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
-
-This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
-
-12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
-
-13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.
-
-14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
-
-NO WARRANTY
-
-15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-END OF TERMS AND CONDITIONS
-
-How to Apply These Terms to Your New Libraries
-
-If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License).
-
-To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
-
- one line to give the library's name and an idea of what it does.
- Copyright (C) year name of author
-
- This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names:
-
-Yoyodyne, Inc., hereby disclaims all copyright interest in
-the library `Frob' (a library for tweaking knobs) written
-by James Random Hacker.
-
-signature of Ty Coon, 1 April 1990
-Ty Coon, President of Vice
-That's all there is to it!
diff --git a/options/license/LGPL-3.0-only b/options/license/LGPL-3.0
index 513d1c01fe..513d1c01fe 100644
--- a/options/license/LGPL-3.0-only
+++ b/options/license/LGPL-3.0
diff --git a/options/license/LGPL-3.0-linking-exception b/options/license/LGPL-3.0-linking-exception
deleted file mode 100644
index 186456fb0e..0000000000
--- a/options/license/LGPL-3.0-linking-exception
+++ /dev/null
@@ -1,16 +0,0 @@
-As a special exception to the GNU Lesser General Public License version 3
-("LGPL3"), the copyright holders of this Library give you permission to
-convey to a third party a Combined Work that links statically or dynamically
-to this Library without providing any Minimal Corresponding Source or
-Minimal Application Code as set out in 4d or providing the installation
-information set out in section 4e, provided that you comply with the other
-provisions of LGPL3 and provided that you meet, for the Application the
-terms and conditions of the license(s) which apply to the Application.
-
-Except as stated in this special exception, the provisions of LGPL3 will
-continue to comply in full to this Library. If you modify this Library, you
-may apply this exception to your version of this Library, but you are not
-obliged to do so. If you do not wish to do so, delete this exception
-statement from your version. This exception does not (and cannot) modify any
-license terms which apply to the Application, with which you must still
-comply.
diff --git a/options/license/LGPL-3.0-or-later b/options/license/LGPL-3.0-or-later
deleted file mode 100644
index 513d1c01fe..0000000000
--- a/options/license/LGPL-3.0-or-later
+++ /dev/null
@@ -1,304 +0,0 @@
-GNU LESSER GENERAL PUBLIC LICENSE
-Version 3, 29 June 2007
-
-Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below.
-
-0. Additional Definitions.
-
-As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License.
-
-"The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below.
-
-An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library.
-
-A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version".
-
-The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version.
-
-The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work.
-
-1. Exception to Section 3 of the GNU GPL.
-You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL.
-
-2. Conveying Modified Versions.
-If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version:
-
- a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy.
-
-3. Object Code Incorporating Material from Library Header Files.
-The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license document.
-
-4. Combined Works.
-You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following:
-
- a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license document.
-
- c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version.
-
- e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.)
-
-5. Combined Libraries.
-You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.
-
-6. Revised Versions of the GNU Lesser General Public License.
-The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation.
-
-If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.
-
-GNU GENERAL PUBLIC LICENSE
-Version 3, 29 June 2007
-
-Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-
-Preamble
-
-The GNU General Public License is a free, copyleft license for software and other kinds of works.
-
-The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.
-
-To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.
-
-For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
-
-Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.
-
-For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.
-
-Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.
-
-Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.
-
-The precise terms and conditions for copying, distribution and modification follow.
-
-TERMS AND CONDITIONS
-
-0. Definitions.
-
-“This License†refers to version 3 of the GNU General Public License.
-
-“Copyright†also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
-
-“The Program†refers to any copyrightable work licensed under this License. Each licensee is addressed as “youâ€. “Licensees†and “recipients†may be individuals or organizations.
-
-To “modify†a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version†of the earlier work or a work “based on†the earlier work.
-
-A “covered work†means either the unmodified Program or a work based on the Program.
-
-To “propagate†a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.
-
-To “convey†a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.
-
-An interactive user interface displays “Appropriate Legal Notices†to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
-
-1. Source Code.
-The “source code†for a work means the preferred form of the work for making modifications to it. “Object code†means any non-source form of a work.
-
-A “Standard Interface†means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.
-
-The “System Libraries†of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Componentâ€, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.
-
-The “Corresponding Source†for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.
-
-The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
-
-The Corresponding Source for a work in source code form is that same work.
-
-2. Basic Permissions.
-All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
-
-You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
-
-Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
-
-3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.
-
-When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.
-
-4. Conveying Verbatim Copies.
-You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.
-
-You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
-
-5. Conveying Modified Source Versions.
-You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all noticesâ€.
-
- c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
-
-A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate†if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.
-
-6. Conveying Non-Source Forms.
-You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
-
- d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
-
-A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.
-
-A “User Product†is either (1) a “consumer productâ€, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used†refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.
-
-“Installation Information†for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.
-
-If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).
-
-The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.
-
-Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.
-
-7. Additional Terms.
-“Additional permissions†are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.
-
-When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.
-
-Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
-
-All other non-permissive additional terms are considered “further restrictions†within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.
-
-If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
-
-Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.
-
-8. Termination.
-You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).
-
-However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
-
-Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.
-
-9. Acceptance Not Required for Having Copies.
-You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.
-
-10. Automatic Licensing of Downstream Recipients.
-Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.
-
-An “entity transaction†is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.
-
-You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
-
-11. Patents.
-A “contributor†is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor versionâ€.
-
-A contributor's “essential patent claims†are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control†includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
-
-Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.
-
-In the following three paragraphs, a “patent license†is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant†such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.
-
-If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying†means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.
-
-If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.
-
-A patent license is “discriminatory†if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
-
-Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.
-
-12. No Surrender of Others' Freedom.
-If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
-
-13. Use with the GNU Affero General Public License.
-Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.
-
-14. Revised Versions of this License.
-The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version†applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.
-
-If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.
-
-Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.
-
-15. Disclaimer of Warranty.
-THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS†WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-16. Limitation of Liability.
-IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-17. Interpretation of Sections 15 and 16.
-If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
-
-END OF TERMS AND CONDITIONS
-
-How to Apply These Terms to Your New Programs
-
-If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
-
-To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright†line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
-
- <program> Copyright (C) <year> <name of author>
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about boxâ€.
-
-You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer†for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>.
-
-The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/options/license/LGPLLR b/options/license/LGPLLR
deleted file mode 100644
index 73d8040e98..0000000000
--- a/options/license/LGPLLR
+++ /dev/null
@@ -1,89 +0,0 @@
-Lesser General Public License For Linguistic Resources
-
-Preamble
-
-The licenses for most data are designed to take away your freedom to share and change it. By contrast, this License is intended to guarantee your freedom to share and change free data--to make sure the data are free for all their users.
-
-This License, the Lesser General Public License for Linguistic Resources, applies to some specially designated linguistic resources -- typically lexicons and grammars.
-
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-0. This License Agreement applies to any Linguistic Resource which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License for Linguistic Resources (also called "this License"). Each licensee is addressed as "you".
-
-A "linguistic resource" means a collection of data about language prepared so as to be used with application programs.
-
-The "Linguistic Resource", below, refers to any such work which has been distributed under these terms. A "work based on the Linguistic Resource" means either the Linguistic Resource or any derivative work under copyright law: that is to say, a work containing the Linguistic Resource or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".)
-
-"Legible form" for a linguistic resource means the preferred form of the resource for making modifications to it.
-
-Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Linguistic Resource is not restricted, and output from such a program is covered only if its contents constitute a work based on the Linguistic Resource (independent of the use of the Linguistic Resource in a tool for writing it). Whether that is true depends on what the program that uses the Linguistic Resource does.
-
-1. You may copy and distribute verbatim copies of the Linguistic Resource as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Linguistic Resource.
-
-You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
-
-2. You may modify your copy or copies of the Linguistic Resource or any portion of it, thus forming a work based on the Linguistic Resource, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
-
- a) The modified work must itself be a linguistic resource.
-
- b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.
-
- c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.
-
-These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Linguistic Resource, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Linguistic Resource, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Linguistic Resource.
-
-In addition, mere aggregation of another work not based on the Linguistic Resource with the Linguistic Resource (or with a work based on the Linguistic Resource) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
-
-3. A program that contains no derivative of any portion of the Linguistic Resource, but is designed to work with the Linguistic Resource (or an encrypted form of the Linguistic Resource) by reading it or being compiled or linked with it, is called a "work that uses the Linguistic Resource". Such a work, in isolation, is not a derivative work of the Linguistic Resource, and therefore falls outside the scope of this License.
-
-However, combining a "work that uses the Linguistic Resource" with the Linguistic Resource (or an encrypted form of the Linguistic Resource) creates a package that is a derivative of the Linguistic Resource (because it contains portions of the Linguistic Resource), rather than a "work that uses the Linguistic Resource". If the package is a derivative of the Linguistic Resource, you may distribute the package under the terms of Section 4. Any works containing that package also fall under Section 4.
-
-4. As an exception to the Sections above, you may also combine a "work that uses the Linguistic Resource" with the Linguistic Resource (or an encrypted form of the Linguistic Resource) to produce a package containing portions of the Linguistic Resource, and distribute that package under terms of your choice, provided that the terms permit modification of the package for the customer's own use and reverse engineering for debugging such modifications.
-
-You must give prominent notice with each copy of the package that the Linguistic Resource is used in it and that the Linguistic Resource and its use are covered by this License. You must supply a copy of this License. If the package during execution displays copyright notices, you must include the copyright notice for the Linguistic Resource among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:
-
- a) Accompany the package with the complete corresponding machine-readable legible form of the Linguistic Resource including whatever changes were used in the package (which must be distributed under Sections 1 and 2 above); and, if the package contains an encrypted form of the Linguistic Resource, with the complete machine-readable "work that uses the Linguistic Resource", as object code and/or source code, so that the user can modify the Linguistic Resource and then encrypt it to produce a modified package containing the modified Linguistic Resource.
-
- b) Use a suitable mechanism for combining with the Linguistic Resource. A suitable mechanism is one that will operate properly with a modified version of the Linguistic Resource, if the user installs one, as long as the modified version is interface-compatible with the version that the package was made with.
-
- c) Accompany the package with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 4a, above, for a charge no more than the cost of performing this distribution.
-
- d) If distribution of the package is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.
-
- e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.
-
-If the package includes an encrypted form of the Linguistic Resource, the required form of the "work that uses the Linguistic Resource" must include any data and utility programs needed for reproducing the package from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
-
-It may happen that this requirement contradicts the license restrictions of proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Linguistic Resource together in a package that you distribute.
-
-5. You may not copy, modify, sublicense, link with, or distribute the Linguistic Resource except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Linguistic Resource is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
-
-6. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Linguistic Resource or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Linguistic Resource (or any work based on the Linguistic Resource), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Linguistic Resource or works based on it.
-
-7. Each time you redistribute the Linguistic Resource (or any work based on the Linguistic Resource), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Linguistic Resource subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License.
-
-8. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Linguistic Resource at all. For example, if a patent license would not permit royalty-free redistribution of the Linguistic Resource by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Linguistic Resource.
-
-If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free resource distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of data distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute resources through any other system and a licensee cannot impose that choice.
-
-This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
-
-9. If the distribution and/or use of the Linguistic Resource is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Linguistic Resource under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
-
-10. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License for Linguistic Resources from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Linguistic Resource specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Linguistic Resource does not specify a license version number, you may choose any version ever published by the Free Software Foundation.
-
-11. If you wish to incorporate parts of the Linguistic Resource into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission.
-
- NO WARRANTY
-
-12. BECAUSE THE LINGUISTIC RESOURCE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LINGUISTIC RESOURCE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LINGUISTIC RESOURCE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LINGUISTIC RESOURCE IS WITH YOU. SHOULD THE LINGUISTIC RESOURCE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-13. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LINGUISTIC RESOURCE AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LINGUISTIC RESOURCE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LINGUISTIC RESOURCE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
diff --git a/options/license/LLGPL b/options/license/LLGPL
deleted file mode 100644
index 889d0b92e5..0000000000
--- a/options/license/LLGPL
+++ /dev/null
@@ -1,56 +0,0 @@
-Preamble to the Gnu Lesser General Public License
-
-Copyright (c) 2016 Franz Inc., Berkeley, CA 94704
-
-The concept of the GNU Lesser General Public License version 2.1 ("LGPL")
-has been adopted to govern the use and distribution of above-mentioned
-application. However, the LGPL uses terminology that is more appropriate
-for a program written in C than one written in Lisp. Nevertheless, the
-LGPL can still be applied to a Lisp program if certain clarifications
-are made. This document details those clarifications. Accordingly, the
-license for the open-source Lisp applications consists of this document
-plus the LGPL. Wherever there is a conflict between this document and
-the LGPL, this document takes precedence over the LGPL.
-
-A "Library" in Lisp is a collection of Lisp functions, data and foreign
-modules. The form of the Library can be Lisp source code (for processing
-by an interpreter) or object code (usually the result of compilation of
-source code or built with some other mechanisms). Foreign modules are
-object code in a form that can be linked into a Lisp executable. When
-we speak of functions we do so in the most general way to include, in
-addition, methods and unnamed functions. Lisp "data" is also a general
-term that includes the data structures resulting from defining Lisp
-classes. A Lisp application may include the same set of Lisp objects
-as does a Library, but this does not mean that the application is
-necessarily a "work based on the Library" it contains.
-
-The Library consists of everything in the distribution file set before
-any modifications are made to the files. If any of the functions or
-classes in the Library are redefined in other files, then those
-redefinitions ARE considered a work based on the Library. If additional
-methods are added to generic functions in the Library, those additional
-methods are NOT considered a work based on the Library. If Library classes
-are subclassed, these subclasses are NOT considered a work based on the Library.
-If the Library is modified to explicitly call other functions that are neither
-part of Lisp itself nor an available add-on module to Lisp, then the functions
-called by the modified Library ARE considered a work based on the Library.
-The goal is to ensure that the Library will compile and run without getting
-undefined function errors.
-
-It is permitted to add proprietary source code to the Library, but it must
-be done in a way such that the Library will still run without that proprietary
-code present. Section 5 of the LGPL distinguishes between the case of a
-library being dynamically linked at runtime and one being statically linked
-at build time. Section 5 of the LGPL states that the former results in an
-executable that is a "work that uses the Library." Section 5 of the LGPL
-states that the latter results in one that is a "derivative of the Library",
-which is therefore covered by the LGPL. Since Lisp only offers one choice,
-which is to link the Library into an executable at build time, we declare that,
-for the purpose applying the LGPL to the Library, an executable that results
-from linking a "work that uses the Library" with the Library is considered a
-"work that uses the Library" and is therefore NOT covered by the LGPL.
-
-Because of this declaration, section 6 of LGPL is not applicable to the Library.
-However, in connection with each distribution of this executable, you must also
-deliver, in accordance with the terms and conditions of the LGPL, the source code
-of Library (or your derivative thereof) that is incorporated into this executable.
diff --git a/options/license/LLVM-exception b/options/license/LLVM-exception
deleted file mode 100644
index fa4b725a0e..0000000000
--- a/options/license/LLVM-exception
+++ /dev/null
@@ -1,15 +0,0 @@
----- LLVM Exceptions to the Apache 2.0 License ----
-
- As an exception, if, as a result of your compiling your source code, portions
- of this Software are embedded into an Object form of such source code, you
- may redistribute such embedded portions in such Object form without complying
- with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
-
- In addition, if you combine or link compiled forms of this Software with
- software that is licensed under the GPLv2 ("Combined Software") and if a
- court of competent jurisdiction determines that the patent provision (Section
- 3), the indemnity provision (Section 9) or other Section of the License
- conflicts with the conditions of the GPLv2, you may retroactively and
- prospectively choose to deem waived or otherwise exclude such Section(s) of
- the License, but only in their entirety and only with respect to the Combined
- Software.
diff --git a/options/license/LOOP b/options/license/LOOP
deleted file mode 100644
index 434d2c45e2..0000000000
--- a/options/license/LOOP
+++ /dev/null
@@ -1,44 +0,0 @@
-Portions of LOOP are Copyright (c) 1986 by the Massachusetts Institute of Technology.
-All Rights Reserved.
-
-Permission to use, copy, modify and distribute this software and its
-documentation for any purpose and without fee is hereby granted,
-provided that the M.I.T. copyright notice appear in all copies and that
-both that copyright notice and this permission notice appear in
-supporting documentation. The names "M.I.T." and "Massachusetts
-Institute of Technology" may not be used in advertising or publicity
-pertaining to distribution of the software without specific, written
-prior permission. Notice must be given in supporting documentation that
-copying distribution is by permission of M.I.T. M.I.T. makes no
-representations about the suitability of this software for any purpose.
-It is provided "as is" without express or implied warranty.
-
-Massachusetts Institute of Technology
-77 Massachusetts Avenue
-Cambridge, Massachusetts 02139
-United States of America
-+1-617-253-1000
-
-Portions of LOOP are Copyright (c) 1989, 1990, 1991, 1992 by Symbolics, Inc.
-All Rights Reserved.
-
-Permission to use, copy, modify and distribute this software and its
-documentation for any purpose and without fee is hereby granted,
-provided that the Symbolics copyright notice appear in all copies and
-that both that copyright notice and this permission notice appear in
-supporting documentation. The name "Symbolics" may not be used in
-advertising or publicity pertaining to distribution of the software
-without specific, written prior permission. Notice must be given in
-supporting documentation that copying distribution is by permission of
-Symbolics. Symbolics makes no representations about the suitability of
-this software for any purpose. It is provided "as is" without express
-or implied warranty.
-
-Symbolics, CLOE Runtime, and Minima are trademarks, and CLOE, Genera,
-and Zetalisp are registered trademarks of Symbolics, Inc.
-
-Symbolics, Inc.
-8 New England Executive Park, East
-Burlington, Massachusetts 01803
-United States of America
-+1-617-221-1000
diff --git a/options/license/LPD-document b/options/license/LPD-document
deleted file mode 100644
index 0b46392e2f..0000000000
--- a/options/license/LPD-document
+++ /dev/null
@@ -1,8 +0,0 @@
-Copyright (c) 1996 L. Peter Deutsch
-
-Permission is granted to copy and distribute this
-document for any purpose and without charge, including
-translations into other languages and incorporation
-into compilations, provided that the copyright notice
-and this notice are preserved, and that any substantive
-changes or deletions from the original are clearly marked.
diff --git a/options/license/LPL-1.0 b/options/license/LPL-1.0
deleted file mode 100644
index 8546bc2a2d..0000000000
--- a/options/license/LPL-1.0
+++ /dev/null
@@ -1,81 +0,0 @@
-Lucent Public License Version 1.0
-
-THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
-
-1. DEFINITIONS
-
-"Contribution" means:
-
- a. in the case of <ORGANIZATION> ("<OWNER>"), the Original Program, and
- b. in the case of each Contributor,
-
- i. changes to the Program, and
- ii. additions to the Program; where such changes and/or additions to the Program originate from and are "Contributed" by that particular Contributor.
-
- A Contribution is "Contributed" by a Contributor only (i) if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf, and (ii) the Contributor explicitly consents, in accordance with Section 3C, to characterization of the changes and/or additions as Contributions. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
-
-"Contributor" means <OWNER> and any other entity that has Contributed a Contribution to the Program.
-
-"Distributor" means a Recipient that distributes the Program, modifications to the Program, or any part thereof.
-
-"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
-
-"Original Program" means the original version of the software accompanying this Agreement as released by <OWNER>, including source code, object code and documentation, if any.
-
-"Program" means the Original Program and Contributions or any part thereof
-
-"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
-
-2. GRANT OF RIGHTS
-
- a. Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
-
- b. Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. The patent license granted by a Contributor shall also apply to the combination of the Contribution of that Contributor and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license granted by a Contributor shall not apply to (i) any other combinations which include the Contribution, nor to (ii) Contributions of other Contributors. No hardware per se is licensed hereunder.
-
- c. Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
-
- d. Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
-
-3. REQUIREMENTS
-
- A. Distributor may choose to distribute the Program in any form under this Agreement or under its own license agreement, provided that:
-
- 1. it complies with the terms and conditions of this Agreement;
- 2. if the Program is distributed in source code or other tangible form, a copy of this Agreement or Distributor's own license agreement is included with each copy of the Program; and
- 3. if distributed under Distributor's own license agreement, such license agreement:
-
- a. effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
- b. effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; and
- c. states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party.
-
- B. Each Distributor must include the following in a conspicuous location in the Program:
-
- Copyright (C) <YEAR>, <ORGANIZATION> and others. All Rights Reserved.
-
- C. In addition, each Contributor must identify itself as the originator of its Contribution, if any, and indicate its consent to characterization of its additions and/or changes as a Contribution, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. Once consent is granted, it may not thereafter be revoked.
-
-4. COMMERCIAL DISTRIBUTION
-
-Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Distributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for Contributors. Therefore, if a Distributor includes the Program in a commercial product offering, such Distributor ("Commercial Distributor") hereby agrees to defend and indemnify every Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Distributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Distributor in writing of such claim, and b) allow the Commercial Distributor to control, and cooperate with the Commercial Distributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
-
-For example, a Distributor might include the Program in a commercial product offering, Product X. That Distributor is then a Commercial Distributor. If that Commercial Distributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Distributor's responsibility alone. Under this section, the Commercial Distributor would have to defend claims against the Contributors related to those performance claims and warranties, and if a court requires any Contributor to pay any damages as a result, the Commercial Distributor must pay those damages.
-
-5. NO WARRANTY
-
-EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
-
-6. DISCLAIMER OF LIABILITY
-
-EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. GENERAL
-
-If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
-If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
-
-All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
-
-<OWNER> may publish new versions (including revisions) of this Agreement from time to time. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. No one other than <OWNER> has the right to modify this Agreement. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
-
-This Agreement is governed by the laws of the State of <STATE> and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
diff --git a/options/license/LPL-1.02 b/options/license/LPL-1.02
deleted file mode 100644
index 4b3bbf7f1c..0000000000
--- a/options/license/LPL-1.02
+++ /dev/null
@@ -1,85 +0,0 @@
-Lucent Public License Version 1.02
-
-THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
-
-1. DEFINITIONS
-
-"Contribution" means:
-
- a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original Program, and
- b. in the case of each Contributor,
-
- i. changes to the Program, and
- ii. additions to the Program;
-
- where such changes and/or additions to the Program were added to the Program by such Contributor itself or anyone acting on such Contributor's behalf, and the Contributor explicitly consents, in accordance with Section 3C, to characterization of the changes and/or additions as Contributions.
-
-"Contributor" means LUCENT and any other entity that has Contributed a Contribution to the Program.
-
-"Distributor" means a Recipient that distributes the Program, modifications to the Program, or any part thereof.
-
-"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
-
-"Original Program" means the original version of the software accompanying this Agreement as released by LUCENT, including source code, object code and documentation, if any.
-
-"Program" means the Original Program and Contributions or any part thereof
-
-"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
-
-2. GRANT OF RIGHTS
-
- a. Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
-
- b. Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. The patent license granted by a Contributor shall also apply to the combination of the Contribution of that Contributor and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license granted by a Contributor shall not apply to (i) any other combinations which include the Contribution, nor to (ii) Contributions of other Contributors. No hardware per se is licensed hereunder.
-
- c. Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
-
- d. Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
-
-3. REQUIREMENTS
-
- A. Distributor may choose to distribute the Program in any form under this Agreement or under its own license agreement, provided that:
-
- 1. it complies with the terms and conditions of this Agreement;
- 2. if the Program is distributed in source code or other tangible form, a copy of this Agreement or Distributor's own license agreement is included with each copy of the Program; and
- 3. if distributed under Distributor's own license agreement, such license agreement:
-
- a. effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
- b. effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; and
- c. states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party.
-
- B. Each Distributor must include the following in a conspicuous location in the Program:
-
- Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights Reserved.
-
- C. In addition, each Contributor must identify itself as the originator of its Contribution in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. Also, each Contributor must agree that the additions and/or changes are intended to be a Contribution. Once a Contribution is contributed, it may not thereafter be revoked.
-
-4. COMMERCIAL DISTRIBUTION
-
-Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Distributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for Contributors. Therefore, if a Distributor includes the Program in a commercial product offering, such Distributor ("Commercial Distributor") hereby agrees to defend and indemnify every Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Distributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Distributor in writing of such claim, and b) allow the Commercial Distributor to control, and cooperate with the Commercial Distributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
-
-For example, a Distributor might include the Program in a commercial product offering, Product X. That Distributor is then a Commercial Distributor. If that Commercial Distributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Distributor's responsibility alone. Under this section, the Commercial Distributor would have to defend claims against the Contributors related to those performance claims and warranties, and if a court requires any Contributor to pay any damages as a result, the Commercial Distributor must pay those damages.
-
-5. NO WARRANTY
-
-EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
-
-6. DISCLAIMER OF LIABILITY
-
-EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. EXPORT CONTROL
-
-Recipient agrees that Recipient alone is responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries).
-
-8. GENERAL
-
-If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
-If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
-
-All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
-
-LUCENT may publish new versions (including revisions) of this Agreement from time to time. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. No one other than LUCENT has the right to modify this Agreement. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
-
-This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
diff --git a/options/license/LPPL-1.0 b/options/license/LPPL-1.0
deleted file mode 100644
index 1472b174ee..0000000000
--- a/options/license/LPPL-1.0
+++ /dev/null
@@ -1,103 +0,0 @@
-LaTeX Project Public License
-
-LPPL Version 1.0 1999-03-01
-
-Copyright 1999 LaTeX3 Project
-
-Everyone is permitted to copy and distribute verbatim copies of this license document, but modification is not allowed.
-
-Preamble
-
-The LaTeX Project Public License (LPPL) is the license under which the base LaTeX distribution is distributed. As described below you may use this licence for any software that you wish to distribute.
-
-It may be particularly suitable if your software is TeX related (such as a LaTeX package file) but it may be used for any software, even if it is unrelated to TeX.
-
-To use this license, the files of your distribution should have an explicit copyright notice giving your name and the year, together with a reference to this license.
-
-A typical example would be
-
- %% pig.sty %% Copyright 2001 M. Y. Name
-
- % This program can redistributed and/or modified under the terms
- % of the LaTeX Project Public License Distributed from CTAN
- % archives in directory macros/latex/base/lppl.txt; either
- % version 1 of the License, or (at your option) any later version.
-
-Given such a notice in the file, the conditions of this document would apply, with:
-
-`The Program' referring to the software `pig.sty' and `The Copyright Holder' referring to the person `M. Y. Name'.
-
-To see a real example, see the file legal.txt which carries the copyright notice for the base latex distribution.
-
-This license gives terms under which files of The Program may be distributed and modified. Individual files may have specific further constraints on modification, but no file should have restrictions on distribution other than those specified below.
-
-This is to ensure that a distributor wishing to distribute a complete unmodified copy of The Program need only check the conditions in this file, and does not need to check every file in The Program for extra restrictions. If you do need to modify the distribution terms of some files, do not refer to this license, instead distribute The Program under a different license. You may use the parts of the text of LPPL as a model for your own license, but your license should not directly refer to the LPPL or otherwise give the impression that The Program is distributed under the LPPL.
-
- The LaTeX Project Public License
-================================
-Terms And Conditions For Copying, Distribution And Modification
-===============================================================
-
-
-WARRANTY
-========
-
-There is no warranty for The Program, to the extent permitted by applicable law. Except when otherwise stated in writing, The Copyright Holder provides The Program `as is' without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the program is with you. Should The Program prove defective, you assume the cost of all necessary servicing, repair or correction.
-
-In no event unless required by applicable law or agreed to in writing will The Copyright Holder, or any of the individual authors named in the source for The Program, be liable to you for damages, including any general, special, incidental or consequential damages arising out of any use of The Program or out of inability to use The Program (including but not limited to loss of data or data being rendered inaccurate or losses sustained by you or by third parties as a result of a failure of The Program to operate with any other programs), even if such holder or other party has been advised of the possibility of such damages.
-
-
-DISTRIBUTION
-============
-
-Redistribution of unchanged files is allowed provided that all files that make up the distribution of The Program are distributed. In particular this means that The Program has to be distributed including its documentation if documentation was part of the original distribution.
-
-The distribution of The Program will contain a prominent file listing all the files covered by this license.
-
-If you receive only some of these files from someone, complain!
-
-The distribution of changed versions of certain files included in the The Program, and the reuse of code from The Program, are allowed under the following restrictions:
-
- * It is allowed only if the legal notice in the file does not expressly forbid it. See note below, under "Conditions on individual files".
-
- * You rename the file before you make any changes to it, unless the file explicitly says that renaming is not required. Any such changed files must be distributed under a license that forbids distribution of those files, and any files derived from them, under the names used by the original files in the distribution of The Program.
-
- * You change any `identification string' in The Program to clearly indicate that the file is not part of the standard system.
-
- * If The Program includes an `error report address' so that errors may be reported to The Copyright Holder, or other specified addresses, this address must be changed in any modified versions of The Program, so that reports for files not maintained by the original program maintainers are directed to the maintainers of the changed files.
-
- * You acknowledge the source and authorship of the original version in the modified file.
-
- * You also distribute the unmodified version of the file or alternatively provide sufficient information so that the user of your modified file can be reasonably expected to be able to obtain an original, unmodified copy of The Program. For example, you may specify a URL to a site that you expect will freely provide the user with a copy of The Program (either the version on which your modification is based, or perhaps a later version).
-
- * If The Program is intended to be used with, or is based on, LaTeX, then files with the following file extensions which have special meaning in LaTeX Software, have special modification rules under the license:
-
- - Files with extension `.ins' (installation files): these files may not be modified at all because they contain the legal notices that are placed in the generated files.
-
- - Files with extension `.fd' (LaTeX font definitions files): these files are allowed to be modified without changing the name, but only to enable use of all available fonts and to prevent attempts to access unavailable fonts. However, modified files are not allowed to be distributed in place of original files.
-
- - Files with extension `.cfg' (configuration files): these files can be created or modified to enable easy configuration of the system. The documentation in cfgguide.tex in the base LaTeX distribution describes when it makes sense to modify or generate such files.
-
-The above restrictions are not intended to prohibit, and hence do not apply to, the updating, by any method, of a file so that it becomes identical to the latest version of that file in The Program.
-
-========================================================================
-
-NOTES
-=====
-
-We believe that these requirements give you the freedom you to make modifications that conform with whatever technical specifications you wish, whilst maintaining the availability, integrity and reliability of The Program. If you do not see how to achieve your goal whilst adhering to these requirements then read the document cfgguide.tex in the base LaTeX distribution for suggestions.
-
-Because of the portability and exchangeability aspects of systems like LaTeX, The LaTeX3 Project deprecates the distribution of non-standard versions of components of LaTeX or of generally available contributed code for them but such distributions are permitted under the above restrictions.
-
-The document modguide.tex in the base LaTeX distribution details the reasons for the legal requirements detailed above. Even if The Program is unrelated to LaTeX, the argument in modguide.tex may still apply, and should be read before a modified version of The Program is distributed.
-
-Conditions on individual files
-==============================
-
-The individual files may bear additional conditions which supersede the general conditions on distribution and modification contained in this file. If there are any such files, the distribution of The Program will contain a prominent file that lists all the exceptional files.
-
-Typical examples of files with more restrictive modification conditions would be files that contain the text of copyright notices.
-
- * The conditions on individual files differ only in the extent of *modification* that is allowed.
-
- * The conditions on *distribution* are the same for all the files. Thus a (re)distributor of a complete, unchanged copy of The Program need meet only the conditions in this file; it is not necessary to check the header of every file in the distribution to check that a distribution meets these requirements.
diff --git a/options/license/LPPL-1.1 b/options/license/LPPL-1.1
deleted file mode 100644
index 010fdd5318..0000000000
--- a/options/license/LPPL-1.1
+++ /dev/null
@@ -1,141 +0,0 @@
-The LaTeX Project Public License
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-
-LPPL Version 1.1 1999-07-10
-
-Copyright 1999 LaTeX3 Project
-
-Everyone is allowed to distribute verbatim copies of this license document, but modification of it is not allowed.
-
-PREAMBLE
-========
-
-The LaTeX Project Public License (LPPL) is the license under which the base LaTeX distribution is distributed.
-
-You may use this license for any program that you have written and wish to distribute. This license may be particularly suitable if your program is TeX-related (such as a LaTeX package), but you may use it even if your program is unrelated to TeX. The section `WHETHER AND HOW TO DISTRIBUTE PROGRAMS UNDER THIS LICENSE', below, gives instructions, examples, and recommendations for authors who are considering distributing their programs under this license.
-
-In this license document, `The Program' refers to any program distributed under this license.
-
-This license gives conditions under which The Program may be distributed and conditions under which modified versions of The Program may be distributed. Individual files of The Program may bear supplementary and/or superseding conditions on modification of themselves and on the distribution of modified versions of themselves, but *no* file of The Program may bear supplementary or superseding conditions on the distribution of an unmodified copy of the file. A distributor wishing to distribute a complete, unmodified copy of The Program therefore needs to check the conditions only in this license and nowhere else.
-
-Activities other than distribution and/or modification of The Program are not covered by this license; they are outside its scope. In particular, the act of running The Program is not restricted.
-
-We, the LaTeX3 Project, believe that the conditions below give you the freedom to make and distribute modified versions of The Program that conform with whatever technical specifications you wish while maintaining the availability, integrity, and reliability of The Program. If you do not see how to achieve your goal while meeting these conditions, then read the document `cfgguide.tex' in the base LaTeX distribution for suggestions.
-
-CONDITIONS ON DISTRIBUTION AND MODIFICATION
-===========================================
-
-You may distribute a complete, unmodified copy of The Program. Distribution of only part of The Program is not allowed.
-
-You may not modify in any way a file of The Program that bears a legal notice forbidding modification of that file.
-
-You may distribute a modified file of The Program if, and only if, the following eight conditions are met:
-
- 1. You must meet any additional conditions borne by the file on the distribution of a modified version of the file as described below in the subsection `Additional Conditions on Individual Files of The Program'.
-
- 2. If the file is a LaTeX software file, then you must meet any applicable additional conditions on the distribution of a modified version of the file that are described below in the subsection `Additional Conditions on LaTeX Software Files'.
-
- 3. You must not distribute the modified file with the filename of the original file.
-
- 4. In the modified file, you must acknowledge the authorship and name of the original file, and the name (if any) of the program which contains it.
-
- 5. You must change any identification string in the file to indicate clearly that the modified file is not part of The Program.
-
- 6. You must change any addresses in the modified file for the reporting of errors in the file or in The Program generally to ensure that reports for files no longer maintained by the original maintainers will be directed to the maintainers of the modified files.
-
- 7. You must distribute the modified file under a license that forbids distribution both of the modified file and of any files derived from the modified file with the filename of the original file.
-
- 8. You must do either (A) or (B):
-
- (A) distribute a copy of The Program (that is, a complete, unmodified copy of The Program) together with the modified file; if your distribution of the modified file is made by offering access to copy the modified file from a designated place, then offering equivalent access to copy The Program from the same place meets this condition, even though third parties are not compelled to copy The Program along with the modified file;
-
- (B) provide to those who receive the modified file information that is sufficient for them to obtain a copy of The Program; for example, you may provide a Uniform Resource Locator (URL) for a site that you expect will provide them with a copy of The Program free of charge (either the version from which your modification is derived, or perhaps a later version).
-
-Note that in the above, `distribution' of a file means making the file available to others by any means. This includes, for instance, installing the file on any machine in such a way that the file is accessible by users other than yourself. `Modification' of a file means any procedure that produces a derivative file under any applicable law -- that is, a file containing the original file or a significant portion of it, either verbatim or with modifications and/or translated into another language.
-
-Changing the name of a file is considered to be a modification of the file.
-
-The distribution conditions in this license do not have to be applied to files that have been modified in accordance with the above conditions. Note, however, that Condition 7. does apply to any such modified file.
-
-The conditions above are not intended to prohibit, and hence do not apply to, the updating, by any method, of a file so that it becomes identical to the latest version of that file of The Program.
-
-A Recommendation on Modification Without Distribution
------------------------------------------------------
-
-It is wise never to modify a file of The Program, even for your own personal use, without also meeting the above eight conditions for distributing the modified file. While you might intend that such modified files will never be distributed, often this will happen by accident -- you may forget that you have modified the file; or it may not occur to you when allowing others to access the modified file that you are thus distributing it and violating the conditions of this license. It is usually in your best interest to keep your copy of The Program identical with the public one. Many programs provide ways to control the behavior of that program without altering its licensed files.
-
-Additional Conditions on Individual Files of The Program
---------------------------------------------------------
-
-An individual file of The Program may bear additional conditions that supplement and/or supersede the conditions in this license if, and only if, such additional conditions exclusively concern modification of the file or distribution of a modified version of the file. The conditions on individual files of The Program therefore may differ only with respect to the kind and extent of modification of those files that is allowed, and with respect to the distribution of modified versions of those files.
-
-Additional Conditions on LaTeX Software Files
----------------------------------------------
-
-If a file of The Program is intended to be used with LaTeX (that is, if it is a LaTeX software file), then the following additional conditions, which supplement and/or supersede the conditions above, apply to the file according to its filename extension:
-
- - You may not modify any file with filename extension `.ins' since these are installation files containing the legal notices that are placed in the files they generate.
-
- - You may distribute modified versions of files with filename extension `.fd' (LaTeX font definition files) under the standard conditions of the LPPL as described above. You may also distribute such modified LaTeX font definition files with their original names provided that:
- (1) the only changes to the original files either enable use of available fonts or prevent attempts to access unavailable fonts;
- (2) you also distribute the original, unmodified files (TeX input paths can be used to control which set of LaTeX font definition files is actually used by TeX).
-
- - You may distribute modified versions of files with filename extension `.cfg' (configuration files) with their original names. The Program may (and usually will) specify the range of commands that are allowed in a particular configuration file.
-
-Because of portability and exchangeability issues in LaTeX software, The LaTeX3 Project deprecates the distribution of modified versions of components of LaTeX or of generally available contributed code for them, but such distribution can meet the conditions of this license.
-
-NO WARRANTY
-===========
-
-There is no warranty for The Program. Except when otherwise stated in writing, The Copyright Holder provides The Program `as is', without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of The Program is with you. Should The Program prove defective, you assume the cost of all necessary servicing, repair, or correction.
-
-In no event unless agreed to in writing will The Copyright Holder, or any author named in the files of The Program, or any other party who may distribute and/or modify The Program as permitted below, be liable to you for damages, including any general, special, incidental or consequential damages arising out of any use of The Program or out of inability to use The Program (including, but not limited to, loss of data, data being rendered inaccurate, or losses sustained by anyone as a result of any failure of The Program to operate with any other programs), even if The Copyright Holder or said author or said other party has been advised of the possibility of such damages.
-
-WHETHER AND HOW TO DISTRIBUTE PROGRAMS UNDER THIS LICENSE
-=========================================================
-
-This section contains important instructions, examples, and recommendations for authors who are considering distributing their programs under this license. These authors are addressed as `you' in this section.
-
-Choosing This License or Another License
-----------------------------------------
-
-If for any part of your program you want or need to use *distribution* conditions that differ from those in this license, then do not refer to this license anywhere in your program but instead distribute your program under a different license. You may use the text of this license as a model for your own license, but your license should not refer to the LPPL or otherwise give the impression that your program is distributed under the LPPL.
-
-The document `modguide.tex' in the base LaTeX distribution explains the motivation behind the conditions of this license. It explains, for example, why distributing LaTeX under the GNU General Public License (GPL) was considered inappropriate. Even if your program is unrelated to LaTeX, the discussion in `modguide.tex' may still be relevant, and authors intending to distribute their programs under any license are encouraged to read it.
-
-How to Use This License
------------------------
-
-To use this license, place in each of the files of your program both an explicit copyright notice including your name and the year and also a statement that the distribution and/or modification of the file is constrained by the conditions in this license.
-
-Here is an example of such a notice and statement:
-
- %% pig.dtx
- %% Copyright 2001 M. Y. Name
- %
- % This program may be distributed and/or modified under the
- % conditions of the LaTeX Project Public License, either version 1.1
- % of this license or (at your option) any later version.
- % The latest version of this license is in % http://www.latex-project.org/lppl.txt
- % and version 1.1 or later is part of all distributions of LaTeX % version 1999/06/01 or later.
- %
- % This program consists of the files pig.dtx and pig.ins
-
-Given such a notice and statement in a file, the conditions given in this license document would apply, with `The Program' referring to the two files `pig.dtx' and `pig.ins', and `The Copyright Holder' referring to the person `M. Y. Name'.
-
-Important Recommendations
--------------------------
-
-Defining What Constitutes The Program
-
-The LPPL requires that distributions of The Program contain all the files of The Program. It is therefore important that you provide a way for the licensee to determine which files constitute The Program. This could, for example, be achieved by explicitly listing all the files of The Program near the copyright notice of each file or by using a line like
-
- % This program consists of all files listed in manifest.txt.
-
-in that place. In the absence of an unequivocal list it might be impossible for the licensee to determine what is considered by you to comprise The Program.
-
-Noting Exceptional Files
-
-If The Program contains any files bearing additional conditions on modification, or on distribution of modified versions, of those files (other than those listed in `Additional Conditions on LaTeX Software Files'), then it is recommended that The Program contain a prominent file that defines the exceptional conditions, and either lists the exceptional files or defines one or more categories of exceptional files.
-
-Files containing the text of a license (such as this file) are often examples of files bearing more restrictive conditions on modification. LaTeX configuration files (with filename extension `.cfg') are examples of files bearing less restrictive conditions on the distribution of a modified version of the file. The additional conditions on LaTeX software given above are examples of declaring a category of files bearing exceptional additional conditions.
diff --git a/options/license/LPPL-1.2 b/options/license/LPPL-1.2
deleted file mode 100644
index cdaf551ae9..0000000000
--- a/options/license/LPPL-1.2
+++ /dev/null
@@ -1,139 +0,0 @@
-The LaTeX Project Public License
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-
-LPPL Version 1.2 1999-09-03
-
-Copyright 1999 LaTeX3 Project
-
-Everyone is allowed to distribute verbatim copies of this license document, but modification of it is not allowed.
-
-PREAMBLE
-========
-
-The LaTeX Project Public License (LPPL) is the license under which the base LaTeX distribution is distributed.
-
-You may use this license for any program that you have written and wish to distribute. This license may be particularly suitable if your program is TeX-related (such as a LaTeX package), but you may use it even if your program is unrelated to TeX. The section `WHETHER AND HOW TO DISTRIBUTE PROGRAMS UNDER THIS LICENSE', below, gives instructions, examples, and recommendations for authors who are considering distributing their programs under this license.
-
-In this license document, `The Program' refers to any program distributed under this license.
-
-This license gives conditions under which The Program may be distributed and conditions under which modified versions of The Program may be distributed. Individual files of The Program may bear supplementary and/or superseding conditions on modification of themselves and on the distribution of modified versions of themselves, but *no* file of The Program may bear supplementary or superseding conditions on the distribution of an unmodified copy of the file. A distributor wishing to distribute a complete, unmodified copy of The Program therefore needs to check the conditions only in this license and nowhere else.
-
-Activities other than distribution and/or modification of The Program are not covered by this license; they are outside its scope. In particular, the act of running The Program is not restricted.
-
-We, the LaTeX3 Project, believe that the conditions below give you the freedom to make and distribute modified versions of The Program that conform with whatever technical specifications you wish while maintaining the availability, integrity, and reliability of The Program. If you do not see how to achieve your goal while meeting these conditions, then read the document `cfgguide.tex' in the base LaTeX distribution for suggestions.
-
-CONDITIONS ON DISTRIBUTION AND MODIFICATION
-===========================================
-
-You may distribute a complete, unmodified copy of The Program. Distribution of only part of The Program is not allowed.
-
-You may not modify in any way a file of The Program that bears a legal notice forbidding modification of that file.
-
-You may distribute a modified file of The Program if, and only if, the following eight conditions are met:
-
- 1. You must meet any additional conditions borne by the file on the distribution of a modified version of the file as described below in the subsection `Additional Conditions on Individual Files of The Program'.
-
- 2. If the file is a LaTeX software file, then you must meet any applicable additional conditions on the distribution of a modified version of the file that are described below in the subsection `Additional Conditions on LaTeX Software Files'.
-
- 3. You must not distribute the modified file with the filename of the original file.
-
- 4. In the modified file, you must acknowledge the authorship and name of the original file, and the name (if any) of the program which contains it.
-
- 5. You must change any identification string in the file to indicate clearly that the modified file is not part of The Program.
-
- 6. You must change any addresses in the modified file for the reporting of errors in the file or in The Program generally to ensure that reports for files no longer maintained by the original maintainers will be directed to the maintainers of the modified files.
-
- 7. You must distribute the modified file under a license that forbids distribution both of the modified file and of any files derived from the modified file with the filename of the original file.
-
- 8. You must do either (A) or (B):
-
- (A) distribute a copy of The Program (that is, a complete, unmodified copy of The Program) together with the modified file; if your distribution of the modified file is made by offering access to copy the modified file from a designated place, then offering equivalent access to copy The Program from the same place meets this condition, even though third parties are not compelled to copy The Program along with the modified file;
-
- (B) provide to those who receive the modified file information that is sufficient for them to obtain a copy of The Program; for example, you may provide a Uniform Resource Locator (URL) for a site that you expect will provide them with a copy of The Program free of charge (either the version from which your modification is derived, or perhaps a later version).
-
-Note that in the above, `distribution' of a file means making the file available to others by any means. This includes, for instance, installing the file on any machine in such a way that the file is accessible by users other than yourself. `Modification' of a file means any procedure that produces a derivative file under any applicable law -- that is, a file containing the original file or a significant portion of it, either verbatim or with modifications and/or translated into another language.
-
-Changing the name of a file (other than as necessitated by the file conventions of the target file systems) is considered to be a modification of the file.
-
-The distribution conditions in this license do not have to be applied to files that have been modified in accordance with the above conditions. Note, however, that Condition 7. does apply to any such modified file.
-
-The conditions above are not intended to prohibit, and hence do not apply to, the updating, by any method, of a file so that it becomes identical to the latest version of that file of The Program.
-
-
-A Recommendation on Modification Without Distribution -----------------------------------------------------
-
-It is wise never to modify a file of The Program, even for your own personal use, without also meeting the above eight conditions for distributing the modified file. While you might intend that such modified files will never be distributed, often this will happen by accident -- you may forget that you have modified the file; or it may not occur to you when allowing others to access the modified file that you are thus distributing it and violating the conditions of this license. It is usually in your best interest to keep your copy of The Program identical with the public one. Many programs provide ways to control the behavior of that program without altering its licensed files.
-
-Additional Conditions on Individual Files of The Program --------------------------------------------------------
-
-An individual file of The Program may bear additional conditions that supplement and/or supersede the conditions in this license if, and only if, such additional conditions exclusively concern modification of the file or distribution of a modified version of the file. The conditions on individual files of The Program therefore may differ only with respect to the kind and extent of modification of those files that is allowed, and with respect to the distribution of modified versions of those files.
-
-Additional Conditions on LaTeX Software Files
----------------------------------------------
-
-If a file of The Program is intended to be used with LaTeX (that is, if it is a LaTeX software file), then the following additional conditions, which supplement and/or supersede the conditions above, apply to the file according to its filename extension:
-
- - You may not modify any file with filename extension `.ins' since these are installation files containing the legal notices that are placed in the files they generate.
-
- - You may distribute modified versions of files with filename extension `.fd' (LaTeX font definition files) under the standard conditions of the LPPL as described above. You may also distribute such modified LaTeX font definition files with their original names provided that:
- (1) the only changes to the original files either enable use of available fonts or prevent attempts to access unavailable fonts;
- (2) you also distribute the original, unmodified files (TeX input paths can be used to control which set of LaTeX font definition files is actually used by TeX).
-
- - You may distribute modified versions of files with filename extension `.cfg' (configuration files) with their original names. The Program may (and usually will) specify the range of commands that are allowed in a particular configuration file.
-
-Because of portability and exchangeability issues in LaTeX software, The LaTeX3 Project deprecates the distribution of modified versions of components of LaTeX or of generally available contributed code for them, but such distribution can meet the conditions of this license.
-
-NO WARRANTY
-===========
-
-There is no warranty for The Program. Except when otherwise stated in writing, The Copyright Holder provides The Program `as is', without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of The Program is with you. Should The Program prove defective, you assume the cost of all necessary servicing, repair, or correction.
-
-In no event unless agreed to in writing will The Copyright Holder, or any author named in the files of The Program, or any other party who may distribute and/or modify The Program as permitted above, be liable to you for damages, including any general, special, incidental or consequential damages arising out of any use of The Program or out of inability to use The Program (including, but not limited to, loss of data, data being rendered inaccurate, or losses sustained by anyone as a result of any failure of The Program to operate with any other programs), even if The Copyright Holder or said author or said other party has been advised of the possibility of such damages.
-
-WHETHER AND HOW TO DISTRIBUTE PROGRAMS UNDER THIS LICENSE =========================================================
-
-This section contains important instructions, examples, and recommendations for authors who are considering distributing their programs under this license. These authors are addressed as `you' in this section.
-
-Choosing This License or Another License
-----------------------------------------
-
-If for any part of your program you want or need to use *distribution* conditions that differ from those in this license, then do not refer to this license anywhere in your program but instead distribute your program under a different license. You may use the text of this license as a model for your own license, but your license should not refer to the LPPL or otherwise give the impression that your program is distributed under the LPPL.
-
-The document `modguide.tex' in the base LaTeX distribution explains the motivation behind the conditions of this license. It explains, for example, why distributing LaTeX under the GNU General Public License (GPL) was considered inappropriate. Even if your program is unrelated to LaTeX, the discussion in `modguide.tex' may still be relevant, and authors intending to distribute their programs under any license are encouraged to read it.
-
-How to Use This License
------------------------
-
-To use this license, place in each of the files of your program both an explicit copyright notice including your name and the year and also a statement that the distribution and/or modification of the file is constrained by the conditions in this license.
-
-Here is an example of such a notice and statement:
-
- %% pig.dtx
- %% Copyright 2001 M. Y. Name
- %
- % This program may be distributed and/or modified under the
- % conditions of the LaTeX Project Public License, either version 1.2
- % of this license or (at your option) any later version.
- % The latest version of this license is in
- % http://www.latex-project.org/lppl.txt
- % and version 1.2 or later is part of all distributions of LaTeX
- % version 1999/12/01 or later.
- %
- % This program consists of the files pig.dtx and pig.ins
-
-Given such a notice and statement in a file, the conditions given in this license document would apply, with `The Program' referring to the two files `pig.dtx' and `pig.ins', and `The Copyright Holder' referring to the person `M. Y. Name'.
-
-Important Recommendations
--------------------------
-
-Defining What Constitutes The Program
-
-The LPPL requires that distributions of The Program contain all the files of The Program. It is therefore important that you provide a way for the licensee to determine which files constitute The Program. This could, for example, be achieved by explicitly listing all the files of The Program near the copyright notice of each file or by using a line like
-
- % This program consists of all files listed in manifest.txt.
-
-in that place. In the absence of an unequivocal list it might be impossible for the licensee to determine what is considered by you to comprise The Program.
-
-Noting Exceptional Files If The Program contains any files bearing additional conditions on modification, or on distribution of modified versions, of those files (other than those listed in `Additional Conditions on LaTeX Software Files'), then it is recommended that The Program contain a prominent file that defines the exceptional conditions, and either lists the exceptional files or defines one or more categories of exceptional files.
-
-Files containing the text of a license (such as this file) are often examples of files bearing more restrictive conditions on modification. LaTeX configuration files (with filename extension `.cfg') are examples of files bearing less restrictive conditions on the distribution of a modified version of the file. The additional conditions on LaTeX software given above are examples of declaring a category of files bearing exceptional additional conditions.
diff --git a/options/license/LPPL-1.3a b/options/license/LPPL-1.3a
deleted file mode 100644
index b159f90fdf..0000000000
--- a/options/license/LPPL-1.3a
+++ /dev/null
@@ -1,175 +0,0 @@
-The LaTeX Project Public License
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-
-LPPL Version 1.3a 2004-10-01
-
-Copyright 1999 2002-04 LaTeX3
-Project Everyone is allowed to distribute verbatim copies of this license document, but modification of it is not allowed.
-
-PREAMBLE
-========
-
-The LaTeX Project Public License (LPPL) is the primary license under which the the LaTeX kernel and the base LaTeX packages are distributed.
-
-You may use this license for any work of which you hold the copyright and which you wish to distribute. This license may be particularly suitable if your work is TeX-related (such as a LaTeX package), but you may use it with small modifications even if your work is unrelated to TeX.
-
-The section `WHETHER AND HOW TO DISTRIBUTE WORKS UNDER THIS LICENSE', below, gives instructions, examples, and recommendations for authors who are considering distributing their works under this license.
-
-This license gives conditions under which a work may be distributed and modified, as well as conditions under which modified versions of that work may be distributed.
-
-We, the LaTeX3 Project, believe that the conditions below give you the freedom to make and distribute modified versions of your work that conform with whatever technical specifications you wish while maintaining the availability, integrity, and reliability of that work. If you do not see how to achieve your goal while meeting these conditions, then read the document `cfgguide.tex' and `modguide.tex' in the base LaTeX distribution for suggestions.
-
-DEFINITIONS
-===========
-
-In this license document the following terms are used:
-
-`Work' Any work being distributed under this License. `Derived Work' Any work that under any applicable law is derived from the Work.
-
-`Modification' Any procedure that produces a Derived Work under any applicable law -- for example, the production of a file containing an original file associated with the Work or a significant portion of such a file, either verbatim or with modifications and/or translated into another language.
-
-`Modify' To apply any procedure that produces a Derived Work under any applicable law. `Distribution' Making copies of the Work available from one person to another, in whole or in part. Distribution includes (but is not limited to) making any electronic components of the Work accessible by file transfer protocols such as FTP or HTTP or by shared file systems such as Sun's Network File System (NFS).
-
-`Compiled Work' A version of the Work that has been processed into a form where it is directly usable on a computer system. This processing may include using installation facilities provided by the Work, transformations of the Work, copying of components of the Work, or other activities. Note that modification of any installation facilities provided by the Work constitutes modification of the Work.
-
-`Current Maintainer' A person or persons nominated as such within the Work. If there is no such explicit nomination then it is the `Copyright Holder' under any applicable law.
-
-`Base Interpreter' A program or process that is normally needed for running or interpreting a part or the whole of the Work. A Base Interpreter may depend on external components but these are not considered part of the Base Interpreter provided that each external component clearly identifies itself whenever it is used interactively. Unless explicitly specified when applying the license to the Work, the only applicable Base Interpreter is a "LaTeX-Format".
-
-CONDITIONS ON DISTRIBUTION AND MODIFICATION
-===========================================
-
-1. Activities other than distribution and/or modification of the Work are not covered by this license; they are outside its scope. In particular, the act of running the Work is not restricted and no requirements are made concerning any offers of support for the Work.
-
-2. You may distribute a complete, unmodified copy of the Work as you received it. Distribution of only part of the Work is considered modification of the Work, and no right to distribute such a Derived Work may be assumed under the terms of this clause.
-
-3. You may distribute a Compiled Work that has been generated from a complete, unmodified copy of the Work as distributed under Clause 2 above, as long as that Compiled Work is distributed in such a way that the recipients may install the Compiled Work on their system exactly as it would have been installed if they generated a Compiled Work directly from the Work.
-
-4. If you are the Current Maintainer of the Work, you may, without restriction, modify the Work, thus creating a Derived Work. You may also distribute the Derived Work without restriction, including Compiled Works generated from the Derived Work. Derived Works distributed in this manner by the Current Maintainer are considered to be updated versions of the Work.
-
-5. If you are not the Current Maintainer of the Work, you may modify your copy of the Work, thus creating a Derived Work based on the Work, and compile this Derived Work, thus creating a Compiled Work based on the Derived Work.
-
-6. If you are not the Current Maintainer of the Work, you may distribute a Derived Work provided the following conditions are met for every component of the Work unless that component clearly states in the copyright notice that it is exempt from that condition. Only the Current Maintainer is allowed to add such statements of exemption to a component of the Work.
-
- a. If a component of this Derived Work can be a direct replacement for a component of the Work when that component is used with the Base Interpreter, then, wherever this component of the Work identifies itself to the user when used interactively with that Base Interpreter, the replacement component of this Derived Work clearly and unambiguously identifies itself as a modified version of this component to the user when used interactively with that Base Interpreter.
-
- b. Every component of the Derived Work contains prominent notices detailing the nature of the changes to that component, or a prominent reference to another file that is distributed as part of the Derived Work and that contains a complete and accurate log of the changes.
-
- c. No information in the Derived Work implies that any persons, including (but not limited to) the authors of the original version of the Work, provide any support, including (but not limited to) the reporting and handling of errors, to recipients of the Derived Work unless those persons have stated explicitly that they do provide such support for the Derived Work.
-
- d. You distribute at least one of the following with the Derived Work:
-
- 1. A complete, unmodified copy of the Work; if your distribution of a modified component is made by offering access to copy the modified component from a designated place, then offering equivalent access to copy the Work from the same or some similar place meets this condition, even though third parties are not compelled to copy the Work along with the modified component;
-
- 2. Information that is sufficient to obtain a complete, unmodified copy of the Work.
-
-7. If you are not the Current Maintainer of the Work, you may distribute a Compiled Work generated from a Derived Work, as long as the Derived Work is distributed to all recipients of the Compiled Work, and as long as the conditions of Clause 6, above, are met with regard to the Derived Work.
-
-8. The conditions above are not intended to prohibit, and hence do not apply to, the modification, by any method, of any component so that it becomes identical to an updated version of that component of the Work as it is distributed by the Current Maintainer under Clause 4, above.
-
-9. Distribution of the Work or any Derived Work in an alternative format, where the Work or that Derived Work (in whole or in part) is then produced by applying some process to that format, does not relax or nullify any sections of this license as they pertain to the results of applying that process.
-
-10.
- a. A Derived Work may be distributed under a different license provided that license itself honors the conditions listed in Clause 6 above, in regard to the Work, though it does not have to honor the rest of the conditions in this license.
-
- b. If a Derived Work is distributed under this license, that Derived Work must provide sufficient documentation as part of itself to allow each recipient of that Derived Work to honor the restrictions in Clause 6 above, concerning changes from the Work.
-
-11. This license places no restrictions on works that are unrelated to the Work, nor does this license place any restrictions on aggregating such works with the Work by any means.
-
-12. Nothing in this license is intended to, or may be used to, prevent complete compliance by all parties with all applicable laws.
-
-NO WARRANTY
-===========
-
-There is no warranty for the Work. Except when otherwise stated in writing, the Copyright Holder provides the Work `as is', without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the Work is with you. Should the Work prove defective, you assume the cost of all necessary servicing, repair, or correction.
-
-In no event unless required by applicable law or agreed to in writing will The Copyright Holder, or any author named in the components of the Work, or any other party who may distribute and/or modify the Work as permitted above, be liable to you for damages, including any general, special, incidental or consequential damages arising out of any use of the Work or out of inability to use the Work (including, but not limited to, loss of data, data being rendered inaccurate, or losses sustained by anyone as a result of any failure of the Work to operate with any other programs), even if the Copyright Holder or said author or said other party has been advised of the possibility of such damages.
-
-MAINTENANCE OF THE WORK
-=======================
-
-The Work has the status `author-maintained' if the Copyright Holder explicitly and prominently states near the primary copyright notice in the Work that the Work can only be maintained by the Copyright Holder or simply that is `author-maintained'.
-
-The Work has the status `maintained' if there is a Current Maintainer who has indicated in the Work that they are willing to receive error reports for the Work (for example, by supplying a valid e-mail address). It is not required for the Current Maintainer to acknowledge or act upon these error reports.
-
-The Work changes from status `maintained' to `unmaintained' if there is no Current Maintainer, or the person stated to be Current Maintainer of the work cannot be reached through the indicated means of communication for a period of six months, and there are no other significant signs of active maintenance.
-
-You can become the Current Maintainer of the Work by agreement with any existing Current Maintainer to take over this role.
-
-If the Work is unmaintained, you can become the Current Maintainer of the Work through the following steps:
-
- 1. Make a reasonable attempt to trace the Current Maintainer (and the Copyright Holder, if the two differ) through the means of an Internet or similar search.
-
- 2. If this search is successful, then enquire whether the Work is still maintained.
-
- a. If it is being maintained, then ask the Current Maintainer to update their communication data within one month.
-
- b. If the search is unsuccessful or no action to resume active maintenance is taken by the Current Maintainer, then announce within the pertinent community your intention to take over maintenance. (If the Work is a LaTeX work, this could be done, for example, by posting to comp.text.tex.)
-
- 3a. If the Current Maintainer is reachable and agrees to pass maintenance of the Work to you, then this takes effect immediately upon announcement.
-
- b. If the Current Maintainer is not reachable and the Copyright Holder agrees that maintenance of the Work be passed to you, then this takes effect immediately upon announcement.
-
- 4. If you make an `intention announcement' as described in 2b. above and after three months your intention is challenged neither by the Current Maintainer nor by the Copyright Holder nor by other people, then you may arrange for the Work to be changed so as to name you as the (new) Current Maintainer.
-
- 5. If the previously unreachable Current Maintainer becomes reachable once more within three months of a change completed under the terms of 3b) or 4), then that Current Maintainer must become or remain the Current Maintainer upon request provided they then update their communication data within one month.
-
-A change in the Current Maintainer does not, of itself, alter the fact that the Work is distributed under the LPPL license.
-
-If you become the Current Maintainer of the Work, you should immediately provide, within the Work, a prominent and unambiguous statement of your status as Current Maintainer. You should also announce your new status to the same pertinent community as in 2b) above.
-
-WHETHER AND HOW TO DISTRIBUTE WORKS UNDER THIS LICENSE
-======================================================
-
-This section contains important instructions, examples, and recommendations for authors who are considering distributing their works under this license. These authors are addressed as `you' in this section.
-
-Choosing This License or Another License
-----------------------------------------
-
-If for any part of your work you want or need to use *distribution* conditions that differ significantly from those in this license, then do not refer to this license anywhere in your work but, instead, distribute your work under a different license. You may use the text of this license as a model for your own license, but your license should not refer to the LPPL or otherwise give the impression that your work is distributed under the LPPL.
-
-The document `modguide.tex' in the base LaTeX distribution explains the motivation behind the conditions of this license. It explains, for example, why distributing LaTeX under the GNU General Public License (GPL) was considered inappropriate. Even if your work is unrelated to LaTeX, the discussion in `modguide.tex' may still be relevant, and authors intending to distribute their works under any license are encouraged to read it.
-
-A Recommendation on Modification Without Distribution
------------------------------------------------------
-
-It is wise never to modify a component of the Work, even for your own personal use, without also meeting the above conditions for distributing the modified component. While you might intend that such modifications will never be distributed, often this will happen by accident -- you may forget that you have modified that component; or it may not occur to you when allowing others to access the modified version that you are thus distributing it and violating the conditions of this license in ways that could have legal implications and, worse, cause problems for the community. It is therefore usually in your best interest to keep your copy of the Work identical with the public one. Many works provide ways to control the behavior of that work without altering any of its licensed components.
-
-How to Use This License
------------------------
-
-To use this license, place in each of the components of your work both an explicit copyright notice including your name and the year the work was authored and/or last substantially modified. Include also a statement that the distribution and/or modification of that component is constrained by the conditions in this license.
-
-Here is an example of such a notice and statement:
-
- %% pig.dtx
- %% Copyright 2003 M. Y. Name
- %
- % This work may be distributed and/or modified under the
- % conditions of the LaTeX Project Public License, either version 1.3
- % of this license or (at your option) any later version.
- % The latest version of this license is in
- % http://www.latex-project.org/lppl.txt
- % and version 1.3 or later is part of all distributions of LaTeX
- % version 2003/12/01 or later.
- %
- % This work has the LPPL maintenance status "maintained".
- %
- % This Current Maintainer of this work is M. Y. Name.
- %
- % This work consists of the files pig.dtx and pig.ins % and the derived file pig.sty.
-
-Given such a notice and statement in a file, the conditions given in this license document would apply, with the `Work' referring to the three files `pig.dtx', `pig.ins', and `pig.sty' (the last being generated from `pig.dtx' using `pig.ins'), the `Base Interpreter' referring to any "LaTeX-Format", and both `Copyright Holder' and `Current Maintainer' referring to the person `M. Y. Name'.
-
-If you do not want the Maintenance section of LPPL to apply to your Work, change "maintained" above into "author-maintained". However, we recommend that you use "maintained" as the Maintenance section was added in order to ensure that your Work remains useful to the community even when you can no longer maintain and support it yourself.
-
-Important Recommendations
--------------------------
-
-Defining What Constitutes the Work
-
-The LPPL requires that distributions of the Work contain all the files of the Work. It is therefore important that you provide a way for the licensee to determine which files constitute the Work. This could, for example, be achieved by explicitly listing all the files of the Work near the copyright notice of each file or by using a line such as:
-
- % This work consists of all files listed in manifest.txt.
-
-in that place. In the absence of an unequivocal list it might be impossible for the licensee to determine what is considered by you to comprise the Work and, in such a case, the licensee would be entitled to make reasonable conjectures as to which files comprise the Work.
diff --git a/options/license/LPPL-1.3c b/options/license/LPPL-1.3c
deleted file mode 100644
index 4e3b06e3ad..0000000000
--- a/options/license/LPPL-1.3c
+++ /dev/null
@@ -1,184 +0,0 @@
-The LaTeX Project Public License
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-
-LPPL Version 1.3c 2008-05-04
-
-Copyright 1999 2002-2008 LaTeX3 Project
-
-Everyone is allowed to distribute verbatim copies of this license document, but modification of it is not allowed.
-
-PREAMBLE
-========
-
-The LaTeX Project Public License (LPPL) is the primary license under which the LaTeX kernel and the base LaTeX packages are distributed.
-
-You may use this license for any work of which you hold the copyright and which you wish to distribute. This license may be particularly suitable if your work is TeX-related (such as a LaTeX package), but it is written in such a way that you can use it even if your work is unrelated to TeX.
-
-The section `WHETHER AND HOW TO DISTRIBUTE WORKS UNDER THIS LICENSE', below, gives instructions, examples, and recommendations for authors who are considering distributing their works under this license.
-
-This license gives conditions under which a work may be distributed and modified, as well as conditions under which modified versions of that work may be distributed.
-
-We, the LaTeX3 Project, believe that the conditions below give you the freedom to make and distribute modified versions of your work that conform with whatever technical specifications you wish while maintaining the availability, integrity, and reliability of that work. If you do not see how to achieve your goal while meeting these conditions, then read the document `cfgguide.tex' and `modguide.tex' in the base LaTeX distribution for suggestions.
-
-DEFINITIONS
-===========
-
-In this license document the following terms are used:
-
-`Work' Any work being distributed under this License. `Derived Work' Any work that under any applicable law is derived from the Work.
-
-`Modification' Any procedure that produces a Derived Work under any applicable law -- for example, the production of a file containing an original file associated with the Work or a significant portion of such a file, either verbatim or with modifications and/or translated into another language.
-
-`Modify' To apply any procedure that produces a Derived Work under any applicable law. `Distribution' Making copies of the Work available from one person to another, in whole or in part. Distribution includes (but is not limited to) making any electronic components of the Work accessible by file transfer protocols such as FTP or HTTP or by shared file systems such as Sun's Network File System (NFS).
-
-`Compiled Work' A version of the Work that has been processed into a form where it is directly usable on a computer system. This processing may include using installation facilities provided by the Work, transformations of the Work, copying of components of the Work, or other activities. Note that modification of any installation facilities provided by the Work constitutes modification of the Work.
-
-`Current Maintainer' A person or persons nominated as such within the Work. If there is no such explicit nomination then it is the `Copyright Holder' under any applicable law.
-
-`Base Interpreter' A program or process that is normally needed for running or interpreting a part or the whole of the Work.
-
-A Base Interpreter may depend on external components but these are not considered part of the Base Interpreter provided that each external component clearly identifies itself whenever it is used interactively. Unless explicitly specified when applying the license to the Work, the only applicable Base Interpreter is a `LaTeX-Format' or in the case of files belonging to the `LaTeX-format' a program implementing the `TeX language'.
-
-CONDITIONS ON DISTRIBUTION AND MODIFICATION
-===========================================
-
-1. Activities other than distribution and/or modification of the Work are not covered by this license; they are outside its scope. In particular, the act of running the Work is not restricted and no requirements are made concerning any offers of support for the Work.
-
-2. You may distribute a complete, unmodified copy of the Work as you received it. Distribution of only part of the Work is considered modification of the Work, and no right to distribute such a Derived Work may be assumed under the terms of this clause.
-
-3. You may distribute a Compiled Work that has been generated from a complete, unmodified copy of the Work as distributed under Clause 2 above, as long as that Compiled Work is distributed in such a way that the recipients may install the Compiled Work on their system exactly as it would have been installed if they generated a Compiled Work directly from the Work.
-
-4. If you are the Current Maintainer of the Work, you may, without restriction, modify the Work, thus creating a Derived Work. You may also distribute the Derived Work without restriction, including Compiled Works generated from the Derived Work. Derived Works distributed in this manner by the Current Maintainer are considered to be updated versions of the Work.
-
-5. If you are not the Current Maintainer of the Work, you may modify your copy of the Work, thus creating a Derived Work based on the Work, and compile this Derived Work, thus creating a Compiled Work based on the Derived Work.
-
-6. If you are not the Current Maintainer of the Work, you may distribute a Derived Work provided the following conditions are met for every component of the Work unless that component clearly states in the copyright notice that it is exempt from that condition. Only the Current Maintainer is allowed to add such statements of exemption to a component of the Work.
-
- a. If a component of this Derived Work can be a direct replacement for a component of the Work when that component is used with the Base Interpreter, then, wherever this component of the Work identifies itself to the user when used interactively with that Base Interpreter, the replacement component of this Derived Work clearly and unambiguously identifies itself as a modified version of this component to the user when used interactively with that Base Interpreter.
-
- b. Every component of the Derived Work contains prominent notices detailing the nature of the changes to that component, or a prominent reference to another file that is distributed as part of the Derived Work and that contains a complete and accurate log of the changes.
-
- c. No information in the Derived Work implies that any persons, including (but not limited to) the authors of the original version of the Work, provide any support, including (but not limited to) the reporting and handling of errors, to recipients of the Derived Work unless those persons have stated explicitly that they do provide such support for the Derived Work.
-
- d. You distribute at least one of the following with the Derived Work:
-
- 1. A complete, unmodified copy of the Work; if your distribution of a modified component is made by offering access to copy the modified component from a designated place, then offering equivalent access to copy the Work from the same or some similar place meets this condition, even though third parties are not compelled to copy the Work along with the modified component;
-
- 2. Information that is sufficient to obtain a complete, unmodified copy of the Work.
-
-7. If you are not the Current Maintainer of the Work, you may distribute a Compiled Work generated from a Derived Work, as long as the Derived Work is distributed to all recipients of the Compiled Work, and as long as the conditions of Clause 6, above, are met with regard to the Derived Work.
-
-8. The conditions above are not intended to prohibit, and hence do not apply to, the modification, by any method, of any component so that it becomes identical to an updated version of that component of the Work as it is distributed by the Current Maintainer under Clause 4, above.
-
-9. Distribution of the Work or any Derived Work in an alternative format, where the Work or that Derived Work (in whole or in part) is then produced by applying some process to that format, does not relax or nullify any sections of this license as they pertain to the results of applying that process.
-
-10.
- a. A Derived Work may be distributed under a different license provided that license itself honors the conditions listed in Clause 6 above, in regard to the Work, though it does not have to honor the rest of the conditions in this license.
-
- b. If a Derived Work is distributed under a different license, that Derived Work must provide sufficient documentation as part of itself to allow each recipient of that Derived Work to honor the restrictions in Clause 6 above, concerning changes from the Work.
-
-11. This license places no restrictions on works that are unrelated to the Work, nor does this license place any restrictions on aggregating such works with the Work by any means.
-
-12. Nothing in this license is intended to, or may be used to, prevent complete compliance by all parties with all applicable laws.
-
-NO WARRANTY
-===========
-
-There is no warranty for the Work. Except when otherwise stated in writing, the Copyright Holder provides the Work `as is', without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the Work is with you. Should the Work prove defective, you assume the cost of all necessary servicing, repair, or correction.
-
-In no event unless required by applicable law or agreed to in writing will The Copyright Holder, or any author named in the components of the Work, or any other party who may distribute and/or modify the Work as permitted above, be liable to you for damages, including any general, special, incidental or consequential damages arising out of any use of the Work or out of inability to use the Work (including, but not limited to, loss of data, data being rendered inaccurate, or losses sustained by anyone as a result of any failure of the Work to operate with any other programs), even if the Copyright Holder or said author or said other party has been advised of the possibility of such damages.
-
-MAINTENANCE OF THE WORK
-=======================
-
-The Work has the status `author-maintained' if the Copyright Holder explicitly and prominently states near the primary copyright notice in the Work that the Work can only be maintained by the Copyright Holder or simply that it is `author-maintained'.
-
-The Work has the status `maintained' if there is a Current Maintainer who has indicated in the Work that they are willing to receive error reports for the Work (for example, by supplying a valid e-mail address). It is not required for the Current Maintainer to acknowledge or act upon these error reports.
-
-The Work changes from status `maintained' to `unmaintained' if there is no Current Maintainer, or the person stated to be Current Maintainer of the work cannot be reached through the indicated means of communication for a period of six months, and there are no other significant signs of active maintenance.
-
-You can become the Current Maintainer of the Work by agreement with any existing Current Maintainer to take over this role.
-
-If the Work is unmaintained, you can become the Current Maintainer of the Work through the following steps:
-
-1. Make a reasonable attempt to trace the Current Maintainer (and the Copyright Holder, if the two differ) through the means of an Internet or similar search.
-
-2. If this search is successful, then enquire whether the Work is still maintained.
-
- a. If it is being maintained, then ask the Current Maintainer to update their communication data within one month.
-
- b. If the search is unsuccessful or no action to resume active maintenance is taken by the Current Maintainer, then announce within the pertinent community your intention to take over maintenance. (If the Work is a LaTeX work, this could be done, for example, by posting to comp.text.tex.)
-
-3a. If the Current Maintainer is reachable and agrees to pass maintenance of the Work to you, then this takes effect immediately upon announcement.
-
-b. If the Current Maintainer is not reachable and the Copyright Holder agrees that maintenance of the Work be passed to you, then this takes effect immediately upon announcement.
-
-4. If you make an `intention announcement' as described in 2b. above and after three months your intention is challenged neither by the Current Maintainer nor by the Copyright Holder nor by other people, then you may arrange for the Work to be changed so as to name you as the (new) Current Maintainer.
-
-5. If the previously unreachable Current Maintainer becomes reachable once more within three months of a change completed under the terms of 3b) or 4), then that Current Maintainer must become or remain the Current Maintainer upon request provided they then update their communication data within one month.
-
-A change in the Current Maintainer does not, of itself, alter the fact that the Work is distributed under the LPPL license.
-
-If you become the Current Maintainer of the Work, you should immediately provide, within the Work, a prominent and unambiguous statement of your status as Current Maintainer. You should also announce your new status to the same pertinent community as in 2b) above.
-
-WHETHER AND HOW TO DISTRIBUTE WORKS UNDER THIS LICENSE
-======================================================
-
-This section contains important instructions, examples, and recommendations for authors who are considering distributing their works under this license. These authors are addressed as `you' in this section.
-
-Choosing This License or Another License
-----------------------------------------
-
-If for any part of your work you want or need to use *distribution* conditions that differ significantly from those in this license, then do not refer to this license anywhere in your work but, instead, distribute your work under a different license. You may use the text of this license as a model for your own license, but your license should not refer to the LPPL or otherwise give the impression that your work is distributed under the LPPL.
-
-The document `modguide.tex' in the base LaTeX distribution explains the motivation behind the conditions of this license. It explains, for example, why distributing LaTeX under the GNU General Public License (GPL) was considered inappropriate. Even if your work is unrelated to LaTeX, the discussion in `modguide.tex' may still be relevant, and authors intending to distribute their works under any license are encouraged to read it.
-
-A Recommendation on Modification Without Distribution
------------------------------------------------------
-
-It is wise never to modify a component of the Work, even for your own personal use, without also meeting the above conditions for distributing the modified component. While you might intend that such modifications will never be distributed, often this will happen by accident -- you may forget that you have modified that component; or it may not occur to you when allowing others to access the modified version that you are thus distributing it and violating the conditions of this license in ways that could have legal implications and, worse, cause problems for the community. It is therefore usually in your best interest to keep your copy of the Work identical with the public one. Many works provide ways to control the behavior of that work without altering any of its licensed components.
-
-How to Use This License
------------------------
-
-To use this license, place in each of the components of your work both an explicit copyright notice including your name and the year the work was authored and/or last substantially modified. Include also a statement that the distribution and/or modification of that component is constrained by the conditions in this license.
-
-Here is an example of such a notice and statement:
-
- %% pig.dtx
- %% Copyright 2005 M. Y. Name
- %
- % This work may be distributed and/or modified under the
- % conditions of the LaTeX Project Public License, either version 1.3
- % of this license or (at your option) any later version.
- % The latest version of this license is in
- % http://www.latex-project.org/lppl.txt
- % and version 1.3 or later is part of all distributions of LaTeX
- % version 2005/12/01 or later.
- %
- % This work has the LPPL maintenance status `maintained'.
- %
- % The Current Maintainer of this work is M. Y. Name.
- %
- % This work consists of the files pig.dtx and pig.ins
- % and the derived file pig.sty.
-
-Given such a notice and statement in a file, the conditions given in this license document would apply, with the `Work' referring to the three files `pig.dtx', `pig.ins', and `pig.sty' (the last being generated from `pig.dtx' using `pig.ins'), the `Base Interpreter' referring to any `LaTeX-Format', and both `Copyright Holder' and `Current Maintainer' referring to the person `M. Y. Name'.
-
-If you do not want the Maintenance section of LPPL to apply to your Work, change `maintained' above into `author-maintained'. However, we recommend that you use `maintained', as the Maintenance section was added in order to ensure that your Work remains useful to the community even when you can no longer maintain and support it yourself.
-
-Derived Works That Are Not Replacements
----------------------------------------
-
-Several clauses of the LPPL specify means to provide reliability and stability for the user community. They therefore concern themselves with the case that a Derived Work is intended to be used as a (compatible or incompatible) replacement of the original Work. If this is not the case (e.g., if a few lines of code are reused for a completely different task), then clauses 6b and 6d shall not apply.
-
-Important Recommendations
--------------------------
-
-Defining What Constitutes the Work
-
-The LPPL requires that distributions of the Work contain all the files of the Work. It is therefore important that you provide a way for the licensee to determine which files constitute the Work. This could, for example, be achieved by explicitly listing all the files of the Work near the copyright notice of each file or by using a line such as:
-
- % This work consists of all files listed in manifest.txt.
-
-in that place. In the absence of an unequivocal list it might be impossible for the licensee to determine what is considered by you to comprise the Work and, in such a case, the licensee would be entitled to make reasonable conjectures as to which files comprise the Work.
diff --git a/options/license/LZMA-SDK-9.11-to-9.20 b/options/license/LZMA-SDK-9.11-to-9.20
deleted file mode 100644
index 5da25bf883..0000000000
--- a/options/license/LZMA-SDK-9.11-to-9.20
+++ /dev/null
@@ -1,8 +0,0 @@
-LICENSE
--------
-
-LZMA SDK is written and placed in the public domain by Igor Pavlov.
-
-Some code in LZMA is based on public domain code from another developers:
- 1) PPMd var.H (2001): Dmitry Shkarin
- 2) SHA-256: Wei Dai (Crypto++ library)
diff --git a/options/license/LZMA-SDK-9.22 b/options/license/LZMA-SDK-9.22
deleted file mode 100644
index ef4768d2a7..0000000000
--- a/options/license/LZMA-SDK-9.22
+++ /dev/null
@@ -1,15 +0,0 @@
-LICENSE
--------
-
-LZMA SDK is written and placed in the public domain by Igor Pavlov.
-
-Some code in LZMA SDK is based on public domain code from another developers:
- 1) PPMd var.H (2001): Dmitry Shkarin
- 2) SHA-256: Wei Dai (Crypto++ library)
-
-Anyone is free to copy, modify, publish, use, compile, sell, or distribute the
-original LZMA SDK code, either in source code form or as a compiled binary, for
-any purpose, commercial or non-commercial, and by any means.
-
-LZMA SDK code is compatible with open source licenses, for example, you can
-include it to GNU GPL or GNU LGPL code.
diff --git a/options/license/LZMA-exception b/options/license/LZMA-exception
deleted file mode 100644
index 6fc9c1352b..0000000000
--- a/options/license/LZMA-exception
+++ /dev/null
@@ -1,3 +0,0 @@
-I.6 Special exception for LZMA compression module
-
-Igor Pavlov and Amir Szekely, the authors of the LZMA compression module for NSIS, expressly permit you to statically or dynamically link your code (or bind by name) to the files from the LZMA compression module for NSIS without subjecting your linked code to the terms of the Common Public license version 1.0. Any modifications or additions to files from the LZMA compression module for NSIS, however, are subject to the terms of the Common Public License version 1.0.
diff --git a/options/license/Latex2e b/options/license/Latex2e
deleted file mode 100644
index 2ce86bed0e..0000000000
--- a/options/license/Latex2e
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright (C) 2007, 2008, 2009, 2010 Karl Berry.
-Copyright (C) 1988, 1994, 2007 Stephen Gilmore.
-Copyright (C) 1994, 1995, 1996 Torsten Martinsen.
-
-Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies.
-
-Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.
-
-Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions.
diff --git a/options/license/Latex2e-translated-notice b/options/license/Latex2e-translated-notice
deleted file mode 100644
index 5ac100f4cb..0000000000
--- a/options/license/Latex2e-translated-notice
+++ /dev/null
@@ -1,26 +0,0 @@
-Copyright @copyright{} 1989, 1992, 1993, 1994, 1995, 1996, 2014 Free Software
-Foundation, Inc.
-
-Copyright @copyright{} 1995, 1996 Joseph Arceneaux.
-
-Copyright @copyright{} 1999, Carlo Wood.
-
-Copyright @copyright{} 2001, David Ingamells.
-
-Copyright @copyright{} 2013, Åukasz Stelmach.
-
-Copyright @copyright{} 2015, Tim Hentenaar.
-
-Permission is granted to make and distribute verbatim copies of
-this manual provided the copyright notice and this permission notice
-are preserved on all copies.
-
-Permission is granted to copy and distribute modified versions of this
-manual under the conditions for verbatim copying, provided that the entire
-resulting derived work is distributed under the terms of a permission
-notice identical to this one.
-
-Permission is granted to copy and distribute translations of this manual
-into another language, under the above conditions for modified versions,
-except that this permission notice may be stated in a translation approved
-by the Foundation.
diff --git a/options/license/Leptonica b/options/license/Leptonica
deleted file mode 100644
index 9bc67e6ca8..0000000000
--- a/options/license/Leptonica
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright (C) 2001 Leptonica. All rights reserved.
-
-This software is distributed in the hope that it will be useful, but with NO WARRANTY OF ANY KIND.
-
-No author or distributor accepts responsibility to anyone for the consequences of using this software, or for whether it serves any particular purpose or works at all, unless he or she says so in writing. Everyone is granted permission to copy, modify and redistribute this source code, for commercial or non-commercial purposes, with the following restrictions:
-
-(1) the origin of this source code must not be misrepresented;
-(2) modified versions must be plainly marked as such; and
-(3) this notice may not be removed or altered from any source or modified source distribution.
diff --git a/options/license/LiLiQ-P-1.1 b/options/license/LiLiQ-P-1.1
deleted file mode 100644
index 594cc742e0..0000000000
--- a/options/license/LiLiQ-P-1.1
+++ /dev/null
@@ -1,70 +0,0 @@
-Licence Libre du Québec – Permissive (LiLiQ-P)
-
-Version 1.1
-
-1. Préambule
-Cette licence s'applique à tout logiciel distribué dont le titulaire du droit d'auteur précise qu'il est sujet aux termes de la Licence Libre du Québec – Permissive (LiLiQ-P) (ci-après appelée la « licence »).
-
-2. Définitions
-Dans la présente licence, à moins que le contexte n'indique un sens différent, on entend par:
-
- « concédant » : le titulaire du droit d'auteur sur le logiciel, ou toute personne dûment autorisée par ce dernier à accorder la présente licence;
- « contributeur » : le titulaire du droit d'auteur ou toute personne autorisée par ce dernier à soumettre au concédant une contribution. Un contributeur dont sa contribution est incorporée au logiciel est considéré comme un concédant en regard de sa contribution;
- « contribution » : tout logiciel original, ou partie de logiciel original soumis et destiné à être incorporé dans le logiciel;
- « distribution » : le fait de délivrer une copie du logiciel;
- « licencié » : toute personne qui possède une copie du logiciel et qui exerce les droits concédés par la licence;
- « logiciel » : une œuvre protégée par le droit d'auteur, telle qu'un programme d'ordinateur et sa documentation, pour laquelle le titulaire du droit d'auteur a précisé qu'elle est sujette aux termes de la présente licence;
- « logiciel dérivé » : tout logiciel original réalisé par un licencié, autre que le logiciel ou un logiciel modifié, qui produit ou reproduit la totalité ou une partie importante du logiciel;
- « logiciel modifié » : toute modification par un licencié de l'un des fichiers source du logiciel ou encore tout nouveau fichier source qui incorpore le logiciel ou une partie importante de ce dernier.
-
-3. Licence de droit d'auteur
-Sous réserve des termes de la licence, le concédant accorde au licencié une licence non exclusive et libre de redevances lui permettant d’exercer les droits suivants sur le logiciel :
-
- 1 Produire ou reproduire la totalité ou une partie importante;
- 2 Exécuter ou représenter la totalité ou une partie importante en public;
- 3 Publier la totalité ou une partie importante;
- 4 Sous-licencier sous une autre licence libre, approuvée ou certifiée par la Free Software Foundation ou l'Open Source Initiative.
-
-Cette licence est accordée sans limite territoriale et sans limite de temps.
-
-L'exercice complet de ces droits est sujet à la distribution par le concédant du code source du logiciel, lequel doit être sous une forme permettant d'y apporter des modifications. Le concédant peut aussi distribuer le logiciel accompagné d'une offre de distribuer le code source du logiciel, sans frais supplémentaires, autres que ceux raisonnables afin de permettre la livraison du code source. Cette offre doit être valide pendant une durée raisonnable.
-
-4. Distribution
-Le licencié peut distribuer des copies du logiciel, d'un logiciel modifié ou dérivé, sous réserve de respecter les conditions suivantes :
-
- 1 Le logiciel doit être accompagné d'un exemplaire de cette licence;
- 2 Si le logiciel a été modifié, le licencié doit en faire la mention, de préférence dans chacun des fichiers modifiés dont la nature permet une telle mention;
- 3 Les étiquettes ou mentions faisant état des droits d'auteur, des marques de commerce, des garanties ou de la paternité concernant le logiciel ne doivent pas être modifiées ou supprimées, à moins que ces étiquettes ou mentions ne soient inapplicables à un logiciel modifié ou dérivé donné.
-
-5. Contributions
-Sous réserve d'une entente distincte, toute contribution soumise par un contributeur au concédant pour inclusion dans le logiciel sera soumise aux termes de cette licence.
-
-6. Marques de commerce
-La licence n'accorde aucune permission particulière qui permettrait d'utiliser les marques de commerce du concédant, autre que celle requise permettant d'identifier la provenance du logiciel.
-
-7. Garanties
-Sauf mention contraire, le concédant distribue le logiciel sans aucune garantie, aux risques et périls de l'acquéreur de la copie du logiciel, et ce, sans assurer que le logiciel puisse répondre à un besoin particulier ou puisse donner un résultat quelconque.
-
-Sans lier le concédant d'une quelconque manière, rien n'empêche un licencié d'offrir ou d'exclure des garanties ou du support.
-
-8. Responsabilité
-Le licencié est responsable de tout préjudice résultant de l'exercice des droits accordés par la licence.
-
-Le concédant ne saurait être tenu responsable de dommages subis par le licencié ou par des tiers, pour quelque cause que ce soit en lien avec la licence et les droits qui y sont accordés.
-
-9. Résiliation
-La présente licence est automatiquement résiliée dès que les droits qui y sont accordés ne sont pas exercés conformément aux termes qui y sont stipulés.
-
-Toutefois, si le défaut est corrigé dans un délai de 30 jours de sa prise de connaissance par la personne en défaut, et qu'il s'agit du premier défaut, la licence est accordée de nouveau.
-
-Pour tout défaut subséquent, le consentement exprès du concédant est nécessaire afin que la licence soit accordée de nouveau.
-
-10. Version de la licence
-Le Centre de services partagés du Québec, ses ayants cause ou toute personne qu'il désigne, peuvent diffuser des versions révisées ou modifiées de cette licence. Chaque version recevra un numéro unique. Si un logiciel est déjà soumis aux termes d'une version spécifique, c'est seulement cette version qui liera les parties à la licence.
-
-Le concédant peut aussi choisir de concéder la licence sous la version actuelle ou toute version ultérieure, auquel cas le licencié peut choisir sous quelle version la licence lui est accordée.
-
-11. Divers
-Dans la mesure où le concédant est un ministère, un organisme public ou une personne morale de droit public, créés en vertu d'une loi de l'Assemblée nationale du Québec, la licence est régie par le droit applicable au Québec et en cas de contestation, les tribunaux du Québec seront seuls compétents.
-
-La présente licence peut être distribuée sans conditions particulières. Toutefois, une version modifiée doit être distribuée sous un nom différent. Toute référence au Centre de services partagés du Québec, et, le cas échéant, ses ayant cause, doit être retirée, autre que celle permettant d'identifier la provenance de la licence.
diff --git a/options/license/LiLiQ-R-1.1 b/options/license/LiLiQ-R-1.1
deleted file mode 100644
index 449febd599..0000000000
--- a/options/license/LiLiQ-R-1.1
+++ /dev/null
@@ -1,94 +0,0 @@
-Licence Libre du Québec – Réciprocité (LiLiQ-R)
-
-Version 1.1
-
-1. Préambule
-Cette licence s'applique à tout logiciel distribué dont le titulaire du droit d'auteur précise qu'il est sujet aux termes de la Licence Libre du Québec – Réciprocité (LiLiQ-R) (ci-après appelée la « licence »).
-
-2. Définitions
-Dans la présente licence, à moins que le contexte n'indique un sens différent, on entend par:
-
- « concédant » : le titulaire du droit d'auteur sur le logiciel, ou toute personne dûment autorisée par ce dernier à accorder la présente licence;
- « contributeur » : le titulaire du droit d'auteur ou toute personne autorisée par ce dernier à soumettre au concédant une contribution. Un contributeur dont sa contribution est incorporée au logiciel est considéré comme un concédant en regard de sa contribution;
- « contribution » : tout logiciel original, ou partie de logiciel original soumis et destiné à être incorporé dans le logiciel;
- « distribution » : le fait de délivrer une copie du logiciel;
- « licencié » : toute personne qui possède une copie du logiciel et qui exerce les droits concédés par la licence;
- « logiciel » : une œuvre protégée par le droit d'auteur, telle qu'un programme d'ordinateur et sa documentation, pour laquelle le titulaire du droit d'auteur a précisé qu'elle est sujette aux termes de la présente licence;
- « logiciel dérivé » : tout logiciel original réalisé par un licencié, autre que le logiciel ou un logiciel modifié, qui produit ou reproduit la totalité ou une partie importante du logiciel;
- « logiciel modifié » : toute modification par un licencié de l'un des fichiers source du logiciel ou encore tout nouveau fichier source qui incorpore le logiciel ou une partie importante de ce dernier.
-
-3. Licence de droit d'auteur
-Sous réserve des termes de la licence, le concédant accorde au licencié une licence non exclusive et libre de redevances lui permettant d’exercer les droits suivants sur le logiciel :
-
- 1 Produire ou reproduire la totalité ou une partie importante;
- 2 Exécuter ou représenter la totalité ou une partie importante en public;
- 3 Publier la totalité ou une partie importante.
-
-Cette licence est accordée sans limite territoriale et sans limite de temps.
-
-L'exercice complet de ces droits est sujet à la distribution par le concédant du code source du logiciel, lequel doit être sous une forme permettant d'y apporter des modifications. Le concédant peut aussi distribuer le logiciel accompagné d'une offre de distribuer le code source du logiciel, sans frais supplémentaires, autres que ceux raisonnables afin de permettre la livraison du code source. Cette offre doit être valide pendant une durée raisonnable.
-
-4. Distribution
-Le licencié peut distribuer des copies du logiciel, d'un logiciel modifié ou dérivé, sous réserve de respecter les conditions suivantes :
-
- 1 Le logiciel doit être accompagné d'un exemplaire de cette licence;
- 2 Si le logiciel a été modifié, le licencié doit en faire la mention, de préférence dans chacun des fichiers modifiés dont la nature permet une telle mention;
- 3 Les étiquettes ou mentions faisant état des droits d'auteur, des marques de commerce, des garanties ou de la paternité concernant le logiciel ne doivent pas être modifiées ou supprimées, à moins que ces étiquettes ou mentions ne soient inapplicables à un logiciel modifié ou dérivé donné.
-
-4.1. Réciprocité
-Chaque fois que le licencié distribue le logiciel, le concédant offre au récipiendaire une concession sur le logiciel selon les termes de la présente licence. Le licencié doit offrir une concession selon les termes de la présente licence pour tout logiciel modifié qu'il distribue.
-
-Chaque fois que le licencié distribue le logiciel ou un logiciel modifié, ce dernier doit assumer l'obligation d'en distribuer le code source, de la manière prévue au troisième alinéa de l'article 3.
-
-4.2. Compatibilité
-Dans la mesure où le licencié souhaite distribuer un logiciel modifié combiné à un logiciel assujetti à une licence compatible, mais dont il ne serait pas possible d'en respecter les termes, le concédant offre, en plus de la présente concession, une concession selon les termes de cette licence compatible.
-
-Un licencié qui est titulaire exclusif du droit d'auteur sur le logiciel assujetti à une licence compatible ne peut pas se prévaloir de cette offre. Il en est de même pour toute autre personne dûment autorisée à sous-licencier par le titulaire exclusif du droit d'auteur sur le logiciel assujetti à une licence compatible.
-
-Est considérée comme une licence compatible toute licence libre approuvée ou certifiée par la Free Software Foundation ou l'Open Source Initiative, dont le niveau de réciprocité est comparable ou supérieur à celui de la présente licence, sans toutefois être moindre, notamment :
-
- 1 Common Development and Distribution License (CDDL-1.0)
- 2 Common Public License Version 1.0 (CPL-1.0)
- 3 Contrat de licence de logiciel libre CeCILL, version 2.1 (CECILL-2.1)
- 4 Contrat de licence de logiciel libre CeCILL-C (CECILL-C)
- 5 Eclipse Public License - v 1.0 (EPL-1.0)
- 6 European Union Public License, version 1.1 (EUPL v. 1.1)
- 7 Licence Libre du Québec – Réciprocité forte version 1.1 (LiLiQ-R+ 1.1)
- 8 GNU General Public License Version 2 (GNU GPLv2)
- 9 GNU General Public License Version 3 (GNU GPLv3)
- 10 GNU Lesser General Public License Version 2.1 (GNU LGPLv2.1)
- 11 GNU Lesser General Public License Version 3 (GNU LGPLv3)
- 12 Mozilla Public License Version 2.0 (MPL-2.0)
-
-5. Contributions
-Sous réserve d'une entente distincte, toute contribution soumise par un contributeur au concédant pour inclusion dans le logiciel sera soumise aux termes de cette licence.
-
-6. Marques de commerce
-La licence n'accorde aucune permission particulière qui permettrait d'utiliser les marques de commerce du concédant, autre que celle requise permettant d'identifier la provenance du logiciel.
-
-7. Garanties
-Sauf mention contraire, le concédant distribue le logiciel sans aucune garantie, aux risques et périls de l'acquéreur de la copie du logiciel, et ce, sans assurer que le logiciel puisse répondre à un besoin particulier ou puisse donner un résultat quelconque.
-
-Sans lier le concédant d'une quelconque manière, rien n'empêche un licencié d'offrir ou d'exclure des garanties ou du support.
-
-8. Responsabilité
-Le licencié est responsable de tout préjudice résultant de l'exercice des droits accordés par la licence.
-
-Le concédant ne saurait être tenu responsable du préjudice subi par le licencié ou par des tiers, pour quelque cause que ce soit en lien avec la licence et les droits qui y sont accordés.
-
-9. Résiliation
-La présente licence est résiliée de plein droit dès que les droits qui y sont accordés ne sont pas exercés conformément aux termes qui y sont stipulés.
-
-Toutefois, si le défaut est corrigé dans un délai de 30 jours de sa prise de connaissance par la personne en défaut, et qu'il s'agit du premier défaut, la licence est accordée de nouveau.
-
-Pour tout défaut subséquent, le consentement exprès du concédant est nécessaire afin que la licence soit accordée de nouveau.
-
-10. Version de la licence
-Le Centre de services partagés du Québec, ses ayants cause ou toute personne qu'il désigne, peuvent diffuser des versions révisées ou modifiées de cette licence. Chaque version recevra un numéro unique. Si un logiciel est déjà soumis aux termes d'une version spécifique, c'est seulement cette version qui liera les parties à la licence.
-
-Le concédant peut aussi choisir de concéder la licence sous la version actuelle ou toute version ultérieure, auquel cas le licencié peut choisir sous quelle version la licence lui est accordée.
-
-11. Divers
-Dans la mesure où le concédant est un ministère, un organisme public ou une personne morale de droit public, créés en vertu d'une loi de l'Assemblée nationale du Québec, la licence est régie par le droit applicable au Québec et en cas de contestation, les tribunaux du Québec seront seuls compétents.
-
-La présente licence peut être distribuée sans conditions particulières. Toutefois, une version modifiée doit être distribuée sous un nom différent. Toute référence au Centre de services partagés du Québec, et, le cas échéant, ses ayant droit, doit être retirée, autre que celle permettant d'identifier la provenance de la licence.
diff --git a/options/license/LiLiQ-Rplus-1.1 b/options/license/LiLiQ-Rplus-1.1
deleted file mode 100644
index 58566cb1bb..0000000000
--- a/options/license/LiLiQ-Rplus-1.1
+++ /dev/null
@@ -1,88 +0,0 @@
-Licence Libre du Québec – Réciprocité forte (LiLiQ-R+)
-
-Version 1.1
-
-1. Préambule
-Cette licence s'applique à tout logiciel distribué dont le titulaire du droit d'auteur précise qu'il est sujet aux termes de la Licence Libre du Québec – Réciprocité forte (LiLiQ-R+) (ci-après appelée la « licence »).
-
-2. Définitions
-Dans la présente licence, à moins que le contexte n'indique un sens différent, on entend par:
-
- « concédant » : le titulaire du droit d'auteur sur le logiciel, ou toute personne dûment autorisée par ce dernier à accorder la présente licence;
- « contributeur » : le titulaire du droit d'auteur ou toute personne autorisée par ce dernier à soumettre au concédant une contribution. Un contributeur dont sa contribution est incorporée au logiciel est considéré comme un concédant en regard de sa contribution;
- « contribution » : tout logiciel original, ou partie de logiciel original soumis et destiné à être incorporé dans le logiciel;
- « distribution » : le fait de délivrer une copie du logiciel;
- « licencié » : toute personne qui possède une copie du logiciel et qui exerce les droits concédés par la licence;
- « logiciel » : une œuvre protégée par le droit d'auteur, telle qu'un programme d'ordinateur et sa documentation, pour laquelle le titulaire du droit d'auteur a précisé qu'elle est sujette aux termes de la présente licence;
- « logiciel dérivé » : tout logiciel original réalisé par un licencié, autre que le logiciel ou un logiciel modifié, qui produit ou reproduit la totalité ou une partie importante du logiciel;
- « logiciel modifié » : toute modification par un licencié de l'un des fichiers source du logiciel ou encore tout nouveau fichier source qui incorpore le logiciel ou une partie importante de ce dernier.
-
-3. Licence de droit d'auteur
-Sous réserve des termes de la licence, le concédant accorde au licencié une licence non exclusive et libre de redevances lui permettant d’exercer les droits suivants sur le logiciel :
-
- 1 Produire ou reproduire la totalité ou une partie importante;
- 2 Exécuter ou représenter la totalité ou une partie importante en public;
- 3 Publier la totalité ou une partie importante.
-
-Cette licence est accordée sans limite territoriale et sans limite de temps.
-
-L'exercice complet de ces droits est sujet à la distribution par le concédant du code source du logiciel, lequel doit être sous une forme permettant d'y apporter des modifications. Le concédant peut aussi distribuer le logiciel accompagné d'une offre de distribuer le code source du logiciel, sans frais supplémentaires, autres que ceux raisonnables afin de permettre la livraison du code source. Cette offre doit être valide pendant une durée raisonnable.
-
-4. Distribution
-Le licencié peut distribuer des copies du logiciel, d'un logiciel modifié ou dérivé, sous réserve de respecter les conditions suivantes :
-
- 1 Le logiciel doit être accompagné d'un exemplaire de cette licence;
- 2 Si le logiciel a été modifié, le licencié doit en faire la mention, de préférence dans chacun des fichiers modifiés dont la nature permet une telle mention;
- 3 Les étiquettes ou mentions faisant état des droits d'auteur, des marques de commerce, des garanties ou de la paternité concernant le logiciel ne doivent pas être modifiées ou supprimées, à moins que ces étiquettes ou mentions ne soient inapplicables à un logiciel modifié ou dérivé donné.
-
-4.1. Réciprocité
-Chaque fois que le licencié distribue le logiciel, le concédant offre au récipiendaire une concession sur le logiciel selon les termes de la présente licence. Le licencié doit offrir une concession selon les termes de la présente licence pour tout logiciel modifié ou dérivé qu'il distribue.
-
-Chaque fois que le licencié distribue le logiciel, un logiciel modifié, ou un logiciel dérivé, ce dernier doit assumer l'obligation d'en distribuer le code source, de la manière prévue au troisième alinéa de l'article 3.
-
-4.2. Compatibilité
-Dans la mesure où le licencié souhaite distribuer un logiciel modifié ou dérivé combiné à un logiciel assujetti à une licence compatible, mais dont il ne serait pas possible d'en respecter les termes, le concédant offre, en plus de la présente concession, une concession selon les termes de cette licence compatible.
-
-Un licencié qui est titulaire exclusif du droit d'auteur sur le logiciel assujetti à une licence compatible ne peut pas se prévaloir de cette offre. Il en est de même pour toute autre personne dûment autorisée à sous-licencier par le titulaire exclusif du droit d'auteur sur le logiciel assujetti à une licence compatible.
-
-Est considérée comme une licence compatible toute licence libre approuvée ou certifiée par la Free Software Foundation ou l'Open Source Initiative, dont le niveau de réciprocité est comparable à celui de la présente licence, sans toutefois être moindre, notamment :
-
- 1 Common Public License Version 1.0 (CPL-1.0)
- 2 Contrat de licence de logiciel libre CeCILL, version 2.1 (CECILL-2.1)
- 3 Eclipse Public License - v 1.0 (EPL-1.0)
- 4 European Union Public License, version 1.1 (EUPL v. 1.1)
- 5 GNU General Public License Version 2 (GNU GPLv2)
- 6 GNU General Public License Version 3 (GNU GPLv3)
-
-5. Contributions
-Sous réserve d'une entente distincte, toute contribution soumise par un contributeur au concédant pour inclusion dans le logiciel sera soumise aux termes de cette licence.
-
-6. Marques de commerce
-La licence n'accorde aucune permission particulière qui permettrait d'utiliser les marques de commerce du concédant, autre que celle requise permettant d'identifier la provenance du logiciel.
-
-7. Garanties
-Sauf mention contraire, le concédant distribue le logiciel sans aucune garantie, aux risques et périls de l'acquéreur de la copie du logiciel, et ce, sans assurer que le logiciel puisse répondre à un besoin particulier ou puisse donner un résultat quelconque.
-
-Sans lier le concédant d'une quelconque manière, rien n'empêche un licencié d'offrir ou d'exclure des garanties ou du support.
-
-8. Responsabilité
-Le licencié est responsable de tout préjudice résultant de l'exercice des droits accordés par la licence.
-
-Le concédant ne saurait être tenu responsable du préjudice subi par le licencié ou par des tiers, pour quelque cause que ce soit en lien avec la licence et les droits qui y sont accordés.
-
-9. Résiliation
-La présente licence est résiliée de plein droit dès que les droits qui y sont accordés ne sont pas exercés conformément aux termes qui y sont stipulés.
-
-Toutefois, si le défaut est corrigé dans un délai de 30 jours de sa prise de connaissance par la personne en défaut, et qu'il s'agit du premier défaut, la licence est accordée de nouveau.
-
-Pour tout défaut subséquent, le consentement exprès du concédant est nécessaire afin que la licence soit accordée de nouveau.
-
-10. Version de la licence
-Le Centre de services partagés du Québec, ses ayants cause ou toute personne qu'il désigne, peuvent diffuser des versions révisées ou modifiées de cette licence. Chaque version recevra un numéro unique. Si un logiciel est déjà soumis aux termes d'une version spécifique, c'est seulement cette version qui liera les parties à la licence.
-
-Le concédant peut aussi choisir de concéder la licence sous la version actuelle ou toute version ultérieure, auquel cas le licencié peut choisir sous quelle version la licence lui est accordée.
-
-11. Divers
-Dans la mesure où le concédant est un ministère, un organisme public ou une personne morale de droit public, créés en vertu d'une loi de l'Assemblée nationale du Québec, la licence est régie par le droit applicable au Québec et en cas de contestation, les tribunaux du Québec seront seuls compétents.
-
-La présente licence peut être distribuée sans conditions particulières. Toutefois, une version modifiée doit être distribuée sous un nom différent. Toute référence au Centre de services partagés du Québec, et, le cas échéant, ses ayant cause, doit être retirée, autre que celle permettant d'identifier la provenance de la licence.
diff --git a/options/license/Libpng b/options/license/Libpng
deleted file mode 100644
index 5287855448..0000000000
--- a/options/license/Libpng
+++ /dev/null
@@ -1,76 +0,0 @@
-This copy of the libpng notices is provided for your convenience. In case of any discrepancy between this copy and the notices in the file png.h that is included in the libpng distribution, the latter shall prevail.
-
-COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
-
-If you modify libpng you may insert additional notices immediately following this sentence.
-
-This code is released under the libpng license.
-
-libpng versions 1.2.6, August 15, 2004, through 1.4.5, December 9, 2010, are Copyright (c) 2004, 2006-2010 Glenn Randers-Pehrson, and are distributed according to the same disclaimer and license as libpng-1.2.5 with the following individual added to the list of Contributing Authors
-
- Cosmin Truta
-
-libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, are
-Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are distributed according to the same disclaimer and license as libpng-1.0.6 with the following individuals added to the list of Contributing Authors
-
- Simon-Pierre Cadieux
- Eric S. Raymond
- Gilles Vollant
-
-and with the following additions to the disclaimer:
-
- There is no warranty against interference with your enjoyment of the library or against infringement. There is no warranty that our efforts or the library will fulfill any of your particular purposes or needs. This library is provided with all faults, and the entire risk of satisfactory quality, performance, accuracy, and effort is with the user.
-
-libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
-Copyright (c) 1998, 1999 Glenn Randers-Pehrson, and are distributed according to the same disclaimer and license as libpng-0.96, with the following individuals added to the list of Contributing Authors:
-
- Tom Lane
- Glenn Randers-Pehrson
- Willem van Schaik
-
-libpng versions 0.89, June 1996, through 0.96, May 1997, are
-Copyright (c) 1996, 1997 Andreas Digger
-Distributed according to the same disclaimer and license as libpng-0.88, with the following individuals added to the list of Contributing Authors:
-
- John Bowler
- Kevin Bracey
- Sam Bushell
- Magnus Holmgren
- Greg Roelofs
- Tom Tanner
-
-libpng versions 0.5, May 1995, through 0.88, January 1996, are
-Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
-
-For the purposes of this copyright and license, "Contributing Authors" is defined as the following set of individuals:
-
- Andreas Dilger
- Dave Martindale
- Guy Eric Schalnat
- Paul Schmidt
- Tim Wegner
-
-The PNG Reference Library is supplied "AS IS". The Contributing Authors and Group 42, Inc. disclaim all warranties, expressed or implied, including, without limitation, the warranties of merchantability and of fitness for any purpose. The Contributing Authors and Group 42, Inc. assume no liability for direct, indirect, incidental, special, exemplary, or consequential damages, which may result from the use of the PNG Reference Library, even if advised of the possibility of such damage.
-
-Permission is hereby granted to use, copy, modify, and distribute this source code, or portions hereof, for any purpose, without fee, subject to the following restrictions:
-
-1. The origin of this source code must not be misrepresented.
-
-2. Altered versions must be plainly marked as such and must not be misrepresented as being the original source.
-
-3. This Copyright notice may not be removed or altered from any source or altered source distribution.
-
-The Contributing Authors and Group 42, Inc. specifically permit, without fee, and encourage the use of this source code as a component to supporting the PNG file format in commercial products. If you use this source code in a product, acknowledgment is not required but would be appreciated.
-
-
-A "png_get_copyright" function is available, for convenient use in "about" boxes and the like:
-
- printf("%s",png_get_copyright(NULL));
-
-Also, the PNG logo (in PNG format, of course) is supplied in the files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31).
-
-Libpng is OSI Certified Open Source Software. OSI Certified Open Source is a certification mark of the Open Source Initiative.
-
-Glenn Randers-Pehrson
-glennrp at users.sourceforge.net
-December 9, 2010
diff --git a/options/license/Libtool-exception b/options/license/Libtool-exception
deleted file mode 100644
index 729b1e9530..0000000000
--- a/options/license/Libtool-exception
+++ /dev/null
@@ -1 +0,0 @@
-As a special exception to the GNU General Public License, if you distribute this file as part of a program or library that is built using GNU Libtool, you may include this file under the same distribution terms that you use for the rest of that program.
diff --git a/options/license/Linux-OpenIB b/options/license/Linux-OpenIB
deleted file mode 100644
index 03ccf61ccc..0000000000
--- a/options/license/Linux-OpenIB
+++ /dev/null
@@ -1,18 +0,0 @@
-Redistribution and use in source and binary forms, with or
-without modification, are permitted provided that the following
-conditions are met:
-
-- Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-- Redistributions in binary form must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation
-and/or other materials provided with the distribution.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/options/license/Linux-man-pages-1-para b/options/license/Linux-man-pages-1-para
deleted file mode 100644
index 6eff9081a0..0000000000
--- a/options/license/Linux-man-pages-1-para
+++ /dev/null
@@ -1,4 +0,0 @@
-Permission is granted to distribute possibly modified
-copies of this page provided the header is included
-verbatim, and in case of nontrivial modification author
-and date of the modification is added to the header.
diff --git a/options/license/Linux-man-pages-copyleft b/options/license/Linux-man-pages-copyleft
deleted file mode 100644
index 764635ae5c..0000000000
--- a/options/license/Linux-man-pages-copyleft
+++ /dev/null
@@ -1,21 +0,0 @@
-Copyright (c) <year> <owner> All rights reserved.
-
-Permission is granted to make and distribute verbatim copies of this
-manual provided the copyright notice and this permission notice are
-preserved on all copies.
-
-Permission is granted to copy and distribute modified versions of
-this manual under the conditions for verbatim copying, provided that
-the entire resulting derived work is distributed under the terms of
-a permission notice identical to this one.
-
-Since the Linux kernel and libraries are constantly changing, this
-manual page may be incorrect or out-of-date. The author(s) assume
-no responsibility for errors or omissions, or for damages resulting
-from the use of the information contained herein. The author(s) may
-not have taken the same level of care in the production of this
-manual, which is licensed free of charge, as they might when working
-professionally.
-
-Formatted or processed versions of this manual, if unaccompanied by
-the source, must acknowledge the copyright and authors of this work.
diff --git a/options/license/Linux-man-pages-copyleft-2-para b/options/license/Linux-man-pages-copyleft-2-para
deleted file mode 100644
index b0871675b3..0000000000
--- a/options/license/Linux-man-pages-copyleft-2-para
+++ /dev/null
@@ -1,8 +0,0 @@
-Permission is granted to make and distribute verbatim copies of this
-manual provided the copyright notice and this permission notice are
-preserved on all copies.
-
-Permission is granted to copy and distribute modified versions of this
-manual under the conditions for verbatim copying, provided that the
-entire resulting derived work is distributed under the terms of a
-permission notice identical to this one.
diff --git a/options/license/Linux-man-pages-copyleft-var b/options/license/Linux-man-pages-copyleft-var
deleted file mode 100644
index 1742303553..0000000000
--- a/options/license/Linux-man-pages-copyleft-var
+++ /dev/null
@@ -1,16 +0,0 @@
-Permission is granted to make and distribute verbatim copies of
-this manual provided the copyright notice and this permission
-notice are preserved on all copies.
-
-Permission is granted to copy and distribute modified versions of
-this manual under the conditions for verbatim copying, provided
-that the entire resulting derived work is distributed under the
-terms of a permission notice identical to this one.
-
-Since the Linux kernel and libraries are constantly changing, this
-manual page may be incorrect or out-of-date. The author(s) assume
-no responsibility for errors or omissions, or for damages resulting
-from the use of the information contained herein.
-
-Formatted or processed versions of this manual, if unaccompanied by
-the source, must acknowledge the copyright and authors of this work.
diff --git a/options/license/Linux-syscall-note b/options/license/Linux-syscall-note
deleted file mode 100644
index fcd056364e..0000000000
--- a/options/license/Linux-syscall-note
+++ /dev/null
@@ -1,12 +0,0 @@
- NOTE! This copyright does *not* cover user programs that use kernel
- services by normal system calls - this is merely considered normal use
- of the kernel, and does *not* fall under the heading of "derived work".
- Also note that the GPL below is copyrighted by the Free Software
- Foundation, but the instance of code that it refers to (the Linux
- kernel) is copyrighted by me and others who actually wrote it.
-
- Also note that the only valid version of the GPL as far as the kernel
- is concerned is _this_ particular version of the license (ie v2, not
- v2.2 or v3.x or whatever), unless explicitly otherwise stated.
-
- Linus Torvalds
diff --git a/options/license/Lucida-Bitmap-Fonts b/options/license/Lucida-Bitmap-Fonts
deleted file mode 100644
index 35be63ed33..0000000000
--- a/options/license/Lucida-Bitmap-Fonts
+++ /dev/null
@@ -1,53 +0,0 @@
-This is the LEGAL NOTICE pertaining to the Lucida fonts from Bigelow & Holmes:
-
- NOTICE TO USER: The source code, including the glyphs or icons
- forming a par of the OPEN LOOK TM Graphic User Interface, on this
- tape and in these files is copyrighted under U.S. and international
- laws. Sun Microsystems, Inc. of Mountain View, California owns
- the copyright and has design patents pending on many of the icons.
- AT&T is the owner of the OPEN LOOK trademark associated with the
- materials on this tape. Users and possessors of this source code
- are hereby granted a nonexclusive, royalty-free copyright and
- design patent license to use this code in individual and
- commercial software. A royalty-free, nonexclusive trademark
- license to refer to the code and output as "OPEN LOOK" compatible
- is available from AT&T if, and only if, the appearance of the
- icons or glyphs is not changed in any manner except as absolutely
- necessary to accommodate the standard resolution of the screen or
- other output device, the code and output is not changed except as
- authorized herein, and the code and output is validated by AT&T.
- Bigelow & Holmes is the owner of the Lucida (R) trademark for the
- fonts and bit-mapped images associated with the materials on this
- tape. Users are granted a royalty-free, nonexclusive license to use
- the trademark only to identify the fonts and bit-mapped images if,
- and only if, the fonts and bit-mapped images are not modified in any
- way by the user.
-
- Any use of this source code must include, in the user documentation
- and internal comments to the code, notices to the end user as
- follows:
-
- (c) Copyright 1989 Sun Microsystems, Inc. Sun design patents
- pending in the U.S. and foreign countries. OPEN LOOK is a
- trademark of AT&T. Used by written permission of the owners.
-
- (c) Copyright Bigelow & Holmes 1986, 1985. Lucida is a registered
- trademark of Bigelow & Holmes. Permission to use the Lucida
- trademark is hereby granted only in association with the images
- and fonts described in this file.
-
- SUN MICROSYSTEMS, INC., AT&T, AND BIGELOW & HOLMES
- MAKE NO REPRESENTATIONS ABOUT THE SUITABILITY OF
- THIS SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS"
- WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND.
- SUN MICROSYSTEMS, INC., AT&T AND BIGELOW & HOLMES,
- SEVERALLY AND INDIVIDUALLY, DISCLAIM ALL WARRANTIES
- WITH REGARD TO THIS SOURCE CODE, INCLUDING ALL IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- PARTICULAR PURPOSE. IN NO EVENT SHALL SUN MICROSYSTEMS,
- INC., AT&T OR BIGELOW & HOLMES BE LIABLE FOR ANY
- SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
- OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
- OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
- WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE.
diff --git a/options/license/MIPS b/options/license/MIPS
deleted file mode 100644
index cf57a05639..0000000000
--- a/options/license/MIPS
+++ /dev/null
@@ -1,4 +0,0 @@
-Copyright (c) 1992, 1991, 1990 MIPS Computer Systems, Inc.
-MIPS Computer Systems, Inc. grants reproduction and use
-rights to all parties, PROVIDED that this comment is
-maintained in the copy.
diff --git a/options/license/MIT-CMU b/options/license/MIT-CMU
deleted file mode 100644
index 0ca287d982..0000000000
--- a/options/license/MIT-CMU
+++ /dev/null
@@ -1,7 +0,0 @@
-<copyright notice>
-
-By obtaining, using, and/or copying this software and/or its associated documentation, you agree that you have read, understood, and will comply with the following terms and conditions:
-
-Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holder not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.
-
-THE COPYRIGHT HOLDER DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM THE LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/options/license/MIT-Click b/options/license/MIT-Click
deleted file mode 100644
index 82054edc39..0000000000
--- a/options/license/MIT-Click
+++ /dev/null
@@ -1,30 +0,0 @@
-Portions of this software are subject to the license below. The relevant
-source files are clearly marked; they refer to this file using the phrase
-"the Click LICENSE file". This license is an MIT license, plus a clause
-(taken from the W3C license) requiring prior written permission to use our
-names in publicity.
-
-===========================================================================
-
-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 name and trademarks of copyright holders may NOT be used in advertising
-or publicity pertaining to the Software without specific, written prior
-permission. Title to copyright in this Software and any associated
-documentation will at all times remain with copyright holders.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
diff --git a/options/license/MIT-Festival b/options/license/MIT-Festival
deleted file mode 100644
index 6ec072db0a..0000000000
--- a/options/license/MIT-Festival
+++ /dev/null
@@ -1,22 +0,0 @@
-Permission is hereby granted, free of charge, to use and distribute
-this software and its documentation without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of this work, and to
-permit persons to whom this work is furnished to do so, subject to
-the following conditions:
-1. The code must retain the above copyright notice, this list of
-conditions and the following disclaimer.
-2. Any modifications must be clearly marked as such.
-3. Original authors' names are not deleted.
-4. The authors' names are not used to endorse or promote products
-derived from this software without specific prior written
-permission.
-THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK
-DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
-ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
-SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE
-FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
-AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
-ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-THIS SOFTWARE.
diff --git a/options/license/MIT-Khronos-old b/options/license/MIT-Khronos-old
deleted file mode 100644
index 430863bc98..0000000000
--- a/options/license/MIT-Khronos-old
+++ /dev/null
@@ -1,23 +0,0 @@
-Copyright (c) 2014-2020 The Khronos Group Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and/or associated documentation files (the "Materials"),
-to deal in the Materials without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Materials, and to permit persons to whom the
-Materials are 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 Materials.
-
-MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
-STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
-HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
-
-THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
-IN THE MATERIALS.
diff --git a/options/license/MIT-Modern-Variant b/options/license/MIT-Modern-Variant
deleted file mode 100644
index d5075a3c96..0000000000
--- a/options/license/MIT-Modern-Variant
+++ /dev/null
@@ -1,17 +0,0 @@
-Permission is hereby granted, without written agreement and without
-license or royalty fees, to use, copy, modify, and distribute this
-software and its documentation for any purpose, provided that the
-above copyright notice and the following two paragraphs appear in
-all copies of this software.
-
-IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
-DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
-ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
-IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGE.
-
-THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
-BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
-FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
-ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
-PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
diff --git a/options/license/MIT-Wu b/options/license/MIT-Wu
deleted file mode 100644
index 86eec3c517..0000000000
--- a/options/license/MIT-Wu
+++ /dev/null
@@ -1,28 +0,0 @@
-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/MIT-advertising b/options/license/MIT-advertising
deleted file mode 100644
index 4a991a734d..0000000000
--- a/options/license/MIT-advertising
+++ /dev/null
@@ -1,7 +0,0 @@
-Copyright (C) 2000-2008 Carsten Haitzler, Geoff Harrison and various contributors Copyright (C) 2004-2008 Kim Woelders
-
-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 of the Software, its documentation and marketing & publicity materials, and acknowledgment shall be given in the documentation, materials and software packages that this Software was used.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/options/license/MIT-enna b/options/license/MIT-enna
deleted file mode 100644
index 6d6dd6032d..0000000000
--- a/options/license/MIT-enna
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright (C) 2000 Carsten Haitzler and various contributors (see AUTHORS)
-
-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 of the Software and its Copyright notices. In addition publicly documented acknowledgment must be given that this software has been used if no source code of this software is made available publicly. This includes acknowledgments in either Copyright notices, Manuals, Publicity and Marketing documents or any documentation provided with any product containing this software. This License does not apply to any software that links to the libraries provided by this software (statically or dynamically), but only to the software provided.
-
-Please see the COPYING.PLAIN for a plain-english explanation of this notice and it's intent.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/options/license/MIT-feh b/options/license/MIT-feh
deleted file mode 100644
index 33412bad7c..0000000000
--- a/options/license/MIT-feh
+++ /dev/null
@@ -1,5 +0,0 @@
-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 of the Software and its documentation and acknowledgment shall be given in the documentation and software packages that this Software was used.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/options/license/MIT-open-group b/options/license/MIT-open-group
deleted file mode 100644
index ff185d30ed..0000000000
--- a/options/license/MIT-open-group
+++ /dev/null
@@ -1,23 +0,0 @@
-Copyright <yyyy, yyyy> The Open Group
-
-Permission to use, copy, modify, distribute, and sell this software and
-its documentation for any purpose is hereby granted without fee,
-provided that the above copyright notice appear in all copies and that
-both that copyright notice and this permission notice appear in
-supporting documentation.
-
-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", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL BE LIABLE FOR ANY CLAIM, DAMAGES
-OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
-THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-Except as contained in this notice, the name of The Open Group
-shall not be used in advertising or otherwise to promote the sale, use
-or other dealings in this Software without prior written authorization
-from The Open Group.
diff --git a/options/license/MIT-testregex b/options/license/MIT-testregex
deleted file mode 100644
index 321b4bf9bb..0000000000
--- a/options/license/MIT-testregex
+++ /dev/null
@@ -1,17 +0,0 @@
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of THIS SOFTWARE FILE (the "Software"), to deal in the Software
- * without restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, and/or sell copies of the
- * Software, and to permit persons to whom the Software is furnished to do
- * so, subject to the following disclaimer:
- *
- * THIS SOFTWARE IS PROVIDED BY AT&T ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL AT&T BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/MITNFA b/options/license/MITNFA
deleted file mode 100644
index 6d44edb3db..0000000000
--- a/options/license/MITNFA
+++ /dev/null
@@ -1,7 +0,0 @@
-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.
-
-Distributions of all or part of the Software intended to be used by the recipients as they would use the unmodified Software, containing modifications that substantially alter, remove, or disable functionality of the Software, outside of the documented configuration mechanisms provided by the Software, shall be modified such that the Original Author's bug reporting email addresses and urls are either replaced with the contact information of the parties responsible for the changes, or removed entirely.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/options/license/MMIXware b/options/license/MMIXware
deleted file mode 100644
index 04e0814208..0000000000
--- a/options/license/MMIXware
+++ /dev/null
@@ -1,17 +0,0 @@
-copyright 1999 Donald E. Knuth
-
-This file may be freely copied and distributed, provided that
-no changes whatsoever are made. All users are asked to help keep
-the MMIXware files consistent and ``uncorrupted,''
-identical everywhere in the world. Changes are permissible only
-if the modified file is given a new name, different from the names of
-existing files in the MMIXware package,
-and only if the modified file is clearly identified
-as not being part of that package.
-(The CWEB system has a ``change file'' facility by
-which users can easily make minor alterations without modifying
-the master source files in any way. Everybody is supposed to use
-change files instead of changing the files.)
-The author has tried his best to produce correct and useful programs,
-in order to help promote computer science research,
-but no warranty of any kind should be assumed.
diff --git a/options/license/MPEG-SSG b/options/license/MPEG-SSG
deleted file mode 100644
index a0b6f4ffff..0000000000
--- a/options/license/MPEG-SSG
+++ /dev/null
@@ -1,23 +0,0 @@
-Copyright (C) 1994, MPEG Software Simulation Group. All Rights Reserved. */
-
-Disclaimer of Warranty
-
-These software programs are available to the user without any license fee or
-royalty on an "as is" basis. The MPEG Software Simulation Group disclaims
-any and all warranties, whether express, implied, or statuary, including any
-implied warranties or merchantability or of fitness for a particular
-purpose. In no event shall the copyright-holder be liable for any
-incidental, punitive, or consequential damages of any kind whatsoever
-arising from the use of these programs.
-
-This disclaimer of warranty extends to the user of these programs and user's
-customers, employees, agents, transferees, successors, and assigns.
-
-The MPEG Software Simulation Group does not represent or warrant that the
-programs furnished hereunder are free of infringement of any third-party
-patents.
-
-Commercial implementations of MPEG-1 and MPEG-2 video, including shareware,
-are subject to royalty fees to patent holders. Many of these patents are
-general enough such that they are unavoidable regardless of implementation
-design.
diff --git a/options/license/MPL-1.0 b/options/license/MPL-1.0
deleted file mode 100644
index 5ffd54be97..0000000000
--- a/options/license/MPL-1.0
+++ /dev/null
@@ -1,123 +0,0 @@
-MOZILLA PUBLIC LICENSE
-Version 1.0
-
-1. Definitions.
-
- 1.1. ``Contributor'' means each entity that creates or contributes to the creation of Modifications.
-
- 1.2. ``Contributor Version'' means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
- 1.3. ``Covered Code'' means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
- 1.4. ``Electronic Distribution Mechanism'' means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
- 1.5. ``Executable'' means Covered Code in any form other than Source Code.
-
- 1.6. ``Initial Developer'' means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
- 1.7. ``Larger Work'' means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.8. ``License'' means this document.
-
- 1.9. ``Modifications'' means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
-
- A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
-
- B. Any new file that contains any part of the Original Code or previous Modifications.
-
- 1.10. ``Original Code'' means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
- 1.11. ``Source Code'' means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or a list of source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
- 1.12. ``You'' means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, ``You'' includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, ``control'' means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
-2. Source Code License.
-
- 2.1. The Initial Developer Grant.
-The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, or as part of a Larger Work; and
-
- (b) under patents now or hereafter owned or controlled by Initial Developer, to make, have made, use and sell (``Utilize'') the Original Code (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Original Code (or portions thereof) and not to any greater extent that may be necessary to Utilize further Modifications or combinations.
-
- 2.2. Contributor Grant.
-Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code or as part of a Larger Work; and
-
- (b) under patents now or hereafter owned or controlled by Contributor, to Utilize the Contributor Version (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Contributor Version (or portions thereof), and not to any greater extent that may be necessary to Utilize further Modifications or combinations.
-
-3. Distribution Obligations.
-
- 3.1. Application of License.
- The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
- 3.2. Availability of Source Code.
- Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
- 3.3. Description of Modifications.
- You must cause all Covered Code to which you contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
- 3.4. Intellectual Property Matters
-
- (a) Third Party Claims.
- If You have knowledge that a party claims an intellectual property right in particular functionality or code (or its utilization under this License), you must include a text file with the source code distribution titled ``LEGAL'' which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after You make Your Modification available as described in Section 3.2, You shall promptly modify the LEGAL file in all copies You make available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
-
- (b) Contributor APIs.
- If Your Modification is an application programming interface and You own or control patents which are reasonably necessary to implement that API, you must also include this information in the LEGAL file.
-
- 3.5. Required Notices.
- You must duplicate the notice in Exhibit A in each file of the Source Code, and this License in any documentation for the Source Code, where You describe recipients' rights relating to Covered Code. If You created one or more Modification(s), You may add your name as a Contributor to the notice described in Exhibit A. If it is not possible to put such notice in a particular Source Code file due to its structure, then you must include such notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
- 3.6. Distribution of Executable Versions.
- You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
- 3.7. Larger Works.
- You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Application of this License.
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A, and to related Covered Code.
-
-6. Versions of the License.
-
- 6.1. New Versions.
- Netscape Communications Corporation (``Netscape'') may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
- 6.2. Effect of New Versions.
- Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License.
-
- 6.3. Derivative Works.
- If you create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), you must (a) rename Your license so that the phrases ``Mozilla'', ``MOZILLAPL'', ``MOZPL'', ``Netscape'', ``NPL'' or any confusingly similar phrase do not appear anywhere in your license and (b) otherwise make it clear that your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
-7. DISCLAIMER OF WARRANTY.
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN ``AS IS'' BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-9. LIMITATION OF LIABILITY.
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO YOU OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS.
-The Covered Code is a ``commercial item,'' as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of ``commercial computer software'' and ``commercial computer software documentation,'' as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
-11. MISCELLANEOUS.
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in, the United States of America: (a) unless otherwise agreed in writing, all disputes relating to this License (excepting any dispute relating to intellectual property rights) shall be subject to final and binding arbitration, with the losing party paying all costs of arbitration; (b) any arbitration relating to this Agreement shall be held in Santa Clara County, California, under the auspices of JAMS/EndDispute; and (c) any litigation relating to this Agreement shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-12. RESPONSIBILITY FOR CLAIMS.
-Except in cases where another Contributor has failed to comply with Section 3.4, You are responsible for damages arising, directly or indirectly, out of Your utilization of rights under this License, based on the number of copies of Covered Code you made available, the revenues you received from utilizing such rights, and other relevant factors. You agree to work with affected parties to distribute responsibility on an equitable basis.
-
-EXHIBIT A.
-
-``The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-The Original Code is ______________________________________.
-
-The Initial Developer of the Original Code is ________________________. Portions created by ______________________ are Copyright (C) ______ _______________________. All Rights Reserved.
-
-Contributor(s): ______________________________________.''
diff --git a/options/license/MPL-1.1 b/options/license/MPL-1.1
deleted file mode 100644
index e1c842828c..0000000000
--- a/options/license/MPL-1.1
+++ /dev/null
@@ -1,143 +0,0 @@
-Mozilla Public License Version 1.1
-
-1. Definitions.
-
- 1.0.1. "Commercial Use" means distribution or otherwise making the Covered Code available to a third party.
-
- 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications.
-
- 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
- 1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
- 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
- 1.5. "Executable" means Covered Code in any form other than Source Code.
-
- 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
- 1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.8. "License" means this document.
-
- 1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
- 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
-Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
-Any new file that contains any part of the Original Code or previous Modifications.
-
- 1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
- 1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
- 1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
- 1.12. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2. Source Code License.
-
- 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- a. under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and
- b. under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).
- c. the licenses granted in this Section 2.1 (a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.
- d. Notwithstanding Section 2.1 (b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices.
-
- 2.2. Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license
-
- a. under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and
- b. under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
- c. the licenses granted in Sections 2.2 (a) and 2.2 (b) are effective on the date Contributor first makes Commercial Use of the Covered Code.
- d. Notwithstanding Section 2.2 (b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor.
-
-3. Distribution Obligations.
-
- 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
- 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
- 3.3. Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
- 3.4. Intellectual Property Matters
-
- (a) Third Party Claims
- If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
-
- (b) Contributor APIs
- If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file.
-
- (c) Representations.
- Contributor represents that, except as disclosed pursuant to Section 3.4 (a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.
-
- 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
- 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Sections 3.1, 3.2, 3.3, 3.4 and 3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
- 3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Application of this License.
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code.
-
-6. Versions of the License.
-
- 6.1. New Versions
- Netscape Communications Corporation ("Netscape") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
- 6.2. Effect of New Versions
- Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License.
-
- 6.3. Derivative Works
- If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", "MPL", "NPL" or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
-7. DISCLAIMER OF WARRANTY
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. Termination
-
- 8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
- 8.2. If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant") alleging that:
-
- a. such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.
- b. any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant.
-
- 8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
- 8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. government end users
-The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
-11. Miscellaneous
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-12. Responsibility for claims
-As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-13. Multiple-licensed code
-Initial Developer may designate portions of the Covered Code as "Multiple-Licensed". "Multiple-Licensed" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the MPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A.
-
-Exhibit A - Mozilla Public License.
-
-"The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-The Original Code is ______________________________________.
-
-The Initial Developer of the Original Code is ________________________.
-Portions created by ______________________ are Copyright (C) ______
-_______________________. All Rights Reserved.
-
-Contributor(s): ______________________________________.
-
-Alternatively, the contents of this file may be used under the terms of the _____ license (the "[___] License"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the [___] License."
-
-NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.
diff --git a/options/license/MPL-2.0-no-copyleft-exception b/options/license/MPL-2.0-no-copyleft-exception
deleted file mode 100644
index ee6256cdb6..0000000000
--- a/options/license/MPL-2.0-no-copyleft-exception
+++ /dev/null
@@ -1,373 +0,0 @@
-Mozilla Public License Version 2.0
-==================================
-
-1. Definitions
---------------
-
-1.1. "Contributor"
- means each individual or legal entity that creates, contributes to
- the creation of, or owns Covered Software.
-
-1.2. "Contributor Version"
- means the combination of the Contributions of others (if any) used
- by a Contributor and that particular Contributor's Contribution.
-
-1.3. "Contribution"
- means Covered Software of a particular Contributor.
-
-1.4. "Covered Software"
- means Source Code Form to which the initial Contributor has attached
- the notice in Exhibit A, the Executable Form of such Source Code
- Form, and Modifications of such Source Code Form, in each case
- including portions thereof.
-
-1.5. "Incompatible With Secondary Licenses"
- means
-
- (a) that the initial Contributor has attached the notice described
- in Exhibit B to the Covered Software; or
-
- (b) that the Covered Software was made available under the terms of
- version 1.1 or earlier of the License, but not also under the
- terms of a Secondary License.
-
-1.6. "Executable Form"
- means any form of the work other than Source Code Form.
-
-1.7. "Larger Work"
- means a work that combines Covered Software with other material, in
- a separate file or files, that is not Covered Software.
-
-1.8. "License"
- means this document.
-
-1.9. "Licensable"
- means having the right to grant, to the maximum extent possible,
- whether at the time of the initial grant or subsequently, any and
- all of the rights conveyed by this License.
-
-1.10. "Modifications"
- means any of the following:
-
- (a) any file in Source Code Form that results from an addition to,
- deletion from, or modification of the contents of Covered
- Software; or
-
- (b) any new file in Source Code Form that contains any Covered
- Software.
-
-1.11. "Patent Claims" of a Contributor
- means any patent claim(s), including without limitation, method,
- process, and apparatus claims, in any patent Licensable by such
- Contributor that would be infringed, but for the grant of the
- License, by the making, using, selling, offering for sale, having
- made, import, or transfer of either its Contributions or its
- Contributor Version.
-
-1.12. "Secondary License"
- means either the GNU General Public License, Version 2.0, the GNU
- Lesser General Public License, Version 2.1, the GNU Affero General
- Public License, Version 3.0, or any later versions of those
- licenses.
-
-1.13. "Source Code Form"
- means the form of the work preferred for making modifications.
-
-1.14. "You" (or "Your")
- means an individual or a legal entity exercising rights under this
- License. For legal entities, "You" includes any entity that
- controls, is controlled by, or is under common control with You. For
- purposes of this definition, "control" means (a) the power, direct
- or indirect, to cause the direction or management of such entity,
- whether by contract or otherwise, or (b) ownership of more than
- fifty percent (50%) of the outstanding shares or beneficial
- ownership of such entity.
-
-2. License Grants and Conditions
---------------------------------
-
-2.1. Grants
-
-Each Contributor hereby grants You a world-wide, royalty-free,
-non-exclusive license:
-
-(a) under intellectual property rights (other than patent or trademark)
- Licensable by such Contributor to use, reproduce, make available,
- modify, display, perform, distribute, and otherwise exploit its
- Contributions, either on an unmodified basis, with Modifications, or
- as part of a Larger Work; and
-
-(b) under Patent Claims of such Contributor to make, use, sell, offer
- for sale, have made, import, and otherwise transfer either its
- Contributions or its Contributor Version.
-
-2.2. Effective Date
-
-The licenses granted in Section 2.1 with respect to any Contribution
-become effective for each Contribution on the date the Contributor first
-distributes such Contribution.
-
-2.3. Limitations on Grant Scope
-
-The licenses granted in this Section 2 are the only rights granted under
-this License. No additional rights or licenses will be implied from the
-distribution or licensing of Covered Software under this License.
-Notwithstanding Section 2.1(b) above, no patent license is granted by a
-Contributor:
-
-(a) for any code that a Contributor has removed from Covered Software;
- or
-
-(b) for infringements caused by: (i) Your and any other third party's
- modifications of Covered Software, or (ii) the combination of its
- Contributions with other software (except as part of its Contributor
- Version); or
-
-(c) under Patent Claims infringed by Covered Software in the absence of
- its Contributions.
-
-This License does not grant any rights in the trademarks, service marks,
-or logos of any Contributor (except as may be necessary to comply with
-the notice requirements in Section 3.4).
-
-2.4. Subsequent Licenses
-
-No Contributor makes additional grants as a result of Your choice to
-distribute the Covered Software under a subsequent version of this
-License (see Section 10.2) or under the terms of a Secondary License (if
-permitted under the terms of Section 3.3).
-
-2.5. Representation
-
-Each Contributor represents that the Contributor believes its
-Contributions are its original creation(s) or it has sufficient rights
-to grant the rights to its Contributions conveyed by this License.
-
-2.6. Fair Use
-
-This License is not intended to limit any rights You have under
-applicable copyright doctrines of fair use, fair dealing, or other
-equivalents.
-
-2.7. Conditions
-
-Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
-in Section 2.1.
-
-3. Responsibilities
--------------------
-
-3.1. Distribution of Source Form
-
-All distribution of Covered Software in Source Code Form, including any
-Modifications that You create or to which You contribute, must be under
-the terms of this License. You must inform recipients that the Source
-Code Form of the Covered Software is governed by the terms of this
-License, and how they can obtain a copy of this License. You may not
-attempt to alter or restrict the recipients' rights in the Source Code
-Form.
-
-3.2. Distribution of Executable Form
-
-If You distribute Covered Software in Executable Form then:
-
-(a) such Covered Software must also be made available in Source Code
- Form, as described in Section 3.1, and You must inform recipients of
- the Executable Form how they can obtain a copy of such Source Code
- Form by reasonable means in a timely manner, at a charge no more
- than the cost of distribution to the recipient; and
-
-(b) You may distribute such Executable Form under the terms of this
- License, or sublicense it under different terms, provided that the
- license for the Executable Form does not attempt to limit or alter
- the recipients' rights in the Source Code Form under this License.
-
-3.3. Distribution of a Larger Work
-
-You may create and distribute a Larger Work under terms of Your choice,
-provided that You also comply with the requirements of this License for
-the Covered Software. If the Larger Work is a combination of Covered
-Software with a work governed by one or more Secondary Licenses, and the
-Covered Software is not Incompatible With Secondary Licenses, this
-License permits You to additionally distribute such Covered Software
-under the terms of such Secondary License(s), so that the recipient of
-the Larger Work may, at their option, further distribute the Covered
-Software under the terms of either this License or such Secondary
-License(s).
-
-3.4. Notices
-
-You may not remove or alter the substance of any license notices
-(including copyright notices, patent notices, disclaimers of warranty,
-or limitations of liability) contained within the Source Code Form of
-the Covered Software, except that You may alter any license notices to
-the extent required to remedy known factual inaccuracies.
-
-3.5. Application of Additional Terms
-
-You may choose to offer, and to charge a fee for, warranty, support,
-indemnity or liability obligations to one or more recipients of Covered
-Software. However, You may do so only on Your own behalf, and not on
-behalf of any Contributor. You must make it absolutely clear that any
-such warranty, support, indemnity, or liability obligation is offered by
-You alone, and You hereby agree to indemnify every Contributor for any
-liability incurred by such Contributor as a result of warranty, support,
-indemnity or liability terms You offer. You may include additional
-disclaimers of warranty and limitations of liability specific to any
-jurisdiction.
-
-4. Inability to Comply Due to Statute or Regulation
----------------------------------------------------
-
-If it is impossible for You to comply with any of the terms of this
-License with respect to some or all of the Covered Software due to
-statute, judicial order, or regulation then You must: (a) comply with
-the terms of this License to the maximum extent possible; and (b)
-describe the limitations and the code they affect. Such description must
-be placed in a text file included with all distributions of the Covered
-Software under this License. Except to the extent prohibited by statute
-or regulation, such description must be sufficiently detailed for a
-recipient of ordinary skill to be able to understand it.
-
-5. Termination
---------------
-
-5.1. The rights granted under this License will terminate automatically
-if You fail to comply with any of its terms. However, if You become
-compliant, then the rights granted under this License from a particular
-Contributor are reinstated (a) provisionally, unless and until such
-Contributor explicitly and finally terminates Your grants, and (b) on an
-ongoing basis, if such Contributor fails to notify You of the
-non-compliance by some reasonable means prior to 60 days after You have
-come back into compliance. Moreover, Your grants from a particular
-Contributor are reinstated on an ongoing basis if such Contributor
-notifies You of the non-compliance by some reasonable means, this is the
-first time You have received notice of non-compliance with this License
-from such Contributor, and You become compliant prior to 30 days after
-Your receipt of the notice.
-
-5.2. If You initiate litigation against any entity by asserting a patent
-infringement claim (excluding declaratory judgment actions,
-counter-claims, and cross-claims) alleging that a Contributor Version
-directly or indirectly infringes any patent, then the rights granted to
-You by any and all Contributors for the Covered Software under Section
-2.1 of this License shall terminate.
-
-5.3. In the event of termination under Sections 5.1 or 5.2 above, all
-end user license agreements (excluding distributors and resellers) which
-have been validly granted by You or Your distributors under this License
-prior to termination shall survive termination.
-
-************************************************************************
-* *
-* 6. Disclaimer of Warranty *
-* ------------------------- *
-* *
-* Covered Software is provided under this License on an "as is" *
-* basis, without warranty of any kind, either expressed, implied, or *
-* statutory, including, without limitation, warranties that the *
-* Covered Software is free of defects, merchantable, fit for a *
-* particular purpose or non-infringing. The entire risk as to the *
-* quality and performance of the Covered Software is with You. *
-* Should any Covered Software prove defective in any respect, You *
-* (not any Contributor) assume the cost of any necessary servicing, *
-* repair, or correction. This disclaimer of warranty constitutes an *
-* essential part of this License. No use of any Covered Software is *
-* authorized under this License except under this disclaimer. *
-* *
-************************************************************************
-
-************************************************************************
-* *
-* 7. Limitation of Liability *
-* -------------------------- *
-* *
-* Under no circumstances and under no legal theory, whether tort *
-* (including negligence), contract, or otherwise, shall any *
-* Contributor, or anyone who distributes Covered Software as *
-* permitted above, be liable to You for any direct, indirect, *
-* special, incidental, or consequential damages of any character *
-* including, without limitation, damages for lost profits, loss of *
-* goodwill, work stoppage, computer failure or malfunction, or any *
-* and all other commercial damages or losses, even if such party *
-* shall have been informed of the possibility of such damages. This *
-* limitation of liability shall not apply to liability for death or *
-* personal injury resulting from such party's negligence to the *
-* extent applicable law prohibits such limitation. Some *
-* jurisdictions do not allow the exclusion or limitation of *
-* incidental or consequential damages, so this exclusion and *
-* limitation may not apply to You. *
-* *
-************************************************************************
-
-8. Litigation
--------------
-
-Any litigation relating to this License may be brought only in the
-courts of a jurisdiction where the defendant maintains its principal
-place of business and such litigation shall be governed by laws of that
-jurisdiction, without reference to its conflict-of-law provisions.
-Nothing in this Section shall prevent a party's ability to bring
-cross-claims or counter-claims.
-
-9. Miscellaneous
-----------------
-
-This License represents the complete agreement concerning the subject
-matter hereof. If any provision of this License is held to be
-unenforceable, such provision shall be reformed only to the extent
-necessary to make it enforceable. Any law or regulation which provides
-that the language of a contract shall be construed against the drafter
-shall not be used to construe this License against a Contributor.
-
-10. Versions of the License
----------------------------
-
-10.1. New Versions
-
-Mozilla Foundation is the license steward. Except as provided in Section
-10.3, no one other than the license steward has the right to modify or
-publish new versions of this License. Each version will be given a
-distinguishing version number.
-
-10.2. Effect of New Versions
-
-You may distribute the Covered Software under the terms of the version
-of the License under which You originally received the Covered Software,
-or under the terms of any subsequent version published by the license
-steward.
-
-10.3. Modified Versions
-
-If you create software not governed by this License, and you want to
-create a new license for such software, you may create and use a
-modified version of this License if you rename the license and remove
-any references to the name of the license steward (except to note that
-such modified license differs from this License).
-
-10.4. Distributing Source Code Form that is Incompatible With Secondary
-Licenses
-
-If You choose to distribute Source Code Form that is Incompatible With
-Secondary Licenses under the terms of this version of the License, the
-notice described in Exhibit B of this License must be attached.
-
-Exhibit A - Source Code Form License Notice
--------------------------------------------
-
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-If it is not possible or desirable to put the notice in a particular
-file, then You may include the notice in a location (such as a LICENSE
-file in a relevant directory) where a recipient would be likely to look
-for such a notice.
-
-You may add additional accurate notices of copyright ownership.
-
-Exhibit B - "Incompatible With Secondary Licenses" Notice
----------------------------------------------------------
-
- This Source Code Form is "Incompatible With Secondary Licenses", as
- defined by the Mozilla Public License, v. 2.0.
diff --git a/options/license/MS-LPL b/options/license/MS-LPL
deleted file mode 100644
index ea8bffcaae..0000000000
--- a/options/license/MS-LPL
+++ /dev/null
@@ -1,24 +0,0 @@
-Microsoft Limited Public License (Ms-LPL)
-
-This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
-
-1. Definitions
-The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to the software. A "contributor" is any person that distributes its contribution under this license. "Licensed patents" are a contributor's patent claims that read directly on its contribution.
-
-2. Grant of Rights
- (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
-
- (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
-
-3. Conditions and Limitations
- (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
-
- (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
-
- (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
-
- (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
-
- (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees, or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
-
- (F) Platform Limitation- The licenses granted in sections 2(A) & 2(B) extend only to the software or derivative works that you create that run on a Microsoft Windows operating system product.
diff --git a/options/license/MS-PL b/options/license/MS-PL
deleted file mode 100644
index c61790bc8d..0000000000
--- a/options/license/MS-PL
+++ /dev/null
@@ -1,22 +0,0 @@
-Microsoft Public License (Ms-PL)
-
-This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
-
-1. Definitions
-The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to the software. A "contributor" is any person that distributes its contribution under this license. "Licensed patents" are a contributor's patent claims that read directly on its contribution.
-
-2. Grant of Rights
- (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
-
- (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
-
-3. Conditions and Limitations
- (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
-
- (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
-
- (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
-
- (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
-
- (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees, or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
diff --git a/options/license/MS-RL b/options/license/MS-RL
deleted file mode 100644
index 768d5e3114..0000000000
--- a/options/license/MS-RL
+++ /dev/null
@@ -1,30 +0,0 @@
-Microsoft Reciprocal License (Ms-RL)
-
-This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
-
-1. Definitions
-The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
-
-A "contribution" is the original software, or any additions or changes to the software.
-
-A "contributor" is any person that distributes its contribution under this license.
-
-"Licensed patents" are a contributor's patent claims that read directly on its contribution.
-
-2. Grant of Rights
- (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
-
- (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
-
-3. Conditions and Limitations
- (A) Reciprocal Grants- For any file you distribute that contains code from the software (in source code or binary format), you must provide recipients the source code to that file along with a copy of this license, which license will govern that file. You may license other files that are entirely your own work and do not contain code from the software under any terms you choose.
-
- (B) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
-
- (C) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
-
- (D) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
-
- (E) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
-
- (F) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees, or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
diff --git a/options/license/MTLL b/options/license/MTLL
deleted file mode 100644
index 0af2b318fb..0000000000
--- a/options/license/MTLL
+++ /dev/null
@@ -1,24 +0,0 @@
-Software License for MTL
-
-Copyright (c) 2007 The Trustees of Indiana University.
- 2008 Dresden University of Technology and the Trustees of Indiana University.
- 2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
-All rights reserved.
-Authors: Peter Gottschling and Andrew Lumsdaine
-
-This file is part of the Matrix Template Library
-
-Dresden University of Technology -- short TUD -- and Indiana University -- short IU -- have the exclusive rights to license this product under the following license.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- 1. All redistributions of source code must retain the above copyright notice, the list of authors in the original source code, this list of conditions and the disclaimer listed in this license;
- 2. All redistributions in binary form must reproduce the above copyright notice, this list of conditions and the disclaimer listed in this license in the documentation and/or other materials provided with the distribution;
- 3. Any documentation included with all redistributions must include the following acknowledgement:
- "This product includes software developed at the University of Notre Dame, the Pervasive Technology Labs at Indiana University, and Dresden University of Technology. For technical information contact Andrew Lumsdaine at the Pervasive Technology Labs at Indiana University. For administrative and license questions contact the Advanced Research and Technology Institute at 1100 Waterway Blvd. Indianapolis, Indiana 46202, phone 317-274-5905, fax 317-274-5902."
- Alternatively, this acknowledgement may appear in the software itself, and wherever such third-party acknowledgments normally appear.
- 4. The name "MTL" shall not be used to endorse or promote products derived from this software without prior written permission from IU or TUD. For written permission, please contact Indiana University Advanced Research & Technology Institute.
- 5. Products derived from this software may not be called "MTL", nor may "MTL" appear in their name, without prior written permission of Indiana University Advanced Research & Technology Institute.
-
-TUD and IU provide no reassurances that the source code provided does not infringe the patent or any other intellectual property rights of any other entity. TUD and IU disclaim any liability to any recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise.
-
-LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. DRESDEN UNIVERSITY OF TECHNOLOGY AND INDIANA UNIVERSITY GIVE NO WARRANTIES AND MAKE NO REPRESENTATION THAT SOFTWARE IS FREE OF INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. DRESDEN UNIVERSITY OF TECHNOLOGY AND INDIANA UNIVERSITY MAKE NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE. LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION GENERATED USING SOFTWARE.
diff --git a/options/license/Mackerras-3-Clause b/options/license/Mackerras-3-Clause
deleted file mode 100644
index 6467f0c98e..0000000000
--- a/options/license/Mackerras-3-Clause
+++ /dev/null
@@ -1,25 +0,0 @@
-Copyright (c) 1995 Eric Rosenquist. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
- 3. The name(s) of the authors of this software must not be used to
- endorse or promote products derived from this software without
- prior written permission.
-
- THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
- THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
- SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/options/license/Mackerras-3-Clause-acknowledgment b/options/license/Mackerras-3-Clause-acknowledgment
deleted file mode 100644
index 5f0187add7..0000000000
--- a/options/license/Mackerras-3-Clause-acknowledgment
+++ /dev/null
@@ -1,25 +0,0 @@
-Copyright (c) 1993-2002 Paul Mackerras. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-2. The name(s) of the authors of this software must not be used to
- endorse or promote products derived from this software without
- prior written permission.
-
-3. Redistributions of any form whatsoever must retain the following
- acknowledgment:
- "This product includes software developed by Paul Mackerras
- <paulus@ozlabs.org>".
-
-THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
-THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
-SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
-AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
-OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/options/license/MakeIndex b/options/license/MakeIndex
deleted file mode 100644
index c345384298..0000000000
--- a/options/license/MakeIndex
+++ /dev/null
@@ -1,19 +0,0 @@
-MakeIndex Distribution Notice
-
-11/11/1989
-
-Copyright (C) 1989 by Chen & Harrison International Systems, Inc.
-Copyright (C) 1988 by Olivetti Research Center
-Copyright (C) 1987 by Regents of the University of California
-
-Author:
- Pehong Chen (phc@renoir.berkeley.edu)
- Chen & Harrison International Systems, Inc.
- Palo Alto, California
- USA
-
-Permission is hereby granted to make and distribute original copies of this program provided that the copyright notice and this permission notice are preserved and provided that the recipient is not asked to waive or limit his right to redistribute copies as allowed by this permission notice and provided that anyone who receives an executable form of this program is granted access to a machine-readable form of the source code for this program at a cost not greater than reasonable reproduction, shipping, and handling costs. Executable forms of this program distributed without the source code must be accompanied by a conspicuous copy of this permission notice and a statement that tells the recipient how to obtain the source code.
-
-Permission is granted to distribute modified versions of all or part of this program under the conditions above with the additional requirement that the entire modified work must be covered by a permission notice identical to this permission notice. Anything distributed with and usable only in conjunction with something derived from this program, whose useful purpose is to extend or adapt or add capabilities to this program, is to be considered a modified version of this program under the requirement above. Ports of this program to other systems not supported in the distribution are also considered modified versions. All modified versions should be reported back to the author.
-
-This program is distributed with no warranty of any sort. No contributor accepts responsibility for the consequences of using this program or for whether it serves any particular purpose.
diff --git a/options/license/Martin-Birgmeier b/options/license/Martin-Birgmeier
deleted file mode 100644
index 48d32f846e..0000000000
--- a/options/license/Martin-Birgmeier
+++ /dev/null
@@ -1,5 +0,0 @@
-Copyright (c) 1993 Martin Birgmeier All rights reserved.
-
-You may redistribute unmodified or modified versions of this source code provided that the above copyright notice and this and the following conditions are retained.
-
-This software is provided ``as is'', and comes with no warranties of any kind. I shall in no event be liable for anything that happens to anyone/anything when using this software.
diff --git a/options/license/McPhee-slideshow b/options/license/McPhee-slideshow
deleted file mode 100644
index 0ddf7ba350..0000000000
--- a/options/license/McPhee-slideshow
+++ /dev/null
@@ -1,6 +0,0 @@
-Copyright 2001, Patrick TJ McPhee
-everyone is welcome to use this code for any purpose, to modify it, and
-to copy it in whole or in part for use in other macro sets, with the
-conditions that this copyright notice be preserved with any significant
-portion of the code, and that modifications to this file be clearly
-marked.
diff --git a/options/license/Minpack b/options/license/Minpack
deleted file mode 100644
index 132cc3f33f..0000000000
--- a/options/license/Minpack
+++ /dev/null
@@ -1,51 +0,0 @@
-Minpack Copyright Notice (1999) University of Chicago. All rights reserved
-
-Redistribution and use in source and binary forms, with or
-without modification, are permitted provided that the
-following conditions are met:
-
-1. Redistributions of source code must retain the above
-copyright notice, this list of conditions and the following
-disclaimer.
-
-2. Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following
-disclaimer in the documentation and/or other materials
-provided with the distribution.
-
-3. The end-user documentation included with the
-redistribution, if any, must include the following
-acknowledgment:
-
- "This product includes software developed by the
- University of Chicago, as Operator of Argonne National
- Laboratory.
-
-Alternately, this acknowledgment may appear in the software
-itself, if and wherever such third-party acknowledgments
-normally appear.
-
-4. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS"
-WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE
-UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND
-THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE
-OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY
-OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR
-USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF
-THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4)
-DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION
-UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL
-BE CORRECTED.
-
-5. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT
-HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF
-ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT,
-INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF
-ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF
-PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER
-SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT
-(INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE,
-EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
-POSSIBILITY OF SUCH LOSS OR DAMAGES.
diff --git a/options/license/MirOS b/options/license/MirOS
deleted file mode 100644
index ee21265cda..0000000000
--- a/options/license/MirOS
+++ /dev/null
@@ -1,7 +0,0 @@
-The MirOS Licence
-
-Copyright [YEAR] [NAME] [EMAIL]
-
-Provided that these terms and disclaimer and all copyright notices are retained or reproduced in an accompanying document, permission is granted to deal in this work without restriction, including unlimited rights to use, publicly perform, distribute, sell, modify, merge, give away, or sublicence.
-
-This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to the utmost extent permitted by applicable law, neither express nor implied; without malicious intent or gross negligence. In no event may a licensor, author or contributor be held liable for indirect, direct, other damage, loss, or other issues arising in any way out of dealing in the work, even if advised of the possibility of such damage or existence of a defect, except proven that it results out of said person's immediate fault when using the work as intended.
diff --git a/options/license/Motosoto b/options/license/Motosoto
deleted file mode 100644
index a25cff026e..0000000000
--- a/options/license/Motosoto
+++ /dev/null
@@ -1,372 +0,0 @@
-MOTOSOTO OPEN SOURCE LICENSE - Version 0.9.1
-
-This Motosoto Open Source License (the "License") applies to "Community Portal Server" and related
-software products as well as any updatesor maintenance releases of that software ("Motosoto
-Products") that are distributed by Motosoto.Com B.V. ("Licensor"). Any Motosoto Product licensed
-pursuant to this License is a "Licensed Product." Licensed Product, in its entirety, is protected
-by Dutch copyright law. This License identifies the terms under which you may use, copy, distribute
-or modify Licensed Product and has been submitted to the Open Software Initiative (OSI) for
-approval.
-
-Preamble
-
-This Preamble is intended to describe, in plain English, the nature and scope of this License.
-However, this Preamble is not a part of this license. The legal effect of this License is dependent
-only upon the terms of the License and not this Preamble. This License complies with the Open
-Source Definition and has been approved by Open Source Initiative. Software distributed under this
-License may be marked as "OSI Certified Open Source Software."
-
-This License provides that:
-
-1. You may use, sell or give away the Licensed Product, alone or as a component of an aggregate
-software distribution containing programs from several different sources. No royalty or other fee
-is required.
-
-2. Both Source Code and executable versions of the Licensed Product, including Modifications made
-by previous Contributors, are available for your use. (The terms "Licensed Product,"
-"Modifications," "Contributors" and "Source Code" are defined in the License.)
-
-3. You are allowed to make Modifications to the Licensed Product, and you can create Derivative
-Works from it. (The term "Derivative Works" is defined in the License.)
-
-4. By accepting the Licensed Product under the provisions of this License, you agree that any
-Modifications you make to the Licensed Product and then distribute are governed by the provisions
-of this License. In particular, you must make the Source Code of your Modifications available to
-others.
-
-5. You may use the Licensed Product for any purpose, but the Licensor is not providing you any
-warranty whatsoever, nor is the Licensor accepting any liability in the event that the Licensed
-Product doesn't work properly or causes you any injury or damages.
-
-6. If you sublicense the Licensed Product or Derivative Works, you may charge fees for warranty or
-support, or for accepting indemnity or liability obligations to your customers. You cannot charge
-for the Source Code.
-
-7. If you assert any patent claims against the Licensor relating to the Licensed Product, or if you
-breach any terms of the License, your rights to the Licensed Product under this License
-automatically terminate.
-
-You may use this License to distribute your own Derivative Works, in which case the provisions of
-this License will apply to your Derivative Works just as they do to the original Licensed Product.
-
-Alternatively, you may distribute your Derivative Works under any other OSI-approved Open Source
-license, or under a proprietary license of your choice. If you use any license other than this
-License, however, you must continue to fulfill the requirements of this License (including the
-provisions relating to publishing the Source Code) for those portions of your Derivative Works that
-consist of the Licensed Product, including the files containing Modifications.
-
-New versions of this License may be published from time to time. You may choose to continue to use
-the license terms in this version of the License or those from the new version. However, only the
-Licensor has the right to change the License terms as they apply to the Licensed Product. This
-License relies on precise definitions for certain terms. Those terms are defined when they are
-first used, and the definitions are repeated for your convenience in a Glossary at the end of the
-License.
-
-License Terms
-
-1. Grant of License From Licensor.
-
-Licensor hereby grants you a world-wide, royalty-free, non-exclusive license, subject to third
-party intellectual property claims, to do the following:
-
- a. Use, reproduce, modify, display, perform, sublicense and distribute Licensed Product or
-portions thereof (including Modifications as hereinafter defined), in both Source Code or as an
-executable program. "Source Code" means the preferred form for making modifications to the Licensed
-Product, including all modules contained therein, plus any associated interface definition files,
-scripts used to control compilation and installation of an executable program, or a list of
-differential comparisons against the Source Code of the Licensed Product.
-
- b. Create Derivative Works (as that term is defined under Dutch copyright law) of Licensed
-Product by adding to or deleting from the substance or structure of said Licensed Product.
-
- c. Under claims of patents now or hereafter owned or controlled by Licensor, to make, use,
-sell, offer for sale, have made, and/or otherwise dispose of Licensed Product or portions thereof,
-but solely to the extent that any such claim is necessary to enable you to make, use, sell, offer
-for sale, have made, and/or otherwise dispose of Licensed Product or portions thereof or Derivative
-Works thereof.
-
-2. Grant of License to Modifications From Contributor.
-
-"Modifications" means any additions to or deletions from the substance or structure of (i) a file
-containing Licensed Product, or (ii) any new file that contains any part of Licensed Product.
-Hereinafter in this License, the term "Licensed Product" shall include all previous Modifications
-that you receive from any Contributor. By application of the provisions in Section 4(a) below, each
-person or entity who created or contributed to the creation of, and distributed, a Modification (a
-"Contributor") hereby grants you a world-wide, royalty-free, non-exclusive license, subject to
-third party intellectual property claims, to do the following:
-
- a. Use, reproduce, modify, display, perform, sublicense and distribute any Modifications
-created by such Contributor or portions thereof, in both Source Code or as an executable program,
-either on an unmodified basis or as part of Derivative Works.
-
- b. Under claims of patents now or hereafter owned or controlled by Contributor, to make, use,
-sell, offer for sale, have made, and/or otherwise dispose of Modifications or portions thereof, but
-solely to the extent that any such claim is necessary to enable you to make, use, sell, offer for
-sale, have made, and/or otherwise dispose of Modifications or portions thereof or Derivative Works
-thereof.
-
-3. Exclusions From License Grant.
-
-Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents,
-trade secrets or any other intellectual property of Licensor or any Contributor except as expressly
-stated herein. No patent license is granted separate from the Licensed Product, for code that you
-delete from the Licensed Product, or for combinations of the Licensed Product with other software
-or hardware. No right is granted to the trademarks of Licensor or any Contributor even if such
-marks are included in the Licensed Product. Nothing in this License shall be interpreted to
-prohibit Licensor from licensing under different terms from this License any code that Licensor
-otherwise would have a right to license.
-
-4. Your Obligations Regarding Distribution.
-
- a. Application of This License to Your Modifications. As an express condition for your use of
-the Licensed Product, you hereby agree that any Modifications that you create or to which you
-contribute, and which you distribute, are governed by the terms of this License including, without
-limitation, Section 2. Any Modifications that you create or to which you contribute may be
-distributed only under the terms of this License or a future version of this License released under
-Section 7. You must include a copy of this License with every copy of the Modifications you
-distribute. You agree not to offer or impose any terms on any Source Code or executable version of
-the Licensed Product or Modifications that alter or restrict the applicable version of this License
-or the recipients' rights hereunder. However, you may include an additional document offering the
-additional rights described in Section 4(e).
-
- b. Availability of Source Code. You must make available, under the terms of this License, the
-Source Code of the Licensed Product and any Modifications that you distribute, either on the same
-media as you distribute any executable or other form of the Licensed Product, or via a mechanism
-generally accepted in the software development community for the electronic transfer of data (an
-"Electronic Distribution Mechanism"). The Source Code for any version of Licensed Product or
-Modifications that you distribute must remain available for at least twelve (12) months after the
-date it initially became available, or at least six (6) months after a subsequent version of said
-Licensed Product or Modifications has been made available. You are responsible for ensuring that
-the Source Code version remains available even if the Electronic Distribution Mechanism is
-maintained by a third party.
-
- c. Description of Modifications. You must cause any Modifications that you create or to which
-you contribute, and which you distribute, to contain a file documenting the additions, changes or
-deletions you made to create or contribute to those Modifications, and the dates of any such
-additions, changes or deletions. You must include a prominent statement that the Modifications are
-derived, directly or indirectly, from the Licensed Product and include the names of the Licensor
-and any Contributor to the Licensed Product in (i) the Source Code and (ii) in any notice displayed
-by a version of the Licensed Product you distribute or in related documentation in which you
-describe the origin or ownership of the Licensed Product. You may not modify or delete any
-preexisting copyright notices in the Licensed Product.
-
- d. Intellectual Property Matters.
- i. Third Party Claims. If you have knowledge that a license to a third party's
-intellectual property right is required to exercise the rights granted by this License, you must
-include a text file with the Source Code distribution titled "LEGAL" that describes the claim and
-the party making the claim in sufficient detail that a recipient will know whom to contact. If you
-obtain such knowledge after you make any Modifications available as described in Section 4(b), you
-shall promptly modify the LEGAL file in all copies you make available thereafter and shall take
-other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to
-inform those who received the Licensed Product from you that new knowledge has been obtained.
-
- ii. Contributor APIs. If your Modifications include an application programming interface
-("API") and you have knowledge of patent licenses that are reasonably necessary to implement that
-API, you must also include this information in the LEGAL file.
-
- iii. Representations. You represent that, except as disclosed pursuant to 4(d)(i) above,
-you believe that any Modifications you distribute are your original creations and that you have
-sufficient rights to grant the rights conveyed by this License.
-
- e. Required Notices. You must duplicate this License in any documentation you provide along
-with the Source Code of any Modifications you create or to which you contribute, and which you
-distribute, wherever you describe recipients' rights relating to Licensed Product. You must
-duplicate the notice contained in Exhibit A (the "Notice") in each file of the Source Code of any
-copy you distribute of the Licensed Product. If you created a Modification, you may add your name
-as a Contributor to the Notice. If it is not possible to put the Notice in a particular Source Code
-file due to its structure, then you must include such Notice in a location (such as a relevant
-directory file) where a user would be likely to look for such a notice. You may choose to offer,
-and charge a fee for, warranty, support, indemnity or liability obligations to one or more
-recipients of Licensed Product. However, you may do so only on your own behalf, and not on behalf
-of the Licensor or any Contributor. You must make it clear that any such warranty, support,
-indemnity or liability obligation is offered by you alone, and you hereby agree to indemnify the
-Licensor and every Contributor for any liability incurred by the Licensor or such Contributor as a
-result of warranty, support, indemnity or liability terms you offer.
-
- f. Distribution of Executable Versions. You may distribute Licensed Product as an executable
-program under a license of your choice that may contain terms different from this License provided
-(i) you have satisfied the requirements of Sections 4(a) through 4(e) for that distribution, (ii)
-you include a conspicuous notice in the executable version, related documentation and collateral
-materials stating that the Source Code version of the Licensed Product is available under the terms
-of this License, including a description of how and where you have fulfilled the obligations of
-Section 4(b), (iii) you retain all existing copyright notices in the Licensed Product, and (iv) you
-make it clear that any terms that differ from this License are offered by you alone, not by
-Licensor or any Contributor. You hereby agree to indemnify the Licensor and every Contributor for
-any liability incurred by Licensor or such Contributor as a result of any terms you offer.
-
- g. Distribution of Derivative Works. You may create Derivative Works (e.g., combinations of
-some or all of the Licensed Product with other code) and distribute the Derivative Works as
-products under any other license you select, with the proviso that the requirements of this License
-are fulfilled for those portions of the Derivative Works that consist of the Licensed Product or
-any Modifications thereto.
-
-5. Inability to Comply Due to Statute or Regulation.
-
-If it is impossible for you to comply with any of the terms of this License with respect to some or
-all of the Licensed Product due to statute, judicial order, or regulation, then you must (i) comply
-with the terms of this License to the maximum extent possible, (ii) cite the statute or regulation
-that prohibits you from adhering to the License, and (iii) describe the limitations and the code
-they affect. Such description must be included in the LEGAL file described in Section 4(d), and
-must be included with all distributions of the Source Code. Except to the extent prohibited by
-statute or regulation, such description must be sufficiently detailed for a recipient of ordinary
-skill at computer programming to be able to understand it.
-
-6. Application of This License.
-
-This License applies to code to which Licensor or Contributor has attached the Notice in Exhibit A,
-which is incorporated herein by this reference.
-
-7. Versions of This License.
-
- a. Version. The Motosoto Open Source License is derived from the Jabber Open Source License.
-All changes are related to applicable law and the location of court.
-
- b. New Versions. Licensor may publish from time to time revised and/or new versions of the
-License.
-
- c. Effect of New Versions. Once Licensed Product has been published under a particular version
-of the License, you may always continue to use it under the terms of that version. You may also
-choose to use such Licensed Product under the terms of any subsequent version of the License
-published by Licensor. No one other than Lic ensor has the right to modify the terms applicable to
-Licensed Product created under this License.
-
- d. Derivative Works of this License. If you create or use a modified version of this License,
-which you may do only in order to apply it to software that is not already a Licensed Product under
-this License, you must rename your license so that it is not confusingly similar to this License,
-and must make it clear that your license contains terms that differ from this License. In so naming
-your license, you may not use any trademark of Licensor or any Contributor.
-
-8. Disclaimer of Warranty.
-
-LICENSED PRODUCT IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND,
-EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE LICENSED PRODUCT IS
-FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE LICENSED PRODUCT IS WITH YOU. SHOULD LICENSED PRODUCT PROVE
-DEFECTIVE IN ANY RESPECT, YOU (AND NOT THE LICENSOR OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF
-ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
-PART OF THIS LICENSE. NO USE OF LICENSED PRODUCT IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
-DISCLAIMER.
-
-9. Termination.
-
- a. Automatic Termination Upon Breach. This license and the rights granted hereunder will
-terminate automatically if you fail to comply with the terms herein and fail to cure such breach
-within thirty (30) days of becoming aware of the breach. All sublicenses to the Licensed Product
-that are properly granted shall survive any termination of this license. Provisions that, by their
-nature, must remain in effect beyond the termination of this License, shall survive.
-
- b. Termination Upon Assertion of Patent Infringement. If you initiate litigation by asserting
-a patent infringement claim (excluding declaratory judgment actions) against Licensor or a
-Contributor (Licensor or Contributor against whom you file such an action is referred to herein as
-"Respondent") alleging that Licensed Product directly or indirectly infringes any patent, then any
-and all rights granted by such Respondent to you under Sections 1 or 2 of this License shall
-terminate prospectively upon sixty (60) days notice from Respondent (the "Notice Period") unless
-within that Notice Period you either agree in writing (i) to pay Respondent a mutually agreeable
-reasonably royalty for your past or future use of Licensed Product made by such Respondent, or (ii)
-withdraw your litigation claim with respect to Licensed Product against such Respondent. If within
-said Notice Period a reasonable royalty and payment arrangement are not mutually agreed upon in
-writing by the parties or the litigation claim is not withdrawn, the rights granted by Licensor to
-you under Sections 1 and 2 automatically terminate at the expiration of said Notice Period.
-
- c. Reasonable Value of This License. If you assert a patent infringement claim against
-Respondent alleging that Licensed Product directly or indirectly infringes any patent where such
-claim is resolved (such as by license or settlement) prior to the initiation of patent infringement
-litigation, then the reasonable value of the licenses granted by said Respondent under Sections 1
-and 2 shall be taken into account in determining the amount or value of any payment or license.
-
- d. No Retroactive Effect of Termination. In the event of termination under Sections 9(a) or
-9(b) above, all end user license agreements (excluding licenses to distributors and reselle rs)
-that have been validly granted by you or any distributor hereunder prior to termination shall
-survive termination.
-
-10. Limitation of Liability.
-
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR
-OTHERWISE, SHALL THE LICENSOR, ANY CONTRIBUTOR, OR ANY DISTRIBUTOR OF LICENSED PRODUCT, OR ANY
-SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
-CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
-WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES,
-EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
-LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY's
-NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE 
-EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY 
-NOT APPLY TO YOU.
-
-11. Responsibility for Claims. 
-
-As between Licensor and Contributors, each party is responsible for claims and damages arising, 
-directly or indirectly, out of its utilization of rights under this License. You agree to work with 
-Licensor and Contributors to distribute such responsibility on an equitable basis. Nothing herein is 
-intended or shall be deemed to constitute any admission of liability.
-
-12. U.S. Government End Users. 
-
-The Licensed Product is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), 
-consisting of "commercial computer software" and "commercial computer software documentation," 
-as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 
-48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire 
-Licensed Product with only those rights set forth herein.
-
-13. Miscellaneous. 
-This License represents the complete agreement concerning the subject matter hereof. If any 
-provision of this License is held to be unenforceable, such provision shall be reformed only 
-to the extent necessary to make it enforceable. This License shall be governed by Dutch law 
-provisions. The application of the United Nations Convention on Contracts for the International 
-Sale of Goods is expressly excluded. You and Licensor expressly waive any rights to a jury trial 
-in any litigation concerning Licensed Product or this License. Any law or regulation that provides 
-that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-14. Definition of "You" in This License. 
-"You" throughout this License, whether in upper or lower case, means an individual or a legal entity 
-exercising rights under, and complying with all of the terms of, this License or a future version of 
-this License issued under Section 7. For legal entities, "you" includes any entity that controls, is 
-controlled by, or is under common control with you. For purposes of this definition, "control" means 
-(i) the power, direct or indirect, to cause the direction or management of such entity, whether by 
-contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, 
-or (iii) beneficial ownership of such entity.
-
-15. Glossary.
-All defined terms in this License that are used in more than one Section of this License are 
-repeated here, in alphabetical order, for the convenience of the reader. The Section of this 
-License in which each defined term is first used is shown in parentheses. 
-
-Contributor: Each person or entity who created or contributed to the creation of, and distributed, a Modification. (See Section 2)
-
-Derivative Works: That term as used in this License is defined under Dutch copyright law. (See Section 1(b))
-
-License: This Motosoto Open Source License. (See first paragraph of License)
-
-Licensed Product: Any Motosoto Product licensed pursuant to this License. The term
-"Licensed Product" includes all previous Modifications from any Contributor that you receive. 
-(See first paragraph of License and Section 2)
-
-Licensor: Motosoto.Com B.V.. (See first paragraph of License)
-
-Modifications: Any additions to or deletions from the substance or structure of (i) a file 
-containing Licensed Product, or (ii) any new file that contains any part of Licensed Product. (See Section 2)
-
-Notice: The notice contained in Exhibit A. (See Section 4(e))
-
-Source Code: The preferred form for making modifications to the Licensed Product, including 
-all modules contained therein, plus any associated interface definition files, scripts used 
-to control compilation and installation of an executable program, or a list of differential 
-comparisons against the Source Code of the Licensed Product. (See Section 1(a))
-
-You: This term is defined in Section 14 of this License.
- 
-EXHIBIT A
-The Notice below must appear in each file of the Source Code of any copy you distribute of the Licensed Product or any Modifications thereto. Contributors to any Modifications may add their own copyright notices to identify their own contributions.
-
-License:
-The contents of this file are subject to the Motosoto Open Source License Version 0.9 (the "License"). You may not copy or use this file, in either source code or executable form, except in compliance with the License. You may obtain a copy of the License at http://www.motosoto.com/license/ or at http://www.opensource.org/.
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-Copyrights:
-Portions created by or assigned to Motosoto.com B.V. are Copyright (c) 2000-2001 Motosoto.com B.V.
-All Rights Reserved. Contact information for Motosoto.com B.V. is available at http://www.motosoto.com/.
-
-Acknowledgements
-Special thanks to the Motosoto Open Source Contributors for their suggestions and support of Motosoto.
-
-Modifications:
diff --git a/options/license/MulanPSL-1.0 b/options/license/MulanPSL-1.0
deleted file mode 100644
index 8d8fae9511..0000000000
--- a/options/license/MulanPSL-1.0
+++ /dev/null
@@ -1,116 +0,0 @@
-木兰宽æ¾è®¸å¯è¯, 第1版
-
-木兰宽æ¾è®¸å¯è¯ï¼Œ 第1版
-
-2019年8月 http://license.coscl.org.cn/MulanPSL
-
-您对“软件â€çš„å¤åˆ¶ã€ä½¿ç”¨ã€ä¿®æ”¹åŠåˆ†å‘å—æœ¨å…°å®½æ¾è®¸å¯è¯ï¼Œç¬¬1版(“本许å¯è¯â€ï¼‰çš„å¦‚ä¸‹æ¡æ¬¾çš„约æŸï¼š
-
-0. 定义
-
-â€œè½¯ä»¶â€æ˜¯æŒ‡ç”±â€œè´¡çŒ®â€æž„æˆçš„许å¯åœ¨â€œæœ¬è®¸å¯è¯â€ä¸‹çš„程åºå’Œç›¸å…³æ–‡æ¡£çš„集åˆã€‚
-
-â€œè´¡çŒ®è€…â€æ˜¯æŒ‡å°†å—ç‰ˆæƒæ³•ä¿æŠ¤çš„ä½œå“许å¯åœ¨â€œæœ¬è®¸å¯è¯â€ä¸‹çš„自然人或“法人实体â€ã€‚
-
-â€œæ³•äººå®žä½“â€æ˜¯æŒ‡æäº¤è´¡çŒ®çš„æœºæž„åŠå…¶â€œå…³è”实体â€ã€‚
-
-“关è”å®žä½“â€æ˜¯æŒ‡ï¼Œå¯¹â€œæœ¬è®¸å¯è¯â€ä¸‹çš„一方而言,控制ã€å—控制或与其共åŒå—æŽ§åˆ¶çš„æœºæž„ï¼Œæ­¤å¤„çš„æŽ§åˆ¶æ˜¯æŒ‡æœ‰å—æŽ§æ–¹æˆ–å…±åŒå—控方至少50%直接或间接的投票æƒã€èµ„金或其他有价è¯åˆ¸ã€‚
-
-â€œè´¡çŒ®â€æ˜¯æŒ‡ç”±ä»»ä¸€â€œè´¡çŒ®è€…â€è®¸å¯åœ¨â€œæœ¬è®¸å¯è¯â€ä¸‹çš„å—ç‰ˆæƒæ³•ä¿æŠ¤çš„ä½œå“。
-
-1. 授予版æƒè®¸å¯
-
-æ¯ä¸ªâ€œè´¡çŒ®è€…â€æ ¹æ®â€œæœ¬è®¸å¯è¯â€æŽˆäºˆæ‚¨æ°¸ä¹…性的ã€å…¨çƒæ€§çš„ã€å…费的ã€éžç‹¬å çš„ã€ä¸å¯æ’¤é”€çš„版æƒè®¸å¯ï¼Œæ‚¨å¯ä»¥å¤åˆ¶ã€ä½¿ç”¨ã€ä¿®æ”¹ã€åˆ†å‘其“贡献â€ï¼Œä¸è®ºä¿®æ”¹ä¸Žå¦ã€‚
-
-2. 授予专利许å¯
-
-æ¯ä¸ªâ€œè´¡çŒ®è€…â€æ ¹æ®â€œæœ¬è®¸å¯è¯â€æŽˆäºˆæ‚¨æ°¸ä¹…性的ã€å…¨çƒæ€§çš„ã€å…费的ã€éžç‹¬å çš„ã€ä¸å¯æ’¤é”€çš„ï¼ˆæ ¹æ®æœ¬æ¡è§„定撤销除外)专利许å¯ï¼Œä¾›æ‚¨åˆ¶é€ ã€å§”托制造ã€ä½¿ç”¨ã€è®¸è¯ºé”€å”®ã€é”€å”®ã€è¿›å£å…¶â€œè´¡çŒ®â€æˆ–以其他方å¼è½¬ç§»å…¶â€œè´¡çŒ®â€ã€‚å‰è¿°ä¸“利许å¯ä»…é™äºŽâ€œè´¡çŒ®è€…â€çŽ°åœ¨æˆ–å°†æ¥æ‹¥æœ‰æˆ–æŽ§åˆ¶çš„å…¶â€œè´¡çŒ®â€æœ¬èº«æˆ–其“贡献â€ä¸Žè®¸å¯â€œè´¡çŒ®â€æ—¶çš„“软件â€ç»“åˆè€Œå°†å¿…然会侵犯的专利æƒåˆ©è¦æ±‚,ä¸åŒ…æ‹¬ä»…å› æ‚¨æˆ–ä»–äººä¿®æ”¹â€œè´¡çŒ®â€æˆ–其他结åˆè€Œå°†å¿…然会侵犯到的专利æƒåˆ©è¦æ±‚。如您或您的“关è”实体â€ç›´æŽ¥æˆ–间接地(包括通过代ç†ã€ä¸“利被许å¯äººæˆ–å—è®©äººï¼‰ï¼Œå°±â€œè½¯ä»¶â€æˆ–其中的“贡献â€å¯¹ä»»ä½•人å‘起专利侵æƒè¯‰è®¼ï¼ˆåŒ…括å诉或交å‰è¯‰è®¼ï¼‰æˆ–其他专利维æƒè¡ŒåŠ¨ï¼ŒæŒ‡æŽ§å…¶ä¾µçŠ¯ä¸“åˆ©æƒï¼Œåˆ™â€œæœ¬è®¸å¯è¯â€æŽˆäºˆæ‚¨å¯¹â€œè½¯ä»¶â€çš„专利许å¯è‡ªæ‚¨æèµ·è¯‰è®¼æˆ–å‘èµ·ç»´æƒè¡ŒåŠ¨ä¹‹æ—¥ç»ˆæ­¢ã€‚
-
-3. 无商标许å¯
-
-“本许å¯è¯â€ä¸æä¾›å¯¹â€œè´¡çŒ®è€…â€çš„商å“åç§°ã€å•†æ ‡ã€æœåŠ¡æ ‡å¿—æˆ–äº§å“å称的商标许å¯ï¼Œä½†æ‚¨ä¸ºæ»¡è¶³ç¬¬4æ¡è§„定的声明义务而必须使用除外。
-
-4. 分å‘é™åˆ¶
-
-您å¯ä»¥åœ¨ä»»ä½•媒介中将“软件â€ä»¥æºç¨‹åºå½¢å¼æˆ–坿‰§è¡Œå½¢å¼é‡æ–°åˆ†å‘,ä¸è®ºä¿®æ”¹ä¸Žå¦ï¼Œä½†æ‚¨å¿…é¡»å‘æŽ¥æ”¶è€…æä¾›â€œæœ¬è®¸å¯è¯â€çš„副本,并ä¿ç•™â€œè½¯ä»¶â€ä¸­çš„版æƒã€å•†æ ‡ã€ä¸“利åŠå…责声明。
-
-5. å…责声明与责任é™åˆ¶
-
-“软件â€åŠå…¶ä¸­çš„“贡献â€åœ¨æä¾›æ—¶ä¸å¸¦ä»»ä½•明示或默示的担ä¿ã€‚åœ¨ä»»ä½•æƒ…å†µä¸‹ï¼Œâ€œè´¡çŒ®è€…â€æˆ–ç‰ˆæƒæ‰€æœ‰è€…ä¸å¯¹ä»»ä½•äººå› ä½¿ç”¨â€œè½¯ä»¶â€æˆ–其中的“贡献â€è€Œå¼•å‘的任何直接或间接æŸå¤±æ‰¿æ‹…责任,ä¸è®ºå› ä½•ç§åŽŸå› å¯¼è‡´æˆ–è€…åŸºäºŽä½•ç§æ³•律ç†è®º,å³ä½¿å…¶æ›¾è¢«å»ºè®®æœ‰æ­¤ç§æŸå¤±çš„å¯èƒ½æ€§ã€‚
-
-æ¡æ¬¾ç»“æŸ
-
-如何将木兰宽æ¾è®¸å¯è¯ï¼Œç¬¬1版,应用到您的软件
-
-如果您希望将木兰宽æ¾è®¸å¯è¯ï¼Œç¬¬1版,应用到您的新软件,为了方便接收者查阅,建议您完æˆå¦‚下三步:
-
-1, 请您补充如下声明中的空白,包括软件åã€è½¯ä»¶çš„首次å‘è¡¨å¹´ä»½ä»¥åŠæ‚¨ä½œä¸ºç‰ˆæƒäººçš„åå­—ï¼›
-
-2, 请您在软件包的一级目录下创建以“LICENSEâ€ä¸ºå的文件,将整个许å¯è¯æ–‡æœ¬æ”¾å…¥è¯¥æ–‡ä»¶ä¸­ï¼›
-
-3, 请将如下声明文本放入æ¯ä¸ªæºæ–‡ä»¶çš„头部注释中。
-
-Copyright (c) [2019] [name of copyright holder]
-[Software Name] is licensed under the Mulan PSL v1.
-You can use this software according to the terms and conditions of the Mulan PSL v1.
-You may obtain a copy of Mulan PSL v1 at:
- http://license.coscl.org.cn/MulanPSL
-THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-See the Mulan PSL v1 for more details.
-Mulan Permissive Software License,Version 1
-
-Mulan Permissive Software License,Version 1 (Mulan PSL v1)
-
-August 2019 http://license.coscl.org.cn/MulanPSL
-
-Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v1 (this License) with following terms and conditions:
-
-0. Definition
-
-Software means the program and related documents which are comprised of those Contribution and licensed under this License.
-
-Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
-
-Legal Entity means the entity making a Contribution and all its Affiliates.
-
-Affiliates means entities that control, or are controlled by, or are under common control with a party to this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
-
-Contribution means the copyrightable work licensed by a particular Contributor under this License.
-
-1. Grant of Copyright License
-
-Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not.
-
-2. Grant of Patent License
-
-Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed, excluding of any patent claims solely be infringed by your or others’ modification or other combinations. If you or your Affiliates directly or indirectly (including through an agent, patent licensee or assignee), institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken.
-
-3. No Trademark License
-
-No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in section 4.
-
-4. Distribution Restriction
-
-You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software.
-
-5. Disclaimer of Warranty and Limitation of Liability
-
-The Software and Contribution in it are provided without warranties of any kind, either express or implied. In no event shall any Contributor or copyright holder be liable to you for any damages, including, but not limited to any direct, or indirect, special or consequential damages arising from your use or inability to use the Software or the Contribution in it, no matter how it’s caused or based on which legal theory, even if advised of the possibility of such damages.
-
-End of the Terms and Conditions
-
-How to apply the Mulan Permissive Software License,Version 1 (Mulan PSL v1) to your software
-
-To apply the Mulan PSL v1 to your work, for easy identification by recipients, you are suggested to complete following three steps:
-
-i. Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner;
-ii. Create a file named “LICENSE†which contains the whole context of this License in the first directory of your software package;
-iii. Attach the statement to the appropriate annotated syntax at the beginning of each source file.
-
-Copyright (c) [2019] [name of copyright holder]
-[Software Name] is licensed under the Mulan PSL v1.
-You can use this software according to the terms and conditions of the Mulan PSL v1.
-You may obtain a copy of Mulan PSL v1 at:
- http://license.coscl.org.cn/MulanPSL
-THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-See the Mulan PSL v1 for more details.
diff --git a/options/license/Multics b/options/license/Multics
deleted file mode 100644
index 9137819123..0000000000
--- a/options/license/Multics
+++ /dev/null
@@ -1,13 +0,0 @@
-Multics License
-
-Historical Background
-
-This edition of the Multics software materials and documentation is provided and donated to Massachusetts Institute of Technology by Group BULL including BULL HN Information Systems Inc. as a contribution to computer science knowledge. This donation is made also to give evidence of the common contributions of Massachusetts Institute of Technology, Bell Laboratories, General Electric, Honeywell Information Systems Inc., Honeywell BULL Inc., Groupe BULL and BULL HN Information Systems Inc. to the development of this operating system. Multics development was initiated by Massachusetts Institute of Technology Project MAC (1963-1970), renamed the MIT Laboratory for Computer Science and Artificial Intelligence in the mid 1970s, under the leadership of Professor Fernando Jose Corbato. Users consider that Multics provided the best software architecture for managing computer hardware properly and for executing programs. Many subsequent operating systems incorporated Multics principles. Multics was distributed in 1975 to 2000 by Group Bull in Europe , and in the U.S. by Bull HN Information Systems Inc., as successor in interest by change in name only to Honeywell Bull Inc. and Honeywell Information Systems Inc.
-
------------------------------------------------------------
-
-Permission to use, copy, modify, and distribute these programs and their documentation for any purpose and without fee is hereby granted,provided that the below copyright notice and historical background appear in all copies and that both the copyright notice and historical background and this permission notice appear in supporting documentation, and that the names of MIT, HIS, BULL or BULL HN not be used in advertising or publicity pertaining to distribution of the programs without specific prior written permission.
-
-Copyright 1972 by Massachusetts Institute of Technology and Honeywell Information Systems Inc.
-Copyright 2006 by BULL HN Information Systems Inc.
-Copyright 2006 by Bull SAS All Rights Reserved
diff --git a/options/license/Mup b/options/license/Mup
deleted file mode 100644
index 57c163a401..0000000000
--- a/options/license/Mup
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright (c) 1995-2012 by Arkkra Enterprises. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following DISCLAIMER.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following DISCLAIMER in the documentation and/or other materials provided with the distribution.
-
-3. Any additions, deletions, or changes to the original files must be clearly indicated in accompanying documentation. including the reasons for the changes, and the names of those who made the modifications.
-
-DISCLAIMER
-
-THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/NAIST-2003 b/options/license/NAIST-2003
deleted file mode 100644
index 40d27d3287..0000000000
--- a/options/license/NAIST-2003
+++ /dev/null
@@ -1,70 +0,0 @@
-Copyright 2000, 2001, 2002, 2003 Nara Institute of Science
-and Technology. All Rights Reserved.
-
-Use, reproduction, and distribution of this software is permitted.
-Any copy of this software, whether in its original form or modified,
-must include both the above copyright notice and the following
-paragraphs.
-
-Nara Institute of Science and Technology (NAIST),
-the copyright holders, disclaims all warranties with regard to this
-software, including all implied warranties of merchantability and
-fitness, in no event shall NAIST be liable for
-any special, indirect or consequential damages or any damages
-whatsoever resulting from loss of use, data or profits, whether in an
-action of contract, negligence or other tortuous action, arising out
-of or in connection with the use or performance of this software.
-
-A large portion of the dictionary entries
-originate from ICOT Free Software. The following conditions for ICOT
-Free Software applies to the current dictionary as well.
-
-Each User may also freely distribute the Program, whether in its
-original form or modified, to any third party or parties, PROVIDED
-that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear
-on, or be attached to, the Program, which is distributed substantially
-in the same form as set out herein and that such intended
-distribution, if actually made, will neither violate or otherwise
-contravene any of the laws and regulations of the countries having
-jurisdiction over the User or the intended distribution itself.
-
-NO WARRANTY
-
-The program was produced on an experimental basis in the course of the
-research and development conducted during the project and is provided
-to users as so produced on an experimental basis. Accordingly, the
-program is provided without any warranty whatsoever, whether express,
-implied, statutory or otherwise. The term "warranty" used herein
-includes, but is not limited to, any warranty of the quality,
-performance, merchantability and fitness for a particular purpose of
-the program and the nonexistence of any infringement or violation of
-any right of any third party.
-
-Each user of the program will agree and understand, and be deemed to
-have agreed and understood, that there is no warranty whatsoever for
-the program and, accordingly, the entire risk arising from or
-otherwise connected with the program is assumed by the user.
-
-Therefore, neither ICOT, the copyright holder, or any other
-organization that participated in or was otherwise related to the
-development of the program and their respective officials, directors,
-officers and other employees shall be held liable for any and all
-damages, including, without limitation, general, special, incidental
-and consequential damages, arising out of or otherwise in connection
-with the use or inability to use the program or any product, material
-or result produced or otherwise obtained by using the program,
-regardless of whether they have been advised of, or otherwise had
-knowledge of, the possibility of such damages at any time during the
-project or thereafter. Each user will be deemed to have agreed to the
-foregoing by his or her commencement of use of the program. The term
-"use" as used herein includes, but is not limited to, the use,
-modification, copying and distribution of the program and the
-production of secondary products from the program.
-
-In the case where the program, whether in its original form or
-modified, was distributed or delivered to or received by a user from
-any person, organization or entity other than ICOT, unless it makes or
-grants independently of ICOT any specific warranty to the user in
-writing, such person, organization or entity, will also be exempted
-from and not be held liable to the user for any such damages as noted
-above as far as the program is concerned.
diff --git a/options/license/NASA-1.3 b/options/license/NASA-1.3
deleted file mode 100644
index c67c97c1bd..0000000000
--- a/options/license/NASA-1.3
+++ /dev/null
@@ -1,85 +0,0 @@
-NASA OPEN SOURCE AGREEMENT VERSION 1.3
-
-THIS OPEN SOURCE AGREEMENT ("AGREEMENT") DEFINES THE RIGHTS OF USE, REPRODUCTION, DISTRIBUTION, MODIFICATION AND REDISTRIBUTION OF CERTAIN COMPUTER SOFTWARE ORIGINALLY RELEASED BY THE UNITED STATES GOVERNMENT AS REPRESENTED BY THE GOVERNMENT AGENCY LISTED BELOW ("GOVERNMENT AGENCY"). THE UNITED STATES GOVERNMENT, AS REPRESENTED BY GOVERNMENT AGENCY, IS AN INTENDED THIRD-PARTY BENEFICIARY OF ALL SUBSEQUENT DISTRIBUTIONS OR REDISTRIBUTIONS OF THE SUBJECT SOFTWARE. ANYONE WHO USES, REPRODUCES, DISTRIBUTES, MODIFIES OR REDISTRIBUTES THE SUBJECT SOFTWARE, AS DEFINED HEREIN, OR ANY PART THEREOF, IS, BY THAT ACTION, ACCEPTING IN FULL THE RESPONSIBILITIES AND OBLIGATIONS CONTAINED IN THIS AGREEMENT.
-
-Government Agency: _____ Government Agency Original Software Designation: __ Government Agency Original Software Title: _____ User Registration Requested. Please Visit http://___ Government Agency Point of Contact for Original Software: _____
-
-DEFINITIONS
-
-A. "Contributor" means Government Agency, as the developer of the Original Software, and any entity that makes a Modification. B. "Covered Patents" mean patent claims licensable by a Contributor that are necessarily infringed by the use or sale of its Modification alone or when combined with the Subject Software. C. "Display" means the showing of a copy of the Subject Software, either directly or by means of an image, or any other device. D. "Distribution" means conveyance or transfer of the Subject Software, regardless of means, to another. E. "Larger Work" means computer software that combines Subject Software, or portions thereof, with software separate from the Subject Software that is not governed by the terms of this Agreement. F. "Modification" means any alteration of, including addition to or deletion from, the substance or structure of either the Original Software or Subject Software, and includes derivative works, as that term is defined in the Copyright Statute, 17 USC 101. However, the act of including Subject Software as part of a Larger Work does not in and of itself constitute a Modification. G. "Original Software" means the computer software first released under this Agreement by Government Agency with Government Agency designation __ and entitled _________, including source code, object code and accompanying documentation, if any. H. "Recipient" means anyone who acquires the Subject Software under this Agreement, including all Contributors. I. "Redistribution" means Distribution of the Subject Software after a Modification has been made. J. "Reproduction" means the making of a counterpart, image or copy of the Subject Software. K. "Sale" means the exchange of the Subject Software for money or equivalent value. L. "Subject Software" means the Original Software, Modifications, or any respective parts thereof. M. "Use" means the application or employment of the Subject Software for any purpose.
-
-GRANT OF RIGHTS
-
-A. Under Non-Patent Rights: Subject to the terms and conditions of this Agreement, each Contributor, with respect to its own contribution to the Subject Software, hereby grants to each Recipient a non-exclusive, world-wide, royalty-free license to engage in the following activities pertaining to the Subject Software:
-
- 1. Use
- 2. Distribution
- 3. Reproduction
- 4. Modification
- 5. Redistribution
- 6. Display
-
-B. Under Patent Rights: Subject to the terms and conditions of this Agreement, each Contributor, with respect to its own contribution to the Subject Software, hereby grants to each Recipient under Covered Patents a non-exclusive, world-wide, royalty-free license to engage in the following activities pertaining to the Subject Software:
-
- 1. Use
- 2. Distribution
- 3. Reproduction
- 4. Sale
- 5. Offer for Sale
-
-C. The rights granted under Paragraph B. also apply to the combination of a Contributor's Modification and the Subject Software if, at the time the Modification is added by the Contributor, the addition of such Modification causes the combination to be covered by the Covered Patents. It does not apply to any other combinations that include a Modification.
-
-D. The rights granted in Paragraphs A. and B. allow the Recipient to sublicense those same rights. Such sublicense must be under the same terms and conditions of this Agreement.
-
-OBLIGATIONS OF RECIPIENT
-
-A. Distribution or Redistribution of the Subject Software must be made under this Agreement except for additions covered under paragraph 3H.
-
- 1. Whenever a Recipient distributes or redistributes the Subject Software, a copy of this Agreement must be included with each copy of the Subject Software; and
- 2. If Recipient distributes or redistributes the Subject Software in any form other than source code, Recipient must also make the source code freely available, and must provide with each copy of the Subject Software information on how to obtain the source code in a reasonable manner on or through a medium customarily used for software exchange.
-
-B. Each Recipient must ensure that the following copyright notice appears prominently in the Subject Software:
-
-[Government Agency will insert the applicable copyright notice in each agreement accompanying the initial distribution of original software and remove this bracketed language.]
-
-[The following copyright notice will be used if created by a contractor pursuant to Government Agency contract and rights obtained from creator by assignment. Government Agency will insert the year and its Agency designation and remove the bracketed language.] Copyright (c) {YEAR} United States Government as represented by ___ ____. All Rights Reserved.
-
-[The following copyright notice will be used if created by civil servants only. Government Agency will insert the year and its Agency designation and remove the bracketed language.] Copyright (c) {YEAR} United States Government as represented by ____ ____. No copyright is claimed in the United States under Title 17, U.S.Code. All Other Rights Reserved.
-
-C. Each Contributor must characterize its alteration of the Subject Software as a Modification and must identify itself as the originator of its Modification in a manner that reasonably allows subsequent Recipients to identify the originator of the Modification. In fulfillment of these requirements, Contributor must include a file (e.g., a change log file) that describes the alterations made and the date of the alterations, identifies Contributor as originator of the alterations, and consents to characterization of the alterations as a Modification, for example, by including a statement that the Modification is derived, directly or indirectly, from Original Software provided by Government Agency. Once consent is granted, it may not thereafter be revoked.
-
-D. A Contributor may add its own copyright notice to the Subject Software. Once a copyright notice has been added to the Subject Software, a Recipient may not remove it without the express permission of the Contributor who added the notice.
-
-E. A Recipient may not make any representation in the Subject Software or in any promotional, advertising or other material that may be construed as an endorsement by Government Agency or by any prior Recipient of any product or service provided by Recipient, or that may seek to obtain commercial advantage by the fact of Government Agency's or a prior Recipient's participation in this Agreement.
-
-F. In an effort to track usage and maintain accurate records of the Subject Software, each Recipient, upon receipt of the Subject Software, is requested to register with Government Agency by visiting the following website: ______. Recipient's name and personal information shall be used for statistical purposes only. Once a Recipient makes a Modification available, it is requested that the Recipient inform Government Agency at the web site provided above how to access the Modification.
-
-[Alternative paragraph for use when a web site for release and monitoring of subject software will not be supported by releasing Government Agency] In an effort to track usage and maintain accurate records of the Subject Software, each Recipient, upon receipt of the Subject Software, is requested to provide Government Agency, by e-mail to the Government Agency Point of Contact listed in clause 5.F., the following information: ______. Recipient's name and personal information shall be used for statistical purposes only. Once a Recipient makes a Modification available, it is requested that the Recipient inform Government Agency, by e-mail to the Government Agency Point of Contact listed in clause 5.F., how to access the Modification.
-
-G. Each Contributor represents that that its Modification is believed to be Contributor's original creation and does not violate any existing agreements, regulations, statutes or rules, and further that Contributor has sufficient rights to grant the rights conveyed by this Agreement.
-
-H. A Recipient may choose to offer, and to charge a fee for, warranty, support, indemnity and/or liability obligations to one or more other Recipients of the Subject Software. A Recipient may do so, however, only on its own behalf and not on behalf of Government Agency or any other Recipient. Such a Recipient must make it absolutely clear that any such warranty, support, indemnity and/or liability obligation is offered by that Recipient alone. Further, such Recipient agrees to indemnify Government Agency and every other Recipient for any liability incurred by them as a result of warranty, support, indemnity and/or liability offered by such Recipient.
-
-I. A Recipient may create a Larger Work by combining Subject Software with separate software not governed by the terms of this agreement and distribute the Larger Work as a single product. In such case, the Recipient must make sure Subject Software, or portions thereof, included in the Larger Work is subject to this Agreement.
-
-J. Notwithstanding any provisions contained herein, Recipient is hereby put on notice that export of any goods or technical data from the United States may require some form of export license from the U.S. Government. Failure to obtain necessary export licenses may result in criminal liability under U.S. laws. Government Agency neither represents that a license shall not be required nor that, if required, it shall be issued. Nothing granted herein provides any such export license.
-
-DISCLAIMER OF WARRANTIES AND LIABILITIES; WAIVER AND INDEMNIFICATION
-
-A. No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS."
-
-B. Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
-
-GENERAL TERMS
-
-A. Termination: This Agreement and the rights granted hereunder will terminate automatically if a Recipient fails to comply with these terms and conditions, and fails to cure such noncompliance within thirty (30) days of becoming aware of such noncompliance. Upon termination, a Recipient agrees to immediately cease use and distribution of the Subject Software. All sublicenses to the Subject Software properly granted by the breaching Recipient shall survive any such termination of this Agreement.
-
-B. Severability: If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement.
-
-C. Applicable Law: This Agreement shall be subject to United States federal law only for all purposes, including, but not limited to, determining the validity of this Agreement, the meaning of its provisions and the rights, obligations and remedies of the parties.
-
-D. Entire Understanding: This Agreement constitutes the entire understanding and agreement of the parties relating to release of the Subject Software and may not be superseded, modified or amended except by further written agreement duly executed by the parties.
-
-E. Binding Authority: By accepting and using the Subject Software under this Agreement, a Recipient affirms its authority to bind the Recipient to all terms and conditions of this Agreement and that that Recipient hereby agrees to all terms and conditions herein.
-
-F. Point of Contact: Any Recipient contact with Government Agency is to be directed to the designated representative as follows: ___________.
diff --git a/options/license/NBPL-1.0 b/options/license/NBPL-1.0
deleted file mode 100644
index f6bf87992f..0000000000
--- a/options/license/NBPL-1.0
+++ /dev/null
@@ -1,59 +0,0 @@
-The Net Boolean Public License
-
-Version 1, 22 August 1998 Copyright 1998, Net Boolean Incorporated, Redwood City, California, USA All Rights Reserved.
-
-Note: This license is derived from the "Artistic License" as distributed with the Perl Programming Language. Its terms are different from those of the "Artistic License."
-
-PREAMBLE
-
-The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications.
-
-Definitions:
-
- "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification.
-
- "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder.
-
- "Copyright Holder" is whoever is named in the copyright or copyrights for the package.
-
- "You" is you, if you're thinking about copying or distributing this Package.
-
- "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.)
-
- "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it.
-
-1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers.
-
-2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version.
-
-3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following:
-
- a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package.
-
- b) use the modified Package only within your corporation or organization.
-
- c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version.
-
- d) make other distribution arrangements with the Copyright Holder.
-
-4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following:
-
- a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version.
-
- b) accompany the distribution with the machine-readable source of the Package with your modifications.
-
- c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version.
-
- d) make other distribution arrangements with the Copyright Holder.
-
-5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own.
-
-6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package.
-
-7. C subroutines supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language.
-
-8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission.
-
-9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-The End
diff --git a/options/license/NCBI-PD b/options/license/NCBI-PD
deleted file mode 100644
index d838cf36b9..0000000000
--- a/options/license/NCBI-PD
+++ /dev/null
@@ -1,19 +0,0 @@
-PUBLIC DOMAIN NOTICE
-National Center for Biotechnology Information
-
-This software is a "United States Government Work" under the terms of the
-United States Copyright Act. It was written as part of the authors'
-official duties as United States Government employees and thus cannot
-be copyrighted. This software is freely available to the public for
-use. The National Library of Medicine and the U.S. Government have not
-placed any restriction on its use or reproduction.
-
-Although all reasonable efforts have been taken to ensure the accuracy
-and reliability of the software and data, the NLM and the U.S.
-Government do not and cannot warrant the performance or results that
-may be obtained by using this software or data. The NLM and the U.S.
-Government disclaim all warranties, express or implied, including
-warranties of performance, merchantability or fitness for any
-particular purpose.
-
-Please cite the author in any work or product based on this material.
diff --git a/options/license/NCGL-UK-2.0 b/options/license/NCGL-UK-2.0
deleted file mode 100644
index 15c4f63c22..0000000000
--- a/options/license/NCGL-UK-2.0
+++ /dev/null
@@ -1,67 +0,0 @@
-Non-Commercial Government Licence
-for public sector information
-
-You are encouraged to use and re-use the Information that is available under this licence freely and flexibly, with only a few conditions.
-
-Using information under this licence
-
-Use of copyright and database right material expressly made available under this licence (the ‘Information’) indicates your acceptance of the terms and conditions below.
-
-The Licensor grants you a worldwide, royalty-free, perpetual, non-exclusive licence to use the Information for Non-Commercial purposes only subject to the conditions below.
-
-This licence does not affect your freedom under fair dealing or fair use or any other copyright or database right exceptions and limitations.
-
-You are free to:
- copy, publish, distribute and transmit the Information;
- adapt the Information;
- exploit the Information for Non-Commercial purposes for example, by combining it with other information in your own product or application.
-
-You are not permitted to:
- exercise any of the rights granted to you by this licence in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation.
-
-You must, where you do any of the above:
- acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence;
-
-If the Information Provider does not provide a specific attribution statement, you must use the following:
- Contains information licensed under the Non-Commercial Government Licence v2.0.
-
-If you are using Information from several Information Providers and listing multiple attributions is not practical in your product or application, you may include a URI or hyperlink to a resource that contains the required attribution statements.
- ensure that any onward licensing of the Information – for example when combined with other information – is for Non-Commercial purposes only.
-
-These are important conditions of this licence and if you fail to comply with them or use the Information other than for Non-Commercial purposes the rights granted to you under this licence, or any similar licence granted by the Licensor, will end automatically.
-
-Exemptions
-
-This licence does not cover the use of:
- • personal data in the Information;
- • Information that has not been accessed by way of publication or disclosure under information access legislation (including the Freedom of Information Acts for the UK and Scotland) by or with the consent of the Information Provider;
- • departmental or public sector organisation logos, crests, military insignia and the Royal Arms except where they form an integral part of a document or dataset;
- • military insignia
- • third party rights the Information Provider is not authorised to license;
- • other intellectual property rights, including patents, trade marks, and design rights; and
- • identity documents such as the British Passport.
-
-Non-endorsement
-This licence does not grant you any right to use the Information in a way that suggests any official status or that the Information Provider and/or Licensor endorse you or your use of the Information.
-
-No warranty
-The Information is licensed ‘as is’ and the Information Provider excludes all representations, warranties, obligations and liabilities in relation to the Information to the maximum extent permitted by law.
-The Information Provider is not liable for any errors or omissions in the Information and shall not be liable for any loss, injury or damage of any kind caused by its use. The Information Provider does not guarantee the continued supply of the Information.
-
-Governing Law
-This licence is governed by the laws of the jurisdiction in which the Information Provider has its principal place of business, unless otherwise specified by the Information Provider.
-
-Definitions
-In this licence the terms below have the following meanings:
-
-‘Information’ means information protected by copyright or by database right (for example, literary and artistic works, content, data and source code) offered for use under the terms of this licence.
-
-‘Information Provider’ means the person or organisation providing the Information under this licence.
-
-‘Licensor’ means any Information Provider which has the authority to offer Information under the terms of this licence or the Keeper of the Public Records, who has the authority to offer Information subject to Crown copyright and Crown database rights and Information subject to copyright and database right that has been assigned to or acquired by the Crown, under the terms of this licence.
-
-‘Non-Commercial purposes’ means not intended for or directed toward commercial advantage or private monetary compensation. For the purposes of this licence, ‘private monetary compensation’ does not include the exchange of the Information for other copyrighted works by means of digital file-sharing or otherwise provided there is no payment of any monetary compensation in connection with the exchange of the Information.
-
-‘Use’ as a verb, means doing any act which is restricted by copyright or database right, whether in the original medium or in any other medium, and includes without limitation distributing, copying, adapting, modifying as may be technically necessary to use it in a different mode or format.
-
-‘You’ means the natural or legal person, or body of persons corporate or incorporate, acquiring rights under this licence.
diff --git a/options/license/NCL b/options/license/NCL
deleted file mode 100644
index 3bfb658c26..0000000000
--- a/options/license/NCL
+++ /dev/null
@@ -1,32 +0,0 @@
-Copyright (c) 2004 the University Corporation for Atmospheric
-Research ("UCAR"). All rights reserved. Developed by NCAR's
-Computational and Information Systems Laboratory, UCAR,
-www.cisl.ucar.edu.
-
-Redistribution and use of the Software in source and binary forms,
-with or without modification, is permitted provided that the
-following conditions are met:
-
-- Neither the names of NCAR's Computational and Information Systems
-Laboratory, the University Corporation for Atmospheric Research,
-nor the names of its sponsors or contributors may be used to
-endorse or promote products derived from this Software without
-specific prior written permission.
-
-- Redistributions of source code must retain the above copyright
-notices, this list of conditions, and the disclaimer below.
-
-- Redistributions in binary form must reproduce the above copyright
-notice, this list of conditions, and the disclaimer below in the
-documentation and/or other materials provided with the
-distribution.
-
-THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
-HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
-EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
-SOFTWARE.
diff --git a/options/license/NCSA b/options/license/NCSA
deleted file mode 100644
index cf5413effa..0000000000
--- a/options/license/NCSA
+++ /dev/null
@@ -1,15 +0,0 @@
-University of Illinois/NCSA Open Source License
-
-Copyright (c) <Year> <Owner Organization Name>. All rights reserved.
-
-Developed by: <Name of Development Group> <Name of Institution> <URL for Development Group/Institution>
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with 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:
-
- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers.
-
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution.
-
- * Neither the names of <Name of Development Group, Name of Institution>, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
diff --git a/options/license/NGPL b/options/license/NGPL
deleted file mode 100644
index acd7c47a29..0000000000
--- a/options/license/NGPL
+++ /dev/null
@@ -1,21 +0,0 @@
-NETHACK GENERAL PUBLIC LICENSE
-(Copyright 1989 M. Stephenson)
-(Based on the BISON general public license, copyright 1988 Richard M. Stallman)
-Everyone is permitted to copy and distribute verbatim copies of this license, but changing it is not allowed. You can also use this wording to make the terms for other programs.
-The license agreements of most software companies keep you at the mercy of those companies. By contrast, our general public license is intended to give everyone the right to share NetHack. To make sure that you get the rights we want you to have, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. Hence this license agreement.
-Specifically, we want to make sure that you have the right to give away copies of NetHack, that you receive source code or else can get it if you want it, that you can change NetHack or use pieces of it in new free programs, and that you know you can do these things.
-To make sure that everyone has such rights, we have to forbid you to deprive anyone else of these rights. For example, if you distribute copies of NetHack, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights.
-Also, for our own protection, we must make certain that everyone finds out that there is no warranty for NetHack. If NetHack is modified by someone else and passed on, we want its recipients to know that what they have is not what we distributed.
-Therefore we (Mike Stephenson and other holders of NetHack copyrights) make the following terms which say what you must do to be allowed to distribute or change NetHack.
-COPYING POLICIES
- 1. You may copy and distribute verbatim copies of NetHack source code as you receive it, in any medium, provided that you keep intact the notices on all files that refer to copyrights, to this License Agreement, and to the absence of any warranty; and give any other recipients of the NetHack program a copy of this License Agreement along with the program.
- 2. You may modify your copy or copies of NetHack or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above (including distributing this License Agreement), provided that you also do the following:
- a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and
- b) cause the whole of any work that you distribute or publish, that in whole or in part contains or is a derivative of NetHack or any part thereof, to be licensed at no charge to all third parties on terms identical to those contained in this License Agreement (except that you may choose to grant more extensive warranty protection to some or all third parties, at your option)
- c) You may charge a distribution fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
- 3. You may copy and distribute NetHack (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following:
- a) accompany it with the complete machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or,
- b) accompany it with full information as to how to obtain the complete machine-readable source code from an appropriate archive site. (This alternative is allowed only for noncommercial distribution.)
- For these purposes, complete source code means either the full source distribution as originally released over Usenet or updated copies of the files in this distribution used to create the object code or executable.
- 4. You may not copy, sublicense, distribute or transfer NetHack except as expressly provided under this License Agreement. Any attempt otherwise to copy, sublicense, distribute or transfer NetHack is void and your rights to use the program under this License agreement shall be automatically terminated. However, parties who have received computer software programs from you with this License Agreement will not have their licenses terminated so long as such parties remain in full compliance.
-Stated plainly: You are permitted to modify NetHack, or otherwise use parts of NetHack, provided that you comply with the conditions specified above; in particular, your modified NetHack or program containing parts of NetHack must remain freely available as provided in this License Agreement. In other words, go ahead and share NetHack, but don't try to stop anyone else from sharing it farther.
diff --git a/options/license/NICTA-1.0 b/options/license/NICTA-1.0
deleted file mode 100644
index 04622e308d..0000000000
--- a/options/license/NICTA-1.0
+++ /dev/null
@@ -1,61 +0,0 @@
-NICTA Public Software Licence
-Version 1.0
-
-Copyright © 2004 National ICT Australia Ltd
-
-All rights reserved.
-
-By this licence, National ICT Australia Ltd (NICTA) grants permission,
-free of charge, to any person who obtains a copy of this software
-and any associated documentation files ("the Software") to use and
-deal with the Software in source code and binary forms without
-restriction, with or without modification, and to permit persons
-to whom the Software is furnished to do so, provided that the
-following conditions are met:
-
-- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimers.
-- Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimers in
- the documentation and/or other materials provided with the
- distribution.
-- The name of NICTA may not be used to endorse or promote products
- derived from this Software without specific prior written permission.
-
-EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT
-PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS" AND
-NICTA MAKES NO REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY
-KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY
-REPRESENTATIONS, WARRANTIES OR CONDITIONS REGARDING THE CONTENTS
-OR ACCURACY OF THE SOFTWARE, OR OF TITLE, MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, THE ABSENCE OF LATENT
-OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR
-NOT DISCOVERABLE.
-
-TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL
-NICTA BE LIABLE ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
-NEGLIGENCE) FOR ANY LOSS OR DAMAGE WHATSOEVER, INCLUDING (WITHOUT
-LIMITATION) LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR
-CORRUPTION OF DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS,
-OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC LOSS;
-OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR
-EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THIS LICENCE,
-THE SOFTWARE OR THE USE OF THE SOFTWARE, EVEN IF NICTA HAS BEEN
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-If applicable legislation implies warranties or conditions, or
-imposes obligations or liability on NICTA in respect of the Software
-that cannot be wholly or partly excluded, restricted or modified,
-NICTA's liability is limited, to the full extent permitted by the
-applicable legislation, at its option, to:
-
-a. in the case of goods, any one or more of the following:
- i. the replacement of the goods or the supply of equivalent goods;
- ii. the repair of the goods;
- iii. the payment of the cost of replacing the goods or of acquiring
- equivalent goods;
- iv. the payment of the cost of having the goods repaired; or
-b. in the case of services:
- i. the supplying of the services again; or
- ii. the payment of the cost of having the services supplied
- again.
diff --git a/options/license/NIST-PD b/options/license/NIST-PD
deleted file mode 100644
index e1a4e65bab..0000000000
--- a/options/license/NIST-PD
+++ /dev/null
@@ -1,15 +0,0 @@
-Terms Of Use
-
-This software was developed by employees of the National Institute of Standards
-and Technology (NIST), and others. This software has been contributed to the
-public domain. Pursuant to title 15 Untied States Code Section 105, works of
-NIST employees are not subject to copyright protection in the United States and
-are considered to be in the public domain. As a result, a formal license is
-not needed to use this software.
-
-This software is provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS,
-IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT AND DATA
-ACCURACY. NIST does not warrant or make any representations regarding the use
-of the software or the results thereof, including but not limited to the
-correctness, accuracy, reliability or usefulness of this software.
diff --git a/options/license/NIST-PD-fallback b/options/license/NIST-PD-fallback
deleted file mode 100644
index 49f91bce1f..0000000000
--- a/options/license/NIST-PD-fallback
+++ /dev/null
@@ -1,5 +0,0 @@
-Conditions of Use
-
-This software was developed by employees of the National Institute of Standards and Technology (NIST), an agency of the Federal Government and is being made available as a public service. Pursuant to title 17 United States Code Section 105, works of NIST employees are not subject to copyright protection in the United States. This software may be subject to foreign copyright. Permission in the United States and in foreign countries, to the extent that NIST may hold copyright, to use, copy, modify, create derivative works, and distribute this software and its documentation without fee is hereby granted on a non-exclusive basis, provided that this notice and disclaimer of warranty appears in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
diff --git a/options/license/NIST-Software b/options/license/NIST-Software
deleted file mode 100644
index 0ae22d9052..0000000000
--- a/options/license/NIST-Software
+++ /dev/null
@@ -1,28 +0,0 @@
-NIST-developed software is provided by NIST as a public service.
-You may use, copy, and distribute copies of the software in any
-medium, provided that you keep intact this entire notice. You may
-improve, modify, and create derivative works of the software or any
-portion of the software, and you may copy and distribute such
-modifications or works. Modified works should carry a notice stating
-that you changed the software and should note the date and nature of
-any such change. Please explicitly acknowledge the National Institute
-of Standards and Technology as the source of the software.
-
-NIST-developed software is expressly provided "AS IS." NIST MAKES NO
-WARRANTY OF ANY KIND, EXPRESS, IMPLIED, IN FACT, OR ARISING BY OPERATION
-OF LAW, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND DATA ACCURACY. NIST
-NEITHER REPRESENTS NOR WARRANTS THAT THE OPERATION OF THE SOFTWARE WILL BE
-UNINTERRUPTED OR ERROR-FREE, OR THAT ANY DEFECTS WILL BE CORRECTED. NIST DOES
-NOT WARRANT OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF THE SOFTWARE OR
-THE RESULTS THEREOF, INCLUDING BUT NOT LIMITED TO THE CORRECTNESS, ACCURACY,
-RELIABILITY, OR USEFULNESS OF THE SOFTWARE.
-
-You are solely responsible for determining the appropriateness of using and
-distributing the software and you assume all risks associated with its use,
-including but not limited to the risks and costs of program errors, compliance
-with applicable laws, damage to or loss of data, programs or equipment, and the
-unavailability or interruption of operation. This software is not intended to be
-used in any situation where a failure could cause risk of injury or damage to
-property. The software developed by NIST employees is not subject to copyright
-protection within the United States.
diff --git a/options/license/NLOD-1.0 b/options/license/NLOD-1.0
deleted file mode 100644
index b0b0767e38..0000000000
--- a/options/license/NLOD-1.0
+++ /dev/null
@@ -1,79 +0,0 @@
-Norwegian Licence for Open Government Data (NLOD) 1.0
-
-Preface of licence
-
-This licence grants you the right to copy, use and distribute information, provided you acknowledge the contributors and comply with the terms and conditions stipulated in this licence. By using information made available under this licence, you accept the terms and conditions set forth in this licence. As set out in Section 7, the licensor disclaims any and all liability for the quality of the information and what the information is used for.
-
-This licence shall not impose any limitations on the rights or freedoms of the licensee under the Norwegian Freedom of Information Act or any other legislation granting the general public a right of access to public sector information, or that follow from exemptions or limitations stipulated in the Norwegian Copyright Act. Further, the licence shall not impose any limitations on the licensee's freedom of expression recognized by law.
-
-1. Definitions
-
- «Database» shall mean a database or similar protected under Section 43 of the Norwegian Copyright Act.
- «Information» shall mean texts, images, recordings, data sets or other works protected under Section 1 of the Norwegian Copyright Act, or which are protected under provisions addressing what is referred to as «neighbouring rights» in Chapter 5 of the Norwegian Copyright Act (including databases and photographs), and which are distributed under this licence.
- «Copy» shall mean reproduction in any form.
- «Licensee» and «you» shall mean natural or legal persons using information under this licence.
- «Licensor» shall mean the natural or legal person that makes information available under this licence.
- «Distribute» shall mean any actions whereby information is made available, including to distribute, transfer, communicate, disperse, show, perform, sell, lend and rent.
- «Use» shall mean one or more actions relevant to copyright law requiring permission from the owner of the copyright.
-
-2. Licence
-The licensee, subject to the limitations that follow from this licence, may use the information for any purpose and in all contexts, by:
-
- * copying the information and distributing the information to others,
- * modifying the information and/or combining the information with other information, and
- * copying and distributing such changed or combined information.
- * This is a non-exclusive, free, perpetual and worldwide licence. The information may be used in any medium and format known today and/or which will become known in the future. The Licensee shall not sub-license or transfer this licence.
-
-3. Exemptions
-The licence does not apply to and therefore does not grant a right to use:
-
- * information which contains personal data covered by the Norwegian Personal Data Act unless there is a legitimate basis for the disclosure and further processing of the personal data
- * information distributed in violation of a statutory obligation to observe confidentiality
- * information excluded from public disclosure pursuant to law, including information deemed sensitive under the Norwegian National Security Act
- * information subject to third party rights which the licensor is not authorised to license to the licensee
- * information protected by intellectual property rights other than copyright and neighbouring rights in accordance with Chapter 5 of the Norwegian Copyright Act, such as trademarks, patents and design rights, but this does not entail an impediment to use information where the licensor's logo has been permanently integrated into the information or to attribute the origin of the information in accordance with the article below relating to attribution.
-
-If the licensor has made available information not covered by the licence according to the above list, the licensee must cease all use of the information under the licence, and erase the information as soon as he or she becomes aware of or should have understood that the information is not covered by the licence.
-
-4. Effects of breach of the licence
-The licence is subject to the licensee's compliance with the terms and conditions of this licence. In the event that the licensee commits a breach of this licence, this will entail that the licensee's right to use the information will be revoked immediately without further notice. In case of such a breach, the licensee must immediately and without further notice take measures to cause the infringement to end. Because the right to use the information has been terminated, the licensee must cease all use of the information by virtue of the licence.
-
-5. Attribution
-The licensee shall attribute the licensor as specified by the licensor and include a reference to this licence. To the extent practically possible, the licensee shall provide a link to both this licence and the source of the information.
-
-If the licensor has not specified how attributions shall be made, the licensee shall normally state the following: «Contains data under the Norwegian licence for Open Government data (NLOD) distributed by [name of licensor]».
-
-If the licensor has specified that the information shall only be available under a specific version of this licence, cf. Section 10, the licensee shall also state this.
-
-If the information has been changed, the licensee must clearly indicate that changes have been made by the licensee.
-
-6. Proper use
-The licensee shall not use the information in a manner that appears misleading nor present the information in a distorted or incorrect manner.
-Neither the licensor's nor other contributors' names or trademarks must be used to support, recommend or market the licensee or any products or services using the information.
-
-7. Disclaimer of liability
-The information is licensed «as is». The information may contain errors and omissions. The licensor provides no warranties, including relating to the content and relevance of the information.
-
-The licensor disclaims any liability for errors and defects associated with the information to the maximum extent permitted by law.
-
-The licensor shall not be liable for direct or indirect losses as a result of use of the information or in connection with copying or further distribution of the information.
-
-8. Guarantees regarding data quality and accessibility
-This licence does not prevent the licensor from issuing supplementary statements regarding expected or intended data quality and accessibility. Such statements shall be regarded as indicative in nature and not binding on the part of the licensor. The disclaimers in Section 7 also apply in full for such indicative statements. Based on separate agreement, the licensor may provide guarantees and distribute the information on terms and conditions different from those set forth in this licence.
-
-9. Licence compatibility
-If the licensee is to distribute an adapted or combined work based on information covered by this licence and some other work licensed under a licence compatible by contract, such distribution may be based on an appropriate licence compatible by contract, cf. the list below.
-
-A licence compatible by contract shall mean the following licences:
-
- * for all information: Open Government Licence (version 1.0),
- * for those parts of the information which do not constitute databases: Creative Commons Attribution Licence (generic version 1.0, 2.0, 2.5 and unported version 3.0) and Creative Commons Navngivelse 3.0 Norge,
- * for those parts of the information which constitute databases: Open Data Commons Attribution License (version 1.0).
-
-This provision does not prevent other licences from being compatible with this licence based on their content.
-
-10. New versions of the licence
-The licensee may choose to use the information covered by this licence under any new versions of the Norwegian licence for Open Government data (NLOD) issued by the responsible ministry (currently the Ministry of Government Administration, Reform and Church Affairs) when these versions are final and official, unless the licensor when making the information available under this licence specifically has stated that solely version 1.0 of this licence may be used.
-
-11. Governing law and legal venue
-This licence, including its formation, and any disputes and claims arising in connection with or relating to this licence, shall be regulated by Norwegian law. The legal venue shall be the licensor's ordinary legal venue. The licensor may, with regard to intellectual proprietary rights, choose to pursue a claim at other competent legal venues and/or based on the laws of the country where the intellectual property rights are sought enforced.
diff --git a/options/license/NLOD-2.0 b/options/license/NLOD-2.0
deleted file mode 100644
index 6233940c11..0000000000
--- a/options/license/NLOD-2.0
+++ /dev/null
@@ -1,80 +0,0 @@
-Norwegian Licence for Open Government Data (NLOD) 2.0
-
-Preface of licence
-
-This licence grants you the right to copy, use and distribute information, provided you acknowledge the contributors and comply with the terms and conditions stipulated in this licence. By using information made available under this licence, you accept the terms and conditions set forth in this licence. As set out in Section 7, the licensor disclaims any and all liability for the quality of the information and what the information is used for.
-
-This licence shall not impose any limitations on the rights or freedoms of the licensee under the Norwegian Freedom of Information Act or any other legislation granting the general public a right of access to public sector information, or that follow from exemptions or limitations stipulated in the Norwegian Copyright Act. Further, the licence shall not impose any limitations on the licensee’s freedom of expression recognized by law.
-
-1. Definitions
-
- «Database» shall mean a database or similar protected under Section 43 of the Norwegian Copyright Act.
- «Information» shall mean texts, images, recordings, data sets or other works protected under Section 1 of the Norwegian Copyright Act, or which are protected under provisions addressing what is referred to as «neighbouring rights» in Chapter 5 of the Norwegian Copyright Act (including databases and photographs), and which are distributed under this licence.
- «Copy» shall mean reproduction in any form.
- «Licensee» and «you» shall mean natural or legal persons using information under this licence.
- «Licensor» shall mean the natural or legal person that makes information available under this licence.
- «Distribute» shall mean any actions whereby information is made available, including to distribute, transfer, communicate, disperse, show, perform, sell, lend and rent.
- «Use» shall mean one or more actions relevant to copyright law requiring permission from the owner of the copyright.
-
-2. Licence
-The licensee, subject to the limitations that follow from this licence, may use the information for any purpose and in all contexts, by:
-
- * copying the information and distributing the information to others,
- * modifying the information and/or combining the information with other information, and
- * copying and distributing such changed or combined information.
-
-This is a non-exclusive, free, perpetual and worldwide licence. The information may be used in any medium and format known today and/or which will become known in the future. The Licensee shall not sub-license or transfer this licence.
-
-3. Exemptions
-The licence does not apply to and therefore does not grant a right to use:
-
- * information which contains personal data covered by the Norwegian Personal Data Act unless there is a legitimate basis for the disclosure and further processing of the personal data
- * information distributed in violation of a statutory obligation to observe confidentiality
- * information excluded from public disclosure pursuant to law, including information deemed sensitive under the Norwegian National Security Act
- * information subject to third party rights which the licensor is not authorised to license to the licensee
- * information protected by intellectual property rights other than copyright and neighbouring rights in accordance with Chapter 5 of the Norwegian Copyright Act, such as trademarks, patents and design rights, but this does not entail an impediment to use information where the licensor’s logo has been permanently integrated into the information or to attribute the origin of the information in accordance with the article below relating to attribution.
-
-If the licensor has made available information not covered by the licence according to the above list, the licensee must cease all use of the information under the licence, and erase the information as soon as he or she becomes aware of or should have understood that the information is not covered by the licence.
-
-4. Effects of breach of the licence
-The licence is subject to the licensee’s compliance with the terms and conditions of this licence. In the event that the licensee commits a breach of this licence, this will entail that the licensee’s right to use the information will be revoked immediately without further notice. In case of such a breach, the licensee must immediately and without further notice take measures to cause the infringement to end. Because the right to use the information has been terminated, the licensee must cease all use of the information by virtue of the licence.
-
-5. Attribution
-The licensee shall attribute the licensor as specified by the licensor and include a reference to this licence. To the extent practically possible, the licensee shall provide a link to both this licence and the source of the information.
-
-If the licensor has not specified how attributions shall be made, the licensee shall normally state the following: «Contains data under the Norwegian licence for Open Government data (NLOD) distributed by [name of licensor]».
-
-If the licensor has specified that the information shall only be available under a specific version of this licence, cf. Section 10, the licensee shall also state this.
-
-If the information has been changed, the licensee must clearly indicate that changes have been made by the licensee.
-
-6. Proper use
-The licensee shall not use the information in a manner that appears misleading nor present the information in a distorted or incorrect manner.
-Neither the licensor’s nor other contributors' names or trademarks must be used to support, recommend or market the licensee or any products or services using the information.
-
-7. Disclaimer of liability
-The information is licensed «as is». The information may contain errors and omissions. The licensor provides no warranties, including relating to the content and relevance of the information.
-
-The licensor disclaims any liability for errors and defects associated with the information to the maximum extent permitted by law.
-
-The licensor shall not be liable for direct or indirect losses as a result of use of the information or in connection with copying or further distribution of the information.
-
-8. Guarantees regarding data quality and accessibility
-This licence does not prevent the licensor from issuing supplementary statements regarding expected or intended data quality and accessibility. Such statements shall be regarded as indicative in nature and not binding on the part of the licensor. The disclaimers in Section 7 also apply in full for such indicative statements. Based on separate agreement, the licensor may provide guarantees and distribute the information on terms and conditions different from those set forth in this licence.
-
-9. Licence compatibility
-If the licensee is to distribute an adapted or combined work based on information covered by this licence and some other work licensed under a licence compatible by contract, such distribution may be based on an appropriate licence compatible by contract, cf. the list below.
-
-A licence compatible by contract shall mean the following licences:
-
- * for all information: Open Government Licence (version 1.0, 2.0 and 3.0), Creative Commons Attribution Licence (international version 4.0 and norwegian version 4.0),
- * for those parts of the information which do not constitute databases: Creative Commons Attribution Licence (generic version 1.0, 2.0, 2.5 and unported version 3.0) and Creative Commons Navngivelse 3.0 Norge,
- * for those parts of the information which constitute databases: Open Data Commons Attribution License (version 1.0).
-
-This provision does not prevent other licences from being compatible with this licence based on their content.
-
-10. New versions of the licence
-The licensee may choose to use the information covered by this licence under any new versions of the Norwegian licence for Open Government data (NLOD) issued by the responsible ministry (currently the Ministry of Local Government and Modernisation) when these versions are final and official, unless the licensor when making the information available under this licence specifically has stated that solely version 2.0 of this licence may be used.
-
-11. Governing law and legal venue
-This licence, including its formation, and any disputes and claims arising in connection with or relating to this licence, shall be regulated by Norwegian law. The legal venue shall be the licensor’s ordinary legal venue. The licensor may, with regard to intellectual proprietary rights, choose to pursue a claim at other competent legal venues and/or based on the laws of the country where the intellectual property rights are sought enforced.
diff --git a/options/license/NLPL b/options/license/NLPL
deleted file mode 100644
index 79f83af89b..0000000000
--- a/options/license/NLPL
+++ /dev/null
@@ -1,14 +0,0 @@
-NO LIMIT PUBLIC LICENSE
- Version 0, June 2012
-
-Gilles LAMIRAL
-La Billais
-35580 Baulon
-France
-
-NO LIMIT PUBLIC LICENSE
-
-Terms and conditions for copying, distribution, modification
-or anything else.
-
- 0. No limit to do anything with this work and this license.
diff --git a/options/license/NOSL b/options/license/NOSL
deleted file mode 100644
index ff16a148c6..0000000000
--- a/options/license/NOSL
+++ /dev/null
@@ -1,150 +0,0 @@
-NETIZEN OPEN SOURCE LICENSE
-Version 1.0
-
-1. Definitions.
-
- 1.0.1. "Commercial Use" means distribution or otherwise making the Covered Code available to a third party.
-
- 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications.
-
- 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
- 1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
- 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
- 1.5. "Executable" means Covered Code in any form other than Source Code.
-
- 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
- 1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.8. "License" means this document.
-
- 1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
- 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
- A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
- B. Any new file that contains any part of the Original Code or previous Modifications.
-
- 1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
- 1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
- 1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
- 1.12. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2. Source Code License.
-
- 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
- (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and
- (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).
- (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.
- (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices.
-
- 2.2. Contributor Grant.
- Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license
- (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and
- (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
- (c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first makes Commercial Use of the Covered Code.
- (d) Notwithstanding Section 2.2(b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor.
-
-3. Distribution Obligations.
-
- 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
- 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
- 3.3. Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
- 3.4. Intellectual Property Matters
- (a) Third Party Claims.
- If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
- (b) Contributor APIs.
- If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file.
- (c) Representations.
- Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.
-
- 3.5. Required Notices.
- You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
- 3.6. Distribution of Executable Versions.
- You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
- 3.7. Larger Works.
- You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single LEDs product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Application of this License.
-
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code.
-
-6. Versions of the License.
-
- 6.1. New Versions.
- Netizen Pty Ltd ("Netizen ") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
- 6.2. Effect of New Versions.
- Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netizen. No one other than Netizen has the right to modify the terms applicable to Covered Code created under this License.
-
- 6.3. Derivative Works.
- If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases "Netizen", "NOSL" or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the Netizen Open Source License and Xen Open Source License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
-7. DISCLAIMER OF WARRANTY.
-
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
- 7.1 To the extent permitted by law and except as expressly provided to the contrary in this Agreement, all warranties whether express, implied, statutory or otherwise, relating in any way to the subject matter of this Agreement or to this Agreement generally, are excluded. Where legislation implies in this Agreement any condition or warranty and that legislation avoids or prohibits provisions in a contract excluding or modifying the application of or the exercise of or liability under such term, such term shall be deemed to be included in this Agreement. However, the liability of Supplier for any breach of such term shall be limited, at the option of Supplier, to any one or more of the following: if the breach related to goods: the replacement of the goods or the supply of equivalent goods; the repair of such goods; the payment of the cost of replacing the goods or of acquiring equivalent goods; or the payment of the cost of having the goods repaired; and if the breach relates to services the supplying of the services again; or the payment of the cost of having the services supplied again.
-
-8. TERMINATION.
-
- 8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
- 8.2. If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant") alleging that:
- (a) such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.
- (b) any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant.
-
- 8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
- 8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY.
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS.
-
-The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
-11. MISCELLANEOUS.
-
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
-This Agreement shall be governed by and construed according to the law of the State of Victoria. The parties irrevocably submit to the exclusive jurisdiction of the Courts of Victoria and Australia and any Courts hearing appeals from such Courts. This Agreement is deemed to have been made in Victoria.
-The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-12. RESPONSIBILITY FOR CLAIMS.
-
-As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-13. MULTIPLE-LICENSED CODE.
-
-Initial Developer may designate portions of the Covered Code as "Multiple-Licensed". "Multiple-Licensed" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the NPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A.
-
-EXHIBIT A - Netizen Open Source License
-
- ``The contents of this file are subject to the Netizen Open Source License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://netizen.com.au/licenses/NOPL/
-
- Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
- The Original Code is ______________________________________.
-
- The Initial Developer of the Original Code is ________________________. Portions created by ______________________ are Copyright (C) ______ _______________________. All Rights Reserved.
-
- Contributor(s): ______________________________________.
-
- Alternatively, the contents of this file may be used under the terms of the _____ license (the "[___] License"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the NOSL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the NOSL or the [___] License."
-
- [NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.]
diff --git a/options/license/NPL-1.0 b/options/license/NPL-1.0
deleted file mode 100644
index 65983791a2..0000000000
--- a/options/license/NPL-1.0
+++ /dev/null
@@ -1,102 +0,0 @@
-NETSCAPE PUBLIC LICENSE Version 1.0
-
-1. Definitions.
-
- 1.1. ``Contributor'' means each entity that creates or contributes to the creation of Modifications.
-
- 1.2. ``Contributor Version'' means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
- 1.3. ``Covered Code'' means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
- 1.4. ``Electronic Distribution Mechanism'' means a mechanism generally accepted in the software development community for the electronic transfer of data.
- 1.5. ``Executable'' means Covered Code in any form other than Source Code.
- 1.6. ``Initial Developer'' means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
- 1.7. ``Larger Work'' means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
- 1.8. ``License'' means this document.
- 1.9. ``Modifications'' means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
- A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications. B. Any new file that contains any part of the Original Code or previous Modifications.
-
- 1.10. ``Original Code'' means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
- 1.11. ``Source Code'' means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or a list of source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
- 1.12. ``You'' means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, ``You'' includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, ``control'' means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
-2. Source Code License.
-
- 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
- (a) to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, or as part of a Larger Work; and
- (b) under patents now or hereafter owned or controlled by Initial Developer, to make, have made, use and sell (``Utilize'') the Original Code (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Original Code (or portions thereof) and not to any greater extent that may be necessary to Utilize further Modifications or combinations.
-
- 2.2. Contributor Grant. Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
- (a) to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code or as part of a Larger Work; and
- (b) under patents now or hereafter owned or controlled by Contributor, to Utilize the Contributor Version (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Contributor Version (or portions thereof), and not to any greater extent that may be necessary to Utilize further Modifications or combinations.
-
-3. Distribution Obligations.
-
- 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
- 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
- 3.3. Description of Modifications. You must cause all Covered Code to which you contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
- 3.4. Intellectual Property Matters
- (a) Third Party Claims. If You have knowledge that a party claims an intellectual property right in particular functionality or code (or its utilization under this License), you must include a text file with the source code distribution titled ``LEGAL'' which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after You make Your Modification available as described in Section 3.2, You shall promptly modify the LEGAL file in all copies You make available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
- (b) Contributor APIs. If Your Modification is an application programming interface and You own or control patents which are reasonably necessary to implement that API, you must also include this information in the LEGAL file.
-
- 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code, and this License in any documentation for the Source Code, where You describe recipients' rights relating to Covered Code. If You created one or more Modification(s), You may add your name as a Contributor to the notice described in Exhibit A. If it is not possible to put such notice in a particular Source Code file due to its structure, then you must include such notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
- 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
- 3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Application of this License.
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A, and to related Covered Code.
-
-6. Versions of the License.
- 6.1. New Versions. Netscape Communications Corporation (``Netscape'') may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
- 6.2. Effect of New Versions. Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License.
- 6.3. Derivative Works. If you create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), you must (a) rename Your license so that the phrases ``Mozilla'', ``MOZILLAPL'', ``MOZPL'', ``Netscape'', ``NPL'' or any confusingly similar phrase do not appear anywhere in your license and (b) otherwise make it clear that your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
-7. DISCLAIMER OF WARRANTY.
-
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN ``AS IS'' BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-
-This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-9. LIMITATION OF LIABILITY.
-
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO YOU OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS.
-
-The Covered Code is a ``commercial item,'' as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of ``commercial computer software'' and ``commercial computer software documentation,'' as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
-11. MISCELLANEOUS.
-
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in, the United States of America: (a) unless otherwise agreed in writing, all disputes relating to this License (excepting any dispute relating to intellectual property rights) shall be subject to final and binding arbitration, with the losing party paying all costs of arbitration; (b) any arbitration relating to this Agreement shall be held in Santa Clara County, California, under the auspices of JAMS/EndDispute; and (c) any litigation relating to this Agreement shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-12. RESPONSIBILITY FOR CLAIMS.
-
-Except in cases where another Contributor has failed to comply with Section 3.4, You are responsible for damages arising, directly or indirectly, out of Your utilization of rights under this License, based on the number of copies of Covered Code you made available, the revenues you received from utilizing such rights, and other relevant factors. You agree to work with affected parties to distribute responsibility on an equitable basis.
-
-AMENDMENTS
-Additional Terms applicable to the Netscape Public License.
-
- I. Effect. These additional terms described in this Netscape Public License -- Amendments shall apply to the Mozilla Communicator client code and to all Covered Code under this License.
- II. ``Netscape's Branded Code'' means Covered Code that Netscape distributes and/or permits others to distribute under one or more trademark(s) which are controlled by Netscape but which are not licensed for use under this License.
- III. Netscape and logo. This License does not grant any rights to use the trademark ``Netscape'', the ``Netscape N and horizon'' logo or the Netscape lighthouse logo, even if such marks are included in the Original Code.
- IV. Inability to Comply Due to Contractual Obligation. Prior to licensing the Original Code under this License, Netscape has licensed third party code for use in Netscape's Branded Code. To the extent that Netscape is limited contractually from making such third party code available under this License, Netscape may choose to reintegrate such code into Covered Code without being required to distribute such code in
-Source Code form, even if such code would otherwise be considered ``Modifications'' under this License.
- V. Use of Modifications and Covered Code by Initial Developer.
- V.1. In General. The obligations of Section 3 apply to Netscape, except to the extent specified in this Amendment, Section V.2 and V.3. V.2. Other Products. Netscape may include Covered Code in products other than the Netscape's Branded Code which are released by Netscape during the two (2) years following the release date of the Original Code, without such additional products becoming subject to the terms of this License, and may license such additional products on different terms from those contained in this License. V.3. Alternative Licensing. Netscape may license the Source Code of Netscape's Branded Code, including Modifications incorporated therein, without such additional products becoming subject to the terms of this License, and may license such additional products on different terms from those contained in this License.
-
- VI. Arbitration and Litigation. Notwithstanding the limitations of Section 11 above, the provisions regarding arbitration and litigation in Section 11(a), (b) and (c) of the License shall apply to all disputes relating to this License.
-
-EXHIBIT A.
-
-“The contents of this file are subject to the Netscape Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/NPL/
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-The Original Code is Mozilla Communicator client code, released March 31, 1998.
-The Initial Developer of the Original Code is Netscape Communications Corporation. Portions created by Netscape are Copyright (C) 1998 Netscape Communications Corporation. All Rights Reserved.
-Contributor(s): ______________________________________.â€
-[NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. This is due to time constraints encountered in simultaneously finalizing the License and in preparing the Original Code for release. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.]
diff --git a/options/license/NPL-1.1 b/options/license/NPL-1.1
deleted file mode 100644
index 0d5457ff04..0000000000
--- a/options/license/NPL-1.1
+++ /dev/null
@@ -1,186 +0,0 @@
-Netscape Public LIcense version 1.1
-
-AMENDMENTS
-
-The Netscape Public License Version 1.1 ("NPL") consists of the Mozilla Public License Version 1.1 with the following Amendments, including Exhibit A-Netscape Public License. Files identified with "Exhibit A-Netscape Public License" are governed by the Netscape Public License Version 1.1.
-
-Additional Terms applicable to the Netscape Public License.
-
- I. Effect.
- These additional terms described in this Netscape Public License -- Amendments shall apply to the Mozilla Communicator client code and to all Covered Code under this License.
-
- II. "Netscape's Branded Code" means Covered Code that Netscape distributes and/or permits others to distribute under one or more trademark(s) which are controlled by Netscape but which are not licensed for use under this License.
- III. Netscape and logo.
- This License does not grant any rights to use the trademarks "Netscape", the "Netscape N and horizon" logo or the "Netscape lighthouse" logo, "Netcenter", "Gecko", "Java" or "JavaScript", "Smart Browsing" even if such marks are included in the Original Code or Modifications.
- IV. Inability to Comply Due to Contractual Obligation.
- Prior to licensing the Original Code under this License, Netscape has licensed third party code for use in Netscape's Branded Code. To the extent that Netscape is limited contractually from making such third party code available under this License, Netscape may choose to reintegrate such code into Covered Code without being required to distribute such code in Source Code form, even if such code would otherwise be considered "Modifications" under this License.
- V. Use of Modifications and Covered Code by Initial Developer.
- V.1. In General.
- The obligations of Section 3 apply to Netscape, except to the extent specified in this Amendment, Section V.2 and V.3.
-
- V.2. Other Products.
- Netscape may include Covered Code in products other than the Netscape's Branded Code which are released by Netscape during the two (2) years following the release date of the Original Code, without such additional products becoming subject to the terms of this License, and may license such additional products on different terms from those contained in this License.
-
- V.3. Alternative Licensing.
- Netscape may license the Source Code of Netscape's Branded Code, including Modifications incorporated therein, without such Netscape Branded Code becoming subject to the terms of this License, and may license such Netscape Branded Code on different terms from those contained in this License.
-
- VI. Litigation.
- Notwithstanding the limitations of Section 11 above, the provisions regarding litigation in Section 11(a), (b) and (c) of the License shall apply to all disputes relating to this License.
-
- EXHIBIT A-Netscape Public License.
-
-"The contents of this file are subject to the Netscape Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/NPL/
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-The Original Code is Mozilla Communicator client code, released March 31, 1998.
-
-The Initial Developer of the Original Code is Netscape Communications Corporation. Portions created by Netscape are Copyright (C) 1998-1999 Netscape Communications Corporation. All Rights Reserved.
-Contributor(s): ______________________________________.
-
-Alternatively, the contents of this file may be used under the terms of the _____ license (the "[___] License"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the NPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the NPL or the [___] License."
-
-
-Mozilla Public License Version 1.1
-
-1. Definitions.
-
- 1.0.1. "Commercial Use" means distribution or otherwise making the Covered Code available to a third party.
-
- 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications.
-
- 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
- 1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
- 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
- 1.5. "Executable" means Covered Code in any form other than Source Code.
-
- 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
- 1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.8. "License" means this document.
-
- 1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
- 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
-Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
-Any new file that contains any part of the Original Code or previous Modifications.
-
- 1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
- 1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
- 1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
- 1.12. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2. Source Code License.
-
- 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- a. under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and
- b. under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).
- c. the licenses granted in this Section 2.1 (a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.
- d. Notwithstanding Section 2.1 (b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices.
-
- 2.2. Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license
-
- a. under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and
- b. under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
- c. the licenses granted in Sections 2.2 (a) and 2.2 (b) are effective on the date Contributor first makes Commercial Use of the Covered Code.
- d. Notwithstanding Section 2.2 (b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor.
-
-3. Distribution Obligations.
-
- 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
- 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
- 3.3. Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
- 3.4. Intellectual Property Matters
-
- (a) Third Party Claims
- If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
-
- (b) Contributor APIs
- If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file.
-
- (c) Representations.
- Contributor represents that, except as disclosed pursuant to Section 3.4 (a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.
-
- 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
- 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Sections 3.1, 3.2, 3.3, 3.4 and 3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
- 3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Application of this License.
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code.
-
-6. Versions of the License.
-
- 6.1. New Versions
- Netscape Communications Corporation ("Netscape") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
- 6.2. Effect of New Versions
- Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License.
-
- 6.3. Derivative Works
- If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", "MPL", "NPL" or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
-7. DISCLAIMER OF WARRANTY
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. Termination
-
- 8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
- 8.2. If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant") alleging that:
-
- a. such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.
- b. any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant.
-
- 8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
- 8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. government end users
-The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
-11. Miscellaneous
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-12. Responsibility for claims
-As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-13. Multiple-licensed code
-Initial Developer may designate portions of the Covered Code as "Multiple-Licensed". "Multiple-Licensed" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the MPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A.
-
-Exhibit A - Mozilla Public License.
-
-"The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-The Original Code is ______________________________________.
-
-The Initial Developer of the Original Code is ________________________.
-Portions created by ______________________ are Copyright (C) ______
-_______________________. All Rights Reserved.
-
-Contributor(s): ______________________________________.
-
-Alternatively, the contents of this file may be used under the terms of the _____ license (the "[___] License"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the [___] License."
-
-NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.
diff --git a/options/license/NPOSL-3.0 b/options/license/NPOSL-3.0
deleted file mode 100644
index e065bae727..0000000000
--- a/options/license/NPOSL-3.0
+++ /dev/null
@@ -1,59 +0,0 @@
-Non-Profit Open Software License 3.0
-
-This Non-Profit Open Software License ("Non-Profit OSL") version 3.0 (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
-
- Licensed under the Non-Profit Open Software License version 3.0
-
-1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
-
- a) to reproduce the Original Work in copies, either alone or as part of a collective work;
-
- b) to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
-
- c) to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Non-Profit Open Software License or as provided in section 17(d);
-
- d) to perform the Original Work publicly; and
-
- e) to display the Original Work publicly.
-
-2) Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
-
-3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
-
-4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
-
-5) External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
-
-6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
-
-7) Warranty of Provenance and Disclaimer of Warranty. The Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
-
-8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
-
-9) Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
-
-10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
-
-11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
-
-12) Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
-
-13) Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
-
-14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
-
-16) Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
-
-17) Non-Profit Amendment. The name of this amended version of the Open Software License ("OSL 3.0") is "Non-Profit Open Software License 3.0". The original OSL 3.0 license has been amended as follows:
-
- (a) Licensor represents and declares that it is a not-for-profit organization that derives no revenue whatsoever from the distribution of the Original Work or Derivative Works thereof, or from support or services relating thereto.
-
- (b) The first sentence of Section 7 ["Warranty of Provenance"] of OSL 3.0 has been stricken. For Original Works licensed under this Non-Profit OSL 3.0, LICENSOR OFFERS NO WARRANTIES WHATSOEVER.
-
- (c) In the first sentence of Section 8 ["Limitation of Liability"] of this Non-Profit OSL 3.0, the list of damages for which LIABILITY IS LIMITED now includes "direct" damages.
-
- (d) The proviso in Section 1(c) of this License now refers to this "Non-Profit Open Software License" rather than the "Open Software License". You may distribute or communicate the Original Work or Derivative Works thereof under this Non-Profit OSL 3.0 license only if You make the representation and declaration in paragraph (a) of this Section 17. Otherwise, You shall distribute or communicate the Original Work or Derivative Works thereof only under the OSL 3.0 license and You shall publish clear licensing notices so stating. Also by way of clarification, this License does not authorize You to distribute or communicate works under this Non-Profit OSL 3.0 if You received them under the original OSL 3.0 license.
-
- (e) Original Works licensed under this license shall reference "Non-Profit OSL 3.0" in licensing notices to distinguish them from works licensed under the original OSL 3.0 license.
diff --git a/options/license/NRL b/options/license/NRL
deleted file mode 100644
index 5e104730c6..0000000000
--- a/options/license/NRL
+++ /dev/null
@@ -1,28 +0,0 @@
-NRL License
-
-COPYRIGHT NOTICE
-
-All of the documentation and software included in this software distribution from the US Naval Research Laboratory (NRL) are copyrighted by their respective developers.
-
-Portions of the software are derived from the Net/2 and 4.4-Lite Berkeley Software Distributions (BSD) of the University of California at Berkeley and those portions are copyright by The Regents of the University of California. All Rights Reserved. The UC Berkeley Copyright and License agreement is binding on those portions of the software. In all cases, the NRL developers have retained the original UC Berkeley copyright and license notices in the respective files in accordance with the UC Berkeley copyrights and license.
-
-Portions of this software and documentation were developed at NRL by various people. Those developers have each copyrighted the portions that they developed at NRL and have assigned All Rights for those portions to NRL. Outside the USA, NRL has copyright on some of the software developed at NRL. The affected files all contain specific copyright notices and those notices must be retained in any derived work.
-
-NRL LICENSE
-
-NRL grants permission for redistribution and use in source and binary forms, with or without modification, of the software and documentation created at NRL provided that the following conditions are met:
-
- 1. All terms of the UC Berkeley copyright and license must be followed.
- 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- 4. All advertising materials mentioning features or use of this software must display the following acknowledgements:
-
- This product includes software developed by the University of California, Berkeley and its contributors.
-
- This product includes software developed at the Information Technology Division, US Naval Research Laboratory.
-
- 5. Neither the name of the NRL nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the US Naval Research Laboratory (NRL).
diff --git a/options/license/NTP b/options/license/NTP
deleted file mode 100644
index 929a49509c..0000000000
--- a/options/license/NTP
+++ /dev/null
@@ -1,5 +0,0 @@
-NTP License (NTP)
-
-Copyright (c) (CopyrightHoldersName) (From 4-digit-year)-(To 4-digit-year)
-
-Permission to use, copy, modify, and distribute this software and its documentation for any purpose with or without fee is hereby granted, provided that the above copyright notice appears in all copies and that both the copyright notice and this permission notice appear in supporting documentation, and that the name (TrademarkedName) not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. (TrademarkedName) makes no representations about the suitability this software for any purpose. It is provided "as is" without express or implied warranty.
diff --git a/options/license/NTP-0 b/options/license/NTP-0
deleted file mode 100644
index 84a2bdf6e6..0000000000
--- a/options/license/NTP-0
+++ /dev/null
@@ -1,5 +0,0 @@
-NTP No Attribution (NTP-0)
-
-Copyright (4-digit-year) by (CopyrightHoldersName)
-
-Permission to use, copy, modify, and distribute this software and its documentation for any purpose is hereby granted, provided that the name of (TrademarkedName) not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. (TrademarkedName) make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.
diff --git a/options/license/Naumen b/options/license/Naumen
deleted file mode 100644
index 95411eecee..0000000000
--- a/options/license/Naumen
+++ /dev/null
@@ -1,21 +0,0 @@
-NAUMEN Public License
-
-This software is Copyright (c) NAUMEN (tm) and Contributors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions in source code must retain the above copyright notice, this list of conditions, and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. The name NAUMEN (tm) must not be used to endorse or promote products derived from this software without prior written permission from NAUMEN.
-
-4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of NAUMEN.
-
-5. If any files originating from NAUMEN or Contributors are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
-
-Disclaimer:
-
-THIS SOFTWARE IS PROVIDED BY NAUMEN "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NAUMEN OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-This software consists of contributions made by NAUMEN and Contributors. Specific attributions are listed in the accompanying credits file.
diff --git a/options/license/NetCDF b/options/license/NetCDF
deleted file mode 100644
index 1a54965287..0000000000
--- a/options/license/NetCDF
+++ /dev/null
@@ -1,7 +0,0 @@
-Copyright 1993-2014 University Corporation for Atmospheric Research/Unidata
-
-Portions of this software were developed by the Unidata Program at the University Corporation for Atmospheric Research.
-
-Access and use of this software shall impose the following obligations and understandings on the user. The user is granted the right, without any fee or cost, to use, copy, modify, alter, enhance and distribute this software, and any derivative works thereof, and its supporting documentation for any purpose whatsoever, provided that this entire notice appears in all copies of the software, derivative works and supporting documentation. Further, UCAR requests that the user credit UCAR/Unidata in any publications that result from the use of this software or in any product that includes this software, although this is not an obligation. The names UCAR and/or Unidata, however, may not be used in any advertising or publicity to endorse or promote any products or commercial entity unless specific written permission is obtained from UCAR/Unidata. The user also understands that UCAR/Unidata is not obligated to provide the user with any support, consulting, training or assistance of any kind with regard to the use, operation and performance of this software nor to provide the user with any updates, revisions, new versions or "bug fixes."
-
-THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/options/license/Newsletr b/options/license/Newsletr
deleted file mode 100644
index e91de7bd0b..0000000000
--- a/options/license/Newsletr
+++ /dev/null
@@ -1,7 +0,0 @@
-Copyright 1989--2004 by Hunter Goatley.
-
-Permission is granted to anyone to use this software for any purpose on any computer system, and to redistribute it freely, subject to the following restrictions:
-
-1. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-2. Altered versions must be plainly marked as such, and must not be misrepresented as being the original software.
diff --git a/options/license/Nokia b/options/license/Nokia
deleted file mode 100644
index ac0b78cd6f..0000000000
--- a/options/license/Nokia
+++ /dev/null
@@ -1,159 +0,0 @@
-Nokia Open Source License (NOKOS License)
-
-Version 1.0a
-
-1. DEFINITIONS.
-
-"Affiliates" of a party shall mean an entity
-
- a) which is directly or indirectly controlling such party;
-
- b) which is under the same direct or indirect ownership or control as such party; or
-
- c) which is directly or indirectly owned or controlled by such party.
-
- For these purposes, an entity shall be treated as being controlled by another if that other entity has fifty percent (50%) or more of the votes in such entity, is able to direct its affairs and/or to control the composition of its board of directors or equivalent body.
-
-"Commercial Use" shall mean distribution or otherwise making the Covered Software available to a third party.
-
-"Contributor" shall mean each entity that creates or contributes to the creation of Modifications.
-
-"Contributor Version" shall mean in case of any Contributor the combination of the Original Software, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor and in case of Nokia in addition the Original Software in any form, including the form as Exceutable.
-
-"Covered Software" shall mean the Original Software or Modifications or the combination of the Original Software and Modifications, in each case including portions thereof.
-
-"Electronic Distribution Mechanism" shall mean a mechanism generally accepted in the software development community for the electronic transfer of data.
-
-"Executable" shall mean Covered Software in any form other than Source Code.
-
-"Nokia" shall mean Nokia Corporation and its Affiliates.
-
-"Larger Work" shall mean a work, which combines Covered Software or portions thereof with code not governed by the terms of this License.
-
-"License" shall mean this document.
-
-"Licensable" shall mean having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
-"Modifications" shall mean any addition to or deletion from the substance or structure of either the Original Software or any previous Modifications. When Covered Software is released as a series of files, a Modification is:
-
- a) Any addition to or deletion from the contents of a file containing Original Software or previous Modifications.
-
- b) Any new file that contains any part of the Original Software or previous Modifications.
-
-"Original Software" shall mean the Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Software, and which, at the time of its release under this License is not already Covered Software governed by this License.
-
-"Patent Claims" shall mean any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
-"Source Code" shall mean the preferred form of the Covered Software for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Software or another well known, available Covered Software of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
-"You" (or "Your") shall mean an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes Affiliates of such entity.
-
-2. SOURCE CODE LICENSE.
-
- 2.1 Nokia Grant.
- Subject to the terms of this License, Nokia hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- a) under copyrights Licensable by Nokia to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof) with or without Modifications, and/or as part of a Larger Work;
-
- b) and under Patents Claims necessarily infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof).
-
- c) The licenses granted in this Section 2.1(a) and (b) are effective on the date Nokia first distributes Original Software under the terms of this License.
-
- d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Software; 2) separate from the Original Software; or 3) for infringements caused by: i) the modification of the Original Software or ii) the combination of the Original Software with other software or devices.
-
- 2.2 Contributor Grant.
- Subject to the terms of this License and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license
-
- a) under copyrights Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and
-
- b) under Patent Claims necessarily infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
-
- c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first makes Commercial Use of the Covered Software.
-
- d) Notwithstanding Section 2.2(b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor.
-
-3. DISTRIBUTION OBLIGATIONS.
-
- 3.1 Application of License.
- The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Software may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
- 3.2 Availability of Source Code.
- Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
- 3.3 Description of Modifications.
- You must cause all Covered Software to which You contribute to contain a file documenting the changes You made to create that Covered Software and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Software provided by Nokia and including the name of Nokia in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Software.
-
- 3.4 Intellectual Property Matters
-
- (a) Third Party Claims. If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Software that new knowledge has been obtained.
-
- (b) Contributor APIs. If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file.
-
- (c) Representations. Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.
-
- 3.5 Required Notices.
- You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Software. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of Nokia or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify Nokia and every Contributor for any liability incurred by Nokia or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
- 3.6 Distribution of Executable Versions.
- You may distribute Covered Software in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Software, and if You include a notice stating that the Source Code version of the Covered Software is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Software. You may distribute the Executable version of Covered Software or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by Nokia or any Contributor. You hereby agree to indemnify Nokia and every Contributor for any liability incurred by Nokia or such Contributor as a result of any such terms You offer.
-
- 3.7 Larger Works.
- You may create a Larger Work by combining Covered Software with other software not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software.
-
-4. INABILITY TO COMPLY DUE TO STATUTE OR REGULATION.
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code.
-
-Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. APPLICATION OF THIS LICENSE.
-This License applies to code to which Nokia has attached the notice in Exhibit A and to related Covered Software.
-
-6. VERSIONS OF THE LICENSE.
-
- 6.1 New Versions.
- Nokia may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
- 6.2 Effect of New Versions.
- Once Covered Software has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Software under the terms of any subsequent version of the License published by Nokia. No one other than Nokia has the right to modify the terms applicable to Covered Software created under this License.
-
-7. DISCLAIMER OF WARRANTY.
-COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT NOKIA, ITS LICENSORS OR AFFILIATES OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-
- 8.1 This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Software which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
- 8.2 If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Nokia or a Contributor (Nokia or Contributor against whom You file such action is referred to as "Participant") alleging that:
-
- a) such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.
-
- b) any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant.
-
- 8.3 If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
- 8.4 In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY.
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, NOKIA, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, BUT MAY ALLOW LIABILITY TO BE LIMITED; IN SUCH CASES, A PARTY's, ITS EMPLOYEES, LICENSORS OR AFFILIATES' LIABILITY SHALL BE LIMITED TO U.S. $50. Nothing contained in this License shall prejudice the statutory rights of any party dealing as a consumer.
-
-10. MISCELLANEOUS.
-This License represents the complete agreement concerning subject matter hereof. All rights in the Covered Software not expressly granted under this License are reserved. Nothing in this License shall grant You any rights to use any of the trademarks of Nokia or any of its Affiliates, even if any of such trademarks are included in any part of Covered Software and/or documentation to it.
-
-This License is governed by the laws of Finland excluding its conflict-of-law provisions. All disputes arising from or relating to this Agreement shall be settled by a single arbitrator appointed by the Central Chamber of Commerce of Finland. The arbitration procedure shall take place in Helsinki, Finland in the English language. If any part of this Agreement is found void and unenforceable, it will not affect the validity of the balance of the Agreement, which shall remain valid and enforceable according to its terms.
-
-11. RESPONSIBILITY FOR CLAIMS.
-As between Nokia and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Nokia and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-EXHIBIT A
-
-The contents of this file are subject to the NOKOS License Version 1.0 (the "License"); you may not use this file except in compliance with the License.
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-The Original Software is
-
-______________________________________.
-
-Copyright © <year> Nokia and others. All Rights Reserved.
-
-Contributor(s): ______________________________________.
diff --git a/options/license/Nokia-Qt-exception-1.1 b/options/license/Nokia-Qt-exception-1.1
deleted file mode 100644
index b4ec52b7a1..0000000000
--- a/options/license/Nokia-Qt-exception-1.1
+++ /dev/null
@@ -1,16 +0,0 @@
-Nokia Qt LGPL Exception version 1.1
-
-As an additional permission to the GNU Lesser General Public License version 2.1, the object code form of a "work that uses the Library" may incorporate material from a header file that is part of the Library. You may distribute such object code under terms of your choice, provided that:
-
- (i) the header files of the Library have not been modified; and
- (ii) the incorporated material is limited to numerical parameters, data structure layouts, accessors, macros, inline functions and templates; and
- (iii) you comply with the terms of Section 6 of the GNU Lesser General Public License version 2.1.
-
-Moreover, you may apply this exception to a modified version of the Library, provided that such modification does not involve copying material from the Library into the modified Library's header files unless such material is limited to
-
- (i) numerical parameters;
- (ii) data structure layouts;
- (iii) accessors; and
- (iv) small macros, templates and inline functions of five lines or less in length.
-
-Furthermore, you are not required to apply this additional permission to a modified version of the Library.
diff --git a/options/license/Noweb b/options/license/Noweb
deleted file mode 100644
index 8271989fb9..0000000000
--- a/options/license/Noweb
+++ /dev/null
@@ -1,9 +0,0 @@
-Noweb is copyright 1989-2000 by Norman Ramsey. All rights reserved.
-
-Noweb is protected by copyright. It is not public-domain software or shareware, and it is not protected by a ``copyleft'' agreement like the one used by the Free Software Foundation.
-
-Noweb is available free for any use in any field of endeavor. You may redistribute noweb in whole or in part provided you acknowledge its source and include this COPYRIGHT file. You may modify noweb and create derived works, provided you retain this copyright notice, but the result may not be called noweb without my written consent.
-
-You may sell noweb if you wish. For example, you may sell a CD-ROM including noweb.
-
-You may sell a derived work, provided that all source code for your derived work is available, at no additional charge, to anyone who buys your derived work in any form. You must give permisson for said source code to be used and modified under the terms of this license. You must state clearly that your work uses or is based on noweb and that noweb is available free of change. You must also request that bug reports on your work be reported to you.
diff --git a/options/license/O-UDA-1.0 b/options/license/O-UDA-1.0
deleted file mode 100644
index cf4b2bc9a1..0000000000
--- a/options/license/O-UDA-1.0
+++ /dev/null
@@ -1,47 +0,0 @@
-Open Use of Data Agreement v1.0
-
-This is the Open Use of Data Agreement, Version 1.0 (the "O-UDA"). Capitalized terms are defined in Section 5. Data Provider and you agree as follows:
-
-1. Provision of the Data
-
- 1.1. You may use, modify, and distribute the Data made available to you by the Data Provider under this O-UDA if you follow the O-UDA's terms.
-
- 1.2. Data Provider will not sue you or any Downstream Recipient for any claim arising out of the use, modification, or distribution of the Data provided you meet the terms of the O-UDA.
-
- 1.3 This O-UDA does not restrict your use, modification, or distribution of any portions of the Data that are in the public domain or that may be used, modified, or distributed under any other legal exception or limitation.
-
-2. No Restrictions on Use or Results
-
- 2.1. The O-UDA does not impose any restriction with respect to:
-
- 2.1.1. the use or modification of Data; or
-
- 2.1.2. the use, modification, or distribution of Results.
-
-3. Redistribution of Data
-
- 3.1. You may redistribute the Data under terms of your choice, so long as:
-
- 3.1.1. You include with any Data you redistribute all credit or attribution information that you received with the Data, and your terms require any Downstream Recipient to do the same; and
-
- 3.1.2. Your terms include a warranty disclaimer and limitation of liability for Upstream Data Providers at least as broad as those contained in Section 4.2 and 4.3 of the O-UDA.
-
-4. No Warranty, Limitation of Liability
-
- 4.1. Data Provider does not represent or warrant that it has any rights whatsoever in the Data.
-
- 4.2. THE DATA IS PROVIDED ON AN “AS IS†BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
-
- 4.3. NEITHER DATA PROVIDER NOR ANY UPSTREAM DATA PROVIDER SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE DATA OR RESULTS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-5. Definitions
-
- 5.1. "Data" means the material you receive under the O-UDA in modified or unmodified form, but not including Results.
-
- 5.2. "Data Provider" means the source from which you receive the Data and with whom you enter into the O-UDA.
-
- 5.3. "Downstream Recipient" means any person or persons who receives the Data directly or indirectly from you in accordance with the O-UDA.
-
- 5.4. "Result" means anything that you develop or improve from your use of Data that does not include more than a de minimis portion of the Data on which the use is based. Results may include de minimis portions of the Data necessary to report on or explain use that has been conducted with the Data, such as figures in scientific papers, but do not include more. Artificial intelligence models trained on Data (and which do not include more than a de minimis portion of Data) are Results.
-
- 5.5. "Upstream Data Providers" means the source or sources from which the Data Provider directly or indirectly received, under the terms of the O-UDA, material that is included in the Data.
diff --git a/options/license/OAR b/options/license/OAR
deleted file mode 100644
index ca5c4b9617..0000000000
--- a/options/license/OAR
+++ /dev/null
@@ -1,12 +0,0 @@
-COPYRIGHT (c) 1989-2013, 2015.
-On-Line Applications Research Corporation (OAR).
-
-Permission to use, copy, modify, and distribute this software for any
-purpose without fee is hereby granted, provided that this entire notice
-is included in all copies of any software which is or includes a copy
-or modification of this software.
-
-THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
-WARRANTY. IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION
-OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS
-SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
diff --git a/options/license/OCCT-PL b/options/license/OCCT-PL
deleted file mode 100644
index 9b6fccc1c9..0000000000
--- a/options/license/OCCT-PL
+++ /dev/null
@@ -1,112 +0,0 @@
-Open CASCADE Technology Public License
-Version 6.6, April 2013
-
-OPEN CASCADE releases and makes publicly available the source code of the software Open CASCADE Technology to the free software development community under the terms and conditions of this license.
-
-It is not the purpose of this license to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this license has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
-
-Please read this license carefully and completely before downloading this software. By downloading, using, modifying, distributing and sublicensing this software, you indicate your acceptance to be bound by the terms and conditions of this license. If you do not want to accept or cannot accept for any reasons the terms and conditions of this license, please do not download or use in any manner this software.
-
-1. Definitions
-
-Unless there is something in the subject matter or in the context inconsistent therewith, the capitalized terms used in this License shall have the following meaning.
-
-"Applicable Intellectual Property Rights" means (a) with respect to the Initial Developer, any rights under patents or patents applications or other intellectual property rights that are now or hereafter acquired, owned by or assigned to the Initial Developer and that cover subject matter contained in the Original Code, but only to the extent necessary to use, reproduce, modify, distribute or sublicense the Original Code without infringement; and (b) with respect to You or any Contributor, any rights under patents or patents applications or other intellectual property rights that are now or hereafter acquired, owned by or assigned to You or to such Contributor and that cover subject matter contained in Your Modifications or in such Contributor's Modifications, taken alone or in combination with Original Code.
-
-"Contributor" means each individual or legal entity that creates or contributes to the creation of any Modification, including the Initial Developer.
-
-"Derivative Program": means a new program combining the Software or portions thereof with other source code not governed by the terms of this License.
-
-"Initial Developer": means OPEN CASCADE, with main offices at 1, place des Frères Montgolfier, 78280, Guyancourt, France.
-
-"Modifications": mean any addition to, deletion from or change to the substance or the structure of the Software. When source code of the Software is released as a series of files, a Modification is: (a) any addition to, deletion from or change to the contents of a file containing the Software or (b) any new file or other representation of computer program statements that contains any part of the Software. By way of example, Modifications include any debug of, or improvement to, the Original Code or any of its components or portions as well as its next versions or releases thereof.
-
-"Original Code": means (a) the source code of the software Open CASCADE Technology originally made available by the Initial Developer under this License, including the source code of any updates or upgrades of the Original Code and (b) the object code compiled from such source code and originally made available by Initial Developer under this License.
-
-"Software": means the Original Code, the Modifications, the combination of Original Code and any Modifications or any respective portions thereof.
-
-"You" or "Your": means an individual or a legal entity exercising rights under this License
-
-2. Acceptance of license
-By using, reproducing, modifying, distributing or sublicensing the Software or any portion thereof, You expressly indicate Your acceptance of the terms and conditions of this License and undertake to act in accordance with all the provisions of this License applicable to You.
-
-3. Scope and purpose
-This License applies to the Software and You may not use, reproduce, modify, distribute, sublicense or circulate the Software, or any portion thereof, except as expressly provided under this License. Any attempt to otherwise use, reproduce, modify, distribute or sublicense the Software is void and will automatically terminate Your rights under this License.
-
-4. Contributor license
-Subject to the terms and conditions of this License, the Initial Developer and each of the Contributors hereby grant You a world-wide, royalty-free, irrevocable and non-exclusive license under the Applicable Intellectual Property Rights they own or control, to use, reproduce, modify, distribute and sublicense the Software provided that:
-
- You reproduce in all copies of the Software the copyright and other proprietary notices and disclaimers of the Initial Developer as they appear in the Original Code and attached hereto as Schedule "A" and any other notices or disclaimers attached to the Software and keep intact all notices in the Original Code that refer to this License and to the absence of any warranty;
-
- You include a copy of this License with every copy of the Software You distribute;
-
- If you distribute or sublicense the Software (as modified by You or on Your behalf as the case may be), You cause such Software to be licensed as a whole, at no charge, to all third parties, under the terms and conditions of the License, making in particular available to all third parties the source code of the Software;
-
- You document all Your Modifications, indicate the date of each such Modification, designate the version of the Software You used, prominently include a file carrying such information with respect to the Modifications and duplicate the copyright and other proprietary notices and disclaimers attached hereto as Schedule "B" or any other notices or disclaimers attached to the Software with your Modifications.
-
-For greater certainty, it is expressly understood that You may freely create Derivative Programs (without any obligation to publish such Derivative Program) and distribute same as a single product. In such case, You must ensure that all the requirements of this License are fulfilled for the Software or any portion thereof.
-
-5. Your license
-You hereby grant all Contributors and anyone who becomes a party under this License a world-wide, non-exclusive, royalty-free and irrevocable license under the Applicable Intellectual Property Rights owned or controlled by You, to use, reproduce, modify, distribute and sublicense all Your Modifications under the terms and conditions of this License.
-
-6. Software subject to license
-Your Modifications shall be governed by the terms and conditions of this License. You are not authorized to impose any other terms or conditions than those prevailing under this License when You distribute and/or sublicense the Software, save and except as permitted under Section 7 hereof.
-
-7. Additional terms
-You may choose to offer, on a non-exclusive basis, and to charge a fee for any warranty, support, maintenance, liability obligations or other rights consistent with the scope of this License with respect to the Software (the "Additional Terms") to the recipients of the Software. However, You may do so only on Your own behalf and on Your sole and exclusive responsibility. You must obtain the recipient's agreement that any such Additional Terms are offered by You alone, and You hereby agree to indemnify, defend and hold the Initial Developer and any Contributor harmless for any liability incurred by or claims asserted against the Initial Developer or any Contributors with respect to any such Additional Terms.
-
-8. Disclaimer of warranty
-The Software is provided under this License on an "as is" basis, without warranty of any kind, including without limitation, warranties that the Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Software is with You.
-
-9. Liability
-Under no circumstances shall You, the Initial Developer or any Contributor be liable to any person for any direct or indirect damages of any kind including, without limitation, damages for loss of goodwill, loss of data, work stoppage, computer failure or malfunction or any and all other commercial damages or losses resulting from or relating to this License or indirectly to the use of the Software.
-
-10. Trademark
-This License does not grant any rights to use the trademarks, trade names and domain names "MATRA", "EADS Matra Datavision", "CAS.CADE", "Open CASCADE", "opencascade.com" and "opencascade.org" or any other trademarks, trade names or domain names used or owned by the Initial Developer.
-
-11. Copyright
-The Initial Developer retains all rights, title and interest in and to the Original Code. You may not remove the copyright © notice which appears when You download the Software.
-
-12. Term
-This License is granted to You for a term equal to the remaining period of protection covered by the intellectual property rights applicable to the Original Code.
-
-13. Termination
-In case of termination, as provided in Section 3 above, You agree to immediately stop any further use, reproduction, modification, distribution and sublicensing of the Software and to destroy all copies of the Software that are in Your possession or control. All sublicenses of the Software which have been properly granted prior to termination shall survive any termination of this License. In addition, Sections 5, 8 to 11, 13.2 and 15.2 of this License, in reason of their nature, shall survive the termination of this License for a period of fifteen (15) years.
-
-14. Versions of the license
-The Initial Developer may publish new versions of this License from time to time. Once Original Code has been published under a particular version of this License, You may choose to continue to use it under the terms and conditions of that version or use the Original Code under the terms of any subsequent version of this License published by the Initial Developer.
-
-15. Miscellaneous
- 15.1 Relationship of the Parties This License will not be construed as creating an agency, partnership, joint venture or any other form of legal association between You and the Initial Developer, and You will not represent to the contrary, whether expressly, by implication or otherwise.
-
- 15.2 Independent Development Nothing in this License will impair the Initial Developer's right to acquire, license, develop, have others develop for it, market or distribute technology or products that perform the same or similar functions as, or otherwise compete with, Modifications, Derivative Programs, technology or products that You may develop, produce, market or distribute.
-
- 15.3 Severability If for any reason a court of competent jurisdiction finds any provision of this License, or portion thereof, to be unenforceable, that provision of the License will be enforced to the maximum extent permissible so as to effect the economic benefits and intent of the parties, and the remainder of this License will continue in full force and extent.
-
-END OF THE TERMS AND CONDITIONS OF THIS LICENSE
-
-OPEN CASCADE is a French société par actions simplifiée having its registered head office at 1, place des Frères Montgolfier, 78280, Guyancourt, France and main offices at 1, place des Frères Montgolfier, 78280, Guyancourt, France. Its web site is located at the following address opencascade.com
-
-Open CASCADE Technology Public License
-Schedule "A"
-
- The content of this file is subject to the Open CASCADE Technology Public License (the "License"). You may not use the content of this file except in compliance with the License. Please obtain a copy of the License at opencascade.com and read it completely before using this file.
-
- The Initial Developer of the Original Code is OPEN CASCADE, with main offices at 1, place des Frères Montgolfier, 78280, Guyancourt, France. The Original Code is copyright © OPEN CASCADE SAS, 2001. All rights reserved. "The Original Code and all software distributed under the License are distributed on an "AS IS" basis, without warranty of any kind, and the Initial Developer hereby disclaims all such warranties, including without limitation, any warranties of merchantability, fitness for a particular purpose or non-infringement.
-
- Please see the License for the specific terms and conditions governing rights and limitations under the License".
- End of Schedule "A"
-
-Open CASCADE Technology Public License
-Schedule "B"
-
- "The content of this file is subject to the Open CASCADE Technology Public License (the "License"). You may not use the content of this file except in compliance with the License. Please obtain a copy of the License at opencascade.com and read it completely before using this file.
-
- The Initial Developer of the Original Code is OPEN CASCADE, with main offices at 1, place des Frères Montgolfier, 78280, Guyancourt, France. The Original Code is copyright © Open CASCADE SAS, 2001. All rights reserved.
-
- Modifications to the Original Code have been made by ________________________. Modifications are copyright © [Year to be included]. All rights reserved.
-
- The software Open CASCADE Technology and all software distributed under the License are distributed on an "AS IS" basis, without warranty of any kind, and the Initial Developer hereby disclaims all such warranties, including without limitation, any warranties of merchantability, fitness for a particular purpose or non-infringement.
-
- Please see the License for the specific terms and conditions governing rights and limitations under the License"
- End of Schedule "B"
diff --git a/options/license/OCCT-exception-1.0 b/options/license/OCCT-exception-1.0
deleted file mode 100644
index d41c35bff5..0000000000
--- a/options/license/OCCT-exception-1.0
+++ /dev/null
@@ -1,3 +0,0 @@
-Open CASCADE Exception (version 1.0) to GNU LGPL version 2.1.
-
-The object code (i.e. not a source) form of a "work that uses the Library" can incorporate material from a header file that is part of the Library. As a special exception to the GNU Lesser General Public License version 2.1, you may distribute such object code incorporating material from header files provided with the Open CASCADE Technology libraries (including code of CDL generic classes) under terms of your choice, provided that you give prominent notice in supporting documentation to this code that it makes use of or is based on facilities provided by the Open CASCADE Technology software.
diff --git a/options/license/OCLC-2.0 b/options/license/OCLC-2.0
deleted file mode 100644
index ffb2f7f0b5..0000000000
--- a/options/license/OCLC-2.0
+++ /dev/null
@@ -1,76 +0,0 @@
-OCLC Research Public License 2.0
-Terms & Conditions Of Use
-May, 2002
-Copyright © 2002. OCLC Online Computer Library Center, Inc. All Rights Reserved
-
-PLEASE READ THIS DOCUMENT CAREFULLY. BY DOWNLOADING OR USING THE CODE BASE AND/OR DOCUMENTATION ACCOMPANYING THIS LICENSE (THE "License"), YOU AGREE TO THE FOLLOWING TERMS AND CONDITIONS OF THIS LICENSE.
-
-Section 1. Your Rights
-
-Subject to these terms and conditions of this License, the OCLC Office of Research (the "Original Contributor") and each subsequent contributor (collectively with the Original Contributor, the "Contributors") hereby grant you a non-exclusive, worldwide, no-charge, transferable license to execute, prepare derivative works of, and distribute (internally and externally), for commercial and noncommercial purposes, the original code contributed by Original Contributor and all Modifications (collectively called the "Program").
-
-Section 2. Definitions
-
-A "Modification" to the Program is any addition to or deletion from the contents of any file of the Program and any new file that contains any part of the Program. If you make a Modification and distribute the Program externally you are a "Contributor." The distribution of the Program must be under the terms of this license including those in Section 3 below.
-
-A "Combined Work" results from combining and integrating all or parts of the Program with other code. A Combined Work may be thought of as having multiple parents or being result of multiple lines of code development.
-
-Section 3. Distribution Licensing Terms
-
-A. General Requirements
-Except as necessary to recognize third-party rights or third-party restriction (see below), a distribution of the Program in any of the forms listed below must not put any further restrictions on the recipient’s exercise of the rights granted herein.
-
-As a Contributor, you represent that your Modification(s) are your original creation(s) and, to the best of your knowledge, no third party has any claim (including but not limited to intellectual property claims) relating to your Modification(s). You represent that each of your Modifications includes complete details of any third-party right or other third-party restriction associated with any part of your Modification (including a copy of any applicable license agreement).
-
-The Program must be distributed without charge beyond the costs of physically transferring the files to the recipient.
-
-This Warranty Disclaimer/Limitation of Liability must be prominently displayed with every distribution of the Program in any form:
-
-YOU AGREE THAT THE PROGRAM IS PROVIDED AS-IS, WITHOUT WARRANTY OF ANY KIND (EITHER EXPRESS OR IMPLIED). ACCORDINGLY, OCLC MAKES NO WARRANTIES, REPRESENTATIONS OR GUARANTEES, EITHER EXPRESS OR IMPLIED, AND DISCLAIMS ALL SUCH WARRANTIES, REPRESENTATIONS OR GUARANTEES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE, AS TO: (A) THE FUNCTIONALITY OR NONINFRINGEMENT OF PROGRAM, ANY MODIFICATION, A COMBINED WORK OR AN AGGREGATE WORK; OR (B) THE RESULTS OF ANY PROJECT UNDERTAKEN USING THE PROGRAM, ANY MODIFICATION, A COMBINED WORK OR AN AGGREGATE WORK. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, CONSEQUENTIAL OR ANY OTHER DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE PROGRAM, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. YOU HEREBY WAIVE ANY CLAIMS FOR DAMAGES OF ANY KIND AGAINST CONTRIBUTORS WHICH MAY RESULT FROM YOUR USE OF THE PROGRAM.
-
-B. Requirements for a Distribution of Modifiable Code
-If you distribute the Program in a form to which the recipient can make Modifications (e.g. source code), the terms of this license apply to use by recipient. In addition, each source and data file of the Program and any Modification you distribute must contain the following notice:
-
- "Copyright (c) 2000- (insert then current year) OCLC Online Computer Library Center, Inc. and other contributors. All rights reserved. The contents of this file, as updated from time to time by the OCLC Office of Research, are subject to OCLC Research Public License Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a current copy of the License at http://purl.oclc.org/oclc/research/ORPL/. Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. This software consists of voluntary contributions made by many individuals on behalf of OCLC Research. For more information on OCLC Research, please see http://www.oclc.org/research/. The Original Code is ______________________________. The Initial Developer of the Original Code is ________________________. Portions created by ______________________ are Copyright (C) _____ _______________________. All Rights Reserved. Contributor(s): ______________________________________."
-
-C. Requirements for a Distribution of Non-modifiable Code
-If you distribute the Program in a form to which the recipient cannot make Modifications (e.g. object code), the terms of this license apply to use by recipient and you must include the following statement in appropriate and conspicuous locations:
-
-"Copyright (c) 2000- (insert then current year) OCLC Online Computer Library Center, Inc. and other contributors. All rights reserved."
-
-In addition, the source code must be included with the object code distribution or the distributor must provide the source code to the recipient upon request.
-
-D. Requirements for a Combined Work Distribution
-Distributions of Combined Works are subject to the terms of this license and must be made at no charge to the recipient beyond the costs of physically transferring the files to recipient.
-
-A Combined Work may be distributed as either modifiable or non-modifiable code. The requirements of Section 3.B or 3.C above (as appropriate) apply to such distributions.
-
-An "Aggregate Work" is when the Program exists, without integration, with other programs on a storage medium. This License does not apply to portions of an Aggregate Work which are not covered by the definition of "Program" provided in this License. You are not forbidden from selling an Aggregate Work. However, the Program contained in an Aggregate Work is subject to this License. Also, should the Program be extracted from an Aggregate Work, this License applies to any use of the Program apart from the Aggregate Work.
-
-Section 4. License Grant
-
-For purposes of permitting use of your Modifications by OCLC and other licensees hereunder, you hereby grant to OCLC and such other licensees the non-exclusive, worldwide, royalty-free, transferable, sublicenseable license to execute, copy, alter, delete, modify, adapt, change, revise, enhance, develop, publicly display, distribute (internally and externally) and/or create derivative works based on your Modifications (and derivative works thereof) in accordance with these Terms. This Section 4 shall survive termination of this License for any reason.
-
-Section 5. Termination of Rights
-
-This non-exclusive license (with respect to the grant from a particular Contributor) automatically terminates for any entity that initiates legal action for intellectual property infringement (with respect to the Program) against such Contributor as of the initiation of such action.
-
-If you fail to comply with this License, your rights (but not your obligations) under this License shall terminate automatically unless you cure such breach within thirty (30) days of becoming aware of the noncompliance. All sublicenses granted by you which preexist such termination and are properly granted shall survive such termination.
-
-Section 6. Other Terms
-
-Except for the copyright notices required above, you may not use any trademark of any of the Contributors without the prior written consent of the relevant Contributor. You agree not to remove, alter or obscure any copyright or other proprietary rights notice contained in the Program.
-
-All transfers of the Program or any part thereof shall be made in compliance with U.S. import/export regulations or other restrictions of the U.S. Department of Commerce, as well as other similar trade or commerce restrictions which might apply.
-
-Any patent obtained by any party covering the Program or any part thereof must include a provision providing for the free, perpetual and unrestricted commercial and noncommercial use by any third party.
-
-If, as a consequence of a court judgment or settlement relating to intellectual property infringement or any other cause of action, conditions are imposed on you that contradict the conditions of this License, such conditions do not excuse you from compliance with this License. If you cannot distribute the Program so as to simultaneously satisfy your obligations under this License and such other conditions, you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, you could not satisfy both the patent license and this License, and you would be required to refrain entirely from distribution of the Program.
-
-If you learn of a third party claim or other restriction relating to a Program you have already distributed you shall promptly redo your Program to address the issue and take all reasonable steps to inform those who may have received the Program at issue. An example of an appropriate reasonable step to inform would be posting an announcement on an appropriate web bulletin board.
-
-The provisions of this License are deemed to be severable, and the invalidity or unenforceability of any provision shall not affect or impair the remaining provisions which shall continue in full force and effect. In substitution for any provision held unlawful, there shall be substituted a provision of similar import reflecting the original intent of the parties hereto to the extent permissible under law.
-
-The Original Contributor from time to time may change this License, and the amended license will apply to all copies of the Program downloaded after the new license is posted. This License grants only the rights expressly stated herein and provides you with no implied rights or licenses to the intellectual property of any Contributor.
-
-This License is the complete and exclusive statement of the agreement between the parties concerning the subject matter hereof and may not be amended except by the written agreement of the parties. This License shall be governed by and construed in accordance with the laws of the State of Ohio and the United States of America, without regard to principles of conflicts of law.
diff --git a/options/license/OCaml-LGPL-linking-exception b/options/license/OCaml-LGPL-linking-exception
deleted file mode 100644
index 7fc88d7307..0000000000
--- a/options/license/OCaml-LGPL-linking-exception
+++ /dev/null
@@ -1 +0,0 @@
-As a special exception to the GNU Lesser General Public License, you may link, statically or dynamically, a "work that uses the OCaml Core System" with a publicly distributed version of the OCaml Core System to produce an executable file containing portions of the OCaml Core System, and distribute that executable file under terms of your choice, without any of the additional requirements listed in clause 6 of the GNU Lesser General Public License. By "a publicly distributed version of the OCaml Core System", we mean either the unmodified OCaml Core System as distributed by INRIA, or a modified version of the OCaml Core System that is distributed under the conditions defined in clause 2 of the GNU Lesser General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Lesser General Public License.
diff --git a/options/license/ODC-By-1.0 b/options/license/ODC-By-1.0
deleted file mode 100644
index 15115ca9ca..0000000000
--- a/options/license/ODC-By-1.0
+++ /dev/null
@@ -1,195 +0,0 @@
-# ODC Attribution License (ODC-By)
-
-### Preamble
-
-The Open Data Commons Attribution License is a license agreement intended to allow users to freely share, modify, and use this Database subject only to the attribution requirements set out in Section 4.
-
-Databases can contain a wide variety of types of content (images, audiovisual material, and sounds all in the same database, for example), and so this license only governs the rights over the Database, and not the contents of the Database individually. Licensors may therefore wish to use this license together with another license for the contents.
-
-Sometimes the contents of a database, or the database itself, can be covered by other rights not addressed here (such as private contracts, trademark over the name, or privacy rights / data protection rights over information in the contents), and so you are advised that you may have to consult other documents or clear other rights before doing activities not covered by this License.
-
-------
-
-The Licensor (as defined below)
-
-and
-
-You (as defined below)
-
-agree as follows:
-
-### 1.0 Definitions of Capitalised Words
-
-"Collective Database" – Means this Database in unmodified form as part of a collection of independent databases in themselves that together are assembled into a collective whole. A work that constitutes a Collective Database will not be considered a Derivative Database.
-
-"Convey" – As a verb, means Using the Database, a Derivative Database, or the Database as part of a Collective Database in any way that enables a Person to make or receive copies of the Database or a Derivative Database. Conveying does not include interaction with a user through a computer network, or creating and Using a Produced Work, where no transfer of a copy of the Database or a Derivative Database occurs.
-
-"Contents" – The contents of this Database, which includes the information, independent works, or other material collected into the Database. For example, the contents of the Database could be factual data or works such as images, audiovisual material, text, or sounds.
-
-"Database" – A collection of material (the Contents) arranged in a systematic or methodical way and individually accessible by electronic or other means offered under the terms of this License.
-
-"Database Directive" – Means Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended or succeeded.
-
-"Database Right" – Means rights resulting from the Chapter III ("sui generis") rights in the Database Directive (as amended and as transposed by member states), which includes the Extraction and Re-utilisation of the whole or a Substantial part of the Contents, as well as any similar rights available in the relevant jurisdiction under Section 10.4.
-
-"Derivative Database" – Means a database based upon the Database, and includes any translation, adaptation, arrangement, modification, or any other alteration of the Database or of a Substantial part of the Contents. This includes, but is not limited to, Extracting or Re-utilising the whole or a Substantial part of the Contents in a new Database.
-
-"Extraction" – Means the permanent or temporary transfer of all or a Substantial part of the Contents to another medium by any means or in any form.
-
-"License" – Means this license agreement and is both a license of rights such as copyright and Database Rights and an agreement in contract.
-
-"Licensor" – Means the Person that offers the Database under the terms of this License.
-
-"Person" – Means a natural or legal person or a body of persons corporate or incorporate.
-
-"Produced Work" – a work (such as an image, audiovisual material, text, or sounds) resulting from using the whole or a Substantial part of the Contents (via a search or other query) from this Database, a Derivative Database, or this Database as part of a Collective Database.
-
-"Publicly" – means to Persons other than You or under Your control by either more than 50% ownership or by the power to direct their activities (such as contracting with an independent consultant).
-
-"Re-utilisation" – means any form of making available to the public all or a Substantial part of the Contents by the distribution of copies, by renting, by online or other forms of transmission.
-
-"Substantial" – Means substantial in terms of quantity or quality or a combination of both. The repeated and systematic Extraction or Re-utilisation of insubstantial parts of the Contents may amount to the Extraction or Re-utilisation of a Substantial part of the Contents.
-
-"Use" – As a verb, means doing any act that is restricted by copyright or Database Rights whether in the original medium or any other; and includes without limitation distributing, copying, publicly performing, publicly displaying, and preparing derivative works of the Database, as well as modifying the Database as may be technically necessary to use it in a different mode or format.
-
-"You" – Means a Person exercising rights under this License who has not previously violated the terms of this License with respect to the Database, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
-Words in the singular include the plural and vice versa.
-
-### 2.0 What this License covers
-
-2.1. Legal effect of this document. This License is:
-
- a. A license of applicable copyright and neighbouring rights;
-
- b. A license of the Database Right; and
-
- c. An agreement in contract between You and the Licensor.
-
-2.2 Legal rights covered. This License covers the legal rights in the Database, including:
-
- a. Copyright. Any copyright or neighbouring rights in the Database. The copyright licensed includes any individual elements of the Database, but does not cover the copyright over the Contents independent of this Database. See Section 2.4 for details. Copyright law varies between jurisdictions, but is likely to cover: the Database model or schema, which is the structure, arrangement, and organisation of the Database, and can also include the Database tables and table indexes; the data entry and output sheets; and the Field names of Contents stored in the Database;
-
- b. Database Rights. Database Rights only extend to the Extraction and Re-utilisation of the whole or a Substantial part of the Contents. Database Rights can apply even when there is no copyright over the Database. Database Rights can also apply when the Contents are removed from the Database and are selected and arranged in a way that would not infringe any applicable copyright; and
-
- c. Contract. This is an agreement between You and the Licensor for access to the Database. In return you agree to certain conditions of use on this access as outlined in this License.
-
-2.3 Rights not covered.
-
- a. This License does not apply to computer programs used in the making or operation of the Database;
-
- b. This License does not cover any patents over the Contents or the Database; and
-
- c. This License does not cover any trademarks associated with the Database.
-
-2.4 Relationship to Contents in the Database. The individual items of the Contents contained in this Database may be covered by other rights, including copyright, patent, data protection, privacy, or personality rights, and this License does not cover any rights (other than Database Rights or in contract) in individual Contents contained in the Database. For example, if used on a Database of images (the Contents), this License would not apply to copyright over individual images, which could have their own separate licenses, or one single license covering all of the rights over the images.
-
-### 3.0 Rights granted
-
-3.1 Subject to the terms and conditions of this License, the Licensor grants to You a worldwide, royalty-free, non-exclusive, terminable (but only under Section 9) license to Use the Database for the duration of any applicable copyright and Database Rights. These rights explicitly include commercial use, and do not exclude any field of endeavour. To the extent possible in the relevant jurisdiction, these rights may be exercised in all media and formats whether now known or created in the future.
-
-The rights granted cover, for example:
-
- a. Extraction and Re-utilisation of the whole or a Substantial part of the Contents;
-
- b. Creation of Derivative Databases;
-
- c. Creation of Collective Databases;
-
- d. Creation of temporary or permanent reproductions by any means and in any form, in whole or in part, including of any Derivative Databases or as a part of Collective Databases; and
-
- e. Distribution, communication, display, lending, making available, or performance to the public by any means and in any form, in whole or in part, including of any Derivative Database or as a part of Collective Databases.
-
-3.2 Compulsory license schemes. For the avoidance of doubt:
-
- a. Non-waivable compulsory license schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-
- b. Waivable compulsory license schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
-
- c. Voluntary license schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
-
-3.3 The right to release the Database under different terms, or to stop distributing or making available the Database, is reserved. Note that this Database may be multiple-licensed, and so You may have the choice of using alternative licenses for this Database. Subject to Section 10.4, all other rights not expressly granted by Licensor are reserved.
-
-### 4.0 Conditions of Use
-
-4.1 The rights granted in Section 3 above are expressly made subject to Your complying with the following conditions of use. These are important conditions of this License, and if You fail to follow them, You will be in material breach of its terms.
-
-4.2 Notices. If You Publicly Convey this Database, any Derivative Database, or the Database as part of a Collective Database, then You must:
-
- a. Do so only under the terms of this License;
-
- b. Include a copy of this License or its Uniform Resource Identifier (URI) with the Database or Derivative Database, including both in the Database or Derivative Database and in any relevant documentation;
-
- c. Keep intact any copyright or Database Right notices and notices that refer to this License; and
-
- d. If it is not possible to put the required notices in a particular file due to its structure, then You must include the notices in a location (such as a relevant directory) where users would be likely to look for it.
-
-4.3 Notice for using output (Contents). Creating and Using a Produced Work does not require the notice in Section 4.2. However, if you Publicly Use a Produced Work, You must include a notice associated with the Produced Work reasonably calculated to make any Person that uses, views, accesses, interacts with, or is otherwise exposed to the Produced Work aware that Content was obtained from the Database, Derivative Database, or the Database as part of a Collective Database, and that it is available under this License.
-
- a. Example notice. The following text will satisfy notice under Section 4.3:
-
- Contains information from DATABASE NAME which is made available under the ODC Attribution License.
-
-DATABASE NAME should be replaced with the name of the Database and a hyperlink to the location of the Database. "ODC Attribution License" should contain a hyperlink to the URI of the text of this License. If hyperlinks are not possible, You should include the plain text of the required URI's with the above notice.
-
-4.4 Licensing of others. You may not sublicense the Database. Each time You communicate the Database, the whole or Substantial part of the Contents, or any Derivative Database to anyone else in any way, the Licensor offers to the recipient a license to the Database on the same terms and conditions as this License. You are not responsible for enforcing compliance by third parties with this License, but You may enforce any rights that You have over a Derivative Database. You are solely responsible for any modifications of a Derivative Database made by You or another Person at Your direction. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License.
-
-### 5.0 Moral rights
-
-5.1 Moral rights. This section covers moral rights, including any rights to be identified as the author of the Database or to object to treatment that would otherwise prejudice the author's honour and reputation, or any other derogatory treatment:
-
- a. For jurisdictions allowing waiver of moral rights, Licensor waives all moral rights that Licensor may have in the Database to the fullest extent possible by the law of the relevant jurisdiction under Section 10.4;
-
- b. If waiver of moral rights under Section 5.1 a in the relevant jurisdiction is not possible, Licensor agrees not to assert any moral rights over the Database and waives all claims in moral rights to the fullest extent possible by the law of the relevant jurisdiction under Section 10.4; and
-
- c. For jurisdictions not allowing waiver or an agreement not to assert moral rights under Section 5.1 a and b, the author may retain their moral rights over certain aspects of the Database.
-
-Please note that some jurisdictions do not allow for the waiver of moral rights, and so moral rights may still subsist over the Database in some jurisdictions.
-
-### 6.0 Fair dealing, Database exceptions, and other rights not affected
-
-6.1 This License does not affect any rights that You or anyone else may independently have under any applicable law to make any use of this Database, including without limitation:
-
- a. Exceptions to the Database Right including: Extraction of Contents from non-electronic Databases for private purposes, Extraction for purposes of illustration for teaching or scientific research, and Extraction or Re-utilisation for public security or an administrative or judicial procedure.
-
- b. Fair dealing, fair use, or any other legally recognised limitation or exception to infringement of copyright or other applicable laws.
-
-6.2 This License does not affect any rights of lawful users to Extract and Re-utilise insubstantial parts of the Contents, evaluated quantitatively or qualitatively, for any purposes whatsoever, including creating a Derivative Database (subject to other rights over the Contents, see Section 2.4). The repeated and systematic Extraction or Re-utilisation of insubstantial parts of the Contents may however amount to the Extraction or Re-utilisation of a Substantial part of the Contents.
-
-### 7.0 Warranties and Disclaimer
-
-7.1 The Database is licensed by the Licensor "as is" and without any warranty of any kind, either express, implied, or arising by statute, custom, course of dealing, or trade usage. Licensor specifically disclaims any and all implied warranties or conditions of title, non-infringement, accuracy or completeness, the presence or absence of errors, fitness for a particular purpose, merchantability, or otherwise. Some jurisdictions do not allow the exclusion of implied warranties, so this exclusion may not apply to You.
-
-### 8.0 Limitation of liability
-
-8.1 Subject to any liability that may not be excluded or limited by law, the Licensor is not liable for, and expressly excludes, all liability for loss or damage however and whenever caused to anyone by any use under this License, whether by You or by anyone else, and whether caused by any fault on the part of the Licensor or not. This exclusion of liability includes, but is not limited to, any special, incidental, consequential, punitive, or exemplary damages such as loss of revenue, data, anticipated profits, and lost business. This exclusion applies even if the Licensor has been advised of the possibility of such damages.
-
-8.2 If liability may not be excluded by law, it is limited to actual and direct financial loss to the extent it is caused by proved negligence on the part of the Licensor.
-
-### 9.0 Termination of Your rights under this License
-
-9.1 Any breach by You of the terms and conditions of this License automatically terminates this License with immediate effect and without notice to You. For the avoidance of doubt, Persons who have received the Database, the whole or a Substantial part of the Contents, Derivative Databases, or the Database as part of a Collective Database from You under this License will not have their licenses terminated provided their use is in full compliance with this License or a license granted under Section 4.8 of this License. Sections 1, 2, 7, 8, 9 and 10 will survive any termination of this License.
-
-9.2 If You are not in breach of the terms of this License, the Licensor will not terminate Your rights under it.
-
-9.3 Unless terminated under Section 9.1, this License is granted to You for the duration of applicable rights in the Database.
-
-9.4 Reinstatement of rights. If you cease any breach of the terms and conditions of this License, then your full rights under this License will be reinstated:
-
- a. Provisionally and subject to permanent termination until the 60th day after cessation of breach;
-
- b. Permanently on the 60th day after cessation of breach unless otherwise reasonably notified by the Licensor; or
-
- c. Permanently if reasonably notified by the Licensor of the violation, this is the first time You have received notice of violation of this License from the Licensor, and You cure the violation prior to 30 days after your receipt of the notice.
-
-9.5 Notwithstanding the above, Licensor reserves the right to release the Database under different license terms or to stop distributing or making available the Database. Releasing the Database under different license terms or stopping the distribution of the Database will not withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-### 10.0 General
-
-10.1 If any provision of this License is held to be invalid or unenforceable, that must not affect the validity or enforceability of the remainder of the terms and conditions of this License and each remaining provision of this License shall be valid and enforced to the fullest extent permitted by law.
-
-10.2 This License is the entire agreement between the parties with respect to the rights granted here over the Database. It replaces any earlier understandings, agreements or representations with respect to the Database.
-
-10.3 If You are in breach of the terms of this License, You will not be entitled to rely on the terms of this License or to complain of any breach by the Licensor.
-
-10.4 Choice of law. This License takes effect in and will be governed by the laws of the relevant jurisdiction in which the License terms are sought to be enforced. If the standard suite of rights granted under applicable copyright law and Database Rights in the relevant jurisdiction includes additional rights not granted under this License, these additional rights are granted in this License in order to meet the terms of this License.
diff --git a/options/license/ODbL-1.0 b/options/license/ODbL-1.0
deleted file mode 100644
index 8ca51c358d..0000000000
--- a/options/license/ODbL-1.0
+++ /dev/null
@@ -1,540 +0,0 @@
-## Open Data Commons Open Database License (ODbL)
-
-### Preamble
-
-The Open Database License (ODbL) is a license agreement intended to
-allow users to freely share, modify, and use this Database while
-maintaining this same freedom for others. Many databases are covered by
-copyright, and therefore this document licenses these rights. Some
-jurisdictions, mainly in the European Union, have specific rights that
-cover databases, and so the ODbL addresses these rights, too. Finally,
-the ODbL is also an agreement in contract for users of this Database to
-act in certain ways in return for accessing this Database.
-
-Databases can contain a wide variety of types of content (images,
-audiovisual material, and sounds all in the same database, for example),
-and so the ODbL only governs the rights over the Database, and not the
-contents of the Database individually. Licensors should use the ODbL
-together with another license for the contents, if the contents have a
-single set of rights that uniformly covers all of the contents. If the
-contents have multiple sets of different rights, Licensors should
-describe what rights govern what contents together in the individual
-record or in some other way that clarifies what rights apply.
-
-Sometimes the contents of a database, or the database itself, can be
-covered by other rights not addressed here (such as private contracts,
-trade mark over the name, or privacy rights / data protection rights
-over information in the contents), and so you are advised that you may
-have to consult other documents or clear other rights before doing
-activities not covered by this License.
-
-------
-
-The Licensor (as defined below)
-
-and
-
-You (as defined below)
-
-agree as follows:
-
-### 1.0 Definitions of Capitalised Words
-
-"Collective Database" – Means this Database in unmodified form as part
-of a collection of independent databases in themselves that together are
-assembled into a collective whole. A work that constitutes a Collective
-Database will not be considered a Derivative Database.
-
-"Convey" – As a verb, means Using the Database, a Derivative Database,
-or the Database as part of a Collective Database in any way that enables
-a Person to make or receive copies of the Database or a Derivative
-Database. Conveying does not include interaction with a user through a
-computer network, or creating and Using a Produced Work, where no
-transfer of a copy of the Database or a Derivative Database occurs.
-"Contents" – The contents of this Database, which includes the
-information, independent works, or other material collected into the
-Database. For example, the contents of the Database could be factual
-data or works such as images, audiovisual material, text, or sounds.
-
-"Database" – A collection of material (the Contents) arranged in a
-systematic or methodical way and individually accessible by electronic
-or other means offered under the terms of this License.
-
-"Database Directive" – Means Directive 96/9/EC of the European
-Parliament and of the Council of 11 March 1996 on the legal protection
-of databases, as amended or succeeded.
-
-"Database Right" – Means rights resulting from the Chapter III ("sui
-generis") rights in the Database Directive (as amended and as transposed
-by member states), which includes the Extraction and Re-utilisation of
-the whole or a Substantial part of the Contents, as well as any similar
-rights available in the relevant jurisdiction under Section 10.4.
-
-"Derivative Database" – Means a database based upon the Database, and
-includes any translation, adaptation, arrangement, modification, or any
-other alteration of the Database or of a Substantial part of the
-Contents. This includes, but is not limited to, Extracting or
-Re-utilising the whole or a Substantial part of the Contents in a new
-Database.
-
-"Extraction" – Means the permanent or temporary transfer of all or a
-Substantial part of the Contents to another medium by any means or in
-any form.
-
-"License" – Means this license agreement and is both a license of rights
-such as copyright and Database Rights and an agreement in contract.
-
-"Licensor" – Means the Person that offers the Database under the terms
-of this License.
-
-"Person" – Means a natural or legal person or a body of persons
-corporate or incorporate.
-
-"Produced Work" – a work (such as an image, audiovisual material, text,
-or sounds) resulting from using the whole or a Substantial part of the
-Contents (via a search or other query) from this Database, a Derivative
-Database, or this Database as part of a Collective Database.
-
-"Publicly" – means to Persons other than You or under Your control by
-either more than 50% ownership or by the power to direct their
-activities (such as contracting with an independent consultant).
-
-"Re-utilisation" – means any form of making available to the public all
-or a Substantial part of the Contents by the distribution of copies, by
-renting, by online or other forms of transmission.
-
-"Substantial" – Means substantial in terms of quantity or quality or a
-combination of both. The repeated and systematic Extraction or
-Re-utilisation of insubstantial parts of the Contents may amount to the
-Extraction or Re-utilisation of a Substantial part of the Contents.
-
-"Use" – As a verb, means doing any act that is restricted by copyright
-or Database Rights whether in the original medium or any other; and
-includes without limitation distributing, copying, publicly performing,
-publicly displaying, and preparing derivative works of the Database, as
-well as modifying the Database as may be technically necessary to use it
-in a different mode or format.
-
-"You" – Means a Person exercising rights under this License who has not
-previously violated the terms of this License with respect to the
-Database, or who has received express permission from the Licensor to
-exercise rights under this License despite a previous violation.
-
-Words in the singular include the plural and vice versa.
-
-### 2.0 What this License covers
-
-2.1. Legal effect of this document. This License is:
-
- a. A license of applicable copyright and neighbouring rights;
-
- b. A license of the Database Right; and
-
- c. An agreement in contract between You and the Licensor.
-
-2.2 Legal rights covered. This License covers the legal rights in the
-Database, including:
-
- a. Copyright. Any copyright or neighbouring rights in the Database.
- The copyright licensed includes any individual elements of the
- Database, but does not cover the copyright over the Contents
- independent of this Database. See Section 2.4 for details. Copyright
- law varies between jurisdictions, but is likely to cover: the Database
- model or schema, which is the structure, arrangement, and organisation
- of the Database, and can also include the Database tables and table
- indexes; the data entry and output sheets; and the Field names of
- Contents stored in the Database;
-
- b. Database Rights. Database Rights only extend to the Extraction and
- Re-utilisation of the whole or a Substantial part of the Contents.
- Database Rights can apply even when there is no copyright over the
- Database. Database Rights can also apply when the Contents are removed
- from the Database and are selected and arranged in a way that would
- not infringe any applicable copyright; and
-
- c. Contract. This is an agreement between You and the Licensor for
- access to the Database. In return you agree to certain conditions of
- use on this access as outlined in this License.
-
-2.3 Rights not covered.
-
- a. This License does not apply to computer programs used in the making
- or operation of the Database;
-
- b. This License does not cover any patents over the Contents or the
- Database; and
-
- c. This License does not cover any trademarks associated with the
- Database.
-
-2.4 Relationship to Contents in the Database. The individual items of
-the Contents contained in this Database may be covered by other rights,
-including copyright, patent, data protection, privacy, or personality
-rights, and this License does not cover any rights (other than Database
-Rights or in contract) in individual Contents contained in the Database.
-For example, if used on a Database of images (the Contents), this
-License would not apply to copyright over individual images, which could
-have their own separate licenses, or one single license covering all of
-the rights over the images.
-
-### 3.0 Rights granted
-
-3.1 Subject to the terms and conditions of this License, the Licensor
-grants to You a worldwide, royalty-free, non-exclusive, terminable (but
-only under Section 9) license to Use the Database for the duration of
-any applicable copyright and Database Rights. These rights explicitly
-include commercial use, and do not exclude any field of endeavour. To
-the extent possible in the relevant jurisdiction, these rights may be
-exercised in all media and formats whether now known or created in the
-future.
-
-The rights granted cover, for example:
-
- a. Extraction and Re-utilisation of the whole or a Substantial part of
- the Contents;
-
- b. Creation of Derivative Databases;
-
- c. Creation of Collective Databases;
-
- d. Creation of temporary or permanent reproductions by any means and
- in any form, in whole or in part, including of any Derivative
- Databases or as a part of Collective Databases; and
-
- e. Distribution, communication, display, lending, making available, or
- performance to the public by any means and in any form, in whole or in
- part, including of any Derivative Database or as a part of Collective
- Databases.
-
-3.2 Compulsory license schemes. For the avoidance of doubt:
-
- a. Non-waivable compulsory license schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme cannot be waived, the Licensor reserves
- the exclusive right to collect such royalties for any exercise by You
- of the rights granted under this License;
-
- b. Waivable compulsory license schemes. In those jurisdictions in
- which the right to collect royalties through any statutory or
- compulsory licensing scheme can be waived, the Licensor waives the
- exclusive right to collect such royalties for any exercise by You of
- the rights granted under this License; and,
-
- c. Voluntary license schemes. The Licensor waives the right to collect
- royalties, whether individually or, in the event that the Licensor is
- a member of a collecting society that administers voluntary licensing
- schemes, via that society, from any exercise by You of the rights
- granted under this License.
-
-3.3 The right to release the Database under different terms, or to stop
-distributing or making available the Database, is reserved. Note that
-this Database may be multiple-licensed, and so You may have the choice
-of using alternative licenses for this Database. Subject to Section
-10.4, all other rights not expressly granted by Licensor are reserved.
-
-### 4.0 Conditions of Use
-
-4.1 The rights granted in Section 3 above are expressly made subject to
-Your complying with the following conditions of use. These are important
-conditions of this License, and if You fail to follow them, You will be
-in material breach of its terms.
-
-4.2 Notices. If You Publicly Convey this Database, any Derivative
-Database, or the Database as part of a Collective Database, then You
-must:
-
- a. Do so only under the terms of this License or another license
- permitted under Section 4.4;
-
- b. Include a copy of this License (or, as applicable, a license
- permitted under Section 4.4) or its Uniform Resource Identifier (URI)
- with the Database or Derivative Database, including both in the
- Database or Derivative Database and in any relevant documentation; and
-
- c. Keep intact any copyright or Database Right notices and notices
- that refer to this License.
-
- d. If it is not possible to put the required notices in a particular
- file due to its structure, then You must include the notices in a
- location (such as a relevant directory) where users would be likely to
- look for it.
-
-4.3 Notice for using output (Contents). Creating and Using a Produced
-Work does not require the notice in Section 4.2. However, if you
-Publicly Use a Produced Work, You must include a notice associated with
-the Produced Work reasonably calculated to make any Person that uses,
-views, accesses, interacts with, or is otherwise exposed to the Produced
-Work aware that Content was obtained from the Database, Derivative
-Database, or the Database as part of a Collective Database, and that it
-is available under this License.
-
- a. Example notice. The following text will satisfy notice under
- Section 4.3:
-
- Contains information from DATABASE NAME, which is made available
- here under the Open Database License (ODbL).
-
-DATABASE NAME should be replaced with the name of the Database and a
-hyperlink to the URI of the Database. "Open Database License" should
-contain a hyperlink to the URI of the text of this License. If
-hyperlinks are not possible, You should include the plain text of the
-required URI's with the above notice.
-
-4.4 Share alike.
-
- a. Any Derivative Database that You Publicly Use must be only under
- the terms of:
-
- i. This License;
-
- ii. A later version of this License similar in spirit to this
- License; or
-
- iii. A compatible license.
-
- If You license the Derivative Database under one of the licenses
- mentioned in (iii), You must comply with the terms of that license.
-
- b. For the avoidance of doubt, Extraction or Re-utilisation of the
- whole or a Substantial part of the Contents into a new database is a
- Derivative Database and must comply with Section 4.4.
-
- c. Derivative Databases and Produced Works. A Derivative Database is
- Publicly Used and so must comply with Section 4.4. if a Produced Work
- created from the Derivative Database is Publicly Used.
-
- d. Share Alike and additional Contents. For the avoidance of doubt,
- You must not add Contents to Derivative Databases under Section 4.4 a
- that are incompatible with the rights granted under this License.
-
- e. Compatible licenses. Licensors may authorise a proxy to determine
- compatible licenses under Section 4.4 a iii. If they do so, the
- authorised proxy's public statement of acceptance of a compatible
- license grants You permission to use the compatible license.
-
-
-4.5 Limits of Share Alike. The requirements of Section 4.4 do not apply
-in the following:
-
- a. For the avoidance of doubt, You are not required to license
- Collective Databases under this License if You incorporate this
- Database or a Derivative Database in the collection, but this License
- still applies to this Database or a Derivative Database as a part of
- the Collective Database;
-
- b. Using this Database, a Derivative Database, or this Database as
- part of a Collective Database to create a Produced Work does not
- create a Derivative Database for purposes of Section 4.4; and
-
- c. Use of a Derivative Database internally within an organisation is
- not to the public and therefore does not fall under the requirements
- of Section 4.4.
-
-4.6 Access to Derivative Databases. If You Publicly Use a Derivative
-Database or a Produced Work from a Derivative Database, You must also
-offer to recipients of the Derivative Database or Produced Work a copy
-in a machine readable form of:
-
- a. The entire Derivative Database; or
-
- b. A file containing all of the alterations made to the Database or
- the method of making the alterations to the Database (such as an
- algorithm), including any additional Contents, that make up all the
- differences between the Database and the Derivative Database.
-
-The Derivative Database (under a.) or alteration file (under b.) must be
-available at no more than a reasonable production cost for physical
-distributions and free of charge if distributed over the internet.
-
-4.7 Technological measures and additional terms
-
- a. This License does not allow You to impose (except subject to
- Section 4.7 b.) any terms or any technological measures on the
- Database, a Derivative Database, or the whole or a Substantial part of
- the Contents that alter or restrict the terms of this License, or any
- rights granted under it, or have the effect or intent of restricting
- the ability of any person to exercise those rights.
-
- b. Parallel distribution. You may impose terms or technological
- measures on the Database, a Derivative Database, or the whole or a
- Substantial part of the Contents (a "Restricted Database") in
- contravention of Section 4.74 a. only if You also make a copy of the
- Database or a Derivative Database available to the recipient of the
- Restricted Database:
-
- i. That is available without additional fee;
-
- ii. That is available in a medium that does not alter or restrict
- the terms of this License, or any rights granted under it, or have
- the effect or intent of restricting the ability of any person to
- exercise those rights (an "Unrestricted Database"); and
-
- iii. The Unrestricted Database is at least as accessible to the
- recipient as a practical matter as the Restricted Database.
-
- c. For the avoidance of doubt, You may place this Database or a
- Derivative Database in an authenticated environment, behind a
- password, or within a similar access control scheme provided that You
- do not alter or restrict the terms of this License or any rights
- granted under it or have the effect or intent of restricting the
- ability of any person to exercise those rights.
-
-4.8 Licensing of others. You may not sublicense the Database. Each time
-You communicate the Database, the whole or Substantial part of the
-Contents, or any Derivative Database to anyone else in any way, the
-Licensor offers to the recipient a license to the Database on the same
-terms and conditions as this License. You are not responsible for
-enforcing compliance by third parties with this License, but You may
-enforce any rights that You have over a Derivative Database. You are
-solely responsible for any modifications of a Derivative Database made
-by You or another Person at Your direction. You may not impose any
-further restrictions on the exercise of the rights granted or affirmed
-under this License.
-
-### 5.0 Moral rights
-
-5.1 Moral rights. This section covers moral rights, including any rights
-to be identified as the author of the Database or to object to treatment
-that would otherwise prejudice the author's honour and reputation, or
-any other derogatory treatment:
-
- a. For jurisdictions allowing waiver of moral rights, Licensor waives
- all moral rights that Licensor may have in the Database to the fullest
- extent possible by the law of the relevant jurisdiction under Section
- 10.4;
-
- b. If waiver of moral rights under Section 5.1 a in the relevant
- jurisdiction is not possible, Licensor agrees not to assert any moral
- rights over the Database and waives all claims in moral rights to the
- fullest extent possible by the law of the relevant jurisdiction under
- Section 10.4; and
-
- c. For jurisdictions not allowing waiver or an agreement not to assert
- moral rights under Section 5.1 a and b, the author may retain their
- moral rights over certain aspects of the Database.
-
-Please note that some jurisdictions do not allow for the waiver of moral
-rights, and so moral rights may still subsist over the Database in some
-jurisdictions.
-
-### 6.0 Fair dealing, Database exceptions, and other rights not affected
-
-6.1 This License does not affect any rights that You or anyone else may
-independently have under any applicable law to make any use of this
-Database, including without limitation:
-
- a. Exceptions to the Database Right including: Extraction of Contents
- from non-electronic Databases for private purposes, Extraction for
- purposes of illustration for teaching or scientific research, and
- Extraction or Re-utilisation for public security or an administrative
- or judicial procedure.
-
- b. Fair dealing, fair use, or any other legally recognised limitation
- or exception to infringement of copyright or other applicable laws.
-
-6.2 This License does not affect any rights of lawful users to Extract
-and Re-utilise insubstantial parts of the Contents, evaluated
-quantitatively or qualitatively, for any purposes whatsoever, including
-creating a Derivative Database (subject to other rights over the
-Contents, see Section 2.4). The repeated and systematic Extraction or
-Re-utilisation of insubstantial parts of the Contents may however amount
-to the Extraction or Re-utilisation of a Substantial part of the
-Contents.
-
-### 7.0 Warranties and Disclaimer
-
-7.1 The Database is licensed by the Licensor "as is" and without any
-warranty of any kind, either express, implied, or arising by statute,
-custom, course of dealing, or trade usage. Licensor specifically
-disclaims any and all implied warranties or conditions of title,
-non-infringement, accuracy or completeness, the presence or absence of
-errors, fitness for a particular purpose, merchantability, or otherwise.
-Some jurisdictions do not allow the exclusion of implied warranties, so
-this exclusion may not apply to You.
-
-### 8.0 Limitation of liability
-
-8.1 Subject to any liability that may not be excluded or limited by law,
-the Licensor is not liable for, and expressly excludes, all liability
-for loss or damage however and whenever caused to anyone by any use
-under this License, whether by You or by anyone else, and whether caused
-by any fault on the part of the Licensor or not. This exclusion of
-liability includes, but is not limited to, any special, incidental,
-consequential, punitive, or exemplary damages such as loss of revenue,
-data, anticipated profits, and lost business. This exclusion applies
-even if the Licensor has been advised of the possibility of such
-damages.
-
-8.2 If liability may not be excluded by law, it is limited to actual and
-direct financial loss to the extent it is caused by proved negligence on
-the part of the Licensor.
-
-### 9.0 Termination of Your rights under this License
-
-9.1 Any breach by You of the terms and conditions of this License
-automatically terminates this License with immediate effect and without
-notice to You. For the avoidance of doubt, Persons who have received the
-Database, the whole or a Substantial part of the Contents, Derivative
-Databases, or the Database as part of a Collective Database from You
-under this License will not have their licenses terminated provided
-their use is in full compliance with this License or a license granted
-under Section 4.8 of this License. Sections 1, 2, 7, 8, 9 and 10 will
-survive any termination of this License.
-
-9.2 If You are not in breach of the terms of this License, the Licensor
-will not terminate Your rights under it.
-
-9.3 Unless terminated under Section 9.1, this License is granted to You
-for the duration of applicable rights in the Database.
-
-9.4 Reinstatement of rights. If you cease any breach of the terms and
-conditions of this License, then your full rights under this License
-will be reinstated:
-
- a. Provisionally and subject to permanent termination until the 60th
- day after cessation of breach;
-
- b. Permanently on the 60th day after cessation of breach unless
- otherwise reasonably notified by the Licensor; or
-
- c. Permanently if reasonably notified by the Licensor of the
- violation, this is the first time You have received notice of
- violation of this License from the Licensor, and You cure the
- violation prior to 30 days after your receipt of the notice.
-
-Persons subject to permanent termination of rights are not eligible to
-be a recipient and receive a license under Section 4.8.
-
-9.5 Notwithstanding the above, Licensor reserves the right to release
-the Database under different license terms or to stop distributing or
-making available the Database. Releasing the Database under different
-license terms or stopping the distribution of the Database will not
-withdraw this License (or any other license that has been, or is
-required to be, granted under the terms of this License), and this
-License will continue in full force and effect unless terminated as
-stated above.
-
-### 10.0 General
-
-10.1 If any provision of this License is held to be invalid or
-unenforceable, that must not affect the validity or enforceability of
-the remainder of the terms and conditions of this License and each
-remaining provision of this License shall be valid and enforced to the
-fullest extent permitted by law.
-
-10.2 This License is the entire agreement between the parties with
-respect to the rights granted here over the Database. It replaces any
-earlier understandings, agreements or representations with respect to
-the Database.
-
-10.3 If You are in breach of the terms of this License, You will not be
-entitled to rely on the terms of this License or to complain of any
-breach by the Licensor.
-
-10.4 Choice of law. This License takes effect in and will be governed by
-the laws of the relevant jurisdiction in which the License terms are
-sought to be enforced. If the standard suite of rights granted under
-applicable copyright law and Database Rights in the relevant
-jurisdiction includes additional rights not granted under this License,
-these additional rights are granted in this License in order to meet the
-terms of this License.
diff --git a/options/license/OFFIS b/options/license/OFFIS
deleted file mode 100644
index ad48f181c3..0000000000
--- a/options/license/OFFIS
+++ /dev/null
@@ -1,22 +0,0 @@
-Copyright (C) 1994-2001, OFFIS
-
-This software and supporting documentation were developed by
-
-Kuratorium OFFIS e.V.
-Healthcare Information and Communication Systems
-Escherweg 2
-D-26121 Oldenburg, Germany
-
-THIS SOFTWARE IS MADE AVAILABLE, AS IS, AND OFFIS MAKES NO WARRANTY
-REGARDING THE SOFTWARE, ITS PERFORMANCE, ITS MERCHANTABILITY OR
-FITNESS FOR ANY PARTICULAR USE, FREEDOM FROM ANY COMPUTER DISEASES OR
-ITS CONFORMITY TO ANY SPECIFICATION. THE ENTIRE RISK AS TO QUALITY AND
-PERFORMANCE OF THE SOFTWARE IS WITH THE USER.
-
-Copyright of the software and supporting documentation is, unless
-otherwise stated, owned by OFFIS, and free access is hereby granted as
-a license to use this software, copy this software and prepare
-derivative works based upon this software. However, any distribution
-of this software source code or supporting documentation or derivative
-works (source code and supporting documentation) must include the
-three paragraphs of this copyright notice.
diff --git a/options/license/OFL-1.0 b/options/license/OFL-1.0
deleted file mode 100644
index 9673cd20f5..0000000000
--- a/options/license/OFL-1.0
+++ /dev/null
@@ -1,49 +0,0 @@
-SIL OPEN FONT LICENSE
-
-Version 1.0 - 22 November 2005
-
-PREAMBLE
-
-The goals of the Open Font License (OFL) are to stimulate worldwide development of cooperative font projects, to support the font creation efforts of academic and linguistic communities, and to provide an open framework in which fonts may be shared and improved in partnership with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and sold with any software provided that the font names of derivative works are changed. The fonts and derivatives, however, cannot be released under any other type of license.
-
-DEFINITIONS
-
-"Font Software" refers to any and all of the following:
-
- - font files
- - data files
- - source code
- - build scripts
- - documentation
-
-"Reserved Font Name" refers to the Font Software name as seen by users and any other names as specified after the copyright statement.
-
-"Standard Version" refers to the collection of Font Software components as distributed by the Copyright Holder.
-
-"Modified Version" refers to any derivative font software made by adding to, deleting, or substituting — in part or in whole -- any of the components of the Standard Version, by changing formats or by porting the Font Software to a new environment.
-
-"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components, in Standard or Modified Versions, may be sold by itself.
-
-2) Standard or Modified Versions of the Font Software may be bundled, redistributed and sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font Name(s), in part or in whole, unless explicit written permission is granted by the Copyright Holder. This restriction applies to all references stored in the Font Software, such as the font menu name and other font description fields, which are used to differentiate the font from others.
-
-4) The name(s) of the Copyright Holder or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder and the Author(s) or with their explicit written permission.
-
-5) The Font Software, modified or unmodified, in part or in whole, must be distributed using this license, and may not be distributed under any other license.
-
-TERMINATION
-
-This license becomes null and void if any of the above conditions are not met.
-
-DISCLAIMER
-
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/options/license/OFL-1.0-RFN b/options/license/OFL-1.0-RFN
deleted file mode 100644
index 9673cd20f5..0000000000
--- a/options/license/OFL-1.0-RFN
+++ /dev/null
@@ -1,49 +0,0 @@
-SIL OPEN FONT LICENSE
-
-Version 1.0 - 22 November 2005
-
-PREAMBLE
-
-The goals of the Open Font License (OFL) are to stimulate worldwide development of cooperative font projects, to support the font creation efforts of academic and linguistic communities, and to provide an open framework in which fonts may be shared and improved in partnership with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and sold with any software provided that the font names of derivative works are changed. The fonts and derivatives, however, cannot be released under any other type of license.
-
-DEFINITIONS
-
-"Font Software" refers to any and all of the following:
-
- - font files
- - data files
- - source code
- - build scripts
- - documentation
-
-"Reserved Font Name" refers to the Font Software name as seen by users and any other names as specified after the copyright statement.
-
-"Standard Version" refers to the collection of Font Software components as distributed by the Copyright Holder.
-
-"Modified Version" refers to any derivative font software made by adding to, deleting, or substituting — in part or in whole -- any of the components of the Standard Version, by changing formats or by porting the Font Software to a new environment.
-
-"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components, in Standard or Modified Versions, may be sold by itself.
-
-2) Standard or Modified Versions of the Font Software may be bundled, redistributed and sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font Name(s), in part or in whole, unless explicit written permission is granted by the Copyright Holder. This restriction applies to all references stored in the Font Software, such as the font menu name and other font description fields, which are used to differentiate the font from others.
-
-4) The name(s) of the Copyright Holder or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder and the Author(s) or with their explicit written permission.
-
-5) The Font Software, modified or unmodified, in part or in whole, must be distributed using this license, and may not be distributed under any other license.
-
-TERMINATION
-
-This license becomes null and void if any of the above conditions are not met.
-
-DISCLAIMER
-
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/options/license/OFL-1.0-no-RFN b/options/license/OFL-1.0-no-RFN
deleted file mode 100644
index 9673cd20f5..0000000000
--- a/options/license/OFL-1.0-no-RFN
+++ /dev/null
@@ -1,49 +0,0 @@
-SIL OPEN FONT LICENSE
-
-Version 1.0 - 22 November 2005
-
-PREAMBLE
-
-The goals of the Open Font License (OFL) are to stimulate worldwide development of cooperative font projects, to support the font creation efforts of academic and linguistic communities, and to provide an open framework in which fonts may be shared and improved in partnership with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and sold with any software provided that the font names of derivative works are changed. The fonts and derivatives, however, cannot be released under any other type of license.
-
-DEFINITIONS
-
-"Font Software" refers to any and all of the following:
-
- - font files
- - data files
- - source code
- - build scripts
- - documentation
-
-"Reserved Font Name" refers to the Font Software name as seen by users and any other names as specified after the copyright statement.
-
-"Standard Version" refers to the collection of Font Software components as distributed by the Copyright Holder.
-
-"Modified Version" refers to any derivative font software made by adding to, deleting, or substituting — in part or in whole -- any of the components of the Standard Version, by changing formats or by porting the Font Software to a new environment.
-
-"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components, in Standard or Modified Versions, may be sold by itself.
-
-2) Standard or Modified Versions of the Font Software may be bundled, redistributed and sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font Name(s), in part or in whole, unless explicit written permission is granted by the Copyright Holder. This restriction applies to all references stored in the Font Software, such as the font menu name and other font description fields, which are used to differentiate the font from others.
-
-4) The name(s) of the Copyright Holder or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder and the Author(s) or with their explicit written permission.
-
-5) The Font Software, modified or unmodified, in part or in whole, must be distributed using this license, and may not be distributed under any other license.
-
-TERMINATION
-
-This license becomes null and void if any of the above conditions are not met.
-
-DISCLAIMER
-
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/options/license/OFL-1.1-RFN b/options/license/OFL-1.1-RFN
deleted file mode 100644
index 6fe84ee21e..0000000000
--- a/options/license/OFL-1.1-RFN
+++ /dev/null
@@ -1,43 +0,0 @@
-SIL OPEN FONT LICENSE
-
-Version 1.1 - 26 February 2007
-
-PREAMBLE
-
-The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
-
-DEFINITIONS
-
-"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
-
-"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
-
-"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
-
-"Modified Version" refers to any derivative made by adding to, deleting, or substituting — in part or in whole — any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
-
-"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
-
-2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
-
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
-
-5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
-
-TERMINATION
-
-This license becomes null and void if any of the above conditions are not met.
-
-DISCLAIMER
-
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/options/license/OFL-1.1-no-RFN b/options/license/OFL-1.1-no-RFN
deleted file mode 100644
index 6fe84ee21e..0000000000
--- a/options/license/OFL-1.1-no-RFN
+++ /dev/null
@@ -1,43 +0,0 @@
-SIL OPEN FONT LICENSE
-
-Version 1.1 - 26 February 2007
-
-PREAMBLE
-
-The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
-
-DEFINITIONS
-
-"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
-
-"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
-
-"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
-
-"Modified Version" refers to any derivative made by adding to, deleting, or substituting — in part or in whole — any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
-
-"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
-
-2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
-
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
-
-5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
-
-TERMINATION
-
-This license becomes null and void if any of the above conditions are not met.
-
-DISCLAIMER
-
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/options/license/OGC-1.0 b/options/license/OGC-1.0
deleted file mode 100644
index 1b60c1fc7a..0000000000
--- a/options/license/OGC-1.0
+++ /dev/null
@@ -1,17 +0,0 @@
-OGC Software License, Version 1.0
-
-This OGC work (including software, documents, or other related items) is being provided by the copyright holders under the following license. By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions:
-
-Permission to use, copy, and modify this software and its documentation, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make:
-
-1. The full text of this NOTICE in a location viewable to users of the redistributed or derivative work.
-
-2. Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, a short notice of the following form (hypertext is preferred, text is permitted) should be used within the body of any redistributed or derivative code: "Copyright © [$date-of-document] Open Geospatial Consortium, Inc. All Rights Reserved. http://www.ogc.org/ogc/legal (Hypertext is preferred, but a textual representation is permitted.)
-
-3. Notice of any changes or modifications to the OGC files, including the date changes were made. (We recommend you provide URIs to the location from which the code is derived.)
-
-THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
-
-COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
-
-The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the software without specific, written prior permission. Title to copyright in this software and any associated documentation will at all times remain with copyright holders.
diff --git a/options/license/OGDL-Taiwan-1.0 b/options/license/OGDL-Taiwan-1.0
deleted file mode 100644
index 7f9e64b5c0..0000000000
--- a/options/license/OGDL-Taiwan-1.0
+++ /dev/null
@@ -1,141 +0,0 @@
-æ”¿åºœè³‡æ–™é–‹æ”¾æŽˆæ¬Šæ¢æ¬¾ï¼ç¬¬1版
-
-ä¸­è¯æ°‘國104å¹´7月27日訂定
-
-ç‚ºä¾¿åˆ©æ°‘çœ¾å…±äº«åŠæ‡‰ç”¨æ”¿åºœè³‡æ–™ã€ä¿ƒé€²åŠæ´»åŒ–政府資料應用ã€çµåˆæ°‘é–“å‰µæ„æå‡æ”¿åºœè³‡æ–™å“質åŠåƒ¹å€¼ã€å„ªåŒ–政府æœå‹™å“è³ªï¼Œè¨‚å®šæœ¬æ¢æ¬¾ã€‚
-
-一ã€å®šç¾©
-
-(一)資料æä¾›æ©Ÿé—œï¼šæŒ‡å°‡è·æ¬Šç¯„åœå…§å–得或作æˆä¹‹å„類電å­è³‡æ–™ï¼Œé€éŽæœ¬æ¢æ¬¾é‡‹å‡ºäºˆå…¬çœ¾ä¹‹æ”¿åºœæ©Ÿé—œï¼ˆæ§‹ï¼‰ã€å…¬ç‡Ÿäº‹æ¥­æ©Ÿæ§‹ã€å…¬ç«‹å­¸æ ¡åŠè¡Œæ”¿æ³•人。
-
-(二)ä½¿ç”¨è€…ï¼šæŒ‡ä¾æœ¬æ¢æ¬¾è¦å®šå–得開放資料,並å°å…¶åˆ©ç”¨ä¹‹è‡ªç„¶äººã€æ³•äººæˆ–åœ˜é«”ï¼ŒåŒ…æ‹¬ä¾æœ¬æ¢æ¬¾æŽˆæ¬Šä½¿ç”¨è€…å†è½‰æŽˆæ¬Šåˆ©ç”¨ä¹‹äººæˆ–團體。
-
-(三)開放資料:指資料æä¾›æ©Ÿé—œæ“有完整著作財產權,或經授權得å†è½‰æŽˆæ¬Šç¬¬ä¸‰äººåˆ©ç”¨ä¹‹è³‡æ–™ï¼Œä¸¦ä»¥å…¬é–‹ã€å¯ä¿®æ”¹ï¼Œä¸”ç„¡ä¸å¿…è¦æŠ€è¡“é™åˆ¶ä¹‹æ ¼å¼æä¾›è€…,包括但ä¸é™æ–¼ä¸‹åˆ—著作:
-
- 1. ç·¨è¼¯è‘—ä½œï¼šé¸æ“‡ã€ç·¨æŽ’具有創作性,而å¯å—著作權法ä¿è­·ä¹‹è³‡æ–™åº«æˆ–å…¶ä»–çµæ§‹åŒ–資料組åˆã€‚
-
- 2. ç´ æï¼šæŒ‡é–‹æ”¾è³‡æ–™é›†åˆç‰©ä¸­ï¼Œå…¶ä»–å¯å—著作權法ä¿è­·ä¹‹ç¨ç«‹è‘—作。
-
-(å››)è¡ç”Ÿç‰©ï¼šæŒ‡ä¾æœ¬æ¢æ¬¾æ‰€æä¾›ä¹‹é–‹æ”¾è³‡æ–™ï¼Œé€²è¡Œå¾ŒçºŒé‡è£½ã€æ”¹ä½œã€ç·¨è¼¯æˆ–為其他方å¼åˆ©ç”¨ä¹‹ä¿®æ”¹ç‰©ã€‚
-
-(五)資訊:指ä¸å—著作權法ä¿è­·ä¹‹ç´”粹紀錄,並隨åŒé–‹æ”¾è³‡æ–™ä¸€ä½µæä¾›è€…ã€‚å‰æ­è³‡è¨Šé™¤æœ¬æ¢æ¬¾æŽˆèˆ‡æ¬Šåˆ©ä¹‹è¦å®šå¤–,比照有關開放資料之è¦å®šè¾¦ç†ã€‚
-
-äºŒã€æŽˆèˆ‡æ¬Šåˆ©
-
-(一)儿©Ÿé—œæ‰€æä¾›ä¹‹é–‹æ”¾è³‡æ–™ï¼ŒæŽˆæ¬Šä½¿ç”¨è€…ä¸é™ç›®çš„ã€æ™‚é–“åŠåœ°åŸŸã€éžå°ˆå±¬ã€ä¸å¯æ’¤å›žã€å…授權金進行利用,利用之方å¼åŒ…括é‡è£½ã€æ•£å¸ƒã€å…¬é–‹å‚³è¼¸ã€å…¬é–‹æ’­é€ã€å…¬é–‹å£è¿°ã€å…¬é–‹ä¸Šæ˜ ã€å…¬é–‹æ¼”出ã€ç·¨è¼¯ã€æ”¹ä½œï¼ŒåŒ…括但ä¸é™æ–¼é–‹ç™¼å„ç¨®ç”¢å“æˆ–æœå‹™åž‹æ…‹ä¹‹è¡ç”Ÿç‰©ã€‚
-
-(二)使用者得å†è½‰æŽˆæ¬Šä»–人為å‰é …之利用。
-
-(三)ä½¿ç”¨è€…ä¾æœ¬æ¢æ¬¾è¦å®šåˆ©ç”¨é–‹æ”¾è³‡æ–™ï¼Œç„¡é ˆå¦è¡Œå–å¾—å„資料æä¾›æ©Ÿé—œä¹‹æ›¸é¢æˆ–å…¶ä»–æ–¹å¼æŽˆæ¬Šã€‚
-
-(å››)æœ¬æ¢æ¬¾ä¹‹æŽˆæ¬Šç¯„åœä¸åŒ…括專利權åŠå•†æ¨™æ¬Šã€‚
-
-三ã€èª²äºˆç¾©å‹™
-
-(一)ä½¿ç”¨è€…åˆ©ç”¨ä¾æœ¬æ¢æ¬¾æä¾›ä¹‹é–‹æ”¾è³‡æ–™ï¼Œè¦–ç‚ºåŒæ„éµå®ˆæœ¬æ¢æ¬¾ä¹‹å„é …è¦å®šï¼Œä¸¦æ‡‰ä»¥å°Šé‡ç¬¬ä¸‰äººè‘—作人格權之方å¼åˆ©ç”¨ä¹‹ã€‚
-
-(二)ä½¿ç”¨è€…åˆ©ç”¨ä¾æœ¬æ¢æ¬¾æä¾›ä¹‹é–‹æ”¾è³‡æ–™ï¼ŒåŠå¾ŒçºŒä¹‹è¡ç”Ÿç‰©ï¼Œæ‡‰ä»¥ç¬¦åˆé™„件所示「顯åè²æ˜Žã€è¦æ±‚之方å¼ï¼Œæ˜Žç¢ºæ¨™ç¤ºåŽŸè³‡æ–™æä¾›æ©Ÿé—œä¹‹ç›¸é—œè²æ˜Žï¼›æœªç›¡é¡¯å標示義務者,視為自始未å–得開放資料之授權。
-
-å››ã€ç‰ˆæœ¬æ›´æ–°åŠæŽˆæ¬Šè½‰æ›
-
-(一)æœ¬æ¢æ¬¾å¦‚有修正,ä¾èˆŠæ¢æ¬¾æä¾›ä¹‹é–‹æ”¾è³‡æ–™ï¼Œæ–¼æ–°æ¢æ¬¾å…¬å‘Šæ™‚ï¼Œä½¿ç”¨è€…å¾—é¸æ“‡æŽ¡ç”¨æ–°æ¢æ¬¾åˆ©ç”¨ã€‚但原資料æä¾›æ©Ÿé—œï¼Œæ–¼æä¾›é–‹æ”¾è³‡æ–™æ™‚ï¼Œå·²è¨‚æ˜Žå…¶ä½¿ç”¨ä¹‹ç‰¹å®šç‰ˆæœ¬æ¢æ¬¾è€…,ä¸åœ¨æ­¤é™ã€‚
-
-(二)æœ¬æ¢æ¬¾èˆ‡ã€Œå‰µç”¨CC授權 姓忍™ç¤º 4.0 國際版本ã€ç›¸å®¹ï¼Œä½¿ç”¨è€…便œ¬æ¢æ¬¾åˆ©ç”¨é–‹æ”¾è³‡æ–™ï¼Œå¦‚後續以「創用CC授權 姓忍™ç¤º 4.0 國際版本ã€è¦å®šä¹‹æ–¹å¼åˆ©ç”¨ï¼Œè¦–ç‚ºç¬¦åˆæœ¬æ¢æ¬¾ä¹‹è¦å®šã€‚
-
-五ã€åœæ­¢æä¾›
-
-有下列情形之一者,å„資料æä¾›æ©Ÿé—œå¾—åœæ­¢å…¨éƒ¨æˆ–一部開放資料之æä¾›ï¼Œä½¿ç”¨è€…ä¸å¾—å‘資料æä¾›æ©Ÿé—œè«‹æ±‚任何賠償或補償:
-
- 1. 因情事變更或其他正當事由,致å„資料æä¾›æ©Ÿé—œè©•估繼續æä¾›è©²é–‹æ”¾è³‡æ–™ä¾›å…¬çœ¾ä½¿ç”¨ï¼Œå·²ä¸ç¬¦åˆå…¬å…±åˆ©ç›Šä¹‹è¦æ±‚。
-
- 2. 所æä¾›ä¹‹é–‹æ”¾è³‡æ–™ï¼Œæœ‰ä¾µå®³ç¬¬ä¸‰äººæ™ºæ…§è²¡ç”¢æ¬Šã€éš±ç§æ¬Šæˆ–其他法律上利益之虞。
-
-å…­ã€å…è²¬è²æ˜Ž
-
-(一)便œ¬æ¢æ¬¾æä¾›ä¹‹é–‹æ”¾è³‡æ–™ï¼Œä¸æ§‹æˆä»»ä½•資料æä¾›æ©Ÿé—œç”³è¿°ã€ä¿è­‰æˆ–暗示其推薦ã€åŒæ„ã€è¨±å¯æˆ–æ ¸å‡†ä¹‹æ„æ€è¡¨ç¤ºï¼›å„資料æä¾›æ©Ÿé—œåƒ…於知悉其所æä¾›ä¹‹é–‹æ”¾è³‡æ–™æœ‰éŒ¯èª¤æˆ–éºæ¼æ™‚,負修正åŠè£œå……之責。
-
-(二)ä½¿ç”¨è€…åˆ©ç”¨ä¾æœ¬æ¢æ¬¾æä¾›ä¹‹é–‹æ”¾è³‡æ–™ï¼Œå—有æå®³æˆ–æå¤±ï¼Œæˆ–è‡´ç¬¬ä¸‰äººå—æœ‰æå®³æˆ–æå¤±ï¼Œè€Œé­æ±‚å„Ÿè€…ï¼Œé™¤æ³•ä»¤å¦æœ‰è¦å®šå¤–,å„資料æä¾›æ©Ÿé—œä¸è² ä»»ä½•賠償或補償之責。
-
-(三)ä½¿ç”¨è€…åˆ©ç”¨ä¾æœ¬æ¢æ¬¾æä¾›ä¹‹é–‹æ”¾è³‡æ–™ï¼Œå› æ•…æ„æˆ–éŽå¤±ï¼Œè‡´è³‡æ–™æä¾›æ©Ÿé—œé­å—æå®³ï¼Œæˆ–第三人因此å‘資料æä¾›æ©Ÿé—œè«‹æ±‚賠償æå®³ï¼Œä½¿ç”¨è€…應å°å„機關負賠償責任。
-
-ä¸ƒã€æº–據法
-
-æœ¬æ¢æ¬¾ä¹‹è§£é‡‹ã€æ•ˆåŠ›ã€å±¥è¡ŒåŠå…¶ä»–æœªç›¡äº‹å®œï¼Œä»¥ä¸­è¯æ°‘國法律為準據法。
-
-附件:顯åè²æ˜Ž
-
- 1. æä¾›æ©Ÿé—œï¼å–®ä½ [年份] [開放資料釋出å稱與版本號]
-
- 2. æ­¤é–‹æ”¾è³‡æ–™ä¾æ”¿åºœè³‡æ–™é–‹æ”¾æŽˆæ¬Šæ¢æ¬¾ (Open Government Data License) 進行公眾釋出,使用者於éµå®ˆæœ¬æ¢æ¬¾å„é …è¦å®šä¹‹å‰æä¸‹ï¼Œå¾—利用之。
-
- 3. æ”¿åºœè³‡æ–™é–‹æ”¾æŽˆæ¬Šæ¢æ¬¾ï¼šhttps://data.gov.tw/license
-
-Open Government Data License, version 1.0
-
-The Open Government Data License (the License) is intended to facilitate government data sharing and application among the public in outreaching and promotion method, and to advance government service efficacy and government data value and quality in collaboration with the creative private sector.
-
-1. Definition
-
-1.1. "Data Providing Organization" refers to government agency, government-owned business, public school and administrative legal entity that has various types of electronic data released to the public under the License when it is obtained or made in the scope of performance for public duties.
-
-1.2. "User" refers to individual, legal entity or group that receives and uses Open Data under the License, including individual, legal entity or group who is receiving and using Open Data as the recipient of the former Users under the sublicensing scenario.
-
-1.3. "Open Data" means data that the Data Providing Organization owns its copyright in whole or has full authority to provide it to third parties in sublicensing way, and provides it in an open and modifiable form such that there are no unnecessary technological obstacles to the performance of the licensed rights, including but not limited to the following creation protected by copyright:
-
- a. "Compilation Work" means a work formed by the creative selection and arrangement of data, and can be protected by copyright law, such as database or other qualified structured data combination.
-
- b. "Material" means a separate work, that is collected into the Open Data aggregation and can be protected by copyright law independently.
-
-1.4. "Derivative Work" means any adaptation based upon the Open Data provided under the License and in which the original data is reproduced, adapted, compiled, or otherwise modified.
-
-1.5. "Information" means the pure record that is not subject to copyright law and providing along with the Open Data. Accordingly, the granting of copyright license hereunder does not apply to such Information, however, other provisions of the License shall be applied to it as well as to the Open Data.
-
-2. Grant of Copyright License
-
-2.1. The Data Providing Organization grants User a perpetual, worldwide, non-exclusive, irrevocable, royalty-free copyright license to reproduce, distribute, publicly transmit, publicly broadcast, publicly recite, publicly present, publicly perform, compile, adapt to the Open Data provided for any purpose, including but not limited to making all kinds of Derivative Works either as products or services.
-
-2.2. User can sublicense the copyrights which he/she is granted through 2.1. to others.
-
-2.3. Any additional written offer or other formality for copyright license from the Data Providing Organization is not required, if User makes use of Open Data in compliance with the License.
-
-2.4. The License does not grant any rights in the patents and trademarks.
-
-3. Condition and Obligation
-
-3.1. By utilizing the Open Data provided under the License, User indicates his/her acceptance of this License and all its terms and conditions overall to do so, and shall make the reasonable efforts with respect to moral right protection of the third parties involved.
-
-3.2. When User makes use of the Open Data and its Derivative Work, he/she must make an explicit notice of statement as attribution requested in the Exhibit below by the Data Providing Organization. If User fails to comply with the attribution requirement, the rights granted under this License shall be deemed to have been void ab initio.
-
-4. License Version and Compatibility
-
-4.1. When a new version of the License has been updated and declared, if not the Data Providing Organization has already appointed a specific version of the License for the Open Data it provided, User may make use of the Open Data under the terms of the version of the License under which he/she originally received, or under the terms of any subsequent version published thereafter.
-
-4.2. The License is compatible with the Creative Commons Attribution License 4.0 International. This means that when the Open Data is provided under the License, User automatically satisfies the conditions of this License when he/she makes use of the Open Data in compliance with the Creative Commons Attribution License 4.0 International thereafter.
-
-5. Cessation of Data Providing
-
-5.1. Under the circumstances described hereunder, the Data Providing Organization may cease to provide all or part of a specific Open Data, and User shall not claim any damages or compensations on account of that to the provider:
-
- a. It has been evaluated by the Data Providing Organization that continuously providing of a specific Open Data as not being met the requirement of public interest due to the change of circumstances unpredictable or for a legitimate cause.
-
- b. A provided Open Data might jeopardize third parties' intellectual property rights, privacy rights, or other interests protected at law.
-
-6. Disclaimer
-
-6.1. The providing of Open Data under the License shall not be construed as any statement, warranty, or implication to the recommendation, permission, approval, or sanction of all kinds of authoritative declaration of intention made by the Data Providing Organization. And the Data Providing Organization shall only be liable to make the correcting and updating when the errors or omissions of Open Data provided by it has been acknowledged.
-
-6.2. The Data Providing Organization shall not be liable for damage or loss User encounters when he/she makes use of the Open Data provided under the License. This disclaimer applies as well when User has third parties encountered damage or loss and thus has been claimed for remedies. Unless otherwise specified according to law, the Data Providing Organization shall not be held responsible for any damages or compensations herein.
-
-6.3. User shall be liable for the damages to the Data Providing Organization, if he/she has used the Open Data provided wrongfully due to an intentional or negligent misconduct and caused damages to the Data Providing Organization. The same reimbursement rule for wrongful misconducting shall be applied to the User when the damaged one is a third party and the compensations have already been disbursed by the Data Providing Organization to the third party due to a legal claim.
-
-7. Governing Law
-
-7.1. The interpretation, validity, enforcement and matters not mentioned herein for the License is governed by the Laws of Republic of China (Taiwan).
-
-Exhibit - Attribution
-
- a. Data Providing Organization/Agency [year] [distinguishing full name of the released Open Data and its version number]
-
- b. The Open Data is made available to the public under the Open Government Data License, User can make use of it when complying to the condition and obligation of its terms.
-
- c. Open Government Data License:https://data.gov.tw/license
diff --git a/options/license/OGL-Canada-2.0 b/options/license/OGL-Canada-2.0
deleted file mode 100644
index d638334a11..0000000000
--- a/options/license/OGL-Canada-2.0
+++ /dev/null
@@ -1,51 +0,0 @@
-Open Government Licence - Canada
-
-You are encouraged to use the Information that is available under this licence with only a few conditions.
-
-Using Information under this licence
-* Use of any Information indicates your acceptance of the terms below.
-* The Information Provider grants you a worldwide, royalty-free, perpetual, non-exclusive licence to use the Information, including for commercial purposes, subject to the terms below.
-
-You are free to:
-* Copy, modify, publish, translate, adapt, distribute or otherwise use the Information in any medium, mode or format for any lawful purpose.
-
-You must, where you do any of the above:
-* Acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence.
-* If the Information Provider does not provide a specific attribution statement, or if you are using Information from several information providers and multiple attributions are not practical for your product or application, you must use the following attribution statement:
- Contains information licensed under the Open Government Licence – Canada.
-
-The terms of this licence are important, and if you fail to comply with any of them, the rights granted to you under this licence, or any similar licence granted by the Information Provider, will end automatically.
-
-Exemptions
-This licence does not grant you any right to use:
-* Personal Information;
-* third party rights the Information Provider is not authorized to license;
-* the names, crests, logos, or other official symbols of the Information Provider; and
-* Information subject to other intellectual property rights, including patents, trade-marks and official marks.
-
-Non-endorsement
-This licence does not grant you any right to use the Information in a way that suggests any official status or that the Information Provider endorses you or your use of the Information.
-
-No Warranty
-The Information is licensed “as isâ€, and the Information Provider excludes all representations, warranties, obligations, and liabilities, whether express or implied, to the maximum extent permitted by law.
-
-The Information Provider is not liable for any errors or omissions in the Information, and will not under any circumstances be liable for any direct, indirect, special, incidental, consequential, or other loss, injury or damage caused by its use or otherwise arising in connection with this licence or the Information, even if specifically advised of the possibility of such loss, injury or damage.
-
-Governing Law
-This licence is governed by the laws of the province of Ontario and the applicable laws of Canada.
-
-Legal proceedings related to this licence may only be brought in the courts of Ontario or the Federal Court of Canada.
-
-Definitions
-In this licence, the terms below have the following meanings:
-
-"Information" means information resources protected by copyright or other information that is offered for use under the terms of this licence.
-
-"Information Provider" means Her Majesty the Queen in right of Canada.
-
-“Personal Information†means “personal information†as defined in section 3 of the Privacy Act, R.S.C. 1985, c. P-21.
-
-"You" means the natural or legal person, or body of persons corporate or incorporate, acquiring rights under this licence.
-
-Versioning
-This is version 2.0 of the Open Government Licence – Canada. The Information Provider may make changes to the terms of this licence from time to time and issue a new version of the licence. Your use of the Information will be governed by the terms of the licence in force as of the date you accessed the information.
diff --git a/options/license/OGL-UK-1.0 b/options/license/OGL-UK-1.0
deleted file mode 100644
index 867c0e353b..0000000000
--- a/options/license/OGL-UK-1.0
+++ /dev/null
@@ -1,69 +0,0 @@
-Open Government Licence v1.0
-
-You are encouraged to use and re-use the Information that is available under this licence, the Open Government Licence, freely and flexibly, with only a few conditions.
-Using information under this licence
-
-Use of copyright and database right material expressly made available under this licence (the ‘Information’) indicates your acceptance of the terms and conditions below.
-
-The Licensor grants you a worldwide, royalty-free, perpetual, non-exclusive licence to use the Information subject to the conditions below.
-
-This licence does not affect your freedom under fair dealing or fair use or any other copyright or database right exceptions and limitations.
-
-You are free to:
- copy, publish, distribute and transmit the Information;
- adapt the Information;
- exploit the Information commercially for example, by combining it with other Information, or by including it in your own product or application.
-
-You must, where you do any of the above:
- acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence;
- If the Information Provider does not provide a specific attribution statement, or if you are using Information from several Information Providers and multiple attributions are not practical in your product or application, you may consider using the following: Contains public sector information licensed under the Open Government Licence v1.0.
- ensure that you do not use the Information in a way that suggests any official status or that the Information Provider endorses you or your use of the Information;
- ensure that you do not mislead others or misrepresent the Information or its source;
- ensure that your use of the Information does not breach the Data Protection Act 1998 or the Privacy and Electronic Communications (EC Directive) Regulations 2003.
-
-These are important conditions of this licence and if you fail to comply with them the rights granted to you under this licence, or any similar licence granted by the Licensor, will end automatically.
-
- Exemptions
-
-This licence does not cover the use of:
- - personal data in the Information;
- - Information that has neither been published nor disclosed under information access legislation (including the Freedom of Information Acts for the UK and Scotland) by or with the consent of the Information Provider;
- - departmental or public sector organisation logos, crests and the Royal Arms except where they form an integral part of a document or dataset;
- - military insignia;
- - third party rights the Information Provider is not authorised to license;
- - Information subject to other intellectual property rights, including patents, trademarks, and design rights; and
- - identity documents such as the British Passport.
-
-No warranty
-
-The Information is licensed ‘as is’ and the Information Provider excludes all representations, warranties, obligations and liabilities in relation to the Information to the maximum extent permitted by law.
-
-The Information Provider is not liable for any errors or omissions in the Information and shall not be liable for any loss, injury or damage of any kind caused by its use. The Information Provider does not guarantee the continued supply of the Information.
-
-Governing Law
-
-This licence is governed by the laws of the jurisdiction in which the Information Provider has its principal place of business, unless otherwise specified by the Information Provider.
-
-Definitions
-
-In this licence, the terms below have the following meanings:
-
-‘Information’ means information protected by copyright or by database right (for example, literary and artistic works, content, data and source code) offered for use under the terms of this licence.
-
-‘Information Provider’ means the person or organisation providing the Information under this licence.
-
-‘Licensor’ means any Information Provider which has the authority to offer Information under the terms of this licence or the Controller of Her Majesty’s Stationery Office, who has the authority to offer Information subject to Crown copyright and Crown database rights and Information subject to copyright and database right that has been assigned to or acquired by the Crown, under the terms of this licence.
-
-‘Use’ as a verb, means doing any act which is restricted by copyright or database right, whether in the original medium or in any other medium, and includes without limitation distributing, copying, adapting, modifying as may be technically necessary to use it in a different mode or format.
-
-‘You’ means the natural or legal person, or body of persons corporate or incorporate, acquiring rights under this licence.
-
-About the Open Government Licence
-The Controller of Her Majesty’s Stationery Office (HMSO) has developed this licence as a tool to enable Information Providers in the public sector to license the use and re-use of their Information under a common open licence. The Controller invites public sector bodies owning their own copyright and database rights to permit the use of their Information under this licence.
-
-The Controller of HMSO has authority to license Information subject to copyright and database right owned by the Crown. The extent of the Controller’s offer to license this Information under the terms of this licence is set out in the UK Government Licensing Framework.
-
-This is version 1.0 of the Open Government Licence. The Controller of HMSO may, from time to time, issue new versions of the Open Government Licence. However, you may continue to use Information licensed under this version should you wish to do so.
-These terms have been aligned to be interoperable with any Creative Commons Attribution Licence, which covers copyright, and Open Data Commons Attribution License, which covers database rights and applicable copyrights.
-
-Further context, best practice and guidance can be found in the UK Government Licensing Framework section on The National Archives website.
diff --git a/options/license/OGL-UK-2.0 b/options/license/OGL-UK-2.0
deleted file mode 100644
index 319c1b53a8..0000000000
--- a/options/license/OGL-UK-2.0
+++ /dev/null
@@ -1,72 +0,0 @@
-Open Government Licence v2.0
-
-You are encouraged to use and re-use the Information that is available under this licence freely and flexibly, with only a few conditions.
-
-Using Information under this licence
-Use of copyright and database right material expressly made available under this licence (the ‘Information’) indicates your acceptance of the terms and conditions below.
-
-The Licensor grants you a worldwide, royalty-free, perpetual, non-exclusive licence to use the Information subject to the conditions below.
-
-This licence does not affect your freedom under fair dealing or fair use or any other copyright or database right exceptions and limitations.
-
-You are free to:
-copy, publish, distribute and transmit the Information;
-adapt the Information;
-exploit the Information commercially and non-commercially for example, by combining it with other Information, or by including it in your own product or application.
-You must, where you do any of the above:
-acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence;
- If the Information Provider does not provide a specific attribution statement, or if you are using Information from several Information Providers and multiple attributions are not practical in your product or application, you may use the following:
-
- Contains public sector information licensed under the Open Government Licence v2.0.
-
-These are important conditions of this licence and if you fail to comply with them the rights granted to you under this licence, or any similar licence granted by the Licensor, will end automatically.
-
- Exemptions
-This licence does not cover:
-
-personal data in the Information;
-information that has neither been published nor disclosed under information access legislation (including the Freedom of Information Acts for the UK and Scotland) by or with the consent of the Information Provider;
-departmental or public sector organisation logos, crests and the Royal Arms except where they form an integral part of a document or dataset;
-military insignia;
-third party rights the Information Provider is not authorised to license;
-other intellectual property rights, including patents, trade marks, and design rights; and
-identity documents such as the British Passport
-Non-endorsement
-This licence does not grant you any right to use the Information in a way that suggests any official status or that the Information Provider endorses you or your use of the Information.
-
-Non warranty
-The Information is licensed ‘as is’ and the Information Provider excludes all representations, warranties, obligations and liabilities in relation to the Information to the maximum extent permitted by law.
-
-The Information Provider is not liable for any errors or omissions in the Information and shall not be liable for any loss, injury or damage of any kind caused by its use. The Information Provider does not guarantee the continued supply of the Information.
-
-Governing Law
-This licence is governed by the laws of the jurisdiction in which the Information Provider has its principal place of business, unless otherwise specified by the Information Provider.
-
-Definitions
-In this licence, the terms below have the following meanings:
-
-‘Information’
-means information protected by copyright or by database right (for example, literary and artistic works, content, data and source code) offered for use under the terms of this licence.
-
-‘Information Provider’
-means the person or organisation providing the Information under this licence.
-
-‘Licensor’
-means any Information Provider who has the authority to offer Information under the terms of this licence. It includes the Controller of Her Majesty’s Stationery Office, who has the authority to offer Information subject to Crown copyright and Crown database rights, and Information subject to copyright and database rights which have been assigned to or acquired by the Crown, under the terms of this licence.
-
-‘Use’
-means doing any act which is restricted by copyright or database right, whether in the original medium or in any other medium, and includes without limitation distributing, copying, adapting, modifying as may be technically necessary to use it in a different mode or format.
-
-‘You’
-means the natural or legal person, or body of persons corporate or incorporate, acquiring rights under this licence.
-
-About the Open Government Licence
-The Controller of Her Majesty’s Stationery Office (HMSO) has developed this licence as a tool to enable Information Providers in the public sector to license the use and re-use of their Information under a common open licence. The Controller invites public sector bodies owning their own copyright and database rights to permit the use of their Information under this licence.
-
-The Controller of HMSO has authority to license Information subject to copyright and database right owned by the Crown. The extent of the Controller’s offer to license this Information under the terms of this licence is set out on The National Archives website.
-
-This is version 2.0 of the Open Government Licence. The Controller of HMSO may, from time to time, issue new versions of the Open Government Licence. If you are already using Information under a previous version of the Open Government Licence, the terms of that licence will continue to apply.
-
-These terms are compatible with the Creative Commons Attribution License 4.0 and the Open Data Commons Attribution License, both of which license copyright and database rights. This means that when the Information is adapted and licensed under either of those licences, you automatically satisfy the conditions of the OGL when you comply with the other licence. The OGLv2.0 is Open Definition compliant.
-
-Further context, best practice and guidance can be found in the UK Government Licensing Framework section on The National Archives website.
diff --git a/options/license/OGL-UK-3.0 b/options/license/OGL-UK-3.0
deleted file mode 100644
index febac4164b..0000000000
--- a/options/license/OGL-UK-3.0
+++ /dev/null
@@ -1,69 +0,0 @@
-Open Government Licence v3.0
-
-You are encouraged to use and re-use the Information that is available under this licence freely and flexibly, with only a few conditions.
-
-Using Information under this licence
-Use of copyright and database right material expressly made available under this licence (the 'Information') indicates your acceptance of the terms and conditions below.
-
-The Licensor grants you a worldwide, royalty-free, perpetual, non-exclusive licence to use the Information subject to the conditions below.
-
-This licence does not affect your freedom under fair dealing or fair use or any other copyright or database right exceptions and limitations.
-
-You are free to:
-copy, publish, distribute and transmit the Information;
-adapt the Information;
-exploit the Information commercially and non-commercially for example, by combining it with other Information, or by including it in your own product or application.
-You must (where you do any of the above):
-acknowledge the source of the Information in your product or application by including or linking to any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence;
- If the Information Provider does not provide a specific attribution statement, you must use the following:
-
- Contains public sector information licensed under the Open Government Licence v3.0.
-
-If you are using Information from several Information Providers and listing multiple attributions is not practical in your product or application, you may include a URI or hyperlink to a resource that contains the required attribution statements.
-
-These are important conditions of this licence and if you fail to comply with them the rights granted to you under this licence, or any similar licence granted by the Licensor, will end automatically.
-
- Exemptions
-This licence does not cover:
-
-personal data in the Information;
-Information that has not been accessed by way of publication or disclosure under information access legislation (including the Freedom of Information Acts for the UK and Scotland) by or with the consent of the Information Provider;
-departmental or public sector organisation logos, crests and the Royal Arms except where they form an integral part of a document or dataset;
-military insignia;
-third party rights the Information Provider is not authorised to license;
-other intellectual property rights, including patents, trade marks, and design rights; and
-identity documents such as the British Passport
-Non-endorsement
-This licence does not grant you any right to use the Information in a way that suggests any official status or that the Information Provider and/or Licensor endorse you or your use of the Information.
-
-No warranty
-The Information is licensed 'as is' and the Information Provider and/or Licensor excludes all representations, warranties, obligations and liabilities in relation to the Information to the maximum extent permitted by law.
-
-The Information Provider and/or Licensor are not liable for any errors or omissions in the Information and shall not be liable for any loss, injury or damage of any kind caused by its use. The Information Provider does not guarantee the continued supply of the Information.
-
-Governing Law
-This licence is governed by the laws of the jurisdiction in which the Information Provider has its principal place of business, unless otherwise specified by the Information Provider.
-
-Definitions
-In this licence, the terms below have the following meanings:
-
-'Information' means information protected by copyright or by database right (for example, literary and artistic works, content, data and source code) offered for use under the terms of this licence.
-
-'Information Provider' means the person or organisation providing the Information under this licence.
-
-'Licensor' means any Information Provider which has the authority to offer Information under the terms of this licence or the Keeper of Public Records, who has the authority to offer Information subject to Crown copyright and Crown database rights and Information subject to copyright and database right that has been assigned to or acquired by the Crown, under the terms of this licence.
-
-'Use' means doing any act which is restricted by copyright or database right, whether in the original medium or in any other medium, and includes without limitation distributing, copying, adapting, modifying as may be technically necessary to use it in a different mode or format.
-
-'You', 'you' and 'your' means the natural or legal person, or body of persons corporate or incorporate, acquiring rights in the Information (whether the Information is obtained directly from the Licensor or otherwise) under this licence.
-
-About the Open Government Licence
-The National Archives has developed this licence as a tool to enable Information Providers in the public sector to license the use and re-use of their Information under a common open licence. The National Archives invites public sector bodies owning their own copyright and database rights to permit the use of their Information under this licence.
-
-The Keeper of the Public Records has authority to license Information subject to copyright and database right owned by the Crown. The extent of the offer to license this Information under the terms of this licence is set out in the UK Government Licensing Framework.
-
-This is version 3.0 of the Open Government Licence. The National Archives may, from time to time, issue new versions of the Open Government Licence. If you are already using Information under a previous version of the Open Government Licence, the terms of that licence will continue to apply.
-
-These terms are compatible with the Creative Commons Attribution License 4.0 and the Open Data Commons Attribution License, both of which license copyright and database rights. This means that when the Information is adapted and licensed under either of those licences, you automatically satisfy the conditions of the OGL when you comply with the other licence. The OGLv3.0 is Open Definition compliant.
-
-Further context, best practice and guidance can be found in the UK Government Licensing Framework section on The National Archives website.
diff --git a/options/license/OGTSL b/options/license/OGTSL
deleted file mode 100644
index 08617b0ef9..0000000000
--- a/options/license/OGTSL
+++ /dev/null
@@ -1,55 +0,0 @@
-The Open Group Test Suite License
-
-Preamble
-
-The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications.
-
-Testing is essential for proper development and maintenance of standards-based products.
-
-For buyers: adequate conformance testing leads to reduced integration costs and protection of investments in applications, software and people.
-
-For software developers: conformance testing of platforms and middleware greatly reduces the cost of developing and maintaining multi-platform application software.
-
-For suppliers: In-depth testing increases customer satisfaction and keeps development and support costs in check. API conformance is highly measurable and suppliers who claim it must be able to substantiate that claim.
-
-As such, since these are benchmark measures of conformance, we feel the integrity of test tools is of importance. In order to preserve the integrity of the existing conformance modes of this test package and to permit recipients of modified versions of this package to run the original test modes, this license requires that the original test modes be preserved.
-
-If you find a bug in one of the standards mode test cases, please let us know so we can feed this back into the original, and also raise any specification issues with the appropriate bodies (for example the POSIX committees).
-
-Definitions:
-
- "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification.
-
- "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder.
-
- "Copyright Holder" is whoever is named in the copyright or copyrights for the package.
-
- "You" is you, if you're thinking about copying or distributing this Package.
-
- "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.)
-
- "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it.
-
-1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers.
-
-2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version.
-
-3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least the following:
-
- rename any non-standard executables and testcases so the names do not conflict with standard executables and testcases, which must also be provided, and provide a separate manual page for each non-standard executable and testcase that clearly documents how it differs from the Standard Version.
-
-4. You may distribute the programs of this Package in object code or executable form, provided that you do at least the following:
-
- accompany any non-standard executables and testcases with their corresponding Standard Version executables and testcases, giving the non-standard executables and testcases non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version.
-
-5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own.
-
-6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package.
-
-7.Subroutines supplied by you and linked into this Package shall not be considered part of this Package.
-
-8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission.
-
-9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-The End
diff --git a/options/license/OLDAP-1.1 b/options/license/OLDAP-1.1
deleted file mode 100644
index f78dd0462d..0000000000
--- a/options/license/OLDAP-1.1
+++ /dev/null
@@ -1,60 +0,0 @@
-The OpenLDAP Public License
-Version 1.1, 25 August 1998
-
-Copyright 1998, The OpenLDAP Foundation. All Rights Reserved.
-
-Note: This license is derived from the "Artistic License" as distributed with the Perl Programming Language. Its terms are different from those of the "Artistic License."
-
-PREAMBLE
-
-The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications.
-
-Definitions:
-
- "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification.
-
- "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder.
-
- "Copyright Holder" is whoever is named in the copyright or copyrights for the package.
-
- "You" is you, if you're thinking about copying or distributing this Package.
-
- "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.)
-
- "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it.
-
-1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers.
-
-2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version.
-
-3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following:
-
- a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package.
-
- b) use the modified Package only within your corporation or organization.
-
- c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version.
-
- d) make other distribution arrangements with the Copyright Holder.
-
-4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following:
-
- a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version.
-
- b) accompany the distribution with the machine-readable source of the Package with your modifications.
-
- c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version.
-
- d) make other distribution arrangements with the Copyright Holder.
-
-5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own.
-
-6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package.
-
-7. C subroutines supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language.
-
-8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission.
-
-9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-The End
diff --git a/options/license/OLDAP-1.2 b/options/license/OLDAP-1.2
deleted file mode 100644
index c61d6026e3..0000000000
--- a/options/license/OLDAP-1.2
+++ /dev/null
@@ -1,60 +0,0 @@
-The OpenLDAP Public License
-Version 1.2, 1 September 1998
-
-Copyright 1998, The OpenLDAP Foundation. All Rights Reserved.
-
-Note: This license is derived from the "Artistic License" as distributed with the Perl Programming Language. As differences may exist, the complete license should be read.
-
-PREAMBLE
-
-The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications.
-
-Definitions:
-
- "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification.
-
- "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder.
-
- "Copyright Holder" is whoever is named in the copyright or copyrights for the package.
-
- "You" is you, if you're thinking about copying or distributing this Package.
-
- "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.)
-
- "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it.
-
-1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers.
-
-2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version.
-
-3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following:
-
- a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package.
-
- b) use the modified Package only within your corporation or organization.
-
- c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version.
-
- d) make other distribution arrangements with the Copyright Holder.
-
-4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following:
-
- a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version.
-
- b) accompany the distribution with the machine-readable source of the Package with your modifications.
-
- c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version.
-
- d) make other distribution arrangements with the Copyright Holder.
-
-5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own.
-
-6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package.
-
-7. C subroutines supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language.
-
-8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission.
-
-9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-The End
diff --git a/options/license/OLDAP-1.3 b/options/license/OLDAP-1.3
deleted file mode 100644
index f19e722f32..0000000000
--- a/options/license/OLDAP-1.3
+++ /dev/null
@@ -1,62 +0,0 @@
-The OpenLDAP Public License
-Version 1.3, 17 January 1999
-
-Copyright 1998-1999, The OpenLDAP Foundation. All Rights Reserved.
-
-Note: This license is derived from the "Artistic License" as distributed with the Perl Programming Language. As significant differences exist, the complete license should be read.
-
-PREAMBLE
-
-The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications.
-
-Definitions:
-
- "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification.
-
- "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder.
-
- "Copyright Holder" is whoever is named in the copyright or copyrights for the package.
-
- "You" is you, if you're thinking about copying or distributing this Package.
-
- "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.)
-
- "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it.
-
-1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers.
-
-2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version.
-
-3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following:
-
- a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package.
-
- b) use the modified Package only within your corporation or organization.
-
- c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version.
-
- d) make other distribution arrangements with the Copyright Holder.
-
-4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following:
-
- a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version.
-
- b) accompany the distribution with the machine-readable source of the Package with your modifications.
-
- c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version.
-
- d) make other distribution arrangements with the Copyright Holder.
-
-5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own.
-
-6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package.
-
-7. C subroutines supplied by you and linked into this Package in order to emulate subroutines and variables defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the behavior of the Package in any way that would cause it to fail the regression tests for the Package.
-
-8. Software supplied by you and linked with this Package in order to use subroutines and variables defined by this Package shall not be considered part of this Package and do not automatically fall under the copyright of this Package, and the executables produced by linking your software with this Package may be used and redistributed without restriction and may be sold commercially.
-
-9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission.
-
-10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-The End
diff --git a/options/license/OLDAP-1.4 b/options/license/OLDAP-1.4
deleted file mode 100644
index 4267f1421e..0000000000
--- a/options/license/OLDAP-1.4
+++ /dev/null
@@ -1,62 +0,0 @@
-The OpenLDAP Public License
-Version 1.4, 18 January 1999
-
-Copyright 1998-1999, The OpenLDAP Foundation. All Rights Reserved.
-
-Note: This license is derived from the "Artistic License" as distributed with the Perl Programming Language. As significant differences exist, the complete license should be read.
-
-PREAMBLE
-
-The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications.
-
-Definitions:
-
- "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification.
-
- "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder.
-
- "Copyright Holder" is whoever is named in the copyright or copyrights for the package.
-
- "You" is you, if you're thinking about copying or distributing this Package.
-
- "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.)
-
- "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it.
-
-1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers.
-
-2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version.
-
-3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following:
-
- a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package.
-
- b) use the modified Package only within your corporation or organization.
-
- c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version.
-
- d) make other distribution arrangements with the Copyright Holder.
-
-4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following:
-
- a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version.
-
- b) accompany the distribution with the machine-readable source of the Package with your modifications.
-
- c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version.
-
- d) make other distribution arrangements with the Copyright Holder.
-
-5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own.
-
-6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package.
-
-7. C subroutines supplied by you and linked into this Package in order to emulate subroutines and variables defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the behavior of the Package in any way that would cause it to fail the regression tests for the Package.
-
-8. Software supplied by you and linked with this Package in order to use subroutines and variables defined by this Package shall not be considered part of this Package and do not automatically fall under the copyright of this Package. Executables produced by linking your software with this Package may be used and redistributed without restriction and may be sold commercially so long as the primary function of your software is different than the package itself.
-
-9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission.
-
-10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-The End
diff --git a/options/license/OLDAP-2.0 b/options/license/OLDAP-2.0
deleted file mode 100644
index 8c460cb788..0000000000
--- a/options/license/OLDAP-2.0
+++ /dev/null
@@ -1,18 +0,0 @@
-The OpenLDAP Public License
-Version 2.0, 7 June 1999
-
-Copyright 1999, The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved.
-
-Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain copyright statements and notices. Redistributions must also contain a copy of this document.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. The name "OpenLDAP" must not be used to endorse or promote products derived from this Software without prior written permission of the OpenLDAP Foundation. For written permission, please contact foundation@openldap.org.
-
-4. Products derived from this Software may not be called "OpenLDAP" nor may "OpenLDAP" appear in their names without prior written permission of the OpenLDAP Foundation. OpenLDAP is a registered trademark of the OpenLDAP Foundation.
-
-5. Due credit should be given to the OpenLDAP Project (http://www.openldap.org/).
-
-THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/OLDAP-2.0.1 b/options/license/OLDAP-2.0.1
deleted file mode 100644
index c04920e943..0000000000
--- a/options/license/OLDAP-2.0.1
+++ /dev/null
@@ -1,18 +0,0 @@
-The OpenLDAP Public License
-Version 2.0.1, 21 December 1999
-
-Copyright 1999, The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved.
-
-Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain copyright statements and notices. Redistributions must also contain a copy of this document.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. The name "OpenLDAP" must not be used to endorse or promote products derived from this Software without prior written permission of the OpenLDAP Foundation. For written permission, please contact foundation@openldap.org.
-
-4. Products derived from this Software may not be called "OpenLDAP" nor may "OpenLDAP" appear in their names without prior written permission of the OpenLDAP Foundation. OpenLDAP is a trademark of the OpenLDAP Foundation.
-
-5. Due credit should be given to the OpenLDAP Project (http://www.openldap.org/).
-
-THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/OLDAP-2.1 b/options/license/OLDAP-2.1
deleted file mode 100644
index 4f461fe869..0000000000
--- a/options/license/OLDAP-2.1
+++ /dev/null
@@ -1,20 +0,0 @@
-The OpenLDAP Public License
-Version 2.1, 29 February 2000
-
-Copyright 1999-2000, The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved.
-
-Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain copyright statements and notices. Redistributions must also contain a copy of this document.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. The name "OpenLDAP" must not be used to endorse or promote products derived from this Software without prior written permission of the OpenLDAP Foundation. For written permission, please contact foundation@openldap.org.
-
-4. Products derived from this Software may not be called "OpenLDAP" nor may "OpenLDAP" appear in their names without prior written permission of the OpenLDAP Foundation. OpenLDAP is a trademark of the OpenLDAP Foundation.
-
-5. Due credit should be given to the OpenLDAP Project (http://www.openldap.org/).
-
-6. The OpenLDAP Foundation may revise this license from time to time. Each revision is distinguished by a version number. You may use the Software under terms of this license revision or under the terms of any subsequent license revision.
-
-THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/OLDAP-2.2 b/options/license/OLDAP-2.2
deleted file mode 100644
index 24446fd529..0000000000
--- a/options/license/OLDAP-2.2
+++ /dev/null
@@ -1,22 +0,0 @@
-The OpenLDAP Public License
-Version 2.2, 1 March 2000
-
-Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain copyright statements and notices. Redistributions must also contain a copy of this document.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. The name "OpenLDAP" must not be used to endorse or promote products derived from this Software without prior written permission of the OpenLDAP Foundation.
-
-4. Products derived from this Software may not be called "OpenLDAP" nor may "OpenLDAP" appear in their names without prior written permission of the OpenLDAP Foundation.
-
-5. Due credit should be given to the OpenLDAP Project (http://www.openldap.org/).
-
-6. The OpenLDAP Foundation may revise this license from time to time. Each revision is distinguished by a version number. You may use the Software under terms of this license revision or under the terms of any subsequent the license.
-
-THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-OpenLDAP is a trademark of the OpenLDAP Foundation.
-
-Copyright 1999-2000, The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved. Permission to copy and distributed verbatim copies of this document is granted.
diff --git a/options/license/OLDAP-2.2.1 b/options/license/OLDAP-2.2.1
deleted file mode 100644
index 3fd04859b8..0000000000
--- a/options/license/OLDAP-2.2.1
+++ /dev/null
@@ -1,22 +0,0 @@
-The OpenLDAP Public License
-Version 2.2.1, 1 March 2000
-
-Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain copyright statements and notices. Redistributions must also contain a copy of this document.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. The name "OpenLDAP" must not be used to endorse or promote products derived from this Software without prior written permission of the OpenLDAP Foundation.
-
-4. Products derived from this Software may not be called "OpenLDAP" nor may "OpenLDAP" appear in their names without prior written permission of the OpenLDAP Foundation.
-
-5. Due credit should be given to the OpenLDAP Project (http://www.openldap.org/).
-
-6. The OpenLDAP Foundation may revise this license from time to time. Each revision is distinguished by a version number. You may use the Software under terms of this license revision or under the terms of any subsequent revision of the license.
-
-THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-OpenLDAP is a trademark of the OpenLDAP Foundation.
-
-Copyright 1999-2000 The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved. Permission to copy and distributed verbatim copies of this document is granted.
diff --git a/options/license/OLDAP-2.2.2 b/options/license/OLDAP-2.2.2
deleted file mode 100644
index f466cdcc51..0000000000
--- a/options/license/OLDAP-2.2.2
+++ /dev/null
@@ -1,24 +0,0 @@
-The OpenLDAP Public License
-Version 2.2.2, 28 July 2000
-
-Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain copyright statements and notices.
-
-2. Redistributions in binary form must reproduce applicable copyright statements and notices, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. Redistributions must contain a verbatim copy of this document.
-
-4. The name "OpenLDAP" must not be used to endorse or promote products derived from this Software without prior written permission of the OpenLDAP Foundation.
-
-5. Products derived from this Software may not be called "OpenLDAP" nor may "OpenLDAP" appear in their names without prior written permission of the OpenLDAP Foundation.
-
-6. Due credit should be given to the OpenLDAP Project (http://www.openldap.org/).
-
-7. The OpenLDAP Foundation may revise this license from time to time. Each revision is distinguished by a version number. You may use the Software under terms of this license revision or under the terms of any subsequent revision of the license.
-
-THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-OpenLDAP is a trademark of the OpenLDAP Foundation.
-
-Copyright 1999-2000 The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved. Permission to copy and distributed verbatim copies of this document is granted.
diff --git a/options/license/OLDAP-2.3 b/options/license/OLDAP-2.3
deleted file mode 100644
index ebfc7f8efa..0000000000
--- a/options/license/OLDAP-2.3
+++ /dev/null
@@ -1,24 +0,0 @@
-The OpenLDAP Public License
-Version 2.3, 28 July 2000
-
-Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain copyright statements and notices.
-
-2. Redistributions in binary form must reproduce applicable copyright statements and notices, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. Redistributions must contain a verbatim copy of this document.
-
-4. The name "OpenLDAP" must not be used to endorse or promote products derived from this Software without prior written permission of the OpenLDAP Foundation.
-
-5. Products derived from this Software may not be called "OpenLDAP" nor may "OpenLDAP" appear in their names without prior written permission of the OpenLDAP Foundation.
-
-6. Due credit should be given to the OpenLDAP Project (http://www.openldap.org/).
-
-7. The OpenLDAP Foundation may revise this license from time to time. Each revision is distinguished by a version number. You may use the Software under terms of this license revision or under the terms of any subsequent revision of the license.
-
-THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-OpenLDAP is a trademark of the OpenLDAP Foundation.
-
-Copyright 1999-2000 The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved. Permission to copy and distributed verbatim copies of this document is granted.
diff --git a/options/license/OLDAP-2.4 b/options/license/OLDAP-2.4
deleted file mode 100644
index 353a553f2a..0000000000
--- a/options/license/OLDAP-2.4
+++ /dev/null
@@ -1,22 +0,0 @@
-The OpenLDAP Public License
-Version 2.4, 8 December 2000
-
-Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain copyright statements and notices.
-
-2. Redistributions in binary form must reproduce applicable copyright statements and notices, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. Redistributions must contain a verbatim copy of this document.
-
-4. The names and trademarks of the authors and copyright holders must not be used in advertising or otherwise to promote the sale, use or other dealing in this Software without specific, written prior permission.
-
-5. Due credit should be given to the OpenLDAP Project.
-
-6. The OpenLDAP Foundation may revise this license from time to time. Each revision is distinguished by a version number. You may use the Software under terms of this license revision or under the terms of any subsequent revision of the license.
-
-THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-OpenLDAP is a trademark of the OpenLDAP Foundation.
-
-Copyright 1999-2000 The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved. Permission to copy and distributed verbatim copies of this document is granted.
diff --git a/options/license/OLDAP-2.5 b/options/license/OLDAP-2.5
deleted file mode 100644
index 98e78e3c7d..0000000000
--- a/options/license/OLDAP-2.5
+++ /dev/null
@@ -1,22 +0,0 @@
-The OpenLDAP Public License
-Version 2.5, 11 May 2001
-
-Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain copyright statements and notices.
-
-2. Redistributions in binary form must reproduce applicable copyright statements and notices, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. Redistributions must contain a verbatim copy of this document.
-
-4. The names and trademarks of the authors and copyright holders must not be used in advertising or otherwise to promote the sale, use or other dealing in this Software without specific, written prior permission.
-
-5. Due credit should be given to the authors of the Software.
-
-6. The OpenLDAP Foundation may revise this license from time to time. Each revision is distinguished by a version number. You may use the Software under terms of this license revision or under the terms of any subsequent revision of the license.
-
-THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-OpenLDAP is a trademark of the OpenLDAP Foundation.
-
-Copyright 1999-2001 The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved. Permission to copy and distributed verbatim copies of this document is granted.
diff --git a/options/license/OLDAP-2.6 b/options/license/OLDAP-2.6
deleted file mode 100644
index 576d81ce84..0000000000
--- a/options/license/OLDAP-2.6
+++ /dev/null
@@ -1,20 +0,0 @@
-The OpenLDAP Public License
-Version 2.6, 14 June 2001
-
-Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain copyright statements and notices.
-
-2. Redistributions in binary form must reproduce applicable copyright statements and notices, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. Redistributions must contain a verbatim copy of this document.
-
-4. The names and trademarks of the authors and copyright holders must not be used in advertising or otherwise to promote the sale, use or other dealing in this Software without specific, written prior permission.
-
-5. The OpenLDAP Foundation may revise this license from time to time. Each revision is distinguished by a version number. You may use the Software under terms of this license revision or under the terms of any subsequent revision of the license.
-
-THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-OpenLDAP is a trademark of the OpenLDAP Foundation.
-
-Copyright 1999-2001 The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved. Permission to copy and distributed verbatim copies of this document is granted.
diff --git a/options/license/OLDAP-2.7 b/options/license/OLDAP-2.7
deleted file mode 100644
index 9ecc91292e..0000000000
--- a/options/license/OLDAP-2.7
+++ /dev/null
@@ -1,20 +0,0 @@
-The OpenLDAP Public License
-Version 2.7, 7 September 2001
-
-Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain copyright statements and notices,
-
-2. Redistributions in binary form must reproduce applicable copyright statements and notices, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution, and
-
-3. Redistributions must contain a verbatim copy of this document.
-
-The OpenLDAP Foundation may revise this license from time to time. Each revision is distinguished by a version number. You may use this Software under terms of this license revision or under the terms of any subsequent revision of the license.
-
-THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-The names of the authors and copyright holders must not be used in advertising or otherwise to promote the sale, use or other dealing in this Software without specific, written prior permission. Title to copyright in this Software shall at all times remain with copyright holders.
-
-OpenLDAP is a registered trademark of the OpenLDAP Foundation.
-
-Copyright 1999-2001 The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved. Permission to copy and distribute verbatim copies of this document is granted.
diff --git a/options/license/OLDAP-2.8 b/options/license/OLDAP-2.8
deleted file mode 100644
index bf512dd04f..0000000000
--- a/options/license/OLDAP-2.8
+++ /dev/null
@@ -1,20 +0,0 @@
-The OpenLDAP Public License
-Version 2.8, 17 August 2003
-
-Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions in source form must retain copyright statements and notices,
-
-2. Redistributions in binary form must reproduce applicable copyright statements and notices, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution, and
-
-3. Redistributions must contain a verbatim copy of this document.
-
-The OpenLDAP Foundation may revise this license from time to time. Each revision is distinguished by a version number. You may use this Software under terms of this license revision or under the terms of any subsequent revision of the license.
-
-THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-The names of the authors and copyright holders must not be used in advertising or otherwise to promote the sale, use or other dealing in this Software without specific, written prior permission. Title to copyright in this Software shall at all times remain with copyright holders.
-
-OpenLDAP is a registered trademark of the OpenLDAP Foundation.
-
-Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, California, USA. All Rights Reserved. Permission to copy and distribute verbatim copies of this document is granted.
diff --git a/options/license/OLFL-1.3 b/options/license/OLFL-1.3
deleted file mode 100644
index 77ffc8dc07..0000000000
--- a/options/license/OLFL-1.3
+++ /dev/null
@@ -1,220 +0,0 @@
-Open Logistics Foundation License
-Version 1.3, January 2023
-https://www.openlogisticsfoundation.org/licenses/
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION AND DISTRIBUTION
-
-§1 Definitions
-
-(1) "Subject Matter of the License" shall mean the works of software components
-in Source or Object form as well as any other components protected under
-copyright, design and/or patent law which are made available under this License.
-
-(2) "License" shall mean the terms and conditions for the use, reproduction and
-distribution of the Subject Matter of the License in accordance with the
-provisions of this document.
-
-(3) "Licensor(s)" shall mean the copyright holder(s) or the entity authorized by
-law or contract by the copyright holder(s) to grant the License.
-
-(4) "You" (or "Your") shall mean a natural or legal person exercising the
-permissions granted by this License.
-
-(5) "Source" form shall mean the preferred form for making modifications,
-including but not limited to software source code, documentation source, and
-configuration files.
-
-(6) "Object" form shall mean any form resulting from mechanical transformation
-or translation of a Source form, including but not limited to compiled object
-code, generated documentation, and conversions to other media types.
-
-(7) "Derivative Works" shall mean any work, whether in Source or Object form or
-any other form, that is based on (or derived from) the Subject Matter of the
-License and for which the editorial revisions, annotations, elaborations, or
-other modifications represent, as a whole, an original work of authorship. For
-the purposes of this License, Derivative Works shall not include works that
-remain separable from, or merely link (or bind by name) to the interfaces of,
-the Subject Matter of the License and Derivative Works thereof.
-
-(8) "Contribution" shall mean any proprietary work, including the original
-version of the Subject Matter of the License and any changes or additions to
-such work, or Derivative Works of such work, that the rights holder, or a
-natural or legal person authorized to make submissions, intentionally submits to
-a Licensor to be incorporated into the Subject Matter of the License. For the
-purposes of this definition, "submit" shall mean any form of electronic or
-written communication which is sent to a Licensor or its representatives for the
-purpose of discussing or improving the Subject Matter of the License, including
-but not limited to communications sent via electronic mailing lists, source code
-control systems and issue tracking systems; however, communications that are
-clearly marked by the copyright holder as "not a contribution" or otherwise
-identified as such in writing are excluded.
-
-(9) "Contributor" shall mean the Licensor(s) and/or any natural or legal person
-on whose behalf the Licensor(s) receive(s) any Contribution subsequently
-incorporated into the Subject Matter of the License.
-
-§2 Grant of usage rights
-
-Subject to the terms and conditions of this License and compliance with the
-provisions of this License, You are hereby granted by each Contributor, insofar
-as applicable to the respective Subject Matter of the License the
-
-- royalty-free and non-exclusive,
-- sub-licensable for commercial and non-commercial purposes,
-- worldwide and perpetual,
-- irrevocable and non-terminable
-
-right to reproduce, prepare Derivative Works of, publicly display, publicly
-perform, and distribute the Subject Matter of the License and such Derivative
-Works in any form. This right of use includes but is not limited to the right
-
-- to use the Subject Matter of the License in any hardware and software
- environment (with regard to the software and data components), in particular
- to store or load it permanently or temporarily, to display it and run it,
- including to the extent reproductions are necessary to that end,
-- to otherwise modify, interpret, edit or redesign it,
-- to store, reproduce, exhibit, publish, distribute it in tangible or intangible
- form, on any medium or in any other way, for commercial and non-commercial
- purposes, in particular to communicate it privately or publicly, including via
- image, audio and other information carriers, irrespective of whether by wire
- or wireless means,
-- to use it in databases, data networks and online services, including the right
- to make the software and data components of the Subject Matter of the License
- available in Source or Object form to users of the aforementioned databases,
- networks and online services for research and retrieval purposes,
-- to allow third parties to use or operate it,
-- to use it for own purposes but also to provide services to third parties,
-- to distribute it
-
-in its original or modified, interpreted, edited or redesigned form.
-
-The foregoing right of use relates to the Subject Matter of the License, in
-particular to its Source and Object form of software components (including
-design rights, where applicable).
-
-§3 Grant of patent license
-
-Subject to the terms and conditions of this License and compliance with the
-provisions of this License, You are hereby granted by each Contributor a
-- royalty-free and non-exclusive,
-- worldwide and perpetual,
-- irrevocable (with the exception of the restrictions set out in this Section 3)
-
-patent license in all rights deriving from the patents, owned and licensable by
-the Contributor at the time of the submission of the Contribution, to
-
-- produce,
-- have produced,
-- use,
-- offer for sale,
-- sell,
-- import and otherwise transfer
-
-the Subject Matter of the License.
-
-However, said patent license shall cover only those rights deriving from the
-patents of the respective Contributors which are indispensable in order not to
-infringe that patent and only to the extent that the use of the Contributor’s
-respective Contributions, whether alone or in combination with other
-Contributions of the Contributors or any third parties together with the Subject
-Matter of the License for which these Contributions were submitted, would
-otherwise infringe that patent. The grant of license shall not include rights
-deriving from the patents which may in future become necessary for their lawful
-use due to subsequent modifications to the Subject Matter or Contributions made
-by third parties after the original submission.
-
-In the event that You institute patent litigation against any entity or person
-(including a counterclaim or countersuit in a legal action), alleging that the
-Subject Matter of the License or a Contribution incorporated or contained
-therein constitutes patent infringement or indirect infringement, all patent
-licenses which have been granted to You under this License for the Subject
-Matter of the License as well as this License itself shall be deemed terminated
-as of the date on which the action is filed.
-
-§4 Distribution
-
-You may reproduce and distribute copies of the Subject Matter of the License or
-Derivative Works on any medium, with or without modifications (with regard to
-software components in Source or Object form), provided that You comply with
-the following rules:
-
-- You must provide all other recipients of the Subject Matter of the License or
- of Derivative Works with a copy of this License and inform them that the
- Subject Matter of the License was originally licensed under this License.
-- You must ensure that modified files contain prominent notices indicating that
- You have modified the files.
-- You must retain all copyright, patent, trademark and attribution notices in
- the Subject Matter of the License in the Source form of any Derivative Works
- You distribute, with the exception of those notices that do not pertain to any
- part of the Derivative Works.
-
-You may add Your own copyright notices to Your modifications and state any
-additional or different license conditions and conditions for the use,
-reproduction or distribution of Your modifications or for those Derivative Works
-as a whole, provided that Your use, reproduction and distribution of the work
-complies with the terms and conditions set out in this License in all other
-respects.
-
-§5 Submission of Contributions
-
-Unless expressly stated otherwise, every Contribution that You have
-intentionally submitted for inclusion in the Subject Matter of the License is
-subject to this License without any additional terms or conditions applying.
-Irrespective of the above, none of the terms or conditions contained herein may
-be interpreted to supersede or modify the terms or conditions of any separate
-licensing agreement that You may have concluded with a Licensor for such
-Contributions, such as a so-called "Contributor License Agreement" (CLA).
-
-§6 Trademarks
-
-This License does not grant permission to use the trade names, trademarks,
-service marks or product names of the Licensor(s) or of a Contributor.
-
-§7 Limited warranty
-
-This License is granted free of charge and thus constitutes a gift. Accordingly,
-any warranty is excluded. The Subject Matter of the License is a work in
-progress; it is constantly being improved by countless Contributors. The Subject
-Matter of the License is not complete and may therefore contain errors ("bugs")
-or additional patents of Contributors or third parties, as is inherent in this
-type of development.
-
-§8 Limitation of liability
-
-Except in cases of intentional and grossly negligent conduct, the Contributors,
-their legal representatives, trustees, officers and employees shall not be
-liable for direct or indirect, material or immaterial loss or damage of any kind
-arising from the License or the use of the Subject Matter of the License; this
-applies, among other things, but not exclusively, to loss of goodwill, loss of
-production, computer failures or errors, loss of data or economic loss or
-damage, even if the Contributor has been notified of the possibility of such
-loss or damage. Irrespective of the above, the Licensor shall only be liable
-within the scope of statutory product liability to the extent that the
-respective provisions are applicable to the Subject Matter of the License or the
-Contribution.
-
-Except in cases of intentional conduct, the Contributors, their legal
-representatives, trustees, officers and employees shall not be liable for any
-infringement of third-party patent or intellectual property rights arising from
-the Contributions nor do they warrant that the Contributions are accurate,
-devoid of mistakes, complete and/or fit for any particular purpose.
-
-§9 Provision of warranties or assumption of additional liability in the event of
-distribution of the Subject Matter of the License
-
-In the event of distribution of the Subject Matter of the License or Derivative
-Works, You are free to accept support, warranty, indemnity or other liability
-obligations and/or rights consistent with this License and to charge a fee in
-return. However, in accepting such obligations, You may act only on Your own
-behalf and on Your sole responsibility, not on behalf of any other Contributor,
-and You hereby agree to indemnify, defend, and hold each Contributor harmless
-for any liability incurred by, or claims asserted against, such Contributor by
-reason of Your accepting any such warranty or additional liability.
-
-§10 Applicable law
-
-This License is governed by German law, excluding its conflict of laws
-provisions and the provisions of the UN Convention on Contracts for the
-International Sale of Goods (CISG).
-
-END OF TERMS AND CONDITIONS
diff --git a/options/license/OML b/options/license/OML
deleted file mode 100644
index 62b6589712..0000000000
--- a/options/license/OML
+++ /dev/null
@@ -1,5 +0,0 @@
-This FastCGI application library source and object code (the "Software") and its documentation (the "Documentation") are copyrighted by Open Market, Inc ("Open Market"). The following terms apply to all files associated with the Software and Documentation unless explicitly disclaimed in individual files.
-
-Open Market permits you to use, copy, modify, distribute, and license this Software and the Documentation for any purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses. Modifications to this Software and Documentation may be copyrighted by their authors and need not follow the licensing terms described here. If modifications to this Software and Documentation have new licensing terms, the new terms must be clearly indicated on the first page of each file where they apply.
-
-OPEN MARKET MAKES NO EXPRESS OR IMPLIED WARRANTY WITH RESPECT TO THE SOFTWARE OR THE DOCUMENTATION, INCLUDING WITHOUT LIMITATION ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL OPEN MARKET BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY DAMAGES ARISING FROM OR RELATING TO THIS SOFTWARE OR THE DOCUMENTATION, INCLUDING, WITHOUT LIMITATION, ANY INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR SIMILAR DAMAGES, INCLUDING LOST PROFITS OR LOST DATA, EVEN IF OPEN MARKET HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS". OPEN MARKET HAS NO LIABILITY IN CONTRACT, TORT, NEGLIGENCE OR OTHERWISE ARISING OUT OF THIS SOFTWARE OR THE DOCUMENTATION.
diff --git a/options/license/OPL-1.0 b/options/license/OPL-1.0
deleted file mode 100644
index 4d4fa3a6ed..0000000000
--- a/options/license/OPL-1.0
+++ /dev/null
@@ -1,136 +0,0 @@
-OPEN PUBLIC LICENSE
-Version 1.0
-
-1. Definitions.
-
- 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications.
-
- 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
- 1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
- 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
- 1.5. "Executable" means Covered Code in any form other than Source Code.
-
- 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
- 1.7. "Larger Work" means a work, which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.8. "License" means this document and the corresponding addendum described in section 6.4 below.
-
- 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
-
- A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
-
- B. Any new file that contains any part of the Original Code or previous Modifications.
-
- 1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
- 1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or a list of source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
- 1.12. "You" means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You'' includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control'' means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
- 1.13 "License Author" means Lutris Technologies, Inc.
-
-2. Source Code License.
-
- 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a worldwide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) under intellectual property rights (other than patent or trademark) to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, or as part of a Larger Work; and
-
- (b) under patents now or hereafter owned or controlled by Initial Developer, to make, have made, use and sell (``offer to sell and import'') the Original Code (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Original Code (or portions thereof) and not to any greater extent that may be necessary to Utilize further Modifications or combinations.
-
- 2.2. Contributor Grant. Each Contributor hereby grants You a worldwide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) under intellectual property rights (other than patent or trademark) to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code or as part of a Larger Work; and
-
- (b) under patents now or hereafter owned or controlled by Contributor, to to make, have made, use and sell (``offer to sell and import'') the Contributor Version (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Contributor Version (or portions thereof), and not to any greater extent that may be necessary to Utilize further Modifications or combinations
-
-3. Distribution Obligations.
-
- 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
- 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available, prior to any use, except for internal development and practice, in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You shall notify the Initial Developer of the Modification and the location of the Source Code via the contact means provided for in the Developer Specific license. Initial Developer will be acting as maintainer of the Source Code and may provide an Electronic Distribution mechanism for the Modification to be made available.
-
- 3.3. Description of Modifications. You must cause all Covered Code to which you contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
- 3.4. Intellectual Property Matters
-
- (a) Third Party Claims. If You have knowledge that a party claims an intellectual property right in particular functionality or code (or its utilization under this License), you must include a text file with the source code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after You make Your Modification available as described in Section 3.2, You shall promptly modify the LEGAL file in all copies You make available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
-
- (b) Representations. Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.
-
- 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code, and this License in any documentation for the Source Code, where You describe recipients' rights relating to Covered Code. If You created one or more Modification(s), You may add your name as a Contributor to the notice described in Exhibit A. If it is not possible to put such notice in a particular Source Code file due to its structure, then you must include such notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
- 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. If you distribute executable versions containing Covered Code, you must reproduce the notice in Exhibit B in the documentation and/or other materials provided with the product.
-
- 3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) Cite all of the statutes or regulations that prohibit you from complying fully with this license. (c) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Application of this License.
-
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A, and to related Covered Code.
-
-6. Versions of the License.
-
- 6.1. New Versions. License Author may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number and shall be submitted to opensource.org for certification.
-
- 6.2. Effect of New Versions. Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Initial Developer. No one other than Initial Developer has the right to modify the terms applicable to Covered Code created under this License.
-
- 6.3. Derivative Works. If you create or use a modified version of this License, except in association with the required Developer Specific License described in section 6.4, (which you may only do in order to apply it to code which is not already Covered Code governed by this License), you must (a) rename Your license so that the phrases ``Open'', ``OpenPL'', ``OPL'' or any confusingly similar phrase do not appear anywhere in your license and (b) otherwise make it clear that your version of the license contains terms which differ from the Open Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
- 6.4. Required Additional Developer Specific License
-This license is a union of the following two parts that should be found as text files in the same place (directory), in the order of preeminence:
-
- [1] A Developer specific license.
-
- [2] The contents of this file OPL_1_0.TXT, stating the general licensing policy of the software.
-
-In case of conflicting dispositions in the parts of this license, the terms of the lower-numbered part will always be superseded by the terms of the higher numbered part.
-
-7. DISCLAIMER OF WARRANTY.
-
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-
- 8.1 Termination upon Breach
-This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code, which are properly granted, shall survive any termination of this License. Provisions that, by their nature, must remain in effect beyond the termination of this License shall survive.
-
- 8.2. Termination Upon Litigation. If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant") alleging that:
-
- (a) such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.
-
- (b) any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant.
-
- 8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
- 8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY.
-
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO YOU OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS.
-
-The Covered Code is a ``commercial item,'' as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of ``commercial computer software'' and ``commercial computer software documentation,'' as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
-11. MISCELLANEOUS.
-
- his section was intentionally left blank. The contents of this section are found in the corresponding addendum described above.
-
-12. RESPONSIBILITY FOR CLAIMS.
-
-Except in cases where another Contributor has failed to comply with Section 3.4, You are responsible for damages arising, directly or indirectly, out of Your utilization of rights under this License, based on the number of copies of Covered Code you made available, the revenues you received from utilizing such rights, and other relevant factors. You agree to work with affected parties to distribute with Initial Developer responsibility on an equitable basis.
-
-EXHIBIT A.
-
-Text for this Exhibit A is found in the corresponding addendum, described in section 6.4 above, text file provided by the Initial Developer. This license is not valid or complete with out that file.
-
-EXHIBIT B.
-
-Text for this Exhibit B is found in the corresponding addendum, described in section 6.4 above, text file provided by the Initial Developer. This license is not valid or complete with out that file.
diff --git a/options/license/OPL-UK-3.0 b/options/license/OPL-UK-3.0
deleted file mode 100644
index ee8ca4dd81..0000000000
--- a/options/license/OPL-UK-3.0
+++ /dev/null
@@ -1,114 +0,0 @@
-United Kingdom Open Parliament Licence v3.0
-
-Open Parliament Licence
-
-You are encouraged to use and re-use the information that
-is available under this licence freely and flexibly, with
-only a few conditions. Using information under this licence
-
-Use of copyright and database right material made
-available under this licence (the ‘information’) indicates
-your acceptance of the terms and conditions below.
-
-The Licensor grants you a worldwide, royalty-free,
-perpetual, non-exclusive licence to use the
-information subject to the conditions below.
-
-This licence does not affect your freedom under
-fair dealing or fair use or any other copyright
-or database right exceptions and limitations.
-
-You are free to:
- * copy, publish, distribute and transmit the information
- * adapt the information
- * exploit the information commercially and non-commercially,
- for example, by combining it with other information,
- or by including it in your own product or application
-
-You must (where you do any of the above):
- * acknowledge the source of the information in your
- product or application by including the following
- attribution statement and, where possible, provide a
- link to this licence: Contains Parliamentary information
- licensed under the Open Parliament Licence v3.0.
-
-These are important conditions of this licence and
-if you fail to comply with them the rights granted to
-you under this licence, or any similar licence granted
-by the Licensor, will end automatically.
-
-Exemptions
-
-This licence does not cover the use of:
- * personal data in the information;
- * information that has neither been published nor disclosed
- under information access legislation (including the
- Freedom of Information Acts for the UK and Scotland) by or
- with the consent of the Licensor;
- * the Royal Arms and the Crowned Portcullis;
- * third party rights the Licensor is not authorised to license;
- * information subject to other intellectual property rights,
- including patents, trademarks, and design rights
-
-Non-endorsment
-
-This licence does not grant you any right to use the
-information in a way that suggests any official status or
-that the Licensor endorses you or your use of the Information.
-
-No warranty
-
-The information is licensed ‘as is’ and the
-Licensor excludes all representations, warranties,
-obligations and liabilities in relation to the
-information to the maximum extent permitted by law.
-The Licensor is not liable for any errors or omissions in
-the information and shall not be liable for any loss, injury
-or damage of any kind caused by its use. The Licensor does
-not guarantee the continued supply of the information.
-
-Governing law
-
-This licence is governed by the laws of England and Wales.
-
-Definitions
-
-In this licence, the terms below have the following meanings:
-
-‘Information’ means information protected by copyright
-or by database right (for example, literary and
-artistic works, content, data and source code)
-offered for use under the terms of this licence.
-
-‘Information Provider’ means either House of Parliament.
-
-‘Licensor’ means—
-(a) in relation to copyright, the Speaker of the House of
-Commons and the Clerk of the Parliaments representing
-the House of Commons and House of Lords respectively, and
-(b) in relation to database right, the Corporate
-Officer of the House of Commons and the Corporate
-Officer of the House of Lords respectively.
-
-‘Use’ means doing any act which is restricted by copyright
-or database right, whether in the original medium or in any
-other medium, and includes without limitation distributing,
-copying, adapting and modifying as may be technically
-necessary to use it in a different mode or format.
-
-‘You’ means the natural or legal person, or body of persons
-corporate or incorporate, acquiring rights under this licence.
-
-About the Open Parliament Licence
-
-This is version 3.0 of the Open Parliament Licence. The
-Licensor may, from time to time, issue new versions of the
-Open Parliament Licence. However, you may continue to use
-information licensed under this version should you wish to do so.
-
-The information licensed under the Open Parliament
-Licence includes Parliamentary information in which
-Crown copyright subsists. Further context, best practice
-and guidance relating to the re-use of public sector
-information can be found in the UK Government Licensing
-Framework section on The National Archives website.
diff --git a/options/license/OPUBL-1.0 b/options/license/OPUBL-1.0
deleted file mode 100644
index 1386621e0f..0000000000
--- a/options/license/OPUBL-1.0
+++ /dev/null
@@ -1,78 +0,0 @@
-Open Publication License
-
-v1.0, 8 June 1999
-
-I. REQUIREMENTS ON BOTH UNMODIFIED AND MODIFIED VERSIONS
-
-The Open Publication works may be reproduced and distributed in whole or in part, in any medium physical or electronic, provided that the terms of this license are adhered to, and that this license or an incorporation of it by reference (with any options elected by the author(s) and/or publisher) is displayed in the reproduction.
-
-Proper form for an incorporation by reference is as follows:
-
- Copyright (c) <year> by <author's name or designee>. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, vX.Y or later (the latest version is presently available at http://www.opencontent.org/openpub/).
-
-The reference must be immediately followed with any options elected by the author(s) and/or publisher of the document (see section VI).
-
-Commercial redistribution of Open Publication-licensed material is permitted.
-
-Any publication in standard (paper) book form shall require the citation of the original publisher and author. The publisher and author's names shall appear on all outer surfaces of the book. On all outer surfaces of the book the original publisher's name shall be as large as the title of the work and cited as possessive with respect to the title.
-
-II. COPYRIGHT
-
-The copyright to each Open Publication is owned by its author(s) or designee.
-
-III. SCOPE OF LICENSE
-
-The following license terms apply to all Open Publication works, unless otherwise explicitly stated in the document.
-
-Mere aggregation of Open Publication works or a portion of an Open Publication work with other works or programs on the same media shall not cause this license to apply to those other works. The aggregate work shall contain a notice specifying the inclusion of the Open Publication material and appropriate copyright notice.
-
-SEVERABILITY. If any part of this license is found to be unenforceable in any jurisdiction, the remaining portions of the license remain in force.
-
-NO WARRANTY. Open Publication works are licensed and provided "as is" without warranty of any kind, express or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose or a warranty of non-infringement.
-
-IV. REQUIREMENTS ON MODIFIED WORKS
-
-All modified versions of documents covered by this license, including translations, anthologies, compilations and partial documents, must meet the following requirements:
- 1. The modified version must be labeled as such.
- 2. The person making the modifications must be identified and the modifications dated.
- 3. Acknowledgement of the original author and publisher if applicable must be retained according to normal academic citation practices.
- 4. The location of the original unmodified document must be identified.
- 5. The original author's (or authors') name(s) may not be used to assert or imply endorsement of the resulting document without the original author's (or authors') permission.
-
-V. GOOD-PRACTICE RECOMMENDATIONS
-
-In addition to the requirements of this license, it is requested from and strongly recommended of redistributors that:
- 1. If you are distributing Open Publication works on hardcopy or CD-ROM, you provide email notification to the authors of your intent to redistribute at least thirty days before your manuscript or media freeze, to give the authors time to provide updated documents. This notification should describe modifications, if any, made to the document.
- 2. All substantive modifications (including deletions) be either clearly marked up in the document or else described in an attachment to the document.
- 3. Finally, while it is not mandatory under this license, it is considered good form to offer a free copy of any hardcopy and CD-ROM expression of an Open Publication-licensed work to its author(s).
-
-VI. LICENSE OPTIONS
-
-The author(s) and/or publisher of an Open Publication-licensed document may elect certain options by appending language to the reference to or copy of the license. These options are considered part of the license instance and must be included with the license (or its incorporation by reference) in derived works.
-
-A. To prohibit distribution of substantively modified versions without the explicit permission of the author(s). "Substantive modification" is defined as a change to the semantic content of the document, and excludes mere changes in format or typographical corrections.
-
-To accomplish this, add the phrase `Distribution of substantively modified versions of this document is prohibited without the explicit permission of the copyright holder.' to the license reference or copy.
-
-B. To prohibit any publication of this work or derivative works in whole or in part in standard (paper) book form for commercial purposes is prohibited unless prior permission is obtained from the copyright holder.
-
-To accomplish this, add the phrase 'Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.' to the license reference or copy.
-
-OPEN PUBLICATION POLICY APPENDIX:
-
-(This is not considered part of the license.)
-
-Open Publication works are available in source format via the Open Publication home page at http://works.opencontent.org/.
-
-Open Publication authors who want to include their own license on Open Publication works may do so, as long as their terms are not more restrictive than the Open Publication license.
-
-If you have questions about the Open Publication License, please contact David Wiley, and/or the Open Publication Authors' List at opal@opencontent.org, via email.
-
-To subscribe to the Open Publication Authors' List:
-Send E-mail to opal-request@opencontent.org with the word "subscribe" in the body.
-
-To post to the Open Publication Authors' List:
-Send E-mail to opal@opencontent.org or simply reply to a previous post.
-
-To unsubscribe from the Open Publication Authors' List:
-Send E-mail to opal-request@opencontent.org with the word "unsubscribe" in the body.
diff --git a/options/license/OSET-PL-2.1 b/options/license/OSET-PL-2.1
deleted file mode 100644
index e0ed2e1398..0000000000
--- a/options/license/OSET-PL-2.1
+++ /dev/null
@@ -1,162 +0,0 @@
-OSET Public License
-(c) 2015 ALL RIGHTS RESERVED VERSION 2.1
-
-THIS LICENSE DEFINES THE RIGHTS OF USE, REPRODUCTION, DISTRIBUTION, MODIFICATION, AND REDISTRIBUTION OF CERTAIN COVERED SOFTWARE (AS DEFINED BELOW) ORIGINALLY RELEASED BY THE OPEN SOURCE ELECTION TECHNOLOGY FOUNDATION (FORMERLY “THE OSDV FOUNDATIONâ€). ANYONE WHO USES, REPRODUCES, DISTRIBUTES, MODIFIES, OR REDISTRIBUTES THE COVERED SOFTWARE, OR ANY PART THEREOF, IS BY THAT ACTION, ACCEPTING IN FULL THE TERMS CONTAINED IN THIS AGREEMENT. IF YOU DO NOT AGREE TO SUCH TERMS, YOU ARE NOT PERMITTED TO USE THE COVERED SOFTWARE.
-
-This license was prepared based on the Mozilla Public License (“MPLâ€), version 2.0. For annotation of the differences between this license and MPL 2.0, please see the OSET Foundation web site at www.OSETFoundation.org/public-license.
-
-The text of the license begins here:
-
-1. Definitions
-
- 1.1 “Contributor†means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software.
-
- 1.2 “Contributor Version†means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor’s Contribution.
-
- 1.3 “Contribution†means Covered Software of a particular Contributor.
-
- 1.4 “Covered Software†means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof.
-
- 1.5 “Incompatible With Secondary Licenses†means:
- a. That the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or
- b. that the Covered Software was made available under the terms of version 1.x or earlier of the License, but not also under the terms of a Secondary License.
-
- 1.6 “Executable Form†means any form of the work other than Source Code Form.
-
- 1.7 “Larger Work†means a work that combines Covered Software with other material, in a separate file (or files) that is not Covered Software.
-
- 1.8 “License†means this document.
-
- 1.9 “Licensable†means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License.
-
- 1.10 “Modifications†means any of the following:
- a. any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or
- b. any new file in Source Code Form that contains any Covered Software.
-
- 1.11 “Patent Claims†of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version.
-
- 1.12 “Secondary License†means one of: the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses.
-
- 1.13 “Source Code Form†means the form of the work preferred for making modifications.
-
- 1.14 “You†(or “Yourâ€) means an individual or a legal entity exercising rights under this License. For legal entities, “You†includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, “control†means: (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2. License Grants and Conditions
-
- 2.1 Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:
- a. under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and
- b. under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version.
-
- 2.2 Effective Date
- The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution.
-
- 2.3 Limitations on Grant Scope
- The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor:
- a. for any code that a Contributor has removed from Covered Software; or
- b. for infringements caused by: (i) Your and any other third party’s modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or
- c. under Patent Claims infringed by Covered Software in the absence of its Contributions.
- This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4).
-
- 2.4 Subsequent Licenses
- No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3).
-
- 2.5 Representation
- Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License.
-
- 2.6 Fair Use
- This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents.
-
- 2.7 Conditions
- Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1.
-
-3. Responsibilities
-
- 3.1 Distribution of Source Form
- All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You must cause any of Your Modifications to carry prominent notices stating that You changed the files. You may not attempt to alter or restrict the recipients’ rights in the Source Code Form.
-
- 3.2 Distribution of Executable Form
- If You distribute Covered Software in Executable Form then:
-
- a. such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and
- b. You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients’ rights in the Source Code Form under this License.
-
- 3.3 Distribution of a Larger Work
- You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s).
-
- 3.4 Notices
- You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies.
-
- 3.5 Application of Additional Terms
-
- 3.5.1 You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction.
-
- 3.5.2 You may place additional conditions upon the rights granted in this License to the extent necessary due to statute, judicial order, regulation (including without limitation state and federal procurement regulation), national security, or public interest. Any such additional conditions must be clearly described in the notice provisions required under Section 3.4. Any alteration of the terms of this License will apply to all copies of the Covered Software distributed by You or by any downstream recipients that receive the Covered Software from You.
-
-4. Inability to Comply Due to Statute or Regulation
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation, then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the notices required under Section 3.4. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Termination
-
- 5.1 Failure to Comply
- The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60-days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30-days after Your receipt of the notice.
-
- 5.2 Patent Infringement Claims
- If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate.
-
- 5.3 Additional Compliance Terms
- Notwithstanding the foregoing in this Section 5, for purposes of this Section, if You breach Section 3.1 (Distribution of Source Form), Section 3.2 (Distribution of Executable Form), Section 3.3 (Distribution of a Larger Work), or Section 3.4 (Notices), then becoming compliant as described in Section 5.1 must also include, no later than 30 days after receipt by You of notice of such violation by a Contributor, making the Covered Software available in Source Code Form as required by this License on a publicly available computer network for a period of no less than three (3) years.
-
- 5.4 Contributor Remedies
- If You fail to comply with the terms of this License and do not thereafter become compliant in accordance with Section 5.1 and, if applicable, Section 5.3, then each Contributor reserves its right, in addition to any other rights it may have in law or in equity, to bring an action seeking injunctive relief, or damages for willful copyright or patent infringement (including without limitation damages for unjust enrichment, where available under law), for all actions in violation of rights that would otherwise have been granted under the terms of this License.
-
- 5.5 End User License Agreements
- In the event of termination under this Section 5, all end user license agreements (excluding distributors and resellers), which have been validly granted by You or Your distributors under this License prior to termination shall survive termination.
-
-6. Disclaimer of Warranty
-Covered Software is provided under this License on an “as is†basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer.
-
-7. Limitation of Liability
-Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such party’s negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.
-
-8. Litigation
-Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party’s ability to bring cross-claims or counter-claims.
-
-9. Government Terms
-
- 9.1 Commercial Item
- The Covered Software is a “commercial item,†as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of “commercial computer software†and “commercial computer software documentation,†as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein.
-
- 9.2 No Sovereign Immunity
- The U.S. federal government and states that use or distribute Covered Software hereby waive their sovereign immunity with respect to enforcement of the provisions of this License.
-
- 9.3 Choice of Law and Venue
-
- 9.3.1 If You are a government of a state of the United States, or Your use of the Covered Software is pursuant to a procurement contract with such a state government, this License shall be governed by the law of such state, excluding its conflict-of-law provisions, and the adjudication of disputes relating to this License will be subject to the exclusive jurisdiction of the state and federal courts located in such state.
- 9.3.2 If You are an agency of the United States federal government, or Your use of the Covered Software is pursuant to a procurement contract with such an agency, this License shall be governed by federal law for all purposes, and the adjudication of disputes relating to this License will be subject to the exclusive jurisdiction of the federal courts located in Washington, D.C.
- 9.3.3 You may alter the terms of this Section 9.3 for this License as described in Section 3.5.2.
-
- 9.4 Supremacy
- This Section 9 is in lieu of, and supersedes, any other Federal Acquisition Regulation, Defense Federal Acquisition Regulation, or other clause or provision that addresses government rights in computer software under this License.
-
-10. Miscellaneous
-This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation, which provides that the language of a contract shall be construed against the drafter, shall not be used to construe this License against a Contributor.
-
-11. Versions of the License
-
- 11.1 New Versions The Open Source Election Technology Foundation (“OSETâ€) (formerly known as the Open Source Digital Voting Foundation) is the steward of this License. Except as provided in Section 11.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number.
-
- 11.2 Effects of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward.
-
- 11.3 Modified Versions If You create software not governed by this License, and You want to create a new license for such software, You may create and use a modified version of this License if You rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License).
-
- 11.4 Distributing Source Code Form That is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached.
-
-EXHIBIT A – Source Code Form License Notice
-
-This Source Code Form is subject to the terms of the OSET Public License, v.2.1 (“OSET-PL-2.1â€). If a copy of the OPL was not distributed with this file, You can obtain one at: www.OSETFoundation.org/public-license.
-
-If it is not possible or desirable to put the Notice in a particular file, then You may include the Notice in a location (e.g., such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership.
-
-EXHIBIT B - “Incompatible With Secondary License†Notice
-
-This Source Code Form is “Incompatible With Secondary Licensesâ€, as defined by the OSET Public License, v.2.1.
diff --git a/options/license/OSL-1.0 b/options/license/OSL-1.0
deleted file mode 100644
index 57ef0d058a..0000000000
--- a/options/license/OSL-1.0
+++ /dev/null
@@ -1,45 +0,0 @@
-The Open Software License v. 1.0
-
-This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
-
- "Licensed under the Open Software License version 1.0"
-
-License Terms
-
-1) Grant of Copyright License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, non-sublicenseable license to do the following:
-
- a) to reproduce the Original Work in copies;
-
- b) to prepare derivative works ("Derivative Works") based upon the Original Work;
-
- c) to distribute copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute shall be licensed under the Open Software License;
-
- d) to perform the Original Work publicly; and
-
- e) to display the Original Work publicly.
-
-2) Grant of Patent License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, non-sublicenseable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor ("Licensed Claims") to make, use, sell and offer for sale the Original Work. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, non-sublicenseable license under the Licensed Claims to make, use, sell and offer for sale Derivative Works.
-
-3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to access and modify the Original Work. Licensor hereby agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work, and by publishing the address of that information repository in a notice immediately following the copyright notice that applies to the Original Work.
-
-4) Exclusions From License Grant. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor except as expressly stated herein. No patent license is granted to make, use, sell or offer to sell embodiments of any patent claims other than the Licensed Claims defined in Section 2. No right is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any Original Work that Licensor otherwise would have a right to license.
-
-5) External Deployment. The term "External Deployment" means the use or distribution of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be accessed or used by anyone other than You, whether the Original Work or Derivative Works are distributed to those persons, made available as an application intended for use over a computer network, or used to provide services or otherwise deliver content to anyone other than You. As an express condition for the grants of license hereunder, You agree that any External Deployment by You shall be deemed a distribution and shall be licensed to all under the terms of this License, as prescribed in section 1(c) herein.
-
-6) Warranty and Disclaimer of Warranty. LICENSOR WARRANTS THAT THE COPYRIGHT IN AND TO THE ORIGINAL WORK IS OWNED BY THE LICENSOR OR THAT THE ORIGINAL WORK IS DISTRIBUTED BY LICENSOR UNDER A VALID CURRENT LICENSE FROM THE COPYRIGHT OWNER. EXCEPT AS EXPRESSLY STATED IN THE IMMEDIATELY PRECEEDING SENTENCE, THE ORIGINAL WORK IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, THE WARRANTY OF NON-INFRINGEMENT AND WARRANTIES THAT THE ORIGINAL WORK IS MERCHANTABLE OR FIT FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO LICENSE TO ORIGINAL WORK IS GRANTED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-7) Limitation of Liability. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE LICENSOR BE LIABLE TO ANY PERSON FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER ARISING AS A RESULT OF THIS LICENSE OR THE USE OF THE ORIGINAL WORK INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PERSON SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-8) Acceptance and Termination. Nothing else but this License (or another written agreement between Licensor and You) grants You permission to create Derivative Works based upon the Original Work, and any attempt to do so except under the terms of this License (or another written agreement between Licensor and You) is expressly prohibited by U.S. copyright law, the equivalent laws of other countries, and by international treaty. Therefore, by exercising any of the rights granted to You in Sections 1 and 2 herein, You indicate Your acceptance of this License and all of its terms and conditions. This license shall terminate immediately and you may no longer exercise any of the rights granted to You by this License upon Your failure to honor the proviso in Section 1(c) herein.
-
-9) Mutual Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License if You file a lawsuit in any court alleging that any OSI Certified open source software that is licensed under any license containing this "Mutual Termination for Patent Action" clause infringes any patent claims that are essential to use that software.
-
-10) Jurisdiction, Venue and Governing Law. You agree that any lawsuit arising under or relating to this License shall be maintained in the courts of the jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et seq., the equivalent laws of other countries, and international treaty. This section shall survive the termination of this License.
-
-11) Attorneys Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
-
-12) Miscellaneous. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
-
-13) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-This license is Copyright (C) 2002 Lawrence E. Rosen. All rights reserved. Permission is hereby granted to copy and distribute this license without modification. This license may not be modified without the express written permission of its copyright owner.
diff --git a/options/license/OSL-1.1 b/options/license/OSL-1.1
deleted file mode 100644
index 050f131fc2..0000000000
--- a/options/license/OSL-1.1
+++ /dev/null
@@ -1,47 +0,0 @@
-The Open Software License v. 1.1
-
-This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
-
- Licensed under the Open Software License version 1.1
-
-1) Grant of Copyright License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, non-sublicenseable license to do the following:
-
- a) to reproduce the Original Work in copies;
-
- b) to prepare derivative works ("Derivative Works") based upon the Original Work;
-
- c) to distribute copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute shall be licensed under the Open Software License;
-
- d) to perform the Original Work publicly; and
-
- e) to display the Original Work publicly.
-
-2) Grant of Patent License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, non-sublicenseable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor ("Licensed Claims") to make, use, sell and offer for sale the Original Work. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, non-sublicenseable license under the Licensed Claims to make, use, sell and offer for sale Derivative Works.
-
-3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor hereby agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work, and by publishing the address of that information repository in a notice immediately following the copyright notice that applies to the Original Work.
-
-4) Exclusions From License Grant. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor except as expressly stated herein. No patent license is granted to make, use, sell or offer to sell embodiments of any patent claims other than the Licensed Claims defined in Section 2. No right is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any Original Work that Licensor otherwise would have a right to license.
-
-5) External Deployment. The term "External Deployment" means the use or distribution of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether the Original Work or Derivative Works are distributed to those persons or made available as an application intended for use over a computer network. As an express condition for the grants of license hereunder, You agree that any External Deployment by You of a Derivative Work shall be deemed a distribution and shall be licensed to all under the terms of this License, as prescribed in section 1(c) herein.
-
-6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
-
-7) Warranty and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work is owned by the Licensor or that the Original Work is distributed by Licensor under a valid current license from the copyright owner. Except as expressly stated in the immediately proceeding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to Original Work is granted hereunder except under this disclaimer.
-
-8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to any person for any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to liability for death or personal injury resulting from Licensor's negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.
-
-9) Acceptance and Termination. If You distribute copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express and volitional assent of recipients to the terms of this License. Nothing else but this License (or another written agreement between Licensor and You) grants You permission to create Derivative Works based upon the Original Work or to exercise any of the rights granted in Sections 1 herein, and any attempt to do so except under the terms of this License (or another written agreement between Licensor and You) is expressly prohibited by U.S. copyright law, the equivalent laws of other countries, and by international treaty. Therefore, by exercising any of the rights granted to You in Sections 1 herein, You indicate Your acceptance of this License and all of its terms and conditions. This License shall terminate immediately and you may no longer exercise any of the rights granted to You by this License upon Your failure to honor the proviso in Section 1(c) herein.
-
-10) Mutual Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License if You file a lawsuit in any court alleging that any OSI Certified open source software that is licensed under any license containing this "Mutual Termination for Patent Action" clause infringes any patent claims that are essential to use that software.
-
-11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of the U.S. Copyright Act, 17 U.S.C. å¤ 101 et seq., the equivalent laws of other countries, and international treaty. This section shall survive the termination of this License.
-
-12) Attorneys Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
-
-13) Miscellaneous. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
-
-14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
-
-This license is Copyright (C) 2002 Lawrence E. Rosen. All rights reserved. Permission is hereby granted to copy and distribute this license without modification. This license may not be modified without the express written permission of its copyright owner.
diff --git a/options/license/OSL-2.0 b/options/license/OSL-2.0
deleted file mode 100644
index 78626fa53c..0000000000
--- a/options/license/OSL-2.0
+++ /dev/null
@@ -1,47 +0,0 @@
-Open Software License v. 2.0
-
-This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
-
- Licensed under the Open Software License version 2.0
-
-1) Grant of Copyright License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license to do the following:
-
- a) to reproduce the Original Work in copies;
-
- b) to prepare derivative works ("Derivative Works") based upon the Original Work;
-
- c) to distribute copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute shall be licensed under the Open Software License;
-
- d) to perform the Original Work publicly; and
-
- e) to display the Original Work publicly.
-
-2) Grant of Patent License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, to make, use, sell and offer for sale the Original Work and Derivative Works.
-
-3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor hereby agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work, and by publishing the address of that information repository in a notice immediately following the copyright notice that applies to the Original Work.
-
-4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior written permission of the Licensor. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor except as expressly stated herein. No patent license is granted to make, use, sell or offer to sell embodiments of any patent claims other than the licensed claims defined in Section 2. No right is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any Original Work that Licensor otherwise would have a right to license.
-
-5) External Deployment. The term "External Deployment" means the use or distribution of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether the Original Work or Derivative Works are distributed to those persons or made available as an application intended for use over a computer network. As an express condition for the grants of license hereunder, You agree that any External Deployment by You of a Derivative Work shall be deemed a distribution and shall be licensed to all under the terms of this License, as prescribed in section 1(c) herein.
-
-6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
-
-7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately proceeding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to Original Work is granted hereunder except under this disclaimer.
-
-8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to any person for any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to liability for death or personal injury resulting from Licensor's negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.
-
-9) Acceptance and Termination. If You distribute copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. Nothing else but this License (or another written agreement between Licensor and You) grants You permission to create Derivative Works based upon the Original Work or to exercise any of the rights granted in Section 1 herein, and any attempt to do so except under the terms of this License (or another written agreement between Licensor and You) is expressly prohibited by U.S. copyright law, the equivalent laws of other countries, and by international treaty. Therefore, by exercising any of the rights granted to You in Section 1 herein, You indicate Your acceptance of this License and all of its terms and conditions. This License shall terminate immediately and you may no longer exercise any of the rights granted to You by this License upon Your failure to honor the proviso in Section 1(c) herein.
-
-10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, for patent infringement (i) against Licensor with respect to a patent applicable to software or (ii) against any entity with respect to a patent applicable to the Original Work (but excluding combinations of the Original Work with other software or hardware).
-
-11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of the U.S. Copyright Act, 17 U.S.C. 101 et seq., the equivalent laws of other countries, and international treaty. This section shall survive the termination of this License.
-
-12) Attorneys Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
-
-13) Miscellaneous. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
-
-14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
-
-This license is Copyright (C) 2003 Lawrence E. Rosen. All rights reserved. Permission is hereby granted to copy and distribute this license without modification. This license may not be modified without the express written permission of its copyright owner.
diff --git a/options/license/OSL-2.1 b/options/license/OSL-2.1
deleted file mode 100644
index a2f08d176d..0000000000
--- a/options/license/OSL-2.1
+++ /dev/null
@@ -1,47 +0,0 @@
-The Open Software Licensev. 2.1
-
-This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
-
- Licensed under the Open Software License version 2.1
-
-1) Grant of Copyright License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license to do the following:
-
- a) to reproduce the Original Work in copies;
-
- b) to prepare derivative works ("Derivative Works") based upon the Original Work;
-
- c) to distribute copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute shall be licensed under the Open Software License;
-
- d) to perform the Original Work publicly; and
-
- e) to display the Original Work publicly.
-
-2) Grant of Patent License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, to make, use, sell and offer for sale the Original Work and Derivative Works.
-
-3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor hereby agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work, and by publishing the address of that information repository in a notice immediately following the copyright notice that applies to the Original Work.
-
-4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior written permission of the Licensor. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor except as expressly stated herein. No patent license is granted to make, use, sell or offer to sell embodiments of any patent claims other than the licensed claims defined in Section 2. No right is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any Original Work that Licensor otherwise would have a right to license.
-
-5) External Deployment. The term "External Deployment" means the use or distribution of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether the Original Work or Derivative Works are distributed to those persons or made available as an application intended for use over a computer network. As an express condition for the grants of license hereunder, You agree that any External Deployment by You of a Derivative Work shall be deemed a distribution and shall be licensed to all under the terms of this License, as prescribed in section 1(c) herein.
-
-6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
-
-7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately proceeding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to Original Work is granted hereunder except under this disclaimer.
-
-8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to any person for any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to liability for death or personal injury resulting from Licensor's negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.
-
-9) Acceptance and Termination. If You distribute copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. Nothing else but this License (or another written agreement between Licensor and You) grants You permission to create Derivative Works based upon the Original Work or to exercise any of the rights granted in Section 1 herein, and any attempt to do so except under the terms of this License (or another written agreement between Licensor and You) is expressly prohibited by U.S. copyright law, the equivalent laws of other countries, and by international treaty. Therefore, by exercising any of the rights granted to You in Section 1 herein, You indicate Your acceptance of this License and all of its terms and conditions. This License shall terminate immediately and you may no longer exercise any of the rights granted to You by this License upon Your failure to honor the proviso in Section 1(c) herein.
-
-10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
-
-11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et seq., the equivalent laws of other countries, and international treaty. This section shall survive the termination of this License.
-
-12) Attorneys Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
-
-13) Miscellaneous. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
-
-14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
-
-This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved. Permission is hereby granted to copy and distribute this license without modification. This license may not be modified without the express written permission of its copyright owner.
diff --git a/options/license/OpenJDK-assembly-exception-1.0 b/options/license/OpenJDK-assembly-exception-1.0
deleted file mode 100644
index 3a35f11ed2..0000000000
--- a/options/license/OpenJDK-assembly-exception-1.0
+++ /dev/null
@@ -1,31 +0,0 @@
-The OpenJDK source code made available by Oracle America, Inc.
-(Oracle) at openjdk.java.net ("OpenJDK Code") is distributed
-under the terms of the GNU General Public License
-<http://www.gnu.org/copyleft/gpl.html> version 2 only
-("GPL2"), with the following clarification and special
-exception.
-
-Linking this OpenJDK Code statically or dynamically with
-other code is making a combined work based on this
-library. Thus, the terms and conditions of GPL2 cover the
-whole combination.
-
-As a special exception, Oracle gives you permission to
-link this OpenJDK Code with certain code licensed by
-Oracle as indicated at
-http://openjdk.java.net/legal/exception-modules-2007-05-08.html
-("Designated Exception Modules") to produce an
-executable, regardless of the license terms of the
-Designated Exception Modules, and to copy and distribute
-the resulting executable under GPL2, provided that the
-Designated Exception Modules continue to be governed by
-the licenses under which they were offered by Oracle.
-
-As such, it allows licensees and sublicensees of Oracle's GPL2
-OpenJDK Code to build an executable that includes those
-portions of necessary code that Oracle could not provide under
-GPL2 (or that Oracle has provided under GPL2 with the Classpath
-exception). If you modify or add to the OpenJDK code, that new
-GPL2 code may still be combined with Designated Exception
-Modules if the new code is made subject to this exception by
-its copyright holder.
diff --git a/options/license/OpenPBS-2.3 b/options/license/OpenPBS-2.3
deleted file mode 100644
index 61f52c2202..0000000000
--- a/options/license/OpenPBS-2.3
+++ /dev/null
@@ -1,76 +0,0 @@
-
- OpenPBS (Portable Batch System) v2.3 Software License
-
-Copyright (c) 1999-2000 Veridian Information Solutions, Inc.
-All rights reserved.
-
----------------------------------------------------------------------------
-For a license to use or redistribute the OpenPBS software under conditions
-other than those described below, or to purchase support for this software,
-please contact Veridian Systems, PBS Products Department ("Licensor") at:
-
- www.OpenPBS.org +1 650 967-4675 sales@OpenPBS.org
- 877 902-4PBS (US toll-free)
----------------------------------------------------------------------------
-
-This license covers use of the OpenPBS v2.3 software (the "Software") at
-your site or location, and, for certain users, redistribution of the
-Software to other sites and locations. Use and redistribution of
-OpenPBS v2.3 in source and binary forms, with or without modification,
-are permitted provided that all of the following conditions are met.
-After December 31, 2001, only conditions 3-6 must be met:
-
-1. Commercial and/or non-commercial use of the Software is permitted
- provided a current software registration is on file at www.OpenPBS.org.
- If use of this software contributes to a publication, product, or
- service, proper attribution must be given; see www.OpenPBS.org/credit.html
-
-2. Redistribution in any form is only permitted for non-commercial,
- non-profit purposes. There can be no charge for the Software or any
- software incorporating the Software. Further, there can be no
- expectation of revenue generated as a consequence of redistributing
- the Software.
-
-3. Any Redistribution of source code must retain the above copyright notice
- and the acknowledgment contained in paragraph 6, this list of conditions
- and the disclaimer contained in paragraph 7.
-
-4. Any Redistribution in binary form must reproduce the above copyright
- notice and the acknowledgment contained in paragraph 6, this list of
- conditions and the disclaimer contained in paragraph 7 in the
- documentation and/or other materials provided with the distribution.
-
-5. Redistributions in any form must be accompanied by information on how to
- obtain complete source code for the OpenPBS software and any
- modifications and/or additions to the OpenPBS software. The source code
- must either be included in the distribution or be available for no more
- than the cost of distribution plus a nominal fee, and all modifications
- and additions to the Software must be freely redistributable by any party
- (including Licensor) without restriction.
-
-6. All advertising materials mentioning features or use of the Software must
- display the following acknowledgment:
-
- "This product includes software developed by NASA Ames Research Center,
- Lawrence Livermore National Laboratory, and Veridian Information Solutions,
- Inc. Visit www.OpenPBS.org for OpenPBS software support,
- products, and information."
-
-7. DISCLAIMER OF WARRANTY
-
-THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. ANY EXPRESS
-OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT
-ARE EXPRESSLY DISCLAIMED.
-
-IN NO EVENT SHALL VERIDIAN CORPORATION, ITS AFFILIATED COMPANIES, OR THE
-U.S. GOVERNMENT OR ANY OF ITS AGENCIES BE LIABLE FOR ANY DIRECT OR INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
-OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
-EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-This license will be governed by the laws of the Commonwealth of Virginia,
-without reference to its choice of law rules.
diff --git a/options/license/OpenSSL b/options/license/OpenSSL
deleted file mode 100644
index 3b5d232683..0000000000
--- a/options/license/OpenSSL
+++ /dev/null
@@ -1,48 +0,0 @@
-OpenSSL License
-
-Copyright (c) 1998-2008 The OpenSSL Project. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. All advertising materials mentioning features or use of this software must display the following acknowledgment: "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
-
-4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core@openssl.org.
-
-5. Products derived from this software may not be called "OpenSSL" nor may "OpenSSL" appear in their names without prior written permission of the OpenSSL Project.
-
-6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)"
-
-THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-This product includes cryptographic software written by Eric Young (eay@cryptsoft.com). This product includes software written by Tim Hudson (tjh@cryptsoft.com).
-
-
-Original SSLeay License
-
-Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
-
-This package is an SSL implementation written by Eric Young (eay@cryptsoft.com). The implementation was written so as to conform with Netscapes SSL.
-
-This library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh@cryptsoft.com).
-
-Copyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed. If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. All advertising materials mentioning features or use of this software must display the following acknowledgement:
-"This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)"
-The word 'cryptographic' can be left out if the rouines from the library being used are not cryptographic related :-).
-
-4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
-
-THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-The licence and distribution terms for any publically available version or derivative of this code cannot be changed. i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]
diff --git a/options/license/OpenSSL-standalone b/options/license/OpenSSL-standalone
deleted file mode 100644
index 82b14c736d..0000000000
--- a/options/license/OpenSSL-standalone
+++ /dev/null
@@ -1,50 +0,0 @@
-Copyright (c) 1998-2019 The OpenSSL Project. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
- 3. All advertising materials mentioning features or use of this
- software must display the following acknowledgment:
- "This product includes software developed by the OpenSSL Project
- for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
-
- 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
- endorse or promote products derived from this software without
- prior written permission. For written permission, please contact
- openssl-core@openssl.org.
-
- 5. Products derived from this software may not be called "OpenSSL"
- nor may "OpenSSL" appear in their names without prior written
- permission of the OpenSSL Project.
-
- 6. Redistributions of any form whatsoever must retain the following
- acknowledgment:
- "This product includes software developed by the OpenSSL Project
- for use in the OpenSSL Toolkit (http://www.openssl.org/)"
-
- THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
- EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
- ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- OF THE POSSIBILITY OF SUCH DAMAGE.
- ====================================================================
-
- This product includes cryptographic software written by Eric Young
- (eay@cryptsoft.com). This product includes software written by Tim
- Hudson (tjh@cryptsoft.com).
diff --git a/options/license/OpenVision b/options/license/OpenVision
deleted file mode 100644
index 983505389e..0000000000
--- a/options/license/OpenVision
+++ /dev/null
@@ -1,33 +0,0 @@
-Copyright, OpenVision Technologies, Inc., 1993-1996, All Rights
-Reserved
-
-WARNING: Retrieving the OpenVision Kerberos Administration system
-source code, as described below, indicates your acceptance of the
-following terms. If you do not agree to the following terms, do
-not retrieve the OpenVision Kerberos administration system.
-
-You may freely use and distribute the Source Code and Object Code
-compiled from it, with or without modification, but this Source
-Code is provided to you "AS IS" EXCLUSIVE OF ANY WARRANTY,
-INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY OR
-FITNESS FOR A PARTICULAR PURPOSE, OR ANY OTHER WARRANTY, WHETHER
-EXPRESS OR IMPLIED. IN NO EVENT WILL OPENVISION HAVE ANY LIABILITY
-FOR ANY LOST PROFITS, LOSS OF DATA OR COSTS OF PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES, OR FOR ANY SPECIAL, INDIRECT, OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, INCLUDING,
-WITHOUT LIMITATION, THOSE RESULTING FROM THE USE OF THE SOURCE
-CODE, OR THE FAILURE OF THE SOURCE CODE TO PERFORM, OR FOR ANY
-OTHER REASON.
-
-OpenVision retains all copyrights in the donated Source Code.
-OpenVision also retains copyright to derivative works of the Source
-Code, whether created by OpenVision or by a third party. The
-OpenVision copyright notice must be preserved if derivative works
-are made based on the donated Source Code.
-
-OpenVision Technologies, Inc. has donated this Kerberos
-Administration system to MIT for inclusion in the standard Kerberos
-5 distribution. This donation underscores our commitment to
-continuing Kerberos technology development and our gratitude for
-the valuable work which has been performed by MIT and the Kerberos
-community.
diff --git a/options/license/PADL b/options/license/PADL
deleted file mode 100644
index 84ba0b4db9..0000000000
--- a/options/license/PADL
+++ /dev/null
@@ -1,6 +0,0 @@
-Portions (C) Copyright PADL Software Pty Ltd. 1999
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that this notice is preserved
-and that due credit is given to PADL Software Pty Ltd. This software
-is provided ``as is'' without express or implied warranty.
diff --git a/options/license/PCRE2-exception b/options/license/PCRE2-exception
deleted file mode 100644
index eb7fd11767..0000000000
--- a/options/license/PCRE2-exception
+++ /dev/null
@@ -1,8 +0,0 @@
-EXEMPTION FOR BINARY LIBRARY-LIKE PACKAGES
-------------------------------------------
-
-The second condition in the BSD licence (covering binary redistributions) does
-not apply all the way down a chain of software. If binary package A includes
-PCRE2, it must respect the condition, but if package B is software that
-includes package A, the condition is not imposed on package B unless it uses
-PCRE2 independently.
diff --git a/options/license/PDDL-1.0 b/options/license/PDDL-1.0
deleted file mode 100644
index b5399f7c13..0000000000
--- a/options/license/PDDL-1.0
+++ /dev/null
@@ -1,136 +0,0 @@
-Open Data Commons Public Domain Dedication & License (PDDL)
-
-Preamble
-The Open Data Commons - Public Domain Dedication & Licence is a document intended to allow you to freely share, modify, and use this work for any purpose and without any restrictions. This licence is intended for use on databases or their contents ("data"), either together or individually.
-
-Many databases are covered by copyright. Some jurisdictions, mainly in Europe, have specific special rights that cover databases called the "sui generis" database right. Both of these sets of rights, as well as other legal rights used to protect databases and data, can create uncertainty or practical difficulty for those wishing to share databases and their underlying data but retain a limited amount of rights under a "some rights reserved" approach to licensing as outlined in the Science Commons Protocol for Implementing Open Access Data. As a result, this waiver and licence tries to the fullest extent possible to eliminate or fully license any rights that cover this database and data. Any Community Norms or similar statements of use of the database or data do not form a part of this document, and do not act as a contract for access or other terms of use for the database or data.
-
-The position of the recipient of the work
-
-Because this document places the database and its contents in or as close as possible within the public domain, there are no restrictions or requirements placed on the recipient by this document. Recipients may use this work commercially, use technical protection measures, combine this data or database with other databases or data, and share their changes and additions or keep them secret. It is not a requirement that recipients provide further users with a copy of this licence or attribute the original creator of the data or database as a source. The goal is to eliminate restrictions held by the original creator of the data and database on the use of it by others.
-
-The position of the dedicator of the work
-
-Copyright law, as with most other law under the banner of "intellectual property", is inherently national law. This means that there exists several differences in how copyright and other IP rights can be relinquished, waived or licensed in the many legal jurisdictions of the world. This is despite much harmonisation of minimum levels of protection. The internet and other communication technologies span these many disparate legal jurisdictions and thus pose special difficulties for a document relinquishing and waiving intellectual property rights, including copyright and database rights, for use by the global community. Because of this feature of intellectual property law, this document first relinquishes the rights and waives the relevant rights and claims. It then goes on to license these same rights for jurisdictions or areas of law that may make it difficult to relinquish or waive rights or claims.
-
-The purpose of this document is to enable rightsholders to place their work into the public domain. Unlike licences for free and open source software, free cultural works, or open content licences, rightsholders will not be able to "dual license" their work by releasing the same work under different licences. This is because they have allowed anyone to use the work in whatever way they choose. Rightsholders therefore can’t re-license it under copyright or database rights on different terms because they have nothing left to license. Doing so creates truly accessible data to build rich applications and advance the progress of science and the arts.
-
-This document can cover either or both of the database and its contents (the data). Because databases can have a wide variety of content - not just factual data - rightsholders should use the Open Data Commons - Public Domain Dedication & Licence for an entire database and its contents only if everything can be placed under the terms of this document. Because even factual data can sometimes have intellectual property rights, rightsholders should use this licence to cover both the database and its factual data when making material available under this document; even if it is likely that the data would not be covered by copyright or database rights.
-
-Rightsholders can also use this document to cover any copyright or database rights claims over only a database, and leave the contents to be covered by other licences or documents. They can do this because this document refers to the "Work", which can be either - or both - the database and its contents. As a result, rightsholders need to clearly state what they are dedicating under this document when they dedicate it.
-
-Just like any licence or other document dealing with intellectual property, rightsholders should be aware that one can only license what one owns. Please ensure that the rights have been cleared to make this material available under this document.
-
-This document permanently and irrevocably makes the Work available to the public for any use of any kind, and it should not be used unless the rightsholder is prepared for this to happen.
-
-Part I: Introduction
-
-The Rightsholder (the Person holding rights or claims over the Work) agrees as follows:
-
-1.0 Definitions of Capitalised Words
-
-"Copyright" - Includes rights under copyright and under neighbouring rights and similarly related sets of rights under the law of the relevant jurisdiction under Section 6.4.
-
-"Data" - The contents of the Database, which includes the information, independent works, or other material collected into the Database offered under the terms of this Document.
-
-"Database" - A collection of Data arranged in a systematic or methodical way and individually accessible by electronic or other means offered under the terms of this Document.
-
-"Database Right" - Means rights over Data resulting from the Chapter III ("sui generis") rights in the Database Directive (Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases) and any future updates as well as any similar rights available in the relevant jurisdiction under Section 6.4.
-
-"Document" - means this relinquishment and waiver of rights and claims and back up licence agreement.
-
-"Person" - Means a natural or legal person or a body of persons corporate or incorporate.
-
-"Use" - As a verb, means doing any act that is restricted by Copyright or Database Rights whether in the original medium or any other; and includes modifying the Work as may be technically necessary to use it in a different mode or format. This includes the right to sublicense the Work.
-
-"Work" - Means either or both of the Database and Data offered under the terms of this Document.
-
-"You" - the Person acquiring rights under the licence elements of this Document.
-
-Words in the singular include the plural and vice versa.
-
-2.0 What this document covers
-
-2.1. Legal effect of this Document. This Document is:
-
- a. A dedication to the public domain and waiver of Copyright and Database Rights over the Work; and
-
- b. A licence of Copyright and Database Rights over the Work in jurisdictions that do not allow for relinquishment or waiver.
-
-2.2. Legal rights covered.
-
- a. Copyright. Any copyright or neighbouring rights in the Work. Copyright law varies between jurisdictions, but is likely to cover: the Database model or schema, which is the structure, arrangement, and organisation of the Database, and can also include the Database tables and table indexes; the data entry and output sheets; and the Field names of Data stored in the Database. Copyright may also cover the Data depending on the jurisdiction and type of Data; and
-
- b. Database Rights. Database Rights only extend to the extraction and re-utilisation of the whole or a substantial part of the Data. Database Rights can apply even when there is no copyright over the Database. Database Rights can also apply when the Data is removed from the Database and is selected and arranged in a way that would not infringe any applicable copyright.
-
-2.2 Rights not covered.
-
- a. This Document does not apply to computer programs used in the making or operation of the Database;
-
- b. This Document does not cover any patents over the Data or the Database. Please see Section 4.2 later in this Document for further details; and
-
- c. This Document does not cover any trade marks associated with the Database. Please see Section 4.3 later in this Document for further details.
-
-Users of this Database are cautioned that they may have to clear other rights or consult other licences.
-
-2.3 Facts are free. The Rightsholder takes the position that factual information is not covered by Copyright. This Document however covers the Work in jurisdictions that may protect the factual information in the Work by Copyright, and to cover any information protected by Copyright that is contained in the Work.
-
-Part II: Dedication to the public domain
-
-3.0 Dedication, waiver, and licence of Copyright and Database Rights
-
-3.1 Dedication of Copyright and Database Rights to the public domain. The Rightsholder by using this Document, dedicates the Work to the public domain for the benefit of the public and relinquishes all rights in Copyright and Database Rights over the Work.
-
- a. The Rightsholder realises that once these rights are relinquished, that the Rightsholder has no further rights in Copyright and Database Rights over the Work, and that the Work is free and open for others to Use.
-
- b. The Rightsholder intends for their relinquishment to cover all present and future rights in the Work under Copyright and Database Rights, whether they are vested or contingent rights, and that this relinquishment of rights covers all their heirs and successors.
-
-The above relinquishment of rights applies worldwide and includes media and formats now known or created in the future.
-
-3.2 Waiver of rights and claims in Copyright and Database Rights when Section 3.1 dedication inapplicable. If the dedication in Section 3.1 does not apply in the relevant jurisdiction under Section 6.4, the Rightsholder waives any rights and claims that the Rightsholder may have or acquire in the future over the Work in:
-
- a. Copyright; and
-
- b. Database Rights.
-
-To the extent possible in the relevant jurisdiction, the above waiver of rights and claims applies worldwide and includes media and formats now known or created in the future. The Rightsholder agrees not to assert the above rights and waives the right to enforce them over the Work.
-
-3.3 Licence of Copyright and Database Rights when Sections 3.1 and 3.2 inapplicable. If the dedication and waiver in Sections 3.1 and 3.2 does not apply in the relevant jurisdiction under Section 6.4, the Rightsholder and You agree as follows:
-
- a. The Licensor grants to You a worldwide, royalty-free, non-exclusive, licence to Use the Work for the duration of any applicable Copyright and Database Rights. These rights explicitly include commercial use, and do not exclude any field of endeavour. To the extent possible in the relevant jurisdiction, these rights may be exercised in all media and formats whether now known or created in the future.
-
-3.4 Moral rights. This section covers moral rights, including the right to be identified as the author of the Work or to object to treatment that would otherwise prejudice the author’s honour and reputation, or any other derogatory treatment:
-
- a. For jurisdictions allowing waiver of moral rights, Licensor waives all moral rights that Licensor may have in the Work to the fullest extent possible by the law of the relevant jurisdiction under Section 6.4;
-
- b. If waiver of moral rights under Section 3.4 a in the relevant jurisdiction is not possible, Licensor agrees not to assert any moral rights over the Work and waives all claims in moral rights to the fullest extent possible by the law of the relevant jurisdiction under Section 6.4; and
-
- c. For jurisdictions not allowing waiver or an agreement not to assert moral rights under Section 3.4 a and b, the author may retain their moral rights over the copyrighted aspects of the Work.
-
-Please note that some jurisdictions do not allow for the waiver of moral rights, and so moral rights may still subsist over the work in some jurisdictions.
-
-4.0 Relationship to other rights
-
-4.1 No other contractual conditions. The Rightsholder makes this Work available to You without any other contractual obligations, either express or implied. Any Community Norms statement associated with the Work is not a contract and does not form part of this Document.
-
-4.2 Relationship to patents. This Document does not grant You a licence for any patents that the Rightsholder may own. Users of this Database are cautioned that they may have to clear other rights or consult other licences.
-
-4.3 Relationship to trade marks. This Document does not grant You a licence for any trade marks that the Rightsholder may own or that the Rightsholder may use to cover the Work. Users of this Database are cautioned that they may have to clear other rights or consult other licences. Part III: General provisions
-
-5.0 Warranties, disclaimer, and limitation of liability
-
-5.1 The Work is provided by the Rightsholder "as is" and without any warranty of any kind, either express or implied, whether of title, of accuracy or completeness, of the presence of absence of errors, of fitness for purpose, or otherwise. Some jurisdictions do not allow the exclusion of implied warranties, so this exclusion may not apply to You.
-
-5.2 Subject to any liability that may not be excluded or limited by law, the Rightsholder is not liable for, and expressly excludes, all liability for loss or damage however and whenever caused to anyone by any use under this Document, whether by You or by anyone else, and whether caused by any fault on the part of the Rightsholder or not. This exclusion of liability includes, but is not limited to, any special, incidental, consequential, punitive, or exemplary damages. This exclusion applies even if the Rightsholder has been advised of the possibility of such damages.
-
-5.3 If liability may not be excluded by law, it is limited to actual and direct financial loss to the extent it is caused by proved negligence on the part of the Rightsholder.
-
-6.0 General
-
-6.1 If any provision of this Document is held to be invalid or unenforceable, that must not affect the validity or enforceability of the remainder of the terms of this Document.
-
-6.2 This Document is the entire agreement between the parties with respect to the Work covered here. It replaces any earlier understandings, agreements or representations with respect to the Work not specified here.
-
-6.3 This Document does not affect any rights that You or anyone else may independently have under any applicable law to make any use of this Work, including (for jurisdictions where this Document is a licence) fair dealing, fair use, database exceptions, or any other legally recognised limitation or exception to infringement of copyright or other applicable laws.
-
-6.4 This Document takes effect in the relevant jurisdiction in which the Document terms are sought to be enforced. If the rights waived or granted under applicable law in the relevant jurisdiction includes additional rights not waived or granted under this Document, these additional rights are included in this Document in order to meet the intent of this Document.
diff --git a/options/license/PHP-3.0 b/options/license/PHP-3.0
deleted file mode 100644
index d6e8ae61a7..0000000000
--- a/options/license/PHP-3.0
+++ /dev/null
@@ -1,28 +0,0 @@
-The PHP License, version 3.0
-
-Copyright (c) 1999 - 2006 The PHP Group. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. The name "PHP" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact group@php.net.
-
-4. Products derived from this software may not be called "PHP", nor may "PHP" appear in their name, without prior written permission from group@php.net. You may indicate that your software works in conjunction with PHP by saying "Foo for PHP" instead of calling it "PHP Foo" or "phpfoo"
-
-5. The PHP Group may publish revised and/or new versions of the license from time to time. Each version will be given a distinguishing version number. Once covered code has been published under a particular version of the license, you may always continue to use it under the terms of that version. You may also choose to use such covered code under the terms of any subsequent version of the license published by the PHP Group. No one other than the PHP Group has the right to modify the terms applicable to covered code created under this License.
-
-6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes PHP, freely available from <http://www.php.net/>".
-
-THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-This software consists of voluntary contributions made by many individuals on behalf of the PHP Group.
-
-The PHP Group can be contacted via Email at group@php.net.
-
-For more information on the PHP Group and the PHP project, please see <http://www.php.net>.
-
-This product includes the Zend Engine, freely available at <http://www.zend.com>.
diff --git a/options/license/PHP-3.01 b/options/license/PHP-3.01
deleted file mode 100644
index 6ffc95218f..0000000000
--- a/options/license/PHP-3.01
+++ /dev/null
@@ -1,27 +0,0 @@
-The PHP License, version 3.01
-
-Copyright (c) 1999 - 2012 The PHP Group. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. The name "PHP" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact group@php.net.
-
-4. Products derived from this software may not be called "PHP", nor may "PHP" appear in their name, without prior written permission from group@php.net. You may indicate that your software works in conjunction with PHP by saying "Foo for PHP" instead of calling it "PHP Foo" or "phpfoo"
-
-5. The PHP Group may publish revised and/or new versions of the license from time to time. Each version will be given a distinguishing version number. Once covered code has been published under a particular version of the license, you may always continue to use it under the terms of that version. You may also choose to use such covered code under the terms of any subsequent version of the license published by the PHP Group. No one other than the PHP Group has the right to modify the terms applicable to covered code created under this License.
-
-6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes PHP software, freely available from <http://www.php.net/software/>".
-
-THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-This software consists of voluntary contributions made by many individuals on behalf of the PHP Group.
-
-The PHP Group can be contacted via Email at group@php.net.
-
-For more information on the PHP Group and the PHP project, please see <http://www.php.net>.
-
-PHP includes the Zend Engine, freely available at <http://www.zend.com>.
diff --git a/options/license/PPL b/options/license/PPL
deleted file mode 100644
index 013303699e..0000000000
--- a/options/license/PPL
+++ /dev/null
@@ -1,96 +0,0 @@
-Peer Production License
-
-Created by John Magyar, B.A., J.D. and Dmytri Kleiner, the following Peer Production License, a model for a Copyfarleft license, has been derived from the Creative Commons ‘Attribution-NonCommercial-ShareAlike' license available at http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode.
-
-LICENSE
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS COPYFARLEFT PUBLIC LICENSE ("LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND ALL OTHER APPLICABLE LAWS. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED IN THIS LICENSE, YOU AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN AS CONSIDERATION FOR ACCEPTING THE TERMS AND CONDITIONS OF THIS LICENSE AND FOR AGREEING TO BE BOUND BY THE TERMS AND CONDITIONS OF THIS LICENSE.
-
-1. DEFINITIONS
-
- a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
-
- b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
-
- c. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale, gift or any other transfer of possession or ownership.
-
- d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
-
- e. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
-
- f. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
-
- g. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-
- h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
-
- i. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
-
-2. FAIR DEALING RIGHTS
-Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
-
-3. LICENSE GRANT
-Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
- a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
-
- b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
-
- c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
-
- d. to Distribute and Publicly Perform Adaptations. The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Section 4(f).
-
-4. RESTRICTIONS
-The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
- a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(d), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(d), as requested.
-
- b. Subject to the exception in Section 4(c), you may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
-
- c. You may exercise the rights granted in Section 3 for commercial purposes only if:
-
- i. You are a worker-owned business or worker-owned collective; and
-
- ii. all financial gain, surplus, profits and benefits produced by the business or collective are distributed among the worker-owners
-
- d. Any use by a business that is privately owned and managed, and that seeks to generate profit from the labor of employees paid by salary or other wages, is not permitted under this license.
-
- e. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, (iv) consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(d) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
-
- f. For the avoidance of doubt:
-
- i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-
- ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and,
-
- iii.Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b).
-
- g. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
-
-5. REPRESENTATIONS, WARRANTIES AND DISCLAIMER
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. LIMITATION ON LIABILITY
-
-EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. TERMINATION
-
- a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-
- b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. MISCELLANEOUS
-
- a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-
- b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-
- c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-
- d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-
- e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-
- f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
diff --git a/options/license/PS-or-PDF-font-exception-20170817 b/options/license/PS-or-PDF-font-exception-20170817
deleted file mode 100644
index cf22439fff..0000000000
--- a/options/license/PS-or-PDF-font-exception-20170817
+++ /dev/null
@@ -1,8 +0,0 @@
-The font and related files in this directory are distributed under the
-GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (see the file COPYING), with
-the following exemption:
-
-As a special exception, permission is granted to include these font
-programs in a Postscript or PDF file that consists of a document that
-contains text to be displayed or printed using this font, regardless
-of the conditions or license applying to the document itself.
diff --git a/options/license/PSF-2.0 b/options/license/PSF-2.0
deleted file mode 100644
index 8a38e525ca..0000000000
--- a/options/license/PSF-2.0
+++ /dev/null
@@ -1,47 +0,0 @@
-PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
-
-1. This LICENSE AGREEMENT is between the Python Software Foundation
-("PSF"), and the Individual or Organization ("Licensee") accessing and
-otherwise using this software ("Python") in source or binary form and
-its associated documentation.
-
-2. Subject to the terms and conditions of this License Agreement, PSF hereby
-grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
-analyze, test, perform and/or display publicly, prepare derivative works,
-distribute, and otherwise use Python alone or in any derivative version,
-provided, however, that PSF's License Agreement and PSF's notice of copyright,
-i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 Python Software Foundation;
-All Rights Reserved" are retained in Python alone or in any derivative version
-prepared by Licensee.
-
-3. In the event Licensee prepares a derivative work that is based on
-or incorporates Python or any part thereof, and wants to make
-the derivative work available to others as provided herein, then
-Licensee hereby agrees to include in any such work a brief summary of
-the changes made to Python.
-
-4. PSF is making Python available to Licensee on an "AS IS"
-basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
-FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
-A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
-OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-6. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-7. Nothing in this License Agreement shall be deemed to create any
-relationship of agency, partnership, or joint venture between PSF and
-Licensee. This License Agreement does not grant permission to use PSF
-trademarks or trade name in a trademark sense to endorse or promote
-products or services of Licensee, or any third party.
-
-8. By copying, installing or otherwise using Python, Licensee
-agrees to be bound by the terms and conditions of this License
-Agreement.
diff --git a/options/license/Parity-6.0.0 b/options/license/Parity-6.0.0
deleted file mode 100644
index a3bb2a623d..0000000000
--- a/options/license/Parity-6.0.0
+++ /dev/null
@@ -1,44 +0,0 @@
-The Parity Public License 6.0.0
-
-Contributor: Example, Inc.
-
-Source Code: https://example.com/sourcecode
-
-This license lets you use and share this software for free, as
-long as you contribute software you make with it. Specifically:
-
-If you follow the rules below, you may do everything with this
-software that would otherwise infringe either the contributor's
-copyright in it, any patent claim the contributor can license,
-or both.
-
-1. Contribute changes and additions you make to this software.
-
-2. If you combine this software with other software, contribute
- that other software.
-
-3. Contribute software you develop, deploy, monitor, or run with
- this software.
-
-4. Ensure everyone who gets a copy of this software from you, in
- source code or any other form, gets the text of this license
- and the contributor and source code lines above.
-
-5. Do not make any legal claim against anyone accusing this
- software, with or without changes, alone or with other
- software, of infringing any patent claim.
-
-To contribute software, publish all its source code, in the
-preferred form for making changes, through a freely accessible
-distribution system widely used for similar source code, and
-license contributions not already licensed to the public on terms
-as permissive as this license accordingly.
-
-You are excused for unknowingly breaking 1, 2, or 3 if you
-contribute as required, or stop doing anything requiring this
-license, within 30 days of learning you broke the rule.
-
-**As far as the law allows, this software comes as is, without
-any warranty, and the contributor will not be liable to anyone
-for any damages related to this software or this license, for any
-kind of legal claim.**
diff --git a/options/license/Parity-7.0.0 b/options/license/Parity-7.0.0
deleted file mode 100644
index e5e022061b..0000000000
--- a/options/license/Parity-7.0.0
+++ /dev/null
@@ -1,71 +0,0 @@
-# The Parity Public License 7.0.0
-
-Contributor: Artless Devices, LLC [US-CA]
-
-Source Code: https://github.com/licensezero/licensezero.com
-
-## Purpose
-
-This license allows you to use and share this software for free, but you have to share software that builds on it alike.
-
-## Agreement
-
-In order to receive this license, you have to agree to its rules. Those rules are both obligations under that agreement and conditions to your license. Don't do anything with this software that triggers a rule you can't or won't follow.
-
-## Notices
-
-Make sure everyone who gets a copy of any part of this software from you, with or without changes, also gets the text of this license and the contributor and source code lines above.
-
-## Copyleft
-
-[Contribute](#contribute) software you develop, operate, or analyze with this software, including changes or additions to this software. When in doubt, [contribute](#contribute).
-
-## Prototypes
-
-You don't have to [contribute](#contribute) any change, addition, or other software that meets all these criteria:
-
-1. You don't use it for more than thirty days.
-
-2. You don't share it outside the team developing it, other than for non-production user testing.
-
-3. You don't develop, operate, or analyze other software with it for anyone outside the team developing it.
-
-## Reverse Engineering
-
-You may use this software to operate and analyze software you can't [contribute](#contribute) in order to develop alternatives you can and do [contribute](#contribute).
-
-## Contribute
-
-To [contribute](#contribute) software:
-
-1. Publish all source code for the software in the preferred form for making changes through a freely accessible distribution system widely used for similar source code so the contributor and others can find and copy it.
-
-2. Make sure every part of the source code is available under this license or another license that allows everything this license does, such as [the Blue Oak Model License 1.0.0](https://blueoakcouncil.org/license/1.0.0), [the Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html), [the MIT license](https://spdx.org/licenses/MIT.html), or [the two-clause BSD license](https://spdx.org/licenses/BSD-2-Clause.html).
-
-3. Take these steps within thirty days.
-
-4. Note that this license does _not_ allow you to change the license terms for this software. You must follow [Notices](#notices).
-
-## Excuse
-
-You're excused for unknowingly breaking [Copyleft](#copyleft) if you [contribute](#contribute) as required, or stop doing anything requiring this license, within thirty days of learning you broke the rule. You're excused for unknowingly breaking [Notices](#notices) if you take all practical steps to comply within thirty days of learning you broke the rule.
-
-## Defense
-
-Don't make any legal claim against anyone accusing this software, with or without changes, alone or with other technology, of infringing any patent.
-
-## Copyright
-
-The contributor licenses you to do everything with this software that would otherwise infringe their copyright in it.
-
-## Patent
-
-The contributor licenses you to do everything with this software that would otherwise infringe any patents they can license or become able to license.
-
-## Reliability
-
-The contributor can't revoke this license.
-
-## No Liability
-
-***As far as the law allows, this software comes as is, without any warranty or condition, and the contributor won't be liable to anyone for any damages related to this software or this license, under any kind of legal claim.***
diff --git a/options/license/Pixar b/options/license/Pixar
deleted file mode 100644
index c7533090bb..0000000000
--- a/options/license/Pixar
+++ /dev/null
@@ -1,174 +0,0 @@
-
- Modified Apache 2.0 License
-
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor
- and its affiliates, except as required to comply with Section 4(c) of
- the License and to reproduce the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
diff --git a/options/license/Plexus b/options/license/Plexus
deleted file mode 100644
index c92bc72454..0000000000
--- a/options/license/Plexus
+++ /dev/null
@@ -1,15 +0,0 @@
-Copyright 2002 (C) The Codehaus. All Rights Reserved.
-
-Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain copyright statements and notices. Redistributions must also contain a copy of this document.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. The name "classworlds†must not be used to endorse or promote products derived from this Software without prior written permission of The Codehaus. For written permission, please contact bob@codehaus.org.
-
-4. Products derived from this Software may not be called "classworlds†nor may "classworlds†appear in their names without prior written permission of The Codehaus. "classworlds†is a registered trademark of The Codehaus.
-
-5. Due credit should be given to The Codehaus. (http://classworlds.codehaus.org/).
-
-THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/PolyForm-Noncommercial-1.0.0 b/options/license/PolyForm-Noncommercial-1.0.0
deleted file mode 100644
index 1a71cb6439..0000000000
--- a/options/license/PolyForm-Noncommercial-1.0.0
+++ /dev/null
@@ -1,131 +0,0 @@
-# PolyForm Noncommercial License 1.0.0
-
-<https://polyformproject.org/licenses/noncommercial/1.0.0>
-
-## Acceptance
-
-In order to get any license under these terms, you must agree
-to them as both strict obligations and conditions to all
-your licenses.
-
-## Copyright License
-
-The licensor grants you a copyright license for the
-software to do everything you might do with the software
-that would otherwise infringe the licensor's copyright
-in it for any permitted purpose. However, you may
-only distribute the software according to [Distribution
-License](#distribution-license) and make changes or new works
-based on the software according to [Changes and New Works
-License](#changes-and-new-works-license).
-
-## Distribution License
-
-The licensor grants you an additional copyright license
-to distribute copies of the software. Your license
-to distribute covers distributing the software with
-changes and new works permitted by [Changes and New Works
-License](#changes-and-new-works-license).
-
-## Notices
-
-You must ensure that anyone who gets a copy of any part of
-the software from you also gets a copy of these terms or the
-URL for them above, as well as copies of any plain-text lines
-beginning with `Required Notice:` that the licensor provided
-with the software. For example:
-
-> Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
-
-## Changes and New Works License
-
-The licensor grants you an additional copyright license to
-make changes and new works based on the software for any
-permitted purpose.
-
-## Patent License
-
-The licensor grants you a patent license for the software that
-covers patent claims the licensor can license, or becomes able
-to license, that you would infringe by using the software.
-
-## Noncommercial Purposes
-
-Any noncommercial purpose is a permitted purpose.
-
-## Personal Uses
-
-Personal use for research, experiment, and testing for
-the benefit of public knowledge, personal study, private
-entertainment, hobby projects, amateur pursuits, or religious
-observance, without any anticipated commercial application,
-is use for a permitted purpose.
-
-## Noncommercial Organizations
-
-Use by any charitable organization, educational institution,
-public research organization, public safety or health
-organization, environmental protection organization,
-or government institution is use for a permitted purpose
-regardless of the source of funding or obligations resulting
-from the funding.
-
-## Fair Use
-
-You may have "fair use" rights for the software under the
-law. These terms do not limit them.
-
-## No Other Rights
-
-These terms do not allow you to sublicense or transfer any of
-your licenses to anyone else, or prevent the licensor from
-granting licenses to anyone else. These terms do not imply
-any other licenses.
-
-## Patent Defense
-
-If you make any written claim that the software infringes or
-contributes to infringement of any patent, your patent license
-for the software granted under these terms ends immediately. If
-your company makes such a claim, your patent license ends
-immediately for work on behalf of your company.
-
-## Violations
-
-The first time you are notified in writing that you have
-violated any of these terms, or done anything with the software
-not covered by your licenses, your licenses can nonetheless
-continue if you come into full compliance with these terms,
-and take practical steps to correct past violations, within
-32 days of receiving notice. Otherwise, all your licenses
-end immediately.
-
-## No Liability
-
-***As far as the law allows, the software comes as is, without
-any warranty or condition, and the licensor will not be liable
-to you for any damages arising out of these terms or the use
-or nature of the software, under any kind of legal claim.***
-
-## Definitions
-
-The **licensor** is the individual or entity offering these
-terms, and the **software** is the software the licensor makes
-available under these terms.
-
-**You** refers to the individual or entity agreeing to these
-terms.
-
-**Your company** is any legal entity, sole proprietorship,
-or other kind of organization that you work for, plus all
-organizations that have control over, are under the control of,
-or are under common control with that organization. **Control**
-means ownership of substantially all the assets of an entity,
-or the power to direct its management and policies by vote,
-contract, or otherwise. Control can be direct or indirect.
-
-**Your licenses** are all the licenses granted to you for the
-software under these terms.
-
-**Use** means anything you do with the software requiring one
-of your licenses.
diff --git a/options/license/PolyForm-Small-Business-1.0.0 b/options/license/PolyForm-Small-Business-1.0.0
deleted file mode 100644
index 5b5790e04a..0000000000
--- a/options/license/PolyForm-Small-Business-1.0.0
+++ /dev/null
@@ -1,121 +0,0 @@
-# PolyForm Small Business License 1.0.0
-
-<https://polyformproject.org/licenses/small-business/1.0.0>
-
-## Acceptance
-
-In order to get any license under these terms, you must agree
-to them as both strict obligations and conditions to all
-your licenses.
-
-## Copyright License
-
-The licensor grants you a copyright license for the
-software to do everything you might do with the software
-that would otherwise infringe the licensor's copyright
-in it for any permitted purpose. However, you may
-only distribute the software according to [Distribution
-License](#distribution-license) and make changes or new works
-based on the software according to [Changes and New Works
-License](#changes-and-new-works-license).
-
-## Distribution License
-
-The licensor grants you an additional copyright license
-to distribute copies of the software. Your license
-to distribute covers distributing the software with
-changes and new works permitted by [Changes and New Works
-License](#changes-and-new-works-license).
-
-## Notices
-
-You must ensure that anyone who gets a copy of any part of
-the software from you also gets a copy of these terms or the
-URL for them above, as well as copies of any plain-text lines
-beginning with `Required Notice:` that the licensor provided
-with the software. For example:
-
-> Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
-
-## Changes and New Works License
-
-The licensor grants you an additional copyright license to
-make changes and new works based on the software for any
-permitted purpose.
-
-## Patent License
-
-The licensor grants you a patent license for the software that
-covers patent claims the licensor can license, or becomes able
-to license, that you would infringe by using the software.
-
-## Fair Use
-
-You may have "fair use" rights for the software under the
-law. These terms do not limit them.
-
-## Small Business
-
-Use of the software for the benefit of your company is use for
-a permitted purpose if your company has fewer than 100 total
-individuals working as employees and independent contractors,
-and less than 1,000,000 USD (2019) total revenue in the prior
-tax year. Adjust this revenue threshold for inflation according
-to the United States Bureau of Labor Statistics' consumer price
-index for all urban consumers, U.S. city average, for all items,
-not seasonally adjusted, with 1982–1984=100 reference base.
-
-## No Other Rights
-
-These terms do not allow you to sublicense or transfer any of
-your licenses to anyone else, or prevent the licensor from
-granting licenses to anyone else. These terms do not imply
-any other licenses.
-
-## Patent Defense
-
-If you make any written claim that the software infringes or
-contributes to infringement of any patent, your patent license
-for the software granted under these terms ends immediately. If
-your company makes such a claim, your patent license ends
-immediately for work on behalf of your company.
-
-## Violations
-
-The first time you are notified in writing that you have
-violated any of these terms, or done anything with the software
-not covered by your licenses, your licenses can nonetheless
-continue if you come into full compliance with these terms,
-and take practical steps to correct past violations, within
-32 days of receiving notice. Otherwise, all your licenses
-end immediately.
-
-## No Liability
-
-***As far as the law allows, the software comes as is, without
-any warranty or condition, and the licensor will not be liable
-to you for any damages arising out of these terms or the use
-or nature of the software, under any kind of legal claim.***
-
-## Definitions
-
-The **licensor** is the individual or entity offering these
-terms, and the **software** is the software the licensor makes
-available under these terms.
-
-**You** refers to the individual or entity agreeing to these
-terms.
-
-**Your company** is any legal entity, sole proprietorship,
-or other kind of organization that you work for, plus all
-organizations that have control over, are under the control of,
-or are under common control with that organization. **Control**
-means ownership of substantially all the assets of an entity,
-or the power to direct its management and policies by vote,
-contract, or otherwise. Control can be direct or indirect.
-
-**Your licenses** are all the licenses granted to you for the
-software under these terms.
-
-**Use** means anything you do with the software requiring one
-of your licenses.
diff --git a/options/license/PostgreSQL b/options/license/PostgreSQL
deleted file mode 100644
index f5775a4c59..0000000000
--- a/options/license/PostgreSQL
+++ /dev/null
@@ -1,12 +0,0 @@
-PostgreSQL Database Management System
-(formerly known as Postgres, then as Postgres95)
-
-Portions Copyright (c) 1996-2010, The PostgreSQL Global Development Group
-
-Portions Copyright (c) 1994, The Regents of the University of California
-
-Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies.
-
-IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
diff --git a/options/license/Python-2.0 b/options/license/Python-2.0
deleted file mode 100644
index b212cb2cb2..0000000000
--- a/options/license/Python-2.0
+++ /dev/null
@@ -1,72 +0,0 @@
-PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
-
- 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated documentation.
-
- 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee.
-
- 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python.
-
- 4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
-
- 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
- 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
-
- 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party.
-
- 8. By copying, installing or otherwise using Python, Licensee agrees to be bound by the terms and conditions of this License Agreement.
-
-
-BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
-
-BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
-
- 1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the Individual or Organization ("Licensee") accessing and otherwise using this software in source or binary form and its associated documentation ("the Software").
-
- 2. Subject to the terms and conditions of this BeOpen Python License Agreement, BeOpen hereby grants Licensee a non-exclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use the Software alone or in any derivative version, provided, however, that the BeOpen Python License is retained in the Software, alone or in any derivative version prepared by Licensee.
-
- 3. BeOpen is making the Software available to Licensee on an "AS IS" basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
-
- 4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
- 5. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
-
- 6. This License Agreement shall be governed by and interpreted in all respects by the law of the State of California, excluding conflict of law provisions. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between BeOpen and Licensee. This License Agreement does not grant permission to use BeOpen trademarks or trade names in a trademark sense to endorse or promote products or services of Licensee, or any third party. As an exception, the "BeOpen Python" logos available at http://www.pythonlabs.com/logos.html may be used according to the permissions granted on that web page.
-
- 7. By copying, installing or otherwise using the software, Licensee agrees to be bound by the terms and conditions of this License Agreement.
-
-
-CNRI OPEN SOURCE LICENSE AGREEMENT (for Python 1.6b1)
-
-IMPORTANT: PLEASE READ THE FOLLOWING AGREEMENT CAREFULLY.
-
-BY CLICKING ON "ACCEPT" WHERE INDICATED BELOW, OR BY COPYING, INSTALLING OR OTHERWISE USING PYTHON 1.6, beta 1 SOFTWARE, YOU ARE DEEMED TO HAVE AGREED TO THE TERMS AND CONDITIONS OF THIS LICENSE AGREEMENT.
-
- 1. This LICENSE AGREEMENT is between the Corporation for National Research Initiatives, having an office at 1895 Preston White Drive, Reston, VA 20191 ("CNRI"), and the Individual or Organization ("Licensee") accessing and otherwise using Python 1.6, beta 1 software in source or binary form and its associated documentation, as released at the www.python.org Internet site on August 4, 2000 ("Python 1.6b1").
-
- 2. Subject to the terms and conditions of this License Agreement, CNRI hereby grants Licensee a non-exclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python 1.6b1 alone or in any derivative version, provided, however, that CNRIs License Agreement is retained in Python 1.6b1, alone or in any derivative version prepared by Licensee.
-
- Alternately, in lieu of CNRIs License Agreement, Licensee may substitute the following text (omitting the quotes): "Python 1.6, beta 1, is made available subject to the terms and conditions in CNRIs License Agreement. This Agreement may be located on the Internet using the following unique, persistent identifier (known as a handle): 1895.22/1011. This Agreement may also be obtained from a proxy server on the Internet using the URL:http://hdl.handle.net/1895.22/1011".
-
- 3. In the event Licensee prepares a derivative work that is based on or incorporates Python 1.6b1 or any part thereof, and wants to make the derivative work available to the public as provided herein, then Licensee hereby agrees to indicate in any such work the nature of the modifications made to Python 1.6b1.
-
- 4. CNRI is making Python 1.6b1 available to Licensee on an "AS IS" basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6b1 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
-
- 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF USING, MODIFYING OR DISTRIBUTING PYTHON 1.6b1, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
- 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
-
- 7. This License Agreement shall be governed by and interpreted in all respects by the law of the State of Virginia, excluding conflict of law provisions. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between CNRI and Licensee. This License Agreement does not grant permission to use CNRI trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party.
-
- 8. By clicking on the "ACCEPT" button where indicated, or by copying, installing or otherwise using Python 1.6b1, Licensee agrees to be bound by the terms and conditions of this License Agreement.
-
-ACCEPT
-
-
-CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
-
-Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, The Netherlands. All rights reserved.
-
- Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Stichting Mathematisch Centrum or CWI not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.
-
- STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/options/license/Python-2.0.1 b/options/license/Python-2.0.1
deleted file mode 100644
index 22f32578d4..0000000000
--- a/options/license/Python-2.0.1
+++ /dev/null
@@ -1,193 +0,0 @@
-PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
---------------------------------------------
-
-1. This LICENSE AGREEMENT is between the Python Software Foundation
-("PSF"), and the Individual or Organization ("Licensee") accessing and
-otherwise using this software ("Python") in source or binary form and
-its associated documentation.
-
-2. Subject to the terms and conditions of this License Agreement, PSF hereby
-grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
-analyze, test, perform and/or display publicly, prepare derivative works,
-distribute, and otherwise use Python alone or in any derivative version,
-provided, however, that PSF's License Agreement and PSF's notice of copyright,
-i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation;
-All Rights Reserved" are retained in Python alone or in any derivative version
-prepared by Licensee.
-
-3. In the event Licensee prepares a derivative work that is based on
-or incorporates Python or any part thereof, and wants to make
-the derivative work available to others as provided herein, then
-Licensee hereby agrees to include in any such work a brief summary of
-the changes made to Python.
-
-4. PSF is making Python available to Licensee on an "AS IS"
-basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
-FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
-A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
-OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-6. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-7. Nothing in this License Agreement shall be deemed to create any
-relationship of agency, partnership, or joint venture between PSF and
-Licensee. This License Agreement does not grant permission to use PSF
-trademarks or trade name in a trademark sense to endorse or promote
-products or services of Licensee, or any third party.
-
-8. By copying, installing or otherwise using Python, Licensee
-agrees to be bound by the terms and conditions of this License
-Agreement.
-
-
-BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
--------------------------------------------
-
-BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
-
-1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
-office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
-Individual or Organization ("Licensee") accessing and otherwise using
-this software in source or binary form and its associated
-documentation ("the Software").
-
-2. Subject to the terms and conditions of this BeOpen Python License
-Agreement, BeOpen hereby grants Licensee a non-exclusive,
-royalty-free, world-wide license to reproduce, analyze, test, perform
-and/or display publicly, prepare derivative works, distribute, and
-otherwise use the Software alone or in any derivative version,
-provided, however, that the BeOpen Python License is retained in the
-Software, alone or in any derivative version prepared by Licensee.
-
-3. BeOpen is making the Software available to Licensee on an "AS IS"
-basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
-SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
-AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
-DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-5. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-6. This License Agreement shall be governed by and interpreted in all
-respects by the law of the State of California, excluding conflict of
-law provisions. Nothing in this License Agreement shall be deemed to
-create any relationship of agency, partnership, or joint venture
-between BeOpen and Licensee. This License Agreement does not grant
-permission to use BeOpen trademarks or trade names in a trademark
-sense to endorse or promote products or services of Licensee, or any
-third party. As an exception, the "BeOpen Python" logos available at
-http://www.pythonlabs.com/logos.html may be used according to the
-permissions granted on that web page.
-
-7. By copying, installing or otherwise using the software, Licensee
-agrees to be bound by the terms and conditions of this License
-Agreement.
-
-
-CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
----------------------------------------
-
-1. This LICENSE AGREEMENT is between the Corporation for National
-Research Initiatives, having an office at 1895 Preston White Drive,
-Reston, VA 20191 ("CNRI"), and the Individual or Organization
-("Licensee") accessing and otherwise using Python 1.6.1 software in
-source or binary form and its associated documentation.
-
-2. Subject to the terms and conditions of this License Agreement, CNRI
-hereby grants Licensee a nonexclusive, royalty-free, world-wide
-license to reproduce, analyze, test, perform and/or display publicly,
-prepare derivative works, distribute, and otherwise use Python 1.6.1
-alone or in any derivative version, provided, however, that CNRI's
-License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
-1995-2001 Corporation for National Research Initiatives; All Rights
-Reserved" are retained in Python 1.6.1 alone or in any derivative
-version prepared by Licensee. Alternately, in lieu of CNRI's License
-Agreement, Licensee may substitute the following text (omitting the
-quotes): "Python 1.6.1 is made available subject to the terms and
-conditions in CNRI's License Agreement. This Agreement together with
-Python 1.6.1 may be located on the internet using the following
-unique, persistent identifier (known as a handle): 1895.22/1013. This
-Agreement may also be obtained from a proxy server on the internet
-using the following URL: http://hdl.handle.net/1895.22/1013".
-
-3. In the event Licensee prepares a derivative work that is based on
-or incorporates Python 1.6.1 or any part thereof, and wants to make
-the derivative work available to others as provided herein, then
-Licensee hereby agrees to include in any such work a brief summary of
-the changes made to Python 1.6.1.
-
-4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
-basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
-1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
-A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
-OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-6. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-7. This License Agreement shall be governed by the federal
-intellectual property law of the United States, including without
-limitation the federal copyright law, and, to the extent such
-U.S. federal law does not apply, by the law of the Commonwealth of
-Virginia, excluding Virginia's conflict of law provisions.
-Notwithstanding the foregoing, with regard to derivative works based
-on Python 1.6.1 that incorporate non-separable material that was
-previously distributed under the GNU General Public License (GPL), the
-law of the Commonwealth of Virginia shall govern this License
-Agreement only as to issues arising under or with respect to
-Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
-License Agreement shall be deemed to create any relationship of
-agency, partnership, or joint venture between CNRI and Licensee. This
-License Agreement does not grant permission to use CNRI trademarks or
-trade name in a trademark sense to endorse or promote products or
-services of Licensee, or any third party.
-
-8. By clicking on the "ACCEPT" button where indicated, or by copying,
-installing or otherwise using Python 1.6.1, Licensee agrees to be
-bound by the terms and conditions of this License Agreement.
-
- ACCEPT
-
-
-CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
---------------------------------------------------
-
-Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
-The Netherlands. All rights reserved.
-
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose and without fee is hereby granted,
-provided that the above copyright notice appear in all copies and that
-both that copyright notice and this permission notice appear in
-supporting documentation, and that the name of Stichting Mathematisch
-Centrum or CWI not be used in advertising or publicity pertaining to
-distribution of the software without specific, written prior
-permission.
-
-STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
-THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
-FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
-FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/options/license/QPL-1.0 b/options/license/QPL-1.0
deleted file mode 100644
index 64c35da10a..0000000000
--- a/options/license/QPL-1.0
+++ /dev/null
@@ -1,50 +0,0 @@
-THE Q PUBLIC LICENSE version 1.0
-
-Copyright (C) 1999-2005 Trolltech AS, Norway.
-
-Everyone is permitted to copy and distribute this license document.
-
-The intent of this license is to establish freedom to share and change the software regulated by this license under the open source model.
-
-This license applies to any software containing a notice placed by the copyright holder saying that it may be distributed under the terms of the Q Public License version 1.0. Such software is herein referred to as the Software. This license covers modification and distribution of the Software, use of third-party application programs based on the Software, and development of free software which uses the Software.
-
-Granted Rights
-
-1. You are granted the non-exclusive rights set forth in this license provided you agree to and comply with any and all conditions in this license. Whole or partial distribution of the Software, or software items that link with the Software, in any form signifies acceptance of this license.
-
-2. You may copy and distribute the Software in unmodified form provided that the entire package, including - but not restricted to - copyright, trademark notices and disclaimers, as released by the initial developer of the Software, is distributed.
-
-3. You may make modifications to the Software and distribute your modifications, in a form that is separate from the Software, such as patches. The following restrictions apply to modifications:
-
-a. Modifications must not alter or remove any copyright notices in the Software.
-b. When modifications to the Software are released under this license, a non-exclusive royalty-free right is granted to the initial developer of the Software to distribute your modification in future versions of the Software provided such versions remain available under these terms in addition to any other license(s) of the initial developer.
-
-4. You may distribute machine-executable forms of the Software or machine-executable forms of modified versions of the Software, provided that you meet these restrictions:
-
- a. You must include this license document in the distribution.
-
- b. You must ensure that all recipients of the machine-executable forms are also able to receive the complete machine-readable source code to the distributed Software, including all modifications, without any charge beyond the costs of data transfer, and place prominent notices in the distribution explaining this.
-
- c. You must ensure that all modifications included in the machine-executable forms are available under the terms of this license.
-
-5. You may use the original or modified versions of the Software to compile, link and run application programs legally developed by you or by others.
-
-6. You may develop application programs, reusable components and other software items that link with the original or modified versions of the Software. These items, when distributed, are subject to the following requirements:
-
- a. You must ensure that all recipients of machine-executable forms of these items are also able to receive and use the complete machine-readable source code to the items without any charge beyond the costs of data transfer.
-
- b. You must explicitly license all recipients of your items to use and re-distribute original and modified versions of the items in both machine-executable and source code forms. The recipients must be able to do so without any charges whatsoever, and they must be able to re-distribute to anyone they choose.
-
- c. If the items are not available to the general public, and the initial developer of the Software requests a copy of the items, then you must supply one.
-
-Limitations of Liability
-
-In no event shall the initial developers or copyright holders be liable for any damages whatsoever, including - but not restricted to - lost revenue or profits or other direct, indirect, special, incidental or consequential damages, even if they have been advised of the possibility of such damages, except to the extent invariable law, if any, provides otherwise.
-
-No Warranty
-
-The Software and this license document are provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-Choice of Law
-
-This license is governed by the Laws of Norway. Disputes shall be settled by Oslo City Court.
diff --git a/options/license/QPL-1.0-INRIA-2004 b/options/license/QPL-1.0-INRIA-2004
deleted file mode 100644
index 45d946e2e2..0000000000
--- a/options/license/QPL-1.0-INRIA-2004
+++ /dev/null
@@ -1,102 +0,0 @@
- THE Q PUBLIC LICENSE version 1.0
-
- Copyright (C) 1999 Troll Tech AS, Norway.
- Everyone is permitted to copy and
- distribute this license document.
-
-The intent of this license is to establish freedom to share and change
-the software regulated by this license under the open source model.
-
-This license applies to any software containing a notice placed by the
-copyright holder saying that it may be distributed under the terms of
-the Q Public License version 1.0. Such software is herein referred to
-as the Software. This license covers modification and distribution of
-the Software, use of third-party application programs based on the
-Software, and development of free software which uses the Software.
-
- Granted Rights
-
-1. You are granted the non-exclusive rights set forth in this license
-provided you agree to and comply with any and all conditions in this
-license. Whole or partial distribution of the Software, or software
-items that link with the Software, in any form signifies acceptance of
-this license.
-
-2. You may copy and distribute the Software in unmodified form
-provided that the entire package, including - but not restricted to -
-copyright, trademark notices and disclaimers, as released by the
-initial developer of the Software, is distributed.
-
-3. You may make modifications to the Software and distribute your
-modifications, in a form that is separate from the Software, such as
-patches. The following restrictions apply to modifications:
-
- a. Modifications must not alter or remove any copyright notices
- in the Software.
-
- b. When modifications to the Software are released under this
- license, a non-exclusive royalty-free right is granted to the
- initial developer of the Software to distribute your
- modification in future versions of the Software provided such
- versions remain available under these terms in addition to any
- other license(s) of the initial developer.
-
-4. You may distribute machine-executable forms of the Software or
-machine-executable forms of modified versions of the Software,
-provided that you meet these restrictions:
-
- a. You must include this license document in the distribution.
-
- b. You must ensure that all recipients of the machine-executable
- forms are also able to receive the complete machine-readable
- source code to the distributed Software, including all
- modifications, without any charge beyond the costs of data
- transfer, and place prominent notices in the distribution
- explaining this.
-
- c. You must ensure that all modifications included in the
- machine-executable forms are available under the terms of this
- license.
-
-5. You may use the original or modified versions of the Software to
-compile, link and run application programs legally developed by you or
-by others.
-
-6. You may develop application programs, reusable components and other
-software items that link with the original or modified versions of the
-Software. These items, when distributed, are subject to the following
-requirements:
-
- a. You must ensure that all recipients of machine-executable
- forms of these items are also able to receive and use the
- complete machine-readable source code to the items without any
- charge beyond the costs of data transfer.
-
- b. You must explicitly license all recipients of your items to
- use and re-distribute original and modified versions of the
- items in both machine-executable and source code forms. The
- recipients must be able to do so without any charges whatsoever,
- and they must be able to re-distribute to anyone they choose.
-
- c. If the items are not available to the general public, and the
- initial developer of the Software requests a copy of the items,
- then you must supply one.
-
- Limitations of Liability
-
-In no event shall the initial developers or copyright holders be
-liable for any damages whatsoever, including - but not restricted to -
-lost revenue or profits or other direct, indirect, special, incidental
-or consequential damages, even if they have been advised of the
-possibility of such damages, except to the extent invariable law, if
-any, provides otherwise.
-
- No Warranty
-
-The Software and this license document are provided AS IS with NO
-WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN,
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
- Choice of Law
-
-This license is governed by the Laws of France.
diff --git a/options/license/QPL-1.0-INRIA-2004-exception b/options/license/QPL-1.0-INRIA-2004-exception
deleted file mode 100644
index 2418a26377..0000000000
--- a/options/license/QPL-1.0-INRIA-2004-exception
+++ /dev/null
@@ -1,5 +0,0 @@
-As a special exception to the Q Public Licence, you may develop
-application programs, reusable components and other software items
-that link with the original or modified versions of the Software
-and are not made available to the general public, without any of the
-additional requirements listed in clause 6c of the Q Public licence.
diff --git a/options/license/Qhull b/options/license/Qhull
deleted file mode 100644
index 0c795af167..0000000000
--- a/options/license/Qhull
+++ /dev/null
@@ -1,17 +0,0 @@
-Qhull, Copyright (c) 1993-2003
-
-The National Science and Technology Research Center for Computation and Visualization of Geometric Structures (The Geometry Center) University of Minnesota
-
-email: qhull@qhull.org
-
-This software includes Qhull from The Geometry Center. Qhull is copyrighted as noted above. Qhull is free software and may be obtained via http from www.qhull.org. It may be freely copied, modified, and redistributed under the following conditions:
-
-1. All copyright notices must remain intact in all files.
-
-2. A copy of this text file must be distributed along with any copies of Qhull that you redistribute; this includes copies that you have modified, or copies of programs or other software products that include Qhull.
-
-3. If you modify Qhull, you must include a notice giving the name of the person performing the modification, the date of modification, and the reason for such modification.
-
-4. When distributing modified versions of Qhull, or other software products that include Qhull, you must provide notice that the original source code may be obtained as noted above.
-
-5. There is no warranty or other guarantee of fitness for Qhull, it is provided solely "as is". Bug reports or fixes may be sent to qhull_bug@qhull.org; the authors may or may not act on them as they desire.
diff --git a/options/license/Qt-GPL-exception-1.0 b/options/license/Qt-GPL-exception-1.0
deleted file mode 100644
index 761d0327a4..0000000000
--- a/options/license/Qt-GPL-exception-1.0
+++ /dev/null
@@ -1,21 +0,0 @@
-The Qt Company GPL Exception 1.0
-
-Exception 1:
-
-As a special exception you may create a larger work which contains the
-output of this application and distribute that work under terms of your
-choice, so long as the work is not otherwise derived from or based on
-this application and so long as the work does not in itself generate
-output that contains the output from this application in its original
-or modified form.
-
-Exception 2:
-
-As a special exception, you have permission to combine this application
-with Plugins licensed under the terms of your choice, to produce an
-executable, and to copy and distribute the resulting executable under
-the terms of your choice. However, the executable must be accompanied
-by a prominent notice offering all users of the executable the entire
-source code to this application, excluding the source code of the
-independent modules, but including any changes you have made to this
-application, under the terms of this license.
diff --git a/options/license/Qt-LGPL-exception-1.1 b/options/license/Qt-LGPL-exception-1.1
deleted file mode 100644
index bd94b5538f..0000000000
--- a/options/license/Qt-LGPL-exception-1.1
+++ /dev/null
@@ -1,22 +0,0 @@
-The Qt Company Qt LGPL Exception version 1.1
-
-As an additional permission to the GNU Lesser General Public License version
-2.1, the object code form of a "work that uses the Library" may incorporate
-material from a header file that is part of the Library. You may distribute
-such object code under terms of your choice, provided that:
- (i) the header files of the Library have not been modified; and
- (ii) the incorporated material is limited to numerical parameters, data
- structure layouts, accessors, macros, inline functions and
- templates; and
- (iii) you comply with the terms of Section 6 of the GNU Lesser General
- Public License version 2.1.
-
-Moreover, you may apply this exception to a modified version of the Library,
-provided that such modification does not involve copying material from the
-Library into the modified Library's header files unless such material is
-limited to (i) numerical parameters; (ii) data structure layouts;
-(iii) accessors; and (iv) small macros, templates and inline functions of
-five lines or less in length.
-
-Furthermore, you are not required to apply this additional permission to a
-modified version of the Library.
diff --git a/options/license/Qwt-exception-1.0 b/options/license/Qwt-exception-1.0
deleted file mode 100644
index b45cdd0b54..0000000000
--- a/options/license/Qwt-exception-1.0
+++ /dev/null
@@ -1,12 +0,0 @@
-Qwt License Version 1.0,
-January 1, 2003
-
-The Qwt library and included programs are provided under the terms of the GNU LESSER GENERAL PUBLIC LICENSE (LGPL) with the following exceptions:
-
-1. Widgets that are subclassed from Qwt widgets do not constitute a derivative work.
-
-2. Static linking of applications and widgets to the Qwt library does not constitute a derivative work and does not require the author to provide source code for the application or widget, use the shared Qwt libraries, or link their applications or widgets against a user-supplied version of Qwt. If you link the application or widget to a modified version of Qwt, then the changes to Qwt must be provided under the terms of the LGPL in sections 1, 2, and 4.
-
-3. You do not have to provide a copy of the Qwt license with programs that are linked to the Qwt library, nor do you have to identify the Qwt license in your program or documentation as required by section 6 of the LGPL.
-
-However, programs must still identify their use of Qwt. The following example statement can be included in user documentation to satisfy this requirement: [program/widget] is based in part on the work of the Qwt project (http://qwt.sf.net)."
diff --git a/options/license/RHeCos-1.1 b/options/license/RHeCos-1.1
deleted file mode 100644
index db5350bae6..0000000000
--- a/options/license/RHeCos-1.1
+++ /dev/null
@@ -1,137 +0,0 @@
-Red Hat eCos Public License v1.1
-
-1. DEFINITIONS
-
- 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications.
-
- 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
- 1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
- 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
- 1.5. "Executable" means Covered Code in any form other than Source Code.
-
- 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
- 1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.8. "License" means this document.
-
- 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
-
- A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
-
- B. Any new file that contains any part of the Original Code or previous Modifications.
-
- 1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
- 1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or a list of source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
- 1.12. "You" means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
- 1.13. "Red Hat Branded Code" is code that Red Hat distributes and/or permits others to distribute under different terms than the Red Hat eCos Public License. Red Hat's Branded Code may contain part or all of the Covered Code.
-
-2. SOURCE CODE LICENSE
-
- 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, or as part of a Larger Work; and
-
- (b) under patents now or hereafter owned or controlled by Initial Developer, to make, have made, use and sell ("Utilize") the Original Code (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Original Code (or portions thereof) and not to any greater extent that may be necessary to Utilize further Modifications or combinations.
-
- 2.2. Contributor Grant. Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code or as part of a Larger Work; and
-
- (b) under patents now or hereafter owned or controlled by Contributor, to Utilize the Contributor Version (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Contributor Version (or portions thereof), and not to any greater extent that may be necessary to Utilize further Modifications or combinations.
-
-3. DISTRIBUTION OBLIGATIONS
-
- 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
- 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available and to the Initial Developer; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. You are responsible for notifying the Initial Developer of the Modification and the location of the Source if a contact means is provided. Red Hat will be acting as maintainer of the Source and may provide an Electronic Distribution mechanism for the Modification to be made available. You can contact Red Hat to make the Modification available and to notify the Initial Developer. (http://sourceware.cygnus.com/ecos/)
-
- 3.3. Description of Modifications. You must cause all Covered Code to which you contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
- 3.4. Intellectual Property Matters
-
- (a) Third Party Claims. If You have knowledge that a party claims an intellectual property right in particular functionality or code (or its utilization under this License), you must include a text file with the source code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after You make Your Modification available as described in Section 3.2, You shall promptly modify the LEGAL file in all copies You make available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
-
- (b) Contributor APIs. If Your Modification is an application programming interface and You own or control patents which are reasonably necessary to implement that API, you must also include this information in the LEGAL file.
-
- 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code, and this License in any documentation for the Source Code, where You describe recipients' rights relating to Covered Code. If You created one or more Modification(s), You may add your name as a Contributor to the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then you must include such notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code.
-
- However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
- 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
- If you distribute executable versions containing Covered Code, you must reproduce the notice in Exhibit B in the documentation and/or other materials provided with the product.
-
- 3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. INABILITY TO COMPLY DUE TO STATUTE OR REGULATION
-
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; (b) cite the statute or regulation that prohibits you from adhering to the license; and (c) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. You must submit this LEGAL file to Red Hat for review, and You will not be able use the covered code in any means until permission is granted from Red Hat to allow for the inability to comply due to statute or regulation.
-
-5. APPLICATION OF THIS LICENSE
-
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A, and to related Covered Code.
-
-Red Hat may include Covered Code in products without such additional products becoming subject to the terms of this License, and may license such additional products on different terms from those contained in this License.
-
-Red Hat may license the Source Code of Red Hat Branded Code without Red Hat Branded Code becoming subject to the terms of this License, and may license Red Hat Branded Code on different terms from those contained in this License. Contact Red Hat for details of alternate licensing terms available.
-
-6. VERSIONS OF THE LICENSE
-
- 6.1. New Versions. Red Hat may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
- 6.2. Effect of New Versions. Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Red Hat. No one other than Red Hat has the right to modify the terms applicable to Covered Code beyond what is granted under this and subsequent Licenses.
-
- 6.3. Derivative Works. If you create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), you must (a) rename Your license so that the phrases "ECOS", "eCos", "Red Hat", "RHEPL" or any confusingly similar phrase do not appear anywhere in your license and (b) otherwise make it clear that your version of the license contains terms which differ from the Red Hat eCos Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
-7. DISCLAIMER OF WARRANTY
-
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION
-
-This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-9. LIMITATION OF LIABILITY
-
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO YOU OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS
-
-The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
-11. MISCELLANEOUS
-
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in, the United States of America: (a) unless otherwise agreed in writing, all disputes relating to this License (excepting any dispute relating to intellectual property rights) shall be subject to final and binding arbitration, with the losing party paying all costs of arbitration; (b) any arbitration relating to this Agreement shall be held in Santa Clara County, California, under the auspices of JAMS/EndDispute; and (c) any litigation relating to this Agreement shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-12. RESPONSIBILITY FOR CLAIMS
-
-Except in cases where another Contributor has failed to comply with Section 3.4, You are responsible for damages arising, directly or indirectly, out of Your utilization of rights under this License, based on the number of copies of Covered Code you made available, the revenues you received from utilizing such rights, and other relevant factors. You agree to work with affected parties to distribute responsibility on an equitable basis.
-
-13. ADDITIONAL TERMS APPLICABLE TO THE RED HAT ECOS PUBLIC LICENSE
-
-Nothing in this License shall be interpreted to prohibit Red Hat from licensing under different terms than this License any code which Red Hat otherwise would have a right to license.
-
-Red Hat and logo - This License does not grant any rights to use the trademark Red Hat, the Red Hat logo, eCos logo, even if such marks are included in the Original Code. You may contact Red Hat for permission to display the Red Hat and eCos marks in either the documentation or the Executable version beyond that required in Exhibit B.
-
-Inability to Comply Due to Contractual Obligation - To the extent that Red Hat is limited contractually from making third party code available under this License, Red Hat may choose to integrate such third party code into Covered Code without being required to distribute such third party code in Source Code form, even if such third party code would otherwise be considered "Modifications" under this License.
-
-EXHIBIT A
-
-"The contents of this file are subject to the Red Hat eCos Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.redhat.com/
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-The Original Code is eCos - Embedded Configurable Operating System, released September 30, 1998. The Initial Developer of the Original Code is Red Hat. Portions created by Red Hat are Copyright (C) 1998, 1999, 2000 Red Hat, Inc. All Rights Reserved."
-
-EXHIBIT B
-
-Part of the software embedded in this product is eCos - Embedded Configurable Operating System, a trademark of Red Hat. Portions created by Red Hat are Copyright (C) 1998, 1999, 2000 Red Hat, Inc. (http://www.redhat.com/). All Rights Reserved.
-
-THE SOFTWARE IN THIS PRODUCT WAS IN PART PROVIDED BY RED HAT AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/RPL-1.1 b/options/license/RPL-1.1
deleted file mode 100644
index 8db27a884c..0000000000
--- a/options/license/RPL-1.1
+++ /dev/null
@@ -1,177 +0,0 @@
-Reciprocal Public License, version 1.1
-
-Copyright (C) 2001-2002 Technical Pursuit Inc., All Rights Reserved.
-
-PREAMBLE
-
-This Preamble is intended to describe, in plain English, the nature, intent, and scope of this License. However, this Preamble is not a part of this License. The legal effect of this License is dependent only upon the terms of the License and not this Preamble.
-
-This License is based on the concept of reciprocity. In exchange for being granted certain rights under the terms of this License to Licensor's Software, whose Source Code You have access to, You are required to reciprocate by providing equal access and rights to all third parties to the Source Code of any Modifications, Derivative Works, and Required Components for execution of same (collectively defined as Extensions) that You Deploy by Deploying Your Extensions under the terms of this License. In this fashion the available Source Code related to the original Licensed Software is enlarged for the benefit of everyone.
-
-Under the terms of this License You may:
-
-a. Distribute the Licensed Software exactly as You received it under the terms of this License either alone or as a component of an aggregate software distribution containing programs from several different sources without payment of a royalty or other fee.
-
-b. Use the Licensed Software for any purpose consistent with the rights granted by this License, but the Licensor is not providing You any warranty whatsoever, nor is the Licensor accepting any liability in the event that the Licensed Software doesn't work properly or causes You any injury or damages.
-
-c. Create Extensions to the Licensed Software consistent with the rights granted by this License, provided that You make the Source Code to any Extensions You Deploy available to all third parties under the terms of this License, document Your Modifications clearly, and title all Extensions distinctly from the Licensed Software.
-
-d. Charge a fee for warranty or support, or for accepting indemnity or liability obligations for Your customers.
-
-Under the terms of this License You may not:
-
-a. Charge for the Source Code to the Licensed Software, or Your Extensions, other than a nominal fee not to exceed Your cost for reproduction and distribution where such reproduction and distribution involve physical media.
-
-b. Modify or delete any pre-existing copyright notices, change notices, or License text in the Licensed Software.
-
-c. Assert any patent claims against the Licensor or Contributors, or which would in any way restrict the ability of any third party to use the Licensed Software or portions thereof in any form under the terms of this License, or Your rights to the Licensed Software under this License automatically terminate.
-
-d. Represent either expressly or by implication, appearance, or otherwise that You represent Licensor or
-
-Contributors in any capacity or that You have any form of legal association by virtue of this License.
-
-Under the terms of this License You must:
-
-a. Document any Modifications You make to the Licensed Software including the nature of the change, the authors of the change, and the date of the change. This documentation must appear both in the Source Code and in a text file titled "CHANGES" distributed with the Licensed Software and Your Extensions.
-
-b. Make the Source Code for any Extensions You Deploy available in a timely fashion via an Electronic Distribution Mechanism such as FTP or HTTP download.
-
-c. Notify the Licensor of the availability of Source Code to Your Extensions in a timely fashion and include in such notice a brief description of the Extensions, the distinctive title used, and instructions on how to acquire the Source Code and future updates.
-
-d. Grant Licensor and all third parties a world-wide, non-exclusive, royalty-free license under any intellectual property rights owned or controlled by You to use, reproduce, display, perform, modify, sublicense, and distribute Your Extensions, in any form, under the terms of this License.
-
-LICENSE TERMS
-
-1.0 General; Applicability & Definitions. This Reciprocal Public License Version 1.1 ("License") applies to any programs or other works as well as any and all updates or maintenance releases of said programs or works ("Software") not already covered by this License which the Software copyright holder ("Licensor") makes publicly available containing a Notice (hereinafter defined) from the Licensor specifying or allowing use or distribution under the terms of this License. As used in this License and Preamble:
-
- 1.1 "Contributor" means any person or entity who created or contributed to the creation of an Extension.
-
- 1.2 "Deploy" means to use, Serve, sublicense or distribute Licensed Software other than for Your internal Research and/or Personal Use, and includes without limitation, any and all internal use or distribution of Licensed Software within Your business or organization other than for Research and/or Personal Use, as well as direct or indirect sublicensing or distribution of Licensed Software by You to any third party in any form or manner.
-
- 1.3 "Derivative Works" as used in this License is defined under U.S. copyright law.
-
- 1.4 "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data such as download from an FTP or web site, where such mechanism is publicly accessible.
-
- 1.5 "Extensions" means any Modifications, Derivative Works, or Required Components as those terms are defined in this License.
-
- 1.6 "License" means this Reciprocal Public License.
-
- 1.7 "Licensed Software" means any Software licensed pursuant to this License. Licensed Software also includes all previous Extensions from any Contributor that You receive.
-
- 1.8 "Licensor" means the copyright holder of any Software previously uncovered by this License who releases the Software under the terms of this License.
-
- 1.9 "Modifications" means any additions to or deletions from the substance or structure of (i) a file or other storage containing Licensed Software, or (ii) any new file or storage that contains any part of Licensed Software, or (iii) any file or storage which replaces or otherwise alters the original functionality of Licensed Software at runtime.
-
- 1.10 "Notice" means the notice contained in EXHIBIT A.
-
- 1.11 "Personal Use" means use of Licensed Software by an individual solely for his or her personal, private and non-commercial purposes. An individual's use of Licensed Software in his or her capacity as an officer, employee, member, independent contractor or agent of a corporation, business or organization (commercial or non-commercial) does not qualify as Personal Use.
-
- 1.12 "Required Components" means any text, programs, scripts, schema, interface definitions, control files, or other works created by You which are required by a third party of average skill to successfully install and run Licensed Software containing Your Modifications, or to install and run Your Derivative Works.
-
- 1.13 "Research" means investigation or experimentation for the purpose of understanding the nature and limits of the Licensed Software and its potential uses.
-
- 1.14 "Serve" means to deliver Licensed Software and/or Your Extensions by means of a computer network to one or more computers for purposes of execution of Licensed Software and/or Your Extensions.
-
- 1.15 "Software" means any computer programs or other works as well as any updates or maintenance releases of those programs or works which are distributed publicly by Licensor.
-
- 1.16 "Source Code" means the preferred form for making modifications to the Licensed Software and/or Your Extensions, including all modules contained therein, plus any associated text, interface definition files, scripts used to control compilation and installation of an executable program or other components required by a third party of average skill to build a running version of the Licensed Software or Your Extensions.
-
- 1.17 "You" or "Your" means an individual or a legal entity exercising rights under this License. For legal entities, "You" or "Your" includes any entity which controls, is controlled by, or is under common control with, You, where "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
-2.0 Acceptance Of License. You are not required to accept this License since you have not signed it, however nothing else grants you permission to use, copy, distribute, modify, or create derivatives of either the Software or any Extensions created by a Contributor. These actions are prohibited by law if you do not accept this License. Therefore, by performing any of these actions You indicate Your acceptance of this License and Your agreement to be bound by all its terms and conditions. IF YOU DO NOT AGREE WITH ALL THE TERMS AND CONDITIONS OF THIS LICENSE DO NOT USE, MODIFY, CREATE DERIVATIVES, OR DISTRIBUTE THE SOFTWARE. IF IT IS IMPOSSIBLE FOR YOU TO COMPLY WITH ALL THE TERMS AND CONDITIONS OF THIS LICENSE THEN YOU CAN NOT USE, MODIFY, CREATE DERIVATIVES, OR DISTRIBUTE THE SOFTWARE.
-
-3.0 Grant of License From Licensor. Subject to the terms and conditions of this License, Licensor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to Licensor's intellectual property rights, and any third party intellectual property claims derived from the Licensed Software under this License, to do the following:
-
- 3.1 Use, reproduce, modify, display, perform, sublicense and distribute Licensed Software and Your Extensions in both Source Code form or as an executable program.
-
- 3.2 Create Derivative Works (as that term is defined under U.S. copyright law) of Licensed Software by adding to or deleting from the substance or structure of said Licensed Software.
-
- 3.3 Under claims of patents now or hereafter owned or controlled by Licensor, to make, use, have made, and/or otherwise dispose of Licensed Software or portions thereof, but solely to the extent that any such claim is necessary to enable You to make, use, have made, and/or otherwise dispose of Licensed Software or portions thereof.
-
- 3.4 Licensor reserves the right to release new versions of the Software with different features, specifications, capabilities, functions, licensing terms, general availability or other characteristics. Title, ownership rights, and intellectual property rights in and to the Licensed Software shall remain in Licensor and/or its Contributors.
-
-4.0 Grant of License From Contributor. By application of the provisions in Section 6 below, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to said Contributor's intellectual property rights, and any third party intellectual property claims derived from the Licensed Software under this License, to do the following:
-
- 4.1 Use, reproduce, modify, display, perform, sublicense and distribute any Extensions Deployed by such Contributor or portions thereof, in both Source Code form or as an executable program, either on an unmodified basis or as part of Derivative Works.
-
- 4.2 Under claims of patents now or hereafter owned or controlled by Contributor, to make, use, have made, and/or otherwise dispose of Extensions or portions thereof, but solely to the extent that any such claim is necessary to enable You to make, use, have made, and/or otherwise dispose of Contributor's Extensions or portions thereof.
-
-5.0 Exclusions From License Grant. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor or any Contributor except as expressly stated herein. Except as expressly stated in Sections 3 and 4, no other patent rights, express or implied, are granted herein. Your Extensions may require additional patent licenses from Licensor or Contributors which each may grant in its sole discretion. No right is granted to the trademarks of Licensor or any Contributor even if such marks are included in the Licensed Software. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any code that Licensor otherwise would have a right to license.
-
- 5.1 You expressly acknowledge and agree that although Licensor and each Contributor grants the licenses to their respective portions of the Licensed Software set forth herein, no assurances are provided by Licensor or any Contributor that the Licensed Software does not infringe the patent or other intellectual property rights of any other entity. Licensor and each Contributor disclaim any liability to You for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, You hereby assume sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow You to distribute the Licensed Software, it is Your responsibility to acquire that license before distributing the Licensed Software.
-
-6.0 Your Obligations And Grants. In consideration of, and as an express condition to, the licenses granted to You under this License You hereby agree that any Modifications, Derivative Works, or Required Components (collectively Extensions) that You create or to which You contribute are governed by the terms of this License including, without limitation, Section 4. Any Extensions that You create or to which You contribute must be Deployed under the terms of this License or a future version of this License released under Section 7. You hereby grant to Licensor and all third parties a world-wide, non-exclusive, royalty-free license under those intellectual property rights You own or control to use, reproduce, display, perform, modify, create derivatives, sublicense, and distribute Your Extensions, in any form. Any Extensions You make and Deploy must have a distinct title so as to readily tell any subsequent user or Contributor that the Extensions are by You. You must include a copy of this License with every copy of the Extensions You distribute. You agree not to offer or impose any terms on any Source Code or executable version of the Licensed Software, or its Extensions that alter or restrict the applicable version of this License or the recipients' rights hereunder.
-
- 6.1 Availability of Source Code. You must make available, under the terms of this License, the Source Code of the Licensed Software and any Extensions that You Deploy, either on the same media as You distribute any executable or other form of the Licensed Software, or via an Electronic Distribution Mechanism. The Source Code for any version of Licensed Software, or its Extensions that You Deploy must be made available at the time of Deployment and must remain available for as long as You Deploy the Extensions or at least twelve (12) months after the date You Deploy, whichever is longer. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. You may not charge a fee for the Source Code distributed under this Section in excess of Your actual cost of duplication and distribution where such duplication and distribution involve physical media.
-
- 6.2 Description of Modifications. You must cause any Modifications that You create or to which You contribute, to update the file titled "CHANGES" distributed with Licensed Software documenting the additions, changes or deletions You made, the authors of such Modifications, and the dates of any such additions, changes or deletions. You must also cause a cross-reference to appear in the Source Code at the location of each change. You must include a prominent statement that the Modifications are derived, directly or indirectly, from the Licensed Software and include the names of the Licensor and any Contributor to the Licensed Software in (i) the Source Code and (ii) in any notice displayed by the Licensed Software You distribute or in related documentation in which You describe the origin or ownership of the Licensed Software. You may not modify or delete any pre-existing copyright notices, change notices or License text in the Licensed Software.
-
- 6.3 Intellectual Property Matters.
-
- a. Third Party Claims. If You have knowledge that a license to a third party's intellectual property right is required to exercise the rights granted by this License, You must include a text file with the Source Code distribution titled "LEGAL" that describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If You obtain such knowledge after You make any Extensions available as described in Section 6.1, You shall promptly modify the LEGAL file in all copies You make available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Licensed Software from You that new knowledge has been obtained.
-
- b. Contributor APIs. If Your Extensions include an application programming interface ("API") and You have knowledge of patent licenses that are reasonably necessary to implement that API, You must also include this information in the LEGAL file.
-
- c. Representations. You represent that, except as disclosed pursuant to 6.3(a) above, You believe that any Extensions You distribute are Your original creations and that You have sufficient rights to grant the rights conveyed by this License.
-
- 6.4 Required Notices.
-
- a. License Text. You must duplicate this License in any documentation You provide along with the Source Code of any Extensions You create or to which You contribute, wherever You describe recipients' rights relating to Licensed Software. You must duplicate the notice contained in EXHIBIT A (the "Notice") in each file of the Source Code of any copy You distribute of the Licensed Software and Your Extensions. If You create an Extension, You may add Your name as a Contributor to the text file titled "CONTRIB" distributed with the Licensed Software along with a description of the contribution. If it is not possible to put the Notice in a particular Source Code file due to its structure, then You must include such Notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice.
-
- b. Source Code Availability. You must notify Licensor within one (1) month of the date You initially Deploy of the availability of Source Code to Your Extensions and include in such notification the name under which you Deployed Your Extensions, a description of the Extensions, and instructions on how to acquire the Source Code, including instructions on how to acquire updates over time. Should such instructions change you must provide Licensor with revised instructions within one (1) month of the date of change. Should you be unable to notify Licensor directly, you must provide notification by posting to appropriate news groups, mailing lists, or web sites where a search engine would reasonably be expected to index them.
-
- 6.5 Additional Terms. You may choose to offer, and charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Licensed Software. However, You may do so only on Your own behalf, and not on behalf of the Licensor or any Contributor. You must make it clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Licensor and every Contributor for any liability plus attorney fees, costs, and related expenses due to any such action or claim incurred by the Licensor or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
- 6.6 Conflicts With Other Licenses. Where any portion of Your Extensions, by virtue of being Derivative Works of another product or similar circumstance, fall under the terms of another license, the terms of that license should be honored however You must also make Your Extensions available under this License. If the terms of this License continue to conflict with the terms of the other license you may write the Licensor for permission to resolve the conflict in a fashion that remains consistent with the intent of this License. Such permission will be granted at the sole discretion of the Licensor.
-
-7.0 Versions of This License. Licensor may publish from time to time revised and/or new versions of the License. Once Licensed Software has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Licensed Software under the terms of any subsequent version of the License published by Licensor. No one other than Licensor has the right to modify the terms applicable to Licensed Software created under this License.
-
- 7.1 If You create or use a modified version of this License, which You may do only in order to apply it to software that is not already Licensed Software under this License, You must rename Your license so that it is not confusingly similar to this License, and must make it clear that Your license contains terms that differ from this License. In so naming Your license, You may not use any trademark of Licensor or of any Contributor. Should Your modifications to this License be limited to alteration of EXHIBIT A purely for purposes of adjusting the Notice You require of licensees, You may continue to refer to Your License as the Reciprocal Public License or simply the RPL.
-
-8.0 Disclaimer of Warranty. LICENSED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE LICENSED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. FURTHER THERE IS NO WARRANTY MADE AND ALL IMPLIED WARRANTIES ARE DISCLAIMED THAT THE LICENSED SOFTWARE MEETS OR COMPLIES WITH ANY DESCRIPTION OF PERFORMANCE OR OPERATION, SAID COMPATIBILITY AND SUITABILITY BEING YOUR RESPONSIBILITY. LICENSOR DISCLAIMS ANY WARRANTY, IMPLIED OR EXPRESSED, THAT ANY CONTRIBUTOR'S EXTENSIONS MEET ANY STANDARD OF COMPATIBILITY OR DESCRIPTION OF PERFORMANCE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LICENSED SOFTWARE IS WITH YOU. SHOULD LICENSED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (AND NOT THE LICENSOR OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. UNDER THE TERMS OF THIS LICENSOR WILL NOT SUPPORT THIS SOFTWARE AND IS UNDER NO OBLIGATION TO ISSUE UPDATES TO THIS SOFTWARE. LICENSOR HAS NO KNOWLEDGE OF ERRANT CODE OR VIRUS IN THIS SOFTWARE, BUT DOES NOT WARRANT THAT THE SOFTWARE IS FREE FROM SUCH ERRORS OR VIRUSES. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF LICENSED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-9.0 Limitation of Liability. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE LICENSOR, ANY CONTRIBUTOR, OR ANY DISTRIBUTOR OF LICENSED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10.0 High Risk Activities. THE LICENSED SOFTWARE IS NOT FAULT-TOLERANT AND IS NOT DESIGNED, MANUFACTURED, OR INTENDED FOR USE OR DISTRIBUTION AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT NAVIGATION OR COMMUNICATIONS SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE LICENSED SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). LICENSOR AND CONTRIBUTORS SPECIFICALLY DISCLAIM ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.
-
-11.0 Responsibility for Claims. As between Licensor and Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License which specifically disclaims warranties and limits any liability of the Licensor. This paragraph is to be used in conjunction with and controlled by the Disclaimer Of Warranties of Section 8, the Limitation Of Damages in Section 9, and the disclaimer against use for High Risk Activities in Section 10. The Licensor has thereby disclaimed all warranties and limited any damages that it is or may be liable for. You agree to work with Licensor and Contributors to distribute such responsibility on an equitable basis consistent with the terms of this License including Sections 8, 9, and 10. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-12.0 Termination. This License and all rights granted hereunder will terminate immediately in the event of the circumstances described in Section 13.6 or if applicable law prohibits or restricts You from fully and or specifically complying with Sections 3, 4 and/or 6, or prevents the enforceability of any of those Sections, and You must immediately discontinue any use of Licensed Software.
-
- 12.1 Automatic Termination Upon Breach. This License and the rights granted hereunder will terminate automatically if You fail to comply with the terms herein and fail to cure such breach within thirty (30) days of becoming aware of the breach. All sublicenses to the Licensed Software that are properly granted shall survive any termination of this License. Provisions that, by their nature, must remain in effect beyond the termination of this License, shall survive.
-
- 12.2 Termination Upon Assertion of Patent Infringement. If You initiate litigation by asserting a patent infringement claim (excluding declaratory judgment actions) against Licensor or a Contributor (Licensor or Contributor against whom You file such an action is referred to herein as "Respondent") alleging that Licensed Software directly or indirectly infringes any patent, then any and all rights granted by such Respondent to You under Sections 3 or 4 of this License shall terminate prospectively upon sixty (60) days notice from Respondent (the "Notice Period") unless within that Notice Period You either agree in writing (i) to pay Respondent a mutually agreeable reasonably royalty for Your past or future use of Licensed Software made by such Respondent, or (ii) withdraw Your litigation claim with respect to Licensed Software against such Respondent. If within said Notice Period a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Licensor to You under Sections 3 and 4 automatically terminate at the expiration of said Notice Period.
-
- 12.3 Reasonable Value of This License. If You assert a patent infringement claim against Respondent alleging that Licensed Software directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by said Respondent under Sections 3 and 4 shall be taken into account in determining the amount or value of any payment or license.
-
- 12.4 No Retroactive Effect of Termination. In the event of termination under this Section all end user license agreements (excluding licenses to distributors and resellers) that have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-13.0 Miscellaneous.
-
- 13.1 U.S. Government End Users. The Licensed Software is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Licensed Software with only those rights set forth herein.
-
- 13.2 Relationship of Parties. This License will not be construed as creating an agency, partnership, joint venture, or any other form of legal association between or among You, Licensor, or any Contributor, and You will not represent to the contrary, whether expressly, by implication, appearance, or otherwise.
-
- 13.3 Independent Development. Nothing in this License will impair Licensor's right to acquire, license, develop, subcontract, market, or distribute technology or products that perform the same or similar functions as, or otherwise compete with, Extensions that You may develop, produce, market, or distribute.
-
- 13.4 Consent To Breach Not Waiver. Failure by Licensor or Contributor to enforce any provision of this License will not be deemed a waiver of future enforcement of that or any other provision.
-
- 13.5 Severability. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
-
- 13.6 Inability to Comply Due to Statute or Regulation. If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Licensed Software due to statute, judicial order, or regulation, then You cannot use, modify, or distribute the software.
-
- 13.7 Export Restrictions. You may be restricted with respect to downloading or otherwise acquiring, exporting, or reexporting the Licensed Software or any underlying information or technology by United States and other applicable laws and regulations. By downloading or by otherwise obtaining the Licensed Software, You are agreeing to be responsible for compliance with all applicable laws and regulations.
-
- 13.8 Arbitration, Jurisdiction & Venue. This License shall be governed by Colorado law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. You expressly agree that any dispute relating to this License shall be submitted to binding arbitration under the rules then prevailing of the American Arbitration Association. You further agree that Adams County, Colorado USA is proper venue and grant such arbitration proceeding jurisdiction as may be appropriate for purposes of resolving any dispute under this License. Judgement upon any award made in arbitration may be entered and enforced in any court of competent jurisdiction. The arbitrator shall award attorney's fees and costs of arbitration to the prevailing party. Should either party find it necessary to enforce its arbitration award or seek specific performance of such award in a civil court of competent jurisdiction, the prevailing party shall be entitled to reasonable attorney's fees and costs. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. You and Licensor expressly waive any rights to a jury trial in any litigation concerning Licensed Software or this License. Any law or regulation that provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
- 13.9 Entire Agreement. This License constitutes the entire agreement between the parties with respect to the subject matter hereof.
-
-EXHIBIT A
-
-The Notice below must appear in each file of the Source Code of any copy You distribute of the Licensed Software or any Extensions thereto, except as may be modified as allowed under the terms of Section 7.1
-Copyright (C) 1999-2002 Technical Pursuit Inc., All Rights Reserved. Patent Pending, Technical Pursuit Inc.
-
-Unless explicitly acquired and licensed from Licensor under the Technical Pursuit License ("TPL") Version 1.0 or greater, the contents of this file are subject to the Reciprocal Public License ("RPL") Version 1.1, or subsequent versions as allowed by the RPL, and You may not copy or use this file in either source code or executable form, except in compliance with the terms and conditions of the RPL.
-You may obtain a copy of both the TPL and the RPL (the "Licenses") from Technical Pursuit Inc. at http://www.technicalpursuit.com.
-
-All software distributed under the Licenses is provided strictly on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND TECHNICAL PURSUIT INC. HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT, OR NON-INFRINGEMENT. See the Licenses for specific language governing rights and limitations under the Licenses.
diff --git a/options/license/RPL-1.5 b/options/license/RPL-1.5
deleted file mode 100644
index f277618bcb..0000000000
--- a/options/license/RPL-1.5
+++ /dev/null
@@ -1,167 +0,0 @@
-Reciprocal Public License 1.5 (RPL1.5)
-
-Version 1.5, July 15, 2007
-
-Copyright (C) 2001-2007 Technical Pursuit Inc., All Rights Reserved.
-
-PREAMBLE
-
-The Reciprocal Public License (RPL) is based on the concept of reciprocity or, if you prefer, fairness.
-
-In short, this license grew out of a desire to close loopholes in previous open source licenses, loopholes that allowed parties to acquire open source software and derive financial benefit from it without having to release their improvements or derivatives to the community which enabled them. This occurred any time an entity did not release their application to a "third party".
-
-While there is a certain freedom in this model of licensing, it struck the authors of the RPL as being unfair to the open source community at large and to the original authors of the works in particular. After all, bug fixes, extensions, and meaningful and valuable derivatives were not consistently finding their way back into the community where they could fuel further, and faster, growth and expansion of the overall open source software base.
-
-While you should clearly read and understand the entire license, the essence of the RPL is found in two definitions: "Deploy" and "Required Components".
-
-Regarding deployment, under the RPL your changes, bug fixes, extensions, etc. must be made available to the open source community at large when you Deploy in any form -- either internally or to an outside party. Once you start running the software you have to start sharing the software.
-
-Further, under the RPL all components you author including schemas, scripts, source code, etc. -- regardless of whether they're compiled into a single binary or used as two halves of client/server application -- must be shared. You have to share the whole pie, not an isolated slice of it.
-
-In addition to these goals, the RPL was authored to meet the requirements of the Open Source Definition as maintained by the Open Source Initiative (OSI).
-
-The specific terms and conditions of the license are defined in the remainder of this document.
-
-LICENSE TERMS
-
-1.0 General; Applicability & Definitions. This Reciprocal Public License Version 1.5 ("License") applies to any programs or other works as well as any and all updates or maintenance releases of said programs or works ("Software") not already covered by this License which the Software copyright holder ("Licensor") makes available containing a License Notice (hereinafter defined) from the Licensor specifying or allowing use or distribution under the terms of this License. As used in this License:
-
- 1.1 "Contributor" means any person or entity who created or contributed to the creation of an Extension.
-
- 1.2 "Deploy" means to use, Serve, sublicense or distribute Licensed Software other than for Your internal Research and/or Personal Use, and includes without limitation, any and all internal use or distribution of Licensed Software within Your business or organization other than for Research and/or Personal Use, as well as direct or indirect sublicensing or distribution of Licensed Software by You to any third party in any form or manner.
-
- 1.3 "Derivative Works" as used in this License is defined under U.S. copyright law.
-
- 1.4 "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data such as download from an FTP server or web site, where such mechanism is publicly accessible.
-
- 1.5 "Extensions" means any Modifications, Derivative Works, or Required Components as those terms are defined in this License.
-
- 1.6 "License" means this Reciprocal Public License.
-
- 1.7 "License Notice" means any notice contained in EXHIBIT A.
-
- 1.8 "Licensed Software" means any Software licensed pursuant to this License. Licensed Software also includes all previous Extensions from any Contributor that You receive.
-
- 1.9 "Licensor" means the copyright holder of any Software previously not covered by this License who releases the Software under the terms of this License.
-
- 1.10 "Modifications" means any additions to or deletions from the substance or structure of (i) a file or other storage containing Licensed Software, or (ii) any new file or storage that contains any part of Licensed Software, or (iii) any file or storage which replaces or otherwise alters the original functionality of Licensed Software at runtime.
-
- 1.11 "Personal Use" means use of Licensed Software by an individual solely for his or her personal, private and non-commercial purposes. An individual's use of Licensed Software in his or her capacity as an officer, employee, member, independent contractor or agent of a corporation, business or organization (commercial or non-commercial) does not qualify as Personal Use.
-
- 1.12 "Required Components" means any text, programs, scripts, schema, interface definitions, control files, or other works created by You which are required by a third party of average skill to successfully install and run Licensed Software containing Your Modifications, or to install and run Your Derivative Works.
-
- 1.13 "Research" means investigation or experimentation for the purpose of understanding the nature and limits of the Licensed Software and its potential uses.
-
- 1.14 "Serve" means to deliver Licensed Software and/or Your Extensions by means of a computer network to one or more computers for purposes of execution of Licensed Software and/or Your Extensions.
-
- 1.15 "Software" means any computer programs or other works as well as any updates or maintenance releases of those programs or works which are distributed publicly by Licensor.
-
- 1.16 "Source Code" means the preferred form for making modifications to the Licensed Software and/or Your Extensions, including all modules contained therein, plus any associated text, interface definition files, scripts used to control compilation and installation of an executable program or other components required by a third party of average skill to build a running version of the Licensed Software or Your Extensions.
-
- 1.17 "User-Visible Attribution Notice" means any notice contained in EXHIBIT B.
-
- 1.18 "You" or "Your" means an individual or a legal entity exercising rights under this License. For legal entities, "You" or "Your" includes any entity which controls, is controlled by, or is under common control with, You, where "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
-2.0 Acceptance Of License. You are not required to accept this License since you have not signed it, however nothing else grants you permission to use, copy, distribute, modify, or create derivatives of either the Software or any Extensions created by a Contributor. These actions are prohibited by law if you do not accept this License. Therefore, by performing any of these actions You indicate Your acceptance of this License and Your agreement to be bound by all its terms and conditions. IF YOU DO NOT AGREE WITH ALL THE TERMS AND CONDITIONS OF THIS LICENSE DO NOT USE, MODIFY, CREATE DERIVATIVES, OR DISTRIBUTE THE SOFTWARE. IF IT IS IMPOSSIBLE FOR YOU TO COMPLY WITH ALL THE TERMS AND CONDITIONS OF THIS LICENSE THEN YOU CAN NOT USE, MODIFY, CREATE DERIVATIVES, OR DISTRIBUTE THE SOFTWARE.
-
-3.0 Grant of License From Licensor. Subject to the terms and conditions of this License, Licensor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to Licensor's intellectual property rights, and any third party intellectual property claims derived from the Licensed Software under this License, to do the following:
-
- 3.1 Use, reproduce, modify, display, perform, sublicense and distribute Licensed Software and Your Extensions in both Source Code form or as an executable program.
-
- 3.2 Create Derivative Works (as that term is defined under U.S. copyright law) of Licensed Software by adding to or deleting from the substance or structure of said Licensed Software.
-
- 3.3 Under claims of patents now or hereafter owned or controlled by Licensor, to make, use, have made, and/or otherwise dispose of Licensed Software or portions thereof, but solely to the extent that any such claim is necessary to enable You to make, use, have made, and/or otherwise dispose of Licensed Software or portions thereof.
-
- 3.4 Licensor reserves the right to release new versions of the Software with different features, specifications, capabilities, functions, licensing terms, general availability or other characteristics. Title, ownership rights, and intellectual property rights in and to the Licensed Software shall remain in Licensor and/or its Contributors.
-
-4.0 Grant of License From Contributor. By application of the provisions in Section 6 below, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to said Contributor's intellectual property rights, and any third party intellectual property claims derived from the Licensed Software under this License, to do the following:
-
- 4.1 Use, reproduce, modify, display, perform, sublicense and distribute any Extensions Deployed by such Contributor or portions thereof, in both Source Code form or as an executable program, either on an unmodified basis or as part of Derivative Works.
-
- 4.2 Under claims of patents now or hereafter owned or controlled by Contributor, to make, use, have made, and/or otherwise dispose of Extensions or portions thereof, but solely to the extent that any such claim is necessary to enable You to make, use, have made, and/or otherwise dispose of Licensed Software or portions thereof.
-
-5.0 Exclusions From License Grant. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor or any Contributor except as expressly stated herein. Except as expressly stated in Sections 3 and 4, no other patent rights, express or implied, are granted herein. Your Extensions may require additional patent licenses from Licensor or Contributors which each may grant in its sole discretion. No right is granted to the trademarks of Licensor or any Contributor even if such marks are included in the Licensed Software. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any code that Licensor otherwise would have a right to license.
-
- 5.1 You expressly acknowledge and agree that although Licensor and each Contributor grants the licenses to their respective portions of the Licensed Software set forth herein, no assurances are provided by Licensor or any Contributor that the Licensed Software does not infringe the patent or other intellectual property rights of any other entity. Licensor and each Contributor disclaim any liability to You for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, You hereby assume sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow You to distribute the Licensed Software, it is Your responsibility to acquire that license before distributing the Licensed Software.
-
-6.0 Your Obligations And Grants. In consideration of, and as an express condition to, the licenses granted to You under this License You hereby agree that any Modifications, Derivative Works, or Required Components (collectively Extensions) that You create or to which You contribute are governed by the terms of this License including, without limitation, Section 4. Any Extensions that You create or to which You contribute must be Deployed under the terms of this License or a future version of this License released under Section 7. You hereby grant to Licensor and all third parties a world-wide, non-exclusive, royalty-free license under those intellectual property rights You own or control to use, reproduce, display, perform, modify, create derivatives, sublicense, and distribute Licensed Software, in any form. Any Extensions You make and Deploy must have a distinct title so as to readily tell any subsequent user or Contributor that the Extensions are by You. You must include a copy of this License or directions on how to obtain a copy with every copy of the Extensions You distribute. You agree not to offer or impose any terms on any Source Code or executable version of the Licensed Software, or its Extensions that alter or restrict the applicable version of this License or the recipients' rights hereunder.
-
- 6.1 Availability of Source Code. You must make available, under the terms of this License, the Source Code of any Extensions that You Deploy, via an Electronic Distribution Mechanism. The Source Code for any version that You Deploy must be made available within one (1) month of when you Deploy and must remain available for no less than twelve (12) months after the date You cease to Deploy. You are responsible for ensuring that the Source Code to each version You Deploy remains available even if the Electronic Distribution Mechanism is maintained by a third party. You may not charge a fee for any copy of the Source Code distributed under this Section in excess of Your actual cost of duplication and distribution of said copy.
-
- 6.2 Description of Modifications. You must cause any Modifications that You create or to which You contribute to be documented in the Source Code, clearly describing the additions, changes or deletions You made. You must include a prominent statement that the Modifications are derived, directly or indirectly, from the Licensed Software and include the names of the Licensor and any Contributor to the Licensed Software in (i) the Source Code and (ii) in any notice displayed by the Licensed Software You distribute or in related documentation in which You describe the origin or ownership of the Licensed Software. You may not modify or delete any pre-existing copyright notices, change notices or License text in the Licensed Software without written permission of the respective Licensor or Contributor.
-
- 6.3 Intellectual Property Matters.
-
- a. Third Party Claims. If You have knowledge that a license to a third party's intellectual property right is required to exercise the rights granted by this License, You must include a human-readable file with Your distribution that describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact.
-
- b. Contributor APIs. If Your Extensions include an application programming interface ("API") and You have knowledge of patent licenses that are reasonably necessary to implement that API, You must also include this information in a human-readable file supplied with Your distribution.
-
- c. Representations. You represent that, except as disclosed pursuant to 6.3(a) above, You believe that any Extensions You distribute are Your original creations and that You have sufficient rights to grant the rights conveyed by this License.
-
- 6.4 Required Notices.
-
- a. License Text. You must duplicate this License or instructions on how to acquire a copy in any documentation You provide along with the Source Code of any Extensions You create or to which You contribute, wherever You describe recipients' rights relating to Licensed Software.
-
- b. License Notice. You must duplicate any notice contained in EXHIBIT A (the "License Notice") in each file of the Source Code of any copy You distribute of the Licensed Software and Your Extensions. If You create an Extension, You may add Your name as a Contributor to the Source Code and accompanying documentation along with a description of the contribution. If it is not possible to put the License Notice in a particular Source Code file due to its structure, then You must include such License Notice in a location where a user would be likely to look for such a notice.
-
- c. Source Code Availability. You must notify the software community of the availability of Source Code to Your Extensions within one (1) month of the date You initially Deploy and include in such notification a description of the Extensions, and instructions on how to acquire the Source Code. Should such instructions change you must notify the software community of revised instructions within one (1) month of the date of change. You must provide notification by posting to appropriate news groups, mailing lists, weblogs, or other sites where a publicly accessible search engine would reasonably be expected to index your post in relationship to queries regarding the Licensed Software and/or Your Extensions.
-
- d. User-Visible Attribution. You must duplicate any notice contained in EXHIBIT B (the "User-Visible Attribution Notice") in each user-visible display of the Licensed Software and Your Extensions which delineates copyright, ownership, or similar attribution information. If You create an Extension, You may add Your name as a Contributor, and add Your attribution notice, as an equally visible and functional element of any User-Visible Attribution Notice content. To ensure proper attribution, You must also include such User-Visible Attribution Notice in at least one location in the Software documentation where a user would be likely to look for such notice.
-
- 6.5 Additional Terms. You may choose to offer, and charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Licensed Software. However, You may do so only on Your own behalf, and not on behalf of the Licensor or any Contributor except as permitted under other agreements between you and Licensor or Contributor. You must make it clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Licensor and every Contributor for any liability plus attorney fees, costs, and related expenses due to any such action or claim incurred by the Licensor or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
- 6.6 Conflicts With Other Licenses. Where any portion of Your Extensions, by virtue of being Derivative Works of another product or similar circumstance, fall under the terms of another license, the terms of that license should be honored however You must also make Your Extensions available under this License. If the terms of this License continue to conflict with the terms of the other license you may write the Licensor for permission to resolve the conflict in a fashion that remains consistent with the intent of this License. Such permission will be granted at the sole discretion of the Licensor.
-
-7.0 Versions of This License. Licensor may publish from time to time revised versions of the License. Once Licensed Software has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Licensed Software under the terms of any subsequent version of the License published by Licensor. No one other than Licensor has the right to modify the terms applicable to Licensed Software created under this License.
-
- 7.1 If You create or use a modified version of this License, which You may do only in order to apply it to software that is not already Licensed Software under this License, You must rename Your license so that it is not confusingly similar to this License, and must make it clear that Your license contains terms that differ from this License. In so naming Your license, You may not use any trademark of Licensor or of any Contributor. Should Your modifications to this License be limited to alteration of a) Section 13.8 solely to modify the legal Jurisdiction or Venue for disputes, b) EXHIBIT A solely to define License Notice text, or c) to EXHIBIT B solely to define a User-Visible Attribution Notice, You may continue to refer to Your License as the Reciprocal Public License or simply the RPL.
-
-8.0 Disclaimer of Warranty. LICENSED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE LICENSED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. FURTHER THERE IS NO WARRANTY MADE AND ALL IMPLIED WARRANTIES ARE DISCLAIMED THAT THE LICENSED SOFTWARE MEETS OR COMPLIES WITH ANY DESCRIPTION OF PERFORMANCE OR OPERATION, SAID COMPATIBILITY AND SUITABILITY BEING YOUR RESPONSIBILITY. LICENSOR DISCLAIMS ANY WARRANTY, IMPLIED OR EXPRESSED, THAT ANY CONTRIBUTOR'S EXTENSIONS MEET ANY STANDARD OF COMPATIBILITY OR DESCRIPTION OF PERFORMANCE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LICENSED SOFTWARE IS WITH YOU. SHOULD LICENSED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (AND NOT THE LICENSOR OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. UNDER THE TERMS OF THIS LICENSOR WILL NOT SUPPORT THIS SOFTWARE AND IS UNDER NO OBLIGATION TO ISSUE UPDATES TO THIS SOFTWARE. LICENSOR HAS NO KNOWLEDGE OF ERRANT CODE OR VIRUS IN THIS SOFTWARE, BUT DOES NOT WARRANT THAT THE SOFTWARE IS FREE FROM SUCH ERRORS OR VIRUSES. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF LICENSED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-9.0 Limitation of Liability. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE LICENSOR, ANY CONTRIBUTOR, OR ANY DISTRIBUTOR OF LICENSED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10.0 High Risk Activities. THE LICENSED SOFTWARE IS NOT FAULT-TOLERANT AND IS NOT DESIGNED, MANUFACTURED, OR INTENDED FOR USE OR DISTRIBUTION AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT NAVIGATION OR COMMUNICATIONS SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE LICENSED SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). LICENSOR AND CONTRIBUTORS SPECIFICALLY DISCLAIM ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.
-
-11.0 Responsibility for Claims. As between Licensor and Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License which specifically disclaims warranties and limits any liability of the Licensor. This paragraph is to be used in conjunction with and controlled by the Disclaimer Of Warranties of Section 8, the Limitation Of Damages in Section 9, and the disclaimer against use for High Risk Activities in Section 10. The Licensor has thereby disclaimed all warranties and limited any damages that it is or may be liable for. You agree to work with Licensor and Contributors to distribute such responsibility on an equitable basis consistent with the terms of this License including Sections 8, 9, and 10. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-12.0 Termination. This License and all rights granted hereunder will terminate immediately in the event of the circumstances described in Section 13.6 or if applicable law prohibits or restricts You from fully and or specifically complying with Sections 3, 4 and/or 6, or prevents the enforceability of any of those Sections, and You must immediately discontinue any use of Licensed Software.
-
- 12.1 Automatic Termination Upon Breach. This License and the rights granted hereunder will terminate automatically if You fail to comply with the terms herein and fail to cure such breach within thirty (30) days of becoming aware of the breach. All sublicenses to the Licensed Software that are properly granted shall survive any termination of this License. Provisions that, by their nature, must remain in effect beyond the termination of this License, shall survive.
-
- 12.2 Termination Upon Assertion of Patent Infringement. If You initiate litigation by asserting a patent infringement claim (excluding declaratory judgment actions) against Licensor or a Contributor (Licensor or Contributor against whom You file such an action is referred to herein as "Respondent") alleging that Licensed Software directly or indirectly infringes any patent, then any and all rights granted by such Respondent to You under Sections 3 or 4 of this License shall terminate prospectively upon sixty (60) days notice from Respondent (the "Notice Period") unless within that Notice Period You either agree in writing (i) to pay Respondent a mutually agreeable reasonably royalty for Your past or future use of Licensed Software made by such Respondent, or (ii) withdraw Your litigation claim with respect to Licensed Software against such Respondent. If within said Notice Period a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Licensor to You under Sections 3 and 4 automatically terminate at the expiration of said Notice Period.
-
- 12.3 Reasonable Value of This License. If You assert a patent infringement claim against Respondent alleging that Licensed Software directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by said Respondent under Sections 3 and 4 shall be taken into account in determining the amount or value of any payment or license.
-
- 12.4 No Retroactive Effect of Termination. In the event of termination under this Section all end user license agreements (excluding licenses to distributors and resellers) that have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-13.0 Miscellaneous.
-
- 13.1 U.S. Government End Users. The Licensed Software is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Licensed Software with only those rights set forth herein.
-
- 13.2 Relationship of Parties. This License will not be construed as creating an agency, partnership, joint venture, or any other form of legal association between or among You, Licensor, or any Contributor, and You will not represent to the contrary, whether expressly, by implication, appearance, or otherwise.
-
- 13.3 Independent Development. Nothing in this License will impair Licensor's right to acquire, license, develop, subcontract, market, or distribute technology or products that perform the same or similar functions as, or otherwise compete with, Extensions that You may develop, produce, market, or distribute.
-
- 13.4 Consent To Breach Not Waiver. Failure by Licensor or Contributor to enforce any provision of this License will not be deemed a waiver of future enforcement of that or any other provision.
-
- 13.5 Severability. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
-
- 13.6 Inability to Comply Due to Statute or Regulation. If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Licensed Software due to statute, judicial order, or regulation, then You cannot use, modify, or distribute the software.
-
- 13.7 Export Restrictions. You may be restricted with respect to downloading or otherwise acquiring, exporting, or reexporting the Licensed Software or any underlying information or technology by United States and other applicable laws and regulations. By downloading or by otherwise obtaining the Licensed Software, You are agreeing to be responsible for compliance with all applicable laws and regulations.
-
- 13.8 Arbitration, Jurisdiction & Venue. This License shall be governed by Colorado law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. You expressly agree that any dispute relating to this License shall be submitted to binding arbitration under the rules then prevailing of the American Arbitration Association. You further agree that Adams County, Colorado USA is proper venue and grant such arbitration proceeding jurisdiction as may be appropriate for purposes of resolving any dispute under this License. Judgement upon any award made in arbitration may be entered and enforced in any court of competent jurisdiction. The arbitrator shall award attorney's fees and costs of arbitration to the prevailing party. Should either party find it necessary to enforce its arbitration award or seek specific performance of such award in a civil court of competent jurisdiction, the prevailing party shall be entitled to reasonable attorney's fees and costs. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. You and Licensor expressly waive any rights to a jury trial in any litigation concerning Licensed Software or this License. Any law or regulation that provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
- 13.9 Entire Agreement. This License constitutes the entire agreement between the parties with respect to the subject matter hereof.
-
-EXHIBIT A
-
-The License Notice below must appear in each file of the Source Code of any copy You distribute of the Licensed Software or any Extensions thereto:
-
-Unless explicitly acquired and licensed from Licensor under another license, the contents of this file are subject to the Reciprocal Public License ("RPL") Version 1.5, or subsequent versions as allowed by the RPL, and You may not copy or use this file in either source code or executable form, except in compliance with the terms and conditions of the RPL.
-
-All software distributed under the RPL is provided strictly on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND LICENSOR HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT, OR NON-INFRINGEMENT. See the RPL for specific language governing rights and limitations under the RPL.
-
-EXHIBIT B
-
-The User-Visible Attribution Notice below, when provided, must appear in each user-visible display as defined in Section 6.4 (d):
diff --git a/options/license/RPSL-1.0 b/options/license/RPSL-1.0
deleted file mode 100644
index b4ea616ceb..0000000000
--- a/options/license/RPSL-1.0
+++ /dev/null
@@ -1,179 +0,0 @@
-RealNetworks Public Source License Version 1.0
-
-(Rev. Date October 28, 2002)
-
-1. General Definitions. This License applies to any program or other work which RealNetworks, Inc., or any other entity that elects to use this license, ("Licensor") makes publicly available and which contains a notice placed by Licensor identifying such program or work as "Original Code" and stating that it is subject to the terms of this RealNetworks Public Source License version 1.0 (or subsequent version thereof) ("License"). You are not required to accept this License. However, nothing else grants You permission to use, copy, modify or distribute the software or its derivative works. These actions are prohibited by law if You do not accept this License. Therefore, by modifying, copying or distributing the software (or any work based on the software), You indicate your acceptance of this License to do so, and all its terms and conditions. In addition, you agree to the terms of this License by clicking the Accept button or downloading the software. As used in this License:
-
- 1.1 "Applicable Patent Rights" mean: (a) in the case where Licensor is the grantor of rights, claims of patents that (i) are now or hereafter acquired, owned by or assigned to Licensor and (ii) are necessarily infringed by using or making the Original Code alone and not in combination with other software or hardware; and (b) in the case where You are the grantor of rights, claims of patents that (i) are now or hereafter acquired, owned by or assigned to You and (ii) are infringed (directly or indirectly) by using or making Your Modifications, taken alone or in combination with Original Code.
-
- 1.2 "Compatible Source License" means any one of the licenses listed on Exhibit B or at https://www.helixcommunity.org/content/complicense or other licenses specifically identified by Licensor in writing. Notwithstanding any term to the contrary in any Compatible Source License, any code covered by any Compatible Source License that is used with Covered Code must be made readily available in Source Code format for royalty-free use under the terms of the Compatible Source License or this License.
-
- 1.3 "Contributor" means any person or entity that creates or contributes to the creation of Modifications.
-
- 1.4 "Covered Code" means the Original Code, Modifications, the combination of Original Code and any Modifications, and/or any respective portions thereof.
-
- 1.5 "Deploy" means to use, sublicense or distribute Covered Code other than for Your internal research and development (R&D) and/or Personal Use, and includes without limitation, any and all internal use or distribution of Covered Code within Your business or organization except for R&D use and/or Personal Use, as well as direct or indirect sublicensing or distribution of Covered Code by You to any third party in any form or manner.
-
- 1.6 "Derivative Work" means either the Covered Code or any derivative work under United States copyright law, and including any work containing or including any portion of the Covered Code or Modifications, either verbatim or with modifications and/or translated into another language. Derivative Work also includes any work which combines any portion of Covered Code or Modifications with code not otherwise governed by the terms of this License.
-
- 1.7 "Externally Deploy" means to Deploy the Covered Code in any way that may be accessed or used by anyone other than You, used to provide any services to anyone other than You, or used in any way to deliver any content to anyone other than You, whether the Covered Code is distributed to those parties, made available as an application intended for use over a computer network, or used to provide services or otherwise deliver content to anyone other than You.
-
- 1.8. "Interface" means interfaces, functions, properties, class definitions, APIs, header files, GUIDs, V-Tables, and/or protocols allowing one piece of software, firmware or hardware to communicate or interoperate with another piece of software, firmware or hardware.
-
- 1.9 "Modifications" mean any addition to, deletion from, and/or change to, the substance and/or structure of the Original Code, any previous Modifications, the combination of Original Code and any previous Modifications, and/or any respective portions thereof. When code is released as a series of files, a Modification is: (a) any addition to or deletion from the contents of a file containing Covered Code; and/or (b) any new file or other representation of computer program statements that contains any part of Covered Code.
-
- 1.10 "Original Code" means (a) the Source Code of a program or other work as originally made available by Licensor under this License, including the Source Code of any updates or upgrades to such programs or works made available by Licensor under this License, and that has been expressly identified by Licensor as such in the header file(s) of such work; and (b) the object code compiled from such Source Code and originally made available by Licensor under this License.
-
- 1.11 "Personal Use" means use of Covered Code by an individual solely for his or her personal, private and non-commercial purposes. An individual's use of Covered Code in his or her capacity as an officer, employee, member, independent contractor or agent of a corporation, business or organization (commercial or non-commercial) does not qualify as Personal Use.
-
- 1.12 "Source Code" means the human readable form of a program or other work that is suitable for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an executable (object code).
-
- 1.13 "You" or "Your" means an individual or a legal entity exercising rights under this License. For legal entities, "You" or "Your" includes any entity which controls, is controlled by, or is under common control with, You, where "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
-2. Permitted Uses; Conditions & Restrictions. Subject to the terms and conditions of this License, Licensor hereby grants You, effective on the date You accept this License (via downloading or using Covered Code or otherwise indicating your acceptance of this License), a worldwide, royalty-free, non-exclusive copyright license, to the extent of Licensor's copyrights cover the Original Code, to do the following:
-
- 2.1 You may reproduce, display, perform, modify and Deploy Covered Code, provided that in each instance:
-
- (a) You must retain and reproduce in all copies of Original Code the copyright and other proprietary notices and disclaimers of Licensor as they appear in the Original Code, and keep intact all notices in the Original Code that refer to this License;
-
- (b) You must include a copy of this License with every copy of Source Code of Covered Code and documentation You distribute, and You may not offer or impose any terms on such Source Code that alter or restrict this License or the recipients' rights hereunder, except as permitted under Section 6;
-
- (c) You must duplicate, to the extent it does not already exist, the notice in Exhibit A in each file of the Source Code of all Your Modifications, and cause the modified files to carry prominent notices stating that You changed the files and the date of any change;
-
- (d) You must make Source Code of all Your Externally Deployed Modifications publicly available under the terms of this License, including the license grants set forth in Section 3 below, for as long as you Deploy the Covered Code or twelve (12) months from the date of initial Deployment, whichever is longer. You should preferably distribute the Source Code of Your Deployed Modifications electronically (e.g. download from a web site); and
-
- (e) if You Deploy Covered Code in object code, executable form only, You must include a prominent notice, in the code itself as well as in related documentation, stating that Source Code of the Covered Code is available under the terms of this License with information on how and where to obtain such Source Code. You must also include the Object Code Notice set forth in Exhibit A in the "about" box or other appropriate place where other copyright notices are placed, including any packaging materials.
-
- 2.2 You expressly acknowledge and agree that although Licensor and each Contributor grants the licenses to their respective portions of the Covered Code set forth herein, no assurances are provided by Licensor or any Contributor that the Covered Code does not infringe the patent or other intellectual property rights of any other entity. Licensor and each Contributor disclaim any liability to You for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, You hereby assume sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow You to make, use, sell, import or offer for sale the Covered Code, it is Your responsibility to acquire such license(s).
-
- 2.3 Subject to the terms and conditions of this License, Licensor hereby grants You, effective on the date You accept this License (via downloading or using Covered Code or otherwise indicating your acceptance of this License), a worldwide, royalty-free, perpetual, non-exclusive patent license under Licensor's Applicable Patent Rights to make, use, sell, offer for sale and import the Covered Code, provided that in each instance you comply with the terms of this License.
-
-3. Your Grants. In consideration of, and as a condition to, the licenses granted to You under this License:
-
- (a) You grant to Licensor and all third parties a non-exclusive, perpetual, irrevocable, royalty free license under Your Applicable Patent Rights and other intellectual property rights owned or controlled by You, to make, sell, offer for sale, use, import, reproduce, display, perform, modify, distribute and Deploy Your Modifications of the same scope and extent as Licensor's licenses under Sections 2.1 and 2.2; and
-
- (b) You grant to Licensor and its subsidiaries a non-exclusive, worldwide, royalty-free, perpetual and irrevocable license, under Your Applicable Patent Rights and other intellectual property rights owned or controlled by You, to make, use, sell, offer for sale, import, reproduce, display, perform, distribute, modify or have modified (for Licensor and/or its subsidiaries), sublicense and distribute Your Modifications, in any form and for any purpose, through multiple tiers of distribution.
-
- (c) You agree not use any information derived from Your use and review of the Covered Code, including but not limited to any algorithms or inventions that may be contained in the Covered Code, for the purpose of asserting any of Your patent rights, or assisting a third party to assert any of its patent rights, against Licensor or any Contributor.
-
-4. Derivative Works. You may create a Derivative Work by combining Covered Code with other code not otherwise governed by the terms of this License and distribute the Derivative Work as an integrated product. In each such instance, You must make sure the requirements of this License are fulfilled for the Covered Code or any portion thereof, including all Modifications.
-
- 4.1 You must cause any Derivative Work that you distribute, publish or Externally Deploy, that in whole or in part contains or is derived from the Covered Code or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License and no other license except as provided in Section 4.2. You also must make Source Code available for the Derivative Work under the same terms as Modifications, described in Sections 2 and 3, above.
-
- 4.2 Compatible Source Licenses. Software modules that have been independently developed without any use of Covered Code and which contain no portion of the Covered Code, Modifications or other Derivative Works, but are used or combined in any way with the Covered Code or any Derivative Work to form a larger Derivative Work, are exempt from the conditions described in Section 4.1 but only to the extent that: the software module, including any software that is linked to, integrated with, or part of the same applications as, the software module by any method must be wholly subject to one of the Compatible Source Licenses. Notwithstanding the foregoing, all Covered Code must be subject to the terms of this License. Thus, the entire Derivative Work must be licensed under a combination of the RPSL (for Covered Code) and a Compatible Source License for any independently developed software modules within the Derivative Work. The foregoing requirement applies even if the Compatible Source License would ordinarily allow the software module to link with, or form larger works with, other software that is not subject to the Compatible Source License. For example, although the Mozilla Public License v1.1 allows Mozilla code to be combined with proprietary software that is not subject to the MPL, if MPL-licensed code is used with Covered Code the MPL-licensed code could not be combined or linked with any code not governed by the MPL. The general intent of this section 4.2 is to enable use of Covered Code with applications that are wholly subject to an acceptable open source license. You are responsible for determining whether your use of software with Covered Code is allowed under Your license to such software.
-
- 4.3 Mere aggregation of another work not based on the Covered Code with the Covered Code (or with a work based on the Covered Code) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. If You deliver the Covered Code for combination and/or integration with an application previously provided by You (for example, via automatic updating technology), such combination and/or integration constitutes a Derivative Work subject to the terms of this License.
-
-5. Exclusions From License Grant. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor or any Contributor except as expressly stated herein. No right is granted to the trademarks of Licensor or any Contributor even if such marks are included in the Covered Code. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any code that Licensor otherwise would have a right to license. Modifications, Derivative Works and/or any use or combination of Covered Code with other technology provided by Licensor or third parties may require additional patent licenses from Licensor which Licensor may grant in its sole discretion. No patent license is granted separate from the Original Code or combinations of the Original Code with other software or hardware.
-
- 5.1. Trademarks. This License does not grant any rights to use the trademarks or trade names owned by Licensor ("Licensor Marks" defined in Exhibit C) or to any trademark or trade name belonging to any Contributor. No Licensor Marks may be used to endorse or promote products derived from the Original Code other than as permitted by the Licensor Trademark Policy defined in Exhibit C.
-
-6. Additional Terms. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations and/or other rights consistent with the scope of the license granted herein ("Additional Terms") to one or more recipients of Covered Code. However, You may do so only on Your own behalf and as Your sole responsibility, and not on behalf of Licensor or any Contributor. You must obtain the recipient's agreement that any such Additional Terms are offered by You alone, and You hereby agree to indemnify, defend and hold Licensor and every Contributor harmless for any liability incurred by or claims asserted against Licensor or such Contributor by reason of any such Additional Terms.
-
-7. Versions of the License. Licensor may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Once Original Code has been published under a particular version of this License, You may continue to use it under the terms of that version. You may also choose to use such Original Code under the terms of any subsequent version of this License published by Licensor. No one other than Licensor has the right to modify the terms applicable to Covered Code created under this License.
-
-8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in part pre-release, untested, or not fully tested works. The Covered Code may contain errors that could cause failures or loss of data, and may be incomplete or contain inaccuracies. You expressly acknowledge and agree that use of the Covered Code, or any portion thereof, is at Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND LICENSOR AND LICENSOR'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "LICENSOR" FOR THE PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. LICENSOR AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO ORAL OR WRITTEN DOCUMENTATION, INFORMATION OR ADVICE GIVEN BY LICENSOR, A LICENSOR AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. You acknowledge that the Covered Code is not intended for use in high risk activities, including, but not limited to, the design, construction, operation or maintenance of nuclear facilities, aircraft navigation, aircraft communication systems, or air traffic control machines in which case the failure of the Covered Code could lead to death, personal injury, or severe physical or environmental damage. Licensor disclaims any express or implied warranty of fitness for such uses.
-
-9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT SHALL LICENSOR OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE OR STRICT LIABILITY), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF LICENSOR OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU. In no event shall Licensor's total liability to You for all damages (other than as may be required by applicable law) under this License exceed the amount of ten dollars ($10.00).
-
-10. Ownership. Subject to the licenses granted under this License, each Contributor retains all rights, title and interest in and to any Modifications made by such Contributor. Licensor retains all rights, title and interest in and to the Original Code and any Modifications made by or on behalf of Licensor ("Licensor Modifications"), and such Licensor Modifications will not be automatically subject to this License. Licensor may, at its sole discretion, choose to license such Licensor Modifications under this License, or on different terms from those contained in this License or may choose not to license them at all.
-
-11. Termination.
-
- 11.1 Term and Termination. The term of this License is perpetual unless terminated as provided below. This License and the rights granted hereunder will terminate:
-
- (a) automatically without notice from Licensor if You fail to comply with any term(s) of this License and fail to cure such breach within 30 days of becoming aware of such breach;
-
- (b) immediately in the event of the circumstances described in Section 12.5(b); or
-
- (c) automatically without notice from Licensor if You, at any time during the term of this License, commence an action for patent infringement against Licensor (including by cross-claim or counter claim in a lawsuit);
-
- (d) upon written notice from Licensor if You, at any time during the term of this License, commence an action for patent infringement against any third party alleging that the Covered Code itself (excluding combinations with other software or hardware) infringes any patent (including by cross-claim or counter claim in a lawsuit).
-
- 11.2 Effect of Termination. Upon termination, You agree to immediately stop any further use, reproduction, modification, sublicensing and distribution of the Covered Code and to destroy all copies of the Covered Code that are in your possession or control. All sublicenses to the Covered Code which have been properly granted prior to termination shall survive any termination of this License. Provisions which, by their nature, should remain in effect beyond the termination of this License shall survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. No party will be liable to any other for compensation, indemnity or damages of any sort solely as a result of terminating this License in accordance with its terms, and termination of this License will be without prejudice to any other right or remedy of any party.
-
-12. Miscellaneous.
-
- 12.1 Government End Users. The Covered Code is a "commercial item" as defined in FAR 2.101. Government software and technical data rights in the Covered Code include only those rights customarily provided to the public as defined in this License. This customary commercial license in technical data and software is provided in accordance with FAR 12.211 (Technical Data) and 12.212 (Computer Software) and, for Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 (Rights in Commercial Computer Software or Computer Software Documentation). Accordingly, all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
- 12.2 Relationship of Parties. This License will not be construed as creating an agency, partnership, joint venture or any other form of legal association between or among You, Licensor or any Contributor, and You will not represent to the contrary, whether expressly, by implication, appearance or otherwise.
-
- 12.3 Independent Development. Nothing in this License will impair Licensor's right to acquire, license, develop, have others develop for it, market and/or distribute technology or products that perform the same or similar functions as, or otherwise compete with, Modifications, Derivative Works, technology or products that You may develop, produce, market or distribute.
-
- 12.4 Waiver; Construction. Failure by Licensor or any Contributor to enforce any provision of this License will not be deemed a waiver of future enforcement of that or any other provision. Any law or regulation which provides that the language of a contract shall be construed against the drafter will not apply to this License.
-
- 12.5 Severability. (a) If for any reason a court of competent jurisdiction finds any provision of this License, or portion thereof, to be unenforceable, that provision of the License will be enforced to the maximum extent permissible so as to effect the economic benefits and intent of the parties, and the remainder of this License will continue in full force and effect. (b) Notwithstanding the foregoing, if applicable law prohibits or restricts You from fully and/or specifically complying with Sections 2 and/or 3 or prevents the enforceability of either of those Sections, this License will immediately terminate and You must immediately discontinue any use of the Covered Code and destroy all copies of it that are in your possession or control.
-
- 12.6 Dispute Resolution. Any litigation or other dispute resolution between You and Licensor relating to this License shall take place in the Seattle, Washington, and You and Licensor hereby consent to the personal jurisdiction of, and venue in, the state and federal courts within that District with respect to this License. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded.
-
- 12.7 Export/Import Laws. This software is subject to all export and import laws and restrictions and regulations of the country in which you receive the Covered Code and You are solely responsible for ensuring that You do not export, re-export or import the Covered Code or any direct product thereof in violation of any such restrictions, laws or regulations, or without all necessary authorizations.
-
- 12.8 Entire Agreement; Governing Law. This License constitutes the entire agreement between the parties with respect to the subject matter hereof. This License shall be governed by the laws of the United States and the State of Washington.
-
- Where You are located in the province of Quebec, Canada, the following clause applies: The parties hereby confirm that they have requested that this License and all related documents be drafted in English. Les parties ont exigé que le présent contrat et tous les documents connexes soient rédigés en anglais.
-
-
-EXHIBIT A.
-
-"Copyright (c) 1995-2002 RealNetworks, Inc. and/or its licensors. All Rights Reserved.
-
-The contents of this file, and the files included with this file, are subject to the current version of the RealNetworks Public Source License Version 1.0 (the "RPSL") available at https://www.helixcommunity.org/content/rpsl unless you have licensed the file under the RealNetworks Community Source License Version 1.0 (the "RCSL") available at https://www.helixcommunity.org/content/rcsl, in which case the RCSL will apply. You may also obtain the license terms directly from RealNetworks. You may not use this file except in compliance with the RPSL or, if you have a valid RCSL with RealNetworks applicable to this file, the RCSL. Please see the applicable RPSL or RCSL for the rights, obligations and limitations governing use of the contents of the file.
-
-This file is part of the Helix DNA Technology. RealNetworks is the developer of the Original code and owns the copyrights in the portions it created.
-
-This file, and the files included with this file, is distributed and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
-
-Contributor(s): ____________________________________
-
-Technology Compatibility Kit Test Suite(s) Location (if licensed under the RCSL):
-
-________________________________"
-
-Object Code Notice: Helix DNA Client technology included. Copyright (c) RealNetworks, Inc., 1995-2002. All rights reserved.
-
-EXHIBIT B
-
-Compatible Source Licenses for the RealNetworks Public Source License. The following list applies to the most recent version of the license as of October 25, 2002, unless otherwise indicated.
-
-Academic Free License
-Apache Software License
-Apple Public Source License
-Artistic license
-Attribution Assurance Licenses
-BSD license
-Common Public License1
-Eiffel Forum License
-GNU General Public License (GPL)1
-GNU Library or "Lesser" General Public License (LGPL)1
-IBM Public License
-Intel Open Source License
-Jabber Open Source License
-MIT license
-MITRE Collaborative Virtual Workspace License (CVW License)
-Motosoto License
-Mozilla Public License 1.0 (MPL)
-Mozilla Public License 1.1 (MPL)
-Nokia Open Source License
-Open Group Test Suite License
-Python Software Foundation License
-Ricoh Source Code Public License
-Sun Industry Standards Source License (SISSL)
-Sun Public License
-University of Illinois/NCSA Open Source License
-Vovida Software License v. 1.0
-W3C License
-X.Net License
-Zope Public License
-zlib/libpng license
-
-1Note: because this license contains certain reciprocal licensing terms that purport to extend to independently developed code, You may be prohibited under the terms of this otherwise compatible license from using code licensed under its terms with Covered Code because Covered Code may only be licensed under the RealNetworks Public Source License. Any attempt to apply non RPSL license terms, including without limitation the GPL, to Covered Code is expressly forbidden. You are responsible for ensuring that Your use of Compatible Source Licensed code does not violate either the RPSL or the Compatible Source License.
-
-The latest version of this list can be found at: https://www.helixcommunity.org/content/complicense
-
-EXHIBIT C
-
-RealNetworks' Trademark policy.
-
-RealNetworks defines the following trademarks collectively as "Licensor Trademarks": "RealNetworks", "RealPlayer", "RealJukebox", "RealSystem", "RealAudio", "RealVideo", "RealOne Player", "RealMedia", "Helix" or any other trademarks or trade names belonging to RealNetworks.
-
-RealNetworks "Licensor Trademark Policy" forbids any use of Licensor Trademarks except as permitted by and in strict compliance at all times with RealNetworks' third party trademark usage guidelines which are posted at www.realnetworks.com/info/helixlogo.html.
diff --git a/options/license/RRDtool-FLOSS-exception-2.0 b/options/license/RRDtool-FLOSS-exception-2.0
deleted file mode 100644
index d88dae5868..0000000000
--- a/options/license/RRDtool-FLOSS-exception-2.0
+++ /dev/null
@@ -1,66 +0,0 @@
-FLOSS License Exception
-=======================
-(Adapted from http://www.mysql.com/company/legal/licensing/foss-exception.html)
-
-I want specified Free/Libre and Open Source Software ("FLOSS")
-applications to be able to use specified GPL-licensed RRDtool
-libraries (the "Program") despite the fact that not all FLOSS licenses are
-compatible with version 2 of the GNU General Public License (the "GPL").
-
-As a special exception to the terms and conditions of version 2.0 of the GPL:
-
-You are free to distribute a Derivative Work that is formed entirely from
-the Program and one or more works (each, a "FLOSS Work") licensed under one
-or more of the licenses listed below, as long as:
-
-1. You obey the GPL in all respects for the Program and the Derivative
-Work, except for identifiable sections of the Derivative Work which are
-not derived from the Program, and which can reasonably be considered
-independent and separate works in themselves,
-
-2. all identifiable sections of the Derivative Work which are not derived
-from the Program, and which can reasonably be considered independent and
-separate works in themselves,
-
-1. are distributed subject to one of the FLOSS licenses listed
-below, and
-
-2. the object code or executable form of those sections are
-accompanied by the complete corresponding machine-readable source
-code for those sections on the same medium and under the same FLOSS
-license as the corresponding object code or executable forms of
-those sections, and
-
-3. any works which are aggregated with the Program or with a Derivative
-Work on a volume of a storage or distribution medium in accordance with
-the GPL, can reasonably be considered independent and separate works in
-themselves which are not derivatives of either the Program, a Derivative
-Work or a FLOSS Work.
-
-If the above conditions are not met, then the Program may only be copied,
-modified, distributed or used under the terms and conditions of the GPL.
-
-FLOSS License List
-==================
-License name Version(s)/Copyright Date
-Academic Free License 2.0
-Apache Software License 1.0/1.1/2.0
-Apple Public Source License 2.0
-Artistic license From Perl 5.8.0
-BSD license "July 22 1999"
-Common Public License 1.0
-GNU Library or "Lesser" General Public License (LGPL) 2.0/2.1
-IBM Public License, Version 1.0
-Jabber Open Source License 1.0
-MIT License (As listed in file MIT-License.txt) -
-Mozilla Public License (MPL) 1.0/1.1
-Open Software License 2.0
-OpenSSL license (with original SSLeay license) "2003" ("1998")
-PHP License 3.01
-Python license (CNRI Python License) -
-Python Software Foundation License 2.1.1
-Sleepycat License "1999"
-W3C License "2001"
-X11 License "2001"
-Zlib/libpng License -
-Zope Public License 2.0/2.1
diff --git a/options/license/RSA-MD b/options/license/RSA-MD
deleted file mode 100644
index a8be92883f..0000000000
--- a/options/license/RSA-MD
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved.
-
-License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing this software or this function.
-
-License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work.
-
-RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind.
-
-These notices must be retained in any copies of any part of this documentation and/or software.
diff --git a/options/license/RSCPL b/options/license/RSCPL
deleted file mode 100644
index 832920e956..0000000000
--- a/options/license/RSCPL
+++ /dev/null
@@ -1,128 +0,0 @@
-Ricoh Source Code Public License
-Version 1.0
-
-1. Definitions.
-
- 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications.
-
- 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
- 1.3. "Electronic Distribution Mechanism" means a website or any other mechanism generally accepted in the software development community for the electronic transfer of data.
-
- 1.4. "Executable Code" means Governed Code in any form other than Source Code.
-
- 1.5. "Governed Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
- 1.6. "Larger Work" means a work which combines Governed Code or portions thereof with code not governed by the terms of this License.
-
- 1.7. "Licensable" means the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
- 1.8. "License" means this document.
-
- 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Governed Code is released as a series of files, a Modification is:
-
- (a) Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
-
- (b) Any new file that contains any part of the Original Code or previous Modifications.
-
- 1.10. "Original Code" means the "Platform for Information Applications" Source Code as released under this License by RSV.
-
- 1.11 "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by the grantor of a license thereto.
-
- 1.12. "RSV" means Ricoh Silicon Valley, Inc., a California corporation with offices at 2882 Sand Hill Road, Suite 115, Menlo Park, CA 94025-7022.
-
- 1.13. "Source Code" means the preferred form of the Governed Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of Executable Code, or a list of source code differential comparisons against either the Original Code or another well known, available Governed Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
- 1.14. "You" means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
-2. Source Code License.
-
- 2.1. Grant from RSV. RSV hereby grants You a worldwide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) to use, reproduce, modify, create derivative works of, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, or as part of a Larger Work; and
-
- (b) under Patent Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).
-
- 2.2. Contributor Grant. Each Contributor hereby grants You a worldwide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) to use, reproduce, modify, create derivative works of, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Governed Code or as part of a Larger Work; and
-
- (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (i) Modifications made by that Contributor (or portions thereof); and (ii) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
-
-3. Distribution Obligations.
-
- 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Governed Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
- 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable Code version or via an Electronic Distribution Mechanism to anyone to whom you made an Executable Code version available; and if made available via an Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
- 3.3. Description of Modifications. You must cause all Governed Code to which you contribute to contain a file documenting the changes You made to create that Governed Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by RSV and including the name of RSV in (a) the Source Code, and (b) in any notice in an Executable Code version or related documentation in which You describe the origin or ownership of the Governed Code.
-
- 3.4. Intellectual Property Matters.
-
- 3.4.1. Third Party Claims. If You have knowledge that a party claims an intellectual property right in particular functionality or code (or its utilization under this License), you must include a text file with the source code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after You make Your Modification available as described in Section 3.2, You shall promptly modify the LEGAL file in all copies You make available thereafter and shall take other steps (such as notifying RSV and appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Governed Code that new knowledge has been obtained. In the event that You are a Contributor, You represent that, except as disclosed in the LEGAL file, your Modifications are your original creations and, to the best of your knowledge, no third party has any claim (including but not limited to intellectual property claims) relating to your Modifications. You represent that the LEGAL file includes complete details of any license or other restriction associated with any part of your Modifications.
-
- 3.4.2. Contributor APIs. If Your Modification is an application programming interface and You own or control patents which are reasonably necessary to implement that API, you must also include this information in the LEGAL file.
-
- 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code, and this License in any documentation for the Source Code, where You describe recipients' rights relating to Governed Code. If You created one or more Modification(s), You may add your name as a Contributor to the notice described in Exhibit A. If it is not possible to put such notice in a particular Source Code file due to its structure, then you must include such notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Governed Code. However, You may do so only on Your own behalf, and not on behalf of RSV or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify RSV and every Contributor for any liability incurred by RSV or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
- 3.6. Distribution of Executable Code Versions. You may distribute Governed Code in Executable Code form only if the requirements of Section 3.1-3.5 have been met for that Governed Code, and if You include a prominent notice stating that the Source Code version of the Governed Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable Code version, related documentation or collateral in which You describe recipients' rights relating to the Governed Code. You may distribute the Executable Code version of Governed Code under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable Code version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable Code version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by RSV or any Contributor. You hereby agree to indemnify RSV and every Contributor for any liability incurred by RSV or such Contributor as a result of any such terms You offer.
-
- 3.7. Larger Works. You may create a Larger Work by combining Governed Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Governed Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-If it is impossible for You to comply with any of theterms of this License with respect to some or all of the Governed Code due to statute or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Trademark Usage.
-
- 5.1. Advertising Materials. All advertising materials mentioning features or use of the Governed Code must display the following acknowledgement: "This product includes software developed by Ricoh Silicon Valley, Inc."
-
- 5.2. Endorsements. The names "Ricoh," "Ricoh Silicon Valley," and "RSV" must not be used to endorse or promote Contributor Versions or Larger Works without the prior written permission of RSV.
-
- 5.3. Product Names. Contributor Versions and Larger Works may not be called "Ricoh" nor may the word "Ricoh" appear in their names without the prior written permission of RSV.
-
-6. Versions of the License.
-
- 6.1. New Versions. RSV may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
- 6.2. Effect of New Versions. Once Governed Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Governed Code under the terms of any subsequent version of the License published by RSV. No one other than RSV has the right to modify the terms applicable to Governed Code created under this License.
-
-7. Disclaimer of Warranty.
-GOVERNED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE GOVERNED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE GOVERNED CODE IS WITH YOU. SHOULD ANY GOVERNED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT RSV OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY GOVERNED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. Termination.
-
- 8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Governed Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
- 8.2. If You initiate patent infringement litigation against RSV or a Contributor (RSV or the Contributor against whom You file such action is referred to as "Participant") alleging that:
-
- (a) such Participant's Original Code or Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of the Original Code or the Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Original Code or the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.
-
- (b) any software, hardware, or device provided to You by the Participant, other than such Participant's Original Code or Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Original Code or the Modifications made by that Participant.
-
- 8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Original Code or Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
- 8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-9. Limitation of Liability.
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL RSV, ANY CONTRIBUTOR, OR ANY DISTRIBUTOR OF GOVERNED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO YOU OR ANY OTHER PERSON FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. TO THE EXTENT THAT ANY EXCLUSION OF DAMAGES ABOVE IS NOT VALID, YOU AGREE THAT IN NO EVENT WILL RSVS LIABILITY UNDER OR RELATED TO THIS AGREEMENT EXCEED FIVE THOUSAND DOLLARS ($5,000). THE GOVERNED CODE IS NOT INTENDED FOR USE IN CONNECTION WITH ANY NUCLER, AVIATION, MASS TRANSIT OR MEDICAL APPLICATION OR ANY OTHER INHERENTLY DANGEROUS APPLICATION THAT COULD RESULT IN DEATH, PERSONAL INJURY, CATASTROPHIC DAMAGE OR MASS DESTRUCTION, AND YOU AGREE THAT NEITHER RSV NOR ANY CONTRIBUTOR SHALL HAVE ANY LIABILITY OF ANY NATURE AS A RESULT OF ANY SUCH USE OF THE GOVERNED CODE.
-
-
-10. U.S. Government End Users.
-The Governed Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Governed Code with only those rights set forth herein.
-
-11. Miscellaneous.
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. The parties submit to personal jurisdiction in California and further agree that any cause of action arising under or related to this Agreement shall be brought in the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California. The losing party shall be responsible for costs, including without limitation, court costs and reasonable attorneys fees and expenses. Notwithstanding anything to the contrary herein, RSV may seek injunctive relief related to a breach of this Agreement in any court of competent jurisdiction. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-12. Responsibility for Claims.
-Except in cases where another Contributor has failed to comply with Section 3.4, You are responsible for damages arising, directly or indirectly, out of Your utilization of rights under this License, based on the number of copies of Governed Code you made available, the revenues you received from utilizing such rights, and other relevant factors. You agree to work with affected parties to distribute responsibility on an equitable basis.
-
-
-EXHIBIT A
-
-"The contents of this file are subject to the Ricoh Source Code Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.risource.org/RPL
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-This code was initially developed by Ricoh Silicon Valley, Inc. Portions created by Ricoh Silicon Valley, Inc. are Copyright (C) 1995-1999. All Rights Reserved.
-
-Contributor(s): ______________________________________."
diff --git a/options/license/Rdisc b/options/license/Rdisc
deleted file mode 100644
index 04104beca8..0000000000
--- a/options/license/Rdisc
+++ /dev/null
@@ -1,13 +0,0 @@
-Rdisc (this program) was developed by Sun Microsystems, Inc. and is provided for unrestricted use provided that this legend is included on all tape media and as a part of the software program in whole or part. Users may copy or modify Rdisc without charge, and they may freely distribute it.
-
-RDISC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
-
-Rdisc is provided with no support and without any obligation on the part of Sun Microsystems, Inc. to assist in its use, correction, modification or enhancement.
-
-SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY RDISC OR ANY PART THEREOF.
-
-In no event will Sun Microsystems, Inc. be liable for any lost revenue or profits or other special, indirect and consequential damages, even if Sun has been advised of the possibility of such damages.
-
-Sun Microsystems, Inc.
-2550 Garcia Avenue
-Mountain View, California 94043
diff --git a/options/license/Ruby b/options/license/Ruby
deleted file mode 100644
index 0c38b22ea9..0000000000
--- a/options/license/Ruby
+++ /dev/null
@@ -1,29 +0,0 @@
-1. You may make and give away verbatim copies of the source form of the software without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers.
-
-2. You may modify your copy of the software in any way, provided that you do at least ONE of the following:
-
- a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or by allowing the author to include your modifications in the software.
-
- b) use the modified software only within your corporation or organization.
-
- c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution.
-
- d) make other distribution arrangements with the author.
-
-3. You may distribute the software in object code or binary form, provided that you do at least ONE of the following:
-
- a) distribute the binaries and library files of the software, together with instructions (in the manual page or equivalent) on where to get the original distribution.
-
- b) accompany the distribution with the machine-readable source of the software.
-
- c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution.
-
- d) make other distribution arrangements with the author.
-
-4. You may modify and include the part of the software into any other software (possibly commercial). But some files in the distribution are not written by the author, so that they are not under these terms.
-
-For the list of those files and their copying conditions, see the file LEGAL.
-
-5. The scripts and library files supplied as input to or produced as output from the software do not automatically fall under the copyright of the software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this software.
-
-6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/options/license/Ruby-pty b/options/license/Ruby-pty
deleted file mode 100644
index c817762f84..0000000000
--- a/options/license/Ruby-pty
+++ /dev/null
@@ -1,10 +0,0 @@
-(c) Copyright 1998 by Akinori Ito.
-
-This software may be redistributed freely for this purpose, in full
-or in part, provided that this entire copyright notice is included
-on any copies of this software and applications and derivations thereof.
-
-This software is provided on an "as is" basis, without warranty of any
-kind, either expressed or implied, as to any matter including, but not
-limited to warranty of fitness of purpose, or merchantability, or
-results obtained from use of this software.
diff --git a/options/license/SANE-exception b/options/license/SANE-exception
deleted file mode 100644
index 198a8c67cc..0000000000
--- a/options/license/SANE-exception
+++ /dev/null
@@ -1,20 +0,0 @@
-As a special exception, the authors of sane-airscan give permission for
-additional uses of the libraries contained in this release of sane-airscan.
-
-The exception is that, if you link a sane-airscan library with other files
-to produce an executable, this does not by itself cause the
-resulting executable to be covered by the GNU General Public
-License. Your use of that executable is in no way restricted on
-account of linking the sane-airscan library code into it.
-
-This exception does not, however, invalidate any other reasons why
-the executable file might be covered by the GNU General Public
-License.
-
-If you submit changes to sane-airscan to the maintainers to be included in
-a subsequent release, you agree by submitting the changes that
-those changes may be distributed with this exception intact.
-
-If you write modifications of your own for sane-airscan, it is your choice
-whether to permit this exception to apply to your modifications.
-If you do not wish that, delete this exception notice.
diff --git a/options/license/SAX-PD b/options/license/SAX-PD
deleted file mode 100644
index f8e6def627..0000000000
--- a/options/license/SAX-PD
+++ /dev/null
@@ -1,31 +0,0 @@
-Copyright Status
-
-SAX is free!
-
-In fact, it's not possible to own a license to SAX, since it's been placed in the public domain.
-
-No Warranty
-
-Because SAX is released to the public domain, there is no warranty for the design or for the software implementation, to the extent permitted by applicable law. Except when otherwise stated in writing the copyright holders and/or other parties provide SAX "as is" without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of SAX is with you. Should SAX prove defective, you assume the cost of all necessary servicing, repair or correction.
-
-In no event unless required by applicable law or agreed to in writing will any copyright holder, or any other party who may modify and/or redistribute SAX, be liable to you for damages, including any general, special, incidental or consequential damages arising out of the use or inability to use SAX (including but not limited to loss of data or data being rendered inaccurate or losses sustained by you or third parties or a failure of the SAX to operate with any other programs), even if such holder or other party has been advised of the possibility of such damages.
-
-Copyright Disclaimers
-
-This page includes statements to that effect by David Megginson, who would have been able to claim copyright for the original work.
-
-SAX 1.0
-
-Version 1.0 of the Simple API for XML (SAX), created collectively by the membership of the XML-DEV mailing list, is hereby released into the public domain.
-
-No one owns SAX: you may use it freely in both commercial and non-commercial applications, bundle it with your software distribution, include it on a CD-ROM, list the source code in a book, mirror the documentation at your own web site, or use it in any other way you see fit.
-
-David Megginson, Megginson Technologies Ltd.
-1998-05-11
-
-SAX 2.0
-
-I hereby abandon any property rights to SAX 2.0 (the Simple API for XML), and release all of the SAX 2.0 source code, compiled code, and documentation contained in this distribution into the Public Domain. SAX comes with NO WARRANTY or guarantee of fitness for any purpose.
-
-David Megginson, Megginson Technologies Ltd.
-2000-05-05
diff --git a/options/license/SAX-PD-2.0 b/options/license/SAX-PD-2.0
deleted file mode 100644
index b329db3bb5..0000000000
--- a/options/license/SAX-PD-2.0
+++ /dev/null
@@ -1,10 +0,0 @@
-SAX2 is Free!
-
-I hereby abandon any property rights to SAX 2.0 (the Simple API for
-XML), and release all of the SAX 2.0 source code, compiled code, and
-documentation contained in this distribution into the Public Domain.
-SAX comes with NO WARRANTY or guarantee of fitness for any
-purpose.
-
-David Megginson, david@megginson.com
-2000-05-05
diff --git a/options/license/SCEA b/options/license/SCEA
deleted file mode 100644
index 48892757e9..0000000000
--- a/options/license/SCEA
+++ /dev/null
@@ -1,60 +0,0 @@
-SCEA Shared Source License 1.0
-
-Terms and Conditions:
-
-1. Definitions:
-
-"Software" shall mean the software and related documentation, whether in Source or Object Form, made available under this SCEA Shared Source license ("License"), that is indicated by a copyright notice file included in the source files or attached or accompanying the source files.
-
-"Licensor" shall mean Sony Computer Entertainment America, Inc. (herein "SCEA")
-
-"Object Code" or "Object Form" shall mean any form that results from translation or transformation of Source Code, including but not limited to compiled object code or conversions to other forms intended for machine execution.
-"Source Code" or "Source Form" shall have the plain meaning generally accepted in the software industry, including but not limited to software source code, documentation source, header and configuration files.
-
-"You" or "Your" shall mean you as an individual or as a company, or whichever form under which you are exercising rights under this License.
-
-2. License Grant.
-
-Licensor hereby grants to You, free of charge subject to the terms and conditions of this License, an irrevocable, non-exclusive, worldwide, perpetual, and royalty-free license to use, modify, reproduce, distribute, publicly perform or display the Software in Object or Source Form .
-
-3. No Right to File for Patent.
-In exchange for the rights that are granted to You free of charge under this License, You agree that You will not file for any patent application, seek copyright protection or take any other action that might otherwise impair the ownership rights in and to the Software that may belong to SCEA or any of the other contributors/authors of the Software.
-
-4. Contributions.
-
-SCEA welcomes contributions in form of modifications, optimizations, tools or documentation designed to improve or expand the performance and scope of the Software (collectively "Contributions"). Per the terms of this License You are free to modify the Software and those modifications would belong to You. You may however wish to donate Your Contributions to SCEA for consideration for inclusion into the Software. For the avoidance of doubt, if You elect to send Your Contributions to SCEA, You are doing so voluntarily and are giving the Contributions to SCEA and its parent company Sony Computer Entertainment, Inc., free of charge, to use, modify or distribute in any form or in any manner. SCEA acknowledges that if You make a donation of Your Contributions to SCEA, such Contributions shall not exclusively belong to SCEA or its parent company and such donation shall not be to Your exclusion. SCEA, in its sole discretion, shall determine whether or not to include Your donated Contributions into the Software, in whole, in part, or as modified by SCEA. Should SCEA elect to include any such Contributions into the Software, it shall do so at its own risk and may elect to give credit or special thanks to any such contributors in the attached copyright notice. However, if any of Your contributions are included into the Software, they will become part of the Software and will be distributed under the terms and conditions of this License. Further, if Your donated Contributions are integrated into the Software then Sony Computer Entertainment, Inc. shall become the copyright owner of the Software now containing Your contributions and SCEA would be the Licensor.
-
-5. Redistribution in Source Form
-
-You may redistribute copies of the Software, modifications or derivatives thereof in Source Code Form, provided that You:
-
- a. Include a copy of this License and any copyright notices with source
-
- b. Identify modifications if any were made to the Software
-
- c. Include a copy of all documentation accompanying the Software and modifications made by You
-
-6. Redistribution in Object Form
-
-If You redistribute copies of the Software, modifications or derivatives thereof in Object Form only (as incorporated into finished goods, i.e. end user applications) then You will not have a duty to include any copies of the code, this License, copyright notices, other attributions or documentation.
-
-7. No Warranty
-
-THE SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT ANY REPRESENTATIONS OR WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. YOU ARE SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USING, MODIFYING OR REDISTRIBUTING THE SOFTWARE AND ASSUME ANY RISKS ASSOCIATED WITH YOUR EXERCISE OF PERMISSIONS UNDER THIS LICENSE.
-
-8. Limitation of Liability
-
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY WILL EITHER PARTY BE LIABLE TO THE OTHER PARTY OR ANY THIRD PARTY FOR ANY DIRECT, INDIRECT, CONSEQUENTIAL, SPECIAL, INCIDENTAL, OR EXEMPLARY DAMAGES WITH RESPECT TO ANY INJURY, LOSS, OR DAMAGE, ARISING UNDER OR IN CONNECTION WITH THIS LETTER AGREEMENT, WHETHER FORESEEABLE OR UNFORESEEABLE, EVEN IF SUCH PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH INJURY, LOSS, OR DAMAGE. THE LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION SHALL APPLY TO THE FULLEST EXTENT PERMISSIBLE AT LAW OR ANY GOVERMENTAL REGULATIONS.
-
-9. Governing Law and Consent to Jurisdiction
-
-This Agreement shall be governed by and interpreted in accordance with the laws of the State of California, excluding that body of law related to choice of laws, and of the United States of America. Any action or proceeding brought to enforce the terms of this Agreement or to adjudicate any dispute arising hereunder shall be brought in the Superior Court of the County of San Mateo, State of California or the United States District Court for the Northern District of California. Each of the parties hereby submits itself to the exclusive jurisdiction and venue of such courts for purposes of any such action. In addition, each party hereby waives the right to a jury trial in any action or proceeding related to this Agreement.
-
-10. Copyright Notice for Redistribution of Source Code
-
-Copyright 2005 Sony Computer Entertainment Inc.
-
-Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at:
-http://research.scea.com/scea_shared_source_license.html
-
-Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
diff --git a/options/license/SGI-B-1.0 b/options/license/SGI-B-1.0
deleted file mode 100644
index 0c0fcce29a..0000000000
--- a/options/license/SGI-B-1.0
+++ /dev/null
@@ -1,82 +0,0 @@
-SGI FREE SOFTWARE LICENSE B
-(Version 1.0 1/25/2000)
-
-1. Definitions.
-
- 1.1 "Additional Notice Provisions" means such additional provisions as appear in the Notice in Original Code under the heading "Additional Notice Provisions."
-
- 1.2 "API" means an application programming interface established by SGI in conjunction with the Original Code.
-
- 1.3 "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
- 1.4 "Hardware" means any physical device that accepts input, processes input, stores the results of processing, and/or provides output.
-
- 1.5 "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.6 "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
- 1.7 "License" means this document.
-
- 1.8 "Modifications" means any addition to the substance or structure of the Original Code and/or any addition to or deletion from previous Modifications. When Covered Code is released as a series of files, a Modification is:
-
- A. Any addition to the contents of a file containing Original Code and/or any addition to or deletion from previous Modifications.
-
- B. Any new file that contains any part of the Original Code or previous Modifications.
-
- 1.9 "Notice" means any notice in Original Code or Covered Code, as required by and in compliance with this License.
-
- 1.10 "Original Code" means source code of computer software code which is described in the source code Notice required by Exhibit A as Original Code, and updates and error corrections specifically thereto.
-
- 1.11 "Recipient" means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 8. For legal entities, "Recipient" includes any entity which controls, is controlled by, or is under common control with Recipient. For purposes of this definition, "control" of an entity means (a) the power, direct or indirect, to direct or manage such entity, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
- 1.12 SGI" means Silicon Graphics, Inc.
-
-2. License Grant and Restrictions.
-
- 2.1v License Grant. Subject to the provisions of this License and any third party intellectual property claims, for the duration of intellectual property protections inherent in the Original Code, SGI hereby grants Recipient a worldwide, royalty-free, non-exclusive license, to do the following: (i) under copyrights Licensable by SGI, to reproduce, distribute, create derivative works from, and, to the extent applicable, display and perform the Original Code alone and/or as part of a Larger Work; and (ii) under any patent claims Licensable by SGI and embodied in the Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code. Recipient accepts the terms and conditions of this License by undertaking any of the aforementioned actions.
-
- 2.2 Restriction on Patent License. Notwithstanding the provisions of Section 2.1(ii), no patent license is granted: 1) separate from the Original Code; nor 2) for infringements caused by (i) modification of the Original Code, or (ii) the combination of the Original Code with other software or Hardware.
-
- 2.3 No License For Hardware Implementations. The licenses granted in Section 2.1 are not applicable to implementation in Hardware of the algorithms embodied in the Original Code.
-
- 2.4 Modifications License and API Compliance. Modifications are only licensed under Section 2.1(i) to the extent such Modifications are fully compliant with any API as may be identified in Additional Notice Provisions as appear in the Original Code.
-
-3. Redistributions.
-
- A. Retention of Notice/Copy of License. The Notice set forth in Exhibit A, below, must be conspicuously retained or included in any and all redistributions of Covered Code. For distributions of the Covered Code in source code form, the Notice must appear in every file that can include a text comments field; in executable form, the Notice and a copy of this License must appear in related documentation or collateral where the Recipient’s rights relating to Covered Code are described. Any Additional Notice Provisions which actually appears in the Original Code must also be retained or included in any and all redistributions of Covered Code.
-
- B. Alternative License. Provided that Recipient is in compliance with the terms of this License, Recipient may distribute the source code and/or executable version(s) of Covered Code under (1) this License; (2) a license identical to this License but for only such changes as are necessary in order to clarify Recipient’s role as licensor of Modifications, without derogation of any of SGI’s rights; and/or (3) a license of Recipient’s choosing, containing terms different from this License, provided that the license terms include this Section 3 and Sections 4, 6, 7, 10, 12, and 13, which terms may not be modified or superseded by any other terms of such license. If Recipient elects to use any license other than this License, Recipient must make it absolutely clear that any of its terms which differ from this License are offered by Recipient alone, and not by SGI.
-
- C. Indemnity. Recipient hereby agrees to indemnify SGI for any liability incurred by SGI as a result of any such alternative license terms Recipient offers.
-
-4. Termination. This License and the rights granted hereunder will terminate automatically if Recipient breaches any term herein and fails to cure such breach within 30 days thereof. Any sublicense to the Covered Code that is properly granted shall survive any termination of this License, absent termination by the terms of such sublicense. Provisions that, by their nature, must remain in effect beyond the termination of this License, shall survive.
-
-5. No Trademark Or Other Rights. This License does not grant any rights to: (i) any software apart from the Covered Code, nor shall any other rights or licenses not expressly granted hereunder arise by implication, estoppel or otherwise with respect to the Covered Code; (ii) any trade name, trademark or service mark whatsoever, including without limitation any related right for purposes of endorsement or promotion of products derived from the Covered Code, without prior written permission of SGI; or (iii) any title to or ownership of the Original Code, which shall at all times remains with SGI. All rights in the Original Code not expressly granted under this License are reserved.
-
-6. Compliance with Laws; Non-Infringement. Recipient hereby assures that it shall comply with all applicable laws, regulations, and executive orders, in connection with any and all dispositions of Covered Code, including but not limited to, all export, re-export, and import control laws, regulations, and executive orders, of the U.S. government and other countries. Recipient may not distribute Covered Code that (i) in any way infringes (directly or contributorily) the rights (including patent, copyright, trade secret, trademark or other intellectual property rights of any kind) of any other person or entity or (ii) breaches any representation or warranty, express, implied or statutory, to which, under any applicable law, it might be deemed to have been subject.
-
-7. Claims of Infringement. If Recipient learns of any third party claim that any disposition of Covered Code and/or functionality wholly or partially infringes the third party's intellectual property rights, Recipient will promptly notify SGI of such claim.
-
-8. Versions of the License. SGI may publish revised and/or new versions of the License from time to time, each with a distinguishing version number. Once Covered Code has been published under a particular version of the License, Recipient may, for the duration of the license, continue to use it under the terms of that version, or choose to use such Covered Code under the terms of any subsequent version published by SGI. Subject to the provisions of Sections 3 and 4 of this License, only SGI may modify the terms applicable to Covered Code created under this License.
-
-9. DISCLAIMER OF WARRANTY. COVERED CODE IS PROVIDED "AS IS." ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS ARE DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. SGI ASSUMES NO RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, SGI ASSUMES NO COST OR LIABILITY FOR SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY IS AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT SUBJECT TO THIS DISCLAIMER.
-
-10. LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES NOR LEGAL THEORY, WHETHER TORT (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE OR STRICT LIABILITY), CONTRACT, OR OTHERWISE, SHALL SGI OR ANY SGI LICENSOR BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, LOSS OF DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SGI's NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT EXCLUSION AND LIMITATION MAY NOT APPLY TO RECIPIENT.
-
-11. Indemnity. Recipient shall be solely responsible for damages arising, directly or indirectly, out of its utilization of rights under this License. Recipient will defend, indemnify and hold harmless Silicon Graphics, Inc. from and against any loss, liability, damages, costs or expenses (including the payment of reasonable attorneys fees) arising out of Recipient's use, modification, reproduction and distribution of the Covered Code or out of any representation or warranty made by Recipient.
-
-12. U.S. Government End Users. The Covered Code is a "commercial item" consisting of "commercial computer software" as such terms are defined in title 48 of the Code of Federal Regulations and all U.S. Government End Users acquire only the rights set forth in this License and are subject to the terms of this License.
-
-13. Miscellaneous. This License represents the complete agreement concerning the its subject matter. If any provision of this License is held to be unenforceable, such provision shall be reformed so as to achieve as nearly as possible the same legal and economic effect as the original provision and the remainder of this License will remain in effect. This License shall be governed by and construed in accordance with the laws of the United States and the State of California as applied to agreements entered into and to be performed entirely within California between California residents. Any litigation relating to this License shall be subject to the exclusive jurisdiction of the Federal Courts of the Northern District of California (or, absent subject matter jurisdiction in such courts, the courts of the State of California), with venue lying exclusively in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-Exhibit A
-
-License Applicability. Except to the extent portions of this file are made subject to an alternative license as permitted in the SGI Free Software License B, Version 1.0 (the "License"), the contents of this file are subject only to the provisions of the License. You may not use this file except in compliance with the License. You may obtain a copy of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 Ampitheatre Parkway, Mountain View, CA 94043-1351, or at:
-
-http://oss.sgi.com/projects/FreeB
-
-Note that, as provided in the License, the Software is distributed on an "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-
-Original Code. The Original Code is: [name of software, version number, and release date], developed by Silicon Graphics, Inc. The Original Code is Copyright (c) [dates of first publication, as appearing in the Notice in the Original Code] Silicon Graphics, Inc. Copyright in any portions created by third parties is as indicated elsewhere herein. All Rights Reserved.
-
-Additional Notice Provisions: [such additional provisions, if any, as appear in the Notice in the Original Code under the heading "Additional Notice Provisions"]
diff --git a/options/license/SGI-B-1.1 b/options/license/SGI-B-1.1
deleted file mode 100644
index 3f688177ef..0000000000
--- a/options/license/SGI-B-1.1
+++ /dev/null
@@ -1,84 +0,0 @@
-SGI FREE SOFTWARE LICENSE B
-(Version 1.1 02/22/2000)
-
-1. Definitions.
-
- 1.1 "Additional Notice Provisions" means such additional provisions as appear in the Notice in Original Code under the heading "Additional Notice Provisions."
-
- 1.2 "Covered Code" means the Original Code or Modifications, or any combination thereof.
-
- 1.3 "Hardware" means any physical device that accepts input, processes input, stores the results of processing, and/or provides output.
-
- 1.4 "Larger Work" means a work that combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.5 "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
- 1.6 "License" means this document.
-
- 1.7 "Licensed Patents" means patent claims Licensable by SGI that are infringed by the use or sale of Original Code or any Modifications provided by SGI, or any combination thereof.
-
- 1.8 "Modifications" means any addition to or deletion from the substance or structure of the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
-
- A. Any addition to the contents of a file containing Original Code and/or addition to or deletion from the contents of a file containing previous Modifications.
-
- B. Any new file that contains any part of the Original Code or previous Modifications.
-
- 1.9 "Notice" means any notice in Original Code or Covered Code, as required by and in compliance with this License.
-
- 1.10 "Original Code" means source code of computer software code that is described in the source code Notice required by Exhibit A as Original Code, and updates and error corrections specifically thereto.
-
- 1.11 "Recipient" means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 8. For legal entities, "Recipient" includes any entity that controls, is controlled by, or is under common control with Recipient. For purposes of this definition, "control" of an entity means (a) the power, direct or indirect, to direct or manage such entity, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
- 1.12 "Recipient Patents" means patent claims Licensable by a Recipient that are infringed by the use or sale of Original Code or any Modifications provided by SGI, or any combination thereof.
-
- 1.13 "SGI" means Silicon Graphics, Inc.
-
- 1.14 "SGI Patents" means patent claims Licensable by SGI other than the Licensed Patents.
-
-2. License Grant and Restrictions.
-
- 2.1 SGI License Grant. Subject to the terms of this License and any third party intellectual property claims, for the duration of intellectual property protections inherent in the Original Code, SGI hereby grants Recipient a worldwide, royalty-free, non-exclusive license, to do the following: (i) under copyrights Licensable by SGI, to reproduce, distribute, create derivative works from, and, to the extent applicable, display and perform the Original Code and/or any Modifications provided by SGI alone and/or as part of a Larger Work; and (ii) under any Licensable Patents, to make, have made, use, sell, offer for sale, import and/or otherwise transfer the Original Code and/or any Modifications provided by SGI. Recipient accepts the terms and conditions of this License by undertaking any of the aforementioned actions. The patent license shall apply to the Covered Code if, at the time any related Modification is added, such addition of the Modification causes such combination to be covered by the Licensed Patents. The patent license in Section 2.1(ii) shall not apply to any other combinations that include the Modification. No patent license is provided under SGI Patents for infringements of SGI Patents by Modifications not provided by SGI or combinations of Original Code and Modifications not provided by SGI.
-
- 2.2 Recipient License Grant. Subject to the terms of this License and any third party intellectual property claims, Recipient hereby grants SGI and any other Recipients a worldwide, royalty-free, non-exclusive license, under any Recipient Patents, to make, have made, use, sell, offer for sale, import and/or otherwise transfer the Original Code and/or any Modifications provided by SGI.
-
- 2.3 No License For Hardware Implementations. The licenses granted in Section 2.1 and 2.2 are not applicable to implementation in Hardware of the algorithms embodied in the Original Code or any Modifications provided by SGI .
-
-3. Redistributions.
-
- 3.1 Retention of Notice/Copy of License. The Notice set forth in Exhibit A, below, must be conspicuously retained or included in any and all redistributions of Covered Code. For distributions of the Covered Code in source code form, the Notice must appear in every file that can include a text comments field; in executable form, the Notice and a copy of this License must appear in related documentation or collateral where the Recipient’s rights relating to Covered Code are described. Any Additional Notice Provisions which actually appears in the Original Code must also be retained or included in any and all redistributions of Covered Code.
-
- 3.2 Alternative License. Provided that Recipient is in compliance with the terms of this License, Recipient may, so long as without derogation of any of SGI’s rights in and to the Original Code, distribute the source code and/or executable version(s) of Covered Code under (1) this License; (2) a license identical to this License but for only such changes as are necessary in order to clarify Recipient’s role as licensor of Modifications; and/or (3) a license of Recipient’s choosing, containing terms different from this License, provided that the license terms include this Section 3 and Sections 4, 6, 7, 10, 12, and 13, which terms may not be modified or superseded by any other terms of such license. If Recipient elects to use any license other than this License, Recipient must make it absolutely clear that any of its terms which differ from this License are offered by Recipient alone, and not by SGI. It is emphasized that this License is a limited license, and, regardless of the license form employed by Recipient in accordance with this Section 3.2, Recipient may relicense only such rights, in Original Code and Modifications by SGI, as it has actually been granted by SGI in this License.
-
- 3.3 Indemnity. Recipient hereby agrees to indemnify SGI for any liability incurred by SGI as a result of any such alternative license terms Recipient offers.
-
-4. Termination. This License and the rights granted hereunder will terminate automatically if Recipient breaches any term herein and fails to cure such breach within 30 days thereof. Any sublicense to the Covered Code that is properly granted shall survive any termination of this License, absent termination by the terms of such sublicense. Provisions that, by their nature, must remain in effect beyond the termination of this License, shall survive.
-
-5. No Trademark Or Other Rights. This License does not grant any rights to: (i) any software apart from the Covered Code, nor shall any other rights or licenses not expressly granted hereunder arise by implication, estoppel or otherwise with respect to the Covered Code; (ii) any trade name, trademark or service mark whatsoever, including without limitation any related right for purposes of endorsement or promotion of products derived from the Covered Code, without prior written permission of SGI; or (iii) any title to or ownership of the Original Code, which shall at all times remains with SGI. All rights in the Original Code not expressly granted under this License are reserved.
-
-6. Compliance with Laws; Non-Infringement. There are various worldwide laws, regulations, and executive orders applicable to dispositions of Covered Code, including without limitation export, re-export, and import control laws, regulations, and executive orders, of the U.S. government and other countries, and Recipient is reminded it is obliged to obey such laws, regulations, and executive orders. Recipient may not distribute Covered Code that (i) in any way infringes (directly or contributorily) any intellectual property rights of any kind of any other person or entity or (ii) breaches any representation or warranty, express, implied or statutory, to which, under any applicable law, it might be deemed to have been subject.
-
-7. Claims of Infringement. If Recipient learns of any third party claim that any disposition of Covered Code and/or functionality wholly or partially infringes the third party's intellectual property rights, Recipient will promptly notify SGI of such claim.
-
-8. Versions of the License. SGI may publish revised and/or new versions of the License from time to time, each with a distinguishing version number. Once Covered Code has been published under a particular version of the License, Recipient may, for the duration of the license, continue to use it under the terms of that version, or choose to use such Covered Code under the terms of any subsequent version published by SGI. Subject to the provisions of Sections 3 and 4 of this License, only SGI may modify the terms applicable to Covered Code created under this License.
-
-9. DISCLAIMER OF WARRANTY. COVERED CODE IS PROVIDED "AS IS." ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS ARE DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. SGI ASSUMES NO RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, SGI ASSUMES NO COST OR LIABILITY FOR SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY IS AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT SUBJECT TO THIS DISCLAIMER.
-
-10. LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES NOR LEGAL THEORY, WHETHER TORT (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE OR STRICT LIABILITY), CONTRACT, OR OTHERWISE, SHALL SGI OR ANY SGI LICENSOR BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, LOSS OF DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SGI's NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT EXCLUSION AND LIMITATION MAY NOT APPLY TO RECIPIENT.
-
-11. Indemnity. Recipient shall be solely responsible for damages arising, directly or indirectly, out of its utilization of rights under this License. Recipient will defend, indemnify and hold harmless Silicon Graphics, Inc. from and against any loss, liability, damages, costs or expenses (including the payment of reasonable attorneys fees) arising out of Recipient's use, modification, reproduction and distribution of the Covered Code or out of any representation or warranty made by Recipient.
-
-12. U.S. Government End Users. The Covered Code is a "commercial item" consisting of "commercial computer software" as such terms are defined in title 48 of the Code of Federal Regulations and all U.S. Government End Users acquire only the rights set forth in this License and are subject to the terms of this License.
-
-13. Miscellaneous. This License represents the complete agreement concerning the its subject matter. If any provision of this License is held to be unenforceable, such provision shall be reformed so as to achieve as nearly as possible the same legal and economic effect as the original provision and the remainder of this License will remain in effect. This License shall be governed by and construed in accordance with the laws of the United States and the State of California as applied to agreements entered into and to be performed entirely within California between California residents. Any litigation relating to this License shall be subject to the exclusive jurisdiction of the Federal Courts of the Northern District of California (or, absent subject matter jurisdiction in such courts, the courts of the State of California), with venue lying exclusively in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation that provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-Exhibit A
-
-License Applicability. Except to the extent portions of this file are made subject to an alternative license as permitted in the SGI Free Software License B, Version 1.1 (the "License"), the contents of this file are subject only to the provisions of the License. You may not use this file except in compliance with the License. You may obtain a copy of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-
-http://oss.sgi.com/projects/FreeB
-
-Note that, as provided in the License, the Software is distributed on an "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-
-Original Code. The Original Code is: [name of software, version number, and release date], developed by Silicon Graphics, Inc. The Original Code is Copyright (c) [dates of first publication, as appearing in the Notice in the Original Code] Silicon Graphics, Inc. Copyright in any portions created by third parties is as indicated elsewhere herein. All Rights Reserved.
-
-Additional Notice Provisions: [such additional provisions, if any, as appear in the Notice in the Original Code under the heading "Additional Notice Provisions"]
diff --git a/options/license/SGI-B-2.0 b/options/license/SGI-B-2.0
deleted file mode 100644
index 19a4a9e985..0000000000
--- a/options/license/SGI-B-2.0
+++ /dev/null
@@ -1,12 +0,0 @@
-SGI FREE SOFTWARE LICENSE B
-(Version 2.0, Sept. 18, 2008)
-
-Copyright (C) [dates of first publication] Silicon Graphics, Inc. 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 including the dates of first publication and either this permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-Except as contained in this notice, the name of Silicon Graphics, Inc. shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Silicon Graphics, Inc.
diff --git a/options/license/SGI-OpenGL b/options/license/SGI-OpenGL
deleted file mode 100644
index 2b4c542aa0..0000000000
--- a/options/license/SGI-OpenGL
+++ /dev/null
@@ -1,34 +0,0 @@
-(c) Copyright 1993, Silicon Graphics, Inc.
-ALL RIGHTS RESERVED
-Permission to use, copy, modify, and distribute this software for
-any purpose and without fee is hereby granted, provided that the above
-copyright notice appear in all copies and that both the copyright notice
-and this permission notice appear in supporting documentation, and that
-the name of Silicon Graphics, Inc. not be used in advertising
-or publicity pertaining to distribution of the software without specific,
-written prior permission.
-
-THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "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 SILICON
-GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
-SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
-KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
-LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
-THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN
-ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
-POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
-
-US Government Users Restricted Rights
-Use, duplication, or disclosure by the Government is subject to
-restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
-(c)(1)(ii) of the Rights in Technical Data and Computer Software
-clause at DFARS 252.227-7013 and/or in similar or successor
-clauses in the FAR or the DOD or NASA FAR Supplement.
-Unpublished-- rights reserved under the copyright laws of the
-United States. Contractor/manufacturer is Silicon Graphics,
-Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311.
-
-OpenGL(TM) is a trademark of Silicon Graphics, Inc.
diff --git a/options/license/SGP4 b/options/license/SGP4
deleted file mode 100644
index 1b86e057c7..0000000000
--- a/options/license/SGP4
+++ /dev/null
@@ -1 +0,0 @@
-There is no license associated with the code and you may use it for any purpose—personal or commercial—as you wish. We ask only that you include citations in your documentation and source code to show the source of the code and provide links to the main page, to facilitate communications regarding any questions on the theory or source code.
diff --git a/options/license/SHL-0.5 b/options/license/SHL-0.5
deleted file mode 100644
index 52dee6fac4..0000000000
--- a/options/license/SHL-0.5
+++ /dev/null
@@ -1,64 +0,0 @@
-SOLDERPAD HARDWARE LICENSE version 0.5
-
-This license is based closely on the Apache License Version 2.0, but is not approved or endorsed by the Apache Foundation. A copy of the non-modified Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0.
-
-As this license is not currently OSI or FSF approved, the Licensor permits any Work licensed under this License, at the option of the Licensee, to be treated as licensed under the Apache License Version 2.0 (which is so approved).
-
-This License is licensed under the terms of this License and in particular clause 7 below (Disclaimer of Warranties) applies in relation to its use.
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-1. Definitions.
-
-"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
-
-"Licensor" shall mean the Rights owner or entity authorized by the Rights owner that is granting the License.
-
-"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
-
-“Rights†means copyright and any similar right including design right (whether registered or unregistered), semiconductor topography (mask) rights and database extraction rights (but excluding Patents and Trademarks).
-
-"Source" form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files.
-
-"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, artwork and semiconductor topographies (mask works).
-
-"Work" shall mean the work of authorship, whether in Source form or other Object form, made available under the License, as indicated by a Rights notice that is included in or attached to the work (an example is provided in the Appendix below).
-
-"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) or physically connect to or interoperate with the interfaces of, the Work and Derivative Works thereof.
-
-"Contribution" shall mean any design or work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the Rights owner or by an individual or Legal Entity authorized to submit on behalf of the Rights owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the Rights owner as "Not a Contribution."
-
-"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
-
-2. Grant of License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist.
-
-3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
-
-4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
-
-You must give any other recipients of the Work or Derivative Works a copy of this License; and
-
-You must cause any modified files to carry prominent notices stating that You changed the files; and
-
-You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
-
-If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
-
-5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
-
-6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
-
-END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply this license to your work
-To apply this license to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
-
-Copyright [yyyy] [name of copyright owner] Copyright and related rights are licensed under the Solderpad Hardware License, Version 0.5 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://solderpad.org/licenses/SHL-0.5. Unless required by applicable law or agreed to in writing, software, hardware and materials distributed under this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
diff --git a/options/license/SHL-0.51 b/options/license/SHL-0.51
deleted file mode 100644
index 548639d16e..0000000000
--- a/options/license/SHL-0.51
+++ /dev/null
@@ -1,65 +0,0 @@
-SOLDERPAD HARDWARE LICENSE version 0.51
-
-This license is based closely on the Apache License Version 2.0, but is not approved or endorsed by the Apache Foundation. A copy of the non-modified Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0.
-
-As this license is not currently OSI or FSF approved, the Licensor permits any Work licensed under this License, at the option of the Licensee, to be treated as licensed under the Apache License Version 2.0 (which is so approved).
-
-This License is licensed under the terms of this License and in particular clause 7 below (Disclaimer of Warranties) applies in relation to its use.
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-1. Definitions.
-
-"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
-
-"Licensor" shall mean the Rights owner or entity authorized by the Rights owner that is granting the License.
-
-"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
-
-"Rights" means copyright and any similar right including design right (whether registered or unregistered), semiconductor topography (mask) rights and database rights (but excluding Patents and Trademarks).
-
-"Source" form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files.
-
-"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, artwork and semiconductor topographies (mask works).
-
-"Work" shall mean the work of authorship, whether in Source form or other Object form, made available under the License, as indicated by a Rights notice that is included in or attached to the work (an example is provided in the Appendix below).
-
-"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) or physically connect to or interoperate with the interfaces of, the Work and Derivative Works thereof.
-
-"Contribution" shall mean any design or work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the Rights owner or by an individual or Legal Entity authorized to submit on behalf of the Rights owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the Rights owner as "Not a Contribution."
-
-"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
-
-2. Grant of License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist.
-
-3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
-
-4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
-
- 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and
-
- 2. You must cause any modified files to carry prominent notices stating that You changed the files; and
-
- 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
-
- 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
-
-5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
-
-6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
-
-END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply this license to your work
-
-To apply this license to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
-
-Copyright [yyyy] [name of copyright owner] Copyright and related rights are licensed under the Solderpad Hardware License, Version 0.51 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law or agreed to in writing, software, hardware and materials distributed under this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
diff --git a/options/license/SHL-2.0 b/options/license/SHL-2.0
deleted file mode 100644
index 9218b47a72..0000000000
--- a/options/license/SHL-2.0
+++ /dev/null
@@ -1,22 +0,0 @@
-# Solderpad Hardware Licence Version 2.0
-
-This licence (the “Licenceâ€) operates as a wraparound licence to the Apache License Version 2.0 (the “Apache Licenseâ€) and grants to You the rights, and imposes the obligations, set out in the Apache License (which can be found here: http://apache.org/licenses/LICENSE-2.0), with the following extensions. It must be read in conjunction with the Apache License. Section 1 below modifies definitions in the Apache License, and section 2 below replaces sections 2 of the Apache License. You may, at your option, choose to treat any Work released under this License as released under the Apache License (thus ignoring all sections written below entirely). Words in italics indicate changes rom the Apache License, but are indicative and not to be taken into account in interpretation.
-
-1. The definitions set out in the Apache License are modified as follows:
-
-Copyright any reference to ‘copyright’ (whether capitalised or not) includes ‘Rights’ (as defined below).
-
-Contribution also includes any design, as well as any work of authorship.
-
-Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the interfaces of the Work and Derivative Works thereof.
-
-Object form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works).
-
-Rights means copyright and any similar right including design right (whether registered or unregistered), semiconductor topography (mask) rights and database rights (but excluding Patents and Trademarks).
-
-Source form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files.
-Work also includes a design or work of authorship, whether in Source form or other Object form.
-
-2. Grant of Licence
-
-2.1 Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, make, adapt, repair, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist.
diff --git a/options/license/SHL-2.1 b/options/license/SHL-2.1
deleted file mode 100644
index c9ae53741f..0000000000
--- a/options/license/SHL-2.1
+++ /dev/null
@@ -1,45 +0,0 @@
-SOLDERPAD HARDWARE LICENSE VERSION 2.1
-
-This license operates as a wraparound license to the Apache License Version 2.0 (the "Apache License") and incorporates the terms and conditions of the Apache License (which can be found here: http://apache.org/licenses/LICENSE-2.0), with the following additions and modifications. It must be read in conjunction with the Apache License. Section 1 below modifies definitions and terminology in the Apache License and Section 2 below replaces Section 2 of the Apache License. The Appendix replaces the Appendix in the Apache License. You may, at your option, choose to treat any Work released under this license as released under the Apache License (thus ignoring all sections written below entirely).
-
-1. Terminology in the Apache License is supplemented or modified as follows:
-
-"Authorship": any reference to 'authorship' shall be taken to read "authorship or design".
-
-"Copyright owner": any reference to 'copyright owner' shall be taken to read "Rights owner".
-
-"Copyright statement": the reference to 'copyright statement' shall be taken to read 'copyright or other statement pertaining to Rights'
-
-The following new definition shall be added to the Definitions section of the Apache License:
-
-"Rights" means copyright and any similar right including design right (whether registered or unregistered), rights in semiconductor topographies (mask works) and database rights (but excluding Patents and Trademarks).
-
-The following definitions shall replace the corresponding definitions in the Apache License:
-
-"License" shall mean this Solderpad Hardware License version 2.1, being the terms and conditions for use, manufacture, instantiation, adaptation, reproduction, and distribution as defined by Sections 1 through 9 of this document.
-
-"Licensor" shall mean the Rights owner or entity authorized by the Rights owner that is granting the License.
-
-"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship or design. For the purposes of this License, Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the Work and Derivative Works thereof.
-
-"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object or material and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works).
-
-"Source" form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files.
-
-"Work" shall mean the work of authorship or design, whether in Source or Object form, made available under the License, as indicated by a notice relating to Rights that is included in or attached to the work (an example is provided in the Appendix below).
-
-2. Grant of License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, make, adapt, repair, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist.
-
-
-APPENDIX
-
-Copyright [yyyy] [name of copyright owner]
-SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
-
-Licensed under the Solderpad Hardware License v 2.1 (the "License"); you may not use this file except in compliance with the License, or, at your option, the Apache License version 2.0.
-You may obtain a copy of the License at
-
-https://solderpad.org/licenses/SHL-2.1/
-
-Unless required by applicable law or agreed to in writing, any work distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and limitations under the License.
diff --git a/options/license/SISSL b/options/license/SISSL
deleted file mode 100644
index af38d02d92..0000000000
--- a/options/license/SISSL
+++ /dev/null
@@ -1,116 +0,0 @@
-Sun Industry Standards Source License - Version 1.1
-
-1.0 DEFINITIONS
-
- 1.1 "Commercial Use" means distribution or otherwise making the Original Code available to a third party.
-
- 1.2 "Contributor Version" means the combination of the Original Code, and the Modifications made by that particular Contributor.
-
- 1.3 "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
- 1.4 "Executable" means Original Code in any form other than Source Code.
-
- 1.5 "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
- 1.6 "Larger Work" means a work which combines Original Code or portions thereof with code not governed by the terms of this License.
-
- 1.7 "License" means this document.
-
- 1.8 "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
- 1.9 "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. A Modification is:
-
- A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
-
- B. Any new file that contains any part of the Original Code or previous Modifications.
-
- 1.10 "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code.
-
- 1.11 "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
- 1.12 "Source Code" means the preferred form of the Original Code for making modifications to it, including all modules it contains, plus any associated interface definition files, or scripts used to control compilation and installation of an Executable.
-
- 1.13 "Standards" means the standards identified in Exhibit B.
-
- 1.14 "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You'' includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control'' means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2.0 SOURCE CODE LICENSE
-
- 2.1 The Initial Developer Grant The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and
-
- (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).
- (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.
- (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices, including but not limited to Modifications.
-
-3.0 DISTRIBUTION OBLIGATIONS
-
- 3.1 Application of License. The Source Code version of Original Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. Your license for shipment of the Contributor Version is conditioned upon Your full compliance with this Section. The Modifications which You create must comply with all requirements set out by the Standards body in effect one hundred twenty (120) days before You ship the Contributor Version. In the event that the Modifications do not meet such requirements, You agree to publish either (i) any deviation from the Standards protocol resulting from implementation of Your Modifications and a reference implementation of Your Modifications or (ii) Your Modifications in Source Code form, and to make any such deviation and reference implementation or Modifications available to all third parties under the same terms as this license on a royalty free basis within thirty (30) days of Your first customer shipment of Your Modifications.
-
- 3.2 Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add Your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Initial Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Your version of the Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer for any liability incurred by the Initial Developer as a result of warranty, support, indemnity or liability terms You offer.
-
- 3.3 Distribution of Executable Versions. You may distribute Original Code in Executable and Source form only if the requirements of Sections 3.1 and 3.2 have been met for that Original Code, and if You include a notice stating that the Source Code version of the Original Code is available under the terms of this License. The notice must be conspicuously included in any notice in an Executable or Source versions, related documentation or collateral in which You describe recipients' rights relating to the Original Code. You may distribute the Executable and Source versions of Your version of the Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License. If You distribute the Executable and Source versions under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer. You hereby agree to indemnify the Initial Developer for any liability incurred by the Initial Developer as a result of any such terms You offer.
-
- 3.4 Larger Works. You may create a Larger Work by combining Original Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Original Code.
-
-4.0 INABILITY TO COMPLY DUE TO STATUTE OR REGULATION
-
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Original Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.2 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5.0 APPLICATION OF THIS LICENSE
-
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Modifications as set out in Section 3.1.
-
-6.0 VERSIONS OF THE LICENSE
-
- 6.1 New Versions. Sun may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
- 6.2 Effect of New Versions. Once Original Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Original Code under the terms of any subsequent version of the License published by Sun. No one other than Sun has the right to modify the terms applicable to Original Code.
-
-7.0 DISCLAIMER OF WARRANTY
-
-ORIGINAL CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE ORIGINAL CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE ORIGINAL CODE IS WITH YOU. SHOULD ANY ORIGINAL CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY ORIGINAL CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8.0 TERMINATION
-
- 8.1 This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Original Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
- 8.2 In the event of termination under Section 8.1 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-9.0 LIMIT OF LIABILITY
-
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF ORIGINAL CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10.0 U.S. GOVERNMENT END USERS
-
-U.S. Government: If this Software is being acquired by or on behalf of the U.S. Government or by a U.S. Government prime contractor or subcontractor (at any tier), then the Government's rights in the Software and accompanying documentation shall be only as set forth in this license; this is in accordance with 48 C.F.R. 227.7201 through 227.7202-4 (for Department of Defense (DoD) acquisitions) and with 48 C.F.R. 2.101 and 12.212 (for non-DoD acquisitions).
-
-11.0 MISCELLANEOUS
-
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-EXHIBIT A - Sun Standards License
-
-"The contents of this file are subject to the Sun Standards License Version 1.1 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at _______________________________.
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
-express or implied. See the License for the specific language governing rights and limitations under the License.
-
-The Original Code is ______________________________________.
-
-The Initial Developer of the Original Code is:
-Sun Microsystems, Inc..
-
-Portions created by: _______________________________________
-
-are Copyright (C): _______________________________________
-
-All Rights Reserved.
-
-Contributor(s): _______________________________________
-EXHIBIT B - Standards
-
-The Standard is defined as the following:
-OpenOffice.org XML File Format Specification, located at http://xml.openoffice.org
-OpenOffice.org Application Programming Interface Specification, located at http://api.openoffice.org
diff --git a/options/license/SISSL-1.2 b/options/license/SISSL-1.2
deleted file mode 100644
index 0809ea147a..0000000000
--- a/options/license/SISSL-1.2
+++ /dev/null
@@ -1,113 +0,0 @@
-SUN INDUSTRY STANDARDS SOURCE LICENSE
-Version 1.2
-1.0 DEFINITIONS
-
- 1.1 Commercial Use means distribution or otherwise making the Original Code available to a third party.
-
- 1.2 Contributor Version means the combination of the Original Code, and the Modifications made by that particular Contributor.
-
- 1.3 Electronic Distribution Mechanism means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
- 1.4 Executable means Original Code in any form other than Source Code.
-
- 1.5 Initial Developer means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
- 1.6 Larger Work means a work which combines Original Code or portions thereof with code not governed by the terms of this License.
-
- 1.7 License means this document.
-
- 1.8 Licensable means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
- 1.9 Modifications means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. A Modification is:
-
- A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
-
- B. Any new file that contains any part of the Original Code or previous Modifications.
-
- 1.10 Original Code means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code.
-
- 1.11 Patent Claims means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
- 1.12 Source Code means the preferred form of the Original Code for making modifications to it, including all modules it contains, plus any associated interface definition files, or scripts used to control compilation and installation of an Executable.
-
- 1.13 Standards means the standards identified in Exhibit B.
-
- 1.14 You (or Your) means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, You includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, control means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2.0 SOURCE CODE LICENSE
-
- 2.1 The Initial Developer Grant The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a)under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and
-
- (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).
-
- (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.
-
- (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices, including but not limited to Modifications.
-
-3.0 DISTRIBUTION OBLIGATIONS
-
- 3.1 Application of License. The Source Code version of Original Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients rights hereunder. Your license for shipment of the Contributor Version is conditioned upon Your full compliance with this Section. The Modifications which You create must comply with all requirements set out by the Standards body in effect one hundred twenty (120) days before You ship the Contributor Version. In the event that the Modifications do not meet such requirements, You agree to publish either (i) any deviation from the Standards protocol resulting from implementation of Your Modifications and a reference implementation of Your Modifications or (ii) Your Modifications in Source Code form, and to make any such deviation and reference implementation or Modifications available to all third parties under the same terms a this license on a royalty free basis within thirty (30) days of Your first customer shipment of Your Modifications. Additionally, in the event that the Modifications you create do not meet the requirements set out in this Section, You agree to comply with the Standards requirements set out in Exhibit B.
-
- 3.2 Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add Your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients rights or ownership rights relating to Initial Code.
-
- You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Your version of the Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer for any liability incurred by the Initial Developer as a result of warranty, support, indemnity or liability terms You offer.
-
- 3.3 Distribution of Executable Versions. You may distribute Original Code in Executable and Source form only if the requirements of Sections 3.1 and 3.2 have been met for that Original Code, and if You include a notice stating that the Source Code version of the Original Code is available under the terms of this License. The notice must be conspicuously included in any notice in an Executable or Source versions, related documentation or collateral in which You describe recipients rights relating to the Original Code. You may distribute the Executable and Source versions of Your version of the Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License. If You distribute the Executable and Source versions under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer. You hereby agree to indemnify the Initial Developer for any liability incurred by the Initial Developer as a result of any such terms You offer.
-
- 3.4 Larger Works. You may create a Larger Work by combining Original Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Original Code.
-
-4.0 INABILITY TO COMPLY DUE TO STATUTE OR REGULATION
-
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Original Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.2 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5.0 APPLICATION OF THIS LICENSE
-
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Modifications as set out in Section 3.1.
-
-6.0 VERSIONS OF THE LICENSE
-
- 6.1 New Versions. Sun may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
- 6.2 Effect of New Versions. Once Original Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Original Code under the terms of any subsequent version of the License published by Sun. No one other than Sun has the right to modify the terms applicable to Original Code.
-
-7.0 DISCLAIMER OF WARRANTY
-
-ORIGINAL CODE IS PROVIDED UNDER THIS LICENSE ON AN AS IS BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE ORIGINAL CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE ORIGINAL CODE IS WITH YOU. SHOULD ANY ORIGINAL CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY ORIGINAL CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8.0 TERMINATION
-
- 8.1 This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Original Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. 8.2 In the event of termination under Section 8.1 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-
-EXHIBIT A - Sun Industry Standards Source License
-
-"The contents of this file are subject to the Sun Industry Standards Source License Version 1.2 (the License); You
-may not use this file except in compliance with the License."
-
-"You may obtain a copy of the License at gridengine.sunsource.net/license.html"
-
-"Software distributed under the License is distributed on an AS IS basis, WITHOUT WARRANTY OF ANY KIND, either express or
-implied. See the License for the specific language governing rights and limitations under the License."
-
-"The Original Code is Grid Engine."
-
-"The Initial Developer of the Original Code is:
-Sun Microsystems, Inc."
-
-"Portions created by: Sun Microsystems, Inc. are Copyright (C) 2001 Sun Microsystems, Inc."
-
-"All Rights Reserved."
-
-"Contributor(s):__________________________________"
-
-EXHIBIT B - Standards
-
-1.0 Requirements for project Standards. The requirements for project Standards are version-dependent and are defined at: Grid Engine standards.
-
-2.0 Additional requirements. The additional requirements pursuant to Section 3.1 are defined as:
-
- 2.1 Naming Conventions. If any of your Modifications do not meet the requirements of the Standard, then you must change the product name so that Grid Engine, gridengine, gridengine.sunsource, and similar naming conventions are not used.
-
- 2.2 Compliance Claims. If any of your Modifications do not meet the requirements of the Standards you may not claim, directly or indirectly, that your implementation of the Standards is compliant.
diff --git a/options/license/SL b/options/license/SL
deleted file mode 100644
index cc3857d224..0000000000
--- a/options/license/SL
+++ /dev/null
@@ -1,4 +0,0 @@
-Everyone is permitted to do anything on this program including copying,
-modifying, and improving, unless you try to pretend that you wrote it.
-i.e., the above copyright notice has to appear in all copies.
-THE AUTHOR DISCLAIMS ANY RESPONSIBILITY WITH REGARD TO THIS SOFTWARE.
diff --git a/options/license/SMAIL-GPL b/options/license/SMAIL-GPL
deleted file mode 100644
index be799ec39d..0000000000
--- a/options/license/SMAIL-GPL
+++ /dev/null
@@ -1,144 +0,0 @@
-SMAIL GENERAL PUBLIC LICENSE
- (Clarified 11 Feb 1988)
-
- Copyright (C) 1988 Landon Curt Noll & Ronald S. Karr
- Copyright (C) 1992 Ronald S. Karr
- Copyleft (GNU) 1988 Landon Curt Noll & Ronald S. Karr
-
- Everyone is permitted to copy and distribute verbatim copies
- of this license, but changing it is not allowed. You can also
- use this wording to make the terms for other programs.
-
- The license agreements of most software companies keep you at the
-mercy of those companies. By contrast, our general public license is
-intended to give everyone the right to share SMAIL. To make sure that
-you get the rights we want you to have, we need to make restrictions
-that forbid anyone to deny you these rights or to ask you to surrender
-the rights. Hence this license agreement.
-
- Specifically, we want to make sure that you have the right to give
-away copies of SMAIL, that you receive source code or else can get it
-if you want it, that you can change SMAIL or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To make sure that everyone has such rights, we have to forbid you to
-deprive anyone else of these rights. For example, if you distribute
-copies of SMAIL, you must give the recipients all the rights that you
-have. You must make sure that they, too, receive or can get the
-source code. And you must tell them their rights.
-
- Also, for our own protection, we must make certain that everyone
-finds out that there is no warranty for SMAIL. If SMAIL is modified by
-someone else and passed on, we want its recipients to know that what
-they have is not what we distributed, so that any problems introduced
-by others will not reflect on our reputation.
-
- Therefore we (Landon Curt Noll and Ronald S. Karr) make the following
-terms which say what you must do to be allowed to distribute or change
-SMAIL.
-
-
- COPYING POLICIES
-
- 1. You may copy and distribute verbatim copies of SMAIL source code
-as you receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy a valid copyright notice "Copyright
-(C) 1988 Landon Curt Noll & Ronald S. Karr" (or with whatever year is
-appropriate); keep intact the notices on all files that refer to this
-License Agreement and to the absence of any warranty; and give any
-other recipients of the SMAIL program a copy of this License
-Agreement along with the program. You may charge a distribution fee
-for the physical act of transferring a copy.
-
- 2. You may modify your copy or copies of SMAIL or any portion of it,
-and copy and distribute such modifications under the terms of
-Paragraph 1 above, provided that you also do the following:
-
- a) cause the modified files to carry prominent notices stating
- that you changed the files and the date of any change; and
-
- b) cause the whole of any work that you distribute or publish,
- that in whole or in part contains or is a derivative of SMAIL or
- any part thereof, to be licensed at no charge to all third
- parties on terms identical to those contained in this License
- Agreement (except that you may choose to grant more extensive
- warranty protection to some or all third parties, at your option).
-
- c) You may charge a distribution fee for the physical act of
- transferring a copy, and you may at your option offer warranty
- protection in exchange for a fee.
-
-Mere aggregation of another unrelated program with this program (or its
-derivative) on a volume of a storage or distribution medium does not bring
-the other program under the scope of these terms.
-
- 3. You may copy and distribute SMAIL (or a portion or derivative of it,
-under Paragraph 2) in object code or executable form under the terms of
-Paragraphs 1 and 2 above provided that you also do one of the following:
-
- a) accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of
- Paragraphs 1 and 2 above; or,
-
- b) accompany it with a written offer, valid for at least three
- years, to give any third party free (except for a nominal
- shipping charge) a complete machine-readable copy of the
- corresponding source code, to be distributed under the terms of
- Paragraphs 1 and 2 above; or,
-
- c) accompany it with the information you received as to where the
- corresponding source code may be obtained. (This alternative is
- allowed only for non-commercial distribution and only if you
- received the program in object code or executable form alone.)
-
-For an executable file, complete source code means all the source code for
-all modules it contains; but, as a special exception, it need not include
-source code for modules which are standard libraries that accompany the
-operating system on which the executable file runs.
-
- 4. You may not copy, sublicense, distribute or transfer SMAIL
-except as expressly provided under this License Agreement. Any attempt
-otherwise to copy, sublicense, distribute or transfer SMAIL is void and
-your rights to use the program under this License agreement shall be
-automatically terminated. However, parties who have received computer
-software programs from you with this License Agreement will not have
-their licenses terminated so long as such parties remain in full compliance.
-
- 5. If you wish to incorporate parts of SMAIL into other free
-programs whose distribution conditions are different, write to Landon
-Curt Noll & Ronald S. Karr via the Free Software Foundation at 51
-Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. We have not yet
-worked out a simple rule that can be stated here, but we will often
-permit this. We will be guided by the two goals of preserving the
-free status of all derivatives of our free software and of promoting
-the sharing and reuse of software.
-
-Your comments and suggestions about our licensing policies and our
-software are welcome! This contract was based on the contract made by
-the Free Software Foundation. Please contact the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
-USA, or call (617) 542-5942 for details on copylefted material in
-general.
-
- NO WARRANTY
-
- BECAUSE SMAIL IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY NO
-WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING, LANDON CURT NOLL & RONALD S. KARR AND/OR
-OTHER PARTIES PROVIDE SMAIL "AS IS" WITHOUT WARRANTY OF ANY KIND,
-EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF SMAIL IS WITH
-YOU. SHOULD SMAIL PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
-NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL LANDON CURT NOLL &
-RONALD S. KARR AND/OR ANY OTHER PARTY WHO MAY MODIFY AND REDISTRIBUTE
-SMAIL AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-LOST PROFITS, LOST MONIES, OR OTHER SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
-(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
-INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A FAILURE OF THE
-PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) SMAIL, EVEN IF YOU HAVE
-BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY
-ANY OTHER PARTY.
diff --git a/options/license/SMLNJ b/options/license/SMLNJ
deleted file mode 100644
index 8b49442a62..0000000000
--- a/options/license/SMLNJ
+++ /dev/null
@@ -1,7 +0,0 @@
-STANDARD ML OF NEW JERSEY COPYRIGHT NOTICE, LICENSE AND DISCLAIMER.
-
-Copyright (c) 2001-2011 by The Fellowship of SML/NJ Copyright (c) 1989-2001 by Lucent Technologies
-
-Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both the copyright notice and this permission notice and warranty disclaimer appear in supporting documentation, and that the name of Lucent Technologies, Bell Labs or any Lucent entity not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.
-
-Lucent disclaims all warranties with regard to this software, including all implied warranties of merchantability and fitness. In no event shall Lucent be liable for any special, indirect or consequential damages or any damages whatsoever resulting from loss of use, data or profits, whether in an action of contract, negligence or other tortious action, arising out of or in connection with the use or performance of this software.
diff --git a/options/license/SMPPL b/options/license/SMPPL
deleted file mode 100644
index 1780a306d7..0000000000
--- a/options/license/SMPPL
+++ /dev/null
@@ -1,29 +0,0 @@
-Secure Messaging Protocol (SMP) Libraries [ACL, CML, SFL]
-
-Distribution Rights
-
-All source code for the SMP is being provided at no cost and with no financial limitations regarding its use and distribution. Organizations can use the SMP without paying any royalties or licensing fees. The SMP was originally developed by the U.S. Government. BAE Systems is enhancing and supporting the SMP under contract to the U.S. Government. The U.S. Government is furnishing the SMP software at no cost to the vendor subject to the conditions of the SMP Public License provided with the SMP software.
-
-29 May 2002
-
-Secure Messaging Protocol (SMP) Public License
-
-The United States Government/Department of Defense/National Security Agency/Office of Network Security (collectively "the U.S. Government") hereby grants permission to any person obtaining a copy of the SMP source and object files (the "SMP Software") and associated documentation files (the "SMP Documentation"), or any portions thereof, to do the following, subject to the following license conditions:
-
-You may, free of charge and without additional permission from the U.S. Government, use, copy, modify, sublicense and otherwise distribute the SMP Software or components of the SMP Software, with or without modifications developed by you and/or by others.
-
-You may, free of charge and without additional permission from the U.S. Government, distribute copies of the SMP Documentation, with or without modifications developed by you and/or by others, at no charge or at a charge that covers the cost of reproducing such copies, provided that this SMP Public License is retained.
-
-Furthermore, if you distribute the SMP Software or parts of the SMP Software, with or without modifications developed by you and/or others, then you must either make available the source code to all portions of the SMP Software (exclusive of any modifications made by you and/or by others) upon request, or instead you may notify anyone requesting the SMP Software source code that it is freely available from the U.S. Government.
-
-Transmission of this SMP Public License must accompany whatever portions of the SMP Software you redistribute.
-
-The SMP Software is provided without warranty or guarantee of any nature, express or implied, including without limitation the warranties of merchantability and fitness for a particular purpose.
-
-The U.S. Government cannot be held liable for any damages either directly or indirectly caused by the use of the SMP Software.
-
-It is not permitted to copy, sublicense, distribute or transfer any of the SMP Software except as expressly indicated herein. Any attempts to do otherwise will be considered a violation of this License and your rights to the SMP Software will be voided.
-
-The SMP uses the Enhanced SNACC (eSNACC) Abstract Syntax Notation One (ASN.1) C++ Library to ASN.1 encode and decode security-related data objects. The eSNACC ASN.1 C++ Library is covered by the ENHANCED SNACC SOFTWARE PUBLIC LICENSE. None of the GNU public licenses apply to the eSNACC ASN.1 C++ Library. The eSNACC Compiler is not distributed as part of the SMP.
-
-Copyright © 1997-2002 National Security Agency
diff --git a/options/license/SNIA b/options/license/SNIA
deleted file mode 100644
index 285798e827..0000000000
--- a/options/license/SNIA
+++ /dev/null
@@ -1,122 +0,0 @@
-STORAGE NETWORKING INDUSTRY ASSOCIATION
-PUBLIC LICENSE
-Version 1.1
-
-1. Definitions.
-
-1.1 "Commercial Use" means distribution or otherwise making the Covered Code available to a third party.
-
-1.2 "Contributor" means each entity that creates or contributes to the creation of Modifications.
-
-1.3 "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
-1.4 "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
-1.5 "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
-1.6 "Executable" means Covered Code in any form other than Source Code.
-
-1.7 "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
-1.8 "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
-1.9 "License" means this document.
-
-1.10 "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
-1.11 "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
- A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
- B. Any new file that contains any part of the Original Code or previous Modifications.
-
-1.12 "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
-1.13 "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
-1.14 "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
-1.15 "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity
-
-2. Source Code License.
-
-2.1 The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
- (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and
- (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).
- (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.
- (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices.
-
-2.2 Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license
- (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and
- (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
- (c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first makes Commercial Use of the Covered Code.
- (d) Notwithstanding Section 2.2(b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor.
-
-3. Distribution Obligations.
-
-3.1 Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
-3.2 Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
-3.3 Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
-3.4 Intellectual Property Matters.
- (a) Third Party Claims. If Contributor has actual knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter.
- (b) Contributor API's. If Contributor's Modifications include an application programming interface and Contributor has actual knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file.
- (c) Representations. Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.
-
-3.5 Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be most likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability (excluding any liability arising from intellectual property claims relating to the Covered Code) incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
-3.6 Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligation of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability (excluding any liability arising from intellectual property claims relating to the Covered Code) incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
-3.7 Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation. If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Application of this License. This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code.
-
-6. Versions of the License.
-
-6.1 New Versions. The Storage Networking Industry Association (the "SNIA") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
-6.2 Effect of New Versions. Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by the SNIA. No one other than the SNIA has the right to modify the terms applicable to Covered Code created under this License.
-
-6.3 Derivative Works. If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases "Storage Networking Industry Association," "SNIA," or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the SNIA Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
-7. DISCLAIMER OF WARRANTY. COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-
-8.1 This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within a reasonable time after becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-8.2 If You initiate litigation by asserting a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant") alleging that: o (a) such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.
-
-8.3 If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
-8.4 In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS. The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
-11. MISCELLANEOUS This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-12. RESPONSIBILITY FOR CLAIMS. As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-13. MULTIPLE-LICENSED CODE. Initial Developer may designate portions of the Covered Code as "Multiple-Licensed". "Multiple-Licensed" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of this License or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A.
-
-14. ACCEPTANCE. This License is accepted by You if You retain, use, or distribute the Covered Code for any purpose.
-
-EXHIBIT A The SNIA Public License.
-
-The contents of this file are subject to the SNIA Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
-
-www.snia.org/smi/developers/cim/
-
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-The Original Code is .
-
-The Initial Developer of the Original Code is [COMPLETE THIS] .
-
-Contributor(s): ______________________________________.
-
-Read more about this license at http://www.snia.org/smi/developers/open_source/
diff --git a/options/license/SPL-1.0 b/options/license/SPL-1.0
deleted file mode 100644
index 2ee64f3ef3..0000000000
--- a/options/license/SPL-1.0
+++ /dev/null
@@ -1,149 +0,0 @@
-SUN PUBLIC LICENSE Version 1.0
-
-1. Definitions.
-
- 1.0.1. "Commercial Use" means distribution or otherwise making the Covered Code available to a third party.
-
- 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications.
-
- 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
- 1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof and corresponding documentation released with the source code.
-
- 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
- 1.5. "Executable" means Covered Code in any form other than Source Code.
-
- 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
- 1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.8. "License" means this document.
-
- 1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
- 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
-
- A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
-
- B. Any new file that contains any part of the Original Code or previous Modifications.
-
- 1.10. "Original Code"../ means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
- 1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
- 1.11. "Source Code"../ means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated documentation, interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
- 1.12. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control"../ means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2. Source Code License.
-
- 2.1 The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
-
- (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and
-
- (b) under Patent Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).
-
- (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.
-
- (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by:
-
- i) the modification of the Original Code or
-
- ii) the combination of the Original Code with other software or devices.
-
- 2.2. Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license
-
- (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and
-
- b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
-
- (c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first makes Commercial Use of the Covered Code.
-
- (d) notwithstanding Section 2.2(b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor.
-
-3. Distribution Obligations.
-
- 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
- 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
- 3.3. Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
- 3.4. Intellectual Property Matters.
-
- (a) Third Party Claims. If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "../LEGAL'' which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
-
- (b) Contributor APIs. If Contributor's Modifications include an application programming interface ("API"../) and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file.
-
- (c) Representations. Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.
-
- 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
- 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
- 3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Application of this License.
-
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code.
-
-6. Versions of the License.
-
- 6.1. New Versions. Sun Microsystems, Inc. ("Sun") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
- 6.2. Effect of New Versions. Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Sun. No one other than Sun has the right to modify the terms applicable to Covered Code created under this License.
-
- 6.3. Derivative Works. If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must: (a) rename Your license so that the phrases "Sun," "Sun Public License," or "SPL"../ or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the Sun Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
-7. DISCLAIMER OF WARRANTY.
-
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS'' BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-
- 8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
- 8.2. If You initiate litigation by asserting a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant") alleging that:
-
- (a) such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.
-
- (b) any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant.
-
- 8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
- 8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY.
-
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS.
-
-The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation,"../ as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
-11. MISCELLANEOUS.
-
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-12. RESPONSIBILITY FOR CLAIMS.
-
-As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-13. MULTIPLE-LICENSED CODE.
-
-Initial Developer may designate portions of the Covered Code as ?Multiple-Licensed?. ?Multiple-Licensed? means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A.
-
-Exhibit A -Sun Public License Notice.
-
-The contents of this file are subject to the Sun Public License Version 1.0 (the License); you may not use this file except in compliance with the License. A copy of the License is available at http://www.sun.com/
-
-The Original Code is _________________. The Initial Developer of the Original Code is ___________. Portions created by ______ are Copyright (C)_________. All Rights Reserved.
-
-Contributor(s): ______________________________________.
-
-Alternatively, the contents of this file may be used under the terms of the _____ license (the ?[___] License?), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the SPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the SPL or the [___] License. [NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.]
diff --git a/options/license/SSH-OpenSSH b/options/license/SSH-OpenSSH
deleted file mode 100644
index 35a5c8c829..0000000000
--- a/options/license/SSH-OpenSSH
+++ /dev/null
@@ -1,67 +0,0 @@
-* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
-* All rights reserved
-*
-* As far as I am concerned, the code I have written for this software
-* can be used freely for any purpose. Any derived versions of this
-* software must be clearly marked as such, and if the derived work is
-* incompatible with the protocol description in the RFC file, it must be
-* called by a name other than "ssh" or "Secure Shell".
-
-[Tatu continues]
-* However, I am not implying to give any licenses to any patents or
-* copyrights held by third parties, and the software includes parts that
-* are not under my direct control. As far as I know, all included
-* source code is used in accordance with the relevant license agreements
-* and can be used freely for any purpose (the GNU license being the most
-* restrictive); see below for details.
-
-[However, none of that term is relevant at this point in time. All of
-these restrictively licenced software components which he talks about
-have been removed from OpenSSH, i.e.,
-
-- RSA is no longer included, found in the OpenSSL library
-- IDEA is no longer included, its use is deprecated
-- DES is now external, in the OpenSSL library
-- GMP is no longer used, and instead we call BN code from OpenSSL
-- Zlib is now external, in a library
-- The make-ssh-known-hosts script is no longer included
-- TSS has been removed
-- MD5 is now external, in the OpenSSL library
-- RC4 support has been replaced with ARC4 support from OpenSSL
-- Blowfish is now external, in the OpenSSL library
-
-[The licence continues]
-
-Note that any information and cryptographic algorithms used in this
-software are publicly available on the Internet and at any major
-bookstore, scientific library, and patent office worldwide. More
-information can be found e.g. at "http://www.cs.hut.fi/crypto".
-
-The legal status of this program is some combination of all these
-permissions and restrictions. Use only at your own responsibility.
-You will be responsible for any legal consequences yourself; I am not
-making any claims whether possessing or using this is legal or not in
-your country, and I am not taking any responsibility on your behalf.
-
-
- NO WARRANTY
-
-BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
diff --git a/options/license/SSH-short b/options/license/SSH-short
deleted file mode 100644
index bf6f8a0e62..0000000000
--- a/options/license/SSH-short
+++ /dev/null
@@ -1,5 +0,0 @@
-As far as I am concerned, the code I have written for this software
-can be used freely for any purpose. Any derived versions of this
-software must be clearly marked as such, and if the derived work is
-incompatible with the protocol description in the RFC file, it must be
-called by a name other than "ssh" or "Secure Shell".
diff --git a/options/license/SSLeay-standalone b/options/license/SSLeay-standalone
deleted file mode 100644
index 61618b40eb..0000000000
--- a/options/license/SSLeay-standalone
+++ /dev/null
@@ -1,58 +0,0 @@
-Original SSLeay License
- -----------------------
-
- Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- All rights reserved.
-
- This package is an SSL implementation written
- by Eric Young (eay@cryptsoft.com).
- The implementation was written so as to conform with Netscapes SSL.
-
- This library is free for commercial and non-commercial use as long as
- the following conditions are aheared to. The following conditions
- apply to all code found in this distribution, be it the RC4, RSA,
- lhash, DES, etc., code; not just the SSL code. The SSL documentation
- included with this distribution is covered by the same copyright terms
- except that the holder is Tim Hudson (tjh@cryptsoft.com).
-
- Copyright remains Eric Young's, and as such any Copyright notices in
- the code are not to be removed.
- If this package is used in a product, Eric Young should be given attribution
- as the author of the parts of the library used.
- This can be in the form of a textual message at program startup or
- in documentation (online or textual) provided with the package.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. All advertising materials mentioning features or use of this software
- must display the following acknowledgement:
- "This product includes cryptographic software written by
- Eric Young (eay@cryptsoft.com)"
- The word 'cryptographic' can be left out if the rouines from the library
- being used are not cryptographic related :-).
- 4. If you include any Windows specific code (or a derivative thereof) from
- the apps directory (application code) you must include an acknowledgement:
- "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
-
- THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
- The licence and distribution terms for any publically available version or
- derivative of this code cannot be changed. i.e. this code cannot simply be
- copied and put under another distribution licence
- [including the GNU Public Licence.]
diff --git a/options/license/SSPL-1.0 b/options/license/SSPL-1.0
deleted file mode 100644
index ed242d9a09..0000000000
--- a/options/license/SSPL-1.0
+++ /dev/null
@@ -1,557 +0,0 @@
- Server Side Public License
- VERSION 1, OCTOBER 16, 2018
-
- Copyright © 2018 MongoDB, Inc.
-
- Everyone is permitted to copy and distribute verbatim copies of this
- license document, but changing it is not allowed.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- “This License†refers to Server Side Public License.
-
- “Copyright†also means copyright-like laws that apply to other kinds of
- works, such as semiconductor masks.
-
- “The Program†refers to any copyrightable work licensed under this
- License. Each licensee is addressed as “youâ€. “Licensees†and
- “recipients†may be individuals or organizations.
-
- To “modify†a work means to copy from or adapt all or part of the work in
- a fashion requiring copyright permission, other than the making of an
- exact copy. The resulting work is called a “modified version†of the
- earlier work or a work “based on†the earlier work.
-
- A “covered work†means either the unmodified Program or a work based on
- the Program.
-
- To “propagate†a work means to do anything with it that, without
- permission, would make you directly or secondarily liable for
- infringement under applicable copyright law, except executing it on a
- computer or modifying a private copy. Propagation includes copying,
- distribution (with or without modification), making available to the
- public, and in some countries other activities as well.
-
- To “convey†a work means any kind of propagation that enables other
- parties to make or receive copies. Mere interaction with a user through a
- computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays “Appropriate Legal Notices†to the
- extent that it includes a convenient and prominently visible feature that
- (1) displays an appropriate copyright notice, and (2) tells the user that
- there is no warranty for the work (except to the extent that warranties
- are provided), that licensees may convey the work under this License, and
- how to view a copy of this License. If the interface presents a list of
- user commands or options, such as a menu, a prominent item in the list
- meets this criterion.
-
- 1. Source Code.
-
- The “source code†for a work means the preferred form of the work for
- making modifications to it. “Object code†means any non-source form of a
- work.
-
- A “Standard Interface†means an interface that either is an official
- standard defined by a recognized standards body, or, in the case of
- interfaces specified for a particular programming language, one that is
- widely used among developers working in that language. The “System
- Libraries†of an executable work include anything, other than the work as
- a whole, that (a) is included in the normal form of packaging a Major
- Component, but which is not part of that Major Component, and (b) serves
- only to enable use of the work with that Major Component, or to implement
- a Standard Interface for which an implementation is available to the
- public in source code form. A “Major Componentâ€, in this context, means a
- major essential component (kernel, window system, and so on) of the
- specific operating system (if any) on which the executable work runs, or
- a compiler used to produce the work, or an object code interpreter used
- to run it.
-
- The “Corresponding Source†for a work in object code form means all the
- source code needed to generate, install, and (for an executable work) run
- the object code and to modify the work, including scripts to control
- those activities. However, it does not include the work's System
- Libraries, or general-purpose tools or generally available free programs
- which are used unmodified in performing those activities but which are
- not part of the work. For example, Corresponding Source includes
- interface definition files associated with source files for the work, and
- the source code for shared libraries and dynamically linked subprograms
- that the work is specifically designed to require, such as by intimate
- data communication or control flow between those subprograms and other
- parts of the work.
-
- The Corresponding Source need not include anything that users can
- regenerate automatically from other parts of the Corresponding Source.
-
- The Corresponding Source for a work in source code form is that same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
- copyright on the Program, and are irrevocable provided the stated
- conditions are met. This License explicitly affirms your unlimited
- permission to run the unmodified Program, subject to section 13. The
- output from running a covered work is covered by this License only if the
- output, given its content, constitutes a covered work. This License
- acknowledges your rights of fair use or other equivalent, as provided by
- copyright law. Subject to section 13, you may make, run and propagate
- covered works that you do not convey, without conditions so long as your
- license otherwise remains in force. You may convey covered works to
- others for the sole purpose of having them make modifications exclusively
- for you, or provide you with facilities for running those works, provided
- that you comply with the terms of this License in conveying all
- material for which you do not control copyright. Those thus making or
- running the covered works for you must do so exclusively on your
- behalf, under your direction and control, on terms that prohibit them
- from making any copies of your copyrighted material outside their
- relationship with you.
-
- Conveying under any other circumstances is permitted solely under the
- conditions stated below. Sublicensing is not allowed; section 10 makes it
- unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
- measure under any applicable law fulfilling obligations under article 11
- of the WIPO copyright treaty adopted on 20 December 1996, or similar laws
- prohibiting or restricting circumvention of such measures.
-
- When you convey a covered work, you waive any legal power to forbid
- circumvention of technological measures to the extent such circumvention is
- effected by exercising rights under this License with respect to the
- covered work, and you disclaim any intention to limit operation or
- modification of the work as a means of enforcing, against the work's users,
- your or third parties' legal rights to forbid circumvention of
- technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
- receive it, in any medium, provided that you conspicuously and
- appropriately publish on each copy an appropriate copyright notice; keep
- intact all notices stating that this License and any non-permissive terms
- added in accord with section 7 apply to the code; keep intact all notices
- of the absence of any warranty; and give all recipients a copy of this
- License along with the Program. You may charge any price or no price for
- each copy that you convey, and you may offer support or warranty
- protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
- produce it from the Program, in the form of source code under the terms
- of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified it,
- and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is released
- under this License and any conditions added under section 7. This
- requirement modifies the requirement in section 4 to “keep intact all
- noticesâ€.
-
- c) You must license the entire work, as a whole, under this License to
- anyone who comes into possession of a copy. This License will therefore
- apply, along with any applicable section 7 additional terms, to the
- whole of the work, and all its parts, regardless of how they are
- packaged. This License gives no permission to license the work in any
- other way, but it does not invalidate such permission if you have
- separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your work
- need not make them do so.
-
- A compilation of a covered work with other separate and independent
- works, which are not by their nature extensions of the covered work, and
- which are not combined with it such as to form a larger program, in or on
- a volume of a storage or distribution medium, is called an “aggregate†if
- the compilation and its resulting copyright are not used to limit the
- access or legal rights of the compilation's users beyond what the
- individual works permit. Inclusion of a covered work in an aggregate does
- not cause this License to apply to the other parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms of
- sections 4 and 5, provided that you also convey the machine-readable
- Corresponding Source under the terms of this License, in one of these
- ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium customarily
- used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a written
- offer, valid for at least three years and valid for as long as you
- offer spare parts or customer support for that product model, to give
- anyone who possesses the object code either (1) a copy of the
- Corresponding Source for all the software in the product that is
- covered by this License, on a durable physical medium customarily used
- for software interchange, for a price no more than your reasonable cost
- of physically performing this conveying of source, or (2) access to
- copy the Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This alternative is
- allowed only occasionally and noncommercially, and only if you received
- the object code with such an offer, in accord with subsection 6b.
-
- d) Convey the object code by offering access from a designated place
- (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to copy
- the object code is a network server, the Corresponding Source may be on
- a different server (operated by you or a third party) that supports
- equivalent copying facilities, provided you maintain clear directions
- next to the object code saying where to find the Corresponding Source.
- Regardless of what server hosts the Corresponding Source, you remain
- obligated to ensure that it is available for as long as needed to
- satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided you
- inform other peers where the object code and Corresponding Source of
- the work are being offered to the general public at no charge under
- subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
- from the Corresponding Source as a System Library, need not be included
- in conveying the object code work.
-
- A “User Product†is either (1) a “consumer productâ€, which means any
- tangible personal property which is normally used for personal, family,
- or household purposes, or (2) anything designed or sold for incorporation
- into a dwelling. In determining whether a product is a consumer product,
- doubtful cases shall be resolved in favor of coverage. For a particular
- product received by a particular user, “normally used†refers to a
- typical or common use of that class of product, regardless of the status
- of the particular user or of the way in which the particular user
- actually uses, or expects or is expected to use, the product. A product
- is a consumer product regardless of whether the product has substantial
- commercial, industrial or non-consumer uses, unless such uses represent
- the only significant mode of use of the product.
-
- “Installation Information†for a User Product means any methods,
- procedures, authorization keys, or other information required to install
- and execute modified versions of a covered work in that User Product from
- a modified version of its Corresponding Source. The information must
- suffice to ensure that the continued functioning of the modified object
- code is in no case prevented or interfered with solely because
- modification has been made.
-
- If you convey an object code work under this section in, or with, or
- specifically for use in, a User Product, and the conveying occurs as part
- of a transaction in which the right of possession and use of the User
- Product is transferred to the recipient in perpetuity or for a fixed term
- (regardless of how the transaction is characterized), the Corresponding
- Source conveyed under this section must be accompanied by the
- Installation Information. But this requirement does not apply if neither
- you nor any third party retains the ability to install modified object
- code on the User Product (for example, the work has been installed in
- ROM).
-
- The requirement to provide Installation Information does not include a
- requirement to continue to provide support service, warranty, or updates
- for a work that has been modified or installed by the recipient, or for
- the User Product in which it has been modified or installed. Access
- to a network may be denied when the modification itself materially
- and adversely affects the operation of the network or violates the
- rules and protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided, in
- accord with this section must be in a format that is publicly documented
- (and with an implementation available to the public in source code form),
- and must require no special password or key for unpacking, reading or
- copying.
-
- 7. Additional Terms.
-
- “Additional permissions†are terms that supplement the terms of this
- License by making exceptions from one or more of its conditions.
- Additional permissions that are applicable to the entire Program shall be
- treated as though they were included in this License, to the extent that
- they are valid under applicable law. If additional permissions apply only
- to part of the Program, that part may be used separately under those
- permissions, but the entire Program remains governed by this License
- without regard to the additional permissions. When you convey a copy of
- a covered work, you may at your option remove any additional permissions
- from that copy, or from any part of it. (Additional permissions may be
- written to require their own removal in certain cases when you modify the
- work.) You may place additional permissions on material, added by you to
- a covered work, for which you have or can give appropriate copyright
- permission.
-
- Notwithstanding any other provision of this License, for material you add
- to a covered work, you may (if authorized by the copyright holders of
- that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some trade
- names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that material
- by anyone who conveys the material (or modified versions of it) with
- contractual assumptions of liability to the recipient, for any
- liability that these contractual assumptions directly impose on those
- licensors and authors.
-
- All other non-permissive additional terms are considered “further
- restrictions†within the meaning of section 10. If the Program as you
- received it, or any part of it, contains a notice stating that it is
- governed by this License along with a term that is a further restriction,
- you may remove that term. If a license document contains a further
- restriction but permits relicensing or conveying under this License, you
- may add to a covered work material governed by the terms of that license
- document, provided that the further restriction does not survive such
- relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you must
- place, in the relevant source files, a statement of the additional terms
- that apply to those files, or a notice indicating where to find the
- applicable terms. Additional terms, permissive or non-permissive, may be
- stated in the form of a separately written license, or stated as
- exceptions; the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
- provided under this License. Any attempt otherwise to propagate or modify
- it is void, and will automatically terminate your rights under this
- License (including any patent licenses granted under the third paragraph
- of section 11).
-
- However, if you cease all violation of this License, then your license
- from a particular copyright holder is reinstated (a) provisionally,
- unless and until the copyright holder explicitly and finally terminates
- your license, and (b) permanently, if the copyright holder fails to
- notify you of the violation by some reasonable means prior to 60 days
- after the cessation.
-
- Moreover, your license from a particular copyright holder is reinstated
- permanently if the copyright holder notifies you of the violation by some
- reasonable means, this is the first time you have received notice of
- violation of this License (for any work) from that copyright holder, and
- you cure the violation prior to 30 days after your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
- licenses of parties who have received copies or rights from you under
- this License. If your rights have been terminated and not permanently
- reinstated, you do not qualify to receive new licenses for the same
- material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or run a
- copy of the Program. Ancillary propagation of a covered work occurring
- solely as a consequence of using peer-to-peer transmission to receive a
- copy likewise does not require acceptance. However, nothing other than
- this License grants you permission to propagate or modify any covered
- work. These actions infringe copyright if you do not accept this License.
- Therefore, by modifying or propagating a covered work, you indicate your
- acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically receives
- a license from the original licensors, to run, modify and propagate that
- work, subject to this License. You are not responsible for enforcing
- compliance by third parties with this License.
-
- An “entity transaction†is a transaction transferring control of an
- organization, or substantially all assets of one, or subdividing an
- organization, or merging organizations. If propagation of a covered work
- results from an entity transaction, each party to that transaction who
- receives a copy of the work also receives whatever licenses to the work
- the party's predecessor in interest had or could give under the previous
- paragraph, plus a right to possession of the Corresponding Source of the
- work from the predecessor in interest, if the predecessor has it or can
- get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the rights
- granted or affirmed under this License. For example, you may not impose a
- license fee, royalty, or other charge for exercise of rights granted
- under this License, and you may not initiate litigation (including a
- cross-claim or counterclaim in a lawsuit) alleging that any patent claim
- is infringed by making, using, selling, offering for sale, or importing
- the Program or any portion of it.
-
- 11. Patents.
-
- A “contributor†is a copyright holder who authorizes use under this
- License of the Program or a work on which the Program is based. The work
- thus licensed is called the contributor's “contributor versionâ€.
-
- A contributor's “essential patent claims†are all patent claims owned or
- controlled by the contributor, whether already acquired or hereafter
- acquired, that would be infringed by some manner, permitted by this
- License, of making, using, or selling its contributor version, but do not
- include claims that would be infringed only as a consequence of further
- modification of the contributor version. For purposes of this definition,
- “control†includes the right to grant patent sublicenses in a manner
- consistent with the requirements of this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
- patent license under the contributor's essential patent claims, to make,
- use, sell, offer for sale, import and otherwise run, modify and propagate
- the contents of its contributor version.
-
- In the following three paragraphs, a “patent license†is any express
- agreement or commitment, however denominated, not to enforce a patent
- (such as an express permission to practice a patent or covenant not to
- sue for patent infringement). To “grant†such a patent license to a party
- means to make such an agreement or commitment not to enforce a patent
- against the party.
-
- If you convey a covered work, knowingly relying on a patent license, and
- the Corresponding Source of the work is not available for anyone to copy,
- free of charge and under the terms of this License, through a publicly
- available network server or other readily accessible means, then you must
- either (1) cause the Corresponding Source to be so available, or (2)
- arrange to deprive yourself of the benefit of the patent license for this
- particular work, or (3) arrange, in a manner consistent with the
- requirements of this License, to extend the patent license to downstream
- recipients. “Knowingly relying†means you have actual knowledge that, but
- for the patent license, your conveying the covered work in a country, or
- your recipient's use of the covered work in a country, would infringe
- one or more identifiable patents in that country that you have reason
- to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
- arrangement, you convey, or propagate by procuring conveyance of, a
- covered work, and grant a patent license to some of the parties receiving
- the covered work authorizing them to use, propagate, modify or convey a
- specific copy of the covered work, then the patent license you grant is
- automatically extended to all recipients of the covered work and works
- based on it.
-
- A patent license is “discriminatory†if it does not include within the
- scope of its coverage, prohibits the exercise of, or is conditioned on
- the non-exercise of one or more of the rights that are specifically
- granted under this License. You may not convey a covered work if you are
- a party to an arrangement with a third party that is in the business of
- distributing software, under which you make payment to the third party
- based on the extent of your activity of conveying the work, and under
- which the third party grants, to any of the parties who would receive the
- covered work from you, a discriminatory patent license (a) in connection
- with copies of the covered work conveyed by you (or copies made from
- those copies), or (b) primarily for and in connection with specific
- products or compilations that contain the covered work, unless you
- entered into that arrangement, or that patent license was granted, prior
- to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting any
- implied license or other defenses to infringement that may otherwise be
- available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
- otherwise) that contradict the conditions of this License, they do not
- excuse you from the conditions of this License. If you cannot use,
- propagate or convey a covered work so as to satisfy simultaneously your
- obligations under this License and any other pertinent obligations, then
- as a consequence you may not use, propagate or convey it at all. For
- example, if you agree to terms that obligate you to collect a royalty for
- further conveying from those to whom you convey the Program, the only way
- you could satisfy both those terms and this License would be to refrain
- entirely from conveying the Program.
-
- 13. Offering the Program as a Service.
-
- If you make the functionality of the Program or a modified version
- available to third parties as a service, you must make the Service Source
- Code available via network download to everyone at no charge, under the
- terms of this License. Making the functionality of the Program or
- modified version available to third parties as a service includes,
- without limitation, enabling third parties to interact with the
- functionality of the Program or modified version remotely through a
- computer network, offering a service the value of which entirely or
- primarily derives from the value of the Program or modified version, or
- offering a service that accomplishes for users the primary purpose of the
- Program or modified version.
-
- “Service Source Code†means the Corresponding Source for the Program or
- the modified version, and the Corresponding Source for all programs that
- you use to make the Program or modified version available as a service,
- including, without limitation, management software, user interfaces,
- application program interfaces, automation software, monitoring software,
- backup software, storage software and hosting software, all such that a
- user could run an instance of the service using the Service Source Code
- you make available.
-
- 14. Revised Versions of this License.
-
- MongoDB, Inc. may publish revised and/or new versions of the Server Side
- Public License from time to time. Such new versions will be similar in
- spirit to the present version, but may differ in detail to address new
- problems or concerns.
-
- Each version is given a distinguishing version number. If the Program
- specifies that a certain numbered version of the Server Side Public
- License “or any later version†applies to it, you have the option of
- following the terms and conditions either of that numbered version or of
- any later version published by MongoDB, Inc. If the Program does not
- specify a version number of the Server Side Public License, you may
- choose any version ever published by MongoDB, Inc.
-
- If the Program specifies that a proxy can decide which future versions of
- the Server Side Public License can be used, that proxy's public statement
- of acceptance of a version permanently authorizes you to choose that
- version for the Program.
-
- Later license versions may give you additional or different permissions.
- However, no additional obligations are imposed on any author or copyright holder
- as a result of your choosing to follow a later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
- APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS
- AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS†WITHOUT WARRANTY
- OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
- IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
- ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
- WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
- THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING
- ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF
- THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO
- LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU
- OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
- PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided above
- cannot be given local legal effect according to their terms, reviewing
- courts shall apply local law that most closely approximates an absolute
- waiver of all civil liability in connection with the Program, unless a
- warranty or assumption of liability accompanies a copy of the Program in
- return for a fee.
-
- END OF TERMS AND CONDITIONS
diff --git a/options/license/SWI-exception b/options/license/SWI-exception
deleted file mode 100644
index 9ccfb9b89c..0000000000
--- a/options/license/SWI-exception
+++ /dev/null
@@ -1,6 +0,0 @@
-As a special exception, if you link this library with other files,
-compiled with a Free Software compiler, to produce an executable, this
-library does not by itself cause the resulting executable to be covered
-by the GNU General Public License. This exception does not however
-invalidate any other reasons why the executable file might be covered by
-the GNU General Public License.
diff --git a/options/license/SWL b/options/license/SWL
deleted file mode 100644
index 84c9c418a8..0000000000
--- a/options/license/SWL
+++ /dev/null
@@ -1,7 +0,0 @@
-The authors hereby grant permission to use, copy, modify, distribute, and license this software and its documentation for any purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses. Modifications to this software may be copyrighted by their authors and need not follow the licensing terms described here, provided that the new terms are clearly indicated on the first page of each file where they apply.
-
-IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
-
-GOVERNMENT USE: If you are acquiring this software on behalf of the U.S. government, the Government shall have only "Restricted Rights" in the software and related documentation as defined in the Federal Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you are acquiring the software on behalf of the Department of Defense, the software shall be classified as "Commercial Computer Software" and the Government shall have only "Restricted Rights" as defined in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the authors grant the U.S. Government and others acting in its behalf permission to use and distribute the software in accordance with the terms specified in this license.
-
-BY INSTALLING THIS SOFTWARE, YOU ACKNOWLEDGE THAT YOU HAVE READ THIS AGREEMENT, THAT YOU UNDERSTAND IT, AND THAT YOU AGREE TO BE BOUND BY ITS TERMS AND CONDITIONS.
diff --git a/options/license/Saxpath b/options/license/Saxpath
deleted file mode 100644
index f985c29537..0000000000
--- a/options/license/Saxpath
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (C) 2000-2002 werken digital.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the disclaimer that follows these conditions in the documentation and/or other materials provided with the distribution.
-
- 3. The name "SAXPath" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact license@saxpath.org.
-
- 4. Products derived from this software may not be called "SAXPath", nor may "SAXPath" appear in their name, without prior written permission from the SAXPath Project Management (pm@saxpath.org).
-
-In addition, we request (but do not require) that you include in the end-user documentation provided with the redistribution and/or in the software itself an acknowledgement equivalent to the following:
- "This product includes software developed by the SAXPath Project (http://www.saxpath.org/)."
-
-Alternatively, the acknowledgment may be graphical using the logos available at http://www.saxpath.org/
-
-THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE SAXPath AUTHORS OR THE PROJECT CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/SchemeReport b/options/license/SchemeReport
deleted file mode 100644
index 943b512adf..0000000000
--- a/options/license/SchemeReport
+++ /dev/null
@@ -1,3 +0,0 @@
-We intend this report to belong to the entire Scheme community, and so we grant permission
-to copy it in whole or in part without fee. In particular, we encourage implementors of Scheme
-to use this report as a starting point for manuals and other documentation, modifying it as necessary.
diff --git a/options/license/Sendmail b/options/license/Sendmail
deleted file mode 100644
index 86eecd39c3..0000000000
--- a/options/license/Sendmail
+++ /dev/null
@@ -1,36 +0,0 @@
-SENDMAIL LICENSE
-
-The following license terms and conditions apply, unless a redistribution agreement or other license is obtained from Sendmail, Inc., 6475 Christie Ave, Third Floor, Emeryville, CA 94608, USA, or by electronic mail at license@sendmail.com.
-
-License Terms:
-
-Use, Modification and Redistribution (including distribution of any modified or derived work) in source and binary forms is permitted only if each of the following conditions is met:
-
-1. Redistributions qualify as "freeware" or "Open Source Software" under one of the following terms:
-
- (a) Redistributions are made at no charge beyond the reasonable cost of materials and delivery.
-
- (b) Redistributions are accompanied by a copy of the Source Code or by an irrevocable offer to provide a copy of the Source Code for up to three years at the cost of materials and delivery. Such redistributions must allow further use, modification, and redistribution of the Source Code under substantially the same terms as this license. For the purposes of redistribution "Source Code" means the complete compilable and linkable source code of sendmail including all modifications.
-
-2. Redistributions of Source Code must retain the copyright notices as they appear in each Source Code file, these license terms, and the disclaimer/limitation of liability set forth as paragraph 6 below.
-
-3. Redistributions in binary form must reproduce the Copyright Notice, these license terms, and the disclaimer/limitation of liability set forth as paragraph 6 below, in the documentation and/or other materials provided with the distribution. For the purposes of binary distribution the "Copyright Notice" refers to the following language:
-"Copyright (c) 1998-2010 Sendmail, Inc. All rights reserved."
-
-4. Neither the name of Sendmail, Inc. nor the University of California nor names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission. The name "sendmail" is a trademark of Sendmail, Inc.
-
-5. All redistributions must comply with the conditions imposed by the University of California on certain embedded code, which copyright Notice and conditions for redistribution are as follows:
-
- (a) Copyright (c) 1988, 1993 The Regents of the University of California. All rights reserved.
-
- (b) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- (i) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
- (ii) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- (iii) Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-6. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY SENDMAIL, INC. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SENDMAIL, INC., THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-$Revision: 8.16 $, Last updated $Date: 2010/10/25 23:11:19 $, Document 139848.1
diff --git a/options/license/Sendmail-8.23 b/options/license/Sendmail-8.23
deleted file mode 100644
index e076ca3d7d..0000000000
--- a/options/license/Sendmail-8.23
+++ /dev/null
@@ -1,36 +0,0 @@
-SENDMAIL LICENSE
-
-The following license terms and conditions apply, unless a redistribution agreement or other license is obtained from Proofpoint, Inc., 892 Ross Street, Sunnyvale, CA, 94089, USA, or by electronic mail at sendmail-license@proofpoint.com.
-
-License Terms:
-
-Use, Modification and Redistribution (including distribution of any modified or derived work) in source and binary forms is permitted only if each of the following conditions is met:
-
-1. Redistributions qualify as "freeware" or "Open Source Software" under one of the following terms:
-
- (a) Redistributions are made at no charge beyond the reasonable cost of materials and delivery.
-
- (b) Redistributions are accompanied by a copy of the Source Code or by an irrevocable offer to provide a copy of the Source Code for up to three years at the cost of materials and delivery. Such redistributions must allow further use, modification, and redistribution of the Source Code under substantially the same terms as this license. For the purposes of redistribution "Source Code" means the complete compilable and linkable source code of sendmail and associated libraries and utilities in the sendmail distribution including all modifications.
-
-2. Redistributions of Source Code must retain the copyright notices as they appear in each Source Code file, these license terms, and the disclaimer/limitation of liability set forth as paragraph 6 below.
-
-3. Redistributions in binary form must reproduce the Copyright Notice, these license terms, and the disclaimer/limitation of liability set forth as paragraph 6 below, in the documentation and/or other materials provided with the distribution. For the purposes of binary distribution the "Copyright Notice" refers to the following language:
-"Copyright (c) 1998-2014 Proofpoint, Inc. All rights reserved."
-
-4. Neither the name of Proofpoint, Inc. nor the University of California nor names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission. The name "sendmail" is a trademark of Proofpoint, Inc.
-
-5. All redistributions must comply with the conditions imposed by the University of California on certain embedded code, which copyright Notice and conditions for redistribution are as follows:
-
- (a) Copyright (c) 1988, 1993 The Regents of the University of California. All rights reserved.
-
- (b) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- (i) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
- (ii) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- (iii) Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-6. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY SENDMAIL, INC. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SENDMAIL, INC., THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-$Revision: 8.23 $, Last updated $Date: 2014-01-26 20:10:01 $, Document 139848.1
diff --git a/options/license/Sendmail-Open-Source-1.1 b/options/license/Sendmail-Open-Source-1.1
deleted file mode 100644
index 054f719ee5..0000000000
--- a/options/license/Sendmail-Open-Source-1.1
+++ /dev/null
@@ -1,75 +0,0 @@
-SENDMAIL OPEN SOURCE LICENSE
-
-The following license terms and conditions apply to this open source
-software ("Software"), unless a different license is obtained directly
-from Sendmail, Inc. ("Sendmail") located at 6475 Christie Ave, Suite 350,
-Emeryville, CA 94608, USA.
-
-Use, modification and redistribution (including distribution of any
-modified or derived work) of the Software in source and binary forms is
-permitted only if each of the following conditions of 1-6 are met:
-
-1. Redistributions of the Software qualify as "freeware" or "open
- source software" under one of the following terms:
-
- (a) Redistributions are made at no charge beyond the reasonable
- cost of materials and delivery; or
-
- (b) Redistributions are accompanied by a copy of the modified
- Source Code (on an acceptable machine-readable medium) or by an
- irrevocable offer to provide a copy of the modified Source Code
- (on an acceptable machine-readable medium) for up to three years
- at the cost of materials and delivery. Such redistributions must
- allow further use, modification, and redistribution of the Source
- Code under substantially the same terms as this license. For
- the purposes of redistribution "Source Code" means the complete
- human-readable, compilable, linkable, and operational source
- code of the redistributed module(s) including all modifications.
-
-2. Redistributions of the Software Source Code must retain the
- copyright notices as they appear in each Source Code file, these
- license terms and conditions, and the disclaimer/limitation of
- liability set forth in paragraph 6 below. Redistributions of the
- Software Source Code must also comply with the copyright notices
- and/or license terms and conditions imposed by contributors on
- embedded code. The contributors' license terms and conditions
- and/or copyright notices are contained in the Source Code
- distribution.
-
-3. Redistributions of the Software in binary form must reproduce the
- Copyright Notice described below, these license terms and conditions,
- and the disclaimer/limitation of liability set forth in paragraph
- 6 below, in the documentation and/or other materials provided with
- the binary distribution. For the purposes of binary distribution,
- "Copyright Notice" refers to the following language: "Copyright (c)
- 1998-2009 Sendmail, Inc. All rights reserved."
-
-4. Neither the name, trademark or logo of Sendmail, Inc. (including
- without limitation its subsidiaries or affiliates) or its contributors
- may be used to endorse or promote products, or software or services
- derived from this Software without specific prior written permission.
- The name "sendmail" is a registered trademark and service mark of
- Sendmail, Inc.
-
-5. We reserve the right to cancel this license if you do not comply with
- the terms. This license is governed by California law and both of us
- agree that for any dispute arising out of or relating to this Software,
- that jurisdiction and venue is proper in San Francisco or Alameda
- counties. These license terms and conditions reflect the complete
- agreement for the license of the Software (which means this supercedes
- prior or contemporaneous agreements or representations). If any term
- or condition under this license is found to be invalid, the remaining
- terms and conditions still apply.
-
-6. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY
- SENDMAIL AND ITS CONTRIBUTORS "AS IS" WITHOUT WARRANTY OF ANY KIND
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT AND FITNESS FOR A
- PARTICULAR PURPOSE ARE EXPRESSLY DISCLAIMED. IN NO EVENT SHALL SENDMAIL
- OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
- OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- WITHOUT LIMITATION NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/options/license/SimPL-2.0 b/options/license/SimPL-2.0
deleted file mode 100644
index 0fde0fa080..0000000000
--- a/options/license/SimPL-2.0
+++ /dev/null
@@ -1,37 +0,0 @@
-Simple Public License (SimPL)
-
-Preamble
-
-This Simple Public License 2.0 (SimPL 2.0 for short) is a plain language implementation of GPL 2.0. The words are different, but the goal is the same - to guarantee for all users the freedom to share and change software. If anyone wonders about the meaning of the SimPL, they should interpret it as consistent with GPL 2.0.
-
-Simple Public License (SimPL) 2.0
-
-The SimPL applies to the software's source and object code and comes with any rights that I have in it (other than trademarks). You agree to the SimPL by copying, distributing, or making a derivative work of the software.
-
-You get the royalty free right to:
-
-- Use the software for any purpose;
-- Make derivative works of it (this is called a "Derived Work");
-- Copy and distribute it and any Derived Work.
-
-If you distribute the software or a Derived Work, you must give back to the community by:
-
-- Prominently noting the date of any changes you make;
-- Leaving other people's copyright notices, warranty disclaimers, and license terms in place;
-- Providing the source code, build scripts, installation scripts, and interface definitions in a form that is easy to get and best to modify;
-- Licensing it to everyone under SimPL, or substantially similar terms (such as GPL 2.0), without adding further restrictions to the rights provided;
-- Conspicuously announcing that it is available under that license.
-
-There are some things that you must shoulder:
-
-- You get NO WARRANTIES. None of any kind;
-- If the software damages you in any way, you may only recover direct damages up to the amount you paid for it (that is zero if you did not pay anything). You may not recover any other damages, including those called "consequential damages." (The state or country where you live may not allow you to limit your liability in this way, so this may not apply to you);
-
-The SimPL continues perpetually, except that your license rights end automatically if:
-
-- You do not abide by the "give back to the community" terms (your licensees get to keep their rights if they abide);
-- Anyone prevents you from distributing the software under the terms of the SimPL.
-
-License for the License
-
-You may do anything that you want with the SimPL text; it's a license form to use in any way that you find helpful. To avoid confusion, however, if you change the terms in any way then you may not call your license the Simple Public License or the SimPL (but feel free to acknowledge that your license is "based on the Simple Public License").
diff --git a/options/license/Sleepycat b/options/license/Sleepycat
deleted file mode 100644
index 19d7e1ea15..0000000000
--- a/options/license/Sleepycat
+++ /dev/null
@@ -1,37 +0,0 @@
-The Sleepycat License Copyright (c) 1990-1999 Sleepycat Software. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
- - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- - Redistributions in any form must be accompanied by information on how to obtain complete source code for the DB software and any accompanying software that uses the DB software. The source code must either be included in the distribution or be available for no more than the cost of distribution plus a nominal fee, and must be freely redistributable under reasonable conditions. For an executable file, complete source code means the source code for all modules it contains. It does not include source code for modules or files that typically accompany the major components of the operating system on which the executable file runs.
-
-THIS SOFTWARE IS PROVIDED BY SLEEPYCAT SOFTWARE ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SLEEPYCAT SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-Copyright (c) 1990, 1993, 1994, 1995 The Regents of the University of California. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
- - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- - Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-Copyright (c) 1995, 1996 The President and Fellows of Harvard University. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
- - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- - Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY HARVARD AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL HARVARD OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/Soundex b/options/license/Soundex
deleted file mode 100644
index 16c3fa7664..0000000000
--- a/options/license/Soundex
+++ /dev/null
@@ -1,9 +0,0 @@
-(c) Copyright 1998-2007 by Mark Mielke
-
-Freedom to use these sources for whatever you want, as long as credit
-is given where credit is due, is hereby granted. You may make modifications
-where you see fit but leave this copyright somewhere visible. As well, try
-to initial any changes you make so that if I like the changes I can
-incorporate them into later versions.
-
- - Mark Mielke <mark@mielke.cc>
diff --git a/options/license/Spencer-86 b/options/license/Spencer-86
deleted file mode 100644
index ace415744d..0000000000
--- a/options/license/Spencer-86
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright (c) 1986 by University of Toronto. Written by Henry Spencer. Not derived from licensed software.
-
-Permission is granted to anyone to use this software for any purpose on any computer system, and to redistribute it freely, subject to the following restrictions:
-
-1. The author is not responsible for the consequences of use of this software, no matter how awful, even if they arise from defects in it.
-
-2. The origin of this software must not be misrepresented, either by explicit claim or by omission.
-
-3. Altered versions must be plainly marked as such, and must not be misrepresented as being the original software.
diff --git a/options/license/Spencer-94 b/options/license/Spencer-94
deleted file mode 100644
index 75ba7f7d2e..0000000000
--- a/options/license/Spencer-94
+++ /dev/null
@@ -1,12 +0,0 @@
-Copyright 1992, 1993, 1994 Henry Spencer. All rights reserved.
-This software is not subject to any license of the American Telephone and Telegraph Company or of the Regents of the University of California.
-
-Permission is granted to anyone to use this software for any purpose on any computer system, and to alter it and redistribute it, subject to the following restrictions:
-
-1. The author is not responsible for the consequences of use of this software, no matter how awful, even if they arise from flaws in it.
-
-2. The origin of this software must not be misrepresented, either by explicit claim or by omission. Since few users ever read sources, credits must appear in the documentation.
-
-3. Altered versions must be plainly marked as such, and must not be misrepresented as being the original software. Since few users ever read sources, credits must appear in the documentation.
-
-4. This notice may not be removed or altered.
diff --git a/options/license/Spencer-99 b/options/license/Spencer-99
deleted file mode 100644
index ca7099033f..0000000000
--- a/options/license/Spencer-99
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright (c) 1998, 1999 Henry Spencer. All rights reserved.
-
-Development of this software was funded, in part, by Cray Research Inc., UUNET Communications Services Inc., Sun Microsystems Inc., and Scriptics Corporation, none of whom are responsible for the results. The author thanks all of them.
-
-Redistribution and use in source and binary forms - with or without modification - are permitted for any purpose, provided that redistributions in source form retain this entire copyright notice and indicate the origin and nature of any modifications.
-
-I'd appreciate being given credit for this package in the documentation of software which uses it, but that is not a requirement.
-
-THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL HENRY SPENCER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/SugarCRM-1.1.3 b/options/license/SugarCRM-1.1.3
deleted file mode 100644
index e00b5b3f3b..0000000000
--- a/options/license/SugarCRM-1.1.3
+++ /dev/null
@@ -1,149 +0,0 @@
-SUGARCRM PUBLIC LICENSE
-
-Applies to Sugar Open Source Edition v1 through v4. Please note that these releases are no longer supported or distributed.
-
-Version 1.1.3
-
-The SugarCRM Public License Version ("SPL") consists of the Mozilla Public License Version 1.1, modified to be specific to SugarCRM, with the Additional Terms in Exhibit B. The original Mozilla Public License 1.1 can be found at: http://www.mozilla.org/MPL/MPL-1.1.html
-
-1. Definitions.
-
- 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications.
-
- 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
- 1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.
-
- 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
- 1.5. "Executable" means Covered Code in any form other than Source Code.
-
- 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
- 1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.8. "License" means this document.
-
- 1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
- 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
-
- B. Any new file that contains any part of the Original Code or previous Modifications.
-
- A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
-
- 1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
- 1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
- 1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
- 1.12. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2. Source Code License.
-
- 2.2. Contributor Grant.
-Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license
-
- (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
-
- (c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first makes Commercial Use of the Covered Code.
-
- (d) Notwithstanding Section 2.2(b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor.
-
- (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and
-
-3. Distribution Obligations.
-
- 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.
-
- 3.3. Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
- 3.4. Intellectual Property Matters
-
- (b) Contributor APIs. If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file.
-
- (a) Third Party Claims. If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
-
- (c) Representations. Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.
-
- 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
- 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
- 3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5. Application of this License.
-
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code.
-
-6. Versions of the License.
-
- 6.2. Effect of New Versions. Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by SugarCRM. No one other than SugarCRM has the right to modify the terms applicable to Covered Code created under this License.
-
- 6.3. Derivative Works. If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases "SugarCRM", "SPL" or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the SugarCRM Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
-7. DISCLAIMER OF WARRANTY.
-
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-
- 8.2. If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant") alleging that:
-
- (a) such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.
-
- (b) any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant.
-
- 8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
- 8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY.
-
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS.
-
-The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
-11. MISCELLANEOUS.
-
-This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.
-
-12. RESPONSIBILITY FOR CLAIMS.
-
-As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-13. MULTIPLE-LICENSED CODE.
-
-Initial Developer may designate portions of the Covered Code as "Multiple-Licensed". "Multiple-Licensed" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the SPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A.
-
-SugarCRM Public License 1.1.3 - Exhibit A
-
-The contents of this file are subject to the SugarCRM Public License Version 1.1.3 ("License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.sugarcrm.com/SPL Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-
-The Original Code is: SugarCRM Open Source
-
-The Initial Developer of the Original Code is SugarCRM, Inc.
-Portions created by SugarCRM are Copyright (C) 2004 SugarCRM, Inc.;
-All Rights Reserved.
-Contributor(s): ______________________________________.
-
-[NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.]
-
-SugarCRM Public License 1.1.3 - Exhibit B
-
-Additional Terms applicable to the SugarCRM Public License.
-
-I. Effect.
-These additional terms described in this SugarCRM Public License - Additional Terms shall apply to the Covered Code under this License.
-
-II. SugarCRM and logo.
-This License does not grant any rights to use the trademarks "SugarCRM" and the "SugarCRM" logos even if such marks are included in the Original Code or Modifications.
-
-However, in addition to the other notice obligations, all copies of the Covered Code in Executable and Source Code form distributed must, as a form of attribution of the original author, include on each user interface screen (i) the "Powered by SugarCRM" logo and (ii) the copyright notice in the same form as the latest version of the Covered Code distributed by SugarCRM, Inc. at the time of distribution of such copy. In addition, the "Powered by SugarCRM" logo must be visible to all users and be located at the very bottom center of each user interface screen. Notwithstanding the above, the dimensions of the "Powered By SugarCRM" logo must be at least 106 x 23 pixels. When users click on the "Powered by SugarCRM" logo it must direct them back to http://www.sugarforge.org. In addition, the copyright notice must remain visible to all users at all times at the bottom of the user interface screen. When users click on the copyright notice, it must direct them back to http://www.sugarcrm.com
diff --git a/options/license/Sun-PPP b/options/license/Sun-PPP
deleted file mode 100644
index 5f94a13437..0000000000
--- a/options/license/Sun-PPP
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright (c) 2001 by Sun Microsystems, Inc.
-All rights reserved.
-
-Non-exclusive rights to redistribute, modify, translate, and use
-this software in source and binary forms, in whole or in part, is
-hereby granted, provided that the above copyright notice is
-duplicated in any source form, and that neither the name of the
-copyright holder nor the author is used to endorse or promote
-products derived from this software.
-
-THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/options/license/Sun-PPP-2000 b/options/license/Sun-PPP-2000
deleted file mode 100644
index 914c19544a..0000000000
--- a/options/license/Sun-PPP-2000
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright (c) 2000 by Sun Microsystems, Inc.
-All rights reserved.
-
-Permission to use, copy, modify, and distribute this software and its
-documentation is hereby granted, provided that the above copyright
-notice appears in all copies.
-
-SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
-THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
-TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
-ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
-DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
diff --git a/options/license/SunPro b/options/license/SunPro
deleted file mode 100644
index 1ccb78add0..0000000000
--- a/options/license/SunPro
+++ /dev/null
@@ -1,6 +0,0 @@
-Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
-
-Developed at SunSoft, a Sun Microsystems, Inc. business.
-Permission to use, copy, modify, and distribute this
-software is freely granted, provided that this notice
-is preserved.
diff --git a/options/license/Swift-exception b/options/license/Swift-exception
deleted file mode 100644
index 32b573cea5..0000000000
--- a/options/license/Swift-exception
+++ /dev/null
@@ -1,6 +0,0 @@
-### Runtime Library Exception to the Apache 2.0 License: ###
-
-As an exception, if you use this Software to compile your source code and
-portions of this Software are embedded into the binary product as a result,
-you may redistribute such product without providing attribution as would
-otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.
diff --git a/options/license/Symlinks b/options/license/Symlinks
deleted file mode 100644
index 35420d2ba9..0000000000
--- a/options/license/Symlinks
+++ /dev/null
@@ -1,10 +0,0 @@
- My "symlinks" utility pre-dates the "open source licensing"
-fad by a number of years. Just to clarify, this is 100%
-freeware, written entirely by myself. The intent is to use
-it to detect missing/obsolete symlink targets on an installed
-distro, before creating the "gold" (or "final") release discs.
-
-Use and distribute and modify as you (or anyone
-else) sees fit. There have no formal restrictions or
-requirements whatsoever regarding distribution of either
-binaries or source code, whether modified or original.
diff --git a/options/license/TAPR-OHL-1.0 b/options/license/TAPR-OHL-1.0
deleted file mode 100644
index e20c23663b..0000000000
--- a/options/license/TAPR-OHL-1.0
+++ /dev/null
@@ -1,266 +0,0 @@
-The TAPR Open Hardware License
-Version 1.0 (May 25, 2007)
-Copyright 2007 TAPR - http://www.tapr.org/OHL
-
-PREAMBLE
-
-Open Hardware is a thing - a physical artifact, either electrical or
-mechanical - whose design information is available to, and usable by,
-the public in a way that allows anyone to make, modify, distribute, and
-use that thing. In this preface, design information is called
-"documentation" and things created from it are called "products."
-
-The TAPR Open Hardware License ("OHL") agreement provides a legal
-framework for Open Hardware projects. It may be used for any kind of
-product, be it a hammer or a computer motherboard, and is TAPR's
-contribution to the community; anyone may use the OHL for their Open
-Hardware project.
-
-Like the GNU General Public License, the OHL is designed to guarantee
-your freedom to share and to create. It forbids anyone who receives
-rights under the OHL to deny any other licensee those same rights to
-copy, modify, and distribute documentation, and to make, use and
-distribute products based on that documentation.
-
-Unlike the GPL, the OHL is not primarily a copyright license. While
-copyright protects documentation from unauthorized copying, modification,
-and distribution, it has little to do with your right to make, distribute,
-or use a product based on that documentation. For better or worse, patents
-play a significant role in those activities. Although it does not prohibit
-anyone from patenting inventions embodied in an Open Hardware design, and
-of course cannot prevent a third party from enforcing their patent rights,
-those who benefit from an OHL design may not bring lawsuits claiming that
-design infringes their patents or other intellectual property.
-
-The OHL addresses unique issues involved in the creation of tangible,
-physical things, but does not cover software, firmware, or code loaded
-into programmable devices. A copyright-oriented license such as the GPL
-better suits these creations.
-
-How can you use the OHL, or a design based upon it? While the terms and
-conditions below take precedence over this preamble, here is a summary:
-
-* You may modify the documentation and make products based upon it.
-
-* You may use products for any legal purpose without limitation.
-
-* You may distribute unmodified documentation, but you must include the
-complete package as you received it.
-
-* You may distribute products you make to third parties, if you either
-include the documentation on which the product is based, or make it
-available without charge for at least three years to anyone who requests
-it.
-
-* You may distribute modified documentation or products based on it, if
-you:
- * License your modifications under the OHL.
- * Include those modifications, following the requirements stated
- below.
- * Attempt to send the modified documentation by email to any of the
- developers who have provided their email address. This is a good
- faith obligation - if the email fails, you need do nothing more
- and may go on with your distribution.
-
-* If you create a design that you want to license under the OHL, you
-should:
- * Include this document in a file named LICENSE (with the appropriate
- extension) that is included in the documentation package.
- * If the file format allows, include a notice like "Licensed under
- the TAPR Open Hardware License (www.tapr.org/OHL)" in each
- documentation file. While not required, you should also include
- this notice on printed circuit board artwork and the product
- itself; if space is limited the notice can be shortened or
- abbreviated.
- * Include a copyright notice in each file and on printed circuit
- board artwork.
- * If you wish to be notified of modifications that others may make,
- include your email address in a file named "CONTRIB.TXT" or
- something similar.
-
-* Any time the OHL requires you to make documentation available to
-others, you must include all the materials you received from the
-upstream licensors. In addition, if you have modified the
-documentation:
- * You must identify the modifications in a text file (preferably
- named "CHANGES.TXT") that you include with the documentation.
- That file must also include a statement like "These modifications
- are licensed under the TAPR Open Hardware License."
- * You must include any new files you created, including any
- manufacturing files (such as Gerber files) you create in the
- course of making products.
- * You must include both "before" and "after" versions of all files
- you modified.
- * You may include files in proprietary formats, but you must also
- include open format versions (such as Gerber, ASCII, Postscript,
- or PDF) if your tools can create them.
-
-TERMS AND CONDITIONS
-
-1. Introduction
-1.1 This Agreement governs how you may use, copy, modify, and
-distribute Documentation, and how you may make, have made, and
-distribute Products based on that Documentation. As used in this
-Agreement, to "distribute" Documentation means to directly or indirectly
-make copies available to a third party, and to "distribute" Products
-means to directly or indirectly give, loan, sell or otherwise transfer
-them to a third party.
-
-1.2 "Documentation" includes:
- (a) schematic diagrams;
- (b) circuit or circuit board layouts, including Gerber and other
- data files used for manufacture;
- (c) mechanical drawings, including CAD, CAM, and other data files
- used for manufacture;
- (d) flow charts and descriptive text; and
- (e) other explanatory material.
-Documentation may be in any tangible or intangible form of expression,
-including but not limited to computer files in open or proprietary
-formats and representations on paper, film, or other media.
-
-1.3 "Products" include:
- (a) circuit boards, mechanical assemblies, and other physical parts
- and components;
- (b) assembled or partially assembled units (including components
- and subassemblies); and
- (c) parts and components combined into kits intended for assembly
- by others;
-which are based in whole or in part on the Documentation.
-
-1.4 This Agreement applies to any Documentation which contains a
-notice stating it is subject to the TAPR Open Hardware License, and to
-all Products based in whole or in part on that Documentation. If
-Documentation is distributed in an archive (such as a "zip" file) which
-includes this document, all files in that archive are subject to this
-Agreement unless they are specifically excluded. Each person who
-contributes content to the Documentation is referred to in this
-Agreement as a "Licensor."
-
-1.5 By (a) using, copying, modifying, or distributing the
-Documentation, or (b) making or having Products made or distributing
-them, you accept this Agreement, agree to comply with its terms, and
-become a "Licensee." Any activity inconsistent with this Agreement will
-automatically terminate your rights under it (including the immunities
-from suit granted in Section 2), but the rights of others who have
-received Documentation, or have obtained Products, directly or
-indirectly from you will not be affected so long as they fully comply
-with it themselves.
-
-1.6 This Agreement does not apply to software, firmware, or code
-loaded into programmable devices which may be used in conjunction with
-Documentation or Products. Such software is subject to the license
-terms established by its copyright holder(s).
-
-2. Patents
-2.1 Each Licensor grants you, every other Licensee, and every
-possessor or user of Products a perpetual, worldwide, and royalty-free
-immunity from suit under any patent, patent application, or other
-intellectual property right which he or she controls, to the extent
-necessary to make, have made, possess, use, and distribute Products.
-This immunity does not extend to infringement arising from modifications
-subsequently made by others.
-
-2.2 If you make or have Products made, or distribute Documentation
-that you have modified, you grant every Licensor, every other Licensee,
-and every possessor or user of Products a perpetual, worldwide, and
-royalty-free immunity from suit under any patent, patent application, or
-other intellectual property right which you control, to the extent
-necessary to make, have made, possess, use, and distribute Products.
-This immunity does not extend to infringement arising from modifications
-subsequently made by others.
-
-2.3 To avoid doubt, providing Documentation to a third party for the
-sole purpose of having that party make Products on your behalf is not
-considered "distribution,"\" and a third party's act of making Products
-solely on your behalf does not cause that party to grant the immunity
-described in the preceding paragraph.
-
-2.4 These grants of immunity are a material part of this Agreement,
-and form a portion of the consideration given by each party to the
-other. If any court judgment or legal agreement prevents you from
-granting the immunity required by this Section, your rights under this
-Agreement will terminate and you may no longer use, copy, modify or
-distribute the Documentation, or make, have made, or distribute
-Products.
-
-3. Modifications
-You may modify the Documentation, and those modifications will become
-part of the Documentation. They are subject to this Agreement, as are
-Products based in whole or in part on them. If you distribute the
-modified Documentation, or Products based in whole or in part upon it,
-you must email the modified Documentation in a form compliant with
-Section 4 to each Licensor who has provided an email address with the
-Documentation. Attempting to send the email completes your obligations
-under this Section and you need take no further action if any address
-fails.
-
-4. Distributing Documentation
-4.1 You may distribute unmodified copies of the Documentation in its
-entirety in any medium, provided that you retain all copyright and other
-notices (including references to this Agreement) included by each
-Licensor, and include an unaltered copy of this Agreement.
-4.2 You may distribute modified copies of the Documentation if you
-comply with all the requirements of the preceding paragraph and:
- (a) include a prominent notice in an ASCII or other open format
- file identifying those elements of the Documentation that you
- changed, and stating that the modifications are licensed under
- the terms of this Agreement;
- (b) include all new documentation files that you create, as well as
- both the original and modified versions of each file you change
- (files may be in your development tool's native file format,
- but if reasonably possible, you must also include open format,
- such as Gerber, ASCII, Postscript, or PDF, versions);
- (c) do not change the terms of this Agreement with respect to
- subsequent licensees; and
- (d) if you make or have Products made, include in the Documentation
- all elements reasonably required to permit others to make
- Products, including Gerber, CAD/CAM and other files used for
- manufacture.
-
-5. Making Products
-5.1 You may use the Documentation to make or have Products made,
-provided that each Product retains any notices included by the Licensor
-(including, but not limited to, copyright notices on circuit boards).
-5.2 You may distribute Products you make or have made, provided that
-you include with each unit a copy of the Documentation in a form
-consistent with Section 4. Alternatively, you may include either (i) an
-offer valid for at least three years to provide that Documentation, at
-no charge other than the reasonable cost of media and postage, to any
-person who requests it; or (ii) a URL where that Documentation may be
-downloaded, available for at least three years after you last distribute
-the Product.
-
-6. NEW LICENSE VERSIONS
-TAPR may publish updated versions of the OHL which retain the same
-general provisions as the present version, but differ in detail to
-address new problems or concerns, and carry a distinguishing version
-number. If the Documentation specifies a version number which applies
-to it and "any later version", you may choose either that version or any
-later version published by TAPR. If the Documentation does not specify
-a version number, you may choose any version ever published by TAPR.
-TAPR owns the copyright to the OHL, but grants permission to any person
-to copy, distribute, and use it in unmodified form.
-
-7. WARRANTY AND LIABILITY LIMITATIONS
-7.1 THE DOCUMENTATION IS PROVIDED ON AN"AS-IS" BASIS WITHOUT
-WARRANTY OF ANY KIND, TO THE EXTENT PERMITTED BY APPLICABLE LAW. ALL
-WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY
-WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
-TITLE, ARE HEREBY EXPRESSLY DISCLAIMED.
-7.2 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL ANY LICENSOR
-BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, CONSEQUENTIAL, PUNITIVE, OR EXEMPLARY DAMAGES ARISING OUT OF
-THE USE OF, OR INABILITY TO USE, THE DOCUMENTATION OR PRODUCTS,
-INCLUDING BUT NOT LIMITED TO CLAIMS OF INTELLECTUAL PROPERTY
-INFRINGEMENT OR LOSS OF DATA, EVEN IF THAT PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-7.3 You agree that the foregoing limitations are reasonable due to
-the non-financial nature of the transaction represented by this
-Agreement, and acknowledge that were it not for these limitations, the
-Licensor(s) would not be willing to make the Documentation available to
-you.
-7.4 You agree to defend, indemnify, and hold each Licensor harmless
-from any claim brought by a third party alleging any defect in the
-design, manufacture, or operation of any Product which you make, have
-made, or distribute pursuant to this Agreement.
- ####
diff --git a/options/license/TCL b/options/license/TCL
deleted file mode 100644
index 4b9cdb48a6..0000000000
--- a/options/license/TCL
+++ /dev/null
@@ -1,9 +0,0 @@
-This software is copyrighted by the Regents of the University of California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState Corporation and other parties. The following terms apply to all files associated with the software unless explicitly disclaimed in individual files.
-
-The authors hereby grant permission to use, copy, modify, distribute, and license this software and its documentation for any purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses. Modifications to this software may be copyrighted by their authors and need not follow the licensing terms described here, provided that the new terms are clearly indicated on the first page of each file where they apply.
-
-IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
-
-GOVERNMENT USE: If you are acquiring this software on behalf of the U.S. government, the Government shall have only "Restricted Rights" in the software and related documentation as defined in the Federal Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you are acquiring the software on behalf of the Department of Defense, the software shall be classified as "Commercial Computer Software" and the Government shall have only "Restricted Rights" as defined in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the authors grant the U.S. Government and others acting in its behalf permission to use and distribute the software in accordance with the terms specified in this license.
diff --git a/options/license/TCP-wrappers b/options/license/TCP-wrappers
deleted file mode 100644
index e13d4f5327..0000000000
--- a/options/license/TCP-wrappers
+++ /dev/null
@@ -1,7 +0,0 @@
-Copyright 1995 by Wietse Venema. All rights reserved. Some individual files may be covered by other copyrights.
-
-This material was originally written and compiled by Wietse Venema at Eindhoven University of Technology, The Netherlands, in 1990, 1991, 1992, 1993, 1994 and 1995.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that this entire copyright notice is duplicated in all such copies.
-
-This software is provided "as is" and without any expressed or implied warranties, including, without limitation, the implied warranties of merchantibility and fitness for any particular purpose.
diff --git a/options/license/TGPPL-1.0 b/options/license/TGPPL-1.0
deleted file mode 100644
index fbafe92c82..0000000000
--- a/options/license/TGPPL-1.0
+++ /dev/null
@@ -1,181 +0,0 @@
-=======================================================
-Transitive Grace Period Public Licence ("TGPPL") v. 1.0
-=======================================================
-
-This Transitive Grace Period Public Licence (the "License") applies to any
-original work of authorship (the "Original Work") whose owner (the
-"Licensor") has placed the following licensing notice adjacent to the
-copyright notice for the Original Work:
-
- *Licensed under the Transitive Grace Period Public Licence version 1.0*
-
-1. **Grant of Copyright License.** Licensor grants You a worldwide,
- royalty-free, non-exclusive, sublicensable license, for the duration of
- the copyright, to do the following:
-
- a. to reproduce the Original Work in copies, either alone or as part of a
- collective work;
-
- b. to translate, adapt, alter, transform, modify, or arrange the Original
- Work, thereby creating derivative works ("Derivative Works") based upon
- the Original Work;
-
- c. to distribute or communicate copies of the Original Work and Derivative
- Works to the public, with the proviso that copies of Original Work or
- Derivative Works that You distribute or communicate shall be licensed
- under this Transitive Grace Period Public Licence no later than 12
- months after You distributed or communicated said copies;
-
- d. to perform the Original Work publicly; and
-
- e. to display the Original Work publicly.
-
-2. **Grant of Patent License.** Licensor grants You a worldwide,
- royalty-free, non-exclusive, sublicensable license, under patent claims
- owned or controlled by the Licensor that are embodied in the Original
- Work as furnished by the Licensor, for the duration of the patents, to
- make, use, sell, offer for sale, have made, and import the Original Work
- and Derivative Works.
-
-3. **Grant of Source Code License.** The term "Source Code" means the
- preferred form of the Original Work for making modifications to it and
- all available documentation describing how to modify the Original
- Work. Licensor agrees to provide a machine-readable copy of the Source
- Code of the Original Work along with each copy of the Original Work that
- Licensor distributes. Licensor reserves the right to satisfy this
- obligation by placing a machine-readable copy of the Source Code in an
- information repository reasonably calculated to permit inexpensive and
- convenient access by You for as long as Licensor continues to distribute
- the Original Work.
-
-4. **Exclusions From License Grant.** Neither the names of Licensor, nor the
- names of any contributors to the Original Work, nor any of their
- trademarks or service marks, may be used to endorse or promote products
- derived from this Original Work without express prior permission of the
- Licensor. Except as expressly stated herein, nothing in this License
- grants any license to Licensor's trademarks, copyrights, patents, trade
- secrets or any other intellectual property. No patent license is granted
- to make, use, sell, offer for sale, have made, or import embodiments of
- any patent claims other than the licensed claims defined in Section 2. No
- license is granted to the trademarks of Licensor even if such marks are
- included in the Original Work. Nothing in this License shall be
- interpreted to prohibit Licensor from licensing under terms different
- from this License any Original Work that Licensor otherwise would have a
- right to license.
-
-5. **External Deployment.** The term "External Deployment" means the use,
- distribution, or communication of the Original Work or Derivative Works
- in any way such that the Original Work or Derivative Works may be used by
- anyone other than You, whether those works are distributed or
- communicated to those persons or made available as an application
- intended for use over a network. As an express condition for the grants
- of license hereunder, You must treat any External Deployment by You of
- the Original Work or a Derivative Work as a distribution under section
- 1(c).
-
-6. **Attribution Rights.** You must retain, in the Source Code of any
- Derivative Works that You create, all copyright, patent, or trademark
- notices from the Source Code of the Original Work, as well as any notices
- of licensing and any descriptive text identified therein as an
- "Attribution Notice." You must cause the Source Code for any Derivative
- Works that You create to carry a prominent Attribution Notice reasonably
- calculated to inform recipients that You have modified the Original Work.
-
-7. **Warranty of Provenance and Disclaimer of Warranty.** Licensor warrants
- that the copyright in and to the Original Work and the patent rights
- granted herein by Licensor are owned by the Licensor or are sublicensed
- to You under the terms of this License with the permission of the
- contributor(s) of those copyrights and patent rights. Except as expressly
- stated in the immediately preceding sentence, the Original Work is
- provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY,
- either express or implied, including, without limitation, the warranties
- of non-infringement, merchantability or fitness for a particular
- purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH
- YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this
- License. No license to the Original Work is granted by this License
- except under this disclaimer.
-
-8. **Limitation of Liability.** Under no circumstances and under no legal
- theory, whether in tort (including negligence), contract, or otherwise,
- shall the Licensor be liable to anyone for any indirect, special,
- incidental, or consequential damages of any character arising as a result
- of this License or the use of the Original Work including, without
- limitation, damages for loss of goodwill, work stoppage, computer failure
- or malfunction, or any and all other commercial damages or losses. This
- limitation of liability shall not apply to the extent applicable law
- prohibits such limitation.
-
-9. **Acceptance and Termination.** If, at any time, You expressly assented
- to this License, that assent indicates your clear and irrevocable
- acceptance of this License and all of its terms and conditions. If You
- distribute or communicate copies of the Original Work or a Derivative
- Work, You must make a reasonable effort under the circumstances to obtain
- the express assent of recipients to the terms of this License. This
- License conditions your rights to undertake the activities listed in
- Section 1, including your right to create Derivative Works based upon the
- Original Work, and doing so without honoring these terms and conditions
- is prohibited by copyright law and international treaty. Nothing in this
- License is intended to affect copyright exceptions and limitations
- (including 'fair use' or 'fair dealing'). This License shall terminate
- immediately and You may no longer exercise any of the rights granted to
- You by this License upon your failure to honor the conditions in Section
- 1(c).
-
-10. **Termination for Patent Action.** This License shall terminate
- automatically and You may no longer exercise any of the rights granted to
- You by this License as of the date You commence an action, including a
- cross-claim or counterclaim, against Licensor or any licensee alleging
- that the Original Work infringes a patent. This termination provision
- shall not apply for an action alleging patent infringement by
- combinations of the Original Work with other software or hardware.
-
-11. **Jurisdiction, Venue and Governing Law.** Any action or suit relating to
- this License may be brought only in the courts of a jurisdiction wherein
- the Licensor resides or in which Licensor conducts its primary business,
- and under the laws of that jurisdiction excluding its conflict-of-law
- provisions. The application of the United Nations Convention on Contracts
- for the International Sale of Goods is expressly excluded. Any use of the
- Original Work outside the scope of this License or after its termination
- shall be subject to the requirements and penalties of copyright or patent
- law in the appropriate jurisdiction. This section shall survive the
- termination of this License.
-
-12. **Attorneys' Fees.** In any action to enforce the terms of this License
- or seeking damages relating thereto, the prevailing party shall be
- entitled to recover its costs and expenses, including, without
- limitation, reasonable attorneys' fees and costs incurred in connection
- with such action, including any appeal of such action. This section shall
- survive the termination of this License.
-
-13. **Miscellaneous.** If any provision of this License is held to be
- unenforceable, such provision shall be reformed only to the extent
- necessary to make it enforceable.
-
-14. **Definition of "You" in This License.** "You" throughout this License,
- whether in upper or lower case, means an individual or a legal entity
- exercising rights under, and complying with all of the terms of, this
- License. For legal entities, "You" includes any entity that controls, is
- controlled by, or is under common control with you. For purposes of this
- definition, "control" means (i) the power, direct or indirect, to cause
- the direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
-15. **Right to Use.** You may use the Original Work in all ways not otherwise
- restricted or conditioned by this License or by law, and Licensor
- promises not to interfere with or be responsible for such uses by You.
-
-16. **Modification of This License.** This License is Copyright © 2007 Zooko
- Wilcox-O'Hearn. Permission is granted to copy, distribute, or communicate
- this License without modification. Nothing in this License permits You to
- modify this License as applied to the Original Work or to Derivative
- Works. However, You may modify the text of this License and copy,
- distribute or communicate your modified version (the "Modified License")
- and apply it to other original works of authorship subject to the
- following conditions: (i) You may not indicate in any way that your
- Modified License is the "Transitive Grace Period Public Licence" or
- "TGPPL" and you may not use those names in the name of your Modified
- License; and (ii) You must replace the notice specified in the first
- paragraph above with the notice "Licensed under <insert your license name
- here>" or with a notice of your own that is not confusingly similar to
- the notice in this License.
diff --git a/options/license/TMate b/options/license/TMate
deleted file mode 100644
index 75fe4b42f9..0000000000
--- a/options/license/TMate
+++ /dev/null
@@ -1,21 +0,0 @@
-The TMate Open Source License.
-
-This license applies to all portions of TMate SVNKit library, which are not externally-maintained libraries (e.g. Ganymed SSH library).
-
-All the source code and compiled classes in package org.tigris.subversion.javahl except SvnClient class are covered by the license in JAVAHL-LICENSE file
-
-Copyright (c) 2004-2012 TMate Software. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- * Redistributions in any form must be accompanied by information on how to obtain complete source code for the software that uses SVNKit and any accompanying software that uses the software that uses SVNKit. The source code must either be included in the distribution or be available for no more than the cost of distribution plus a nominal fee, and must be freely redistributable under reasonable conditions. For an executable file, complete source code means the source code for all modules it contains. It does not include source code for modules or files that typically accompany the major components of the operating system on which the executable file runs.
-
- * Redistribution in any form without redistributing source code for software that uses SVNKit is possible only when such redistribution is explictly permitted by TMate Software. Please, contact TMate Software at support@svnkit.com to get such permission.
-
-THIS SOFTWARE IS PROVIDED BY TMATE SOFTWARE ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED.
-
-IN NO EVENT SHALL TMATE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/TORQUE-1.1 b/options/license/TORQUE-1.1
deleted file mode 100644
index 52c884e8de..0000000000
--- a/options/license/TORQUE-1.1
+++ /dev/null
@@ -1,25 +0,0 @@
-TORQUE v2.5+ Software License v1.1
-Copyright (c) 2010-2011 Adaptive Computing Enterprises, Inc. All rights reserved.
-
-Use this license to use or redistribute the TORQUE software v2.5+ and later versions. For free support for TORQUE users, questions should be emailed to the community of TORQUE users at torqueusers@supercluster.org. Users can also subscribe to the user mailing list at http://www.supercluster.org/mailman/listinfo/torqueusers. Customers using TORQUE that also are licensed users of Moab branded software from Adaptive Computing Inc. can get TORQUE support from Adaptive Computing via:
-Email: torque-support@adaptivecomputing.com.
-Phone: (801) 717-3700
-Web: www.adaptivecomputing.com www.clusterresources.com
-
-This license covers use of the TORQUE v2.5 software (the "Software") at your site or location, and, for certain users, redistribution of the Software to other sites and locations1. Later versions of TORQUE are also covered by this license. Use and redistribution of TORQUE v2.5 in source and binary forms, with or without modification, are permitted provided that all of the following conditions are met.
-
-1. Any Redistribution of source code must retain the above copyright notice and the acknowledgment contained in paragraph 5, this list of conditions and the disclaimer contained in paragraph 5.
-
-2. Any Redistribution in binary form must reproduce the above copyright notice and the acknowledgment contained in paragraph 4, this list of conditions and the disclaimer contained in paragraph 5 in the documentation and/or other materials provided with the distribution.
-
-3. Redistributions in any form must be accompanied by information on how to obtain complete source code for TORQUE and any modifications and/or additions to TORQUE. The source code must either be included in the distribution or be available for no more than the cost of distribution plus a nominal fee, and all modifications and additions to the Software must be freely redistributable by any party (including Licensor) without restriction.
-
-4. All advertising materials mentioning features or use of the Software must display the following acknowledgment:
-"TORQUE is a modification of OpenPBS which was developed by NASA Ames Research Center, Lawrence Livermore National Laboratory, and Veridian TORQUE Open Source License v1.1. 1 Information Solutions, Inc. Visit www.clusterresources.com/products/ for more information about TORQUE and to download TORQUE. For information about Moab branded products and so receive support from Adaptive Computing for TORQUE, see www.adaptivecomputing.com.â€
-
-5. DISCLAIMER OF WARRANTY THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED. IN NO EVENT SHALL ADAPTIVE COMPUTING ENTERPRISES, INC. CORPORATION, ITS AFFILIATED COMPANIES, OR THE U.S. GOVERNMENT OR ANY OF ITS AGENCIES BE LIABLE FOR ANY DIRECT OR INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-This license will be governed by the laws of Utah, without reference to its choice of law rules.
-
-Note 1: TORQUE is developed from an earlier version v2.3 of OpenPBS. TORQUE has been developed beyond OpenPBS v2.3. The OpenPBS v2.3 license and OpenPBS software can be obtained at:
-http://www.pbsworks.com/ResLibSearchResult.aspx?keywords=openpbs&industry=All&pro duct_service=All&category=Free%20Software%20Downloads&order_by=title. Users of TORQUE should comply with the TORQUE license as well as the OpenPBS license.
diff --git a/options/license/TOSL b/options/license/TOSL
deleted file mode 100644
index efee67e571..0000000000
--- a/options/license/TOSL
+++ /dev/null
@@ -1,9 +0,0 @@
-Trusster Open Source License version 1.0a (TRUST) copyright (c) 2006 Mike Mintz and Robert Ekendahl. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- * Redistributions in any form must be accompanied by information on how to obtain complete source code for this software and any accompanying software that uses this software. The source code must either be included in the distribution or be available in a timely fashion for no more than the cost of distribution plus a nominal fee, and must be freely redistributable under reasonable and no more restrictive conditions. For an executable file, complete source code means the source code for all modules it contains. It does not include source code for modules or files that typically accompany the major components of the operating system on which the executable file runs.
-
-THIS SOFTWARE IS PROVIDED BY MIKE MINTZ AND ROBERT EKENDAHL ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL MIKE MINTZ AND ROBERT EKENDAHL OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/TPDL b/options/license/TPDL
deleted file mode 100644
index d950f8f19e..0000000000
--- a/options/license/TPDL
+++ /dev/null
@@ -1,2 +0,0 @@
-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/TPL-1.0 b/options/license/TPL-1.0
deleted file mode 100644
index 1634db4912..0000000000
--- a/options/license/TPL-1.0
+++ /dev/null
@@ -1,475 +0,0 @@
-THOR Public Licence (TPL)
-
-0. Notes of Origin
-
-0.1 As required by paragraph 6.3 of the "Mozilla Public Licence",
-"MPL" in the following, it is hereby stated that this Licence
-condition ("TPL") differs in the following items from the original
-"Mozilla Public Licence" as provided by "Netscape Communications
-Corporation":
-
-a) Paragraphs 6.2 and 6.3 of the MPL has been modified to bind licence
-modifications to the Author of this Licence, Thomas Richter.
-
-b) Paragraph 11 has been modified to gover this Licence by German
-law rather than Californian Law.
-
-c) The licence has been renamed to "TPL" and "THOR Public
-Licence". All references towards "MPL" have been removed except in
-section 0 to indicate the difference from "MPL".
-
-No other modifications have been made.
-
-
-1. Definitions.
-
-1.0.1. "Commercial Use" means distribution or otherwise making the
-Covered Code available to a third party.
-
-1.1. "Contributor" means each entity that creates or contributes to
-the creation of Modifications.
-
-1.2. "Contributor Version" means the combination of the Original Code,
-prior Modifications used by a Contributor, and the Modifications made
-by that particular Contributor.
-
-1.3. "Covered Code" means the Original Code or Modifications or the
-combination of the Original Code and Modifications, in each case
-including portions thereof.
-
-1.4. "Electronic Distribution Mechanism" means a mechanism generally
-accepted in the software development community for the electronic
-transfer of data.
-
-1.5. "Executable" means Covered Code in any form other than Source
-Code.
-
-1.6. "Initial Developer" means the individual or entity identified as
-the Initial Developer in the Source Code notice required by Exhibit A.
-
-1.7. "Larger Work" means a work which combines Covered Code or
-portions thereof with code not governed by the terms of this License.
-
-1.8. "License" means this document.
-
-1.8.1. "Licensable" means having the right to grant, to the maximum
-extent possible, whether at the time of the initial grant or
-subsequently acquired, any and all of the rights conveyed herein.
-
-1.9. "Modifications" means any addition to or deletion from the
-substance or structure of either the Original Code or any previous
-Modifications. When Covered Code is released as a series of files, a
-Modification is: A. Any addition to or deletion from the contents of a
-file containing Original Code or previous Modifications.
-
-B. Any new file that contains any part of the Original Code or
-previous Modifications.
-
-1.10. "Original Code" means Source Code of computer software code
-which is described in the Source Code notice required by Exhibit A as
-Original Code, and which, at the time of its release under this
-License is not already Covered Code governed by this License.
-
-1.10.1. "Patent Claims" means any patent claim(s), now owned or
-hereafter acquired, including without limitation, method, process, and
-apparatus claims, in any patent Licensable by grantor.
-
-1.11. "Source Code" means the preferred form of the Covered Code for
-making modifications to it, including all modules it contains, plus
-any associated interface definition files, scripts used to control
-compilation and installation of an Executable, or source code
-differential comparisons against either the Original Code or another
-well known, available Covered Code of the Contributor's choice. The
-Source Code can be in a compressed or archival form, provided the
-appropriate decompression or de-archiving software is widely available
-for no charge.
-
-1.12. "You" (or "Your") means an individual or a legal entity
-exercising rights under, and complying with all of the terms of, this
-License or a future version of this License issued under Section
-6.1. For legal entities, "You" includes any entity which controls, is
-controlled by, or is under common control with You. For purposes of
-this definition, "control" means (a) the power, direct or indirect, to
-cause the direction or management of such entity, whether by contract
-or otherwise, or (b) ownership of more than fifty percent (50%) of the
-outstanding shares or beneficial ownership of such entity.
-
-2. Source Code License.
-
-2.1. The Initial Developer Grant. The Initial Developer hereby grants
-You a world-wide, royalty-free, non-exclusive license, subject to
-third party intellectual property claims: (a) under intellectual
-property rights (other than patent or trademark) Licensable by Initial
-Developer to use, reproduce, modify, display, perform, sublicense and
-distribute the Original Code (or portions thereof) with or without
-Modifications, and/or as part of a Larger Work; and
-
-(b) under Patents Claims infringed by the making, using or selling of
-Original Code, to make, have made, use, practice, sell, and offer for
-sale, and/or otherwise dispose of the Original Code (or portions
-thereof).
-
-(c) the licenses granted in this Section 2.1(a) and (b) are effective
-on the date Initial Developer first distributes Original Code under
-the terms of this License.
-
-(d) Notwithstanding Section 2.1(b) above, no patent license is
-granted: 1) for code that You delete from the Original Code; 2)
-separate from the Original Code; or 3) for infringements caused by: i)
-the modification of the Original Code or ii) the combination of the
-Original Code with other software or devices.
-
-2.2. Contributor Grant. Subject to third party intellectual property
-claims, each Contributor hereby grants You a world-wide, royalty-free,
-non-exclusive license
-
-(a) under intellectual property rights (other than patent or
-trademark) Licensable by Contributor, to use, reproduce, modify,
-display, perform, sublicense and distribute the Modifications created
-by such Contributor (or portions thereof) either on an unmodified
-basis, with other Modifications, as Covered Code and/or as part of a
-Larger Work; and
-
-(b) under Patent Claims infringed by the making, using, or selling of
-Modifications made by that Contributor either alone and/or in
-combination with its Contributor Version (or portions of such
-combination), to make, use, sell, offer for sale, have made, and/or
-otherwise dispose of: 1) Modifications made by that Contributor (or
-portions thereof); and 2) the combination of Modifications made by
-that Contributor with its Contributor Version (or portions of such
-combination).
-
-(c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective
-on the date Contributor first makes Commercial Use of the Covered
-Code.
-
-(d) Notwithstanding Section 2.2(b) above, no patent license is
-granted: 1) for any code that Contributor has deleted from the
-Contributor Version; 2) separate from the Contributor Version; 3) for
-infringements caused by: i) third party modifications of Contributor
-Version or ii) the combination of Modifications made by that
-Contributor with other software (except as part of the Contributor
-Version) or other devices; or 4) under Patent Claims infringed by
-Covered Code in the absence of Modifications made by that Contributor.
-
-
-3. Distribution Obligations.
-
-3.1. Application of License. The Modifications which You create or to
-which You contribute are governed by the terms of this License,
-including without limitation Section 2.2. The Source Code version of
-Covered Code may be distributed only under the terms of this License
-or a future version of this License released under Section 6.1, and
-You must include a copy of this License with every copy of the Source
-Code You distribute. You may not offer or impose any terms on any
-Source Code version that alters or restricts the applicable version of
-this License or the recipients' rights hereunder. However, You may
-include an additional document offering the additional rights
-described in Section 3.5.
-
-3.2. Availability of Source Code. Any Modification which You create
-or to which You contribute must be made available in Source Code form
-under the terms of this License either on the same media as an
-Executable version or via an accepted Electronic Distribution
-Mechanism to anyone to whom you made an Executable version available;
-and if made available via Electronic Distribution Mechanism, must
-remain available for at least twelve (12) months after the date it
-initially became available, or at least six (6) months after a
-subsequent version of that particular Modification has been made
-available to such recipients. You are responsible for ensuring that
-the Source Code version remains available even if the Electronic
-Distribution Mechanism is maintained by a third party.
-
-3.3. Description of Modifications. You must cause all Covered Code to
-which You contribute to contain a file documenting the changes You
-made to create that Covered Code and the date of any change. You must
-include a prominent statement that the Modification is derived,
-directly or indirectly, from Original Code provided by the Initial
-Developer and including the name of the Initial Developer in (a) the
-Source Code, and (b) in any notice in an Executable version or related
-documentation in which You describe the origin or ownership of the
-Covered Code.
-
-3.4. Intellectual Property Matters (a) Third Party Claims. If
-Contributor has knowledge that a license under a third party's
-intellectual property rights is required to exercise the rights
-granted by such Contributor under Sections 2.1 or 2.2, Contributor
-must include a text file with the Source Code distribution titled
-"LEGAL" which describes the claim and the party making the claim in
-sufficient detail that a recipient will know whom to contact. If
-Contributor obtains such knowledge after the Modification is made
-available as described in Section 3.2, Contributor shall promptly
-modify the LEGAL file in all copies Contributor makes available
-thereafter and shall take other steps (such as notifying appropriate
-mailing lists or newsgroups) reasonably calculated to inform those who
-received the Covered Code that new knowledge has been obtained.
-
-(b) Contributor APIs. If Contributor's Modifications include an
-application programming interface and Contributor has knowledge of
-patent licenses which are reasonably necessary to implement that API,
-Contributor must also include this information in the LEGAL file.
-
-(c) Representations. Contributor represents that, except as disclosed
-pursuant to Section 3.4(a) above, Contributor believes that
-Contributor's Modifications are Contributor's original creation(s)
-and/or Contributor has sufficient rights to grant the rights conveyed
-by this License.
-
-
-3.5. Required Notices. You must duplicate the notice in Exhibit A in
-each file of the Source Code. If it is not possible to put such
-notice in a particular Source Code file due to its structure, then You
-must include such notice in a location (such as a relevant directory)
-where a user would be likely to look for such a notice. If You
-created one or more Modification(s) You may add your name as a
-Contributor to the notice described in Exhibit A. You must also
-duplicate this License in any documentation for the Source Code where
-You describe recipients' rights or ownership rights relating to
-Covered Code. You may choose to offer, and to charge a fee for,
-warranty, support, indemnity or liability obligations to one or more
-recipients of Covered Code. However, You may do so only on Your own
-behalf, and not on behalf of the Initial Developer or any
-Contributor. You must make it absolutely clear than any such warranty,
-support, indemnity or liability obligation is offered by You alone,
-and You hereby agree to indemnify the Initial Developer and every
-Contributor for any liability incurred by the Initial Developer or
-such Contributor as a result of warranty, support, indemnity or
-liability terms You offer.
-
-3.6. Distribution of Executable Versions. You may distribute Covered
-Code in Executable form only if the requirements of Section 3.1-3.5
-have been met for that Covered Code, and if You include a notice
-stating that the Source Code version of the Covered Code is available
-under the terms of this License, including a description of how and
-where You have fulfilled the obligations of Section 3.2. The notice
-must be conspicuously included in any notice in an Executable version,
-related documentation or collateral in which You describe recipients'
-rights relating to the Covered Code. You may distribute the Executable
-version of Covered Code or ownership rights under a license of Your
-choice, which may contain terms different from this License, provided
-that You are in compliance with the terms of this License and that the
-license for the Executable version does not attempt to limit or alter
-the recipient's rights in the Source Code version from the rights set
-forth in this License. If You distribute the Executable version under
-a different license You must make it absolutely clear that any terms
-which differ from this License are offered by You alone, not by the
-Initial Developer or any Contributor. You hereby agree to indemnify
-the Initial Developer and every Contributor for any liability incurred
-by the Initial Developer or such Contributor as a result of any such
-terms You offer.
-
-3.7. Larger Works. You may create a Larger Work by combining Covered
-Code with other code not governed by the terms of this License and
-distribute the Larger Work as a single product. In such a case, You
-must make sure the requirements of this License are fulfilled for the
-Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-
-If it is impossible for You to comply with any of the terms of this
-License with respect to some or all of the Covered Code due to
-statute, judicial order, or regulation then You must: (a) comply with
-the terms of this License to the maximum extent possible; and (b)
-describe the limitations and the code they affect. Such description
-must be included in the LEGAL file described in Section 3.4 and must
-be included with all distributions of the Source Code. Except to the
-extent prohibited by statute or regulation, such description must be
-sufficiently detailed for a recipient of ordinary skill to be able to
-understand it.
-
-5. Application of this License.
-
-This License applies to code to which the Initial Developer has
-attached the notice in Exhibit A and to related Covered Code.
-
-6. Versions of the License.
-
-6.1. New Versions. Thomas Richter may publish revised and/or new
-versions of the License from time to time. Each version will be given
-a distinguishing version number.
-
-6.2. Effect of New Versions. Once Covered Code has been published
-under a particular version of the License, You may always continue to
-use it under the terms of that version. You may also choose to use
-such Covered Code under the terms of any subsequent version of the
-License published by Thomas Richter. No one other than Thomas Richter
-has the right to modify the terms applicable to Covered Code created
-under this License.
-
-6.3. Derivative Works. If You create or use a modified version of
-this License (which you may only do in order to apply it to code which
-is not already Covered Code governed by this License), You must (a)
-rename Your license so that the phrases "TPL", "THOR Software",
-"Thomas Richter" or any confusingly similar phrase do not appear in
-your license (except to note that your license differs from this
-License) and (b) otherwise make it clear that Your version of the
-license contains terms which differ from the THOR Public
-License. (Filling in the name of the Initial Developer, Original Code
-or Contributor in the notice described in Exhibit A shall not of
-themselves be deemed to be modifications of this License.)
-
-7. DISCLAIMER OF WARRANTY.
-
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
-WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
-WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
-DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR
-NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF
-THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE
-IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER
-CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR
-CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART
-OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER
-EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-
-8.1. This License and the rights granted hereunder will terminate
-automatically if You fail to comply with terms herein and fail to cure
-such breach within 30 days of becoming aware of the breach. All
-sublicenses to the Covered Code which are properly granted shall
-survive any termination of this License. Provisions which, by their
-nature, must remain in effect beyond the termination of this License
-shall survive.
-
-8.2. If You initiate litigation by asserting a patent infringement
-claim (excluding declatory judgment actions) against Initial Developer
-or a Contributor (the Initial Developer or Contributor against whom
-You file such action is referred to as "Participant") alleging that:
-
-(a) such Participant's Contributor Version directly or indirectly
-infringes any patent, then any and all rights granted by such
-Participant to You under Sections 2.1 and/or 2.2 of this License
-shall, upon 60 days notice from Participant terminate prospectively,
-unless if within 60 days after receipt of notice You either: (i) agree
-in writing to pay Participant a mutually agreeable reasonable royalty
-for Your past and future use of Modifications made by such
-Participant, or (ii) withdraw Your litigation claim with respect to
-the Contributor Version against such Participant. If within 60 days
-of notice, a reasonable royalty and payment arrangement are not
-mutually agreed upon in writing by the parties or the litigation claim
-is not withdrawn, the rights granted by Participant to You under
-Sections 2.1 and/or 2.2 automatically terminate at the expiration of
-the 60 day notice period specified above.
-
-(b) any software, hardware, or device, other than such Participant's
-Contributor Version, directly or indirectly infringes any patent, then
-any rights granted to You by such Participant under Sections 2.1(b)
-and 2.2(b) are revoked effective as of the date You first made, used,
-sold, distributed, or had made, Modifications made by that
-Participant.
-
-8.3. If You assert a patent infringement claim against Participant
-alleging that such Participant's Contributor Version directly or
-indirectly infringes any patent where such claim is resolved (such as
-by license or settlement) prior to the initiation of patent
-infringement litigation, then the reasonable value of the licenses
-granted by such Participant under Sections 2.1 or 2.2 shall be taken
-into account in determining the amount or value of any payment or
-license.
-
-8.4. In the event of termination under Sections 8.1 or 8.2 above, all
-end user license agreements (excluding distributors and resellers)
-which have been validly granted by You or any distributor hereunder
-prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY.
-
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
-(INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
-DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
-OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
-ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
-CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
-WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
-COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
-INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
-LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
-RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
-PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
-EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
-THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS.
-
-The Covered Code is a "commercial item," as that term is defined in 48
-C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software"
-and "commercial computer software documentation," as such terms are
-used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
-C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
-all U.S. Government End Users acquire Covered Code with only those
-rights set forth herein.
-
-11. MISCELLANEOUS.
-
-This License represents the complete agreement concerning subject
-matter hereof. If any provision of this License is held to be
-unenforceable, such provision shall be reformed only to the extent
-necessary to make it enforceable. This License shall be governed by
-German law provisions (except to the extent applicable law, if any,
-provides otherwise), excluding its conflict-of-law provisions. With
-respect to disputes in which at least one party is a citizen of, or an
-entity chartered or registered to do business in Federal Republic of
-Germany, any litigation relating to this License shall be subject to
-the jurisdiction of the Federal Courts of the Federal Republic of
-Germany, with the losing party responsible for costs, including
-without limitation, court costs and reasonable attorneys' fees and
-expenses. Any law or regulation which provides that the language of a
-contract shall be construed against the drafter shall not apply to
-this License.
-
-12. RESPONSIBILITY FOR CLAIMS.
-
-As between Initial Developer and the Contributors, each party is
-responsible for claims and damages arising, directly or indirectly,
-out of its utilization of rights under this License and You agree to
-work with Initial Developer and Contributors to distribute such
-responsibility on an equitable basis. Nothing herein is intended or
-shall be deemed to constitute any admission of liability.
-
-13. MULTIPLE-LICENSED CODE.
-
-Initial Developer may designate portions of the Covered Code as
-Multiple-Licensed. Multiple-Licensed means that the Initial Developer
-permits you to utilize portions of the Covered Code under Your choice
-of the TPL or the alternative licenses, if any, specified by the
-Initial Developer in the file described in Exhibit A.
-
-
-EXHIBIT A - THOR Public License.
-
-The contents of this file are subject to the THOR Public License
-Version 1.0 (the "License"); you may not use this file except in
-compliance with the License.
-
-Software distributed under the License is distributed on an "AS IS"
-basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-the License for the specificlanguage governing rights and limitations
-under the License.
-
-The Original Code is ______________________________________.
-
-The Initial Developer of the Original Code is _____________.
-
-Portions created by ______________________ are
-Copyright (C) ______ _______________________.
-
-All Rights Reserved.
-
-Contributor(s): ______________________________________.
-
-Alternatively, the contents of this file may be used under the terms
-of the _____ license (the [___] License), in which case the provisions
-of [______] License are applicable instead of those above. If you
-wish to allow use of your version of this file only under the terms of
-the [____] License and not to allow others to use your version of this
-file under the TPL, indicate your decision by deleting the provisions
-above and replace them with the notice and other provisions required
-by the [___] License. If you do not delete the provisions above, a
-recipient may use your version of this file under either the TPL or
-the [___] License."
-
-[NOTE: The text of this Exhibit A may differ slightly from the text of
-the notices in the Source Code files of the Original Code. You should
-use the text of this Exhibit A rather than the text found in the
-Original Code Source Code for Your Modifications.]
diff --git a/options/license/TTWL b/options/license/TTWL
deleted file mode 100644
index c13d3fbe04..0000000000
--- a/options/license/TTWL
+++ /dev/null
@@ -1,8 +0,0 @@
-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.
diff --git a/options/license/TTYP0 b/options/license/TTYP0
deleted file mode 100644
index 3df2e4c4e9..0000000000
--- a/options/license/TTYP0
+++ /dev/null
@@ -1,29 +0,0 @@
-THE TTYP0 LICENSE
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this font software and associated files (the "Software"),
-to deal in the Software without restriction, including without
-limitation the rights to use, copy, modify, merge, publish,
-distribute, embed, 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:
-
-(1) The above copyright notice, this permission notice, and the
- disclaimer below shall be included in all copies or substantial
- portions of the Software.
-
-(2) If the design of any glyphs in the fonts that are contained in the
- Software or generated during the installation process is modified
- or if additional glyphs are added to the fonts, the fonts
- must be renamed. The new names may not contain the word "UW",
- irrespective of capitalisation; the new names may contain the word
- "ttyp0", irrespective of capitalisation, only if preceded by a
- foundry name different from "UW".
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/options/license/TU-Berlin-1.0 b/options/license/TU-Berlin-1.0
deleted file mode 100644
index 08f954e88c..0000000000
--- a/options/license/TU-Berlin-1.0
+++ /dev/null
@@ -1,10 +0,0 @@
-Copyright 1992 by Jutta Degener and Carsten Bormann,
-Technische Universitaet Berlin
-
-Any use of this software is permitted provided that this notice is not removed and that neither the authors nor the Technische Universitaet Berlin are deemed to have made any representations as to the suitability of this software for any purpose nor are held responsible for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
-
-As a matter of courtesy, the authors request to be informed about uses this software has found, about bugs in this software, and about any improvements that may be of general interest.
-
-Berlin, 15.09.1992
-Jutta Degener
-Carsten Bormann
diff --git a/options/license/TU-Berlin-2.0 b/options/license/TU-Berlin-2.0
deleted file mode 100644
index 82897c1871..0000000000
--- a/options/license/TU-Berlin-2.0
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
-Technische Universitaet Berlin
-
-Any use of this software is permitted provided that this notice is not removed and that neither the authors nor the Technische Universitaet Berlin are deemed to have made any representations as to the suitability of this software for any purpose nor are held responsible for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
-
-As a matter of courtesy, the authors request to be informed about uses this software has found, about bugs in this software, and about any improvements that may be of general interest.
-
-Berlin, 28.11.1994
-Jutta Degener
-Carsten Bormann
-
-oOo
-
-Since the original terms of 15 years ago maybe do not make our intentions completely clear given today's refined usage of the legal terms, we append this additional permission:
-
-Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that this notice is not removed and that neither the authors nor the Technische Universitaet Berlin are deemed to have made any representations as to the suitability of this software for any purpose nor are held responsible for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
-
-Berkeley/Bremen, 05.04.2009
-Jutta Degener
-Carsten Bormann
diff --git a/options/license/TermReadKey b/options/license/TermReadKey
deleted file mode 100644
index ee668e0f31..0000000000
--- a/options/license/TermReadKey
+++ /dev/null
@@ -1 +0,0 @@
-Unlimited distribution and/or modification is allowed as long as this copyright notice remains intact.
diff --git a/options/license/Texinfo-exception b/options/license/Texinfo-exception
deleted file mode 100644
index 931a4070b4..0000000000
--- a/options/license/Texinfo-exception
+++ /dev/null
@@ -1,4 +0,0 @@
-As a special exception, when this file is read by TeX when
-processing a Texinfo source document, you may use the result without
-restriction. This Exception is an additional permission under
-section 7 of the GNU General Public License, version 3 ("GPLv3").
diff --git a/options/license/ThirdEye b/options/license/ThirdEye
deleted file mode 100644
index ce75b566e3..0000000000
--- a/options/license/ThirdEye
+++ /dev/null
@@ -1,7 +0,0 @@
-(C) Copyright 1984 by Third Eye Software, Inc.
-
-Third Eye Software, Inc. grants reproduction and use rights to
-all parties, PROVIDED that this comment is maintained in the copy.
-
-Third Eye makes no claims about the applicability of this
-symbol table to a particular use.
diff --git a/options/license/TrustedQSL b/options/license/TrustedQSL
deleted file mode 100644
index 982d4269f6..0000000000
--- a/options/license/TrustedQSL
+++ /dev/null
@@ -1,58 +0,0 @@
-Copyright (C) 2001-2015 American Radio Relay League, Inc. All rights
-reserved.
-
-Portions (C) 2003-2023 The TrustedQSL Developers. Please see the AUTHORS.txt
-file for contributors.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Any redistribution of source code must retain the above copyright
-notice, this list of conditions and the disclaimer shown in
-Paragraph 5 (below).
-
-2. Redistribution in binary form must reproduce the above copyright
-notice, this list of conditions and the disclaimer shown in
-Paragraph 5 (below) in the documentation and/or other materials
-provided with the distribution.
-
-3. Products derived from or including this software may not use
-"Logbook of the World" or "LoTW" or any other American Radio Relay
-League, Incorporated trademarks or servicemarks in their names
-without prior written permission of the ARRL. See Paragraph 6
-(below) for contact information.
-
-4. Use of this software does not imply endorsement by ARRL of
-products derived from or including this software and vendors may not
-claim such endorsement.
-
-5. Disclaimer: This software is provided "as-is" without
-representation, guarantee or warranty of any kind, either express or
-implied, including but not limited to the implied warranties of
-merchantability or of fitness for a particular purpose. The entire
-risk as to the quality and performance of the software is solely
-with you. Should the software prove defective, you (and not the
-American Radio Relay League, its officers, directors, employees or
-agents) assume the entire cost of all necessary servicing, repair or
-correction. In no event will ARRL be liable to you or to any third
-party for any damages, whether direct or indirect, including lost
-profits, lost savings, or other incidental or consequential damages
-arising out of the use or inability to use such software, regardless
-of whether ARRL has been advised of the possibility of such damages.
-
-6. Contact information:
-
-American Radio Relay League, Inc.
-Attn: Logbook of the World Manager
-225 Main St
-Newington, CT 06111
-voice: 860-594-0200
-fax: 860-594-0259
-email: logbook@arrl.org
-Worldwide Web: www.arrl.org
-
-This software consists of voluntary contributions made by many
-individuals on behalf of the ARRL. More information on the "Logbook
-of The World" project and the ARRL is available from the ARRL Web
-site at www.arrl.org.
diff --git a/options/license/UBDL-exception b/options/license/UBDL-exception
deleted file mode 100644
index 780ddcd775..0000000000
--- a/options/license/UBDL-exception
+++ /dev/null
@@ -1,59 +0,0 @@
-UNMODIFIED BINARY DISTRIBUTION LICENCE
-
-
-PREAMBLE
-
-The GNU General Public License provides a legal guarantee that
-software covered by it remains free (in the sense of freedom, not
-price). It achieves this guarantee by imposing obligations on anyone
-who chooses to distribute the software.
-
-Some of these obligations may be seen as unnecessarily burdensome. In
-particular, when the source code for the software is already publicly
-and freely available, there is minimal value in imposing upon each
-distributor the obligation to provide the complete source code (or an
-equivalent written offer to provide the complete source code).
-
-This Licence allows for the distribution of unmodified binaries built
-from publicly available source code, without imposing the obligations
-of the GNU General Public License upon anyone who chooses to
-distribute only the unmodified binaries built from that source code.
-
-The extra permissions granted by this Licence apply only to unmodified
-binaries built from source code which has already been made available
-to the public in accordance with the terms of the GNU General Public
-Licence. Nothing in this Licence allows for the creation of
-closed-source modified versions of the Program. Any modified versions
-of the Program are subject to the usual terms and conditions of the
-GNU General Public License.
-
-
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-This Licence applies to any Program or other work which contains a
-notice placed by the copyright holder saying it may be distributed
-under the terms of this Unmodified Binary Distribution Licence. All
-terms used in the text of this Licence are to be interpreted as they
-are used in version 2 of the GNU General Public License as published
-by the Free Software Foundation.
-
-If you have made this Program available to the public in both source
-code and executable form in accordance with the terms of the GNU
-General Public License as published by the Free Software Foundation;
-either version 2 of the License, or (at your option) any later
-version, then you are hereby granted an additional permission to use,
-copy, and distribute the unmodified executable form of this Program
-(the "Unmodified Binary") without restriction, including the right to
-permit persons to whom the Unmodified Binary is furnished to do
-likewise, subject to the following conditions:
-
-- when started running, the Program must display an announcement which
- includes the details of your existing publication of the Program
- made in accordance with the terms of the GNU General Public License.
- For example, the Program could display the URL of the publicly
- available source code from which the Unmodified Binary was built.
-
-- when exercising your right to grant permissions under this Licence,
- you do not need to refer directly to the text of this Licence, but
- you may not grant permissions beyond those granted to you by this
- Licence.
diff --git a/options/license/UCAR b/options/license/UCAR
deleted file mode 100644
index 36e1810283..0000000000
--- a/options/license/UCAR
+++ /dev/null
@@ -1,32 +0,0 @@
-Copyright 2014 University Corporation for Atmospheric Research and contributors.
-All rights reserved.
-
-This software was developed by the Unidata Program Center of the
-University Corporation for Atmospheric Research (UCAR)
-<http://www.unidata.ucar.edu>.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
- 1) Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- 2) Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- 3) Neither the names of the development group, the copyright holders, nor the
- names of contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
- 4) This license shall terminate automatically and you may no longer exercise
- any of the rights granted to you by this license as of the date you
- commence an action, including a cross-claim or counterclaim, against
- the copyright holders or any contributor alleging that this software
- infringes a patent. This termination provision shall not apply for an
- action alleging patent infringement by combinations of this software with
- other software or hardware.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS
-OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
diff --git a/options/license/UCL-1.0 b/options/license/UCL-1.0
deleted file mode 100644
index 8915f82b45..0000000000
--- a/options/license/UCL-1.0
+++ /dev/null
@@ -1,48 +0,0 @@
-Upstream Compatibility License v. 1.0 (UCL-1.0)
-
-This Upstream Compatibility License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
-
- Licensed under the Upstream Compatibility License 1.0
-
-1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
-
- a) to reproduce the Original Work in copies, either alone or as part of a collective work;
-
- b) to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
-
- c) to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work You distribute or communicate shall be licensed under this Upstream Compatibility License and all Derivative Work You distribute or communicate
- shall be licensed under both this Upstream Compatibility License and the Apache License 2.0 or later;
-
- d) to perform the Original Work publicly; and
-
- e) to display the Original Work publicly.
-
-2) Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
-
-3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
-
-4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor’s trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
-
-5) External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
-
-6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
-
-7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
-
-8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
-
-9) Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including “fair use†or “fair dealingâ€). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
-
-10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
-
-11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
-
-12) Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
-
-13) Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
-
-14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
-
-16) Modification of This License. This License is Copyright (c) 2005 Lawrence Rosen and Copyright (c) 2017 Nigel Tzeng. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" or the "Upstream Compatibility License" or "UCL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
diff --git a/options/license/UMich-Merit b/options/license/UMich-Merit
deleted file mode 100644
index 93e304b90e..0000000000
--- a/options/license/UMich-Merit
+++ /dev/null
@@ -1,19 +0,0 @@
-[C] The Regents of the University of Michigan and Merit Network, Inc. 1992,
-1993, 1994, 1995 All Rights Reserved
-
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose and without fee is hereby granted, provided
-that the above copyright notice and this permission notice appear in all
-copies of the software and derivative works or modified versions thereof,
-and that both the copyright notice and this permission and disclaimer
-notice appear in supporting documentation.
-
-THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
-EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE REGENTS OF THE
-UNIVERSITY OF MICHIGAN AND MERIT NETWORK, INC. DO NOT WARRANT THAT THE
-FUNCTIONS CONTAINED IN THE SOFTWARE WILL MEET LICENSEE'S REQUIREMENTS OR
-THAT OPERATION WILL BE UNINTERRUPTED OR ERROR FREE. The Regents of the
-University of Michigan and Merit Network, Inc. shall not be liable for any
-special, indirect, incidental or consequential damages with respect to any
-claim by Licensee or any third party arising from use of the software.
diff --git a/options/license/URT-RLE b/options/license/URT-RLE
deleted file mode 100644
index 11dad58c21..0000000000
--- a/options/license/URT-RLE
+++ /dev/null
@@ -1,15 +0,0 @@
- * This software is copyrighted as noted below. It may be freely copied,
- * modified, and redistributed, provided that the copyright notice is
- * preserved on all copies.
- *
- * There is no warranty or other guarantee of fitness for this software,
- * it is provided solely "as is". Bug reports or fixes may be sent
- * to the author, who may or may not act on them as he desires.
- *
- * You may not include this software in a program or other software product
- * without supplying the source, or without informing the end-user that the
- * source is available for no extra charge.
- *
- * If you modify this software, you should include a notice giving the
- * name of the person performing the modification, the date of modification,
- * and the reason for such modification.
diff --git a/options/license/Ubuntu-font-1.0 b/options/license/Ubuntu-font-1.0
deleted file mode 100644
index ae78a8f94e..0000000000
--- a/options/license/Ubuntu-font-1.0
+++ /dev/null
@@ -1,96 +0,0 @@
--------------------------------
-UBUNTU FONT LICENCE Version 1.0
--------------------------------
-
-PREAMBLE
-This licence allows the licensed fonts to be used, studied, modified and
-redistributed freely. The fonts, including any derivative works, can be
-bundled, embedded, and redistributed provided the terms of this licence
-are met. The fonts and derivatives, however, cannot be released under
-any other licence. The requirement for fonts to remain under this
-licence does not require any document created using the fonts or their
-derivatives to be published under this licence, as long as the primary
-purpose of the document is not to be a vehicle for the distribution of
-the fonts.
-
-DEFINITIONS
-"Font Software" refers to the set of files released by the Copyright
-Holder(s) under this licence and clearly marked as such. This may
-include source files, build scripts and documentation.
-
-"Original Version" refers to the collection of Font Software components
-as received under this licence.
-
-"Modified Version" refers to any derivative made by adding to, deleting,
-or substituting -- in part or in whole -- any of the components of the
-Original Version, by changing formats or by porting the Font Software to
-a new environment.
-
-"Copyright Holder(s)" refers to all individuals and companies who have a
-copyright ownership of the Font Software.
-
-"Substantially Changed" refers to Modified Versions which can be easily
-identified as dissimilar to the Font Software by users of the Font
-Software comparing the Original Version with the Modified Version.
-
-To "Propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification and with or without charging
-a redistribution fee), making available to the public, and in some
-countries other activities as well.
-
-PERMISSION & CONDITIONS
-This licence does not grant any rights under trademark law and all such
-rights are reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of the Font Software, to propagate the Font Software, subject to
-the below conditions:
-
-1) Each copy of the Font Software must contain the above copyright
-notice and this licence. These can be included either as stand-alone
-text files, human-readable headers or in the appropriate machine-
-readable metadata fields within text or binary files as long as those
-fields can be easily viewed by the user.
-
-2) The font name complies with the following:
-(a) The Original Version must retain its name, unmodified.
-(b) Modified Versions which are Substantially Changed must be renamed to
-avoid use of the name of the Original Version or similar names entirely.
-(c) Modified Versions which are not Substantially Changed must be
-renamed to both (i) retain the name of the Original Version and (ii) add
-additional naming elements to distinguish the Modified Version from the
-Original Version. The name of such Modified Versions must be the name of
-the Original Version, with "derivative X" where X represents the name of
-the new work, appended to that name.
-
-3) The name(s) of the Copyright Holder(s) and any contributor to the
-Font Software shall not be used to promote, endorse or advertise any
-Modified Version, except (i) as required by this licence, (ii) to
-acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with
-their explicit written permission.
-
-4) The Font Software, modified or unmodified, in part or in whole, must
-be distributed entirely under this licence, and must not be distributed
-under any other licence. The requirement for fonts to remain under this
-licence does not affect any document created using the Font Software,
-except any version of the Font Software extracted from a document
-created using the Font Software may only be distributed under this
-licence.
-
-TERMINATION
-This licence becomes null and void if any of the above conditions are
-not met.
-
-DISCLAIMER
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
-COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
-DEALINGS IN THE FONT SOFTWARE.
diff --git a/options/license/Unicode-3.0 b/options/license/Unicode-3.0
deleted file mode 100644
index 11f2842a30..0000000000
--- a/options/license/Unicode-3.0
+++ /dev/null
@@ -1,39 +0,0 @@
-UNICODE LICENSE V3
-
-COPYRIGHT AND PERMISSION NOTICE
-
-Copyright © 1991-2023 Unicode, Inc.
-
-NOTICE TO USER: Carefully read the following legal agreement. BY
-DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
-SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
-TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
-DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of data files and any associated documentation (the "Data Files") or
-software and any associated documentation (the "Software") to deal in the
-Data Files or Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, and/or sell
-copies of the Data Files or Software, and to permit persons to whom the
-Data Files or Software are furnished to do so, provided that either (a)
-this copyright and permission notice appear with all copies of the Data
-Files or Software, or (b) this copyright and permission notice appear in
-associated Documentation.
-
-THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
-KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
-THIRD PARTY RIGHTS.
-
-IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
-BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
-OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
-WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
-ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
-FILES OR SOFTWARE.
-
-Except as contained in this notice, the name of a copyright holder shall
-not be used in advertising or otherwise to promote the sale, use or other
-dealings in these Data Files or Software without prior written
-authorization of the copyright holder.
diff --git a/options/license/Unicode-DFS-2015 b/options/license/Unicode-DFS-2015
deleted file mode 100644
index 278eee1e5e..0000000000
--- a/options/license/Unicode-DFS-2015
+++ /dev/null
@@ -1,19 +0,0 @@
-UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
-
-Unicode Data Files include all data files under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/, and http://www.unicode.org/cldr/data/. Unicode Data Files do not include PDF online code charts under the directory http://www.unicode.org/Public/. Software includes any source code published in the Unicode Standard or under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/, and http://www.unicode.org/cldr/data/.
-
-NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
-
-COPYRIGHT AND PERMISSION NOTICE
-
-Copyright © 1991-2015 Unicode, Inc. All rights reserved. Distributed under the Terms of Use in http://www.unicode.org/copyright.html.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of the Unicode data files and any associated documentation (the "Data Files") or Unicode software and any associated documentation (the "Software") to deal in the Data Files or Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Data Files or Software, and to permit persons to whom the Data Files or Software are furnished to do so, provided that
-
- (a) this copyright and permission notice appear with all copies of the Data Files or Software,
- (b) this copyright and permission notice appear in associated documentation, and
- (c) there is clear notice in each modified Data File or in the Software as well as in the documentation associated with the Data File(s) or Software that the data or software has been modified.
-
-THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.
-
-Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder.
diff --git a/options/license/Unicode-DFS-2016 b/options/license/Unicode-DFS-2016
deleted file mode 100644
index 71fd6ac5e1..0000000000
--- a/options/license/Unicode-DFS-2016
+++ /dev/null
@@ -1,22 +0,0 @@
-UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
-
-Unicode Data Files include all data files under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/, http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and http://www.unicode.org/utility/trac/browser/.
-
-Unicode Data Files do not include PDF online code charts under the directory http://www.unicode.org/Public/.
-
-Software includes any source code published in the Unicode Standard or under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/, http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and http://www.unicode.org/utility/trac/browser/.
-
-NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
-
-COPYRIGHT AND PERMISSION NOTICE
-
-Copyright © 1991-2016 Unicode, Inc. All rights reserved. Distributed under the Terms of Use in http://www.unicode.org/copyright.html.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of the Unicode data files and any associated documentation (the "Data Files") or Unicode software and any associated documentation (the "Software") to deal in the Data Files or Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Data Files or Software, and to permit persons to whom the Data Files or Software are furnished to do so, provided that either
-
- (a) this copyright and permission notice appear with all copies of the Data Files or Software, or
- (b) this copyright and permission notice appear in associated Documentation.
-
-THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.
-
-Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder.
diff --git a/options/license/Unicode-TOU b/options/license/Unicode-TOU
deleted file mode 100644
index 49e3eee7eb..0000000000
--- a/options/license/Unicode-TOU
+++ /dev/null
@@ -1,51 +0,0 @@
-Unicode Terms of Use
-
-For the general privacy policy governing access to this site, see the Unicode Privacy Policy. For trademark usage, see the Unicode® Consortium Name and Trademark Usage Policy.
-
-A. Unicode Copyright.
-
- 1. Copyright © 1991-2014 Unicode, Inc. All rights reserved.
-
- 2. Certain documents and files on this website contain a legend indicating that "Modification is permitted." Any person is hereby authorized, without fee, to modify such documents and files to create derivative works conforming to the Unicode® Standard, subject to Terms and Conditions herein.
-
- 3. Any person is hereby authorized, without fee, to view, use, reproduce, and distribute all documents and files solely for informational purposes in the creation of products supporting the Unicode Standard, subject to the Terms and Conditions herein.
-
- 4. Further specifications of rights and restrictions pertaining to the use of the particular set of data files known as the "Unicode Character Database" can be found in Exhibit 1.
-
- 5. Each version of the Unicode Standard has further specifications of rights and restrictions of use. For the book editions (Unicode 5.0 and earlier), these are found on the back of the title page. The online code charts carry specific restrictions. All other files, including online documentation of the core specification for Unicode 6.0 and later, are covered under these general Terms of Use.
-
- 6. No license is granted to "mirror" the Unicode website where a fee is charged for access to the "mirror" site.
-
- 7. Modification is not permitted with respect to this document. All copies of this document must be verbatim.
-
-B. Restricted Rights Legend. Any technical data or software which is licensed to the United States of America, its agencies and/or instrumentalities under this Agreement is commercial technical data or commercial computer software developed exclusively at private expense as defined in FAR 2.101, or DFARS 252.227-7014 (June 1995), as applicable. For technical data, use, duplication, or disclosure by the Government is subject to restrictions as set forth in DFARS 202.227-7015 Technical Data, Commercial and Items (Nov 1995) and this Agreement. For Software, in accordance with FAR 12-212 or DFARS 227-7202, as applicable, use, duplication or disclosure by the Government is subject to the restrictions set forth in this Agreement.
-
-C. Warranties and Disclaimers.
-
- 1. This publication and/or website may include technical or typographical errors or other inaccuracies . Changes are periodically added to the information herein; these changes will be incorporated in new editions of the publication and/or website. Unicode may make improvements and/or changes in the product(s) and/or program(s) described in this publication and/or website at any time.
-
- 2. If this file has been purchased on magnetic or optical media from Unicode, Inc. the sole and exclusive remedy for any claim will be exchange of the defective media within ninety (90) days of original purchase.
-
- 3. EXCEPT AS PROVIDED IN SECTION C.2, THIS PUBLICATION AND/OR SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND EITHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. UNICODE AND ITS LICENSORS ASSUME NO RESPONSIBILITY FOR ERRORS OR OMISSIONS IN THIS PUBLICATION AND/OR SOFTWARE OR OTHER DOCUMENTS WHICH ARE REFERENCED BY OR LINKED TO THIS PUBLICATION OR THE UNICODE WEBSITE.
-
-D. Waiver of Damages. In no event shall Unicode or its licensors be liable for any special, incidental, indirect or consequential damages of any kind, or any damages whatsoever, whether or not Unicode was advised of the possibility of the damage, including, without limitation, those resulting from the following: loss of use, data or profits, in connection with the use, modification or distribution of this information or its derivatives.
-
-E. Trademarks & Logos.
-
- 1. The Unicode Word Mark and the Unicode Logo are trademarks of Unicode, Inc. “The Unicode Consortium†and “Unicode, Inc.†are trade names of Unicode, Inc. Use of the information and materials found on this website indicates your acknowledgement of Unicode, Inc.’s exclusive worldwide rights in the Unicode Word Mark, the Unicode Logo, and the Unicode trade names.
-
- 2. The Unicode Consortium Name and Trademark Usage Policy (“Trademark Policyâ€) are incorporated herein by reference and you agree to abide by the provisions of the Trademark Policy, which may be changed from time to time in the sole discretion of Unicode, Inc.
-
- 3. All third party trademarks referenced herein are the property of their respective owners.
-
-F. Miscellaneous.
-
- 1. Jurisdiction and Venue. This server is operated from a location in the State of California, United States of America. Unicode makes no representation that the materials are appropriate for use in other locations. If you access this server from other locations, you are responsible for compliance with local laws. This Agreement, all use of this site and any claims and damages resulting from use of this site are governed solely by the laws of the State of California without regard to any principles which would apply the laws of a different jurisdiction. The user agrees that any disputes regarding this site shall be resolved solely in the courts located in Santa Clara County, California. The user agrees said courts have personal jurisdiction and agree to waive any right to transfer the dispute to any other forum.
-
- 2. Modification by Unicode Unicode shall have the right to modify this Agreement at any time by posting it to this site. The user may not assign any part of this Agreement without Unicode’s prior written consent.
-
- 3. Taxes. The user agrees to pay any taxes arising from access to this website or use of the information herein, except for those based on Unicode’s net income.
-
- 4. Severability. If any provision of this Agreement is declared invalid or unenforceable, the remaining provisions of this Agreement shall remain in effect.
-
- 5. Entire Agreement. This Agreement constitutes the entire agreement between the parties.
diff --git a/options/license/Universal-FOSS-exception-1.0 b/options/license/Universal-FOSS-exception-1.0
deleted file mode 100644
index 4a79cbdcf6..0000000000
--- a/options/license/Universal-FOSS-exception-1.0
+++ /dev/null
@@ -1,11 +0,0 @@
-The Universal FOSS Exception, Version 1.0
-
-In addition to the rights set forth in the other license(s) included in the distribution for this software, data, and/or documentation (collectively the "Software," and such licenses collectively with this additional permission the "Software License"), the copyright holders wish to facilitate interoperability with other software, data, and/or documentation distributed with complete corresponding source under a license that is OSI-approved and/or categorized by the FSF as free (collectively "Other FOSS"). We therefore hereby grant the following additional permission with respect to the use and distribution of the Software with Other FOSS, and the constants, function signatures, data structures and other invocation methods used to run or interact with each of them (as to each, such software's "Interfaces"):
-
-(i) The Software's Interfaces may, to the extent permitted by the license of the Other FOSS, be copied into, used and distributed in the Other FOSS in order to enable interoperability, without requiring a change to the license of the Other FOSS other than as to any Interfaces of the Software embedded therein. The Software's Interfaces remain at all times under the Software License, including without limitation as used in the Other FOSS (which upon any such use also then contains a portion of the Software under the Software License).
-
-(ii) The Other FOSS's Interfaces may, to the extent permitted by the license of the Other FOSS, be copied into, used and distributed in the Software in order to enable interoperability, without requiring that such Interfaces be licensed under the terms of the Software License or otherwise altering their original terms, if this does not require any portion of the Software other than such Interfaces to be licensed under the terms other than the Software License.
-
-(iii) If only Interfaces and no other code is copied between the Software and the Other FOSS in either direction, the use and/or distribution of the Software with the Other FOSS shall not be deemed to require that the Other FOSS be licensed under the license of the Software, other than as to any Interfaces of the Software copied into the Other FOSS. This includes, by way of example and without limitation, statically or dynamically linking the Software together with Other FOSS after enabling interoperability using the Interfaces of one or both, and distributing the resulting combination under different licenses for the respective portions thereof.
-
-For avoidance of doubt, a license which is OSI-approved or categorized by the FSF as free, includes, for the purpose of this permission, such licenses with additional permissions, and any license that has previously been so-approved or categorized as free, even if now deprecated or otherwise no longer recognized as approved or free. Nothing in this additional permission grants any right to distribute any portion of the Software on terms other than those of the Software License or grants any additional permission of any kind for use or distribution of the Software in conjunction with software other than Other FOSS.
diff --git a/options/license/UnixCrypt b/options/license/UnixCrypt
deleted file mode 100644
index 280853382b..0000000000
--- a/options/license/UnixCrypt
+++ /dev/null
@@ -1,6 +0,0 @@
-Copyright (c) 1996 Aki Yoshida. All rights reserved.
-
-Permission to use, copy, modify and distribute this software
-for non-commercial or commercial purposes and without fee is
-hereby granted provided that this copyright notice appears in
-all copies.
diff --git a/options/license/VOSTROM b/options/license/VOSTROM
deleted file mode 100644
index efd0a63137..0000000000
--- a/options/license/VOSTROM
+++ /dev/null
@@ -1,27 +0,0 @@
-VOSTROM Public License for Open Source
-
-Copyright (c) 2007 VOSTROM Holdings, Inc.
-
-This VOSTROM Holdings, Inc. (VOSTROM) Distribution (code and documentation) is made available to the open source community as a public service by VOSTROM. Contact VOSTROM at license@vostrom.com for information on other licensing arrangements (e.g. for use in proprietary applications).
-
-Under this license, this Distribution may be modified and the original version and modified versions may be copied, distributed, publicly displayed and performed provided that the following conditions are met:
-
-1. Modified versions are distributed with source code and documentation and with permission for others to use any code and documentation (whether in original or modified versions) as granted under this license;
-
-2. if modified, the source code, documentation, and user run-time elements should be clearly labeled by placing an identifier of origin (such as a name, initial, or other tag) after the version number;
-
-3. users, modifiers, distributors, and others coming into possession or using the Distribution in original or modified form accept the entire risk as to the possession, use, and performance of the Distribution;
-
-4. this copyright management information (software identifier and version number, copyright notice and license) shall be retained in all versions of the Distribution;
-
-5. VOSTROM may make modifications to the Distribution that are substantially similar to modified versions of the Distribution, and may make, use, sell, copy, distribute, publicly display, and perform such modifications, including making such modifications available under this or other licenses, without obligation or restriction;
-
-6. modifications incorporating code, libraries, and/or documentation subject to any other open source license may be made, and the resulting work may be distributed under the terms of such open source license if required by that open source license, but doing so will not affect this Distribution, other modifications made under this license or modifications made under other VOSTROM licensing arrangements;
-
-7. no permission is granted to distribute, publicly display, or publicly perform modifications to the Distribution made using proprietary materials that cannot be released in source format under conditions of this license;
-
-8. the name of VOSTROM may not be used in advertising or publicity pertaining to Distribution of the software without specific, prior written permission.
-
-This software is made available "as is", and
-
-VOSTROM DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL VOSTROM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/options/license/VSL-1.0 b/options/license/VSL-1.0
deleted file mode 100644
index f924e0bec7..0000000000
--- a/options/license/VSL-1.0
+++ /dev/null
@@ -1,18 +0,0 @@
-Vovida Software License v. 1.0
-
-This license applies to all software incorporated in the "Vovida Open Communication Application Library" except for those portions incorporating third party software specifically identified as being licensed under separate license.
-
-The Vovida Software License, Version 1.0
-Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. The names "VOCAL", "Vovida Open Communication Application Library", and "Vovida Open Communication Application Library (VOCAL)" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact vocal@vovida.org.
-
-4. Products derived from this software may not be called "VOCAL", nor may "VOCAL" appear in their name, without prior written permission.
-
-THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DAMAGES IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/Vim b/options/license/Vim
deleted file mode 100644
index 84aaf416b6..0000000000
--- a/options/license/Vim
+++ /dev/null
@@ -1,30 +0,0 @@
-VIM LICENSE
-
-I) There are no restrictions on distributing unmodified copies of Vim except that they must include this license text. You can also distribute unmodified parts of Vim, likewise unrestricted except that they must include this license text. You are also allowed to include executables that you made from the unmodified Vim sources, plus your own usage examples and Vim scripts.
-
-II) It is allowed to distribute a modified (or extended) version of Vim, including executables and/or source code, when the following four conditions are met:
-
- 1) This license text must be included unmodified.
-
- 2) The modified Vim must be distributed in one of the following five ways:
-
- a) If you make changes to Vim yourself, you must clearly describe in the distribution how to contact you. When the maintainer asks you (in any way) for a copy of the modified Vim you distributed, you must make your changes, including source code, available to the maintainer without fee. The maintainer reserves the right to include your changes in the official version of Vim. What the maintainer will do with your changes and under what license they will be distributed is negotiable. If there has been no negotiation then this license, or a later version, also applies to your changes. The current maintainer is Bram Moolenaar <Bram@vim.org>. If this changes it will be announced in appropriate places (most likely vim.sf.net, www.vim.org and/or comp.editors). When it is completely impossible to contact the maintainer, the obligation to send him your changes ceases. Once the maintainer has confirmed that he has received your changes they will not have to be sent again.
-
- b) If you have received a modified Vim that was distributed as mentioned under a) you are allowed to further distribute it unmodified, as mentioned at I). If you make additional changes the text under a) applies to those changes.
-
- c) Provide all the changes, including source code, with every copy of the modified Vim you distribute. This may be done in the form of a context diff. You can choose what license to use for new code you add. The changes and their license must not restrict others from making their own changes to the official version of Vim.
-
- d) When you have a modified Vim which includes changes as mentioned under c), you can distribute it without the source code for the changes if the following three conditions are met:
- - The license that applies to the changes permits you to distribute the changes to the Vim maintainer without fee or restriction, and permits the Vim maintainer to include the changes in the official version of Vim without fee or restriction.
- - You keep the changes for at least three years after last distributing the corresponding modified Vim. When the maintainer or someone who you distributed the modified Vim to asks you (in any way) for the changes within this period, you must make them available to him.
- - You clearly describe in the distribution how to contact you. This contact information must remain valid for at least three years after last distributing the corresponding modified Vim, or as long as possible.
-
- e) When the GNU General Public License (GPL) applies to the changes, you can distribute the modified Vim under the GNU GPL version 2 or any later version.
-
- 3) A message must be added, at least in the output of the ":version" command and in the intro screen, such that the user of the modified Vim is able to see that it was modified. When distributing as mentioned under 2)e) adding the message is only required for as far as this does not conflict with the license used for the changes.
-
- 4) The contact information as required under 2)a) and 2)d) must not be removed or changed, except that the person himself can make corrections.
-
-III) If you distribute a modified version of Vim, you are encouraged to use the Vim license for your changes and make them available to the maintainer, including the source code. The preferred way to do this is by e-mail or by uploading the files to a server and e-mailing the URL. If the number of changes is small (e.g., a modified Makefile) e-mailing a context diff will do. The e-mail address to be used is <maintainer@vim.org>
-
-IV) It is not allowed to remove this license from the distribution of the Vim sources, parts of it or from a modified version. You may use this license for previous Vim releases instead of the license that they came with, at your option.
diff --git a/options/license/W3C b/options/license/W3C
deleted file mode 100644
index a485d1021a..0000000000
--- a/options/license/W3C
+++ /dev/null
@@ -1,29 +0,0 @@
-W3C SOFTWARE NOTICE AND LICENSE
-
-This work (and included software, documentation such as READMEs, or other related items) is being provided by the copyright holders under the following license.
-
-License
-
-By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions.
-
-Permission to copy, modify, and distribute this software and its documentation, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications:
-
- The full text of this NOTICE in a location viewable to users of the redistributed or derivative work.
-
- Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C Software Short Notice should be included (hypertext is preferred, text is permitted) within the body of any redistributed or derivative code.
-
- Notice of any changes or modifications to the files, including the date changes were made. (We recommend you provide URIs to the location from which the code is derived.)
-
-Disclaimers
-
-THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
-
-COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
-
-The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the software without specific, written prior permission. Title to copyright in this software and any associated documentation will at all times remain with copyright holders.
-
-Notes
-
-This version: http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
-
-This formulation of W3C's notice and license became active on December 31 2002. This version removes the copyright ownership notice such that this license can be used with materials other than those owned by the W3C, reflects that ERCIM is now a host of the W3C, includes references to this specific dated version of the license, and removes the ambiguous grant of "use". Otherwise, this version is the same as the previous version and is written so as to preserve the Free Software Foundation's assessment of GPL compatibility and OSI's certification under the Open Source Definition.
diff --git a/options/license/W3C-19980720 b/options/license/W3C-19980720
deleted file mode 100644
index 134879044d..0000000000
--- a/options/license/W3C-19980720
+++ /dev/null
@@ -1,23 +0,0 @@
-W3C® SOFTWARE NOTICE AND LICENSE
-
-Copyright (c) 1994-2002 World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/
-
-This W3C work (including software, documents, or other related items) is being provided by the copyright holders under the following license. By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions:
-
-Permission to use, copy, modify, and distribute this software and its documentation, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make:
-
- 1. The full text of this NOTICE in a location viewable to users of the redistributed or derivative work.
-
- 2. Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, a short notice of the following form (hypertext is preferred, text is permitted) should be used within the body of any redistributed or derivative code: "Copyright © [$date-of-software] World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/"
-
- 3. Notice of any changes or modifications to the W3C files, including the date changes were made. (We recommend you provide URIs to the location from which the code is derived.)
-
-THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
-
-COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
-
-The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the software without specific, written prior permission. Title to copyright in this software and any associated documentation will at all times remain with copyright holders.
-
-____________________________________
-
-This formulation of W3C's notice and license became active on August 14 1998 so as to improve compatibility with GPL. This version ensures that W3C software licensing terms are no more restrictive than GPL and consequently W3C software may be distributed in GPL packages. See the older formulation for the policy prior to this date. Please see our Copyright FAQ for common questions about using materials from our site, including specific terms and conditions for packages like libwww, Amaya, and Jigsaw. Other questions about this notice can be directed to site-policy@w3.org.
diff --git a/options/license/W3C-20150513 b/options/license/W3C-20150513
deleted file mode 100644
index abe1af9ae3..0000000000
--- a/options/license/W3C-20150513
+++ /dev/null
@@ -1,17 +0,0 @@
-This work is being provided by the copyright holders under the following license.
-
-License
-By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions.
-
-Permission to copy, modify, and distribute this work, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the work or portions thereof, including modifications:
-
- • The full text of this NOTICE in a location viewable to users of the redistributed or derivative work.
- • Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C Software and Document Short Notice should be included.
- • Notice of any changes or modifications, through a copyright statement on the new code or document such as "This software or document includes material copied from or derived from [title and URI of the W3C document]. Copyright (c) [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)."
-
-Disclaimers
-THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
-
-COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT.
-
-The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. Title to copyright in this work will at all times remain with copyright holders.
diff --git a/options/license/Watcom-1.0 b/options/license/Watcom-1.0
deleted file mode 100644
index b39fe622d0..0000000000
--- a/options/license/Watcom-1.0
+++ /dev/null
@@ -1,106 +0,0 @@
-Sybase Open Watcom Public License version 1.0
-
-USE OF THE SYBASE OPEN WATCOM SOFTWARE DESCRIBED BELOW ("SOFTWARE") IS SUBJECT TO THE TERMS AND CONDITIONS SET FORTH IN THE SYBASE OPEN WATCOM PUBLIC LICENSE SET FORTH BELOW ("LICENSE"). YOU MAY NOT USE THE SOFTWARE IN ANY MANNER UNLESS YOU ACCEPT THE TERMS AND CONDITIONS OF THE LICENSE. YOU INDICATE YOUR ACCEPTANCE BY IN ANY MANNER USING (INCLUDING WITHOUT LIMITATION BY REPRODUCING, MODIFYING OR DISTRIBUTING) THE SOFTWARE. IF YOU DO NOT ACCEPT ALL OF THE TERMS AND CONDITIONS OF THE LICENSE, DO NOT USE THE SOFTWARE IN ANY MANNER.
-
-Sybase Open Watcom Public License version 1.0
-
-1. General; Definitions. This License applies only to the following software programs: the open source versions of Sybase's Watcom C/C++ and Fortran compiler products ("Software"), which are modified versions of, with significant changes from, the last versions made commercially available by Sybase. As used in this License:
-
- 1.1 "Applicable Patent Rights" mean: (a) in the case where Sybase is the grantor of rights, (i) claims of patents that are now or hereafter acquired, owned by or assigned to Sybase and (ii) that cover subject matter contained in the Original Code, but only to the extent necessary to use, reproduce and/or distribute the Original Code without infringement; and (b) in the case where You are the grantor of rights, (i) claims of patents that are now or hereafter acquired, owned by or assigned to You and (ii) that cover subject matter in Your Modifications, taken alone or in combination with Original Code.
-
- 1.2 "Contributor" means any person or entity that creates or contributes to the creation of Modifications.
-
- 1.3 "Covered Code" means the Original Code, Modifications, the combination of Original Code and any Modifications, and/or any respective portions thereof.
-
- 1.4 "Deploy" means to use, sublicense or distribute Covered Code other than for Your internal research and development (R&D) and/or Personal Use, and includes without limitation, any and all internal use or distribution of Covered Code within Your business or organization except for R&D use and/or Personal Use, as well as direct or indirect sublicensing or distribution of Covered Code by You to any third party in any form or manner.
-
- 1.5 "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.6 "Modifications" mean any addition to, deletion from, and/or change to, the substance and/or structure of the Original Code, any previous Modifications, the combination of Original Code and any previous Modifications, and/or any respective portions thereof. When code is released as a series of files, a Modification is: (a) any addition to or deletion from the contents of a file containing Covered Code; and/or (b) any new file or other representation of computer program statements that contains any part of Covered Code.
-
- 1.7 "Original Code" means (a) the Source Code of a program or other work as originally made available by Sybase under this License, including the Source Code of any updates or upgrades to such programs or works made available by Sybase under this License, and that has been expressly identified by Sybase as such in the header file(s) of such work; and (b) the object code compiled from such Source Code and originally made available by Sybase under this License.
-
- 1.8 "Personal Use" means use of Covered Code by an individual solely for his or her personal, private and non-commercial purposes. An individual's use of Covered Code in his or her capacity as an officer, employee, member, independent contractor or agent of a corporation, business or organization (commercial or non-commercial) does not qualify as Personal Use.
-
- 1.9 "Source Code" means the human readable form of a program or other work that is suitable for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an executable (object code).
-
- 1.10 "You" or "Your" means an individual or a legal entity exercising rights under this License. For legal entities, "You" or "Your" includes any entity which controls, is controlled by, or is under common control with, You, where "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity.
-
-2. Permitted Uses; Conditions & Restrictions.Subject to the terms and conditions of this License, Sybase hereby grants You, effective on the date You accept this License and download the Original Code, a world-wide, royalty-free, non-exclusive license, to the extent of Sybase's Applicable Patent Rights and copyrights covering the Original Code, to do the following:
-
- 2.1 You may use, reproduce, display, perform, modify and distribute Original Code, with or without Modifications, solely for Your internal research and development and/or Personal Use, provided that in each instance:
-
- (a) You must retain and reproduce in all copies of Original Code the copyright and other proprietary notices and disclaimers of Sybase as they appear in the Original Code, and keep intact all notices in the Original Code that refer to this License; and
-
- (b) You must retain and reproduce a copy of this License with every copy of Source Code of Covered Code and documentation You distribute, and You may not offer or impose any terms on such Source Code that alter or restrict this License or the recipients' rights hereunder, except as permitted under Section 6.
-
- (c) Whenever reasonably feasible you should include the copy of this License in a click-wrap format, which requires affirmative acceptance by clicking on an "I accept" button or similar mechanism. If a click-wrap format is not included, you must include a statement that any use (including without limitation reproduction, modification or distribution) of the Software, and any other affirmative act that you define, constitutes acceptance of the License, and instructing the user not to use the Covered Code in any manner if the user does not accept all of the terms and conditions of the License.
-
- 2.2 You may use, reproduce, display, perform, modify and Deploy Covered Code, provided that in each instance:
-
- (a) You must satisfy all the conditions of Section 2.1 with respect to the Source Code of the Covered Code;
-
- (b) You must duplicate, to the extent it does not already exist, the notice in Exhibit A in each file of the Source Code of all Your Modifications, and cause the modified files to carry prominent notices stating that You changed the files and the date of any change;
-
- (c) You must make Source Code of all Your Deployed Modifications publicly available under the terms of this License, including the license grants set forth in Section 3 below, for as long as you Deploy the Covered Code or twelve (12) months from the date of initial Deployment, whichever is longer. You should preferably distribute the Source Code of Your Deployed Modifications electronically (e.g. download from a web site);
-
- (d) if You Deploy Covered Code in object code, executable form only, You must include a prominent notice, in the code itself as well as in related documentation, stating that Source Code of the Covered Code is available under the terms of this License with information on how and where to obtain such Source Code; and
-
- (e) the object code form of the Covered Code may be distributed under Your own license agreement, provided that such license agreement contains terms no less protective of Sybase and each Contributor than the terms of this License, and stating that any provisions which differ from this License are offered by You alone and not by any other party.
-
- 2.3 You expressly acknowledge and agree that although Sybase and each Contributor grants the licenses to their respective portions of the Covered Code set forth herein, no assurances are provided by Sybase or any Contributor that the Covered Code does not infringe the patent or other intellectual property rights of any other entity. Sybase and each Contributor disclaim any liability to You for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, You hereby assume sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow You to distribute the Covered Code, it is Your responsibility to acquire that license before distributing the Covered Code.
-
-3. Your Grants. In consideration of, and as a condition to, the licenses granted to You under this License, You hereby grant to Sybase and all third parties a non-exclusive, royalty-free license, under Your Applicable Patent Rights and other intellectual property rights (other than patent) owned or controlled by You, to use, reproduce, display, perform, modify, distribute and Deploy Your Modifications of the same scope and extent as Sybase's licenses under Sections 2.1 and 2.2.
-
-4. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In each such instance, You must make sure the requirements of this License are fulfilled for the Covered Code or any portion thereof.
-
-5. Limitations on Patent License. Except as expressly stated in Section 2, no other patent rights, express or implied, are granted by Sybase herein. Modifications and/or Larger Works may require additional patent licenses from Sybase which Sybase may grant in its sole discretion.
-
-6. Additional Terms. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations and/or other rights consistent with this License ("Additional Terms") to one or more recipients of Covered Code. However, You may do so only on Your own behalf and as Your sole responsibility, and not on behalf of Sybase or any Contributor. You must obtain the recipient's agreement that any such Additional Terms are offered by You alone, and You hereby agree to indemnify, defend and hold Sybase and every Contributor harmless for any liability incurred by or claims asserted against Sybase or such Contributor by reason of any such Additional Terms.
-
-7. Versions of the License. Sybase may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Once Original Code has been published under a particular version of this License, You may continue to use it under the terms of that version. You may also choose to use such Original Code under the terms of any subsequent version of this License published by Sybase. No one other than Sybase has the right to modify the terms applicable to Covered Code created under this License.
-
-8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in part pre-release, untested, or not fully tested works. The Covered Code may contain errors that could cause failures or loss of data, and may be incomplete or contain inaccuracies. You expressly acknowledge and agree that use of the Covered Code, or any portion thereof, is at Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND SYBASE AND SYBASE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "SYBASE" FOR THE PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. SYBASE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY SYBASE, A SYBASE AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. You acknowledge that the Covered Code is not intended for use in the operation of nuclear facilities, aircraft navigation, communication systems, or air traffic control machines in which case the failure of the Covered Code could lead to death, personal injury, or severe physical or environmental damage.
-
-9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT SHALL SYBASE OR ANY CONTRIBUTOR BE LIABLE FOR ANY DIRECT, INCIDENTAL, SPECIAL, INDIRECT, CONSEQUENTIAL OR OTHER DAMAGES OF ANY KIND ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF SYBASE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF INCIDENTAL OR CONSEQUENTIAL OR OTHER DAMAGES OF ANY KIND, SO THIS LIMITATION MAY NOT APPLY TO YOU. In no event shall Sybase's or any Contributor's total liability to You for all damages (other than as may be required by applicable law) under this License exceed the amount of five hundred dollars ($500.00).
-
-10. Trademarks. This License does not grant any rights to use the trademarks or trade names "Sybase" or any other trademarks or trade names belonging to Sybase (collectively "Sybase Marks") or to any trademark or trade name belonging to any Contributor("Contributor Marks"). No Sybase Marks or Contributor Marks may be used to endorse or promote products derived from the Original Code or Covered Code other than with the prior written consent of Sybase or the Contributor, as applicable.
-
-11. Ownership. Subject to the licenses granted under this License, each Contributor retains all rights, title and interest in and to any Modifications made by such Contributor. Sybase retains all rights, title and interest in and to the Original Code and any Modifications made by or on behalf of Sybase ("Sybase Modifications"), and such Sybase Modifications will not be automatically subject to this License. Sybase may, at its sole discretion, choose to license such Sybase Modifications under this License, or on different terms from those contained in this License or may choose not to license them at all.
-
-12. Termination.
-
- 12.1 Termination. This License and the rights granted hereunder will terminate:
-
- (a) automatically without notice if You fail to comply with any term(s) of this License and fail to cure such breach within 30 days of becoming aware of such breach;
-
- (b) immediately in the event of the circumstances described in Section 13.5(b); or
-
- (c) automatically without notice if You, at any time during the term of this License, commence an action for patent infringement (including as a cross claim or counterclaim) against Sybase or any Contributor.
-
- 12.2 Effect of Termination. Upon termination, You agree to immediately stop any further use, reproduction, modification, sublicensing and distribution of the Covered Code and to destroy all copies of the Covered Code that are in your possession or control. All sublicenses to the Covered Code that have been properly granted prior to termination shall survive any termination of this License. Provisions which, by their nature, should remain in effect beyond the termination of this License shall survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. No party will be liable to any other for compensation, indemnity or damages of any sort solely as a result of terminating this License in accordance with its terms, and termination of this License will be without prejudice to any other right or remedy of any party.
-
-13. Miscellaneous.
-
- 13.1 Government End Users. The Covered Code is a "commercial item" as defined in FAR 2.101. Government software and technical data rights in the Covered Code include only those rights customarily provided to the public as defined in this License. This customary commercial license in technical data and software is provided in accordance with FAR 12.211 (Technical Data) and 12.212 (Computer Software) and, for Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 (Rights in Commercial Computer Software or Computer Software Documentation). Accordingly, all U.S. Government End Users acquire Covered Code with only those rights set forth herein.
-
- 13.2 Relationship of Parties. This License will not be construed as creating an agency, partnership, joint venture or any other form of legal association between or among you, Sybase or any Contributor, and You will not represent to the contrary, whether expressly, by implication, appearance or otherwise.
-
- 13.3 Independent Development. Nothing in this License will impair Sybase's or any Contributor's right to acquire, license, develop, have others develop for it, market and/or distribute technology or products that perform the same or similar functions as, or otherwise compete with, Modifications, Larger Works, technology or products that You may develop, produce, market or distribute.
-
- 13.4 Waiver; Construction. Failure by Sybase or any Contributor to enforce any provision of this License will not be deemed a waiver of future enforcement of that or any other provision. Any law or regulation which provides that the language of a contract shall be construed against the drafter will not apply to this License.
-
- 13.5 Severability. (a) If for any reason a court of competent jurisdiction finds any provision of this License, or portion thereof, to be unenforceable, that provision of the License will be enforced to the maximum extent permissible so as to effect the economic benefits and intent of the parties, and the remainder of this License will continue in full force and effect. (b) Notwithstanding the foregoing, if applicable law prohibits or restricts You from fully and/or specifically complying with Sections 2 and/or 3 or prevents the enforceability of either of those Sections, this License will immediately terminate and You must immediately discontinue any use of the Covered Code and destroy all copies of it that are in your possession or control.
-
- 13.6 Dispute Resolution. Any litigation or other dispute resolution between You and Sybase relating to this License shall take place in the Northern District of California, and You and Sybase hereby consent to the personal jurisdiction of, and venue in, the state and federal courts within that District with respect to this License. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded.
-
- 13.7 Entire Agreement; Governing Law. This License constitutes the entire agreement between the parties with respect to the subject matter hereof. This License shall be governed by the laws of the United States and the State of California, except that body of California law concerning conflicts of law.
-Where You are located in the province of Quebec, Canada, the following clause applies: The parties hereby confirm that they have requested that this License and all related documents be drafted in English. Les parties ont exigè que le prèsent contrat et tous les documents connexes soient rèdiès en anglais.
-
-
-EXHIBIT A.
-
-"Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
-This file contains Original Code and/or Modifications of Original Code as defined in and that are subject to the Sybase Open Watcom Public License version 1.0 (the 'License'). You may not use this file except in compliance with the License. BY USING THIS FILE YOU AGREE TO ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is provided with the Original Code and Modifications, and is also available at www.sybase.com/developer/opensource.
-
-The Original Code and all software distributed under the License are distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the specific language governing rights and limitations under the License."
diff --git a/options/license/Widget-Workshop b/options/license/Widget-Workshop
deleted file mode 100644
index d4df9b5067..0000000000
--- a/options/license/Widget-Workshop
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved.
-
-Permission to use, copy, modify, and distribute this software and
-its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
-without fee is hereby granted, provided that this copyright notice
-is kept intact. WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES
-ABOUT THE SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP
-SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT
-OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
-THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
-CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE PERFORMANCE,
-SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT NAVIGATION OR
-COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE SUPPORT MACHINES,
-OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE SOFTWARE COULD LEAD
-DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL
-DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET WORKSHOP SPECIFICALLY DISCLAIMS
-ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.
diff --git a/options/license/Wsuipa b/options/license/Wsuipa
deleted file mode 100644
index 8a37a28d61..0000000000
--- a/options/license/Wsuipa
+++ /dev/null
@@ -1,5 +0,0 @@
-This file was added by Clea F. Rees on 2008/11/30 with the permission of Dean Guenther and pointers to this file were added to all source files.
-
-Unlimited copying and redistribution of each of the files is permitted as long as the file is not modified. Modifications, and redistribution of modified versions, are also permitted, but only if the resulting file is renamed.
-
-The copyright holder is Washington State University. The original author of the fonts is Janene Winter. The primary contact (as of 2008) is Dean Guenther.
diff --git a/options/license/WxWindows-exception-3.1 b/options/license/WxWindows-exception-3.1
deleted file mode 100644
index 9e71b0ae3f..0000000000
--- a/options/license/WxWindows-exception-3.1
+++ /dev/null
@@ -1,9 +0,0 @@
-EXCEPTION NOTICE
-
-1. As a special exception, the copyright holders of this library give permission for additional uses of the text contained in this release of the library as licenced under the wxWindows Library Licence, applying either version 3.1 of the Licence, or (at your option) any later version of the Licence as published by the copyright holders of version 3.1 of the Licence document.
-
-2. The exception is that you may use, copy, link, modify and distribute under your own terms, binary object code versions of works based on the Library.
-
-3. If you copy code from files distributed under the terms of the GNU General Public Licence or the GNU Library General Public Licence into a copy of this library, as this licence permits, the exception does not apply to the code that you add in this way. To avoid misleading anyone as to the status of such modified files, you must delete this exception notice from such code and/or adjust the licensing conditions notice accordingly.
-
-4. If you write modifications of your own for this library, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, you must delete the exception notice from such code and/or adjust the licensing conditions notice accordingly.
diff --git a/options/license/X11 b/options/license/X11
deleted file mode 100644
index 6b41092f29..0000000000
--- a/options/license/X11
+++ /dev/null
@@ -1,13 +0,0 @@
-X11 License
-
-Copyright (C) 1996 X Consortium
-
-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", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-Except as contained in this notice, the name of the X Consortium shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the X Consortium.
-
-X Window System is a trademark of X Consortium, Inc.
diff --git a/options/license/X11-distribute-modifications-variant b/options/license/X11-distribute-modifications-variant
deleted file mode 100644
index 978c199731..0000000000
--- a/options/license/X11-distribute-modifications-variant
+++ /dev/null
@@ -1,25 +0,0 @@
-Copyright (c) <year> <copyright holders>
-
-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, distribute with modifications, 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", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
-THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-Except as contained in this notice, the name(s) of the above copyright
-holders shall not be used in advertising or otherwise to promote the
-sale, use or other dealings in this Software without prior written
-authorization.
diff --git a/options/license/X11-swapped b/options/license/X11-swapped
deleted file mode 100644
index b023bd546e..0000000000
--- a/options/license/X11-swapped
+++ /dev/null
@@ -1,23 +0,0 @@
-Copyright (c) 2008-2010 Derick Eddington. 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.
-
-Except as contained in this notice, the name(s) of the above copyright
-holders shall not be used in advertising or otherwise to promote the sale,
-use or other dealings in this Software without prior written authorization.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
diff --git a/options/license/XFree86-1.1 b/options/license/XFree86-1.1
deleted file mode 100644
index b40e8e1713..0000000000
--- a/options/license/XFree86-1.1
+++ /dev/null
@@ -1,16 +0,0 @@
-XFree86 License (version 1.1)
-
-Copyright (C) 1994-2006 The XFree86 Project, Inc.
-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:
-
- 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution, and in the same place and form as other copyright, license and disclaimer information.
-
- 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by The XFree86 Project, Inc (http://www.xfree86.org/) and its contributors", in the same place and form as other third-party acknowledgments. Alternately, this acknowledgment may appear in the software itself, in the same form and location as other such third-party acknowledgments.
-
- 4. Except as contained in this notice, the name of The XFree86 Project, Inc shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The XFree86 Project, Inc.
-
-THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE XFREE86 PROJECT, INC OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/XSkat b/options/license/XSkat
deleted file mode 100644
index 0b77a07877..0000000000
--- a/options/license/XSkat
+++ /dev/null
@@ -1,10 +0,0 @@
-This program is free software; you can redistribute it freely.
-Use it at your own risk; there is NO WARRANTY.
-
-Redistribution of modified versions is permitted provided that the following conditions are met:
-
-1. All copyright & permission notices are preserved.
-
-2.a) Only changes required for packaging or porting are made.
- or
-2.b) It is clearly stated who last changed the program. The program is renamed or the version number is of the form x.y.z, where x.y is the version of the original program and z is an arbitrary suffix.
diff --git a/options/license/Xdebug-1.03 b/options/license/Xdebug-1.03
deleted file mode 100644
index 548e74455c..0000000000
--- a/options/license/Xdebug-1.03
+++ /dev/null
@@ -1,60 +0,0 @@
---------------------------------------------------------------------
- The Xdebug License, version 1.03
- (Based on "The PHP License", version 3.01)
- Copyright (c) 2003-2022 Derick Rethans. All rights reserved.
---------------------------------------------------------------------
-
-Redistribution and use in source and binary forms, with or without
-modification, is permitted provided that the following conditions
-are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
- 3. The name "Xdebug" must not be used to endorse or promote products
- derived from this software without prior written permission. For
- written permission, please contact derick@xdebug.org.
-
- 4. Products derived from this software may not be called "Xdebug", nor
- may "Xdebug" appear in their name, without prior written permission
- from derick@xdebug.org.
-
- 5. Derick Rethans may publish revised and/or new versions of the
- license from time to time. Each version will be given a
- distinguishing version number. Once covered code has been
- published under a particular version of the license, you may
- always continue to use it under the terms of that version. You
- may also choose to use such covered code under the terms of any
- subsequent version of the license published by Derick Rethans. No
- one other than Derick Rethans has the right to modify the terms
- applicable to covered code created under this License.
-
- 6. Redistributions of any form whatsoever must retain the following
- acknowledgment: "This product includes Xdebug software, freely
- available from <https://xdebug.org/>".
-
-THIS SOFTWARE IS PROVIDED BY DERICK RETHANS ``AS IS'' AND ANY
-EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP DEVELOPMENT TEAM OR
-ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
---------------------------------------------------------------------
-
-This software consists of voluntary contributions made by some
-individuals on behalf of Derick Rethans.
-
-Derick Rethans can be contacted via e-mail at derick@xdebug.org.
-
-For more information on Xdebug, please see <https://xdebug.org>.
diff --git a/options/license/Xerox b/options/license/Xerox
deleted file mode 100644
index 0667d0d211..0000000000
--- a/options/license/Xerox
+++ /dev/null
@@ -1,5 +0,0 @@
-Copyright (c) 1995, 1996 Xerox Corporation. All Rights Reserved.
-
-Use and copying of this software and preparation of derivative works based upon this software are permitted. Any copy of this software or of any derivative work must include the above copyright notice of Xerox Corporation, this paragraph and the one after it. Any distribution of this software or derivative works must comply with all applicable United States export control laws.
-
-This software is made available AS IS, and XEROX CORPORATION DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND NOTWITHSTANDING ANY OTHER PROVISION CONTAINED HEREIN, ANY LIABILITY FOR DAMAGES RESULTING FROM THE SOFTWARE OR ITS USE IS EXPRESSLY DISCLAIMED, WHETHER ARISING IN CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, EVEN IF XEROX CORPORATION IS ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/options/license/Xfig b/options/license/Xfig
deleted file mode 100644
index c2d56093d5..0000000000
--- a/options/license/Xfig
+++ /dev/null
@@ -1,7 +0,0 @@
-Any party obtaining a copy of these files is granted, free of charge,
-a full and unrestricted irrevocable, world-wide, paid up, royalty-free,
-nonexclusive right and license to deal in this software and documentation
-files (the "Software"), including without limitation the rights to use,
-copy, modify, merge, publish and/or distribute copies of the Software,
-and to permit persons who receive copies from any such party to do so,
-with the only requirement being that this copyright notice remain intact.
diff --git a/options/license/Xnet b/options/license/Xnet
deleted file mode 100644
index 334e73a965..0000000000
--- a/options/license/Xnet
+++ /dev/null
@@ -1,11 +0,0 @@
-The X.Net, Inc. License
-
-Copyright (c) 2000-2001 X.Net, Inc. Lafayette, California, USA
-
-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", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-This agreement shall be governed in all respects by the laws of the State of California and by the laws of the United States of America.
diff --git a/options/license/YPL-1.0 b/options/license/YPL-1.0
deleted file mode 100644
index cfbec9e2f3..0000000000
--- a/options/license/YPL-1.0
+++ /dev/null
@@ -1,47 +0,0 @@
-Yahoo! Public License, Version 1.0 (YPL)
-
-This Yahoo! Public License (this "Agreement") is a legal agreement that describes the terms under which Yahoo! Inc., a Delaware corporation having its principal place of business at 701 First Avenue, Sunnyvale, California 94089 ("Yahoo!") will provide software to you via download or otherwise ("Software"). By using the Software, you, an individual or an entity ("You") agree to the terms of this Agreement.
-
-In consideration of the mutual promises and upon the terms and conditions set forth below, the parties agree as follows:
-
-1. Grant of Copyright License
-
- 1.1 - Subject to the terms and conditions of this Agreement, Yahoo! hereby grants to You, under any and all of its copyright interest in and to the Software, a royalty-free, non-exclusive, non-transferable license to copy, modify, compile, execute, and distribute the Software and Modifications. For the purposes of this Agreement, any change to, addition to, or abridgement of the Software made by You is a "Modification;" however, any file You add to the Software that does not contain any part of the Software is not a "Modification."
-
- 1.2 - If You are an individual acting on behalf of a corporation or other entity, Your use of the Software or any Modification is subject to Your having the authority to bind such corporation or entity to this Agreement. Providing copies to persons within such corporation or entity is not considered distribution for purposes of this Agreement.
-
- 1.3 - For the Software or any Modification You distribute in source code format, You must do so only under the terms of this Agreement, and You must include a complete copy of this Agreement with Your distribution. With respect to any Modification You distribute in source code format, the terms of this Agreement will apply to You in the same way those terms apply to Yahoo! with respect to the Software. In other words, when You are distributing Modifications under this Agreement, You "stand in the shoes" of Yahoo! in terms of the rights You grant and how the terms and conditions apply to You and the licensees of Your Modifications. Notwithstanding the foregoing, when You "stand in the shoes" of Yahoo!, You are not subject to the jurisdiction provision under Section 7, which requires all disputes under this Agreement to be subject to the jurisdiction of federal or state courts of northern California.
-
- 1.4 - For the Software or any Modification You distribute in compiled or object code format, You must also provide recipients with access to the Software or Modification in source code format along with a complete copy of this Agreement. The distribution of the Software or Modifications in compiled or object code format may be under a license of Your choice, provided that You are in compliance with the terms of this Agreement. In addition, You must make absolutely clear that any license terms applying to such Software or Modification that differ from this Agreement are offered by You alone and not by Yahoo!, and that such license does not restrict recipients from exercising rights in the source code to the Software granted by Yahoo! under this Agreement or rights in the source code to any Modification granted by You as described in Section 1.3.
-
- 1.5 - This Agreement does not limit Your right to distribute files that are entirely Your own work (i.e., which do not incorporate any portion of the Software and are not Modifications) under any terms You choose.
-
-2. Support
-Yahoo! has no obligation to provide technical support or updates to You. Nothing in this Agreement requires Yahoo! to enter into any license with You for any other edition of the Software.
-
-3. Intellectual Property Rights
-
- 3.1 - Except for the license expressly granted under copyright in Section 1.1, no rights, licenses or forbearances are granted or may arise in relation to this Agreement whether expressly, by implication, exhaustion, estoppel or otherwise. All rights, including all intellectual property rights, that are not expressly granted under this Agreement are hereby reserved.
-
- 3.2 - In any copy of the Software or in any Modification you create, You must retain and reproduce, any and all copyright, patent, trademark, and attribution notices that are included in the Software in the same form as they appear in the Software. This includes the preservation of attribution notices in the form of trademarks or logos that exist within a user interface of the Software.
-
- 3.3 - This license does not grant You rights to use any party's name, logo, or trademarks, except solely as necessary to comply with Section 3.2.
-
-4. Disclaimer of Warranties
-THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND. YAHOO! MAKES NO WARRANTIES, WHETHER EXPRESS, IMPLIED, OR STATUTORY REGARDING OR RELATING TO THE SOFTWARE. SPECIFICALLY, YAHOO! DOES NOT WARRANT THAT THE SOFTWARE WILL BE ERROR FREE OR WILL PERFORM IN AN UNINTERRUPTED MANNER. TO THE GREATEST EXTENT ALLOWED BY LAW, YAHOO! SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE (EVEN IF YAHOO! HAD BEEN INFORMED OF SUCH PURPOSE), AND NONINFRINGEMENT WITH RESPECT TO THE SOFTWARE, ANY MODIFICATIONS THERETO AND WITH RESPECT TO THE USE OF THE FOREGOING.
-
-5. Limitation of Liability
-IN NO EVENT WILL YAHOO! BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES OF ANY KIND (INCLUDING WITHOUT LIMITATION LOSS OF PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, LOSS OF DATA, COST OF COVER) IN CONNECTION WITH OR ARISING OUT OF OR RELATING TO THE FURNISHING, PERFORMANCE OR USE OF THE SOFTWARE OR ANY OTHER RIGHTS GRANTED HEREUNDER, WHETHER ALLEGED AS A BREACH OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE, AND EVEN IF YAHOO! HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-6. Term and Termination
-
- 6.1 - This Agreement will continue in effect unless and until terminated earlier pursuant to this Section 6.
-
- 6.2 - In the event Yahoo! determines that You have breached this Agreement, Yahoo! may terminate this Agreement.
-
- 6.3 - All licenses granted hereunder shall terminate upon the termination of this Agreement. Termination will be in addition to any rights and remedies available to Yahoo! at law or equity or under this Agreement.
-
- 6.4 - Termination of this Agreement will not affect the provisions regarding reservation of rights (Section 3.1), provisions disclaiming or limiting Yahoo!'s liability (Sections 4 and 5), Termination (Section 6) or Miscellaneous (Section 7), which provisions will survive termination of this Agreement.
-
-7. Miscellaneous
-This Agreement contains the entire agreement of the parties with respect to the subject matter of this Agreement and supersedes all previous communications, representations, understandings and agreements, either oral or written, between the parties with respect to said subject matter. The relationship of the parties hereunder is that of independent contractors, and this Agreement will not be construed as creating an agency, partnership, joint venture or any other form of legal association between the parties. If any term, condition, or provision in this Agreement is found to be invalid, unlawful or unenforceable to any extent, this Agreement will be construed in a manner that most closely effectuates the intent of this Agreement. Such invalid term, condition or provision will be severed from the remaining terms, conditions and provisions, which will continue to be valid and enforceable to the fullest extent permitted by law. This Agreement will be interpreted and construed in accordance with the laws of the State of California and the United States of America, without regard to conflict of law principles. The U.N. Convention on Contracts for the International Sale of Goods shall not apply to this Agreement. All disputes arising out of this Agreement involving Yahoo! or any of its subsidiaries shall be subject to the jurisdiction of the federal or state courts of northern California, with venue lying in Santa Clara County, California. No rights may be assigned, no obligations may be delegated, and this Agreement may not be transferred by You, in whole or in part, whether voluntary or by operation of law, including by way of sale of assets, merger or consolidation, without the prior written consent of Yahoo!, and any purported assignment, delegation or transfer without such consent shall be void ab initio. Any waiver of the provisions of this Agreement or of a party's rights or remedies under this Agreement must be in writing to be effective. Failure, neglect or delay by a party to enforce the provisions of this Agreement or its rights or remedies at any time, will not be construed or be deemed to be a waiver of such party's rights under this Agreement and will not in any way affect the validity of the whole or any part of this Agreement or prejudice such party's right to take subsequent action.
diff --git a/options/license/YPL-1.1 b/options/license/YPL-1.1
deleted file mode 100644
index d88d77ac05..0000000000
--- a/options/license/YPL-1.1
+++ /dev/null
@@ -1,47 +0,0 @@
-Yahoo! Public License, Version 1.1 (YPL)
-
-This Yahoo! Public License (this "Agreement") is a legal agreement that describes the terms under which Yahoo! Inc., a Delaware corporation having its principal place of business at 701 First Avenue, Sunnyvale, California 94089 ("Yahoo!") will provide software to you via download or otherwise ("Software"). By using the Software, you, an individual or an entity ("You") agree to the terms of this Agreement.
-
-In consideration of the mutual promises and upon the terms and conditions set forth below, the parties agree as follows:
-
-1. Grant of Copyright License
-
- 1.1 - Subject to the terms and conditions of this Agreement, Yahoo! hereby grants to You, under any and all of its copyright interest in and to the Software, a royalty-free, non-exclusive, non-transferable license to copy, modify, compile, execute, and distribute the Software and Modifications. For the purposes of this Agreement, any change to, addition to, or abridgement of the Software made by You is a "Modification;" however, any file You add to the Software that does not contain any part of the Software is not a "Modification."
-
- 1.2 - If You are an individual acting on behalf of a corporation or other entity, Your use of the Software or any Modification is subject to Your having the authority to bind such corporation or entity to this Agreement. Providing copies to persons within such corporation or entity is not considered distribution for purposes of this Agreement.
-
- 1.3 - For the Software or any Modification You distribute in source code format, You must do so only under the terms of this Agreement, and You must include a complete copy of this Agreement with Your distribution. With respect to any Modification You distribute in source code format, the terms of this Agreement will apply to You in the same way those terms apply to Yahoo! with respect to the Software. In other words, when You are distributing Modifications under this Agreement, You "stand in the shoes" of Yahoo! in terms of the rights You grant and how the terms and conditions apply to You and the licensees of Your Modifications. Notwithstanding the foregoing, when You "stand in the shoes" of Yahoo!, You are not subject to the jurisdiction provision under Section 7, which requires all disputes under this Agreement to be subject to the jurisdiction of federal or state courts of northern California.
-
- 1.4 - For the Software or any Modification You distribute in compiled or object code format, You must also provide recipients with access to the Software or Modification in source code format along with a complete copy of this Agreement. The distribution of the Software or Modifications in compiled or object code format may be under a license of Your choice, provided that You are in compliance with the terms of this Agreement. In addition, You must make absolutely clear that any license terms applying to such Software or Modification that differ from this Agreement are offered by You alone and not by Yahoo!, and that such license does not restrict recipients from exercising rights in the source code to the Software granted by Yahoo! under this Agreement or rights in the source code to any Modification granted by You as described in Section 1.3.
-
- 1.5 - This Agreement does not limit Your right to distribute files that are entirely Your own work (i.e., which do not incorporate any portion of the Software and are not Modifications) under any terms You choose.
-
-2. Support
-Yahoo! has no obligation to provide technical support or updates to You. Nothing in this Agreement requires Yahoo! to enter into any license with You for any other edition of the Software.
-
-3. Intellectual Property Rights
-
- 3.1 - Except for the license expressly granted under copyright in Section 1.1, no rights, licenses or forbearances are granted or may arise in relation to this Agreement whether expressly, by implication, exhaustion, estoppel or otherwise. All rights, including all intellectual property rights, that are not expressly granted under this Agreement are hereby reserved.
-
- 3.2 - In any copy of the Software or in any Modification you create, You must retain and reproduce, any and all copyright, patent, trademark, and attribution notices that are included in the Software in the same form as they appear in the Software. This includes the preservation of attribution notices in the form of trademarks or logos that exist within a user interface of the Software.
-
- 3.3 - This license does not grant You rights to use any party's name, logo, or trademarks, except solely as necessary to comply with Section 3.2.
-
-4. Disclaimer of Warranties
-THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND. YAHOO! MAKES NO WARRANTIES, WHETHER EXPRESS, IMPLIED, OR STATUTORY REGARDING OR RELATING TO THE SOFTWARE. SPECIFICALLY, YAHOO! DOES NOT WARRANT THAT THE SOFTWARE WILL BE ERROR FREE OR WILL PERFORM IN AN UNINTERRUPTED MANNER. TO THE GREATEST EXTENT ALLOWED BY LAW, YAHOO! SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE (EVEN IF YAHOO! HAD BEEN INFORMED OF SUCH PURPOSE), AND NONINFRINGEMENT WITH RESPECT TO THE SOFTWARE, ANY MODIFICATIONS THERETO AND WITH RESPECT TO THE USE OF THE FOREGOING.
-
-5. Limitation of Liability
-IN NO EVENT WILL YAHOO! BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES OF ANY KIND (INCLUDING WITHOUT LIMITATION LOSS OF PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, LOSS OF DATA, COST OF COVER) IN CONNECTION WITH OR ARISING OUT OF OR RELATING TO THE FURNISHING, PERFORMANCE OR USE OF THE SOFTWARE OR ANY OTHER RIGHTS GRANTED HEREUNDER, WHETHER ALLEGED AS A BREACH OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE, AND EVEN IF YAHOO! HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-6. Term and Termination
-
- 6.1 - This Agreement will continue in effect unless and until terminated earlier pursuant to this Section 6.
-
- 6.2 - In the event You violate the terms of this Agreement, Yahoo! may terminate this Agreement.
-
- 6.3 - All licenses granted hereunder shall terminate upon the termination of this Agreement. Termination will be in addition to any rights and remedies available to Yahoo! at law or equity or under this Agreement.
-
- 6.4 - Termination of this Agreement will not affect the provisions regarding reservation of rights (Section 3.1), provisions disclaiming or limiting Yahoo!'s liability (Sections 4 and 5), Termination (Section 6) or Miscellaneous (Section 7), which provisions will survive termination of this Agreement.
-
-7.Miscellaneous
-This Agreement contains the entire agreement of the parties with respect to the subject matter of this Agreement and supersedes all previous communications, representations, understandings and agreements, either oral or written, between the parties with respect to said subject matter. The relationship of the parties hereunder is that of independent contractors, and this Agreement will not be construed as creating an agency, partnership, joint venture or any other form of legal association between the parties. If any term, condition, or provision in this Agreement is found to be invalid, unlawful or unenforceable to any extent, this Agreement will be construed in a manner that most closely effectuates the intent of this Agreement. Such invalid term, condition or provision will be severed from the remaining terms, conditions and provisions, which will continue to be valid and enforceable to the fullest extent permitted by law. This Agreement will be interpreted and construed in accordance with the laws of the State of California and the United States of America, without regard to conflict of law principles. The U.N. Convention on Contracts for the International Sale of Goods shall not apply to this Agreement. All disputes arising out of this Agreement involving Yahoo! or any of its subsidiaries shall be subject to the jurisdiction of the federal or state courts of northern California, with venue lying in Santa Clara County, California. No rights may be assigned, no obligations may be delegated, and this Agreement may not be transferred by You, in whole or in part, whether voluntary or by operation of law, including by way of sale of assets, merger or consolidation, without the prior written consent of Yahoo!, and any purported assignment, delegation or transfer without such consent shall be void ab initio. Any waiver of the provisions of this Agreement or of a party's rights or remedies under this Agreement must be in writing to be effective. Failure, neglect or delay by a party to enforce the provisions of this Agreement or its rights or remedies at any time, will not be construed or be deemed to be a waiver of such party's rights under this Agreement and will not in any way affect the validity of the whole or any part of this Agreement or prejudice such party's right to take subsequent action.
diff --git a/options/license/ZPL-1.1 b/options/license/ZPL-1.1
deleted file mode 100644
index 1d70905f5a..0000000000
--- a/options/license/ZPL-1.1
+++ /dev/null
@@ -1,33 +0,0 @@
-Zope Public License (ZPL) Version 1.1
-
-Copyright (c) Zope Corporation. All rights reserved.
-
-This license has been certified as open source.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- 1. Redistributions in source code must retain the above copyright notice, this list of conditions, and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- 3. All advertising materials and documentation mentioning features derived from or use of this software must display the following acknowledgement:
-
- "This product includes software developed by Zope Corporation for use in the Z Object Publishing Environment (http://www.zope.com/)."
-
- In the event that the product being advertised includes an intact Zope distribution (with copyright and license included) then this clause is waived.
-
- 4. Names associated with Zope or Zope Corporation must not be used to endorse or promote products derived from this software without prior written permission from Zope Corporation.
-
- 5. Modified redistributions of any form whatsoever must retain the following acknowledgment:
-
- "This product includes software developed by Zope Corporation for use in the Z Object Publishing Environment (http://www.zope.com/)."
-
- Intact (re-)distributions of any official Zope release do not require an external acknowledgement.
-
- 6. Modifications are encouraged but must be packaged separately as patches to official Zope releases. Distributions that do not clearly separate the patches from the original work must be clearly labeled as unofficial distributions. Modifications which do not carry the name Zope may be packaged in any form, as long as they conform to all of the clauses above.
-
-Disclaimer
-
-THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-This software consists of contributions made by Zope Corporation and many individuals on behalf of Zope Corporation. Specific attributions are listed in the accompanying credits file.
diff --git a/options/license/ZPL-2.0 b/options/license/ZPL-2.0
deleted file mode 100644
index 5bb847f77f..0000000000
--- a/options/license/ZPL-2.0
+++ /dev/null
@@ -1,23 +0,0 @@
-Zope Public License (ZPL) Version 2.0
-
-This software is Copyright (c) Zope Corporation (tm) and Contributors. All rights reserved.
-
-This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF).
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- 1. Redistributions in source code must retain the above copyright notice, this list of conditions, and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- 3. The name Zope Corporation (tm) must not be used to endorse or promote products derived from this software without prior written permission from Zope Corporation.
-
- 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of Zope Corporation. Use of them is covered in a separate agreement (see http://www.zope.com/Marks).
-
- 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
-
-Disclaimer
-
-THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-This software consists of contributions made by Zope Corporation and many individuals on behalf of Zope Corporation. Specific attributions are listed in the accompanying credits file.
diff --git a/options/license/ZPL-2.1 b/options/license/ZPL-2.1
deleted file mode 100644
index 7c3cd5aed6..0000000000
--- a/options/license/ZPL-2.1
+++ /dev/null
@@ -1,21 +0,0 @@
-Zope Public License (ZPL) Version 2.1
-
-A copyright notice accompanies this license document that identifies the copyright holders.
-
-This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF).
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders.
-
- 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders.
-
- 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
-
-Disclaimer
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/Zed b/options/license/Zed
deleted file mode 100644
index 5f7cd2cc05..0000000000
--- a/options/license/Zed
+++ /dev/null
@@ -1,3 +0,0 @@
-(c) Jim Davies, January 1995
-You may copy and distribute this file freely. Any queries and complaints should be forwarded to Jim.Davies@comlab.ox.ac.uk.
-If you make any changes to this file, please do not distribute the results under the name `zed-csp.sty'.
diff --git a/options/license/Zeeff b/options/license/Zeeff
deleted file mode 100644
index 408efb2ffd..0000000000
--- a/options/license/Zeeff
+++ /dev/null
@@ -1,3 +0,0 @@
-Copyright 1988 Jon Zeeff (zeeff@b-tech.ann-arbor.mi.us)
-You can use this code in any manner, as long as you leave my
-name on it and don't hold me responsible for any problems with it.
diff --git a/options/license/Zend-2.0 b/options/license/Zend-2.0
deleted file mode 100644
index c9b5144e1e..0000000000
--- a/options/license/Zend-2.0
+++ /dev/null
@@ -1,18 +0,0 @@
-The Zend Engine License, version 2.00
-Copyright (c) 1999-2002 Zend Technologies Ltd. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
- 3. The names "Zend" and "Zend Engine" must not be used to endorse or promote products derived from this software without prior permission from Zend Technologies Ltd. For written permission, please contact license@zend.com.
-
- 4. Zend Technologies Ltd. may publish revised and/or new versions of the license from time to time. Each version will be given a distinguishing version number. Once covered code has been published under a particular version of the license, you may always continue to use it under the terms of that version. You may also choose to use such covered code under the terms of any subsequent version of the license published by Zend Technologies Ltd. No one other than Zend Technologies Ltd. has the right to modify the terms applicable to covered code created under this License.
-
- 5. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes the Zend Engine, freely available at http://www.zend.com"
-
- 6. All advertising materials mentioning features or use of this software must display the following acknowledgment: "The Zend Engine is freely available at http://www.zend.com"
-
-THIS SOFTWARE IS PROVIDED BY ZEND TECHNOLOGIES LTD. ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ZEND TECHNOLOGIES LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/Zimbra-1.3 b/options/license/Zimbra-1.3
deleted file mode 100644
index 78be653023..0000000000
--- a/options/license/Zimbra-1.3
+++ /dev/null
@@ -1,47 +0,0 @@
-Zimbra Public License, Version 1.3 (ZPL)
-
-This Zimbra Public License (this "Agreement") is a legal agreement that describes the terms under which VMware, Inc., a Delaware corporation having its principal place of business at 3401 Hillview Avenue, Palo Alto, California 94304 ("VMware") will provide software to you via download or otherwise ("Software"). By using the Software, you, an individual or an entity ("You") agree to the terms of this Agreement.
-
-In consideration of the mutual promises and upon the terms and conditions set forth below, the parties agree as follows:
-
-1. Grant of Copyright License
-
- 1.1 - Subject to the terms and conditions of this Agreement, VMware hereby grants to You, under any and all of its copyright interest in and to the Software, a royalty-free, non-exclusive, non-transferable license to copy, modify, compile, execute, and distribute the Software and Modifications. For the purposes of this Agreement, any change to, addition to, or abridgement of the Software made by You is a "Modification;" however, any file You add to the Software that does not contain any part of the Software is not a "Modification."
-
- 1.2 - If You are an individual acting on behalf of a corporation or other entity, Your use of the Software or any Modification is subject to Your having the authority to bind such corporation or entity to this Agreement. Providing copies to persons within such corporation or entity is not considered distribution for purposes of this Agreement.
-
- 1.3 - For the Software or any Modification You distribute in source code format, You must do so only under the terms of this Agreement, and You must include a complete copy of this Agreement with Your distribution. With respect to any Modification You distribute in source code format, the terms of this Agreement will apply to You in the same way those terms apply to VMware with respect to the Software. In other words, when You are distributing Modifications under this Agreement, You "stand in the shoes" of VMware in terms of the rights You grant and how the terms and conditions apply to You and the licensees of Your Modifications. Notwithstanding the foregoing, when You "stand in the shoes" of VMware, You are not subject to the jurisdiction provision under Section 7, which requires all disputes under this Agreement to be subject to the jurisdiction of federal or state courts of northern California.
-
- 1.4 - For the Software or any Modification You distribute in compiled or object code format, You must also provide recipients with access to the Software or Modification in source code format along with a complete copy of this Agreement. The distribution of the Software or Modifications in compiled or object code format may be under a license of Your choice, provided that You are in compliance with the terms of this Agreement. In addition, You must make absolutely clear that any license terms applying to such Software or Modification that differ from this Agreement are offered by You alone and not by VMware, and that such license does not restrict recipients from exercising rights in the source code to the Software granted by VMware under this Agreement or rights in the source code to any Modification granted by You as described in Section 1.3.
-
- 1.5 - This Agreement does not limit Your right to distribute files that are entirely Your own work (i.e., which do not incorporate any portion of the Software and are not Modifications) under any terms You choose.
-
-2. Support
-VMware has no obligation to provide technical support or updates to You. Nothing in this Agreement requires VMware to enter into any license with You for any other edition of the Software.
-
-3. Intellectual Property Rights
-
- 3.1 - Except for the license expressly granted under copyright in Section 1.1, no rights, licenses or forbearances are granted or may arise in relation to this Agreement whether expressly, by implication, exhaustion, estoppel or otherwise. All rights, including all intellectual property rights, that are not expressly granted under this Agreement are hereby reserved.
-
- 3.2 - In any copy of the Software or in any Modification you create, You must retain and reproduce, any and all copyright, patent, trademark, and attribution notices that are included in the Software in the same form as they appear in the Software. This includes the preservation of attribution notices in the form of trademarks or logos that exist within a user interface of the Software.
-
- 3.3 - This license does not grant You rights to use any party's name, logo, or trademarks, except solely as necessary to comply with Section 3.2.
-
-4. Disclaimer of Warranties
-THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND. VMWARE MAKES NO WARRANTIES, WHETHER EXPRESS, IMPLIED, OR STATUTORY REGARDING OR RELATING TO THE SOFTWARE. SPECIFICALLY, VMWARE DOES NOT WARRANT THAT THE SOFTWARE WILL BE ERROR FREE OR WILL PERFORM IN AN UNINTERRUPTED MANNER. TO THE GREATEST EXTENT ALLOWED BY LAW, VMWARE SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE (EVEN IF VMWARE HAD BEEN INFORMED OF SUCH PURPOSE), AND NONINFRINGEMENT WITH RESPECT TO THE SOFTWARE, ANY MODIFICATIONS THERETO AND WITH RESPECT TO THE USE OF THE FOREGOING.
-
-5. Limitation of Liability
-IN NO EVENT WILL VMWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES OF ANY KIND (INCLUDING WITHOUT LIMITATION LOSS OF PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, LOSS OF DATA, COST OF COVER) IN CONNECTION WITH OR ARISING OUT OF OR RELATING TO THE FURNISHING, PERFORMANCE OR USE OF THE SOFTWARE OR ANY OTHER RIGHTS GRANTED HEREUNDER, WHETHER ALLEGED AS A BREACH OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE, AND EVEN IF VMWARE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-6. Term and Termination
-
- 6.1 - This Agreement will continue in effect unless and until terminated earlier pursuant to this Section 6.
-
- 6.2 - In the event You violate the terms of this Agreement, VMware may terminate this Agreement.
-
- 6.3 - All licenses granted hereunder shall terminate upon the termination of this Agreement. Termination will be in addition to any rights and remedies available to VMware at law or equity or under this Agreement.
-
- 6.4 - Termination of this Agreement will not affect the provisions regarding reservation of rights (Section 3.1), provisions disclaiming or limiting VMware's liability (Sections 4 and 5), Termination (Section 6) or Miscellaneous (Section 7), which provisions will survive termination of this Agreement.
-
-7. Miscellaneous
-This Agreement contains the entire agreement of the parties with respect to the subject matter of this Agreement and supersedes all previous communications, representations, understandings and agreements, either oral or written, between the parties with respect to said subject matter. The relationship of the parties hereunder is that of independent contractors, and this Agreement will not be construed as creating an agency, partnership, joint venture or any other form of legal association between the parties. If any term, condition, or provision in this Agreement is found to be invalid, unlawful or unenforceable to any extent, this Agreement will be construed in a manner that most closely effectuates the intent of this Agreement. Such invalid term, condition or provision will be severed from the remaining terms, conditions and provisions, which will continue to be valid and enforceable to the fullest extent permitted by law. This Agreement will be interpreted and construed in accordance with the laws of the State of California and the United States of America, without regard to conflict of law principles. The U.N. Convention on Contracts for the International Sale of Goods shall not apply to this Agreement. All disputes arising out of this Agreement involving VMware or any of its subsidiaries shall be subject to the jurisdiction of the federal or state courts of northern California, with venue lying in Santa Clara County, California. No rights may be assigned, no obligations may be delegated, and this Agreement may not be transferred by You, in whole or in part, whether voluntary or by operation of law, including by way of sale of assets, merger or consolidation, without the prior written consent of VMware, and any purported assignment, delegation or transfer without such consent shall be void ab initio. Any waiver of the provisions of this Agreement or of a party's rights or remedies under this Agreement must be in writing to be effective. Failure, neglect or delay by a party to enforce the provisions of this Agreement or its rights or remedies at any time, will not be construed or be deemed to be a waiver of such party's rights under this Agreement and will not in any way affect the validity of the whole or any part of this Agreement or prejudice such party's right to take subsequent action.
diff --git a/options/license/Zimbra-1.4 b/options/license/Zimbra-1.4
deleted file mode 100644
index 868e4d878b..0000000000
--- a/options/license/Zimbra-1.4
+++ /dev/null
@@ -1,47 +0,0 @@
-Zimbra Public License, Version 1.4 (ZPL)
-
-This Zimbra Public License (this “Agreementâ€) is a legal agreement that describes the terms under which Zimbra, Inc., a Texas corporation (“Zimbraâ€) will provide software to you via download or otherwise (“Softwareâ€). By using the Software, you, an individual or an entity (“Youâ€) agree to the terms of this Agreement.
-
-In consideration of the mutual promises and upon the terms and conditions set forth below, the parties agree as follows:
-
-1. Grant of Copyright License
-
- 1.1 - Subject to the terms and conditions of this Agreement, Zimbra hereby grants to You, under any and all of its copyright interest in and to the Software, a royalty-free, non-exclusive, non-transferable license to copy, modify, compile, execute, and distribute the Software and Modifications. For the purposes of this Agreement, any change to, addition to, or abridgement of the Software made by You is a “Modification;†however, any file You add to the Software that does not contain any part of the Software is not a “Modification.â€
-
- 1.2 - If You are an individual acting on behalf of a corporation or other entity, Your use of the Software or any Modification is subject to Your having the authority to bind such corporation or entity to this Agreement. Providing copies to persons within such corporation or entity is not considered distribution for purposes of this Agreement.
-
- 1.3 - For the Software or any Modification You distribute in source code format, You must do so only under the terms of this Agreement, and You must include a complete copy of this Agreement with Your distribution. With respect to any Modification You distribute in source code format, the terms of this Agreement will apply to You in the same way those terms apply to Zimbra with respect to the Software. In other words, when You are distributing Modifications under this Agreement, You “stand in the shoes†of Zimbra in terms of the rights You grant and how the terms and conditions apply to You and the licensees of Your Modifications. Notwithstanding the foregoing, when You “stand in the shoes†of Zimbra, You are not subject to the jurisdiction provision under Section 7, which requires all disputes under this Agreement to be subject to the jurisdiction of federal or state courts of Northern Texas.
-
- 1.4 - For the Software or any Modification You distribute in compiled or object code format, You must also provide recipients with access to the Software or Modification in source code format along with a complete copy of this Agreement. The distribution of the Software or Modifications in compiled or object code format may be under a license of Your choice, provided that You are in compliance with the terms of this Agreement. In addition, You must make absolutely clear that any license terms applying to such Software or Modification that differ from this Agreement are offered by You alone and not by Zimbra, and that such license does not restrict recipients from exercising rights in the source code to the Software granted by Zimbra under this Agreement or rights in the source code to any Modification granted by You as described in Section 1.3.
-
- 1.5 - This Agreement does not limit Your right to distribute files that are entirely Your own work (i.e., which do not incorporate any portion of the Software and are not Modifications) under any terms You choose.
-
-2. Support
-Zimbra has no obligation to provide technical support or updates to You. Nothing in this Agreement requires Zimbra to enter into any license with You for any other edition of the Software.
-
-3. Intellectual Property Rights
-
- 3.1 - Except for the license expressly granted under copyright in Section 1.1, no rights, licenses or forbearances are granted or may arise in relation to this Agreement whether expressly, by implication, exhaustion, estoppel or otherwise. All rights, including all intellectual property rights, that are not expressly granted under this Agreement are hereby reserved.
-
- 3.2 - In any copy of the Software or in any Modification you create, You must retain and reproduce any and all copyright, patent, trademark, and attribution notices that are included in the Software in the same form as they appear in the Software. This includes the preservation of attribution notices in the form of trademarks or logos that exist within a user interface of the Software.
-
- 3.3 - This license does not grant You rights to use any party’s name, logo, or trademarks, except solely as necessary to comply with Section 3.2.
-
-4. Disclaimer of Warranties
-THE SOFTWARE IS PROVIDED “AS IS†AND WITHOUT WARRANTY OF ANY KIND. ZIMBRA MAKES NO WARRANTIES, WHETHER EXPRESS, IMPLIED, OR STATUTORY, REGARDING OR RELATING TO THE SOFTWARE. SPECIFICALLY, ZIMBRA DOES NOT WARRANT THAT THE SOFTWARE WILL BE ERROR FREE OR WILL PERFORM IN AN UNINTERRUPTED MANNER. TO THE GREATEST EXTENT ALLOWED BY LAW, ZIMBRA SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE (EVEN IF ZIMBRA HAD BEEN INFORMED OF SUCH PURPOSE), AND NONINFRINGEMENT WITH RESPECT TO THE SOFTWARE, ANY MODIFICATIONS THERETO, AND WITH RESPECT TO THE USE OF THE FOREGOING.
-
-5. Limitation of Liability
-IN NO EVENT WILL ZIMBRA BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES OF ANY KIND (INCLUDING WITHOUT LIMITATION LOSS OF PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, LOSS OF DATA, AND COST OF COVER) IN CONNECTION WITH OR ARISING OUT OF OR RELATING TO THE FURNISHING, PERFORMANCE, OR USE OF THE SOFTWARE OR ANY OTHER RIGHTS GRANTED HEREUNDER, WHETHER ALLEGED AS A BREACH OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE, AND EVEN IF ZIMBRA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-6. Term and Termination
-
- 6.1 - This Agreement will continue in effect unless and until terminated earlier pursuant to this Section 6.
-
- 6.2 - In the event You violate the terms of this Agreement, Zimbra may terminate this Agreement.
-
- 6.3 - All licenses granted hereunder shall terminate upon the termination of this Agreement. Termination will be in addition to any rights and remedies available to Zimbra at law or equity or under this Agreement.
-
- 6.4 - Termination of this Agreement will not affect the provisions regarding reservation of rights (Section 3.1), provisions disclaiming or limiting Zimbra’s liability (Sections 4 and 5), Termination (Section 6), or Miscellaneous (Section 7), which provisions will survive termination of this Agreement.
-
-7. Miscellaneous
-This Agreement contains the entire agreement of the parties with respect to the subject matter of this Agreement and supersedes all previous communications, representations, understandings, and agreements, either oral or written, between the parties with respect to said subject matter. The relationship of the parties hereunder is that of independent contractors, and this Agreement will not be construed as creating an agency, partnership, joint venture, or any other form of legal association between the parties. If any term, condition, or provision in this Agreement is found to be invalid, unlawful, or unenforceable to any extent, this Agreement will be construed in a manner that most closely effectuates the intent of this Agreement. Such invalid term, condition or provision will be severed from the remaining terms, conditions, and provisions, which will continue to be valid and enforceable to the fullest extent permitted by law. This Agreement will be interpreted and construed in accordance with the laws of the State of Delaware and the United States of America, without regard to conflict of law principles. The U.N. Convention on Contracts for the International Sale of Goods shall not apply to this Agreement. All disputes arising out of this Agreement involving Zimbra or any of its parents or subsidiaries shall be subject to the jurisdiction of the federal or state courts of Northern Texas, with venue lying in Dallas County, Texas. No rights may be assigned, no obligations may be delegated, and this Agreement may not be transferred by You, in whole or in part, whether voluntary or by operation of law, including by way of sale of assets, merger, or consolidation, without the prior written consent of Zimbra, and any purported assignment, delegation, or transfer without such consent shall be void ab initio. Any waiver of the provisions of this Agreement or of a party’s rights or remedies under this Agreement must be in writing to be effective. Failure, neglect, or delay by a party to enforce the provisions of this Agreement or its rights or remedies at any time will not be construed or be deemed to be a waiver of such party’s rights under this Agreement and will not in any way affect the validity of the whole or any part of this Agreement or prejudice such party’s right to take subsequent action.
diff --git a/options/license/any-OSI b/options/license/any-OSI
deleted file mode 100644
index 5f69e02b8a..0000000000
--- a/options/license/any-OSI
+++ /dev/null
@@ -1,3 +0,0 @@
-Pick your favourite OSI approved license :)
-
-http://www.opensource.org/licenses/alphabetical
diff --git a/options/license/any-OSI-perl-modules b/options/license/any-OSI-perl-modules
deleted file mode 100644
index 108db04581..0000000000
--- a/options/license/any-OSI-perl-modules
+++ /dev/null
@@ -1,11 +0,0 @@
-This software may be redistributed under the terms of the GPL, LGPL,
-modified BSD, or Artistic license, or any of the other OSI approved
-licenses listed at http://www.opensource.org/licenses/alphabetical.
-Distribution is allowed under all of these licenses, or any smaller
-subset of multiple or just one of these licenses.
-
-When using a packaged version, please refer to the package metadata to see
-under which license terms it was distributed. Alternatively, a distributor
-may choose to replace the LICENSE section of the documentation and/or
-include a LICENSE file to reflect the license(s) they chose to redistribute
-under.
diff --git a/options/license/bcrypt-Solar-Designer b/options/license/bcrypt-Solar-Designer
deleted file mode 100644
index 8cb05017fc..0000000000
--- a/options/license/bcrypt-Solar-Designer
+++ /dev/null
@@ -1,11 +0,0 @@
-Written by Solar Designer <solar at openwall.com> in 1998-2014.
-No copyright is claimed, and the software is hereby placed in the public
-domain. In case this attempt to disclaim copyright and place the software
-in the public domain is deemed null and void, then the software is
-Copyright (c) 1998-2014 Solar Designer and it is hereby released to the
-general public under the following terms:
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted.
-
-There's ABSOLUTELY NO WARRANTY, express or implied.
diff --git a/options/license/blessing b/options/license/blessing
deleted file mode 100644
index 7a1b711a6b..0000000000
--- a/options/license/blessing
+++ /dev/null
@@ -1,5 +0,0 @@
-The author disclaims copyright to this source code. In place of a legal notice, here is a blessing:
-
-May you do good and not evil.
-May you find forgiveness for yourself and forgive others.
-May you share freely, never taking more than you give.
diff --git a/options/license/bzip2-1.0.6 b/options/license/bzip2-1.0.6
deleted file mode 100644
index d9b9184877..0000000000
--- a/options/license/bzip2-1.0.6
+++ /dev/null
@@ -1,15 +0,0 @@
-This program, "bzip2", the associated library "libbzip2", and all documentation, are copyright (C) 1996-2010 Julian R Seward. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
- 2. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
-
- 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
-
- 4. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-Julian Seward, jseward@bzip.org bzip2/libbzip2 version 1.0.6 of 6 September 2010
diff --git a/options/license/check-cvs b/options/license/check-cvs
deleted file mode 100644
index 85fee4791e..0000000000
--- a/options/license/check-cvs
+++ /dev/null
@@ -1,2 +0,0 @@
-Permission is granted to copy and/or distribute this file, with or
-without modifications, provided this notice is preserved.
diff --git a/options/license/checkmk b/options/license/checkmk
deleted file mode 100644
index 46c6b74278..0000000000
--- a/options/license/checkmk
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2006, 2010 Micah Cowan
-#
-# Redistribution of this program in any form, with or without
-# modifications, is permitted, provided that the above copyright is
-# retained in distributions of this program in source form.
-#
-# (This is a free, non-copyleft license compatible with pretty much any
-# other free or proprietary license, including the GPL. It's essentially
-# a scaled-down version of the "modified" BSD license.)
diff --git a/options/license/copyleft-next-0.3.0 b/options/license/copyleft-next-0.3.0
deleted file mode 100644
index a66d5bf5ee..0000000000
--- a/options/license/copyleft-next-0.3.0
+++ /dev/null
@@ -1,219 +0,0 @@
- copyleft-next 0.3.0 ("this License")
- Release date: 2013-05-16
-
-1. License Grants; No Trademark License
-
- Subject to the terms of this License, I grant You:
-
- a) A non-exclusive, worldwide, perpetual, royalty-free, irrevocable
- copyright license, to reproduce, Distribute, prepare derivative works
- of, publicly perform and publicly display My Work.
-
- b) A non-exclusive, worldwide, perpetual, royalty-free, irrevocable
- patent license under Licensed Patents to make, have made, use, sell,
- offer for sale, and import Covered Works.
-
- This License does not grant any rights in My name, trademarks, service
- marks, or logos.
-
-2. Distribution: General Conditions
-
- You may Distribute Covered Works, provided that You (i) inform
- recipients how they can obtain a copy of this License; (ii) satisfy the
- applicable conditions of sections 3 through 6; and (iii) preserve all
- Legal Notices contained in My Work (to the extent they remain
- pertinent). "Legal Notices" means copyright notices, license notices,
- license texts, and author attributions, but does not include logos,
- other graphical images, trademarks or trademark legends.
-
-3. Conditions for Distributing Derived Works; Outbound GPL Compatibility
-
- If You Distribute a Derived Work, You must license the entire Derived
- Work as a whole under this License, with prominent notice of such
- licensing. This condition may not be avoided through such means as
- separate Distribution of portions of the Derived Work. You may
- additionally license the Derived Work under the GPL, so that the
- recipient may further Distribute the Derived Work under either this
- License or the GPL.
-
-4. Condition Against Further Restrictions; Inbound License Compatibility
-
- When Distributing a Covered Work, You may not impose further
- restrictions on the exercise of rights in the Covered Work granted under
- this License. This condition is not excused merely because such
- restrictions result from Your compliance with conditions or obligations
- extrinsic to this License (such as a court order or an agreement with a
- third party).
-
- However, You may Distribute a Covered Work incorporating material
- governed by a license that is both OSI-Approved and FSF-Free as of the
- release date of this License, provided that Your Distribution complies
- with such other license.
-
-5. Conditions for Distributing Object Code
-
- You may Distribute an Object Code form of a Covered Work, provided that
- you accompany the Object Code with a URL through which the Corresponding
- Source is made available, at no charge, by some standard or customary
- means of providing network access to source code.
-
- If you Distribute the Object Code in a physical product or tangible
- storage medium ("Product"), the Corresponding Source must be available
- through such URL for two years from the date of Your most recent
- Distribution of the Object Code in the Product. However, if the Product
- itself contains or is accompanied by the Corresponding Source (made
- available in a customarily accessible manner), You need not also comply
- with the first paragraph of this section.
-
- Each recipient of the Covered Work from You is an intended third-party
- beneficiary of this License solely as to this section 5, with the right
- to enforce its terms.
-
-6. Symmetrical Licensing Condition for Upstream Contributions
-
- If You Distribute a work to Me specifically for inclusion in or
- modification of a Covered Work (a "Patch"), and no explicit licensing
- terms apply to the Patch, You license the Patch under this License, to
- the extent of Your copyright in the Patch. This condition does not
- negate the other conditions of this License, if applicable to the Patch.
-
-7. Nullification of Copyleft/Proprietary Dual Licensing
-
- If I offer to license, for a fee, a Covered Work under terms other than
- a license that is OSI-Approved or FSF-Free as of the release date of this
- License or a numbered version of copyleft-next released by the
- Copyleft-Next Project, then the license I grant You under section 1 is no
- longer subject to the conditions in sections 2 through 5.
-
-8. Copyleft Sunset
-
- The conditions in sections 2 through 5 no longer apply once fifteen
- years have elapsed from the date of My first Distribution of My Work
- under this License.
-
-9. Pass-Through
-
- When You Distribute a Covered Work, the recipient automatically receives
- a license to My Work from Me, subject to the terms of this License.
-
-10. Termination
-
- Your license grants under section 1 are automatically terminated if You
-
- a) fail to comply with the conditions of this License, unless You cure
- such noncompliance within thirty days after becoming aware of it, or
-
- b) initiate a patent infringement litigation claim (excluding
- declaratory judgment actions, counterclaims, and cross-claims)
- alleging that any part of My Work directly or indirectly infringes
- any patent.
-
- Termination of Your license grants extends to all copies of Covered
- Works You subsequently obtain. Termination does not terminate the
- rights of those who have received copies or rights from You subject to
- this License.
-
- To the extent permission to make copies of a Covered Work is necessary
- merely for running it, such permission is not terminable.
-
-11. Later License Versions
-
- The Copyleft-Next Project may release new versions of copyleft-next,
- designated by a distinguishing version number ("Later Versions").
- Unless I explicitly remove the option of Distributing Covered Works
- under Later Versions, You may Distribute Covered Works under any Later
- Version.
-
-** 12. No Warranty **
-** **
-** My Work is provided "as-is", without warranty. You bear the risk **
-** of using it. To the extent permitted by applicable law, each **
-** Distributor of My Work excludes the implied warranties of title, **
-** merchantability, fitness for a particular purpose and **
-** non-infringement. **
-
-** 13. Limitation of Liability **
-** **
-** To the extent permitted by applicable law, in no event will any **
-** Distributor of My Work be liable to You for any damages **
-** whatsoever, whether direct, indirect, special, incidental, or **
-** consequential damages, whether arising under contract, tort **
-** (including negligence), or otherwise, even where the Distributor **
-** knew or should have known about the possibility of such damages. **
-
-14. Severability
-
- The invalidity or unenforceability of any provision of this License
- does not affect the validity or enforceability of the remainder of
- this License. Such provision is to be reformed to the minimum extent
- necessary to make it valid and enforceable.
-
-15. Definitions
-
- "Copyleft-Next Project" means the project that maintains the source
- code repository at <https://gitorious.org/copyleft-next/> as of the
- release date of this License.
-
- "Corresponding Source" of a Covered Work in Object Code form means (i)
- the Source Code form of the Covered Work; (ii) all scripts,
- instructions and similar information that are reasonably necessary for
- a skilled developer to generate such Object Code from the Source Code
- provided under (i); and (iii) a list clearly identifying all Separate
- Works (other than those provided in compliance with (ii)) that were
- specifically used in building and (if applicable) installing the
- Covered Work (for example, a specified proprietary compiler including
- its version number). Corresponding Source must be machine-readable.
-
- "Covered Work" means My Work or a Derived Work.
-
- "Derived Work" means a work of authorship that copies from, modifies,
- adapts, is based on, is a derivative work of, transforms, translates or
- contains all or part of My Work, such that copyright permission is
- required. The following are not Derived Works: (i) Mere Aggregation;
- (ii) a mere reproduction of My Work; and (iii) if My Work fails to
- explicitly state an expectation otherwise, a work that merely makes
- reference to My Work.
-
- "Distribute" means to distribute, transfer or make a copy available to
- someone else, such that copyright permission is required.
-
- "Distributor" means Me and anyone else who Distributes a Covered Work.
-
- "FSF-Free" means classified as 'free' by the Free Software Foundation.
-
- "GPL" means a version of the GNU General Public License or the GNU
- Affero General Public License.
-
- "I"/"Me"/"My" refers to the individual or legal entity that places My
- Work under this License. "You"/"Your" refers to the individual or legal
- entity exercising rights in My Work under this License. A legal entity
- includes each entity that controls, is controlled by, or is under
- common control with such legal entity. "Control" means (a) the power to
- direct the actions of such legal entity, whether by contract or
- otherwise, or (b) ownership of more than fifty percent of the
- outstanding shares or beneficial ownership of such legal entity.
-
- "Licensed Patents" means all patent claims licensable royalty-free by
- Me, now or in the future, that are necessarily infringed by making,
- using, or selling My Work, and excludes claims that would be infringed
- only as a consequence of further modification of My Work.
-
- "Mere Aggregation" means an aggregation of a Covered Work with a
- Separate Work.
-
- "My Work" means the particular work of authorship I license to You
- under this License.
-
- "Object Code" means any form of a work that is not Source Code.
-
- "OSI-Approved" means approved as 'Open Source' by the Open Source
- Initiative.
-
- "Separate Work" means a work that is separate from and independent of a
- particular Covered Work and is not by its nature an extension or
- enhancement of the Covered Work, and/or a runtime library, standard
- library or similar component that is used to generate an Object Code
- form of a Covered Work.
-
- "Source Code" means the preferred form of a work for making
- modifications to it.
diff --git a/options/license/copyleft-next-0.3.1 b/options/license/copyleft-next-0.3.1
deleted file mode 100644
index 21c87270fc..0000000000
--- a/options/license/copyleft-next-0.3.1
+++ /dev/null
@@ -1,220 +0,0 @@
- copyleft-next 0.3.1 ("this License")
- Release date: 2016-04-29
-
-1. License Grants; No Trademark License
-
- Subject to the terms of this License, I grant You:
-
- a) A non-exclusive, worldwide, perpetual, royalty-free, irrevocable
- copyright license, to reproduce, Distribute, prepare derivative works
- of, publicly perform and publicly display My Work.
-
- b) A non-exclusive, worldwide, perpetual, royalty-free, irrevocable
- patent license under Licensed Patents to make, have made, use, sell,
- offer for sale, and import Covered Works.
-
- This License does not grant any rights in My name, trademarks, service
- marks, or logos.
-
-2. Distribution: General Conditions
-
- You may Distribute Covered Works, provided that You (i) inform
- recipients how they can obtain a copy of this License; (ii) satisfy the
- applicable conditions of sections 3 through 6; and (iii) preserve all
- Legal Notices contained in My Work (to the extent they remain
- pertinent). "Legal Notices" means copyright notices, license notices,
- license texts, and author attributions, but does not include logos,
- other graphical images, trademarks or trademark legends.
-
-3. Conditions for Distributing Derived Works; Outbound GPL Compatibility
-
- If You Distribute a Derived Work, You must license the entire Derived
- Work as a whole under this License, with prominent notice of such
- licensing. This condition may not be avoided through such means as
- separate Distribution of portions of the Derived Work.
-
- If the Derived Work includes material licensed under the GPL, You may
- instead license the Derived Work under the GPL.
-
-4. Condition Against Further Restrictions; Inbound License Compatibility
-
- When Distributing a Covered Work, You may not impose further
- restrictions on the exercise of rights in the Covered Work granted under
- this License. This condition is not excused merely because such
- restrictions result from Your compliance with conditions or obligations
- extrinsic to this License (such as a court order or an agreement with a
- third party).
-
- However, You may Distribute a Covered Work incorporating material
- governed by a license that is both OSI-Approved and FSF-Free as of the
- release date of this License, provided that compliance with such
- other license would not conflict with any conditions stated in other
- sections of this License.
-
-5. Conditions for Distributing Object Code
-
- You may Distribute an Object Code form of a Covered Work, provided that
- you accompany the Object Code with a URL through which the Corresponding
- Source is made available, at no charge, by some standard or customary
- means of providing network access to source code.
-
- If you Distribute the Object Code in a physical product or tangible
- storage medium ("Product"), the Corresponding Source must be available
- through such URL for two years from the date of Your most recent
- Distribution of the Object Code in the Product. However, if the Product
- itself contains or is accompanied by the Corresponding Source (made
- available in a customarily accessible manner), You need not also comply
- with the first paragraph of this section.
-
- Each direct and indirect recipient of the Covered Work from You is an
- intended third-party beneficiary of this License solely as to this
- section 5, with the right to enforce its terms.
-
-6. Symmetrical Licensing Condition for Upstream Contributions
-
- If You Distribute a work to Me specifically for inclusion in or
- modification of a Covered Work (a "Patch"), and no explicit licensing
- terms apply to the Patch, You license the Patch under this License, to
- the extent of Your copyright in the Patch. This condition does not
- negate the other conditions of this License, if applicable to the Patch.
-
-7. Nullification of Copyleft/Proprietary Dual Licensing
-
- If I offer to license, for a fee, a Covered Work under terms other than
- a license that is OSI-Approved or FSF-Free as of the release date of this
- License or a numbered version of copyleft-next released by the
- Copyleft-Next Project, then the license I grant You under section 1 is no
- longer subject to the conditions in sections 3 through 5.
-
-8. Copyleft Sunset
-
- The conditions in sections 3 through 5 no longer apply once fifteen
- years have elapsed from the date of My first Distribution of My Work
- under this License.
-
-9. Pass-Through
-
- When You Distribute a Covered Work, the recipient automatically receives
- a license to My Work from Me, subject to the terms of this License.
-
-10. Termination
-
- Your license grants under section 1 are automatically terminated if You
-
- a) fail to comply with the conditions of this License, unless You cure
- such noncompliance within thirty days after becoming aware of it, or
-
- b) initiate a patent infringement litigation claim (excluding
- declaratory judgment actions, counterclaims, and cross-claims)
- alleging that any part of My Work directly or indirectly infringes
- any patent.
-
- Termination of Your license grants extends to all copies of Covered
- Works You subsequently obtain. Termination does not terminate the
- rights of those who have received copies or rights from You subject to
- this License.
-
- To the extent permission to make copies of a Covered Work is necessary
- merely for running it, such permission is not terminable.
-
-11. Later License Versions
-
- The Copyleft-Next Project may release new versions of copyleft-next,
- designated by a distinguishing version number ("Later Versions").
- Unless I explicitly remove the option of Distributing Covered Works
- under Later Versions, You may Distribute Covered Works under any Later
- Version.
-
-** 12. No Warranty **
-** **
-** My Work is provided "as-is", without warranty. You bear the risk **
-** of using it. To the extent permitted by applicable law, each **
-** Distributor of My Work excludes the implied warranties of title, **
-** merchantability, fitness for a particular purpose and **
-** non-infringement. **
-
-** 13. Limitation of Liability **
-** **
-** To the extent permitted by applicable law, in no event will any **
-** Distributor of My Work be liable to You for any damages **
-** whatsoever, whether direct, indirect, special, incidental, or **
-** consequential damages, whether arising under contract, tort **
-** (including negligence), or otherwise, even where the Distributor **
-** knew or should have known about the possibility of such damages. **
-
-14. Severability
-
- The invalidity or unenforceability of any provision of this License
- does not affect the validity or enforceability of the remainder of
- this License. Such provision is to be reformed to the minimum extent
- necessary to make it valid and enforceable.
-
-15. Definitions
-
- "Copyleft-Next Project" means the project that maintains the source
- code repository at <https://github.com/copyleft-next/copyleft-next.git/>
- as of the release date of this License.
-
- "Corresponding Source" of a Covered Work in Object Code form means (i)
- the Source Code form of the Covered Work; (ii) all scripts,
- instructions and similar information that are reasonably necessary for
- a skilled developer to generate such Object Code from the Source Code
- provided under (i); and (iii) a list clearly identifying all Separate
- Works (other than those provided in compliance with (ii)) that were
- specifically used in building and (if applicable) installing the
- Covered Work (for example, a specified proprietary compiler including
- its version number). Corresponding Source must be machine-readable.
-
- "Covered Work" means My Work or a Derived Work.
-
- "Derived Work" means a work of authorship that copies from, modifies,
- adapts, is based on, is a derivative work of, transforms, translates or
- contains all or part of My Work, such that copyright permission is
- required. The following are not Derived Works: (i) Mere Aggregation;
- (ii) a mere reproduction of My Work; and (iii) if My Work fails to
- explicitly state an expectation otherwise, a work that merely makes
- reference to My Work.
-
- "Distribute" means to distribute, transfer or make a copy available to
- someone else, such that copyright permission is required.
-
- "Distributor" means Me and anyone else who Distributes a Covered Work.
-
- "FSF-Free" means classified as 'free' by the Free Software Foundation.
-
- "GPL" means a version of the GNU General Public License or the GNU
- Affero General Public License.
-
- "I"/"Me"/"My" refers to the individual or legal entity that places My
- Work under this License. "You"/"Your" refers to the individual or legal
- entity exercising rights in My Work under this License. A legal entity
- includes each entity that controls, is controlled by, or is under
- common control with such legal entity. "Control" means (a) the power to
- direct the actions of such legal entity, whether by contract or
- otherwise, or (b) ownership of more than fifty percent of the
- outstanding shares or beneficial ownership of such legal entity.
-
- "Licensed Patents" means all patent claims licensable royalty-free by
- Me, now or in the future, that are necessarily infringed by making,
- using, or selling My Work, and excludes claims that would be infringed
- only as a consequence of further modification of My Work.
-
- "Mere Aggregation" means an aggregation of a Covered Work with a
- Separate Work.
-
- "My Work" means the particular work of authorship I license to You
- under this License.
-
- "Object Code" means any form of a work that is not Source Code.
-
- "OSI-Approved" means approved as 'Open Source' by the Open Source
- Initiative.
-
- "Separate Work" means a work that is separate from and independent of a
- particular Covered Work and is not by its nature an extension or
- enhancement of the Covered Work, and/or a runtime library, standard
- library or similar component that is used to generate an Object Code
- form of a Covered Work.
-
- "Source Code" means the preferred form of a work for making
- modifications to it.
diff --git a/options/license/cryptsetup-OpenSSL-exception b/options/license/cryptsetup-OpenSSL-exception
deleted file mode 100644
index 25c1c420d2..0000000000
--- a/options/license/cryptsetup-OpenSSL-exception
+++ /dev/null
@@ -1,12 +0,0 @@
-In addition, as a special exception, the copyright holders give
-permission to link the code of portions of this program with the OpenSSL
-library under certain conditions as described in each individual source
-file, and distribute linked combinations including the two.
-
-You must obey the GNU General Public License in all respects for all of
-the code used other than OpenSSL. If you modify file(s) with this
-exception, you may extend this exception to your version of the file(s),
-but you are not obligated to do so. If you do not wish to do so, delete
-this exception statement from your version. If you delete this exception
-statement from all source files in the program, then also delete it
-here.
diff --git a/options/license/curl b/options/license/curl
deleted file mode 100644
index dd333ab2e4..0000000000
--- a/options/license/curl
+++ /dev/null
@@ -1,10 +0,0 @@
-COPYRIGHT AND PERMISSION NOTICE
-
-Copyright (c) 1996 - 2015, Daniel Stenberg, <daniel@haxx.se>.
-All rights reserved.
-
-Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder.
diff --git a/options/license/cve-tou b/options/license/cve-tou
deleted file mode 100644
index c7b2f02e3e..0000000000
--- a/options/license/cve-tou
+++ /dev/null
@@ -1,16 +0,0 @@
-CVE Usage: MITRE hereby grants you a perpetual, worldwide, non-exclusive,
-no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
-derivative works of, publicly display, publicly perform, sublicense, and
-distribute Common Vulnerabilities and Exposures (CVE®). Any copy you make for
-such purposes is authorized provided that you reproduce MITRE's copyright
-designation and this license in any such copy.
-
-DISCLAIMERS
-
-ALL DOCUMENTS AND THE INFORMATION CONTAINED THEREIN PROVIDED BY MITRE ARE
-PROVIDED ON AN "AS IS" BASIS AND THE CONTRIBUTOR, THE ORGANIZATION HE/SHE
-REPRESENTS OR IS SPONSORED BY (IF ANY), THE MITRE CORPORATION, ITS BOARD OF
-TRUSTEES, OFFICERS, AGENTS, AND EMPLOYEES, DISCLAIM ALL WARRANTIES, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
-INFORMATION THEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
-MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/options/license/diffmark b/options/license/diffmark
deleted file mode 100644
index 02d3b5013d..0000000000
--- a/options/license/diffmark
+++ /dev/null
@@ -1,2 +0,0 @@
-1. you can do what you want with it
-2. I refuse any responsibility for the consequences
diff --git a/options/license/dtoa b/options/license/dtoa
deleted file mode 100644
index 6de2b084fc..0000000000
--- a/options/license/dtoa
+++ /dev/null
@@ -1,14 +0,0 @@
-The author of this software is David M. Gay.
-
-Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
-
-Permission to use, copy, modify, and distribute this software for any
-purpose without fee is hereby granted, provided that this entire notice
-is included in all copies of any software which is or includes a copy
-or modification of this software and in all copies of the supporting
-documentation for such software.
-
-THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
-WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
-REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
-OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
diff --git a/options/license/dvipdfm b/options/license/dvipdfm
deleted file mode 100644
index b73cb48e47..0000000000
--- a/options/license/dvipdfm
+++ /dev/null
@@ -1 +0,0 @@
-A modified version of this file may be distributed, but it should be distributed with a *different* name. Changed files must be distributed *together with a complete and unchanged* distribution of these files.
diff --git a/options/license/eCos-exception-2.0 b/options/license/eCos-exception-2.0
deleted file mode 100644
index a0bf0077c5..0000000000
--- a/options/license/eCos-exception-2.0
+++ /dev/null
@@ -1,3 +0,0 @@
-As a special exception, if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other works to produce a work based on this file, this file does not by itself cause the resulting work to be covered by the GNU General Public License. However the source code for this file must still be made available in accordance with section (3) of the GNU General Public License.
-
-This exception does not invalidate any other reasons why a work based on this file might be covered by the GNU General Public License.
diff --git a/options/license/eGenix b/options/license/eGenix
deleted file mode 100644
index 9b4d30128a..0000000000
--- a/options/license/eGenix
+++ /dev/null
@@ -1,40 +0,0 @@
-EGENIX.COM PUBLIC LICENSE AGREEMENT
-Version 1.1.0
-
-This license agreement is based on the Python CNRI License Agreement, a widely accepted open- source license.
-
-1. Introduction
-This "License Agreement" is between eGenix.com Software, Skills and Services GmbH ("eGenix.com"), having an office at Pastor-Loeh-Str. 48, D-40764 Langenfeld, Germany, and the Individual or Organization ("Licensee") accessing and otherwise using this software in source or binary form and its associated documentation ("the Software").
-
-2. License
-Subject to the terms and conditions of this eGenix.com Public License Agreement, eGenix.com hereby grants Licensee a non-exclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use the Software alone or in any derivative version, provided, however, that the eGenix.com Public License Agreement is retained in the Software, or in any derivative version of the Software prepared by Licensee.
-
-3. NO WARRANTY
-eGenix.com is making the Software available to Licensee on an "AS IS" basis. SUBJECT TO ANY STATUTORY WARRANTIES WHICH CAN NOT BE EXCLUDED, EGENIX.COM MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, EGENIX.COM MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
-
-4. LIMITATION OF LIABILITY
-EGENIX.COM SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS) AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THE ABOVE EXCLUSION OR LIMITATION MAY NOT APPLY TO LICENSEE.
-
-5. Termination
-This License Agreement will automatically terminate upon a material breach of its terms and conditions.
-
-6. Third Party Rights
-Any software or documentation in source or binary form provided along with the Software that is associated with a separate license agreement is licensed to Licensee under the terms of that license agreement. This License Agreement does not apply to those portions of the Software. Copies of the third party licenses are included in the Software Distribution.
-
-7. General
-Nothing in this License Agreement affects any statutory rights of consumers that cannot be waived or limited by contract.
-
-Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between eGenix.com and Licensee.
-
-If any provision of this License Agreement shall be unlawful, void, or for any reason unenforceable, such provision shall be modified to the extent necessary to render it enforceable without losing its intent, or, if no such modification is possible, be severed from this License Agreement and shall not affect the validity and enforceability of the remaining provisions of this License Agreement.
-
-This License Agreement shall be governed by and interpreted in all respects by the law of Germany, excluding conflict of law provisions. It shall not be governed by the United Nations Convention on Contracts for International Sale of Goods. This License Agreement does not grant permission to use eGenix.com trademarks or trade names in a trademark sense to endorse or promote products or services of Licensee, or any third party.
-
-The controlling language of this License Agreement is English. If Licensee has received a translation into another language, it has been provided for Licensee's convenience only.
-
-8. Agreement
-By downloading, copying, installing or otherwise using the Software, Licensee agrees to be bound by the terms and conditions of this License Agreement. For question regarding this License Agreement, please write to:
- eGenix.com Software, Skills and Services GmbH
- Pastor-Loeh-Str. 48
- D-40764 Langenfeld
- Germany
diff --git a/options/license/erlang-otp-linking-exception b/options/license/erlang-otp-linking-exception
deleted file mode 100644
index ca8b775480..0000000000
--- a/options/license/erlang-otp-linking-exception
+++ /dev/null
@@ -1,11 +0,0 @@
-If you modify this Program, or any covered work, by linking or
-combining it with runtime libraries of Erlang/OTP as released by
-Ericsson on https://www.erlang.org (or a modified version of these
-libraries), containing parts covered by the terms of the Erlang Public
-License (https://www.erlang.org/EPLICENSE), the licensors of this
-Program grant you additional permission to convey the resulting work
-without the need to license the runtime libraries of Erlang/OTP under
-the GNU Affero General Public License. Corresponding Source for a
-non-source form of such a combination shall include the source code
-for the parts of the runtime libraries of Erlang/OTP used as well as
-that of the covered work.
diff --git a/options/license/etalab-2.0 b/options/license/etalab-2.0
deleted file mode 100644
index 2ba1978351..0000000000
--- a/options/license/etalab-2.0
+++ /dev/null
@@ -1,179 +0,0 @@
-LICENCE OUVERTE / OPEN LICENCE
-===================================================================
-
-- Version 2.0
-- Avril 2017
-
-
-« RÉUTILISATION » DE L’« INFORMATION » SOUS CETTE LICENCE
--------------------------------------------------------------------
-
-Le « Concédant » concède au « Réutilisateur » un droit non exclusif et gratuit
-de libre « Réutilisation » de l’« Information » objet de la présente licence,
-à des fins commerciales ou non, dans le monde entier et pour une durée
-illimitée, dans les conditions exprimées ci-dessous.
-
-Le « Réutilisateur » est libre de réutiliser l’« Information » :
-
-- de la reproduire, la copier,
-- de l’adapter, la modifier, l’extraire et la transformer, pour créer des
-« Informations dérivées », des produits ou des services,
-- de la communiquer, la diffuser, la redistribuer, la publier et la transmettre,
-- de l’exploiter à titre commercial, par exemple en la combinant avec d’autres
-informations, ou en l’incluant dans son propre produit ou application.
-
-Sous réserve de :
-
-- mentionner la paternité de l’« Information » : sa source (au moins le nom du
-« Concédant ») et la date de dernière mise à jour de l’« Information »
-réutilisée.
-
-Le « Réutilisateur » peut notamment s’acquitter de cette condition en renvoyant,
-par un lien hypertexte, vers la source de « l’Information » et assurant une
-mention effective de sa paternité.
-
-Par exemple :
-
-« Ministère de xxx - Données originales téléchargées sur
-http://www.data.gouv.fr/fr/datasets/xxx/, mise à jour du 14 février 2017 ».
-
-Cette mention de paternité ne confère aucun caractère officiel à la
-« Réutilisation » de l’« Information », et ne doit pas suggérer une quelconque
-reconnaissance ou caution par le « Concédant », ou par toute autre entité
-publique, du « Réutilisateur » ou de sa « Réutilisation ».
-
-
-« DONNÉES À CARACTÈRE PERSONNEL »
--------------------------------------------------------------------
-
-L’« Information » mise à disposition peut contenir des « Données à caractère
-personnel » pouvant faire l’objet d’une « Réutilisation ». Si tel est le cas,
-le « Concédant » informe le « Réutilisateur » de leur présence.
-
-L’« Information » peut être librement réutilisée, dans le cadre des droits
-accordés par la présente licence, à condition de respecter le cadre légal
-relatif à la protection des données à caractère personnel.
-
-
-« DROITS DE PROPRIÉTÉ INTELLECTUELLE »
--------------------------------------------------------------------
-
-Il est garanti au « Réutilisateur » que les éventuels « Droits de propriété
-intellectuelle » détenus par des tiers ou par le « Concédant » sur
-l’« Information » ne font pas obstacle aux droits accordés par la présente
-licence.
-
-Lorsque le « Concédant » détient des « Droits de propriété intellectuelle »
-cessibles sur l’« Information », il les cède au « Réutilisateur » de façon non
-exclusive, à titre gracieux, pour le monde entier, pour toute la durée des
-« Droits de propriété intellectuelle », et le « Réutilisateur » peut faire tout
-usage de l’« Information » conformément aux libertés et aux conditions définies
-par la présente licence.
-
-
-RESPONSABILITÉ
--------------------------------------------------------------------
-
-L’« Information » est mise à disposition telle que produite ou reçue par le
-« Concédant », sans autre garantie expresse ou tacite que celles prévues par la
-présente licence. L’absence de défauts ou d’erreurs éventuellement contenues
-dans l’« Information », comme la fourniture continue de l’« Information » n’est
-pas garantie par le « Concédant ». Il ne peut être tenu pour responsable de
-toute perte, préjudice ou dommage de quelque sorte causé à des tiers du fait de
-la « Réutilisation ».
-
-Le « Réutilisateur » est seul responsable de la « Réutilisation » de
-l’« Information ».
-
-La « Réutilisation » ne doit pas induire en erreur des tiers quant au contenu
-de l’« Information », sa source et sa date de mise à jour.
-
-
-DROIT APPLICABLE
--------------------------------------------------------------------
-
-La présente licence est régie par le droit français.
-
-
-COMPATIBILITÉ DE LA PRÉSENTE LICENCE
--------------------------------------------------------------------
-
-La présente licence a été conçue pour être compatible avec toute licence libre
-qui exige au moins la mention de paternité et notamment avec la version
-antérieure de la présente licence ainsi qu’avec les licences :
-
-- « Open Government Licence » (OGL) du Royaume-Uni,
-- « Creative Commons Attribution » (CC-BY) de Creative Commons et
-- « Open Data Commons Attribution » (ODC-BY) de l’Open Knowledge Foundation.
-
-
-DÉFINITIONS
--------------------------------------------------------------------
-
-Sont considérés, au sens de la présente licence comme :
-
-Le « Concédant » : toute personne concédant un droit de « Réutilisation » sur
-l’« Information » dans les libertés et les conditions prévues par la présente
-licence
-
-L’« Information » :
-
-- toute information publique figurant dans des documents communiqués ou publiés
-par une administration mentionnée au premier alinéa de l’article L.300-2 du
-CRPA;
-- toute information mise à disposition par toute personne selon les termes et
-conditions de la présente licence.
-
-La « Réutilisation » : l’utilisation de l’« Information » à d’autres fins que
-celles pour lesquelles elle a été produite ou reçue.
-
-Le « Réutilisateur »: toute personne qui réutilise les « Informations »
-conformément aux conditions de la présente licence.
-
-Des « Données à caractère personnel » : toute information se rapportant à une
-personne physique identifiée ou identifiable, pouvant être identifiée
-directement ou indirectement. Leur « Réutilisation » est subordonnée au respect
-du cadre juridique en vigueur.
-
-Une « Information dérivée » : toute nouvelle donnée ou information créées
-directement à partir de l’« Information » ou à partir d’une combinaison de
-l’« Information » et d’autres données ou informations non soumises à cette
-licence.
-
-Les « Droits de propriété intellectuelle » : tous droits identifiés comme tels
-par le Code de la propriété intellectuelle (notamment le droit d’auteur, droits
-voisins au droit d’auteur, droit sui generis des producteurs de bases de
-données…).
-
-
-À PROPOS DE CETTE LICENCE
--------------------------------------------------------------------
-
-La présente licence a vocation à être utilisée par les administrations pour la
-réutilisation de leurs informations publiques. Elle peut également être
-utilisée par toute personne souhaitant mettre à disposition de
-l’« Information » dans les conditions définies par la présente licence.
-
-La France est dotée d’un cadre juridique global visant à une diffusion
-spontanée par les administrations de leurs informations publiques afin d’en
-permettre la plus large réutilisation.
-
-Le droit de la « Réutilisation » de l’« Information » des administrations est
-régi par le code des relations entre le public et l’administration (CRPA).
-
-Cette licence facilite la réutilisation libre et gratuite des informations
-publiques et figure parmi les licences qui peuvent être utilisées par
-l’administration en vertu du décret pris en application de l’article L.323-2
-du CRPA.
-
-Etalab est la mission chargée, sous l’autorité du Premier ministre, d’ouvrir le
-plus grand nombre de données publiques des administrations de l’Etat et de ses
-établissements publics. Elle a réalisé la Licence Ouverte pour faciliter la
-réutilisation libre et gratuite de ces informations publiques, telles que
-définies par l’article L321-1 du CRPA.
-
-Cette licence est la version 2.0 de la Licence Ouverte.
-
-Etalab se réserve la faculté de proposer de nouvelles versions de la Licence
-Ouverte. Cependant, les « Réutilisateurs » pourront continuer à réutiliser les
-informations qu’ils ont obtenues sous cette licence s’ils le souhaitent.
diff --git a/options/license/etc/license-aliases.json b/options/license/etc/license-aliases.json
deleted file mode 100644
index fe2cf2d58e..0000000000
--- a/options/license/etc/license-aliases.json
+++ /dev/null
@@ -1 +0,0 @@
-{"AGPL-1.0-only":"AGPL-1.0","AGPL-1.0-or-later":"AGPL-1.0","AGPL-3.0-only":"AGPL-3.0","AGPL-3.0-or-later":"AGPL-3.0","CAL-1.0":"CAL-1.0","CAL-1.0-Combined-Work-Exception":"CAL-1.0","GFDL-1.1-invariants-only":"GFDL-1.1","GFDL-1.1-invariants-or-later":"GFDL-1.1","GFDL-1.1-no-invariants-only":"GFDL-1.1","GFDL-1.1-no-invariants-or-later":"GFDL-1.1","GFDL-1.1-only":"GFDL-1.1","GFDL-1.1-or-later":"GFDL-1.1","GFDL-1.2-invariants-only":"GFDL-1.2","GFDL-1.2-invariants-or-later":"GFDL-1.2","GFDL-1.2-no-invariants-only":"GFDL-1.2","GFDL-1.2-no-invariants-or-later":"GFDL-1.2","GFDL-1.2-only":"GFDL-1.2","GFDL-1.2-or-later":"GFDL-1.2","GFDL-1.3-invariants-only":"GFDL-1.3","GFDL-1.3-invariants-or-later":"GFDL-1.3","GFDL-1.3-no-invariants-only":"GFDL-1.3","GFDL-1.3-no-invariants-or-later":"GFDL-1.3","GFDL-1.3-only":"GFDL-1.3","GFDL-1.3-or-later":"GFDL-1.3","GPL-1.0-only":"GPL-1.0","GPL-1.0-or-later":"GPL-1.0","GPL-2.0-only":"GPL-2.0","GPL-2.0-or-later":"GPL-2.0","GPL-3.0-only":"GPL-3.0","GPL-3.0-or-later":"GPL-3.0","LGPL-2.0-only":"LGPL-2.0","LGPL-2.0-or-later":"LGPL-2.0","LGPL-2.1-only":"LGPL-2.1","LGPL-2.1-or-later":"LGPL-2.1","LGPL-3.0-only":"LGPL-3.0","LGPL-3.0-or-later":"LGPL-3.0","MPL-2.0":"MPL-2.0","MPL-2.0-no-copyleft-exception":"MPL-2.0","OFL-1.0":"OFL-1.0","OFL-1.0-RFN":"OFL-1.0","OFL-1.0-no-RFN":"OFL-1.0","OFL-1.1":"OFL-1.1","OFL-1.1-RFN":"OFL-1.1","OFL-1.1-no-RFN":"OFL-1.1"} \ No newline at end of file
diff --git a/options/license/fmt-exception b/options/license/fmt-exception
deleted file mode 100644
index 6036f7d360..0000000000
--- a/options/license/fmt-exception
+++ /dev/null
@@ -1,6 +0,0 @@
---- Optional exception to the license ---
-
-As an exception, if, as a result of your compiling your source code, portions
-of this Software are embedded into a machine-executable object form of such
-source code, you may redistribute such embedded portions in such object form
-without including the above copyright and permission notices.
diff --git a/options/license/freertos-exception-2.0 b/options/license/freertos-exception-2.0
deleted file mode 100644
index 0105e95971..0000000000
--- a/options/license/freertos-exception-2.0
+++ /dev/null
@@ -1,19 +0,0 @@
-Any FreeRTOS source code, whether modified or in its original release form, or whether in whole or in part, can only be distributed by you under the terms of the GNU General Public License plus this exception. An independent module is a module which is not derived from or based on FreeRTOS.
-
-EXCEPTION TEXT:
-
-Clause 1
-
-Linking FreeRTOS statically or dynamically with other modules is making a combined work based on FreeRTOS. Thus, the terms and conditions of the GNU General Public License cover the whole combination.
-
-As a special exception, the copyright holder of FreeRTOS gives you permission to link FreeRTOS with independent modules that communicate with FreeRTOS solely through the FreeRTOS API interface, regardless of the license terms of these independent modules, and to copy and distribute the resulting combined work under terms of your choice, provided that
-
-Every copy of the combined work is accompanied by a written statement that details to the recipient the version of FreeRTOS used and an offer by yourself to provide the FreeRTOS source code (including any modifications you may have made) should the recipient request it.
-
-The combined work is not itself an RTOS, scheduler, kernel or related product.
-
-The independent modules add significant and primary functionality to FreeRTOS and do not merely extend the existing functionality already present in FreeRTOS.
-
-Clause 2
-
-FreeRTOS may not be used for any competitive or comparative purpose, including the publication of any form of run time or compile time metric, without the express permission of Real Time Engineers Ltd. (this is the norm within the industry and is intended to ensure information accuracy).
diff --git a/options/license/fwlw b/options/license/fwlw
deleted file mode 100644
index 472a85a564..0000000000
--- a/options/license/fwlw
+++ /dev/null
@@ -1,5 +0,0 @@
-Copyright (C) 1993,1995 by Donald Arseneau
-Vancouver, Canada, email asnd@triumf.ca
-
-This software package may be freely used, transmitted, reproduced, or modified provided that
-this notice is left intact.
diff --git a/options/license/gSOAP-1.3b b/options/license/gSOAP-1.3b
deleted file mode 100644
index d7f2704a3a..0000000000
--- a/options/license/gSOAP-1.3b
+++ /dev/null
@@ -1,148 +0,0 @@
-gSOAP Public License
-
-Version 1.3b
-
-The gSOAP public license is derived from the Mozilla Public License (MPL1.1). The sections that were deleted from the original MPL1.1 text are 1.0.1, 2.1.(c),(d), 2.2.(c),(d), 8.2.(b), 10, and 11. Section 3.8 was added. The modified sections are 2.1.(b), 2.2.(b), 3.2 (simplified), 3.5 (deleted the last sentence), and 3.6 (simplified).
-
-This license applies to the gSOAP software package, with the exception of the soapcpp2 and wsdl2h source code located in gsoap/src and gsoap/wsdl, all code generated by soapcpp2 and wsdl2h, the UDDI source code gsoap/uddi2, and the Web server sample source code samples/webserver. To use any of these software tools and components commercially, a commercial license is required and can be obtained from www.genivia.com.
-
-1 DEFINITIONS.
-
-1.0.1.
- 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications.
-
- 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.
-
- 1.3. "Covered Code" means the Original Code, or Modifications or the combination of the Original Code, and Modifications, in each case including portions thereof.
-
- 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data.
-
- 1.5. "Executable" means Covered Code in any form other than Source Code.
-
- 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.
-
- 1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.
-
- 1.8. "License" means this document.
-
- 1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
- 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:
- A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.
- B. Any new file that contains any part of the Original Code, or previous Modifications.
-
- 1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.
-
- 1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
- 1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.
-
- 1.12. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2 SOURCE CODE LICENSE.
-
-2.1. The Initial Developer Grant.
-The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:
- (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and
- (b) under patents now or hereafter owned or controlled by Initial Developer, to make, have made, use and sell ("offer to sell and import") the Original Code, Modifications, or portions thereof, but solely to the extent that any such patent is reasonably necessary to enable You to utilize, alone or in combination with other software, the Original Code, Modifications, or any combination or portions thereof.
- (c)
- (d)
-
-2.2. Contributor Grant.
-Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license
- (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and
- (b) under patents now or hereafter owned or controlled by Contributor, to make, have made, use and sell ("offer to sell and import") the Contributor Version (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to utilize, alone or in combination with other software, the Contributor Version (or portions thereof).
- (c)
- (d)
-
-3 DISTRIBUTION OBLIGATIONS.
-
-3.1. Application of License.
-The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.
-
-3.2. Availability of Source Code.
-Any Modification created by You will be provided to the Initial Developer in Source Code form and are subject to the terms of the License.
-
-3.3. Description of Modifications.
-You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.
-
-3.4. Intellectual Property Matters.
- (a) Third Party Claims. If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.
-
- (b) Contributor APIs. If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file.
-
- (c) Representations. Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.
-
-3.5. Required Notices.
-You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor.
-
-3.6. Distribution of Executable Versions.
-You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. If you distribute executable versions containing Covered Code as part of a product, you must reproduce the notice in Exhibit B in the documentation and/or other materials provided with the product.
-
-3.7. Larger Works.
-You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.
-
-3.8. Restrictions.
-You may not remove any product identification, copyright, proprietary notices or labels from gSOAP.
-
-4 INABILITY TO COMPLY DUE TO STATUTE OR REGULATION.
-
-If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.
-
-5 APPLICATION OF THIS LICENSE.
-
-This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code.
-
-6 VERSIONS OF THE LICENSE.
-
-6.1. New Versions.
-Grantor may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.
-
-6.2. Effect of New Versions.
-Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License.
-
-6.3. Derivative Works.
-If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrase "gSOAP" or any confusingly similar phrase do not appear in your license (except to note that you license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the gSOAP Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)
-
-7 DISCLAIMER OF WARRANTY.
-
-COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY RIGHTS, AND ANY WARRANTY THAT MAY ARISE BY REASON OF TRADE USAGE, CUSTOM, OR COURSE OF DEALING. WITHOUT LIMITING THE FOREGOING, YOU ACKNOWLEDGE THAT THE SOFTWARE IS PROVIDED "AS IS" AND THAT THE AUTHORS DO NOT WARRANT THE SOFTWARE WILL RUN UNINTERRUPTED OR ERROR FREE. LIMITED LIABILITY THE ENTIRE RISK AS TO RESULTS AND PERFORMANCE OF THE SOFTWARE IS ASSUMED BY YOU. UNDER NO CIRCUMSTANCES WILL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES OF ANY KIND OR NATURE WHATSOEVER, WHETHER BASED ON CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, ARISING OUT OF OR IN ANY WAY RELATED TO THE SOFTWARE, EVEN IF THE AUTHORS HAVE BEEN ADVISED ON THE POSSIBILITY OF SUCH DAMAGE OR IF SUCH DAMAGE COULD HAVE BEEN REASONABLY FORESEEN, AND NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF ANY EXCLUSIVE REMEDY PROVIDED. SUCH LIMITATION ON DAMAGES INCLUDES, BUT IS NOT LIMITED TO, DAMAGES FOR LOSS OF GOODWILL, LOST PROFITS, LOSS OF DATA OR SOFTWARE, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION OR IMPAIRMENT OF OTHER GOODS. IN NO EVENT WILL THE AUTHORS BE LIABLE FOR THE COSTS OF PROCUREMENT OF SUBSTITUTE SOFTWARE OR SERVICES. YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT DESIGNED FOR USE IN ON-LINE EQUIPMENT IN HAZARDOUS ENVIRONMENTS SUCH AS OPERATION OF NUCLEAR FACILITIES, AIRCRAFT NAVIGATION OR CONTROL, OR LIFE-CRITICAL APPLICATIONS. THE AUTHORS EXPRESSLY DISCLAIM ANY LIABILITY RESULTING FROM USE OF THE SOFTWARE IN ANY SUCH ON-LINE EQUIPMENT IN HAZARDOUS ENVIRONMENTS AND ACCEPTS NO LIABILITY IN RESPECT OF ANY ACTIONS OR CLAIMS BASED ON THE USE OF THE SOFTWARE IN ANY SUCH ON-LINE EQUIPMENT IN HAZARDOUS ENVIRONMENTS BY YOU. FOR PURPOSES OF THIS PARAGRAPH, THE TERM "LIFE-CRITICAL APPLICATION" MEANS AN APPLICATION IN WHICH THE FUNCTIONING OR MALFUNCTIONING OF THE SOFTWARE MAY RESULT DIRECTLY OR INDIRECTLY IN PHYSICAL INJURY OR LOSS OF HUMAN LIFE. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8 TERMINATION.
-
-8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-8.2.
-
-8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
-8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.
-
-9 LIMITATION OF LIABILITY.
-
-UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10 U.S. GOVERNMENT END USERS.
-
-11 MISCELLANEOUS.
-
-12 RESPONSIBILITY FOR CLAIMS.
-
-As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-EXHIBIT A.
-
-"The contents of this file are subject to the gSOAP Public License Version 1.3 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
-http://www.cs.fsu.edu/ engelen/soaplicense.html
-Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.
-The Original Code of the gSOAP Software is: stdsoap.h, stdsoap2.h, stdsoap.c, stdsoap2.c, stdsoap.cpp, stdsoap2.cpp, soapcpp2.h, soapcpp2.c, soapcpp2_lex.l, soapcpp2_yacc.y, error2.h, error2.c, symbol2.c, init2.c, soapdoc2.html, and soapdoc2.pdf, httpget.h, httpget.c, stl.h, stldeque.h, stllist.h, stlvector.h, stlset.h.
-The Initial Developer of the Original Code is Robert A. van Engelen. Portions created by Robert A. van Engelen are Copyright (C) 2001-2004 Robert A. van Engelen, Genivia inc. All Rights Reserved.
-Contributor(s):
-"________________________."
-[Note: The text of this Exhibit A may differ slightly form the text of the notices in the Source Code files of the Original code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.]
-
-EXHIBIT B.
-
-"Part of the software embedded in this product is gSOAP software.
-Portions created by gSOAP are Copyright (C) 2001-2009 Robert A. van Engelen, Genivia inc. All Rights Reserved.
-THE SOFTWARE IN THIS PRODUCT WAS IN PART PROVIDED BY GENIVIA INC AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
diff --git a/options/license/generic-xts b/options/license/generic-xts
deleted file mode 100644
index bf08a2b421..0000000000
--- a/options/license/generic-xts
+++ /dev/null
@@ -1,17 +0,0 @@
-Copyright (C) 2008, Damien Miller
-Copyright (C) 2011, Alex Hornung
-
-Permission to use, copy, and modify this software with or without fee
-is hereby granted, provided that this entire notice is included in
-all copies of any software which is or includes a copy or
-modification of this software.
-You may use this code under the GNU public license if you so wish. Please
-contribute changes back to the authors under this freer than GPL license
-so that we may further the use of strong encryption without limitations to
-all.
-
-THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
-IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
-REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
-MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
-PURPOSE.
diff --git a/options/license/gnu-javamail-exception b/options/license/gnu-javamail-exception
deleted file mode 100644
index 8f3b9ab0d0..0000000000
--- a/options/license/gnu-javamail-exception
+++ /dev/null
@@ -1 +0,0 @@
-As a special exception, if you link this library with other files to produce an executable, this library does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU General Public License.
diff --git a/options/license/gnuplot b/options/license/gnuplot
deleted file mode 100644
index 10bfbd4241..0000000000
--- a/options/license/gnuplot
+++ /dev/null
@@ -1,14 +0,0 @@
-Copyright 1986 - 1993, 1998, 2004 Thomas Williams, Colin Kelley
-
-Permission to use, copy, and distribute this software and its documentation for any purpose with or without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation.
-
-Permission to modify the software is granted, but not the right to distribute the complete modified source code. Modifications are to be distributed as patches to the released version. Permission to distribute binaries produced by compiling modified sources is granted, provided you
-
- 1. distribute the corresponding source modifications from the released version in the form of a patch file along with the binaries,
- 2. add special version identification to distinguish your version in addition to the base release version number,
- 3. provide your name and address as the primary contact for the support of your modified version, and
- 4. retain our contact information in regard to use of the base software.
-
-Permission to distribute the released version of the source code along with corresponding source modifications in the form of a patch file is granted with same provisions 2 through 4 for binary distributions.
-
-This software is provided "as is" without express or implied warranty to the extent permitted by applicable law.
diff --git a/options/license/gtkbook b/options/license/gtkbook
deleted file mode 100644
index 91215e80d6..0000000000
--- a/options/license/gtkbook
+++ /dev/null
@@ -1,6 +0,0 @@
-Copyright 2005 Syd Logan, All Rights Reserved
-
-This code is distributed without warranty. You are free to use
-this code for any purpose, however, if this code is republished or
-redistributed in its original form, as hardcopy or electronically,
-then you must include this copyright notice along with the code.
diff --git a/options/license/harbour-exception b/options/license/harbour-exception
deleted file mode 100644
index 25d75e9fc7..0000000000
--- a/options/license/harbour-exception
+++ /dev/null
@@ -1,23 +0,0 @@
-As a special exception, the Harbour Project gives permission for
-additional uses of the text contained in its release of Harbour.
-
-The exception is that, if you link the Harbour libraries with other
-files to produce an executable, this does not by itself cause the
-resulting executable to be covered by the GNU General Public License.
-Your use of that executable is in no way restricted on account of
-linking the Harbour library code into it.
-
-This exception does not however invalidate any other reasons why
-the executable file might be covered by the GNU General Public License.
-
-This exception applies only to the code released by the Harbour
-Project under the name Harbour. If you copy code from other
-Harbour Project or Free Software Foundation releases into a copy of
-Harbour, as the General Public License permits, the exception does
-not apply to the code that you add in this way. To avoid misleading
-anyone as to the status of such modified files, you must delete
-this exception notice from them.
-
-If you write modifications of your own for Harbour, it is your choice
-whether to permit this exception to apply to your modifications.
-If you do not wish that, delete this exception notice.
diff --git a/options/license/hdparm b/options/license/hdparm
deleted file mode 100644
index 280a1c0797..0000000000
--- a/options/license/hdparm
+++ /dev/null
@@ -1,9 +0,0 @@
-BSD-Style Open Source License:
-
-You may freely use, modify, and redistribute the hdparm program,
-as either binary or source, or both.
-
-The only condition is that my name and copyright notice
-remain in the source code as-is.
-
-Mark Lord (mlord@pobox.com)
diff --git a/options/license/i2p-gpl-java-exception b/options/license/i2p-gpl-java-exception
deleted file mode 100644
index 2b7277d778..0000000000
--- a/options/license/i2p-gpl-java-exception
+++ /dev/null
@@ -1 +0,0 @@
-In addition, as a special exception, <<var;name=licensor;original=XXXX;match=.+>> gives permission to link the code of this program with the proprietary Java implementation provided by Sun (or other vendors as well), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than the proprietary Java implementation. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version.
diff --git a/options/license/iMatix b/options/license/iMatix
deleted file mode 100644
index 7689d97b3b..0000000000
--- a/options/license/iMatix
+++ /dev/null
@@ -1,39 +0,0 @@
-The SFL License Agreement
-
-This license agreement covers your use of the iMatix STANDARD FUNCTION LIBRARY (SFL), its source code, documentation, and executable files, hereinafter referred to as "the Product".
-
-The Product is Copyright (c) 1991-2000 iMatix Corporation. You may use it and distribute it according to this following License Agreement. If you do not agree with these terms, please remove the Product from your system. By incorporating the Product in your work or distributing the Product to others you implicitly agree to these license terms.
-
-Statement Of Copyright
-
-The Product is, and remains, Copyright © 1991-2000 iMatix Corporation, with exception of specific copyrights as noted in the individual source files.
-
-Conditions Of Use
-
-You do not need to provide the source code for the Product as part of your product. However, you must do one of these things to comply with the Product License Agreement:
-
- 1. Provide the source code for Product modules that you use, or
- 2. Make your product freely available according to a license similar to the GNU General Public License, or the Perl Artistic License, or
- 3. Add this phrase to the documentation for your product: "This product uses parts of the iMatix SFL, Copyright © 1991-2000 iMatix Corporation <http://www.imatix.com>".
-
-Rights Of Usage
-
-You may freely and at no cost use the Product in any project, commercial, academic, military, or private, so long as you respect the License Agreement. The License Agreement does not affect any software except the Product. In particular, any application that uses the Product does not itself fall under the License Agreement.
-
-You may modify any part of the Product, including sources and documentation, except this License Agreement, which you may not modify.
-
-You must clearly indicate any modifications at the start of each source file. The user of any modified Product code must know that the source file is not original.
-
-At your discretion, you may rewrite or reuse any part of the Product so that your derived code is not obviously part of the Product. This derived code does not fall under the Product License Agreement directly, but you must include a credit at the start of each source file indicating the original authorship and source of the code, and a statement of copyright as follows:"Parts copyright (c) 1991-2000 iMatix Corporation."
-
-Rights Of Distribution
-
-You may freely distribute the Product, or any subset of the Product, by any means. The License, in the form of the file called "LICENSE.TXT" must accompany any such distribution.
-
-You may charge a fee for distributing the Product, for providing a warranty on the Product, for making modifications to the Product, or for any other service provided in relation to the Product. You are not required to ask our permission for any of these activities.
-
-At no time will iMatix associate itself with any distribution of the Product except that supplied from the Internet site http://www.imatix.com.
-
-Disclaimer Of Warranty
-
-The Product is provided as free software, in the hope that it will be useful. It is provided "as-is", without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the Product is with you. Should the Product prove defective, the full cost of repair, servicing, or correction lies with you.
diff --git a/options/license/libpng-2.0 b/options/license/libpng-2.0
deleted file mode 100644
index 72ae6e44ef..0000000000
--- a/options/license/libpng-2.0
+++ /dev/null
@@ -1,33 +0,0 @@
-PNG Reference Library License version 2
----------------------------------------
-
- * Copyright (c) 1995-2018 The PNG Reference Library Authors.
- * Copyright (c) 2018 Cosmin Truta.
- * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
- * Copyright (c) 1996-1997 Andreas Dilger.
- * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
-
-The software is supplied "as is", without warranty of any kind,
-express or implied, including, without limitation, the warranties
-of merchantability, fitness for a particular purpose, title, and
-non-infringement. In no event shall the Copyright owners, or
-anyone distributing the software, be liable for any damages or
-other liability, whether in contract, tort or otherwise, arising
-from, out of, or in connection with the software, or the use or
-other dealings in the software, even if advised of the possibility
-of such damage.
-
-Permission is hereby granted to use, copy, modify, and distribute
-this software, or portions hereof, for any purpose, without fee,
-subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you
- must not claim that you wrote the original software. If you
- use this software in a product, an acknowledgment in the product
- documentation would be appreciated, but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must
- not be misrepresented as being the original software.
-
- 3. This Copyright notice may not be removed or altered from any
- source or altered source distribution.
diff --git a/options/license/libpri-OpenH323-exception b/options/license/libpri-OpenH323-exception
deleted file mode 100644
index 490d9596d6..0000000000
--- a/options/license/libpri-OpenH323-exception
+++ /dev/null
@@ -1,4 +0,0 @@
-As a special exception, libpri may also be linked to the
-OpenH323 library, so long as the entirity of the derivative
-work (as defined within the GPL) is licensed either under
-the MPL of the OpenH323 license or the GPL of libpri.
diff --git a/options/license/libselinux-1.0 b/options/license/libselinux-1.0
deleted file mode 100644
index d386268911..0000000000
--- a/options/license/libselinux-1.0
+++ /dev/null
@@ -1,21 +0,0 @@
-This library (libselinux) is public domain software, i.e. not copyrighted.
-
-Warranty Exclusion
-------------------
-You agree that this software is a
-non-commercially developed program that may contain "bugs" (as that
-term is used in the industry) and that it may not function as intended.
-The software is licensed "as is". NSA makes no, and hereby expressly
-disclaims all, warranties, express, implied, statutory, or otherwise
-with respect to the software, including noninfringement and the implied
-warranties of merchantability and fitness for a particular purpose.
-
-Limitation of Liability
------------------------
-In no event will NSA be liable for any damages, including loss of data,
-lost profits, cost of cover, or other special, incidental,
-consequential, direct or indirect damages arising from the software or
-the use thereof, however caused and on any theory of liability. This
-limitation will apply even if NSA has been advised of the possibility
-of such damage. You acknowledge that this is a reasonable allocation of
-risk.
diff --git a/options/license/libtiff b/options/license/libtiff
deleted file mode 100644
index b11996b671..0000000000
--- a/options/license/libtiff
+++ /dev/null
@@ -1,8 +0,0 @@
-Copyright (c) 1988-1997 Sam Leffler
-Copyright (c) 1991-1997 Silicon Graphics, Inc.
-
-Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that (i) the above copyright notices and this permission notice appear in all copies of the software and related documentation, and (ii) the names of Sam Leffler and Silicon Graphics may not be used in any advertising or publicity relating to the software without the specific, prior written permission of Sam Leffler and Silicon Graphics.
-
-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 SAM LEFFLER OR SILICON GRAPHICS 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.
diff --git a/options/license/libutil-David-Nugent b/options/license/libutil-David-Nugent
deleted file mode 100644
index e04b03e340..0000000000
--- a/options/license/libutil-David-Nugent
+++ /dev/null
@@ -1,15 +0,0 @@
-Copyright (c) 1995 David Nugent <davidn@blaze.net.au>
-All rights reserved.
-
-
-Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice immediately at the beginning of the file, without modification, this list of conditions, and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. This work was done expressly for inclusion into FreeBSD. Other use is permitted provided this notation is included.
-
-4. Absolutely no warranty of function or purpose is made by the author David Nugent.
-
-5. Modifications may be freely made to this file providing the above conditions are met.
diff --git a/options/license/lsof b/options/license/lsof
deleted file mode 100644
index 279721a90a..0000000000
--- a/options/license/lsof
+++ /dev/null
@@ -1,26 +0,0 @@
-Copyright 2002 Purdue Research Foundation, West Lafayette,
-Indiana 47907. All rights reserved.
-
-Written by Victor A. Abell
-
-This software is not subject to any license of the American
-Telephone and Telegraph Company or the Regents of the
-University of California.
-
-Permission is granted to anyone to use this software for
-any purpose on any computer system, and to alter it and
-redistribute it freely, subject to the following
-restrictions:
-
-1. Neither the authors nor Purdue University are responsible
- for any consequences of the use of this software.
-
-2. The origin of this software must not be misrepresented,
- either by explicit claim or by omission. Credit to the
- authors and Purdue University must appear in documentation
- and sources.
-
-3. Altered versions must be plainly marked as such, and must
- not be misrepresented as being the original software.
-
-4. This notice may not be removed or altered.
diff --git a/options/license/magaz b/options/license/magaz
deleted file mode 100644
index 34d033c03e..0000000000
--- a/options/license/magaz
+++ /dev/null
@@ -1,4 +0,0 @@
-Copyright 1999-2011, Donald Arseneau, asnd@triumf.ca, Vancouver, Canada
-
-This software may be freely used, transmitted, reproduced, or modified provided that
-the copyright notice and this permission is retained.
diff --git a/options/license/mailprio b/options/license/mailprio
deleted file mode 100644
index e004e4b683..0000000000
--- a/options/license/mailprio
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright 1994, 1996, Tony Sanders <sanders@earth.com>
-
-Rights are hereby granted to download, use, modify, sell, copy, and
-redistribute this software so long as the original copyright notice
-and this list of conditions remain intact and modified versions are
-noted as such.
-
-I would also very much appreciate it if you could send me a copy of
-any changes you make so I can possibly integrate them into my version.
diff --git a/options/license/metamail b/options/license/metamail
deleted file mode 100644
index be7a8a4e5a..0000000000
--- a/options/license/metamail
+++ /dev/null
@@ -1,12 +0,0 @@
-Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
-
-Permission to use, copy, modify, and distribute this material
-for any purpose and without fee is hereby granted, provided
-that the above copyright notice and this permission notice
-appear in all copies, and that the name of Bellcore not be
-used in advertising or publicity pertaining to this
-material without the specific, prior written permission
-of an authorized representative of Bellcore. BELLCORE
-MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
-OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS",
-WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
diff --git a/options/license/mif-exception b/options/license/mif-exception
deleted file mode 100644
index ceb2626c6f..0000000000
--- a/options/license/mif-exception
+++ /dev/null
@@ -1 +0,0 @@
-As a special exception, you may use this file as part of a free software library without restriction. Specifically, if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other files to produce an executable, this file does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU General Public License.
diff --git a/options/license/mpi-permissive b/options/license/mpi-permissive
deleted file mode 100644
index 2abcbe3ab0..0000000000
--- a/options/license/mpi-permissive
+++ /dev/null
@@ -1,15 +0,0 @@
-* Copyright (C) 2000-2004 by Etnus, LLC
- *
- * Permission is hereby granted to use, reproduce, prepare derivative
- * works, and to redistribute to others.
- *
- * DISCLAIMER
- *
- * Neither Etnus, nor any of their employees, makes any warranty
- * express or implied, or assumes any legal liability or
- * responsibility for the accuracy, completeness, or usefulness of any
- * information, apparatus, product, or process disclosed, or
- * represents that its use would not infringe privately owned rights.
- *
- * This code was written by
- * James Cownie: Etnus, LLC. <jcownie@etnus.com>
diff --git a/options/license/mpich2 b/options/license/mpich2
deleted file mode 100644
index 1fa4acbc62..0000000000
--- a/options/license/mpich2
+++ /dev/null
@@ -1,24 +0,0 @@
-COPYRIGHT
-
-The following is a notice of limited availability of the code, and disclaimer which must be included in the prologue of the code and in all source listings of the code.
-
-Copyright Notice
-1998--2020, Argonne National Laboratory
-
-Permission is hereby granted to use, reproduce, prepare derivative works, and to redistribute to others. This software was authored by:
-
-Mathematics and Computer Science Division
-Argonne National Laboratory, Argonne IL 60439
-
-(and)
-
-Department of Computer Science
-University of Illinois at Urbana-Champaign
-
-GOVERNMENT LICENSE
-
-Portions of this material resulted from work developed under a U.S. Government Contract and are subject to the following license: the Government is granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable worldwide license in this computer software to reproduce, prepare derivative works, and perform publicly and display publicly.
-
-DISCLAIMER
-
-This computer code material was prepared, in part, as an account of work sponsored by an agency of the United States Government. Neither the United States, nor the University of Chicago, nor any of their employees, makes any warranty express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately owned rights.
diff --git a/options/license/mplus b/options/license/mplus
deleted file mode 100644
index 7ff7d504d1..0000000000
--- a/options/license/mplus
+++ /dev/null
@@ -1,6 +0,0 @@
-
-These fonts are free softwares. Unlimited permission is
-granted to use, copy, and distribute it, with or without
-modification, either commercially and noncommercially.
-THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
-
diff --git a/options/license/mxml-exception b/options/license/mxml-exception
deleted file mode 100644
index 32928e8dd6..0000000000
--- a/options/license/mxml-exception
+++ /dev/null
@@ -1,16 +0,0 @@
-Mini-XML
-
-Copyright © 2003-2024 by Michael R Sweet
-
-
-(Optional) Exceptions to the Apache 2.0 License:
-================================================
-
-In addition, if you combine or link compiled forms of this Software with
-software that is licensed under the GPLv2 or LGPLv2 (“Combined Softwareâ€) and if
-a court of competent jurisdiction determines that the patent provision (Section
-3), the indemnity provision (Section 9) or other Section of the License
-conflicts with the conditions of the GPLv2 or LGPLv2, you may retroactively and
-prospectively choose to deem waived or otherwise exclude such Section(s) of the
-License, but only in their entirety and only with respect to the Combined
-Software.
diff --git a/options/license/openvpn-openssl-exception b/options/license/openvpn-openssl-exception
deleted file mode 100644
index e9e0a367ea..0000000000
--- a/options/license/openvpn-openssl-exception
+++ /dev/null
@@ -1,3 +0,0 @@
-Special exception for linking OpenVPN with OpenSSL:
-
-In addition, as a special exception, OpenVPN Technologies, Inc. gives permission to link the code of this program with the OpenSSL Library (or with modified versions of OpenSSL that use the same license as OpenSSL), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version.
diff --git a/options/license/pkgconf b/options/license/pkgconf
deleted file mode 100644
index b8b2ffd996..0000000000
--- a/options/license/pkgconf
+++ /dev/null
@@ -1,7 +0,0 @@
-Permission to use, copy, modify, and/or distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-This software is provided 'as is' and without any warranty, express or
-implied. In no event shall the authors be liable for any damages arising
-from the use of this software.
diff --git a/options/license/pnmstitch b/options/license/pnmstitch
deleted file mode 100644
index cb9dc762d9..0000000000
--- a/options/license/pnmstitch
+++ /dev/null
@@ -1,23 +0,0 @@
-Copyright (c) 2002 Mark Salyzyn
-All rights reserved.
-
-TERMS AND CONDITIONS OF USE
-
-Redistribution and use in source form, with or without modification, are
-permitted provided that redistributions of source code must retain the
-above copyright notice, this list of conditions and the following
-disclaimer.
-
-This software is provided `as is' by Mark Salyzyn and any express or implied
-warranties, including, but not limited to, the implied warranties of
-merchantability and fitness for a particular purpose, are disclaimed. In no
-event shall Mark Salyzyn be liable for any direct, indirect, incidental,
-special, exemplary or consequential damages (including, but not limited to,
-procurement of substitute goods or services; loss of use, data, or profits;
-or business interruptions) however caused and on any theory of liability,
-whether in contract, strict liability, or tort (including negligence or
-otherwise) arising in any way out of the use of this software, even if
-advised of the possibility of such damage.
-
-Any restrictions or encumberances added to this source code or derivitives,
-is prohibited.
diff --git a/options/license/psfrag b/options/license/psfrag
deleted file mode 100644
index c5c3a77737..0000000000
--- a/options/license/psfrag
+++ /dev/null
@@ -1,5 +0,0 @@
-psfrag.dtx
-Copyright (C) 1996 Craig Barratt, Michael C. Grant, and David Carlisle.
-All rights are reserved.
-
-This system is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Don't come complaining to us if you modify this file and it doesn't work! If this file is modified by anyone but the authors, those changes and their authors must be explicitly stated HERE.
diff --git a/options/license/psutils b/options/license/psutils
deleted file mode 100644
index a420741853..0000000000
--- a/options/license/psutils
+++ /dev/null
@@ -1,29 +0,0 @@
-PS Utilities Package
-
-The constituent files of this package listed below are copyright (C) 1991-1995 Angus J. C. Duggan.
-
-LICENSE Makefile.msc Makefile.nt Makefile.os2
-Makefile.unix README config.h descrip.mms
-epsffit.c epsffit.man extractres.man extractres.pl
-fixdlsrps.man fixdlsrps.pl fixfmps.man fixfmps.pl
-fixmacps.man fixmacps.pl fixpsditps.man fixpsditps.pl
-fixpspps.man fixpspps.pl fixscribeps.man fixscribeps.pl
-fixtpps.man fixtpps.pl fixwfwps.man fixwfwps.pl
-fixwpps.man fixwpps.pl fixwwps.man fixwwps.pl
-getafm getafm.man includeres.man includeres.pl
-maketext patchlev.h psbook.c psbook.man
-pserror.c pserror.h psmerge.man psmerge.pl
-psnup.c psnup.man psresize.c psresize.man
-psselect.c psselect.man psspec.c psspec.h
-pstops.c pstops.man psutil.c psutil.h
-showchar
-
-They may be copied and used for any purpose (including distribution as part of a for-profit product), provided:
-
-1) The original attribution of the programs is clearly displayed in the product and/or documentation, even if the programs are modified and/or renamed as part of the product.
-
-2) The original source code of the programs is provided free of charge (except for reasonable distribution costs). For a definition of reasonable distribution costs, see the Gnu General Public License or Larry Wall's Artistic License (provided with the Perl 4 kit). The GPL and Artistic License in NO WAY affect this license; they are merely used as examples of the spirit in which it is intended.
-
-3) These programs are provided "as-is". No warranty or guarantee of their fitness for any particular task is provided. Use of these programs is completely at your own risk.
-
-Basically, I don't mind how you use the programs so long as you acknowledge the author, and give people the originals if they want them.
diff --git a/options/license/python-ldap b/options/license/python-ldap
deleted file mode 100644
index 733e8cfc0a..0000000000
--- a/options/license/python-ldap
+++ /dev/null
@@ -1,10 +0,0 @@
-The python-ldap package is distributed under Python-style license.
-
-Standard disclaimer:
- This software is made available by the author(s) to the public for free
- and "as is". All users of this free software are solely and entirely
- responsible for their own choice and use of this software for their
- own purposes. By using this software, each user agrees that the
- author(s) shall not be liable for damages of any kind in relation to
- its use or performance. The author(s) do not warrant that this software
- is fit for any purpose.
diff --git a/options/license/radvd b/options/license/radvd
deleted file mode 100644
index 4e77909ed7..0000000000
--- a/options/license/radvd
+++ /dev/null
@@ -1,37 +0,0 @@
- The author(s) grant permission for redistribution and use in source and
-binary forms, with or without modification, of the software and documentation
-provided that the following conditions are met:
-
-0. If you receive a version of the software that is specifically labelled
- as not being for redistribution (check the version message and/or README),
- you are not permitted to redistribute that version of the software in any
- way or form.
-1. All terms of all other applicable copyrights and licenses must be
- followed.
-2. Redistributions of source code must retain the authors' copyright
- notice(s), this list of conditions, and the following disclaimer.
-3. Redistributions in binary form must reproduce the authors' copyright
- notice(s), this list of conditions, and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-4. All advertising materials mentioning features or use of this software
- must display the following acknowledgement with the name(s) of the
- authors as specified in the copyright notice(s) substituted where
- indicated:
-
- This product includes software developed by the authors which are
- mentioned at the start of the source files and other contributors.
-
-5. Neither the name(s) of the author(s) nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/romic-exception b/options/license/romic-exception
deleted file mode 100644
index 57def44818..0000000000
--- a/options/license/romic-exception
+++ /dev/null
@@ -1,6 +0,0 @@
-Additional permission under the GNU Affero GPL version 3 section 7:
-
-If you modify this Program, or any covered work, by linking or
-combining it with other code, such other code is not for that reason
-alone subject to any of the requirements of the GNU Affero GPL
-version 3.
diff --git a/options/license/snprintf b/options/license/snprintf
deleted file mode 100644
index 9e4ae73daa..0000000000
--- a/options/license/snprintf
+++ /dev/null
@@ -1,3 +0,0 @@
-Copyright Patrick Powell 1995
-
-This code is based on code written by Patrick Powell (papowell@astart.com) It may be used for any purpose as long as this notice remains intact on all source code distributions
diff --git a/options/license/softSurfer b/options/license/softSurfer
deleted file mode 100644
index 1bbc88c34c..0000000000
--- a/options/license/softSurfer
+++ /dev/null
@@ -1,6 +0,0 @@
-Copyright 2001, softSurfer (www.softsurfer.com)
-This code may be freely used and modified for any purpose
-providing that this copyright notice is included with it.
-SoftSurfer makes no warranty for this code, and cannot be held
-liable for any real or imagined damage resulting from its use.
-Users of this code must verify correctness for their application.
diff --git a/options/license/ssh-keyscan b/options/license/ssh-keyscan
deleted file mode 100644
index 6c97472c1e..0000000000
--- a/options/license/ssh-keyscan
+++ /dev/null
@@ -1,5 +0,0 @@
-* Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
-*
-* Modification and redistribution in source and binary forms is
-* permitted provided that due credit is given to the author and the
-* OpenBSD project by leaving this copyright notice intact.
diff --git a/options/license/stunnel-exception b/options/license/stunnel-exception
deleted file mode 100644
index 5e38d00a0f..0000000000
--- a/options/license/stunnel-exception
+++ /dev/null
@@ -1,5 +0,0 @@
-Linking stunnel statically or dynamically with other modules is making a combined work based on stunnel. Thus, the terms and conditions of the GNU General Public License cover the whole combination.
-
-In addition, as a special exception, the copyright holder of stunnel gives you permission to combine stunnel with free software programs or libraries that are released under the GNU LGPL and with code included in the standard release of OpenSSL under the OpenSSL License (or modified versions of such code, with unchanged license). You may copy and distribute such a system following the terms of the GNU GPL for stunnel and the licenses of the other code concerned.
-
-Note that people who make modified versions of stunnel are not obligated to grant this special exception for their modified versions; it is their choice whether to do so. The GNU General Public License gives permission to release a modified version without this exception; this exception also makes it possible to release a modified version which carries forward this exception.
diff --git a/options/license/swrule b/options/license/swrule
deleted file mode 100644
index aebc5fd6d3..0000000000
--- a/options/license/swrule
+++ /dev/null
@@ -1 +0,0 @@
-The style package is copyrighted but may be used and extended in any way, as long as a pointer to the original author is maintained. The author is not liable for any problem that may or may not result from using this package. Use at your own risk.
diff --git a/options/license/threeparttable b/options/license/threeparttable
deleted file mode 100644
index 498b728226..0000000000
--- a/options/license/threeparttable
+++ /dev/null
@@ -1,3 +0,0 @@
-This file may be distributed, modified, and used in other works with just
-one restriction: modified versions must clearly indicate the modification
-(a name change, or a displayed message, or ?).
diff --git a/options/license/u-boot-exception-2.0 b/options/license/u-boot-exception-2.0
deleted file mode 100644
index 3158dade32..0000000000
--- a/options/license/u-boot-exception-2.0
+++ /dev/null
@@ -1,6 +0,0 @@
-The U-Boot License Exception:
-
-Even though U-Boot in general is covered by the GPL-2.0/GPL-2.0+, this does *not* cover the so-called "standalone" applications that use U-Boot services by means of the jump table provided by U-Boot exactly for this purpose - this is merely considered normal use of U-Boot, and does *not* fall under the heading of "derived work".
-
-The header files "include/image.h" and "arch/*/include/asm/u-boot.h" define interfaces to U-Boot. Including these (unmodified) header files in another file is considered normal use of U-Boot, and does *not* fall under the heading of "derived work".
--- Wolfgang Denk
diff --git a/options/license/ulem b/options/license/ulem
deleted file mode 100644
index ee49efe8dd..0000000000
--- a/options/license/ulem
+++ /dev/null
@@ -1,4 +0,0 @@
-Copyright 1989-2019 by Donald Arseneau (Vancouver, Canada, asnd@triumf.ca)
-
-This software may be freely transmitted, reproduced, or modified
-for any purpose provided that this copyright notice is left intact.
diff --git a/options/license/vsftpd-openssl-exception b/options/license/vsftpd-openssl-exception
deleted file mode 100644
index a864761e48..0000000000
--- a/options/license/vsftpd-openssl-exception
+++ /dev/null
@@ -1,5 +0,0 @@
-vsftpd is licensed under version 2 of the GNU GPL.
-As copyright holder, I give permission for vsftpd to be linked to the OpenSSL
-libraries. This includes permission for vsftpd binaries to be distributed
-linked against the OpenSSL libraries. All other obligations under the GPL v2
-remain intact.
diff --git a/options/license/w3m b/options/license/w3m
deleted file mode 100644
index 37081007bf..0000000000
--- a/options/license/w3m
+++ /dev/null
@@ -1,11 +0,0 @@
-(C) Copyright 1994-2002 by Akinori Ito
-(C) Copyright 2002-2011 by Akinori Ito, Hironori Sakamoto, Fumitoshi Ukai
-
-Use, modification and redistribution of this software is hereby granted,
-provided that this entire copyright notice is included on any copies of
-this software and applications and derivations thereof.
-
-This software is provided on an "as is" basis, without warranty of any
-kind, either expressed or implied, as to any matter including, but not
-limited to warranty of fitness of purpose, or merchantability, or
-results obtained from use of this software.
diff --git a/options/license/wwl b/options/license/wwl
deleted file mode 100644
index 12486ff638..0000000000
--- a/options/license/wwl
+++ /dev/null
@@ -1,5 +0,0 @@
-db@FreeBSD.ORG wrote this file. As long as you retain this notice you
-can do whatever you want with this code, except you may not
-license it under any form of the GPL.
-A postcard or QSL card showing me you appreciate
-this code would be nice. Diane Bruce va3db
diff --git a/options/license/x11vnc-openssl-exception b/options/license/x11vnc-openssl-exception
deleted file mode 100644
index 040e31c7a9..0000000000
--- a/options/license/x11vnc-openssl-exception
+++ /dev/null
@@ -1,9 +0,0 @@
-In addition, as a special exception, Karl J. Runge
-gives permission to link the code of its release of x11vnc with the
-OpenSSL project's "OpenSSL" library (or with modified versions of it
-that use the same license as the "OpenSSL" library), and distribute
-the linked executables. You must obey the GNU General Public License
-in all respects for all of the code used other than "OpenSSL". If you
-modify this file, you may extend this exception to your version of the
-file, but you are not obligated to do so. If you do not wish to do
-so, delete this exception statement from your version.
diff --git a/options/license/xinetd b/options/license/xinetd
deleted file mode 100644
index 684e13ba35..0000000000
--- a/options/license/xinetd
+++ /dev/null
@@ -1,25 +0,0 @@
-ORIGINAL LICENSE: This software is
-
-(c) Copyright 1992 by Panagiotis Tsirigotis
-
-The author (Panagiotis Tsirigotis) grants permission to use, copy, and distribute this software and its documentation for any purpose and without fee, provided that the above copyright notice extant in files in this distribution is not removed from files included in any redistribution and that this copyright notice is also included in any redistribution.
-
-Modifications to this software may be distributed, either by distributing the modified software or by distributing patches to the original software, under the following additional terms:
-
-1. The version number will be modified as follows:
- a. The first 3 components of the version number (i.e <number>.<number>.<number>) will remain unchanged.
- b. A new component will be appended to the version number to indicate the modification level. The form of this component is up to the author of the modifications.
-
-2. The author of the modifications will include his/her name by appending it along with the new version number to this file and will be responsible for any wrong behavior of the modified software.
-
-The author makes no representations about the suitability of this software for any purpose. It is provided "as is" without any express or implied warranty.
-
-Modifications: Version: 2.1.8.7-current Copyright 1998-2001 by Rob Braun
-
-Sensor Addition Version: 2.1.8.9pre14a Copyright 2001 by Steve Grubb
-
-This is an exerpt from an email I recieved from the original author, allowing xinetd as maintained by me (Rob Braun), to use the higher version numbers:
-
-I appreciate your maintaining the version string guidelines as specified in the copyright. But I did not mean them to last as long as they did.
-
-So, if you want, you may use any 2.N.* (N >= 3) version string for future xinetd versions that you release. Note that I am excluding the 2.2.* line; using that would only create confusion. Naming the next release 2.3.0 would put to rest the confusion about 2.2.1 and 2.1.8.*.
diff --git a/options/license/xkeyboard-config-Zinoviev b/options/license/xkeyboard-config-Zinoviev
deleted file mode 100644
index 509fc255e2..0000000000
--- a/options/license/xkeyboard-config-Zinoviev
+++ /dev/null
@@ -1,15 +0,0 @@
-Copyright (C) 1999, 2000 by Anton Zinoviev <anton@lml.bas.bg>
-
-This software may be used, modified, copied, distributed, and sold,
-in both source and binary form provided that the above copyright
-and these terms are retained. Under no circumstances is the author
-responsible for the proper functioning of this software, nor does
-the author assume any responsibility for damages incurred with its
-use.
-
-Permission is granted to anyone to use, distribute and modify
-this file in any way, provided that the above copyright notice
-is left intact and the author of the modification summarizes
-the changes in this header.
-
-This file is distributed without any expressed or implied warranty.
diff --git a/options/license/xlock b/options/license/xlock
deleted file mode 100644
index 39ccda0b4d..0000000000
--- a/options/license/xlock
+++ /dev/null
@@ -1,14 +0,0 @@
-Copyright (c) 1990 by Sun Microsystems, Inc.
-Author: Patrick J. Naughton naughton@wind.sun.com
-
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose and without fee is hereby granted,
-provided that the above copyright notice appear in all copies and
-that both that copyright notice and this permission notice appear in
-supporting documentation.
-
-This file is provided AS IS with no warranties of any kind. The author
-shall have no liability with respect to the infringement of copyrights,
-trade secrets or any patents by this file or any part thereof. In no event
-will the author be liable for any lost revenue or profits or other special,
-indirect and consequential damages.
diff --git a/options/license/xpp b/options/license/xpp
deleted file mode 100644
index c36660a729..0000000000
--- a/options/license/xpp
+++ /dev/null
@@ -1,21 +0,0 @@
-LICENSE FOR THE Extreme! Lab PullParser
-
-Copyright (c) 2002 The Trustees of Indiana University. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1) All redistributions of source code must retain the above copyright notice, the list of authors in the original source code, this list of conditions and the disclaimer listed in this license;
-
-2) All redistributions in binary form must reproduce the above copyright notice, this list of conditions and the disclaimer listed in this license in the documentation and/or other materials provided with the distribution;
-
-3) Any documentation included with all redistributions must include the following acknowledgement:
-
- "This product includes software developed by the Indiana University Extreme! Lab. For further information please visit http://www.extreme.indiana.edu/"
-
-Alternatively, this acknowledgment may appear in the software itself, and wherever such third-party acknowledgments normally appear.
-
-4) The name "Indiana Univeristy" and "Indiana Univeristy Extreme! Lab" shall not be used to endorse or promote products derived from this software without prior written permission from Indiana University. For written permission, please contact http://www.extreme.indiana.edu/.
-
-5) Products derived from this software may not use "Indiana Univeristy" name nor may "Indiana Univeristy" appear in their name, without prior written permission of the Indiana University. Indiana University provides no reassurances that the source code provided does not infringe the patent or any other intellectual property rights of any other entity. Indiana University disclaims any liability to any recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise.
-
-LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE. LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION GENERATED USING SOFTWARE.
diff --git a/options/license/xzoom b/options/license/xzoom
deleted file mode 100644
index f312dedbc2..0000000000
--- a/options/license/xzoom
+++ /dev/null
@@ -1,12 +0,0 @@
-Copyright Itai Nahshon 1995, 1996.
-This program is distributed with no warranty.
-
-Source files for this program may be distributed freely.
-Modifications to this file are okay as long as:
- a. This copyright notice and comment are preserved and
- left at the top of the file.
- b. The man page is fixed to reflect the change.
- c. The author of this change adds his name and change
- description to the list of changes below.
-Executable files may be distributed with sources, or with
-exact location where the source code can be obtained.
diff --git a/options/license/zlib-acknowledgement b/options/license/zlib-acknowledgement
deleted file mode 100644
index cf95431f24..0000000000
--- a/options/license/zlib-acknowledgement
+++ /dev/null
@@ -1,15 +0,0 @@
-Copyright (c) 2002-2007 Charlie Poole
-Copyright (c) 2002-2004 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov
-Copyright (c) 2000-2002 Philip A. Craig
-
-This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment (see the following) in the product documentation is required.
-
- Portions Copyright (c) 2002-2007 Charlie Poole or Copyright (c) 2002-2004 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov or Copyright (c) 2000-2002 Philip A. Craig
-
-2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
-
-3. This notice may not be removed or altered from any source distribution.
diff --git a/options/locale/TRANSLATORS b/options/locale/TRANSLATORS
index e67255f2fb..4eee2b26c1 100644
--- a/options/locale/TRANSLATORS
+++ b/options/locale/TRANSLATORS
@@ -66,6 +66,7 @@ Piotr Orzechowski <piotr AT orzechowski DOT tech>
Richard Bukovansky <richard DOT bukovansky AT gmail DOT com>
Robert Nuske <robert DOT nuske AT web DOT de>
Robin Hübner <profan AT prfn DOT se>
+Ryo Hanafusa <ryo7gumi AT gmail DOT com>
SeongJae Park <sj38 DOT park AT gmail DOT com>
Thiago Avelino <thiago AT avelino DOT xxx>
Thomas Fanninger <gogs DOT thomas AT fanninger DOT at>
diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini
index beeb1dc3b8..17ee12af79 100644
--- a/options/locale/locale_cs-CZ.ini
+++ b/options/locale/locale_cs-CZ.ini
@@ -54,6 +54,7 @@ webauthn_reload=Znovu naÄíst
repository=Repozitář
organization=Organizace
mirror=Zrcadlo
+issue_milestone=Milník
new_repo=Nový repozitář
new_migrate=Nová migrace
new_mirror=Nové zrcadlo
@@ -145,6 +146,7 @@ confirm_delete_selected=Potvrdit odstranění všech vybraných položek?
name=Název
value=Hodnota
+readme=Readme
filter=Filtr
filter.clear=Vymazat filtr
@@ -243,6 +245,7 @@ license_desc=Vše je na <a target="_blank" rel="noopener noreferrer" href="%[1]s
[install]
install=Instalace
+installing_desc=Probíhá instalace, Äekejte prosím...
title=Výchozí konfigurace
docker_helper=Pokud spouÅ¡títe Gitea v Dockeru, pÅ™eÄtÄ›te si <a target="_blank" rel="noopener noreferrer" href="%s">dokumentaci</a>, než budete mÄ›nit jakákoliv nastavení.
require_db_desc=Gitea requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol).
@@ -382,6 +385,7 @@ show_only_public=Zobrazeny pouze veřejné
issues.in_your_repos=Ve vašich repozitářích
+
[explore]
repos=Repozitáře
users=Uživatelé
@@ -442,7 +446,6 @@ oauth_signup_submit=DokonÄit úÄet
oauth_signin_tab=Propojit s existujícím úÄtem
oauth_signin_title=PÅ™ihlaste se pro ověření propojeného úÄtu
oauth_signin_submit=Propojit úÄet
-oauth.signin.error=Došlo k chybě při zpracování žádosti o autorizaci. Pokud tato chyba přetrvává, obraťte se na správce webu.
oauth.signin.error.access_denied=Žádost o autorizaci byla zamítnuta.
oauth.signin.error.temporarily_unavailable=Autorizace se nezdaÅ™ila, protože ověřovací server je doÄasnÄ› nedostupný. Opakujte akci pozdÄ›ji.
oauth_callback_unable_auto_reg=Automatická registrace je povolena, ale OAuth2 poskytovatel %[1]s vrátil chybÄ›jící pole: %[2]s, nelze vytvoÅ™it úÄet automaticky, vytvoÅ™te úÄet nebo se pÅ™ipojte k úÄtu, nebo kontaktujte správce webu.
@@ -459,6 +462,7 @@ authorize_application=Autorizovat aplikaci
authorize_redirect_notice=Budete přesměrováni na %s, pokud autorizujete tuto aplikaci.
authorize_application_created_by=Tuto aplikaci vytvořil %s.
authorize_application_description=Pokud povolíte přístup, bude moci pÅ™istupovat a zapisovat do vÅ¡ech vaÅ¡ich informací o úÄtu vÄetnÄ› soukromých repozitářů a organizací.
+authorize_application_with_scopes=S rozsahy působnosti: %s
authorize_title=Autorizovat „%s“ pro přístup k vaÅ¡emu úÄtu?
authorization_failed=Autorizace selhala
authorization_failed_desc=Autorizace selhala, protože jsme detekovali neplatný požadavek. Kontaktujte prosím správce aplikace, kterou jste se pokoušeli autorizovat.
@@ -714,8 +718,6 @@ public_profile=Veřejný profil
biography_placeholder=Řekněte nám něco o sobě! (Můžete použít Markdown)
location_placeholder=Sdílejte svou přibližnou polohu s ostatními
profile_desc=Nastavte, jak bude váš profil zobrazen ostatním uživatelům. Vaše hlavní e-mailová adresa bude použita pro oznámení, obnovení hesla a operace Git.
-password_username_disabled=Nemáte oprávnění měnit jejich uživatelské jméno. Pro více informací kontaktujte svého administrátora.
-password_full_name_disabled=Nemáte oprávnění měnit jejich celé jméno. Pro více informací kontaktujte správce webu.
full_name=Celé jméno
website=Web
location=Místo
@@ -765,6 +767,7 @@ uploaded_avatar_not_a_image=Nahraný soubor není obrázek.
uploaded_avatar_is_too_big=Nahraný soubor (%d KiB) přesahuje maximální velikost (%d KiB).
update_avatar_success=Vaše avatar byl aktualizován.
update_user_avatar_success=Uživatelův avatar byl aktualizován.
+cropper_prompt=Před uložením můžete obrázek upravit. Upravený obrázek bude uložen jako PNG.
change_password=Aktualizovat heslo
old_password=Stávající heslo
@@ -905,7 +908,6 @@ delete_token_success=Token byl odstranÄ›n. Aplikace, které jej používají jiÅ
repo_and_org_access=Repozitář a přístup organizace
permissions_public_only=Pouze veřejnost
permissions_access_all=Vše (veřejné, soukromé a omezené)
-select_permissions=Vyberte oprávnění
permission_not_set=Není nastaveno
permission_no_access=Bez přístupu
permission_read=PÅ™eÄtené
@@ -1012,6 +1014,9 @@ new_repo_helper=Repozitář obsahuje vÅ¡echny projektové soubory, vÄetnÄ› hist
owner=Vlastník
owner_helper=NÄ›které organizace se nemusejí v seznamu zobrazit kvůli maximálnímu dosaženému poÄtu repozitářů.
repo_name=Název repozitáře
+repo_name_profile_public_hint=.profile je speciální repozitář, který můžete použít k pÅ™idání souboru README.md do svého veÅ™ejného profilu organizace, který je viditelný pro každého. UjistÄ›te se, že je veÅ™ejný, a pro zaÄátek jej inicializujte pomocí README v adresáři profilu.
+repo_name_profile_private_hint=.profile-private je speciální repozitář, který můžete použít k pÅ™idání souboru README.md do profilu Älena organizace, který je viditelný pouze pro Äleny organizace. UjistÄ›te se, že je soukromý, a pro zaÄátek jej inicializujte pomocí README v adresáři profilu.
+repo_name_helper=Dobrá jména repozitářů používají krátká, zapamatovatelná a unikátní klíÄová slova. Repozitář s názvem „.profile“ nebo „.profile-private“ lze použít k pÅ™idání README.md pro uživatelský/organizaÄní profil.
repo_size=Velikost repozitáře
template=Å ablona
template_select=Vyberte šablonu.
@@ -1030,6 +1035,8 @@ fork_to_different_account=RozÅ¡tÄ›pit na jiný úÄet
fork_visibility_helper=Viditelnost rozštěpeného repozitáře nemůže být změněna.
fork_branch=Větev, která má být klonována pro fork
all_branches=Všechny větve
+view_all_branches=Zobrazit všechny větve
+view_all_tags=Zobrazit vÅ¡echny znaÄky
fork_no_valid_owners=Tento repozitář nemůže být rozštěpen, protože neexistují žádní platní vlastníci.
fork.blocked_user=Nelze rozštěpit repozitář, protože jste blokováni majitelem repozitáře.
use_template=Použít tuto šablonu
@@ -1041,6 +1048,8 @@ generate_repo=Generovat repozitář
generate_from=Generovat z
repo_desc=Popis
repo_desc_helper=Zadejte krátký popis (volitelné)
+repo_no_desc=Nebyl uveden žádný popis
+repo_lang=Jazyky
repo_gitignore_helper=Vyberte šablony .gitignore.
repo_gitignore_helper_desc=Vyberte soubory, které nechcete sledovat ze seznamu Å¡ablon pro běžné jazyky. Typické artefakty generované nástroji pro sestavení každého jazyka jsou ve výchozím stavu souÄástí .gitignore.
issue_labels=Štítky úkolů
@@ -1102,10 +1111,9 @@ delete_preexisting_success=Smazány nepřijaté soubory v %s
blame_prior=Zobrazit blame před touto změnou
blame.ignore_revs=Ignorování revizí v <a href="%s">.git-blame-ignorerevs</a>. Klikněte zde <a href="%s">pro obejití</a> a zobrazení normálního pohledu blame.
blame.ignore_revs.failed=Nepodařilo se ignorovat revize v <a href="%s">.git-blame-ignore-revs</a>.
+user_search_tooltip=Zobrazí maximálně 30 uživatelů
-tree_path_not_found_commit=Cesta %[1]s v commitu %[2]s neexistuje
-tree_path_not_found_branch=Cesta %[1]s ve větvi %[2]s neexistuje
-tree_path_not_found_tag=Cesta %[1]s ve znaÄce %[2]s neexistuje
+tree_path_not_found=Cesta %[1]s neexistuje v %[2]s
transfer.accept=Přijmout převod
transfer.accept_desc=Převést do „%s“
@@ -1223,6 +1231,7 @@ create_new_repo_command=Vytvořit nový repozitář na příkazové řádce
push_exist_repo=Nahrání existujícího repozitáře z příkazové řádky
empty_message=Tento repozitář nemá žádný obsah.
broken_message=Data gitu, která jsou základem tohoto repozitáře, nelze Äíst. Kontaktujte správce této instance nebo smažte tento repositář.
+no_branch=Tento repozitář nemá žádné větve.
code=Zdrojový kód
code.desc=Přístup ke zdrojovým kódům, souborům, commitům a větvím.
@@ -1242,6 +1251,7 @@ labels=Štítky
org_labels_desc=Štítky na úrovni organizace, které mohou být použity se <strong>všemi repozitáři</strong> v rámci této organizace
org_labels_desc_manage=spravovat
+milestone=Milník
milestones=Milníky
commits=Commity
commit=Commit
@@ -1275,7 +1285,6 @@ file_copy_permalink=Kopírovat trvalý odkaz
view_git_blame=Zobrazit Git Blame
video_not_supported_in_browser=Váš prohlížeÄ nepodporuje znaÄku pro HTML5 video.
audio_not_supported_in_browser=Váš prohlížeÄ nepodporuje znaÄku pro HTML5 audio.
-stored_lfs=Uloženo pomocí Git LFS
symbolic_link=Symbolický odkaz
executable_file=Spustitelný soubor
vendored=Vendorováno
@@ -1321,7 +1330,6 @@ editor.update=Aktualizovat %s
editor.delete=Odstranit %s
editor.patch=Použít záplatu
editor.patching=Záplatování:
-editor.fail_to_apply_patch=Nelze použít záplatu „%s“
editor.new_patch=Nová záplata
editor.commit_message_desc=Přidat volitelný rozšířený popis…
editor.signoff_desc=Přidat Signed-off-by podpis přispěvatele na konec zprávy o commitu.
@@ -1339,8 +1347,6 @@ editor.branch_already_exists=Větev „%s“ již existuje v tomto repozitáři.
editor.directory_is_a_file=Jméno adresáře „%s“ je již použito jako jméno souboru v tomto repozitáři.
editor.file_is_a_symlink=`„%s“ je symbolický odkaz. Symbolické odkazy nemohou být upravovány ve webovém editoru`
editor.filename_is_a_directory=Jméno souboru „%s“ je již použito jako jméno adresáře v tomto repozitáři.
-editor.file_editing_no_longer_exists=Upravovaný soubor „%s“ již není souÄástí tohoto repozitáře.
-editor.file_deleting_no_longer_exists=Odstraňovaný soubor „%s“ již není souÄástí tohoto repozitáře.
editor.file_changed_while_editing=Obsah souboru byl zmÄ›nÄ›n od doby, kdy jste zaÄaly s úpravou. <a target="_blank" rel="noopener noreferrer" href="%s">KliknÄ›te zde</a>, abyste je zobrazili, nebo <strong>potvrÄte zmÄ›ny jeÅ¡tÄ› jednou</strong> pro jejich pÅ™epsání.
editor.file_already_exists=Soubor „%s“ již existuje v tomto repozitáři.
editor.commit_id_not_matching=ID commitu se neshoduje s ID, když jsi zaÄal/a s úpravami. Odevzdat do záplatové vÄ›tve a poté slouÄit.
@@ -1348,8 +1354,6 @@ editor.push_out_of_date=Nahrání se zdá být zastaralé.
editor.commit_empty_file_header=Odevzdat prázdný soubor
editor.commit_empty_file_text=Soubor, který se chystáte odevzdat, je prázdný. PokraÄovat?
editor.no_changes_to_show=Žádné změny k zobrazení.
-editor.fail_to_update_file=Nepodařilo se aktualizovat/vytvořit soubor „%s“.
-editor.fail_to_update_file_summary=Chybové hlášení:
editor.push_rejected_no_message=ZmÄ›na byla serverem zamítnuta bez zprávy. Prosím, zkontrolujte háÄky Gitu.
editor.push_rejected=ZmÄ›na byla serverem zamítnuta. Prosím, zkontrolujte háÄky Gitu.
editor.push_rejected_summary=Úplná zpráva o odmítnutí:
@@ -1364,6 +1368,7 @@ editor.require_signed_commit=Větev vyžaduje podepsaný commit
editor.cherry_pick=Cherry-pick %s na:
editor.revert=Vrátit %s na:
+
commits.desc=Procházet historii změn zdrojového kódu.
commits.commits=Commity
commits.no_commits=Žádné spoleÄné commity. „%s“ a „%s“ mají zcela odliÅ¡nou historii.
@@ -1517,9 +1522,9 @@ issues.filter_project=Projekt
issues.filter_project_all=Všechny projekty
issues.filter_project_none=Žádný projekt
issues.filter_assignee=Zpracovatel
-issues.filter_assginee_no_select=Všichni zpracovatelé
-issues.filter_assginee_no_assignee=Bez zpracovatele
issues.filter_poster=Autor
+issues.filter_user_placeholder=Hledat uživatele
+issues.filter_user_no_select=Všichni uživatelé
issues.filter_type=Typ
issues.filter_type.all_issues=Všechny úkoly
issues.filter_type.assigned_to_you=Přiřazené vám
@@ -1636,13 +1641,12 @@ issues.attachment.open_tab=`Klikněte pro zobrazení „%s“ v nové záložce`
issues.attachment.download=`Klikněte pro stažení „%s“`
issues.subscribe=Odebírat
issues.unsubscribe=Zrušit odběr
-issues.unpin_issue=Odepnout úkol
+issues.unpin=Odepnout
issues.max_pinned=Nemůžete připnout další úkoly
issues.pin_comment=připnuto %s
issues.unpin_comment=odepnul/a tento %s
issues.lock=Uzamknout konverzaci
issues.unlock=Odemknout konverzaci
-issues.lock.unknown_reason=Úkol nelze z neznámého důvodu uzamknout.
issues.lock_duplicate=Úkol nemůže být uzamÄený dvakrát.
issues.unlock_error=Nelze odemknout úkol, který je uzamÄený.
issues.lock_with_reason=uzamkl/a jako <strong>%s</strong> a omezil/a konverzaci na spolupracovníky %s
@@ -1664,12 +1668,25 @@ issues.delete.title=Smazat tento úkol?
issues.delete.text=Opravdu chcete tento úkol smazat? (Tím se trvale odstraní veškerý obsah. Pokud jej hodláte archivovat, zvažte raději jeho uzavření.)
issues.tracker=Sledování Äasu
-
+issues.timetracker_timer_start=Spustit ÄasovaÄ
+issues.timetracker_timer_stop=Zastavit ÄasovaÄ
+issues.timetracker_timer_discard=Zahodit ÄasovaÄ
+issues.timetracker_timer_manually_add=PÅ™idat Äas
+
+issues.time_estimate_set=Nastavit odhadovaný Äas
+issues.time_estimate_display=Odhad: %s
+issues.change_time_estimate_at=zmÄ›nil/a odhad Äasu na <b>%[1]s</b> %[2]s
+issues.remove_time_estimate_at=odstranil/a odhad Äasu %s
+issues.time_estimate_invalid=Formát odhadu Äasu je neplatný
+issues.start_tracking_history=zapoÄal/a práci %s
issues.tracker_auto_close=ÄŒasovaÄ se automaticky zastaví po zavÅ™ení tohoto úkolu
issues.tracking_already_started=`Již jste spustili sledování Äasu na <a href="%s">jiném úkolu</a>!`
+issues.stop_tracking_history=pracoval/a <b>%[1]s</b> %[2]s
issues.cancel_tracking_history=`zruÅ¡il/a sledování Äasu %s`
issues.del_time=Odstranit tento Äasový záznam
+issues.add_time_history=pÅ™idal/a strávený Äas <b>%[1]s</b> %[2]s
issues.del_time_history=`odstranil/a strávený Äas %s`
+issues.add_time_manually=PÅ™idat Äas ruÄnÄ›
issues.add_time_hours=Hodiny
issues.add_time_minutes=Minuty
issues.add_time_sum_to_small=Čas nebyl zadán.
@@ -1827,7 +1844,6 @@ pulls.add_prefix=Přidat prefix <strong>%s</strong>
pulls.remove_prefix=Odstranit prefix <strong>%s</strong>
pulls.data_broken=Tento pull request je rozbitý kvůli chybějícím informacím o rozštěpení.
pulls.files_conflicted=Tento pull request obsahuje změny, které kolidují s cílovou větví.
-pulls.is_checking=PrávÄ› probíhá kontrola konfliktů pÅ™i slouÄení. Zkuste to za chvíli.
pulls.is_ancestor=Tato vÄ›tev je již souÄástí cílové vÄ›tve. Není co slouÄit.
pulls.is_empty=Změny na této větvi jsou již na cílové větvi. Toto bude prázdný commit.
pulls.required_status_check_failed=Některé požadované kontroly nebyly úspěšné.
@@ -1897,12 +1913,10 @@ pulls.outdated_with_base_branch=Tato větev je zastaralá oproti základní vět
pulls.close=Zavřít pull request
pulls.closed_at=`uzavřel/a tento pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at=`znovuotevřel/a tento pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-pulls.cmd_instruction_hint=`Zobrazit <a class="show-instruction">instrukce příkazové řádky</a>.`
pulls.cmd_instruction_checkout_title=Checkout
pulls.cmd_instruction_checkout_desc=Z vašeho repositáře projektu se podívejte na novou větev a vyzkoušejte změny.
pulls.cmd_instruction_merge_title=SlouÄit
pulls.cmd_instruction_merge_desc=SluÄte zmÄ›ny a aktualizujte je na Gitea.
-pulls.cmd_instruction_merge_warning=Varování: Tato operace nemůže slouÄit požadavek na natažení, protože „autodetekce manuálních slouÄení“ nebyla povolena
pulls.clear_merge_message=Vymazat zprávu o slouÄení
pulls.clear_merge_message_hint=Vymazání zprávy o slouÄení odstraní pouze obsah zprávy a ponechá generované přídavky gitu jako "Co-AuthoreBy …".
@@ -1922,6 +1936,11 @@ pulls.delete.title=Odstranit tento pull request?
pulls.delete.text=Opravdu chcete tento pull request smazat? (Tím se trvale odstraní veškerý obsah. Pokud jej hodláte archivovat, zvažte raději jeho uzavření.)
pulls.recently_pushed_new_branches=Nahráli jste větev <strong>%[1]s</strong> %[2]s
+pulls.upstream_diverging_prompt_behind_1=Tato větev je %[1]d commit pozadu za %[2]s
+pulls.upstream_diverging_prompt_behind_n=Tato větev je %[1]d commitů pozadu za %[2]s
+pulls.upstream_diverging_prompt_base_newer=Hlavní větev %s má nové změny
+pulls.upstream_diverging_merge=Synchornizovat rozštěpení
+pulls.upstream_diverging_merge_confirm=Chcete slouÄit „%[1]s“ do „%[2]s“?
pull.deleted_branch=(odstraněno):%s
pull.agit_documentation=Prohlédněte si dokumentaci o AGit
@@ -2127,7 +2146,6 @@ settings.advanced_settings=PokroÄilá nastavení
settings.wiki_desc=Povolit Wiki repozitáře
settings.use_internal_wiki=Používat vestavěnou Wiki
settings.default_wiki_branch_name=Výchozí název větve Wiki
-settings.default_wiki_everyone_access=Výchozí přístupová práva pro přihlášené uživatele:
settings.failed_to_change_default_wiki_branch=Změna výchozí větve wiki se nezdařila.
settings.use_external_wiki=Používat externí Wiki
settings.external_wiki_url=URL externí Wiki
@@ -2256,8 +2274,6 @@ settings.hooks_desc=Webové háÄky automaticky vytvářejí dotazy HTTP POST na
settings.webhook_deletion=Odstranit webový háÄek
settings.webhook_deletion_desc=OdstranÄ›ní webového háÄku smaže jeho nastavení a historii doruÄení. PokraÄovat?
settings.webhook_deletion_success=Webový háÄek byl smazán.
-settings.webhook.test_delivery=Test doruÄitelnosti
-settings.webhook.test_delivery_desc=VyzkouÅ¡et tento webový háÄek pomocí faleÅ¡né události.
settings.webhook.test_delivery_desc_disabled=Chcete-li tento webový háÄek otestovat s faleÅ¡nou událostí, aktivujte ho.
settings.webhook.request=Požadavek
settings.webhook.response=OdpovÄ›Ä
@@ -2304,7 +2320,6 @@ settings.event_repository=Repozitář
settings.event_repository_desc=Repozitář vytvořen nebo smazán.
settings.event_header_issue=Události úkolů
settings.event_issues=Úkoly
-settings.event_issues_desc=Úkol otevřen, uzavřen, znovu otevřen nebo upraven.
settings.event_issue_assign=Úkol přiřazen
settings.event_issue_assign_desc=Úkol přiřazen nebo nepřiřazen.
settings.event_issue_label=Úkol oštítkován
@@ -2315,7 +2330,6 @@ settings.event_issue_comment=Komentář k úkolu
settings.event_issue_comment_desc=Komentář úkolu přidán, upraven nebo smazán.
settings.event_header_pull_request=Události pull requestu
settings.event_pull_request=Pull request
-settings.event_pull_request_desc=Pull request otevřen, uzavřen, znovu otevřen nebo upraven.
settings.event_pull_request_assign=Pull request přiřazen
settings.event_pull_request_assign_desc=Pull request přiřazen nebo nepřiřazen.
settings.event_pull_request_label=Pull request oštítkován
@@ -2546,7 +2560,6 @@ diff.commit=revize
diff.git-notes=Poznámky
diff.data_not_available=Rozdílový obsah není dostupný
diff.options_button=Možnosti rozdílového porovnání
-diff.show_diff_stats=Zobrazit statistiky
diff.download_patch=Stáhněte soubor záplaty
diff.download_diff=Stáhněte rozdílový soubor
diff.show_split_view=Rozdělené zobrazení
@@ -2595,6 +2608,9 @@ diff.image.overlay=Překrytí
diff.has_escaped=Tento řádek má skryté znaky Unicode
diff.show_file_tree=Zobrazit souborový strom
diff.hide_file_tree=Skrýt souborový strom
+diff.submodule_added=Submodul %[1]s přidán v %[2]s
+diff.submodule_deleted=Submodul %[1]s odebrán z %[2]s
+diff.submodule_updated=Submodul %[1]s aktualizován: %[2]s
releases.desc=Sledování verzí projektu a souborů ke stažení.
release.releases=Vydání
@@ -2604,6 +2620,7 @@ release.new_release=Nové vydání
release.draft=Koncept
release.prerelease=Předběžná verze
release.stable=Stabilní
+release.latest=Nejnovější
release.compare=Porovnat
release.edit=upravit
release.ahead.commits=<strong>%d</strong> revizí
@@ -2678,6 +2695,8 @@ branch.create_branch_operation=Vytvořit větev
branch.new_branch=Vytvořit novou větev
branch.new_branch_from=Vytvořit novou větev z „%s“
branch.renamed=Větev %s byla přejmenována na %s.
+branch.rename_default_or_protected_branch_error=Pouze administrátoři mohou přejmenovat výchozí nebo chráněné větve.
+branch.rename_protected_branch_failed=Tato větev je chráněna pravidly ochrany založenými na zástupném vzoru.
tag.create_tag=VytvoÅ™it znaÄku %s
tag.create_tag_operation=VytvoÅ™it znaÄku
@@ -2752,15 +2771,13 @@ settings.visibility.private_shortname=Soukromý
settings.update_settings=Upravit nastavení
settings.update_setting_success=Nastavení organizace bylo upraveno.
-settings.change_orgname_prompt=Poznámka: Změna názvu organizace také změní adresu URL vaší organizace a uvolní staré jméno této organizace.
-settings.change_orgname_redirect_prompt=Staré jméno bude přesměrovávat, dokud nebude znovu obsazeno.
+
+
settings.update_avatar_success=Avatar organizace byl aktualizován.
settings.delete=Smazat organizaci
settings.delete_account=Smazat tuto organizaci
settings.delete_prompt=Organizace bude trvale odstraněna. Tato změna <strong>NEMŮŽE</strong> být vrácena!
settings.confirm_delete_account=Potvrdit smazání
-settings.delete_org_title=Smazat organizaci
-settings.delete_org_desc=Tato organizace bude trvale smazána. PokraÄovat?
settings.hooks_desc=PÅ™idat webové háÄky, které budou spouÅ¡tÄ›ny pro <strong>vÅ¡echny repozitáře</strong> v této organizaci.
settings.labels_desc=Přidejte štítky, které mohou být použity pro úkoly <strong>všech repositářů</strong> v rámci této organizace.
@@ -2832,6 +2849,10 @@ teams.invite.title=Byli jste pozváni do týmu <strong>%s</strong> v organizaci
teams.invite.by=Pozvání od %s
teams.invite.description=Pro pÅ™ipojení k týmu kliknÄ›te na tlaÄítko níže.
+view_as_role=Zobrazit jako: %s
+view_as_public_hint=Prohlížíte README jako veřejný uživatel.
+view_as_member_hint=Prohlížíte README jako Älen této organizace.
+
[admin]
maintenance=Údržba
@@ -3049,7 +3070,7 @@ packages.size=Velikost
packages.published=Publikováno
defaulthooks=Výchozí webové háÄky
-defaulthooks.desc=Webové háÄky automaticky vytvářejí HTTP POST dotazy na server pÅ™i urÄitých Gitea událostech. Webové háÄky definované zde jsou výchozí a budou zkopírovány do vÅ¡ech nových repozitářů. PÅ™eÄtÄ›te si více v <a target="_blank" rel="noopener" href=%s">průvodci webovými háÄky</a>.
+defaulthooks.desc=Webové háÄky automaticky vytvářejí HTTP POST dotazy na server pÅ™i urÄitých Gitea událostech. Webové háÄky definované zde jsou výchozí a budou zkopírovány do vÅ¡ech nových repozitářů. PÅ™eÄtÄ›te si více v <a target="_blank" rel="noopener" href="%s">průvodci webovými háÄky</a>.
defaulthooks.add_webhook=PÅ™idat výchozí webový háÄek
defaulthooks.update_webhook=Aktualizovat výchozí webový háÄek
@@ -3203,8 +3224,6 @@ config.ssh_domain=Doména SSH serveru
config.ssh_port=Port
config.ssh_listen_port=Port pro naslouchání
config.ssh_root_path=Kořenová cesta
-config.ssh_key_test_path=Cesta testu klíÄů
-config.ssh_keygen_path=Cesta ke generátoru klíÄů ('ssh-keygen')
config.ssh_minimum_key_size_check=Kontrola minimální velikosti klíÄů
config.ssh_minimum_key_sizes=Minimální velikost klíÄů
@@ -3327,6 +3346,8 @@ monitor.previous=PÅ™edeÅ¡lý Äas spuÅ¡tÄ›ní
monitor.execute_times=Vykonání
monitor.process=Spuštěné procesy
monitor.stacktrace=Výpisy zásobníku
+monitor.trace=Trasovat
+monitor.performance_logs=Výkonnostní logy
monitor.processes_count=%d procesů
monitor.download_diagnosis_report=Stáhnout diagnosttickou zprávu
monitor.desc=Popis
@@ -3335,7 +3356,6 @@ monitor.execute_time=Doba provádění
monitor.last_execution_result=Výsledek
monitor.process.cancel=Zrušit proces
monitor.process.cancel_desc=Zrušení procesu může způsobit ztrátu dat
-monitor.process.cancel_notices=Zrušit: <strong>%s</strong>?
monitor.process.children=Potomek
monitor.queues=Fronty
@@ -3502,6 +3522,7 @@ versions=Verze
versions.view_all=Zobrazit všechny
dependency.id=ID
dependency.version=Verze
+search_in_external_registry=Hledat v %s
alpine.registry=Nastavte tento registr přidáním URL do <code>/etc/apk/repositories</code>:
alpine.registry.key=StáhnÄ›te si veÅ™ejný RSA klÃ­Ä registru do složky <code>/etc/apk/keys/</code> pro ověření podpisu indexu:
alpine.registry.info=Vyberte $branch a $repository ze seznamu níže.
@@ -3510,6 +3531,8 @@ alpine.repository=Informace o repozitáři
alpine.repository.branches=Větve
alpine.repository.repositories=Repozitáře
alpine.repository.architectures=Architektury
+arch.registry=Přidejte server se souvisejícím repozitářem a architekturou do <code>/etc/pacman.conf</code>:
+arch.install=Synchronizovat balíÄek s pacman:
arch.repository=Informace o repozitáři
arch.repository.repositories=Repozitáře
arch.repository.architectures=Architektury
@@ -3529,7 +3552,8 @@ conda.install=Pro instalaci balíÄku pomocí Conda spusÅ¥te následující pÅ™Ã
container.details.type=Typ obrazu
container.details.platform=Platforma
container.pull=Stáhněte obraz z příkazové řádky:
-container.digest=Výběr:
+container.images=Obrázky
+container.digest=Výběr
container.multi_arch=OS/architektura
container.layers=Vrstvy obrazů
container.labels=Štítky
@@ -3632,11 +3656,13 @@ owner.settings.chef.keypair.description=Pro autentizaci do registru Chef je zapo
secrets=Tajné klíÄe
description=Tejné klíÄe budou pÅ™edány urÄitým akcím a nelze je pÅ™eÄíst jinak.
none=Zatím zde nejsou žádné tajné klíÄe.
-creation=PÅ™idat tajný klíÄ
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Popis
creation.name_placeholder=nerozliÅ¡ovat velká a malá písmena, pouze alfanumerické znaky nebo podtržítka, nemohou zaÄínat na GITEA_ nebo GITHUB_
creation.value_placeholder=Vložte jakýkoliv obsah. Mezery na zaÄátku a konci budou vynechány.
-creation.success=Tajný klÃ­Ä â€ž%s“ byl pÅ™idán.
-creation.failed=NepodaÅ™ilo se pÅ™idat tajný klíÄ.
+
+
deletion=Odstranit tajný klíÄ
deletion.description=OdstranÄ›ní tajného klíÄe je trvalé a nelze ho vrátit zpÄ›t. PokraÄovat?
deletion.success=Tajný klÃ­Ä byl odstranÄ›n.
@@ -3692,6 +3718,7 @@ runners.status.active=Aktivní
runners.status.offline=Offline
runners.version=Verze
runners.reset_registration_token=Resetovat registraÄní token
+runners.reset_registration_token_confirm=Chcete zneplatnit stávající token a vygenerovat nový?
runners.reset_registration_token_success=RegistraÄní token runneru byl úspěšnÄ› obnoven
runs.all_workflows=Všechny pracovní postupy
@@ -3724,6 +3751,7 @@ workflow.not_found=Pracovní postup „%s“ nebyl nalezen.
workflow.run_success=Pracovní postup „%s“ proběhl úspěšně.
workflow.from_ref=Použít pracovní postup od
workflow.has_workflow_dispatch=Tento pracovní postup má spouÅ¡tÄ›Ä události workflow_dispatch.
+workflow.has_no_workflow_dispatch=Pracovní postup „%s“ memá workflow_dispatch spouÅ¡tÄ›Ä.
need_approval_desc=Potřebujete schválení pro spuštění pracovních postupů pro rozštěpený pull request.
@@ -3743,6 +3771,8 @@ variables.creation.success=Proměnná „%s“ byla přidána.
variables.update.failed=Úprava proměnné se nezdařila.
variables.update.success=Proměnná byla upravena.
+logs.always_auto_scroll=Vždy automaticky posouvat logy
+logs.always_expand_running=Vždy rozšířit běžící logy
[projects]
deleted.display_name=Odstraněný projekt
diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini
index ce2c43cb56..b989fc5473 100644
--- a/options/locale/locale_de-DE.ini
+++ b/options/locale/locale_de-DE.ini
@@ -54,6 +54,7 @@ webauthn_reload=Neu laden
repository=Repository
organization=Organisation
mirror=Mirror
+issue_milestone=Meilenstein
new_repo=Neues Repository
new_migrate=Neue Migration
new_mirror=Neuer Mirror
@@ -112,9 +113,11 @@ copy_type_unsupported=Dieser Dateityp kann nicht kopiert werden
write=Verfassen
preview=Vorschau
loading=Laden…
+files=Dateien
error=Fehler
error404=Die Seite, die Du versuchst aufzurufen, <strong>existiert nicht</strong> oder <strong>Du bist nicht berechtigt</strong>, diese anzusehen.
+error503=Der Server konnte deine Anfrage nicht abschließen. Bitte versuche es später erneut.
go_back=Zurück
invalid_data=Ungültige Daten: %v
@@ -127,6 +130,7 @@ pin=Anheften
unpin=Loslösen
artifacts=Artefakte
+expired=Abgelaufen
confirm_delete_artifact=Bist du sicher, dass du das Artefakt '%s' löschen möchtest?
archived=Archiviert
@@ -168,6 +172,10 @@ search=Suche ...
type_tooltip=Suchmodus
fuzzy=Ähnlich
fuzzy_tooltip=Ergebnisse einbeziehen, die dem Suchbegriff ähnlich sind
+words=Wörter
+words_tooltip=Nur Suchbegriffe einbeziehen, die den Suchbegriffen exakt entsprechen
+regexp=Regexp
+regexp_tooltip=Nur Suchbegriffe einbeziehen, die dem Regexp exakt entsprechen
exact=Exakt
exact_tooltip=Nur Suchbegriffe einbeziehen, die dem exakten Suchbegriff entsprechen
repo_kind=Repositories durchsuchen ...
@@ -244,6 +252,7 @@ license_desc=Hol dir den Code unter <a target="_blank" rel="noopener noreferrer"
[install]
install=Installation
+installing_desc=Wird jetzt installiert, bitte warten...
title=Erstkonfiguration
docker_helper=Wenn du Gitea in einem Docker-Container nutzt, lies bitte die <a target="_blank" rel="noopener noreferrer" href="%s">Dokumentation</a>, bevor du irgendwelche Einstellungen veränderst.
require_db_desc=Gitea benötigt MySQL, PostgreSQL, MSSQL, SQLite3 oder TiDB (MySQL-Protokoll).
@@ -383,6 +392,13 @@ show_only_public=Nur öffentliche anzeigen
issues.in_your_repos=Eigene Repositories
+guide_title=Keine Aktivität
+guide_desc=Du folgst derzeit keinen Repositories oder Benutzern, sodass es keinen Inhalt zum Anzeigen gibt. Du kannst Repositories oder Benutzer von Interesse über die untenstehenden Links erkunden.
+explore_repos=Repositories erkunden
+explore_users=Benutzer erkunden
+empty_org=Es gibt noch keine Organisationen.
+empty_repo=Es gibt noch keine Repositories.
+
[explore]
repos=Repositories
users=Benutzer
@@ -435,6 +451,7 @@ use_scratch_code=Einmalpasswort verwenden
twofa_scratch_used=Du hast dein Einmalpasswort verwendet. Du wurdest zu den Einstellung der Zwei-Faktor-Authentifizierung umgeleitet, dort kannst du dein Gerät abmelden oder ein neues Einmalpasswort erzeugen.
twofa_passcode_incorrect=Ungültige PIN. Wenn du dein Gerät verloren hast, verwende dein Einmalpasswort.
twofa_scratch_token_incorrect=Das Einmalpasswort ist falsch.
+twofa_required=Du musst die Zwei-Faktor-Authentifizierung einrichten, um Zugriff auf die Repositories zu erhalten, oder versuche dich erneut anzumelden.
login_userpass=Anmelden
login_openid=OpenID
oauth_signup_tab=Neues Konto registrieren
@@ -443,7 +460,7 @@ oauth_signup_submit=Konto vervollständigen
oauth_signin_tab=Mit existierendem Konto verbinden
oauth_signin_title=Anmelden um verbundenes Konto zu autorisieren
oauth_signin_submit=Konto verbinden
-oauth.signin.error=Beim Verarbeiten der Autorisierungsanfrage ist ein Fehler aufgetreten. Wenn dieser Fehler weiterhin besteht, wende dich bitte an deinen Administrator.
+oauth.signin.error.general=Beim Verarbeiten der Autorisierungsanfrage ist ein Fehler aufgetreten: %s. Wenn dieser Fehler weiterhin besteht, wende dich bitte an den Administrator.
oauth.signin.error.access_denied=Die Autorisierungsanfrage wurde abgelehnt.
oauth.signin.error.temporarily_unavailable=Autorisierung fehlgeschlagen, da der Authentifizierungsserver vorübergehend nicht verfügbar ist. Bitte versuch es später erneut.
oauth_callback_unable_auto_reg=Automatische Registrierung ist aktiviert, aber der OAuth2-Provider %[1]s hat fehlende Felder zurückgegeben: %[2]s, kann den Account nicht automatisch erstellen. Bitte erstelle oder verbinde einen Account oder kontaktieren den Administrator.
@@ -908,11 +925,13 @@ delete_token_success=Der Zugriffstoken wurde gelöscht. Anwendungen die diesen T
repo_and_org_access=Repository- und Organisationszugriff
permissions_public_only=Nur öffentlich
permissions_access_all=Alle (öffentlich, privat und begrenzt)
-select_permissions=Berechtigungen auswählen
permission_not_set=Nicht festgelegt
permission_no_access=Kein Zugriff
permission_read=Lesen
permission_write=Lesen und Schreiben
+permission_anonymous_read=Anonymes Lesen
+permission_everyone_read=Alle lesen
+permission_everyone_write=Alle schreiben
access_token_desc=Ausgewählte Token-Berechtigungen beschränken die Authentifizierung auf die entsprechenden <a %s>API</a>-Routen. Lies die <a %s>Dokumentation</a> für mehr Informationen.
at_least_one_permission=Du musst mindestens eine Berechtigung auswählen, um ein Token zu erstellen
permissions_list=Berechtigungen:
@@ -1015,6 +1034,9 @@ new_repo_helper=Ein Repository enthält alle Projektdateien, einschließlich des
owner=Besitzer
owner_helper=Einige Organisationen könnten in der Dropdown-Liste nicht angezeigt werden, da die Anzahl an Repositories begrenzt ist.
repo_name=Repository-Name
+repo_name_profile_public_hint=.profile ist ein spezielles Repository, mit dem du die README.md zu deinem öffentlichen Organisationsprofil hinzufügen kannst, das für jeden sichtbar ist. Stelle sicher, dass es öffentlich ist und initialisiere es mit einer README im Profilverzeichnis, um loszulegen.
+repo_name_profile_private_hint=.profile ist ein spezielles Repository, mit dem du die README.md zu deinem privaten Organisationsprofil hinzufügen kannst, das nur für Organisationsmitglieder sichtbar ist. Stelle sicher, dass es privat ist und initialisiere es mit einer README im Profilverzeichnis, um loszulegen.
+repo_name_helper=Ein guter Repository-Name besteht normalerweise aus kurzen, unvergesslichen und einzigartigen Schlagwörtern. Ein Repository namens ".profile" or ".profile-private" kann verwendet werden, um die README.md auf dem Benutzer- oder Organisationsprofil anzuzeigen.
repo_size=Repository-Größe
template=Template
template_select=Vorlage auswählen
@@ -1111,9 +1133,7 @@ blame.ignore_revs=Revisionen in <a href="%s">.git-blame-ignore-revs</a> werden i
blame.ignore_revs.failed=Fehler beim Ignorieren der Revisionen in <a href="%s">.git-blame-ignore-revs</a>.
user_search_tooltip=Zeigt maximal 30 Benutzer
-tree_path_not_found_commit=Pfad %[1]s existiert nicht in Commit%[2]s
-tree_path_not_found_branch=Pfad %[1]s existiert nicht in Branch %[2]s
-tree_path_not_found_tag=Pfad %[1]s existiert nicht in Tag %[2]s
+tree_path_not_found=Pfad %[1]s existiert nicht in %[2]s
transfer.accept=Übertragung Akzeptieren
transfer.accept_desc=`Übertragung nach "%s"`
@@ -1124,6 +1144,7 @@ transfer.no_permission_to_reject=Du hast keine Berechtigung, diesen Transfer abz
desc.private=Privat
desc.public=Öffentlich
+desc.public_access=Öffentlicher Zugriff
desc.template=Template
desc.internal=Intern
desc.archived=Archiviert
@@ -1231,6 +1252,7 @@ create_new_repo_command=Erstelle ein neues Repository von der Kommandozeile aus
push_exist_repo=Bestehendes Repository via Kommandozeile pushen
empty_message=Dieses Repository hat keinen Inhalt.
broken_message=Die Git-Daten, die diesem Repository zugrunde liegen, können nicht gelesen werden. Kontaktiere den Administrator deiner Instanz oder lösche dieses Repository.
+no_branch=Dieses Repository hat keine Branches.
code=Code
code.desc=Zugriff auf Quellcode, Dateien, Commits und Branches.
@@ -1250,6 +1272,7 @@ labels=Label
org_labels_desc=Labels der Organisationsebene, die mit <strong>allen Repositories</strong> in dieser Organisation verwendet werden können
org_labels_desc_manage=verwalten
+milestone=Meilenstein
milestones=Meilensteine
commits=Commits
commit=Commit
@@ -1283,7 +1306,6 @@ file_copy_permalink=Permalink kopieren
view_git_blame=Git Blame ansehen
video_not_supported_in_browser=Dein Browser unterstützt das HTML5 'video'-Tag nicht.
audio_not_supported_in_browser=Dein Browser unterstützt den HTML5 'audio'-Tag nicht.
-stored_lfs=Gespeichert mit Git LFS
symbolic_link=Softlink
executable_file=Ausführbare Datei
vendored=Vendor
@@ -1329,7 +1351,6 @@ editor.update=%s aktualisiert
editor.delete=%s gelöscht
editor.patch=Patch anwenden
editor.patching=Patche:
-editor.fail_to_apply_patch=Patch "%s" nicht anwendbar
editor.new_patch=Neuer Patch
editor.commit_message_desc=Eine ausführlichere (optionale) Beschreibung hinzufügen…
editor.signoff_desc=Am Ende der Commit Nachricht einen Signed-off-by Anhang vom Committer hinzufügen.
@@ -1342,13 +1363,13 @@ editor.new_branch_name_desc=Neuer Branchname…
editor.cancel=Abbrechen
editor.filename_cannot_be_empty=Der Dateiname darf nicht leer sein.
editor.filename_is_invalid=Ungültiger Dateiname: "%s".
+editor.commit_email=Commit-E-Mail-Adresse
+editor.invalid_commit_email=Die E-Mail-Adresse für den Commit ist ungültig.
editor.branch_does_not_exist=Branch "%s" existiert nicht in diesem Repository.
editor.branch_already_exists=Branch "%s" existiert bereits in diesem Repository.
editor.directory_is_a_file=Der Verzeichnisname "%s" wird bereits als Dateiname in diesem Repository verwendet.
editor.file_is_a_symlink=`"%s" ist ein symbolischer Link. Symbolische Links können mit dem Web-Editor nicht bearbeitet werden`
editor.filename_is_a_directory=Der Dateiname "%s" wird bereits als Verzeichnisname in diesem Repository verwendet.
-editor.file_editing_no_longer_exists=Die bearbeitete Datei "%s" existiert nicht mehr in diesem Repository.
-editor.file_deleting_no_longer_exists=Die zu löschende Datei "%s" existiert nicht mehr in diesem Repository.
editor.file_changed_while_editing=Der Inhalt der Datei hat sich seit dem Beginn der Bearbeitung geändert. <a target="_blank" rel="noopener noreferrer" href="%s">Hier klicken</a>, um die Änderungen anzusehen, oder <strong>Änderungen erneut comitten</strong>, um sie zu überschreiben.
editor.file_already_exists=Eine Datei mit dem Namen '%s' existiert bereits in diesem Repository.
editor.commit_id_not_matching=Die Commit-ID stimmt nicht mit der ID überein, bei welcher du mit der Bearbeitung begonnen hast. Commite in einen Patch-Branch und merge daraufhin.
@@ -1356,8 +1377,6 @@ editor.push_out_of_date=Der Push scheint veraltet zu sein.
editor.commit_empty_file_header=Leere Datei committen
editor.commit_empty_file_text=Die Datei, die du commiten willst, ist leer. Fortfahren?
editor.no_changes_to_show=Keine Änderungen vorhanden.
-editor.fail_to_update_file=Fehler beim Aktualisieren/Erstellen der Datei "%s".
-editor.fail_to_update_file_summary=Fehlermeldung:
editor.push_rejected_no_message=Die Änderung wurde vom Server ohne Nachricht abgelehnt. Bitte überprüfe die Git Hooks.
editor.push_rejected=Die Änderung wurde vom Server abgelehnt. Bitte überprüfe die Git Hooks.
editor.push_rejected_summary=Vollständige Ablehnungsmeldung:
@@ -1372,6 +1391,7 @@ editor.require_signed_commit=Branch erfordert einen signierten Commit
editor.cherry_pick=Cherry-Picke %s von:
editor.revert=%s zurücksetzen auf:
+
commits.desc=Durchsuche die Quellcode-Änderungshistorie.
commits.commits=Commits
commits.no_commits=Keine gemeinsamen Commits. "%s" und "%s" haben vollständig unterschiedliche Historien.
@@ -1390,6 +1410,7 @@ commits.signed_by_untrusted_user_unmatched=Signiert von nicht vertrauenswürdige
commits.gpg_key_id=GPG-Schlüssel-ID
commits.ssh_key_fingerprint=SSH-Key-Fingerabdruck
commits.view_path=An diesem Punkt im Verlauf anzeigen
+commits.view_file_diff=Änderungen an dieser Datei in diesem Commit anzeigen
commit.operations=Operationen
commit.revert=Zurücksetzen
@@ -1450,6 +1471,8 @@ issues.filter_milestones=Meilenstein filtern
issues.filter_projects=Projekt filtern
issues.filter_labels=Label filtern
issues.filter_reviewers=Reviewer filtern
+issues.filter_no_results=Keine Ergebnisse
+issues.filter_no_results_placeholder=Versuche, deine Suchfilter anzupassen.
issues.new=Neues Issue
issues.new.title_empty=Der Titel kann nicht leer sein
issues.new.labels=Label
@@ -1525,13 +1548,14 @@ issues.filter_project=Projekt
issues.filter_project_all=Alle Projekte
issues.filter_project_none=Kein Projekt
issues.filter_assignee=Zuständig
-issues.filter_assginee_no_select=Alle Zuständigen
-issues.filter_assginee_no_assignee=Niemand zuständig
+issues.filter_assignee_no_assignee=Niemandem zugewiesen
+issues.filter_assignee_any_assignee=Jemandem zugewiesen
issues.filter_poster=Autor
issues.filter_user_placeholder=Benutzer suchen
issues.filter_user_no_select=Alle Benutzer
issues.filter_type=Typ
issues.filter_type.all_issues=Alle Issues
+issues.filter_type.all_pull_requests=Alle Pull-Requests
issues.filter_type.assigned_to_you=Dir zugewiesen
issues.filter_type.created_by_you=Von dir erstellt
issues.filter_type.mentioning_you=Hat dich erwähnt
@@ -1623,12 +1647,15 @@ issues.save=Speichern
issues.label_title=Labelname
issues.label_description=Labelbeschreibung
issues.label_color=Labelfarbe
+issues.label_color_invalid=Ungültige Farbe
issues.label_exclusive=Exklusiv
issues.label_archive=Label archivieren
issues.label_archived_filter=Archivierte Labels anzeigen
issues.label_archive_tooltip=Archivierte Labels werden bei der Suche nach Label standardmäßig von den Vorschlägen ausgeschlossen.
issues.label_exclusive_desc=Nenne das Label <code>Bereich/Element</code> um es gegenseitig ausschließend mit anderen <code>Bereich/</code> Labels zu machen.
issues.label_exclusive_warning=Alle im Konflikt stehenden Labels werden beim Bearbeiten der Labels eines Issues oder eines Pull-Requests entfernt.
+issues.label_exclusive_order=Sortierreihenfolge
+issues.label_exclusive_order_tooltip=Exklusive Labels im gleichen Bereich werden nach dieser numerischen Reihenfolge sortiert.
issues.label_count=%d Label
issues.label_open_issues=%d offene Issues
issues.label_edit=Bearbeiten
@@ -1646,13 +1673,12 @@ issues.attachment.open_tab=`Klicken, um „%s“ in einem neuen Tab zu öffnen`
issues.attachment.download=`Klicken, um „%s“ herunterzuladen`
issues.subscribe=Abonnieren
issues.unsubscribe=Abbestellen
-issues.unpin_issue=Issue abheften
+issues.unpin=Loslösen
issues.max_pinned=Du kannst keine weiteren Issues anheften
issues.pin_comment=hat das %s angeheftet
issues.unpin_comment=hat das %s abgeheftet
issues.lock=Diskussion sperren
issues.unlock=Diskussion entsperren
-issues.lock.unknown_reason=Es ist nicht möglich, einen Issue mit unbekanntem Grund zu sperren.
issues.lock_duplicate=Eine Diskussion kann nicht mehrfach gesperrt werden.
issues.unlock_error=Es ist nicht möglich, einen nicht gesperrten Issue zu entsperren.
issues.lock_with_reason=gesperrt als <strong>%s</strong> und Diskussion auf Mitarbeiter beschränkt %s
@@ -1681,16 +1707,18 @@ issues.timetracker_timer_manually_add=Zeit hinzufügen
issues.time_estimate_set=Geschätzte Zeit festlegen
issues.time_estimate_display=Schätzung: %s
-issues.change_time_estimate_at=Zeitschätzung geändert zu <b>%s</b> %s
+issues.change_time_estimate_at=Zeitschätzung geändert zu <b>%[1]s</b> %[2]s
issues.remove_time_estimate_at=Zeitschätzung %s entfernt
issues.time_estimate_invalid=Format der Zeitschätzung ist ungültig
issues.start_tracking_history=hat die Zeiterfassung %s gestartet
issues.tracker_auto_close=Der Timer wird automatisch gestoppt, wenn dieser Issue geschlossen wird
issues.tracking_already_started=`Du hast die Zeiterfassung bereits in <a href="%s">diesem Issue</a> gestartet!`
-issues.stop_tracking_history=hat für <b>%s</b> gearbeitet %s
+issues.stop_tracking=Timer stoppen
+issues.stop_tracking_history=hat für <b>%[1]s</b> %[2]s gearbeitet
+issues.cancel_tracking=Verwerfen
issues.cancel_tracking_history=`hat die Zeiterfassung %s abgebrochen`
issues.del_time=Diese Zeiterfassung löschen
-issues.add_time_history=hat <b>%s</b> gearbeitete Zeit hinzugefügt %s
+issues.add_time_history=hat <b>%[1]s</b> %[2]s gearbeitete Zeit hinzugefügt
issues.del_time_history=`hat %s gearbeitete Zeit gelöscht`
issues.add_time_manually=Zeit manuell hinzufügen
issues.add_time_hours=Stunden
@@ -1920,12 +1948,11 @@ pulls.outdated_with_base_branch=Dieser Branch enthält nicht die neusten Commits
pulls.close=Pull-Request schließen
pulls.closed_at=`hat diesen Pull-Request <a id="%[1]s" href="#%[1]s">%[2]s</a> geschlossen`
pulls.reopened_at=`hat diesen Pull-Request <a id="%[1]s" href="#%[1]s">%[2]s</a> wieder geöffnet`
-pulls.cmd_instruction_hint=`Zeige <a class="show-instruction">Kommandozeilenanweisungen</a>.`
+pulls.cmd_instruction_hint=Zeige Kommandozeilenanweisungen
pulls.cmd_instruction_checkout_title=Checkout
pulls.cmd_instruction_checkout_desc=Wechsle auf einen neuen Branch in deinem lokalen Repository und teste die Änderungen.
pulls.cmd_instruction_merge_title=Mergen
pulls.cmd_instruction_merge_desc=Die Änderungen mergen und auf Gitea aktualisieren.
-pulls.cmd_instruction_merge_warning=Warnung: Dieser Vorgang kann den Pull-Request nicht mergen, da "manueller Merge" nicht aktiviert wurde
pulls.clear_merge_message=Merge-Nachricht löschen
pulls.clear_merge_message_hint=Das Löschen der Merge-Nachricht wird nur den Inhalt der Commit-Nachricht entfernen und generierte Git-Trailer wie "Co-Authored-By …" erhalten.
@@ -1949,6 +1976,7 @@ pulls.upstream_diverging_prompt_behind_1=Dieser Branch ist %[1]d Commit hinter %
pulls.upstream_diverging_prompt_behind_n=Dieser Branch ist %[1]d Commits hinter %[2]s
pulls.upstream_diverging_prompt_base_newer=Der Basis-Branch %s hat neue Änderungen
pulls.upstream_diverging_merge=Fork synchronisieren
+pulls.upstream_diverging_merge_confirm=Möchtest du „%[1]s“ in „%[2]s“ mergen?
pull.deleted_branch=(gelöscht):%s
pull.agit_documentation=Dokumentation zu AGit durchschauen
@@ -2108,6 +2136,12 @@ contributors.contribution_type.deletions=Löschungen
settings=Einstellungen
settings.desc=In den Einstellungen kannst du die Einstellungen des Repositories anpassen
settings.options=Repository
+settings.public_access=Öffentlicher Zugriff
+settings.public_access_desc=Konfiguriere die Zugriffsrechte öffentlicher Besucher, um die Standardwerte dieses Repositorys zu überschreiben.
+settings.public_access.docs.not_set=Nicht gesetzt: keine zusätzliche öffentliche Zugriffsberechtigung. Die Berechtigung der Besucher folgt den Sichtbarkeits- und Mitgliedsberechtigungen des Repositorys.
+settings.public_access.docs.anonymous_read=Anonymes Lesen: Nicht angemeldete Benutzer können mit Leseberechtigung auf die Einheit zugreifen.
+settings.public_access.docs.everyone_read=Alle Lesen: Alle angemeldeten Benutzer können mit Leseberechtigung auf die Einheit zugreifen. Leseberechtigung für Issues/Pull-Request-Einheiten bedeutet auch, dass Benutzer neue Issues/Pull-Requests erstellen können.
+settings.public_access.docs.everyone_write=Alle Schreiben: Alle angemeldeten Benutzer haben Schreibrechte auf die Einheit. Nur die Wiki-Einheit unterstützt diese Berechtigung.
settings.collaboration=Mitarbeiter
settings.collaboration.admin=Administrator
settings.collaboration.write=Schreibrechte
@@ -2154,7 +2188,6 @@ settings.advanced_settings=Erweiterte Einstellungen
settings.wiki_desc=Repository-Wiki aktivieren
settings.use_internal_wiki=Eingebautes Wiki verwenden
settings.default_wiki_branch_name=Standardbezeichnung für Wiki-Branch
-settings.default_wiki_everyone_access=Standard-Zugriffsberechtigung für angemeldete Benutzer:
settings.failed_to_change_default_wiki_branch=Das Ändern des Standard-Wiki-Branches ist fehlgeschlagen.
settings.use_external_wiki=Externes Wiki verwenden
settings.external_wiki_url=Externe Wiki-URL
@@ -2283,8 +2316,6 @@ settings.hooks_desc=Webhooks senden bei bestimmten Gitea-Events automatisch „H
settings.webhook_deletion=Webhook löschen
settings.webhook_deletion_desc=Das Entfernen eines Webhooks löscht seine Einstellungen und Zustellungsverlauf. Fortfahren?
settings.webhook_deletion_success=Webhook wurde entfernt.
-settings.webhook.test_delivery=Senden testen
-settings.webhook.test_delivery_desc=Teste diesen Webhook mit einem Fake-Event.
settings.webhook.test_delivery_desc_disabled=Um diesen Webhook mit einem Fake-Event zu testen, aktiviere ihn.
settings.webhook.request=Anfrage
settings.webhook.response=Antwort
@@ -2322,6 +2353,8 @@ settings.event_fork=Fork
settings.event_fork_desc=Repository geforkt.
settings.event_wiki=Wiki
settings.event_wiki_desc=Wiki-Seite erstellt, umbenannt, bearbeitet oder gelöscht.
+settings.event_statuses=Status
+settings.event_statuses_desc=Commit-Status von der API aktualisiert.
settings.event_release=Release
settings.event_release_desc=Release in einem Repository veröffentlicht, aktualisiert oder gelöscht.
settings.event_push=Push
@@ -2331,7 +2364,6 @@ settings.event_repository=Repository
settings.event_repository_desc=Repository erstellt oder gelöscht.
settings.event_header_issue=Issue Ereignisse
settings.event_issues=Issues
-settings.event_issues_desc=Issue geöffnet, geschlossen, wieder geöffnet oder bearbeitet.
settings.event_issue_assign=Issue zugewiesen
settings.event_issue_assign_desc=Issue zugewiesen oder Zuweisung entfernt.
settings.event_issue_label=Issue mit Label versehen
@@ -2342,7 +2374,6 @@ settings.event_issue_comment=Issue-Kommentar
settings.event_issue_comment_desc=Issue-Kommentar angelegt, geändert oder gelöscht.
settings.event_header_pull_request=Pull-Request-Ereignisse
settings.event_pull_request=Pull-Request
-settings.event_pull_request_desc=Pull-Request geöffnet, geschlossen, wieder geöffnet oder bearbeitet.
settings.event_pull_request_assign=Pull-Request zugewiesen
settings.event_pull_request_assign_desc=Pull-Request zugewiesen oder Zuweisung entfernt.
settings.event_pull_request_label=Pull-Request mit Label versehen
@@ -2359,6 +2390,9 @@ settings.event_pull_request_review_request=Überprüfung des Pull-Requests angef
settings.event_pull_request_review_request_desc=Überprüfung des Pull-Requests angefragt oder die Anfrage entfernt.
settings.event_pull_request_approvals=Zustimmungen zum Pull-Request
settings.event_pull_request_merge=Pull-Request-Merge
+settings.event_header_workflow=Workflow-Ereignisse
+settings.event_workflow_job=Workflow-Jobs
+settings.event_workflow_job_desc=Gitea Actions Workflow Job in Warteschlange, wartend, in Bearbeitung oder abgeschlossen.
settings.event_package=Paket
settings.event_package_desc=Paket wurde in einem Repository erstellt oder gelöscht.
settings.branch_filter=Branch-Filter
@@ -2573,7 +2607,6 @@ diff.commit=Commit
diff.git-notes=Hinweise
diff.data_not_available=Keine Diff-Daten verfügbar
diff.options_button=Diff-Optionen
-diff.show_diff_stats=Statistiken anzeigen
diff.download_patch=Patch-Datei herunterladen
diff.download_diff=Vergleichs-Datei herunterladen
diff.show_split_view=Geteilte Ansicht
@@ -2622,6 +2655,9 @@ diff.image.overlay=Überlagert
diff.has_escaped=Diese Zeile enthält versteckte Unicode-Zeichen
diff.show_file_tree=Dateibaum anzeigen
diff.hide_file_tree=Dateibaum ausblenden
+diff.submodule_added=Submodul %[1]s hinzugefügt bei %[2]s
+diff.submodule_deleted=Submodul %[1]s gelöscht von %[2]s
+diff.submodule_updated=Submodul %[1]s aktualisiert: %[2]s
releases.desc=Behalte den Überblick über Versionen und Downloads.
release.releases=Releases
@@ -2692,6 +2728,7 @@ branch.restore_success=Branch "%s" wurde wiederhergestellt.
branch.restore_failed=Wiederherstellung vom Branch "%s" gescheitert.
branch.protected_deletion_failed=Branch "%s" ist geschützt und kann nicht gelöscht werden.
branch.default_deletion_failed=Branch "%s" kann nicht gelöscht werden, da dieser Branch der Standard-Branch ist.
+branch.default_branch_not_exist=Standardzweig „%s“ existiert nicht.
branch.restore=Branch "%s" wiederherstellen
branch.download=Branch "%s" herunterladen
branch.rename=Branch "%s" umbenennen
@@ -2706,6 +2743,8 @@ branch.create_branch_operation=Branch erstellen
branch.new_branch=Neue Branch erstellen
branch.new_branch_from=Neuen Branch von "%s" erstellen
branch.renamed=Branch %s wurde in %s umbenannt.
+branch.rename_default_or_protected_branch_error=Nur Administratoren können Standard- oder geschützte Branches umbenennen.
+branch.rename_protected_branch_failed=Dieser Branch ist durch globale Schutzregeln geschützt.
tag.create_tag=Tag %s erstellen
tag.create_tag_operation=Tag erstellen
@@ -2780,15 +2819,13 @@ settings.visibility.private_shortname=Privat
settings.update_settings=Einstellungen speichern
settings.update_setting_success=Organisationseinstellungen wurden aktualisiert.
-settings.change_orgname_prompt=Hinweis: Das Ändern des Organisationsnamens wird auch die URL deiner Organisation ändern und den alten Namen freigeben.
-settings.change_orgname_redirect_prompt=Der alte Name wird weiterleiten, bis er wieder beansprucht wird.
+
+
settings.update_avatar_success=Der Organisationsavatar wurde aktualisiert.
settings.delete=Organisation löschen
settings.delete_account=Diese Organisation löschen
settings.delete_prompt=Die Organisation wird dauerhaft gelöscht. Dies <strong>KANN NICHT</strong> rückgängig gemacht werden!
settings.confirm_delete_account=Löschen bestätigen
-settings.delete_org_title=Organisation löschen
-settings.delete_org_desc=Diese Organisation wird dauerhaft gelöscht. Fortfahren?
settings.hooks_desc=Webhooks hinzufügen, die für <strong>alle</strong> Repositories dieser Organisation ausgelöst werden.
settings.labels_desc=Labels hinzufügen, die für <strong>alle Repositories</strong> dieser Organisation genutzt werden können.
@@ -2860,6 +2897,18 @@ teams.invite.title=Du wurdest eingeladen, dem Team <strong>%s</strong> in der Or
teams.invite.by=Von %s eingeladen
teams.invite.description=Bitte klicke auf die folgende Schaltfläche, um dem Team beizutreten.
+view_as_role=Ansehen als: %s
+view_as_public_hint=Du siehst die README als ein öffentlicher Benutzer.
+view_as_member_hint=Du siehst die README als ein Mitglied dieser Organisation.
+
+worktime=Arbeitszeit
+worktime.date_range_start=Startdatum
+worktime.date_range_end=Enddatum
+worktime.query=Abfrage
+worktime.time=Zeit
+worktime.by_repositories=Nach Repositories
+worktime.by_milestones=Nach Meilensteinen
+worktime.by_members=Nach Mitgliedern
[admin]
maintenance=Wartung
@@ -3180,7 +3229,7 @@ auths.tip.oauth2_provider=OAuth2-Anbieter
auths.tip.bitbucket=Registriere einen neuen OAuth-Consumer unter %s und füge die Berechtigung 'Account' - 'Read' hinzu
auths.tip.nextcloud=Registriere über das "Settings -> Security -> OAuth 2.0 client"-Menü einen neuen "OAuth consumer" auf der Nextcloud-Instanz
auths.tip.dropbox=Erstelle eine neue App auf %s
-auths.tip.facebook=Erstelle eine neue Anwendung auf %s und füge das Produkt "Facebook Login“ hinzu
+auths.tip.facebook=Erstelle eine neue Anwendung auf %s und füge das Produkt „Facebook Login“ hinzu
auths.tip.github=Erstelle unter %s eine neue OAuth-Anwendung
auths.tip.gitlab_new=Erstelle eine neue Anwendung unter %s
auths.tip.google_plus=Du erhältst die OAuth2-Client-Zugangsdaten in der Google-API-Konsole unter %s
@@ -3231,8 +3280,6 @@ config.ssh_domain=SSH-Server-Domain
config.ssh_port=Port
config.ssh_listen_port=Listen-Port
config.ssh_root_path=Wurzelverzeichnis
-config.ssh_key_test_path=Schlüssel-Test-Pfad
-config.ssh_keygen_path=Keygen-Pfad („ssh-keygen“)
config.ssh_minimum_key_size_check=Prüfung der Mindestschlüssellänge
config.ssh_minimum_key_sizes=Mindestschlüssellängen
@@ -3355,6 +3402,8 @@ monitor.previous=Letzte Ausführung
monitor.execute_times=Ausführungen
monitor.process=Laufende Prozesse
monitor.stacktrace=Stacktraces
+monitor.trace=Ablaufverfolgung
+monitor.performance_logs=Leistungsprotokolle
monitor.processes_count=%d Prozesse
monitor.download_diagnosis_report=Diagnosebericht herunterladen
monitor.desc=Beschreibung
@@ -3363,7 +3412,6 @@ monitor.execute_time=Ausführungszeit
monitor.last_execution_result=Ergebnis
monitor.process.cancel=Prozess abbrechen
monitor.process.cancel_desc=Abbrechen eines Prozesses kann Datenverlust verursachen
-monitor.process.cancel_notices=Abbrechen: <strong>%s</strong>?
monitor.process.children=Subprozesse
monitor.queues=Warteschlangen
@@ -3530,6 +3578,7 @@ versions=Versionen
versions.view_all=Alle anzeigen
dependency.id=ID
dependency.version=Version
+search_in_external_registry=In %s suchen
alpine.registry=Richte diese Registry ein, indem Du die URL in die <code>/etc/apk/repositories</code>-Datei hinzufügst:
alpine.registry.key=Lade den öffentlichen RSA-Key der Registry in den <code>/etc/apk/keys/</code>-Ordner, um die Signatur zu überprüfen:
alpine.registry.info=Wähle $branch und $repository aus der Liste unten.
@@ -3559,7 +3608,8 @@ conda.install=Um das Paket mit Conda zu installieren, führe den folgenden Befeh
container.details.type=Container-Image Typ
container.details.platform=Plattform
container.pull=Downloade das Container-Image aus der Kommandozeile:
-container.digest=Digest:
+container.images=Images
+container.digest=Digest
container.multi_arch=Betriebsystem / Architektur
container.layers=Container-Image Ebenen
container.labels=Labels
@@ -3662,11 +3712,18 @@ owner.settings.chef.keypair.description=Ein Schlüsselpaar ist notwendig, um mit
secrets=Secrets
description=Secrets werden an bestimmte Aktionen weitergegeben und können nicht anderweitig ausgelesen werden.
none=Noch keine Secrets vorhanden.
-creation=Secret hinzufügen
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Beschreibung
creation.name_placeholder=Groß-/Kleinschreibung wird ignoriert, nur alphanumerische Zeichen oder Unterstriche, darf nicht mit GITEA_ oder GITHUB_ beginnen
creation.value_placeholder=Beliebigen Inhalt eingeben. Leerzeichen am Anfang und Ende werden weggelassen.
-creation.success=Das Secret "%s" wurde hinzugefügt.
-creation.failed=Secret konnte nicht hinzugefügt werden.
+creation.description_placeholder=Gib eine Kurzbeschreibung ein (optional).
+
+save_success=Das Secret "%s" wurde gespeichert.
+save_failed=Secret konnte nicht gespeichert werden.
+
+add_secret=Secret hinzufügen
+edit_secret=Secret bearbeiten
deletion=Secret entfernen
deletion.description=Das Entfernen eines Secrets kann nicht rückgängig gemacht werden. Fortfahren?
deletion.success=Das Secret wurde entfernt.
@@ -3717,7 +3774,7 @@ runners.delete_runner_header=Bestätigen, um diesen Runner zu löschen
runners.delete_runner_notice=Wenn eine Aufgabe auf diesem Runner ausgeführt wird, wird sie beendet und als fehlgeschlagen markiert. Dies könnte Workflows zerstören.
runners.none=Keine Runner verfügbar
runners.status.unspecified=Unbekannt
-runners.status.idle=Inaktiv
+runners.status.idle=Leerlauf
runners.status.active=Aktiv
runners.status.offline=Offline
runners.version=Version
@@ -3744,6 +3801,10 @@ runs.no_workflows.documentation=Weitere Informationen zu Gitea Actions findest d
runs.no_runs=Der Workflow hat noch keine Ausführungen.
runs.empty_commit_message=(leere Commit-Nachricht)
runs.expire_log_message=Protokolle wurden geleert, weil sie zu alt waren.
+runs.delete=Workflow-Ausführung löschen
+runs.delete.description=Bist du sicher, dass du diese Workflow-Ausführung dauerhaft löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.
+runs.not_done=Diese Workflow-Ausführung ist noch nicht abgeschlossen.
+runs.view_workflow_file=Workflow-Datei anzeigen
workflow.disable=Workflow deaktivieren
workflow.disable_success=Workflow '%s' erfolgreich deaktiviert.
@@ -3755,6 +3816,7 @@ workflow.not_found=Workflow '%s' wurde nicht gefunden.
workflow.run_success=Workflow '%s' erfolgreich ausgeführt.
workflow.from_ref=Nutze Workflow von
workflow.has_workflow_dispatch=Dieser Workflow hat einen workflow_dispatch Event-Trigger.
+workflow.has_no_workflow_dispatch=Der Workflow '%s' hat keinen workflow_dispatch Event-Trigger.
need_approval_desc=Um Workflows für den Pull-Request eines Forks auszuführen, ist eine Genehmigung erforderlich.
@@ -3782,6 +3844,8 @@ deleted.display_name=Gelöschtes Projekt
type-1.display_name=Individuelles Projekt
type-2.display_name=Repository-Projekt
type-3.display_name=Organisationsprojekt
+enter_fullscreen=Vollbild
+exit_fullscreen=Vollbild verlassen
[git.filemode]
changed_filemode=%[1]s → %[2]s
diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini
index 31e57bbf97..0411686084 100644
--- a/options/locale/locale_el-GR.ini
+++ b/options/locale/locale_el-GR.ini
@@ -53,6 +53,7 @@ webauthn_reload=Ανανέωση
repository=ΑποθετήÏιο
organization=ΟÏγανισμός
mirror=ΑντίγÏαφο
+issue_milestone=ΟÏόσημο
new_repo=Îέο ΑποθετήÏιο
new_migrate=Îέα ΜεταφοÏά
new_mirror=Îέο Είδωλο
@@ -334,6 +335,7 @@ show_only_public=Εμφανίζονται μόνο δημόσια
issues.in_your_repos=Στα αποθετήÏια σας
+
[explore]
repos=ΑποθετήÏια
users=ΧÏήστες
@@ -388,7 +390,6 @@ oauth_signup_submit=ΟλοκληÏωμένος ΛογαÏιασμός
oauth_signin_tab=ΣÏνδεση με υπάÏχων λογαÏιασμό
oauth_signin_title=Συνδεθείτε για να εγκÏίνετε τον Συνδεδεμένο ΛογαÏιασμό
oauth_signin_submit=ΣÏνδεση ΛογαÏιασμοÏ
-oauth.signin.error=ΠαÏουσιάστηκε σφάλμα κατά την επεξεÏγασία του αιτήματος εξουσιοδότησης. Εάν αυτό το σφάλμα επιμένει, παÏακαλοÏμε επικοινωνήστε με το διαχειÏιστή του ιστοτόπου.
oauth.signin.error.access_denied=Η αίτηση εξουσιοδότησης αποÏÏίφθηκε.
oauth.signin.error.temporarily_unavailable=Η εξουσιοδότηση απέτυχε επειδή ο διακομιστής ταυτοποίησης δεν είναι διαθέσιμος Ï€ÏοσωÏινά. ΠαÏακαλώ Ï€Ïοσπαθήστε ξανά αÏγότεÏα.
openid_connect_submit=ΣÏνδεση
@@ -808,7 +809,6 @@ delete_token_success=Το διακÏιτικό έχει διαγÏαφεί. Οι
repo_and_org_access=ΠÏόσβαση στο ΑποθετήÏιο και ΟÏγανισμό
permissions_public_only=Δημόσια μόνο
permissions_access_all=Όλα (δημόσια, ιδιωτικά, και πεÏιοÏισμένα)
-select_permissions=Επιλέξτε δικαιώματα
permission_no_access=Καμία ΠÏόσβαση
permission_read=Αναγνωσμένες
permission_write=Ανάγνωση και ΕγγÏαφή
@@ -992,9 +992,6 @@ blame_prior=ΠÏοβολή ευθÏνης Ï€Ïιν από αυτή την αλλ
blame.ignore_revs=Αγνόηση των αναθεωÏήσεων στο <a href="%s">.git-blame-ignore-revs</a>. Πατήστε <a href="%s">εδώ</a> για να το παÏακάμψετε και να δείτε την κανονική Ï€Ïοβολή ευθυνών.
blame.ignore_revs.failed=Αποτυχία αγνόησης των αναθεωÏήσεων στο <a href="%s">.git-blame-ignore-revs</a>.
-tree_path_not_found_commit=Η διαδÏομή %[1]s δεν υπάÏχει στην υποβολή %[2]s
-tree_path_not_found_branch=Η διαδÏομή %[1]s δεν υπάÏχει στον κλάδο %[2]s
-tree_path_not_found_tag=Η διαδÏομή %[1]s δεν υπάÏχει στην ετικέτα %[2]s
transfer.accept=Αποδοχή ΜεταφοÏάς
transfer.reject=ΑπόÏÏιψη ΜεταφοÏάς
@@ -1122,6 +1119,7 @@ labels=Σήματα
org_labels_desc=Τα σήματα στο επίπεδο οÏγανισμοÏ, που μποÏοÏν να χÏησιμοποιηθοÏν με <strong>όλα τα αποθετήÏια</strong> κάτω από αυτόν τον οÏγανισμό
org_labels_desc_manage=διαχείÏιση
+milestone=ΟÏόσημο
milestones=ΟÏόσημα
commits=Υποβολές
commit=Υποβολή
@@ -1150,7 +1148,6 @@ file_copy_permalink=ΑντιγÏαφή Permalink
view_git_blame=ΠÏοβολή Git Blame
video_not_supported_in_browser=Το Ï€ÏόγÏαμμα πεÏιήγησής σας δεν υποστηÏίζει την ετικέτα HTML5 'video'.
audio_not_supported_in_browser=Το Ï€ÏόγÏαμμα πεÏιήγησής σας δεν υποστηÏίζει την ετικέτα HTML5 'audio'.
-stored_lfs=ΑποθηκεÏτηκε με το Git LFS
symbolic_link=Symbolic link
executable_file=Εκτελέσιμο ΑÏχείο
commit_graph=ΓÏάφημα Υποβολών
@@ -1193,7 +1190,6 @@ editor.update=ΕνημέÏωση %s
editor.delete=ΔιαγÏαφή %s
editor.patch=ΕφαÏμογή ΔιόÏθωσης
editor.patching=ΕπιδιόÏθωση:
-editor.fail_to_apply_patch=`Αδυναμία εφαÏμογής της επιδιόÏθωσης "%s"`
editor.new_patch=Îέα ΔιόÏθωση
editor.commit_message_desc=ΠÏοσθήκη Ï€ÏοαιÏετικής εκτενοÏÏ‚ πεÏιγÏαφής…
editor.signoff_desc=ΠÏοσθέστε ένα Ï€Ïόσθετο Signed-off-by στο τέλος του μηνÏματος καταγÏαφής της υποβολής.
@@ -1211,15 +1207,11 @@ editor.branch_already_exists=Ο κλάδος "%s" υπάÏχει ήδη σε αÏ
editor.directory_is_a_file=Το όνομα φακέλου "%s" χÏησιμοποιείται ήδη ως όνομα αÏχείου σε αυτό το αποθετήÏιο.
editor.file_is_a_symlink=`Το "%s" είναι συμβολικός σÏνδεσμος. Οι συμβολικοί σÏνδεσμοι δεν μποÏοÏν να επεξεÏγαστοÏν στην ενσωματωμένη εφαÏμογή`
editor.filename_is_a_directory=Το όνομα αÏχείου "%s" χÏησιμοποιείται ήδη ως όνομα φακέλου σε αυτό το αποθετήÏιο.
-editor.file_editing_no_longer_exists=Το αÏχείο "%s" που επεξεÏγάζεται, δεν υπάÏχει πλέον σε αυτό το αποθετήÏιο.
-editor.file_deleting_no_longer_exists=Το αÏχείο "%s" που διαγÏάφεται, δεν υπάÏχει πλέον σε αυτό το αποθετήÏιο.
editor.file_changed_while_editing=Τα πεÏιεχόμενα του αÏχείου άλλαξαν από τότε που ξεκίνησε η επεξεÏγασία. <a target="_blank" rel="noopener noreferrer" href="%s">Κάντε κλικ εδώ</a> για να τα δείτε ή <strong>Υποβολή Αλλαγών ξανά</strong> για να τα αντικαταστήσετε.
editor.file_already_exists=Ένα αÏχείο με το όνομα "%s" υπάÏχει ήδη σε αυτό το αποθετήÏιο.
editor.commit_empty_file_header=Υποβολή ενός ÎºÎµÎ½Î¿Ï Î±Ïχείου
editor.commit_empty_file_text=Το αÏχείο που Ï€Ïόκειται να υποβληθεί είναι κενό. Συνέχεια;
editor.no_changes_to_show=Δεν υπάÏχουν αλλαγές για εμφάνιση.
-editor.fail_to_update_file=Αποτυχία ενημέÏωσης/δημιουÏγίας του αÏχείου "%s".
-editor.fail_to_update_file_summary=Μήνυμα Σφάλματος:
editor.push_rejected_no_message=Η αλλαγή αποÏÏίφθηκε από το διακομιστή χωÏίς κάποιο μήνυμα. ΠαÏακαλώ ελέγξτε τα ΆγκιστÏα Git.
editor.push_rejected=Η αλλαγή αποÏÏίφθηκε από τον διακομιστή. ΠαÏακαλώ ελέγξτε τα ΆγκιστÏα Git.
editor.push_rejected_summary=Μήνυμα ΠλήÏους ΑπόÏÏιψης:
@@ -1234,6 +1226,7 @@ editor.require_signed_commit=Ο κλάδος απαιτεί υπογεγÏαμμ
editor.cherry_pick=Ανθολόγηση (cherry-pic) του %s στο:
editor.revert=ΑπόσυÏση του %s στο:
+
commits.desc=Δείτε το ιστοÏικό αλλαγών του πηγαίου κώδικα.
commits.commits=Υποβολές
commits.no_commits=Δεν υπάÏχουν κοινές υποβολές. Τα "%s" και "%s" έχουν εντελώς διαφοÏετικές ιστοÏίες.
@@ -1379,8 +1372,6 @@ issues.filter_project=ΈÏγο
issues.filter_project_all=Όλα τα έÏγα
issues.filter_project_none=ΧωÏίς έÏγα
issues.filter_assignee=Αποδέκτης
-issues.filter_assginee_no_select=Όλοι οι αποδέκτες
-issues.filter_assginee_no_assignee=Κανένας Αποδέκτης
issues.filter_poster=ΣυγγÏαφέας
issues.filter_type=ΤÏπος
issues.filter_type.all_issues=Όλα τα ζητήματα
@@ -1495,13 +1486,12 @@ issues.attachment.open_tab=`Κάντε κλικ για να δείτε το "%s"
issues.attachment.download=`Κάντε κλικ για να λάβετε το "%s"`
issues.subscribe=ΕγγÏαφή
issues.unsubscribe=ΔιαγÏαφή
-issues.unpin_issue=Άφεση Ζητήματος
+issues.unpin=Άφεση
issues.max_pinned=Δεν μποÏείτε να διατηÏήσετε πεÏισσότεÏα ζητήματα
issues.pin_comment=διατήÏησε αυτό %s
issues.unpin_comment=άφησε αυτό %s
issues.lock=Κλείδωμα συνομιλίας
issues.unlock=Ξεκλείδωμα συνομιλίας
-issues.lock.unknown_reason=Αδυναμία κλειδώματος ενός ζητήματος με άγνωστο λόγο.
issues.lock_duplicate=Ένα ζήτημα δεν μποÏεί να κλειδωθεί δÏο φοÏές.
issues.unlock_error=Δεν είναι δυνατό να ξεκλειδώσετε ένα ζήτημα που δεν είναι κλειδωμένο.
issues.lock_with_reason=κλειδωμένο ως <strong>%s</strong> και πεÏιοÏισμένη συνομιλία με συνεÏγάτες %s
@@ -1676,7 +1666,6 @@ pulls.add_prefix=ΠÏοσθήκη <strong>%s</strong> Ï€Ïοθέματος
pulls.remove_prefix=ΑφαίÏεση <strong>%s</strong> Ï€Ïοθέματος
pulls.data_broken=Αυτό το pull request είναι κατεστÏαμμένο λόγω των πληÏοφοÏιών του fork που λείπουν.
pulls.files_conflicted=Αυτό το pull request πεÏιέχει αλλαγές που συγκÏοÏονται με το κλάδο Ï€ÏοοÏισμοÏ.
-pulls.is_checking=Ο έλεγχος συγκÏοÏσεων κατά την συγχώνευση είναι σε εξέλιξη. Δοκιμάστε ξανά σε λίγα λεπτά.
pulls.is_ancestor=Αυτός ο κλάδος πεÏιλαμβάνεται ήδη στον κλάδο Ï€ÏοοÏισμοÏ. Δεν υπάÏχει τίποτα για συγχώνευση.
pulls.is_empty=Οι αλλαγές σε αυτόν τον κλάδο είναι ήδη στον κλάδο Ï€ÏοοÏισμοÏ. Θα είναι μια κενή υποβολή.
pulls.required_status_check_failed=ΟÏισμένοι απαιτοÏμενοι έλεγχοι δεν ήταν επιτυχείς.
@@ -1741,7 +1730,6 @@ pulls.outdated_with_base_branch=Αυτός ο κλάδος δεν είναι εÎ
pulls.close=Κλείσιμο Pull Request
pulls.closed_at=`έκλεισε αυτό το pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at=`άνοιξε ξανά αυτό το pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-pulls.cmd_instruction_hint=`Δείτε τις <a class="show-instruction">οδηγίες γÏαμμής εντολών</a>.`
pulls.cmd_instruction_checkout_title=Έλεγχος
pulls.cmd_instruction_checkout_desc=Από το αποθετήÏιο του έÏγου σας, ελέγξτε έναν νέο κλάδο και δοκιμάστε τις αλλαγές.
pulls.cmd_instruction_merge_title=Συγχώνευση
@@ -2078,8 +2066,6 @@ settings.hooks_desc=Τα Webhooks κάνουν αυτόματα αιτήσεις
settings.webhook_deletion=ΑφαίÏεση Webhook
settings.webhook_deletion_desc=Η αφαίÏεση ενός webhook διαγÏάφει τις Ïυθμίσεις και το ιστοÏικό παÏαδόσεων. Συνέχεια;
settings.webhook_deletion_success=Το webhook έχει αφαιÏεθεί.
-settings.webhook.test_delivery=Δοκιμή ΠαÏάδοσης
-settings.webhook.test_delivery_desc=Δοκιμάστε αυτό το webhook με ένα ψεÏτικο συμβάν.
settings.webhook.test_delivery_desc_disabled=Για να δοκιμάσετε αυτό το webhook με μια ψεÏτικη κλήση, ενεÏγοποιήστε το.
settings.webhook.request=Αίτημα
settings.webhook.response=Απάντηση
@@ -2125,7 +2111,6 @@ settings.event_repository=ΑποθετήÏιο
settings.event_repository_desc=Το αποθετήÏιο δημιουÏγήθηκε ή διαγÏάφηκε.
settings.event_header_issue=Γεγονότα Ζητήματος
settings.event_issues=Ζητήματα
-settings.event_issues_desc=Το ζήτημα άνοιξε, έκλεισε, ανοίχθηκε εκ νέου ή επεξεÏγάστηκε.
settings.event_issue_assign=Ζήτημα Ανατέθηκε
settings.event_issue_assign_desc=Ζήτημα εκχωÏημένο ή μη εκχωÏημένο.
settings.event_issue_label=Σήμανση Ζητήματος
@@ -2136,7 +2121,6 @@ settings.event_issue_comment=Σχόλιο Ζητήματος
settings.event_issue_comment_desc=Το σχόλιο στο ζήτημα δημιουÏγήθηκε, επεξεÏγάστηκε ή διαγÏάφηκε.
settings.event_header_pull_request=Γεγονότα Pull Requests
settings.event_pull_request=Pull Request
-settings.event_pull_request_desc=Το pull request άνοιξε, έκλεισε, άνοιξε εκ νέου ή επεξεÏγάστηκε.
settings.event_pull_request_assign=Το Pull Request Ανατέθηκε
settings.event_pull_request_assign_desc=Το pull request ανατέθηκε ή έγινε αδιάθετο.
settings.event_pull_request_label=Σήμανση Pull Request
@@ -2317,7 +2301,6 @@ diff.commit=υποβολή
diff.git-notes=Σημειώσεις
diff.data_not_available=Δεν ΥπάÏχει Διαθέσιμο ΠεÏιεχόμενο Diff
diff.options_button=Επιλογές Diff
-diff.show_diff_stats=Εμφάνιση Στατιστικών
diff.download_patch=Λήψη ΑÏχείου Patch
diff.download_diff=Λήψη ΑÏχείου Diff
diff.show_split_view=ΔιαιÏεμένη ΠÏοβολή
@@ -2514,15 +2497,13 @@ settings.visibility.private_shortname=Ιδιωτικός
settings.update_settings=ΕνημέÏωση Ρυθμίσεων
settings.update_setting_success=Οι Ïυθμίσεις του οÏÎ³Î±Î½Î¹ÏƒÎ¼Î¿Ï Î­Ï‡Î¿Ï…Î½ ενημεÏωθεί.
-settings.change_orgname_prompt=Σημείωση: Η αλλαγή του ονόματος του οÏÎ³Î±Î½Î¹ÏƒÎ¼Î¿Ï Î¸Î± αλλάξει επίσης τη διεÏθυνση URL του οÏÎ³Î±Î½Î¹ÏƒÎ¼Î¿Ï ÏƒÎ±Ï‚ και θα απελευθεÏώσει το παλιό όνομα.
-settings.change_orgname_redirect_prompt=Το παλιό όνομα θα ανακατευθÏνει μέχÏι να διεκδικηθεί.
+
+
settings.update_avatar_success=Η εικόνα του οÏÎ³Î±Î½Î¹ÏƒÎ¼Î¿Ï Î­Ï‡ÎµÎ¹ ενημεÏωθεί.
settings.delete=ΔιαγÏαφή ΟÏγανισμοÏ
settings.delete_account=ΔιαγÏαφή Î‘Ï…Ï„Î¿Ï Î¤Î¿Ï… ΟÏγανισμοÏ
settings.delete_prompt=Ο οÏγανισμός θα αφαιÏεθεί οÏιστικά. Αυτό το <strong>ΔΕΠΜΠΟΡΕΙ</strong> να αναιÏεθεί!
settings.confirm_delete_account=Επιβεβαίωση ΔιαγÏαφής
-settings.delete_org_title=ΔιαγÏαφή ΟÏγανισμοÏ
-settings.delete_org_desc=Αυτός ο οÏγανισμός θα διαγÏαφεί οÏιστικά. Συνέχεια;
settings.hooks_desc=ΠÏοσθήκη webhooks που θα ενεÏγοποιοÏνται για <strong>όλα τα αποθετήÏια</strong> κάτω από αυτό τον οÏγανισμό.
settings.labels_desc=ΠÏοσθήκη σημάτων που μποÏοÏν να χÏησιμοποιηθοÏν σε ζητήματα για <strong>όλα τα αποθετήÏια</strong> κάτω από αυτό τον οÏγανισμό.
@@ -2593,6 +2574,7 @@ teams.invite.by=ΠÏοσκλήθηκε από %s
teams.invite.description=ΠαÏακαλώ κάντε κλικ στον παÏακάτω σÏνδεσμο για συμμετοχή στην ομάδα.
+
[admin]
dashboard=Πίνακας Ελέγχου
identity_access=Ταυτότητα & ΠÏόσβαση
@@ -2932,8 +2914,6 @@ config.ssh_domain=Domain Διακομιστή SSH
config.ssh_port=ΘÏÏα
config.ssh_listen_port=ΘÏÏα ΑκÏόασης
config.ssh_root_path=Ριζική ΔιαδÏομή
-config.ssh_key_test_path=ΔιαδÏομή Δοκιμής ΚλειδιοÏ
-config.ssh_keygen_path=ΔιαδÏομή Keygen ('ssh-keygen')
config.ssh_minimum_key_size_check=Έλεγχος Ελάχιστου Μεγέθους ΚλειδιοÏ
config.ssh_minimum_key_sizes=Ελάχιστα Μεγέθη Κλειδιών
@@ -3239,7 +3219,6 @@ conda.install=Για να εγκαταστήσετε το πακέτο χÏησÎ
container.details.type=ΤÏπος Εικόνας
container.details.platform=ΠλατφόÏμα
container.pull=Κατεβάστε την εικόνα από τη γÏαμμή εντολών:
-container.digest=ΣÏνοψη:
container.multi_arch=ΛΣ / ΑÏχιτεκτονική
container.layers=ΣτÏώματα Εικόνας
container.labels=Ετικέτες
@@ -3340,11 +3319,13 @@ owner.settings.chef.keypair.description=Ένα ζεÏγος κλειδιών εÎ
secrets=Μυστικά
description=Τα μυστικά θα πεÏάσουν σε οÏισμένες δÏάσεις και δεν μποÏοÏν να αναγνωστοÏν αλλοÏ.
none=Δεν υπάÏχουν ακόμα μυστικά.
-creation=ΠÏοσθήκη ΜυστικοÏ
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=ΠεÏιγÏαφή
creation.name_placeholder=αλφαÏιθμητικοί χαÏακτήÏες ή κάτω παÏλες μόνο, δεν μποÏοÏν να ξεκινοÏν με GITEA_ ή GITHUB_
creation.value_placeholder=Εισάγετε οποιοδήποτε πεÏιεχόμενο. Τα κενά στην αÏχή παÏαλείπονται.
-creation.success=Το μυστικό "%s" Ï€Ïοστέθηκε.
-creation.failed=Αποτυχία δημιουÏγίας μυστικοÏ.
+
+
deletion=ΑφαίÏεση μυστικοÏ
deletion.description=Η αφαίÏεση ενός Î¼Ï…ÏƒÏ„Î¹ÎºÎ¿Ï ÎµÎ¯Î½Î±Î¹ μόνιμη και δεν μποÏεί να αναιÏεθεί. Συνέχεια;
deletion.success=Το μυστικό έχει αφαιÏεθεί.
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 6029d49ade..9a2591e9ee 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -54,6 +54,7 @@ webauthn_reload = Reload
repository = Repository
organization = Organization
mirror = Mirror
+issue_milestone = Milestone
new_repo = New Repository
new_migrate = New Migration
new_mirror = New Mirror
@@ -112,9 +113,11 @@ copy_type_unsupported = This file type cannot be copied
write = Write
preview = Preview
loading = Loading…
+files = Files
error = Error
error404 = The page you are trying to reach either <strong>does not exist</strong> or <strong>you are not authorized</strong> to view it.
+error503 = The server was unable to complete your request. Please try again later.
go_back = Go Back
invalid_data = Invalid data: %v
@@ -127,6 +130,7 @@ pin = Pin
unpin = Unpin
artifacts = Artifacts
+expired = Expired
confirm_delete_artifact = Are you sure you want to delete the artifact '%s' ?
archived = Archived
@@ -168,6 +172,10 @@ search = Search...
type_tooltip = Search type
fuzzy = Fuzzy
fuzzy_tooltip = Include results that also match the search term closely
+words = Words
+words_tooltip = Include only results that match the search term words
+regexp = Regexp
+regexp_tooltip = Include only results that match the regexp search term
exact = Exact
exact_tooltip = Include only results that match the exact search term
repo_kind = Search repos...
@@ -384,6 +392,13 @@ show_only_public = Showing only public
issues.in_your_repos = In your repositories
+guide_title = No Activity
+guide_desc = You are currently not following any repositories or users, so there is no content to display. You can explore repositories or users of interest from the links below.
+explore_repos = Explore repositories
+explore_users = Explore users
+empty_org = There are no organizations yet.
+empty_repo = There are no repositories yet.
+
[explore]
repos = Repositories
users = Users
@@ -406,6 +421,7 @@ remember_me.compromised = The login token is not valid anymore which may indicat
forgot_password_title= Forgot Password
forgot_password = Forgot password?
need_account = Need an account?
+sign_up_tip = You are registering the first account in the system, which has administrator privileges. Please carefully remember your username and password. If you forget the username or password, please refer to the Gitea documentation to recover the account.
sign_up_now = Register now.
sign_up_successful = Account was successfully created. Welcome!
confirmation_mail_sent_prompt_ex = A new confirmation email has been sent to <b>%s</b>. Please check your inbox within the next %s to complete the registration process. If your registration email address is incorrect, you can sign in again and change it.
@@ -436,6 +452,7 @@ use_scratch_code = Use a scratch code
twofa_scratch_used = You have used your scratch code. You have been redirected to the two-factor settings page so you may remove your device enrollment or generate a new scratch code.
twofa_passcode_incorrect = Your passcode is incorrect. If you misplaced your device, use your scratch code to sign in.
twofa_scratch_token_incorrect = Your scratch code is incorrect.
+twofa_required = You must setup Two-Factor Authentication to get access to repositories, or try to login again.
login_userpass = Sign In
login_openid = OpenID
oauth_signup_tab = Register New Account
@@ -444,7 +461,7 @@ oauth_signup_submit = Complete Account
oauth_signin_tab = Link to Existing Account
oauth_signin_title = Sign In to Authorize Linked Account
oauth_signin_submit = Link Account
-oauth.signin.error = There was an error processing the authorization request. If this error persists, please contact the site administrator.
+oauth.signin.error.general = There was an error processing the authorization request: %s. If this error persists, please contact the site administrator.
oauth.signin.error.access_denied = The authorization request was denied.
oauth.signin.error.temporarily_unavailable = Authorization failed because the authentication server is temporarily unavailable. Please try again later.
oauth_callback_unable_auto_reg = Auto Registration is enabled, but OAuth2 Provider %[1]s returned missing fields: %[2]s, unable to create an account automatically, please create or link to an account, or contact the site administrator.
@@ -717,8 +734,8 @@ public_profile = Public Profile
biography_placeholder = Tell us a little bit about yourself! (You can use Markdown)
location_placeholder = Share your approximate location with others
profile_desc = Control how your profile is show to other users. Your primary email address will be used for notifications, password recovery and web-based Git operations.
-password_username_disabled = You are not allowed to change their username. Please contact your site administrator for more details.
-password_full_name_disabled = You are not allowed to change their full name. Please contact your site administrator for more details.
+password_username_disabled = You are not allowed to change your username. Please contact your site administrator for more details.
+password_full_name_disabled = You are not allowed to change your full name. Please contact your site administrator for more details.
full_name = Full Name
website = Website
location = Location
@@ -909,11 +926,13 @@ delete_token_success = The token has been deleted. Applications using it no long
repo_and_org_access = Repository and Organization Access
permissions_public_only = Public only
permissions_access_all = All (public, private, and limited)
-select_permissions = Select permissions
permission_not_set = Not set
permission_no_access = No Access
permission_read = Read
permission_write = Read and Write
+permission_anonymous_read = Anonymous Read
+permission_everyone_read = Everyone Read
+permission_everyone_write = Everyone Write
access_token_desc = Selected token permissions limit authorization only to the corresponding <a %s>API</a> routes. Read the <a %s>documentation</a> for more information.
at_least_one_permission = You must select at least one permission to create a token
permissions_list = Permissions:
@@ -1115,9 +1134,7 @@ blame.ignore_revs = Ignoring revisions in <a href="%s">.git-blame-ignore-revs</a
blame.ignore_revs.failed = Failed to ignore revisions in <a href="%s">.git-blame-ignore-revs</a>.
user_search_tooltip = Shows a maximum of 30 users
-tree_path_not_found_commit = Path %[1]s doesn't exist in commit %[2]s
-tree_path_not_found_branch = Path %[1]s doesn't exist in branch %[2]s
-tree_path_not_found_tag = Path %[1]s doesn't exist in tag %[2]s
+tree_path_not_found = Path %[1]s doesn't exist in %[2]s
transfer.accept = Accept Transfer
transfer.accept_desc = Transfer to "%s"
@@ -1128,6 +1145,7 @@ transfer.no_permission_to_reject = You do not have permission to reject this tra
desc.private = Private
desc.public = Public
+desc.public_access = Public Access
desc.template = Template
desc.internal = Internal
desc.archived = Archived
@@ -1211,6 +1229,7 @@ migrate.migrating_issues = Migrating Issues
migrate.migrating_pulls = Migrating Pull Requests
migrate.cancel_migrating_title = Cancel Migration
migrate.cancel_migrating_confirm = Do you want to cancel this migration?
+migration_status = Migration status
mirror_from = mirror of
forked_from = forked from
@@ -1235,6 +1254,7 @@ create_new_repo_command = Creating a new repository on the command line
push_exist_repo = Pushing an existing repository from the command line
empty_message = This repository does not contain any content.
broken_message = The Git data underlying this repository cannot be read. Contact the administrator of this instance or delete this repository.
+no_branch = This repository doesn’t have any branches.
code = Code
code.desc = Access source code, files, commits and branches.
@@ -1254,6 +1274,7 @@ labels = Labels
org_labels_desc = Organization level labels that can be used with <strong>all repositories</strong> under this organization
org_labels_desc_manage = manage
+milestone = Milestone
milestones = Milestones
commits = Commits
commit = Commit
@@ -1287,7 +1308,6 @@ file_copy_permalink = Copy Permalink
view_git_blame = View Git Blame
video_not_supported_in_browser = Your browser does not support the HTML5 'video' tag.
audio_not_supported_in_browser = Your browser does not support the HTML5 'audio' tag.
-stored_lfs = Stored with Git LFS
symbolic_link = Symbolic link
executable_file = Executable File
vendored = Vendored
@@ -1313,7 +1333,9 @@ editor.upload_file = Upload File
editor.edit_file = Edit File
editor.preview_changes = Preview Changes
editor.cannot_edit_lfs_files = LFS files cannot be edited in the web interface.
+editor.cannot_edit_too_large_file = The file is too large to be edited.
editor.cannot_edit_non_text_files = Binary files cannot be edited in the web interface.
+editor.file_not_editable_hint = But you can still rename or move it.
editor.edit_this_file = Edit File
editor.this_file_locked = File is locked
editor.must_be_on_a_branch = You must be on a branch to make or propose changes to this file.
@@ -1333,7 +1355,7 @@ editor.update = Update %s
editor.delete = Delete %s
editor.patch = Apply Patch
editor.patching = Patching:
-editor.fail_to_apply_patch = Unable to apply patch "%s"
+editor.fail_to_apply_patch = Unable to apply patch
editor.new_patch = New Patch
editor.commit_message_desc = Add an optional extended description…
editor.signoff_desc = Add a Signed-off-by trailer by the committer at the end of the commit log message.
@@ -1346,13 +1368,14 @@ editor.new_branch_name_desc = New branch name…
editor.cancel = Cancel
editor.filename_cannot_be_empty = The filename cannot be empty.
editor.filename_is_invalid = The filename is invalid: "%s".
+editor.commit_email = Commit email
+editor.invalid_commit_email = The email for the commit is invalid.
editor.branch_does_not_exist = Branch "%s" does not exist in this repository.
editor.branch_already_exists = Branch "%s" already exists in this repository.
editor.directory_is_a_file = Directory name "%s" is already used as a filename in this repository.
editor.file_is_a_symlink = `"%s" is a symbolic link. Symbolic links cannot be edited in the web editor`
editor.filename_is_a_directory = Filename "%s" is already used as a directory name in this repository.
-editor.file_editing_no_longer_exists = The file being edited, "%s", no longer exists in this repository.
-editor.file_deleting_no_longer_exists = The file being deleted, "%s", no longer exists in this repository.
+editor.file_modifying_no_longer_exists = The file being modified, "%s", no longer exists in this repository.
editor.file_changed_while_editing = The file contents have changed since you started editing. <a target="_blank" rel="noopener noreferrer" href="%s">Click here</a> to see them or <strong>Commit Changes again</strong> to overwrite them.
editor.file_already_exists = A file named "%s" already exists in this repository.
editor.commit_id_not_matching = The Commit ID does not match the ID when you began editing. Commit into a patch branch and then merge.
@@ -1360,8 +1383,6 @@ editor.push_out_of_date = The push appears to be out of date.
editor.commit_empty_file_header = Commit an empty file
editor.commit_empty_file_text = The file you're about to commit is empty. Proceed?
editor.no_changes_to_show = There are no changes to show.
-editor.fail_to_update_file = Failed to update/create file "%s".
-editor.fail_to_update_file_summary = Error Message:
editor.push_rejected_no_message = The change was rejected by the server without a message. Please check Git Hooks.
editor.push_rejected = The change was rejected by the server. Please check Git Hooks.
editor.push_rejected_summary = Full Rejection Message:
@@ -1375,6 +1396,15 @@ editor.user_no_push_to_branch = User cannot push to branch
editor.require_signed_commit = Branch requires a signed commit
editor.cherry_pick = Cherry-pick %s onto:
editor.revert = Revert %s onto:
+editor.failed_to_commit = Failed to commit changes.
+editor.failed_to_commit_summary = Error Message:
+
+editor.fork_create = Fork Repository to Propose Changes
+editor.fork_create_description = You can not edit this repository directly. Instead you can create a fork, make edits and create a pull request.
+editor.fork_edit_description = You can not edit this repository directly. The changes will be written to your fork <b>%s</b>, so you can create a pull request.
+editor.fork_not_editable = You have forked this repository but your fork is not editable.
+editor.fork_failed_to_push_branch = Failed to push branch %s to your repository.
+editor.fork_branch_exists = Branch "%s" already exists in your fork, please choose a new branch name.
commits.desc = Browse source code change history.
commits.commits = Commits
@@ -1394,6 +1424,7 @@ commits.signed_by_untrusted_user_unmatched = Signed by untrusted user who does n
commits.gpg_key_id = GPG Key ID
commits.ssh_key_fingerprint = SSH Key Fingerprint
commits.view_path=View at this point in history
+commits.view_file_diff = View changes to this file in this commit
commit.operations = Operations
commit.revert = Revert
@@ -1411,7 +1442,6 @@ commitstatus.success = Success
ext_issues = Access to External Issues
ext_issues.desc = Link to an external issue tracker.
-projects = Projects
projects.desc = Manage issues and pulls in projects.
projects.description = Description (optional)
projects.description_placeholder = Description
@@ -1455,6 +1485,8 @@ issues.filter_milestones = Filter Milestone
issues.filter_projects = Filter Project
issues.filter_labels = Filter Label
issues.filter_reviewers = Filter Reviewer
+issues.filter_no_results = No results
+issues.filter_no_results_placeholder = Try adjusting your search filters.
issues.new = New Issue
issues.new.title_empty = Title cannot be empty
issues.new.labels = Labels
@@ -1530,13 +1562,14 @@ issues.filter_project = Project
issues.filter_project_all = All projects
issues.filter_project_none = No project
issues.filter_assignee = Assignee
-issues.filter_assginee_no_select = All assignees
-issues.filter_assginee_no_assignee = No assignee
+issues.filter_assignee_no_assignee = Assigned to nobody
+issues.filter_assignee_any_assignee = Assigned to anybody
issues.filter_poster = Author
issues.filter_user_placeholder = Search users
issues.filter_user_no_select = All users
issues.filter_type = Type
issues.filter_type.all_issues = All issues
+issues.filter_type.all_pull_requests = All pull requests
issues.filter_type.assigned_to_you = Assigned to you
issues.filter_type.created_by_you = Created by you
issues.filter_type.mentioning_you = Mentioning you
@@ -1628,12 +1661,15 @@ issues.save = Save
issues.label_title = Name
issues.label_description = Description
issues.label_color = Color
+issues.label_color_invalid = Invalid color
issues.label_exclusive = Exclusive
issues.label_archive = Archive Label
issues.label_archived_filter = Show archived labels
issues.label_archive_tooltip = Archived labels are excluded by default from the suggestions when searching by label.
issues.label_exclusive_desc = Name the label <code>scope/item</code> to make it mutually exclusive with other <code>scope/</code> labels.
issues.label_exclusive_warning = Any conflicting scoped labels will be removed when editing the labels of an issue or pull request.
+issues.label_exclusive_order = Sort Order
+issues.label_exclusive_order_tooltip = Exclusive labels in the same scope will be sorted according to this numeric order.
issues.label_count = %d labels
issues.label_open_issues = %d open issues/pull requests
issues.label_edit = Edit
@@ -1651,13 +1687,12 @@ issues.attachment.open_tab = `Click to see "%s" in a new tab`
issues.attachment.download = `Click to download "%s"`
issues.subscribe = Subscribe
issues.unsubscribe = Unsubscribe
-issues.unpin_issue = Unpin Issue
+issues.unpin = Unpin
issues.max_pinned = "You can't pin more issues"
issues.pin_comment = "pinned this %s"
issues.unpin_comment = "unpinned this %s"
issues.lock = Lock conversation
issues.unlock = Unlock conversation
-issues.lock.unknown_reason = Cannot lock an issue with an unknown reason.
issues.lock_duplicate = An issue cannot be locked twice.
issues.unlock_error = Cannot unlock an issue that is not locked.
issues.lock_with_reason = "locked as <strong>%s</strong> and limited conversation to collaborators %s"
@@ -1686,16 +1721,20 @@ issues.timetracker_timer_manually_add = Add Time
issues.time_estimate_set = Set estimated time
issues.time_estimate_display = Estimate: %s
-issues.change_time_estimate_at = changed time estimate to <b>%s</b> %s
+issues.change_time_estimate_at = changed time estimate to <b>%[1]s</b> %[2]s
issues.remove_time_estimate_at = removed time estimate %s
issues.time_estimate_invalid = Time estimate format is invalid
issues.start_tracking_history = started working %s
issues.tracker_auto_close = Timer will be stopped automatically when this issue gets closed
+issues.stopwatch_already_stopped = The timer for this issue is already stopped
+issues.stopwatch_already_created = The timer for this issue already exists
issues.tracking_already_started = `You have already started time tracking on <a href="%s">another issue</a>!`
-issues.stop_tracking_history = worked for <b>%s</b> %s
+issues.stop_tracking = Stop Timer
+issues.stop_tracking_history = worked for <b>%[1]s</b> %[2]s
+issues.cancel_tracking = Discard
issues.cancel_tracking_history = `canceled time tracking %s`
issues.del_time = Delete this time log
-issues.add_time_history = added spent time <b>%s</b> %s
+issues.add_time_history = added spent time <b>%[1]s</b> %[2]s
issues.del_time_history= `deleted spent time %s`
issues.add_time_manually = Manually Add Time
issues.add_time_hours = Hours
@@ -1855,7 +1894,7 @@ pulls.add_prefix = Add <strong>%s</strong> prefix
pulls.remove_prefix = Remove <strong>%s</strong> prefix
pulls.data_broken = This pull request is broken due to missing fork information.
pulls.files_conflicted = This pull request has changes conflicting with the target branch.
-pulls.is_checking = "Merge conflict checking is in progress. Try again in few moments."
+pulls.is_checking = Checking for merge conflicts ...
pulls.is_ancestor = "This branch is already included in the target branch. There is nothing to merge."
pulls.is_empty = "The changes on this branch are already on the target branch. This will be an empty commit."
pulls.required_status_check_failed = Some required checks were not successful.
@@ -1925,12 +1964,12 @@ pulls.outdated_with_base_branch = This branch is out-of-date with the base branc
pulls.close = Close Pull Request
pulls.closed_at = `closed this pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at = `reopened this pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-pulls.cmd_instruction_hint = `View <a class="show-instruction">command line instructions</a>.`
+pulls.cmd_instruction_hint = View command line instructions
pulls.cmd_instruction_checkout_title = Checkout
pulls.cmd_instruction_checkout_desc = From your project repository, check out a new branch and test the changes.
pulls.cmd_instruction_merge_title = Merge
pulls.cmd_instruction_merge_desc = Merge the changes and update on Gitea.
-pulls.cmd_instruction_merge_warning = Warning: This operation can not merge pull request because "autodetect manual merge" was not enable
+pulls.cmd_instruction_merge_warning = Warning: This operation cannot merge pull request because "autodetect manual merge" is not enabled.
pulls.clear_merge_message = Clear merge message
pulls.clear_merge_message_hint = Clearing the merge message will only remove the commit message content and keep generated git trailers such as "Co-Authored-By …".
@@ -1954,6 +1993,7 @@ pulls.upstream_diverging_prompt_behind_1 = This branch is %[1]d commit behind %[
pulls.upstream_diverging_prompt_behind_n = This branch is %[1]d commits behind %[2]s
pulls.upstream_diverging_prompt_base_newer = The base branch %s has new changes
pulls.upstream_diverging_merge = Sync fork
+pulls.upstream_diverging_merge_confirm = Would you like to merge "%[1]s" onto "%[2]s"?
pull.deleted_branch = (deleted):%s
pull.agit_documentation = Review documentation about AGit
@@ -2113,12 +2153,19 @@ contributors.contribution_type.deletions = Deletions
settings = Settings
settings.desc = Settings is where you can manage the settings for the repository
settings.options = Repository
+settings.public_access = Public Access
+settings.public_access_desc = Configure public visitor's access permissions to override the defaults of this repository.
+settings.public_access.docs.not_set = Not Set: no extra public access permission. The visitor's permission follows the repository's visibility and member permissions.
+settings.public_access.docs.anonymous_read = Anonymous Read: users who are not logged in can access the unit with read permission.
+settings.public_access.docs.everyone_read = Everyone Read: all logged-in users can access the unit with read permission. Read permission of issues/pull-requests units also means users can create new issues/pull-requests.
+settings.public_access.docs.everyone_write = Everyone Write: all logged-in users have write permission to the unit. Only Wiki unit supports this permission.
settings.collaboration = Collaborators
settings.collaboration.admin = Administrator
settings.collaboration.write = Write
settings.collaboration.read = Read
settings.collaboration.owner = Owner
settings.collaboration.undefined = Undefined
+settings.collaboration.per_unit = Unit Permissions
settings.hooks = Webhooks
settings.githooks = Git Hooks
settings.basic_settings = Basic Settings
@@ -2159,7 +2206,6 @@ settings.advanced_settings = Advanced Settings
settings.wiki_desc = Enable Repository Wiki
settings.use_internal_wiki = Use Built-In Wiki
settings.default_wiki_branch_name = Default Wiki Branch Name
-settings.default_wiki_everyone_access = Default Access Permission for signed-in users:
settings.failed_to_change_default_wiki_branch = Failed to change the default wiki branch.
settings.use_external_wiki = Use External Wiki
settings.external_wiki_url = External Wiki URL
@@ -2288,8 +2334,8 @@ settings.hooks_desc = Webhooks automatically make HTTP POST requests to a server
settings.webhook_deletion = Remove Webhook
settings.webhook_deletion_desc = Removing a webhook deletes its settings and delivery history. Continue?
settings.webhook_deletion_success = The webhook has been removed.
-settings.webhook.test_delivery = Test Delivery
-settings.webhook.test_delivery_desc = Test this webhook with a fake event.
+settings.webhook.test_delivery = Test Push Event
+settings.webhook.test_delivery_desc = Test this webhook with a fake push event.
settings.webhook.test_delivery_desc_disabled = To test this webhook with a fake event, activate it.
settings.webhook.request = Request
settings.webhook.response = Response
@@ -2309,6 +2355,7 @@ settings.payload_url = Target URL
settings.http_method = HTTP Method
settings.content_type = POST Content Type
settings.secret = Secret
+settings.webhook_secret_desc = If the webhook server supports using secret, you can follow the webhook's manual and fill in a secret here.
settings.slack_username = Username
settings.slack_icon_url = Icon URL
settings.slack_color = Color
@@ -2327,6 +2374,8 @@ settings.event_fork = Fork
settings.event_fork_desc = Repository forked.
settings.event_wiki = Wiki
settings.event_wiki_desc = Wiki page created, renamed, edited or deleted.
+settings.event_statuses = Statuses
+settings.event_statuses_desc = Commit Status updated from the API.
settings.event_release = Release
settings.event_release_desc = Release published, updated or deleted in a repository.
settings.event_push = Push
@@ -2336,7 +2385,7 @@ settings.event_repository = Repository
settings.event_repository_desc = Repository created or deleted.
settings.event_header_issue = Issue Events
settings.event_issues = Issues
-settings.event_issues_desc = Issue opened, closed, reopened, or edited.
+settings.event_issues_desc = Issue opened, closed, reopened, edited or deleted.
settings.event_issue_assign = Issue Assigned
settings.event_issue_assign_desc = Issue assigned or unassigned.
settings.event_issue_label = Issue Labeled
@@ -2347,7 +2396,7 @@ settings.event_issue_comment = Issue Comment
settings.event_issue_comment_desc = Issue comment created, edited, or deleted.
settings.event_header_pull_request = Pull Request Events
settings.event_pull_request = Pull Request
-settings.event_pull_request_desc = Pull request opened, closed, reopened, or edited.
+settings.event_pull_request_desc = Pull request opened, closed, reopened, edited or deleted.
settings.event_pull_request_assign = Pull Request Assigned
settings.event_pull_request_assign_desc = Pull request assigned or unassigned.
settings.event_pull_request_label = Pull Request Labeled
@@ -2364,6 +2413,11 @@ settings.event_pull_request_review_request = Pull Request Review Requested
settings.event_pull_request_review_request_desc = Pull request review requested or review request removed.
settings.event_pull_request_approvals = Pull Request Approvals
settings.event_pull_request_merge = Pull Request Merge
+settings.event_header_workflow = Workflow Events
+settings.event_workflow_run = Workflow Run
+settings.event_workflow_run_desc = Gitea Actions Workflow run queued, waiting, in progress, or completed.
+settings.event_workflow_job = Workflow Jobs
+settings.event_workflow_job_desc = Gitea Actions Workflow job queued, waiting, in progress, or completed.
settings.event_package = Package
settings.event_package_desc = Package created or deleted in a repository.
settings.branch_filter = Branch filter
@@ -2578,7 +2632,6 @@ diff.commit = commit
diff.git-notes = Notes
diff.data_not_available = Diff Content Not Available
diff.options_button = Diff Options
-diff.show_diff_stats = Show Stats
diff.download_patch = Download Patch File
diff.download_diff = Download Diff File
diff.show_split_view = Split View
@@ -2627,6 +2680,9 @@ diff.image.overlay = Overlay
diff.has_escaped = This line has hidden Unicode characters
diff.show_file_tree = Show file tree
diff.hide_file_tree = Hide file tree
+diff.submodule_added = Submodule %[1]s added at %[2]s
+diff.submodule_deleted = Submodule %[1]s deleted from %[2]s
+diff.submodule_updated = Submodule %[1]s updated: %[2]s
releases.desc = Track project versions and downloads.
release.releases = Releases
@@ -2697,6 +2753,7 @@ branch.restore_success = Branch "%s" has been restored.
branch.restore_failed = Failed to restore branch "%s".
branch.protected_deletion_failed = Branch "%s" is protected. It cannot be deleted.
branch.default_deletion_failed = Branch "%s" is the default branch. It cannot be deleted.
+branch.default_branch_not_exist = Default branch "%s" does not exist.
branch.restore = Restore Branch "%s"
branch.download = Download Branch "%s"
branch.rename = Rename Branch "%s"
@@ -2711,6 +2768,10 @@ branch.create_branch_operation = Create branch
branch.new_branch = Create new branch
branch.new_branch_from = Create new branch from "%s"
branch.renamed = Branch %s was renamed to %s.
+branch.rename_default_or_protected_branch_error = Only admins can rename default or protected branches.
+branch.rename_protected_branch_failed = This branch is protected by glob-based protection rules.
+branch.commits_divergence_from = Commits divergence: %[1]d behind and %[2]d ahead of %[3]s
+branch.commits_no_divergence = The same as branch %[1]s
tag.create_tag = Create tag %s
tag.create_tag_operation = Create tag
@@ -2724,6 +2785,7 @@ topic.done = Done
topic.count_prompt = You cannot select more than 25 topics
topic.format_prompt = Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
+find_file.follow_symlink= Follow this symlink to where it is pointing at
find_file.go_to_file = Go to file
find_file.no_matching = No matching file found
@@ -2764,6 +2826,7 @@ team_permission_desc = Permission
team_unit_desc = Allow Access to Repository Sections
team_unit_disabled = (Disabled)
+form.name_been_taken = The organisation name "%s" has already been taken.
form.name_reserved = The organization name "%s" is reserved.
form.name_pattern_not_allowed = The pattern "%s" is not allowed in an organization name.
form.create_org_not_allowed = You are not allowed to create an organization.
@@ -2785,15 +2848,28 @@ settings.visibility.private_shortname = Private
settings.update_settings = Update Settings
settings.update_setting_success = Organization settings have been updated.
-settings.change_orgname_prompt = Note: Changing the organization name will also change your organization's URL and free the old name.
-settings.change_orgname_redirect_prompt = The old name will redirect until it is claimed.
+
+settings.rename = Rename Organization
+settings.rename_desc = Changing the organization name will also change your organization's URL and free the old name.
+settings.rename_success = Organization %[1]s have been renamed to %[2]s successfully.
+settings.rename_no_change = Organization name is no change.
+settings.rename_new_org_name = New Organization Name
+settings.rename_failed = Rename Organization failed because of internal error
+settings.rename_notices_1 = This operation <strong>CANNOT</strong> be undone.
+settings.rename_notices_2 = The old name will redirect until it is claimed.
+
settings.update_avatar_success = The organization's avatar has been updated.
settings.delete = Delete Organization
settings.delete_account = Delete This Organization
settings.delete_prompt = The organization will be permanently removed. This <strong>CANNOT</strong> be undone!
+settings.name_confirm = Enter the organization name as confirmation:
+settings.delete_notices_1 = This operation <strong>CANNOT</strong> be undone.
+settings.delete_notices_2 = This operation will permanently delete all the <strong>repositories</strong> of <strong>%s</strong> including code, issues, comments, wiki data and collaborator settings.
+settings.delete_notices_3 = This operation will permanently delete all the <strong>packages</strong> of <strong>%s</strong>.
+settings.delete_notices_4 = This operation will permanently delete all the <strong>projects</strong> of <strong>%s</strong>.
settings.confirm_delete_account = Confirm Deletion
-settings.delete_org_title = Delete Organization
-settings.delete_org_desc = This organization will be deleted permanently. Continue?
+settings.delete_failed = Delete Organization failed because of internal error
+settings.delete_successful = Organization <b>%s</b> has been deleted successfully.
settings.hooks_desc = Add webhooks which will be triggered for <strong>all repositories</strong> under this organization.
settings.labels_desc = Add labels which can be used on issues for <strong>all repositories</strong> under this organization.
@@ -2869,6 +2945,15 @@ view_as_role = View as: %s
view_as_public_hint = You are viewing the README as a public user.
view_as_member_hint = You are viewing the README as a member of this organization.
+worktime = Worktime
+worktime.date_range_start = Start date
+worktime.date_range_end = End date
+worktime.query = Query
+worktime.time = Time
+worktime.by_repositories = By repositories
+worktime.by_milestones = By milestones
+worktime.by_members = By members
+
[admin]
maintenance = Maintenance
dashboard = Dashboard
@@ -3239,8 +3324,6 @@ config.ssh_domain = SSH Server Domain
config.ssh_port = Port
config.ssh_listen_port = Listen Port
config.ssh_root_path = Root Path
-config.ssh_key_test_path = Key Test Path
-config.ssh_keygen_path = Keygen ('ssh-keygen') Path
config.ssh_minimum_key_size_check = Minimum Key Size Check
config.ssh_minimum_key_sizes = Minimum Key Sizes
@@ -3363,6 +3446,8 @@ monitor.previous = Previous Time
monitor.execute_times = Executions
monitor.process = Running Processes
monitor.stacktrace = Stacktrace
+monitor.trace = Trace
+monitor.performance_logs = Performance Logs
monitor.processes_count = %d Processes
monitor.download_diagnosis_report = Download diagnosis report
monitor.desc = Description
@@ -3371,7 +3456,6 @@ monitor.execute_time = Execution Time
monitor.last_execution_result = Result
monitor.process.cancel = Cancel process
monitor.process.cancel_desc = Cancelling a process may cause data loss
-monitor.process.cancel_notices = Cancel: <strong>%s</strong>?
monitor.process.children = Children
monitor.queues = Queues
@@ -3568,7 +3652,8 @@ conda.install = To install the package using Conda, run the following command:
container.details.type = Image Type
container.details.platform = Platform
container.pull = Pull the image from the command line:
-container.digest = Digest:
+container.images = Images
+container.digest = Digest
container.multi_arch = OS / Arch
container.layers = Image Layers
container.labels = Labels
@@ -3671,11 +3756,18 @@ owner.settings.chef.keypair.description = A key pair is necessary to authenticat
secrets = Secrets
description = Secrets will be passed to certain actions and cannot be read otherwise.
none = There are no secrets yet.
-creation = Add Secret
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description = Description
creation.name_placeholder = case-insensitive, alphanumeric characters or underscores only, cannot start with GITEA_ or GITHUB_
creation.value_placeholder = Input any content. Whitespace at the start and end will be omitted.
-creation.success = The secret "%s" has been added.
-creation.failed = Failed to add secret.
+creation.description_placeholder = Enter short description (optional).
+
+save_success = The secret "%s" has been saved.
+save_failed = Failed to save secret.
+
+add_secret = Add secret
+edit_secret = Edit secret
deletion = Remove secret
deletion.description = Removing a secret is permanent and cannot be undone. Continue?
deletion.success = The secret has been removed.
@@ -3753,6 +3845,11 @@ runs.no_workflows.documentation = For more information on Gitea Actions, see <a
runs.no_runs = The workflow has no runs yet.
runs.empty_commit_message = (empty commit message)
runs.expire_log_message = Logs have been purged because they were too old.
+runs.delete = Delete workflow run
+runs.cancel = Cancel workflow run
+runs.delete.description = Are you sure you want to permanently delete this workflow run? This action cannot be undone.
+runs.not_done = This workflow run is not done.
+runs.view_workflow_file = View workflow file
workflow.disable = Disable Workflow
workflow.disable_success = Workflow '%s' disabled successfully.
@@ -3764,6 +3861,7 @@ workflow.not_found = Workflow '%s' not found.
workflow.run_success = Workflow '%s' run successfully.
workflow.from_ref = Use workflow from
workflow.has_workflow_dispatch = This workflow has a workflow_dispatch event trigger.
+workflow.has_no_workflow_dispatch = Workflow '%s' has no workflow_dispatch event trigger.
need_approval_desc = Need approval to run workflows for fork pull request.
@@ -3791,6 +3889,8 @@ deleted.display_name = Deleted Project
type-1.display_name = Individual Project
type-2.display_name = Repository Project
type-3.display_name = Organization Project
+enter_fullscreen = Fullscreen
+exit_fullscreen = Exit Fullscreen
[git.filemode]
changed_filemode = %[1]s → %[2]s
diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini
index cdfe1fb2e5..9359bacc04 100644
--- a/options/locale/locale_es-ES.ini
+++ b/options/locale/locale_es-ES.ini
@@ -52,6 +52,7 @@ webauthn_reload=Recargar
repository=Repositorio
organization=Organización
mirror=Réplica
+issue_milestone=Hito
new_repo=Nuevo repositorio
new_migrate=Nueva migración
new_mirror=Nueva réplica
@@ -332,6 +333,7 @@ show_only_public=Mostrar sólo repositorios públicos
issues.in_your_repos=En tus repositorios
+
[explore]
repos=Repositorios
users=Usuarios
@@ -385,7 +387,6 @@ oauth_signup_submit=Completar Cuenta
oauth_signin_tab=Vincular a una Cuenta Existente
oauth_signin_title=Regístrese para autorizar cuenta vinculada
oauth_signin_submit=Vincular Cuenta
-oauth.signin.error=Hubo un error al procesar la solicitud de autorización. Si este error persiste, póngase en contacto con el administrador del sitio.
oauth.signin.error.access_denied=La solicitud de autorización fue denegada.
oauth.signin.error.temporarily_unavailable=La autorización falló porque el servidor de autenticación no está disponible temporalmente. Inténtalo de nuevo más tarde.
openid_connect_submit=Conectar
@@ -804,7 +805,6 @@ delete_token_success=El token ha sido eliminado. Las aplicaciones que lo usen ya
repo_and_org_access=Acceso al Repositorio y a la Organización
permissions_public_only=Sólo público
permissions_access_all=Todo (público, privado y limitado)
-select_permissions=Seleccionar permisos
permission_no_access=Sin acceso
permission_read=Leídas
permission_write=Lectura y Escritura
@@ -982,9 +982,6 @@ blame_prior=Ver la culpa antes de este cambio
blame.ignore_revs=Ignorando revisiones en <a href="%s">.git-blame-ignore-revs</a>. Haga clic <a href="%s">aquí para saltar</a> y para a la vista normal.
blame.ignore_revs.failed=No se pudieron ignorar las revisiones en <a href="%s">.git-blame-ignore-revs</a>.
-tree_path_not_found_commit=La ruta %[1]s no existe en el commit %[2]s
-tree_path_not_found_branch=La ruta %[1]s no existe en la rama %[2]s
-tree_path_not_found_tag=La ruta %[1]s no existe en la etiqueta %[2]s
transfer.accept=Aceptar transferencia
transfer.reject=Rechazar transferencia
@@ -1112,6 +1109,7 @@ labels=Etiquetas
org_labels_desc=Etiquetas de nivel de la organización que pueden ser utilizadas con <strong>todos los repositorios</strong> bajo esta organización
org_labels_desc_manage=gestionar
+milestone=Hito
milestones=Hitos
commits=Commits
commit=Commit
@@ -1140,7 +1138,6 @@ file_copy_permalink=Copiar Permalink
view_git_blame=Ver la culpa de Git
video_not_supported_in_browser=Su navegador no soporta el tag video de HTML5.
audio_not_supported_in_browser=Su navegador no soporta el tag audio de HTML5.
-stored_lfs=Almacenados con Git LFS
symbolic_link=Enlace simbólico
executable_file=Archivo Ejecutable
commit_graph=Gráfico de commits
@@ -1183,7 +1180,6 @@ editor.update=Actualizar %s
editor.delete=Eliminar %s
editor.patch=Aplicar parche
editor.patching=Parcheando:
-editor.fail_to_apply_patch=`No se puede aplicar el parche "%s"`
editor.new_patch=Nuevo parche
editor.commit_message_desc=Añadir una descripción extendida opcional…
editor.signoff_desc=Añadir un trailer firmado por el committer al final del mensaje de registro de confirmación.
@@ -1201,15 +1197,11 @@ editor.branch_already_exists=La rama "%s" ya existe en este repositorio.
editor.directory_is_a_file=Nombre del directorio "%s" ya se utiliza como nombre de archivo en este repositorio.
editor.file_is_a_symlink=`"%s" es un enlace simbólico. Los enlaces simbólicos no se pueden editar en el editor web`
editor.filename_is_a_directory=Nombre de archivo "%s" ya se utiliza como nombre de directorio en este repositorio.
-editor.file_editing_no_longer_exists=El archivo que se está editando, "%s", ya no existe en este repositorio.
-editor.file_deleting_no_longer_exists=El archivo que se está eliminando, "%s", ya no existe en este repositorio.
editor.file_changed_while_editing=Desde que comenzó a editar, el contenido del archivo ha sido cambiado. <a target="_blank" rel="noopener noreferrer" href="%s">Haga clic aquí</a> para ver qué ha cambiado o <strong>presione confirmar de nuevo</strong> para sobrescribir los cambios.
editor.file_already_exists=Ya existe un archivo llamado "%s" en este repositorio.
editor.commit_empty_file_header=Commit un archivo vacío
editor.commit_empty_file_text=El archivo que estás tratando de commit está vacío. ¿Proceder?
editor.no_changes_to_show=No existen cambios para mostrar.
-editor.fail_to_update_file=Error al actualizar/crear el archivo "%s".
-editor.fail_to_update_file_summary=Mensaje de error
editor.push_rejected_no_message=El cambio fue rechazado por el servidor sin un mensaje. Por favor, compruebe Git Hooks.
editor.push_rejected=El cambio fue rechazado por el servidor. Por favor, comprueba los Git Hooks.
editor.push_rejected_summary=Mensaje completo de rechazo
@@ -1224,6 +1216,7 @@ editor.require_signed_commit=Esta rama requiere un commit firmado
editor.cherry_pick=Hacer Cherry-pick %s en:
editor.revert=Revertir %s en:
+
commits.desc=Ver el historial de cambios de código fuente.
commits.commits=Commits
commits.no_commits=No hay commits en común. "%s" y "%s" tienen historias totalmente diferentes.
@@ -1369,8 +1362,6 @@ issues.filter_project=Proyecto
issues.filter_project_all=Todos los proyectos
issues.filter_project_none=Ningún proyecto
issues.filter_assignee=Asignada a
-issues.filter_assginee_no_select=Todos los asignados
-issues.filter_assginee_no_assignee=Sin asignado
issues.filter_poster=Autor
issues.filter_type=Tipo
issues.filter_type.all_issues=Todas las incidencias
@@ -1485,13 +1476,12 @@ issues.attachment.open_tab='Haga clic para ver "%s" en una pestaña nueva'
issues.attachment.download=`Haga clic para descargar "%s"`
issues.subscribe=Suscribir
issues.unsubscribe=Desuscribirse
-issues.unpin_issue=Desanclar incidencia
+issues.unpin=Desanclar
issues.max_pinned=No puedes anclar más incidencias
issues.pin_comment=anclado este %s
issues.unpin_comment=desanclado este %s
issues.lock=Bloquear conversación
issues.unlock=Desbloquear conversación
-issues.lock.unknown_reason=No se puede bloquear una incidencia con una razón desconocida.
issues.lock_duplicate=Una incidencia no puede ser bloqueada dos veces.
issues.unlock_error=No puede desbloquear una incidencia que no esta bloqueada.
issues.lock_with_reason=bloqueado como <strong>%s</strong> y conversación limitada a colaboradores %s
@@ -1666,7 +1656,6 @@ pulls.add_prefix=Añadir prefijo <strong>%s</strong>
pulls.remove_prefix=Eliminar prefijo <strong>%s</strong>
pulls.data_broken=Este pull request está rota debido a que falta información del fork.
pulls.files_conflicted=Este pull request tiene cambios en conflicto con la rama de destino.
-pulls.is_checking=La comprobación de conflicto de fusión está en progreso. Inténtalo de nuevo en unos momentos.
pulls.is_ancestor=Esta rama ya está incluida en la rama de destino. No hay nada que fusionar.
pulls.is_empty=Los cambios en esta rama ya están en la rama de destino. Esto será un commit vacío.
pulls.required_status_check_failed=Algunos controles requeridos no han tenido éxito.
@@ -2061,8 +2050,6 @@ settings.hooks_desc=Los webhooks automáticamente hacen peticiones HTTP POST a u
settings.webhook_deletion=Eliminar Webhook
settings.webhook_deletion_desc=Eliminar un webhook borra sus ajustes e historial de entrega. ¿Continuar?
settings.webhook_deletion_success=El webhook ha sido eliminado.
-settings.webhook.test_delivery=Test de entrega
-settings.webhook.test_delivery_desc=Prueba este webhook con un evento falso.
settings.webhook.test_delivery_desc_disabled=Para probar este webhook con un evento falso, actívalo.
settings.webhook.request=Petición
settings.webhook.response=Respuesta
@@ -2108,7 +2095,6 @@ settings.event_repository=Repositorio
settings.event_repository_desc=Repositorio creado o eliminado.
settings.event_header_issue=Eventos de incidencias
settings.event_issues=Incidencias
-settings.event_issues_desc=Incidencia abierta, cerrada, reabierta o editada.
settings.event_issue_assign=Incidencia asignada
settings.event_issue_assign_desc=Incidencia asignada o no asignada.
settings.event_issue_label=Incidencia etiquetada
@@ -2119,7 +2105,6 @@ settings.event_issue_comment=Comentario de incidencia
settings.event_issue_comment_desc=Comentario de incidencias creado, editado o borrado.
settings.event_header_pull_request=Eventos de Pull Requests
settings.event_pull_request=Pull Request
-settings.event_pull_request_desc=Pull request abierto, cerrado, reabierto o editado.
settings.event_pull_request_assign=Pull Request asignado
settings.event_pull_request_assign_desc=Pull Request asignado o no asignado.
settings.event_pull_request_label=Pull Request Etiquetado
@@ -2298,7 +2283,6 @@ diff.commit=commit
diff.git-notes=Notas
diff.data_not_available=El contenido del Diff no está disponible
diff.options_button=Opciones de diferencias
-diff.show_diff_stats=Mostrar estadísticas
diff.download_patch=Descargar archivo de parche
diff.download_diff=Descargar archivo de diferencias
diff.show_split_view=Dividir vista
@@ -2495,15 +2479,13 @@ settings.visibility.private_shortname=Privado
settings.update_settings=Actualizar configuración
settings.update_setting_success=Configuración de la organización se han actualizado.
-settings.change_orgname_prompt=Nota: Cambiar el nombre de la organización también cambiará la URL de su organización y liberará el nombre antiguo.
-settings.change_orgname_redirect_prompt=El nombre antiguo se redirigirá hasta que se reclame.
+
+
settings.update_avatar_success=Se ha actualizado el avatar de la organización.
settings.delete=Eliminar organización
settings.delete_account=Eliminar esta organización
settings.delete_prompt=La organización será eliminada permanentemente. ¡Esta acción <strong>NO PUEDE</strong> deshacerse!
settings.confirm_delete_account=Confirmar eliminación
-settings.delete_org_title=Eliminar organización
-settings.delete_org_desc=Esta organización se eliminará permanentemente. ¿Continuar?
settings.hooks_desc=Añadir webhooks que serán ejecutados para <strong>todos los repositorios</strong> de esta organización.
settings.labels_desc=Añadir etiquetas que pueden ser utilizadas en problemas para <strong>todos los repositorios</strong> bajo esta organización.
@@ -2574,6 +2556,7 @@ teams.invite.by=Invitado por %s
teams.invite.description=Por favor, haga clic en el botón de abajo para unirse al equipo.
+
[admin]
dashboard=Panel de control
identity_access=Identidad y acceso
@@ -2911,8 +2894,6 @@ config.ssh_domain=Dominio del servidor SSH
config.ssh_port=Puerto
config.ssh_listen_port=Puerto de escucha
config.ssh_root_path=Ruta raíz
-config.ssh_key_test_path=Ruta de la clave de prueba
-config.ssh_keygen_path=Ruta del generador de claves ('ssh-keygen')
config.ssh_minimum_key_size_check=Tamaño mínimo de la clave de verificación
config.ssh_minimum_key_sizes=Tamaños de clave mínimos
@@ -3218,7 +3199,6 @@ conda.install=Para instalar el paquete usando Conda, ejecute el siguiente comand
container.details.type=Tipo de imagen
container.details.platform=Plataforma
container.pull=Arrastra la imagen desde la línea de comandos:
-container.digest=Resumen:
container.multi_arch=SO / Arquitectura
container.layers=Capas de imagen
container.labels=Etiquetas
@@ -3319,11 +3299,13 @@ owner.settings.chef.keypair.description=Un par de claves es necesario para auten
secrets=Secretos
description=Los secretos pasarán a ciertas acciones y no se podrán leer de otro modo.
none=Todavía no hay secretos.
-creation=Añadir secreto
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Descripción
creation.name_placeholder=sin distinción de mayúsculas, solo carácteres alfanuméricos o guiones bajos, no puede empezar por GITEA_ o GITHUB_
creation.value_placeholder=Introduce cualquier contenido. Se omitirá el espacio en blanco en el inicio y el final.
-creation.success=El secreto "%s" ha sido añadido.
-creation.failed=Error al añadir secreto.
+
+
deletion=Eliminar secreto
deletion.description=Eliminar un secreto es permanente y no se puede deshacer. ¿Continuar?
deletion.success=El secreto ha sido eliminado.
diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini
index 4d90cf9876..c201973a09 100644
--- a/options/locale/locale_fa-IR.ini
+++ b/options/locale/locale_fa-IR.ini
@@ -256,6 +256,7 @@ show_only_public=نمایش دادن موارد عمومی
issues.in_your_repos=در مخازن شما
+
[explore]
repos=مخازن
users=کاربران
@@ -899,7 +900,6 @@ file_too_large=حجم این پرونده بیشتر از آن است Ú©Ù‡ قاØ
file_copy_permalink=پرمالینک را کپی کنید
video_not_supported_in_browser=مرورگر شما از تگ video که در HTML5 تعری٠شده است، پشتیبانی نمی کند.
audio_not_supported_in_browser=مرورگر شما از تگ audio که در HTML5 تعری٠شده است، پشتیبانی نمی کند.
-stored_lfs=ذخیره شده با GIT LFS
symbolic_link=پیوند نمادین
commit_graph=نمودار کامیت
commit_graph.select=انتخاب برنچها
@@ -943,13 +943,13 @@ editor.file_changed_while_editing=محتوای پرونده تغییر میکنØ
editor.commit_empty_file_header=کامیت کردن یک پرونده خالی
editor.commit_empty_file_text=ÙØ§ÛŒÙ„ÛŒ Ú©Ù‡ درخواست ارسال دارید خالی است. ادامه بدم?
editor.no_changes_to_show=تغییری برای نمایش وجود ندارد.
-editor.fail_to_update_file_summary=متن خطا:
editor.push_rejected_summary=متن کامل پیام دلیل رد شدن:
editor.add_subdir=Ø§ÙØ²ÙˆØ¯Ù† پوشه…
editor.no_commit_to_branch=نمی‌توان به طور مستقیم درمورد شاخه نطر داد زیرا:
editor.user_no_push_to_branch=کاربر نمیتواند به شاخه ارسال کند
editor.require_signed_commit=شاخه یک کامیت امضا شده لازم دارد
+
commits.desc=تاریخچه تغییرات کد منبع را مرور کنید.
commits.commits=کامیت‌ها
commits.nothing_to_compare=این شاخه ها برابرند.
@@ -1058,8 +1058,6 @@ issues.filter_label_no_select=تمامی برچسب‎ها
issues.filter_milestone=نقطه عطÙ
issues.filter_project_none=هیچ پروژه ثبت نشده
issues.filter_assignee=مسئول رسیدگی
-issues.filter_assginee_no_select=تمامی مسئولان رسیدگی
-issues.filter_assginee_no_assignee=بدون مسئول رسیدگی
issues.filter_type=نوع
issues.filter_type.all_issues=همه مسائل
issues.filter_type.assigned_to_you=به شما محول شده
@@ -1147,7 +1145,6 @@ issues.subscribe=مشترک شدن
issues.unsubscribe=لغو اشتراک
issues.lock=Ù‚ÙÙ„ کردن مکالمه
issues.unlock=بازکردن مکالمه
-issues.lock.unknown_reason=نمیتوانید این موضوع را بدون دلیل ببندید.
issues.lock_duplicate=یک مسئله دومرتبه نمی‎تواند بسته شود.
issues.unlock_error=این مسئله نمی‎تواند باز شود لذا Ù‚ÙÙ„ نشده بود.
issues.lock_with_reason=Ù‚ÙÙ„ شده با عنوان <strong>%s</strong> Ùˆ مکالمه همکاران %s محدود شده است
@@ -1287,7 +1284,6 @@ pulls.add_prefix=اضاÙÙ‡ کردن پیشوند <strong>%s</strong>
pulls.remove_prefix=حذ٠پیشوند <strong>%s</strong>
pulls.data_broken=این تقاضای واکشی به دلیل از دست Ø±ÙØªÙ† اطلاعات انشعاب با شکست مواجه شد.
pulls.files_conflicted=این تقاضای واکشی دارای تغییراتی است که با شاخه هد٠تداخل دارد.
-pulls.is_checking=در حال پردازش تداخل در ادغام می‌باشد. Ù„Ø·ÙØ§Ù‹ لحظاتی بعد امتحان کنید.
pulls.required_status_check_failed=برخی بررسی های ضروری موÙقیت آمیز نبود.
pulls.required_status_check_missing=برخی بررسی های موردنیاز از قلم Ø§ÙØªØ§Ø¯Ù‡ است.
pulls.required_status_check_administrator=مثل یک مدیر، ممکن است شما این تقاضای واکشی را مسکوت بگذارید.
@@ -1595,8 +1591,6 @@ settings.hooks_desc=هوک تحت وب به صورت خودکار درخواست
settings.webhook_deletion=حذ٠Webhook
settings.webhook_deletion_desc=حذ٠هوک تحت وب موجب حذ٠تنظیمات آن و تاریخچه تحویل آن می‌شود. همچنان ادامه می‌دهید؟
settings.webhook_deletion_success=هوک تحت وب حذ٠شد.
-settings.webhook.test_delivery=امتحان‌کردن تحویل
-settings.webhook.test_delivery_desc=Webhook را با رویداد جعلی امتحان کنید.
settings.webhook.request=درخواست
settings.webhook.response=پاسخ
settings.webhook.headers=سربرگ‎ها
@@ -1636,7 +1630,6 @@ settings.event_repository=مخزن
settings.event_repository_desc=مخزن ساخته یا حذ٠شد.
settings.event_header_issue=رویدادهای مساله
settings.event_issues=مسائل
-settings.event_issues_desc=مساله باز شد، بسته شد، دوباره باز شد، یا ویرایش شد.
settings.event_issue_assign=مساله تعیین شد
settings.event_issue_assign_desc=مساله واگذار شده یا واگذار نشده است.
settings.event_issue_label=مساله برجسب خورد
@@ -1647,7 +1640,6 @@ settings.event_issue_comment=دیدگاه های مسئله
settings.event_issue_comment_desc=نظر در مسئله ایجاد شد، ویرایش شد یا حذ٠شد.
settings.event_header_pull_request=رویداد های درخواست pull
settings.event_pull_request=تقاضای واکشی
-settings.event_pull_request_desc=درخواست pull باز شد، بسته شد، دوباره باز شد، یا ویرایش شد.
settings.event_pull_request_assign=درخواست pull واگذار شد
settings.event_pull_request_assign_desc=درخواست pull واگذاری شده یا واگذاری نشده.
settings.event_pull_request_label=درخواست pull برچسب دار شد
@@ -1776,7 +1768,6 @@ diff.commit=کامیت
diff.git-notes=یادداشت‌ها
diff.data_not_available=محتوای ØªÙØ§ÙˆØª ها در دسترس نیست
diff.options_button=تنظیمات (diff) تغییرات
-diff.show_diff_stats=نمایش وضعیت
diff.download_patch=دانلود پرونده وصله
diff.download_diff=دانلود ÙØ§ÛŒÙ„ تغییرات diff
diff.show_split_view=مشاهده تقسیم شده
@@ -1925,14 +1916,13 @@ settings.visibility.private_shortname=پوشیده
settings.update_settings=به‌ روزرسانی تنظیمات
settings.update_setting_success=تنظیمات این سازمان به‌روز شد.
-settings.change_orgname_redirect_prompt=نام قدیمی تا زمانی که ادعا شود تغییر مسیر می دهد.
+
+
settings.update_avatar_success=آواتار این سازمان به‌روز شد.
settings.delete=حذ٠سازمان
settings.delete_account=حذ٠این سازمان
settings.delete_prompt=سازمان برای همیشه حذ٠خواهد شد. این قابل برگشت <strong>نخواهد بود</strong>!
settings.confirm_delete_account=تاییدیه حذÙ
-settings.delete_org_title=حذ٠سازمان
-settings.delete_org_desc=سازمان برای همیشه حذ٠خواهد شد. آیا همچنان ادامه می‌دهید؟
settings.hooks_desc=Ø§ÙØ²ÙˆØ¯Ù† webhook های Ú©Ù‡ برای<strong> تمام مخازن</strong> این سازمان اجرا میشود.
settings.labels_desc=تگ هایی را اضاÙÙ‡ کنید Ú©Ù‡ می‌توانند برای مشکلات <strong>همه مخازن</strong> تحت این سازمان Ø§Ø³ØªÙØ§Ø¯Ù‡ شوند.
@@ -1993,6 +1983,7 @@ teams.all_repositories_write_permission_desc=این تیم دسترسی<strong>
teams.all_repositories_admin_permission_desc=این تیم دسترسی<strong> مدیر </strong> به <strong> مخازن همه</strong> را Ù…ÛŒ بخشد: اعضا Ù…ÛŒ توانند مخازن را بخواند، همکار Ùˆ مخزن اضاÙÙ‡ کنند.
+
[admin]
dashboard=پیشخوان
users=حساب کاربران
@@ -2280,8 +2271,6 @@ config.ssh_domain=دامنه سرور SSH
config.ssh_port=درگاه (پورت)
config.ssh_listen_port=گوش دادن به پورت
config.ssh_root_path=مسیر ریشه
-config.ssh_key_test_path=مسیر کلید آزمایش
-config.ssh_keygen_path=مسیر ÙØ§ÛŒÙ„ ssh-keygen
config.ssh_minimum_key_size_check=بررسی حداقل طول کلید
config.ssh_minimum_key_sizes=حداقل اندازه‌ی کلید ها
@@ -2513,6 +2502,11 @@ owner.settings.cleanuprules.enabled=ÙØ¹Ø§Ù„ شده
[secrets]
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=شرح
+
+
+
[actions]
diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini
index b5fa5c8afc..fc48f1b7aa 100644
--- a/options/locale/locale_fi-FI.ini
+++ b/options/locale/locale_fi-FI.ini
@@ -49,6 +49,7 @@ webauthn_reload=Päivitä
repository=Repo
organization=Organisaatio
mirror=Peili
+issue_milestone=Merkkipaalu
new_repo=Uusi repo
new_migrate=Uusi migraatio
new_mirror=Uusi peilaus
@@ -265,6 +266,7 @@ show_only_public=Näytetään vain julkiset
issues.in_your_repos=Repoissasi
+
[explore]
repos=Repot
users=Käyttäjät
@@ -720,6 +722,7 @@ projects=Projektit
packages=Paketit
labels=Tunnisteet
+milestone=Merkkipaalu
milestones=Merkkipaalut
commits=Commitit
commit=Commit
@@ -761,6 +764,7 @@ editor.no_changes_to_show=Ei muutoksia näytettäväksi.
editor.add_subdir=Lisää hakemisto…
editor.require_signed_commit=Haara vaatii vahvistetun commitin
+
commits.commits=Commitit
commits.nothing_to_compare=Nämä haarat vastaavat toisiaan.
commits.search_all=Kaikki haarat
@@ -1093,7 +1097,6 @@ settings.teams=Tiimit
settings.add_team=Lisää tiimi
settings.add_webhook=Lisää webkoukku
settings.webhook_deletion=Poista webkoukku
-settings.webhook.test_delivery=Testitoimitus
settings.webhook.request=Pyyntö
settings.webhook.response=Vastaus
settings.webhook.headers=Otsikot
@@ -1125,7 +1128,6 @@ settings.event_repository=Repo
settings.event_repository_desc=Repo luotu tai poistettu.
settings.event_header_issue=Ongelmien tapahtumat
settings.event_issues=Ongelmat
-settings.event_issues_desc=Ongelma avattu, suljettu, avattu uudelleen tai muokattu.
settings.event_issue_assign=Ongelma määritetty
settings.event_issue_assign_desc=Ongelma osoitettu tai osoitus poistettu.
settings.event_issue_label_desc=Ongelman tunnisteet päivitetty tai tyhjennetty.
@@ -1315,11 +1317,12 @@ settings.visibility.private=Yksityinen (näkyvä vain organisaation jäsenille)
settings.visibility.private_shortname=Yksityinen
settings.update_settings=Päivitä asetukset
+
+
settings.delete=Poista organisaatio
settings.delete_account=Poista tämä organisaatio
settings.delete_prompt=Organisaatio poistetaan pysyvästi, ja tätä <strong>EI VOI</strong> peruuttaa myöhemmin!
settings.confirm_delete_account=Vahvista poisto
-settings.delete_org_title=Poista organisaatio
settings.hooks_desc=Lisää webkoukkuja, jotka suoritetaan <strong>kaikissa repoissa</strong> tässä organisaatiossa.
@@ -1361,6 +1364,7 @@ teams.members.none=Ei jäseniä tässä tiimissä.
teams.all_repositories=Kaikki repot
+
[admin]
dashboard=Kojelauta
users=Käyttäjätilit
@@ -1534,8 +1538,6 @@ config.ssh_enabled=Käytössä
config.ssh_port=Portti
config.ssh_listen_port=Kuuntele porttia
config.ssh_root_path=Juuren polku
-config.ssh_key_test_path=Polku jossa avaimet testataan
-config.ssh_keygen_path=Keygen ('ssh-keygen') polku
config.ssh_minimum_key_size_check=Avaimen vähimmäiskoko tarkistus
config.ssh_minimum_key_sizes=Avaimen vähimmäiskoot
@@ -1691,6 +1693,11 @@ owner.settings.cleanuprules.enabled=Käytössä
[secrets]
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Kuvaus
+
+
+
[actions]
diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini
index 743b766256..b72eb2bb7f 100644
--- a/options/locale/locale_fr-FR.ini
+++ b/options/locale/locale_fr-FR.ini
@@ -46,7 +46,7 @@ webauthn_unsupported_browser=Votre navigateur ne prend actuellement pas en charg
webauthn_error_unknown=Une erreur indéterminée s'est produite. Veuillez réessayer.
webauthn_error_insecure=`WebAuthn ne prend en charge que les connexions sécurisées. Pour les tests via HTTP, vous pouvez utiliser l'origine "localhost" ou "127.0.0.1"`
webauthn_error_unable_to_process=Le serveur n'a pas pu traiter votre demande.
-webauthn_error_duplicated=La clé de sécurité n'est pas autorisée pour cette demande. Veuillez vous assurer que la clé n'est pas déjà enregistrée.
+webauthn_error_duplicated=La clé de sécurité n’est pas autorisée pour cette demande. Veuillez vous assurer que la clé n’est pas déjà enregistrée.
webauthn_error_empty=Vous devez définir un nom pour cette clé.
webauthn_error_timeout=Le délai d'attente imparti a été atteint avant que votre clé ne puisse être lue. Veuillez recharger la page pour réessayer.
webauthn_reload=Recharger
@@ -54,6 +54,7 @@ webauthn_reload=Recharger
repository=Dépôt
organization=Organisation
mirror=Miroir
+issue_milestone=Jalon
new_repo=Nouveau dépôt
new_migrate=Nouvelle migration
new_mirror=Nouveau miroir
@@ -112,9 +113,11 @@ copy_type_unsupported=Ce type de fichier ne peut pas être copié
write=Écrire
preview=Aperçu
loading=Chargement…
+files=Fichiers
error=Erreur
error404=La page que vous essayez d'atteindre <strong>n'existe pas</strong> ou <strong>vous n'êtes pas autorisé</strong> à la voir.
+error503=Le serveur n’a pas pu répondre à votre requête. Veuillez réessayer plus tard.
go_back=Retour
invalid_data=Données invalides : %v
@@ -127,6 +130,7 @@ pin=Épingler
unpin=Désépingler
artifacts=Artefacts
+expired=Expiré
confirm_delete_artifact=Êtes-vous sûr de vouloir supprimer l‘artefact « %s » ?
archived=Archivé
@@ -168,6 +172,10 @@ search=Rechercher…
type_tooltip=Type de recherche
fuzzy=Approximative
fuzzy_tooltip=Inclure également les résultats proches de la recherche
+words=Mots
+words_tooltip=Inclure uniquement les résultats qui correspondent exactement aux mots recherchés
+regexp=Regexp
+regexp_tooltip=Inclure uniquement les résultats qui correspondent à l’expression régulière recherchée
exact=Exact
exact_tooltip=Inclure uniquement les résultats qui correspondent exactement au terme de recherche
repo_kind=Chercher des dépôts…
@@ -244,6 +252,7 @@ license_desc=Venez récupérer <a target="_blank" rel="noopener noreferrer" href
[install]
install=Installation
+installing_desc=Installation en cours, veuillez patienter…
title=Configuration initiale
docker_helper=Si vous exécutez Gitea dans Docker, veuillez lire la <a target="_blank" rel="noopener noreferrer" href="%s">documentation</a> avant de modifier les paramètres.
require_db_desc=Gitea nécessite MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (avec le protocole MySQL).
@@ -383,6 +392,13 @@ show_only_public=Afficher uniquement les dépôts publics
issues.in_your_repos=Dans vos dépôts
+guide_title=Aucune activité
+guide_desc=Vous n’êtes actuellement abonné à aucun dépôt ou utilisateur et il n’y a donc aucun contenu à afficher. Les liens ci-dessous vous permettront d’explorer des dépôts ou des utilisateurs susceptibles de vous intéresser.
+explore_repos=Explorer des dépôts
+explore_users=Explorer des utilisateurs
+empty_org=Il n’y a pas encore d’organisations.
+empty_repo=Il n’y a pas encore de dépôts.
+
[explore]
repos=Dépôts
users=Utilisateurs
@@ -405,6 +421,7 @@ remember_me.compromised=Le jeton de connexion n’est plus valide, ce qui peut i
forgot_password_title=Mot de passe oublié
forgot_password=Mot de passe oublié ?
need_account=Besoin d‘un compte ?
+sign_up_tip=Vous êtes en train d’enregistrer le premier compte du système, doté des privilèges administrateur. Veuillez conserver précieusement ses nom d’utilisateur et mot de passe. En cas d’oublie, consultez la documentation de Gitea pour en récupérer l’accès.
sign_up_now=Inscrivez-vous dès maintenant !
sign_up_successful=Le compte a été créé avec succès. Bienvenue !
confirmation_mail_sent_prompt_ex=Un nouveau courriel de confirmation a été envoyé à <b>%s</b>. Veuillez vérifier votre boîte de réception dans la prochaine %s pour terminer le processus d’inscription. Si votre adresse courriel est incorrecte, vous pouvez vous reconnecter et la modifier.
@@ -435,6 +452,7 @@ use_scratch_code=Utiliser un code de secours
twofa_scratch_used=Vous avez utilisé votre code de secours. Vous avez été redirigé vers cette page de configuration afin de supprimer l'authentification à deux facteurs de votre appareil ou afin de générer un nouveau code de secours.
twofa_passcode_incorrect=Votre code d’accès n’est pas correct. Si vous avez égaré votre appareil, utilisez votre code de secours pour vous connecter.
twofa_scratch_token_incorrect=Votre code de secours est incorrect.
+twofa_required=Vous devez configurer l’authentification à deux facteurs pour avoir accès aux dépôts, ou essayer de vous reconnecter.
login_userpass=Connexion
login_openid=OpenID
oauth_signup_tab=Créer un compte
@@ -443,7 +461,7 @@ oauth_signup_submit=Finaliser la création du compte
oauth_signin_tab=Lier à un compte existant
oauth_signin_title=Connectez-vous pour autoriser le compte lié
oauth_signin_submit=Lier un compte
-oauth.signin.error=Une erreur s'est produite lors du traitement de la demande d'autorisation. Si cette erreur persiste, veuillez contacter l'administrateur du site.
+oauth.signin.error.general=Une erreur s’est produite lors du traitement de la demande d’autorisation : %s. Si l’erreur persiste, veuillez contacter l’administrateur du site.
oauth.signin.error.access_denied=La demande d'autorisation a été refusée.
oauth.signin.error.temporarily_unavailable=L'autorisation a échoué car le serveur d'authentification est temporairement indisponible. Veuillez réessayer plus tard.
oauth_callback_unable_auto_reg=L’inscription automatique est activée, mais le fournisseur OAuth2 %[1]s a signalé des champs manquants : %[2]s, impossible de créer un compte automatiquement, veuillez créer ou lier un compte, ou bien contacter l’administrateur du site.
@@ -468,7 +486,7 @@ sspi_auth_failed=Échec de l'authentification SSPI
password_pwned=Le mot de passe que vous avez choisi <a target="_blank" rel="noopener noreferrer" href="%s">fait partit des mots de passe ayant fuité</a> sur internet. Veuillez réessayer avec un mot de passe différent et considérez remplacer ce mot de passe si vous l’utilisez ailleurs.
password_pwned_err=Impossible d'envoyer la demande à HaveIBeenPwned
last_admin=Vous ne pouvez pas supprimer ce compte car au moins un administrateur est requis.
-signin_passkey=Se connecter avec une clé d’identification (passkey)
+signin_passkey=Se connecter avec une clé d’accès (passkey)
back_to_sign_in=Revenir à la page de connexion
[mail]
@@ -716,8 +734,8 @@ public_profile=Profil public
biography_placeholder=Parlez-nous un peu de vous ! (Vous pouvez utiliser Markdown)
location_placeholder=Partagez votre position approximative avec d'autres personnes
profile_desc=Contrôlez comment votre profil est affiché aux autres utilisateurs. Votre adresse courriel principale sera utilisée pour les notifications, la récupération de mot de passe et les opérations Git basées sur le Web.
-password_username_disabled=Vous n’êtes pas autorisé à modifier leur nom d’utilisateur. Veuillez contacter l’administrateur de votre site pour plus de détails.
-password_full_name_disabled=Vous n’êtes pas autorisé à modifier leur nom complet. Veuillez contacter l’administrateur du site pour plus de détails.
+password_username_disabled=Vous n’êtes pas autorisé à modifier votre nom d’utilisateur. Veuillez contacter l’administrateur de votre site pour plus de détails.
+password_full_name_disabled=Vous n’êtes pas autorisé à modifier votre nom complet. Veuillez contacter l’administrateur du site pour plus de détails.
full_name=Nom complet
website=Site Web
location=Localisation
@@ -817,7 +835,7 @@ manage_ssh_keys=Gérer les clés SSH
manage_ssh_principals=Gérer les certificats principaux SSH
manage_gpg_keys=Gérer les clés GPG
add_key=Ajouter une clé
-ssh_desc=Ces clefs SSH publiques sont associées à votre compte. Les clefs privées correspondantes permettent l'accès complet à vos repos.
+ssh_desc=Ces clés SSH publiques sont associées à votre compte. Les clés privées correspondantes permettent l’accès complet à vos dépôts.
principal_desc=Ces Principaux de certificats SSH sont associés à votre compte et permettent un accès complet à vos dépôts.
gpg_desc=Ces clés GPG sont associées à votre compte. Conservez-les en lieu sûr, car elles permettent de vérifier vos révisions.
ssh_helper=<strong>Besoin d'aide ?</strong> Consultez le guide de GitHub pour <a href="%s">créer vos propres clés SSH</a> ou <a href="%s">résoudre les problèmes courants</a> que vous pourriez rencontrer en utilisant SSH.
@@ -908,11 +926,13 @@ delete_token_success=Ce jeton a été supprimé. Les applications l'utilisant n'
repo_and_org_access=Accès aux Organisations et Dépôts
permissions_public_only=Publique uniquement
permissions_access_all=Tout (public, privé et limité)
-select_permissions=Sélectionner les autorisations
permission_not_set=Non défini
permission_no_access=Aucun accès
permission_read=Lecture
permission_write=Lecture et écriture
+permission_anonymous_read=Consultation anonyme
+permission_everyone_read=Consultation collective
+permission_everyone_write=Participation collective
access_token_desc=Les autorisations des jetons sélectionnées se limitent aux <a %s>routes API</a> correspondantes. Lisez la <a %s>documentation</a> pour plus d’informations.
at_least_one_permission=Vous devez sélectionner au moins une permission pour créer un jeton.
permissions_list=Autorisations :
@@ -968,7 +988,7 @@ passcode_invalid=Le mot de passe est invalide. Réessayez.
twofa_enrolled=L’authentification à deux facteurs a été activée pour votre compte. Gardez votre clé de secours (%s) en lieu sûr, car il ne vous sera montré qu'une seule fois.
twofa_failed_get_secret=Impossible d'obtenir le secret.
-webauthn_desc=Les clefs de sécurité sont des dispositifs matériels contenant des clefs cryptographiques. Elles peuvent être utilisées pour l’authentification à deux facteurs. La clef de sécurité doit supporter le standard <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a>.
+webauthn_desc=Les clés de sécurité sont des dispositifs matériels contenant des clés cryptographiques. Elles peuvent être utilisées pour l’authentification à deux facteurs. La clé de sécurité doit supporter le standard <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a>.
webauthn_register_key=Ajouter une clé de sécurité
webauthn_nickname=Pseudonyme
webauthn_delete_key=Retirer la clé de sécurité
@@ -1015,6 +1035,9 @@ new_repo_helper=Un dépôt contient tous les fichiers d’un projet, ainsi que l
owner=Propriétaire
owner_helper=Certaines organisations peuvent ne pas apparaître dans la liste déroulante en raison d'une limite maximale du nombre de dépôts.
repo_name=Nom du dépôt
+repo_name_profile_public_hint=.profile est un dépôt spécial que vous pouvez utiliser pour ajouter un README.md à votre profil public d’organisation, visible à tout le monde. Assurez-vous qu’il soit public et initialisez-le avec un README dans le répertoire de profil pour commencer.
+repo_name_profile_private_hint=.profile-private est un dépôt spécial que vous pouvez utiliser pour ajouter un README.md à votre profil d’organisation, visible uniquement aux membres de l’organisation. Assurez-vous qu’il soit privé et initialisez-le avec un README dans le répertoire de profil pour commencer.
+repo_name_helper=Idéalement, le nom d’un dépôt devrait être court, mémorisable et unique. Vous pouvez personnaliser votre profil ou celui de votre organisation en créant un dépôt nommé « .profile » ou « .profile-private » et contenant un README.md.
repo_size=Taille du dépôt
template=Modèle
template_select=Répliquer un modèle
@@ -1111,9 +1134,7 @@ blame.ignore_revs=Les révisions dans <a href="%s">.git-blame-ignore-revs</a> so
blame.ignore_revs.failed=Impossible d'ignorer les révisions dans <a href="%s">.git-blame-ignore-revs</a>.
user_search_tooltip=Affiche un maximum de 30 utilisateurs
-tree_path_not_found_commit=Le chemin %[1]s n’existe pas dans la révision %[2]s.
-tree_path_not_found_branch=Le chemin %[1]s n’existe pas dans la branche %[2]s.
-tree_path_not_found_tag=Le chemin %[1]s n’existe pas dans l’étiquette %[2]s.
+tree_path_not_found=Le chemin %[1]s n’existe pas dans %[2]s
transfer.accept=Accepter le transfert
transfer.accept_desc=Transférer à « %s »
@@ -1124,6 +1145,7 @@ transfer.no_permission_to_reject=Vous n’êtes pas autorisé à rejeter ce tran
desc.private=Privé
desc.public=Publique
+desc.public_access=Accès public
desc.template=Modèle
desc.internal=Interne
desc.archived=Archivé
@@ -1170,7 +1192,7 @@ migrate_items_releases=Publications
migrate_repo=Migrer le dépôt
migrate.clone_address=Migrer/Cloner depuis une URL
migrate.clone_address_desc=L'URL HTTP(S) ou Git "clone" d'un dépôt existant
-migrate.github_token_desc=Vous pouvez mettre un ou plusieurs jetons séparés par des virgules ici pour rendre la migration plus rapide en raison de la limite de débit de l'API GitHub. ATTENTION : Abuser de cette fonctionnalité peut enfreindre la politique du fournisseur de services et entraîner un blocage de compte.
+migrate.github_token_desc=Vous pouvez mettre un ou plusieurs jetons séparés par des virgules ici pour rendre la migration plus rapide et contourner la limite de débit de l’API GitHub. ATTENTION : Abuser de cette fonctionnalité peut enfreindre la politique du fournisseur de service et entraîner un blocage de votre compte.
migrate.clone_local_path=ou un chemin serveur local
migrate.permission_denied=Vous n'êtes pas autorisé à importer des dépôts locaux.
migrate.permission_denied_blocked=Vous ne pouvez pas importer depuis des hôtes interdits, veuillez demander à l'administrateur de vérifier les paramètres ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS.
@@ -1207,6 +1229,7 @@ migrate.migrating_issues=Migration des tickets
migrate.migrating_pulls=Migration des demandes d'ajout
migrate.cancel_migrating_title=Annuler la migration
migrate.cancel_migrating_confirm=Voulez-vous abandonner cette migration ?
+migration_status=Statut de la migration
mirror_from=miroir de
forked_from=bifurqué depuis
@@ -1231,6 +1254,7 @@ create_new_repo_command=Création d'un nouveau dépôt en ligne de commande
push_exist_repo=Soumission d'un dépôt existant par ligne de commande
empty_message=Ce dépôt n'a pas de contenu.
broken_message=Les données git de ce dépôt ne peuvent pas être lues. Contactez l'administrateur de cette instance ou supprimez ce dépôt.
+no_branch=Ce dépôt n’a aucune branche.
code=Code
code.desc=Accéder au code source, fichiers, révisions et branches.
@@ -1250,6 +1274,7 @@ labels=Labels
org_labels_desc=Les labels d'une organisation peuvent être utilisés avec <strong>tous les dépôts</strong> de cette organisation.
org_labels_desc_manage=gérer
+milestone=Jalon
milestones=Jalons
commits=Révisions
commit=Révision
@@ -1283,7 +1308,6 @@ file_copy_permalink=Copier le lien permanent
view_git_blame=Voir Git Blâme
video_not_supported_in_browser=Votre navigateur ne supporte pas la balise « vidéo » HTML5.
audio_not_supported_in_browser=Votre navigateur ne supporte pas la balise « audio » HTML5.
-stored_lfs=Stocké avec Git LFS
symbolic_link=Lien symbolique
executable_file=Fichiers exécutables
vendored=Externe
@@ -1309,7 +1333,9 @@ editor.upload_file=Téléverser un fichier
editor.edit_file=Modifier le fichier
editor.preview_changes=Aperçu des modifications
editor.cannot_edit_lfs_files=Les fichiers LFS ne peuvent pas être modifiés dans l'interface web.
+editor.cannot_edit_too_large_file=Le fichier est trop gros pour être édité.
editor.cannot_edit_non_text_files=Les fichiers binaires ne peuvent pas être édités dans l'interface web.
+editor.file_not_editable_hint=Mais vous pouvez toujours le renommer ou le déplacer.
editor.edit_this_file=Modifier le fichier
editor.this_file_locked=Le fichier est verrouillé
editor.must_be_on_a_branch=Vous devez être sur une branche pour appliquer ou proposer des modifications à ce fichier.
@@ -1321,7 +1347,7 @@ editor.name_your_file=Nommez votre fichier…
editor.filename_help=Ajoutez un dossier en entrant son nom suivi d'une barre oblique ('/'). Supprimez un dossier avec un retour arrière au début du champ.
editor.or=ou
editor.cancel_lower=Annuler
-editor.commit_signed_changes=Réviser les changements (signé)
+editor.commit_signed_changes=Réviser les changements signés
editor.commit_changes=Réviser les changements
editor.add_tmpl=Ajouter {filename}
editor.add=Ajouter %s
@@ -1329,7 +1355,7 @@ editor.update=Actualiser %s
editor.delete=Supprimer %s
editor.patch=Appliquer le correctif
editor.patching=Correction:
-editor.fail_to_apply_patch=`Impossible d'appliquer le correctif "%s"`
+editor.fail_to_apply_patch=Impossible d’appliquer le correctif.
editor.new_patch=Nouveau correctif
editor.commit_message_desc=Ajouter une description détaillée facultative…
editor.signoff_desc=Créditer l'auteur "Signed-off-by:" en pied de révision.
@@ -1342,13 +1368,14 @@ editor.new_branch_name_desc=Nouveau nom de la branche…
editor.cancel=Annuler
editor.filename_cannot_be_empty=Le nom de fichier ne peut être vide.
editor.filename_is_invalid=Le nom du fichier est invalide : "%s".
+editor.commit_email=Courriel de la révision
+editor.invalid_commit_email=Le courriel pour la révision n’est pas valide.
editor.branch_does_not_exist=La branche "%s" n'existe pas dans ce dépôt.
editor.branch_already_exists=La branche "%s" existe déjà dans ce dépôt.
editor.directory_is_a_file=Le nom de dossier "%s" est déjà utilisé comme nom de fichier dans ce dépôt.
editor.file_is_a_symlink=`« %s » est un lien symbolique. Ce type de fichiers ne peut être modifié dans l'éditeur web.`
editor.filename_is_a_directory=« %s » est déjà utilisé comme nom de dossier dans ce dépôt.
-editor.file_editing_no_longer_exists=Impossible de modifier le fichier « %s » car il n’existe plus dans ce dépôt.
-editor.file_deleting_no_longer_exists=Impossible de supprimer le fichier « %s » car il n’existe plus dans ce dépôt.
+editor.file_modifying_no_longer_exists=Le fichier en cours d’édition, '%s', n’existe plus dans ce dépôt.
editor.file_changed_while_editing=Le contenu du fichier a changé depuis que vous avez commencé à éditer. <a target="_blank" rel="noopener noreferrer" href="%s">Cliquez ici</a> pour voir les changements ou <strong>soumettez de nouveau</strong> pour les écraser.
editor.file_already_exists=Un fichier nommé "%s" existe déjà dans ce dépôt.
editor.commit_id_not_matching=L’ID de la révision ne correspond pas à l’ID lorsque vous avez commencé à éditer. Faites une révision dans une branche de correctif puis fusionnez.
@@ -1356,8 +1383,6 @@ editor.push_out_of_date=Cet envoi semble être obsolète.
editor.commit_empty_file_header=Réviser un fichier vide
editor.commit_empty_file_text=Le fichier que vous allez réviser est vide. Continuer ?
editor.no_changes_to_show=Il n’y a aucune modification à afficher.
-editor.fail_to_update_file=Impossible de mettre à jour/créer le fichier "%s".
-editor.fail_to_update_file_summary=Message d'erreur :
editor.push_rejected_no_message=La modification a été rejetée par le serveur sans message. Veuillez vérifier les Git Hooks.
editor.push_rejected=La modification a été rejetée par le serveur. Veuillez vérifier vos Git Hooks.
editor.push_rejected_summary=Message de rejet complet :
@@ -1371,6 +1396,15 @@ editor.user_no_push_to_branch=L'utilisateur ne peut pas pousser vers la branche
editor.require_signed_commit=Cette branche nécessite une révision signée
editor.cherry_pick=Picorer %s vers:
editor.revert=Rétablir %s sur:
+editor.failed_to_commit=Impossible de réviser les modifications.
+editor.failed_to_commit_summary=Message d’erreur :
+
+editor.fork_create=Bifurquer le Dépôt pour proposer vos modifications
+editor.fork_create_description=Vous ne pouvez pas modifier ce dépôt directement. Cependant, vous pouvez bifurquer ce dépôt, et créer une demande d’ajout avec vos contributions.
+editor.fork_edit_description=Vous ne pouvez pas modifier ce dépôt directement. Les modifications seront écrites sur une bifurcation <b>%s</b>, et ainsi faire une demande d’ajout.
+editor.fork_not_editable=Vous avez bifurqué ce dépôt mais votre copie n’est pas modifiable.
+editor.fork_failed_to_push_branch=Impossible de pousser la branche %s vers votre dépôt.
+editor.fork_branch_exists=La branche « %s » existe déjà dans votre bifurcation, veuillez choisir un nouveau nom de branche.
commits.desc=Naviguer dans l'historique des modifications.
commits.commits=Révisions
@@ -1390,6 +1424,7 @@ commits.signed_by_untrusted_user_unmatched=Signature discordante de l'auteur de
commits.gpg_key_id=ID de la clé GPG
commits.ssh_key_fingerprint=Empreinte numérique de la clé SSH
commits.view_path=Voir à ce point de l'historique
+commits.view_file_diff=Voir les modifications du fichier dans cette révision
commit.operations=Opérations
commit.revert=Rétablir
@@ -1450,6 +1485,8 @@ issues.filter_milestones=Filtrer le jalon
issues.filter_projects=Filtrer par projet
issues.filter_labels=Filtrer par labels
issues.filter_reviewers=Filtrer par évaluateur
+issues.filter_no_results=Aucun résultat
+issues.filter_no_results_placeholder=Essayez d’ajuster vos filtres de recherche.
issues.new=Nouveau ticket
issues.new.title_empty=Le titre ne peut pas être vide
issues.new.labels=Labels
@@ -1496,7 +1533,7 @@ issues.remove_labels=a supprimé les labels %s %s.
issues.add_remove_labels=a ajouté le label %s et supprimé %s %s.
issues.add_milestone_at=`a ajouté ça au jalon <b>%s</b> %s.`
issues.add_project_at=`a ajouté ça au projet <b>%s</b> %s.`
-issues.move_to_column_of_project=`a déplacé ça vers %s dans %s sur %s`
+issues.move_to_column_of_project=`a déplacé ça vers %s dans %s %s.`
issues.change_milestone_at=`a remplacé le jalon <b><strike>%s</strike></b> par <b>%s</b> %s.`
issues.change_project_at=`a remplacé le projet <b><strike>%s</strike></b> par <b>%s</b> %s.`
issues.remove_milestone_at=`a supprimé ça du jalon <b>%s</b> %s.`
@@ -1525,13 +1562,14 @@ issues.filter_project=Projet
issues.filter_project_all=Tous les projets
issues.filter_project_none=Aucun projet
issues.filter_assignee=Assigné
-issues.filter_assginee_no_select=Tous les assignés
-issues.filter_assginee_no_assignee=Aucun assigné
+issues.filter_assignee_no_assignee=Non-assigné
+issues.filter_assignee_any_assignee=Assigné
issues.filter_poster=Auteur
issues.filter_user_placeholder=Rechercher des utilisateurs
issues.filter_user_no_select=Tous les utilisateurs
issues.filter_type=Type
issues.filter_type.all_issues=Tous les tickets
+issues.filter_type.all_pull_requests=Toutes les demandes d’ajout
issues.filter_type.assigned_to_you=Qui vous sont assignés
issues.filter_type.created_by_you=Créés par vous
issues.filter_type.mentioning_you=Vous mentionnant
@@ -1559,12 +1597,12 @@ issues.action_assignee=Assigné à
issues.action_assignee_no_select=Pas d'assignataire
issues.action_check=Cocher/Décocher
issues.action_check_all=Cocher/Décocher tous les éléments
-issues.opened_by=créé %[1]s par <a href="%[2]s">%[3]s</a>
-pulls.merged_by=par <a href="%[2]s">%[3]s</a> fusionné %[1]s.
-pulls.merged_by_fake=par %[2]s fusionné %[1]s.
-issues.closed_by=de <a href="%[2]s">%[3]s</a>, clôt %[1]s
-issues.opened_by_fake=%[1]s ouvert par %[2]s
-issues.closed_by_fake=de %[2]s, clôt %[1]s
+issues.opened_by=ouvert(e) par <a href="%[2]s">%[3]s</a> %[1]s
+pulls.merged_by=par <a href="%[2]s">%[3]s</a> a été fusionnée %[1]s
+pulls.merged_by_fake=par %[2]s a été fusionnée %[1]s
+issues.closed_by=par <a href="%[2]s">%[3]s</a> a été fermé(e) %[1]s
+issues.opened_by_fake=ouvert(e) par %[2]s %[1]s
+issues.closed_by_fake=par %[2]s a été fermé(e) %[1]s
issues.previous=Précédent
issues.next=Suivant
issues.open_title=Ouvert
@@ -1623,12 +1661,15 @@ issues.save=Enregistrer
issues.label_title=Nom du label
issues.label_description=Description du label
issues.label_color=Couleur du label
+issues.label_color_invalid=Couleur invalide
issues.label_exclusive=Exclusif
issues.label_archive=Archivé
issues.label_archived_filter=Afficher les labels archivés
issues.label_archive_tooltip=Les labels archivés sont par défaut exclus des suggestions lors de la recherche par label.
issues.label_exclusive_desc=Remarque : pour rendre des labels mutuellement exclusifs, préfixez leur nom d’une portée au format <code>portée/label</code>.
issues.label_exclusive_warning=Tout label d'une portée en conflit sera retiré lors de la modification des labels d’un ticket ou d’une demande d’ajout.
+issues.label_exclusive_order=Ordre de tri
+issues.label_exclusive_order_tooltip=Les labels exclusifs partageant la même portée seront triées selon cet ordre numérique.
issues.label_count=%d labels
issues.label_open_issues=%d tickets ouverts
issues.label_edit=Éditer
@@ -1646,13 +1687,12 @@ issues.attachment.open_tab=`Cliquez ici pour voir « %s » dans un nouvel ongl
issues.attachment.download=`Cliquez pour télécharger « %s ».`
issues.subscribe=S’abonner
issues.unsubscribe=Se désabonner
-issues.unpin_issue=Désépingler le ticket
+issues.unpin=Désépingler
issues.max_pinned=Vous ne pouvez pas épingler plus de tickets
issues.pin_comment=a épinglé ça %s.
issues.unpin_comment=a désépinglé ça %s.
issues.lock=Verrouiller la conversation
issues.unlock=Déverrouiller la conversation
-issues.lock.unknown_reason=Impossible de verrouiller un ticket avec une raison inconnue.
issues.lock_duplicate=Un ticket ne peut pas être verrouillé à deux reprises.
issues.unlock_error=Impossible de déverrouiller un ticket qui n'est pas verrouillé.
issues.lock_with_reason=a verrouillé en tant que <strong>%s</strong> et limité la conversation aux collaborateurs %s
@@ -1681,16 +1721,20 @@ issues.timetracker_timer_manually_add=Pointer du temps
issues.time_estimate_set=Définir le temps estimé
issues.time_estimate_display=Estimation : %s
-issues.change_time_estimate_at=a changé le temps estimé à <b>%s</b> %s
+issues.change_time_estimate_at=a changé le temps estimé à <b>%[1]s</b> %[2]s
issues.remove_time_estimate_at=a supprimé le temps estimé %s
issues.time_estimate_invalid=Le format du temps estimé est invalide
issues.start_tracking_history=`a commencé son travail %s.`
issues.tracker_auto_close=Le minuteur sera automatiquement arrêté quand le ticket sera fermé.
+issues.stopwatch_already_stopped=Le minuteur pour ce ticket est déjà arrêtée.
+issues.stopwatch_already_created=Le minuteur pour ce ticket existe déjà.
issues.tracking_already_started=`Vous avez déjà un minuteur en cours sur <a href="%s">un autre ticket</a> !`
-issues.stop_tracking_history=`a fini de travailler sur <b>%s</b> %s.`
+issues.stop_tracking=Arrêter le minuteur
+issues.stop_tracking_history=a travaillé <b>%[1]s</b> %[2]s
+issues.cancel_tracking=Abandonner
issues.cancel_tracking_history=`a abandonné son minuteur %s.`
issues.del_time=Supprimer ce minuteur du journal
-issues.add_time_history=`a pointé du temps de travail %s.`
+issues.add_time_history=a pointé <b>%[1]s</b> de travail %[2]s.
issues.del_time_history=`a supprimé son temps de travail %s.`
issues.add_time_manually=Temps pointé manuellement
issues.add_time_hours=Heures
@@ -1732,8 +1776,8 @@ issues.dependency.added_dependency=`a créé une dépendance %s.`
issues.dependency.removed_dependency=`a supprimé une dépendance %s.`
issues.dependency.pr_closing_blockedby=La fermeture de cette demande d’ajout est bloquée par les tickets suivants
issues.dependency.issue_closing_blockedby=La fermeture de ce ticket est bloquée par les tickets suivants
-issues.dependency.issue_close_blocks=Cette demande d'ajout empêche la clôture des tickets suivants
-issues.dependency.pr_close_blocks=Cette demande d'ajout empêche la clôture des tickets suivants
+issues.dependency.issue_close_blocks=Ce ticket empêche la clôture des tickets suivants
+issues.dependency.pr_close_blocks=Cette demande d’ajout empêche la clôture des tickets suivants
issues.dependency.issue_close_blocked=Vous devez fermer tous les tickets qui bloquent ce ticket avant de pouvoir le fermer.
issues.dependency.issue_batch_close_blocked=Impossible de fermer tous les tickets que vous avez choisis, car le ticket #%d a toujours des dépendances ouvertes.
issues.dependency.pr_close_blocked=Vous devez fermer tous les tickets qui bloquent cette demande d'ajout avant de pouvoir la fusionner.
@@ -1850,7 +1894,7 @@ pulls.add_prefix=Ajouter le préfixe <strong>%s</strong>
pulls.remove_prefix=Enlever le préfixe <strong>%s</strong>
pulls.data_broken=Cette demande d’ajout est impossible par manque d'informations de bifurcation.
pulls.files_conflicted=Cette demande d'ajout contient des modifications en conflit avec la branche ciblée.
-pulls.is_checking=Vérification des conflits de fusion en cours. Réessayez dans quelques instants.
+pulls.is_checking=Recherche de conflits de fusion…
pulls.is_ancestor=Cette branche est déjà présente dans la branche ciblée. Il n'y a rien à fusionner.
pulls.is_empty=Les changements sur cette branche sont déjà sur la branche cible. Cette révision sera vide.
pulls.required_status_check_failed=Certains contrôles requis n'ont pas réussi.
@@ -1920,7 +1964,7 @@ pulls.outdated_with_base_branch=Cette branche est désynchronisée avec la branc
pulls.close=Fermer la demande d’ajout
pulls.closed_at=`a fermé cette demande d'ajout <a id="%[1]s" href="#%[1]s">%[2]s</a>.`
pulls.reopened_at=`a rouvert cette demande d'ajout <a id="%[1]s" href="#%[1]s">%[2]s</a>.`
-pulls.cmd_instruction_hint=`Voir <a class="show-instruction">les instructions en ligne de commande</a>.`
+pulls.cmd_instruction_hint=Voir les instructions en ligne de commande
pulls.cmd_instruction_checkout_title=Basculer
pulls.cmd_instruction_checkout_desc=Depuis votre dépôt, basculer sur une nouvelle branche et tester des modifications.
pulls.cmd_instruction_merge_title=Fusionner
@@ -1949,6 +1993,7 @@ pulls.upstream_diverging_prompt_behind_1=Cette branche est en retard de %d révi
pulls.upstream_diverging_prompt_behind_n=Cette branche est en retard de %d révisions sur %s
pulls.upstream_diverging_prompt_base_newer=La branche de base %s a de nouveaux changements
pulls.upstream_diverging_merge=Synchroniser la bifurcation
+pulls.upstream_diverging_merge_confirm=Voulez-vous fusionner la branche par défaut du dépôt de base sur la branche %s de ce dépôt ?
pull.deleted_branch=(supprimé) : %s
pull.agit_documentation=Voir la documentation sur AGit
@@ -2108,12 +2153,19 @@ contributors.contribution_type.deletions=Suppressions
settings=Paramètres
settings.desc=Les paramètres sont l'endroit où gérer les options du dépôt
settings.options=Dépôt
+settings.public_access=Accès public
+settings.public_access_desc=Configurer les permissions des visiteurs publics remplaçant les valeurs par défaut de ce dépôt.
+settings.public_access.docs.not_set=Non défini : ne donne aucune permission supplémentaire. Les règles du dépôt et les permissions des utilisateurs font foi.
+settings.public_access.docs.anonymous_read=Lecture anonyme : les utilisateurs qui ne sont pas connectés peuvent consulter la ressource.
+settings.public_access.docs.everyone_read=Consultation publique : tous les utilisateurs connectés peuvent consulter la ressource. Mettre les tickets et demandes d’ajouts en accès public signifie que les utilisateurs connectés peuvent en créer.
+settings.public_access.docs.everyone_write=Participation publique : tous les utilisateurs connectés ont la permission d’écrire sur la ressource. Seule le Wiki supporte cette autorisation.
settings.collaboration=Collaborateurs
settings.collaboration.admin=Administrateur
settings.collaboration.write=Écriture
settings.collaboration.read=Lecture
settings.collaboration.owner=Propriétaire
settings.collaboration.undefined=Indéfini
+settings.collaboration.per_unit=Permissions de ressource
settings.hooks=Webhooks
settings.githooks=Déclencheurs Git
settings.basic_settings=Paramètres de base
@@ -2154,7 +2206,6 @@ settings.advanced_settings=Paramètres avancés
settings.wiki_desc=Activer le wiki du dépôt
settings.use_internal_wiki=Utiliser le wiki interne
settings.default_wiki_branch_name=Nom de la branche du Wiki par défaut
-settings.default_wiki_everyone_access=Autorisation d’accès par défaut pour les utilisateurs connectés :
settings.failed_to_change_default_wiki_branch=Impossible de modifier la branche du wiki par défaut.
settings.use_external_wiki=Utiliser un wiki externe
settings.external_wiki_url=URL Wiki externe
@@ -2283,8 +2334,6 @@ settings.hooks_desc=Les Webhooks font automatiquement des requêtes HTTP POST à
settings.webhook_deletion=Retirer le Webhook
settings.webhook_deletion_desc=Supprimer un webhook supprime ses paramètres et son historique. Continuer ?
settings.webhook_deletion_success=Le webhook a été supprimé.
-settings.webhook.test_delivery=Tester l'envoi
-settings.webhook.test_delivery_desc=Testez ce webhook avec un faux événement.
settings.webhook.test_delivery_desc_disabled=Pour tester ce webhook avec un faux événement, activez-le.
settings.webhook.request=Requête
settings.webhook.response=Réponse
@@ -2322,6 +2371,8 @@ settings.event_fork=Bifurcation
settings.event_fork_desc=Dépôt bifurqué.
settings.event_wiki=Wiki
settings.event_wiki_desc=Page wiki créée, renommée, modifiée ou supprimée.
+settings.event_statuses=Statuts
+settings.event_statuses_desc=Statut de validation mis à jour depuis l’API.
settings.event_release=Publication
settings.event_release_desc=Publication publiée, mise à jour ou supprimée.
settings.event_push=Soumission
@@ -2331,7 +2382,7 @@ settings.event_repository=Dépôt
settings.event_repository_desc=Dépôt créé ou supprimé.
settings.event_header_issue=Événements de ticket
settings.event_issues=Ticket
-settings.event_issues_desc=Ticket ouvert, rouvert, fermé ou modifié.
+settings.event_issues_desc=Ticket ouvert, rouvert, fermé, modifié ou supprimé.
settings.event_issue_assign=Assignation
settings.event_issue_assign_desc=Ticket assigné ou dé-assigné.
settings.event_issue_label=Labellisation
@@ -2342,7 +2393,7 @@ settings.event_issue_comment=Commentaire
settings.event_issue_comment_desc=Commentaire créé, modifié ou supprimé.
settings.event_header_pull_request=Événements de demande d'ajout
settings.event_pull_request=Demande d'ajout
-settings.event_pull_request_desc=Demande d’ajout ouverte, rouverte, fermée ou modifiée.
+settings.event_pull_request_desc=Demande d’ajout ouverte, rouverte, fermée, modifiée ou supprimée.
settings.event_pull_request_assign=Assignation
settings.event_pull_request_assign_desc=Demande d'ajout assignée ou non assignée.
settings.event_pull_request_label=Labellisation
@@ -2359,6 +2410,11 @@ settings.event_pull_request_review_request=Demande d’évaluation
settings.event_pull_request_review_request_desc=Création ou suppresion de demandes d’évaluation.
settings.event_pull_request_approvals=Approbations de demande d'ajout
settings.event_pull_request_merge=Fusion de demande d'ajout
+settings.event_header_workflow=Événements du flux de travail
+settings.event_workflow_run=Exécution du flux de travail
+settings.event_workflow_run_desc=Tâche du flux de travail Gitea Actions ajoutée, en attente, en cours ou terminée.
+settings.event_workflow_job=Tâches du flux de travail
+settings.event_workflow_job_desc=Travaux du flux de travail Gitea Actions en file d’attente, en attente, en cours ou terminée.
settings.event_package=Paquet
settings.event_package_desc=Paquet créé ou supprimé.
settings.branch_filter=Filtre de branche
@@ -2395,17 +2451,17 @@ settings.packagist_api_token=Jeton API
settings.packagist_package_url=URL du paquet Packagist
settings.deploy_keys=Clés de déploiement
settings.add_deploy_key=Ajouter une clé de déploiement
-settings.deploy_key_desc=Les clefs de déploiement ont un accès en lecture seule au dépôt.
+settings.deploy_key_desc=Les clés de déploiement ont un accès en lecture seule au dépôt.
settings.is_writable=Activer l'accès en écriture
settings.is_writable_info=Autoriser cette clé de déploiement à <strong>soumettre</strong> sur le dépôt.
-settings.no_deploy_keys=Il n'y a pas encore de clefs de déploiement.
+settings.no_deploy_keys=Il n’y a pas encore de clés de déploiement.
settings.title=Titre
settings.deploy_key_content=Contenu
-settings.key_been_used=Une clef de déploiement identique est déjà en cours d'utilisation.
-settings.key_name_used=Une clef de déploiement du même nom existe déjà.
-settings.add_key_success=La clé de déploiement "%s" a été ajoutée.
-settings.deploy_key_deletion=Supprimer une clef de déploiement
-settings.deploy_key_deletion_desc=La suppression d'une clef de déploiement révoque son accès à ce dépôt. Continuer ?
+settings.key_been_used=Une clé de déploiement identique est déjà en cours d’utilisation.
+settings.key_name_used=Une clé de déploiement du même nom existe déjà.
+settings.add_key_success=La clé de déploiement « %s » a été ajoutée.
+settings.deploy_key_deletion=Supprimer une clé de déploiement
+settings.deploy_key_deletion_desc=La suppression d’une clé de déploiement révoque son accès à ce dépôt. Continuer ?
settings.deploy_key_deletion_success=La clé de déploiement a été supprimée.
settings.branches=Branches
settings.protected_branch=Protection de branche
@@ -2437,7 +2493,7 @@ settings.protect_whitelist_teams=Équipes autorisées à pousser :
settings.protect_force_push_allowlist_users=Utilisateurs autorisés à pousser en force :
settings.protect_force_push_allowlist_teams=Équipes autorisées à pousser en force :
settings.protect_force_push_allowlist_deploy_keys=Clés de déploiement pouvant pousser autorisées à pousser en force.
-settings.protect_merge_whitelist_committers=Activer la liste d’autorisés pour la fusion
+settings.protect_merge_whitelist_committers=Fusion sur autorisation uniquement
settings.protect_merge_whitelist_committers_desc=N’autoriser que les utilisateurs et les équipes listés à appliquer les demandes de fusion sur cette branche.
settings.protect_merge_whitelist_users=Utilisateurs autorisés à fusionner :
settings.protect_merge_whitelist_teams=Équipes autorisées à fusionner :
@@ -2451,7 +2507,7 @@ settings.protect_invalid_status_check_pattern=Motif de vérification des statuts
settings.protect_no_valid_status_check_patterns=Aucun motif de vérification des statuts valide.
settings.protect_required_approvals=Minimum d'approbations requis :
settings.protect_required_approvals_desc=Permet de fusionner les demandes d’ajout lorsque suffisamment d’évaluation sont positives.
-settings.protect_approvals_whitelist_enabled=Restreindre les approbations aux utilisateurs ou aux équipes sur liste d’autorisés
+settings.protect_approvals_whitelist_enabled=Restreindre les approbations sur autorisation uniquement
settings.protect_approvals_whitelist_enabled_desc=Seuls les évaluations des utilisateurs ou des équipes suivantes compteront dans les approbations requises. Si laissé vide, les évaluations de toute personne ayant un accès en écriture seront comptabilisées à la place.
settings.protect_approvals_whitelist_users=Évaluateurs autorisés :
settings.protect_approvals_whitelist_teams=Équipes d’évaluateurs autorisés :
@@ -2573,7 +2629,6 @@ diff.commit=révision
diff.git-notes=Notes
diff.data_not_available=Contenu de la comparaison indisponible
diff.options_button=Option de Diff
-diff.show_diff_stats=Voir les Statistiques
diff.download_patch=Télécharger le Fichier Patch
diff.download_diff=Télécharger le Fichier des Différences
diff.show_split_view=Vue séparée
@@ -2622,6 +2677,9 @@ diff.image.overlay=Superposition
diff.has_escaped=Cette ligne contient des caractères Unicode cachés
diff.show_file_tree=Afficher l’arborescence des fichiers
diff.hide_file_tree=Masquer l’arborescence des fichiers
+diff.submodule_added=Sous-module %[1]s ajouté à %[2]s
+diff.submodule_deleted=Sous-module %[1]s supprimé de %[2]s
+diff.submodule_updated=Sous-module %[1]s mis-à-jour : %[2]s
releases.desc=Suivi des publications et des téléchargements.
release.releases=Publications
@@ -2692,6 +2750,7 @@ branch.restore_success=La branche "%s" a été restaurée.
branch.restore_failed=Impossible de restaurer la branche "%s".
branch.protected_deletion_failed=La branche "%s" est protégé. Elle ne peut pas être supprimée.
branch.default_deletion_failed=La branche "%s" est la branche par défaut. Elle ne peut pas être supprimée.
+branch.default_branch_not_exist=La branche par défaut « %s » n‘existe pas.
branch.restore=`Restaurer la branche "%s"`
branch.download=`Télécharger la branche "%s"`
branch.rename=`Renommer la branche "%s"`
@@ -2706,6 +2765,10 @@ branch.create_branch_operation=Créer une branche
branch.new_branch=Créer une nouvelle branche
branch.new_branch_from=`Créer une nouvelle branche à partir de "%s"`
branch.renamed=La branche %s à été renommée en %s.
+branch.rename_default_or_protected_branch_error=Seuls les administrateurs peuvent renommer les branches par défaut ou protégées.
+branch.rename_protected_branch_failed=Cette branche est protégée par des règles de protection basées sur des globs.
+branch.commits_divergence_from=Divergence de révisions : %[1]d en retard et %[2]d en avance sur %[3]s
+branch.commits_no_divergence=Identique à la branche %[1]s
tag.create_tag=Créer l'étiquette %s
tag.create_tag_operation=Créer une étiquette
@@ -2759,6 +2822,7 @@ team_permission_desc=Autorisation
team_unit_desc=Permettre l’accès aux Sections du dépôt
team_unit_disabled=(Désactivé)
+form.name_been_taken=Le nom d’organisation « %s » a déjà été utilisé.
form.name_reserved=Le nom d'organisation "%s" est réservé.
form.name_pattern_not_allowed=Le motif « %s » n'est pas autorisé dans un nom d'organisation.
form.create_org_not_allowed=Vous n'êtes pas autorisé à créer une organisation.
@@ -2780,15 +2844,28 @@ settings.visibility.private_shortname=Privé
settings.update_settings=Appliquer les paramètres
settings.update_setting_success=Les paramètres de l'organisation ont été mis à jour.
-settings.change_orgname_prompt=Remarque : Changer le nom de l'organisation changera également l'URL de votre organisation et libèrera l'ancien nom.
-settings.change_orgname_redirect_prompt=L'ancien nom d'utilisateur redirigera jusqu'à ce qu'il soit réclamé.
+
+settings.rename=Renommer l’organisation
+settings.rename_desc=Changer le nom de l’organisation changera également l’URL de votre organisation et libèrera l’ancien nom.
+settings.rename_success=L’organisation %[1]s a bien été renommé en %[2]s.
+settings.rename_no_change=Le nom de l’organisation n’a pas été modifié.
+settings.rename_new_org_name=Nouveau nom d’organisation
+settings.rename_failed=Le renommage de l’organisation a échoué en raison d’une erreur interne.
+settings.rename_notices_1=Cette opération <strong>ne peut pas </strong> être annulée.
+settings.rename_notices_2=L’ancien nom redirigera jusqu'à ce qu'il soit réclamé.
+
settings.update_avatar_success=L'avatar de l'organisation a été mis à jour.
settings.delete=Supprimer l'organisation
settings.delete_account=Supprimer cette organisation
settings.delete_prompt=Cette organisation sera supprimée définitivement. Cette action est <strong>IRRÉVERSIBLE</strong> !
+settings.name_confirm=Entrez le nom de l’organisation pour confirmer :
+settings.delete_notices_1=Cette opération <strong>ne peut pas </strong> être annulée.
+settings.delete_notices_2=Cette opération supprimera définitivement <strong>tous les dépôts</strong> de <strong>%s</strong>, y compris le code, les tickets, les commentaires, les données de wiki et les accès des collaborateurs.
+settings.delete_notices_3=Cette opération supprimera définitivement <strong>tous les paquets</strong> de <strong>%s</strong>.
+settings.delete_notices_4=Cette opération supprimera définitivement <strong>tous les projets</strong> de <strong>%s</strong>.
settings.confirm_delete_account=Confirmer la suppression
-settings.delete_org_title=Supprimer l'organisation
-settings.delete_org_desc=Cette organisation sera supprimée définitivement. Voulez-vous continuer ?
+settings.delete_failed=La suppression de l’organisation a échoué en raison d’une erreur interne.
+settings.delete_successful=L’organisation <b>%s</b> a été supprimée avec succès.
settings.hooks_desc=Vous pouvez ajouter des webhooks qui seront activés pour <strong>tous les dépôts</strong> de cette organisation.
settings.labels_desc=Ajoute des labels qui peuvent être utilisés sur les tickets pour <strong>tous les dépôts</strong> de cette organisation.
@@ -2860,6 +2937,18 @@ teams.invite.title=Vous avez été invité à rejoindre l'équipe <strong>%s</st
teams.invite.by=Invité par %s
teams.invite.description=Veuillez cliquer sur le bouton ci-dessous pour rejoindre l’équipe.
+view_as_role=Voir en tant que %s
+view_as_public_hint=Vous visualisez le README en tant qu’utilisateur public.
+view_as_member_hint=Vous visualisez le README en tant que membre de cette organisation.
+
+worktime=Temps de travail
+worktime.date_range_start=Date de début
+worktime.date_range_end=Date de fin
+worktime.query=Demande
+worktime.time=Durée
+worktime.by_repositories=Par dépôts
+worktime.by_milestones=Par jalons
+worktime.by_members=Par membres
[admin]
maintenance=Maintenance
@@ -3108,7 +3197,7 @@ auths.attribute_username_placeholder=Laisser vide afin d'utiliser le nom d'utili
auths.attribute_name=Attribut prénom
auths.attribute_surname=Attribut nom de famille
auths.attribute_mail=Attribut e-mail
-auths.attribute_ssh_public_key=Attribut clef SSH publique
+auths.attribute_ssh_public_key=Attribut clé SSH publique
auths.attribute_avatar=Attribut de l'avatar
auths.attributes_in_bind=Aller chercher les attributs dans le contexte de liaison DN
auths.allow_deactivate_all=Permettre à un résultat de recherche vide de désactiver tous les utilisateurs
@@ -3231,8 +3320,6 @@ config.ssh_domain=Domaine du serveur SSH
config.ssh_port=Port
config.ssh_listen_port=Port d'écoute
config.ssh_root_path=Emplacement racine
-config.ssh_key_test_path=Chemin de test des clés
-config.ssh_keygen_path=Chemin vers le générateur de clefs ("ssh-keygen")
config.ssh_minimum_key_size_check=Vérification de la longueur de clé minimale
config.ssh_minimum_key_sizes=Tailles de clé minimales
@@ -3355,6 +3442,8 @@ monitor.previous=Précédent
monitor.execute_times=Exécutions
monitor.process=Processus en cours d'exécution
monitor.stacktrace=Piles d'execution
+monitor.trace=Trace
+monitor.performance_logs=Journaux de performance
monitor.processes_count=%d processus
monitor.download_diagnosis_report=Télécharger le rapport de diagnostic
monitor.desc=Description
@@ -3363,7 +3452,6 @@ monitor.execute_time=Heure d'Éxécution
monitor.last_execution_result=Résultat
monitor.process.cancel=Annuler le processus
monitor.process.cancel_desc=L’annulation d’un processus peut entraîner une perte de données.
-monitor.process.cancel_notices=Annuler : <strong>%s</strong> ?
monitor.process.children=Enfant
monitor.queues=Files d'attente
@@ -3373,12 +3461,12 @@ monitor.queue.type=Type
monitor.queue.exemplar=Type d'exemple
monitor.queue.numberworkers=Nombre de processus
monitor.queue.activeworkers=Processus actifs
-monitor.queue.maxnumberworkers=Nombre maximale de processus
+monitor.queue.maxnumberworkers=Nombre maximal de processus
monitor.queue.numberinqueue=Position dans la queue
monitor.queue.review_add=Examiner / ajouter des processus
monitor.queue.settings.title=Paramètres du réservoir
monitor.queue.settings.desc=Les bassins croissent proportionnellement au besoin de leurs exécuteurs.
-monitor.queue.settings.maxnumberworkers=Nombre maximale de processus
+monitor.queue.settings.maxnumberworkers=Nombre maximal de processus
monitor.queue.settings.maxnumberworkers.placeholder=Actuellement %[1]d
monitor.queue.settings.maxnumberworkers.error=Le nombre de processus doit être un nombre
monitor.queue.settings.submit=Appliquer les paramètres
@@ -3530,6 +3618,7 @@ versions=Versions
versions.view_all=Voir tout
dependency.id=ID
dependency.version=Version
+search_in_external_registry=Rechercher dans %s
alpine.registry=Configurez ce registre en ajoutant l’URL dans votre fichier <code>/etc/apk/repositories</code> :
alpine.registry.key=Téléchargez la clé RSA publique du registre dans le dossier <code>/etc/apk/keys/</code> pour vérifier la signature de l'index :
alpine.registry.info=Choisissez $branch et $repository dans la liste ci-dessous.
@@ -3559,7 +3648,8 @@ conda.install=Pour installer le paquet en utilisant Conda, exécutez la commande
container.details.type=Type d'image
container.details.platform=Plateforme
container.pull=Tirez l'image depuis un terminal :
-container.digest=Empreinte :
+container.images=Images
+container.digest=Empreinte
container.multi_arch=SE / Arch
container.layers=Calques d'image
container.labels=Labels
@@ -3662,11 +3752,18 @@ owner.settings.chef.keypair.description=Une paire de clés est nécessaire pour
secrets=Secrets
description=Les secrets seront transmis à certaines actions et ne pourront pas être lus autrement.
none=Il n'y a pas encore de secrets.
-creation=Ajouter un secret
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Description
creation.name_placeholder=Caractères alphanumériques ou tirets bas uniquement, insensibles à la casse, ne peut commencer par GITEA_ ou GITHUB_.
creation.value_placeholder=Entrez n’importe quoi. Les blancs cernant seront taillés.
-creation.success=Le secret "%s" a été ajouté.
-creation.failed=Impossible d'ajouter le secret.
+creation.description_placeholder=Décrire brièvement votre dépôt (optionnel).
+
+save_success=Le secret « %s » a été enregistré.
+save_failed=Impossible d’enregistrer le secret.
+
+add_secret=Ajouter un secret
+edit_secret=Modifier le secret
deletion=Supprimer le secret
deletion.description=La suppression d'un secret est permanente et irréversible. Continuer ?
deletion.success=Le secret a été supprimé.
@@ -3722,6 +3819,7 @@ runners.status.active=Actif
runners.status.offline=Hors-ligne
runners.version=Version
runners.reset_registration_token=Réinitialiser le jeton d'enregistrement
+runners.reset_registration_token_confirm=Voulez-vous révoquer le jeton actuel et en générer un nouveau ?
runners.reset_registration_token_success=Le jeton d’inscription de l’exécuteur a été réinitialisé avec succès
runs.all_workflows=Tous les flux de travail
@@ -3743,6 +3841,11 @@ runs.no_workflows.documentation=Pour plus d’informations sur les actions Gitea
runs.no_runs=Le flux de travail n'a pas encore d'exécution.
runs.empty_commit_message=(message de révision vide)
runs.expire_log_message=Les journaux ont été supprimés car ils étaient trop anciens.
+runs.delete=Supprimer cette exécution
+runs.cancel=Annuler l’exécution du flux
+runs.delete.description=Êtes-vous sûr de vouloir supprimer définitivement cette exécution ? Cette action ne peut pas être annulée.
+runs.not_done=Cette exécution du flux de travail n’est pas terminée.
+runs.view_workflow_file=Voir le fichier du flux de travail
workflow.disable=Désactiver le flux de travail
workflow.disable_success=Le flux de travail « %s » a bien été désactivé.
@@ -3754,6 +3857,7 @@ workflow.not_found=Flux de travail « %s » introuvable.
workflow.run_success=Le flux de travail « %s » s’est correctement exécuté.
workflow.from_ref=Utiliser le flux de travail depuis
workflow.has_workflow_dispatch=Ce flux de travail a un déclencheur d’événement workflow_dispatch.
+workflow.has_no_workflow_dispatch=Le flux de travail %s n’a pas de déclencheur d’événement workflow_dispatch.
need_approval_desc=Besoin d’approbation pour exécuter des flux de travail pour une demande d’ajout de bifurcation.
@@ -3773,12 +3877,16 @@ variables.creation.success=La variable « %s » a été ajoutée.
variables.update.failed=Impossible d’éditer la variable.
variables.update.success=La variable a bien été modifiée.
+logs.always_auto_scroll=Toujours faire défiler les journaux automatiquement
+logs.always_expand_running=Toujours développer les journaux en cours
[projects]
deleted.display_name=Projet supprimé
type-1.display_name=Projet personnel
type-2.display_name=Projet de dépôt
type-3.display_name=Projet d’organisation
+enter_fullscreen=Plein écran
+exit_fullscreen=Quitter le plein écran
[git.filemode]
changed_filemode=%[1]s → %[2]s
diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini
index cf6c76a9db..99c8b56f60 100644
--- a/options/locale/locale_ga-IE.ini
+++ b/options/locale/locale_ga-IE.ini
@@ -54,6 +54,7 @@ webauthn_reload=Athlódáil
repository=Stór
organization=Eagraíocht
mirror=Scáthán
+issue_milestone=Cloch Mhíle
new_repo=Stór Nua
new_migrate=Imirce Nua
new_mirror=Scáthán Nua
@@ -112,9 +113,11 @@ copy_type_unsupported=Ní féidir an cineál comhaid seo a chóipeáil
write=Scríobh
preview=Réamhamharc
loading=à lódáil...
+files=Comhaid
error=Earráid
error404=Níl an leathanach atá tú ag iarraidh a bhaint amach <strong>ann</strong> nó <strong>níl tú údaraithe</strong> chun é a fheiceáil.
+error503=Níorbh fhéidir leis an bhfreastalaí d'iarratas a chur i gcrích. Bain triail eile as ar ball.
go_back=Ar ais
invalid_data=Sonraí neamhbhailí: %v
@@ -127,6 +130,7 @@ pin=Bioráin
unpin=Díphoráil
artifacts=Déantáin
+expired=Imithe in éag
confirm_delete_artifact=An bhfuil tú cinnte gur mhaith leat an déantán '%s' a scriosadh?
archived=Cartlann
@@ -168,6 +172,10 @@ search=Cuardaigh...
type_tooltip=Cineál cuardaigh
fuzzy=Doiléir
fuzzy_tooltip=Cuir san áireamh torthaí a mheaitseálann an téarma cuardaigh go dlúth freisin
+words=Focail
+words_tooltip=Ná cuir san áireamh ach torthaí a mheaitseálann na focail téarma cuardaigh
+regexp=Nathanna Rialta
+regexp_tooltip=Ná cuir ach torthaí a mheaitseálann an téarma cuardaigh nathanna rialta san áireamh
exact=Beacht
exact_tooltip=Ní chuir san áireamh ach torthaí a mheaitseálann leis an téarma
repo_kind=Cuardaigh stórtha...
@@ -244,6 +252,7 @@ license_desc=Téigh go bhfaighidh <a target="_blank" rel="noopener noreferrer" h
[install]
install=Suiteáil
+installing_desc=Suiteáil anois, fan go fóill...
title=Cumraíocht Tosaigh
docker_helper=Má ritheann tú Gitea taobh istigh de Docker, léigh an <a target="_blank" rel="noopener noreferrer" href="%s">doiciméadúchán</a> roimh aon socruithe a athrú.
require_db_desc=Éilíonn Gitea MySQL, PostgreSQL, MSSQL, SQLite3 nó TiDB (prótacal MySQL).
@@ -383,6 +392,13 @@ show_only_public=Ag taispeáint poiblí amháin
issues.in_your_repos=I do stórais
+guide_title=Gan Ghníomhaíocht
+guide_desc=Níl aon stórtha nó úsáideoirí á leanúint agat faoi láthair, mar sin níl aon ábhar le taispeáint. Is féidir leat stórtha nó úsáideoirí spéise a iniúchadh ó na naisc thíos.
+explore_repos=Déan stórtha a iniúchadh
+explore_users=Déan iniúchadh ar úsáideoirí
+empty_org=Níl aon eagraíochtaí ann fós.
+empty_repo=Níl aon stórtha ann fós.
+
[explore]
repos=Stórais
users=Úsáideoirí
@@ -405,6 +421,7 @@ remember_me.compromised=Níl an comhartha logála isteach bailí níos mó a d'f
forgot_password_title=Dearmad ar an bPasfhocal
forgot_password=Dearmad ar an bPasfhocal?
need_account=An bhfuil cuntas ag teastáil uait?
+sign_up_tip=Tá tú ag clárú an chéad chuntais sa chóras, a bhfuil pribhléidí riarthóra aige. Cuimhnigh go cúramach ar d’ainm úsáideora agus do phasfhocal. Má dhéanann tú dearmad ar an ainm úsáideora nó ar an pasfhocal, féach ar dhoiciméadacht Gitea le do thoil chun an cuntas a aisghabháil.
sign_up_now=Cláraigh anois.
sign_up_successful=Cruthaíodh cuntas go rathúil. Fáilte romhat!
confirmation_mail_sent_prompt_ex=Tá ríomhphost dearbhaithe nua seolta chuig <b>%s</b>. Seiceáil do bhosca isteach laistigh den chéad %s eile chun an próiseas clárúcháin a chur i gcrích. Má tá do sheoladh ríomhphoist clárúcháin mícheart, is féidir leat síniú isteach arís agus é a athrú.
@@ -435,6 +452,7 @@ use_scratch_code=Úsáid cód scratch
twofa_scratch_used=D'úsáid tú do chód scratch. Tá tú atreoraithe chuig an leathanach socruithe dhá fhachtóir ionas gur féidir leat clárú do ghléas a bhaint nó cód scratch nua a ghiniúint.
twofa_passcode_incorrect=Tá do phaschód mícheart. Má chuir tú do ghléas míchuir tú, bain úsáid as do chód scratch chun síniú isteach.
twofa_scratch_token_incorrect=Tá do chód scratch mícheart.
+twofa_required=Ní mór duit Fíordheimhniú Dhá Fhachtóir a shocrú chun rochtain a fháil ar stórtha, nó iarracht a dhéanamh logáil isteach arís.
login_userpass=Sínigh isteach
login_openid=OpenID
oauth_signup_tab=Cláraigh Cuntas Nua
@@ -443,7 +461,7 @@ oauth_signup_submit=Cuntas Comhlánaigh
oauth_signin_tab=Nasc leis an gCuntas Reatha
oauth_signin_title=Sínigh isteach chun Cuntas Nasctha a Údarú
oauth_signin_submit=Cuntas Nasc
-oauth.signin.error=Bhí earráid ann ag próiseáil an t-iarratas ar údarú. Má leanann an earráid seo, déan teagmháil le riarthóir an láithreáin.
+oauth.signin.error.general=Tharla earráid agus an t-iarratas údaraithe á phróiseáil: %s. Má leanann an earráid seo, téigh i dteagmháil le riarthóir an tsuímh.
oauth.signin.error.access_denied=Diúltaíodh an t-iarratas ar údarú.
oauth.signin.error.temporarily_unavailable=Theip ar údarú toisc nach bhfuil an fhreastalaí fíordheimhnithe ar fáil Bain triail as arís níos déanaí.
oauth_callback_unable_auto_reg=Tá Clárú Uathoibríoch cumasaithe, ach sheol Soláthraí OAuth2 %[1]s réimsí in easnamh ar ais: %[2]s, ní raibh sé in ann cuntas a chruthú go huathoibríoch, cruthaigh nó nasc le cuntas, nó déan teagmháil le riarthóir an tsuímh.
@@ -573,9 +591,9 @@ alpha_dash_dot_error=` níor cheart go mbeadh ach alfa-uimhriúil, dash ('-'), c
git_ref_name_error=` caithfidh gur ainm tagartha Git dea-chruthaithe é.`
size_error=` ní mór méid %s.`
min_size_error=` ní mór go mbeadh carachtar %s ar a laghad ann.`
-max_size_error=` caithfidh `%s carachtar ar a mhéad a bheith ann.`
+max_size_error=caithfidh %s carachtar ar a mhéad a bheith ann.
email_error=`ní seoladh ríomhphoist bailí é.`
-url_error=`ní URL bailí é `"%s". `
+url_error=`ní URL bailí é "%s".`
include_error=` ní mór fotheaghrán a bheith ann "%s".`
glob_pattern_error=` tá patrún glob neamhbhailí: %s.`
regex_pattern_error=`tá patrún regex neamhbhailí: %s.`
@@ -716,8 +734,8 @@ public_profile=Próifíl Phoiblí
biography_placeholder=Inis dúinn beagán fút féin! (Is féidir leat Markdown a úsáid)
location_placeholder=Comhroinn do shuíomh thart le daoine eile
profile_desc=Rialú conas a thaispeánfar do phróifíl d'úsáideoirí eile. Úsáidfear do phríomhsheoladh ríomhphoist le haghaidh fógraí, aisghabháil pasfhocail agus oibríochtaí Git gréasán-bhunaithe.
-password_username_disabled=Níl cead agat a n-ainm úsáideora a athrú. Déan teagmháil le do riarthóir suímh le haghaidh tuilleadh sonraí.
-password_full_name_disabled=Níl cead agat a n-ainm iomlán a athrú. Déan teagmháil le do riarthóir suímh le haghaidh tuilleadh sonraí.
+password_username_disabled=Níl cead agat d'ainm úsáideora a athrú. Déan teagmháil le do riarthóir suímh le haghaidh tuilleadh sonraí.
+password_full_name_disabled=Níl cead agat d’ainm iomlán a athrú. Déan teagmháil le do riarthóir suímh le haghaidh tuilleadh sonraí.
full_name=Ainm Iomlán
website=Láithreán Gréasáin
location=Suíomh
@@ -908,11 +926,13 @@ delete_token_success=Tá an comhartha scriosta. Níl rochtain ag iarratais a ús
repo_and_org_access=Rochtain Stórála agus Eagraíochta
permissions_public_only=Poiblí amháin
permissions_access_all=Gach (poiblí, príobháideach agus teoranta)
-select_permissions=Roghnaigh ceadanna
permission_not_set=Níl leagtha
permission_no_access=Gan rochtain
permission_read=Léigh
permission_write=Léigh agus Scríobh
+permission_anonymous_read=Léamh gan Ainm
+permission_everyone_read=Léigh gach duine
+permission_everyone_write=Scríobh gach duine
access_token_desc=Ní chuireann ceadchomharthaí roghnaithe ach teorainn leis an údarú do na bealaí <a %s>API</a> comhfhreagracha. Léigh <a %s>doiciméadúchán</a> chun tuilleadh eolais a fháil.
at_least_one_permission=Ní mór duit cead amháin ar a laghad a roghnú chun comhartha a chruthú
permissions_list=Ceadanna:
@@ -1015,6 +1035,9 @@ new_repo_helper=Tá gach comhad tionscadail i stór, lena n-áirítear stair ath
owner=Úinéir
owner_helper=B'fhéidir nach dtaispeánfar roinnt eagraíochtaí sa anuas mar gheall ar theorainn uasta comhaireamh stórais.
repo_name=Ainm Stórais
+repo_name_profile_public_hint=Is stóras speisialta é .profile is féidir leat a úsáid chun README.md a chur le do phróifíl eagraíochta poiblí, le feiceáil ag aon duine. Cinntigh go bhfuil sé poiblí agus tosaigh é le README san eolaire próifíle le tosú.
+repo_name_profile_private_hint=Is stóras speisialta é .profile-private is féidir leat a úsáid chun README.md a chur le do phróifíl bhall eagraíochta, nach féidir a fheiceáil ach ag baill eagraíochta. Cinntigh go bhfuil sé príobháideach agus tosaigh le README sa eolaire próifíle chun tús a chur leis.
+repo_name_helper=Úsáideann ainmneacha maith stóras focail eochair gairide, áithnid agus uathúla. D'fhéadfaí stóras darbh ainm '.profile' nó '.profile-private' a úsáid chun README.md a chur leis an bpróifíl úsáideora/eagraíochta.
repo_size=Méid an Stóras
template=Teimpléad
template_select=Roghnaigh teimpléad.
@@ -1111,9 +1134,7 @@ blame.ignore_revs=Ag déanamh neamhairde de leasuithe i <a href="%s">.git-blame-
blame.ignore_revs.failed=Theip ar neamhaird a dhéanamh ar leasuithe i <a href="%s">.git-blame-ignore-revs</a>.
user_search_tooltip=Taispeáint uasmhéid de 30 úsáideoir
-tree_path_not_found_commit=Níl cosán %[1]s ann i dtiomantas %[2]s
-tree_path_not_found_branch=Níl cosán %[1]s ann i mbrainse %[2]s
-tree_path_not_found_tag=Níl cosán %[1]s ann i gclib %[2]s
+tree_path_not_found=Níl cosán %[1]s ann i %[2]s
transfer.accept=Glac le hAistriú
transfer.accept_desc=Aistriú chuig “%sâ€
@@ -1124,6 +1145,7 @@ transfer.no_permission_to_reject=Níl cead agat an aistriú seo a dhiúltú.
desc.private=Príobháideach
desc.public=Poiblí
+desc.public_access=Rochtain Phoiblí
desc.template=Teimpléad
desc.internal=Inmheánach
desc.archived=Cartlannaithe
@@ -1207,6 +1229,7 @@ migrate.migrating_issues=Saincheisteanna Imirce
migrate.migrating_pulls=Iarratais Tarraingthe á n-Imirce
migrate.cancel_migrating_title=Cealaigh Imirce
migrate.cancel_migrating_confirm=Ar mhaith leat an imirce seo a chealú?
+migration_status=Stádas imirce
mirror_from=scáthán de
forked_from=forcailte ó
@@ -1231,6 +1254,7 @@ create_new_repo_command=Stóras nua a chruthú ar an líne ordaithe
push_exist_repo=Stóras atá ann cheana a bhrú ón líne ordaithe
empty_message=Níl aon ábhar sa stóras seo.
broken_message=Ní féidir na sonraí Git atá mar bhunús leis an stóras seo a léamh. Déan teagmháil le riarthóir an chás seo nó scrios an stóras seo.
+no_branch=Níl aon bhrainsí ag an stóras seo.
code=Cód
code.desc=Rochtain ar chód foinse, comhaid, gealltanais agus brainsí.
@@ -1250,6 +1274,7 @@ labels=Lipéid
org_labels_desc=Lipéid ar leibhéal eagraíochta is féidir a úsáid le <strong>gach stóras</strong> faoin eagraíocht seo
org_labels_desc_manage=bainistigh
+milestone=Cloch Mhíle
milestones=Clocha míle
commits=Tiomáintí
commit=Tiomantas
@@ -1275,7 +1300,7 @@ ambiguous_runes_header=`Tá carachtair Unicode débhríoch sa chomhad seo `
ambiguous_runes_description=`Tá carachtair Unicode sa chomhad seo a d'fhéadfadh a bheith mearbhall le carachtair eile. Má cheapann tú go bhfuil sé seo d'aon ghnó, is féidir leat neamhaird a dhéanamh go sábháilte don rabhadh seo Úsáid an cnaipe Escape chun iad a nochtadh. `
invisible_runes_line=`Tá carachtair unicode dofheicthe ag an líne seo `
ambiguous_runes_line=`Tá carachtair unicode débhríoch ag an líne seo `
-ambiguous_character=Is féidir `%[1]c [U+%04[1]X] a mheascadh le %[2]c [U+%04[2]X]`
+ambiguous_character=`Is féidir %[1]c [U+%04[1]X] a mheascadh le %[2]c [U+%04[2]X]`
escape_control_characters=Éalú
unescape_control_characters=Dí-Éalú
@@ -1283,7 +1308,6 @@ file_copy_permalink=Cóipeáil Buan-nasc
view_git_blame=Féach ar Git Blame
video_not_supported_in_browser=Ní thacaíonn do bhrabhsálaí leis an gclib 'video' HTML5.
audio_not_supported_in_browser=Ní thacaíonn do bhrabhsálaí leis an gclib 'audio' HTML5.
-stored_lfs=Stóráilte le Git LFS
symbolic_link=Nasc siombalach
executable_file=Comhad Infheidhmithe
vendored=Díoltóra
@@ -1309,7 +1333,9 @@ editor.upload_file=Uaslódáil Comhad
editor.edit_file=Cuir Comhad in eagar
editor.preview_changes=Athruithe Réamhamhar
editor.cannot_edit_lfs_files=Ní féidir comhaid LFS a chur in eagar sa chomhéadan gréasáin.
+editor.cannot_edit_too_large_file=Tá an comhad rómhór le cur in eagar.
editor.cannot_edit_non_text_files=Ní féidir comhaid dhénártha a chur in eagar sa chomhéadan gréasáin.
+editor.file_not_editable_hint=Ach is féidir leat é a athainmniú nó a bhogadh fós.
editor.edit_this_file=Cuir Comhad in eagar
editor.this_file_locked=Tá an comhad faoi ghlas
editor.must_be_on_a_branch=Caithfidh tú a bheith ar bhrainse chun athruithe a dhéanamh nó a mholadh ar an gcomhad seo.
@@ -1329,7 +1355,7 @@ editor.update=Nuashonraigh %s
editor.delete=Scrios %s
editor.patch=Cuir paiste i bhfeidh
editor.patching=Paisteáil:
-editor.fail_to_apply_patch=Ní féidir paiste "%s" a chur i bhfeidhm
+editor.fail_to_apply_patch=Ní féidir an paiste a chur i bhfeidhm
editor.new_patch=Paiste Nua
editor.commit_message_desc=Cuir cur síos leathnaithe roghnach leis…
editor.signoff_desc=Cuir leantóir sínithe ag an gcoiteoir ag deireadh na teachtaireachta logála tiomanta.
@@ -1342,13 +1368,14 @@ editor.new_branch_name_desc=Ainm brainse nua…
editor.cancel=Cealaigh
editor.filename_cannot_be_empty=Ní féidir ainm an chomhaid a bheith folamh.
editor.filename_is_invalid=Tá ainm an chomhaid neamhbhailí: "%s".
+editor.commit_email=Tiomantas ríomhphost
+editor.invalid_commit_email=Tá an ríomhphost don ghealltanas neamhbhailí.
editor.branch_does_not_exist=Níl brainse "%s" ann sa stóras seo.
editor.branch_already_exists=Tá brainse "%s" ann cheana féin sa stóras seo.
editor.directory_is_a_file=Úsáidtear ainm eolaire "%s" cheana féin mar ainm comhaid sa stóras seo.
-editor.file_is_a_symlink=Is nasc siombalach é `"%s". Ní féidir naisc shiombalacha a chur in eagar san eagarthóir gréasáin`
+editor.file_is_a_symlink=Is nasc siombalach é "%s". Ní féidir naisc shiombalacha a chur in eagar san eagarthóir gréasáin
editor.filename_is_a_directory=Úsáidtear ainm comhaid "%s" cheana féin mar ainm eolaire sa stóras seo.
-editor.file_editing_no_longer_exists=Níl an comhad atá á chur in eagar, "%s", ann sa stóras seo a thuilleadh.
-editor.file_deleting_no_longer_exists=Níl an comhad atá á scriosadh, "%s", ann sa stóras seo a thuilleadh.
+editor.file_modifying_no_longer_exists=Níl an comhad atá á mhodhnú, "%s", sa stóras seo a thuilleadh.
editor.file_changed_while_editing=Tá athrú tagtha ar ábhar an chomhad ó thosaigh tú ag eagarthóireacht <a target="_blank" rel="noopener noreferrer" href="%s">Cliceáil anseo</a> chun iad a fheiceáil nó Athru <strong>ithe a Tiomantas arís</strong> chun iad a fhorscríobh.
editor.file_already_exists=Tá comhad darb ainm "%s" ann cheana féin sa stóras seo.
editor.commit_id_not_matching=Ní mheaitseálann an ID Tiomanta leis an ID nuair a thosaigh tú ag eagarthóireacht. Tiomanta isteach i mbrainse paiste agus ansin cumaisc.
@@ -1356,8 +1383,6 @@ editor.push_out_of_date=Is cosúil go bhfuil an brú as dáta.
editor.commit_empty_file_header=Tiomantas comhad folamh
editor.commit_empty_file_text=Tá an comhad atá tú ar tí tiomantas folamh. Ar aghaidh?
editor.no_changes_to_show=Níl aon athruithe le taispeáint.
-editor.fail_to_update_file=Theip ar nuashonrú/cruthú comhad "%s".
-editor.fail_to_update_file_summary=Teachtaireacht Earráide:
editor.push_rejected_no_message=Dhiúltaigh an freastalaí an t-athrú gan teachtaireacht. Seiceáil Git Hooks le do thoil.
editor.push_rejected=Dhiúltaigh an freastalaí an t-athrú. Seiceáil Git Hooks le do thoil.
editor.push_rejected_summary=Teachtaireacht Diúltaithe Iomlán:
@@ -1371,6 +1396,15 @@ editor.user_no_push_to_branch=Ní féidir leis an úsáideoir brúigh go dtí an
editor.require_signed_commit=Éilíonn an Brainse tiomantas sínithe
editor.cherry_pick=Roghnaigh silíní %s ar:
editor.revert=Fill %s ar:
+editor.failed_to_commit=Theip ar athruithe a chur i bhfeidhm.
+editor.failed_to_commit_summary=Teachtaireacht Earráide:
+
+editor.fork_create=Stóras Forc chun Athruithe a Mholadh
+editor.fork_create_description=Ní féidir leat an stóras seo a chur in eagar go díreach. Ina áit sin, is féidir leat forc a chruthú, eagarthóireachtaí a dhéanamh agus iarratas tarraingthe a chruthú.
+editor.fork_edit_description=Ní féidir leat an stóras seo a chur in eagar go díreach. Scríobhfar na hathruithe chuig do fhorc <b>%s</b>, ionas gur féidir leat iarratas tarraingthe a chruthú.
+editor.fork_not_editable=Tá forc déanta agat ar an stóras seo ach ní féidir do fhorc a chur in eagar.
+editor.fork_failed_to_push_branch=Theip ar bhrainse %s a bhrú chuig do stóras.
+editor.fork_branch_exists=Tá brainse "%s" ann cheana féin i do fhorc, roghnaigh ainm brainse nua le do thoil.
commits.desc=Brabhsáil stair athraithe cód foinse.
commits.commits=Tiomáintí
@@ -1390,6 +1424,7 @@ commits.signed_by_untrusted_user_unmatched=Sínithe ag úsáideoir neamhiontaofa
commits.gpg_key_id=GPG Eochair ID
commits.ssh_key_fingerprint=Méarloirg Eochair SSH
commits.view_path=Féach ag an bpointe seo sa stair
+commits.view_file_diff=Féach ar athruithe ar an gcomhad seo sa tiomantas seo
commit.operations=Oibríochtaí
commit.revert=Téigh ar ais
@@ -1450,6 +1485,8 @@ issues.filter_milestones=Cloch Mhíle Scagaire
issues.filter_projects=Tionscadal Scagaire
issues.filter_labels=Lipéad Scagaire
issues.filter_reviewers=Athbhreithneoir Scagaire
+issues.filter_no_results=Gan torthaí
+issues.filter_no_results_placeholder=Bain triail as do scagairí cuardaigh a choigeartú.
issues.new=Eagrán Nua
issues.new.title_empty=Ní féidir leis an teideal a bheith folamh
issues.new.labels=Lipéid
@@ -1525,13 +1562,14 @@ issues.filter_project=Tionscadal
issues.filter_project_all=Gach tionscadal
issues.filter_project_none=Gan aon tionscadal
issues.filter_assignee=Sannaitheoir
-issues.filter_assginee_no_select=Gach sannaithe
-issues.filter_assginee_no_assignee=Gan sannaitheoir
+issues.filter_assignee_no_assignee=Sannta do dhuine ar bith
+issues.filter_assignee_any_assignee=Sannta do dhuine ar bith
issues.filter_poster=Údar
issues.filter_user_placeholder=Cuardaigh úsáideoirí
issues.filter_user_no_select=Gach úsáideoir
issues.filter_type=Cineál
issues.filter_type.all_issues=Gach saincheist
+issues.filter_type.all_pull_requests=Gach iarratas tarraingt
issues.filter_type.assigned_to_you=Sannta duit
issues.filter_type.created_by_you=Cruthaithe agat
issues.filter_type.mentioning_you=Ag tagairt duit
@@ -1623,12 +1661,15 @@ issues.save=Sábháil
issues.label_title=Ainm
issues.label_description=Cur síos
issues.label_color=Dath
+issues.label_color_invalid=Dath neamhbhailí
issues.label_exclusive=Eisiach
issues.label_archive=Lipéad Cartlann
issues.label_archived_filter=Taispeáin lipéid cartlainne
issues.label_archive_tooltip=Eisiatar lipéid chartlainne de réir réamhshocraithe ó na moltaí nuair a dhéantar cuardach de réir lipéid.
issues.label_exclusive_desc=Ainmnigh an lipéad <code>scope/item</code> chun é a dhéanamh comheisiatach le lipéid <code>scope/</code> eile.
issues.label_exclusive_warning=Bainfear aon lipéid scóipe contrártha le linn eagarthóireacht a dhéanamh ar lipéid iarratais eisiúna nó tarraingthe.
+issues.label_exclusive_order=Ordú Sórtála
+issues.label_exclusive_order_tooltip=Déanfar lipéid eisiacha sa raon feidhme céanna a shórtáil de réir an oird uimhriúil seo.
issues.label_count=%d lipéid
issues.label_open_issues=%d saincheisteanna oscailte/iarratais tarraing
issues.label_edit=Cuir in eagar
@@ -1643,16 +1684,15 @@ issues.label.filter_sort.by_size=Méid is lú
issues.label.filter_sort.reverse_by_size=Méid is mó
issues.num_participants=%d Rannpháirtithe
issues.attachment.open_tab=`Cliceáil chun "%s" a fheiceáil i gcluaisín nua`
-issues.attachment.download=`Cliceáil chun "%s" a íoslódáil
+issues.attachment.download=`Cliceáil chun "%s" a íoslódáil`
issues.subscribe=Liostáil
issues.unsubscribe=Díliostáil
-issues.unpin_issue=Bain pionna an t-eagrán
+issues.unpin=Díphoráil
issues.max_pinned=Ní féidir leat níos mó saincheisteanna a phionadh
issues.pin_comment=phionnáil an %s seo
issues.unpin_comment=bain pionna an %s seo
issues.lock=Cuir glas ar an gcomhrá
issues.unlock=Díghlasáil comhrá
-issues.lock.unknown_reason=Ní féidir fadhb a ghlasáil le cúis anaithnid.
issues.lock_duplicate=Ní féidir saincheist a ghlasáil faoi dhó.
issues.unlock_error=Ní féidir saincheist nach bhfuil glasáilte a dhíghlasáil.
issues.lock_with_reason=curtha ar ceal mar <strong>%s</strong> agus comhrá teoranta do chomhoibrithe %s
@@ -1681,16 +1721,20 @@ issues.timetracker_timer_manually_add=Cuir Am leis
issues.time_estimate_set=Socraigh am measta
issues.time_estimate_display=Meastachán: %s
-issues.change_time_estimate_at=d'athraigh an meastachán ama go <b>%s</b> %s
+issues.change_time_estimate_at=d'athraigh an meastachán ama go <b>%[1]s</b> %[2]s
issues.remove_time_estimate_at=baineadh meastachán ama %s
issues.time_estimate_invalid=Tá formáid meastachán ama neamhbhailí
issues.start_tracking_history=thosaigh ag obair %s
issues.tracker_auto_close=Stopfar ama go huathoibríoch nuair a dhúnfar an tsaincheist seo
+issues.stopwatch_already_stopped=Tá an lasc ama don cheist seo stoptha cheana féin
+issues.stopwatch_already_created=Tá an lasc ama don cheist seo ann cheana féin
issues.tracking_already_started=`Tá tús curtha agat cheana féin ag rianú ama ar <a href="%s">eagrán eile</a>!`
-issues.stop_tracking_history=d'oibrigh do <b>%s</b> %s
+issues.stop_tracking=Stad uaineadóir
+issues.stop_tracking_history=d'oibrigh do <b>%[1]s</b> %[2]s
+issues.cancel_tracking=Caith amach
issues.cancel_tracking_history=`rianú ama curtha ar ceal %s`
issues.del_time=Scrios an log ama seo
-issues.add_time_history=cuireadh am caite <b>%s</b> %s leis
+issues.add_time_history=cuireadh am caite <b>%[1]s</b> %[2]s leis
issues.del_time_history=`an t-am caite scriosta %s`
issues.add_time_manually=Cuir Am leis de Láimh
issues.add_time_hours=Uaireanta
@@ -1850,7 +1894,7 @@ pulls.add_prefix=Cuir réimír <strong>%s</strong> leis
pulls.remove_prefix=Bain an réimír <strong>%s</strong>
pulls.data_broken=Tá an t-iarratas tarraingthe seo briste mar gheall ar fhaisnéis forc a bheith in easnamh.
pulls.files_conflicted=Tá athruithe ag an iarratas tarraingthe seo atá contrártha leis an spriocbhrainse.
-pulls.is_checking=Tá seiceáil coinbhleachta cumaisc ar siúl. Bain triail eile as i gceann cúpla nóiméad.
+pulls.is_checking=Ag seiceáil le haghaidh coinbhleachtaí cumaisc ...
pulls.is_ancestor=Tá an brainse seo san áireamh cheana féin sa spriocbhrainse. Níl aon rud le cumasc.
pulls.is_empty=Tá na hathruithe ar an mbrainse seo ar an spriocbhrainse cheana féin. Is tiomantas folamh é seo.
pulls.required_status_check_failed=Níor éirigh le roinnt seiceálacha riachtanacha.
@@ -1920,12 +1964,12 @@ pulls.outdated_with_base_branch=Tá an brainse seo as dáta leis an mbunbhrainse
pulls.close=Dún Iarratas Tarraing
pulls.closed_at=`dhún an t-iarratas tarraingthe seo <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at=`athoscail an t-iarratas tarraingthe seo <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-pulls.cmd_instruction_hint=`Féach ar <a class="show-instruction">treoracha na líne ordaithe</a>.`
+pulls.cmd_instruction_hint=Féach ar threoracha na n-orduithe
pulls.cmd_instruction_checkout_title=Seiceáil
pulls.cmd_instruction_checkout_desc=Ó stór tionscadail, seiceáil brainse nua agus déan tástáil ar na hathruithe.
pulls.cmd_instruction_merge_title=Cumaisc
pulls.cmd_instruction_merge_desc=Cumaisc na hathruithe agus nuashonrú ar Gitea.
-pulls.cmd_instruction_merge_warning=Rabhadh: Ní féidir leis an oibríocht seo an t-iarratas tarraingthe a chumasc toisc nach raibh "cumasc láimhe uathoibríoch" cumasaithe
+pulls.cmd_instruction_merge_warning=Rabhadh: Ní féidir iarratas tarraingthe cumaisc a dhéanamh leis an oibríocht seo mar nach bhfuil "autodetect manual merge" cumasaithe.
pulls.clear_merge_message=Glan an teachtaireacht chumaisc
pulls.clear_merge_message_hint=Má imrítear an teachtaireacht chumaisc ní bhainfear ach ábhar na teachtaireachta tiomanta agus coimeádfar leantóirí git ginte ar nós "Co-Authored-By …".
@@ -1949,6 +1993,7 @@ pulls.upstream_diverging_prompt_behind_1=Tá an brainse seo %[1]d tiomantas taob
pulls.upstream_diverging_prompt_behind_n=Tá an brainse seo %[1]d geallta taobh thiar de %[2]s
pulls.upstream_diverging_prompt_base_newer=Tá athruithe nua ar an mbunbhrainse %s
pulls.upstream_diverging_merge=Forc sionc
+pulls.upstream_diverging_merge_confirm=Ar mhaith leat "%[1]s" a chumasc le "%[2]s"?
pull.deleted_branch=(scriosta): %s
pull.agit_documentation=Déan athbhreithniú ar dhoiciméid faoi AGit
@@ -2108,12 +2153,19 @@ contributors.contribution_type.deletions=Scriosadh
settings=Socruithe
settings.desc=Is é socruithe an áit ar féidir leat na socruithe don stóras a bhainistiú
settings.options=Stóras
+settings.public_access=Rochtain Phoiblí
+settings.public_access_desc=Cumraigh ceadanna rochtana an chuairteora phoiblí chun réamhshocruithe an stóras seo a shárú.
+settings.public_access.docs.not_set=Gan Socrú: níl cead rochtana poiblí breise ar bith. Leanann cead an chuairteora infheictheacht an stór agus ceadanna na mball.
+settings.public_access.docs.anonymous_read=Léamh gan Ainm: is féidir le húsáideoirí nach bhfuil logáilte isteach rochtain a fháil ar an aonad le cead léite.
+settings.public_access.docs.everyone_read=Léamh ag Gach Duine: is féidir le gach úsáideoir logáilte isteach rochtain a fháil ar an aonad le cead léite. Ciallaíonn cead léite na n-aonad eisiúna/iarrataí tarraingthe freisin gur féidir le húsáideoirí saincheisteanna nua/iarratais tarraingthe a chruthú.
+settings.public_access.docs.everyone_write=Scríobh Gach Duine: tá cead scríofa ag gach úsáideoir logáilte isteach don aonad. Ní thacaíonn ach aonad Vicí leis an gcead seo.
settings.collaboration=Comhoibritheoirí
settings.collaboration.admin=Riarthóir
settings.collaboration.write=Scríobh
settings.collaboration.read=Léigh
settings.collaboration.owner=Úinéir
settings.collaboration.undefined=Neamhshainithe
+settings.collaboration.per_unit=Ceadanna Aonaid
settings.hooks=Crúcaí Gréasán
settings.githooks=Crúcanna Git
settings.basic_settings=Socruithe Bunúsacha
@@ -2154,7 +2206,6 @@ settings.advanced_settings=Ardsocruithe
settings.wiki_desc=Cumasaigh Stór Vicí
settings.use_internal_wiki=Úsáid Vicí Insuite
settings.default_wiki_branch_name=Ainm Brainse Réamhshocraithe Vicí
-settings.default_wiki_everyone_access=Cead Rochtana Réamhshocraithe d'úsáideoirí sínithe isteach:
settings.failed_to_change_default_wiki_branch=Theip ar an brainse réamhshocraithe vicí a athrú.
settings.use_external_wiki=Úsáid Vicí Seachtrach
settings.external_wiki_url=URL Vicí Seachtrach
@@ -2283,8 +2334,6 @@ settings.hooks_desc=Déanann Crúcaí Gréasán iarratais HTTP POST go huathoibr
settings.webhook_deletion=Bain Crúca Gréasán
settings.webhook_deletion_desc=Scriostar a shocruithe agus a stair seachadta a bhaineann le Crúca Gréasán a bhaint. Lean ar aghaidh?
settings.webhook_deletion_success=Tá an Crúca Gréasán bainte amach.
-settings.webhook.test_delivery=Seachadadh Tástála
-settings.webhook.test_delivery_desc=Déan tástáil ar an Crúca Gréasán seo le himeacht bhréige.
settings.webhook.test_delivery_desc_disabled=Chun an Crúca Gréasán seo a thástáil le himeacht bhréige, gníomhachtaigh é.
settings.webhook.request=Iarratas
settings.webhook.response=Freagra
@@ -2322,6 +2371,8 @@ settings.event_fork=Forc
settings.event_fork_desc=Forcadh stóras.
settings.event_wiki=Vicí
settings.event_wiki_desc=Leathanach Vicí cruthaithe, athainmnithe, curtha in eagar nó scriosta.
+settings.event_statuses=Stádais
+settings.event_statuses_desc=Nuashonraíodh Stádas Commit ón API.
settings.event_release=Scaoileadh
settings.event_release_desc=Scaoileadh foilsithe, nuashonraithe nó scriosta i stóras.
settings.event_push=Brúigh
@@ -2331,7 +2382,7 @@ settings.event_repository=Stóras
settings.event_repository_desc=Stóras a cruthaíodh nó a scriosadh.
settings.event_header_issue=Imeachtaí Eisiúint
settings.event_issues=Saincheisteanna
-settings.event_issues_desc=Osclaíodh, dúnadh, athosclaíodh nó cuireadh an cheist in eagar.
+settings.event_issues_desc=Ceist oscailte, dúnta, athoscailte, eagarthóireachta nó scriosta.
settings.event_issue_assign=Saincheist Sannaithe
settings.event_issue_assign_desc=Eisiúint sannta nó neamhshannta.
settings.event_issue_label=Eisiúint Lipéadaithe
@@ -2342,7 +2393,7 @@ settings.event_issue_comment=Trácht Eisiúna
settings.event_issue_comment_desc=Trácht eisiúna cruthaithe, curtha in eagar nó a scriosadh.
settings.event_header_pull_request=Tarraingt Imeachtaí Iarratas
settings.event_pull_request=Iarratas Tarraingthe
-settings.event_pull_request_desc=Iarratas tarraingthe oscailte, dúnta, athoscailte nó curtha in eagar.
+settings.event_pull_request_desc=Iarratas tarraingthe a osclaíodh, a dúnadh, a hathosclaíodh, a cuireadh in eagar nó a scriosadh.
settings.event_pull_request_assign=Iarratas Tarraingthe Sannta
settings.event_pull_request_assign_desc=Iarratas tarraingthe sannta nó neamhshannta.
settings.event_pull_request_label=Iarratas Tarraingthe Lipéadaithe
@@ -2359,6 +2410,11 @@ settings.event_pull_request_review_request=Iarratas ar Athbhreithniú Tarraingth
settings.event_pull_request_review_request_desc=Tarraing athbhreithniú iarratais iarrtha nó baineadh iarratas athbhreithnithe.
settings.event_pull_request_approvals=Ceaduithe Iarratais Tarraing
settings.event_pull_request_merge=Cumaisc Iarratas Tarraing
+settings.event_header_workflow=Imeachtaí Sreabhadh Oibre
+settings.event_workflow_run=Rith Sreabhadh Oibre
+settings.event_workflow_run_desc=Tá rith Sreabhadh Oibre Gníomhartha Gitea sa scuaine, ag fanacht, ar siúl, nó críochnaithe.
+settings.event_workflow_job=Poist Sreabhadh Oibre
+settings.event_workflow_job_desc=Gitea Actions Sreabhadh oibre post ciúáilte, ag fanacht, ar siúl, nó críochnaithe.
settings.event_package=Pacáiste
settings.event_package_desc=Pacáiste a cruthaíodh nó a scriosadh i stóras.
settings.branch_filter=Scagaire brainse
@@ -2573,7 +2629,6 @@ diff.commit=tiomantas
diff.git-notes=Nótaí
diff.data_not_available=Níl Ãbhar Difríochtaí Ar Fáil
diff.options_button=Roghanna Diff
-diff.show_diff_stats=Taispeáin Staitisticí
diff.download_patch=Ãoslódáil an comhad paiste
diff.download_diff=Ãoslódáil Comhad Diff
diff.show_split_view=Amharc Scoilt
@@ -2622,6 +2677,9 @@ diff.image.overlay=Forleagan
diff.has_escaped=Tá carachtair Unicode i bhfolach ag an líne seo
diff.show_file_tree=Taispeáin crann comhad
diff.hide_file_tree=Folaigh crann comhad
+diff.submodule_added=Fomhodúl %[1]s curtha leis ag %[2]s
+diff.submodule_deleted=Scriosadh fomhodúl %[1]s ó %[2]s
+diff.submodule_updated=Nuashonraíodh fomhodúl %[1]s: %[2]s
releases.desc=Rian leaganacha tionscadal agus íoslódálacha.
release.releases=Eisiúintí
@@ -2692,6 +2750,7 @@ branch.restore_success=Tá brainse "%s" curtha ar ais.
branch.restore_failed=Theip ar chur ar ais brainse "%s".
branch.protected_deletion_failed=Tá brainse "%s" cosanta. Ní féidir é a scriosadh.
branch.default_deletion_failed=Is é brainse "%s" an brainse réamhshocraithe. Ní féidir é a scriosadh.
+branch.default_branch_not_exist=Níl an brainse réamhshocraithe "%s" ann.
branch.restore=`Athchóirigh Brainse "%s"`
branch.download=`Brainse Ãosluchtaithe "%s"`
branch.rename=`Athainmnigh Brainse "%s"`
@@ -2706,6 +2765,10 @@ branch.create_branch_operation=Cruthaigh brainse
branch.new_branch=Cruthaigh brainse nua
branch.new_branch_from=`Cruthaigh brainse nua ó "%s"`
branch.renamed=Ainmníodh brainse %s go %s.
+branch.rename_default_or_protected_branch_error=Ní féidir ach le riarthóirí brainsí réamhshocraithe nó cosanta a athainmniú.
+branch.rename_protected_branch_failed=Tá an brainse seo faoi chosaint ag rialacha cosanta domhanda.
+branch.commits_divergence_from=Déanann sé dialltacht a thiomnú: %[1]d taobh thiar agus %[2]d chun tosaigh ar %[3]s
+branch.commits_no_divergence=Mar an gcéanna le brainse %[1]s
tag.create_tag=Cruthaigh clib %s
tag.create_tag_operation=Cruthaigh clib
@@ -2719,6 +2782,7 @@ topic.done=Déanta
topic.count_prompt=Ní féidir leat níos mó ná 25 topaicí a roghnú
topic.format_prompt=Ní mór do thopaicí tosú le litir nó uimhir, is féidir daiseanna ('-') agus poncanna ('.') a áireamh, a bheith suas le 35 carachtar ar fad. Ní mór litreacha a bheith i litreacha beaga.
+find_file.follow_symlink=Lean an nasc siombalach seo go dtí an áit a bhfuil sé ag pointeáil air
find_file.go_to_file=Téigh go dtí an comhad
find_file.no_matching=Níl aon chomhad meaitseála le fáil
@@ -2759,6 +2823,7 @@ team_permission_desc=Cead
team_unit_desc=Ceadaigh Rochtain ar Rannóga Stóras
team_unit_disabled=(Díchumasaithe)
+form.name_been_taken=Tá ainm na heagraíochta "%s" tógtha cheana féin.
form.name_reserved=Tá an t-ainm eagraíochta "%s" curtha in áirithe.
form.name_pattern_not_allowed=Ní cheadaítear an patrún "%s" in ainm eagraíochta.
form.create_org_not_allowed=Níl cead agat eagraíocht a chruthú.
@@ -2780,15 +2845,28 @@ settings.visibility.private_shortname=Príobháideach
settings.update_settings=Nuashonrú Socruithe
settings.update_setting_success=Nuashonraíodh socruithe eagraíochta.
-settings.change_orgname_prompt=Nóta: Athróidh ainm na heagraíochta ag athrú URL d'eagraíochta agus saorfar an sean-ainm.
-settings.change_orgname_redirect_prompt=Déanfaidh an sean-ainm a atreorú go dtí go n-éilítear é.
+
+settings.rename=Athainmnigh an Eagraíocht
+settings.rename_desc=Má athraíonn tú ainm na heagraíochta, athrófar URL d’eagraíochta freisin agus saorfar an seanainm.
+settings.rename_success=Athainmníodh an eagraíocht %[1]s go %[2]s go rathúil.
+settings.rename_no_change=Níl aon athrú ar ainm na heagraíochta.
+settings.rename_new_org_name=Ainm Nua na hEagraíochta
+settings.rename_failed=Theip ar athainmniú na hEagraíochta mar gheall ar earráid inmheánach
+settings.rename_notices_1=Nà <strong>FÉIDIR</strong> an oibríocht seo a chealú.
+settings.rename_notices_2=Déanfar an seanainm a atreorú go dtí go n-éileofar é.
+
settings.update_avatar_success=Nuashonraíodh avatar na heagraíochta.
settings.delete=Scrios Eagraíocht
settings.delete_account=Scrios an Eagraíocht seo
settings.delete_prompt=Bainfear an eagraíocht go buan. <strong>Nà FÉIDIR</strong> é seo a chealú!
+settings.name_confirm=Cuir isteach ainm na heagraíochta mar dheimhniú:
+settings.delete_notices_1=Nà <strong>FÉIDIR</strong> an oibríocht seo a chealú.
+settings.delete_notices_2=Scriosfaidh an oibríocht seo go buan gach <strong>stórais</strong> de chuid <strong>%s</strong>, lena n-áirítear cód, saincheisteanna, tuairimí, sonraí vicí agus socruithe comhoibritheora.
+settings.delete_notices_3=Scriosfaidh an oibríocht seo gach <strong>pacáiste</strong> de chuid <strong>%s</strong> go buan.
+settings.delete_notices_4=Scriosfaidh an oibríocht seo gach <strong>tionscadal</strong> de chuid <strong>%s</strong> go buan.
settings.confirm_delete_account=Deimhnigh scriosadh
-settings.delete_org_title=Scrios Eagraíocht
-settings.delete_org_desc=Scriosfar an eagraíocht seo go buan. Lean ar aghaidh?
+settings.delete_failed=Theip ar Scriosadh na hEagraíochta mar gheall ar earráid inmheánach
+settings.delete_successful=Scriosadh an eagraíocht <b>%s</b> go rathúil.
settings.hooks_desc=Cuir crúcaí gréasán in leis a spreagfar do <strong>gach stóras</strong> faoin eagraíocht seo.
settings.labels_desc=Cuir lipéid leis ar féidir iad a úsáid ar shaincheisteanna do <strong>gach stóras</strong> faoin eagraíocht seo.
@@ -2860,6 +2938,18 @@ teams.invite.title=Tugadh cuireadh duit dul isteach i bhfoireann <strong>%s</str
teams.invite.by=Ar cuireadh ó %s
teams.invite.description=Cliceáil ar an gcnaipe thíos le do thoil chun dul isteach san fhoireann.
+view_as_role=Féach mar: %s
+view_as_public_hint=Tá tú ag féachaint ar an README mar úsáideoir poiblí.
+view_as_member_hint=Tá tú ag féachaint ar an README mar bhall den eagraíocht seo.
+
+worktime=Am oibre
+worktime.date_range_start=Dáta tosaithe
+worktime.date_range_end=Dáta deiridh
+worktime.query=Ceist
+worktime.time=Am
+worktime.by_repositories=De réir stórtha
+worktime.by_milestones=De réir clocha míle
+worktime.by_members=Ag baill
[admin]
maintenance=Cothabháil
@@ -3231,8 +3321,6 @@ config.ssh_domain=Fearainn Freastalaí SSH
config.ssh_port=Calafort
config.ssh_listen_port=Éist Calafort
config.ssh_root_path=Cosán Fréimhe
-config.ssh_key_test_path=Cosán Tástáil Eochair
-config.ssh_keygen_path=Keygen ('ssh-keygen') Cosán
config.ssh_minimum_key_size_check=Seiceáil Ãosta Méid Eochair
config.ssh_minimum_key_sizes=Méideanna Ãosta Eochrach
@@ -3355,6 +3443,8 @@ monitor.previous=Am Roimhe Seo
monitor.execute_times=Forghníomhaíochtaí
monitor.process=Próisis reatha
monitor.stacktrace=Rian cruachta
+monitor.trace=Rian
+monitor.performance_logs=Logaí Feidhmíochta
monitor.processes_count=Próisis %d
monitor.download_diagnosis_report=Ãoslódáil tuairisc diagnóis
monitor.desc=Cur síos
@@ -3363,7 +3453,6 @@ monitor.execute_time=Am Forghníomhaithe
monitor.last_execution_result=Toradh
monitor.process.cancel=Cealaigh próiseas
monitor.process.cancel_desc=Má chuirtear próiseas ar ceal d'fhéadfadh go gcaillfí sonraí
-monitor.process.cancel_notices=Cealaigh: <strong>%s</strong>?
monitor.process.children=Leanaí
monitor.queues=Scuaineanna
@@ -3530,6 +3619,7 @@ versions=Leaganacha
versions.view_all=Féach ar gach
dependency.id=ID
dependency.version=Leagan
+search_in_external_registry=Cuardaigh i %s
alpine.registry=Socraigh an chlár seo tríd an url a chur i do chomhad <code>/etc/apk/repositories</code>:
alpine.registry.key=Ãoslódáil eochair RSA poiblí na clárlainne isteach san fhillteán <code>/etc/apk/keys/</code> chun an síniú innéacs a fhíorú:
alpine.registry.info=Roghnaigh $branch agus $repository ón liosta thíos.
@@ -3559,7 +3649,8 @@ conda.install=Chun an pacáiste a shuiteáil ag úsáid Conda, reáchtáil an t-
container.details.type=Cineál Ãomhá
container.details.platform=Ardán
container.pull=Tarraing an íomhá ón líne ordaithe:
-container.digest=Díleáigh:
+container.images=Ãomhánna
+container.digest=Díleáigh
container.multi_arch=Córas Oibriúcháin / Ailtireacht
container.layers=Sraitheanna Ãomhá
container.labels=Lipéid
@@ -3662,11 +3753,18 @@ owner.settings.chef.keypair.description=Tá eochairphéire riachtanach le fíord
secrets=Rúin
description=Cuirfear rúin ar aghaidh chuig gníomhartha áirithe agus ní féidir iad a léamh ar mhalairt.
none=Níl aon rúin ann fós.
-creation=Cuir Rúnda leis
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Cur síos
creation.name_placeholder=carachtair alfanumair nó íoslaghda amháin nach féidir a thosú le GITEA_ nó GITHUB_
creation.value_placeholder=Ionchur ábhar ar bith. Fágfar spás bán ag tús agus ag deireadh ar lár.
-creation.success=Tá an rún "%s" curtha leis.
-creation.failed=Theip ar an rún a chur leis.
+creation.description_placeholder=Cuir isteach cur síos gairid (roghnach).
+
+save_success=Tá an rún "%s" sábháilte.
+save_failed=Theip ar an rún a shábháil.
+
+add_secret=Cuir rún leis
+edit_secret=Cuir rún in eagar
deletion=Bain rún
deletion.description=Is buan rún a bhaint agus ní féidir é a chealú. Lean ort?
deletion.success=Tá an rún bainte.
@@ -3744,6 +3842,11 @@ runs.no_workflows.documentation=Le haghaidh tuilleadh eolais ar Gitea Actions, f
runs.no_runs=Níl aon rith ag an sreabhadh oibre fós.
runs.empty_commit_message=(teachtaireacht tiomantas folamh)
runs.expire_log_message=Glanadh logaí toisc go raibh siad ró-sean.
+runs.delete=Scrios rith sreabha oibre
+runs.cancel=Cealaigh rith an tsreabha oibre
+runs.delete.description=An bhfuil tú cinnte gur mian leat an rith sreabha oibre seo a scriosadh go buan? Ní féidir an gníomh seo a chealú.
+runs.not_done=Níl an rith sreabha oibre seo críochnaithe.
+runs.view_workflow_file=Féach ar chomhad sreabha oibre
workflow.disable=Díchumasaigh sreabhadh oibre
workflow.disable_success=D'éirigh le sreabhadh oibre '%s' a dhíchumasú.
@@ -3755,6 +3858,7 @@ workflow.not_found=Níor aimsíodh sreabhadh oibre '%s'.
workflow.run_success=Ritheann sreabhadh oibre '%s' go rathúil.
workflow.from_ref=Úsáid sreabhadh oibre ó
workflow.has_workflow_dispatch=Tá comhoibriú ag an gcur i bhfeidhm seo le himeacht workflow_dispatch.
+workflow.has_no_workflow_dispatch=Níl aon truicear teagmhais workflow_dispatch ag sreabhadh oibre '%s'.
need_approval_desc=Teastaíonn faomhadh chun sreafaí oibre a rith le haghaidh iarratas tarraingt forc.
@@ -3782,6 +3886,8 @@ deleted.display_name=Tionscadal scriosta
type-1.display_name=Tionscadal Aonair
type-2.display_name=Tionscadal Stórais
type-3.display_name=Tionscadal Eagrúcháin
+enter_fullscreen=Lánscáileán
+exit_fullscreen=Scoir Lánscáileáin
[git.filemode]
changed_filemode=%[1]s → %[2]s
diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini
index f0935a2916..e823b325a5 100644
--- a/options/locale/locale_hu-HU.ini
+++ b/options/locale/locale_hu-HU.ini
@@ -225,6 +225,7 @@ show_only_public=Csak publikus mutatása
issues.in_your_repos=A tárolóidban
+
[explore]
repos=Tárolók
users=Felhasználók
@@ -678,7 +679,6 @@ file_too_large=Ez a fájl túl nagy ahhoz, hogy megjelenítsük.
video_not_supported_in_browser=A böngésző nem támogatja a HTML5 video tag-et.
audio_not_supported_in_browser=A böngésző nem támogatja a HTML5 audio tag-et.
-stored_lfs=Git LFS-el eltárolva
symbolic_link=Szimbolikus hivatkozás
commit_graph=Commit gráf
commit_graph.hide_pr_refs=Pull request-ek elrejtése
@@ -711,6 +711,7 @@ editor.commit_empty_file_header=Egy üres fájl commitolása
editor.no_changes_to_show=Nincsen megjeleníthető változás.
editor.add_subdir=Mappa hozzáadása…
+
commits.commits=Commit-ok
commits.search_all=Minden ág
commits.author=Szerző
@@ -773,8 +774,6 @@ issues.filter_label=Címke
issues.filter_label_no_select=Minden címke
issues.filter_milestone=Mérföldkő
issues.filter_assignee=Megbízott
-issues.filter_assginee_no_select=Minden megbízott
-issues.filter_assginee_no_assignee=Nincs megbízott
issues.filter_type=Típus
issues.filter_type.all_issues=Minden hibajegy
issues.filter_type.assigned_to_you=Hozzám rendelt
@@ -845,7 +844,6 @@ issues.subscribe=Feliratkozás
issues.unsubscribe=Leiratkozás
issues.lock=Beszélgetés lezárása
issues.unlock=Beszélgetés feloldása
-issues.lock.unknown_reason=Egy hibajegy nem zárolható ismeretlen okból.
issues.lock_duplicate=Egy hibajegy nem zárható be kétszer.
issues.unlock_error=Nem nyithatsz meg egy hibajegyet ami nincs is lezárva.
issues.lock_confirm=Lezárás
@@ -1097,7 +1095,6 @@ diff.parent=szülő
diff.commit=commit
diff.git-notes=Megjegyzések
diff.data_not_available=A különbségek nem megjeleníthetőek
-diff.show_diff_stats=Statisztikák mutatása
diff.show_split_view=Osztott nézet
diff.show_unified_view=Egyesített nézet
diff.stats_desc=<strong>%d fájl</strong> változott, egészen pontosan <strong>%d új sor hozzáadva</strong> és <strong>%d régi sor törölve</strong>
@@ -1178,13 +1175,13 @@ settings.visibility.private_shortname=Privát
settings.update_settings=Beállítások frissítése
settings.update_setting_success=A szervezet beállításai frissültek.
+
+
settings.update_avatar_success=A szervezet avatarja frissítve.
settings.delete=Szervezet törlése
settings.delete_account=A szervezet törlése
settings.delete_prompt=A szervezet véglegesen el lesz távolítva. <strong>NEM</strong> vonható vissza!
settings.confirm_delete_account=Törlés megerősítése
-settings.delete_org_title=Szervezet törlése
-settings.delete_org_desc=Ez a szervezet véglegesen törölve lesz. Folytatható?
settings.hooks_desc=Webhook hozzáadása a szervezet <strong>összes tárolójához</strong>.
@@ -1229,6 +1226,7 @@ teams.specific_repositories=Meghatározott tárolók
teams.all_repositories=Minden tároló
+
[admin]
dashboard=Műszerfal
users=Felhasználói fiókok
@@ -1413,8 +1411,6 @@ config.ssh_start_builtin_server=Beépített szerver használata
config.ssh_port=Port
config.ssh_listen_port=Figyelő port
config.ssh_root_path=Gyökérkönyvtár
-config.ssh_key_test_path=Kulcs ellenőrzés útvonala
-config.ssh_keygen_path=Kulcsgeneráló ('ssh-keygen') elérési útja
config.ssh_minimum_key_size_check=Kulcsok minimum méretének ellenőrzése
config.ssh_minimum_key_sizes=Minimális kulcsok méretek
@@ -1598,6 +1594,11 @@ owner.settings.cleanuprules.enabled=Engedélyezett
[secrets]
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Leírás
+
+
+
[actions]
diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini
index 1fcf6d59b6..10b600128d 100644
--- a/options/locale/locale_id-ID.ini
+++ b/options/locale/locale_id-ID.ini
@@ -243,6 +243,7 @@ show_private=Pribadi
issues.in_your_repos=Dalam repositori anda
+
[explore]
repos=Repositori
users=Pengguna
@@ -684,7 +685,6 @@ file_view_raw=Lihat Mentah
file_permalink=Permalink
file_too_large=Berkas terlalu besar untuk ditampilkan.
-stored_lfs=Tersimpan dengan GIT LFS
commit_graph=Grafik Komit
blame=Salahkan
normal_view=Pandangan Normal
@@ -717,6 +717,7 @@ editor.new_branch_name_desc=Nama branch baru…
editor.cancel=Membatalkan
editor.no_changes_to_show=Tidak ada perubahan untuk ditampilkan.
+
commits.commits=Melakukan
commits.author=Penulis
commits.message=Pesan
@@ -762,7 +763,6 @@ issues.delete_branch_at=`telah dihapus cabang <b>%s</b> %s`
issues.filter_label=Label
issues.filter_milestone=Tonggak
issues.filter_assignee=Menerima
-issues.filter_assginee_no_assignee=Tidak ada yang menerima
issues.filter_type=Tipe
issues.filter_type.all_issues=Semua masalah
issues.filter_type.assigned_to_you=Ditugaskan kepada anda
@@ -818,6 +818,7 @@ issues.attachment.open_tab=`Klik untuk melihat "%s" di tab baru`
issues.attachment.download=`Klik untuk mengunduh "%s"`
issues.subscribe=Berlangganan
issues.unsubscribe=Berhenti berlangganan
+issues.unpin=Lepas sematan
issues.delete=Hapus
@@ -955,7 +956,6 @@ settings.delete_notices_1=- Operasi ini <strong>TIDAK BISA</strong> dibatalkan.
settings.delete_collaborator=Menghapus
settings.teams=Tim
settings.add_webhook=Tambahkan Webhook
-settings.webhook.test_delivery=Percobaan Pengiriman
settings.webhook.request=Permintaan
settings.webhook.response=Tanggapan
settings.webhook.headers=Tajuk
@@ -1055,10 +1055,11 @@ settings.visibility.private_shortname=Pribadi
settings.update_settings=Perbarui Setelan
settings.update_setting_success=Pengaturan organisasi telah diperbarui.
+
+
settings.delete=Menghapus Organisasi
settings.delete_account=Menghapus Organisasi Ini
settings.confirm_delete_account=Konfirmasi Penghapusan
-settings.delete_org_title=Menghapus Organisasi
settings.hooks_desc=Tambahkan webhooks yang akan dipicu untuk <strong>semua repositori</strong> di bawah organisasi ini.
@@ -1083,6 +1084,7 @@ teams.delete_team_success=Tim sudah di hapus.
teams.repositories=Tim repositori
+
[admin]
dashboard=Dasbor
organizations=Organisasi
@@ -1217,8 +1219,6 @@ config.ssh_enabled=Aktif
config.ssh_port=Port
config.ssh_listen_port=Listen Port
config.ssh_root_path=Path Induk
-config.ssh_key_test_path=Path Key Test
-config.ssh_keygen_path=Path Keygen ('ssh-keygen')
config.ssh_minimum_key_size_check=Periksa ukuran kunci minimum
config.ssh_minimum_key_sizes=Ukuran kunci minimum
@@ -1396,6 +1396,11 @@ owner.settings.cleanuprules.enabled=Aktif
[secrets]
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Deskripsi
+
+
+
[actions]
diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini
index 1eab4d58be..3b4c605801 100644
--- a/options/locale/locale_is-IS.ini
+++ b/options/locale/locale_is-IS.ini
@@ -49,6 +49,7 @@ webauthn_reload=Endurhlaða
repository=Hugbúnaðarsafn
organization=Stofnun
mirror=Speglun
+issue_milestone=Tímamót
new_repo=Nýtt Hugbúnaðarsafn
new_migrate=Nýr Flutningur
new_mirror=Ný Speglun
@@ -239,6 +240,7 @@ show_only_public=Að sýna aðeins opinber
issues.in_your_repos=à hugbúnaðarsöfnum þínum
+
[explore]
repos=Hugbúnaðarsöfn
users=Notendur
@@ -652,6 +654,7 @@ projects=Verkefni
packages=Pakkar
labels=Skýringar
+milestone=Tímamót
milestones=Tímamót
commits=Framlög
commit=Framlag
@@ -664,7 +667,6 @@ file_view_source=Skoða Frumkóða
file_view_rendered=Skoða Unnið
file_copy_permalink=Afrita Varanlega Slóð
-stored_lfs=Geymt með Git LFS
commit_graph.hide_pr_refs=Fela Sameiningarbeiðnir
commit_graph.monochrome=Einlitað
commit_graph.color=Litað
@@ -687,7 +689,7 @@ editor.create_new_branch=Búðu til <strong>nýja grein</strong> og sameiningarb
editor.create_new_branch_np=Búðu til <strong>nýja grein</strong> fyrir þetta framlag.
editor.new_branch_name_desc=Heiti nýjar greinar…
editor.cancel=Hætta við
-editor.fail_to_update_file_summary=Villuskilaboð:
+
commits.commits=Framlög
commits.author=Höfundur
@@ -1025,11 +1027,9 @@ settings.event_release=Útgáfa
settings.event_push=Push
settings.event_repository=Hugbúnaðarsafn
settings.event_issues=Vandamál
-settings.event_issues_desc=Vandamál opið, lokað, enduropnað eða breytt.
settings.event_issue_label=Vandamál Lýst
settings.event_issue_comment=Ummæli um Vandamál
settings.event_pull_request=Sameiningarbeiðni
-settings.event_pull_request_desc=Sameiningarbeiðni opnuð, lokuð, enduropnuð eða breytt.
settings.active=Virkt
settings.update_webhook=Uppfæra Vefkrók
settings.slack_token=Táknlykill
@@ -1116,6 +1116,8 @@ settings.visibility.private_shortname=Einka
settings.update_settings=Uppfæra Stillingar
+
+
members.private=Faldir
members.owner=Eigandi
members.member=Meðlimur
@@ -1137,6 +1139,7 @@ teams.update_settings=Uppfæra Stillingar
teams.all_repositories=Öll hugbúnaðarsöfn
+
[admin]
repositories=Hugbúnaðarsöfn
config=Stilling
@@ -1323,6 +1326,11 @@ pypi.requires=Þarfnast Python
[secrets]
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Lýsing
+
+
+
[actions]
diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini
index 17f0aa83d2..52fb0a3a50 100644
--- a/options/locale/locale_it-IT.ini
+++ b/options/locale/locale_it-IT.ini
@@ -50,6 +50,7 @@ webauthn_reload=Ricarica
repository=Repository
organization=Organizzazione
mirror=Mirror
+issue_milestone=Traguardo
new_repo=Nuovo Repository
new_migrate=Nuova Migrazione
new_mirror=Nuovo Mirror
@@ -276,6 +277,7 @@ show_only_public=Mostrando solo pubblici
issues.in_your_repos=Nei tuoi repository
+
[explore]
repos=Repository
users=Utenti
@@ -321,7 +323,6 @@ oauth_signup_submit=Completa l'Account
oauth_signin_tab=Collegamento ad un Account Esistente
oauth_signin_title=Accedi per autorizzare l' Account collegato
oauth_signin_submit=Collega Account
-oauth.signin.error=Si è verificato un errore nell'elaborazione della richiesta di autorizzazione. Se questo errore persiste, si prega di contattare l'amministratore del sito.
oauth.signin.error.access_denied=La richiesta di autorizzazione è stata negata.
oauth.signin.error.temporarily_unavailable=Autorizzazione non riuscita perché il server di autenticazione non è temporaneamente disponibile. Riprova più tardi.
openid_connect_submit=Connetti
@@ -942,6 +943,7 @@ labels=Etichette
org_labels_desc=Etichette a livello di organizzazione che possono essere utilizzate con <strong>tutti i repository</strong> sotto questa organizzazione
org_labels_desc_manage=gestisci
+milestone=Traguardo
milestones=Traguardi
commits=Commit
commit=Commit
@@ -965,7 +967,6 @@ file_copy_permalink=Copia Permalink
view_git_blame=Visualizza Git Blame
video_not_supported_in_browser=Il tuo browser non supporta i tag "video" di HTML5.
audio_not_supported_in_browser=Il tuo browser non supporta il tag "video" di HTML5.
-stored_lfs=Memorizzati con Git LFS
symbolic_link=Link Simbolico
commit_graph=Grafico dei commit
commit_graph.select=Seleziona rami
@@ -1013,7 +1014,6 @@ editor.file_changed_while_editing=I contenuti di questo file hanno subito dei ca
editor.commit_empty_file_header=Commit di un file vuoto
editor.commit_empty_file_text=Il file che stai per effettuare il commit è vuoto. Procedere?
editor.no_changes_to_show=Non ci sono cambiamenti da mostrare.
-editor.fail_to_update_file_summary=Messaggio d'errore:
editor.push_rejected_no_message=La modifica è stata rifiutata dal server senza un messaggio. Controlla Git Hooks.
editor.push_rejected=La modifica è stata rifiutata dal server. Controlla Git Hooks.
editor.push_rejected_summary=Messaggio Di Rifiuto Completo:
@@ -1024,6 +1024,7 @@ editor.require_signed_commit=Il branch richiede un commit firmato
editor.cherry_pick=Cherry-pick %s suto:
editor.revert=Ripristina %s su:
+
commits.desc=Sfoglia la cronologia di modifiche del codice rogente.
commits.commits=Commit
commits.nothing_to_compare=Questi rami sono uguali.
@@ -1141,8 +1142,6 @@ issues.filter_milestone=Traguardo
issues.filter_project=Progetto
issues.filter_project_none=Nessun progetto
issues.filter_assignee=Assegnatario
-issues.filter_assginee_no_select=Tutte le assegnazioni
-issues.filter_assginee_no_assignee=Nessun assegnatario
issues.filter_poster=Autore
issues.filter_type=Tipo
issues.filter_type.all_issues=Tutti i problemi
@@ -1191,7 +1190,7 @@ issues.context.edit=Modifica
issues.context.delete=Elimina
issues.reopen_issue=Riapri
issues.create_comment=Commento
-issues.closed_at=`chiuso questo probleam <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+issues.closed_at=`ha chiuso questo problema <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`riaperto questo problema <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`ha fatto riferimento a questa issue dal commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from=`<a href="%[3]s">ha fatto riferimento a questo problema %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
@@ -1236,7 +1235,6 @@ issues.subscribe=Iscriviti
issues.unsubscribe=Annulla iscrizione
issues.lock=Blocca conversazione
issues.unlock=Sblocca conversazione
-issues.lock.unknown_reason=Impossibile bloccare un problema con un motivo sconosciuto.
issues.lock_duplicate=Un issue non può essere bloccato due volte.
issues.unlock_error=Impossibile sbloccare un problema che non è bloccato.
issues.lock_with_reason=ha bloccato come <strong>%s</strong> e limitato la conversazione ai collaboratori %s
@@ -1389,7 +1387,6 @@ pulls.add_prefix=Aggiungi prefisso <strong>%s</strong>
pulls.remove_prefix=Rimuovi il prefisso <strong>%s</strong>
pulls.data_broken=Questa pull request è rovinata a causa di informazioni mancanti del fork.
pulls.files_conflicted=Questa pull request ha modifiche in conflitto con il branch di destinazione.
-pulls.is_checking=Verifica dei conflitti di merge in corso. Riprova tra qualche istante.
pulls.is_ancestor=Questo ramo è già incluso nel ramo di destinazione. Non c'è nulla da unire.
pulls.is_empty=Le modifiche di questo ramo sono già nel ramo di destinazione. Questo sarà un commit vuoto.
pulls.required_status_check_failed=Alcuni controlli richiesti non hanno avuto successo.
@@ -1723,8 +1720,6 @@ settings.hooks_desc=I Webhook effettuano automaticamente richieste HTTP POST ad
settings.webhook_deletion=Rimuovi Webhook
settings.webhook_deletion_desc=Rimuovere un webhook rimuove le sue impostazioni e la sua cronologia di consegna. Continuare?
settings.webhook_deletion_success=Il webhook è stato rimosso.
-settings.webhook.test_delivery=Test di consegna
-settings.webhook.test_delivery_desc=Prova questo webhook con un evento falso.
settings.webhook.request=Richiesta
settings.webhook.response=Risposta
settings.webhook.headers=Intestazioni
@@ -1767,7 +1762,6 @@ settings.event_repository=Repository
settings.event_repository_desc=Repository creato o eliminato.
settings.event_header_issue=Eventi dei Problemi
settings.event_issues=Issues
-settings.event_issues_desc=Issue aperto, chiuso, riaperto o modificato.
settings.event_issue_assign=Issue Assegnato
settings.event_issue_assign_desc=Issue assegnata o non assegnata.
settings.event_issue_label=Issue etichettato
@@ -1778,7 +1772,6 @@ settings.event_issue_comment=Commento Issue
settings.event_issue_comment_desc=Commento issue creato, modificato o rimosso.
settings.event_header_pull_request=Eventi di Pull Request
settings.event_pull_request=Pull Request
-settings.event_pull_request_desc=Pull request aperta, chiusa, riaperta o modificata.
settings.event_pull_request_assign=Pull Request assegnata
settings.event_pull_request_assign_desc=Pull request assegnata o non assegnata.
settings.event_pull_request_label=Pull Request etichettata
@@ -1926,7 +1919,6 @@ diff.commit=commit
diff.git-notes=Note
diff.data_not_available=Dati Diff non disponibili
diff.options_button=Opzioni Diff
-diff.show_diff_stats=Mostra statistiche
diff.download_patch=Scarica il file Patch
diff.download_diff=Scarica il file Diff
diff.show_split_view=Visualizzazione separata
@@ -2082,14 +2074,13 @@ settings.visibility.private_shortname=Privato
settings.update_settings=Aggiorna Impostazioni
settings.update_setting_success=Le impostazioni dell'organizzazione sono state aggiornate.
-settings.change_orgname_redirect_prompt=Il vecchio nome reindirizzerà fino a quando non sarà richiesto.
+
+
settings.update_avatar_success=L'avatar dell'organizzazione è stato aggiornato.
settings.delete=Elimina organizzazione
settings.delete_account=Elimina questa organizzazione
settings.delete_prompt=L'organizzazione verrà rimossa definitivamente. Questa operazione <strong>NON PUÒ</strong> essere annullata!
settings.confirm_delete_account=Conferma Eliminazione
-settings.delete_org_title=Elimina organizzazione
-settings.delete_org_desc=Questa organizzazione verrà eliminata definitivamente. Continuare?
settings.hooks_desc=Aggiungi i webhooks che verranno attivati per <strong>tutti i repository</strong> sotto questa organizzazione.
settings.labels_desc=Aggiungi i webhooks che verranno attivati per <strong>tutti i repository</strong> sotto questa organizzazione.
@@ -2154,6 +2145,7 @@ teams.all_repositories_write_permission_desc=Questo team concede <strong>permess
teams.all_repositories_admin_permission_desc=Questo team concede a <strong>Amministratore</strong> l'accesso a <strong>tutte le repository</strong>: i membri possono leggere, pushare e aggiungere collaboratori alle repository.
+
[admin]
dashboard=Pannello di Controllo
users=Account utenti
@@ -2464,8 +2456,6 @@ config.ssh_domain=Dominio Server Ssh
config.ssh_port=Porta
config.ssh_listen_port=Porta in ascolto
config.ssh_root_path=Percorso Root
-config.ssh_key_test_path=Percorso chiave di test
-config.ssh_keygen_path=Percorso Keygen ('ssh-keygen')
config.ssh_minimum_key_size_check=Verifica delle dimensioni minime della chiave
config.ssh_minimum_key_sizes=Dimensioni minime della chiave
@@ -2788,6 +2778,11 @@ owner.settings.cleanuprules.enabled=Attivo
[secrets]
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Descrizione
+
+
+
[actions]
diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index 89582af89c..face088116 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -54,6 +54,7 @@ webauthn_reload=リロード
repository=リãƒã‚¸ãƒˆãƒª
organization=組織
mirror=ミラー
+issue_milestone=マイルストーン
new_repo=æ–°ã—ã„リãƒã‚¸ãƒˆãƒª
new_migrate=æ–°ã—ã„移行
new_mirror=æ–°ã—ã„ミラー
@@ -112,9 +113,11 @@ copy_type_unsupported=ã“ã®ãƒ•ァイルタイプã¯ã‚³ãƒ”ーã§ãã¾ã›ã‚“
write=書ãè¾¼ã¿
preview=プレビュー
loading=読ã¿è¾¼ã¿ä¸­â€¦
+files=ファイル
error=エラー
error404=アクセスã—よã†ã¨ã—ãŸãƒšãƒ¼ã‚¸ã¯<strong>存在ã—ãªã„</strong>ã‹ã€é–²è¦§ãŒ<strong>許å¯ã•れã¦ã„ã¾ã›ã‚“</strong>。
+error503=サーãƒãƒ¼ã¯ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’完了ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ 後ã§ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。
go_back=戻る
invalid_data=無効ãªãƒ‡ãƒ¼ã‚¿: %v
@@ -127,6 +130,7 @@ pin=ピン留ã‚
unpin=ピン留ã‚解除
artifacts=æˆæžœç‰©
+expired=期é™åˆ‡ã‚Œ
confirm_delete_artifact=アーティファクト %s を削除ã—ã¦ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ
archived=アーカイブ
@@ -168,6 +172,10 @@ search=検索…
type_tooltip=検索タイプ
fuzzy=ã‚ã„ã¾ã„
fuzzy_tooltip=検索語ã«ãŠãŠã‚ˆã一致ã™ã‚‹çµæžœã‚‚å«ã‚ã¾ã™
+words=å˜èªž
+words_tooltip=検索語ã¨ä¸€è‡´ã™ã‚‹çµæžœã ã‘ã‚’å«ã‚ã¾ã™
+regexp=æ­£è¦è¡¨ç¾
+regexp_tooltip=æ­£è¦è¡¨ç¾æ¤œç´¢ãƒ‘ターンã¨ä¸€è‡´ã™ã‚‹çµæžœã ã‘ã‚’å«ã‚ã¾ã™
exact=完全一致
exact_tooltip=検索語ã¨å®Œå…¨ã«ä¸€è‡´ã™ã‚‹çµæžœã ã‘ã‚’å«ã‚ã¾ã™
repo_kind=リãƒã‚¸ãƒˆãƒªã‚’検索...
@@ -244,6 +252,7 @@ license_desc=Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">%[
[install]
install=インストール
+installing_desc=インストール中ã§ã™ã€ãŠå¾…ã¡ãã ã•ã„...
title=åˆæœŸè¨­å®š
docker_helper=Giteaã‚’Docker内ã§å®Ÿè¡Œã™ã‚‹å ´åˆã¯ã€è¨­å®šã‚’変更ã™ã‚‹å‰ã«<a target="_blank" rel="noopener noreferrer" href="%s">ドキュメント</a>を読んã§ãã ã•ã„。
require_db_desc=Giteaã«ã¯ã€MySQLã€PostgreSQLã€MSSQLã€SQLite3ã€ã¾ãŸã¯TiDB(MySQL プロトコル) ãŒå¿…è¦ã§ã™ã€‚
@@ -383,6 +392,13 @@ show_only_public=公開ã®ã¿è¡¨ç¤º
issues.in_your_repos=ã‚ãªãŸã®ãƒªãƒã‚¸ãƒˆãƒª
+guide_title=アクティビティã¯ã‚りã¾ã›ã‚“
+guide_desc=ç¾åœ¨ãƒ•ォロー中ã®ãƒªãƒã‚¸ãƒˆãƒªã‚„ユーザーãŒãªã„ãŸã‚ã€è¡¨ç¤ºã™ã‚‹ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ãŒã‚りã¾ã›ã‚“。 以下ã®ãƒªãƒ³ã‚¯ã‹ã‚‰ã€èˆˆå‘³ã®ã‚るリãƒã‚¸ãƒˆãƒªã‚„ユーザーを探ã™ã“ã¨ãŒã§ãã¾ã™ã€‚
+explore_repos=リãƒã‚¸ãƒˆãƒªã‚’探ã™
+explore_users=ユーザーを探ã™
+empty_org=組織ã¯ã¾ã ã‚りã¾ã›ã‚“。
+empty_repo=リãƒã‚¸ãƒˆãƒªã¯ã¾ã ã‚りã¾ã›ã‚“。
+
[explore]
repos=リãƒã‚¸ãƒˆãƒª
users=ユーザー
@@ -405,6 +421,7 @@ remember_me.compromised=ログイントークンã¯ã‚‚ã†æœ‰åйã§ã¯ãªãã€ã‚
forgot_password_title=パスワードを忘れãŸ
forgot_password=パスワードをãŠå¿˜ã‚Œã§ã™ã‹ï¼Ÿ
need_account=アカウントãŒå¿…è¦ã§ã™ã‹ï¼Ÿ
+sign_up_tip=管ç†è€…権é™ã‚’æŒã¤ã€ã“ã®ã‚·ã‚¹ãƒ†ãƒ ã®æœ€åˆã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’登録ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚ ユーザーåã¨ãƒ‘スワードをよã覚ãˆã¦ãŠã„ã¦ãã ã•ã„。 ユーザーåã¾ãŸã¯ãƒ‘スワードを忘れãŸå ´åˆã¯ã€Giteaã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‚’å‚ç…§ã—ã¦ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’復元ã—ã¦ãã ã•ã„。
sign_up_now=登録ã¯ã“ã¡ã‚‰ã€‚
sign_up_successful=アカウントã¯ç„¡äº‹ã«ä½œæˆã•れã¾ã—ãŸã€‚よã†ã“ã!
confirmation_mail_sent_prompt_ex=æ–°ã—ã„確èªãƒ¡ãƒ¼ãƒ«ã‚’ <b>%s</b> ã«é€ä¿¡ã—ã¾ã—ãŸã€‚ %s以内ã«ãƒ¡ãƒ¼ãƒ«ãƒœãƒƒã‚¯ã‚¹ã‚’確èªã—ã€ç™»éŒ²æ‰‹ç¶šãを完了ã—ã¦ãã ã•ã„。 登録メールアドレスãŒé–“é•ã£ã¦ã„ã‚‹å ´åˆã¯ã€ã‚‚ã†ã„ã¡ã©ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã™ã‚‹ã¨å¤‰æ›´ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚
@@ -435,6 +452,7 @@ use_scratch_code=スクラッãƒã‚³ãƒ¼ãƒ‰ã‚’使ã†
twofa_scratch_used=ã‚ãªãŸã¯ã‚¹ã‚¯ãƒ©ãƒƒãƒã‚³ãƒ¼ãƒ‰ã‚’使用ã—ã¾ã—ãŸã€‚ 2è¦ç´ èªè¨¼ã®è¨­å®šãƒšãƒ¼ã‚¸ã«ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã—ã¾ã—ãŸã®ã§ã€ãƒ‡ãƒã‚¤ã‚¹ã®ç™»éŒ²ã‚’解除ã™ã‚‹ã‹ã€æ–°ã—ã„スクラッãƒã‚³ãƒ¼ãƒ‰ã‚’生æˆã—ã¾ã—ょã†ã€‚
twofa_passcode_incorrect=ãƒ‘ã‚¹ã‚³ãƒ¼ãƒ‰ãŒæ­£ã—ãã‚りã¾ã›ã‚“。デãƒã‚¤ã‚¹ã‚’紛失ã—ãŸå ´åˆã¯ã€ã‚¹ã‚¯ãƒ©ãƒƒãƒã‚³ãƒ¼ãƒ‰ã‚’使ã£ã¦ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã—ã¦ãã ã•ã„。
twofa_scratch_token_incorrect=スクラッãƒã‚³ãƒ¼ãƒ‰ãŒæ­£ã—ãã‚りã¾ã›ã‚“。
+twofa_required=リãƒã‚¸ãƒˆãƒªã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã«ã¯2段階èªè¨¼ã‚’設定ã™ã‚‹ã‹ã€å†åº¦ãƒ­ã‚°ã‚¤ãƒ³ã—ã¦ãã ã•ã„。
login_userpass=サインイン
login_openid=OpenID
oauth_signup_tab=æ–°è¦ã‚¢ã‚«ã‚¦ãƒ³ãƒˆç™»éŒ²
@@ -443,7 +461,7 @@ oauth_signup_submit=アカウント登録完了
oauth_signin_tab=既存アカウントã«ãƒªãƒ³ã‚¯
oauth_signin_title=リンク先アカウントèªå¯ã®ãŸã‚サインイン
oauth_signin_submit=アカウントã«ãƒªãƒ³ã‚¯
-oauth.signin.error=èªå¯ãƒªã‚¯ã‚¨ã‚¹ãƒˆã®å‡¦ç†ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã“ã®ã‚¨ãƒ©ãƒ¼ãŒè§£æ±ºã—ãªã„å ´åˆã¯ã€ã‚µã‚¤ãƒˆç®¡ç†è€…ã«å•ã„åˆã‚ã›ã¦ãã ã•ã„。
+oauth.signin.error.general=èªå¯ãƒªã‚¯ã‚¨ã‚¹ãƒˆã®å‡¦ç†ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ: %s 。ã“ã®ã‚¨ãƒ©ãƒ¼ãŒè§£æ±ºã—ãªã„å ´åˆã¯ã€ã‚µã‚¤ãƒˆç®¡ç†è€…ã«å•ã„åˆã‚ã›ã¦ãã ã•ã„。
oauth.signin.error.access_denied=èªå¯ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒæ‹’å¦ã•れã¾ã—ãŸã€‚
oauth.signin.error.temporarily_unavailable=èªè¨¼ã‚µãƒ¼ãƒãƒ¼ãŒä¸€æ™‚çš„ã«åˆ©ç”¨ã§ããªã„ãŸã‚ã€èªå¯ã«å¤±æ•—ã—ã¾ã—ãŸã€‚後ã§ã‚‚ã†ä¸€åº¦ã‚„り直ã—ã¦ãã ã•ã„。
oauth_callback_unable_auto_reg=è‡ªå‹•ç™»éŒ²ãŒæœ‰åйã«ãªã£ã¦ã„ã¾ã™ãŒã€OAuth2プロãƒã‚¤ãƒ€ãƒ¼ %[1]s ã®å¿œç­”ã¯ãƒ•ィールド %[2]s ãŒä¸è¶³ã—ã¦ãŠã‚Šã€è‡ªå‹•ã§ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’作æˆã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。 アカウントを作æˆã¾ãŸã¯ãƒªãƒ³ã‚¯ã™ã‚‹ã‹ã€ã‚µã‚¤ãƒˆç®¡ç†è€…ã«å•ã„åˆã‚ã›ã¦ãã ã•ã„。
@@ -908,11 +926,13 @@ delete_token_success=トークンを削除ã—ã¾ã—ãŸã€‚ 削除ã—ãŸãƒˆãƒ¼ã‚¯ã
repo_and_org_access=リãƒã‚¸ãƒˆãƒªã¨çµ„ç¹”ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹
permissions_public_only=公開ã®ã¿
permissions_access_all=ã™ã¹ã¦ (公開ã€ãƒ—ライベートã€é™å®š)
-select_permissions=許å¯ã®é¸æŠž
permission_not_set=設定ãªã—
permission_no_access=アクセスä¸å¯
permission_read=読ã¿å–り
permission_write=読ã¿å–ã‚Šã¨æ›¸ãè¾¼ã¿
+permission_anonymous_read=匿åã®èª­ã¿è¾¼ã¿
+permission_everyone_read=全員ã®èª­ã¿è¾¼ã¿
+permission_everyone_write=å…¨å“¡ã®æ›¸ãè¾¼ã¿
access_token_desc=é¸æŠžã—ãŸãƒˆãƒ¼ã‚¯ãƒ³æ¨©é™ã«å¿œã˜ã¦ã€é–¢é€£ã™ã‚‹<a %s>API</a>ルートã®ã¿ã«è¨±å¯ãŒåˆ¶é™ã•れã¾ã™ã€‚ 詳細ã¯<a %s>ドキュメント</a>ã‚’å‚ç…§ã—ã¦ãã ã•ã„。
at_least_one_permission=トークンを作æˆã™ã‚‹ã«ã¯ã€å°‘ãªãã¨ã‚‚ã²ã¨ã¤ã®è¨±å¯ã‚’é¸æŠžã™ã‚‹å¿…è¦ãŒã‚りã¾ã™
permissions_list=許å¯:
@@ -1015,6 +1035,9 @@ new_repo_helper=リãƒã‚¸ãƒˆãƒªã«ã¯ã€ãƒ—ロジェクトã®ã™ã¹ã¦ã®ãƒ•ã‚¡ã
owner=オーナー
owner_helper=リãƒã‚¸ãƒˆãƒªæ•°ã®ä¸Šé™ã«ã‚ˆã‚Šã€ä¸€éƒ¨ã®çµ„ç¹”ã¯ãƒ‰ãƒ­ãƒƒãƒ—ダウンã«è¡¨ç¤ºã•れãªã„å ´åˆãŒã‚りã¾ã™ã€‚
repo_name=リãƒã‚¸ãƒˆãƒªå
+repo_name_profile_public_hint=.profile ã¯ç‰¹åˆ¥ãªãƒªãƒã‚¸ãƒˆãƒªã§ã€ã“れを使用ã—ã¦ã€ã‚ãªãŸã®çµ„ç¹”ã®å…¬é–‹ãƒ—ロフィール(誰ã§ã‚‚閲覧å¯èƒ½)ã« README.md を追加ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ 利用を開始ã™ã‚‹ã«ã¯ã€å¿…ãšå…¬é–‹ãƒªãƒã‚¸ãƒˆãƒªã¨ã—ã€ãƒ—ロフィールディレクトリã«READMEを追加ã—ã¦åˆæœŸåŒ–ã—ã¦ãã ã•ã„。
+repo_name_profile_private_hint=.profile-private ã¯ç‰¹åˆ¥ãªãƒªãƒã‚¸ãƒˆãƒªã§ã€ã“れを使用ã—ã¦ã€ã‚ãªãŸã®çµ„ç¹”ã®ãƒ¡ãƒ³ãƒãƒ¼å‘ã‘プロフィール(組織メンãƒãƒ¼ã®ã¿é–²è¦§å¯èƒ½)ã« README.md を追加ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ 利用を開始ã™ã‚‹ã«ã¯ã€å¿…ãšãƒ—ライベートリãƒã‚¸ãƒˆãƒªã¨ã—ã€ãƒ—ロフィールディレクトリã«READMEを追加ã—ã¦åˆæœŸåŒ–ã—ã¦ãã ã•ã„。
+repo_name_helper=リãƒã‚¸ãƒˆãƒªåã¯ã€çŸ­ãã€è¦šãˆã‚„ã™ãã€ä»–ã¨é‡è¤‡ã—ãªã„キーワードを使用ã—ã¾ã—ょã†ã€‚ リãƒã‚¸ãƒˆãƒªåã‚’ ".profile" ã¾ãŸã¯ ".profile-private" ã«ã—㦠README.md を追加ã™ã‚‹ã¨ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚„組織ã®ãƒ—ロフィールã¨ãªã‚Šã¾ã™ã€‚
repo_size=リãƒã‚¸ãƒˆãƒªã‚µã‚¤ã‚º
template=テンプレート
template_select=ãƒ†ãƒ³ãƒ—ãƒ¬ãƒ¼ãƒˆã‚’é¸æŠžã—ã¦ãã ã•ã„。
@@ -1033,6 +1056,8 @@ fork_to_different_account=別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ãƒ•ォークã™ã‚‹
fork_visibility_helper=フォークã—ãŸãƒªãƒã‚¸ãƒˆãƒªã®å…¬é–‹/éžå…¬é–‹ã¯å¤‰æ›´ã§ãã¾ã›ã‚“。
fork_branch=フォークã«ã‚¯ãƒ­ãƒ¼ãƒ³ã•れるブランãƒ
all_branches=ã™ã¹ã¦ã®ãƒ–ランãƒ
+view_all_branches=ã™ã¹ã¦ã®ãƒ–ランãƒã‚’表示
+view_all_tags=ã™ã¹ã¦ã®ã‚¿ã‚°ã‚’表示
fork_no_valid_owners=ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã«ã¯æœ‰åйãªã‚ªãƒ¼ãƒŠãƒ¼ãŒã„ãªã„ãŸã‚ã€ãƒ•ォークã§ãã¾ã›ã‚“。
fork.blocked_user=リãƒã‚¸ãƒˆãƒªã®ã‚ªãƒ¼ãƒŠãƒ¼ãŒã‚ãªãŸã‚’ブロックã—ã¦ã„ã‚‹ãŸã‚ã€ãƒªãƒã‚¸ãƒˆãƒªã‚’フォークã§ãã¾ã›ã‚“。
use_template=ã“ã®ãƒ†ãƒ³ãƒ—レートを使用
@@ -1107,10 +1132,9 @@ delete_preexisting_success=%s ã®æœªç™»éŒ²ãƒ•ァイルを削除ã—ã¾ã—ãŸ
blame_prior=ã“ã®å¤‰æ›´ã‚ˆã‚Šå‰ã®Blameを表示
blame.ignore_revs=<a href="%s">.git-blame-ignore-revs</a> ã§æŒ‡å®šã•れãŸãƒªãƒ“ジョンã¯é™¤å¤–ã—ã¦ã„ã¾ã™ã€‚ ã“れを迂回ã—ã¦é€šå¸¸ã®Blame表示を見るã«ã¯ <a href="%s">ã“ã“</a>をクリック。
blame.ignore_revs.failed=<a href="%s">.git-blame-ignore-revs</a> ã«ã‚ˆã‚‹ãƒªãƒ“ジョンã®ç„¡è¦–ã¯å¤±æ•—ã—ã¾ã—ãŸã€‚
+user_search_tooltip=最大30人ã¾ã§ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’表示
-tree_path_not_found_commit=パス %[1]s ã¯ã‚³ãƒŸãƒƒãƒˆ %[2]s ã«å­˜åœ¨ã—ã¾ã›ã‚“
-tree_path_not_found_branch=パス %[1]s ã¯ãƒ–ランム%[2]s ã«å­˜åœ¨ã—ã¾ã›ã‚“
-tree_path_not_found_tag=パス %[1]s ã¯ã‚¿ã‚° %[2]s ã«å­˜åœ¨ã—ã¾ã›ã‚“
+tree_path_not_found=パス %[1]s 㯠%[2]s ã«å­˜åœ¨ã—ã¾ã›ã‚“
transfer.accept=移転を承èª
transfer.accept_desc=`"%s" ã«ç§»è»¢`
@@ -1121,6 +1145,7 @@ transfer.no_permission_to_reject=ã“ã®ç§»è»¢ã‚’æ‹’å¦ã™ã‚‹æ¨©é™ãŒã‚りã¾ã
desc.private=プライベート
desc.public=公開
+desc.public_access=公開アクセス
desc.template=テンプレート
desc.internal=内部
desc.archived=アーカイブ
@@ -1228,6 +1253,7 @@ create_new_repo_command=コマンドラインã‹ã‚‰æ–°ã—ã„リãƒã‚¸ãƒˆãƒªã‚’ä½
push_exist_repo=コマンドラインã‹ã‚‰æ—¢å­˜ã®ãƒªãƒã‚¸ãƒˆãƒªã‚’プッシュ
empty_message=ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã®ä¸­ã«ã¯ä½•ã‚‚ã‚りã¾ã›ã‚“。
broken_message=ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã®åŸºç¤Žã¨ãªã‚‹ Git ã®ãƒ‡ãƒ¼ã‚¿ã‚’読ã¿å–れã¾ã›ã‚“。ã“ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã®ç®¡ç†è€…ã«ç›¸è«‡ã™ã‚‹ã‹ã€ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã‚’削除ã—ã¦ãã ã•ã„。
+no_branch=ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã«ã¯ãƒ–ランãƒãŒã‚りã¾ã›ã‚“。
code=コード
code.desc=ソースコードã€ãƒ•ァイルã€ã‚³ãƒŸãƒƒãƒˆã€ãƒ–ランãƒã«ã‚¢ã‚¯ã‚»ã‚¹ã€‚
@@ -1247,6 +1273,7 @@ labels=ラベル
org_labels_desc=組織ã§å®šç¾©ã•れã¦ã„るラベル (組織ã®<strong>ã™ã¹ã¦ã®ãƒªãƒã‚¸ãƒˆãƒª</strong>ã§ä½¿ç”¨å¯èƒ½ãªã‚‚ã®)
org_labels_desc_manage=編集
+milestone=マイルストーン
milestones=マイルストーン
commits=コミット
commit=コミット
@@ -1280,7 +1307,6 @@ file_copy_permalink=パーマリンクをコピー
view_git_blame=Git Blameを表示
video_not_supported_in_browser=ã“ã®ãƒ–ラウザã¯HTML5ã®videoタグをサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“。
audio_not_supported_in_browser=ã“ã®ãƒ–ラウザーã¯HTML5ã®audioタグをサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“。
-stored_lfs=Git LFSã§ä¿ç®¡ã•れã¦ã„ã¾ã™
symbolic_link=シンボリック リンク
executable_file=実行ファイル
vendored=ベンダーファイル
@@ -1306,7 +1332,9 @@ editor.upload_file=ファイルをアップロード
editor.edit_file=ファイルを編集
editor.preview_changes=変更をプレビュー
editor.cannot_edit_lfs_files=LFSã®ãƒ•ァイルã¯Webインターフェースã§ç·¨é›†ã§ãã¾ã›ã‚“。
+editor.cannot_edit_too_large_file=ã“ã®ãƒ•ァイルã¯å¤§ãã™ãŽã‚‹ãŸã‚ã€ç·¨é›†ã§ãã¾ã›ã‚“。
editor.cannot_edit_non_text_files=ãƒã‚¤ãƒŠãƒªãƒ•ァイルã¯Webインターフェースã§ç·¨é›†ã§ãã¾ã›ã‚“。
+editor.file_not_editable_hint=åå‰ã®å¤‰æ›´ã‚„移動ã¯å¯èƒ½ã§ã™ã€‚
editor.edit_this_file=ファイルを編集
editor.this_file_locked=ファイルã¯ãƒ­ãƒƒã‚¯ã•れã¦ã„ã¾ã™
editor.must_be_on_a_branch=ã“ã®ãƒ•ァイルを変更ã—ãŸã‚Šå¤‰æ›´ã®ææ¡ˆã‚’ã™ã‚‹ã«ã¯ã€ãƒ–ランãƒä¸Šã«ã„ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚
@@ -1326,7 +1354,7 @@ editor.update=%s ã‚’æ›´æ–°
editor.delete=%s を削除
editor.patch=パッãƒã®é©ç”¨
editor.patching=パッãƒ:
-editor.fail_to_apply_patch=`パッãƒã‚’é©ç”¨ã§ãã¾ã›ã‚“ "%s"`
+editor.fail_to_apply_patch=パッãƒã‚’é©ç”¨ã§ãã¾ã›ã‚“
editor.new_patch=æ–°ã—ã„パッãƒ
editor.commit_message_desc=詳細ãªèª¬æ˜Žã‚’追加…
editor.signoff_desc=ã‚³ãƒŸãƒƒãƒˆãƒ­ã‚°ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã®æœ€å¾Œã«ã‚³ãƒŸãƒƒã‚¿ãƒ¼ã® Signed-off-by 行を追加
@@ -1339,13 +1367,14 @@ editor.new_branch_name_desc=æ–°ã—ã„ブランãƒå…
editor.cancel=キャンセル
editor.filename_cannot_be_empty=ファイルåã¯ç©ºã«ã§ãã¾ã›ã‚“。
editor.filename_is_invalid=`ファイルåãŒä¸æ­£ã§ã™: "%s"`
+editor.commit_email=コミット メールアドレス
+editor.invalid_commit_email=コミットã«ä½¿ã†ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ãŒæ­£ã—ãã‚りã¾ã›ã‚“。
editor.branch_does_not_exist=ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã«ãƒ–ランム"%s" ã¯å­˜åœ¨ã—ã¾ã›ã‚“。
editor.branch_already_exists=ブランム"%s" ã¯ã€ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã«æ—¢ã«å­˜åœ¨ã—ã¾ã™ã€‚
editor.directory_is_a_file=ディレクトリå "%s" ã¯ã™ã§ã«ãƒªãƒã‚¸ãƒˆãƒªå†…ã®ãƒ•ァイルã§ä½¿ç”¨ã•れã¦ã„ã¾ã™ã€‚
editor.file_is_a_symlink=`"%s" ã¯ã‚·ãƒ³ãƒœãƒªãƒƒã‚¯ãƒªãƒ³ã‚¯ã§ã™ã€‚ シンボリックリンクã¯Webエディターã§ç·¨é›†ã§ãã¾ã›ã‚“。`
editor.filename_is_a_directory=ファイルå "%s" ã¯ã€ã“ã®ãƒªãƒã‚¸ãƒˆãƒªä¸Šã§ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªåã¨ã—ã¦ã™ã§ã«ä½¿ç”¨ã•れã¦ã„ã¾ã™ã€‚
-editor.file_editing_no_longer_exists=編集中ã®ãƒ•ァイル "%s" ãŒã€ã‚‚ã†ãƒªãƒã‚¸ãƒˆãƒªå†…ã«ã‚りã¾ã›ã‚“。
-editor.file_deleting_no_longer_exists=削除ã—よã†ã¨ã—ãŸãƒ•ァイル "%s" ãŒã€ã™ã§ã«ãƒªãƒã‚¸ãƒˆãƒªå†…ã«ã‚りã¾ã›ã‚“。
+editor.file_modifying_no_longer_exists=修正中ã®ãƒ•ァイル "%s" ãŒã€ã™ã§ã«ãƒªãƒã‚¸ãƒˆãƒªå†…ã«ã‚りã¾ã›ã‚“。
editor.file_changed_while_editing=ã‚ãªãŸãŒç·¨é›†ã‚’é–‹å§‹ã—ãŸã‚ã¨ã€ãƒ•ァイルã®å†…容ãŒå¤‰æ›´ã•れã¾ã—ãŸã€‚ <a target="_blank" rel="noopener noreferrer" href="%s">ã“ã“をクリック</a>ã—ã¦ä½•ãŒå¤‰æ›´ã•れãŸã‹ç¢ºèªã™ã‚‹ã‹ã€<strong>ã‚‚ã†ä¸€åº¦"変更をコミット"をクリック</strong>ã—ã¦ä¸Šæ›¸ãã—ã¾ã™ã€‚
editor.file_already_exists=ファイル "%s" ã¯ã€ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã«æ—¢ã«å­˜åœ¨ã—ã¾ã™ã€‚
editor.commit_id_not_matching=コミットIDãŒç·¨é›†ã‚’é–‹å§‹ã—ãŸã¨ãã®IDã¨ä¸€è‡´ã—ã¾ã›ã‚“。 パッãƒç”¨ã®ãƒ–ランãƒã«ã‚³ãƒŸãƒƒãƒˆã—ãŸã‚ã¨ãƒžãƒ¼ã‚¸ã—ã¦ãã ã•ã„。
@@ -1353,8 +1382,6 @@ editor.push_out_of_date=ã“ã®ãƒ—ãƒƒã‚·ãƒ¥ã¯æœ€æ–°ã§ã¯ãªã„よã†ã§ã™ã€‚
editor.commit_empty_file_header=空ファイルã®ã‚³ãƒŸãƒƒãƒˆ
editor.commit_empty_file_text=コミットã—よã†ã¨ã—ã¦ã„るファイルã¯ç©ºã§ã™ã€‚ 続行ã—ã¾ã™ã‹ï¼Ÿ
editor.no_changes_to_show=表示ã™ã‚‹å¤‰æ›´ç®‡æ‰€ã¯ã‚りã¾ã›ã‚“。
-editor.fail_to_update_file=ファイル "%s" を作æˆã¾ãŸã¯å¤‰æ›´ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚
-editor.fail_to_update_file_summary=エラーメッセージ:
editor.push_rejected_no_message=サーãƒãƒ¼ãŒãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’出ã•ãšã«å¤‰æ›´ã‚’æ‹’å¦ã—ã¾ã—ãŸã€‚ Git フックを確èªã—ã¦ãã ã•ã„。
editor.push_rejected=サーãƒãƒ¼ãŒå¤‰æ›´ã‚’æ‹’å¦ã—ã¾ã—ãŸã€‚ Gitフックを確èªã—ã¦ãã ã•ã„。
editor.push_rejected_summary=æ‹’å¦ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸å…¨ä½“:
@@ -1368,6 +1395,9 @@ editor.user_no_push_to_branch=ユーザーã¯ãƒ–ランãƒã«ãƒ—ッシュã§ãã
editor.require_signed_commit=ブランãƒã§ã¯ç½²åã•れãŸã‚³ãƒŸãƒƒãƒˆãŒå¿…é ˆã§ã™
editor.cherry_pick=ãƒã‚§ãƒªãƒ¼ãƒ”ック %s:
editor.revert=リãƒãƒ¼ãƒˆ %s:
+editor.failed_to_commit=変更ã®ã‚³ãƒŸãƒƒãƒˆã«å¤±æ•—ã—ã¾ã—ãŸã€‚
+editor.failed_to_commit_summary=エラーメッセージ:
+
commits.desc=ソースコードã®å¤‰æ›´å±¥æ­´ã‚’å‚ç…§ã—ã¾ã™ã€‚
commits.commits=コミット
@@ -1387,6 +1417,7 @@ commits.signed_by_untrusted_user_unmatched=コミッターã¨ä¸€è‡´ã—ãªã„ä¿¡é
commits.gpg_key_id=GPGキーID
commits.ssh_key_fingerprint=SSHéµã®ãƒ•ィンガープリント
commits.view_path=ã“ã®æ™‚点を表示
+commits.view_file_diff=ã“ã®ãƒ•ァイルã®ã€ã“ã®ã‚³ãƒŸãƒƒãƒˆã§ã®å¤‰æ›´å†…容を表示
commit.operations=æ“作
commit.revert=リãƒãƒ¼ãƒˆ
@@ -1447,6 +1478,8 @@ issues.filter_milestones=マイルストーンã®çµžã‚Šè¾¼ã¿
issues.filter_projects=プロジェクトã®çµžã‚Šè¾¼ã¿
issues.filter_labels=ラベルã®çµžã‚Šè¾¼ã¿
issues.filter_reviewers=レビューアã®çµžã‚Šè¾¼ã¿
+issues.filter_no_results=æ¤œç´¢çµæžœãªã—
+issues.filter_no_results_placeholder=検索フィルターを変ãˆã¦ã¿ã¦ä¸‹ã•ã„。
issues.new=æ–°ã—ã„イシュー
issues.new.title_empty=タイトルã¯ç©ºã«ã§ãã¾ã›ã‚“
issues.new.labels=ラベル
@@ -1522,11 +1555,14 @@ issues.filter_project=プロジェクト
issues.filter_project_all=ã™ã¹ã¦ã®ãƒ—ロジェクト
issues.filter_project_none=プロジェクトãªã—
issues.filter_assignee=担当者
-issues.filter_assginee_no_select=ã™ã¹ã¦ã®æ‹…当者
-issues.filter_assginee_no_assignee=担当者ãªã—
+issues.filter_assignee_no_assignee=担当者ãªã—
+issues.filter_assignee_any_assignee=担当者ã‚り
issues.filter_poster=作æˆè€…
+issues.filter_user_placeholder=ユーザーを検索
+issues.filter_user_no_select=ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼
issues.filter_type=タイプ
issues.filter_type.all_issues=ã™ã¹ã¦ã®ã‚¤ã‚·ãƒ¥ãƒ¼
+issues.filter_type.all_pull_requests=ã™ã¹ã¦ã®ãƒ—ルリクエスト
issues.filter_type.assigned_to_you=è‡ªåˆ†ãŒæ‹…当
issues.filter_type.created_by_you=自分ãŒä½œæˆ
issues.filter_type.mentioning_you=自分ãŒé–¢ä¿‚
@@ -1618,12 +1654,15 @@ issues.save=ä¿å­˜
issues.label_title=åå‰
issues.label_description=説明
issues.label_color=カラー
+issues.label_color_invalid=無効ãªè‰²ã§ã™
issues.label_exclusive=排他
issues.label_archive=アーカイブ ラベル
issues.label_archived_filter=アーカイブã•れãŸãƒ©ãƒ™ãƒ«ã‚’表示
issues.label_archive_tooltip=アーカイブã•れãŸãƒ©ãƒ™ãƒ«ã¯ã€ãƒ©ãƒ™ãƒ«ã«ã‚ˆã‚‹æ¤œç´¢æ™‚ã®ã‚µã‚¸ã‚§ã‚¹ãƒˆã‹ã‚‰ãƒ‡ãƒ•ォルトã§é™¤å¤–ã•れã¾ã™ã€‚
issues.label_exclusive_desc=ラベルåã‚’ <code>スコープ/アイテム</code> ã®å½¢ã«ã™ã‚‹ã“ã¨ã§ã€ä»–ã® <code>スコープ/</code> ãƒ©ãƒ™ãƒ«ã¨æŽ’ä»–çš„ã«ãªã‚Šã¾ã™ã€‚
issues.label_exclusive_warning=イシューやプルリクエストã®ãƒ©ãƒ™ãƒ«ç·¨é›†ã§ã¯ã€ç«¶åˆã™ã‚‹ã‚¹ã‚³ãƒ¼ãƒ—付ãラベルã¯è§£é™¤ã•れã¾ã™ã€‚
+issues.label_exclusive_order=ソート順
+issues.label_exclusive_order_tooltip=åŒã˜ã‚¹ã‚³ãƒ¼ãƒ—å†…ã®æŽ’ä»–çš„ãªãƒ©ãƒ™ãƒ«ã¯ã€ã“ã®æ•°å€¤é †ã«ã‚½ãƒ¼ãƒˆã•れã¾ã™ã€‚
issues.label_count=ラベル %d件
issues.label_open_issues=オープン中ã®ã‚¤ã‚·ãƒ¥ãƒ¼ %dä»¶
issues.label_edit=編集
@@ -1641,13 +1680,12 @@ issues.attachment.open_tab=`クリックã—ã¦æ–°ã—ã„タブ㧠"%s" を見る`
issues.attachment.download=`クリックã—㦠"%s" をダウンロード`
issues.subscribe=購読ã™ã‚‹
issues.unsubscribe=購読を解除
-issues.unpin_issue=イシューã®ãƒ”ン留ã‚を解除
+issues.unpin=ピン留ã‚解除
issues.max_pinned=ã“れ以上イシューをピン留ã‚ã§ãã¾ã›ã‚“
issues.pin_comment=ãŒãƒ”ン留゠%s
issues.unpin_comment=ãŒãƒ”ン留ã‚を解除 %s
issues.lock=会話をロック
issues.unlock=会話をアンロック
-issues.lock.unknown_reason=未定義ã®ç†ç”±ã§ã¯ã‚¤ã‚·ãƒ¥ãƒ¼ã‚’ロックã§ãã¾ã›ã‚“。
issues.lock_duplicate=イシューã¯äºŒé‡ã«ãƒ­ãƒƒã‚¯ã§ãã¾ã›ã‚“。
issues.unlock_error=ロックã•れã¦ã„ãªã„イシューをアンロックã§ãã¾ã›ã‚“。
issues.lock_with_reason=ãŒ<strong>%s</strong>ã®ãŸã‚ロックã—会話を共åŒä½œæ¥­è€…ã«é™å®š %s
@@ -1676,16 +1714,18 @@ issues.timetracker_timer_manually_add=時間を追加
issues.time_estimate_set=è¦‹ç©æ™‚間を設定
issues.time_estimate_display=è¦‹ç©æ™‚é–“: %s
-issues.change_time_estimate_at=ãŒè¦‹ç©æ™‚é–“ã‚’ <b>%s</b> ã«å¤‰æ›´ %s
+issues.change_time_estimate_at=ãŒè¦‹ç©æ™‚é–“ã‚’ <b>%[1]s</b> ã«å¤‰æ›´ %[2]s
issues.remove_time_estimate_at=ãŒè¦‹ç©æ™‚間を削除 %s
issues.time_estimate_invalid=è¦‹ç©æ™‚é–“ã®ãƒ•ォーマットãŒä¸æ­£ã§ã™
issues.start_tracking_history=ãŒä½œæ¥­ã‚’é–‹å§‹ %s
issues.tracker_auto_close=タイマーã¯ã€ã“ã®ã‚¤ã‚·ãƒ¥ãƒ¼ãŒã‚¯ãƒ­ãƒ¼ã‚ºã•れるã¨è‡ªå‹•çš„ã«çµ‚了ã—ã¾ã™
issues.tracking_already_started=`<a href="%s">別ã®ã‚¤ã‚·ãƒ¥ãƒ¼</a>ã§æ—¢ã«ã‚¿ã‚¤ãƒ ãƒˆãƒ©ãƒƒã‚­ãƒ³ã‚°ã‚’é–‹å§‹ã—ã¦ã„ã¾ã™ï¼`
-issues.stop_tracking_history=㌠<b>%s</b> ã®ä½œæ¥­ã‚’終了 %s
+issues.stop_tracking=タイマー終了
+issues.stop_tracking_history=㌠<b>%[1]s</b> ã®ä½œæ¥­ã‚’終了 %[2]s
+issues.cancel_tracking=破棄
issues.cancel_tracking_history=`ãŒã‚¿ã‚¤ãƒ ãƒˆãƒ©ãƒƒã‚­ãƒ³ã‚°ã‚’中止 %s`
issues.del_time=ã“ã®ã‚¿ã‚¤ãƒ ãƒ­ã‚°ã‚’削除
-issues.add_time_history=ãŒä½œæ¥­æ™‚é–“ <b>%s</b> を追加 %s
+issues.add_time_history=ãŒä½œæ¥­æ™‚é–“ <b>%[1]s</b> を追加 %[2]s
issues.del_time_history=`ãŒä½œæ¥­æ™‚間を削除 %s`
issues.add_time_manually=æ™‚é–“ã®æ‰‹å…¥åŠ›
issues.add_time_hours=時間
@@ -1845,7 +1885,7 @@ pulls.add_prefix=先頭㫠<strong>%s</strong> を追加
pulls.remove_prefix=先頭㮠<strong>%s</strong> を除去
pulls.data_broken=ã“ã®ãƒ—ルリクエストã¯ã€ãƒ•ã‚©ãƒ¼ã‚¯ã®æƒ…å ±ãŒè¦‹ã¤ã‹ã‚‰ãªã„ãŸã‚壊れã¦ã„ã¾ã™ã€‚
pulls.files_conflicted=ã“ã®ãƒ—ルリクエストã¯ã€ã‚¿ãƒ¼ã‚²ãƒƒãƒˆãƒ–ランãƒã¨ç«¶åˆã™ã‚‹å¤‰æ›´ã‚’å«ã‚“ã§ã„ã¾ã™ã€‚
-pulls.is_checking=マージã®ã‚³ãƒ³ãƒ•リクトを確èªä¸­ã§ã™ã€‚ å°‘ã—å¾…ã£ã¦ã‹ã‚‰ã‚‚ã†ä¸€åº¦å®Ÿè¡Œã—ã¦ãã ã•ã„。
+pulls.is_checking=マージã®ç«¶åˆã‚’確èªã—ã¦ã„ã¾ã™...
pulls.is_ancestor=ã“ã®ãƒ–ランãƒã¯æ—¢ã«ã‚¿ãƒ¼ã‚²ãƒƒãƒˆãƒ–ランãƒã«å«ã¾ã‚Œã¦ã„ã¾ã™ã€‚マージã™ã‚‹ã‚‚ã®ã¯ã‚りã¾ã›ã‚“。
pulls.is_empty=ã“ã®ãƒ–ランãƒã®å¤‰æ›´ã¯æ—¢ã«ã‚¿ãƒ¼ã‚²ãƒƒãƒˆãƒ–ランãƒã«ã‚りã¾ã™ã€‚ã“れã¯ç©ºã®ã‚³ãƒŸãƒƒãƒˆã«ãªã‚Šã¾ã™ã€‚
pulls.required_status_check_failed=ã„ãã¤ã‹ã®å¿…è¦ãªã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒã‚§ãƒƒã‚¯ãŒæˆåŠŸã—ã¦ã„ã¾ã›ã‚“。
@@ -1915,12 +1955,11 @@ pulls.outdated_with_base_branch=ã“ã®ãƒ–ランãƒã¯ãƒ™ãƒ¼ã‚¹ãƒ–ランãƒã«å¯¾
pulls.close=プルリクエストをクローズ
pulls.closed_at=`ãŒãƒ—ルリクエストをクローズ <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at=`ãŒãƒ—ルリクエストをå†ã‚ªãƒ¼ãƒ—ン <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-pulls.cmd_instruction_hint=`<a class="show-instruction">ã‚³ãƒžãƒ³ãƒ‰ãƒ©ã‚¤ãƒ³ã®æ‰‹é †</a>を表示ã—ã¾ã™ã€‚`
+pulls.cmd_instruction_hint=ã‚³ãƒžãƒ³ãƒ‰ãƒ©ã‚¤ãƒ³ã®æ‰‹é †ã‚’表示
pulls.cmd_instruction_checkout_title=ãƒã‚§ãƒƒã‚¯ã‚¢ã‚¦ãƒˆ
pulls.cmd_instruction_checkout_desc=プロジェクトリãƒã‚¸ãƒˆãƒªã‹ã‚‰æ–°ã—ã„ブランãƒã‚’ãƒã‚§ãƒƒã‚¯ã‚¢ã‚¦ãƒˆã—ã€å¤‰æ›´å†…容をテストã—ã¾ã™ã€‚
pulls.cmd_instruction_merge_title=マージ
pulls.cmd_instruction_merge_desc=変更内容をマージã—ã¦ã€Giteaã«å映ã—ã¾ã™ã€‚
-pulls.cmd_instruction_merge_warning=警告: 「手動マージã®è‡ªå‹•検出ã€ãŒæœ‰åйã§ã¯ãªã„ãŸã‚ã€ã“ã®æ“作ã§ã¯ãƒ—ルリクエストをマージã§ãã¾ã›ã‚“
pulls.clear_merge_message=マージメッセージをクリア
pulls.clear_merge_message_hint=マージメッセージã®ã‚¯ãƒªã‚¢ã¯ã€ã‚³ãƒŸãƒƒãƒˆãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã®é™¤åŽ»ã ã‘を行ã„ã¾ã™ã€‚ 生æˆã•れãŸGitトレーラー("Co-Authored-By …" ç­‰)ã¯ãã®ã¾ã¾æ®‹ã‚Šã¾ã™ã€‚
@@ -1940,8 +1979,11 @@ pulls.delete.title=ã“ã®ãƒ—ルリクエストを削除ã—ã¾ã™ã‹ï¼Ÿ
pulls.delete.text=本当ã«ã“ã®ãƒ—ルリクエストを削除ã—ã¾ã™ã‹? (ã“れã¯ã™ã¹ã¦ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„を完全ã«å‰Šé™¤ã—ã¾ã™ã€‚ ä¿å­˜ã—ã¦ãŠããŸã„å ´åˆã¯ã€ä»£ã‚りã«ã‚¯ãƒ­ãƒ¼ã‚ºã™ã‚‹ã“ã¨ã‚’検討ã—ã¦ãã ã•ã„)
pulls.recently_pushed_new_branches=%[2]s ã€ã‚ãªãŸã¯ãƒ–ランム<strong>%[1]s</strong> ã«ãƒ—ッシュã—ã¾ã—ãŸ
+pulls.upstream_diverging_prompt_behind_1=ã“ã®ãƒ–ランãƒã¯ %[2]s よりも %[1]d コミットé…れã¦ã„ã¾ã™
+pulls.upstream_diverging_prompt_behind_n=ã“ã®ãƒ–ランãƒã¯ %[2]s よりも %[1]d コミットé…れã¦ã„ã¾ã™
pulls.upstream_diverging_prompt_base_newer=ベースブランム%s ã«æ–°ã—ã„変更ãŒã‚りã¾ã™
pulls.upstream_diverging_merge=ãƒ•ã‚©ãƒ¼ã‚¯ã‚’åŒæœŸ
+pulls.upstream_diverging_merge_confirm=`"%[2]s" ã« "%[1]s" をマージã—ã¦ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ`
pull.deleted_branch=(削除済ã¿):%s
pull.agit_documentation=AGitã«é–¢ã™ã‚‹ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã‚’確èªã™ã‚‹
@@ -2101,6 +2143,12 @@ contributors.contribution_type.deletions=削除
settings=設定
settings.desc=設定ã§ã¯ã€ãƒªãƒã‚¸ãƒˆãƒªã®è¨­å®šã‚’管ç†ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚
settings.options=リãƒã‚¸ãƒˆãƒª
+settings.public_access=公開アクセス
+settings.public_access_desc=外部ã‹ã‚‰ã®è¨ªå•者ã®ã‚¢ã‚¯ã‚»ã‚¹æ¨©é™ã«ã¤ã„ã¦ã€ã“ã®ãƒªãƒã‚¸ãƒˆãƒªã®ãƒ‡ãƒ•ォルト設定を上書ãã—ã¾ã™ã€‚
+settings.public_access.docs.not_set=設定ãªã—: 公開アクセス権é™ã¯ã‚りã¾ã›ã‚“。訪å•è€…ã®æ¨©é™ã¯ã€ãƒªãƒã‚¸ãƒˆãƒªã®å…¬é–‹ç¯„囲ã¨ãƒ¡ãƒ³ãƒãƒ¼ã®æ¨©é™ã«å¾“ã„ã¾ã™ã€‚
+settings.public_access.docs.anonymous_read=匿åã®èª­ã¿è¾¼ã¿: ログインã—ã¦ã„ãªã„ユーザーã¯èª­ã¿å–り権é™ã§ãƒ¦ãƒ‹ãƒƒãƒˆã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™ã€‚
+settings.public_access.docs.everyone_read=全員ã®èª­ã¿è¾¼ã¿: ã™ã¹ã¦ã®ãƒ­ã‚°ã‚¤ãƒ³ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯èª­ã¿å–り権é™ã§ãƒ¦ãƒ‹ãƒƒãƒˆã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™ã€‚イシュー/プルリクエストユニットã®èª­ã¿å–り権é™ã¯ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒæ–°ã—ã„イシュー/プルリクエストを作æˆã§ãã‚‹ã“ã¨ã‚’æ„味ã—ã¾ã™ã€‚
+settings.public_access.docs.everyone_write=å…¨å“¡ã®æ›¸ãè¾¼ã¿: ã™ã¹ã¦ã®ãƒ­ã‚°ã‚¤ãƒ³ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«æ›¸ãè¾¼ã¿æ¨©é™ãŒã‚りã¾ã™ã€‚Wikiユニットã®ã¿ãŒã“ã®æ¨©é™ã‚’サãƒãƒ¼ãƒˆã—ã¾ã™ã€‚
settings.collaboration=å…±åŒä½œæ¥­è€…
settings.collaboration.admin=管ç†è€…
settings.collaboration.write=書ãè¾¼ã¿
@@ -2147,7 +2195,6 @@ settings.advanced_settings=拡張設定
settings.wiki_desc=Wikiを有効ã«ã™ã‚‹
settings.use_internal_wiki=ビルトインã®Wikiを使用ã™ã‚‹
settings.default_wiki_branch_name=デフォルトã®Wikiブランãƒå
-settings.default_wiki_everyone_access=サインインユーザーã®ãƒ‡ãƒ•ォルトã®ã‚¢ã‚¯ã‚»ã‚¹æ¨©é™:
settings.failed_to_change_default_wiki_branch=デフォルトã®Wikiブランãƒã‚’変更ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚
settings.use_external_wiki=外部ã®Wikiを使用ã™ã‚‹
settings.external_wiki_url=外部Wikiã®URL
@@ -2276,8 +2323,6 @@ settings.hooks_desc=Webhookã¯ã€æŒ‡å®šã—ãŸGiteaイベントãŒç™ºç”Ÿã—ãŸã¨
settings.webhook_deletion=Webhookã®å‰Šé™¤
settings.webhook_deletion_desc=Webhook設定ã¨é…信履歴ãŒå‰Šé™¤ã•れã¾ã™ã€‚ 続行ã—ã¾ã™ã‹ï¼Ÿ
settings.webhook_deletion_success=Webhookを削除ã—ã¾ã—ãŸã€‚
-settings.webhook.test_delivery=テストé…ä¿¡
-settings.webhook.test_delivery_desc=ダミーã®ã‚¤ãƒ™ãƒ³ãƒˆã§ã“ã®Webhookをテストã—ã¾ã™ã€‚
settings.webhook.test_delivery_desc_disabled=ã“ã®Webhookをダミーã®ã‚¤ãƒ™ãƒ³ãƒˆã§ãƒ†ã‚¹ãƒˆã™ã‚‹ã«ã¯ã€æœ‰åйã«ã—ã¦ãã ã•ã„。
settings.webhook.request=リクエスト
settings.webhook.response=レスãƒãƒ³ã‚¹
@@ -2315,6 +2360,8 @@ settings.event_fork=フォーク
settings.event_fork_desc=リãƒã‚¸ãƒˆãƒªãŒãƒ•ォークã•れãŸã¨ã。
settings.event_wiki=Wiki
settings.event_wiki_desc=WikiページãŒä½œæˆãƒ»åå‰å¤‰æ›´ãƒ»ç·¨é›†ãƒ»å‰Šé™¤ã•れãŸã¨ã。
+settings.event_statuses=ステータス
+settings.event_statuses_desc=APIã«ã‚ˆã£ã¦ã‚³ãƒŸãƒƒãƒˆã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãŒæ›´æ–°ã•れãŸã¨ã。
settings.event_release=リリース
settings.event_release_desc=リãƒã‚¸ãƒˆãƒªã§ãƒªãƒªãƒ¼ã‚¹ãŒä½œæˆãƒ»æ›´æ–°ãƒ»å‰Šé™¤ã•れãŸã¨ã。
settings.event_push=プッシュ
@@ -2324,7 +2371,6 @@ settings.event_repository=リãƒã‚¸ãƒˆãƒª
settings.event_repository_desc=リãƒã‚¸ãƒˆãƒªãŒä½œæˆãƒ»å‰Šé™¤ã•れãŸã¨ã。
settings.event_header_issue=イシューã®ã‚¤ãƒ™ãƒ³ãƒˆ
settings.event_issues=イシュー
-settings.event_issues_desc=イシューãŒã‚ªãƒ¼ãƒ—ン・クローズ・å†ã‚ªãƒ¼ãƒ—ン・編集ã•れãŸã¨ã。
settings.event_issue_assign=イシューã®ã‚¢ã‚µã‚¤ãƒ³
settings.event_issue_assign_desc=ã‚¤ã‚·ãƒ¥ãƒ¼ã®æ‹…当者ãŒå‰²ã‚Šå½“ã¦ã‚‰ã‚ŒãŸã¨ãã€è§£é™¤ã•れãŸã¨ã。
settings.event_issue_label=イシューã®ãƒ©ãƒ™ãƒ«
@@ -2335,7 +2381,6 @@ settings.event_issue_comment=イシューã¸ã®ã‚³ãƒ¡ãƒ³ãƒˆ
settings.event_issue_comment_desc=イシューã¸ã®ã‚³ãƒ¡ãƒ³ãƒˆãŒä½œæˆãƒ»ç·¨é›†ãƒ»å‰Šé™¤ã•れãŸã¨ã。
settings.event_header_pull_request=プルリクエストã®ã‚¤ãƒ™ãƒ³ãƒˆ
settings.event_pull_request=プルリクエスト
-settings.event_pull_request_desc=プルリクエストãŒã‚ªãƒ¼ãƒ—ン・クローズ・å†ã‚ªãƒ¼ãƒ—ン・編集ã•れãŸã¨ã。
settings.event_pull_request_assign=プルリクエストã®ã‚¢ã‚µã‚¤ãƒ³
settings.event_pull_request_assign_desc=ãƒ—ãƒ«ãƒªã‚¯ã‚¨ã‚¹ãƒˆã®æ‹…当者ãŒå‰²ã‚Šå½“ã¦ãƒ»è§£é™¤ã•れãŸã¨ã。
settings.event_pull_request_label=プルリクエストã®ãƒ©ãƒ™ãƒ«
@@ -2352,6 +2397,11 @@ settings.event_pull_request_review_request=プルリクエストã®ãƒ¬ãƒ“ューä
settings.event_pull_request_review_request_desc=プルリクエストã®ãƒ¬ãƒ“ューãŒä¾é ¼ã•れãŸã¨ãã€ã¾ãŸã¯ä¾é ¼ãŒå‰Šé™¤ã•れãŸã¨ã。
settings.event_pull_request_approvals=ãƒ—ãƒ«ãƒªã‚¯ã‚¨ã‚¹ãƒˆã®æ‰¿èª
settings.event_pull_request_merge=プルリクエストã®ãƒžãƒ¼ã‚¸
+settings.event_header_workflow=ワークフローイベント
+settings.event_workflow_run=ワークフロー実行
+settings.event_workflow_run_desc=Gitea Actions ã®ãƒ¯ãƒ¼ã‚¯ãƒ•ロー実行ãŒã€ã‚­ãƒ¥ãƒ¼ã«è¿½åŠ ã€å¾…機中ã€å®Ÿè¡Œä¸­ã€å®Œäº†ã«ãªã£ãŸã¨ã。
+settings.event_workflow_job=ワークフロージョブ
+settings.event_workflow_job_desc=Gitea Actions ã®ãƒ¯ãƒ¼ã‚¯ãƒ•ロージョブãŒã€ã‚­ãƒ¥ãƒ¼ã«è¿½åŠ ã€å¾…機中ã€å®Ÿè¡Œä¸­ã€å®Œäº†ã«ãªã£ãŸã¨ã。
settings.event_package=パッケージ
settings.event_package_desc=リãƒã‚¸ãƒˆãƒªã«ãƒ‘ッケージãŒä½œæˆã¾ãŸã¯å‰Šé™¤ã•れãŸã¨ã。
settings.branch_filter=ブランムフィルター
@@ -2566,7 +2616,6 @@ diff.commit=コミット
diff.git-notes=Notes
diff.data_not_available=差分ã¯ã‚りã¾ã›ã‚“
diff.options_button=差分オプション
-diff.show_diff_stats=統計情報を表示
diff.download_patch=Patchファイルをダウンロード
diff.download_diff=Diffファイルをダウンロード
diff.show_split_view=分割表示
@@ -2615,6 +2664,9 @@ diff.image.overlay=オーãƒãƒ¼ãƒ¬ã‚¤
diff.has_escaped=ã“ã®è¡Œã«ã¯ä¸å¯è¦–Unicode文字ãŒã‚りã¾ã™
diff.show_file_tree=ファイルツリーを表示
diff.hide_file_tree=ファイルツリーを隠ã™
+diff.submodule_added=サブモジュール %[1]s ㌠%[2]s ã§è¿½åŠ ã•れã¾ã—ãŸ
+diff.submodule_deleted=サブモジュール %[1]s ㌠%[2]s ã‹ã‚‰å‰Šé™¤ã•れã¾ã—ãŸ
+diff.submodule_updated=サブモジュール %[1]s ãŒæ›´æ–°ã•れã¾ã—ãŸ: %[2]s
releases.desc=プロジェクトãƒãƒ¼ã‚¸ãƒ§ãƒ³ã¨ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã®è¿½è·¡ã€‚
release.releases=リリース
@@ -2624,6 +2676,7 @@ release.new_release=æ–°ã—ã„リリース
release.draft=下書ã
release.prerelease=プレリリース
release.stable=安定版
+release.latest=最新
release.compare=比較
release.edit=編集
release.ahead.commits=<strong>%d</strong>ä»¶ã®ã‚³ãƒŸãƒƒãƒˆ
@@ -2684,6 +2737,7 @@ branch.restore_success=ブランム"%s" を復元ã—ã¾ã—ãŸã€‚
branch.restore_failed=ブランム"%s" ã®å¾©å…ƒã«å¤±æ•—ã—ã¾ã—ãŸã€‚
branch.protected_deletion_failed=ブランム"%s" ã¯ä¿è­·ã•れã¦ã„ã¾ã™ã€‚ 削除ã§ãã¾ã›ã‚“。
branch.default_deletion_failed=ブランム"%s" ã¯ãƒ‡ãƒ•ォルトブランãƒã§ã™ã€‚ 削除ã§ãã¾ã›ã‚“。
+branch.default_branch_not_exist=デフォルトブランム"%s" ãŒã‚りã¾ã›ã‚“。
branch.restore=ブランム"%s" ã®å¾©å…ƒ
branch.download=ブランム"%s" をダウンロード
branch.rename=ブランãƒå "%s" を変更
@@ -2698,6 +2752,8 @@ branch.create_branch_operation=ブランãƒã‚’作æˆ
branch.new_branch=æ–°ã—ã„ブランãƒã®ä½œæˆ
branch.new_branch_from=`"%s" ã‹ã‚‰æ–°ã—ã„ブランãƒã‚’作æˆ`
branch.renamed=ブランム%s 㯠%s ã«ãƒªãƒãƒ¼ãƒ ã•れã¾ã—ãŸã€‚
+branch.rename_default_or_protected_branch_error=デフォルトブランãƒã‚„ä¿è­·ãƒ–ランãƒã®ãƒªãƒãƒ¼ãƒ ãŒå¯èƒ½ãªã®ã¯ç®¡ç†è€…ã ã‘ã§ã™ã€‚
+branch.rename_protected_branch_failed=ã“ã®ãƒ–ランãƒã¯globベースã®ä¿è­·ãƒ«ãƒ¼ãƒ«ã«å¾“ã£ã¦ä¿è­·ã•れã¦ã„ã¾ã™ã€‚
tag.create_tag=ã‚¿ã‚° %s を作æˆ
tag.create_tag_operation=ã‚¿ã‚°ã®ä½œæˆ
@@ -2751,6 +2807,7 @@ team_permission_desc=権é™
team_unit_desc=リãƒã‚¸ãƒˆãƒªã®ã‚»ã‚¯ã‚·ãƒ§ãƒ³ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’許å¯
team_unit_disabled=(無効)
+form.name_been_taken=組織å "%s" ã¯æ—¢ã«ä½¿ç”¨ã•れã¦ã„ã¾ã™ã€‚
form.name_reserved=組織å "%s" ã¯äºˆç´„ã•れã¦ã„ã¾ã™ã€‚
form.name_pattern_not_allowed=`"%s" ã®å½¢å¼ã¯çµ„ç¹”åã«ä½¿ç”¨ã§ãã¾ã›ã‚“。`
form.create_org_not_allowed=組織を作æˆã™ã‚‹æ¨©é™ãŒã‚りã¾ã›ã‚“。
@@ -2772,15 +2829,28 @@ settings.visibility.private_shortname=プライベート
settings.update_settings=è¨­å®šã®æ›´æ–°
settings.update_setting_success=組織ã®è¨­å®šã‚’æ›´æ–°ã—ã¾ã—ãŸã€‚
-settings.change_orgname_prompt=注æ„: 組織åを変更ã™ã‚‹ã¨çµ„ç¹”ã®URLも変更ã•れã€å¤ã„åå‰ã¯è§£æ”¾ã•れã¾ã™ã€‚
-settings.change_orgname_redirect_prompt=å¤ã„åå‰ã¯ã€å†ä½¿ç”¨ã•れã¦ã„ãªã„é™ã‚Šãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã—ã¾ã™ã€‚
+
+settings.rename=組織åã®å¤‰æ›´
+settings.rename_desc=組織åを変更ã™ã‚‹ã¨çµ„ç¹”ã®URLも変更ã•れã€å¤ã„åå‰ã¯è§£æ”¾ã•れã¾ã™ã€‚
+settings.rename_success=組織 %[1]s ã® %[2]s ã¸ã®æ”¹åã«æˆåŠŸã—ã¾ã—ãŸã€‚
+settings.rename_no_change=組織åã®å¤‰æ›´ã¯ã‚りã¾ã›ã‚“。
+settings.rename_new_org_name=æ–°ã—ã„組織å
+settings.rename_failed=内部エラーã®ãŸã‚組織åを変更ã§ãã¾ã›ã‚“ã§ã—ãŸ
+settings.rename_notices_1=ã“ã®æ“作ã¯<strong>å…ƒã«æˆ»ã›ã¾ã›ã‚“</strong> 。
+settings.rename_notices_2=å¤ã„åå‰ã¯ã€å†ä½¿ç”¨ã•れるã¾ã§ã¯ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã—ã¾ã™ã€‚
+
settings.update_avatar_success=組織ã®ã‚¢ãƒã‚¿ãƒ¼ã‚’æ›´æ–°ã—ã¾ã—ãŸã€‚
settings.delete=組織を削除
settings.delete_account=ã“ã®çµ„織を削除
settings.delete_prompt=çµ„ç¹”ã¯æ’ä¹…çš„ã«å‰Šé™¤ã•れã¾ã™ã€‚ å…ƒã«æˆ»ã™ã“ã¨ã¯<strong>ã§ãã¾ã›ã‚“</strong>ï¼
+settings.name_confirm=確èªã®ãŸã‚組織åを入力:
+settings.delete_notices_1=ã“ã®æ“作ã¯<strong>å…ƒã«æˆ»ã›ã¾ã›ã‚“</strong> 。
+settings.delete_notices_2=ã“ã®æ“作ã«ã‚ˆã‚Šã€<strong>%s</strong>ã®ã™ã¹ã¦ã®<strong>リãƒã‚¸ãƒˆãƒª</strong>ãŒæ’ä¹…çš„ã«å‰Šé™¤ã•れã¾ã™ã€‚ コードã€ã‚¤ã‚·ãƒ¥ãƒ¼ã€ã‚³ãƒ¡ãƒ³ãƒˆã€Wikiデータã€å…±åŒä½œæ¥­è€…ã®è¨­å®šã‚‚å«ã¾ã‚Œã¾ã™ã€‚
+settings.delete_notices_3=ã“ã®æ“作ã«ã‚ˆã‚Šã€<strong>%s</strong>ã®ã™ã¹ã¦ã®<strong>パッケージ</strong>ãŒæ’ä¹…çš„ã«å‰Šé™¤ã•れã¾ã™ã€‚
+settings.delete_notices_4=ã“ã®æ“作ã«ã‚ˆã‚Šã€<strong>%s</strong>ã®ã™ã¹ã¦ã®<strong>プロジェクト</strong>ãŒæ’ä¹…çš„ã«å‰Šé™¤ã•れã¾ã™ã€‚
settings.confirm_delete_account=削除を確èª
-settings.delete_org_title=組織ã®å‰Šé™¤
-settings.delete_org_desc=組織をæ’ä¹…çš„ã«å‰Šé™¤ã—ã¾ã™ã€‚ 続行ã—ã¾ã™ã‹ï¼Ÿ
+settings.delete_failed=内部エラーã®ãŸã‚組織を削除ã§ãã¾ã›ã‚“ã§ã—ãŸ
+settings.delete_successful=組織ã®<b>%s</b>ã®å‰Šé™¤ã«æˆåŠŸã—ã¾ã—ãŸã€‚
settings.hooks_desc=ã“ã®çµ„ç¹”ã®<strong>ã™ã¹ã¦ã®ãƒªãƒã‚¸ãƒˆãƒª</strong>ã§ãƒˆãƒªã‚¬ãƒ¼ã•れるWebhookを追加ã—ã¾ã™ã€‚
settings.labels_desc=ã“ã®çµ„ç¹”ã®<strong>ã™ã¹ã¦ã®ãƒªãƒã‚¸ãƒˆãƒª</strong>ã§ä½¿ç”¨å¯èƒ½ãªã‚¤ã‚·ãƒ¥ãƒ¼ãƒ©ãƒ™ãƒ«ã‚’追加ã—ã¾ã™ã€‚
@@ -2852,6 +2922,18 @@ teams.invite.title=ã‚ãªãŸã¯çµ„ç¹” <strong>%[2]s</strong> 内ã®ãƒãƒ¼ãƒ  <st
teams.invite.by=%s ã‹ã‚‰ã®æ‹›å¾…
teams.invite.description=下ã®ãƒœã‚¿ãƒ³ã‚’クリックã—ã¦ãƒãƒ¼ãƒ ã«å‚加ã—ã¦ãã ã•ã„。
+view_as_role=表示: %s
+view_as_public_hint=READMEを公開ユーザーã¨ã—ã¦è¦‹ã¦ã„ã¾ã™ã€‚
+view_as_member_hint=READMEã‚’ã“ã®çµ„ç¹”ã®ãƒ¡ãƒ³ãƒãƒ¼ã¨ã—ã¦è¦‹ã¦ã„ã¾ã™ã€‚
+
+worktime=作業時間
+worktime.date_range_start=期間 (自)
+worktime.date_range_end=期間 (至)
+worktime.query=集計
+worktime.time=時間
+worktime.by_repositories=リãƒã‚¸ãƒˆãƒªåˆ¥
+worktime.by_milestones=マイルストーン別
+worktime.by_members=メンãƒãƒ¼åˆ¥
[admin]
maintenance=メンテナンス
@@ -3223,8 +3305,6 @@ config.ssh_domain=SSHサーãƒãƒ¼ã®ãƒ‰ãƒ¡ã‚¤ãƒ³
config.ssh_port=ãƒãƒ¼ãƒˆ
config.ssh_listen_port=å¾…å—ãƒãƒ¼ãƒˆ
config.ssh_root_path=ルートパス
-config.ssh_key_test_path=キーテストパス
-config.ssh_keygen_path=キージェãƒãƒ¬ãƒ¼ã‚¿('ssh-keygen')パス
config.ssh_minimum_key_size_check=最å°ã‚­ãƒ¼é•·ã®ãƒã‚§ãƒƒã‚¯
config.ssh_minimum_key_sizes=最å°ã‚­ãƒ¼é•·
@@ -3347,6 +3427,8 @@ monitor.previous=å‰å›ž
monitor.execute_times=実行回数
monitor.process=実行中ã®ãƒ—ロセス
monitor.stacktrace=スタックトレース
+monitor.trace=トレース
+monitor.performance_logs=パフォーマンスログ
monitor.processes_count=%d プロセス
monitor.download_diagnosis_report=診断レãƒãƒ¼ãƒˆã‚’ダウンロード
monitor.desc=説明
@@ -3355,7 +3437,6 @@ monitor.execute_time=実行時間
monitor.last_execution_result=çµæžœ
monitor.process.cancel=処ç†ã‚’キャンセル
monitor.process.cancel_desc=処ç†ã‚’キャンセルã™ã‚‹ã¨ãƒ‡ãƒ¼ã‚¿ãŒå¤±ã‚れるå¯èƒ½æ€§ãŒã‚りã¾ã™
-monitor.process.cancel_notices=キャンセル: <strong>%s</strong>?
monitor.process.children=å­ãƒ—ロセス
monitor.queues=キュー
@@ -3522,6 +3603,7 @@ versions=ãƒãƒ¼ã‚¸ãƒ§ãƒ³
versions.view_all=ã™ã¹ã¦è¡¨ç¤º
dependency.id=ID
dependency.version=ãƒãƒ¼ã‚¸ãƒ§ãƒ³
+search_in_external_registry=%s ã§æ¤œç´¢
alpine.registry=ã‚ãªãŸã® <code>/etc/apk/repositories</code> ファイルã«URLを追加ã—ã¦ã€ã“ã®ãƒ¬ã‚¸ã‚¹ãƒˆãƒªã‚’セットアップã—ã¾ã™:
alpine.registry.key=インデックス署åã®æ¤œè¨¼ã®ãŸã‚ã€ãƒ¬ã‚¸ã‚¹ãƒˆãƒªã®RSA公開éµã‚’ <code>/etc/apk/keys/</code> フォルダã«ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã—ã¾ã™:
alpine.registry.info=$branch 㨠$repository ã¯ä¸‹ã«ã‚るリストã‹ã‚‰é¸ã‚“ã§ãã ã•ã„。
@@ -3530,6 +3612,8 @@ alpine.repository=リãƒã‚¸ãƒˆãƒªæƒ…å ±
alpine.repository.branches=Branches
alpine.repository.repositories=Repositories
alpine.repository.architectures=Architectures
+arch.registry=<code>/etc/pacman.conf</code> ã«ãƒªãƒã‚¸ãƒˆãƒªã¨ã‚¢ãƒ¼ã‚­ãƒ†ã‚¯ãƒãƒ£ã‚’å«ã‚ã¦ã‚µãƒ¼ãƒãƒ¼ã‚’追加ã—ã¾ã™:
+arch.install=pacmanã§ãƒ‘ãƒƒã‚±ãƒ¼ã‚¸ã‚’åŒæœŸã—ã¾ã™:
arch.repository=リãƒã‚¸ãƒˆãƒªæƒ…å ±
arch.repository.repositories=リãƒã‚¸ãƒˆãƒª
arch.repository.architectures=Architectures
@@ -3549,7 +3633,8 @@ conda.install=Conda を使用ã—ã¦ãƒ‘ッケージをインストールã™ã‚‹ã«
container.details.type=イメージタイプ
container.details.platform=プラットフォーム
container.pull=コマンドラインã§ã‚¤ãƒ¡ãƒ¼ã‚¸ã‚’å–å¾—ã—ã¾ã™:
-container.digest=ダイジェスト:
+container.images=イメージ
+container.digest=ダイジェスト
container.multi_arch=OS / アーキテクãƒãƒ£
container.layers=イメージレイヤー
container.labels=ラベル
@@ -3652,11 +3737,18 @@ owner.settings.chef.keypair.description=Chefレジストリã®èªè¨¼ã«ã¯ã‚­ãƒ¼
secrets=シークレット
description=シークレットã¯ç‰¹å®šã®Actionsã«æ¸¡ã•れã¾ã™ã€‚ ãれ以外ã§èª­ã¿å‡ºã•れるã“ã¨ã¯ã‚りã¾ã›ã‚“。
none=シークレットã¯ã¾ã ã‚りã¾ã›ã‚“。
-creation=シークレットを追加
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=説明
creation.name_placeholder=å¤§æ–‡å­—å°æ–‡å­—ã®åŒºåˆ¥ãªã—ã€è‹±æ•°å­—ã¨ã‚¢ãƒ³ãƒ€ãƒ¼ã‚¹ã‚³ã‚¢ã®ã¿ã€GITEA_ ã‚„ GITHUB_ ã§å§‹ã¾ã‚‹ã‚‚ã®ã¯ä¸å¯
creation.value_placeholder=内容を入力ã—ã¦ãã ã•ã„。å‰å¾Œã®ç©ºç™½ã¯é™¤åŽ»ã•れã¾ã™ã€‚
-creation.success=シークレット "%s" を追加ã—ã¾ã—ãŸã€‚
-creation.failed=シークレットã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸã€‚
+creation.description_placeholder=ç°¡å˜ãªèª¬æ˜Žã‚’入力ã—ã¦ãã ã•ã„。 (オプション)
+
+save_success=シークレット "%s" ã‚’ä¿å­˜ã—ã¾ã—ãŸã€‚
+save_failed=シークレットã®ä¿å­˜ã«å¤±æ•—ã—ã¾ã—ãŸã€‚
+
+add_secret=シークレットを追加
+edit_secret=シークレットを編集
deletion=シークレットã®å‰Šé™¤
deletion.description=シークレットã®å‰Šé™¤ã¯æ’ä¹…çš„ã§å…ƒã«æˆ»ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“。 続行ã—ã¾ã™ã‹ï¼Ÿ
deletion.success=シークレットを削除ã—ã¾ã—ãŸã€‚
@@ -3712,6 +3804,7 @@ runners.status.active=稼åƒä¸­
runners.status.offline=オフライン
runners.version=ãƒãƒ¼ã‚¸ãƒ§ãƒ³
runners.reset_registration_token=登録トークンをリセット
+runners.reset_registration_token_confirm=ç¾åœ¨ã®ãƒˆãƒ¼ã‚¯ãƒ³ã‚’無効ã«ã—ã¦ã€æ–°ã—ã„トークンを生æˆã—ã¾ã™ã‹ï¼Ÿ
runners.reset_registration_token_success=ランナー登録トークンをリセットã—ã¾ã—ãŸ
runs.all_workflows=ã™ã¹ã¦ã®ãƒ¯ãƒ¼ã‚¯ãƒ•ロー
@@ -3733,6 +3826,10 @@ runs.no_workflows.documentation=Gitea Actions ã®è©³ç´°ã«ã¤ã„ã¦ã¯ã€<a targ
runs.no_runs=ワークフローã¯ã¾ã å®Ÿè¡Œã•れã¦ã„ã¾ã›ã‚“。
runs.empty_commit_message=(空ã®ã‚³ãƒŸãƒƒãƒˆãƒ¡ãƒƒã‚»ãƒ¼ã‚¸)
runs.expire_log_message=ログã¯å¤ã™ãŽã‚‹ãŸã‚消去ã•れã¦ã„ã¾ã™ã€‚
+runs.delete=ワークフローã®å®Ÿè¡Œã‚’削除
+runs.delete.description=ã“ã®ãƒ¯ãƒ¼ã‚¯ãƒ•ローを完全ã«å‰Šé™¤ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿã“ã®æ“作ã¯å…ƒã«æˆ»ã›ã¾ã›ã‚“。
+runs.not_done=ã“ã®ãƒ¯ãƒ¼ã‚¯ãƒ•ローã®å®Ÿè¡Œã¯å®Œäº†ã—ã¦ã„ã¾ã›ã‚“。
+runs.view_workflow_file=ワークフローファイルを表示
workflow.disable=ワークフローを無効ã«ã™ã‚‹
workflow.disable_success=ワークフロー '%s' ãŒç„¡åйã«ãªã‚Šã¾ã—ãŸã€‚
@@ -3744,6 +3841,7 @@ workflow.not_found=ワークフロー '%s' ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。
workflow.run_success=ワークフロー '%s' ã¯æ­£å¸¸ã«å®Ÿè¡Œã•れã¾ã—ãŸã€‚
workflow.from_ref=使用ã™ã‚‹ãƒ¯ãƒ¼ã‚¯ãƒ•ローã®å–å¾—å…ƒ
workflow.has_workflow_dispatch=ã“ã®ãƒ¯ãƒ¼ã‚¯ãƒ•ローã«ã¯ workflow_dispatch イベントトリガーãŒã‚りã¾ã™ã€‚
+workflow.has_no_workflow_dispatch=ワークフロー '%s' ã«ã¯ workflow_dispatch イベントトリガーãŒã‚りã¾ã›ã‚“。
need_approval_desc=フォークプルリクエストã®ãƒ¯ãƒ¼ã‚¯ãƒ•ローを実行ã™ã‚‹ã«ã¯æ‰¿èªãŒå¿…è¦ã§ã™ã€‚
@@ -3763,12 +3861,16 @@ variables.creation.success=変数 "%s" を追加ã—ã¾ã—ãŸã€‚
variables.update.failed=変数を更新ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚
variables.update.success=変数を更新ã—ã¾ã—ãŸã€‚
+logs.always_auto_scroll=常ã«ãƒ­ã‚°ã‚’自動スクロール
+logs.always_expand_running=常ã«å®Ÿè¡Œä¸­ã®ãƒ­ã‚°ã‚’展開
[projects]
deleted.display_name=削除ã•れãŸãƒ—ロジェクト
type-1.display_name=個人プロジェクト
type-2.display_name=リãƒã‚¸ãƒˆãƒª プロジェクト
type-3.display_name=組織プロジェクト
+enter_fullscreen=フルスクリーン
+exit_fullscreen=フルスクリーンを終了
[git.filemode]
changed_filemode=%[1]s → %[2]s
diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini
index 4ce66282b2..4d6156e93f 100644
--- a/options/locale/locale_ko-KR.ini
+++ b/options/locale/locale_ko-KR.ini
@@ -111,7 +111,7 @@ license=오픈 소스
[install]
install=설치
title=초기 설정
-docker_helper="Gitea를 Dockerì—서 실행하려면 설정 ì „ì— ì´ <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">문서</a>를 ì½ì–´ë³´ì„¸ìš”."
+docker_helper=Gitea를 Dockerì—서 실행하려면 설정 ì „ì— ì´ <a target="_blank" rel="noopener noreferrer" href="%s">문서</a>를 ì½ì–´ë³´ì„¸ìš”.
db_title=ë°ì´í„°ë² ì´ìФ 설정
db_type=ë°ì´í„°ë² ì´ìФ 유형
host=호스트
@@ -212,6 +212,7 @@ show_private=비공개
issues.in_your_repos=ë‹¹ì‹ ì˜ ì €ìž¥ì†Œì—
+
[explore]
repos=저장소
users=유저
@@ -436,8 +437,8 @@ manage_gpg_keys=GPG 키 관리
add_key=키 추가
ssh_desc=ì´ëŸ¬í•œ SSH 공용 키는 ê·€í•˜ì˜ ê³„ì •ê³¼ ì—°ê²°ë˜ì–´ 있습니다. 해당 ê°œì¸ í‚¤ëŠ” ë‹¹ì‹ ì˜ ì €ìž¥ì†Œì— ëŒ€í•œ ì „ì²´ 액세스를 가능하게 합니다.
gpg_desc=ì´ëŸ¬í•œ GPG 공개키는 ë‹¹ì‹ ì˜ ê³„ì •ê³¼ ì—°ê²°ë˜ì–´ìžˆìŠµë‹ˆë‹¤. ì»¤ë°‹ì´ ê²€ì¦ë  수 있ë„ë¡ ë‹¹ì‹ ì˜ ê°œì¸ í‚¤ë¥¼ 안전하게 유지하십시오.
-ssh_helper="<strong>ë„ì›€ì´ í•„ìš”í•˜ì„¸ìš”?</strong> GitHubì˜ ì„¤ëª…ì„œë¥¼ 참조하시기 ë°”ëžë‹ˆë‹¤: <a href=\"%s\">SSH 키 ìƒì„±í•˜ê¸°</a> ë˜ëŠ” SSH를 사용할 때 <a href=\"%s\">ì¼ë°˜ì ì¸ 문제</a>"
-gpg_helper="<strong>ë„ì›€ì´ í•„ìš”í•˜ì„¸ìš”?</strong> GitHubì˜ ì„¤ëª…ì„œë¥¼ 참조하시기 ë°”ëžë‹ˆë‹¤: <a href=\"%s\">GPGí‚¤ì— ëŒ€í•˜ì—¬</a>."
+ssh_helper=<strong>ë„ì›€ì´ í•„ìš”í•˜ì„¸ìš”?</strong> GitHubì˜ ì„¤ëª…ì„œë¥¼ 참조하시기 ë°”ëžë‹ˆë‹¤: <a href="%s">SSH 키 ìƒì„±í•˜ê¸°</a> ë˜ëŠ” SSH를 사용할 때 <a href="%s">ì¼ë°˜ì ì¸ 문제</a>
+gpg_helper=<strong>ë„ì›€ì´ í•„ìš”í•˜ì„¸ìš”?</strong> GitHubì˜ ì„¤ëª…ì„œë¥¼ 참조하시기 ë°”ëžë‹ˆë‹¤: <a href="%s">GPGí‚¤ì— ëŒ€í•˜ì—¬</a>.
add_new_key=SSH 키 추가
add_new_gpg_key=GPG 키 추가
gpg_key_id_used=ê°™ì€ IDì˜ GPG 공개키가 ì´ë¯¸ 존재합니다.
@@ -538,7 +539,7 @@ template_helper=템플릿으로 저장소 만들기
visibility=가시성
visibility_helper_forced=사ì´íЏ 관리ìžê°€ 새 ë ˆí¬ì§€í† ë¦¬ì— 대해 비공개로만 ìƒì„±ë˜ë„ë¡ í•˜ì˜€ìŠµë‹ˆë‹¤.
visibility_fork_helper=(ë³€ê²½ì‚¬í•­ì„ ì ìš©í•˜ëŠ” 경우 모든 í¬í¬ê°€ ì˜í–¥ì„ 받게 ë©ë‹ˆë‹¤.)
-clone_helper="í´ë¡ í•˜ëŠ”ë°ì— ë„ì›€ì´ í•„ìš”í•˜ë©´ <a target=\"_blank\" href=\"%s\">Help</a>ì— ë°©ë¬¸í•˜ì„¸ìš”."
+clone_helper=í´ë¡ í•˜ëŠ”ë°ì— ë„ì›€ì´ í•„ìš”í•˜ë©´ <a target="_blank" href="%s">Help</a>ì— ë°©ë¬¸í•˜ì„¸ìš”.
fork_repo=저장소 í¬í¬
fork_from=ì›ë³¸ 프로ì íЏ :
fork_visibility_helper=í¬í¬ëœ ì €ìž¥ì†Œì˜ ê°€ì‹œì„±ì€ ë³€ê²½í•˜ì‹¤ 수 없습니다.
@@ -625,7 +626,6 @@ file_too_large=보여주기ì—는 파ì¼ì´ 너무 í½ë‹ˆë‹¤.
video_not_supported_in_browser=ë‹¹ì‹ ì˜ ë¸Œë¼ìš°ì €ê°€ HTML5 'video' 태그를 ì§€ì›í•˜ì§€ 않습니다.
audio_not_supported_in_browser=ë‹¹ì‹ ì˜ ë¸Œë¼ìš°ì €ê°€ HTML5 'audio' 태그를 ì§€ì›í•˜ì§€ 않습니다.
-stored_lfs=Git LFSì— ì €ìž¥ë˜ì–´ 있습니다
commit_graph=커밋 그래프
editor.new_file=새 파ì¼
@@ -640,7 +640,7 @@ editor.or=혹ì€
editor.cancel_lower=취소
editor.commit_changes=변경 ë‚´ìš©ì„ ì»¤ë°‹
editor.commit_message_desc=ì„ íƒì  확장 ì„¤ëª…ì„ ì¶”ê°€...
-editor.commit_directly_to_this_branch="<strong class=\"branch-name\">%s</strong> 브랜치ì—서 ì§ì ‘ 커밋해주세요."
+editor.commit_directly_to_this_branch=<strong class="branch-name">%s</strong> 브랜치ì—서 ì§ì ‘ 커밋해주세요.
editor.create_new_branch=ì´ ì»¤ë°‹ì— ëŒ€í•œ <strong>새로운 브랜치</strong>를 만들고 ëŒì–´ì˜¤ê¸° ìš”ì²­ì„ ì‹œìž‘í•©ë‹ˆë‹¤.
editor.new_branch_name_desc=새로운 브랜치 명...
editor.cancel=취소
@@ -648,6 +648,7 @@ editor.filename_cannot_be_empty=파ì¼ëª…ì´ ë¹ˆì¹¸ìž…ë‹ˆë‹¤.
editor.no_changes_to_show=표시할 ë³€ê²½ì‚¬í•­ì´ ì—†ìŠµë‹ˆë‹¤.
editor.add_subdir=경로 추가...
+
commits.desc=소스 코드 변경 ë‚´ì—­ íƒìƒ‰
commits.commits=커밋
commits.search_all=모든 브랜치
@@ -667,7 +668,7 @@ ext_issues.desc=외부 ì´ìŠˆ 트래커 ì—°ê²°.
projects.description_placeholder=설명
projects.title=제목
projects.new=새 프로ì íЏ
-projects.template.desc="템플릿"
+projects.template.desc=템플릿
projects.column.edit_title=ì´ë¦„
projects.column.new_title=ì´ë¦„
@@ -706,8 +707,6 @@ issues.filter_label=ë ˆì´ë¸”
issues.filter_label_no_select=모든 ë ˆì´ë¸”
issues.filter_milestone=마ì¼ìŠ¤í†¤
issues.filter_assignee=담당ìž
-issues.filter_assginee_no_select=모든 담당ìž
-issues.filter_assginee_no_assignee=ë‹´ë‹¹ìž ì—†ìŒ
issues.filter_type=유형
issues.filter_type.all_issues=모든 ì´ìŠˆ
issues.filter_type.assigned_to_you=나ì—게 할당ë¨
@@ -731,7 +730,7 @@ issues.action_milestone=마ì¼ìŠ¤í†¤
issues.action_milestone_no_select=마ì¼ìŠ¤í†¤ ì—†ìŒ
issues.action_assignee=담당ìž
issues.action_assignee_no_select=ë‹´ë‹¹ìž ì—†ìŒ
-issues.opened_by="<a href=\"%[2]s\"> %[3]s</a>ê°€ %[1]sì„ ì˜¤í”ˆ"
+issues.opened_by=<a href="%[2]s"> %[3]s</a>ê°€ %[1]sì„ ì˜¤í”ˆ
issues.previous=ì´ì „
issues.next=다ìŒ
issues.open_title=오픈
@@ -747,7 +746,7 @@ issues.create_comment=코멘트
issues.commit_ref_at=` 커밋 <a id="%[1]s" href="#%[1]s">%[2]s</a>ì—서 ì´ ì´ìŠˆ 언급`
issues.role.owner=소유ìž
issues.role.member=멤버
-issues.sign_in_require_desc="<a href=\"%s\">로그ì¸</a>하여 ì´ ëŒ€í™”ì— ì°¸ì—¬"
+issues.sign_in_require_desc=<a href="%s">로그ì¸</a>하여 ì´ ëŒ€í™”ì— ì°¸ì—¬
issues.edit=수정
issues.cancel=취소
issues.save=저장
@@ -780,9 +779,9 @@ issues.time_spent_total=ì´ ê²½ê³¼ëœ ì‹œê°„
issues.time_spent_from_all_authors=`ì´ ê²½ê³¼ëœ ì‹œê°„: %s`
issues.due_date=마ê°ì¼
-issues.invalid_due_date_format="마ê°ì¼ì€ 반드시 'yyyy-mm-dd' 형ì‹ì´ì–´ì•¼ 합니다."
-issues.error_modifying_due_date="마ê°ì¼ ìˆ˜ì •ì„ ì‹¤íŒ¨í•˜ì˜€ìŠµë‹ˆë‹¤."
-issues.error_removing_due_date="마ê°ì¼ 삭제를 실패하였습니다."
+issues.invalid_due_date_format=마ê°ì¼ì€ 반드시 'yyyy-mm-dd' 형ì‹ì´ì–´ì•¼ 합니다.
+issues.error_modifying_due_date=마ê°ì¼ ìˆ˜ì •ì„ ì‹¤íŒ¨í•˜ì˜€ìŠµë‹ˆë‹¤.
+issues.error_removing_due_date=마ê°ì¼ 삭제를 실패하였습니다.
issues.due_date_form=yyyy-mm-dd
issues.due_date_form_add=마ê°ì¼ 추가
issues.due_date_form_edit=편집
@@ -790,8 +789,8 @@ issues.due_date_form_remove=삭제
issues.due_date_not_set=마ê°ì¼ì´ 설정ë˜ì§€ 않았습니다.
issues.due_date_added=마ê°ì¼ %s 를 추가 %s
issues.due_date_remove=%s %s 마ê°ì¼ì´ ì‚­ì œë˜ì—ˆìŠµë‹ˆë‹¤.
-issues.due_date_overdue="기한 초과"
-issues.due_date_invalid="ê¸°í•œì´ ì˜¬ë°”ë¥´ì§€ 않거나 범위를 벗어났습니다. 'yyyy-mm-dd'형ì‹ì„ 사용해주십시오."
+issues.due_date_overdue=기한 초과
+issues.due_date_invalid=ê¸°í•œì´ ì˜¬ë°”ë¥´ì§€ 않거나 범위를 벗어났습니다. 'yyyy-mm-dd'형ì‹ì„ 사용해주십시오.
issues.dependency.title=ì˜ì¡´ì„±
issues.dependency.add=ì˜ì¡´ì„± 추가...
issues.dependency.cancel=취소
@@ -809,7 +808,7 @@ issues.dependency.add_error_dep_exists=ì˜ì¡´ì„±ì´ ì´ë¯¸ 존재합니다.
issues.dependency.add_error_dep_not_same_repo=ë‘ ì´ìŠˆëŠ” ê°™ì€ ë ˆí¬ì§€í† ë¦¬ ì•ˆì— ìžˆì–´ì•¼ 합니다.
issues.review.self.approval=ìžì‹ ì˜ í’€ 리퀘스트를 승ì¸í•  수 없습니다.
issues.review.self.rejection=ìžì‹ ì˜ í’€ ë¦¬í€˜ìŠ¤íŠ¸ì— ëŒ€í•œ ë³€ê²½ì„ ìš”ì²­í•  수 없습니다.
-issues.review.approve="ì´ ë³€ê²½ì‚¬í•­ì„ ìŠ¹ì¸í•˜ì˜€ìŠµë‹ˆë‹¤. %s"
+issues.review.approve=ì´ ë³€ê²½ì‚¬í•­ì„ ìŠ¹ì¸í•˜ì˜€ìŠµë‹ˆë‹¤. %s
issues.review.pending=보류
issues.review.review=검토
issues.review.reviewers=리뷰어
@@ -824,7 +823,7 @@ pulls.compare_base=병합하기
pulls.compare_compare=다ìŒìœ¼ë¡œë¶€í„° í’€
pulls.filter_branch=Filter Branch
pulls.create=í’€ 리퀘스트 ìƒì„±
-pulls.title_desc="<code>%[2]s</code> ì—서 <code id=\"branch_target\">%[3]s</code> 로 %[1]d commits 를 머지하려 합니다"
+pulls.title_desc=<code>%[2]s</code> ì—서 <code id="branch_target">%[3]s</code> 로 %[1]d commits 를 머지하려 합니다
pulls.merged_title_desc=<code>%[2]s</code> ì—서 <code>%[3]s</code> 로 %[1]d commits 를 머지했습니다 %[4]s
pulls.tab_conversation=대화
pulls.tab_commits=커밋
@@ -855,7 +854,7 @@ milestones.title=타ì´í‹€
milestones.desc=설명
milestones.due_date=기한 (ì„ íƒ ì‚¬í•­)
milestones.clear=지우기
-milestones.invalid_due_date_format="마ê°ì¼ì€ 반드시 'yyyy-mm-dd' 형ì‹ì´ì–´ì•¼ 합니다."
+milestones.invalid_due_date_format=마ê°ì¼ì€ 반드시 'yyyy-mm-dd' 형ì‹ì´ì–´ì•¼ 합니다.
milestones.edit=마ì¼ìŠ¤í†¤ 편집
milestones.cancel=취소
milestones.modify=마ì¼ìŠ¤í†¤ 갱신
@@ -994,8 +993,6 @@ settings.teams=팀
settings.add_webhook=Webhook 추가
settings.webhook_deletion=Webhook 삭제
settings.webhook_deletion_success=Webhookì„ ì‚­ì œí–ˆìŠµë‹ˆë‹¤.
-settings.webhook.test_delivery=전달 시험
-settings.webhook.test_delivery_desc=ì´ ì›¹í›…ì„ ê°€ìƒ ì´ë²¤íŠ¸ë¡œ 테스트
settings.webhook.request=요청
settings.webhook.response=ì‘답
settings.webhook.headers=제목
@@ -1153,12 +1150,12 @@ settings.visibility.private_shortname=비공개
settings.update_settings=설정 ì—…ë°ì´íЏ
settings.update_setting_success=ì¡°ì§ ì„¤ì •ì´ ë³€ê²½ë˜ì—ˆìŠµë‹ˆë‹¤.
+
+
settings.update_avatar_success=ì¡°ì§ì˜ 아바타가 갱신ë˜ì—ˆìŠµë‹ˆë‹¤.
settings.delete=ì¡°ì§ ì‚­ì œ
settings.delete_account=ì´ ì¡°ì§ì„ 삭제합니다.
settings.confirm_delete_account=ì‚­ì œ 승ì¸
-settings.delete_org_title=ì¡°ì§ ì‚­ì œ
-settings.delete_org_desc=ì´ ì¡°ì§ì´ ì˜êµ¬ížˆ ì‚­ì œë©ë‹ˆë‹¤. ê³„ì† í•˜ì‹œê² ìŠµë‹ˆê¹Œ?
members.membership_visibility=íšŒì› í‘œì‹œ:
@@ -1191,6 +1188,7 @@ teams.add_duplicate_users=사용ìžê°€ ì´ë¯¸ 팀 멤버입니다.
teams.members.none=ì´ íŒ€ì— ë©¤ë²„ê°€ 없습니다.
+
[admin]
dashboard=대시보드
users=ì‚¬ìš©ìž ê³„ì •
@@ -1372,8 +1370,6 @@ config.ssh_start_builtin_server=빌트-ì¸ ì„œë²„ 사용
config.ssh_port=í¬íЏ
config.ssh_listen_port=수신 대기 í¬íЏ
config.ssh_root_path=최ìƒìœ„ 경로
-config.ssh_key_test_path=주 테스트 경로
-config.ssh_keygen_path=키 ìƒì„± ('ssh-keygen') 경로
config.ssh_minimum_key_size_check=최소 키 사ì´ì¦ˆ 검사
config.ssh_minimum_key_sizes=최소 키 사ì´ì¦ˆ
@@ -1546,6 +1542,11 @@ owner.settings.cleanuprules.enabled=활성화ë¨
[secrets]
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=설명
+
+
+
[actions]
diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini
index 0dfc5683ec..2e430ff399 100644
--- a/options/locale/locale_lv-LV.ini
+++ b/options/locale/locale_lv-LV.ini
@@ -54,6 +54,7 @@ webauthn_reload=PÄrlÄdÄ“t
repository=Repozitorijs
organization=OrganizÄcija
mirror=Spogulis
+issue_milestone=Atskaites punktus
new_repo=Jauns repozitorijs
new_migrate=Jauna migrÄcija
new_mirror=Jauns spogulis
@@ -337,6 +338,7 @@ show_only_public=Attēlot tikai publiskos
issues.in_your_repos=Jūsu repozitorijos
+
[explore]
repos=Repozitoriji
users=LietotÄji
@@ -391,7 +393,6 @@ oauth_signup_submit=Pabeigt reÄ£istrÄciju
oauth_signin_tab=Sasaistīt ar esošu kontu
oauth_signin_title=Pieteikties, lai autorizētu saistīto kontu
oauth_signin_submit=Sasaistīt kontu
-oauth.signin.error=RadÄs kļūda apstrÄdÄjot pieteikÅ¡anÄs pieprasÄ«jumu. Ja šī kļūda atkÄrtojas, sazinieties ar lapas administratoru.
oauth.signin.error.access_denied=AutorizÄcijas pieprasÄ«jums tika noraidÄ«ts.
oauth.signin.error.temporarily_unavailable=PieteikÅ¡anÄs neizdevÄs, jo autentifikÄcijas serveris ir Ä«slaicÄ«gi nepieejams. Mēģiniet autorizÄ“ties vÄ“lÄk.
openid_connect_submit=Pievienoties
@@ -812,7 +813,6 @@ delete_token_success=Pilnvara tika izdzÄ“sta. LietojumprogrammÄm, kas izmantoja
repo_and_org_access=Repozitorija un organizÄcijas piekļuve
permissions_public_only=Tikai publiskie
permissions_access_all=Visi (publiskie, privÄtie un ierobežotie)
-select_permissions=NorÄdiet tiesÄ«bas
permission_no_access=Nav piekļuves
permission_read=SkatīšanÄs
permission_write=SkatīšanÄs un raksīšanas
@@ -997,9 +997,6 @@ blame_prior=AplÅ«kot vainÄ«go par izmaiņÄm pirms šīs revÄ«zijas
blame.ignore_revs=Neņem vÄ“rÄ izmaiņas no <a href="%s">.git-blame-ignore-revs</a>. Nospiediet <a href="%s">Å¡eit, lai to apietu</a> un redzÄ“tu visu izmaiņu skatu.
blame.ignore_revs.failed=NeizdevÄs neņemt vÄ“rÄ izmaiņas no <a href="%s">.git-blam-ignore-revs</a>.
-tree_path_not_found_commit=RevÄ«zijÄ %[2]s neeksistÄ“ ceļš %[1]s
-tree_path_not_found_branch=AtzarÄ %[2]s nepastÄv ceļš %[1]s
-tree_path_not_found_tag=TagÄ %[2]s nepastÄv ceļš %[1]s
transfer.accept=ApstiprinÄt Ä«paÅ¡nieka maiņu
transfer.reject=Noraidīt īpašnieka maiņu
@@ -1128,6 +1125,7 @@ labels=Iezīmes
org_labels_desc=OrganizÄcijas lÄ«meņa iezÄ«mes var tikt izmantotas <strong>visiem repozitorijiem</strong> Å¡ajÄ organizÄcijÄ
org_labels_desc_manage=pÄrvaldÄ«t
+milestone=Atskaites punktus
milestones=Atskaites punkti
commits=Revīzijas
commit=Revīzija
@@ -1156,7 +1154,6 @@ file_copy_permalink=Kopēt saiti
view_git_blame=Aplūkot Git vainīgos
video_not_supported_in_browser=JÅ«su pÄrlÅ«ks neatbalsta HTML5 video.
audio_not_supported_in_browser=JÅ«su pÄrlÅ«ks neatbalsta HTML5 audio.
-stored_lfs=SaglabÄts Git LFS
symbolic_link=Simboliska saite
executable_file=IzpildÄmais fails
commit_graph=Revīziju grafs
@@ -1199,7 +1196,6 @@ editor.update=Atjaunot %s
editor.delete=Dzēst %s
editor.patch=Pielietot ielÄpu
editor.patching=Pielieto ielÄpu:
-editor.fail_to_apply_patch=`NeizdevÄs pielietot ielÄpu "%s"`
editor.new_patch=Jauns ielÄps
editor.commit_message_desc=Pievienot neobligÄtu paplaÅ¡inÄtu aprakstu…
editor.signoff_desc=Pievienot revÄ«zijas žurnÄla ziņojuma beigÄs Signed-off-by ar revÄ«zijas autoru.
@@ -1217,15 +1213,11 @@ editor.branch_already_exists=Atzars "%s" Å¡ajÄ repozitorijÄ jau eksistÄ“.
editor.directory_is_a_file=Direktorijas nosaukums "%s" vecÄka ceÄ¼Ä ir fails nevis direktorija Å¡ajÄ repozitorijÄ.
editor.file_is_a_symlink=Fails "%s" ir norÄde, kuru nav iespÄ“jams labot no tÄ«mekļa redaktora
editor.filename_is_a_directory=Faila nosaukums "%s" sakrÄ«t ar direktorijas nosaukumu Å¡ajÄ repozitorijÄ.
-editor.file_editing_no_longer_exists=Fails "%s", ko labojat, vairs neeksistÄ“ Å¡ajÄ repozitorijÄ.
-editor.file_deleting_no_longer_exists=Fails "%s", ko dzēšat, vairs neeksistÄ“ Å¡ajÄ repozitorijÄ.
editor.file_changed_while_editing=Faila saturs ir mainÄ«jies kopÅ¡ sÄkÄt to labot. Noklikšķiniet <a target="_blank" rel="noopener noreferrer" href="%s">Å¡eit</a>, lai apskatÄ«tu, vai <strong>NosÅ«tiet izmaiņas atkÄrtoti</strong>, lai pÄrrakstÄ«tu.
editor.file_already_exists=Fails ar nosaukumu "%s" Å¡ajÄ repozitorijÄ jau eksistÄ“.
editor.commit_empty_file_header=Iesūtīt tukšu failu
editor.commit_empty_file_text=Fails, ko vÄ“laties iesÅ«tÄ«t, ir tukÅ¡s. Vai turpinÄt?
editor.no_changes_to_show=Nav izmaiņu, ko rÄdÄ«t.
-editor.fail_to_update_file=NeizdevÄs atjaunot/izveidot failu "%s".
-editor.fail_to_update_file_summary=Kļūdas ziņojums:
editor.push_rejected_no_message=Izmaiņu iesÅ«tīšana tika noraidÄ«ta, bet serveris neatgrieza paziņojumu. PÄrbaudiet git ÄÄ·us Å¡im repozitorijam.
editor.push_rejected=Serveris noraidÄ«ja Å¡o izmaiņu. PÄrbaudiet git ÄÄ·us.
editor.push_rejected_summary=Pilns noraidīšanas ziņojums:
@@ -1240,6 +1232,7 @@ editor.require_signed_commit=AtzarÄ var iesÅ«tÄ«t tikai parakstÄ«tas revÄ«zijas
editor.cherry_pick=Izlasīt %s uz:
editor.revert=Atgriezt %s uz:
+
commits.desc=PÄrlÅ«kot pirmkoda izmaiņu vÄ“sturi.
commits.commits=Revīzijas
commits.no_commits=Nav kopÄ«gu revÄ«ziju. Atzariem "%s" un "%s" ir pilnÄ«bÄ atšķirÄ«ga izmaiņu vÄ“sture.
@@ -1385,8 +1378,6 @@ issues.filter_project=Projekts
issues.filter_project_all=Visi projekti
issues.filter_project_none=Nav projekta
issues.filter_assignee=Atbildīgais
-issues.filter_assginee_no_select=Visi atbildīgie
-issues.filter_assginee_no_assignee=Nav atbildÄ«gÄ
issues.filter_poster=Autors
issues.filter_type=Veids
issues.filter_type.all_issues=Visas problēmas
@@ -1501,13 +1492,12 @@ issues.attachment.open_tab=`Noklikšķiniet, lai apskatÄ«tos "%s" jaunÄ logÄ`
issues.attachment.download=`Noklikšķiniet, lai lejupielÄdÄ“tu "%s"`
issues.subscribe=Abonēt
issues.unsubscribe=Atrakstīties
-issues.unpin_issue=Atspraust problēmu
+issues.unpin=Atspraust
issues.max_pinned=Nevar piespraust vairÄk problÄ“mas
issues.pin_comment=piesprauda šo %s
issues.unpin_comment=atsprauda šo %s
issues.lock=Slēgt komentēšanu
issues.unlock=Atļaut komentēšanu
-issues.lock.unknown_reason=NeizdevÄs slÄ“gt problÄ“mas komentēšanu.
issues.lock_duplicate=ProblÄ“mas komentēšanu nevar slÄ“gt vairÄkas reizes.
issues.unlock_error=Nevar atļaut komentēšanu, ja problÄ“mai tÄ nav slÄ“gta.
issues.lock_with_reason=slÄ“dza ar iemeslu <strong>%s</strong> un ierobežoja komentÄru pievienoÅ¡anu tikai lÄ«dzstrÄdniekiem %s
@@ -1682,7 +1672,6 @@ pulls.add_prefix=Pievienot <strong>%s</strong> prefiksu
pulls.remove_prefix=Noņemt <strong>%s</strong> prefiksu
pulls.data_broken=Izmaiņu pieprasÄ«jums ir bojÄts, jo dzÄ“sta informÄcija no atdalÄ«tÄ repozitorija.
pulls.files_conflicted=Šīs izmaiņu pieprasījuma izmaiņas konfliktē ar mērķa atzaru.
-pulls.is_checking=Notiek konfliktu pÄrbaude, mirkli uzgaidiet un atjaunojiet lapu.
pulls.is_ancestor=Atzars jau ir pilnÄ«bÄ iekļauts mÄ“rÄ·Ä atzarÄ. Nav izmaiņu, ko sapludinÄt.
pulls.is_empty=Mērķa atzars jau satur šī atzara izmaiņas. Šī revīzija būs tukša.
pulls.required_status_check_failed=Dažas no pÄrbaudÄ“m nebija veiksmÄ«gas.
@@ -1747,7 +1736,6 @@ pulls.outdated_with_base_branch=Atzars ir novecojis salÄ«dzinot ar bÄzes atzaru
pulls.close=Aizvērt izmaiņu pieprasījumu
pulls.closed_at=`aizvēra šo izmaiņu pieprasījumu <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at=`atkÄrtoti atvÄ“ra Å¡o izmaiņu pieprasÄ«jumu <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-pulls.cmd_instruction_hint=`ApskatÄ«t <a class="show-instruction">komandrindas izmantoÅ¡anas norÄdes</a>.`
pulls.cmd_instruction_checkout_title=Paņemt
pulls.cmd_instruction_checkout_desc=Projekta repozitorijÄ jÄizveido jauns atzars un jÄpÄrbauda izmaiņas.
pulls.cmd_instruction_merge_title=SapludinÄt
@@ -2084,8 +2072,6 @@ settings.hooks_desc=TÄ«mekļa ÄÄ·i ļauj paziņot ÄrÄ“jiem servisiem par notei
settings.webhook_deletion=Noņemt tÄ«mekļa ÄÄ·i
settings.webhook_deletion_desc=Noņemot tÄ«mekļa ÄÄ·i, tiks dzÄ“sti visi tÄ iestatÄ«jumi un piegÄdes vÄ“sture. Vai turpinÄt?
settings.webhook_deletion_success=TÄ«mekļa ÄÄ·is tika noņemts.
-settings.webhook.test_delivery=Testa piegÄde
-settings.webhook.test_delivery_desc=Veikt viltus push-notikuma piegÄdi, lai notestÄ“tu JÅ«su tÄ«mekļa ÄÄ·a iestatÄ«jumus.
settings.webhook.test_delivery_desc_disabled=Lai pÄrbaudÄ«tu Å¡o tÄ«mekļa ÄÄ·i ar neÄ«stu notikumu, tas ir jÄiespÄ“jo.
settings.webhook.request=Pieprasījums
settings.webhook.response=Atbilde
@@ -2131,7 +2117,6 @@ settings.event_repository=Repozitorijs
settings.event_repository_desc=Repozitorijs izveidots vai dzēsts.
settings.event_header_issue=Problēmu notikumi
settings.event_issues=Problēmas
-settings.event_issues_desc=ProblÄ“ma atvÄ“rta, aizvÄ“rta, atkÄrtoti atvÄ“rta vai mainÄ«ta.
settings.event_issue_assign=Problēmas atbildīgie
settings.event_issue_assign_desc=Problēmai piešķirti vai noņemti atbildīgie.
settings.event_issue_label=Problēmu iezīmes
@@ -2142,7 +2127,6 @@ settings.event_issue_comment=ProblÄ“mas komentÄrs
settings.event_issue_comment_desc=ProblÄ“mas komentÄrs pievienots, labots vai dzÄ“sts.
settings.event_header_pull_request=Izmaiņu pieprasījuma notikumi
settings.event_pull_request=Izmaiņu pieprasījums
-settings.event_pull_request_desc=Izmaiņu pieprasÄ«jums atvÄ“rts, aizvÄ“rts, atkÄrtoti atvÄ“rts vai mainÄ«ts.
settings.event_pull_request_assign=Izmaiņu pieprasījuma atbildīgie
settings.event_pull_request_assign_desc=Izmaiņu pieprasījumam piešķirti vai noņemti atbildīgie.
settings.event_pull_request_label=Izmaiņu pieprasījuma iezīmes
@@ -2317,7 +2301,6 @@ diff.commit=revīzija
diff.git-notes=Piezīmes
diff.data_not_available=Satura salÄ«dzinÄÅ¡ana nav pieejama
diff.options_button=SalÄ«dzinÄÅ¡anas iespÄ“jas
-diff.show_diff_stats=RÄdÄ«t statistiku
diff.download_patch=LejupielÄdÄ“t ielÄpa failu
diff.download_diff=LejupielÄdÄ“t izmaiņu failu
diff.show_split_view=Dalītais skats
@@ -2518,15 +2501,13 @@ settings.visibility.private_shortname=PrivÄta
settings.update_settings=Mainīt iestatījumus
settings.update_setting_success=OrganizÄcijas iestatÄ«jumi tika saglabÄti.
-settings.change_orgname_prompt=PiezÄ«me: organizÄcijas nosaukuma maiņa izmainÄ«s arÄ« organizÄcijas URL un atbrÄ«vos veco nosaukumu.
-settings.change_orgname_redirect_prompt=Vecais vÄrds pÄrsÅ«tÄ«s uz jauno, kamÄ“r vien tas nebÅ«s izmantots.
+
+
settings.update_avatar_success=OrganizÄcijas attÄ“ls tika saglabÄts.
settings.delete=DzÄ“st organizÄciju
settings.delete_account=DzÄ“st Å¡o organizÄciju
settings.delete_prompt=Å Ä« darbÄ«ba pilnÄ«bÄ dzÄ“sÄ«s Å¡o organizÄciju, kÄ arÄ« tÄ ir <strong>NEATGRIEZENISKA</strong>!
settings.confirm_delete_account=ApstiprinÄt dzēšanu
-settings.delete_org_title=DzÄ“st organizÄciju
-settings.delete_org_desc=OrganizÄcija tiks dzÄ“sta neatgriezeniski. Vai turpinÄt?
settings.labels_desc=Pievienojiet iezÄ«mes, kas var tikt izmantotas <strong>visos</strong> šīs organizÄcijas repozitorijos.
@@ -2596,6 +2577,7 @@ teams.invite.by=UzaicinÄja %s
teams.invite.description=Nospiediet pogu zemÄk, lai pievienotos komandai.
+
[admin]
dashboard=Infopanelis
self_check=PaÅ¡pÄrbaude
@@ -2935,8 +2917,6 @@ config.ssh_domain=SSH servera domēns
config.ssh_port=Ports
config.ssh_listen_port=KlausīšanÄs ports
config.ssh_root_path=Saknes ceļš
-config.ssh_key_test_path=AtslÄ“gu pÄrbaudes ceļš
-config.ssh_keygen_path=Keygen ('ssh-keygen') ceļš
config.ssh_minimum_key_size_check=MinimÄlÄ atslÄ“gas lieluma pÄrbaude
config.ssh_minimum_key_sizes=MinimÄlais atslÄ“gas lielums
@@ -3242,7 +3222,6 @@ conda.install=Lai instalētu Conda pakotni, izpildiet sekojošu komandu:
container.details.type=AttÄ“la formÄts
container.details.platform=Platforma
container.pull=AtgÄdÄjiet Å¡o attÄ“lu no komandrindas:
-container.digest=Īssavilkums:
container.multi_arch=OS / arhitektūra
container.layers=AttÄ“la slÄņi
container.labels=Iezīmes
@@ -3343,11 +3322,13 @@ owner.settings.chef.keypair.description=AtslÄ“gu pÄris ir nepiecieÅ¡ams, lai au
secrets=Noslēpumi
description=NoslÄ“pumi tiks padoti atsevišķÄm darbÄ«bÄm un citÄdi nevar tikt nolasÄ«ti.
none=PagaidÄm nav neviena noslÄ“puma.
-creation=Pievienot noslēpumu
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Apraksts
creation.name_placeholder=reÄ£istr-nejÅ«tÄ«gs, tikai burti, cipari un apakÅ¡svÄ«tras, nevar sÄkties ar GITEA_ vai GITHUB_
creation.value_placeholder=Ievadiet jebkÄdu saturu. Atstarpes sÄkumÄ un beigÄ tiks noņemtas.
-creation.success=Noslēpums "%s" tika pievienots.
-creation.failed=NeizdevÄs pievienot noslÄ“pumu.
+
+
deletion=Dzēst noslēpumu
deletion.description=NoslÄ“puma dzēšana ir neatgriezeniska. Vai turpinÄt?
deletion.success=Noslēpums tika izdzēsts.
diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini
index 8a6dabbceb..01e279f91a 100644
--- a/options/locale/locale_nl-NL.ini
+++ b/options/locale/locale_nl-NL.ini
@@ -50,6 +50,7 @@ webauthn_reload=Vernieuwen
repository=Repository
organization=Organisatie
mirror=Kopie
+issue_milestone=Mijlpaal
new_repo=Nieuwe repository
new_migrate=Nieuwe migratie
new_mirror=Nieuwe kopie
@@ -275,6 +276,7 @@ show_only_public=Toon alleen opbenbaar
issues.in_your_repos=In uw repositories
+
[explore]
repos=Repositories
users=Gebruikers
@@ -320,7 +322,6 @@ oauth_signup_submit=Account voltooien
oauth_signin_tab=Bestaand account koppelen
oauth_signin_title=Inloggen om het gekoppelde account te machtigen
oauth_signin_submit=Account koppelen
-oauth.signin.error=Er is een fout opgetreden bij het verwerken van het autorisatieverzoek. Als deze fout zich blijft voordoen, neem dan contact op met de sitebeheerder.
oauth.signin.error.access_denied=Het autorisatieverzoek is geweigerd.
oauth.signin.error.temporarily_unavailable=Autorisatie mislukt omdat de verificatieserver tijdelijk niet beschikbaar is. Probeer het later opnieuw.
openid_connect_submit=Verbinden
@@ -940,6 +941,7 @@ labels=Labels
org_labels_desc=Organisatielabel dat gebruikt kan worden met <strong>alle repositories</strong> onder deze organisatie
org_labels_desc_manage=beheren
+milestone=Mijlpaal
milestones=Mijlpalen
commits=Commits
commit=Commit
@@ -963,7 +965,6 @@ file_copy_permalink=Permalink kopiëren
view_git_blame=Bekijk Git Blame
video_not_supported_in_browser=Je browser ondersteunt de HTML5 'video'-tag niet.
audio_not_supported_in_browser=Je browser ondersteunt de HTML5 'audio'-tag niet.
-stored_lfs=Opgeslagen met Git LFS
symbolic_link=Symbolic link
commit_graph=Commit grafiek
commit_graph.select=Selecteer branches
@@ -1011,7 +1012,6 @@ editor.file_changed_while_editing=De bestandsinhoud is veranderd sinds je bent b
editor.commit_empty_file_header=Commit een leeg bestand
editor.commit_empty_file_text=Het bestand dat u wilt committen is leeg. Doorgaan?
editor.no_changes_to_show=Er zijn geen wijzigingen om weer te geven.
-editor.fail_to_update_file_summary=Foutmelding:
editor.push_rejected_no_message=De wijziging is afgewezen door de server zonder bericht. Controleer de Git Hooks alsjeblieft.
editor.push_rejected=De wijziging is afgewezen door de server. Controleer Controleer de Git Hooks alsjeblieft.
editor.push_rejected_summary=Volledig afwijzingsbericht:
@@ -1022,6 +1022,7 @@ editor.require_signed_commit=Branch vereist een ondertekende commit
editor.cherry_pick=Cherry-pick %s op:
editor.revert=%s ongedaan maken op:
+
commits.desc=Bekijk de broncode-wijzigingsgeschiedenis.
commits.commits=Commits
commits.nothing_to_compare=Deze branches zijn gelijk.
@@ -1139,8 +1140,6 @@ issues.filter_milestone=Mijlpaal
issues.filter_project=Project
issues.filter_project_none=Geen project
issues.filter_assignee=Aangewezene
-issues.filter_assginee_no_select=Alle toegewezen personen
-issues.filter_assginee_no_assignee=Geen verantwoordelijke
issues.filter_poster=Auteur
issues.filter_type=Type
issues.filter_type.all_issues=Alle kwesties
@@ -1233,7 +1232,6 @@ issues.subscribe=Abonneren
issues.unsubscribe=Uitschrijven
issues.lock=Gesprek vergrendelen
issues.unlock=Gesprek ontgrendelen
-issues.lock.unknown_reason=Kan een probleem niet vergrendelen met een onbekende reden.
issues.lock_duplicate=Een issue kan niet twee keer vergrendeld worden.
issues.unlock_error=Kan een niet vergrendeld issue niet ontgrendelen.
issues.lock_with_reason=vergrendeld als <strong>%s</strong> en beperkt gesprek tot medewerkers %s
@@ -1386,7 +1384,6 @@ pulls.add_prefix=Voeg <strong>%s</strong> prefix toe
pulls.remove_prefix=Verwijder <strong>%s</strong> prefix
pulls.data_broken=Deze pull-aanvraag is ongeldig wegens missende fork-informatie.
pulls.files_conflicted=Dit pull request heeft wijzigingen die strijdig zijn met de doel branch.
-pulls.is_checking=Controle op samenvoegingsconflicten is nog bezig. Probeer later nog een keer.
pulls.is_ancestor=Deze branch is al opgenomen in de toegewezen branch. Er is niets om samen te voegen.
pulls.is_empty=De wijzigingen in deze branch bevinden zich al in de toegewezen branch. Dit zal een lege commit zijn.
pulls.required_status_check_failed=Sommige vereiste controles waren niet succesvol.
@@ -1669,8 +1666,6 @@ settings.hooks_desc=Webhooks maken automatisch een HTTP POST verzoek naar een se
settings.webhook_deletion=Verwijder webhook
settings.webhook_deletion_desc=Verwijderen van een webhook verwijdert de instellingen en de geschiedenis van afleveringen. Doorgaan?
settings.webhook_deletion_success=Webhook is verwijderd.
-settings.webhook.test_delivery=Test-bezorging
-settings.webhook.test_delivery_desc=Test deze webhook met een nep-gebeurtenis.
settings.webhook.request=Verzoek
settings.webhook.response=Antwoord
settings.webhook.headers=Headers
@@ -1710,7 +1705,6 @@ settings.event_repository=Repository
settings.event_repository_desc=Repository gemaakt of verwijderd.
settings.event_header_issue=Issue gebeurtenissen
settings.event_issues=Kwesties
-settings.event_issues_desc=Issue geopend, gesloten, heropend of bewerkt.
settings.event_issue_assign=Probleem toegekend
settings.event_issue_assign_desc=Issue toegewezen of niet-toegewezen.
settings.event_issue_label=Issue gelabeld
@@ -1721,7 +1715,6 @@ settings.event_issue_comment=Issue commentaar
settings.event_issue_comment_desc=Issue reactie aangemaakt, bewerkt of verwijderd.
settings.event_header_pull_request=Pull Request Events
settings.event_pull_request=Pull request
-settings.event_pull_request_desc=Pull request geopend, gesloten, heropend of bewerkt.
settings.event_pull_request_assign=Pull request toegewezen
settings.event_pull_request_assign_desc=Pull request toegewezen of niet-toegewezen.
settings.event_pull_request_label=Pull-aanvraag gelabeld
@@ -1861,7 +1854,6 @@ diff.commit=commit
diff.git-notes=Notities
diff.data_not_available=Diff gegevens niet beschikbaar
diff.options_button=Diff opties
-diff.show_diff_stats=Statistieken weergeven
diff.download_patch=Download Patch-bestand
diff.download_diff=Download Diff-bestand
diff.show_split_view=Zij-aan-zij weergave
@@ -1993,13 +1985,13 @@ settings.visibility.private=Privé (alleen zichtbaar voor organisatieleden)
settings.visibility.private_shortname=Privé
settings.update_settings=Instellingen bijwerken
+
+
settings.update_avatar_success=De avatar van de organisatie is aangepast.
settings.delete=Verwijder organisatie
settings.delete_account=Verwijder deze organisatie
settings.delete_prompt=Deze organisatie zal permanent worden verwijderd. U kunt dit <strong>NIET</strong> ongedaan maken!
settings.confirm_delete_account=Bevestig verwijdering
-settings.delete_org_title=Verwijder organisatie
-settings.delete_org_desc=Deze organisatie zal permanent verwijderd worden. Doorgaan?
settings.hooks_desc=Een webhook toevoegen die door <strong>alle repositories</strong> in deze organisatie getriggerd kan worden.
settings.labels_desc=Voeg labels toe die kunnen worden gebruikt bij problemen voor <strong>alle repositories</strong> in deze organisatie.
@@ -2055,6 +2047,7 @@ teams.all_repositories_helper=Team heeft toegang tot alle repositories. Door dit
teams.all_repositories_read_permission_desc=Dit team heeft <strong>Lees</strong> toegang tot <strong>alle repositories</strong>: leden kunnen repositories bekijken en klonen.
+
[admin]
dashboard=Overzicht
users=Gebruikersacount
@@ -2310,8 +2303,6 @@ config.ssh_start_builtin_server=Gebruik de ingebouwde server
config.ssh_port=Poort
config.ssh_listen_port=Luister op poort
config.ssh_root_path=Root-pad
-config.ssh_key_test_path=Pad voor key-tests
-config.ssh_keygen_path=Pad van keygen ('ssh-keygen')
config.ssh_minimum_key_size_check=Controleer minimale key-lengte
config.ssh_minimum_key_sizes=Minimale key-lengtes
@@ -2521,6 +2512,11 @@ owner.settings.cleanuprules.enabled=Ingeschakeld
[secrets]
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Omschrijving
+
+
+
[actions]
diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini
index 4d049c83d1..cccbe47f9d 100644
--- a/options/locale/locale_pl-PL.ini
+++ b/options/locale/locale_pl-PL.ini
@@ -272,6 +272,7 @@ show_only_public=Wyświetlanie tylko publicznych
issues.in_your_repos=W Twoich repozytoriach
+
[explore]
repos=Repozytoria
users=Użytkownicy
@@ -899,7 +900,6 @@ file_too_large=Ten plik jest zbyt duży, aby go wyświetlić.
file_copy_permalink=Kopiuj bezpośredni odnośnik
video_not_supported_in_browser=Twoja przeglądarka nie obsługuje znacznika HTML5 "video".
audio_not_supported_in_browser=Twoja przeglądarka nie obsługuje znacznika HTML5 "audio".
-stored_lfs=Przechowane za pomocÄ… Git LFS
symbolic_link=DowiÄ…zanie symboliczne
commit_graph=Wykres commitów
commit_graph.select=Wybierz gałęzie
@@ -942,13 +942,13 @@ editor.file_changed_while_editing=Zawartość pliku zmieniła się, odkąd rozpo
editor.commit_empty_file_header=Commituj pusty plik
editor.commit_empty_file_text=Plik, który zamierzasz commitować, jest pusty. Kontynuować?
editor.no_changes_to_show=Brak zmian do pokazania.
-editor.fail_to_update_file_summary=Komunikat błędu:
editor.push_rejected_summary=Pełny komunikat odrzucenia:
editor.add_subdir=Dodaj katalog…
editor.no_commit_to_branch=Zatwierdzanie bezpośrednio do tej gałęzi nie jest możliwe, ponieważ:
editor.user_no_push_to_branch=Użytkownik nie może wypychać do gałęzi
editor.require_signed_commit=Gałąź wymaga podpisanych commitów
+
commits.desc=Przeglądaj historię zmian kodu źródłowego.
commits.commits=Commity
commits.search_all=Wszystkie gałęzie
@@ -1053,8 +1053,6 @@ issues.filter_label_no_select=Wszystkie etykiety
issues.filter_milestone=Kamień milowy
issues.filter_project_none=Brak projektu
issues.filter_assignee=Przypisany
-issues.filter_assginee_no_select=Wszyscy przypisani
-issues.filter_assginee_no_assignee=Brak przypisania
issues.filter_type=Typ
issues.filter_type.all_issues=Wszystkie zgłoszenia
issues.filter_type.assigned_to_you=Przypisane do Ciebie
@@ -1136,7 +1134,6 @@ issues.subscribe=Subskrybuj
issues.unsubscribe=Anuluj subskrypcjÄ™
issues.lock=Zablokuj konwersacjÄ™
issues.unlock=Odblokuj konwersacjÄ™
-issues.lock.unknown_reason=Nie można zablokować zagadnienia bez żadnego powodu.
issues.lock_duplicate=Zagadnienie nie może być zablokowane ponownie.
issues.unlock_error=Nie można odblokować zagadnienia, które nie jest zablokowane.
issues.lock_with_reason=zablokowano jako <strong>%s</strong> i ograniczono konwersację do współtwórców %s
@@ -1263,7 +1260,6 @@ pulls.add_prefix=Dodaj <strong>%s</strong> prefiks
pulls.remove_prefix=Usuń <strong>%s</strong> prefiks
pulls.data_broken=Ten Pull Request jest uszkodzony ze względu na brakujące informacje o forku.
pulls.files_conflicted=Ten Pull Request zawiera zmiany konfliktujące z docelową gałęzią.
-pulls.is_checking=Sprawdzanie konfliktów ze scalaniem w toku. Spróbuj ponownie za chwilę.
pulls.required_status_check_failed=Niektóre kontrole stanów nie były pomyślne.
pulls.required_status_check_missing=Brakuje pewnych wymaganych etapów.
pulls.required_status_check_administrator=Jako administrator, możesz wciąż scalić ten Pull Request.
@@ -1557,8 +1553,6 @@ settings.hooks_desc=Webhooki automatycznie tworzÄ… zapytania HTTP POST do serwer
settings.webhook_deletion=Usuń Webhooka
settings.webhook_deletion_desc=Usunięcie Webhooka wykasuje jego ustawienia i historię dostaw. Kontynuować?
settings.webhook_deletion_success=Webhook został usunięty.
-settings.webhook.test_delivery=Testuj dostawÄ™
-settings.webhook.test_delivery_desc=Sprawdź tego Webhooka przy pomocy testowego zdarzenia.
settings.webhook.request=Żądanie
settings.webhook.response=Odpowiedź
settings.webhook.headers=Nagłówki
@@ -1598,7 +1592,6 @@ settings.event_repository=Repozytorium
settings.event_repository_desc=Repozytorium stworzone lub usunięte.
settings.event_header_issue=Zdarzenia zgłoszeń
settings.event_issues=Zgłoszenia
-settings.event_issues_desc=Zgłoszenie otwarte, zamknięte, ponownie otwarte lub zmodyfikowane.
settings.event_issue_assign=Zgłoszenie przypisane
settings.event_issue_assign_desc=Zgłoszenie przypisane bądź nieprzypisane.
settings.event_issue_label=Zgłoszenie oznaczone
@@ -1609,7 +1602,6 @@ settings.event_issue_comment=Komentarz w zgłoszeniu
settings.event_issue_comment_desc=Komentarz w zgłoszeniu stworzony, edytowany lub usunięty.
settings.event_header_pull_request=Zdarzenia Pull Requestów
settings.event_pull_request=Pull Request
-settings.event_pull_request_desc=Pull request otwarty, zamknięty, ponownie otwarty lub zmodyfikowany.
settings.event_pull_request_assign=Pull Request przypisany
settings.event_pull_request_assign_desc=Pull Request przypisany bÄ…dz nieprzypisany.
settings.event_pull_request_label=Pull Request zaetykietowany
@@ -1727,7 +1719,6 @@ diff.commit=commit
diff.git-notes=Notatki
diff.data_not_available=Informacje nt. zmian nie są dostępne
diff.options_button=Opcje porównania
-diff.show_diff_stats=Pokaż statystyki
diff.download_patch=ÅšciÄ…gnij plik aktualizacji
diff.download_diff=Ściągnij plik porównania
diff.show_split_view=Widok podzielony
@@ -1867,14 +1858,13 @@ settings.visibility.private_shortname=Prywatny
settings.update_settings=Aktualizuj ustawienia
settings.update_setting_success=Ustawienia organizacji zostały zaktualizowane.
-settings.change_orgname_redirect_prompt=Stara nazwa będzie przekierowywała dopóki ktoś jej nie zajmie.
+
+
settings.update_avatar_success=Awatar organizacji został zaktualizowany.
settings.delete=Usuń organizację
settings.delete_account=Usuń tą organizację
settings.delete_prompt=Organizacja zostanie trwale usunięta. Tej akcji <strong>NIE MOŻNA</strong> cofnąć!
settings.confirm_delete_account=Potwierdź usunięcie
-settings.delete_org_title=Usuń organizację
-settings.delete_org_desc=Ta organizacja zostanie trwale usunięta. Kontynuować?
settings.hooks_desc=Dodaj webhooki, uruchamiane dla <strong>wszystkich repozytoriów</strong> w tej organizacji.
settings.labels_desc=Dodaj etykiety, które mogą być używane w zgłoszeniach dla <strong>wszystkich repozytoriów</strong> w tej organizacji.
@@ -1934,6 +1924,7 @@ teams.all_repositories_write_permission_desc=Ten zespół nadaje uprawnienie <st
teams.all_repositories_admin_permission_desc=Ten zespół nadaje uprawnienia <strong>Administratora</strong> do <strong>wszystkich repozytoriów</strong>: jego członkowie mogą odczytywać, przesyłać oraz dodawać innych współtwórców do repozytoriów.
+
[admin]
dashboard=Pulpit
users=Konta użytkownika
@@ -2199,8 +2190,6 @@ config.ssh_start_builtin_server=Wykorzystaj wbudowany serwer
config.ssh_port=Port
config.ssh_listen_port=Port nasłuchiwania
config.ssh_root_path=Ścieżka do katalogu głównego
-config.ssh_key_test_path=Ścieżka do klucza testowego
-config.ssh_keygen_path=Ścieżka do generatora ('ssh-keygen')
config.ssh_minimum_key_size_check=Sprawdzanie minimalnej długości klucza
config.ssh_minimum_key_sizes=Minimalne rozmiary kluczy
@@ -2310,7 +2299,6 @@ monitor.start=Czas rozpoczęcia
monitor.execute_time=Czas wykonania
monitor.process.cancel=Anuluj proces
monitor.process.cancel_desc=Anulowanie procesu może spowodować utratę danych
-monitor.process.cancel_notices=Anuluj: <strong>%s</strong>?
monitor.queues=Kolejki
monitor.queue=Kolejka: %s
@@ -2413,6 +2401,11 @@ owner.settings.cleanuprules.enabled=Włączone
[secrets]
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Opis
+
+
+
[actions]
diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini
index f0c034a133..3f7eb73a52 100644
--- a/options/locale/locale_pt-BR.ini
+++ b/options/locale/locale_pt-BR.ini
@@ -52,6 +52,7 @@ webauthn_reload=Recarregar
repository=Repositório
organization=Organização
mirror=Espelhamento
+issue_milestone=Marco
new_repo=Novo repositório
new_migrate=Nova migração
new_mirror=Novo espelhamento
@@ -334,6 +335,7 @@ show_only_public=Mostrando somente públicos
issues.in_your_repos=Em seus repositórios
+
[explore]
repos=Repositórios
users=Usuários
@@ -388,7 +390,6 @@ oauth_signup_submit=Completar conta
oauth_signin_tab=Vincular à uma conta existente
oauth_signin_title=Acesse com uma conta vinculada
oauth_signin_submit=Vincular conta
-oauth.signin.error=Ocorreu um erro durante o processamento do pedido de autorização. Se este erro persistir, contate o administrador.
oauth.signin.error.access_denied=O pedido de autorização foi negado.
oauth.signin.error.temporarily_unavailable=A autorização falhou porque o servidor de autenticação está temporariamente indisponível. Por favor, tente novamente mais tarde.
openid_connect_submit=Conectar
@@ -810,7 +811,6 @@ delete_token_success=O token foi excluído. Os aplicativos que o utilizam já nÃ
repo_and_org_access=Acesso ao Repositório e Organização
permissions_public_only=Apenas público
permissions_access_all=Todos (público, privado e limitado)
-select_permissions=Selecionar permissões
permission_no_access=Sem acesso
permission_read=Ler
permission_write=Ler e escrever
@@ -1119,6 +1119,7 @@ labels=Etiquetas
org_labels_desc=Rótulos de nível de organização que podem ser usados em <strong>todos os repositórios</strong> sob esta organização
org_labels_desc_manage=gerenciar
+milestone=Marco
milestones=Marcos
commits=Commits
commit=Commit
@@ -1135,9 +1136,9 @@ file_view_raw=Ver original
file_permalink=Link permanente
file_too_large=O arquivo é muito grande para ser mostrado.
invisible_runes_header=`Este arquivo contém caracteres Unicode invisíveis`
-invisible_runes_description=`Este arquivo contém caracteres Unicode invisíveis que são indistinguíveis para humanos, mas que podem ser processados de forma diferente por um computador. Se você acha que isso é intencional, pode ignorar esse aviso com segurança. Use o botão Escapar para revelá-los
+invisible_runes_description=`Este arquivo contém caracteres Unicode invisíveis que são indistinguíveis para humanos, mas que podem ser processados de forma diferente por um computador. Se você acha que isso é intencional, pode ignorar esse aviso com segurança. Use o botão Escapar para revelá-los`
ambiguous_runes_header=`Este arquivo contém caracteres Unicode ambíguos`
-ambiguous_runes_description=`Este arquivo contém caracteres Unicode que podem ser confundidos com outros caracteres. Se você acha que isso é intencional, pode ignorar esse aviso com segurança. Use o botão Escapar para revelá-los
+ambiguous_runes_description=`Este arquivo contém caracteres Unicode que podem ser confundidos com outros caracteres. Se você acha que isso é intencional, pode ignorar esse aviso com segurança. Use o botão Escapar para revelá-los`
invisible_runes_line=`Esta linha tem caracteres unicode invisíveis`
ambiguous_runes_line=`Esta linha tem caracteres unicode ambíguos`
@@ -1147,7 +1148,6 @@ file_copy_permalink=Copiar Link Permanente
view_git_blame=Ver Git Blame
video_not_supported_in_browser=Seu navegador não suporta a tag 'video' do HTML5.
audio_not_supported_in_browser=Seu navegador não suporta a tag 'audio' do HTML5.
-stored_lfs=Armazenado com Git LFS
symbolic_link=Link simbólico
executable_file=Arquivo executável
generated=Gerado
@@ -1191,7 +1191,6 @@ editor.update=Atualizar %s
editor.delete=Excluir %s
editor.patch=Aplicar Correção
editor.patching=Corrigindo:
-editor.fail_to_apply_patch=`Não foi possível aplicar a correção "%s"`
editor.new_patch=Nova correção
editor.commit_message_desc=Adicione uma descrição detalhada (opcional)...
editor.signoff_desc=Adicione um assinado-por-committer no final do log do commit.
@@ -1209,15 +1208,11 @@ editor.branch_already_exists=Branch "%s" já existe neste repositório.
editor.directory_is_a_file=O nome do diretório "%s" já é usado como um nome de arquivo neste repositório.
editor.file_is_a_symlink=`"%s" é um link simbólico. Links simbólicos não podem ser editados no editor da web`
editor.filename_is_a_directory=O nome do arquivo "%s" já é usado como um nome de diretório neste repositório.
-editor.file_editing_no_longer_exists=O arquivo que está sendo editado, "%s", não existe mais neste repositório.
-editor.file_deleting_no_longer_exists=O arquivo a ser excluído, "%s", não existe mais neste repositório.
editor.file_changed_while_editing=O conteúdo do arquivo mudou desde que você começou a editar. <a target="_blank" rel="noopener noreferrer" href="%s">Clique aqui</a> para ver o que foi editado ou <strong>clique em Aplicar commit das alterações novamemente</strong> para sobreescrever estas alterações.
editor.file_already_exists=Um arquivo com nome "%s" já existe neste repositório.
editor.commit_empty_file_header=Fazer commit de um arquivo vazio
editor.commit_empty_file_text=O arquivo que você está prestes fazer commit está vazio. Continuar?
editor.no_changes_to_show=Nenhuma alteração a mostrar.
-editor.fail_to_update_file=Falha ao atualizar/criar arquivo "%s".
-editor.fail_to_update_file_summary=Mensagem de erro:
editor.push_rejected_no_message=A alteração foi rejeitada pelo servidor sem uma mensagem. Por favor, verifique os Hooks Git.
editor.push_rejected=A alteração foi rejeitada pelo servidor. Por favor, verifique os Hooks Git.
editor.push_rejected_summary=Mensagem completa de rejeição:
@@ -1232,6 +1227,7 @@ editor.require_signed_commit=Branch requer um commit assinado
editor.cherry_pick=Cherry-pick %s para:
editor.revert=Reverter %s para:
+
commits.desc=Veja o histórico de alterações do código de fonte.
commits.commits=Commits
commits.no_commits=Nenhum commit em comum. "%s" e "%s" tem históricos completamente diferentes.
@@ -1377,8 +1373,6 @@ issues.filter_project=Projeto
issues.filter_project_all=Todos os projetos
issues.filter_project_none=Sem projeto
issues.filter_assignee=Atribuído
-issues.filter_assginee_no_select=Todos os responsáveis
-issues.filter_assginee_no_assignee=Sem responsável
issues.filter_poster=Autor
issues.filter_type=Tipo
issues.filter_type.all_issues=Todas as issues
@@ -1491,13 +1485,12 @@ issues.attachment.open_tab=`Clique para ver "%s" em uma nova aba`
issues.attachment.download=`Clique para baixar "%s"`
issues.subscribe=Inscrever-se
issues.unsubscribe=Desinscrever
-issues.unpin_issue=Desfixar issue
+issues.unpin=Desfixar
issues.max_pinned=Você não pode fixar mais issues
issues.pin_comment=fixou isto %s
issues.unpin_comment=desafixou isto %s
issues.lock=Bloquear conversação
issues.unlock=Desbloquear conversação
-issues.lock.unknown_reason=Não pode-se bloquear uma issue com um motivo desconhecido.
issues.lock_duplicate=Uma issue não pode ser bloqueada duas vezes.
issues.unlock_error=Não pode-se desbloquear uma issue que não esteja bloqueada.
issues.lock_with_reason=bloqueada como <strong>%s</strong> e conversação limitada para colaboradores %s
@@ -1672,7 +1665,6 @@ pulls.add_prefix=Adicione o prefixo <strong>%s</strong>
pulls.remove_prefix=Remover o prefixo <strong>%s</strong>
pulls.data_broken=Este pull request está quebrado devido a falta de informação do fork.
pulls.files_conflicted=Este pull request tem alterações conflitantes com o branch de destino.
-pulls.is_checking=Verificação de conflitos do merge está em andamento. Tente novamente em alguns momentos.
pulls.is_ancestor=Este branch já está incluído no branch de destino. Não há nada para mesclar.
pulls.is_empty=As alterações neste branch já estão na branch de destino. Este será um commit vazio.
pulls.required_status_check_failed=Algumas verificações necessárias não foram bem sucedidas.
@@ -2063,8 +2055,6 @@ settings.hooks_desc=Webhooks automaticamente fazem requisições de HTTP POST pa
settings.webhook_deletion=Remover webhook
settings.webhook_deletion_desc=A exclusão de um webhook exclui suas configurações e o histórico de entrega. Continuar?
settings.webhook_deletion_success=O webhook foi removido.
-settings.webhook.test_delivery=Entrega de teste
-settings.webhook.test_delivery_desc=Teste este webhook com um falso evento.
settings.webhook.request=Solicitação
settings.webhook.response=Resposta
settings.webhook.headers=Cabeçalhos
@@ -2108,7 +2098,6 @@ settings.event_repository=Repositório
settings.event_repository_desc=Repositório criado ou excluído.
settings.event_header_issue=Eventos da Issue
settings.event_issues=Issues
-settings.event_issues_desc=Issue aberta, fechada, reaberta ou editada.
settings.event_issue_assign=Issue Atribuída
settings.event_issue_assign_desc=Issue atribuída ou não atribuída.
settings.event_issue_label=Issue Rotulada
@@ -2119,7 +2108,6 @@ settings.event_issue_comment=Comentário da issue
settings.event_issue_comment_desc=Comentário da issue criado, editado ou excluído.
settings.event_header_pull_request=Eventos de Pull Request
settings.event_pull_request=Pull request
-settings.event_pull_request_desc=Pull request aberto, fechado, reaberto ou editado.
settings.event_pull_request_assign=Pull Request Atribuído
settings.event_pull_request_assign_desc=Pull request atribuído ou desatribuído.
settings.event_pull_request_label=Pull Request Rotulado
@@ -2192,7 +2180,7 @@ settings.protect_check_status_contexts_list=Verificações de status encontradas
settings.protect_required_approvals=Aprovações necessárias:
settings.dismiss_stale_approvals=Descartar aprovações obsoletas
settings.dismiss_stale_approvals_desc=Quando novos commits que mudam o conteúdo do pull request são enviados para o branch, as antigas aprovações serão descartadas.
-settings.require_signed_commits=Exibir commits assinados
+settings.require_signed_commits=Exigir commits assinados
settings.require_signed_commits_desc=Rejeitar pushes para este branch se não estiverem assinados ou não forem validáveis.
settings.protect_branch_name_pattern=Padrão de Nome de Branch Protegida
settings.protect_patterns=Padrões
@@ -2279,7 +2267,6 @@ diff.commit=commit
diff.git-notes=Notas
diff.data_not_available=Conteúdo de diff não disponível
diff.options_button=Opções de diferenças
-diff.show_diff_stats=Mostrar estatísticas
diff.download_patch=Baixar arquivo de patch
diff.download_diff=Baixar arquivo de diferenças
diff.show_split_view=Visão dividida
@@ -2473,14 +2460,13 @@ settings.visibility.private_shortname=Privado
settings.update_settings=Atualizar Configurações
settings.update_setting_success=Configurações da organização foram atualizadas.
-settings.change_orgname_redirect_prompt=O nome antigo irá redirecionar até que seja reivindicado.
+
+
settings.update_avatar_success=O avatar da organização foi atualizado.
settings.delete=Excluir organização
settings.delete_account=Excluir esta organização
settings.delete_prompt=A organização será excluída permanentemente. Isto <strong>NÃO PODERÃ</strong> ser desfeito!
settings.confirm_delete_account=Confirmar exclusão
-settings.delete_org_title=Excluir organização
-settings.delete_org_desc=Essa organização será excluída permanentemente. Continuar?
settings.hooks_desc=Adicionar Webhooks que serão acionados para <strong>todos os repositórios</strong> desta organização.
settings.labels_desc=Adicionar rótulos que possam ser usadas em issues para <strong>todos os repositórios</strong> desta organização.
@@ -2551,6 +2537,7 @@ teams.invite.by=Convidado por %s
teams.invite.description=Por favor, clique no botão abaixo para se juntar à equipe.
+
[admin]
dashboard=Painel
identity_access=Identidade e acesso
@@ -2879,8 +2866,6 @@ config.ssh_domain=Domínio do servidor SSH
config.ssh_port=Porta
config.ssh_listen_port=Porta de escuta
config.ssh_root_path=Caminho da raiz
-config.ssh_key_test_path=Caminho da chave de teste
-config.ssh_keygen_path=Caminho do keygen ('ssh-keygen')
config.ssh_minimum_key_size_check=Verificar tamanho mínimo da chave
config.ssh_minimum_key_sizes=Tamanhos mínimos da chave
@@ -3180,7 +3165,6 @@ conda.install=Para instalar o pacote usando o Conda, execute o seguinte comando:
container.details.type=Tipo de Imagem
container.details.platform=Plataforma
container.pull=Puxe a imagem pela linha de comando:
-container.digest=Digest:
container.multi_arch=S.O. / Arquitetura
container.layers=Camadas da Imagem
container.labels=Rótulos
@@ -3276,11 +3260,13 @@ owner.settings.chef.keypair=Gerar par de chaves
secrets=Segredos
description=Os segredos serão passados a certas ações e não poderão ser lidos de outra forma.
none=Não há segredos ainda.
-creation=Adicionar Segredo
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Descrição
creation.name_placeholder=apenas caracteres alfanuméricos ou underline (_), não pode começar com GITEA_ ou GITHUB_
creation.value_placeholder=Insira qualquer conteúdo. Espaços em branco no início e no fim serão omitidos.
-creation.success=O segredo "%s" foi adicionado.
-creation.failed=Falha ao adicionar segredo.
+
+
deletion=Excluir segredo
deletion.description=A exclusão de um segredo é permanente e não pode ser desfeita. Continuar?
deletion.success=O segredo foi excluído.
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 0fb56f6763..2ebea36af9 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -54,6 +54,7 @@ webauthn_reload=Recarregar
repository=Repositório
organization=Organização
mirror=Réplica
+issue_milestone=Etapa
new_repo=Novo repositório
new_migrate=Nova migração
new_mirror=Nova réplica
@@ -112,9 +113,11 @@ copy_type_unsupported=Este tipo de ficheiro não pode ser copiado
write=Escrever
preview=Pré-visualizar
loading=Carregando…
+files=Ficheiros
error=Erro
error404=A página que pretende aceder <strong>não existe</strong> ou <strong>não tem autorização</strong> para a ver.
+error503=O servidor não conseguiu concluir o seu pedido. Tente novamente mais tarde.
go_back=Voltar
invalid_data=Dados inválidos: %v
@@ -127,6 +130,7 @@ pin=Fixar
unpin=Desafixar
artifacts=Artefactos
+expired=Expirado
confirm_delete_artifact=Tem a certeza que quer eliminar este artefacto "%s"?
archived=Arquivado
@@ -168,6 +172,10 @@ search=Pesquisar...
type_tooltip=Tipo de pesquisa
fuzzy=Aproximada
fuzzy_tooltip=Incluir também os resultados que estejam próximos do termo de pesquisa
+words=Palavras
+words_tooltip=Incluir apenas os resultados que correspondam às palavras do termo de pesquisa
+regexp=Regexp
+regexp_tooltip=Incluir apenas os resultados que correspondam ao termo de pesquisa com expressões regulares
exact=Fiel
exact_tooltip=Incluir somente os resultados que correspondam rigorosamente ao termo de pesquisa
repo_kind=Pesquisar repositórios...
@@ -261,7 +269,7 @@ path=Localização
sqlite_helper=Localização do ficheiro da base de dados em SQLite3.<br>Insira um caminho absoluto se corre o Gitea como um serviço.
reinstall_error=Está a tentar instalar numa base de dados do Gitea já existente
reinstall_confirm_message=Reinstalar com uma base de dados do Gitea já existente pode causar múltiplos problemas. Na maioria dos casos deve usar o seu "app.ini" existente para correr o Gitea. Se souber o que está a fazer, confirme o seguinte:
-reinstall_confirm_check_1=Os dados encriptados pela chave secreta (SECRET_KEY) no ficheiro app.ini poderão ser perdidos: utilizadores poderão não ser capazes de iniciar a sessão com autenticação em dois passos (2FA) ou com chaves de utilização única (OTP) e as réplicas poderão deixar de funcionar em condições. Ao marcar esta opção estará a confirmar que o ficheiro app.ini vigente contém a SECRET_KEY certa.
+reinstall_confirm_check_1=Os dados encriptados pela chave secreta (SECRET_KEY) no ficheiro app.ini poderão ser perdidos: utilizadores poderão não ser capazes de iniciar a sessão com autenticação em dois passos (2FA) ou com chaves de utilização única (OTP) e as réplicas poderão deixar de funcionar em boas condições. Ao marcar esta opção estará a confirmar que o ficheiro app.ini vigente contém a SECRET_KEY certa.
reinstall_confirm_check_2=Os repositórios e as configurações poderão ter de voltar a ser sincronizados. Ao marcar esta opção estará a confirmar que vai voltar a sincronizar manualmente os automatismos para os repositórios e o ficheiro authorized_keys. Estará também a confirmar que vai assegurar que as configurações do repositório e das réplicas estão em condições.
reinstall_confirm_check_3=Você confirma que tem a certeza absoluta de que este Gitea está a correr com a localização certa do ficheiro app.ini e que tem a certeza de que tem de voltar a instalar. Você confirma que tomou conhecimento dos riscos acima descritos.
err_empty_db_path=A localização da base de dados SQLite3 não pode estar vazia.
@@ -384,6 +392,13 @@ show_only_public=Apresentando somente os públicos
issues.in_your_repos=Nos seus repositórios
+guide_title=Sem trabalho
+guide_desc=Neste momento não está a seguir repositórios nem utilizadores, por isso não há conteúdo a apresentar. Pode explorar repositórios ou utilizadores de interesse a partir das ligações abaixo.
+explore_repos=Explorar repositórios
+explore_users=Explorar utilizadores
+empty_org=Ainda não há organizações.
+empty_repo=Ainda não há repositórios.
+
[explore]
repos=Repositórios
users=Utilizadores
@@ -406,6 +421,7 @@ remember_me.compromised=O identificador da sessão já não é válido, o que po
forgot_password_title=Esqueci-me da senha
forgot_password=Esqueceu a sua senha?
need_account=Precisa de uma conta?
+sign_up_tip=Você está a registar a primeira conta no sistema, que tem privilégios de administrador. Guarde cuidadosamente o nome de utilizador e a senha. Se se esquecer do nome de utilizador ou da senha, consulte a documentação do Gitea para recuperar a conta.
sign_up_now=Inscreva-se agora.
sign_up_successful=A conta foi criada com sucesso. Bem-vindo/a!
confirmation_mail_sent_prompt_ex=Foi enviado um email de confirmação para <b>%s</b>. Verifique a sua caixa de entrada dentro de %s para completar o processo de registo. Se o seu endereço de email de registo estiver errado, pode iniciar a sessão novamente e mudá-lo.
@@ -436,6 +452,7 @@ use_scratch_code=Usar um código de recuperação
twofa_scratch_used=Você usou o seu código de recuperação. Foi reencaminhado para a página de configurações da autenticação em dois passos para poder remover o registo do seu dispositivo ou gerar um novo código de recuperação.
twofa_passcode_incorrect=A senha está errada. Se perdeu o seu dispositivo, use o código de recuperação para iniciar a sessão.
twofa_scratch_token_incorrect=O código de recuperação está errado.
+twofa_required=Tem de configurar a autenticação em dois passos para obter acesso aos repositórios ou então tentar iniciar a sessão novamente.
login_userpass=Iniciar sessão
login_openid=OpenID
oauth_signup_tab=Fazer inscrição
@@ -444,7 +461,7 @@ oauth_signup_submit=Completar conta
oauth_signin_tab=Vincular a uma conta existente
oauth_signin_title=Inicie a sessão para autorizar a vinculação à conta
oauth_signin_submit=Vincular conta
-oauth.signin.error=Ocorreu um erro durante o processamento do pedido de autorização. Se este erro persistir, contacte o administrador.
+oauth.signin.error.general=Ocorreu um erro durante o processamento do pedido de autorização: %s: Se este erro persistir, contacte o administrador.
oauth.signin.error.access_denied=O pedido de autorização foi negado.
oauth.signin.error.temporarily_unavailable=A autorização falhou porque o servidor de autenticação está temporariamente indisponível. Tente mais tarde.
oauth_callback_unable_auto_reg=O registo automático está habilitado, mas o fornecedor OAuth2 %[1]s sinalizou campos em falta: %[2]s, por isso não foi possível criar uma conta automaticamente. Crie ou vincule uma conta ou contacte o administrador do sítio.
@@ -495,7 +512,7 @@ register_notify.text_3=Se esta conta foi criada para si, <a href="%s">defina a s
reset_password=Recupere a sua conta
reset_password.title=%s, você pediu para recuperar a sua conta
-reset_password.text=Por favor clique na seguinte ligação para recuperar a sua conta em <b>%s</b>:
+reset_password.text=Para recuperar a sua conta, clique na ligação seguinte (válida por <b>%s</b>):
register_success=Inscrição bem sucedida
@@ -717,8 +734,8 @@ public_profile=Perfil público
biography_placeholder=Conte-nos um pouco sobre si! (Pode usar Markdown)
location_placeholder=Partilhe a sua localização aproximada com outros
profile_desc=Controle como o seu perfil é apresentado aos outros utilizadores. O seu endereço de email principal será usado para notificações, recuperação de senha e operações Git baseadas na web.
-password_username_disabled=Não tem permissão para alterar os nomes de utilizador deles/delas. Entre em contacto com o administrador para saber mais detalhes.
-password_full_name_disabled=Não tem permissão para alterar o nome completo deles/delas. Entre em contacto com o administrador para saber mais detalhes.
+password_username_disabled=Não tem permissão para alterar o seu nome de utilizador. Entre em contacto com o administrador para saber mais detalhes.
+password_full_name_disabled=Não tem permissão para alterar o seu nome completo. Entre em contacto com o administrador para saber mais detalhes.
full_name=Nome completo
website=Sítio web
location=Localização
@@ -909,11 +926,13 @@ delete_token_success=O código foi eliminado. Aplicações que o usavam deixaram
repo_and_org_access=Acesso aos repositórios e às organizações
permissions_public_only=Apenas público
permissions_access_all=Tudo (público, privado e limitado)
-select_permissions=Escolher permissões
permission_not_set=Não definido
permission_no_access=Sem acesso
permission_read=Lidas
permission_write=Leitura e escrita
+permission_anonymous_read=Leitura anónima
+permission_everyone_read=Leitura pública
+permission_everyone_write=Escrita pública
access_token_desc=As permissões dos códigos escolhidos limitam a autorização apenas às rotas da <a %s>API</a> correspondentes. Leia a <a %s>documentação</a> para obter mais informação.
at_least_one_permission=Tem que escolher pelo menos uma permissão para criar um código
permissions_list=Permissões:
@@ -1017,6 +1036,8 @@ owner=Proprietário(a)
owner_helper=Algumas organizações podem não aparecer na lista suspensa devido a um limite máximo de contagem de repositórios.
repo_name=Nome do repositório
repo_name_profile_public_hint=.profile é um repositório especial que pode usar para adicionar README.md ao seu perfil público da organização, visível para qualquer pessoa. Certifique-se que é público e inicialize-o com um README na pasta do perfil para começar.
+repo_name_profile_private_hint=.profile-private é um repositório especial que pode usar para adicionar um README.md ao seu perfil de membro da organização, visível apenas para membros da organização. Certifique-se que é privado e inicialize-o com um README na pasta de perfil para começar.
+repo_name_helper=Bons nomes de repositórios usam palavras-chave curtas, memorizáveis e únicas. Um repositório chamado ".profile" ou ".profile-private" pode ser usado para adicionar um README.md ao perfil do utilizador ou da organização.
repo_size=Tamanho do repositório
template=Modelo
template_select=Escolha um modelo.
@@ -1049,7 +1070,7 @@ generate_from=Gerar a partir de
repo_desc=Descrição
repo_desc_helper=Insira uma descrição curta (opcional)
repo_no_desc=Descrição não fornecida
-repo_lang=Idiomas
+repo_lang=Linguagens
repo_gitignore_helper=Escolher modelos .gitignore.
repo_gitignore_helper_desc=Escolha os ficheiros que não são para rastrear, a partir de uma lista de modelos de linguagens comuns. Serão incluídos no ficheiro .gitignore, logo à partida, artefactos típicos gerados pelas ferramentas de construção de cada uma das linguagens.
issue_labels=Rótulos para as questões
@@ -1113,9 +1134,7 @@ blame.ignore_revs=Ignorando as revisões em <a href="%s">.git-blame-ignore-revs<
blame.ignore_revs.failed=Falhou ao ignorar as revisões em <a href="%s">.git-blame-ignore-revs</a>.
user_search_tooltip=Mostra um máximo de 30 utilizadores
-tree_path_not_found_commit=A localização %[1]s não existe no cometimento %[2]s
-tree_path_not_found_branch=A localização %[1]s não existe no ramo %[2]s
-tree_path_not_found_tag=A localização %[1]s não existe na etiqueta %[2]s
+tree_path_not_found=A localização %[1]s não existe em %[2]s
transfer.accept=Aceitar transferência
transfer.accept_desc=`Transferir para "%s"`
@@ -1126,6 +1145,7 @@ transfer.no_permission_to_reject=Você não tem permissão para rejeitar esta tr
desc.private=Privado
desc.public=Público
+desc.public_access=Acesso público
desc.template=Modelo
desc.internal=Interno
desc.archived=Arquivado
@@ -1209,6 +1229,7 @@ migrate.migrating_issues=Migrando questões
migrate.migrating_pulls=Migrando pedidos de integração
migrate.cancel_migrating_title=Cancelar migração
migrate.cancel_migrating_confirm=Quer cancelar esta migração?
+migration_status=Estado da migração
mirror_from=réplica de
forked_from=derivado de
@@ -1233,6 +1254,7 @@ create_new_repo_command=Criando um novo repositório na linha de comandos
push_exist_repo=Enviando, pela linha de comandos, um repositório existente
empty_message=Este repositório não contém qualquer conteúdo.
broken_message=Os dados Git subjacentes a este repositório não podem ser lidos. Contacte o administrador desta instância ou elimine este repositório.
+no_branch=Este repositório não tem quaisquer ramos.
code=Código
code.desc=Aceder ao código fonte, ficheiros, cometimentos e ramos.
@@ -1252,6 +1274,7 @@ labels=Rótulos
org_labels_desc=Rótulos ao nível da organização que podem ser usados em <strong>todos os repositórios</strong> desta organização
org_labels_desc_manage=gerir
+milestone=Etapa
milestones=Etapas
commits=Cometimentos
commit=Cometimento
@@ -1285,7 +1308,6 @@ file_copy_permalink=Copiar ligação permanente
view_git_blame=Ver Git Blame
video_not_supported_in_browser=O seu navegador não suporta a etiqueta 'video' do HTML5.
audio_not_supported_in_browser=O seu navegador não suporta a etiqueta 'audio' do HTML5.
-stored_lfs=Armazenado com Git LFS
symbolic_link=Ligação simbólica
executable_file=Ficheiro executável
vendored=Externo
@@ -1311,7 +1333,9 @@ editor.upload_file=Carregar ficheiro
editor.edit_file=Editar ficheiro
editor.preview_changes=Pré-visualizar modificações
editor.cannot_edit_lfs_files=Ficheiros LFS não podem ser editados na interface web.
+editor.cannot_edit_too_large_file=O ficheiro é demasiado grande para ser editado.
editor.cannot_edit_non_text_files=Ficheiros binários não podem ser editados na interface da web.
+editor.file_not_editable_hint=Mas ainda pode renomeá-lo ou movê-lo.
editor.edit_this_file=Editar ficheiro
editor.this_file_locked=Ficheiro bloqueado
editor.must_be_on_a_branch=Tem que estar num ramo para fazer ou propor modificações neste ficheiro.
@@ -1331,7 +1355,7 @@ editor.update=Modificar %s
editor.delete=Eliminar %s
editor.patch=Aplicar remendo (patch)
editor.patching=Remendando (patching):
-editor.fail_to_apply_patch=`Não foi possível aplicar o remendo (patch) "%s"`
+editor.fail_to_apply_patch=Não foi possível aplicar o remendo (patch)
editor.new_patch=Novo remendo (patch)
editor.commit_message_desc=Adicionar uma descrição alargada opcional…
editor.signoff_desc=Adicionar "Assinado-por" seguido do autor do cometimento no fim da mensagem do registo de cometimentos.
@@ -1344,13 +1368,14 @@ editor.new_branch_name_desc=Nome do novo ramo…
editor.cancel=Cancelar
editor.filename_cannot_be_empty=O nome do ficheiro não pode estar em branco.
editor.filename_is_invalid=O nome do ficheiro é inválido: "%s".
+editor.commit_email=Email do cometimento
+editor.invalid_commit_email=O email do comentimento é inválido.
editor.branch_does_not_exist=O ramo "%s" não existe neste repositório.
editor.branch_already_exists=O ramo "%s" já existe neste repositório.
editor.directory_is_a_file=O nome da pasta "%s" já é usado como um nome de ficheiro neste repositório.
editor.file_is_a_symlink=`"%s" é uma ligação simbólica. Ligações simbólicas não podem ser editadas no editor web`
editor.filename_is_a_directory=O nome de ficheiro "%s" já está a ser usado como um nome de pasta neste repositório.
-editor.file_editing_no_longer_exists=O ficheiro que está a ser editado, "%s", já não existe neste repositório.
-editor.file_deleting_no_longer_exists=O ficheiro que está a ser eliminado, "%s", já não existe neste repositório.
+editor.file_modifying_no_longer_exists=O ficheiro que está a ser modificado, "%s", já não existe neste repositório.
editor.file_changed_while_editing=O conteúdo do ficheiro mudou desde que começou a editar. <a target="_blank" rel="noopener noreferrer" href="%s">Clique aqui</a> para ver as modificações ou clique em <strong>Cometer novamente</strong> para escrever por cima.
editor.file_already_exists=Já existe um ficheiro com o nome "%s" neste repositório.
editor.commit_id_not_matching=O ID do cometimento não corresponde ao ID de quando começou a editar. Faça o cometimento para um ramo de remendo (patch) e depois faça a integração.
@@ -1358,8 +1383,6 @@ editor.push_out_of_date=O envio parece estar obsoleto.
editor.commit_empty_file_header=Cometer um ficheiro vazio
editor.commit_empty_file_text=O ficheiro que está prestes a cometer está vazio. Quer continuar?
editor.no_changes_to_show=Não existem modificações para mostrar.
-editor.fail_to_update_file=Falhou ao modificar/criar o ficheiro "%s".
-editor.fail_to_update_file_summary=Mensagem de erro:
editor.push_rejected_no_message=A modificação foi rejeitada pelo servidor sem qualquer mensagem. Verifique os Automatismos do Git.
editor.push_rejected=A modificação foi rejeitada pelo servidor. Verifique os Automatismos do Git.
editor.push_rejected_summary=Mensagem completa de rejeição:
@@ -1373,6 +1396,15 @@ editor.user_no_push_to_branch=O utilizador não pode enviar para o ramo
editor.require_signed_commit=O ramo requer um cometimento assinado
editor.cherry_pick=Escolher a dedo %s para:
editor.revert=Reverter %s para:
+editor.failed_to_commit=Falhou ao cometer as modificações.
+editor.failed_to_commit_summary=Mensagem de erro:
+
+editor.fork_create=Faça uma derivação do repositório para propor modificações
+editor.fork_create_description=Não pode editar este repositório. Ao invés disso, crie uma derivação, faça as modificações nessa derivação e crie um pedido de integração.
+editor.fork_edit_description=Não pode editar este repositório. As modificações irão ser escritas na sua derivação <b>%s</b>, para que possa criar um pedido de integração.
+editor.fork_not_editable=Fez uma derivação deste repositório, mas a sua derivação não é editável.
+editor.fork_failed_to_push_branch=Falhou ao enviar o ramo %s para o seu repositório.
+editor.fork_branch_exists=O ramo "%s" já existe na sua derivação, escolha outro nome para o ramo.
commits.desc=Navegar pelo histórico de modificações no código fonte.
commits.commits=Cometimentos
@@ -1392,6 +1424,7 @@ commits.signed_by_untrusted_user_unmatched=Assinado por um utilizador não fiáv
commits.gpg_key_id=ID da chave GPG
commits.ssh_key_fingerprint=Identificação digital da chave SSH
commits.view_path=Vista neste ponto do histórico
+commits.view_file_diff=Ver as modificações deste ficheiro neste cometimento
commit.operations=Operações
commit.revert=Reverter
@@ -1452,6 +1485,8 @@ issues.filter_milestones=Filtrar etapa
issues.filter_projects=Filtrar planeamento
issues.filter_labels=Filtrar rótulo
issues.filter_reviewers=Filtrar revisor
+issues.filter_no_results=Sem resultados
+issues.filter_no_results_placeholder=Tente ajustar os seus filtros de pesquisa.
issues.new=Questão nova
issues.new.title_empty=O título não pode estar vazio
issues.new.labels=Rótulos
@@ -1498,7 +1533,7 @@ issues.remove_labels=removeu os rótulos %s %s
issues.add_remove_labels=adicionou o(s) rótulo(s) %s e removeu %s %s
issues.add_milestone_at=`adicionou esta questão à etapa <b>%s</b> %s`
issues.add_project_at=`adicionou esta questão ao planeamento <b>%s</b> %s`
-issues.move_to_column_of_project=`isto foi movido para %s dentro de %s em %s`
+issues.move_to_column_of_project=`moveu isto para %s em %s %s`
issues.change_milestone_at=`modificou a etapa de <b>%s</b> para <b>%s</b> %s`
issues.change_project_at=`modificou o planeamento de <b>%s</b> para <b>%s</b> %s`
issues.remove_milestone_at=`removeu esta questão da etapa <b>%s</b> %s`
@@ -1527,13 +1562,14 @@ issues.filter_project=Planeamento
issues.filter_project_all=Todos os planeamentos
issues.filter_project_none=Nenhum planeamento
issues.filter_assignee=Encarregado
-issues.filter_assginee_no_select=Todos os encarregados
-issues.filter_assginee_no_assignee=Sem encarregado
+issues.filter_assignee_no_assignee=Não atribuída
+issues.filter_assignee_any_assignee=Atribuída a alguém
issues.filter_poster=Autor(a)
issues.filter_user_placeholder=Procurar utilizadores
issues.filter_user_no_select=Todos os utilizadores
issues.filter_type=Tipo
issues.filter_type.all_issues=Todas as questões
+issues.filter_type.all_pull_requests=Todos os pedidos de integração
issues.filter_type.assigned_to_you=Atribuídas a si
issues.filter_type.created_by_you=Criadas por si
issues.filter_type.mentioning_you=Mencionando a si
@@ -1625,12 +1661,15 @@ issues.save=Guardar
issues.label_title=Nome do rótulo
issues.label_description=Descrição do rótulo
issues.label_color=Cor do rótulo
+issues.label_color_invalid=Cor inválida
issues.label_exclusive=Exclusivo
issues.label_archive=Arquivar rótulo
issues.label_archived_filter=Mostrar rótulos arquivados
issues.label_archive_tooltip=Os rótulos arquivados são, por norma, excluídos das sugestões ao pesquisar por rótulo.
issues.label_exclusive_desc=Nomeie o rótulo <code>âmbito/item</code> para torná-lo mutuamente exclusivo com outros rótulos do <code>âmbito/</code>.
issues.label_exclusive_warning=Quaisquer rótulos com âmbito que estejam em conflito irão ser removidos ao editar os rótulos de uma questão ou de um pedido de integração.
+issues.label_exclusive_order=Ordenação
+issues.label_exclusive_order_tooltip=Rótulos exclusivos no mesmo âmbito serão ordenados de acordo com esta ordem numérica.
issues.label_count=%d rótulos
issues.label_open_issues=%d questões abertas
issues.label_edit=Editar
@@ -1648,13 +1687,12 @@ issues.attachment.open_tab=`Clique para ver "%s" num separador novo`
issues.attachment.download=`Clique para descarregar "%s"`
issues.subscribe=Subscrever
issues.unsubscribe=Anular subscrição
-issues.unpin_issue=Desafixar questão
+issues.unpin=Desafixar
issues.max_pinned=Já não pode fixar mais questões
issues.pin_comment=fixou isto %s
issues.unpin_comment=desafixou isto %s
issues.lock=Bloquear diálogo
issues.unlock=Desbloquear diálogo
-issues.lock.unknown_reason=Não é possível bloquear uma questão com um motivo desconhecido.
issues.lock_duplicate=Uma questão não pode ser bloqueada duas vezes.
issues.unlock_error=Não é possível desbloquear uma questão que não está bloqueada.
issues.lock_with_reason=bloqueou o diálogo como sendo <strong>%s</strong> e restringiu-o aos colaboradores %s
@@ -1683,16 +1721,20 @@ issues.timetracker_timer_manually_add=Adicionar tempo
issues.time_estimate_set=Definir tempo estimado
issues.time_estimate_display=Estimativa: %s
-issues.change_time_estimate_at=alterou a estimativa de tempo para <b>%s</b> %s
+issues.change_time_estimate_at=alterou a estimativa de tempo para <b>%[1]s</b> %[2]s
issues.remove_time_estimate_at=removeu a estimativa de tempo %s
issues.time_estimate_invalid=O formato da estimativa de tempo é inválido
issues.start_tracking_history=começou a trabalhar %s
issues.tracker_auto_close=O cronómetro será parado automaticamente quando esta questão for fechada
+issues.stopwatch_already_stopped=O cronómetro para esta questão já está parado
+issues.stopwatch_already_created=O cronómetro para esta questão já existe
issues.tracking_already_started=`Você já iniciou a contagem de tempo <a href="%s">noutra questão</a>!`
-issues.stop_tracking_history=trabalhou durante <b>%s</b> %s
+issues.stop_tracking=Parar cronómetro
+issues.stop_tracking_history=trabalhou durante <b>%[1]s</b> %[2]s
+issues.cancel_tracking=Descartar
issues.cancel_tracking_history=`cancelou a contagem de tempo %s`
issues.del_time=Eliminar este registo de tempo
-issues.add_time_history=adicionou <b>%s</b> de tempo gasto %s
+issues.add_time_history=adicionou <b>%[1]s</b> de tempo gasto %[2]s
issues.del_time_history=`eliminou o tempo gasto nesta questão %s`
issues.add_time_manually=Adicionar tempo manualmente
issues.add_time_hours=Horas
@@ -1852,7 +1894,7 @@ pulls.add_prefix=Adicione o prefixo <strong>%s</strong>
pulls.remove_prefix=Remover o prefixo <strong>%s</strong>
pulls.data_broken=Este pedido de integração está danificado devido à falta de informação da derivação.
pulls.files_conflicted=Este pedido de integração contém modificações que entram em conflito com o ramo de destino.
-pulls.is_checking=Está em andamento uma verificação de conflitos na integração. Tente novamente daqui a alguns momentos.
+pulls.is_checking=Verificando se existem conflitos na integração...
pulls.is_ancestor=Este ramo já está incluído no ramo de destino. Não há nada a integrar.
pulls.is_empty=As modificações feitas neste ramo já existem no ramo de destino. Este cometimento ficará vazio.
pulls.required_status_check_failed=Algumas das verificações obrigatórias não foram bem sucedidas.
@@ -1922,12 +1964,12 @@ pulls.outdated_with_base_branch=Este ramo é obsoleto em relação ao ramo base
pulls.close=Encerrar pedido de integração
pulls.closed_at=`fechou este pedido de integração <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at=`reabriu este pedido de integração <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-pulls.cmd_instruction_hint=`Ver <a class="show-instruction">instruções para a linha de comandos</a>.`
+pulls.cmd_instruction_hint=Ver instruções para a linha de comandos
pulls.cmd_instruction_checkout_title=Checkout
pulls.cmd_instruction_checkout_desc=A partir do seu repositório, crie um novo ramo e teste nele as modificações.
pulls.cmd_instruction_merge_title=Integrar
pulls.cmd_instruction_merge_desc=Integrar as modificações e enviar para o Gitea.
-pulls.cmd_instruction_merge_warning=Aviso: Esta operação não pode executar pedidos de integração porque "auto-identificar integração manual" não estava habilitado
+pulls.cmd_instruction_merge_warning=Aviso: Esta operação não pode executar pedidos de integração porque a opção "auto-identificar integração manual" não está habilitada.
pulls.clear_merge_message=Apagar mensagem de integração
pulls.clear_merge_message_hint=Apagar a mensagem de integração apenas remove o conteúdo da mensagem de cometimento e mantém os rodapés do git, tais como "Co-Autorado-Por …".
@@ -1951,6 +1993,7 @@ pulls.upstream_diverging_prompt_behind_1=Este ramo está %[1]d cometimento atrá
pulls.upstream_diverging_prompt_behind_n=Este ramo está %[1]d cometimentos atrás de %[2]s
pulls.upstream_diverging_prompt_base_newer=O ramo base %s tem novas modificações
pulls.upstream_diverging_merge=Sincronizar derivação
+pulls.upstream_diverging_merge_confirm=Gostaria de integrar "%[1]s" em "%[2]s"?
pull.deleted_branch=(eliminado):%s
pull.agit_documentation=Rever a documentação sobre o AGit
@@ -2110,12 +2153,19 @@ contributors.contribution_type.deletions=Eliminações
settings=Configurações
settings.desc=Configurações é onde pode gerir as configurações do repositório
settings.options=Repositório
+settings.public_access=Acesso público
+settings.public_access_desc=Configurar as permissões de acesso público do visitante para substituir os valores predefinidos deste repositório.
+settings.public_access.docs.not_set=Não definido: nenhuma permissão extra de acesso público. As permissões do visitante seguem a visibilidade e as permissões de membro do repositório.
+settings.public_access.docs.anonymous_read=Leitura anónima: utilizadores sem sessão iniciada podem consultar a unidade.
+settings.public_access.docs.everyone_read=Leitura pública: todos os utilizadores com sessão iniciada podem aceder à unidade com permissão de leitura. Permissão de leitura das unidades de questões / pedidos de integração também significa que os utilizadores podem criar novas questões / pedidos de integração.
+settings.public_access.docs.everyone_write=Escrita pública: Todos os utilizadores com sessão iniciada têm permissão de escrita na unidade. Apenas a unidade Wiki suporta esta permissão.
settings.collaboration=Colaboradores
settings.collaboration.admin=Administrador
settings.collaboration.write=Escrita
settings.collaboration.read=Leitura
settings.collaboration.owner=Proprietário(a)
settings.collaboration.undefined=Não definido
+settings.collaboration.per_unit=Permissões da Unidade
settings.hooks=Automatismos web
settings.githooks=Automatismos do Git
settings.basic_settings=Configurações básicas
@@ -2156,7 +2206,6 @@ settings.advanced_settings=Configurações avançadas
settings.wiki_desc=Habilitar wiki do repositório
settings.use_internal_wiki=Usar o wiki integrado
settings.default_wiki_branch_name=Nome do ramo predefinido do wiki
-settings.default_wiki_everyone_access=Permissão de acesso predefinida para utilizadores registados:
settings.failed_to_change_default_wiki_branch=Falhou ao mudar o nome do ramo predefinido do wiki.
settings.use_external_wiki=Usar um wiki externo
settings.external_wiki_url=URL do wiki externo
@@ -2285,8 +2334,6 @@ settings.hooks_desc=Os automatismos web fazem pedidos HTTP POST automaticamente
settings.webhook_deletion=Remover automatismo web
settings.webhook_deletion_desc=Remover um automatismo web elimina as configurações e o histórico de entrega desse automatismo. Quer continuar?
settings.webhook_deletion_success=O automatismo web foi removido.
-settings.webhook.test_delivery=Entrega de teste
-settings.webhook.test_delivery_desc=Testar este automatismo web com um evento falso.
settings.webhook.test_delivery_desc_disabled=Para testar este automatismo web com um evento falso, habilite-o.
settings.webhook.request=Pedido
settings.webhook.response=Resposta
@@ -2324,6 +2371,8 @@ settings.event_fork=Derivar
settings.event_fork_desc=Feita a derivação do repositório.
settings.event_wiki=Wiki
settings.event_wiki_desc=Página do wiki criada, renomeada, editada ou eliminada.
+settings.event_statuses=Estados
+settings.event_statuses_desc=Estado do cometimento modificado através da API.
settings.event_release=Lançamento
settings.event_release_desc=Lançamento publicado, modificado ou eliminado num repositório.
settings.event_push=Enviar
@@ -2333,7 +2382,7 @@ settings.event_repository=Repositório
settings.event_repository_desc=Repositório criado ou eliminado.
settings.event_header_issue=Eventos da questão
settings.event_issues=Questões
-settings.event_issues_desc=Questão aberta, fechada, reaberta ou editada.
+settings.event_issues_desc=Questão aberta, fechada, reaberta, editada ou eliminada.
settings.event_issue_assign=Questão atribuída
settings.event_issue_assign_desc=Encarregado atribuído ou retirado à questão.
settings.event_issue_label=Questão com rótulo
@@ -2344,7 +2393,7 @@ settings.event_issue_comment=Comentário da questão
settings.event_issue_comment_desc=Comentário da questão criado, editado ou eliminado.
settings.event_header_pull_request=Eventos de pedidos de integração
settings.event_pull_request=Pedido de integração
-settings.event_pull_request_desc=Pedido de integração aberto, fechado, reaberto ou editado.
+settings.event_pull_request_desc=Pedido de integração aberto, fechado, reaberto, editado ou eliminado.
settings.event_pull_request_assign=Encarregado atribuído ao pedido de integração
settings.event_pull_request_assign_desc=Encarregado atribuído ou retirado ao pedido de integração.
settings.event_pull_request_label=Rótulo atribuído ao pedido de integração
@@ -2361,6 +2410,11 @@ settings.event_pull_request_review_request=Solicitada a revisão do pedido de in
settings.event_pull_request_review_request_desc=A revisão do pedido de integração foi solicitada ou a solicitação de revisão foi removida.
settings.event_pull_request_approvals=Aprovações do pedido de integração
settings.event_pull_request_merge=Integração constante no pedido
+settings.event_header_workflow=Eventos da sequência de trabalho
+settings.event_workflow_run=Execução da sequência de trabalho
+settings.event_workflow_run_desc=A execução da sequência de trabalho das operações do Gitea foi colocada em fila, está em espera, em andamento ou concluída.
+settings.event_workflow_job=Trabalhos da sequência de trabalho
+settings.event_workflow_job_desc=O trabalho da sequência de trabalho das operações do Gitea foi colocado em fila, está em espera, em andamento ou concluído.
settings.event_package=Pacote
settings.event_package_desc=Pacote criado ou eliminado num repositório.
settings.branch_filter=Filtro de ramos
@@ -2575,7 +2629,6 @@ diff.commit=cometimento
diff.git-notes=Notas
diff.data_not_available=O conteúdo das diferenças não está disponível
diff.options_button=Opções das diferenças
-diff.show_diff_stats=Mostrar estatísticas
diff.download_patch=Descarregar ficheiro patch
diff.download_diff=Descarregar ficheiro diff
diff.show_split_view=Visualização em 2 colunas
@@ -2624,6 +2677,9 @@ diff.image.overlay=Sobrepor
diff.has_escaped=Esta linha tem caracteres unicode escondidos
diff.show_file_tree=Mostrar árvore de ficheiros
diff.hide_file_tree=Esconder árvore de ficheiros
+diff.submodule_added=Submódulo %[1]s adicionado em %[2]s
+diff.submodule_deleted=Submódulo %[1]s eliminado de %[2]s
+diff.submodule_updated=Submódulo %[1]s modificado: %[2]s
releases.desc=Acompanhe as versões e as descargas do repositório.
release.releases=Lançamentos
@@ -2694,6 +2750,7 @@ branch.restore_success=O ramo "%s" foi restaurado.
branch.restore_failed=Falhou a restauração do ramo "%s".
branch.protected_deletion_failed=O ramo "%s" está protegido, não pode ser eliminado.
branch.default_deletion_failed=O ramo "%s" é o ramo principal, não pode ser eliminado.
+branch.default_branch_not_exist=O ramo predefinido "%s" não existe.
branch.restore=`Restaurar o ramo "%s"`
branch.download=`Descarregar o ramo "%s"`
branch.rename=`Renomear ramo "%s"`
@@ -2708,6 +2765,10 @@ branch.create_branch_operation=Criar ramo
branch.new_branch=Criar um novo ramo
branch.new_branch_from=`Criar um novo ramo a partir do ramo "%s"`
branch.renamed=O ramo %s foi renomeado para %s.
+branch.rename_default_or_protected_branch_error=Só os administradores é que podem renomear o ramo principal ou ramos protegidos.
+branch.rename_protected_branch_failed=Este ramo está protegido por regras de salvaguarda baseadas em padrões glob.
+branch.commits_divergence_from=Divergência nos cometimentos: %[1]d atrás e %[2]d à frente de %[3]s
+branch.commits_no_divergence=Idêntico ao ramo %[1]s
tag.create_tag=Criar etiqueta %s
tag.create_tag_operation=Criar etiqueta
@@ -2721,6 +2782,7 @@ topic.done=Concluído
topic.count_prompt=Não pode escolher mais do que 25 tópicos
topic.format_prompt=Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') ou pontos ('.') e podem ter até 35 caracteres. As letras têm que ser minúsculas.
+find_file.follow_symlink=Seguir esta ligação simbólica para onde ela está apontando
find_file.go_to_file=Ir para o ficheiro
find_file.no_matching=Não foi encontrado qualquer ficheiro correspondente
@@ -2761,6 +2823,7 @@ team_permission_desc=Permissão
team_unit_desc=Permitir acesso às secções do repositório
team_unit_disabled=(desabilitada)
+form.name_been_taken=O nome da organização "%s" já foi tomado.
form.name_reserved=O nome de organização "%s" está reservado.
form.name_pattern_not_allowed=O padrão "%s" não é permitido no nome de uma organização.
form.create_org_not_allowed=Não tem permissão para criar uma organização.
@@ -2782,15 +2845,28 @@ settings.visibility.private_shortname=Privado
settings.update_settings=Modificar configurações
settings.update_setting_success=As configurações da organização foram modificadas.
-settings.change_orgname_prompt=Nota: Mudar o nome da organização também irá mudar o URL da organização e libertar o nome antigo.
-settings.change_orgname_redirect_prompt=O nome antigo, enquanto não for reivindicado, irá reencaminhar para o novo.
+
+settings.rename=Renomear organização
+settings.rename_desc=Mudar o nome da organização também irá mudar o URL da organização e libertar o nome antigo.
+settings.rename_success=A organização %[1]s foi renomeada para %[2]s com sucesso.
+settings.rename_no_change=O nome da organização não foi alterado.
+settings.rename_new_org_name=Novo nome da organização
+settings.rename_failed=A renomeação da organização falhou por causa de um erro interno
+settings.rename_notices_1=Esta operação <strong>NÃO PODERÃ</strong> ser revertida.
+settings.rename_notices_2=O antigo nome, enquanto não for reivindicado, irá reencaminhar para o novo.
+
settings.update_avatar_success=O avatar da organização foi modificado.
settings.delete=Eliminar organização
settings.delete_account=Eliminar esta organização
settings.delete_prompt=A organização será removida permanentemente. Essa operação <strong>NÃO PODERÃ</strong> ser revertida!
+settings.name_confirm=Insira o nome da organização para confirmar:
+settings.delete_notices_1=Esta operação <strong>NÃO PODERÃ</strong> ser revertida.
+settings.delete_notices_2=Esta operação irá eliminar de forma permanente todos os <strong>repositórios</strong> de <strong>%s</strong>, incluindo código-fonte, questões, comentários, dados do wiki e configurações dos colaboradores.
+settings.delete_notices_3=Esta operação irá eliminar de forma permanente todos os <strong>pacotes</strong> de <strong>%s</strong>.
+settings.delete_notices_4=Esta operação irá eliminar de forma permanente todos os <strong>planeamentos</strong> de <strong>%s</strong>.
settings.confirm_delete_account=Confirme a eliminação
-settings.delete_org_title=Eliminar organização
-settings.delete_org_desc=Esta organização será eliminada permanentemente. Quer continuar?
+settings.delete_failed=A eliminação da organização falhou por causa de um erro interno
+settings.delete_successful=A organização <b>%s</b> foi eliminada com sucesso.
settings.hooks_desc=Adicionar automatismos web que serão despoletados para <strong>todos os repositórios</strong> desta organização.
settings.labels_desc=Adicionar rótulos que possam ser usados em questões para <strong>todos os repositórios</strong> desta organização.
@@ -2866,6 +2942,15 @@ view_as_role=Ver como: %s
view_as_public_hint=Está a ver o README como um utilizador público.
view_as_member_hint=Está a ver o README como um membro desta organização.
+worktime=Tempo de trabalho
+worktime.date_range_start=Data do início
+worktime.date_range_end=Data do fim
+worktime.query=Consulta
+worktime.time=Tempo
+worktime.by_repositories=Por repositórios
+worktime.by_milestones=Por etapas
+worktime.by_members=Por membros
+
[admin]
maintenance=Manutenção
dashboard=Painel de controlo
@@ -3236,8 +3321,6 @@ config.ssh_domain=Domínio do servidor SSH
config.ssh_port=Porto
config.ssh_listen_port=Porto de escuta
config.ssh_root_path=Localização base
-config.ssh_key_test_path=Localização do teste das chaves
-config.ssh_keygen_path=Localização do gerador de chaves ('ssh-keygen')
config.ssh_minimum_key_size_check=Verificação de tamanho mínimo da chave
config.ssh_minimum_key_sizes=Tamanhos mínimos da chave
@@ -3360,6 +3443,8 @@ monitor.previous=Execução anterior
monitor.execute_times=Execuções
monitor.process=Processos em execução
monitor.stacktrace=Vestígios da pilha
+monitor.trace=Rastreio
+monitor.performance_logs=Registos de desempenho
monitor.processes_count=%d processos
monitor.download_diagnosis_report=Descarregar relatório de diagnóstico
monitor.desc=Descrição
@@ -3368,7 +3453,6 @@ monitor.execute_time=Tempo de execução
monitor.last_execution_result=Resultado
monitor.process.cancel=Cancelar processo
monitor.process.cancel_desc=Cancelar um processo pode resultar na perda de dados
-monitor.process.cancel_notices=Cancelar: <strong>%s</strong>?
monitor.process.children=Descendentes
monitor.queues=Filas
@@ -3565,7 +3649,8 @@ conda.install=Para instalar o pacote usando o Conda, execute o seguinte comando:
container.details.type=Tipo de imagem
container.details.platform=Plataforma
container.pull=Puxar a imagem usando a linha de comandos:
-container.digest=Resumo:
+container.images=Imagens
+container.digest=Resumo
container.multi_arch=S.O. / Arquit.
container.layers=Camadas de imagem
container.labels=Rótulos
@@ -3668,11 +3753,18 @@ owner.settings.chef.keypair.description=É necessário um par de chaves para aut
secrets=Segredos
description=Os segredos serão transmitidos a certas operações e não poderão ser lidos de outra forma.
none=Ainda não há segredos.
-creation=Adicionar segredo
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Descrição
creation.name_placeholder=Só sublinhados ou alfanuméricos sem distinguir maiúsculas, sem começar com GITEA_ nem GITHUB_
creation.value_placeholder=Insira um conteúdo qualquer. Espaços em branco no início ou no fim serão omitidos.
-creation.success=O segredo "%s" foi adicionado.
-creation.failed=Falhou ao adicionar o segredo.
+creation.description_placeholder=Escreva uma descrição curta (opcional).
+
+save_success=O segredo "%s" foi guardado.
+save_failed=Falhou ao guardar o segredo.
+
+add_secret=Adicionar segredo
+edit_secret=Editar segredo
deletion=Remover segredo
deletion.description=Remover um segredo é permanente e não pode ser revertido. Continuar?
deletion.success=O segredo foi removido.
@@ -3750,6 +3842,11 @@ runs.no_workflows.documentation=Para mais informação sobre o Gitea Actions vej
runs.no_runs=A sequência de trabalho ainda não foi executada.
runs.empty_commit_message=(mensagem de cometimento vazia)
runs.expire_log_message=Os registros foram removidos porque eram muito antigos.
+runs.delete=Eliminar execução da sequência de trabalho
+runs.cancel=Cancelar a execução da sequência de trabalho
+runs.delete.description=Tem a certeza que pretende eliminar permanentemente a execução desta sequência de trabalho? Esta operação não poderá ser desfeita.
+runs.not_done=A execução desta sequência de trabalho ainda não terminou.
+runs.view_workflow_file=Ver ficheiro da sequência de trabalho
workflow.disable=Desabilitar sequência de trabalho
workflow.disable_success=A sequência de trabalho '%s' foi desabilitada com sucesso.
@@ -3761,6 +3858,7 @@ workflow.not_found=A sequência de trabalho '%s' não foi encontrada.
workflow.run_success=A sequência de trabalho '%s' foi executada com sucesso.
workflow.from_ref=Usar sequência de trabalho de
workflow.has_workflow_dispatch=Esta sequência de trabalho tem um despoletador de eventos workflow_dispatch.
+workflow.has_no_workflow_dispatch=A sequência de trabalho '%s' não tem nenhum despoletador de eventos workflow_dispatch.
need_approval_desc=É necessária aprovação para executar sequências de trabalho para a derivação do pedido de integração.
@@ -3788,6 +3886,8 @@ deleted.display_name=Planeamento eliminado
type-1.display_name=Planeamento individual
type-2.display_name=Planeamento do repositório
type-3.display_name=Planeamento da organização
+enter_fullscreen=Ecrã inteiro
+exit_fullscreen=Sair do ecrã inteiro
[git.filemode]
changed_filemode=%[1]s → %[2]s
diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini
index 027a2cb19d..bfaf7143bd 100644
--- a/options/locale/locale_ru-RU.ini
+++ b/options/locale/locale_ru-RU.ini
@@ -52,6 +52,7 @@ webauthn_reload=Обновить
repository=Репозиторий
organization=ОрганизациÑ
mirror=Зеркало
+issue_milestone=Этап
new_repo=Ðовый репозиторий
new_migrate=ÐÐ¾Ð²Ð°Ñ Ð¼Ð¸Ð³Ñ€Ð°Ñ†Ð¸Ñ
new_mirror=Ðовое зеркало
@@ -332,6 +333,7 @@ show_only_public=Показаны только публичные
issues.in_your_repos=Ð’ ваших репозиториÑÑ…
+
[explore]
repos=Репозитории
users=Пользователи
@@ -386,7 +388,6 @@ oauth_signup_submit=ÐŸÐ¾Ð»Ð½Ð°Ñ ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ
oauth_signin_tab=СÑылка на ÑущеÑтвующую учётную запиÑÑŒ
oauth_signin_title=Войдите, чтобы авторизовать ÑвÑзанную учётную запиÑÑŒ
oauth_signin_submit=ПривÑзать учётную запиÑÑŒ
-oauth.signin.error=Произошла ошибка при обработке запроÑа авторизации. ЕÑли Ñта ошибка повторÑетÑÑ, обратитеÑÑŒ к админиÑтратору Ñайта.
oauth.signin.error.access_denied=Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° авторизацию был отклонен.
oauth.signin.error.temporarily_unavailable=Произошла ошибка авторизации, так как Ñервер аутентификации временно недоÑтупен. ПожалуйÑта, повторите попытку позже.
openid_connect_submit=Подключить
@@ -805,7 +806,6 @@ delete_token_success=Токен удалён. ПриложениÑ, иÑполь
repo_and_org_access=ДоÑтуп к репозиторию и организации
permissions_public_only=Только публичные
permissions_access_all=Ð’Ñе (публичные, приватные и ограниченные)
-select_permissions=Выбрать разрешениÑ
permission_no_access=Ðет доÑтупа
permission_read=Прочитанные
permission_write=Чтение и запиÑÑŒ
@@ -978,8 +978,6 @@ delete_preexisting_content=Удалить файлы из %s
delete_preexisting_success=Удалены непринÑтые файлы в %s
blame_prior=Показать авторÑтво предшеÑтвующих изменений
-tree_path_not_found_commit=Путь %[1]s не ÑущеÑтвует в коммите %[2]s
-tree_path_not_found_branch=Путь %[1]s не ÑущеÑтвует в ветке %[2]s
transfer.accept=ПринÑть транÑфер
transfer.reject=ОтказатьÑÑ Ð¾Ñ‚ перемещениÑ
@@ -1102,6 +1100,7 @@ labels=Метки
org_labels_desc=Метки ÑƒÑ€Ð¾Ð²Ð½Ñ Ð¾Ñ€Ð³Ð°Ð½Ð¸Ð·Ð°Ñ†Ð¸Ð¸, которые можно иÑпользовать Ñ <strong>вÑеми репозиториÑми</strong> в Ñтой организации
org_labels_desc_manage=управлÑть
+milestone=Этап
milestones=Этапы
commits=коммитов
commit=коммит
@@ -1128,7 +1127,6 @@ file_copy_permalink=Копировать поÑтоÑнную ÑÑылку
view_git_blame=Показать git blame
video_not_supported_in_browser=Ваш браузер не поддерживает HTML5 'video' Ñ‚Ñг.
audio_not_supported_in_browser=Ваш браузер не поддерживает HTML5 'audio' Ñ‚Ñг.
-stored_lfs=ХранитÑÑ Git LFS
symbolic_link=СимволичеÑÐºÐ°Ñ ÑÑылка
executable_file=ИÑполнÑемый файл
commit_graph=Граф коммитов
@@ -1171,7 +1169,6 @@ editor.update=Обновить %s
editor.delete=Удалить %s
editor.patch=Применить патч
editor.patching=ИÑправление:
-editor.fail_to_apply_patch=Ðевозможно применить патч «%s»
editor.new_patch=Ðовый патч
editor.commit_message_desc=Добавьте необÑзательное раÑширенное опиÑание…
editor.signoff_desc=Добавить трейлер Signed-off-by Ñ Ð°Ð²Ñ‚Ð¾Ñ€Ð¾Ð¼ коммита в конце ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°.
@@ -1189,15 +1186,11 @@ editor.branch_already_exists=Ветка «%s» уже ÑущеÑтвует в Ñ
editor.directory_is_a_file=Ð˜Ð¼Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ð° «%s» уже иÑпользуетÑÑ Ð² качеÑтве имени файла в Ñтом репозитории.
editor.file_is_a_symlink=`«%s» ÑвлÑетÑÑ ÑимволичеÑкой ÑÑылкой. СимволичеÑкие ÑÑылки невозможно отредактировать в веб-редакторе`
editor.filename_is_a_directory=Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° «%s» уже иÑпользуетÑÑ Ð² качеÑтве каталога в Ñтом репозитории.
-editor.file_editing_no_longer_exists=Редактируемый файл «%s» больше не ÑущеÑтвует в Ñтом репозитории.
-editor.file_deleting_no_longer_exists=УдалÑемый файл «%s» больше не ÑущеÑтвует в Ñтом репозитории.
editor.file_changed_while_editing=Содержимое файла изменилоÑÑŒ Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° начала редактированиÑ. <a target="_blank" rel="noopener noreferrer" href="%s">Ðажмите здеÑÑŒ</a>, чтобы увидеть, что было изменено, или <strong>ЗафикÑировать Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñнова</strong>, чтобы заменить их.
editor.file_already_exists=Файл Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ «%s» уже ÑущеÑтвует в репозитории.
editor.commit_empty_file_header=Закоммитить пуÑтой файл
editor.commit_empty_file_text=Файл, который вы ÑобираетеÑÑŒ зафикÑировать, пуÑÑ‚. Продолжить?
editor.no_changes_to_show=Ðет изменений.
-editor.fail_to_update_file=Ðе удалоÑÑŒ обновить/Ñоздать файл «%s».
-editor.fail_to_update_file_summary=Ошибка:
editor.push_rejected_no_message=Изменение отклонено Ñервером без ÑообщениÑ. ПожалуйÑта, проверьте хуки Git.
editor.push_rejected=Изменение отклонено Ñервером. ПожалуйÑта, проверьте хуки Git.
editor.push_rejected_summary=Полное Ñообщение об отклонении:
@@ -1212,6 +1205,7 @@ editor.require_signed_commit=Ветка ожидает подпиÑанный к
editor.cherry_pick=ПеренеÑти Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ %s в:
editor.revert=Откатить %s к:
+
commits.desc=ПроÑмотр иÑтории изменений иÑходного кода.
commits.commits=Коммитов
commits.no_commits=Ðет общих коммитов. «%s» и «%s» имеют Ñовершенно разные иÑтории.
@@ -1356,8 +1350,6 @@ issues.filter_project=Проект
issues.filter_project_all=Ð’Ñе проекты
issues.filter_project_none=Ðет проекта
issues.filter_assignee=Ðазначено
-issues.filter_assginee_no_select=Ð’Ñе назначениÑ
-issues.filter_assginee_no_assignee=Ðет ответÑтвенного
issues.filter_poster=Ðвтор
issues.filter_type=Тип
issues.filter_type.all_issues=Ð’Ñе задачи
@@ -1471,13 +1463,12 @@ issues.attachment.open_tab=`Ðажмите, чтобы увидеть «%s» в
issues.attachment.download=`Ðажмите, чтобы Ñкачать «%s»`
issues.subscribe=ПодпиÑатьÑÑ
issues.unsubscribe=ОтказатьÑÑ Ð¾Ñ‚ подпиÑки
-issues.unpin_issue=Открепить задачу
+issues.unpin=Открепить
issues.max_pinned=ÐÐµÐ»ÑŒÐ·Ñ Ð·Ð°ÐºÑ€ÐµÐ¿Ð¸Ñ‚ÑŒ больше задач
issues.pin_comment=закрепил(а) Ñту задачу %s
issues.unpin_comment=открепил(а) Ñту задачу %s
issues.lock=Ограничить обÑуждение
issues.unlock=СнÑть ограничение
-issues.lock.unknown_reason=Ð”Ð»Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð¾Ð±ÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾ указать причину.
issues.lock_duplicate=ОбÑуждение задачи уже ограничено.
issues.unlock_error=Ðевозможно ÑнÑть неÑущеÑтвующее ограничение обÑуждениÑ.
issues.lock_with_reason=заблокировано как <strong>%s</strong> и ограничено обÑуждение Ð´Ð»Ñ ÑоучаÑтников %s
@@ -1648,7 +1639,6 @@ pulls.add_prefix=Добавить <strong>%s</strong> префикÑ
pulls.remove_prefix=Удалить <strong>%s</strong> префикÑ
pulls.data_broken=Содержимое Ñтого запроÑа было нарушено вÑледÑтвие ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ форка.
pulls.files_conflicted=Этот Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние имеет Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð»Ð¸ÐºÑ‚ÑƒÑŽÑ‰Ð¸Ðµ Ñ Ñ†ÐµÐ»ÐµÐ²Ð¾Ð¹ веткой.
-pulls.is_checking=ПродолжаетÑÑ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ° конфликтов, пожалуйÑта обновите Ñтраницу неÑколько позже.
pulls.is_ancestor=Эта ветка уже включена в целевую ветку. Сливать нечего.
pulls.is_empty=Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸Ð· Ñтой ветки уже еÑть в целевой ветке. Это будет пуÑтой коммит.
pulls.required_status_check_failed=Ðекоторые необходимые проверки не были пройдены.
@@ -1708,7 +1698,6 @@ pulls.outdated_with_base_branch=Эта ветка отÑтает от базов
pulls.close=Закрыть Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние
pulls.closed_at=`закрыл Ñтот Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at=`переоткрыл Ñтот Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-pulls.cmd_instruction_hint=`ПроÑмотреть <a class="show-instruction">инÑтрукции Ð´Ð»Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¾Ð¹ Ñтроки</a>.`
pulls.cmd_instruction_merge_title=Слить
pulls.cmd_instruction_merge_desc=Слить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸ обновить в Gitea.
pulls.clear_merge_message=ОчиÑтить Ñообщение о ÑлиÑнии
@@ -2036,8 +2025,6 @@ settings.hooks_desc=Веб-хуки позволÑÑŽÑ‚ внешним Ñлужб
settings.webhook_deletion=Удалить веб-хук
settings.webhook_deletion_desc=Удаление Ñтого веб-хука приведет к удалению вÑей ÑвÑзанной Ñ Ð½Ð¸Ð¼ информации, Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ Ð¸Ñторию. Хотите продолжить?
settings.webhook_deletion_success=Веб-хук был удалён.
-settings.webhook.test_delivery=Проверить доÑтавку
-settings.webhook.test_delivery_desc=Отправить теÑтовое Ñобытие Ð´Ð»Ñ Ñ‚ÐµÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð½Ð°Ñтройки веб-хука.
settings.webhook.request=ЗапроÑ
settings.webhook.response=Ответ
settings.webhook.headers=Заголовки
@@ -2081,7 +2068,6 @@ settings.event_repository=Репозиторий
settings.event_repository_desc=Репозиторий Ñоздан или удален.
settings.event_header_issue=Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð·Ð°Ð´Ð°Ñ‡Ð¸
settings.event_issues=Задачи
-settings.event_issues_desc=Задача открыта, закрыта, переоткрыта или отредактирована.
settings.event_issue_assign=Ðазначена задача
settings.event_issue_assign_desc=Задача назначена или ÑнÑта Ñ Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ.
settings.event_issue_label=Ярлык задачи
@@ -2092,7 +2078,6 @@ settings.event_issue_comment=Комментарии в задаче
settings.event_issue_comment_desc=Комментарий Ñоздан, изменён или удалён.
settings.event_header_pull_request=Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа на ÑлиÑние
settings.event_pull_request=Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние
-settings.event_pull_request_desc=Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние открыт, закрыт, переоткрыт или отредактирован.
settings.event_pull_request_assign=ЗапроÑа на ÑлиÑние назначен
settings.event_pull_request_assign_desc=Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние назначен или не назначен.
settings.event_pull_request_label=Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние отмечен
@@ -2267,7 +2252,6 @@ diff.commit=Коммит
diff.git-notes=Заметки
diff.data_not_available=Разница недоÑтупна
diff.options_button=Опции Diff
-diff.show_diff_stats=Показать ÑтатиÑтику
diff.download_patch=Скачать Patch файл
diff.download_diff=Скачать Diff файл
diff.show_split_view=Разделённый вид
@@ -2463,15 +2447,13 @@ settings.visibility.private_shortname=Приватный
settings.update_settings=Обновить наÑтройки
settings.update_setting_success=ÐаÑтройки организации обновлены.
-settings.change_orgname_prompt=Обратите внимание: изменение Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ñ Ð¾Ñ€Ð³Ð°Ð½Ð¸Ð·Ð°Ñ†Ð¸Ð¸ также изменит URL вашей организации и оÑвободит Ñтарое имÑ.
-settings.change_orgname_redirect_prompt=Старое Ð¸Ð¼Ñ Ð±ÑƒÐ´ÐµÑ‚ перенаправлено до тех пор, пока оно не будет введено.
+
+
settings.update_avatar_success=Ðватар организации обновлён.
settings.delete=Удалить организацию
settings.delete_account=Удалить Ñту организацию
settings.delete_prompt=Это дейÑтвие <strong>БЕЗВОЗВРÐТÐО</strong> удалит Ñту организацию навÑегда!
settings.confirm_delete_account=Подтвердить удаление
-settings.delete_org_title=Удалить организацию
-settings.delete_org_desc=Эта Ð¾Ñ€Ð³Ð°Ð½Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚ безвозвратно удалена. Продолжить?
settings.hooks_desc=Добавьте веб-хуки, которые будет вызыватьÑÑ Ð´Ð»Ñ <strong>вÑех репозиториев</strong> под Ñтой организации.
settings.labels_desc=Добавьте метки, которые могут быть иÑпользованы в задачах Ð´Ð»Ñ <strong>вÑех репозиториев</strong> Ñтой организации.
@@ -2542,6 +2524,7 @@ teams.invite.by=Приглашен(а) %s
teams.invite.description=Ðажмите на кнопку ниже, чтобы приÑоединитьÑÑ Ðº команде.
+
[admin]
dashboard=Панель
identity_access=Ð˜Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð¸ доÑтуп
@@ -2874,8 +2857,6 @@ config.ssh_domain=Домен SSH Ñервера
config.ssh_port=Порт
config.ssh_listen_port=ПроÑлушиваемый порт
config.ssh_root_path=Корневой путь
-config.ssh_key_test_path=Путь к теÑтовому ключу
-config.ssh_keygen_path=Путь к генератору ключей ('ssh-keygen')
config.ssh_minimum_key_size_check=Минимальный размер ключа проверки
config.ssh_minimum_key_sizes=Минимальные размеры ключа
@@ -3178,7 +3159,6 @@ conda.install=Чтобы уÑтановить пакет Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Conda
container.details.type=Тип образа
container.details.platform=Платформа
container.pull=Загрузите образ из командной Ñтроки:
-container.digest=Отпечаток:
container.multi_arch=ОС / архитектура
container.layers=Слои образа
container.labels=Метки
@@ -3276,11 +3256,13 @@ owner.settings.chef.keypair=Создать пару ключей
secrets=Секреты
description=Секреты будут передаватьÑÑ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð½Ñ‹Ð¼ дейÑтвиÑм и не могут быть прочитаны иначе.
none=Секретов пока нет.
-creation=Добавить Ñекрет
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=ОпиÑание
creation.name_placeholder=региÑтр не важен, только алфавитно-цифровые Ñимволы и подчёркиваниÑ, не может начинатьÑÑ Ñ GITEA_ или GITHUB_
creation.value_placeholder=Введите любое Ñодержимое. Пробельные Ñимволы в начале и конце будут опущены.
-creation.success=Секрет «%s» добавлен.
-creation.failed=Ðе удалоÑÑŒ добавить Ñекрет.
+
+
deletion=Удалить Ñекрет
deletion.description=Удаление Ñекрета необратимо, его Ð½ÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ. Продолжить?
deletion.success=Секрет удалён.
diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini
index 167ecaf24a..6a15dae3f2 100644
--- a/options/locale/locale_si-LK.ini
+++ b/options/locale/locale_si-LK.ini
@@ -246,6 +246,7 @@ show_only_public=à¶´à·Šâ€à¶»à·ƒà·’ද්ධ පමණක් පෙන්වයà
issues.in_your_repos=ඔබගේ à¶šà·à·‚්ඨවල
+
[explore]
repos=à¶šà·à·‚්ඨ
users=පරිà·à·“ලකයින්
@@ -873,7 +874,6 @@ file_too_large=ගොනුව පෙන්වීමට තරම් විà·à·
file_copy_permalink=à¶´à·’à¶§à¶´à¶­à·Š මà·à¶¸à¶½à·’න්ක්
video_not_supported_in_browser=ඔබගේ බ්රව්සරය HTML5 'වීඩියà·' à¶§à·à¶œà¶º සඳහ෠සහය නොදක්වයි.
audio_not_supported_in_browser=ඔබගේ බ්රව්සරය HTML5 'à·à·Šà¶»à·€à·Šà¶º' à¶§à·à¶œà¶º සඳහ෠සහය නොදක්වයි.
-stored_lfs=Git LFS සමඟ ගබඩà·
symbolic_link=සංකේතà·à¶­à·Šà¶¸à¶š සබà·à¶³à·’ය
commit_graph=ප්රස්තà·à¶»à¶º à¶šà·à¶´
commit_graph.select=à·à·à¶›à· à¶­à·à¶»à¶±à·Šà¶±
@@ -917,13 +917,13 @@ editor.file_changed_while_editing=ඔබ සංස්කරණය කිරීà¶
editor.commit_empty_file_header=හිස් ගොනුවක් à¶šà·à¶´ කරන්න
editor.commit_empty_file_text=ඔබ à¶šà·à¶´ කිරීමට යන ගොනුව හිස් ය. ඉදිරියට?
editor.no_changes_to_show=පෙන්වීමට කිසිදු වෙනසක් à¶±à·à¶­.
-editor.fail_to_update_file_summary=දà·à·‚ පණිවිඩය:
editor.push_rejected_summary=පූර්ණ ප්රතික්ෂේප පණිවිඩය:
editor.add_subdir=ඩිරෙක්ටරියක් එක් කරන්න…
editor.no_commit_to_branch=à·à·à¶›à·à·€à¶§ කෙලින්ම à¶šà·à¶´à·€à·’ය නොහà·à¶šà·’ නිසà·:
editor.user_no_push_to_branch=පරිà·à·“ලකයà·à¶§ à·à·à¶›à·à·€à¶§ තල්ලු à¶šà·… නොහà·à¶š
editor.require_signed_commit=à·à·à¶›à·à·€à¶§ අත්සන් à¶šà·… à¶šà·à¶´à·€à·“මක් à¶…à·€à·à·Šà¶º වේ
+
commits.desc=මූලà·à·à·Šà¶» à¶šà·šà¶­ වෙනස් කිරීමේ ඉතිහà·à·ƒà¶º පිරික්සන්න.
commits.commits=විවරයන්
commits.nothing_to_compare=මෙම à·à·à¶›à· සමà·à¶± වේ.
@@ -1024,8 +1024,6 @@ issues.filter_label_no_select=සියලු ලේබල
issues.filter_milestone=සන්ධිස්ථà·à¶±à¶º
issues.filter_project_none=ව්â€à¶ºà·à¶´à·˜à¶­à·’ à¶±à·à¶­
issues.filter_assignee=අස්ගිනී
-issues.filter_assginee_no_select=සියලුම ඇග්රි
-issues.filter_assginee_no_assignee=කිසිදු අස්වà·à¶¯à·Šà¶¯à·”මක්
issues.filter_type=වර්ගය
issues.filter_type.all_issues=සියලු à¶œà·à¶§à·…à·”
issues.filter_type.assigned_to_you=ඔබට පවර෠ඇත
@@ -1112,7 +1110,6 @@ issues.subscribe=දà·à¶ºà¶š වන්න
issues.unsubscribe=දà·à¶ºà¶š වන්න
issues.lock=ලොක් සංවà·à¶¯à¶º
issues.unlock=සංවà·à¶¯à¶º අගුළු ඇරීමට
-issues.lock.unknown_reason=නොදන්න෠හේතුවක් සමඟ à¶œà·à¶§à·…ුවක් අගුලු දà·à¶¸à·’ය නොහà·à¶š.
issues.lock_duplicate=à¶´à·Šà¶»à·à·Šà¶±à¶ºà¶šà·Š දෙවරක් අගුලු දà·à¶¸à·’ය නොහà·à¶š.
issues.unlock_error=අගුලු දම෠නà·à¶­à·’ à¶¶à·€ à¶´à·Šà¶»à·à·Šà¶±à¶ºà¶šà·Š අන්ලොක් කරන්න à¶¶à·à·„à·.
issues.lock_with_reason=<strong>%s</strong> ලෙස අගුළු දම෠ඇති à¶…à¶­à¶» සහයà·à¶œà·“à¶­à·à¶šà¶»à·”වන්ට සීමිත සංවà·à¶¯à¶ºà¶šà·Š %s
@@ -1252,7 +1249,6 @@ pulls.add_prefix=<strong>%s</strong> උපසර්ගය à¶‘à¶šà¶­à·” à¶šà¶»à¶
pulls.remove_prefix=<strong>%s</strong> උපසර්ගය ඉවත් කරන්න
pulls.data_broken=අතුරුදහන් වූ දෙබලක තොරතුරු හේතුවෙන් මෙම අදින්න ඉල්ලීම à¶šà·à¶©à·“ ඇත.
pulls.files_conflicted=මෙම අදින්න ඉල්ලීම ඉලක්කගත à·à·à¶›à·à·€ සමග එකිනෙකට වෙනස් වෙනස්කම් ඇත.
-pulls.is_checking=à¶œà·à¶§à·”ම් පරීක්ෂ෠කිරීම à¶’à¶šà·à¶¶à¶¯à·Šà¶° කිරීම ක්රියà·à¶­à·Šà¶¸à¶š වෙමින් පවතී. සුළු මොහොතකින් à¶±à·à·€à¶­ à¶‹à¶­à·Šà·ƒà·à·„ කරන්න.
pulls.required_status_check_failed=සමහර à¶…à·€à·à·Šà¶º චෙක්පත් à·ƒà·à¶»à·Šà¶®à¶š නොවීය.
pulls.required_status_check_missing=සමහර à¶…à·€à·à·Šà¶º චෙක්පත් අස්ථà·à¶±à¶œà¶­ වී ඇත.
pulls.required_status_check_administrator=පරිපà·à¶½à¶šà¶ºà·™à¶šà·” ලෙස, ඔබ තවමත් මෙම අදින්න ඉල්ලීම à¶’à¶šà·à¶¶à¶¯à·Šà¶° à¶šà·… à·„à·à¶šà·’ය.
@@ -1559,8 +1555,6 @@ settings.hooks_desc=ඇතà·à¶¸à·Š Gitea සිදුවීම් අවුලà
settings.webhook_deletion=වෙබ්හූක් ඉවත් කරන්න
settings.webhook_deletion_desc=වෙබ්කොක්කක් ඉවත් කිරීම à¶‘à·„à·’ à·ƒà·à¶šà·ƒà·”ම් සහ බෙදà·à·„à·à¶»à·“මේ ඉතිහà·à·ƒà¶º මක෠දමයි. දිගටම?
settings.webhook_deletion_success=වෙබ්කොක්කෙන් ඉවත් කර ඇත.
-settings.webhook.test_delivery=ටෙස්ට් à·ƒà·à¶´à¶ºà·”ම්
-settings.webhook.test_delivery_desc=ව්යà·à¶¢ සිදුවීමකින් මෙම වෙබ්කොක්කෙන් පරීක්ෂ෠කරන්න.
settings.webhook.request=ඉල්ලීම
settings.webhook.response=à¶´à·Šâ€à¶»à¶­à·’à¶ à·à¶»à¶º
settings.webhook.headers=à·à·“ර්ෂ
@@ -1600,7 +1594,6 @@ settings.event_repository=à¶šà·à·‚්ඨය
settings.event_repository_desc=ගබඩà·à·€ නිර්මà·à¶«à¶º කරන ලද හ෠මක෠දමන ලදි.
settings.event_header_issue=නිකුත් කිරීමේ සිදුවීම්
settings.event_issues=à¶œà·à¶§à·…à·”
-settings.event_issues_desc=නිකුත් කිරීම විවෘත කිරීම, වස෠දà·à¶¸à·“ම, à¶±à·à·€à¶­ විවෘත කිරීම හ෠සංස්කරණය කිරීම.
settings.event_issue_assign=පවර෠ඇති නිකුතුව
settings.event_issue_assign_desc=පවර෠ඇති හ෠පවර෠නොමà·à¶­à·’ නිකුත් කිරීම.
settings.event_issue_label=ලේබල් නිකුත්
@@ -1611,7 +1604,6 @@ settings.event_issue_comment=නිකුතුව
settings.event_issue_comment_desc=නිකුත් අදහස් නිර්මà·à¶«à¶º, සංස්කරණය, හ෠මකà·.
settings.event_header_pull_request=ඉල්ලීම් සිදුවීම් අදින්න
settings.event_pull_request=ඉල්ලීම අදින්න
-settings.event_pull_request_desc=අදින්න ඉල්ලීම විවෘත, වසà·, à¶±à·à·€à¶­ විවෘත, හ෠සංස්කරණය.
settings.event_pull_request_assign=පවර෠ඉල්ලීම අදින්න
settings.event_pull_request_assign_desc=පවර෠ඇති හ෠පවර෠නොමà·à¶­à·’ ඉල්ලීම අදින්න.
settings.event_pull_request_label=ලේබල් ඉල්ලීම අදින්න
@@ -1738,7 +1730,6 @@ diff.commit=à¶šà·à¶´
diff.git-notes=සටහන්
diff.data_not_available=Diff අන්තර්ගත ලබà·à¶œà¶­ නොහà·à¶š
diff.options_button=විවිධ විකල්ප
-diff.show_diff_stats=සංඛ්යà·à¶± පෙන්වන්න
diff.download_patch=à¶´à·à¶ à·Š ගොනුව à¶¶à·à¶œà¶­
diff.download_diff=à¶¶à·à¶œà¶­ Dff ගොනුව
diff.show_split_view=භේදය දà·à¶šà·Šà¶¸
@@ -1887,14 +1878,13 @@ settings.visibility.private_shortname=පෞද්ගලික
settings.update_settings=à·ƒà·à¶šà·ƒà·”ම් යà·à·€à¶­à·Šà¶šà·à¶½ කරන්න
settings.update_setting_success=සංවිධà·à¶±à¶ºà·š à·ƒà·à¶šà·ƒà·”ම් යà·à·€à¶­à·Šà¶šà·à¶½ à¶šà¶» ඇත.
-settings.change_orgname_redirect_prompt=à¶´à·à¶»à¶«à·’ නම ඉල්ල෠සිටින තුරු à¶±à·à·€à¶­ හරව෠යවයි.
+
+
settings.update_avatar_success=සංවිධà·à¶±à¶ºà·š අවතà·à¶»à¶º යà·à·€à¶­à·Šà¶šà·à¶½à·“à¶± à¶šà¶» ඇත.
settings.delete=සංවිධà·à¶±à¶º මකන්න
settings.delete_account=මෙම සංවිධà·à¶±à¶º මකන්න
settings.delete_prompt=සංවිධà·à¶±à¶º ස්ථිරවම ඉවත් කරනු à¶½à·à¶¶à·š. මෙම <strong></strong> à¶…à·„à·à·ƒà·’ à¶šà·… නොහà·à¶š!
settings.confirm_delete_account=මකà·à¶¯à·à¶¸à·“ම තහවුරු කරන්න
-settings.delete_org_title=සංවිධà·à¶±à¶º මකන්න
-settings.delete_org_desc=මෙම සංවිධà·à¶±à¶º ස්ථිරවම මක෠දමනු ඇත. දිගටම?
settings.hooks_desc=මෙම සංවිධà·à¶±à¶º යටතේ <strong>සියලුම ගබඩà·à·€à¶±à·Š</strong> සඳහ෠මුලපුරනු ලබන වෙබ් කොකු à¶‘à¶šà¶­à·” කරන්න.
settings.labels_desc=මෙම සංවිධà·à¶±à¶º යටතේ <strong>සියලුම ගබඩà·à·€à¶½à¶¯à·“</strong> සඳහ෠ගà·à¶§à·…à·” සඳහ෠භà·à·€à·’à¶­à· à¶šà·… à·„à·à¶šà·’ ලේබල් à¶‘à¶šà¶­à·” කරන්න.
@@ -1955,6 +1945,7 @@ teams.all_repositories_write_permission_desc=මෙම à¶šà¶«à·Šà¶©à·à¶ºà¶¸ à¶´
teams.all_repositories_admin_permission_desc=මෙම à¶šà¶«à·Šà¶©à·à¶ºà¶¸ ප්රදà·à¶±à¶º කරයි <strong>පරිපà·à¶½à¶š</strong> වෙත ප්රවේà·à¶º <strong>සියලු ගබඩà·à·€à¶±à·Šà¶§</strong>: à·ƒà·à¶¸à·à¶¢à·’කයින්ට කියවීමට, තල්ලු කිරීමට සහ ගබඩà·à·€à¶±à·Šà¶§ සහයà·à¶œà·“කයින් à¶‘à¶šà¶­à·” කිරීමට.
+
[admin]
dashboard=උපකරණ පුවරුව
users=පරිà·à·“ලක ගිණුම්
@@ -2241,8 +2232,6 @@ config.ssh_domain=SSH සේවà·à¶¯à·à¶ºà¶šà¶º වසම්
config.ssh_port=වරà·à¶º
config.ssh_listen_port=සවන් වරà·à¶º
config.ssh_root_path=මූල මà·à¶»à·Šà¶œà¶º
-config.ssh_key_test_path=ප්රධà·à¶± ටෙස්ට් මà·à¶»à·Šà¶œà¶º
-config.ssh_keygen_path=Keygen ('ssh-keygen') මà·à¶»à·Šà¶œà¶º
config.ssh_minimum_key_size_check=අවම à¶šà·“ ප්රමà·à¶«à¶º පරීක්ෂà·
config.ssh_minimum_key_sizes=අවම යතුරෙහි à¶´à·Šâ€à¶»à¶¸à·à¶«
@@ -2454,6 +2443,11 @@ owner.settings.cleanuprules.enabled=සබල කර ඇත
[secrets]
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=සවිස්තරය
+
+
+
[actions]
diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini
index 43b190098f..61afb9546f 100644
--- a/options/locale/locale_sk-SK.ini
+++ b/options/locale/locale_sk-SK.ini
@@ -53,6 +53,7 @@ webauthn_reload=Znovu naÄítaÅ¥
repository=Repozitár
organization=Organizácia
mirror=Zrkadlo
+issue_milestone=Míľnik
new_repo=Nový repozitár
new_migrate=Nová migrácia
new_mirror=Nové zrkadlo
@@ -327,6 +328,7 @@ show_only_public=Zobrazuje sa iba verejné
issues.in_your_repos=Vo vašich repozitároch
+
[explore]
repos=Repozitáre
users=Používatelia
@@ -375,7 +377,6 @@ oauth_signup_submit=DokonÄiÅ¥ úÄet
oauth_signin_tab=PrepojiÅ¥ s existujúcim úÄtom
oauth_signin_title=Prihláste sa na overenie prepojeného úÄtu
oauth_signin_submit=PrepojiÅ¥ úÄet
-oauth.signin.error=Vyskytla sa chyba poÄas spracovania vaÅ¡ej autorizaÄnej žiadosti. Ak chyba pretrváva, kontaktujte, prosím, správcu.
oauth.signin.error.access_denied=Žiadosť o autorizáciu bola zamietnutá.
oauth.signin.error.temporarily_unavailable=Autorizácia zlyhala, pretože overovací server je doÄasne nedostupný. Skúste to prosím neskôr.
openid_connect_submit=Pripojiť
@@ -967,6 +968,7 @@ labels=Štítky
org_labels_desc=Štítky na úrovni organizácie, ktoré možno použiť so <strong>všetkými repozitármi</strong> v rámci tejto organizácie
org_labels_desc_manage=spravovať
+milestone=Míľnik
milestones=Míľniky
commits=Commitov
release=Vydanie
@@ -979,7 +981,6 @@ file_copy_permalink=Kopírovať trvalý odkaz
view_git_blame=Zobraziť Git Blame
video_not_supported_in_browser=Váš prehliadaÄ nepodporuje HTML5 tag 'video'.
audio_not_supported_in_browser=Váš prehliadaÄ nepodporuje HTML5 tag 'audio'.
-stored_lfs=Uložené pomocou Git LFS
symbolic_link=Symbolický odkaz
commit_graph=Graf commitov
line=riadok
@@ -1006,6 +1007,7 @@ editor.commit_empty_file_text=Súbor, ktorý sa chystáte odoslať, je prázdny.
editor.no_commit_to_branch=Nedá sa odoslať priamo do vetvy, pretože:
editor.require_signed_commit=Vetva vyžaduje podpísaný commit
+
commits.commits=Commity
commits.search_all=Všetky vetvy
commits.author=Autor
@@ -1068,6 +1070,7 @@ issues.dismiss_review=Zamietnuť revíziu
issues.dismiss_review_warning=Naozaj chcete zrušiť túto revíziu?
issues.cancel=Zrušiť
issues.save=Uložiť
+issues.unpin=Odopnúť
@@ -1162,7 +1165,6 @@ settings.hooks_desc=Webhooky automaticky odosielajú požiadavky HTTP POST na se
settings.webhook_deletion=Odstrániť webhook
settings.webhook_deletion_desc=Odstránením webhooku sa vymažú jeho nastavenia a história doruÄovania. ÄŽalej?
settings.webhook_deletion_success=Webhook bol odstránený.
-settings.webhook.test_delivery_desc=Otestujte tento webhook pomocou testovacej udalosti.
settings.webhook.replay.description=Zopakujte tento webhook.
settings.add_webhook_desc=Gitea odoÅ¡le požiadavky <code>POST</code> so Å¡pecifikovaným typom obsahu na cieľovú adresu URL. PreÄítajte si viac v <a target="_blank" rel="noopener noreferrer" href="%s">sprievodcovi webhookmi</a>.
settings.event_header_repository=Udalosti repozitára
@@ -1217,6 +1219,8 @@ lower_repositories=repozitáre
settings.visibility.private=Súkromná ​​(viditeľné iba pre Älenov organizácie)
settings.visibility.private_shortname=Súkromný
+
+
settings.hooks_desc=Pridajte webhooky, ktoré sa spustia nad <strong>všetkými repozitármi</strong> v rámci tejto organizácie.
@@ -1235,6 +1239,7 @@ teams.all_repositories_write_permission_desc=Tomuto tímu je pridelený prístup
teams.all_repositories_admin_permission_desc=Tomuto tímu je pridelený <strong>Admin</strong> prístup ku <strong>vÅ¡etkým repozitárom</strong>: Älenovia môžu prezeraÅ¥, nahrávaÅ¥ do repozitárov a pridávaÅ¥ do nich spolupracovníkov.
+
[admin]
repositories=Repozitáre
hooks=Webhooky
@@ -1318,6 +1323,10 @@ owner.settings.cleanuprules.enabled=Povolené
[secrets]
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+
+
+
[actions]
diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini
index 0315ebe9a1..da4b1ba6fd 100644
--- a/options/locale/locale_sv-SE.ini
+++ b/options/locale/locale_sv-SE.ini
@@ -233,6 +233,7 @@ show_only_public=Visar endast publika
issues.in_your_repos=I dina utvecklingskataloger
+
[explore]
repos=Utvecklingskataloger
users=Användare
@@ -738,7 +739,6 @@ file_too_large=Filen är för stor för att visas.
video_not_supported_in_browser=Din webbläsare stödjer ej HTML5-taggen 'video'.
audio_not_supported_in_browser=Din webbläsare stöder inte taggen 'audio' i HTML5.
-stored_lfs=Sparad med Git LFS
symbolic_link=Symbolisk länk
commit_graph=Commit-Graf
commit_graph.monochrome=Mono
@@ -777,12 +777,12 @@ editor.file_changed_while_editing=Filens innehåll har ändrats sedan du påbör
editor.commit_empty_file_header=Committa en tom fil
editor.commit_empty_file_text=Filen du vill committa är tom. Vill du fortsätta?
editor.no_changes_to_show=Det finns inga ändringar att visa.
-editor.fail_to_update_file_summary=Felmeddelande:
editor.add_subdir=Lägga till en katalog…
editor.no_commit_to_branch=Det gick inte att committa direkt till branchen för:
editor.user_no_push_to_branch=Användaren kan inte pusha till branchen
editor.require_signed_commit=Branchen kräver en signerad commit
+
commits.desc=Bläddra i källkodens förändringshistorik.
commits.commits=Incheckningar
commits.search_all=Alla brancher
@@ -872,8 +872,6 @@ issues.filter_label_no_select=Alla etiketter
issues.filter_milestone=Milsten
issues.filter_project_none=Inget projekt
issues.filter_assignee=Förvärvare
-issues.filter_assginee_no_select=Alla tilldelade
-issues.filter_assginee_no_assignee=Ingen tilldelad
issues.filter_type=Typ
issues.filter_type.all_issues=Alla ärenden
issues.filter_type.assigned_to_you=Tilldelad dig
@@ -956,7 +954,6 @@ issues.subscribe=Prenumerera
issues.unsubscribe=Avsluta prenumerationen
issues.lock=LÃ¥s konversation
issues.unlock=LÃ¥s upp konversation
-issues.lock.unknown_reason=Kan inte låsa ärende utan angiven anledning.
issues.lock_duplicate=Ett ärende kan inte låsas två gånger.
issues.unlock_error=Kan inte låsa upp ett olåst ärende.
issues.lock_with_reason=låst som <strong>%s</strong> och begränsad konversation till medarbetare %s
@@ -1073,7 +1070,6 @@ pulls.is_closed=Pull-förfrågan har stängts.
pulls.title_wip_desc=`<a href="#">Börja titeln med <strong>%s</strong></a> för att förhindra att pull-förfrågan sammanfogas av misstag`
pulls.data_broken=Pull-requesten är trasig pågrund av oexisterande information on forken.
pulls.files_conflicted=Den här pull-förfrågan ha ändringar som är i konflikt med mål-branchen.
-pulls.is_checking=Merge-konfliktkontroll pågår. Försök igen senare.
pulls.required_status_check_failed=Vissa tvingande kontroller lyckades inte.
pulls.required_status_check_missing=Vissa tvingande kontroller saknas.
pulls.required_status_check_administrator=Som administratör kan du fortfarande merga den här pull requesten.
@@ -1296,8 +1292,6 @@ settings.hooks_desc=Webhooks gör automatiskt ett HTTP POST anrop mot en server
settings.webhook_deletion=Ta bort Webhook
settings.webhook_deletion_desc=Borttagning utav en webhook tar även bort dess inställningar och leveranshistorik. Vill du fortsätta?
settings.webhook_deletion_success=Webhooken har blivit borttagen.
-settings.webhook.test_delivery=Testa Leverans
-settings.webhook.test_delivery_desc=Testa webhooken genom ett testevent.
settings.webhook.request=Begäran
settings.webhook.response=Svar
settings.webhook.headers=Huvuden
@@ -1528,13 +1522,13 @@ settings.visibility.private=Privat (synlig endast för organisationens medlemmar
settings.visibility.private_shortname=Privat
settings.update_settings=Uppdatera inställningar
+
+
settings.update_avatar_success=Organisationens avatar har uppdateras.
settings.delete=Tag bort organisation
settings.delete_account=Tag bort denna organisation
settings.delete_prompt=Organisationen kommer tas bort permanent, och det går <strong>INTE</strong> att ångra detta!
settings.confirm_delete_account=Bekräfta borttagning
-settings.delete_org_title=Ta bort organisation
-settings.delete_org_desc=Denna organisation kommer tas bort permanent. Vill du fortsätta?
settings.hooks_desc=Lägg till webbhook som triggas för <strong>alla utvecklingskataloger</strong> under denna organisationen.
settings.labels_desc=Lägg till etiketter som kan användas till ärenden för <strong>alla utvecklingskataloger</strong> under denna organisation.
@@ -1592,6 +1586,7 @@ teams.all_repositories_write_permission_desc=Detta team beviljar <strong>Skriv</
teams.all_repositories_admin_permission_desc=Detta team beviljar <strong>Admin</strong>-rättigheter till <strong>alla utvecklingskataloger</strong>: medlemmar kan läsa från, pusha till och lägga till kollaboratörer för utvecklingskatalogerna.
+
[admin]
dashboard=Instrumentpanel
users=Användarkonto
@@ -1794,8 +1789,6 @@ config.ssh_start_builtin_server=Använd inbyggd Server
config.ssh_port=Port
config.ssh_listen_port=Lyssningsport
config.ssh_root_path=Rotsökväg
-config.ssh_key_test_path=Testsökväg för nyckel
-config.ssh_keygen_path=Sökväg för nyckelgenerator ('ssh-keygen')
config.ssh_minimum_key_size_check=Kontroll av minsta tillåtna nyckelstorlek
config.ssh_minimum_key_sizes=Minsta tillåtna nyckelstorlek
@@ -1988,6 +1981,11 @@ owner.settings.cleanuprules.enabled=Aktiv
[secrets]
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Beskrivning
+
+
+
[actions]
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index fb30ab3b67..25773f43c7 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -54,6 +54,7 @@ webauthn_reload=Yeniden yükle
repository=Depo
organization=Organizasyon
mirror=Yansı
+issue_milestone=Dönüm noktası
new_repo=Yeni Depo
new_migrate=Yeni Göç
new_mirror=Yeni Yansı
@@ -78,7 +79,7 @@ forks=Çatallar
activities=Etkinlikler
pull_requests=Değişiklik İstekleri
issues=Konular
-milestones=Kilometre Taşları
+milestones=Dönüm noktaları
ok=Tamam
cancel=İptal
@@ -112,9 +113,11 @@ copy_type_unsupported=Bu dosya türü kopyalanamaz
write=Yaz
preview=Önizleme
loading=Yükleniyor…
+files=Dosyalar
error=Hata
error404=Ulaşmaya çalıştığınız sayfa <strong>mevcut değil</strong> veya <strong>görüntüleme yetkiniz yok</strong>.
+error503=Sunucu isteğinizi gerçekleştiremedi. Lütfen daha sonra tekrar deneyin.
go_back=Geri Git
invalid_data=Geçersiz veri: %v
@@ -127,6 +130,7 @@ pin=Sabitle
unpin=Sabitlemeyi kaldır
artifacts=Yapılar
+expired=Süresi doldu
confirm_delete_artifact=%s yapısını silmek istediğinizden emin misiniz?
archived=ArÅŸivlenmiÅŸ
@@ -168,6 +172,10 @@ search=Ara...
type_tooltip=Arama türü
fuzzy=Bulanık
fuzzy_tooltip=Arama terimine benzeyen sonuçları da içer
+words=Kelimeler
+words_tooltip=Sadece arama terimi kelimeleriyle eşleşen sonuçları içer
+regexp=Regexp
+regexp_tooltip=Sadece regexp arama terimiyle tamamen eşleşen sonuçları içer
exact=Tam
exact_tooltip=Sadece arama terimiyle tamamen eşleşen sonuçları içer
repo_kind=Depoları ara...
@@ -234,13 +242,17 @@ network_error=Ağ hatası
[startpage]
app_desc=Zahmetsiz, kendi sunucunuzda barındırabileceğiniz Git servisi
install=Kurulumu kolay
+install_desc=Platformunuz için <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com/installation/install-from-binary">ikili dosyayı çalıştırın</a>, <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> ile yükleyin veya <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com/installation/install-from-package">paket</a> olarak edinin.
platform=Farklı platformlarda çalışablir
+platform_desc=Gitea <a target="_blank" rel="noopener noreferrer" href="%s">Go</a> ile derleme yapılabilecek her yerde çalışmaktadır: Windows, macOS, Linux, ARM, vb. Hangisini seviyorsanız onu seçin!
lightweight=Hafif
lightweight_desc=Gitea'nın minimal gereksinimleri çok düşüktür ve ucuz bir Raspberry Pi üzerinde çalışabilmektedir. Makine enerjinizden tasarruf edin!
license=Açık Kaynak
+license_desc=Gidin ve <a target="_blank" rel="noopener noreferrer" href="https://code.gitea.io/gitea">code.gitea.io/gitea</a>'yı edinin! Bu projeyi daha da iyi yapmak için <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea">katkıda bulunarak</a> bize katılın. Katkıda bulunmaktan çekinmeyin!
[install]
install=Kurulum
+installing_desc=Şimdi kuruluyor, lütfen bekleyin...
title=Başlangıç Yapılandırması
docker_helper=Eğer Gitea'yı Docker içerisinde çalıştırıyorsanız, lütfen herhangi bir değişiklik yapmadan önce <a target="_blank" rel="noopener noreferrer" href="%s">belgeleri</a> okuyun.
require_db_desc=Gitea MySQL, PostgreSQL, MSSQL, SQLite3 veya TiDB (MySQL protokolü) gerektirir.
@@ -351,6 +363,7 @@ enable_update_checker=Güncelleme Denetleyicisini Etkinleştir
enable_update_checker_helper=Düzenli olarak gitea.io'ya bağlanarak yeni yayınlanan sürümleri denetler.
env_config_keys=Ortam Yapılandırma
env_config_keys_prompt=Aşağıdaki ortam değişkenleri de yapılandırma dosyanıza eklenecektir:
+config_write_file_prompt=Bu yapılandırma seçenekleri şuraya yazılacak: %s
[home]
nav_menu=Gezinti Menüsü
@@ -379,6 +392,13 @@ show_only_public=Yalnızca açık olanlar gösteriliyor
issues.in_your_repos=Depolarınızda
+guide_title=Etkinlik yok
+guide_desc=Herhangi bir depo veya kullanıcı takip etmiyorsunuz, bu yüzden görüntülenecek bir içerik yok. Aşağıdaki bağlantıları kullanarak ilgi çekici depo ve kullanıcıları keşfedebilirsiniz.
+explore_repos=Depoları keşfet
+explore_users=Kullanıcıları keşfet
+empty_org=Henüz bir organizasyon yok.
+empty_repo=Henüz bir depo yok.
+
[explore]
repos=Depolar
users=Kullanıcılar
@@ -401,6 +421,7 @@ remember_me.compromised=Oturum açma tokeni artık geçerli değil, bu ele geçi
forgot_password_title=Åžifremi unuttum
forgot_password=Åžifrenizi mi unuttunuz?
need_account=Bir hesaba mı ihtiyacın var?
+sign_up_tip=Sistemdeki yönetici ayrıcalıklarına sahip olan ilk hesabı oluşturuyorsunuz. Lütfen kullanıcı adınızı ve parolanızı unutmamaya dikkat edin. Kullanıcı adı veya parolayı unutursanız, hesabı kurtarmak için lütfen Gitea belgelerine bakın.
sign_up_now=Hemen kaydolun.
sign_up_successful=Hesap başarılı bir şekilde oluşturuldu. Hoşgeldiniz!
confirmation_mail_sent_prompt_ex=Yeni bir doğrulama e-postası <b>%s</b> adresine gönderildi. Lütfen kayıt sürecini tamamlamak için %s içinde gelen kutunuzu denetleyin. Eğer kayıt e-posta adresiniz hatalı ise, tekrar oturum açıp değiştirebilirsiniz.
@@ -431,6 +452,7 @@ use_scratch_code=Bir çizgi kodu kullanınız
twofa_scratch_used=Geçici kodunuzu kullandınız. İki aşamalı ayarlar sayfasına yönlendirildiniz, burada aygıt kaydınızı kaldırabilir veya yeni bir geçici kod oluşturabilirsiniz.
twofa_passcode_incorrect=Şifreniz yanlış. Aygıtınızı yanlış yerleştirdiyseniz, oturum açmak için çizgi kodunuzu kullanın.
twofa_scratch_token_incorrect=Çizgi kodunuz doğru değildir.
+twofa_required=Depolara erişmek için iki aşama doğrulama kullanmanız veya tekrar oturum açmayı denemeniz gereklidir.
login_userpass=Oturum Aç
login_openid=Açık Kimlik
oauth_signup_tab=Yeni Hesap OluÅŸtur
@@ -439,7 +461,7 @@ oauth_signup_submit=Hesabı Tamamla
oauth_signin_tab=Mevcut Hesaba BaÄŸla
oauth_signin_title=Bağlantılı Hesabı Yetkilendirmek için Giriş Yapın
oauth_signin_submit=Hesabı Bağla
-oauth.signin.error=Yetkilendirme isteğini işlerken bir hata oluştu. Eğer hata devam ederse lütfen site yöneticisiyle bağlantıya geçin.
+oauth.signin.error.general=Yetkilendirme isteğini işlerken bir hata oluştu: %s. Eğer hata devam ederse lütfen site yöneticisiyle bağlantıya geçin.
oauth.signin.error.access_denied=Yetkilendirme isteÄŸi reddedildi.
oauth.signin.error.temporarily_unavailable=Yetkilendirme sunucusu geçici olarak erişilemez olduğu için yetkilendirme başarısız oldu. Lütfen daha sonra tekrar deneyin.
oauth_callback_unable_auto_reg=Otomatik kayıt etkin ancak OAuth2 Sağlayıcı %[1] eksik sahalar döndürdü: %[2]s, otomatik olarak hesap oluşturulamıyor, lütfen bir hesap oluşturun veya bağlantı verin, veya site yöneticisiyle iletişim kurun.
@@ -456,10 +478,12 @@ authorize_application=Uygulamayı Yetkilendir
authorize_redirect_notice=Bu uygulamayı yetkilendirirseniz %s adresine yönlendirileceksiniz.
authorize_application_created_by=Bu uygulama %s tarafından oluşturuldu.
authorize_application_description=Erişime izin verirseniz, özel depolar ve organizasyonlar da dahil olmak üzere tüm hesap bilgilerinize erişebilir ve yazabilir.
+authorize_application_with_scopes=Kapsamlar: %s
authorize_title=Hesabınıza erişmesi için "%s" yetkilendirilsin mi?
authorization_failed=Yetkilendirme başarısız oldu
authorization_failed_desc=Geçersiz bir istek tespit ettiğimiz için yetkilendirme başarısız oldu. Lütfen izin vermeye çalıştığınız uygulamanın sağlayıcısı ile iletişim kurun.
sspi_auth_failed=SSPI kimlik doğrulaması başarısız oldu
+password_pwned=Seçtiğiniz parola, daha önce herkese açık veri ihlallerinde açığa çıkan bir <a target="_blank" rel="noopener noreferrer" href="%s">çalınan parola listesindedir</a>. Lütfen farklı bir parola ile tekrar deneyin ve başka yerlerde de bu parolayı değiştirmeyi düşünün.
password_pwned_err=HaveIBeenPwned'e yapılan istek tamamlanamadı
last_admin=Son yöneticiyi silemezsiniz. En azından bir yönetici olmalıdır.
signin_passkey=Bir parola anahtarı ile oturum aç
@@ -582,6 +606,8 @@ lang_select_error=Listeden bir dil seçin.
username_been_taken=Bu kullanıcı adı daha önce alınmış.
username_change_not_local_user=Yerel olmayan kullanıcılar kendi kullanıcı adlarını değiştiremezler.
+change_username_disabled=Kullanıcı adı değişikliği devre dışıdır.
+change_full_name_disabled=Tam ad değişikliği devre dışıdır.
username_has_not_been_changed=Kullanıcı adı değişmedi
repo_name_been_taken=Depo adı zaten kullanılıyor.
repository_force_private=Gizliyi Zorla devrede: gizli depolar herkese açık yapılamaz.
@@ -631,6 +657,7 @@ org_still_own_repo=Bu organizasyon hala bir veya daha fazla depoya sahip, önce
org_still_own_packages=Bu organizasyon hala bir veya daha fazla pakete sahip, önce onları silin.
target_branch_not_exist=Hedef dal mevcut deÄŸil.
+target_ref_not_exist=Hedef referans mevcut deÄŸil %s
admin_cannot_delete_self=Yöneticiyken kendinizi silemezsiniz. Lütfen önce yönetici haklarınızı kaldırın.
@@ -697,14 +724,18 @@ applications=Uygulamalar
orgs=Organizasyonları Yönet
repos=Depolar
delete=Hesabı Sil
+twofa=İki Aşamalı Kimlik Doğrulama (TOTP)
account_link=Bağlı Hesaplar
organization=Organizasyonlar
uid=UID
+webauthn=İki-Aşamalı Kimlik Doğrulama (Güvenlik Anahtarları)
public_profile=Herkese Açık Profil
biography_placeholder=Bize kendiniz hakkında birşeyler söyleyin! (Markdown kullanabilirsiniz)
location_placeholder=Yaklaşık konumunuzu başkalarıyla paylaşın
profile_desc=Profilinizin başkalarına nasıl gösterildiğini yönetin. Ana e-posta adresiniz bildirimler, parola kurtarma ve web tabanlı Git işlemleri için kullanılacaktır.
+password_username_disabled=Yerel olmayan kullanıcılara kullanıcı adlarını değiştirme izni verilmemiştir. Daha fazla bilgi edinmek için lütfen site yöneticisi ile iletişime geçiniz.
+password_full_name_disabled=Tam adınızı değiştirme izniniz yoktur. Daha fazla bilgi edinmek için lütfen site yöneticisi ile iletişime geçiniz.
full_name=Ad Soyad
website=Web Sitesi
location=Konum
@@ -754,6 +785,7 @@ uploaded_avatar_not_a_image=Yüklenen dosya bir resim dosyası değil.
uploaded_avatar_is_too_big=Yüklenen dosyanın boyutu (%d KiB), azami boyutu (%d KiB) aşıyor.
update_avatar_success=Profil resminiz deÄŸiÅŸtirildi.
update_user_avatar_success=Kullanıcının avatarı güncellendi.
+cropper_prompt=Kaydetmeden önce resmi düzenleyebilirsiniz. Düzenlenen resim PNG biçiminde kaydedilecektir.
change_password=Parolayı Güncelle
old_password=Mevcut Parola
@@ -796,6 +828,7 @@ add_email_success=Yeni e-posta adresi eklendi.
email_preference_set_success=E-posta tercihi başarıyla ayarlandı.
add_openid_success=Yeni OpenID adresi eklendi.
keep_email_private=E-posta Adresini Gizle
+keep_email_private_popup=Bu, e-posta adresinizi profilde, değişiklik isteği yaptığınızda veya web arayüzünde dosya düzenlediğinizde gizleyecektir. İtilen işlemeler değişmeyecektir.
openid_desc=OpenID, kimlik doğrulama işlemini harici bir sağlayıcıya devretmenize olanak sağlar.
manage_ssh_keys=SSH Anahtarlarını Yönet
@@ -893,11 +926,13 @@ delete_token_success=Jeton silindi. Onu kullanan uygulamalar artık hesabınıza
repo_and_org_access=Depo ve Organizasyon EriÅŸimi
permissions_public_only=Yalnızca herkese açık
permissions_access_all=Tümü (herkese açık, özel ve sınırlı)
-select_permissions=İzinleri seçin
permission_not_set=Ayarlanmadı
permission_no_access=EriÅŸim Yok
permission_read=OkunmuÅŸ
permission_write=Okuma ve Yazma
+permission_anonymous_read=Anonim Okuma
+permission_everyone_read=Herkes Okuyabilir
+permission_everyone_write=Herkes Yazabilir
access_token_desc=Seçili token izinleri, yetkilendirmeyi ilgili <a %s>API</a> yollarıyla sınırlandıracaktır. Daha fazla bilgi için <a %s>belgeleri</a> okuyun.
at_least_one_permission=Bir token oluşturmak için en azından bir izin seçmelisiniz
permissions_list=İzinler:
@@ -925,6 +960,7 @@ oauth2_client_secret_hint=Bu sayfadan ayrıldıktan veya yeniledikten sonra gizl
oauth2_application_edit=Düzenle
oauth2_application_create_description=OAuth2 uygulamaları, üçüncü taraf uygulamanıza bu durumda kullanıcı hesaplarına erişim sağlar.
oauth2_application_remove_description=Bir OAuth2 uygulamasının kaldırılması, bu sunucudaki yetkili kullanıcı hesaplarına erişmesini önler. Devam edilsin mi?
+oauth2_application_locked=Gitea kimi OAuth2 uygulamalarının başlangıçta ön kaydını, yapılandırmada etkinleştirilmişse yapabilir. Beklenmeyen davranışı önlemek için bunlar ne düzenlenmeli ne de kaldırılmalı. Daha fazla bilgi için OAuth2 belgesine bakın.
authorized_oauth2_applications=Yetkili OAuth2 Uygulamaları
authorized_oauth2_applications_description=Kişisel Gitea hesabınıza bu üçüncü parti uygulamalara erişim izni verdiniz. Lütfen artık ihtiyaç duyulmayan uygulamalara erişimi iptal edin.
@@ -933,20 +969,26 @@ revoke_oauth2_grant=Erişimi İptal Et
revoke_oauth2_grant_description=Bu üçüncü taraf uygulamasına erişimin iptal edilmesi bu uygulamanın verilerinize erişmesini önleyecektir. Emin misiniz?
revoke_oauth2_grant_success=Erişim başarıyla kaldırıldı.
+twofa_desc=İki aşamalı kimlik doğrulama, hesabınızın güvenliğini artırır.
twofa_recovery_tip=Aygıtınızı kaybetmeniz durumunda, hesabınıza tekrar erişmek için tek kullanımlık kurtarma anahtarını kullanabileceksiniz.
twofa_is_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde <strong>kaydedilmiş</strong>.
twofa_not_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde kaydedilmemiş.
twofa_disable=İki Aşamalı Doğrulamayı Devre Dışı Bırak
+twofa_scratch_token_regenerate=Geçici Kodu Yeniden Üret
+twofa_scratch_token_regenerated=Geçici kodunuz şimdi %s. Güvenli bir yerde saklayın, tekrar gösterilmeyecektir.
twofa_enroll=İki Faktörlü Kimlik Doğrulamaya Kaydolun
twofa_disable_note=Gerekirse iki faktörlü kimlik doğrulamayı devre dışı bırakabilirsiniz.
twofa_disable_desc=İki faktörlü kimlik doğrulamayı devre dışı bırakmak hesabınızı daha az güvenli hale getirir. Devam edilsin mi?
+regenerate_scratch_token_desc=Geçici kodunuzu kaybettiyseniz veya oturum açmak için kullandıysanız, buradan sıfırlayabilirsiniz.
twofa_disabled=İki faktörlü kimlik doğrulama devre dışı bırakıldı.
scan_this_image=Kim doğrulama uygulamanızla bu görüntüyü tarayın:
or_enter_secret=Veya gizli ÅŸeyi girin: %s
then_enter_passcode=Ve uygulamada gösterilen şifreyi girin:
passcode_invalid=Şifre geçersiz. Tekrar deneyin.
+twofa_enrolled=Hesabınız başarıyla kaydedildi. Tek kullanımlık geçici kodunuz (%s) tekrar gösterilmeyeceği için güvenilir bir yerde saklayın.
twofa_failed_get_secret=Gizlilik elde edilemedi.
+webauthn_desc=Güvenlik anahtarları, şifreleme anahtarlarını içeren donanım aygıtlarıdır. İki aşamalı kimlik doğrulama için kullanılabilirler. Güvenlik anahtarları <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a> standardını desteklemelidir.
webauthn_register_key=Güvenlik Anahtarı Ekle
webauthn_nickname=Takma Ad
webauthn_delete_key=Güvenlik Anahtarını Kaldır
@@ -993,6 +1035,9 @@ new_repo_helper=Bir depo, sürüm geçmişi dahil tüm proje dosyalarını içer
owner=Sahibi
owner_helper=Bazı organizasyonlar, en çok depo sayısı sınırı nedeniyle açılır menüde görünmeyebilir.
repo_name=Depo İsmi
+repo_name_profile_public_hint=.profile herkese açık organizasyonunuzun profiline herkesin görüntüleyebileceği bir README.md dosyası eklemek için kullanabileceğiniz özel bir depodur. Başlamak için herkese açık olduğundan ve profile dizininde README ile başladığınızdan emin olun.
+repo_name_profile_private_hint=.profile-private organizasyonunuzun üye profiline sadece organizasyon üyelerinin görüntüleyebileceği bir README.md eklemek için kullanabileceğiniz özel bir depodur. Başlamak için özel olduğundan ve profil dizininde README ile başladığınızdan emin olun.
+repo_name_helper=İyi depo isimleri kısa, akılda kalıcı ve benzersiz anahtar kelimeler kullanır. “.profile†veya ‘.profile-private’ adlı bir depo, kullanıcı/kuruluş profili için bir README.md eklemek için kullanılabilir.
repo_size=Depo Boyutu
template=Åžablon
template_select=Bir şablon seçin.
@@ -1011,6 +1056,8 @@ fork_to_different_account=Başka bir hesaba çatalla
fork_visibility_helper=Çatallanmış bir deponun görünürlüğü değiştirilemez.
fork_branch=Çatala klonlanacak dal
all_branches=Tüm dallar
+view_all_branches=Tüm dalları görüntüle
+view_all_tags=Tüm etiketleri görüntüle
fork_no_valid_owners=Geçerli bir sahibi olmadığı için bu depo çatallanamaz.
fork.blocked_user=Depo çatallanamıyor, depo sahibi tarafından engellenmişsiniz.
use_template=Bu ÅŸablonu kullan
@@ -1022,6 +1069,8 @@ generate_repo=Depo OluÅŸtur
generate_from=Åžuradan OluÅŸtur
repo_desc=Açıklama
repo_desc_helper=Kısa açıklama girin (isteğe bağlı)
+repo_no_desc=Hiçbir açıklama sağlanmadı
+repo_lang=Dil
repo_gitignore_helper=.gitignore şablonlarını seç.
repo_gitignore_helper_desc=Sık kullanılan diller için bir şablon listesinden hangi dosyaların izlenmeyeceğini seçin. Her dilin oluşturma araçları tarafından oluşturulan tipik yapılar, varsayılan olarak .gitignore dosyasına dahil edilmiştir.
issue_labels=Konu Etiketleri
@@ -1029,6 +1078,7 @@ issue_labels_helper=Bir konu etiket seti seçin.
license=Lisans
license_helper=Bir lisans dosyası seçin.
license_helper_desc=Bir lisans, başkalarının kodunuzla neler yapıp yapamayacağını yönetir. Projeniz için hangisinin doğru olduğundan emin değil misiniz? <a target="_blank" rel="noopener noreferrer" href="%s">Lisans seçme</a> konusuna bakın
+multiple_licenses=Çoklu Lisans
object_format=Nesne Biçimi
object_format_helper=Deponun nesne biçimi. Daha sonra değiştirilemez. SHA1 en uyumlu olandır.
readme=README
@@ -1082,18 +1132,20 @@ delete_preexisting_success=%s içindeki kabul edilmeyen dosyalar silindi
blame_prior=Bu değişiklikten önceki suçu görüntüle
blame.ignore_revs=<a href="%s">.git-blame-ignore-revs</a> dosyasındaki sürümler yok sayılıyor. Bunun yerine normal sorumlu görüntüsü için <a href="%s">buraya tıklayın</a>.
blame.ignore_revs.failed=<a href="%s">.git-blame-ignore-revs</a> dosyasındaki sürümler yok sayılamadı.
+user_search_tooltip=En fazla 30 kullanıcı görüntüler
-tree_path_not_found_commit=%[1] yolu, %[2]s iÅŸlemesinde mevcut deÄŸil
-tree_path_not_found_branch=%[1] yolu, %[2]s dalında mevcut değil
-tree_path_not_found_tag=%[1] yolu, %[2]s etiketinde mevcut deÄŸil
+tree_path_not_found=%[1] yolu, %[2]s deposunda mevcut deÄŸil
transfer.accept=Aktarımı Kabul Et
+transfer.accept_desc=`"%s" tarafına aktar`
transfer.reject=Aktarımı Reddet
+transfer.reject_desc=`"%s" tarafına aktarımı iptal et`
transfer.no_permission_to_accept=Bu aktarımı kabul etme izniniz yok.
transfer.no_permission_to_reject=Bu aktarımı reddetme izniniz yok.
desc.private=Özel
desc.public=Genel
+desc.public_access=Herkese Açık Erişim
desc.template=Åžablon
desc.internal=Dahili
desc.archived=ArÅŸivlenmiÅŸ
@@ -1131,7 +1183,7 @@ migrate_options_lfs_endpoint.description.local=Yerel bir sunucu yolu da destekle
migrate_options_lfs_endpoint.placeholder=Boş bırakılırsa, uç nokta klon URL'sinden türetilecektir
migrate_items=Göç Öğeleri
migrate_items_wiki=Wiki
-migrate_items_milestones=Kilometre Taşları
+migrate_items_milestones=Dönüm noktaları
migrate_items_labels=Etiketler
migrate_items_issues=Konular
migrate_items_pullrequests=Değişiklik İstekleri
@@ -1163,6 +1215,11 @@ migrate.gogs.description=Notabug.org veya diğer Gogs sunucularından veri aktar
migrate.onedev.description=Code.onedev.io ve diğer OneDev sunucularından veri aktar.
migrate.codebase.description=Codebasehq.com sitesinden veri aktar.
migrate.gitbucket.description=GitBucket sunucularından veri aktar.
+migrate.codecommit.description=AWS CodeCommit'ten veri aktar.
+migrate.codecommit.aws_access_key_id=AWS Erişim Anahtarı Kimliği
+migrate.codecommit.aws_secret_access_key=AWS Gizli Erişim Anahtarı
+migrate.codecommit.https_git_credentials_username=HTTPS Git Kimliği Kullanıcı Adı
+migrate.codecommit.https_git_credentials_password=HTTPS Git Kimliği Parolası
migrate.migrating_git=Git Verilerini Taşıma
migrate.migrating_topics=Konuları Taşıma
migrate.migrating_milestones=Kilometre Taşlarını Taşıma
@@ -1172,6 +1229,7 @@ migrate.migrating_issues=Konuları Taşıma
migrate.migrating_pulls=Değişiklik İsteklerini Taşıma
migrate.cancel_migrating_title=Göçü İptal Et
migrate.cancel_migrating_confirm=Bu göçü iptal etmek istiyor musunuz?
+migration_status=Aktarma durumu
mirror_from=şunun yansıması
forked_from=şundan çatallanmış
@@ -1196,6 +1254,7 @@ create_new_repo_command=Komut satırında yeni bir depo oluşturuluyor
push_exist_repo=Komut satırından mevcut bir depo itiliyor
empty_message=Bu depoda herhangi bir içerik yok.
broken_message=Bu deponun altındaki Git verisi okunamıyor. Bu sunucunun yöneticisiyle bağlantıya geçin veya bu depoyu silin.
+no_branch=Bu deponun hiç bir dalı yok.
code=Kod
code.desc=Kaynak koda, dosyalara, iÅŸlemelere ve dallara eriÅŸ.
@@ -1215,6 +1274,7 @@ labels=Etiketler
org_labels_desc=Bu organizasyon altında <strong>tüm depolarla</strong> kullanılabilen organizasyon düzeyinde etiketler
org_labels_desc_manage=yönet
+milestone=Dönüm noktası
milestones=Kilometre Taşları
commits=İşleme
commit=İşle
@@ -1239,6 +1299,7 @@ ambiguous_runes_header=`Bu dosya muğlak Evrensel Kodlu karakter içeriyor`
ambiguous_runes_description=`Bu dosya, başka karakterlerle karıştırılabilecek evrensel kodlu karakter içeriyor. Eğer bunu kasıtlı olarak yaptıysanız bu uyarıyı yok sayabilirsiniz. Gizli karakterleri göstermek için Kaçış Karakterli düğmesine tıklayın.`
invisible_runes_line=`Bu satırda görünmez evrensel kodlu karakter var`
ambiguous_runes_line=`Bu satırda muğlak evrensel kodlu karakter var`
+ambiguous_character=`%[1]c [U+%04[1]X], %[2]c [U+%04[2]X] ile karıştırılabilir`
escape_control_characters=Kaçış Karakterli
unescape_control_characters=Kaçış Karaktersiz
@@ -1246,7 +1307,6 @@ file_copy_permalink=Kalıcı Bağlantıyı Kopyala
view_git_blame=Git Suç Görüntüle
video_not_supported_in_browser=Tarayıcınız HTML5 'video' etiketini desteklemiyor.
audio_not_supported_in_browser=Tarayıcınız HTML5 'audio' etiketini desteklemiyor.
-stored_lfs=Git LFS ile depolandı
symbolic_link=Sembolik Bağlantı
executable_file=Çalıştırılabilir Dosya
vendored=Sağlanmış
@@ -1272,7 +1332,9 @@ editor.upload_file=Dosya Yükle
editor.edit_file=Dosyayı Düzenle
editor.preview_changes=Değişiklikleri Önizle
editor.cannot_edit_lfs_files=LFS dosyaları web arayüzünde düzenlenemez.
+editor.cannot_edit_too_large_file=Bu dosya düzenlenmek için çok büyük.
editor.cannot_edit_non_text_files=Bu tür dosyalar web arayüzünden düzenlenemez.
+editor.file_not_editable_hint=Ama yine de ismini değiştirebilir veya taşıyabilirsiniz.
editor.edit_this_file=Dosyayı Düzenle
editor.this_file_locked=Dosya kilitlendi
editor.must_be_on_a_branch=Bu dosyada değişiklik yapmak veya önermek için bir dalda olmalısınız.
@@ -1292,7 +1354,7 @@ editor.update=%s Güncelle
editor.delete=%s Sil
editor.patch=Yama Uygula
editor.patching=Yamalanıyor:
-editor.fail_to_apply_patch=`"%s" yaması uygulanamıyor`
+editor.fail_to_apply_patch=Yama uygulanamıyor
editor.new_patch=Yeni Yama
editor.commit_message_desc=İsteğe bağlı uzun bir açıklama ekleyin…
editor.signoff_desc=İşleme günlüğü mesajının sonuna işleyen tarafından imzalanan bir fragman ekleyin.
@@ -1305,13 +1367,14 @@ editor.new_branch_name_desc=Yeni dal ismi…
editor.cancel=İptal
editor.filename_cannot_be_empty=Dosya adı boş olamaz.
editor.filename_is_invalid=Dosya adı geçersiz: "%s".
+editor.commit_email=İşleme e-postası
+editor.invalid_commit_email=İşleme e-postası hatalı.
editor.branch_does_not_exist=Bu depoda "%s" dalı yok.
editor.branch_already_exists=Bu depoda "%s" dalı zaten var.
editor.directory_is_a_file=Dizin adı "%s" zaten bu depoda bir dosya adı olarak kullanılmaktadır.
editor.file_is_a_symlink=`"%s" sembolik bir bağlantıdır. Sembolik bağlantılar web düzenleyicisinde düzenlenemez`
editor.filename_is_a_directory=Dosya adı "%s" zaten bu depoda bir dizin adı olarak kullanılmaktadır.
-editor.file_editing_no_longer_exists=Düzenlenmekte olan "%s" dosyası artık bu depoda yer almıyor.
-editor.file_deleting_no_longer_exists=Silinen "%s" dosyası artık bu depoda yer almıyor.
+editor.file_modifying_no_longer_exists=Değiştirilmekte olan "%s" dosyası artık bu depoda yer almıyor.
editor.file_changed_while_editing=Düzenlemeye başladığınızdan beri dosya içeriği değişti. Görmek için <a target="_blank" rel="noopener noreferrer" href="%s">burayı tıklayın</a> veya üzerine yazmak için <strong>değişiklikleri yine de işleyin</strong>.
editor.file_already_exists=Bu depoda "%s" isimli bir dosya zaten var.
editor.commit_id_not_matching=İşleme ID'si, düzenlemeye başladığınız ID ile uyuşmuyor, bir yama dalına işleme yapın ve sonra birleştirin.
@@ -1319,8 +1382,6 @@ editor.push_out_of_date=İtme eskimiş.
editor.commit_empty_file_header=BoÅŸ bir dosya iÅŸle
editor.commit_empty_file_text=İşlemek üzere olduğunuz dosya boş. Devam edilsin mi?
editor.no_changes_to_show=Gösterilecek değişiklik yok.
-editor.fail_to_update_file=`"%s" dosyası güncellenemedi/oluşturulamadı.`
-editor.fail_to_update_file_summary=Hata Mesajı:
editor.push_rejected_no_message=Değişiklik, bir ileti olmadan sunucu tarafından reddedildi. Git Hooks'u kontrol edin.
editor.push_rejected=Değişiklik sunucu tarafından reddedildi. Lütfen Git Hooks'u kontrol edin.
editor.push_rejected_summary=Tam Red Mesajı:
@@ -1334,6 +1395,15 @@ editor.user_no_push_to_branch=Kullanıcı dala gönderemez
editor.require_signed_commit=Dal imzalı bir işleme gerektirir
editor.cherry_pick=%s şunun üzerine cımbızla:
editor.revert=%s şuna geri döndür:
+editor.failed_to_commit=Değişikliklerin işlenmesi başarısız oldu.
+editor.failed_to_commit_summary=Hata Mesajı:
+
+editor.fork_create=Değişiklikler Önermek için Depoyu Çatallayın
+editor.fork_create_description=Bu depoyu doğrudan düzenleyemezsiniz. Bunun yerine bir çatal oluşturabilir, düzenlemeler yapabilir ve bir değişiklik isteği oluşturabilirsiniz.
+editor.fork_edit_description=Bu depoyu doğrudan düzenleyemezsiniz. Değişiklikler <b>%s</b> çatalınıza yazılacak, böylece bir değişiklik isteği oluşturabilirsiniz.
+editor.fork_not_editable=Bu deponun çatalını oluşturdunuz ancak çatalınız düzenlenemez durumda.
+editor.fork_failed_to_push_branch=%s dalını deponuza gönderme başarısız oldu.
+editor.fork_branch_exists=`"%s" dalı çatalınızda zaten mevcut, lütfen yeni bir dal adı seçin.`
commits.desc=Kaynak kodu değişiklik geçmişine göz atın.
commits.commits=İşleme
@@ -1353,6 +1423,7 @@ commits.signed_by_untrusted_user_unmatched=İşleyici ile eşleşmeyen güvenilm
commits.gpg_key_id=GPG Anahtar KimliÄŸi
commits.ssh_key_fingerprint=SSH Anahtar Parmak İzi
commits.view_path=Geçmişte bu noktayı görüntüle
+commits.view_file_diff=Bu dosyanın bu işlemedeki değişikliklerini görüntüle
commit.operations=İşlemler
commit.revert=Geri Al
@@ -1413,6 +1484,8 @@ issues.filter_milestones=Kilometre Taşı Süzgeci
issues.filter_projects=Projeyi Süz
issues.filter_labels=Etiket Süzgeci
issues.filter_reviewers=Gözden Geçiren Süzgeci
+issues.filter_no_results=Sonuç yok
+issues.filter_no_results_placeholder=Arama filtrelerinizi ayarlamayı deneyin.
issues.new=Yeni Konu
issues.new.title_empty=Başlık boş olamaz
issues.new.labels=Etiketler
@@ -1430,6 +1503,7 @@ issues.new.clear_milestone=Kilometre Taşlarını Temizle
issues.new.assignees=Atananlar
issues.new.clear_assignees=Atamaları Temizle
issues.new.no_assignees=Atanan KiÅŸi Yok
+issues.new.no_reviewers=Gözden geçiren yok
issues.new.blocked_user=Konu oluşturulamıyor, depo sahibi tarafından engellenmişsiniz.
issues.edit.already_changed=Konuya yapılan değişiklikler kaydedilemiyor. İçerik başka kullanıcı tarafından değiştirilmiş gözüküyor. Diğerlerinin değişikliklerinin üzerine yazmamak için lütfen sayfayı yenileyin ve tekrar düzenlemeye çalışın
issues.edit.blocked_user=İçerik düzenlenemiyor, gönderen veya depo sahibi tarafından engellenmişsiniz.
@@ -1486,11 +1560,14 @@ issues.filter_project=Proje
issues.filter_project_all=Tüm projeler
issues.filter_project_none=Proje yok
issues.filter_assignee=Atanan
-issues.filter_assginee_no_select=Tüm atananlar
-issues.filter_assginee_no_assignee=Atanan yok
+issues.filter_assignee_no_assignee=Hiç kimseye atanmamış
+issues.filter_assignee_any_assignee=Herhangi bir kimseye atanmış
issues.filter_poster=Yazar
+issues.filter_user_placeholder=Kullanıcıları ara
+issues.filter_user_no_select=Tüm kullanıcılar
issues.filter_type=Tür
issues.filter_type.all_issues=Tüm konular
+issues.filter_type.all_pull_requests=Tüm değişiklik istekleri
issues.filter_type.assigned_to_you=Size atanan
issues.filter_type.created_by_you=Sizin oluşturduklarınız
issues.filter_type.mentioning_you=Sizden bahsedilen
@@ -1582,12 +1659,14 @@ issues.save=Kaydet
issues.label_title=Etiket adı
issues.label_description=Etiket açıklaması
issues.label_color=Etiket rengi
+issues.label_color_invalid=Geçersiz renk
issues.label_exclusive=Özel
issues.label_archive=Etiketi ArÅŸivle
issues.label_archived_filter=Arşivlenmiş etiketleri göster
issues.label_archive_tooltip=Arşivlenmiş etiketler, etiket araması yapılırken varsayılan olarak önerilerin dışında tutuluyor.
issues.label_exclusive_desc=<code>Kapsam/öğe</code> etiketini, diğer <code>kapsam/</code> etiketleriyle ayrışık olacak şekilde adlandırın.
issues.label_exclusive_warning=Çakışan kapsamlı etiketler, bir konu veya değişiklik isteği etiketleri düzenlenirken kaldırılacaktır.
+issues.label_exclusive_order_tooltip=Aynı bağlamdaki özel etiketler bu sayısal sıralamaya göre sıralanacaktır.
issues.label_count=%d etiket
issues.label_open_issues=%d açık konu
issues.label_edit=Düzenle
@@ -1605,13 +1684,12 @@ issues.attachment.open_tab=`Yeni bir sekmede "%s" görmek için tıkla`
issues.attachment.download=`"%s" indirmek için tıkla`
issues.subscribe=Abone Ol
issues.unsubscribe=Abonelikten Çık
-issues.unpin_issue=Konuyu Sabitlemeden Kaldır
+issues.unpin=Sabitlemeyi kaldır
issues.max_pinned=Daha fazla konuyu sabitleyemezsiniz
issues.pin_comment=%s sabitlendi
issues.unpin_comment=%s sabitlenmesi kaldırıldı
issues.lock=Konuşmayı kilitle
issues.unlock=Konuşmanın kilidini aç
-issues.lock.unknown_reason=Sebep belirtmeden konuyu kilitleyemezsiniz.
issues.lock_duplicate=Bir konu iki kez kilitlenemez.
issues.unlock_error=Kilitlenmemiş bir konunun kilidini açamazsınız.
issues.lock_with_reason=<strong>%s</strong> olarak kilitlendi ve katkıcılar için sınırlandırıldı %s
@@ -1633,12 +1711,25 @@ issues.delete.title=Bu konu silinsin mi?
issues.delete.text=Bu konuyu gerçekten silmek istiyor musunuz? (Bu işlem tüm içeriği kalıcı olarak silecektir. Arşivde tutma niyetiniz varsa silmek yerine kapatmayı düşünün)
issues.tracker=Zaman Takibi
-
+issues.timetracker_timer_start=Zamanlayıcıyı başlat
+issues.timetracker_timer_stop=Zamanlayıcıyı durdur
+issues.timetracker_timer_discard=Zamanlayıcıyı kaldır
+issues.timetracker_timer_manually_add=Zaman Ekle
+
+issues.time_estimate_set=Tahmini zaman ayarla
+issues.time_estimate_display=Tahmin: %s
+issues.change_time_estimate_at=zaman tahmini deÄŸiÅŸtirildi: <b>%[1]s</b>%[2]s
+issues.remove_time_estimate_at=zaman tahmini %s kaldırıldı
+issues.time_estimate_invalid=Zaman tahmini biçimi hatalı
issues.tracker_auto_close=Bu konu kapatıldığında zamanlayıcı otomatik olarak durur
issues.tracking_already_started=`<a href="%s">başka bir konuda</a> zaten zaman izleyici başlattınız!`
+issues.stop_tracking=Zamanlayıcıyı Durdur
+issues.stop_tracking_history=<b>%[1]s</b> %[2]s için çalıştı
+issues.cancel_tracking=Yoksay
issues.cancel_tracking_history=`%s zaman takibini iptal etti`
issues.del_time=Bu zaman kaydını sil
issues.del_time_history=`%s harcanan zaman silindi`
+issues.add_time_manually=El ile Zaman Ekle
issues.add_time_hours=Saat
issues.add_time_minutes=Dakika
issues.add_time_sum_to_small=Zaman girilmedi.
@@ -1698,6 +1789,7 @@ issues.dependency.add_error_dep_not_same_repo=Her iki konu da aynı depoda olmal
issues.review.self.approval=Kendi değişiklik isteğinizi onaylayamazsınız.
issues.review.self.rejection=Kendi deÄŸiÅŸiklik isteÄŸinizde deÄŸiÅŸiklik isteyemezsiniz.
issues.review.approve=%s bu değişiklikleri onayladı
+issues.review.comment=%s incelendi
issues.review.dismissed=%s incelemesini %s reddetti
issues.review.dismissed_label=Reddedildi
issues.review.left_comment=bir yorum yaptı
@@ -1722,7 +1814,12 @@ issues.review.hide_resolved=Çözülenleri gizle
issues.review.resolve_conversation=Konuşmayı çöz
issues.review.un_resolve_conversation=Konuşmayı çözme
issues.review.resolved_by=bu konuşmayı çözümlenmiş olarak işaretledi
-issues.review.commented=Yorum Yap
+issues.review.commented=Yorum
+issues.review.official=Onaylandı
+issues.review.requested=İnceleme bekliyor
+issues.review.rejected=DeÄŸiÅŸiklik istendi
+issues.review.stale=Onaydan sonra güncellendi
+issues.review.unofficial=Sayılmamış onay
issues.assignee.error=Beklenmeyen bir hata nedeniyle tüm atananlar eklenmedi.
issues.reference_issue.body=Gövde
issues.content_history.deleted=silindi
@@ -1790,12 +1887,14 @@ pulls.add_prefix=<strong>%s</strong> ön ekini ekle
pulls.remove_prefix=<strong>%s</strong> ön ekini kaldır
pulls.data_broken=Bu değişiklik isteği, çatallama bilgilerinin eksik olması nedeniyle bozuldu.
pulls.files_conflicted=Bu değişiklik isteğinde, hedef dalla çakışan değişiklikler var.
-pulls.is_checking=Birleştirme çakışması denetimi devam ediyor. Birkaç dakika sonra tekrar deneyin.
+pulls.is_checking=Birleştirme çakışması denetleniyor ...
pulls.is_ancestor=Bu dal zaten hedef dalda mevcut. BirleÅŸtirilecek bir ÅŸey yok.
pulls.is_empty=Bu daldaki değişiklikler zaten hedef dalda mevcut. Bu boş bir işleme olacaktır.
pulls.required_status_check_failed=Bazı gerekli denetimler başarılı olmadı.
pulls.required_status_check_missing=Gerekli bazı kontroller eksik.
pulls.required_status_check_administrator=Yönetici olarak, bu değişiklik isteğini yine de birleştirebilirsiniz.
+pulls.blocked_by_approvals=Bu değişiklik isteğinin henüz yeterli onayı yok. %d onay var, %d onay gerekiyor.
+pulls.blocked_by_approvals_whitelisted=Bu değişiklik isteği henüz yeterince onaya sahip değil. İzinli kullanıcılar veya takımlar %d/%d onay verdi.
pulls.blocked_by_rejection=Bu değişiklik isteğinde resmi bir gözden geçiren tarafından istenen değişiklikler var.
pulls.blocked_by_official_review_requests=Bu deÄŸiÅŸiklik isteÄŸinde resmi inceleme istekleri var.
pulls.blocked_by_outdated_branch=Bu değişiklik isteği eskidiği için engellendi.
@@ -1837,7 +1936,9 @@ pulls.unrelated_histories=Birleştirme Başarısız: Birleştirme başlığı ve
pulls.merge_out_of_date=Birleştirme Başarısız: Birleştirme oluşturulurken, taban güncellendi. İpucu: Tekrar deneyin.
pulls.head_out_of_date=Birleştirme Başarısız: Birleştirme oluşturulurken, ana güncellendi. İpucu: Tekrar deneyin.
pulls.has_merged=Başarısız: Değişiklik isteği birleştirildi, yeniden birleştiremez veya hedef dalı değiştiremezsiniz.
+pulls.push_rejected=Gönderme Başarısız Oldu: Gönderme reddedildi. Bu depo için Git İstemcilerini inceleyin.
pulls.push_rejected_summary=Tam Red Mesajı
+pulls.push_rejected_no_message=Gönderme Başarısız Oldu: Gönderme reddedildi, ancak uzak bir mesaj yoktu. Bu depo için Git İstemcilerini inceleyin
pulls.open_unmerged_pull_exists=`Aynı özelliklere sahip bekleyen bir değişiklik isteği (#%d) olduğundan yeniden açma işlemini gerçekleştiremezsiniz.`
pulls.status_checking=Bazı denetlemeler beklemede
pulls.status_checks_success=Tüm denetlemeler başarılı oldu
@@ -1856,7 +1957,6 @@ pulls.outdated_with_base_branch=Bu dal, temel dal ile güncel değil
pulls.close=Değişiklik İsteğini Kapat
pulls.closed_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> değişiklik isteğini kapattı`
pulls.reopened_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> değişiklik isteğini yeniden açtı`
-pulls.cmd_instruction_hint=`<a class="show-instruction">Komut satırı talimatlarını</a> görüntüleyin.`
pulls.cmd_instruction_checkout_title=Çekme
pulls.cmd_instruction_checkout_desc=Proje deponuzdan yeni bir dalı çekin ve değişiklikleri test edin.
pulls.cmd_instruction_merge_title=BirleÅŸtir
@@ -2037,6 +2137,7 @@ contributors.contribution_type.deletions=Silmeler
settings=Ayarlar
settings.desc=Ayarlar, deponun ayarlarını yönetebileceğiniz yerdir
settings.options=Depo
+settings.public_access=Herkese Açık Erişim
settings.collaboration=Katkıcılar
settings.collaboration.admin=Yönetici
settings.collaboration.write=Yazma
@@ -2083,7 +2184,6 @@ settings.advanced_settings=GeliÅŸmiÅŸ Ayarlar
settings.wiki_desc=Depo Wiki'sini EtkinkleÅŸtir
settings.use_internal_wiki=Dahili Wiki Kullan
settings.default_wiki_branch_name=Varsayılan Viki Dal Adı
-settings.default_wiki_everyone_access=Oturum açmış kullanıcılar için Varsayılan Erişim İzinleri:
settings.failed_to_change_default_wiki_branch=Varsayılan viki dalı değiştirilemedi.
settings.use_external_wiki=Harici Wiki Kullan
settings.external_wiki_url=Harici Wiki bağlantısı
@@ -2210,8 +2310,6 @@ settings.hooks_desc=Web istemcileri, belirli Gitea olayları tetiklendiğinde ot
settings.webhook_deletion=Web İsteğini Sil
settings.webhook_deletion_desc=Bir web isteğini kaldırmak, ayarlarını ve teslimat geçmişini siler. Devam edilsin mi?
settings.webhook_deletion_success=Web isteÄŸi silindi.
-settings.webhook.test_delivery=Test Dağıtımı
-settings.webhook.test_delivery_desc=Bu web isteÄŸini sahte bir olayla test edin.
settings.webhook.test_delivery_desc_disabled=Bu web istemcisini sahte bir olayla denemek için etkinleştirin.
settings.webhook.request=İstekler
settings.webhook.response=Cevaplar
@@ -2258,7 +2356,6 @@ settings.event_repository=Depo
settings.event_repository_desc=Depo oluÅŸturuldu veya silindi.
settings.event_header_issue=Konu Olayları
settings.event_issues=Konular
-settings.event_issues_desc=Konu açıldı, kapatıldı, yeniden açıldı veya düzenlendi.
settings.event_issue_assign=Konu Atandı
settings.event_issue_assign_desc=Konu atandı veya atanmadı.
settings.event_issue_label=Konu Etiketlendi
@@ -2269,7 +2366,6 @@ settings.event_issue_comment=Konu Yorumu
settings.event_issue_comment_desc=Konu yorumu eklendiğinde, düzenlendiğinde veya silindiğinde.
settings.event_header_pull_request=Değişiklik İsteği Olayları
settings.event_pull_request=İstek Çek
-settings.event_pull_request_desc=Değişiklik isteği açıldı, kapatıldı, yeniden açıldı veya düzenlendi.
settings.event_pull_request_assign=Değişiklik İsteği Atandı
settings.event_pull_request_assign_desc=Değişiklik isteği atanmış veya atanmamış.
settings.event_pull_request_label=Değişiklik İsteği Etiketlendi
@@ -2289,6 +2385,7 @@ settings.event_pull_request_merge=Değişiklik İsteği Birleştirme
settings.event_package=Paket
settings.event_package_desc=Bir depoda paket oluÅŸturuldu veya silindi.
settings.branch_filter=Dal filtresi
+settings.branch_filter_desc=Gönderme, dal oluşturma ve dal silme olayları için glob deseni olarak belirtilen dal beyaz listesi. Boşsa veya <code>*</code> ise, tüm dallar için olaylar raporlanır. Sözdizimi için <a href="%[1]s">%[2]s</a> belgelerine bakın. Örnekler: <code>master</code>, <code>{master,release*}</code>.
settings.authorization_header=Yetkilendirme Başlığı
settings.authorization_header_desc=Mevcutsa isteklere yetkilendirme başlığı olarak eklenecektir. Örnekler: %s.
settings.active=Etkin
@@ -2471,7 +2568,6 @@ diff.commit=iÅŸleme
diff.git-notes=Notlar
diff.data_not_available=Farklı İçerik Mevut Değil
diff.options_button=Diff Seçenekleri
-diff.show_diff_stats=İstatistikleri Göster
diff.download_patch=Yama Dosyasını İndir
diff.download_diff=Diff Dosyasını İndir
diff.show_split_view=Görünümü Böl
@@ -2676,15 +2772,13 @@ settings.visibility.private_shortname=Özel
settings.update_settings=Ayarları Güncelle
settings.update_setting_success=Organizasyon ayarları güncellendi.
-settings.change_orgname_prompt=Not: Organizasyon adını değiştirmek organizasyonunuzun URL'sini de değiştirecek ve eski ismi serbest bıracaktır.
-settings.change_orgname_redirect_prompt=Eski ad, talep edilene kadar yeniden yönlendirilecektir.
+
+
settings.update_avatar_success=Organizasyonun resmi güncellendi.
settings.delete=Organizasyonu Sil
settings.delete_account=Bu Organizasyonu Sil
settings.delete_prompt=Organizasyon kalıcı olarak kaldırılacaktır. Bu işlem <strong>GERİ ALINAMAZ</strong>!
settings.confirm_delete_account=Silmeyi Onaylıyorum
-settings.delete_org_title=Organizasyonu Sil
-settings.delete_org_desc=Bu organizasyon kalıcı olarak silinecektir. Devam edilsin mi?
settings.hooks_desc=Bu organizasyon altındaki <strong>tüm depolar</strong> için tetiklenecek webhook'lar ekle.
settings.labels_desc=Bu organizasyonun altındaki <strong>tüm depolar</strong> ile ilgili konularda kullanılabilecek etiketler ekleyin.
@@ -2756,6 +2850,7 @@ teams.invite.by=%s tarafından davet edildi
teams.invite.description=Takıma katılmak için aşağıdaki düğmeye tıklayın.
+
[admin]
maintenance=Bakım
dashboard=Pano
@@ -3112,8 +3207,6 @@ config.ssh_domain=SSH Sunucusu Alan Adı
config.ssh_port=Bağlantı Noktası
config.ssh_listen_port=Port'u Dinle
config.ssh_root_path=Kök Yol
-config.ssh_key_test_path=Anahtar Test Yolu
-config.ssh_keygen_path=Keygen ('ssh-keygen') Yolu
config.ssh_minimum_key_size_check=Minimum Anahtar Uzunluğu Kontrolü
config.ssh_minimum_key_sizes=Minimum Anahtar Uzunlukları
@@ -3434,7 +3527,6 @@ conda.install=Conda ile paket kurmak için aşağıdaki komutu çalıştırın:
container.details.type=Görüntü Türü
container.details.platform=Platform
container.pull=Görüntüyü komut satırını kullanarak çekin:
-container.digest=Özet:
container.multi_arch=İşletim Sistemi / Mimari
container.layers=Görüntü Katmanları
container.labels=Etiketler
@@ -3537,11 +3629,13 @@ owner.settings.chef.keypair.description=Chef kütüğünde kimlik doğrulaması
secrets=Gizlilikler
description=Gizlilikler belirli işlemlere aktarılacaktır, bunun dışında okunamaz.
none=Henüz gizlilik yok.
-creation=Gizlilik Ekle
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=Açıklama
creation.name_placeholder=küçük-büyük harfe duyarlı değil, alfanümerik karakterler veya sadece alt tire, GITEA_ veya GITHUB_ ile başlayamaz
creation.value_placeholder=Herhangi bir içerik girin. Baştaki ve sondaki boşluklar ihmal edilecektir.
-creation.success=Gizlilik "%s" eklendi.
-creation.failed=Gizlilik eklenemedi.
+
+
deletion=Gizliliği kaldır
deletion.description=Bir gizliliği kaldırma kalıcıdır ve geri alınamaz. Devam edilsin mi?
deletion.success=Gizlilik kaldırıldı.
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index 2b0e57c8e0..6d7721537b 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -2,7 +2,9 @@ home=Головна
dashboard=Панель управліннÑ
explore=ОглÑд
help=Довідка
+logo=Логотип
sign_in=Увійти
+sign_in_with_provider=Увійдіть за допомогою %s
sign_in_or=або
sign_out=Вийти
sign_up=РеєÑтраціÑ
@@ -15,34 +17,50 @@ template=Шаблон
language=Мова
notifications=СповіщеннÑ
active_stopwatch=Трекер робочого чаÑу
+tracked_time_summary=ПідÑумок відÑтежуваного чаÑу на оÑнові фільтрів ÑпиÑку задач
create_new=Створити…
user_profile_and_more=Профіль Ñ– налаштуваннÑ…
signed_in_as=Увійшов Ñк
+enable_javascript=Ð”Ð»Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸ цього Ñайту потрібен JavaScript.
toc=ЗміÑÑ‚
licenses=Ліцензії
return_to_gitea=ПовернутиÑÑ Ð´Ð¾ Gitea
+more_items=Більше елементів
username=Ім'Ñ ÐºÑ€Ð¸Ñтувача
email=ÐдреÑа електронної пошти
password=Пароль
-access_token=Токен ДоÑтупу
-re_type=ÐŸÑ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ
+access_token=Токен доÑтупу
+re_type=Підтвердити пароль
captcha=CAPTCHA
-twofa=Двофакторна авторизаціÑ
+twofa=Двофакторна автентифікаціÑ
twofa_scratch=Двофакторний одноразовий пароль
passcode=Код доÑтупу
+webauthn_insert_key=Ð’Ñтавте ключ безпеки
+webauthn_sign_in=ÐатиÑніть кнопку на вашому ключі безпеки. Якщо ваш ключ без фізичної кнопки, поновно вÑтавте ключ.
+webauthn_press_button=Будь лаÑка, натиÑніть кнопку на вашому ключі безпеки…
+webauthn_use_twofa=ВикориÑтовуйте двофакторний код із Вашого телефона
+webauthn_error=Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ ваш ключ безпеки.
+webauthn_unsupported_browser=Ваш браузер наразі не підтримує WebAuthn.
+webauthn_error_unknown=СталаÑÑ Ð½ÐµÐ²Ñ–Ð´Ð¾Ð¼Ð° помилка. Будь лаÑка, Ñпробуйте ще раз.
+webauthn_error_insecure=`WebAuthn підтримує тільки безпечні з’єднаннÑ. Ð”Ð»Ñ Ñ‚ÐµÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾ HTTP, ви можете ÑкориÑтатиÑÑ "localhost" або "127.0.0.1"`
+webauthn_error_unable_to_process=Сервер не зміг обробити ваш запит.
+webauthn_error_duplicated=Ключ безпеки не підходить Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ запиту. ПереконайтеÑÑ, що ключ ще не зареєÑтровано.
+webauthn_error_empty=Ви повинні вÑтановити назву Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ ключа.
+webauthn_error_timeout=Ð§Ð°Ñ Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ñ‡ÐµÑ€Ð¿Ð°Ð½Ð¾, перш ніж ваш ключ було прочитано. Перезавантажте Ñторінку та Ñпробуйте ще раз.
webauthn_reload=Оновити
-repository=Репозиторій
+repository=Сховище
organization=ОрганізаціÑ
mirror=Дзеркало
-new_repo=Ðовий репозиторій
+issue_milestone=Етап
+new_repo=Ðове Ñховище
new_migrate=Ðова міграціÑ
new_mirror=Ðове дзеркало
-new_fork=Ðовий репозиторій - копіÑ
new_org=Ðова організаціÑ
new_project=Ðовий проєкт
+new_project_column=Ðовий Ñтовпець
manage_org=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñми
admin_panel=Панель ÐдмініÑтратора
account_settings=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу
@@ -52,7 +70,7 @@ your_starred=Обрані
your_settings=ÐалаштуваннÑ
all=УÑÑ–
-sources=ВлаÑні
+sources=Джерела
mirrors=Дзеркала
collaborative=Спільні
forks=Форки
@@ -64,302 +82,446 @@ milestones=Етапи
ok=OK
cancel=Відмінити
+retry=Повторіть Ñпробу
+rerun=ПерезапуÑтити
+rerun_all=ПерезапуÑтити вÑÑ– завданнÑ
save=Зберегти
add=Додати
add_all=Додати вÑе
remove=Видалити
remove_all=Видалити вÑе
+remove_label_str=`Видалити елемент "%s"`
edit=Редагувати
+view=ПереглÑнути
+test=ТеÑÑ‚
enabled=Увімкнено
disabled=Вимкнено
+locked=Заблоковано
copy=Копіювати
copy_url=Копіювати URL
+copy_hash=Копіювати хеш
+copy_content=Копіювати вміÑÑ‚
copy_branch=Копіювати назву гілки
+copy_path=Копіювати шлÑÑ…
copy_success=Скопійовано!
copy_error=Ðе вдалоÑÑ Ñкопіювати
+copy_type_unsupported=Цей тип файлу не можна Ñкопіювати
write=ПиÑати
preview=Попередній переглÑд
loading=ЗавантаженнÑ…
+files=Файли
error=Помилка
-error404=Сторінка, до Ñкої ви намагаєтеÑÑ Ð·Ð²ÐµÑ€Ð½ÑƒÑ‚Ð¸ÑÑ Ð°Ð±Ð¾ до <strong>, не Ñ–Ñнує</strong> або <strong>Ви не маєте права</strong> на Ñ—Ñ— переглÑд.
+error404=Сторінка, Ñку ви намагаєтеÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸, <strong>не Ñ–Ñнує</strong> або <strong>ви не маєте права</strong> на Ñ—Ñ— переглÑд.
+error503=Сервер не зміг виконати ваш запит. Будь лаÑка, Ñпробуйте пізніше.
+go_back=Ðазад
+invalid_data=ÐедійÑні дані: %v
never=Ðіколи
+unknown=Ðевідомо
+rss_feed=Стрічка RSS
+pin=Закріпити
+unpin=Відкріпити
+artifacts=Ðртефакти
+expired=ПроÑтрочено
+confirm_delete_artifact=Справді видалити артефакт '%s' ?
-archived=Ðрхівовані
+archived=Ðрхівовано
-concept_code_repository=Репозиторій
+concept_code_repository=Сховище
concept_user_organization=ОрганізаціÑ
+show_timestamps=Показувати чаÑові мітки
+show_log_seconds=Показувати Ñекунди
+show_full_screen=Показати на веÑÑŒ екран
+download_logs=Завантажити журнали
+confirm_delete_selected=Підтверджуєте Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²ÑÑ–Ñ… вибраних елементів?
name=Ðазва
+value=ЗначеннÑ
+readme=Файл readme
filter=Фільтр
-filter.is_archived=Ðрхівовані
+filter.clear=ОчиÑтити фільтр
+filter.is_archived=Ðрхівовано
+filter.not_archived=Ðе архівовано
+filter.is_fork=Відгалужено
+filter.not_fork=Ðе відгалужено
+filter.is_mirror=Віддзеркалено
+filter.not_mirror=Ðе віддзеркалено
filter.is_template=Шаблон
-filter.public=Публічний
+filter.not_template=Ðе шаблон
+filter.public=Публічна
filter.private=Приватний
+no_results_found=Ðічого не знайдено.
+internal_error_skipped=ТрапилаÑÑŒ Ð²Ð½ÑƒÑ‚Ñ€Ñ–ÑˆÐ½Ñ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°, але пропущена: %s
[search]
+search=Пошук...
+type_tooltip=Тип пошуку
+fuzzy=Ðеточний
+fuzzy_tooltip=Включити результати, Ñкі також близько відповідають пошуковому запиту
+words=Слова
+words_tooltip=Включати тільки результати, Ñкі відповідають пошуковим Ñловам
+regexp=РегулÑрний вираз
+regexp_tooltip=Включати тільки результати, Ñкі відповідають пошуковому запиту регулÑрного виразу
+exact=Точний
+exact_tooltip=Включати тільки результати, Ñкі відповідають точному пошуковому запиту
+repo_kind=Пошук Ñховищ...
+user_kind=Пошук кориÑтувачів...
+org_kind=Пошук організацій...
+team_kind=Пошук команд...
+code_kind=Пошук коду...
+code_search_unavailable=Пошук коду наразі недоÑтупний. Будь лаÑка, звернітьÑÑ Ð´Ð¾ адмініÑтратора Ñайту.
+code_search_by_git_grep=Поточні результати пошуку коду надаютьÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð¾ÑŽ "git grep". Результати можуть бути кращими, Ñкщо адмініÑтратор Ñайту увімкне індекÑатор Ñховища.
+package_kind=Пошук пакетів...
+project_kind=Пошук проєктів...
+branch_kind=Пошук гілок...
+tag_kind=Пошук за мітками...
+commit_kind=Пошук комітів...
+no_results=Ðе знайдено жодного збігу.
+issue_kind=Пошук задач...
+pull_kind=Пошук запитів на злиттÑ...
+keyword_search_unavailable=Пошук за ключовими Ñловами наразі недоÑтупний. Будь лаÑка, звернітьÑÑ Ð´Ð¾ адмініÑтратора Ñайту.
[aria]
+navbar=Панель навігації
+footer=Ðижній колонтитул
+footer.software=Про програмне забезпеченнÑ
+footer.links=ПоÑиланнÑ
[heatmap]
+number_of_contributions_in_the_last_12_months=%s внеÑків за оÑтанні 12 міÑÑців
+no_contributions=Ðемає внеÑків
+less=Менше
+more=Більше
[editor]
+buttons.heading.tooltip=Додати заголовок
+buttons.bold.tooltip=Додати грубий текÑÑ‚
+buttons.italic.tooltip=Додати курÑивний текÑÑ‚
+buttons.quote.tooltip=Цитувати текÑÑ‚
+buttons.code.tooltip=Додати код
+buttons.link.tooltip=Додати поÑиланнÑ
+buttons.list.unordered.tooltip=Додати ÑпиÑок
+buttons.list.ordered.tooltip=Додати нумерований ÑпиÑок
+buttons.list.task.tooltip=Додати ÑпиÑок завдань
+buttons.table.add.tooltip=Додати таблицю
buttons.table.add.insert=Додати
+buttons.table.rows=РÑдки
+buttons.table.cols=Стовпці
+buttons.mention.tooltip=Згадати кориÑтувача або команду
+buttons.ref.tooltip=ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° задачу або запит на злиттÑ
+buttons.switch_to_legacy.tooltip=ВикориÑтовувати заÑтарілий редактор
+buttons.enable_monospace_font=Увімкнути моноширинний шрифт
+buttons.disable_monospace_font=Вимкнути моноширинний шрифт
[filter]
+string.asc=Р- Я
+string.desc=Я - Ð
[error]
occurred=СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°
+report_message=Якщо ви вважаєте, що це помилка Gitea, будь лаÑка, Ñпробуйте відшукати відповідну проблему на <a href="https://github.com/go-gitea/gitea/issues">GitHub</a> або Ñтворіть нову, Ñкщо необхідно.
+not_found=Ціль не знайдено.
network_error=Помилка мережі
[startpage]
app_desc=Зручний влаÑний ÑÐµÑ€Ð²Ñ–Ñ Ñ…Ð¾Ñтингу репозиторіїв Git
install=Легко вÑтановити
+install_desc=ПроÑто <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com/installation/install-from-binary">запуÑтіть двійковий файл</a> Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ñ— платформи, ÑкориÑтайтеÑÑ <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a>, або вÑтановіть <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com/installation/install-from-package">ÑиÑтемою ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð°ÐºÑƒÐ½ÐºÐ°Ð¼Ð¸</a>.
platform=ПлатформонезалежніÑть
+platform_desc=Gitea запуÑкаєтьÑÑ Ð±ÑƒÐ´ÑŒ-де, де <a target=«_blank» rel=«noopener noreferrer» href=«%s»>Go</a> може компілюватиÑÑŒ: на Windows, macOS, Linux, ARM тощо. Виберіть платформу, Ñку любите!
lightweight=ÐевибагливіÑть
-lightweight_desc=Gitea має низькі вимоги до реÑурÑів та може працювати на недорогому Raspberry Pi. Збережіть Ñвою машину енергію!
+lightweight_desc=Gitea має мінімальні вимоги Ñ– може працювати на недорогому Raspberry Pi. Заощаджуйте реÑурÑи вашої машини!
license=Відкритий вихідний код
[install]
install=Ð’ÑтановленнÑ
+installing_desc=Ð’ÑтановленнÑ, будь лаÑка, зачекайте...
title=Початкова конфігураціÑ
-docker_helper=Якщо ви запуÑкаєте Gitea вÑередині Docker, будь лаÑка уважно прочитайте <a target="_blank" rel="noopener" href="%s">документацію</a> перед тим, Ñк щоÑÑŒ змінити на цій Ñторінці.
+docker_helper=Якщо ви запуÑкаєте Gitea у Docker, будь лаÑка, прочитайте <a target="_blank" rel="noopener noreferrer" href="%s">документацію</a> перед тим, Ñк змінювати будь-Ñкі налаштуваннÑ.
+require_db_desc=Gitea потребує MySQL, PostgreSQL, MSSQL, SQLite3 або TiDB (протокол MySQL).
db_title=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð±Ð°Ð·Ð¸ даних
db_type=Тип бази даних
host=ХоÑÑ‚
user=Ім'Ñ ÐºÑ€Ð¸Ñтувача
password=Пароль
-db_name=Ім'Ñ Ð±Ð°Ð·Ð¸ даних
+db_name=Ðазва бази даних
db_schema=Схема
-db_schema_helper=Залиште пуÑтим Ð´Ð»Ñ Ð±Ð°Ð·Ð¸ даних за замовчуваннÑм ("публічна").
+db_schema_helper=Залиште пуÑтим Ð´Ð»Ñ Ñ‚Ð¸Ð¿Ð¾Ð²Ð¾Ñ— Ñхеми бази даних ("публічна").
ssl_mode=SSL
path=ШлÑÑ…
-sqlite_helper=ШлÑÑ… до файлу Ð´Ð»Ñ Ð±Ð°Ð·Ð¸ даних SQLite3.<br>Введіть абÑолютний шлÑÑ…, Ñкщо ви запуÑкаєте GÑ–tea Ñк ÑервіÑ.
+sqlite_helper=ШлÑÑ… до файлу бази даних SQLite3.<br>Введіть абÑолютний шлÑÑ…, Ñкщо ви запуÑкаєте GÑ–tea Ñк ÑервіÑ.
reinstall_error=Ви намагаєтеÑÑ Ð²Ñтановити в наÑвну базу даних Gitea
reinstall_confirm_message=Повторне вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð² наÑвну базу даних Gitea може Ñпричинити багато проблем. Ð’ більшоÑті випадків, ви повинні викориÑтовувати Ñвій наÑвний "app.ini" Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑку Gitea. Якщо ви знаєте, що робите, Ñпробуйте наÑтупне:
-reinstall_confirm_check_1=Дані зашифровані з викориÑтаннÑм SECRET_KEY з app.ini можуть бути втрачені: кориÑтувачі не зможуть увійти з 2FA/OTP Ñ– дзеркала можуть працювати некоректно. Ð’Ñтановлюючи цей прапорець, ви підтверджуєте, що в поточному файлі app.ini вказано правильне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ SECRET_KEY.
-reinstall_confirm_check_2=Репозиторії та Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð½ÐµÐ¾Ð±Ñ…Ñ–Ð´Ð½Ð¾ повторно Ñинхронізувати. Ð’Ñтановлюючи цей прапорець, ви підтверджуєте, що ви Ñинхронізуватимете хуки репозиторіїв та authorized_keys вручну. Ви підтверджуєте, що Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–ÑŽ Ñ– дзеркала Ñ” правильними.
-reinstall_confirm_check_3=Ви підтверджуєте, що повніÑтю впевнені в тому, що Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ екземплÑра Gitea вказано правильне Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ app.ini та екземплÑÑ€ Ñлід вÑтановити повторно. Ви підтверджуєте, що уÑвідомлюєте вищенаведені ризики.
+reinstall_confirm_check_1=Дані, зашифровані за допомогою SECRET_KEY в app.ini, можуть бути втрачені: кориÑтувачі не зможуть увійти за допомогою 2FA/OTP, а дзеркала можуть працювати некоректно. Ð’Ñтановивши цей прапорець, ви підтверджуєте, що поточний файл app.ini міÑтить правильний SECRET_KEY.
+reinstall_confirm_check_2=Можливо, потрібно буде повторно Ñинхронізувати Ñховища та налаштуваннÑ. Ð’Ñтановивши цей прапорець, ви підтверджуєте, що будете реÑинхронізувати хуки Ð´Ð»Ñ Ñховищ Ñ– файл authorized_keys вручну. Ви підтверджуєте, що забезпечите правильніÑть налаштувань Ñховища Ñ– дзеркала.
+reinstall_confirm_check_3=Ви підтверджуєте, що абÑолютно впевнені, що Gitea працює з правильним розташуваннÑм файлу app.ini, Ñ– що вам потрібно перевÑтановити програму. Ви підтверджуєте, що уÑвідомлюєте вищевказані ризики.
err_empty_db_path=ШлÑÑ… до файлу бази даних SQLite3 не може бути порожнім.
-no_admin_and_disable_registration=Ви не можете вимкнути реєÑтрацію до ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу адмініÑтратора.
+no_admin_and_disable_registration=Ви не можете вимкнути реєÑтрацію без ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу адмініÑтратора.
err_empty_admin_password=Пароль адмініÑтратора не може бути порожнім.
err_empty_admin_email=Електронна адреÑа адмініÑтратора не може бути порожньою.
-err_admin_name_is_reserved=Ðеправильне ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача-адмініÑтратора - ім'Ñ Ð·Ð°Ñ€ÐµÐ·ÐµÑ€Ð²Ð¾Ð²Ð°Ð½Ðµ
-err_admin_name_pattern_not_allowed=Ім'Ñ Ð°Ð´Ð¼Ñ–Ð½Ñ–Ñтратора недійÑне, це ім'Ñ Ð¿Ñ–Ð´Ð¿Ð°Ð´Ð°Ñ” під зарезервований шаблон
-err_admin_name_is_invalid=Ðеправильне ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача-адмініÑтратора
+err_admin_name_is_reserved=Ðеправильне ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача адмініÑтратора - ім'Ñ Ð·Ð°Ñ€ÐµÐ·ÐµÑ€Ð²Ð¾Ð²Ð°Ð½Ð¾
+err_admin_name_pattern_not_allowed=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача адмініÑтратора недійÑне, воно відповідає зарезервованому шаблону
+err_admin_name_is_invalid=ÐедійÑне ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача адмініÑтратора
general_title=Загальні налаштуваннÑ
app_name=Ðазва Ñайту
app_name_helper=Тут ви можете ввеÑти назву Ñвоєї компанії.
-repo_path=Кореневий шлÑÑ… репозиторіÑ
-repo_path_helper=Ð’ÑÑ– вилучені Git репозиторії будуть збережені в цей каталог.
-lfs_path=Кореневої шлÑÑ… Git LFS
-lfs_path_helper=У цій папці будуть зберігатиÑÑ Ñ„Ð°Ð¹Ð»Ð¸ Git LFS. Залиште порожнім, щоб вимкнути LFS.
-run_user=ЗапуÑк від імені КориÑтувача
+repo_path=Кореневий шлÑÑ… Ñховища
+repo_path_helper=До цього каталогу буде збережено віддалені Ñховища Git.
+lfs_path=Кореневий шлÑÑ… Git LFS
+lfs_path_helper=У цій теці будуть зберігатиÑÑ Ñ„Ð°Ð¹Ð»Ð¸ Git LFS. Залиште порожнім, щоб вимкнути.
+run_user=Виконати Ñк
+run_user_helper=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача операційної ÑиÑтеми, від імені Ñкого запуÑкаєтьÑÑ Gitea. Зауважте, що цей кориÑтувач повинен мати доÑтуп до кореневого шлÑху Ñховища.
domain=Домен Ñервера
-domain_helper=Домен або адреÑа хоÑта Ñервера.
+domain_helper=Домен або хоÑÑ‚-адреÑа Ñервера.
ssh_port=Порт SSH Ñервера
ssh_port_helper=Ðомер порту, Ñкий викориÑтовує SSH Ñервер. Залиште порожнім, щоб вимкнути SSH.
http_port=Gitea HTTP порт
-http_port_helper=Ðомер порту, Ñкий буде проÑлуховуватиÑÑ Giteas веб-Ñервером.
+http_port_helper=Ðомер порту, Ñкий буде проÑлуховуватиÑÑ Ñервером Giteas.
app_url=Базова URL-адреÑа Gitea
-app_url_helper=Базова адреÑа Ð´Ð»Ñ HTTP(S) ÐºÐ»Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡ÐµÑ€ÐµÐ· URL та повідомлень електронної пошти.
-log_root_path=ШлÑÑ… до лог файлу
-log_root_path_helper=Файли журналу будуть запиÑані в цей каталог.
-
-optional_title=Додаткові налаштуваннÑ
-email_title=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Email
-smtp_addr=SMTP хоÑÑ‚
-smtp_port=SMTP порт
-smtp_from=ВідправлÑти Email від імені
-smtp_from_helper=Електронна пошта Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ Ð² GÑ–tea. Введіть звичайну електронну адреÑу або викориÑтовуйте формат: "Ім'Ñ" <email@example.com>.
-mailer_user=SMTP Ім'Ñ ÐºÑ€Ð¸Ñтувача
-mailer_password=SMTP Пароль
-register_confirm=Потрібно підтвердити електронну пошту Ð´Ð»Ñ Ñ€ÐµÑ”Ñтрації
+app_url_helper=Базова адреÑа Ð´Ð»Ñ URL-Ð°Ð´Ñ€ÐµÑ ÐºÐ»Ð¾Ð½Ñ–Ð² HTTP(S) та Ñповіщень електронною поштою.
+log_root_path=ШлÑÑ… до журналу
+log_root_path_helper=Файли журналу будуть запиÑані в цю теку.
+
+optional_title=Ðеобов'Ñзкові налаштуваннÑ
+email_title=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти
+smtp_addr=Сервер SMTP
+smtp_port=Порт SMTP
+smtp_from=Відправити лиÑта від імені
+smtp_from_invalid=ÐдреÑа "ÐадіÑлати лиÑта Ñк" недійÑна
+smtp_from_helper=ÐдреÑа електронної пошти, Ñку буде викориÑтовувати Gitea. Введіть звичайну адреÑу електронної пошти або викориÑтовуйте формат «Ім'Ñ» <email@example.com>.
+mailer_user=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача SMTP
+mailer_password=Пароль SMTP
+register_confirm=Вимагати Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾ÑŽ поштою Ð´Ð»Ñ Ñ€ÐµÑ”Ñтрації
mail_notify=Увімкнути ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾ÑŽ поштою
-server_service_title=Сервер Ñ– Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ–Ñ… Ñлужб
+server_service_title=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñервера Ñ– Ñторонніх ÑервіÑів
offline_mode=Увімкнути локальний режим
-offline_mode_popup=Відключити Ñторонні мережі доÑтавки контенту Ñ– обÑлуговувати вÑÑ– реÑурÑи локально.
+offline_mode_popup=Вимкнути Ñторонні мережі доÑтавки контенту та обÑлуговувати вÑÑ– реÑурÑи локально.
disable_gravatar=Вимкнути Gravatar
-disable_gravatar_popup=Відключити Gravatar Ñ– Ñторонні джерела аватарів. Якщо кориÑтувач не завантажить аватар локально то за замовчуваннÑм буде викориÑтовуватиÑÑ Ñтандартний аватар.
-federated_avatar_lookup=Увімкнути федеративні аватари
-federated_avatar_lookup_popup=Увімкнути зовнішний Ðватар за допомогою Libravatar.
-disable_registration=Вимкнути ÑамоÑтійну реєÑтрацію
-disable_registration_popup=Вимкнути ÑамоÑтійну реєÑтрацію кориÑтувачів, тільки адмініÑтратор може Ñтворювати нові облікові запиÑи.
+disable_gravatar_popup=Вимкнути Gravatar та Ñторонні джерела аватарок. Якщо кориÑтувач локально не завантажить аватар, буде викориÑтовуватиÑÑ Ñ‚Ð¸Ð¿Ð¾Ð²Ð¸Ð¹ аватар.
+federated_avatar_lookup=Увімкнути зовнішні аватари
+federated_avatar_lookup_popup=Увімкнути пошук об'єднаних аватарів за допомогою Libravatar.
+disable_registration=Вимкнути реєÑтрацію
+disable_registration_popup=Вимкнути реєÑтрацію кориÑтувачів, тільки адмініÑтратор може Ñтворювати нові облікові запиÑи.
allow_only_external_registration_popup=Дозволити реєÑтрацію тільки через Ñторонні ÑервіÑи
-openid_signin=Увімкнути реєÑтрацію за допомогою OpenID
-openid_signin_popup=Увімкнути вхід за допомогою OpenID.
+openid_signin=Увімкнути вхід за допомогою OpenID
+openid_signin_popup=Увімкнути вхід кориÑтувачів за допомогою OpenID.
openid_signup=Увімкнути ÑамоÑтійну реєÑтрацію за допомогою OpenID
openid_signup_popup=Увімкнути ÑамореєÑтрацію кориÑтувачів на оÑнові OpenID.
-enable_captcha=Увімкнути CAPTCHA при реєÑтрації
-enable_captcha_popup=Вимагати перевірку CAPTCHA при ÑамоÑтійній реєÑтрації кориÑтувача.
+enable_captcha=Увімкнути CAPTCHA Ð´Ð»Ñ Ñ€ÐµÑ”Ñтрації
+enable_captcha_popup=Вимагати CAPTCHA Ð´Ð»Ñ ÑамореєÑтрації кориÑтувачів.
require_sign_in_view=Вимагати авторизації Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду Ñторінок
+require_sign_in_view_popup=Обмежити доÑтуп до Ñторінки лише Ð´Ð»Ñ Ð·Ð°Ñ€ÐµÑ”Ñтрованих кориÑтувачів. Відвідувачі побачать тільки Ñторінки входу Ñ– реєÑтрації.
admin_setting_desc=Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу адмініÑтратора необов'Ñзково. Перший зареєÑтрований кориÑтувач автоматично Ñтає адмініÑтратором.
admin_title=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу адмініÑтратора
admin_name=Ім'Ñ ÐºÑ€Ð¸Ñтувача ÐдмініÑтратора
admin_password=Пароль
-confirm_password=ÐŸÑ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ
+confirm_password=Підтвердити пароль
admin_email=ÐдреÑа електронної пошти
-install_btn_confirm=Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Gitea
-test_git_failed=Ðе в змозі перевірити 'git' команду: %v
+install_btn_confirm=Ð’Ñтановити Gitea
+test_git_failed=Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€Ð¸Ñ‚Ð¸ команду 'git': %v
sqlite3_not_available=Ð¦Ñ Ð²ÐµÑ€ÑÑ–Ñ Gitea не підтримує SQLite3. Будь лаÑка, завантажте офіційну бінарну верÑÑ–ÑŽ з %s (не верÑÑ–ÑŽ gobuild).
-invalid_db_setting=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð±Ð°Ð·Ð¸ даних Ñ” некоректними: %v
-invalid_repo_path=Помилковий шлÑÑ… до ÐºÐ¾Ñ€ÐµÐ½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–ÑŽ: %v
-invalid_app_data_path=Ðекоректний шлÑÑ… до даних програми: %v
-run_user_not_match=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача 'run as' не Ñ” поточним ім'Ñм кориÑтувача: %s -> %s
-internal_token_failed=Ðе вдалоÑÑ Ð·Ð³ÐµÐ½ÐµÑ€ÑƒÐ²Ð°Ñ‚Ð¸ внутрішній токен: %v
-secret_key_failed=Ðе вдалоÑÑ Ð·Ð³ÐµÐ½ÐµÑ€ÑƒÐ²Ð°Ñ‚Ð¸ Ñекретний ключ: %v
-save_config_failed=Ðе в змозі зберегти конфігурацію: %v
-invalid_admin_setting=ÐеприпуÑтимі Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу адмініÑтратора: %v
-invalid_log_root_path=ÐеприпуÑтимий шлÑÑ… Ð´Ð»Ñ Ð»Ð¾Ð³Ñ–Ð²: %v
-default_keep_email_private=Приховати адреÑу електронної пошти за замовчуваннÑм
-default_keep_email_private_popup=Приховати адреÑу електронної пошти нових облікових запиÑів за замовчуваннÑм.
-default_allow_create_organization=Дозволити ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ð¹ за замовчуваннÑм
-default_allow_create_organization_popup=Дозволити новим обліковим запиÑам кориÑтувачів Ñтворювати організації за замовчуваннÑм.
-default_enable_timetracking=Увімкнути відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡Ð°Ñу за замовчуваннÑм
-default_enable_timetracking_popup=Включити відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡Ð°Ñу Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… репозиторіїв за замовчуваннÑм.
+invalid_db_setting=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð±Ð°Ð·Ð¸ даних недійÑні: %v
+invalid_db_table=База даних таблиці "%s" Ñ” недійÑною: %v
+invalid_repo_path=Кореневий шлÑÑ… до Ñховища невірний: %v
+invalid_app_data_path=ШлÑÑ… до даних додатка невірний: %v
+run_user_not_match=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача “Виконати Ñк†не Ñ” поточним ім'Ñм кориÑтувача: %s -> %s
+internal_token_failed=Ðе вдалоÑÑ Ñтворити внутрішній токен: %v
+secret_key_failed=Ðе вдалоÑÑ Ñтворити Ñекретний ключ: %v
+save_config_failed=Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ конфігурацію: %v
+invalid_admin_setting=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу адмініÑтратора Ñ” недійÑним: %v
+invalid_log_root_path=Ðеправильний шлÑÑ… до журналу: %v
+default_keep_email_private=Типово приховувати адреÑи електронної пошти
+default_keep_email_private_popup=Типово приховувати адреÑи електронної пошти нових облікових запиÑів.
+default_allow_create_organization=За замовчуваннÑм дозволити ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ð¹
+default_allow_create_organization_popup=За замовчуваннÑм дозволити новим кориÑтувачам Ñтворювати організації.
+default_enable_timetracking=За замовчуваннÑм увімкнути відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡Ð°Ñу
+default_enable_timetracking_popup=Увімкнути відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡Ð°Ñу Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… Ñховищ за замовчуваннÑм.
no_reply_address=Прихований поштовий домен
-no_reply_address_helper=Доменне ім'Ñ Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів із прихованою електронною адреÑою. Ðаприклад, ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача 'joe' буде входити в Git Ñк 'joe@noreply.example.org', Ñкщо Ð´Ð»Ñ Ð¿Ñ€Ð¸Ñ…Ð¾Ð²Ð°Ð½Ð¾Ð³Ð¾ домену електронної пошти вÑтановлено 'noreply.example.org'.
+no_reply_address_helper=Доменне ім'Ñ Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів із прихованою електронною адреÑою. Ðаприклад, ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача 'Joe' буде зображатиÑÑ Ð² Git Ñк 'joe@noreply.example.org', Ñкщо прихований домен електронної пошти вÑтановлено 'noreply.example.org'.
password_algorithm=Ðлгоритм Ñ…ÐµÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ
+invalid_password_algorithm=ÐедійÑний хеш-алгоритм паролÑ
+password_algorithm_helper=Ð’Ñтановіть алгоритм Ñ…ÐµÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ. Ðлгоритми мають різні вимоги та ÑтійкіÑть. Ðлгоритм argon2 Ñ” доÑить безпечним, але викориÑтовує багато пам'Ñті Ñ– може бути недоречним Ð´Ð»Ñ Ð¼Ð°Ð»Ð¸Ñ… ÑиÑтем.
+enable_update_checker=Увімкнути перевірку оновлень
+enable_update_checker_helper=Періодично перевірÑти наÑвніÑть нових верÑій, підключаючиÑÑŒ до gitea.io.
+env_config_keys=ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ñередовища
+env_config_keys_prompt=ÐаÑтупні змінні Ñередовища також будуть заÑтоÑовані до вашого файлу конфігурації:
+config_write_file_prompt=Ці параметри будуть запиÑані в: %s
[home]
-uname_holder=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача або Ел. пошта
+nav_menu=Меню навігації
+uname_holder=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача або адреÑа електронної пошти
password_holder=Пароль
switch_dashboard_context=Переключити контекÑÑ‚ панелі управліннÑ
-my_repos=Репозиторії
-show_more_repos=Показати більше репозиторіїв…
-collaborative_repos=Спільні репозиторії
+my_repos=Сховища
+show_more_repos=Показати більше Ñховищ…
+collaborative_repos=Спільні Ñховища
my_orgs=Мої організації
my_mirrors=Мої дзеркала
view_home=ПереглÑнути %s
filter=Інші фільтри
-filter_by_team_repositories=Фільтрувати за репозиторіÑми команд
+filter_by_team_repositories=Фільтрувати за Ñховищами команд
feed_of=`Стрічка "%s"`
show_archived=Ðрхівовані
-show_both_archived_unarchived=Показано архівовані і не архівовані
+show_both_archived_unarchived=Показано архівовані і неархівовані
show_only_archived=Показано тільки архівовані
-show_only_unarchived=Показано тільки не архівовані
+show_only_unarchived=Показано тільки неархівовані
show_private=Приватні
show_both_private_public=Показано публічні та приватні
show_only_private=Показано тільки приватні
show_only_public=Показано тільки публічні
-issues.in_your_repos=Ð’ ваших репозиторіÑÑ…
+issues.in_your_repos=У ваших Ñховищах
+
+guide_title=Жодної активноÑті
+guide_desc=Ðаразі ви не Ñтежите за жодним Ñховищем або кориÑтувачем, тому нема чого відображати. Ви можете переглÑнути Ñховища або кориÑтувачів, Ñкі Ð²Ð°Ñ Ñ†Ñ–ÐºÐ°Ð²Ð»Ñть, за поÑиланнÑми нижче.
+explore_repos=ОглÑд Ñховищ
+explore_users=ОглÑд кориÑтувачів
+empty_org=Організацій поки що немає.
+empty_repo=Сховищ поки що немає.
[explore]
-repos=Репозиторії
+repos=Сховища
users=КориÑтувачі
organizations=Організації
+go_to=Перейти до
code=Код
code_last_indexed_at=ОÑтанні індекÑовані %s
+relevant_repositories_tooltip=Сховища, Ñкі Ñ” відгалуженими або не мають теми, піктограми й опиÑу, приховуютьÑÑ.
+relevant_repositories=Показано лише важливі Ñховища, <a href="%s">показати нефільтровані результати</a>.
[auth]
create_new_account=РеєÑÑ‚Ñ€Ð°Ñ†Ñ–Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу
-disable_register_prompt=Вибачте, можливіÑть реєÑтрації відключена. Будь лаÑка, зв'ÑжітьÑÑ Ð· адмініÑтратором Ñайту.
+already_have_account=Вже зареєÑтровані?
+sign_in_now=Увійдіть зараз!
+disable_register_prompt=РеєÑтрацію вимкнено. Будь лаÑка, зв'ÑжітьÑÑ Ð· адмініÑтратором Ñайту.
disable_register_mail=ÐŸÑ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ñ€ÐµÑ”Ñтрації електронною поштою вимкнено.
+manual_activation_only=ЗвернітьÑÑ Ð´Ð¾ адмініÑтратора Ñайту Ð´Ð»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ñ–Ñ—.
remember_me=Запам’Ñтати цей приÑтрій
+remember_me.compromised=Токен Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ більше не дійÑний, що може Ñвідчити про Ñкомпрометований обліковий запиÑ. Перевірте Ñвій обліковий Ð·Ð°Ð¿Ð¸Ñ Ð½Ð° наÑвніÑть незвичайних дій.
forgot_password_title=Забув пароль
forgot_password=Забули пароль?
-must_change_password=Оновіть Ñвій пароль
-allow_password_change=Вимагати в кориÑтувача змінити пароль (рекомендуєтьÑÑ)
-reset_password_mail_sent_prompt=Електронний лиÑÑ‚ із підтвердженнÑм надіÑлано <b>%s</b>. Перевірте папку 'Вхідні' в межах наÑтупних %s, щоб завершити Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу.
+need_account=Потрібен обліковий запиÑ?
+sign_up_tip=Ви реєÑтруєте перший обліковий Ð·Ð°Ð¿Ð¸Ñ Ñƒ ÑиÑтемі, з правами адмініÑтратора. Будь лаÑка, уважно запам'Ñтайте Ñвоє ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача та пароль. Якщо ви Ñ—Ñ… забудете, звернітьÑÑ Ð´Ð¾ документації Gitea, щоб відновити обліковий запиÑ.
+sign_up_now=ЗареєÑтруватиÑÑ.
+sign_up_successful=Обліковий Ð·Ð°Ð¿Ð¸Ñ Ñтворено уÑпішно. Вітаю!
+confirmation_mail_sent_prompt_ex=Ðовий лиÑÑ‚ з підтвердженнÑм було надіÑлано на <b>%s</b>. Будь лаÑка, перевірте Ñвою поштову Ñкриньку протÑгом наÑтупних %s, щоб завершити Ð¿Ñ€Ð¾Ñ†ÐµÑ Ñ€ÐµÑ”Ñтрації. Якщо ви вказали невірну адреÑу електронної пошти, ви можете увійти ще раз Ñ– змінити Ñ—Ñ—.
+must_change_password=Оновити пароль
+allow_password_change=Вимагати від кориÑтувача змінити пароль (рекомендовано)
+reset_password_mail_sent_prompt=Ðа адреÑу <b>%s</b> було надіÑлано лиÑÑ‚ із підтвердженнÑм. Будь лаÑка, перевірте Ñвою поштову Ñкриньку протÑгом наÑтупних %s, щоб завершити Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу.
active_your_account=Ðктивувати обліковий запиÑ
account_activated=Обліковий Ð·Ð°Ð¿Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¾Ð²Ð°Ð½Ð¾
-prohibit_login=Вхід заборонений
-resent_limit_prompt=Вибачте, ви вже запроÑили активацію по електронній пошті нещодавно. Будь лаÑка, зачекайте 3 хвилини, а потім Ñпробуйте ще раз.
-has_unconfirmed_mail=Привіт %s, у Ð²Ð°Ñ Ñ” непідтверджена електронна адреÑа (<b>%s </b>). Якщо ви не отримали електронний лиÑÑ‚ із підтвердженнÑм або вам потрібно надіÑлати новий, натиÑніть на кнопку нижче.
-resend_mail=ÐатиÑніть тут, щоб виÑлати лиÑÑ‚ активації знову
-email_not_associate=Ð¦Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð° пошта не пов'Ñзана ні з одним обліковим запиÑом.
-send_reset_mail=ÐадіÑлати електронний лиÑÑ‚ Ð´Ð»Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу
+prohibit_login=Вхід заборонено
+prohibit_login_desc=Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¾ Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ, звернітьÑÑ Ð´Ð¾ адмініÑтратора Ñайту.
+resent_limit_prompt=Ви вже надÑилали запит на активацію нещодавно. Зачекайте 3 хвилини Ñ– Ñпробуйте ще раз.
+has_unconfirmed_mail=Привіт %s, у Ð²Ð°Ñ Ð½ÐµÐ¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð° адреÑа електронної пошти (<b>%s</b>). Якщо ви не отримали лиÑта з підтвердженнÑм або вам потрібно надіÑлати новий, будь лаÑка, натиÑніть кнопку нижче.
+change_unconfirmed_mail_address=Якщо ваша адреÑа електронної пошти Ð´Ð»Ñ Ñ€ÐµÑ”Ñтрації невірна, ви можете змінити Ñ—Ñ— тут Ñ– надіÑлати новий лиÑÑ‚ з підтвердженнÑм.
+resend_mail=ÐатиÑніть тут, щоб повторно надіÑлати лиÑÑ‚ з активацією
+email_not_associate=Ð¦Ñ Ð°Ð´Ñ€ÐµÑа електронної пошти не пов'Ñзана з жодним обліковим запиÑом.
+send_reset_mail=ÐадіÑлати лиÑÑ‚ Ð´Ð»Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу
reset_password=Ð’Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу
-invalid_code=Цей код Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð½ÐµÐ´Ñ–Ð¹Ñний або закінчивÑÑ.
+invalid_code=Ваш код Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð½ÐµÐ´Ñ–Ð¹Ñний або його термін дії закінчивÑÑ.
+invalid_code_forgot_password=Ваш код Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð½ÐµÐ´Ñ–Ð¹Ñний або термін дії минув. ÐатиÑніть <a href="%s">тут</a>, щоб почати новий ÑеанÑ.
+invalid_password=Ваш пароль не збігаєтьÑÑ Ð· паролем, Ñкий викориÑтовувавÑÑ Ð¿Ñ€Ð¸ Ñтворенні облікового запиÑу.
reset_password_helper=Відновити обліковий запиÑ
+reset_password_wrong_user=Ви увійшли Ñк %s, але поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу призначене Ð´Ð»Ñ %s
password_too_short=Довжина Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð½Ðµ може бути меншою за %d Ñимволів.
-non_local_account=Ðелокальні акаунти не можуть змінити пароль через Gitea.
+non_local_account=Ðелокальні кориÑтувачі не можуть оновити Ñвій пароль через Ð²ÐµÐ±Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Gitea.
verify=Підтвердити
scratch_code=Одноразовий пароль
-use_scratch_code=ВикориÑтовувати одноразовий пароль
-twofa_scratch_used=Ви викориÑтовували одноразовий пароль. Ви були перенаправлені на Ñторінку налаштувань Ð´Ð»Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ñ–Ñ— нового коду або Ð²Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð²ÑƒÑ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð¾Ñ— автентифікації.
-twofa_passcode_incorrect=Ваш пароль Ñ” невірним. Якщо ви втратили приÑтрій, викориÑтовуйте ваш одноразовий пароль.
+use_scratch_code=СкориÑтатиÑÑŒ одноразовим паролем
+twofa_scratch_used=Ви ÑкориÑталиÑÑ Ð¾Ð´Ð½Ð¾Ñ€Ð°Ð·Ð¾Ð²Ð¸Ð¼ паролем. Ð’Ð°Ñ Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¾ на Ñторінку налаштувань двофакторної автентифікації, щоб видалити приÑтрій або згенерувати новий одноразовий пароль.
+twofa_passcode_incorrect=Ваш пароль неправильний. Якщо ви загубили Ñвій приÑтрій, ÑкориÑтайтеÑÑ Ð¾Ð´Ð½Ð¾Ñ€Ð°Ð·Ð¾Ð²Ð¸Ð¼ паролем.
twofa_scratch_token_incorrect=Ðевірний одноразовий пароль.
login_userpass=Увійти
login_openid=OpenID
oauth_signup_tab=ЗареєÑтрувати обліковий запиÑ
-oauth_signup_title=Повний новий обліковий запиÑ
-oauth_signup_submit=Повний обліковий запиÑ
-oauth_signin_tab=ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° Ñ–Ñнуючий обліковий запиÑ
+oauth_signup_title=Завершити реєÑтрацію облікового запиÑу
+oauth_signup_submit=Поповнити обліковий запиÑ
+oauth_signin_tab=Прив'Ñзати до Ñ–Ñнуючого облікового запиÑу
oauth_signin_title=Увійдіть щоб авторизувати пов'Ñзаний обліковий запиÑ
oauth_signin_submit=Прив'Ñзати обліковий запиÑ
+oauth.signin.error.general=Під Ñ‡Ð°Ñ Ð¾Ð±Ñ€Ð¾Ð±ÐºÐ¸ запиту на авторизацію ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°: %s. Якщо Ñ†Ñ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° повторитьÑÑ, звернітьÑÑ Ð´Ð¾ адмініÑтратора Ñайту.
+oauth.signin.error.access_denied=Запит на авторизацію відхилено.
+oauth.signin.error.temporarily_unavailable=Помилка авторизації, Ñервер автентифікації тимчаÑово недоÑтупний. Спробуйте пізніше.
openid_connect_submit=Під’єднатиÑÑ
openid_connect_title=ПідключитиÑÑ Ð´Ð¾ Ñ–Ñнуючого облікового запиÑу
-openid_connect_desc=Вибраний OpenID URI невідомий. Пов'Ñжіть його з новим обліковим запиÑом тут.
+openid_connect_desc=Обраний OpenID URI невідомий. Зв'Ñжіть його тут з новим обліковим запиÑом.
openid_register_title=Створити новий обліковий запиÑ
-openid_register_desc=Вибраний OpenID URI невідомий. Пов'Ñжіть йогоз новим обліковим запиÑом тут.
+openid_register_desc=Обраний OpenID URI невідомий. Зв'Ñжіть його тут з новим обліковим запиÑом.
+openid_signin_desc=Введіть Ñвій OpenID URI. Ðаприклад: alice.openid.example.org або https://openid.example.org/alice.
disable_forgot_password_mail=Ð’Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу вимкнено, оÑкільки не налаштована електронна пошта. Будь лаÑка, зв'ÑжітьÑÑ Ð· адмініÑтратором Ñайту.
-disable_forgot_password_mail_admin=Ð’Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу доÑтупне лише піÑÐ»Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти. Будь лаÑка, налаштуйте ел. пошту Ð´Ð»Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу.
-email_domain_blacklisted=З вказаним email реєÑÑ‚Ñ€Ð°Ñ†Ñ–Ñ Ð½ÐµÐ¼Ð¾Ð¶Ð»Ð¸Ð²Ð°.
+disable_forgot_password_mail_admin=Ð’Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу доÑтупне лише за наÑвноÑті електронної пошти. Будь лаÑка, налаштуйте електронну пошту, щоб увімкнути Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу.
+email_domain_blacklisted=Ви не можете зареєÑтруватиÑÑ Ð· адреÑою електронної пошти.
authorize_application=Ðвторизувати програму
authorize_redirect_notice=Ð’Ð°Ñ Ð±ÑƒÐ´Ðµ переадреÑовано до %s, Ñкщо ви авторизуєте цю програму.
authorize_application_created_by=Ð¦Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð° Ñтворена %s.
-authorize_application_description=Якщо ви надаÑте цей доÑтуп, то він матиме доÑтуп до вÑÑ–Ñ… ваших даних облікового запиÑу, включаючи приватні репозиторії та організації.
-authorize_title=Ðвторизуйвати "%s" Ð´Ð»Ñ Ð´Ð¾Ñтупу до вашого облікового запиÑу?
+authorize_application_description=Якщо ви авторизуєте цю програму, їй буде надано дозвіл на Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð±Ð¾ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð²Ñієї інформації вашого облікового запиÑу, включно з приватними Ñховищами та організаціÑми.
+authorize_title=Ðвторизувати "%s" Ð´Ð»Ñ Ð´Ð¾Ñтупу до вашого облікового запиÑу?
authorization_failed=Помилка авторизації
-sspi_auth_failed=Помилка SSPI-автентифікації
+authorization_failed_desc=ÐÐ²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ñ–Ñ Ð½Ðµ вдалаÑÑ, оÑкільки ми виÑвили недійÑний запит. ЗвернітьÑÑ Ð´Ð¾ розробника програми, Ñку ви намагалиÑÑ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·ÑƒÐ²Ð°Ñ‚Ð¸.
+sspi_auth_failed=Помилка автентифікації SSPI
+password_pwned=Пароль, Ñкий ви обрали, знаходитьÑÑ Ð² <a target="_blank" rel="noopener noreferrer" href="https://haveibeenpwned.com/Passwords">ÑпиÑку викрадених паролів</a>, раніше викритих в публічних витоках даних. Спробуйте ще раз з іншим паролем Ñ– подумайте про зміну цього Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð´ÐµÑ–Ð½Ð´Ðµ.
password_pwned_err=Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ запит до HaveIBeenPwed
+last_admin=Ðе можна видалити оÑтаннього адмініÑтратора. Повинен бути хоча б один адмініÑтратор.
+signin_passkey=Увійти за допомогою ключа доÑтупу
+back_to_sign_in=ПовернутиÑÑ Ð´Ð¾ авторизації
[mail]
view_it_on=ПереглÑнути на %s
-link_not_working_do_paste=Ðе працює? Спробуйте Ñкопіювати та вÑтавити його в Ñвій браузер.
+reply=або надішліть відповідь безпоÑередньо на цей електронний лиÑÑ‚
+link_not_working_do_paste=Ðе працює? Спробуйте Ñкопіювати та вÑтавити його у браузер.
hi_user_x=Привіт <b>%s</b>,
activate_account=Будь лаÑка, активуйте ваш обліковий запиÑ
activate_account.title=%s, будь лаÑка, активуйте Ñвій обліковий запиÑ
activate_account.text_1=Привіт, <b>%[1]s</b>, дÑкуємо за реєÑтрацію на %[2]s!
-activate_account.text_2=Перейдіть за цим поÑиланнÑм, щоб активувати ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð² <b>%s</b>:
+activate_account.text_2=Будь лаÑка, перейдіть за наÑтупним поÑиланнÑм, щоб активувати Ñвій обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¿Ñ€Ð¾Ñ‚Ñгом <b>%s</b>:
-activate_email=Підтвердить вашу адреÑу електронної пошти
-activate_email.text=Перейдіть за цим поÑиланнÑм, щоб підтвердити вашу електронну адреÑу в <b>%s</b>:
+activate_email=Підтвердіть адреÑу електронної пошти
+activate_email.title=%s, будь лаÑка, підтвердіть вашу адреÑу електронної пошти
+activate_email.text=Будь лаÑка, перейдіть за наÑтупним поÑиланнÑм протÑгом <b>%s</b>, щоб підтвердити Ñвою електронну адреÑу:
+register_notify=ЛаÑкаво проÑимо до %s
register_notify.title=%[1]s, лаÑкаво проÑимо до %[2]s
-register_notify.text_1=це ваша е-пошта Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ñ€ÐµÑ”Ñтрації Ð´Ð»Ñ %s!
+register_notify.text_1=це лиÑÑ‚ з підтвердженнÑм реєÑтрації на %s!
register_notify.text_2=Тепер ви можете увійти Ñк: %s.
-register_notify.text_3=Якщо цей обліковий Ð·Ð°Ð¿Ð¸Ñ Ð±ÑƒÐ»Ð¾ Ñтворено Ð´Ð»Ñ Ð²Ð°Ñ, будь лаÑка, Ñпочатку <a href="%s">вÑтановіть Ñвій пароль</a>.
+register_notify.text_3=Якщо цей обліковий Ð·Ð°Ð¿Ð¸Ñ Ñтворено Ð´Ð»Ñ Ð²Ð°Ñ, будь лаÑка, Ñпершу <a href="%s">вÑтановіть пароль</a>.
-reset_password=Ð’Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ облікового запиÑу
-reset_password.title=%s, ви відправили запит на Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу
-reset_password.text=Перейдіть за цим поÑиланнÑм, щоб відновити ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð² <b>%s</b>:
+reset_password=Відновити обліковий запиÑ
+reset_password.title=%s, ви надіÑлали запит на Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу
+reset_password.text=Щоб відновити обліковий запиÑ, перейдіть за наÑтупним поÑиланнÑм протÑгом <b>%s</b>:
register_success=РеєÑÑ‚Ñ€Ð°Ñ†Ñ–Ñ ÑƒÑпішна
-issue_assigned.pull=@%[1]s призначив вам запит Ð·Ð»Ð¸Ñ‚Ñ‚Ñ %[2]s в репозиторії %[3]s.
-issue_assigned.issue=@%[1]s призначив вам задачу %[2]s у репозиторії %[3]s.
+issue_assigned.pull=@%[1]s призначив вам запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ %[2]s в Ñховищі %[3]s.
+issue_assigned.issue=@%[1]s призначив вам задачу %[2]s в Ñховищі %[3]s.
issue.x_mentioned_you=<b>@%s</b> згадав ваÑ:
-issue.action.force_push=<b>%[1]s</b> force-pushed <b>%[2]s</b> з %[3]s в %[4]s.
-issue.action.push_1=<b>@%[1]s</b> надіÑлав %[3]d коміти %[2]s
-issue.action.push_n=<b>@%[1]s</b> відправив %[3]d коміти до %[2]s
-issue.action.close=<b>@%[1]s</b> закрито #%[2]d.
+issue.action.push_1=<b>@%[1]s</b> надіÑлав %[3]d коміт в %[2]s
+issue.action.push_n=<b>@%[1]s</b> надіÑлав %[3]d коміти до %[2]s
+issue.action.close=<b>@%[1]s</b> закрив #%[2]d.
issue.action.reopen=<b>@%[1]s</b> заново відкрив #%[2]d.
issue.action.merge=<b>@%[1]s</b> об'єднав #%[2]d до %[3]s.
-issue.action.approve=<b>@%[1]s</b> затвердили цей запит на злиттÑ.
-issue.action.reject=<b>@%[1]s</b> запитують зміни на цей запит на злиттÑ.
-issue.action.review=<b>@%[1]s</b> прокоментували цей запит на злиттÑ.
-issue.action.review_dismissed=<b>@%[1]s</b> відхилено оÑтанній відгук від %[2]s Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ запиту на злиттÑ.
-issue.action.ready_for_review=<b>@%[1]s</b> позначили цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñк готовий до розглÑду.
-issue.action.new=<b>@%[1]s</b> Ñтворили #%[2]d.
+issue.action.approve=<b>@%[1]s</b> затвердив цей запит на злиттÑ.
+issue.action.reject=<b>@%[1]s</b> запроÑив зміни у цьому запиті на злиттÑ.
+issue.action.review=<b>@%[1]s</b> прокоментував цей запит на злиттÑ.
+issue.action.review_dismissed=<b>@%[1]s</b> відхилив оÑтанній відгук від %[2]s на цей запит на злиттÑ.
+issue.action.ready_for_review=<b>@%[1]s</b> позначив цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñк готовий до розглÑду.
+issue.action.new=<b>@%[1]s</b> Ñтворив #%[2]d.
issue.in_tree_path=Ð’ %s:
release.new.subject=%s в %s випущено
@@ -370,35 +532,40 @@ release.downloads=ЗвантаженнÑ:
release.download.zip=Вихідний код (ZIP)
release.download.targz=Вихідний код (TAR.GZ)
-repo.transfer.subject_to=%s бажає передати"%s" в %s
-repo.transfer.subject_to_you=%s бажає передати"%s" вам
+repo.transfer.subject_to=%s хоче перенеÑти "%s" в %s
+repo.transfer.subject_to_you=%s хоче передати"%s" вам
repo.transfer.to_you=вам
repo.transfer.body=Щоб прийнÑти або відхилити перейдіть до %s або проÑто ігноруйте.
repo.collaborator.added.subject=%s додав Ð²Ð°Ñ Ð´Ð¾ %s
-repo.collaborator.added.text=Ви були додані в ÑкоÑті Ñпівавтора репозиторію:
+repo.collaborator.added.text=Ð’Ð°Ñ Ð´Ð¾Ð´Ð°Ð»Ð¸ Ñк Ñпівавтора до Ñховища:
+team_invite.subject=%[1]s запрошує Ð²Ð°Ñ Ð¿Ñ€Ð¸Ñ”Ð´Ð½Ð°Ñ‚Ð¸ÑÑ Ð´Ð¾ організації %[2]s
+team_invite.text_1=%[1]s запрошує Ð²Ð°Ñ Ð´Ð¾ команди %[2]s в організації %[3]s.
+team_invite.text_2=Перейдіть за поÑиланнÑм, щоб приєднатиÑÑ Ð´Ð¾ команди:
+team_invite.text_3=Примітка: Це Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ðµ Ð´Ð»Ñ %[1]s. Якщо ви не очікували цього запрошеннÑ, ви можете проігнорувати це повідомленнÑ.
[modal]
yes=Так
no=ÐÑ–
+confirm=Підтвердити
cancel=Відмінити
-modify=ОновленнÑ
+modify=Оновити
[form]
UserName=Ð†Ð¼â€™Ñ ÐºÐ¾Ñ€Ð¸Ñтувача
-RepoName=Ðазва репозиторію
+RepoName=Ðазва Ñховища
Email=ÐдреÑа електронної пошти
Password=Пароль
-Retype=ÐŸÑ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ
-SSHTitle=Iм'Ñ SSH ключа
+Retype=Підтвердити пароль
+SSHTitle=Ðазва ключа SSH
HttpsUrl=ÐдреÑа HTTPS
PayloadUrl=URL обробника
TeamName=Ðазва команди
AuthName=Ðазва авторизації
-AdminEmail=Email адмініÑтратора
+AdminEmail=ÐдреÑа електронної пошти адмініÑтратора
-NewBranchName=Ім'Ñ Ð½Ð¾Ð²Ð¾Ñ— гілки
+NewBranchName=Ðазва нової гілки
CommitSummary=Резюме коміту
CommitMessage=ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñƒ
CommitChoice=Вибір коміта
@@ -408,76 +575,127 @@ Content=ЗміÑÑ‚
SSPISeparatorReplacement=Розділювач
SSPIDefaultLanguage=Типова мова
-require_error=` не може бути пуÑтим.`
-alpha_dash_error=` повинен міÑтити тільки літерно-цифрові Ñимволи, Ð´ÐµÑ„Ñ–Ñ ('-') та підкреÑÐ»ÐµÐ½Ð½Ñ ('_'). `
-alpha_dash_dot_error=` повинен міÑтити тільки літерно-цифрові Ñимволи, Ð´ÐµÑ„Ñ–Ñ ('-') , підкреÑÐ»ÐµÐ½Ð½Ñ ('_') та точки ('.'). `
-git_ref_name_error=` повинен бути правильним поÑилальним ім'Ñм Git.`
+require_error=` не може бути порожнім.`
+alpha_dash_error=` повинен міÑтити тільки алфавітно-цифрові Ñимволи, Ð´ÐµÑ„Ñ–Ñ ('-') та підкреÑÐ»ÐµÐ½Ð½Ñ ('_').`
+alpha_dash_dot_error=` повинен міÑтити тільки алфавітно-цифрові Ñимволи, Ð´ÐµÑ„Ñ–Ñ ('-'), підкреÑÐ»ÐµÐ½Ð½Ñ ('_') та крапку ('.').`
+git_ref_name_error=` повинен бути правильно Ñформованим ім'Ñм-поÑиланнÑм на Git.`
size_error=` повинен бути розмір %s.`
-min_size_error=` повинен бути принаймні %s Ñимволів.`
-max_size_error=` повинен бути не більш Ñк %s Ñимволів.`
-email_error=` не Ñ” адреÑою електронної пошти.`
-glob_pattern_error=` неприпуÑтимий шаблон glob: %s.`
-regex_pattern_error=` неприпуÑтимий шаблон regex: %s.`
+min_size_error=` має міÑтити принаймні %s Ñимволів.`
+max_size_error=` має міÑтити не більше %s Ñимволів.`
+email_error=` не Ñ” дійÑною адреÑою електронної пошти.`
+url_error=`"%s" не Ñ” дійÑною URL-адреÑою.`
+include_error=` повинен міÑтити підрÑдок "%s".`
+glob_pattern_error=` недійÑний шаблон glob: %s.`
+regex_pattern_error=` недійÑний шаблон регулÑрного виразу: %s.`
+username_error=` може міÑтити лише алфавітно-цифрові Ñимволи ('0-9', 'a-z', 'A-Z'), Ð´ÐµÑ„Ñ–Ñ ('-'), підкреÑÐ»ÐµÐ½Ð½Ñ ('_') та крапку ('.'). Ðе може починатиÑÑ Ð°Ð±Ð¾ закінчуватиÑÑ Ð½ÐµÐ°Ð»Ñ„Ð°Ð²Ñ–Ñ‚Ð½Ð¸Ð¼Ð¸ Ñимволами; поÑлідовні неалфавітні Ñимволи також заборонені.`
unknown_error=Ðевідома помилка:
captcha_incorrect=Код CAPTCHA неправильний.
-password_not_match=Паролі не Ñпівпадають.
-lang_select_error=Оберіть мову з переліку.
-
-username_been_taken=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача вже зайнÑто.
-username_change_not_local_user=Ðелокальні кориÑтувачі не можуть змінити Ñвоє ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача.
-repo_name_been_taken=Ім'Ñ Ñ€ÐµÐ¿Ð¾Ð·Ñ–Ñ‚Ð¾Ñ€Ñ–ÑŽ вже викориÑтовуєтьÑÑ.
-repository_files_already_exist=Файли вже Ñ–Ñнують Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ репозитарію. ЗвернітьÑÑ Ð´Ð¾ ÑиÑтемного адмініÑтратора.
-repository_files_already_exist.adopt=Файли вже Ñ–Ñнують Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ репозиторію Ñ– можуть бути лише прийнÑті.
-repository_files_already_exist.delete=Файли вже Ñ–Ñнують Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ñховища. Ви повинні видалити Ñ—Ñ….
-repository_files_already_exist.adopt_or_delete=Файли вже Ñ–Ñнують Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ репозиторію. Їх можливо прийнÑти або видалити.
-visit_rate_limit=ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ ÑˆÐ²Ð¸Ð´ÐºÐ¾Ñті віддаленого доÑтупу.
-2fa_auth_required=Ð”Ð»Ñ Ð²Ñ–Ð´Ð´Ð°Ð»ÐµÐ½Ð¾Ð³Ð¾ доÑтупу необхідна двуфакторна аутентифікаціÑ.
-org_name_been_taken=Ðазва організації вже зайнÑто.
-team_name_been_taken=Ðазва команди вже зайнÑто.
-team_no_units_error=Дозволити доÑтуп до принаймні одного розділу репозитарію.
-email_been_used=Ð¦Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð° адреÑа вже викориÑтовуєтьÑÑ.
-email_invalid=ÐдреÑа електронної пошти помилкова.
+password_not_match=Паролі не збігаютьÑÑ.
+lang_select_error=Оберіть мову зі ÑпиÑку.
+
+username_been_taken=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача вже зайнÑте.
+username_change_not_local_user=Ðелокальні кориÑтувачі не можуть змінювати Ñвоє ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача.
+change_username_disabled=Зміна імені кориÑтувача відключена.
+change_full_name_disabled=Зміна повного імені відключена.
+username_has_not_been_changed=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача не змінено
+repo_name_been_taken=Ðазва Ñховища вже викориÑтовуєтьÑÑ.
+repository_force_private=ПримуÑову приватніÑть ввімкнено: приватні Ñховища не можна зробити загальнодоÑтупними.
+repository_files_already_exist=Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ñховища вже Ñ–Ñнують файли. ЗвернітьÑÑ Ð´Ð¾ ÑиÑтемного адмініÑтратора.
+repository_files_already_exist.adopt=Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ñховища вже Ñ–Ñнують файли, Ñ– Ñ—Ñ… можна лише прийнÑти.
+repository_files_already_exist.delete=Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ñховища вже Ñ–Ñнують файли. Ви повинні видалити Ñ—Ñ….
+repository_files_already_exist.adopt_or_delete=Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ñховища вже Ñ–Ñнують файли. Прийміть Ñ—Ñ… або видаліть.
+visit_rate_limit=Віддалений доÑтуп відхилено у зв'Ñзку з обмеженнÑм кількоÑті Ñпроб.
+2fa_auth_required=Ð”Ð»Ñ Ð²Ñ–Ð´Ð´Ð°Ð»ÐµÐ½Ð¾Ð³Ð¾ доÑтупу необхідна двофакторна автентифікаціÑ.
+org_name_been_taken=Ðазва організації вже зайнÑта.
+team_name_been_taken=Ðазва команди вже зайнÑта.
+team_no_units_error=Дозволити доÑтуп до принаймні одного розділу Ñховища.
+email_been_used=ÐдреÑа електронної пошти вже викориÑтовуєтьÑÑ.
+email_invalid=ÐдреÑа електронної пошти недійÑна.
+email_domain_is_not_allowed=Домен електронної пошти кориÑтувача <b>%s</b> конфліктує з EMAIL_DOMAIN_ALLOWLIST або EMAIL_DOMAIN_BLOCKLIST. ПереконайтеÑÑ, що ваша Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð°.
+openid_been_used=ÐдреÑа OpenID '%s' вже викориÑтовуєтьÑÑ.
username_password_incorrect=Ðеправильне ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача або пароль.
-password_complexity=Пароль не відповідає вимогам до ÑкладноÑті:
-password_lowercase_one=Принаймні одна буква в нижньому регіÑтрі
-password_uppercase_one=Принаймні одна буква в верхньому регіÑтрі
+password_complexity=Пароль не відповідає вимогам ÑкладноÑті:
+password_lowercase_one=Принаймні один Ñимвол нижнього регіÑтру
+password_uppercase_one=Принаймні один Ñимвол верхнього регіÑтру
password_digit_one=Принаймні одна цифра
-password_special_one=Принаймні один Ñпеціальний Ñимвол (пунктуаціÑ, дужки, лапки тощо)
-enterred_invalid_repo_name=Ðевірно введено ім'Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–ÑŽ.
-enterred_invalid_org_name=Ðевірно введено ім'Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ—.
-enterred_invalid_owner_name=Ім'Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ влаÑника не Ñ” дійÑним.
-enterred_invalid_password=Введений вами пароль некоректний.
-user_not_exist=Даний кориÑтувач не Ñ–Ñнує.
+password_special_one=Принаймні один Ñпеціальний Ñимвол (розділові знаки, дужки, лапки тощо)
+enterred_invalid_repo_name=Ви ввели неправильну назву Ñховища.
+enterred_invalid_org_name=Ви ввели неправильну назву організації.
+enterred_invalid_owner_name=Ім'Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ влаÑника недійÑне.
+enterred_invalid_password=Ви ввели неправильний пароль.
+unset_password=КориÑтувач не вÑтановив пароль.
+unsupported_login_type=Тип входу не підтримуєтьÑÑ Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу.
+user_not_exist=КориÑтувач не Ñ–Ñнує.
team_not_exist=Команда не Ñ–Ñнує.
-last_org_owner=Ви не можете видалити оÑтаннього кориÑтувача з команди 'влаÑники'. У кожній команді має бути принаймні один влаÑник.
+last_org_owner=Ви не можете видалити оÑтаннього кориÑтувача з групи 'влаÑників'. Ð’ організації має бути принаймні один влаÑник.
cannot_add_org_to_team=Організацію неможливо додати Ñк учаÑника команди.
-
-invalid_ssh_key=Ðеможливо перевірити ваш SSH ключ: %s
-invalid_gpg_key=Ðеможливо перевірити ваш GPG ключ: %s
-invalid_ssh_principal=Ðекоректний відповідальний: %s
+duplicate_invite_to_team=КориÑтувача вже запрошено Ñк члена команди.
+organization_leave_success=Ви уÑпішно покинули організацію %s.
+
+invalid_ssh_key=Ðе вдаєтьÑÑ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€Ð¸Ñ‚Ð¸ ключ SSH: %s
+invalid_gpg_key=Ðе вдаєтьÑÑ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€Ð¸Ñ‚Ð¸ ключ GPG: %s
+invalid_ssh_principal=Ðевірна ідентичніÑть: %s
+must_use_public_key=Ðаданий вами ключ — приватний. Будь лаÑка, нікуди не завантажуйте Ñвій приватний ключ. ÐатоміÑть викориÑтовуйте публічний ключ.
+unable_verify_ssh_key=Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€Ð¸Ñ‚Ð¸ ключ SSH, перевірте його на наÑвніÑть помилок.
auth_failed=Помилка автентифікації: %v
+still_own_repo=Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð²Ð¾Ð»Ð¾Ð´Ñ–Ñ” одним або декількома Ñховищами, Ñпершу видаліть або перенеÑіть Ñ—Ñ….
+still_has_org=Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ” учаÑником однієї або декількох організацій, Ñпершу залиште Ñ—Ñ….
+still_own_packages=Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð²Ð¾Ð»Ð¾Ð´Ñ–Ñ” одним або декількома пакетами, Ñпершу видаліть Ñ—Ñ….
+org_still_own_repo=Ð¦Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ Ð²Ñе ще володіє одним або декількома Ñховищами, Ñпочатку видаліть або перенеÑіть Ñ—Ñ….
+org_still_own_packages=Ð¦Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ Ð²Ñе ще має один або декілька пакетів, Ñпочатку видаліть Ñ—Ñ….
target_branch_not_exist=Цільової гілки не Ñ–Ñнує.
+target_ref_not_exist=Цільове поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ðµ Ñ–Ñнує %s
+admin_cannot_delete_self=Ви не можете видалити Ñебе, допоки ви адмініÑтратор. Будь лаÑка, Ñпочатку видаліть права адмініÑтратора.
[user]
-change_avatar=Змінити Ñвій аватар…
-repositories=Репозиторії
+change_avatar=Змінити аватар…
+joined_on=ПриєднавÑÑ(-лаÑÑŒ) %s
+repositories=Сховища
activity=Публічна активніÑть
-followers=Читачі
+followers=ПоÑлідовники
show_more=Показати більше
-starred=Обрані Репозиторії
-watched=ВідÑтежувані репозиторії
-projects=Проєкт
+starred=Обрані Ñховища
+watched=ВідÑтежувані Ñховища
+code=Код
+projects=Проєкти
overview=ОглÑд
-following=Читає
-follow=ПідпиÑатиÑÑ
-unfollow=ВідпиÑатиÑÑ
+following=ВідÑтежувані
+follow=Стежити
+unfollow=Ðе Ñтежити
user_bio=БіографіÑ
-disabled_public_activity=Цей кориÑтувач вимкнув публічний показ діÑльноÑті.
-
-
+disabled_public_activity=Цей кориÑтувач вимкнув публічну видиміÑть активноÑті.
+email_visibility.limited=Ваша електронна пошта видима Ð´Ð»Ñ Ð²ÑÑ–Ñ… автентифікованих кориÑтувачів
+email_visibility.private=Вашу адреÑу електронної пошти бачитимете лише ви та адмініÑтратори
+show_on_map=Показати це міÑце на карті
+settings=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувача
+
+form.name_reserved=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача "%s" зарезервовано.
+form.name_pattern_not_allowed=Шаблон "%s" не дозволено в імені кориÑтувача.
+form.name_chars_not_allowed=Ð†Ð¼â€™Ñ ÐºÐ¾Ñ€Ð¸Ñтувача "%s" міÑтить неприпуÑтимі Ñимволи.
+
+block.block=Заблокувати
+block.block.user=Заблокувати кориÑтувача
+block.block.org=Заблокувати кориÑтувача Ð´Ð»Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ—
+block.block.failure=Ðе вдалоÑÑ Ð·Ð°Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ñ‚Ð¸ кориÑтувача: %s
+block.unblock=Розблокувати
+block.unblock.failure=Ðе вдалоÑÑ Ñ€Ð¾Ð·Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ñ‚Ð¸ кориÑтувача: %s
+block.blocked=Ви заблокували цього кориÑтувача.
+block.title=Заблокувати кориÑтувача
+block.info=Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувача не дозволÑÑ” йому взаємодіÑти зі Ñховищами, наприклад, відкривати або коментувати запити на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð°Ð±Ð¾ задачі. ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувача.
+block.info_1=Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувача запобігає наÑтупним діÑм у вашому обліковому запиÑÑ– та ваших Ñховищах:
+block.info_2=Ñлідкують за вашим обліковим запиÑом
+block.info_3=надÑилати вам ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ @згадавши ваше ім'Ñ
+block.info_6=Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° ÐºÐ¾Ð¼ÐµÐ½Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡ або запитів на злиттÑ
+block.user_to_block=Блокувати КориÑтувача
+block.note=Примітка
+block.note.title=Ðеобов’Ñзкова примітка:
+block.note.info=Ðотатка не видима Ð´Ð»Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¾Ð³Ð¾ кориÑтувача.
+block.note.edit=Редагувати нотатку
+block.list=Заблоковані кориÑтувачі
+block.list.none=Ви не заблокували жодного кориÑтувача.
[settings]
profile=Профіль
@@ -486,336 +704,457 @@ appearance=Зовнішній виглÑд
password=Пароль
security=Безпека
avatar=Ðватар
-ssh_gpg_keys=SSH / GPG ключі
+ssh_gpg_keys=Ключі SSH / GPG
social=Соціальні облікові запиÑи
applications=Додатки
orgs=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñми
repos=Репозиторії
delete=Видалити обліковий запиÑ
+twofa=Двофакторна Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ (TOTP)
account_link=Прив'Ñзані облікові запиÑи
organization=Організації
+uid=UID
+webauthn=Двофакторна Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ (ключі безпеки)
public_profile=ЗагальнодоÑтупний профіль
+biography_placeholder=Розкажіть нам трохи про Ñебе! (Ви можете викориÑтовувати Markdown)
+location_placeholder=ДілитиÑÑ Ñвоїм приблизним географічним положеннÑм з іншими
+profile_desc=Керуйте тим, Ñк ваш профіль буде показано іншим кориÑтувачам. Ваша оÑновна адреÑа електронної пошти викориÑтовуватиметьÑÑ Ð´Ð»Ñ Ñповіщень, Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ Ñ‚Ð° веб-операцій Git.
+password_username_disabled=Вам не дозволено змінювати ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача. Будь лаÑка, зв'ÑжітьÑÑ Ð· адмініÑтратором Ñайту Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð±Ñ–Ð»ÑŒÑˆ докладної інформації.
+password_full_name_disabled=Вам не дозволено змінювати ваше ім'Ñ. Будь лаÑка, зв'ÑжітьÑÑ Ð· адмініÑтратором Ñайту Ð´Ð»Ñ Ð±Ñ–Ð»ÑŒÑˆ докладної інформації.
full_name=Повне ім'Ñ
website=Веб-Ñайт
location=МіÑцезнаходженнÑ
update_theme=Оновити тему
update_profile=Оновити профіль
update_language=Оновити мову
+update_language_not_found=Мова "%s" недоÑтупна.
update_language_success=Мову оновлено.
update_profile_success=Профіль уÑпішно оновлено.
-change_username=Ваше Ім'Ñ ÐºÑ€Ð¸Ñтувача було змінено.
+change_username=Ваше ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача змінено.
+change_username_prompt=Примітка: Зміна імені кориÑтувача також змінює URL-адреÑу облікового запиÑу.
+change_username_redirect_prompt=Старе ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача буде перенаправлÑтиÑÑ Ð½Ð° нове, поки хтоÑÑŒ не викориÑтає його.
continue=Продовжити
cancel=Відмінити
language=Мова
ui=Тема
+hidden_comment_types=Приховані типи коментарів
+hidden_comment_types_description=Позначені тут типи коментарів не будуть показані на Ñторінках проблем. Ðаприклад, позначка «Мітка» вилучає вÑÑ– коментарі «{user} додав/вилучив {label}».
+hidden_comment_types.ref_tooltip=Коментарі, де на цю задачу було зроблено поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð· іншого випуÑку/коміту/…
+hidden_comment_types.issue_ref_tooltip=Коментарі, в Ñких кориÑтувач змінює гілку/тег, пов'Ñзані з нею
+comment_type_group_reference=ПоÑиланнÑ
comment_type_group_label=Мітка
comment_type_group_milestone=Етап
comment_type_group_assignee=Виконавець
comment_type_group_title=Заголовок
comment_type_group_branch=Гілка
+comment_type_group_time_tracking=Облік чаÑу
+comment_type_group_deadline=Крайній Ñтрок
+comment_type_group_dependency=ЗалежніÑть
+comment_type_group_lock=Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ
+comment_type_group_review_request=Запит на перевірку
+comment_type_group_pull_request_push=Додані коміти
comment_type_group_project=Проєкт
+comment_type_group_issue_ref=ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° задачу
+saved_successfully=Ваші Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÑпішно збережено.
privacy=ПриватніÑть
+keep_activity_private=Приховати активніÑть зі Ñторінки профілю
keep_activity_private_popup=Показувати вашу активніÑть лише Вам та адмініÑтраторам
lookup_avatar_by_mail=Знайти Ðватар за адреÑою електронної пошти
federated_avatar_lookup=Знайти зовнішній аватар
-enable_custom_avatar=Увімкнути кориÑтувацькі аватари
-choose_new_avatar=Оберіть новий аватар
+enable_custom_avatar=Увімкнути кориÑтувацький аватар
+choose_new_avatar=Обрати новий аватар
update_avatar=Оновити аватар
delete_current_avatar=Видалити поточний аватар
uploaded_avatar_not_a_image=Завантажений файл не Ñ” зображеннÑм.
-update_avatar_success=Ваш аватар був змінений.
+uploaded_avatar_is_too_big=Розмір завантаженого файлу (%d KiB) перевищує макÑимальний розмір (%d KiB).
+update_avatar_success=Ваш аватар оновлено.
update_user_avatar_success=Ðватар кориÑтувача оновлено.
+cropper_prompt=Ви можете відредагувати Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÐµÐ´ збереженнÑм. Відредаговане Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð±ÑƒÐ´Ðµ збережено Ñк PNG.
change_password=Оновити пароль
old_password=Поточний пароль
new_password=Ðовий пароль
+retype_new_password=Підтвердити новий пароль
password_incorrect=Поточний пароль неправильний.
-change_password_success=Ваш пароль був оновлений. Тепер увійдіть в ÑиÑтему, викориÑтовуючи новий пароль.
-password_change_disabled=Ðелокальні акаунти не можуть змінити пароль через Gitea.
+change_password_success=Ваш пароль оновлено. Відтепер входьте в ÑиÑтему, викориÑтовуючи новий пароль.
+password_change_disabled=Ðелокальні кориÑтувачі не можуть оновити Ñвій пароль через Ð²ÐµÐ±Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Gitea.
emails=ÐдреÑа електронної пошти
-manage_emails=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð´Ñ€ÐµÑами ел. пошти
-manage_themes=Виберіть тему за замовчуваннÑм
-manage_openid=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ OpenID
+manage_emails=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð´Ñ€ÐµÑами електронної пошти
+manage_themes=Обрати типову тему
+manage_openid=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð´Ñ€ÐµÑами OpenID
+email_desc=Ваша оÑновна адреÑа електронної пошти викориÑтовуватиметьÑÑ Ð´Ð»Ñ Ñповіщень, Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ Ñ–, за умови, що вона не прихована, Ð´Ð»Ñ Ð²ÐµÐ±-операцій з Git.
theme_desc=Ð¦Ñ Ñ‚ÐµÐ¼Ð° буде типовою Ð´Ð»Ñ Ð²Ñього Ñайту.
+theme_colorblindness_help=Підтримка тем колірної Ñліпоти
+theme_colorblindness_prompt=Gitea щойно отримала деÑкі теми з базовою підтримкою колірної Ñліпоти, в Ñких визначено лише кілька кольорів. Робота над вÑе ще триває. Ще більше покращень можна зробити, визначивши більше кольорів у CSS-файлах теми.
primary=ОÑновний
activated=Ðктивовано
requires_activation=Потрібна активаціÑ
-primary_email=Зробити оÑновним
+primary_email=Зробити оÑновною
activate_email=ÐадіÑлати активацію
-activations_pending=Ðктивації в очікуванні
+activations_pending=ÐžÑ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ñ–Ñ—
+can_not_add_email_activations_pending=ВідбуваєтьÑÑ Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ñ–Ñ, Ñпробуйте ще раз через кілька хвилин, Ñкщо хочете додати нову адреÑу електронної пошти.
delete_email=Видалити
email_deletion=Видалити адреÑу електронної пошти
-email_deletion_desc=Електронна адреÑа та пов'Ñзана з нею Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð±ÑƒÐ´Ðµ видалена з вашого облікового запиÑу. Git коміти, здійÑнені через цю електронну адреÑу, залишитьÑÑ Ð±ÐµÐ· змін. Продовжити?
-email_deletion_success=ÐдреÑу електронної пошти було видалено.
+email_deletion_desc=ÐдреÑа електронної пошти та пов'Ñзана з нею Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð±ÑƒÐ´Ðµ видалена з вашого облікового запиÑу. Коміти Git, здійÑнені через цю адреÑу електронну пошту, залишитьÑÑ Ð±ÐµÐ· змін. Продовжити?
+email_deletion_success=ÐдреÑу електронної пошти видалено.
theme_update_success=Тему оновлено.
-theme_update_error=Вибрана тема не Ñ–Ñнує.
+theme_update_error=Обрана тема не Ñ–Ñнує.
openid_deletion=Видалити адреÑу OpenID
-openid_deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— OpenID-адреÑи з вашого облікового запиÑу заборонÑÑ” вам входити з ним. Продовжити?
-openid_deletion_success=ÐдреÑа OpenID була видалена.
+openid_deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— адреÑи OpenID не дозволить вам увійти за нею. Продовжити?
+openid_deletion_success=ÐдреÑу OpenID видалено.
add_new_email=Додати нову адреÑу електронної пошти
add_new_openid=Додати новий OpenID URI
add_email=Додати адреÑу електронної пошти
add_openid=Додати OpenID URI
+add_email_confirmation_sent=Електронний лиÑÑ‚ із підтвердженнÑм було відправлено на '%s', будь лаÑка, перевірте вашу поштову Ñкриньку протÑгом наÑтупних %s, щоб підтвердити адреÑу.
add_email_success=Додано нову адреÑу електронної пошти.
-email_preference_set_success=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти уÑпішно вÑтановлені.
-add_openid_success=Ðова адреÑа OpenID була додана.
+email_preference_set_success=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти уÑпішно вÑтановлено.
+add_openid_success=Додано нову адреÑу OpenID.
keep_email_private=Приховати адреÑу електронної пошти
-openid_desc=OpenID дозволÑÑ” делегувати аутентифікацію зовнішньому поÑтачальнику поÑлуг.
+openid_desc=OpenID дозволÑÑ” делегувати автентифікацію зовнішньому поÑтачальнику поÑлуг.
-manage_ssh_keys=Керувати SSH ключами
-manage_ssh_principals=Ð£Ð¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ SSH Ñертифікатами кориÑтувачів
-manage_gpg_keys=Керувати GPG ключами
+manage_ssh_keys=Керувати ключами SSH
+manage_ssh_principals=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð´ÐµÐ½Ñ‚Ð¸Ñ‡Ð½Ð¾ÑÑ‚Ñми Ñертифікатів SSH
+manage_gpg_keys=Керувати ключами SSH
add_key=Додати ключ
-ssh_desc=Ці відкриті SSH-ключі пов'Ñзані з вашим обліковим запиÑом. Відповідні приватні ключі дозволÑють отримати повний доÑтуп до ваших репозиторіїв.
-principal_desc=Ці наÑтройки SSH Ñертифікатів вказані у вашому обліковому запиÑÑ– та надають повний доÑтуп до ваших репозиторіїв.
-gpg_desc=Ці публічні ключі GPG пов'Ñзані з вашим обліковим запиÑом. Тримайте Ñвої приватні ключі в безпеці, оÑкільки вони дозволÑють здійÑнювати перевірку комітів.
-ssh_helper=<strong>Потрібна допомога?</strong> ДивітьÑÑ Ð³Ñ–Ð´ на GitHub з <a href="%s"> генерації ключів SSH</a> або Ð²Ð¸Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ <a href="%s">типових неполадок SSH</a>.
-gpg_helper=<strong> Потрібна допомога? </strong> ПереглÑньте поÑібник GitHub <a href="%s"> про GPG </a>.
-add_new_key=Додати SSH ключ
-add_new_gpg_key=Додати GPG ключ
+ssh_desc=Ці публічні ключі SSH пов'Ñзані з вашим обліковим запиÑом. Відповідні приватні ключі надають повний доÑтуп до ваших Ñховищ.
+principal_desc=Ці ідентифікатори Ñертифікатів SSH прив'Ñзані до вашого облікового запиÑу Ñ– надають повний доÑтуп до ваших Ñховищ.
+gpg_desc=Ці публічні ключі GPG пов'Ñзані з вашим обліковим запиÑом. Зберігайте Ñвої приватні ключі в безпеці, оÑкільки вони дозволÑють підтверджувати коміти.
+ssh_helper=<strong>Потрібна допомога?</strong> ОзнайомтеÑÑ Ð· інÑтрукцією GitHub щодо <a href="%s">ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð²Ð»Ð°Ñних ключів SSH</a> або Ð²Ð¸Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ <a href="%s">типових неполадок SSH.</a>
+gpg_helper=<strong>Потрібна допомога?</strong> ПереглÑньте поÑібник GitHub <a href="%s">про GPG</a>.
+add_new_key=Додати ключ SSH
+add_new_gpg_key=Додати ключ GPG
key_content_ssh_placeholder=ПочинаєтьÑÑ Ð· 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', або 'sk-ssh-ed25519@openssh.com'
key_content_gpg_placeholder=ПочинаєтьÑÑ Ð· '-----BEGIN PGP PUBLIC KEY BLOCK-----'
-add_new_principal=Додати кориÑтувача
-ssh_key_been_used=Цей SSH ключ вже був додано до Ñервера.
+add_new_principal=Додати ідентичніÑть
+ssh_key_been_used=Цей ключ SSH вже було додано до Ñервера.
ssh_key_name_used=Ключ SSH з таким ім'Ñм вже Ñ–Ñнує у вашому обліковому запиÑÑ–.
-ssh_principal_been_used=Цей кориÑтувач вже був доданий на Ñервер.
+ssh_principal_been_used=Цю ідентичніÑть вже було додано до Ñервера.
gpg_key_id_used=Публічний ключ GPG з таким Ñамим ідентифікатором вже Ñ–Ñнує.
-gpg_no_key_email_found=Цей ключ GPG не відповідає жодній активованій поштовій адреÑÑ–, Ñка пов'Ñзана з вашим обліковим запиÑом. Його вÑе рівно можна додати, Ñкщо ви підпишете наданий токен.
-gpg_key_matched_identities=Відповідні отримувачі:
-gpg_key_matched_identities_long=Вбудовані ідентифікатори цього ключа збігаютьÑÑ Ð· наÑтупними активованими адреÑами електронної пошти вказаного кориÑтувача. Коміти, Ñкі відповідають цим адреÑам, можуть бути підтверджені цим ключем.
+gpg_no_key_email_found=Цей ключ GPG не відповідає жодній активованій адреÑÑ– електронної пошти, пов'Ñзаній з вашим обліковим запиÑом. Його вÑе одно можна додати, Ñкщо ви підпишете наданий токен.
+gpg_key_matched_identities=Відповідні ідентичноÑті:
+gpg_key_matched_identities_long=Вбудовані ідентифікатори цього ключа збігаютьÑÑ Ð· наÑтупними активованими адреÑами електронної пошти цього кориÑтувача. За допомогою цього ключа можна перевірÑти коміти, що відповідають цим адреÑам електронної пошти.
gpg_key_verified=Перевірений ключ
-gpg_key_verified_long=Ключ перевірений за допомогою токена Ñ– може бути викориÑтано Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ–Ð², Ñкі відповідають будь-Ñкій з активованих Ð°Ð´Ñ€ÐµÑ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ кориÑтувача, на додачу до будь-Ñких відповідних ідентифікацій Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ ключа.
+gpg_key_verified_long=Ключ був перевірений токеном Ñ– може бути викориÑтаний Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ комітів, що відповідають будь-Ñким активованим адреÑам електронної пошти Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ кориÑтувача, а також будь-Ñких ідентифікаторів, що відповідають цьому ключу.
gpg_key_verify=Підтвердити
-gpg_invalid_token_signature=Ðаданий ключ GPG, Ð¿Ñ–Ð´Ð¿Ð¸Ñ Ñ– токен не Ñпівпадають або токен заÑтарів.
+gpg_invalid_token_signature=Ðадані ключ GPG, Ð¿Ñ–Ð´Ð¿Ð¸Ñ Ñ– токен не збігаютьÑÑ Ð°Ð±Ð¾ токен заÑтарілий.
gpg_token_required=Вам потрібно надати Ð¿Ñ–Ð´Ð¿Ð¸Ñ Ð´Ð»Ñ Ð½Ð¸Ð¶Ñ‡ÐµÐ²ÐºÐ°Ð·Ð°Ð½Ð¾Ð³Ð¾ токена
gpg_token=Токен
gpg_token_help=Ви можете Ñтворити Ð¿Ñ–Ð´Ð¿Ð¸Ñ Ð·Ð° допомогою:
gpg_token_signature=ТекÑтовий (armored) Ð¿Ñ–Ð´Ð¿Ð¸Ñ GPG
key_signature_gpg_placeholder=`ПочинаєтьÑÑ Ð· "-----BEGIN PGP SIGNATURE-----"`
+verify_gpg_key_success=Ключ GPG '%s' перевірено.
ssh_key_verified=Перевірений ключ
-ssh_key_verify=Підтвердити
+ssh_key_verified_long=Ключ було перевірено за допомогою токена. Його можна викориÑтовувати Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ комітів, що відповідають будь-Ñким активованим адреÑам електронної пошти цього кориÑтувача.
+ssh_key_verify=Перевірити
+ssh_invalid_token_signature=Ключ SSH, Ð¿Ñ–Ð´Ð¿Ð¸Ñ Ñ– токен не збігаютьÑÑ Ð°Ð±Ð¾ токен заÑтарілий.
ssh_token_required=Вам потрібно надати Ð¿Ñ–Ð´Ð¿Ð¸Ñ Ð´Ð»Ñ Ð½Ð¸Ð¶Ñ‡ÐµÐ²ÐºÐ°Ð·Ð°Ð½Ð¾Ð³Ð¾ токена
ssh_token=Токен
ssh_token_help=Ви можете Ñтворити Ð¿Ñ–Ð´Ð¿Ð¸Ñ Ð·Ð° допомогою:
+ssh_token_signature=ТекÑтовий Ð¿Ñ–Ð´Ð¿Ð¸Ñ SSH
+key_signature_ssh_placeholder=`ПочинаєтьÑÑ Ð· "-----BEGIN SSH SIGNATURE-----"`
+verify_ssh_key_success=Ключ SSH '%s' перевірено.
subkeys=Підключі
-key_id=ID ключа
-key_name=Ім'Ñ ÐºÐ»ÑŽÑ‡Ð°
+key_id=Ідентифікатор ключа
+key_name=Ðазва ключа
key_content=ЗміÑÑ‚
principal_content=ЗміÑÑ‚
+add_key_success=Ключ SSH '%s' додано.
+add_gpg_key_success=Ключ GPG '%s' додано.
+add_principal_success=Було додано SSH Ñертифікат ідентичноÑті '%s'.
delete_key=Видалити
-ssh_key_deletion=Видалити SSH ключ
-gpg_key_deletion=Видалити GPG ключ
-ssh_principal_deletion=Видалити SSH Ñертифікат кориÑтувача
+ssh_key_deletion=Видалити ключ SSH
+gpg_key_deletion=Видалити ключ GPG
+ssh_principal_deletion=Видалити ідентичніÑть Ñертифікату SSH
ssh_key_deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ ÐºÐ»ÑŽÑ‡Ð° SSH ÑкаÑовує доÑтуп до вашого облікового запиÑу. Продовжити?
-gpg_key_deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ GPG ключа ÑкаÑовує перевірку підпиÑаних ним комітів. Продовжити?
-ssh_principal_deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ ÐºÐ»ÑŽÑ‡Ð° SSH ÑкаÑовує доÑтуп до вашого облікового запиÑу. Продовжити?
-ssh_key_deletion_success=SSH ключ був видалений.
-gpg_key_deletion_success=GPG було видалено.
-ssh_principal_deletion_success=КориÑтувача видалено.
+gpg_key_deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ ÐºÐ»ÑŽÑ‡Ð° GPG ÑкаÑовує перевірку підпиÑаних ним комітів. Продовжити?
+ssh_principal_deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ–Ð´ÐµÐ½Ñ‚Ð¸Ñ‡Ð½Ð¾Ñті Ñертифіката SSH ÑкаÑовує доÑтуп до вашого облікового запиÑу. Продовжити?
+ssh_key_deletion_success=Ключ SSH видалено.
+gpg_key_deletion_success=Ключ GPG видалено.
+ssh_principal_deletion_success=ІдентичніÑть видалено.
+added_on=Додано %s
+valid_until_date=ДійÑно до %s
valid_forever=ДійÑний завжди
-last_used=ОÑтаннє викориÑтаннÑ
-no_activity=Жодної діÑльноÑті
-can_read_info=Читати
-can_write_info=ÐапиÑати
-key_state_desc=Цей ключ викориÑтовувавÑÑ Ð² оÑтанні 7 днів
-token_state_desc=Цей токен викориÑтовувавÑÑ Ð² оÑтанні 7 днів
-principal_state_desc=УчаÑтник був на Ñайті в оÑтанні 7 днів
+last_used=ВоÑтаннє викориÑтано
+no_activity=ÐÐµÑ‰Ð¾Ð´Ð°Ð²Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ–Ñть відÑутнÑ
+can_read_info=ЧитаннÑ
+can_write_info=ЗапиÑ
+key_state_desc=Цей ключ викориÑтовувавÑÑ Ð¿Ñ€Ð¾Ñ‚Ñгом оÑтанніх 7 днів
+token_state_desc=Цей токен викориÑтовувавÑÑ Ð¿Ñ€Ð¾Ñ‚Ñгом оÑтанніх 7 днів
+principal_state_desc=Ð¦Ñ Ñ–Ð´ÐµÐ½Ñ‚Ð¸Ñ‡Ð½Ñ–Ñть викориÑтовувалаÑÑ Ð¿Ñ€Ð¾Ñ‚Ñгом оÑтанніх 7 днів
show_openid=Показати у профілю
-hide_openid=Ðе показувати у профілі
+hide_openid=Приховати з профілю
ssh_disabled=SSH вимкнено
-ssh_externally_managed=Цей ключ SSH має зовнішнє ÑƒÐ¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ кориÑтувача
-manage_social=Керувати зв'Ñзаними обліковими запиÑами Ñоціальних мереж
+ssh_signonly=SSH наразі вимкнено, тому ці ключі викориÑтовуютьÑÑ Ð»Ð¸ÑˆÐµ Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ підпиÑу комітів.
+ssh_externally_managed=Цей ключ SSH керуєтьÑÑ Ð·Ð·Ð¾Ð²Ð½Ñ– Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ кориÑтувача
+manage_social=Керувати пов'Ñзаними обліковими запиÑами Ñоціальних мереж
+social_desc=Ці облікові запиÑи Ñоціальних мереж можна викориÑтовувати Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ в ваш обліковий запиÑ. ПереконайтеÑÑ, що вÑÑ– вони належать вам.
unbind=Від'єднати
+unbind_success=Соціальний обліковий Ð·Ð°Ð¿Ð¸Ñ ÑƒÑпішно видалено.
-manage_access_token=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð¾ÐºÐµÐ½Ð°Ð¼Ð¸ доÑтупу
-generate_new_token=Згенерувати новий токен
-tokens_desc=Ці токени надають доÑтуп до вашого облікового запиÑу за допомогою Gitea API.
-token_name=Ім'Ñ Ñ‚Ð¾ÐºÐµÐ½Ñƒ
+manage_access_token=Керувати токенами доÑтупу
+generate_new_token=Створити новий токен
+tokens_desc=Ці токени надають доÑтуп до вашого облікового запиÑу за допомогою API Gitea.
+token_name=Ðазва токену
generate_token=Згенерувати токен
-generate_token_success=Ваш новий токен був Ñтворений. Скопіюйте його зараз, оÑкільки він не буде показаний знову.
+generate_token_success=Ваш новий токен Ñтворено. Скопіюйте його зараз, оÑкільки він не буде показаний знову.
generate_token_name_duplicate=Ðазва програми <strong>%s</strong> вже викориÑтовуєтьÑÑ. Будь лаÑка, викориÑтайте нову.
delete_token=Видалити
access_token_deletion=Видалити токен доÑтупу
access_token_deletion_cancel_action=Відмінити
access_token_deletion_confirm_action=Видалити
-delete_token_success=Токен був знищений. Програми, що викориÑтовують його, більше не мають доÑтупу до вашого облікового запиÑу.
-permission_read=Прочитані
-
-manage_oauth2_applications=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð°Ð¼Ð¸ OAuth2
-edit_oauth2_application=Редагувати програму OAuth2
-oauth2_applications_desc=Програми OAuth2 дають можливіÑть вашим Ñтороннім програмам надійно аутентифікувати кориÑтувачів у цьому екземплÑрі Gitea.
-remove_oauth2_application=Видалити програму OAuth2
-remove_oauth2_application_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¸ OAuth2 ÑкаÑовує доÑтуп до вÑÑ–Ñ… підпиÑаних маркерів доÑтупу. Продовжити?
-remove_oauth2_application_success=Програму видалено.
-create_oauth2_application=Створити нову програму OAuth2
-create_oauth2_application_button=Створити програму
-oauth2_application_name=Ðазва програми
+access_token_deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ‚Ð¾ÐºÐµÐ½Ð° призведе до Ð²Ñ–Ð´ÐºÐ»Ð¸ÐºÐ°Ð½Ð½Ñ Ð´Ð¾Ñтупу до вашого облікового запиÑу Ð´Ð»Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÑ–Ð², Ñкі його викориÑтовують. Це неможливо ÑкаÑувати. Продовжити?
+delete_token_success=Токен знищено. Додатки, що викориÑтовують його, більше не мають доÑтупу до вашого облікового запиÑу.
+repo_and_org_access=ДоÑтуп до Ñховища та організації
+permissions_public_only=Лише загальнодоÑтупні
+permissions_access_all=Ð’ÑÑ– (загальнодоÑтупні, приватні та з обмеженим доÑтупом)
+permission_not_set=Ðе вÑтановлено
+permission_no_access=Ðемає доÑтупу
+permission_read=ЧитаннÑ
+permission_write=Ð§Ð¸Ñ‚Ð°Ð½Ð½Ñ Ñ– запиÑ
+permission_anonymous_read=Ðнонімне читаннÑ
+permission_everyone_read=Ð§Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð²ÑÑ–Ñ…
+permission_everyone_write=Ð—Ð°Ð¿Ð¸Ñ Ð´Ð»Ñ Ð²ÑÑ–Ñ…
+access_token_desc=Обрані дозволи токена обмежують авторизацію лише відповідними маршрутами <a %s>API</a>. Читайте <a %s>документацію</a> Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ñ— інформації.
+at_least_one_permission=Ðеобхідно вибрати хоча б одне право доÑтупу Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ‚Ð¾ÐºÐµÐ½Ð°
+permissions_list=Дозволи:
+
+manage_oauth2_applications=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ°Ð¼Ð¸ OAuth2
+edit_oauth2_application=Редагувати додаток OAuth2
+oauth2_applications_desc=Додатки OAuth2 дозволÑють вашому Ñторонньому додатку безпечно автентифікувати кориÑтувачів у цьому екземплÑрі Gitea.
+remove_oauth2_application=Видалити додаток OAuth2
+remove_oauth2_application_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ° OAuth2 ÑкаÑує доÑтуп до вÑÑ–Ñ… підпиÑаних токенів доÑтупу. Продовжити?
+remove_oauth2_application_success=Додаток видалено.
+create_oauth2_application=Створити новий додаток OAuth2
+create_oauth2_application_button=Створити додаток
+create_oauth2_application_success=Ви уÑпішно Ñтворили новий додаток OAuth2.
+update_oauth2_application_success=Ви уÑпішно оновили додаток OAuth2.
+oauth2_application_name=Ðазва додатка
+oauth2_confidential_client=Конфіденційний клієнт. Виберіть Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼, Ñкі зберігають конфіденційніÑть, наприклад, веб-програм. Ðе обирайте Ð´Ð»Ñ Ð½Ð°Ñ‚Ð¸Ð²Ð½Ð¸Ñ… додатків, зокрема ПК та мобільних додатків.
+oauth2_skip_secondary_authorization=ПропуÑтити авторизацію Ð´Ð»Ñ Ð¿ÑƒÐ±Ð»Ñ–Ñ‡Ð½Ð¸Ñ… клієнтів піÑÐ»Ñ Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупу один раз. <strong>Може Ñтановити ризик Ð´Ð»Ñ Ð±ÐµÐ·Ð¿ÐµÐºÐ¸.</strong>
+oauth2_redirect_uris=URI Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ. Будь лаÑка, викориÑтовуйте новий Ñ€Ñдок Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ URI.
save_application=Зберегти
-oauth2_client_id=ID Клієнта
+oauth2_client_id=Ідентифікатор клієнта
oauth2_client_secret=Ключ клієнта
oauth2_regenerate_secret=Відновити ключ
-oauth2_regenerate_secret_hint=Ви втратили Ñвій ключ?
+oauth2_regenerate_secret_hint=Втратили ключ?
oauth2_application_edit=Редагувати
oauth2_application_create_description=Програми OAuth2 надають вашим Ñтороннім програмам доÑтуп до облікових запиÑів кориÑтувачів у цьому екземплÑрі.
+oauth2_application_remove_description=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ OAuth2 не дозволить додатку отримати доÑтуп до авторизованих облікових запиÑів кориÑтувачів на цьому Ñервері. Продовжити?
+oauth2_application_locked=Gitea попередньо реєÑтрує деÑкі програми OAuth2 під Ñ‡Ð°Ñ Ð·Ð°Ð¿ÑƒÑку, Ñкщо це ввімкнено в конфігурації. Щоб запобігти неÑподіваній поведінці, Ñ—Ñ… не можна ні редагувати, ні видалÑти. Ð”Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ñ— інформації звернітьÑÑ Ð´Ð¾ документації OAuth2.
authorized_oauth2_applications=Ðвторизовані програми OAuth2
+authorized_oauth2_applications_description=Ви надали цим Ñтороннім додаткам доÑтуп до Ñвого облікового запиÑу Gitea. СкаÑуйте доÑтуп Ð´Ð»Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÑ–Ð², Ñкі вам більше не потрібні.
revoke_key=Відкликати
revoke_oauth2_grant=СкаÑувати доÑтуп
-revoke_oauth2_grant_description=СкаÑÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупу Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— програми третьої Ñторони не дозволить їй отримувати доÑтуп до ваших даних. Ви впевнені?
+revoke_oauth2_grant_description=СкаÑÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупу Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ñтороннього додатка не дозволить йому отримувати доÑтуп до ваших даних. Ви впевнені?
+revoke_oauth2_grant_success=ДоÑтуп уÑпішно ÑкаÑовано.
-twofa_is_enrolled=Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð½Ð° даний Ñ‡Ð°Ñ <strong>викориÑтовує</strong> двофакторну автентифікацію.
-twofa_not_enrolled=Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð½Ð°Ñ€Ð°Ð·Ñ– не викориÑтовує двофакторну автентифікаціїю.
+twofa_desc=Щоб захиÑтити Ñвій обліковий Ð·Ð°Ð¿Ð¸Ñ Ð²Ñ–Ð´ крадіжки паролÑ, ви можете викориÑтовувати Ñмартфон або інший приÑтрій Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð¾Ð´Ð½Ð¾Ñ€Ð°Ð·Ð¾Ð²Ð¸Ñ… паролів, прив'Ñзаних до чаÑу (TOTP).
+twofa_recovery_tip=Якщо ви втратите Ñвій приÑтрій, ви зможете ÑкориÑтатиÑÑ Ð¾Ð´Ð½Ð¾Ñ€Ð°Ð·Ð¾Ð²Ð¸Ð¼ ключем відновленнÑ, щоб відновити доÑтуп до Ñвого облікового запиÑу.
+twofa_is_enrolled=Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð½Ð°Ñ€Ð°Ð·Ñ– <strong>викориÑтовує</strong> двофакторну автентифікацію.
+twofa_not_enrolled=Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð½Ð°Ñ€Ð°Ð·Ñ– не викориÑтовує двофакторну автентифікацію.
twofa_disable=Вимкнути двофакторну автентифікацію
+twofa_scratch_token_regenerate=Регенерувати одноразовий ключ відновленнÑ
+twofa_scratch_token_regenerated=Ваш одноразовий ключ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‚ÐµÐ¿ÐµÑ€ %s. Зберігайте його у безпечному міÑці, оÑкільки його більше не буде показано.
twofa_enroll=Увімкнути двофакторну автентифікацію
-twofa_disable_note=При необхідноÑті можна відключити двофакторну автентифікацію.
+twofa_disable_note=За потреби ви можете вимкнути двофакторну автентифікацію.
twofa_disable_desc=Ð’Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ð´Ð²Ð¾Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð¾Ñ— автентифікації зробить ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¼ÐµÐ½Ñˆ безпечним. Продовжити?
-twofa_disabled=Двофакторна Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð°.
-scan_this_image=ПроÑкануйте це Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð²Ð°ÑˆÐ¸Ð¼ додатком Ð´Ð»Ñ Ð´Ð²ÑƒÑ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð¾Ñ— автентифікації:
-or_enter_secret=Ðбо введіть Ñекрет: %s
+regenerate_scratch_token_desc=Якщо ви втратили ключ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ вже викориÑтовували його Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ, ви можете Ñкинути його тут.
+twofa_disabled=Двофакторну автентифікацію вимкнено.
+scan_this_image=ВідÑкануйте це Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð²Ð°ÑˆÐ¸Ð¼ додатком Ð´Ð»Ñ Ð´Ð²Ð¾Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð¾Ñ— автентифікації:
+or_enter_secret=Ðбо введіть код: %s
then_enter_passcode=І введіть пароль, Ñкий відображаєтьÑÑ Ð² додатку:
passcode_invalid=Ðекоректний пароль. Спробуй ще раз.
-twofa_failed_get_secret=Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ Ñекрет.
+twofa_failed_get_secret=Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ код.
+webauthn_register_key=Додати ключ безпеки
+webauthn_nickname=ПÑевдонім
+webauthn_delete_key=Видалити ключ безпеки
+webauthn_delete_key_desc=Якщо ви видалите ключ безпеки, ви більше не зможете ввійти за його допомогою. Продовжити?
+webauthn_key_loss_warning=Якщо ви втратите ключі безпеки, ви втратите доÑтуп до Ñвого облікового запиÑу.
+webauthn_alternative_tip=Ви можете налаштувати додатковий метод автентифікації.
-manage_account_links=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ð¼Ð¸ запиÑами
-manage_account_links_desc=Ці зовнішні акаунти прив'Ñзані до вашого аккаунту Gitea.
+manage_account_links=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ð²'Ñзаними обліковими запиÑами
+manage_account_links_desc=Ці зовнішні облікові запиÑи прив'Ñзані до вашого облікового запиÑу Gitea.
account_links_not_available=Ðаразі немає зовнішніх облікових запиÑів, пов'Ñзаних із вашим обліковим запиÑом Gitea.
link_account=Прив'Ñзати обліковий запиÑ
-remove_account_link=Видалити облікові запиÑи
+remove_account_link=Видалити обліковий запиÑ
remove_account_link_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¿Ð¾Ð²'Ñзаного облікового запиÑу відкликає його доÑтуп до вашого облікового запиÑу Gitea. Продовжити?
-remove_account_link_success=Зв'Ñзаний обліковий Ð·Ð°Ð¿Ð¸Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð¾.
+remove_account_link_success=Прив'Ñзаний обліковий Ð·Ð°Ð¿Ð¸Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð¾.
+hooks.desc=Додайте веб-хуки, Ñкі запуÑкатимутьÑÑ Ð´Ð»Ñ <strong>уÑÑ–Ñ… репозиторіїв</strong>, Ñкими ви володієте.
-orgs_none=Ви не Ñ” учаÑником будь-Ñкої організації.
+orgs_none=Ви не є членом організації.
+repos_none=У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” Ñховищ.
-delete_account=Видалити ваш обліковий запиÑ
-delete_prompt=Ð¦Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð¾Ñтаточно видалить обліковий Ð·Ð°Ð¿Ð¸Ñ ÐºÐ¾Ñ€Ð¸Ñтувача. Це <strong>ÐЕ МОЖЛИВО</strong> відмінити.
-delete_with_all_comments=Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¼Ð¾Ð»Ð¾Ð´ÑˆÐ¸Ð¹ за %s днів. Щоб уникнути коментарів-привидів, вÑÑ– запити/PR коментрарі будуть видалені з ним.
-confirm_delete_account=ÐŸÑ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ
-delete_account_title=Видалити цей обліковий запиÑ
+delete_account=Видалити обліковий запиÑ
+delete_prompt=Ð¦Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð¾Ñтаточно видалить ваш обліковий запиÑ. Її <strong>ÐЕ МОЖЛИВО</strong> ÑкаÑувати.
+delete_with_all_comments=Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¼Ð¾Ð»Ð¾Ð´ÑˆÐ¸Ð¹ за %s днів. Щоб уникнути коментарів-привидів, уÑÑ– ваші коментарі будуть видалені разом з ним.
+confirm_delete_account=Підтвердити видаленнÑ
+delete_account_title=Видалити обліковий запиÑ
delete_account_desc=Ви впевнені, що хочете оÑтаточно видалити цей обліковий запиÑ?
-email_notifications.enable=Увімкнути ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ email
-email_notifications.onmention=ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ email тільки коли згадують
-email_notifications.disable=Вимкнути email ÑповіщеннÑ
-email_notifications.submit=Ðалаштувати параметри email
+email_notifications.enable=Увімкнути ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾ÑŽ поштою
+email_notifications.onmention=ПовідомлÑти електронною поштою коли згадують
+email_notifications.disable=Вимкнути ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾ÑŽ поштою
+email_notifications.submit=Ðалаштувати параметри електронної пошти
+email_notifications.andyourown=І ваші влаÑні повідомленнÑ
visibility=ВидиміÑть кориÑтувача
visibility.public=Публічний
visibility.limited=Обмежений
+visibility.limited_tooltip=ДоÑтупно лише Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ… кориÑтувачів
visibility.private=Приватний
+visibility.private_tooltip=ДоÑтупно лише Ð´Ð»Ñ Ñ‡Ð»ÐµÐ½Ñ–Ð² організацій, до Ñких ви долучилиÑÑ
[repo]
+new_repo_helper=Сховище міÑтить уÑÑ– файли проєкту, включно з Ñ–Ñторією ревізій. Ви вже розміщуєте його деінде? <a href="%s">ПеренеÑти Ñховище.</a>
owner=ВлаÑник
-owner_helper=ДеÑкі організації можуть не відображатиÑÑ Ñƒ випадаючому ÑпиÑку через макÑимальну кількіÑть репозиторііїв.
-repo_name=Ðазва репозиторію
-repo_size=Розмір репозиторію
+owner_helper=ДеÑкі організації можуть не відображатиÑÑ Ñƒ ÑпиÑку через Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð½Ð° макÑимальну кількіÑть Ñховищ.
+repo_name=Ðазва Ñховища
+repo_name_profile_public_hint=.profile - це Ñпеціальне Ñховище, за допомогою Ñкого ви можете додати README.md до профілю вашої публічної організації, Ñкий буде видимим Ð´Ð»Ñ Ð²ÑÑ–Ñ…. ПереконайтеÑÑ, що він Ñ” публічним, та ініціалізуйте його за допомогою README у каталозі профілю.
+repo_name_helper=У хороших назвах Ñховищ викориÑтовуютьÑÑ ÐºÐ¾Ñ€Ð¾Ñ‚ÐºÑ– ключові Ñлова, Ñкі легко запам'ÑтовуютьÑÑ Ñ‚Ð° Ñ” унікальними. Сховище з назвою «.profile» або «.profile-private» можна викориÑтовувати Ð´Ð»Ñ Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ README.md Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ„Ñ–Ð»ÑŽ кориÑтувача/організації.
+repo_size=Розмір Ñховища
template=Шаблон
-template_select=Оберіть шаблон.
-template_helper=Зробити репозиторій шаблоном
-template_description=Шаблонні репозиторії дозволÑють кориÑтувачам генерувати нові репозиторії із такою ж Ñтруктурою директорій, файлами та додатковими налаштуваннÑми.
+template_select=Обрати шаблон.
+template_helper=Зробити Ñховище шаблоном
+template_description=Шаблонні Ñховища дозволÑють кориÑтувачам Ñтворювати нові Ñховища з такою ж Ñтруктурою каталогів, файлами та додатковими налаштуваннÑми.
visibility=ВидиміÑть
-visibility_description=Тільки влаÑник або члени організації Ñкі мають віповідні права, зможуть побачити.
+visibility_description=Тільки влаÑник або члени організації, Ñкщо вони мають дозвіл, зможуть його побачити.
+visibility_helper=Зробити Ñховище приватним
visibility_helper_forced=ÐдмініÑтратор вашого Ñайту налаштував параметри: вÑÑ– нові репозиторії будуть приватними.
visibility_fork_helper=(Ці зміни вплинуть на вÑÑ– форки.)
clone_helper=Потрібна допомога у клонуванні? Відвідайте Ñторінку <a target="_blank" rel="noopener" href="%s">Допомога</a>.
fork_repo=Форкнути репозиторій
fork_from=Форк з
-fork_visibility_helper=Ðеможливо змінити видиміÑть форкнутого репозиторію.
+fork_visibility_helper=Ðеможливо змінити видиміÑть розгалуженого Ñховища.
+all_branches=УÑÑ– гілки
+view_all_branches=ПереглÑнути вÑÑ– гілки
+view_all_tags=ПереглÑнути вÑÑ– мітки
use_template=ЗаÑтоÑувати цей шаблон
+open_with_editor=Відкрити в %s
download_zip=Завантажити ZIP
download_tar=Завантажити TAR.GZ
download_bundle=Завантажити BUNDLE
-generate_repo=Згенерувати репозиторій
-generate_from=Генерувати з
+generate_repo=Створити Ñховище
+generate_from=Створити з
repo_desc=ОпиÑ
-repo_desc_helper=Введіть короткий Ð¾Ð¿Ð¸Ñ (опціонально)
-repo_gitignore_helper=Виберіть шаблон .gitignore.
-repo_gitignore_helper_desc=Оберіть з ÑпиÑку мовних шаблонів файли, Ñкі не будуть відÑтежуватиÑÑŒ. Типові артефакти, Ñкі генеруютьÑÑ Ð·Ð° допомогою інÑтрументів побудови кожної мови, за замовчуваннÑм включені до .gitignor.
+repo_desc_helper=Введіть короткий Ð¾Ð¿Ð¸Ñ (необов'Ñзково)
+repo_no_desc=Ðемає опиÑу
+repo_lang=Мови
+repo_gitignore_helper=Обрати шаблон .gitignore.
+repo_gitignore_helper_desc=Оберіть з ÑпиÑку мовних шаблонів файли, Ñкі не Ñлід відÑтежувати. Типові артефакти, що генеруютьÑÑ Ñ–Ð½Ñтрументами збірки кожної мови, за замовчуваннÑм включені до .gitignor.
issue_labels=Мітки задачі
-issue_labels_helper=Вибрати мітку Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ–.
+issue_labels_helper=Виберіть набір міток задачі.
license=ЛіцензіÑ
-license_helper=Виберіть ліцензійний файл.
-license_helper_desc=Ð›Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñ Ñ€ÐµÐ³ÑƒÐ»ÑŽÑ” те, що інші можуть Ñ– не можуть робити з вашим кодом. Ðе впевнені, що Ñаме підходить Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ проєкту? ДивітьÑÑ <a target="_blank" rel="noopener noreferrer" href="%s">Виберіть ліцензію.</a>
+license_helper=Обрати файл ліцензії.
+license_helper_desc=Ð›Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñ Ð²Ð¸Ð·Ð½Ð°Ñ‡Ð°Ñ”, що інші можуть робити з вашим кодом, а що ні. Ðе впевнені, Ñка підходить Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ проєкту? ДивітьÑÑ <a target="_blank" rel="noopener noreferrer" href="%s">Вибір ліцензії.</a>
+multiple_licenses=Кілька ліцензій
+object_format=Формат об'єкту
+object_format_helper=Формат об'єкту Ñховища. Ðеможливо буде змінити пізніше. SHA1 Ñ” найбільш ÑуміÑним.
readme=README
readme_helper=Виберіть шаблон README.
-readme_helper_desc=Це міÑце, де ви можете напиÑати повний Ð¾Ð¿Ð¸Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ проєкту.
-auto_init=Ініціалізувати репозиторій (Додає .gitignore, LICENSE та README)
+readme_helper_desc=Тут ви можете повніÑтю опиÑати ваш проєкт.
+auto_init=Ініціалізувати Ñховище (додає файли .gitignore, ліцензію та README)
trust_model_helper=Виберіть модель довіри Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñу. Можливі варіанти:
-trust_model_helper_collaborator=Співавтор: підпиÑи довіри від Ñпівавторів
-trust_model_helper_committer=УчаÑник: довірені підпиÑи учаÑтників
-trust_model_helper_collaborator_committer=Співавтор+Комітер: довірчі підпиÑи від Ñпівавторів, Ñкі відповідають комітеру
-trust_model_helper_default=За замовчуваннÑм: викориÑтовувати Ñтандартну модель довіри Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— уÑтановки
-create_repo=Створити репозиторій
+trust_model_helper_collaborator=Співавтор: довірÑти підпиÑам Ñпівавторів
+trust_model_helper_committer=Комітер: довірÑти підпиÑам, Ñкі відповідають комітерам
+trust_model_helper_collaborator_committer=Співавтор+Комітер: довірÑти підпиÑам Ñпівавторів, Ñкі відповідають комітеру
+trust_model_helper_default=Типово: викориÑтовувати Ñтандартну модель довіри Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— уÑтановки
+create_repo=Створити Ñховище
default_branch=Головна гілка
-default_branch_helper=Гілка за замовчуваннÑм Ñ” базовою гілкою Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð² на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° комітів коду.
+default_branch_label=типово
+default_branch_helper=Типова гілка Ñ” базовою гілкою Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð² на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° комітів.
mirror_prune=ОчиÑтити
-mirror_prune_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð·Ð°Ñтарілих поÑилань Ñкі ви відÑлідковуєте
-mirror_interval_invalid=Інтервал Ð´Ð·ÐµÑ€ÐºÐ°Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ñ” неприпуÑтимим.
-mirror_address=ÐšÐ»Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð· URL-адреÑи
-mirror_address_desc=ПоміÑтіть будь-Ñкі необхідні облікові дані у розділі ÐвторизаціÑ.
-mirror_lfs=Склад великих файлів (LFS)
-mirror_lfs_desc=Ðктивувати дзеркальне Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… LFS.
+mirror_prune_desc=Видалити заÑтарілі поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° віддалені відÑтеженнÑ
+mirror_interval=Інтервал Ð´Ð·ÐµÑ€ÐºÐ°Ð»ÑŽÐ²Ð°Ð½Ð½Ñ (допуÑтимі одиниці виміру чаÑу 'h', 'm', 's'). 0 - щоб вимкнути періодичну Ñинхронізацію. (Мінімальний інтервал: %s)
+mirror_interval_invalid=Інтервал Ð´Ð·ÐµÑ€ÐºÐ°Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ð½ÐµÐ´Ñ–Ð¹Ñний.
+mirror_sync=Ñинхронізовано
+mirror_sync_on_commit=Синхронізувати, коли надÑилаютьÑÑ ÐºÐ¾Ð¼Ñ–Ñ‚Ð¸
+mirror_address=Клонувати з URL-адреÑи
+mirror_address_desc=Введіть необхідні облікові дані в розділі ÐвторизаціÑ.
+mirror_address_url_invalid=URL-адреÑа недійÑна. Ðеобхідно правильно екранувати вÑÑ– компоненти URL-адреÑи.
+mirror_address_protocol_invalid=URL-адреÑа недійÑна. ДопуÑтимі лише адреÑи http(s):// або git://.
+mirror_lfs=Сховище великих файлів (LFS)
+mirror_lfs_desc=Ðктивувати Ð´Ð·ÐµÑ€ÐºÐ°Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… LFS.
mirror_lfs_endpoint=Кінцева точка LFS
-mirror_lfs_endpoint_desc=Ð¡Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ Ñпробує викориÑтовувати url Ð´Ð»Ñ ÐºÐ»Ð¾Ð½Ñƒ щоб <a target="_blank" rel="noopener noreferrer" href="%s">визначити LFS-Ñервер</a>. Ви також можете вказати кінцеву точку кориÑтувача, Ñкщо дані репозиторію LFS зберігаютьÑÑ Ð² іншому міÑці.
+mirror_lfs_endpoint_desc=Ð¡Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ Ñпробує викориÑтати URL-адреÑу клону Ð´Ð»Ñ <a target="_blank" rel="noopener noreferrer" href="%s">Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñервера LFS</a>. Ви також можете вказати влаÑну кінцеву точку, Ñкщо дані LFS Ñховища зберігаютьÑÑ Ð² іншому міÑці.
mirror_last_synced=ОÑÑ‚Ð°Ð½Ð½Ñ ÑинхронізаціÑ
mirror_password_placeholder=(без змін)
-mirror_password_blank_placeholder=(відключено)
+mirror_password_blank_placeholder=(Ðе вÑтановлено)
mirror_password_help=Змініть ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача, щоб видалити збережений пароль.
watchers=СпоÑтерігачі
-stargazers=Зацікавлені
+stargazers=Шанувальники
+stars_remove_warning=Це видалить уÑÑ– зірки з цього Ñховища.
forks=Форки
-reactions_more=додати %d більше
-unit_disabled=ÐдмініÑтратор Ñайту вимкнув цей розділ репозиторію.
+stars=Зірки
+reactions_more=і ще %d
+unit_disabled=ÐдмініÑтратор Ñайту вимкнув цей розділ Ñховища.
language_other=Інші
-adopt_search=Введіть ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ неприйнÑтних репозиторіїв... (залиште порожнім, щоб знайти вÑÑ–)
-adopt_preexisting_label=ПрийнÑті файли
-adopt_preexisting=ПрийнÑти вже Ñ–Ñнуючі файли
-adopt_preexisting_content=Створити репозиторій з %s
-adopt_preexisting_success=ПрийнÑти файли та Ñтворити репозиторій з %s
+adopt_search=Введіть ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ неприйнÑтих Ñховищ... (залиште порожнім, щоб знайти вÑÑ–)
+adopt_preexisting_label=ПрийнÑти файли
+adopt_preexisting=ПрийнÑти попередньо Ñтворені файли
+adopt_preexisting_content=Створити Ñховище з %s
+adopt_preexisting_success=ПрийнÑти файли та Ñтворити Ñховище з %s
delete_preexisting_label=Видалити
-delete_preexisting=Видалити Ñ–Ñнуючі файли
+delete_preexisting=Видалити попередньо Ñтворені файли
delete_preexisting_content=Видалити файли з %s
delete_preexisting_success=Видалено неприйнÑті файли в %s
blame_prior=ПереглÑнути анотацію, що передує цій зміні
+blame.ignore_revs.failed=Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ñ–Ð³Ð½Ð¾Ñ€ÑƒÐ²Ð°Ñ‚Ð¸ ревізії у <a href="%s">.git-blame-ignore-revs</a>.
+user_search_tooltip=Показує не більше 30 кориÑтувачів
+tree_path_not_found=ШлÑÑ… %[1]s не Ñ–Ñнує в %[2]s
-transfer.accept=Дозволити транÑфер
-transfer.reject=Відхилити транÑфер
+transfer.accept=Дозволити переміщеннÑ
+transfer.accept_desc=`ПереміÑтити до "%s"`
+transfer.reject=Відхилити переміщеннÑ
+transfer.reject_desc=`СкаÑувати Ð¿ÐµÑ€ÐµÐ¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð´Ð¾ "%s"`
+transfer.no_permission_to_accept=У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” дозволу приймати цю передачу.
+transfer.no_permission_to_reject=У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” дозволу на Ð²Ñ–Ð´Ñ…Ð¸Ð»ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— передачі.
desc.private=Приватний
desc.public=Публічний
+desc.public_access=Публічний доÑтуп
desc.template=Шаблон
desc.internal=Внутрішній
-desc.archived=Ðрхівний
+desc.archived=Ðрхівований
+desc.sha256=SHA256
template.items=Елементи шаблону
template.git_content=ВміÑÑ‚ Git (типова гілка)
-template.git_hooks=Перехоплювачі Git
-template.webhooks=Webhook'и
+template.git_hooks=Хуки Git
+template.git_hooks_tooltip=Ðаразі ви не можете змінювати або видалÑти Git-хуки піÑÐ»Ñ Ñ—Ñ… додаваннÑ. Виберіть це лише Ñкщо ви довірÑєте Ñховищу шаблонів.
+template.webhooks=Веб-хуки
template.topics=Теми
template.avatar=Ðватар
template.issue_labels=Мітки задачі
template.one_item=Слід обрати хоча б один елемент шаблону
-template.invalid=Слід обрати шаблонний репозиторій
+template.invalid=Слід обрати шаблонне Ñховище
-archive.issue.nocomment=Цей репозиторій архівовано. Ви не можете коментувати задачі.
-archive.pull.nocomment=Це архівний репозитарій. Ви не можете коментувати пулл-реквеÑти.
+archive.title=Це архівне Ñховище. Ви можете переглÑдати й клонувати файли, але не можете завантажувати Ñвої зміни або відкривати задачі чи запити на злиттÑ.
+archive.title_date=Це архівне Ñховище на %s. Ви можете переглÑдати файли й клонувати його, але не можете завантажувати Ñвої зміни або відкривати задачі чи запити на злиттÑ.
+archive.issue.nocomment=Це Ñховище архівовано. Ви не можете коментувати задачі.
+archive.pull.nocomment=Це Ñховище архівовано. Ви не можете коментувати запити на злиттÑ.
-form.reach_limit_of_creation_1=Ви вже доÑÑгли ліміту в %d репозиторіїв.
-form.reach_limit_of_creation_n=Ви доÑÑгли макÑимальної кількоÑті %d Ñтворених репозиторіїв.
+form.reach_limit_of_creation_1=Ви доÑÑгли макÑимальної кількоÑті %d Ñховища.
+form.reach_limit_of_creation_n=Ви доÑÑгли макÑимальної кількоÑті %d Ñховищ.
+form.name_reserved=Ðазву Ñховища '%s' зарезервовано.
+form.name_pattern_not_allowed=Шаблон '%s' не дозволено в назві Ñховища.
need_auth=ÐвторизаціÑ
migrate_options=Параметри міграції
migrate_service=Ð¡ÐµÑ€Ð²Ñ–Ñ Ð¼Ñ–Ð³Ñ€Ð°Ñ†Ñ–Ñ—
-migrate_options_lfs=ПеренеÑÐµÐ½Ð½Ñ LFS файлів
+migrate_options_mirror_helper=Це Ñховище буде дзеркалом
+migrate_options_lfs=ПеренеÑÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð² LFS
migrate_options_lfs_endpoint.label=Кінцева точка LFS
-migrate_options_lfs_endpoint.description=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Ð±ÑƒÐ´Ðµ намагатиÑÑ Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñтовувати ваш Git віддалено, щоб <a target="_blank" rel="noopener noreferrer" href="%s">визначати LFS Ñервер</a>. Ви також можете вказати Ñвою кінцеву точку, Ñкщо дані репозиторію LFS зберігаютьÑÑ Ð² іншому міÑці.
-migrate_options_lfs_endpoint.description.local=Також підтримуютьÑÑ ÑˆÐ»Ñхи на локальному Ñервері.
-migrate_items=Деталі міграції
+migrate_options_lfs_endpoint.description=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Ñпробує викориÑтати ваш Git віддалено, щоб <a target="_blank" rel="noopener noreferrer" href="%s">визначати Ñервер LFS</a>. Ви також можете вказати влаÑну кінцеву точку, Ñкщо дані Ñховища LFS зберігаютьÑÑ Ð² іншому міÑці.
+migrate_options_lfs_endpoint.description.local=Також підтримуєтьÑÑ ÑˆÐ»ÑÑ… до локального Ñервера.
+migrate_items=Елементи міграції
migrate_items_wiki=Вікі
migrate_items_milestones=Етапи
migrate_items_labels=Мітки
@@ -825,26 +1164,34 @@ migrate_items_merge_requests=Запити на злиттÑ
migrate_items_releases=Релізи
migrate_repo=ПеренеÑти репозиторій
migrate.clone_address=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ / клонувати з URL-адреÑи
-migrate.clone_address_desc=URL-адреÑа HTTP(S) або Git "clone" Ñ–Ñнуючого репозиторіÑ
+migrate.clone_address_desc=URL-адреÑа HTTP(S) або Git "clone" Ñ–Ñнуючого Ñховища
+migrate.github_token_desc=Ви можете додати один або декілька токенів через кому, щоб пришвидшити міграцію через Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ ÑˆÐ²Ð¸Ð´ÐºÐ¾Ñті API GitHub. ПОПЕРЕДЖЕÐÐЯ: Ð—Ð»Ð¾Ð²Ð¶Ð¸Ð²Ð°Ð½Ð½Ñ Ñ†Ñ–Ñ”ÑŽ функцією може порушити політику поÑтачальника поÑлуг Ñ– призвеÑти до Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу.
migrate.clone_local_path=або шлÑÑ… до локального Ñерверу
migrate.permission_denied=Вам не дозволено імпортувати локальні репозиторії.
migrate.permission_denied_blocked=Ви не можете імпортувати з заборонених вузлів, будь лаÑка, попроÑіть адмініÑтратора перевірити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS.
-migrate.invalid_lfs_endpoint=Помилкова кінцева точка LFS.
+migrate.invalid_local_path=Локальний шлÑÑ… недійÑний. Він не Ñ–Ñнує або не Ñ” каталогом.
+migrate.invalid_lfs_endpoint=Кінцева точка LFS недійÑна.
migrate.failed=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Ð½Ðµ вдалаÑÑ: %v
migrate.migrate_items_options=Ð”Ð»Ñ Ð¿ÐµÑ€ÐµÐ½ÐµÑÐµÐ½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¸Ñ… елементів потрібен токен доÑтупу
migrated_from=ПеренеÑено з <a href="%[1]s">%[2]s</a>
migrated_from_fake=ПеренеÑено з %[1]s
-migrate.migrate=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Ð· %s
+migrate.migrate=Мігрувати з %s
migrate.migrating=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Ñ–Ð· <b>%s</b>...
migrate.migrating_failed=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Ñ–Ð· <b>%s</b> не вдалаÑÑ.
+migrate.migrating_failed.error=Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ½ÐµÑти: %s
migrate.migrating_failed_no_addr=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Ð½Ðµ вдалаÑÑ.
-migrate.git.description=ПеренеÑÐµÐ½Ð½Ñ Ð»Ð¸ÑˆÐµ репозиторію з будь-Ñкої Ñлужби Git.
+migrate.github.description=ПеренеÑти дані з github.com чи інших Ñерверів Github.
+migrate.git.description=ПеренеÑти Ñховище з будь-Ñкого ÑервіÑу Git'у.
migrate.gitlab.description=ПеренеÑти дані з gitlab.com та інших екземплÑрів GitLab.
migrate.gitea.description=ПеренеÑти дані з gitea.com та інших екземплÑрів Gitea.
migrate.gogs.description=ПеренеÑти дані з notabug.org та інших екземплÑрів Gogs.
migrate.onedev.description=ПеренеÑти дані з code.onedev.io та інших екземплÑрів OneDev.
migrate.codebase.description=ПеренеÑти дані з codebasehq.com.
migrate.gitbucket.description=ПеренеÑти дані з екземплÑрів GitBucket.
+migrate.codecommit.description=ПеренеÑÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… з AWS CodeCommit.
+migrate.codecommit.aws_access_key_id=ID ключа доÑтупу AWS
+migrate.codecommit.https_git_credentials_username=Ð†Ð¼â€™Ñ ÐºÐ¾Ñ€Ð¸Ñтувача HTTPS Git
+migrate.codecommit.https_git_credentials_password=Пароль кориÑтувача HTTPS Git
migrate.migrating_git=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Git даних
migrate.migrating_topics=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Ñ‚ÐµÐ¼
migrate.migrating_milestones=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ ÐµÑ‚Ð°Ð¿Ñ–Ð²
@@ -852,26 +1199,34 @@ migrate.migrating_labels=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Ð¼Ñ–Ñ‚Ð¾Ðº
migrate.migrating_releases=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Ñ€ÐµÐ»Ñ–Ð·Ñ–Ð²
migrate.migrating_issues=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Ð·Ð°Ð´Ð°Ñ‡
migrate.migrating_pulls=ÐœÑ–Ð³Ñ€Ð°Ñ†Ñ–Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð² на злиттÑ
+migrate.cancel_migrating_title=СкаÑувати міграцію
+migrate.cancel_migrating_confirm=Ви хочете ÑкаÑувати міграцію?
+migration_status=CÑ‚Ð°Ñ‚ÑƒÑ Ð¼Ñ–Ð³Ñ€Ð°Ñ†Ñ–Ñ—
mirror_from=дзеркало
forked_from=форк від
generated_from=згенеровано з
-fork_from_self=Ви не можете форкнути репозиторій, так Ñк ви його влаÑник.
+fork_from_self=Ви не можете форкнути влаÑне Ñховище.
fork_guest_user=Увійдіть, щоб зробити форк репозитарію.
watch_guest_user=Увійдіть, щоб Ñлідкувати за цим репозиторієм.
star_guest_user=Увійдіть, щоб додати в обране цей репозиторій.
unwatch=Ðе Ñтежити
-watch=Слідкувати
+watch=Стежити
unstar=Видалити із обраних
star=В обрані
fork=Форк
+action.blocked_user=Ðеможливо виконати дію, оÑкільки ви заблоковані влаÑником Ñховища.
download_archive=Скачати репозиторій
+more_operations=Інші операції
quick_guide=Короткий поÑібник
clone_this_repo=Кнонувати цей репозиторій
+cite_this_repo=ПоÑлатиÑÑ Ð½Ð° це Ñховище
create_new_repo_command=Створити новий репозиторій з командного Ñ€Ñдка
push_exist_repo=Опублікувати Ñ–Ñнуючий репозиторій з командного Ñ€Ñдка
-empty_message=Цей репозиторій порожній.
+empty_message=Це Ñховище порожнє.
+broken_message=Ðеможливо прочитати дані Git, що лежать в оÑнові цього Ñховища. ЗвернітьÑÑ Ð´Ð¾ адмініÑтратора Ñервера або видаліть Ñховище.
+no_branch=Це Ñховище не має гілок.
code=Код
code.desc=ДоÑтуп до коду, файлів, комітів та гілок.
@@ -885,96 +1240,153 @@ tags=Теги
issues=Задачі
pulls=Запити на злиттÑ
projects=Проєкти
+packages=Пакети
+actions=Дії
labels=Мітки
org_labels_desc=Мітки Ñ€Ñ–Ð²Ð½Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ— можуть викориÑтовуватиÑÑ <strong>в уÑÑ–Ñ… репозиторіÑÑ…</strong> цієї організації
org_labels_desc_manage=керувати
-milestones=Етап
+milestone=Етап
+milestones=Етапи
commits=Коміти
commit=Коміт
release=Реліз
releases=Релізи
tag=Тег
-released_this=випущені релізи
+released_this=випуÑтив(-ла)
+file.title=%s в %s
file_raw=Ðеформатований
file_history=ІÑторіÑ
file_view_source=ПереглÑнути вихідний код
-file_view_rendered=ПереглÑнути відрендерено
file_view_raw=ПереглÑд Raw
file_permalink=ПоÑтійне поÑиланнÑ
-file_too_large=Цей файл завеликий щоб бути показаним.
-
+file_too_large=Файл занадто великий Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ.
+file_is_empty=Файл порожній.
+code_preview_line_from_to=РÑдки від %[1]d до %[2]d в %[3]s
+code_preview_line_in=РÑдок %[1]d в %[2]s
+invisible_runes_header=`Цей файл міÑтить невидимі Ñимволи Юнікоду`
+invisible_runes_description=`Цей файл міÑтить невидимі Ñимволи Юнікоду, Ñкі не розрізнÑютьÑÑ Ð»ÑŽÐ´Ð¸Ð½Ð¾ÑŽ, але можуть по-різному оброблÑтиÑÑ ÐºÐ¾Ð¼Ð¿'ютером. Якщо ви вважаєте, що це зроблено навмиÑно, можете Ñміливо ігнорувати це попередженнÑ. Щоб показати Ñ—Ñ…, ÑкориÑтайтеÑÑ ÐºÐ½Ð¾Ð¿ÐºÐ¾ÑŽ Escape.`
+ambiguous_runes_header=`Цей файл міÑтить неоднозначні Ñимволи Юнікоду`
+ambiguous_runes_description=`Цей файл міÑтить Ñимволи Юнікоду, Ñкі можна Ñплутати з іншими Ñимволами. Якщо ви вважаєте, що це зроблено навмиÑно, можете Ñміливо ігнорувати це попередженнÑ. Щоб показати Ñ—Ñ…, ÑкориÑтайтеÑÑ ÐºÐ½Ð¾Ð¿ÐºÐ¾ÑŽ Escape.`
+invisible_runes_line=`Цей Ñ€Ñдок міÑтить невидимі Ñимволи Юнікоду`
+ambiguous_runes_line=`Цей Ñ€Ñдок міÑтить неоднозначні Ñимволи Юнікоду`
+ambiguous_character=`%[1]c [U+%04[1]X] можна Ñплутати з %[2]c [U+%04[2]X]`
+
+escape_control_characters=Екранувати
+unescape_control_characters=Відмінити екрануваннÑ
file_copy_permalink=Копіювати поÑтійне поÑиланнÑ
video_not_supported_in_browser=Ваш браузер не підтримує тег 'video' HTML5.
audio_not_supported_in_browser=Ваш браузер не підтримує тег HTML5 'audio'.
-stored_lfs=Збережено з Git LFS
symbolic_link=Символічне поÑиланнÑ
+executable_file=Виконуваний файл
+generated=Створено
commit_graph=Графік комітів
commit_graph.select=Виберіть гілки
commit_graph.hide_pr_refs=Приховати запити на злиттÑ
-commit_graph.monochrome=Монохром
+commit_graph.monochrome=Монохромний
commit_graph.color=Колір
-blame=ЗвинуваченнÑ
+commit.contained_in=Цей коміт міÑтитьÑÑ Ð²:
+commit.contained_in_default_branch=Цей коміт Ñ” чаÑтиною типової гілки
+commit.load_referencing_branches_and_tags=Завантажити гілки та мітки, Ñкі поÑилаютьÑÑ Ð½Ð° цей коміт
+blame=ÐнотаціÑ
download_file=Завантажити файл
normal_view=Звичайний виглÑд
line=Ñ€Ñдок
lines=Ñ€Ñдки
+from_comment=(коментар)
+editor.add_file=Додати файл
editor.new_file=Ðовий файл
editor.upload_file=Завантажити файл
-editor.edit_file=Ð ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ
+editor.edit_file=Редагувати файл
editor.preview_changes=Попередній переглÑд змін
editor.cannot_edit_lfs_files=Файли LFS не можна редагувати в веб-інтерфейÑÑ–.
+editor.cannot_edit_too_large_file=Файл занадто великий Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ.
editor.cannot_edit_non_text_files=Бінарні файли не можливо редагувати у веб-інтерфейÑÑ–.
+editor.file_not_editable_hint=Ðле ви вÑе ще можете перейменувати або переміÑтити його.
editor.edit_this_file=Редагувати файл
editor.this_file_locked=Файл заблоковано
-editor.must_be_on_a_branch=Ви повинні бути у гілці щоб зробити, або запропонувати зміни до цього файлу.
-editor.fork_before_edit=Ðеобхідно зробити форк цього репозиторій, щоб внеÑти або запропонувати зміни в цей файл.
+editor.must_be_on_a_branch=Ви повинні бути у гілці щоб робити або пропонувати зміни до цього файлу.
+editor.fork_before_edit=Ðеобхідно зробити форк цього Ñховища, щоб внеÑти або запропонувати зміни в цей файл.
editor.delete_this_file=Видалити файл
editor.must_have_write_access=Ви повинні мати доÑтуп на Ð·Ð°Ð¿Ð¸Ñ Ñ‰Ð¾Ð± запропонувати зміни до цього файлу.
-editor.name_your_file=Дайте назву файлу…
-editor.filename_help=Щоб додати каталог, наберіть його назву, а потім - коÑу риÑку ('/'). Щоб видалити каталог, перейдіть до початку Ð¿Ð¾Ð»Ñ Ñ– натиÑніть backspace.
+editor.file_delete_success=Файл "%s" видалено.
+editor.name_your_file=Ðазвіть файл…
+editor.filename_help=Щоб додати каталог, наберіть його назву, а потім - прÑмий Ñлеш ('/'). Щоб видалити каталог, перейдіть до початку Ð¿Ð¾Ð»Ñ Ñ– натиÑніть видалити ліворуч.
editor.or=або
editor.cancel_lower=СкаÑувати
editor.commit_signed_changes=ВнеÑти підпиÑані зміни
-editor.commit_changes=Закомітити зміни
+editor.commit_changes=ЗафікÑувати зміни
+editor.add_tmpl=Додати '{filename}'
+editor.add=Додати %s
+editor.update=Оновити %s
+editor.delete=Видалити %s
+editor.patch=ЗаÑтоÑувати патч
+editor.patching=ЗаÑтоÑÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½ÑŒ:
+editor.new_patch=Ðовий патч
editor.commit_message_desc=Додати необов'Ñзковий розширений опиÑ…
-editor.signoff_desc=Додатиь Signed-off-by комітом в конці Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ñƒ комітів.
-editor.commit_directly_to_this_branch=Зробіть коміт прÑмо в гілку <strong class="branch-name">%s</strong>.
+editor.signoff_desc=Додати «ПідпиÑано комітером» в кінці Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñƒ.
+editor.commit_directly_to_this_branch=Зробити коміт безпоÑередньо в гілку <strong class="branch-name">%s</strong>.
editor.create_new_branch=Створити <strong>нову гілку</strong> Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ коміту та відкрити запит на злиттÑ.
editor.create_new_branch_np=Створити <strong>нову гілку</strong> Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ коміту.
editor.propose_file_change=Запропонувати зміну файлу
-editor.new_branch_name_desc=Ім'Ñ Ð½Ð¾Ð²Ð¾Ñ— гілки…
+editor.new_branch_name=Ðазвіть нову гілку Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ коміту
+editor.new_branch_name_desc=Ðазва нової гілки…
editor.cancel=Відмінити
-editor.filename_cannot_be_empty=Ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ не може бути порожнім.
+editor.filename_cannot_be_empty=Ðазва файлу не може бути порожньою.
+editor.filename_is_invalid=Ðазва файлу недійÑна: "%s".
+editor.commit_email=Електронна пошта коміту
+editor.invalid_commit_email=ÐдреÑа електронної пошти Ð´Ð»Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñƒ недійÑна.
+editor.file_is_a_symlink=`"%s" - це Ñимволічне поÑиланнÑ. Символічні поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ðµ можна редагувати у веб-редакторі`
+editor.filename_is_a_directory=Ðазва файлу '%s' вже викориÑтовуєтьÑÑ Ñк назва каталогу у цьому Ñховищі.
+editor.file_modifying_no_longer_exists=Редагований файл '%s' більше не Ñ–Ñнує в цьому Ñховищі.
editor.file_changed_while_editing=ЗміÑÑ‚ файлу змінивÑÑ Ð· моменту початку редагуваннÑ. <a target="_blank" rel="noopener" href="%s"> ÐатиÑніть тут </a>, щоб переглÑнути що було змінено, або <strong>закомітьте зміни ще раз</strong>, щоб перепиÑати Ñ—Ñ….
editor.commit_empty_file_header=Закомітити порожній файл
-editor.commit_empty_file_text=Файл, в комміті порожній. Продовжити?
-editor.no_changes_to_show=Ðема змін Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ñƒ.
-editor.fail_to_update_file_summary=Помилка:
+editor.commit_empty_file_text=Файл, Ñкий ви збираєтеÑÑ Ð·Ð°ÐºÐ¾Ð¼Ñ–Ñ‚Ð¸Ñ‚Ð¸, порожній. Продовжувати?
+editor.no_changes_to_show=Ðемає змін.
+editor.push_rejected_no_message=Зміну відхилено Ñервером без повідомленнÑ. Будь лаÑка, перевірте Git-хуки.
+editor.push_rejected=Зміну відхилено Ñервером. Будь лаÑка, перевірте Git-хуки.
editor.push_rejected_summary=Повне Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ відмову:
editor.add_subdir=Додати каталог…
+editor.unable_to_upload_files=Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ файли до '"%s". Помилка: %v
+editor.upload_file_is_locked=Файл "%s" заблоковано %s.
+editor.upload_files_to_dir=`Завантажити файли до "%s"`
editor.no_commit_to_branch=Ðе вдалоÑÑ Ð²Ð½ÐµÑти коміт безпоÑередньо до гілки, тому що:
-editor.user_no_push_to_branch=КориÑтувач не може здійÑнити пуш до гілки
editor.require_signed_commit=Гілка вимагає підпиÑаного коміту
+editor.revert=Повернути %s до:
+editor.failed_to_commit=Ðе вдалоÑÑ Ð·Ð°Ñ„Ñ–ÐºÑувати зміни.
+editor.failed_to_commit_summary=Помилка:
+
+editor.fork_edit_description=Ви не можете редагувати це Ñховище безпоÑередньо. Зміни буде запиÑано до вашого форку <b>%s</b>, тож ви зможете Ñтворити запит на злиттÑ.
commits.desc=ПереглÑнути Ñ–Ñторію зміни коду.
commits.commits=Коміти
+commits.no_commits=Ðемає Ñпільних комітів. '%s' та '%s' мають різну Ñ–Ñторію.
commits.nothing_to_compare=Ці гілки однакові.
+commits.search_branch=Ð¦Ñ Ð³Ñ–Ð»ÐºÐ°
commits.search_all=УÑÑ– гілки
commits.author=Ðвтор
commits.message=ПовідомленнÑ
commits.date=Дата
-commits.older=Давніше
-commits.newer=Ðовіше
+commits.older=Старіші
+commits.newer=Ðовіші
commits.signed_by=ПідпиÑано
commits.signed_by_untrusted_user=ПідпиÑаний недовіреним кориÑтувачем
-commits.signed_by_untrusted_user_unmatched=ПідпиÑаний недовіреним кориÑтувачем, Ñкий не відповідає комітеру
+commits.signed_by_untrusted_user_unmatched=ПідпиÑано недовіреним кориÑтувачем, Ñкий не відповідає комітеру
commits.gpg_key_id=Ідентифікатор GPG ключа
+commits.ssh_key_fingerprint=Відбиток ключа SSH
+commits.view_path=ПереглÑнути в Ñ–Ñторії
+commits.view_file_diff=ПереглÑнути зміни до цього файлу в цьому коміті
+commit.operations=Дії
+commit.revert=Повернути до попереднього Ñтану
+commit.revert-header=Повернути: %s
+commit.revert-content=Виберіть гілку, до Ñкої хочете повернутиÑÑ:
commitstatus.error=Помилка
+commitstatus.failure=Ðевдача
commitstatus.pending=ОчікуваннÑ
+commitstatus.success=УÑпіх
ext_issues=ДоÑтуп до зовнішніх задач
ext_issues.desc=ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° зовнішню ÑиÑтему відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡.
@@ -984,23 +1396,35 @@ projects.description_placeholder=ОпиÑ
projects.create=Створити проєкт
projects.title=Ðазва
projects.new=Ðовий проєкт
-projects.new_subheader=Координуйте, відÑтежуйте та оновлюйте інформацію про виконувану роботу в одному міÑці, аби проєкти залишалиÑÑ Ð¿Ñ€Ð¾Ð·Ð¾Ñ€Ð¸Ð¼Ð¸ та за розкладом.
+projects.new_subheader=Координуйте, відÑтежуйте та оновлюйте Ñвою роботу в одному міÑці, щоб проєкти залишалиÑÑ Ð¿Ñ€Ð¾Ð·Ð¾Ñ€Ð¸Ð¼Ð¸ та виконувалиÑÑ Ð·Ð° графіком.
+projects.create_success=Проєкт "%s" Ñтворено.
projects.deletion=Видалити проєкт
projects.deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñƒ видалÑÑ” його з уÑÑ–Ñ… пов'Ñзаних задач. Продовжити?
projects.deletion_success=Проєкт видалено.
-projects.edit=Редагувати проєкти
+projects.edit=Редагувати проєкт
projects.edit_subheader=Проєкти організовують задачі та відÑтежують прогреÑ.
-projects.modify=Оновити проєкт
+projects.modify=Редагувати проєкт
+projects.edit_success=Проєкт "%s" оновлено.
projects.type.none=ВідÑутній
projects.type.basic_kanban=Спрощений канбан
projects.type.bug_triage=Ð¡Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð¼Ð¸Ð»Ð¾Ðº
projects.template.desc=Шаблон проєкту
-projects.template.desc_helper=Оберіть шаблон проєкту, аби почати
+projects.template.desc_helper=Оберіть шаблон проєкту, щоб розпочати роботу
+projects.column.edit=Редагувати Ñтовпець
projects.column.edit_title=Ðазва
projects.column.new_title=Ðазва
+projects.column.new_submit=Створити Ñтовпець
+projects.column.new=Ðовий Ñтовпець
+projects.column.set_default=Ð’Ñтановити типово
+projects.column.set_default_desc=Ð’Ñтановіть цей Ñтовпець типовим Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡ Ñ– запитів на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð±ÐµÐ· категорії
+projects.column.delete=Видалити Ñтовпець
projects.column.color=Колір
projects.open=Відкрити
projects.close=Закрити
+projects.column.assigned_to=Призначено
+projects.card_type.desc=Попередні переглÑди картки
+projects.card_type.images_and_text=Ð—Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ñ– текÑÑ‚
+projects.card_type.text_only=Лише текÑÑ‚
issues.desc=ÐžÑ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ Ð·Ð²Ñ–Ñ‚Ñ–Ð² про помилки, завдань та етапів.
issues.filter_assignees=Фільтр виконавців
@@ -1008,8 +1432,10 @@ issues.filter_milestones=Фільтр етапів
issues.filter_projects=Фільтр проєктів
issues.filter_labels=Фільтр міток
issues.filter_reviewers=Фільтр рецензентів
+issues.filter_no_results=Ðемає результатів
+issues.filter_no_results_placeholder=Спробуйте налаштувати Ñвої фільтри пошуку.
issues.new=Ðова задача
-issues.new.title_empty=Заголовок не може бути пуÑтим
+issues.new.title_empty=Заголовок не може бути порожнім
issues.new.labels=Мітки
issues.new.no_label=Без мітки
issues.new.clear_labels=ОчиÑтити мітки
@@ -1020,15 +1446,21 @@ issues.new.open_projects=Відкриті проєкти
issues.new.closed_projects=Закриті проєкти
issues.new.no_items=Ðемає елементів
issues.new.milestone=Етап
-issues.new.no_milestone=Етап відÑутній
+issues.new.no_milestone=Етапи відÑутні
issues.new.clear_milestone=ОчиÑтити етап
issues.new.assignees=Виконавці
issues.new.clear_assignees=Прибрати виконавців
-issues.new.no_assignees=Ðемає виконавцÑ
-issues.choose.get_started=Початок роботи
+issues.new.no_assignees=Ðемає виконавців
+issues.new.no_reviewers=Ðемає рецензентів
+issues.new.blocked_user=Ðе вдалоÑÑ Ñтворити задачу, тому що ви заблоковані влаÑником Ñховища.
+issues.edit.blocked_user=Ðеможливо редагувати вміÑÑ‚, оÑкільки Ð²Ð°Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¾ автором або влаÑником Ñховища.
+issues.choose.get_started=Розпочати
issues.choose.open_external_link=Відкрити
issues.choose.blank=Типово
-issues.choose.blank_about=Створити задачу із шаблону за замовчуваннÑм.
+issues.choose.blank_about=Створити задачу із Ñтандартного шаблону.
+issues.choose.ignore_invalid_templates=ÐедійÑні шаблони проігноровано
+issues.choose.invalid_templates=Знайдено %v недійÑний(Ñ…) шаблон(ів)
+issues.choose.invalid_config=ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ– міÑтить помилки:
issues.no_ref=Ðе вказана гілка або тег
issues.create=Створити задачу
issues.new_label=Ðова мітка
@@ -1038,7 +1470,8 @@ issues.create_label=Створити мітку
issues.label_templates.title=Завантажити визначений набір міток
issues.label_templates.info=Ще немає міток. ÐатиÑніть 'Ðова мітка' або викориÑтовуйте попередньо визначений набір міток:
issues.label_templates.helper=Оберіть набір міток
-issues.label_templates.use=ВикориÑтовувати набір міток
+issues.label_templates.use=ВикориÑтати набір міток
+issues.label_templates.fail_to_load_file=Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ файл шаблона мітки '%s': %v
issues.add_label=додано %s з міткою %s
issues.add_labels=додано %s з мітками %s
issues.remove_label=видалено %s з міткою %s
@@ -1064,29 +1497,40 @@ issues.delete_branch_at=`видалена гілка <b>%s</b> %s`
issues.filter_label=Мітка
issues.filter_label_exclude=`ВикориÑтовуйте <code>Alt</code> + <code>клік/Enter</code> Ð´Ð»Ñ Ð²Ð¸ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð¼Ñ–Ñ‚Ð¾Ðº`
issues.filter_label_no_select=Ð’ÑÑ– мітки
+issues.filter_label_select_no_label=Без мітки
issues.filter_milestone=Етап
+issues.filter_milestone_all=Ð’ÑÑ– етапи
+issues.filter_milestone_none=Етапи відÑутні
+issues.filter_milestone_open=Відкриті етапи
+issues.filter_milestone_closed=Закриті етапи
issues.filter_project=Проєкт
+issues.filter_project_all=Ð’ÑÑ– проєкти
issues.filter_project_none=Проєкт відÑутній
issues.filter_assignee=Виконавець
-issues.filter_assginee_no_select=Ð’ÑÑ– виконавці
-issues.filter_assginee_no_assignee=Ðемає виконавцÑ
+issues.filter_assignee_no_assignee=Ðікому не приÑвоєно
+issues.filter_assignee_any_assignee=Призначено будь-кому
+issues.filter_poster=Ðвтор
+issues.filter_user_placeholder=Пошук кориÑтувачів
+issues.filter_user_no_select=УÑÑ– кориÑтувачі
issues.filter_type=Тип
issues.filter_type.all_issues=Ð’ÑÑ– задачі
+issues.filter_type.all_pull_requests=УÑÑ– запити на злиттÑ
issues.filter_type.assigned_to_you=Призначене вам
issues.filter_type.created_by_you=Створено вами
issues.filter_type.mentioning_you=Ð’Ð°Ñ Ð·Ð³Ð°Ð´Ð°Ð½Ð¾
-issues.filter_type.review_requested=Відгук запитано
+issues.filter_type.review_requested=Запит на рецензію
+issues.filter_type.reviewed_by_you=Перевірено вами
issues.filter_sort=Сортувати
issues.filter_sort.latest=Ðайновіші
issues.filter_sort.oldest=ÐайÑтаріші
-issues.filter_sort.recentupdate=Ðещодавно оновлено
+issues.filter_sort.recentupdate=Ðещодавно оновлені
issues.filter_sort.leastupdate=Ðайдавніше оновлені
issues.filter_sort.mostcomment=Ðайбільш коментовані
issues.filter_sort.leastcomment=Ðайменш коментовані
-issues.filter_sort.nearduedate=Ðайновіша дата
-issues.filter_sort.farduedate=ÐайÑтаріша дата
-issues.filter_sort.moststars=Ðайбільш обраних
-issues.filter_sort.feweststars=Ðайменш обраних
+issues.filter_sort.nearduedate=Ðайближчий термін
+issues.filter_sort.farduedate=Ðайвіддаленіший термін
+issues.filter_sort.moststars=Ðайбільш фаворизовані
+issues.filter_sort.feweststars=Ðайменш фаворизовані
issues.filter_sort.mostforks=Ðайбільше форків
issues.filter_sort.fewestforks=Ðайменше форків
issues.action_open=Відкрити
@@ -1096,13 +1540,16 @@ issues.action_milestone=Етап
issues.action_milestone_no_select=Етап відÑутній
issues.action_assignee=Виконавець
issues.action_assignee_no_select=Ðемає виконавцÑ
+issues.action_check=Ð’Ñтановити/знÑти позначку
+issues.action_check_all=Ð’Ñтановити/знÑти позначку з уÑÑ–Ñ… елементів
issues.opened_by=%[1]s відкрито <a href="%[2]s">%[3]s</a>
-issues.opened_by_fake=відкрито %[1]s кориÑтувачем %[2]s
+issues.opened_by_fake=%[1]s відкрито кориÑтувачем %[2]s
issues.previous=Попередній
issues.next=Далі
issues.open_title=Відкрито
issues.closed_title=Закрито
issues.draft_title=Чернетка
+issues.num_comments_1=%d коментар
issues.num_comments=%d коментарів
issues.commented_at=`прокоментував(ла) <a href="#%s">%s</a>`
issues.delete_comment_confirm=Ви впевнені, що хочете видалити цей коментар?
@@ -1111,33 +1558,56 @@ issues.context.quote_reply=Цитувати відповідь
issues.context.reference_issue=ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð² новій задачі
issues.context.edit=Редагувати
issues.context.delete=Видалити
+issues.no_content=Ðемає опиÑу.
+issues.close=Закрити задачу
+issues.comment_pull_merged_at=об'єднав(-ла) коміти %[1]s в %[2]s %[3]s
+issues.comment_manually_pull_merged_at=вручну об'єднав(-ла) коміти %[1]s в %[2]s %[3]s
+issues.close_comment_issue=Закрити з коментарем
issues.reopen_issue=Відкрити знову
+issues.reopen_comment_issue=Повторно відкрити з коментарем
issues.create_comment=Коментар
-issues.closed_at=`закрив цю задачу <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-issues.reopened_at=`повторно відкрив цю задачу <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+issues.comment.blocked_user=Ðеможливо Ñтворити або редагувати коментар, тому що ви заблоковані автором або влаÑником Ñховища.
+issues.closed_at=`закрив(ла) цю задачу <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+issues.reopened_at=`повторно відкрив(ла) цю задачу <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`згадано цю задачу в коміті <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-issues.ref_issue_from=`<a href="%[3]s">поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° цю задачу %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+issues.ref_issue_from=`<a href="%[3]s"> вказав(ла) на цю задачу %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from=`<a href="%[3]s">поÑлавÑÑ Ð½Ð° цей запит Ð·Ð»Ð¸Ñ‚Ñ‚Ñ %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from=`<a href="%[3]s">згадав запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ %[4]s, Ñкі закриють цю задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-issues.ref_reopening_from=`<a href="%[3]s">згадав запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ %[4]s, Ñкі повторно відкриють цю задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+issues.ref_reopening_from=`<a href="%[3]s">згадав запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ %[4]s, Ñкий знову відкриє цю задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closed_from=`<a href="%[3]s">закрив цю задачу %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">повторно відкрито цю задачу %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_from=`із %[1]s`
+issues.author=Ðвтор
+issues.author_helper=Цей кориÑтувач Ñ” автором.
issues.role.owner=ВлаÑник
+issues.role.owner_helper=Цей кориÑтувач — влаÑник Ñховища.
issues.role.member=УчаÑник
+issues.role.member_helper=Цей кориÑтувач Ñ” учаÑником організації, Ñкій належить це Ñховище.
+issues.role.collaborator=Співавтор
+issues.role.collaborator_helper=Цей кориÑтувач був запрошений до Ñпівпраці у Ñховищі.
+issues.role.first_time_contributor=УчаÑник, Ñкий вперше долучивÑÑ
+issues.role.first_time_contributor_helper=Це перший внеÑок цього кориÑтувача в Ñховищі.
+issues.role.contributor=Співавтор
+issues.role.contributor_helper=Цей кориÑтувач раніше вже вноÑив зміни до Ñховища.
issues.re_request_review=Повторно попроÑити рецензію
issues.is_stale=З чаÑу оÑтанньої перевірки в цей PR було внеÑено деÑкі зміни
issues.remove_request_review=Видалити запит рецензуваннÑ
issues.remove_request_review_block=Ðеможливо видалити запит рецензуваннÑ
-issues.dismiss_review=Відхилити рецензiю
-issues.dismiss_review_warning=Ви впевнені, що хочете відхилити цей відгук?
-issues.sign_in_require_desc=<a href="%s">ПідпишітьÑÑ</a> щоб приєднатиÑÑ Ð´Ð¾ обговореннÑ.
+issues.dismiss_review=Відхилити рецензію
+issues.dismiss_review_warning=Ви впевнені, що хочете відхилити рецензію?
+issues.sign_in_require_desc=<a href="%s">Увійдіть</a>, щоб приєднатиÑÑ Ð´Ð¾ розмови.
issues.edit=Редагувати
issues.cancel=Відмінити
issues.save=Зберегти
issues.label_title=Ðазва мітки
issues.label_description=ÐžÐ¿Ð¸Ñ Ð¼Ñ–Ñ‚ÐºÐ¸
-issues.label_color=Колір мітки
+issues.label_color=Колір
+issues.label_color_invalid=ÐедійÑний колір
+issues.label_exclusive=ЕкÑклюзивно
+issues.label_archive=Мітка архіву
+issues.label_archived_filter=Показати архівовані мітки
+issues.label_archive_tooltip=Ðрхівовані мітки типово виключаютьÑÑ Ð· пропозицій під Ñ‡Ð°Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ за мітками.
+issues.label_exclusive_order=ПорÑдок ÑортуваннÑ
issues.label_count=%d міток
issues.label_open_issues=%d відкритих задач
issues.label_edit=Редагувати
@@ -1145,9 +1615,9 @@ issues.label_delete=Видалити
issues.label_modify=Редагувати мітку
issues.label_deletion=Видалити мітку
issues.label_deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¼Ñ–Ñ‚ÐºÐ¸ видалÑÑ” Ñ—Ñ— з уÑÑ–Ñ… задач. Продовжити?
-issues.label_deletion_success=Мітку було видалено.
+issues.label_deletion_success=Мітку видалено.
issues.label.filter_sort.alphabetically=За алфавітом
-issues.label.filter_sort.reverse_alphabetically=З ÐºÑ–Ð½Ñ†Ñ Ð°Ð»Ñ„Ð°Ð²Ñ–Ñ‚Ñƒ
+issues.label.filter_sort.reverse_alphabetically=У зворотному алфавітному порÑдку
issues.label.filter_sort.by_size=Ðайменший розмір
issues.label.filter_sort.reverse_by_size=Ðайбільший розмір
issues.num_participants=%d учаÑників
@@ -1155,18 +1625,21 @@ issues.attachment.open_tab=`ÐатиÑніть щоб побачити "%s" у Ð
issues.attachment.download=`ÐатиÑніть щоб завантажити "%s"`
issues.subscribe=ПідпиÑатиÑÑ
issues.unsubscribe=ВідпиÑатиÑÑ
-issues.lock=Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ
-issues.unlock=Ð Ð¾Ð·Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ
-issues.lock.unknown_reason=Ðеможливо заблокувати задачу з невідомою причиною.
-issues.lock_duplicate=Задача не може бути заблокованим двічі.
+issues.unpin=Відкріпити
+issues.max_pinned=Ви не можете прикріпити більше задач
+issues.pin_comment=прикріпив(-ла) %s
+issues.unpin_comment=відкріпив(-ла) %s
+issues.lock=Блокувати обговореннÑ
+issues.unlock=Розблокувати обговореннÑ
+issues.lock_duplicate=ÐžÐ±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ– не може бути заблоковано двічі.
issues.unlock_error=Ðе можливо розблокувати задачу, Ñка не заблокована.
issues.lock_with_reason=заблоковано Ñк <strong>%s</strong> та обмежене Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñпівавторів %s
issues.lock_no_reason=заблоковано та обмежене Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñпівавторів %s
-issues.unlock_comment=розблоковане Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ %s
+issues.unlock_comment=розблокував(ла) Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ %s
issues.lock_confirm=Заблокувати
issues.unlock_confirm=Розблокувати
issues.lock.notice_1=- Інші кориÑтувачі не можуть додавати нові коментарі до цієї задачі.
-issues.lock.notice_2=- Ви й інші Ñпівавтори, Ñкі мають доÑтуп до цього репозиторію, можете залишати коментарі, Ñкі інші можуть бачити.
+issues.lock.notice_2=- Ви та інші Ñпівавтори, Ñкі мають доÑтуп до цього Ñховища, вÑе ще можете залишати коментарі, Ñкі можуть бачити інші.
issues.lock.notice_3=- Ви завжди зможете розблокувати цю задачу в майбутньому.
issues.unlock.notice_1=- Кожен зможе прокоментувати цю задачу ще раз.
issues.unlock.notice_2=- Ви завжди зможете заблокувати цю задачу в майбутньому.
@@ -1175,37 +1648,61 @@ issues.lock.title=Заблокувати Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— задÐ
issues.unlock.title=Розблокувати Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— задачі.
issues.comment_on_locked=Ви не можете коментувати заблоковану задачу.
issues.delete=Видалити
+issues.delete.title=Видалити цю задачу?
+issues.delete.text=Ви дійÑно хочете видалити цю задачу? (Це видалить веÑÑŒ вміÑÑ‚. ÐатоміÑть подумайте про те, щоб закрити Ñ—Ñ— та зберегти в архіві)
issues.tracker=ВідÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡Ð°Ñу
-
+issues.timetracker_timer_start=ЗапуÑтити таймер
+issues.timetracker_timer_stop=Зупинити таймер
+issues.timetracker_timer_discard=Скинути таймер
+issues.timetracker_timer_manually_add=Додати чаÑ
+
+issues.time_estimate_set=Ð’Ñтановити орієнтовний чаÑ
+issues.time_estimate_display=Оцінка: %s
+issues.change_time_estimate_at=змінено приблизний Ñ‡Ð°Ñ Ð½Ð° <b>%[1]s</b> %[2]s
+issues.remove_time_estimate_at=видалено оцінку чаÑу %s
+issues.time_estimate_invalid=Ðевірний формат розрахунку чаÑу
issues.tracker_auto_close=Таймер буде автоматично зупинено, коли Ñ†Ñ Ð·Ð°Ð´Ð°Ñ‡Ð° буде закрита
issues.tracking_already_started=`Ви вже почали відÑтежувати Ñ‡Ð°Ñ Ð´Ð»Ñ <a href="%s">іншої задачі</a>!`
+issues.stop_tracking=Зупинити таймер
+issues.stop_tracking_history=працював Ð´Ð»Ñ <b>%[1]s</b> %[2]s
+issues.cancel_tracking=Скинути
+issues.cancel_tracking_history=`ÑкаÑував(-ла) відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡Ð°Ñу %s`
issues.del_time=Видалити цей журнал чаÑу
+issues.add_time_history=додав(ла) витрачений Ñ‡Ð°Ñ <b>%[1]s</b> %[2]s
issues.del_time_history=`видалив витрачений Ñ‡Ð°Ñ %s`
+issues.add_time_manually=Вручну додати чаÑ
issues.add_time_hours=Години
issues.add_time_minutes=Хвилини
issues.add_time_sum_to_small=Ð§Ð°Ñ Ð½Ðµ введено.
issues.time_spent_total=Загальний витрачений чаÑ
issues.time_spent_from_all_authors=`Загальний витрачений чаÑ: %s`
-issues.due_date=Дата завершеннÑ
-issues.invalid_due_date_format=Дата Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ Ð¼Ð°Ñ” бути в форматі 'ррр-мм-дд'.
-issues.error_modifying_due_date=Ðе вдалоÑÑ Ð·Ð¼Ñ–Ð½Ð¸Ñ‚Ð¸ дату завершеннÑ.
+issues.due_date=Строк виконаннÑ
+issues.invalid_due_date_format=Формат Ñтроку Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ð¸Ð½ÐµÐ½ бути 'рррр-мм-дд'.
+issues.error_modifying_due_date=Ðе вдалоÑÑ Ð·Ð¼Ñ–Ð½Ð¸Ñ‚Ð¸ термін виконаннÑ.
issues.error_removing_due_date=Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ дату завершеннÑ.
issues.push_commit_1=додав %d коміт %s
issues.push_commits_n=додав %d коміти(-ів) %s
-issues.force_push_codes=`примуÑово залито %[1]s з <a class="ui sha" href="%[3]s"><code>%[2]s</code></a> до <a class="ui sha" href="%[5]s"><code>%[4]s</code></a> %[6]s`
+issues.force_push_codes=`примуÑово надіÑлано %[1]s з <a class="ui sha" href="%[3]s"><code>%[2]s</code></a> до <a class="ui sha" href="%[5]s"><code>%[4]s</code></a> %[6]s`
issues.force_push_compare=ПорівнÑти
issues.due_date_form=рррр-мм-дд
issues.due_date_form_add=Додати дату завершеннÑ
issues.due_date_form_edit=Редагувати
issues.due_date_form_remove=Видалити
+issues.due_date_not_writer=Вам потрібен доÑтуп на Ð·Ð°Ð¿Ð¸Ñ Ð´Ð¾ цього Ñховища, щоб оновити дату Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ–.
issues.due_date_not_set=Термін Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð½Ðµ вÑтановлений.
issues.due_date_added=додав(ла) дату Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ %s %s
+issues.due_date_modified=змінив(-ла) термін Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð· %[2]s на %[1]s %[3]s
issues.due_date_remove=видалив(ла) дату Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ %s %s
issues.due_date_overdue=ПроÑтрочено
-issues.due_date_invalid=Термін дії не дійÑний або знаходитьÑÑ Ð·Ð° межами допуÑтимого діапазону. Будь лаÑка викориÑтовуйте формат 'yyyy-mm-dd'.
+issues.due_date_invalid=Термін дії не дійÑний або знаходитьÑÑ Ð·Ð° межами діапазону. Будь лаÑка, викориÑтовуйте формат 'рррр-мм-дд'.
issues.dependency.title=ЗалежноÑті
+issues.dependency.issue_no_dependencies=ЗалежноÑтей не вÑтановлено.
+issues.dependency.pr_no_dependencies=ЗалежноÑтей не вÑтановлено.
+issues.dependency.no_permission_1=У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” дозволу на Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ %d залежноÑті
+issues.dependency.no_permission_n=Ви не маєте дозволу на Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ %d залежноÑтей
+issues.dependency.no_permission.can_remove=Ви не маєте дозволу на Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ñ†Ñ–Ñ”Ñ— залежноÑті, але можете видалити Ñ—Ñ—.
issues.dependency.add=Додати залежніÑть…
issues.dependency.cancel=Відмінити
issues.dependency.remove=Видалити
@@ -1213,39 +1710,45 @@ issues.dependency.remove_info=Видалити цю залежніÑть
issues.dependency.added_dependency=`додав нову залежніÑть %s`
issues.dependency.removed_dependency=`видалив залежніÑть %s`
issues.dependency.pr_closing_blockedby=Ð—Ð°ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñ†ÑŒÐ¾Ð³Ð¾ запиту Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¾ наÑтупними задачами
-issues.dependency.issue_closing_blockedby=Ð—Ð°ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñ†Ñ–Ñ”Ñ— задачи заблоковано наÑтупними задачами
-issues.dependency.issue_close_blocks=Ð¦Ñ Ð·Ð°Ð´Ð°Ñ‡Ð° блокує Ð·Ð°ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¸Ñ… задач
+issues.dependency.issue_closing_blockedby=Ð—Ð°ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñ†Ñ–Ñ”Ñ— задачі заблоковано наÑтупними задачами
+issues.dependency.issue_close_blocks=Ð¦Ñ Ð·Ð°Ð´Ð°Ñ‡Ð° блокує Ð·Ð°ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ð½Ð°Ñтупних задач
issues.dependency.pr_close_blocks=Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð±Ð»Ð¾ÐºÑƒÑ” Ð·Ð°ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¸Ñ… задач
-issues.dependency.issue_close_blocked=Вам потрібно закрити вÑÑ– задачі, що блокують цю задачу, перед Ñ—Ñ— закриттÑм.
+issues.dependency.issue_close_blocked=Перш ніж закрити це завданнÑ, вам потрібно закрити вÑÑ– завданнÑ, що блокують його.
+issues.dependency.issue_batch_close_blocked=Ðеможливо пакетно закрити обрані задачі, оÑкільки задача #%d вÑе ще має відкриті залежноÑті
issues.dependency.pr_close_blocked=Вам потрібно закрити вÑÑ– задачі, що блокують цей запит, перед його злиттÑм.
issues.dependency.blocks_short=Блоки
issues.dependency.blocked_by_short=Залежить від
issues.dependency.remove_header=Видалити залежніÑть
issues.dependency.issue_remove_text=Це призведе до Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾Ñті з цієї задачі. Продовжити?
-issues.dependency.pr_remove_text=Це призведе до Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾Ñті з цього пулл-реквеÑту. Продовжити?
+issues.dependency.pr_remove_text=Це вилучить залежніÑть з цього запиту на злиттÑ. Продовжити?
issues.dependency.setting=Увімкнути залежноÑті Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡ та запитів на злиттÑ
issues.dependency.add_error_same_issue=Ви не можете зробити задачу залежною від Ñебе.
-issues.dependency.add_error_dep_issue_not_exist=ЗалежніÑть Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ– не Ñ–Ñнує.
+issues.dependency.add_error_dep_issue_not_exist=Залежної задачі не Ñ–Ñнує.
issues.dependency.add_error_dep_not_exist=ЗалежніÑть не Ñ–Ñнує.
issues.dependency.add_error_dep_exists=ЗалежніÑть уже Ñ–Ñнує.
issues.dependency.add_error_cannot_create_circular=Ви не можете Ñтворити залежніÑть з двома задачами, Ñкі блокують одна одну.
issues.dependency.add_error_dep_not_same_repo=Обидві задачі повинні бути в одному репозиторії.
-issues.review.self.approval=Ви не можете Ñхвалити влаÑний пулл-реквеÑÑ‚.
+issues.review.self.approval=Ви не можете затвердити влаÑний запит на злиттÑ.
issues.review.self.rejection=Ви не можете надіÑлати запит на зміну на влаÑний пулл-реквеÑÑ‚.
-issues.review.approve=зміни затверджено %s
-issues.review.dismissed=відхилено відгук %s %s
+issues.review.approve=затвердив(ла) ці зміни %s
+issues.review.comment=рецензовано %s
+issues.review.dismissed=відхилив(ла) рецензію %s %s
issues.review.dismissed_label=Відхилено
issues.review.left_comment=додав коментар
-issues.review.content.empty=Запрошуючи зміни, ви зобов'Ñзані залишити коментар з поÑÑненнÑми Ñвоїх побажань відноÑно Pull Request'а.
+issues.review.content.empty=Вам потрібно залишити коментар із зазначеннÑм бажаної зміни (змін).
issues.review.reject=зробив запит змін %s
-issues.review.wait=попроÑив рецензію %s
+issues.review.wait=запроÑили на рецензію %s
issues.review.add_review_request=попроÑив рецензію від %s %s
issues.review.remove_review_request=видалив запит на рецензію до %s %s
issues.review.remove_review_request_self=відмовивÑÑ Ñ€ÐµÑ†ÐµÐ½Ð·ÑƒÐ²Ð°Ñ‚Ð¸ %s
issues.review.pending=ОчікуваннÑ
+issues.review.pending.tooltip=Цей коментар наразі не видно іншим кориÑтувачам. Щоб відправити Ñвій коментар, виберіть "%s" -> "%s/%s/%s" у верхній чаÑтині Ñторінки.
issues.review.review=Рецензії
issues.review.reviewers=Рецензенти
issues.review.outdated=ЗаÑтарілі
+issues.review.outdated_description=ВміÑÑ‚ змінивÑÑ Ð· моменту ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ коментарÑ
+issues.review.option.show_outdated_comments=Показати заÑтарілі коментарі
+issues.review.option.hide_outdated_comments=Приховати заÑтарілі коментарі
issues.review.show_outdated=Показати заÑтарілі
issues.review.hide_outdated=Приховати заÑтарілі
issues.review.show_resolved=Показати вирішене
@@ -1253,7 +1756,12 @@ issues.review.hide_resolved=Приховати вирішене
issues.review.resolve_conversation=Завершити обговореннÑ
issues.review.un_resolve_conversation=Поновити обговореннÑ
issues.review.resolved_by=позначив Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ð¼
-issues.review.commented=Коментар
+issues.review.commented=Коментувати
+issues.review.official=Затверджено
+issues.review.requested=ОчікуєтьÑÑ Ñ€Ð¾Ð·Ð³Ð»Ñд
+issues.review.rejected=Запит на зміни
+issues.review.stale=Оновлено з моменту затвердженнÑ
+issues.review.unofficial=Ðевраховане затвердженнÑ
issues.assignee.error=Додано не вÑÑ–Ñ… виконавців через непередбачену помилку.
issues.reference_issue.body=Тіло
issues.content_history.deleted=видалено
@@ -1261,21 +1769,39 @@ issues.content_history.edited=відредаговано
issues.content_history.created=Ñтворено
issues.content_history.delete_from_history=Видалити з Ñ–Ñторії
issues.content_history.delete_from_history_confirm=Видалити з Ñ–Ñторії?
-issues.content_history.options=ÐалаштуваннÑ
+issues.content_history.options=Параметри
+issues.reference_link=ПоÑиланнÑ: %s
compare.compare_base=оÑнова
compare.compare_head=порівнÑти
pulls.desc=Увімкнути запити на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° оглÑд коду.
pulls.new=Ðовий запит на злиттÑ
+pulls.new.blocked_user=Ðеможливо Ñтворити запит на злиттÑ, тому що ви заблоковані влаÑником Ñховища.
+pulls.new.must_collaborator=Ви повинні бути учаÑником, щоб Ñтворити запит на злиттÑ.
+pulls.edit.already_changed=Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ зміни до запиту на злиттÑ. Схоже, вміÑÑ‚ вже змінено іншим кориÑтувачем. Будь лаÑка, оновіть Ñторінку Ñ– Ñпробуйте редагувати ще раз, щоб уникнути перезапиÑу Ñ—Ñ… змін
+pulls.view=ПереглÑнути запит на злиттÑ
pulls.compare_changes=Ðовий запит на злиттÑ
+pulls.allow_edits_from_maintainers_desc=КориÑтувачі з доÑтупом на Ð·Ð°Ð¿Ð¸Ñ Ð´Ð¾ базової гілки також можуть завантажувати Ñвої зміни до цієї гілки
+pulls.allow_edits_from_maintainers_err=ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ðµ вдалоÑÑ
pulls.compare_changes_desc=ПорівнÑти дві гілки Ñ– Ñтворити запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð»Ñ Ð·Ð¼Ñ–Ð½.
+pulls.has_viewed_file=ПереглÑдів
+pulls.has_changed_since_last_review=Змінено з моменту вашого оÑтаннього відгуку
+pulls.viewed_files_label=ПереглÑнуто %[1]d / %[2]d файлів
+pulls.expand_files=Розгорнути вÑÑ– файли
+pulls.collapse_files=Згорнути вÑÑ– файли
pulls.compare_base=злити в
-pulls.compare_compare=pull з
-pulls.switch_comparison_type=Перемкнути виглÑд порівнÑннÑ
+pulls.switch_comparison_type=Перемкнути тип порівнÑннÑ
pulls.switch_head_and_base=ПомінÑти міÑцÑми оÑновну та базову гілку
pulls.filter_branch=Фільтр по гілці
+pulls.show_all_commits=Показати вÑÑ– коміти
+pulls.show_changes_since_your_last_review=Показати зміни піÑÐ»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ оÑтаннього відгуку
+pulls.showing_only_single_commit=ВідображаютьÑÑ Ð»Ð¸ÑˆÐµ зміни коміту %[1]s
+pulls.showing_specified_commit_range=ВідображаютьÑÑ Ð»Ð¸ÑˆÐµ зміни між %[1]s..%[2]s
+pulls.select_commit_hold_shift_for_range=Виберіть коміт. ÐатиÑніть клавішу Shift + клацніть, щоб виділити діапазон
+pulls.filter_changes_by_commit=Фільтр за комітом
pulls.nothing_to_compare=Ці гілки однакові. Ðемає необхідноÑті Ñтворювати запитів на злиттÑ.
+pulls.nothing_to_compare_have_tag=Виділена гілка або мітка ідентичні.
pulls.nothing_to_compare_and_allow_empty_pr=Одинакові гілки. Цей PR буде порожнім.
pulls.has_pull_request=`Запит Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð»Ñ Ñ†Ð¸Ñ… гілок вже Ñ–Ñнує: <a href="%[1]s">%[2]s#%[3]d</a>`
pulls.create=Створити запит на злиттÑ
@@ -1285,114 +1811,154 @@ pulls.change_target_branch_at=`змінена цільова гілка з <b>%s
pulls.tab_conversation=ОбговореннÑ
pulls.tab_commits=Коміти
pulls.tab_files=Змінені файли
-pulls.reopen_to_merge=Будь лаÑка перевідкрийте цей запит щоб здіÑнити операцію злиттÑ.
+pulls.reopen_to_merge=Будь лаÑка, заново відкрийте цей запит щоб виконати злиттÑ.
pulls.cant_reopen_deleted_branch=Цей запит не можна повторно відкрити, оÑкільки гілку видалено.
pulls.merged=Злито
+pulls.merged_success=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ ÑƒÑпішно об'єднано Ñ– закрито
+pulls.closed=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð¾
pulls.manually_merged=Ручне злиттÑ
-pulls.is_closed=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð±ÑƒÐ»Ð¾ закрито.
-pulls.title_wip_desc=`<a href="#">Почніть заголовок з <strong>%s</strong></a> щоб запобігти випадковому злиттю запитів.`
-pulls.cannot_merge_work_in_progress=Цей пулл-реквеÑÑ‚ позначений Ñк прийнÑтий в опрацюваннÑ.
-pulls.still_in_progress=Ð’Ñе ще в процеÑÑ–?
+pulls.merged_info_text=Гілку %s тепер можна видалити.
+pulls.is_closed=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð¾.
+pulls.title_wip_desc=`<a href="#">Почніть заголовок з <strong>%s</strong></a> щоб запобігти випадковому об'єднанню.`
+pulls.cannot_merge_work_in_progress=Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¾ Ñк незавершений.
+pulls.still_in_progress=Ще не закінчено?
pulls.add_prefix=Додати Ð¿Ñ€ÐµÑ„Ñ–ÐºÑ <strong>%s</strong>
pulls.remove_prefix=Видалити Ð¿Ñ€ÐµÑ„Ñ–ÐºÑ <strong>%s</strong>
-pulls.data_broken=ЗміÑÑ‚ цього запиту було порушено внаÑлідок Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— Форком. Цей запит Ñ‚ÑгнетьÑÑ Ñ‡ÐµÑ€ÐµÐ· відÑутніÑть інформації про вилученнÑ.
-pulls.files_conflicted=Цей запит має зміни, що конфліктують з цільовою гілкою.
-pulls.is_checking=Триває перевірка конфліктів, будь лаÑка обновіть Ñторінку дещо пізніше.
+pulls.data_broken=Збій цього запиту на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‡ÐµÑ€ÐµÐ· відÑутніÑть інформації про форк.
+pulls.files_conflicted=Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¼Ð°Ñ” зміни, що конфліктують з цільовою гілкою.
+pulls.is_checking=Перевірка конфліктів об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ (merge) ...
+pulls.is_ancestor=Цю гілку вже включено до цільової гілки. Ðема чого об'єднувати.
pulls.required_status_check_failed=ДеÑкі необхідні перевірки виконані з помилками.
pulls.required_status_check_missing=Декілька з необхідних перевірок відÑутні.
pulls.required_status_check_administrator=Як адмініÑтратор ви вÑе одно можете об'єднати цей запит на злиттÑ.
-pulls.can_auto_merge_desc=Цей запит можна об'єднати автоматично.
-pulls.cannot_auto_merge_desc=Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð½Ðµ може бути злитий автоматично через конфлікти.
-pulls.cannot_auto_merge_helper=Злийте вручну Ð´Ð»Ñ Ð²Ð¸Ñ€Ñ–ÑˆÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ð»Ñ–ÐºÑ‚Ñ–Ð².
-pulls.num_conflicting_files_1=%d конфліктуючий файл
-pulls.num_conflicting_files_n=%d конфліктуючі файли
+pulls.can_auto_merge_desc=Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¼Ð¾Ð¶Ð½Ð° об'єднати автоматично.
+pulls.cannot_auto_merge_desc=Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð½Ðµ може бути об'єднано автоматично через конфлікти.
+pulls.cannot_auto_merge_helper=Об'єднайте вручну Ð´Ð»Ñ Ð²Ð¸Ñ€Ñ–ÑˆÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ð»Ñ–ÐºÑ‚Ñ–Ð².
+pulls.num_conflicting_files_1=%d конфліктний файл
+pulls.num_conflicting_files_n=%d конфліктних файлів
pulls.approve_count_1=%d ÑхваленнÑ
pulls.approve_count_n=%d Ñхвалень
pulls.reject_count_1=%d запит на зміну
-pulls.reject_count_n=%d запити на зміну
-pulls.waiting_count_1=очікуєтьÑÑ %d рецензіÑ
+pulls.reject_count_n=%d запитів на зміну
+pulls.waiting_count_1=очікуєтьÑÑ %d рецензій
pulls.waiting_count_n=очікуєтьÑÑ %d рецензії(й)
-pulls.wrong_commit_id=id коміту повинен бути id коміту в цільовій гілці
-
-pulls.no_merge_desc=Цей запити на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð½ÐµÐ¼Ð¾Ð¶Ð»Ð¸Ð²Ð¾ злити, оÑкільки вÑÑ– параметри об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð¾.
-pulls.no_merge_helper=Увімкніть параметри Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð² налаштуваннÑÑ… Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ Ð°Ð±Ð¾ злийте запити на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ñ€ÑƒÑ‡Ð½Ñƒ.
-pulls.no_merge_wip=Цей пулл-реквеÑÑ‚ не можливо об'єднати, тому-що він вже виконуєтьÑÑ.
-pulls.no_merge_not_ready=Цей запит не готовий до злиттÑ, перевірте ÑÑ‚Ð°Ñ‚ÑƒÑ Ñ€ÐµÑ†ÐµÐ½Ð·Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ñ– ÑÑ‚Ð°Ñ‚ÑƒÑ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸.
-pulls.no_merge_access=Ви не авторизовані, щоб виконати цей запит на злиттÑ.
-pulls.merge_pull_request=Створити коміт зі злиттÑм
+pulls.wrong_commit_id=ідентифікатор коміту повинен бути ідентифікатором коміту в цільовій гілці
+
+pulls.no_merge_desc=Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð½ÐµÐ¼Ð¾Ð¶Ð»Ð¸Ð²Ð¾ об'єднати, оÑкільки вÑÑ– параметри об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñховищ вимкнено.
+pulls.no_merge_helper=Увімкніть параметри об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð² налаштуваннÑÑ… Ñховища або об'єднайте запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ñ€ÑƒÑ‡Ð½Ñƒ.
+pulls.no_merge_wip=Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð½ÐµÐ¼Ð¾Ð¶Ð»Ð¸Ð²Ð¾ об'єднаний, оÑкільки він позначений Ñк незавершений.
+pulls.no_merge_not_ready=Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‰Ðµ не готовий до об'єднаннÑ, перевірте ÑтатуÑи Ñ€ÐµÑ†ÐµÐ½Ð·Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ñ‚Ð° перевірки.
+pulls.no_merge_access=Ви не авторизовані об'єднувати цей запит на злиттÑ.
+pulls.merge_pull_request=Створити коміт об'єднаннÑ
pulls.rebase_merge_pull_request=Перебазувати, а потім виконати Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿ÐµÑ€ÐµÐ¼Ð¾Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñм
-pulls.rebase_merge_commit_pull_request=Перебазувати, а потім Ñтворити коміт злиттÑ
+pulls.rebase_merge_commit_pull_request=Перебазувати, а потім Ñтворити коміт об'єднаннÑ
pulls.squash_merge_pull_request=Створити зварений (squash) коміт
pulls.merge_manually=Об’єднано вручну
-pulls.merge_commit_id=ID коміту злиттÑ
-pulls.require_signed_wont_sign=Гілка вимагає підпиÑаних комітів, але це Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð½Ðµ буде підпиÑано
+pulls.merge_commit_id=Ідентифікатор коміту об’єднаннÑ
+pulls.require_signed_wont_sign=Гілка вимагає підпиÑаних комітів, але це об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð½Ðµ буде підпиÑано
-pulls.invalid_merge_option=Цей параметр Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð½Ðµ можна викориÑтовувати Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Pull Request'а.
-pulls.merge_conflict=Ð—Ð»Ð¸Ñ‚Ñ‚Ñ Ð½Ðµ вдалоÑÑ: Був конфлікт при злиттÑ. Підказка: Ñпробуйте іншу Ñтратегію
+pulls.invalid_merge_option=Цей параметр об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð½Ðµ можна викориÑтовувати Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ запиту на злиттÑ.
+pulls.merge_conflict=Об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð½Ðµ вдалоÑÑ: Під Ñ‡Ð°Ñ Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ð¸Ð½Ð¸Ðº конфлікт. Підказка: Спробуйте іншу Ñтратегію
pulls.merge_conflict_summary=Помилка
-pulls.rebase_conflict=Ð—Ð»Ð¸Ñ‚Ñ‚Ñ Ð½Ðµ вдалоÑÑ: відбувÑÑ ÐºÐ¾Ð½Ñ„Ð»Ñ–ÐºÑ‚ під Ñ‡Ð°Ñ Ð·Ð»Ð¸Ñ‚Ñ‚Ñ: %[1]s. Підказка: Ñпробуйте іншу Ñтратегію
+pulls.rebase_conflict=Об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð½Ðµ вдалоÑÑ: відбувÑÑ ÐºÐ¾Ð½Ñ„Ð»Ñ–ÐºÑ‚ під Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ±Ð°Ð·ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñƒ: %[1]s. Підказка: Ñпробуйте іншу Ñтратегію
pulls.rebase_conflict_summary=Помилка
-pulls.unrelated_histories=Помилка злиттÑ: head та base Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð½Ðµ мають Ñпільної Ñ–Ñторії. Підказка: Ñпробуйте іншу Ñтратегію
-pulls.merge_out_of_date=Помилка злиттÑ: base було оновлено, поки відбувалоÑÑ Ð·Ð»Ð¸Ñ‚Ñ‚Ñ. Підказка: Ñпробуйте знову.
pulls.push_rejected_summary=Повне Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ відмову
-pulls.open_unmerged_pull_exists=`Ви не можете знову відкрити, оÑкільки вже Ñ–Ñнує запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ (%d) з того ж Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ Ð· тією ж інформацією про Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ– в очікуванні.`
-pulls.status_checking=ДеÑкі перевірки знаходÑтьÑÑ Ð½Ð° розглÑді
+pulls.open_unmerged_pull_exists=`Ви не можете повторно відкрити цей запит на злиттÑ, оÑкільки вже Ñ–Ñнує один (#%d) з ідентичними влаÑтивоÑÑ‚Ñми.`
+pulls.status_checking=ДеÑкі перевірки ще не завершені
pulls.status_checks_success=Ð’ÑÑ– перевірки були уÑпішними
pulls.status_checks_warning=Декілька перевірок завершилиÑÑ Ð· попередженнÑми
-pulls.status_checks_failure=Декілька перевірок не були уÑпішними
+pulls.status_checks_failure=ДеÑкі перевірки не Ñпрацювали
pulls.status_checks_error=Декілька перевірок завершилиÑÑ Ð· помилками
pulls.status_checks_requested=Обов'Ñзково
pulls.status_checks_details=Подробиці
-pulls.update_branch=Оновити гілку шлÑхом злиттÑ
+pulls.status_checks_hide_all=Приховати вÑÑ– перевірки
+pulls.status_checks_show_all=Показати вÑÑ– перевірки
+pulls.update_branch=Оновити гілку шлÑхом об'єднаннÑ
pulls.update_branch_rebase=Оновити гілку перебазуваннÑм
-pulls.update_branch_success=ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð³Ñ–Ð»ÐºÐ¸ пройшло уÑпішно
+pulls.update_branch_success=ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð³Ñ–Ð»ÐºÐ¸ уÑпішне
pulls.update_not_allowed=Ви не можете оновити гілку
pulls.outdated_with_base_branch=Ð¦Ñ Ð³Ñ–Ð»ÐºÐ° заÑтаріла відноÑно базової гілки
+pulls.close=Закрити запит на злиттÑ
pulls.closed_at=`закрив цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at=`повторно відкрив цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+pulls.cmd_instruction_hint=ПереглÑнути інÑтрукції командного Ñ€Ñдка
+pulls.cmd_instruction_merge_title=Об'єднати
+pulls.cmd_instruction_merge_desc=Об'єднати зміни і оновити на Gitea.
+pulls.clear_merge_message=ОчиÑтити Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ об'єднаннÑ
+pulls.auto_merge_button_when_succeed=(Якщо перевірки уÑпішні)
+pulls.auto_merge_when_succeed=Ðвтоматичне об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð¿Ñ–ÑÐ»Ñ ÑƒÑпішного Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð²ÑÑ–Ñ… перевірок
+pulls.auto_merge_cancel_schedule=СкаÑувати автоматичне об'єднаннÑ
+pulls.auto_merge_not_scheduled=Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð½Ðµ плануєтьÑÑ Ð¾Ð±'єднувати автоматично.
+pulls.delete.title=Видалити цей запит на злиттÑ?
+pulls.upstream_diverging_prompt_behind_1=Ð¦Ñ Ð³Ñ–Ð»ÐºÐ° на %[1]d коміт позаду %[2]s
+pulls.upstream_diverging_prompt_behind_n=Ð¦Ñ Ð³Ñ–Ð»ÐºÐ° на %[1]d комітів позаду %[2]s
+pulls.upstream_diverging_prompt_base_newer=Базова гілка %s має нові зміни
+pulls.upstream_diverging_merge_confirm=Хочете об’єднати "%[1]s" з "%[2]s"?
+pull.deleted_branch=(видалена):%s
+pull.agit_documentation=ПереглÑнути документацію про AGit
milestones.new=Ðовий етап
milestones.closed=Закрито %s
+milestones.update_ago=Оновлено %s
milestones.no_due_date=Ðемає дати завершеннÑ
milestones.open=Відкрити
milestones.close=Закрити
+milestones.completeness=<strong>%d%%</strong> завершено
milestones.create=Створити етап
milestones.title=Заголовок
milestones.desc=ОпиÑ
-milestones.due_date=Дата Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ (опціонально)
+milestones.due_date=Дата Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ (необов’Ñзково)
milestones.clear=ОчиÑтити
milestones.invalid_due_date_format=Дата Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¼Ð°Ñ” бути в форматі 'рррр-мм-дд'.
+milestones.create_success=Етап "%s" Ñтворено.
milestones.edit=Редагувати етап
-milestones.edit_subheader=Створюйте етапи Ð´Ð»Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ— ваших задач.
+milestones.edit_subheader=Етапи впорÑдковують задачі та відÑтежують прогреÑ.
milestones.cancel=Відмінити
milestones.modify=Оновити етап
+milestones.edit_success=Етап '%s' оновлено.
milestones.deletion=Видалити етап
-milestones.deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ ÐµÑ‚Ð°Ð¿Ñƒ призведе до його Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð· уÑÑ–Ñ… пов'Ñзаних задач. Продовжити?
-milestones.deletion_success=Етап уÑпішно видалено.
+milestones.deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ ÐµÑ‚Ð°Ð¿Ñƒ видалÑÑ” його з уÑÑ–Ñ… пов'Ñзаних з ним задач. Продовжити?
+milestones.deletion_success=Етап видалено.
milestones.filter_sort.name=Ðазва
-milestones.filter_sort.least_complete=Менш повне
-milestones.filter_sort.most_complete=Більш повне
-milestones.filter_sort.most_issues=Ðайбільш задач
-milestones.filter_sort.least_issues=Ðайменш задач
-
-
+milestones.filter_sort.earliest_due_data=Ðайраніший Ñтрок
+milestones.filter_sort.latest_due_date=ОÑтанній Ñтрок
+milestones.filter_sort.most_issues=Ðайбільше задач
+milestones.filter_sort.least_issues=Ðайменше задач
+
+signing.will_sign=Цей коміт буде підпиÑано ключем "%s".
+signing.wont_sign.error=Виникла помилка під Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ можливоÑті підпиÑÐ°Ð½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñƒ.
+signing.wont_sign.nokey=Ðемає ключа Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¿Ð¸ÑÐ°Ð½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ коміту.
+signing.wont_sign.never=Коміти ніколи не підпиÑуютьÑÑ.
+signing.wont_sign.always=Коміти завжди підпиÑуютьÑÑ.
+signing.wont_sign.pubkey=Коміт не буде підпиÑано, оÑкільки у Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” публічного ключа, пов'Ñзаного з вашим обліковим запиÑом.
+signing.wont_sign.twofa=Ð”Ð»Ñ Ð¿Ñ–Ð´Ð¿Ð¸ÑÐ°Ð½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ–Ð² у Ð²Ð°Ñ Ð¼Ð°Ñ” бути увімкнена двофакторна автентифікаціÑ.
+signing.wont_sign.parentsigned=Цей коміт не буде підпиÑано, оÑкільки не підпиÑано батьківÑький коміт.
+signing.wont_sign.basesigned=Об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð½Ðµ буде підпиÑане, оÑкільки базовий коміт не підпиÑано.
+signing.wont_sign.headsigned=Об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð½Ðµ буде підпиÑане, оÑкільки не підпиÑано головний коміт.
+signing.wont_sign.commitssigned=Об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð½Ðµ буде підпиÑане, оÑкільки вÑÑ– пов'Ñзані з ним коміти не підпиÑані.
+signing.wont_sign.approved=Об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð½Ðµ буде підпиÑане, оÑкільки Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð½Ðµ затверджено.
+signing.wont_sign.not_signed_in=Ви не увійшли до ÑиÑтеми.
+
+ext_wiki=ДоÑтуп до зовнішньої вікі
ext_wiki.desc=ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° зовнішню вікі.
wiki=Вікі
wiki.welcome=ЛаÑкаво проÑимо до Вікі.
-wiki.welcome_desc=Wiki дозволÑÑ” пиÑати та ділитиÑÑ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ”ÑŽ з Ñпівавторами.
-wiki.desc=Пишіть та обмінюйтеÑÑ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ”ÑŽ із Ñпівавторами.
+wiki.welcome_desc=Wiki дозволÑÑ” пиÑати та ділитиÑÑ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ”ÑŽ зі Ñпівавторами.
+wiki.desc=Пишіть та обмінюйтеÑÑ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ”ÑŽ зі Ñпівавторами.
wiki.create_first_page=Створити першу Ñторінку
wiki.page=Сторінка
wiki.filter_page=Фільтр Ñторінок
wiki.new_page=Сторінка
+wiki.page_title=Заголовок Ñторінки
+wiki.page_content=ЗміÑÑ‚ Ñторінки
wiki.default_commit_message=Ðапишіть примітку про Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— Ñторінки (необов'Ñзково).
wiki.save_page=Зберегти Ñторінку
wiki.last_commit_info=%s редагував цю Ñторінку %s
@@ -1402,12 +1968,19 @@ wiki.file_revision=Ревізії Ñторінки
wiki.wiki_page_revisions=Ревізії вікі Ñторінок
wiki.back_to_wiki=ПовернутиÑÑŒ на Ñторінку Вікі
wiki.delete_page_button=Видалити Ñторінку
-wiki.page_already_exists=Вікі-Ñторінка з таким Ñамим ім'Ñм вже Ñ–Ñнує.
+wiki.delete_page_notice_1=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²Ñ–ÐºÑ–-Ñторінки "%s" не може бути ÑкаÑовано. Продовжити?
+wiki.page_already_exists=Сторінка Вікі з такою назвою вже Ñ–Ñнує.
+wiki.reserved_page=Ðазва Ñторінки вікі "%s" зарезервована.
wiki.pages=Сторінки
wiki.last_updated=ОÑтанні Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ %s
wiki.page_name_desc=Введіть назву вікі-Ñторінки. ДеÑкі із Ñпеціальних імен: 'Home', '_Sidebar' та '_Footer'.
+wiki.original_git_entry_tooltip=ПереглÑд оригінального файлу Git заміÑть викориÑÑ‚Ð°Ð½Ð½Ñ Ð´Ñ€ÑƒÐ¶Ð½ÑŒÐ¾Ð³Ð¾ поÑиланнÑ.
activity=ÐктивніÑть
+activity.navbar.pulse=ПульÑ
+activity.navbar.code_frequency=ЧаÑтота коду
+activity.navbar.contributors=Співавтори
+activity.navbar.recent_commits=Ðещодавні коміти
activity.period.filter_label=Період:
activity.period.daily=1 день
activity.period.halfweekly=3 дні
@@ -1417,25 +1990,25 @@ activity.period.quarterly=3 міÑÑці
activity.period.semiyearly=6 міÑÑців
activity.period.yearly=1 рік
activity.overview=ОглÑд
-activity.active_prs_count_1=<strong>%d</strong> Ðктивний запити на злиттÑ
+activity.active_prs_count_1=<strong>%d</strong> Ðктивний запит на злиттÑ
activity.active_prs_count_n=<strong>%d</strong> Ðктивні запити на злиттÑ
-activity.merged_prs_count_1=Злитий запит на злиттÑ
-activity.merged_prs_count_n=Злиті запити на злиттÑ
+activity.merged_prs_count_1=Об'єднаний запит на злиттÑ
+activity.merged_prs_count_n=Об'єднані запити на злиттÑ
activity.opened_prs_count_1=Запропонований запит на злиттÑ
-activity.opened_prs_count_n=Запропонованих запитів на злиттÑ
+activity.opened_prs_count_n=Запропоновані запити на злиттÑ
activity.title.user_1=%d кориÑтувачем
activity.title.user_n=%d кориÑтувачами
activity.title.prs_1=%d Запит на злиттÑ
activity.title.prs_n=%d Запитів на злиттÑ
activity.title.prs_merged_by=%s злито %s
activity.title.prs_opened_by=%s запропоновано %s
-activity.merged_prs_label=Злито
+activity.merged_prs_label=Об'єднано
activity.opened_prs_label=Запропоновано
activity.active_issues_count_1=<strong>%d</strong> Ðктивна задача
activity.active_issues_count_n=<strong>%d</strong> Ðктивні задачі
activity.closed_issues_count_1=Закрита задача
activity.closed_issues_count_n=Закриті задачі
-activity.title.issues_1=%d Задач
+activity.title.issues_1=%d Задача
activity.title.issues_n=%d Задач
activity.title.issues_closed_from=%s закрито %s
activity.title.issues_created_by=%s Ñтворена(Ñ–) %s
@@ -1445,97 +2018,131 @@ activity.new_issues_count_n=Ðові Задачі
activity.new_issue_label=Відкриті
activity.title.unresolved_conv_1=%d Ðезавершене обговореннÑ
activity.title.unresolved_conv_n=%d Ðезавершених обговорень
-activity.unresolved_conv_desc=СпиÑок вÑÑ–Ñ… Ñтарих задач Ñ– Pull Request'ів з недавньої активніÑтю, але ще не закритих або прийнÑтих.
+activity.unresolved_conv_desc=Ці нещодавно змінені задачі Ñ– запити на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‰Ðµ не вирішені.
activity.unresolved_conv_label=Відкрити
activity.title.releases_1=%d Реліз
activity.title.releases_n=%d Релізів
activity.title.releases_published_by=%s опубліковано %s
activity.published_release_label=Опубліковано
-activity.no_git_activity=У цей період не було здійÑнено жодних дій.
-activity.git_stats_exclude_merges=Ðе враховуючи злиттÑ,
+activity.no_git_activity=За цей період не було жодної активноÑті комітів.
+activity.git_stats_exclude_merges=Ðе враховуючи об'єднаннÑ,
activity.git_stats_author_1=%d автор
activity.git_stats_author_n=%d автори
-activity.git_stats_pushed_1=відправлено
-activity.git_stats_pushed_n=відправлено
+activity.git_stats_pushed_1=відправив(ла)
+activity.git_stats_pushed_n=відправили
activity.git_stats_commit_1=%d коміт
-activity.git_stats_commit_n=%d коміти
+activity.git_stats_commit_n=%d комітів
activity.git_stats_push_to_branch=в %s та
activity.git_stats_push_to_all_branches=до вÑÑ–Ñ… гілок.
activity.git_stats_on_default_branch=Ðа %s,
activity.git_stats_file_1=%d файл
-activity.git_stats_file_n=%d файли
-activity.git_stats_files_changed_1=змінено
+activity.git_stats_file_n=%d файлів
+activity.git_stats_files_changed_1=змінив(ла)
activity.git_stats_files_changed_n=змінено
activity.git_stats_additions=і були
activity.git_stats_addition_1=%d добавка
-activity.git_stats_addition_n=%d добавки
+activity.git_stats_addition_n=%d додатків
activity.git_stats_and_deletions=та
activity.git_stats_deletion_1=%d видалений
activity.git_stats_deletion_n=%d видалені
+contributors.contribution_type.filter_label=Тип внеÑку:
contributors.contribution_type.commits=Коміти
+contributors.contribution_type.deletions=ВидаленнÑ
settings=ÐалаштуваннÑ
-settings.desc=У налаштуваннÑÑ… ви можете змінювати різні параметри цього репозиторіÑ
-settings.options=Репозиторій
+settings.desc=У налаштуваннÑÑ… ви можете керувати параметрами Ñховища
+settings.options=Сховище
+settings.public_access=Публічний доÑтуп
+settings.public_access.docs.everyone_read=Ð’ÑÑ– читають: вÑÑ– зареєÑтровані кориÑтувачі можуть читати розділ. Це також дозволÑÑ” кориÑтувачам Ñтворювати нові задачі/запити на злиттÑ.
+settings.public_access.docs.everyone_write=Ð’ÑÑ– пишуть: вÑÑ– зареєÑтровані кориÑтувачі можуть вноÑити зміни до розділу. Тільки розділ Wiki підтримує цей дозвіл.
settings.collaboration=Співавтори
settings.collaboration.admin=ÐдмініÑтратор
settings.collaboration.write=ЗапиÑ
-settings.collaboration.read=Читати
+settings.collaboration.read=ЧитаннÑ
settings.collaboration.owner=ВлаÑник
settings.collaboration.undefined=Ðе визначено
settings.hooks=Веб-хуки
settings.githooks=Git хуки
settings.basic_settings=Базові налаштуваннÑ
settings.mirror_settings=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð·ÐµÑ€ÐºÐ°Ð»Ð°
-settings.mirror_settings.mirrored_repository=Віддзеркалений репозиторій
+settings.mirror_settings.docs=Ðалаштуйте Ñховище на автоматичну Ñинхронізацію комітів, міток Ñ– гілок з іншим Ñховищем.
+settings.mirror_settings.docs.disabled_pull_mirror.instructions=Ðалаштуйте ваш проєкт на автоматичне перенеÑÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ–Ð², міток Ñ– гілок до іншого Ñховища. ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð·Ð¼Ñ–Ð½ з дзеркал було вимкнено адмініÑтратором вашого Ñайту.
+settings.mirror_settings.docs.disabled_push_mirror.instructions=Ðалаштуйте Ñвій проєкт на автоматичне Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ–Ð², міток Ñ– гілок з іншого Ñховища.
+settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning=Ðаразі це можна зробити лише в меню «Ðова міграціÑ». Ð”Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ñ— інформації звернітьÑÑ Ð´Ð¾:
+settings.mirror_settings.docs.no_new_mirrors=Ваше Ñховище віддзеркалює зміни до іншого Ñховища або з нього. Будь лаÑка, майте на увазі, що наразі ви не можете Ñтворювати нові дзеркала.
+settings.mirror_settings.docs.can_still_use=Хоча ви не можете змінювати наÑвні дзеркала чи Ñтворювати нові, ви вÑе одно можете викориÑтовувати чинне дзеркало.
+settings.mirror_settings.docs.doc_link_title=Як віддзеркалити Ñховища?
+settings.mirror_settings.docs.doc_link_pull_section=розділ документації "Ð—Ð»Ð¸Ñ‚Ñ‚Ñ Ð· віддаленого Ñховища".
+settings.mirror_settings.docs.pulling_remote_title=Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð· віддаленого Ñховища
+settings.mirror_settings.mirrored_repository=Віддзеркалене Ñховища
settings.mirror_settings.direction=ÐапрÑмок
settings.mirror_settings.direction.pull=Pull
settings.mirror_settings.direction.push=Push
settings.mirror_settings.last_update=ОÑтаннє оновленнÑ
-settings.mirror_settings.push_mirror.none=Ðе налаштовано дзеркало push
-settings.mirror_settings.push_mirror.remote_url=URL віддаленого Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð°Ñ€Ñ–Ñ git
-settings.mirror_settings.push_mirror.add=Додати Push дзеркало
+settings.mirror_settings.push_mirror.remote_url=URL віддаленого Ñховища Git
+settings.mirror_settings.push_mirror.edit_sync_time=Редагувати інтервал Ñинхронізації дзеркал
settings.sync_mirror=Синхронізувати зараз
settings.site=Веб-Ñайт
settings.update_settings=Оновити налаштуваннÑ
-settings.branches.update_default_branch=Оновити гілку за замовчуваннÑм
-settings.advanced_settings=Додаткові налаштуваннÑ
-settings.wiki_desc=Увімкнути репозиторії Вікі
-settings.use_internal_wiki=ВикориÑтовувати вбудовані Вікі
-settings.use_external_wiki=ВикориÑтовувати зовнішні Вікі
+settings.update_mirror_settings=Оновити параметри дзеркала
+settings.branches.switch_default_branch=Змінити типову гілку
+settings.branches.update_default_branch=Оновити типову гілку
+settings.branches.add_new_rule=Додати нове правило
+settings.advanced_settings=Розширені налаштуваннÑ
+settings.wiki_desc=Увімкнути Вікі Ñховища
+settings.use_internal_wiki=ВикориÑтовувати вбудовану Вікі
+settings.default_wiki_branch_name=Ðазва типової гілки Вікі
+settings.failed_to_change_default_wiki_branch=Ðе вдалоÑÑ Ð·Ð¼Ñ–Ð½Ð¸Ñ‚Ð¸ Ñтандартну гілку вікі.
+settings.use_external_wiki=ВикориÑтовувати зовнішню Вікі
settings.external_wiki_url=URL зовнішньої вікі
-settings.external_wiki_url_error=Ð—Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ URL-адреÑа wiki не Ñ” допуÑтимою URL-адреÑою.
-settings.external_wiki_url_desc=Відвідувачі будуть перенаправлені на URL-адреÑу, коли вони клацають по вкладці.
+settings.external_wiki_url_error=Ð—Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ URL-адреÑа Вікі Ñ” недійÑною.
+settings.external_wiki_url_desc=Відвідувачі перенаправлÑютьÑÑ Ð½Ð° URL-адреÑу зовнішньої вікі при натиÑканні вкладки.
settings.issues_desc=Увімкнути відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡ в репозиторію
settings.use_internal_issue_tracker=ВикориÑтовувати вбудовану ÑиÑтему відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡
settings.use_external_issue_tracker=ВикориÑтовувати зовнішню ÑиÑтему обліку задач
settings.external_tracker_url=URL зовнішньої ÑиÑтеми відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡
-settings.external_tracker_url_error=URL зовнішнього баг-трекера не Ñ” допуÑтимою URL-адреÑою.
-settings.external_tracker_url_desc=Відвідувачі перенаправлÑютьÑÑ Ð½Ð° зовнішню URL-адреÑу, коли натиÑкають вкладку 'Задачі'.
+settings.external_tracker_url_error=URL-адреÑа зовнішнього трекера задач Ñ” недійÑною.
+settings.external_tracker_url_desc=Відвідувачі перенаправлÑютьÑÑ Ð½Ð° URL-адреÑу зовнішнього трекера задач при натиÑканні на вкладку.
settings.tracker_url_format=Формат URL зовнішнього трекера задач
-settings.tracker_url_format_error=Ðеправильний формат URL-адреÑи зовнішнього баг-трекера.
-settings.tracker_issue_style=Формат номеру Ð´Ð»Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½ÑŒÐ¾Ñ— ÑиÑтеми обліку задач
+settings.tracker_url_format_error=Формат URL-адреÑи зовнішнього трекера проблем недійÑний.
settings.tracker_issue_style.numeric=Цифровий
-settings.tracker_issue_style.alphanumeric=Буквено-цифровий
+settings.tracker_issue_style.alphanumeric=Ðлфавітно-цифровий
+settings.tracker_issue_style.regexp=РегулÑрний вираз
+settings.tracker_issue_style.regexp_pattern=Шаблон регулÑрного виразу
+settings.tracker_issue_style.regexp_pattern_desc=Першу захоплену групу буде викориÑтано заміÑть <code>{index}</code>.
settings.tracker_url_format_desc=ВикориÑтовуйте шаблони <code>{user}</code>, <code>{repo}</code> та <code>{index}</code> Ð´Ð»Ñ Ñ–Ð¼ÐµÐ½Ñ– кориÑтувача, репозиторію та номеру задічі.
settings.enable_timetracker=Увімкнути відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡Ð°Ñу
-settings.allow_only_contributors_to_track_time=Враховувати тільки учаÑників розробки в підрахунку чаÑу
+settings.allow_only_contributors_to_track_time=Лише відÑтежувати Ñ‡Ð°Ñ Ñпівавторів
settings.pulls_desc=Увімкнути запити на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð² репозиторій
settings.pulls.ignore_whitespace=Ігнорувати пробіл у конфліктах
-settings.pulls.enable_autodetect_manual_merge=Увімкнути Ð°Ð²Ñ‚Ð¾Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ€ÑƒÑ‡Ð½Ð¾Ð³Ð¾ Ð·Ð»Ð¸Ñ‚Ñ‚Ñ (Примітка: у деÑких оÑобливий випадках можуть виникнуть помилки)
+settings.pulls.enable_autodetect_manual_merge=Увімкнути автоматичне Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ€ÑƒÑ‡Ð½Ð¾Ð³Ð¾ об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ (Примітка: у деÑких оÑобливих випадках можуть виникати помилкові оцінки)
settings.pulls.default_delete_branch_after_merge=ВидалÑти гілку запиту злиттÑ, коли його прийнÑто
+settings.releases_desc=Увімкнути релізи Ñховища
+settings.packages_desc=Увімкнути реєÑтр пакетів Ñховища
+settings.projects_desc=Увімкнути проєкти
+settings.projects_mode_desc=Режим проєктів (Ñкі типи проєктів показувати)
+settings.projects_mode_repo=Тільки проєкти Ñховища
+settings.projects_mode_owner=Тільки проєкти кориÑтувачів або організацій
+settings.projects_mode_all=Ð’ÑÑ– проєкти
+settings.actions_desc=Увімкнути дії Ñховища
settings.admin_settings=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð´Ð¼Ñ–Ð½Ñ–Ñтратора
-settings.admin_enable_health_check=Включити перевірки працездатноÑті репозиторію (git fsck)
+settings.admin_enable_health_check=Увімкнути перевірку Ñтану Ñховища (git fsck)
+settings.admin_code_indexer=ІндекÑатор коду
+settings.admin_stats_indexer=ІндекÑатор ÑтатиÑтики коду
+settings.admin_indexer_commit_sha=ОÑтанній індекÑований SHA
+settings.admin_indexer_unindexed=Ðе індекÑовано
+settings.reindex_button=Додати до черги на реіндекÑацію
+settings.reindex_requested=Запит на переіндекÑацію
settings.admin_enable_close_issues_via_commit_in_any_branch=Закрити задачу за допомогою коміта, зробленого не в головній гілці
settings.danger_zone=Ðебезпечна зона
-settings.new_owner_has_same_repo=Ðовий влаÑник вже має репозиторій з такою назвою. Будь лаÑка, виберіть інше ім'Ñ.
-settings.convert=Перетворити на звичайний репозиторій
-settings.convert_desc=Ви можете Ñконвертувати це дзеркало у звичайний репозиторій. Це не може бути ÑкаÑовано.
+settings.new_owner_has_same_repo=Ðовий влаÑник вже має Ñховище з такою назвою. Будь лаÑка, виберіть іншу назву.
+settings.convert=Перетворити на звичайне Ñховище
+settings.convert_desc=Ви можете перетворити це дзеркало на звичайне Ñховище. Це неможливо ÑкаÑувати.
settings.convert_notices_1=Ð¦Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð¿ÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€Ð¸Ñ‚ÑŒ дзеркало у звичайний репозиторій Ñ– не може бути ÑкаÑована.
settings.convert_confirm=Перетворити репозиторій
-settings.convert_succeed=Репозиторій уÑпішно перетворений в звичайний.
+settings.convert_succeed=Дзеркало було перетворено на звичайне Ñховище.
settings.convert_fork=Перетворити на звичайний репозиторій
settings.convert_fork_desc=Ви можете перетворити цей форк на звичайний репозиторій. Цю дію неможливо ÑкаÑувати.
settings.convert_fork_notices_1=Ð¦Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð¿ÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€Ð¸Ñ‚ÑŒ форк на звичайний репозиторій та не може бути ÑкаÑованою.
@@ -1545,89 +2152,90 @@ settings.transfer=Передати новому влаÑнику
settings.transfer.rejected=ПеренеÑÐµÐ½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–ÑŽ відхилено.
settings.transfer.success=ПеренеÑÐµÐ½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–ÑŽ виконано.
settings.transfer_abort=СкаÑувати перенеÑеннÑ
-settings.transfer_abort_invalid=Ви не можете ÑкаÑувати неіÑнуюче перенеÑÐµÐ½Ð½Ñ Ñховища.
-settings.transfer_desc=Передати репозиторій кориÑтувачеві або організації, де ви маєте права адмініÑтратора.
-settings.transfer_form_title=Введіть ім'Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ Ñк підтвердженнÑ:
-settings.transfer_in_progress=Ð’ даний Ñ‡Ð°Ñ Ð²Ñ–Ð´Ð±ÑƒÐ²Ð°Ñ”Ñ‚ÑŒÑÑ Ð¿ÐµÑ€ÐµÐ½ÐµÑеннÑ. Будь лаÑка, ÑкаÑуйте його, Ñкщо ви бажаєте перенеÑти цей репозиторій іншому кориÑтувачу.
-settings.transfer_notices_1=- Ви втратите доÑтуп до репозиторіÑ, Ñкщо ви переведете його окремому кориÑтувачеві.
-settings.transfer_notices_2=- Ви збережете доÑтуп, Ñкщо новим влаÑником Ñтане організаціÑ, влаÑником Ñкої ви Ñ”.
-settings.transfer_notices_3=- Якщо репозиторій Ñ” приватним Ñ– передаєтьÑÑ Ð¾ÐºÑ€ÐµÐ¼Ð¾Ð¼Ñƒ кориÑтувачеві, Ñ†Ñ Ð´Ñ–Ñ Ð³Ð°Ñ€Ð°Ð½Ñ‚ÑƒÑ”, що кориÑтувач має хоча б дозвіл на Ñ‡Ð¸Ñ‚Ð°Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð°Ñ€Ñ–ÑŽ (Ñ– при необхідноÑті змінює права дозволів).
+settings.transfer_abort_invalid=Ви не можете ÑкаÑувати перенеÑÐµÐ½Ð½Ñ Ð½ÐµÑ–Ñнуючого Ñховища.
+settings.transfer_abort_success=ПеренеÑÐµÐ½Ð½Ñ Ñховища до %s уÑпішно ÑкаÑовано.
+settings.transfer_desc=Передати це Ñховище кориÑтувачеві або організації, Ð´Ð»Ñ Ñкої ви маєте права адмініÑтратора.
+settings.transfer_form_title=Введіть назву Ñховища Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ:
+settings.transfer_in_progress=Ðаразі триває передача. Будь лаÑка, ÑкаÑуйте його, Ñкщо ви хочете передати це Ñховище іншому кориÑтувачеві.
+settings.transfer_notices_1=- Ви втратите доÑтуп до Ñховища, Ñкщо передаÑте його окремому кориÑтувачеві.
+settings.transfer_notices_2=- Ви збережете доÑтуп до Ñховища, Ñкщо передаÑте його організації, Ñкою ви (Ñпів)володієте.
+settings.transfer_notices_3=- Якщо Ñховище Ñ” приватним Ñ– передаєтьÑÑ Ð¾ÐºÑ€ÐµÐ¼Ð¾Ð¼Ñƒ кориÑтувачеві, Ñ†Ñ Ð´Ñ–Ñ Ð³Ð°Ñ€Ð°Ð½Ñ‚ÑƒÑ”, що кориÑтувач має принаймні права на Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ (Ñ– змінює ці права, Ñкщо необхідно).
settings.transfer_owner=Ðовий влаÑник
settings.transfer_perform=ЗдіÑнити перенеÑеннÑ
-settings.transfer_started=`Цей репозиторій чекає Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÐµÐ½ÐµÑÐµÐ½Ð½Ñ Ð²Ñ–Ð´ "%s"`
-settings.transfer_succeed=Репозиторій був перенеÑений.
+settings.transfer_started=Це Ñховище було позначено Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ñ– та очікує на Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ «%s»
+settings.transfer_succeed=Сховище перенеÑено.
settings.signing_settings=Параметри перевірки підпиÑу
-settings.trust_model=Модель довіри Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñу
-settings.trust_model.default=Модель довіри за замовчуваннÑм
-settings.trust_model.default.desc=ВикориÑтовувати модель довіри репозиторію за замовчуваннÑм Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ñайту.
+settings.trust_model=Модель довіри до підпиÑу
+settings.trust_model.default=Типова модель довіри
+settings.trust_model.default.desc=ВикориÑтовувати типову модель довіри до Ñховища Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ñайту.
settings.trust_model.collaborator=Співавтор
settings.trust_model.collaborator.long=Співавтор: підпиÑи довіри від Ñпівавторів
-settings.trust_model.collaborator.desc=ДопуÑтимі підпиÑи Ñпівавторів цього репозиторію буде позначано Ñк "довірені" - (Ñкщо вони відповідають комітеру чи ні). Ð’ іншому випадку дійÑні підпиÑи будуть позначені Ñк «ненадійні», Ñкщо Ð¿Ñ–Ð´Ð¿Ð¸Ñ Ñпівпадає з комітером Ñ– «невідповідні», Ñкщо ні.
-settings.trust_model.committer=Коммітер
-settings.trust_model.committer.long=Коммітер: ДовірÑти підпиÑам Ñкі відповідають комітерам (Так Ñк Ñ– на GitHub, Ñ– змуÑить підпиÑати коміти Gitea в ÑкоÑті коммітера)
-settings.trust_model.collaboratorcommitter=Співавтор+Коммітер
-settings.trust_model.collaboratorcommitter.long=Співавтор+Коммітер: ДовірÑти підпиÑам від Ñпівавторів, Ñкі відповідають комітеру
-settings.trust_model.collaboratorcommitter.desc=ДопуÑтимі підпиÑи Ñпівавторів цього репозиторію будуть позначатиÑÑ Ñк "довірені", Ñкщо вони відповідають комітеру. Ð’ іншому випадку дійÑні підпиÑи будуть позначені Ñк «ненадійні», Ñкщо Ð¿Ñ–Ð´Ð¿Ð¸Ñ Ñпівпадає з комітером Ñ– Ñк «невідповіді» в іншому випадку. Це змуÑить Gitea бути відміченим Ñк комітер піÑÐ»Ñ Ð¿Ñ–Ð´Ð¿Ð¸ÑÐ°Ð½Ð½Ñ Ñ„Ð°ÐºÑ‚Ð¸Ñ‡Ð½Ð¸Ð¼ комітером, позначеним Co-Authored-By: Ñ– Co-Committed-By: прикріпленим до комміту. Типовий ключ Gitea повинен відповідати кориÑтувачу в базі даних.
-settings.wiki_delete=Видалити вікі-дані
-settings.wiki_delete_desc=Будьте уважні! Як тільки ви видалите Вікі - шлÑху назад не буде.
-settings.wiki_delete_notices_1=- Це назавжди знищить Ñ– відключить wiki Ð´Ð»Ñ %s.
-settings.confirm_wiki_delete=Видалити Вікі-дані
-settings.wiki_deletion_success=Дані wiki були видалені.
+settings.trust_model.collaborator.desc=ДійÑні підпиÑи Ñпівавторів цього Ñховища будуть позначені Ñк «довірені» - (незалежно від того, чи збігаютьÑÑ Ð²Ð¾Ð½Ð¸ з підпиÑом комітера чи ні). Ð’ іншому випадку дійÑні підпиÑи будуть позначені Ñк «недійÑні», Ñкщо Ð¿Ñ–Ð´Ð¿Ð¸Ñ Ð·Ð±Ñ–Ð³Ð°Ñ”Ñ‚ÑŒÑÑ Ð· комітером Ñ– «невідповідні», Ñкщо ні.
+settings.trust_model.committer=Комітер
+settings.trust_model.committer.long=Комітер: ДовірÑти підпиÑам, Ñкі відповідають комітерам (це відповідає GitHub Ñ– змуÑить підпиÑані Gitea коміти мати Gitea в ÑкоÑті комітера)
+settings.trust_model.collaboratorcommitter=Співавтор+Комітер
+settings.trust_model.collaboratorcommitter.long=Співавтор+Комітер: ДовірÑти підпиÑам від Ñпівавторів, Ñкі відповідають комітеру
+settings.trust_model.collaboratorcommitter.desc=ДійÑні підпиÑи Ñпівавторів цього Ñховища будуть позначені Ñк «довірені», Ñкщо вони збігаютьÑÑ Ð· комітером. Ð’ іншому випадку, дійÑні підпиÑи будуть позначені Ñк «недійÑні», Ñкщо Ð¿Ñ–Ð´Ð¿Ð¸Ñ Ð·Ð±Ñ–Ð³Ð°Ñ”Ñ‚ÑŒÑÑ Ð· комітером, Ñ– «невідповідні» у протилежному випадку. Це призведе до того, що Gitea буде позначено комітером у підпиÑаних комітах, а Ñправжній комітер буде позначений Ñк Co-Author-By: та Co-Committed-By: у трейлері коміта. Типовий ключ Gitea має відповідати кориÑтувачеві у базі даних.
+settings.wiki_delete=Видалити дані Вікі
+settings.wiki_delete_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… Вікі Ñховища Ñ” оÑтаточним Ñ– не може бути ÑкаÑоване.
+settings.wiki_delete_notices_1=- Це назавжди видалить Ñ– вимкне вікі Ñховища Ð´Ð»Ñ %s.
+settings.confirm_wiki_delete=Видалити дані Вікі
+settings.wiki_deletion_success=Дані Вікі видалено.
settings.delete=Видалити цей репозиторій
-settings.delete_desc=Будьте уважні! Як тільки ви видалите репозиторій - шлÑху назад не буде.
-settings.delete_notices_1=- Цю операцію <strong>ÐЕ МОЖÐÐ</strong> відмінити.
-settings.delete_notices_2=- Ð¦Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð¾Ñтаточно видалить <strong>%s</strong> репозиторій, включаючи код, задачі, коментарі, вікі та Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñпівавторів.
+settings.delete_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñховища Ñ” оÑтаточним Ñ– не може бути ÑкаÑоване.
+settings.delete_notices_1=- Цю операцію <strong>ÐЕМОЖЛИВО</strong> ÑкаÑувати.
+settings.delete_notices_2=- Ð¦Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ð°Ð·Ð°Ð²Ð¶Ð´Ð¸ видалить Ñховище <strong>%s</strong>, включно з кодом, проблемами, коментарÑми, даними вікі та налаштуваннÑми Ñпівавторів.
settings.delete_notices_fork_1=- Ð’ÑÑ– форки Ñтануть незалежними репозиторіÑми піÑÐ»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ.
settings.deletion_success=Репозиторій уÑпішно видалено.
-settings.update_settings_success=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–ÑŽ було оновлено.
+settings.update_settings_success=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñховища оновлено.
+settings.update_settings_no_unit=Сховище повинно дозволÑти хоча б ÑкуÑÑŒ взаємодію.
settings.confirm_delete=Видалити репозиторій
settings.add_collaborator=Додати Ñпівавтора
settings.add_collaborator_success=Додано Ñпівавтора.
-settings.add_collaborator_inactive_user=Ðе можливо додати неактивного кориÑтувача ÑкоÑті Ñпівавтора.
+settings.add_collaborator_inactive_user=Ðеможливо додати неактивного кориÑтувача Ñк Ñпівавтора.
+settings.add_collaborator_owner=Ðеможливо додати влаÑника Ñк Ñпівавтора.
settings.add_collaborator_duplicate=Співавтора уже додано до цього репозиторію.
settings.delete_collaborator=Видалити
settings.collaborator_deletion=Видалити Ñпівавтора
-settings.collaborator_deletion_desc=Цей кориÑтувач більше не матиме доÑтупу Ð´Ð»Ñ Ñпільної роботи в цьому репозиторії піÑÐ»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ. Ви хочете продовжити?
-settings.remove_collaborator_success=Співавтор видалений.
+settings.collaborator_deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñпівавтора призведе до Ð²Ñ–Ð´ÐºÐ»Ð¸ÐºÐ°Ð½Ð½Ñ Ð¹Ð¾Ð³Ð¾ доÑтупу до цього Ñховища. Продовжити?
+settings.remove_collaborator_success=Співавтора видалено.
settings.org_not_allowed_to_be_collaborator=Організації не можуть бути додані Ñк Ñпівавтори.
settings.change_team_access_not_allowed=Зміна доÑтупу команди до репозитарію обмежена влаÑником організації
-settings.team_not_in_organization=Команда та репозитарій мають привÑзки до різних організацій
+settings.team_not_in_organization=Команда не належить до тієї ж організації, що й Ñховище
settings.teams=Команди
-settings.add_team=Додати Команду
-settings.add_team_duplicate=Команда вже має привÑзку до репозитарію
-settings.add_team_success=Команда отримала доÑтуп до репозиторію.
-settings.change_team_permission_tip=Дозволи команди вÑтановлюютьÑÑ Ð½Ð° Ñторінці налаштувань команди та не можуть бути заданими Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ з репозиторіїв окремо
+settings.add_team=Додати команду
+settings.add_team_duplicate=Команда вже має Ñховище
+settings.add_team_success=Команда тепер має доÑтуп до Ñховища.
+settings.change_team_permission_tip=Дозвіл команди вÑтановлюєтьÑÑ Ð½Ð° Ñторінці налаштувань команди Ñ– не може бути змінений Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ Ñховища
settings.delete_team_tip=Ð¦Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° має доÑтуп до вÑÑ–Ñ… репозиторіїв та не може бути видалена
-settings.remove_team_success=ДоÑтуп команди до репозиторію видалений.
+settings.remove_team_success=ДоÑтуп команди до Ñховища видалено.
settings.add_webhook=Додати веб-хук
-settings.add_webhook.invalid_channel_name=Ðазва каналу Webhook не може бути порожньою Ñ– не може міÑтити лише Ñимвол #.
-settings.hooks_desc=Веб-хуки автоматично робить HTTP POST-запити на Ñервер, коли відбуваютьÑÑ Ð¿ÐµÐ²Ð½Ñ– події Gitea. ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ в <a target="_blank" rel="noopener" href="%s"> інÑтрукції по викориÑтанню web-хуків </a>.
+settings.add_webhook.invalid_channel_name=Ðазва каналу веб-хука не може бути порожньою Ñ– міÑтити лише Ñимвол #.
+settings.hooks_desc=Веб-хуки автоматично роблÑть HTTP POST запити до Ñервера, коли відбуваютьÑÑ Ð¿ÐµÐ²Ð½Ñ– події Gitea. ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ в <a target="_blank" rel="noopener noreferrer" href="%s">інÑтрукції по викориÑтанню веб-хуків</a>.
settings.webhook_deletion=Видалити веб-хук
-settings.webhook_deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ веб-хука призведе до Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²Ñієї пов'Ñзаної з ним інформації, включаючи Ñ–Ñторію. Бажаєте продовжити?
-settings.webhook_deletion_success=Webhook видалено.
-settings.webhook.test_delivery=Перевірити доÑтавку
-settings.webhook.test_delivery_desc=Перевірте цей веб-хук з підробленою подією.
+settings.webhook_deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²ÐµÐ±-хука видалÑÑ” його Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð° Ñ–Ñторію доÑтавки. Продовжити?
+settings.webhook_deletion_success=Веб-хук видалено.
settings.webhook.request=Запит
settings.webhook.response=Відповідь
settings.webhook.headers=Заголовки
settings.webhook.payload=ЗміÑÑ‚
settings.webhook.body=Тіло
-settings.githook_edit_desc=Якщо хук неактивний, буде предÑтавлено зразок зміÑту. Порожнє Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñƒ цьому полі призведе до Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ñ…ÑƒÐºÑƒ.
-settings.githook_name=Ім'Ñ Ñ…ÑƒÐºÑƒ
+settings.webhook.replay.description=Повторити цей веб-хук.
+settings.githook_edit_desc=Якщо хук неактивний, буде показано зразок вміÑту. Якщо залишити вміÑÑ‚ порожнім, хук буде вимкнено.
+settings.githook_name=Ðазва хуку
settings.githook_content=ЗміÑÑ‚ хука
settings.update_githook=Оновити хук
-settings.add_webhook_desc=Gitea буде відправлÑти <code>POST</code> запити на вказану URL адреÑу, з інформацією про події, що відбуваютьÑÑ. Подробиці на Ñторінці <a target="_blank" rel="noopener" href="%s"> інÑтрукції по викориÑтанню web-хуків </a>.
+settings.add_webhook_desc=Gitea надішле запити <code>POST</code> із зазначеним типом зміÑту на цільову URL-адреÑу. ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ в <a target="_blank" rel="noopener noreferrer" href="%s">інÑтрукції по викориÑтанню веб-хуків</a>.
settings.payload_url=Цільова URL-адреÑа
settings.http_method=Метод HTTP
settings.content_type=Тип зміÑту
settings.secret=Секрет
settings.slack_username=Ім'Ñ ÐºÑ€Ð¸Ñтувача
-settings.slack_icon_url=URL іконки
+settings.slack_icon_url=URL піктограми
settings.slack_color=Колір
settings.discord_username=Ім'Ñ ÐºÑ€Ð¸Ñтувача
-settings.discord_icon_url=URL іконки
+settings.discord_icon_url=URL піктограми
settings.event_desc=Тригер:
-settings.event_push_only=Push події
settings.event_send_everything=Ð’ÑÑ– події
settings.event_choose=ВлаÑні події…
settings.event_header_repository=Події репозиторію
@@ -1636,41 +2244,44 @@ settings.event_create_desc=Гілку або тег Ñтворено.
settings.event_delete=Видалити
settings.event_delete_desc=Гілку або мітку було видалено.
settings.event_fork=Форк
-settings.event_fork_desc=Репозиторій було форкнуто.
settings.event_wiki=Вікі
+settings.event_statuses=СтатуÑи
+settings.event_statuses_desc=Ð¡Ñ‚Ð°Ñ‚ÑƒÑ ÐºÐ¾Ð¼Ñ–Ñ‚Ñƒ оновлено з API.
settings.event_release=Реліз
-settings.event_release_desc=Реліз опублікований, оновлений або видалений з репозиторіÑ.
+settings.event_release_desc=Реліз опубліковано, оновлено або видалено зі Ñховища.
settings.event_push=Push
-settings.event_push_desc=Git push до репозиторію.
settings.event_repository=Репозиторій
settings.event_repository_desc=Репозиторій Ñтворений або видалено.
settings.event_header_issue=Події задачі
settings.event_issues=Задачі
-settings.event_issues_desc=Задача відкрита, закрита, повторно відкрита або відредагована.
-settings.event_issue_assign=Задача прив'Ñзана
+settings.event_issues_desc=Задачу відкрито, закрито, повторно відкрито, відредаговано або видалено.
+settings.event_issue_assign=Задачу призначено
settings.event_issue_assign_desc=Задачу призначено або ÑкаÑовано.
-settings.event_issue_label=Задача з міткою
settings.event_issue_label_desc=Мітки задачі оновлено або видалено.
-settings.event_issue_milestone=Задача з етапом
-settings.event_issue_milestone_desc=Задача призначена на етап або видалена з етапу.
settings.event_issue_comment=Коментар задачі
settings.event_issue_comment_desc=Коментар задачі Ñтворено, видалено чи відредаговано.
settings.event_header_pull_request=Події запиту злиттÑ
-settings.event_pull_request=Запити до злиттÑ
-settings.event_pull_request_desc=Запит до Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¾, закрито, перевідкрито або відредаговано.
+settings.event_pull_request=Запити на злиттÑ
+settings.event_pull_request_desc=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¾, закрито, повторно відкрито, відредаговано або видалено.
settings.event_pull_request_assign=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¾
-settings.event_pull_request_assign_desc=Запит про Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¾ або ÑкаÑовано.
+settings.event_pull_request_assign_desc=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¾ або ÑкаÑовано.
settings.event_pull_request_label=Запиту на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð° мітка
settings.event_pull_request_label_desc=Мітка запиту на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð° або очищена.
-settings.event_pull_request_milestone=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ð¹ на етап
-settings.event_pull_request_milestone_desc=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ð¹ на етап або видалений з етапу.
-settings.event_pull_request_comment=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¾ÐºÐ¾Ð¼ÐµÐ½Ñ‚Ð¾Ð²Ð°Ð½Ð¸Ð¹
+settings.event_pull_request_milestone=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð¾Ð´Ð°Ð½Ð¾ до етапу
+settings.event_pull_request_milestone_desc=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð¾Ð´Ð°Ð½Ð¾ до етапу або видалено з етапу.
+settings.event_pull_request_comment=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¾ÐºÐ¾Ð¼ÐµÐ½Ñ‚Ð¾Ð²Ð°Ð½Ð¾
settings.event_pull_request_comment_desc=Коментар запиту на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñтворено, відредаговано чи видалено.
settings.event_pull_request_review=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ€ÐµÑ†ÐµÐ½Ð·Ð¾Ð²Ð°Ð½Ð¾
-settings.event_pull_request_review_desc=Коментар запиту до Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð¸Ð¹, відхилений або рецензований.
+settings.event_pull_request_review_desc=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð¾, відхилено або прокоментовано.
settings.event_pull_request_sync=Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ ÑинхронізуєтьÑÑ
settings.event_pull_request_sync_desc=Запит до Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñинхронізовано.
+settings.event_header_workflow=Події робочого процеÑу
+settings.event_workflow_run=Запущений робочий процеÑ
+settings.event_workflow_run_desc=Запущений робочий Ð¿Ñ€Ð¾Ñ†ÐµÑ Gitea в черзі, в очікуванні, в процеÑÑ– Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð°Ð±Ð¾ завершений.
+settings.event_workflow_job=Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‡Ð¾Ð³Ð¾ процеÑу
+settings.event_package=Пакет
settings.branch_filter=Фільтр гілок
+settings.authorization_header=Заголовок авторизації
settings.active=Ðктивний
settings.active_helper=Інформацію про викликані події буде надіÑлано за цією веб-хук URL-адреÑою.
settings.add_hook_success=Веб-хук було додано.
@@ -1682,71 +2293,109 @@ settings.hook_type=Тип хука
settings.slack_token=Токен
settings.slack_domain=Домен
settings.slack_channel=Канал
-settings.deploy_keys=Ключі Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ
-settings.add_deploy_key=Додати ключ Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ
-settings.deploy_key_desc=Ключі Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупні тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ. Це не те ж Ñаме що Ñ– SSH-ключі аккаунта.
+settings.web_hook_name_gitea=Gitea
+settings.web_hook_name_gogs=Gogs
+settings.web_hook_name_slack=Slack
+settings.web_hook_name_discord=Discord
+settings.web_hook_name_dingtalk=DingTalk
+settings.web_hook_name_telegram=Telegram
+settings.web_hook_name_matrix=Matrix
+settings.web_hook_name_msteams=Microsoft Teams
+settings.web_hook_name_feishu_or_larksuite=Feishu / Lark Suite
+settings.web_hook_name_feishu=Feishu
+settings.web_hook_name_larksuite=Lark Suite
+settings.web_hook_name_wechatwork=WeCom (Wechat Work)
+settings.web_hook_name_packagist=Packagist
+settings.packagist_username=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача Packagist
+settings.packagist_api_token=Токен API
+settings.deploy_keys=Ключі Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ
+settings.add_deploy_key=Додати ключ Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ
+settings.deploy_key_desc=Ключі Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð¼Ð°ÑŽÑ‚ÑŒ доÑтуп до Ñховища лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ.
settings.is_writable=Увімкнути доÑтуп Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñу
-settings.is_writable_info=Чи може цей ключ бути викориÑтаний Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ <strong>push</strong> в репозиторій? Ключі Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð·Ð°Ð²Ð¶Ð´Ð¸ мають доÑтуп на pull.
-settings.no_deploy_keys=Ви не додавали ключі розгортаннÑ.
+settings.no_deploy_keys=Ключів Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ñ‰Ðµ немає.
settings.title=Заголовок
settings.deploy_key_content=ЗміÑÑ‚
-settings.key_been_used=ЗміÑÑ‚ ключа Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð²Ð¶Ðµ викориÑтовуєтьÑÑ.
-settings.key_name_used=Ключ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð· таким заголовком вже Ñ–Ñнує.
-settings.deploy_key_deletion=Видалити ключ Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ
-settings.deploy_key_deletion_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ ÐºÐ»ÑŽÑ‡Ð° розгортки унеможливить доÑтуп до Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ Ð· його допомогою. Ви впевнені?
+settings.key_been_used=Ключ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð· ідентичним вміÑтом вже викориÑтовуєтьÑÑ.
+settings.key_name_used=Ключ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð· такою ж назвою вже Ñ–Ñнує.
+settings.deploy_key_deletion=Видалити ключ розгортаннÑ
+settings.deploy_key_deletion_desc=Ð’Ð¸Ð»ÑƒÑ‡ÐµÐ½Ð½Ñ ÐºÐ»ÑŽÑ‡Ð° Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ð·Ð²ÐµÐ´Ðµ до Ð²Ñ–Ð´ÐºÐ»Ð¸ÐºÐ°Ð½Ð½Ñ Ð¹Ð¾Ð³Ð¾ доÑтупу до цього Ñховища. Продовжити?
settings.deploy_key_deletion_success=Ключі Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¾ видалено.
settings.branches=Гілки
settings.protected_branch=ЗахиÑÑ‚ гілки
-settings.protected_branch_can_push=Дозволити push?
-settings.protected_branch_can_push_yes=Ви можете виконувати push
-settings.protect_this_branch=ЗахиÑтити цю гілку
-settings.protect_this_branch_desc=Запобігає видаленню гілки та обмежує Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð² ній push та злиттÑ.
+settings.protected_branch.save_rule=Зберегти правило
+settings.protected_branch.delete_rule=Видалити правило
+settings.branch_protection=ЗахиÑÑ‚ гілки '<b>%s</b>'
+settings.protect_this_branch=Увімкнути захиÑÑ‚ гілок
settings.protect_disable_push=Заборонити Push
settings.protect_disable_push_desc=Ð”Ð»Ñ Ñ†Ñ–Ñ”Ñ— гілки буде заборонено Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ push.
settings.protect_enable_push=Дозволити Push
settings.protect_enable_push_desc=Будь-хто із правом запиÑу зможе виконувати push Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— гілки (за виключеннÑм force push).
+settings.protect_enable_merge=Увімкнути об’єднаннÑ
settings.protect_check_status_contexts=Увімкнути перевірку Ñтану
+settings.protect_status_check_patterns=Шаблони перевірки Ñтану:
+settings.protect_status_check_patterns_desc=Введіть шаблони, щоб вказати, Ñкі перевірки Ñтану повинні пройти гілки, перш ніж Ñ—Ñ… буде об'єднано у гілку, що відповідає цьому правилу. Кожен Ñ€Ñдок визначає шаблон. Шаблони не можуть бути порожніми.
settings.protect_check_status_contexts_list=Перевірки ÑтатуÑу знайдено Ð´Ð»Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð°Ñ€Ñ–ÑŽ за минулий тиждень
+settings.protect_status_check_matched=Збіг
+settings.protect_invalid_status_check_pattern=ÐедійÑний шаблон перевірки Ñтану: "%s".
+settings.protect_no_valid_status_check_patterns=Ðемає дійÑних шаблонів перевірки Ñтану.
settings.protect_required_approvals=Ðеобхідно ÑхваленнÑ:
settings.dismiss_stale_approvals=Відхилити заÑтарілі погодженнÑ
settings.dismiss_stale_approvals_desc=Коли нові коміти що змінюють вміÑÑ‚ пулл-запиту відправлÑютьÑÑ Ð² гілку, Ñтарі Ð¿Ð¾Ð³Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ Ð±ÑƒÐ´ÑƒÑ‚ÑŒ відхилені.
+settings.ignore_stale_approvals=Ігнорувати заÑтарілі затвердженнÑ
settings.require_signed_commits=Потрібно підпиÑані коміти
settings.require_signed_commits_desc=ВідхилÑти push до цієї гілки, Ñкщо вони не підпиÑані або Ð¿Ñ–Ð´Ð¿Ð¸Ñ Ð½ÐµÐ¼Ð¾Ð¶Ð»Ð¸Ð²Ð¾ перевірити.
+settings.protect_branch_name_pattern=Шаблон назви захищених гілок
+settings.protect_branch_name_pattern_desc=Шаблони назв захищених гілок. ПереглÑньте <a href="https://github.com/gobwas/glob">документацію</a> щодо ÑинтакÑиÑу шаблону. Приклади: main, release/**
+settings.protect_patterns=Шаблони
+settings.protect_protected_file_patterns=Шаблони захищених файлів (розділені крапками з комою ';'):
+settings.protect_unprotected_file_patterns=Шаблони незахищених файлів (розділені крапкою з комою ';'):
settings.add_protected_branch=Увімкнути захиÑÑ‚
settings.delete_protected_branch=Вимкнути захиÑÑ‚
+settings.update_protect_branch_success=Оновлено захиÑÑ‚ гілок Ð´Ð»Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð° "%s".
+settings.remove_protected_branch_success=Видалено захиÑÑ‚ гілок Ð´Ð»Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»Ð° "%s".
+settings.remove_protected_branch_failed=Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ правило захиÑту гілки "%s.
settings.protected_branch_deletion_desc=Будь-Ñкий кориÑтувач з дозволами на Ð·Ð°Ð¿Ð¸Ñ Ð·Ð¼Ð¾Ð¶Ðµ виконувати push в цю гілку. Ви впевнені?
-settings.block_rejected_reviews=Блокувати Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¸ відкидаючих рецензіÑÑ…
-settings.block_rejected_reviews_desc=Ð—Ð»Ð¸Ñ‚Ñ‚Ñ Ð±ÑƒÐ´Ðµ недоÑтупним, Ñкщо Ñ” запит змін від офіційних рецензентів, навіть за наÑвноÑті доÑтатньої кількоÑті Ñхвалень.
-settings.block_on_official_review_requests=Блокувати Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¸ запиті на офіціальний розглÑд
+settings.block_rejected_reviews=Блокувати об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñкщо рецензії відхилено
+settings.block_rejected_reviews_desc=Об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ неможливим, Ñкщо офіційні рецензенти вимагають внеÑÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½, навіть Ñкщо Ñ” доÑÑ‚Ð°Ñ‚Ð½Ñ ÐºÑ–Ð»ÑŒÐºÑ–Ñть Ñхвалень.
+settings.block_on_official_review_requests=Блокувати об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð·Ð° офіційними запитами на рецензуваннÑ
settings.block_on_official_review_requests_desc=ÐžÐ±â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð½ÐµÐ¼Ð¾Ð¶Ð»Ð¸Ð²Ðµ, коли воно має офіційні запити на розглÑд, навіть Ñкщо доÑтатньо Ñхвалень.
-settings.block_outdated_branch=Блокувати злиттÑ, Ñкщо запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð°Ñтарів
-settings.block_outdated_branch_desc=Ð—Ð»Ð¸Ñ‚Ñ‚Ñ Ð±ÑƒÐ´Ðµ неможливим, коли головна гілка позаду оÑновної.
-settings.default_branch_desc=Головна гілка Ñ” 'базовою' Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ репозиторіÑ, на Ñку за замовчуваннÑм ÑпрÑмовані вÑÑ– запити на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ– Ñка Ñ” обличчÑм вашого репозиторіÑ. Перше, що побачить відвідувач - це зміÑÑ‚ головної гілки. Виберіть Ñ—Ñ— з уже Ñ–Ñнуючих:
+settings.block_outdated_branch=Блокувати об'єднаннÑ, Ñкщо запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð°Ñтарів
+settings.block_outdated_branch_desc=Об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ неможливим, Ñкщо головна гілка позаду оÑновної.
+settings.block_admin_merge_override=ÐдмініÑтратори повинні дотримуватиÑÑ Ð¿Ñ€Ð°Ð²Ð¸Ð» захиÑту гілки
+settings.block_admin_merge_override_desc=ÐдмініÑтратори повинні дотримуватиÑÑ Ð¿Ñ€Ð°Ð²Ð¸Ð» захиÑту гілки Ñ– не можуть Ñ—Ñ… обійти.
+settings.default_branch_desc=Обрати типову гілку Ñховища Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð² на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ– комітів:
+settings.merge_style_desc=Стилі об'єднаннÑ
+settings.default_merge_style_desc=Типовий Ñтиль об'єднаннÑ
settings.choose_branch=Оберіть гілку…
settings.no_protected_branch=Ðемає захищених гілок.
settings.edit_protected_branch=Редагувати
+settings.protected_branch_required_rule_name=Ðеобхідна назва правила
+settings.protected_branch_duplicate_rule_name=Ðазва правила вже Ñ–Ñнує
settings.protected_branch_required_approvals_min=ЧиÑло необхідних Ñхвалень не може бути від'ємним.
settings.tags=Мітки
settings.tags.protection=ЗахиÑÑ‚ мітки
-settings.tags.protection.pattern=Шаблон тега
+settings.tags.protection.pattern=Шаблон мітки
settings.tags.protection.allowed=Дозволено
settings.tags.protection.allowed.users=Дозволені кориÑтувачі
settings.tags.protection.allowed.teams=Дозволені команди
settings.tags.protection.allowed.noone=Ðіхто
-settings.tags.protection.create=ЗахиÑтна мітка
+settings.tags.protection.create=ЗахиÑтити мітку
settings.tags.protection.none=Там не немає захищених міток.
+settings.tags.protection.pattern.description=Ви можете викориÑтовувати єдине ім’Ñ, глобальний шаблон або регулÑрний вираз, щоб відповідати кільком міткам. Докладніше читайте в <a target="_blank" rel="noopener" href="https://docs.gitea.com/usage/protected-tags">поÑібнику із захищених міток</a>.
settings.bot_token=Токен Ð´Ð»Ñ Ð±Ð¾Ñ‚Ð°
-settings.chat_id=Чат ID
+settings.chat_id=ID чату
settings.matrix.homeserver_url=URL домашньої Ñторінки
-settings.matrix.room_id=Ðомер кімнати
+settings.matrix.room_id=ID кімнати
settings.matrix.message_type=Тип повідомленнÑ
-settings.archive.button=Ðрхівний репозиторій
+settings.visibility.public.button=Зробити публічним
settings.archive.header=Відправити репозиторій в архів
-settings.archive.success=Репозиторію уÑпішно приÑвоєно ÑÑ‚Ð°Ñ‚ÑƒÑ Ð°Ñ€Ñ…Ñ–Ð²Ð½Ð¾Ð³Ð¾.
-settings.archive.error=СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при Ñпробі архівувати репозиторій. Докладнішу інформацію див. у журналі.
-settings.archive.error_ismirror=Ðеможливо архівувати дзеркальний репозиротрій.
+settings.archive.success=Сховище уÑпішно заархівовано.
+settings.archive.error=СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при Ñпробі архівувати репозиторій. Докладнішу інформацію дивітьÑÑ Ñƒ журналі.
+settings.archive.error_ismirror=Ðеможливо архівувати дзеркальне Ñховище.
settings.archive.branchsettings_unavailable=Параметри гілки не доÑтупні, Ñкщо репозиторій архівний.
settings.archive.tagsettings_unavailable=Параметри міток недоÑтупні, Ñкщо репозиторій архівний.
+settings.unarchive.header=Розархівувати це Ñховище
+settings.unarchive.success=Сховище уÑпішно розархівовано.
settings.update_avatar_success=Ðватар репозиторію оновлений.
settings.lfs=LFS
settings.lfs_filelist=Файли LFS, Ñкі зберігаютьÑÑ Ð² цьому репозиторії
@@ -1758,9 +2407,9 @@ settings.lfs_delete=Видалити файл LFS з OID %s
settings.lfs_delete_warning=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ LFS може Ñпричинити помилки "Об'єкт не Ñ–Ñнує" під Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸. Ви впевнені?
settings.lfs_findpointerfiles=Знайти файли-поÑиланнÑ
settings.lfs_locks=БлокуваннÑ
-settings.lfs_invalid_locking_path=ÐеприпуÑтимий шлÑÑ…: %s
+settings.lfs_invalid_locking_path=ÐедійÑний шлÑÑ…: %s
settings.lfs_invalid_lock_directory=Ðе можливо заблокувати каталог: %s
-settings.lfs_lock_already_exists=Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¶Ðµ викориÑтовуєтьÑÑ: %s
+settings.lfs_lock_already_exists=Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¶Ðµ Ñ–Ñнує: %s
settings.lfs_lock=Блокувати
settings.lfs_lock_path=ШлÑÑ… до файлу Ð´Ð»Ñ Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ...
settings.lfs_locks_no_locks=ВідÑутнє блокуваннÑ
@@ -1786,7 +2435,6 @@ diff.commit=коміт
diff.git-notes=Примітки
diff.data_not_available=Ð Ñ–Ð·Ð½Ð¸Ñ†Ñ Ð½ÐµÐ´Ð¾Ñтупна
diff.options_button=Параметри порівнÑннÑ
-diff.show_diff_stats=Показати ÑтатиÑтику
diff.download_patch=Завантажити патч
diff.download_diff=Завантажити файл різниці
diff.show_split_view=Розділений переглÑд
@@ -1801,7 +2449,7 @@ diff.stats_desc_file=%d змін: %d доповнень та %d видалень
diff.bin=BIN
diff.bin_not_shown=Бінарний файл не відображаєтьÑÑ.
diff.view_file=ПереглÑнути файл
-diff.file_before=Перед
+diff.file_before=До
diff.file_after=ПіÑлÑ
diff.file_image_width=Ширина
diff.file_image_height=ВиÑота
@@ -1813,6 +2461,7 @@ diff.show_more=Показати більше
diff.load=Завантажити різницю
diff.generated=згенерований
diff.vendored=Ñторонній
+diff.comment.add_line_comment=Додати проÑтий коментар
diff.comment.placeholder=Залишити коментар
diff.comment.add_single_comment=Додати проÑтий коментар
diff.comment.add_review_comment=Додати коментар
@@ -1820,81 +2469,130 @@ diff.comment.start_review=Розпочати рецензію
diff.comment.reply=Відповідь
diff.review=РецензіÑ
diff.review.header=ÐадіÑлати рецензію
-diff.review.placeholder=Рецензійований коментарій
+diff.review.placeholder=Ð ÐµÑ†ÐµÐ½Ð·Ñ–Ñ ÐºÐ¾Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ñ
diff.review.comment=Коментар
diff.review.approve=Затвердити
diff.review.reject=Запит змін
diff.committed_by=зафікÑовано
diff.protected=Захищений
-diff.image.side_by_side=Пліч-о-пліч
-diff.image.swipe=Свайп
-diff.image.overlay=Оверлей
+diff.image.side_by_side=Поруч
+diff.image.swipe=ПровеÑти пальцем
+diff.image.overlay=ÐаклаÑти
+diff.has_escaped=Цей Ñ€Ñдок міÑтить приховані Ñимволи Юнікоду
+diff.show_file_tree=Показати дерево файлів
+diff.hide_file_tree=Сховати дерево файлів
+diff.submodule_added=Підмодуль %[1]s додано в %[2]s
+diff.submodule_deleted=Підмодуль %[1]s видалено з %[2]s
+diff.submodule_updated=Підмодуль %[1]s оновлено: %[2]s
releases.desc=ВідÑлідковувати верÑÑ–Ñ— проєкту Ñ– завантаженнÑ.
release.releases=Релізи
release.detail=Деталі релізу
-release.tags=Теги
+release.tags=Мітки
release.new_release=Ðовий реліз
release.draft=Чернетка
release.prerelease=Пре-реліз
release.stable=Стабільний
+release.latest=ОÑтанні
release.compare=ПорівнÑти
release.edit=редагувати
release.ahead.commits=<strong>%d</strong> коміт(ів)
release.ahead.target=до %s з моменту цього випуÑку
release.source_code=Код
-release.new_subheader=ÐŸÑƒÐ±Ð»Ñ–ÐºÐ°Ñ†Ñ–Ñ Ñ€ÐµÐ»Ñ–Ð·Ñ–Ð² допоможе вам організувати верÑÑ–ÑŽ проєкту.
-release.edit_subheader=ÐŸÑƒÐ±Ð»Ñ–ÐºÐ°Ñ†Ñ–Ñ Ñ€ÐµÐ»Ñ–Ð·Ñ–Ð² допоможе вам організувати верÑÑ–ÑŽ проєкту.
-release.tag_name=Ðазва тегу
+release.new_subheader=Релізи впорÑдковують верÑÑ–Ñ— проєкту.
+release.edit_subheader=Релізи впорÑдковують верÑÑ–Ñ— проєкту.
+release.tag_name=Ðазва мітки
release.target=Ціль
-release.tag_helper=Виберіть Ñ–Ñнуючий тег або Ñтворіть новий.
+release.tag_helper=Вибрати Ñ–Ñнуючу мітку або Ñтворити нову.
+release.title=Ðазва релізу
+release.title_empty=Заголовок не може бути порожнім.
+release.message=Опишіть цей реліз
release.prerelease_desc=Позначити Ñк пре-реліз
-release.prerelease_helper=Позначте цей випуÑк непридатним Ð´Ð»Ñ ÐŸÐ ÐžÐ” викориÑтаннÑ.
+release.prerelease_helper=Позначите випуÑк Ñк непридатний Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ð¾Ð³Ð¾ викориÑтаннÑ.
release.cancel=Відмінити
release.publish=Опублікувати реліз
release.save_draft=Зберегти чернетку
release.edit_release=Оновити реліз
release.delete_release=Видалити реліз
-release.delete_tag=Видалити тег
+release.delete_tag=Видалити мітку
release.deletion=Видалити реліз
-release.deletion_success=Реліз, було видалено.
+release.deletion_success=Реліз видалено.
release.deletion_tag_desc=Буде видалено цей тег із репозиторію. ВміÑÑ‚ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ Ñ‚Ð° Ñ–ÑÑ‚Ð¾Ñ€Ñ–Ñ Ð·Ð°Ð»Ð¸ÑˆÐ°Ñ‚ÑŒÑÑ Ð½ÐµÐ·Ð¼Ñ–Ð½Ð½Ð¸Ð¼Ð¸. Продовжити?
release.deletion_tag_success=Мітка видалена.
-release.tag_name_already_exist=Реліз з цим ім'Ñм мітки вже Ñ–Ñнує.
-release.tag_name_invalid=ÐеприпуÑтиме ім'Ñ Ñ‚ÐµÐ³Ð°.
-release.tag_name_protected=Ім'Ñ Ñ‚ÐµÐ³Ð° захищене.
-release.tag_already_exist=Цей тег вже викориÑтовуєтьÑÑ.
-release.downloads=Завантажити
+release.tag_name_already_exist=Реліз з такою ж міткою вже Ñ–Ñнує.
+release.tag_name_invalid=Ðазва мітки недійÑна.
+release.tag_name_protected=Ðазва мітки захищена.
+release.tag_already_exist=Ðазва мітки вже Ñ–Ñнує.
+release.downloads=ЗавантаженнÑ
release.download_count=ЗавантаженнÑ: %s
-release.add_tag_msg=ВикориÑтовуйте заголовок Ñ– зміÑÑ‚ релізу Ñк Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñк тег повідомленнÑ.
+release.add_tag_msg=ВикориÑтовуйте заголовок Ñ– зміÑÑ‚ релізу Ñк Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¼Ñ–Ñ‚ÐºÐ¸.
release.add_tag=Створити тільки мітку
+release.releases_for=Релізи Ð´Ð»Ñ %s
+release.tags_for=Мітки Ð´Ð»Ñ %s
-branch.name=Ім'Ñ Ð³Ñ–Ð»ÐºÐ¸
+branch.name=Ðазва гілки
+branch.already_exists=Гілка з назвою "%s" вже Ñ–Ñнує.
branch.delete_head=Видалити
+branch.delete=`Видалити гілку "%s"`
branch.delete_html=Видалити гілку
+branch.delete_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð³Ñ–Ð»ÐºÐ¸ Ñ” незворотним. Хоча видалена гілка може продовжувати Ñ–Ñнувати ще деÑкий Ñ‡Ð°Ñ Ð´Ð¾ того, Ñк Ñ—Ñ— буде видалено оÑтаточно, у більшоÑті випадків це ÐЕМОЖЛИВО ÑкаÑувати. Продовжити?
+branch.deletion_success=Гілку "%s" видалено.
+branch.deletion_failed=Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ гілку "%s".
+branch.delete_branch_has_new_commits=Гілку "%s" не можна видалити, оÑкільки піÑÐ»Ñ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¾ додано нові коміти.
branch.create_branch=Створити гілку %s
+branch.create_from=`з "%s"`
+branch.create_success=Створено гілку "%s".
+branch.branch_name_conflict=Ðазва гілки "%s" конфліктує з уже Ñ–Ñнуючою гілкою "%s".
+branch.tag_collision=Гілка "%s" не може бути Ñтворена, оÑкільки у Ñховищі вже Ñ–Ñнує мітка з такою назвою.
branch.deleted_by=Видалено %s
+branch.restore_success=Гілку "%s" відновлено.
+branch.restore_failed=Ðе вдалоÑÑ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ гілку "%s".
+branch.protected_deletion_failed=Гілка "%s" захищена. Її неможливо видалити.
+branch.default_deletion_failed=Гілка "%s" Ñтандартна. Її неможливо видалити.
+branch.restore=`Відновити гілку "%s"`
+branch.download=`Завантажити гілку "%s"`
+branch.rename=`Перейменувати гілку "%s"`
branch.included_desc=Ð¦Ñ Ð³Ñ–Ð»ÐºÐ° Ñ” чаÑтиною типової гілки
branch.included=Включено
branch.create_new_branch=Створити гілку з гілки:
branch.confirm_create_branch=Створити гілку
+branch.warning_rename_default_branch=Ви перейменовуєте Ñтандартну гілку.
+branch.rename_branch_to=Перейменувати "%s" на:
branch.confirm_rename_branch=Перейменувати гілку
branch.create_branch_operation=Створити гілку
branch.new_branch=Створити нову гілку
+branch.new_branch_from=`Створити нову гілку з "%s"`
branch.renamed=Гілку %s перейменовано на %s.
+branch.rename_default_or_protected_branch_error=Лише адмініÑтратори можуть перейменовувати типові або захищені гілки.
+branch.rename_protected_branch_failed=Ð¦Ñ Ð³Ñ–Ð»ÐºÐ° захищена правилами захиÑту на оÑнові глобальних правил.
tag.create_tag=Створити тег %s
+tag.create_tag_operation=Створити мітку
+tag.confirm_create_tag=Створити мітку
+tag.create_tag_from=`Створити нову мітку з "%s"`
+tag.create_success=Мітку "%s" Ñтворено.
-topic.manage_topics=Керувати тематичними мітками
+topic.manage_topics=Керувати темами
topic.done=Готово
+topic.count_prompt=Ви не можете вибрати більше ніж 25 тем
+topic.format_prompt=Теми мають починатиÑÑ Ð· літери або цифри, можуть міÑтити дефіÑи ('-') Ñ– крапки ('.'), мати довжину до 35 Ñимволів. Літери повинні бути малими.
+find_file.go_to_file=Перейти до файлу
+find_file.no_matching=Ðе знайдено відповідного файлу
error.csv.too_large=Ðе вдаєтьÑÑ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð·Ð¸Ñ‚Ð¸ цей файл, тому що він завеликий.
error.csv.unexpected=Ðе вдаєтьÑÑ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð·Ð¸Ñ‚Ð¸ цей файл, тому що він міÑтить неочікуваний Ñимвол в Ñ€Ñдку %d Ñ– Ñтовпці %d.
error.csv.invalid_field_count=Ðе вдаєтьÑÑ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð·Ð¸Ñ‚Ð¸ цей файл, тому що він має неправильну кількіÑть полів у Ñ€Ñдку %d.
[graphs]
+component_loading=Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ %s...
+component_loading_failed=Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ %s
+component_loading_info=Це може зайнÑти трохи чаÑу…
+component_failed_to_load=СталаÑÑŒ непередбачена помилка.
+code_frequency.what=чаÑтота коду
+contributors.what=внеÑки
+recent_commits.what=нові коміти
[org]
org_name_holder=Ðазва організації
@@ -1904,6 +2602,7 @@ create_org=Створити організацію
repo_updated=Оновлено
members=УчаÑники
teams=Команди
+code=Код
lower_members=учаÑники
lower_repositories=репозиторії
create_new_team=Ðова команда
@@ -1913,43 +2612,62 @@ team_name=Ðазва команди
team_desc=ОпиÑ
team_name_helper=Ðазва команди має бути проÑтою та зрозумілою.
team_desc_helper=Опишіть мету або роль команди.
-team_access_desc=ДоÑтуп до репозиторіÑ
+team_access_desc=ДоÑтуп до Ñховища
team_permission_desc=Права доÑтупу
team_unit_desc=Дозволити доÑтуп до розділів репозиторію
team_unit_disabled=(Вимкнено)
+form.name_been_taken=Ðазва організації "%s" вже зайнÑта.
+form.name_reserved=Ðазву організації "%s" зарезервовано.
+form.name_pattern_not_allowed=Шаблон "%s" не допуÑкаєтьÑÑ Ð² назві організації.
form.create_org_not_allowed=Вам не дозволено Ñтворювати організації.
settings=ÐалаштуваннÑ
settings.options=ОрганізаціÑ
settings.full_name=Повне ім'Ñ
+settings.email=Контактна адреÑа електронної пошти
settings.website=Веб-Ñайт
settings.location=РозташуваннÑ
settings.permission=Дозволи
settings.repoadminchangeteam=ÐдмініÑтратор репозитарію може додавати та видалÑти доÑтуп Ð´Ð»Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´
settings.visibility=ВидиміÑть
-settings.visibility.public=Публічний
-settings.visibility.limited_shortname=Обмежений
-settings.visibility.private=Приватний (Видимий лише членам організації)
+settings.visibility.public=Публічна
+settings.visibility.limited=Обмежений (Видимий лише Ð´Ð»Ñ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ¾Ð²Ð°Ð½Ð¸Ñ… кориÑтувачів)
+settings.visibility.limited_shortname=Обмежена
+settings.visibility.private=Приватна (Видима лише членам організації)
settings.visibility.private_shortname=Приватний
settings.update_settings=Оновити налаштуваннÑ
settings.update_setting_success=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ— оновлені.
-settings.change_orgname_redirect_prompt=Старе ім'Ñ Ð±ÑƒÐ´Ðµ перенаправлено до тих пір, поки воно не буде заброньовано.
+
+settings.rename=Перейменувати організацію
+settings.rename_desc=Зміна назви організації також змінить URL адреÑу вашої організації Ñ– звільнить Ñтару назву.
+settings.rename_success=Організацію %[1]s уÑпішно перейменована на %[2].
+settings.rename_no_change=Ðазва організації не змінилаÑÑ.
+settings.rename_new_org_name=Ðазва нової організації
+settings.rename_failed=Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ¹Ð¼ÐµÐ½ÑƒÐ²Ð°Ñ‚Ð¸ організацію через внутрішню помилку
+settings.rename_notices_1=Цю операцію <strong>ÐЕМОЖЛИВО</strong> ÑкаÑувати.
+settings.rename_notices_2=Стара назва буде перенаправлÑтиÑÑ Ð½Ð° нову, поки хтоÑÑŒ не викориÑтає Ñ—Ñ—.
+
settings.update_avatar_success=Ðватар організації оновлений.
settings.delete=Видалити організацію
settings.delete_account=Видалити цю організацію
-settings.delete_prompt=ÐžÑ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ Ð±ÑƒÐ´Ðµ оÑтаточно видалена. Це <strong>ÐЕ МОЖЛИВО</strong> відмінити!
-settings.confirm_delete_account=Підтвердіть видаленнÑ
-settings.delete_org_title=Видалити організацію
-settings.delete_org_desc=Ð¦Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ Ð±ÑƒÐ´Ðµ безповоротно видалена. Продовжити?
-settings.hooks_desc=Додайте webhooks, Ñкий буде викликатиÑÑ Ð´Ð»Ñ <strong>вÑÑ–Ñ… репозиторіїв</strong> Ñкими володіє Ñ†Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ.
-
-settings.labels_desc=Додати мітки, Ñкі можуть бути викориÑтані Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡ Ð´Ð»Ñ <strong>вÑÑ–Ñ… репозиторіїв</strong> в цій організації.
+settings.delete_prompt=Організацію буде оÑтаточно видалено. Це <strong>ÐЕМОЖЛИВО</strong> ÑкаÑувати!
+settings.name_confirm=Введіть назву організації Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ:
+settings.delete_notices_1=Цю операцію <strong>ÐЕМОЖЛИВО</strong> ÑкаÑувати.
+settings.delete_notices_2=Ð¦Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ð°Ð·Ð°Ð²Ð¶Ð´Ð¸ видалить <strong>Ñховища</strong> <strong>%s</strong>, включно з кодом, задачами, коментарÑми, даними вікі та налаштуваннÑми Ñпівавторів.
+settings.delete_notices_3=Ð¦Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ð°Ð·Ð°Ð²Ð¶Ð´Ð¸ видалить вÑÑ– <strong>пакети</strong> <strong>%s</strong>.
+settings.delete_notices_4=Ð¦Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ð°Ð·Ð°Ð²Ð¶Ð´Ð¸ видалить вÑÑ– <strong>проєкти</strong> <strong>%s</strong>.
+settings.confirm_delete_account=Підтвердити видаленнÑ
+settings.delete_failed=Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ організацію через внутрішню помилку
+settings.delete_successful=Організацію <b>%s</b> уÑпішно видалено.
+settings.hooks_desc=Додайте веб-хуки, Ñкі Ñпрацьовуватимуть Ð´Ð»Ñ <strong>вÑÑ–Ñ… Ñховищ</strong> у цій організації.
+
+settings.labels_desc=Додайте мітки, Ñкі можна викориÑтовувати у задачах Ð´Ð»Ñ <strong>уÑÑ–Ñ… Ñховищ</strong> у цій організації.
members.membership_visibility=ВидиміÑть учаÑника:
members.public=Показувати
-members.public_helper=зробити прихованим
+members.public_helper=приховати
members.private=Прихований
members.private_helper=зробити видимим
members.member_role=Роль учаÑника:
@@ -1967,22 +2685,28 @@ teams.leave=Покинути
teams.leave.detail=Покинути %s?
teams.can_create_org_repo=Створити репозиторії
teams.can_create_org_repo_helper=УчаÑники можуть Ñтворювати нові репозиторії в організації. Ðвтор отримає доÑтуп адмініÑтратора до нового репозиторію.
-teams.read_access=Прочитані
+teams.none_access=Ðемає доÑтупу
+teams.none_access_helper=УчаÑники не можуть переглÑдати або виконувати будь-Ñкі інші дії з цією одиницею. Це не впливає на загальнодоÑтупні Ñховища.
+teams.general_access=Загальний доÑтуп
+teams.general_access_helper=Дозволи учаÑників будуть визначатиÑÑ Ð²Ñ–Ð´Ð¿Ð¾Ð²Ñ–Ð´Ð½Ð¾ до наведеної нижче таблиці дозволів.
+teams.read_access=ЧитаннÑ
teams.read_access_helper=УчаÑники можуть переглÑдати та клонувати репозиторії команд.
+teams.write_access=ЗапиÑ
teams.write_access_helper=УчаÑники можуть читати Ñ– виконувати push в репозиторії команд.
teams.admin_access=ДоÑтуп адмініÑтратора
teams.admin_access_helper=УчаÑники можуть виконувати pull, push в репозиторії команд Ñ– додавати Ñпівавторів в команду.
teams.no_desc=Ð¦Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° не має опиÑу
teams.settings=ÐалаштуваннÑ
-teams.owners_permission_desc=ВлаÑник має повний доÑтуп до <strong>уÑÑ–Ñ… репозиторіїв</strong> та має <strong>права адмініÑтратора</strong> організації.
+teams.owners_permission_desc=ВлаÑники мають повний доÑтуп до <strong>уÑÑ–Ñ… репозиторіїв</strong> та <strong>права адмініÑтратора</strong> організації.
teams.members=УчаÑники команди
teams.update_settings=Оновити налаштуваннÑ
teams.delete_team=Видалити команду
teams.add_team_member=Додати учаÑника команди
+teams.invite_team_member=ЗапроÑити до %s
teams.delete_team_title=Видалити команду
teams.delete_team_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð¸ ÑкаÑовує доÑтуп до Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ Ð´Ð»Ñ Ñ—Ñ— учаÑників. Продовжити?
teams.delete_team_success=Команду було видалено.
-teams.read_permission_desc=Ð¦Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° має доÑтуп Ð´Ð»Ñ <strong>читаннÑ</strong>: учаÑники можуть переглÑдати та клонувати репозиторії.
+teams.read_permission_desc=Ð¦Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° має доÑтуп на <strong>читаннÑ</strong>: учаÑники можуть переглÑдати та клонувати репозиторії.
teams.write_permission_desc=Ð¦Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° надає доÑтуп на <strong>запиÑ</strong>: учаÑники можуть отримувати й виконувати push команди до репозитрію.
teams.admin_permission_desc=Ð¦Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° надає <strong>адмініÑтраторÑький</strong> доÑтуп: учаÑники можуть читати, виконувати push команди та додавати Ñпівробітників до репозиторію.
teams.create_repo_permission_desc=Крім того, Ñ†Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° надає дозвіл <strong>Створити репозиторій</strong>: учаÑники можуть Ñтворювати нові репозиторії в організації.
@@ -1991,9 +2715,11 @@ teams.remove_all_repos_title=Видалити вÑÑ– репозиторії ко
teams.remove_all_repos_desc=Це видалить уÑÑ– репозиторії команди.
teams.add_all_repos_title=Додати вÑÑ– репозиторії
teams.add_all_repos_desc=Це додаÑть вÑÑ– репозиторії організації до команди.
+teams.add_nonexistent_repo=Сховище, Ñке ви намагаєтеÑÑ Ð´Ð¾Ð´Ð°Ñ‚Ð¸, не Ñ–Ñнує, будь лаÑка, Ñтворіть його Ñпочатку.
teams.add_duplicate_users=КориÑтувач уже Ñ” членом команди.
teams.repos.none=Ð”Ð»Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð¸ немає доÑтупних репозиторіїв.
teams.members.none=Ðемає членів в цій команді.
+teams.members.blocked_user=Ðе вдаєтьÑÑ Ð´Ð¾Ð´Ð°Ñ‚Ð¸ кориÑтувача, оÑкільки він заблокований організацією.
teams.specific_repositories=Конкретні репозиторії
teams.specific_repositories_helper=УчаÑники матимуть доÑтуп лише до репозиторіїв, Ñкі були Ñвно додані до команди. Вибір цього пункту <strong>не призводить</strong> до автоматичного Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–Ñ—Ð², доданих з <i>Ð’ÑÑ– репозиторії</i>.
teams.all_repositories=Ð’ÑÑ– репозиторії
@@ -2001,16 +2727,35 @@ teams.all_repositories_helper=Команда має доÑтуп до вÑÑ–Ñ… Ñ
teams.all_repositories_read_permission_desc=Ð¦Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° надає дозвіл <strong>ПереглÑд</strong> Ð´Ð»Ñ <strong>вÑÑ–Ñ… репозиторіїв</strong>: учаÑники можуть переглÑдати та клонувати Ñ—Ñ….
teams.all_repositories_write_permission_desc=Ð¦Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° надає дозвіл <strong>ЗапиÑ</strong> Ð´Ð»Ñ <strong>вÑÑ–Ñ… репозиторіїв</strong>: учаÑники можуть переглÑдати та виконувати push в репозиторіÑÑ….
teams.all_repositories_admin_permission_desc=Ð¦Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° надає дозвіл <strong>ÐдмініÑтруваннÑ</strong> Ð´Ð»Ñ <strong>вÑÑ–Ñ… репозиторіїв</strong>: учаÑники можуть переглÑдати, виконувати push та додавати Ñпівробітників.
-
+teams.invite.title=Ð’Ð°Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñили приєднатиÑÑ Ð´Ð¾ команди <strong>%s</strong> в організації <strong>%s</strong>.
+teams.invite.by=Запрошений %s
+teams.invite.description=Будь лаÑка, натиÑніть на кнопку нижче, щоб приєднатиÑÑ Ð´Ð¾ команди.
+
+view_as_role=ПереглÑнути Ñк: %s
+view_as_public_hint=Ви переглÑдаєте README Ñк публічний кориÑтувач.
+view_as_member_hint=Ви переглÑдаєте README Ñк член цієї організації.
+
+worktime=Ð§Ð°Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸
+worktime.date_range_start=Дата початку
+worktime.date_range_end=Дата завершеннÑ
+worktime.query=Запит
+worktime.time=ЧаÑ
+worktime.by_milestones=За етапами
+worktime.by_members=За учаÑниками
[admin]
+maintenance=Технічне обÑлуговуваннÑ
dashboard=Панель управліннÑ
+self_check=Самоперевірка
+identity_access=Ð†Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Ñ‚Ð° доÑтуп
users=Облікові запиÑи кориÑтувачів
organizations=Організації
+assets=РеÑурÑи коду
repositories=Репозиторії
hooks=Веб-хуки
+integrations=Інтеграції
authentication=Джерела автентифікації
-emails=Електронні адреÑи КориÑтувача
+emails=Електронна пошта кориÑтувача
config=КонфігураціÑ
config_summary=ПідÑумок
config_settings=ÐалаштуваннÑ
@@ -2019,8 +2764,11 @@ monitor=Моніторинг
first_page=Перша
last_page=ОÑтаннÑ
total=Разом: %d
+settings=ÐдмініÑтративні налаштуваннÑ
+dashboard.new_version_hint=Gitea %s тепер доÑтупна, ви викориÑтовуєте %s. Перевірте <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">блог</a> Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ñ— інформації.
dashboard.statistic=ПідÑумок
+dashboard.maintenance_operations=Операції з технічного обÑлуговуваннÑ
dashboard.system_status=Ð¡Ñ‚Ð°Ñ‚ÑƒÑ ÑиÑтеми
dashboard.operation_name=Ðазва операції
dashboard.operation_switch=Перемкнути
@@ -2029,20 +2777,23 @@ dashboard.clean_unbind_oauth=ОчиÑтити ÑпиÑок незавершенÐ
dashboard.clean_unbind_oauth_success=Ð’ÑÑ– незавершені зв'Ñзки OAuth були видалені.
dashboard.task.started=Запущено завданнÑ: %[1]s
dashboard.task.process=ЗавданнÑ: %[1]s
+dashboard.task.cancelled=ЗавданнÑ: %[1]s ÑкаÑовано: %[3]s
dashboard.task.error=Помилка у завданні: %[1]s:%[3]s
dashboard.task.finished=ЗавершилоÑÑ Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ, Ñке запуÑтив %[2]s: %[1]s
dashboard.task.unknown=Ðевідоме завданнÑ: %[1]s
dashboard.cron.started=Запущено Cron: %[1]s
dashboard.cron.process=Cron: %[1]s
+dashboard.cron.cancelled=Планувальник: %[1]s ÑкаÑовано: %[3]s
dashboard.cron.error=Помилка в Cron: %s: %[3]s
dashboard.cron.finished=Cron: %[1]s завершено
dashboard.delete_inactive_accounts=Видалити вÑÑ– неактивовані облікові запиÑи
-dashboard.delete_inactive_accounts.started=Запущено Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²ÑÑ– неактивованих облікових запиÑів.
+dashboard.delete_inactive_accounts.started=Запущено Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²ÑÑ–Ñ… неактивованих облікових запиÑів.
dashboard.delete_repo_archives=Видалити вÑÑ– архіви репозиторіїв (ZIP, TAR.GZ, Ñ– Ñ‚. д..)
dashboard.delete_repo_archives.started=Запущено Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²ÑÑ–Ñ… архівів репозиторіїв.
-dashboard.delete_missing_repos=Видалити вÑÑ– запиÑи про репозиторії з відÑутніми файлами Git
+dashboard.delete_missing_repos=Видаліть уÑÑ– Ñховища, в Ñких відÑутні файли Git
dashboard.delete_missing_repos.started=Запущено Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²ÑÑ–Ñ… репозиторіїв, в Ñких відÑутні файли Git.
-dashboard.delete_generated_repository_avatars=Видалити репозиторій з згенерованими аватарами
+dashboard.delete_generated_repository_avatars=Видалити згенеровані аватарки Ñховища
+dashboard.sync_repo_branches=Синхронізувати пропущені гілки з даних git до баз даних
dashboard.update_mirrors=Оновити дзеркала
dashboard.repo_health_check=Перевірка Ñтану вÑÑ–Ñ… репозиторіїв
dashboard.check_repo_stats=Перевірити ÑтатиÑтику вÑÑ–Ñ… репозиторіїв
@@ -2052,11 +2803,13 @@ dashboard.update_migration_poster_id=Оновити мігровані ID авт
dashboard.git_gc_repos=Виконати очиÑтку ÑÐ¼Ñ–Ñ‚Ñ‚Ñ Ð´Ð»Ñ Ð²ÑÑ–Ñ… репозиторіїв
dashboard.resync_all_sshkeys=Оновити файл '.ssh/authorized_keys' з SSH ключами Gitea.
dashboard.resync_all_sshprincipals=Оновіть файл '.ssh/authorized_princÑ‚ipals' з SSH даними кориÑтувача Gitea.
-dashboard.resync_all_hooks=ПереÑинхронізувати перед-прийнÑтні, оновлюючі та поÑÑ‚-прийнÑтні хуки в уÑÑ–Ñ… репозиторіÑÑ….
-dashboard.reinit_missing_repos=Переініціалізувати уÑÑ– репозитрії git-файли Ñких втрачено
+dashboard.resync_all_hooks=Заново Ñинхронізувати хуки попереднього отриманнÑ, Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‚Ð° поÑÑ‚-Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð²ÑÑ–Ñ… Ñховищ.
+dashboard.reinit_missing_repos=Заново ініціалізувати вÑÑ– відÑутні Ñховища Git'а, Ð´Ð»Ñ Ñких Ñ–Ñнують запиÑи
dashboard.sync_external_users=Синхронізувати дані зовнішніх кориÑтувачів
-dashboard.cleanup_hook_task_table=ОчиÑтити hook_task таблицю
-dashboard.server_uptime=Uptime Ñерверу
+dashboard.cleanup_hook_task_table=ОчиÑтити таблицю hook_task
+dashboard.cleanup_packages=ОчиÑтити заÑтарілі пакети
+dashboard.cleanup_actions=ÐžÑ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ñ€ÐµÑурÑів проÑтрочених дій
+dashboard.server_uptime=Ð§Ð°Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸ Ñервера
dashboard.current_goroutine=Поточна кількіÑть Goroutines
dashboard.current_memory_usage=Поточне викориÑÑ‚Ð°Ð½Ð½Ñ Ð¿Ð°Ð¼'Ñті
dashboard.total_memory_allocated=Виділено пам'Ñті загалом
@@ -2085,6 +2838,18 @@ dashboard.total_gc_time=Загальна пауза збирача ÑміттÑ
dashboard.total_gc_pause=Загальна пауза збирача ÑÐ¼Ñ–Ñ‚Ñ‚Ñ (GC)
dashboard.last_gc_pause=ОÑÑ‚Ð°Ð½Ð½Ñ Ð¿Ð°ÑƒÐ·Ð° збирача ÑÐ¼Ñ–Ñ‚Ñ‚Ñ (GC)
dashboard.gc_times=КількіÑть запуÑків збирача ÑÐ¼Ñ–Ñ‚Ñ‚Ñ (GC)
+dashboard.delete_old_actions=Видалити вÑÑ– Ñтарі дії з бази даних
+dashboard.delete_old_actions.started=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²ÑÑ–Ñ… Ñтарих дій з бази даних розпочато.
+dashboard.update_checker=Перевірка оновлень
+dashboard.delete_old_system_notices=Видалити вÑÑ– Ñтарі ÑиÑтемні Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð· бази даних
+dashboard.gc_lfs=Збір ÑÐ¼Ñ–Ñ‚Ñ‚Ñ Ð¼ÐµÑ‚Ð°-об'єктів LFS
+dashboard.stop_endless_tasks=Зупинити неÑкінченні завданнÑ
+dashboard.cancel_abandoned_jobs=СкаÑувати покинуті завданнÑ
+dashboard.start_schedule_tasks=ЗапуÑк запланованих завдань
+dashboard.sync_branch.started=Розпочато Ñинхронізацію гілок
+dashboard.sync_tag.started=Розпочато Ñинхронізацію міток
+dashboard.rebuild_issue_indexer=Перебудувати індекÑатор задач
+dashboard.sync_repo_licenses=Синхронізувати ліцензії Ñховища
users.user_manage_panel=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ð¼Ð¸ запиÑами кориÑтувачів
users.new_account=Створити обліковий запиÑ
@@ -2093,12 +2858,16 @@ users.full_name=Повне ім'Ñ
users.activated=Ðктивовано
users.admin=ÐдмініÑтратор
users.restricted=Обмежено
+users.reserved=Зарезервовано
+users.bot=Бот
+users.remote=Віддалений
users.2fa=2FA
users.repos=Репозиторії
users.created=Створено
users.last_login=ОÑтанній вхід
users.never_login=Ðіколи не входив
users.send_register_notify=ÐадіÑлати Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ реєÑтрацію кориÑтувача
+users.new_success=Обліковий Ð·Ð°Ð¿Ð¸Ñ "%s" Ñтворено.
users.edit=Редагувати
users.auth_source=Джерело автентифікації
users.local=Локальні
@@ -2118,8 +2887,10 @@ users.allow_import_local=Може імпортувати локальні реп
users.allow_create_organization=Може Ñтворювати організацій
users.update_profile=Оновити обліковий запиÑ
users.delete_account=Видалити цей обліковий запиÑ
-users.still_own_repo=Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð²Ñе ще володіє одним або кількома репозиторіÑми, Ñпочатку вам потрібно видалити або передати Ñ—Ñ….
-users.still_has_org=Цей обліковий Ð·Ð°Ð¿Ð¸Ñ Ð²Ñе ще Ñ” учаÑником однієї або декількох організацій. Ð”Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ, покиньте або видаліть організації.
+users.cannot_delete_self=Ви не можете видалити Ñебе
+users.still_own_repo=Цей кориÑтувач вÑе ще володіє одним або кількома Ñховищами. Спочатку видаліть або передайте ці Ñховища.
+users.still_has_org=Цей кориÑтувач Ñ” членом організації. Спочатку видаліть кориÑтувача з уÑÑ–Ñ… організацій.
+users.purge=Видалити кориÑтувача
users.deletion_success=Обліковий Ð·Ð°Ð¿Ð¸Ñ ÐºÐ¾Ñ€Ð¸Ñтувача було видалено.
users.reset_2fa=Скинути 2FA
users.list_status_filter.menu_text=Фільтр
@@ -2130,13 +2901,14 @@ users.list_status_filter.is_admin=ÐдмініÑтратор
users.list_status_filter.not_admin=Ðе адмініÑтратор
users.list_status_filter.is_restricted=З обмеженнÑми
users.list_status_filter.not_restricted=Без обмежень
-users.list_status_filter.is_prohibit_login=Вхід заборонено
-users.list_status_filter.not_prohibit_login=Вхід дозволено
+users.list_status_filter.is_prohibit_login=Заборонити вхід
+users.list_status_filter.not_prohibit_login=Дозволити вхід
users.list_status_filter.is_2fa_enabled=2FA увімкнена
users.list_status_filter.not_2fa_enabled=2FA вимкнена
+users.details=Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ кориÑтувача
emails.email_manage_panel=Ð£Ð¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ Ð¿Ð¾ÑˆÑ‚Ð¾ÑŽ кориÑтувача
-emails.primary=Головний
+emails.primary=ОÑновна
emails.activated=Ðктивовано
emails.filter_sort.email=Електронна пошта
emails.filter_sort.email_reverse=Електронна пошта (зворотна)
@@ -2144,8 +2916,13 @@ emails.filter_sort.name=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача
emails.filter_sort.name_reverse=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача (зворотне)
emails.updated=Електронну пошту оновлено
emails.not_updated=Ðе вдалоÑÑŒ оновити адреÑу електронної пошти: %v
-emails.duplicate_active=Ð¦Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð° адреÑа вже активна Ð´Ð»Ñ Ñ–Ð½ÑˆÐ¾Ð³Ð¾ кориÑтувача.
+emails.duplicate_active=Ð¦Ñ Ð°Ð´Ñ€ÐµÑа електронної пошти вже активна Ð´Ð»Ñ Ñ–Ð½ÑˆÐ¾Ð³Ð¾ кориÑтувача.
emails.change_email_header=Редагувати влаÑтивоÑті електронної пошти
+emails.change_email_text=Ви впевнені, що хочете оновити адреÑу електронної пошти?
+emails.delete=Видалити адреÑу електронної пошти
+emails.delete_desc=Ви впевнені, що хочете видалити адреÑу електронної пошти?
+emails.deletion_success=ÐдреÑу електронної пошти видалено.
+emails.delete_primary_email_error=Ви не можете видалити оÑновну адреÑу електронної пошти.
orgs.org_manage_panel=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñми
orgs.name=Ðазва
@@ -2161,12 +2938,21 @@ repos.name=Ðазва
repos.private=Приватний
repos.issues=Задачі
repos.size=Розмір
+repos.lfs_size=Розмір LFS
+packages.package_manage_panel=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð°ÐºÐµÑ‚Ð°Ð¼Ð¸
+packages.total_size=Загальний розмір: %s
+packages.unreferenced_size=Розмір без поÑилань: %s
+packages.cleanup=ОчиÑтити проÑтрочені дані
+packages.cleanup.success=ÐžÑ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ñтрочених даних завершено
packages.owner=ВлаÑник
+packages.creator=Ðвтор
packages.name=Ðазва
+packages.version=ВерÑÑ–Ñ
packages.type=Тип
-packages.repository=Репозиторій
+packages.repository=Сховище
packages.size=Розмір
+packages.published=Опубліковано
defaulthooks=Веб-хуки за замовчуваннÑм
defaulthooks.add_webhook=Додати веб-хук за замовчуваннÑм
@@ -2178,7 +2964,7 @@ systemhooks.update_webhook=Оновити ÑиÑтемний вебхук
auths.auth_manage_panel=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¶ÐµÑ€ÐµÐ»Ð¾Ð¼ автентифікації
auths.new=Додати джерело автентифікації
-auths.name=Ім'Ñ
+auths.name=Ðазва
auths.type=Тип
auths.enabled=Увімкнено
auths.syncenabled=Увімкнути Ñинхронізацію кориÑтувача
@@ -2195,12 +2981,13 @@ auths.user_base=База пошуку кориÑтувачів
auths.user_dn=DN кориÑтувача
auths.attribute_username=Ðтрибут імені кориÑтувача
auths.attribute_username_placeholder=Залиште порожнім, щоб викориÑтовувати ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача Ð´Ð»Ñ Ñ€ÐµÑ”Ñтрації.
-auths.attribute_name=Ðтрибут імені
-auths.attribute_surname=Ðтрибут Surname
-auths.attribute_mail=Ðтрибут Email
-auths.attribute_ssh_public_key=Ðтрибут Відкритий SSH ключ
+auths.attribute_name=ВлаÑтивоÑті імені
+auths.attribute_surname=ВлаÑтивоÑті прізвища
+auths.attribute_mail=ВлаÑтивоÑті електронної пошти
+auths.attribute_ssh_public_key=ВлаÑтивоÑті публічного ключа SSH
+auths.attribute_avatar=ВлаÑтивоÑті аватару
auths.attributes_in_bind=ВитÑгувати атрибути в контекÑті Bind DN
-auths.allow_deactivate_all=Дозволити порожньому результату пошуку відключити вÑÑ–Ñ… кориÑтувачів
+auths.allow_deactivate_all=Дозволити порожній результат пошуку, щоб деактивувати вÑÑ–Ñ… кориÑтувачів
auths.use_paged_search=ВикориÑтовувати поÑторінковий пошук
auths.search_page_size=Розмір Ñторінки
auths.filter=КориÑтувацький фільтр
@@ -2210,6 +2997,7 @@ auths.restricted_filter_helper=Залиште пуÑтим, щоб не вÑта
auths.group_search_base=Пошукова база груп DN
auths.group_attribute_list_users=Ðтрибут групи зі ÑпиÑком кориÑтувачів
auths.user_attribute_in_group=Ðтрибути кориÑтувача в групі
+auths.enable_ldap_groups=Увімкнути групи LDAP
auths.ms_ad_sa=Ðтрибути пошуку MS AD
auths.smtp_auth=Тип автентифікації SMTP
auths.smtphost=SMTP хоÑÑ‚
@@ -2225,7 +3013,7 @@ auths.disable_helo=Вимкнути HELO
auths.pam_service_name=Ім'Ñ Ñлужби PAM
auths.pam_email_domain=Поштовий домен PAM (необов'Ñзково)
auths.oauth2_provider=ПоÑтачальник OAuth2
-auths.oauth2_icon_url=URL іконки
+auths.oauth2_icon_url=URL піктограми
auths.oauth2_clientID=ID клієнта (ключ)
auths.oauth2_clientSecret=Ключ клієнта
auths.openIdConnectAutoDiscoveryURL=OpenID Connect URL Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ— входу
@@ -2237,6 +3025,7 @@ auths.oauth2_emailURL=URL електронної пошти
auths.skip_local_two_fa=ПропуÑтити локальну 2FA
auths.skip_local_two_fa_helper=Якщо Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð½Ðµ вказано, локальнам кориÑтувачам, що викориÑтовують двофакторну автентифікацію, вÑе одно проходитимуть Ñ—Ñ— Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ в ÑиÑтему
auths.oauth2_tenant=Tenant
+auths.oauth2_map_group_to_team_removal=Видалити кориÑтувачів із Ñинхронізованих команд, Ñкщо кориÑтувач не належить до відповідної групи.
auths.enable_auto_register=Увімкнути автоматичну реєÑтрацію
auths.sspi_auto_create_users=Ðвтоматично Ñтворювати кориÑтувачів
auths.sspi_auto_create_users_helper=Дозволити автоматичне ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð½Ð¾Ð²Ð¸Ñ… облікових запиÑів Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів, Ñкі вперше увійшли з викориÑÑ‚Ð°Ð½Ð½Ñ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ— SSPI
@@ -2250,21 +3039,33 @@ auths.sspi_default_language=Типова мова кориÑтувача
auths.sspi_default_language_helper=Типова мова Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів, Ñкі ÑтворюютьÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾ при SSPI-автентифікації. Залиште не вказаним, Ñкщо надаєте перевагу автоматичному визначенню мови.
auths.tips=Поради
auths.tips.oauth2.general=OAuth2 автентифікаціÑ
+auths.tips.oauth2.general.tip=Під Ñ‡Ð°Ñ Ñ€ÐµÑ”Ñтрації нової автентифікації OAuth2 URL-адреÑа зворотного виклику/Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ð¾Ð²Ð¸Ð½Ð½Ð° бути такою:
auths.tip.oauth2_provider=ПоÑтачальник OAuth2
+auths.tip.bitbucket=`ЗареєÑтруйте нового Ñпоживача OAuth на %s Ñ– додайте дозвіл "Обліковий запиÑ" - "ЧитаннÑ"`
auths.tip.nextcloud=`ЗареєÑтруйте нового Ñпоживача OAuth у вашому екземплÑрі за допомогою наÑтупного меню "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ -> Безпека -> клієнт OAuth 2.0"`
+auths.tip.dropbox=Створити новий додаток на %s
+auths.tip.facebook=`ЗареєÑтруйте новий додаток на %s Ñ– додайте модуль "Facebook Login"`
+auths.tip.github=ЗареєÑтруйте новий додаток OAuth на %s
+auths.tip.gitlab_new=ЗареєÑтруйте новий додаток на %s
+auths.tip.google_plus=Отримайте облікові дані клієнта OAuth2 з конÑолі Google API за адреÑою %s
+auths.tip.twitter=Перейдіть до %s, Ñтворіть додаток Ñ– переконайтеÑÑ, що параметр «Дозволити викориÑтовувати цей додаток Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ в Twitter» увімкнено
+auths.tip.discord=ЗареєÑтрувати новий додаток на %s
+auths.tip.gitea=ЗареєÑтруйте новий додаток OAuth2. ПоÑібник можна знайти за поÑиланнÑм %s
auths.tip.mastodon=Введіть URL Ñпеціального екземплÑра Ð´Ð»Ñ ÐµÐºÐ·ÐµÐ¼Ð¿Ð»Ñра mastodon, Ñкий ви хочете автентифікувати за допомогою (або викориÑтовувати за замовчуваннÑм)
auths.edit=Редагувати джерело автентифікації
-auths.activated=Ð¦Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¾Ð²Ð°Ð½Ð°
-auths.update_success=Параметри аутентифікації оновлені.
+auths.activated=Це джерело автентифікації активовано
+auths.new_success=Ðвтентифікацію "%s" додано.
+auths.update_success=Джерело автентифікації оновлено.
auths.update=Оновити джерело автентифікації
auths.delete=Видалити джерело автентифікації
auths.delete_auth_title=Видалити джерело автентифікації
-auths.delete_auth_desc=Це джерело аутентифікації буде видалене, ви впевнені, що ви хочете продовжити?
-auths.still_in_used=Ð¦Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ° ÑправжноÑті доÑÑ– викориÑтовуєтьÑÑ Ð´ÐµÑкими кориÑтувачами. Видаліть або змініть Ð´Ð»Ñ Ñ†Ð¸Ñ… кориÑтувачів тип входу в ÑиÑтему.
-auths.deletion_success=Канал аутентифікації уÑпішно знищений.
-auths.login_source_of_type_exist=Джерело автентифікації такого типу вже наÑвне.
+auths.delete_auth_desc=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð´Ð¶ÐµÑ€ÐµÐ»Ð° автентифікації заборонÑÑ” кориÑтувачам викориÑтовувати його Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ. Продовжити?
+auths.still_in_used=Джерело автентифікації вÑе ще викориÑтовуєтьÑÑ. Спочатку перетворіть або видаліть кориÑтувачів, Ñкі викориÑтовують це джерело автентифікації.
+auths.deletion_success=Джерело автентифікації видалено.
+auths.login_source_exist=Джерело автентифікації "%s" вже Ñ–Ñнує.
+auths.login_source_of_type_exist=Джерело автентифікації такого типу вже Ñ–Ñнує.
-config.server_config=ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ñервера
+config.server_config=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñервера
config.app_name=Ðазва Ñайту
config.app_ver=ВерÑÑ–Ñ Gitea
config.app_url=Базова URL-адреÑа Gitea
@@ -2272,13 +3073,14 @@ config.custom_conf=ШлÑÑ… до файлу конфігурації
config.custom_file_root_path=ШлÑÑ… до файлу кориÑтувача
config.domain=Домен Ñервера
config.offline_mode=Локальний режим
-config.disable_router_log=Вимкнути Ð»Ð¾Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€Ð¾ÑƒÑ‚ÐµÑ€Ñƒ
+config.disable_router_log=Вимкнути журнал маршрутизатора
config.run_user=ЗапуÑк від імені КориÑтувача
config.run_mode=Режим виконаннÑ
config.git_version=ВерÑÑ–Ñ Git
+config.app_data_path=ШлÑÑ… до даних додатка
config.repo_root_path=Кореневий шлÑÑ… репозиторіÑ
-config.lfs_root_path=Кореневої шлÑÑ… LFS
-config.log_file_root_path=ШлÑÑ… до лог файлу
+config.lfs_root_path=Кореневий шлÑÑ… LFS
+config.log_file_root_path=ШлÑÑ… до журналу
config.script_type=Тип Ñкрипта
config.reverse_auth_user=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ñ–Ñ— на reverse proxy
@@ -2288,9 +3090,7 @@ config.ssh_start_builtin_server=ВикориÑтовувати вбудованÐ
config.ssh_domain=Домен SSH Ñервера
config.ssh_port=Порт
config.ssh_listen_port=Порт що проÑлуховуєтьÑÑ
-config.ssh_root_path=ШлÑÑ… до кореню
-config.ssh_key_test_path=ШлÑÑ… до теÑтового ключа
-config.ssh_keygen_path=ШлÑÑ… до генератора ключів ('ssh-keygen')
+config.ssh_root_path=ШлÑÑ… до коренÑ
config.ssh_minimum_key_size_check=Мінімальний розмір ключа перевірки
config.ssh_minimum_key_sizes=Мінімальні розміри ключів
@@ -2302,8 +3102,8 @@ config.lfs_http_auth_expiry=ЗаÑтаріла LFS HTTP аунтифікаціÑ
config.db_config=ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð±Ð°Ð·Ð¸ даних
config.db_type=Тип
config.db_host=ХоÑÑ‚
-config.db_name=Ім'Ñ
-config.db_user=Ім'Ñ ÐºÑ€Ð¸Ñтувача
+config.db_name=Ðазва
+config.db_user=Ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача
config.db_schema=Схема
config.db_ssl_mode=SSL
config.db_path=ШлÑÑ…
@@ -2335,16 +3135,24 @@ config.queue_length=Довжина черги
config.deliver_timeout=Затримка доÑтавки
config.skip_tls_verify=ПропуÑтити перевірку TLS
+config.mailer_config=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾ÑˆÑ‚Ð¸
config.mailer_enabled=Увімкнено
-config.mailer_name=Ім'Ñ
-config.mailer_smtp_port=SMTP порт
+config.mailer_enable_helo=Увімкнути HELO
+config.mailer_name=Ðазва
+config.mailer_protocol=Протокол
+config.mailer_smtp_addr=ÐдреÑа SMTP
+config.mailer_smtp_port=Порт SMTP
config.mailer_user=КориÑтувач
config.mailer_use_sendmail=ВикориÑтовувати Sendmail
config.mailer_sendmail_path=ШлÑÑ… до Sendmail
config.mailer_sendmail_args=Додаткові аргументи до Sendmail
config.mailer_sendmail_timeout=Тайм-аут Sendmail
+config.mailer_use_dummy=Даммі
config.test_email_placeholder=ÐдреÑа електронної пошти (наприклад, test@example.com)
config.send_test_mail=Відправити теÑтового лиÑта
+config.send_test_mail_submit=ÐадіÑлати
+config.test_mail_failed=Ðе вдалоÑÑ Ð½Ð°Ð´Ñ–Ñлати теÑтовий лиÑÑ‚ "%s": %v
+config.test_mail_sent=ТеÑтовий лиÑÑ‚ надіÑлано "%s".
config.oauth_config=ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ OAuth
config.oauth_enabled=Увімкнено
@@ -2354,6 +3162,10 @@ config.cache_adapter=Ðдаптер кешу
config.cache_interval=Інтервал кешуваннÑ
config.cache_conn=ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ кешу
config.cache_item_ttl=Ð§Ð°Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… кешу
+config.cache_test=Перевірити кеш
+config.cache_test_failed=Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€Ð¸Ñ‚Ð¸ кеш: %v.
+config.cache_test_slow=Перевірка кешу уÑпішна, але відповідь повільна: %s.
+config.cache_test_succeeded=ТеÑÑ‚ кешу уÑпішно завершено, отримано відповідь за %s.
config.session_config=ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ ÑеÑÑ–Ñ—
config.session_provider=Провайдер ÑеÑÑ–Ñ—
@@ -2382,23 +3194,32 @@ config.git_pull_timeout=Тайм-аут операції Pull
config.git_gc_timeout=Тайм-аут операції збирача ÑÐ¼Ñ–Ñ‚Ñ‚Ñ (GC)
config.log_config=ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ñƒ
+config.logger_name_fmt=Журнал: %s
config.disabled_logger=Вимкнено
config.access_log_mode=Режим доÑтупу до журналу
+config.access_log_template=Шаблон журналу доÑтупу
config.xorm_log_sql=Журнал SQL
+config.set_setting_failed=Ðе вдалоÑÑ Ð²Ñтановити параметр %s
+monitor.stats=СтатиÑтика
monitor.cron=Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ cron
-monitor.name=Ім'Ñ
+monitor.name=Ðазва
monitor.schedule=Розклад
monitor.next=ÐаÑтупного разу
monitor.previous=Попереднього разу
monitor.execute_times=КількіÑть виконань
monitor.process=Запущені процеÑи
+monitor.performance_logs=Журнал швидкодії
+monitor.processes_count=%d процеÑи(-ів)
+monitor.download_diagnosis_report=Завантажити звіт діагноÑтики
monitor.desc=ОпиÑ
monitor.start=Ð§Ð°Ñ Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ
monitor.execute_time=Ð§Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ
+monitor.last_execution_result=Результат
monitor.process.cancel=Зупинити процеÑ
+monitor.process.cancel_desc=СкаÑÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñ†ÐµÑу може призвеÑти до втрати даних
monitor.process.children=Дочірні процеÑи
monitor.queues=Черги
@@ -2408,15 +3229,19 @@ monitor.queue.type=Тип
monitor.queue.exemplar=Приклад типу
monitor.queue.numberworkers=КількіÑть робочих потоків
monitor.queue.maxnumberworkers=МакÑимальна кількіÑть робочих потоків
+monitor.queue.numberinqueue=Ðомер у черзі
monitor.queue.settings.title=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿ÑƒÐ»Ñƒ
monitor.queue.settings.maxnumberworkers=МакÑимальна кількіÑть робочих потоків
monitor.queue.settings.maxnumberworkers.placeholder=Поточний %[1]d
monitor.queue.settings.maxnumberworkers.error=МакÑимальна кількіÑть робочих потоків має бути чиÑлом
monitor.queue.settings.submit=Оновити налаштуваннÑ
monitor.queue.settings.changed=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð¾
+monitor.queue.settings.remove_all_items=Видалити вÑе
+monitor.queue.settings.remove_all_items_done=УÑÑ– елементи черги видалено.
notices.system_notice_list=Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ ÑиÑтеми
notices.view_detail_header=ПереглÑнути деталі повідомленнÑ
+notices.operations=Дії
notices.select_all=Вибрати вÑе
notices.deselect_all=СкаÑувати виділеннÑ
notices.inverse_selection=Інвертувати виділене
@@ -2429,11 +3254,19 @@ notices.desc=ОпиÑ
notices.op=Оп.
notices.delete_success=Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ ÑиÑтеми були видалені.
+self_check.no_problem_found=Ðаразі проблем не виÑвлено.
+self_check.startup_warnings=ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ–Ð´ Ñ‡Ð°Ñ Ð·Ð°Ð¿ÑƒÑку:
+self_check.database_collation_mismatch=ОчікуєтьÑÑ, що база даних викориÑтає зіÑтавленнÑ: %s
+self_check.database_collation_case_insensitive=У базі даних викориÑтовуєтьÑÑ Ð·Ñ–ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ %s, що Ñ” нечутливим. Хоч Gitea може працювати з ним, у деÑких рідкіÑних випадках він може працювати не так, Ñк очікуєтьÑÑ.
+self_check.database_inconsistent_collation_columns=База даних викориÑтовує зіÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ %s, але ці Ñтовпці викориÑтовують невідповідні зіÑтавленнÑ. Це може Ñпричинити деÑкі неÑподівані проблеми.
+self_check.database_fix_mysql=Ð”Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів MySQL/MariaDB можна викориÑтати команду "gitea doctor convert" Ð´Ð»Ñ Ñ€Ð¾Ð·Ð²'ÑÐ·Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼ з ÑортуваннÑм, або ви також можете розв'Ñзати проблему SQL командою "ALTER ... COLLATE ..." вручну.
+self_check.database_fix_mssql=`Ð”Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів MSSQL, наразі ви можете виправити цю проблему тільки через SQL запит "ALTER ... COLATE ..."`
+self_check.location_origin_mismatch=Поточна URL-адреÑа (%[1]) не відповідає URL-адреÑÑ– Gitea (%[2]). Якщо ви викориÑтовуєте зворотний прокÑÑ–, переконайтеÑÑ, що заголовки "Host" та "X-Forwarded-Proto" вÑтановлені правильно.
[action]
create_repo=Ñтворив(ла) репозиторій <a href="%s">%s</a>
rename_repo=репозиторій перейменовано з <code>%[1]s</code> на <a href="%[2]s">%[3]s</a>
-commit_repo=надіÑлав зміни (push) до <a href="%[2]s">%[3]s</a> о <a href="%[1]s">%[4]s</a>
+commit_repo=надіÑлав зміни (push) до <a href="%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a>
create_issue=`відкрив задачу <a href="%[1]s">%[3]s#%[2]s</a>`
close_issue=`закрив задачу <a href="%[1]s">%[3]s#%[2]s</a>`
reopen_issue=`повторно відкрив задачу <a href="%[1]s">%[3]s#%[2]s</a>`
@@ -2443,6 +3276,7 @@ reopen_pull_request=`повторно відкрив запит Ð·Ð»Ð¸Ñ‚Ñ‚Ñ <a
comment_issue=`прокоментував задачу <a href="%[1]s">%[3]s#%[2]s</a>`
comment_pull=`прокоментував запит Ð·Ð»Ð¸Ñ‚Ñ‚Ñ <a href="%[1]s">%[3]s#%[2]s</a>`
merge_pull_request=`прийнÑв запит Ð·Ð»Ð¸Ñ‚Ñ‚Ñ <a href="%[1]s">%[3]s#%[2]s</a>`
+auto_merge_pull_request=`автоматично об'єднано запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ <a href="%[1]s">%[3]s#%[2]s</a>`
transfer_repo=перенеÑено репозиторій <code>%s</code> у <a href="%s">%s</a>
push_tag=Ñтворив мітку <a href="%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a>
delete_tag=видалено мітку %[2]s з <a href="%[1]s">%[3]s</a>
@@ -2475,7 +3309,7 @@ seconds=%d Ñекунди
minutes=%d хвилини
hours=%d години
days=%d дні
-weeks=%d тижднів
+weeks=%d тижні(в)
months=%d міÑÑці
years=%d роки
raw_seconds=Ñекунди
@@ -2483,6 +3317,7 @@ raw_minutes=хвилини
[dropzone]
default_message=ПеретÑгніть файли або натиÑніть тут, щоб завантажити.
+invalid_input_type=Ðеможливо завантажити файли цього типу.
file_too_big=Розмір файлу ({{filesize}} MB), що більше ніж макÑимальний розмір: ({{maxFilesize}} MB).
remove_file=Видалити файл
@@ -2496,53 +3331,292 @@ pin=Прикріпити ÑповіщеннÑ
mark_as_read=Позначити Ñк прочитане
mark_as_unread=Позначити Ñк непрочитане
mark_all_as_read=Позначити вÑÑ– Ñк прочитані
+subscriptions=Ðбонементи
+watching=Стежить
+no_subscriptions=Ðемає абонементів
[gpg]
default_key=ПідпиÑано типовим ключем
error.extract_sign=Ðе вдалоÑÑ Ð²Ð¸Ñ‚Ñгти підпиÑ
error.generate_hash=Ðе вдалоÑÑ Ð·Ð³ÐµÐ½ÐµÑ€ÑƒÐ²Ð°Ñ‚Ð¸ хеш коміту
-error.no_committer_account=Ðккаунт кориÑтувача з таким Email не знайдено
-error.no_gpg_keys_found=Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ GPG ключ що відповідає даному підпиÑу
+error.no_committer_account=Ðемає облікового запиÑу, прив'Ñзаного до адреÑи електронної пошти комітера
+error.no_gpg_keys_found=Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ підпиÑу в базі даних не знайдено жодного відомого ключа
error.not_signed_commit=ÐепідпиÑаний коміт
-error.failed_retrieval_gpg_keys=Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ відповідний GPG ключ кориÑтувача
+error.failed_retrieval_gpg_keys=Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ жодного ключа, прив'Ñзаного до облікового запиÑу комітера
error.probable_bad_signature=УВÐГÐ! Хоча ключ з таким ID Ñ– Ñ” в базі, коміт не може бути ним перевірено! Цей коміт ПІДОЗРІЛИЙ.
error.probable_bad_default_signature=УВÐГÐ! Хоча типовий ключ має цей ID, коміт не може бути ним перевірено! Цей коміт ПІДОЗРІЛИЙ.
[units]
-error.no_unit_allowed_repo=У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” доÑтупу до жодного розділу цього репозиториÑ.
-error.unit_not_allowed=У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” доÑтупу до жодного розділу цього репозиториÑ.
+unit=ÐžÐ´Ð¸Ð½Ð¸Ñ†Ñ Ð²Ð¸Ð¼Ñ–Ñ€ÑŽÐ²Ð°Ð½Ð½Ñ
+error.no_unit_allowed_repo=У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” доÑтупу до жодного розділу цього Ñховища.
+error.unit_not_allowed=У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” доÑтупу до жодного розділу цього Ñховища.
[packages]
+title=Пакети
+desc=Керувати пакетами Ñховища.
+empty=Ðаразі пакети відÑутні.
+no_metadata=Ðемає метаданих.
+empty.documentation=Ð”Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ñ— інформації про реєÑтр пакетів, дивітьÑÑ <a target="_blank" rel="noopener noreferrer" href="%s">документацію</a>.
+empty.repo=Ви завантажили пакунок, але він тут не відображаєтьÑÑ? Перейдіть до <a href="%[1]s">Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð°ÐºÐµÑ‚Ñƒ</a> та під'єднайте його до цього Ñховища.
+registry.documentation=Докладнішу інформацію про реєÑтр %s наведено у <a target="_blank" rel="noopener noreferrer" href="%s">документації</a>.
filter.type=Тип
+filter.type.all=Ð’ÑÑ–
+filter.no_result=Ваш фільтр не дав результатів.
+filter.container.tagged=З міткою
+filter.container.untagged=Без мітки
+published_by=%[1]s опубліковано <a href="%[2]s">%[3]s</a>
+published_by_in=%[1]s опубліковано <a href="%[2]s">%[3]s</a> у <a href="%[4]s"><strong>%[5]s</strong></a>
+installation=Ð’ÑтановленнÑ
+about=Про цей пакет
+requirements=Вимоги
+dependencies=ЗалежноÑті
+keywords=Ключові Ñлова
+details=Подробиці
+details.author=Ðвтор
+details.project_site=Сторінка проєкту
+details.repository_site=Сторінка Ñховища
+details.documentation_site=Сторінка документації
+details.license=ЛіцензіÑ
+assets=РеÑурÑи
+versions=ВерÑÑ–Ñ—
+versions.view_all=ПереглÑнути вÑе
+dependency.id=ID
+dependency.version=ВерÑÑ–Ñ
+search_in_external_registry=Шукати в %s
+alpine.registry=Ðалаштуйте цей реєÑтр, додавши URL у ваш файл <code>/etc/apk/repositories</code>:
+alpine.registry.key=Завантажте публічний ключ RSA реєÑтру в теку <code>/etc/apk/keys/</code> Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ підпиÑу індекÑу:
+alpine.registry.info=Виберіть $branch та $repository зі ÑпиÑку нижче.
+alpine.install=Щоб вÑтановити пакет, виконайте наÑтупну команду:
+alpine.repository=Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ Ñховище
alpine.repository.branches=Гілки
alpine.repository.repositories=Репозиторії
+alpine.repository.architectures=Ðрхітектури
+arch.registry=Додати Ñервер з відповідним Ñховищем та архітектурою до <code>/etc/pacman.conf</code>:
+arch.install=Синхронізувати пакет з pacman:
+arch.repository=Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ Ñховище
arch.repository.repositories=Репозиторії
-conan.details.repository=Репозиторій
+arch.repository.architectures=Ðрхітектури
+cargo.registry=Ðалаштуйте цей реєÑтр у файлі конфігурації Cargo (наприклад, <code>~/.cargo/config.toml</code>):
+cargo.install=Щоб вÑтановити пакет за допомогою Cargo, виконайте наÑтупну команду:
+chef.registry=Ðалаштуйте цей реєÑтр у вашому файлі <code>~/.chef/config.rb</code>:
+chef.install=Щоб вÑтановити пакет, виконайте наÑтупну команду:
+composer.registry=Ðалаштуйте цей реєÑтр у вашому файлі <code>~/.composer/config.json</code>:
+composer.install=Щоб вÑтановити пакет за допомогою Composer, виконайте наÑтупну команду:
+composer.dependencies=ЗалежноÑті
+composer.dependencies.development=ЗалежноÑті розробки
+conan.details.repository=Сховище
+conan.registry=Ðалаштувати реєÑтр із командного Ñ€Ñдка:
+conan.install=Щоб вÑтановити пакет за допомогою Conan, виконайте наÑтупну команду:
+conda.registry=Ðалаштуйте цей реєÑтр Ñк Ñховище Conda у влаÑному файлі <code>.condarc</code>:
+conda.install=Щоб вÑтановити пакет за допомогою Conda, виконайте наÑтупну команду:
+container.details.type=Тип зображеннÑ
+container.details.platform=Платформа
+container.pull=Завантажити образ з командного Ñ€Ñдка:
+container.images=Образи
+container.multi_arch=ОС / Ðрхітектура
+container.layers=Шари образів
+container.labels=Мітки
+container.labels.key=Ключ
+container.labels.value=ЗначеннÑ
+cran.registry=Ðалаштуйте цей реєÑтр у вашому файлі <code>Rprofile.site</code>:
+cran.install=Щоб вÑтановити пакет, виконайте наÑтупну команду:
+debian.registry=Ðалаштувати реєÑтр із командного Ñ€Ñдка:
+debian.registry.info=Оберіть $distribution та $component зі ÑпиÑку нижче.
+debian.install=Щоб вÑтановити пакет, виконайте наÑтупну команду:
+debian.repository=Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ Ñховище
+debian.repository.distributions=ДиÑтрибутиви
+debian.repository.components=Компоненти
+debian.repository.architectures=Ðрхітектури
+generic.download=Завантажити пакет з командного Ñ€Ñдка:
+go.install=Ð’Ñтановити пакет із командного Ñ€Ñдка:
+helm.registry=Ðалаштувати реєÑтр із командного Ñ€Ñдка:
+helm.install=Щоб вÑтановити пакет, виконайте наÑтупну команду:
+maven.registry=Ðалаштуйте цей реєÑтр у файлі <code>pom.xml</code> вашого проєкту:
+maven.install=Щоб викориÑтати пакет, додайте наÑтупне до блоку <code>dependencies</code> у файлі <code>pom.xml</code>:
+maven.install2=Виконати з командного Ñ€Ñдка:
+maven.download=Щоб завантажити залежніÑть, виконайте в командному Ñ€Ñдку:
+nuget.registry=Ðалаштувати реєÑтр із командного Ñ€Ñдка:
+nuget.install=Щоб вÑтановити пакет за допомогою NuGet, запуÑтіть наÑтупну команду:
+nuget.dependency.framework=Цільовий фреймворк
+npm.registry=Ðалаштувати цей реєÑтр у файлі вашого проєкту <code>.npmrc</code>:
+npm.install=Щоб вÑтановити пакет за допомогою npm, виконайте наÑтупну команду:
+npm.install2=або додайте до файлу package.json:
+npm.dependencies=ЗалежноÑті
+npm.dependencies.development=ЗалежноÑті розробки
+npm.dependencies.optional=ÐеобовʼÑзкові залежноÑті
+npm.details.tag=Мітка
+pypi.requires=Потрібен Python
+rpm.registry=Ðалаштувати реєÑтр із командного Ñ€Ñдка:
+rpm.distros.redhat=на диÑтрибутивах на оÑнові RedHat
+rpm.distros.suse=на диÑтрибутивах на оÑнові SUSE
+rpm.install=Щоб вÑтановити пакет, виконайте наÑтупну команду:
+rpm.repository=Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ Ñховище
+rpm.repository.architectures=Ðрхітектури
+rpm.repository.multiple_groups=Цей пакет доÑтупний у багатьох групах.
+rubygems.install=Щоб вÑтановити пакет за допомогою gem, виконайте наÑтупну команду:
+rubygems.install2=або додайте до Gemfile:
+rubygems.dependencies.development=ЗалежноÑті розробки
+rubygems.required.ruby=Вимагає верÑÑ–ÑŽ Ruby
+rubygems.required.rubygems=Вимагає верÑÑ–ÑŽ RubyGem
+swift.registry=Ðалаштувати реєÑтр із командного Ñ€Ñдка:
+swift.install=Додайте пакет у ваш файл <code>Package.swift</code>:
+swift.install2=Ñ– виконайте наÑтупну команду:
+vagrant.install=Щоб додати Ð±Ð¾ÐºÑ Vagrant, виконайте наÑтупну команду:
+settings.link=Прив'Ñзати пакет до Ñховища
+settings.link.description=Якщо ви зв'Ñжете пакет зі Ñховищем, його буде вказано у ÑпиÑку пакетів Ñховища.
+settings.link.select=Обрати Ñховище
+settings.link.button=Оновити поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° Ñховище
+settings.link.error=Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° Ñховище.
+settings.delete=Видалити пакет
+settings.delete.description=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¿Ð°ÐºÐµÑ‚Ð° Ñ” оÑтаточним Ñ– не може бути ÑкаÑоване.
+settings.delete.notice=Ви збираєтеÑÑŒ видалити %s (%s). Цю операцію неможливо ÑкаÑувати, ви впевнені?
+settings.delete.success=Пакет видалено.
+settings.delete.error=Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ пакет.
+owner.settings.cargo.title=Ð†Ð½Ð´ÐµÐºÑ Ñ€ÐµÑ”Ñтру Cargo
+owner.settings.cargo.initialize=Ініціалізувати індекÑ
+owner.settings.cargo.initialize.description=Ð”Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ Ñ€ÐµÑ”Ñтру Cargo потрібне Ñпеціальне Ñховище індекÑів Git. ВикориÑÑ‚Ð°Ð½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ параметра дозволить (повторно) Ñтворити та автоматично налаштувати Ñховище.
+owner.settings.cargo.initialize.error=Ðе вдалоÑÑ Ñ–Ð½Ñ–Ñ†Ñ–Ð°Ð»Ñ–Ð·ÑƒÐ²Ð°Ñ‚Ð¸ Ñ–Ð½Ð´ÐµÐºÑ Cargo: %v
+owner.settings.cargo.initialize.success=Ð†Ð½Ð´ÐµÐºÑ Cargo уÑпішно Ñтворено.
+owner.settings.cargo.rebuild=Перебудувати індекÑ
+owner.settings.cargo.rebuild.description=Повторна збірка може бути кориÑною, Ñкщо Ñ–Ð½Ð´ÐµÐºÑ Ð½Ðµ Ñинхронізовано зі збереженими пакетами Cargo.
+owner.settings.cargo.rebuild.error=Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ±ÑƒÐ´ÑƒÐ²Ð°Ñ‚Ð¸ Ñ–Ð½Ð´ÐµÐºÑ Cargo: %v
+owner.settings.cargo.rebuild.success=Ð†Ð½Ð´ÐµÐºÑ Cargo уÑпішно перебудовано.
+owner.settings.cleanuprules.title=Керувати правилами очищеннÑ
+owner.settings.cleanuprules.add=Додати правило очищеннÑ
+owner.settings.cleanuprules.edit=Редагувати правило очищеннÑ
+owner.settings.cleanuprules.none=Правила Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð²Ñ–Ð´Ñутні. Будь лаÑка, звернітьÑÑ Ð´Ð¾ документації.
+owner.settings.cleanuprules.preview=Попередній переглÑд правила очищеннÑ
+owner.settings.cleanuprules.preview.overview=Заплановано Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ %d пакетів.
+owner.settings.cleanuprules.preview.none=Правило Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð½Ðµ відповідає жодному пакету.
owner.settings.cleanuprules.enabled=Увімкнено
+owner.settings.cleanuprules.pattern_full_match=ЗаÑтоÑувати шаблон до повної назви пакета
+owner.settings.cleanuprules.keep.title=ВерÑÑ–Ñ—, Ñкі відповідають цим правилам, зберігаютьÑÑ, навіть Ñкщо вони відповідають наведеному нижче правилу видаленнÑ.
+owner.settings.cleanuprules.keep.count=Залишити найновіші
+owner.settings.cleanuprules.keep.count.1=1 верÑÑ–Ñ Ð½Ð° пакет
+owner.settings.cleanuprules.keep.count.n=%d верÑÑ–Ñ—(-й) на пакет
+owner.settings.cleanuprules.keep.pattern=Зберігати верÑÑ–Ñ—, що збігаютьÑÑ
+owner.settings.cleanuprules.remove.title=ВерÑÑ–Ñ—, Ñкі відповідають цим правилам, видалÑютьÑÑ, Ñкщо правило вище не вимагає Ñ—Ñ… збереженнÑ.
+owner.settings.cleanuprules.remove.days=Видалити верÑÑ–Ñ—, Ñтаріші за
+owner.settings.cleanuprules.remove.pattern=Видалити верÑÑ–Ñ—, що збігаютьÑÑ
+owner.settings.cleanuprules.success.update=Правило Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð¾.
+owner.settings.cleanuprules.success.delete=Правило Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð¾.
+owner.settings.chef.title=РеєÑтр Chef
+owner.settings.chef.keypair=Згенерувати ключову пару
+owner.settings.chef.keypair.description=Ключова пара необхідна Ð´Ð»Ñ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ— у реєÑтрі Chef. Якщо ви Ñтворювали ключову пару раніше, ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð½Ð¾Ð²Ð¾Ñ— пари призведе до ÑкаÑÑƒÐ²Ð°Ð½Ð½Ñ Ñтарої.
[secrets]
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=ОпиÑ
+creation.name_placeholder=без ÑƒÑ€Ð°Ñ…ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÐ³Ñ–Ñтру, тільки алфавітно-цифрові Ñимволи або підкреÑленнÑ, не можуть починатиÑÑ Ð· GITEA_ або GITHUB_.
+creation.value_placeholder=Введіть довільний вміÑÑ‚. Пробіли на початку та в кінці будуть пропущені.
+creation.description_placeholder=Введіть короткий Ð¾Ð¿Ð¸Ñ (необов'Ñзково).
+
+
+
[actions]
+actions=Дії
+unit.desc=Керувати діÑми
+status.unknown=Ðевідомий
+status.waiting=ОчікуваннÑ
+status.running=ВиконуєтьÑÑ
+status.success=УÑпіх
+status.failure=Ðевдача
+status.cancelled=СкаÑовано
+status.skipped=Пропущено
+status.blocked=Заблоковано
+runners.status=СтатуÑ
+runners.id=ID
runners.name=Ðазва
runners.owner_type=Тип
runners.description=ОпиÑ
+runners.labels=Мітки
+runners.last_online=ОÑтанній раз онлайн
+runners.task_list.no_tasks=Ðаразі завдань немає.
runners.task_list.run=ЗапуÑтити
+runners.task_list.status=СтатуÑ
runners.task_list.repository=Репозиторій
runners.task_list.commit=Коміт
+runners.task_list.done_at=Завершено о
+runners.update_runner=Оновити зміни
+runners.status.unspecified=Ðевідомий
+runners.status.idle=ОчікуваннÑ
runners.status.active=Ðктивний
+runners.version=ВерÑÑ–Ñ
+runners.reset_registration_token=Скинути реєÑтраційний токен
+runs.all_workflows=Ð’ÑÑ– робочі процеÑи
runs.commit=Коміт
-
-
-
-
+runs.scheduled=Заплановано
+runs.pushed_by=завантажено
+runs.invalid_workflow_helper=Файл конфігурації робочих процеÑів недійÑний. Будь лаÑка, перевірте файл конфігурації: %s
+runs.no_job_without_needs=Робочий Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð¿Ð¾Ð²Ð¸Ð½ÐµÐ½ міÑтити принаймні одну задачу без залежноÑтей.
+runs.no_job=Робочий Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð¿Ð¾Ð²Ð¸Ð½ÐµÐ½ міÑтити принаймні одну задачу
+runs.actor=Ðктор
+runs.status=СтатуÑ
+runs.actors_no_select=УÑÑ– актори
+runs.status_no_select=Ð’ÑÑ– ÑтатуÑи
+runs.no_results=Збігів немає.
+runs.no_workflows=Робочих процеÑів наразі немає.
+runs.no_workflows.quick_start=Ðе знаєте, Ñк почати з Gitea Дії? ДивітьÑÑ <a target="_blank" rel="noopener noreferrer" href="%s">поÑібник швидкого Ñтарту</a>.
+runs.no_workflows.documentation=Ð”Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ñ— інформації про Gitea Дії, переглÑньте <a target="_blank" rel="noopener noreferrer" href="%s">документацію</a>.
+runs.no_runs=Робочий Ð¿Ñ€Ð¾Ñ†ÐµÑ Ñ‰Ðµ не виконувавÑÑ.
+runs.empty_commit_message=(порожнє Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñƒ)
+runs.expire_log_message=Журнали були очищені, тому що вони були занадто Ñтарі.
+runs.delete=Видалити запущений робочий процеÑ
+runs.delete.description=Ви впевнені, що хочете оÑтаточно видалити цей робочий процеÑ? Цю дію неможливо ÑкаÑувати.
+runs.not_done=Ð’Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ робочого процеÑу не завершено.
+runs.view_workflow_file=ПереглÑд файлу робочого процеÑу
+
+workflow.disable=Вимкнути робочий процеÑ
+workflow.disable_success=Робочий Ð¿Ñ€Ð¾Ñ†ÐµÑ '%s' уÑпішно вимкнено.
+workflow.enable=Увімкнути робочий процеÑ
+workflow.enable_success=Робочий Ð¿Ñ€Ð¾Ñ†ÐµÑ '%s' уÑпішно ввімкнено.
+workflow.disabled=Робочий Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð¸Ð¹.
+workflow.run=ЗапуÑтити робочий процеÑ
+workflow.not_found=Робочий Ð¿Ñ€Ð¾Ñ†ÐµÑ '%s' не знайдено.
+workflow.run_success=Робочий Ð¿Ñ€Ð¾Ñ†ÐµÑ '%s' завершивÑÑ ÑƒÑпішно.
+workflow.from_ref=ВикориÑтати робочий Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð·
+workflow.has_workflow_dispatch=Цей робочий Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð¼Ð°Ñ” тригер події workflow_dispatch.
+workflow.has_no_workflow_dispatch=Робочий Ð¿Ñ€Ð¾Ñ†ÐµÑ â€œ%s†не має тригера події workflow_dispatch.
+
+
+variables=Змінні
+variables.management=ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð¼Ñ–Ð½Ð½Ð¸Ð¼Ð¸
+variables.creation=Додати змінну
+variables.none=Ðаразі немає змінних.
+variables.deletion=Видалити змінну
+variables.deletion.description=Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½Ð½Ð¾Ñ— Ñ” оÑтаточним Ñ– не може бути ÑкаÑоване. Продовжити?
+variables.description=Змінні будуть передані певним діÑм Ñ– не можуть бути прочитані інакше.
+variables.id_not_exist=Змінної з ідентифікатором %d не Ñ–Ñнує.
+variables.edit=Редагувати змінну
+variables.deletion.failed=Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ змінну.
+variables.deletion.success=Змінну видалено.
+variables.creation.failed=Ðе вдалоÑÑ Ð´Ð¾Ð´Ð°Ñ‚Ð¸ змінну.
+variables.creation.success=Змінну "%s" додано.
+variables.update.failed=Ðе вдалоÑÑ Ð²Ñ–Ð´Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ñ‚Ð¸ змінну.
+variables.update.success=Змінну відредаговано.
+
+logs.always_auto_scroll=Завжди автоматично прокручувати журнали
+logs.always_expand_running=Завжди розгортати поточні журнали
[projects]
+deleted.display_name=Видалений проєкт
+type-1.display_name=Індивідуальний проєкт
+type-2.display_name=Проєкт Ñховища
+type-3.display_name=Проєкт організації
+enter_fullscreen=Повноекранний режим
+exit_fullscreen=Вийти з повноекранного режиму
[git.filemode]
+changed_filemode=%[1]s → %[2]s
; Ordered by git filemode value, ascending. E.g. directory has "040000", normal file has "100644", …
+directory=Тека
+normal_file=Звичайний файл
+executable_file=Виконуваний файл
symbolic_link=Символічне поÑиланнÑ
+submodule=Підмодуль
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 5e4723a4cd..2a97941d6b 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -4,14 +4,14 @@ explore=探索
help=帮助
logo=徽标
sign_in=登录
-sign_in_with_provider=使用 %s 登录
+sign_in_with_provider=使用「%sã€ç™»å½•
sign_in_or=或
sign_out=退出
sign_up=注册
link_account=链接账户
register=注册
-version=当å‰ç‰ˆæœ¬
-powered_by=Powered by %s
+version=版本
+powered_by=由 %s 强力驱动
page=页é¢
template=模æ¿
language=语言选项
@@ -28,7 +28,7 @@ return_to_gitea=返回 Gitea
more_items=更多选项
username=用户å
-email=电å­é‚®ä»¶åœ°å€
+email=邮箱地å€
password=密ç 
access_token=访问令牌(Access Token)
re_type=确认密ç 
@@ -42,7 +42,7 @@ webauthn_sign_in=按下安全密钥上的按钮。如果安全密钥没有按钮
webauthn_press_button=请按下安全密钥上的按钮…
webauthn_use_twofa=使用æ¥è‡ªæ‰‹æœºä¸­çš„两步验è¯ç 
webauthn_error=无法读å–安全密钥。
-webauthn_unsupported_browser=ä½ çš„æµè§ˆå™¨ç›®å‰ä¸æ”¯æŒ WebAuthn。
+webauthn_unsupported_browser=您的æµè§ˆå™¨ç›®å‰ä¸æ”¯æŒ WebAuthn。
webauthn_error_unknown=å‘生未知错误。请é‡è¯•。
webauthn_error_insecure=WebAuthn 仅支æŒå®‰å…¨è¿žæŽ¥ã€‚如果è¦åœ¨ HTTP å议上进行测试,请使用 "localhost" 或 "127.0.0.1" ä½œä¸ºè®¿é—®æ¥æº
webauthn_error_unable_to_process=æœåŠ¡å™¨æ— æ³•å¤„ç†æ‚¨çš„请求。
@@ -54,14 +54,15 @@ webauthn_reload=釿–°åŠ è½½
repository=仓库
organization=组织
mirror=镜åƒ
+issue_milestone=里程碑
new_repo=创建仓库
new_migrate=è¿ç§»å¤–部仓库
new_mirror=创建新的镜åƒ
-new_fork=新的仓库Fork
+new_fork=派生新仓库
new_org=创建组织
new_project=创建项目
new_project_column=创建列
-manage_org=ç®¡ç†æˆ‘的组织
+manage_org=管ç†ç»„织
admin_panel=管ç†åŽå°
account_settings=叿ˆ·è®¾ç½®
settings=设置
@@ -77,7 +78,7 @@ forks=派生
activities=最近活动
pull_requests=åˆå¹¶è¯·æ±‚
-issues=å·¥å•管ç†
+issues=å·¥å•
milestones=里程碑
ok=确定
@@ -90,13 +91,13 @@ add=添加
add_all=添加所有
remove=移除
remove_all=移除所有
-remove_label_str=`删除标签 "%s"`
+remove_label_str=删除标签「%sã€
edit=编辑
view=查看
test=测试
-enabled=å¯ç”¨
-disabled=ç¦ç”¨
+enabled=å·²å¯ç”¨
+disabled=å·²ç¦ç”¨
locked=å·²é”定
copy=å¤åˆ¶
@@ -112,9 +113,11 @@ copy_type_unsupported=无法å¤åˆ¶æ­¤ç±»åž‹çš„æ–‡ä»¶å†…容
write=撰写
preview=预览
loading=正在加载...
+files=文件
error=错误
error404=您正å°è¯•è®¿é—®çš„é¡µé¢ <strong>ä¸å­˜åœ¨</strong> 或 <strong>您尚未被授æƒ</strong> 查看该页é¢ã€‚
+error503=æœåŠ¡å™¨æ— æ³•å®Œæˆæ‚¨çš„请求,请ç¨åŽé‡è¯•。
go_back=返回
invalid_data=无效数æ®ï¼š %v
@@ -126,8 +129,9 @@ rss_feed=RSS 订阅æº
pin=固定
unpin=å–æ¶ˆç½®é¡¶
-artifacts=制å“
-confirm_delete_artifact=您确定è¦åˆ é™¤åˆ¶å“'%s'å—?
+artifacts=产物
+expired=已过期
+confirm_delete_artifact=您确定è¦åˆ é™¤äº§ç‰©ã€Œ%sã€å—?
archived=已归档
@@ -147,7 +151,7 @@ name=åç§°
value=值
readme=自述文档
-filter=过滤
+filter=筛选
filter.clear=清除筛选器
filter.is_archived=已归档
filter.not_archived=éžå­˜æ¡£
@@ -158,16 +162,20 @@ filter.not_mirror=éžé•œåƒ
filter.is_template=模æ¿
filter.not_template=éžæ¨¡æ¿
filter.public=公开
-filter.private=ç§æœ‰åº“
+filter.private=ç§æœ‰
no_results_found=未找到结果
-internal_error_skipped=å‘生内部错误,但已被跳过: %s
+internal_error_skipped=å‘生内部错误,但已跳过: %s
[search]
search=æœç´¢...
type_tooltip=æœç´¢ç±»åž‹
fuzzy=模糊
fuzzy_tooltip=包å«è¿‘ä¼¼åŒ¹é…æœç´¢è¯çš„结果
+words=è¯
+words_tooltip=仅包å«åŒ¹é…æœç´¢è¯çš„结果
+regexp=正则表达å¼
+regexp_tooltip=仅包å«åŒ¹é…æ­£åˆ™è¡¨è¾¾å¼æœç´¢è¯çš„结果
exact=精确
exact_tooltip=仅包å«ç²¾ç¡®åŒ¹é…æœç´¢è¯çš„结果
repo_kind=æœç´¢ä»“库...
@@ -176,14 +184,14 @@ org_kind=æœç´¢ç»„织...
team_kind=æœç´¢å›¢é˜Ÿ...
code_kind=æœç´¢ä»£ç ...
code_search_unavailable=ä»£ç æœç´¢å½“å‰ä¸å¯ç”¨ã€‚请与网站管ç†å‘˜è”系。
-code_search_by_git_grep=当å‰ä»£ç æœç´¢ç»“果由“git grepâ€æä¾›ã€‚å¦‚æžœç«™ç‚¹ç®¡ç†å‘˜å¯ç”¨ä»“库索引器,å¯èƒ½ä¼šæœ‰æ›´å¥½çš„结果。
+code_search_by_git_grep=当å‰ä»£ç æœç´¢ç»“果由「git grepã€æä¾›ã€‚å¦‚æžœç«™ç‚¹ç®¡ç†å‘˜å¯ç”¨ä»“库索引器,å¯èƒ½ä¼šæœ‰æ›´å¥½çš„结果。
package_kind=æœç´¢è½¯ä»¶åŒ…...
project_kind=æœç´¢é¡¹ç›®...
branch_kind=æœç´¢åˆ†æ”¯...
tag_kind=æœç´¢æ ‡ç­¾...
-tag_tooltip=æœç´¢åŒ¹é…的标签。使用“%â€æ¥åŒ¹é…任何åºåˆ—的数字
+tag_tooltip=æœç´¢åŒ¹é…的标签。使用「%ã€æ¥åŒ¹é…任何åºåˆ—的数字。
commit_kind=æœç´¢æäº¤è®°å½•...
-runner_kind=æœç´¢runners...
+runner_kind=æœç´¢è¿è¡Œå™¨...
no_results=未找到匹é…结果
issue_kind=æœç´¢å·¥å•...
pull_kind=æœç´¢åˆå¹¶è¯·æ±‚...
@@ -244,6 +252,7 @@ license_desc=所有的代ç éƒ½å¼€æºåœ¨ <a target="_blank" rel="noopener norefe
[install]
install=安装页é¢
+installing_desc=正在安装,请ç¨å€™...
title=åˆå§‹é…ç½®
docker_helper=如果您正在使用 Docker 容器è¿è¡Œ Gitea,请务必先仔细阅读 <a target="_blank" rel="noopener noreferrer" href="%s">官方文档</a> åŽå†å¯¹æœ¬é¡µé¢è¿›è¡Œå¡«å†™ã€‚
require_db_desc=Gitea 需è¦ä½¿ç”¨ MySQLã€PostgreSQLã€MSSQLã€SQLite3 或 TiDB (MySQLåè®®) 等数æ®åº“
@@ -253,20 +262,20 @@ host=æ•°æ®åº“主机
user=用户å
password=æ•°æ®åº“用户密ç 
db_name=æ•°æ®åº“åç§°
-db_schema=Schema
+db_schema=æž¶æž„
db_schema_helper=留空则数æ®åº“中默认值为("public")。
ssl_mode=SSL
path=æ•°æ®åº“文件路径
sqlite_helper=SQLite3 æ•°æ®åº“的文件路径。<br>如果以æœåŠ¡çš„æ–¹å¼è¿è¡Œ Gitea,请输入ç»å¯¹è·¯å¾„。
reinstall_error=您正在å°è¯•å®‰è£…åˆ°ä¸€ä¸ªå·²ç»æœ‰ Gitea æ•°æ®çš„æ•°æ®åº“中
-reinstall_confirm_message=使用现有的 Gitea æ•°æ®åº“釿–°å®‰è£…å¯èƒ½ä¼šå¯¼è‡´å¤šä¸ªé—®é¢˜ã€‚在大多数情况下,你应该使用你现有的 “app.ini†æ¥è¿è¡Œ Gitea。如果你知é“自己在åšä»€ä¹ˆï¼Œè¯·ç¡®è®¤ä»¥ä¸‹å†…容:
+reinstall_confirm_message=使用现有的 Gitea æ•°æ®åº“釿–°å®‰è£…å¯èƒ½ä¼šå¯¼è‡´å¤šä¸ªé—®é¢˜ã€‚在大多数情况下,您应该使用您现有的「app.iniã€æ¥è¿è¡Œ Gitea。如果您知é“自己在åšä»€ä¹ˆï¼Œè¯·ç¡®è®¤ä»¥ä¸‹å†…容:
reinstall_confirm_check_1=使用 app.ini 中 SECRET KEY 加密的数æ®å¯èƒ½ä¼šä¸¢å¤±ï¼šç”¨æˆ·å¯èƒ½æ— æ³•使用 2FA/OTP 登录,仓库镜åƒå¯èƒ½æ— æ³•æ­£å¸¸å·¥ä½œã€‚å‹¾é€‰æ­¤æ¡†ï¼Œè¡¨ç¤ºæ‚¨ç¡®è®¤å½“å‰ app.ini æ–‡ä»¶åŒ…å«æ­£ç¡®çš„ SECRET KEY。
reinstall_confirm_check_2=代ç ä»“库和设置å¯èƒ½éœ€è¦é‡æ–°åŒæ­¥ã€‚å‹¾é€‰æ­¤æ¡†ï¼Œè¡¨ç¤ºæ‚¨ç¡®è®¤å°†æ‰‹åŠ¨é‡æ–°åŒæ­¥ä»“库和 SSH authorized_keys 的钩å­ã€‚您确认您将确ä¿ä»£ç ä»“库和镜åƒè®¾ç½®æ˜¯æ­£ç¡®çš„。
-reinstall_confirm_check_3=你确认你ç»å¯¹è‚¯å®šè¿™ä¸ª Gitea 在正确的 app.ini ä½ç½®ä¸Šè¿è¡Œï¼Œè€Œä¸”ä½ ç¡®å®šä½ å¿…é¡»é‡æ–°å®‰è£…。你确认你知晓上述风险。
+reinstall_confirm_check_3=您确认您ç»å¯¹è‚¯å®šè¿™ä¸ª Gitea 在正确的 app.ini ä½ç½®ä¸Šè¿è¡Œï¼Œè€Œä¸”æ‚¨ç¡®å®šæ‚¨å¿…é¡»é‡æ–°å®‰è£…。您确认您知晓上述风险。
err_empty_db_path=SQLite æ•°æ®åº“文件路径ä¸èƒ½ä¸ºç©ºã€‚
no_admin_and_disable_registration=您ä¸èƒ½å¤Ÿåœ¨æœªåˆ›å»ºç®¡ç†å‘˜ç”¨æˆ·çš„æƒ…å†µä¸‹ç¦æ­¢æ³¨å†Œã€‚
err_empty_admin_password=管ç†å‘˜å¯†ç ä¸èƒ½ä¸ºç©ºã€‚
-err_empty_admin_email=管ç†å‘˜ç”µå­é‚®ä»¶ä¸èƒ½ä¸ºç©ºã€‚
+err_empty_admin_email=管ç†å‘˜é‚®ç®±ä¸èƒ½ä¸ºç©ºã€‚
err_admin_name_is_reserved=管ç†å‘˜ç”¨æˆ·åæ— æ•ˆï¼Œç”¨æˆ·åæ˜¯ä¿ç•™çš„
err_admin_name_pattern_not_allowed=管ç†å‘˜ç”¨æˆ·åæ— æ•ˆï¼Œç”¨æˆ·åæ˜¯ä¿ç•™å­—
err_admin_name_is_invalid=管ç†å‘˜ç”¨æˆ·å无效
@@ -287,7 +296,7 @@ ssh_port_helper=SSH æœåŠ¡å™¨çš„ç«¯å£å·ï¼Œä¸ºç©ºåˆ™ç¦ç”¨å®ƒã€‚
http_port=HTTP æœåŠ¡ç«¯å£
http_port_helper=Giteas web æœåС噍将侦å¬çš„端å£å·ã€‚
app_url=基础URL
-app_url_helper=用于 HTTP (S) 克隆和电å­é‚®ä»¶é€šçŸ¥çš„基本地å€ã€‚
+app_url_helper=用于 HTTP (S) 克隆和邮件通知的基本地å€ã€‚
log_root_path=日志路径
log_root_path_helper=日志文件将写入此目录。
@@ -295,12 +304,12 @@ optional_title=å¯é€‰è®¾ç½®
email_title=电å­é‚®ç®±è®¾ç½®
smtp_addr=SMTP 主机地å€
smtp_port=SMTP 端å£
-smtp_from=电å­é‚®ä»¶å‘件人
-smtp_from_invalid=`"å‘é€ç”µå­é‚®ä»¶ä¸º"åœ°å€æ— æ•ˆ`
-smtp_from_helper=请输入一个用于 Gitea 的电å­é‚®ä»¶åœ°å€ï¼Œæˆ–者使用完整格å¼ï¼š"åç§°" <email@example.com>
+smtp_from=邮件å‘件人
+smtp_from_invalid=「邮件å‘件人ã€åœ°å€æ— æ•ˆ
+smtp_from_helper=请输入一个用于 Gitea 的邮箱地å€ï¼Œæˆ–者使用完整格å¼ï¼šã€Œåç§°ã€<email@example.com>。
mailer_user=SMTP 用户å
mailer_password=SMTP 密ç 
-register_confirm=需è¦å‘电å­é‚®ä»¶ç¡®è®¤æ³¨å†Œ
+register_confirm=需è¦é‚®ä»¶ç¡®è®¤æ³¨å†Œ
mail_notify=å¯ç”¨é‚®ä»¶é€šçŸ¥æé†’
server_service_title=æœåŠ¡å™¨å’Œç¬¬ä¸‰æ–¹æœåŠ¡è®¾ç½®
offline_mode=å¯ç”¨æœ¬åœ°æ¨¡å¼
@@ -325,12 +334,12 @@ admin_title=管ç†å‘˜å¸å·è®¾ç½®
admin_name=管ç†å‘˜ç”¨æˆ·å
admin_password=管ç†å‘˜å¯†ç 
confirm_password=确认密ç 
-admin_email=电å­é‚®ä»¶åœ°å€
+admin_email=邮箱地å€
install_btn_confirm=ç«‹å³å®‰è£…
test_git_failed=无法识别 'git' 命令:%v
sqlite3_not_available=您所使用的å‘è¡Œç‰ˆä¸æ”¯æŒ SQLite3,请从 %s ä¸‹è½½å®˜æ–¹æž„å»ºç‰ˆï¼Œè€Œä¸æ˜¯ gobuild 版本。
invalid_db_setting=æ•°æ®åº“设置无效: %v
-invalid_db_table=æ•°æ®åº“表 '%s' 无效: %v
+invalid_db_table=æ•°æ®åº“表「%sã€æ— æ•ˆï¼š%v
invalid_repo_path=仓库根目录设置无效:%v
invalid_app_data_path=应用数æ®è·¯å¾„无效: %v
run_user_not_match=è¿è¡Œç”¨æˆ·å䏿˜¯å½“å‰çš„用户å:%s -> %s
@@ -339,14 +348,14 @@ secret_key_failed=生æˆå¯†é’¥å¤±è´¥ï¼š %v
save_config_failed=应用é…ç½®ä¿å­˜å¤±è´¥ï¼š%v
invalid_admin_setting=管ç†å‘˜å¸æˆ·è®¾ç½®æ— æ•ˆ: %v
invalid_log_root_path=日志路径无效: %v
-default_keep_email_private=默认情况下éšè—电å­é‚®ä»¶åœ°å€
-default_keep_email_private_popup=默认情况下, éšè—æ–°ç”¨æˆ·å¸æˆ·çš„电å­é‚®ä»¶åœ°å€ã€‚
+default_keep_email_private=默认情况下éšè—邮箱地å€
+default_keep_email_private_popup=默认情况下,éšè—æ–°ç”¨æˆ·å¸æˆ·çš„邮箱地å€ã€‚
default_allow_create_organization=默认情况下å…许创建组织
default_allow_create_organization_popup=默认情况下, å…è®¸æ–°ç”¨æˆ·å¸æˆ·åˆ›å»ºç»„织。
default_enable_timetracking=默认情况下å¯ç”¨æ—¶é—´è·Ÿè¸ª
default_enable_timetracking_popup=默认情况下å¯ç”¨æ–°ä»“库的时间跟踪。
-no_reply_address=éšè—电å­é‚®ä»¶
-no_reply_address_helper=具有éšè—电å­é‚®ä»¶åœ°å€çš„用户的域å。例如, 用户å "joe" 将以 "joe@noreply.example.org" 的身份登录到 Git 中. 如果éšè—的电å­é‚®ä»¶åŸŸè®¾ç½®ä¸º "noreply.example.org"。
+no_reply_address=éšè—邮件域
+no_reply_address_helper=具有éšè—邮箱地å€çš„用户的域å。例如,如果éšè—邮箱域å设置为「noreply.example.orgã€ï¼Œé‚£ä¹ˆç”¨æˆ·å「joeã€åœ¨ Git 中将显示为「joe@noreply.example.orgã€ã€‚
password_algorithm=密ç å“ˆå¸Œç®—法
invalid_password_algorithm=无效的密ç å“ˆå¸Œç®—法
password_algorithm_helper=è®¾ç½®å¯†ç æ•£åˆ—算法。算法有ä¸åŒçš„è¦æ±‚和强度。 argon2 算法相当安全,但使用大é‡å†…存,因此å¯èƒ½ä¸é€‚åˆå°åž‹ç³»ç»Ÿã€‚
@@ -369,7 +378,7 @@ my_mirrors=我的镜åƒ
view_home=访问 %s
filter=其他过滤器
filter_by_team_repositories=按团队仓库筛选
-feed_of=`"%s"çš„æº`
+feed_of=「%sã€çš„æº
show_archived=已归档
show_both_archived_unarchived=显示已归档和未归档的
@@ -383,6 +392,13 @@ show_only_public=åªæ˜¾ç¤ºå…¬å¼€çš„
issues.in_your_repos=在您的仓库中
+guide_title=无活动
+guide_desc=æ‚¨ç›®å‰æ²¡æœ‰å…³æ³¨ä»»ä½•ä»“åº“æˆ–ç”¨æˆ·ï¼Œæ‰€ä»¥æ²¡æœ‰è¦æ˜¾ç¤ºçš„内容。 您å¯ä»¥ä»Žä¸‹é¢çš„链接中探索感兴趣的仓库或用户。
+explore_repos=探索仓库
+explore_users=探索用户
+empty_org=ç›®å‰è¿˜æ²¡æœ‰ç»„织。
+empty_repo=ç›®å‰è¿˜æ²¡æœ‰ä»“库。
+
[explore]
repos=仓库
users=用户
@@ -398,43 +414,45 @@ create_new_account=注册å¸å·
already_have_account=已有账å·ï¼Ÿ
sign_in_now=ç«‹å³ç™»å½•
disable_register_prompt=对ä¸èµ·ï¼Œæ³¨å†ŒåŠŸèƒ½å·²è¢«å…³é—­ã€‚è¯·è”系网站管ç†å‘˜ã€‚
-disable_register_mail=å·²ç¦ç”¨æ³¨å†Œçš„电å­é‚®ä»¶ç¡®è®¤ã€‚
+disable_register_mail=å·²ç¦ç”¨æ³¨å†Œé‚®ä»¶ç¡®è®¤ã€‚
manual_activation_only=请è”系您的站点管ç†å‘˜æ¥å®Œæˆæ¿€æ´»ã€‚
remember_me=è®°ä½æ­¤è®¾å¤‡
remember_me.compromised=登录令牌ä¸å†æœ‰æ•ˆï¼Œå› ä¸ºå®ƒå¯èƒ½è¡¨æ˜Žå¸æˆ·å·²è¢«ç ´åã€‚è¯·æ£€æŸ¥æ‚¨çš„å¸æˆ·æ˜¯å¦æœ‰å¼‚常活动。
forgot_password_title=忘记密ç 
forgot_password=忘记密ç ï¼Ÿ
-need_account=需è¦ä¸€ä¸ªå¸æˆ·?
-sign_up_now=还没账å·ï¼Ÿé©¬ä¸Šæ³¨å†Œã€‚
+need_account=需è¦ä¸€ä¸ªå¸æˆ·ï¼Ÿ
+sign_up_tip=æ‚¨æ­£åœ¨ç³»ç»Ÿä¸­æ³¨å†Œç¬¬ä¸€ä¸ªå¸æˆ·ï¼Œå®ƒæ‹¥æœ‰ç®¡ç†å‘˜æƒé™ã€‚è¯·ä»”ç»†è®°ä½æ‚¨çš„用户å和密ç ã€‚ å¦‚æžœæ‚¨å¿˜è®°äº†ç”¨æˆ·åæˆ–密ç ï¼Œè¯·å‚阅 Gitea 文档以æ¢å¤è´¦æˆ·ã€‚
+sign_up_now=ç«‹å³æ³¨å†Œã€‚
sign_up_successful=叿ˆ·åˆ›å»ºæˆåŠŸã€‚æ¬¢è¿Žï¼
-confirmation_mail_sent_prompt_ex=䏀尿–°çš„确认邮件已ç»å‘é€åˆ° <b>%s</b>请在下一个 %s ä¸­æ£€æŸ¥æ‚¨çš„æ”¶ä»¶ç®±ä»¥å®Œæˆæ³¨å†Œè¿‡ç¨‹ã€‚ 如果您的注册电å­é‚®ä»¶åœ°å€ä¸æ­£ç¡®ï¼Œæ‚¨å¯ä»¥é‡æ–°ç™»å½•并更改它。
+confirmation_mail_sent_prompt_ex=䏀尿–°çš„确认邮件已ç»å‘é€åˆ° <b>%s</b>。请在下一个 %s ä¸­æ£€æŸ¥æ‚¨çš„æ”¶ä»¶ç®±ä»¥å®Œæˆæ³¨å†Œæµç¨‹ã€‚ 如果您的注册邮箱地å€ä¸æ­£ç¡®ï¼Œæ‚¨å¯ä»¥é‡æ–°ç™»å½•并更改它。
must_change_password=更新您的密ç 
allow_password_change=è¦æ±‚用户更改密ç ï¼ˆæŽ¨è)
-reset_password_mail_sent_prompt=确认电å­é‚®ä»¶å·²è¢«å‘é€åˆ° <b>%s</b>。请您在 %s 内检查您的收件箱 ,完æˆå¯†ç é‡ç½®è¿‡ç¨‹ã€‚
+reset_password_mail_sent_prompt=确认邮件已被å‘é€åˆ° <b>%s</b>。请您在 %s 内检查您的收件箱 ,完æˆå¯†ç é‡ç½®æµç¨‹ã€‚
active_your_account=æ¿€æ´»æ‚¨çš„å¸æˆ·
account_activated=叿ˆ·å·²æ¿€æ´»
prohibit_login=ç¦æ­¢ç™»å½•
prohibit_login_desc=æ‚¨çš„å¸æˆ·è¢«ç¦æ­¢ç™»å½•,请与网站管ç†å‘˜è”系。
resent_limit_prompt=您请求å‘逿¿€æ´»é‚®ä»¶è¿‡äºŽé¢‘ç¹ï¼Œè¯·ç­‰å¾… 3 分钟åŽå†è¯•ï¼
has_unconfirmed_mail=%s 您好,系统检测到您有一å°å‘é€è‡³ <b>%s</b> 但未被确认的邮件。如果您未收到激活邮件,或需è¦é‡æ–°å‘é€ï¼Œè¯·å•击下方的按钮。
-change_unconfirmed_mail_address=如果您的注册电å­é‚®ä»¶åœ°å€ä¸æ­£ç¡®ï¼Œæ‚¨å¯ä»¥åœ¨æ­¤æ›´æ”¹å¹¶é‡æ–°å‘逿–°çš„确认电å­é‚®ä»¶ã€‚
+change_unconfirmed_mail_address=如果您的注册邮箱地å€ä¸æ­£ç¡®ï¼Œæ‚¨å¯ä»¥åœ¨æ­¤æ›´æ”¹å¹¶é‡æ–°å‘逿–°çš„确认邮件。
resend_mail=å•å‡»æ­¤å¤„é‡æ–°å‘é€ç¡®è®¤é‚®ä»¶
email_not_associate=æ‚¨è¾“å…¥çš„é‚®ç®±åœ°å€æœªè¢«å…³è”到任何å¸å·ï¼
send_reset_mail=å‘é€è´¦æˆ·æ¢å¤é‚®ä»¶
reset_password=账户æ¢å¤
invalid_code=此确认密钥无效或已过期。
-invalid_code_forgot_password=ä½ çš„ç¡®è®¤ç æ— æ•ˆæˆ–者已过期,点击 <a href="%s">这里</a> 开始新的会è¯ã€‚
+invalid_code_forgot_password=æ‚¨çš„ç¡®è®¤ç æ— æ•ˆæˆ–已过期,点击 <a href="%s">这里</a> 开始新的会è¯ã€‚
invalid_password=您的密ç ä¸Žç”¨äºŽåˆ›å»ºè´¦æˆ·çš„密ç ä¸åŒ¹é…。
reset_password_helper=æ¢å¤è´¦æˆ·
reset_password_wrong_user=您以 %s 登录,但æ¢å¤è´¦å·é“¾æŽ¥æ˜¯ç”¨äºŽ %s。
password_too_short=密ç é•¿åº¦ä¸èƒ½å°‘于 %d ä½ã€‚
-non_local_account=éžæœ¬åœ°å¸æˆ·ä¸èƒ½é€šè¿‡ Gitea çš„ web ç•Œé¢æ›´æ”¹å¯†ç ã€‚
+non_local_account=éžæœ¬åœ°å¸æˆ·ä¸èƒ½é€šè¿‡ Gitea çš„ Web ç•Œé¢æ›´æ”¹å¯†ç ã€‚
verify=验è¯
scratch_code=验è¯å£ä»¤
use_scratch_code=使用验è¯å£ä»¤
-twofa_scratch_used=ä½ å·²ç»ä½¿ç”¨äº†ä½ çš„验è¯å£ä»¤ã€‚你将会转到两步验è¯è®¾ç½®é¡µé¢ä»¥ä¾¿ç§»é™¤ä½ çš„æ³¨å†Œè®¾å¤‡æˆ–è€…é‡æ–°ç”Ÿæˆæ–°çš„验è¯å£ä»¤ã€‚
-twofa_passcode_incorrect=你的验è¯ç ä¸æ­£ç¡®ã€‚如果你丢失了你的设备,请使用你的验è¯å£ä»¤ã€‚
-twofa_scratch_token_incorrect=你的验è¯å£ä»¤ä¸æ­£ç¡®ã€‚
+twofa_scratch_used=您已ç»ä½¿ç”¨äº†æ‚¨çš„验è¯å£ä»¤ã€‚您将会转到两步验è¯è®¾ç½®é¡µé¢ä»¥ä¾¿ç§»é™¤æ‚¨çš„æ³¨å†Œè®¾å¤‡æˆ–è€…é‡æ–°ç”Ÿæˆæ–°çš„验è¯å£ä»¤ã€‚
+twofa_passcode_incorrect=您的验è¯ç ä¸æ­£ç¡®ã€‚如果您丢失了您的设备,请使用您的验è¯å£ä»¤ã€‚
+twofa_scratch_token_incorrect=您的验è¯å£ä»¤ä¸æ­£ç¡®ã€‚
+twofa_required=æ‚¨å¿…é¡»è®¾ç½®ä¸¤æ­¥éªŒè¯æ¥è®¿é—®ä»“库,或者å°è¯•釿–°ç™»å½•。
login_userpass=登录
login_openid=OpenID
oauth_signup_tab=注册å¸å·
@@ -443,24 +461,24 @@ oauth_signup_submit=完æˆè´¦å·
oauth_signin_tab=绑定到现有å¸å·
oauth_signin_title=登录以授æƒç»‘å®šå¸æˆ·
oauth_signin_submit=绑定账å·
-oauth.signin.error=å¤„ç†æŽˆæƒè¯·æ±‚时出错。 如果此错误ä»ç„¶å­˜â€‹â€‹åœ¨ï¼Œè¯·è”系站点管ç†å‘˜ã€‚
+oauth.signin.error.general=å¤„ç†æŽˆæƒè¯·æ±‚时出错:%s。如果此错误ä»ç„¶å­˜åœ¨ï¼Œè¯·ä¸Žç«™ç‚¹ç®¡ç†å‘˜è”系。
oauth.signin.error.access_denied=授æƒè¯·æ±‚被拒ç»ã€‚
oauth.signin.error.temporarily_unavailable=授æƒå¤±è´¥ï¼Œå› ä¸ºè®¤è¯æœåŠ¡å™¨æš‚æ—¶ä¸å¯ç”¨ã€‚请ç¨åŽå†è¯•。
-oauth_callback_unable_auto_reg=自动注册已å¯ç”¨ï¼Œä½†OAuth2 æä¾›å•† %[1]s 返回缺失的字段:%[2]sï¼Œæ— æ³•è‡ªåŠ¨åˆ›å»ºå¸æˆ·ï¼Œè¯·åˆ›å»ºæˆ–é“¾æŽ¥åˆ°ä¸€ä¸ªå¸æˆ·ï¼Œæˆ–è”系站点管ç†å‘˜ã€‚
+oauth_callback_unable_auto_reg=自动注册已å¯ç”¨ï¼Œä½† OAuth2 æä¾›å•† %[1]s 返回缺失的字段:%[2]sï¼Œæ— æ³•è‡ªåŠ¨åˆ›å»ºå¸æˆ·ï¼Œè¯·åˆ›å»ºæˆ–é“¾æŽ¥åˆ°ä¸€ä¸ªå¸æˆ·ï¼Œæˆ–è”系站点管ç†å‘˜ã€‚
openid_connect_submit=连接
openid_connect_title=è¿žæŽ¥åˆ°çŽ°æœ‰çš„å¸æˆ·
openid_connect_desc=所选的 OpenID URI 未知。在这里关è”ä¸€ä¸ªæ–°å¸æˆ·ã€‚
openid_register_title=åˆ›å»ºæ–°å¸æˆ·
openid_register_desc=所选的 OpenID URI 未知。在这里关è”ä¸€ä¸ªæ–°å¸æˆ·ã€‚
openid_signin_desc=输入您的OpenID地å€ã€‚例如:alice.openid.example.org 或 https://openid.example.org/alice.
-disable_forgot_password_mail=由于未设置电å­é‚®ä»¶ï¼Œå¸æˆ·æ¢å¤è¢«ç¦ç”¨ã€‚ 请è”系您的站点管ç†å‘˜ã€‚
-disable_forgot_password_mail_admin=叿ˆ·æ¢å¤ä»…在设置电å­é‚®ä»¶åŽå¯ç”¨ã€‚ 请设置电å­é‚®ä»¶ä»¥å¯ç”¨å¸æˆ·æ¢å¤ã€‚
-email_domain_blacklisted=您ä¸èƒ½ä½¿ç”¨æ‚¨çš„电å­é‚®ä»¶åœ°å€æ³¨å†Œã€‚
+disable_forgot_password_mail=ç”±äºŽæœªè®¾ç½®é‚®ç®±ï¼Œå¸æˆ·æ¢å¤è¢«ç¦ç”¨ã€‚ 请è”系您的站点管ç†å‘˜ã€‚
+disable_forgot_password_mail_admin=叿ˆ·æ¢å¤ä»…在设置邮箱åŽå¯ç”¨ã€‚ 请设置邮箱以å¯ç”¨å¸æˆ·æ¢å¤ã€‚
+email_domain_blacklisted=您ä¸èƒ½ä½¿ç”¨æ‚¨çš„é‚®ç®±åœ°å€æ³¨å†Œã€‚
authorize_application=应用授æƒ
authorize_redirect_notice=å¦‚æžœæ‚¨æŽˆæƒæ­¤åº”用,您将会被é‡å®šå‘到 %s。
-authorize_application_created_by=此应用由%s创建。
+authorize_application_created_by=此应用由 %s 创建。
authorize_application_description=如果您å…许,它将能够读å–å’Œä¿®æ”¹æ‚¨çš„æ‰€æœ‰å¸æˆ·ä¿¡æ¯ï¼ŒåŒ…括ç§äººä»“库和组织。
-authorize_application_with_scopes=范围: %s
+authorize_application_with_scopes=范围:%s
authorize_title=æŽˆæƒ %s è®¿é—®æ‚¨çš„å¸æˆ·ï¼Ÿ
authorization_failed=授æƒå¤±è´¥
authorization_failed_desc=因为检测到无效请求,授æƒå¤±è´¥ã€‚请å°è¯•è”系您授æƒåº”用的管ç†å‘˜ã€‚
@@ -468,7 +486,7 @@ sspi_auth_failed=SSPI 认è¯å¤±è´¥
password_pwned=此密ç å‡ºçŽ°åœ¨ <a target="_blank" rel="noopener noreferrer" href="%s">被盗密ç </a> 列表上并且曾ç»è¢«å…¬å¼€ã€‚ 请使用å¦ä¸€ä¸ªå¯†ç å†è¯•一次。
password_pwned_err=无法完æˆå¯¹ HaveIBeenPwned 的请求
last_admin=您ä¸èƒ½åˆ é™¤æœ€åŽä¸€ä¸ªç®¡ç†å‘˜ã€‚必须至少ä¿ç•™ä¸€ä¸ªç®¡ç†å‘˜ã€‚
-signin_passkey=使用密钥登录
+signin_passkey=使用通行密钥登录
back_to_sign_in=返回登录页é¢
[mail]
@@ -484,24 +502,24 @@ activate_account.text_2=请在 <b>%s</b> 时间内,点击以下链接激活您
activate_email=è¯·éªŒè¯æ‚¨çš„邮箱地å€
activate_email.title=%sï¼Œè¯·éªŒè¯æ‚¨çš„邮箱
-activate_email.text=请在 <b>%s</b> 时间内,点击以下链接,以验è¯ä½ çš„电å­é‚®ä»¶åœ°å€ï¼š
+activate_email.text=请在 <b>%s</b> æ—¶é—´å†…ï¼Œç‚¹å‡»ä»¥ä¸‹é“¾æŽ¥ï¼Œä»¥éªŒè¯æ‚¨çš„邮箱地å€ï¼š
register_notify=欢迎æ¥åˆ° %s
register_notify.title=%[1]s,欢迎æ¥åˆ° %[2]s
-register_notify.text_1=这是您的 %s 注册确认电å­é‚®ä»¶ ï¼
+register_notify.text_1=这是您的 %s 注册确认邮件 ï¼
register_notify.text_2=您现在å¯ä»¥ä»¥ç”¨æˆ·å %s 登录。
register_notify.text_3=如果此账户已为您创建,请先 <a href="%s">设置您的密ç </a>。
reset_password=æ¢å¤æ‚¨çš„账户
reset_password.title=%s,您已请求æ¢å¤æ‚¨çš„叿ˆ·
-reset_password.text=请在 <b>%s</b> 时间内,点击以下链接,æ¢å¤ä½ çš„账户:
+reset_password.text=请在 <b>%s</b> 时间内,点击以下链接,以æ¢å¤æ‚¨çš„账户:
register_success=注册æˆåŠŸ
issue_assigned.pull=@%[1]s 已将仓库 %[3]s 中的åˆå¹¶è¯·æ±‚ %[2]s 指派给您
issue_assigned.issue=@%[1]s 已将仓库 %[3]s ä¸­çš„å·¥å• %[2]s 指派给您
-issue.x_mentioned_you=<b>@%s</b> æåˆ°äº†æ‚¨ï¼š
+issue.x_mentioned_you=<b>@%s</b> æåŠäº†æ‚¨ï¼š
issue.action.force_push=<b>%[1]s</b> 强制从 %[3]s æŽ¨é€ <b>%[2]s</b> 至 [4]s。
issue.action.push_1=<b>@%[1]s</b> 推é€äº† %[3]d 个æäº¤åˆ° %[2]s
issue.action.push_n=<b>@%[1]s</b> 推é€äº† %[3]d 个æäº¤åˆ° %[2]s
@@ -516,26 +534,26 @@ issue.action.ready_for_review=<b>@%[1]s</b> 标记此åˆå¹¶è¯·æ±‚已评审通过
issue.action.new=<b>@%[1]s</b> 创建了 #%[2]d.
issue.in_tree_path=在 %s 中:
-release.new.subject=%[2]s 中的 %[1]s å‘布了
+release.new.subject=%[2]s 中的 %[1]s å·²å‘布
release.new.text=<b>@%[1]s</b> 于 %[3]s å‘布了 %[2]s
-release.title=标题: %s
+release.title=标题:%s
release.note=注释:
release.downloads=下载:
-release.download.zip=æºä»£ç  (ZIP)
-release.download.targz=æºä»£ç  (TAR.GZ)
+release.download.zip=æºä»£ç ï¼ˆZIP)
+release.download.targz=æºä»£ç ï¼ˆTAR.GZ)
-repo.transfer.subject_to=%s 想è¦å°† "%s" 转让给 %s
-repo.transfer.subject_to_you=%s 想è¦å°† "%s" 转让给你
-repo.transfer.to_you=ä½ 
+repo.transfer.subject_to=%s 想è¦å°†ã€Œ%sã€è½¬ç§»ç»™ %s
+repo.transfer.subject_to_you=%s 想è¦å°†ã€Œ%sã€è½¬ç§»ç»™æ‚¨
+repo.transfer.to_you=您
repo.transfer.body=访问 %s ä»¥æŽ¥å—æˆ–æ‹’ç»è½¬ç§»ï¼Œäº¦å¯å¿½ç•¥æ­¤é‚®ä»¶ã€‚
-repo.collaborator.added.subject=%s 把你添加到了 %s
-repo.collaborator.added.text=您已被添加为代ç åº“çš„å作者:
+repo.collaborator.added.subject=%s 把您添加到了 %s
+repo.collaborator.added.text=您已被添加为仓库的å作者:
team_invite.subject=%[1]s 邀请您加入组织 %[2]s
team_invite.text_1=%[1]s 邀请您加入组织 %[3]s 中的团队 %[2]s。
team_invite.text_2=请点击下é¢çš„链接加入团队:
-team_invite.text_3=注æ„:这是å‘é€ç»™ %[1]s 的邀请。如果您未曾收到过此类邀请,请忽略这å°ç”µå­é‚®ä»¶ã€‚
+team_invite.text_3=注æ„:此邀请是å‘é€ç»™ %[1]s 的。如果您未预期收到此邀请,请忽略这å°é‚®ä»¶ã€‚
[modal]
yes=确认æ“作
@@ -575,9 +593,9 @@ size_error=长度必须为 %s。
min_size_error=长度最å°ä¸º %s 个字符。
max_size_error=长度最大为 %s 个字符。
email_error=䏿˜¯ä¸€ä¸ªæœ‰æ•ˆçš„邮箱地å€ã€‚
-url_error=`'%s' 䏿˜¯ä¸€ä¸ªæœ‰æ•ˆçš„ URL。`
-include_error=`必须包å«å­å­—符串 "%s"。`
-glob_pattern_error=`åŒ¹é…æ¨¡å¼æ— æ•ˆï¼š%s.`
+url_error=`「%sã€ä¸æ˜¯ä¸€ä¸ªæœ‰æ•ˆçš„ URL。`
+include_error=`必须包å«å­å­—符串「%sã€ã€‚`
+glob_pattern_error=`匹é…è¡¨è¾¾å¼æ— æ•ˆï¼š%s.`
regex_pattern_error=`æ­£åˆ™è¡¨è¾¾å¼æ— æ•ˆï¼š%s.`
username_error=` åªèƒ½åŒ…å«å­—æ¯æ•°å­—字符('0-9','a-z','A-Z'), ç ´æŠ˜å· ('-'), 下划线 ('_') 和点 ('.'). ä¸èƒ½ä»¥éžå­—æ¯æ•°å­—字符开头或结尾,并且ä¸å…许连续的éžå­—æ¯æ•°å­—字符。`
invalid_group_team_map_error=`映射无效: %s`
@@ -586,26 +604,26 @@ captcha_incorrect=验è¯ç ä¸æ­£ç¡®ã€‚
password_not_match=密ç ä¸åŒ¹é…。
lang_select_error=从列表中选出语言
-username_been_taken=用户å已被使用。
+username_been_taken=用户å已使用。
username_change_not_local_user=éžæœ¬åœ°ç”¨æˆ·ä¸å…许更改用户å。
-change_username_disabled=更改用户å已被ç¦ç”¨ã€‚
+change_username_disabled=更改用户åå·²ç¦ç”¨ã€‚
change_full_name_disabled=更改用户全åå·²ç¦ç”¨
username_has_not_been_changed=ç”¨æˆ·åæœªæ›´æ”¹
-repo_name_been_taken=仓库å称已被使用。
-repository_force_private=â€œå¼ºåˆ¶ç§æœ‰â€å·²å¯ç”¨ï¼šç§æœ‰ä»“库ä¸èƒ½è¢«å…¬å¼€ã€‚
+repo_name_been_taken=仓库å称已使用。
+repository_force_private=ã€Œå¼ºåˆ¶ç§æœ‰ã€å·²å¯ç”¨ï¼šç§æœ‰ä»“库ä¸èƒ½è¢«å…¬å¼€ã€‚
repository_files_already_exist=此仓库已存在文件。请è”系系统管ç†å‘˜ã€‚
repository_files_already_exist.adopt=此仓库已存在文件,åªèƒ½è¢«æ”¶å½•。
repository_files_already_exist.delete=此仓库已存在文件,必须先删除他们。
repository_files_already_exist.adopt_or_delete=此仓库已存在文件,è¦ä¹ˆåˆ é™¤ä»–们,è¦ä¹ˆæ”¶å½•他们。
visit_rate_limit=远程访问达到速度é™åˆ¶ã€‚
2fa_auth_required=远程访问需è¦åŒé‡éªŒè¯ã€‚
-org_name_been_taken=组织å称已被使用。
-team_name_been_taken=团队å称已被使用。
+org_name_been_taken=组织å称已使用。
+team_name_been_taken=团队å称已使用。
team_no_units_error=至少选择一项仓库å•元。
-email_been_used=该电å­é‚®ä»¶åœ°å€å·²åœ¨ä½¿ç”¨ä¸­ã€‚
+email_been_used=该邮箱地å€å·²åœ¨ä½¿ç”¨ä¸­ã€‚
email_invalid=æ­¤é‚®ç®±åœ°å€æ— æ•ˆã€‚
email_domain_is_not_allowed=用户 <b>%s</b> 与EMAIL_DOMAIN_ALLOWLIT 或 EMAIL_DOMAIN_BLOCKLIT 冲çªã€‚è¯·ç¡®ä¿æ‚¨çš„æ“ä½œæ˜¯é¢„æœŸçš„ã€‚
-openid_been_used=OpenID åœ°å€ "%s" 已被使用。
+openid_been_used=OpenID 地å€ã€Œ%sã€å·²è¢«ä½¿ç”¨ã€‚
username_password_incorrect=ç”¨æˆ·åæˆ–密ç ä¸æ­£ç¡®ã€‚
password_complexity=å¯†ç æœªè¾¾åˆ°å¤æ‚ç¨‹åº¦è¦æ±‚:
password_lowercase_one=至少一个å°å†™å­—符
@@ -620,14 +638,14 @@ unset_password=登录用户没有设置密ç ã€‚
unsupported_login_type=æ­¤ç™»å½•ç±»åž‹ä¸æ”¯æŒæ‰‹åŠ¨åˆ é™¤å¸æˆ·ã€‚
user_not_exist=该用户ä¸å­˜åœ¨
team_not_exist=团队ä¸å­˜åœ¨
-last_org_owner=您ä¸èƒ½ä»Ž "所有者" 团队中删除最åŽä¸€ä¸ªç”¨æˆ·ã€‚组织中必须至少有一个所有者。
-cannot_add_org_to_team=组织ä¸èƒ½è¢«åŠ å…¥åˆ°å›¢é˜Ÿä¸­ã€‚
+last_org_owner=您ä¸èƒ½ä»Žã€Œæ‰€æœ‰è€…ã€å›¢é˜Ÿä¸­åˆ é™¤æœ€åŽä¸€ä¸ªç”¨æˆ·ã€‚组织中必须至少有一个所有者。
+cannot_add_org_to_team=组织ä¸èƒ½åŠ å…¥åˆ°å›¢é˜Ÿä¸­ã€‚
duplicate_invite_to_team=此用户已被邀请为团队æˆå‘˜ã€‚
organization_leave_success=您已æˆåŠŸç¦»å¼€ç»„ç»‡ %s。
-invalid_ssh_key=æ— æ³•éªŒè¯æ‚¨çš„ SSH 密钥: %s
-invalid_gpg_key=æ— æ³•éªŒè¯æ‚¨çš„ GPG 密钥: %s
-invalid_ssh_principal=无效的规则: %s
+invalid_ssh_key=æ— æ³•éªŒè¯æ‚¨çš„ SSH 密钥:%s
+invalid_gpg_key=æ— æ³•éªŒè¯æ‚¨çš„ GPG 密钥:%s
+invalid_ssh_principal=无效的规则:%s
must_use_public_key=您æä¾›çš„密钥是ç§é’¥ã€‚ä¸è¦åœ¨ä»»ä½•地方上传您的ç§é’¥ï¼Œè¯·æ”¹ç”¨æ‚¨çš„公钥。
unable_verify_ssh_key=æ— æ³•éªŒè¯ SSH å¯†é’¥ï¼Œè¯·ä»”ç»†æ£€æŸ¥æ˜¯å¦æœ‰é”™è¯¯ã€‚
auth_failed=授æƒéªŒè¯å¤±è´¥ï¼š%v
@@ -660,27 +678,27 @@ follow=关注
unfollow=å–æ¶ˆå…³æ³¨
user_bio=简历
disabled_public_activity=该用户已éšè—活动记录。
-email_visibility.limited=所有已认è¯ç”¨æˆ·å‡å¯çœ‹åˆ°æ‚¨çš„电å­é‚®ä»¶åœ°å€
-email_visibility.private=åªæœ‰ä½ æœ¬äººå’Œç®¡ç†å‘˜å¯ä»¥çœ‹åˆ°ä½ çš„电å­é‚®ä»¶åœ°å€
+email_visibility.limited=所有已认è¯ç”¨æˆ·å‡å¯çœ‹åˆ°æ‚¨çš„邮箱地å€
+email_visibility.private=åªæœ‰æ‚¨æœ¬äººå’Œç®¡ç†å‘˜å¯ä»¥çœ‹åˆ°æ‚¨çš„邮箱地å€
show_on_map=在地图上显示这个ä½ç½®
settings=用户设置
-form.name_reserved=用户å "%s" 被ä¿ç•™ã€‚
-form.name_pattern_not_allowed=用户å中ä¸å…许使用 "%s" æ ¼å¼ã€‚
-form.name_chars_not_allowed=用户å "%s" åŒ…å«æ— æ•ˆå­—符。
+form.name_reserved=用户å「%sã€è¢«ä¿ç•™ã€‚
+form.name_pattern_not_allowed=用户å中ä¸å…许使用「%sã€æ ¼å¼ã€‚
+form.name_chars_not_allowed=用户å「%sã€åŒ…嫿— æ•ˆå­—符。
block.block=å±è”½
block.block.user=å±è”½ç”¨æˆ·
block.block.org=å±è”½ç”¨æˆ·è®¿é—®ç»„织
-block.block.failure=å±è”½ç”¨æˆ·å¤±è´¥: %s
+block.block.failure=å±è”½ç”¨æˆ·å¤±è´¥ï¼š%s
block.unblock=å–æ¶ˆå±è”½
-block.unblock.failure=å±è”½ç”¨æˆ·å¤±è´¥: %s
+block.unblock.failure=å–æ¶ˆå±è”½ç”¨æˆ·å¤±è´¥ï¼š%s
block.blocked=您已å±è”½æ­¤ç”¨æˆ·ã€‚
block.title=å±è”½ä¸€ä¸ªç”¨æˆ·
block.info=å±è”½ç”¨æˆ·ä¼šé˜»æ­¢ä»–们与仓库进行交互,例如打开或评论åˆå¹¶è¯·æ±‚或出现问题。了解更多关于å±è”½ç”¨æˆ·çš„ä¿¡æ¯ã€‚
block.info_1=é˜»æ­¢ç”¨æˆ·åœ¨æ‚¨çš„å¸æˆ·å’Œä»“库中进行以下æ“作:
-block.info_2=关注你的账å·
-block.info_3=通过@æåŠæ‚¨çš„用户å呿‚¨å‘é€é€šçŸ¥
+block.info_2=关注您的账å·
+block.info_3=通过 @ æåŠæ‚¨çš„用户å呿‚¨å‘é€é€šçŸ¥
block.info_4=邀请您作为å作者到他们的仓库中
block.info_5=åœ¨ä»“åº“ä¸­ç‚¹èµžã€æ´¾ç”Ÿæˆ–关注
block.info_6=æ‰“å¼€å’Œè¯„è®ºå·¥å•æˆ–åˆå¹¶è¯·æ±‚
@@ -713,18 +731,18 @@ uid=UID
webauthn=两步验è¯ï¼ˆå®‰å…¨å¯†é’¥ï¼‰
public_profile=公开信æ¯
-biography_placeholder=å‘Šè¯‰æˆ‘ä»¬ä¸€ç‚¹æ‚¨è‡ªå·±ï¼ (您å¯ä»¥ä½¿ç”¨Markdown)
-location_placeholder=与他人分享你的大概ä½ç½®
-profile_desc=控制您的个人资料对其他用户的显示方å¼ã€‚您的主è¦ç”µå­é‚®ä»¶åœ°å€å°†ç”¨äºŽé€šçŸ¥ã€å¯†ç æ¢å¤å’ŒåŸºäºŽç½‘页界é¢çš„ Git æ“作
-password_username_disabled=ä¸å…è®¸éžæœ¬åœ°ç”¨æˆ·æ›´æ”¹ä»–们的用户å。更多详情请è”系您的系统管ç†å‘˜ã€‚
-password_full_name_disabled=æ‚¨æ— æƒæ›´æ”¹ä»–们的全å。请è”系您的站点管ç†å‘˜äº†è§£æ›´å¤šè¯¦æƒ…。
+biography_placeholder=å‘Šè¯‰æˆ‘ä»¬ä¸€ç‚¹æ‚¨è‡ªå·±ï¼ (您å¯ä»¥ä½¿ç”¨ Markdown)
+location_placeholder=与他人分享您的大概ä½ç½®
+profile_desc=控制您的个人资料对其他用户的显示方å¼ã€‚您的主邮箱地å€å°†ç”¨äºŽé€šçŸ¥ã€å¯†ç æ¢å¤å’ŒåŸºäºŽç½‘页的 Git æ“作。
+password_username_disabled=您ä¸è¢«å…许更改您的用户å。更多详情请è”系您的系统管ç†å‘˜ã€‚
+password_full_name_disabled=您ä¸è¢«å…许更改您的全å。请è”系您的站点管ç†å‘˜äº†è§£æ›´å¤šè¯¦æƒ…。
full_name=自定义åç§°
website=个人网站
location=所在地区
update_theme=更新主题
update_profile=æ›´æ–°ä¿¡æ¯
update_language=更新语言
-update_language_not_found=语言 %s ä¸å¯ç”¨ã€‚
+update_language_not_found=语言「%sã€ä¸å¯ç”¨ã€‚
update_language_success=语言已更新。
update_profile_success=您的资料信æ¯å·²ç»æ›´æ–°
change_username=您的用户å已更改。
@@ -735,7 +753,7 @@ cancel=å–æ¶ˆæ“作
language=界é¢è¯­è¨€
ui=主题
hidden_comment_types=éšè—的评论类型
-hidden_comment_types_description=此处选中的注释类型ä¸ä¼šæ˜¾ç¤ºåœ¨é—®é¢˜é¡µé¢ä¸­ã€‚æ¯”å¦‚ï¼Œå‹¾é€‰â€æ ‡ç­¾â€œåˆ é™¤æ‰€æœ‰ "<user> 添加/删除的 <label>" 注释。
+hidden_comment_types_description=此处选中的注释类型ä¸ä¼šæ˜¾ç¤ºåœ¨å·¥å•页é¢ä¸­ã€‚比如,勾选「标记ã€åˆ é™¤æ‰€æœ‰ã€Œ{user} 添加/删除的 {label}ã€æ³¨é‡Šã€‚
hidden_comment_types.ref_tooltip=注释此问题在何处被æåŠè¿‡ï¼Œå¦‚å¦ä¸€ä¸ªé—®é¢˜ã€ä»£ç æäº¤ç­‰
hidden_comment_types.issue_ref_tooltip=注释用户在何处更改了与此问题相关è”的分支/标签
comment_type_group_reference=引用
@@ -757,7 +775,7 @@ privacy=éšç§è®¾ç½®
keep_activity_private=éšè—个人资料页é¢ä¸­çš„æ´»åЍ
keep_activity_private_popup=使活动仅对您和管ç†å‘˜å¯è§
-lookup_avatar_by_mail=从电å­é‚®ç®±åœ°å€æŸ¥æ‰¾å¤´åƒ
+lookup_avatar_by_mail=ä»Žé‚®ç®±åœ°å€æŸ¥æ‰¾å¤´åƒ
federated_avatar_lookup=Federated Avatar 查找
enable_custom_avatar=å¯åŠ¨è‡ªå®šä¹‰å¤´åƒ
choose_new_avatar=选择新的头åƒ
@@ -781,61 +799,61 @@ emails=邮箱地å€
manage_emails=管ç†é‚®ç®±åœ°å€
manage_themes=选择默认主题
manage_openid=ç®¡ç† OpenID 地å€
-email_desc=您的主è¦ç”µå­é‚®ä»¶åœ°å€å°†ç”¨äºŽé€šçŸ¥ã€å¯†ç æ¢å¤ï¼ŒåŸºäºŽç½‘页界é¢çš„Gitæ“作(åªè¦å®ƒä¸æ˜¯è®¾ç½®ä¸ºéšè—çš„)。
+email_desc=您的主邮箱地å€å°†ç”¨äºŽé€šçŸ¥ã€å¯†ç æ¢å¤ä»¥åŠåŸºäºŽç½‘页的 Git æ“作(如果它未设为éšè—)。
theme_desc=这将是您在整个网站上的默认主题。
theme_colorblindness_help=颜色障ç¢ä¸»é¢˜æ”¯æŒ
-theme_colorblindness_prompt=Gitea åªèƒ½èŽ·å¾—ä¸€äº›åŸºæœ¬çš„é¢œè‰²éšœç¢æ”¯æŒï¼Œè¿™äº›ä¸»é¢˜åªå®šä¹‰äº†å°‘数颜色。 这项工作ä»åœ¨è¿›è¡Œä¸­ï¼Œå¯ä»¥é€šè¿‡åœ¨ä¸»é¢˜çš„ CSS 文件中定义更多颜色æ¥åšæ›´å¤šçš„æ”¹è¿›ã€‚
+theme_colorblindness_prompt=Gitea åªèƒ½èŽ·å¾—ä¸€äº›åŸºæœ¬çš„é¢œè‰²éšœç¢æ”¯æŒï¼Œè¿™äº›ä¸»é¢˜åªå®šä¹‰äº†å°‘数颜色。这项工作ä»åœ¨è¿›è¡Œä¸­ï¼Œå¯ä»¥é€šè¿‡åœ¨ä¸»é¢˜çš„ CSS 文件中定义更多颜色æ¥åšæ›´å¤šçš„æ”¹è¿›ã€‚
primary=主è¦
activated=已激活
requires_activation=éœ€è¦æ¿€æ´»
-primary_email=设为主è¦é‚®ä»¶åœ°å€
+primary_email=设为主邮箱
activate_email=å‘逿¿€æ´»é‚®ä»¶
activations_pending=等待激活
-can_not_add_email_activations_pending=有一个待处ç†çš„æ¿€æ´»è¯·æ±‚,请ç¨ç­‰å‡ åˆ†é’ŸåŽå†å°è¯•添加新的电å­é‚®ä»¶åœ°å€ã€‚
+can_not_add_email_activations_pending=有一个待处ç†çš„æ¿€æ´»è¯·æ±‚,请ç¨ç­‰å‡ åˆ†é’ŸåŽå†å°è¯•添加新的邮箱地å€ã€‚
delete_email=移除
-email_deletion=移除电å­é‚®ä»¶åœ°å€
-email_deletion_desc=电å­é‚®ç®±åœ°å€å’Œç›¸å…³ä¿¡æ¯å°†ä¼šè¢«åˆ é™¤ã€‚使用此电å­é‚®ç®±åœ°å€å‘é€çš„Gitæäº¤å°†ä¼šä¿ç•™ï¼Œç»§ç»­ï¼Ÿ
-email_deletion_success=您的电å­é‚®ç®±åœ°å€å·²è¢«ç§»é™¤ã€‚
+email_deletion=移除邮箱地å€
+email_deletion_desc=邮箱地å€å’Œç›¸å…³ä¿¡æ¯å°†ä¼šè¢«åˆ é™¤ã€‚使用此邮箱地å€å‘é€çš„Gitæäº¤å°†ä¼šä¿ç•™ï¼Œç»§ç»­ï¼Ÿ
+email_deletion_success=您的邮箱地å€å·²ç§»é™¤ã€‚
theme_update_success=您的主题已更新。
theme_update_error=所选主题ä¸å­˜åœ¨ã€‚
openid_deletion=移除 OpenID 地å€
-openid_deletion_desc=删除此 OpenID 地å€å°†ä¼šé˜»æ­¢ä½ ä½¿ç”¨å®ƒè¿›è¡Œç™»å½•。你确定è¦ç»§ç»­å—?
-openid_deletion_success=OpenID地å€å·²è¢«ç§»é™¤ã€‚
+openid_deletion_desc=删除此 OpenID 地å€å°†ä¼šé˜»æ­¢æ‚¨ä½¿ç”¨å®ƒè¿›è¡Œç™»å½•。您确定è¦ç»§ç»­å—?
+openid_deletion_success=OpenID 地å€å·²ç§»é™¤ã€‚
add_new_email=添加新的邮箱地å€
add_new_openid=添加新的 OpenID URI
-add_email=增加电å­é‚®ä»¶åœ°å€
+add_email=新增邮箱地å€
add_openid=添加 OpenID URI
-add_email_confirmation_sent=一å°ç¡®è®¤é‚®ä»¶å·²ç»è¢«å‘é€è‡³ %s,请检查您的收件箱并在 %s 内完æˆç¡®è®¤æ³¨å†Œæ“作。
-add_email_success=新的电å­é‚®ä»¶åœ°å€å·²æ·»åŠ ã€‚
-email_preference_set_success=电å­é‚®ä»¶é¦–选项已æˆåŠŸè®¾ç½®ã€‚
+add_email_confirmation_sent=一å°ç¡®è®¤é‚®ä»¶å·²ç»å‘é€è‡³ã€Œ%sã€ï¼Œè¯·æ£€æŸ¥æ‚¨çš„æ”¶ä»¶ç®±å¹¶åœ¨ %s 内完æˆç¡®è®¤æ³¨å†Œæ“作。
+add_email_success=新邮箱地å€å·²æ·»åŠ ã€‚
+email_preference_set_success=邮件首选项已æˆåŠŸè®¾ç½®ã€‚
add_openid_success=æ–°çš„ OpenID 地å€å·²æ·»åŠ ã€‚
-keep_email_private=éšè—电å­é‚®ä»¶åœ°å€
-keep_email_private_popup=这将会éšè—您的电å­é‚®ä»¶åœ°å€ï¼Œä¸ä»…在您的个人资料中,还在您使用Web界é¢åˆ›å»ºåˆå¹¶è¯·æ±‚或编辑文件时。已推é€çš„æäº¤å°†ä¸ä¼šè¢«ä¿®æ”¹ã€‚在æäº¤ä¸­ä½¿ç”¨ %s 以和您的账å·å…³è”。
-openid_desc=OpenID 让你å¯ä»¥å°†è®¤è¯è½¬å‘到外部æœåŠ¡ã€‚
+keep_email_private=éšè—邮箱地å€
+keep_email_private_popup=这将会éšè—您的邮箱地å€ï¼Œä¸ä»…在您的个人资料中,还在您使用 Web 界é¢åˆ›å»ºåˆå¹¶è¯·æ±‚或编辑文件时。已推é€çš„æäº¤å°†ä¸ä¼šè¢«ä¿®æ”¹ã€‚在æäº¤ä¸­ä½¿ç”¨ %s 以和您的账å·å…³è”。
+openid_desc=OpenID 让您å¯ä»¥å°†è®¤è¯è½¬å‘到外部æœåŠ¡ã€‚
manage_ssh_keys=ç®¡ç† SSH 密钥
-manage_ssh_principals=管ç†SSHè¯ä¹¦è§„则
+manage_ssh_principals=ç®¡ç† SSH è¯ä¹¦è§„则
manage_gpg_keys=ç®¡ç† GPG 密钥
add_key=增加密钥
-ssh_desc=这些 SSH 公钥已ç»å…³è”到你的账å·ã€‚相应的ç§é’¥æ‹¥æœ‰å®Œå…¨æ“作你的仓库的æƒé™ã€‚
-principal_desc=这些SSHè¯ä¹¦è§„则已关è”到你的账å·å°†å…许完全访问你的所有仓库。
-gpg_desc=这些 GPG 公钥已ç»å…³è”到你的账å·ã€‚请妥善ä¿ç®¡ä½ çš„ç§é’¥å› ä¸ºä»–ä»¬å°†è¢«ç”¨äºŽè®¤è¯æäº¤ã€‚
+ssh_desc=这些 SSH 公钥已ç»å…³è”到您的账å·ã€‚相应的ç§é’¥æ‹¥æœ‰å®Œå…¨æ“作您仓库的æƒé™ã€‚
+principal_desc=这些 SSH è¯ä¹¦è§„则已关è”到您的账å·å°†å…许完全访问您所有仓库。
+gpg_desc=这些 GPG 公钥已ç»å…³è”到您的账å·ã€‚请妥善ä¿ç®¡æ‚¨çš„ç§é’¥å› ä¸ºä»–ä»¬å°†è¢«ç”¨äºŽè®¤è¯æäº¤ã€‚
ssh_helper=<strong>需è¦å¸®åŠ©ï¼Ÿ</strong> 请查看有关 <a href="%s">å¦‚ä½•ç”Ÿæˆ SSH 密钥</a> 或 <a href="%s">å¸¸è§ SSH 问题</a> 寻找答案。
-gpg_helper=<strong>需è¦å¸®åŠ©å—?</strong>看一看 GitHub <a href="%s">关于GPG</a> 的指导。
+gpg_helper=<strong>需è¦å¸®åŠ©ï¼Ÿ</strong>看一看 GitHub <a href="%s">关于 GPG</a> 的指导。
add_new_key=增加 SSH 密钥
add_new_gpg_key=添加的 GPG 密钥
key_content_ssh_placeholder=以 'ssh-ed25519'〠'ssh-rsa'〠'ecdsa-sha2-nistp256'ã€'ecdsa-sha2-nistp384'ã€'ecdsa-sha2-nistp521'〠'sk-ecdsa-sha2-nistp256@openssh.com' 或 'sk-ssh-ed25519@openssh.com' 开头
key_content_gpg_placeholder=以 '-----BEGIN PGP PUBLIC KEY BLOCK-----' 开头
add_new_principal=添加规则
ssh_key_been_used=æ­¤ SSH 密钥已添加到æœåŠ¡å™¨ã€‚
-ssh_key_name_used=使用相åŒåç§°çš„SSH公钥已ç»å­˜åœ¨ï¼
+ssh_key_name_used=使用相åŒåç§°çš„ SSH 公钥已ç»å­˜åœ¨ã€‚
ssh_principal_been_used=此规则已ç»åŠ å…¥åˆ°äº†æœåŠ¡å™¨ã€‚
-gpg_key_id_used=使用相åŒåç§°çš„GPG公钥已ç»å­˜åœ¨ï¼
-gpg_no_key_email_found=æ­¤ GPG å¯†é’¥ä¸Žæ‚¨å¸æˆ·å…³è”的任何已激活电å­é‚®ä»¶åœ°å€å‡ä¸åŒ¹é…。如果您在æä¾›çš„令牌上签å,它ä»ç„¶å¯ä»¥è¢«æ·»åŠ ã€‚
+gpg_key_id_used=使用相åŒåç§°çš„ GPG 公钥已ç»å­˜åœ¨ã€‚
+gpg_no_key_email_found=æ­¤ GPG å¯†é’¥ä¸Žæ‚¨å¸æˆ·å…³è”的任何已激活邮箱地å€å‡ä¸åŒ¹é…。如果您已对æä¾›çš„令牌进行签å,ä»å¯æ·»åŠ è¯¥å¯†é’¥ã€‚
gpg_key_matched_identities=匹é…的身份:
-gpg_key_matched_identities_long=此密钥中包å«çš„身份信æ¯ä¸Žä¸‹é¢è¿™ä¸ªè¯¥ç”¨æˆ·å·²æ¿€æ´»ç”µå­é‚®ä»¶åœ°å€æ˜¯ç›¸åŒ¹é…的。因此,能与这些电å­é‚®ä»¶åœ°å€ç›¸åŒ¹é…çš„æäº¤å¯ä»¥é€šè¿‡æ­¤å¯†é’¥è¿›è¡ŒéªŒè¯ã€‚
+gpg_key_matched_identities_long=此密钥中包å«çš„身份信æ¯ä¸Žä»¥ä¸‹æ­¤ç”¨æˆ·å·²æ¿€æ´»é‚®ç®±åœ°å€åŒ¹é…。与这些邮箱地å€ç›¸åŒ¹é…çš„æäº¤å¯é€šè¿‡æ­¤å¯†é’¥è¿›è¡ŒéªŒè¯ã€‚
gpg_key_verified=已验è¯çš„密钥
-gpg_key_verified_long=密钥已ç»ç”¨ä»¤ç‰Œè¿›è¡Œäº†éªŒè¯ï¼Œå¹¶ä¸”å¯ä»¥ç”¨æ¥éªŒè¯åŒ¹é…此用户任何已激活电å­é‚®ä»¶åœ°å€çš„æäº¤ï¼Œä»¥åŠåŒ¹é…此密钥的任何身份。
+gpg_key_verified_long=密钥已通过令牌验è¯ï¼Œé™¤ä¸Žæ­¤å¯†é’¥åŒ¹é…的任何身份外,还å¯ç”¨äºŽéªŒè¯ä¸Žè¯¥ç”¨æˆ·ä»»ä½•已激活邮箱地å€åŒ¹é…çš„æäº¤ã€‚
gpg_key_verify=验è¯
gpg_invalid_token_signature=æä¾›çš„ GPG 密钥ã€ç­¾å和令牌ä¸åŒ¹é…或过期。
gpg_token_required=您必须为下é¢çš„令牌æä¾›ç­¾å
@@ -843,9 +861,9 @@ gpg_token=令牌
gpg_token_help=您å¯ä»¥ä½¿ç”¨ä»¥ä¸‹æ–¹å¼ç”Ÿæˆç­¾å:
gpg_token_signature=GPG 增强签å
key_signature_gpg_placeholder=以 '-----BEGIN PGP PUBLIC KEY BLOCK-----' 开头
-verify_gpg_key_success=GPG 密钥 %s 已被验è¯ã€‚
+verify_gpg_key_success=GPG 密钥「%sã€å·²éªŒè¯ã€‚
ssh_key_verified=已验è¯çš„密钥
-ssh_key_verified_long=密钥已ç»ç”¨ä»¤ç‰Œè¿›è¡Œäº†éªŒè¯ï¼Œå¹¶ä¸”å¯ä»¥ç”¨æ¥éªŒè¯åŒ¹é…此用户任何已激活电å­é‚®ä»¶åœ°å€çš„æäº¤ã€‚
+ssh_key_verified_long=密钥已通过令牌验è¯ï¼Œå¯ç”¨äºŽéªŒè¯ä¸Žè¯¥ç”¨æˆ·ä»»ä½•已激活邮箱地å€åŒ¹é…çš„æäº¤ã€‚
ssh_key_verify=验è¯
ssh_invalid_token_signature=æä¾›çš„ SSH 密钥ã€ç­¾å或令牌ä¸åŒ¹é…或令牌已过期。
ssh_token_required=您必须为下é¢çš„令牌æä¾›ç­¾å
@@ -853,24 +871,24 @@ ssh_token=令牌
ssh_token_help=您å¯ä»¥ä½¿ç”¨ä»¥ä¸‹æ–¹å¼ç”Ÿæˆç­¾å:
ssh_token_signature=增强 SSH ç­¾å
key_signature_ssh_placeholder=以 '-----BEGIN SSH SIGNATURE -----' 开头
-verify_ssh_key_success=SSH 密钥 %s 已被验è¯ã€‚
+verify_ssh_key_success=SSH 密钥「%sã€å·²éªŒè¯ã€‚
subkeys=å­é¡¹
-key_id=é”®ID
+key_id=密钥 ID
key_name=密钥åç§°
key_content=密钥内容
principal_content=内容
-add_key_success=SSH 密钥 %s 添加æˆåŠŸã€‚
-add_gpg_key_success=GPG 密钥 %s 添加æˆåŠŸã€‚
-add_principal_success=SSHè¯ä¹¦è§„则 %s 添加æˆåŠŸã€‚
+add_key_success=SSH 密钥「%sã€æ·»åŠ æˆåŠŸã€‚
+add_gpg_key_success=GPG 密钥「%sã€æ·»åŠ æˆåŠŸã€‚
+add_principal_success=SSH è¯ä¹¦è§„则「%sã€æ·»åŠ æˆåŠŸã€‚
delete_key=删除
ssh_key_deletion=删除 SSH 密钥
gpg_key_deletion=删除 GPG 密钥
ssh_principal_deletion=删除 SSH è¯ä¹¦è§„则
ssh_key_deletion_desc=删除 SSH 公钥将喿¶ˆå¯¹åº”çš„ç§é’¥å¯¹æ‚¨çš„ Gitea 叿ˆ·çš„访问æƒé™ã€‚继续?
-gpg_key_deletion_desc=删除 GPG 公钥将无法认知使用对应ç§é’¥ç­¾åçš„æäº¤ï¼Œç»§ç»­ï¼Ÿ
+gpg_key_deletion_desc=删除 GPG 公钥将无法认è¯å¯¹åº”ç§é’¥ç­¾åçš„æäº¤ï¼Œç»§ç»­ï¼Ÿ
ssh_principal_deletion_desc=删除此 SSH è¯ä¹¦è§„åˆ™å°†å–æ¶ˆå®ƒå¯¹æ‚¨çš„账户的访问æƒé™ã€‚继续?
-ssh_key_deletion_success=GPG 密钥已被删除。
-gpg_key_deletion_success=GPG 密钥已被删除。
+ssh_key_deletion_success=SSH 密钥已删除。
+gpg_key_deletion_success=GPG 密钥已删除。
ssh_principal_deletion_success=此规则删除æˆåŠŸã€‚
added_on=添加于 %s
valid_until_date=有效期至 %s
@@ -884,7 +902,7 @@ token_state_desc=7 天内使用过该密钥
principal_state_desc=7 天内使用过该规则
show_openid=在个人信æ¯ä¸Šæ˜¾ç¤º
hide_openid=在个人信æ¯ä¸Šéšè—
-ssh_disabled=SSH 被ç¦ç”¨
+ssh_disabled=SSH å·²ç¦ç”¨
ssh_signonly=SSH ç›®å‰å·²ç¦ç”¨ï¼Œå› æ­¤è¿™äº›å¯†é’¥ä»…用于æäº¤ç­¾å验è¯ã€‚
ssh_externally_managed=æ­¤ SSH 密钥是由外部管ç†çš„
manage_social=管ç†å…³è”ç¤¾äº¤å¸æˆ·
@@ -904,17 +922,19 @@ access_token_deletion=删除 Access Token
access_token_deletion_cancel_action=å–æ¶ˆ
access_token_deletion_confirm_action=刪除
access_token_deletion_desc=删除令牌将撤销程åºå¯¹æ‚¨è´¦æˆ·çš„访问æƒé™ã€‚æ­¤æ“作无法撤消。是å¦ç»§ç»­ï¼Ÿ
-delete_token_success=令牌已ç»è¢«åˆ é™¤ã€‚使用该令牌的应用将ä¸å†èƒ½å¤Ÿè®¿é—®ä½ çš„è´¦å·ã€‚
+delete_token_success=令牌已ç»è¢«åˆ é™¤ã€‚使用该令牌的应用将ä¸å†èƒ½å¤Ÿè®¿é—®æ‚¨çš„è´¦å·ã€‚
repo_and_org_access=仓库和组织访问æƒé™
permissions_public_only=仅公开
-permissions_access_all=全部(公开ã€ç§æœ‰å’Œå—é™)
-select_permissions=选择æƒé™
+permissions_access_all=全部(公开ã€ç§æœ‰å’Œå—é™ï¼‰
permission_not_set=未设置
permission_no_access=无访问æƒé™
permission_read=å¯è¯»
permission_write=读写
+permission_anonymous_read=匿å读
+permission_everyone_read=所有人å¯è¯»
+permission_everyone_write=所有人å¯å†™
access_token_desc=所选令牌æƒé™ä»…é™äºŽå¯¹åº”çš„ <a %s>API</a> 路由的授æƒã€‚阅读 <a %s>文档</a> ä»¥èŽ·å–æ›´å¤šä¿¡æ¯ã€‚
-at_least_one_permission=你需è¦é€‰æ‹©è‡³å°‘一个æƒé™æ‰èƒ½åˆ›å»ºä»¤ç‰Œ
+at_least_one_permission=您需è¦é€‰æ‹©è‡³å°‘一个æƒé™æ‰èƒ½åˆ›å»ºä»¤ç‰Œ
permissions_list=æƒé™ï¼š
manage_oauth2_applications=ç®¡ç† OAuth2 应用程åº
@@ -922,13 +942,13 @@ edit_oauth2_application=编辑 OAuth2 应用程åº
oauth2_applications_desc=OAuth2 应用å…许第三方应用程åºåœ¨æ­¤ Gitea 实例中安全验è¯ç”¨æˆ·ã€‚
remove_oauth2_application=删除 OAuth2 应用程åº
remove_oauth2_application_desc=删除 OAuth2 应用将撤销所有签å的访问令牌。继续å—?
-remove_oauth2_application_success=该应用已被删除。
+remove_oauth2_application_success=该应用已删除。
create_oauth2_application=创建新的 OAuth2 应用程åº
create_oauth2_application_button=创建应用
create_oauth2_application_success=您已æˆåŠŸåˆ›å»ºäº†ä¸€ä¸ªæ–°çš„ OAuth2 应用。
update_oauth2_application_success=您已æˆåŠŸæ›´æ–°äº†æ­¤ OAuth2 应用。
oauth2_application_name=应用åç§°
-oauth2_confidential_client=æœºå¯†å®¢æˆ·ç«¯ã€‚æ˜¯å¦æ˜¯èƒ½å¤Ÿç»´æŒå‡­æ®æœºå¯†æ€§çš„应用,比如网页应用程åºã€‚如果是本地应用程åºè¯·ä¸è¦å‹¾é€‰ï¼ŒåŒ…括桌é¢å’Œç§»åŠ¨ç«¯åº”ç”¨ã€‚
+oauth2_confidential_client=机密客户端。对于需è¦ä¿å¯†çš„应用(例如 Web 应用),请选择此选项。对于包括桌é¢å’Œç§»åŠ¨åº”ç”¨åœ¨å†…çš„æœ¬æœºåº”ç”¨ï¼Œè¯·å‹¿é€‰æ‹©æ­¤é€‰é¡¹ã€‚
oauth2_skip_secondary_authorization=首次授æƒåŽå…è®¸å…¬å…±å®¢æˆ·ç«¯è·³è¿‡æŽˆæƒæ­¥éª¤ã€‚ <strong>å¯èƒ½ä¼šå¸¦æ¥å®‰å…¨é£Žé™©ã€‚</strong>
oauth2_redirect_uris=é‡å®šå‘ URI。æ¯è¡Œä¸€ä¸ª URI。
save_application=ä¿å­˜
@@ -940,7 +960,7 @@ oauth2_client_secret_hint=您离开或刷新此页é¢åŽå°†ä¸ä¼šå†æ˜¾ç¤ºæ­¤å¯†
oauth2_application_edit=编辑
oauth2_application_create_description=OAuth2 应用å…许您的第三方应用程åºè®¿é—®æ­¤å®žä¾‹çš„ç”¨æˆ·å¸æˆ·ã€‚
oauth2_application_remove_description=移除一个OAuth2应用将会阻止它访问此实例上的已授æƒç”¨æˆ·è´¦æˆ·ã€‚是å¦ç»§ç»­ï¼Ÿ
-oauth2_application_locked=如果é…ç½®å¯ç”¨ï¼ŒGitea预注册一些OAuth2应用程åºã€‚ 为了防止æ„外的行为, 这些应用既ä¸èƒ½ç¼–辑也ä¸èƒ½åˆ é™¤ã€‚请å‚阅OAuth2æ–‡æ¡£ä»¥èŽ·å–æ›´å¤šä¿¡æ¯ã€‚
+oauth2_application_locked=如果é…ç½®å¯ç”¨ï¼ŒGitea 将在å¯åŠ¨æ—¶é¢„æ³¨å†Œä¸€äº› OAuth2 应用程åºã€‚ 为了防止æ„外的行为, 这些应用既ä¸èƒ½ç¼–辑也ä¸èƒ½åˆ é™¤ã€‚请å‚阅 OAuth2 æ–‡æ¡£ä»¥èŽ·å–æ›´å¤šä¿¡æ¯ã€‚
authorized_oauth2_applications=已授æƒçš„ OAuth2 应用
authorized_oauth2_applications_description=您已授予这些第三方应用程åºè®¿é—®æ‚¨çš„个人 Gitea 账户的æƒé™ã€‚请撤销那些您ä¸å†éœ€è¦çš„应用程åºçš„访问æƒé™ã€‚
@@ -949,10 +969,10 @@ revoke_oauth2_grant=撤回æƒé™
revoke_oauth2_grant_description=确定撤销此三方应用程åºçš„æŽˆæƒï¼Œå¹¶é˜»æ­¢æ­¤åº”用程åºè®¿é—®æ‚¨çš„æ•°æ®ï¼Ÿ
revoke_oauth2_grant_success=æˆåŠŸæ’¤é”€äº†è®¿é—®æƒé™ã€‚
-twofa_desc=ä¸ºä¿æŠ¤ä½ çš„è´¦å·å¯†ç å®‰å…¨ï¼Œä½ å¯ä»¥ä½¿ç”¨æ™ºèƒ½æ‰‹æœºæˆ–å…¶å®ƒè®¾å¤‡æ¥æŽ¥æ”¶æ—¶é—´å¼ºç›¸å…³çš„ä¸€æ¬¡æ€§å¯†ç ï¼ˆTOTP)。
+twofa_desc=ä¸ºä¿æŠ¤æ‚¨çš„è´¦å·å¯†ç å®‰å…¨ï¼Œæ‚¨å¯ä»¥ä½¿ç”¨æ™ºèƒ½æ‰‹æœºæˆ–å…¶å®ƒè®¾å¤‡æ¥æŽ¥æ”¶æ—¶é—´å¼ºç›¸å…³çš„ä¸€æ¬¡æ€§å¯†ç ï¼ˆTOTP)。
twofa_recovery_tip=如果您丢失了您的设备,您将能够使用一次性æ¢å¤å¯†é’¥æ¥é‡æ–°èŽ·å¾—å¯¹æ‚¨è´¦æˆ·çš„è®¿é—®ã€‚
-twofa_is_enrolled=你的账å·<strong>å·²å¯ç”¨</strong>了两步验è¯ã€‚
-twofa_not_enrolled=ä½ çš„è´¦å·æœªå¼€å¯ä¸¤æ­¥éªŒè¯ã€‚
+twofa_is_enrolled=您的账å·<strong>å·²å¯ç”¨</strong>了两步验è¯ã€‚
+twofa_not_enrolled=æ‚¨çš„è´¦å·æœªå¼€å¯ä¸¤æ­¥éªŒè¯ã€‚
twofa_disable=ç¦ç”¨ä¸¤æ­¥è®¤è¯
twofa_scratch_token_regenerate=釿–°ç”Ÿæˆåˆå§‹ä»¤ç‰Œ
twofa_scratch_token_regenerated=您的åˆå§‹ä»¤ç‰ŒçŽ°åœ¨æ˜¯ %s。将其存放在安全的地方,它将ä¸ä¼šå†æ¬¡æ˜¾ç¤ºã€‚
@@ -960,12 +980,12 @@ twofa_enroll=å¯ç”¨ä¸¤æ­¥éªŒè¯
twofa_disable_note=如果需è¦, å¯ä»¥ç¦ç”¨åŒå› ç´ èº«ä»½éªŒè¯ã€‚
twofa_disable_desc=关掉两步验è¯ä¼šä½¿å¾—您的账å·ä¸å®‰å…¨ï¼Œç»§ç»­æ‰§è¡Œï¼Ÿ
regenerate_scratch_token_desc=如果您丢失了您的æ¢å¤å¯†é’¥æˆ–å·²ç»ä½¿ç”¨å®ƒç™»å½•, 您å¯ä»¥åœ¨è¿™é‡Œé‡ç½®å®ƒã€‚
-twofa_disabled=两步验è¯å·²è¢«ç¦ç”¨ã€‚
+twofa_disabled=两步验è¯å·²ç¦ç”¨ã€‚
scan_this_image=使用您的授æƒåº”用扫æè¿™å¼ å›¾ç‰‡ï¼š
or_enter_secret=或者输入密钥:%s
then_enter_passcode=并输入应用程åºä¸­æ˜¾ç¤ºçš„密ç :
passcode_invalid=密ç ä¸æ­£ç¡®ã€‚å†è¯•一次。
-twofa_enrolled=你的账å·å·²ç»å¯ç”¨äº†ä¸¤æ­¥éªŒè¯ã€‚请ä¿å­˜åˆå§‹ä»¤ç‰Œï¼ˆ%s)到一个安全的地方,此令牌仅显示一次。
+twofa_enrolled=您的账å·å·²ç»å¯ç”¨äº†ä¸¤æ­¥éªŒè¯ã€‚请ä¿å­˜åˆå§‹ä»¤ç‰Œï¼ˆ%s)到一个安全的地方,此令牌仅显示一次。
twofa_failed_get_secret=èŽ·å– secret 失败。
webauthn_desc=安全密钥是包å«åŠ å¯†å¯†é’¥çš„ç¡¬ä»¶è®¾å¤‡ã€‚å®ƒä»¬å¯ä»¥ç”¨äºŽåŒå› ç´ èº«ä»½éªŒè¯ã€‚å®‰å…¨å¯†é’¥å¿…é¡»æ”¯æŒ <a rel="noreferrer" target="_blank" href="%s">WebAuthn 身份验è¯å™¨</a> 标准。
@@ -984,23 +1004,23 @@ remove_account_link=删除已绑定的账å·
remove_account_link_desc=åˆ é™¤å·²ç»‘å®šå¸æˆ·å°†åŠé”€å…¶å¯¹æ‚¨çš„ Gitea 叿ˆ·çš„访问æƒé™ã€‚继续?
remove_account_link_success=已喿¶ˆç»‘å®šå¸æˆ·ã€‚
-hooks.desc=添加 Webhooks,它们将会在您拥有的<strong>所有仓库</strong>上触å‘
+hooks.desc=添加 Web é’©å­ï¼Œå®ƒä»¬å°†ä¼šåœ¨æ‚¨æ‹¥æœ‰çš„<strong>所有仓库</strong>上触å‘。
orgs_none=æ‚¨çŽ°åœ¨è¿˜ä¸æ˜¯ä»»ä½•组织的æˆå‘˜ã€‚
-repos_none=你并䏿‹¥æœ‰ä»»ä½•仓库。
+repos_none=æ‚¨å¹¶ä¸æ‹¥æœ‰ä»»ä½•仓库。
delete_account=删除当å‰å¸æˆ·
-delete_prompt=æ­¤æ“ä½œå°†æ°¸ä¹…åˆ é™¤æ‚¨çš„ç”¨æˆ·å¸æˆ·ã€‚它 <strong>ä¸èƒ½</strong> 被撤消。
-delete_with_all_comments=ä½ çš„å¸æˆ·å¹´é¾„å°äºŽ %s。为了é¿å…å¹½çµè¯„论,所有工å•/åˆå¹¶è¯·æ±‚的评论都将与它一起被删除。
+delete_prompt=æ­¤æ“ä½œå°†æ°¸ä¹…åˆ é™¤æ‚¨çš„ç”¨æˆ·å¸æˆ·ã€‚它 <strong>无法</strong> 被撤消。
+delete_with_all_comments=æ‚¨çš„å¸æˆ·å¹´é¾„å°äºŽ %s。为了é¿å…å¹½çµè¯„论,所有工å•/åˆå¹¶è¯·æ±‚的评论都将与它一起被删除。
confirm_delete_account=ç¡®è®¤åˆ é™¤å¸æˆ·
delete_account_title=删除当å‰å¸æˆ·
delete_account_desc=ç¡®å®žè¦æ°¸ä¹…åˆ é™¤æ­¤ç”¨æˆ·å¸æˆ·å—?
email_notifications.enable=å¯ç”¨é‚®ä»¶é€šçŸ¥
-email_notifications.onmention=åªåœ¨è¢«æåˆ°æ—¶é‚®ä»¶é€šçŸ¥
+email_notifications.onmention=仅被æåŠæ—¶é€šçŸ¥
email_notifications.disable=åœç”¨é‚®ä»¶é€šçŸ¥
-email_notifications.submit=邮件通知设置
-email_notifications.andyourown=和您自己的通知
+email_notifications.submit=设置邮件通知
+email_notifications.andyourown=仅与您相关的通知
visibility=用户å¯è§æ€§
visibility.public=公开
@@ -1015,6 +1035,9 @@ new_repo_helper=代ç ä»“库包å«äº†æ‰€æœ‰çš„项目文件,包括版本历å²è
owner=拥有者
owner_helper=由于最大仓库数é‡é™åˆ¶ï¼Œä¸€äº›ç»„织å¯èƒ½ä¸ä¼šæ˜¾ç¤ºåœ¨ä¸‹æ‹‰åˆ—表中。
repo_name=仓库åç§°
+repo_name_profile_public_hint=.profile 是一个特殊的仓库,您å¯ä»¥ä½¿ç”¨å®ƒå°† README.md 添加到您的公共组织资料中,任何人都å¯ä»¥çœ‹åˆ°ã€‚请确ä¿å®ƒæ˜¯å…¬å¼€çš„,并使用个人资料目录中的 README 对其进行åˆå§‹åŒ–以开始使用。
+repo_name_profile_private_hint=.profile-private 是一个特殊的仓库,您å¯ä»¥ä½¿ç”¨å®ƒå‘您的组织æˆå‘˜ä¸ªäººèµ„料添加 README.md,仅对组织æˆå‘˜å¯è§ã€‚请确ä¿å®ƒæ˜¯ç§æœ‰çš„,并使用个人资料目录中的 README 对其进行åˆå§‹åŒ–以开始使用。
+repo_name_helper=ç†æƒ³çš„仓库åç§°åº”ç”±ç®€çŸ­ã€æœ‰æ„义和独特的关键è¯ç»„æˆã€‚「.profileã€å’Œã€Œ.profile-privateã€å¯ç”¨äºŽä¸ºç”¨æˆ·/组织添加 README.md。
repo_size=仓库大å°
template=模æ¿
template_select=选择模æ¿
@@ -1028,10 +1051,10 @@ visibility_fork_helper=(修改该值将会影å“到所有派生仓库)
clone_helper=ä¸çŸ¥é“如何克隆?查看<a target="_blank" rel="noopener noreferrer" href="%s">帮助</a> 。
fork_repo=派生仓库
fork_from=派生自
-already_forked=ä½ å·²ç»æ´¾ç”Ÿè¿‡ %s
+already_forked=æ‚¨å·²ç»æ´¾ç”Ÿè¿‡ %s
fork_to_different_account=派生到其他账å·
fork_visibility_helper=无法更改派生仓库的å¯è§æ€§ã€‚
-fork_branch=è¦å…‹éš†åˆ° Fork 的分支
+fork_branch=è¦å…‹éš†ä¸ºæ´¾ç”Ÿçš„分支
all_branches=所有分支
view_all_branches=查看所有分支
view_all_tags=查看所有标签
@@ -1039,30 +1062,31 @@ fork_no_valid_owners=这个代ç ä»“库无法被派生,因为没有有效的æ‰
fork.blocked_user=无法克隆仓库,因为您被仓库所有者å±è”½ã€‚
use_template=使用此模æ¿
open_with_editor=用 %s 打开
+
download_zip=下载 ZIP
download_tar=下载 TAR.GZ
download_bundle=下载 BUNDLE
generate_repo=生æˆä»“库
generate_from=生æˆè‡ª
-repo_desc=仓库æè¿°
+repo_desc=æè¿°
repo_desc_helper=è¾“å…¥ç®€è¦æè¿° (å¯é€‰)
repo_no_desc=无详细信æ¯
-repo_lang=编程语言
-repo_gitignore_helper=选择 .gitignore 模æ¿ã€‚
+repo_lang=语言
+repo_gitignore_helper=选择 .gitignore 模æ¿
repo_gitignore_helper_desc=从常è§è¯­è¨€çš„æ¨¡æ¿åˆ—è¡¨ä¸­é€‰æ‹©å¿½ç•¥è·Ÿè¸ªçš„æ–‡ä»¶ã€‚é»˜è®¤æƒ…å†µä¸‹ï¼Œç”±å¼€å‘æˆ–构建工具生æˆçš„特殊文件都包å«åœ¨ .gitignore 中。
issue_labels=工啿 ‡ç­¾
issue_labels_helper=é€‰æ‹©ä¸€ä¸ªå·¥å•æ ‡ç­¾é›†
license=授æƒè®¸å¯
-license_helper=选择授æƒè®¸å¯æ–‡ä»¶ã€‚
-license_helper_desc=许å¯è¯è¯´æ˜Žäº†å…¶ä»–人å¯ä»¥å’Œä¸å¯ä»¥ç”¨æ‚¨çš„代ç åšä»€ä¹ˆã€‚ä¸ç¡®å®šå“ªä¸€ä¸ªé€‚åˆä½ çš„é¡¹ç›®ï¼Ÿè§ <a target="_blank" rel="noopener noreferrer" href="%s">选择一个许å¯è¯</a>
+license_helper=选择授æƒè®¸å¯æ–‡ä»¶
+license_helper_desc=许å¯è¯è¯´æ˜Žäº†å…¶ä»–人å¯ä»¥å’Œä¸å¯ä»¥ç”¨æ‚¨çš„代ç åšä»€ä¹ˆã€‚ä¸ç¡®å®šå“ªä¸€ä¸ªé€‚åˆæ‚¨çš„é¡¹ç›®ï¼Ÿè§ <a target="_blank" rel="noopener noreferrer" href="%s">选择一个许å¯è¯</a>
multiple_licenses=多许å¯è¯
object_format=对象格å¼
object_format_helper=仓库的对象格å¼ã€‚ä¹‹åŽæ— æ³•更改。SHA1 是最兼容的。
readme=自述
readme_helper=选择自述文件模æ¿ã€‚
readme_helper_desc=这是您å¯ä»¥ä¸ºæ‚¨çš„项目撰写完整æè¿°çš„地方。
-auto_init=åˆå§‹åŒ–仓库(添加. gitignoreã€è®¸å¯è¯å’Œè‡ªè¿°æ–‡ä»¶)
-trust_model_helper=选择签å验è¯çš„“信任模型â€ã€‚å¯èƒ½çš„选项是:
+auto_init=åˆå§‹åŒ–仓库(添加 .gitignoreã€è®¸å¯è¯å’Œè‡ªè¿°æ–‡ä»¶ï¼‰
+trust_model_helper=选择签å验è¯çš„信任模型。å¯èƒ½çš„选项是:
trust_model_helper_collaborator=å作者:信任å作者的签å
trust_model_helper_committer=æäº¤è€…:信任与æäº¤è€…相符的签å
trust_model_helper_collaborator_committer=å作者+æäº¤è€…:信任åä½œè€…åŒæ—¶æ˜¯æäº¤è€…的签å
@@ -1084,13 +1108,13 @@ mirror_address_protocol_invalid=æä¾›çš„URL无效。åªèƒ½ä½¿ç”¨http(s)://或gi
mirror_lfs=大文件存储 (LFS)
mirror_lfs_desc=é•œåƒ LFS æ•°æ®ã€‚
mirror_lfs_endpoint=LFS 网å€
-mirror_lfs_endpoint_desc=åŒæ­¥å°†å°è¯•ä½¿ç”¨å…‹éš†ç½‘å€æ¥ <a target="_blank" rel="noopener noreferrer" href="%s">确定 LFS æœåС噍</a>。如果仓库 LFS æ•°æ®å­˜å‚¨åœ¨å…¶ä»–ä½ç½®ï¼Œä½ è¿˜å¯ä»¥æŒ‡å®šè‡ªå®šä¹‰ç½‘å€ã€‚
+mirror_lfs_endpoint_desc=åŒæ­¥å°†å°è¯•ä½¿ç”¨å…‹éš†ç½‘å€æ¥ <a target="_blank" rel="noopener noreferrer" href="%s">确定 LFS æœåС噍</a>。如果仓库 LFS æ•°æ®å­˜å‚¨åœ¨å…¶ä»–ä½ç½®ï¼Œæ‚¨è¿˜å¯ä»¥æŒ‡å®šè‡ªå®šä¹‰ç½‘å€ã€‚
mirror_last_synced=ä¸Šæ¬¡åŒæ­¥
mirror_password_placeholder=(未更改)
mirror_password_blank_placeholder=(未设置)
mirror_password_help=更改用户å以删除已储存的密ç ã€‚
watchers=关注者
-stargazers=称赞者
+stargazers=已赞者
stars_remove_warning=这将清除此仓库的所有点赞数。
forks=派生仓库
stars=点赞数
@@ -1111,28 +1135,27 @@ blame.ignore_revs=忽略 <a href="%s">.git-blame-ignore-revs</a> 的修订。点
blame.ignore_revs.failed=忽略 <a href="%s">.git-blame-ignore-revs</a> 版本失败。
user_search_tooltip=最多显示30å用户
-tree_path_not_found_commit=路径%[1]s 在æäº¤ %[2]s 中ä¸å­˜åœ¨
-tree_path_not_found_branch=路径 %[1]s ä¸å­˜åœ¨äºŽåˆ†æ”¯ %[2]s 中。
-tree_path_not_found_tag=路径 %[1]s ä¸å­˜åœ¨äºŽæ ‡ç­¾ %[2]s 中
+tree_path_not_found=%[2]s 中ä¸å­˜åœ¨è·¯å¾„ %[1]s
transfer.accept=接å—转移
-transfer.accept_desc=`转移到 "%s"`
+transfer.accept_desc=转移到「%sã€
transfer.reject=æ‹’ç»è½¬ç§»
-transfer.reject_desc=`å–æ¶ˆè½¬ç§»åˆ° "%s"`
-transfer.no_permission_to_accept=您没有æƒé™æŽ¥å—此转让。
-transfer.no_permission_to_reject=您没有æƒé™æ‹’ç»æ­¤è½¬è®©ã€‚
+transfer.reject_desc=å–æ¶ˆè½¬ç§»åˆ°ã€Œ%sã€
+transfer.no_permission_to_accept=您没有æƒé™æŽ¥å—此转移。
+transfer.no_permission_to_reject=您没有æƒé™æ‹’ç»æ­¤è½¬ç§»ã€‚
desc.private=ç§æœ‰åº“
desc.public=公开
+desc.public_access=公开访问
desc.template=模æ¿
desc.internal=内部
desc.archived=已存档
desc.sha256=SHA256
template.items=模æ¿é€‰é¡¹
-template.git_content=Gitæ•°æ®(默认分支)
+template.git_content=Git æ•°æ®ï¼ˆé»˜è®¤åˆ†æ”¯ï¼‰
template.git_hooks=Git é’©å­
-template.git_hooks_tooltip=ä½ ç›®å‰æ— æ³•修改或删除被添加过的 Git Hook。仅当你信任模æ¿ä»“库时æ‰å¯ä»¥é€‰æ‹©æ­¤é¡¹ã€‚
+template.git_hooks_tooltip=æ‚¨ç›®å‰æ— æ³•修改或删除被添加过的 Git é’©å­ã€‚仅当您信任模æ¿ä»“库时æ‰å¯ä»¥é€‰æ‹©æ­¤é¡¹ã€‚
template.webhooks=Web é’©å­
template.topics=主题
template.avatar=头åƒ
@@ -1145,10 +1168,10 @@ archive.title_date=该仓库已于 %s 归档。您å¯ä»¥æŸ¥çœ‹æ–‡ä»¶æˆ–克隆它
archive.issue.nocomment=此仓库已存档,您ä¸èƒ½åœ¨æ­¤å·¥å•添加评论。
archive.pull.nocomment=此仓库已存档,您ä¸èƒ½åœ¨æ­¤åˆå¹¶è¯·æ±‚添加评论。
-form.reach_limit_of_creation_1=ä½ å·²ç»è¾¾åˆ°äº† %d 仓库的上é™ã€‚
-form.reach_limit_of_creation_n=ä½ å·²ç»è¾¾åˆ°äº† %d 个仓库的上é™ã€‚
-form.name_reserved=仓库åç§° %s 是被ä¿ç•™çš„。
-form.name_pattern_not_allowed=仓库å称中ä¸å…许使用 %s æ ¼å¼ã€‚
+form.reach_limit_of_creation_1=您已ç»è¾¾åˆ°äº† %d 仓库的上é™ã€‚
+form.reach_limit_of_creation_n=您已ç»è¾¾åˆ°äº† %d 个仓库的上é™ã€‚
+form.name_reserved=仓库åç§° %s 是ä¿ç•™çš„。
+form.name_pattern_not_allowed=仓库å中ä¸å…许使用「%sã€æ ¼å¼ã€‚
need_auth=授æƒ
migrate_options=è¿ç§»é€‰é¡¹
@@ -1156,7 +1179,7 @@ migrate_service=è¿ç§»æœåŠ¡
migrate_options_mirror_helper=该仓库将是一个镜åƒ
migrate_options_lfs=è¿ç§» LFS 文件
migrate_options_lfs_endpoint.label=LFS 网å€
-migrate_options_lfs_endpoint.description=è¿ç§»å°†å°è¯•使用你的 Git remote æ¥ <a target="_blank" rel="noopener noreferrer" href="%s">确定 LFS æœåС噍</a>。如果仓库 LFS æ•°æ®å­˜å‚¨åœ¨å…¶ä»–ä½ç½®ï¼Œä½ è¿˜å¯ä»¥æŒ‡å®šè‡ªå®šä¹‰ç½‘å€ã€‚
+migrate_options_lfs_endpoint.description=è¿ç§»å°†å°è¯•使用您的 Git remote æ¥ <a target="_blank" rel="noopener noreferrer" href="%s">确定 LFS æœåС噍</a>。如果仓库 LFS æ•°æ®å­˜å‚¨åœ¨å…¶ä»–ä½ç½®ï¼Œæ‚¨è¿˜å¯ä»¥æŒ‡å®šè‡ªå®šä¹‰ç½‘å€ã€‚
migrate_options_lfs_endpoint.description.local=æ”¯æŒæœ¬åœ°æœåŠ¡å™¨è·¯å¾„ã€‚
migrate_options_lfs_endpoint.placeholder=如果留空,网å€å°†ä»Žå…‹éš† URL 中得到
migrate_items=è¿ç§»é¡¹ç›®
@@ -1166,7 +1189,7 @@ migrate_items_labels=标签
migrate_items_issues=å·¥å•
migrate_items_pullrequests=åˆå¹¶è¯·æ±‚
migrate_items_merge_requests=åˆå¹¶è¯·æ±‚
-migrate_items_releases=版本å‘布
+migrate_items_releases=å‘布
migrate_repo=è¿ç§»ä»“库
migrate.clone_address=从 URL è¿ç§»/克隆
migrate.clone_address_desc=现有仓库的 HTTP(s) 或 Git "clone" URL
@@ -1207,6 +1230,7 @@ migrate.migrating_issues=è¿ç§»å·¥å•
migrate.migrating_pulls=è¿ç§»åˆå¹¶è¯·æ±‚
migrate.cancel_migrating_title=å–æ¶ˆè¿ç§»
migrate.cancel_migrating_confirm=您想è¦å–消此次è¿ç§»å—?
+migration_status=è¿ç§»çжæ€
mirror_from=镜åƒè‡ªåœ°å€
forked_from=派生自
@@ -1231,6 +1255,7 @@ create_new_repo_command=从命令行创建一个新的仓库
push_exist_repo=从命令行推é€å·²ç»åˆ›å»ºçš„仓库
empty_message=这个家伙很懒,什么都没有推é€ã€‚
broken_message=æ— æ³•è¯»å–æ­¤ä»“库下的 Git æ•°æ®ã€‚ è”系此实例的管ç†å‘˜æˆ–删除此仓库。
+no_branch=该仓库没有任何分支。
code=代ç 
code.desc=查看æºç ã€æ–‡ä»¶ã€æäº¤å’Œåˆ†æ”¯ã€‚
@@ -1238,24 +1263,25 @@ branch=分支
tree=目录树
clear_ref=`清除当å‰å¼•用`
filter_branch_and_tag=过滤分支或标签
-find_tag=查找Git标签
+find_tag=查找标签
branches=分支列表
tags=标签列表
issues=å·¥å•
pulls=åˆå¹¶è¯·æ±‚
projects=项目
packages=软件包
-actions=Actions
+actions=工作æµ
labels=标签
org_labels_desc=组织级别的标签,å¯ä»¥è¢«æœ¬ç»„织下的 <strong>所有仓库</strong> 使用
org_labels_desc_manage=管ç†
+milestone=里程碑
milestones=里程碑
commits=æäº¤
commit=æäº¤
-release=版本å‘布
-releases=版本å‘布
-tag=Git标签
+release=å‘布
+releases=å‘布
+tag=标签
released_this=å‘布
tagged_this=已标记
file.title=%s ä½äºŽ %s
@@ -1283,7 +1309,6 @@ file_copy_permalink=å¤åˆ¶æ°¸ä¹…链接
view_git_blame=查看 Git Blame
video_not_supported_in_browser=您的æµè§ˆå™¨ä¸æ”¯æŒä½¿ç”¨ HTML5 'video' 标签。
audio_not_supported_in_browser=您的æµè§ˆå™¨ä¸æ”¯æŒä½¿ç”¨ HTML5 'video' 标签。
-stored_lfs=存储到Git LFS
symbolic_link=符å·é“¾æŽ¥
executable_file=坿‰§è¡Œæ–‡ä»¶
vendored=被供应的
@@ -1309,16 +1334,18 @@ editor.upload_file=上传文件
editor.edit_file=编辑文件
editor.preview_changes=é¢„è§ˆå˜æ›´
editor.cannot_edit_lfs_files=无法在 web 界é¢ä¸­ç¼–辑 lfs 文件。
+editor.cannot_edit_too_large_file=文件过大,无法编辑。
editor.cannot_edit_non_text_files=网页ä¸èƒ½ç¼–辑二进制文件。
+editor.file_not_editable_hint=但您ä»ç„¶å¯ä»¥é‡å‘½å或移动它。
editor.edit_this_file=编辑文件
editor.this_file_locked=文件已é”定
editor.must_be_on_a_branch=您必须在æŸä¸ªåˆ†æ”¯ä¸Šæ‰èƒ½å¯¹æ­¤æ–‡ä»¶è¿›è¡Œä¿®æ”¹æ“作。
-editor.fork_before_edit=您必须在派生这个仓库æ‰èƒ½å¯¹æ­¤æ–‡ä»¶è¿›è¡Œä¿®æ”¹æ“作
+editor.fork_before_edit=您必须派生这个仓库æ‰èƒ½å¯¹æ­¤æ–‡ä»¶è¿›è¡Œä¿®æ”¹æ“作。
editor.delete_this_file=删除文件
editor.must_have_write_access=您必须具有写æƒé™æ‰èƒ½å¯¹æ­¤æ–‡ä»¶è¿›è¡Œä¿®æ”¹æ“作。
-editor.file_delete_success=文件 %s 已被删除。
+editor.file_delete_success=文件「%sã€å·²åˆ é™¤ã€‚
editor.name_your_file=命忖‡ä»¶...
-editor.filename_help=通过键入åç§°åŽè·Ÿæ–œçº¿ ("/") æ¥æ·»åŠ ç›®å½•ã€‚é€šè¿‡åœ¨è¾“å…¥æ¡†çš„å¼€å¤´é”®å…¥ "退格" æ¥åˆ é™¤ç›®å½•。
+editor.filename_help=通过键入åç§°åŽè·Ÿæ–œçº¿ ("/") æ¥æ·»åŠ ç›®å½•ã€‚é€šè¿‡åœ¨è¾“å…¥æ¡†çš„å¼€å¤´é”®å…¥ã€Œé€€æ ¼ã€æ¥åˆ é™¤ç›®å½•。
editor.or=或
editor.cancel_lower=å–æ¶ˆ
editor.commit_signed_changes=æäº¤å·²ç­¾å的更改
@@ -1329,52 +1356,60 @@ editor.update=æ›´æ–° %s
editor.delete=删除 %s
editor.patch=应用补ä¸
editor.patching=打补ä¸ï¼š
-editor.fail_to_apply_patch=æ— æ³•åº”ç”¨è¡¥ä¸ %s
+editor.fail_to_apply_patch=无法应用补ä¸
editor.new_patch=æ–°è¡¥ä¸
editor.commit_message_desc=添加一个å¯é€‰çš„æ‰©å±•æè¿°...
editor.signoff_desc=在æäº¤æ—¥å¿—æ¶ˆæ¯æœ«å°¾æ·»åŠ ç­¾ç½²äººä¿¡æ¯ã€‚
editor.commit_directly_to_this_branch=直接æäº¤è‡³ <strong class="branch-name">%s</strong> 分支。
editor.create_new_branch=为此æäº¤åˆ›å»ºä¸€ä¸ª <strong>新的分支</strong> å¹¶å‘èµ·åˆå¹¶è¯·æ±‚。
editor.create_new_branch_np=为此æäº¤åˆ›å»º <strong>新分支</strong>。
-editor.propose_file_change=æè®®æ–‡ä»¶æ›´æ”¹
+editor.propose_file_change=建议文件更改
editor.new_branch_name=为这次æäº¤çš„æ–°åˆ†æ”¯å‘½å
editor.new_branch_name_desc=新的分支åç§°...
editor.cancel=å–æ¶ˆ
editor.filename_cannot_be_empty=文件åä¸èƒ½ä¸ºç©ºã€‚
-editor.filename_is_invalid=文件å %s 无效
-editor.branch_does_not_exist=此仓库中ä¸å­˜åœ¨å为 %s 的分支。
-editor.branch_already_exists=此仓库已存在å为 %s 的分支。
-editor.directory_is_a_file=%s å·²ç»ä½œä¸ºæ–‡ä»¶å在此仓库中存在。
-editor.file_is_a_symlink=`"%s" 是一个符å·é“¾æŽ¥ï¼Œæ— æ³•在 web 编辑器中编辑`
-editor.filename_is_a_directory=此仓库中已存在å为“%s†的目录。
-editor.file_editing_no_longer_exists=正在编辑的文件 %s å·²ä¸å­˜åœ¨ã€‚
-editor.file_deleting_no_longer_exists=正在删除的文件 %s å·²ä¸å­˜åœ¨ã€‚
+editor.filename_is_invalid=æ–‡ä»¶åæ— æ•ˆï¼šã€Œ%sã€ã€‚
+editor.commit_email=æäº¤é‚®ç®±åœ°å€
+editor.invalid_commit_email=æäº¤çš„é‚®ç®±åœ°å€æ— æ•ˆã€‚
+editor.branch_does_not_exist=此仓库中ä¸å­˜åœ¨å为「%sã€çš„分支。
+editor.branch_already_exists=此仓库已存在å为「%sã€çš„分支。
+editor.directory_is_a_file=目录å「%sã€å·²ä½œä¸ºæ–‡ä»¶å在此仓库中存在。
+editor.file_is_a_symlink=`「%sã€æ˜¯ä¸€ä¸ªç¬¦å·é“¾æŽ¥ï¼Œæ— æ³•在 Web 编辑器中编辑`
+editor.filename_is_a_directory=文件å「%sã€å·²ä½œä¸ºç›®å½•å在此仓库中存在。
+editor.file_modifying_no_longer_exists=正在修改的文件「%sã€å·²ä¸å­˜åœ¨äºŽæ­¤ä»“库。
editor.file_changed_while_editing=文件内容在您进行编辑时已ç»å‘生å˜åŠ¨ã€‚<a target="_blank" rel="noopener noreferrer" href="%s">å•击此处</a> 查看å˜åŠ¨çš„å…·ä½“å†…å®¹ï¼Œæˆ–è€… <strong>冿¬¡æäº¤</strong> 覆盖已å‘生的å˜åŠ¨ã€‚
-editor.file_already_exists=此仓库已ç»å­˜åœ¨å为 %s 的文件。
-editor.commit_id_not_matching=æäº¤ID与您开始编辑时的IDä¸åŒ¹é…。请æäº¤åˆ°è¡¥ä¸åˆ†æ”¯ç„¶åŽåˆå¹¶ã€‚
+editor.file_already_exists=此仓库已ç»å­˜åœ¨å为「%sã€çš„æ–‡ä»¶ã€‚
+editor.commit_id_not_matching=æäº¤ ID 与您开始编辑时的 ID ä¸åŒ¹é…。请æäº¤åˆ°è¡¥ä¸åˆ†æ”¯ç„¶åŽåˆå¹¶ã€‚
editor.push_out_of_date=推é€ä¼¼ä¹Žå·²ç»è¿‡æ—¶ã€‚
editor.commit_empty_file_header=æäº¤ä¸€ä¸ªç©ºæ–‡ä»¶
editor.commit_empty_file_text=æ‚¨è¦æäº¤çš„æ–‡ä»¶æ˜¯ç©ºçš„ï¼Œç»§ç»­å—?
editor.no_changes_to_show=没有å¯ä»¥æ˜¾ç¤ºçš„å˜æ›´ã€‚
-editor.fail_to_update_file=更新/创建文件 %s 失败。
-editor.fail_to_update_file_summary=错误信æ¯ï¼š
-editor.push_rejected_no_message=此修改被æœåŠ¡å™¨æ‹’ç»å¹¶ä¸”没有å馈消æ¯ã€‚请检查 Git Hook。
-editor.push_rejected=此修改被æœåŠ¡å™¨æ‹’ç»ã€‚请检查 Git Hook。
+editor.push_rejected_no_message=此修改被æœåŠ¡å™¨æ‹’ç»å¹¶ä¸”没有å馈消æ¯ã€‚请检查 Git é’©å­ã€‚
+editor.push_rejected=此修改被æœåŠ¡å™¨æ‹’ç»ã€‚请检查 Git é’©å­ã€‚
editor.push_rejected_summary=详细拒ç»ä¿¡æ¯ï¼š
editor.add_subdir=添加目录
-editor.unable_to_upload_files=上传文件至 %s æ—¶å‘生错误:%v
-editor.upload_file_is_locked=文件 %s 被 %s é”定。
-editor.upload_files_to_dir=上传文件至 %s
-editor.cannot_commit_to_protected_branch=ä¸å¯ä»¥æäº¤åˆ°å—ä¿æŠ¤çš„åˆ†æ”¯ %s。
+editor.unable_to_upload_files=上传文件至「%sã€å¤±è´¥ï¼š%v
+editor.upload_file_is_locked=文件「%sã€è¢« %s é”定。
+editor.upload_files_to_dir=上传文件至「%sã€
+editor.cannot_commit_to_protected_branch=ä¸å¯ä»¥æäº¤åˆ°å—ä¿æŠ¤çš„åˆ†æ”¯ã€Œ%sã€ã€‚
editor.no_commit_to_branch=无法直接æäº¤åˆ†æ”¯ï¼Œå› ä¸ºï¼š
editor.user_no_push_to_branch=用户ä¸èƒ½æŽ¨é€åˆ°åˆ†æ”¯
editor.require_signed_commit=分支需è¦ç­¾åæäº¤
-editor.cherry_pick=Cherry-pick %s 到:
+editor.cherry_pick=拣选æäº¤ %s 到:
editor.revert=将 %s 还原到:
+editor.failed_to_commit=æäº¤æ›´æ”¹å¤±è´¥ã€‚
+editor.failed_to_commit_summary=错误信æ¯ï¼š
+
+editor.fork_create=派生仓库å‘èµ·è¯·æ±‚å˜æ›´
+editor.fork_create_description=您ä¸èƒ½ç›´æŽ¥ç¼–辑此仓库。您å¯ä»¥ä»Žæ­¤ä»“库派生,进行编辑并创建一个拉å–请求。
+editor.fork_edit_description=您ä¸èƒ½ç›´æŽ¥ç¼–辑此仓库。 更改将写入您的派生仓库 <b>%s</b>,以便您å¯ä»¥åˆ›å»ºä¸€ä¸ªæ‹‰å–请求。
+editor.fork_not_editable=ä½ å·²ç»æ´¾ç”Ÿäº†è¿™ä¸ªä»“åº“ï¼Œä½†æ˜¯ä½ çš„åˆ†å‰æ˜¯ä¸å¯ç¼–辑的。
+editor.fork_failed_to_push_branch=推é€åˆ†æ”¯ %s 到仓库失败。
+editor.fork_branch_exists=分支 "%s" 已存在于您的派生仓库中,请选择一个新的分支å称。
commits.desc=æµè§ˆä»£ç ä¿®æ”¹åކå²
commits.commits=æ¬¡ä»£ç æäº¤
-commits.no_commits=没有共åŒçš„æäº¤ã€‚%s å’Œ %s 的历å²å®Œå…¨ä¸åŒã€‚
+commits.no_commits=没有共åŒçš„æäº¤ã€‚「%sã€å’Œã€Œ%sã€çš„历å²å®Œå…¨ä¸åŒã€‚
commits.nothing_to_compare=这些分支是相åŒçš„。
commits.search.tooltip=`您å¯ä»¥åœ¨å…³é”®è¯å‰åŠ ä¸Šå‰ç¼€ï¼Œå¦‚"author:", "committer:", "after:", 或"before:", 例如 "retrin author:Alice before:2019-01-13"`
commits.search_branch=此分支
@@ -1390,18 +1425,19 @@ commits.signed_by_untrusted_user_unmatched=由与æäº¤è€…ä¸åŒ¹é…的未授信ç
commits.gpg_key_id=GPG 密钥 ID
commits.ssh_key_fingerprint=SSH 密钥指纹
commits.view_path=在历å²è®°å½•中的此处查看
+commits.view_file_diff=查看æäº¤ä¸­çš„æ–‡ä»¶æ›´æ”¹
commit.operations=æ“作
commit.revert=还原
-commit.revert-header=还原: %s
+commit.revert-header=还原:%s
commit.revert-content=选择è¦è¿˜åŽŸçš„åˆ†æ”¯ï¼š
-commit.cherry-pick=Cherry-pick
-commit.cherry-pick-header=Cherry-pick: %s
-commit.cherry-pick-content=选择 cherry-pick 的目标分支:
+commit.cherry-pick=拣选æäº¤
+commit.cherry-pick-header=拣选æäº¤ï¼š%s
+commit.cherry-pick-content=é€‰æ‹©è¦æ‹£é€‰æäº¤çš„目标分支:
commitstatus.error=错误
commitstatus.failure=失败
-commitstatus.pending=待定
+commitstatus.pending=队列
commitstatus.success=æˆåŠŸ
ext_issues=访问外部工å•
@@ -1414,14 +1450,14 @@ projects.create=创建项目
projects.title=标题
projects.new=创建项目
projects.new_subheader=在一个地方åè°ƒã€è·Ÿè¸ªå’Œæ›´æ–°æ‚¨çš„å·¥ä½œï¼Œè®©é¡¹ç›®ä¿æŒé€æ˜Žå¹¶æŒ‰è®¡åˆ’进行。
-projects.create_success=项目 %s 创建æˆåŠŸã€‚
+projects.create_success=项目「%sã€åˆ›å»ºæˆåŠŸã€‚
projects.deletion=删除项目
projects.deletion_desc=删除项目会从所有相关的工å•中移除它。是å¦ç»§ç»­ï¼Ÿ
-projects.deletion_success=该项目已被删除。
+projects.deletion_success=该项目已删除。
projects.edit=编辑项目
projects.edit_subheader=项目用于组织工å•和跟踪进展情况。
projects.modify=更新项目
-projects.edit_success=项目 %s æ›´æ–°æˆåŠŸã€‚
+projects.edit_success=项目「%sã€æ›´æ–°æˆåŠŸã€‚
projects.type.none=æ— 
projects.type.basic_kanban=基础看æ¿
projects.type.bug_triage=Bug分类看æ¿
@@ -1435,8 +1471,8 @@ projects.column.new=创建列
projects.column.set_default=设为默认
projects.column.set_default_desc=设置此列为未分类问题和åˆå¹¶è¯·æ±‚的默认值
projects.column.delete=删除列
-projects.column.deletion_desc=删除项目列会将所有相关问题移到“未分类â€ã€‚是å¦ç»§ç»­ï¼Ÿ
-projects.column.color=彩色
+projects.column.deletion_desc=删除项目列会将所有相关问题移至默认列。是å¦ç»§ç»­ï¼Ÿ
+projects.column.color=颜色
projects.open=å¼€å¯
projects.close=关闭
projects.column.assigned_to=指派给
@@ -1450,6 +1486,8 @@ issues.filter_milestones=筛选里程碑
issues.filter_projects=筛选项目
issues.filter_labels=筛选标签
issues.filter_reviewers=筛选审核者
+issues.filter_no_results=没有结果
+issues.filter_no_results_placeholder=请å°è¯•调整您的æœç´¢è¿‡æ»¤å™¨ã€‚
issues.new=创建工å•
issues.new.title_empty=标题ä¸èƒ½ä¸ºç©º
issues.new.labels=标签
@@ -1478,19 +1516,19 @@ issues.choose.blank_about=从默认模æ¿åˆ›å»ºä¸€ä¸ªå·¥å•。
issues.choose.ignore_invalid_templates=已忽略无效模æ¿
issues.choose.invalid_templates=å‘现了 %v 个无效模æ¿
issues.choose.invalid_config=问题é…置包å«é”™è¯¯ï¼š
-issues.no_ref=分支/标记未指定
+issues.no_ref=分支/Git标签未指定
issues.create=创建工å•
issues.new_label=创建标签
issues.new_label_placeholder=标签åç§°
issues.new_label_desc_placeholder=æè¿°
issues.create_label=创建标签
issues.label_templates.title=加载预定义的标签模æ¿
-issues.label_templates.info=还没有任何标签。您å¯ä»¥ä½¿ç”¨'创建标签'按钮或者加载预定义的标签集创建标签
+issues.label_templates.info=还没有任何标签。您å¯ä»¥ä½¿ç”¨ã€Œåˆ›å»ºæ ‡ç­¾ã€æŒ‰é’®æˆ–者加载预定义的标签集创建标签:
issues.label_templates.helper=选择标签模æ¿
issues.label_templates.use=使用标签集
-issues.label_templates.fail_to_load_file=åŠ è½½æ ‡ç­¾æ¨¡æ¿æ–‡ä»¶ %s æ—¶å‘生错误:%v
+issues.label_templates.fail_to_load_file=åŠ è½½æ ‡ç­¾æ¨¡æ¿æ–‡ä»¶ã€Œ%sã€æ—¶å‘生错误:%v
issues.add_label=于 %[2]s 添加了标签 %[1]s
-issues.add_labels=于 %s 添加 %s 标签
+issues.add_labels=于 %[2]s 添加了标签 %[1]s
issues.remove_label=于 %[2]s 删除了标签 %[1]s
issues.remove_labels=于 %[2]s 删除了标签 %[1]s
issues.add_remove_labels=于 %[3]s 添加了标签 %[1]s ,删除了标签 %[2]s
@@ -1500,11 +1538,11 @@ issues.move_to_column_of_project=`将此对象移至 %s 的 %s 中在 %s 上`
issues.change_milestone_at=`%[3]s 修改了里程碑从 <b>%[1]s</b> 到 <b>%[2]s</b>`
issues.change_project_at=于 %[3]s 将此从项目 <b>%[1]s</b> 移到 <b>%[2]s</b>
issues.remove_milestone_at=`%[2]s 删除了里程碑 <b>%[1]s</b>`
-issues.remove_project_at=`从 <b>%s</b> 项目 %s 中删除`
+issues.remove_project_at=`于 %[2]s 将此工å•从项目 <b>%[1]s</b> 中删除`
issues.deleted_milestone=(已删除)
issues.deleted_project=`(已删除)`
issues.self_assign_at=`于 %s 指派给自己`
-issues.add_assignee_at=`于 %[2]s 被 <b>%[1]s</b> 指派`
+issues.add_assignee_at=`于 %[2]s 由 <b>%[1]s</b> 指派`
issues.remove_assignee_at=`<b>%s</b> å–æ¶ˆäº†æŒ‡æ´¾åœ¨ %s`
issues.remove_self_assignment=`于 %s å–æ¶ˆäº†æŒ‡æ´¾`
issues.change_title_at=`于 %[3]s 修改标题 <b><strike>%[1]s</strike></b> 为 <b>%[2]s</b>`
@@ -1523,15 +1561,16 @@ issues.filter_milestone_open=进行中的里程碑
issues.filter_milestone_closed=已关闭的里程碑
issues.filter_project=项目
issues.filter_project_all=所有项目
-issues.filter_project_none=暂无项目
+issues.filter_project_none=未加项目
issues.filter_assignee=指派人筛选
-issues.filter_assginee_no_select=所有指派æˆå‘˜
-issues.filter_assginee_no_assignee=未指派
+issues.filter_assignee_no_assignee=未指派给任何人
+issues.filter_assignee_any_assignee=已有指派
issues.filter_poster=作者
issues.filter_user_placeholder=æœç´¢ç”¨æˆ·
issues.filter_user_no_select=所有用户
issues.filter_type=类型筛选
issues.filter_type.all_issues=所有工å•
+issues.filter_type.all_pull_requests=所有åˆå¹¶è¯·æ±‚
issues.filter_type.assigned_to_you=指派给您的
issues.filter_type.created_by_you=由您创建的
issues.filter_type.mentioning_you=æåŠæ‚¨çš„
@@ -1560,11 +1599,11 @@ issues.action_assignee_no_select=未指派
issues.action_check=选中/å–æ¶ˆé€‰ä¸­
issues.action_check_all=选中/å–æ¶ˆé€‰ä¸­æ‰€æœ‰é¡¹ç›®
issues.opened_by=由 <a href="%[2]s">%[3]s</a> 于 %[1]s创建
-pulls.merged_by=ç”± <a href="%[2]s">%[3]s</a> 创建,被åˆå¹¶äºŽ %[1]s
-pulls.merged_by_fake=ç”± %[2]s 创建,被åˆå¹¶äºŽ %[1]s
-issues.closed_by=由 <a href="%[2]s">%[3]s</a> 创建,被关闭于 %[1]s
+pulls.merged_by=ç”± <a href="%[2]s">%[3]s</a> 创建,åˆå¹¶äºŽ %[1]s
+pulls.merged_by_fake=ç”± %[2]s 创建,åˆå¹¶äºŽ %[1]s
+issues.closed_by=由 <a href="%[2]s">%[3]s</a> 创建,关闭于 %[1]s
issues.opened_by_fake=由 %[2]s 于 %[1]s 打开
-issues.closed_by_fake=由 %[2]s 创建,被关闭于 %[1]s
+issues.closed_by_fake=由 %[2]s 创建,关闭于 %[1]s
issues.previous=上一页
issues.next=下一页
issues.open_title=å¼€å¯ä¸­
@@ -1584,8 +1623,8 @@ issues.close=关闭工å•
issues.comment_pull_merged_at=å·²åˆå¹¶æäº¤ %[1]s 到 %[2]s %[3]s
issues.comment_manually_pull_merged_at=手动åˆå¹¶æäº¤ %[1]s 到 %[2]s %[3]s
issues.close_comment_issue=评论并关闭
-issues.reopen_issue=釿–°å¼€å¯
-issues.reopen_comment_issue=è¯„è®ºå¹¶é‡æ–°å¼€å¯
+issues.reopen_issue=釿–°æ‰“å¼€
+issues.reopen_comment_issue=è¯„è®ºå¹¶é‡æ–°æ‰“å¼€
issues.create_comment=评论
issues.comment.blocked_user=无法创建或编辑评论,因为您已被仓库所有者或工å•创建者å±è”½ã€‚
issues.closed_at=`于 <a id="%[1]s" href="#%[1]s">%[2]s</a> 关闭此工å•`
@@ -1600,9 +1639,9 @@ issues.ref_reopened_from=`<a href="%[3]s">釿–°æ‰“å¼€è¿™ä¸ªå·¥å• %[4]s</a> <a
issues.ref_from=`æ¥è‡ª %[1]s`
issues.author=作者
issues.author_helper=此用户是作者。
-issues.role.owner=管ç†å‘˜
+issues.role.owner=所有者
issues.role.owner_helper=该用户是该仓库的所有者。
-issues.role.member=普通æˆå‘˜
+issues.role.member=æˆå‘˜
issues.role.member_helper=该用户是拥有该仓库的组织æˆå‘˜ã€‚
issues.role.collaborator=å作者
issues.role.collaborator_helper=该用户已被邀请在仓库上进行å作。
@@ -1621,39 +1660,41 @@ issues.edit=编辑
issues.cancel=å–æ¶ˆ
issues.save=ä¿å­˜
issues.label_title=标签åç§°
-issues.label_description=标签æè¿°
-issues.label_color=标签颜色
-issues.label_exclusive=独有
+issues.label_description=æè¿°
+issues.label_color=颜色
+issues.label_color_invalid=无效的颜色
+issues.label_exclusive=互斥
issues.label_archive=归档标签
-issues.label_archived_filter=显示存档标签
-issues.label_archive_tooltip=在标签æœç´¢æ—¶ï¼Œé»˜è®¤æƒ…况下存档标签将被排除在外。
+issues.label_archived_filter=显示已归档标签
+issues.label_archive_tooltip=在标签æœç´¢æ—¶ï¼Œé»˜è®¤æƒ…况下已归档标签将被排除在外。
issues.label_exclusive_desc=命忠‡ç­¾ä¸º <code>scope/item</code> 以使其与其他以 <code>scope/</code> 开头的标签互斥。
issues.label_exclusive_warning=åœ¨ç¼–è¾‘å·¥å•æˆ–åˆå¹¶è¯·æ±‚的标签时,任何冲çªçš„范围标签都将被删除。
+issues.label_exclusive_order=排åºé¡ºåº
+issues.label_exclusive_order_tooltip=在åŒä¸€ä¸ªèŒƒå›´å†…的互斥标签将按照这个数字进行排åº
issues.label_count=%d 个标签
-issues.label_open_issues=%d 个开å¯çš„å·¥å•
+issues.label_open_issues=%d 个已打开工å•/åˆå¹¶è¯·æ±‚
issues.label_edit=编辑
issues.label_delete=删除
issues.label_modify=编辑标签
issues.label_deletion=删除标签
issues.label_deletion_desc=删除标签会将其从所有问题中删除。继续?
-issues.label_deletion_success=该标签已被删除。
+issues.label_deletion_success=该标签已删除。
issues.label.filter_sort.alphabetically=按字æ¯é¡ºåºæŽ’åº
issues.label.filter_sort.reverse_alphabetically=按字æ¯é€†åºæŽ’åº
issues.label.filter_sort.by_size=最å°å°ºå¯¸
issues.label.filter_sort.reverse_by_size=最大尺寸
issues.num_participants=%d åå‚与者
-issues.attachment.open_tab=`在新的标签页中查看 '%s'`
-issues.attachment.download=`点击下载 '%s'`
+issues.attachment.open_tab=`在新的标签页中查看「%sã€`
+issues.attachment.download=`点击下载「%sã€`
issues.subscribe=订阅
issues.unsubscribe=å–æ¶ˆè®¢é˜…
-issues.unpin_issue=å–æ¶ˆç½®é¡¶
+issues.unpin=å–æ¶ˆç½®é¡¶
issues.max_pinned=您ä¸èƒ½ç½®é¡¶æ›´å¤šå·¥å•
-issues.pin_comment=于 %s 被置顶
+issues.pin_comment=于 %s 置顶
issues.unpin_comment=于 %s å–æ¶ˆç½®é¡¶
issues.lock=é”定对è¯
issues.unlock=è§£é”对è¯
-issues.lock.unknown_reason=由于未知原因无法é”定。
-issues.lock_duplicate=一个工å•ä¸èƒ½è¢«é”定两次。
+issues.lock_duplicate=一个工å•ä¸èƒ½é”定两次。
issues.unlock_error=无法解é”一个未é”定的工å•。
issues.lock_with_reason=因为 <strong>%s</strong> 而é”定,并将对è¯é™åˆ¶ä¸ºå作者 %s
issues.lock_no_reason=é”定并é™åˆ¶ä»…å作者 %s
@@ -1680,17 +1721,21 @@ issues.timetracker_timer_discard=删除计时器
issues.timetracker_timer_manually_add=添加时间
issues.time_estimate_set=设置预计时间
-issues.time_estimate_display=预计: %s
-issues.change_time_estimate_at=将预计时间修改为 <b>%s</b> %s
-issues.remove_time_estimate_at=删除预计时间 %s
+issues.time_estimate_display=预估:%s
+issues.change_time_estimate_at=预估时间已修改为 <b>%[1]s</b> %[2]s
+issues.remove_time_estimate_at=删除预估时间 %s
issues.time_estimate_invalid=é¢„è®¡æ—¶é—´æ ¼å¼æ— æ•ˆ
issues.start_tracking_history=`开始工作 %s`
issues.tracker_auto_close=当此工å•å…³é—­æ—¶ï¼Œè‡ªåŠ¨åœæ­¢è®¡æ—¶å™¨
-issues.tracking_already_started=`ä½ å·²ç»å¼€å§‹å¯¹ <a href="%s">å¦ä¸€ä¸ªå·¥å•</a> 进行时间跟踪ï¼`
-issues.stop_tracking_history=`åœæ­¢å·¥ä½œ %s`
+issues.stopwatch_already_stopped=此工å•的计时器已ç»åœæ­¢
+issues.stopwatch_already_created=此工å•的计时器已ç»å­˜åœ¨
+issues.tracking_already_started=`您已ç»å¼€å§‹å¯¹ <a href="%s">å¦ä¸€ä¸ªå·¥å•</a> 进行时间跟踪ï¼`
+issues.stop_tracking=åœæ­¢è®¡æ—¶å™¨
+issues.stop_tracking_history=工作 <b>%[1]s</b> 于 %[2]s åœæ­¢
+issues.cancel_tracking=å–æ¶ˆ
issues.cancel_tracking_history=`å–æ¶ˆæ—¶é—´è·Ÿè¸ª %s`
issues.del_time=删除此时间跟踪日志
-issues.add_time_history=`添加计时 %s`
+issues.add_time_history=已于 %[2]s 添加计时 <b>%[1]</b>
issues.del_time_history=`已删除时间 %s`
issues.add_time_manually=手动添加时间
issues.add_time_hours=å°æ—¶
@@ -1700,7 +1745,7 @@ issues.time_spent_total=总用时
issues.time_spent_from_all_authors=`总花费时间:%s`
issues.due_date=到期时间
-issues.invalid_due_date_format=到期时间的格å¼å¿…须是 'yyyy-mm-dd' 的形å¼ã€‚
+issues.invalid_due_date_format=到期时间的格å¼å¿…须是「yyyy-mm-ddã€çš„å½¢å¼ã€‚
issues.error_modifying_due_date=修改到期时间失败。
issues.error_removing_due_date=删除到期时间失败。
issues.push_commit_1=于 %[2]s 推é€äº† %[1]d 个æäº¤
@@ -1743,7 +1788,7 @@ issues.dependency.remove_header=删除ä¾èµ–项
issues.dependency.issue_remove_text=æ­¤æ“作将从工å•中删除ä¾èµ–。是å¦è¦ç»§ç»­?
issues.dependency.pr_remove_text=æ­¤æ“作将从åˆå¹¶è¯·æ±‚中删除ä¾èµ–。是å¦è¦ç»§ç»­?
issues.dependency.setting=为工å•å’Œåˆå¹¶è¯·æ±‚å¯ç”¨ä¾èµ–
-issues.dependency.add_error_same_issue=ä½ ä¸èƒ½è®©ä¸€ä¸ªå·¥å•ä¾èµ–于自己。
+issues.dependency.add_error_same_issue=您ä¸èƒ½è®©ä¸€ä¸ªå·¥å•ä¾èµ–于自身。
issues.dependency.add_error_dep_issue_not_exist=ä¾èµ–项ä¸å­˜åœ¨ã€‚
issues.dependency.add_error_dep_not_exist=ä¾èµ–项ä¸å­˜åœ¨ã€‚
issues.dependency.add_error_dep_exists=ä¾èµ–项已存在。
@@ -1785,9 +1830,9 @@ issues.review.stale=批准åŽå·²æ›´æ–°
issues.review.unofficial=éžå®˜æ–¹å®¡æ‰¹æ•°
issues.assignee.error=å› ä¸ºæœªçŸ¥åŽŸå› ï¼Œå¹¶éžæ‰€æœ‰çš„æŒ‡æ´¾éƒ½æˆåŠŸã€‚
issues.reference_issue.body=内容
-issues.content_history.deleted=删除于
-issues.content_history.edited=最åŽç¼–辑于
-issues.content_history.created=创建于
+issues.content_history.deleted=已删除
+issues.content_history.edited=已编辑
+issues.content_history.created=已创建
issues.content_history.delete_from_history=从历å²è®°å½•中删除
issues.content_history.delete_from_history_confirm=从历å²è®°å½•中删除å—?
issues.content_history.options=选项
@@ -1841,7 +1886,7 @@ pulls.merged=å·²åˆå¹¶
pulls.merged_success=åˆå¹¶è¯·æ±‚å·²æˆåŠŸåˆå¹¶å’Œå…³é—­
pulls.closed=åˆå¹¶è¯·æ±‚已关闭
pulls.manually_merged=已手动åˆå¹¶
-pulls.merged_info_text=分支 %s 现在å¯ä»¥è¢«åˆ é™¤äº†ã€‚
+pulls.merged_info_text=分支 %s 现在å¯ä»¥åˆ é™¤äº†ã€‚
pulls.is_closed=åˆå¹¶è¯·æ±‚å·²ç»å…³é—­ã€‚
pulls.title_wip_desc=`<a href="#">标题以 <strong>%s</strong> 开头</a>以å…åˆå¹¶è¯·æ±‚æ„外åˆå¹¶ã€‚`
pulls.cannot_merge_work_in_progress=æ­¤åˆå¹¶è¯·æ±‚被标记为正在进行的工作。
@@ -1850,7 +1895,7 @@ pulls.add_prefix=添加 <strong>%s</strong> å‰ç¼€
pulls.remove_prefix=删除 <strong>%s</strong> å‰ç¼€
pulls.data_broken=æ­¤åˆå¹¶è¯·æ±‚因为派生仓库信æ¯ç¼ºå¤±è€Œä¸­æ–­ã€‚
pulls.files_conflicted=æ­¤åˆå¹¶è¯·æ±‚æœ‰å˜æ›´ä¸Žç›®æ ‡åˆ†æ”¯å†²çªã€‚
-pulls.is_checking=正在进行åˆå¹¶å†²çªæ£€æµ‹ï¼Œè¯·ç¨åŽå†è¯•。
+pulls.is_checking=正在进行åˆå¹¶å†²çªæ£€æµ‹ ...
pulls.is_ancestor=此分支已ç»åŒ…å«åœ¨ç›®æ ‡åˆ†æ”¯ä¸­ï¼Œæ²¡æœ‰ä»€ä¹ˆå¯ä»¥åˆå¹¶ã€‚
pulls.is_empty=此分支上的更改已ç»åœ¨ç›®æ ‡åˆ†æ”¯ä¸Šã€‚这将是一个空æäº¤ã€‚
pulls.required_status_check_failed=一些必è¦çš„æ£€æŸ¥æ²¡æœ‰æˆåŠŸ
@@ -1876,7 +1921,7 @@ pulls.waiting_count_1=%d 个正在等待审核
pulls.waiting_count_n=%d 个正在等待审核
pulls.wrong_commit_id=æäº¤ id 必须在目标分支 上
-pulls.no_merge_desc=由于未å¯ç”¨åˆå¹¶é€‰é¡¹ï¼Œæ­¤åˆå¹¶è¯·æ±‚无法被åˆå¹¶ã€‚
+pulls.no_merge_desc=由于未å¯ç”¨åˆå¹¶é€‰é¡¹ï¼Œæ­¤åˆå¹¶è¯·æ±‚无法åˆå¹¶ã€‚
pulls.no_merge_helper=在仓库设置中å¯ç”¨åˆå¹¶é€‰é¡¹æˆ–者手工åˆå¹¶è¯·æ±‚。
pulls.no_merge_wip=这个åˆå¹¶è¯·æ±‚无法åˆå¹¶ï¼Œå› ä¸ºè¢«æ ‡è®°ä¸ºå°šæœªå®Œæˆçš„工作。
pulls.no_merge_not_ready=æ­¤åˆå¹¶è¯·æ±‚尚未准备好åˆå¹¶ï¼Œè¯·æ£€æŸ¥å®¡æ ¸çжæ€å’ŒçŠ¶æ€æ£€æŸ¥ã€‚
@@ -1890,7 +1935,7 @@ pulls.merge_manually=手动åˆå¹¶
pulls.merge_commit_id=åˆå¹¶æäº¤ ID
pulls.require_signed_wont_sign=分支需è¦ç­¾åçš„æäº¤ï¼Œä½†è¿™ä¸ªåˆå¹¶å°†ä¸ä¼šè¢«ç­¾å
-pulls.invalid_merge_option=ä½ å¯ä»¥åœ¨æ­¤åˆå¹¶è¯·æ±‚中使用åˆå¹¶é€‰é¡¹ã€‚
+pulls.invalid_merge_option=您å¯ä»¥åœ¨æ­¤åˆå¹¶è¯·æ±‚中使用åˆå¹¶é€‰é¡¹ã€‚
pulls.merge_conflict=åˆå¹¶å¤±è´¥ï¼šåˆå¹¶æ—¶æœ‰å†²çªå‘生。æç¤ºï¼šé‡‡ç”¨å…¶å®ƒåˆå¹¶ç­–ç•¥
pulls.merge_conflict_summary=错误信æ¯
pulls.rebase_conflict=åˆå¹¶å¤±è´¥ï¼šå˜åŸºæäº¤æœ‰å†²çªï¼š%[1]s。æç¤ºï¼šé‡‡ç”¨å…¶å®ƒåˆå¹¶ç­–ç•¥
@@ -1898,7 +1943,7 @@ pulls.rebase_conflict_summary=错误信æ¯
pulls.unrelated_histories=åˆå¹¶å¤±è´¥ï¼šä¸¤ä¸ªåˆ†æ”¯æ²¡æœ‰å…±åŒåކå²ã€‚æç¤ºï¼šå°è¯•ä¸åŒçš„ç­–ç•¥
pulls.merge_out_of_date=åˆå¹¶å¤±è´¥ï¼šåœ¨ç”Ÿæˆåˆå¹¶æ—¶ï¼Œä¸»åˆ†æ”¯å·²æ›´æ–°ã€‚æç¤ºï¼šå†è¯•一次。
pulls.head_out_of_date=åˆå¹¶å¤±è´¥ï¼šåœ¨ç”Ÿæˆåˆå¹¶æ—¶ï¼Œhead 已更新。æç¤ºï¼šå†è¯•一次。
-pulls.has_merged=失败:åˆå¹¶è¯·æ±‚å·²ç»è¢«åˆå¹¶ï¼Œæ‚¨ä¸èƒ½å†æ¬¡åˆå¹¶æˆ–更改目标分支。
+pulls.has_merged=失败:åˆå¹¶è¯·æ±‚å·²ç»åˆå¹¶ï¼Œæ‚¨ä¸èƒ½å†æ¬¡åˆå¹¶æˆ–更改目标分支。
pulls.push_rejected=推é€å¤±è´¥ï¼šæŽ¨é€è¢«æ‹’ç»ã€‚审查此仓库的 Git é’©å­ã€‚
pulls.push_rejected_summary=详细拒ç»ä¿¡æ¯
pulls.push_rejected_no_message=推é€å¤±è´¥ï¼šæ­¤æŽ¨é€è¢«æ‹’ç»ä½†æœªæä¾›å…¶ä»–ä¿¡æ¯ã€‚请检查此仓库的 Git é’©å­ã€‚
@@ -1920,14 +1965,14 @@ pulls.outdated_with_base_branch=此分支相比基础分支已过期
pulls.close=关闭åˆå¹¶è¯·æ±‚
pulls.closed_at=`于 <a id="%[1]s" href="#%[1]s">%[2]s</a> 关闭此åˆå¹¶è¯·æ±‚ `
pulls.reopened_at=`釿–°æ‰“开此åˆå¹¶è¯·æ±‚ <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-pulls.cmd_instruction_hint=`查看 <a class="show-instruction">命令行æç¤º</a>。`
+pulls.cmd_instruction_hint=查看命令行æç¤º
pulls.cmd_instruction_checkout_title=检出
-pulls.cmd_instruction_checkout_desc=ä»Žä½ çš„ä»“åº“ä¸­æ£€å‡ºä¸€ä¸ªæ–°çš„åˆ†æ”¯å¹¶æµ‹è¯•å˜æ›´ã€‚
+pulls.cmd_instruction_checkout_desc=ä»Žæ‚¨çš„ä»“åº“ä¸­æ£€å‡ºä¸€ä¸ªæ–°çš„åˆ†æ”¯å¹¶æµ‹è¯•å˜æ›´ã€‚
pulls.cmd_instruction_merge_title=åˆå¹¶
pulls.cmd_instruction_merge_desc=åˆå¹¶å˜æ›´å¹¶æ›´æ–°åˆ° Gitea 上
-pulls.cmd_instruction_merge_warning=警告:此æ“作ä¸èƒ½åˆå¹¶è¯¥åˆå¹¶è¯·æ±‚,因为“自动检测手动åˆå¹¶â€æœªå¯ç”¨
+pulls.cmd_instruction_merge_warning=警告:此æ“作ä¸èƒ½åˆå¹¶è¯¥åˆå¹¶è¯·æ±‚,因为「自动检测手动åˆå¹¶ã€æœªå¯ç”¨
pulls.clear_merge_message=清除åˆå¹¶ä¿¡æ¯
-pulls.clear_merge_message_hint=清除åˆå¹¶æ¶ˆæ¯åªä¼šåˆ é™¤æäº¤æ¶ˆæ¯å†…容,并ä¿ç•™ç”Ÿæˆçš„ git 附加内容,如“Co-Authored-By …â€ã€‚
+pulls.clear_merge_message_hint=清除åˆå¹¶æ¶ˆæ¯åªä¼šåˆ é™¤æäº¤æ¶ˆæ¯å†…容,并ä¿ç•™ç”Ÿæˆçš„ Git 附加内容,如「Co-Authored-By…ã€ã€‚
pulls.auto_merge_button_when_succeed=(当检查æˆåŠŸæ—¶)
pulls.auto_merge_when_succeed=在所有检查æˆåŠŸåŽè‡ªåЍåˆå¹¶
@@ -1942,15 +1987,16 @@ pulls.auto_merge_newly_scheduled_comment=`已于 %[1]s 设置此åˆå¹¶è¯·æ±‚在æ
pulls.auto_merge_canceled_schedule_comment=`已于 %[1]s å–æ¶ˆäº†è‡ªåЍåˆå¹¶è®¾ç½® `
pulls.delete.title=删除此åˆå¹¶è¯·æ±‚?
-pulls.delete.text=你真的è¦åˆ é™¤è¿™ä¸ªåˆå¹¶è¯·æ±‚å—? (这将永久删除所有内容。如果你打算将内容存档,请考虑关闭它)
+pulls.delete.text=您真的è¦åˆ é™¤è¿™ä¸ªåˆå¹¶è¯·æ±‚å—?(这将永久删除所有内容。如果您打算将内容存档,请考虑关闭它)
pulls.recently_pushed_new_branches=您已ç»äºŽ%[2]s推é€äº†åˆ†æ”¯ <strong>%[1]s</strong>
pulls.upstream_diverging_prompt_behind_1=该分支è½åŽäºŽ %[2]s %[1]d 个æäº¤
pulls.upstream_diverging_prompt_behind_n=该分支è½åŽäºŽ %[2]s %[1]d 个æäº¤
pulls.upstream_diverging_prompt_base_newer=基础分支 %s 有新的更改
pulls.upstream_diverging_merge=åŒæ­¥æ´¾ç”Ÿ
+pulls.upstream_diverging_merge_confirm=è¦å°† %[1]s åˆå¹¶åˆ° %[2]s å—?
-pull.deleted_branch=(已删除): %s
+pull.deleted_branch=(已删除):%s
pull.agit_documentation=查看有关 AGit 的文档
comments.edit.already_changed=无法ä¿å­˜å¯¹è¯„论的更改。其内容似乎已被其他用户更改。 请刷新页é¢å¹¶é‡æ–°ç¼–辑以é¿å…覆盖他们的更改
@@ -1959,7 +2005,7 @@ milestones.new=新的里程碑
milestones.closed=于 %s关闭
milestones.update_ago=已更新 %s
milestones.no_due_date=暂无截止日期
-milestones.open=å¼€å¯
+milestones.open=打开
milestones.close=关闭
milestones.new_subheader=里程碑å¯ä»¥å¸®åŠ©æ‚¨ç»„ç»‡å·¥å•并跟踪其进度。
milestones.completeness=<strong>%d%%</strong> 已完æˆ
@@ -1968,16 +2014,16 @@ milestones.title=标题
milestones.desc=æè¿°
milestones.due_date=截止日期(å¯é€‰ï¼‰
milestones.clear=清除
-milestones.invalid_due_date_format=到期时间的格å¼å¿…须是 'yyyy-mm-dd' 的形å¼ã€‚
-milestones.create_success=里程碑 %s 创建æˆåŠŸã€‚
+milestones.invalid_due_date_format=到期时间的格å¼å¿…须是「yyyy-mm-ddã€çš„å½¢å¼ã€‚
+milestones.create_success=里程碑「%sã€åˆ›å»ºæˆåŠŸã€‚
milestones.edit=编辑里程碑
milestones.edit_subheader=里程碑组织工å•,åˆå¹¶è¯·æ±‚和跟踪进度。
milestones.cancel=å–æ¶ˆ
milestones.modify=更新里程碑
-milestones.edit_success=里程碑 %s å·²ç»æ›´æ–°ã€‚
+milestones.edit_success=里程碑「%sã€æ›´æ–°æˆåŠŸã€‚
milestones.deletion=删除里程碑
milestones.deletion_desc=删除该里程碑将会移除所有工å•中相关的信æ¯ã€‚是å¦ç»§ç»­ï¼Ÿ
-milestones.deletion_success=里程碑已被删除。
+milestones.deletion_success=里程碑已删除。
milestones.filter_sort.name=åç§°
milestones.filter_sort.earliest_due_data=到期日从远到近
milestones.filter_sort.latest_due_date=到期日从近到远
@@ -1986,7 +2032,7 @@ milestones.filter_sort.most_complete=完æˆåº¦ä»Žé«˜åˆ°ä½Ž
milestones.filter_sort.most_issues=å·¥å•从多到少
milestones.filter_sort.least_issues=å·¥å•从少到多
-signing.will_sign=这个æäº¤å°†ç”¨å¯†é’¥ "%s" ç­¾å。
+signing.will_sign=这个æäº¤å°†ç”¨å¯†é’¥ã€Œ%sã€ç­¾å。
signing.wont_sign.error=在检查æäº¤æ˜¯å¦å¯ä»¥è¢«ç­¾å时出错。
signing.wont_sign.nokey=没有å¯ç”¨çš„密钥æ¥ç­¾ç½²è¿™ä¸ªæäº¤ã€‚
signing.wont_sign.never=æäº¤ä»Žæœªç­¾å。
@@ -2001,11 +2047,11 @@ signing.wont_sign.approved=åˆå¹¶å°†ä¸ä¼šè¢«ç­¾å,因为åˆå¹¶è¯·æ±‚未被æ‰
signing.wont_sign.not_signed_in=您还没有登录。
ext_wiki=访问外部百科
-ext_wiki.desc=链接到外部 wiki。
+ext_wiki.desc=链接到外部百科。
wiki=百科
-wiki.welcome=欢迎æ¥åˆ°ç™¾ç§‘!
-wiki.welcome_desc=百科å…许你撰写和与å作者分享文档
+wiki.welcome=欢迎æ¥åˆ°ç™¾ç§‘。
+wiki.welcome_desc=百科å…许您撰写和与å作者分享文档。
wiki.desc=撰写和与å作者分享文档
wiki.create_first_page=创建第一个页é¢
wiki.page=页é¢
@@ -2022,98 +2068,105 @@ wiki.file_revision=页é¢åކå²
wiki.wiki_page_revisions=页é¢åކå²
wiki.back_to_wiki=返回百科
wiki.delete_page_button=删除页é¢
-wiki.delete_page_notice_1=ç™¾ç§‘é¡µé¢ %s åˆ é™¤åŽæ— æ³•æ¢å¤ï¼Œæ˜¯å¦ç»§ç»­ï¼Ÿ
+wiki.delete_page_notice_1=百科页é¢ã€Œ%sã€åˆ é™¤åŽæ— æ³•æ¢å¤ï¼Œæ˜¯å¦ç»§ç»­ï¼Ÿ
wiki.page_already_exists=相åŒåç§°çš„ Wiki 页é¢å·²ç»å­˜åœ¨ã€‚
-wiki.reserved_page=百科页é¢åç§° %s 是被ä¿ç•™çš„。
+wiki.reserved_page=百科页é¢å称「%sã€æ˜¯ä¿ç•™çš„。
wiki.pages=所有页é¢
wiki.last_updated=æœ€åŽæ›´æ–°äºŽ %s
-wiki.page_name_desc=输入此 Wiki 页é¢çš„å称。特殊å称有:'Home', '_Sidebar' å’Œ '_Footer'。
+wiki.page_name_desc=输入此百科页é¢çš„å称。特殊å称有:「Homeã€ï¼Œã€Œ_Sidebarã€å’Œã€Œ_Footerã€ã€‚
wiki.original_git_entry_tooltip=查看原始的 Git æ–‡ä»¶è€Œä¸æ˜¯ä½¿ç”¨å‹å¥½é“¾æŽ¥ã€‚
-activity=动æ€
+activity=活动
activity.navbar.pulse=活动
activity.navbar.code_frequency=代ç é¢‘率
activity.navbar.contributors=贡献者
-activity.navbar.recent_commits=最近的æäº¤
+activity.navbar.recent_commits=最近æäº¤
activity.period.filter_label=周期:
activity.period.daily=1 天
activity.period.halfweekly=3 天
-activity.period.weekly=1周
-activity.period.monthly=1 个月
-activity.period.quarterly=3个月
-activity.period.semiyearly=6 个月
-activity.period.yearly=1å¹´
+activity.period.weekly=1 周
+activity.period.monthly=1 月
+activity.period.quarterly=3 月
+activity.period.semiyearly=6 月
+activity.period.yearly=1 å¹´
activity.overview=概览
-activity.active_prs_count_1=<strong>%d</strong> åˆå¹¶è¯·æ±‚
-activity.active_prs_count_n=<strong>%d</strong> åˆå¹¶è¯·æ±‚
-activity.merged_prs_count_1=åˆå¹¶è¯·æ±‚
-activity.merged_prs_count_n=åˆå¹¶è¯·æ±‚
-activity.opened_prs_count_1=æ–°åˆå¹¶è¯·æ±‚
-activity.opened_prs_count_n=æ–°åˆå¹¶è¯·æ±‚
-activity.title.user_1=%d 用户
-activity.title.user_n=%d 用户
-activity.title.prs_1=%d åˆå¹¶è¯·æ±‚
-activity.title.prs_n=%d åˆå¹¶è¯·æ±‚
+activity.active_prs_count_1=<strong>%d</strong> 个åˆå¹¶è¯·æ±‚
+activity.active_prs_count_n=<strong>%d</strong> 个åˆå¹¶è¯·æ±‚
+activity.merged_prs_count_1=å·²åˆå¹¶çš„åˆå¹¶è¯·æ±‚
+activity.merged_prs_count_n=å·²åˆå¹¶çš„åˆå¹¶è¯·æ±‚
+activity.opened_prs_count_1=创建的åˆå¹¶è¯·æ±‚
+activity.opened_prs_count_n=创建的åˆå¹¶è¯·æ±‚
+activity.title.user_1=%d ä½ç”¨æˆ·
+activity.title.user_n=%d ä½ç”¨æˆ·
+activity.title.prs_1=%d 个åˆå¹¶è¯·æ±‚
+activity.title.prs_n=%d 个åˆå¹¶è¯·æ±‚
activity.title.prs_merged_by=%[2]s ç”± %[1]s åˆå¹¶
activity.title.prs_opened_by=%[2]s 创建了 %[1]s
activity.merged_prs_label=å·²åˆå¹¶
activity.opened_prs_label=已创建
-activity.active_issues_count_1=<strong>%d</strong> å·¥å•
-activity.active_issues_count_n=<strong>%d</strong> å·¥å•
+activity.active_issues_count_1=<strong>%d</strong> å¼ å·¥å•
+activity.active_issues_count_n=<strong>%d</strong> å¼ å·¥å•
activity.closed_issues_count_1=已关闭的工å•
activity.closed_issues_count_n=已关闭的工å•
-activity.title.issues_1=%d å·¥å•
-activity.title.issues_n=%d å·¥å•
-activity.title.issues_closed_from=%s 从 %s 被关闭
+activity.title.issues_1=%d å¼ å·¥å•
+activity.title.issues_n=%d å¼ å·¥å•
+activity.title.issues_closed_from=%s 从 %s 关闭
activity.title.issues_created_by=%[2]s 创建了 %[1]s
activity.closed_issue_label=已关闭
-activity.new_issues_count_1=创建工å•
-activity.new_issues_count_n=创建工å•
+activity.new_issues_count_1=已打开的工å•
+activity.new_issues_count_n=已打开的工å•
activity.new_issue_label=打开的
activity.title.unresolved_conv_1=%d 未解决的会è¯
activity.title.unresolved_conv_n=%d 未解决的会è¯
activity.unresolved_conv_desc=这些最近更新的工å•å’Œåˆå¹¶è¯·æ±‚还没有解决。
activity.unresolved_conv_label=打开
-activity.title.releases_1=%d 版本å‘布
-activity.title.releases_n=%d 版本å‘布
+activity.title.releases_1=%d 个å‘布
+activity.title.releases_n=%d 个å‘布
activity.title.releases_published_by=%[2]s å‘布了 %[1]s
activity.published_release_label=å·²å‘布
activity.no_git_activity=在此期间没有任何æäº¤æ´»åŠ¨ã€‚
-activity.git_stats_exclude_merges=排除åˆå¹¶ï¼Œ
-activity.git_stats_author_1=%d 作者
-activity.git_stats_author_n=%d 作者
-activity.git_stats_pushed_1=å·²ç»æŽ¨é€
-activity.git_stats_pushed_n=å·²ç»æŽ¨é€
-activity.git_stats_commit_1=%d æäº¤
-activity.git_stats_commit_n=%d æäº¤
+activity.git_stats_exclude_merges=排除åˆå¹¶åŽï¼Œ
+activity.git_stats_author_1=%d ä½ä½œè€…
+activity.git_stats_author_n=%d ä½ä½œè€…
+activity.git_stats_pushed_1=已推é€
+activity.git_stats_pushed_n=已推é€
+activity.git_stats_commit_1=%d 次æäº¤
+activity.git_stats_commit_n=%d 次æäº¤
activity.git_stats_push_to_branch=到 %s 和
activity.git_stats_push_to_all_branches=到所有分支。
activity.git_stats_on_default_branch=在 %s 上,
-activity.git_stats_file_1=%d 文件
-activity.git_stats_file_n=%d 文件
-activity.git_stats_files_changed_1=å·²ç»æ”¹å˜
-activity.git_stats_files_changed_n=å·²ç»æ”¹å˜
-activity.git_stats_additions=而且
-activity.git_stats_addition_1=新增 %d 行
-activity.git_stats_addition_n=新增 %d 行
+activity.git_stats_file_1=%d 个文件
+activity.git_stats_file_n=%d 个文件
+activity.git_stats_files_changed_1=已修改
+activity.git_stats_files_changed_n=已修改
+activity.git_stats_additions=并且已有
+activity.git_stats_addition_1=%d 行新增
+activity.git_stats_addition_n=%d 行新增
activity.git_stats_and_deletions=和
-activity.git_stats_deletion_1=删除 %d 行
-activity.git_stats_deletion_n=删除 %d 行
+activity.git_stats_deletion_1=%d 行删除
+activity.git_stats_deletion_n=%d 行删除
contributors.contribution_type.filter_label=贡献类型:
contributors.contribution_type.commits=æäº¤
-contributors.contribution_type.additions=更多
+contributors.contribution_type.additions=新增
contributors.contribution_type.deletions=删除
settings=设置
-settings.desc=设置是你å¯ä»¥ç®¡ç†ä»“库设置的地方
+settings.desc=设置是您å¯ä»¥ç®¡ç†ä»“库设置的地方
settings.options=仓库
+settings.public_access=公开访问
+settings.public_access_desc=é…置公共访客访问æƒé™ä»¥è¦†ç›–此仓库的默认值。
+settings.public_access.docs.not_set=未设置:没有é¢å¤–的公共访问æƒé™ã€‚访客æƒé™éµå¾ªä»“库的å¯è§æ€§å’Œæˆå‘˜æƒé™ã€‚
+settings.public_access.docs.anonymous_read=匿åå¯è¯»ï¼šæœªç™»å½•的用户å¯ä»¥é€šè¿‡è¯»å–æƒé™è®¿é—®å•元。
+settings.public_access.docs.everyone_read=所有人å¯è¯»ï¼šæ‰€æœ‰ç™»å½•用户都å¯ä»¥é€šè¿‡è¯»å–æƒé™è®¿é—®å•元。读å–问题/拉å–请求å•元的æƒé™ä¹Ÿæ„味ç€ç”¨æˆ·å¯ä»¥åˆ›å»ºæ–°çš„问题/拉å–请求。
+settings.public_access.docs.everyone_write=所有人å¯å†™ï¼šæ‰€æœ‰ç™»å½•用户都有写入æƒé™ã€‚åªæœ‰ç™¾ç§‘æ”¯æŒæ­¤æƒé™ã€‚
settings.collaboration=å作者
settings.collaboration.admin=管ç†å‘˜
settings.collaboration.write=å¯å†™æƒé™
settings.collaboration.read=å¯è¯»æƒé™
settings.collaboration.owner=所有者
settings.collaboration.undefined=未定义
+settings.collaboration.per_unit=å•å…ƒæƒé™
settings.hooks=Web é’©å­
settings.githooks=ç®¡ç† Git é’©å­
settings.basic_settings=基本设置
@@ -2121,14 +2174,14 @@ settings.mirror_settings=镜åƒè®¾ç½®
settings.mirror_settings.docs=è®¾ç½®æ‚¨çš„ä»“åº“ä»¥è‡ªåŠ¨åŒæ­¥å¦ä¸€ä¸ªä»“库的æäº¤ã€æ ‡ç­¾å’Œåˆ†æ”¯ã€‚
settings.mirror_settings.docs.disabled_pull_mirror.instructions=设置您的项目以自动将æäº¤ã€æ ‡ç­¾å’Œåˆ†æ”¯æŽ¨é€åˆ°å¦ä¸€ä¸ªä»“库。您的站点管ç†å‘˜å·²ç¦ç”¨äº†æ‹‰å–镜åƒã€‚
settings.mirror_settings.docs.disabled_push_mirror.instructions=å°†æ‚¨çš„é¡¹ç›®è®¾ç½®ä¸ºè‡ªåŠ¨ä»Žä¸€ä¸ªä»“åº“æ‹‰å–æäº¤ã€æ ‡ç­¾å’Œåˆ†æ”¯ã€‚
-settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning=现在,这åªèƒ½åœ¨â€œè¿ç§»å¤–部仓库â€èœå•中完æˆã€‚欲了解更多信æ¯ï¼Œè¯·å’¨è¯¢ï¼š
+settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning=现在,这åªèƒ½åœ¨ã€Œè¿ç§»å¤–部仓库ã€èœå•中完æˆã€‚欲了解更多信æ¯ï¼Œè¯·å‚考:
settings.mirror_settings.docs.disabled_push_mirror.info=您的站点管ç†å‘˜å·²ç¦ç”¨æŽ¨é€é•œåƒã€‚
settings.mirror_settings.docs.no_new_mirrors=您的仓库将镜åƒåŒæ­¥å¦ä¸€ä¸ªä»“库的更改。请注æ„,您现在ä¸èƒ½åˆ›å»ºä»»ä½•新的镜åƒã€‚
settings.mirror_settings.docs.can_still_use=虽然您ä¸èƒ½ä¿®æ”¹çŽ°æœ‰é•œåƒæˆ–创建新镜åƒï¼Œä½†æ‚¨ä»ç„¶å¯ä»¥ä½¿ç”¨å·²å­˜åœ¨çš„镜åƒã€‚
settings.mirror_settings.docs.pull_mirror_instructions=è¦åˆ›å»ºä¸€ä¸ªæ‹‰å–镜åƒï¼Œè¯·å‚阅:
settings.mirror_settings.docs.more_information_if_disabled=您å¯ä»¥åœ¨è¿™é‡Œæ‰¾åˆ°æ›´å¤šå…³äºŽæŽ¨é€å’Œæ‹‰å–镜åƒçš„ä¿¡æ¯ï¼š
settings.mirror_settings.docs.doc_link_title=如何镜åƒä»“库?
-settings.mirror_settings.docs.doc_link_pull_section=文档中的 “从远程仓库拉å–†部分。
+settings.mirror_settings.docs.doc_link_pull_section=文档中的「从远程仓库拉å–ã€éƒ¨åˆ†ã€‚
settings.mirror_settings.docs.pulling_remote_title=从远程仓库拉å–代ç 
settings.mirror_settings.mirrored_repository=镜åƒåº“
settings.mirror_settings.pushed_repository=推é€ä»“库
@@ -2154,25 +2207,24 @@ settings.advanced_settings=高级设置
settings.wiki_desc=å¯ç”¨ä»“库百科
settings.use_internal_wiki=使用内置百科
settings.default_wiki_branch_name=默认百科分支åç§°
-settings.default_wiki_everyone_access=登录用户的默认访问æƒé™ï¼š
settings.failed_to_change_default_wiki_branch=更改百科默认分支失败。
settings.use_external_wiki=使用外部百科
-settings.external_wiki_url=外部 Wiki 链接
+settings.external_wiki_url=外部百科链接
settings.external_wiki_url_error=外部百科链接无效
-settings.external_wiki_url_desc=当点击百科标签时,访问者将被é‡å®šå‘到外部百科系统的URL。
+settings.external_wiki_url_desc=当点击百科标签时,访问者将被é‡å®šå‘到外部百科系统的 URL。
settings.issues_desc=å¯ç”¨å·¥å•系统
-settings.use_internal_issue_tracker=使用内置的轻é‡çº§å·¥å•管ç†ç³»ç»Ÿ
-settings.use_external_issue_tracker=使用外部的工å•管ç†ç³»ç»Ÿ
+settings.use_internal_issue_tracker=使用内置工å•系统
+settings.use_external_issue_tracker=使用外部工å•系统
settings.external_tracker_url=外部工å•系统 URL
settings.external_tracker_url_error=外部百科链接无效
-settings.external_tracker_url_desc=å½“ç‚¹å‡»å·¥å•æ ‡ç­¾æ—¶ï¼Œè®¿é—®è€…将被é‡å®šå‘到外部工å•系统的URL。
+settings.external_tracker_url_desc=å½“ç‚¹å‡»å·¥å•æ ‡ç­¾æ—¶ï¼Œè®¿é—®è€…将被é‡å®šå‘到外部工å•系统的 URL。
settings.tracker_url_format=外部工å•管ç†ç³»ç»Ÿçš„ URL æ ¼å¼
settings.tracker_url_format_error=外部工å•链接无效
settings.tracker_issue_style=外部工å•管ç†ç³»ç»Ÿçš„ç¼–å·æ ¼å¼
settings.tracker_issue_style.numeric=纯数字形å¼
settings.tracker_issue_style.alphanumeric=è‹±æ–‡å­—æ¯æ•°å­—组åˆå½¢å¼
settings.tracker_issue_style.regexp=正则表达å¼
-settings.tracker_issue_style.regexp_pattern=æ­£åˆ™è¡¨è¾¾å¼æ¨¡å¼
+settings.tracker_issue_style.regexp_pattern=正则表达å¼
settings.tracker_issue_style.regexp_pattern_desc=第一个被æ•获的组将å–代 <code>{index}</code>。
settings.tracker_url_format_desc=使用å ä½ç¬¦ <code>{user}</code>, <code>{repo}</code> å’Œ <code>{index}</code> 作为用户åã€ä»“库å和工å•索引。
settings.enable_timetracker=å¯ç”¨æ—¶é—´è·Ÿè¸ª
@@ -2182,15 +2234,15 @@ settings.pulls.ignore_whitespace=忽略空白冲çª
settings.pulls.enable_autodetect_manual_merge=å¯ç”¨è‡ªåŠ¨æ£€æµ‹æ‰‹åŠ¨åˆå¹¶ (注æ„:在æŸäº›ç‰¹æ®Šæƒ…况下å¯èƒ½å‘生错误判断)
settings.pulls.allow_rebase_update=å…许通过å˜åŸºæ›´æ–°åˆå¹¶è¯·æ±‚分支
settings.pulls.default_delete_branch_after_merge=默认åˆå¹¶åŽåˆ é™¤åˆå¹¶è¯·æ±‚分支
-settings.pulls.default_allow_edits_from_maintainers=默认开å¯å…许维护者编辑
-settings.releases_desc=å¯ç”¨å‘布
+settings.pulls.default_allow_edits_from_maintainers=默认å…许维护者编辑
+settings.releases_desc=å¯ç”¨ä»“库å‘布
settings.packages_desc=å¯ç”¨ä»“库软件包注册中心
settings.projects_desc=å¯ç”¨é¡¹ç›®
settings.projects_mode_desc=é¡¹ç›®æ¨¡å¼ (è¦æ˜¾ç¤ºçš„项目类型)
settings.projects_mode_repo=仅仓库项目
settings.projects_mode_owner=ä»…é™ç”¨æˆ·æˆ–组织项目
settings.projects_mode_all=所有项目
-settings.actions_desc=å¯ç”¨ Actions
+settings.actions_desc=å¯ç”¨ä»“库工作æµ
settings.admin_settings=管ç†å‘˜è®¾ç½®
settings.admin_enable_health_check=å¯ç”¨ä»“库å¥åº·æ£€æŸ¥ (git fsck)
settings.admin_code_indexer=代ç ç´¢å¼•器
@@ -2213,23 +2265,23 @@ settings.convert_fork_notices_1=该æ“作会将派生仓库转æ¢ä¸ºæ™®é€šä»“库
settings.convert_fork_confirm=转æ¢ä»“库
settings.convert_fork_succeed=此派生仓库已ç»è½¬æ¢ä¸ºæ™®é€šä»“库。
settings.transfer=转移仓库所有æƒ
-settings.transfer.rejected=代ç åº“转移被拒ç»ã€‚
-settings.transfer.success=代ç åº“转移æˆåŠŸã€‚
-settings.transfer.blocked_user=无法传输仓库,因为您被新的所有者å±è”½ã€‚
+settings.transfer.rejected=仓库转移被拒ç»ã€‚
+settings.transfer.success=仓库转移æˆåŠŸã€‚
+settings.transfer.blocked_user=无法转移仓库,因为您已被新所有者å±è”½ã€‚
settings.transfer_abort=å–æ¶ˆè½¬ç§»
-settings.transfer_abort_invalid=ä½ ä¸èƒ½å–消ä¸å­˜åœ¨çš„代ç åº“转移。
-settings.transfer_abort_success=æˆåŠŸå–æ¶ˆäº†å°†ä»£ç åº“转让给 %s。
+settings.transfer_abort_invalid=您ä¸èƒ½å–消ä¸å­˜åœ¨çš„仓库转移。
+settings.transfer_abort_success=æˆåŠŸå–æ¶ˆå°†ä»“库转移给 %s。
settings.transfer_desc=您å¯ä»¥å°†ä»“库转移至您拥有管ç†å‘˜æƒé™çš„叿ˆ·æˆ–组织。
-settings.transfer_form_title=输入仓库å称以åšç¡®è®¤:
-settings.transfer_in_progress=当剿­£åœ¨è¿›è¡Œè½¬è®©ã€‚ 如果你想将此代ç åº“转让给å¦ä¸€ä¸ªç”¨æˆ·ï¼Œè¯·å–消它。
-settings.transfer_notices_1=- 如果将此仓库转移给其他用户, 您将失去对此仓库的访问æƒé™ã€‚
-settings.transfer_notices_2=-如果将其转移到您 (å…±åŒ) 拥有的组织,您å¯ä»¥ç»§ç»­è®¿é—®è¯¥ä»“库。
-settings.transfer_notices_3=- å¦‚æžœä»“åº“æ˜¯ç§æœ‰çš„并且被转移给æŸä¸ªç”¨æˆ·ï¼Œé‚£ä¹ˆæ­¤æ“作å¯ä»¥ç¡®ä¿è¯¥ç”¨æˆ·è‡³å°‘具有读æƒé™(以åŠå¿…è¦æ—¶çš„æ›´æ”¹æƒé™)。
-settings.transfer_notices_4=- 如果存储库属于æŸä¸ªç»„织,而您将其转移给å¦ä¸€ä¸ªç»„织或个人,那么您将失去存储库工å•与其组织项目系统之间的链接。
+settings.transfer_form_title=输入仓库å称以确认:
+settings.transfer_in_progress=当剿­£åœ¨è¿›è¡Œè½¬ç§»ã€‚ 如果您想将此仓库转移给å¦ä¸€ä¸ªç”¨æˆ·ï¼Œè¯·å–消它。
+settings.transfer_notices_1=- 如果将此仓库转移给其他用户,您将失去对此仓库的访问æƒé™ã€‚
+settings.transfer_notices_2=- 如果将其转移到您(共åŒï¼‰æ‹¥æœ‰çš„组织,您å¯ä»¥ç»§ç»­è®¿é—®è¯¥ä»“库。
+settings.transfer_notices_3=- å¦‚æžœä»“åº“æ˜¯ç§æœ‰çš„并且被转移给æŸä¸ªç”¨æˆ·ï¼Œé‚£ä¹ˆæ­¤æ“作å¯ä»¥ç¡®ä¿è¯¥ç”¨æˆ·è‡³å°‘具有读æƒé™ï¼ˆä»¥åŠå¿…è¦æ—¶çš„æ›´æ”¹æƒé™ï¼‰ã€‚
+settings.transfer_notices_4=- 如果仓库属于æŸä¸ªç»„织,而您将其转移给å¦ä¸€ä¸ªç»„织或个人,那么您将失去仓库工å•与其组织项目系统之间的链接。
settings.transfer_owner=新拥有者
-settings.transfer_perform=执行转让
-settings.transfer_started=该代ç åº“已被标记为转让并等待æ¥è‡ª %s 的确认
-settings.transfer_succeed=仓库已被转移。
+settings.transfer_perform=执行转移
+settings.transfer_started=该仓库已标记为转移并等待æ¥è‡ª %s 的确认
+settings.transfer_succeed=仓库已转移。
settings.signing_settings=ç­¾å验è¯è®¾ç½®
settings.trust_model=ç­¾å信任模型
settings.trust_model.default=默认信任模型
@@ -2239,7 +2291,7 @@ settings.trust_model.collaborator.long=å作者:信任å作者的签å
settings.trust_model.collaborator.desc=此仓库中å作者的有效签å将被标记为「å¯ä¿¡ã€(æ— è®ºå®ƒä»¬æ˜¯å¦æ˜¯æäº¤è€…),签ååªç¬¦åˆæäº¤è€…时将标记为「ä¸å¯ä¿¡ã€ï¼Œéƒ½ä¸åŒ¹é…时标记为「ä¸åŒ¹é…ã€ã€‚
settings.trust_model.committer=æäº¤è€…
settings.trust_model.committer.long=æäº¤è€…: 信任与æäº¤è€…相符的签å (此特性类似 GitHub,这会强制采用 Gitea 作为æäº¤è€…和签å者)
-settings.trust_model.committer.desc=有效签ååªæœ‰å’Œæäº¤è€…ç›¸åŒ¹é…æ‰ä¼šè¢«æ ‡è®°ä¸ºâ€œå—ä¿¡ä»»â€ï¼Œå¦åˆ™å®ƒä»¬å°†è¢«æ ‡è®°ä¸ºâ€œä¸åŒ¹é…â€ã€‚这强制 Gitea æˆä¸ºç­¾åæäº¤çš„æäº¤è€…,而实际æäº¤è€…被加上 Co-authored-by: å’Œ Co-committed-by: 的标记。 默认的 Gitea å¯†é’¥å¿…é¡»åŒ¹é…æ•°æ®åº“中的一å用户。
+settings.trust_model.committer.desc=有效签ååªæœ‰å’Œæäº¤è€…ç›¸åŒ¹é…æ‰ä¼šè¢«æ ‡è®°ä¸ºã€Œå—ä¿¡ä»»ã€ï¼Œå¦åˆ™å®ƒä»¬å°†è¢«æ ‡è®°ä¸ºã€Œä¸åŒ¹é…ã€ã€‚这强制 Gitea æˆä¸ºç­¾åæäº¤çš„æäº¤è€…,而实际æäº¤è€…被加上 Co-authored-by: å’Œ Co-committed-by: 的标记。 默认的 Gitea å¯†é’¥å¿…é¡»åŒ¹é…æ•°æ®åº“中的一å用户。
settings.trust_model.collaboratorcommitter=å作者+æäº¤è€…
settings.trust_model.collaboratorcommitter.long=å作者+æäº¤è€…:信任åä½œè€…åŒæ—¶æ˜¯æäº¤è€…的签å
settings.trust_model.collaboratorcommitter.desc=此仓库中å作者的有效签ååœ¨ä»–åŒæ—¶æ˜¯æäº¤è€…时将被标记为「å¯ä¿¡ã€ï¼Œç­¾ååªåŒ¹é…了æäº¤è€…时将标记为「ä¸å¯ä¿¡ã€ï¼Œéƒ½ä¸åŒ¹é…时标记为「ä¸åŒ¹é…ã€ã€‚这会强制 Gitea æˆä¸ºç­¾å者和æäº¤è€…,实际的æäº¤è€…将被标记于æäº¤æ¶ˆæ¯ç»“尾处的「Co-Authored-By:ã€å’Œã€ŒCo-Committed-By:ã€ã€‚默认的 Gitea ç­¾åå¯†é’¥å¿…é¡»åŒ¹é…æ•°æ®åº“中的一个用户密钥。
@@ -2250,19 +2302,19 @@ settings.confirm_wiki_delete=删除百科数æ®
settings.wiki_deletion_success=仓库百科数æ®åˆ é™¤æˆåŠŸï¼
settings.delete=删除本仓库
settings.delete_desc=删除仓库是永久性的, 无法撤消。
-settings.delete_notices_1=- æ­¤æ“作 <strong>ä¸å¯ä»¥</strong> 被回滚。
+settings.delete_notices_1=- æ­¤æ“作 <strong>无法</strong> 被回滚。
settings.delete_notices_2=- æ­¤æ“作将永久删除仓库 <strong>%s</strong>,包括 Git æ•°æ®ã€ å·¥å•ã€è¯„论ã€ç™¾ç§‘å’Œå作者的æ“作æƒé™ã€‚
settings.delete_notices_fork_1=- 在此仓库删除åŽï¼Œå®ƒçš„æ´¾ç”Ÿä»“åº“å°†å˜æˆç‹¬ç«‹ä»“库。
-settings.deletion_success=仓库已被删除。
+settings.deletion_success=仓库已删除。
settings.update_settings_success=仓库设置已更新。
-settings.update_settings_no_unit=该代ç åº“应该至少å…许æŸç§å½¢å¼çš„交互。
+settings.update_settings_no_unit=该仓库应至少å…许æŸç§å½¢å¼çš„交互。
settings.confirm_delete=删除本仓库
settings.add_collaborator=增加å作者
settings.add_collaborator_success=å作者添加æˆåŠŸï¼
settings.add_collaborator_inactive_user=无法添加未激活的用户作为åˆä½œè€…。
settings.add_collaborator_owner=ä¸èƒ½å°†æ‰€æœ‰è€…添加为å作者。
-settings.add_collaborator_duplicate=åˆä½œè€…å·²ç»è¢«æ·»åŠ åˆ°æœ¬ä»“åº“ã€‚
-settings.add_collaborator.blocked_user=此写作者被仓库所有者å±è”½ï¼Œå之亦然。
+settings.add_collaborator_duplicate=åä½œè€…å·²ç»æ·»åŠ åˆ°æœ¬ä»“åº“ã€‚
+settings.add_collaborator.blocked_user=æ­¤å作者被仓库所有者å±è”½ï¼Œå之亦然。
settings.delete_collaborator=删除
settings.collaborator_deletion=删除å作者
settings.collaborator_deletion_desc=删除å作者åŽä»–将无法å†å¯¹æ­¤ä»“库的访问。继续?
@@ -2276,25 +2328,23 @@ settings.add_team_duplicate=å›¢é˜Ÿå·²ç»æ‹¥æœ‰ä»“库
settings.add_team_success=团队现在å¯ä»¥è®¿é—®ä»“库。
settings.change_team_permission_tip=团队æƒé™è®¾ç½®äºŽå›¢é˜Ÿè®¾ç½®é¡µé¢ï¼Œä¸èƒ½æ ¹æ®ä»“库更改
settings.delete_team_tip=è¯¥å›¢é˜Ÿä»æœ‰ä»“库, 无法删除
-settings.remove_team_success=团队访问仓库的æƒé™å·²è¢«åˆ é™¤ã€‚
+settings.remove_team_success=团队访问仓库的æƒé™å·²åˆ é™¤ã€‚
settings.add_webhook=添加 Web é’©å­
-settings.add_webhook.invalid_channel_name=Webhook 通é“åç§°ä¸èƒ½ä¸ºç©ºä¸”ä¸èƒ½ä»…包å«ä¸€ä¸ª # 字符。
-settings.hooks_desc=当Gitea事件å‘生时,Webé’©å­è‡ªåЍå‘出HTTP POST请求。在 <a target="_blank" rel="noopener noreferrer" href="%s"> 指å—</a> 中阅读更多内容。
+settings.add_webhook.invalid_channel_name=Web é’©å­é€šé“åç§°ä¸èƒ½ä¸ºç©ºä¸”ä¸èƒ½ä»…包å«ä¸€ä¸ª # 字符。
+settings.hooks_desc=当 Gitea 事件å‘生时,Web é’©å­è‡ªåЍå‘出 HTTP POST 请求。在 <a target="_blank" rel="noopener noreferrer" href="%s"> 指å—</a> 中阅读更多内容。
settings.webhook_deletion=删除 Web é’©å­
-settings.webhook_deletion_desc=删除 webé’©å­ å°†åˆ é™¤å…¶è®¾ç½®å’ŒåŽ†å²è®°å½•。继续?
+settings.webhook_deletion_desc=删除 Web é’©å­å°†åˆ é™¤å…¶è®¾ç½®å’Œåކå²è®°å½•。继续?
settings.webhook_deletion_success=Web é’©å­åˆ é™¤æˆåŠŸï¼
-settings.webhook.test_delivery=测试推é€
-settings.webhook.test_delivery_desc=用å‡äº‹ä»¶æµ‹è¯•这个 webé’©å­ã€‚
-settings.webhook.test_delivery_desc_disabled=è¦ç”¨ 虚å‡äº‹ä»¶ 测试这个Webhook,请激活它。
+settings.webhook.test_delivery_desc_disabled=è¦ç”¨å‡äº‹ä»¶æµ‹è¯•这个 Webé’©å­ï¼Œè¯·æ¿€æ´»å®ƒã€‚
settings.webhook.request=请求内容
settings.webhook.response=å“应内容
settings.webhook.headers=头信æ¯
settings.webhook.payload=内容
settings.webhook.body=å“应体
-settings.webhook.replay.description=釿”¾æ­¤ webhook。
-settings.webhook.replay.description_disabled=è‹¥è¦é‡æ’­æ­¤ WebHook,请激活它。
-settings.webhook.delivery.success=一个事件已被添加到推é€é˜Ÿåˆ—。å¯èƒ½éœ€è¦è¿‡å‡ ç§’é’Ÿæ‰ä¼šæ˜¾ç¤ºåœ¨æŽ¨é€è®°å½•中。
-settings.githooks_desc=Git Hook 是 Git 本身æä¾›çš„功能。您å¯ä»¥åœ¨ä¸‹æ–¹ç¼–辑 hook 文件以设置自定义æ“作。
+settings.webhook.replay.description=釿”¾æ­¤ Web é’©å­ã€‚
+settings.webhook.replay.description_disabled=è‹¥è¦é‡æ–°è¿è¡Œæ­¤ Web é’©å­ï¼Œè¯·æ¿€æ´»å®ƒã€‚
+settings.webhook.delivery.success=一个事件已添加到推é€é˜Ÿåˆ—。å¯èƒ½éœ€è¦è¿‡å‡ ç§’é’Ÿæ‰ä¼šæ˜¾ç¤ºåœ¨æŽ¨é€è®°å½•中。
+settings.githooks_desc=Git é’©å­æ˜¯ Git 本身æä¾›çš„功能。您å¯ä»¥åœ¨ä¸‹æ–¹ç¼–辑 hook 文件以设置自定义æ“作。
settings.githook_edit_desc=å¦‚æžœé’©å­æœªå¯åŠ¨ï¼Œåˆ™ä¼šæ˜¾ç¤ºæ ·ä¾‹æ–‡ä»¶ä¸­çš„å†…å®¹ã€‚å¦‚æžœæƒ³è¦åˆ é™¤æŸä¸ªé’©å­ï¼Œåˆ™æäº¤ç©ºç™½æ–‡æœ¬å³å¯ã€‚
settings.githook_name=é’©å­åç§°
settings.githook_content=é’©å­æ–‡æœ¬
@@ -2302,8 +2352,8 @@ settings.update_githook=æ›´æ–°é’©å­è®¾ç½®
settings.add_webhook_desc=Gitea å°†å‘目标 URL å‘é€å…·æœ‰æŒ‡å®šå†…容类型的 <code>POST</code> 请求。在 <a target="_blank" rel="noopener noreferrer" href="%s">webhooks 指å—</a> 中阅读更多内容。
settings.payload_url=目标 URL
settings.http_method=HTTP 方法
-settings.content_type=POST Content Type
-settings.secret=密钥文本
+settings.content_type=POST 内容类型
+settings.secret=密钥
settings.slack_username=æœåŠ¡åç§°
settings.slack_icon_url=图标 URL
settings.slack_color=颜色
@@ -2319,10 +2369,12 @@ settings.event_create_desc=创建分支或标签
settings.event_delete=刪除
settings.event_delete_desc=分支或标签已删除。
settings.event_fork=派生
-settings.event_fork_desc=仓库被派生。
+settings.event_fork_desc=仓库已派生。
settings.event_wiki=百科
settings.event_wiki_desc=创建ã€é‡å‘½åã€ç¼–辑或删除了百科页é¢ã€‚
-settings.event_release=版本å‘布
+settings.event_statuses=状æ€
+settings.event_statuses_desc=已从 API æ›´æ–°æäº¤çжæ€ã€‚
+settings.event_release=å‘布
settings.event_release_desc=å‘å¸ƒã€æ›´æ–°æˆ–删除版本时。
settings.event_push=推é€
settings.event_force_push=强制推é€
@@ -2333,40 +2385,45 @@ settings.event_header_issue=å·¥å•事件
settings.event_issues=å·¥å•
settings.event_issues_desc=å·¥å•已打开ã€å·²å…³é—­ã€å·²é‡æ–°æ‰“开或已编辑。
settings.event_issue_assign=å·¥å•已指派
-settings.event_issue_assign_desc=å·¥å•å·²è¢«æŒ‡æ´¾æˆ–å–æ¶ˆæŒ‡æ´¾ã€‚
-settings.event_issue_label=已标记工å•
-settings.event_issue_label_desc=工啿 ‡ç­¾è¢«æ›´æ–°æˆ–清除。
-settings.event_issue_milestone=å·¥å•被收入里程碑中
-settings.event_issue_milestone_desc=å·¥å•è¢«æ”¶å…¥æˆ–å–æ¶ˆæ”¶å…¥é‡Œç¨‹ç¢‘中。
+settings.event_issue_assign_desc=å·¥å•å·²æŒ‡æ´¾æˆ–å–æ¶ˆæŒ‡æ´¾ã€‚
+settings.event_issue_label=å·¥å•增删标签
+settings.event_issue_label_desc=工啿 ‡ç­¾å·²æ›´æ–°æˆ–清除。
+settings.event_issue_milestone=å·¥å•已收入里程碑中
+settings.event_issue_milestone_desc=å·¥å•å·²æ”¶å…¥æˆ–å–æ¶ˆæ”¶å…¥é‡Œç¨‹ç¢‘中。
settings.event_issue_comment=å·¥å•评论
-settings.event_issue_comment_desc=å·¥å•评论被创建ã€ç¼–辑或删除
+settings.event_issue_comment_desc=å·¥å•评论已创建ã€ç¼–辑或删除。
settings.event_header_pull_request=åˆå¹¶è¯·æ±‚事件
settings.event_pull_request=åˆå¹¶è¯·æ±‚
-settings.event_pull_request_desc=åˆå¹¶è¯·æ±‚被打开ã€è¢«å…³é—­ã€è¢«é‡æ–°æ‰“开或被编辑。
-settings.event_pull_request_assign=åˆå¹¶è¯·æ±‚被指派
-settings.event_pull_request_assign_desc=åˆå¹¶è¯·æ±‚è¢«æŒ‡æ´¾æˆ–å–æ¶ˆæŒ‡æ´¾ã€‚
-settings.event_pull_request_label=åˆå¹¶è¯·æ±‚被贴上标签
-settings.event_pull_request_label_desc=åˆå¹¶è¯·æ±‚的标签被更新或清除。
-settings.event_pull_request_milestone=åˆå¹¶è¯·æ±‚被记录于里程碑中
-settings.event_pull_request_milestone_desc=åˆå¹¶è¯·æ±‚è¢«è®°å½•æˆ–å–æ¶ˆè®°å½•于里程碑中。
-settings.event_pull_request_comment=åˆå¹¶è¯·æ±‚被评论
-settings.event_pull_request_comment_desc=åˆå¹¶è¯·æ±‚评论被创建ã€ç¼–辑或删除。
+settings.event_pull_request_desc=åˆå¹¶è¯·æ±‚已打开ã€å…³é—­ã€é‡æ–°æ‰“开或编辑。
+settings.event_pull_request_assign=åˆå¹¶è¯·æ±‚已指派
+settings.event_pull_request_assign_desc=åˆå¹¶è¯·æ±‚å·²æŒ‡æ´¾æˆ–å–æ¶ˆæŒ‡æ´¾ã€‚
+settings.event_pull_request_label=åˆå¹¶è¯·æ±‚增删标签
+settings.event_pull_request_label_desc=åˆå¹¶è¯·æ±‚的标签已更新或清除。
+settings.event_pull_request_milestone=åˆå¹¶è¯·æ±‚已记录于里程碑中
+settings.event_pull_request_milestone_desc=åˆå¹¶è¯·æ±‚å·²è®°å½•æˆ–å–æ¶ˆè®°å½•于里程碑中。
+settings.event_pull_request_comment=åˆå¹¶è¯·æ±‚已评论
+settings.event_pull_request_comment_desc=åˆå¹¶è¯·æ±‚评论已创建ã€ç¼–辑或删除。
settings.event_pull_request_review=已审核的åˆå¹¶è¯·æ±‚
-settings.event_pull_request_review_desc=åˆå¹¶è¯·æ±‚è¢«æ‰¹å‡†ã€æ‹’ç»æˆ–æå‡ºå®¡æŸ¥æ„è§
-settings.event_pull_request_sync=åˆå¹¶è¯·æ±‚è¢«åŒæ­¥
-settings.event_pull_request_sync_desc=åˆå¹¶è¯·æ±‚è¢«åŒæ­¥ã€‚
+settings.event_pull_request_review_desc=åˆå¹¶è¯·æ±‚å·²æ‰¹å‡†ã€æ‹’ç»æˆ–æå‡ºå®¡æŸ¥æ„è§ã€‚
+settings.event_pull_request_sync=åˆå¹¶è¯·æ±‚å·²åŒæ­¥
+settings.event_pull_request_sync_desc=åˆå¹¶è¯·æ±‚å·²åŒæ­¥ã€‚
settings.event_pull_request_review_request=å‘èµ·åˆå¹¶è¯·æ±‚评审
settings.event_pull_request_review_request_desc=åˆå¹¶è¯·æ±‚è¯„å®¡å·²è¯·æ±‚æˆ–å·²å–æ¶ˆ
settings.event_pull_request_approvals=åˆå¹¶è¯·æ±‚批准
settings.event_pull_request_merge=åˆå¹¶è¯·æ±‚åˆå¹¶
+settings.event_header_workflow=工作æµç¨‹äº‹ä»¶
+settings.event_workflow_run=工作æµè¿è¡Œ
+settings.event_workflow_run_desc=Gitea 工作æµé˜Ÿåˆ—中ã€ç­‰å¾…ä¸­ã€æ­£åœ¨è¿›è¡Œæˆ–已完æˆçš„任务。
+settings.event_workflow_job=工作æµä»»åŠ¡
+settings.event_workflow_job_desc=Gitea 工作æµé˜Ÿåˆ—中ã€ç­‰å¾…ä¸­ã€æ­£åœ¨è¿›è¡Œæˆ–已完æˆçš„任务。
settings.event_package=软件包
-settings.event_package_desc=软件包已在仓库中被创建或删除。
+settings.event_package_desc=软件包在仓库中已创建或删除。
settings.branch_filter=分支过滤
-settings.branch_filter_desc=推é€ã€åˆ›å»ºï¼Œåˆ é™¤åˆ†æ”¯äº‹ä»¶çš„分支白åå•,使用 glob 模å¼åŒ¹é…指定。若为空或 <code>*</code>ï¼Œåˆ™å°†æŠ¥å‘Šæ‰€æœ‰åˆ†æ”¯çš„äº‹ä»¶ã€‚è¯­æ³•æ–‡æ¡£è§ <a href="%[1]s">%[2]s</a>。示例:<code>master</code>,<code>{master,release*}</code>。
+settings.branch_filter_desc=推é€ã€åˆ›å»ºï¼Œåˆ é™¤åˆ†æ”¯äº‹ä»¶çš„分支白åå•,使用 glob 表达å¼åŒ¹é…指定。若为空或 <code>*</code>ï¼Œåˆ™ä¼šæŠ¥å‘Šæ‰€æœ‰åˆ†æ”¯çš„äº‹ä»¶ã€‚è¯­æ³•æ–‡æ¡£è§ <a href="%[1]s">%[2]s</a>。示例:<code>master</code>,<code>{master,release*}</code>。
settings.authorization_header=æŽˆæƒæ ‡å¤´
settings.authorization_header_desc=å½“å­˜åœ¨æ—¶å°†è¢«ä½œä¸ºæŽˆæƒæ ‡å¤´åŒ…å«åœ¨å†…。例如: %s。
settings.active=激活
-settings.active_helper=触å‘事件的信æ¯å°†å‘é€åˆ°æ­¤ webhook 网å€ã€‚
+settings.active_helper=触å‘事件的信æ¯å°†å‘é€åˆ°æ­¤ Web é’©å­ URL。
settings.add_hook_success=Web é’©å­æ·»åŠ æˆåŠŸï¼
settings.update_webhook=æ›´æ–° Web é’©å­
settings.update_hook_success=Web é’©å­æ›´æ–°æˆåŠŸï¼
@@ -2376,7 +2433,7 @@ settings.hook_type=é’©å­ç±»åž‹
settings.slack_token=令牌
settings.slack_domain=域å
settings.slack_channel=频é“
-settings.add_web_hook_desc=å°† <a target="_blank" rel="noreferrer" href="%s">%s</a>集æˆåˆ°æ‚¨çš„代ç åº“。
+settings.add_web_hook_desc=å°† <a target="_blank" rel="noreferrer" href="%s">%s</a> 集æˆåˆ°æ‚¨çš„仓库。
settings.web_hook_name_gitea=Gitea
settings.web_hook_name_gogs=Gogs
settings.web_hook_name_slack=Slack
@@ -2403,7 +2460,7 @@ settings.title=标题
settings.deploy_key_content=密钥文本
settings.key_been_used=具有相åŒå†…容的部署密钥已在使用中。
settings.key_name_used=使用相åŒå称的部署密钥已ç»å­˜åœ¨ï¼
-settings.add_key_success=部署密钥 %s 添加æˆåŠŸã€‚
+settings.add_key_success=部署密钥「%sã€æ·»åŠ æˆåŠŸã€‚
settings.deploy_key_deletion=删除部署密钥
settings.deploy_key_deletion_desc=åˆ é™¤éƒ¨ç½²å¯†é’¥å°†å–æ¶ˆæ­¤å¯†é’¥å¯¹æ­¤ä»“库的访问æƒé™ã€‚继续?
settings.deploy_key_deletion_success=部署密钥已删除。
@@ -2412,11 +2469,11 @@ settings.protected_branch=åˆ†æ”¯ä¿æŠ¤
settings.protected_branch.save_rule=ä¿å­˜è§„则
settings.protected_branch.delete_rule=删除规则
settings.protected_branch_can_push=是å¦å…许推é€ï¼Ÿ
-settings.protected_branch_can_push_yes=ä½ å¯ä»¥æŽ¨
-settings.protected_branch_can_push_no=ä½ ä¸èƒ½æŽ¨é€
-settings.branch_protection=分支 '<b>%s</b>' çš„ä¿æŠ¤è§„åˆ™
+settings.protected_branch_can_push_yes=您å¯ä»¥æŽ¨é€
+settings.protected_branch_can_push_no=您ä¸èƒ½æŽ¨é€
+settings.branch_protection=分支「<b>%s</b>ã€çš„ä¿æŠ¤è§„åˆ™
settings.protect_this_branch=å¯ç”¨åˆ†æ”¯ä¿æŠ¤
-settings.protect_this_branch_desc=阻止删除并é™åˆ¶Git推é€å’Œåˆå¹¶åˆ°åˆ†æ”¯ã€‚
+settings.protect_this_branch_desc=阻止删除并é™åˆ¶ Git 推é€å’Œåˆå¹¶åˆ°åˆ†æ”¯ã€‚
settings.protect_disable_push=ç¦ç”¨æŽ¨é€
settings.protect_disable_push_desc=此分支ä¸å…许推é€ã€‚
settings.protect_disable_force_push=ç¦ç”¨å¼ºåˆ¶æŽ¨é€
@@ -2442,13 +2499,13 @@ settings.protect_merge_whitelist_committers_desc=ä»…å…许白åå•用户或团é
settings.protect_merge_whitelist_users=åˆå¹¶ç™½åå•用户:
settings.protect_merge_whitelist_teams=åˆå¹¶ç™½åå•团队:
settings.protect_check_status_contexts=å¯ç”¨çŠ¶æ€æ£€æŸ¥
-settings.protect_status_check_patterns=çŠ¶æ€æ£€æŸ¥æ¨¡å¼ï¼š
-settings.protect_status_check_patterns_desc=输入模å¼ï¼ŒæŒ‡å®šå“ªäº›çŠ¶æ€æ£€æŸ¥å¿…须通过,æ‰èƒ½å°†åˆ†æ”¯åˆå¹¶åˆ°ç¬¦åˆæ­¤è§„则的分支中去。æ¯ä¸€è¡ŒæŒ‡å®šä¸€ä¸ªæ¨¡å¼ï¼Œæ¨¡å¼ä¸èƒ½ä¸ºç©ºã€‚
+settings.protect_status_check_patterns=çŠ¶æ€æ£€æŸ¥è¡¨è¾¾å¼ï¼š
+settings.protect_status_check_patterns_desc=输入表达å¼ä»¥æŒ‡å®šåœ¨åˆ†æ”¯åˆå¹¶åˆ°åŒ¹é…此规则的分支之å‰å¿…é¡»é€šè¿‡å“ªäº›çŠ¶æ€æ£€æŸ¥ã€‚æ¯ä¸€è¡ŒæŒ‡å®šä¸€ä¸ªè¡¨è¾¾å¼ä¸”表达å¼ä¸èƒ½ä¸ºç©ºã€‚
settings.protect_check_status_contexts_desc=è¦æ±‚çŠ¶æ€æ£€æŸ¥é€šè¿‡æ‰èƒ½åˆå¹¶ã€‚如果å¯ç”¨ï¼Œæäº¤å¿…须先推é€åˆ°å¦ä¸€ä¸ªåˆ†æ”¯ï¼Œç„¶åŽå†åˆå¹¶æˆ–推é€åˆ°åŒ¹é…è¿™äº›ä¿æŠ¤è§„åˆ™çš„åˆ†æ”¯ã€‚å¦‚æžœæ²¡æœ‰é€‰æ‹©å…·ä½“çš„çŠ¶æ€æ£€æŸ¥ä¸Šä¸‹æ–‡ï¼Œåˆ™æ‰€æœ‰çš„çŠ¶æ€æ£€æŸ¥éƒ½é€šè¿‡æ‰èƒ½åˆå¹¶ã€‚
settings.protect_check_status_contexts_list=æ­¤ä»“åº“ä¸Šå‘¨è¿›è¡Œè¿‡çš„çŠ¶æ€æ£€æŸ¥
settings.protect_status_check_matched=匹é…
-settings.protect_invalid_status_check_pattern=æ— æ•ˆçš„çŠ¶æ€æ£€æŸ¥è§„则:“%sâ€ã€‚
-settings.protect_no_valid_status_check_patterns=æ²¡æœ‰æœ‰æ•ˆçš„çŠ¶æ€æ£€æŸ¥è§„则。
+settings.protect_invalid_status_check_pattern=æ— æ•ˆçš„çŠ¶æ€æ£€æŸ¥è¡¨è¾¾å¼ï¼šã€Œ%sã€ã€‚
+settings.protect_no_valid_status_check_patterns=æ²¡æœ‰æœ‰æ•ˆçš„çŠ¶æ€æ£€æŸ¥è¡¨è¾¾å¼ã€‚
settings.protect_required_approvals=所需的批准:
settings.protect_required_approvals_desc=åªå…许åˆå¹¶æœ‰è¶³å¤Ÿå®¡æ ¸çš„åˆå¹¶è¯·æ±‚ã€‚è¦æ±‚的审核必须æ¥è‡ªç™½å啿ˆ–者有æƒé™çš„用户或团队。
settings.protect_approvals_whitelist_enabled=仅列入白åå•的用户或团队æ‰å¯æ‰¹å‡†
@@ -2461,18 +2518,18 @@ settings.ignore_stale_approvals=忽略过期批准
settings.ignore_stale_approvals_desc=对旧æäº¤ï¼ˆè¿‡æœŸå®¡æ ¸ï¼‰çš„æ‰¹å‡†å°†ä¸è®¡å…¥ PR 的批准数。如果过期审查已被驳回,则与此无关。
settings.require_signed_commits=需è¦ç­¾åæäº¤
settings.require_signed_commits_desc=æ‹’ç»æŽ¨é€æœªç­¾å或无法验è¯çš„æäº¤åˆ°åˆ†æ”¯
-settings.protect_branch_name_pattern=å—ä¿æŠ¤çš„åˆ†æ”¯å称模å¼
-settings.protect_branch_name_pattern_desc=åˆ†æ”¯ä¿æŠ¤çš„å称匹é…规则。语法请å‚阅 <a href="%s">文档</a> 。如:main, release/**
-settings.protect_patterns=规则
-settings.protect_protected_file_patterns=å—ä¿æŠ¤çš„æ–‡ä»¶æ¨¡å¼(ä½¿ç”¨åˆ†å· ';' 分隔):
-settings.protect_protected_file_patterns_desc=å³ä½¿ç”¨æˆ·æœ‰æƒæ·»åŠ ã€ç¼–辑或删除此分支中的文件,也ä¸å…许直接更改å—ä¿æŠ¤çš„æ–‡ä»¶ã€‚ å¯ä»¥ä½¿ç”¨åˆ†å· (';') 分隔多个模å¼ã€‚ è§<a href='%[1]s'>%[2]s</a>文档了解模å¼è¯­æ³•。例如: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>
-settings.protect_unprotected_file_patterns=ä¸å—ä¿æŠ¤çš„æ–‡ä»¶æ¨¡å¼(ä½¿ç”¨åˆ†å· ';' 分隔):
-settings.protect_unprotected_file_patterns_desc=如果用户有写æƒé™ï¼Œåˆ™å…许直接更改的ä¸å—ä¿æŠ¤çš„æ–‡ä»¶ï¼Œä»¥ç»•è¿‡æŽ¨é€é™åˆ¶ã€‚å¯ä»¥ä½¿ç”¨åˆ†å·åˆ†éš”å¤šä¸ªæ¨¡å¼ (';')。 è§ <a href='%[1]s'>%[2]s</a> 文档了解模å¼è¯­æ³•。例如: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>
+settings.protect_branch_name_pattern=å—ä¿æŠ¤çš„åˆ†æ”¯å称表达å¼
+settings.protect_branch_name_pattern_desc=åˆ†æ”¯ä¿æŠ¤çš„å称匹é…表达å¼ã€‚语法请å‚阅 <a href="%s">文档</a> 。如:main, release/**
+settings.protect_patterns=表达å¼
+settings.protect_protected_file_patterns=å—ä¿æŠ¤çš„æ–‡ä»¶è¡¨è¾¾å¼ï¼ˆä½¿ç”¨åˆ†å·ã€Œ;ã€åˆ†éš”):
+settings.protect_protected_file_patterns_desc=å³ä½¿ç”¨æˆ·æœ‰æƒæ·»åŠ ã€ç¼–辑或删除此分支中的文件,也ä¸å…许直接更改å—ä¿æŠ¤çš„æ–‡ä»¶ã€‚ å¯ä»¥ä½¿ç”¨åˆ†å·ã€Œ;ã€åˆ†éš”多个表达å¼ã€‚ è§<a href='%[1]s'>%[2]s</a>文档了解表达å¼è¯­æ³•。例如: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。
+settings.protect_unprotected_file_patterns=ä¸å—ä¿æŠ¤çš„æ–‡ä»¶è¡¨è¾¾å¼ï¼ˆä½¿ç”¨åˆ†å·ã€Œ;ã€åˆ†éš”):
+settings.protect_unprotected_file_patterns_desc=如果用户有写æƒé™ï¼Œåˆ™å…许直接更改的ä¸å—ä¿æŠ¤çš„æ–‡ä»¶ï¼Œä»¥ç»•è¿‡æŽ¨é€é™åˆ¶ã€‚å¯ä»¥ä½¿ç”¨åˆ†å·åˆ†éš”多个表达å¼ã€Œ;ã€ã€‚ è§ <a href='%[1]s'>%[2]s</a> 文档了解表达å¼è¯­æ³•。例如: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。
settings.add_protected_branch=å¯ç”¨ä¿æŠ¤
settings.delete_protected_branch=ç¦ç”¨ä¿æŠ¤
-settings.update_protect_branch_success=åˆ†æ”¯ä¿æŠ¤è§„åˆ™ %s æ›´æ–°æˆåŠŸã€‚
-settings.remove_protected_branch_success=ç§»é™¤åˆ†æ”¯ä¿æŠ¤è§„åˆ™"%s"æˆåŠŸã€‚
-settings.remove_protected_branch_failed=ç§»é™¤åˆ†æ”¯ä¿æŠ¤è§„åˆ™"%s"失败。
+settings.update_protect_branch_success=åˆ†æ”¯ä¿æŠ¤è§„åˆ™ã€Œ%sã€æ›´æ–°æˆåŠŸã€‚
+settings.remove_protected_branch_success=åˆ†æ”¯ä¿æŠ¤è§„åˆ™ã€Œ%sã€ç§»é™¤æˆåŠŸã€‚
+settings.remove_protected_branch_failed=åˆ†æ”¯ä¿æŠ¤è§„åˆ™ã€Œ%sã€ç§»é™¤å¤±è´¥ã€‚
settings.protected_branch_deletion=åˆ é™¤åˆ†æ”¯ä¿æŠ¤
settings.protected_branch_deletion_desc=ç¦ç”¨åˆ†æ”¯ä¿æŠ¤å…许具有写入æƒé™çš„ç”¨æˆ·æŽ¨é€æäº¤åˆ°æ­¤åˆ†æ”¯ã€‚ç»§ç»­ï¼Ÿ
settings.block_rejected_reviews=æ‹’ç»å®¡æ ¸é˜»æ­¢äº†æ­¤åˆå¹¶
@@ -2493,15 +2550,15 @@ settings.protected_branch_required_rule_name=必须填写规则åç§°
settings.protected_branch_duplicate_rule_name=规则å称已存在
settings.protected_branch_required_approvals_min=所需的审批数ä¸èƒ½ä¸ºè´Ÿæ•°ã€‚
settings.tags=标签
-settings.tags.protection=Gitæ ‡ç­¾ä¿æŠ¤
-settings.tags.protection.pattern=Git标签模å¼
+settings.tags.protection=æ ‡ç­¾ä¿æŠ¤
+settings.tags.protection.pattern=标签表达å¼
settings.tags.protection.allowed=å…许列表
settings.tags.protection.allowed.users=å…许的账å·
settings.tags.protection.allowed.teams=å…许的团队
settings.tags.protection.allowed.noone=æ— 
-settings.tags.protection.create=ä¿æŠ¤Git标签
-settings.tags.protection.none=没有å—ä¿æŠ¤çš„Git标签
-settings.tags.protection.pattern.description=ä½ å¯ä»¥ä½¿ç”¨å•个å称或 glob 模å¼åŒ¹é…æˆ–æ­£åˆ™è¡¨è¾¾å¼æ¥åŒ¹é…多个标签。了解详情请访问 <a target="_blank" rel="noopener" href="%s">ä¿æŠ¤Git标签指å—</a>。
+settings.tags.protection.create=ä¿æŠ¤æ ‡ç­¾
+settings.tags.protection.none=没有å—ä¿æŠ¤çš„æ ‡ç­¾ã€‚
+settings.tags.protection.pattern.description=您å¯ä»¥ä½¿ç”¨å•个å称或 glob 表达å¼åŒ¹é…æˆ–æ­£åˆ™è¡¨è¾¾å¼æ¥åŒ¹é…多个标签。了解详情请访问 <a target="_blank" rel="noopener" href="%s">ä¿æŠ¤æ ‡ç­¾æŒ‡å—</a>。
settings.bot_token=Bot 令牌
settings.chat_id=èŠå¤© ID
settings.thread_id=线程 ID
@@ -2522,13 +2579,13 @@ settings.visibility.error=试图更改仓库å¯è§æ€§æ—¶å‡ºé”™ã€‚
settings.visibility.fork_error=无法更改派生仓库的å¯è§æ€§ã€‚
settings.archive.button=归档仓库
settings.archive.header=归档此仓库
-settings.archive.text=归档仓库将使其完全åªè¯»ã€‚它将在首页éšè—。没有人(甚至你ï¼ï¼‰èƒ½å¤Ÿè¿›è¡Œæ–°çš„æäº¤ï¼Œæˆ–打开工å•åŠåˆå¹¶è¯·æ±‚。
+settings.archive.text=归档仓库将使其完全åªè¯»ã€‚它将在首页éšè—。没有人(包括您)能够进行新的æäº¤ï¼Œæˆ–打开工å•åŠåˆå¹¶è¯·æ±‚。
settings.archive.success=仓库已æˆåŠŸå½’æ¡£ã€‚
settings.archive.error=仓库在归档时出现异常。请通过日志获å–详细信æ¯ã€‚
settings.archive.error_ismirror=请ä¸è¦å¯¹é•œåƒä»“库归档,谢谢ï¼
settings.archive.branchsettings_unavailable=已归档仓库无法进行分支设置。
-settings.archive.tagsettings_unavailable=已归档仓库的Git标签设置ä¸å¯ç”¨ã€‚
-settings.archive.mirrors_unavailable=如果仓库已被归档,镜åƒå°†ä¸å¯ç”¨ã€‚
+settings.archive.tagsettings_unavailable=已归档仓库的标签设置ä¸å¯ç”¨ã€‚
+settings.archive.mirrors_unavailable=如果仓库已归档,镜åƒå°†ä¸å¯ç”¨ã€‚
settings.unarchive.button=撤销仓库归档
settings.unarchive.header=撤销此仓库归档
settings.unarchive.text=撤销归档将æ¢å¤ä»“库接收æäº¤ã€æŽ¨é€ï¼Œä»¥åŠæ–°å·¥å•å’Œåˆå¹¶è¯·æ±‚的能力。
@@ -2573,7 +2630,6 @@ diff.commit=å½“å‰æäº¤
diff.git-notes=Notes
diff.data_not_available=比较内容ä¸å¯ç”¨
diff.options_button=Diff 选项
-diff.show_diff_stats=显示统计
diff.download_patch=下载 Patch 文件
diff.download_diff=下载 Diff 文件
diff.show_split_view=分列视图
@@ -2583,7 +2639,7 @@ diff.whitespace_show_everything=显示所有更改
diff.whitespace_ignore_all_whitespace=比较行时忽略空白符å·
diff.whitespace_ignore_amount_changes=å¿½ç•¥ç©ºç™½ç¬¦å·æ•°é‡çš„å˜åŒ–
diff.whitespace_ignore_at_eol=忽略行末空白符å·çš„æ›´æ”¹
-diff.stats_desc=共有 <strong> %d 个文件被更改</strong>,包括 <strong>%d 次æ’å…¥</strong> å’Œ <strong>%d 次删除</strong>
+diff.stats_desc=å…±<strong>修改 %d 个文件</strong>ï¼ŒåŒ…å« <strong>%d 行新增</strong>å’Œ <strong>%d 行删除</strong>
diff.stats_desc_file=å˜æ›´ %d 行:新增 %d 行,删除 %d 行
diff.bin=二进制
diff.bin_not_shown=二进制文件未显示。
@@ -2622,24 +2678,27 @@ diff.image.overlay=å åŠ 
diff.has_escaped=这一行有éšè—çš„ Unicode 字符
diff.show_file_tree=显示文件树
diff.hide_file_tree=éšè—文件树
+diff.submodule_added=å­æ¨¡å— %[1]s 已添加到 %[2]s
+diff.submodule_deleted=å­æ¨¡å— %[1]s 已从 %[2]s 中删除
+diff.submodule_updated=å­æ¨¡å— %[1]s 已更新:%[2]s
releases.desc=跟踪项目版本和下载。
-release.releases=版本å‘布
+release.releases=å‘布
release.detail=å‘布详情
-release.tags=Git标签
+release.tags=标签
release.new_release=å‘布新版
release.draft=è‰ç¨¿
-release.prerelease=预å‘行
+release.prerelease=预å‘布
release.stable=稳定
-release.latest=最新版本
+release.latest=最新
release.compare=比较
release.edit=编辑
release.ahead.commits=<strong>%d</strong> 次æäº¤
-release.ahead.target=在此版本å‘布åŽè¢«åŠ å…¥åˆ° %s
+release.ahead.target=在此版本å‘布åŽå·²åŠ å…¥åˆ° %s
tag.ahead.target=自此标签到 %s
release.source_code=æºä»£ç 
-release.new_subheader=版本å‘布组织项目的版本。
-release.edit_subheader=版本å‘布组织项目的版本。
+release.new_subheader=å‘布组织项目的版本。
+release.edit_subheader=å‘布组织项目的版本。
release.tag_name=标签åç§°
release.target=目标分支
release.tag_helper=选择一个存在的标签或者创建新标签。
@@ -2648,71 +2707,74 @@ release.tag_helper_existing=现有标签。
release.title=å‘布标题
release.title_empty=标题ä¸èƒ½ä¸ºç©ºã€‚
release.message=æè¿°è¿™ä¸ªç‰ˆæœ¬
-release.prerelease_desc=标记为预å‘行
+release.prerelease_desc=标记为预å‘布
release.prerelease_helper=标记此版本ä¸é€‚åˆç”Ÿäº§ä½¿ç”¨ã€‚
release.cancel=å–æ¶ˆ
release.publish=å‘布版本
release.save_draft=ä¿å­˜è‰ç¨¿
release.edit_release=ä¿å­˜æ­¤æ¬¡å‘布
release.delete_release=删除å‘布
-release.delete_tag=删除 Git标签
+release.delete_tag=删除标签
release.deletion=删除å‘布
-release.deletion_desc=删除版本å‘布åªä¼šä»Ž Gitea 中移除。这ä¸ä¼šå½±å“ Git çš„æ ‡ç­¾ä»¥åŠæ‚¨ä»“库的内容和历å²ã€‚是å¦ç»§ç»­ï¼Ÿ
-release.deletion_success=Release已被删除。
-release.deletion_tag_desc=将从仓库中删除此 Git标签。仓库内容和历å²è®°å½•ä¿æŒä¸å˜ã€‚ç»§ç»­å—?
-release.deletion_tag_success=该 Git标签 å·²ç»è¢«åˆ é™¤
-release.tag_name_already_exist=使用此标签åç§°çš„å‘布版本已ç»å­˜åœ¨ã€‚
+release.deletion_desc=删除å‘布åªä¼šä»Ž Gitea 中移除å‘布。这ä¸ä¼šå½±å“ Git çš„æ ‡ç­¾ä»¥åŠæ‚¨ä»“库的内容和历å²ã€‚是å¦ç»§ç»­ï¼Ÿ
+release.deletion_success=该å‘布已删除。
+release.deletion_tag_desc=将从仓库中删除此标签。仓库内容和历å²è®°å½•ä¿æŒä¸å˜ã€‚ç»§ç»­å—?
+release.deletion_tag_success=该标签已删除。
+release.tag_name_already_exist=使用此标签åç§°çš„å‘布已ç»å­˜åœ¨ã€‚
release.tag_name_invalid=标签å称无效。
-release.tag_name_protected=Git标签åç§°å·²å—ä¿æŠ¤ã€‚
-release.tag_already_exist=æ­¤ Git标签 å称已存在
+release.tag_name_protected=标签åå·²å—ä¿æŠ¤ã€‚
+release.tag_already_exist=此标签å已存在。
release.downloads=下载附件
release.download_count=下载:%s
release.add_tag_msg=使用å‘布的标题和内容作为标签消æ¯ã€‚
release.add_tag=仅创建标签
-release.releases_for=%s 的版本å‘布
+release.releases_for=%s çš„å‘布
release.tags_for=%s 的标签
branch.name=分支åç§°
-branch.already_exists=å为 %s 的分支已存在。
+branch.already_exists=å为「%sã€çš„分支已存在。
branch.delete_head=刪除
-branch.delete=删除分支 %s
+branch.delete=删除分支「%sã€
branch.delete_html=删除分支
branch.delete_desc=åˆ é™¤åˆ†æ”¯æ˜¯æ°¸ä¹…çš„ã€‚è™½ç„¶å·²åˆ é™¤çš„åˆ†æ”¯åœ¨å®žé™…è¢«åˆ é™¤å‰æœ‰å¯èƒ½ä¼šçŸ­æ—¶é—´å­˜åœ¨ï¼Œä½†è¿™åœ¨å¤§å¤šæ•°æƒ…况下无法撤销。是å¦ç»§ç»­ï¼Ÿ
-branch.deletion_success=分支 %s 已被删除。
-branch.deletion_failed=删除分支 %s 失败。
-branch.delete_branch_has_new_commits=因为åˆå¹¶ä¹‹åŽæœ‰æ–°çš„æäº¤ï¼Œåˆ†æ”¯ %s 无法被删除。
+branch.deletion_success=分支「%sã€åˆ é™¤æˆåŠŸã€‚
+branch.deletion_failed=分支「%sã€åˆ é™¤å¤±è´¥ã€‚
+branch.delete_branch_has_new_commits=因为åˆå¹¶ä¹‹åŽæœ‰æ–°çš„æäº¤ï¼Œåˆ†æ”¯ã€Œ%sã€æ— æ³•删除。
branch.create_branch=创建分支 %s
-branch.create_from=从 %s
-branch.create_success=分支 '%s' 已创建。
-branch.branch_already_exists=此仓库已存在å为 %s 的分支。
-branch.branch_name_conflict=分支åç§°"%s"与已存在的分支"%s"冲çªã€‚
-branch.tag_collision=分支 %s ä¸èƒ½è¢«åˆ›å»ºå› ä¸ºåŒå的标签已ç»å­˜åœ¨ã€‚
+branch.create_from=从「%sã€
+branch.create_success=分支「%sã€å·²åˆ›å»ºã€‚
+branch.branch_already_exists=此仓库已存在å为「%sã€çš„分支。
+branch.branch_name_conflict=分支å称「%sã€ä¸Žå·²å­˜åœ¨çš„分支「%sã€å†²çªã€‚
+branch.tag_collision=分支「%sã€æ— æ³•创建,因为åŒå的标签已ç»å­˜åœ¨ã€‚
branch.deleted_by=删除人:%s
-branch.restore_success=分支 "%s"已还原。
-branch.restore_failed=还原分支 "%s"失败。
-branch.protected_deletion_failed=ä¸èƒ½åˆ é™¤å—ä¿æŠ¤çš„åˆ†æ”¯ "%s"。
-branch.default_deletion_failed=ä¸èƒ½åˆ é™¤é»˜è®¤åˆ†æ”¯"%s"。
-branch.restore=`还原分支 "%s"`
-branch.download=`下载分支 "%s"`
-branch.rename=`é‡å‘½å分支 "%s"`
+branch.restore_success=分支「%sã€å·²è¿˜åŽŸã€‚
+branch.restore_failed=分支「%sã€è¿˜åŽŸå¤±è´¥ã€‚
+branch.protected_deletion_failed=ä¸èƒ½åˆ é™¤å—ä¿æŠ¤çš„åˆ†æ”¯ã€Œ%sã€ã€‚
+branch.default_deletion_failed=ä¸èƒ½åˆ é™¤é»˜è®¤åˆ†æ”¯ã€Œ%sã€ã€‚
+branch.default_branch_not_exist=默认分支「%sã€ä¸å­˜åœ¨ã€‚
+branch.restore=还原分支「%sã€
+branch.download=下载分支「%sã€
+branch.rename=é‡å‘½å分支「%sã€
branch.included_desc=此分支是默认分支的一部分
branch.included=已包å«
branch.create_new_branch=从下列分支创建分支:
branch.confirm_create_branch=创建分支
branch.warning_rename_default_branch=您正在é‡å‘½å默认分支。
-branch.rename_branch_to=é‡å‘½å %s 为:
+branch.rename_branch_to=é‡å‘½å「%sã€ä¸ºï¼š
branch.confirm_rename_branch=é‡å‘½å分支
branch.create_branch_operation=创建分支
branch.new_branch=创建新分支
-branch.new_branch_from=基于"%s"创建新分支
-branch.renamed=分支 %s 被é‡å‘½å为 %s。
+branch.new_branch_from=基于「%sã€åˆ›å»ºæ–°åˆ†æ”¯
+branch.renamed=分支 %s å·²é‡å‘½å为 %s。
+branch.rename_default_or_protected_branch_error=åªæœ‰ç®¡ç†å‘˜èƒ½é‡å‘½å默认分支和å—ä¿æŠ¤çš„åˆ†æ”¯ã€‚
+branch.rename_protected_branch_failed=此分支å—到 glob è¯­æ³•è§„åˆ™çš„ä¿æŠ¤ã€‚
tag.create_tag=创建标签 %s
tag.create_tag_operation=创建标签
tag.confirm_create_tag=创建标签
-tag.create_tag_from=基于"%s"创建新标签
+tag.create_tag_from=基于「%sã€åˆ›å»ºæ–°æ ‡ç­¾
-tag.create_success=标签"%s"已存在
+tag.create_success=标签「%sã€å·²å­˜åœ¨ã€‚
topic.manage_topics=管ç†ä¸»é¢˜
topic.done=ä¿å­˜
@@ -2730,7 +2792,7 @@ error.broken_git_hook=此仓库的 Git é’©å­ä¼¼ä¹Žå·²æŸå。 请按照 <a tar
[graphs]
component_loading=正在加载 %s...
component_loading_failed=无法加载 %s
-component_loading_info=è¿™å¯èƒ½éœ€è¦ä¸€ç‚¹â€¦
+component_loading_info=è¿™å¯èƒ½éœ€è¦ä¸€ç‚¹æ—¶é—´â€¦
component_failed_to_load=æ„外的错误å‘生了。
code_frequency.what=代ç é¢‘率
contributors.what=贡献
@@ -2759,14 +2821,15 @@ team_permission_desc=æƒé™
team_unit_desc=å…许访问仓库å•å…ƒ
team_unit_disabled=(å·²ç¦ç”¨)
-form.name_reserved=组织åç§° '%s' 是被ä¿ç•™çš„。
-form.name_pattern_not_allowed=仓库å称中ä¸å…许使用 "%s"。
+form.name_been_taken=组织å称「%sã€å·²ç»è¢«å ç”¨ã€‚
+form.name_reserved=组织å称「%sã€æ˜¯ä¿ç•™çš„。
+form.name_pattern_not_allowed=组织å中ä¸å…许使用「%sã€æ ¼å¼ã€‚
form.create_org_not_allowed=此账å·ç¦æ­¢åˆ›å»ºç»„织
settings=组织设置
settings.options=组织
settings.full_name=组织全å
-settings.email=è”系电å­é‚®ä»¶
+settings.email=è”系邮箱
settings.website=网站
settings.location=所在地区
settings.permission=æƒé™
@@ -2780,15 +2843,28 @@ settings.visibility.private_shortname=ç§æœ‰
settings.update_settings=更新组织设置
settings.update_setting_success=组织设置已更新。
-settings.change_orgname_prompt=注æ„:更改组织åç§°åŒæ—¶ä¼šæ›´æ”¹ç»„织的 URL 地å€å¹¶é‡Šæ”¾æ—§çš„å称。
-settings.change_orgname_redirect_prompt=在被人使用å‰ï¼Œæ—§ç”¨æˆ·å将会被é‡å®šå‘。
+
+settings.rename=修改组织åç§°
+settings.rename_desc=更改组织åç§°åŒæ—¶ä¼šæ›´æ”¹ç»„织的 URL 地å€å¹¶é‡Šæ”¾æ—§çš„å称。
+settings.rename_success=组织 %[1]s å·²æˆåŠŸé‡å‘½å为 %[2]s。
+settings.rename_no_change=组织å称没有å˜åŒ–。
+settings.rename_new_org_name=新组织åç§°
+settings.rename_failed=由于内部错误,é‡å‘½å组织失败
+settings.rename_notices_1=æ­¤æ“作 <strong>无法</strong> 被回滚。
+settings.rename_notices_2=在被人使用å‰ï¼Œæ—§å称将会被é‡å®šå‘。
+
settings.update_avatar_success=组织头åƒå·²ç»æ›´æ–°ã€‚
settings.delete=删除组织
settings.delete_account=删除当å‰ç»„织
-settings.delete_prompt=删除æ“作会永久清除该组织的信æ¯ï¼Œå¹¶ä¸” <strong>ä¸å¯æ¢å¤</strong>ï¼
+settings.delete_prompt=删除æ“作会永久清除该组织的信æ¯ï¼Œå¹¶ä¸” <strong>无法</strong> æ¢å¤ï¼
+settings.name_confirm=输入组织å称以确认:
+settings.delete_notices_1=æ­¤æ“作 <strong>无法</strong> 被回滚。
+settings.delete_notices_2=æ­¤æ“作将永久删除 <strong>%s</strong> 的所有<strong>仓库</strong>,包括 Git æ•°æ®ã€ å·¥å•ã€è¯„论ã€ç™¾ç§‘å’Œå作者的æ“作æƒé™ã€‚
+settings.delete_notices_3=æ­¤æ“作将永久删除 <strong>%s</strong> 的所有 <strong>软件包</strong>。
+settings.delete_notices_4=æ­¤æ“作将永久删除 <strong>%s</strong> 的所有 <strong>项目</strong>。
settings.confirm_delete_account=确认删除组织
-settings.delete_org_title=删除组织
-settings.delete_org_desc=此组织将会被永久删除,确认继续å—?
+settings.delete_failed=由于内部错误,删除组织失败
+settings.delete_successful=组织 <b>%s</b> å·²æˆåŠŸåˆ é™¤ã€‚
settings.hooks_desc=在此处添加的 Web é’©å­å°†ä¼šåº”用到该组织下的 <strong>所有仓库</strong>。
settings.labels_desc=添加能够被该组织下的 <strong>所有仓库</strong> 的工å•使用的标签。
@@ -2834,7 +2910,7 @@ teams.invite_team_member=邀请加入 %s
teams.invite_team_member.list=待处ç†çš„邀请
teams.delete_team_title=删除团队
teams.delete_team_desc=删除一个团队将删除团队æˆå‘˜çš„访问æƒé™ï¼Œç»§ç»­ï¼Ÿ
-teams.delete_team_success=该团队已被删除。
+teams.delete_team_success=该团队已删除。
teams.read_permission_desc=该团队拥有对所属仓库的 <strong>读å–</strong> æƒé™ï¼Œå›¢é˜Ÿæˆå‘˜å¯ä»¥è¿›è¡ŒæŸ¥çœ‹å’Œå…‹éš†ç­‰åªè¯»æ“作。
teams.write_permission_desc=该团队拥有对所属仓库的 <strong>读å–</strong> å’Œ <strong>写入</strong> çš„æƒé™ã€‚
teams.admin_permission_desc=该团队拥有一定的 <strong>管ç†</strong> æƒé™ï¼Œå›¢é˜Ÿæˆå‘˜å¯ä»¥è¯»å–ã€å…‹éš†ã€æŽ¨é€ä»¥åŠæ·»åŠ å…¶å®ƒä»“åº“å作者。
@@ -2860,6 +2936,18 @@ teams.invite.title=您已被邀请加入组织 <strong>%s</strong> 中的团队
teams.invite.by=邀请人 %s
teams.invite.description=请点击下é¢çš„æŒ‰é’®åŠ å…¥å›¢é˜Ÿã€‚
+view_as_role=以 %s 身份查看
+view_as_public_hint=您正在以公开用户的身份查看 README
+view_as_member_hint=您正在以组织æˆå‘˜çš„身份查看 README
+
+worktime=工作时间
+worktime.date_range_start=起始日期
+worktime.date_range_end=ç»“æŸæ—¥æœŸ
+worktime.query=查询
+worktime.time=æ—¶é—´
+worktime.by_repositories=按仓库
+worktime.by_milestones=按里程碑
+worktime.by_members=按æˆå‘˜
[admin]
maintenance=维护
@@ -2873,10 +2961,10 @@ repositories=仓库管ç†
hooks=Web é’©å­
integrations=集æˆ
authentication=è®¤è¯æº
-emails=用户邮件
+emails=用户邮箱
config=应用é…ç½®
config_summary=摘è¦
-config_settings=组织设置
+config_settings=设置
notices=系统æç¤º
monitor=ç›‘æŽ§é¢æ¿
first_page=首页
@@ -2892,27 +2980,27 @@ dashboard.operation_name=æ“作åç§°
dashboard.operation_switch=开关
dashboard.operation_run=执行
dashboard.clean_unbind_oauth=æ¸…ç†æœªç»‘定的 OAuth 连接
-dashboard.clean_unbind_oauth_success=所有未绑定的 OAuth 连接已被删除。
+dashboard.clean_unbind_oauth_success=所有未绑定的 OAuth 连接已删除。
dashboard.task.started=已开始任务:%[1]s
dashboard.task.process=任务: %[1]s
dashboard.task.cancelled=任务: %[1]s 已喿¶ˆ: %[3]s
dashboard.task.error=任务中的错误: %[1]s: %[3]s
dashboard.task.finished=任务: %[2]s å¯åŠ¨çš„ %[1]s 已完æˆ
dashboard.task.unknown=未知任务: %[1]s
-dashboard.cron.started=已开始计划任务:%[1]s
+dashboard.cron.started=计划任务:%[1]s å·²å¯åЍ
dashboard.cron.process=计划任务:%[1]s
-dashboard.cron.cancelled=定时任务: %[1]s 已喿¶ˆ: %[3]s
-dashboard.cron.error=任务中的错误: %s: %[3]s
-dashboard.cron.finished=任务:%[1]s å·²ç»å®Œæˆ
+dashboard.cron.cancelled=计划任务:%[1]s 已喿¶ˆï¼š%[3]s
+dashboard.cron.error=计划任务错误: %s: %[3]s
+dashboard.cron.finished=计划任务:%[1]s 已完æˆ
dashboard.delete_inactive_accounts=åˆ é™¤æ‰€æœ‰æœªæ¿€æ´»çš„å¸æˆ·
dashboard.delete_inactive_accounts.started=删除所有未激活的账户任务已å¯åŠ¨ã€‚
-dashboard.delete_repo_archives=删除所有代ç åº“的存档 (ZIP〠TARã€GZ, ç­‰...)
+dashboard.delete_repo_archives=删除所有仓库的存档(ZIPã€TARã€GZ等)
dashboard.delete_repo_archives.started=删除所有仓库存档任务已å¯åŠ¨ã€‚
dashboard.delete_missing_repos=删除所有丢失 Git 文件的仓库
dashboard.delete_missing_repos.started=删除所有丢失 Git 文件的仓库任务已å¯åŠ¨ã€‚
dashboard.delete_generated_repository_avatars=删除生æˆçš„仓库头åƒ
-dashboard.sync_repo_branches=将缺少的分支从 git æ•°æ®åŒæ­¥åˆ°æ•°æ®åº“
-dashboard.sync_repo_tags=从 git æ•°æ®åŒæ­¥æ ‡ç­¾åˆ°æ•°æ®åº“
+dashboard.sync_repo_branches=将缺少的分支从 Git æ•°æ®åŒæ­¥åˆ°æ•°æ®åº“
+dashboard.sync_repo_tags=从 Git æ•°æ®åŒæ­¥æ ‡ç­¾åˆ°æ•°æ®åº“
dashboard.update_mirrors=更新镜åƒä»“库
dashboard.repo_health_check=å¥åº·æ£€æŸ¥æ‰€æœ‰ä»“库
dashboard.check_repo_stats=检查所有仓库统计
@@ -2927,11 +3015,11 @@ dashboard.reinit_missing_repos=釿–°åˆå§‹åŒ–所有丢失的 Git 仓库存在çš
dashboard.sync_external_users=åŒæ­¥å¤–部用户数æ®
dashboard.cleanup_hook_task_table=æ¸…ç† hook_task 表
dashboard.cleanup_packages=清ç†è¿‡æœŸçš„软件包
-dashboard.cleanup_actions=清ç†è¿‡æœŸçš„ Actions 资æº
+dashboard.cleanup_actions=清ç†è¿‡æœŸçš„工作æµèµ„æº
dashboard.server_uptime=æœåŠ¡è¿è¡Œæ—¶é—´
dashboard.current_goroutine=å½“å‰ Goroutines æ•°é‡
dashboard.current_memory_usage=当å‰å†…存使用é‡
-dashboard.total_memory_allocated=所有被分é…的内存
+dashboard.total_memory_allocated=所有已分é…的内存
dashboard.memory_obtained=内存å ç”¨é‡
dashboard.pointer_lookup_times=指针查找次数
dashboard.memory_allocate_times=å†…å­˜åˆ†é…æ¬¡æ•°
@@ -2940,36 +3028,36 @@ dashboard.current_heap_usage=å½“å‰ Heap 内存使用é‡
dashboard.heap_memory_obtained=Heap 内存å ç”¨é‡
dashboard.heap_memory_idle=Heap 内存空闲é‡
dashboard.heap_memory_in_use=正在使用的 Heap 内存
-dashboard.heap_memory_released=被释放的 Heap 内存
+dashboard.heap_memory_released=已释放的 Heap 内存
dashboard.heap_objects=Heap 对象数é‡
dashboard.bootstrap_stack_usage=å¯åЍ Stack 使用é‡
-dashboard.stack_memory_obtained=被分é…çš„ Stack 内存
+dashboard.stack_memory_obtained=已分é…çš„ Stack 内存
dashboard.mspan_structures_usage=MSpan 结构内存使用é‡
-dashboard.mspan_structures_obtained=被分é…çš„ MSpan 结构内存
+dashboard.mspan_structures_obtained=已分é…çš„ MSpan 结构内存
dashboard.mcache_structures_usage=MCache 结构内存使用é‡
-dashboard.mcache_structures_obtained=被分é…çš„ MCache 结构内存
-dashboard.profiling_bucket_hash_table_obtained=被分é…的剖æžå“ˆå¸Œè¡¨å†…å­˜
-dashboard.gc_metadata_obtained=被分é…çš„ GC 元数æ®å†…å­˜
-dashboard.other_system_allocation_obtained=其它被分é…的系统内存
+dashboard.mcache_structures_obtained=已分é…çš„ MCache 结构内存
+dashboard.profiling_bucket_hash_table_obtained=已分é…的剖æžå“ˆå¸Œè¡¨å†…å­˜
+dashboard.gc_metadata_obtained=已分é…çš„ GC 元数æ®å†…å­˜
+dashboard.other_system_allocation_obtained=其它已分é…的系统内存
dashboard.next_gc_recycle=下次 GC 内存回收é‡
dashboard.last_gc_time=è·ç¦»ä¸Šæ¬¡ GC æ—¶é—´
dashboard.total_gc_time=GC æš‚åœæ—¶é—´æ€»é‡
dashboard.total_gc_pause=GC æš‚åœæ—¶é—´æ€»é‡
dashboard.last_gc_pause=上次 GC æš‚åœæ—¶é—´
dashboard.gc_times=GC 执行次数
-dashboard.delete_old_actions=从数æ®åº“中删除所有旧æ“作记录
-dashboard.delete_old_actions.started=已开始从数æ®åº“中删除所有旧æ“作记录。
+dashboard.delete_old_actions=从数æ®åº“中删除所有旧工作æµè®°å½•
+dashboard.delete_old_actions.started=已开始从数æ®åº“中删除所有旧工作æµè®°å½•。
dashboard.update_checker=更新检查器
dashboard.delete_old_system_notices=从数æ®åº“中删除所有旧系统通知
dashboard.gc_lfs=垃圾回收 LFS 元数æ®
-dashboard.stop_zombie_tasks=åœæ­¢åƒµå°¸ä»»åŠ¡
-dashboard.stop_endless_tasks=åœæ­¢æ— æ³•åœæ­¢çš„任务
-dashboard.cancel_abandoned_jobs=å–æ¶ˆä¸¢å¼ƒçš„任务
-dashboard.start_schedule_tasks=开始Actions调度任务
+dashboard.stop_zombie_tasks=åœæ­¢åƒµå°¸å·¥ä½œæµä»»åŠ¡
+dashboard.stop_endless_tasks=åœæ­¢æ— é™å¾ªçŽ¯çš„å·¥ä½œæµä»»åŠ¡
+dashboard.cancel_abandoned_jobs=å–æ¶ˆå·²æ”¾å¼ƒçš„工作æµä»»åŠ¡
+dashboard.start_schedule_tasks=å¯åŠ¨å·¥ä½œæµè®¡åˆ’任务
dashboard.sync_branch.started=åˆ†æ”¯åŒæ­¥å·²å¼€å§‹
dashboard.sync_tag.started=æ ‡ç­¾åŒæ­¥å·²å¼€å§‹
dashboard.rebuild_issue_indexer=é‡å»ºå·¥å•索引
-dashboard.sync_repo_licenses=釿–°ä»“库许å¯è¯æŽ¢æµ‹
+dashboard.sync_repo_licenses=釿–°æŽ¢æµ‹ä»“库许å¯è¯
users.user_manage_panel=ç”¨æˆ·å¸æˆ·ç®¡ç†
users.new_account=åˆ›å»ºæ–°å¸æˆ·
@@ -2987,33 +3075,33 @@ users.created=创建时间
users.last_login=上次登录
users.never_login=从未登录
users.send_register_notify=å‘逿³¨å†Œé€šçŸ¥
-users.new_success=用户账户 '%s' 已被创建。
+users.new_success=用户账户「%sã€å·²åˆ›å»ºã€‚
users.edit=修改
users.auth_source=è®¤è¯æº
users.local=本地
users.auth_login_name=认è¯ç™»å½•åç§°
users.password_helper=ä¿æŒå¯†ç ä¸ºç©ºå°†ä¸æ›´æ”¹å¯†ç ã€‚
-users.update_profile_success=è¯¥å¸æˆ·å·²è¢«æ›´æ–°ã€‚
+users.update_profile_success=è¯¥å¸æˆ·å·²æ›´æ–°ã€‚
users.edit_account=编辑å¸å·
users.max_repo_creation=最大仓库数
users.max_repo_creation_desc=(设置为 -1 表示使用全局默认值)
-users.is_activated=该用户已被激活
+users.is_activated=该用户已激活
users.prohibit_login=ç¦ç”¨ç™»å½•
users.is_admin=是管ç†å‘˜
users.is_restricted=å—é™
users.allow_git_hook=å…许创建 Git é’©å­
-users.allow_git_hook_tooltip=Git é’©å­å°†ä¼šè¢«ä»¥æ“作系统用户è¿è¡Œï¼Œå°†ä¼šæ‹¥æœ‰åŒæ ·çš„主机访问æƒé™ã€‚因此,拥有此特殊的Git é’©å­æƒé™å°†èƒ½å¤Ÿè®¿é—®åˆä¿®æ”¹æ‰€æœ‰çš„ Gitea 仓库或者Gitea的数æ®åº“ã€‚åŒæ—¶ä¹Ÿèƒ½èŽ·å¾—Gitea的管ç†å‘˜æƒé™ã€‚
+users.allow_git_hook_tooltip=Git é’©å­å°†ä¼šä»¥æ“作系统用户è¿è¡Œï¼Œæ‹¥æœ‰åŒæ ·çš„主机访问æƒé™ã€‚因此,拥有此特殊的 Git é’©å­æƒé™å°†èƒ½å¤Ÿè®¿é—®åˆä¿®æ”¹æ‰€æœ‰çš„ Gitea 仓库或者 Gitea 的数æ®åº“ã€‚åŒæ—¶ä¹Ÿèƒ½èŽ·å¾— Gitea 的管ç†å‘˜æƒé™ã€‚
users.allow_import_local=å…许导入本地仓库
users.allow_create_organization=å…许创建组织
users.update_profile=æ›´æ–°å¸æˆ·
users.delete_account=åˆ é™¤å¸æˆ·
-users.cannot_delete_self=ä½ ä¸èƒ½åˆ é™¤è‡ªå·±
-users.still_own_repo=此用户ä»ç„¶æ‹¥æœ‰ä¸€ä¸ªæˆ–多个仓库。必须首先删除或转让这些仓库。
+users.cannot_delete_self=您ä¸èƒ½åˆ é™¤è‡ªå·±
+users.still_own_repo=此用户ä»ç„¶æ‹¥æœ‰ä¸€ä¸ªæˆ–多个仓库。必须首先删除或转移这些仓库。
users.still_has_org=此用户是组织的æˆå‘˜ã€‚必须先从组织中删除用户。
users.purge=清ç†ç”¨æˆ·
-users.purge_help=强制删除用户和用户拥有的任何仓库ã€ç»„织和软件包。所有评论也将被删除。
+users.purge_help=强制删除用户和用户拥有的任何仓库ã€ç»„织和软件包。所有评论也将删除。
users.still_own_packages=此用户ä»ç„¶æ‹¥æœ‰ä¸€ä¸ªæˆ–多个软件包,请先删除这些软件包。
-users.deletion_success=ç”¨æˆ·å¸æˆ·å·²è¢«åˆ é™¤ã€‚
+users.deletion_success=ç”¨æˆ·å¸æˆ·å·²åˆ é™¤ã€‚
users.reset_2fa=é‡ç½®ä¸¤æ­¥éªŒè¯
users.list_status_filter.menu_text=过滤
users.list_status_filter.reset=é‡ç½®
@@ -3032,19 +3120,19 @@ users.details=用户详细信æ¯
emails.email_manage_panel=邮件管ç†
emails.primary=主è¦çš„
emails.activated=已激活
-emails.filter_sort.email=电å­é‚®ä»¶
-emails.filter_sort.email_reverse=电å­é‚®ä»¶(逆åº)
+emails.filter_sort.email=邮箱
+emails.filter_sort.email_reverse=邮箱(倒åºï¼‰
emails.filter_sort.name=用户å
emails.filter_sort.name_reverse=用户å(倒åº)
-emails.updated=电å­é‚®ä»¶å·²æ›´æ–°
-emails.not_updated=无法更新请求的电å­é‚®ä»¶åœ°å€ï¼š %v
-emails.duplicate_active=此电å­é‚®ä»¶åœ°å€å·²è¢«å¦ä¸€ä¸ªç”¨æˆ·æ¿€æ´»ä½¿ç”¨ã€‚
-emails.change_email_header=更新电å­é‚®ä»¶å±žæ€§
-emails.change_email_text=æ‚¨ç¡®å®šè¦æ›´æ–°è¯¥ç”µå­é‚®ä»¶åœ°å€å—?
-emails.delete=删除电å­é‚®ä»¶
-emails.delete_desc=您确定è¦åˆ é™¤è¯¥ç”µå­é‚®ä»¶åœ°å€ï¼Ÿ
-emails.deletion_success=电å­é‚®ä»¶åœ°å€å·²è¢«åˆ é™¤ã€‚
-emails.delete_primary_email_error=您ä¸èƒ½åˆ é™¤ä¸»ç”µå­é‚®ä»¶ã€‚
+emails.updated=邮箱已更新
+emails.not_updated=无法更新请求的邮箱地å€ï¼š%v
+emails.duplicate_active=此邮箱地å€å·²è¢«å¦ä¸€ä¸ªç”¨æˆ·æ¿€æ´»ä½¿ç”¨ã€‚
+emails.change_email_header=更新邮箱属性
+emails.change_email_text=æ‚¨ç¡®å®šè¦æ›´æ–°è¯¥é‚®ç®±åœ°å€å—?
+emails.delete=删除邮箱
+emails.delete_desc=您确定è¦åˆ é™¤è¯¥é‚®ç®±åœ°å€ï¼Ÿ
+emails.deletion_success=邮箱地å€å·²åˆ é™¤ã€‚
+emails.delete_primary_email_error=您ä¸èƒ½åˆ é™¤ä¸»é‚®ç®±ã€‚
orgs.org_manage_panel=组织管ç†
orgs.name=åç§°
@@ -3064,7 +3152,7 @@ repos.lfs_size=LFS 大å°
packages.package_manage_panel=软件包管ç†
packages.total_size=总大å°ï¼š%s
-packages.unreferenced_size=未引用大å°ï¼š %s
+packages.unreferenced_size=未引用大å°ï¼š%s
packages.cleanup=清ç†è¿‡æœŸæ•°æ®
packages.cleanup.success=清ç†è¿‡æœŸæ•°æ®æˆåŠŸ
packages.owner=所有者
@@ -3076,13 +3164,13 @@ packages.repository=仓库
packages.size=大å°
packages.published=å·²å‘布
-defaulthooks=默认Webé’©å­
+defaulthooks=默认 Web é’©å­
defaulthooks.desc=当æŸäº› Gitea äº‹ä»¶è§¦å‘æ—¶ï¼ŒWeb é’©å­è‡ªåЍ呿œåС噍å‘出 HTTP POST 请求。这里定义的 Web é’©å­æ˜¯é»˜è®¤é…置,将被å¤åˆ¶åˆ°æ‰€æœ‰æ–°çš„仓库中。详情请访问 <a target="_blank" rel="noopener" href="%s">Web é’©å­æŒ‡å—</a>。
-defaulthooks.add_webhook=添加默认Web é’©å­
+defaulthooks.add_webhook=添加默认 Web é’©å­
defaulthooks.update_webhook=更新默认 Web 钩å­
systemhooks=系统 Web é’©å­
-systemhooks.desc=当æŸäº› Gitea äº‹ä»¶è§¦å‘æ—¶ï¼ŒWeb é’©å­è‡ªåЍ呿œåС噍å‘出HTTP POST请求。这里定义的 Web é’©å­å°†ä½œç”¨äºŽç³»ç»Ÿä¸Šçš„æ‰€æœ‰ä»“库,所以请考虑这å¯èƒ½å¸¦æ¥çš„任何性能影å“。了解详情请访问 <a target="_blank" rel="noopener" href="%s">Web é’©å­æŒ‡å—</a>。
+systemhooks.desc=当æŸäº› Gitea äº‹ä»¶è§¦å‘æ—¶ï¼ŒWeb é’©å­è‡ªåЍ呿œåС噍å‘出 HTTP POST 请求。这里定义的 Web é’©å­å°†ä½œç”¨äºŽç³»ç»Ÿä¸Šçš„æ‰€æœ‰ä»“库,所以请考虑这å¯èƒ½å¸¦æ¥çš„任何性能影å“。了解详情请访问 <a target="_blank" rel="noopener" href="%s">Web é’©å­æŒ‡å—</a>。
systemhooks.add_webhook=添加系统 Web é’©å­
systemhooks.update_webhook=更新系统 Web é’©å­
@@ -3104,7 +3192,7 @@ auths.bind_password=绑定密ç 
auths.user_base=用户æœç´¢åŸºå‡†
auths.user_dn=用户 DN
auths.attribute_username=用户å属性
-auths.attribute_username_placeholder=置空将使用Gitea的用户å。
+auths.attribute_username_placeholder=置空将使用 Gitea 的用户å。
auths.attribute_name=å字属性
auths.attribute_surname=å§“æ°å±žæ€§
auths.attribute_mail=电å­é‚®ç®±å±žæ€§
@@ -3138,7 +3226,7 @@ auths.helo_hostname=HELO 主机å
auths.helo_hostname_helper=用 HELO å‘é€çš„主机å。 留空å‘é€å½“å‰ä¸»æœºå。
auths.disable_helo=ç¦ç”¨ HELO
auths.pam_service_name=PAM æœåŠ¡åç§°
-auths.pam_email_domain=PAM 电å­é‚®ä»¶åŸŸ(å¯é€‰)
+auths.pam_email_domain=PAM 邮件域(å¯é€‰ï¼‰
auths.oauth2_provider=OAuth2 æä¾›ç¨‹åº
auths.oauth2_icon_url=图标 URL
auths.oauth2_clientID=客户端 ID (键)
@@ -3147,8 +3235,8 @@ auths.openIdConnectAutoDiscoveryURL=OpenID 连接自动å‘现 URL
auths.oauth2_use_custom_url=使用自定义的 URL è€Œä¸æ˜¯é»˜è®¤çš„ URL
auths.oauth2_tokenURL=令牌 URL
auths.oauth2_authURL=æŽˆæƒ URL
-auths.oauth2_profileURL=Profile URL
-auths.oauth2_emailURL=电å­é‚®ä»¶ URL
+auths.oauth2_profileURL=个人资料 URL
+auths.oauth2_emailURL=邮箱 URL
auths.skip_local_two_fa=跳过本地两步验è¯
auths.skip_local_two_fa_helper=ä¸è®¾ç½®æ„味ç€è®¾ç½®äº†ä¸¤æ­¥éªŒè¯çš„æœ¬åœ°ç”¨æˆ·ä»ç„¶éœ€è¦é€šè¿‡ä¸¤æ­¥éªŒè¯æ‰èƒ½ç™»å½•
auths.oauth2_tenant=租户
@@ -3168,31 +3256,31 @@ auths.sspi_auto_create_users_helper=å…许 SSPI 认è¯åœ¨ç”¨æˆ·ç¬¬ä¸€æ¬¡ç™»å½•æ—
auths.sspi_auto_activate_users=自动激活用户
auths.sspi_auto_activate_users_helper=å…许 SSPI 认è¯è‡ªåŠ¨æ¿€æ´»æ–°ç”¨æˆ·
auths.sspi_strip_domain_names=从用户å中删除域å部分
-auths.sspi_strip_domain_names_helper=如果选中此项,域å将从登录å中删除(例如,"DOMAIN\user"å’Œ"user@example.org"ï¼Œä¸¤è€…éƒ½å°†å˜æˆåªæ˜¯â€œç”¨æˆ·â€)。
+auths.sspi_strip_domain_names_helper=如果选中此项,域å将从登录å中删除(例如,「DOMAIN\userã€å’Œã€Œuser@example.orgã€ï¼Œä¸¤è€…éƒ½å°†å˜æˆåªæ˜¯ã€Œç”¨æˆ·ã€ï¼‰ã€‚
auths.sspi_separator_replacement=è¦ä½¿ç”¨çš„分隔符代替\, / å’Œ @
-auths.sspi_separator_replacement_helper=用于替æ¢ä¸‹çº§ç™»å½•å称分隔符的字符 (例如 "DOMAIN\user") 中的 \ 和用户主åå­—(如"user@example.org"中的 @)。
+auths.sspi_separator_replacement_helper=用于替æ¢ä¸‹çº§ç™»å½•å称分隔符的字符(例如「DOMAIN\userã€ï¼‰ä¸­çš„ \ 和用户主å字(如「user@example.orgã€ä¸­çš„ @)。
auths.sspi_default_language=默认语言
auths.sspi_default_language_helper=SSPI è®¤è¯æ–¹æ³•为用户自动创建的默认语言。如果您想è¦è‡ªåŠ¨æ£€æµ‹åˆ°è¯­è¨€ï¼Œè¯·ç•™ç©ºã€‚
auths.tips=帮助æç¤º
auths.tips.oauth2.general=OAuth2 认è¯
auths.tips.oauth2.general.tip=当注册新的 OAuth2 èº«ä»½éªŒè¯æ—¶ï¼Œå›žè°ƒ/é‡å®šå‘ URL 应该是:
auths.tip.oauth2_provider=OAuth2 æä¾›ç¨‹åº
-auths.tip.bitbucket=在 %s 注册新的 OAuth ä½¿ç”¨è€…åŒæ—¶æ·»åŠ æƒé™â€œè´¦å·â€-“读å–â€
-auths.tip.nextcloud=使用下é¢çš„èœå•“设置(Settings) -> 安全(Security) -> OAuth 2.0 clientâ€åœ¨æ‚¨çš„实例上注册一个新的 OAuth 客户端。
+auths.tip.bitbucket=在 %s 注册新的 OAuth ä½¿ç”¨è€…åŒæ—¶æ·»åŠ æƒé™ã€Œè´¦å·ã€-「读å–ã€
+auths.tip.nextcloud=使用下é¢çš„èœå•「设置 -> 安全 -> OAuth 2.0 客户端ã€åœ¨æ‚¨çš„实例上注册一个新的 OAuth 客户端
auths.tip.dropbox=在 %s 上创建一个新的应用程åº
-auths.tip.facebook=`在 %s 注册一个新的应用,并添加产å“"Facebook 登录"`
+auths.tip.facebook=在 %s 注册一个新的应用,并添加产å“「Facebook 登录ã€
auths.tip.github=在 %s 注册一个 OAuth 应用程åº
auths.tip.gitlab_new=在 %s 注册一个新的应用
auths.tip.google_plus=从谷歌 API æŽ§åˆ¶å° %s 获得 OAuth2 客户端凭æ®
auths.tip.openid_connect=使用 OpenID 连接å‘现 URL ({server}/.well-known/openid-configuration) æ¥æŒ‡å®šç»ˆç‚¹
-auths.tip.twitter=访问 %s,创建应用并确ä¿å¯ç”¨äº†"å…许此应用程åºç”¨äºŽç™»å½• Twitter"的选项。
+auths.tip.twitter=访问 %s,创建应用并确ä¿å¯ç”¨äº†ã€Œå…许此应用程åºä½¿ç”¨ Twitter 登录ã€çš„选项
auths.tip.discord=在 %s 上注册新应用程åº
auths.tip.gitea=注册一个新的 OAuth2 应用程åºã€‚å¯ä»¥è®¿é—® %s 查看帮助
-auths.tip.yandex=在 %s 上创建一个新的应用程åºã€‚在“ Yandex.Passport APIâ€è¿™éƒ¨åˆ†ä¸­é€‰æ‹©ä»¥ä¸‹æƒé™ï¼šâ€œè®¿é—®ç”µå­é‚®ä»¶åœ°å€ï¼ˆAccess to email address)â€ï¼Œâ€œè®¿é—®ç”¨æˆ·å¤´åƒï¼ˆAccess to user avatar)â€å’Œâ€œè®¿é—®ç”¨æˆ·å,å字和姓æ°ï¼Œæ€§åˆ«ï¼ˆAccess to username, first name and surname, genderAccess to username, first name and surname, gender)â€
+auths.tip.yandex=在 %s 上创建一个新的应用。在「Yandex.Passport APIã€è¿™éƒ¨åˆ†ä¸­é€‰æ‹©ä»¥ä¸‹æƒé™ï¼šã€Œè®¿é—®é‚®ç®±åœ°å€ã€ï¼Œã€Œè®¿é—®ç”¨æˆ·å¤´åƒã€å’Œã€Œè®¿é—®ç”¨æˆ·å,å字和姓æ°ï¼Œæ€§åˆ«ã€
auths.tip.mastodon=输入您想è¦è®¤è¯çš„ mastodon 实例的自定义 URL (或使用默认值)
auths.edit=ä¿®æ”¹è®¤è¯æº
auths.activated=è¯¥è®¤è¯æºå·²ç»å¯ç”¨
-auths.new_success=å·²æ·»åŠ èº«ä»½éªŒè¯ '%s'。
+auths.new_success=已添加身份验è¯ã€Œ%sã€ã€‚
auths.update_success=è®¤è¯æºå·²ç»æ›´æ–°ã€‚
auths.update=æ›´æ–°è®¤è¯æº
auths.delete=åˆ é™¤è®¤è¯æº
@@ -3200,10 +3288,10 @@ auths.delete_auth_title=åˆ é™¤èº«ä»½éªŒè¯æº
auths.delete_auth_desc=åˆ é™¤ä¸€ä¸ªè®¤è¯æºå°†é˜»æ­¢ä½¿ç”¨å®ƒè¿›è¡Œç™»å½•。确认?
auths.still_in_used=è®¤è¯æºä»åœ¨ä½¿ç”¨ã€‚è¯·å…ˆè§£é™¤æˆ–è€…åˆ é™¤ä½¿ç”¨æ­¤è®¤è¯æºçš„用户。
auths.deletion_success=è®¤è¯æºå·²ç»æ›´æ–°ã€‚
-auths.login_source_exist=è®¤è¯æº '%s' å·²ç»å­˜åœ¨ã€‚
+auths.login_source_exist=è®¤è¯æºã€Œ%sã€å·²ç»å­˜åœ¨ã€‚
auths.login_source_of_type_exist=æ­¤ç±»åž‹çš„è®¤è¯æºå·²å­˜åœ¨ã€‚
-auths.unable_to_initialize_openid=无法åˆå§‹åŒ– OpenID Connect æä¾›å•†ï¼š %s
-auths.invalid_openIdConnectAutoDiscoveryURL=无效的 Auto Discovery URL (这必须是一个以 http:// 或 https://开头的有效的 URL)
+auths.unable_to_initialize_openid=无法åˆå§‹åŒ– OpenID Connect æä¾›å•†ï¼š%s
+auths.invalid_openIdConnectAutoDiscoveryURL=无效的自动å‘现 URL(这必须是一个以 http:// 或 https:// 开头的有效的 URL)
config.server_config=æœåС噍é…ç½®
config.app_name=站点åç§°
@@ -3231,8 +3319,6 @@ config.ssh_domain=SSH æœåŠ¡å™¨åŸŸå
config.ssh_port=端å£
config.ssh_listen_port=监å¬ç«¯å£
config.ssh_root_path=根目录
-config.ssh_key_test_path=密钥测试路径
-config.ssh_keygen_path=密钥生æˆå™¨ï¼ˆ'ssh-keygen')路径
config.ssh_minimum_key_size_check=密钥最å°é•¿åº¦æ£€æŸ¥
config.ssh_minimum_key_sizes=密钥最å°é•¿åº¦é™åˆ¶
@@ -3246,16 +3332,16 @@ config.db_type=类型
config.db_host=主机
config.db_name=æ•°æ®åº“åç§°
config.db_user=用户å
-config.db_schema=架构模å¼
+config.db_schema=æž¶æž„
config.db_ssl_mode=SSL
config.db_path=æ•°æ®åº“路径
config.service_config=æœåŠ¡é…ç½®
-config.register_email_confirm=需è¦ç”µå­é‚®ä»¶ç¡®è®¤æ³¨å†Œ
+config.register_email_confirm=需è¦é‚®ä»¶ç¡®è®¤æ³¨å†Œ
config.disable_register=ç¦æ­¢ç”¨æˆ·æ³¨å†Œ
-config.allow_only_internal_registration=åªå…许通过 Gitea 进行注册
+config.allow_only_internal_registration=ä»…å…许通过 Gitea 进行注册
config.allow_only_external_registration=ä»…å…许通过外部æœåŠ¡æ³¨å†Œ
-config.enable_openid_signup=å¯ç”¨ OpenID 自注册
+config.enable_openid_signup=å¯ç”¨ OpenID 自助注册
config.enable_openid_signin=å¯ç”¨ OpenID 登录
config.show_registration_button=显示注册按钮
config.require_sign_in_view=å¯ç”¨ç™»å½•访问é™åˆ¶
@@ -3263,12 +3349,12 @@ config.mail_notify=å¯ç”¨é‚®ä»¶é€šçŸ¥
config.enable_captcha=å¯ç”¨ç™»å½•验è¯ç 
config.active_code_lives=激活用户链接有效期
config.reset_password_code_lives=æ¢å¤è´¦æˆ·éªŒè¯ç è¿‡æœŸæ—¶é—´
-config.default_keep_email_private=默认情况下éšè—电å­é‚®ä»¶åœ°å€
+config.default_keep_email_private=默认éšè—邮箱地å€
config.default_allow_create_organization=默认情况下å…许创建组织
config.enable_timetracking=å¯ç”¨æ—¶é—´è·Ÿè¸ª
config.default_enable_timetracking=默认情况下å¯ç”¨æ—¶é—´è·Ÿè¸ª
config.default_allow_only_contributors_to_track_time=ä»…å…许æˆå‘˜è·Ÿè¸ªæ—¶é—´
-config.no_reply_address=éšè—电å­é‚®ä»¶åŸŸ
+config.no_reply_address=éšè—邮件域
config.default_visibility_organization=新组织的默认å¯è§æ€§
config.default_enable_dependencies=默认情况下å¯ç”¨å·¥å•ä¾èµ–
@@ -3279,7 +3365,7 @@ config.skip_tls_verify=跳过 TLS 验è¯
config.mailer_config=Mailer é…ç½®
config.mailer_enabled=å¯ç”¨æœåŠ¡
-config.mailer_enable_helo=å¯ç”¨HELO
+config.mailer_enable_helo=å¯ç”¨ HELO
config.mailer_name=任务åç§°
config.mailer_protocol=åè®®
config.mailer_smtp_addr=SMTP 地å€
@@ -3293,8 +3379,8 @@ config.mailer_use_dummy=Dummy
config.test_email_placeholder=电å­é‚®å€ (例如,test@example.com)
config.send_test_mail=å‘逿µ‹è¯•邮件
config.send_test_mail_submit=å‘é€
-config.test_mail_failed=å‘逿µ‹è¯•邮件至 '%s' 时失败:%v
-config.test_mail_sent=测试邮件已ç»å‘é€è‡³ '%s'。
+config.test_mail_failed=å‘逿µ‹è¯•邮件至「%sã€å¤±è´¥ï¼š%v
+config.test_mail_sent=测试邮件已ç»å‘é€è‡³ã€Œ%sã€ã€‚
config.oauth_config=OAuth é…ç½®
config.oauth_enabled=å¯ç”¨
@@ -3306,7 +3392,7 @@ config.cache_conn=Cache 连接字符串
config.cache_item_ttl=缓存项目 TTL
config.cache_test=测试缓存
config.cache_test_failed=缓存测试失败: %v。
-config.cache_test_slow=缓存测试æˆåŠŸï¼Œä½†å“应缓慢: %s。
+config.cache_test_slow=缓存测试æˆåŠŸï¼Œä½†å“应缓慢:%s。
config.cache_test_succeeded=缓存测试æˆåŠŸï¼Œåœ¨ %s 时间内得到å“应。
config.session_config=Session é…ç½®
@@ -3347,7 +3433,7 @@ config.set_setting_failed=设置 %s 失败
monitor.stats=统计
-monitor.cron=Cron 任务
+monitor.cron=计划任务
monitor.name=任务åç§°
monitor.schedule=任务安排
monitor.next=下次执行时间
@@ -3355,6 +3441,8 @@ monitor.previous=上次执行时间
monitor.execute_times=执行次数
monitor.process=è¿è¡Œä¸­è¿›ç¨‹
monitor.stacktrace=调用栈踪迹
+monitor.trace=追踪
+monitor.performance_logs=性能日志
monitor.processes_count=%d 个进程
monitor.download_diagnosis_report=下载诊断报告
monitor.desc=进程æè¿°
@@ -3363,11 +3451,10 @@ monitor.execute_time=执行时长
monitor.last_execution_result=结果
monitor.process.cancel=中止进程
monitor.process.cancel_desc=中止一个进程å¯èƒ½å¯¼è‡´æ•°æ®ä¸¢å¤±
-monitor.process.cancel_notices=中止:<strong>%s</strong> ?
monitor.process.children=å­è¿›ç¨‹
monitor.queues=队列
-monitor.queue=队列: %s
+monitor.queue=队列:%s
monitor.queue.name=åç§°
monitor.queue.type=类型
monitor.queue.exemplar=æ•°æ®ç±»åž‹
@@ -3384,7 +3471,7 @@ monitor.queue.settings.maxnumberworkers.error=最大工作者数必须是数字
monitor.queue.settings.submit=更新设置
monitor.queue.settings.changed=设置已更新
monitor.queue.settings.remove_all_items=移除全部
-monitor.queue.settings.remove_all_items_done=队列中的所有项目已被移除。
+monitor.queue.settings.remove_all_items_done=队列中的所有项目已移除。
notices.system_notice_list=系统æç¤ºç®¡ç†
notices.view_detail_header=查看æç¤ºè¯¦æƒ…
@@ -3399,16 +3486,16 @@ notices.type_1=仓库
notices.type_2=任务
notices.desc=æç¤ºæè¿°
notices.op=æ“作
-notices.delete_success=系统通知已被删除。
+notices.delete_success=系统通知已删除。
self_check.no_problem_found=尚未å‘现问题。
self_check.startup_warnings=å¯åŠ¨è­¦å‘Šï¼š
self_check.database_collation_mismatch=期望数æ®åº“使用的校验方å¼ï¼š%s
-self_check.database_collation_case_insensitive=æ•°æ®åº“正在使用一个校验 %s, è¿™æ˜¯ä¸€ä¸ªä¸æ•感的校验. 虽然Giteaå¯ä»¥ä¸Žå®ƒåˆä½œï¼Œä½†å¯èƒ½æœ‰ä¸€äº›ç½•è§çš„æƒ…况ä¸å¦‚预期的那样起作用。
-self_check.database_inconsistent_collation_columns=æ•°æ®åº“正在使用%s的排åºè§„则,但是这些列使用了ä¸åŒ¹é…的排åºè§„则。这å¯èƒ½ä¼šé€ æˆä¸€äº›æ„外问题。
-self_check.database_fix_mysql=对于MySQL/MariaDB用户,您å¯ä»¥ä½¿ç”¨â€œgitea doctor convertâ€å‘½ä»¤æ¥è§£å†³æ ¡éªŒé—®é¢˜ã€‚ 或者您也å¯ä»¥é€šè¿‡ "ALTER ... COLLATE ..." 这样的SQL æ¥æ‰‹åŠ¨è§£å†³è¿™ä¸ªé—®é¢˜ã€‚
-self_check.database_fix_mssql=对于MSSQL用户,您现在åªèƒ½é€šè¿‡"ALTER ... COLLATE ..."SQLs手动解决这个问题。
-self_check.location_origin_mismatch=å½“å‰ URL (%[1]s) 与 Gitea çš„ URL (%[2]s) ä¸åŒ¹é… 。 如果您正在使用åå‘代ç†ï¼Œè¯·ç¡®ä¿è®¾ç½®æ­£ç¡®çš„“主机â€å’Œâ€œX-转å‘-åŽŸå§‹â€æ ‡é¢˜ã€‚
+self_check.database_collation_case_insensitive=æ•°æ®åº“正在使用一个校验 %sï¼Œè¿™æ˜¯ä¸€ä¸ªä¸æ•感的校验。虽然 Gitea å¯ä»¥ä¸Žå®ƒåˆä½œï¼Œä½†å¯èƒ½æœ‰ä¸€äº›ç½•è§çš„æƒ…况ä¸å¦‚预期的那样起作用。
+self_check.database_inconsistent_collation_columns=æ•°æ®åº“正在使用 %s 的排åºè§„则,但是这些列使用了ä¸åŒ¹é…的排åºè§„则。这å¯èƒ½ä¼šé€ æˆä¸€äº›æ„外问题。
+self_check.database_fix_mysql=对于 MySQL/MariaDB 用户,您å¯ä»¥ä½¿ç”¨ã€Œgitea doctor convertã€å‘½ä»¤æ¥è§£å†³æ ¡éªŒé—®é¢˜ã€‚ 或者您也å¯ä»¥é€šè¿‡ã€ŒALTER ... COLLATE ...ã€è¿™æ ·çš„ SQL æ¥æ‰‹åŠ¨è§£å†³è¿™ä¸ªé—®é¢˜ã€‚
+self_check.database_fix_mssql=对于 MSSQL 用户,您现在åªèƒ½é€šè¿‡ã€ŒALTER ... COLLATE ...ã€SQL 手动解决这个问题。
+self_check.location_origin_mismatch=å½“å‰ URL(%[1]s)与 Gitea çš„ URL(%[2]s)ä¸åŒ¹é… 。 如果您正在使用åå‘代ç†ï¼Œè¯·ç¡®ä¿è®¾ç½®æ­£ç¡®çš„「Hostã€å’Œã€ŒX-Forwarded-Protoã€æ ‡å¤´ã€‚
[action]
create_repo=创建了仓库 <a href="%s">%s</a>
@@ -3416,10 +3503,10 @@ rename_repo=é‡å‘½å仓库 <code>%[1]s</code> 为 <a href="%[2]s">%[3]s</a>
commit_repo=推é€åˆ°äº†ä»“库 <a href="%[1]s">%[4]s</a> çš„ <a href="%[2]s">%[3]s</a> 分支
create_issue=`åˆ›å»ºäº†å·¥å• <a href="%[1]s">%[3]s#%[2]s</a>`
close_issue=`å…³é—­äº†å·¥å• <a href="%[1]s">%[3]s#%[2]s</a>`
-reopen_issue=`釿–°å¼€å¯äº†å·¥å• <a href="%[1]s">%[3]s#%[2]s</a>`
+reopen_issue=`釿–°æ‰“å¼€äº†å·¥å• <a href="%[1]s">%[3]s#%[2]s</a>`
create_pull_request=`创建了åˆå¹¶è¯·æ±‚ <a href="%[1]s">%[3]s#%[2]s</a>`
close_pull_request=`关闭了åˆå¹¶è¯·æ±‚ <a href="%[1]s">%[3]s#%[2]s</a>`
-reopen_pull_request=`釿–°å¼€å¯äº†åˆå¹¶è¯·æ±‚ <a href="%[1]s">%[3]s#%[2]s</a>`
+reopen_pull_request=`釿–°æ‰“开了åˆå¹¶è¯·æ±‚ <a href="%[1]s">%[3]s#%[2]s</a>`
comment_issue=`è¯„è®ºäº†å·¥å• <a href="%[1]s">%[3]s#%[2]s</a>`
comment_pull=`评论了åˆå¹¶è¯·æ±‚ <a href="%[1]s">%[3]s#%[2]s</a>`
merge_pull_request=`åˆå¹¶äº†åˆå¹¶è¯·æ±‚ <a href="%[1]s">%[3]s#%[2]s</a>`
@@ -3487,7 +3574,7 @@ no_subscriptions=无订阅
default_key=使用默认密钥签å
error.extract_sign=无法æå–ç­¾å
error.generate_hash=æ— æ³•ç”Ÿæˆæäº¤çš„å“ˆå¸Œ
-error.no_committer_account=æ²¡æœ‰å¸æˆ·é“¾æŽ¥åˆ°æäº¤è€…的电å­é‚®ä»¶
+error.no_committer_account=æ²¡æœ‰å¸æˆ·é“¾æŽ¥åˆ°æäº¤è€…的邮箱
error.no_gpg_keys_found=找ä¸åˆ°æ­¤ç­¾å对应的密钥
error.not_signed_commit=未签åçš„æäº¤
error.failed_retrieval_gpg_keys=找ä¸åˆ°ä»»ä½•与该æäº¤è€…è´¦å·ç›¸å…³çš„密钥
@@ -3530,23 +3617,24 @@ versions=版本
versions.view_all=查看全部
dependency.id=ID
dependency.version=版本
+search_in_external_registry=在 %s 中æœç´¢
alpine.registry=通过在您的 <code>/etc/apk/repositories</code> 文件中添加 URL æ¥è®¾ç½®æ­¤æ³¨å†Œä¸­å¿ƒï¼š
alpine.registry.key=下载注册中心公开的 RSA 密钥到 <code>/etc/apk/keys/</code> 文件夹æ¥éªŒè¯ç´¢å¼•ç­¾å:
alpine.registry.info=从下é¢çš„列表中选择 $branch å’Œ $repository。
-alpine.install=è¦å®‰è£…包,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
+alpine.install=è¦å®‰è£…软件包,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
alpine.repository=仓库信æ¯
alpine.repository.branches=分支
-alpine.repository.repositories=仓库管ç†
+alpine.repository.repositories=仓库
alpine.repository.architectures=æž¶æž„
arch.registry=添加具有相关仓库和架构的æœåŠ¡å™¨åˆ° <code>/etc/pacman.conf</code> 中:
arch.install=使用 pacman åŒæ­¥è½¯ä»¶åŒ…:
arch.repository=仓库信æ¯
-arch.repository.repositories=仓库管ç†
+arch.repository.repositories=仓库
arch.repository.architectures=æž¶æž„
cargo.registry=在 Cargo é…置文件中设置此注册中心(例如:<code>~/.cargo/config.toml</code>):
cargo.install=è¦ä½¿ç”¨ Cargo 安装软件包,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
chef.registry=在您的 <code>~/.chef/config.rb</code> 文件中设置此注册中心:
-chef.install=è¦å®‰è£…包,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
+chef.install=è¦å®‰è£…软件包,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
composer.registry=在您的 <code>~/.composer/config.json</code> 文件中设置此注册中心:
composer.install=è¦ä½¿ç”¨ Composer 安装软件包,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
composer.dependencies=ä¾èµ–
@@ -3559,17 +3647,18 @@ conda.install=è¦ä½¿ç”¨ Conda 安装软件包,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
container.details.type=镜åƒç±»åž‹
container.details.platform=å¹³å°
container.pull=从命令行拉å–镜åƒï¼š
-container.digest=摘è¦ï¼š
+container.images=镜åƒ
+container.digest=摘è¦
container.multi_arch=OS / Arch
container.layers=镜åƒå±‚
container.labels=标签
container.labels.key=é”®
container.labels.value=值
cran.registry=在您的 <code>Rprofile.site</code> 文件中设置此注册中心:
-cran.install=è¦å®‰è£…包,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
+cran.install=è¦å®‰è£…软件包,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
debian.registry=从命令行设置此注册中心:
debian.registry.info=从下é¢çš„列表中选择 $distribution å’Œ $component。
-debian.install=è¦å®‰è£…包,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
+debian.install=è¦å®‰è£…软件包,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
debian.repository=仓库信æ¯
debian.repository.distributions=å‘行版
debian.repository.components=组件
@@ -3600,7 +3689,7 @@ pypi.install=è¦ä½¿ç”¨ pip 安装软件包,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
rpm.registry=从命令行设置此注册中心:
rpm.distros.redhat=在基于 RedHat çš„å‘行版
rpm.distros.suse=在基于 SUSE çš„å‘行版
-rpm.install=è¦å®‰è£…包,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
+rpm.install=è¦å®‰è£…软件包,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
rpm.repository=仓库信æ¯
rpm.repository.architectures=æž¶æž„
rpm.repository.multiple_groups=此软件包å¯åœ¨å¤šä¸ªç»„中使用。
@@ -3611,11 +3700,11 @@ rubygems.dependencies.development=å¼€å‘ä¾èµ–
rubygems.required.ruby=éœ€è¦ Ruby 版本
rubygems.required.rubygems=éœ€è¦ RubyGem 版本
swift.registry=从命令行设置此注册中心:
-swift.install=在你的 <code>Package.swift</code>文件中添加该包:
+swift.install=在您的 <code>Package.swift</code>文件中添加该包:
swift.install2=å¹¶è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
vagrant.install=è‹¥è¦æ·»åŠ ä¸€ä¸ª Vagrant box,请è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
settings.link=将此软件包链接到仓库
-settings.link.description=如果您将一个软件包与一个代ç åº“链接起æ¥ï¼Œè½¯ä»¶åŒ…将显示在代ç åº“的软件包列表中。
+settings.link.description=如果您将一个软件包与一个仓库链接起æ¥ï¼Œè½¯ä»¶åŒ…将显示在仓库的软件包列表中。
settings.link.select=选择仓库
settings.link.button=更新仓库链接
settings.link.success=仓库链接已æˆåŠŸæ›´æ–°ã€‚
@@ -3623,13 +3712,13 @@ settings.link.error=更新仓库链接失败。
settings.delete=删除软件包
settings.delete.description=删除软件包是永久性的,无法撤消。
settings.delete.notice=您将è¦åˆ é™¤ %s (%s)。此æ“作是ä¸å¯é€†çš„,您确定å—?
-settings.delete.success=软件包已被删除。
+settings.delete.success=软件包已删除。
settings.delete.error=删除软件包失败。
owner.settings.cargo.title=Cargo 注册中心索引
owner.settings.cargo.initialize=åˆå§‹åŒ–索引
-owner.settings.cargo.initialize.description=使用 Cargo 注册中心时需è¦ä¸€ä¸ªç‰¹æ®Šç´¢å¼•çš„ Git ä»“åº“ã€‚ä½¿ç”¨æ­¤é€‰é¡¹å°†ï¼ˆé‡æ–°ï¼‰åˆ›å»ºå­˜å‚¨åº“并自动é…置它。
-owner.settings.cargo.initialize.error=åˆå§‹åŒ–Cargo索引失败: %v
-owner.settings.cargo.initialize.success=Cargoç´¢å¼•å·²ç»æˆåŠŸåˆ›å»ºã€‚
+owner.settings.cargo.initialize.description=使用 Cargo 注册中心时需è¦ä¸€ä¸ªç‰¹æ®Šç´¢å¼•çš„ Git ä»“åº“ã€‚ä½¿ç”¨æ­¤é€‰é¡¹å°†ï¼ˆé‡æ–°ï¼‰åˆ›å»ºä»“库并自动é…置它。
+owner.settings.cargo.initialize.error=åˆå§‹åŒ– Cargo 索引失败: %v
+owner.settings.cargo.initialize.success=Cargo ç´¢å¼•å·²ç»æˆåŠŸåˆ›å»ºã€‚
owner.settings.cargo.rebuild=é‡å»ºç´¢å¼•
owner.settings.cargo.rebuild.description=如果索引与存储的 Cargo 包ä¸åŒæ­¥ï¼Œé‡å»ºå¯èƒ½ä¼šæœ‰ç”¨ã€‚
owner.settings.cargo.rebuild.error=无法é‡å»º Cargo 索引: %v
@@ -3642,7 +3731,7 @@ owner.settings.cleanuprules.preview=清ç†è§„则预览
owner.settings.cleanuprules.preview.overview=%d 个软件包计划被删除。
owner.settings.cleanuprules.preview.none=清ç†è§„则与任何软件包都ä¸åŒ¹é…。
owner.settings.cleanuprules.enabled=å¯ç”¨
-owner.settings.cleanuprules.pattern_full_match=应用规则到完整软件包åç§°
+owner.settings.cleanuprules.pattern_full_match=应用表达å¼åˆ°å®Œæ•´è½¯ä»¶åŒ…åç§°
owner.settings.cleanuprules.keep.title=与这些规则相匹é…的版本å³ä½¿ä¸Žä¸‹é¢çš„删除规则相匹é…,也将予以ä¿ç•™ã€‚
owner.settings.cleanuprules.keep.count=ä¿ç•™æœ€æ–°çš„
owner.settings.cleanuprules.keep.count.1=æ¯ä¸ªè½¯ä»¶åŒ…1个版本
@@ -3660,23 +3749,30 @@ owner.settings.chef.keypair.description=需è¦å¯†é’¥å¯¹æ‰èƒ½å‘ Chef 注册中å
[secrets]
secrets=密钥
-description=Secrets 将被传给特定的 Actions,其它情况将ä¸èƒ½è¯»å–
+description=密钥将被传给特定的工作æµï¼Œå…¶å®ƒæƒ…况无法读å–。
none=还没有密钥。
-creation=添加密钥
-creation.name_placeholder=ä¸åŒºåˆ†å¤§å°å†™ï¼Œå­—æ¯æ•°å­—或下划线ä¸èƒ½ä»¥GITEA_ 或 GITHUB_ 开头。
-creation.value_placeholder=输入任何内容,开头和结尾的空白都会被çœç•¥
-creation.success=您的密钥 '%s' 添加æˆåŠŸã€‚
-creation.failed=添加密钥失败。
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=组织æè¿°
+creation.name_placeholder=ä¸åŒºåˆ†å¤§å°å†™ï¼Œä»…é™å­—æ¯æ•°å­—或下划线且ä¸èƒ½ä»¥ GITEA_ 或 GITHUB_ 开头
+creation.value_placeholder=输入任何内容,开头和结尾的空白将会被忽略
+creation.description_placeholder=输入简短æè¿°ï¼ˆå¯é€‰ï¼‰
+
+save_success=密钥「%sã€ä¿å­˜æˆåŠŸã€‚
+save_failed=密钥ä¿å­˜å¤±è´¥ã€‚
+
+add_secret=添加密钥
+edit_secret=编辑密钥
deletion=删除密钥
-deletion.description=删除密钥是永久性的,无法撤消。继续å—?
-deletion.success=此Secret已被删除。
+deletion.description=删除密钥是永久性的且无法撤消。继续?
+deletion.success=此密钥已删除。
deletion.failed=删除密钥失败。
management=密钥管ç†
[actions]
-actions=Actions
+actions=工作æµ
-unit.desc=管ç†Actions
+unit.desc=管ç†å·¥ä½œæµ
status.unknown=未知
status.waiting=等待中
@@ -3687,20 +3783,20 @@ status.cancelled=已喿¶ˆ
status.skipped=已忽略
status.blocked=阻塞中
-runners=Runners
-runners.runner_manage_panel=Runners管ç†
-runners.new=创建 Runner
+runners=è¿è¡Œå™¨
+runners.runner_manage_panel=è¿è¡Œå™¨ç®¡ç†
+runners.new=创建新è¿è¡Œå™¨
runners.new_notice=如何å¯åŠ¨ä¸€ä¸ªè¿è¡Œå™¨
runners.status=状æ€
runners.id=ID
runners.name=åç§°
runners.owner_type=类型
-runners.description=组织æè¿°
+runners.description=æè¿°
runners.labels=标签
runners.last_online=上次在线时间
-runners.runner_title=Runner
-runners.task_list=最近在此runner上的任务
-runners.task_list.no_tasks=还没有任务。
+runners.runner_title=è¿è¡Œå™¨
+runners.task_list=最近在此è¿è¡Œå™¨ä¸Šçš„任务
+runners.task_list.no_tasks=ç›®å‰è¿˜æ²¡æœ‰ä»»åŠ¡ã€‚
runners.task_list.run=执行
runners.task_list.status=状æ€
runners.task_list.repository=仓库
@@ -3709,27 +3805,28 @@ runners.task_list.done_at=完æˆäºŽ
runners.edit_runner=编辑è¿è¡Œå™¨
runners.update_runner=更新更改
runners.update_runner_success=è¿è¡Œå™¨æ›´æ–°æˆåŠŸ
-runners.update_runner_failed=æ›´æ–°è¿è¡Œå™¨å¤±è´¥
-runners.delete_runner=删除è¿è¡Œå™¨
+runners.update_runner_failed=è¿è¡Œå™¨æ›´æ–°å¤±è´¥
+runners.delete_runner=删除此è¿è¡Œå™¨
runners.delete_runner_success=è¿è¡Œå™¨åˆ é™¤æˆåŠŸ
-runners.delete_runner_failed=删除è¿è¡Œå™¨å¤±è´¥
+runners.delete_runner_failed=è¿è¡Œå™¨åˆ é™¤å¤±è´¥
runners.delete_runner_header=确认è¦åˆ é™¤æ­¤è¿è¡Œå™¨
runners.delete_runner_notice=如果一个任务正在è¿è¡Œåœ¨æ­¤è¿è¡Œå™¨ä¸Šï¼Œå®ƒå°†è¢«ç»ˆæ­¢å¹¶æ ‡è®°ä¸ºå¤±è´¥ã€‚它å¯èƒ½ä¼šæ‰“断正在构建的工作æµã€‚
-runners.none=æ— å¯ç”¨çš„ Runner
+runners.none=æ— å¯ç”¨è¿è¡Œå™¨
runners.status.unspecified=未知
runners.status.idle=空闲
-runners.status.active=激活
+runners.status.active=å¯ç”¨
runners.status.offline=离线
runners.version=版本
runners.reset_registration_token=é‡ç½®æ³¨å†Œä»¤ç‰Œ
+runners.reset_registration_token_confirm=是å¦åŠé”€å½“å‰ä»¤ç‰Œå¹¶ç”Ÿæˆä¸€ä¸ªæ–°ä»¤ç‰Œï¼Ÿ
runners.reset_registration_token_success=æˆåŠŸé‡ç½®è¿è¡Œå™¨æ³¨å†Œä»¤ç‰Œ
runs.all_workflows=所有工作æµ
runs.commit=æäº¤
runs.scheduled=已计划的
runs.pushed_by=推é€è€…
-runs.invalid_workflow_helper=工作æµé…置文件无效。请检查您的é…置文件: %s
-runs.no_matching_online_runner_helper=æ²¡æœ‰åŒ¹é…æ ‡ç­¾çš„在线 runner: %s
+runs.invalid_workflow_helper=工作æµé…置文件无效。请检查您的é…置文件:%s
+runs.no_matching_online_runner_helper=æ²¡æœ‰åŒ¹é… %s 标签的在线è¿è¡Œå™¨
runs.no_job_without_needs=工作æµå¿…须包å«è‡³å°‘一个没有ä¾èµ–关系的作业。
runs.no_job=工作æµå¿…须包å«è‡³å°‘一个作业
runs.actor=æ“作者
@@ -3738,22 +3835,28 @@ runs.actors_no_select=所有æ“作者
runs.status_no_select=所有状æ€
runs.no_results=没有匹é…的结果。
runs.no_workflows=ç›®å‰è¿˜æ²¡æœ‰å·¥ä½œæµã€‚
-runs.no_workflows.quick_start=ä¸çŸ¥é“如何使用 Gitea Actionså—?请查看 <a target="_blank" rel="noopener noreferrer" href="%s">快速å¯åŠ¨æŒ‡å—</a>。
-runs.no_workflows.documentation=关于Gitea Actions的更多信æ¯ï¼Œè¯·å‚阅 <a target="_blank" rel="noopener noreferrer" href="%s">文档</a>。
+runs.no_workflows.quick_start=ä¸çŸ¥é“如何使用 Gitea 工作æµå—?请查看<a target="_blank" rel="noopener noreferrer" href="%s">快速开始指å—</a>。
+runs.no_workflows.documentation=关于 Gitea 工作æµçš„æ›´å¤šä¿¡æ¯ï¼Œè¯·å‚阅<a target="_blank" rel="noopener noreferrer" href="%s">文档</a>。
runs.no_runs=工作æµå°šæœªè¿è¡Œè¿‡ã€‚
-runs.empty_commit_message=(空白的æäº¤æ¶ˆæ¯)
-runs.expire_log_message=旧的日志已被清除
+runs.empty_commit_message=(空白的æäº¤æ¶ˆæ¯ï¼‰
+runs.expire_log_message=旧的日志已清除。
+runs.delete=删除工作æµè¿è¡Œ
+runs.cancel=å–æ¶ˆå·¥ä½œæµè¿è¡Œ
+runs.delete.description=æ‚¨ç¡®å®šè¦æ°¸ä¹…删除此工作æµè¿è¡Œå—?此æ“作无法撤消。
+runs.not_done=此工作æµè¿è¡Œå°šæœªå®Œæˆã€‚
+runs.view_workflow_file=æŸ¥çœ‹å·¥ä½œæµæ–‡ä»¶
workflow.disable=ç¦ç”¨å·¥ä½œæµ
-workflow.disable_success=å·¥ä½œæµ '%s' å·²æˆåŠŸç¦ç”¨ã€‚
+workflow.disable_success=工作æµã€Œ%sã€å·²æˆåŠŸç¦ç”¨ã€‚
workflow.enable=å¯ç”¨å·¥ä½œæµ
-workflow.enable_success=å·¥ä½œæµ '%s' å·²æˆåŠŸå¯ç”¨ã€‚
+workflow.enable_success=工作æµã€Œ%sã€å·²æˆåŠŸå¯ç”¨ã€‚
workflow.disabled=工作æµå·²ç¦ç”¨ã€‚
workflow.run=è¿è¡Œå·¥ä½œæµ
-workflow.not_found=å·¥ä½œæµ %s 未找到。
-workflow.run_success=å·¥ä½œæµ %s å·²æˆåŠŸè¿è¡Œã€‚
+workflow.not_found=工作æµã€Œ%sã€æœªæ‰¾åˆ°ã€‚
+workflow.run_success=工作æµã€Œ%sã€å·²æˆåŠŸè¿è¡Œã€‚
workflow.from_ref=使用工作æµä»Ž
-workflow.has_workflow_dispatch=æ­¤ Workflow 有一个 Workflow_dispatch 事件触å‘器。
+workflow.has_workflow_dispatch=æ­¤å·¥ä½œæµæœ‰ä¸€ä¸ª workflow_dispatch 事件触å‘器。
+workflow.has_no_workflow_dispatch=工作æµã€Œ%sã€æ²¡æœ‰ workflow_dispatch 事件触å‘器。
need_approval_desc=该工作æµç”±æ´¾ç”Ÿä»“库的åˆå¹¶è¯·æ±‚所触å‘ï¼Œéœ€è¦æ‰¹å‡†æ–¹å¯è¿è¡Œã€‚
@@ -3763,22 +3866,26 @@ variables.creation=添加å˜é‡
variables.none=ç›®å‰è¿˜æ²¡æœ‰å˜é‡ã€‚
variables.deletion=删除å˜é‡
variables.deletion.description=删除å˜é‡æ˜¯æ°¸ä¹…性的,无法撤消。继续å—?
-variables.description=å˜é‡å°†è¢«ä¼ ç»™ç‰¹å®šçš„ Actions,其它情况将ä¸èƒ½è¯»å–
+variables.description=å˜é‡å°†è¢«ä¼ ç»™ç‰¹å®šçš„工作æµï¼Œå…¶å®ƒæƒ…况下无法读å–。
variables.id_not_exist=ID为 %d çš„å˜é‡ä¸å­˜åœ¨ã€‚
variables.edit=编辑å˜é‡
-variables.deletion.failed=删除å˜é‡å¤±è´¥ã€‚
-variables.deletion.success=å˜é‡å·²è¢«åˆ é™¤ã€‚
-variables.creation.failed=添加å˜é‡å¤±è´¥ã€‚
-variables.creation.success=å˜é‡ “%s†添加æˆåŠŸã€‚
-variables.update.failed=编辑å˜é‡å¤±è´¥ã€‚
-variables.update.success=该å˜é‡å·²è¢«ç¼–辑。
+variables.deletion.failed=å˜é‡åˆ é™¤å¤±è´¥ã€‚
+variables.deletion.success=å˜é‡å·²åˆ é™¤ã€‚
+variables.creation.failed=å˜é‡æ·»åŠ å¤±è´¥ã€‚
+variables.creation.success=å˜é‡ã€Œ%sã€æ·»åŠ æˆåŠŸã€‚
+variables.update.failed=å˜é‡ç¼–辑失败。
+variables.update.success=å˜é‡å·²ç¼–辑。
+logs.always_auto_scroll=总是自动滚动日志
+logs.always_expand_running=总是展开è¿è¡Œæ—¥å¿—
[projects]
deleted.display_name=已删除项目
type-1.display_name=个人项目
type-2.display_name=仓库项目
type-3.display_name=组织项目
+enter_fullscreen=å…¨å±
+exit_fullscreen=退出全å±
[git.filemode]
changed_filemode=%[1]s -> %[2]s
diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini
index 77f8d8a25d..3ae5954ea3 100644
--- a/options/locale/locale_zh-HK.ini
+++ b/options/locale/locale_zh-HK.ini
@@ -118,6 +118,7 @@ show_private=ç§æœ‰åº«
issues.in_your_repos=屬於該用戶儲存庫的
+
[explore]
repos=儲存庫
users=使用者
@@ -363,7 +364,6 @@ file_history=文件歷å²
file_view_raw=查看原始文件
file_permalink=永久連çµ
-stored_lfs=儲存到到 Git LFS
editor.preview_changes=é è¦½æ›´æ”¹
editor.or=或
@@ -374,6 +374,7 @@ editor.create_new_branch=建立 <strong>新的分支</strong> 為此æäº¤å’Œé–‹
editor.cancel=å–æ¶ˆ
editor.no_changes_to_show=沒有å¯ä»¥é¡¯ç¤ºçš„變更。
+
commits.commits=次程å¼ç¢¼æäº¤
commits.author=作者
commits.message=備註
@@ -415,7 +416,6 @@ issues.delete_branch_at=`刪除分支 <b>%s</b> %s`
issues.filter_label=標籤篩é¸
issues.filter_milestone=里程碑篩é¸
issues.filter_assignee=指派人篩é¸
-issues.filter_assginee_no_assignee=無負責人
issues.filter_type=類型篩é¸
issues.filter_type.all_issues=所有å•題
issues.filter_type.assigned_to_you=指派給您的
@@ -569,7 +569,6 @@ settings.delete_notices_1=- æ­¤æ“作 <strong>ä¸å¯ä»¥</strong> 被回滾。
settings.delete_collaborator=移除æˆå“¡
settings.teams=組織團隊
settings.add_webhook=建立 Webhook
-settings.webhook.test_delivery=測試推é€
settings.webhook.request=請求內容
settings.webhook.response=響應內容
settings.webhook.headers=標題
@@ -657,10 +656,11 @@ settings.visibility.private_shortname=ç§æœ‰åº«
settings.update_settings=更新組織設定
settings.update_setting_success=組織設定已更新。
+
+
settings.delete=刪除組織
settings.delete_account=刪除當å‰çµ„ç¹”
settings.confirm_delete_account=確èªåˆªé™¤çµ„ç¹”
-settings.delete_org_title=刪除組織
settings.hooks_desc=新增 webhooks 將觸發在這個組織下 <strong>全部的儲存庫</strong> 。
@@ -685,6 +685,7 @@ teams.delete_team_success=該團隊已被刪除。
teams.repositories=團隊儲存庫
+
[admin]
dashboard=控制é¢ç‰ˆ
organizations=組織管ç†
@@ -815,8 +816,6 @@ config.ssh_enabled=已啟用
config.ssh_port=埠
config.ssh_listen_port=監è½åŸ 
config.ssh_root_path=根路徑
-config.ssh_key_test_path=金鑰測試路徑
-config.ssh_keygen_path=金鑰產生 (' ssh-keygen ') 路徑
config.ssh_minimum_key_size_check=金鑰最å°å¤§å°æª¢æŸ¥
config.ssh_minimum_key_sizes=金鑰最å°å¤§å°
@@ -962,6 +961,11 @@ owner.settings.cleanuprules.enabled=已啟用
[secrets]
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=組織æè¿°
+
+
+
[actions]
diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini
index 948b47bc9c..de0a4ed8a8 100644
--- a/options/locale/locale_zh-TW.ini
+++ b/options/locale/locale_zh-TW.ini
@@ -54,6 +54,7 @@ webauthn_reload=釿–°è¼‰å…¥
repository=儲存庫
organization=組織
mirror=é¡åƒ
+issue_milestone=里程碑
new_repo=新增儲存庫
new_migrate=é·ç§»å¤–部儲存庫
new_mirror=æ–°é¡åƒ
@@ -382,6 +383,7 @@ show_only_public=åªé¡¯ç¤ºå…¬é–‹
issues.in_your_repos=在您的儲存庫中
+
[explore]
repos=儲存庫
users=使用者
@@ -442,7 +444,6 @@ oauth_signup_submit=完æˆå¸³æˆ¶
oauth_signin_tab=連çµåˆ°ç¾æœ‰å¸³æˆ¶
oauth_signin_title=登入以授權連çµå¸³æˆ¶
oauth_signin_submit=連çµå¸³æˆ¶
-oauth.signin.error=è™•ç†æŽˆæ¬Šè«‹æ±‚æ™‚ç™¼ç”ŸéŒ¯èª¤ã€‚å¦‚æžœé€™å€‹å•題æŒçºŒç™¼ç”Ÿï¼Œè«‹è¯çµ¡ç¶²ç«™ç®¡ç†å“¡ã€‚
oauth.signin.error.access_denied=授權請求被拒絕。
oauth.signin.error.temporarily_unavailable=授權失敗,因為èªè­‰ä¼ºæœå™¨æš«æ™‚無法使用。請ç¨å¾Œå†è©¦ã€‚
oauth_callback_unable_auto_reg=自助註冊已啟用,但是 OAuth2 æä¾›è€… %[1]s å›žå‚³çš„çµæžœç¼ºå°‘欄ä½ï¼š%[2]s,導致無法自動建立帳號。請建立新帳號或是連çµè‡³æ—¢å­˜çš„帳號,或是è¯çµ¡ç¶²ç«™ç®¡ç†è€…。
@@ -713,8 +714,6 @@ public_profile=公開的個人資料
biography_placeholder=告訴我們一些關於您的事情å§! (您å¯ä»¥ä½¿ç”¨ Markdown)
location_placeholder=與其他人分享您的大概ä½ç½®
profile_desc=控制您的個人檔案會如何呈ç¾çµ¦å…¶å¥¹ä½¿ç”¨è€…。您的主è¦é›»å­éƒµä»¶åœ°å€æœƒè¢«ç”¨æ–¼é€šçŸ¥ã€å¯†ç¢¼æ•‘æ´ä»¥åŠç¶²é ä¸Šçš„ Git æ“作。
-password_username_disabled=éžæœ¬åœ°ä½¿ç”¨è€…ä¸å…許更改他們的帳號。詳細資訊請è¯çµ¡æ‚¨çš„系統管ç†å“¡ã€‚
-password_full_name_disabled=您ä¸è¢«å…許更改他們的全å。詳細資訊請è¯çµ¡æ‚¨çš„系統管ç†å“¡ã€‚
full_name=å…¨å
website=個人網站
location=所在地å€
@@ -905,7 +904,6 @@ delete_token_success=已刪除 Token。使用此 Token 的應用程å¼ç„¡æ³•å†å
repo_and_org_access=儲存庫和組織存å–
permissions_public_only=僅公開
permissions_access_all=全部 (公開ã€ç§æœ‰èˆ‡å—é™)
-select_permissions=鏿“‡æ¬Šé™
permission_not_set=尚未設定
permission_no_access=沒有權é™
permission_read=讀å–
@@ -1108,9 +1106,6 @@ blame.ignore_revs=忽略 <a href="%s">.git-blame-ignore-revs</a> 中的修訂。
blame.ignore_revs.failed=忽略 <a href="%s">.git-blame-ignore-revs</a> 中的修訂失敗。
user_search_tooltip=顯示最多 30 個使用者
-tree_path_not_found_commit=路徑 %[1]s 在æäº¤ %[2]s 中ä¸å­˜åœ¨
-tree_path_not_found_branch=路徑 %[1]s 在分支 %[2]s 中ä¸å­˜åœ¨
-tree_path_not_found_tag=路徑 %[1]s 在標籤 %[2]s 中ä¸å­˜åœ¨
transfer.accept=åŒæ„轉移
transfer.accept_desc=轉移到「%sã€
@@ -1244,6 +1239,7 @@ labels=標籤
org_labels_desc=組織層級標籤å¯ç”¨æ–¼æ­¤çµ„織下的<strong>所有存儲庫</strong>。
org_labels_desc_manage=管ç†
+milestone=里程碑
milestones=里程碑
commits=æäº¤æ­·å²
commit=æäº¤
@@ -1277,7 +1273,6 @@ file_copy_permalink=複製固定連çµ
view_git_blame=檢視 Git Blame
video_not_supported_in_browser=您的ç€è¦½å™¨ä¸æ”¯æ´ä½¿ç”¨ HTML5 播放影片。
audio_not_supported_in_browser=您的ç€è¦½å™¨ä¸æ”¯æ´ HTML5 的「audioã€æ¨™ç±¤
-stored_lfs=已使用 Git LFS 儲存
symbolic_link=符號連çµ
executable_file=å¯åŸ·è¡Œæª”
vendored=已供應
@@ -1323,7 +1318,6 @@ editor.update=æ›´æ–° %s
editor.delete=刪除 %s
editor.patch=套用 Patch
editor.patching=正在 Patch:
-editor.fail_to_apply_patch=無法套用 Patch「%sã€
editor.new_patch=新增 Patch
editor.commit_message_desc=(é¸ç”¨) 加入詳細說明...
editor.signoff_desc=在æäº¤è¨Šæ¯åº•部加入æäº¤è€…的「Signed-off-byã€è³‡è¨Šã€‚
@@ -1341,8 +1335,6 @@ editor.branch_already_exists=此儲存庫已有å為「%sã€çš„分支。
editor.directory_is_a_file=目錄å稱「%sã€å·²è¢«æ­¤å„²å­˜åº«çš„æª”案使用。
editor.file_is_a_symlink=`"%s" 是一個符號連çµã€‚符號連çµç„¡æ³•在網é ç·¨è¼¯å™¨ä¸­ç·¨è¼¯`
editor.filename_is_a_directory=檔å「%sã€å·²è¢«æ­¤å„²å­˜åº«çš„目錄å稱使用。
-editor.file_editing_no_longer_exists=æ­£è¦ç·¨è¼¯çš„æª”案「%sã€å·²ä¸å­˜åœ¨æ­¤å„²å­˜åº«ä¸­ã€‚
-editor.file_deleting_no_longer_exists=æ­£è¦åˆªé™¤çš„æª”案「%sã€å·²ä¸å­˜åœ¨æ­¤å„²å­˜åº«ä¸­ã€‚
editor.file_changed_while_editing=檔案內容在您編輯的途中已被變更。<a target="_blank" rel="noopener noreferrer" href="%s">按一下此處</a>查看更動的地方或<strong>冿¬¡æäº¤</strong>以覆蓋這些變更。
editor.file_already_exists=此儲存庫已有å為「%sã€çš„æª”案。
editor.commit_id_not_matching=æäº¤ ID 與您開始編輯時的 ID ä¸åŒ¹é…。請æäº¤åˆ°ä¸€å€‹è£œä¸åˆ†æ”¯ç„¶å¾Œåˆä½µã€‚
@@ -1350,8 +1342,6 @@ editor.push_out_of_date=推é€ä¼¼ä¹Žå·²éŽæ™‚。
editor.commit_empty_file_header=æäº¤ç©ºç™½æª”案
editor.commit_empty_file_text=你準備æäº¤çš„æª”案是空白的,是å¦ç¹¼çºŒï¼Ÿ
editor.no_changes_to_show=沒有å¯ä»¥é¡¯ç¤ºçš„變更。
-editor.fail_to_update_file=æ›´æ–°/建立檔案「%sã€å¤±æ•—。
-editor.fail_to_update_file_summary=錯誤訊æ¯:
editor.push_rejected_no_message=該變更被伺æœå™¨æ‹’絕但未æä¾›å…¶ä»–資訊。請檢查 Git Hook。
editor.push_rejected=該變更被伺æœå™¨æ‹’絕。請檢查 Git Hook。
editor.push_rejected_summary=完整的拒絕訊æ¯:
@@ -1366,6 +1356,7 @@ editor.require_signed_commit=分支僅接å—經簽署的æäº¤
editor.cherry_pick=Cherry-pick %s 到:
editor.revert=還原 %s 到:
+
commits.desc=ç€è¦½åŽŸå§‹ç¢¼ä¿®æ”¹æ­·ç¨‹ã€‚
commits.commits=次程å¼ç¢¼æäº¤
commits.no_commits=沒有共åŒçš„æäº¤ã€‚「%sã€å’Œã€Œ%sã€çš„æ­·å²å®Œå…¨ä¸åŒã€‚
@@ -1519,8 +1510,6 @@ issues.filter_project=專案
issues.filter_project_all=所有專案
issues.filter_project_none=æœªé¸æ“‡å°ˆæ¡ˆ
issues.filter_assignee=負責人
-issues.filter_assginee_no_select=所有負責人
-issues.filter_assginee_no_assignee=沒有負責人
issues.filter_poster=作者
issues.filter_user_placeholder=æœå°‹ä½¿ç”¨è€…
issues.filter_user_no_select=所有使用者
@@ -1640,13 +1629,12 @@ issues.attachment.open_tab=`在新分é ä¸­æŸ¥çœ‹ã€Œ%sã€`
issues.attachment.download=`點擊下載「%sã€`
issues.subscribe=訂閱
issues.unsubscribe=å–æ¶ˆè¨‚é–±
-issues.unpin_issue=å–æ¶ˆå›ºå®šå•題
+issues.unpin=å–æ¶ˆå›ºå®š
issues.max_pinned=您ä¸èƒ½å›ºå®šæ›´å¤šå•題
issues.pin_comment=固定於 %s
issues.unpin_comment=å–æ¶ˆå›ºå®šæ–¼ %s
issues.lock=鎖定å°è©±
issues.unlock=解鎖å°è©±
-issues.lock.unknown_reason=由於未知的原因而無法鎖定å•題。
issues.lock_duplicate=å•題無法被鎖定兩次。
issues.unlock_error=無法解鎖未被鎖定的å•題。
issues.lock_with_reason=因為 <strong>%s</strong> 而鎖定,並將å°è©±è¨­ç‚ºå”作者é™å®š %s
@@ -1675,16 +1663,13 @@ issues.timetracker_timer_manually_add=手動新增時間
issues.time_estimate_set=設定é ä¼°æ™‚é–“
issues.time_estimate_display=é ä¼°æ™‚間:%s
-issues.change_time_estimate_at=å°‡é ä¼°æ™‚間更改為 <b>%s</b> %s
issues.remove_time_estimate_at=移除é ä¼°æ™‚é–“ %s
issues.time_estimate_invalid=é ä¼°æ™‚é–“æ ¼å¼ç„¡æ•ˆ
issues.start_tracking_history=`開始工作 %s`
issues.tracker_auto_close=當這個å•é¡Œè¢«é—œé–‰æ™‚ï¼Œè‡ªå‹•åœæ­¢è¨ˆæ™‚器
issues.tracking_already_started=`您已在<a href="%s">å¦ä¸€å€‹å•題</a>上開始時間追蹤ï¼`
-issues.stop_tracking_history=`çµæŸå·¥ä½œ %s`
issues.cancel_tracking_history=`å–æ¶ˆæ™‚間追蹤 %s`
issues.del_time=刪除此時間記錄
-issues.add_time_history=`加入了花費時間 %s`
issues.del_time_history=`刪除了花費時間 %s`
issues.add_time_manually=手動新增時間
issues.add_time_hours=å°æ™‚
@@ -1844,7 +1829,6 @@ pulls.add_prefix=加入 <strong>%s</strong> å‰ç¶´
pulls.remove_prefix=移除 <strong>%s</strong> å‰ç¶´
pulls.data_broken=æ­¤åˆä½µè«‹æ±‚å·²ææ¯€ï¼Œå› ç‚ºéºå¤± Fork 資訊。
pulls.files_conflicted=æ­¤åˆä½µè«‹æ±‚有變更和目標分支è¡çªã€‚
-pulls.is_checking=正在進行åˆä½µè¡çªæª¢æŸ¥ï¼Œè«‹ç¨å¾Œå†è©¦ã€‚
pulls.is_ancestor=這個分支已經åˆä½µåˆ°ç›®æ¨™åˆ†æ”¯ä¸Šã€‚沒有å¯ä»¥åˆä½µçš„內容。
pulls.is_empty=在這個分支上的更動都已經套用在目標分支上。這將會產生一個空的æäº¤ã€‚
pulls.required_status_check_failed=æœªé€šéŽæŸäº›å¿…è¦çš„æª¢æŸ¥ã€‚
@@ -1913,12 +1897,10 @@ pulls.outdated_with_base_branch=ç›¸å°æ–¼åŸºåº•åˆ†æ”¯ï¼Œæ­¤åˆ†æ”¯å·²éŽæ™‚
pulls.close=關閉åˆä½µè«‹æ±‚
pulls.closed_at=`關閉了這個åˆä½µè«‹æ±‚ <a id="%[1]s" href="#%[1]s">%[2]s</a>`
pulls.reopened_at=`釿–°é–‹æ”¾äº†é€™å€‹åˆä½µè«‹æ±‚ <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-pulls.cmd_instruction_hint=`檢視 <a class="show-instruction">命令列指示</a>。`
pulls.cmd_instruction_checkout_title=檢出
pulls.cmd_instruction_checkout_desc=從您的專案儲存庫中,檢出一個新分支並測試變更。
pulls.cmd_instruction_merge_title=åˆä½µ
pulls.cmd_instruction_merge_desc=åˆä½µè®Šæ›´ä¸¦åœ¨ Gitea 上更新。
-pulls.cmd_instruction_merge_warning=警告:此æ“作無法åˆä½µåˆä½µè«‹æ±‚,因為未啟用「自動檢測手動åˆä½µã€
pulls.clear_merge_message=清除åˆä½µè¨Šæ¯
pulls.clear_merge_message_hint=清除åˆä½µè¨Šæ¯å°‡åƒ…移除æäº¤è¨Šæ¯å…§å®¹ï¼Œç•™ä¸‹ç”¢ç”Ÿçš„ git çµå°¾ï¼Œå¦‚「Co-Authored-By …ã€ã€‚
@@ -2145,7 +2127,6 @@ settings.advanced_settings=進階設定
settings.wiki_desc=啟用儲存庫 Wiki
settings.use_internal_wiki=使用內建 Wiki
settings.default_wiki_branch_name=é è¨­ Wiki 分支å稱
-settings.default_wiki_everyone_access=登入使用者的é è¨­å­˜å–權é™ï¼š
settings.failed_to_change_default_wiki_branch=更改é è¨­ Wiki 分支失敗。
settings.use_external_wiki=使用外部 Wiki
settings.external_wiki_url=外部 Wiki 連çµ
@@ -2274,8 +2255,6 @@ settings.hooks_desc=當觸發æŸäº› Gitea 事件時,Webhook 會自動發出 HT
settings.webhook_deletion=移除 Webhook
settings.webhook_deletion_desc=移除 Webhook 將刪除它的設定åŠå‚³é€è¨˜éŒ„,是å¦ç¹¼çºŒï¼Ÿ
settings.webhook_deletion_success=Webhook 已移除。
-settings.webhook.test_delivery=傳逿¸¬è©¦è³‡æ–™
-settings.webhook.test_delivery_desc=使用å‡äº‹ä»¶æ¸¬è©¦æ­¤ Webhook。
settings.webhook.test_delivery_desc_disabled=è¦ä½¿ç”¨å‡äº‹ä»¶æ¸¬è©¦æ­¤ Webhook,請啟用它。
settings.webhook.request=請求
settings.webhook.response=回應
@@ -2322,7 +2301,6 @@ settings.event_repository=儲存庫
settings.event_repository_desc=建立或刪除儲存庫。
settings.event_header_issue=å•題事件
settings.event_issues=å•題
-settings.event_issues_desc=建立ã€ç·¨è¼¯ã€é—œé–‰åŠé‡æ–°é–‹æ”¾å•題。
settings.event_issue_assign=指派å•題
settings.event_issue_assign_desc=æŒ‡æ´¾æˆ–å–æ¶ˆæŒ‡æ´¾å•題。
settings.event_issue_label=標籤
@@ -2333,7 +2311,6 @@ settings.event_issue_comment=å•題留言
settings.event_issue_comment_desc=已經建立ã€ç·¨è¼¯æˆ–刪除的å•題留言。
settings.event_header_pull_request=åˆä½µè«‹æ±‚事件
settings.event_pull_request=åˆä½µè«‹æ±‚
-settings.event_pull_request_desc=建立ã€ç·¨è¼¯ã€é—œé–‰åŠé‡æ–°é–‹æ”¾åˆä½µè«‹æ±‚。
settings.event_pull_request_assign=指派åˆä½µè«‹æ±‚
settings.event_pull_request_assign_desc=æŒ‡æ´¾æˆ–å–æ¶ˆæŒ‡æ´¾åˆä½µè«‹æ±‚。
settings.event_pull_request_label=åˆä½µè«‹æ±‚標籤
@@ -2564,7 +2541,6 @@ diff.commit=ç•¶å‰æäº¤
diff.git-notes=備註
diff.data_not_available=沒有內容比較å¯ä»¥ä½¿ç”¨
diff.options_button=差異é¸é …
-diff.show_diff_stats=顯示統計資料
diff.download_patch=下載 Patch 檔
diff.download_diff=下載差異檔
diff.show_split_view=分割檢視
@@ -2771,15 +2747,13 @@ settings.visibility.private_shortname=ç§æœ‰
settings.update_settings=更新設定
settings.update_setting_success=組織設定已更新。
-settings.change_orgname_prompt=注æ„:更改組織åç¨±å°‡åŒæ™‚更改組織的 URL 並釋放舊å稱。
-settings.change_orgname_redirect_prompt=舊的å稱被領用å‰ï¼Œæœƒé‡æ–°å°Žå‘æ–°å稱。
+
+
settings.update_avatar_success=已更新組織的大頭貼。
settings.delete=刪除組織
settings.delete_account=刪除這個組織
settings.delete_prompt=該組織將被永久刪除。此動作<strong>ä¸å¯</strong>還原ï¼
settings.confirm_delete_account=確èªåˆªé™¤çµ„ç¹”
-settings.delete_org_title=刪除組織
-settings.delete_org_desc=å³å°‡æ°¸ä¹…刪除這個組織,是å¦ç¹¼çºŒï¼Ÿ
settings.hooks_desc=此組織下的<strong>所有存儲庫</strong>都會觸發在此新增的 Webhook。
settings.labels_desc=在此處新增的標籤å¯ç”¨æ–¼æ­¤çµ„織下的<strong>所有儲存庫</strong>。
@@ -2852,6 +2826,7 @@ teams.invite.by=邀請人 %s
teams.invite.description=請點擊下方按鈕加入團隊。
+
[admin]
maintenance=ç¶­è­·
dashboard=資訊主é 
@@ -3222,8 +3197,6 @@ config.ssh_domain=SSH 伺æœå™¨åŸŸå
config.ssh_port=連接埠
config.ssh_listen_port=監è½åŸ 
config.ssh_root_path=根路徑
-config.ssh_key_test_path=金鑰測試路徑
-config.ssh_keygen_path=金鑰產生 (' ssh-keygen ') 路徑
config.ssh_minimum_key_size_check=金鑰最å°å¤§å°æª¢æŸ¥
config.ssh_minimum_key_sizes=金鑰最å°å¤§å°
@@ -3354,7 +3327,6 @@ monitor.execute_time=已執行時間
monitor.last_execution_result=çµæžœ
monitor.process.cancel=çµæŸè™•ç†ç¨‹åº
monitor.process.cancel_desc=çµæŸè™•ç†ç¨‹åºå¯èƒ½é€ æˆè³‡æ–™éºå¤±
-monitor.process.cancel_notices=çµæŸ: <strong>%s</strong>?
monitor.process.children=å­ç¨‹åº
monitor.queues=佇列
@@ -3550,7 +3522,6 @@ conda.install=執行下列命令以使用 Conda å®‰è£æ­¤å¥—ä»¶:
container.details.type=æ˜ åƒæª”類型
container.details.platform=å¹³å°
container.pull=é€éŽä¸‹åˆ—å‘½ä»¤æ‹‰å–æ˜ åƒæª”:
-container.digest=摘è¦:
container.multi_arch=作業系統 / 架構
container.layers=æ˜ åƒæª” Layers
container.labels=標籤
@@ -3653,11 +3624,13 @@ owner.settings.chef.keypair.description=é©—è­‰ Chef 註冊中心需è¦ä¸€å€‹å¯†é
secrets=Secret
description=Secret 會被傳給特定的 Action,其他情æ³ç„¡æ³•讀å–。
none=還沒有 Secret。
-creation=加入 Secret
+
+; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
+creation.description=æè¿°
creation.name_placeholder=ä¸å€åˆ†å¤§å°å¯«ï¼Œåªèƒ½åŒ…å«è‹±æ–‡å­—æ¯ã€æ•¸å­—ã€åº•ç·š ('_'),ä¸èƒ½ä»¥ GITEA_ 或 GITHUB_ 開頭。
creation.value_placeholder=輸入任何內容,頭尾的空白都會被忽略。
-creation.success=已新增 Secret「%sã€ã€‚
-creation.failed=加入 Secret 失敗。
+
+
deletion=移除 Secret
deletion.description=移除 Secret 是永久的且ä¸å¯é‚„原,是å¦ç¹¼çºŒï¼Ÿ
deletion.success=已移除此 Secret。
diff --git a/package-lock.json b/package-lock.json
index 1e3c5ab155..21e29a2081 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,117 +5,119 @@
"packages": {
"": {
"dependencies": {
- "@citation-js/core": "0.7.14",
- "@citation-js/plugin-bibtex": "0.7.16",
- "@citation-js/plugin-csl": "0.7.14",
+ "@citation-js/core": "0.7.18",
+ "@citation-js/plugin-bibtex": "0.7.18",
+ "@citation-js/plugin-csl": "0.7.18",
"@citation-js/plugin-software-formats": "0.6.1",
"@github/markdown-toolbar-element": "2.2.3",
- "@github/relative-time-element": "4.4.4",
- "@github/text-expander-element": "2.8.0",
+ "@github/relative-time-element": "4.4.8",
+ "@github/text-expander-element": "2.9.2",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
- "@primer/octicons": "19.14.0",
+ "@primer/octicons": "19.15.3",
"@silverwind/vue3-calendar-heatmap": "2.0.6",
"add-asset-webpack-plugin": "3.0.0",
- "ansi_up": "6.0.2",
- "asciinema-player": "3.8.1",
- "chart.js": "4.4.7",
+ "ansi_up": "6.0.6",
+ "asciinema-player": "3.10.0",
+ "chart.js": "4.5.0",
"chartjs-adapter-dayjs-4": "1.0.4",
"chartjs-plugin-zoom": "2.2.0",
- "clippie": "4.1.4",
+ "clippie": "4.1.7",
"cropperjs": "1.6.2",
"css-loader": "7.1.2",
"dayjs": "1.11.13",
"dropzone": "6.0.0-beta.2",
- "easymde": "2.18.0",
- "esbuild-loader": "4.2.2",
- "escape-goat": "4.0.0",
- "fast-glob": "3.3.2",
- "htmx.org": "2.0.4",
- "idiomorph": "0.3.0",
+ "easymde": "2.20.0",
+ "esbuild-loader": "4.3.0",
+ "fast-glob": "3.3.3",
+ "htmx.org": "2.0.6",
+ "idiomorph": "0.7.3",
"jquery": "3.7.1",
- "katex": "0.16.18",
+ "katex": "0.16.22",
"license-checker-webpack-plugin": "0.2.1",
- "mermaid": "11.4.1",
+ "mermaid": "11.8.0",
"mini-css-extract-plugin": "2.9.2",
- "minimatch": "10.0.1",
+ "minimatch": "10.0.3",
"monaco-editor": "0.52.2",
"monaco-editor-webpack-plugin": "7.1.0",
- "pdfobject": "2.3.0",
+ "online-3d-viewer": "0.16.0",
+ "pdfobject": "2.3.1",
"perfect-debounce": "1.0.0",
- "postcss": "8.4.49",
+ "postcss": "8.5.6",
"postcss-loader": "8.1.1",
- "postcss-nesting": "13.0.1",
+ "postcss-nesting": "13.0.2",
"sortablejs": "1.15.6",
- "swagger-ui-dist": "5.18.2",
+ "swagger-ui-dist": "5.26.0",
"tailwindcss": "3.4.17",
"throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0",
"tippy.js": "6.3.7",
"toastify-js": "1.12.0",
"tributejs": "5.1.3",
- "typescript": "5.7.2",
- "uint8-to-base64": "0.2.0",
+ "typescript": "5.8.3",
+ "uint8-to-base64": "0.2.1",
"vanilla-colorful": "0.7.2",
- "vue": "3.5.13",
+ "vue": "3.5.17",
"vue-bar-graph": "2.2.0",
"vue-chartjs": "5.3.2",
"vue-loader": "17.4.2",
- "webpack": "5.97.1",
- "webpack-cli": "5.1.4",
+ "webpack": "5.99.9",
+ "webpack-cli": "6.0.1",
"wrap-ansi": "9.0.0"
},
"devDependencies": {
- "@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
- "@playwright/test": "1.49.1",
- "@silverwind/vue-tsc": "2.1.13",
- "@stoplight/spectral-cli": "6.14.2",
- "@stylistic/eslint-plugin-js": "2.12.1",
- "@stylistic/stylelint-plugin": "3.1.1",
+ "@eslint-community/eslint-plugin-eslint-comments": "4.5.0",
+ "@playwright/test": "1.53.2",
+ "@stoplight/spectral-cli": "6.15.0",
+ "@stylistic/eslint-plugin-js": "3.1.0",
+ "@stylistic/stylelint-plugin": "3.1.3",
"@types/dropzone": "5.7.9",
"@types/jquery": "3.5.32",
"@types/katex": "0.16.7",
- "@types/license-checker-webpack-plugin": "0.2.4",
+ "@types/license-checker-webpack-plugin": "0.2.5",
"@types/pdfobject": "2.2.5",
"@types/sortablejs": "1.15.8",
- "@types/swagger-ui-dist": "3.30.5",
+ "@types/swagger-ui-dist": "3.30.6",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
- "@types/toastify-js": "1.12.3",
- "@typescript-eslint/eslint-plugin": "8.18.1",
- "@typescript-eslint/parser": "8.18.1",
- "@vitejs/plugin-vue": "5.2.1",
+ "@types/toastify-js": "1.12.4",
+ "@typescript-eslint/eslint-plugin": "8.35.1",
+ "@typescript-eslint/parser": "8.35.1",
+ "@vitejs/plugin-vue": "6.0.0",
+ "@vitest/eslint-plugin": "1.3.4",
"eslint": "8.57.0",
- "eslint-import-resolver-typescript": "3.7.0",
+ "eslint-import-resolver-typescript": "4.4.4",
"eslint-plugin-array-func": "4.0.0",
"eslint-plugin-github": "5.0.2",
- "eslint-plugin-import-x": "4.6.1",
- "eslint-plugin-no-jquery": "3.1.0",
+ "eslint-plugin-import-x": "4.16.1",
+ "eslint-plugin-no-jquery": "3.1.1",
"eslint-plugin-no-use-extend-native": "0.5.0",
- "eslint-plugin-playwright": "2.1.0",
- "eslint-plugin-regexp": "2.7.0",
- "eslint-plugin-sonarjs": "3.0.1",
+ "eslint-plugin-playwright": "2.2.0",
+ "eslint-plugin-regexp": "2.9.0",
+ "eslint-plugin-sonarjs": "3.0.4",
"eslint-plugin-unicorn": "56.0.1",
- "eslint-plugin-vitest": "0.4.1",
- "eslint-plugin-vitest-globals": "1.5.0",
- "eslint-plugin-vue": "9.32.0",
- "eslint-plugin-vue-scoped-css": "2.9.0",
- "eslint-plugin-wc": "2.2.0",
- "happy-dom": "15.11.7",
- "markdownlint-cli": "0.43.0",
- "nolyfill": "1.0.43",
- "postcss-html": "1.7.0",
- "stylelint": "16.12.0",
+ "eslint-plugin-vue": "10.3.0",
+ "eslint-plugin-vue-scoped-css": "2.11.0",
+ "eslint-plugin-wc": "3.0.1",
+ "happy-dom": "18.0.1",
+ "markdownlint-cli": "0.45.0",
+ "material-icon-theme": "5.24.0",
+ "nolyfill": "1.0.44",
+ "postcss-html": "1.8.0",
+ "stylelint": "16.21.1",
+ "stylelint-config-recommended": "16.0.0",
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
- "stylelint-declaration-strict-value": "1.10.6",
+ "stylelint-declaration-strict-value": "1.10.11",
+ "stylelint-define-config": "16.21.0",
"stylelint-value-no-unknown-custom-properties": "6.0.1",
- "svgo": "3.3.2",
- "type-fest": "4.30.2",
- "updates": "16.4.1",
- "vite-string-plugin": "1.3.4",
- "vitest": "2.1.8"
+ "svgo": "4.0.0",
+ "type-fest": "4.41.0",
+ "updates": "16.4.2",
+ "vite-string-plugin": "1.4.4",
+ "vitest": "3.2.4",
+ "vue-tsc": "3.0.1"
},
"engines": {
- "node": ">= 18.0.0"
+ "node": ">= 20.0.0"
}
},
"node_modules/@alloc/quick-lru": {
@@ -130,46 +132,32 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@ampproject/remapping": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
- "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.24"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/@antfu/install-pkg": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.4.1.tgz",
- "integrity": "sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz",
+ "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==",
"license": "MIT",
"dependencies": {
- "package-manager-detector": "^0.2.0",
- "tinyexec": "^0.3.0"
+ "package-manager-detector": "^1.3.0",
+ "tinyexec": "^1.0.1"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@antfu/utils": {
- "version": "0.7.10",
- "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz",
- "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==",
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz",
+ "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@asyncapi/specs": {
- "version": "6.8.0",
- "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.8.0.tgz",
- "integrity": "sha512-1i6xs8+IOh6U5T7yH+bCMGQBF+m7kP/NpwyAlt++XaDQutoGCgACf24mQBgcDVqDWWoY81evQv+9ABvw0BviVg==",
+ "version": "6.8.1",
+ "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.8.1.tgz",
+ "integrity": "sha512-czHoAk3PeXTLR+X8IUaD+IpT+g+zUvkcgMDJVothBsan+oHN3jfcFcFUNdOPAAFoUCQN1hXF1dWuphWy05THlA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -177,426 +165,50 @@
}
},
"node_modules/@babel/code-frame": {
- "version": "7.26.2",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
- "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
"license": "MIT",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.27.1",
"js-tokens": "^4.0.0",
- "picocolors": "^1.0.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/compat-data": {
- "version": "7.26.3",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz",
- "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/core": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz",
- "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@ampproject/remapping": "^2.2.0",
- "@babel/code-frame": "^7.26.0",
- "@babel/generator": "^7.26.0",
- "@babel/helper-compilation-targets": "^7.25.9",
- "@babel/helper-module-transforms": "^7.26.0",
- "@babel/helpers": "^7.26.0",
- "@babel/parser": "^7.26.0",
- "@babel/template": "^7.25.9",
- "@babel/traverse": "^7.25.9",
- "@babel/types": "^7.26.0",
- "convert-source-map": "^2.0.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.2",
- "json5": "^2.2.3",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/babel"
- }
- },
- "node_modules/@babel/core/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/eslint-parser": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.9.tgz",
- "integrity": "sha512-5UXfgpK0j0Xr/xIdgdLEhOFxaDZ0bRPWJJchRpqOSur/3rZoPbqqki5mm0p4NE2cs28krBEiSM2MB7//afRSQQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
- "eslint-visitor-keys": "^2.1.0",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.11.0",
- "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
- }
- },
- "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
- "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@babel/eslint-parser/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/generator": {
- "version": "7.26.3",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz",
- "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.26.3",
- "@babel/types": "^7.26.3",
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.25",
- "jsesc": "^3.0.2"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-annotate-as-pure": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz",
- "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/types": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-compilation-targets": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz",
- "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/compat-data": "^7.25.9",
- "@babel/helper-validator-option": "^7.25.9",
- "browserslist": "^4.24.0",
- "lru-cache": "^5.1.1",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/helper-create-class-features-plugin": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz",
- "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.25.9",
- "@babel/helper-member-expression-to-functions": "^7.25.9",
- "@babel/helper-optimise-call-expression": "^7.25.9",
- "@babel/helper-replace-supers": "^7.25.9",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
- "@babel/traverse": "^7.25.9",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/helper-create-regexp-features-plugin": {
- "version": "7.26.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz",
- "integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.25.9",
- "regexpu-core": "^6.2.0",
- "semver": "^6.3.1"
+ "picocolors": "^1.1.1"
},
"engines": {
"node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
}
},
- "node_modules/@babel/helper-define-polyfill-provider": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz",
- "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-compilation-targets": "^7.22.6",
- "@babel/helper-plugin-utils": "^7.22.5",
- "debug": "^4.1.1",
- "lodash.debounce": "^4.0.8",
- "resolve": "^1.14.2"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/@babel/helper-member-expression-to-functions": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz",
- "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/traverse": "^7.25.9",
- "@babel/types": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-imports": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
- "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/traverse": "^7.25.9",
- "@babel/types": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-transforms": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz",
- "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-module-imports": "^7.25.9",
- "@babel/helper-validator-identifier": "^7.25.9",
- "@babel/traverse": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-optimise-call-expression": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz",
- "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/types": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-plugin-utils": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz",
- "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-remap-async-to-generator": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz",
- "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.25.9",
- "@babel/helper-wrap-function": "^7.25.9",
- "@babel/traverse": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-replace-supers": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz",
- "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-member-expression-to-functions": "^7.25.9",
- "@babel/helper-optimise-call-expression": "^7.25.9",
- "@babel/traverse": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz",
- "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/traverse": "^7.25.9",
- "@babel/types": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- }
+ "node_modules/@babel/code-frame/node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
- "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
- "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-validator-option": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz",
- "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-wrap-function": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz",
- "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==",
- "dev": true,
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+ "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"license": "MIT",
- "dependencies": {
- "@babel/template": "^7.25.9",
- "@babel/traverse": "^7.25.9",
- "@babel/types": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helpers": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz",
- "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/template": "^7.25.9",
- "@babel/types": "^7.26.0"
- },
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
- "version": "7.26.3",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz",
- "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==",
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
+ "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.26.3"
+ "@babel/types": "^7.28.0"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -605,1372 +217,32 @@
"node": ">=6.0.0"
}
},
- "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz",
- "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/traverse": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz",
- "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz",
- "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz",
- "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
- "@babel/plugin-transform-optional-chaining": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.13.0"
- }
- },
- "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz",
- "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/traverse": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-proposal-decorators": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.25.9.tgz",
- "integrity": "sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/plugin-syntax-decorators": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-proposal-private-property-in-object": {
- "version": "7.21.0-placeholder-for-preset-env.2",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
- "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-decorators": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.25.9.tgz",
- "integrity": "sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-flow": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz",
- "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-import-assertions": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz",
- "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-import-attributes": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz",
- "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-jsx": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz",
- "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-unicode-sets-regex": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
- "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.18.6",
- "@babel/helper-plugin-utils": "^7.18.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-transform-arrow-functions": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz",
- "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-async-generator-functions": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz",
- "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/helper-remap-async-to-generator": "^7.25.9",
- "@babel/traverse": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-async-to-generator": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz",
- "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-module-imports": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/helper-remap-async-to-generator": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-block-scoped-functions": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz",
- "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-block-scoping": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz",
- "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-class-properties": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz",
- "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-class-static-block": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz",
- "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.12.0"
- }
- },
- "node_modules/@babel/plugin-transform-classes": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz",
- "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.25.9",
- "@babel/helper-compilation-targets": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/helper-replace-supers": "^7.25.9",
- "@babel/traverse": "^7.25.9",
- "globals": "^11.1.0"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-classes/node_modules/globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/plugin-transform-computed-properties": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz",
- "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/template": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-destructuring": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz",
- "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-dotall-regex": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz",
- "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-duplicate-keys": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz",
- "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz",
- "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-transform-dynamic-import": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz",
- "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-exponentiation-operator": {
- "version": "7.26.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz",
- "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-export-namespace-from": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz",
- "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-flow-strip-types": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.25.9.tgz",
- "integrity": "sha512-/VVukELzPDdci7UUsWQaSkhgnjIWXnIyRpM02ldxaVoFK96c41So8JcKT3m0gYjyv7j5FNPGS5vfELrWalkbDA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/plugin-syntax-flow": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-for-of": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz",
- "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-function-name": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz",
- "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-compilation-targets": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/traverse": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-json-strings": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz",
- "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-literals": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz",
- "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-logical-assignment-operators": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz",
- "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-member-expression-literals": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz",
- "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-modules-amd": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz",
- "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-module-transforms": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-modules-commonjs": {
- "version": "7.26.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz",
- "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-module-transforms": "^7.26.0",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-modules-systemjs": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz",
- "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-module-transforms": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/helper-validator-identifier": "^7.25.9",
- "@babel/traverse": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-modules-umd": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz",
- "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-module-transforms": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz",
- "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-transform-new-target": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz",
- "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz",
- "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-numeric-separator": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz",
- "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-object-rest-spread": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz",
- "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-compilation-targets": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/plugin-transform-parameters": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-object-super": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz",
- "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/helper-replace-supers": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-optional-catch-binding": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz",
- "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-optional-chaining": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz",
- "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-parameters": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz",
- "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-private-methods": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz",
- "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-private-property-in-object": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz",
- "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.25.9",
- "@babel/helper-create-class-features-plugin": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-property-literals": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz",
- "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-react-display-name": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz",
- "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-react-jsx": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz",
- "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.25.9",
- "@babel/helper-module-imports": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/plugin-syntax-jsx": "^7.25.9",
- "@babel/types": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-react-jsx-development": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz",
- "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/plugin-transform-react-jsx": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-react-pure-annotations": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz",
- "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-regenerator": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz",
- "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9",
- "regenerator-transform": "^0.15.2"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-regexp-modifiers": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz",
- "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-transform-reserved-words": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz",
- "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-shorthand-properties": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz",
- "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-spread": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz",
- "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-sticky-regex": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz",
- "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-template-literals": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz",
- "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-typeof-symbol": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz",
- "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-unicode-escapes": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz",
- "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-unicode-property-regex": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz",
- "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-unicode-regex": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz",
- "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-unicode-sets-regex": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz",
- "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/preset-env": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz",
- "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/compat-data": "^7.26.0",
- "@babel/helper-compilation-targets": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/helper-validator-option": "^7.25.9",
- "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9",
- "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9",
- "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9",
- "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9",
- "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9",
- "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
- "@babel/plugin-syntax-import-assertions": "^7.26.0",
- "@babel/plugin-syntax-import-attributes": "^7.26.0",
- "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
- "@babel/plugin-transform-arrow-functions": "^7.25.9",
- "@babel/plugin-transform-async-generator-functions": "^7.25.9",
- "@babel/plugin-transform-async-to-generator": "^7.25.9",
- "@babel/plugin-transform-block-scoped-functions": "^7.25.9",
- "@babel/plugin-transform-block-scoping": "^7.25.9",
- "@babel/plugin-transform-class-properties": "^7.25.9",
- "@babel/plugin-transform-class-static-block": "^7.26.0",
- "@babel/plugin-transform-classes": "^7.25.9",
- "@babel/plugin-transform-computed-properties": "^7.25.9",
- "@babel/plugin-transform-destructuring": "^7.25.9",
- "@babel/plugin-transform-dotall-regex": "^7.25.9",
- "@babel/plugin-transform-duplicate-keys": "^7.25.9",
- "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9",
- "@babel/plugin-transform-dynamic-import": "^7.25.9",
- "@babel/plugin-transform-exponentiation-operator": "^7.25.9",
- "@babel/plugin-transform-export-namespace-from": "^7.25.9",
- "@babel/plugin-transform-for-of": "^7.25.9",
- "@babel/plugin-transform-function-name": "^7.25.9",
- "@babel/plugin-transform-json-strings": "^7.25.9",
- "@babel/plugin-transform-literals": "^7.25.9",
- "@babel/plugin-transform-logical-assignment-operators": "^7.25.9",
- "@babel/plugin-transform-member-expression-literals": "^7.25.9",
- "@babel/plugin-transform-modules-amd": "^7.25.9",
- "@babel/plugin-transform-modules-commonjs": "^7.25.9",
- "@babel/plugin-transform-modules-systemjs": "^7.25.9",
- "@babel/plugin-transform-modules-umd": "^7.25.9",
- "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9",
- "@babel/plugin-transform-new-target": "^7.25.9",
- "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9",
- "@babel/plugin-transform-numeric-separator": "^7.25.9",
- "@babel/plugin-transform-object-rest-spread": "^7.25.9",
- "@babel/plugin-transform-object-super": "^7.25.9",
- "@babel/plugin-transform-optional-catch-binding": "^7.25.9",
- "@babel/plugin-transform-optional-chaining": "^7.25.9",
- "@babel/plugin-transform-parameters": "^7.25.9",
- "@babel/plugin-transform-private-methods": "^7.25.9",
- "@babel/plugin-transform-private-property-in-object": "^7.25.9",
- "@babel/plugin-transform-property-literals": "^7.25.9",
- "@babel/plugin-transform-regenerator": "^7.25.9",
- "@babel/plugin-transform-regexp-modifiers": "^7.26.0",
- "@babel/plugin-transform-reserved-words": "^7.25.9",
- "@babel/plugin-transform-shorthand-properties": "^7.25.9",
- "@babel/plugin-transform-spread": "^7.25.9",
- "@babel/plugin-transform-sticky-regex": "^7.25.9",
- "@babel/plugin-transform-template-literals": "^7.25.9",
- "@babel/plugin-transform-typeof-symbol": "^7.25.9",
- "@babel/plugin-transform-unicode-escapes": "^7.25.9",
- "@babel/plugin-transform-unicode-property-regex": "^7.25.9",
- "@babel/plugin-transform-unicode-regex": "^7.25.9",
- "@babel/plugin-transform-unicode-sets-regex": "^7.25.9",
- "@babel/preset-modules": "0.1.6-no-external-plugins",
- "babel-plugin-polyfill-corejs2": "^0.4.10",
- "babel-plugin-polyfill-corejs3": "^0.10.6",
- "babel-plugin-polyfill-regenerator": "^0.6.1",
- "core-js-compat": "^3.38.1",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/preset-env/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/preset-flow": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.25.9.tgz",
- "integrity": "sha512-EASHsAhE+SSlEzJ4bzfusnXSHiU+JfAYzj+jbw2vgQKgq5HrUr8qs+vgtiEL5dOH6sEweI+PNt2D7AqrDSHyqQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/helper-validator-option": "^7.25.9",
- "@babel/plugin-transform-flow-strip-types": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/preset-modules": {
- "version": "0.1.6-no-external-plugins",
- "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
- "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@babel/types": "^7.4.4",
- "esutils": "^2.0.2"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/@babel/preset-react": {
- "version": "7.26.3",
- "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.26.3.tgz",
- "integrity": "sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.25.9",
- "@babel/helper-validator-option": "^7.25.9",
- "@babel/plugin-transform-react-display-name": "^7.25.9",
- "@babel/plugin-transform-react-jsx": "^7.25.9",
- "@babel/plugin-transform-react-jsx-development": "^7.25.9",
- "@babel/plugin-transform-react-pure-annotations": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
"node_modules/@babel/runtime": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
- "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
- "license": "MIT",
- "dependencies": {
- "regenerator-runtime": "^0.14.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/template": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
- "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/code-frame": "^7.25.9",
- "@babel/parser": "^7.25.9",
- "@babel/types": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/traverse": {
- "version": "7.26.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz",
- "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==",
- "dev": true,
+ "version": "7.27.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz",
+ "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==",
"license": "MIT",
- "dependencies": {
- "@babel/code-frame": "^7.26.2",
- "@babel/generator": "^7.26.3",
- "@babel/parser": "^7.26.3",
- "@babel/template": "^7.25.9",
- "@babel/types": "^7.26.3",
- "debug": "^4.3.1",
- "globals": "^11.1.0"
- },
"engines": {
"node": ">=6.9.0"
}
},
- "node_modules/@babel/traverse/node_modules/globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/@babel/types": {
- "version": "7.26.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
- "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz",
+ "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==",
"license": "MIT",
"dependencies": {
- "@babel/helper-string-parser": "^7.25.9",
- "@babel/helper-validator-identifier": "^7.25.9"
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@braintree/sanitize-url": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.0.tgz",
- "integrity": "sha512-o+UlMLt49RvtCASlOMW0AkHnabN9wR9rwCCherxO0yG4Npy34GkvrAqdXQvrhNs+jh+gkK8gB8Lf05qL/O7KWg==",
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz",
+ "integrity": "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==",
"license": "MIT"
},
"node_modules/@chevrotain/cst-dts-gen": {
@@ -2013,9 +285,9 @@
"license": "Apache-2.0"
},
"node_modules/@citation-js/core": {
- "version": "0.7.14",
- "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.7.14.tgz",
- "integrity": "sha512-dgeGqYDSQmn2MtnWZkwPGpJQPh43yr1lAAr9jl1NJ9pIY1RXUQxtlAUZVur0V9PHdbfQC+kkvB1KC3VpgVV3MA==",
+ "version": "0.7.18",
+ "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.7.18.tgz",
+ "integrity": "sha512-EjLuZWA5156dIFGdF7OnyPyWFBW43B8Ckje6Sn/W2RFxHDu0oACvW4/6TNgWT80jhEA4bVFm7ahrZe9MJ2B2UQ==",
"license": "MIT",
"dependencies": {
"@citation-js/date": "^0.5.0",
@@ -2046,9 +318,9 @@
}
},
"node_modules/@citation-js/plugin-bibtex": {
- "version": "0.7.16",
- "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.7.16.tgz",
- "integrity": "sha512-Udeli19VAoFjOw0H1bB1KgmekRoW6XP5cdR3OQF5c2Mt1tZatXWcSQVdq+FeLKzodRocZXG5NFRvjyUZjVbV6A==",
+ "version": "0.7.18",
+ "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.7.18.tgz",
+ "integrity": "sha512-TdsZSMpgpfcx2NMPu0KiulEoecllwT5EtRUzAJl2pDsdPD1tUqqbyj/NBi0l8fwNy1r7WwAqSFGiqGPjQWpFdg==",
"license": "MIT",
"dependencies": {
"@citation-js/date": "^0.5.0",
@@ -2076,9 +348,9 @@
}
},
"node_modules/@citation-js/plugin-csl": {
- "version": "0.7.14",
- "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.7.14.tgz",
- "integrity": "sha512-7AKB8lMz1IqdtoE33NnWIpteLYMuSl3xqT+Cax7sQKwAIJEoq2HBmb43Ja8xQQ36nREAupQJv1V6XksIAmYnCg==",
+ "version": "0.7.18",
+ "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.7.18.tgz",
+ "integrity": "sha512-cJcOdEZurmtIxNj0d4cOERHpVQJB/mN3YPSDNqfI/xTFRN3bWDpFAsaqubPtMO2ZPpoDS+ZGIP1kggbwCfMmlA==",
"license": "MIT",
"dependencies": {
"@citation-js/date": "^0.5.0",
@@ -2159,9 +431,9 @@
}
},
"node_modules/@csstools/css-parser-algorithms": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz",
- "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==",
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
+ "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
"dev": true,
"funding": [
{
@@ -2178,13 +450,13 @@
"node": ">=18"
},
"peerDependencies": {
- "@csstools/css-tokenizer": "^3.0.3"
+ "@csstools/css-tokenizer": "^3.0.4"
}
},
"node_modules/@csstools/css-tokenizer": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz",
- "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==",
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
+ "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
"dev": true,
"funding": [
{
@@ -2226,12 +498,12 @@
}
},
"node_modules/@discoveryjs/json-ext": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
- "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz",
+ "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==",
"license": "MIT",
"engines": {
- "node": ">=10.0.0"
+ "node": ">=14.17.0"
}
},
"node_modules/@dual-bundle/import-meta-resolve": {
@@ -2245,10 +517,44 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/@emnapi/core": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz",
+ "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.0.2",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz",
+ "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz",
+ "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
- "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
+ "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
"cpu": [
"ppc64"
],
@@ -2258,13 +564,13 @@
"aix"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
- "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
+ "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
"cpu": [
"arm"
],
@@ -2274,13 +580,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
- "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
+ "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
"cpu": [
"arm64"
],
@@ -2290,13 +596,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
- "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
+ "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
"cpu": [
"x64"
],
@@ -2306,13 +612,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
- "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
+ "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
"cpu": [
"arm64"
],
@@ -2322,13 +628,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
- "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
+ "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
"cpu": [
"x64"
],
@@ -2338,13 +644,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
- "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
+ "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
"cpu": [
"arm64"
],
@@ -2354,13 +660,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
- "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
+ "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
"cpu": [
"x64"
],
@@ -2370,13 +676,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
- "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
+ "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
"cpu": [
"arm"
],
@@ -2386,13 +692,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
- "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
+ "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
"cpu": [
"arm64"
],
@@ -2402,13 +708,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
- "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
+ "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
"cpu": [
"ia32"
],
@@ -2418,13 +724,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
- "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
+ "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
"cpu": [
"loong64"
],
@@ -2434,13 +740,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
- "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
+ "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
"cpu": [
"mips64el"
],
@@ -2450,13 +756,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
- "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
+ "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
"cpu": [
"ppc64"
],
@@ -2466,13 +772,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
- "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
+ "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
"cpu": [
"riscv64"
],
@@ -2482,13 +788,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
- "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
+ "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
"cpu": [
"s390x"
],
@@ -2498,13 +804,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
- "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
+ "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
"cpu": [
"x64"
],
@@ -2514,13 +820,29 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
+ "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
- "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
+ "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
"cpu": [
"x64"
],
@@ -2530,13 +852,29 @@
"netbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
+ "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
- "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
+ "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
"cpu": [
"x64"
],
@@ -2546,13 +884,13 @@
"openbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
- "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
+ "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
"cpu": [
"x64"
],
@@ -2562,13 +900,13 @@
"sunos"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
- "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
+ "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
"cpu": [
"arm64"
],
@@ -2578,13 +916,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
- "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
+ "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
"cpu": [
"ia32"
],
@@ -2594,13 +932,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
- "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
+ "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
"cpu": [
"x64"
],
@@ -2610,13 +948,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@eslint-community/eslint-plugin-eslint-comments": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.4.1.tgz",
- "integrity": "sha512-lb/Z/MzbTf7CaVYM9WCFNQZ4L1yi3ev2fsFPF99h31ljhSEyUoyEsKsNWiU+qD1glbYTDJdqgyaLKtyTkkqtuQ==",
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.5.0.tgz",
+ "integrity": "sha512-MAhuTKlr4y/CE3WYX26raZjy+I/kS2PLKSzvfmDCGrBLTFHOYwqROZdr4XwPgXwX3K9rjzMr4pSmUWGnzsUyMg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2634,9 +972,9 @@
}
},
"node_modules/@eslint-community/eslint-utils": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
- "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==",
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
+ "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2716,17 +1054,6 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
- "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
@@ -2808,19 +1135,19 @@
"license": "MIT"
},
"node_modules/@github/relative-time-element": {
- "version": "4.4.4",
- "resolved": "https://registry.npmjs.org/@github/relative-time-element/-/relative-time-element-4.4.4.tgz",
- "integrity": "sha512-Oi8uOL8O+ZWLD7dHRWCkm2cudcTYtB3VyOYf9BtzCgDGm+OKomyOREtItNMtWl1dxvec62BTKErq36uy+RYxQg==",
+ "version": "4.4.8",
+ "resolved": "https://registry.npmjs.org/@github/relative-time-element/-/relative-time-element-4.4.8.tgz",
+ "integrity": "sha512-FSLYm6F3TSQnqHE1EMQUVVgi2XjbCvsESwwXfugHFpBnhyF1uhJOtu0Psp/BB/qqazfdkk7f5fVcu7WuXl3t8Q==",
"license": "MIT"
},
"node_modules/@github/text-expander-element": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/@github/text-expander-element/-/text-expander-element-2.8.0.tgz",
- "integrity": "sha512-kkS2rZ/CG8HGKblpLDQ8vcK/K7l/Jsvzi/N4ovwPAsFSOImcIbJh2MgCv9tzqE3wAm/qXlscvh3Ms4Hh1vtZvw==",
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/@github/text-expander-element/-/text-expander-element-2.9.2.tgz",
+ "integrity": "sha512-XY8EUMqM4GAloNxXNA1Py1ny+engWwYntbgsnpstQN4piaTI9rIlfYldyd0nnPXhxjGCVqHPmP6yg17Q0/n9Vg==",
"license": "MIT",
"dependencies": {
"@github/combobox-nav": "^2.0.2",
- "dom-input-range": "^1.2.0"
+ "dom-input-range": "^2.0.0"
}
},
"node_modules/@humanwhocodes/config-array": {
@@ -2839,17 +1166,6 @@
"node": ">=10.10.0"
}
},
- "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -2892,25 +1208,25 @@
"license": "MIT"
},
"node_modules/@iconify/utils": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.2.1.tgz",
- "integrity": "sha512-0/7J7hk4PqXmxo5PDBDxmnecw5PxklZJfNjIVG9FM0mEfVrvfudS22rYWsqVk6gR3UJ/mSYS90X4R3znXnqfNA==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz",
+ "integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==",
"license": "MIT",
"dependencies": {
- "@antfu/install-pkg": "^0.4.1",
- "@antfu/utils": "^0.7.10",
+ "@antfu/install-pkg": "^1.0.0",
+ "@antfu/utils": "^8.1.0",
"@iconify/types": "^2.0.0",
"debug": "^4.4.0",
- "globals": "^15.13.0",
+ "globals": "^15.14.0",
"kolorist": "^1.8.0",
- "local-pkg": "^0.5.1",
- "mlly": "^1.7.3"
+ "local-pkg": "^1.0.0",
+ "mlly": "^1.7.4"
}
},
"node_modules/@iconify/utils/node_modules/globals": {
- "version": "15.14.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz",
- "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==",
+ "version": "15.15.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+ "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -2919,6 +1235,27 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@isaacs/balanced-match": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
+ "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
+ "license": "MIT",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/@isaacs/brace-expansion": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
+ "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@isaacs/balanced-match": "^4.0.1"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -3010,17 +1347,13 @@
}
},
"node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.8",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
- "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+ "version": "0.3.12",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz",
+ "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
"license": "MIT",
"dependencies": {
- "@jridgewell/set-array": "^1.2.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
- },
- "engines": {
- "node": ">=6.0.0"
}
},
"node_modules/@jridgewell/resolve-uri": {
@@ -3032,19 +1365,10 @@
"node": ">=6.0.0"
}
},
- "node_modules/@jridgewell/set-array": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
- "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
- "license": "MIT",
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/@jridgewell/source-map": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
- "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+ "version": "0.3.10",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.10.tgz",
+ "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==",
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
@@ -3052,15 +1376,15 @@
}
},
"node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
- "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
+ "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.25",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
- "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "version": "0.3.29",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz",
+ "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==",
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
@@ -3106,6 +1430,41 @@
"jsep": "^0.4.0||^1.0.0"
}
},
+ "node_modules/@keyv/serialize": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.3.tgz",
+ "integrity": "sha512-qnEovoOp5Np2JDGonIDL6Ayihw0RhnRh6vxPuHo4RDn1UOzwEo4AeIfpL6UGIrsceWrCMiVPgwRjbHu4vYFc3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^6.0.3"
+ }
+ },
+ "node_modules/@keyv/serialize/node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
"node_modules/@kurkle/color": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
@@ -3164,46 +1523,25 @@
}
},
"node_modules/@mermaid-js/parser": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.3.0.tgz",
- "integrity": "sha512-HsvL6zgE5sUPGgkIDlmAWR1HTNHz2Iy11BAWPTa4Jjabkpguy4Ze2gzfLrg6pdRuBvFwgUYyxiaNqZwrEEXepA==",
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.0.tgz",
+ "integrity": "sha512-7DNESgpyZ5WG1SIkrYafVBhWmImtmQuoxOO1lawI3gQYWxBX3v1FW3IyuuRfKJAO06XrZR71W0Kif5VEGGd4VA==",
"license": "MIT",
"dependencies": {
- "langium": "3.0.0"
+ "langium": "3.3.1"
}
},
- "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
- "version": "5.1.1-v1",
- "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
- "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==",
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz",
+ "integrity": "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==",
"dev": true,
"license": "MIT",
+ "optional": true,
"dependencies": {
- "eslint-scope": "5.1.1"
- }
- },
- "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
- "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "esrecurse": "^4.3.0",
- "estraverse": "^4.1.1"
- },
- "engines": {
- "node": ">=8.0.0"
- }
- },
- "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
- "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=4.0"
+ "@emnapi/core": "^1.4.3",
+ "@emnapi/runtime": "^1.4.3",
+ "@tybys/wasm-util": "^0.9.0"
}
},
"node_modules/@nodelib/fs.scandir": {
@@ -3241,20 +1579,10 @@
"node": ">= 8"
}
},
- "node_modules/@nolyfill/is-core-module": {
- "version": "1.0.39",
- "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
- "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.4.0"
- }
- },
"node_modules/@nolyfill/shared": {
- "version": "1.0.28",
- "resolved": "https://registry.npmjs.org/@nolyfill/shared/-/shared-1.0.28.tgz",
- "integrity": "sha512-UJTshFMDgugBcYXGLopbL1enYpGREOEfjUMQKLPLeJqWfbfElGtYbGbUcucCENa7cicGo3M5u/DnPiZe/PYQyw==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/shared/-/shared-1.0.44.tgz",
+ "integrity": "sha512-NI1zxDh4LYL7PYlKKCwojjuc5CEZslywrOTKBNyodjmWjRiZ4AlCMs3Gp+zDoPQPNkYCSQp/luNojHmJWWfCbw==",
"dev": true,
"license": "MIT"
},
@@ -3269,26 +1597,26 @@
}
},
"node_modules/@pkgr/core": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
- "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz",
+ "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
},
"funding": {
- "url": "https://opencollective.com/unts"
+ "url": "https://opencollective.com/pkgr"
}
},
"node_modules/@playwright/test": {
- "version": "1.49.1",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz",
- "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==",
+ "version": "1.53.2",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.2.tgz",
+ "integrity": "sha512-tEB2U5z74ebBeyfGNZ3Jfg29AnW+5HlWhvHtb/Mqco9pFdZU1ZLNdVb2UtB5CvmiilNr2ZfVH/qMmAROG/XTzw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright": "1.49.1"
+ "playwright": "1.53.2"
},
"bin": {
"playwright": "cli.js"
@@ -3308,14 +1636,21 @@
}
},
"node_modules/@primer/octicons": {
- "version": "19.14.0",
- "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-19.14.0.tgz",
- "integrity": "sha512-9Ovw/xcUFHC/zbsNhr/Hkp1+m9XnNeQvnGHDHrI5vhlf6PRZVzSsdMnesV2xCzQh7jXP3EVRcaeXsUGlsZrfcA==",
+ "version": "19.15.3",
+ "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-19.15.3.tgz",
+ "integrity": "sha512-SVD0JbTzabLqLx4d5Cl3cyY+i0u3j5/lI6P3FyNkkJb9Z4VAQ+mBvl7WgK0ZFEXBsLc2gxnoYXvC3TkNSEz1NQ==",
"license": "MIT",
"dependencies": {
"object-assign": "^4.1.1"
}
},
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.19",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz",
+ "integrity": "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@rollup/plugin-commonjs": {
"version": "22.0.2",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.2.tgz",
@@ -3364,9 +1699,9 @@
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz",
- "integrity": "sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz",
+ "integrity": "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==",
"cpu": [
"arm"
],
@@ -3378,9 +1713,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.1.tgz",
- "integrity": "sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz",
+ "integrity": "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==",
"cpu": [
"arm64"
],
@@ -3392,9 +1727,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.1.tgz",
- "integrity": "sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz",
+ "integrity": "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==",
"cpu": [
"arm64"
],
@@ -3406,9 +1741,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.1.tgz",
- "integrity": "sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz",
+ "integrity": "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==",
"cpu": [
"x64"
],
@@ -3420,9 +1755,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.1.tgz",
- "integrity": "sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz",
+ "integrity": "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==",
"cpu": [
"arm64"
],
@@ -3434,9 +1769,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.1.tgz",
- "integrity": "sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz",
+ "integrity": "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==",
"cpu": [
"x64"
],
@@ -3448,9 +1783,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.1.tgz",
- "integrity": "sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz",
+ "integrity": "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==",
"cpu": [
"arm"
],
@@ -3462,9 +1797,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.1.tgz",
- "integrity": "sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz",
+ "integrity": "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==",
"cpu": [
"arm"
],
@@ -3476,9 +1811,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.1.tgz",
- "integrity": "sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz",
+ "integrity": "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==",
"cpu": [
"arm64"
],
@@ -3490,9 +1825,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.1.tgz",
- "integrity": "sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz",
+ "integrity": "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==",
"cpu": [
"arm64"
],
@@ -3504,9 +1839,9 @@
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.28.1.tgz",
- "integrity": "sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz",
+ "integrity": "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==",
"cpu": [
"loong64"
],
@@ -3518,9 +1853,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.1.tgz",
- "integrity": "sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz",
+ "integrity": "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==",
"cpu": [
"ppc64"
],
@@ -3532,9 +1867,23 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.1.tgz",
- "integrity": "sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz",
+ "integrity": "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz",
+ "integrity": "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==",
"cpu": [
"riscv64"
],
@@ -3546,9 +1895,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.1.tgz",
- "integrity": "sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz",
+ "integrity": "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==",
"cpu": [
"s390x"
],
@@ -3560,9 +1909,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.1.tgz",
- "integrity": "sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz",
+ "integrity": "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==",
"cpu": [
"x64"
],
@@ -3574,9 +1923,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.1.tgz",
- "integrity": "sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz",
+ "integrity": "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==",
"cpu": [
"x64"
],
@@ -3588,9 +1937,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.1.tgz",
- "integrity": "sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz",
+ "integrity": "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==",
"cpu": [
"arm64"
],
@@ -3602,9 +1951,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.1.tgz",
- "integrity": "sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz",
+ "integrity": "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==",
"cpu": [
"ia32"
],
@@ -3616,9 +1965,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz",
- "integrity": "sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz",
+ "integrity": "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==",
"cpu": [
"x64"
],
@@ -3643,24 +1992,6 @@
"hasInstallScript": true,
"license": "Apache-2.0"
},
- "node_modules/@silverwind/vue-tsc": {
- "version": "2.1.13",
- "resolved": "https://registry.npmjs.org/@silverwind/vue-tsc/-/vue-tsc-2.1.13.tgz",
- "integrity": "sha512-ejFxz1KZiUGAESbC+eURnjqt0N95qkU9eZU7W15wgF9zV+v2FEu3ZLduuXTC7D/Sg6lL1R/QjPfUbxbAbBQOsw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@volar/typescript": "~2.4.11",
- "@vue/language-core": "2.1.10",
- "semver": "^7.5.4"
- },
- "bin": {
- "vue-tsc": "bin/vue-tsc.js"
- },
- "peerDependencies": {
- "typescript": ">=5.0.0"
- }
- },
"node_modules/@silverwind/vue3-calendar-heatmap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@silverwind/vue3-calendar-heatmap/-/vue3-calendar-heatmap-2.0.6.tgz",
@@ -3674,6 +2005,16 @@
"vue": "^3.2.29"
}
},
+ "node_modules/@simonwep/pickr": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.9.0.tgz",
+ "integrity": "sha512-oEYvv15PyfZzjoAzvXYt3UyNGwzsrpFxLaZKzkOSd0WYBVwLd19iJerePDONxC1iF6+DpcswPdLIM2KzCJuYFg==",
+ "license": "MIT",
+ "dependencies": {
+ "core-js": "3.32.2",
+ "nanopop": "2.3.0"
+ }
+ },
"node_modules/@stoplight/better-ajv-errors": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@stoplight/better-ajv-errors/-/better-ajv-errors-1.0.3.tgz",
@@ -3773,15 +2114,15 @@
}
},
"node_modules/@stoplight/spectral-cli": {
- "version": "6.14.2",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.14.2.tgz",
- "integrity": "sha512-yn49Tkin/Zzjwt39CbQvj3NZVolvXrjO3OLNn+Yd+LhQE5C96QNsULuE4q98e7+WRcLu4gK+Z0l5CxYNtsoPtw==",
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.15.0.tgz",
+ "integrity": "sha512-FVeQIuqQQnnLfa8vy+oatTKUve7uU+3SaaAfdjpX/B+uB1NcfkKRJYhKT9wMEehDRaMPL5AKIRYMCFerdEbIpw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@stoplight/json": "~3.21.0",
"@stoplight/path": "1.3.2",
- "@stoplight/spectral-core": "^1.19.4",
+ "@stoplight/spectral-core": "^1.19.5",
"@stoplight/spectral-formatters": "^1.4.1",
"@stoplight/spectral-parsers": "^1.0.4",
"@stoplight/spectral-ref-resolver": "^1.0.4",
@@ -3837,9 +2178,9 @@
}
},
"node_modules/@stoplight/spectral-core": {
- "version": "1.19.4",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.19.4.tgz",
- "integrity": "sha512-8hnZXfssTlV99SKo8J8BwMt5LsiBFHkCh0V3P7j8IPcCNl//bpG92U4TpYy7AwmUms/zCLX7sxNQC6AZ+bkfzg==",
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.20.0.tgz",
+ "integrity": "sha512-5hBP81nCC1zn1hJXL/uxPNRKNcB+/pEIHgCjPRpl/w/qy9yC9ver04tw1W0l/PMiv0UeB5dYgozXVQ4j5a6QQQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -3854,9 +2195,9 @@
"@types/json-schema": "^7.0.11",
"ajv": "^8.17.1",
"ajv-errors": "~3.0.0",
- "ajv-formats": "~2.1.0",
+ "ajv-formats": "~2.1.1",
"es-aggregate-error": "^1.0.7",
- "jsonpath-plus": "10.2.0",
+ "jsonpath-plus": "^10.3.0",
"lodash": "~4.17.21",
"lodash.topath": "^4.5.2",
"minimatch": "3.1.2",
@@ -3883,17 +2224,6 @@
"node": "^12.20 || >=14.13"
}
},
- "node_modules/@stoplight/spectral-core/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/@stoplight/spectral-core/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -3924,9 +2254,9 @@
}
},
"node_modules/@stoplight/spectral-formatters": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-formatters/-/spectral-formatters-1.4.3.tgz",
- "integrity": "sha512-03Nc6nhjMO9aHhJPgBH4zDwMPklKLWEMtvx+PMmzfStCndMjJkf8ki7O/55u3myZ1TwxBzln9z9tXPLSL3KKhw==",
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-formatters/-/spectral-formatters-1.5.0.tgz",
+ "integrity": "sha512-lR7s41Z00Mf8TdXBBZQ3oi2uR8wqAtR6NO0KA8Ltk4FSpmAy0i6CKUmJG9hZQjanTnGmwpQkT/WP66p1GY3iXA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -3949,9 +2279,9 @@
}
},
"node_modules/@stoplight/spectral-functions": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-functions/-/spectral-functions-1.9.3.tgz",
- "integrity": "sha512-jy4mguk0Ddz0Vr76PHervOZeyXTUW650zVfNT2Vt9Ji3SqtTVziHjq913CBVEGFS+IQw1McUXuHVLM6YKVZ6fQ==",
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-functions/-/spectral-functions-1.10.1.tgz",
+ "integrity": "sha512-obu8ZfoHxELOapfGsCJixKZXZcffjg+lSoNuttpmUFuDzVLT3VmH8QkPXfOGOL5Pz80BR35ClNAToDkdnYIURg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -3963,7 +2293,7 @@
"ajv": "^8.17.1",
"ajv-draft-04": "~1.0.0",
"ajv-errors": "~3.0.0",
- "ajv-formats": "~2.1.0",
+ "ajv-formats": "~2.1.1",
"lodash": "~4.17.21",
"tslib": "^2.8.1"
},
@@ -4019,9 +2349,9 @@
}
},
"node_modules/@stoplight/spectral-ruleset-bundler": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-bundler/-/spectral-ruleset-bundler-1.6.1.tgz",
- "integrity": "sha512-Pk0OVqyHXc/grFtaOWXF268UNRjwAnSGf9idBXO1XZJbieUyrYRJ44v5/E1UVxRMvzVkQ6/As/Ggi8hsEybKZw==",
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-bundler/-/spectral-ruleset-bundler-1.6.3.tgz",
+ "integrity": "sha512-AQFRO6OCKg8SZJUupnr3+OzI1LrMieDTEUHsYgmaRpNiDRPvzImE3bzM1KyQg99q58kTQyZ8kpr7sG8Lp94RRA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -4047,9 +2377,9 @@
}
},
"node_modules/@stoplight/spectral-ruleset-migrator": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.11.1.tgz",
- "integrity": "sha512-z2A1Ual3bU7zLDxYqdHaxYgyirb7TVDaWXc9ONEBAo5W1isio0EHV59ujAUEOUHCLcY5ubd0eYeqgSjqPIQe8w==",
+ "version": "1.11.2",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.11.2.tgz",
+ "integrity": "sha512-6r5i4hrDmppspSSxdUKKNHc07NGSSIkvwKNk3M5ukCwvSslImvDEimeWAhPBryhmSJ82YAsKr8erZZpKullxWw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -4096,9 +2426,9 @@
"license": "Apache-2.0"
},
"node_modules/@stoplight/spectral-rulesets": {
- "version": "1.21.3",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-rulesets/-/spectral-rulesets-1.21.3.tgz",
- "integrity": "sha512-SQp/NNDykfCvgmo9DW1pBAbmyKRHhEHmsc28kuRHC6nJblGFsLyNVGkEDjSIJuviR7ooC2Y00vmf0R3OGcyhyw==",
+ "version": "1.22.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-rulesets/-/spectral-rulesets-1.22.0.tgz",
+ "integrity": "sha512-l2EY2jiKKLsvnPfGy+pXC0LeGsbJzcQP5G/AojHgf+cwN//VYxW1Wvv4WKFx/CLmLxc42mJYF2juwWofjWYNIQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -4112,7 +2442,7 @@
"@stoplight/types": "^13.6.0",
"@types/json-schema": "^7.0.7",
"ajv": "^8.17.1",
- "ajv-formats": "~2.1.0",
+ "ajv-formats": "~2.1.1",
"json-schema-traverse": "^1.0.0",
"leven": "3.1.0",
"lodash": "~4.17.21",
@@ -4123,9 +2453,9 @@
}
},
"node_modules/@stoplight/spectral-runtime": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-runtime/-/spectral-runtime-1.1.3.tgz",
- "integrity": "sha512-uoKSVX/OYXOEBRQN7EtAaVefl8MlyhBkDcU2aDYEGALwYXHAH+vmF3ljhZrueMA3fSWLHTL3RxWqsjeeCor6lw==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-runtime/-/spectral-runtime-1.1.4.tgz",
+ "integrity": "sha512-YHbhX3dqW0do6DhiPSgSGQzr6yQLlWybhKwWx0cqxjMwxej3TqLv3BXMfIUYFKKUqIwH4Q2mV8rrMM8qD2N0rQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -4134,7 +2464,7 @@
"@stoplight/types": "^13.6.0",
"abort-controller": "^3.0.0",
"lodash": "^4.17.21",
- "node-fetch": "^2.6.7",
+ "node-fetch": "^2.7.0",
"tslib": "^2.8.1"
},
"engines": {
@@ -4193,9 +2523,9 @@
}
},
"node_modules/@stylistic/eslint-plugin-js": {
- "version": "2.12.1",
- "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.12.1.tgz",
- "integrity": "sha512-5ybogtEgWIGCR6dMnaabztbWyVdAPDsf/5XOk6jBonWug875Q9/a6gm9QxnU3rhdyDEnckWKX7dduwYJMOWrVA==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-3.1.0.tgz",
+ "integrity": "sha512-lQktsOiCr8S6StG29C5fzXYxLOD6ID1rp4j6TRS+E/qY1xd59Fm7dy5qm9UauJIEoSTlYx6yGsCHYh5UkgXPyg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4210,9 +2540,9 @@
}
},
"node_modules/@stylistic/stylelint-plugin": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.1.1.tgz",
- "integrity": "sha512-XagAHHIa528EvyGybv8EEYGK5zrVW74cHpsjhtovVATbhDRuJYfE+X4HCaAieW9lCkwbX6L+X0I4CiUG3w/hFw==",
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.1.3.tgz",
+ "integrity": "sha512-85fsmzgsIVmyG3/GFrjuYj6Cz8rAM7IZiPiXCMiSMfoDOC1lOrzrXPDk24WqviAghnPqGpx8b0caK2PuewWGFg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4220,10 +2550,10 @@
"@csstools/css-tokenizer": "^3.0.1",
"@csstools/media-query-list-parser": "^3.0.1",
"is-plain-object": "^5.0.0",
+ "postcss": "^8.4.41",
"postcss-selector-parser": "^6.1.2",
"postcss-value-parser": "^4.2.0",
- "style-search": "^0.1.0",
- "stylelint": "^16.8.2"
+ "style-search": "^0.1.0"
},
"engines": {
"node": "^18.12 || >=20.9"
@@ -4238,20 +2568,31 @@
"integrity": "sha512-wpCQMhf5p5GhNg2MmGKXzUNwxe7zRiCsmqYsamez2beP7mKPCSiu+BjZcdN95yYSzO857kr0VfQewmGpS77nqA==",
"license": "MIT"
},
- "node_modules/@trysound/sax": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
- "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz",
+ "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==",
"dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=10.13.0"
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/chai": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz",
+ "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/deep-eql": "*"
}
},
"node_modules/@types/codemirror": {
- "version": "5.60.15",
- "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.15.tgz",
- "integrity": "sha512-dTOvwEQ+ouKJ/rE9LT1Ue2hmP6H1mZv5+CCnNWu2qtiOe2LQa9lCprEY20HxiDmV/Bxh+dXjywmy5aKvoGjULA==",
+ "version": "5.60.16",
+ "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.16.tgz",
+ "integrity": "sha512-V/yHdamffSS075jit+fDxaOAmdP2liok8NSNJnAZfDJErzOheuygHZEhAJrfmk5TEyM32MhkZjwo/idX791yxw==",
"license": "MIT",
"dependencies": {
"@types/tern": "*"
@@ -4420,9 +2761,9 @@
}
},
"node_modules/@types/d3-path": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz",
- "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
+ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
"license": "MIT"
},
"node_modules/@types/d3-polygon": {
@@ -4444,9 +2785,9 @@
"license": "MIT"
},
"node_modules/@types/d3-scale": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
- "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
+ "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
"license": "MIT",
"dependencies": {
"@types/d3-time": "*"
@@ -4465,9 +2806,9 @@
"license": "MIT"
},
"node_modules/@types/d3-shape": {
- "version": "3.1.6",
- "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz",
- "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==",
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
+ "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
"license": "MIT",
"dependencies": {
"@types/d3-path": "*"
@@ -4510,10 +2851,20 @@
"@types/d3-selection": "*"
}
},
- "node_modules/@types/doctrine": {
- "version": "0.0.9",
- "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz",
- "integrity": "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==",
+ "node_modules/@types/debug": {
+ "version": "4.1.12",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
+ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "node_modules/@types/deep-eql": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
"dev": true,
"license": "MIT"
},
@@ -4564,9 +2915,9 @@
"license": "MIT"
},
"node_modules/@types/geojson": {
- "version": "7946.0.15",
- "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.15.tgz",
- "integrity": "sha512-9oSxFzDCT2Rj6DfcHF8G++jxBKS7mBqXl5xrRW+Kbvjry6Uduya2iiwqHPhVXpasAVMBYKkEPGgKhd3+/HZ6xA==",
+ "version": "7946.0.16",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
+ "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
"license": "MIT"
},
"node_modules/@types/hammerjs": {
@@ -4606,9 +2957,9 @@
"license": "MIT"
},
"node_modules/@types/license-checker-webpack-plugin": {
- "version": "0.2.4",
- "resolved": "https://registry.npmjs.org/@types/license-checker-webpack-plugin/-/license-checker-webpack-plugin-0.2.4.tgz",
- "integrity": "sha512-QTWqHJ5T9lgm3vPwWSZnBwAB+15zl4QBfGoNDcjnthHQEP8VTV87fYfp1HVeCtrDip73xWMtasQeA4QHQ0nFLw==",
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/@types/license-checker-webpack-plugin/-/license-checker-webpack-plugin-0.2.5.tgz",
+ "integrity": "sha512-raj3YPZxjkDlRrJJUq6So+C9i/fqYxUtVZzlXxm6bwwXONOnTHvGCfYmbQhzsHJneiJdlR6mMH76/bvWbZZN8Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4628,13 +2979,20 @@
"integrity": "sha512-a79Yc3TOk6dGdituy8hmTTJXjOkZ7zsFYV10L337ttq/rec8lRMDBpV7fL3uLx6TgbFCa5DU/h8FmIBQPSbU0w==",
"license": "MIT"
},
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/node": {
- "version": "22.10.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz",
- "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==",
+ "version": "24.0.10",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz",
+ "integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==",
"license": "MIT",
"dependencies": {
- "undici-types": "~6.20.0"
+ "undici-types": "~7.8.0"
}
},
"node_modules/@types/normalize-package-data": {
@@ -4680,9 +3038,9 @@
"license": "MIT"
},
"node_modules/@types/swagger-ui-dist": {
- "version": "3.30.5",
- "resolved": "https://registry.npmjs.org/@types/swagger-ui-dist/-/swagger-ui-dist-3.30.5.tgz",
- "integrity": "sha512-SrXhD9L8qeIxJzN+o1kmf3wXeVf/+Km3jIdRM1+Yq3I5b/dlF5TcGr5WCVM7I/cBYpgf43/gCPIucQ13AhICiw==",
+ "version": "3.30.6",
+ "resolved": "https://registry.npmjs.org/@types/swagger-ui-dist/-/swagger-ui-dist-3.30.6.tgz",
+ "integrity": "sha512-FVxN7wjLYRtJsZBscOcOcf8oR++m38vbUFjT33Mr9HBuasX9bRDrJsp7iwixcOtKSHEEa2B7o2+4wEiXqC+Ebw==",
"dev": true,
"license": "MIT"
},
@@ -4717,9 +3075,9 @@
"license": "MIT"
},
"node_modules/@types/toastify-js": {
- "version": "1.12.3",
- "resolved": "https://registry.npmjs.org/@types/toastify-js/-/toastify-js-1.12.3.tgz",
- "integrity": "sha512-9RjLlbAHMSaae/KZNHGv19VG4gcLIm3YjvacCXBtfMfYn26h76YP5oxXI8k26q4iKXCB9LNfv18lsoS0JnFPTg==",
+ "version": "1.12.4",
+ "resolved": "https://registry.npmjs.org/@types/toastify-js/-/toastify-js-1.12.4.tgz",
+ "integrity": "sha512-zfZHU4tKffPCnZRe7pjv/eFKzTVHozKewFCKaCjZ4gFinKgJRz/t0bkZiMCXJxPhv/ZoeDGNOeRD09R0kQZ/nw==",
"dev": true,
"license": "MIT"
},
@@ -4740,6 +3098,13 @@
"source-map": "^0.6.1"
}
},
+ "node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/urijs": {
"version": "1.19.25",
"resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.25.tgz",
@@ -4784,22 +3149,29 @@
"node": ">= 8"
}
},
+ "node_modules/@types/whatwg-mimetype": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz",
+ "integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.18.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.1.tgz",
- "integrity": "sha512-Ncvsq5CT3Gvh+uJG0Lwlho6suwDfUXH0HztslDf5I+F2wAFAZMRwYLEorumpKLzmO2suAXZ/td1tBg4NZIi9CQ==",
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz",
+ "integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.18.1",
- "@typescript-eslint/type-utils": "8.18.1",
- "@typescript-eslint/utils": "8.18.1",
- "@typescript-eslint/visitor-keys": "8.18.1",
+ "@typescript-eslint/scope-manager": "8.35.1",
+ "@typescript-eslint/type-utils": "8.35.1",
+ "@typescript-eslint/utils": "8.35.1",
+ "@typescript-eslint/visitor-keys": "8.35.1",
"graphemer": "^1.4.0",
- "ignore": "^5.3.1",
+ "ignore": "^7.0.0",
"natural-compare": "^1.4.0",
- "ts-api-utils": "^1.3.0"
+ "ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4809,22 +3181,32 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "@typescript-eslint/parser": "^8.35.1",
"eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.18.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.18.1.tgz",
- "integrity": "sha512-rBnTWHCdbYM2lh7hjyXqxk70wvon3p2FyaniZuey5TrcGBpfhVp0OxOa6gxr9Q9YhZFKyfbEnxc24ZnVbbUkCA==",
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz",
+ "integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.18.1",
- "@typescript-eslint/types": "8.18.1",
- "@typescript-eslint/typescript-estree": "8.18.1",
- "@typescript-eslint/visitor-keys": "8.18.1",
+ "@typescript-eslint/scope-manager": "8.35.1",
+ "@typescript-eslint/types": "8.35.1",
+ "@typescript-eslint/typescript-estree": "8.35.1",
+ "@typescript-eslint/visitor-keys": "8.35.1",
"debug": "^4.3.4"
},
"engines": {
@@ -4836,18 +3218,40 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz",
+ "integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.35.1",
+ "@typescript-eslint/types": "^8.35.1",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.18.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.1.tgz",
- "integrity": "sha512-HxfHo2b090M5s2+/9Z3gkBhI6xBH8OJCFjH9MhQ+nnoZqxU3wNxkLT+VWXWSFWc3UF3Z+CfPAyqdCTdoXtDPCQ==",
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz",
+ "integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.18.1",
- "@typescript-eslint/visitor-keys": "8.18.1"
+ "@typescript-eslint/types": "8.35.1",
+ "@typescript-eslint/visitor-keys": "8.35.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4857,17 +3261,34 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz",
+ "integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.18.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.1.tgz",
- "integrity": "sha512-jAhTdK/Qx2NJPNOTxXpMwlOiSymtR2j283TtPqXkKBdH8OAMmhiUfP0kJjc/qSE51Xrq02Gj9NY7MwK+UxVwHQ==",
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz",
+ "integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/typescript-estree": "8.18.1",
- "@typescript-eslint/utils": "8.18.1",
+ "@typescript-eslint/typescript-estree": "8.35.1",
+ "@typescript-eslint/utils": "8.35.1",
"debug": "^4.3.4",
- "ts-api-utils": "^1.3.0"
+ "ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4878,13 +3299,13 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.18.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.1.tgz",
- "integrity": "sha512-7uoAUsCj66qdNQNpH2G8MyTFlgerum8ubf21s3TSM3XmKXuIn+H2Sifh/ES2nPOPiYSRJWAk0fDkW0APBWcpfw==",
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz",
+ "integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4896,20 +3317,22 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.18.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.1.tgz",
- "integrity": "sha512-z8U21WI5txzl2XYOW7i9hJhxoKKNG1kcU4RzyNvKrdZDmbjkmLBo8bgeiOJmA06kizLI76/CCBAAGlTlEeUfyg==",
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz",
+ "integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.18.1",
- "@typescript-eslint/visitor-keys": "8.18.1",
+ "@typescript-eslint/project-service": "8.35.1",
+ "@typescript-eslint/tsconfig-utils": "8.35.1",
+ "@typescript-eslint/types": "8.35.1",
+ "@typescript-eslint/visitor-keys": "8.35.1",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
- "ts-api-utils": "^1.3.0"
+ "ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4919,7 +3342,24 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "typescript": ">=4.8.4 <5.8.0"
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
@@ -4939,16 +3379,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.18.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.1.tgz",
- "integrity": "sha512-8vikiIj2ebrC4WRdcAdDcmnu9Q/MXXwg+STf40BVfT8exDqBCUPdypvzcUPxEqRGKg9ALagZ0UWcYCtn+4W2iQ==",
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz",
+ "integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "8.18.1",
- "@typescript-eslint/types": "8.18.1",
- "@typescript-eslint/typescript-estree": "8.18.1"
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.35.1",
+ "@typescript-eslint/types": "8.35.1",
+ "@typescript-eslint/typescript-estree": "8.35.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4959,18 +3399,18 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.18.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.1.tgz",
- "integrity": "sha512-Vj0WLm5/ZsD013YeUKn+K0y8p1M0jPpxOkKdbD1wB0ns53a5piVY02zjf072TblEweAbcYiFiPoSMF3kp+VhhQ==",
+ "version": "8.35.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz",
+ "integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.18.1",
- "eslint-visitor-keys": "^4.2.0"
+ "@typescript-eslint/types": "8.35.1",
+ "eslint-visitor-keys": "^4.2.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -4981,59 +3421,355 @@
}
},
"node_modules/@ungap/structured-clone": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz",
- "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
+ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
"dev": true,
"license": "ISC"
},
+ "node_modules/@unrs/resolver-binding-android-arm-eabi": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.10.1.tgz",
+ "integrity": "sha512-zohDKXT1Ok0yhbVGff4YAg9HUs5ietG5GpvJBPFSApZnGe7uf2cd26DRhKZbn0Be6xHUZrSzP+RAgMmzyc71EA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-android-arm64": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.10.1.tgz",
+ "integrity": "sha512-tAN6k5UrTd4nicpA7s2PbjR/jagpDzAmvXFjbpTazUe5FRsFxVcBlS1F5Lzp5jtWU6bdiqRhSvd4X8rdpCffeA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-arm64": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.10.1.tgz",
+ "integrity": "sha512-+FCsag8WkauI4dQ50XumCXdfvDCZEpMUnvZDsKMxfOisnEklpDFXc6ThY0WqybBYZbiwR5tWcFaZmI0G6b4vrg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-x64": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.10.1.tgz",
+ "integrity": "sha512-qYKGGm5wk71ONcXTMZ0+J11qQeOAPz3nw6VtqrBUUELRyXFyvK8cHhHsLBFR4GHnilc2pgY1HTB2TvdW9wO26Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-freebsd-x64": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.10.1.tgz",
+ "integrity": "sha512-hOHMAhbvIQ63gkpgeNsXcWPSyvXH7ZEyeg254hY0Lp/hX8NdW+FsUWq73g9946Pc/BrcVI/I3C1cmZ4RCX9bNw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.10.1.tgz",
+ "integrity": "sha512-6ds7+zzHJgTDmpe0gmFcOTvSUhG5oZukkt+cCsSb3k4Uiz2yEQB4iCRITX2hBwSW+p8gAieAfecITjgqCkswXw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.10.1.tgz",
+ "integrity": "sha512-P7A0G2/jW00diNJyFeq4W9/nxovD62Ay8CMP4UK9OymC7qO7rG1a8Upad68/bdfpIOn7KSp7Aj/6lEW3yyznAA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.10.1.tgz",
+ "integrity": "sha512-Cg6xzdkrpltcTPO4At+A79zkC7gPDQIgosJmVV8M104ImB6KZi1MrNXgDYIAfkhUYjPzjNooEDFRAwwPadS7ZA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.10.1.tgz",
+ "integrity": "sha512-aNeg99bVkXa4lt+oZbjNRPC8ZpjJTKxijg/wILrJdzNyAymO2UC/HUK1UfDjt6T7U5p/mK24T3CYOi3/+YEQSA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.10.1.tgz",
+ "integrity": "sha512-ylz5ojeXrkPrtnzVhpCO+YegG63/aKhkoTlY8PfMfBfLaUG8v6m6iqrL7sBUKdVBgOB4kSTUPt9efQdA/Y3Z/w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.10.1.tgz",
+ "integrity": "sha512-xcWyhmJfXXOxK7lvE4+rLwBq+on83svlc0AIypfe6x4sMJR+S4oD7n9OynaQShfj2SufPw2KJAotnsNb+4nN2g==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.10.1.tgz",
+ "integrity": "sha512-mW9JZAdOCyorgi1eLJr4gX7xS67WNG9XNPYj5P8VuttK72XNsmdw9yhOO4tDANMgiLXFiSFaiL1gEpoNtRPw/A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.10.1.tgz",
+ "integrity": "sha512-NZGKhBy6xkJ0k09cWNZz4DnhBcGlhDd3W+j7EYoNvf5TSwj2K6kbmfqTWITEgkvjsMUjm1wsrc4IJaH6VtjyHQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.10.1.tgz",
+ "integrity": "sha512-VsjgckJ0gNMw7p0d8In6uPYr+s0p16yrT2rvG4v2jUpEMYkpnfnCiALa9SWshbvlGjKQ98Q2x19agm3iFk8w8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-musl": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.10.1.tgz",
+ "integrity": "sha512-idMnajMeejnaFi0Mx9UTLSYFDAOTfAEP7VjXNgxKApso3Eu2Njs0p2V95nNIyFi4oQVGFmIuCkoznAXtF/Zbmw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-wasm32-wasi": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.10.1.tgz",
+ "integrity": "sha512-7jyhjIRNFjzlr8x5pth6Oi9hv3a7ubcVYm2GBFinkBQKcFhw4nIs5BtauSNtDW1dPIGrxF0ciynCZqzxMrYMsg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^0.2.11"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.10.1.tgz",
+ "integrity": "sha512-TY79+N+Gkoo7E99K+zmsKNeiuNJYlclZJtKqsHSls8We2iGhgxtletVsiBYie93MSTDRDMI8pkBZJlIJSZPrdA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.10.1.tgz",
+ "integrity": "sha512-BAJN5PEPlEV+1m8+PCtFoKm3LQ1P57B4Z+0+efU0NzmCaGk7pUaOxuPgl+m3eufVeeNBKiPDltG0sSB9qEfCxw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.10.1.tgz",
+ "integrity": "sha512-2v3erKKmmCyIVvvhI2nF15qEbdBpISTq44m9pyd5gfIJB1PN94oePTLWEd82XUbIbvKhv76xTSeUQSCOGesLeg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
"node_modules/@vitejs/plugin-vue": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz",
- "integrity": "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==",
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.0.tgz",
+ "integrity": "sha512-iAliE72WsdhjzTOp2DtvKThq1VBC4REhwRcaA+zPAAph6I+OQhUXv+Xu2KS7ElxYtb7Zc/3R30Hwv1DxEo7NXQ==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "1.0.0-beta.19"
+ },
"engines": {
- "node": "^18.0.0 || >=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
- "vite": "^5.0.0 || ^6.0.0",
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
"vue": "^3.2.25"
}
},
+ "node_modules/@vitest/eslint-plugin": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.3.4.tgz",
+ "integrity": "sha512-EOg8d0jn3BAiKnR55WkFxmxfWA3nmzrbIIuOXyTe6A72duryNgyU+bdBEauA97Aab3ho9kLmAwgPX63Ckj4QEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/utils": "^8.24.1"
+ },
+ "peerDependencies": {
+ "eslint": ">= 8.57.0",
+ "typescript": ">= 5.0.0",
+ "vitest": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ },
+ "vitest": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@vitest/expect": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz",
- "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz",
+ "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/spy": "2.1.8",
- "@vitest/utils": "2.1.8",
- "chai": "^5.1.2",
- "tinyrainbow": "^1.2.0"
+ "@types/chai": "^5.2.2",
+ "@vitest/spy": "3.2.4",
+ "@vitest/utils": "3.2.4",
+ "chai": "^5.2.0",
+ "tinyrainbow": "^2.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/mocker": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz",
- "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz",
+ "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/spy": "2.1.8",
+ "@vitest/spy": "3.2.4",
"estree-walker": "^3.0.3",
- "magic-string": "^0.30.12"
+ "magic-string": "^0.30.17"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"msw": "^2.4.9",
- "vite": "^5.0.0"
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
},
"peerDependenciesMeta": {
"msw": {
@@ -5045,9 +3781,9 @@
}
},
"node_modules/@vitest/mocker/node_modules/@types/estree": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
- "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
@@ -5072,42 +3808,43 @@
}
},
"node_modules/@vitest/pretty-format": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz",
- "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz",
+ "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "tinyrainbow": "^1.2.0"
+ "tinyrainbow": "^2.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/runner": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz",
- "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz",
+ "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/utils": "2.1.8",
- "pathe": "^1.1.2"
+ "@vitest/utils": "3.2.4",
+ "pathe": "^2.0.3",
+ "strip-literal": "^3.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/snapshot": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz",
- "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz",
+ "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "2.1.8",
- "magic-string": "^0.30.12",
- "pathe": "^1.1.2"
+ "@vitest/pretty-format": "3.2.4",
+ "magic-string": "^0.30.17",
+ "pathe": "^2.0.3"
},
"funding": {
"url": "https://opencollective.com/vitest"
@@ -5124,100 +3861,100 @@
}
},
"node_modules/@vitest/spy": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz",
- "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz",
+ "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "tinyspy": "^3.0.2"
+ "tinyspy": "^4.0.3"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/utils": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz",
- "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz",
+ "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "2.1.8",
- "loupe": "^3.1.2",
- "tinyrainbow": "^1.2.0"
+ "@vitest/pretty-format": "3.2.4",
+ "loupe": "^3.1.4",
+ "tinyrainbow": "^2.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@volar/language-core": {
- "version": "2.4.11",
- "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.11.tgz",
- "integrity": "sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==",
+ "version": "2.4.17",
+ "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.17.tgz",
+ "integrity": "sha512-chmRZMbKmcGpKMoO7Reb70uiLrzo0KWC2CkFttKUuKvrE+VYgi+fL9vWMJ07Fv5ulX0V1TAyyacN9q3nc5/ecA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@volar/source-map": "2.4.11"
+ "@volar/source-map": "2.4.17"
}
},
"node_modules/@volar/source-map": {
- "version": "2.4.11",
- "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.11.tgz",
- "integrity": "sha512-ZQpmafIGvaZMn/8iuvCFGrW3smeqkq/IIh9F1SdSx9aUl0J4Iurzd6/FhmjNO5g2ejF3rT45dKskgXWiofqlZQ==",
+ "version": "2.4.17",
+ "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.17.tgz",
+ "integrity": "sha512-QDybtQyO3Ms/NjFqNHTC5tbDN2oK5VH7ZaKrcubtfHBDj63n2pizHC3wlMQ+iT55kQXZUUAbmBX5L1C8CHFeBw==",
"dev": true,
"license": "MIT"
},
"node_modules/@volar/typescript": {
- "version": "2.4.11",
- "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.11.tgz",
- "integrity": "sha512-2DT+Tdh88Spp5PyPbqhyoYavYCPDsqbHLFwcUI9K1NlY1YgUJvujGdrqUp0zWxnW7KWNTr3xSpMuv2WnaTKDAw==",
+ "version": "2.4.17",
+ "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.17.tgz",
+ "integrity": "sha512-3paEFNh4P5DkgNUB2YkTRrfUekN4brAXxd3Ow1syMqdIPtCZHbUy4AW99S5RO/7mzyTWPMdDSo3mqTpB/LPObQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@volar/language-core": "2.4.11",
+ "@volar/language-core": "2.4.17",
"path-browserify": "^1.0.1",
"vscode-uri": "^3.0.8"
}
},
"node_modules/@vue/compiler-core": {
- "version": "3.5.13",
- "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz",
- "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==",
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.17.tgz",
+ "integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.25.3",
- "@vue/shared": "3.5.13",
+ "@babel/parser": "^7.27.5",
+ "@vue/shared": "3.5.17",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
- "source-map-js": "^1.2.0"
+ "source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-dom": {
- "version": "3.5.13",
- "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz",
- "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==",
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz",
+ "integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-core": "3.5.13",
- "@vue/shared": "3.5.13"
+ "@vue/compiler-core": "3.5.17",
+ "@vue/shared": "3.5.17"
}
},
"node_modules/@vue/compiler-sfc": {
- "version": "3.5.13",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz",
- "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==",
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz",
+ "integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.25.3",
- "@vue/compiler-core": "3.5.13",
- "@vue/compiler-dom": "3.5.13",
- "@vue/compiler-ssr": "3.5.13",
- "@vue/shared": "3.5.13",
+ "@babel/parser": "^7.27.5",
+ "@vue/compiler-core": "3.5.17",
+ "@vue/compiler-dom": "3.5.17",
+ "@vue/compiler-ssr": "3.5.17",
+ "@vue/shared": "3.5.17",
"estree-walker": "^2.0.2",
- "magic-string": "^0.30.11",
- "postcss": "^8.4.48",
- "source-map-js": "^1.2.0"
+ "magic-string": "^0.30.17",
+ "postcss": "^8.5.6",
+ "source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-sfc/node_modules/magic-string": {
@@ -5230,13 +3967,13 @@
}
},
"node_modules/@vue/compiler-ssr": {
- "version": "3.5.13",
- "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz",
- "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==",
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz",
+ "integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.5.13",
- "@vue/shared": "3.5.13"
+ "@vue/compiler-dom": "3.5.17",
+ "@vue/shared": "3.5.17"
}
},
"node_modules/@vue/compiler-vue2": {
@@ -5251,18 +3988,18 @@
}
},
"node_modules/@vue/language-core": {
- "version": "2.1.10",
- "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.10.tgz",
- "integrity": "sha512-DAI289d0K3AB5TUG3xDp9OuQ71CnrujQwJrQnfuZDwo6eGNf0UoRlPuaVNO+Zrn65PC3j0oB2i7mNmVPggeGeQ==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.0.1.tgz",
+ "integrity": "sha512-sq+/Mc1IqIexWEQ+Q2XPiDb5SxSvY5JPqHnMOl/PlF5BekslzduX8dglSkpC17VeiAQB6dpS+4aiwNLJRduCNw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@volar/language-core": "~2.4.8",
+ "@volar/language-core": "2.4.17",
"@vue/compiler-dom": "^3.5.0",
"@vue/compiler-vue2": "^2.7.16",
"@vue/shared": "^3.5.0",
- "alien-signals": "^0.2.0",
- "minimatch": "^9.0.3",
+ "alien-signals": "^2.0.5",
+ "minimatch": "^10.0.1",
"muggle-string": "^0.4.1",
"path-browserify": "^1.0.1"
},
@@ -5275,70 +4012,54 @@
}
}
},
- "node_modules/@vue/language-core/node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/@vue/reactivity": {
- "version": "3.5.13",
- "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
- "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==",
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.17.tgz",
+ "integrity": "sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==",
"license": "MIT",
"dependencies": {
- "@vue/shared": "3.5.13"
+ "@vue/shared": "3.5.17"
}
},
"node_modules/@vue/runtime-core": {
- "version": "3.5.13",
- "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz",
- "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==",
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.17.tgz",
+ "integrity": "sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==",
"license": "MIT",
"dependencies": {
- "@vue/reactivity": "3.5.13",
- "@vue/shared": "3.5.13"
+ "@vue/reactivity": "3.5.17",
+ "@vue/shared": "3.5.17"
}
},
"node_modules/@vue/runtime-dom": {
- "version": "3.5.13",
- "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz",
- "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==",
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.17.tgz",
+ "integrity": "sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==",
"license": "MIT",
"dependencies": {
- "@vue/reactivity": "3.5.13",
- "@vue/runtime-core": "3.5.13",
- "@vue/shared": "3.5.13",
+ "@vue/reactivity": "3.5.17",
+ "@vue/runtime-core": "3.5.17",
+ "@vue/shared": "3.5.17",
"csstype": "^3.1.3"
}
},
"node_modules/@vue/server-renderer": {
- "version": "3.5.13",
- "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz",
- "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==",
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.17.tgz",
+ "integrity": "sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-ssr": "3.5.13",
- "@vue/shared": "3.5.13"
+ "@vue/compiler-ssr": "3.5.17",
+ "@vue/shared": "3.5.17"
},
"peerDependencies": {
- "vue": "3.5.13"
+ "vue": "3.5.17"
}
},
"node_modules/@vue/shared": {
- "version": "3.5.13",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz",
- "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==",
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.17.tgz",
+ "integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==",
"license": "MIT"
},
"node_modules/@webassemblyjs/ast": {
@@ -5488,42 +4209,42 @@
}
},
"node_modules/@webpack-cli/configtest": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz",
- "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz",
+ "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==",
"license": "MIT",
"engines": {
- "node": ">=14.15.0"
+ "node": ">=18.12.0"
},
"peerDependencies": {
- "webpack": "5.x.x",
- "webpack-cli": "5.x.x"
+ "webpack": "^5.82.0",
+ "webpack-cli": "6.x.x"
}
},
"node_modules/@webpack-cli/info": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz",
- "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz",
+ "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==",
"license": "MIT",
"engines": {
- "node": ">=14.15.0"
+ "node": ">=18.12.0"
},
"peerDependencies": {
- "webpack": "5.x.x",
- "webpack-cli": "5.x.x"
+ "webpack": "^5.82.0",
+ "webpack-cli": "6.x.x"
}
},
"node_modules/@webpack-cli/serve": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz",
- "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz",
+ "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==",
"license": "MIT",
"engines": {
- "node": ">=14.15.0"
+ "node": ">=18.12.0"
},
"peerDependencies": {
- "webpack": "5.x.x",
- "webpack-cli": "5.x.x"
+ "webpack": "^5.82.0",
+ "webpack-cli": "6.x.x"
},
"peerDependenciesMeta": {
"webpack-dev-server": {
@@ -5557,9 +4278,9 @@
}
},
"node_modules/acorn": {
- "version": "8.14.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
- "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -5664,16 +4385,16 @@
}
},
"node_modules/alien-signals": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.2.2.tgz",
- "integrity": "sha512-cZIRkbERILsBOXTQmMrxc9hgpxglstn69zm+F1ARf4aPAzdAFYd6sBq87ErO0Fj3DV94tglcyHG5kQz9nDC/8A==",
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-2.0.5.tgz",
+ "integrity": "sha512-PdJB6+06nUNAClInE3Dweq7/2xVAYM64vvvS1IHVHSJmgeOtEdrAGyp7Z2oJtYm0B342/Exd2NT0uMJaThcjLQ==",
"dev": true,
"license": "MIT"
},
"node_modules/ansi_up": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/ansi_up/-/ansi_up-6.0.2.tgz",
- "integrity": "sha512-3G3vKvl1ilEp7J1u6BmULpMA0xVoW/f4Ekqhl8RTrJrhEBkonKn5k3bUc5Xt+qDayA6iDX0jyUh3AbZjB/l0tw==",
+ "version": "6.0.6",
+ "resolved": "https://registry.npmjs.org/ansi_up/-/ansi_up-6.0.6.tgz",
+ "integrity": "sha512-yIa1x3Ecf8jWP4UWEunNjqNX6gzE4vg2gGz+xqRGY+TBSucnYp6RRdPV4brmtg6bQ1ljD48mZ5iGSEj7QEpRKA==",
"license": "MIT",
"engines": {
"node": "*"
@@ -5755,13 +4476,13 @@
},
"node_modules/array-includes": {
"name": "@nolyfill/array-includes",
- "version": "1.0.28",
- "resolved": "https://registry.npmjs.org/@nolyfill/array-includes/-/array-includes-1.0.28.tgz",
- "integrity": "sha512-3LFZArKSQTQu//UvQXb4lBHWvhxmiZ5h2v50WIXfWb5UPNgeLpeGP8WgsfTePCpZgNlxt5JVFDdv5zLRa7cQXw==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/array-includes/-/array-includes-1.0.44.tgz",
+ "integrity": "sha512-IVEqpEgFbLaU0hUoMwJYXNSdi6lq+FxHdxd8xTKDLxh8k6u5YNGz4Bo6bT46l7p0x8PbJmHViBtngqhvE528fA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@nolyfill/shared": "1.0.28"
+ "@nolyfill/shared": "1.0.44"
},
"engines": {
"node": ">=12.4.0"
@@ -5779,34 +4500,27 @@
},
"node_modules/array.prototype.findlastindex": {
"name": "@nolyfill/array.prototype.findlastindex",
- "version": "1.0.24",
- "resolved": "https://registry.npmjs.org/@nolyfill/array.prototype.findlastindex/-/array.prototype.findlastindex-1.0.24.tgz",
- "integrity": "sha512-UhPUzrObJnaFB94ywGz818q9KLbgffieqKfkG/5kL9j7VS+ikC4gG2jo8/i4zqgvJT3ppHb9buEQ3RRg7fZg8Q==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/array.prototype.findlastindex/-/array.prototype.findlastindex-1.0.44.tgz",
+ "integrity": "sha512-BLeHS3SulsR3iFxxETL9q21lArV2KS7lh2wcUnhue1ppx19xah1W7MdFxepyeGbM3Umk9S90snfboXAds5HkTg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@nolyfill/shared": "1.0.24"
+ "@nolyfill/shared": "1.0.44"
},
"engines": {
"node": ">=12.4.0"
}
},
- "node_modules/array.prototype.findlastindex/node_modules/@nolyfill/shared": {
- "version": "1.0.24",
- "resolved": "https://registry.npmjs.org/@nolyfill/shared/-/shared-1.0.24.tgz",
- "integrity": "sha512-TGCpg3k5N7jj9AgU/1xFw9K1g4AC1vEE5ZFkW77oPNNLzprxT17PvFaNr/lr3BkkT5fJ5LNMntaTIq+pyWaeEA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/array.prototype.flat": {
"name": "@nolyfill/array.prototype.flat",
- "version": "1.0.28",
- "resolved": "https://registry.npmjs.org/@nolyfill/array.prototype.flat/-/array.prototype.flat-1.0.28.tgz",
- "integrity": "sha512-bvBWaZDCWV7+jD70tJCy3Olp03Qx9svHN2KmC2j0CYvqfYRet5+iOb09nzb6QULqGrj7O8DQJ03ZQk6gih9J3g==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/array.prototype.flat/-/array.prototype.flat-1.0.44.tgz",
+ "integrity": "sha512-HnOqOT4te0l+XU9UKhy3ry+pc+ZRNsUJFR7omMEtjXf4+dq6oXmIBk7vR35+hSTk4ldjwm/27jwV3ZIGp3l4IQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@nolyfill/shared": "1.0.28"
+ "@nolyfill/shared": "1.0.44"
},
"engines": {
"node": ">=12.4.0"
@@ -5814,13 +4528,13 @@
},
"node_modules/array.prototype.flatmap": {
"name": "@nolyfill/array.prototype.flatmap",
- "version": "1.0.28",
- "resolved": "https://registry.npmjs.org/@nolyfill/array.prototype.flatmap/-/array.prototype.flatmap-1.0.28.tgz",
- "integrity": "sha512-Ui/aMijqnYISchzIG0MbRiRh2DKWORJW2s//nw6rJ5jFp6x+nmFCQ5U2be3+id36VsmTxXiv+qLAHxdfXz8g8g==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/array.prototype.flatmap/-/array.prototype.flatmap-1.0.44.tgz",
+ "integrity": "sha512-P6OsaEUrpBJ9NdNekFDQVM9LOFHPDKSJzwOWRBaC6LqREX+4lkZT2Q+to78R6aG6atuOQsxBVqPjMGCKjWdvyQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@nolyfill/shared": "1.0.28"
+ "@nolyfill/shared": "1.0.44"
},
"engines": {
"node": ">=12.4.0"
@@ -5837,9 +4551,9 @@
}
},
"node_modules/asciinema-player": {
- "version": "3.8.1",
- "resolved": "https://registry.npmjs.org/asciinema-player/-/asciinema-player-3.8.1.tgz",
- "integrity": "sha512-NkpbFg81Y6iJFpDRndakLCQ0G26XSpvuT3vJTFjMRgHb26lqHgRNY9gun54e5MehZ4fEDNYkMZv+z6MfZ8c2aA==",
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/asciinema-player/-/asciinema-player-3.10.0.tgz",
+ "integrity": "sha512-shoOK6F606nDKZxDVM7JuGSCAyWLePoGRFNlV+FqiP5Sqvyn0BlE7wlbjZyd2X4P1iRhv/HKfVNtnQIxmgphRA==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "^7.21.0",
@@ -5910,9 +4624,9 @@
}
},
"node_modules/axe-core": {
- "version": "4.10.2",
- "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz",
- "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==",
+ "version": "4.10.3",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz",
+ "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==",
"dev": true,
"license": "MPL-2.0",
"engines": {
@@ -5929,62 +4643,11 @@
"node": ">= 0.4"
}
},
- "node_modules/babel-plugin-polyfill-corejs2": {
- "version": "0.4.12",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz",
- "integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/compat-data": "^7.22.6",
- "@babel/helper-define-polyfill-provider": "^0.6.3",
- "semver": "^6.3.1"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/babel-plugin-polyfill-corejs3": {
- "version": "0.10.6",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz",
- "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-define-polyfill-provider": "^0.6.2",
- "core-js-compat": "^3.38.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/babel-plugin-polyfill-regenerator": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz",
- "integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-define-polyfill-provider": "^0.6.3"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
"node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz",
+ "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==",
+ "dev": true,
"license": "MIT"
},
"node_modules/base64-js": {
@@ -6036,14 +4699,21 @@
"license": "ISC"
},
"node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"license": "MIT",
"dependencies": {
- "balanced-match": "^1.0.0"
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
}
},
+ "node_modules/brace-expansion/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "license": "MIT"
+ },
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
@@ -6057,9 +4727,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.24.3",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz",
- "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==",
+ "version": "4.25.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
+ "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
"funding": [
{
"type": "opencollective",
@@ -6076,10 +4746,10 @@
],
"license": "MIT",
"dependencies": {
- "caniuse-lite": "^1.0.30001688",
- "electron-to-chromium": "^1.5.73",
+ "caniuse-lite": "^1.0.30001726",
+ "electron-to-chromium": "^1.5.173",
"node-releases": "^2.0.19",
- "update-browserslist-db": "^1.1.1"
+ "update-browserslist-db": "^1.1.3"
},
"bin": {
"browserslist": "cli.js"
@@ -6158,6 +4828,27 @@
"node": ">=8"
}
},
+ "node_modules/cacheable": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.10.1.tgz",
+ "integrity": "sha512-Fa2BZY0CS9F0PFc/6aVA6tgpOdw+hmv9dkZOlHXII5v5Hw+meJBIWDcPrG9q/dXxGcNbym5t77fzmawrBQfTmQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hookified": "^1.10.0",
+ "keyv": "^5.3.4"
+ }
+ },
+ "node_modules/cacheable/node_modules/keyv": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.3.4.tgz",
+ "integrity": "sha512-ypEvQvInNpUe+u+w8BIcPkQvEqXquyyibWE/1NB5T2BTzIpS5cGEV1LZskDzPSTvNAaT4+5FutvzlvnkxOSKlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@keyv/serialize": "^1.0.3"
+ }
+ },
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -6177,9 +4868,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001690",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz",
- "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==",
+ "version": "1.0.30001726",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz",
+ "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==",
"funding": [
{
"type": "opencollective",
@@ -6197,9 +4888,9 @@
"license": "CC-BY-4.0"
},
"node_modules/chai": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz",
- "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==",
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz",
+ "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6229,10 +4920,43 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
+ "node_modules/character-entities": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-reference-invalid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/chart.js": {
- "version": "4.4.7",
- "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.7.tgz",
- "integrity": "sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==",
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz",
+ "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==",
"license": "MIT",
"dependencies": {
"@kurkle/color": "^0.3.0"
@@ -6339,6 +5063,13 @@
"node": ">= 6"
}
},
+ "node_modules/chroma-js": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-3.1.2.tgz",
+ "integrity": "sha512-IJnETTalXbsLx1eKEgx19d5L6SRM7cH4vINw/99p/M11HCuXGRWL+6YmCm7FWFGIo6dtWuQoQi1dc5yQ7ESIHg==",
+ "dev": true,
+ "license": "(BSD-3-Clause AND Apache-2.0)"
+ },
"node_modules/chrome-trace-event": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
@@ -6349,9 +5080,9 @@
}
},
"node_modules/ci-info": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz",
- "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz",
+ "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==",
"dev": true,
"funding": [
{
@@ -6394,9 +5125,9 @@
}
},
"node_modules/clippie": {
- "version": "4.1.4",
- "resolved": "https://registry.npmjs.org/clippie/-/clippie-4.1.4.tgz",
- "integrity": "sha512-oTjh7B58LKvP8SAIVSQAUQ1QHpZhneOeEbVMORCcTK3/S78YUnppDC4DhuzNEt51oeeVVv8INv08Ua6FzyxYhA==",
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/clippie/-/clippie-4.1.7.tgz",
+ "integrity": "sha512-l8BmUmWqOt4mpxeSflcfODJJOU+DsE5atWj82k1zsxd2X82haz2+g12PJPMOt6e1Cs4/ZnOWdRAV+nY9n2o1Rg==",
"license": "BSD-2-Clause"
},
"node_modules/cliui": {
@@ -6456,9 +5187,9 @@
}
},
"node_modules/codemirror": {
- "version": "5.65.18",
- "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.18.tgz",
- "integrity": "sha512-Gaz4gHnkbHMGgahNt3CA5HBk5lLQBqmD/pBgeB4kQU6OedZmqMBjlRF0LSrp2tJ4wlLNPm2FfaUd1pDy0mdlpA==",
+ "version": "5.65.19",
+ "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.19.tgz",
+ "integrity": "sha512-+aFkvqhaAVr1gferNMuN8vkTSrWIFvzlMV9I2KBLCWS2WpZ2+UAkZjlMZmEuT+gcXTi6RrGQCkWq1/bDtGqhIA==",
"license": "MIT"
},
"node_modules/codemirror-spell-checker": {
@@ -6534,26 +5265,30 @@
"license": "MIT"
},
"node_modules/confbox": {
- "version": "0.1.8",
- "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
- "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz",
+ "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
"license": "MIT"
},
- "node_modules/convert-source-map": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
- "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
- "dev": true,
- "license": "MIT"
+ "node_modules/core-js": {
+ "version": "3.32.2",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.2.tgz",
+ "integrity": "sha512-pxXSw1mYZPDGvTQqEc5vgIb83jGQKFGYWY76z4a7weZXUolw3G+OvpZqSRcfYOoOVUQJYEPsWeQK8pKEnUtWxQ==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
},
"node_modules/core-js-compat": {
- "version": "3.39.0",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz",
- "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==",
+ "version": "3.43.0",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz",
+ "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "browserslist": "^4.24.2"
+ "browserslist": "^4.25.0"
},
"funding": {
"type": "opencollective",
@@ -6673,9 +5408,9 @@
}
},
"node_modules/css-select": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
- "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
+ "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -6704,9 +5439,9 @@
}
},
"node_modules/css-what": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
- "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
+ "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
@@ -6771,9 +5506,9 @@
"license": "MIT"
},
"node_modules/cytoscape": {
- "version": "3.30.4",
- "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.4.tgz",
- "integrity": "sha512-OxtlZwQl1WbwMmLiyPSEBuzeTIQnwZhJYYWFzZ2PhEHVFwpeaqNIkUzSiso00D98qk60l8Gwon2RP304d3BJ1A==",
+ "version": "3.32.0",
+ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.32.0.tgz",
+ "integrity": "sha512-5JHBC9n75kz5851jeklCPmZWcg3hUe6sjqJvyk3+hVqFaKcHwHgxsjeN1yLmggoUc6STbtm9/NQyabQehfjvWQ==",
"license": "MIT",
"engines": {
"node": ">=0.10"
@@ -7306,9 +6041,9 @@
"license": "MIT"
},
"node_modules/debug": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
- "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -7322,6 +6057,20 @@
}
}
},
+ "node_modules/decode-named-character-reference": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz",
+ "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "character-entities": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/decode-uri-component": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
@@ -7359,6 +6108,33 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/deep-rename-keys": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/deep-rename-keys/-/deep-rename-keys-0.2.1.tgz",
+ "integrity": "sha512-RHd9ABw4Fvk+gYDWqwOftG849x0bYOySl/RgX0tLI9i27ZIeSO91mLZJEp7oPHOMFqHvpgu21YptmDt0FYD/0A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "kind-of": "^3.0.2",
+ "rename-keys": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/deep-rename-keys/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/delaunator": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
@@ -7378,6 +6154,30 @@
"node": ">= 0.6.0"
}
},
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@@ -7417,9 +6217,9 @@
}
},
"node_modules/dom-input-range": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/dom-input-range/-/dom-input-range-1.2.0.tgz",
- "integrity": "sha512-8HVA5Oy5Vt872S7IXsjjp6/5Hqsm5YZLhurxwwQXp80T9qVsj8/mEUH3sQlFujLLUoWfxiaThHHuJ3/q1MHVuA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/dom-input-range/-/dom-input-range-2.0.1.tgz",
+ "integrity": "sha512-UMGnuQlEepIPCju5NrPe+8y52B+pRvSjIaSPW92KpukoDGSEVkI2iaCR4HxK7K6C7zmVsWE8PEjCYZaJMr3vpg==",
"license": "MIT",
"workspaces": [
"demos"
@@ -7470,18 +6270,18 @@
}
},
"node_modules/dompurify": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.3.tgz",
- "integrity": "sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA==",
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz",
+ "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
}
},
"node_modules/domutils": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
- "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
+ "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -7510,22 +6310,22 @@
"license": "MIT"
},
"node_modules/easymde": {
- "version": "2.18.0",
- "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.18.0.tgz",
- "integrity": "sha512-IxVVUxNWIoXLeqtBU4BLc+eS/ScYhT1Dcb6yF5Wchoj1iXAV+TIIDWx+NCaZhY7RcSHqDPKllbYq7nwGKILnoA==",
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.20.0.tgz",
+ "integrity": "sha512-V1Z5f92TfR42Na852OWnIZMbM7zotWQYTddNaLYZFVKj7APBbyZ3FYJ27gBw2grMW3R6Qdv9J8n5Ij7XRSIgXQ==",
"license": "MIT",
"dependencies": {
- "@types/codemirror": "^5.60.4",
+ "@types/codemirror": "^5.60.10",
"@types/marked": "^4.0.7",
- "codemirror": "^5.63.1",
+ "codemirror": "^5.65.15",
"codemirror-spell-checker": "1.1.2",
"marked": "^4.1.0"
}
},
"node_modules/electron-to-chromium": {
- "version": "1.5.74",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.74.tgz",
- "integrity": "sha512-ck3//9RC+6oss/1Bh9tiAVFy5vfSKbRHAFh7Z3/eTRkEqJeWgymloShB17Vg3Z4nmDNp35vAd1BZ6CMW4Wt6Iw==",
+ "version": "1.5.179",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.179.tgz",
+ "integrity": "sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==",
"license": "ISC"
},
"node_modules/emoji-regex": {
@@ -7544,9 +6344,9 @@
}
},
"node_modules/enhanced-resolve": {
- "version": "5.17.1",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
- "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
+ "version": "5.18.2",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz",
+ "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
@@ -7600,69 +6400,71 @@
},
"node_modules/es-aggregate-error": {
"name": "@nolyfill/es-aggregate-error",
- "version": "1.0.28",
- "resolved": "https://registry.npmjs.org/@nolyfill/es-aggregate-error/-/es-aggregate-error-1.0.28.tgz",
- "integrity": "sha512-rznCu74ormHtT2JkCgARsrWRAMDhkvS3PM+vz1BXKY0IsrXfwtxM0PlxfDexvCQ1tf/kq69Dfp2No5hJ1WAJVQ==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/es-aggregate-error/-/es-aggregate-error-1.0.44.tgz",
+ "integrity": "sha512-NBXDS4gpOiK4csFDGjqAF0WVKAMsqq/2ZU+/z+q+Mmvpr6CIrwKC/X+EbMpalEOZYT8kSTaCMVDUcXTDeMYOLw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@nolyfill/shared": "1.0.28"
+ "@nolyfill/shared": "1.0.44"
},
"engines": {
"node": ">=12.4.0"
}
},
"node_modules/es-module-lexer": {
- "version": "1.5.4",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz",
- "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==",
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+ "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
"license": "MIT"
},
"node_modules/esbuild": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
- "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "version": "0.25.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
+ "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
- "node": ">=12"
+ "node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.21.5",
- "@esbuild/android-arm": "0.21.5",
- "@esbuild/android-arm64": "0.21.5",
- "@esbuild/android-x64": "0.21.5",
- "@esbuild/darwin-arm64": "0.21.5",
- "@esbuild/darwin-x64": "0.21.5",
- "@esbuild/freebsd-arm64": "0.21.5",
- "@esbuild/freebsd-x64": "0.21.5",
- "@esbuild/linux-arm": "0.21.5",
- "@esbuild/linux-arm64": "0.21.5",
- "@esbuild/linux-ia32": "0.21.5",
- "@esbuild/linux-loong64": "0.21.5",
- "@esbuild/linux-mips64el": "0.21.5",
- "@esbuild/linux-ppc64": "0.21.5",
- "@esbuild/linux-riscv64": "0.21.5",
- "@esbuild/linux-s390x": "0.21.5",
- "@esbuild/linux-x64": "0.21.5",
- "@esbuild/netbsd-x64": "0.21.5",
- "@esbuild/openbsd-x64": "0.21.5",
- "@esbuild/sunos-x64": "0.21.5",
- "@esbuild/win32-arm64": "0.21.5",
- "@esbuild/win32-ia32": "0.21.5",
- "@esbuild/win32-x64": "0.21.5"
+ "@esbuild/aix-ppc64": "0.25.5",
+ "@esbuild/android-arm": "0.25.5",
+ "@esbuild/android-arm64": "0.25.5",
+ "@esbuild/android-x64": "0.25.5",
+ "@esbuild/darwin-arm64": "0.25.5",
+ "@esbuild/darwin-x64": "0.25.5",
+ "@esbuild/freebsd-arm64": "0.25.5",
+ "@esbuild/freebsd-x64": "0.25.5",
+ "@esbuild/linux-arm": "0.25.5",
+ "@esbuild/linux-arm64": "0.25.5",
+ "@esbuild/linux-ia32": "0.25.5",
+ "@esbuild/linux-loong64": "0.25.5",
+ "@esbuild/linux-mips64el": "0.25.5",
+ "@esbuild/linux-ppc64": "0.25.5",
+ "@esbuild/linux-riscv64": "0.25.5",
+ "@esbuild/linux-s390x": "0.25.5",
+ "@esbuild/linux-x64": "0.25.5",
+ "@esbuild/netbsd-arm64": "0.25.5",
+ "@esbuild/netbsd-x64": "0.25.5",
+ "@esbuild/openbsd-arm64": "0.25.5",
+ "@esbuild/openbsd-x64": "0.25.5",
+ "@esbuild/sunos-x64": "0.25.5",
+ "@esbuild/win32-arm64": "0.25.5",
+ "@esbuild/win32-ia32": "0.25.5",
+ "@esbuild/win32-x64": "0.25.5"
}
},
"node_modules/esbuild-loader": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-4.2.2.tgz",
- "integrity": "sha512-Mdq/A1L8p37hkibp8jGFwuQTDSWhDmlueAefsrCPRwNWThEOlQmIglV7Gd6GE2mO5bt7ksfxKOMwkuY7jjVTXg==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-4.3.0.tgz",
+ "integrity": "sha512-D7HeJNdkDKKMarPQO/3dlJT6RwN2YJO7ENU6RPlpOz5YxSHnUNi2yvW41Bckvi1EVwctIaLzlb0ni5ag2GINYA==",
"license": "MIT",
"dependencies": {
- "esbuild": "^0.21.0",
+ "esbuild": "^0.25.0",
"get-tsconfig": "^4.7.0",
"loader-utils": "^2.0.4",
"webpack-sources": "^1.4.3"
@@ -7683,18 +6485,6 @@
"node": ">=6"
}
},
- "node_modules/escape-goat": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz",
- "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -7766,9 +6556,9 @@
}
},
"node_modules/eslint-compat-utils": {
- "version": "0.6.4",
- "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.6.4.tgz",
- "integrity": "sha512-/u+GQt8NMfXO8w17QendT4gvO5acfxQsAKirAt0LVxDnr2N8YLCVbregaNc/Yhp7NM128DwCaRvr8PLDfeNkQw==",
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.6.5.tgz",
+ "integrity": "sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7782,18 +6572,46 @@
}
},
"node_modules/eslint-config-prettier": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
- "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz",
+ "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==",
"dev": true,
"license": "MIT",
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
+ "funding": {
+ "url": "https://opencollective.com/eslint-config-prettier"
+ },
"peerDependencies": {
"eslint": ">=7.0.0"
}
},
+ "node_modules/eslint-import-context": {
+ "version": "0.1.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.9.tgz",
+ "integrity": "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-tsconfig": "^4.10.1",
+ "stable-hash-x": "^0.2.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-import-context"
+ },
+ "peerDependencies": {
+ "unrs-resolver": "^1.0.0"
+ },
+ "peerDependenciesMeta": {
+ "unrs-resolver": {
+ "optional": true
+ }
+ }
+ },
"node_modules/eslint-import-resolver-node": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
@@ -7817,26 +6635,25 @@
}
},
"node_modules/eslint-import-resolver-typescript": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.7.0.tgz",
- "integrity": "sha512-Vrwyi8HHxY97K5ebydMtffsWAn1SCR9eol49eCd5fJS4O1WV7PaAjbcjmbfJJSMz/t4Mal212Uz/fQZrOB8mow==",
+ "version": "4.4.4",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.4.tgz",
+ "integrity": "sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==",
"dev": true,
"license": "ISC",
"dependencies": {
- "@nolyfill/is-core-module": "1.0.39",
- "debug": "^4.3.7",
- "enhanced-resolve": "^5.15.0",
- "fast-glob": "^3.3.2",
- "get-tsconfig": "^4.7.5",
- "is-bun-module": "^1.0.2",
- "is-glob": "^4.0.3",
- "stable-hash": "^0.0.4"
+ "debug": "^4.4.1",
+ "eslint-import-context": "^0.1.8",
+ "get-tsconfig": "^4.10.1",
+ "is-bun-module": "^2.0.0",
+ "stable-hash-x": "^0.2.0",
+ "tinyglobby": "^0.2.14",
+ "unrs-resolver": "^1.7.11"
},
"engines": {
- "node": "^14.18.0 || >=16.0.0"
+ "node": "^16.17.0 || >=18.6.0"
},
"funding": {
- "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts"
+ "url": "https://opencollective.com/eslint-import-resolver-typescript"
},
"peerDependencies": {
"eslint": "*",
@@ -7853,9 +6670,9 @@
}
},
"node_modules/eslint-module-utils": {
- "version": "2.12.0",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz",
- "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==",
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
+ "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7995,30 +6812,30 @@
}
},
"node_modules/eslint-plugin-import": {
- "version": "2.31.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz",
- "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
+ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rtsao/scc": "^1.1.0",
- "array-includes": "^3.1.8",
- "array.prototype.findlastindex": "^1.2.5",
- "array.prototype.flat": "^1.3.2",
- "array.prototype.flatmap": "^1.3.2",
+ "array-includes": "^3.1.9",
+ "array.prototype.findlastindex": "^1.2.6",
+ "array.prototype.flat": "^1.3.3",
+ "array.prototype.flatmap": "^1.3.3",
"debug": "^3.2.7",
"doctrine": "^2.1.0",
"eslint-import-resolver-node": "^0.3.9",
- "eslint-module-utils": "^2.12.0",
+ "eslint-module-utils": "^2.12.1",
"hasown": "^2.0.2",
- "is-core-module": "^2.15.1",
+ "is-core-module": "^2.16.1",
"is-glob": "^4.0.3",
"minimatch": "^3.1.2",
"object.fromentries": "^2.0.8",
"object.groupby": "^1.0.3",
- "object.values": "^1.2.0",
+ "object.values": "^1.2.1",
"semver": "^6.3.1",
- "string.prototype.trimend": "^1.0.8",
+ "string.prototype.trimend": "^1.0.9",
"tsconfig-paths": "^3.15.0"
},
"engines": {
@@ -8029,58 +6846,40 @@
}
},
"node_modules/eslint-plugin-import-x": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.6.1.tgz",
- "integrity": "sha512-wluSUifMIb7UfwWXqx7Yx0lE/SGCcGXECLx/9bCmbY2nneLwvAZ4vkd1IXDjPKFvdcdUgr1BaRnaRpx3k2+Pfw==",
+ "version": "4.16.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.16.1.tgz",
+ "integrity": "sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@types/doctrine": "^0.0.9",
- "@typescript-eslint/scope-manager": "^8.1.0",
- "@typescript-eslint/utils": "^8.1.0",
- "debug": "^4.3.4",
- "doctrine": "^3.0.0",
- "enhanced-resolve": "^5.17.1",
- "eslint-import-resolver-node": "^0.3.9",
- "get-tsconfig": "^4.7.3",
+ "@typescript-eslint/types": "^8.35.0",
+ "comment-parser": "^1.4.1",
+ "debug": "^4.4.1",
+ "eslint-import-context": "^0.1.9",
"is-glob": "^4.0.3",
- "minimatch": "^9.0.3",
- "semver": "^7.6.3",
- "stable-hash": "^0.0.4",
- "tslib": "^2.6.3"
+ "minimatch": "^9.0.3 || ^10.0.1",
+ "semver": "^7.7.2",
+ "stable-hash-x": "^0.2.0",
+ "unrs-resolver": "^1.9.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0"
- }
- },
- "node_modules/eslint-plugin-import-x/node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
+ "funding": {
+ "url": "https://opencollective.com/eslint-plugin-import-x"
},
- "engines": {
- "node": ">=16 || 14 >=14.17"
+ "peerDependencies": {
+ "@typescript-eslint/utils": "^8.0.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "eslint-import-resolver-node": "*"
},
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/eslint-plugin-import/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
+ "peerDependenciesMeta": {
+ "@typescript-eslint/utils": {
+ "optional": true
+ },
+ "eslint-import-resolver-node": {
+ "optional": true
+ }
}
},
"node_modules/eslint-plugin-import/node_modules/debug": {
@@ -8159,17 +6958,6 @@
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9"
}
},
- "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -8184,9 +6972,9 @@
}
},
"node_modules/eslint-plugin-no-jquery": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-3.1.0.tgz",
- "integrity": "sha512-Ze+eRlAbLAoceBqMXI2E9s6o3dC7zE75niP2Sy4D8I/u1TyLegrIpjc4emPN90dH0IA+uXNUmQbzBuCaihxwIQ==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-3.1.1.tgz",
+ "integrity": "sha512-LTLO3jH/Tjr1pmxCEqtV6qmt+OChv8La4fwgG470JRpgxyFF4NOzoC9CRy92GIWD3Yjl0qLEgPmD2FLQWcNEjg==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -8220,9 +7008,9 @@
}
},
"node_modules/eslint-plugin-playwright": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.1.0.tgz",
- "integrity": "sha512-wMbHOehofSB1cBdzz2CLaCYaKNLeTQ0YnOW+7AHa281TJqlpEJUBgTHbRUYOUxiXphfWwOyTPvgr6vvEmArbSA==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.2.0.tgz",
+ "integrity": "sha512-qSQpAw7RcSzE3zPp8FMGkthaCWovHZ/BsXtpmnGax9vQLIovlh1bsZHEa2+j2lv9DWhnyeLM/qZmp7ffQZfQvg==",
"dev": true,
"license": "MIT",
"workspaces": [
@@ -8239,14 +7027,14 @@
}
},
"node_modules/eslint-plugin-prettier": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz",
- "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==",
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz",
+ "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==",
"dev": true,
"license": "MIT",
"dependencies": {
"prettier-linter-helpers": "^1.0.0",
- "synckit": "^0.9.1"
+ "synckit": "^0.11.7"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
@@ -8257,7 +7045,7 @@
"peerDependencies": {
"@types/eslint": ">=8.0.0",
"eslint": ">=8.0.0",
- "eslint-config-prettier": "*",
+ "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0",
"prettier": ">=3.0.0"
},
"peerDependenciesMeta": {
@@ -8270,9 +7058,9 @@
}
},
"node_modules/eslint-plugin-regexp": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.7.0.tgz",
- "integrity": "sha512-U8oZI77SBtH8U3ulZ05iu0qEzIizyEDXd+BWHvyVxTOjGwcDcvy/kEpgFG4DYca2ByRLiVPFZ2GeH7j1pdvZTA==",
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.9.0.tgz",
+ "integrity": "sha512-9WqJMnOq8VlE/cK+YAo9C9YHhkOtcEtEk9d12a+H7OSZFwlpI6stiHmYPGa2VE0QhTzodJyhlyprUaXDZLgHBw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8292,32 +7080,44 @@
}
},
"node_modules/eslint-plugin-sonarjs": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-3.0.1.tgz",
- "integrity": "sha512-RT6VgdPqizbMLmTryIc3fB169hRjvDFlqieSZEEswGtApPb4Dn9BndmN9qyfBV/By0hbseIX8zQWKBz5E7lyiQ==",
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-3.0.4.tgz",
+ "integrity": "sha512-ftQcP811kRJNXapqpQXHErEoVOdTPfYPPYd7n3AExIPwv4qWKKHf4slFvXmodiOnfgy1Tl3waPZZLD7lcvJOtw==",
"dev": true,
"license": "LGPL-3.0-only",
"dependencies": {
- "@babel/core": "7.26.0",
- "@babel/eslint-parser": "7.25.9",
- "@babel/plugin-proposal-decorators": "7.25.9",
- "@babel/preset-env": "7.26.0",
- "@babel/preset-flow": "7.25.9",
- "@babel/preset-react": "7.26.3",
"@eslint-community/regexpp": "4.12.1",
"builtin-modules": "3.3.0",
"bytes": "3.1.2",
"functional-red-black-tree": "1.0.1",
"jsx-ast-utils": "3.3.5",
+ "lodash.merge": "4.6.2",
"minimatch": "9.0.5",
"scslre": "0.3.0",
- "semver": "7.6.3",
- "typescript": "^5"
+ "semver": "7.7.2",
+ "typescript": ">=5"
},
"peerDependencies": {
"eslint": "^8.0.0 || ^9.0.0"
}
},
+ "node_modules/eslint-plugin-sonarjs/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint-plugin-sonarjs/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
"node_modules/eslint-plugin-sonarjs/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
@@ -8369,9 +7169,9 @@
}
},
"node_modules/eslint-plugin-unicorn/node_modules/globals": {
- "version": "15.14.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz",
- "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==",
+ "version": "15.15.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+ "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8381,206 +7181,48 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/eslint-plugin-vitest": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.4.1.tgz",
- "integrity": "sha512-+PnZ2u/BS+f5FiuHXz4zKsHPcMKHie+K+1Uvu/x91ovkCMEOJqEI8E9Tw1Wzx2QRz4MHOBHYf1ypO8N1K0aNAA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/utils": "^7.4.0"
- },
- "engines": {
- "node": "^18.0.0 || >= 20.0.0"
- },
- "peerDependencies": {
- "eslint": ">=8.0.0",
- "vitest": "*"
- },
- "peerDependenciesMeta": {
- "@typescript-eslint/eslint-plugin": {
- "optional": true
- },
- "vitest": {
- "optional": true
- }
- }
- },
- "node_modules/eslint-plugin-vitest-globals": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-vitest-globals/-/eslint-plugin-vitest-globals-1.5.0.tgz",
- "integrity": "sha512-ZSsVOaOIig0oVLzRTyk8lUfBfqzWxr/J3/NFMfGGRIkGQPejJYmDH3gXmSJxAojts77uzAGB/UmVrwi2DC4LYA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/scope-manager": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz",
- "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "7.18.0",
- "@typescript-eslint/visitor-keys": "7.18.0"
- },
- "engines": {
- "node": "^18.18.0 || >=20.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/types": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
- "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || >=20.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/typescript-estree": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz",
- "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "@typescript-eslint/types": "7.18.0",
- "@typescript-eslint/visitor-keys": "7.18.0",
- "debug": "^4.3.4",
- "globby": "^11.1.0",
- "is-glob": "^4.0.3",
- "minimatch": "^9.0.4",
- "semver": "^7.6.0",
- "ts-api-utils": "^1.3.0"
- },
- "engines": {
- "node": "^18.18.0 || >=20.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
- "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/utils": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz",
- "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "7.18.0",
- "@typescript-eslint/types": "7.18.0",
- "@typescript-eslint/typescript-estree": "7.18.0"
- },
- "engines": {
- "node": "^18.18.0 || >=20.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.56.0"
- }
- },
- "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/visitor-keys": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz",
- "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "7.18.0",
- "eslint-visitor-keys": "^3.4.3"
- },
- "engines": {
- "node": "^18.18.0 || >=20.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/eslint-plugin-vitest/node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint-plugin-vitest/node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/eslint-plugin-vue": {
- "version": "9.32.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.32.0.tgz",
- "integrity": "sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug==",
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.3.0.tgz",
+ "integrity": "sha512-A0u9snqjCfYaPnqqOaH6MBLVWDUIN4trXn8J3x67uDcXvR7X6Ut8p16N+nYhMCQ9Y7edg2BIRGzfyZsY0IdqoQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
- "globals": "^13.24.0",
"natural-compare": "^1.4.0",
"nth-check": "^2.1.1",
"postcss-selector-parser": "^6.0.15",
"semver": "^7.6.3",
- "vue-eslint-parser": "^9.4.3",
"xml-name-validator": "^4.0.0"
},
"engines": {
- "node": "^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"peerDependencies": {
- "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0"
+ "@typescript-eslint/parser": "^7.0.0 || ^8.0.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "vue-eslint-parser": "^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@typescript-eslint/parser": {
+ "optional": true
+ }
}
},
"node_modules/eslint-plugin-vue-scoped-css": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-vue-scoped-css/-/eslint-plugin-vue-scoped-css-2.9.0.tgz",
- "integrity": "sha512-zXeKtEUpfk3PlsgKnr9/2U8K2xcsCV1M9hXWRhKbl3wipVowGXfHrhqUzHFVWNAHzEQv0DCDXGFWrmsGFqhGGA==",
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-vue-scoped-css/-/eslint-plugin-vue-scoped-css-2.11.0.tgz",
+ "integrity": "sha512-rrJgLY8iroTIUMSyxhyhJzFcRxABbk3gFrOLkl41F9G1VBqNNpDShyf6PmDoBEWDk07/bJlnqYlvnQ3giUrRYQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
- "eslint-compat-utils": "^0.6.0",
+ "eslint-compat-utils": "^0.6.5",
"lodash": "^4.17.21",
"postcss": "^8.4.31",
"postcss-safe-parser": "^6.0.0",
"postcss-scss": "^4.0.3",
- "postcss-selector-parser": "^6.0.9",
+ "postcss-selector-parser": "^7.0.0",
"postcss-styl": "^0.12.0"
},
"engines": {
@@ -8594,15 +7236,29 @@
"vue-eslint-parser": ">=7.1.0"
}
},
+ "node_modules/eslint-plugin-vue-scoped-css/node_modules/postcss-selector-parser": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
+ "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/eslint-plugin-wc": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.2.0.tgz",
- "integrity": "sha512-kjPp+aXz23fOl0JZJOJS+6adwhEv98KjZ2FJqWpc4vtmk4Oenz/JJmmNZrGSARgtyR0BLIF/kVWC6GSlHA+5MA==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-3.0.1.tgz",
+ "integrity": "sha512-0p1wkSlA2Ue3FA4qW+5LZ+15sy0p1nUyVl1eyBMLq4rtN1LtE9IdI49BXNWMz8N8bM/y7Ulx8SWGAni5f8XO5g==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-valid-element-name": "^1.0.0",
- "js-levenshtein-esm": "^1.2.0"
+ "js-levenshtein-esm": "^2.0.0"
},
"peerDependencies": {
"eslint": ">=8.40.0"
@@ -8636,9 +7292,9 @@
}
},
"node_modules/eslint-visitor-keys": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
- "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -8665,17 +7321,6 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
- "node_modules/eslint/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/eslint/node_modules/eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
@@ -8728,15 +7373,15 @@
}
},
"node_modules/espree": {
- "version": "10.3.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
- "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
- "acorn": "^8.14.0",
+ "acorn": "^8.15.0",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.2.0"
+ "eslint-visitor-keys": "^4.2.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -8805,6 +7450,13 @@
"node": ">=6"
}
},
+ "node_modules/eventemitter3": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
+ "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
@@ -8815,15 +7467,21 @@
}
},
"node_modules/expect-type": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz",
- "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz",
+ "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=12.0.0"
}
},
+ "node_modules/exsolve": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz",
+ "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==",
+ "license": "MIT"
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -8838,16 +7496,16 @@
"license": "Apache-2.0"
},
"node_modules/fast-glob": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
- "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
- "micromatch": "^4.0.4"
+ "micromatch": "^4.0.8"
},
"engines": {
"node": ">=8.6.0"
@@ -8869,6 +7527,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/fast-levenshtein": {
@@ -8886,9 +7545,19 @@
"license": "MIT"
},
"node_modules/fast-uri": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz",
- "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==",
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
+ "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
"license": "BSD-3-Clause"
},
"node_modules/fastest-levenshtein": {
@@ -8901,9 +7570,9 @@
}
},
"node_modules/fastq": {
- "version": "1.17.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
- "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
"license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
@@ -8938,6 +7607,12 @@
}
}
},
+ "node_modules/fflate": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
+ "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
+ "license": "MIT"
+ },
"node_modules/file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -9005,19 +7680,19 @@
}
},
"node_modules/flatted": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
- "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
"dev": true,
"license": "ISC"
},
"node_modules/foreground-child": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
- "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
"license": "ISC",
"dependencies": {
- "cross-spawn": "^7.0.0",
+ "cross-spawn": "^7.0.6",
"signal-exit": "^4.0.1"
},
"engines": {
@@ -9069,16 +7744,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/gensync": {
- "version": "1.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@@ -9123,9 +7788,9 @@
}
},
"node_modules/get-tsconfig": {
- "version": "4.8.1",
- "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz",
- "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==",
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
+ "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==",
"license": "MIT",
"dependencies": {
"resolve-pkg-maps": "^1.0.0"
@@ -9173,16 +7838,6 @@
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
"license": "BSD-2-Clause"
},
- "node_modules/glob/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/glob/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -9329,20 +7984,37 @@
}
},
"node_modules/happy-dom": {
- "version": "15.11.7",
- "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-15.11.7.tgz",
- "integrity": "sha512-KyrFvnl+J9US63TEzwoiJOQzZBJY7KgBushJA8X61DMbNsH+2ONkDuLDnCnwUiPTF42tLoEmrPyoqbenVA5zrg==",
+ "version": "18.0.1",
+ "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-18.0.1.tgz",
+ "integrity": "sha512-qn+rKOW7KWpVTtgIUi6RVmTBZJSe2k0Db0vh1f7CWrWclkkc7/Q+FrOfkZIb2eiErLyqu5AXEzE7XthO9JVxRA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "entities": "^4.5.0",
- "webidl-conversions": "^7.0.0",
+ "@types/node": "^20.0.0",
+ "@types/whatwg-mimetype": "^3.0.2",
"whatwg-mimetype": "^3.0.0"
},
"engines": {
- "node": ">=18.0.0"
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/happy-dom/node_modules/@types/node": {
+ "version": "20.19.4",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.4.tgz",
+ "integrity": "sha512-OP+We5WV8Xnbuvw0zC2m4qfB/BJvjyCwtNjhHdJxV1639SGSKrLmJkc3fMnp2Qy8nJyHp8RO6umxELN/dS1/EA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
}
},
+ "node_modules/happy-dom/node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -9360,9 +8032,9 @@
},
"node_modules/hasown": {
"name": "@nolyfill/hasown",
- "version": "1.0.29",
- "resolved": "https://registry.npmjs.org/@nolyfill/hasown/-/hasown-1.0.29.tgz",
- "integrity": "sha512-9h/nxZqmCy26r9VXGUz+Q77vq3eINXOYgE4st3dj6DoE7tulfJueCLw5d4hfDy3S8mKg4cFXaP+KxYQ+txvMzw==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/hasown/-/hasown-1.0.44.tgz",
+ "integrity": "sha512-GA/21lkTr2PAQuT6jGnhLuBD5IFd/AEhBXJ/tf33+/bVxPxg+5ejKx9jGQGnyV/P0eSmdup5E+s8b2HL6lOrwQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -9379,6 +8051,13 @@
"he": "bin/he"
}
},
+ "node_modules/hookified": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.10.0.tgz",
+ "integrity": "sha512-dJw0492Iddsj56U1JsSTm9E/0B/29a1AuoSLRAte8vQg/kaTGF3IgjEWT8c8yG4cC10+HisE1x5QAwR0Xwc+DA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
@@ -9430,9 +8109,9 @@
}
},
"node_modules/htmx.org": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.4.tgz",
- "integrity": "sha512-HLxMCdfXDOJirs3vBZl/ZLoY+c7PfM4Ahr2Ad4YXh6d22T5ltbTXFFkpx9Tgb2vvmWFMbIc3LqN2ToNkZJvyYQ==",
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.6.tgz",
+ "integrity": "sha512-7ythjYneGSk3yCHgtCnQeaoF+D+o7U2LF37WU3O0JYv3gTZSicdEFiI/Ai/NJyC5ZpYJWMpUb11OC5Lr6AfAqA==",
"license": "0BSD"
},
"node_modules/iconv-lite": {
@@ -9460,10 +8139,10 @@
}
},
"node_modules/idiomorph": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/idiomorph/-/idiomorph-0.3.0.tgz",
- "integrity": "sha512-UhV1Ey5xCxIwR9B+OgIjQa+1Jx99XQ1vQHUsKBU1RpQzCx1u+b+N6SOXgf5mEJDqemUI/ffccu6+71l2mJUsRA==",
- "license": "BSD 2-Clause"
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/idiomorph/-/idiomorph-0.7.3.tgz",
+ "integrity": "sha512-YI/L1QQkBRDtiaGZN+/KolIkNoZuIsCgus1ciueosiqdyWz/weeP+ghFgDQpk2vzA3BkX6/M/kY5SCM03LBDkA==",
+ "license": "0BSD"
},
"node_modules/ieee754": {
"version": "1.2.1",
@@ -9507,9 +8186,9 @@
}
},
"node_modules/import-fresh": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
- "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
"license": "MIT",
"dependencies": {
"parent-module": "^1.0.0",
@@ -9606,6 +8285,32 @@
"node": ">=10.13.0"
}
},
+ "node_modules/is-alphabetical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -9624,6 +8329,13 @@
"node": ">=8"
}
},
+ "node_modules/is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/is-builtin-module": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
@@ -9641,13 +8353,13 @@
}
},
"node_modules/is-bun-module": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.3.0.tgz",
- "integrity": "sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
+ "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "semver": "^7.6.3"
+ "semver": "^7.7.1"
}
},
"node_modules/is-core-module": {
@@ -9660,6 +8372,17 @@
"node": ">=12.4.0"
}
},
+ "node_modules/is-decimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -9701,6 +8424,17 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-hexadecimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/is-js-type": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-js-type/-/is-js-type-2.0.0.tgz",
@@ -9805,9 +8539,9 @@
}
},
"node_modules/jackspeak": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz",
- "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz",
+ "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
@@ -9865,16 +8599,17 @@
"license": "MIT"
},
"node_modules/js-levenshtein-esm": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz",
- "integrity": "sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-2.0.0.tgz",
+ "integrity": "sha512-1n4LEPOL4wRXY8rOQcuA7Iuaphe5xCMayvufCzlLAi+hRsnBRDbSS6XPuV58CBVJxj5D9ApFLyjQ7KzFToyHBw==",
"dev": true,
"license": "MIT"
},
"node_modules/js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
+ "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
+ "dev": true,
"license": "MIT"
},
"node_modules/js-types": {
@@ -9991,9 +8726,9 @@
}
},
"node_modules/jsonpath-plus": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.2.0.tgz",
- "integrity": "sha512-T9V+8iNYKFL2n2rF+w02LBOT2JjDnTjioaNFrxRy0Bv1y/hNsqR/EBK7Ojy2ythRHwmz2cRIls+9JitQGZC/sw==",
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz",
+ "integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10042,9 +8777,9 @@
"license": "MIT"
},
"node_modules/katex": {
- "version": "0.16.18",
- "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.18.tgz",
- "integrity": "sha512-LRuk0rPdXrecAFwQucYjMiIs0JFefk6N1q/04mlw14aVIVgxq1FO0MA9RiIIGVaKOB5GIP5GH4aBBNraZERmaQ==",
+ "version": "0.16.22",
+ "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.22.tgz",
+ "integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==",
"funding": [
"https://opencollective.com/katex",
"https://github.com/sponsors/katex"
@@ -10082,9 +8817,9 @@
}
},
"node_modules/known-css-properties": {
- "version": "0.35.0",
- "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.35.0.tgz",
- "integrity": "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==",
+ "version": "0.37.0",
+ "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.37.0.tgz",
+ "integrity": "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==",
"dev": true,
"license": "MIT"
},
@@ -10095,9 +8830,9 @@
"license": "MIT"
},
"node_modules/langium": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/langium/-/langium-3.0.0.tgz",
- "integrity": "sha512-+Ez9EoiByeoTu/2BXmEaZ06iPNXM6thWJp02KfBO/raSMyCJ4jw7AkWWa+zBCTm0+Tw1Fj9FOxdqSskyN5nAwg==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz",
+ "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==",
"license": "MIT",
"dependencies": {
"chevrotain": "~11.0.3",
@@ -10180,16 +8915,6 @@
"webpack": "^4.4.0 || ^5.4.0"
}
},
- "node_modules/license-checker-webpack-plugin/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/license-checker-webpack-plugin/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -10277,13 +9002,14 @@
}
},
"node_modules/local-pkg": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz",
- "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz",
+ "integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==",
"license": "MIT",
"dependencies": {
- "mlly": "^1.7.3",
- "pkg-types": "^1.2.1"
+ "mlly": "^1.7.4",
+ "pkg-types": "^2.0.1",
+ "quansync": "^0.2.8"
},
"engines": {
"node": ">=14"
@@ -10334,13 +9060,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/lodash.debounce": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
- "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/lodash.kebabcase": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
@@ -10373,6 +9092,7 @@
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
"integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
+ "deprecated": "This package is deprecated. Use https://socket.dev/npm/package/eta instead.",
"license": "MIT",
"dependencies": {
"lodash._reinterpolate": "^3.0.0",
@@ -10410,9 +9130,9 @@
"license": "MIT"
},
"node_modules/loupe": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz",
- "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==",
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz",
+ "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==",
"dev": true,
"license": "MIT"
},
@@ -10427,13 +9147,13 @@
}
},
"node_modules/lru-cache": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
- "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz",
+ "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==",
"dev": true,
"license": "ISC",
- "dependencies": {
- "yallist": "^3.0.2"
+ "engines": {
+ "node": "20 || >=22"
}
},
"node_modules/magic-string": {
@@ -10472,51 +9192,58 @@
}
},
"node_modules/markdownlint": {
- "version": "0.36.1",
- "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.36.1.tgz",
- "integrity": "sha512-s73fU2CQN7WCgjhaQUQ8wYESQNzGRNOKDd+3xgVqu8kuTEhmwepd/mxOv1LR2oV046ONrTLBFsM7IoKWNvmy5g==",
+ "version": "0.38.0",
+ "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.38.0.tgz",
+ "integrity": "sha512-xaSxkaU7wY/0852zGApM8LdlIfGCW8ETZ0Rr62IQtAnUMlMuifsg09vWJcNYeL4f0anvr8Vo4ZQar8jGpV0btQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "markdown-it": "14.1.0",
- "markdownlint-micromark": "0.1.12"
+ "micromark": "4.0.2",
+ "micromark-core-commonmark": "2.0.3",
+ "micromark-extension-directive": "4.0.0",
+ "micromark-extension-gfm-autolink-literal": "2.1.0",
+ "micromark-extension-gfm-footnote": "2.1.0",
+ "micromark-extension-gfm-table": "2.1.1",
+ "micromark-extension-math": "3.1.0",
+ "micromark-util-types": "2.0.2"
},
"engines": {
- "node": ">=18"
+ "node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/DavidAnson"
}
},
"node_modules/markdownlint-cli": {
- "version": "0.43.0",
- "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.43.0.tgz",
- "integrity": "sha512-6vwurKK4B21eyYzwgX6ph13cZS7hE6LZfcS8QyD722CyxVD2RtAvbZK2p7k+FZbbKORulEuwl+hJaEq1l6/hoQ==",
+ "version": "0.45.0",
+ "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.45.0.tgz",
+ "integrity": "sha512-GiWr7GfJLVfcopL3t3pLumXCYs8sgWppjIA1F/Cc3zIMgD3tmkpyZ1xkm1Tej8mw53B93JsDjgA3KOftuYcfOw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "commander": "~12.1.0",
- "glob": "~11.0.0",
- "ignore": "~6.0.2",
- "js-yaml": "^4.1.0",
+ "commander": "~13.1.0",
+ "glob": "~11.0.2",
+ "ignore": "~7.0.4",
+ "js-yaml": "~4.1.0",
"jsonc-parser": "~3.3.1",
- "jsonpointer": "5.0.1",
- "markdownlint": "~0.36.1",
+ "jsonpointer": "~5.0.1",
+ "markdown-it": "~14.1.0",
+ "markdownlint": "~0.38.0",
"minimatch": "~10.0.1",
"run-con": "~1.3.2",
- "smol-toml": "~1.3.1"
+ "smol-toml": "~1.3.4"
},
"bin": {
"markdownlint": "markdownlint.js"
},
"engines": {
- "node": ">=18"
+ "node": ">=20"
}
},
"node_modules/markdownlint-cli/node_modules/commander": {
- "version": "12.1.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
- "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "version": "13.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
+ "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -10524,15 +9251,15 @@
}
},
"node_modules/markdownlint-cli/node_modules/glob": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz",
- "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==",
+ "version": "11.0.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz",
+ "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==",
"dev": true,
"license": "ISC",
"dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^4.0.1",
- "minimatch": "^10.0.0",
+ "foreground-child": "^3.3.1",
+ "jackspeak": "^4.1.1",
+ "minimatch": "^10.0.3",
"minipass": "^7.1.2",
"package-json-from-dist": "^1.0.0",
"path-scurry": "^2.0.0"
@@ -10548,9 +9275,9 @@
}
},
"node_modules/markdownlint-cli/node_modules/ignore": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-6.0.2.tgz",
- "integrity": "sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==",
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -10564,19 +9291,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/markdownlint-micromark": {
- "version": "0.1.12",
- "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.12.tgz",
- "integrity": "sha512-RlB6EwMGgc0sxcIhOQ2+aq7Zw1V2fBnzbXKGgYK/mVWdT7cz34fteKSwfYeo4rL6+L/q2tyC9QtD/PgZbkdyJQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/DavidAnson"
- }
- },
"node_modules/marked": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
@@ -10589,6 +9303,25 @@
"node": ">= 12"
}
},
+ "node_modules/material-icon-theme": {
+ "version": "5.24.0",
+ "resolved": "https://registry.npmjs.org/material-icon-theme/-/material-icon-theme-5.24.0.tgz",
+ "integrity": "sha512-fKCtRSOZwSMipgfsisDtlERXndg7hu4Ykhw8Vr+92NFVhYAi3izRHyKg6YooTw6yIB8fMF6Gq8IeNhbRxRY2SQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chroma-js": "^3.0.0",
+ "events": "^3.3.0",
+ "fast-deep-equal": "^3.1.3",
+ "svgson": "^5.3.1"
+ },
+ "engines": {
+ "vscode": "^1.55.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/material-extensions"
+ }
+ },
"node_modules/mathml-tag-names": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
@@ -10643,37 +9376,37 @@
}
},
"node_modules/mermaid": {
- "version": "11.4.1",
- "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.4.1.tgz",
- "integrity": "sha512-Mb01JT/x6CKDWaxigwfZYuYmDZ6xtrNwNlidKZwkSrDaY9n90tdrJTV5Umk+wP1fZscGptmKFXHsXMDEVZ+Q6A==",
+ "version": "11.8.0",
+ "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.8.0.tgz",
+ "integrity": "sha512-uAZUwnBiqREZcUrFw3G5iQ5Pj3hTYUP95EZc3ec/nGBzHddJZydzYGE09tGZDBS1VoSoDn0symZ85FmypSTo5g==",
"license": "MIT",
"dependencies": {
- "@braintree/sanitize-url": "^7.0.1",
- "@iconify/utils": "^2.1.32",
- "@mermaid-js/parser": "^0.3.0",
+ "@braintree/sanitize-url": "^7.0.4",
+ "@iconify/utils": "^2.1.33",
+ "@mermaid-js/parser": "^0.6.0",
"@types/d3": "^7.4.3",
- "cytoscape": "^3.29.2",
+ "cytoscape": "^3.29.3",
"cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.2.0",
"d3": "^7.9.0",
"d3-sankey": "^0.12.3",
"dagre-d3-es": "7.0.11",
- "dayjs": "^1.11.10",
- "dompurify": "^3.2.1",
+ "dayjs": "^1.11.13",
+ "dompurify": "^3.2.5",
"katex": "^0.16.9",
"khroma": "^2.1.0",
"lodash-es": "^4.17.21",
- "marked": "^13.0.2",
+ "marked": "^15.0.7",
"roughjs": "^4.6.6",
- "stylis": "^4.3.1",
+ "stylis": "^4.3.6",
"ts-dedent": "^2.2.0",
- "uuid": "^9.0.1"
+ "uuid": "^11.1.0"
}
},
"node_modules/mermaid/node_modules/marked": {
- "version": "13.0.3",
- "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz",
- "integrity": "sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==",
+ "version": "15.0.12",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz",
+ "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
@@ -10682,6 +9415,542 @@
"node": ">= 18"
}
},
+ "node_modules/micromark": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
+ "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/debug": "^4.0.0",
+ "debug": "^4.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
+ "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-destination": "^2.0.0",
+ "micromark-factory-label": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-title": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-html-tag-name": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-directive": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz",
+ "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "parse-entities": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
+ "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
+ "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-math": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz",
+ "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/katex": "^0.16.0",
+ "devlop": "^1.0.0",
+ "katex": "^0.16.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-factory-destination": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
+ "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-label": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
+ "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-space": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
+ "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
+ "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-chunked": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
+ "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-classify-character": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
+ "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-combine-extensions": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
+ "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-numeric-character-reference": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
+ "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
+ "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-html-tag-name": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
+ "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-normalize-identifier": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
+ "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-resolve-all": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
+ "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-subtokenize": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
+ "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
+ "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
@@ -10747,12 +10016,12 @@
}
},
"node_modules/minimatch": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
- "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
+ "version": "10.0.3",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz",
+ "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==",
"license": "ISC",
"dependencies": {
- "brace-expansion": "^2.0.1"
+ "@isaacs/brace-expansion": "^5.0.0"
},
"engines": {
"node": "20 || >=22"
@@ -10781,17 +10050,34 @@
}
},
"node_modules/mlly": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.3.tgz",
- "integrity": "sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==",
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz",
+ "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==",
"license": "MIT",
"dependencies": {
"acorn": "^8.14.0",
- "pathe": "^1.1.2",
- "pkg-types": "^1.2.1",
+ "pathe": "^2.0.1",
+ "pkg-types": "^1.3.0",
"ufo": "^1.5.4"
}
},
+ "node_modules/mlly/node_modules/confbox": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
+ "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
+ "license": "MIT"
+ },
+ "node_modules/mlly/node_modules/pkg-types": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
+ "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "confbox": "^0.1.8",
+ "mlly": "^1.7.4",
+ "pathe": "^2.0.1"
+ }
+ },
"node_modules/monaco-editor": {
"version": "0.52.2",
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz",
@@ -10842,9 +10128,9 @@
}
},
"node_modules/nanoid": {
- "version": "3.3.8",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
- "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"funding": [
{
"type": "github",
@@ -10859,6 +10145,28 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
+ "node_modules/nanopop": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/nanopop/-/nanopop-2.3.0.tgz",
+ "integrity": "sha512-fzN+T2K7/Ah25XU02MJkPZ5q4Tj5FpjmIYq4rvoHX4yb16HzFdCO6JxFFn5Y/oBhQ8no8fUZavnyIv9/+xkBBw==",
+ "license": "MIT"
+ },
+ "node_modules/napi-postinstall": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.0.tgz",
+ "integrity": "sha512-M7NqKyhODKV1gRLdkwE7pDsZP2/SC2a2vHkOYh9MCpKMbWVfyVfUw5MaH83Fv6XMjxr5jryUp3IDDL9rlxsTeA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "napi-postinstall": "lib/cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/napi-postinstall"
+ }
+ },
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -10933,9 +10241,9 @@
}
},
"node_modules/nolyfill": {
- "version": "1.0.43",
- "resolved": "https://registry.npmjs.org/nolyfill/-/nolyfill-1.0.43.tgz",
- "integrity": "sha512-wi8cCDStYl2cmKOWoF5Jv/0PZXkFcRgKpk9rtxWk+7/VDlI6WDBdPM5nUOsphZjibudajsyAh1+QQvxCyLnbMA==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/nolyfill/-/nolyfill-1.0.44.tgz",
+ "integrity": "sha512-PoggwVLiJUn0MnodpftsiC7EuknW5+6v62ntTOQ6T6l7g2r6aoaOwgk0tQW2BxGLYw9bF298LL8jDFTmEFuzlA==",
"dev": true,
"license": "MIT",
"bin": {
@@ -11020,13 +10328,13 @@
},
"node_modules/object.assign": {
"name": "@nolyfill/object.assign",
- "version": "1.0.28",
- "resolved": "https://registry.npmjs.org/@nolyfill/object.assign/-/object.assign-1.0.28.tgz",
- "integrity": "sha512-rrtnXgU2XJvUF9jFMwRbyvLdAlCIJOKtecflza4xWDom6u8UPliTOS0OQ6kvhql7/hpv9b8x9p0s467BVY58xg==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/object.assign/-/object.assign-1.0.44.tgz",
+ "integrity": "sha512-cZoXq09YZXDgkxRMAP/TTb3kAsWm7p5OyBugWDe4fOfxf0XRI55mgDSkuyq41sV1qW1zVC5aSsKEh1hQo1KOvA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@nolyfill/shared": "1.0.28"
+ "@nolyfill/shared": "1.0.44"
},
"engines": {
"node": ">=12.4.0"
@@ -11034,13 +10342,13 @@
},
"node_modules/object.fromentries": {
"name": "@nolyfill/object.fromentries",
- "version": "1.0.28",
- "resolved": "https://registry.npmjs.org/@nolyfill/object.fromentries/-/object.fromentries-1.0.28.tgz",
- "integrity": "sha512-EUt70p38p+xdHDi2i8pIgw6HjrI3y9zndVhAZdEQsAvatKGKRpe3XWZRleEwYRZjkbeAG53Pz30j4tE1IJjvQQ==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/object.fromentries/-/object.fromentries-1.0.44.tgz",
+ "integrity": "sha512-/LrsCtpLmByZ6GwP/NeXULSgMyNsVr5d6FlgQy1HZatAiBc8c+WZ1VmFkK19ZLXCNNXBedXDultrp0x4Nz+QQw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@nolyfill/shared": "1.0.28"
+ "@nolyfill/shared": "1.0.44"
},
"engines": {
"node": ">=12.4.0"
@@ -11048,34 +10356,27 @@
},
"node_modules/object.groupby": {
"name": "@nolyfill/object.groupby",
- "version": "1.0.24",
- "resolved": "https://registry.npmjs.org/@nolyfill/object.groupby/-/object.groupby-1.0.24.tgz",
- "integrity": "sha512-1PYpcT9MfPB4WRoZMUhuOrXNplTiqob7t5RKUYRh+yJm1Y8lSaDWKw2EUIJDthPbjB+UMpo75nKxdbXhRms5SQ==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/object.groupby/-/object.groupby-1.0.44.tgz",
+ "integrity": "sha512-jCt/8pN+10mlbeg0ZESpVVaqn5qqpv6kpjM+GDfEP7cXGDSPlIjtvfYWRZK4k4Gftkhhgqkzvcrr8z1wuNO1TQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@nolyfill/shared": "1.0.24"
+ "@nolyfill/shared": "1.0.44"
},
"engines": {
"node": ">=12.4.0"
}
},
- "node_modules/object.groupby/node_modules/@nolyfill/shared": {
- "version": "1.0.24",
- "resolved": "https://registry.npmjs.org/@nolyfill/shared/-/shared-1.0.24.tgz",
- "integrity": "sha512-TGCpg3k5N7jj9AgU/1xFw9K1g4AC1vEE5ZFkW77oPNNLzprxT17PvFaNr/lr3BkkT5fJ5LNMntaTIq+pyWaeEA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/object.values": {
"name": "@nolyfill/object.values",
- "version": "1.0.28",
- "resolved": "https://registry.npmjs.org/@nolyfill/object.values/-/object.values-1.0.28.tgz",
- "integrity": "sha512-W6CdQv4Y/19aA5tenUhRELqlBoD92D4Uh1TDp5uHXD7s9zEHgcDCPCdA8ak6y4I66fR//Fir6C1mAQWv1QLnXw==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/object.values/-/object.values-1.0.44.tgz",
+ "integrity": "sha512-bwIpVzFMudUC0ofnvdSDB/OyGUizcU+r32ZZ0QTMbN03gUttMtdCFDekuSYT0XGFgufTQyZ4ONBnAeb3DFCPGQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@nolyfill/shared": "1.0.28"
+ "@nolyfill/shared": "1.0.44"
},
"engines": {
"node": ">=12.4.0"
@@ -11090,6 +10391,17 @@
"wrappy": "1"
}
},
+ "node_modules/online-3d-viewer": {
+ "version": "0.16.0",
+ "resolved": "https://registry.npmjs.org/online-3d-viewer/-/online-3d-viewer-0.16.0.tgz",
+ "integrity": "sha512-Mcmo41TM3K+svlMDRH8ySKSY2e8s7Sssdb5U9LV3gkFKVWGGuS304Vk5gqxopAJbE72DpsC67Ve3YNtcAuROwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@simonwep/pickr": "1.9.0",
+ "fflate": "0.8.2",
+ "three": "0.176.0"
+ }
+ },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -11156,9 +10468,9 @@
"license": "BlueOak-1.0.0"
},
"node_modules/package-manager-detector": {
- "version": "0.2.7",
- "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.7.tgz",
- "integrity": "sha512-g4+387DXDKlZzHkP+9FLt8yKj8+/3tOkPv7DVTJGGRm00RkEWgqbFstX1mXJ4M0VDYhUqsTOiISqNOJnhAu3PQ==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz",
+ "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==",
"license": "MIT"
},
"node_modules/parent-module": {
@@ -11173,6 +10485,26 @@
"node": ">=6"
}
},
+ "node_modules/parse-entities": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
+ "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@@ -11254,16 +10586,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/path-scurry/node_modules/lru-cache": {
- "version": "11.0.2",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz",
- "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "20 || >=22"
- }
- },
"node_modules/path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
@@ -11275,15 +10597,15 @@
}
},
"node_modules/pathe": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
- "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"license": "MIT"
},
"node_modules/pathval": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
- "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
+ "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -11291,9 +10613,9 @@
}
},
"node_modules/pdfobject": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pdfobject/-/pdfobject-2.3.0.tgz",
- "integrity": "sha512-w/9pXDXTDs3IDmOri/w8lM/w6LHR0/F4fcBLLzH+4csSoyshQ5su0TE7k0FLHZO7aOjVLDGecqd1M89+PVpVAA==",
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/pdfobject/-/pdfobject-2.3.1.tgz",
+ "integrity": "sha512-vluuGiSDmMGpOvWFGiUY4trNB8aGKLDVxIXuuGHjX0kK3bMxCANUVtLivctE7uejLBScWCnbVarKatFVvdwXaQ==",
"license": "MIT"
},
"node_modules/perfect-debounce": {
@@ -11330,9 +10652,9 @@
}
},
"node_modules/pirates": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
- "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
+ "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
"license": "MIT",
"engines": {
"node": ">= 6"
@@ -11403,24 +10725,24 @@
}
},
"node_modules/pkg-types": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.1.tgz",
- "integrity": "sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.2.0.tgz",
+ "integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==",
"license": "MIT",
"dependencies": {
- "confbox": "^0.1.8",
- "mlly": "^1.7.2",
- "pathe": "^1.1.2"
+ "confbox": "^0.2.2",
+ "exsolve": "^1.0.7",
+ "pathe": "^2.0.3"
}
},
"node_modules/playwright": {
- "version": "1.49.1",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz",
- "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==",
+ "version": "1.53.2",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz",
+ "integrity": "sha512-6K/qQxVFuVQhRQhFsVZ9fGeatxirtrpPgxzBYWyZLEXJzqYwuL4fuNmfOfD5et1tJE4GScKyPNeLhZeRwuTU3A==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright-core": "1.49.1"
+ "playwright-core": "1.53.2"
},
"bin": {
"playwright": "cli.js"
@@ -11433,9 +10755,9 @@
}
},
"node_modules/playwright-core": {
- "version": "1.49.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz",
- "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==",
+ "version": "1.53.2",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.2.tgz",
+ "integrity": "sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -11482,9 +10804,9 @@
}
},
"node_modules/postcss": {
- "version": "8.4.49",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
- "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"funding": [
{
"type": "opencollective",
@@ -11501,7 +10823,7 @@
],
"license": "MIT",
"dependencies": {
- "nanoid": "^3.3.7",
+ "nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
@@ -11510,28 +10832,21 @@
}
},
"node_modules/postcss-html": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.7.0.tgz",
- "integrity": "sha512-MfcMpSUIaR/nNgeVS8AyvyDugXlADjN9AcV7e5rDfrF1wduIAGSkL4q2+wgrZgA3sHVAHLDO9FuauHhZYW2nBw==",
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.8.0.tgz",
+ "integrity": "sha512-5mMeb1TgLWoRKxZ0Xh9RZDfwUUIqRrcxO2uXO+Ezl1N5lqpCiSU5Gk6+1kZediBfBHFtPCdopr2UZ2SgUsKcgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"htmlparser2": "^8.0.0",
"js-tokens": "^9.0.0",
- "postcss": "^8.4.0",
+ "postcss": "^8.5.0",
"postcss-safe-parser": "^6.0.0"
},
"engines": {
"node": "^12 || >=14"
}
},
- "node_modules/postcss-html/node_modules/js-tokens": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
- "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/postcss-import": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
@@ -11568,41 +10883,6 @@
"postcss": "^8.4.21"
}
},
- "node_modules/postcss-load-config": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
- "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "lilconfig": "^3.0.0",
- "yaml": "^2.3.4"
- },
- "engines": {
- "node": ">= 14"
- },
- "peerDependencies": {
- "postcss": ">=8.0.9",
- "ts-node": ">=9.0.0"
- },
- "peerDependenciesMeta": {
- "postcss": {
- "optional": true
- },
- "ts-node": {
- "optional": true
- }
- }
- },
"node_modules/postcss-loader": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz",
@@ -11664,9 +10944,9 @@
}
},
"node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz",
- "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
+ "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
@@ -11692,9 +10972,9 @@
}
},
"node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz",
- "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
+ "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
@@ -11745,9 +11025,9 @@
}
},
"node_modules/postcss-nesting": {
- "version": "13.0.1",
- "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.1.tgz",
- "integrity": "sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ==",
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz",
+ "integrity": "sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==",
"funding": [
{
"type": "github",
@@ -11760,7 +11040,7 @@
],
"license": "MIT-0",
"dependencies": {
- "@csstools/selector-resolve-nested": "^3.0.0",
+ "@csstools/selector-resolve-nested": "^3.1.0",
"@csstools/selector-specificity": "^5.0.0",
"postcss-selector-parser": "^7.0.0"
},
@@ -11772,9 +11052,9 @@
}
},
"node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.0.0.tgz",
- "integrity": "sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz",
+ "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==",
"funding": [
{
"type": "github",
@@ -11816,9 +11096,9 @@
}
},
"node_modules/postcss-nesting/node_modules/postcss-selector-parser": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz",
- "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
+ "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
@@ -11929,9 +11209,9 @@
}
},
"node_modules/prettier": {
- "version": "3.4.2",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz",
- "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==",
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
+ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"dev": true,
"license": "MIT",
"bin": {
@@ -11978,6 +11258,7 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -11993,6 +11274,22 @@
"node": ">=6"
}
},
+ "node_modules/quansync": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz",
+ "integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/antfu"
+ },
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -12178,42 +11475,6 @@
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
- "node_modules/regenerate": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
- "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/regenerate-unicode-properties": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz",
- "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "regenerate": "^1.4.2"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/regenerator-runtime": {
- "version": "0.14.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
- "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
- "license": "MIT"
- },
- "node_modules/regenerator-transform": {
- "version": "0.15.2",
- "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz",
- "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.8.4"
- }
- },
"node_modules/regexp-ast-analysis": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/regexp-ast-analysis/-/regexp-ast-analysis-0.7.1.tgz",
@@ -12238,57 +11499,6 @@
"regexp-tree": "bin/regexp-tree"
}
},
- "node_modules/regexpu-core": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz",
- "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "regenerate": "^1.4.2",
- "regenerate-unicode-properties": "^10.2.0",
- "regjsgen": "^0.8.0",
- "regjsparser": "^0.12.0",
- "unicode-match-property-ecmascript": "^2.0.0",
- "unicode-match-property-value-ecmascript": "^2.1.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/regexpu-core/node_modules/jsesc": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
- "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "jsesc": "bin/jsesc"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/regexpu-core/node_modules/regjsparser": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz",
- "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "jsesc": "~3.0.2"
- },
- "bin": {
- "regjsparser": "bin/parser"
- }
- },
- "node_modules/regjsgen": {
- "version": "0.8.0",
- "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
- "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/regjsparser": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz",
@@ -12311,6 +11521,16 @@
"jsesc": "bin/jsesc"
}
},
+ "node_modules/rename-keys": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/rename-keys/-/rename-keys-1.2.0.tgz",
+ "integrity": "sha512-U7XpAktpbSgHTRSNRrjKSrjYkZKuhUukfoBlXWXUExCAqhzh1TU3BDRAfJmarcl5voKS+pbKU9MvyLWKZ4UEEg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -12340,9 +11560,9 @@
}
},
"node_modules/resolve": {
- "version": "1.22.9",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.9.tgz",
- "integrity": "sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==",
+ "version": "1.22.10",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
+ "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
"license": "MIT",
"dependencies": {
"is-core-module": "^2.16.0",
@@ -12352,6 +11572,9 @@
"bin": {
"resolve": "bin/resolve"
},
+ "engines": {
+ "node": ">= 0.4"
+ },
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -12396,9 +11619,9 @@
}
},
"node_modules/reusify": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
- "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
"license": "MIT",
"engines": {
"iojs": ">=1.0.0",
@@ -12503,9 +11726,9 @@
},
"node_modules/safe-buffer": {
"name": "@nolyfill/safe-buffer",
- "version": "1.0.41",
- "resolved": "https://registry.npmjs.org/@nolyfill/safe-buffer/-/safe-buffer-1.0.41.tgz",
- "integrity": "sha512-QUutoN6a0Rf49n6kYVSppQpfraXfrtrUaxTU/GhcxIhz+3GHp+SrBVnTKE3ASB7AzgZJ7XrxhaBgwrF+hSmeKg==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/safe-buffer/-/safe-buffer-1.0.44.tgz",
+ "integrity": "sha512-SqlKXtlhNTDMeZKey9jnnuPhi8YTl1lJuEcY9zbm5i4Pqe79UJJ8IJ9oiD6DhgI8KjYc+HtLzpQJNRdNYqb/hw==",
"license": "MIT",
"engines": {
"node": ">=12.4.0"
@@ -12513,9 +11736,9 @@
},
"node_modules/safe-regex-test": {
"name": "@nolyfill/safe-regex-test",
- "version": "1.0.29",
- "resolved": "https://registry.npmjs.org/@nolyfill/safe-regex-test/-/safe-regex-test-1.0.29.tgz",
- "integrity": "sha512-aar+tW/KIy5tzhV/DDty2IM3tEvXqj6dhP8iXIVZOa9gFwPqLZzy54D7gYkn7EwxLPNhHordzsqmnrFEHDYTSg==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/safe-regex-test/-/safe-regex-test-1.0.44.tgz",
+ "integrity": "sha512-Q6veatd1NebtD8Sre6zjvO35QzG21IskMVOOEbePFcNO9noanNJgsqHeOCr0c5yZz6Z0DAizLg2gIZWokJSkXw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -12531,9 +11754,9 @@
},
"node_modules/safer-buffer": {
"name": "@nolyfill/safer-buffer",
- "version": "1.0.41",
- "resolved": "https://registry.npmjs.org/@nolyfill/safer-buffer/-/safer-buffer-1.0.41.tgz",
- "integrity": "sha512-RieuhcNjFpL2NObzKlsNGM5rCan1y2PH3KUUJ01Yhxqj4gTB20WplufPmQRSrLdI9BWf8cBrxvXuRtjvMRKzxQ==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/safer-buffer/-/safer-buffer-1.0.44.tgz",
+ "integrity": "sha512-Ouw1fMwjAy1V4MpnDASfu1DCPgkP0nNFteiiWbFoEGSqa7Vnmkb6if2c522N2WcMk+RuaaabQbC1F1D4/kTXcg==",
"license": "MIT",
"engines": {
"node": ">=12.4.0"
@@ -12547,9 +11770,9 @@
"license": "ISC"
},
"node_modules/schema-utils": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
- "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
+ "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
@@ -12581,9 +11804,9 @@
}
},
"node_modules/semver": {
- "version": "7.6.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
- "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "version": "7.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -12602,18 +11825,18 @@
}
},
"node_modules/seroval": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.1.1.tgz",
- "integrity": "sha512-rqEO6FZk8mv7Hyv4UCj3FD3b6Waqft605TLfsCe/BiaylRpyyMC0b+uA5TJKawX3KzMrdi3wsLbCaLplrQmBvQ==",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz",
+ "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/seroval-plugins": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.1.1.tgz",
- "integrity": "sha512-qNSy1+nUj7hsCOon7AO4wdAIo9P0jrzAMp18XhiOzA6/uO5TKtP7ScozVJ8T293oRIvi5wyCHSM4TrJo/c/GJA==",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.3.2.tgz",
+ "integrity": "sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ==",
"license": "MIT",
"engines": {
"node": ">=10"
@@ -12716,9 +11939,9 @@
}
},
"node_modules/smol-toml": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.1.tgz",
- "integrity": "sha512-tEYNll18pPKHroYSmLLrksq233j021G0giwW7P3D24jC54pQ5W5BXMsQ/Mvw1OJCmEYDgY+lrzT+3nNUtoNfXQ==",
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.4.tgz",
+ "integrity": "sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
@@ -12729,14 +11952,14 @@
}
},
"node_modules/solid-js": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.3.tgz",
- "integrity": "sha512-5ba3taPoZGt9GY3YlsCB24kCg0Lv/rie/HTD4kG6h4daZZz7+yK02xn8Vx8dLYBc9i6Ps5JwAbEiqjmKaLB3Ag==",
+ "version": "1.9.7",
+ "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.7.tgz",
+ "integrity": "sha512-/saTKi8iWEM233n5OSi1YHCCuh66ZIQ7aK2hsToPe4tqGm7qAejU1SwNuTPivbWAYq7SjuHVVYxxuZQNRbICiw==",
"license": "MIT",
"dependencies": {
"csstype": "^3.1.0",
- "seroval": "^1.1.0",
- "seroval-plugins": "^1.1.0"
+ "seroval": "~1.3.0",
+ "seroval-plugins": "~1.3.0"
}
},
"node_modules/sortablejs": {
@@ -12847,9 +12070,9 @@
}
},
"node_modules/spdx-license-ids": {
- "version": "3.0.20",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz",
- "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==",
+ "version": "3.0.21",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz",
+ "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==",
"license": "CC0-1.0"
},
"node_modules/spdx-ranges": {
@@ -12869,12 +12092,15 @@
"spdx-ranges": "^2.0.0"
}
},
- "node_modules/stable-hash": {
- "version": "0.0.4",
- "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz",
- "integrity": "sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==",
+ "node_modules/stable-hash-x": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz",
+ "integrity": "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ }
},
"node_modules/stackback": {
"version": "0.0.2",
@@ -12895,9 +12121,9 @@
}
},
"node_modules/std-env": {
- "version": "3.8.0",
- "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz",
- "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==",
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz",
+ "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==",
"dev": true,
"license": "MIT"
},
@@ -12944,13 +12170,13 @@
},
"node_modules/string.prototype.includes": {
"name": "@nolyfill/string.prototype.includes",
- "version": "1.0.28",
- "resolved": "https://registry.npmjs.org/@nolyfill/string.prototype.includes/-/string.prototype.includes-1.0.28.tgz",
- "integrity": "sha512-RfwmNcAKnstWgNxfVlYpz/hK6V2pnl0r1uinLmGrf4pYN+QviciawKGcBUjkyeB8WUFCuIDE9JhCnTydqJ5O2w==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/string.prototype.includes/-/string.prototype.includes-1.0.44.tgz",
+ "integrity": "sha512-d1t7rnoAYyoap0X3a/gCnusCvxzK6v7uMFzW8k0mI2WtAK8HiKuzaQUwAriyVPh63GsvQCqvXx8Y5gtdh4LjSA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@nolyfill/shared": "1.0.28"
+ "@nolyfill/shared": "1.0.44"
},
"engines": {
"node": ">=12.4.0"
@@ -12958,13 +12184,13 @@
},
"node_modules/string.prototype.trimend": {
"name": "@nolyfill/string.prototype.trimend",
- "version": "1.0.28",
- "resolved": "https://registry.npmjs.org/@nolyfill/string.prototype.trimend/-/string.prototype.trimend-1.0.28.tgz",
- "integrity": "sha512-fxCTYRU/F6O0Kkhm84Ns4vIWhfFKqCErjpTsmzBPxUefYsUtII0gzpKq08Kzolv779PLL3On048VNEwn8dun4g==",
+ "version": "1.0.44",
+ "resolved": "https://registry.npmjs.org/@nolyfill/string.prototype.trimend/-/string.prototype.trimend-1.0.44.tgz",
+ "integrity": "sha512-3dsKlf4Ma7o+uxLIg5OI1Tgwfet2pE8WTbPjEGWvOe6CSjMtK0skJnnSVHaEVX4N4mYU81To0qDeZOPqjaUotg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@nolyfill/shared": "1.0.28"
+ "@nolyfill/shared": "1.0.44"
},
"engines": {
"node": ">=12.4.0"
@@ -13031,6 +12257,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/strip-literal": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz",
+ "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^9.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
"node_modules/style-search": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
@@ -13039,9 +12278,9 @@
"license": "ISC"
},
"node_modules/stylelint": {
- "version": "16.12.0",
- "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.12.0.tgz",
- "integrity": "sha512-F8zZ3L/rBpuoBZRvI4JVT20ZanPLXfQLzMOZg1tzPflRVh9mKpOZ8qcSIhh1my3FjAjZWG4T2POwGnmn6a6hbg==",
+ "version": "16.21.1",
+ "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.21.1.tgz",
+ "integrity": "sha512-WCXdXnYK2tpCbebgMF0Bme3YZH/Rh/UXerj75twYo4uLULlcrLwFVdZTvTEF8idFnAcW21YUDJFyKOfaf6xJRw==",
"dev": true,
"funding": [
{
@@ -13055,41 +12294,41 @@
],
"license": "MIT",
"dependencies": {
- "@csstools/css-parser-algorithms": "^3.0.4",
- "@csstools/css-tokenizer": "^3.0.3",
- "@csstools/media-query-list-parser": "^4.0.2",
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4",
+ "@csstools/media-query-list-parser": "^4.0.3",
"@csstools/selector-specificity": "^5.0.0",
"@dual-bundle/import-meta-resolve": "^4.1.0",
"balanced-match": "^2.0.0",
"colord": "^2.9.3",
"cosmiconfig": "^9.0.0",
"css-functions-list": "^3.2.3",
- "css-tree": "^3.0.1",
- "debug": "^4.3.7",
- "fast-glob": "^3.3.2",
+ "css-tree": "^3.1.0",
+ "debug": "^4.4.1",
+ "fast-glob": "^3.3.3",
"fastest-levenshtein": "^1.0.16",
- "file-entry-cache": "^9.1.0",
+ "file-entry-cache": "^10.1.1",
"global-modules": "^2.0.0",
"globby": "^11.1.0",
"globjoin": "^0.1.4",
"html-tags": "^3.3.1",
- "ignore": "^6.0.2",
+ "ignore": "^7.0.5",
"imurmurhash": "^0.1.4",
"is-plain-object": "^5.0.0",
- "known-css-properties": "^0.35.0",
+ "known-css-properties": "^0.37.0",
"mathml-tag-names": "^2.1.3",
"meow": "^13.2.0",
"micromatch": "^4.0.8",
"normalize-path": "^3.0.0",
"picocolors": "^1.1.1",
- "postcss": "^8.4.49",
+ "postcss": "^8.5.6",
"postcss-resolve-nested-selector": "^0.1.6",
"postcss-safe-parser": "^7.0.1",
- "postcss-selector-parser": "^7.0.0",
+ "postcss-selector-parser": "^7.1.0",
"postcss-value-parser": "^4.2.0",
"resolve-from": "^5.0.0",
"string-width": "^4.2.3",
- "supports-hyperlinks": "^3.1.0",
+ "supports-hyperlinks": "^3.2.0",
"svg-tags": "^1.0.0",
"table": "^6.9.0",
"write-file-atomic": "^5.0.1"
@@ -13101,6 +12340,29 @@
"node": ">=18.12.0"
}
},
+ "node_modules/stylelint-config-recommended": {
+ "version": "16.0.0",
+ "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-16.0.0.tgz",
+ "integrity": "sha512-4RSmPjQegF34wNcK1e1O3Uz91HN8P1aFdFzio90wNK9mjgAI19u5vsU868cVZboKzCaa5XbpvtTzAAGQAxpcXA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/stylelint"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/stylelint"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "peerDependencies": {
+ "stylelint": "^16.16.0"
+ }
+ },
"node_modules/stylelint-declaration-block-no-ignored-properties": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/stylelint-declaration-block-no-ignored-properties/-/stylelint-declaration-block-no-ignored-properties-2.8.0.tgz",
@@ -13115,9 +12377,9 @@
}
},
"node_modules/stylelint-declaration-strict-value": {
- "version": "1.10.6",
- "resolved": "https://registry.npmjs.org/stylelint-declaration-strict-value/-/stylelint-declaration-strict-value-1.10.6.tgz",
- "integrity": "sha512-aZGEW4Ee26Tx4UvpQJbcElVXZ42EleujEByiyKDTT7t83EeSe9t0lAG3OOLJnnvLjz/dQnp+L+3IYTMeQI51vQ==",
+ "version": "1.10.11",
+ "resolved": "https://registry.npmjs.org/stylelint-declaration-strict-value/-/stylelint-declaration-strict-value-1.10.11.tgz",
+ "integrity": "sha512-oVQvhZlFZAiDz9r2BPFZLtTGm1A2JVhdKObKAJoTjFfR4F/NpApC4bMBTxf4sZS76Na3njYKVOaAaKSZ4+FU+g==",
"dev": true,
"license": "MIT",
"engines": {
@@ -13127,6 +12389,24 @@
"stylelint": ">=7 <=16"
}
},
+ "node_modules/stylelint-define-config": {
+ "version": "16.21.0",
+ "resolved": "https://registry.npmjs.org/stylelint-define-config/-/stylelint-define-config-16.21.0.tgz",
+ "integrity": "sha512-DNbScaWa4MvOrEUgpUacIEBI57iV4drbIl9D5GXXlYtcv1z9xrdatDagewTq9uwXADodkwc3QJcraVAL/pWrWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.1.3"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=9.0.0",
+ "pnpm": ">=8.6.0"
+ },
+ "peerDependencies": {
+ "stylelint": ">=16.0.0"
+ }
+ },
"node_modules/stylelint-value-no-unknown-custom-properties": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/stylelint-value-no-unknown-custom-properties/-/stylelint-value-no-unknown-custom-properties-6.0.1.tgz",
@@ -13145,9 +12425,9 @@
}
},
"node_modules/stylelint/node_modules/@csstools/media-query-list-parser": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz",
- "integrity": "sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==",
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz",
+ "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==",
"dev": true,
"funding": [
{
@@ -13164,8 +12444,8 @@
"node": ">=18"
},
"peerDependencies": {
- "@csstools/css-parser-algorithms": "^3.0.4",
- "@csstools/css-tokenizer": "^3.0.3"
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
}
},
"node_modules/stylelint/node_modules/@csstools/selector-specificity": {
@@ -13191,44 +12471,32 @@
"postcss-selector-parser": "^7.0.0"
}
},
- "node_modules/stylelint/node_modules/balanced-match": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz",
- "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/stylelint/node_modules/file-entry-cache": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.1.0.tgz",
- "integrity": "sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==",
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.1.1.tgz",
+ "integrity": "sha512-zcmsHjg2B2zjuBgjdnB+9q0+cWcgWfykIcsDkWDB4GTPtl1eXUA+gTI6sO0u01AqK3cliHryTU55/b2Ow1hfZg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "flat-cache": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
+ "flat-cache": "^6.1.10"
}
},
"node_modules/stylelint/node_modules/flat-cache": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz",
- "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==",
+ "version": "6.1.11",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.11.tgz",
+ "integrity": "sha512-zfOAns94mp7bHG/vCn9Ru2eDCmIxVQ5dELUHKjHfDEOJmHNzE+uGa6208kfkgmtym4a0FFjEuFksCXFacbVhSg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "flatted": "^3.3.1",
- "keyv": "^4.5.4"
- },
- "engines": {
- "node": ">=18"
+ "cacheable": "^1.10.1",
+ "flatted": "^3.3.3",
+ "hookified": "^1.10.0"
}
},
"node_modules/stylelint/node_modules/ignore": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-6.0.2.tgz",
- "integrity": "sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==",
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -13263,9 +12531,9 @@
}
},
"node_modules/stylelint/node_modules/postcss-selector-parser": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz",
- "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
+ "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -13287,9 +12555,9 @@
}
},
"node_modules/stylis": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.4.tgz",
- "integrity": "sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==",
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
+ "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==",
"license": "MIT"
},
"node_modules/stylus": {
@@ -13345,6 +12613,21 @@
"node": ">=16 || 14 >=14.17"
}
},
+ "node_modules/sucrase/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "license": "MIT"
+ },
+ "node_modules/sucrase/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
"node_modules/sucrase/node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@@ -13445,9 +12728,9 @@
}
},
"node_modules/supports-hyperlinks": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz",
- "integrity": "sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz",
+ "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -13458,7 +12741,7 @@
"node": ">=14.18"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1"
}
},
"node_modules/supports-preserve-symlinks-flag": {
@@ -13491,25 +12774,25 @@
"dev": true
},
"node_modules/svgo": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz",
- "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz",
+ "integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@trysound/sax": "0.2.0",
- "commander": "^7.2.0",
+ "commander": "^11.1.0",
"css-select": "^5.1.0",
- "css-tree": "^2.3.1",
+ "css-tree": "^3.0.1",
"css-what": "^6.1.0",
"csso": "^5.0.5",
- "picocolors": "^1.0.0"
+ "picocolors": "^1.1.1",
+ "sax": "^1.4.1"
},
"bin": {
- "svgo": "bin/svgo"
+ "svgo": "bin/svgo.js"
},
"engines": {
- "node": ">=14.0.0"
+ "node": ">=16"
},
"funding": {
"type": "opencollective",
@@ -13517,40 +12800,37 @@
}
},
"node_modules/svgo/node_modules/commander": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
+ "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">= 10"
+ "node": ">=16"
}
},
- "node_modules/svgo/node_modules/css-tree": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
- "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
+ "node_modules/svgo/node_modules/sax": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
+ "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/svgson": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/svgson/-/svgson-5.3.1.tgz",
+ "integrity": "sha512-qdPgvUNWb40gWktBJnbJRelWcPzkLed/ShhnRsjbayXz8OtdPOzbil9jtiZdrYvSDumAz/VNQr6JaNfPx/gvPA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "mdn-data": "2.0.30",
- "source-map-js": "^1.0.1"
- },
- "engines": {
- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ "deep-rename-keys": "^0.2.1",
+ "xml-reader": "2.4.3"
}
},
- "node_modules/svgo/node_modules/mdn-data": {
- "version": "2.0.30",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
- "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
- "dev": true,
- "license": "CC0-1.0"
- },
"node_modules/swagger-ui-dist": {
- "version": "5.18.2",
- "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.18.2.tgz",
- "integrity": "sha512-J+y4mCw/zXh1FOj5wGJvnAajq6XgHOyywsa9yITmwxIlJbMqITq3gYRZHaeqLVH/eV/HOPphE6NjF+nbSNC5Zw==",
+ "version": "5.26.0",
+ "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.26.0.tgz",
+ "integrity": "sha512-U8m1LruHrk33gIIT5qDKhXMygT4FonRGBE92zMbxP4i9ULolPlKISy5Pd3RCES8pWdbGzXhvm/Q6jdA/HsrClg==",
"license": "Apache-2.0",
"dependencies": {
"@scarf/scarf": "=1.4.0"
@@ -13570,20 +12850,19 @@
}
},
"node_modules/synckit": {
- "version": "0.9.2",
- "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
- "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==",
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz",
+ "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@pkgr/core": "^0.1.0",
- "tslib": "^2.6.2"
+ "@pkgr/core": "^0.2.4"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
- "url": "https://opencollective.com/unts"
+ "url": "https://opencollective.com/synckit"
}
},
"node_modules/table": {
@@ -13640,23 +12919,58 @@
"node": ">=14.0.0"
}
},
+ "node_modules/tailwindcss/node_modules/postcss-load-config": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
+ "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "lilconfig": "^3.0.0",
+ "yaml": "^2.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ },
+ "peerDependencies": {
+ "postcss": ">=8.0.9",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "postcss": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
"node_modules/tapable": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz",
+ "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/terser": {
- "version": "5.37.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz",
- "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==",
+ "version": "5.43.1",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
+ "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.8.2",
+ "acorn": "^8.14.0",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
},
@@ -13668,9 +12982,9 @@
}
},
"node_modules/terser-webpack-plugin": {
- "version": "5.3.11",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz",
- "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==",
+ "version": "5.3.14",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz",
+ "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==",
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.25",
@@ -13735,6 +13049,12 @@
"node": ">=0.8"
}
},
+ "node_modules/three": {
+ "version": "0.176.0",
+ "resolved": "https://registry.npmjs.org/three/-/three-0.176.0.tgz",
+ "integrity": "sha512-PWRKYWQo23ojf9oZSlRGH8K09q7nRSWx6LY/HF/UUrMdYgN9i1e2OwJYHoQjwc6HF/4lvvYLC5YC1X8UJL2ZpA==",
+ "license": "MIT"
+ },
"node_modules/throttle-debounce": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz",
@@ -13758,15 +13078,60 @@
"license": "MIT"
},
"node_modules/tinyexec": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz",
- "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz",
+ "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==",
"license": "MIT"
},
+ "node_modules/tinyglobby": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
+ "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.4.6",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
+ "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/tinypool": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz",
- "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
+ "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -13774,9 +13139,9 @@
}
},
"node_modules/tinyrainbow": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz",
- "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz",
+ "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -13784,9 +13149,9 @@
}
},
"node_modules/tinyspy": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
- "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==",
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz",
+ "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -13833,16 +13198,16 @@
"license": "MIT"
},
"node_modules/ts-api-utils": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz",
- "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=16"
+ "node": ">=18.12"
},
"peerDependencies": {
- "typescript": ">=4.2.0"
+ "typescript": ">=4.8.4"
}
},
"node_modules/ts-dedent": {
@@ -13907,9 +13272,9 @@
}
},
"node_modules/type-fest": {
- "version": "4.30.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.30.2.tgz",
- "integrity": "sha512-UJShLPYi1aWqCdq9HycOL/gwsuqda1OISdBO3t8RlXQC4QvtuIz4b5FCfe2dQIWEpmlRExKmcTBfP1r9bhY7ig==",
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+ "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"engines": {
@@ -13920,9 +13285,9 @@
}
},
"node_modules/typescript": {
- "version": "5.7.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
- "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@@ -13946,81 +13311,72 @@
"license": "MIT"
},
"node_modules/ufo": {
- "version": "1.5.4",
- "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz",
- "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==",
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz",
+ "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
"license": "MIT"
},
"node_modules/uint8-to-base64": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/uint8-to-base64/-/uint8-to-base64-0.2.0.tgz",
- "integrity": "sha512-r13jrghEYZAN99GeYpEjM107DOxqB65enskpwce8rRHVAGEtaWmsF5GqoGdPMf8DIXc9XyAJTdvlvRZi4LsszA==",
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/uint8-to-base64/-/uint8-to-base64-0.2.1.tgz",
+ "integrity": "sha512-uO/84GaoDUfiAxpa8EksjVLE77A9Kc7ZTziN4zRpq4de9yLaLcZn3jx1/sVjyupsywcVX6RKWbqLe7gUNyzH+Q==",
"license": "ISC"
},
"node_modules/undici-types": {
- "version": "6.20.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
- "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
+ "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
"license": "MIT"
},
- "node_modules/unicode-canonical-property-names-ecmascript": {
+ "node_modules/universalify": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
- "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=4"
+ "node": ">= 10.0.0"
}
},
- "node_modules/unicode-match-property-ecmascript": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
- "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
+ "node_modules/unrs-resolver": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.10.1.tgz",
+ "integrity": "sha512-EFrL7Hw4kmhZdwWO3dwwFJo6hO3FXuQ6Bg8BK/faHZ9m1YxqBS31BNSTxklIQkxK/4LlV8zTYnPsIRLBzTzjCA==",
"dev": true,
+ "hasInstallScript": true,
"license": "MIT",
"dependencies": {
- "unicode-canonical-property-names-ecmascript": "^2.0.0",
- "unicode-property-aliases-ecmascript": "^2.0.0"
+ "napi-postinstall": "^0.3.0"
},
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/unicode-match-property-value-ecmascript": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz",
- "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/unicode-property-aliases-ecmascript": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
- "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
+ "funding": {
+ "url": "https://opencollective.com/unrs-resolver"
+ },
+ "optionalDependencies": {
+ "@unrs/resolver-binding-android-arm-eabi": "1.10.1",
+ "@unrs/resolver-binding-android-arm64": "1.10.1",
+ "@unrs/resolver-binding-darwin-arm64": "1.10.1",
+ "@unrs/resolver-binding-darwin-x64": "1.10.1",
+ "@unrs/resolver-binding-freebsd-x64": "1.10.1",
+ "@unrs/resolver-binding-linux-arm-gnueabihf": "1.10.1",
+ "@unrs/resolver-binding-linux-arm-musleabihf": "1.10.1",
+ "@unrs/resolver-binding-linux-arm64-gnu": "1.10.1",
+ "@unrs/resolver-binding-linux-arm64-musl": "1.10.1",
+ "@unrs/resolver-binding-linux-ppc64-gnu": "1.10.1",
+ "@unrs/resolver-binding-linux-riscv64-gnu": "1.10.1",
+ "@unrs/resolver-binding-linux-riscv64-musl": "1.10.1",
+ "@unrs/resolver-binding-linux-s390x-gnu": "1.10.1",
+ "@unrs/resolver-binding-linux-x64-gnu": "1.10.1",
+ "@unrs/resolver-binding-linux-x64-musl": "1.10.1",
+ "@unrs/resolver-binding-wasm32-wasi": "1.10.1",
+ "@unrs/resolver-binding-win32-arm64-msvc": "1.10.1",
+ "@unrs/resolver-binding-win32-ia32-msvc": "1.10.1",
+ "@unrs/resolver-binding-win32-x64-msvc": "1.10.1"
}
},
"node_modules/update-browserslist-db": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
- "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
+ "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
"funding": [
{
"type": "opencollective",
@@ -14038,7 +13394,7 @@
"license": "MIT",
"dependencies": {
"escalade": "^3.2.0",
- "picocolors": "^1.1.0"
+ "picocolors": "^1.1.1"
},
"bin": {
"update-browserslist-db": "cli.js"
@@ -14048,9 +13404,9 @@
}
},
"node_modules/updates": {
- "version": "16.4.1",
- "resolved": "https://registry.npmjs.org/updates/-/updates-16.4.1.tgz",
- "integrity": "sha512-dYsXzAISSiTFk6mg2ZB7IOlhIN5CO4qINxpbN7rrE9ffpuycq82SZ8rvLSc2QpBEO98BBY7wKm3d8SdA94o5TQ==",
+ "version": "16.4.2",
+ "resolved": "https://registry.npmjs.org/updates/-/updates-16.4.2.tgz",
+ "integrity": "sha512-FCnbRnBUWrzhbsatc+RqUoLK7WB88VDGB9Jb1vB2JOx8gN69oXAE4/umtZsUzdeiM2TZ0aVb0kQJ0NbY8K21nw==",
"dev": true,
"license": "BSD-2-Clause",
"bin": {
@@ -14064,6 +13420,7 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"punycode": "^2.1.0"
@@ -14093,16 +13450,16 @@
}
},
"node_modules/uuid": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
- "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
+ "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
- "uuid": "dist/bin/uuid"
+ "uuid": "dist/esm/bin/uuid"
}
},
"node_modules/validate-npm-package-license": {
@@ -14133,21 +13490,24 @@
"license": "MIT"
},
"node_modules/vite": {
- "version": "5.4.11",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz",
- "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==",
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.2.tgz",
+ "integrity": "sha512-hxdyZDY1CM6SNpKI4w4lcUc3Mtkd9ej4ECWVHSMrOdSinVc2zYOAppHeGc/hzmRo3pxM5blMzkuWHOJA/3NiFw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "esbuild": "^0.21.3",
- "postcss": "^8.4.43",
- "rollup": "^4.20.0"
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.6",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.6",
+ "rollup": "^4.40.0",
+ "tinyglobby": "^0.2.14"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
- "node": "^18.0.0 || >=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
@@ -14156,19 +13516,25 @@
"fsevents": "~2.3.3"
},
"peerDependencies": {
- "@types/node": "^18.0.0 || >=20.0.0",
- "less": "*",
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
"lightningcss": "^1.21.0",
- "sass": "*",
- "sass-embedded": "*",
- "stylus": "*",
- "sugarss": "*",
- "terser": "^5.4.0"
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
+ "jiti": {
+ "optional": true
+ },
"less": {
"optional": true
},
@@ -14189,46 +13555,67 @@
},
"terser": {
"optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
}
}
},
"node_modules/vite-node": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz",
- "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz",
+ "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==",
"dev": true,
"license": "MIT",
"dependencies": {
"cac": "^6.7.14",
- "debug": "^4.3.7",
- "es-module-lexer": "^1.5.4",
- "pathe": "^1.1.2",
- "vite": "^5.0.0"
+ "debug": "^4.4.1",
+ "es-module-lexer": "^1.7.0",
+ "pathe": "^2.0.3",
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
},
"bin": {
"vite-node": "vite-node.mjs"
},
"engines": {
- "node": "^18.0.0 || >=20.0.0"
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/vite-string-plugin": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.3.4.tgz",
- "integrity": "sha512-mHvcooHgZ0nVbHtj9o+c5dzD2/nclr/SOG023EFYF/zRnO8bxB63bV9WUA9X+njlgLpOwCJ3LI2IdihKoi0gZQ==",
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.4.4.tgz",
+ "integrity": "sha512-mHcdjsDaJSBnUrsAb4L+AWamWVWSFwDb5JuBbU5hAOt1w/ixaQ0EwWx2gAZiqFfpl5XlSE0WmCp2ShbdwgqsvQ==",
"dev": true,
"license": "BSD-2-Clause"
},
"node_modules/vite/node_modules/@types/estree": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
- "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
+ "node_modules/vite/node_modules/fdir": {
+ "version": "6.4.6",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
+ "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
"node_modules/vite/node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -14244,14 +13631,27 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
+ "node_modules/vite/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/vite/node_modules/rollup": {
- "version": "4.28.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.28.1.tgz",
- "integrity": "sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==",
+ "version": "4.44.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz",
+ "integrity": "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@types/estree": "1.0.6"
+ "@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
@@ -14261,70 +13661,75 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.28.1",
- "@rollup/rollup-android-arm64": "4.28.1",
- "@rollup/rollup-darwin-arm64": "4.28.1",
- "@rollup/rollup-darwin-x64": "4.28.1",
- "@rollup/rollup-freebsd-arm64": "4.28.1",
- "@rollup/rollup-freebsd-x64": "4.28.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.28.1",
- "@rollup/rollup-linux-arm-musleabihf": "4.28.1",
- "@rollup/rollup-linux-arm64-gnu": "4.28.1",
- "@rollup/rollup-linux-arm64-musl": "4.28.1",
- "@rollup/rollup-linux-loongarch64-gnu": "4.28.1",
- "@rollup/rollup-linux-powerpc64le-gnu": "4.28.1",
- "@rollup/rollup-linux-riscv64-gnu": "4.28.1",
- "@rollup/rollup-linux-s390x-gnu": "4.28.1",
- "@rollup/rollup-linux-x64-gnu": "4.28.1",
- "@rollup/rollup-linux-x64-musl": "4.28.1",
- "@rollup/rollup-win32-arm64-msvc": "4.28.1",
- "@rollup/rollup-win32-ia32-msvc": "4.28.1",
- "@rollup/rollup-win32-x64-msvc": "4.28.1",
+ "@rollup/rollup-android-arm-eabi": "4.44.2",
+ "@rollup/rollup-android-arm64": "4.44.2",
+ "@rollup/rollup-darwin-arm64": "4.44.2",
+ "@rollup/rollup-darwin-x64": "4.44.2",
+ "@rollup/rollup-freebsd-arm64": "4.44.2",
+ "@rollup/rollup-freebsd-x64": "4.44.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.44.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.44.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.44.2",
+ "@rollup/rollup-linux-arm64-musl": "4.44.2",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.44.2",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.44.2",
+ "@rollup/rollup-linux-riscv64-musl": "4.44.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.44.2",
+ "@rollup/rollup-linux-x64-gnu": "4.44.2",
+ "@rollup/rollup-linux-x64-musl": "4.44.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.44.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.44.2",
+ "@rollup/rollup-win32-x64-msvc": "4.44.2",
"fsevents": "~2.3.2"
}
},
"node_modules/vitest": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz",
- "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/expect": "2.1.8",
- "@vitest/mocker": "2.1.8",
- "@vitest/pretty-format": "^2.1.8",
- "@vitest/runner": "2.1.8",
- "@vitest/snapshot": "2.1.8",
- "@vitest/spy": "2.1.8",
- "@vitest/utils": "2.1.8",
- "chai": "^5.1.2",
- "debug": "^4.3.7",
- "expect-type": "^1.1.0",
- "magic-string": "^0.30.12",
- "pathe": "^1.1.2",
- "std-env": "^3.8.0",
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
+ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/chai": "^5.2.2",
+ "@vitest/expect": "3.2.4",
+ "@vitest/mocker": "3.2.4",
+ "@vitest/pretty-format": "^3.2.4",
+ "@vitest/runner": "3.2.4",
+ "@vitest/snapshot": "3.2.4",
+ "@vitest/spy": "3.2.4",
+ "@vitest/utils": "3.2.4",
+ "chai": "^5.2.0",
+ "debug": "^4.4.1",
+ "expect-type": "^1.2.1",
+ "magic-string": "^0.30.17",
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.2",
+ "std-env": "^3.9.0",
"tinybench": "^2.9.0",
- "tinyexec": "^0.3.1",
- "tinypool": "^1.0.1",
- "tinyrainbow": "^1.2.0",
- "vite": "^5.0.0",
- "vite-node": "2.1.8",
+ "tinyexec": "^0.3.2",
+ "tinyglobby": "^0.2.14",
+ "tinypool": "^1.1.1",
+ "tinyrainbow": "^2.0.0",
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0",
+ "vite-node": "3.2.4",
"why-is-node-running": "^2.3.0"
},
"bin": {
"vitest": "vitest.mjs"
},
"engines": {
- "node": "^18.0.0 || >=20.0.0"
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@edge-runtime/vm": "*",
- "@types/node": "^18.0.0 || >=20.0.0",
- "@vitest/browser": "2.1.8",
- "@vitest/ui": "2.1.8",
+ "@types/debug": "^4.1.12",
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "@vitest/browser": "3.2.4",
+ "@vitest/ui": "3.2.4",
"happy-dom": "*",
"jsdom": "*"
},
@@ -14332,6 +13737,9 @@
"@edge-runtime/vm": {
"optional": true
},
+ "@types/debug": {
+ "optional": true
+ },
"@types/node": {
"optional": true
},
@@ -14359,6 +13767,26 @@
"@jridgewell/sourcemap-codec": "^1.5.0"
}
},
+ "node_modules/vitest/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/vitest/node_modules/tinyexec": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+ "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/vscode-jsonrpc": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
@@ -14409,16 +13837,16 @@
"license": "MIT"
},
"node_modules/vue": {
- "version": "3.5.13",
- "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
- "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.17.tgz",
+ "integrity": "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.5.13",
- "@vue/compiler-sfc": "3.5.13",
- "@vue/runtime-dom": "3.5.13",
- "@vue/server-renderer": "3.5.13",
- "@vue/shared": "3.5.13"
+ "@vue/compiler-dom": "3.5.17",
+ "@vue/compiler-sfc": "3.5.17",
+ "@vue/runtime-dom": "3.5.17",
+ "@vue/server-renderer": "3.5.17",
+ "@vue/shared": "3.5.17"
},
"peerDependencies": {
"typescript": "*"
@@ -14449,56 +13877,43 @@
}
},
"node_modules/vue-eslint-parser": {
- "version": "9.4.3",
- "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz",
- "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==",
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz",
+ "integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
- "debug": "^4.3.4",
- "eslint-scope": "^7.1.1",
- "eslint-visitor-keys": "^3.3.0",
- "espree": "^9.3.1",
- "esquery": "^1.4.0",
- "lodash": "^4.17.21",
- "semver": "^7.3.6"
+ "debug": "^4.4.0",
+ "eslint-scope": "^8.2.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
+ "esquery": "^1.6.0",
+ "semver": "^7.6.3"
},
"engines": {
- "node": "^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://github.com/sponsors/mysticatea"
},
"peerDependencies": {
- "eslint": ">=6.0.0"
- }
- },
- "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
+ "eslint": "^8.57.0 || ^9.0.0"
}
},
- "node_modules/vue-eslint-parser/node_modules/espree": {
- "version": "9.6.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
- "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "node_modules/vue-eslint-parser/node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
"dev": true,
"license": "BSD-2-Clause",
+ "peer": true,
"dependencies": {
- "acorn": "^8.9.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.1"
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -14526,10 +13941,27 @@
}
}
},
+ "node_modules/vue-tsc": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.0.1.tgz",
+ "integrity": "sha512-UvMLQD0hAGL1g/NfEQelnSVB4H5gtf/gz2lJKjMMwWNOUmSNyWkejwJagAxEbSjtV5CPPJYslOtoSuqJ63mhdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/typescript": "2.4.17",
+ "@vue/language-core": "3.0.1"
+ },
+ "bin": {
+ "vue-tsc": "bin/vue-tsc.js"
+ },
+ "peerDependencies": {
+ "typescript": ">=5.0.0"
+ }
+ },
"node_modules/watchpack": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
- "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
+ "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==",
"license": "MIT",
"dependencies": {
"glob-to-regexp": "^0.4.1",
@@ -14540,23 +13972,20 @@
}
},
"node_modules/webidl-conversions": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
- "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=12"
- }
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "license": "BSD-2-Clause"
},
"node_modules/webpack": {
- "version": "5.97.1",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz",
- "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==",
+ "version": "5.99.9",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz",
+ "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==",
"license": "MIT",
"dependencies": {
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
"@webassemblyjs/ast": "^1.14.1",
"@webassemblyjs/wasm-edit": "^1.14.1",
"@webassemblyjs/wasm-parser": "^1.14.1",
@@ -14573,9 +14002,9 @@
"loader-runner": "^4.2.0",
"mime-types": "^2.1.27",
"neo-async": "^2.6.2",
- "schema-utils": "^3.2.0",
+ "schema-utils": "^4.3.2",
"tapable": "^2.1.1",
- "terser-webpack-plugin": "^5.3.10",
+ "terser-webpack-plugin": "^5.3.11",
"watchpack": "^2.4.1",
"webpack-sources": "^3.2.3"
},
@@ -14596,42 +14025,39 @@
}
},
"node_modules/webpack-cli": {
- "version": "5.1.4",
- "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz",
- "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz",
+ "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==",
"license": "MIT",
"dependencies": {
- "@discoveryjs/json-ext": "^0.5.0",
- "@webpack-cli/configtest": "^2.1.1",
- "@webpack-cli/info": "^2.0.2",
- "@webpack-cli/serve": "^2.0.5",
+ "@discoveryjs/json-ext": "^0.6.1",
+ "@webpack-cli/configtest": "^3.0.1",
+ "@webpack-cli/info": "^3.0.1",
+ "@webpack-cli/serve": "^3.0.1",
"colorette": "^2.0.14",
- "commander": "^10.0.1",
+ "commander": "^12.1.0",
"cross-spawn": "^7.0.3",
- "envinfo": "^7.7.3",
+ "envinfo": "^7.14.0",
"fastest-levenshtein": "^1.0.12",
"import-local": "^3.0.2",
"interpret": "^3.1.1",
"rechoir": "^0.8.0",
- "webpack-merge": "^5.7.3"
+ "webpack-merge": "^6.0.1"
},
"bin": {
"webpack-cli": "bin/cli.js"
},
"engines": {
- "node": ">=14.15.0"
+ "node": ">=18.12.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"peerDependencies": {
- "webpack": "5.x.x"
+ "webpack": "^5.82.0"
},
"peerDependenciesMeta": {
- "@webpack-cli/generators": {
- "optional": true
- },
"webpack-bundle-analyzer": {
"optional": true
},
@@ -14641,26 +14067,26 @@
}
},
"node_modules/webpack-cli/node_modules/commander": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
- "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
"license": "MIT",
"engines": {
- "node": ">=14"
+ "node": ">=18"
}
},
"node_modules/webpack-merge": {
- "version": "5.10.0",
- "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz",
- "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz",
+ "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==",
"license": "MIT",
"dependencies": {
"clone-deep": "^4.0.1",
"flat": "^5.0.2",
- "wildcard": "^2.0.0"
+ "wildcard": "^2.0.1"
},
"engines": {
- "node": ">=10.0.0"
+ "node": ">=18.0.0"
}
},
"node_modules/webpack-sources": {
@@ -14674,36 +14100,11 @@
}
},
"node_modules/webpack/node_modules/@types/estree": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
- "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"license": "MIT"
},
- "node_modules/webpack/node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
- "license": "MIT",
- "dependencies": {
- "fast-deep-equal": "^3.1.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/webpack/node_modules/ajv-keywords": {
- "version": "3.5.2",
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
- "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
- "license": "MIT",
- "peerDependencies": {
- "ajv": "^6.9.1"
- }
- },
"node_modules/webpack/node_modules/eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -14726,34 +14127,10 @@
"node": ">=4.0"
}
},
- "node_modules/webpack/node_modules/json-schema-traverse": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
- "license": "MIT"
- },
- "node_modules/webpack/node_modules/schema-utils": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
- "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
- "license": "MIT",
- "dependencies": {
- "@types/json-schema": "^7.0.8",
- "ajv": "^6.12.5",
- "ajv-keywords": "^3.5.2"
- },
- "engines": {
- "node": ">= 10.13.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/webpack"
- }
- },
"node_modules/webpack/node_modules/webpack-sources": {
- "version": "3.2.3",
- "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
- "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz",
+ "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==",
"license": "MIT",
"engines": {
"node": ">=10.13.0"
@@ -14779,12 +14156,6 @@
"webidl-conversions": "^3.0.0"
}
},
- "node_modules/whatwg-url/node_modules/webidl-conversions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
- "license": "BSD-2-Clause"
- },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -14950,6 +14321,16 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
+ "node_modules/xml-lexer": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/xml-lexer/-/xml-lexer-0.2.2.tgz",
+ "integrity": "sha512-G0i98epIwiUEiKmMcavmVdhtymW+pCAohMRgybyIME9ygfVu8QheIi+YoQh3ngiThsT0SQzJT4R0sKDEv8Ou0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "^2.0.0"
+ }
+ },
"node_modules/xml-name-validator": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
@@ -14960,6 +14341,17 @@
"node": ">=12"
}
},
+ "node_modules/xml-reader": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/xml-reader/-/xml-reader-2.4.3.tgz",
+ "integrity": "sha512-xWldrIxjeAMAu6+HSf9t50ot1uL5M+BtOidRCWHXIeewvSeIpscWCsp4Zxjk8kHHhdqFBrfK8U0EJeCcnyQ/gA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "^2.0.0",
+ "xml-lexer": "^0.2.2"
+ }
+ },
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
@@ -14970,23 +14362,16 @@
"node": ">=10"
}
},
- "node_modules/yallist": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/yaml": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz",
- "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==",
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
+ "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
"engines": {
- "node": ">= 14"
+ "node": ">= 14.6"
}
},
"node_modules/yargs": {
diff --git a/package.json b/package.json
index 6881ddb306..4b4f22351b 100644
--- a/package.json
+++ b/package.json
@@ -1,117 +1,119 @@
{
"type": "module",
"engines": {
- "node": ">= 18.0.0"
+ "node": ">= 20.0.0"
},
"dependencies": {
- "@citation-js/core": "0.7.14",
- "@citation-js/plugin-bibtex": "0.7.16",
- "@citation-js/plugin-csl": "0.7.14",
+ "@citation-js/core": "0.7.18",
+ "@citation-js/plugin-bibtex": "0.7.18",
+ "@citation-js/plugin-csl": "0.7.18",
"@citation-js/plugin-software-formats": "0.6.1",
"@github/markdown-toolbar-element": "2.2.3",
- "@github/relative-time-element": "4.4.4",
- "@github/text-expander-element": "2.8.0",
+ "@github/relative-time-element": "4.4.8",
+ "@github/text-expander-element": "2.9.2",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
- "@primer/octicons": "19.14.0",
+ "@primer/octicons": "19.15.3",
"@silverwind/vue3-calendar-heatmap": "2.0.6",
"add-asset-webpack-plugin": "3.0.0",
- "ansi_up": "6.0.2",
- "asciinema-player": "3.8.1",
- "chart.js": "4.4.7",
+ "ansi_up": "6.0.6",
+ "asciinema-player": "3.10.0",
+ "chart.js": "4.5.0",
"chartjs-adapter-dayjs-4": "1.0.4",
"chartjs-plugin-zoom": "2.2.0",
- "clippie": "4.1.4",
+ "clippie": "4.1.7",
"cropperjs": "1.6.2",
"css-loader": "7.1.2",
"dayjs": "1.11.13",
"dropzone": "6.0.0-beta.2",
- "easymde": "2.18.0",
- "esbuild-loader": "4.2.2",
- "escape-goat": "4.0.0",
- "fast-glob": "3.3.2",
- "htmx.org": "2.0.4",
- "idiomorph": "0.3.0",
+ "easymde": "2.20.0",
+ "esbuild-loader": "4.3.0",
+ "fast-glob": "3.3.3",
+ "htmx.org": "2.0.6",
+ "idiomorph": "0.7.3",
"jquery": "3.7.1",
- "katex": "0.16.18",
+ "katex": "0.16.22",
"license-checker-webpack-plugin": "0.2.1",
- "mermaid": "11.4.1",
+ "mermaid": "11.8.0",
"mini-css-extract-plugin": "2.9.2",
- "minimatch": "10.0.1",
+ "minimatch": "10.0.3",
"monaco-editor": "0.52.2",
"monaco-editor-webpack-plugin": "7.1.0",
- "pdfobject": "2.3.0",
+ "online-3d-viewer": "0.16.0",
+ "pdfobject": "2.3.1",
"perfect-debounce": "1.0.0",
- "postcss": "8.4.49",
+ "postcss": "8.5.6",
"postcss-loader": "8.1.1",
- "postcss-nesting": "13.0.1",
+ "postcss-nesting": "13.0.2",
"sortablejs": "1.15.6",
- "swagger-ui-dist": "5.18.2",
+ "swagger-ui-dist": "5.26.0",
"tailwindcss": "3.4.17",
"throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0",
"tippy.js": "6.3.7",
"toastify-js": "1.12.0",
"tributejs": "5.1.3",
- "typescript": "5.7.2",
- "uint8-to-base64": "0.2.0",
+ "typescript": "5.8.3",
+ "uint8-to-base64": "0.2.1",
"vanilla-colorful": "0.7.2",
- "vue": "3.5.13",
+ "vue": "3.5.17",
"vue-bar-graph": "2.2.0",
"vue-chartjs": "5.3.2",
"vue-loader": "17.4.2",
- "webpack": "5.97.1",
- "webpack-cli": "5.1.4",
+ "webpack": "5.99.9",
+ "webpack-cli": "6.0.1",
"wrap-ansi": "9.0.0"
},
"devDependencies": {
- "@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
- "@playwright/test": "1.49.1",
- "@silverwind/vue-tsc": "2.1.13",
- "@stoplight/spectral-cli": "6.14.2",
- "@stylistic/eslint-plugin-js": "2.12.1",
- "@stylistic/stylelint-plugin": "3.1.1",
+ "@eslint-community/eslint-plugin-eslint-comments": "4.5.0",
+ "@playwright/test": "1.53.2",
+ "@stoplight/spectral-cli": "6.15.0",
+ "@stylistic/eslint-plugin-js": "3.1.0",
+ "@stylistic/stylelint-plugin": "3.1.3",
"@types/dropzone": "5.7.9",
"@types/jquery": "3.5.32",
"@types/katex": "0.16.7",
- "@types/license-checker-webpack-plugin": "0.2.4",
+ "@types/license-checker-webpack-plugin": "0.2.5",
"@types/pdfobject": "2.2.5",
"@types/sortablejs": "1.15.8",
- "@types/swagger-ui-dist": "3.30.5",
+ "@types/swagger-ui-dist": "3.30.6",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
- "@types/toastify-js": "1.12.3",
- "@typescript-eslint/eslint-plugin": "8.18.1",
- "@typescript-eslint/parser": "8.18.1",
- "@vitejs/plugin-vue": "5.2.1",
+ "@types/toastify-js": "1.12.4",
+ "@typescript-eslint/eslint-plugin": "8.35.1",
+ "@typescript-eslint/parser": "8.35.1",
+ "@vitejs/plugin-vue": "6.0.0",
+ "@vitest/eslint-plugin": "1.3.4",
"eslint": "8.57.0",
- "eslint-import-resolver-typescript": "3.7.0",
+ "eslint-import-resolver-typescript": "4.4.4",
"eslint-plugin-array-func": "4.0.0",
"eslint-plugin-github": "5.0.2",
- "eslint-plugin-import-x": "4.6.1",
- "eslint-plugin-no-jquery": "3.1.0",
+ "eslint-plugin-import-x": "4.16.1",
+ "eslint-plugin-no-jquery": "3.1.1",
"eslint-plugin-no-use-extend-native": "0.5.0",
- "eslint-plugin-playwright": "2.1.0",
- "eslint-plugin-regexp": "2.7.0",
- "eslint-plugin-sonarjs": "3.0.1",
+ "eslint-plugin-playwright": "2.2.0",
+ "eslint-plugin-regexp": "2.9.0",
+ "eslint-plugin-sonarjs": "3.0.4",
"eslint-plugin-unicorn": "56.0.1",
- "eslint-plugin-vitest": "0.4.1",
- "eslint-plugin-vitest-globals": "1.5.0",
- "eslint-plugin-vue": "9.32.0",
- "eslint-plugin-vue-scoped-css": "2.9.0",
- "eslint-plugin-wc": "2.2.0",
- "happy-dom": "15.11.7",
- "markdownlint-cli": "0.43.0",
- "nolyfill": "1.0.43",
- "postcss-html": "1.7.0",
- "stylelint": "16.12.0",
+ "eslint-plugin-vue": "10.3.0",
+ "eslint-plugin-vue-scoped-css": "2.11.0",
+ "eslint-plugin-wc": "3.0.1",
+ "happy-dom": "18.0.1",
+ "markdownlint-cli": "0.45.0",
+ "material-icon-theme": "5.24.0",
+ "nolyfill": "1.0.44",
+ "postcss-html": "1.8.0",
+ "stylelint": "16.21.1",
+ "stylelint-config-recommended": "16.0.0",
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
- "stylelint-declaration-strict-value": "1.10.6",
+ "stylelint-declaration-strict-value": "1.10.11",
+ "stylelint-define-config": "16.21.0",
"stylelint-value-no-unknown-custom-properties": "6.0.1",
- "svgo": "3.3.2",
- "type-fest": "4.30.2",
- "updates": "16.4.1",
- "vite-string-plugin": "1.3.4",
- "vitest": "2.1.8"
+ "svgo": "4.0.0",
+ "type-fest": "4.41.0",
+ "updates": "16.4.2",
+ "vite-string-plugin": "1.4.4",
+ "vitest": "3.2.4",
+ "vue-tsc": "3.0.1"
},
"browserslist": [
"defaults"
diff --git a/poetry.lock b/poetry.lock
index 5b9029eb8c..7c3f690175 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,14 +1,15 @@
-# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
+# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
[[package]]
name = "click"
-version = "8.1.7"
+version = "8.1.8"
description = "Composable command line interface toolkit"
optional = false
python-versions = ">=3.7"
+groups = ["dev"]
files = [
- {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
- {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
+ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
+ {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
]
[package.dependencies]
@@ -20,6 +21,7 @@ version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+groups = ["dev"]
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
@@ -27,12 +29,14 @@ files = [
[[package]]
name = "cssbeautifier"
-version = "1.15.1"
+version = "1.15.4"
description = "CSS unobfuscator and beautifier."
optional = false
python-versions = "*"
+groups = ["dev"]
files = [
- {file = "cssbeautifier-1.15.1.tar.gz", hash = "sha256:9f7064362aedd559c55eeecf6b6bed65e05f33488dcbe39044f0403c26e1c006"},
+ {file = "cssbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:78c84d5e5378df7d08622bbd0477a1abdbd209680e95480bf22f12d5701efc98"},
+ {file = "cssbeautifier-1.15.4.tar.gz", hash = "sha256:9bb08dc3f64c101a01677f128acf01905914cf406baf87434dcde05b74c0acf5"},
]
[package.dependencies]
@@ -42,33 +46,34 @@ six = ">=1.13.0"
[[package]]
name = "djlint"
-version = "1.36.3"
+version = "1.36.4"
description = "HTML Template Linter and Formatter"
optional = false
python-versions = ">=3.9"
+groups = ["dev"]
files = [
- {file = "djlint-1.36.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ae7c620b58e16d6bf003bd7de3f71376a7a3daa79dc02e77f3726d5a75243f2"},
- {file = "djlint-1.36.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e155ce0970d4a28d0a2e9f2e106733a2ad05910eee90e056b056d48049e4a97b"},
- {file = "djlint-1.36.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e8bb0406e60cc696806aa6226df137618f3889c72f2dbdfa76c908c99151579"},
- {file = "djlint-1.36.3-cp310-cp310-win_amd64.whl", hash = "sha256:76d32faf988ad58ef2e7a11d04046fc984b98391761bf1b61f9a6044da53d414"},
- {file = "djlint-1.36.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:32f7a5834000fff22e94d1d35f95aaf2e06f2af2cae18af0ed2a4e215d60e730"},
- {file = "djlint-1.36.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3eb1b9c0be499e63e8822a051e7e55f188ff1ab8172a85d338a8ae21c872060e"},
- {file = "djlint-1.36.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c2e0dd1f26eb472b8c84eb70d6482877b6497a1fd031d7534864088f016d5ea"},
- {file = "djlint-1.36.3-cp311-cp311-win_amd64.whl", hash = "sha256:a06b531ab9d049c46ad4d2365d1857004a1a9dd0c23c8eae94aa0d233c6ec00d"},
- {file = "djlint-1.36.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e66361a865e5e5a4bbcb40f56af7f256fd02cbf9d48b763a40172749cc294084"},
- {file = "djlint-1.36.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:36e102b80d83e9ac2e6be9a9ded32fb925945f6dbc7a7156e4415de1b0aa0dba"},
- {file = "djlint-1.36.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ac4b7370d80bd82281e57a470de8923ac494ffb571b89d8787cef57c738c69a"},
- {file = "djlint-1.36.3-cp312-cp312-win_amd64.whl", hash = "sha256:107cc56bbef13d60cc0ae774a4d52881bf98e37c02412e573827a3e549217e3a"},
- {file = "djlint-1.36.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2a9f51971d6e63c41ea9b3831c928e1f21ae6fe57e87a3452cfe672d10232433"},
- {file = "djlint-1.36.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:080c98714b55d8f0fef5c42beaee8247ebb2e3d46b0936473bd6c47808bb6302"},
- {file = "djlint-1.36.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f65a80e0b5cb13d357ea51ca6570b34c2d9d18974c1e57142de760ea27d49ed0"},
- {file = "djlint-1.36.3-cp313-cp313-win_amd64.whl", hash = "sha256:95ef6b67ef7f2b90d9434bba37d572031079001dc8524add85c00ef0386bda1e"},
- {file = "djlint-1.36.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e2317a32094d525bc41cd11c8dc064bf38d1b442c99cc3f7c4a2616b5e6ce6e"},
- {file = "djlint-1.36.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e82266c28793cd15f97b93535d72bfbc77306eaaf6b210dd90910383a814ee6c"},
- {file = "djlint-1.36.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01b2101c2d1b079e8d545e6d9d03487fcca14d2371e44cbfdedee15b0bf4567c"},
- {file = "djlint-1.36.3-cp39-cp39-win_amd64.whl", hash = "sha256:15cde63ef28beb5194ff4137883025f125676ece1b574b64a3e1c6daed734639"},
- {file = "djlint-1.36.3-py3-none-any.whl", hash = "sha256:0c05cd5b76785de2c41a2420c06ffd112800bfc0f9c0f399cc7cea7c42557f4c"},
- {file = "djlint-1.36.3.tar.gz", hash = "sha256:d85735da34bc7ac93ad8ef9b4822cc2a23d5f0ce33f25438737b8dca1d404f78"},
+ {file = "djlint-1.36.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2dfb60883ceb92465201bfd392291a7597c6752baede6fbb6f1980cac8d6c5c"},
+ {file = "djlint-1.36.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4bc6a1320c0030244b530ac200642f883d3daa451a115920ef3d56d08b644292"},
+ {file = "djlint-1.36.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3164a048c7bb0baf042387b1e33f9bbbf99d90d1337bb4c3d66eb0f96f5400a1"},
+ {file = "djlint-1.36.4-cp310-cp310-win_amd64.whl", hash = "sha256:3196d5277da5934962d67ad6c33a948ba77a7b6eadf064648bef6ee5f216b03c"},
+ {file = "djlint-1.36.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d68da0ed10ee9ca1e32e225cbb8e9b98bf7e6f8b48a8e4836117b6605b88cc7"},
+ {file = "djlint-1.36.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c0478d5392247f1e6ee29220bbdbf7fb4e1bc0e7e83d291fda6fb926c1787ba7"},
+ {file = "djlint-1.36.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:962f7b83aee166e499eff916d631c6dde7f1447d7610785a60ed2a75a5763483"},
+ {file = "djlint-1.36.4-cp311-cp311-win_amd64.whl", hash = "sha256:53cbc450aa425c832f09bc453b8a94a039d147b096740df54a3547fada77ed08"},
+ {file = "djlint-1.36.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff9faffd7d43ac20467493fa71d5355b5b330a00ade1c4d1e859022f4195223b"},
+ {file = "djlint-1.36.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:79489e262b5ac23a8dfb7ca37f1eea979674cfc2d2644f7061d95bea12c38f7e"},
+ {file = "djlint-1.36.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e58c5fa8c6477144a0be0a87273706a059e6dd0d6efae01146ae8c29cdfca675"},
+ {file = "djlint-1.36.4-cp312-cp312-win_amd64.whl", hash = "sha256:bb6903777bf3124f5efedcddf1f4716aef097a7ec4223fc0fa54b865829a6e08"},
+ {file = "djlint-1.36.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ead475013bcac46095b1bbc8cf97ed2f06e83422335734363f8a76b4ba7e47c2"},
+ {file = "djlint-1.36.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6c601dfa68ea253311deb4a29a7362b7a64933bdfcfb5a06618f3e70ad1fa835"},
+ {file = "djlint-1.36.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bda5014f295002363381969864addeb2db13955f1b26e772657c3b273ed7809f"},
+ {file = "djlint-1.36.4-cp313-cp313-win_amd64.whl", hash = "sha256:16ce37e085afe5a30953b2bd87cbe34c37843d94c701fc68a2dda06c1e428ff4"},
+ {file = "djlint-1.36.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:89678661888c03d7bc6cadd75af69db29962b5ecbf93a81518262f5c48329f04"},
+ {file = "djlint-1.36.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b01a98df3e1ab89a552793590875bc6e954cad661a9304057db75363d519fa0"},
+ {file = "djlint-1.36.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dabbb4f7b93223d471d09ae34ed515fef98b2233cbca2449ad117416c44b1351"},
+ {file = "djlint-1.36.4-cp39-cp39-win_amd64.whl", hash = "sha256:7a483390d17e44df5bc23dcea29bdf6b63f3ed8b4731d844773a4829af4f5e0b"},
+ {file = "djlint-1.36.4-py3-none-any.whl", hash = "sha256:e9699b8ac3057a6ed04fb90835b89bee954ed1959c01541ce4f8f729c938afdd"},
+ {file = "djlint-1.36.4.tar.gz", hash = "sha256:17254f218b46fe5a714b224c85074c099bcb74e3b2e1f15c2ddc2cf415a408a1"},
]
[package.dependencies]
@@ -82,25 +87,30 @@ pyyaml = ">=6"
regex = ">=2023"
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
tqdm = ">=4.62.2"
+typing-extensions = {version = ">=3.6.6", markers = "python_version < \"3.11\""}
[[package]]
name = "editorconfig"
-version = "0.12.4"
+version = "0.17.0"
description = "EditorConfig File Locator and Interpreter for Python"
optional = false
python-versions = "*"
+groups = ["dev"]
files = [
- {file = "EditorConfig-0.12.4.tar.gz", hash = "sha256:24857fa1793917dd9ccf0c7810a07e05404ce9b823521c7dce22a4fb5d125f80"},
+ {file = "EditorConfig-0.17.0-py3-none-any.whl", hash = "sha256:fe491719c5f65959ec00b167d07740e7ffec9a3f362038c72b289330b9991dfc"},
+ {file = "editorconfig-0.17.0.tar.gz", hash = "sha256:8739052279699840065d3a9f5c125d7d5a98daeefe53b0e5274261d77cb49aa2"},
]
[[package]]
name = "jsbeautifier"
-version = "1.15.1"
+version = "1.15.4"
description = "JavaScript unobfuscator and beautifier."
optional = false
python-versions = "*"
+groups = ["dev"]
files = [
- {file = "jsbeautifier-1.15.1.tar.gz", hash = "sha256:ebd733b560704c602d744eafc839db60a1ee9326e30a2a80c4adb8718adc1b24"},
+ {file = "jsbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:72f65de312a3f10900d7685557f84cb61a9733c50dcc27271a39f5b0051bf528"},
+ {file = "jsbeautifier-1.15.4.tar.gz", hash = "sha256:5bb18d9efb9331d825735fbc5360ee8f1aac5e52780042803943aa7f854f7592"},
]
[package.dependencies]
@@ -109,17 +119,18 @@ six = ">=1.13.0"
[[package]]
name = "json5"
-version = "0.10.0"
+version = "0.12.0"
description = "A Python implementation of the JSON5 data format."
optional = false
python-versions = ">=3.8.0"
+groups = ["dev"]
files = [
- {file = "json5-0.10.0-py3-none-any.whl", hash = "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa"},
- {file = "json5-0.10.0.tar.gz", hash = "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559"},
+ {file = "json5-0.12.0-py3-none-any.whl", hash = "sha256:6d37aa6c08b0609f16e1ec5ff94697e2cbbfbad5ac112afa05794da9ab7810db"},
+ {file = "json5-0.12.0.tar.gz", hash = "sha256:0b4b6ff56801a1c7dc817b0241bca4ce474a0e6a163bfef3fc594d3fd263ff3a"},
]
[package.extras]
-dev = ["build (==1.2.2.post1)", "coverage (==7.5.3)", "mypy (==1.13.0)", "pip (==24.3.1)", "pylint (==3.2.3)", "ruff (==0.7.3)", "twine (==5.1.1)", "uv (==0.5.1)"]
+dev = ["build (==1.2.2.post1)", "coverage (==7.5.4) ; python_version < \"3.9\"", "coverage (==7.8.0) ; python_version >= \"3.9\"", "mypy (==1.14.1) ; python_version < \"3.9\"", "mypy (==1.15.0) ; python_version >= \"3.9\"", "pip (==25.0.1)", "pylint (==3.2.7) ; python_version < \"3.9\"", "pylint (==3.3.6) ; python_version >= \"3.9\"", "ruff (==0.11.2)", "twine (==6.1.0)", "uv (==0.6.11)"]
[[package]]
name = "pathspec"
@@ -127,6 +138,7 @@ version = "0.12.1"
description = "Utility library for gitignore style pattern matching of file paths."
optional = false
python-versions = ">=3.8"
+groups = ["dev"]
files = [
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
@@ -138,6 +150,7 @@ version = "6.0.2"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.8"
+groups = ["dev"]
files = [
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
@@ -200,6 +213,7 @@ version = "2024.11.6"
description = "Alternative regular expression module, to replace re."
optional = false
python-versions = ">=3.8"
+groups = ["dev"]
files = [
{file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"},
{file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"},
@@ -303,6 +317,7 @@ version = "1.17.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+groups = ["dev"]
files = [
{file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
@@ -314,6 +329,8 @@ version = "2.2.1"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
+groups = ["dev"]
+markers = "python_version == \"3.10\""
files = [
{file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
{file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
@@ -355,6 +372,7 @@ version = "4.67.1"
description = "Fast, Extensible Progress Meter"
optional = false
python-versions = ">=3.7"
+groups = ["dev"]
files = [
{file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"},
{file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"},
@@ -371,14 +389,28 @@ slack = ["slack-sdk"]
telegram = ["requests"]
[[package]]
+name = "typing-extensions"
+version = "4.13.2"
+description = "Backported and Experimental Type Hints for Python 3.8+"
+optional = false
+python-versions = ">=3.8"
+groups = ["dev"]
+markers = "python_version == \"3.10\""
+files = [
+ {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"},
+ {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"},
+]
+
+[[package]]
name = "yamllint"
-version = "1.35.1"
+version = "1.37.1"
description = "A linter for YAML files."
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["dev"]
files = [
- {file = "yamllint-1.35.1-py3-none-any.whl", hash = "sha256:2e16e504bb129ff515b37823b472750b36b6de07963bd74b307341ef5ad8bdc3"},
- {file = "yamllint-1.35.1.tar.gz", hash = "sha256:7a003809f88324fd2c877734f2d575ee7881dd9043360657cc8049c809eba6cd"},
+ {file = "yamllint-1.37.1-py3-none-any.whl", hash = "sha256:364f0d79e81409f591e323725e6a9f4504c8699ddf2d7263d8d2b539cd66a583"},
+ {file = "yamllint-1.37.1.tar.gz", hash = "sha256:81f7c0c5559becc8049470d86046b36e96113637bcbe4753ecef06977c00245d"},
]
[package.dependencies]
@@ -389,6 +421,6 @@ pyyaml = "*"
dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"]
[metadata]
-lock-version = "2.0"
+lock-version = "2.1"
python-versions = "^3.10"
-content-hash = "01b1e2f910276dd20a70ebb665c83415c37531709d90874f5b7a86a5305e2369"
+content-hash = "ec65cbc253ccb822e50bb7bdb9a3185a5ab781024175a4d5a339055a06207af6"
diff --git a/poetry.toml b/poetry.toml
index 0299355b5d..6eda9f8eff 100644
--- a/poetry.toml
+++ b/poetry.toml
@@ -1,4 +1,3 @@
[virtualenvs]
in-project = true
options.no-pip = true
-options.no-setuptools = true
diff --git a/public/assets/img/feishu.png b/public/assets/img/feishu.png
deleted file mode 100644
index 2c3ab74413..0000000000
--- a/public/assets/img/feishu.png
+++ /dev/null
Binary files differ
diff --git a/public/assets/img/svg/gitea-chef.svg b/public/assets/img/svg/gitea-chef.svg
index c5e8a721cc..8fd8ed325d 100644
--- a/public/assets/img/svg/gitea-chef.svg
+++ b/public/assets/img/svg/gitea-chef.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" class="svg gitea-chef" width="16" height="16" aria-hidden="true"><g fill="none" fill-rule="evenodd"><path fill="#435363" d="M18 25.8c-4.3 0-7.7-3.6-7.7-8s3.4-7.9 7.7-7.9c3.5 0 6.4 2.4 7.3 5.7h3c-1-5-5.2-8.7-10.3-8.7-5.9 0-10.6 4.9-10.6 10.9 0 6.1 4.7 11 10.6 11 5.1 0 9.3-3.7 10.3-8.7h-3c-.9 3.3-3.8 5.7-7.3 5.7"/><path fill="#435363" d="M12.8 23.2c1.3 1.4 3.1 2.3 5.2 2.3v-3.2c-1.2 0-2.3-.5-3.1-1.3z"/><path fill="#F38B00" d="M10.6 17.8c0 1.1.3 2.2.6 3.1l2.9-1.3c-.3-.5-.4-1.1-.4-1.8 0-2.4 1.9-4.4 4.3-4.4v-3.2c-4.1 0-7.4 3.4-7.4 7.6"/><path fill="#435363" d="m20.6 10.7-1.1 3c.9.4 1.7 1.1 2.2 1.9H25c-.7-2.2-2.3-4-4.4-4.9"/><path fill="#F38B00" d="m19.5 22 1.1 2.9c2.1-.8 3.7-2.6 4.4-4.8h-3.3c-.5.8-1.3 1.5-2.2 1.9"/><path fill="#435363" d="M4.4 22.1c-.1-.2-.1-.3-.1-.5-.1-.2-.1-.3-.2-.5V21c0-.1 0-.3-.1-.4v-.5c-.1-.1-.1-.2-.1-.3-.1-.6-.1-1.3-.1-2H.9c0 .8 0 1.5.1 2.2 0 .2.1.4.1.6v.1c0 .2.1.4.1.5s0 .2.1.3v.3c.1.1.1.2.1.4 0 0 .1.1.1.2 0 .2 0 .3.1.4v.2c.2.7.5 1.3.7 2L5 23.8c-.2-.6-.4-1.1-.6-1.7"/><path fill="#F38B00" d="M18 32.6c-3.9 0-7.5-1.7-10.1-4.4l-2 2.2c3.1 3.2 7.3 5.2 12.1 5.2 8.7 0 15.8-6.8 16.9-15.5H32c-1.1 7-7 12.5-14 12.5M18 3.1c3.1 0 6.1 1.1 8.4 2.9l1.8-2.4C25.3 1.4 21.8.1 18 .1 10.7.1 4.5 4.8 2.1 11.4l2.7 1.1C6.8 7 12 3.1 18 3.1"/><path fill="#435363" d="M32 15.6h2.9c-.3-2.6-1.2-5-2.5-7.2L30 10c1 1.7 1.7 3.6 2 5.6"/><path fill="#F38B00" d="M28.7 15.6h2.9c-.8-5.1-4.1-9.3-8.6-11.1l-1.1 2.8c3.5 1.3 6 4.5 6.8 8.3"/><path fill="#435363" d="M18 6.5v-3c-5.9 0-10.9 3.8-12.9 9.1l2.7 1.1C9.4 9.5 13.3 6.5 18 6.5"/><path fill="#F38B00" d="M7 17.8H4.1c0 6.1 3.6 11.2 8.7 13.4l1.1-2.8C9.9 26.7 7 22.6 7 17.8"/><path fill="#435363" d="M18 29.2v3c6.9 0 12.6-5.3 13.6-12.1h-2.9c-1 5.2-5.4 9.1-10.7 9.1"/></g></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" class="svg gitea-chef" width="16" height="16" aria-hidden="true"><g fill="none" fill-rule="evenodd"><path fill="#435363" d="M18 25.8c-4.3 0-7.7-3.6-7.7-8s3.4-7.9 7.7-7.9c3.5 0 6.4 2.4 7.3 5.7h3c-1-5-5.2-8.7-10.3-8.7-5.9 0-10.6 4.9-10.6 10.9 0 6.1 4.7 11 10.6 11 5.1 0 9.3-3.7 10.3-8.7h-3c-.9 3.3-3.8 5.7-7.3 5.7"/><path fill="#435363" d="M12.8 23.2c1.3 1.4 3.1 2.3 5.2 2.3v-3.2c-1.2 0-2.3-.5-3.1-1.3z"/><path fill="#f38b00" d="M10.6 17.8c0 1.1.3 2.2.6 3.1l2.9-1.3c-.3-.5-.4-1.1-.4-1.8 0-2.4 1.9-4.4 4.3-4.4v-3.2c-4.1 0-7.4 3.4-7.4 7.6"/><path fill="#435363" d="m20.6 10.7-1.1 3c.9.4 1.7 1.1 2.2 1.9H25c-.7-2.2-2.3-4-4.4-4.9"/><path fill="#f38b00" d="m19.5 22 1.1 2.9c2.1-.8 3.7-2.6 4.4-4.8h-3.3c-.5.8-1.3 1.5-2.2 1.9"/><path fill="#435363" d="M4.4 22.1c-.1-.2-.1-.3-.1-.5-.1-.2-.1-.3-.2-.5V21c0-.1 0-.3-.1-.4v-.5c-.1-.1-.1-.2-.1-.3-.1-.6-.1-1.3-.1-2H.9c0 .8 0 1.5.1 2.2 0 .2.1.4.1.6v.1c0 .2.1.4.1.5s0 .2.1.3v.3c.1.1.1.2.1.4 0 0 .1.1.1.2 0 .2 0 .3.1.4v.2c.2.7.5 1.3.7 2L5 23.8c-.2-.6-.4-1.1-.6-1.7"/><path fill="#f38b00" d="M18 32.6c-3.9 0-7.5-1.7-10.1-4.4l-2 2.2c3.1 3.2 7.3 5.2 12.1 5.2 8.7 0 15.8-6.8 16.9-15.5H32c-1.1 7-7 12.5-14 12.5M18 3.1c3.1 0 6.1 1.1 8.4 2.9l1.8-2.4C25.3 1.4 21.8.1 18 .1 10.7.1 4.5 4.8 2.1 11.4l2.7 1.1C6.8 7 12 3.1 18 3.1"/><path fill="#435363" d="M32 15.6h2.9c-.3-2.6-1.2-5-2.5-7.2L30 10c1 1.7 1.7 3.6 2 5.6"/><path fill="#f38b00" d="M28.7 15.6h2.9c-.8-5.1-4.1-9.3-8.6-11.1l-1.1 2.8c3.5 1.3 6 4.5 6.8 8.3"/><path fill="#435363" d="M18 6.5v-3c-5.9 0-10.9 3.8-12.9 9.1l2.7 1.1C9.4 9.5 13.3 6.5 18 6.5"/><path fill="#f38b00" d="M7 17.8H4.1c0 6.1 3.6 11.2 8.7 13.4l1.1-2.8C9.9 26.7 7 22.6 7 17.8"/><path fill="#435363" d="M18 29.2v3c6.9 0 12.6-5.3 13.6-12.1h-2.9c-1 5.2-5.4 9.1-10.7 9.1"/></g></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/gitea-codecommit.svg b/public/assets/img/svg/gitea-codecommit.svg
index b44847d9e1..35d0b39c03 100644
--- a/public/assets/img/svg/gitea-codecommit.svg
+++ b/public/assets/img/svg/gitea-codecommit.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="-10 -10 100 100" class="svg gitea-codecommit" width="16" height="16" aria-hidden="true"><g fill="none" fill-rule="evenodd"><path fill="#C925D1" d="M0 0h80v80H0z"/><path fill="#FFF" d="M26.628 28.105h-2.017v-6.982c0-.558.36-.99.926-.99l7.144-.007v1.994H27.95l4.862 4.819-1.445 1.434-4.806-4.728zm28.07 10.867 1.869.827-6.541 14.446-1.868-.827zm1.311 10.493 4.003-2.89-3.526-3.535 1.458-1.422 4.36 4.373a1.002 1.002 0 0 1-.126 1.527l-4.963 3.58zm-9.043-8.802 1.205 1.633-4.061 2.932 3.538 3.536-1.454 1.424-4.374-4.373a1 1 0 0 1 .124-1.528zM69 24.13v42.858c0 .56-.458 1.012-1.024 1.012h-31.26c-.272 0-.53-.107-.723-.297a.96.96 0 0 1-.285-.7V55.034h2.018v10.971h29.256V25.113H37.726v-1.995h30.25c.566 0 1.024.453 1.024 1.012M33.182 34.588c0-1.927 1.585-3.495 3.535-3.495s3.535 1.568 3.535 3.495-1.585 3.495-3.535 3.495-3.535-1.568-3.535-3.495M17.549 66.009c-1.95 0-3.535-1.568-3.535-3.495s1.585-3.494 3.535-3.494 3.535 1.567 3.535 3.494-1.585 3.495-3.535 3.495m-3.535-23.442c0-1.927 1.585-3.495 3.535-3.495 1.982 0 3.535 1.535 3.535 3.495 0 1.927-1.585 3.495-3.535 3.495s-3.535-1.568-3.535-3.495m.004-25.081c0-1.925 1.584-3.491 3.53-3.491 1.948 0 3.532 1.566 3.532 3.49s-1.584 3.491-3.531 3.491-3.531-1.566-3.531-3.49m23.708 29.762v-7.276c2.57-.477 4.535-2.708 4.535-5.384 0-3.022-2.487-5.482-5.544-5.482s-5.545 2.46-5.545 5.482c0 2.676 1.966 4.907 4.536 5.384v7.276c0 1.163-.786 2.218-1.98 2.686l-10.451 4.1c-1.673.657-2.903 1.948-3.434 3.496-.433-.195-.801-.336-1.285-.416v-9.146c2.623-.433 4.535-2.687 4.535-5.401 0-2.764-1.878-4.972-4.535-5.393V22.889c2.626-.431 4.54-2.688 4.54-5.403 0-3.025-2.49-5.486-5.55-5.486S12 14.46 12 17.486c0 2.64 2.022 4.85 4.54 5.369v14.347c-2.515.518-4.536 2.727-4.536 5.365s2.02 4.846 4.536 5.365v9.217c-2.515.52-4.536 2.727-4.536 5.365 0 3.022 2.488 5.482 5.545 5.482s5.544-2.46 5.544-5.482a5.43 5.43 0 0 0-1.458-3.693c.167-1.27 1.066-2.384 2.397-2.905l10.45-4.1c1.98-.777 3.244-2.57 3.244-4.568"/></g></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="-10 -10 100 100" class="svg gitea-codecommit" width="16" height="16" aria-hidden="true"><g fill="none" fill-rule="evenodd"><path fill="#c925d1" d="M0 0h80v80H0z"/><path fill="#fff" d="M26.628 28.105h-2.017v-6.982c0-.558.36-.99.926-.99l7.144-.007v1.994H27.95l4.862 4.819-1.445 1.434-4.806-4.728zm28.07 10.867 1.869.827-6.541 14.446-1.868-.827zm1.311 10.493 4.003-2.89-3.526-3.535 1.458-1.422 4.36 4.373a1.002 1.002 0 0 1-.126 1.527l-4.963 3.58zm-9.043-8.802 1.205 1.633-4.061 2.932 3.538 3.536-1.454 1.424-4.374-4.373a1 1 0 0 1 .124-1.528zM69 24.13v42.858c0 .56-.458 1.012-1.024 1.012h-31.26c-.272 0-.53-.107-.723-.297a.96.96 0 0 1-.285-.7V55.034h2.018v10.971h29.256V25.113H37.726v-1.995h30.25c.566 0 1.024.453 1.024 1.012M33.182 34.588c0-1.927 1.585-3.495 3.535-3.495s3.535 1.568 3.535 3.495-1.585 3.495-3.535 3.495-3.535-1.568-3.535-3.495M17.549 66.009c-1.95 0-3.535-1.568-3.535-3.495s1.585-3.494 3.535-3.494 3.535 1.567 3.535 3.494-1.585 3.495-3.535 3.495m-3.535-23.442c0-1.927 1.585-3.495 3.535-3.495 1.982 0 3.535 1.535 3.535 3.495 0 1.927-1.585 3.495-3.535 3.495s-3.535-1.568-3.535-3.495m.004-25.081c0-1.925 1.584-3.491 3.53-3.491 1.948 0 3.532 1.566 3.532 3.49s-1.584 3.491-3.531 3.491-3.531-1.566-3.531-3.49m23.708 29.762v-7.276c2.57-.477 4.535-2.708 4.535-5.384 0-3.022-2.487-5.482-5.544-5.482s-5.545 2.46-5.545 5.482c0 2.676 1.966 4.907 4.536 5.384v7.276c0 1.163-.786 2.218-1.98 2.686l-10.451 4.1c-1.673.657-2.903 1.948-3.434 3.496-.433-.195-.801-.336-1.285-.416v-9.146c2.623-.433 4.535-2.687 4.535-5.401 0-2.764-1.878-4.972-4.535-5.393V22.889c2.626-.431 4.54-2.688 4.54-5.403 0-3.025-2.49-5.486-5.55-5.486S12 14.46 12 17.486c0 2.64 2.022 4.85 4.54 5.369v14.347c-2.515.518-4.536 2.727-4.536 5.365s2.02 4.846 4.536 5.365v9.217c-2.515.52-4.536 2.727-4.536 5.365 0 3.022 2.488 5.482 5.545 5.482s5.544-2.46 5.544-5.482a5.43 5.43 0 0 0-1.458-3.693c.167-1.27 1.066-2.384 2.397-2.905l10.45-4.1c1.98-.777 3.244-2.57 3.244-4.568"/></g></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/gitea-debian.svg b/public/assets/img/svg/gitea-debian.svg
index fa2f2f49dc..e92d0b6937 100644
--- a/public/assets/img/svg/gitea-debian.svg
+++ b/public/assets/img/svg/gitea-debian.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 210 260" class="svg gitea-debian" width="16" height="16" aria-hidden="true"><g fill="#D70751"><path d="M124.525 137.053c-4.125.058.78 2.125 6.165 2.954a55 55 0 0 0 4.04-3.479c-3.354.821-6.765.838-10.205.525m22.14-5.52c2.457-3.389 4.246-7.102 4.878-10.939-.551 2.736-2.035 5.099-3.435 7.592-7.711 4.854-.726-2.883-.004-5.824-8.29 10.436-1.138 6.257-1.439 9.171m8.174-21.265c.497-7.428-1.462-5.08-2.121-2.245.766.4 1.377 5.237 2.121 2.245M108.883 8.736c2.201.395 4.757.698 4.398 1.224 2.407-.528 2.954-1.015-4.398-1.224M113.281 9.96l-1.556.32 1.448-.127z"/><path d="M181.93 113.085c.247 6.671-1.95 9.907-3.932 15.637l-3.564 1.781c-2.919 5.666.282 3.598-1.807 8.105-4.556 4.049-13.823 12.67-16.789 13.457-2.163-.047 1.469-2.554 1.943-3.537-6.097 4.188-4.894 6.285-14.217 8.83l-.273-.607c-23.001 10.818-54.947-10.622-54.526-39.876-.246 1.857-.698 1.393-1.208 2.144-1.186-15.052 6.952-30.17 20.675-36.343 13.427-6.646 29.163-3.918 38.78 5.044-5.282-6.92-15.795-14.254-28.255-13.568-12.208.193-23.625 7.95-27.436 16.369-6.253 3.938-6.979 15.177-9.704 17.233-3.665 26.943 6.896 38.583 24.762 52.275 2.812 1.896.792 2.184 1.173 3.627-5.936-2.779-11.372-6.976-15.841-12.114 2.372 3.473 4.931 6.847 8.239 9.499-5.596-1.897-13.074-13.563-15.256-14.038 9.647 17.274 39.142 30.295 54.587 23.836-7.146.263-16.226.146-24.256-2.822-3.371-1.734-7.958-5.331-7.14-6.003 21.079 7.875 42.854 5.965 61.09-8.655 4.641-3.614 9.709-9.761 11.173-9.846-2.206 3.317.377 1.596-1.318 4.523 4.625-7.456-2.008-3.035 4.779-12.877l2.507 3.453c-.931-6.188 7.687-13.704 6.813-23.492 1.975-2.994 2.206 3.22.107 10.107 2.912-7.64.767-8.867 1.516-15.171.81 2.118 1.867 4.37 2.412 6.606-1.895-7.382 1.948-12.433 2.898-16.724-.937-.415-2.928 3.264-3.383-5.457.065-3.788 1.054-1.985 1.435-2.917-.744-.427-2.694-3.33-3.88-8.9.86-1.308 2.3 3.393 3.47 3.586-.753-4.429-2.049-7.805-2.103-11.202-3.421-7.149-1.211.953-3.985-3.069-3.641-11.357 3.021-2.637 3.47-7.796 5.52 7.995 8.667 20.387 10.11 25.519-1.103-6.258-2.883-12.32-5.058-18.185 1.677.705-2.699-12.875 2.18-3.882-5.21-19.172-22.302-37.087-38.025-45.493 1.924 1.76 4.354 3.971 3.481 4.317-7.819-4.656-6.444-5.018-7.565-6.985-6.369-2.591-6.788.208-11.007.004-12.005-6.368-14.318-5.69-25.368-9.681l.502 2.349c-7.953-2.649-9.265 1.005-17.862.009-.523-.409 2.753-1.479 5.452-1.871-7.69 1.015-7.329-1.515-14.854.279 1.855-1.301 3.815-2.162 5.793-3.269-6.271.381-14.971 3.649-12.286.677-10.235 4.569-28.403 10.976-38.597 20.535l-.321-2.142c-4.672 5.608-20.371 16.748-21.622 24.011l-1.249.291c-2.431 4.116-4.004 8.781-5.932 13.016-3.18 5.417-4.661 2.085-4.208 2.934-6.253 12.679-9.359 23.332-12.043 32.069 1.912 2.858.046 17.206.769 28.688-3.141 56.709 39.8 111.77 86.737 124.48 6.88 2.459 17.11 2.364 25.813 2.618-10.268-2.937-11.595-1.556-21.595-5.044-7.215-3.398-8.797-7.277-13.907-11.711l2.022 3.573c-10.021-3.547-5.829-4.39-13.982-6.972l2.16-2.82c-3.249-.246-8.604-5.475-10.069-8.371l-3.553.14c-4.27-5.269-6.545-9.063-6.379-12.005l-1.148 2.047c-1.301-2.235-15.709-19.759-8.234-15.679-1.389-1.271-3.235-2.067-5.237-5.703l1.522-1.739c-3.597-4.627-6.621-10.562-6.391-12.536 1.919 2.592 3.25 3.075 4.568 3.52-9.083-22.539-9.593-1.242-16.474-22.942l1.456-.116c-1.116-1.682-1.793-3.506-2.69-5.298l.633-6.313c-6.541-7.562-1.829-32.151-.887-45.637.655-5.485 5.459-11.322 9.114-20.477l-2.227-.384c4.256-7.423 24.301-29.814 33.583-28.662 4.499-5.649-.892-.02-1.772-1.443 9.878-10.223 12.984-7.222 19.65-9.061 7.19-4.268-6.17 1.664-2.761-1.628 12.427-3.174 8.808-7.216 25.021-8.828 1.71.973-3.969 1.503-5.395 2.766 10.354-5.066 32.769-3.914 47.326 2.811 16.895 7.896 35.873 31.232 36.622 53.189l.852.229c-.431 8.729 1.336 18.822-1.727 28.094l2.1-4.385"/><path d="m79.5 142.715-.578 2.893c2.71 3.683 4.861 7.673 8.323 10.552-2.49-4.863-4.341-6.872-7.745-13.445m6.409-.251c-1.435-1.587-2.284-3.497-3.235-5.4.909 3.345 2.771 6.219 4.504 9.143zm113.411-24.65-.605 1.52c-1.111 7.892-3.511 15.701-7.189 22.941a72.1 72.1 0 0 0 7.79-24.461M109.698 6.757c2.789-1.022 6.855-.56 9.814-1.233-3.855.324-7.693.517-11.484 1.005zM11.781 58.824c.642 5.951-4.477 8.26 1.134 4.337 3.007-6.773-1.175-1.87-1.134-4.337M5.188 86.362c1.292-3.967 1.526-6.349 2.02-8.645-3.571 4.566-1.643 5.539-2.02 8.645"/></g></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 210 260" class="svg gitea-debian" width="16" height="16" aria-hidden="true"><g fill="#d70751"><path d="M124.525 137.053c-4.125.058.78 2.125 6.165 2.954a55 55 0 0 0 4.04-3.479c-3.354.821-6.765.838-10.205.525m22.14-5.52c2.457-3.389 4.246-7.102 4.878-10.939-.551 2.736-2.035 5.099-3.435 7.592-7.711 4.854-.726-2.883-.004-5.824-8.29 10.436-1.138 6.257-1.439 9.171m8.174-21.265c.497-7.428-1.462-5.08-2.121-2.245.766.4 1.377 5.237 2.121 2.245M108.883 8.736c2.201.395 4.757.698 4.398 1.224 2.407-.528 2.954-1.015-4.398-1.224M113.281 9.96l-1.556.32 1.448-.127z"/><path d="M181.93 113.085c.247 6.671-1.95 9.907-3.932 15.637l-3.564 1.781c-2.919 5.666.282 3.598-1.807 8.105-4.556 4.049-13.823 12.67-16.789 13.457-2.163-.047 1.469-2.554 1.943-3.537-6.097 4.188-4.894 6.285-14.217 8.83l-.273-.607c-23.001 10.818-54.947-10.622-54.526-39.876-.246 1.857-.698 1.393-1.208 2.144-1.186-15.052 6.952-30.17 20.675-36.343 13.427-6.646 29.163-3.918 38.78 5.044-5.282-6.92-15.795-14.254-28.255-13.568-12.208.193-23.625 7.95-27.436 16.369-6.253 3.938-6.979 15.177-9.704 17.233-3.665 26.943 6.896 38.583 24.762 52.275 2.812 1.896.792 2.184 1.173 3.627-5.936-2.779-11.372-6.976-15.841-12.114 2.372 3.473 4.931 6.847 8.239 9.499-5.596-1.897-13.074-13.563-15.256-14.038 9.647 17.274 39.142 30.295 54.587 23.836-7.146.263-16.226.146-24.256-2.822-3.371-1.734-7.958-5.331-7.14-6.003 21.079 7.875 42.854 5.965 61.09-8.655 4.641-3.614 9.709-9.761 11.173-9.846-2.206 3.317.377 1.596-1.318 4.523 4.625-7.456-2.008-3.035 4.779-12.877l2.507 3.453c-.931-6.188 7.687-13.704 6.813-23.492 1.975-2.994 2.206 3.22.107 10.107 2.912-7.64.767-8.867 1.516-15.171.81 2.118 1.867 4.37 2.412 6.606-1.895-7.382 1.948-12.433 2.898-16.724-.937-.415-2.928 3.264-3.383-5.457.065-3.788 1.054-1.985 1.435-2.917-.744-.427-2.694-3.33-3.88-8.9.86-1.308 2.3 3.393 3.47 3.586-.753-4.429-2.049-7.805-2.103-11.202-3.421-7.149-1.211.953-3.985-3.069-3.641-11.357 3.021-2.637 3.47-7.796 5.52 7.995 8.667 20.387 10.11 25.519-1.103-6.258-2.883-12.32-5.058-18.185 1.677.705-2.699-12.875 2.18-3.882-5.21-19.172-22.302-37.087-38.025-45.493 1.924 1.76 4.354 3.971 3.481 4.317-7.819-4.656-6.444-5.018-7.565-6.985-6.369-2.591-6.788.208-11.007.004-12.005-6.368-14.318-5.69-25.368-9.681l.502 2.349c-7.953-2.649-9.265 1.005-17.862.009-.523-.409 2.753-1.479 5.452-1.871-7.69 1.015-7.329-1.515-14.854.279 1.855-1.301 3.815-2.162 5.793-3.269-6.271.381-14.971 3.649-12.286.677-10.235 4.569-28.403 10.976-38.597 20.535l-.321-2.142c-4.672 5.608-20.371 16.748-21.622 24.011l-1.249.291c-2.431 4.116-4.004 8.781-5.932 13.016-3.18 5.417-4.661 2.085-4.208 2.934-6.253 12.679-9.359 23.332-12.043 32.069 1.912 2.858.046 17.206.769 28.688-3.141 56.709 39.8 111.77 86.737 124.48 6.88 2.459 17.11 2.364 25.813 2.618-10.268-2.937-11.595-1.556-21.595-5.044-7.215-3.398-8.797-7.277-13.907-11.711l2.022 3.573c-10.021-3.547-5.829-4.39-13.982-6.972l2.16-2.82c-3.249-.246-8.604-5.475-10.069-8.371l-3.553.14c-4.27-5.269-6.545-9.063-6.379-12.005l-1.148 2.047c-1.301-2.235-15.709-19.759-8.234-15.679-1.389-1.271-3.235-2.067-5.237-5.703l1.522-1.739c-3.597-4.627-6.621-10.562-6.391-12.536 1.919 2.592 3.25 3.075 4.568 3.52-9.083-22.539-9.593-1.242-16.474-22.942l1.456-.116c-1.116-1.682-1.793-3.506-2.69-5.298l.633-6.313c-6.541-7.562-1.829-32.151-.887-45.637.655-5.485 5.459-11.322 9.114-20.477l-2.227-.384c4.256-7.423 24.301-29.814 33.583-28.662 4.499-5.649-.892-.02-1.772-1.443 9.878-10.223 12.984-7.222 19.65-9.061 7.19-4.268-6.17 1.664-2.761-1.628 12.427-3.174 8.808-7.216 25.021-8.828 1.71.973-3.969 1.503-5.395 2.766 10.354-5.066 32.769-3.914 47.326 2.811 16.895 7.896 35.873 31.232 36.622 53.189l.852.229c-.431 8.729 1.336 18.822-1.727 28.094l2.1-4.385"/><path d="m79.5 142.715-.578 2.893c2.71 3.683 4.861 7.673 8.323 10.552-2.49-4.863-4.341-6.872-7.745-13.445m6.409-.251c-1.435-1.587-2.284-3.497-3.235-5.4.909 3.345 2.771 6.219 4.504 9.143zm113.411-24.65-.605 1.52c-1.111 7.892-3.511 15.701-7.189 22.941a72.1 72.1 0 0 0 7.79-24.461M109.698 6.757c2.789-1.022 6.855-.56 9.814-1.233-3.855.324-7.693.517-11.484 1.005zM11.781 58.824c.642 5.951-4.477 8.26 1.134 4.337 3.007-6.773-1.175-1.87-1.134-4.337M5.188 86.362c1.292-3.967 1.526-6.349 2.02-8.645-3.571 4.566-1.643 5.539-2.02 8.645"/></g></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/gitea-feishu.svg b/public/assets/img/svg/gitea-feishu.svg
new file mode 100644
index 0000000000..d7a5ead499
--- /dev/null
+++ b/public/assets/img/svg/gitea-feishu.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="7 7 26 26" class="svg gitea-feishu" width="16" height="16" aria-hidden="true"><path fill="#00d6b9" d="m21.069 20.504.063-.06.125-.122.085-.084.256-.254.348-.344.299-.296.281-.278.293-.289.269-.266.374-.37.218-.206.419-.359.404-.306.598-.386.617-.33.606-.265.348-.127.177-.058a14.8 14.8 0 0 0-2.793-5.603 1.34 1.34 0 0 0-1.047-.502H12.221a.201.201 0 0 0-.119.364 31.5 31.5 0 0 1 8.943 10.162l.025-.023z"/><path fill="#3370ff" d="M16.791 30c5.57 0 10.423-3.074 12.955-7.618q.133-.239.258-.484a6 6 0 0 1-.425.699 6 6 0 0 1-.17.23 6 6 0 0 1-.225.274q-.092.105-.188.206a6 6 0 0 1-.407.384 6 6 0 0 1-.24.195 7 7 0 0 1-.292.21q-.094.065-.191.122c-.097.057-.134.081-.204.119q-.21.116-.428.215a6 6 0 0 1-.385.157 6 6 0 0 1-.43.138 6 6 0 0 1-.661.143 6 6 0 0 1-.491.055 6.125 6.125 0 0 1-1.543-.085 7 7 0 0 1-.38-.079l-.2-.051-.555-.155-.275-.081-.41-.125-.334-.107-.317-.104-.215-.073-.26-.091-.186-.066-.367-.134-.212-.081-.284-.11-.299-.119-.193-.079-.24-.1-.185-.078-.192-.084-.166-.073-.152-.067-.153-.07-.159-.073-.2-.093-.208-.099-.222-.108-.189-.093a31.2 31.2 0 0 1-8.822-6.583.202.202 0 0 0-.349.138l.005 9.52v.773c0 .448.222.87.595 1.118A14.75 14.75 0 0 0 16.791 30"/><path fill="#133c92" d="m29.746 22.382.051-.093zm.231-.435.014-.025.007-.012z"/><path fill="#133c9a" d="M33.151 16.582a8.45 8.45 0 0 0-3.744-.869 8.5 8.5 0 0 0-2.303.317l-.252.075-.177.058-.348.127-.606.265-.617.33-.598.386-.404.306-.419.359-.218.206-.374.37-.269.266-.293.289-.281.278-.299.296-.348.344-.256.254-.085.084-.125.122-.063.06-.095.09-.105.099a15 15 0 0 1-3.072 2.175l.2.093.159.073.153.07.152.067.166.073.192.084.185.078.24.1.193.079.299.119.284.11.212.081.367.134.186.066.26.09.215.073.317.104.334.107.41.125.275.081.555.155.2.051.379.079.433.062.585.037.525-.014.491-.055a6 6 0 0 0 .66-.143l.43-.138.385-.158.427-.215.204-.119.191-.122.292-.21.24-.195.407-.384.188-.206.225-.274.17-.23a6 6 0 0 0 .421-.693l.144-.288 1.305-2.599-.003.006a8.1 8.1 0 0 1 1.697-2.439z"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/gitea-gitbucket.svg b/public/assets/img/svg/gitea-gitbucket.svg
index 62f603484e..b9e99724b2 100644
--- a/public/assets/img/svg/gitea-gitbucket.svg
+++ b/public/assets/img/svg/gitea-gitbucket.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 316 329" class="svg gitea-gitbucket" width="16" height="16" aria-hidden="true"><path d="M123 21.1c-44.8 2.8-84 12.8-97.1 24.6-5 4.5-5 7.1 0 11.6C41.7 71.5 96.6 82.9 150 83h10.6l5.4-5.6c11.8-12.1 21.3-12.4 32.6-1.2l5.2 5 13.3-1.7c33.8-4.2 61.5-12.7 71.8-22 5.3-4.8 5.3-7.2 0-12-10.1-9.1-39.1-18.1-70.4-21.9-28.3-3.4-65.6-4.4-95.5-2.5M23.2 80.6c.4 1.6 7 42.9 14.8 91.9 7.9 49 14.7 89.5 15.2 90.2 1.7 2.1 25.8 11.4 41.6 15.9 13 3.7 35.1 8.4 40 8.4.6 0 1.2-.6 1.2-1.3 0-.6-17.4-18.5-38.6-39.7C57.9 206.6 55 203.2 55 196s3-10.7 38.3-45.9c30.1-30 34.8-34.3 36.6-33.5 1.1.5 8.7 7.4 17 15.2l15.1 14.4v5.9c0 7.3 2.4 12.4 7.7 16.7l3.8 3v61.8l-3.8 3.4c-10.2 8.9-10.2 22.9-.1 30.7 3.1 2.3 4.9 2.8 10.8 3.1 8.2.4 11.5-1.1 16.2-7.3 2.2-3 2.9-5.1 3.2-10 .4-6.5-.2-8.3-5.3-15.4l-2.5-3.4v-26.6c0-26.7.3-31.1 2.3-31.1.5 0 5.4 4.4 10.9 9.7 9.6 9.5 9.9 10 10.8 15.7 1.7 10.3 8.9 16.6 19 16.6 7.6 0 13.5-3.9 17.4-11.7 3.2-6.4 1.6-14.3-4.3-20.6-4.1-4.4-7.3-5.7-14.9-5.7h-6.8l-12.7-12.1c-10.7-10.1-12.7-12.6-13.2-15.7-1.2-7.2-1.6-8.2-4.7-11.7-3.9-4.5-7.7-6.5-12.2-6.5-1.9 0-4.5-.4-5.8-.9s-9.9-8.2-19.3-17l-17-16-7.1-.1c-10.6 0-36-2.7-52.4-5.5-22.8-4-38.5-8.6-57.9-17.2-1.1-.5-1.3 0-.9 2.3M278.5 83.6c-8.6 3.6-28 8.8-42.5 11.4-6.9 1.2-12.9 2.6-13.5 3.1-.6.6 9.3 11.2 27.5 29.4 15.6 15.6 28.5 28.3 28.7 28.1s1.9-15.8 3.8-34.7 3.7-35.6 4-37.2c.6-3.4-.2-3.4-8-.1M255.2 259.3c-7.8 7.8-14.2 14.6-14.2 15s.7.7 1.6.7c2.2 0 23-8.9 24.2-10.3.9-1.1 3.5-18.7 2.9-19.3-.2-.2-6.7 6.1-14.5 13.9M56 283.5c0 3.4 4 9.5 8.4 12.9 6.1 4.6 19.7 10.4 31.7 13.5 16.9 4.3 32.1 6.2 53.4 6.8l19 .5-7-7.1c-6.8-6.9-7.1-7.1-12-7.1-18.9 0-55.1-7.9-80.6-17.6C62.5 283 57 281 56.6 281c-.3 0-.6 1.1-.6 2.5M262 283.4c-5.3 2.8-25 9.7-36 12.6l-11.4 2.9-7.8 7.8c-4.2 4.2-7.6 7.9-7.4 8.1.9.8 24.4-4.1 33.4-6.9 16.4-5.3 26.7-11.4 30.8-18.5 2.4-4 3.1-7.4 1.7-7.4-.5.1-1.9.7-3.3 1.4"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 316 329" class="svg gitea-gitbucket" width="16" height="16" aria-hidden="true"><path d="M123 21.1c-44.8 2.8-84 12.8-97.1 24.6-5 4.5-5 7.1 0 11.6C41.7 71.5 96.6 82.9 150 83h10.6l5.4-5.6c11.8-12.1 21.3-12.4 32.6-1.2l5.2 5 13.3-1.7c33.8-4.2 61.5-12.7 71.8-22 5.3-4.8 5.3-7.2 0-12-10.1-9.1-39.1-18.1-70.4-21.9-28.3-3.4-65.6-4.4-95.5-2.5M23.2 80.6c.4 1.6 7 42.9 14.8 91.9 7.9 49 14.7 89.5 15.2 90.2 1.7 2.1 25.8 11.4 41.6 15.9 13 3.7 35.1 8.4 40 8.4.6 0 1.2-.6 1.2-1.3 0-.6-17.4-18.5-38.6-39.7C57.9 206.6 55 203.2 55 196s3-10.7 38.3-45.9c30.1-30 34.8-34.3 36.6-33.5 1.1.5 8.7 7.4 17 15.2l15.1 14.4v5.9c0 7.3 2.4 12.4 7.7 16.7l3.8 3v61.8l-3.8 3.4c-10.2 8.9-10.2 22.9-.1 30.7 3.1 2.3 4.9 2.8 10.8 3.1 8.2.4 11.5-1.1 16.2-7.3 2.2-3 2.9-5.1 3.2-10 .4-6.5-.2-8.3-5.3-15.4l-2.5-3.4v-26.6c0-26.7.3-31.1 2.3-31.1.5 0 5.4 4.4 10.9 9.7 9.6 9.5 9.9 10 10.8 15.7 1.7 10.3 8.9 16.6 19 16.6 7.6 0 13.5-3.9 17.4-11.7 3.2-6.4 1.6-14.3-4.3-20.6-4.1-4.4-7.3-5.7-14.9-5.7h-6.8l-12.7-12.1c-10.7-10.1-12.7-12.6-13.2-15.7-1.2-7.2-1.6-8.2-4.7-11.7-3.9-4.5-7.7-6.5-12.2-6.5-1.9 0-4.5-.4-5.8-.9s-9.9-8.2-19.3-17l-17-16-7.1-.1c-10.6 0-36-2.7-52.4-5.5-22.8-4-38.5-8.6-57.9-17.2-1.1-.5-1.3 0-.9 2.3M278.5 83.6c-8.6 3.6-28 8.8-42.5 11.4-6.9 1.2-12.9 2.6-13.5 3.1-.6.6 9.3 11.2 27.5 29.4 15.6 15.6 28.5 28.3 28.7 28.1s1.9-15.8 3.8-34.7 3.7-35.6 4-37.2c.6-3.4-.2-3.4-8-.1M255.2 259.3c-7.8 7.8-14.2 14.6-14.2 15s.7.7 1.6.7c2.2 0 23-8.9 24.2-10.3.9-1.1 3.5-18.7 2.9-19.3-.2-.2-6.7 6.1-14.5 13.9M56 283.5c0 3.4 4 9.5 8.4 12.9 6.1 4.6 19.7 10.4 31.7 13.5 16.9 4.3 32.1 6.2 53.4 6.8l19 .5-7-7.1c-6.8-6.9-7.1-7.1-12-7.1-18.9 0-55.1-7.9-80.6-17.6C62.5 283 57 281 56.6 281c-.3 0-.6 1.1-.6 2.5M262 283.4c-5.3 2.8-25 9.7-36 12.6l-11.4 2.9-7.8 7.8c-4.2 4.2-7.6 7.9-7.4 8.1.9.8 24.4-4.1 33.4-6.9 16.4-5.3 26.7-11.4 30.8-18.5 2.4-4 3.1-7.4 1.7-7.4-.5.1-1.9.7-3.3 1.4"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/gitea-gitlab.svg b/public/assets/img/svg/gitea-gitlab.svg
index 03fcb0b87e..e2d708e7be 100644
--- a/public/assets/img/svg/gitea-gitlab.svg
+++ b/public/assets/img/svg/gitea-gitlab.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="svg gitea-gitlab" width="16" height="16" aria-hidden="true"><path fill="#E24329" d="m31.462 12.779-.045-.115-4.35-11.35a1.14 1.14 0 0 0-.447-.541 1.16 1.16 0 0 0-1.343.071c-.187.15-.322.356-.386.587l-2.94 9.001h-11.9l-2.941-9a1.14 1.14 0 0 0-1.045-.84 1.15 1.15 0 0 0-1.13.72L.579 12.68l-.045.113a8.09 8.09 0 0 0 2.68 9.34l.016.012.038.03 6.635 4.967 3.28 2.484 1.994 1.51a1.35 1.35 0 0 0 1.627 0l1.994-1.51 3.282-2.484 6.673-4.997.018-.013a8.09 8.09 0 0 0 2.69-9.352Z"/><path fill="#FC6D26" d="m31.462 12.779-.045-.115a14.75 14.75 0 0 0-5.856 2.634l-9.553 7.24L22.1 27.14l6.673-4.997.019-.013a8.09 8.09 0 0 0 2.67-9.352Z"/><path fill="#FCA326" d="m9.908 27.14 3.275 2.485 1.994 1.51a1.35 1.35 0 0 0 1.627 0l1.994-1.51 3.282-2.484s-2.835-2.14-6.092-4.603z"/><path fill="#FC6D26" d="M6.435 15.305A14.7 14.7 0 0 0 .58 12.672l-.045.113a8.09 8.09 0 0 0 2.68 9.347l.016.012.038.03 6.635 4.967 6.105-4.603-9.573-7.233Z"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="svg gitea-gitlab" width="16" height="16" aria-hidden="true"><path fill="#e24329" d="m31.462 12.779-.045-.115-4.35-11.35a1.14 1.14 0 0 0-.447-.541 1.16 1.16 0 0 0-1.343.071c-.187.15-.322.356-.386.587l-2.94 9.001h-11.9l-2.941-9a1.14 1.14 0 0 0-1.045-.84 1.15 1.15 0 0 0-1.13.72L.579 12.68l-.045.113a8.09 8.09 0 0 0 2.68 9.34l.016.012.038.03 6.635 4.967 3.28 2.484 1.994 1.51a1.35 1.35 0 0 0 1.627 0l1.994-1.51 3.282-2.484 6.673-4.997.018-.013a8.09 8.09 0 0 0 2.69-9.352Z"/><path fill="#fc6d26" d="m31.462 12.779-.045-.115a14.75 14.75 0 0 0-5.856 2.634l-9.553 7.24L22.1 27.14l6.673-4.997.019-.013a8.09 8.09 0 0 0 2.67-9.352Z"/><path fill="#fca326" d="m9.908 27.14 3.275 2.485 1.994 1.51a1.35 1.35 0 0 0 1.627 0l1.994-1.51 3.282-2.484s-2.835-2.14-6.092-4.603z"/><path fill="#fc6d26" d="M6.435 15.305A14.7 14.7 0 0 0 .58 12.672l-.045.113a8.09 8.09 0 0 0 2.68 9.347l.016.012.038.03 6.635 4.967 6.105-4.603-9.573-7.233Z"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/gitea-google.svg b/public/assets/img/svg/gitea-google.svg
index 7dd2622df6..26ee04cb64 100644
--- a/public/assets/img/svg/gitea-google.svg
+++ b/public/assets/img/svg/gitea-google.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="gitea-google__svg gitea-google__gitea-google svg gitea-google" viewBox="0 0 24 24" width="16" height="16"><path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09"/><path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23"/><path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22z"/><path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53"/><path fill="none" d="M1 1h22v22H1z"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="gitea-google__svg gitea-google__gitea-google svg gitea-google" viewBox="0 0 24 24" width="16" height="16"><path fill="#4285f4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09"/><path fill="#34a853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23"/><path fill="#fbbc05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22z"/><path fill="#ea4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53"/><path fill="none" d="M1 1h22v22H1z"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/gitea-maven.svg b/public/assets/img/svg/gitea-maven.svg
index 320d01a234..f6ece7dc28 100644
--- a/public/assets/img/svg/gitea-maven.svg
+++ b/public/assets/img/svg/gitea-maven.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2392.5 4226.6" class="svg gitea-maven" width="16" height="16" aria-hidden="true"><linearGradient id="gitea-maven__a" x1="-5167.1" x2="-4570.1" y1="697.55" y2="1395.6" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#F69923"/><stop offset=".312" stop-color="#F79A23"/><stop offset=".838" stop-color="#E97826"/></linearGradient><path fill="url(#gitea-maven__a)" d="M1798.9 20.1C1732.6 59.2 1622.5 170 1491 330.5l120.8 228c84.8-121.3 170.9-230.4 257.8-323.6 6.7-7.4 10.2-10.9 10.2-10.9-3.4 3.6-6.8 7.3-10.2 10.9-28.1 31-113.4 130.5-242.1 328.1 123.9-6.2 314.3-31.5 469.6-58.1 46.2-258.8-45.3-377.3-45.3-377.3S1935.5-60.6 1798.9 20.1"/><path fill="none" d="M1594.4 1320.7c.9-.2 1.8-.3 2.7-.5l-17.4 1.9c-1.1.5-2 1-3.1 1.4 6-.9 11.9-1.9 17.8-2.8m-123.3 408.4c-9.9 2.2-20 3.9-30.2 5.4 10.2-1.5 20.3-3.3 30.2-5.4m-838 916.1c1.3-3.4 2.6-6.8 3.8-10.2 26.6-70.2 52.9-138.4 79-204.9 29.3-74.6 58.2-146.8 86.8-216.8 30.1-73.8 59.8-145.1 89.1-214 30.7-72.3 61-141.9 90.7-208.9 24.2-54.5 48-107.3 71.5-158.4 7.8-17 15.6-33.9 23.4-50.6 15.4-33.1 30.7-65.6 45.7-97.3 13.9-29.3 27.7-57.9 41.4-86 4.5-9.4 9.1-18.6 13.6-27.9.7-1.5 1.5-3 2.2-4.5l-14.8 1.6-11.8-23.2c-1.1 2.3-2.3 4.5-3.5 6.8q-31.8 63.15-63 127.5c-12 24.8-24 49.7-35.9 74.7-33 69.3-65.5 139.2-97.4 209.6-32.3 71.1-63.9 142.6-94.9 214.2-30.5 70.3-60.3 140.7-89.6 210.9-29.2 70.1-57.7 140-85.6 209.4-29.1 72.5-57.4 144.3-84.8 215.3-6.2 16-12.4 32-18.5 48-22 57.3-43.4 113.8-64.3 169.6l18.6 36.7 16.6-1.8c.6-1.7 1.2-3.4 1.8-5 26.9-73.5 53.5-145.1 79.9-214.8m800.1-909.5c.1 0 .1-.1.2-.1 0 0-.1 0-.2.1"/><path fill="#BE202E" d="M1393.2 1934.8c-15.4 2.8-31.3 5.5-47.6 8.3-.1 0-.2.1-.3.1 8.2-1.2 16.3-2.4 24.3-3.8s15.8-2.9 23.6-4.6"/><path fill="#BE202E" d="M1393.2 1934.8c-15.4 2.8-31.3 5.5-47.6 8.3-.1 0-.2.1-.3.1 8.2-1.2 16.3-2.4 24.3-3.8s15.8-2.9 23.6-4.6" opacity=".35"/><path fill="#BE202E" d="M1433.6 1735.5s-.1 0-.1.1c-.1 0-.1.1-.2.1 2.6-.3 5.1-.8 7.6-1.1 10.3-1.5 20.4-3.3 30.2-5.4-12.3 2-24.8 4.2-37.5 6.3"/><path fill="#BE202E" d="M1433.6 1735.5s-.1 0-.1.1c-.1 0-.1.1-.2.1 2.6-.3 5.1-.8 7.6-1.1 10.3-1.5 20.4-3.3 30.2-5.4-12.3 2-24.8 4.2-37.5 6.3" opacity=".35"/><linearGradient id="gitea-maven__b" x1="-9585.3" x2="-5326.2" y1="620.5" y2="620.5" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset=".323" stop-color="#9E2064"/><stop offset=".63" stop-color="#C92037"/><stop offset=".751" stop-color="#CD2335"/><stop offset="1" stop-color="#E97826"/></linearGradient><path fill="url(#gitea-maven__b)" d="M1255.7 1147.6c36.7-68.6 73.9-135.7 111.5-201 39-67.8 78.5-133.6 118.4-197 2.3-3.7 4.7-7.5 7-11.3 39.4-62.4 79.2-122.4 119.3-179.8l-120.8-228c-9.1 11.1-18.2 22.4-27.5 33.9-34.8 43.4-71 90.1-108.1 139.6-41.8 55.8-84.8 115.4-128.5 177.9-40.3 57.8-81.2 118.3-122.1 180.9-34.8 53.3-69.8 108.2-104.5 164.5l-3.9 6.3 157.2 310.5c33.6-66.5 67.6-132.1 102-196.5"/><linearGradient id="gitea-maven__c" x1="-9071.2" x2="-6533.2" y1="1047.7" y2="1047.7" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#282662"/><stop offset=".095" stop-color="#662E8D"/><stop offset=".788" stop-color="#9F2064"/><stop offset=".949" stop-color="#CD2032"/></linearGradient><path fill="url(#gitea-maven__c)" d="M539.7 2897.1c-20.8 57.2-41.7 115.4-62.7 174.9-.3.9-.6 1.7-.9 2.6-3 8.4-5.9 16.8-8.9 25.2-14.1 40.1-26.4 76.2-54.5 158.3 46.3 21.1 83.5 76.7 118.7 139.8-3.7-65.3-30.8-126.7-82.1-174.2 228.3 10.3 425-47.4 526.7-214.3 9.1-14.9 17.4-30.5 24.9-47.2-46.2 58.6-103.5 83.5-211.4 77.4-.2.1-.5.2-.7.3.2-.1.5-.2.7-.3 158.8-71.1 238.5-139.3 308.9-252.4 16.7-26.8 32.9-56.1 49.5-88.6-138.9 142.6-299.8 183.2-469.3 152.4l-127.1 13.9c-4 10.7-7.9 21.4-11.8 32.2"/><linearGradient id="gitea-maven__d" x1="-9346.1" x2="-5087" y1="580.82" y2="580.82" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset=".323" stop-color="#9E2064"/><stop offset=".63" stop-color="#C92037"/><stop offset=".751" stop-color="#CD2335"/><stop offset="1" stop-color="#E97826"/></linearGradient><path fill="url(#gitea-maven__d)" d="M599 2612.4c27.5-71 55.8-142.8 84.8-215.3 27.8-69.4 56.4-139.2 85.6-209.4s59.1-140.5 89.6-210.9c31-71.6 62.7-143.1 94.9-214.2 31.9-70.3 64.4-140.3 97.4-209.6 11.9-25 23.9-49.9 35.9-74.7q31.2-64.35 63-127.5c1.1-2.3 2.3-4.5 3.5-6.8l-157.2-310.5c-2.6 4.2-5.1 8.4-7.7 12.6-36.6 59.8-73.1 121-108.9 183.5-36.2 63.1-71.7 127.4-106.4 192.6-29.3 55-57.9 110.5-85.7 166.5-5.6 11.4-11.1 22.6-16.6 33.9-34.3 70.5-65.2 138.6-93.2 204.1-31.7 74.2-59.6 145.1-84 212.3-16.1 44.2-30.7 86.9-44.1 127.9-11 35-21.5 70.1-31.4 105-23.5 82.3-43.7 164.4-60.3 246.2l158 311.9c20.9-55.8 42.3-112.3 64.3-169.6 6.1-15.9 12.3-32 18.5-48"/><linearGradient id="gitea-maven__e" x1="-9035.5" x2="-6797.2" y1="638.44" y2="638.44" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#282662"/><stop offset=".095" stop-color="#662E8D"/><stop offset=".788" stop-color="#9F2064"/><stop offset=".949" stop-color="#CD2032"/></linearGradient><path fill="url(#gitea-maven__e)" d="M356.1 2529.2c-19.8 99.8-33.9 199.2-41 298-.2 3.5-.6 6.9-.8 10.4-49.3-79-181.3-156.1-181-155.4 94.5 137 166.2 273 176.9 406.5-50.6 10.4-119.9-4.6-200-34.1 83.5 76.7 146.2 97.9 170.6 103.6-76.7 4.8-156.6 57.5-237.1 118.2 117.7-48 212.8-67 280.9-51.6-108 305.8-216.3 643.4-324.6 1001.8 33.2-9.8 53-32.1 64.1-62.3 19.3-64.9 147.4-490.7 348.1-1050.4 5.7-15.9 11.5-31.9 17.3-48 1.6-4.5 3.3-9 4.9-13.4 21.2-58.7 43.2-118.6 65.9-179.7 5.2-13.9 10.4-27.8 15.6-41.8.1-.3.2-.6.3-.8l-157.8-311.8c-.7 3.5-1.6 7.1-2.3 10.8"/><linearGradient id="gitea-maven__f" x1="-9346.1" x2="-5087" y1="1021.6" y2="1021.6" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset=".323" stop-color="#9E2064"/><stop offset=".63" stop-color="#C92037"/><stop offset=".751" stop-color="#CD2335"/><stop offset="1" stop-color="#E97826"/></linearGradient><path fill="url(#gitea-maven__f)" d="M1178.1 1370.3c-4.5 9.2-9 18.5-13.6 27.9-13.6 28.1-27.4 56.7-41.4 86-15.1 31.7-30.3 64.1-45.7 97.3-7.8 16.7-15.5 33.5-23.4 50.6-23.5 51.1-47.3 103.9-71.5 158.4-29.7 67-60 136.6-90.7 208.9-29.3 68.9-59 140.2-89.1 214-28.6 70-57.5 142.3-86.8 216.8-26.1 66.5-52.4 134.7-79 204.9-1.3 3.4-2.6 6.8-3.8 10.2-26.4 69.7-53 141.3-79.8 214.7-.6 1.7-1.2 3.4-1.8 5l127.1-13.9c-2.5-.5-5.1-.8-7.6-1.3 152-18.9 354-132.5 484.6-272.7 60.2-64.6 114.8-140.8 165.3-230 37.6-66.4 72.9-140 106.5-221.5 29.4-71.2 57.6-148.3 84.8-231.9-34.9 18.4-74.9 31.9-119 41.3-7.7 1.6-15.6 3.2-23.6 4.6s-16.1 2.7-24.3 3.8c.1 0 .2-.1.3-.1 141.7-54.5 231.1-159.8 296.1-288.7-37.3 25.4-97.9 58.7-170.5 74.7-9.9 2.2-20 3.9-30.2 5.4-2.6.4-5.1.8-7.6 1.1.1 0 .1-.1.2-.1 0 0 .1 0 .1-.1 49.2-20.6 90.7-43.6 126.7-70.8 7.7-5.8 15.2-11.8 22.4-18.1 11-9.5 21.4-19.5 31.4-30 6.4-6.7 12.6-13.6 18.6-20.8 14.1-16.8 27.3-34.9 39.7-54.6 3.8-6 7.5-12.1 11.2-18.4 4.7-9.1 9.2-18 13.6-26.8 19.8-39.8 35.6-75.3 48.2-106.5 6.3-15.6 11.8-30 16.5-43.4 1.9-5.3 3.7-10.5 5.4-15.5 5-15 9.1-28.3 12.3-40 4.8-17.5 7.7-31.4 9.3-41.5-4.8 3.8-10.3 7.6-16.5 11.3-42.8 25.6-116.2 48.8-175.4 59.7l116.7-12.8-116.7 12.8c-.9.2-1.8.3-2.7.5-5.9 1-11.9 1.9-17.9 2.9 1.1-.5 2-1 3.1-1.4l-399.3 43.8c-.7 1.4-1.4 2.8-2.2 4.3"/><linearGradient id="gitea-maven__g" x1="-9610.3" x2="-5351.2" y1="999.73" y2="999.73" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset=".323" stop-color="#9E2064"/><stop offset=".63" stop-color="#C92037"/><stop offset=".751" stop-color="#CD2335"/><stop offset="1" stop-color="#E97826"/></linearGradient><path fill="url(#gitea-maven__g)" d="M1627.6 563.1c-35.5 54.5-74.3 116.4-116 186.5-2.2 3.6-4.4 7.4-6.6 11.1-36 60.7-74.3 127.3-114.5 200.3-34.8 63-71 130.6-108.6 203.3-32.8 63.3-66.7 130.5-101.5 201.6l399.3-43.8c116.3-53.5 168.3-101.9 218.8-171.9 13.4-19.3 26.9-39.5 40.3-60.4 41-64 81.2-134.5 117.2-204.6 34.7-67.7 65.3-134.8 88.8-195.3 14.9-38.5 26.9-74.3 35.2-105.7 7.3-27.7 13-54 17.4-79.1-155.5 26.5-345.9 51.9-469.8 58"/><path fill="#BE202E" d="M1369.6 1939.4c-8 1.4-16.1 2.7-24.3 3.8 8.2-1.1 16.3-2.4 24.3-3.8"/><path fill="#BE202E" d="M1369.6 1939.4c-8 1.4-16.1 2.7-24.3 3.8 8.2-1.1 16.3-2.4 24.3-3.8" opacity=".35"/><linearGradient id="gitea-maven__h" x1="-9346.1" x2="-5087" y1="1152.7" y2="1152.7" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset=".323" stop-color="#9E2064"/><stop offset=".63" stop-color="#C92037"/><stop offset=".751" stop-color="#CD2335"/><stop offset="1" stop-color="#E97826"/></linearGradient><path fill="url(#gitea-maven__h)" d="M1369.6 1939.4c-8 1.4-16.1 2.7-24.3 3.8 8.2-1.1 16.3-2.4 24.3-3.8"/><path fill="#BE202E" d="M1433.2 1735.7c2.6-.3 5.1-.8 7.6-1.1-2.5.3-5 .7-7.6 1.1"/><path fill="#BE202E" d="M1433.2 1735.7c2.6-.3 5.1-.8 7.6-1.1-2.5.3-5 .7-7.6 1.1" opacity=".35"/><linearGradient id="gitea-maven__i" x1="-9346.1" x2="-5087" y1="1137.7" y2="1137.7" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset=".323" stop-color="#9E2064"/><stop offset=".63" stop-color="#C92037"/><stop offset=".751" stop-color="#CD2335"/><stop offset="1" stop-color="#E97826"/></linearGradient><path fill="url(#gitea-maven__i)" d="M1433.2 1735.7c2.6-.3 5.1-.8 7.6-1.1-2.5.3-5 .7-7.6 1.1"/><path fill="#BE202E" d="M1433.5 1735.6s.1 0 .1-.1c0 0-.1 0-.1.1"/><path fill="#BE202E" d="M1433.5 1735.6s.1 0 .1-.1c0 0-.1 0-.1.1" opacity=".35"/><linearGradient id="gitea-maven__j" x1="-6953.4" x2="-6012" y1="1134.7" y2="1134.7" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset=".323" stop-color="#9E2064"/><stop offset=".63" stop-color="#C92037"/><stop offset=".751" stop-color="#CD2335"/><stop offset="1" stop-color="#E97826"/></linearGradient><path fill="url(#gitea-maven__j)" d="M1433.5 1735.6s.1 0 .1-.1c0 0-.1 0-.1.1"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2392.5 4226.6" class="svg gitea-maven" width="16" height="16" aria-hidden="true"><linearGradient id="gitea-maven__a" x1="-5167.1" x2="-4570.1" y1="697.55" y2="1395.6" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f69923"/><stop offset=".312" stop-color="#f79a23"/><stop offset=".838" stop-color="#e97826"/></linearGradient><path fill="url(#gitea-maven__a)" d="M1798.9 20.1C1732.6 59.2 1622.5 170 1491 330.5l120.8 228c84.8-121.3 170.9-230.4 257.8-323.6 6.7-7.4 10.2-10.9 10.2-10.9-3.4 3.6-6.8 7.3-10.2 10.9-28.1 31-113.4 130.5-242.1 328.1 123.9-6.2 314.3-31.5 469.6-58.1 46.2-258.8-45.3-377.3-45.3-377.3S1935.5-60.6 1798.9 20.1"/><path fill="none" d="M1594.4 1320.7c.9-.2 1.8-.3 2.7-.5l-17.4 1.9c-1.1.5-2 1-3.1 1.4 6-.9 11.9-1.9 17.8-2.8m-123.3 408.4c-9.9 2.2-20 3.9-30.2 5.4 10.2-1.5 20.3-3.3 30.2-5.4m-838 916.1c1.3-3.4 2.6-6.8 3.8-10.2 26.6-70.2 52.9-138.4 79-204.9 29.3-74.6 58.2-146.8 86.8-216.8 30.1-73.8 59.8-145.1 89.1-214 30.7-72.3 61-141.9 90.7-208.9 24.2-54.5 48-107.3 71.5-158.4 7.8-17 15.6-33.9 23.4-50.6 15.4-33.1 30.7-65.6 45.7-97.3 13.9-29.3 27.7-57.9 41.4-86 4.5-9.4 9.1-18.6 13.6-27.9.7-1.5 1.5-3 2.2-4.5l-14.8 1.6-11.8-23.2c-1.1 2.3-2.3 4.5-3.5 6.8q-31.8 63.15-63 127.5c-12 24.8-24 49.7-35.9 74.7-33 69.3-65.5 139.2-97.4 209.6-32.3 71.1-63.9 142.6-94.9 214.2-30.5 70.3-60.3 140.7-89.6 210.9-29.2 70.1-57.7 140-85.6 209.4-29.1 72.5-57.4 144.3-84.8 215.3-6.2 16-12.4 32-18.5 48-22 57.3-43.4 113.8-64.3 169.6l18.6 36.7 16.6-1.8c.6-1.7 1.2-3.4 1.8-5 26.9-73.5 53.5-145.1 79.9-214.8m800.1-909.5c.1 0 .1-.1.2-.1 0 0-.1 0-.2.1"/><path fill="#be202e" d="M1393.2 1934.8c-15.4 2.8-31.3 5.5-47.6 8.3-.1 0-.2.1-.3.1 8.2-1.2 16.3-2.4 24.3-3.8s15.8-2.9 23.6-4.6"/><path fill="#be202e" d="M1393.2 1934.8c-15.4 2.8-31.3 5.5-47.6 8.3-.1 0-.2.1-.3.1 8.2-1.2 16.3-2.4 24.3-3.8s15.8-2.9 23.6-4.6" opacity=".35"/><path fill="#be202e" d="M1433.6 1735.5s-.1 0-.1.1c-.1 0-.1.1-.2.1 2.6-.3 5.1-.8 7.6-1.1 10.3-1.5 20.4-3.3 30.2-5.4-12.3 2-24.8 4.2-37.5 6.3"/><path fill="#be202e" d="M1433.6 1735.5s-.1 0-.1.1c-.1 0-.1.1-.2.1 2.6-.3 5.1-.8 7.6-1.1 10.3-1.5 20.4-3.3 30.2-5.4-12.3 2-24.8 4.2-37.5 6.3" opacity=".35"/><linearGradient id="gitea-maven__b" x1="-9585.3" x2="-5326.2" y1="620.5" y2="620.5" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset=".323" stop-color="#9e2064"/><stop offset=".63" stop-color="#c92037"/><stop offset=".751" stop-color="#cd2335"/><stop offset="1" stop-color="#e97826"/></linearGradient><path fill="url(#gitea-maven__b)" d="M1255.7 1147.6c36.7-68.6 73.9-135.7 111.5-201 39-67.8 78.5-133.6 118.4-197 2.3-3.7 4.7-7.5 7-11.3 39.4-62.4 79.2-122.4 119.3-179.8l-120.8-228c-9.1 11.1-18.2 22.4-27.5 33.9-34.8 43.4-71 90.1-108.1 139.6-41.8 55.8-84.8 115.4-128.5 177.9-40.3 57.8-81.2 118.3-122.1 180.9-34.8 53.3-69.8 108.2-104.5 164.5l-3.9 6.3 157.2 310.5c33.6-66.5 67.6-132.1 102-196.5"/><linearGradient id="gitea-maven__c" x1="-9071.2" x2="-6533.2" y1="1047.7" y2="1047.7" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#282662"/><stop offset=".095" stop-color="#662e8d"/><stop offset=".788" stop-color="#9f2064"/><stop offset=".949" stop-color="#cd2032"/></linearGradient><path fill="url(#gitea-maven__c)" d="M539.7 2897.1c-20.8 57.2-41.7 115.4-62.7 174.9-.3.9-.6 1.7-.9 2.6-3 8.4-5.9 16.8-8.9 25.2-14.1 40.1-26.4 76.2-54.5 158.3 46.3 21.1 83.5 76.7 118.7 139.8-3.7-65.3-30.8-126.7-82.1-174.2 228.3 10.3 425-47.4 526.7-214.3 9.1-14.9 17.4-30.5 24.9-47.2-46.2 58.6-103.5 83.5-211.4 77.4-.2.1-.5.2-.7.3.2-.1.5-.2.7-.3 158.8-71.1 238.5-139.3 308.9-252.4 16.7-26.8 32.9-56.1 49.5-88.6-138.9 142.6-299.8 183.2-469.3 152.4l-127.1 13.9c-4 10.7-7.9 21.4-11.8 32.2"/><linearGradient id="gitea-maven__d" x1="-9346.1" x2="-5087" y1="580.82" y2="580.82" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset=".323" stop-color="#9e2064"/><stop offset=".63" stop-color="#c92037"/><stop offset=".751" stop-color="#cd2335"/><stop offset="1" stop-color="#e97826"/></linearGradient><path fill="url(#gitea-maven__d)" d="M599 2612.4c27.5-71 55.8-142.8 84.8-215.3 27.8-69.4 56.4-139.2 85.6-209.4s59.1-140.5 89.6-210.9c31-71.6 62.7-143.1 94.9-214.2 31.9-70.3 64.4-140.3 97.4-209.6 11.9-25 23.9-49.9 35.9-74.7q31.2-64.35 63-127.5c1.1-2.3 2.3-4.5 3.5-6.8l-157.2-310.5c-2.6 4.2-5.1 8.4-7.7 12.6-36.6 59.8-73.1 121-108.9 183.5-36.2 63.1-71.7 127.4-106.4 192.6-29.3 55-57.9 110.5-85.7 166.5-5.6 11.4-11.1 22.6-16.6 33.9-34.3 70.5-65.2 138.6-93.2 204.1-31.7 74.2-59.6 145.1-84 212.3-16.1 44.2-30.7 86.9-44.1 127.9-11 35-21.5 70.1-31.4 105-23.5 82.3-43.7 164.4-60.3 246.2l158 311.9c20.9-55.8 42.3-112.3 64.3-169.6 6.1-15.9 12.3-32 18.5-48"/><linearGradient id="gitea-maven__e" x1="-9035.5" x2="-6797.2" y1="638.44" y2="638.44" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#282662"/><stop offset=".095" stop-color="#662e8d"/><stop offset=".788" stop-color="#9f2064"/><stop offset=".949" stop-color="#cd2032"/></linearGradient><path fill="url(#gitea-maven__e)" d="M356.1 2529.2c-19.8 99.8-33.9 199.2-41 298-.2 3.5-.6 6.9-.8 10.4-49.3-79-181.3-156.1-181-155.4 94.5 137 166.2 273 176.9 406.5-50.6 10.4-119.9-4.6-200-34.1 83.5 76.7 146.2 97.9 170.6 103.6-76.7 4.8-156.6 57.5-237.1 118.2 117.7-48 212.8-67 280.9-51.6-108 305.8-216.3 643.4-324.6 1001.8 33.2-9.8 53-32.1 64.1-62.3 19.3-64.9 147.4-490.7 348.1-1050.4 5.7-15.9 11.5-31.9 17.3-48 1.6-4.5 3.3-9 4.9-13.4 21.2-58.7 43.2-118.6 65.9-179.7 5.2-13.9 10.4-27.8 15.6-41.8.1-.3.2-.6.3-.8l-157.8-311.8c-.7 3.5-1.6 7.1-2.3 10.8"/><linearGradient id="gitea-maven__f" x1="-9346.1" x2="-5087" y1="1021.6" y2="1021.6" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset=".323" stop-color="#9e2064"/><stop offset=".63" stop-color="#c92037"/><stop offset=".751" stop-color="#cd2335"/><stop offset="1" stop-color="#e97826"/></linearGradient><path fill="url(#gitea-maven__f)" d="M1178.1 1370.3c-4.5 9.2-9 18.5-13.6 27.9-13.6 28.1-27.4 56.7-41.4 86-15.1 31.7-30.3 64.1-45.7 97.3-7.8 16.7-15.5 33.5-23.4 50.6-23.5 51.1-47.3 103.9-71.5 158.4-29.7 67-60 136.6-90.7 208.9-29.3 68.9-59 140.2-89.1 214-28.6 70-57.5 142.3-86.8 216.8-26.1 66.5-52.4 134.7-79 204.9-1.3 3.4-2.6 6.8-3.8 10.2-26.4 69.7-53 141.3-79.8 214.7-.6 1.7-1.2 3.4-1.8 5l127.1-13.9c-2.5-.5-5.1-.8-7.6-1.3 152-18.9 354-132.5 484.6-272.7 60.2-64.6 114.8-140.8 165.3-230 37.6-66.4 72.9-140 106.5-221.5 29.4-71.2 57.6-148.3 84.8-231.9-34.9 18.4-74.9 31.9-119 41.3-7.7 1.6-15.6 3.2-23.6 4.6s-16.1 2.7-24.3 3.8c.1 0 .2-.1.3-.1 141.7-54.5 231.1-159.8 296.1-288.7-37.3 25.4-97.9 58.7-170.5 74.7-9.9 2.2-20 3.9-30.2 5.4-2.6.4-5.1.8-7.6 1.1.1 0 .1-.1.2-.1 0 0 .1 0 .1-.1 49.2-20.6 90.7-43.6 126.7-70.8 7.7-5.8 15.2-11.8 22.4-18.1 11-9.5 21.4-19.5 31.4-30 6.4-6.7 12.6-13.6 18.6-20.8 14.1-16.8 27.3-34.9 39.7-54.6 3.8-6 7.5-12.1 11.2-18.4 4.7-9.1 9.2-18 13.6-26.8 19.8-39.8 35.6-75.3 48.2-106.5 6.3-15.6 11.8-30 16.5-43.4 1.9-5.3 3.7-10.5 5.4-15.5 5-15 9.1-28.3 12.3-40 4.8-17.5 7.7-31.4 9.3-41.5-4.8 3.8-10.3 7.6-16.5 11.3-42.8 25.6-116.2 48.8-175.4 59.7l116.7-12.8-116.7 12.8c-.9.2-1.8.3-2.7.5-5.9 1-11.9 1.9-17.9 2.9 1.1-.5 2-1 3.1-1.4l-399.3 43.8c-.7 1.4-1.4 2.8-2.2 4.3"/><linearGradient id="gitea-maven__g" x1="-9610.3" x2="-5351.2" y1="999.73" y2="999.73" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset=".323" stop-color="#9e2064"/><stop offset=".63" stop-color="#c92037"/><stop offset=".751" stop-color="#cd2335"/><stop offset="1" stop-color="#e97826"/></linearGradient><path fill="url(#gitea-maven__g)" d="M1627.6 563.1c-35.5 54.5-74.3 116.4-116 186.5-2.2 3.6-4.4 7.4-6.6 11.1-36 60.7-74.3 127.3-114.5 200.3-34.8 63-71 130.6-108.6 203.3-32.8 63.3-66.7 130.5-101.5 201.6l399.3-43.8c116.3-53.5 168.3-101.9 218.8-171.9 13.4-19.3 26.9-39.5 40.3-60.4 41-64 81.2-134.5 117.2-204.6 34.7-67.7 65.3-134.8 88.8-195.3 14.9-38.5 26.9-74.3 35.2-105.7 7.3-27.7 13-54 17.4-79.1-155.5 26.5-345.9 51.9-469.8 58"/><path fill="#be202e" d="M1369.6 1939.4c-8 1.4-16.1 2.7-24.3 3.8 8.2-1.1 16.3-2.4 24.3-3.8"/><path fill="#be202e" d="M1369.6 1939.4c-8 1.4-16.1 2.7-24.3 3.8 8.2-1.1 16.3-2.4 24.3-3.8" opacity=".35"/><linearGradient id="gitea-maven__h" x1="-9346.1" x2="-5087" y1="1152.7" y2="1152.7" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset=".323" stop-color="#9e2064"/><stop offset=".63" stop-color="#c92037"/><stop offset=".751" stop-color="#cd2335"/><stop offset="1" stop-color="#e97826"/></linearGradient><path fill="url(#gitea-maven__h)" d="M1369.6 1939.4c-8 1.4-16.1 2.7-24.3 3.8 8.2-1.1 16.3-2.4 24.3-3.8"/><path fill="#be202e" d="M1433.2 1735.7c2.6-.3 5.1-.8 7.6-1.1-2.5.3-5 .7-7.6 1.1"/><path fill="#be202e" d="M1433.2 1735.7c2.6-.3 5.1-.8 7.6-1.1-2.5.3-5 .7-7.6 1.1" opacity=".35"/><linearGradient id="gitea-maven__i" x1="-9346.1" x2="-5087" y1="1137.7" y2="1137.7" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset=".323" stop-color="#9e2064"/><stop offset=".63" stop-color="#c92037"/><stop offset=".751" stop-color="#cd2335"/><stop offset="1" stop-color="#e97826"/></linearGradient><path fill="url(#gitea-maven__i)" d="M1433.2 1735.7c2.6-.3 5.1-.8 7.6-1.1-2.5.3-5 .7-7.6 1.1"/><path fill="#be202e" d="M1433.5 1735.6s.1 0 .1-.1c0 0-.1 0-.1.1"/><path fill="#be202e" d="M1433.5 1735.6s.1 0 .1-.1c0 0-.1 0-.1.1" opacity=".35"/><linearGradient id="gitea-maven__j" x1="-6953.4" x2="-6012" y1="1134.7" y2="1134.7" gradientTransform="rotate(-65.001 -2052.931 -4777.847)" gradientUnits="userSpaceOnUse"><stop offset=".323" stop-color="#9e2064"/><stop offset=".63" stop-color="#c92037"/><stop offset=".751" stop-color="#cd2335"/><stop offset="1" stop-color="#e97826"/></linearGradient><path fill="url(#gitea-maven__j)" d="M1433.5 1735.6s.1 0 .1-.1c0 0-.1 0-.1.1"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/gitea-microsoftonline.svg b/public/assets/img/svg/gitea-microsoftonline.svg
index f2ce13ac22..c143eccbb6 100644
--- a/public/assets/img/svg/gitea-microsoftonline.svg
+++ b/public/assets/img/svg/gitea-microsoftonline.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 48 48" class="svg gitea-microsoftonline" width="16" height="16" aria-hidden="true"><path fill="url(#gitea-microsoftonline__a)" d="m20.084 3.026-.224.136a8 8 0 0 0-1.009.722l.648-.456H25L26 11l-5 5-5 3.475v4.008a8 8 0 0 0 3.857 6.844l5.264 3.186L14 40h-2.145l-3.998-2.42A8 8 0 0 1 4 30.737V17.26a8 8 0 0 1 3.86-6.846l12-7.258q.111-.068.224-.131Z"/><path fill="url(#gitea-microsoftonline__b)" d="m20.084 3.026-.224.136a8 8 0 0 0-1.009.722l.648-.456H25L26 11l-5 5-5 3.475v4.008a8 8 0 0 0 3.857 6.844l5.264 3.186L14 40h-2.145l-3.998-2.42A8 8 0 0 1 4 30.737V17.26a8 8 0 0 1 3.86-6.846l12-7.258q.111-.068.224-.131Z"/><path fill="url(#gitea-microsoftonline__c)" d="M32 19v4.48a8 8 0 0 1-3.857 6.844l-12 7.264a8 8 0 0 1-8.008.16l11.722 7.096a8 8 0 0 0 8.286 0l12-7.264A8 8 0 0 0 44 30.736V27.5L43 26z"/><path fill="url(#gitea-microsoftonline__d)" d="M32 19v4.48a8 8 0 0 1-3.857 6.844l-12 7.264a8 8 0 0 1-8.008.16l11.722 7.096a8 8 0 0 0 8.286 0l12-7.264A8 8 0 0 0 44 30.736V27.5L43 26z"/><path fill="url(#gitea-microsoftonline__e)" d="m40.14 10.415-12-7.258a8 8 0 0 0-8.042-.139l-.238.144A8 8 0 0 0 16 10.008v9.483l3.86-2.334a8 8 0 0 1 8.28 0l12 7.258A8 8 0 0 1 43.997 31q.004-.132.004-.263V17.26a8 8 0 0 0-3.86-6.845Z"/><path fill="url(#gitea-microsoftonline__f)" d="m40.14 10.415-12-7.258a8 8 0 0 0-8.042-.139l-.238.144A8 8 0 0 0 16 10.008v9.483l3.86-2.334a8 8 0 0 1 8.28 0l12 7.258A8 8 0 0 1 43.997 31q.004-.132.004-.263V17.26a8 8 0 0 0-3.86-6.845Z"/><path fill="url(#gitea-microsoftonline__g)" d="M4.004 30.998"/><path fill="url(#gitea-microsoftonline__h)" d="M4.004 30.998"/><defs><radialGradient id="gitea-microsoftonline__a" cx="0" cy="0" r="1" gradientTransform="rotate(110.528 5.021 11.358)scale(33.3657 58.1966)" gradientUnits="userSpaceOnUse"><stop offset=".064" stop-color="#AE7FE2"/><stop offset="1" stop-color="#0078D4"/></radialGradient><radialGradient id="gitea-microsoftonline__c" cx="0" cy="0" r="1" gradientTransform="matrix(30.7198 -4.51832 2.98465 20.29248 10.43 36.351)" gradientUnits="userSpaceOnUse"><stop offset=".134" stop-color="#D59DFF"/><stop offset="1" stop-color="#5E438F"/></radialGradient><radialGradient id="gitea-microsoftonline__e" cx="0" cy="0" r="1" gradientTransform="matrix(-24.1583 -6.12555 10.3118 -40.66824 41.055 26.504)" gradientUnits="userSpaceOnUse"><stop offset=".058" stop-color="#50E6FF"/><stop offset="1" stop-color="#436DCD"/></radialGradient><radialGradient id="gitea-microsoftonline__g" cx="0" cy="0" r="1" gradientTransform="matrix(-24.1583 -6.12555 10.3118 -40.66824 41.055 26.504)" gradientUnits="userSpaceOnUse"><stop offset=".058" stop-color="#50E6FF"/><stop offset="1" stop-color="#436DCD"/></radialGradient><linearGradient id="gitea-microsoftonline__b" x1="17.512" x2="12.751" y1="37.868" y2="29.635" gradientUnits="userSpaceOnUse"><stop stop-color="#114A8B"/><stop offset="1" stop-color="#0078D4" stop-opacity="0"/></linearGradient><linearGradient id="gitea-microsoftonline__d" x1="40.357" x2="35.255" y1="25.377" y2="32.692" gradientUnits="userSpaceOnUse"><stop stop-color="#493474"/><stop offset="1" stop-color="#8C66BA" stop-opacity="0"/></linearGradient><linearGradient id="gitea-microsoftonline__f" x1="16.976" x2="24.487" y1="3.057" y2="3.057" gradientUnits="userSpaceOnUse"><stop stop-color="#2D3F80"/><stop offset="1" stop-color="#436DCD" stop-opacity="0"/></linearGradient><linearGradient id="gitea-microsoftonline__h" x1="16.976" x2="24.487" y1="3.057" y2="3.057" gradientUnits="userSpaceOnUse"><stop stop-color="#2D3F80"/><stop offset="1" stop-color="#436DCD" stop-opacity="0"/></linearGradient></defs></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 48 48" class="svg gitea-microsoftonline" width="16" height="16" aria-hidden="true"><path fill="url(#gitea-microsoftonline__a)" d="m20.084 3.026-.224.136a8 8 0 0 0-1.009.722l.648-.456H25L26 11l-5 5-5 3.475v4.008a8 8 0 0 0 3.857 6.844l5.264 3.186L14 40h-2.145l-3.998-2.42A8 8 0 0 1 4 30.737V17.26a8 8 0 0 1 3.86-6.846l12-7.258q.111-.068.224-.131Z"/><path fill="url(#gitea-microsoftonline__b)" d="m20.084 3.026-.224.136a8 8 0 0 0-1.009.722l.648-.456H25L26 11l-5 5-5 3.475v4.008a8 8 0 0 0 3.857 6.844l5.264 3.186L14 40h-2.145l-3.998-2.42A8 8 0 0 1 4 30.737V17.26a8 8 0 0 1 3.86-6.846l12-7.258q.111-.068.224-.131Z"/><path fill="url(#gitea-microsoftonline__c)" d="M32 19v4.48a8 8 0 0 1-3.857 6.844l-12 7.264a8 8 0 0 1-8.008.16l11.722 7.096a8 8 0 0 0 8.286 0l12-7.264A8 8 0 0 0 44 30.736V27.5L43 26z"/><path fill="url(#gitea-microsoftonline__d)" d="M32 19v4.48a8 8 0 0 1-3.857 6.844l-12 7.264a8 8 0 0 1-8.008.16l11.722 7.096a8 8 0 0 0 8.286 0l12-7.264A8 8 0 0 0 44 30.736V27.5L43 26z"/><path fill="url(#gitea-microsoftonline__e)" d="m40.14 10.415-12-7.258a8 8 0 0 0-8.042-.139l-.238.144A8 8 0 0 0 16 10.008v9.483l3.86-2.334a8 8 0 0 1 8.28 0l12 7.258A8 8 0 0 1 43.997 31q.004-.132.004-.263V17.26a8 8 0 0 0-3.86-6.845Z"/><path fill="url(#gitea-microsoftonline__f)" d="m40.14 10.415-12-7.258a8 8 0 0 0-8.042-.139l-.238.144A8 8 0 0 0 16 10.008v9.483l3.86-2.334a8 8 0 0 1 8.28 0l12 7.258A8 8 0 0 1 43.997 31q.004-.132.004-.263V17.26a8 8 0 0 0-3.86-6.845Z"/><path fill="url(#gitea-microsoftonline__g)" d="M4.004 30.998"/><path fill="url(#gitea-microsoftonline__h)" d="M4.004 30.998"/><defs><radialGradient id="gitea-microsoftonline__a" cx="0" cy="0" r="1" gradientTransform="rotate(110.528 5.021 11.358)scale(33.3657 58.1966)" gradientUnits="userSpaceOnUse"><stop offset=".064" stop-color="#ae7fe2"/><stop offset="1" stop-color="#0078d4"/></radialGradient><radialGradient id="gitea-microsoftonline__c" cx="0" cy="0" r="1" gradientTransform="rotate(-8.367 253.693 -53.118)scale(31.0503 20.5108)" gradientUnits="userSpaceOnUse"><stop offset=".134" stop-color="#d59dff"/><stop offset="1" stop-color="#5e438f"/></radialGradient><radialGradient id="gitea-microsoftonline__e" cx="0" cy="0" r="1" gradientTransform="rotate(194.228 22.182 10.69)scale(24.9228 41.9552)" gradientUnits="userSpaceOnUse"><stop offset=".058" stop-color="#50e6ff"/><stop offset="1" stop-color="#436dcd"/></radialGradient><radialGradient id="gitea-microsoftonline__g" cx="0" cy="0" r="1" gradientTransform="rotate(194.228 22.182 10.69)scale(24.9228 41.9552)" gradientUnits="userSpaceOnUse"><stop offset=".058" stop-color="#50e6ff"/><stop offset="1" stop-color="#436dcd"/></radialGradient><linearGradient id="gitea-microsoftonline__b" x1="17.512" x2="12.751" y1="37.868" y2="29.635" gradientUnits="userSpaceOnUse"><stop stop-color="#114a8b"/><stop offset="1" stop-color="#0078d4" stop-opacity="0"/></linearGradient><linearGradient id="gitea-microsoftonline__d" x1="40.357" x2="35.255" y1="25.377" y2="32.692" gradientUnits="userSpaceOnUse"><stop stop-color="#493474"/><stop offset="1" stop-color="#8c66ba" stop-opacity="0"/></linearGradient><linearGradient id="gitea-microsoftonline__f" x1="16.976" x2="24.487" y1="3.057" y2="3.057" gradientUnits="userSpaceOnUse"><stop stop-color="#2d3f80"/><stop offset="1" stop-color="#436dcd" stop-opacity="0"/></linearGradient><linearGradient id="gitea-microsoftonline__h" x1="16.976" x2="24.487" y1="3.057" y2="3.057" gradientUnits="userSpaceOnUse"><stop stop-color="#2d3f80"/><stop offset="1" stop-color="#436dcd" stop-opacity="0"/></linearGradient></defs></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/gitea-npm.svg b/public/assets/img/svg/gitea-npm.svg
index 7ef74e72bd..2b05c79353 100644
--- a/public/assets/img/svg/gitea-npm.svg
+++ b/public/assets/img/svg/gitea-npm.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 7" class="svg gitea-npm" width="16" height="16" aria-hidden="true"><path fill="#CB3837" d="M0 0h18v6H9v1H5V6H0zm1 5h2V2h1v3h1V1H1zm5-4v5h2V5h2V1zm2 1h1v2H8zm3-1v4h2V2h1v3h1V2h1v3h1V1z"/><path fill="#fff" d="M1 5h2V2h1v3h1V1H1zM6 1v5h2V5h2V1zm3 3H8V2h1zM11 1v4h2V2h1v3h1V2h1v3h1V1z"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 7" class="svg gitea-npm" width="16" height="16" aria-hidden="true"><path fill="#cb3837" d="M0 0h18v6H9v1H5V6H0zm1 5h2V2h1v3h1V1H1zm5-4v5h2V5h2V1zm2 1h1v2H8zm3-1v4h2V2h1v3h1V2h1v3h1V1z"/><path fill="#fff" d="M1 5h2V2h1v3h1V1H1zM6 1v5h2V5h2V1zm3 3H8V2h1zM11 1v4h2V2h1v3h1V2h1v3h1V1z"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/gitea-onedev.svg b/public/assets/img/svg/gitea-onedev.svg
index 94ad1bab31..7ecd18895d 100644
--- a/public/assets/img/svg/gitea-onedev.svg
+++ b/public/assets/img/svg/gitea-onedev.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 700 700" class="svg gitea-onedev" width="16" height="16" aria-hidden="true"><path d="M315.5 99.6c-29.5 4-55.8 12-81.2 24.8L223 130l-5.2-4c-14.9-11.3-37.6-14.9-55.8-9-19.1 6.3-35.1 22.2-41.1 41-2.7 8.3-3.6 22.9-1.9 31.2 1.5 8 5 16.5 9.1 22.5 3.1 4.7 3.1 4.8 1.4 7.8C106 260 95.1 294.4 92 337.7c-1.1 15.7-.1 40.2 2.1 53l1.1 6.5-4.9 4.4c-2.8 2.3-7.5 7.6-10.6 11.6-19.4 25.5-24.7 57.9-14.4 88.3 9.2 26.9 31.2 48.8 58.4 58.1 20.6 6.9 40.6 7 61.1.1l6.7-2.2 10.5 7.1c45.6 31 92 45.5 146 45.5 33 0 61.6-5.2 91-16.4 67.6-25.8 122.9-81.1 148.4-148.4l2.7-7.2 7.7-3.8c9.1-4.5 21.1-15.7 25.9-24.3 21.1-37.5-1-84.3-43.2-91.7-19.9-3.5-39.3 2.7-53.9 17.2-7.1 7.1-11.7 14.5-15.3 24.7-3.4 9.4-3.8 25.8-.9 35.3 2.8 9.5 8.5 19.3 15.3 26.4 7.2 7.6 7.2 6 0 20.5-8.9 18.1-20.3 34.1-35.2 49.5-34.6 35.7-78.2 56.3-128.3 60.3-42.8 3.4-89.3-8.9-125-33-1.1-.8-1-1.7.8-5.2 12.1-23.6 13.5-53.7 3.9-78-8.7-21.8-27.5-41.6-48.6-51.2-9-4.1-22.7-7.4-34-8.3l-9.1-.7-.8-9.6c-3.5-46.9 13.5-99.8 45.5-141.7 6.5-8.6 24.3-26.7 33.6-34.2 43.8-35.6 101.3-52.8 158.1-47.2 39.9 3.9 79 19.1 110.6 43 16.9 12.8 37.5 34.9 48.6 52l4.3 6.7-3.3 5.2c-2.9 4.7-3.3 6.3-3.6 13.4-.3 7.3-.1 8.6 2.5 13.6 3.2 6.1 10.2 12 16.3 13.9 22.8 6.8 43-16.9 32.6-38.2-3.1-6.4-9.3-12.2-14.7-13.8-2.5-.8-4.1-2.1-5.2-4.3-.9-1.7-3.2-5.8-5.1-9.2l-3.5-6 3.6-5c17.7-24.4 15.8-57.5-4.4-79.4-8-8.6-15.5-13.6-25.9-17.2-19.8-6.8-38.9-4.2-56.5 7.8l-7.8 5.3-15.3-7.4c-27.9-13.4-55-21.3-84-24.4-13.3-1.5-48.1-1.2-60.3.5"/><path d="M271.8 271.1c-13.9 2.1-30.5 17.3-40.5 37.4-18.3 36.4-13.4 81.5 9.8 91.5 15.2 6.5 34.5-2.7 48.6-23.2 5.5-8 9.7-15.7 9-16.5-.3-.2-2 .3-3.8 1.2-2.4 1.3-5.1 1.6-10.5 1.3-6.1-.3-7.9-.8-11.6-3.4-8.9-6.2-12.4-19.1-7.9-29 2.4-5.2 9-10.8 14.7-12.4 9.1-2.6 20 1.4 25.2 9.2l2.7 4.2.3-12.4c.4-18.9-3.4-31.6-12.4-40.5-6.3-6.3-14.2-8.8-23.6-7.4M420.5 271c-11.6 1.9-20.2 11.3-24.9 27-2.1 6.9-3.1 20-2.2 27.4l.8 5.7 2.1-3.2c10.2-15 31.6-14 39.9 2 6 11.5 1.5 25.1-10.4 31.2-5 2.5-15 2.6-20 .1l-3.6-1.9 1.4 3.3c6.1 14.5 20 30.1 32.3 36.1 5.7 2.8 14.4 4 20.4 2.9 5.2-1 12.1-6.1 16.1-11.9 18.1-26.4 8.1-79-20-105.8-10.8-10.2-21.6-14.6-31.9-12.9M322.5 431.9c-16.1 1.6-23.5 6.1-23.5 14.3 0 11.4 13 21.1 34 25.4 10.2 2 31.2 1.5 40.5-1 13.5-3.7 23.8-10.3 27.6-17.7 4.9-9.7-.2-17.1-13.8-20-6.1-1.2-54.2-2-64.8-1"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 700" class="svg gitea-onedev" width="16" height="16" aria-hidden="true"><path d="M315.5 99.6c-29.5 4-55.8 12-81.2 24.8L223 130l-5.2-4c-14.9-11.3-37.6-14.9-55.8-9-19.1 6.3-35.1 22.2-41.1 41-2.7 8.3-3.6 22.9-1.9 31.2 1.5 8 5 16.5 9.1 22.5 3.1 4.7 3.1 4.8 1.4 7.8C106 260 95.1 294.4 92 337.7c-1.1 15.7-.1 40.2 2.1 53l1.1 6.5-4.9 4.4c-2.8 2.3-7.5 7.6-10.6 11.6-19.4 25.5-24.7 57.9-14.4 88.3 9.2 26.9 31.2 48.8 58.4 58.1 20.6 6.9 40.6 7 61.1.1l6.7-2.2 10.5 7.1c45.6 31 92 45.5 146 45.5 33 0 61.6-5.2 91-16.4 67.6-25.8 122.9-81.1 148.4-148.4l2.7-7.2 7.7-3.8c9.1-4.5 21.1-15.7 25.9-24.3 21.1-37.5-1-84.3-43.2-91.7-19.9-3.5-39.3 2.7-53.9 17.2-7.1 7.1-11.7 14.5-15.3 24.7-3.4 9.4-3.8 25.8-.9 35.3 2.8 9.5 8.5 19.3 15.3 26.4 7.2 7.6 7.2 6 0 20.5-8.9 18.1-20.3 34.1-35.2 49.5-34.6 35.7-78.2 56.3-128.3 60.3-42.8 3.4-89.3-8.9-125-33-1.1-.8-1-1.7.8-5.2 12.1-23.6 13.5-53.7 3.9-78-8.7-21.8-27.5-41.6-48.6-51.2-9-4.1-22.7-7.4-34-8.3l-9.1-.7-.8-9.6c-3.5-46.9 13.5-99.8 45.5-141.7 6.5-8.6 24.3-26.7 33.6-34.2 43.8-35.6 101.3-52.8 158.1-47.2 39.9 3.9 79 19.1 110.6 43 16.9 12.8 37.5 34.9 48.6 52l4.3 6.7-3.3 5.2c-2.9 4.7-3.3 6.3-3.6 13.4-.3 7.3-.1 8.6 2.5 13.6 3.2 6.1 10.2 12 16.3 13.9 22.8 6.8 43-16.9 32.6-38.2-3.1-6.4-9.3-12.2-14.7-13.8-2.5-.8-4.1-2.1-5.2-4.3-.9-1.7-3.2-5.8-5.1-9.2l-3.5-6 3.6-5c17.7-24.4 15.8-57.5-4.4-79.4-8-8.6-15.5-13.6-25.9-17.2-19.8-6.8-38.9-4.2-56.5 7.8l-7.8 5.3-15.3-7.4c-27.9-13.4-55-21.3-84-24.4-13.3-1.5-48.1-1.2-60.3.5"/><path d="M271.8 271.1c-13.9 2.1-30.5 17.3-40.5 37.4-18.3 36.4-13.4 81.5 9.8 91.5 15.2 6.5 34.5-2.7 48.6-23.2 5.5-8 9.7-15.7 9-16.5-.3-.2-2 .3-3.8 1.2-2.4 1.3-5.1 1.6-10.5 1.3-6.1-.3-7.9-.8-11.6-3.4-8.9-6.2-12.4-19.1-7.9-29 2.4-5.2 9-10.8 14.7-12.4 9.1-2.6 20 1.4 25.2 9.2l2.7 4.2.3-12.4c.4-18.9-3.4-31.6-12.4-40.5-6.3-6.3-14.2-8.8-23.6-7.4M420.5 271c-11.6 1.9-20.2 11.3-24.9 27-2.1 6.9-3.1 20-2.2 27.4l.8 5.7 2.1-3.2c10.2-15 31.6-14 39.9 2 6 11.5 1.5 25.1-10.4 31.2-5 2.5-15 2.6-20 .1l-3.6-1.9 1.4 3.3c6.1 14.5 20 30.1 32.3 36.1 5.7 2.8 14.4 4 20.4 2.9 5.2-1 12.1-6.1 16.1-11.9 18.1-26.4 8.1-79-20-105.8-10.8-10.2-21.6-14.6-31.9-12.9M322.5 431.9c-16.1 1.6-23.5 6.1-23.5 14.3 0 11.4 13 21.1 34 25.4 10.2 2 31.2 1.5 40.5-1 13.5-3.7 23.8-10.3 27.6-17.7 4.9-9.7-.2-17.1-13.8-20-6.1-1.2-54.2-2-64.8-1"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/gitea-openid.svg b/public/assets/img/svg/gitea-openid.svg
index f4702d2cdf..10c37145a3 100644
--- a/public/assets/img/svg/gitea-openid.svg
+++ b/public/assets/img/svg/gitea-openid.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 2400 2400" class="svg gitea-openid" width="16" height="16" aria-hidden="true"><path fill="#ff7c00" d="m1270 218.3-173.1 84.3-.7 981.8c-.3 540 .2 981.4 1.1 981l174.5-81.8 172.3-80.8v-984.4c0-541.7-.2-984.7-.4-984.4z"/><path fill="#aaa" d="M981.9 785.5c-425.3 63.2-766.5 264.1-889 523a491.5 491.5 0 0 0-43.6 146c-4.2 29.2-4.7 95-1.2 124 19 152.6 115.2 299.9 273.2 418.8 147.7 111 350.5 196.5 568.6 239.7 59 11.6 179 29 200.5 29 2.3 0 3-23.2 3-109.1v-109.2l-5.1-1-37.9-6a1182 1182 0 0 1-305.4-90.6c-122.2-55.7-225.1-137.7-284.6-226.4-107.5-160.5-81.3-344.3 70-491.3 57-55.5 115.4-95.2 199.5-136.1a1112.6 1112.6 0 0 1 269.4-89.2l29.7-6c3.7-1.2 4-8.6 4-111.5V779.5l-6.3.2a823 823 0 0 0-44.8 5.8m525 104c0 103 .2 110.4 4.1 111.6l29.5 6a1221.6 1221.6 0 0 1 207.7 61.3A1088 1088 0 0 1 1862 1123c4.6 3.7 1.4 5.8-88 56-51.1 28.5-93 52.7-93 53.4 0 1.9 671.6 146.8 673.2 145.2 1.2-1.2-45.5-496-47-497.6-.2-.2-38.5 21-85 47.2l-89.6 50.2c-4.2 2-8.8.2-27.9-10.7-130.8-75-289.6-132.2-460.8-166.1a1871 1871 0 0 0-132.9-21.1c-4 0-4.2 6.7-4.2 110z"/><path fill="#cbaa7c" d="M1094.5 2156.9c0 60.6.3 85.5.5 55 .5-30.2.5-79.9 0-110.3-.2-30.2-.5-5.3-.5 55.3"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2400 2400" class="svg gitea-openid" width="16" height="16" aria-hidden="true"><path fill="#ff7c00" d="m1270 218.3-173.1 84.3-.7 981.8c-.3 540 .2 981.4 1.1 981l174.5-81.8 172.3-80.8v-984.4c0-541.7-.2-984.7-.4-984.4z"/><path fill="#aaa" d="M981.9 785.5c-425.3 63.2-766.5 264.1-889 523a491.5 491.5 0 0 0-43.6 146c-4.2 29.2-4.7 95-1.2 124 19 152.6 115.2 299.9 273.2 418.8 147.7 111 350.5 196.5 568.6 239.7 59 11.6 179 29 200.5 29 2.3 0 3-23.2 3-109.1v-109.2l-5.1-1-37.9-6a1182 1182 0 0 1-305.4-90.6c-122.2-55.7-225.1-137.7-284.6-226.4-107.5-160.5-81.3-344.3 70-491.3 57-55.5 115.4-95.2 199.5-136.1a1112.6 1112.6 0 0 1 269.4-89.2l29.7-6c3.7-1.2 4-8.6 4-111.5V779.5l-6.3.2a823 823 0 0 0-44.8 5.8m525 104c0 103 .2 110.4 4.1 111.6l29.5 6a1221.6 1221.6 0 0 1 207.7 61.3A1088 1088 0 0 1 1862 1123c4.6 3.7 1.4 5.8-88 56-51.1 28.5-93 52.7-93 53.4 0 1.9 671.6 146.8 673.2 145.2 1.2-1.2-45.5-496-47-497.6-.2-.2-38.5 21-85 47.2l-89.6 50.2c-4.2 2-8.8.2-27.9-10.7-130.8-75-289.6-132.2-460.8-166.1a1871 1871 0 0 0-132.9-21.1c-4 0-4.2 6.7-4.2 110z"/><path fill="#cbaa7c" d="M1094.5 2156.9c0 60.6.3 85.5.5 55 .5-30.2.5-79.9 0-110.3-.2-30.2-.5-5.3-.5 55.3"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/gitea-rubygems.svg b/public/assets/img/svg/gitea-rubygems.svg
index 4e43bdf2f4..7cd9d34e6a 100644
--- a/public/assets/img/svg/gitea-rubygems.svg
+++ b/public/assets/img/svg/gitea-rubygems.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 198.13 197.58" class="svg gitea-rubygems" width="16" height="16" aria-hidden="true"><defs><linearGradient id="gitea-rubygems__b" x1="194.9" x2="141.03" y1="153.56" y2="117.41" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#871101"/><stop offset=".99" stop-color="#911209"/><stop offset="1" stop-color="#911209"/></linearGradient><linearGradient id="gitea-rubygems__c" x1="151.8" x2="97.93" y1="217.79" y2="181.64" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#871101"/><stop offset=".99" stop-color="#911209"/><stop offset="1" stop-color="#911209"/></linearGradient><linearGradient id="gitea-rubygems__d" x1="38.696" x2="47.047" y1="127.39" y2="181.66" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fff"/><stop offset=".23" stop-color="#E57252"/><stop offset=".46" stop-color="#DE3B20"/><stop offset=".99" stop-color="#A60003"/><stop offset="1" stop-color="#A60003"/></linearGradient><linearGradient id="gitea-rubygems__e" x1="96.133" x2="99.21" y1="76.715" y2="132.1" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fff"/><stop offset=".23" stop-color="#E4714E"/><stop offset=".56" stop-color="#BE1A0D"/><stop offset=".99" stop-color="#A80D00"/><stop offset="1" stop-color="#A80D00"/></linearGradient><linearGradient id="gitea-rubygems__f" x1="147.1" x2="156.31" y1="25.521" y2="65.216" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fff"/><stop offset=".18" stop-color="#E46342"/><stop offset=".4" stop-color="#C82410"/><stop offset=".99" stop-color="#A80D00"/><stop offset="1" stop-color="#A80D00"/></linearGradient><linearGradient id="gitea-rubygems__g" x1="118.98" x2="158.67" y1="11.542" y2="-8.305" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fff"/><stop offset=".54" stop-color="#C81F11"/><stop offset=".99" stop-color="#BF0905"/><stop offset="1" stop-color="#BF0905"/></linearGradient><linearGradient id="gitea-rubygems__h" x1="3.903" x2="7.17" y1="113.55" y2="146.26" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fff"/><stop offset=".31" stop-color="#DE4024"/><stop offset=".99" stop-color="#BF190B"/><stop offset="1" stop-color="#BF190B"/></linearGradient><linearGradient id="gitea-rubygems__i" x1="-18.556" x2="135.02" y1="155.1" y2="-2.809" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#BD0012"/><stop offset=".07" stop-color="#fff"/><stop offset=".17" stop-color="#fff"/><stop offset=".27" stop-color="#C82F1C"/><stop offset=".33" stop-color="#820C01"/><stop offset=".46" stop-color="#A31601"/><stop offset=".72" stop-color="#B31301"/><stop offset=".99" stop-color="#E82609"/><stop offset="1" stop-color="#E82609"/></linearGradient><linearGradient id="gitea-rubygems__q" x1="99.075" x2="52.818" y1="171.03" y2="159.62" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#8C0C01"/><stop offset=".54" stop-color="#990C00"/><stop offset=".99" stop-color="#A80D0E"/><stop offset="1" stop-color="#A80D0E"/></linearGradient><linearGradient id="gitea-rubygems__r" x1="178.53" x2="137.43" y1="115.51" y2="78.684" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#7E110B"/><stop offset=".99" stop-color="#9E0C00"/><stop offset="1" stop-color="#9E0C00"/></linearGradient><linearGradient id="gitea-rubygems__s" x1="193.62" x2="173.15" y1="47.937" y2="26.054" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#79130D"/><stop offset=".99" stop-color="#9E120B"/><stop offset="1" stop-color="#9E120B"/></linearGradient><linearGradient id="gitea-rubygems__v" x1="26.67" x2="9.989" y1="197.34" y2="140.74" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#8B2114"/><stop offset=".43" stop-color="#9E100A"/><stop offset=".99" stop-color="#B3100C"/><stop offset="1" stop-color="#B3100C"/></linearGradient><linearGradient id="gitea-rubygems__w" x1="154.64" x2="192.04" y1="9.798" y2="26.306" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#B31000"/><stop offset=".44" stop-color="#910F08"/><stop offset=".99" stop-color="#791C12"/><stop offset="1" stop-color="#791C12"/></linearGradient><linearGradient id="gitea-rubygems__a" x1="174.07" x2="132.28" y1="215.55" y2="141.75" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#FB7655"/><stop offset=".41" stop-color="#E42B1E"/><stop offset=".99" stop-color="#900"/><stop offset="1" stop-color="#900"/></linearGradient><radialGradient id="gitea-rubygems__t" cx="143.83" cy="79.388" r="50.358" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#A80D00"/><stop offset=".99" stop-color="#7E0E08"/><stop offset="1" stop-color="#7E0E08"/></radialGradient><radialGradient id="gitea-rubygems__u" cx="74.092" cy="145.75" r="66.944" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#A30C00"/><stop offset=".99" stop-color="#800E08"/><stop offset="1" stop-color="#800E08"/></radialGradient></defs><path fill="url(#gitea-rubygems__a)" fill-rule="evenodd" d="M153.5 130.41 40.38 197.58l146.47-9.94 11.28-147.69z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__b)" fill-rule="evenodd" d="m187.09 187.54-12.59-86.89-34.29 45.28z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__c)" fill-rule="evenodd" d="m187.26 187.54-92.23-7.24-54.16 17.09z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__d)" fill-rule="evenodd" d="m41 197.41 23.04-75.48-50.7 10.84z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__e)" fill-rule="evenodd" d="M140.2 146.18 119 63.14l-60.67 56.87z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__f)" fill-rule="evenodd" d="m193.32 64.31-57.35-46.84L120 69.1z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__g)" fill-rule="evenodd" d="m166.5.77-33.73 18.64L111.49.52z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__h)" fill-rule="evenodd" d="m0 158.09 14.13-25.77-11.43-30.7z" clip-rule="evenodd"/><path fill="#fff" fill-rule="evenodd" d="m1.94 100.65 11.5 32.62 49.97-11.211 57.05-53.02 16.1-51.139L111.209 0l-43.1 16.13C54.53 28.76 28.18 53.75 27.23 54.22c-.94.48-17.4 31.59-25.29 46.43" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__i)" fill-rule="evenodd" d="M42.32 42.05c29.43-29.18 67.37-46.42 81.93-31.73 14.551 14.69-.88 50.39-30.31 79.56s-66.9 47.36-81.45 32.67c-14.56-14.68.4-51.33 29.83-80.5" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__q)" fill-rule="evenodd" d="m41 197.38 22.86-75.72 75.92 24.39c-27.45 25.74-57.98 47.5-98.78 51.33" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__r)" fill-rule="evenodd" d="m120.56 68.89 19.49 77.2c22.93-24.11 43.51-50.03 53.589-82.09z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__s)" fill-rule="evenodd" d="M193.44 64.39c7.8-23.54 9.6-57.31-27.181-63.58l-30.18 16.67z" clip-rule="evenodd"/><path fill="#9e1209" fill-rule="evenodd" d="M0 157.75c1.08 38.851 29.11 39.43 41.05 39.771L13.47 133.11z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__t)" fill-rule="evenodd" d="M120.67 69.01c17.62 10.83 53.131 32.58 53.851 32.98 1.119.63 15.31-23.93 18.53-37.81z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__u)" fill-rule="evenodd" d="m63.83 121.66 30.56 58.96c18.07-9.8 32.22-21.74 45.18-34.53z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__v)" fill-rule="evenodd" d="m13.35 133.19-4.33 51.56c8.17 11.16 19.41 12.13 31.2 11.26-8.53-21.23-25.57-63.68-26.87-62.82" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__w)" fill-rule="evenodd" d="m135.9 17.61 60.71 8.52C193.37 12.4 183.42 3.54 166.46.77z" clip-rule="evenodd"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 198.13 197.58" class="svg gitea-rubygems" width="16" height="16" aria-hidden="true"><defs><linearGradient id="gitea-rubygems__b" x1="194.9" x2="141.03" y1="153.56" y2="117.41" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#871101"/><stop offset=".99" stop-color="#911209"/><stop offset="1" stop-color="#911209"/></linearGradient><linearGradient id="gitea-rubygems__c" x1="151.8" x2="97.93" y1="217.79" y2="181.64" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#871101"/><stop offset=".99" stop-color="#911209"/><stop offset="1" stop-color="#911209"/></linearGradient><linearGradient id="gitea-rubygems__d" x1="38.696" x2="47.047" y1="127.39" y2="181.66" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fff"/><stop offset=".23" stop-color="#e57252"/><stop offset=".46" stop-color="#de3b20"/><stop offset=".99" stop-color="#a60003"/><stop offset="1" stop-color="#a60003"/></linearGradient><linearGradient id="gitea-rubygems__e" x1="96.133" x2="99.21" y1="76.715" y2="132.1" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fff"/><stop offset=".23" stop-color="#e4714e"/><stop offset=".56" stop-color="#be1a0d"/><stop offset=".99" stop-color="#a80d00"/><stop offset="1" stop-color="#a80d00"/></linearGradient><linearGradient id="gitea-rubygems__f" x1="147.1" x2="156.31" y1="25.521" y2="65.216" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fff"/><stop offset=".18" stop-color="#e46342"/><stop offset=".4" stop-color="#c82410"/><stop offset=".99" stop-color="#a80d00"/><stop offset="1" stop-color="#a80d00"/></linearGradient><linearGradient id="gitea-rubygems__g" x1="118.98" x2="158.67" y1="11.542" y2="-8.305" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fff"/><stop offset=".54" stop-color="#c81f11"/><stop offset=".99" stop-color="#bf0905"/><stop offset="1" stop-color="#bf0905"/></linearGradient><linearGradient id="gitea-rubygems__h" x1="3.903" x2="7.17" y1="113.55" y2="146.26" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fff"/><stop offset=".31" stop-color="#de4024"/><stop offset=".99" stop-color="#bf190b"/><stop offset="1" stop-color="#bf190b"/></linearGradient><linearGradient id="gitea-rubygems__i" x1="-18.556" x2="135.02" y1="155.1" y2="-2.809" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#bd0012"/><stop offset=".07" stop-color="#fff"/><stop offset=".17" stop-color="#fff"/><stop offset=".27" stop-color="#c82f1c"/><stop offset=".33" stop-color="#820c01"/><stop offset=".46" stop-color="#a31601"/><stop offset=".72" stop-color="#b31301"/><stop offset=".99" stop-color="#e82609"/><stop offset="1" stop-color="#e82609"/></linearGradient><linearGradient id="gitea-rubygems__q" x1="99.075" x2="52.818" y1="171.03" y2="159.62" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#8c0c01"/><stop offset=".54" stop-color="#990c00"/><stop offset=".99" stop-color="#a80d0e"/><stop offset="1" stop-color="#a80d0e"/></linearGradient><linearGradient id="gitea-rubygems__r" x1="178.53" x2="137.43" y1="115.51" y2="78.684" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#7e110b"/><stop offset=".99" stop-color="#9e0c00"/><stop offset="1" stop-color="#9e0c00"/></linearGradient><linearGradient id="gitea-rubygems__s" x1="193.62" x2="173.15" y1="47.937" y2="26.054" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#79130d"/><stop offset=".99" stop-color="#9e120b"/><stop offset="1" stop-color="#9e120b"/></linearGradient><linearGradient id="gitea-rubygems__v" x1="26.67" x2="9.989" y1="197.34" y2="140.74" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#8b2114"/><stop offset=".43" stop-color="#9e100a"/><stop offset=".99" stop-color="#b3100c"/><stop offset="1" stop-color="#b3100c"/></linearGradient><linearGradient id="gitea-rubygems__w" x1="154.64" x2="192.04" y1="9.798" y2="26.306" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#b31000"/><stop offset=".44" stop-color="#910f08"/><stop offset=".99" stop-color="#791c12"/><stop offset="1" stop-color="#791c12"/></linearGradient><linearGradient id="gitea-rubygems__a" x1="174.07" x2="132.28" y1="215.55" y2="141.75" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fb7655"/><stop offset=".41" stop-color="#e42b1e"/><stop offset=".99" stop-color="#900"/><stop offset="1" stop-color="#900"/></linearGradient><radialGradient id="gitea-rubygems__t" cx="143.83" cy="79.388" r="50.358" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#a80d00"/><stop offset=".99" stop-color="#7e0e08"/><stop offset="1" stop-color="#7e0e08"/></radialGradient><radialGradient id="gitea-rubygems__u" cx="74.092" cy="145.75" r="66.944" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#a30c00"/><stop offset=".99" stop-color="#800e08"/><stop offset="1" stop-color="#800e08"/></radialGradient></defs><path fill="url(#gitea-rubygems__a)" fill-rule="evenodd" d="M153.5 130.41 40.38 197.58l146.47-9.94 11.28-147.69z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__b)" fill-rule="evenodd" d="m187.09 187.54-12.59-86.89-34.29 45.28z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__c)" fill-rule="evenodd" d="m187.26 187.54-92.23-7.24-54.16 17.09z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__d)" fill-rule="evenodd" d="m41 197.41 23.04-75.48-50.7 10.84z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__e)" fill-rule="evenodd" d="M140.2 146.18 119 63.14l-60.67 56.87z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__f)" fill-rule="evenodd" d="m193.32 64.31-57.35-46.84L120 69.1z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__g)" fill-rule="evenodd" d="m166.5.77-33.73 18.64L111.49.52z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__h)" fill-rule="evenodd" d="m0 158.09 14.13-25.77-11.43-30.7z" clip-rule="evenodd"/><path fill="#fff" fill-rule="evenodd" d="m1.94 100.65 11.5 32.62 49.97-11.211 57.05-53.02 16.1-51.139L111.209 0l-43.1 16.13C54.53 28.76 28.18 53.75 27.23 54.22c-.94.48-17.4 31.59-25.29 46.43" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__i)" fill-rule="evenodd" d="M42.32 42.05c29.43-29.18 67.37-46.42 81.93-31.73 14.551 14.69-.88 50.39-30.31 79.56s-66.9 47.36-81.45 32.67c-14.56-14.68.4-51.33 29.83-80.5" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__q)" fill-rule="evenodd" d="m41 197.38 22.86-75.72 75.92 24.39c-27.45 25.74-57.98 47.5-98.78 51.33" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__r)" fill-rule="evenodd" d="m120.56 68.89 19.49 77.2c22.93-24.11 43.51-50.03 53.589-82.09z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__s)" fill-rule="evenodd" d="M193.44 64.39c7.8-23.54 9.6-57.31-27.181-63.58l-30.18 16.67z" clip-rule="evenodd"/><path fill="#9e1209" fill-rule="evenodd" d="M0 157.75c1.08 38.851 29.11 39.43 41.05 39.771L13.47 133.11z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__t)" fill-rule="evenodd" d="M120.67 69.01c17.62 10.83 53.131 32.58 53.851 32.98 1.119.63 15.31-23.93 18.53-37.81z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__u)" fill-rule="evenodd" d="m63.83 121.66 30.56 58.96c18.07-9.8 32.22-21.74 45.18-34.53z" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__v)" fill-rule="evenodd" d="m13.35 133.19-4.33 51.56c8.17 11.16 19.41 12.13 31.2 11.26-8.53-21.23-25.57-63.68-26.87-62.82" clip-rule="evenodd"/><path fill="url(#gitea-rubygems__w)" fill-rule="evenodd" d="m135.9 17.61 60.71 8.52C193.37 12.4 183.42 3.54 166.46.77z" clip-rule="evenodd"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/gitea-swift.svg b/public/assets/img/svg/gitea-swift.svg
index 4182100185..891ac12b56 100644
--- a/public/assets/img/svg/gitea-swift.svg
+++ b/public/assets/img/svg/gitea-swift.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 59.5 59.5" class="svg gitea-swift" width="16" height="16" aria-hidden="true"><path fill="#F05138" d="M59.387 16.45a83 83 0 0 0-.027-1.792c-.034-1.301-.111-2.614-.343-3.9-.234-1.308-.618-2.523-1.222-3.71a12.46 12.46 0 0 0-5.452-5.452C51.156.992 49.94.609 48.635.374c-1.287-.232-2.6-.308-3.902-.343a86 86 0 0 0-1.792-.027Q41.876-.001 40.813 0H18.578q-1.064-.001-2.127.004c-.598.004-1.196.01-1.793.027q-.488.012-.978.036c-.978.047-1.959.133-2.924.307-.98.176-1.908.436-2.811.81A12.5 12.5 0 0 0 3.89 3.89a12.5 12.5 0 0 0-2.294 3.158C.992 8.235.61 9.45.374 10.758c-.231 1.286-.308 2.599-.343 3.9a86 86 0 0 0-.027 1.792Q-.002 17.515 0 18.578v22.234q-.001 1.064.004 2.129c.004.597.01 1.194.027 1.79.035 1.302.112 2.615.343 3.902.235 1.306.618 2.522 1.222 3.71a12.457 12.457 0 0 0 5.453 5.453c1.186.603 2.401.986 3.707 1.22 1.287.232 2.6.309 3.902.344q.896.023 1.793.026 1.063.006 2.127.004h22.235q1.065.002 2.128-.004.897-.003 1.792-.026c1.302-.035 2.615-.112 3.902-.344 1.306-.234 2.521-.617 3.708-1.221a12.46 12.46 0 0 0 5.452-5.453c.604-1.187.988-2.403 1.223-3.71.23-1.286.308-2.599.342-3.9.017-.597.023-1.194.027-1.791q.006-1.065.004-2.129V18.578q.001-1.065-.004-2.128"/><path fill="#fff" d="m47.061 36.661-.004-.005c.066-.223.133-.446.19-.675 2.466-9.82-3.55-21.432-13.731-27.545 4.461 6.048 6.434 13.373 4.681 19.78-.156.572-.344 1.12-.552 1.653-.225-.148-.51-.316-.89-.526 0 0-10.128-6.253-21.104-17.312-.288-.29 5.853 8.776 12.822 16.14-3.283-1.843-12.434-8.5-18.227-13.802.712 1.186 1.559 2.33 2.49 3.43 4.837 6.135 11.145 13.704 18.703 19.517-5.31 3.25-12.814 3.502-20.285.003a30.7 30.7 0 0 1-5.193-3.098c3.162 5.058 8.033 9.423 13.96 11.97 7.07 3.039 14.1 2.833 19.337.05l-.004.007.079-.047q.323-.172.637-.358c2.516-1.306 7.485-2.63 10.152 2.559.653 1.27 2.041-5.46-3.062-11.739z"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 59.5 59.5" class="svg gitea-swift" width="16" height="16" aria-hidden="true"><path fill="#f05138" d="M59.387 16.45a83 83 0 0 0-.027-1.792c-.034-1.301-.111-2.614-.343-3.9-.234-1.308-.618-2.523-1.222-3.71a12.46 12.46 0 0 0-5.452-5.452C51.156.992 49.94.609 48.635.374c-1.287-.232-2.6-.308-3.902-.343a86 86 0 0 0-1.792-.027Q41.876-.001 40.813 0H18.578q-1.064-.001-2.127.004c-.598.004-1.196.01-1.793.027q-.488.012-.978.036c-.978.047-1.959.133-2.924.307-.98.176-1.908.436-2.811.81A12.5 12.5 0 0 0 3.89 3.89a12.5 12.5 0 0 0-2.294 3.158C.992 8.235.61 9.45.374 10.758c-.231 1.286-.308 2.599-.343 3.9a86 86 0 0 0-.027 1.792Q-.002 17.515 0 18.578v22.234q-.001 1.064.004 2.129c.004.597.01 1.194.027 1.79.035 1.302.112 2.615.343 3.902.235 1.306.618 2.522 1.222 3.71a12.457 12.457 0 0 0 5.453 5.453c1.186.603 2.401.986 3.707 1.22 1.287.232 2.6.309 3.902.344q.896.023 1.793.026 1.063.006 2.127.004h22.235q1.065.002 2.128-.004.897-.003 1.792-.026c1.302-.035 2.615-.112 3.902-.344 1.306-.234 2.521-.617 3.708-1.221a12.46 12.46 0 0 0 5.452-5.453c.604-1.187.988-2.403 1.223-3.71.23-1.286.308-2.599.342-3.9.017-.597.023-1.194.027-1.791q.006-1.065.004-2.129V18.578q.001-1.065-.004-2.128"/><path fill="#fff" d="m47.061 36.661-.004-.005c.066-.223.133-.446.19-.675 2.466-9.82-3.55-21.432-13.731-27.545 4.461 6.048 6.434 13.373 4.681 19.78-.156.572-.344 1.12-.552 1.653-.225-.148-.51-.316-.89-.526 0 0-10.128-6.253-21.104-17.312-.288-.29 5.853 8.776 12.822 16.14-3.283-1.843-12.434-8.5-18.227-13.802.712 1.186 1.559 2.33 2.49 3.43 4.837 6.135 11.145 13.704 18.703 19.517-5.31 3.25-12.814 3.502-20.285.003a30.7 30.7 0 0 1-5.193-3.098c3.162 5.058 8.033 9.423 13.96 11.97 7.07 3.039 14.1 2.833 19.337.05l-.004.007.079-.047q.323-.172.637-.358c2.516-1.306 7.485-2.63 10.152 2.559.653 1.27 2.041-5.46-3.062-11.739z"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/gitea-vagrant.svg b/public/assets/img/svg/gitea-vagrant.svg
index ba50101d52..18b05e900d 100644
--- a/public/assets/img/svg/gitea-vagrant.svg
+++ b/public/assets/img/svg/gitea-vagrant.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid" viewBox="0 0 255 263" class="svg gitea-vagrant" width="16" height="16" aria-hidden="true"><path fill="#1159CC" d="M254.22 20.234 196.03 53.47l-1.64 20.618-44.19 99.772-26.27 17.34 3.18 71.6 49.53-28.55 77.58-189.946zM92.45 56.933V34.051l-.238-.136-38.483 19.102 1.642 23.034L103.4 180.6l26.02-14.71-2.31-28.09z"/><path fill="#127EFF" d="m219.56 0-57.75 33.814h-.04v23.119L127.11 137.8v27.02l-23.12 13.41L57.788 74.146V53.81L92.45 33.848 34.668 0 .006 20.234v24.783L78.022 234.49l49.088 28.31v-71.16l23.09-13.41-.27-.17 46.51-103.914V53.81l57.78-33.576z"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid" viewBox="0 0 255 263" class="svg gitea-vagrant" width="16" height="16" aria-hidden="true"><path fill="#1159cc" d="M254.22 20.234 196.03 53.47l-1.64 20.618-44.19 99.772-26.27 17.34 3.18 71.6 49.53-28.55 77.58-189.946zM92.45 56.933V34.051l-.238-.136-38.483 19.102 1.642 23.034L103.4 180.6l26.02-14.71-2.31-28.09z"/><path fill="#127eff" d="m219.56 0-57.75 33.814h-.04v23.119L127.11 137.8v27.02l-23.12 13.41L57.788 74.146V53.81L92.45 33.848 34.668 0 .006 20.234v24.783L78.022 234.49l49.088 28.31v-71.16l23.09-13.41-.27-.17 46.51-103.914V53.81l57.78-33.576z"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/material-folder-generic.svg b/public/assets/img/svg/material-folder-generic.svg
new file mode 100644
index 0000000000..c8dc753b43
--- /dev/null
+++ b/public/assets/img/svg/material-folder-generic.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="svg material-folder-generic" width="16" height="16" aria-hidden="true"><path fill="#42a5f5" d="M10 4H4c-1.11 0-2 .89-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-8z"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/material-folder-symlink.svg b/public/assets/img/svg/material-folder-symlink.svg
new file mode 100644
index 0000000000..49a80b4255
--- /dev/null
+++ b/public/assets/img/svg/material-folder-symlink.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="svg material-folder-symlink" width="16" height="16" aria-hidden="true"><path fill="#42a5f5" d="M10 4H4c-1.11 0-2 .89-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-8z" opacity=".745"/><path fill="#c5e5fd" d="M16.972 10.757v2.641h-6.561v5.281h6.561v2.641l6.562-5.281z" opacity=".81"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/octicon-pause.svg b/public/assets/img/svg/octicon-pause.svg
new file mode 100644
index 0000000000..008de5afdf
--- /dev/null
+++ b/public/assets/img/svg/octicon-pause.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-pause" width="16" height="16" aria-hidden="true"><path d="M5 2h1a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1m5 0h1a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/octicon-sparkle.svg b/public/assets/img/svg/octicon-sparkle.svg
new file mode 100644
index 0000000000..769350bbbe
--- /dev/null
+++ b/public/assets/img/svg/octicon-sparkle.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-sparkle" width="16" height="16" aria-hidden="true"><path d="M7.198.57c.275-.752 1.34-.752 1.615 0l.849 2.317a5.82 5.82 0 0 0 3.462 3.463l2.317.848c.753.275.753 1.34 0 1.615l-2.317.849a5.82 5.82 0 0 0-3.462 3.462l-.849 2.317c-.275.753-1.34.753-1.615 0l-.848-2.317a5.82 5.82 0 0 0-3.463-3.462L.57 8.813c-.752-.275-.752-1.34 0-1.615l2.317-.848A5.82 5.82 0 0 0 6.35 2.887zm.562 2.833A7.32 7.32 0 0 1 3.403 7.76l-.673.246.673.246a7.32 7.32 0 0 1 4.357 4.356l.246.673.246-.673a7.32 7.32 0 0 1 4.356-4.356l.673-.246-.673-.246a7.32 7.32 0 0 1-4.356-4.357l-.246-.673z"/></svg> \ No newline at end of file
diff --git a/public/assets/img/svg/octicon-square-circle.svg b/public/assets/img/svg/octicon-square-circle.svg
new file mode 100644
index 0000000000..7ebb020bad
--- /dev/null
+++ b/public/assets/img/svg/octicon-square-circle.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg octicon-square-circle" width="16" height="16" aria-hidden="true"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16m0-1.5a6.5 6.5 0 1 0 0-13 6.5 6.5 0 0 0 0 13"/><path d="M5 5.75A.75.75 0 0 1 5.75 5h4.5a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-.75.75h-4.5a.75.75 0 0 1-.75-.75Z"/></svg> \ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index 4d36b30ea7..439b9a6fbd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,8 +5,8 @@ package-mode = false
python = "^3.10"
[tool.poetry.group.dev.dependencies]
-djlint = "1.36.3"
-yamllint = "1.35.1"
+djlint = "1.36.4"
+yamllint = "1.37.1"
[tool.djlint]
profile="golang"
diff --git a/routers/api/actions/artifacts.go b/routers/api/actions/artifacts.go
index 910edd6d58..6473659e5c 100644
--- a/routers/api/actions/artifacts.go
+++ b/routers/api/actions/artifacts.go
@@ -135,7 +135,7 @@ func ArtifactContexter() func(next http.Handler) http.Handler {
// we should verify the ACTIONS_RUNTIME_TOKEN
authHeader := req.Header.Get("Authorization")
if len(authHeader) == 0 || !strings.HasPrefix(authHeader, "Bearer ") {
- ctx.Error(http.StatusUnauthorized, "Bad authorization header")
+ ctx.HTTPError(http.StatusUnauthorized, "Bad authorization header")
return
}
@@ -147,12 +147,12 @@ func ArtifactContexter() func(next http.Handler) http.Handler {
task, err = actions.GetTaskByID(req.Context(), tID)
if err != nil {
log.Error("Error runner api getting task by ID: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error runner api getting task by ID")
+ ctx.HTTPError(http.StatusInternalServerError, "Error runner api getting task by ID")
return
}
if task.Status != actions.StatusRunning {
log.Error("Error runner api getting task: task is not running")
- ctx.Error(http.StatusInternalServerError, "Error runner api getting task: task is not running")
+ ctx.HTTPError(http.StatusInternalServerError, "Error runner api getting task: task is not running")
return
}
} else {
@@ -162,14 +162,14 @@ func ArtifactContexter() func(next http.Handler) http.Handler {
task, err = actions.GetRunningTaskByToken(req.Context(), authToken)
if err != nil {
log.Error("Error runner api getting task: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error runner api getting task")
+ ctx.HTTPError(http.StatusInternalServerError, "Error runner api getting task")
return
}
}
if err := task.LoadJob(req.Context()); err != nil {
log.Error("Error runner api getting job: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error runner api getting job")
+ ctx.HTTPError(http.StatusInternalServerError, "Error runner api getting job")
return
}
@@ -211,7 +211,7 @@ func (ar artifactRoutes) getUploadArtifactURL(ctx *ArtifactContext) {
var req getUploadArtifactRequest
if err := json.NewDecoder(ctx.Req.Body).Decode(&req); err != nil {
log.Error("Error decode request body: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error decode request body")
+ ctx.HTTPError(http.StatusInternalServerError, "Error decode request body")
return
}
@@ -250,7 +250,7 @@ func (ar artifactRoutes) uploadArtifact(ctx *ArtifactContext) {
expiredDays, err = strconv.ParseInt(queryRetentionDays, 10, 64)
if err != nil {
log.Error("Error parse retention days: %v", err)
- ctx.Error(http.StatusBadRequest, "Error parse retention days")
+ ctx.HTTPError(http.StatusBadRequest, "Error parse retention days")
return
}
}
@@ -261,7 +261,7 @@ func (ar artifactRoutes) uploadArtifact(ctx *ArtifactContext) {
artifact, err := actions.CreateArtifact(ctx, task, artifactName, artifactPath, expiredDays)
if err != nil {
log.Error("Error create or get artifact: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error create or get artifact")
+ ctx.HTTPError(http.StatusInternalServerError, "Error create or get artifact")
return
}
@@ -271,7 +271,7 @@ func (ar artifactRoutes) uploadArtifact(ctx *ArtifactContext) {
chunksTotalSize, err := saveUploadChunk(ar.fs, ctx, artifact, contentLength, runID)
if err != nil {
log.Error("Error save upload chunk: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error save upload chunk")
+ ctx.HTTPError(http.StatusInternalServerError, "Error save upload chunk")
return
}
@@ -285,7 +285,7 @@ func (ar artifactRoutes) uploadArtifact(ctx *ArtifactContext) {
artifact.ContentEncoding = ctx.Req.Header.Get("Content-Encoding")
if err := actions.UpdateArtifactByID(ctx, artifact.ID, artifact); err != nil {
log.Error("Error update artifact: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error update artifact")
+ ctx.HTTPError(http.StatusInternalServerError, "Error update artifact")
return
}
log.Debug("[artifact] update artifact size, artifact_id: %d, size: %d, compressed size: %d",
@@ -307,12 +307,12 @@ func (ar artifactRoutes) comfirmUploadArtifact(ctx *ArtifactContext) {
artifactName := ctx.Req.URL.Query().Get("artifactName")
if artifactName == "" {
log.Error("Error artifact name is empty")
- ctx.Error(http.StatusBadRequest, "Error artifact name is empty")
+ ctx.HTTPError(http.StatusBadRequest, "Error artifact name is empty")
return
}
if err := mergeChunksForRun(ctx, ar.fs, runID, artifactName); err != nil {
log.Error("Error merge chunks: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error merge chunks")
+ ctx.HTTPError(http.StatusInternalServerError, "Error merge chunks")
return
}
ctx.JSON(http.StatusOK, map[string]string{
@@ -337,15 +337,18 @@ func (ar artifactRoutes) listArtifacts(ctx *ArtifactContext) {
return
}
- artifacts, err := db.Find[actions.ActionArtifact](ctx, actions.FindArtifactsOptions{RunID: runID})
+ artifacts, err := db.Find[actions.ActionArtifact](ctx, actions.FindArtifactsOptions{
+ RunID: runID,
+ Status: int(actions.ArtifactStatusUploadConfirmed),
+ })
if err != nil {
log.Error("Error getting artifacts: %v", err)
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, err.Error())
return
}
if len(artifacts) == 0 {
log.Debug("[artifact] handleListArtifacts, no artifacts")
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
@@ -402,21 +405,22 @@ func (ar artifactRoutes) getDownloadArtifactURL(ctx *ArtifactContext) {
artifacts, err := db.Find[actions.ActionArtifact](ctx, actions.FindArtifactsOptions{
RunID: runID,
ArtifactName: itemPath,
+ Status: int(actions.ArtifactStatusUploadConfirmed),
})
if err != nil {
log.Error("Error getting artifacts: %v", err)
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, err.Error())
return
}
if len(artifacts) == 0 {
log.Debug("[artifact] getDownloadArtifactURL, no artifacts")
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
if itemPath != artifacts[0].ArtifactName {
log.Error("Error dismatch artifact name, itemPath: %v, artifact: %v", itemPath, artifacts[0].ArtifactName)
- ctx.Error(http.StatusBadRequest, "Error dismatch artifact name")
+ ctx.HTTPError(http.StatusBadRequest, "Error dismatch artifact name")
return
}
@@ -460,24 +464,29 @@ func (ar artifactRoutes) downloadArtifact(ctx *ArtifactContext) {
artifact, exist, err := db.GetByID[actions.ActionArtifact](ctx, artifactID)
if err != nil {
log.Error("Error getting artifact: %v", err)
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, err.Error())
return
}
if !exist {
log.Error("artifact with ID %d does not exist", artifactID)
- ctx.Error(http.StatusNotFound, fmt.Sprintf("artifact with ID %d does not exist", artifactID))
+ ctx.HTTPError(http.StatusNotFound, fmt.Sprintf("artifact with ID %d does not exist", artifactID))
return
}
if artifact.RunID != runID {
log.Error("Error mismatch runID and artifactID, task: %v, artifact: %v", runID, artifactID)
- ctx.Error(http.StatusBadRequest)
+ ctx.HTTPError(http.StatusBadRequest)
+ return
+ }
+ if artifact.Status != actions.ArtifactStatusUploadConfirmed {
+ log.Error("Error artifact not found: %s", artifact.Status.ToString())
+ ctx.HTTPError(http.StatusNotFound, "Error artifact not found")
return
}
fd, err := ar.fs.Open(artifact.StoragePath)
if err != nil {
log.Error("Error opening file: %v", err)
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, err.Error())
return
}
defer fd.Close()
diff --git a/routers/api/actions/artifacts_chunks.go b/routers/api/actions/artifacts_chunks.go
index cf48da12aa..708931d1ac 100644
--- a/routers/api/actions/artifacts_chunks.go
+++ b/routers/api/actions/artifacts_chunks.go
@@ -51,7 +51,7 @@ func saveUploadChunkBase(st storage.ObjectStorage, ctx *ArtifactContext,
log.Info("[artifact] check chunk md5, sum: %s, header: %s", chunkMd5String, reqMd5String)
// if md5 not match, delete the chunk
if reqMd5String != chunkMd5String {
- checkErr = fmt.Errorf("md5 not match")
+ checkErr = errors.New("md5 not match")
}
}
if writtenSize != contentSize {
@@ -261,7 +261,7 @@ func mergeChunksForArtifact(ctx *ArtifactContext, chunks []*chunkFileItem, st st
return fmt.Errorf("save merged file error: %v", err)
}
if written != artifact.FileCompressedSize {
- return fmt.Errorf("merged file size is not equal to chunk length")
+ return errors.New("merged file size is not equal to chunk length")
}
defer func() {
@@ -292,7 +292,7 @@ func mergeChunksForArtifact(ctx *ArtifactContext, chunks []*chunkFileItem, st st
}
artifact.StoragePath = storagePath
- artifact.Status = int64(actions.ArtifactStatusUploadConfirmed)
+ artifact.Status = actions.ArtifactStatusUploadConfirmed
if err := actions.UpdateArtifactByID(ctx, artifact.ID, artifact); err != nil {
return fmt.Errorf("update artifact error: %v", err)
}
diff --git a/routers/api/actions/artifacts_utils.go b/routers/api/actions/artifacts_utils.go
index a4ca5797dc..35868c290e 100644
--- a/routers/api/actions/artifacts_utils.go
+++ b/routers/api/actions/artifacts_utils.go
@@ -26,7 +26,7 @@ var invalidArtifactNameChars = strings.Join([]string{"\\", "/", "\"", ":", "<",
func validateArtifactName(ctx *ArtifactContext, artifactName string) bool {
if strings.ContainsAny(artifactName, invalidArtifactNameChars) {
log.Error("Error checking artifact name contains invalid character")
- ctx.Error(http.StatusBadRequest, "Error checking artifact name contains invalid character")
+ ctx.HTTPError(http.StatusBadRequest, "Error checking artifact name contains invalid character")
return false
}
return true
@@ -37,18 +37,18 @@ func validateRunID(ctx *ArtifactContext) (*actions.ActionTask, int64, bool) {
runID := ctx.PathParamInt64("run_id")
if task.Job.RunID != runID {
log.Error("Error runID not match")
- ctx.Error(http.StatusBadRequest, "run-id does not match")
+ ctx.HTTPError(http.StatusBadRequest, "run-id does not match")
return nil, 0, false
}
return task, runID, true
}
-func validateRunIDV4(ctx *ArtifactContext, rawRunID string) (*actions.ActionTask, int64, bool) { //nolint:unparam
+func validateRunIDV4(ctx *ArtifactContext, rawRunID string) (*actions.ActionTask, int64, bool) { //nolint:unparam // ActionTask is never used
task := ctx.ActionTask
runID, err := strconv.ParseInt(rawRunID, 10, 64)
if err != nil || task.Job.RunID != runID {
log.Error("Error runID not match")
- ctx.Error(http.StatusBadRequest, "run-id does not match")
+ ctx.HTTPError(http.StatusBadRequest, "run-id does not match")
return nil, 0, false
}
return task, runID, true
@@ -62,7 +62,7 @@ func validateArtifactHash(ctx *ArtifactContext, artifactName string) bool {
return true
}
log.Error("Invalid artifact hash: %s", paramHash)
- ctx.Error(http.StatusBadRequest, "Invalid artifact hash")
+ ctx.HTTPError(http.StatusBadRequest, "Invalid artifact hash")
return false
}
diff --git a/routers/api/actions/artifactsv4.go b/routers/api/actions/artifactsv4.go
index 8917a7a8a2..e9e9fc6393 100644
--- a/routers/api/actions/artifactsv4.go
+++ b/routers/api/actions/artifactsv4.go
@@ -25,7 +25,7 @@ package actions
// 1.3. Continue Upload Zip Content to Blobstorage (unauthenticated request), repeat until everything is uploaded
// PUT: http://localhost:3000/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact?sig=mO7y35r4GyjN7fwg0DTv3-Fv1NDXD84KLEgLpoPOtDI=&expires=2024-01-23+21%3A48%3A37.20833956+%2B0100+CET&artifactName=test&taskID=75&comp=appendBlock
// 1.4. BlockList xml payload to Blobstorage (unauthenticated request)
-// Files of about 800MB are parallel in parallel and / or out of order, this file is needed to enshure the correct order
+// Files of about 800MB are parallel in parallel and / or out of order, this file is needed to ensure the correct order
// PUT: http://localhost:3000/twirp/github.actions.results.api.v1.ArtifactService/UploadArtifact?sig=mO7y35r4GyjN7fwg0DTv3-Fv1NDXD84KLEgLpoPOtDI=&expires=2024-01-23+21%3A48%3A37.20833956+%2B0100+CET&artifactName=test&taskID=75&comp=blockList
// Request
// <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
@@ -162,15 +162,15 @@ func (r artifactV4Routes) buildSignature(endp, expires, artifactName string, tas
mac.Write([]byte(endp))
mac.Write([]byte(expires))
mac.Write([]byte(artifactName))
- mac.Write([]byte(fmt.Sprint(taskID)))
- mac.Write([]byte(fmt.Sprint(artifactID)))
+ fmt.Fprint(mac, taskID)
+ fmt.Fprint(mac, artifactID)
return mac.Sum(nil)
}
func (r artifactV4Routes) buildArtifactURL(ctx *ArtifactContext, endp, artifactName string, taskID, artifactID int64) string {
expires := time.Now().Add(60 * time.Minute).Format("2006-01-02 15:04:05.999999999 -0700 MST")
uploadURL := strings.TrimSuffix(httplib.GuessCurrentAppURL(ctx), "/") + strings.TrimSuffix(r.prefix, "/") +
- "/" + endp + "?sig=" + base64.URLEncoding.EncodeToString(r.buildSignature(endp, expires, artifactName, taskID, artifactID)) + "&expires=" + url.QueryEscape(expires) + "&artifactName=" + url.QueryEscape(artifactName) + "&taskID=" + fmt.Sprint(taskID) + "&artifactID=" + fmt.Sprint(artifactID)
+ "/" + endp + "?sig=" + base64.URLEncoding.EncodeToString(r.buildSignature(endp, expires, artifactName, taskID, artifactID)) + "&expires=" + url.QueryEscape(expires) + "&artifactName=" + url.QueryEscape(artifactName) + "&taskID=" + strconv.FormatInt(taskID, 10) + "&artifactID=" + strconv.FormatInt(artifactID, 10)
return uploadURL
}
@@ -187,29 +187,29 @@ func (r artifactV4Routes) verifySignature(ctx *ArtifactContext, endp string) (*a
expecedsig := r.buildSignature(endp, expires, artifactName, taskID, artifactID)
if !hmac.Equal(dsig, expecedsig) {
log.Error("Error unauthorized")
- ctx.Error(http.StatusUnauthorized, "Error unauthorized")
+ ctx.HTTPError(http.StatusUnauthorized, "Error unauthorized")
return nil, "", false
}
t, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", expires)
if err != nil || t.Before(time.Now()) {
log.Error("Error link expired")
- ctx.Error(http.StatusUnauthorized, "Error link expired")
+ ctx.HTTPError(http.StatusUnauthorized, "Error link expired")
return nil, "", false
}
task, err := actions.GetTaskByID(ctx, taskID)
if err != nil {
log.Error("Error runner api getting task by ID: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error runner api getting task by ID")
+ ctx.HTTPError(http.StatusInternalServerError, "Error runner api getting task by ID")
return nil, "", false
}
if task.Status != actions.StatusRunning {
log.Error("Error runner api getting task: task is not running")
- ctx.Error(http.StatusInternalServerError, "Error runner api getting task: task is not running")
+ ctx.HTTPError(http.StatusInternalServerError, "Error runner api getting task: task is not running")
return nil, "", false
}
if err := task.LoadJob(ctx); err != nil {
log.Error("Error runner api getting job: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error runner api getting job")
+ ctx.HTTPError(http.StatusInternalServerError, "Error runner api getting job")
return nil, "", false
}
return task, artifactName, true
@@ -230,13 +230,13 @@ func (r *artifactV4Routes) parseProtbufBody(ctx *ArtifactContext, req protorefle
body, err := io.ReadAll(ctx.Req.Body)
if err != nil {
log.Error("Error decode request body: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error decode request body")
+ ctx.HTTPError(http.StatusInternalServerError, "Error decode request body")
return false
}
err = protojson.Unmarshal(body, req)
if err != nil {
log.Error("Error decode request body: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error decode request body")
+ ctx.HTTPError(http.StatusInternalServerError, "Error decode request body")
return false
}
return true
@@ -246,7 +246,7 @@ func (r *artifactV4Routes) sendProtbufBody(ctx *ArtifactContext, req protoreflec
resp, err := protojson.Marshal(req)
if err != nil {
log.Error("Error encode response body: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error encode response body")
+ ctx.HTTPError(http.StatusInternalServerError, "Error encode response body")
return
}
ctx.Resp.Header().Set("Content-Type", "application/json;charset=utf-8")
@@ -275,7 +275,7 @@ func (r *artifactV4Routes) createArtifact(ctx *ArtifactContext) {
artifact, err := actions.CreateArtifact(ctx, ctx.ActionTask, artifactName, artifactName+".zip", rententionDays)
if err != nil {
log.Error("Error create or get artifact: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error create or get artifact")
+ ctx.HTTPError(http.StatusInternalServerError, "Error create or get artifact")
return
}
artifact.ContentEncoding = ArtifactV4ContentEncoding
@@ -283,7 +283,7 @@ func (r *artifactV4Routes) createArtifact(ctx *ArtifactContext) {
artifact.FileCompressedSize = 0
if err := actions.UpdateArtifactByID(ctx, artifact.ID, artifact); err != nil {
log.Error("Error UpdateArtifactByID: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error UpdateArtifactByID")
+ ctx.HTTPError(http.StatusInternalServerError, "Error UpdateArtifactByID")
return
}
@@ -309,28 +309,28 @@ func (r *artifactV4Routes) uploadArtifact(ctx *ArtifactContext) {
artifact, err := r.getArtifactByName(ctx, task.Job.RunID, artifactName)
if err != nil {
log.Error("Error artifact not found: %v", err)
- ctx.Error(http.StatusNotFound, "Error artifact not found")
+ ctx.HTTPError(http.StatusNotFound, "Error artifact not found")
return
}
_, err = appendUploadChunk(r.fs, ctx, artifact, artifact.FileSize, ctx.Req.ContentLength, artifact.RunID)
if err != nil {
log.Error("Error runner api getting task: task is not running")
- ctx.Error(http.StatusInternalServerError, "Error runner api getting task: task is not running")
+ ctx.HTTPError(http.StatusInternalServerError, "Error runner api getting task: task is not running")
return
}
artifact.FileCompressedSize += ctx.Req.ContentLength
artifact.FileSize += ctx.Req.ContentLength
if err := actions.UpdateArtifactByID(ctx, artifact.ID, artifact); err != nil {
log.Error("Error UpdateArtifactByID: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error UpdateArtifactByID")
+ ctx.HTTPError(http.StatusInternalServerError, "Error UpdateArtifactByID")
return
}
} else {
_, err := r.fs.Save(fmt.Sprintf("tmpv4%d/block-%d-%d-%s", task.Job.RunID, task.Job.RunID, ctx.Req.ContentLength, base64.URLEncoding.EncodeToString([]byte(blockid))), ctx.Req.Body, -1)
if err != nil {
log.Error("Error runner api getting task: task is not running")
- ctx.Error(http.StatusInternalServerError, "Error runner api getting task: task is not running")
+ ctx.HTTPError(http.StatusInternalServerError, "Error runner api getting task: task is not running")
return
}
}
@@ -341,7 +341,7 @@ func (r *artifactV4Routes) uploadArtifact(ctx *ArtifactContext) {
_, err := r.fs.Save(fmt.Sprintf("tmpv4%d/%d-%d-blocklist", task.Job.RunID, task.Job.RunID, artifactID), ctx.Req.Body, -1)
if err != nil {
log.Error("Error runner api getting task: task is not running")
- ctx.Error(http.StatusInternalServerError, "Error runner api getting task: task is not running")
+ ctx.HTTPError(http.StatusInternalServerError, "Error runner api getting task: task is not running")
return
}
ctx.JSON(http.StatusCreated, "created")
@@ -389,7 +389,7 @@ func (r *artifactV4Routes) finalizeArtifact(ctx *ArtifactContext) {
artifact, err := r.getArtifactByName(ctx, runID, req.Name)
if err != nil {
log.Error("Error artifact not found: %v", err)
- ctx.Error(http.StatusNotFound, "Error artifact not found")
+ ctx.HTTPError(http.StatusNotFound, "Error artifact not found")
return
}
@@ -400,20 +400,20 @@ func (r *artifactV4Routes) finalizeArtifact(ctx *ArtifactContext) {
chunkMap, err := listChunksByRunID(r.fs, runID)
if err != nil {
log.Error("Error merge chunks: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error merge chunks")
+ ctx.HTTPError(http.StatusInternalServerError, "Error merge chunks")
return
}
chunks, ok = chunkMap[artifact.ID]
if !ok {
log.Error("Error merge chunks")
- ctx.Error(http.StatusInternalServerError, "Error merge chunks")
+ ctx.HTTPError(http.StatusInternalServerError, "Error merge chunks")
return
}
} else {
chunks, err = listChunksByRunIDV4(r.fs, runID, artifact.ID, blockList)
if err != nil {
log.Error("Error merge chunks: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error merge chunks")
+ ctx.HTTPError(http.StatusInternalServerError, "Error merge chunks")
return
}
artifact.FileSize = chunks[len(chunks)-1].End + 1
@@ -426,7 +426,7 @@ func (r *artifactV4Routes) finalizeArtifact(ctx *ArtifactContext) {
}
if err := mergeChunksForArtifact(ctx, chunks, r.fs, artifact, checksum); err != nil {
log.Error("Error merge chunks: %v", err)
- ctx.Error(http.StatusInternalServerError, "Error merge chunks")
+ ctx.HTTPError(http.StatusInternalServerError, "Error merge chunks")
return
}
@@ -448,15 +448,13 @@ func (r *artifactV4Routes) listArtifacts(ctx *ArtifactContext) {
return
}
- artifacts, err := db.Find[actions.ActionArtifact](ctx, actions.FindArtifactsOptions{RunID: runID})
+ artifacts, err := db.Find[actions.ActionArtifact](ctx, actions.FindArtifactsOptions{
+ RunID: runID,
+ Status: int(actions.ArtifactStatusUploadConfirmed),
+ })
if err != nil {
log.Error("Error getting artifacts: %v", err)
- ctx.Error(http.StatusInternalServerError, err.Error())
- return
- }
- if len(artifacts) == 0 {
- log.Debug("[artifact] handleListArtifacts, no artifacts")
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusInternalServerError, err.Error())
return
}
@@ -507,7 +505,12 @@ func (r *artifactV4Routes) getSignedArtifactURL(ctx *ArtifactContext) {
artifact, err := r.getArtifactByName(ctx, runID, artifactName)
if err != nil {
log.Error("Error artifact not found: %v", err)
- ctx.Error(http.StatusNotFound, "Error artifact not found")
+ ctx.HTTPError(http.StatusNotFound, "Error artifact not found")
+ return
+ }
+ if artifact.Status != actions.ArtifactStatusUploadConfirmed {
+ log.Error("Error artifact not found: %s", artifact.Status.ToString())
+ ctx.HTTPError(http.StatusNotFound, "Error artifact not found")
return
}
@@ -535,7 +538,12 @@ func (r *artifactV4Routes) downloadArtifact(ctx *ArtifactContext) {
artifact, err := r.getArtifactByName(ctx, task.Job.RunID, artifactName)
if err != nil {
log.Error("Error artifact not found: %v", err)
- ctx.Error(http.StatusNotFound, "Error artifact not found")
+ ctx.HTTPError(http.StatusNotFound, "Error artifact not found")
+ return
+ }
+ if artifact.Status != actions.ArtifactStatusUploadConfirmed {
+ log.Error("Error artifact not found: %s", artifact.Status.ToString())
+ ctx.HTTPError(http.StatusNotFound, "Error artifact not found")
return
}
@@ -559,14 +567,14 @@ func (r *artifactV4Routes) deleteArtifact(ctx *ArtifactContext) {
artifact, err := r.getArtifactByName(ctx, runID, req.Name)
if err != nil {
log.Error("Error artifact not found: %v", err)
- ctx.Error(http.StatusNotFound, "Error artifact not found")
+ ctx.HTTPError(http.StatusNotFound, "Error artifact not found")
return
}
err = actions.SetArtifactNeedDelete(ctx, runID, req.Name)
if err != nil {
log.Error("Error deleting artifacts: %v", err)
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, err.Error())
return
}
diff --git a/routers/api/actions/ping/ping_test.go b/routers/api/actions/ping/ping_test.go
index 098b003ea2..98d2dcb820 100644
--- a/routers/api/actions/ping/ping_test.go
+++ b/routers/api/actions/ping/ping_test.go
@@ -4,7 +4,6 @@
package ping
import (
- "context"
"net/http"
"net/http/httptest"
"testing"
@@ -51,7 +50,7 @@ func MainServiceTest(t *testing.T, h http.Handler) {
clients := []pingv1connect.PingServiceClient{connectClient, grpcClient, grpcWebClient}
t.Run("ping request", func(t *testing.T) {
for _, client := range clients {
- result, err := client.Ping(context.Background(), connect.NewRequest(&pingv1.PingRequest{
+ result, err := client.Ping(t.Context(), connect.NewRequest(&pingv1.PingRequest{
Data: "foobar",
}))
require.NoError(t, err)
diff --git a/routers/api/actions/runner/main_test.go b/routers/api/actions/runner/main_test.go
deleted file mode 100644
index 1e80a4f5ca..0000000000
--- a/routers/api/actions/runner/main_test.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2024 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package runner
-
-import (
- "testing"
-
- "code.gitea.io/gitea/models/unittest"
-)
-
-func TestMain(m *testing.M) {
- unittest.MainTest(m)
-}
diff --git a/routers/api/actions/runner/runner.go b/routers/api/actions/runner/runner.go
index c55b30f7eb..ce8137592d 100644
--- a/routers/api/actions/runner/runner.go
+++ b/routers/api/actions/runner/runner.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
actions_service "code.gitea.io/gitea/services/actions"
+ notify_service "code.gitea.io/gitea/services/notify"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
"code.gitea.io/actions-proto-go/runner/v1/runnerv1connect"
@@ -77,6 +78,7 @@ func (s *Service) Register(
RepoID: runnerToken.RepoID,
Version: req.Msg.Version,
AgentLabels: labels,
+ Ephemeral: req.Msg.Ephemeral,
}
if err := runner.GenerateToken(); err != nil {
return nil, errors.New("can't generate token")
@@ -95,12 +97,13 @@ func (s *Service) Register(
res := connect.NewResponse(&runnerv1.RegisterResponse{
Runner: &runnerv1.Runner{
- Id: runner.ID,
- Uuid: runner.UUID,
- Token: runner.Token,
- Name: runner.Name,
- Version: runner.Version,
- Labels: runner.AgentLabels,
+ Id: runner.ID,
+ Uuid: runner.UUID,
+ Token: runner.Token,
+ Name: runner.Name,
+ Version: runner.Version,
+ Labels: runner.AgentLabels,
+ Ephemeral: runner.Ephemeral,
},
})
@@ -156,7 +159,7 @@ func (s *Service) FetchTask(
// if the task version in request is not equal to the version in db,
// it means there may still be some tasks not be assgined.
// try to pick a task for the runner that send the request.
- if t, ok, err := pickTask(ctx, runner); err != nil {
+ if t, ok, err := actions_service.PickTask(ctx, runner); err != nil {
log.Error("pick task failed: %v", err)
return nil, status.Errorf(codes.Internal, "pick task: %v", err)
} else if ok {
@@ -210,7 +213,7 @@ func (s *Service) UpdateTask(
if err := task.LoadJob(ctx); err != nil {
return nil, status.Errorf(codes.Internal, "load job: %v", err)
}
- if err := task.Job.LoadRun(ctx); err != nil {
+ if err := task.Job.LoadAttributes(ctx); err != nil {
return nil, status.Errorf(codes.Internal, "load run: %v", err)
}
@@ -219,6 +222,10 @@ func (s *Service) UpdateTask(
actions_service.CreateCommitStatus(ctx, task.Job)
}
+ if task.Status.IsDone() {
+ notify_service.WorkflowJobStatusUpdate(ctx, task.Job.Run.Repo, task.Job.Run.TriggerUser, task.Job, task)
+ }
+
if req.Msg.State.Result != runnerv1.Result_RESULT_UNSPECIFIED {
if err := actions_service.EmitJobsIfReady(task.Job.RunID); err != nil {
log.Error("Emit ready jobs of run %d: %v", task.Job.RunID, err)
diff --git a/routers/api/actions/runner/utils.go b/routers/api/actions/runner/utils.go
deleted file mode 100644
index 539be8d889..0000000000
--- a/routers/api/actions/runner/utils.go
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package runner
-
-import (
- "context"
- "fmt"
-
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- secret_model "code.gitea.io/gitea/models/secret"
- actions_module "code.gitea.io/gitea/modules/actions"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/services/actions"
-
- runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
- "google.golang.org/protobuf/types/known/structpb"
-)
-
-func pickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv1.Task, bool, error) {
- t, ok, err := actions_model.CreateTaskForRunner(ctx, runner)
- if err != nil {
- return nil, false, fmt.Errorf("CreateTaskForRunner: %w", err)
- }
- if !ok {
- return nil, false, nil
- }
-
- secrets, err := secret_model.GetSecretsOfTask(ctx, t)
- if err != nil {
- return nil, false, fmt.Errorf("GetSecretsOfTask: %w", err)
- }
-
- vars, err := actions_model.GetVariablesOfRun(ctx, t.Job.Run)
- if err != nil {
- return nil, false, fmt.Errorf("GetVariablesOfRun: %w", err)
- }
-
- actions.CreateCommitStatus(ctx, t.Job)
-
- task := &runnerv1.Task{
- Id: t.ID,
- WorkflowPayload: t.Job.WorkflowPayload,
- Context: generateTaskContext(t),
- Secrets: secrets,
- Vars: vars,
- }
-
- if needs, err := findTaskNeeds(ctx, t); err != nil {
- log.Error("Cannot find needs for task %v: %v", t.ID, err)
- // Go on with empty needs.
- // If return error, the task will be wild, which means the runner will never get it when it has been assigned to the runner.
- // In contrast, missing needs is less serious.
- // And the task will fail and the runner will report the error in the logs.
- } else {
- task.Needs = needs
- }
-
- return task, true, nil
-}
-
-func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct {
- event := map[string]any{}
- _ = json.Unmarshal([]byte(t.Job.Run.EventPayload), &event)
-
- // TriggerEvent is added in https://github.com/go-gitea/gitea/pull/25229
- // This fallback is for the old ActionRun that doesn't have the TriggerEvent field
- // and should be removed in 1.22
- eventName := t.Job.Run.TriggerEvent
- if eventName == "" {
- eventName = t.Job.Run.Event.Event()
- }
-
- baseRef := ""
- headRef := ""
- ref := t.Job.Run.Ref
- sha := t.Job.Run.CommitSHA
- if pullPayload, err := t.Job.Run.GetPullRequestEventPayload(); err == nil && pullPayload.PullRequest != nil && pullPayload.PullRequest.Base != nil && pullPayload.PullRequest.Head != nil {
- baseRef = pullPayload.PullRequest.Base.Ref
- headRef = pullPayload.PullRequest.Head.Ref
-
- // if the TriggerEvent is pull_request_target, ref and sha need to be set according to the base of pull request
- // In GitHub's documentation, ref should be the branch or tag that triggered workflow. But when the TriggerEvent is pull_request_target,
- // the ref will be the base branch.
- if t.Job.Run.TriggerEvent == actions_module.GithubEventPullRequestTarget {
- ref = git.BranchPrefix + pullPayload.PullRequest.Base.Name
- sha = pullPayload.PullRequest.Base.Sha
- }
- }
-
- refName := git.RefName(ref)
-
- giteaRuntimeToken, err := actions.CreateAuthorizationToken(t.ID, t.Job.RunID, t.JobID)
- if err != nil {
- log.Error("actions.CreateAuthorizationToken failed: %v", err)
- }
-
- taskContext, err := structpb.NewStruct(map[string]any{
- // standard contexts, see https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
- "action": "", // string, The name of the action currently running, or the id of a step. GitHub removes special characters, and uses the name __run when the current step runs a script without an id. If you use the same action more than once in the same job, the name will include a suffix with the sequence number with underscore before it. For example, the first script you run will have the name __run, and the second script will be named __run_2. Similarly, the second invocation of actions/checkout will be actionscheckout2.
- "action_path": "", // string, The path where an action is located. This property is only supported in composite actions. You can use this path to access files located in the same repository as the action.
- "action_ref": "", // string, For a step executing an action, this is the ref of the action being executed. For example, v2.
- "action_repository": "", // string, For a step executing an action, this is the owner and repository name of the action. For example, actions/checkout.
- "action_status": "", // string, For a composite action, the current result of the composite action.
- "actor": t.Job.Run.TriggerUser.Name, // string, The username of the user that triggered the initial workflow run. If the workflow run is a re-run, this value may differ from github.triggering_actor. Any workflow re-runs will use the privileges of github.actor, even if the actor initiating the re-run (github.triggering_actor) has different privileges.
- "api_url": setting.AppURL + "api/v1", // string, The URL of the GitHub REST API.
- "base_ref": baseRef, // string, The base_ref or target branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target.
- "env": "", // string, Path on the runner to the file that sets environment variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions."
- "event": event, // object, The full event webhook payload. You can access individual properties of the event using this context. This object is identical to the webhook payload of the event that triggered the workflow run, and is different for each event. The webhooks for each GitHub Actions event is linked in "Events that trigger workflows." For example, for a workflow run triggered by the push event, this object contains the contents of the push webhook payload.
- "event_name": eventName, // string, The name of the event that triggered the workflow run.
- "event_path": "", // string, The path to the file on the runner that contains the full event webhook payload.
- "graphql_url": "", // string, The URL of the GitHub GraphQL API.
- "head_ref": headRef, // string, The head_ref or source branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target.
- "job": fmt.Sprint(t.JobID), // string, The job_id of the current job.
- "ref": ref, // string, The fully-formed ref of the branch or tag that triggered the workflow run. For workflows triggered by push, this is the branch or tag ref that was pushed. For workflows triggered by pull_request, this is the pull request merge branch. For workflows triggered by release, this is the release tag created. For other triggers, this is the branch or tag ref that triggered the workflow run. This is only set if a branch or tag is available for the event type. The ref given is fully-formed, meaning that for branches the format is refs/heads/<branch_name>, for pull requests it is refs/pull/<pr_number>/merge, and for tags it is refs/tags/<tag_name>. For example, refs/heads/feature-branch-1.
- "ref_name": refName.ShortName(), // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1.
- "ref_protected": false, // boolean, true if branch protections are configured for the ref that triggered the workflow run.
- "ref_type": refName.RefType(), // string, The type of ref that triggered the workflow run. Valid values are branch or tag.
- "path": "", // string, Path on the runner to the file that sets system PATH variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions."
- "repository": t.Job.Run.Repo.OwnerName + "/" + t.Job.Run.Repo.Name, // string, The owner and repository name. For example, Codertocat/Hello-World.
- "repository_owner": t.Job.Run.Repo.OwnerName, // string, The repository owner's name. For example, Codertocat.
- "repositoryUrl": t.Job.Run.Repo.HTMLURL(), // string, The Git URL to the repository. For example, git://github.com/codertocat/hello-world.git.
- "retention_days": "", // string, The number of days that workflow run logs and artifacts are kept.
- "run_id": fmt.Sprint(t.Job.RunID), // string, A unique number for each workflow run within a repository. This number does not change if you re-run the workflow run.
- "run_number": fmt.Sprint(t.Job.Run.Index), // string, A unique number for each run of a particular workflow in a repository. This number begins at 1 for the workflow's first run, and increments with each new run. This number does not change if you re-run the workflow run.
- "run_attempt": fmt.Sprint(t.Job.Attempt), // string, A unique number for each attempt of a particular workflow run in a repository. This number begins at 1 for the workflow run's first attempt, and increments with each re-run.
- "secret_source": "Actions", // string, The source of a secret used in a workflow. Possible values are None, Actions, Dependabot, or Codespaces.
- "server_url": setting.AppURL, // string, The URL of the GitHub server. For example: https://github.com.
- "sha": sha, // string, The commit SHA that triggered the workflow. The value of this commit SHA depends on the event that triggered the workflow. For more information, see "Events that trigger workflows." For example, ffac537e6cbbf934b08745a378932722df287a53.
- "token": t.Token, // string, A token to authenticate on behalf of the GitHub App installed on your repository. This is functionally equivalent to the GITHUB_TOKEN secret. For more information, see "Automatic token authentication."
- "triggering_actor": "", // string, The username of the user that initiated the workflow run. If the workflow run is a re-run, this value may differ from github.actor. Any workflow re-runs will use the privileges of github.actor, even if the actor initiating the re-run (github.triggering_actor) has different privileges.
- "workflow": t.Job.Run.WorkflowID, // string, The name of the workflow. If the workflow file doesn't specify a name, the value of this property is the full path of the workflow file in the repository.
- "workspace": "", // string, The default working directory on the runner for steps, and the default location of your repository when using the checkout action.
-
- // additional contexts
- "gitea_default_actions_url": setting.Actions.DefaultActionsURL.URL(),
- "gitea_runtime_token": giteaRuntimeToken,
- })
- if err != nil {
- log.Error("structpb.NewStruct failed: %v", err)
- }
-
- return taskContext
-}
-
-func findTaskNeeds(ctx context.Context, task *actions_model.ActionTask) (map[string]*runnerv1.TaskNeed, error) {
- if err := task.LoadAttributes(ctx); err != nil {
- return nil, fmt.Errorf("LoadAttributes: %w", err)
- }
- if len(task.Job.Needs) == 0 {
- return nil, nil
- }
- needs := container.SetOf(task.Job.Needs...)
-
- jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: task.Job.RunID})
- if err != nil {
- return nil, fmt.Errorf("FindRunJobs: %w", err)
- }
-
- jobIDJobs := make(map[string][]*actions_model.ActionRunJob)
- for _, job := range jobs {
- jobIDJobs[job.JobID] = append(jobIDJobs[job.JobID], job)
- }
-
- ret := make(map[string]*runnerv1.TaskNeed, len(needs))
- for jobID, jobsWithSameID := range jobIDJobs {
- if !needs.Contains(jobID) {
- continue
- }
- var jobOutputs map[string]string
- for _, job := range jobsWithSameID {
- if job.TaskID == 0 || !job.Status.IsDone() {
- // it shouldn't happen, or the job has been rerun
- continue
- }
- got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID)
- if err != nil {
- return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err)
- }
- outputs := make(map[string]string, len(got))
- for _, v := range got {
- outputs[v.OutputKey] = v.OutputValue
- }
- if len(jobOutputs) == 0 {
- jobOutputs = outputs
- } else {
- jobOutputs = mergeTwoOutputs(outputs, jobOutputs)
- }
- }
- ret[jobID] = &runnerv1.TaskNeed{
- Outputs: jobOutputs,
- Result: runnerv1.Result(actions_model.AggregateJobStatus(jobsWithSameID)),
- }
- }
-
- return ret, nil
-}
-
-// mergeTwoOutputs merges two outputs from two different ActionRunJobs
-// Values with the same output name may be overridden. The user should ensure the output names are unique.
-// See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#using-job-outputs-in-a-matrix-job
-func mergeTwoOutputs(o1, o2 map[string]string) map[string]string {
- ret := make(map[string]string, len(o1))
- for k1, v1 := range o1 {
- if len(v1) > 0 {
- ret[k1] = v1
- } else {
- ret[k1] = o2[k1]
- }
- }
- return ret
-}
diff --git a/routers/api/packages/alpine/alpine.go b/routers/api/packages/alpine/alpine.go
index 4b652c9ecc..ba4a4f23ce 100644
--- a/routers/api/packages/alpine/alpine.go
+++ b/routers/api/packages/alpine/alpine.go
@@ -68,7 +68,7 @@ func GetRepositoryFile(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageVersion(
ctx,
pv,
&packages_service.PackageFileInfo{
@@ -114,7 +114,7 @@ func UploadPackageFile(ctx *context.Context) {
pck, err := alpine_module.ParsePackage(buf)
if err != nil {
- if errors.Is(err, util.ErrInvalidArgument) || err == io.EOF {
+ if errors.Is(err, util.ErrInvalidArgument) || errors.Is(err, io.EOF) {
apiError(ctx, http.StatusBadRequest, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -216,7 +216,7 @@ func DownloadPackageFile(ctx *context.Context) {
}
}
- s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
+ s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0])
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go
index 5b035fbb71..878e0f9945 100644
--- a/routers/api/packages/api.go
+++ b/routers/api/packages/api.go
@@ -5,8 +5,6 @@ package packages
import (
"net/http"
- "regexp"
- "strings"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/perm"
@@ -46,35 +44,36 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) {
if ok { // it's a personal access token but not oauth2 token
scopeMatched := false
var err error
- if accessMode == perm.AccessModeRead {
+ switch accessMode {
+ case perm.AccessModeRead:
scopeMatched, err = scope.HasScope(auth_model.AccessTokenScopeReadPackage)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "HasScope", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "HasScope", err.Error())
return
}
- } else if accessMode == perm.AccessModeWrite {
+ case perm.AccessModeWrite:
scopeMatched, err = scope.HasScope(auth_model.AccessTokenScopeWritePackage)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "HasScope", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "HasScope", err.Error())
return
}
}
if !scopeMatched {
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea Package API"`)
- ctx.Error(http.StatusUnauthorized, "reqPackageAccess", "user should have specific permission or be a site admin")
+ ctx.HTTPError(http.StatusUnauthorized, "reqPackageAccess", "user should have specific permission or be a site admin")
return
}
// check if scope only applies to public resources
publicOnly, err := scope.PublicOnly()
if err != nil {
- ctx.Error(http.StatusForbidden, "tokenRequiresScope", "parsing public resource scope failed: "+err.Error())
+ ctx.HTTPError(http.StatusForbidden, "tokenRequiresScope", "parsing public resource scope failed: "+err.Error())
return
}
if publicOnly {
if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() {
- ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public packages")
+ ctx.HTTPError(http.StatusForbidden, "reqToken", "token scope is limited to public packages")
return
}
}
@@ -83,7 +82,7 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) {
if ctx.Package.AccessMode < accessMode && !ctx.IsUserSiteAdmin() {
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea Package API"`)
- ctx.Error(http.StatusUnauthorized, "reqPackageAccess", "user should have specific permission or be a site admin")
+ ctx.HTTPError(http.StatusUnauthorized, "reqPackageAccess", "user should have specific permission or be a site admin")
return
}
}
@@ -100,7 +99,7 @@ func verifyAuth(r *web.Router, authMethods []auth.Method) {
ctx.Doer, err = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
if err != nil {
log.Error("Failed to verify user: %v", err)
- ctx.Error(http.StatusUnauthorized, "authGroup.Verify")
+ ctx.HTTPError(http.StatusUnauthorized, "authGroup.Verify")
return
}
ctx.IsSigned = ctx.Doer != nil
@@ -138,7 +137,8 @@ func CommonRoutes() *web.Router {
}, reqPackageAccess(perm.AccessModeRead))
r.Group("/arch", func() {
r.Methods("HEAD,GET", "/repository.key", arch.GetRepositoryKey)
- r.PathGroup("*", func(g *web.RouterPathGroup) {
+ r.Methods("PUT", "" /* no repository */, reqPackageAccess(perm.AccessModeWrite), arch.UploadPackageFile)
+ r.PathGroup("/*", func(g *web.RouterPathGroup) {
g.MatchPath("PUT", "/<repository:*>", reqPackageAccess(perm.AccessModeWrite), arch.UploadPackageFile)
g.MatchPath("HEAD,GET", "/<repository:*>/<architecture>/<filename>", arch.GetPackageOrRepositoryFile)
g.MatchPath("DELETE", "/<repository:*>/<name>/<version>/<architecture>", reqPackageAccess(perm.AccessModeWrite), arch.DeletePackageVersion)
@@ -280,42 +280,10 @@ func CommonRoutes() *web.Router {
})
})
}, reqPackageAccess(perm.AccessModeRead))
- r.Group("/conda", func() {
- var (
- downloadPattern = regexp.MustCompile(`\A(.+/)?(.+)/((?:[^/]+(?:\.tar\.bz2|\.conda))|(?:current_)?repodata\.json(?:\.bz2)?)\z`)
- uploadPattern = regexp.MustCompile(`\A(.+/)?([^/]+(?:\.tar\.bz2|\.conda))\z`)
- )
-
- r.Get("/*", func(ctx *context.Context) {
- m := downloadPattern.FindStringSubmatch(ctx.PathParam("*"))
- if len(m) == 0 {
- ctx.Status(http.StatusNotFound)
- return
- }
-
- ctx.SetPathParam("channel", strings.TrimSuffix(m[1], "/"))
- ctx.SetPathParam("architecture", m[2])
- ctx.SetPathParam("filename", m[3])
-
- switch m[3] {
- case "repodata.json", "repodata.json.bz2", "current_repodata.json", "current_repodata.json.bz2":
- conda.EnumeratePackages(ctx)
- default:
- conda.DownloadPackageFile(ctx)
- }
- })
- r.Put("/*", reqPackageAccess(perm.AccessModeWrite), func(ctx *context.Context) {
- m := uploadPattern.FindStringSubmatch(ctx.PathParam("*"))
- if len(m) == 0 {
- ctx.Status(http.StatusNotFound)
- return
- }
-
- ctx.SetPathParam("channel", strings.TrimSuffix(m[1], "/"))
- ctx.SetPathParam("filename", m[2])
-
- conda.UploadPackageFile(ctx)
- })
+ r.PathGroup("/conda/*", func(g *web.RouterPathGroup) {
+ g.MatchPath("GET", "/<architecture>/<filename>", conda.ListOrGetPackages)
+ g.MatchPath("GET", "/<channel:*>/<architecture>/<filename>", conda.ListOrGetPackages)
+ g.MatchPath("PUT", "/<channel:*>/<filename>", reqPackageAccess(perm.AccessModeWrite), conda.UploadPackageFile)
}, reqPackageAccess(perm.AccessModeRead))
r.Group("/cran", func() {
r.Group("/src", func() {
@@ -356,60 +324,15 @@ func CommonRoutes() *web.Router {
}, reqPackageAccess(perm.AccessModeRead))
r.Group("/go", func() {
r.Put("/upload", reqPackageAccess(perm.AccessModeWrite), goproxy.UploadPackage)
- r.Get("/sumdb/sum.golang.org/supported", func(ctx *context.Context) {
- ctx.Status(http.StatusNotFound)
- })
+ r.Get("/sumdb/sum.golang.org/supported", http.NotFound)
- // Manual mapping of routes because the package name contains slashes which chi does not support
// https://go.dev/ref/mod#goproxy-protocol
- r.Get("/*", func(ctx *context.Context) {
- path := ctx.PathParam("*")
-
- if strings.HasSuffix(path, "/@latest") {
- ctx.SetPathParam("name", path[:len(path)-len("/@latest")])
- ctx.SetPathParam("version", "latest")
-
- goproxy.PackageVersionMetadata(ctx)
- return
- }
-
- parts := strings.SplitN(path, "/@v/", 2)
- if len(parts) != 2 {
- ctx.Status(http.StatusNotFound)
- return
- }
-
- ctx.SetPathParam("name", parts[0])
-
- // <package/name>/@v/list
- if parts[1] == "list" {
- goproxy.EnumeratePackageVersions(ctx)
- return
- }
-
- // <package/name>/@v/<version>.zip
- if strings.HasSuffix(parts[1], ".zip") {
- ctx.SetPathParam("version", parts[1][:len(parts[1])-len(".zip")])
-
- goproxy.DownloadPackageFile(ctx)
- return
- }
- // <package/name>/@v/<version>.info
- if strings.HasSuffix(parts[1], ".info") {
- ctx.SetPathParam("version", parts[1][:len(parts[1])-len(".info")])
-
- goproxy.PackageVersionMetadata(ctx)
- return
- }
- // <package/name>/@v/<version>.mod
- if strings.HasSuffix(parts[1], ".mod") {
- ctx.SetPathParam("version", parts[1][:len(parts[1])-len(".mod")])
-
- goproxy.PackageVersionGoModContent(ctx)
- return
- }
-
- ctx.Status(http.StatusNotFound)
+ r.PathGroup("/*", func(g *web.RouterPathGroup) {
+ g.MatchPath("GET", "/<name:*>/@<version:latest>", goproxy.PackageVersionMetadata)
+ g.MatchPath("GET", "/<name:*>/@v/list", goproxy.EnumeratePackageVersions)
+ g.MatchPath("GET", "/<name:*>/@v/<version>.zip", goproxy.DownloadPackageFile)
+ g.MatchPath("GET", "/<name:*>/@v/<version>.info", goproxy.PackageVersionMetadata)
+ g.MatchPath("GET", "/<name:*>/@v/<version>.mod", goproxy.PackageVersionGoModContent)
})
}, reqPackageAccess(perm.AccessModeRead))
r.Group("/generic", func() {
@@ -530,82 +453,26 @@ func CommonRoutes() *web.Router {
})
})
}, reqPackageAccess(perm.AccessModeRead))
+
r.Group("/pypi", func() {
r.Post("/", reqPackageAccess(perm.AccessModeWrite), pypi.UploadPackageFile)
r.Get("/files/{id}/{version}/{filename}", pypi.DownloadPackageFile)
r.Get("/simple/{id}", pypi.PackageMetadata)
}, reqPackageAccess(perm.AccessModeRead))
- r.Group("/rpm", func() {
- r.Group("/repository.key", func() {
- r.Head("", rpm.GetRepositoryKey)
- r.Get("", rpm.GetRepositoryKey)
- })
-
- var (
- repoPattern = regexp.MustCompile(`\A(.*?)\.repo\z`)
- uploadPattern = regexp.MustCompile(`\A(.*?)/upload\z`)
- filePattern = regexp.MustCompile(`\A(.*?)/package/([^/]+)/([^/]+)/([^/]+)(?:/([^/]+\.rpm)|)\z`)
- repoFilePattern = regexp.MustCompile(`\A(.*?)/repodata/([^/]+)\z`)
- )
-
- r.Methods("HEAD,GET,PUT,DELETE", "*", func(ctx *context.Context) {
- path := ctx.PathParam("*")
- isHead := ctx.Req.Method == "HEAD"
- isGetHead := ctx.Req.Method == "HEAD" || ctx.Req.Method == "GET"
- isPut := ctx.Req.Method == "PUT"
- isDelete := ctx.Req.Method == "DELETE"
-
- m := repoPattern.FindStringSubmatch(path)
- if len(m) == 2 && isGetHead {
- ctx.SetPathParam("group", strings.Trim(m[1], "/"))
- rpm.GetRepositoryConfig(ctx)
- return
- }
-
- m = repoFilePattern.FindStringSubmatch(path)
- if len(m) == 3 && isGetHead {
- ctx.SetPathParam("group", strings.Trim(m[1], "/"))
- ctx.SetPathParam("filename", m[2])
- if isHead {
- rpm.CheckRepositoryFileExistence(ctx)
- } else {
- rpm.GetRepositoryFile(ctx)
- }
- return
- }
-
- m = uploadPattern.FindStringSubmatch(path)
- if len(m) == 2 && isPut {
- reqPackageAccess(perm.AccessModeWrite)(ctx)
- if ctx.Written() {
- return
- }
- ctx.SetPathParam("group", strings.Trim(m[1], "/"))
- rpm.UploadPackageFile(ctx)
- return
- }
-
- m = filePattern.FindStringSubmatch(path)
- if len(m) == 6 && (isGetHead || isDelete) {
- ctx.SetPathParam("group", strings.Trim(m[1], "/"))
- ctx.SetPathParam("name", m[2])
- ctx.SetPathParam("version", m[3])
- ctx.SetPathParam("architecture", m[4])
- if isGetHead {
- rpm.DownloadPackageFile(ctx)
- } else {
- reqPackageAccess(perm.AccessModeWrite)(ctx)
- if ctx.Written() {
- return
- }
- rpm.DeletePackageFile(ctx)
- }
- return
- }
- ctx.Status(http.StatusNotFound)
- })
+ r.Methods("HEAD,GET", "/rpm.repo", reqPackageAccess(perm.AccessModeRead), rpm.GetRepositoryConfig)
+ r.PathGroup("/rpm/*", func(g *web.RouterPathGroup) {
+ g.MatchPath("HEAD,GET", "/repository.key", rpm.GetRepositoryKey)
+ g.MatchPath("HEAD,GET", "/<group:*>.repo", rpm.GetRepositoryConfig)
+ g.MatchPath("HEAD", "/<group:*>/repodata/<filename>", rpm.CheckRepositoryFileExistence)
+ g.MatchPath("GET", "/<group:*>/repodata/<filename>", rpm.GetRepositoryFile)
+ g.MatchPath("PUT", "/<group:*>/upload", reqPackageAccess(perm.AccessModeWrite), rpm.UploadPackageFile)
+ // this URL pattern is only used internally in the RPM index, it is generated by us, the filename part is not really used (can be anything)
+ g.MatchPath("HEAD,GET", "/<group:*>/package/<name>/<version>/<architecture>", rpm.DownloadPackageFile)
+ g.MatchPath("HEAD,GET", "/<group:*>/package/<name>/<version>/<architecture>/<filename>", rpm.DownloadPackageFile)
+ g.MatchPath("DELETE", "/<group:*>/package/<name>/<version>/<architecture>", reqPackageAccess(perm.AccessModeWrite), rpm.DeletePackageFile)
}, reqPackageAccess(perm.AccessModeRead))
+
r.Group("/rubygems", func() {
r.Get("/specs.4.8.gz", rubygems.EnumeratePackages)
r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest)
@@ -619,6 +486,7 @@ func CommonRoutes() *web.Router {
r.Delete("/yank", rubygems.DeletePackage)
}, reqPackageAccess(perm.AccessModeWrite))
}, reqPackageAccess(perm.AccessModeRead))
+
r.Group("/swift", func() {
r.Group("", func() { // Needs to be unauthenticated.
r.Post("", swift.CheckAuthenticate)
@@ -630,31 +498,12 @@ func CommonRoutes() *web.Router {
r.Get("", swift.EnumeratePackageVersions)
r.Get(".json", swift.EnumeratePackageVersions)
}, swift.CheckAcceptMediaType(swift.AcceptJSON))
- r.Group("/{version}", func() {
- r.Get("/Package.swift", swift.CheckAcceptMediaType(swift.AcceptSwift), swift.DownloadManifest)
- r.Put("", reqPackageAccess(perm.AccessModeWrite), swift.CheckAcceptMediaType(swift.AcceptJSON), swift.UploadPackageFile)
- r.Get("", func(ctx *context.Context) {
- // Can't use normal routes here: https://github.com/go-chi/chi/issues/781
-
- version := ctx.PathParam("version")
- if strings.HasSuffix(version, ".zip") {
- swift.CheckAcceptMediaType(swift.AcceptZip)(ctx)
- if ctx.Written() {
- return
- }
- ctx.SetPathParam("version", version[:len(version)-4])
- swift.DownloadPackageFile(ctx)
- } else {
- swift.CheckAcceptMediaType(swift.AcceptJSON)(ctx)
- if ctx.Written() {
- return
- }
- if strings.HasSuffix(version, ".json") {
- ctx.SetPathParam("version", version[:len(version)-5])
- }
- swift.PackageVersionMetadata(ctx)
- }
- })
+ r.PathGroup("/*", func(g *web.RouterPathGroup) {
+ g.MatchPath("GET", "/<version>.json", swift.CheckAcceptMediaType(swift.AcceptJSON), swift.PackageVersionMetadata)
+ g.MatchPath("GET", "/<version>.zip", swift.CheckAcceptMediaType(swift.AcceptZip), swift.DownloadPackageFile)
+ g.MatchPath("GET", "/<version>/Package.swift", swift.CheckAcceptMediaType(swift.AcceptSwift), swift.DownloadManifest)
+ g.MatchPath("GET", "/<version>", swift.CheckAcceptMediaType(swift.AcceptJSON), swift.PackageVersionMetadata)
+ g.MatchPath("PUT", "/<version>", reqPackageAccess(perm.AccessModeWrite), swift.CheckAcceptMediaType(swift.AcceptJSON), swift.UploadPackageFile)
})
})
r.Get("/identifiers", swift.CheckAcceptMediaType(swift.AcceptJSON), swift.LookupPackageIdentifiers)
@@ -691,6 +540,8 @@ func ContainerRoutes() *web.Router {
&container.Auth{},
})
+ // TODO: Content Discovery / References (not implemented yet)
+
r.Get("", container.ReqContainerAccess, container.DetermineSupport)
r.Group("/token", func() {
r.Get("", container.Authenticate)
@@ -698,150 +549,24 @@ func ContainerRoutes() *web.Router {
})
r.Get("/_catalog", container.ReqContainerAccess, container.GetRepositoryList)
r.Group("/{username}", func() {
- r.Group("/{image}", func() {
- r.Group("/blobs/uploads", func() {
- r.Post("", container.InitiateUploadBlob)
- r.Group("/{uuid}", func() {
- r.Get("", container.GetUploadBlob)
- r.Patch("", container.UploadBlob)
- r.Put("", container.EndUploadBlob)
- r.Delete("", container.CancelUploadBlob)
- })
- }, reqPackageAccess(perm.AccessModeWrite))
- r.Group("/blobs/{digest}", func() {
- r.Head("", container.HeadBlob)
- r.Get("", container.GetBlob)
- r.Delete("", reqPackageAccess(perm.AccessModeWrite), container.DeleteBlob)
- })
- r.Group("/manifests/{reference}", func() {
- r.Put("", reqPackageAccess(perm.AccessModeWrite), container.UploadManifest)
- r.Head("", container.HeadManifest)
- r.Get("", container.GetManifest)
- r.Delete("", reqPackageAccess(perm.AccessModeWrite), container.DeleteManifest)
- })
- r.Get("/tags/list", container.GetTagList)
- }, container.VerifyImageName)
-
- var (
- blobsUploadsPattern = regexp.MustCompile(`\A(.+)/blobs/uploads/([a-zA-Z0-9-_.=]+)\z`)
- blobsPattern = regexp.MustCompile(`\A(.+)/blobs/([^/]+)\z`)
- manifestsPattern = regexp.MustCompile(`\A(.+)/manifests/([^/]+)\z`)
- )
-
- // Manual mapping of routes because {image} can contain slashes which chi does not support
- r.Methods("HEAD,GET,POST,PUT,PATCH,DELETE", "/*", func(ctx *context.Context) {
- path := ctx.PathParam("*")
- isHead := ctx.Req.Method == "HEAD"
- isGet := ctx.Req.Method == "GET"
- isPost := ctx.Req.Method == "POST"
- isPut := ctx.Req.Method == "PUT"
- isPatch := ctx.Req.Method == "PATCH"
- isDelete := ctx.Req.Method == "DELETE"
-
- if isPost && strings.HasSuffix(path, "/blobs/uploads") {
- reqPackageAccess(perm.AccessModeWrite)(ctx)
- if ctx.Written() {
- return
- }
-
- ctx.SetPathParam("image", path[:len(path)-14])
- container.VerifyImageName(ctx)
- if ctx.Written() {
- return
- }
-
- container.InitiateUploadBlob(ctx)
- return
- }
- if isGet && strings.HasSuffix(path, "/tags/list") {
- ctx.SetPathParam("image", path[:len(path)-10])
- container.VerifyImageName(ctx)
- if ctx.Written() {
- return
- }
-
- container.GetTagList(ctx)
- return
- }
-
- m := blobsUploadsPattern.FindStringSubmatch(path)
- if len(m) == 3 && (isGet || isPut || isPatch || isDelete) {
- reqPackageAccess(perm.AccessModeWrite)(ctx)
- if ctx.Written() {
- return
- }
-
- ctx.SetPathParam("image", m[1])
- container.VerifyImageName(ctx)
- if ctx.Written() {
- return
- }
-
- ctx.SetPathParam("uuid", m[2])
-
- if isGet {
- container.GetUploadBlob(ctx)
- } else if isPatch {
- container.UploadBlob(ctx)
- } else if isPut {
- container.EndUploadBlob(ctx)
- } else {
- container.CancelUploadBlob(ctx)
- }
- return
- }
- m = blobsPattern.FindStringSubmatch(path)
- if len(m) == 3 && (isHead || isGet || isDelete) {
- ctx.SetPathParam("image", m[1])
- container.VerifyImageName(ctx)
- if ctx.Written() {
- return
- }
-
- ctx.SetPathParam("digest", m[2])
-
- if isHead {
- container.HeadBlob(ctx)
- } else if isGet {
- container.GetBlob(ctx)
- } else {
- reqPackageAccess(perm.AccessModeWrite)(ctx)
- if ctx.Written() {
- return
- }
- container.DeleteBlob(ctx)
- }
- return
- }
- m = manifestsPattern.FindStringSubmatch(path)
- if len(m) == 3 && (isHead || isGet || isPut || isDelete) {
- ctx.SetPathParam("image", m[1])
- container.VerifyImageName(ctx)
- if ctx.Written() {
- return
- }
-
- ctx.SetPathParam("reference", m[2])
-
- if isHead {
- container.HeadManifest(ctx)
- } else if isGet {
- container.GetManifest(ctx)
- } else {
- reqPackageAccess(perm.AccessModeWrite)(ctx)
- if ctx.Written() {
- return
- }
- if isPut {
- container.UploadManifest(ctx)
- } else {
- container.DeleteManifest(ctx)
- }
- }
- return
- }
-
- ctx.Status(http.StatusNotFound)
+ r.PathGroup("/*", func(g *web.RouterPathGroup) {
+ g.MatchPath("POST", "/<image:*>/blobs/uploads", reqPackageAccess(perm.AccessModeWrite), container.VerifyImageName, container.PostBlobsUploads)
+ g.MatchPath("GET", "/<image:*>/tags/list", container.VerifyImageName, container.GetTagsList)
+
+ patternBlobsUploadsUUID := g.PatternRegexp(`/<image:*>/blobs/uploads/<uuid:[-.=\w]+>`, reqPackageAccess(perm.AccessModeWrite), container.VerifyImageName)
+ g.MatchPattern("GET", patternBlobsUploadsUUID, container.GetBlobsUpload)
+ g.MatchPattern("PATCH", patternBlobsUploadsUUID, container.PatchBlobsUpload)
+ g.MatchPattern("PUT", patternBlobsUploadsUUID, container.PutBlobsUpload)
+ g.MatchPattern("DELETE", patternBlobsUploadsUUID, container.DeleteBlobsUpload)
+
+ g.MatchPath("HEAD", `/<image:*>/blobs/<digest>`, container.VerifyImageName, container.HeadBlob)
+ g.MatchPath("GET", `/<image:*>/blobs/<digest>`, container.VerifyImageName, container.GetBlob)
+ g.MatchPath("DELETE", `/<image:*>/blobs/<digest>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.DeleteBlob)
+
+ g.MatchPath("HEAD", `/<image:*>/manifests/<reference>`, container.VerifyImageName, container.HeadManifest)
+ g.MatchPath("GET", `/<image:*>/manifests/<reference>`, container.VerifyImageName, container.GetManifest)
+ g.MatchPath("PUT", `/<image:*>/manifests/<reference>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.PutManifest)
+ g.MatchPath("DELETE", `/<image:*>/manifests/<reference>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.DeleteManifest)
})
}, container.ReqContainerAccess, context.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead))
diff --git a/routers/api/packages/arch/arch.go b/routers/api/packages/arch/arch.go
index 573e93cfb0..bf9cc3f1b8 100644
--- a/routers/api/packages/arch/arch.go
+++ b/routers/api/packages/arch/arch.go
@@ -62,7 +62,7 @@ func UploadPackageFile(ctx *context.Context) {
pck, err := arch_module.ParsePackage(buf)
if err != nil {
- if errors.Is(err, util.ErrInvalidArgument) || err == io.EOF {
+ if errors.Is(err, util.ErrInvalidArgument) || errors.Is(err, io.EOF) {
apiError(ctx, http.StatusBadRequest, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -239,7 +239,7 @@ func GetPackageOrRepositoryFile(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
+ s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0])
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
diff --git a/routers/api/packages/cargo/cargo.go b/routers/api/packages/cargo/cargo.go
index 3d8407e6b6..cfcf79244f 100644
--- a/routers/api/packages/cargo/cargo.go
+++ b/routers/api/packages/cargo/cargo.go
@@ -51,7 +51,7 @@ func apiError(ctx *context.Context, status int, obj any) {
// https://rust-lang.github.io/rfcs/2789-sparse-index.html
func RepositoryConfig(ctx *context.Context) {
- ctx.JSON(http.StatusOK, cargo_service.BuildConfig(ctx.Package.Owner, setting.Service.RequireSignInView || ctx.Package.Owner.Visibility != structs.VisibleTypePublic))
+ ctx.JSON(http.StatusOK, cargo_service.BuildConfig(ctx.Package.Owner, setting.Service.RequireSignInViewStrict || ctx.Package.Owner.Visibility != structs.VisibleTypePublic))
}
func EnumeratePackageVersions(ctx *context.Context) {
@@ -95,10 +95,7 @@ type SearchResultMeta struct {
// https://doc.rust-lang.org/cargo/reference/registries.html#search
func SearchPackages(ctx *context.Context) {
- page := ctx.FormInt("page")
- if page < 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
perPage := ctx.FormInt("per_page")
paginator := db.ListOptions{
Page: page,
@@ -168,7 +165,7 @@ func ListOwners(ctx *context.Context) {
// DownloadPackageFile serves the content of a package
func DownloadPackageFile(ctx *context.Context) {
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
@@ -181,7 +178,7 @@ func DownloadPackageFile(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -276,7 +273,7 @@ func UnyankPackage(ctx *context.Context) {
func yankPackage(ctx *context.Context, yank bool) {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeCargo, ctx.PathParam("package"), ctx.PathParam("version"))
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
diff --git a/routers/api/packages/chef/auth.go b/routers/api/packages/chef/auth.go
index a790e9a363..c6808300a2 100644
--- a/routers/api/packages/chef/auth.go
+++ b/routers/api/packages/chef/auth.go
@@ -12,6 +12,7 @@ import (
"crypto/x509"
"encoding/base64"
"encoding/pem"
+ "errors"
"fmt"
"hash"
"math/big"
@@ -121,7 +122,7 @@ func verifyTimestamp(req *http.Request) error {
}
if diff > maxTimeDifference {
- return fmt.Errorf("time difference")
+ return errors.New("time difference")
}
return nil
@@ -190,7 +191,7 @@ func getAuthorizationData(req *http.Request) ([]byte, error) {
tmp := make([]string, len(valueList))
for k, v := range valueList {
if k > len(tmp) {
- return nil, fmt.Errorf("invalid X-Ops-Authorization headers")
+ return nil, errors.New("invalid X-Ops-Authorization headers")
}
tmp[k-1] = v
}
@@ -267,7 +268,7 @@ func verifyDataOld(signature, data []byte, pub *rsa.PublicKey) error {
}
if !slices.Equal(out[skip:], data) {
- return fmt.Errorf("could not verify signature")
+ return errors.New("could not verify signature")
}
return nil
diff --git a/routers/api/packages/chef/chef.go b/routers/api/packages/chef/chef.go
index b3cdf12697..1f11afe548 100644
--- a/routers/api/packages/chef/chef.go
+++ b/routers/api/packages/chef/chef.go
@@ -216,7 +216,7 @@ func PackageVersionMetadata(ctx *context.Context) {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeChef, packageName, packageVersion)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -327,7 +327,7 @@ func UploadPackage(ctx *context.Context) {
func DownloadPackage(ctx *context.Context) {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeChef, ctx.PathParam("name"), ctx.PathParam("version"))
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -343,7 +343,7 @@ func DownloadPackage(ctx *context.Context) {
pf := pd.Files[0].File
- s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
+ s, u, _, err := packages_service.OpenFileForDownload(ctx, pf)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
@@ -368,7 +368,7 @@ func DeletePackageVersion(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
diff --git a/routers/api/packages/composer/api.go b/routers/api/packages/composer/api.go
index a3bcf80417..a3ea2c2f9a 100644
--- a/routers/api/packages/composer/api.go
+++ b/routers/api/packages/composer/api.go
@@ -66,6 +66,7 @@ type PackageMetadataResponse struct {
}
// PackageVersionMetadata contains package metadata
+// https://getcomposer.org/doc/05-repositories.md#package
type PackageVersionMetadata struct {
*composer_module.Metadata
Name string `json:"name"`
@@ -73,6 +74,7 @@ type PackageVersionMetadata struct {
Type string `json:"type"`
Created time.Time `json:"time"`
Dist Dist `json:"dist"`
+ Source Source `json:"source"`
}
// Dist contains package download information
@@ -82,6 +84,13 @@ type Dist struct {
Checksum string `json:"shasum"`
}
+// Source contains package source information
+type Source struct {
+ URL string `json:"url"`
+ Type string `json:"type"`
+ Reference string `json:"reference"`
+}
+
func createPackageMetadataResponse(registryURL string, pds []*packages_model.PackageDescriptor) *PackageMetadataResponse {
versions := make([]*PackageVersionMetadata, 0, len(pds))
@@ -94,7 +103,7 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac
}
}
- versions = append(versions, &PackageVersionMetadata{
+ pkg := PackageVersionMetadata{
Name: pd.Package.Name,
Version: pd.Version.Version,
Type: packageType,
@@ -105,7 +114,16 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac
URL: fmt.Sprintf("%s/files/%s/%s/%s", registryURL, url.PathEscape(pd.Package.LowerName), url.PathEscape(pd.Version.LowerVersion), url.PathEscape(pd.Files[0].File.LowerName)),
Checksum: pd.Files[0].Blob.HashSHA1,
},
- })
+ }
+ if pd.Repository != nil {
+ pkg.Source = Source{
+ URL: pd.Repository.HTMLURL(),
+ Type: "git",
+ Reference: pd.Version.Version,
+ }
+ }
+
+ versions = append(versions, &pkg)
}
return &PackageMetadataResponse{
diff --git a/routers/api/packages/composer/composer.go b/routers/api/packages/composer/composer.go
index 40f72f6484..9daf0ffeff 100644
--- a/routers/api/packages/composer/composer.go
+++ b/routers/api/packages/composer/composer.go
@@ -53,10 +53,7 @@ func ServiceIndex(ctx *context.Context) {
// SearchPackages searches packages, only "q" is supported
// https://packagist.org/apidoc#search-packages
func SearchPackages(ctx *context.Context) {
- page := ctx.FormInt("page")
- if page < 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
perPage := ctx.FormInt("per_page")
paginator := db.ListOptions{
Page: page,
@@ -163,7 +160,7 @@ func PackageMetadata(ctx *context.Context) {
// DownloadPackageFile serves the content of a package
func DownloadPackageFile(ctx *context.Context) {
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
@@ -176,7 +173,7 @@ func DownloadPackageFile(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
diff --git a/routers/api/packages/conan/conan.go b/routers/api/packages/conan/conan.go
index 4a9f0a3ffc..fe70e02cd6 100644
--- a/routers/api/packages/conan/conan.go
+++ b/routers/api/packages/conan/conan.go
@@ -5,6 +5,7 @@ package conan
import (
std_ctx "context"
+ "errors"
"fmt"
"io"
"net/http"
@@ -183,7 +184,7 @@ func serveSnapshot(ctx *context.Context, fileKey string) {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeConan, rref.Name, rref.Version)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -244,7 +245,7 @@ func serveDownloadURLs(ctx *context.Context, fileKey, downloadURL string) {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeConan, rref.Name, rref.Version)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -479,7 +480,7 @@ func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKe
return
}
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
@@ -493,7 +494,7 @@ func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKe
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -509,7 +510,7 @@ func DeleteRecipeV1(ctx *context.Context) {
rref := ctx.Data[recipeReferenceKey].(*conan_module.RecipeReference)
if err := deleteRecipeOrPackage(ctx, rref, true, nil, false); err != nil {
- if err == packages_model.ErrPackageNotExist || err == conan_model.ErrPackageReferenceNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -524,7 +525,7 @@ func DeleteRecipeV2(ctx *context.Context) {
rref := ctx.Data[recipeReferenceKey].(*conan_module.RecipeReference)
if err := deleteRecipeOrPackage(ctx, rref, rref.Revision == "", nil, false); err != nil {
- if err == packages_model.ErrPackageNotExist || err == conan_model.ErrPackageReferenceNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -571,7 +572,7 @@ func DeletePackageV1(ctx *context.Context) {
for _, reference := range references {
pref, _ := conan_module.NewPackageReference(currentRref, reference.Value, conan_module.DefaultRevision)
if err := deleteRecipeOrPackage(ctx, currentRref, true, pref, true); err != nil {
- if err == packages_model.ErrPackageNotExist || err == conan_model.ErrPackageReferenceNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -590,7 +591,7 @@ func DeletePackageV2(ctx *context.Context) {
if pref != nil { // has package reference
if err := deleteRecipeOrPackage(ctx, rref, false, pref, pref.Revision == ""); err != nil {
- if err == packages_model.ErrPackageNotExist || err == conan_model.ErrPackageReferenceNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -615,7 +616,7 @@ func DeletePackageV2(ctx *context.Context) {
pref, _ := conan_module.NewPackageReference(rref, reference.Value, conan_module.DefaultRevision)
if err := deleteRecipeOrPackage(ctx, rref, false, pref, true); err != nil {
- if err == packages_model.ErrPackageNotExist || err == conan_model.ErrPackageReferenceNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -749,7 +750,7 @@ func LatestRecipeRevision(ctx *context.Context) {
revision, err := conan_model.GetLastRecipeRevision(ctx, ctx.Package.Owner.ID, rref)
if err != nil {
- if err == conan_model.ErrRecipeReferenceNotExist || err == conan_model.ErrPackageReferenceNotExist {
+ if errors.Is(err, conan_model.ErrRecipeReferenceNotExist) || errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -766,7 +767,7 @@ func LatestPackageRevision(ctx *context.Context) {
revision, err := conan_model.GetLastPackageRevision(ctx, ctx.Package.Owner.ID, pref)
if err != nil {
- if err == conan_model.ErrRecipeReferenceNotExist || err == conan_model.ErrPackageReferenceNotExist {
+ if errors.Is(err, conan_model.ErrRecipeReferenceNotExist) || errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -796,7 +797,7 @@ func listRevisionFiles(ctx *context.Context, fileKey string) {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeConan, rref.Name, rref.Version)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
diff --git a/routers/api/packages/conan/search.go b/routers/api/packages/conan/search.go
index 7370c702cd..0dbbd500d2 100644
--- a/routers/api/packages/conan/search.go
+++ b/routers/api/packages/conan/search.go
@@ -4,6 +4,7 @@
package conan
import (
+ "errors"
"net/http"
"strings"
@@ -76,7 +77,7 @@ func searchPackages(ctx *context.Context, searchAllRevisions bool) {
if !searchAllRevisions && rref.Revision == "" {
lastRevision, err := conan_model.GetLastRecipeRevision(ctx, ctx.Package.Owner.ID, rref)
if err != nil {
- if err == conan_model.ErrRecipeReferenceNotExist {
+ if errors.Is(err, conan_model.ErrRecipeReferenceNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -87,7 +88,7 @@ func searchPackages(ctx *context.Context, searchAllRevisions bool) {
} else {
has, err := conan_model.RecipeExists(ctx, ctx.Package.Owner.ID, rref)
if err != nil {
- if err == conan_model.ErrRecipeReferenceNotExist {
+ if errors.Is(err, conan_model.ErrRecipeReferenceNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -119,7 +120,7 @@ func searchPackages(ctx *context.Context, searchAllRevisions bool) {
}
packageReferences, err := conan_model.GetPackageReferences(ctx, ctx.Package.Owner.ID, currentRef)
if err != nil {
- if err == conan_model.ErrRecipeReferenceNotExist {
+ if errors.Is(err, conan_model.ErrRecipeReferenceNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -133,7 +134,7 @@ func searchPackages(ctx *context.Context, searchAllRevisions bool) {
pref, _ := conan_module.NewPackageReference(currentRef, packageReference.Value, "")
lastPackageRevision, err := conan_model.GetLastPackageRevision(ctx, ctx.Package.Owner.ID, pref)
if err != nil {
- if err == conan_model.ErrPackageReferenceNotExist {
+ if errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -143,7 +144,7 @@ func searchPackages(ctx *context.Context, searchAllRevisions bool) {
pref = pref.WithRevision(lastPackageRevision.Value)
infoRaw, err := conan_model.GetPackageInfo(ctx, ctx.Package.Owner.ID, pref)
if err != nil {
- if err == conan_model.ErrPackageReferenceNotExist {
+ if errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
diff --git a/routers/api/packages/conda/conda.go b/routers/api/packages/conda/conda.go
index 7a46681235..e8c97503c8 100644
--- a/routers/api/packages/conda/conda.go
+++ b/routers/api/packages/conda/conda.go
@@ -36,6 +36,24 @@ func apiError(ctx *context.Context, status int, obj any) {
})
}
+func isCondaPackageFileName(filename string) bool {
+ return strings.HasSuffix(filename, ".tar.bz2") || strings.HasSuffix(filename, ".conda")
+}
+
+func ListOrGetPackages(ctx *context.Context) {
+ filename := ctx.PathParam("filename")
+ switch filename {
+ case "repodata.json", "repodata.json.bz2", "current_repodata.json", "current_repodata.json.bz2":
+ EnumeratePackages(ctx)
+ return
+ }
+ if isCondaPackageFileName(filename) {
+ DownloadPackageFile(ctx)
+ return
+ }
+ http.NotFound(ctx.Resp, ctx.Req)
+}
+
func EnumeratePackages(ctx *context.Context) {
type Info struct {
Subdir string `json:"subdir"`
@@ -174,6 +192,12 @@ func EnumeratePackages(ctx *context.Context) {
}
func UploadPackageFile(ctx *context.Context) {
+ filename := ctx.PathParam("filename")
+ if !isCondaPackageFileName(filename) {
+ apiError(ctx, http.StatusBadRequest, nil)
+ return
+ }
+
upload, needToClose, err := ctx.UploadStream()
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
@@ -191,7 +215,7 @@ func UploadPackageFile(ctx *context.Context) {
defer buf.Close()
var pck *conda_module.Package
- if strings.HasSuffix(strings.ToLower(ctx.PathParam("filename")), ".tar.bz2") {
+ if strings.HasSuffix(filename, ".tar.bz2") {
pck, err = conda_module.ParsePackageBZ2(buf)
} else {
pck, err = conda_module.ParsePackageConda(buf, buf.Size())
@@ -293,7 +317,7 @@ func DownloadPackageFile(ctx *context.Context) {
pf := pfs[0]
- s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
+ s, u, _, err := packages_service.OpenFileForDownload(ctx, pf)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
diff --git a/routers/api/packages/container/auth.go b/routers/api/packages/container/auth.go
index 1d8ae6af7d..1e1b87eb79 100644
--- a/routers/api/packages/container/auth.go
+++ b/routers/api/packages/container/auth.go
@@ -21,7 +21,7 @@ func (a *Auth) Name() string {
}
// Verify extracts the user from the Bearer token
-// If it's an anonymous session a ghost user is returned
+// If it's an anonymous session, a ghost user is returned
func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) (*user_model.User, error) {
packageMeta, err := packages.ParseAuthorizationRequest(req)
if err != nil {
diff --git a/routers/api/packages/container/blob.go b/routers/api/packages/container/blob.go
index 4595b9a33d..4b7bcee9d0 100644
--- a/routers/api/packages/container/blob.go
+++ b/routers/api/packages/container/blob.go
@@ -20,11 +20,13 @@ import (
container_module "code.gitea.io/gitea/modules/packages/container"
"code.gitea.io/gitea/modules/util"
packages_service "code.gitea.io/gitea/services/packages"
+
+ "github.com/opencontainers/go-digest"
)
// saveAsPackageBlob creates a package blob from an upload
// The uploaded blob gets stored in a special upload version to link them to the package/image
-func saveAsPackageBlob(ctx context.Context, hsr packages_module.HashedSizeReader, pci *packages_service.PackageCreationInfo) (*packages_model.PackageBlob, error) { //nolint:unparam
+func saveAsPackageBlob(ctx context.Context, hsr packages_module.HashedSizeReader, pci *packages_service.PackageCreationInfo) (*packages_model.PackageBlob, error) { //nolint:unparam // PackageBlob is never used
pb := packages_service.NewPackageBlob(hsr)
exists := false
@@ -88,20 +90,18 @@ func mountBlob(ctx context.Context, pi *packages_service.PackageInfo, pb *packag
})
}
-func containerPkgName(piOwnerID int64, piName string) string {
- return fmt.Sprintf("pkg_%d_container_%s", piOwnerID, strings.ToLower(piName))
+func containerGlobalLockKey(piOwnerID int64, piName, usage string) string {
+ return fmt.Sprintf("pkg_%d_container_%s_%s", piOwnerID, strings.ToLower(piName), usage)
}
func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageInfo) (*packages_model.PackageVersion, error) {
- var uploadVersion *packages_model.PackageVersion
-
- releaser, err := globallock.Lock(ctx, containerPkgName(pi.Owner.ID, pi.Name))
+ releaser, err := globallock.Lock(ctx, containerGlobalLockKey(pi.Owner.ID, pi.Name, "package"))
if err != nil {
return nil, err
}
defer releaser()
- err = db.WithTx(ctx, func(ctx context.Context) error {
+ return db.WithTx2(ctx, func(ctx context.Context) (*packages_model.PackageVersion, error) {
created := true
p := &packages_model.Package{
OwnerID: pi.Owner.ID,
@@ -111,46 +111,40 @@ func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageI
}
var err error
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
- if err == packages_model.ErrDuplicatePackage {
- created = false
- } else {
+ if !errors.Is(err, packages_model.ErrDuplicatePackage) {
log.Error("Error inserting package: %v", err)
- return err
+ return nil, err
}
+ created = false
}
if created {
if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository, strings.ToLower(pi.Owner.LowerName+"/"+pi.Name)); err != nil {
log.Error("Error setting package property: %v", err)
- return err
+ return nil, err
}
}
pv := &packages_model.PackageVersion{
PackageID: p.ID,
CreatorID: pi.Owner.ID,
- Version: container_model.UploadVersion,
- LowerVersion: container_model.UploadVersion,
+ Version: container_module.UploadVersion,
+ LowerVersion: container_module.UploadVersion,
IsInternal: true,
MetadataJSON: "null",
}
if pv, err = packages_model.GetOrInsertVersion(ctx, pv); err != nil {
- if err != packages_model.ErrDuplicatePackageVersion {
+ if !errors.Is(err, packages_model.ErrDuplicatePackageVersion) {
log.Error("Error inserting package: %v", err)
- return err
+ return nil, err
}
}
-
- uploadVersion = pv
-
- return nil
+ return pv, nil
})
-
- return uploadVersion, err
}
func createFileForBlob(ctx context.Context, pv *packages_model.PackageVersion, pb *packages_model.PackageBlob) error {
- filename := strings.ToLower(fmt.Sprintf("sha256_%s", pb.HashSHA256))
+ filename := strings.ToLower("sha256_" + pb.HashSHA256)
pf := &packages_model.PackageFile{
VersionID: pv.ID,
@@ -161,7 +155,7 @@ func createFileForBlob(ctx context.Context, pv *packages_model.PackageVersion, p
}
var err error
if pf, err = packages_model.TryInsertFile(ctx, pf); err != nil {
- if err == packages_model.ErrDuplicatePackageFile {
+ if errors.Is(err, packages_model.ErrDuplicatePackageFile) {
return nil
}
log.Error("Error inserting package file: %v", err)
@@ -176,8 +170,8 @@ func createFileForBlob(ctx context.Context, pv *packages_model.PackageVersion, p
return nil
}
-func deleteBlob(ctx context.Context, ownerID int64, image, digest string) error {
- releaser, err := globallock.Lock(ctx, containerPkgName(ownerID, image))
+func deleteBlob(ctx context.Context, ownerID int64, image string, digest digest.Digest) error {
+ releaser, err := globallock.Lock(ctx, containerGlobalLockKey(ownerID, image, "blob"))
if err != nil {
return err
}
@@ -187,7 +181,7 @@ func deleteBlob(ctx context.Context, ownerID int64, image, digest string) error
pfds, err := container_model.GetContainerBlobs(ctx, &container_model.BlobSearchOptions{
OwnerID: ownerID,
Image: image,
- Digest: digest,
+ Digest: string(digest),
})
if err != nil {
return err
diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go
index 3a470ad685..d532f698ad 100644
--- a/routers/api/packages/container/container.go
+++ b/routers/api/packages/container/container.go
@@ -13,6 +13,7 @@ import (
"regexp"
"strconv"
"strings"
+ "sync"
auth_model "code.gitea.io/gitea/models/auth"
packages_model "code.gitea.io/gitea/models/packages"
@@ -21,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/optional"
packages_module "code.gitea.io/gitea/modules/packages"
container_module "code.gitea.io/gitea/modules/packages/container"
"code.gitea.io/gitea/modules/setting"
@@ -31,17 +33,21 @@ import (
packages_service "code.gitea.io/gitea/services/packages"
container_service "code.gitea.io/gitea/services/packages/container"
- digest "github.com/opencontainers/go-digest"
+ "github.com/opencontainers/go-digest"
)
// maximum size of a container manifest
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-manifests
const maxManifestSize = 10 * 1024 * 1024
-var (
- imageNamePattern = regexp.MustCompile(`\A[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)*\z`)
- referencePattern = regexp.MustCompile(`\A[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}\z`)
-)
+var globalVars = sync.OnceValue(func() (ret struct {
+ imageNamePattern, referencePattern *regexp.Regexp
+},
+) {
+ ret.imageNamePattern = regexp.MustCompile(`\A[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)*\z`)
+ ret.referencePattern = regexp.MustCompile(`\A[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}\z`)
+ return ret
+})
type containerHeaders struct {
Status int
@@ -50,7 +56,7 @@ type containerHeaders struct {
Range string
Location string
ContentType string
- ContentLength int64
+ ContentLength optional.Option[int64]
}
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#legacy-docker-support-http-headers
@@ -64,8 +70,8 @@ func setResponseHeaders(resp http.ResponseWriter, h *containerHeaders) {
if h.ContentType != "" {
resp.Header().Set("Content-Type", h.ContentType)
}
- if h.ContentLength != 0 {
- resp.Header().Set("Content-Length", strconv.FormatInt(h.ContentLength, 10))
+ if h.ContentLength.Has() {
+ resp.Header().Set("Content-Length", strconv.FormatInt(h.ContentLength.Value(), 10))
}
if h.UploadUUID != "" {
resp.Header().Set("Docker-Upload-Uuid", h.UploadUUID)
@@ -83,9 +89,7 @@ func jsonResponse(ctx *context.Context, status int, obj any) {
Status: status,
ContentType: "application/json",
})
- if err := json.NewEncoder(ctx.Resp).Encode(obj); err != nil {
- log.Error("JSON encode: %v", err)
- }
+ _ = json.NewEncoder(ctx.Resp).Encode(obj) // ignore network errors
}
func apiError(ctx *context.Context, status int, err error) {
@@ -126,14 +130,14 @@ func apiUnauthorizedError(ctx *context.Context) {
// ReqContainerAccess is a middleware which checks the current user valid (real user or ghost if anonymous access is enabled)
func ReqContainerAccess(ctx *context.Context) {
- if ctx.Doer == nil || (setting.Service.RequireSignInView && ctx.Doer.IsGhost()) {
+ if ctx.Doer == nil || (setting.Service.RequireSignInViewStrict && ctx.Doer.IsGhost()) {
apiUnauthorizedError(ctx)
}
}
// VerifyImageName is a middleware which checks if the image name is allowed
func VerifyImageName(ctx *context.Context) {
- if !imageNamePattern.MatchString(ctx.PathParam("image")) {
+ if !globalVars().imageNamePattern.MatchString(ctx.PathParam("image")) {
apiErrorDefined(ctx, errNameInvalid)
}
}
@@ -152,7 +156,7 @@ func Authenticate(ctx *context.Context) {
u := ctx.Doer
packageScope := auth_service.GetAccessScope(ctx.Data)
if u == nil {
- if setting.Service.RequireSignInView {
+ if setting.Service.RequireSignInViewStrict {
apiUnauthorizedError(ctx)
return
}
@@ -215,7 +219,7 @@ func GetRepositoryList(ctx *context.Context) {
if len(repositories) == n {
v := url.Values{}
if n > 0 {
- v.Add("n", strconv.Itoa(n))
+ v.Add("n", strconv.Itoa(n)) // FIXME: "n" can't be zero here, the logic is inconsistent with GetTagsList
}
v.Add("last", repositories[len(repositories)-1])
@@ -230,7 +234,7 @@ func GetRepositoryList(ctx *context.Context) {
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#mounting-a-blob-from-another-repository
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#single-post
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-a-blob-in-chunks
-func InitiateUploadBlob(ctx *context.Context) {
+func PostBlobsUploads(ctx *context.Context) {
image := ctx.PathParam("image")
mount := ctx.FormTrim("mount")
@@ -312,19 +316,19 @@ func InitiateUploadBlob(ctx *context.Context) {
setResponseHeaders(ctx.Resp, &containerHeaders{
Location: fmt.Sprintf("/v2/%s/%s/blobs/uploads/%s", ctx.Package.Owner.LowerName, image, upload.ID),
- Range: "0-0",
UploadUUID: upload.ID,
Status: http.StatusAccepted,
})
}
-// https://docs.docker.com/registry/spec/api/#get-blob-upload
-func GetUploadBlob(ctx *context.Context) {
+// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-a-blob-in-chunks
+func GetBlobsUpload(ctx *context.Context) {
+ image := ctx.PathParam("image")
uuid := ctx.PathParam("uuid")
upload, err := packages_model.GetBlobUploadByID(ctx, uuid)
if err != nil {
- if err == packages_model.ErrPackageBlobUploadNotExist {
+ if errors.Is(err, packages_model.ErrPackageBlobUploadNotExist) {
apiErrorDefined(ctx, errBlobUploadUnknown)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -332,20 +336,26 @@ func GetUploadBlob(ctx *context.Context) {
return
}
- setResponseHeaders(ctx.Resp, &containerHeaders{
- Range: fmt.Sprintf("0-%d", upload.BytesReceived),
+ // FIXME: undefined behavior when the uploaded content is empty: https://github.com/opencontainers/distribution-spec/issues/578
+ respHeaders := &containerHeaders{
+ Location: fmt.Sprintf("/v2/%s/%s/blobs/uploads/%s", ctx.Package.Owner.LowerName, image, upload.ID),
UploadUUID: upload.ID,
Status: http.StatusNoContent,
- })
+ }
+ if upload.BytesReceived > 0 {
+ respHeaders.Range = fmt.Sprintf("0-%d", upload.BytesReceived-1)
+ }
+ setResponseHeaders(ctx.Resp, respHeaders)
}
+// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#single-post
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-a-blob-in-chunks
-func UploadBlob(ctx *context.Context) {
+func PatchBlobsUpload(ctx *context.Context) {
image := ctx.PathParam("image")
uploader, err := container_service.NewBlobUploader(ctx, ctx.PathParam("uuid"))
if err != nil {
- if err == packages_model.ErrPackageBlobUploadNotExist {
+ if errors.Is(err, packages_model.ErrPackageBlobUploadNotExist) {
apiErrorDefined(ctx, errBlobUploadUnknown)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -376,16 +386,19 @@ func UploadBlob(ctx *context.Context) {
return
}
- setResponseHeaders(ctx.Resp, &containerHeaders{
+ respHeaders := &containerHeaders{
Location: fmt.Sprintf("/v2/%s/%s/blobs/uploads/%s", ctx.Package.Owner.LowerName, image, uploader.ID),
- Range: fmt.Sprintf("0-%d", uploader.Size()-1),
UploadUUID: uploader.ID,
Status: http.StatusAccepted,
- })
+ }
+ if uploader.Size() > 0 {
+ respHeaders.Range = fmt.Sprintf("0-%d", uploader.Size()-1)
+ }
+ setResponseHeaders(ctx.Resp, respHeaders)
}
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-a-blob-in-chunks
-func EndUploadBlob(ctx *context.Context) {
+func PutBlobsUpload(ctx *context.Context) {
image := ctx.PathParam("image")
digest := ctx.FormTrim("digest")
@@ -396,19 +409,14 @@ func EndUploadBlob(ctx *context.Context) {
uploader, err := container_service.NewBlobUploader(ctx, ctx.PathParam("uuid"))
if err != nil {
- if err == packages_model.ErrPackageBlobUploadNotExist {
+ if errors.Is(err, packages_model.ErrPackageBlobUploadNotExist) {
apiErrorDefined(ctx, errBlobUploadUnknown)
} else {
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
- doClose := true
- defer func() {
- if doClose {
- uploader.Close()
- }
- }()
+ defer uploader.Close()
if ctx.Req.Body != nil {
if err := uploader.Append(ctx, ctx.Req.Body); err != nil {
@@ -441,11 +449,10 @@ func EndUploadBlob(ctx *context.Context) {
return
}
- if err := uploader.Close(); err != nil {
- apiError(ctx, http.StatusInternalServerError, err)
- return
- }
- doClose = false
+ // There was a strange bug: the "Close" fails with error "close .../tmp/package-upload/....: file already closed"
+ // AFAIK there should be no other "Close" call to the uploader between NewBlobUploader and this line.
+ // At least it's safe to call Close twice, so ignore the error.
+ _ = uploader.Close()
if err := container_service.RemoveBlobUploadByID(ctx, uploader.ID); err != nil {
apiError(ctx, http.StatusInternalServerError, err)
@@ -460,12 +467,12 @@ func EndUploadBlob(ctx *context.Context) {
}
// https://docs.docker.com/registry/spec/api/#delete-blob-upload
-func CancelUploadBlob(ctx *context.Context) {
+func DeleteBlobsUpload(ctx *context.Context) {
uuid := ctx.PathParam("uuid")
_, err := packages_model.GetBlobUploadByID(ctx, uuid)
if err != nil {
- if err == packages_model.ErrPackageBlobUploadNotExist {
+ if errors.Is(err, packages_model.ErrPackageBlobUploadNotExist) {
apiErrorDefined(ctx, errBlobUploadUnknown)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -484,16 +491,15 @@ func CancelUploadBlob(ctx *context.Context) {
}
func getBlobFromContext(ctx *context.Context) (*packages_model.PackageFileDescriptor, error) {
- d := ctx.PathParam("digest")
-
- if digest.Digest(d).Validate() != nil {
+ d := digest.Digest(ctx.PathParam("digest"))
+ if d.Validate() != nil {
return nil, container_model.ErrContainerBlobNotExist
}
return workaroundGetContainerBlob(ctx, &container_model.BlobSearchOptions{
OwnerID: ctx.Package.Owner.ID,
Image: ctx.PathParam("image"),
- Digest: d,
+ Digest: string(d),
})
}
@@ -501,7 +507,7 @@ func getBlobFromContext(ctx *context.Context) (*packages_model.PackageFileDescri
func HeadBlob(ctx *context.Context) {
blob, err := getBlobFromContext(ctx)
if err != nil {
- if err == container_model.ErrContainerBlobNotExist {
+ if errors.Is(err, container_model.ErrContainerBlobNotExist) {
apiErrorDefined(ctx, errBlobUnknown)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -511,7 +517,7 @@ func HeadBlob(ctx *context.Context) {
setResponseHeaders(ctx.Resp, &containerHeaders{
ContentDigest: blob.Properties.GetByName(container_module.PropertyDigest),
- ContentLength: blob.Blob.Size,
+ ContentLength: optional.Some(blob.Blob.Size),
Status: http.StatusOK,
})
}
@@ -520,7 +526,7 @@ func HeadBlob(ctx *context.Context) {
func GetBlob(ctx *context.Context) {
blob, err := getBlobFromContext(ctx)
if err != nil {
- if err == container_model.ErrContainerBlobNotExist {
+ if errors.Is(err, container_model.ErrContainerBlobNotExist) {
apiErrorDefined(ctx, errBlobUnknown)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -533,9 +539,8 @@ func GetBlob(ctx *context.Context) {
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#deleting-blobs
func DeleteBlob(ctx *context.Context) {
- d := ctx.PathParam("digest")
-
- if digest.Digest(d).Validate() != nil {
+ d := digest.Digest(ctx.PathParam("digest"))
+ if d.Validate() != nil {
apiErrorDefined(ctx, errBlobUnknown)
return
}
@@ -551,7 +556,7 @@ func DeleteBlob(ctx *context.Context) {
}
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-manifests
-func UploadManifest(ctx *context.Context) {
+func PutManifest(ctx *context.Context) {
reference := ctx.PathParam("reference")
mci := &manifestCreationInfo{
@@ -563,7 +568,7 @@ func UploadManifest(ctx *context.Context) {
IsTagged: digest.Digest(reference).Validate() != nil,
}
- if mci.IsTagged && !referencePattern.MatchString(reference) {
+ if mci.IsTagged && !globalVars().referencePattern.MatchString(reference) {
apiErrorDefined(ctx, errManifestInvalid.WithMessage("Tag is invalid"))
return
}
@@ -607,18 +612,18 @@ func UploadManifest(ctx *context.Context) {
}
func getBlobSearchOptionsFromContext(ctx *context.Context) (*container_model.BlobSearchOptions, error) {
- reference := ctx.PathParam("reference")
-
opts := &container_model.BlobSearchOptions{
OwnerID: ctx.Package.Owner.ID,
Image: ctx.PathParam("image"),
IsManifest: true,
}
- if digest.Digest(reference).Validate() == nil {
- opts.Digest = reference
- } else if referencePattern.MatchString(reference) {
+ reference := ctx.PathParam("reference")
+ if d := digest.Digest(reference); d.Validate() == nil {
+ opts.Digest = string(d)
+ } else if globalVars().referencePattern.MatchString(reference) {
opts.Tag = reference
+ opts.OnlyLead = true
} else {
return nil, container_model.ErrContainerBlobNotExist
}
@@ -639,7 +644,7 @@ func getManifestFromContext(ctx *context.Context) (*packages_model.PackageFileDe
func HeadManifest(ctx *context.Context) {
manifest, err := getManifestFromContext(ctx)
if err != nil {
- if err == container_model.ErrContainerBlobNotExist {
+ if errors.Is(err, container_model.ErrContainerBlobNotExist) {
apiErrorDefined(ctx, errManifestUnknown)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -650,7 +655,7 @@ func HeadManifest(ctx *context.Context) {
setResponseHeaders(ctx.Resp, &containerHeaders{
ContentDigest: manifest.Properties.GetByName(container_module.PropertyDigest),
ContentType: manifest.Properties.GetByName(container_module.PropertyMediaType),
- ContentLength: manifest.Blob.Size,
+ ContentLength: optional.Some(manifest.Blob.Size),
Status: http.StatusOK,
})
}
@@ -659,7 +664,7 @@ func HeadManifest(ctx *context.Context) {
func GetManifest(ctx *context.Context) {
manifest, err := getManifestFromContext(ctx)
if err != nil {
- if err == container_model.ErrContainerBlobNotExist {
+ if errors.Is(err, container_model.ErrContainerBlobNotExist) {
apiErrorDefined(ctx, errManifestUnknown)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -705,7 +710,7 @@ func DeleteManifest(ctx *context.Context) {
func serveBlob(ctx *context.Context, pfd *packages_model.PackageFileDescriptor) {
serveDirectReqParams := make(url.Values)
serveDirectReqParams.Set("response-content-type", pfd.Properties.GetByName(container_module.PropertyMediaType))
- s, u, _, err := packages_service.GetPackageBlobStream(ctx, pfd.File, pfd.Blob, serveDirectReqParams)
+ s, u, _, err := packages_service.OpenBlobForDownload(ctx, pfd.File, pfd.Blob, serveDirectReqParams)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
@@ -714,14 +719,14 @@ func serveBlob(ctx *context.Context, pfd *packages_model.PackageFileDescriptor)
headers := &containerHeaders{
ContentDigest: pfd.Properties.GetByName(container_module.PropertyDigest),
ContentType: pfd.Properties.GetByName(container_module.PropertyMediaType),
- ContentLength: pfd.Blob.Size,
+ ContentLength: optional.Some(pfd.Blob.Size),
Status: http.StatusOK,
}
if u != nil {
headers.Status = http.StatusTemporaryRedirect
headers.Location = u.String()
-
+ headers.ContentLength = optional.None[int64]() // do not set Content-Length for redirect responses
setResponseHeaders(ctx.Resp, headers)
return
}
@@ -735,11 +740,11 @@ func serveBlob(ctx *context.Context, pfd *packages_model.PackageFileDescriptor)
}
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#content-discovery
-func GetTagList(ctx *context.Context) {
+func GetTagsList(ctx *context.Context) {
image := ctx.PathParam("image")
if _, err := packages_model.GetPackageByName(ctx, ctx.Package.Owner.ID, packages_model.TypeContainer, image); err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiErrorDefined(ctx, errNameUnknown)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -780,7 +785,8 @@ func GetTagList(ctx *context.Context) {
})
}
-// FIXME: Workaround to be removed in v1.20
+// FIXME: Workaround to be removed in v1.20.
+// Update maybe we should never really remote it, as long as there is legacy data?
// https://github.com/go-gitea/gitea/issues/19586
func workaroundGetContainerBlob(ctx *context.Context, opts *container_model.BlobSearchOptions) (*packages_model.PackageFileDescriptor, error) {
blob, err := container_model.GetContainerBlob(ctx, opts)
diff --git a/routers/api/packages/container/manifest.go b/routers/api/packages/container/manifest.go
index 4a79a58f51..de40215aa7 100644
--- a/routers/api/packages/container/manifest.go
+++ b/routers/api/packages/container/manifest.go
@@ -10,11 +10,13 @@ import (
"io"
"os"
"strings"
+ "time"
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
container_model "code.gitea.io/gitea/models/packages/container"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/globallock"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
@@ -22,23 +24,12 @@ import (
"code.gitea.io/gitea/modules/util"
notify_service "code.gitea.io/gitea/services/notify"
packages_service "code.gitea.io/gitea/services/packages"
+ container_service "code.gitea.io/gitea/services/packages/container"
- digest "github.com/opencontainers/go-digest"
+ "github.com/opencontainers/go-digest"
oci "github.com/opencontainers/image-spec/specs-go/v1"
)
-func isValidMediaType(mt string) bool {
- return strings.HasPrefix(mt, "application/vnd.docker.") || strings.HasPrefix(mt, "application/vnd.oci.")
-}
-
-func isImageManifestMediaType(mt string) bool {
- return strings.EqualFold(mt, oci.MediaTypeImageManifest) || strings.EqualFold(mt, "application/vnd.docker.distribution.manifest.v2+json")
-}
-
-func isImageIndexMediaType(mt string) bool {
- return strings.EqualFold(mt, oci.MediaTypeImageIndex) || strings.EqualFold(mt, "application/vnd.docker.distribution.manifest.list.v2+json")
-}
-
// manifestCreationInfo describes a manifest to create
type manifestCreationInfo struct {
MediaType string
@@ -55,71 +46,71 @@ func processManifest(ctx context.Context, mci *manifestCreationInfo, buf *packag
if err := json.NewDecoder(buf).Decode(&index); err != nil {
return "", err
}
-
if index.SchemaVersion != 2 {
return "", errUnsupported.WithMessage("Schema version is not supported")
}
-
if _, err := buf.Seek(0, io.SeekStart); err != nil {
return "", err
}
- if !isValidMediaType(mci.MediaType) {
+ if !container_module.IsMediaTypeValid(mci.MediaType) {
mci.MediaType = index.MediaType
- if !isValidMediaType(mci.MediaType) {
+ if !container_module.IsMediaTypeValid(mci.MediaType) {
return "", errManifestInvalid.WithMessage("MediaType not recognized")
}
}
- if isImageManifestMediaType(mci.MediaType) {
- return processImageManifest(ctx, mci, buf)
- } else if isImageIndexMediaType(mci.MediaType) {
- return processImageManifestIndex(ctx, mci, buf)
+ // .../container/manifest.go:453:createManifestBlob() [E] Error inserting package blob: Error 1062 (23000): Duplicate entry '..........' for key 'package_blob.UQE_package_blob_md5'
+ releaser, err := globallock.Lock(ctx, containerGlobalLockKey(mci.Owner.ID, mci.Image, "manifest"))
+ if err != nil {
+ return "", err
+ }
+ defer releaser()
+
+ if container_module.IsMediaTypeImageManifest(mci.MediaType) {
+ return processOciImageManifest(ctx, mci, buf)
+ } else if container_module.IsMediaTypeImageIndex(mci.MediaType) {
+ return processOciImageIndex(ctx, mci, buf)
}
return "", errManifestInvalid
}
-func processImageManifest(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) {
- manifestDigest := ""
-
- err := func() error {
- var manifest oci.Manifest
- if err := json.NewDecoder(buf).Decode(&manifest); err != nil {
- return err
- }
-
- if _, err := buf.Seek(0, io.SeekStart); err != nil {
- return err
- }
-
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
-
- configDescriptor, err := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
- OwnerID: mci.Owner.ID,
- Image: mci.Image,
- Digest: string(manifest.Config.Digest),
- })
- if err != nil {
- return err
- }
+type processManifestTxRet struct {
+ pv *packages_model.PackageVersion
+ pb *packages_model.PackageBlob
+ created bool
+ digest string
+}
- configReader, err := packages_module.NewContentStore().Get(packages_module.BlobHash256Key(configDescriptor.Blob.HashSHA256))
- if err != nil {
- return err
+func handleCreateManifestResult(ctx context.Context, err error, mci *manifestCreationInfo, contentStore *packages_module.ContentStore, txRet *processManifestTxRet) (string, error) {
+ if err != nil && txRet.created && txRet.pb != nil {
+ if err := contentStore.Delete(packages_module.BlobHash256Key(txRet.pb.HashSHA256)); err != nil {
+ log.Error("Error deleting package blob from content store: %v", err)
}
- defer configReader.Close()
+ return "", err
+ }
+ pd, err := packages_model.GetPackageDescriptor(ctx, txRet.pv)
+ if err != nil {
+ log.Error("Error getting package descriptor: %v", err) // ignore this error
+ } else {
+ notify_service.PackageCreate(ctx, mci.Creator, pd)
+ }
+ return txRet.digest, nil
+}
- metadata, err := container_module.ParseImageConfig(manifest.Config.MediaType, configReader)
- if err != nil {
- return err
- }
+func processOciImageManifest(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (manifestDigest string, errRet error) {
+ manifest, configDescriptor, metadata, err := container_service.ParseManifestMetadata(ctx, buf, mci.Owner.ID, mci.Image)
+ if err != nil {
+ return "", err
+ }
+ if _, err = buf.Seek(0, io.SeekStart); err != nil {
+ return "", err
+ }
+ contentStore := packages_module.NewContentStore()
+ var txRet processManifestTxRet
+ err = db.WithTx(ctx, func(ctx context.Context) (err error) {
blobReferences := make([]*blobReference, 0, 1+len(manifest.Layers))
-
blobReferences = append(blobReferences, &blobReference{
Digest: manifest.Config.Digest,
MediaType: manifest.Config.MediaType,
@@ -150,78 +141,43 @@ func processImageManifest(ctx context.Context, mci *manifestCreationInfo, buf *p
return err
}
- uploadVersion, err := packages_model.GetInternalVersionByNameAndVersion(ctx, mci.Owner.ID, packages_model.TypeContainer, mci.Image, container_model.UploadVersion)
- if err != nil && err != packages_model.ErrPackageNotExist {
+ uploadVersion, err := packages_model.GetInternalVersionByNameAndVersion(ctx, mci.Owner.ID, packages_model.TypeContainer, mci.Image, container_module.UploadVersion)
+ if err != nil && !errors.Is(err, packages_model.ErrPackageNotExist) {
return err
}
for _, ref := range blobReferences {
- if err := createFileFromBlobReference(ctx, pv, uploadVersion, ref); err != nil {
+ if _, err = createFileFromBlobReference(ctx, pv, uploadVersion, ref); err != nil {
return err
}
}
+ txRet.pv = pv
+ txRet.pb, txRet.created, txRet.digest, err = createManifestBlob(ctx, contentStore, mci, pv, buf)
+ return err
+ })
- pb, created, digest, err := createManifestBlob(ctx, mci, pv, buf)
- removeBlob := false
- defer func() {
- if removeBlob {
- contentStore := packages_module.NewContentStore()
- if err := contentStore.Delete(packages_module.BlobHash256Key(pb.HashSHA256)); err != nil {
- log.Error("Error deleting package blob from content store: %v", err)
- }
- }
- }()
- if err != nil {
- removeBlob = created
- return err
- }
-
- if err := committer.Commit(); err != nil {
- removeBlob = created
- return err
- }
-
- if err := notifyPackageCreate(ctx, mci.Creator, pv); err != nil {
- return err
- }
-
- manifestDigest = digest
+ return handleCreateManifestResult(ctx, err, mci, contentStore, &txRet)
+}
- return nil
- }()
- if err != nil {
+func processOciImageIndex(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (manifestDigest string, errRet error) {
+ var index oci.Index
+ if err := json.NewDecoder(buf).Decode(&index); err != nil {
+ return "", err
+ }
+ if _, err := buf.Seek(0, io.SeekStart); err != nil {
return "", err
}
- return manifestDigest, nil
-}
-
-func processImageManifestIndex(ctx context.Context, mci *manifestCreationInfo, buf *packages_module.HashedBuffer) (string, error) {
- manifestDigest := ""
-
- err := func() error {
- var index oci.Index
- if err := json.NewDecoder(buf).Decode(&index); err != nil {
- return err
- }
-
- if _, err := buf.Seek(0, io.SeekStart); err != nil {
- return err
- }
-
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
-
+ contentStore := packages_module.NewContentStore()
+ var txRet processManifestTxRet
+ err := db.WithTx(ctx, func(ctx context.Context) (err error) {
metadata := &container_module.Metadata{
Type: container_module.TypeOCI,
Manifests: make([]*container_module.Manifest, 0, len(index.Manifests)),
}
for _, manifest := range index.Manifests {
- if !isImageManifestMediaType(manifest.MediaType) {
+ if !container_module.IsMediaTypeImageManifest(manifest.MediaType) {
return errManifestInvalid
}
@@ -240,7 +196,7 @@ func processImageManifestIndex(ctx context.Context, mci *manifestCreationInfo, b
IsManifest: true,
})
if err != nil {
- if err == container_model.ErrContainerBlobNotExist {
+ if errors.Is(err, container_model.ErrContainerBlobNotExist) {
return errManifestBlobUnknown
}
return err
@@ -265,50 +221,12 @@ func processImageManifestIndex(ctx context.Context, mci *manifestCreationInfo, b
return err
}
- pb, created, digest, err := createManifestBlob(ctx, mci, pv, buf)
- removeBlob := false
- defer func() {
- if removeBlob {
- contentStore := packages_module.NewContentStore()
- if err := contentStore.Delete(packages_module.BlobHash256Key(pb.HashSHA256)); err != nil {
- log.Error("Error deleting package blob from content store: %v", err)
- }
- }
- }()
- if err != nil {
- removeBlob = created
- return err
- }
-
- if err := committer.Commit(); err != nil {
- removeBlob = created
- return err
- }
-
- if err := notifyPackageCreate(ctx, mci.Creator, pv); err != nil {
- return err
- }
-
- manifestDigest = digest
-
- return nil
- }()
- if err != nil {
- return "", err
- }
-
- return manifestDigest, nil
-}
-
-func notifyPackageCreate(ctx context.Context, doer *user_model.User, pv *packages_model.PackageVersion) error {
- pd, err := packages_model.GetPackageDescriptor(ctx, pv)
- if err != nil {
+ txRet.pv = pv
+ txRet.pb, txRet.created, txRet.digest, err = createManifestBlob(ctx, contentStore, mci, pv, buf)
return err
- }
-
- notify_service.PackageCreate(ctx, doer, pd)
+ })
- return nil
+ return handleCreateManifestResult(ctx, err, mci, contentStore, &txRet)
}
func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, metadata *container_module.Metadata) (*packages_model.PackageVersion, error) {
@@ -321,12 +239,11 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
}
var err error
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
- if err == packages_model.ErrDuplicatePackage {
- created = false
- } else {
+ if !errors.Is(err, packages_model.ErrDuplicatePackage) {
log.Error("Error inserting package: %v", err)
return nil, err
}
+ created = false
}
if created {
@@ -350,24 +267,33 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
LowerVersion: strings.ToLower(mci.Reference),
MetadataJSON: string(metadataJSON),
}
- var pv *packages_model.PackageVersion
- if pv, err = packages_model.GetOrInsertVersion(ctx, _pv); err != nil {
- if err == packages_model.ErrDuplicatePackageVersion {
- if err := packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil {
- return nil, err
- }
-
- // keep download count on overwrite
- _pv.DownloadCount = pv.DownloadCount
-
- if pv, err = packages_model.GetOrInsertVersion(ctx, _pv); err != nil {
- log.Error("Error inserting package: %v", err)
- return nil, err
- }
- } else {
+ pv, err := packages_model.GetOrInsertVersion(ctx, _pv)
+ if err != nil {
+ if !errors.Is(err, packages_model.ErrDuplicatePackageVersion) {
log.Error("Error inserting package: %v", err)
return nil, err
}
+
+ if container_module.IsMediaTypeImageIndex(mci.MediaType) {
+ if pv.CreatedUnix.AsTime().Before(time.Now().Add(-24 * time.Hour)) {
+ if err = packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil {
+ return nil, err
+ }
+ // keep download count on overwriting
+ _pv.DownloadCount = pv.DownloadCount
+ if pv, err = packages_model.GetOrInsertVersion(ctx, _pv); err != nil {
+ if !errors.Is(err, packages_model.ErrDuplicatePackageVersion) {
+ log.Error("Error inserting package: %v", err)
+ return nil, err
+ }
+ }
+ } else {
+ err = packages_model.UpdateVersion(ctx, &packages_model.PackageVersion{ID: pv.ID, MetadataJSON: _pv.MetadataJSON})
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
}
if err := packages_service.CheckCountQuotaExceeded(ctx, mci.Creator, mci.Owner); err != nil {
@@ -375,14 +301,20 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
}
if mci.IsTagged {
- if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged, ""); err != nil {
- log.Error("Error setting package version property: %v", err)
+ if err = packages_model.InsertOrUpdateProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged, ""); err != nil {
+ return nil, err
+ }
+ } else {
+ if err = packages_model.DeletePropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged); err != nil {
return nil, err
}
}
+
+ if err = packages_model.DeletePropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference); err != nil {
+ return nil, err
+ }
for _, manifest := range metadata.Manifests {
- if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, manifest.Digest); err != nil {
- log.Error("Error setting package version property: %v", err)
+ if _, err = packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, manifest.Digest); err != nil {
return nil, err
}
}
@@ -399,30 +331,31 @@ type blobReference struct {
IsLead bool
}
-func createFileFromBlobReference(ctx context.Context, pv, uploadVersion *packages_model.PackageVersion, ref *blobReference) error {
+func createFileFromBlobReference(ctx context.Context, pv, uploadVersion *packages_model.PackageVersion, ref *blobReference) (*packages_model.PackageFile, error) {
if ref.File.Blob.Size != ref.ExpectedSize {
- return errSizeInvalid
+ return nil, errSizeInvalid
}
if ref.Name == "" {
- ref.Name = strings.ToLower(fmt.Sprintf("sha256_%s", ref.File.Blob.HashSHA256))
+ ref.Name = strings.ToLower("sha256_" + ref.File.Blob.HashSHA256)
}
pf := &packages_model.PackageFile{
- VersionID: pv.ID,
- BlobID: ref.File.Blob.ID,
- Name: ref.Name,
- LowerName: ref.Name,
- IsLead: ref.IsLead,
+ VersionID: pv.ID,
+ BlobID: ref.File.Blob.ID,
+ Name: ref.Name,
+ LowerName: ref.Name,
+ CompositeKey: string(ref.Digest),
+ IsLead: ref.IsLead,
}
var err error
if pf, err = packages_model.TryInsertFile(ctx, pf); err != nil {
- if err == packages_model.ErrDuplicatePackageFile {
+ if errors.Is(err, packages_model.ErrDuplicatePackageFile) {
// Skip this blob because the manifest contains the same filesystem layer multiple times.
- return nil
+ return pf, nil
}
log.Error("Error inserting package file: %v", err)
- return err
+ return nil, err
}
props := map[string]string{
@@ -432,21 +365,21 @@ func createFileFromBlobReference(ctx context.Context, pv, uploadVersion *package
for name, value := range props {
if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeFile, pf.ID, name, value); err != nil {
log.Error("Error setting package file property: %v", err)
- return err
+ return nil, err
}
}
- // Remove the file from the blob upload version
+ // Remove the ref file (old file) from the blob upload version
if uploadVersion != nil && ref.File.File != nil && uploadVersion.ID == ref.File.File.VersionID {
if err := packages_service.DeletePackageFile(ctx, ref.File.File); err != nil {
- return err
+ return nil, err
}
}
- return nil
+ return pf, nil
}
-func createManifestBlob(ctx context.Context, mci *manifestCreationInfo, pv *packages_model.PackageVersion, buf *packages_module.HashedBuffer) (*packages_model.PackageBlob, bool, string, error) {
+func createManifestBlob(ctx context.Context, contentStore *packages_module.ContentStore, mci *manifestCreationInfo, pv *packages_model.PackageVersion, buf *packages_module.HashedBuffer) (_ *packages_model.PackageBlob, created bool, manifestDigest string, _ error) {
pb, exists, err := packages_model.GetOrInsertBlob(ctx, packages_service.NewPackageBlob(buf))
if err != nil {
log.Error("Error inserting package blob: %v", err)
@@ -455,29 +388,48 @@ func createManifestBlob(ctx context.Context, mci *manifestCreationInfo, pv *pack
// FIXME: Workaround to be removed in v1.20
// https://github.com/go-gitea/gitea/issues/19586
if exists {
- err = packages_module.NewContentStore().Has(packages_module.BlobHash256Key(pb.HashSHA256))
+ err = contentStore.Has(packages_module.BlobHash256Key(pb.HashSHA256))
if err != nil && (errors.Is(err, util.ErrNotExist) || errors.Is(err, os.ErrNotExist)) {
log.Debug("Package registry inconsistent: blob %s does not exist on file system", pb.HashSHA256)
exists = false
}
}
if !exists {
- contentStore := packages_module.NewContentStore()
if err := contentStore.Save(packages_module.BlobHash256Key(pb.HashSHA256), buf, buf.Size()); err != nil {
log.Error("Error saving package blob in content store: %v", err)
return nil, false, "", err
}
}
- manifestDigest := digestFromHashSummer(buf)
- err = createFileFromBlobReference(ctx, pv, nil, &blobReference{
+ manifestDigest = digestFromHashSummer(buf)
+ pf, err := createFileFromBlobReference(ctx, pv, nil, &blobReference{
Digest: digest.Digest(manifestDigest),
MediaType: mci.MediaType,
- Name: container_model.ManifestFilename,
+ Name: container_module.ManifestFilename,
File: &packages_model.PackageFileDescriptor{Blob: pb},
ExpectedSize: pb.Size,
IsLead: true,
})
+ if err != nil {
+ return nil, false, "", err
+ }
+ oldManifestFiles, _, err := packages_model.SearchFiles(ctx, &packages_model.PackageFileSearchOptions{
+ OwnerID: mci.Owner.ID,
+ PackageType: packages_model.TypeContainer,
+ VersionID: pv.ID,
+ Query: container_module.ManifestFilename,
+ })
+ if err != nil {
+ return nil, false, "", err
+ }
+ for _, oldManifestFile := range oldManifestFiles {
+ if oldManifestFile.ID != pf.ID && oldManifestFile.IsLead {
+ err = packages_model.UpdateFile(ctx, &packages_model.PackageFile{ID: oldManifestFile.ID, IsLead: false}, []string{"is_lead"})
+ if err != nil {
+ return nil, false, "", err
+ }
+ }
+ }
return pb, !exists, manifestDigest, err
}
diff --git a/routers/api/packages/cran/cran.go b/routers/api/packages/cran/cran.go
index 8a20072cb6..732acd215f 100644
--- a/routers/api/packages/cran/cran.go
+++ b/routers/api/packages/cran/cran.go
@@ -250,7 +250,7 @@ func downloadPackageFile(ctx *context.Context, opts *cran_model.SearchOptions) {
return
}
- s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
+ s, u, _, err := packages_service.OpenFileForDownload(ctx, pf)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
diff --git a/routers/api/packages/debian/debian.go b/routers/api/packages/debian/debian.go
index 162122ccbd..346f71fa5d 100644
--- a/routers/api/packages/debian/debian.go
+++ b/routers/api/packages/debian/debian.go
@@ -59,7 +59,7 @@ func GetRepositoryFile(ctx *context.Context) {
key += "|" + component + "|" + architecture
}
- s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageVersion(
ctx,
pv,
&packages_service.PackageFileInfo{
@@ -68,7 +68,7 @@ func GetRepositoryFile(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
@@ -106,7 +106,7 @@ func GetRepositoryFileByHash(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
+ s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0])
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
@@ -210,7 +210,7 @@ func DownloadPackageFile(ctx *context.Context) {
name := ctx.PathParam("name")
version := ctx.PathParam("version")
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
diff --git a/routers/api/packages/generic/generic.go b/routers/api/packages/generic/generic.go
index 868caf9cf0..db7aeace50 100644
--- a/routers/api/packages/generic/generic.go
+++ b/routers/api/packages/generic/generic.go
@@ -31,7 +31,7 @@ func apiError(ctx *context.Context, status int, obj any) {
// DownloadPackageFile serves the specific generic package.
func DownloadPackageFile(ctx *context.Context) {
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
@@ -44,7 +44,7 @@ func DownloadPackageFile(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -155,7 +155,7 @@ func DeletePackage(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -182,7 +182,7 @@ func DeletePackageFile(ctx *context.Context) {
return pv, pf, nil
}()
if err != nil {
- if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
diff --git a/routers/api/packages/goproxy/goproxy.go b/routers/api/packages/goproxy/goproxy.go
index bde29df739..89ec86bce9 100644
--- a/routers/api/packages/goproxy/goproxy.go
+++ b/routers/api/packages/goproxy/goproxy.go
@@ -106,7 +106,7 @@ func DownloadPackageFile(ctx *context.Context) {
return
}
- s, u, _, err := packages_service.GetPackageFileStream(ctx, pfs[0])
+ s, u, _, err := packages_service.OpenFileForDownload(ctx, pfs[0])
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
diff --git a/routers/api/packages/helm/helm.go b/routers/api/packages/helm/helm.go
index cb30a20074..39c34f4da4 100644
--- a/routers/api/packages/helm/helm.go
+++ b/routers/api/packages/helm/helm.go
@@ -122,7 +122,7 @@ func DownloadPackageFile(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageVersion(
ctx,
pvs[0],
&packages_service.PackageFileInfo{
@@ -130,7 +130,7 @@ func DownloadPackageFile(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
diff --git a/routers/api/packages/maven/api.go b/routers/api/packages/maven/api.go
index 167fe42b56..ec6b9cfb0e 100644
--- a/routers/api/packages/maven/api.go
+++ b/routers/api/packages/maven/api.go
@@ -8,7 +8,6 @@ import (
"strings"
packages_model "code.gitea.io/gitea/models/packages"
- maven_module "code.gitea.io/gitea/modules/packages/maven"
)
// MetadataResponse https://maven.apache.org/ref/3.2.5/maven-repository-metadata/repository-metadata.html
@@ -22,7 +21,7 @@ type MetadataResponse struct {
}
// pds is expected to be sorted ascending by CreatedUnix
-func createMetadataResponse(pds []*packages_model.PackageDescriptor) *MetadataResponse {
+func createMetadataResponse(pds []*packages_model.PackageDescriptor, groupID, artifactID string) *MetadataResponse {
var release *packages_model.PackageDescriptor
versions := make([]string, 0, len(pds))
@@ -35,11 +34,9 @@ func createMetadataResponse(pds []*packages_model.PackageDescriptor) *MetadataRe
latest := pds[len(pds)-1]
- metadata := latest.Metadata.(*maven_module.Metadata)
-
resp := &MetadataResponse{
- GroupID: metadata.GroupID,
- ArtifactID: metadata.ArtifactID,
+ GroupID: groupID,
+ ArtifactID: artifactID,
Latest: latest.Version.Version,
Version: versions,
}
diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go
index 4d04d4d1e9..40a8ff8242 100644
--- a/routers/api/packages/maven/maven.go
+++ b/routers/api/packages/maven/maven.go
@@ -84,16 +84,20 @@ func handlePackageFile(ctx *context.Context, serveContent bool) {
}
func serveMavenMetadata(ctx *context.Context, params parameters) {
- // /com/foo/project/maven-metadata.xml[.md5/.sha1/.sha256/.sha512]
-
- pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeMaven, params.toInternalPackageName())
- if errors.Is(err, util.ErrNotExist) {
- pvs, err = packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeMaven, params.toInternalPackageNameLegacy())
+ // path pattern: /com/foo/project/maven-metadata.xml[.md5/.sha1/.sha256/.sha512]
+ // in case there are legacy package names ("GroupID-ArtifactID") we need to check both, new packages always use ":" as separator("GroupID:ArtifactID")
+ pvsLegacy, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeMaven, params.toInternalPackageNameLegacy())
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
}
+ pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeMaven, params.toInternalPackageName())
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
+ pvs = append(pvsLegacy, pvs...)
+
if len(pvs) == 0 {
apiError(ctx, http.StatusNotFound, packages_model.ErrPackageNotExist)
return
@@ -110,7 +114,7 @@ func serveMavenMetadata(ctx *context.Context, params parameters) {
return pds[i].Version.CreatedUnix < pds[j].Version.CreatedUnix
})
- xmlMetadata, err := xml.Marshal(createMetadataResponse(pds))
+ xmlMetadata, err := xml.Marshal(createMetadataResponse(pds, params.GroupID, params.ArtifactID))
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
@@ -219,7 +223,7 @@ func servePackageFile(ctx *context.Context, params parameters, serveContent bool
return
}
- s, u, _, err := packages_service.GetPackageBlobStream(ctx, pf, pb, nil)
+ s, u, _, err := packages_service.OpenBlobForDownload(ctx, pf, pb, nil)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
diff --git a/routers/api/packages/npm/api.go b/routers/api/packages/npm/api.go
index b4379f3f49..636680242a 100644
--- a/routers/api/packages/npm/api.go
+++ b/routers/api/packages/npm/api.go
@@ -67,6 +67,7 @@ func createPackageMetadataVersion(registryURL string, pd *packages_model.Package
BundleDependencies: metadata.BundleDependencies,
DevDependencies: metadata.DevelopmentDependencies,
PeerDependencies: metadata.PeerDependencies,
+ PeerDependenciesMeta: metadata.PeerDependenciesMeta,
OptionalDependencies: metadata.OptionalDependencies,
Readme: metadata.Readme,
Bin: metadata.Bin,
diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go
index 284723e0d7..1f09816d32 100644
--- a/routers/api/packages/npm/npm.go
+++ b/routers/api/packages/npm/npm.go
@@ -85,7 +85,7 @@ func DownloadPackageFile(ctx *context.Context) {
packageVersion := ctx.PathParam("version")
filename := ctx.PathParam("filename")
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
@@ -98,7 +98,7 @@ func DownloadPackageFile(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -132,7 +132,7 @@ func DownloadPackageFileByName(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageVersion(
ctx,
pvs[0],
&packages_service.PackageFileInfo{
@@ -140,7 +140,7 @@ func DownloadPackageFileByName(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -163,7 +163,7 @@ func UploadPackage(ctx *context.Context) {
return
}
- repo, err := repo_model.GetRepositoryByURL(ctx, npmPackage.Metadata.Repository.URL)
+ repo, err := repo_model.GetRepositoryByURLRelax(ctx, npmPackage.Metadata.Repository.URL)
if err == nil {
canWrite := repo.OwnerID == ctx.Doer.ID
@@ -267,7 +267,7 @@ func DeletePackageVersion(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -341,7 +341,7 @@ func AddPackageTag(ctx *context.Context) {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeNpm, packageName, version)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
diff --git a/routers/api/packages/nuget/api_v2.go b/routers/api/packages/nuget/api_v2.go
index a726065ad0..801c60af13 100644
--- a/routers/api/packages/nuget/api_v2.go
+++ b/routers/api/packages/nuget/api_v2.go
@@ -246,21 +246,30 @@ type TypedValue[T any] struct {
}
type FeedEntryProperties struct {
- Version string `xml:"d:Version"`
- NormalizedVersion string `xml:"d:NormalizedVersion"`
Authors string `xml:"d:Authors"`
+ Copyright string `xml:"d:Copyright,omitempty"`
+ Created TypedValue[time.Time] `xml:"d:Created"`
Dependencies string `xml:"d:Dependencies"`
Description string `xml:"d:Description"`
- VersionDownloadCount TypedValue[int64] `xml:"d:VersionDownloadCount"`
+ DevelopmentDependency TypedValue[bool] `xml:"d:DevelopmentDependency"`
DownloadCount TypedValue[int64] `xml:"d:DownloadCount"`
- PackageSize TypedValue[int64] `xml:"d:PackageSize"`
- Created TypedValue[time.Time] `xml:"d:Created"`
+ ID string `xml:"d:Id"`
+ IconURL string `xml:"d:IconUrl,omitempty"`
+ Language string `xml:"d:Language,omitempty"`
LastUpdated TypedValue[time.Time] `xml:"d:LastUpdated"`
- Published TypedValue[time.Time] `xml:"d:Published"`
+ LicenseURL string `xml:"d:LicenseUrl,omitempty"`
+ MinClientVersion string `xml:"d:MinClientVersion,omitempty"`
+ NormalizedVersion string `xml:"d:NormalizedVersion"`
+ Owners string `xml:"d:Owners,omitempty"`
+ PackageSize TypedValue[int64] `xml:"d:PackageSize"`
ProjectURL string `xml:"d:ProjectUrl,omitempty"`
+ Published TypedValue[time.Time] `xml:"d:Published"`
ReleaseNotes string `xml:"d:ReleaseNotes,omitempty"`
RequireLicenseAcceptance TypedValue[bool] `xml:"d:RequireLicenseAcceptance"`
- Title string `xml:"d:Title"`
+ Tags string `xml:"d:Tags,omitempty"`
+ Title string `xml:"d:Title,omitempty"`
+ Version string `xml:"d:Version"`
+ VersionDownloadCount TypedValue[int64] `xml:"d:VersionDownloadCount"`
}
type FeedEntry struct {
@@ -353,21 +362,30 @@ func createEntry(l *linkBuilder, pd *packages_model.PackageDescriptor, withNames
Author: metadata.Authors,
Content: content,
Properties: &FeedEntryProperties{
- Version: pd.Version.Version,
- NormalizedVersion: pd.Version.Version,
Authors: metadata.Authors,
+ Copyright: metadata.Copyright,
+ Created: createdValue,
Dependencies: buildDependencyString(metadata),
Description: metadata.Description,
- VersionDownloadCount: TypedValue[int64]{Type: "Edm.Int64", Value: pd.Version.DownloadCount},
+ DevelopmentDependency: TypedValue[bool]{Type: "Edm.Boolean", Value: metadata.DevelopmentDependency},
DownloadCount: TypedValue[int64]{Type: "Edm.Int64", Value: pd.Version.DownloadCount},
- PackageSize: TypedValue[int64]{Type: "Edm.Int64", Value: pd.CalculateBlobSize()},
- Created: createdValue,
+ ID: pd.Package.Name,
+ IconURL: metadata.IconURL,
+ Language: metadata.Language,
LastUpdated: createdValue,
- Published: createdValue,
+ LicenseURL: metadata.LicenseURL,
+ MinClientVersion: metadata.MinClientVersion,
+ NormalizedVersion: pd.Version.Version,
+ Owners: metadata.Owners,
+ PackageSize: TypedValue[int64]{Type: "Edm.Int64", Value: pd.CalculateBlobSize()},
ProjectURL: metadata.ProjectURL,
+ Published: createdValue,
ReleaseNotes: metadata.ReleaseNotes,
RequireLicenseAcceptance: TypedValue[bool]{Type: "Edm.Boolean", Value: metadata.RequireLicenseAcceptance},
- Title: pd.Package.Name,
+ Tags: metadata.Tags,
+ Title: metadata.Title,
+ Version: pd.Version.Version,
+ VersionDownloadCount: TypedValue[int64]{Type: "Edm.Int64", Value: pd.Version.DownloadCount},
},
}
diff --git a/routers/api/packages/nuget/nuget.go b/routers/api/packages/nuget/nuget.go
index 70b95e6a77..92d62d90b1 100644
--- a/routers/api/packages/nuget/nuget.go
+++ b/routers/api/packages/nuget/nuget.go
@@ -36,7 +36,7 @@ func apiError(ctx *context.Context, status int, obj any) {
})
}
-func xmlResponse(ctx *context.Context, status int, obj any) { //nolint:unparam
+func xmlResponse(ctx *context.Context, status int, obj any) { //nolint:unparam // status is always StatusOK
ctx.Resp.Header().Set("Content-Type", "application/atom+xml; charset=utf-8")
ctx.Resp.WriteHeader(status)
if _, err := ctx.Resp.Write([]byte(xml.Header)); err != nil {
@@ -259,7 +259,7 @@ func RegistrationLeafV2(ctx *context.Context) {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeNuGet, packageName, packageVersion)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -288,7 +288,7 @@ func RegistrationLeafV3(ctx *context.Context) {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeNuGet, packageName, packageVersion)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -405,7 +405,7 @@ func DownloadPackageFile(ctx *context.Context) {
packageVersion := ctx.PathParam("version")
filename := ctx.PathParam("filename")
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
@@ -418,7 +418,7 @@ func DownloadPackageFile(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -488,7 +488,7 @@ func UploadPackage(ctx *context.Context) {
pv,
&packages_service.PackageFileCreationInfo{
PackageFileInfo: packages_service.PackageFileInfo{
- Filename: strings.ToLower(fmt.Sprintf("%s.nuspec", np.ID)),
+ Filename: strings.ToLower(np.ID + ".nuspec"),
},
Data: nuspecBuf,
},
@@ -669,9 +669,9 @@ func DownloadSymbolFile(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
+ s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0])
if err != nil {
- if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -699,7 +699,7 @@ func DeletePackage(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
diff --git a/routers/api/packages/pub/pub.go b/routers/api/packages/pub/pub.go
index 2be27323fd..4bd36e94b6 100644
--- a/routers/api/packages/pub/pub.go
+++ b/routers/api/packages/pub/pub.go
@@ -124,7 +124,7 @@ func PackageVersionMetadata(ctx *context.Context) {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypePub, packageName, packageVersion)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -233,7 +233,7 @@ func FinalizePackage(ctx *context.Context) {
_, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypePub, packageName, packageVersion)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -258,7 +258,7 @@ func DownloadPackageFile(ctx *context.Context) {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypePub, packageName, packageVersion)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -274,7 +274,7 @@ func DownloadPackageFile(ctx *context.Context) {
pf := pd.Files[0].File
- s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
+ s, u, _, err := packages_service.OpenFileForDownload(ctx, pf)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
diff --git a/routers/api/packages/pypi/pypi.go b/routers/api/packages/pypi/pypi.go
index 5ea86071a9..9b5ae6c89d 100644
--- a/routers/api/packages/pypi/pypi.go
+++ b/routers/api/packages/pypi/pypi.go
@@ -5,11 +5,13 @@ package pypi
import (
"encoding/hex"
+ "errors"
"io"
"net/http"
"regexp"
"sort"
"strings"
+ "unicode"
packages_model "code.gitea.io/gitea/models/packages"
packages_module "code.gitea.io/gitea/modules/packages"
@@ -80,7 +82,7 @@ func DownloadPackageFile(ctx *context.Context) {
packageVersion := ctx.PathParam("version")
filename := ctx.PathParam("filename")
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
@@ -93,7 +95,7 @@ func DownloadPackageFile(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -139,9 +141,30 @@ func UploadPackageFile(ctx *context.Context) {
return
}
- projectURL := ctx.Req.FormValue("home_page")
- if !validation.IsValidURL(projectURL) {
- projectURL = ""
+ // Ensure ctx.Req.Form exists.
+ _ = ctx.Req.ParseForm()
+
+ var homepageURL string
+ projectURLs := ctx.Req.Form["project_urls"]
+ for _, purl := range projectURLs {
+ label, url, found := strings.Cut(purl, ",")
+ if !found {
+ continue
+ }
+ if normalizeLabel(label) != "homepage" {
+ continue
+ }
+ homepageURL = strings.TrimSpace(url)
+ break
+ }
+
+ if len(homepageURL) == 0 {
+ // TODO: Home-page is a deprecated metadata field. Remove this branch once it's no longer apart of the spec.
+ homepageURL = ctx.Req.FormValue("home_page")
+ }
+
+ if !validation.IsValidURL(homepageURL) {
+ homepageURL = ""
}
_, _, err = packages_service.CreatePackageOrAddFileToExisting(
@@ -160,7 +183,7 @@ func UploadPackageFile(ctx *context.Context) {
Description: ctx.Req.FormValue("description"),
LongDescription: ctx.Req.FormValue("long_description"),
Summary: ctx.Req.FormValue("summary"),
- ProjectURL: projectURL,
+ ProjectURL: homepageURL,
License: ctx.Req.FormValue("license"),
RequiresPython: ctx.Req.FormValue("requires_python"),
},
@@ -189,6 +212,23 @@ func UploadPackageFile(ctx *context.Context) {
ctx.Status(http.StatusCreated)
}
+// Normalizes a Project-URL label.
+// See https://packaging.python.org/en/latest/specifications/well-known-project-urls/#label-normalization.
+func normalizeLabel(label string) string {
+ var builder strings.Builder
+
+ // "A label is normalized by deleting all ASCII punctuation and whitespace, and then converting the result
+ // to lowercase."
+ for _, r := range label {
+ if unicode.IsPunct(r) || unicode.IsSpace(r) {
+ continue
+ }
+ builder.WriteRune(unicode.ToLower(r))
+ }
+
+ return builder.String()
+}
+
func isValidNameAndVersion(packageName, packageVersion string) bool {
return nameMatcher.MatchString(packageName) && versionMatcher.MatchString(packageVersion)
}
diff --git a/routers/api/packages/pypi/pypi_test.go b/routers/api/packages/pypi/pypi_test.go
index 3023692177..786105693f 100644
--- a/routers/api/packages/pypi/pypi_test.go
+++ b/routers/api/packages/pypi/pypi_test.go
@@ -36,3 +36,13 @@ func TestIsValidNameAndVersion(t *testing.T) {
assert.False(t, isValidNameAndVersion("test-name", "1.0.1aa"))
assert.False(t, isValidNameAndVersion("test-name", "1.0.0-alpha.beta"))
}
+
+func TestNormalizeLabel(t *testing.T) {
+ // Cases fetched from https://packaging.python.org/en/latest/specifications/well-known-project-urls/#label-normalization.
+ assert.Equal(t, "homepage", normalizeLabel("Homepage"))
+ assert.Equal(t, "homepage", normalizeLabel("Home-page"))
+ assert.Equal(t, "homepage", normalizeLabel("Home page"))
+ assert.Equal(t, "changelog", normalizeLabel("Change_Log"))
+ assert.Equal(t, "whatsnew", normalizeLabel("What's New?"))
+ assert.Equal(t, "github", normalizeLabel("github"))
+}
diff --git a/routers/api/packages/rpm/rpm.go b/routers/api/packages/rpm/rpm.go
index a00a61c079..938c35341d 100644
--- a/routers/api/packages/rpm/rpm.go
+++ b/routers/api/packages/rpm/rpm.go
@@ -96,7 +96,7 @@ func GetRepositoryFile(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageVersion(
ctx,
pv,
&packages_service.PackageFileInfo{
@@ -220,7 +220,7 @@ func DownloadPackageFile(ctx *context.Context) {
name := ctx.PathParam("name")
version := ctx.PathParam("version")
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
diff --git a/routers/api/packages/rubygems/rubygems.go b/routers/api/packages/rubygems/rubygems.go
index 958063e70a..774d5520fd 100644
--- a/routers/api/packages/rubygems/rubygems.go
+++ b/routers/api/packages/rubygems/rubygems.go
@@ -14,6 +14,7 @@ import (
"strings"
packages_model "code.gitea.io/gitea/models/packages"
+ "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/optional"
packages_module "code.gitea.io/gitea/modules/packages"
rubygems_module "code.gitea.io/gitea/modules/packages/rubygems"
@@ -177,7 +178,7 @@ func DownloadPackageFile(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageVersion(
ctx,
pvs[0],
&packages_service.PackageFileInfo{
@@ -185,7 +186,7 @@ func DownloadPackageFile(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -288,7 +289,7 @@ func DeletePackage(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
@@ -309,7 +310,7 @@ func GetPackageInfo(ctx *context.Context) {
apiError(ctx, http.StatusNotFound, nil)
return
}
- infoContent, err := makePackageInfo(ctx, versions)
+ infoContent, err := makePackageInfo(ctx, versions, cache.NewEphemeralCache())
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
@@ -317,7 +318,7 @@ func GetPackageInfo(ctx *context.Context) {
ctx.PlainText(http.StatusOK, infoContent)
}
-// GetAllPackagesVersions returns a custom text based format containing information about all versions of all rubygems.
+// GetAllPackagesVersions returns a custom text-based format containing information about all versions of all rubygems.
// ref: https://guides.rubygems.org/rubygems-org-compact-index-api/
func GetAllPackagesVersions(ctx *context.Context) {
packages, err := packages_model.GetPackagesByType(ctx, ctx.Package.Owner.ID, packages_model.TypeRubyGems)
@@ -326,6 +327,7 @@ func GetAllPackagesVersions(ctx *context.Context) {
return
}
+ ephemeralCache := cache.NewEphemeralCache()
out := &strings.Builder{}
out.WriteString("---\n")
for _, pkg := range packages {
@@ -338,7 +340,7 @@ func GetAllPackagesVersions(ctx *context.Context) {
continue
}
- info, err := makePackageInfo(ctx, versions)
+ info, err := makePackageInfo(ctx, versions, ephemeralCache)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
@@ -348,7 +350,14 @@ func GetAllPackagesVersions(ctx *context.Context) {
_, _ = fmt.Fprintf(out, "%s ", pkg.Name)
for i, v := range versions {
sep := util.Iif(i == len(versions)-1, "", ",")
- _, _ = fmt.Fprintf(out, "%s%s", v.Version, sep)
+ pd, err := packages_model.GetPackageDescriptorWithCache(ctx, v, ephemeralCache)
+ if errors.Is(err, util.ErrNotExist) {
+ continue
+ } else if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ writePackageVersionForList(pd.Metadata, v.Version, sep, out)
}
_, _ = fmt.Fprintf(out, " %x\n", md5.Sum([]byte(info)))
}
@@ -356,6 +365,16 @@ func GetAllPackagesVersions(ctx *context.Context) {
ctx.PlainText(http.StatusOK, out.String())
}
+func writePackageVersionForList(metadata any, version, sep string, out *strings.Builder) {
+ if metadata, _ := metadata.(*rubygems_module.Metadata); metadata != nil && metadata.Platform != "" && metadata.Platform != "ruby" {
+ // VERSION_PLATFORM (see comment above in GetAllPackagesVersions)
+ _, _ = fmt.Fprintf(out, "%s_%s%s", version, metadata.Platform, sep)
+ } else {
+ // VERSION only
+ _, _ = fmt.Fprintf(out, "%s%s", version, sep)
+ }
+}
+
func writePackageVersionRequirements(prefix string, reqs []rubygems_module.VersionRequirement, out *strings.Builder) {
out.WriteString(prefix)
if len(reqs) == 0 {
@@ -367,11 +386,21 @@ func writePackageVersionRequirements(prefix string, reqs []rubygems_module.Versi
}
}
-func makePackageVersionDependency(ctx *context.Context, version *packages_model.PackageVersion) (string, error) {
+func writePackageVersionForDependency(version, platform string, out *strings.Builder) {
+ if platform != "" && platform != "ruby" {
+ // VERSION-PLATFORM (see comment below in makePackageVersionDependency)
+ _, _ = fmt.Fprintf(out, "%s-%s ", version, platform)
+ } else {
+ // VERSION only
+ _, _ = fmt.Fprintf(out, "%s ", version)
+ }
+}
+
+func makePackageVersionDependency(ctx *context.Context, version *packages_model.PackageVersion, c *cache.EphemeralCache) (string, error) {
// format: VERSION[-PLATFORM] [DEPENDENCY[,DEPENDENCY,...]]|REQUIREMENT[,REQUIREMENT,...]
// DEPENDENCY: GEM:CONSTRAINT[&CONSTRAINT]
// REQUIREMENT: KEY:VALUE (always contains "checksum")
- pd, err := packages_model.GetPackageDescriptor(ctx, version)
+ pd, err := packages_model.GetPackageDescriptorWithCache(ctx, version, c)
if err != nil {
return "", err
}
@@ -388,8 +417,7 @@ func makePackageVersionDependency(ctx *context.Context, version *packages_model.
}
buf := &strings.Builder{}
- buf.WriteString(version.Version)
- buf.WriteByte(' ')
+ writePackageVersionForDependency(version.Version, metadata.Platform, buf)
for i, dep := range metadata.RuntimeDependencies {
sep := util.Iif(i == 0, "", ",")
writePackageVersionRequirements(fmt.Sprintf("%s%s:", sep, dep.Name), dep.Version, buf)
@@ -404,10 +432,10 @@ func makePackageVersionDependency(ctx *context.Context, version *packages_model.
return buf.String(), nil
}
-func makePackageInfo(ctx *context.Context, versions []*packages_model.PackageVersion) (string, error) {
+func makePackageInfo(ctx *context.Context, versions []*packages_model.PackageVersion, c *cache.EphemeralCache) (string, error) {
ret := "---\n"
for _, v := range versions {
- dep, err := makePackageVersionDependency(ctx, v)
+ dep, err := makePackageVersionDependency(ctx, v, c)
if err != nil {
return "", err
}
diff --git a/routers/api/packages/rubygems/rubygems_test.go b/routers/api/packages/rubygems/rubygems_test.go
new file mode 100644
index 0000000000..a07e12a7d3
--- /dev/null
+++ b/routers/api/packages/rubygems/rubygems_test.go
@@ -0,0 +1,41 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package rubygems
+
+import (
+ "strings"
+ "testing"
+
+ rubygems_module "code.gitea.io/gitea/modules/packages/rubygems"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestWritePackageVersion(t *testing.T) {
+ buf := &strings.Builder{}
+
+ writePackageVersionForList(nil, "1.0", " ", buf)
+ assert.Equal(t, "1.0 ", buf.String())
+ buf.Reset()
+
+ writePackageVersionForList(&rubygems_module.Metadata{Platform: "ruby"}, "1.0", " ", buf)
+ assert.Equal(t, "1.0 ", buf.String())
+ buf.Reset()
+
+ writePackageVersionForList(&rubygems_module.Metadata{Platform: "linux"}, "1.0", " ", buf)
+ assert.Equal(t, "1.0_linux ", buf.String())
+ buf.Reset()
+
+ writePackageVersionForDependency("1.0", "", buf)
+ assert.Equal(t, "1.0 ", buf.String())
+ buf.Reset()
+
+ writePackageVersionForDependency("1.0", "ruby", buf)
+ assert.Equal(t, "1.0 ", buf.String())
+ buf.Reset()
+
+ writePackageVersionForDependency("1.0", "os", buf)
+ assert.Equal(t, "1.0-os ", buf.String())
+ buf.Reset()
+}
diff --git a/routers/api/packages/swift/swift.go b/routers/api/packages/swift/swift.go
index 4d7fb8b1a6..bf542f33a7 100644
--- a/routers/api/packages/swift/swift.go
+++ b/routers/api/packages/swift/swift.go
@@ -290,7 +290,24 @@ func DownloadManifest(ctx *context.Context) {
})
}
-// https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-6
+// formFileOptionalReadCloser returns (nil, nil) if the formKey is not present.
+func formFileOptionalReadCloser(ctx *context.Context, formKey string) (io.ReadCloser, error) {
+ multipartFile, _, err := ctx.Req.FormFile(formKey)
+ if err != nil && !errors.Is(err, http.ErrMissingFile) {
+ return nil, err
+ }
+ if multipartFile != nil {
+ return multipartFile, nil
+ }
+
+ content := ctx.Req.FormValue(formKey)
+ if content == "" {
+ return nil, nil
+ }
+ return io.NopCloser(strings.NewReader(content)), nil
+}
+
+// UploadPackageFile refers to https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-6
func UploadPackageFile(ctx *context.Context) {
packageScope := ctx.PathParam("scope")
packageName := ctx.PathParam("name")
@@ -304,9 +321,9 @@ func UploadPackageFile(ctx *context.Context) {
packageVersion := v.Core().String()
- file, _, err := ctx.Req.FormFile("source-archive")
- if err != nil {
- apiError(ctx, http.StatusBadRequest, err)
+ file, err := formFileOptionalReadCloser(ctx, "source-archive")
+ if file == nil || err != nil {
+ apiError(ctx, http.StatusBadRequest, "unable to read source-archive file")
return
}
defer file.Close()
@@ -318,10 +335,13 @@ func UploadPackageFile(ctx *context.Context) {
}
defer buf.Close()
- var mr io.Reader
- metadata := ctx.Req.FormValue("metadata")
- if metadata != "" {
- mr = strings.NewReader(metadata)
+ mr, err := formFileOptionalReadCloser(ctx, "metadata")
+ if err != nil {
+ apiError(ctx, http.StatusBadRequest, "unable to read metadata file")
+ return
+ }
+ if mr != nil {
+ defer mr.Close()
}
pck, err := swift_module.ParsePackage(buf, buf.Size(), mr)
@@ -409,7 +429,7 @@ func DownloadPackageFile(ctx *context.Context) {
pf := pd.Files[0].File
- s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
+ s, u, _, err := packages_service.OpenFileForDownload(ctx, pf)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
diff --git a/routers/api/packages/vagrant/vagrant.go b/routers/api/packages/vagrant/vagrant.go
index 1daf2a0527..9eb67e5397 100644
--- a/routers/api/packages/vagrant/vagrant.go
+++ b/routers/api/packages/vagrant/vagrant.go
@@ -4,6 +4,7 @@
package vagrant
import (
+ "errors"
"fmt"
"io"
"net/http"
@@ -217,7 +218,7 @@ func UploadPackageFile(ctx *context.Context) {
}
func DownloadPackageFile(ctx *context.Context) {
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
@@ -230,7 +231,7 @@ func DownloadPackageFile(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
+ if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
return
}
diff --git a/routers/api/v1/activitypub/person.go b/routers/api/v1/activitypub/person.go
index 995a148f0b..0b0db011c6 100644
--- a/routers/api/v1/activitypub/person.go
+++ b/routers/api/v1/activitypub/person.go
@@ -41,14 +41,14 @@ func Person(ctx *context.APIContext) {
person.Name = ap.NaturalLanguageValuesNew()
err := person.Name.Set("en", ap.Content(ctx.ContextUser.FullName))
if err != nil {
- ctx.ServerError("Set Name", err)
+ ctx.APIErrorInternal(err)
return
}
person.PreferredUsername = ap.NaturalLanguageValuesNew()
err = person.PreferredUsername.Set("en", ap.Content(ctx.ContextUser.Name))
if err != nil {
- ctx.ServerError("Set PreferredUsername", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -68,14 +68,14 @@ func Person(ctx *context.APIContext) {
publicKeyPem, err := activitypub.GetPublicKey(ctx, ctx.ContextUser)
if err != nil {
- ctx.ServerError("GetPublicKey", err)
+ ctx.APIErrorInternal(err)
return
}
person.PublicKey.PublicKeyPem = publicKeyPem
binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(person)
if err != nil {
- ctx.ServerError("MarshalJSON", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType)
diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go
index 853c3c0b59..4eff51782f 100644
--- a/routers/api/v1/activitypub/reqsignature.go
+++ b/routers/api/v1/activitypub/reqsignature.go
@@ -7,6 +7,7 @@ import (
"crypto"
"crypto/x509"
"encoding/pem"
+ "errors"
"fmt"
"io"
"net/http"
@@ -34,7 +35,7 @@ func getPublicKeyFromResponse(b []byte, keyID *url.URL) (p crypto.PublicKey, err
pubKeyPem := pubKey.PublicKeyPem
block, _ := pem.Decode([]byte(pubKeyPem))
if block == nil || block.Type != "PUBLIC KEY" {
- return nil, fmt.Errorf("could not decode publicKeyPem to PUBLIC KEY pem block type")
+ return nil, errors.New("could not decode publicKeyPem to PUBLIC KEY pem block type")
}
p, err = x509.ParsePKIXPublicKey(block.Bytes)
return p, err
@@ -89,9 +90,9 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er
func ReqHTTPSignature() func(ctx *gitea_context.APIContext) {
return func(ctx *gitea_context.APIContext) {
if authenticated, err := verifyHTTPSignatures(ctx); err != nil {
- ctx.ServerError("verifyHttpSignatures", err)
+ ctx.APIErrorInternal(err)
} else if !authenticated {
- ctx.Error(http.StatusForbidden, "reqSignature", "request signature verification failed")
+ ctx.APIError(http.StatusForbidden, "request signature verification failed")
}
}
}
diff --git a/routers/api/v1/admin/action.go b/routers/api/v1/admin/action.go
new file mode 100644
index 0000000000..2fbb8e1a95
--- /dev/null
+++ b/routers/api/v1/admin/action.go
@@ -0,0 +1,93 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package admin
+
+import (
+ "code.gitea.io/gitea/routers/api/v1/shared"
+ "code.gitea.io/gitea/services/context"
+)
+
+// ListWorkflowJobs Lists all jobs
+func ListWorkflowJobs(ctx *context.APIContext) {
+ // swagger:operation GET /admin/actions/jobs admin listAdminWorkflowJobs
+ // ---
+ // summary: Lists all jobs
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: status
+ // in: query
+ // description: workflow status (pending, queued, in_progress, failure, success, skipped)
+ // type: string
+ // required: false
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results
+ // type: integer
+ // responses:
+ // "200":
+ // "$ref": "#/responses/WorkflowJobsList"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ shared.ListJobs(ctx, 0, 0, 0)
+}
+
+// ListWorkflowRuns Lists all runs
+func ListWorkflowRuns(ctx *context.APIContext) {
+ // swagger:operation GET /admin/actions/runs admin listAdminWorkflowRuns
+ // ---
+ // summary: Lists all runs
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: event
+ // in: query
+ // description: workflow event name
+ // type: string
+ // required: false
+ // - name: branch
+ // in: query
+ // description: workflow branch
+ // type: string
+ // required: false
+ // - name: status
+ // in: query
+ // description: workflow status (pending, queued, in_progress, failure, success, skipped)
+ // type: string
+ // required: false
+ // - name: actor
+ // in: query
+ // description: triggered by user
+ // type: string
+ // required: false
+ // - name: head_sha
+ // in: query
+ // description: triggering sha of the workflow run
+ // type: string
+ // required: false
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results
+ // type: integer
+ // responses:
+ // "200":
+ // "$ref": "#/responses/WorkflowRunsList"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ shared.ListRuns(ctx, 0, 0)
+}
diff --git a/routers/api/v1/admin/adopt.go b/routers/api/v1/admin/adopt.go
index 55ea8c6758..c2efed7490 100644
--- a/routers/api/v1/admin/adopt.go
+++ b/routers/api/v1/admin/adopt.go
@@ -46,7 +46,7 @@ func ListUnadoptedRepositories(ctx *context.APIContext) {
}
repoNames, count, err := repo_service.ListUnadoptedRepositories(ctx, ctx.FormString("query"), &listOptions)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -86,33 +86,33 @@ func AdoptRepository(ctx *context.APIContext) {
ctxUser, err := user_model.GetUserByName(ctx, ownerName)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
// check not a repo
has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, repoName)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
isDir, err := util.IsDir(repo_model.RepoPath(ctxUser.Name, repoName))
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
if has || !isDir {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if _, err := repo_service.AdoptRepository(ctx, ctx.Doer, ctxUser, repo_service.CreateRepoOptions{
Name: repoName,
IsPrivate: true,
}); err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -148,31 +148,31 @@ func DeleteUnadoptedRepository(ctx *context.APIContext) {
ctxUser, err := user_model.GetUserByName(ctx, ownerName)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
// check not a repo
has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, repoName)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
isDir, err := util.IsDir(repo_model.RepoPath(ctxUser.Name, repoName))
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
if has || !isDir {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if err := repo_service.DeleteUnadoptedRepository(ctx, ctx.Doer, ctxUser, repoName); err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/admin/cron.go b/routers/api/v1/admin/cron.go
index 962e007776..b4dae11095 100644
--- a/routers/api/v1/admin/cron.go
+++ b/routers/api/v1/admin/cron.go
@@ -76,7 +76,7 @@ func PostCronTask(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
task := cron.GetTask(ctx.PathParam("task"))
if task == nil {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
task.Run()
diff --git a/routers/api/v1/admin/email.go b/routers/api/v1/admin/email.go
index 3de94d6868..ad078347a4 100644
--- a/routers/api/v1/admin/email.go
+++ b/routers/api/v1/admin/email.go
@@ -42,7 +42,7 @@ func GetAllEmails(ctx *context.APIContext) {
ListOptions: listOptions,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetAllEmails", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/admin/hooks.go b/routers/api/v1/admin/hooks.go
index 6b4689047b..a687541be5 100644
--- a/routers/api/v1/admin/hooks.go
+++ b/routers/api/v1/admin/hooks.go
@@ -34,20 +34,40 @@ func ListHooks(ctx *context.APIContext) {
// in: query
// description: page size of results
// type: integer
+ // - type: string
+ // enum:
+ // - system
+ // - default
+ // - all
+ // description: system, default or both kinds of webhooks
+ // name: type
+ // default: system
+ // in: query
+ //
// responses:
// "200":
// "$ref": "#/responses/HookList"
- sysHooks, err := webhook.GetSystemWebhooks(ctx, optional.None[bool]())
+ // for compatibility the default value is true
+ isSystemWebhook := optional.Some(true)
+ typeValue := ctx.FormString("type")
+ switch typeValue {
+ case "default":
+ isSystemWebhook = optional.Some(false)
+ case "all":
+ isSystemWebhook = optional.None[bool]()
+ }
+
+ sysHooks, err := webhook.GetSystemOrDefaultWebhooks(ctx, isSystemWebhook)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetSystemWebhooks", err)
+ ctx.APIErrorInternal(err)
return
}
hooks := make([]*api.Hook, len(sysHooks))
for i, hook := range sysHooks {
h, err := webhook_service.ToHook(setting.AppURL+"/-/admin", hook)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "convert.ToHook", err)
+ ctx.APIErrorInternal(err)
return
}
hooks[i] = h
@@ -77,15 +97,15 @@ func GetHook(ctx *context.APIContext) {
hook, err := webhook.GetSystemOrDefaultWebhook(ctx, hookID)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetSystemOrDefaultWebhook", err)
+ ctx.APIErrorInternal(err)
}
return
}
h, err := webhook_service.ToHook("/-/admin/", hook)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "convert.ToHook", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, h)
@@ -167,9 +187,9 @@ func DeleteHook(ctx *context.APIContext) {
hookID := ctx.PathParamInt64("id")
if err := webhook.DeleteDefaultSystemWebhook(ctx, hookID); err != nil {
if errors.Is(err, util.ErrNotExist) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteDefaultSystemWebhook", err)
+ ctx.APIErrorInternal(err)
}
return
}
diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go
index a5c299bbf0..c3473372f2 100644
--- a/routers/api/v1/admin/org.go
+++ b/routers/api/v1/admin/org.go
@@ -29,7 +29,7 @@ func CreateOrg(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of the user that will own the created organization
+ // description: username of the user who will own the created organization
// type: string
// required: true
// - name: organization
@@ -67,9 +67,9 @@ func CreateOrg(ctx *context.APIContext) {
db.IsErrNameReserved(err) ||
db.IsErrNameCharsNotAllowed(err) ||
db.IsErrNamePatternNotAllowed(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "CreateOrganization", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -101,7 +101,7 @@ func GetAllOrgs(ctx *context.APIContext) {
listOptions := utils.GetListOptions(ctx)
- users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
+ users, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer,
Type: user_model.UserTypeOrganization,
OrderBy: db.SearchOrderByAlphabetically,
@@ -109,7 +109,7 @@ func GetAllOrgs(ctx *context.APIContext) {
Visible: []api.VisibleType{api.VisibleTypePublic, api.VisibleTypeLimited, api.VisibleTypePrivate},
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "SearchOrganizations", err)
+ ctx.APIErrorInternal(err)
return
}
orgs := make([]*api.Organization, len(users))
diff --git a/routers/api/v1/admin/repo.go b/routers/api/v1/admin/repo.go
index c119d5390a..12a78c9c4b 100644
--- a/routers/api/v1/admin/repo.go
+++ b/routers/api/v1/admin/repo.go
@@ -22,7 +22,7 @@ func CreateRepo(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of the user. This user will own the created repository
+ // description: username of the user who will own the created repository
// type: string
// required: true
// - name: repository
diff --git a/routers/api/v1/admin/runners.go b/routers/api/v1/admin/runners.go
index 329242d9f6..736c421229 100644
--- a/routers/api/v1/admin/runners.go
+++ b/routers/api/v1/admin/runners.go
@@ -24,3 +24,81 @@ func GetRegistrationToken(ctx *context.APIContext) {
shared.GetRegistrationToken(ctx, 0, 0)
}
+
+// CreateRegistrationToken returns the token to register global runners
+func CreateRegistrationToken(ctx *context.APIContext) {
+ // swagger:operation POST /admin/actions/runners/registration-token admin adminCreateRunnerRegistrationToken
+ // ---
+ // summary: Get an global actions runner registration token
+ // produces:
+ // - application/json
+ // parameters:
+ // responses:
+ // "200":
+ // "$ref": "#/responses/RegistrationToken"
+
+ shared.GetRegistrationToken(ctx, 0, 0)
+}
+
+// ListRunners get all runners
+func ListRunners(ctx *context.APIContext) {
+ // swagger:operation GET /admin/actions/runners admin getAdminRunners
+ // ---
+ // summary: Get all runners
+ // produces:
+ // - application/json
+ // responses:
+ // "200":
+ // "$ref": "#/definitions/ActionRunnersResponse"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.ListRunners(ctx, 0, 0)
+}
+
+// GetRunner get an global runner
+func GetRunner(ctx *context.APIContext) {
+ // swagger:operation GET /admin/actions/runners/{runner_id} admin getAdminRunner
+ // ---
+ // summary: Get an global runner
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: runner_id
+ // in: path
+ // description: id of the runner
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/definitions/ActionRunner"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.GetRunner(ctx, 0, 0, ctx.PathParamInt64("runner_id"))
+}
+
+// DeleteRunner delete an global runner
+func DeleteRunner(ctx *context.APIContext) {
+ // swagger:operation DELETE /admin/actions/runners/{runner_id} admin deleteAdminRunner
+ // ---
+ // summary: Delete an global runner
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: runner_id
+ // in: path
+ // description: id of the runner
+ // type: string
+ // required: true
+ // responses:
+ // "204":
+ // description: runner has been deleted
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.DeleteRunner(ctx, 0, 0, ctx.PathParamInt64("runner_id"))
+}
diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go
index 21cb2f9ccd..8a267cc418 100644
--- a/routers/api/v1/admin/user.go
+++ b/routers/api/v1/admin/user.go
@@ -40,9 +40,9 @@ func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64
source, err := auth.GetSourceByID(ctx, sourceID)
if err != nil {
if auth.IsErrSourceNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "auth.GetSourceByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -98,13 +98,13 @@ func CreateUser(ctx *context.APIContext) {
if u.LoginType == auth.Plain {
if len(form.Password) < setting.MinPasswordLength {
err := errors.New("PasswordIsRequired")
- ctx.Error(http.StatusBadRequest, "PasswordIsRequired", err)
+ ctx.APIError(http.StatusBadRequest, err)
return
}
if !password.IsComplexEnough(form.Password) {
err := errors.New("PasswordComplexity")
- ctx.Error(http.StatusBadRequest, "PasswordComplexity", err)
+ ctx.APIError(http.StatusBadRequest, err)
return
}
@@ -112,7 +112,7 @@ func CreateUser(ctx *context.APIContext) {
if password.IsErrIsPwnedRequest(err) {
log.Error(err.Error())
}
- ctx.Error(http.StatusBadRequest, "PasswordPwned", errors.New("PasswordPwned"))
+ ctx.APIError(http.StatusBadRequest, errors.New("PasswordPwned"))
return
}
}
@@ -143,9 +143,9 @@ func CreateUser(ctx *context.APIContext) {
user_model.IsErrEmailCharIsNotSupported(err) ||
user_model.IsErrEmailInvalid(err) ||
db.IsErrNamePatternNotAllowed(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "CreateUser", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -175,7 +175,7 @@ func EditUser(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user to edit
+ // description: username of the user whose data is to be edited
// type: string
// required: true
// - name: body
@@ -204,13 +204,13 @@ func EditUser(ctx *context.APIContext) {
if err := user_service.UpdateAuth(ctx, ctx.ContextUser, authOpts); err != nil {
switch {
case errors.Is(err, password.ErrMinLength):
- ctx.Error(http.StatusBadRequest, "PasswordTooShort", fmt.Errorf("password must be at least %d characters", setting.MinPasswordLength))
+ ctx.APIError(http.StatusBadRequest, fmt.Errorf("password must be at least %d characters", setting.MinPasswordLength))
case errors.Is(err, password.ErrComplexity):
- ctx.Error(http.StatusBadRequest, "PasswordComplexity", err)
+ ctx.APIError(http.StatusBadRequest, err)
case errors.Is(err, password.ErrIsPwned), password.IsErrIsPwnedRequest(err):
- ctx.Error(http.StatusBadRequest, "PasswordIsPwned", err)
+ ctx.APIError(http.StatusBadRequest, err)
default:
- ctx.Error(http.StatusInternalServerError, "UpdateAuth", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -219,11 +219,11 @@ func EditUser(ctx *context.APIContext) {
if err := user_service.AdminAddOrSetPrimaryEmailAddress(ctx, ctx.ContextUser, *form.Email); err != nil {
switch {
case user_model.IsErrEmailCharIsNotSupported(err), user_model.IsErrEmailInvalid(err):
- ctx.Error(http.StatusBadRequest, "EmailInvalid", err)
+ ctx.APIError(http.StatusBadRequest, err)
case user_model.IsErrEmailAlreadyUsed(err):
- ctx.Error(http.StatusBadRequest, "EmailUsed", err)
+ ctx.APIError(http.StatusBadRequest, err)
default:
- ctx.Error(http.StatusInternalServerError, "AddOrSetPrimaryEmailAddress", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -239,7 +239,7 @@ func EditUser(ctx *context.APIContext) {
Location: optional.FromPtr(form.Location),
Description: optional.FromPtr(form.Description),
IsActive: optional.FromPtr(form.Active),
- IsAdmin: optional.FromPtr(form.Admin),
+ IsAdmin: user_service.UpdateOptionFieldFromPtr(form.Admin),
Visibility: optional.FromNonDefault(api.VisibilityModes[form.Visibility]),
AllowGitHook: optional.FromPtr(form.AllowGitHook),
AllowImportLocal: optional.FromPtr(form.AllowImportLocal),
@@ -250,9 +250,9 @@ func EditUser(ctx *context.APIContext) {
if err := user_service.UpdateUser(ctx, ctx.ContextUser, opts); err != nil {
if user_model.IsErrDeleteLastAdminUser(err) {
- ctx.Error(http.StatusBadRequest, "LastAdmin", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- ctx.Error(http.StatusInternalServerError, "UpdateUser", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -272,7 +272,7 @@ func DeleteUser(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user to delete
+ // description: username of the user to delete
// type: string
// required: true
// - name: purge
@@ -290,13 +290,13 @@ func DeleteUser(ctx *context.APIContext) {
// "$ref": "#/responses/validationError"
if ctx.ContextUser.IsOrganization() {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("%s is an organization not a user", ctx.ContextUser.Name))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("%s is an organization not a user", ctx.ContextUser.Name))
return
}
// admin should not delete themself
if ctx.ContextUser.ID == ctx.Doer.ID {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("you cannot delete yourself"))
+ ctx.APIError(http.StatusUnprocessableEntity, errors.New("you cannot delete yourself"))
return
}
@@ -305,9 +305,9 @@ func DeleteUser(ctx *context.APIContext) {
org_model.IsErrUserHasOrgs(err) ||
packages_model.IsErrUserOwnPackages(err) ||
user_model.IsErrDeleteLastAdminUser(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteUser", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -328,7 +328,7 @@ func CreatePublicKey(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of the user
+ // description: username of the user who is to receive a public key
// type: string
// required: true
// - name: key
@@ -358,7 +358,7 @@ func DeleteUserPublicKey(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user whose public key is to be deleted
// type: string
// required: true
// - name: id
@@ -377,11 +377,11 @@ func DeleteUserPublicKey(ctx *context.APIContext) {
if err := asymkey_service.DeletePublicKey(ctx, ctx.ContextUser, ctx.PathParamInt64("id")); err != nil {
if asymkey_model.IsErrKeyNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else if asymkey_model.IsErrKeyAccessDenied(err) {
- ctx.Error(http.StatusForbidden, "", "You do not have access to this key")
+ ctx.APIError(http.StatusForbidden, "You do not have access to this key")
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteUserPublicKey", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -405,7 +405,7 @@ func SearchUsers(ctx *context.APIContext) {
// format: int64
// - name: login_name
// in: query
- // description: user's login name to search for
+ // description: identifier of the user, provided by the external authenticator
// type: string
// - name: page
// in: query
@@ -423,7 +423,7 @@ func SearchUsers(ctx *context.APIContext) {
listOptions := utils.GetListOptions(ctx)
- users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
+ users, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer,
Type: user_model.UserTypeIndividual,
LoginName: ctx.FormTrim("login_name"),
@@ -432,7 +432,7 @@ func SearchUsers(ctx *context.APIContext) {
ListOptions: listOptions,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "SearchUsers", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -456,7 +456,7 @@ func RenameUser(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: existing username of user
+ // description: current username of the user
// type: string
// required: true
// - name: body
@@ -473,30 +473,20 @@ func RenameUser(ctx *context.APIContext) {
// "$ref": "#/responses/validationError"
if ctx.ContextUser.IsOrganization() {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("%s is an organization not a user", ctx.ContextUser.Name))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("%s is an organization not a user", ctx.ContextUser.Name))
return
}
- oldName := ctx.ContextUser.Name
newName := web.GetForm(ctx).(*api.RenameUserOption).NewName
- // Check if user name has been changed
+ // Check if username has been changed
if err := user_service.RenameUser(ctx, ctx.ContextUser, newName); err != nil {
- switch {
- case user_model.IsErrUserAlreadyExist(err):
- ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("form.username_been_taken"))
- case db.IsErrNameReserved(err):
- ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("user.form.name_reserved", newName))
- case db.IsErrNamePatternNotAllowed(err):
- ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("user.form.name_pattern_not_allowed", newName))
- case db.IsErrNameCharsNotAllowed(err):
- ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("user.form.name_chars_not_allowed", newName))
- default:
- ctx.ServerError("ChangeUserName", err)
+ if user_model.IsErrUserAlreadyExist(err) || db.IsErrNameReserved(err) || db.IsErrNamePatternNotAllowed(err) || db.IsErrNameCharsNotAllowed(err) {
+ ctx.APIError(http.StatusUnprocessableEntity, err)
+ } else {
+ ctx.APIErrorInternal(err)
}
return
}
-
- log.Trace("User name changed: %s -> %s", oldName, newName)
ctx.Status(http.StatusNoContent)
}
diff --git a/routers/api/v1/admin/user_badge.go b/routers/api/v1/admin/user_badge.go
index 99e20877fd..ce32f455b0 100644
--- a/routers/api/v1/admin/user_badge.go
+++ b/routers/api/v1/admin/user_badge.go
@@ -22,7 +22,7 @@ func ListUserBadges(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user whose badges are to be listed
// type: string
// required: true
// responses:
@@ -33,7 +33,7 @@ func ListUserBadges(ctx *context.APIContext) {
badges, maxResults, err := user_model.GetUserBadges(ctx, ctx.ContextUser)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserBadges", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -53,7 +53,7 @@ func AddUserBadges(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user to whom a badge is to be added
// type: string
// required: true
// - name: body
@@ -70,7 +70,7 @@ func AddUserBadges(ctx *context.APIContext) {
badges := prepareBadgesForReplaceOrAdd(*form)
if err := user_model.AddUserBadges(ctx, ctx.ContextUser, badges); err != nil {
- ctx.Error(http.StatusInternalServerError, "ReplaceUserBadges", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -87,7 +87,7 @@ func DeleteUserBadges(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user whose badge is to be deleted
// type: string
// required: true
// - name: body
@@ -106,7 +106,7 @@ func DeleteUserBadges(ctx *context.APIContext) {
badges := prepareBadgesForReplaceOrAdd(*form)
if err := user_model.RemoveUserBadges(ctx, ctx.ContextUser, badges); err != nil {
- ctx.Error(http.StatusInternalServerError, "ReplaceUserBadges", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 2f943d306c..4a4bf12657 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -7,8 +7,6 @@
// This documentation describes the Gitea API.
//
// Schemes: https, http
-// BasePath: /api/v1
-// Version: {{AppVer | JSEscape}}
// License: MIT http://opensource.org/licenses/MIT
//
// Consumes:
@@ -66,6 +64,8 @@
package v1
import (
+ gocontext "context"
+ "errors"
"fmt"
"net/http"
"strings"
@@ -116,9 +116,9 @@ func sudo() func(ctx *context.APIContext) {
user, err := user_model.GetUserByName(ctx, sudo)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -154,12 +154,12 @@ func repoAssignment() func(ctx *context.APIContext) {
if redirectUserID, err := user_model.LookupUserRedirect(ctx, userName); err == nil {
context.RedirectToUser(ctx.Base, userName, redirectUserID)
} else if user_model.IsErrUserRedirectNotExist(err) {
- ctx.NotFound("GetUserByName", err)
+ ctx.APIErrorNotFound("GetUserByName", err)
} else {
- ctx.Error(http.StatusInternalServerError, "LookupUserRedirect", err)
+ ctx.APIErrorInternal(err)
}
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -175,12 +175,12 @@ func repoAssignment() func(ctx *context.APIContext) {
if err == nil {
context.RedirectToRepo(ctx.Base, redirectRepoID)
} else if repo_model.IsErrRedirectNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "LookupRepoRedirect", err)
+ ctx.APIErrorInternal(err)
}
} else {
- ctx.Error(http.StatusInternalServerError, "GetRepositoryByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -192,11 +192,11 @@ func repoAssignment() func(ctx *context.APIContext) {
taskID := ctx.Data["ActionsTaskID"].(int64)
task, err := actions_model.GetTaskByID(ctx, taskID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "actions_model.GetTaskByID", err)
+ ctx.APIErrorInternal(err)
return
}
if task.RepoID != repo.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -207,29 +207,52 @@ func repoAssignment() func(ctx *context.APIContext) {
}
if err := ctx.Repo.Repository.LoadUnits(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadUnits", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Repo.Permission.SetUnitsWithDefaultAccessMode(ctx.Repo.Repository.Units, ctx.Repo.Permission.AccessMode)
} else {
- ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
+ needTwoFactor, err := doerNeedTwoFactorAuth(ctx, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
+ ctx.APIErrorInternal(err)
return
}
+ if needTwoFactor {
+ ctx.Repo.Permission = access_model.PermissionNoAccess()
+ } else {
+ ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ }
}
- if !ctx.Repo.Permission.HasAnyUnitAccess() {
- ctx.NotFound()
+ if !ctx.Repo.Permission.HasAnyUnitAccessOrPublicAccess() {
+ ctx.APIErrorNotFound()
return
}
}
}
+func doerNeedTwoFactorAuth(ctx gocontext.Context, doer *user_model.User) (bool, error) {
+ if !setting.TwoFactorAuthEnforced {
+ return false, nil
+ }
+ if doer == nil {
+ return false, nil
+ }
+ has, err := auth_model.HasTwoFactorOrWebAuthn(ctx, doer.ID)
+ if err != nil {
+ return false, err
+ }
+ return !has, nil
+}
+
func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if ctx.Package.AccessMode < accessMode && !ctx.IsUserSiteAdmin() {
- ctx.Error(http.StatusForbidden, "reqPackageAccess", "user should have specific permission or be a site admin")
+ ctx.APIError(http.StatusForbidden, "user should have specific permission or be a site admin")
return
}
}
@@ -250,41 +273,41 @@ func checkTokenPublicOnly() func(ctx *context.APIContext) {
switch {
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryRepository):
if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate {
- ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public repos")
+ ctx.APIError(http.StatusForbidden, "token scope is limited to public repos")
return
}
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryIssue):
if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate {
- ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public issues")
+ ctx.APIError(http.StatusForbidden, "token scope is limited to public issues")
return
}
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryOrganization):
if ctx.Org.Organization != nil && ctx.Org.Organization.Visibility != api.VisibleTypePublic {
- ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public orgs")
+ ctx.APIError(http.StatusForbidden, "token scope is limited to public orgs")
return
}
if ctx.ContextUser != nil && ctx.ContextUser.IsOrganization() && ctx.ContextUser.Visibility != api.VisibleTypePublic {
- ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public orgs")
+ ctx.APIError(http.StatusForbidden, "token scope is limited to public orgs")
return
}
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryUser):
- if ctx.ContextUser != nil && ctx.ContextUser.IsUser() && ctx.ContextUser.Visibility != api.VisibleTypePublic {
- ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public users")
+ if ctx.ContextUser != nil && ctx.ContextUser.IsTokenAccessAllowed() && ctx.ContextUser.Visibility != api.VisibleTypePublic {
+ ctx.APIError(http.StatusForbidden, "token scope is limited to public users")
return
}
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryActivityPub):
- if ctx.ContextUser != nil && ctx.ContextUser.IsUser() && ctx.ContextUser.Visibility != api.VisibleTypePublic {
- ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public activitypub")
+ if ctx.ContextUser != nil && ctx.ContextUser.IsTokenAccessAllowed() && ctx.ContextUser.Visibility != api.VisibleTypePublic {
+ ctx.APIError(http.StatusForbidden, "token scope is limited to public activitypub")
return
}
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryNotification):
if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate {
- ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public notifications")
+ ctx.APIError(http.StatusForbidden, "token scope is limited to public notifications")
return
}
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryPackage):
if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() {
- ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public packages")
+ ctx.APIError(http.StatusForbidden, "token scope is limited to public packages")
return
}
}
@@ -308,7 +331,7 @@ func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeC
// use the http method to determine the access level
requiredScopeLevel := auth_model.Read
- if ctx.Req.Method == "POST" || ctx.Req.Method == "PUT" || ctx.Req.Method == "PATCH" || ctx.Req.Method == "DELETE" {
+ if ctx.Req.Method == http.MethodPost || ctx.Req.Method == http.MethodPut || ctx.Req.Method == http.MethodPatch || ctx.Req.Method == http.MethodDelete {
requiredScopeLevel = auth_model.Write
}
@@ -316,12 +339,12 @@ func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeC
requiredScopes := auth_model.GetRequiredScopes(requiredScopeLevel, requiredScopeCategories...)
allow, err := scope.HasScope(requiredScopes...)
if err != nil {
- ctx.Error(http.StatusForbidden, "tokenRequiresScope", "checking scope failed: "+err.Error())
+ ctx.APIError(http.StatusForbidden, "checking scope failed: "+err.Error())
return
}
if !allow {
- ctx.Error(http.StatusForbidden, "tokenRequiresScope", fmt.Sprintf("token does not have at least one of required scope(s), required=%v, token scope=%v", requiredScopes, scope))
+ ctx.APIError(http.StatusForbidden, fmt.Sprintf("token does not have at least one of required scope(s), required=%v, token scope=%v", requiredScopes, scope))
return
}
@@ -330,7 +353,7 @@ func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeC
// check if scope only applies to public resources
publicOnly, err := scope.PublicOnly()
if err != nil {
- ctx.Error(http.StatusForbidden, "tokenRequiresScope", "parsing public resource scope failed: "+err.Error())
+ ctx.APIError(http.StatusForbidden, "parsing public resource scope failed: "+err.Error())
return
}
@@ -350,14 +373,14 @@ func reqToken() func(ctx *context.APIContext) {
if ctx.IsSigned {
return
}
- ctx.Error(http.StatusUnauthorized, "reqToken", "token is required")
+ ctx.APIError(http.StatusUnauthorized, "token is required")
}
}
func reqExploreSignIn() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
- if (setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView) && !ctx.IsSigned {
- ctx.Error(http.StatusUnauthorized, "reqExploreSignIn", "you must be signed in to search for users")
+ if (setting.Service.RequireSignInViewStrict || setting.Service.Explore.RequireSigninView) && !ctx.IsSigned {
+ ctx.APIError(http.StatusUnauthorized, "you must be signed in to search for users")
}
}
}
@@ -365,7 +388,7 @@ func reqExploreSignIn() func(ctx *context.APIContext) {
func reqUsersExploreEnabled() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if setting.Service.Explore.DisableUsersPage {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
}
}
}
@@ -376,7 +399,7 @@ func reqBasicOrRevProxyAuth() func(ctx *context.APIContext) {
return
}
if !ctx.IsBasicAuth {
- ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "auth required")
+ ctx.APIError(http.StatusUnauthorized, "auth required")
return
}
}
@@ -386,7 +409,7 @@ func reqBasicOrRevProxyAuth() func(ctx *context.APIContext) {
func reqSiteAdmin() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if !ctx.IsUserSiteAdmin() {
- ctx.Error(http.StatusForbidden, "reqSiteAdmin", "user should be the site admin")
+ ctx.APIError(http.StatusForbidden, "user should be the site admin")
return
}
}
@@ -396,7 +419,7 @@ func reqSiteAdmin() func(ctx *context.APIContext) {
func reqOwner() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if !ctx.Repo.IsOwner() && !ctx.IsUserSiteAdmin() {
- ctx.Error(http.StatusForbidden, "reqOwner", "user should be the owner of the repo")
+ ctx.APIError(http.StatusForbidden, "user should be the owner of the repo")
return
}
}
@@ -406,7 +429,7 @@ func reqOwner() func(ctx *context.APIContext) {
func reqSelfOrAdmin() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if !ctx.IsUserSiteAdmin() && ctx.ContextUser != ctx.Doer {
- ctx.Error(http.StatusForbidden, "reqSelfOrAdmin", "doer should be the site admin or be same as the contextUser")
+ ctx.APIError(http.StatusForbidden, "doer should be the site admin or be same as the contextUser")
return
}
}
@@ -416,7 +439,7 @@ func reqSelfOrAdmin() func(ctx *context.APIContext) {
func reqAdmin() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() {
- ctx.Error(http.StatusForbidden, "reqAdmin", "user should be an owner or a collaborator with admin write of a repository")
+ ctx.APIError(http.StatusForbidden, "user should be an owner or a collaborator with admin write of a repository")
return
}
}
@@ -426,26 +449,17 @@ func reqAdmin() func(ctx *context.APIContext) {
func reqRepoWriter(unitTypes ...unit.Type) func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if !ctx.IsUserRepoWriter(unitTypes) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() {
- ctx.Error(http.StatusForbidden, "reqRepoWriter", "user should have a permission to write to a repo")
+ ctx.APIError(http.StatusForbidden, "user should have a permission to write to a repo")
return
}
}
}
-// reqRepoBranchWriter user should have a permission to write to a branch, or be a site admin
-func reqRepoBranchWriter(ctx *context.APIContext) {
- options, ok := web.GetForm(ctx).(api.FileOptionInterface)
- if !ok || (!ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, options.Branch()) && !ctx.IsUserSiteAdmin()) {
- ctx.Error(http.StatusForbidden, "reqRepoBranchWriter", "user should have a permission to write to this branch")
- return
- }
-}
-
// reqRepoReader user should have specific read permission or be a repo admin or a site admin
func reqRepoReader(unitType unit.Type) func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if !ctx.Repo.CanRead(unitType) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() {
- ctx.Error(http.StatusForbidden, "reqRepoReader", "user should have specific read permission or be a repo admin or a site admin")
+ ctx.APIError(http.StatusForbidden, "user should have specific read permission or be a repo admin or a site admin")
return
}
}
@@ -455,7 +469,7 @@ func reqRepoReader(unitType unit.Type) func(ctx *context.APIContext) {
func reqAnyRepoReader() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if !ctx.Repo.Permission.HasAnyUnitAccess() && !ctx.IsUserSiteAdmin() {
- ctx.Error(http.StatusForbidden, "reqAnyRepoReader", "user should have any permission to read repository or permissions of site admin")
+ ctx.APIError(http.StatusForbidden, "user should have any permission to read repository or permissions of site admin")
return
}
}
@@ -474,19 +488,20 @@ func reqOrgOwnership() func(ctx *context.APIContext) {
} else if ctx.Org.Team != nil {
orgID = ctx.Org.Team.OrgID
} else {
- ctx.Error(http.StatusInternalServerError, "", "reqOrgOwnership: unprepared context")
+ setting.PanicInDevOrTesting("reqOrgOwnership: unprepared context")
+ ctx.APIErrorInternal(errors.New("reqOrgOwnership: unprepared context"))
return
}
isOwner, err := organization.IsOrganizationOwner(ctx, orgID, ctx.Doer.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsOrganizationOwner", err)
+ ctx.APIErrorInternal(err)
return
} else if !isOwner {
if ctx.Org.Organization != nil {
- ctx.Error(http.StatusForbidden, "", "Must be an organization owner")
+ ctx.APIError(http.StatusForbidden, "Must be an organization owner")
} else {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
}
return
}
@@ -500,30 +515,31 @@ func reqTeamMembership() func(ctx *context.APIContext) {
return
}
if ctx.Org.Team == nil {
- ctx.Error(http.StatusInternalServerError, "", "reqTeamMembership: unprepared context")
+ setting.PanicInDevOrTesting("reqTeamMembership: unprepared context")
+ ctx.APIErrorInternal(errors.New("reqTeamMembership: unprepared context"))
return
}
orgID := ctx.Org.Team.OrgID
isOwner, err := organization.IsOrganizationOwner(ctx, orgID, ctx.Doer.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsOrganizationOwner", err)
+ ctx.APIErrorInternal(err)
return
} else if isOwner {
return
}
if isTeamMember, err := organization.IsTeamMember(ctx, orgID, ctx.Org.Team.ID, ctx.Doer.ID); err != nil {
- ctx.Error(http.StatusInternalServerError, "IsTeamMember", err)
+ ctx.APIErrorInternal(err)
return
} else if !isTeamMember {
isOrgMember, err := organization.IsOrganizationMember(ctx, orgID, ctx.Doer.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsOrganizationMember", err)
+ ctx.APIErrorInternal(err)
} else if isOrgMember {
- ctx.Error(http.StatusForbidden, "", "Must be a team member")
+ ctx.APIError(http.StatusForbidden, "Must be a team member")
} else {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
}
return
}
@@ -543,18 +559,19 @@ func reqOrgMembership() func(ctx *context.APIContext) {
} else if ctx.Org.Team != nil {
orgID = ctx.Org.Team.OrgID
} else {
- ctx.Error(http.StatusInternalServerError, "", "reqOrgMembership: unprepared context")
+ setting.PanicInDevOrTesting("reqOrgMembership: unprepared context")
+ ctx.APIErrorInternal(errors.New("reqOrgMembership: unprepared context"))
return
}
if isMember, err := organization.IsOrganizationMember(ctx, orgID, ctx.Doer.ID); err != nil {
- ctx.Error(http.StatusInternalServerError, "IsOrganizationMember", err)
+ ctx.APIErrorInternal(err)
return
} else if !isMember {
if ctx.Org.Organization != nil {
- ctx.Error(http.StatusForbidden, "", "Must be an organization member")
+ ctx.APIError(http.StatusForbidden, "Must be an organization member")
} else {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
}
return
}
@@ -564,7 +581,7 @@ func reqOrgMembership() func(ctx *context.APIContext) {
func reqGitHook() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if !ctx.Doer.CanEditGitHook() {
- ctx.Error(http.StatusForbidden, "", "must be allowed to edit Git hooks")
+ ctx.APIError(http.StatusForbidden, "must be allowed to edit Git hooks")
return
}
}
@@ -574,7 +591,17 @@ func reqGitHook() func(ctx *context.APIContext) {
func reqWebhooksEnabled() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if setting.DisableWebhooks {
- ctx.Error(http.StatusForbidden, "", "webhooks disabled by administrator")
+ ctx.APIError(http.StatusForbidden, "webhooks disabled by administrator")
+ return
+ }
+ }
+}
+
+// reqStarsEnabled requires Starring to be enabled in the config.
+func reqStarsEnabled() func(ctx *context.APIContext) {
+ return func(ctx *context.APIContext) {
+ if setting.Repository.DisableStars {
+ ctx.APIError(http.StatusForbidden, "stars disabled by administrator")
return
}
}
@@ -603,12 +630,12 @@ func orgAssignment(args ...bool) func(ctx *context.APIContext) {
if err == nil {
context.RedirectToUser(ctx.Base, ctx.PathParam("org"), redirectUserID)
} else if user_model.IsErrUserRedirectNotExist(err) {
- ctx.NotFound("GetOrgByName", err)
+ ctx.APIErrorNotFound("GetOrgByName", err)
} else {
- ctx.Error(http.StatusInternalServerError, "LookupUserRedirect", err)
+ ctx.APIErrorInternal(err)
}
} else {
- ctx.Error(http.StatusInternalServerError, "GetOrgByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -619,9 +646,9 @@ func orgAssignment(args ...bool) func(ctx *context.APIContext) {
ctx.Org.Team, err = organization.GetTeamByID(ctx, ctx.PathParamInt64("teamid"))
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetTeamById", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -647,7 +674,7 @@ func mustEnableIssues(ctx *context.APIContext) {
ctx.Repo.Permission)
}
}
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
}
@@ -670,7 +697,7 @@ func mustAllowPulls(ctx *context.APIContext) {
ctx.Repo.Permission)
}
}
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
}
@@ -696,28 +723,36 @@ func mustEnableIssuesOrPulls(ctx *context.APIContext) {
ctx.Repo.Permission)
}
}
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
}
func mustEnableWiki(ctx *context.APIContext) {
if !(ctx.Repo.CanRead(unit.TypeWiki)) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
}
+// FIXME: for consistency, maybe most mustNotBeArchived checks should be replaced with mustEnableEditor
func mustNotBeArchived(ctx *context.APIContext) {
if ctx.Repo.Repository.IsArchived {
- ctx.Error(http.StatusLocked, "RepoArchived", fmt.Errorf("%s is archived", ctx.Repo.Repository.LogString()))
+ ctx.APIError(http.StatusLocked, fmt.Errorf("%s is archived", ctx.Repo.Repository.FullName()))
+ return
+ }
+}
+
+func mustEnableEditor(ctx *context.APIContext) {
+ if !ctx.Repo.Repository.CanEnableEditor() {
+ ctx.APIError(http.StatusLocked, fmt.Errorf("%s is not allowed to edit", ctx.Repo.Repository.FullName()))
return
}
}
func mustEnableAttachments(ctx *context.APIContext) {
if !setting.Attachment.Enabled {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
}
@@ -728,7 +763,7 @@ func bind[T any](_ T) any {
theObj := new(T) // create a new form obj for every request but not use obj directly
errs := binding.Bind(ctx.Req, theObj)
if len(errs) > 0 {
- ctx.Error(http.StatusUnprocessableEntity, "validationError", fmt.Sprintf("%s: %s", errs[0].FieldNames, errs[0].Error()))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Sprintf("%s: %s", errs[0].FieldNames, errs[0].Error()))
return
}
web.SetForm(ctx, theObj)
@@ -756,7 +791,7 @@ func apiAuth(authMethod auth.Method) func(*context.APIContext) {
return func(ctx *context.APIContext) {
ar, err := common.AuthShared(ctx.Base, nil, authMethod)
if err != nil {
- ctx.Error(http.StatusUnauthorized, "APIAuth", err)
+ ctx.APIError(http.StatusUnauthorized, err)
return
}
ctx.Doer = ar.Doer
@@ -830,15 +865,15 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC
func individualPermsChecker(ctx *context.APIContext) {
// org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked.
if ctx.ContextUser.IsIndividual() {
- switch {
- case ctx.ContextUser.Visibility == api.VisibleTypePrivate:
+ switch ctx.ContextUser.Visibility {
+ case api.VisibleTypePrivate:
if ctx.Doer == nil || (ctx.ContextUser.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin) {
- ctx.NotFound("Visit Project", nil)
+ ctx.APIErrorNotFound("Visit Project", nil)
return
}
- case ctx.ContextUser.Visibility == api.VisibleTypeLimited:
+ case api.VisibleTypeLimited:
if ctx.Doer == nil {
- ctx.NotFound("Visit Project", nil)
+ ctx.APIErrorNotFound("Visit Project", nil)
return
}
}
@@ -874,7 +909,7 @@ func Routes() *web.Router {
m.Use(apiAuth(buildAuthGroup()))
m.Use(verifyAuthWithOptions(&common.VerifyOptions{
- SignInRequired: setting.Service.RequireSignInView,
+ SignInRequired: setting.Service.RequireSignInViewStrict,
}))
addActionsRoutes := func(
@@ -900,8 +935,14 @@ func Routes() *web.Router {
})
m.Group("/runners", func() {
+ m.Get("", reqToken(), reqChecker, act.ListRunners)
m.Get("/registration-token", reqToken(), reqChecker, act.GetRegistrationToken)
+ m.Post("/registration-token", reqToken(), reqChecker, act.CreateRegistrationToken)
+ m.Get("/{runner_id}", reqToken(), reqChecker, act.GetRunner)
+ m.Delete("/{runner_id}", reqToken(), reqChecker, act.DeleteRunner)
})
+ m.Get("/runs", reqToken(), reqChecker, act.ListWorkflowRuns)
+ m.Get("/jobs", reqToken(), reqChecker, act.ListWorkflowJobs)
})
}
@@ -931,7 +972,8 @@ func Routes() *web.Router {
// Misc (public accessible)
m.Group("", func() {
m.Get("/version", misc.Version)
- m.Get("/signing-key.gpg", misc.SigningKey)
+ m.Get("/signing-key.gpg", misc.SigningKeyGPG)
+ m.Get("/signing-key.pub", misc.SigningKeySSH)
m.Post("/markup", reqToken(), bind(api.MarkupOption{}), misc.Markup)
m.Post("/markdown", reqToken(), bind(api.MarkdownOption{}), misc.Markdown)
m.Post("/markdown/raw", reqToken(), misc.MarkdownRaw)
@@ -995,7 +1037,7 @@ func Routes() *web.Router {
m.Get("/{target}", user.CheckFollowing)
})
- m.Get("/starred", user.GetStarredRepos)
+ m.Get("/starred", reqStarsEnabled(), user.GetStarredRepos)
m.Get("/subscriptions", user.GetWatchedRepos)
}, context.UserAssignmentAPI(), checkTokenPublicOnly())
@@ -1031,8 +1073,15 @@ func Routes() *web.Router {
})
m.Group("/runners", func() {
+ m.Get("", reqToken(), user.ListRunners)
m.Get("/registration-token", reqToken(), user.GetRegistrationToken)
+ m.Post("/registration-token", reqToken(), user.CreateRegistrationToken)
+ m.Get("/{runner_id}", reqToken(), user.GetRunner)
+ m.Delete("/{runner_id}", reqToken(), user.DeleteRunner)
})
+
+ m.Get("/runs", reqToken(), user.ListWorkflowRuns)
+ m.Get("/jobs", reqToken(), user.ListWorkflowJobs)
})
m.Get("/followers", user.ListMyFollowers)
@@ -1086,7 +1135,7 @@ func Routes() *web.Router {
m.Put("", user.Star)
m.Delete("", user.Unstar)
}, repoAssignment(), checkTokenPublicOnly())
- }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository))
+ }, reqStarsEnabled(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository))
m.Get("/times", repo.ListMyTrackedTimes)
m.Get("/stopwatches", repo.GetStopwatches)
m.Get("/subscriptions", user.GetMyWatchedRepos)
@@ -1145,11 +1194,22 @@ func Routes() *web.Router {
m.Post("/accept", repo.AcceptTransfer)
m.Post("/reject", repo.RejectTransfer)
}, reqToken())
- addActionsRoutes(
- m,
- reqOwner(),
- repo.NewAction(),
- )
+
+ addActionsRoutes(m, reqOwner(), repo.NewAction()) // it adds the routes for secrets/variables and runner management
+
+ m.Group("/actions/workflows", func() {
+ m.Get("", repo.ActionsListRepositoryWorkflows)
+ m.Get("/{workflow_id}", repo.ActionsGetWorkflow)
+ m.Put("/{workflow_id}/disable", reqRepoWriter(unit.TypeActions), repo.ActionsDisableWorkflow)
+ m.Put("/{workflow_id}/enable", reqRepoWriter(unit.TypeActions), repo.ActionsEnableWorkflow)
+ m.Post("/{workflow_id}/dispatches", reqRepoWriter(unit.TypeActions), bind(api.CreateActionWorkflowDispatch{}), repo.ActionsDispatchWorkflow)
+ }, context.ReferencesGitRepo(), reqToken(), reqRepoReader(unit.TypeActions))
+
+ m.Group("/actions/jobs", func() {
+ m.Get("/{job_id}", repo.GetWorkflowJob)
+ m.Get("/{job_id}/logs", repo.DownloadActionsRunJobLogs)
+ }, reqToken(), reqRepoReader(unit.TypeActions))
+
m.Group("/hooks/git", func() {
m.Combo("").Get(repo.ListGitHooks)
m.Group("/{id}", func() {
@@ -1187,9 +1247,10 @@ func Routes() *web.Router {
}, reqToken())
m.Get("/raw/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFile)
m.Get("/media/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFileOrLFS)
- m.Get("/archive/*", reqRepoReader(unit.TypeCode), repo.GetArchive)
+ m.Methods("HEAD,GET", "/archive/*", reqRepoReader(unit.TypeCode), repo.GetArchive)
m.Combo("/forks").Get(repo.ListForks).
Post(reqToken(), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork)
+ m.Post("/merge-upstream", reqToken(), mustNotBeArchived, reqRepoWriter(unit.TypeCode), bind(api.MergeUpstreamRequest{}), repo.MergeUpstream)
m.Group("/branches", func() {
m.Get("", repo.ListBranches)
m.Get("/*", repo.GetBranch)
@@ -1224,6 +1285,20 @@ func Routes() *web.Router {
}, reqToken(), reqAdmin())
m.Group("/actions", func() {
m.Get("/tasks", repo.ListActionTasks)
+ m.Group("/runs", func() {
+ m.Group("/{run}", func() {
+ m.Get("", repo.GetWorkflowRun)
+ m.Delete("", reqToken(), reqRepoWriter(unit.TypeActions), repo.DeleteActionRun)
+ m.Get("/jobs", repo.ListWorkflowRunJobs)
+ m.Get("/artifacts", repo.GetArtifactsOfRun)
+ })
+ })
+ m.Get("/artifacts", repo.GetArtifacts)
+ m.Group("/artifacts/{artifact_id}", func() {
+ m.Get("", repo.GetArtifact)
+ m.Delete("", reqRepoWriter(unit.TypeActions), repo.DeleteArtifact)
+ })
+ m.Get("/artifacts/{artifact_id}/zip", repo.DownloadArtifact)
}, reqRepoReader(unit.TypeActions), context.ReferencesGitRepo(true))
m.Group("/keys", func() {
m.Combo("").Get(repo.ListDeployKeys).
@@ -1247,7 +1322,7 @@ func Routes() *web.Router {
m.Post("/markup", reqToken(), bind(api.MarkupOption{}), misc.Markup)
m.Post("/markdown", reqToken(), bind(api.MarkdownOption{}), misc.Markdown)
m.Post("/markdown/raw", reqToken(), misc.MarkdownRaw)
- m.Get("/stargazers", repo.ListStargazers)
+ m.Get("/stargazers", reqStarsEnabled(), repo.ListStargazers)
m.Get("/subscribers", repo.ListSubscribers)
m.Group("/subscription", func() {
m.Get("", user.IsWatching)
@@ -1348,18 +1423,29 @@ func Routes() *web.Router {
m.Get("/tags/{sha}", repo.GetAnnotatedTag)
m.Get("/notes/{sha}", repo.GetNote)
}, context.ReferencesGitRepo(true), reqRepoReader(unit.TypeCode))
- m.Post("/diffpatch", reqRepoWriter(unit.TypeCode), reqToken(), bind(api.ApplyDiffPatchFileOptions{}), mustNotBeArchived, repo.ApplyDiffPatch)
m.Group("/contents", func() {
m.Get("", repo.GetContentsList)
- m.Post("", reqToken(), bind(api.ChangeFilesOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.ChangeFiles)
m.Get("/*", repo.GetContents)
- m.Group("/*", func() {
- m.Post("", bind(api.CreateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.CreateFile)
- m.Put("", bind(api.UpdateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.UpdateFile)
- m.Delete("", bind(api.DeleteFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.DeleteFile)
- }, reqToken())
- }, reqRepoReader(unit.TypeCode))
- m.Get("/signing-key.gpg", misc.SigningKey)
+ m.Group("", func() {
+ // "change file" operations, need permission to write to the target branch provided by the form
+ m.Post("", bind(api.ChangeFilesOptions{}), repo.ReqChangeRepoFileOptionsAndCheck, repo.ChangeFiles)
+ m.Group("/*", func() {
+ m.Post("", bind(api.CreateFileOptions{}), repo.ReqChangeRepoFileOptionsAndCheck, repo.CreateFile)
+ m.Put("", bind(api.UpdateFileOptions{}), repo.ReqChangeRepoFileOptionsAndCheck, repo.UpdateFile)
+ m.Delete("", bind(api.DeleteFileOptions{}), repo.ReqChangeRepoFileOptionsAndCheck, repo.DeleteFile)
+ })
+ m.Post("/diffpatch", bind(api.ApplyDiffPatchFileOptions{}), repo.ReqChangeRepoFileOptionsAndCheck, repo.ApplyDiffPatch)
+ }, mustEnableEditor, reqToken())
+ }, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo())
+ m.Group("/contents-ext", func() {
+ m.Get("", repo.GetContentsExt)
+ m.Get("/*", repo.GetContentsExt)
+ }, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo())
+ m.Combo("/file-contents", reqRepoReader(unit.TypeCode), context.ReferencesGitRepo()).
+ Get(repo.GetFileContentsGet).
+ Post(bind(api.GetFilesOptions{}), repo.GetFileContentsPost) // the POST method requires "write" permission, so we also support "GET" method above
+ m.Get("/signing-key.gpg", misc.SigningKeyGPG)
+ m.Get("/signing-key.pub", misc.SigningKeySSH)
m.Group("/topics", func() {
m.Combo("").Get(repo.ListTopics).
Put(reqToken(), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics)
@@ -1380,10 +1466,14 @@ func Routes() *web.Router {
m.Delete("", repo.DeleteAvatar)
}, reqAdmin(), reqToken())
- m.Get("/{ball_type:tarball|zipball|bundle}/*", reqRepoReader(unit.TypeCode), repo.DownloadArchive)
+ m.Methods("HEAD,GET", "/{ball_type:tarball|zipball|bundle}/*", reqRepoReader(unit.TypeCode), repo.DownloadArchive)
}, repoAssignment(), checkTokenPublicOnly())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository))
+ // Artifacts direct download endpoint authenticates via signed url
+ // it is protected by the "sig" parameter (to help to access private repo), so no need to use other middlewares
+ m.Get("/repos/{username}/{reponame}/actions/artifacts/{artifact_id}/zip/raw", repo.DownloadArtifactRaw)
+
// Notifications (requires notifications scope)
m.Group("/repos", func() {
m.Group("/{username}/{reponame}", func() {
@@ -1488,6 +1578,11 @@ func Routes() *web.Router {
Delete(reqToken(), reqAdmin(), repo.UnpinIssue)
m.Patch("/{position}", reqToken(), reqAdmin(), repo.MoveIssuePin)
})
+ m.Group("/lock", func() {
+ m.Combo("").
+ Put(bind(api.LockIssueOption{}), repo.LockIssue).
+ Delete(repo.UnlockIssue)
+ }, reqToken(), reqAdmin())
})
}, mustEnableIssuesOrPulls)
m.Group("/labels", func() {
@@ -1509,13 +1604,24 @@ func Routes() *web.Router {
// NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs
m.Group("/packages/{username}", func() {
- m.Group("/{type}/{name}/{version}", func() {
- m.Get("", reqToken(), packages.GetPackage)
- m.Delete("", reqToken(), reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage)
- m.Get("/files", reqToken(), packages.ListPackageFiles)
+ m.Group("/{type}/{name}", func() {
+ m.Get("/", packages.ListPackageVersions)
+
+ m.Group("/{version}", func() {
+ m.Get("", packages.GetPackage)
+ m.Delete("", reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage)
+ m.Get("/files", packages.ListPackageFiles)
+ })
+
+ m.Group("/-", func() {
+ m.Get("/latest", packages.GetLatestPackageVersion)
+ m.Post("/link/{repo_name}", reqPackageAccess(perm.AccessModeWrite), packages.LinkPackage)
+ m.Post("/unlink", reqPackageAccess(perm.AccessModeWrite), packages.UnlinkPackage)
+ })
})
- m.Get("/", reqToken(), packages.ListPackages)
- }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead), checkTokenPublicOnly())
+
+ m.Get("/", packages.ListPackages)
+ }, reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead), checkTokenPublicOnly())
// Organizations
m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), org.ListMyOrgs)
@@ -1529,6 +1635,7 @@ func Routes() *web.Router {
m.Combo("").Get(org.Get).
Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit).
Delete(reqToken(), reqOrgOwnership(), org.Delete)
+ m.Post("/rename", reqToken(), reqOrgOwnership(), bind(api.RenameOrgOption{}), org.Rename)
m.Combo("/repos").Get(user.ListOrgRepos).
Post(reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo)
m.Group("/members", func() {
@@ -1643,6 +1750,16 @@ func Routes() *web.Router {
Patch(bind(api.EditHookOption{}), admin.EditHook).
Delete(admin.DeleteHook)
})
+ m.Group("/actions", func() {
+ m.Group("/runners", func() {
+ m.Get("", admin.ListRunners)
+ m.Post("/registration-token", admin.CreateRegistrationToken)
+ m.Get("/{runner_id}", admin.GetRunner)
+ m.Delete("/{runner_id}", admin.DeleteRunner)
+ })
+ m.Get("/runs", admin.ListWorkflowRuns)
+ m.Get("/jobs", admin.ListWorkflowJobs)
+ })
m.Group("/runners", func() {
m.Get("/registration-token", admin.GetRegistrationToken)
})
diff --git a/routers/api/v1/misc/gitignore.go b/routers/api/v1/misc/gitignore.go
index b0bf00a921..1ff2628ce8 100644
--- a/routers/api/v1/misc/gitignore.go
+++ b/routers/api/v1/misc/gitignore.go
@@ -48,7 +48,7 @@ func GetGitignoreTemplateInfo(ctx *context.APIContext) {
text, err := options.Gitignore(name)
if err != nil {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
diff --git a/routers/api/v1/misc/label_templates.go b/routers/api/v1/misc/label_templates.go
index f105b4c684..95c156c4ab 100644
--- a/routers/api/v1/misc/label_templates.go
+++ b/routers/api/v1/misc/label_templates.go
@@ -52,7 +52,7 @@ func GetLabelTemplate(ctx *context.APIContext) {
labels, err := repo_module.LoadTemplateLabelsByDisplayName(name)
if err != nil {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
diff --git a/routers/api/v1/misc/licenses.go b/routers/api/v1/misc/licenses.go
index d99b276232..12670afef9 100644
--- a/routers/api/v1/misc/licenses.go
+++ b/routers/api/v1/misc/licenses.go
@@ -37,7 +37,6 @@ func ListLicenseTemplates(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, response)
}
-// Returns information about a gitignore template
func GetLicenseTemplateInfo(ctx *context.APIContext) {
// swagger:operation GET /licenses/{name} miscellaneous getLicenseTemplateInfo
// ---
@@ -59,7 +58,7 @@ func GetLicenseTemplateInfo(ctx *context.APIContext) {
text, err := options.License(name)
if err != nil {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
diff --git a/routers/api/v1/misc/markup.go b/routers/api/v1/misc/markup.go
index 7b3633552f..909310b4c8 100644
--- a/routers/api/v1/misc/markup.go
+++ b/routers/api/v1/misc/markup.go
@@ -38,11 +38,11 @@ func Markup(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.MarkupOption)
if ctx.HasAPIError() {
- ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg())
+ ctx.APIError(http.StatusUnprocessableEntity, ctx.GetErrMsg())
return
}
- mode := util.Iif(form.Wiki, "wiki", form.Mode) //nolint:staticcheck
+ mode := util.Iif(form.Wiki, "wiki", form.Mode) //nolint:staticcheck // form.Wiki is deprecated
common.RenderMarkup(ctx.Base, ctx.Repo, mode, form.Text, form.Context, form.FilePath)
}
@@ -69,11 +69,11 @@ func Markdown(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.MarkdownOption)
if ctx.HasAPIError() {
- ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg())
+ ctx.APIError(http.StatusUnprocessableEntity, ctx.GetErrMsg())
return
}
- mode := util.Iif(form.Wiki, "wiki", form.Mode) //nolint:staticcheck
+ mode := util.Iif(form.Wiki, "wiki", form.Mode) //nolint:staticcheck // form.Wiki is deprecated
common.RenderMarkup(ctx.Base, ctx.Repo, mode, form.Text, form.Context, "")
}
@@ -100,7 +100,7 @@ func MarkdownRaw(ctx *context.APIContext) {
// "$ref": "#/responses/validationError"
defer ctx.Req.Body.Close()
if err := markdown.RenderRaw(markup.NewRenderContext(ctx), ctx.Req.Body, ctx.Resp); err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
}
diff --git a/routers/api/v1/misc/markup_test.go b/routers/api/v1/misc/markup_test.go
index 6063e54cdc..38a1a3be9e 100644
--- a/routers/api/v1/misc/markup_test.go
+++ b/routers/api/v1/misc/markup_test.go
@@ -134,7 +134,7 @@ Here are some links to the most important topics. You can find the full list of
<h2 id="user-content-quick-links">Quick Links</h2>
<p>Here are some links to the most important topics. You can find the full list of pages at the sidebar.</p>
<p><a href="http://localhost:3000/user2/repo1/wiki/Configuration" rel="nofollow">Configuration</a>
-<a href="http://localhost:3000/user2/repo1/wiki/raw/images/icon-bug.png" rel="nofollow"><img src="http://localhost:3000/user2/repo1/wiki/raw/images/icon-bug.png" title="icon-bug.png" alt="images/icon-bug.png"/></a></p>
+<a href="http://localhost:3000/user2/repo1/wiki/images/icon-bug.png" rel="nofollow"><img src="http://localhost:3000/user2/repo1/wiki/raw/images/icon-bug.png" title="icon-bug.png" alt="images/icon-bug.png"/></a></p>
`,
}
@@ -158,19 +158,19 @@ Here are some links to the most important topics. You can find the full list of
input := "[Link](test.md)\n![Image](image.png)"
testRenderMarkdown(t, "gfm", false, input, `<p><a href="http://localhost:3000/user2/repo1/src/branch/main/test.md" rel="nofollow">Link</a>
-<a href="http://localhost:3000/user2/repo1/media/branch/main/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/user2/repo1/media/branch/main/image.png" alt="Image"/></a></p>
+<a href="http://localhost:3000/user2/repo1/src/branch/main/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/user2/repo1/media/branch/main/image.png" alt="Image"/></a></p>
`, http.StatusOK)
testRenderMarkdown(t, "gfm", false, input, `<p><a href="http://localhost:3000/user2/repo1/src/branch/main/test.md" rel="nofollow">Link</a>
-<a href="http://localhost:3000/user2/repo1/media/branch/main/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/user2/repo1/media/branch/main/image.png" alt="Image"/></a></p>
+<a href="http://localhost:3000/user2/repo1/src/branch/main/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/user2/repo1/media/branch/main/image.png" alt="Image"/></a></p>
`, http.StatusOK)
testRenderMarkup(t, "gfm", false, "", input, `<p><a href="http://localhost:3000/user2/repo1/src/branch/main/test.md" rel="nofollow">Link</a>
-<a href="http://localhost:3000/user2/repo1/media/branch/main/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/user2/repo1/media/branch/main/image.png" alt="Image"/></a></p>
+<a href="http://localhost:3000/user2/repo1/src/branch/main/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/user2/repo1/media/branch/main/image.png" alt="Image"/></a></p>
`, http.StatusOK)
testRenderMarkup(t, "file", false, "path/new-file.md", input, `<p><a href="http://localhost:3000/user2/repo1/src/branch/main/path/test.md" rel="nofollow">Link</a>
-<a href="http://localhost:3000/user2/repo1/media/branch/main/path/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/user2/repo1/media/branch/main/path/image.png" alt="Image"/></a></p>
+<a href="http://localhost:3000/user2/repo1/src/branch/main/path/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/user2/repo1/media/branch/main/path/image.png" alt="Image"/></a></p>
`, http.StatusOK)
testRenderMarkup(t, "file", false, "path/test.unknown", "## Test", "unsupported file to render: \"path/test.unknown\"\n", http.StatusUnprocessableEntity)
diff --git a/routers/api/v1/misc/nodeinfo.go b/routers/api/v1/misc/nodeinfo.go
index 5973724782..ffe50e9fda 100644
--- a/routers/api/v1/misc/nodeinfo.go
+++ b/routers/api/v1/misc/nodeinfo.go
@@ -52,7 +52,7 @@ func NodeInfo(ctx *context.APIContext) {
}
if err := ctx.Cache.PutJSON(cacheKeyNodeInfoUsage, nodeInfoUsage, 180); err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
}
diff --git a/routers/api/v1/misc/signing.go b/routers/api/v1/misc/signing.go
index 24a46c1e70..db70e04b8f 100644
--- a/routers/api/v1/misc/signing.go
+++ b/routers/api/v1/misc/signing.go
@@ -4,15 +4,35 @@
package misc
import (
- "fmt"
- "net/http"
-
+ "code.gitea.io/gitea/modules/git"
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/context"
)
-// SigningKey returns the public key of the default signing key if it exists
-func SigningKey(ctx *context.APIContext) {
+func getSigningKey(ctx *context.APIContext, expectedFormat string) {
+ // if the handler is in the repo's route group, get the repo's signing key
+ // otherwise, get the global signing key
+ path := ""
+ if ctx.Repo != nil && ctx.Repo.Repository != nil {
+ path = ctx.Repo.Repository.RepoPath()
+ }
+ content, format, err := asymkey_service.PublicSigningKey(ctx, path)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ if format == "" {
+ ctx.APIErrorNotFound("no signing key")
+ return
+ } else if format != expectedFormat {
+ ctx.APIErrorNotFound("signing key format is " + format)
+ return
+ }
+ _, _ = ctx.Write([]byte(content))
+}
+
+// SigningKeyGPG returns the public key of the default signing key if it exists
+func SigningKeyGPG(ctx *context.APIContext) {
// swagger:operation GET /signing-key.gpg miscellaneous getSigningKey
// ---
// summary: Get default signing-key.gpg
@@ -45,19 +65,42 @@ func SigningKey(ctx *context.APIContext) {
// description: "GPG armored public key"
// schema:
// type: string
+ getSigningKey(ctx, git.SigningKeyFormatOpenPGP)
+}
- path := ""
- if ctx.Repo != nil && ctx.Repo.Repository != nil {
- path = ctx.Repo.Repository.RepoPath()
- }
+// SigningKeySSH returns the public key of the default signing key if it exists
+func SigningKeySSH(ctx *context.APIContext) {
+ // swagger:operation GET /signing-key.pub miscellaneous getSigningKeySSH
+ // ---
+ // summary: Get default signing-key.pub
+ // produces:
+ // - text/plain
+ // responses:
+ // "200":
+ // description: "ssh public key"
+ // schema:
+ // type: string
- content, err := asymkey_service.PublicSigningKey(ctx, path)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "gpg export", err)
- return
- }
- _, err = ctx.Write([]byte(content))
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "gpg export", fmt.Errorf("Error writing key content %w", err))
- }
+ // swagger:operation GET /repos/{owner}/{repo}/signing-key.pub repository repoSigningKeySSH
+ // ---
+ // summary: Get signing-key.pub for given repository
+ // produces:
+ // - text/plain
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // description: "ssh public key"
+ // schema:
+ // type: string
+ getSigningKey(ctx, git.SigningKeyFormatSSH)
}
diff --git a/routers/api/v1/notify/notifications.go b/routers/api/v1/notify/notifications.go
index 46b3c7f5e7..4e4c7dc6dd 100644
--- a/routers/api/v1/notify/notifications.go
+++ b/routers/api/v1/notify/notifications.go
@@ -28,7 +28,7 @@ func NewAvailable(ctx *context.APIContext) {
Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread},
})
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "db.Count[activities_model.Notification]", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
@@ -38,7 +38,7 @@ func NewAvailable(ctx *context.APIContext) {
func getFindNotificationOptions(ctx *context.APIContext) *activities_model.FindNotificationOptions {
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return nil
}
opts := &activities_model.FindNotificationOptions{
diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go
index 1744426ee8..e87054e26c 100644
--- a/routers/api/v1/notify/repo.go
+++ b/routers/api/v1/notify/repo.go
@@ -110,18 +110,18 @@ func ListRepoNotifications(ctx *context.APIContext) {
totalCount, err := db.Count[activities_model.Notification](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
nl, err := db.Find[activities_model.Notification](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
err = activities_model.NotificationList(nl).LoadAttributes(ctx)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -183,7 +183,7 @@ func ReadRepoNotifications(ctx *context.APIContext) {
if len(qLastRead) > 0 {
tmpLastRead, err := time.Parse(time.RFC3339, qLastRead)
if err != nil {
- ctx.Error(http.StatusBadRequest, "Parse", err)
+ ctx.APIError(http.StatusBadRequest, err)
return
}
if !tmpLastRead.IsZero() {
@@ -203,7 +203,7 @@ func ReadRepoNotifications(ctx *context.APIContext) {
}
nl, err := db.Find[activities_model.Notification](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -217,7 +217,7 @@ func ReadRepoNotifications(ctx *context.APIContext) {
for _, n := range nl {
notif, err := activities_model.SetNotificationStatus(ctx, n.ID, ctx.Doer, targetStatus)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
_ = notif.LoadAttributes(ctx)
diff --git a/routers/api/v1/notify/threads.go b/routers/api/v1/notify/threads.go
index 58a38cfd18..dd77e4aae4 100644
--- a/routers/api/v1/notify/threads.go
+++ b/routers/api/v1/notify/threads.go
@@ -42,7 +42,7 @@ func GetThread(ctx *context.APIContext) {
return
}
if err := n.LoadAttributes(ctx); err != nil && !issues_model.IsErrCommentNotExist(err) {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -90,11 +90,11 @@ func ReadThread(ctx *context.APIContext) {
notif, err := activities_model.SetNotificationStatus(ctx, n.ID, ctx.Doer, targetStatus)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
if err = notif.LoadAttributes(ctx); err != nil && !issues_model.IsErrCommentNotExist(err) {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusResetContent, convert.ToNotificationThread(ctx, notif))
@@ -104,14 +104,14 @@ func getThread(ctx *context.APIContext) *activities_model.Notification {
n, err := activities_model.GetNotificationByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if db.IsErrNotExist(err) {
- ctx.Error(http.StatusNotFound, "GetNotificationByID", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
}
return nil
}
if n.UserID != ctx.Doer.ID && !ctx.Doer.IsAdmin {
- ctx.Error(http.StatusForbidden, "GetNotificationByID", fmt.Errorf("only user itself and admin are allowed to read/change this thread %d", n.ID))
+ ctx.APIError(http.StatusForbidden, fmt.Errorf("only user itself and admin are allowed to read/change this thread %d", n.ID))
return nil
}
return n
diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go
index 879f484cce..3ebb678835 100644
--- a/routers/api/v1/notify/user.go
+++ b/routers/api/v1/notify/user.go
@@ -71,18 +71,18 @@ func ListNotifications(ctx *context.APIContext) {
totalCount, err := db.Count[activities_model.Notification](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
nl, err := db.Find[activities_model.Notification](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
err = activities_model.NotificationList(nl).LoadAttributes(ctx)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -133,7 +133,7 @@ func ReadNotifications(ctx *context.APIContext) {
if len(qLastRead) > 0 {
tmpLastRead, err := time.Parse(time.RFC3339, qLastRead)
if err != nil {
- ctx.Error(http.StatusBadRequest, "Parse", err)
+ ctx.APIError(http.StatusBadRequest, err)
return
}
if !tmpLastRead.IsZero() {
@@ -150,7 +150,7 @@ func ReadNotifications(ctx *context.APIContext) {
}
nl, err := db.Find[activities_model.Notification](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -164,7 +164,7 @@ func ReadNotifications(ctx *context.APIContext) {
for _, n := range nl {
notif, err := activities_model.SetNotificationStatus(ctx, n.ID, ctx.Doer, targetStatus)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
_ = notif.LoadAttributes(ctx)
diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go
index 199ee7d777..3ae5e60585 100644
--- a/routers/api/v1/org/action.go
+++ b/routers/api/v1/org/action.go
@@ -54,15 +54,16 @@ func (Action) ListActionsSecrets(ctx *context.APIContext) {
secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
apiSecrets := make([]*api.Secret, len(secrets))
for k, v := range secrets {
apiSecrets[k] = &api.Secret{
- Name: v.Name,
- Created: v.CreatedUnix.AsTime(),
+ Name: v.Name,
+ Description: v.Description,
+ Created: v.CreatedUnix.AsTime(),
}
}
@@ -106,14 +107,14 @@ func (Action) CreateOrUpdateSecret(ctx *context.APIContext) {
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
- _, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("secretname"), opt.Data)
+ _, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("secretname"), opt.Data, opt.Description)
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "CreateOrUpdateSecret", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -156,11 +157,11 @@ func (Action) DeleteSecret(ctx *context.APIContext) {
err := secret_service.DeleteSecretByName(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("secretname"))
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "DeleteSecret", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "DeleteSecret", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteSecret", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -189,6 +190,27 @@ func (Action) GetRegistrationToken(ctx *context.APIContext) {
shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0)
}
+// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization
+// CreateRegistrationToken returns the token to register org runners
+func (Action) CreateRegistrationToken(ctx *context.APIContext) {
+ // swagger:operation POST /orgs/{org}/actions/runners/registration-token organization orgCreateRunnerRegistrationToken
+ // ---
+ // summary: Get an organization's actions runner registration token
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: org
+ // in: path
+ // description: name of the organization
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/RegistrationToken"
+
+ shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0)
+}
+
// ListVariables list org-level variables
func (Action) ListVariables(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/variables organization getOrgVariablesList
@@ -223,17 +245,18 @@ func (Action) ListVariables(ctx *context.APIContext) {
ListOptions: utils.GetListOptions(ctx),
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindVariables", err)
+ ctx.APIErrorInternal(err)
return
}
variables := make([]*api.ActionVariable, len(vars))
for i, v := range vars {
variables[i] = &api.ActionVariable{
- OwnerID: v.OwnerID,
- RepoID: v.RepoID,
- Name: v.Name,
- Data: v.Data,
+ OwnerID: v.OwnerID,
+ RepoID: v.RepoID,
+ Name: v.Name,
+ Data: v.Data,
+ Description: v.Description,
}
}
@@ -273,18 +296,19 @@ func (Action) GetVariable(ctx *context.APIContext) {
})
if err != nil {
if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "GetVariable", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetVariable", err)
+ ctx.APIErrorInternal(err)
}
return
}
variable := &api.ActionVariable{
- OwnerID: v.OwnerID,
- RepoID: v.RepoID,
- Name: v.Name,
- Data: v.Data,
+ OwnerID: v.OwnerID,
+ RepoID: v.RepoID,
+ Name: v.Name,
+ Data: v.Data,
+ Description: v.Description,
}
ctx.JSON(http.StatusOK, variable)
@@ -322,11 +346,11 @@ func (Action) DeleteVariable(ctx *context.APIContext) {
if err := actions_service.DeleteVariableByName(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("variablename")); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "DeleteVariableByName", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "DeleteVariableByName", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteVariableByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -360,13 +384,13 @@ func (Action) CreateVariable(ctx *context.APIContext) {
// "$ref": "#/definitions/CreateVariableOption"
// responses:
// "201":
- // description: response when creating an org-level variable
- // "204":
- // description: response when creating an org-level variable
+ // description: successfully created the org-level variable
// "400":
// "$ref": "#/responses/error"
- // "404":
- // "$ref": "#/responses/notFound"
+ // "409":
+ // description: variable name already exists.
+ // "500":
+ // "$ref": "#/responses/error"
opt := web.GetForm(ctx).(*api.CreateVariableOption)
@@ -378,24 +402,24 @@ func (Action) CreateVariable(ctx *context.APIContext) {
Name: variableName,
})
if err != nil && !errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusInternalServerError, "GetVariable", err)
+ ctx.APIErrorInternal(err)
return
}
if v != nil && v.ID > 0 {
- ctx.Error(http.StatusConflict, "VariableNameAlreadyExists", util.NewAlreadyExistErrorf("variable name %s already exists", variableName))
+ ctx.APIError(http.StatusConflict, util.NewAlreadyExistErrorf("variable name %s already exists", variableName))
return
}
- if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value); err != nil {
+ if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value, opt.Description); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "CreateVariable", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- ctx.Error(http.StatusInternalServerError, "CreateVariable", err)
+ ctx.APIErrorInternal(err)
}
return
}
- ctx.Status(http.StatusNoContent)
+ ctx.Status(http.StatusCreated)
}
// UpdateVariable update an org-level variable
@@ -440,9 +464,9 @@ func (Action) UpdateVariable(ctx *context.APIContext) {
})
if err != nil {
if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "GetVariable", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetVariable", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -450,11 +474,16 @@ func (Action) UpdateVariable(ctx *context.APIContext) {
if opt.Name == "" {
opt.Name = ctx.PathParam("variablename")
}
- if _, err := actions_service.UpdateVariable(ctx, v.ID, opt.Name, opt.Value); err != nil {
+
+ v.Name = opt.Name
+ v.Data = opt.Value
+ v.Description = opt.Description
+
+ if _, err := actions_service.UpdateVariableNameData(ctx, v); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "UpdateVariable", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- ctx.Error(http.StatusInternalServerError, "UpdateVariable", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -462,6 +491,175 @@ func (Action) UpdateVariable(ctx *context.APIContext) {
ctx.Status(http.StatusNoContent)
}
+// ListRunners get org-level runners
+func (Action) ListRunners(ctx *context.APIContext) {
+ // swagger:operation GET /orgs/{org}/actions/runners organization getOrgRunners
+ // ---
+ // summary: Get org-level runners
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: org
+ // in: path
+ // description: name of the organization
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/definitions/ActionRunnersResponse"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.ListRunners(ctx, ctx.Org.Organization.ID, 0)
+}
+
+// GetRunner get an org-level runner
+func (Action) GetRunner(ctx *context.APIContext) {
+ // swagger:operation GET /orgs/{org}/actions/runners/{runner_id} organization getOrgRunner
+ // ---
+ // summary: Get an org-level runner
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: org
+ // in: path
+ // description: name of the organization
+ // type: string
+ // required: true
+ // - name: runner_id
+ // in: path
+ // description: id of the runner
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/definitions/ActionRunner"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.GetRunner(ctx, ctx.Org.Organization.ID, 0, ctx.PathParamInt64("runner_id"))
+}
+
+// DeleteRunner delete an org-level runner
+func (Action) DeleteRunner(ctx *context.APIContext) {
+ // swagger:operation DELETE /orgs/{org}/actions/runners/{runner_id} organization deleteOrgRunner
+ // ---
+ // summary: Delete an org-level runner
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: org
+ // in: path
+ // description: name of the organization
+ // type: string
+ // required: true
+ // - name: runner_id
+ // in: path
+ // description: id of the runner
+ // type: string
+ // required: true
+ // responses:
+ // "204":
+ // description: runner has been deleted
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.DeleteRunner(ctx, ctx.Org.Organization.ID, 0, ctx.PathParamInt64("runner_id"))
+}
+
+func (Action) ListWorkflowJobs(ctx *context.APIContext) {
+ // swagger:operation GET /orgs/{org}/actions/jobs organization getOrgWorkflowJobs
+ // ---
+ // summary: Get org-level workflow jobs
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: org
+ // in: path
+ // description: name of the organization
+ // type: string
+ // required: true
+ // - name: status
+ // in: query
+ // description: workflow status (pending, queued, in_progress, failure, success, skipped)
+ // type: string
+ // required: false
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results
+ // type: integer
+ // responses:
+ // "200":
+ // "$ref": "#/responses/WorkflowJobsList"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.ListJobs(ctx, ctx.Org.Organization.ID, 0, 0)
+}
+
+func (Action) ListWorkflowRuns(ctx *context.APIContext) {
+ // swagger:operation GET /orgs/{org}/actions/runs organization getOrgWorkflowRuns
+ // ---
+ // summary: Get org-level workflow runs
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: org
+ // in: path
+ // description: name of the organization
+ // type: string
+ // required: true
+ // - name: event
+ // in: query
+ // description: workflow event name
+ // type: string
+ // required: false
+ // - name: branch
+ // in: query
+ // description: workflow branch
+ // type: string
+ // required: false
+ // - name: status
+ // in: query
+ // description: workflow status (pending, queued, in_progress, failure, success, skipped)
+ // type: string
+ // required: false
+ // - name: actor
+ // in: query
+ // description: triggered by user
+ // type: string
+ // required: false
+ // - name: head_sha
+ // in: query
+ // description: triggering sha of the workflow run
+ // type: string
+ // required: false
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results
+ // type: integer
+ // responses:
+ // "200":
+ // "$ref": "#/responses/WorkflowRunsList"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.ListRuns(ctx, ctx.Org.Organization.ID, 0)
+}
+
var _ actions_service.API = new(Action)
// Action implements actions_service.API
diff --git a/routers/api/v1/org/avatar.go b/routers/api/v1/org/avatar.go
index f11eb6c1cd..0eb771b2cd 100644
--- a/routers/api/v1/org/avatar.go
+++ b/routers/api/v1/org/avatar.go
@@ -39,13 +39,13 @@ func UpdateAvatar(ctx *context.APIContext) {
content, err := base64.StdEncoding.DecodeString(form.Image)
if err != nil {
- ctx.Error(http.StatusBadRequest, "DecodeImage", err)
+ ctx.APIError(http.StatusBadRequest, err)
return
}
err = user_service.UploadAvatar(ctx, ctx.Org.Organization.AsUser(), content)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "UploadAvatar", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -72,7 +72,7 @@ func DeleteAvatar(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
err := user_service.DeleteAvatar(ctx, ctx.Org.Organization.AsUser())
if err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/org/block.go b/routers/api/v1/org/block.go
index 69a5222a20..6b2f3dc615 100644
--- a/routers/api/v1/org/block.go
+++ b/routers/api/v1/org/block.go
@@ -47,7 +47,7 @@ func CheckUserBlock(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
- // description: user to check
+ // description: username of the user to check
// type: string
// required: true
// responses:
@@ -71,7 +71,7 @@ func BlockUser(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
- // description: user to block
+ // description: username of the user to block
// type: string
// required: true
// - name: note
@@ -101,7 +101,7 @@ func UnblockUser(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
- // description: user to unblock
+ // description: username of the user to unblock
// type: string
// required: true
// responses:
diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go
index df82f4e5a2..f9e0684a97 100644
--- a/routers/api/v1/org/hook.go
+++ b/routers/api/v1/org/hook.go
@@ -78,7 +78,7 @@ func GetHook(ctx *context.APIContext) {
apiHook, err := webhook_service.ToHook(ctx.ContextUser.HomeLink(), hook)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, apiHook)
diff --git a/routers/api/v1/org/label.go b/routers/api/v1/org/label.go
index 2a9bd92e87..b5b70bdc7d 100644
--- a/routers/api/v1/org/label.go
+++ b/routers/api/v1/org/label.go
@@ -46,13 +46,13 @@ func ListLabels(ctx *context.APIContext) {
labels, err := issues_model.GetLabelsByOrgID(ctx, ctx.Org.Organization.ID, ctx.FormString("sort"), utils.GetListOptions(ctx))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLabelsByOrgID", err)
+ ctx.APIErrorInternal(err)
return
}
count, err := issues_model.CountLabelsByOrgID(ctx, ctx.Org.Organization.ID)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -90,7 +90,7 @@ func CreateLabel(ctx *context.APIContext) {
form.Color = strings.Trim(form.Color, " ")
color, err := label.NormalizeColor(form.Color)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "Color", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
form.Color = color
@@ -103,7 +103,7 @@ func CreateLabel(ctx *context.APIContext) {
Description: form.Description,
}
if err := issues_model.NewLabel(ctx, label); err != nil {
- ctx.Error(http.StatusInternalServerError, "NewLabel", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -147,9 +147,9 @@ func GetLabel(ctx *context.APIContext) {
}
if err != nil {
if issues_model.IsErrOrgLabelNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetLabelByOrgID", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -193,9 +193,9 @@ func EditLabel(ctx *context.APIContext) {
l, err := issues_model.GetLabelInOrgByID(ctx, ctx.Org.Organization.ID, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrOrgLabelNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetLabelByRepoID", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -209,7 +209,7 @@ func EditLabel(ctx *context.APIContext) {
if form.Color != nil {
color, err := label.NormalizeColor(*form.Color)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "Color", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
l.Color = color
@@ -219,7 +219,7 @@ func EditLabel(ctx *context.APIContext) {
}
l.SetArchived(form.IsArchived != nil && *form.IsArchived)
if err := issues_model.UpdateLabel(ctx, l); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateLabel", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -250,7 +250,7 @@ func DeleteLabel(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if err := issues_model.DeleteLabel(ctx, ctx.Org.Organization.ID, ctx.PathParamInt64("id")); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteLabel", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go
index 23c7da3d96..1c12b0cc94 100644
--- a/routers/api/v1/org/member.go
+++ b/routers/api/v1/org/member.go
@@ -8,6 +8,7 @@ import (
"net/url"
"code.gitea.io/gitea/models/organization"
+ user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/user"
@@ -28,13 +29,13 @@ func listMembers(ctx *context.APIContext, isMember bool) {
count, err := organization.CountOrgMembers(ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
members, _, err := organization.FindOrgMembers(ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -82,7 +83,7 @@ func ListMembers(ctx *context.APIContext) {
if ctx.Doer != nil {
isMember, err = ctx.Org.Organization.IsOrgMember(ctx, ctx.Doer.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsOrgMember", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -132,7 +133,7 @@ func IsMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
- // description: username of the user
+ // description: username of the user to check for an organization membership
// type: string
// required: true
// responses:
@@ -150,20 +151,20 @@ func IsMember(ctx *context.APIContext) {
if ctx.Doer != nil {
userIsMember, err := ctx.Org.Organization.IsOrgMember(ctx, ctx.Doer.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsOrgMember", err)
+ ctx.APIErrorInternal(err)
return
} else if userIsMember || ctx.Doer.IsAdmin {
userToCheckIsMember, err := ctx.Org.Organization.IsOrgMember(ctx, userToCheck.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsOrgMember", err)
+ ctx.APIErrorInternal(err)
} else if userToCheckIsMember {
ctx.Status(http.StatusNoContent)
} else {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
}
return
} else if ctx.Doer.ID == userToCheck.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
}
@@ -185,7 +186,7 @@ func IsPublicMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
- // description: username of the user
+ // description: username of the user to check for a public organization membership
// type: string
// required: true
// responses:
@@ -200,13 +201,27 @@ func IsPublicMember(ctx *context.APIContext) {
}
is, err := organization.IsPublicMembership(ctx, ctx.Org.Organization.ID, userToCheck.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsPublicMembership", err)
+ ctx.APIErrorInternal(err)
return
}
if is {
ctx.Status(http.StatusNoContent)
} else {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
+ }
+}
+
+func checkCanChangeOrgUserStatus(ctx *context.APIContext, targetUser *user_model.User) {
+ // allow user themselves to change their status, and allow admins to change any user
+ if targetUser.ID == ctx.Doer.ID || ctx.Doer.IsAdmin {
+ return
+ }
+ // allow org owners to change status of members
+ isOwner, err := ctx.Org.Organization.IsOwnedBy(ctx, ctx.Doer.ID)
+ if err != nil {
+ ctx.APIError(http.StatusInternalServerError, err)
+ } else if !isOwner {
+ ctx.APIError(http.StatusForbidden, "Cannot change member visibility")
}
}
@@ -225,7 +240,7 @@ func PublicizeMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
- // description: username of the user
+ // description: username of the user whose membership is to be publicized
// type: string
// required: true
// responses:
@@ -240,13 +255,13 @@ func PublicizeMember(ctx *context.APIContext) {
if ctx.Written() {
return
}
- if userToPublicize.ID != ctx.Doer.ID {
- ctx.Error(http.StatusForbidden, "", "Cannot publicize another member")
+ checkCanChangeOrgUserStatus(ctx, userToPublicize)
+ if ctx.Written() {
return
}
err := organization.ChangeOrgUserStatus(ctx, ctx.Org.Organization.ID, userToPublicize.ID, true)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ChangeOrgUserStatus", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
@@ -267,7 +282,7 @@ func ConcealMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
- // description: username of the user
+ // description: username of the user whose membership is to be concealed
// type: string
// required: true
// responses:
@@ -282,13 +297,13 @@ func ConcealMember(ctx *context.APIContext) {
if ctx.Written() {
return
}
- if userToConceal.ID != ctx.Doer.ID {
- ctx.Error(http.StatusForbidden, "", "Cannot conceal another member")
+ checkCanChangeOrgUserStatus(ctx, userToConceal)
+ if ctx.Written() {
return
}
err := organization.ChangeOrgUserStatus(ctx, ctx.Org.Organization.ID, userToConceal.ID, false)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ChangeOrgUserStatus", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
@@ -309,7 +324,7 @@ func DeleteMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
- // description: username of the user
+ // description: username of the user to remove from the organization
// type: string
// required: true
// responses:
@@ -323,7 +338,7 @@ func DeleteMember(ctx *context.APIContext) {
return
}
if err := org_service.RemoveOrgUser(ctx, ctx.Org.Organization, member); err != nil {
- ctx.Error(http.StatusInternalServerError, "RemoveOrgUser", err)
+ ctx.APIErrorInternal(err)
}
ctx.Status(http.StatusNoContent)
}
diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go
index d65f922434..05744ba155 100644
--- a/routers/api/v1/org/org.go
+++ b/routers/api/v1/org/org.go
@@ -26,16 +26,14 @@ import (
func listUserOrgs(ctx *context.APIContext, u *user_model.User) {
listOptions := utils.GetListOptions(ctx)
- showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == u.ID)
-
opts := organization.FindOrgOptions{
- ListOptions: listOptions,
- UserID: u.ID,
- IncludePrivate: showPrivate,
+ ListOptions: listOptions,
+ UserID: u.ID,
+ IncludeVisibility: organization.DoerViewOtherVisibility(ctx.Doer, u),
}
orgs, maxResults, err := db.FindAndCount[organization.Organization](ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "db.FindAndCount[organization.Organization]", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -84,7 +82,7 @@ func ListUserOrgs(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user whose organizations are to be listed
// type: string
// required: true
// - name: page
@@ -114,7 +112,7 @@ func GetUserOrgsPermissions(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user whose permissions are to be obtained
// type: string
// required: true
// - name: org
@@ -138,14 +136,14 @@ func GetUserOrgsPermissions(ctx *context.APIContext) {
op := api.OrganizationPermissions{}
if !organization.HasOrgOrUserVisible(ctx, o, ctx.ContextUser) {
- ctx.NotFound("HasOrgOrUserVisible", nil)
+ ctx.APIErrorNotFound("HasOrgOrUserVisible", nil)
return
}
org := organization.OrgFromUser(o)
authorizeLevel, err := org.GetOrgUserMaxAuthorizeLevel(ctx, ctx.ContextUser.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetOrgUserAuthorizeLevel", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -164,7 +162,7 @@ func GetUserOrgsPermissions(ctx *context.APIContext) {
op.CanCreateRepository, err = org.CanCreateOrgRepo(ctx, ctx.ContextUser.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "CanCreateOrgRepo", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -201,7 +199,7 @@ func GetAll(ctx *context.APIContext) {
listOptions := utils.GetListOptions(ctx)
- publicOrgs, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
+ publicOrgs, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer,
ListOptions: listOptions,
Type: user_model.UserTypeOrganization,
@@ -209,7 +207,7 @@ func GetAll(ctx *context.APIContext) {
Visible: vMode,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "SearchOrganizations", err)
+ ctx.APIErrorInternal(err)
return
}
orgs := make([]*api.Organization, len(publicOrgs))
@@ -245,7 +243,7 @@ func Create(ctx *context.APIContext) {
// "$ref": "#/responses/validationError"
form := web.GetForm(ctx).(*api.CreateOrgOption)
if !ctx.Doer.CanCreateOrganization() {
- ctx.Error(http.StatusForbidden, "Create organization not allowed", nil)
+ ctx.APIError(http.StatusForbidden, nil)
return
}
@@ -271,9 +269,9 @@ func Create(ctx *context.APIContext) {
db.IsErrNameReserved(err) ||
db.IsErrNameCharsNotAllowed(err) ||
db.IsErrNamePatternNotAllowed(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "CreateOrganization", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -301,7 +299,7 @@ func Get(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if !organization.HasOrgOrUserVisible(ctx, ctx.Org.Organization.AsUser(), ctx.Doer) {
- ctx.NotFound("HasOrgOrUserVisible", nil)
+ ctx.APIErrorNotFound("HasOrgOrUserVisible", nil)
return
}
@@ -315,6 +313,44 @@ func Get(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, org)
}
+func Rename(ctx *context.APIContext) {
+ // swagger:operation POST /orgs/{org}/rename organization renameOrg
+ // ---
+ // summary: Rename an organization
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: org
+ // in: path
+ // description: existing org name
+ // type: string
+ // required: true
+ // - name: body
+ // in: body
+ // required: true
+ // schema:
+ // "$ref": "#/definitions/RenameOrgOption"
+ // responses:
+ // "204":
+ // "$ref": "#/responses/empty"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "422":
+ // "$ref": "#/responses/validationError"
+
+ form := web.GetForm(ctx).(*api.RenameOrgOption)
+ orgUser := ctx.Org.Organization.AsUser()
+ if err := user_service.RenameUser(ctx, orgUser, form.NewName); err != nil {
+ if user_model.IsErrUserAlreadyExist(err) || db.IsErrNameReserved(err) || db.IsErrNamePatternNotAllowed(err) || db.IsErrNameCharsNotAllowed(err) {
+ ctx.APIError(http.StatusUnprocessableEntity, err)
+ } else {
+ ctx.APIErrorInternal(err)
+ }
+ return
+ }
+ ctx.Status(http.StatusNoContent)
+}
+
// Edit change an organization's information
func Edit(ctx *context.APIContext) {
// swagger:operation PATCH /orgs/{org} organization orgEdit
@@ -345,7 +381,7 @@ func Edit(ctx *context.APIContext) {
if form.Email != "" {
if err := user_service.ReplacePrimaryEmailAddress(ctx, ctx.Org.Organization.AsUser(), form.Email); err != nil {
- ctx.Error(http.StatusInternalServerError, "ReplacePrimaryEmailAddress", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -359,7 +395,7 @@ func Edit(ctx *context.APIContext) {
RepoAdminChangeTeamAccess: optional.FromPtr(form.RepoAdminChangeTeamAccess),
}
if err := user_service.UpdateUser(ctx, ctx.Org.Organization.AsUser(), opts); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateUser", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -386,7 +422,7 @@ func Delete(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if err := org.DeleteOrganization(ctx, ctx.Org.Organization, false); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteOrganization", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
@@ -431,7 +467,7 @@ func ListOrgActivityFeeds(ctx *context.APIContext) {
org := organization.OrgFromUser(ctx.ContextUser)
isMember, err := org.IsOrgMember(ctx, ctx.Doer.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsOrgMember", err)
+ ctx.APIErrorInternal(err)
return
}
includePrivate = isMember
@@ -450,7 +486,7 @@ func ListOrgActivityFeeds(ctx *context.APIContext) {
feeds, count, err := feed_service.GetFeeds(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetFeeds", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.SetTotalCountHeader(count)
diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go
index 7f44f6ed95..1a1710750a 100644
--- a/routers/api/v1/org/team.go
+++ b/routers/api/v1/org/team.go
@@ -59,13 +59,13 @@ func ListTeams(ctx *context.APIContext) {
OrgID: ctx.Org.Organization.ID,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadTeams", err)
+ ctx.APIErrorInternal(err)
return
}
apiTeams, err := convert.ToTeams(ctx, teams, false)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ConvertToTeams", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -98,13 +98,13 @@ func ListUserTeams(ctx *context.APIContext) {
UserID: ctx.Doer.ID,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserTeams", err)
+ ctx.APIErrorInternal(err)
return
}
apiTeams, err := convert.ToTeams(ctx, teams, true)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ConvertToTeams", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -134,33 +134,25 @@ func GetTeam(ctx *context.APIContext) {
apiTeam, err := convert.ToTeam(ctx, ctx.Org.Team, true)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, apiTeam)
}
-func attachTeamUnits(team *organization.Team, units []string) {
+func attachTeamUnits(team *organization.Team, defaultAccessMode perm.AccessMode, units []string) {
unitTypes, _ := unit_model.FindUnitTypes(units...)
team.Units = make([]*organization.TeamUnit, 0, len(units))
for _, tp := range unitTypes {
team.Units = append(team.Units, &organization.TeamUnit{
OrgID: team.OrgID,
Type: tp,
- AccessMode: team.AccessMode,
+ AccessMode: defaultAccessMode,
})
}
}
-func convertUnitsMap(unitsMap map[string]string) map[unit_model.Type]perm.AccessMode {
- res := make(map[unit_model.Type]perm.AccessMode, len(unitsMap))
- for unitKey, p := range unitsMap {
- res[unit_model.TypeFromKey(unitKey)] = perm.ParseAccessMode(p)
- }
- return res
-}
-
func attachTeamUnitsMap(team *organization.Team, unitsMap map[string]string) {
team.Units = make([]*organization.TeamUnit, 0, len(unitsMap))
for unitKey, p := range unitsMap {
@@ -214,26 +206,24 @@ func CreateTeam(ctx *context.APIContext) {
// "422":
// "$ref": "#/responses/validationError"
form := web.GetForm(ctx).(*api.CreateTeamOption)
- p := perm.ParseAccessMode(form.Permission)
- if p < perm.AccessModeAdmin && len(form.UnitsMap) > 0 {
- p = unit_model.MinUnitAccessMode(convertUnitsMap(form.UnitsMap))
- }
+ teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin)
team := &organization.Team{
OrgID: ctx.Org.Organization.ID,
Name: form.Name,
Description: form.Description,
IncludesAllRepositories: form.IncludesAllRepositories,
CanCreateOrgRepo: form.CanCreateOrgRepo,
- AccessMode: p,
+ AccessMode: teamPermission,
}
if team.AccessMode < perm.AccessModeAdmin {
if len(form.UnitsMap) > 0 {
attachTeamUnitsMap(team, form.UnitsMap)
} else if len(form.Units) > 0 {
- attachTeamUnits(team, form.Units)
+ unitPerm := perm.ParseAccessMode(form.Permission, perm.AccessModeRead, perm.AccessModeWrite)
+ attachTeamUnits(team, unitPerm, form.Units)
} else {
- ctx.Error(http.StatusInternalServerError, "getTeamUnits", errors.New("units permission should not be empty"))
+ ctx.APIErrorInternal(errors.New("units permission should not be empty"))
return
}
} else {
@@ -242,16 +232,16 @@ func CreateTeam(ctx *context.APIContext) {
if err := org_service.NewTeam(ctx, team); err != nil {
if organization.IsErrTeamAlreadyExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "NewTeam", err)
+ ctx.APIErrorInternal(err)
}
return
}
apiTeam, err := convert.ToTeam(ctx, team, true)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusCreated, apiTeam)
@@ -285,7 +275,7 @@ func EditTeam(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.EditTeamOption)
team := ctx.Org.Team
if err := team.LoadUnits(ctx); err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -304,15 +294,10 @@ func EditTeam(ctx *context.APIContext) {
isAuthChanged := false
isIncludeAllChanged := false
if !team.IsOwnerTeam() && len(form.Permission) != 0 {
- // Validate permission level.
- p := perm.ParseAccessMode(form.Permission)
- if p < perm.AccessModeAdmin && len(form.UnitsMap) > 0 {
- p = unit_model.MinUnitAccessMode(convertUnitsMap(form.UnitsMap))
- }
-
- if team.AccessMode != p {
+ teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin)
+ if team.AccessMode != teamPermission {
isAuthChanged = true
- team.AccessMode = p
+ team.AccessMode = teamPermission
}
if form.IncludesAllRepositories != nil {
@@ -325,20 +310,21 @@ func EditTeam(ctx *context.APIContext) {
if len(form.UnitsMap) > 0 {
attachTeamUnitsMap(team, form.UnitsMap)
} else if len(form.Units) > 0 {
- attachTeamUnits(team, form.Units)
+ unitPerm := perm.ParseAccessMode(form.Permission, perm.AccessModeRead, perm.AccessModeWrite)
+ attachTeamUnits(team, unitPerm, form.Units)
}
} else {
attachAdminTeamUnits(team)
}
if err := org_service.UpdateTeam(ctx, team, isAuthChanged, isIncludeAllChanged); err != nil {
- ctx.Error(http.StatusInternalServerError, "EditTeam", err)
+ ctx.APIErrorInternal(err)
return
}
apiTeam, err := convert.ToTeam(ctx, team)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, apiTeam)
@@ -363,7 +349,7 @@ func DeleteTeam(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if err := org_service.DeleteTeam(ctx, ctx.Org.Team); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteTeam", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
@@ -399,10 +385,10 @@ func GetTeamMembers(ctx *context.APIContext) {
isMember, err := organization.IsOrganizationMember(ctx, ctx.Org.Team.OrgID, ctx.Doer.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsOrganizationMember", err)
+ ctx.APIErrorInternal(err)
return
} else if !isMember && !ctx.Doer.IsAdmin {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -411,7 +397,7 @@ func GetTeamMembers(ctx *context.APIContext) {
TeamID: ctx.Org.Team.ID,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetTeamMembers", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -440,7 +426,7 @@ func GetTeamMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
- // description: username of the member to list
+ // description: username of the user whose data is to be listed
// type: string
// required: true
// responses:
@@ -456,10 +442,10 @@ func GetTeamMember(ctx *context.APIContext) {
teamID := ctx.PathParamInt64("teamid")
isTeamMember, err := organization.IsUserInTeams(ctx, u.ID, []int64{teamID})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsUserInTeams", err)
+ ctx.APIErrorInternal(err)
return
} else if !isTeamMember {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
ctx.JSON(http.StatusOK, convert.ToUser(ctx, u, ctx.Doer))
@@ -481,7 +467,7 @@ func AddTeamMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
- // description: username of the user to add
+ // description: username of the user to add to a team
// type: string
// required: true
// responses:
@@ -498,9 +484,9 @@ func AddTeamMember(ctx *context.APIContext) {
}
if err := org_service.AddTeamMember(ctx, ctx.Org.Team, u); err != nil {
if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, "AddTeamMember", err)
+ ctx.APIError(http.StatusForbidden, err)
} else {
- ctx.Error(http.StatusInternalServerError, "AddTeamMember", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -523,7 +509,7 @@ func RemoveTeamMember(ctx *context.APIContext) {
// required: true
// - name: username
// in: path
- // description: username of the user to remove
+ // description: username of the user to remove from a team
// type: string
// required: true
// responses:
@@ -538,7 +524,7 @@ func RemoveTeamMember(ctx *context.APIContext) {
}
if err := org_service.RemoveTeamMember(ctx, ctx.Org.Team, u); err != nil {
- ctx.Error(http.StatusInternalServerError, "RemoveTeamMember", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
@@ -578,14 +564,14 @@ func GetTeamRepos(ctx *context.APIContext) {
TeamID: team.ID,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetTeamRepositories", err)
+ ctx.APIErrorInternal(err)
return
}
repos := make([]*api.Repository, len(teamRepos))
for i, repo := range teamRepos {
permission, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
+ ctx.APIErrorInternal(err)
return
}
repos[i] = convert.ToRepo(ctx, repo, permission)
@@ -630,13 +616,13 @@ func GetTeamRepo(ctx *context.APIContext) {
}
if !organization.HasTeamRepo(ctx, ctx.Org.Team.OrgID, ctx.Org.Team.ID, repo.ID) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
permission, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetTeamRepos", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -648,9 +634,9 @@ func getRepositoryByParams(ctx *context.APIContext) *repo_model.Repository {
repo, err := repo_model.GetRepositoryByName(ctx, ctx.Org.Team.OrgID, ctx.PathParam("reponame"))
if err != nil {
if repo_model.IsErrRepoNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetRepositoryByName", err)
+ ctx.APIErrorInternal(err)
}
return nil
}
@@ -694,14 +680,14 @@ func AddTeamRepository(ctx *context.APIContext) {
return
}
if access, err := access_model.AccessLevel(ctx, ctx.Doer, repo); err != nil {
- ctx.Error(http.StatusInternalServerError, "AccessLevel", err)
+ ctx.APIErrorInternal(err)
return
} else if access < perm.AccessModeAdmin {
- ctx.Error(http.StatusForbidden, "", "Must have admin-level access to the repository")
+ ctx.APIError(http.StatusForbidden, "Must have admin-level access to the repository")
return
}
if err := repo_service.TeamAddRepository(ctx, ctx.Org.Team, repo); err != nil {
- ctx.Error(http.StatusInternalServerError, "TeamAddRepository", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
@@ -746,14 +732,14 @@ func RemoveTeamRepository(ctx *context.APIContext) {
return
}
if access, err := access_model.AccessLevel(ctx, ctx.Doer, repo); err != nil {
- ctx.Error(http.StatusInternalServerError, "AccessLevel", err)
+ ctx.APIErrorInternal(err)
return
} else if access < perm.AccessModeAdmin {
- ctx.Error(http.StatusForbidden, "", "Must have admin-level access to the repository")
+ ctx.APIError(http.StatusForbidden, "Must have admin-level access to the repository")
return
}
if err := repo_service.RemoveRepositoryFromTeam(ctx, ctx.Org.Team, repo.ID); err != nil {
- ctx.Error(http.StatusInternalServerError, "RemoveRepository", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
@@ -829,7 +815,7 @@ func SearchTeam(ctx *context.APIContext) {
apiTeams, err := convert.ToTeams(ctx, teams, false)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -885,7 +871,7 @@ func ListTeamActivityFeeds(ctx *context.APIContext) {
feeds, count, err := feed_service.GetFeeds(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetFeeds", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.SetTotalCountHeader(count)
diff --git a/routers/api/v1/packages/package.go b/routers/api/v1/packages/package.go
index b38aa13167..41b7f2a43f 100644
--- a/routers/api/v1/packages/package.go
+++ b/routers/api/v1/packages/package.go
@@ -4,11 +4,14 @@
package packages
import (
+ "errors"
"net/http"
"code.gitea.io/gitea/models/packages"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/optional"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/v1/utils"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
@@ -53,37 +56,18 @@ func ListPackages(ctx *context.APIContext) {
listOptions := utils.GetListOptions(ctx)
- packageType := ctx.FormTrim("type")
- query := ctx.FormTrim("q")
-
- pvs, count, err := packages.SearchVersions(ctx, &packages.PackageSearchOptions{
+ apiPackages, count, err := searchPackages(ctx, &packages.PackageSearchOptions{
OwnerID: ctx.Package.Owner.ID,
- Type: packages.Type(packageType),
- Name: packages.SearchValue{Value: query},
+ Type: packages.Type(ctx.FormTrim("type")),
+ Name: packages.SearchValue{Value: ctx.FormTrim("q")},
IsInternal: optional.Some(false),
Paginator: &listOptions,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "SearchVersions", err)
+ ctx.APIErrorInternal(err)
return
}
- pds, err := packages.GetPackageDescriptors(ctx, pvs)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetPackageDescriptors", err)
- return
- }
-
- apiPackages := make([]*api.Package, 0, len(pds))
- for _, pd := range pds {
- apiPackage, err := convert.ToPackage(ctx, pd, ctx.Doer)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "Error converting package for api", err)
- return
- }
- apiPackages = append(apiPackages, apiPackage)
- }
-
ctx.SetLinkHeader(int(count), listOptions.PageSize)
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, apiPackages)
@@ -125,7 +109,7 @@ func GetPackage(ctx *context.APIContext) {
apiPackage, err := convert.ToPackage(ctx, ctx.Package.Descriptor, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "Error converting package for api", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -166,7 +150,7 @@ func DeletePackage(ctx *context.APIContext) {
err := packages_service.RemovePackageVersion(ctx, ctx.Doer, ctx.Package.Descriptor.Version)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "RemovePackageVersion", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
@@ -213,3 +197,260 @@ func ListPackageFiles(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, apiPackageFiles)
}
+
+// ListPackageVersions gets all versions of a package
+func ListPackageVersions(ctx *context.APIContext) {
+ // swagger:operation GET /packages/{owner}/{type}/{name} package listPackageVersions
+ // ---
+ // summary: Gets all versions of a package
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the package
+ // type: string
+ // required: true
+ // - name: type
+ // in: path
+ // description: type of the package
+ // type: string
+ // required: true
+ // - name: name
+ // in: path
+ // description: name of the package
+ // type: string
+ // required: true
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results
+ // type: integer
+ // responses:
+ // "200":
+ // "$ref": "#/responses/PackageList"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ listOptions := utils.GetListOptions(ctx)
+
+ apiPackages, count, err := searchPackages(ctx, &packages.PackageSearchOptions{
+ OwnerID: ctx.Package.Owner.ID,
+ Type: packages.Type(ctx.PathParam("type")),
+ Name: packages.SearchValue{Value: ctx.PathParam("name"), ExactMatch: true},
+ IsInternal: optional.Some(false),
+ Paginator: &listOptions,
+ })
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+
+ ctx.SetLinkHeader(int(count), listOptions.PageSize)
+ ctx.SetTotalCountHeader(count)
+ ctx.JSON(http.StatusOK, apiPackages)
+}
+
+// GetLatestPackageVersion gets the latest version of a package
+func GetLatestPackageVersion(ctx *context.APIContext) {
+ // swagger:operation GET /packages/{owner}/{type}/{name}/-/latest package getLatestPackageVersion
+ // ---
+ // summary: Gets the latest version of a package
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the package
+ // type: string
+ // required: true
+ // - name: type
+ // in: path
+ // description: type of the package
+ // type: string
+ // required: true
+ // - name: name
+ // in: path
+ // description: name of the package
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/Package"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ pvs, _, err := packages.SearchLatestVersions(ctx, &packages.PackageSearchOptions{
+ OwnerID: ctx.Package.Owner.ID,
+ Type: packages.Type(ctx.PathParam("type")),
+ Name: packages.SearchValue{Value: ctx.PathParam("name"), ExactMatch: true},
+ IsInternal: optional.Some(false),
+ })
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ if len(pvs) == 0 {
+ ctx.APIError(http.StatusNotFound, err)
+ return
+ }
+
+ pd, err := packages.GetPackageDescriptor(ctx, pvs[0])
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+
+ apiPackage, err := convert.ToPackage(ctx, pd, ctx.Doer)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+
+ ctx.JSON(http.StatusOK, apiPackage)
+}
+
+// LinkPackage sets a repository link for a package
+func LinkPackage(ctx *context.APIContext) {
+ // swagger:operation POST /packages/{owner}/{type}/{name}/-/link/{repo_name} package linkPackage
+ // ---
+ // summary: Link a package to a repository
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the package
+ // type: string
+ // required: true
+ // - name: type
+ // in: path
+ // description: type of the package
+ // type: string
+ // required: true
+ // - name: name
+ // in: path
+ // description: name of the package
+ // type: string
+ // required: true
+ // - name: repo_name
+ // in: path
+ // description: name of the repository to link.
+ // type: string
+ // required: true
+ // responses:
+ // "201":
+ // "$ref": "#/responses/empty"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ pkg, err := packages.GetPackageByName(ctx, ctx.ContextUser.ID, packages.Type(ctx.PathParam("type")), ctx.PathParam("name"))
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.APIError(http.StatusNotFound, err)
+ } else {
+ ctx.APIErrorInternal(err)
+ }
+ return
+ }
+
+ repo, err := repo_model.GetRepositoryByName(ctx, ctx.ContextUser.ID, ctx.PathParam("repo_name"))
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.APIError(http.StatusNotFound, err)
+ } else {
+ ctx.APIErrorInternal(err)
+ }
+ return
+ }
+
+ err = packages_service.LinkToRepository(ctx, pkg, repo, ctx.Doer)
+ if err != nil {
+ switch {
+ case errors.Is(err, util.ErrInvalidArgument):
+ ctx.APIError(http.StatusBadRequest, err)
+ case errors.Is(err, util.ErrPermissionDenied):
+ ctx.APIError(http.StatusForbidden, err)
+ default:
+ ctx.APIErrorInternal(err)
+ }
+ return
+ }
+ ctx.Status(http.StatusCreated)
+}
+
+// UnlinkPackage sets a repository link for a package
+func UnlinkPackage(ctx *context.APIContext) {
+ // swagger:operation POST /packages/{owner}/{type}/{name}/-/unlink package unlinkPackage
+ // ---
+ // summary: Unlink a package from a repository
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the package
+ // type: string
+ // required: true
+ // - name: type
+ // in: path
+ // description: type of the package
+ // type: string
+ // required: true
+ // - name: name
+ // in: path
+ // description: name of the package
+ // type: string
+ // required: true
+ // responses:
+ // "201":
+ // "$ref": "#/responses/empty"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ pkg, err := packages.GetPackageByName(ctx, ctx.ContextUser.ID, packages.Type(ctx.PathParam("type")), ctx.PathParam("name"))
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.APIError(http.StatusNotFound, err)
+ } else {
+ ctx.APIErrorInternal(err)
+ }
+ return
+ }
+
+ err = packages_service.UnlinkFromRepository(ctx, pkg, ctx.Doer)
+ if err != nil {
+ switch {
+ case errors.Is(err, util.ErrPermissionDenied):
+ ctx.APIError(http.StatusForbidden, err)
+ case errors.Is(err, util.ErrInvalidArgument):
+ ctx.APIError(http.StatusBadRequest, err)
+ default:
+ ctx.APIErrorInternal(err)
+ }
+ return
+ }
+ ctx.Status(http.StatusNoContent)
+}
+
+func searchPackages(ctx *context.APIContext, opts *packages.PackageSearchOptions) ([]*api.Package, int64, error) {
+ pvs, count, err := packages.SearchVersions(ctx, opts)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ pds, err := packages.GetPackageDescriptors(ctx, pvs)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ apiPackages := make([]*api.Package, 0, len(pds))
+ for _, pd := range pds {
+ apiPackage, err := convert.ToPackage(ctx, pd, ctx.Doer)
+ if err != nil {
+ return nil, 0, err
+ }
+ apiPackages = append(apiPackages, apiPackage)
+ }
+
+ return apiPackages, count, nil
+}
diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go
index d27e8d2427..a57db015f0 100644
--- a/routers/api/v1/repo/action.go
+++ b/routers/api/v1/repo/action.go
@@ -4,12 +4,25 @@
package repo
import (
+ go_context "context"
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/base64"
"errors"
+ "fmt"
"net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
secret_model "code.gitea.io/gitea/models/secret"
+ "code.gitea.io/gitea/modules/actions"
+ "code.gitea.io/gitea/modules/httplib"
+ "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
@@ -19,6 +32,8 @@ import (
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
secret_service "code.gitea.io/gitea/services/secrets"
+
+ "github.com/nektos/act/pkg/model"
)
// ListActionsSecrets list an repo's actions secrets
@@ -62,15 +77,16 @@ func (Action) ListActionsSecrets(ctx *context.APIContext) {
secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
apiSecrets := make([]*api.Secret, len(secrets))
for k, v := range secrets {
apiSecrets[k] = &api.Secret{
- Name: v.Name,
- Created: v.CreatedUnix.AsTime(),
+ Name: v.Name,
+ Description: v.Description,
+ Created: v.CreatedUnix.AsTime(),
}
}
@@ -121,14 +137,14 @@ func (Action) CreateOrUpdateSecret(ctx *context.APIContext) {
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
- _, created, err := secret_service.CreateOrUpdateSecret(ctx, 0, repo.ID, ctx.PathParam("secretname"), opt.Data)
+ _, created, err := secret_service.CreateOrUpdateSecret(ctx, 0, repo.ID, ctx.PathParam("secretname"), opt.Data, opt.Description)
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "CreateOrUpdateSecret", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -167,7 +183,7 @@ func (Action) DeleteSecret(ctx *context.APIContext) {
// required: true
// responses:
// "204":
- // description: delete one secret of the organization
+ // description: delete one secret of the repository
// "400":
// "$ref": "#/responses/error"
// "404":
@@ -178,11 +194,11 @@ func (Action) DeleteSecret(ctx *context.APIContext) {
err := secret_service.DeleteSecretByName(ctx, 0, repo.ID, ctx.PathParam("secretname"))
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "DeleteSecret", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "DeleteSecret", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteSecret", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -226,18 +242,19 @@ func (Action) GetVariable(ctx *context.APIContext) {
})
if err != nil {
if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "GetVariable", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetVariable", err)
+ ctx.APIErrorInternal(err)
}
return
}
variable := &api.ActionVariable{
- OwnerID: v.OwnerID,
- RepoID: v.RepoID,
- Name: v.Name,
- Data: v.Data,
+ OwnerID: v.OwnerID,
+ RepoID: v.RepoID,
+ Name: v.Name,
+ Data: v.Data,
+ Description: v.Description,
}
ctx.JSON(http.StatusOK, variable)
@@ -280,11 +297,11 @@ func (Action) DeleteVariable(ctx *context.APIContext) {
if err := actions_service.DeleteVariableByName(ctx, 0, ctx.Repo.Repository.ID, ctx.PathParam("variablename")); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "DeleteVariableByName", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "DeleteVariableByName", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteVariableByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -322,12 +339,12 @@ func (Action) CreateVariable(ctx *context.APIContext) {
// responses:
// "201":
// description: response when creating a repo-level variable
- // "204":
- // description: response when creating a repo-level variable
// "400":
// "$ref": "#/responses/error"
- // "404":
- // "$ref": "#/responses/notFound"
+ // "409":
+ // description: variable name already exists.
+ // "500":
+ // "$ref": "#/responses/error"
opt := web.GetForm(ctx).(*api.CreateVariableOption)
@@ -339,24 +356,24 @@ func (Action) CreateVariable(ctx *context.APIContext) {
Name: variableName,
})
if err != nil && !errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusInternalServerError, "GetVariable", err)
+ ctx.APIErrorInternal(err)
return
}
if v != nil && v.ID > 0 {
- ctx.Error(http.StatusConflict, "VariableNameAlreadyExists", util.NewAlreadyExistErrorf("variable name %s already exists", variableName))
+ ctx.APIError(http.StatusConflict, util.NewAlreadyExistErrorf("variable name %s already exists", variableName))
return
}
- if _, err := actions_service.CreateVariable(ctx, 0, repoID, variableName, opt.Value); err != nil {
+ if _, err := actions_service.CreateVariable(ctx, 0, repoID, variableName, opt.Value, opt.Description); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "CreateVariable", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- ctx.Error(http.StatusInternalServerError, "CreateVariable", err)
+ ctx.APIErrorInternal(err)
}
return
}
- ctx.Status(http.StatusNoContent)
+ ctx.Status(http.StatusCreated)
}
// UpdateVariable update a repo-level variable
@@ -404,9 +421,9 @@ func (Action) UpdateVariable(ctx *context.APIContext) {
})
if err != nil {
if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "GetVariable", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetVariable", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -414,11 +431,16 @@ func (Action) UpdateVariable(ctx *context.APIContext) {
if opt.Name == "" {
opt.Name = ctx.PathParam("variablename")
}
- if _, err := actions_service.UpdateVariable(ctx, v.ID, opt.Name, opt.Value); err != nil {
+
+ v.Name = opt.Name
+ v.Data = opt.Value
+ v.Description = opt.Description
+
+ if _, err := actions_service.UpdateVariableNameData(ctx, v); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "UpdateVariable", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- ctx.Error(http.StatusInternalServerError, "UpdateVariable", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -465,16 +487,18 @@ func (Action) ListVariables(ctx *context.APIContext) {
ListOptions: utils.GetListOptions(ctx),
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindVariables", err)
+ ctx.APIErrorInternal(err)
return
}
variables := make([]*api.ActionVariable, len(vars))
for i, v := range vars {
variables[i] = &api.ActionVariable{
- OwnerID: v.OwnerID,
- RepoID: v.RepoID,
- Name: v.Name,
+ OwnerID: v.OwnerID,
+ RepoID: v.RepoID,
+ Name: v.Name,
+ Data: v.Data,
+ Description: v.Description,
}
}
@@ -507,6 +531,233 @@ func (Action) GetRegistrationToken(ctx *context.APIContext) {
shared.GetRegistrationToken(ctx, 0, ctx.Repo.Repository.ID)
}
+// CreateRegistrationToken returns the token to register repo runners
+func (Action) CreateRegistrationToken(ctx *context.APIContext) {
+ // swagger:operation POST /repos/{owner}/{repo}/actions/runners/registration-token repository repoCreateRunnerRegistrationToken
+ // ---
+ // summary: Get a repository's actions runner registration token
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/RegistrationToken"
+
+ shared.GetRegistrationToken(ctx, 0, ctx.Repo.Repository.ID)
+}
+
+// ListRunners get repo-level runners
+func (Action) ListRunners(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/runners repository getRepoRunners
+ // ---
+ // summary: Get repo-level runners
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/definitions/ActionRunnersResponse"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.ListRunners(ctx, 0, ctx.Repo.Repository.ID)
+}
+
+// GetRunner get an repo-level runner
+func (Action) GetRunner(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/runners/{runner_id} repository getRepoRunner
+ // ---
+ // summary: Get an repo-level runner
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: runner_id
+ // in: path
+ // description: id of the runner
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/definitions/ActionRunner"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.GetRunner(ctx, 0, ctx.Repo.Repository.ID, ctx.PathParamInt64("runner_id"))
+}
+
+// DeleteRunner delete an repo-level runner
+func (Action) DeleteRunner(ctx *context.APIContext) {
+ // swagger:operation DELETE /repos/{owner}/{repo}/actions/runners/{runner_id} repository deleteRepoRunner
+ // ---
+ // summary: Delete an repo-level runner
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: runner_id
+ // in: path
+ // description: id of the runner
+ // type: string
+ // required: true
+ // responses:
+ // "204":
+ // description: runner has been deleted
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.DeleteRunner(ctx, 0, ctx.Repo.Repository.ID, ctx.PathParamInt64("runner_id"))
+}
+
+// GetWorkflowRunJobs Lists all jobs for a workflow run.
+func (Action) ListWorkflowJobs(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/jobs repository listWorkflowJobs
+ // ---
+ // summary: Lists all jobs for a repository
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: name of the owner
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repository
+ // type: string
+ // required: true
+ // - name: status
+ // in: query
+ // description: workflow status (pending, queued, in_progress, failure, success, skipped)
+ // type: string
+ // required: false
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results
+ // type: integer
+ // responses:
+ // "200":
+ // "$ref": "#/responses/WorkflowJobsList"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ repoID := ctx.Repo.Repository.ID
+
+ shared.ListJobs(ctx, 0, repoID, 0)
+}
+
+// ListWorkflowRuns Lists all runs for a repository run.
+func (Action) ListWorkflowRuns(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/runs repository getWorkflowRuns
+ // ---
+ // summary: Lists all runs for a repository run
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: name of the owner
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repository
+ // type: string
+ // required: true
+ // - name: event
+ // in: query
+ // description: workflow event name
+ // type: string
+ // required: false
+ // - name: branch
+ // in: query
+ // description: workflow branch
+ // type: string
+ // required: false
+ // - name: status
+ // in: query
+ // description: workflow status (pending, queued, in_progress, failure, success, skipped)
+ // type: string
+ // required: false
+ // - name: actor
+ // in: query
+ // description: triggered by user
+ // type: string
+ // required: false
+ // - name: head_sha
+ // in: query
+ // description: triggering sha of the workflow run
+ // type: string
+ // required: false
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results
+ // type: integer
+ // responses:
+ // "200":
+ // "$ref": "#/responses/ArtifactsList"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ repoID := ctx.Repo.Repository.ID
+
+ shared.ListRuns(ctx, 0, repoID)
+}
+
var _ actions_service.API = new(Action)
// Action implements actions_service.API
@@ -562,7 +813,7 @@ func ListActionTasks(ctx *context.APIContext) {
RepoID: ctx.Repo.Repository.ID,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ListActionTasks", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -573,7 +824,7 @@ func ListActionTasks(ctx *context.APIContext) {
for i := range tasks {
convertedTask, err := convert.ToActionTask(ctx, tasks[i])
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ToActionTask", err)
+ ctx.APIErrorInternal(err)
return
}
res.Entries[i] = convertedTask
@@ -581,3 +832,852 @@ func ListActionTasks(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, &res)
}
+
+func ActionsListRepositoryWorkflows(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/workflows repository ActionsListRepositoryWorkflows
+ // ---
+ // summary: List repository workflows
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/ActionWorkflowList"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ // "422":
+ // "$ref": "#/responses/validationError"
+ // "500":
+ // "$ref": "#/responses/error"
+
+ workflows, err := convert.ListActionWorkflows(ctx, ctx.Repo.GitRepo, ctx.Repo.Repository)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+
+ ctx.JSON(http.StatusOK, &api.ActionWorkflowResponse{Workflows: workflows, TotalCount: int64(len(workflows))})
+}
+
+func ActionsGetWorkflow(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/workflows/{workflow_id} repository ActionsGetWorkflow
+ // ---
+ // summary: Get a workflow
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: workflow_id
+ // in: path
+ // description: id of the workflow
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/ActionWorkflow"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ // "422":
+ // "$ref": "#/responses/validationError"
+ // "500":
+ // "$ref": "#/responses/error"
+
+ workflowID := ctx.PathParam("workflow_id")
+ workflow, err := convert.GetActionWorkflow(ctx, ctx.Repo.GitRepo, ctx.Repo.Repository, workflowID)
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.APIError(http.StatusNotFound, err)
+ } else {
+ ctx.APIErrorInternal(err)
+ }
+ return
+ }
+
+ ctx.JSON(http.StatusOK, workflow)
+}
+
+func ActionsDisableWorkflow(ctx *context.APIContext) {
+ // swagger:operation PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable repository ActionsDisableWorkflow
+ // ---
+ // summary: Disable a workflow
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: workflow_id
+ // in: path
+ // description: id of the workflow
+ // type: string
+ // required: true
+ // responses:
+ // "204":
+ // description: No Content
+ // "400":
+ // "$ref": "#/responses/error"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ // "422":
+ // "$ref": "#/responses/validationError"
+
+ workflowID := ctx.PathParam("workflow_id")
+ err := actions_service.EnableOrDisableWorkflow(ctx, workflowID, false)
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.APIError(http.StatusNotFound, err)
+ } else {
+ ctx.APIErrorInternal(err)
+ }
+ return
+ }
+
+ ctx.Status(http.StatusNoContent)
+}
+
+func ActionsDispatchWorkflow(ctx *context.APIContext) {
+ // swagger:operation POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches repository ActionsDispatchWorkflow
+ // ---
+ // summary: Create a workflow dispatch event
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: workflow_id
+ // in: path
+ // description: id of the workflow
+ // type: string
+ // required: true
+ // - name: body
+ // in: body
+ // schema:
+ // "$ref": "#/definitions/CreateActionWorkflowDispatch"
+ // responses:
+ // "204":
+ // description: No Content
+ // "400":
+ // "$ref": "#/responses/error"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ // "422":
+ // "$ref": "#/responses/validationError"
+
+ workflowID := ctx.PathParam("workflow_id")
+ opt := web.GetForm(ctx).(*api.CreateActionWorkflowDispatch)
+ if opt.Ref == "" {
+ ctx.APIError(http.StatusUnprocessableEntity, util.NewInvalidArgumentErrorf("ref is required parameter"))
+ return
+ }
+
+ err := actions_service.DispatchActionWorkflow(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, workflowID, opt.Ref, func(workflowDispatch *model.WorkflowDispatch, inputs map[string]any) error {
+ if strings.Contains(ctx.Req.Header.Get("Content-Type"), "form-urlencoded") {
+ // The chi framework's "Binding" doesn't support to bind the form map values into a map[string]string
+ // So we have to manually read the `inputs[key]` from the form
+ for name, config := range workflowDispatch.Inputs {
+ value := ctx.FormString("inputs["+name+"]", config.Default)
+ inputs[name] = value
+ }
+ } else {
+ for name, config := range workflowDispatch.Inputs {
+ value, ok := opt.Inputs[name]
+ if ok {
+ inputs[name] = value
+ } else {
+ inputs[name] = config.Default
+ }
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.APIError(http.StatusNotFound, err)
+ } else if errors.Is(err, util.ErrPermissionDenied) {
+ ctx.APIError(http.StatusForbidden, err)
+ } else {
+ ctx.APIErrorInternal(err)
+ }
+ return
+ }
+
+ ctx.Status(http.StatusNoContent)
+}
+
+func ActionsEnableWorkflow(ctx *context.APIContext) {
+ // swagger:operation PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable repository ActionsEnableWorkflow
+ // ---
+ // summary: Enable a workflow
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: workflow_id
+ // in: path
+ // description: id of the workflow
+ // type: string
+ // required: true
+ // responses:
+ // "204":
+ // description: No Content
+ // "400":
+ // "$ref": "#/responses/error"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ // "409":
+ // "$ref": "#/responses/conflict"
+ // "422":
+ // "$ref": "#/responses/validationError"
+
+ workflowID := ctx.PathParam("workflow_id")
+ err := actions_service.EnableOrDisableWorkflow(ctx, workflowID, true)
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.APIError(http.StatusNotFound, err)
+ } else {
+ ctx.APIErrorInternal(err)
+ }
+ return
+ }
+
+ ctx.Status(http.StatusNoContent)
+}
+
+// GetWorkflowRun Gets a specific workflow run.
+func GetWorkflowRun(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/runs/{run} repository GetWorkflowRun
+ // ---
+ // summary: Gets a specific workflow run
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: name of the owner
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repository
+ // type: string
+ // required: true
+ // - name: run
+ // in: path
+ // description: id of the run
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/WorkflowRun"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ runID := ctx.PathParamInt64("run")
+ job, _, err := db.GetByID[actions_model.ActionRun](ctx, runID)
+
+ if err != nil || job.RepoID != ctx.Repo.Repository.ID {
+ ctx.APIError(http.StatusNotFound, util.ErrNotExist)
+ }
+
+ convertedArtifact, err := convert.ToActionWorkflowRun(ctx, ctx.Repo.Repository, job)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ ctx.JSON(http.StatusOK, convertedArtifact)
+}
+
+// ListWorkflowRunJobs Lists all jobs for a workflow run.
+func ListWorkflowRunJobs(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/runs/{run}/jobs repository listWorkflowRunJobs
+ // ---
+ // summary: Lists all jobs for a workflow run
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: name of the owner
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repository
+ // type: string
+ // required: true
+ // - name: run
+ // in: path
+ // description: runid of the workflow run
+ // type: integer
+ // required: true
+ // - name: status
+ // in: query
+ // description: workflow status (pending, queued, in_progress, failure, success, skipped)
+ // type: string
+ // required: false
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results
+ // type: integer
+ // responses:
+ // "200":
+ // "$ref": "#/responses/WorkflowJobsList"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ repoID := ctx.Repo.Repository.ID
+
+ runID := ctx.PathParamInt64("run")
+
+ // Avoid the list all jobs functionality for this api route to be used with a runID == 0.
+ if runID <= 0 {
+ ctx.APIError(http.StatusBadRequest, util.NewInvalidArgumentErrorf("runID must be a positive integer"))
+ return
+ }
+
+ // runID is used as an additional filter next to repoID to ensure that we only list jobs for the specified repoID and runID.
+ // no additional checks for runID are needed here
+ shared.ListJobs(ctx, 0, repoID, runID)
+}
+
+// GetWorkflowJob Gets a specific workflow job for a workflow run.
+func GetWorkflowJob(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/jobs/{job_id} repository getWorkflowJob
+ // ---
+ // summary: Gets a specific workflow job for a workflow run
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: name of the owner
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repository
+ // type: string
+ // required: true
+ // - name: job_id
+ // in: path
+ // description: id of the job
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/WorkflowJob"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ jobID := ctx.PathParamInt64("job_id")
+ job, _, err := db.GetByID[actions_model.ActionRunJob](ctx, jobID)
+
+ if err != nil || job.RepoID != ctx.Repo.Repository.ID {
+ ctx.APIError(http.StatusNotFound, util.ErrNotExist)
+ }
+
+ convertedWorkflowJob, err := convert.ToActionWorkflowJob(ctx, ctx.Repo.Repository, nil, job)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ ctx.JSON(http.StatusOK, convertedWorkflowJob)
+}
+
+// GetArtifacts Lists all artifacts for a repository.
+func GetArtifactsOfRun(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/runs/{run}/artifacts repository getArtifactsOfRun
+ // ---
+ // summary: Lists all artifacts for a repository run
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: name of the owner
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repository
+ // type: string
+ // required: true
+ // - name: run
+ // in: path
+ // description: runid of the workflow run
+ // type: integer
+ // required: true
+ // - name: name
+ // in: query
+ // description: name of the artifact
+ // type: string
+ // required: false
+ // responses:
+ // "200":
+ // "$ref": "#/responses/ArtifactsList"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ repoID := ctx.Repo.Repository.ID
+ artifactName := ctx.Req.URL.Query().Get("name")
+
+ runID := ctx.PathParamInt64("run")
+
+ artifacts, total, err := db.FindAndCount[actions_model.ActionArtifact](ctx, actions_model.FindArtifactsOptions{
+ RepoID: repoID,
+ RunID: runID,
+ ArtifactName: artifactName,
+ FinalizedArtifactsV4: true,
+ ListOptions: utils.GetListOptions(ctx),
+ })
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+
+ res := new(api.ActionArtifactsResponse)
+ res.TotalCount = total
+
+ res.Entries = make([]*api.ActionArtifact, len(artifacts))
+ for i := range artifacts {
+ convertedArtifact, err := convert.ToActionArtifact(ctx.Repo.Repository, artifacts[i])
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ res.Entries[i] = convertedArtifact
+ }
+
+ ctx.JSON(http.StatusOK, &res)
+}
+
+// DeleteActionRun Delete a workflow run
+func DeleteActionRun(ctx *context.APIContext) {
+ // swagger:operation DELETE /repos/{owner}/{repo}/actions/runs/{run} repository deleteActionRun
+ // ---
+ // summary: Delete a workflow run
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: name of the owner
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repository
+ // type: string
+ // required: true
+ // - name: run
+ // in: path
+ // description: runid of the workflow run
+ // type: integer
+ // required: true
+ // responses:
+ // "204":
+ // description: "No Content"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ runID := ctx.PathParamInt64("run")
+ run, err := actions_model.GetRunByRepoAndID(ctx, ctx.Repo.Repository.ID, runID)
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.APIError(http.StatusNotFound, err)
+ return
+ } else if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ if !run.Status.IsDone() {
+ ctx.APIError(http.StatusBadRequest, "this workflow run is not done")
+ return
+ }
+
+ if err := actions_service.DeleteRun(ctx, run); err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ ctx.Status(http.StatusNoContent)
+}
+
+// GetArtifacts Lists all artifacts for a repository.
+func GetArtifacts(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/artifacts repository getArtifacts
+ // ---
+ // summary: Lists all artifacts for a repository
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: name of the owner
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repository
+ // type: string
+ // required: true
+ // - name: name
+ // in: query
+ // description: name of the artifact
+ // type: string
+ // required: false
+ // responses:
+ // "200":
+ // "$ref": "#/responses/ArtifactsList"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ repoID := ctx.Repo.Repository.ID
+ artifactName := ctx.Req.URL.Query().Get("name")
+
+ artifacts, total, err := db.FindAndCount[actions_model.ActionArtifact](ctx, actions_model.FindArtifactsOptions{
+ RepoID: repoID,
+ ArtifactName: artifactName,
+ FinalizedArtifactsV4: true,
+ ListOptions: utils.GetListOptions(ctx),
+ })
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+
+ res := new(api.ActionArtifactsResponse)
+ res.TotalCount = total
+
+ res.Entries = make([]*api.ActionArtifact, len(artifacts))
+ for i := range artifacts {
+ convertedArtifact, err := convert.ToActionArtifact(ctx.Repo.Repository, artifacts[i])
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ res.Entries[i] = convertedArtifact
+ }
+
+ ctx.JSON(http.StatusOK, &res)
+}
+
+// GetArtifact Gets a specific artifact for a workflow run.
+func GetArtifact(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id} repository getArtifact
+ // ---
+ // summary: Gets a specific artifact for a workflow run
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: name of the owner
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repository
+ // type: string
+ // required: true
+ // - name: artifact_id
+ // in: path
+ // description: id of the artifact
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/Artifact"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ art := getArtifactByPathParam(ctx, ctx.Repo.Repository)
+ if ctx.Written() {
+ return
+ }
+
+ if actions.IsArtifactV4(art) {
+ convertedArtifact, err := convert.ToActionArtifact(ctx.Repo.Repository, art)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ ctx.JSON(http.StatusOK, convertedArtifact)
+ return
+ }
+ // v3 not supported due to not having one unique id
+ ctx.APIError(http.StatusNotFound, "Artifact not found")
+}
+
+// DeleteArtifact Deletes a specific artifact for a workflow run.
+func DeleteArtifact(ctx *context.APIContext) {
+ // swagger:operation DELETE /repos/{owner}/{repo}/actions/artifacts/{artifact_id} repository deleteArtifact
+ // ---
+ // summary: Deletes a specific artifact for a workflow run
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: name of the owner
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repository
+ // type: string
+ // required: true
+ // - name: artifact_id
+ // in: path
+ // description: id of the artifact
+ // type: string
+ // required: true
+ // responses:
+ // "204":
+ // description: "No Content"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ art := getArtifactByPathParam(ctx, ctx.Repo.Repository)
+ if ctx.Written() {
+ return
+ }
+
+ if actions.IsArtifactV4(art) {
+ if err := actions_model.SetArtifactNeedDelete(ctx, art.RunID, art.ArtifactName); err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ ctx.Status(http.StatusNoContent)
+ return
+ }
+ // v3 not supported due to not having one unique id
+ ctx.APIError(http.StatusNotFound, "Artifact not found")
+}
+
+func buildSignature(endp string, expires, artifactID int64) []byte {
+ mac := hmac.New(sha256.New, setting.GetGeneralTokenSigningSecret())
+ mac.Write([]byte(endp))
+ fmt.Fprint(mac, expires)
+ fmt.Fprint(mac, artifactID)
+ return mac.Sum(nil)
+}
+
+func buildDownloadRawEndpoint(repo *repo_model.Repository, artifactID int64) string {
+ return fmt.Sprintf("api/v1/repos/%s/%s/actions/artifacts/%d/zip/raw", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name), artifactID)
+}
+
+func buildSigURL(ctx go_context.Context, endPoint string, artifactID int64) string {
+ // endPoint is a path like "api/v1/repos/owner/repo/actions/artifacts/1/zip/raw"
+ expires := time.Now().Add(60 * time.Minute).Unix()
+ uploadURL := httplib.GuessCurrentAppURL(ctx) + endPoint + "?sig=" + base64.URLEncoding.EncodeToString(buildSignature(endPoint, expires, artifactID)) + "&expires=" + strconv.FormatInt(expires, 10)
+ return uploadURL
+}
+
+// DownloadArtifact Downloads a specific artifact for a workflow run redirects to blob url.
+func DownloadArtifact(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}/zip repository downloadArtifact
+ // ---
+ // summary: Downloads a specific artifact for a workflow run redirects to blob url
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: name of the owner
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repository
+ // type: string
+ // required: true
+ // - name: artifact_id
+ // in: path
+ // description: id of the artifact
+ // type: string
+ // required: true
+ // responses:
+ // "302":
+ // description: redirect to the blob download
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ art := getArtifactByPathParam(ctx, ctx.Repo.Repository)
+ if ctx.Written() {
+ return
+ }
+
+ // if artifacts status is not uploaded-confirmed, treat it as not found
+ if art.Status == actions_model.ArtifactStatusExpired {
+ ctx.APIError(http.StatusNotFound, "Artifact has expired")
+ return
+ }
+ ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s.zip; filename*=UTF-8''%s.zip", url.PathEscape(art.ArtifactName), art.ArtifactName))
+
+ if actions.IsArtifactV4(art) {
+ ok, err := actions.DownloadArtifactV4ServeDirectOnly(ctx.Base, art)
+ if ok {
+ return
+ }
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+
+ redirectURL := buildSigURL(ctx, buildDownloadRawEndpoint(ctx.Repo.Repository, art.ID), art.ID)
+ ctx.Redirect(redirectURL, http.StatusFound)
+ return
+ }
+ // v3 not supported due to not having one unique id
+ ctx.APIError(http.StatusNotFound, "Artifact not found")
+}
+
+// DownloadArtifactRaw Downloads a specific artifact for a workflow run directly.
+func DownloadArtifactRaw(ctx *context.APIContext) {
+ // it doesn't use repoAssignment middleware, so it needs to prepare the repo and check permission (sig) by itself
+ repo, err := repo_model.GetRepositoryByOwnerAndName(ctx, ctx.PathParam("username"), ctx.PathParam("reponame"))
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.APIErrorNotFound()
+ } else {
+ ctx.APIErrorInternal(err)
+ }
+ return
+ }
+ art := getArtifactByPathParam(ctx, repo)
+ if ctx.Written() {
+ return
+ }
+
+ sigStr := ctx.Req.URL.Query().Get("sig")
+ expiresStr := ctx.Req.URL.Query().Get("expires")
+ sigBytes, _ := base64.URLEncoding.DecodeString(sigStr)
+ expires, _ := strconv.ParseInt(expiresStr, 10, 64)
+
+ expectedSig := buildSignature(buildDownloadRawEndpoint(repo, art.ID), expires, art.ID)
+ if !hmac.Equal(sigBytes, expectedSig) {
+ ctx.APIError(http.StatusUnauthorized, "Error unauthorized")
+ return
+ }
+ t := time.Unix(expires, 0)
+ if t.Before(time.Now()) {
+ ctx.APIError(http.StatusUnauthorized, "Error link expired")
+ return
+ }
+
+ // if artifacts status is not uploaded-confirmed, treat it as not found
+ if art.Status == actions_model.ArtifactStatusExpired {
+ ctx.APIError(http.StatusNotFound, "Artifact has expired")
+ return
+ }
+ ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s.zip; filename*=UTF-8''%s.zip", url.PathEscape(art.ArtifactName), art.ArtifactName))
+
+ if actions.IsArtifactV4(art) {
+ err := actions.DownloadArtifactV4(ctx.Base, art)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ return
+ }
+ // v3 not supported due to not having one unique id
+ ctx.APIError(http.StatusNotFound, "artifact not found")
+}
+
+// Try to get the artifact by ID and check access
+func getArtifactByPathParam(ctx *context.APIContext, repo *repo_model.Repository) *actions_model.ActionArtifact {
+ artifactID := ctx.PathParamInt64("artifact_id")
+
+ art, ok, err := db.GetByID[actions_model.ActionArtifact](ctx, artifactID)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return nil
+ }
+ // if artifacts status is not uploaded-confirmed, treat it as not found
+ // only check RepoID here, because the repository owner may change over the time
+ if !ok ||
+ art.RepoID != repo.ID ||
+ art.Status != actions_model.ArtifactStatusUploadConfirmed && art.Status != actions_model.ArtifactStatusExpired {
+ ctx.APIError(http.StatusNotFound, "artifact not found")
+ return nil
+ }
+ return art
+}
diff --git a/routers/api/v1/repo/actions_run.go b/routers/api/v1/repo/actions_run.go
new file mode 100644
index 0000000000..c6d18af6aa
--- /dev/null
+++ b/routers/api/v1/repo/actions_run.go
@@ -0,0 +1,64 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "errors"
+
+ actions_model "code.gitea.io/gitea/models/actions"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/routers/common"
+ "code.gitea.io/gitea/services/context"
+)
+
+func DownloadActionsRunJobLogs(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/actions/jobs/{job_id}/logs repository downloadActionsRunJobLogs
+ // ---
+ // summary: Downloads the job logs for a workflow run
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: name of the owner
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repository
+ // type: string
+ // required: true
+ // - name: job_id
+ // in: path
+ // description: id of the job
+ // type: integer
+ // required: true
+ // responses:
+ // "200":
+ // description: output blob content
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ jobID := ctx.PathParamInt64("job_id")
+ curJob, err := actions_model.GetRunJobByID(ctx, jobID)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ if err = curJob.LoadRepo(ctx); err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+
+ err = common.DownloadActionsRunJobLogs(ctx.Base, ctx.Repo.Repository, curJob)
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.APIErrorNotFound(err)
+ } else {
+ ctx.APIErrorInternal(err)
+ }
+ }
+}
diff --git a/routers/api/v1/repo/avatar.go b/routers/api/v1/repo/avatar.go
index 698337ffd2..593460586f 100644
--- a/routers/api/v1/repo/avatar.go
+++ b/routers/api/v1/repo/avatar.go
@@ -44,13 +44,13 @@ func UpdateAvatar(ctx *context.APIContext) {
content, err := base64.StdEncoding.DecodeString(form.Image)
if err != nil {
- ctx.Error(http.StatusBadRequest, "DecodeImage", err)
+ ctx.APIError(http.StatusBadRequest, err)
return
}
err = repo_service.UploadAvatar(ctx, ctx.Repo.Repository, content)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "UploadAvatar", err)
+ ctx.APIErrorInternal(err)
}
ctx.Status(http.StatusNoContent)
@@ -81,7 +81,7 @@ func DeleteAvatar(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
err := repo_service.DeleteAvatar(ctx, ctx.Repo.Repository)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
+ ctx.APIErrorInternal(err)
}
ctx.Status(http.StatusNoContent)
diff --git a/routers/api/v1/repo/blob.go b/routers/api/v1/repo/blob.go
index f38086954b..9a17fc1bbf 100644
--- a/routers/api/v1/repo/blob.go
+++ b/routers/api/v1/repo/blob.go
@@ -43,12 +43,12 @@ func GetBlob(ctx *context.APIContext) {
sha := ctx.PathParam("sha")
if len(sha) == 0 {
- ctx.Error(http.StatusBadRequest, "", "sha not provided")
+ ctx.APIError(http.StatusBadRequest, "sha not provided")
return
}
- if blob, err := files_service.GetBlobBySHA(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, sha); err != nil {
- ctx.Error(http.StatusBadRequest, "", err)
+ if blob, err := files_service.GetBlobBySHA(ctx.Repo.Repository, ctx.Repo.GitRepo, sha); err != nil {
+ ctx.APIError(http.StatusBadRequest, err)
} else {
ctx.JSON(http.StatusOK, blob)
}
diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go
index 2fcdd02058..9af958a5b7 100644
--- a/routers/api/v1/repo/branch.go
+++ b/routers/api/v1/repo/branch.go
@@ -6,18 +6,19 @@ package repo
import (
"errors"
- "fmt"
"net/http"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/organization"
+ repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/optional"
repo_module "code.gitea.io/gitea/modules/repository"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
"code.gitea.io/gitea/services/context"
@@ -58,31 +59,30 @@ func GetBranch(ctx *context.APIContext) {
branchName := ctx.PathParam("*")
- branch, err := ctx.Repo.GitRepo.GetBranch(branchName)
+ exist, err := git_model.IsBranchExist(ctx, ctx.Repo.Repository.ID, branchName)
if err != nil {
- if git.IsErrBranchNotExist(err) {
- ctx.NotFound(err)
- } else {
- ctx.Error(http.StatusInternalServerError, "GetBranch", err)
- }
+ ctx.APIErrorInternal(err)
+ return
+ } else if !exist {
+ ctx.APIErrorNotFound(err)
return
}
- c, err := branch.GetCommit()
+ c, err := ctx.Repo.GitRepo.GetBranchCommit(branchName)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetCommit", err)
+ ctx.APIErrorInternal(err)
return
}
branchProtection, err := git_model.GetFirstMatchProtectedBranchRule(ctx, ctx.Repo.Repository.ID, branchName)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
+ ctx.APIErrorInternal(err)
return
}
- br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch.Name, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
+ br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branchName, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
if err != nil {
- ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -122,12 +122,12 @@ func DeleteBranch(ctx *context.APIContext) {
// "423":
// "$ref": "#/responses/repoArchivedError"
if ctx.Repo.Repository.IsEmpty {
- ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
+ ctx.APIError(http.StatusNotFound, "Git Repository is empty.")
return
}
if ctx.Repo.Repository.IsMirror {
- ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.")
+ ctx.APIError(http.StatusForbidden, "Git Repository is a mirror.")
return
}
@@ -139,27 +139,27 @@ func DeleteBranch(ctx *context.APIContext) {
IsDeletedBranch: optional.Some(false),
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "CountBranches", err)
+ ctx.APIErrorInternal(err)
return
}
if totalNumOfBranches == 0 { // sync branches immediately because non-empty repository should have at least 1 branch
_, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
if err != nil {
- ctx.ServerError("SyncRepoBranches", err)
+ ctx.APIErrorInternal(err)
return
}
}
- if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
+ if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName, nil); err != nil {
switch {
case git.IsErrBranchNotExist(err):
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
case errors.Is(err, repo_service.ErrBranchIsDefault):
- ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("can not delete default branch"))
+ ctx.APIError(http.StatusForbidden, errors.New("can not delete default branch"))
case errors.Is(err, git_model.ErrBranchIsProtected):
- ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("branch protected"))
+ ctx.APIError(http.StatusForbidden, errors.New("branch protected"))
default:
- ctx.Error(http.StatusInternalServerError, "DeleteBranch", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -204,12 +204,12 @@ func CreateBranch(ctx *context.APIContext) {
// "$ref": "#/responses/repoArchivedError"
if ctx.Repo.Repository.IsEmpty {
- ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
+ ctx.APIError(http.StatusNotFound, "Git Repository is empty.")
return
}
if ctx.Repo.Repository.IsMirror {
- ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.")
+ ctx.APIError(http.StatusForbidden, "Git Repository is a mirror.")
return
}
@@ -221,24 +221,24 @@ func CreateBranch(ctx *context.APIContext) {
if len(opt.OldRefName) > 0 {
oldCommit, err = ctx.Repo.GitRepo.GetCommit(opt.OldRefName)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetCommit", err)
+ ctx.APIErrorInternal(err)
return
}
- } else if len(opt.OldBranchName) > 0 { //nolint
- if ctx.Repo.GitRepo.IsBranchExist(opt.OldBranchName) { //nolint
- oldCommit, err = ctx.Repo.GitRepo.GetBranchCommit(opt.OldBranchName) //nolint
+ } else if len(opt.OldBranchName) > 0 { //nolint:staticcheck // deprecated field
+ if gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, opt.OldBranchName) { //nolint:staticcheck // deprecated field
+ oldCommit, err = ctx.Repo.GitRepo.GetBranchCommit(opt.OldBranchName) //nolint:staticcheck // deprecated field
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBranchCommit", err)
+ ctx.APIErrorInternal(err)
return
}
} else {
- ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
+ ctx.APIError(http.StatusNotFound, "The old branch does not exist")
return
}
} else {
oldCommit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBranchCommit", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -246,40 +246,34 @@ func CreateBranch(ctx *context.APIContext) {
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, oldCommit.ID.String(), opt.BranchName)
if err != nil {
if git_model.IsErrBranchNotExist(err) {
- ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
+ ctx.APIError(http.StatusNotFound, "The old branch does not exist")
} else if release_service.IsErrTagAlreadyExists(err) {
- ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
+ ctx.APIError(http.StatusConflict, "The branch with the same tag already exists.")
} else if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
- ctx.Error(http.StatusConflict, "", "The branch already exists.")
+ ctx.APIError(http.StatusConflict, "The branch already exists.")
} else if git_model.IsErrBranchNameConflict(err) {
- ctx.Error(http.StatusConflict, "", "The branch with the same name already exists.")
+ ctx.APIError(http.StatusConflict, "The branch with the same name already exists.")
} else {
- ctx.Error(http.StatusInternalServerError, "CreateNewBranchFromCommit", err)
+ ctx.APIErrorInternal(err)
}
return
}
- branch, err := ctx.Repo.GitRepo.GetBranch(opt.BranchName)
+ commit, err := ctx.Repo.GitRepo.GetBranchCommit(opt.BranchName)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBranch", err)
+ ctx.APIErrorInternal(err)
return
}
- commit, err := branch.GetCommit()
+ branchProtection, err := git_model.GetFirstMatchProtectedBranchRule(ctx, ctx.Repo.Repository.ID, opt.BranchName)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetCommit", err)
+ ctx.APIErrorInternal(err)
return
}
- branchProtection, err := git_model.GetFirstMatchProtectedBranchRule(ctx, ctx.Repo.Repository.ID, branch.Name)
+ br, err := convert.ToBranch(ctx, ctx.Repo.Repository, opt.BranchName, commit, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
- return
- }
-
- br, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch.Name, commit, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -323,7 +317,7 @@ func ListBranches(ctx *context.APIContext) {
if !ctx.Repo.Repository.IsEmpty {
if ctx.Repo.GitRepo == nil {
- ctx.Error(http.StatusInternalServerError, "Load git repository failed", nil)
+ ctx.APIErrorInternal(nil)
return
}
@@ -335,26 +329,26 @@ func ListBranches(ctx *context.APIContext) {
var err error
totalNumOfBranches, err = db.Count[git_model.Branch](ctx, branchOpts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "CountBranches", err)
+ ctx.APIErrorInternal(err)
return
}
if totalNumOfBranches == 0 { // sync branches immediately because non-empty repository should have at least 1 branch
totalNumOfBranches, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
if err != nil {
- ctx.ServerError("SyncRepoBranches", err)
+ ctx.APIErrorInternal(err)
return
}
}
rules, err := git_model.FindRepoProtectedBranchRules(ctx, ctx.Repo.Repository.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindMatchedProtectedBranchRules", err)
+ ctx.APIErrorInternal(err)
return
}
branches, err := db.Find[git_model.Branch](ctx, branchOpts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBranches", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -367,14 +361,14 @@ func ListBranches(ctx *context.APIContext) {
totalNumOfBranches--
continue
}
- ctx.Error(http.StatusInternalServerError, "GetCommit", err)
+ ctx.APIErrorInternal(err)
return
}
branchProtection := rules.GetFirstMatched(branches[i].Name)
apiBranch, err := convert.ToBranch(ctx, ctx.Repo.Repository, branches[i].Name, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
if err != nil {
- ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
+ ctx.APIErrorInternal(err)
return
}
apiBranches = append(apiBranches, apiBranch)
@@ -431,26 +425,33 @@ func UpdateBranch(ctx *context.APIContext) {
repo := ctx.Repo.Repository
if repo.IsEmpty {
- ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
+ ctx.APIError(http.StatusNotFound, "Git Repository is empty.")
return
}
if repo.IsMirror {
- ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.")
+ ctx.APIError(http.StatusForbidden, "Git Repository is a mirror.")
return
}
msg, err := repo_service.RenameBranch(ctx, repo, ctx.Doer, ctx.Repo.GitRepo, oldName, opt.Name)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "RenameBranch", err)
+ switch {
+ case repo_model.IsErrUserDoesNotHaveAccessToRepo(err):
+ ctx.APIError(http.StatusForbidden, "User must be a repo or site admin to rename default or protected branches.")
+ case errors.Is(err, git_model.ErrBranchIsProtected):
+ ctx.APIError(http.StatusForbidden, "Branch is protected by glob-based protection rules.")
+ default:
+ ctx.APIErrorInternal(err)
+ }
return
}
if msg == "target_exist" {
- ctx.Error(http.StatusUnprocessableEntity, "", "Cannot rename a branch using the same name or rename to a branch that already exists.")
+ ctx.APIError(http.StatusUnprocessableEntity, "Cannot rename a branch using the same name or rename to a branch that already exists.")
return
}
if msg == "from_not_exist" {
- ctx.Error(http.StatusNotFound, "", "Branch doesn't exist.")
+ ctx.APIError(http.StatusNotFound, "Branch doesn't exist.")
return
}
@@ -490,11 +491,11 @@ func GetBranchProtection(ctx *context.APIContext) {
bpName := ctx.PathParam("name")
bp, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, bpName)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
+ ctx.APIErrorInternal(err)
return
}
if bp == nil || bp.RepoID != repo.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -526,7 +527,7 @@ func ListBranchProtections(ctx *context.APIContext) {
repo := ctx.Repo.Repository
bps, err := git_model.FindRepoProtectedBranchRules(ctx, repo.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedBranches", err)
+ ctx.APIErrorInternal(err)
return
}
apiBps := make([]*api.BranchProtection, len(bps))
@@ -578,25 +579,19 @@ func CreateBranchProtection(ctx *context.APIContext) {
ruleName := form.RuleName
if ruleName == "" {
- ruleName = form.BranchName //nolint
+ ruleName = form.BranchName //nolint:staticcheck // deprecated field
}
if len(ruleName) == 0 {
- ctx.Error(http.StatusBadRequest, "both rule_name and branch_name are empty", "both rule_name and branch_name are empty")
+ ctx.APIError(http.StatusBadRequest, "both rule_name and branch_name are empty")
return
}
- isPlainRule := !git_model.IsRuleNameSpecial(ruleName)
- var isBranchExist bool
- if isPlainRule {
- isBranchExist = git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), ruleName)
- }
-
protectBranch, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, ruleName)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectBranchOfRepoByName", err)
+ ctx.APIErrorInternal(err)
return
} else if protectBranch != nil {
- ctx.Error(http.StatusForbidden, "Create branch protection", "Branch protection already exist")
+ ctx.APIError(http.StatusForbidden, "Branch protection already exist")
return
}
@@ -608,37 +603,37 @@ func CreateBranchProtection(ctx *context.APIContext) {
whitelistUsers, err := user_model.GetUserIDsByNames(ctx, form.PushWhitelistUsernames, false)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
forcePushAllowlistUsers, err := user_model.GetUserIDsByNames(ctx, form.ForcePushAllowlistUsernames, false)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
mergeWhitelistUsers, err := user_model.GetUserIDsByNames(ctx, form.MergeWhitelistUsernames, false)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
approvalsWhitelistUsers, err := user_model.GetUserIDsByNames(ctx, form.ApprovalsWhitelistUsernames, false)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
var whitelistTeams, forcePushAllowlistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
@@ -646,37 +641,37 @@ func CreateBranchProtection(ctx *context.APIContext) {
whitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.PushWhitelistTeams, false)
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
forcePushAllowlistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.ForcePushAllowlistTeams, false)
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
mergeWhitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.MergeWhitelistTeams, false)
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
approvalsWhitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.ApprovalsWhitelistTeams, false)
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -707,7 +702,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
BlockAdminMergeOverride: form.BlockAdminMergeOverride,
}
- if err := git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{
+ if err := pull_service.CreateOrUpdateProtectedBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{
UserIDs: whitelistUsers,
TeamIDs: whitelistTeams,
ForcePushUserIDs: forcePushAllowlistUsers,
@@ -717,48 +712,18 @@ func CreateBranchProtection(ctx *context.APIContext) {
ApprovalsUserIDs: approvalsWhitelistUsers,
ApprovalsTeamIDs: approvalsWhitelistTeams,
}); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateProtectBranch", err)
+ ctx.APIErrorInternal(err)
return
}
- if isBranchExist {
- if err := pull_service.CheckPRsForBaseBranch(ctx, ctx.Repo.Repository, ruleName); err != nil {
- ctx.Error(http.StatusInternalServerError, "CheckPRsForBaseBranch", err)
- return
- }
- } else {
- if !isPlainRule {
- if ctx.Repo.GitRepo == nil {
- ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
- return
- }
- }
- // FIXME: since we only need to recheck files protected rules, we could improve this
- matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.Repository.ID, ruleName)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindAllMatchedBranches", err)
- return
- }
-
- for _, branchName := range matchedBranches {
- if err = pull_service.CheckPRsForBaseBranch(ctx, ctx.Repo.Repository, branchName); err != nil {
- ctx.Error(http.StatusInternalServerError, "CheckPRsForBaseBranch", err)
- return
- }
- }
- }
- }
-
// Reload from db to get all whitelists
bp, err := git_model.GetProtectedBranchRuleByName(ctx, ctx.Repo.Repository.ID, ruleName)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
+ ctx.APIErrorInternal(err)
return
}
if bp == nil || bp.RepoID != ctx.Repo.Repository.ID {
- ctx.Error(http.StatusInternalServerError, "New branch protection not found", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -808,11 +773,11 @@ func EditBranchProtection(ctx *context.APIContext) {
bpName := ctx.PathParam("name")
protectBranch, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, bpName)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
+ ctx.APIErrorInternal(err)
return
}
if protectBranch == nil || protectBranch.RepoID != repo.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -923,10 +888,10 @@ func EditBranchProtection(ctx *context.APIContext) {
whitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.PushWhitelistUsernames, false)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
} else {
@@ -936,10 +901,10 @@ func EditBranchProtection(ctx *context.APIContext) {
forcePushAllowlistUsers, err = user_model.GetUserIDsByNames(ctx, form.ForcePushAllowlistUsernames, false)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
} else {
@@ -949,10 +914,10 @@ func EditBranchProtection(ctx *context.APIContext) {
mergeWhitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.MergeWhitelistUsernames, false)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
} else {
@@ -962,10 +927,10 @@ func EditBranchProtection(ctx *context.APIContext) {
approvalsWhitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.ApprovalsWhitelistUsernames, false)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
} else {
@@ -978,10 +943,10 @@ func EditBranchProtection(ctx *context.APIContext) {
whitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.PushWhitelistTeams, false)
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
} else {
@@ -991,10 +956,10 @@ func EditBranchProtection(ctx *context.APIContext) {
forcePushAllowlistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.ForcePushAllowlistTeams, false)
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
} else {
@@ -1004,10 +969,10 @@ func EditBranchProtection(ctx *context.APIContext) {
mergeWhitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.MergeWhitelistTeams, false)
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
} else {
@@ -1017,10 +982,10 @@ func EditBranchProtection(ctx *context.APIContext) {
approvalsWhitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.ApprovalsWhitelistTeams, false)
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
} else {
@@ -1039,19 +1004,19 @@ func EditBranchProtection(ctx *context.APIContext) {
ApprovalsTeamIDs: approvalsWhitelistTeams,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateProtectBranch", err)
+ ctx.APIErrorInternal(err)
return
}
isPlainRule := !git_model.IsRuleNameSpecial(bpName)
var isBranchExist bool
if isPlainRule {
- isBranchExist = git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), bpName)
+ isBranchExist = gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, bpName)
}
if isBranchExist {
if err = pull_service.CheckPRsForBaseBranch(ctx, ctx.Repo.Repository, bpName); err != nil {
- ctx.Error(http.StatusInternalServerError, "CheckPrsForBaseBranch", err)
+ ctx.APIErrorInternal(err)
return
}
} else {
@@ -1059,7 +1024,7 @@ func EditBranchProtection(ctx *context.APIContext) {
if ctx.Repo.GitRepo == nil {
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -1067,13 +1032,13 @@ func EditBranchProtection(ctx *context.APIContext) {
// FIXME: since we only need to recheck files protected rules, we could improve this
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.Repository.ID, protectBranch.RuleName)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindAllMatchedBranches", err)
+ ctx.APIErrorInternal(err)
return
}
for _, branchName := range matchedBranches {
if err = pull_service.CheckPRsForBaseBranch(ctx, ctx.Repo.Repository, branchName); err != nil {
- ctx.Error(http.StatusInternalServerError, "CheckPrsForBaseBranch", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -1083,11 +1048,11 @@ func EditBranchProtection(ctx *context.APIContext) {
// Reload from db to ensure get all whitelists
bp, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, bpName)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedBranchBy", err)
+ ctx.APIErrorInternal(err)
return
}
if bp == nil || bp.RepoID != ctx.Repo.Repository.ID {
- ctx.Error(http.StatusInternalServerError, "New branch protection not found", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -1127,16 +1092,16 @@ func DeleteBranchProtection(ctx *context.APIContext) {
bpName := ctx.PathParam("name")
bp, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, bpName)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
+ ctx.APIErrorInternal(err)
return
}
if bp == nil || bp.RepoID != repo.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if err := git_model.DeleteProtectedBranch(ctx, ctx.Repo.Repository, bp.ID); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteProtectedBranch", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -1180,9 +1145,53 @@ func UpdateBranchProtectionPriories(ctx *context.APIContext) {
repo := ctx.Repo.Repository
if err := git_model.UpdateProtectBranchPriorities(ctx, repo, form.IDs); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateProtectBranchPriorities", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
+
+func MergeUpstream(ctx *context.APIContext) {
+ // swagger:operation POST /repos/{owner}/{repo}/merge-upstream repository repoMergeUpstream
+ // ---
+ // summary: Merge a branch from upstream
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: body
+ // in: body
+ // schema:
+ // "$ref": "#/definitions/MergeUpstreamRequest"
+ // responses:
+ // "200":
+ // "$ref": "#/responses/MergeUpstreamResponse"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ form := web.GetForm(ctx).(*api.MergeUpstreamRequest)
+ mergeStyle, err := repo_service.MergeUpstream(ctx, ctx.Doer, ctx.Repo.Repository, form.Branch, form.FfOnly)
+ if err != nil {
+ if errors.Is(err, util.ErrInvalidArgument) {
+ ctx.APIError(http.StatusBadRequest, err)
+ return
+ } else if errors.Is(err, util.ErrNotExist) {
+ ctx.APIError(http.StatusNotFound, err)
+ return
+ }
+ ctx.APIErrorInternal(err)
+ return
+ }
+ ctx.JSON(http.StatusOK, &api.MergeUpstreamResponse{MergeStyle: mergeStyle})
+}
diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go
index da3ee54e69..c2c10cc695 100644
--- a/routers/api/v1/repo/collaborators.go
+++ b/routers/api/v1/repo/collaborators.go
@@ -7,6 +7,7 @@ package repo
import (
"errors"
"net/http"
+ "strings"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
@@ -59,7 +60,7 @@ func ListCollaborators(ctx *context.APIContext) {
RepoID: ctx.Repo.Repository.ID,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -92,7 +93,7 @@ func IsCollaborator(ctx *context.APIContext) {
// required: true
// - name: collaborator
// in: path
- // description: username of the collaborator
+ // description: username of the user to check for being a collaborator
// type: string
// required: true
// responses:
@@ -106,21 +107,21 @@ func IsCollaborator(ctx *context.APIContext) {
user, err := user_model.GetUserByName(ctx, ctx.PathParam("collaborator"))
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
isColab, err := repo_model.IsCollaborator(ctx, ctx.Repo.Repository.ID, user.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsCollaborator", err)
+ ctx.APIErrorInternal(err)
return
}
if isColab {
ctx.Status(http.StatusNoContent)
} else {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
}
}
@@ -144,7 +145,7 @@ func AddOrUpdateCollaborator(ctx *context.APIContext) {
// required: true
// - name: collaborator
// in: path
- // description: username of the collaborator to add
+ // description: username of the user to add or update as a collaborator
// type: string
// required: true
// - name: body
@@ -166,28 +167,28 @@ func AddOrUpdateCollaborator(ctx *context.APIContext) {
collaborator, err := user_model.GetUserByName(ctx, ctx.PathParam("collaborator"))
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
if !collaborator.IsActive {
- ctx.Error(http.StatusInternalServerError, "InactiveCollaborator", errors.New("collaborator's account is inactive"))
+ ctx.APIErrorInternal(errors.New("collaborator's account is inactive"))
return
}
p := perm.AccessModeWrite
if form.Permission != nil {
- p = perm.ParseAccessMode(*form.Permission)
+ p = perm.ParseAccessMode(*form.Permission, perm.AccessModeRead, perm.AccessModeWrite, perm.AccessModeAdmin)
}
if err := repo_service.AddOrUpdateCollaborator(ctx, ctx.Repo.Repository, collaborator, p); err != nil {
if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, "AddOrUpdateCollaborator", err)
+ ctx.APIError(http.StatusForbidden, err)
} else {
- ctx.Error(http.StatusInternalServerError, "AddOrUpdateCollaborator", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -229,15 +230,15 @@ func DeleteCollaborator(ctx *context.APIContext) {
collaborator, err := user_model.GetUserByName(ctx, ctx.PathParam("collaborator"))
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err := repo_service.DeleteCollaboration(ctx, ctx.Repo.Repository, collaborator); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteCollaboration", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
@@ -263,7 +264,7 @@ func GetRepoPermissions(ctx *context.APIContext) {
// required: true
// - name: collaborator
// in: path
- // description: username of the collaborator
+ // description: username of the collaborator whose permissions are to be obtained
// type: string
// required: true
// responses:
@@ -274,24 +275,25 @@ func GetRepoPermissions(ctx *context.APIContext) {
// "403":
// "$ref": "#/responses/forbidden"
- if !ctx.Doer.IsAdmin && ctx.Doer.LoginName != ctx.PathParam("collaborator") && !ctx.IsUserRepoAdmin() {
- ctx.Error(http.StatusForbidden, "User", "Only admins can query all permissions, repo admins can query all repo permissions, collaborators can query only their own")
+ collaboratorUsername := ctx.PathParam("collaborator")
+ if !ctx.Doer.IsAdmin && ctx.Doer.LowerName != strings.ToLower(collaboratorUsername) && !ctx.IsUserRepoAdmin() {
+ ctx.APIError(http.StatusForbidden, "Only admins can query all permissions, repo admins can query all repo permissions, collaborators can query only their own")
return
}
- collaborator, err := user_model.GetUserByName(ctx, ctx.PathParam("collaborator"))
+ collaborator, err := user_model.GetUserByName(ctx, collaboratorUsername)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusNotFound, "GetUserByName", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
permission, err := access_model.GetUserRepoPermission(ctx, ctx.Repo.Repository, collaborator)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -324,13 +326,13 @@ func GetReviewers(ctx *context.APIContext) {
canChooseReviewer := issue_service.CanDoerChangeReviewRequests(ctx, ctx.Doer, ctx.Repo.Repository, 0)
if !canChooseReviewer {
- ctx.Error(http.StatusForbidden, "GetReviewers", errors.New("doer has no permission to get reviewers"))
+ ctx.APIError(http.StatusForbidden, errors.New("doer has no permission to get reviewers"))
return
}
reviewers, err := pull_service.GetReviewers(ctx, ctx.Repo.Repository, ctx.Doer.ID, 0)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToUsers(ctx, ctx.Doer, reviewers))
@@ -362,7 +364,7 @@ func GetAssignees(ctx *context.APIContext) {
assignees, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToUsers(ctx, ctx.Doer, assignees))
diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go
index 3b144d0c43..6a93be624f 100644
--- a/routers/api/v1/repo/commits.go
+++ b/routers/api/v1/repo/commits.go
@@ -5,10 +5,10 @@
package repo
import (
- "fmt"
"math"
"net/http"
"strconv"
+ "time"
issues_model "code.gitea.io/gitea/models/issues"
user_model "code.gitea.io/gitea/models/user"
@@ -65,7 +65,7 @@ func GetSingleCommit(ctx *context.APIContext) {
sha := ctx.PathParam("sha")
if !git.IsValidRefPattern(sha) {
- ctx.Error(http.StatusUnprocessableEntity, "no valid ref or sha", fmt.Sprintf("no valid ref or sha: %s", sha))
+ ctx.APIError(http.StatusUnprocessableEntity, "no valid ref or sha: "+sha)
return
}
@@ -76,16 +76,16 @@ func getCommit(ctx *context.APIContext, identifier string, toCommitOpts convert.
commit, err := ctx.Repo.GitRepo.GetCommit(identifier)
if err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound(identifier)
+ ctx.APIErrorNotFound("commit doesn't exist: " + identifier)
return
}
- ctx.Error(http.StatusInternalServerError, "gitRepo.GetCommit", err)
+ ctx.APIErrorInternal(err)
return
}
json, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, commit, nil, toCommitOpts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "toCommit", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, json)
@@ -117,6 +117,16 @@ func GetAllCommits(ctx *context.APIContext) {
// in: query
// description: filepath of a file/dir
// type: string
+ // - name: since
+ // in: query
+ // description: Only commits after this date will be returned (ISO 8601 format)
+ // type: string
+ // format: date-time
+ // - name: until
+ // in: query
+ // description: Only commits before this date will be returned (ISO 8601 format)
+ // type: string
+ // format: date-time
// - name: stat
// in: query
// description: include diff stats for every commit (disable for speedup, default 'true')
@@ -149,6 +159,23 @@ func GetAllCommits(ctx *context.APIContext) {
// "409":
// "$ref": "#/responses/EmptyRepository"
+ since := ctx.FormString("since")
+ until := ctx.FormString("until")
+
+ // Validate since/until as ISO 8601 (RFC3339)
+ if since != "" {
+ if _, err := time.Parse(time.RFC3339, since); err != nil {
+ ctx.APIError(http.StatusUnprocessableEntity, "invalid 'since' format, expected ISO 8601 (RFC3339)")
+ return
+ }
+ }
+ if until != "" {
+ if _, err := time.Parse(time.RFC3339, until); err != nil {
+ ctx.APIError(http.StatusUnprocessableEntity, "invalid 'until' format, expected ISO 8601 (RFC3339)")
+ return
+ }
+ }
+
if ctx.Repo.Repository.IsEmpty {
ctx.JSON(http.StatusConflict, api.APIError{
Message: "Git Repository is empty.",
@@ -180,22 +207,16 @@ func GetAllCommits(ctx *context.APIContext) {
var baseCommit *git.Commit
if len(sha) == 0 {
// no sha supplied - use default branch
- head, err := ctx.Repo.GitRepo.GetHEADBranch()
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetHEADBranch", err)
- return
- }
-
- baseCommit, err = ctx.Repo.GitRepo.GetBranchCommit(head.Name)
+ baseCommit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetCommit", err)
+ ctx.APIErrorInternal(err)
return
}
} else {
// get commit specified by sha
baseCommit, err = ctx.Repo.GitRepo.GetCommit(sha)
if err != nil {
- ctx.NotFoundOrServerError("GetCommit", git.IsErrNotExist, err)
+ ctx.NotFoundOrServerError(err)
return
}
}
@@ -205,16 +226,18 @@ func GetAllCommits(ctx *context.APIContext) {
RepoPath: ctx.Repo.GitRepo.Path,
Not: not,
Revision: []string{baseCommit.ID.String()},
+ Since: since,
+ Until: until,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetCommitsCount", err)
+ ctx.APIErrorInternal(err)
return
}
// Query commits
- commits, err = baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize, not)
+ commits, err = baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize, not, since, until)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "CommitsByRange", err)
+ ctx.APIErrorInternal(err)
return
}
} else {
@@ -228,13 +251,15 @@ func GetAllCommits(ctx *context.APIContext) {
Not: not,
Revision: []string{sha},
RelPath: []string{path},
+ Since: since,
+ Until: until,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FileCommitsCount", err)
+ ctx.APIErrorInternal(err)
return
} else if commitsCountTotal == 0 {
- ctx.NotFound("FileCommitsCount", nil)
+ ctx.APIErrorNotFound("FileCommitsCount", nil)
return
}
@@ -244,9 +269,11 @@ func GetAllCommits(ctx *context.APIContext) {
File: path,
Not: not,
Page: listOptions.Page,
+ Since: since,
+ Until: until,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "CommitsByFileAndRange", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -259,7 +286,7 @@ func GetAllCommits(ctx *context.APIContext) {
// Create json struct
apiCommits[i], err = convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, commit, userCache, convert.ParseCommitOptions(ctx))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "toCommit", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -317,10 +344,10 @@ func DownloadCommitDiffOrPatch(ctx *context.APIContext) {
if err := git.GetRawDiff(ctx.Repo.GitRepo, sha, diffType, ctx.Resp); err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound(sha)
+ ctx.APIErrorNotFound("commit doesn't exist: " + sha)
return
}
- ctx.Error(http.StatusInternalServerError, "DownloadCommitDiffOrPatch", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -357,19 +384,19 @@ func GetCommitPullRequest(ctx *context.APIContext) {
pr, err := issues_model.GetPullRequestByMergedCommit(ctx, ctx.Repo.Repository.ID, ctx.PathParam("sha"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.Error(http.StatusNotFound, "GetPullRequestByMergedCommit", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err = pr.LoadBaseRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err)
+ ctx.APIErrorInternal(err)
return
}
if err = pr.LoadHeadRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(ctx, pr, ctx.Doer))
diff --git a/routers/api/v1/repo/compare.go b/routers/api/v1/repo/compare.go
index a1813a8a76..6d427c8073 100644
--- a/routers/api/v1/repo/compare.go
+++ b/routers/api/v1/repo/compare.go
@@ -47,7 +47,7 @@ func CompareDiff(ctx *context.APIContext) {
var err error
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -82,7 +82,7 @@ func CompareDiff(ctx *context.APIContext) {
Files: files,
})
if err != nil {
- ctx.ServerError("toCommit", err)
+ ctx.APIErrorInternal(err)
return
}
apiCommits = append(apiCommits, apiCommit)
diff --git a/routers/api/v1/repo/download.go b/routers/api/v1/repo/download.go
index a8a23c4a8d..acd93ecf2e 100644
--- a/routers/api/v1/repo/download.go
+++ b/routers/api/v1/repo/download.go
@@ -4,7 +4,6 @@
package repo
import (
- "fmt"
"net/http"
"code.gitea.io/gitea/modules/git"
@@ -17,13 +16,13 @@ func DownloadArchive(ctx *context.APIContext) {
var tp git.ArchiveType
switch ballType := ctx.PathParam("ball_type"); ballType {
case "tarball":
- tp = git.TARGZ
+ tp = git.ArchiveTarGz
case "zipball":
- tp = git.ZIP
+ tp = git.ArchiveZip
case "bundle":
- tp = git.BUNDLE
+ tp = git.ArchiveBundle
default:
- ctx.Error(http.StatusBadRequest, "", fmt.Sprintf("Unknown archive type: %s", ballType))
+ ctx.APIError(http.StatusBadRequest, "Unknown archive type: "+ballType)
return
}
@@ -31,20 +30,20 @@ func DownloadArchive(ctx *context.APIContext) {
var err error
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
+ ctx.APIErrorInternal(err)
return
}
}
- r, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"), tp)
+ r, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*")+"."+tp.String())
if err != nil {
- ctx.ServerError("NewRequest", err)
+ ctx.APIErrorInternal(err)
return
}
archive, err := r.Await(ctx)
if err != nil {
- ctx.ServerError("archive.Await", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go
index 1ad55d225b..69b5996222 100644
--- a/routers/api/v1/repo/file.go
+++ b/routers/api/v1/repo/file.go
@@ -16,16 +16,18 @@ import (
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/httpcache"
+ "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/routers/api/v1/utils"
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/context"
pull_service "code.gitea.io/gitea/services/pull"
@@ -60,7 +62,7 @@ func GetRawFile(ctx *context.APIContext) {
// required: true
// - name: ref
// in: query
- // description: "The name of the commit/branch/tag. Default the repository’s default branch"
+ // description: "The name of the commit/branch/tag. Default to the repository’s default branch"
// type: string
// required: false
// responses:
@@ -72,7 +74,7 @@ func GetRawFile(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if ctx.Repo.Repository.IsEmpty {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -83,8 +85,8 @@ func GetRawFile(ctx *context.APIContext) {
ctx.RespHeader().Set(giteaObjectTypeHeader, string(files_service.GetObjectTypeFromTreeEntry(entry)))
- if err := common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified); err != nil {
- ctx.Error(http.StatusInternalServerError, "ServeBlob", err)
+ if err := common.ServeBlob(ctx.Base, ctx.Repo.Repository, ctx.Repo.TreePath, blob, lastModified); err != nil {
+ ctx.APIErrorInternal(err)
}
}
@@ -113,7 +115,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
// required: true
// - name: ref
// in: query
- // description: "The name of the commit/branch/tag. Default the repository’s default branch"
+ // description: "The name of the commit/branch/tag. Default to the repository’s default branch"
// type: string
// required: false
// responses:
@@ -125,7 +127,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if ctx.Repo.Repository.IsEmpty {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -137,31 +139,31 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
ctx.RespHeader().Set(giteaObjectTypeHeader, string(files_service.GetObjectTypeFromTreeEntry(entry)))
// LFS Pointer files are at most 1024 bytes - so any blob greater than 1024 bytes cannot be an LFS file
- if blob.Size() > 1024 {
+ if blob.Size() > lfs.MetaFileMaxSize {
// First handle caching for the blob
if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) {
return
}
- // OK not cached - serve!
- if err := common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified); err != nil {
- ctx.ServerError("ServeBlob", err)
+ // If not cached - serve!
+ if err := common.ServeBlob(ctx.Base, ctx.Repo.Repository, ctx.Repo.TreePath, blob, lastModified); err != nil {
+ ctx.APIErrorInternal(err)
}
return
}
- // OK, now the blob is known to have at most 1024 bytes we can simply read this in one go (This saves reading it twice)
+ // OK, now the blob is known to have at most 1024 (lfs pointer max size) bytes,
+ // we can simply read this in one go (This saves reading it twice)
dataRc, err := blob.DataAsync()
if err != nil {
- ctx.ServerError("DataAsync", err)
+ ctx.APIErrorInternal(err)
return
}
- // FIXME: code from #19689, what if the file is large ... OOM ...
buf, err := io.ReadAll(dataRc)
if err != nil {
_ = dataRc.Close()
- ctx.ServerError("DataAsync", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -179,7 +181,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
return
}
- // OK not cached - serve!
+ // If not cached - serve!
common.ServeContentByReader(ctx.Base, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf))
return
}
@@ -188,7 +190,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
meta, err := git_model.GetLFSMetaObjectByOid(ctx, ctx.Repo.Repository.ID, pointer.Oid)
// If there isn't one, just serve the data directly
- if err == git_model.ErrLFSObjectNotExist {
+ if errors.Is(err, git_model.ErrLFSObjectNotExist) {
// Handle caching for the blob SHA (not the LFS object OID)
if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) {
return
@@ -197,7 +199,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
common.ServeContentByReader(ctx.Base, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf))
return
} else if err != nil {
- ctx.ServerError("GetLFSMetaObjectByOid", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -217,7 +219,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
lfsDataRc, err := lfs.ReadMetaObject(meta.Pointer)
if err != nil {
- ctx.ServerError("ReadMetaObject", err)
+ ctx.APIErrorInternal(err)
return
}
defer lfsDataRc.Close()
@@ -229,21 +231,21 @@ func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEn
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
if err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetTreeEntryByPath", err)
+ ctx.APIErrorInternal(err)
}
return nil, nil, nil
}
if entry.IsDir() || entry.IsSubModule() {
- ctx.NotFound("getBlobForEntry", nil)
+ ctx.APIErrorNotFound("getBlobForEntry", nil)
return nil, nil, nil
}
latestCommit, err := ctx.Repo.GitRepo.GetTreePathLatestCommit(ctx.Repo.Commit.ID.String(), ctx.Repo.TreePath)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetTreePathLatestCommit", err)
+ ctx.APIErrorInternal(err)
return nil, nil, nil
}
when := &latestCommit.Committer.When
@@ -284,7 +286,7 @@ func GetArchive(ctx *context.APIContext) {
var err error
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -293,28 +295,21 @@ func GetArchive(ctx *context.APIContext) {
}
func archiveDownload(ctx *context.APIContext) {
- uri := ctx.PathParam("*")
- ext, tp, err := archiver_service.ParseFileName(uri)
- if err != nil {
- ctx.Error(http.StatusBadRequest, "ParseFileName", err)
- return
- }
-
- aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp)
+ aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"))
if err != nil {
if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) {
- ctx.Error(http.StatusBadRequest, "unknown archive format", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, archiver_service.RepoRefNotFoundError{}) {
- ctx.Error(http.StatusNotFound, "unrecognized reference", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.ServerError("archiver_service.NewRequest", err)
+ ctx.APIErrorInternal(err)
}
return
}
archiver, err := aReq.Await(ctx)
if err != nil {
- ctx.ServerError("archiver.Await", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -346,7 +341,7 @@ func download(ctx *context.APIContext, archiveName string, archiver *repo_model.
// If we have matched and access to release or issue
fr, err := storage.RepoArchives.Open(rPath)
if err != nil {
- ctx.ServerError("Open", err)
+ ctx.APIErrorInternal(err)
return
}
defer fr.Close()
@@ -382,7 +377,7 @@ func GetEditorconfig(ctx *context.APIContext) {
// required: true
// - name: ref
// in: query
- // description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)"
+ // description: "The name of the commit/branch/tag. Default to the repository’s default branch."
// type: string
// required: false
// responses:
@@ -394,9 +389,9 @@ func GetEditorconfig(ctx *context.APIContext) {
ec, _, err := ctx.Repo.GetEditorconfig(ctx.Repo.Commit)
if err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetEditorconfig", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -404,24 +399,12 @@ func GetEditorconfig(ctx *context.APIContext) {
fileName := ctx.PathParam("filename")
def, err := ec.GetDefinitionForFilename(fileName)
if def == nil {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
return
}
ctx.JSON(http.StatusOK, def)
}
-// canWriteFiles returns true if repository is editable and user has proper access level.
-func canWriteFiles(ctx *context.APIContext, branch string) bool {
- return ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, branch) &&
- !ctx.Repo.Repository.IsMirror &&
- !ctx.Repo.Repository.IsArchived
-}
-
-// canReadFiles returns true if repository is readable and user has proper access level.
-func canReadFiles(r *context.Repository) bool {
- return r.Permission.CanRead(unit.TypeCode)
-}
-
func base64Reader(s string) (io.ReadSeeker, error) {
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
@@ -430,6 +413,45 @@ func base64Reader(s string) (io.ReadSeeker, error) {
return bytes.NewReader(b), nil
}
+func ReqChangeRepoFileOptionsAndCheck(ctx *context.APIContext) {
+ commonOpts := web.GetForm(ctx).(api.FileOptionsInterface).GetFileOptions()
+ commonOpts.BranchName = util.IfZero(commonOpts.BranchName, ctx.Repo.Repository.DefaultBranch)
+ commonOpts.NewBranchName = util.IfZero(commonOpts.NewBranchName, commonOpts.BranchName)
+ if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, commonOpts.NewBranchName) && !ctx.IsUserSiteAdmin() {
+ ctx.APIError(http.StatusForbidden, "user should have a permission to write to the target branch")
+ return
+ }
+ changeFileOpts := &files_service.ChangeRepoFilesOptions{
+ Message: commonOpts.Message,
+ OldBranch: commonOpts.BranchName,
+ NewBranch: commonOpts.NewBranchName,
+ Committer: &files_service.IdentityOptions{
+ GitUserName: commonOpts.Committer.Name,
+ GitUserEmail: commonOpts.Committer.Email,
+ },
+ Author: &files_service.IdentityOptions{
+ GitUserName: commonOpts.Author.Name,
+ GitUserEmail: commonOpts.Author.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: commonOpts.Dates.Author,
+ Committer: commonOpts.Dates.Committer,
+ },
+ Signoff: commonOpts.Signoff,
+ }
+ if commonOpts.Dates.Author.IsZero() {
+ commonOpts.Dates.Author = time.Now()
+ }
+ if commonOpts.Dates.Committer.IsZero() {
+ commonOpts.Dates.Committer = time.Now()
+ }
+ ctx.Data["__APIChangeRepoFilesOptions"] = changeFileOpts
+}
+
+func getAPIChangeRepoFileOptions[T api.FileOptionsInterface](ctx *context.APIContext) (apiOpts T, opts *files_service.ChangeRepoFilesOptions) {
+ return web.GetForm(ctx).(T), ctx.Data["__APIChangeRepoFilesOptions"].(*files_service.ChangeRepoFilesOptions)
+}
+
// ChangeFiles handles API call for modifying multiple files
func ChangeFiles(ctx *context.APIContext) {
// swagger:operation POST /repos/{owner}/{repo}/contents repository repoChangeFiles
@@ -466,20 +488,18 @@ func ChangeFiles(ctx *context.APIContext) {
// "$ref": "#/responses/error"
// "423":
// "$ref": "#/responses/repoArchivedError"
-
- apiOpts := web.GetForm(ctx).(*api.ChangeFilesOptions)
-
- if apiOpts.BranchName == "" {
- apiOpts.BranchName = ctx.Repo.Repository.DefaultBranch
+ apiOpts, opts := getAPIChangeRepoFileOptions[*api.ChangeFilesOptions](ctx)
+ if ctx.Written() {
+ return
}
-
- var files []*files_service.ChangeRepoFile
for _, file := range apiOpts.Files {
contentReader, err := base64Reader(file.ContentBase64)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "Invalid base64 content", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
+ // FIXME: ChangeFileOperation.SHA is NOT required for update or delete if last commit is provided in the options
+ // But the LastCommitID is not provided in the API options, need to fully fix them in API
changeRepoFile := &files_service.ChangeRepoFile{
Operation: file.Operation,
TreePath: file.Path,
@@ -487,41 +507,15 @@ func ChangeFiles(ctx *context.APIContext) {
ContentReader: contentReader,
SHA: file.SHA,
}
- files = append(files, changeRepoFile)
- }
-
- opts := &files_service.ChangeRepoFilesOptions{
- Files: files,
- Message: apiOpts.Message,
- OldBranch: apiOpts.BranchName,
- NewBranch: apiOpts.NewBranchName,
- Committer: &files_service.IdentityOptions{
- Name: apiOpts.Committer.Name,
- Email: apiOpts.Committer.Email,
- },
- Author: &files_service.IdentityOptions{
- Name: apiOpts.Author.Name,
- Email: apiOpts.Author.Email,
- },
- Dates: &files_service.CommitDateOptions{
- Author: apiOpts.Dates.Author,
- Committer: apiOpts.Dates.Committer,
- },
- Signoff: apiOpts.Signoff,
- }
- if opts.Dates.Author.IsZero() {
- opts.Dates.Author = time.Now()
- }
- if opts.Dates.Committer.IsZero() {
- opts.Dates.Committer = time.Now()
+ opts.Files = append(opts.Files, changeRepoFile)
}
if opts.Message == "" {
- opts.Message = changeFilesCommitMessage(ctx, files)
+ opts.Message = changeFilesCommitMessage(ctx, opts.Files)
}
- if filesResponse, err := createOrUpdateFiles(ctx, opts); err != nil {
- handleCreateOrUpdateFileError(ctx, err)
+ if filesResponse, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, opts); err != nil {
+ handleChangeRepoFilesError(ctx, err)
} else {
ctx.JSON(http.StatusCreated, filesResponse)
}
@@ -569,56 +563,27 @@ func CreateFile(ctx *context.APIContext) {
// "423":
// "$ref": "#/responses/repoArchivedError"
- apiOpts := web.GetForm(ctx).(*api.CreateFileOptions)
-
- if apiOpts.BranchName == "" {
- apiOpts.BranchName = ctx.Repo.Repository.DefaultBranch
+ apiOpts, opts := getAPIChangeRepoFileOptions[*api.CreateFileOptions](ctx)
+ if ctx.Written() {
+ return
}
-
contentReader, err := base64Reader(apiOpts.ContentBase64)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "Invalid base64 content", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- opts := &files_service.ChangeRepoFilesOptions{
- Files: []*files_service.ChangeRepoFile{
- {
- Operation: "create",
- TreePath: ctx.PathParam("*"),
- ContentReader: contentReader,
- },
- },
- Message: apiOpts.Message,
- OldBranch: apiOpts.BranchName,
- NewBranch: apiOpts.NewBranchName,
- Committer: &files_service.IdentityOptions{
- Name: apiOpts.Committer.Name,
- Email: apiOpts.Committer.Email,
- },
- Author: &files_service.IdentityOptions{
- Name: apiOpts.Author.Name,
- Email: apiOpts.Author.Email,
- },
- Dates: &files_service.CommitDateOptions{
- Author: apiOpts.Dates.Author,
- Committer: apiOpts.Dates.Committer,
- },
- Signoff: apiOpts.Signoff,
- }
- if opts.Dates.Author.IsZero() {
- opts.Dates.Author = time.Now()
- }
- if opts.Dates.Committer.IsZero() {
- opts.Dates.Committer = time.Now()
- }
-
+ opts.Files = append(opts.Files, &files_service.ChangeRepoFile{
+ Operation: "create",
+ TreePath: ctx.PathParam("*"),
+ ContentReader: contentReader,
+ })
if opts.Message == "" {
opts.Message = changeFilesCommitMessage(ctx, opts.Files)
}
- if filesResponse, err := createOrUpdateFiles(ctx, opts); err != nil {
- handleCreateOrUpdateFileError(ctx, err)
+ if filesResponse, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, opts); err != nil {
+ handleChangeRepoFilesError(ctx, err)
} else {
fileResponse := files_service.GetFileResponseFromFilesResponse(filesResponse, 0)
ctx.JSON(http.StatusCreated, fileResponse)
@@ -666,96 +631,55 @@ func UpdateFile(ctx *context.APIContext) {
// "$ref": "#/responses/error"
// "423":
// "$ref": "#/responses/repoArchivedError"
- apiOpts := web.GetForm(ctx).(*api.UpdateFileOptions)
- if ctx.Repo.Repository.IsEmpty {
- ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty"))
- return
- }
- if apiOpts.BranchName == "" {
- apiOpts.BranchName = ctx.Repo.Repository.DefaultBranch
+ apiOpts, opts := getAPIChangeRepoFileOptions[*api.UpdateFileOptions](ctx)
+ if ctx.Written() {
+ return
}
-
contentReader, err := base64Reader(apiOpts.ContentBase64)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "Invalid base64 content", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
-
- opts := &files_service.ChangeRepoFilesOptions{
- Files: []*files_service.ChangeRepoFile{
- {
- Operation: "update",
- ContentReader: contentReader,
- SHA: apiOpts.SHA,
- FromTreePath: apiOpts.FromPath,
- TreePath: ctx.PathParam("*"),
- },
- },
- Message: apiOpts.Message,
- OldBranch: apiOpts.BranchName,
- NewBranch: apiOpts.NewBranchName,
- Committer: &files_service.IdentityOptions{
- Name: apiOpts.Committer.Name,
- Email: apiOpts.Committer.Email,
- },
- Author: &files_service.IdentityOptions{
- Name: apiOpts.Author.Name,
- Email: apiOpts.Author.Email,
- },
- Dates: &files_service.CommitDateOptions{
- Author: apiOpts.Dates.Author,
- Committer: apiOpts.Dates.Committer,
- },
- Signoff: apiOpts.Signoff,
- }
- if opts.Dates.Author.IsZero() {
- opts.Dates.Author = time.Now()
- }
- if opts.Dates.Committer.IsZero() {
- opts.Dates.Committer = time.Now()
- }
-
+ opts.Files = append(opts.Files, &files_service.ChangeRepoFile{
+ Operation: "update",
+ ContentReader: contentReader,
+ SHA: apiOpts.SHA,
+ FromTreePath: apiOpts.FromPath,
+ TreePath: ctx.PathParam("*"),
+ })
if opts.Message == "" {
opts.Message = changeFilesCommitMessage(ctx, opts.Files)
}
- if filesResponse, err := createOrUpdateFiles(ctx, opts); err != nil {
- handleCreateOrUpdateFileError(ctx, err)
+ if filesResponse, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, opts); err != nil {
+ handleChangeRepoFilesError(ctx, err)
} else {
fileResponse := files_service.GetFileResponseFromFilesResponse(filesResponse, 0)
ctx.JSON(http.StatusOK, fileResponse)
}
}
-func handleCreateOrUpdateFileError(ctx *context.APIContext, err error) {
+func handleChangeRepoFilesError(ctx *context.APIContext, err error) {
if files_service.IsErrUserCannotCommit(err) || pull_service.IsErrFilePathProtected(err) {
- ctx.Error(http.StatusForbidden, "Access", err)
+ ctx.APIError(http.StatusForbidden, err)
return
}
if git_model.IsErrBranchAlreadyExists(err) || files_service.IsErrFilenameInvalid(err) || pull_service.IsErrSHADoesNotMatch(err) ||
- files_service.IsErrFilePathInvalid(err) || files_service.IsErrRepoFileAlreadyExists(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Invalid", err)
+ files_service.IsErrFilePathInvalid(err) || files_service.IsErrRepoFileAlreadyExists(err) ||
+ files_service.IsErrCommitIDDoesNotMatch(err) || files_service.IsErrSHAOrCommitIDNotProvided(err) {
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- if git_model.IsErrBranchNotExist(err) || git.IsErrBranchNotExist(err) {
- ctx.Error(http.StatusNotFound, "BranchDoesNotExist", err)
+ if git.IsErrBranchNotExist(err) || files_service.IsErrRepoFileDoesNotExist(err) || git.IsErrNotExist(err) {
+ ctx.APIError(http.StatusNotFound, err)
return
}
-
- ctx.Error(http.StatusInternalServerError, "UpdateFile", err)
-}
-
-// Called from both CreateFile or UpdateFile to handle both
-func createOrUpdateFiles(ctx *context.APIContext, opts *files_service.ChangeRepoFilesOptions) (*api.FilesResponse, error) {
- if !canWriteFiles(ctx, opts.OldBranch) {
- return nil, repo_model.ErrUserDoesNotHaveAccessToRepo{
- UserID: ctx.Doer.ID,
- RepoName: ctx.Repo.Repository.LowerName,
- }
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.APIError(http.StatusNotFound, err)
+ return
}
-
- return files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, opts)
+ ctx.APIErrorInternal(err)
}
// format commit message if empty
@@ -769,7 +693,7 @@ func changeFilesCommitMessage(ctx *context.APIContext, files []*files_service.Ch
switch file.Operation {
case "create":
createFiles = append(createFiles, file.TreePath)
- case "update":
+ case "update", "upload", "rename": // upload and rename works like "update", there is no translation for them at the moment
updateFiles = append(updateFiles, file.TreePath)
case "delete":
deleteFiles = append(deleteFiles, file.TreePath)
@@ -827,85 +751,119 @@ func DeleteFile(ctx *context.APIContext) {
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/error"
+ // "422":
+ // "$ref": "#/responses/error"
// "423":
// "$ref": "#/responses/repoArchivedError"
- apiOpts := web.GetForm(ctx).(*api.DeleteFileOptions)
- if !canWriteFiles(ctx, apiOpts.BranchName) {
- ctx.Error(http.StatusForbidden, "DeleteFile", repo_model.ErrUserDoesNotHaveAccessToRepo{
- UserID: ctx.Doer.ID,
- RepoName: ctx.Repo.Repository.LowerName,
- })
+ apiOpts, opts := getAPIChangeRepoFileOptions[*api.DeleteFileOptions](ctx)
+ if ctx.Written() {
return
}
- if apiOpts.BranchName == "" {
- apiOpts.BranchName = ctx.Repo.Repository.DefaultBranch
- }
-
- opts := &files_service.ChangeRepoFilesOptions{
- Files: []*files_service.ChangeRepoFile{
- {
- Operation: "delete",
- SHA: apiOpts.SHA,
- TreePath: ctx.PathParam("*"),
- },
- },
- Message: apiOpts.Message,
- OldBranch: apiOpts.BranchName,
- NewBranch: apiOpts.NewBranchName,
- Committer: &files_service.IdentityOptions{
- Name: apiOpts.Committer.Name,
- Email: apiOpts.Committer.Email,
- },
- Author: &files_service.IdentityOptions{
- Name: apiOpts.Author.Name,
- Email: apiOpts.Author.Email,
- },
- Dates: &files_service.CommitDateOptions{
- Author: apiOpts.Dates.Author,
- Committer: apiOpts.Dates.Committer,
- },
- Signoff: apiOpts.Signoff,
- }
- if opts.Dates.Author.IsZero() {
- opts.Dates.Author = time.Now()
- }
- if opts.Dates.Committer.IsZero() {
- opts.Dates.Committer = time.Now()
- }
-
+ opts.Files = append(opts.Files, &files_service.ChangeRepoFile{
+ Operation: "delete",
+ SHA: apiOpts.SHA,
+ TreePath: ctx.PathParam("*"),
+ })
if opts.Message == "" {
opts.Message = changeFilesCommitMessage(ctx, opts.Files)
}
if filesResponse, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, opts); err != nil {
- if git.IsErrBranchNotExist(err) || files_service.IsErrRepoFileDoesNotExist(err) || git.IsErrNotExist(err) {
- ctx.Error(http.StatusNotFound, "DeleteFile", err)
- return
- } else if git_model.IsErrBranchAlreadyExists(err) ||
- files_service.IsErrFilenameInvalid(err) ||
- pull_service.IsErrSHADoesNotMatch(err) ||
- files_service.IsErrCommitIDDoesNotMatch(err) ||
- files_service.IsErrSHAOrCommitIDNotProvided(err) {
- ctx.Error(http.StatusBadRequest, "DeleteFile", err)
- return
- } else if files_service.IsErrUserCannotCommit(err) {
- ctx.Error(http.StatusForbidden, "DeleteFile", err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "DeleteFile", err)
+ handleChangeRepoFilesError(ctx, err)
} else {
fileResponse := files_service.GetFileResponseFromFilesResponse(filesResponse, 0)
ctx.JSON(http.StatusOK, fileResponse) // FIXME on APIv2: return http.StatusNoContent
}
}
-// GetContents Get the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir
+func resolveRefCommit(ctx *context.APIContext, ref string, minCommitIDLen ...int) *utils.RefCommit {
+ ref = util.IfZero(ref, ctx.Repo.Repository.DefaultBranch)
+ refCommit, err := utils.ResolveRefCommit(ctx, ctx.Repo.Repository, ref, minCommitIDLen...)
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.APIErrorNotFound(err)
+ } else if err != nil {
+ ctx.APIErrorInternal(err)
+ }
+ return refCommit
+}
+
+func GetContentsExt(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/contents-ext/{filepath} repository repoGetContentsExt
+ // ---
+ // summary: The extended "contents" API, to get file metadata and/or content, or list a directory.
+ // description: It guarantees that only one of the response fields is set if the request succeeds.
+ // Users can pass "includes=file_content" or "includes=lfs_metadata" to retrieve more fields.
+ // "includes=file_content" only works for single file, if you need to retrieve file contents in batch,
+ // use "file-contents" API after listing the directory.
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: filepath
+ // in: path
+ // description: path of the dir, file, symlink or submodule in the repo. Swagger requires path parameter to be "required",
+ // you can leave it empty or pass a single dot (".") to get the root directory.
+ // type: string
+ // required: true
+ // - name: ref
+ // in: query
+ // description: the name of the commit/branch/tag, default to the repository’s default branch.
+ // type: string
+ // required: false
+ // - name: includes
+ // in: query
+ // description: By default this API's response only contains file's metadata. Use comma-separated "includes" options to retrieve more fields.
+ // Option "file_content" will try to retrieve the file content, "lfs_metadata" will try to retrieve LFS metadata,
+ // "commit_metadata" will try to retrieve commit metadata, and "commit_message" will try to retrieve commit message.
+ // type: string
+ // required: false
+ // responses:
+ // "200":
+ // "$ref": "#/responses/ContentsExtResponse"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ if treePath := ctx.PathParam("*"); treePath == "." || treePath == "/" {
+ ctx.SetPathParam("*", "") // workaround for swagger, it requires path parameter to be "required", but we need to list root directory
+ }
+ opts := files_service.GetContentsOrListOptions{TreePath: ctx.PathParam("*")}
+ for includeOpt := range strings.SplitSeq(ctx.FormString("includes"), ",") {
+ if includeOpt == "" {
+ continue
+ }
+ switch includeOpt {
+ case "file_content":
+ opts.IncludeSingleFileContent = true
+ case "lfs_metadata":
+ opts.IncludeLfsMetadata = true
+ case "commit_metadata":
+ opts.IncludeCommitMetadata = true
+ case "commit_message":
+ opts.IncludeCommitMessage = true
+ default:
+ ctx.APIError(http.StatusBadRequest, fmt.Sprintf("unknown include option %q", includeOpt))
+ return
+ }
+ }
+ ctx.JSON(http.StatusOK, getRepoContents(ctx, opts))
+}
+
func GetContents(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/contents/{filepath} repository repoGetContents
// ---
- // summary: Gets the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir
+ // summary: Gets the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir.
+ // description: This API follows GitHub's design, and it is not easy to use. Recommend users to use the "contents-ext" API instead.
// produces:
// - application/json
// parameters:
@@ -926,7 +884,7 @@ func GetContents(ctx *context.APIContext) {
// required: true
// - name: ref
// in: query
- // description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)"
+ // description: "The name of the commit/branch/tag. Default to the repository’s default branch."
// type: string
// required: false
// responses:
@@ -934,34 +892,38 @@ func GetContents(ctx *context.APIContext) {
// "$ref": "#/responses/ContentsResponse"
// "404":
// "$ref": "#/responses/notFound"
-
- if !canReadFiles(ctx.Repo) {
- ctx.Error(http.StatusInternalServerError, "GetContentsOrList", repo_model.ErrUserDoesNotHaveAccessToRepo{
- UserID: ctx.Doer.ID,
- RepoName: ctx.Repo.Repository.LowerName,
- })
+ ret := getRepoContents(ctx, files_service.GetContentsOrListOptions{
+ TreePath: ctx.PathParam("*"),
+ IncludeSingleFileContent: true,
+ IncludeCommitMetadata: true,
+ })
+ if ctx.Written() {
return
}
+ ctx.JSON(http.StatusOK, util.Iif[any](ret.FileContents != nil, ret.FileContents, ret.DirContents))
+}
- treePath := ctx.PathParam("*")
- ref := ctx.FormTrim("ref")
-
- if fileList, err := files_service.GetContentsOrList(ctx, ctx.Repo.Repository, treePath, ref); err != nil {
+func getRepoContents(ctx *context.APIContext, opts files_service.GetContentsOrListOptions) *api.ContentsExtResponse {
+ refCommit := resolveRefCommit(ctx, ctx.FormTrim("ref"))
+ if ctx.Written() {
+ return nil
+ }
+ ret, err := files_service.GetContentsOrList(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, refCommit, opts)
+ if err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound("GetContentsOrList", err)
- return
+ ctx.APIErrorNotFound("GetContentsOrList", err)
+ return nil
}
- ctx.Error(http.StatusInternalServerError, "GetContentsOrList", err)
- } else {
- ctx.JSON(http.StatusOK, fileList)
+ ctx.APIErrorInternal(err)
}
+ return &ret
}
-// GetContentsList Get the metadata of all the entries of the root dir
func GetContentsList(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/contents repository repoGetContentsList
// ---
- // summary: Gets the metadata of all the entries of the root dir
+ // summary: Gets the metadata of all the entries of the root dir.
+ // description: This API follows GitHub's design, and it is not easy to use. Recommend users to use our "contents-ext" API instead.
// produces:
// - application/json
// parameters:
@@ -977,7 +939,7 @@ func GetContentsList(ctx *context.APIContext) {
// required: true
// - name: ref
// in: query
- // description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)"
+ // description: "The name of the commit/branch/tag. Default to the repository’s default branch."
// type: string
// required: false
// responses:
@@ -989,3 +951,102 @@ func GetContentsList(ctx *context.APIContext) {
// same as GetContents(), this function is here because swagger fails if path is empty in GetContents() interface
GetContents(ctx)
}
+
+func GetFileContentsGet(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/file-contents repository repoGetFileContents
+ // ---
+ // summary: Get the metadata and contents of requested files
+ // description: See the POST method. This GET method supports using JSON encoded request body in query parameter.
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: ref
+ // in: query
+ // description: "The name of the commit/branch/tag. Default to the repository’s default branch."
+ // type: string
+ // required: false
+ // - name: body
+ // in: query
+ // description: "The JSON encoded body (see the POST request): {\"files\": [\"filename1\", \"filename2\"]}"
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/ContentsListResponse"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ // The POST method requires "write" permission, so we also support this "GET" method
+ handleGetFileContents(ctx)
+}
+
+func GetFileContentsPost(ctx *context.APIContext) {
+ // swagger:operation POST /repos/{owner}/{repo}/file-contents repository repoGetFileContentsPost
+ // ---
+ // summary: Get the metadata and contents of requested files
+ // description: Uses automatic pagination based on default page size and
+ // max response size and returns the maximum allowed number of files.
+ // Files which could not be retrieved are null. Files which are too large
+ // are being returned with `encoding == null`, `content == null` and `size > 0`,
+ // they can be requested separately by using the `download_url`.
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: ref
+ // in: query
+ // description: "The name of the commit/branch/tag. Default to the repository’s default branch."
+ // type: string
+ // required: false
+ // - name: body
+ // in: body
+ // required: true
+ // schema:
+ // "$ref": "#/definitions/GetFilesOptions"
+ // responses:
+ // "200":
+ // "$ref": "#/responses/ContentsListResponse"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ // This is actually a "read" request, but we need to accept a "files" list, then POST method seems easy to use.
+ // But the permission system requires that the caller must have "write" permission to use POST method.
+ // At the moment, there is no other way to get around the permission check, so there is a "GET" workaround method above.
+ handleGetFileContents(ctx)
+}
+
+func handleGetFileContents(ctx *context.APIContext) {
+ opts, ok := web.GetForm(ctx).(*api.GetFilesOptions)
+ if !ok {
+ err := json.Unmarshal(util.UnsafeStringToBytes(ctx.FormString("body")), &opts)
+ if err != nil {
+ ctx.APIError(http.StatusBadRequest, "invalid body parameter")
+ return
+ }
+ }
+ refCommit := resolveRefCommit(ctx, ctx.FormTrim("ref"))
+ if ctx.Written() {
+ return
+ }
+ filesResponse := files_service.GetContentsListFromTreePaths(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, refCommit, opts.Files)
+ ctx.JSON(http.StatusOK, util.SliceNilAsEmpty(filesResponse))
+}
diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go
index 14a1a8d1c4..58f66954e1 100644
--- a/routers/api/v1/repo/fork.go
+++ b/routers/api/v1/repo/fork.go
@@ -57,15 +57,15 @@ func ListForks(ctx *context.APIContext) {
forks, total, err := repo_service.FindForks(ctx, ctx.Repo.Repository, ctx.Doer, utils.GetListOptions(ctx))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindForks", err)
+ ctx.APIErrorInternal(err)
return
}
if err := repo_model.RepositoryList(forks).LoadOwners(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadOwners", err)
+ ctx.APIErrorInternal(err)
return
}
if err := repo_model.RepositoryList(forks).LoadUnits(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadUnits", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -73,7 +73,7 @@ func ListForks(ctx *context.APIContext) {
for i, fork := range forks {
permission, err := access_model.GetUserRepoPermission(ctx, fork, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
+ ctx.APIErrorInternal(err)
return
}
apiForks[i] = convert.ToRepo(ctx, fork, permission)
@@ -126,19 +126,21 @@ func CreateFork(ctx *context.APIContext) {
org, err := organization.GetOrgByName(ctx, *form.Organization)
if err != nil {
if organization.IsErrOrgNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetOrgByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
- isMember, err := org.IsOrgMember(ctx, ctx.Doer.ID)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsOrgMember", err)
- return
- } else if !isMember {
- ctx.Error(http.StatusForbidden, "isMemberNot", fmt.Sprintf("User is no Member of Organisation '%s'", org.Name))
- return
+ if !ctx.Doer.IsAdmin {
+ isMember, err := org.IsOrgMember(ctx, ctx.Doer.ID)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ } else if !isMember {
+ ctx.APIError(http.StatusForbidden, fmt.Sprintf("User is no Member of Organisation '%s'", org.Name))
+ return
+ }
}
forker = org.AsUser()
}
@@ -157,11 +159,11 @@ func CreateFork(ctx *context.APIContext) {
})
if err != nil {
if errors.Is(err, util.ErrAlreadyExist) || repo_model.IsErrReachLimitOfRepo(err) {
- ctx.Error(http.StatusConflict, "ForkRepository", err)
+ ctx.APIError(http.StatusConflict, err)
} else if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, "ForkRepository", err)
+ ctx.APIError(http.StatusForbidden, err)
} else {
- ctx.Error(http.StatusInternalServerError, "ForkRepository", err)
+ ctx.APIErrorInternal(err)
}
return
}
diff --git a/routers/api/v1/repo/git_hook.go b/routers/api/v1/repo/git_hook.go
index 9b66b69068..487c74e183 100644
--- a/routers/api/v1/repo/git_hook.go
+++ b/routers/api/v1/repo/git_hook.go
@@ -4,6 +4,7 @@
package repo
import (
+ "errors"
"net/http"
"code.gitea.io/gitea/modules/git"
@@ -39,7 +40,7 @@ func ListGitHooks(ctx *context.APIContext) {
hooks, err := ctx.Repo.GitRepo.Hooks()
if err != nil {
- ctx.Error(http.StatusInternalServerError, "Hooks", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -82,10 +83,10 @@ func GetGitHook(ctx *context.APIContext) {
hookID := ctx.PathParam("id")
hook, err := ctx.Repo.GitRepo.GetHook(hookID)
if err != nil {
- if err == git.ErrNotValidHook {
- ctx.NotFound()
+ if errors.Is(err, git.ErrNotValidHook) {
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetHook", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -129,17 +130,17 @@ func EditGitHook(ctx *context.APIContext) {
hookID := ctx.PathParam("id")
hook, err := ctx.Repo.GitRepo.GetHook(hookID)
if err != nil {
- if err == git.ErrNotValidHook {
- ctx.NotFound()
+ if errors.Is(err, git.ErrNotValidHook) {
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetHook", err)
+ ctx.APIErrorInternal(err)
}
return
}
hook.Content = form.Content
if err = hook.Update(); err != nil {
- ctx.Error(http.StatusInternalServerError, "hook.Update", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -178,17 +179,17 @@ func DeleteGitHook(ctx *context.APIContext) {
hookID := ctx.PathParam("id")
hook, err := ctx.Repo.GitRepo.GetHook(hookID)
if err != nil {
- if err == git.ErrNotValidHook {
- ctx.NotFound()
+ if errors.Is(err, git.ErrNotValidHook) {
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetHook", err)
+ ctx.APIErrorInternal(err)
}
return
}
hook.Content = ""
if err = hook.Update(); err != nil {
- ctx.Error(http.StatusInternalServerError, "hook.Update", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/repo/git_ref.go b/routers/api/v1/repo/git_ref.go
index 1743c0fc20..f042e9e344 100644
--- a/routers/api/v1/repo/git_ref.go
+++ b/routers/api/v1/repo/git_ref.go
@@ -4,6 +4,7 @@
package repo
import (
+ "fmt"
"net/http"
"net/url"
@@ -77,12 +78,12 @@ func GetGitRefs(ctx *context.APIContext) {
func getGitRefsInternal(ctx *context.APIContext, filter string) {
refs, lastMethodName, err := utils.GetGitRefs(ctx, filter)
if err != nil {
- ctx.Error(http.StatusInternalServerError, lastMethodName, err)
+ ctx.APIErrorInternal(fmt.Errorf("%s: %w", lastMethodName, err))
return
}
if len(refs) == 0 {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go
index 03143c8f99..ac47e15d64 100644
--- a/routers/api/v1/repo/hook.go
+++ b/routers/api/v1/repo/hook.go
@@ -61,7 +61,7 @@ func ListHooks(ctx *context.APIContext) {
hooks, count, err := db.FindAndCount[webhook.Webhook](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -69,7 +69,7 @@ func ListHooks(ctx *context.APIContext) {
for i := range hooks {
apiHooks[i], err = webhook_service.ToHook(ctx.Repo.RepoLink, hooks[i])
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -116,7 +116,7 @@ func GetHook(ctx *context.APIContext) {
}
apiHook, err := webhook_service.ToHook(repo.RepoLink, hook)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, apiHook)
@@ -189,7 +189,7 @@ func TestHook(ctx *context.APIContext) {
Pusher: convert.ToUserWithAccessMode(ctx, ctx.Doer, perm.AccessModeNone),
Sender: convert.ToUserWithAccessMode(ctx, ctx.Doer, perm.AccessModeNone),
}); err != nil {
- ctx.Error(http.StatusInternalServerError, "PrepareWebhook: ", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -298,9 +298,9 @@ func DeleteHook(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if err := webhook.DeleteWebhookByRepoID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("id")); err != nil {
if webhook.IsErrWebhookNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteWebhookByRepoID", err)
+ ctx.APIErrorInternal(err)
}
return
}
diff --git a/routers/api/v1/repo/hook_test.go b/routers/api/v1/repo/hook_test.go
index c659a16f54..f8d61ccf00 100644
--- a/routers/api/v1/repo/hook_test.go
+++ b/routers/api/v1/repo/hook_test.go
@@ -23,7 +23,7 @@ func TestTestHook(t *testing.T) {
contexttest.LoadRepoCommit(t, ctx)
contexttest.LoadUser(t, ctx, 2)
TestHook(ctx)
- assert.EqualValues(t, http.StatusNoContent, ctx.Resp.Status())
+ assert.Equal(t, http.StatusNoContent, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{
HookID: 1,
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 86dbcee5f7..d4a5872fd1 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -132,7 +132,7 @@ func SearchIssues(ctx *context.APIContext) {
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
@@ -152,7 +152,7 @@ func SearchIssues(ctx *context.APIContext) {
)
{
// find repos user can access (for issue search)
- opts := &repo_model.SearchRepoOptions{
+ opts := repo_model.SearchRepoOptions{
Private: false,
AllPublic: true,
TopicOnly: false,
@@ -170,9 +170,9 @@ func SearchIssues(ctx *context.APIContext) {
owner, err := user_model.GetUserByName(ctx, ctx.FormString("owner"))
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusBadRequest, "Owner not found", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -183,15 +183,15 @@ func SearchIssues(ctx *context.APIContext) {
}
if ctx.FormString("team") != "" {
if ctx.FormString("owner") == "" {
- ctx.Error(http.StatusBadRequest, "", "Owner organisation is required for filtering on team")
+ ctx.APIError(http.StatusBadRequest, "Owner organisation is required for filtering on team")
return
}
team, err := organization.GetTeam(ctx, opts.OwnerID, ctx.FormString("team"))
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusBadRequest, "Team not found", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -204,7 +204,7 @@ func SearchIssues(ctx *context.APIContext) {
}
repoIDs, _, err = repo_model.SearchRepositoryIDs(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "SearchRepositoryIDs", err)
+ ctx.APIErrorInternal(err)
return
}
if len(repoIDs) == 0 {
@@ -237,7 +237,7 @@ func SearchIssues(ctx *context.APIContext) {
}
includedAnyLabels, err = issues_model.GetLabelIDsByNames(ctx, includedLabelNames)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLabelIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -251,7 +251,7 @@ func SearchIssues(ctx *context.APIContext) {
}
includedMilestones, err = issues_model.GetMilestoneIDsByNames(ctx, includedMilestoneNames)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetMilestoneIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -290,10 +290,10 @@ func SearchIssues(ctx *context.APIContext) {
if ctx.IsSigned {
ctxUserID := ctx.Doer.ID
if ctx.FormBool("created") {
- searchOpt.PosterID = optional.Some(ctxUserID)
+ searchOpt.PosterID = strconv.FormatInt(ctxUserID, 10)
}
if ctx.FormBool("assigned") {
- searchOpt.AssigneeID = optional.Some(ctxUserID)
+ searchOpt.AssigneeID = strconv.FormatInt(ctxUserID, 10)
}
if ctx.FormBool("mentioned") {
searchOpt.MentionID = optional.Some(ctxUserID)
@@ -312,12 +312,12 @@ func SearchIssues(ctx *context.APIContext) {
ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "SearchIssues", err)
+ ctx.APIErrorInternal(err)
return
}
issues, err := issues_model.GetIssuesByIDs(ctx, ids, true)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindIssuesByIDs", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -405,7 +405,7 @@ func ListIssues(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
@@ -428,7 +428,7 @@ func ListIssues(ctx *context.APIContext) {
if splitted := strings.Split(ctx.FormString("labels"), ","); len(splitted) > 0 {
labelIDs, err = issues_model.GetLabelIDsInRepoByNames(ctx, ctx.Repo.Repository.ID, splitted)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLabelIDsInRepoByNames", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -444,7 +444,7 @@ func ListIssues(ctx *context.APIContext) {
continue
}
if !issues_model.IsErrMilestoneNotExist(err) {
- ctx.Error(http.StatusInternalServerError, "GetMilestoneByRepoIDANDName", err)
+ ctx.APIErrorInternal(err)
return
}
id, err := strconv.ParseInt(part[i], 10, 64)
@@ -459,7 +459,7 @@ func ListIssues(ctx *context.APIContext) {
if issues_model.IsErrMilestoneNotExist(err) {
continue
}
- ctx.Error(http.StatusInternalServerError, "GetMilestoneByRepoID", err)
+ ctx.APIErrorInternal(err)
}
}
@@ -474,7 +474,7 @@ func ListIssues(ctx *context.APIContext) {
}
if isPull.Has() && !ctx.Repo.CanReadIssuesOrPulls(isPull.Value()) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -482,7 +482,7 @@ func ListIssues(ctx *context.APIContext) {
canReadIssues := ctx.Repo.CanRead(unit.TypeIssues)
canReadPulls := ctx.Repo.CanRead(unit.TypePullRequests)
if !canReadIssues && !canReadPulls {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
} else if !canReadIssues {
isPull = optional.Some(true)
@@ -538,10 +538,10 @@ func ListIssues(ctx *context.APIContext) {
}
if createdByID > 0 {
- searchOpt.PosterID = optional.Some(createdByID)
+ searchOpt.PosterID = strconv.FormatInt(createdByID, 10)
}
if assignedByID > 0 {
- searchOpt.AssigneeID = optional.Some(assignedByID)
+ searchOpt.AssigneeID = strconv.FormatInt(assignedByID, 10)
}
if mentionedByID > 0 {
searchOpt.MentionID = optional.Some(mentionedByID)
@@ -549,12 +549,12 @@ func ListIssues(ctx *context.APIContext) {
ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "SearchIssues", err)
+ ctx.APIErrorInternal(err)
return
}
issues, err := issues_model.GetIssuesByIDs(ctx, ids, true)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindIssuesByIDs", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -571,12 +571,12 @@ func getUserIDForFilter(ctx *context.APIContext, queryName string) int64 {
user, err := user_model.GetUserByName(ctx, userName)
if user_model.IsErrUserNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
return 0
}
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return 0
}
@@ -616,14 +616,14 @@ func GetIssue(ctx *context.APIContext) {
issue, err := issues_model.GetIssueWithAttrsByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, ctx.Doer, issue))
@@ -691,9 +691,9 @@ func CreateIssue(ctx *context.APIContext) {
assigneeIDs, err = issues_model.MakeIDsFromAPIAssigneesToAdd(ctx, form.Assignee, form.Assignees)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Sprintf("Assignee does not exist: [name: %s]", err))
} else {
- ctx.Error(http.StatusInternalServerError, "AddAssigneeByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -702,17 +702,17 @@ func CreateIssue(ctx *context.APIContext) {
for _, aID := range assigneeIDs {
assignee, err := user_model.GetUserByID(ctx, aID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserByID", err)
+ ctx.APIErrorInternal(err)
return
}
valid, err := access_model.CanBeAssigned(ctx, assignee, ctx.Repo.Repository, false)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "canBeAssigned", err)
+ ctx.APIErrorInternal(err)
return
}
if !valid {
- ctx.Error(http.StatusUnprocessableEntity, "canBeAssigned", repo_model.ErrUserDoesNotHaveAccessToRepo{UserID: aID, RepoName: ctx.Repo.Repository.Name})
+ ctx.APIError(http.StatusUnprocessableEntity, repo_model.ErrUserDoesNotHaveAccessToRepo{UserID: aID, RepoName: ctx.Repo.Repository.Name})
return
}
}
@@ -723,11 +723,11 @@ func CreateIssue(ctx *context.APIContext) {
if err := issue_service.NewIssue(ctx, ctx.Repo.Repository, issue, form.Labels, nil, assigneeIDs, 0); err != nil {
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) {
- ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, "NewIssue", err)
+ ctx.APIError(http.StatusForbidden, err)
} else {
- ctx.Error(http.StatusInternalServerError, "NewIssue", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -735,10 +735,10 @@ func CreateIssue(ctx *context.APIContext) {
if form.Closed {
if err := issue_service.CloseIssue(ctx, issue, ctx.Doer, ""); err != nil {
if issues_model.IsErrDependenciesLeft(err) {
- ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies")
+ ctx.APIError(http.StatusPreconditionFailed, "cannot close this issue because it still has open dependencies")
return
}
- ctx.Error(http.StatusInternalServerError, "ChangeStatus", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -746,7 +746,7 @@ func CreateIssue(ctx *context.APIContext) {
// Refetch from database to assign some automatic values
issue, err = issues_model.GetIssueByID(ctx, issue.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetIssueByID", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, issue))
@@ -796,9 +796,9 @@ func EditIssue(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -807,7 +807,7 @@ func EditIssue(ctx *context.APIContext) {
err = issue.LoadAttributes(ctx)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -819,7 +819,7 @@ func EditIssue(ctx *context.APIContext) {
if len(form.Title) > 0 {
err = issue_service.ChangeTitle(ctx, issue, ctx.Doer, form.Title)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ChangeTitle", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -827,18 +827,18 @@ func EditIssue(ctx *context.APIContext) {
err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body, issue.ContentVersion)
if err != nil {
if errors.Is(err, issues_model.ErrIssueAlreadyChanged) {
- ctx.Error(http.StatusBadRequest, "ChangeContent", err)
+ ctx.APIError(http.StatusBadRequest, err)
return
}
- ctx.Error(http.StatusInternalServerError, "ChangeContent", err)
+ ctx.APIErrorInternal(err)
return
}
}
if form.Ref != nil {
err = issue_service.ChangeIssueRef(ctx, issue, ctx.Doer, *form.Ref)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateRef", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -849,7 +849,7 @@ func EditIssue(ctx *context.APIContext) {
if form.RemoveDeadline == nil || !*form.RemoveDeadline {
if form.Deadline == nil {
- ctx.Error(http.StatusBadRequest, "", "The due_date cannot be empty")
+ ctx.APIError(http.StatusBadRequest, "The due_date cannot be empty")
return
}
if !form.Deadline.IsZero() {
@@ -860,7 +860,7 @@ func EditIssue(ctx *context.APIContext) {
}
if err := issues_model.UpdateIssueDeadline(ctx, issue, deadlineUnix, ctx.Doer); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateIssueDeadline", err)
+ ctx.APIErrorInternal(err)
return
}
issue.DeadlineUnix = deadlineUnix
@@ -883,9 +883,9 @@ func EditIssue(ctx *context.APIContext) {
err = issue_service.UpdateAssignees(ctx, issue, oneAssignee, form.Assignees, ctx.Doer)
if err != nil {
if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, "UpdateAssignees", err)
+ ctx.APIError(http.StatusForbidden, err)
} else {
- ctx.Error(http.StatusInternalServerError, "UpdateAssignees", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -895,19 +895,28 @@ func EditIssue(ctx *context.APIContext) {
issue.MilestoneID != *form.Milestone {
oldMilestoneID := issue.MilestoneID
issue.MilestoneID = *form.Milestone
+ if issue.MilestoneID > 0 {
+ issue.Milestone, err = issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, *form.Milestone)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ } else {
+ issue.Milestone = nil
+ }
if err = issue_service.ChangeMilestoneAssign(ctx, issue, ctx.Doer, oldMilestoneID); err != nil {
- ctx.Error(http.StatusInternalServerError, "ChangeMilestoneAssign", err)
+ ctx.APIErrorInternal(err)
return
}
}
if form.State != nil {
if issue.IsPull {
if err := issue.LoadPullRequest(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "GetPullRequest", err)
+ ctx.APIErrorInternal(err)
return
}
if issue.PullRequest.HasMerged {
- ctx.Error(http.StatusPreconditionFailed, "MergedPRState", "cannot change state of this pull request, it was already merged")
+ ctx.APIError(http.StatusPreconditionFailed, "cannot change state of this pull request, it was already merged")
return
}
}
@@ -922,11 +931,11 @@ func EditIssue(ctx *context.APIContext) {
// Refetch from database to assign some automatic values
issue, err = issues_model.GetIssueByID(ctx, issue.ID)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
if err = issue.LoadMilestone(ctx); err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, issue))
@@ -963,15 +972,15 @@ func DeleteIssue(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err = issue_service.DeleteIssue(ctx, ctx.Doer, ctx.Repo.GitRepo, issue); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteIssueByID", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -1019,21 +1028,21 @@ func UpdateIssueDeadline(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
- ctx.Error(http.StatusForbidden, "", "Not repo writer")
+ ctx.APIError(http.StatusForbidden, "Not repo writer")
return
}
deadlineUnix, _ := common.ParseAPIDeadlineToEndOfDay(form.Deadline)
if err := issues_model.UpdateIssueDeadline(ctx, issue, deadlineUnix, ctx.Doer); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateIssueDeadline", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -1042,22 +1051,22 @@ func UpdateIssueDeadline(ctx *context.APIContext) {
func closeOrReopenIssue(ctx *context.APIContext, issue *issues_model.Issue, state api.StateType) {
if state != api.StateOpen && state != api.StateClosed {
- ctx.Error(http.StatusPreconditionFailed, "UnknownIssueStateError", fmt.Sprintf("unknown state: %s", state))
+ ctx.APIError(http.StatusPreconditionFailed, fmt.Sprintf("unknown state: %s", state))
return
}
if state == api.StateClosed && !issue.IsClosed {
if err := issue_service.CloseIssue(ctx, issue, ctx.Doer, ""); err != nil {
if issues_model.IsErrDependenciesLeft(err) {
- ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue or pull request because it still has open dependencies")
+ ctx.APIError(http.StatusPreconditionFailed, "cannot close this issue or pull request because it still has open dependencies")
return
}
- ctx.Error(http.StatusInternalServerError, "CloseIssue", err)
+ ctx.APIErrorInternal(err)
return
}
} else if state == api.StateOpen && issue.IsClosed {
if err := issue_service.ReopenIssue(ctx, issue, ctx.Doer, ""); err != nil {
- ctx.Error(http.StatusInternalServerError, "ReopenIssue", err)
+ ctx.APIErrorInternal(err)
return
}
}
diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go
index d0bcadde37..3f751a295c 100644
--- a/routers/api/v1/repo/issue_attachment.go
+++ b/routers/api/v1/repo/issue_attachment.go
@@ -104,7 +104,7 @@ func ListIssueAttachments(ctx *context.APIContext) {
}
if err := issue.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -171,7 +171,7 @@ func CreateIssueAttachment(ctx *context.APIContext) {
// Get uploaded file from request
file, header, err := ctx.Req.FormFile("attachment")
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FormFile", err)
+ ctx.APIErrorInternal(err)
return
}
defer file.Close()
@@ -189,9 +189,9 @@ func CreateIssueAttachment(ctx *context.APIContext) {
})
if err != nil {
if upload.IsErrFileTypeForbidden(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "UploadAttachment", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -199,7 +199,7 @@ func CreateIssueAttachment(ctx *context.APIContext) {
issue.Attachments = append(issue.Attachments, attachment)
if err := issue_service.ChangeContent(ctx, issue, ctx.Doer, issue.Content, issue.ContentVersion); err != nil {
- ctx.Error(http.StatusInternalServerError, "ChangeContent", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -265,10 +265,10 @@ func EditIssueAttachment(ctx *context.APIContext) {
if err := attachment_service.UpdateAttachment(ctx, setting.Attachment.AllowedTypes, attachment); err != nil {
if upload.IsErrFileTypeForbidden(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "UpdateAttachment", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -319,7 +319,7 @@ func DeleteIssueAttachment(ctx *context.APIContext) {
}
if err := repo_model.DeleteAttachment(ctx, attachment, true); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteAttachment", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -329,7 +329,7 @@ func DeleteIssueAttachment(ctx *context.APIContext) {
func getIssueFromContext(ctx *context.APIContext) *issues_model.Issue {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
- ctx.NotFoundOrServerError("GetIssueByIndex", issues_model.IsErrIssueNotExist, err)
+ ctx.NotFoundOrServerError(err)
return nil
}
@@ -354,7 +354,7 @@ func getIssueAttachmentSafeWrite(ctx *context.APIContext) *repo_model.Attachment
func getIssueAttachmentSafeRead(ctx *context.APIContext, issue *issues_model.Issue) *repo_model.Attachment {
attachment, err := repo_model.GetAttachmentByID(ctx, ctx.PathParamInt64("attachment_id"))
if err != nil {
- ctx.NotFoundOrServerError("GetAttachmentByID", repo_model.IsErrAttachmentNotExist, err)
+ ctx.NotFoundOrServerError(err)
return nil
}
if !attachmentBelongsToRepoOrIssue(ctx, attachment, issue) {
@@ -366,7 +366,7 @@ func getIssueAttachmentSafeRead(ctx *context.APIContext, issue *issues_model.Iss
func canUserWriteIssueAttachment(ctx *context.APIContext, issue *issues_model.Issue) bool {
canEditIssue := ctx.IsSigned && (ctx.Doer.ID == issue.PosterID || ctx.IsUserRepoAdmin() || ctx.IsUserSiteAdmin() || ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull))
if !canEditIssue {
- ctx.Error(http.StatusForbidden, "", "user should have permission to write issue")
+ ctx.APIError(http.StatusForbidden, "user should have permission to write issue")
return false
}
@@ -376,16 +376,16 @@ func canUserWriteIssueAttachment(ctx *context.APIContext, issue *issues_model.Is
func attachmentBelongsToRepoOrIssue(ctx *context.APIContext, attachment *repo_model.Attachment, issue *issues_model.Issue) bool {
if attachment.RepoID != ctx.Repo.Repository.ID {
log.Debug("Requested attachment[%d] does not belong to repo[%-v].", attachment.ID, ctx.Repo.Repository)
- ctx.NotFound("no such attachment in repo")
+ ctx.APIErrorNotFound("no such attachment in repo")
return false
}
if attachment.IssueID == 0 {
log.Debug("Requested attachment[%d] is not in an issue.", attachment.ID)
- ctx.NotFound("no such attachment in issue")
+ ctx.APIErrorNotFound("no such attachment in issue")
return false
} else if issue != nil && attachment.IssueID != issue.ID {
log.Debug("Requested attachment[%d] does not belong to issue[%d, #%d].", attachment.ID, issue.ID, issue.Index)
- ctx.NotFound("no such attachment in issue")
+ ctx.APIErrorNotFound("no such attachment in issue")
return false
}
return true
diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go
index 96a61a527e..cc342a9313 100644
--- a/routers/api/v1/repo/issue_comment.go
+++ b/routers/api/v1/repo/issue_comment.go
@@ -65,16 +65,16 @@ func ListIssueComments(ctx *context.APIContext) {
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetRawIssueByIndex", err)
+ ctx.APIErrorInternal(err)
return
}
if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -89,23 +89,23 @@ func ListIssueComments(ctx *context.APIContext) {
comments, err := issues_model.FindComments(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindComments", err)
+ ctx.APIErrorInternal(err)
return
}
totalCount, err := issues_model.CountComments(ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
if err := comments.LoadPosters(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadPosters", err)
+ ctx.APIErrorInternal(err)
return
}
if err := comments.LoadAttachments(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttachments", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -169,12 +169,12 @@ func ListIssueCommentsAndTimeline(ctx *context.APIContext) {
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetRawIssueByIndex", err)
+ ctx.APIErrorInternal(err)
return
}
issue.Repo = ctx.Repo.Repository
@@ -189,12 +189,12 @@ func ListIssueCommentsAndTimeline(ctx *context.APIContext) {
comments, err := issues_model.FindComments(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindComments", err)
+ ctx.APIErrorInternal(err)
return
}
if err := comments.LoadPosters(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadPosters", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -274,7 +274,7 @@ func ListRepoIssueComments(ctx *context.APIContext) {
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
@@ -288,7 +288,7 @@ func ListRepoIssueComments(ctx *context.APIContext) {
} else if canReadPull {
isPull = optional.Some(true)
} else {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -303,32 +303,32 @@ func ListRepoIssueComments(ctx *context.APIContext) {
comments, err := issues_model.FindComments(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindComments", err)
+ ctx.APIErrorInternal(err)
return
}
totalCount, err := issues_model.CountComments(ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
if err = comments.LoadPosters(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadPosters", err)
+ ctx.APIErrorInternal(err)
return
}
apiComments := make([]*api.Comment, len(comments))
if err := comments.LoadIssues(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadIssues", err)
+ ctx.APIErrorInternal(err)
return
}
if err := comments.LoadAttachments(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttachments", err)
+ ctx.APIErrorInternal(err)
return
}
if _, err := comments.Issues().LoadRepositories(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadRepositories", err)
+ ctx.APIErrorInternal(err)
return
}
for i := range comments {
@@ -382,26 +382,26 @@ func CreateIssueComment(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.CreateIssueCommentOption)
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
return
}
if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.Doer.IsAdmin {
- ctx.Error(http.StatusForbidden, "CreateIssueComment", errors.New(ctx.Locale.TrString("repo.issues.comment_on_locked")))
+ ctx.APIError(http.StatusForbidden, errors.New(ctx.Locale.TrString("repo.issues.comment_on_locked")))
return
}
comment, err := issue_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Body, nil)
if err != nil {
if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, "CreateIssueComment", err)
+ ctx.APIError(http.StatusForbidden, err)
} else {
- ctx.Error(http.StatusInternalServerError, "CreateIssueComment", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -448,15 +448,15 @@ func GetIssueComment(ctx *context.APIContext) {
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrCommentNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err = comment.LoadIssue(ctx); err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
@@ -465,7 +465,7 @@ func GetIssueComment(ctx *context.APIContext) {
}
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -475,7 +475,7 @@ func GetIssueComment(ctx *context.APIContext) {
}
if err := comment.LoadPoster(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "comment.LoadPoster", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -582,15 +582,15 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrCommentNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err := comment.LoadIssue(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -609,15 +609,17 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
return
}
- oldContent := comment.Content
- comment.Content = form.Body
- if err := issue_service.UpdateComment(ctx, comment, comment.ContentVersion, ctx.Doer, oldContent); err != nil {
- if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, "UpdateComment", err)
- } else {
- ctx.Error(http.StatusInternalServerError, "UpdateComment", err)
+ if form.Body != comment.Content {
+ oldContent := comment.Content
+ comment.Content = form.Body
+ if err := issue_service.UpdateComment(ctx, comment, comment.ContentVersion, ctx.Doer, oldContent); err != nil {
+ if errors.Is(err, user_model.ErrBlockedUser) {
+ ctx.APIError(http.StatusForbidden, err)
+ } else {
+ ctx.APIErrorInternal(err)
+ }
+ return
}
- return
}
ctx.JSON(http.StatusOK, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment))
@@ -699,15 +701,15 @@ func deleteIssueComment(ctx *context.APIContext) {
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrCommentNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err := comment.LoadIssue(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -725,7 +727,7 @@ func deleteIssueComment(ctx *context.APIContext) {
}
if err = issue_service.DeleteComment(ctx, ctx.Doer, comment); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteCommentByID", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/repo/issue_comment_attachment.go b/routers/api/v1/repo/issue_comment_attachment.go
index a556a803e5..5f660c5750 100644
--- a/routers/api/v1/repo/issue_comment_attachment.go
+++ b/routers/api/v1/repo/issue_comment_attachment.go
@@ -67,7 +67,7 @@ func GetIssueCommentAttachment(ctx *context.APIContext) {
}
if attachment.CommentID != comment.ID {
log.Debug("User requested attachment[%d] is not in comment[%d].", attachment.ID, comment.ID)
- ctx.NotFound("attachment not in comment")
+ ctx.APIErrorNotFound("attachment not in comment")
return
}
@@ -109,7 +109,7 @@ func ListIssueCommentAttachments(ctx *context.APIContext) {
}
if err := comment.LoadAttachments(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttachments", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -179,7 +179,7 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
// Get uploaded file from request
file, header, err := ctx.Req.FormFile("attachment")
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FormFile", err)
+ ctx.APIErrorInternal(err)
return
}
defer file.Close()
@@ -198,23 +198,23 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
})
if err != nil {
if upload.IsErrFileTypeForbidden(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "UploadAttachment", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err := comment.LoadAttachments(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttachments", err)
+ ctx.APIErrorInternal(err)
return
}
if err = issue_service.UpdateComment(ctx, comment, comment.ContentVersion, ctx.Doer, comment.Content); err != nil {
if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, "UpdateComment", err)
+ ctx.APIError(http.StatusForbidden, err)
} else {
- ctx.ServerError("UpdateComment", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -279,10 +279,10 @@ func EditIssueCommentAttachment(ctx *context.APIContext) {
if err := attachment_service.UpdateAttachment(ctx, setting.Attachment.AllowedTypes, attach); err != nil {
if upload.IsErrFileTypeForbidden(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "UpdateAttachment", attach)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
@@ -331,7 +331,7 @@ func DeleteIssueCommentAttachment(ctx *context.APIContext) {
}
if err := repo_model.DeleteAttachment(ctx, attach, true); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteAttachment", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
@@ -340,15 +340,15 @@ func DeleteIssueCommentAttachment(ctx *context.APIContext) {
func getIssueCommentSafe(ctx *context.APIContext) *issues_model.Comment {
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
- ctx.NotFoundOrServerError("GetCommentByID", issues_model.IsErrCommentNotExist, err)
+ ctx.NotFoundOrServerError(err)
return nil
}
if err := comment.LoadIssue(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "comment.LoadIssue", err)
+ ctx.APIErrorInternal(err)
return nil
}
if comment.Issue == nil || comment.Issue.RepoID != ctx.Repo.Repository.ID {
- ctx.Error(http.StatusNotFound, "", "no matching issue comment found")
+ ctx.APIError(http.StatusNotFound, "no matching issue comment found")
return nil
}
@@ -375,7 +375,7 @@ func getIssueCommentAttachmentSafeWrite(ctx *context.APIContext) *repo_model.Att
func canUserWriteIssueCommentAttachment(ctx *context.APIContext, comment *issues_model.Comment) bool {
canEditComment := ctx.IsSigned && (ctx.Doer.ID == comment.PosterID || ctx.IsUserRepoAdmin() || ctx.IsUserSiteAdmin()) && ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)
if !canEditComment {
- ctx.Error(http.StatusForbidden, "", "user should have permission to edit comment")
+ ctx.APIError(http.StatusForbidden, "user should have permission to edit comment")
return false
}
@@ -385,7 +385,7 @@ func canUserWriteIssueCommentAttachment(ctx *context.APIContext, comment *issues
func getIssueCommentAttachmentSafeRead(ctx *context.APIContext, comment *issues_model.Comment) *repo_model.Attachment {
attachment, err := repo_model.GetAttachmentByID(ctx, ctx.PathParamInt64("attachment_id"))
if err != nil {
- ctx.NotFoundOrServerError("GetAttachmentByID", repo_model.IsErrAttachmentNotExist, err)
+ ctx.NotFoundOrServerError(err)
return nil
}
if !attachmentBelongsToRepoOrComment(ctx, attachment, comment) {
@@ -397,17 +397,17 @@ func getIssueCommentAttachmentSafeRead(ctx *context.APIContext, comment *issues_
func attachmentBelongsToRepoOrComment(ctx *context.APIContext, attachment *repo_model.Attachment, comment *issues_model.Comment) bool {
if attachment.RepoID != ctx.Repo.Repository.ID {
log.Debug("Requested attachment[%d] does not belong to repo[%-v].", attachment.ID, ctx.Repo.Repository)
- ctx.NotFound("no such attachment in repo")
+ ctx.APIErrorNotFound("no such attachment in repo")
return false
}
if attachment.IssueID == 0 || attachment.CommentID == 0 {
log.Debug("Requested attachment[%d] is not in a comment.", attachment.ID)
- ctx.NotFound("no such attachment in comment")
+ ctx.APIErrorNotFound("no such attachment in comment")
return false
}
if comment != nil && attachment.CommentID != comment.ID {
log.Debug("Requested attachment[%d] does not belong to comment[%d].", attachment.ID, comment.ID)
- ctx.NotFound("no such attachment in comment")
+ ctx.APIErrorNotFound("no such attachment in comment")
return false
}
return true
diff --git a/routers/api/v1/repo/issue_dependency.go b/routers/api/v1/repo/issue_dependency.go
index 19dcf999b8..1b58beb7b6 100644
--- a/routers/api/v1/repo/issue_dependency.go
+++ b/routers/api/v1/repo/issue_dependency.go
@@ -57,30 +57,27 @@ func GetIssueDependencies(ctx *context.APIContext) {
// If this issue's repository does not enable dependencies then there can be no dependencies by default
if !ctx.Repo.Repository.IsDependenciesEnabled(ctx) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound("IsErrIssueNotExist", err)
+ ctx.APIErrorNotFound("IsErrIssueNotExist", err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
// 1. We must be able to read this issue
if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
limit := ctx.FormInt("limit")
if limit == 0 {
limit = setting.API.DefaultPagingNum
@@ -98,7 +95,7 @@ func GetIssueDependencies(ctx *context.APIContext) {
PageSize: limit,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "BlockedByDependencies", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -116,7 +113,7 @@ func GetIssueDependencies(ctx *context.APIContext) {
var err error
perm, err = access_model.GetUserRepoPermission(ctx, &blocker.Repository, ctx.Doer)
if err != nil {
- ctx.ServerError("GetUserRepoPermission", err)
+ ctx.APIErrorInternal(err)
return
}
repoPerms[blocker.RepoID] = perm
@@ -324,14 +321,11 @@ func GetIssueBlocks(ctx *context.APIContext) {
}
if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
limit := ctx.FormInt("limit")
if limit <= 1 {
limit = setting.API.DefaultPagingNum
@@ -342,7 +336,7 @@ func GetIssueBlocks(ctx *context.APIContext) {
deps, err := issue.BlockingDependencies(ctx)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "BlockingDependencies", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -367,7 +361,7 @@ func GetIssueBlocks(ctx *context.APIContext) {
var err error
perm, err = access_model.GetUserRepoPermission(ctx, &depMeta.Repository, ctx.Doer)
if err != nil {
- ctx.ServerError("GetUserRepoPermission", err)
+ ctx.APIErrorInternal(err)
return
}
repoPerms[depMeta.RepoID] = perm
@@ -502,9 +496,9 @@ func getParamsIssue(ctx *context.APIContext) *issues_model.Issue {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound("IsErrIssueNotExist", err)
+ ctx.APIErrorNotFound("IsErrIssueNotExist", err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return nil
}
@@ -523,9 +517,9 @@ func getFormIssue(ctx *context.APIContext, form *api.IssueMeta) *issues_model.Is
repo, err = repo_model.GetRepositoryByOwnerAndName(ctx, form.Owner, form.Name)
if err != nil {
if repo_model.IsErrRepoNotExist(err) {
- ctx.NotFound("IsErrRepoNotExist", err)
+ ctx.APIErrorNotFound("IsErrRepoNotExist", err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetRepositoryByOwnerAndName", err)
+ ctx.APIErrorInternal(err)
}
return nil
}
@@ -536,9 +530,9 @@ func getFormIssue(ctx *context.APIContext, form *api.IssueMeta) *issues_model.Is
issue, err := issues_model.GetIssueByIndex(ctx, repo.ID, form.Index)
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound("IsErrIssueNotExist", err)
+ ctx.APIErrorNotFound("IsErrIssueNotExist", err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return nil
}
@@ -553,7 +547,7 @@ func getPermissionForRepo(ctx *context.APIContext, repo *repo_model.Repository)
perm, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
+ ctx.APIErrorInternal(err)
return nil
}
@@ -563,25 +557,25 @@ func getPermissionForRepo(ctx *context.APIContext, repo *repo_model.Repository)
func createIssueDependency(ctx *context.APIContext, target, dependency *issues_model.Issue, targetPerm, dependencyPerm access_model.Permission) {
if target.Repo.IsArchived || !target.Repo.IsDependenciesEnabled(ctx) {
// The target's repository doesn't have dependencies enabled
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if !targetPerm.CanWriteIssuesOrPulls(target.IsPull) {
// We can't write to the target
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if !dependencyPerm.CanReadIssuesOrPulls(dependency.IsPull) {
// We can't read the dependency
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
err := issues_model.CreateIssueDependency(ctx, ctx.Doer, target, dependency)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "CreateIssueDependency", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -589,25 +583,25 @@ func createIssueDependency(ctx *context.APIContext, target, dependency *issues_m
func removeIssueDependency(ctx *context.APIContext, target, dependency *issues_model.Issue, targetPerm, dependencyPerm access_model.Permission) {
if target.Repo.IsArchived || !target.Repo.IsDependenciesEnabled(ctx) {
// The target's repository doesn't have dependencies enabled
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if !targetPerm.CanWriteIssuesOrPulls(target.IsPull) {
// We can't write to the target
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if !dependencyPerm.CanReadIssuesOrPulls(dependency.IsPull) {
// We can't read the dependency
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
err := issues_model.RemoveIssueDependency(ctx, ctx.Doer, target, dependency, issues_model.DependencyTypeBlockedBy)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "CreateIssueDependency", err)
+ ctx.APIErrorInternal(err)
return
}
}
diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go
index ee1a842bc6..d5eee2d469 100644
--- a/routers/api/v1/repo/issue_label.go
+++ b/routers/api/v1/repo/issue_label.go
@@ -5,7 +5,7 @@
package repo
import (
- "fmt"
+ "errors"
"net/http"
"reflect"
@@ -50,15 +50,15 @@ func ListIssueLabels(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err := issue.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -110,13 +110,13 @@ func AddIssueLabels(ctx *context.APIContext) {
}
if err = issue_service.AddLabels(ctx, issue, ctx.Doer, labels); err != nil {
- ctx.Error(http.StatusInternalServerError, "AddLabels", err)
+ ctx.APIErrorInternal(err)
return
}
labels, err = issues_model.GetLabelsByIssueID(ctx, issue.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLabelsByIssueID", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -166,9 +166,9 @@ func DeleteIssueLabel(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -181,15 +181,15 @@ func DeleteIssueLabel(ctx *context.APIContext) {
label, err := issues_model.GetLabelByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrLabelNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetLabelByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err := issue_service.RemoveLabel(ctx, issue, ctx.Doer, label); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteIssueLabel", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -240,13 +240,13 @@ func ReplaceIssueLabels(ctx *context.APIContext) {
}
if err := issue_service.ReplaceLabels(ctx, issue, ctx.Doer, labels); err != nil {
- ctx.Error(http.StatusInternalServerError, "ReplaceLabels", err)
+ ctx.APIErrorInternal(err)
return
}
labels, err = issues_model.GetLabelsByIssueID(ctx, issue.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLabelsByIssueID", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -288,9 +288,9 @@ func ClearIssueLabels(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -301,7 +301,7 @@ func ClearIssueLabels(ctx *context.APIContext) {
}
if err := issue_service.ClearLabels(ctx, issue, ctx.Doer); err != nil {
- ctx.Error(http.StatusInternalServerError, "ClearLabels", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -312,16 +312,16 @@ func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption)
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return nil, nil, err
}
if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
- ctx.Error(http.StatusForbidden, "CanWriteIssuesOrPulls", "write permission is required")
- return nil, nil, fmt.Errorf("permission denied")
+ ctx.APIError(http.StatusForbidden, "write permission is required")
+ return nil, nil, errors.New("permission denied")
}
var (
@@ -336,25 +336,25 @@ func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption)
case reflect.String:
labelNames = append(labelNames, rv.String())
default:
- ctx.Error(http.StatusBadRequest, "InvalidLabel", "a label must be an integer or a string")
- return nil, nil, fmt.Errorf("invalid label")
+ ctx.APIError(http.StatusBadRequest, "a label must be an integer or a string")
+ return nil, nil, errors.New("invalid label")
}
}
if len(labelIDs) > 0 && len(labelNames) > 0 {
- ctx.Error(http.StatusBadRequest, "InvalidLabels", "labels should be an array of strings or integers")
- return nil, nil, fmt.Errorf("invalid labels")
+ ctx.APIError(http.StatusBadRequest, "labels should be an array of strings or integers")
+ return nil, nil, errors.New("invalid labels")
}
if len(labelNames) > 0 {
repoLabelIDs, err := issues_model.GetLabelIDsInRepoByNames(ctx, ctx.Repo.Repository.ID, labelNames)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLabelIDsInRepoByNames", err)
+ ctx.APIErrorInternal(err)
return nil, nil, err
}
labelIDs = append(labelIDs, repoLabelIDs...)
if ctx.Repo.Owner.IsOrganization() {
orgLabelIDs, err := issues_model.GetLabelIDsInOrgByNames(ctx, ctx.Repo.Owner.ID, labelNames)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLabelIDsInOrgByNames", err)
+ ctx.APIErrorInternal(err)
return nil, nil, err
}
labelIDs = append(labelIDs, orgLabelIDs...)
@@ -363,7 +363,7 @@ func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption)
labels, err := issues_model.GetLabelsByIDs(ctx, labelIDs, "id", "repo_id", "org_id", "name", "exclusive")
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLabelsByIDs", err)
+ ctx.APIErrorInternal(err)
return nil, nil, err
}
diff --git a/routers/api/v1/repo/issue_lock.go b/routers/api/v1/repo/issue_lock.go
new file mode 100644
index 0000000000..b9e5bcf6eb
--- /dev/null
+++ b/routers/api/v1/repo/issue_lock.go
@@ -0,0 +1,152 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "errors"
+ "net/http"
+
+ issues_model "code.gitea.io/gitea/models/issues"
+ api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
+)
+
+// LockIssue lock an issue
+func LockIssue(ctx *context.APIContext) {
+ // swagger:operation PUT /repos/{owner}/{repo}/issues/{index}/lock issue issueLockIssue
+ // ---
+ // summary: Lock an issue
+ // consumes:
+ // - application/json
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: index
+ // in: path
+ // description: index of the issue
+ // type: integer
+ // format: int64
+ // required: true
+ // - name: body
+ // in: body
+ // schema:
+ // "$ref": "#/definitions/LockIssueOption"
+ // responses:
+ // "204":
+ // "$ref": "#/responses/empty"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ reason := web.GetForm(ctx).(*api.LockIssueOption).Reason
+ issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
+ if err != nil {
+ if issues_model.IsErrIssueNotExist(err) {
+ ctx.APIErrorNotFound(err)
+ } else {
+ ctx.APIErrorInternal(err)
+ }
+ return
+ }
+
+ if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
+ ctx.APIError(http.StatusForbidden, errors.New("no permission to lock this issue"))
+ return
+ }
+
+ if !issue.IsLocked {
+ opt := &issues_model.IssueLockOptions{
+ Doer: ctx.ContextUser,
+ Issue: issue,
+ Reason: reason,
+ }
+
+ issue.Repo = ctx.Repo.Repository
+ err = issues_model.LockIssue(ctx, opt)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ }
+
+ ctx.Status(http.StatusNoContent)
+}
+
+// UnlockIssue unlock an issue
+func UnlockIssue(ctx *context.APIContext) {
+ // swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/lock issue issueUnlockIssue
+ // ---
+ // summary: Unlock an issue
+ // consumes:
+ // - application/json
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: index
+ // in: path
+ // description: index of the issue
+ // type: integer
+ // format: int64
+ // required: true
+ // responses:
+ // "204":
+ // "$ref": "#/responses/empty"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
+ if err != nil {
+ if issues_model.IsErrIssueNotExist(err) {
+ ctx.APIErrorNotFound(err)
+ } else {
+ ctx.APIErrorInternal(err)
+ }
+ return
+ }
+
+ if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
+ ctx.APIError(http.StatusForbidden, errors.New("no permission to unlock this issue"))
+ return
+ }
+
+ if issue.IsLocked {
+ opt := &issues_model.IssueLockOptions{
+ Doer: ctx.ContextUser,
+ Issue: issue,
+ }
+
+ issue.Repo = ctx.Repo.Repository
+ err = issues_model.UnlockIssue(ctx, opt)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ }
+
+ ctx.Status(http.StatusNoContent)
+}
diff --git a/routers/api/v1/repo/issue_pin.go b/routers/api/v1/repo/issue_pin.go
index 388d4a3e99..71985ac765 100644
--- a/routers/api/v1/repo/issue_pin.go
+++ b/routers/api/v1/repo/issue_pin.go
@@ -44,11 +44,11 @@ func PinIssue(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else if issues_model.IsErrIssueMaxPinReached(err) {
- ctx.Error(http.StatusBadRequest, "MaxPinReached", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -56,13 +56,13 @@ func PinIssue(ctx *context.APIContext) {
// If we don't do this, it will crash when trying to add the pin event to the comment history
err = issue.LoadRepo(ctx)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadRepo", err)
+ ctx.APIErrorInternal(err)
return
}
- err = issue.Pin(ctx, ctx.Doer)
+ err = issues_model.PinIssue(ctx, issue, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "PinIssue", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -101,9 +101,9 @@ func UnpinIssue(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -111,13 +111,13 @@ func UnpinIssue(ctx *context.APIContext) {
// If we don't do this, it will crash when trying to add the unpin event to the comment history
err = issue.LoadRepo(ctx)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadRepo", err)
+ ctx.APIErrorInternal(err)
return
}
- err = issue.Unpin(ctx, ctx.Doer)
+ err = issues_model.UnpinIssue(ctx, issue, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "UnpinIssue", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -162,16 +162,16 @@ func MoveIssuePin(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
- err = issue.MovePin(ctx, int(ctx.PathParamInt64("position")))
+ err = issues_model.MovePin(ctx, issue, int(ctx.PathParamInt64("position")))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "MovePin", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -203,7 +203,7 @@ func ListPinnedIssues(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
issues, err := issues_model.GetPinnedIssues(ctx, ctx.Repo.Repository.ID, false)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadPinnedIssues", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -235,29 +235,29 @@ func ListPinnedPullRequests(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
issues, err := issues_model.GetPinnedIssues(ctx, ctx.Repo.Repository.ID, true)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadPinnedPullRequests", err)
+ ctx.APIErrorInternal(err)
return
}
apiPrs := make([]*api.PullRequest, len(issues))
if err := issues.LoadPullRequests(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadPullRequests", err)
+ ctx.APIErrorInternal(err)
return
}
for i, currentIssue := range issues {
pr := currentIssue.PullRequest
if err = pr.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
if err = pr.LoadBaseRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err)
+ ctx.APIErrorInternal(err)
return
}
if err = pr.LoadHeadRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -295,13 +295,13 @@ func AreNewIssuePinsAllowed(ctx *context.APIContext) {
pinsAllowed.Issues, err = issues_model.IsNewPinAllowed(ctx, ctx.Repo.Repository.ID, false)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsNewIssuePinAllowed", err)
+ ctx.APIErrorInternal(err)
return
}
pinsAllowed.PullRequests, err = issues_model.IsNewPinAllowed(ctx, ctx.Repo.Repository.ID, true)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsNewPullRequestPinAllowed", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/repo/issue_reaction.go b/routers/api/v1/repo/issue_reaction.go
index ead86a717f..e535b5e009 100644
--- a/routers/api/v1/repo/issue_reaction.go
+++ b/routers/api/v1/repo/issue_reaction.go
@@ -54,36 +54,36 @@ func GetIssueCommentReactions(ctx *context.APIContext) {
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrCommentNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err := comment.LoadIssue(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "comment.LoadIssue", err)
+ ctx.APIErrorInternal(err)
return
}
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
- ctx.Error(http.StatusForbidden, "GetIssueCommentReactions", errors.New("no permission to get reactions"))
+ ctx.APIError(http.StatusForbidden, errors.New("no permission to get reactions"))
return
}
reactions, _, err := issues_model.FindCommentReactions(ctx, comment.IssueID, comment.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindCommentReactions", err)
+ ctx.APIErrorInternal(err)
return
}
_, err = reactions.LoadUsers(ctx, ctx.Repo.Repository)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ReactionList.LoadUsers()", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -191,30 +191,30 @@ func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOp
comment, err := issues_model.GetCommentByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrCommentNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err = comment.LoadIssue(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "comment.LoadIssue() failed", err)
+ ctx.APIErrorInternal(err)
return
}
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if comment.Issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull) {
- ctx.Error(http.StatusForbidden, "ChangeIssueCommentReaction", errors.New("no permission to change reaction"))
+ ctx.APIError(http.StatusForbidden, errors.New("no permission to change reaction"))
return
}
@@ -223,7 +223,7 @@ func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOp
reaction, err := issue_service.CreateCommentReaction(ctx, ctx.Doer, comment, form.Reaction)
if err != nil {
if issues_model.IsErrForbiddenIssueReaction(err) || errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, err.Error(), err)
+ ctx.APIError(http.StatusForbidden, err)
} else if issues_model.IsErrReactionAlreadyExist(err) {
ctx.JSON(http.StatusOK, api.Reaction{
User: convert.ToUser(ctx, ctx.Doer, ctx.Doer),
@@ -231,7 +231,7 @@ func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOp
Created: reaction.CreatedUnix.AsTime(),
})
} else {
- ctx.Error(http.StatusInternalServerError, "CreateCommentReaction", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -245,7 +245,7 @@ func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOp
// DeleteIssueCommentReaction part
err = issues_model.DeleteCommentReaction(ctx, ctx.Doer.ID, comment.Issue.ID, comment.ID, form.Reaction)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteCommentReaction", err)
+ ctx.APIErrorInternal(err)
return
}
// ToDo respond 204
@@ -298,26 +298,26 @@ func GetIssueReactions(ctx *context.APIContext) {
issue, err := issues_model.GetIssueWithAttrsByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
- ctx.Error(http.StatusForbidden, "GetIssueReactions", errors.New("no permission to get reactions"))
+ ctx.APIError(http.StatusForbidden, errors.New("no permission to get reactions"))
return
}
reactions, count, err := issues_model.FindIssueReactions(ctx, issue.ID, utils.GetListOptions(ctx))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindIssueReactions", err)
+ ctx.APIErrorInternal(err)
return
}
_, err = reactions.LoadUsers(ctx, ctx.Repo.Repository)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ReactionList.LoadUsers()", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -422,15 +422,15 @@ func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, i
issue, err := issues_model.GetIssueWithAttrsByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
- ctx.Error(http.StatusForbidden, "ChangeIssueCommentReaction", errors.New("no permission to change reaction"))
+ ctx.APIError(http.StatusForbidden, errors.New("no permission to change reaction"))
return
}
@@ -439,7 +439,7 @@ func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, i
reaction, err := issue_service.CreateIssueReaction(ctx, ctx.Doer, issue, form.Reaction)
if err != nil {
if issues_model.IsErrForbiddenIssueReaction(err) || errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, err.Error(), err)
+ ctx.APIError(http.StatusForbidden, err)
} else if issues_model.IsErrReactionAlreadyExist(err) {
ctx.JSON(http.StatusOK, api.Reaction{
User: convert.ToUser(ctx, ctx.Doer, ctx.Doer),
@@ -447,7 +447,7 @@ func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, i
Created: reaction.CreatedUnix.AsTime(),
})
} else {
- ctx.Error(http.StatusInternalServerError, "CreateIssueReaction", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -461,7 +461,7 @@ func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, i
// DeleteIssueReaction part
err = issues_model.DeleteIssueReaction(ctx, ctx.Doer.ID, issue.ID, form.Reaction)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteIssueReaction", err)
+ ctx.APIErrorInternal(err)
return
}
// ToDo respond 204
diff --git a/routers/api/v1/repo/issue_stopwatch.go b/routers/api/v1/repo/issue_stopwatch.go
index e7fba6d0ed..0f28b9757d 100644
--- a/routers/api/v1/repo/issue_stopwatch.go
+++ b/routers/api/v1/repo/issue_stopwatch.go
@@ -4,7 +4,6 @@
package repo
import (
- "errors"
"net/http"
issues_model "code.gitea.io/gitea/models/issues"
@@ -49,13 +48,16 @@ func StartIssueStopwatch(ctx *context.APIContext) {
// "409":
// description: Cannot start a stopwatch again if it already exists
- issue, err := prepareIssueStopwatch(ctx, false)
- if err != nil {
+ issue := prepareIssueForStopwatch(ctx)
+ if ctx.Written() {
return
}
- if err := issues_model.CreateIssueStopwatch(ctx, ctx.Doer, issue); err != nil {
- ctx.Error(http.StatusInternalServerError, "CreateOrStopIssueStopwatch", err)
+ if ok, err := issues_model.CreateIssueStopwatch(ctx, ctx.Doer, issue); err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ } else if !ok {
+ ctx.APIError(http.StatusConflict, "cannot start a stopwatch again if it already exists")
return
}
@@ -96,18 +98,20 @@ func StopIssueStopwatch(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
// "409":
- // description: Cannot stop a non existent stopwatch
+ // description: Cannot stop a non-existent stopwatch
- issue, err := prepareIssueStopwatch(ctx, true)
- if err != nil {
+ issue := prepareIssueForStopwatch(ctx)
+ if ctx.Written() {
return
}
- if err := issues_model.FinishIssueStopwatch(ctx, ctx.Doer, issue); err != nil {
- ctx.Error(http.StatusInternalServerError, "CreateOrStopIssueStopwatch", err)
+ if ok, err := issues_model.FinishIssueStopwatch(ctx, ctx.Doer, issue); err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ } else if !ok {
+ ctx.APIError(http.StatusConflict, "cannot stop a non-existent stopwatch")
return
}
-
ctx.Status(http.StatusCreated)
}
@@ -145,55 +149,45 @@ func DeleteIssueStopwatch(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
// "409":
- // description: Cannot cancel a non existent stopwatch
+ // description: Cannot cancel a non-existent stopwatch
- issue, err := prepareIssueStopwatch(ctx, true)
- if err != nil {
+ issue := prepareIssueForStopwatch(ctx)
+ if ctx.Written() {
return
}
- if err := issues_model.CancelStopwatch(ctx, ctx.Doer, issue); err != nil {
- ctx.Error(http.StatusInternalServerError, "CancelStopwatch", err)
+ if ok, err := issues_model.CancelStopwatch(ctx, ctx.Doer, issue); err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ } else if !ok {
+ ctx.APIError(http.StatusConflict, "cannot cancel a non-existent stopwatch")
return
}
ctx.Status(http.StatusNoContent)
}
-func prepareIssueStopwatch(ctx *context.APIContext, shouldExist bool) (*issues_model.Issue, error) {
+func prepareIssueForStopwatch(ctx *context.APIContext) *issues_model.Issue {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
-
- return nil, err
+ return nil
}
if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
ctx.Status(http.StatusForbidden)
- return nil, errors.New("Unable to write to PRs")
+ return nil
}
if !ctx.Repo.CanUseTimetracker(ctx, issue, ctx.Doer) {
ctx.Status(http.StatusForbidden)
- return nil, errors.New("Cannot use time tracker")
- }
-
- if issues_model.StopwatchExists(ctx, ctx.Doer.ID, issue.ID) != shouldExist {
- if shouldExist {
- ctx.Error(http.StatusConflict, "StopwatchExists", "cannot stop/cancel a non existent stopwatch")
- err = errors.New("cannot stop/cancel a non existent stopwatch")
- } else {
- ctx.Error(http.StatusConflict, "StopwatchExists", "cannot start a stopwatch again if it already exists")
- err = errors.New("cannot start a stopwatch again if it already exists")
- }
- return nil, err
+ return nil
}
-
- return issue, nil
+ return issue
}
// GetStopwatches get all stopwatches
@@ -220,19 +214,19 @@ func GetStopwatches(ctx *context.APIContext) {
sws, err := issues_model.GetUserStopwatches(ctx, ctx.Doer.ID, utils.GetListOptions(ctx))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserStopwatches", err)
+ ctx.APIErrorInternal(err)
return
}
count, err := issues_model.CountUserStopwatches(ctx, ctx.Doer.ID)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
apiSWs, err := convert.ToStopWatches(ctx, sws)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "APIFormat", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/repo/issue_subscription.go b/routers/api/v1/repo/issue_subscription.go
index 4fb80b1ec4..c89f228a06 100644
--- a/routers/api/v1/repo/issue_subscription.go
+++ b/routers/api/v1/repo/issue_subscription.go
@@ -43,7 +43,7 @@ func AddIssueSubscription(ctx *context.APIContext) {
// required: true
// - name: user
// in: path
- // description: user to subscribe
+ // description: username of the user to subscribe the issue to
// type: string
// required: true
// responses:
@@ -87,7 +87,7 @@ func DelIssueSubscription(ctx *context.APIContext) {
// required: true
// - name: user
// in: path
- // description: user witch unsubscribe
+ // description: username of the user to unsubscribe from an issue
// type: string
// required: true
// responses:
@@ -107,9 +107,9 @@ func setIssueSubscription(ctx *context.APIContext, watch bool) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
@@ -118,9 +118,9 @@ func setIssueSubscription(ctx *context.APIContext, watch bool) {
user, err := user_model.GetUserByName(ctx, ctx.PathParam("user"))
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
}
return
@@ -128,13 +128,13 @@ func setIssueSubscription(ctx *context.APIContext, watch bool) {
// only admin and user for itself can change subscription
if user.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin {
- ctx.Error(http.StatusForbidden, "User", fmt.Errorf("%s is not permitted to change subscriptions for %s", ctx.Doer.Name, user.Name))
+ ctx.APIError(http.StatusForbidden, fmt.Errorf("%s is not permitted to change subscriptions for %s", ctx.Doer.Name, user.Name))
return
}
current, err := issues_model.CheckIssueWatch(ctx, user, issue)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "CheckIssueWatch", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -146,7 +146,7 @@ func setIssueSubscription(ctx *context.APIContext, watch bool) {
// Update watch state
if err := issues_model.CreateOrUpdateIssueWatch(ctx, user.ID, issue.ID, watch); err != nil {
- ctx.Error(http.StatusInternalServerError, "CreateOrUpdateIssueWatch", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -188,9 +188,9 @@ func CheckIssueSubscription(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
@@ -198,7 +198,7 @@ func CheckIssueSubscription(ctx *context.APIContext) {
watching, err := issues_model.CheckIssueWatch(ctx, ctx.Doer, issue)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, api.WatchInfo{
@@ -254,9 +254,9 @@ func GetIssueSubscribers(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
@@ -264,7 +264,7 @@ func GetIssueSubscribers(ctx *context.APIContext) {
iwl, err := issues_model.GetIssueWatchers(ctx, issue.ID, utils.GetListOptions(ctx))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetIssueWatchers", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -275,7 +275,7 @@ func GetIssueSubscribers(ctx *context.APIContext) {
users, err := user_model.GetUsersByIDs(ctx, userIDs)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUsersByIDs", err)
+ ctx.APIErrorInternal(err)
return
}
apiUsers := make([]*api.User, 0, len(users))
@@ -285,7 +285,7 @@ func GetIssueSubscribers(ctx *context.APIContext) {
count, err := issues_model.CountIssueWatchers(ctx, issue.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "CountIssueWatchers", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go
index 57961b0660..171da272cc 100644
--- a/routers/api/v1/repo/issue_tracked_time.go
+++ b/routers/api/v1/repo/issue_tracked_time.go
@@ -4,7 +4,7 @@
package repo
import (
- "fmt"
+ "errors"
"net/http"
"time"
@@ -72,15 +72,15 @@ func ListTrackedTimes(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if !ctx.Repo.Repository.IsTimetrackerEnabled(ctx) {
- ctx.NotFound("Timetracker is disabled")
+ ctx.APIErrorNotFound("Timetracker is disabled")
return
}
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -95,16 +95,16 @@ func ListTrackedTimes(ctx *context.APIContext) {
if qUser != "" {
user, err := user_model.GetUserByName(ctx, qUser)
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusNotFound, "User does not exist", err)
+ ctx.APIError(http.StatusNotFound, err)
} else if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
return
}
opts.UserID = user.ID
}
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Base); err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
@@ -116,24 +116,24 @@ func ListTrackedTimes(ctx *context.APIContext) {
if opts.UserID == 0 {
opts.UserID = ctx.Doer.ID
} else {
- ctx.Error(http.StatusForbidden, "", fmt.Errorf("query by user not allowed; not enough rights"))
+ ctx.APIError(http.StatusForbidden, errors.New("query by user not allowed; not enough rights"))
return
}
}
count, err := issues_model.CountTrackedTimes(ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
trackedTimes, err := issues_model.GetTrackedTimes(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err)
+ ctx.APIErrorInternal(err)
return
}
if err = trackedTimes.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -184,16 +184,16 @@ func AddTime(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
if !ctx.Repo.CanUseTimetracker(ctx, issue, ctx.Doer) {
if !ctx.Repo.Repository.IsTimetrackerEnabled(ctx) {
- ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
+ ctx.APIError(http.StatusBadRequest, "time tracking disabled")
return
}
ctx.Status(http.StatusForbidden)
@@ -206,7 +206,7 @@ func AddTime(ctx *context.APIContext) {
// allow only RepoAdmin, Admin and User to add time
user, err = user_model.GetUserByName(ctx, form.User)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
}
}
}
@@ -218,11 +218,11 @@ func AddTime(ctx *context.APIContext) {
trackedTime, err := issues_model.AddTime(ctx, user, issue, form.Time, created)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "AddTime", err)
+ ctx.APIErrorInternal(err)
return
}
if err = trackedTime.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToTrackedTime(ctx, user, trackedTime))
@@ -267,9 +267,9 @@ func ResetIssueTime(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -286,9 +286,9 @@ func ResetIssueTime(ctx *context.APIContext) {
err = issues_model.DeleteIssueUserTimes(ctx, issue, ctx.Doer)
if err != nil {
if db.IsErrNotExist(err) {
- ctx.Error(http.StatusNotFound, "DeleteIssueUserTimes", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteIssueUserTimes", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -340,9 +340,9 @@ func DeleteTime(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -359,14 +359,14 @@ func DeleteTime(ctx *context.APIContext) {
time, err := issues_model.GetTrackedTimeByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if db.IsErrNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetTrackedTimeByID", err)
+ ctx.APIErrorInternal(err)
return
}
if time.Deleted {
- ctx.NotFound(fmt.Errorf("tracked time [%d] already deleted", time.ID))
+ ctx.APIErrorNotFound("tracked time was already deleted")
return
}
@@ -378,7 +378,7 @@ func DeleteTime(ctx *context.APIContext) {
err = issues_model.DeleteTime(ctx, time)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteTime", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
@@ -405,7 +405,7 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
// required: true
// - name: user
// in: path
- // description: username of user
+ // description: username of the user whose tracked times are to be listed
// type: string
// required: true
// responses:
@@ -419,25 +419,25 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if !ctx.Repo.Repository.IsTimetrackerEnabled(ctx) {
- ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
+ ctx.APIError(http.StatusBadRequest, "time tracking disabled")
return
}
user, err := user_model.GetUserByName(ctx, ctx.PathParam("timetrackingusername"))
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
if user == nil {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if !ctx.IsUserRepoAdmin() && !ctx.Doer.IsAdmin && ctx.Doer.ID != user.ID {
- ctx.Error(http.StatusForbidden, "", fmt.Errorf("query by user not allowed; not enough rights"))
+ ctx.APIError(http.StatusForbidden, errors.New("query by user not allowed; not enough rights"))
return
}
@@ -448,11 +448,11 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
trackedTimes, err := issues_model.GetTrackedTimes(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err)
+ ctx.APIErrorInternal(err)
return
}
if err = trackedTimes.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes))
@@ -509,7 +509,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if !ctx.Repo.Repository.IsTimetrackerEnabled(ctx) {
- ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
+ ctx.APIError(http.StatusBadRequest, "time tracking disabled")
return
}
@@ -523,9 +523,9 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
if qUser != "" {
user, err := user_model.GetUserByName(ctx, qUser)
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusNotFound, "User does not exist", err)
+ ctx.APIError(http.StatusNotFound, err)
} else if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
return
}
opts.UserID = user.ID
@@ -533,7 +533,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
var err error
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Base); err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
@@ -545,24 +545,24 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
if opts.UserID == 0 {
opts.UserID = ctx.Doer.ID
} else {
- ctx.Error(http.StatusForbidden, "", fmt.Errorf("query by user not allowed; not enough rights"))
+ ctx.APIError(http.StatusForbidden, errors.New("query by user not allowed; not enough rights"))
return
}
}
count, err := issues_model.CountTrackedTimes(ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
trackedTimes, err := issues_model.GetTrackedTimes(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err)
+ ctx.APIErrorInternal(err)
return
}
if err = trackedTimes.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -607,24 +607,24 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
var err error
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Base); err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
count, err := issues_model.CountTrackedTimes(ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
trackedTimes, err := issues_model.GetTrackedTimes(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetTrackedTimesByUser", err)
+ ctx.APIErrorInternal(err)
return
}
if err = trackedTimes.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go
index 23cc922628..8cb61e9e0c 100644
--- a/routers/api/v1/repo/key.go
+++ b/routers/api/v1/repo/key.go
@@ -92,7 +92,7 @@ func ListDeployKeys(ctx *context.APIContext) {
keys, count, err := db.FindAndCount[asymkey_model.DeployKey](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -100,7 +100,7 @@ func ListDeployKeys(ctx *context.APIContext) {
apiKeys := make([]*api.DeployKey, len(keys))
for i := range keys {
if err := keys[i].GetContent(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "GetContent", err)
+ ctx.APIErrorInternal(err)
return
}
apiKeys[i] = convert.ToDeployKey(apiLink, keys[i])
@@ -146,21 +146,21 @@ func GetDeployKey(ctx *context.APIContext) {
key, err := asymkey_model.GetDeployKeyByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if asymkey_model.IsErrDeployKeyNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetDeployKeyByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
// this check make it more consistent
if key.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if err = key.GetContent(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "GetContent", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -175,11 +175,11 @@ func GetDeployKey(ctx *context.APIContext) {
// HandleCheckKeyStringError handle check key error
func HandleCheckKeyStringError(ctx *context.APIContext, err error) {
if db.IsErrSSHDisabled(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", "SSH is disabled")
+ ctx.APIError(http.StatusUnprocessableEntity, "SSH is disabled")
} else if asymkey_model.IsErrKeyUnableVerify(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", "Unable to verify key content")
+ ctx.APIError(http.StatusUnprocessableEntity, "Unable to verify key content")
} else {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid key content: %w", err))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("Invalid key content: %w", err))
}
}
@@ -187,15 +187,15 @@ func HandleCheckKeyStringError(ctx *context.APIContext, err error) {
func HandleAddKeyError(ctx *context.APIContext, err error) {
switch {
case asymkey_model.IsErrDeployKeyAlreadyExist(err):
- ctx.Error(http.StatusUnprocessableEntity, "", "This key has already been added to this repository")
+ ctx.APIError(http.StatusUnprocessableEntity, "This key has already been added to this repository")
case asymkey_model.IsErrKeyAlreadyExist(err):
- ctx.Error(http.StatusUnprocessableEntity, "", "Key content has been used as non-deploy key")
+ ctx.APIError(http.StatusUnprocessableEntity, "Key content has been used as non-deploy key")
case asymkey_model.IsErrKeyNameAlreadyUsed(err):
- ctx.Error(http.StatusUnprocessableEntity, "", "Key title has been used")
+ ctx.APIError(http.StatusUnprocessableEntity, "Key title has been used")
case asymkey_model.IsErrDeployKeyNameAlreadyUsed(err):
- ctx.Error(http.StatusUnprocessableEntity, "", "A key with the same name already exists")
+ ctx.APIError(http.StatusUnprocessableEntity, "A key with the same name already exists")
default:
- ctx.Error(http.StatusInternalServerError, "AddKey", err)
+ ctx.APIErrorInternal(err)
}
}
@@ -281,9 +281,9 @@ func DeleteDeploykey(ctx *context.APIContext) {
if err := asymkey_service.DeleteDeployKey(ctx, ctx.Repo.Repository, ctx.PathParamInt64("id")); err != nil {
if asymkey_model.IsErrKeyAccessDenied(err) {
- ctx.Error(http.StatusForbidden, "", "You do not have access to this key")
+ ctx.APIError(http.StatusForbidden, "You do not have access to this key")
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteDeployKey", err)
+ ctx.APIErrorInternal(err)
}
return
}
diff --git a/routers/api/v1/repo/label.go b/routers/api/v1/repo/label.go
index 1ece2521e0..4f79d42595 100644
--- a/routers/api/v1/repo/label.go
+++ b/routers/api/v1/repo/label.go
@@ -51,13 +51,13 @@ func ListLabels(ctx *context.APIContext) {
labels, err := issues_model.GetLabelsByRepoID(ctx, ctx.Repo.Repository.ID, ctx.FormString("sort"), utils.GetListOptions(ctx))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLabelsByRepoID", err)
+ ctx.APIErrorInternal(err)
return
}
count, err := issues_model.CountLabelsByRepoID(ctx, ctx.Repo.Repository.ID)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -107,9 +107,9 @@ func GetLabel(ctx *context.APIContext) {
}
if err != nil {
if issues_model.IsErrRepoLabelNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetLabelByRepoID", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -153,7 +153,7 @@ func CreateLabel(ctx *context.APIContext) {
color, err := label.NormalizeColor(form.Color)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "StringToColor", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
form.Color = color
@@ -166,7 +166,7 @@ func CreateLabel(ctx *context.APIContext) {
}
l.SetArchived(form.IsArchived)
if err := issues_model.NewLabel(ctx, l); err != nil {
- ctx.Error(http.StatusInternalServerError, "NewLabel", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -215,9 +215,9 @@ func EditLabel(ctx *context.APIContext) {
l, err := issues_model.GetLabelInRepoByID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrRepoLabelNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetLabelByRepoID", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -231,7 +231,7 @@ func EditLabel(ctx *context.APIContext) {
if form.Color != nil {
color, err := label.NormalizeColor(*form.Color)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "StringToColor", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
l.Color = color
@@ -241,7 +241,7 @@ func EditLabel(ctx *context.APIContext) {
}
l.SetArchived(form.IsArchived != nil && *form.IsArchived)
if err := issues_model.UpdateLabel(ctx, l); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateLabel", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -277,7 +277,7 @@ func DeleteLabel(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if err := issues_model.DeleteLabel(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("id")); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteLabel", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/repo/language.go b/routers/api/v1/repo/language.go
index f1d5bbe45f..00789983ce 100644
--- a/routers/api/v1/repo/language.go
+++ b/routers/api/v1/repo/language.go
@@ -70,7 +70,7 @@ func GetLanguages(ctx *context.APIContext) {
langs, err := repo_model.GetLanguageStats(ctx, ctx.Repo.Repository)
if err != nil {
log.Error("GetLanguageStats failed: %v", err)
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/repo/license.go b/routers/api/v1/repo/license.go
index 8a6bdfd42f..3040815e8a 100644
--- a/routers/api/v1/repo/license.go
+++ b/routers/api/v1/repo/license.go
@@ -38,7 +38,7 @@ func GetLicenses(ctx *context.APIContext) {
licenses, err := repo_model.GetRepoLicenses(ctx, ctx.Repo.Repository)
if err != nil {
log.Error("GetRepoLicenses failed: %v", err)
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go
index 452825c0a3..c1e0b47d33 100644
--- a/routers/api/v1/repo/migrate.go
+++ b/routers/api/v1/repo/migrate.go
@@ -72,21 +72,21 @@ func Migrate(ctx *context.APIContext) {
}
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetUser", err)
+ ctx.APIErrorInternal(err)
}
return
}
if ctx.HasAPIError() {
- ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg())
+ ctx.APIError(http.StatusUnprocessableEntity, ctx.GetErrMsg())
return
}
if !ctx.Doer.IsAdmin {
if !repoOwner.IsOrganization() && ctx.Doer.ID != repoOwner.ID {
- ctx.Error(http.StatusForbidden, "", "Given user is not an organization.")
+ ctx.APIError(http.StatusForbidden, "Given user is not an organization.")
return
}
@@ -94,10 +94,10 @@ func Migrate(ctx *context.APIContext) {
// Check ownership of organization.
isOwner, err := organization.OrgFromUser(repoOwner).IsOwnedBy(ctx, ctx.Doer.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err)
+ ctx.APIErrorInternal(err)
return
} else if !isOwner {
- ctx.Error(http.StatusForbidden, "", "Given user is not owner of organization.")
+ ctx.APIError(http.StatusForbidden, "Given user is not owner of organization.")
return
}
}
@@ -115,12 +115,12 @@ func Migrate(ctx *context.APIContext) {
gitServiceType := convert.ToGitServiceType(form.Service)
if form.Mirror && setting.Mirror.DisableNewPull {
- ctx.Error(http.StatusForbidden, "MirrorsGlobalDisabled", fmt.Errorf("the site administrator has disabled the creation of new pull mirrors"))
+ ctx.APIError(http.StatusForbidden, errors.New("the site administrator has disabled the creation of new pull mirrors"))
return
}
if setting.Repository.DisableMigrations {
- ctx.Error(http.StatusForbidden, "MigrationsGlobalDisabled", fmt.Errorf("the site administrator has disabled migrations"))
+ ctx.APIError(http.StatusForbidden, errors.New("the site administrator has disabled migrations"))
return
}
@@ -129,7 +129,7 @@ func Migrate(ctx *context.APIContext) {
if form.LFS && len(form.LFSEndpoint) > 0 {
ep := lfs.DetermineEndpoint("", form.LFSEndpoint)
if ep == nil {
- ctx.Error(http.StatusInternalServerError, "", ctx.Tr("repo.migrate.invalid_lfs_endpoint"))
+ ctx.APIErrorInternal(errors.New("the LFS endpoint is not valid"))
return
}
err = migrations.IsMigrateURLAllowed(ep.String(), ctx.Doer)
@@ -181,7 +181,7 @@ func Migrate(ctx *context.APIContext) {
IsPrivate: opts.Private || setting.Repository.ForcePrivate,
IsMirror: opts.Mirror,
Status: repo_model.RepositoryBeingMigrated,
- })
+ }, false)
if err != nil {
handleMigrateError(ctx, repoOwner, err)
return
@@ -203,7 +203,7 @@ func Migrate(ctx *context.APIContext) {
}
if repo != nil {
- if errDelete := repo_service.DeleteRepositoryDirectly(ctx, ctx.Doer, repo.ID); errDelete != nil {
+ if errDelete := repo_service.DeleteRepositoryDirectly(ctx, repo.ID); errDelete != nil {
log.Error("DeleteRepository: %v", errDelete)
}
}
@@ -221,35 +221,35 @@ func Migrate(ctx *context.APIContext) {
func handleMigrateError(ctx *context.APIContext, repoOwner *user_model.User, err error) {
switch {
case repo_model.IsErrRepoAlreadyExist(err):
- ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.")
+ ctx.APIError(http.StatusConflict, "The repository with the same name already exists.")
case repo_model.IsErrRepoFilesAlreadyExist(err):
- ctx.Error(http.StatusConflict, "", "Files already exist for this repository. Adopt them or delete them.")
+ ctx.APIError(http.StatusConflict, "Files already exist for this repository. Adopt them or delete them.")
case migrations.IsRateLimitError(err):
- ctx.Error(http.StatusUnprocessableEntity, "", "Remote visit addressed rate limitation.")
+ ctx.APIError(http.StatusUnprocessableEntity, "Remote visit addressed rate limitation.")
case migrations.IsTwoFactorAuthError(err):
- ctx.Error(http.StatusUnprocessableEntity, "", "Remote visit required two factors authentication.")
+ ctx.APIError(http.StatusUnprocessableEntity, "Remote visit required two factors authentication.")
case repo_model.IsErrReachLimitOfRepo(err):
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("You have already reached your limit of %d repositories.", repoOwner.MaxCreationLimit()))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Sprintf("You have already reached your limit of %d repositories.", repoOwner.MaxCreationLimit()))
case db.IsErrNameReserved(err):
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' is reserved.", err.(db.ErrNameReserved).Name))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Sprintf("The username '%s' is reserved.", err.(db.ErrNameReserved).Name))
case db.IsErrNameCharsNotAllowed(err):
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' contains invalid characters.", err.(db.ErrNameCharsNotAllowed).Name))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Sprintf("The username '%s' contains invalid characters.", err.(db.ErrNameCharsNotAllowed).Name))
case db.IsErrNamePatternNotAllowed(err):
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(db.ErrNamePatternNotAllowed).Pattern))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(db.ErrNamePatternNotAllowed).Pattern))
case git.IsErrInvalidCloneAddr(err):
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
case base.IsErrNotSupported(err):
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
default:
err = util.SanitizeErrorCredentialURLs(err)
if strings.Contains(err.Error(), "Authentication failed") ||
strings.Contains(err.Error(), "Bad credentials") ||
strings.Contains(err.Error(), "could not read Username") {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Authentication failed: %v.", err))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Sprintf("Authentication failed: %v.", err))
} else if strings.Contains(err.Error(), "fatal:") {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Migration failed: %v.", err))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Sprintf("Migration failed: %v.", err))
} else {
- ctx.Error(http.StatusInternalServerError, "MigrateRepository", err)
+ ctx.APIErrorInternal(err)
}
}
}
@@ -259,19 +259,19 @@ func handleRemoteAddrError(ctx *context.APIContext, err error) {
addrErr := err.(*git.ErrInvalidCloneAddr)
switch {
case addrErr.IsURLError:
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
case addrErr.IsPermissionDenied:
if addrErr.LocalPath {
- ctx.Error(http.StatusUnprocessableEntity, "", "You are not allowed to import local repositories.")
+ ctx.APIError(http.StatusUnprocessableEntity, "You are not allowed to import local repositories.")
} else {
- ctx.Error(http.StatusUnprocessableEntity, "", "You can not import from disallowed hosts.")
+ ctx.APIError(http.StatusUnprocessableEntity, "You can not import from disallowed hosts.")
}
case addrErr.IsInvalidPath:
- ctx.Error(http.StatusUnprocessableEntity, "", "Invalid local path, it does not exist or not a directory.")
+ ctx.APIError(http.StatusUnprocessableEntity, "Invalid local path, it does not exist or not a directory.")
default:
- ctx.Error(http.StatusInternalServerError, "ParseRemoteAddr", "Unknown error type (ErrInvalidCloneAddr): "+err.Error())
+ ctx.APIErrorInternal(fmt.Errorf("unknown error type (ErrInvalidCloneAddr): %w", err))
}
} else {
- ctx.Error(http.StatusInternalServerError, "ParseRemoteAddr", err)
+ ctx.APIErrorInternal(err)
}
}
diff --git a/routers/api/v1/repo/milestone.go b/routers/api/v1/repo/milestone.go
index 8d7516491e..33fa7c4b16 100644
--- a/routers/api/v1/repo/milestone.go
+++ b/routers/api/v1/repo/milestone.go
@@ -74,7 +74,7 @@ func ListMilestones(ctx *context.APIContext) {
Name: ctx.FormString("name"),
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "db.FindAndCount[issues_model.Milestone]", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -173,7 +173,7 @@ func CreateMilestone(ctx *context.APIContext) {
}
if err := issues_model.NewMilestone(ctx, milestone); err != nil {
- ctx.Error(http.StatusInternalServerError, "NewMilestone", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusCreated, convert.ToAPIMilestone(milestone))
@@ -233,7 +233,7 @@ func EditMilestone(ctx *context.APIContext) {
}
if err := issues_model.UpdateMilestone(ctx, milestone, oldIsClosed); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateMilestone", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToAPIMilestone(milestone))
@@ -272,7 +272,7 @@ func DeleteMilestone(ctx *context.APIContext) {
}
if err := issues_model.DeleteMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, m.ID); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteMilestoneByRepoID", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
@@ -288,7 +288,7 @@ func getMilestoneByIDOrName(ctx *context.APIContext) *issues_model.Milestone {
if err == nil {
return milestone
} else if !issues_model.IsErrMilestoneNotExist(err) {
- ctx.Error(http.StatusInternalServerError, "GetMilestoneByRepoID", err)
+ ctx.APIErrorInternal(err)
return nil
}
}
@@ -296,10 +296,10 @@ func getMilestoneByIDOrName(ctx *context.APIContext) *issues_model.Milestone {
milestone, err := issues_model.GetMilestoneByRepoIDANDName(ctx, ctx.Repo.Repository.ID, mile)
if err != nil {
if issues_model.IsErrMilestoneNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return nil
}
- ctx.Error(http.StatusInternalServerError, "GetMilestoneByRepoID", err)
+ ctx.APIErrorInternal(err)
return nil
}
diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go
index c911f6830c..f11a1603c4 100644
--- a/routers/api/v1/repo/mirror.go
+++ b/routers/api/v1/repo/mirror.go
@@ -5,7 +5,6 @@ package repo
import (
"errors"
- "fmt"
"net/http"
"time"
@@ -53,20 +52,20 @@ func MirrorSync(ctx *context.APIContext) {
repo := ctx.Repo.Repository
if !ctx.Repo.CanWrite(unit.TypeCode) {
- ctx.Error(http.StatusForbidden, "MirrorSync", "Must have write access")
+ ctx.APIError(http.StatusForbidden, "Must have write access")
}
if !setting.Mirror.Enabled {
- ctx.Error(http.StatusBadRequest, "MirrorSync", "Mirror feature is disabled")
+ ctx.APIError(http.StatusBadRequest, "Mirror feature is disabled")
return
}
if _, err := repo_model.GetMirrorByRepoID(ctx, repo.ID); err != nil {
if errors.Is(err, repo_model.ErrMirrorNotExist) {
- ctx.Error(http.StatusBadRequest, "MirrorSync", "Repository is not a mirror")
+ ctx.APIError(http.StatusBadRequest, "Repository is not a mirror")
return
}
- ctx.Error(http.StatusInternalServerError, "MirrorSync", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -104,19 +103,19 @@ func PushMirrorSync(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if !setting.Mirror.Enabled {
- ctx.Error(http.StatusBadRequest, "PushMirrorSync", "Mirror feature is disabled")
+ ctx.APIError(http.StatusBadRequest, "Mirror feature is disabled")
return
}
// Get All push mirrors of a specific repo
pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, ctx.Repo.Repository.ID, db.ListOptions{})
if err != nil {
- ctx.Error(http.StatusNotFound, "PushMirrorSync", err)
+ ctx.APIError(http.StatusNotFound, err)
return
}
for _, mirror := range pushMirrors {
ok := mirror_service.SyncPushMirror(ctx, mirror.ID)
if !ok {
- ctx.Error(http.StatusInternalServerError, "PushMirrorSync", "error occurred when syncing push mirror "+mirror.RemoteName)
+ ctx.APIErrorInternal(errors.New("error occurred when syncing push mirror " + mirror.RemoteName))
return
}
}
@@ -161,7 +160,7 @@ func ListPushMirrors(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if !setting.Mirror.Enabled {
- ctx.Error(http.StatusBadRequest, "GetPushMirrorsByRepoID", "Mirror feature is disabled")
+ ctx.APIError(http.StatusBadRequest, "Mirror feature is disabled")
return
}
@@ -169,7 +168,7 @@ func ListPushMirrors(ctx *context.APIContext) {
// Get all push mirrors for the specified repository.
pushMirrors, count, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, utils.GetListOptions(ctx))
if err != nil {
- ctx.Error(http.StatusNotFound, "GetPushMirrorsByRepoID", err)
+ ctx.APIError(http.StatusNotFound, err)
return
}
@@ -219,7 +218,7 @@ func GetPushMirrorByName(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if !setting.Mirror.Enabled {
- ctx.Error(http.StatusBadRequest, "GetPushMirrorByRemoteName", "Mirror feature is disabled")
+ ctx.APIError(http.StatusBadRequest, "Mirror feature is disabled")
return
}
@@ -230,16 +229,16 @@ func GetPushMirrorByName(ctx *context.APIContext) {
RemoteName: mirrorName,
}.ToConds())
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetPushMirrors", err)
+ ctx.APIErrorInternal(err)
return
} else if !exist {
- ctx.Error(http.StatusNotFound, "GetPushMirrors", nil)
+ ctx.APIError(http.StatusNotFound, nil)
return
}
m, err := convert.ToPushMirror(ctx, pushMirror)
if err != nil {
- ctx.ServerError("GetPushMirrorByRemoteName", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, m)
@@ -280,7 +279,7 @@ func AddPushMirror(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if !setting.Mirror.Enabled {
- ctx.Error(http.StatusBadRequest, "AddPushMirror", "Mirror feature is disabled")
+ ctx.APIError(http.StatusBadRequest, "Mirror feature is disabled")
return
}
@@ -320,7 +319,7 @@ func DeletePushMirrorByRemoteName(ctx *context.APIContext) {
// "$ref": "#/responses/error"
if !setting.Mirror.Enabled {
- ctx.Error(http.StatusBadRequest, "DeletePushMirrorByName", "Mirror feature is disabled")
+ ctx.APIError(http.StatusBadRequest, "Mirror feature is disabled")
return
}
@@ -328,7 +327,7 @@ func DeletePushMirrorByRemoteName(ctx *context.APIContext) {
// Delete push mirror on repo by name.
err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{RepoID: ctx.Repo.Repository.ID, RemoteName: remoteName})
if err != nil {
- ctx.Error(http.StatusNotFound, "DeletePushMirrors", err)
+ ctx.APIError(http.StatusNotFound, err)
return
}
ctx.Status(http.StatusNoContent)
@@ -339,7 +338,7 @@ func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirro
interval, err := time.ParseDuration(mirrorOption.Interval)
if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) {
- ctx.Error(http.StatusBadRequest, "CreatePushMirror", err)
+ ctx.APIError(http.StatusBadRequest, err)
return
}
@@ -354,42 +353,42 @@ func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirro
remoteSuffix, err := util.CryptoRandomString(10)
if err != nil {
- ctx.ServerError("CryptoRandomString", err)
+ ctx.APIErrorInternal(err)
return
}
remoteAddress, err := util.SanitizeURL(mirrorOption.RemoteAddress)
if err != nil {
- ctx.ServerError("SanitizeURL", err)
+ ctx.APIErrorInternal(err)
return
}
pushMirror := &repo_model.PushMirror{
RepoID: repo.ID,
Repo: repo,
- RemoteName: fmt.Sprintf("remote_mirror_%s", remoteSuffix),
+ RemoteName: "remote_mirror_" + remoteSuffix,
Interval: interval,
SyncOnCommit: mirrorOption.SyncOnCommit,
RemoteAddress: remoteAddress,
}
if err = db.Insert(ctx, pushMirror); err != nil {
- ctx.ServerError("InsertPushMirror", err)
+ ctx.APIErrorInternal(err)
return
}
// if the registration of the push mirrorOption fails remove it from the database
if err = mirror_service.AddPushMirrorRemote(ctx, pushMirror, address); err != nil {
if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: pushMirror.ID, RepoID: pushMirror.RepoID}); err != nil {
- ctx.ServerError("DeletePushMirrors", err)
+ ctx.APIErrorInternal(err)
return
}
- ctx.ServerError("AddPushMirrorRemote", err)
+ ctx.APIErrorInternal(err)
return
}
m, err := convert.ToPushMirror(ctx, pushMirror)
if err != nil {
- ctx.ServerError("ToPushMirror", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, m)
@@ -400,13 +399,13 @@ func HandleRemoteAddressError(ctx *context.APIContext, err error) {
addrErr := err.(*git.ErrInvalidCloneAddr)
switch {
case addrErr.IsProtocolInvalid:
- ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Invalid mirror protocol")
+ ctx.APIError(http.StatusBadRequest, "Invalid mirror protocol")
case addrErr.IsURLError:
- ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Invalid Url ")
+ ctx.APIError(http.StatusBadRequest, "Invalid Url ")
case addrErr.IsPermissionDenied:
- ctx.Error(http.StatusUnauthorized, "CreatePushMirror", "Permission denied")
+ ctx.APIError(http.StatusUnauthorized, "Permission denied")
default:
- ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Unknown error")
+ ctx.APIError(http.StatusBadRequest, "Unknown error")
}
return
}
diff --git a/routers/api/v1/repo/notes.go b/routers/api/v1/repo/notes.go
index 8fec844cc4..87efb1caf2 100644
--- a/routers/api/v1/repo/notes.go
+++ b/routers/api/v1/repo/notes.go
@@ -4,7 +4,7 @@
package repo
import (
- "fmt"
+ "errors"
"net/http"
"code.gitea.io/gitea/modules/git"
@@ -54,7 +54,7 @@ func GetNote(ctx *context.APIContext) {
sha := ctx.PathParam("sha")
if !git.IsValidRefPattern(sha) {
- ctx.Error(http.StatusUnprocessableEntity, "no valid ref or sha", fmt.Sprintf("no valid ref or sha: %s", sha))
+ ctx.APIError(http.StatusUnprocessableEntity, "no valid ref or sha: "+sha)
return
}
getNote(ctx, sha)
@@ -62,16 +62,16 @@ func GetNote(ctx *context.APIContext) {
func getNote(ctx *context.APIContext, identifier string) {
if ctx.Repo.GitRepo == nil {
- ctx.InternalServerError(fmt.Errorf("no open git repo"))
+ ctx.APIErrorInternal(errors.New("no open git repo"))
return
}
commitID, err := ctx.Repo.GitRepo.ConvertToGitID(identifier)
if err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "ConvertToSHA1", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -79,10 +79,10 @@ func getNote(ctx *context.APIContext, identifier string) {
var note git.Note
if err := git.GetNote(ctx, ctx.Repo.GitRepo, commitID.String(), &note); err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound(identifier)
+ ctx.APIErrorNotFound("commit doesn't exist: " + identifier)
return
}
- ctx.Error(http.StatusInternalServerError, "GetNote", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -96,7 +96,7 @@ func getNote(ctx *context.APIContext, identifier string) {
Files: files,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ToCommit", err)
+ ctx.APIErrorInternal(err)
return
}
apiNote := api.Note{Message: string(note.Message), Commit: cmt}
diff --git a/routers/api/v1/repo/patch.go b/routers/api/v1/repo/patch.go
index 5e24dcf891..e9f5cf5d90 100644
--- a/routers/api/v1/repo/patch.go
+++ b/routers/api/v1/repo/patch.go
@@ -5,15 +5,10 @@ package repo
import (
"net/http"
- "time"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context"
- pull_service "code.gitea.io/gitea/services/pull"
"code.gitea.io/gitea/services/repository/files"
)
@@ -49,63 +44,22 @@ func ApplyDiffPatch(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
// "423":
// "$ref": "#/responses/repoArchivedError"
- apiOpts := web.GetForm(ctx).(*api.ApplyDiffPatchFileOptions)
-
+ apiOpts, changeRepoFileOpts := getAPIChangeRepoFileOptions[*api.ApplyDiffPatchFileOptions](ctx)
opts := &files.ApplyDiffPatchOptions{
- Content: apiOpts.Content,
- SHA: apiOpts.SHA,
- Message: apiOpts.Message,
- OldBranch: apiOpts.BranchName,
- NewBranch: apiOpts.NewBranchName,
- Committer: &files.IdentityOptions{
- Name: apiOpts.Committer.Name,
- Email: apiOpts.Committer.Email,
- },
- Author: &files.IdentityOptions{
- Name: apiOpts.Author.Name,
- Email: apiOpts.Author.Email,
- },
- Dates: &files.CommitDateOptions{
- Author: apiOpts.Dates.Author,
- Committer: apiOpts.Dates.Committer,
- },
- Signoff: apiOpts.Signoff,
- }
- if opts.Dates.Author.IsZero() {
- opts.Dates.Author = time.Now()
- }
- if opts.Dates.Committer.IsZero() {
- opts.Dates.Committer = time.Now()
- }
-
- if opts.Message == "" {
- opts.Message = "apply-patch"
- }
+ Content: apiOpts.Content,
+ Message: util.IfZero(apiOpts.Message, "apply-patch"),
- if !canWriteFiles(ctx, apiOpts.BranchName) {
- ctx.Error(http.StatusInternalServerError, "ApplyPatch", repo_model.ErrUserDoesNotHaveAccessToRepo{
- UserID: ctx.Doer.ID,
- RepoName: ctx.Repo.Repository.LowerName,
- })
- return
+ OldBranch: changeRepoFileOpts.OldBranch,
+ NewBranch: changeRepoFileOpts.NewBranch,
+ Committer: changeRepoFileOpts.Committer,
+ Author: changeRepoFileOpts.Author,
+ Dates: changeRepoFileOpts.Dates,
+ Signoff: changeRepoFileOpts.Signoff,
}
fileResponse, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, opts)
if err != nil {
- if files.IsErrUserCannotCommit(err) || pull_service.IsErrFilePathProtected(err) {
- ctx.Error(http.StatusForbidden, "Access", err)
- return
- }
- if git_model.IsErrBranchAlreadyExists(err) || files.IsErrFilenameInvalid(err) || pull_service.IsErrSHADoesNotMatch(err) ||
- files.IsErrFilePathInvalid(err) || files.IsErrRepoFileAlreadyExists(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Invalid", err)
- return
- }
- if git_model.IsErrBranchNotExist(err) || git.IsErrBranchNotExist(err) {
- ctx.Error(http.StatusNotFound, "BranchDoesNotExist", err)
- return
- }
- ctx.Error(http.StatusInternalServerError, "ApplyPatch", err)
+ handleChangeRepoFilesError(ctx, err)
} else {
ctx.JSON(http.StatusCreated, fileResponse)
}
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index d0c3459b63..2c194f9253 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -23,6 +23,7 @@ import (
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
@@ -59,6 +60,10 @@ func ListPullRequests(ctx *context.APIContext) {
// description: Name of the repo
// type: string
// required: true
+ // - name: base_branch
+ // in: query
+ // description: Filter by target base branch of the pull request
+ // type: string
// - name: state
// in: query
// description: State of pull request
@@ -69,7 +74,7 @@ func ListPullRequests(ctx *context.APIContext) {
// in: query
// description: Type of sort
// type: string
- // enum: [oldest, recentupdate, leastupdate, mostcomment, leastcomment, priority]
+ // enum: [oldest, recentupdate, recentclose, leastupdate, mostcomment, leastcomment, priority]
// - name: milestone
// in: query
// description: ID of the milestone
@@ -108,7 +113,7 @@ func ListPullRequests(ctx *context.APIContext) {
labelIDs, err := base.StringsToInt64s(ctx.FormStrings("labels"))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "PullRequests", err)
+ ctx.APIErrorInternal(err)
return
}
var posterID int64
@@ -116,9 +121,9 @@ func ListPullRequests(ctx *context.APIContext) {
poster, err := user_model.GetUserByName(ctx, posterStr)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusBadRequest, "Poster not found", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -132,15 +137,16 @@ func ListPullRequests(ctx *context.APIContext) {
Labels: labelIDs,
MilestoneID: ctx.FormInt64("milestone"),
PosterID: posterID,
+ BaseBranch: ctx.FormTrim("base_branch"),
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "PullRequests", err)
+ ctx.APIErrorInternal(err)
return
}
apiPrs, err := convert.ToAPIPullRequests(ctx, ctx.Repo.Repository, prs, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ToAPIPullRequests", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -182,21 +188,25 @@ func GetPullRequest(ctx *context.APIContext) {
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err = pr.LoadBaseRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err)
+ ctx.APIErrorInternal(err)
return
}
if err = pr.LoadHeadRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
+ ctx.APIErrorInternal(err)
return
}
+
+ // Consider API access a view for delayed checking.
+ pull_service.StartPullRequestCheckOnView(ctx, pr)
+
ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(ctx, pr, ctx.Doer))
}
@@ -252,9 +262,9 @@ func GetPullRequestByBaseHead(ctx *context.APIContext) {
repo, err := repo_model.GetRepositoryByOwnerAndName(ctx, owner, name)
if err != nil {
if repo_model.IsErrRepoNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetRepositoryByOwnerName", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -267,21 +277,25 @@ func GetPullRequestByBaseHead(ctx *context.APIContext) {
pr, err := issues_model.GetPullRequestByBaseHeadInfo(ctx, ctx.Repo.Repository.ID, headRepoID, ctx.PathParam("base"), headBranch)
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetPullRequestByBaseHeadInfo", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err = pr.LoadBaseRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err)
+ ctx.APIErrorInternal(err)
return
}
if err = pr.LoadHeadRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
+ ctx.APIErrorInternal(err)
return
}
+
+ // Consider API access a view for delayed checking.
+ pull_service.StartPullRequestCheckOnView(ctx, pr)
+
ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(ctx, pr, ctx.Doer))
}
@@ -327,9 +341,9 @@ func DownloadPullDiffOrPatch(ctx *context.APIContext) {
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -343,7 +357,7 @@ func DownloadPullDiffOrPatch(ctx *context.APIContext) {
binary := ctx.FormBool("binary")
if err := pull_service.DownloadDiffOrPatch(ctx, pr, ctx, patch, binary); err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -388,7 +402,7 @@ func CreatePullRequest(ctx *context.APIContext) {
form := *web.GetForm(ctx).(*api.CreatePullRequestOption)
if form.Head == form.Base {
- ctx.Error(http.StatusUnprocessableEntity, "BaseHeadSame", "Invalid PullRequest: There are no changes between the head and the base")
+ ctx.APIError(http.StatusUnprocessableEntity, "Invalid PullRequest: There are no changes between the head and the base")
return
}
@@ -406,7 +420,7 @@ func CreatePullRequest(ctx *context.APIContext) {
defer closer()
if !compareResult.baseRef.IsBranch() || !compareResult.headRef.IsBranch() {
- ctx.Error(http.StatusUnprocessableEntity, "BaseHeadInvalidRefType", "Invalid PullRequest: base and head must be branches")
+ ctx.APIError(http.StatusUnprocessableEntity, "Invalid PullRequest: base and head must be branches")
return
}
@@ -417,7 +431,7 @@ func CreatePullRequest(ctx *context.APIContext) {
)
if err != nil {
if !issues_model.IsErrPullRequestNotExist(err) {
- ctx.Error(http.StatusInternalServerError, "GetUnmergedPullRequest", err)
+ ctx.APIErrorInternal(err)
return
}
} else {
@@ -429,14 +443,14 @@ func CreatePullRequest(ctx *context.APIContext) {
HeadBranch: existingPr.HeadBranch,
BaseBranch: existingPr.BaseBranch,
}
- ctx.Error(http.StatusConflict, "GetUnmergedPullRequest", err)
+ ctx.APIError(http.StatusConflict, err)
return
}
if len(form.Labels) > 0 {
labels, err := issues_model.GetLabelsInRepoByIDs(ctx, ctx.Repo.Repository.ID, form.Labels)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLabelsInRepoByIDs", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -448,7 +462,7 @@ func CreatePullRequest(ctx *context.APIContext) {
if ctx.Repo.Owner.IsOrganization() {
orgLabels, err := issues_model.GetLabelsInOrgByIDs(ctx, ctx.Repo.Owner.ID, form.Labels)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLabelsInOrgByIDs", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -464,9 +478,9 @@ func CreatePullRequest(ctx *context.APIContext) {
milestone, err := issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, form.Milestone)
if err != nil {
if issues_model.IsErrMilestoneNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetMilestoneByRepoID", err)
+ ctx.APIErrorInternal(fmt.Errorf("GetMilestoneByRepoID: %w", err))
}
return
}
@@ -504,9 +518,9 @@ func CreatePullRequest(ctx *context.APIContext) {
assigneeIDs, err := issues_model.MakeIDsFromAPIAssigneesToAdd(ctx, form.Assignee, form.Assignees)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Sprintf("Assignee does not exist: [name: %s]", err))
} else {
- ctx.Error(http.StatusInternalServerError, "AddAssigneeByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -514,17 +528,17 @@ func CreatePullRequest(ctx *context.APIContext) {
for _, aID := range assigneeIDs {
assignee, err := user_model.GetUserByID(ctx, aID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserByID", err)
+ ctx.APIErrorInternal(err)
return
}
valid, err := access_model.CanBeAssigned(ctx, assignee, repo, true)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "canBeAssigned", err)
+ ctx.APIErrorInternal(err)
return
}
if !valid {
- ctx.Error(http.StatusUnprocessableEntity, "canBeAssigned", repo_model.ErrUserDoesNotHaveAccessToRepo{UserID: aID, RepoName: repo.Name})
+ ctx.APIError(http.StatusUnprocessableEntity, repo_model.ErrUserDoesNotHaveAccessToRepo{UserID: aID, RepoName: repo.Name})
return
}
}
@@ -543,13 +557,13 @@ func CreatePullRequest(ctx *context.APIContext) {
if err := pull_service.NewPullRequest(ctx, prOpts); err != nil {
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) {
- ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, "BlockedUser", err)
+ ctx.APIError(http.StatusForbidden, err)
} else if errors.Is(err, issues_model.ErrMustCollaborator) {
- ctx.Error(http.StatusForbidden, "MustCollaborator", err)
+ ctx.APIError(http.StatusForbidden, err)
} else {
- ctx.Error(http.StatusInternalServerError, "NewPullRequest", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -606,23 +620,23 @@ func EditPullRequest(ctx *context.APIContext) {
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
err = pr.LoadIssue(ctx)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
+ ctx.APIErrorInternal(err)
return
}
issue := pr.Issue
issue.Repo = ctx.Repo.Repository
if err := issue.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -634,7 +648,7 @@ func EditPullRequest(ctx *context.APIContext) {
if len(form.Title) > 0 {
err = issue_service.ChangeTitle(ctx, issue, ctx.Doer, form.Title)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ChangeTitle", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -642,11 +656,11 @@ func EditPullRequest(ctx *context.APIContext) {
err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body, issue.ContentVersion)
if err != nil {
if errors.Is(err, issues_model.ErrIssueAlreadyChanged) {
- ctx.Error(http.StatusBadRequest, "ChangeContent", err)
+ ctx.APIError(http.StatusBadRequest, err)
return
}
- ctx.Error(http.StatusInternalServerError, "ChangeContent", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -661,7 +675,7 @@ func EditPullRequest(ctx *context.APIContext) {
}
if err := issues_model.UpdateIssueDeadline(ctx, issue, deadlineUnix, ctx.Doer); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateIssueDeadline", err)
+ ctx.APIErrorInternal(err)
return
}
issue.DeadlineUnix = deadlineUnix
@@ -679,11 +693,11 @@ func EditPullRequest(ctx *context.APIContext) {
err = issue_service.UpdateAssignees(ctx, issue, form.Assignee, form.Assignees, ctx.Doer)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Sprintf("Assignee does not exist: [name: %s]", err))
} else if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, "UpdateAssignees", err)
+ ctx.APIError(http.StatusForbidden, err)
} else {
- ctx.Error(http.StatusInternalServerError, "UpdateAssignees", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -693,8 +707,13 @@ func EditPullRequest(ctx *context.APIContext) {
issue.MilestoneID != form.Milestone {
oldMilestoneID := issue.MilestoneID
issue.MilestoneID = form.Milestone
+ issue.Milestone, err = issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, form.Milestone)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
if err = issue_service.ChangeMilestoneAssign(ctx, issue, ctx.Doer, oldMilestoneID); err != nil {
- ctx.Error(http.StatusInternalServerError, "ChangeMilestoneAssign", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -702,14 +721,14 @@ func EditPullRequest(ctx *context.APIContext) {
if ctx.Repo.CanWrite(unit.TypePullRequests) && form.Labels != nil {
labels, err := issues_model.GetLabelsInRepoByIDs(ctx, ctx.Repo.Repository.ID, form.Labels)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLabelsInRepoByIDsError", err)
+ ctx.APIErrorInternal(err)
return
}
if ctx.Repo.Owner.IsOrganization() {
orgLabels, err := issues_model.GetLabelsInOrgByIDs(ctx, ctx.Repo.Owner.ID, form.Labels)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLabelsInOrgByIDs", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -717,14 +736,14 @@ func EditPullRequest(ctx *context.APIContext) {
}
if err = issues_model.ReplaceIssueLabels(ctx, issue, labels, ctx.Doer); err != nil {
- ctx.Error(http.StatusInternalServerError, "ReplaceLabelsError", err)
+ ctx.APIErrorInternal(err)
return
}
}
if form.State != nil {
if pr.HasMerged {
- ctx.Error(http.StatusPreconditionFailed, "MergedPRState", "cannot change state of this pull request, it was already merged")
+ ctx.APIError(http.StatusPreconditionFailed, "cannot change state of this pull request, it was already merged")
return
}
@@ -737,22 +756,22 @@ func EditPullRequest(ctx *context.APIContext) {
// change pull target branch
if !pr.HasMerged && len(form.Base) != 0 && form.Base != pr.BaseBranch {
- if !ctx.Repo.GitRepo.IsBranchExist(form.Base) {
- ctx.Error(http.StatusNotFound, "NewBaseBranchNotExist", fmt.Errorf("new base '%s' not exist", form.Base))
+ if !gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, form.Base) {
+ ctx.APIError(http.StatusNotFound, fmt.Errorf("new base '%s' not exist", form.Base))
return
}
if err := pull_service.ChangeTargetBranch(ctx, pr, ctx.Doer, form.Base); err != nil {
if issues_model.IsErrPullRequestAlreadyExists(err) {
- ctx.Error(http.StatusConflict, "IsErrPullRequestAlreadyExists", err)
+ ctx.APIError(http.StatusConflict, err)
return
} else if issues_model.IsErrIssueIsClosed(err) {
- ctx.Error(http.StatusUnprocessableEntity, "IsErrIssueIsClosed", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
} else if pull_service.IsErrPullRequestHasMerged(err) {
- ctx.Error(http.StatusConflict, "IsErrPullRequestHasMerged", err)
+ ctx.APIError(http.StatusConflict, err)
return
}
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
notify_service.PullRequestChangeTargetBranch(ctx, ctx.Doer, pr, form.Base)
@@ -762,10 +781,10 @@ func EditPullRequest(ctx *context.APIContext) {
if form.AllowMaintainerEdit != nil {
if err := pull_service.SetAllowEdits(ctx, ctx.Doer, pr, *form.AllowMaintainerEdit); err != nil {
if errors.Is(err, pull_service.ErrUserHasNoPermissionForAction) {
- ctx.Error(http.StatusForbidden, "SetAllowEdits", fmt.Sprintf("SetAllowEdits: %s", err))
+ ctx.APIError(http.StatusForbidden, fmt.Sprintf("SetAllowEdits: %s", err))
return
}
- ctx.ServerError("SetAllowEdits", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -774,9 +793,9 @@ func EditPullRequest(ctx *context.APIContext) {
pr, err = issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, pr.Index)
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -818,9 +837,9 @@ func IsPullRequestMerged(ctx *context.APIContext) {
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -828,7 +847,7 @@ func IsPullRequestMerged(ctx *context.APIContext) {
if pr.HasMerged {
ctx.Status(http.StatusNoContent)
}
- ctx.NotFound()
+ ctx.APIErrorNotFound()
}
// MergePullRequest merges a PR given an index
@@ -876,20 +895,20 @@ func MergePullRequest(ctx *context.APIContext) {
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound("GetPullRequestByIndex", err)
+ ctx.APIErrorNotFound("GetPullRequestByIndex", err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err := pr.LoadHeadRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
+ ctx.APIErrorInternal(err)
return
}
if err := pr.LoadIssue(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
+ ctx.APIErrorInternal(err)
return
}
pr.Issue.Repo = ctx.Repo.Repository
@@ -897,7 +916,7 @@ func MergePullRequest(ctx *context.APIContext) {
if ctx.IsSigned {
// Update issue-user.
if err = activities_model.SetIssueReadBy(ctx, pr.Issue.ID, ctx.Doer.ID); err != nil {
- ctx.Error(http.StatusInternalServerError, "ReadBy", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -915,21 +934,21 @@ func MergePullRequest(ctx *context.APIContext) {
// start with merging by checking
if err := pull_service.CheckPullMergeable(ctx, ctx.Doer, &ctx.Repo.Permission, pr, mergeCheckType, form.ForceMerge); err != nil {
if errors.Is(err, pull_service.ErrIsClosed) {
- ctx.NotFound()
- } else if errors.Is(err, pull_service.ErrUserNotAllowedToMerge) {
- ctx.Error(http.StatusMethodNotAllowed, "Merge", "User not allowed to merge PR")
+ ctx.APIErrorNotFound()
+ } else if errors.Is(err, pull_service.ErrNoPermissionToMerge) {
+ ctx.APIError(http.StatusMethodNotAllowed, "User not allowed to merge PR")
} else if errors.Is(err, pull_service.ErrHasMerged) {
- ctx.Error(http.StatusMethodNotAllowed, "PR already merged", "")
+ ctx.APIError(http.StatusMethodNotAllowed, "")
} else if errors.Is(err, pull_service.ErrIsWorkInProgress) {
- ctx.Error(http.StatusMethodNotAllowed, "PR is a work in progress", "Work in progress PRs cannot be merged")
+ ctx.APIError(http.StatusMethodNotAllowed, "Work in progress PRs cannot be merged")
} else if errors.Is(err, pull_service.ErrNotMergeableState) {
- ctx.Error(http.StatusMethodNotAllowed, "PR not in mergeable state", "Please try again later")
- } else if pull_service.IsErrDisallowedToMerge(err) {
- ctx.Error(http.StatusMethodNotAllowed, "PR is not ready to be merged", err)
+ ctx.APIError(http.StatusMethodNotAllowed, "Please try again later")
+ } else if errors.Is(err, pull_service.ErrNotReadyToMerge) {
+ ctx.APIError(http.StatusMethodNotAllowed, err)
} else if asymkey_service.IsErrWontSign(err) {
- ctx.Error(http.StatusMethodNotAllowed, fmt.Sprintf("Protected branch %s requires signed commits but this merge would not be signed", pr.BaseBranch), err)
+ ctx.APIError(http.StatusMethodNotAllowed, err)
} else {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -938,14 +957,14 @@ func MergePullRequest(ctx *context.APIContext) {
if manuallyMerged {
if err := pull_service.MergedManually(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
if pull_service.IsErrInvalidMergeStyle(err) {
- ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do)))
+ ctx.APIError(http.StatusMethodNotAllowed, fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do)))
return
}
if strings.Contains(err.Error(), "Wrong commit ID") {
ctx.JSON(http.StatusConflict, err)
return
}
- ctx.Error(http.StatusInternalServerError, "Manually-Merged", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusOK)
@@ -960,7 +979,7 @@ func MergePullRequest(ctx *context.APIContext) {
if len(message) == 0 {
message, _, err = pull_service.GetDefaultMergeMessage(ctx, ctx.Repo.GitRepo, pr, repo_model.MergeStyle(form.Do))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetDefaultMergeMessage", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -971,13 +990,13 @@ func MergePullRequest(ctx *context.APIContext) {
}
if form.MergeWhenChecksSucceed {
- scheduled, err := automerge.ScheduleAutoMerge(ctx, ctx.Doer, pr, repo_model.MergeStyle(form.Do), message)
+ scheduled, err := automerge.ScheduleAutoMerge(ctx, ctx.Doer, pr, repo_model.MergeStyle(form.Do), message, form.DeleteBranchAfterMerge)
if err != nil {
if pull_model.IsErrAlreadyScheduledToAutoMerge(err) {
- ctx.Error(http.StatusConflict, "ScheduleAutoMerge", err)
+ ctx.APIError(http.StatusConflict, err)
return
}
- ctx.Error(http.StatusInternalServerError, "ScheduleAutoMerge", err)
+ ctx.APIErrorInternal(err)
return
} else if scheduled {
// nothing more to do ...
@@ -988,7 +1007,7 @@ func MergePullRequest(ctx *context.APIContext) {
if err := pull_service.Merge(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, message, false); err != nil {
if pull_service.IsErrInvalidMergeStyle(err) {
- ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do)))
+ ctx.APIError(http.StatusMethodNotAllowed, fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do)))
} else if pull_service.IsErrMergeConflicts(err) {
conflictError := err.(pull_service.ErrMergeConflicts)
ctx.JSON(http.StatusConflict, conflictError)
@@ -999,18 +1018,18 @@ func MergePullRequest(ctx *context.APIContext) {
conflictError := err.(pull_service.ErrMergeUnrelatedHistories)
ctx.JSON(http.StatusConflict, conflictError)
} else if git.IsErrPushOutOfDate(err) {
- ctx.Error(http.StatusConflict, "Merge", "merge push out of date")
+ ctx.APIError(http.StatusConflict, "merge push out of date")
} else if pull_service.IsErrSHADoesNotMatch(err) {
- ctx.Error(http.StatusConflict, "Merge", "head out of date")
+ ctx.APIError(http.StatusConflict, "head out of date")
} else if git.IsErrPushRejected(err) {
errPushRej := err.(*git.ErrPushRejected)
if len(errPushRej.Message) == 0 {
- ctx.Error(http.StatusConflict, "Merge", "PushRejected without remote error message")
+ ctx.APIError(http.StatusConflict, "PushRejected without remote error message")
} else {
- ctx.Error(http.StatusConflict, "Merge", "PushRejected with remote message: "+errPushRej.Message)
+ ctx.APIError(http.StatusConflict, "PushRejected with remote message: "+errPushRej.Message)
}
} else {
- ctx.Error(http.StatusInternalServerError, "Merge", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -1024,7 +1043,7 @@ func MergePullRequest(ctx *context.APIContext) {
// Don't cleanup when there are other PR's that use this branch as head branch.
exist, err := issues_model.HasUnmergedPullRequestsByHeadInfo(ctx, pr.HeadRepoID, pr.HeadBranch)
if err != nil {
- ctx.ServerError("HasUnmergedPullRequestsByHeadInfo", err)
+ ctx.APIErrorInternal(err)
return
}
if exist {
@@ -1038,32 +1057,25 @@ func MergePullRequest(ctx *context.APIContext) {
} else {
headRepo, err = gitrepo.OpenRepository(ctx, pr.HeadRepo)
if err != nil {
- ctx.ServerError(fmt.Sprintf("OpenRepository[%s]", pr.HeadRepo.FullName()), err)
+ ctx.APIErrorInternal(err)
return
}
defer headRepo.Close()
}
- if err := pull_service.RetargetChildrenOnMerge(ctx, ctx.Doer, pr); err != nil {
- ctx.Error(http.StatusInternalServerError, "RetargetChildrenOnMerge", err)
- return
- }
- if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, headRepo, pr.HeadBranch); err != nil {
+
+ if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, headRepo, pr.HeadBranch, pr); err != nil {
switch {
case git.IsErrBranchNotExist(err):
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
case errors.Is(err, repo_service.ErrBranchIsDefault):
- ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("can not delete default branch"))
+ ctx.APIError(http.StatusForbidden, errors.New("can not delete default branch"))
case errors.Is(err, git_model.ErrBranchIsProtected):
- ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("branch protected"))
+ ctx.APIError(http.StatusForbidden, errors.New("branch protected"))
default:
- ctx.Error(http.StatusInternalServerError, "DeleteBranch", err)
+ ctx.APIErrorInternal(err)
}
return
}
- if err := issues_model.AddDeletePRBranchComment(ctx, ctx.Doer, pr.BaseRepo, pr.Issue.ID, pr.HeadBranch); err != nil {
- // Do not fail here as branch has already been deleted
- log.Error("DeleteBranch: %v", err)
- }
}
}
@@ -1099,14 +1111,14 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
headUser, err = user_model.GetUserByName(ctx, headInfos[0])
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.NotFound("GetUserByName")
+ ctx.APIErrorNotFound("GetUserByName")
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
}
return nil, nil
}
} else {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return nil, nil
}
@@ -1117,14 +1129,14 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
if headRepo == nil && !isSameRepo {
err = baseRepo.GetBaseRepo(ctx)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBaseRepo", err)
+ ctx.APIErrorInternal(err)
return nil, nil
}
// Check if baseRepo's base repository is the same as headUser's repository.
if baseRepo.BaseRepo == nil || baseRepo.BaseRepo.OwnerID != headUser.ID {
log.Trace("parseCompareInfo[%d]: does not have fork or in same repository", baseRepo.ID)
- ctx.NotFound("GetBaseRepo")
+ ctx.APIErrorNotFound("GetBaseRepo")
return nil, nil
}
// Assign headRepo so it can be used below.
@@ -1139,7 +1151,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
} else {
headGitRepo, err = gitrepo.OpenRepository(ctx, headRepo)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
+ ctx.APIErrorInternal(err)
return nil, nil
}
closer = func() { _ = headGitRepo.Close() }
@@ -1153,13 +1165,13 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
// user should have permission to read baseRepo's codes and pulls, NOT headRepo's
permBase, err := access_model.GetUserRepoPermission(ctx, baseRepo, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
+ ctx.APIErrorInternal(err)
return nil, nil
}
if !permBase.CanReadIssuesOrPulls(true) || !permBase.CanRead(unit.TypeCode) {
log.Trace("Permission Denied: User %-v cannot create/read pull requests or cannot read code in Repo %-v\nUser in baseRepo has Permissions: %-+v", ctx.Doer, baseRepo, permBase)
- ctx.NotFound("Can't read pulls or can't read UnitTypeCode")
+ ctx.APIErrorNotFound("Can't read pulls or can't read UnitTypeCode")
return nil, nil
}
@@ -1167,12 +1179,12 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
// TODO: could the logic be simplified if the headRepo is the same as the baseRepo? Need to think more about it.
permHead, err := access_model.GetUserRepoPermission(ctx, headRepo, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
+ ctx.APIErrorInternal(err)
return nil, nil
}
if !permHead.CanRead(unit.TypeCode) {
log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in headRepo has Permissions: %-+v", ctx.Doer, headRepo, permHead)
- ctx.NotFound("Can't read headRepo UnitTypeCode")
+ ctx.APIErrorNotFound("Can't read headRepo UnitTypeCode")
return nil, nil
}
@@ -1185,13 +1197,13 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
headRefValid := headRef.IsBranch() || headRef.IsTag() || git.IsStringLikelyCommitID(git.ObjectFormatFromName(headRepo.ObjectFormatName), headRef.ShortName())
// Check if base&head ref are valid.
if !baseRefValid || !headRefValid {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return nil, nil
}
compareInfo, err := headGitRepo.GetCompareInfo(repo_model.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseRef.ShortName(), headRef.ShortName(), false, false)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetCompareInfo", err)
+ ctx.APIErrorInternal(err)
return nil, nil
}
@@ -1243,34 +1255,34 @@ func UpdatePullRequest(ctx *context.APIContext) {
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
if pr.HasMerged {
- ctx.Error(http.StatusUnprocessableEntity, "UpdatePullRequest", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
if err = pr.LoadIssue(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
+ ctx.APIErrorInternal(err)
return
}
if pr.Issue.IsClosed {
- ctx.Error(http.StatusUnprocessableEntity, "UpdatePullRequest", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
if err = pr.LoadBaseRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err)
+ ctx.APIErrorInternal(err)
return
}
if err = pr.LoadHeadRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -1278,7 +1290,7 @@ func UpdatePullRequest(ctx *context.APIContext) {
allowedUpdateByMerge, allowedUpdateByRebase, err := pull_service.IsUserAllowedToUpdate(ctx, pr, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsUserAllowedToMerge", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -1290,15 +1302,15 @@ func UpdatePullRequest(ctx *context.APIContext) {
// default merge commit message
message := fmt.Sprintf("Merge branch '%s' into %s", pr.BaseBranch, pr.HeadBranch)
- if err = pull_service.Update(ctx, pr, ctx.Doer, message, rebase); err != nil {
+ if err = pull_service.Update(graceful.GetManager().ShutdownContext(), pr, ctx.Doer, message, rebase); err != nil {
if pull_service.IsErrMergeConflicts(err) {
- ctx.Error(http.StatusConflict, "Update", "merge failed because of conflict")
+ ctx.APIError(http.StatusConflict, "merge failed because of conflict")
return
} else if pull_service.IsErrRebaseConflicts(err) {
- ctx.Error(http.StatusConflict, "Update", "rebase failed because of conflict")
+ ctx.APIError(http.StatusConflict, "rebase failed because of conflict")
return
}
- ctx.Error(http.StatusInternalServerError, "pull_service.Update", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -1343,37 +1355,37 @@ func CancelScheduledAutoMerge(ctx *context.APIContext) {
pull, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, pullIndex)
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
exist, autoMerge, err := pull_model.GetScheduledMergeByPullID(ctx, pull.ID)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
if !exist {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if ctx.Doer.ID != autoMerge.DoerID {
allowed, err := access_model.IsUserRepoAdmin(ctx, ctx.Repo.Repository, ctx.Doer)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
if !allowed {
- ctx.Error(http.StatusForbidden, "No permission to cancel", "user has no permission to cancel the scheduled auto merge")
+ ctx.APIError(http.StatusForbidden, "user has no permission to cancel the scheduled auto merge")
return
}
}
if err := automerge.RemoveScheduledAutoMerge(ctx, ctx.Doer, pull); err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
} else {
ctx.Status(http.StatusNoContent)
}
@@ -1428,22 +1440,22 @@ func GetPullRequestCommits(ctx *context.APIContext) {
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err := pr.LoadBaseRepo(ctx); err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
var prInfo *git.CompareInfo
baseGitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, pr.BaseRepo)
if err != nil {
- ctx.ServerError("OpenRepository", err)
+ ctx.APIErrorInternal(err)
return
}
defer closer.Close()
@@ -1454,7 +1466,7 @@ func GetPullRequestCommits(ctx *context.APIContext) {
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName(), false, false)
}
if err != nil {
- ctx.ServerError("GetCompareInfo", err)
+ ctx.APIErrorInternal(err)
return
}
commits := prInfo.Commits
@@ -1483,7 +1495,7 @@ func GetPullRequestCommits(ctx *context.APIContext) {
Files: files,
})
if err != nil {
- ctx.ServerError("toCommit", err)
+ ctx.APIErrorInternal(err)
return
}
apiCommits = append(apiCommits, apiCommit)
@@ -1551,20 +1563,20 @@ func GetPullRequestFiles(ctx *context.APIContext) {
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err := pr.LoadBaseRepo(ctx); err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
if err := pr.LoadHeadRepo(ctx); err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -1577,13 +1589,13 @@ func GetPullRequestFiles(ctx *context.APIContext) {
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName(), true, false)
}
if err != nil {
- ctx.ServerError("GetCompareInfo", err)
+ ctx.APIErrorInternal(err)
return
}
headCommitID, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
if err != nil {
- ctx.ServerError("GetRefCommitID", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -1593,7 +1605,7 @@ func GetPullRequestFiles(ctx *context.APIContext) {
maxLines := setting.Git.MaxGitDiffLines
// FIXME: If there are too many files in the repo, may cause some unpredictable issues.
- diff, err := gitdiff.GetDiff(ctx, baseGitRepo,
+ diff, err := gitdiff.GetDiffForAPI(ctx, baseGitRepo,
&gitdiff.DiffOptions{
BeforeCommitID: startCommitID,
AfterCommitID: endCommitID,
@@ -1604,13 +1616,18 @@ func GetPullRequestFiles(ctx *context.APIContext) {
WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.FormString("whitespace")),
})
if err != nil {
- ctx.ServerError("GetDiff", err)
+ ctx.APIErrorInternal(err)
return
}
+ diffShortStat, err := gitdiff.GetDiffShortStat(baseGitRepo, startCommitID, endCommitID)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
listOptions := utils.GetListOptions(ctx)
- totalNumberOfFiles := diff.NumFiles
+ totalNumberOfFiles := diffShortStat.NumFiles
totalNumberOfPages := int(math.Ceil(float64(totalNumberOfFiles) / float64(listOptions.PageSize)))
start, limit := listOptions.GetSkipTake()
@@ -1621,7 +1638,9 @@ func GetPullRequestFiles(ctx *context.APIContext) {
apiFiles := make([]*api.ChangedFile, 0, limit)
for i := start; i < start+limit; i++ {
- apiFiles = append(apiFiles, convert.ToChangedFile(diff.Files[i], pr.HeadRepo, endCommitID))
+ // refs/pull/1/head stores the HEAD commit ID, allowing all related commits to be found in the base repository.
+ // The head repository might have been deleted, so we should not rely on it here.
+ apiFiles = append(apiFiles, convert.ToChangedFile(diff.Files[i], pr.BaseRepo, endCommitID))
}
ctx.SetLinkHeader(totalNumberOfFiles, listOptions.PageSize)
diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go
index 6d7a326370..9421a052db 100644
--- a/routers/api/v1/repo/pull_review.go
+++ b/routers/api/v1/repo/pull_review.go
@@ -64,20 +64,20 @@ func ListPullReviews(ctx *context.APIContext) {
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound("GetPullRequestByIndex", err)
+ ctx.APIErrorNotFound("GetPullRequestByIndex", err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err = pr.LoadIssue(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
+ ctx.APIErrorInternal(err)
return
}
if err = pr.Issue.LoadRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadRepo", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -88,19 +88,19 @@ func ListPullReviews(ctx *context.APIContext) {
allReviews, err := issues_model.FindReviews(ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
count, err := issues_model.CountReviews(ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
apiReviews, err := convert.ToPullReviewList(ctx, allReviews, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "convertToPullReviewList", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -151,7 +151,7 @@ func GetPullReview(ctx *context.APIContext) {
apiReview, err := convert.ToPullReview(ctx, review, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "convertToPullReview", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -201,7 +201,7 @@ func GetPullReviewComments(ctx *context.APIContext) {
apiComments, err := convert.ToPullReviewCommentList(ctx, review, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "convertToPullReviewCommentList", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -252,16 +252,16 @@ func DeletePullReview(ctx *context.APIContext) {
}
if ctx.Doer == nil {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if !ctx.Doer.IsAdmin && ctx.Doer.ID != review.ReviewerID {
- ctx.Error(http.StatusForbidden, "only admin and user itself can delete a review", nil)
+ ctx.APIError(http.StatusForbidden, nil)
return
}
if err := issues_model.DeleteReview(ctx, review); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteReview", fmt.Errorf("can not delete ReviewID: %d", review.ID))
+ ctx.APIErrorInternal(fmt.Errorf("can not delete ReviewID: %d", review.ID))
return
}
@@ -309,9 +309,9 @@ func CreatePullReview(ctx *context.APIContext) {
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound("GetPullRequestByIndex", err)
+ ctx.APIErrorNotFound("GetPullRequestByIndex", err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -323,7 +323,7 @@ func CreatePullReview(ctx *context.APIContext) {
}
if err := pr.Issue.LoadRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "pr.Issue.LoadRepo", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -331,14 +331,14 @@ func CreatePullReview(ctx *context.APIContext) {
if opts.CommitID == "" {
gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, pr.Issue.Repo)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "git.OpenRepository", err)
+ ctx.APIErrorInternal(err)
return
}
defer closer.Close()
headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName())
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetRefCommitID", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -364,7 +364,7 @@ func CreatePullReview(ctx *context.APIContext) {
opts.CommitID,
nil,
); err != nil {
- ctx.Error(http.StatusInternalServerError, "CreateCodeComment", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -373,9 +373,9 @@ func CreatePullReview(ctx *context.APIContext) {
review, _, err := pull_service.SubmitReview(ctx, ctx.Doer, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, opts.CommitID, nil)
if err != nil {
if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "SubmitReview", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -383,7 +383,7 @@ func CreatePullReview(ctx *context.APIContext) {
// convert response
apiReview, err := convert.ToPullReview(ctx, review, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "convertToPullReview", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, apiReview)
@@ -439,7 +439,7 @@ func SubmitPullReview(ctx *context.APIContext) {
}
if review.Type != issues_model.ReviewTypePending {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("only a pending review can be submitted"))
+ ctx.APIError(http.StatusUnprocessableEntity, errors.New("only a pending review can be submitted"))
return
}
@@ -451,13 +451,13 @@ func SubmitPullReview(ctx *context.APIContext) {
// if review stay pending return
if reviewType == issues_model.ReviewTypePending {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("review stay pending"))
+ ctx.APIError(http.StatusUnprocessableEntity, errors.New("review stay pending"))
return
}
headCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(pr.GetGitRefName())
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GitRepo: GetRefCommitID", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -465,9 +465,9 @@ func SubmitPullReview(ctx *context.APIContext) {
review, _, err = pull_service.SubmitReview(ctx, ctx.Doer, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, headCommitID, nil)
if err != nil {
if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "SubmitReview", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -475,7 +475,7 @@ func SubmitPullReview(ctx *context.APIContext) {
// convert response
apiReview, err := convert.ToPullReview(ctx, review, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "convertToPullReview", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, apiReview)
@@ -484,7 +484,7 @@ func SubmitPullReview(ctx *context.APIContext) {
// preparePullReviewType return ReviewType and false or nil and true if an error happen
func preparePullReviewType(ctx *context.APIContext, pr *issues_model.PullRequest, event api.ReviewStateType, body string, hasComments bool) (issues_model.ReviewType, bool) {
if err := pr.LoadIssue(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
+ ctx.APIErrorInternal(err)
return -1, true
}
@@ -496,7 +496,7 @@ func preparePullReviewType(ctx *context.APIContext, pr *issues_model.PullRequest
case api.ReviewStateApproved:
// can not approve your own PR
if pr.Issue.IsPoster(ctx.Doer.ID) {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("approve your own pull is not allowed"))
+ ctx.APIError(http.StatusUnprocessableEntity, errors.New("approve your own pull is not allowed"))
return -1, true
}
reviewType = issues_model.ReviewTypeApprove
@@ -505,7 +505,7 @@ func preparePullReviewType(ctx *context.APIContext, pr *issues_model.PullRequest
case api.ReviewStateRequestChanges:
// can not reject your own PR
if pr.Issue.IsPoster(ctx.Doer.ID) {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("reject your own pull is not allowed"))
+ ctx.APIError(http.StatusUnprocessableEntity, errors.New("reject your own pull is not allowed"))
return -1, true
}
reviewType = issues_model.ReviewTypeReject
@@ -515,7 +515,7 @@ func preparePullReviewType(ctx *context.APIContext, pr *issues_model.PullRequest
needsBody = false
// if there is no body we need to ensure that there are comments
if !hasBody && !hasComments {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("review event %s requires a body or a comment", event))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("review event %s requires a body or a comment", event))
return -1, true
}
default:
@@ -524,7 +524,7 @@ func preparePullReviewType(ctx *context.APIContext, pr *issues_model.PullRequest
// reject reviews with empty body if a body is required for this call
if needsBody && !hasBody {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("review event %s requires a body", event))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("review event %s requires a body", event))
return -1, true
}
@@ -536,9 +536,9 @@ func prepareSingleReview(ctx *context.APIContext) (*issues_model.Review, *issues
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound("GetPullRequestByIndex", err)
+ ctx.APIErrorNotFound("GetPullRequestByIndex", err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
+ ctx.APIErrorInternal(err)
}
return nil, nil, true
}
@@ -546,27 +546,27 @@ func prepareSingleReview(ctx *context.APIContext) (*issues_model.Review, *issues
review, err := issues_model.GetReviewByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrReviewNotExist(err) {
- ctx.NotFound("GetReviewByID", err)
+ ctx.APIErrorNotFound("GetReviewByID", err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetReviewByID", err)
+ ctx.APIErrorInternal(err)
}
return nil, nil, true
}
// validate the review is for the given PR
if review.IssueID != pr.IssueID {
- ctx.NotFound("ReviewNotInPR")
+ ctx.APIErrorNotFound("ReviewNotInPR")
return nil, nil, true
}
// make sure that the user has access to this review if it is pending
if review.Type == issues_model.ReviewTypePending && review.ReviewerID != ctx.Doer.ID && !ctx.Doer.IsAdmin {
- ctx.NotFound("GetReviewByID")
+ ctx.APIErrorNotFound("GetReviewByID")
return nil, nil, true
}
if err := review.LoadAttributes(ctx); err != nil && !user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusInternalServerError, "ReviewLoadAttributes", err)
+ ctx.APIErrorInternal(err)
return nil, nil, true
}
@@ -668,10 +668,10 @@ func parseReviewersByNames(ctx *context.APIContext, reviewerNames, teamReviewerN
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.NotFound("UserNotExist", fmt.Sprintf("User '%s' not exist", r))
+ ctx.APIErrorNotFound("UserNotExist", fmt.Sprintf("User '%s' not exist", r))
return nil, nil
}
- ctx.Error(http.StatusInternalServerError, "GetUser", err)
+ ctx.APIErrorInternal(err)
return nil, nil
}
@@ -684,10 +684,10 @@ func parseReviewersByNames(ctx *context.APIContext, reviewerNames, teamReviewerN
teamReviewer, err = organization.GetTeam(ctx, ctx.Repo.Owner.ID, t)
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.NotFound("TeamNotExist", fmt.Sprintf("Team '%s' not exist", t))
+ ctx.APIErrorNotFound("TeamNotExist", fmt.Sprintf("Team '%s' not exist", t))
return nil, nil
}
- ctx.Error(http.StatusInternalServerError, "ReviewRequest", err)
+ ctx.APIErrorInternal(err)
return nil, nil
}
@@ -701,21 +701,21 @@ func apiReviewRequest(ctx *context.APIContext, opts api.PullReviewRequestOptions
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound("GetPullRequestByIndex", err)
+ ctx.APIErrorNotFound("GetPullRequestByIndex", err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err := pr.Issue.LoadRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "pr.Issue.LoadRepo", err)
+ ctx.APIErrorInternal(err)
return
}
permDoer, err := access_model.GetUserRepoPermission(ctx, pr.Issue.Repo, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -733,20 +733,20 @@ func apiReviewRequest(ctx *context.APIContext, opts api.PullReviewRequestOptions
comment, err := issue_service.ReviewRequest(ctx, pr.Issue, ctx.Doer, &permDoer, reviewer, isAdd)
if err != nil {
if issues_model.IsErrReviewRequestOnClosedPR(err) {
- ctx.Error(http.StatusForbidden, "", err)
+ ctx.APIError(http.StatusForbidden, err)
return
}
if issues_model.IsErrNotValidReviewRequest(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "ReviewRequest", err)
+ ctx.APIErrorInternal(err)
return
}
if comment != nil && isAdd {
if err = comment.LoadReview(ctx); err != nil {
- ctx.ServerError("ReviewRequest", err)
+ ctx.APIErrorInternal(err)
return
}
reviews = append(reviews, comment.Review)
@@ -758,20 +758,20 @@ func apiReviewRequest(ctx *context.APIContext, opts api.PullReviewRequestOptions
comment, err := issue_service.TeamReviewRequest(ctx, pr.Issue, ctx.Doer, teamReviewer, isAdd)
if err != nil {
if issues_model.IsErrReviewRequestOnClosedPR(err) {
- ctx.Error(http.StatusForbidden, "", err)
+ ctx.APIError(http.StatusForbidden, err)
return
}
if issues_model.IsErrNotValidReviewRequest(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.ServerError("TeamReviewRequest", err)
+ ctx.APIErrorInternal(err)
return
}
if comment != nil && isAdd {
if err = comment.LoadReview(ctx); err != nil {
- ctx.ServerError("ReviewRequest", err)
+ ctx.APIErrorInternal(err)
return
}
reviews = append(reviews, comment.Review)
@@ -782,7 +782,7 @@ func apiReviewRequest(ctx *context.APIContext, opts api.PullReviewRequestOptions
if isAdd {
apiReviews, err := convert.ToPullReviewList(ctx, reviews, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "convertToPullReviewList", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusCreated, apiReviews)
@@ -884,7 +884,7 @@ func UnDismissPullReview(ctx *context.APIContext) {
func dismissReview(ctx *context.APIContext, msg string, isDismiss, dismissPriors bool) {
if !ctx.Repo.IsAdmin() {
- ctx.Error(http.StatusForbidden, "", "Must be repo admin")
+ ctx.APIError(http.StatusForbidden, "Must be repo admin")
return
}
review, _, isWrong := prepareSingleReview(ctx)
@@ -893,29 +893,29 @@ func dismissReview(ctx *context.APIContext, msg string, isDismiss, dismissPriors
}
if review.Type != issues_model.ReviewTypeApprove && review.Type != issues_model.ReviewTypeReject {
- ctx.Error(http.StatusForbidden, "", "not need to dismiss this review because it's type is not Approve or change request")
+ ctx.APIError(http.StatusForbidden, "not need to dismiss this review because it's type is not Approve or change request")
return
}
_, err := pull_service.DismissReview(ctx, review.ID, ctx.Repo.Repository.ID, msg, ctx.Doer, isDismiss, dismissPriors)
if err != nil {
if pull_service.IsErrDismissRequestOnClosedPR(err) {
- ctx.Error(http.StatusForbidden, "", err)
+ ctx.APIError(http.StatusForbidden, err)
return
}
- ctx.Error(http.StatusInternalServerError, "pull_service.DismissReview", err)
+ ctx.APIErrorInternal(err)
return
}
if review, err = issues_model.GetReviewByID(ctx, review.ID); err != nil {
- ctx.Error(http.StatusInternalServerError, "GetReviewByID", err)
+ ctx.APIErrorInternal(err)
return
}
// convert response
apiReview, err := convert.ToPullReview(ctx, review, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "convertToPullReview", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, apiReview)
diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go
index 076f00f1d1..272b395dfb 100644
--- a/routers/api/v1/repo/release.go
+++ b/routers/api/v1/repo/release.go
@@ -4,6 +4,7 @@
package repo
import (
+ "errors"
"fmt"
"net/http"
@@ -53,16 +54,16 @@ func GetRelease(ctx *context.APIContext) {
id := ctx.PathParamInt64("id")
release, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, id)
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
- ctx.Error(http.StatusInternalServerError, "GetReleaseForRepoByID", err)
+ ctx.APIErrorInternal(err)
return
}
if err != nil && repo_model.IsErrReleaseNotExist(err) || release.IsTag {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if err := release.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release))
@@ -93,17 +94,17 @@ func GetLatestRelease(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
release, err := repo_model.GetLatestReleaseByRepoID(ctx, ctx.Repo.Repository.ID)
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
- ctx.Error(http.StatusInternalServerError, "GetLatestRelease", err)
+ ctx.APIErrorInternal(err)
return
}
if err != nil && repo_model.IsErrReleaseNotExist(err) ||
release.IsTag || release.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if err := release.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release))
@@ -161,13 +162,13 @@ func ListReleases(ctx *context.APIContext) {
releases, err := db.Find[repo_model.Release](ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetReleasesByRepoID", err)
+ ctx.APIErrorInternal(err)
return
}
rels := make([]*api.Release, len(releases))
for i, release := range releases {
if err := release.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
rels[i] = convert.ToAPIRelease(ctx, ctx.Repo.Repository, release)
@@ -175,7 +176,7 @@ func ListReleases(ctx *context.APIContext) {
filteredCount, err := db.Count[repo_model.Release](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -220,13 +221,13 @@ func CreateRelease(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.CreateReleaseOption)
if ctx.Repo.Repository.IsEmpty {
- ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty"))
+ ctx.APIError(http.StatusUnprocessableEntity, errors.New("repo is empty"))
return
}
rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, form.TagName)
if err != nil {
if !repo_model.IsErrReleaseNotExist(err) {
- ctx.Error(http.StatusInternalServerError, "GetRelease", err)
+ ctx.APIErrorInternal(err)
return
}
// If target is not provided use default branch
@@ -246,21 +247,23 @@ func CreateRelease(ctx *context.APIContext) {
IsTag: false,
Repo: ctx.Repo.Repository,
}
- if err := release_service.CreateRelease(ctx.Repo.GitRepo, rel, nil, ""); err != nil {
+ // GitHub doesn't have "tag_message", GitLab has: https://docs.gitlab.com/api/releases/#create-a-release
+ // It doesn't need to be the same as the "release note"
+ if err := release_service.CreateRelease(ctx.Repo.GitRepo, rel, nil, form.TagMessage); err != nil {
if repo_model.IsErrReleaseAlreadyExist(err) {
- ctx.Error(http.StatusConflict, "ReleaseAlreadyExist", err)
+ ctx.APIError(http.StatusConflict, err)
} else if release_service.IsErrProtectedTagName(err) {
- ctx.Error(http.StatusUnprocessableEntity, "ProtectedTagName", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else if git.IsErrNotExist(err) {
- ctx.Error(http.StatusNotFound, "ErrNotExist", fmt.Errorf("target \"%v\" not found: %w", rel.Target, err))
+ ctx.APIError(http.StatusNotFound, fmt.Errorf("target \"%v\" not found: %w", rel.Target, err))
} else {
- ctx.Error(http.StatusInternalServerError, "CreateRelease", err)
+ ctx.APIErrorInternal(err)
}
return
}
} else {
if !rel.IsTag {
- ctx.Error(http.StatusConflict, "GetRelease", "Release is has no Tag")
+ ctx.APIError(http.StatusConflict, "Release is has no Tag")
return
}
@@ -275,7 +278,7 @@ func CreateRelease(ctx *context.APIContext) {
rel.Target = form.Target
if err = release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, nil, nil, nil); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateRelease", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -322,11 +325,11 @@ func EditRelease(ctx *context.APIContext) {
id := ctx.PathParamInt64("id")
rel, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, id)
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
- ctx.Error(http.StatusInternalServerError, "GetReleaseForRepoByID", err)
+ ctx.APIErrorInternal(err)
return
}
if err != nil && repo_model.IsErrReleaseNotExist(err) || rel.IsTag {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -349,18 +352,18 @@ func EditRelease(ctx *context.APIContext) {
rel.IsPrerelease = *form.IsPrerelease
}
if err := release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, nil, nil, nil); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateRelease", err)
+ ctx.APIErrorInternal(err)
return
}
// reload data from database
rel, err = repo_model.GetReleaseByID(ctx, id)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
+ ctx.APIErrorInternal(err)
return
}
if err := rel.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, rel))
@@ -399,19 +402,19 @@ func DeleteRelease(ctx *context.APIContext) {
id := ctx.PathParamInt64("id")
rel, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, id)
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
- ctx.Error(http.StatusInternalServerError, "GetReleaseForRepoByID", err)
+ ctx.APIErrorInternal(err)
return
}
if err != nil && repo_model.IsErrReleaseNotExist(err) || rel.IsTag {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if err := release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, false); err != nil {
if release_service.IsErrProtectedTagName(err) {
- ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag")
+ ctx.APIError(http.StatusUnprocessableEntity, "user not allowed to delete protected tag")
return
}
- ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go
index 54ca1fc843..defde81a1d 100644
--- a/routers/api/v1/repo/release_attachment.go
+++ b/routers/api/v1/repo/release_attachment.go
@@ -23,14 +23,14 @@ func checkReleaseMatchRepo(ctx *context.APIContext, releaseID int64) bool {
release, err := repo_model.GetReleaseByID(ctx, releaseID)
if err != nil {
if repo_model.IsErrReleaseNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return false
}
- ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
+ ctx.APIErrorInternal(err)
return false
}
if release.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return false
}
return true
@@ -81,15 +81,15 @@ func GetReleaseAttachment(ctx *context.APIContext) {
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
if err != nil {
if repo_model.IsErrAttachmentNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
- ctx.Error(http.StatusInternalServerError, "GetAttachmentByID", err)
+ ctx.APIErrorInternal(err)
return
}
if attach.ReleaseID != releaseID {
log.Info("User requested attachment is not in release, release_id %v, attachment_id: %v", releaseID, attachID)
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
@@ -130,18 +130,18 @@ func ListReleaseAttachments(ctx *context.APIContext) {
release, err := repo_model.GetReleaseByID(ctx, releaseID)
if err != nil {
if repo_model.IsErrReleaseNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
- ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
+ ctx.APIErrorInternal(err)
return
}
if release.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if err := release.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release).Attachments)
@@ -194,7 +194,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
// Check if attachments are enabled
if !setting.Attachment.Enabled {
- ctx.NotFound("Attachment is not enabled")
+ ctx.APIErrorNotFound("Attachment is not enabled")
return
}
@@ -212,7 +212,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
if strings.HasPrefix(strings.ToLower(ctx.Req.Header.Get("Content-Type")), "multipart/form-data") {
file, header, err := ctx.Req.FormFile("attachment")
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetFile", err)
+ ctx.APIErrorInternal(err)
return
}
defer file.Close()
@@ -229,7 +229,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
}
if filename == "" {
- ctx.Error(http.StatusBadRequest, "CreateReleaseAttachment", "Could not determine name of attachment.")
+ ctx.APIError(http.StatusBadRequest, "Could not determine name of attachment.")
return
}
@@ -242,10 +242,10 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
})
if err != nil {
if upload.IsErrFileTypeForbidden(err) {
- ctx.Error(http.StatusBadRequest, "DetectContentType", err)
+ ctx.APIError(http.StatusBadRequest, err)
return
}
- ctx.Error(http.StatusInternalServerError, "NewAttachment", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -308,15 +308,15 @@ func EditReleaseAttachment(ctx *context.APIContext) {
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
if err != nil {
if repo_model.IsErrAttachmentNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
- ctx.Error(http.StatusInternalServerError, "GetAttachmentByID", err)
+ ctx.APIErrorInternal(err)
return
}
if attach.ReleaseID != releaseID {
log.Info("User requested attachment is not in release, release_id %v, attachment_id: %v", releaseID, attachID)
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
@@ -326,10 +326,10 @@ func EditReleaseAttachment(ctx *context.APIContext) {
if err := attachment_service.UpdateAttachment(ctx, setting.Repository.Release.AllowedTypes, attach); err != nil {
if upload.IsErrFileTypeForbidden(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "UpdateAttachment", attach)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
@@ -381,21 +381,21 @@ func DeleteReleaseAttachment(ctx *context.APIContext) {
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
if err != nil {
if repo_model.IsErrAttachmentNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
- ctx.Error(http.StatusInternalServerError, "GetAttachmentByID", err)
+ ctx.APIErrorInternal(err)
return
}
if attach.ReleaseID != releaseID {
log.Info("User requested attachment is not in release, release_id %v, attachment_id: %v", releaseID, attachID)
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
if err := repo_model.DeleteAttachment(ctx, attach, true); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteAttachment", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go
index 7380c5231c..b5e7d83b2a 100644
--- a/routers/api/v1/repo/release_tags.go
+++ b/routers/api/v1/repo/release_tags.go
@@ -46,20 +46,20 @@ func GetReleaseByTag(ctx *context.APIContext) {
release, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, tag)
if err != nil {
if repo_model.IsErrReleaseNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
- ctx.Error(http.StatusInternalServerError, "GetRelease", err)
+ ctx.APIErrorInternal(err)
return
}
if release.IsTag {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if err = release.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release))
@@ -99,24 +99,24 @@ func DeleteReleaseByTag(ctx *context.APIContext) {
release, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, tag)
if err != nil {
if repo_model.IsErrReleaseNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
- ctx.Error(http.StatusInternalServerError, "GetRelease", err)
+ ctx.APIErrorInternal(err)
return
}
if release.IsTag {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
if err = release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, release, ctx.Doer, false); err != nil {
if release_service.IsErrProtectedTagName(err) {
- ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag")
+ ctx.APIError(http.StatusUnprocessableEntity, "user not allowed to delete protected tag")
return
}
- ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index ce09e7fc0f..8acc912796 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -5,6 +5,7 @@
package repo
import (
+ "errors"
"fmt"
"net/http"
"slices"
@@ -12,7 +13,6 @@ import (
"strings"
"time"
- actions_model "code.gitea.io/gitea/models/actions"
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
@@ -134,7 +134,7 @@ func Search(ctx *context.APIContext) {
private = false
}
- opts := &repo_model.SearchRepoOptions{
+ opts := repo_model.SearchRepoOptions{
ListOptions: utils.GetListOptions(ctx),
Actor: ctx.Doer,
Keyword: ctx.FormTrim("q"),
@@ -171,7 +171,7 @@ func Search(ctx *context.APIContext) {
opts.Collaborate = optional.Some(true)
case "":
default:
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid search mode: \"%s\"", mode))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("Invalid search mode: \"%s\"", mode))
return
}
@@ -193,11 +193,11 @@ func Search(ctx *context.APIContext) {
if orderBy, ok := searchModeMap[sortMode]; ok {
opts.OrderBy = orderBy
} else {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid sort mode: \"%s\"", sortMode))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("Invalid sort mode: \"%s\"", sortMode))
return
}
} else {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid sort order: \"%s\"", sortOrder))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("Invalid sort order: \"%s\"", sortOrder))
return
}
}
@@ -245,7 +245,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
// If the readme template does not exist, a 400 will be returned.
if opt.AutoInit && len(opt.Readme) > 0 && !slices.Contains(repo_module.Readmes, opt.Readme) {
- ctx.Error(http.StatusBadRequest, "", fmt.Errorf("readme template does not exist, available templates: %v", repo_module.Readmes))
+ ctx.APIError(http.StatusBadRequest, fmt.Errorf("readme template does not exist, available templates: %v", repo_module.Readmes))
return
}
@@ -265,13 +265,13 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
})
if err != nil {
if repo_model.IsErrRepoAlreadyExist(err) {
- ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.")
+ ctx.APIError(http.StatusConflict, "The repository with the same name already exists.")
} else if db.IsErrNameReserved(err) ||
db.IsErrNamePatternNotAllowed(err) ||
label.IsErrTemplateLoad(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "CreateRepository", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -279,7 +279,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
// reload repo from db to get a real state after creation
repo, err = repo_model.GetRepositoryByID(ctx, repo.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetRepositoryByID", err)
+ ctx.APIErrorInternal(err)
}
ctx.JSON(http.StatusCreated, convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeOwner}))
@@ -311,7 +311,7 @@ func Create(ctx *context.APIContext) {
opt := web.GetForm(ctx).(*api.CreateRepoOption)
if ctx.Doer.IsOrganization() {
// Shouldn't reach this condition, but just in case.
- ctx.Error(http.StatusUnprocessableEntity, "", "not allowed creating repository for organization")
+ ctx.APIError(http.StatusUnprocessableEntity, "not allowed creating repository for organization")
return
}
CreateUserRepo(ctx, ctx.Doer, *opt)
@@ -355,12 +355,12 @@ func Generate(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.GenerateRepoOption)
if !ctx.Repo.Repository.IsTemplate {
- ctx.Error(http.StatusUnprocessableEntity, "", "this is not a template repo")
+ ctx.APIError(http.StatusUnprocessableEntity, "this is not a template repo")
return
}
if ctx.Doer.IsOrganization() {
- ctx.Error(http.StatusUnprocessableEntity, "", "not allowed creating repository for organization")
+ ctx.APIError(http.StatusUnprocessableEntity, "not allowed creating repository for organization")
return
}
@@ -379,7 +379,7 @@ func Generate(ctx *context.APIContext) {
}
if !opts.IsValid() {
- ctx.Error(http.StatusUnprocessableEntity, "", "must select at least one template item")
+ ctx.APIError(http.StatusUnprocessableEntity, "must select at least one template item")
return
}
@@ -395,22 +395,22 @@ func Generate(ctx *context.APIContext) {
return
}
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
return
}
if !ctx.Doer.IsAdmin && !ctxUser.IsOrganization() {
- ctx.Error(http.StatusForbidden, "", "Only admin can generate repository for other user.")
+ ctx.APIError(http.StatusForbidden, "Only admin can generate repository for other user.")
return
}
if !ctx.Doer.IsAdmin {
canCreate, err := organization.OrgFromUser(ctxUser).CanCreateOrgRepo(ctx, ctx.Doer.ID)
if err != nil {
- ctx.ServerError("CanCreateOrgRepo", err)
+ ctx.APIErrorInternal(err)
return
} else if !canCreate {
- ctx.Error(http.StatusForbidden, "", "Given user is not allowed to create repository in organization.")
+ ctx.APIError(http.StatusForbidden, "Given user is not allowed to create repository in organization.")
return
}
}
@@ -419,12 +419,12 @@ func Generate(ctx *context.APIContext) {
repo, err := repo_service.GenerateRepository(ctx, ctx.Doer, ctxUser, ctx.Repo.Repository, opts)
if err != nil {
if repo_model.IsErrRepoAlreadyExist(err) {
- ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.")
+ ctx.APIError(http.StatusConflict, "The repository with the same name already exists.")
} else if db.IsErrNameReserved(err) ||
db.IsErrNamePatternNotAllowed(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "CreateRepository", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -498,25 +498,25 @@ func CreateOrgRepo(ctx *context.APIContext) {
org, err := organization.GetOrgByName(ctx, ctx.PathParam("org"))
if err != nil {
if organization.IsErrOrgNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetOrgByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
if !organization.HasOrgOrUserVisible(ctx, org.AsUser(), ctx.Doer) {
- ctx.NotFound("HasOrgOrUserVisible", nil)
+ ctx.APIErrorNotFound("HasOrgOrUserVisible", nil)
return
}
if !ctx.Doer.IsAdmin {
canCreate, err := org.CanCreateOrgRepo(ctx, ctx.Doer.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "CanCreateOrgRepo", err)
+ ctx.APIErrorInternal(err)
return
} else if !canCreate {
- ctx.Error(http.StatusForbidden, "", "Given user is not allowed to create repository in organization.")
+ ctx.APIError(http.StatusForbidden, "Given user is not allowed to create repository in organization.")
return
}
}
@@ -548,7 +548,7 @@ func Get(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if err := ctx.Repo.Repository.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "Repository.LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -578,19 +578,19 @@ func GetByID(ctx *context.APIContext) {
repo, err := repo_model.GetRepositoryByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if repo_model.IsErrRepoNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetRepositoryByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
permission, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
+ ctx.APIErrorInternal(err)
return
} else if !permission.HasAnyUnitAccess() {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
ctx.JSON(http.StatusOK, convert.ToRepo(ctx, repo, permission))
@@ -653,7 +653,7 @@ func Edit(ctx *context.APIContext) {
repo, err := repo_model.GetRepositoryByID(ctx, ctx.Repo.Repository.ID)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -673,13 +673,13 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
if err := repo_service.ChangeRepositoryName(ctx, ctx.Doer, repo, newRepoName); err != nil {
switch {
case repo_model.IsErrRepoAlreadyExist(err):
- ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is already taken [name: %s]", newRepoName), err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
case db.IsErrNameReserved(err):
- ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is reserved [name: %s]", newRepoName), err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
case db.IsErrNamePatternNotAllowed(err):
- ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name's pattern is not allowed [name: %s, pattern: %s]", newRepoName, err.(db.ErrNamePatternNotAllowed).Pattern), err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
default:
- ctx.Error(http.StatusUnprocessableEntity, "ChangeRepositoryName", err)
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("ChangeRepositoryName: %w", err))
}
return err
}
@@ -703,7 +703,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
// Visibility of forked repository is forced sync with base repository.
if repo.IsFork {
if err := repo.GetBaseRepo(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "Unable to load base repository", err)
+ ctx.APIErrorInternal(err)
return err
}
*opts.Private = repo.BaseRepo.IsPrivate
@@ -712,8 +712,8 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
visibilityChanged = repo.IsPrivate != *opts.Private
// when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
if visibilityChanged && setting.Repository.ForcePrivate && !*opts.Private && !ctx.Doer.IsAdmin {
- err := fmt.Errorf("cannot change private repository to public")
- ctx.Error(http.StatusUnprocessableEntity, "Force Private enabled", err)
+ err := errors.New("cannot change private repository to public")
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return err
}
@@ -728,17 +728,17 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
var err error
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "Unable to OpenRepository", err)
+ ctx.APIErrorInternal(err)
return err
}
}
// Default branch only updated if changed and exist or the repository is empty
updateRepoLicense := false
- if opts.DefaultBranch != nil && repo.DefaultBranch != *opts.DefaultBranch && (repo.IsEmpty || ctx.Repo.GitRepo.IsBranchExist(*opts.DefaultBranch)) {
+ if opts.DefaultBranch != nil && repo.DefaultBranch != *opts.DefaultBranch && (repo.IsEmpty || gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, *opts.DefaultBranch)) {
if !repo.IsEmpty {
if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, *opts.DefaultBranch); err != nil {
- ctx.Error(http.StatusInternalServerError, "SetDefaultBranch", err)
+ ctx.APIErrorInternal(err)
return err
}
updateRepoLicense = true
@@ -747,7 +747,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
}
if err := repo_service.UpdateRepository(ctx, repo, visibilityChanged); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateRepository", err)
+ ctx.APIErrorInternal(err)
return err
}
@@ -755,7 +755,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
if err := repo_service.AddRepoToLicenseUpdaterQueue(&repo_service.LicenseUpdaterOptions{
RepoID: ctx.Repo.Repository.ID,
}); err != nil {
- ctx.Error(http.StatusInternalServerError, "AddRepoToLicenseUpdaterQueue", err)
+ ctx.APIErrorInternal(err)
return err
}
}
@@ -781,13 +781,13 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
if newHasIssues && opts.ExternalTracker != nil && !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
// Check that values are valid
if !validation.IsValidExternalURL(opts.ExternalTracker.ExternalTrackerURL) {
- err := fmt.Errorf("External tracker URL not valid")
- ctx.Error(http.StatusUnprocessableEntity, "Invalid external tracker URL", err)
+ err := errors.New("External tracker URL not valid")
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return err
}
if len(opts.ExternalTracker.ExternalTrackerFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(opts.ExternalTracker.ExternalTrackerFormat) {
- err := fmt.Errorf("External tracker URL format not valid")
- ctx.Error(http.StatusUnprocessableEntity, "Invalid external tracker URL format", err)
+ err := errors.New("External tracker URL format not valid")
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return err
}
@@ -848,8 +848,8 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
if newHasWiki && opts.ExternalWiki != nil && !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
// Check that values are valid
if !validation.IsValidExternalURL(opts.ExternalWiki.ExternalWikiURL) {
- err := fmt.Errorf("External wiki URL not valid")
- ctx.Error(http.StatusUnprocessableEntity, "", "Invalid external wiki URL")
+ err := errors.New("External wiki URL not valid")
+ ctx.APIError(http.StatusUnprocessableEntity, "Invalid external wiki URL")
return err
}
@@ -1024,7 +1024,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
if len(units)+len(deleteUnitTypes) > 0 {
if err := repo_service.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateRepositoryUnits", err)
+ ctx.APIErrorInternal(err)
return err
}
}
@@ -1039,24 +1039,24 @@ func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) e
// archive / un-archive
if opts.Archived != nil {
if repo.IsMirror {
- err := fmt.Errorf("repo is a mirror, cannot archive/un-archive")
- ctx.Error(http.StatusUnprocessableEntity, err.Error(), err)
+ err := errors.New("repo is a mirror, cannot archive/un-archive")
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return err
}
if *opts.Archived {
if err := repo_model.SetArchiveRepoState(ctx, repo, *opts.Archived); err != nil {
log.Error("Tried to archive a repo: %s", err)
- ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
+ ctx.APIErrorInternal(err)
return err
}
- if err := actions_model.CleanRepoScheduleTasks(ctx, repo); err != nil {
+ if err := actions_service.CleanRepoScheduleTasks(ctx, repo); err != nil {
log.Error("CleanRepoScheduleTasks for archived repo %s/%s: %v", ctx.Repo.Owner.Name, repo.Name, err)
}
log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
} else {
if err := repo_model.SetArchiveRepoState(ctx, repo, *opts.Archived); err != nil {
log.Error("Tried to un-archive a repo: %s", err)
- ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
+ ctx.APIErrorInternal(err)
return err
}
if ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypeActions) {
@@ -1084,7 +1084,7 @@ func updateMirror(ctx *context.APIContext, opts api.EditRepoOption) error {
mirror, err := repo_model.GetMirrorByRepoID(ctx, repo.ID)
if err != nil {
log.Error("Failed to get mirror: %s", err)
- ctx.Error(http.StatusInternalServerError, "MirrorInterval", err)
+ ctx.APIErrorInternal(err)
return err
}
@@ -1094,14 +1094,14 @@ func updateMirror(ctx *context.APIContext, opts api.EditRepoOption) error {
interval, err := time.ParseDuration(*opts.MirrorInterval)
if err != nil {
log.Error("Wrong format for MirrorInternal Sent: %s", err)
- ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return err
}
// Ensure the provided duration is not too short
if interval != 0 && interval < setting.Mirror.MinInterval {
err := fmt.Errorf("invalid mirror interval: %s is below minimum interval: %s", interval, setting.Mirror.MinInterval)
- ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return err
}
@@ -1120,7 +1120,7 @@ func updateMirror(ctx *context.APIContext, opts api.EditRepoOption) error {
// finally update the mirror in the DB
if err := repo_model.UpdateMirror(ctx, mirror); err != nil {
log.Error("Failed to Set Mirror Interval: %s", err)
- ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return err
}
@@ -1158,10 +1158,10 @@ func Delete(ctx *context.APIContext) {
canDelete, err := repo_module.CanUserDelete(ctx, repo, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "CanUserDelete", err)
+ ctx.APIErrorInternal(err)
return
} else if !canDelete {
- ctx.Error(http.StatusForbidden, "", "Given user is not owner of organization.")
+ ctx.APIError(http.StatusForbidden, "Given user is not owner of organization.")
return
}
@@ -1170,7 +1170,7 @@ func Delete(ctx *context.APIContext) {
}
if err := repo_service.DeleteRepository(ctx, ctx.Doer, repo, true); err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteRepository", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -1315,7 +1315,7 @@ func ListRepoActivityFeeds(ctx *context.APIContext) {
feeds, count, err := feed_service.GetFeeds(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetFeeds", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.SetTotalCountHeader(count)
diff --git a/routers/api/v1/repo/repo_test.go b/routers/api/v1/repo/repo_test.go
index 8d6ca9e3b5..97233f85dc 100644
--- a/routers/api/v1/repo/repo_test.go
+++ b/routers/api/v1/repo/repo_test.go
@@ -58,7 +58,7 @@ func TestRepoEdit(t *testing.T) {
web.SetForm(ctx, &opts)
Edit(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
ID: 1,
}, unittest.Cond("name = ? AND is_archived = 1", *opts.Name))
@@ -78,7 +78,7 @@ func TestRepoEditNameChange(t *testing.T) {
web.SetForm(ctx, &opts)
Edit(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
ID: 1,
diff --git a/routers/api/v1/repo/star.go b/routers/api/v1/repo/star.go
index 99676de119..46218e0e28 100644
--- a/routers/api/v1/repo/star.go
+++ b/routers/api/v1/repo/star.go
@@ -44,10 +44,12 @@ func ListStargazers(ctx *context.APIContext) {
// "$ref": "#/responses/UserList"
// "404":
// "$ref": "#/responses/notFound"
+ // "403":
+ // "$ref": "#/responses/forbidden"
stargazers, err := repo_model.GetStargazers(ctx, ctx.Repo.Repository, utils.GetListOptions(ctx))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetStargazers", err)
+ ctx.APIErrorInternal(err)
return
}
users := make([]*api.User, len(stargazers))
diff --git a/routers/api/v1/repo/status.go b/routers/api/v1/repo/status.go
index 8c910a68f9..40007ea1e5 100644
--- a/routers/api/v1/repo/status.go
+++ b/routers/api/v1/repo/status.go
@@ -55,7 +55,7 @@ func NewCommitStatus(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.CreateStatusOption)
sha := ctx.PathParam("sha")
if len(sha) == 0 {
- ctx.Error(http.StatusBadRequest, "sha not given", nil)
+ ctx.APIError(http.StatusBadRequest, nil)
return
}
status := &git_model.CommitStatus{
@@ -65,7 +65,7 @@ func NewCommitStatus(ctx *context.APIContext) {
Context: form.Context,
}
if err := commitstatus_service.CreateCommitStatus(ctx, ctx.Repo.Repository, ctx.Doer, sha, status); err != nil {
- ctx.Error(http.StatusInternalServerError, "CreateCommitStatus", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -177,20 +177,14 @@ func GetCommitStatusesByRef(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
- filter := utils.ResolveRefOrSha(ctx, ctx.PathParam("ref"))
+ refCommit := resolveRefCommit(ctx, ctx.PathParam("ref"), 7)
if ctx.Written() {
return
}
-
- getCommitStatuses(ctx, filter) // By default filter is maybe the raw SHA
+ getCommitStatuses(ctx, refCommit.CommitID)
}
-func getCommitStatuses(ctx *context.APIContext, sha string) {
- if len(sha) == 0 {
- ctx.Error(http.StatusBadRequest, "ref/sha not given", nil)
- return
- }
- sha = utils.MustConvertToSHA1(ctx.Base, ctx.Repo, sha)
+func getCommitStatuses(ctx *context.APIContext, commitID string) {
repo := ctx.Repo.Repository
listOptions := utils.GetListOptions(ctx)
@@ -198,12 +192,12 @@ func getCommitStatuses(ctx *context.APIContext, sha string) {
statuses, maxResults, err := db.FindAndCount[git_model.CommitStatus](ctx, &git_model.CommitStatusOptions{
ListOptions: listOptions,
RepoID: repo.ID,
- SHA: sha,
+ SHA: commitID,
SortType: ctx.FormTrim("sort"),
State: ctx.FormTrim("state"),
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetCommitStatuses", fmt.Errorf("GetCommitStatuses[%s, %s, %d]: %w", repo.FullName(), sha, ctx.FormInt("page"), err))
+ ctx.APIErrorInternal(fmt.Errorf("GetCommitStatuses[%s, %s, %d]: %w", repo.FullName(), commitID, ctx.FormInt("page"), err))
return
}
@@ -257,26 +251,31 @@ func GetCombinedCommitStatusByRef(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
- sha := utils.ResolveRefOrSha(ctx, ctx.PathParam("ref"))
+ refCommit := resolveRefCommit(ctx, ctx.PathParam("ref"), 7)
if ctx.Written() {
return
}
repo := ctx.Repo.Repository
- statuses, count, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, utils.GetListOptions(ctx))
+ statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, refCommit.Commit.ID.String(), utils.GetListOptions(ctx))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLatestCommitStatus", fmt.Errorf("GetLatestCommitStatus[%s, %s]: %w", repo.FullName(), sha, err))
+ ctx.APIErrorInternal(fmt.Errorf("GetLatestCommitStatus[%s, %s]: %w", repo.FullName(), refCommit.CommitID, err))
return
}
+ count, err := git_model.CountLatestCommitStatus(ctx, repo.ID, refCommit.Commit.ID.String())
+ if err != nil {
+ ctx.APIErrorInternal(fmt.Errorf("CountLatestCommitStatus[%s, %s]: %w", repo.FullName(), refCommit.CommitID, err))
+ return
+ }
+ ctx.SetTotalCountHeader(count)
+
if len(statuses) == 0 {
ctx.JSON(http.StatusOK, &api.CombinedStatus{})
return
}
combiStatus := convert.ToCombinedStatus(ctx, statuses, convert.ToRepo(ctx, repo, ctx.Repo.Permission))
-
- ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, combiStatus)
}
diff --git a/routers/api/v1/repo/subscriber.go b/routers/api/v1/repo/subscriber.go
index 8584182857..14f296a83d 100644
--- a/routers/api/v1/repo/subscriber.go
+++ b/routers/api/v1/repo/subscriber.go
@@ -47,7 +47,7 @@ func ListSubscribers(ctx *context.APIContext) {
subscribers, err := repo_model.GetRepoWatchers(ctx, ctx.Repo.Repository.ID, utils.GetListOptions(ctx))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetRepoWatchers", err)
+ ctx.APIErrorInternal(err)
return
}
users := make([]*api.User, len(subscribers))
diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go
index 8447a8f1f2..9e77637282 100644
--- a/routers/api/v1/repo/tag.go
+++ b/routers/api/v1/repo/tag.go
@@ -57,7 +57,7 @@ func ListTags(ctx *context.APIContext) {
tags, total, err := ctx.Repo.GitRepo.GetTagInfos(listOpts.Page, listOpts.PageSize)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetTags", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -103,16 +103,16 @@ func GetAnnotatedTag(ctx *context.APIContext) {
sha := ctx.PathParam("sha")
if len(sha) == 0 {
- ctx.Error(http.StatusBadRequest, "", "SHA not provided")
+ ctx.APIError(http.StatusBadRequest, "SHA not provided")
return
}
if tag, err := ctx.Repo.GitRepo.GetAnnotatedTag(sha); err != nil {
- ctx.Error(http.StatusBadRequest, "GetAnnotatedTag", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- commit, err := tag.Commit(ctx.Repo.GitRepo)
+ commit, err := ctx.Repo.GitRepo.GetTagCommit(tag.Name)
if err != nil {
- ctx.Error(http.StatusBadRequest, "GetAnnotatedTag", err)
+ ctx.APIError(http.StatusBadRequest, err)
}
ctx.JSON(http.StatusOK, convert.ToAnnotatedTag(ctx, ctx.Repo.Repository, tag, commit))
}
@@ -150,7 +150,7 @@ func GetTag(ctx *context.APIContext) {
tag, err := ctx.Repo.GitRepo.GetTag(tagName)
if err != nil {
- ctx.NotFound(tagName)
+ ctx.APIErrorNotFound("tag doesn't exist: " + tagName)
return
}
ctx.JSON(http.StatusOK, convert.ToTag(ctx.Repo.Repository, tag))
@@ -200,27 +200,27 @@ func CreateTag(ctx *context.APIContext) {
commit, err := ctx.Repo.GitRepo.GetCommit(form.Target)
if err != nil {
- ctx.Error(http.StatusNotFound, "target not found", fmt.Errorf("target not found: %w", err))
+ ctx.APIError(http.StatusNotFound, fmt.Errorf("target not found: %w", err))
return
}
if err := release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, commit.ID.String(), form.TagName, form.Message); err != nil {
if release_service.IsErrTagAlreadyExists(err) {
- ctx.Error(http.StatusConflict, "tag exist", err)
+ ctx.APIError(http.StatusConflict, err)
return
}
if release_service.IsErrProtectedTagName(err) {
- ctx.Error(http.StatusUnprocessableEntity, "CreateNewTag", "user not allowed to create protected tag")
+ ctx.APIError(http.StatusUnprocessableEntity, "user not allowed to create protected tag")
return
}
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
tag, err := ctx.Repo.GitRepo.GetTag(form.TagName)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusCreated, convert.ToTag(ctx.Repo.Repository, tag))
@@ -267,24 +267,24 @@ func DeleteTag(ctx *context.APIContext) {
tag, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, tagName)
if err != nil {
if repo_model.IsErrReleaseNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
- ctx.Error(http.StatusInternalServerError, "GetRelease", err)
+ ctx.APIErrorInternal(err)
return
}
if !tag.IsTag {
- ctx.Error(http.StatusConflict, "IsTag", errors.New("a tag attached to a release cannot be deleted directly"))
+ ctx.APIError(http.StatusConflict, errors.New("a tag attached to a release cannot be deleted directly"))
return
}
if err = release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, tag, ctx.Doer, true); err != nil {
if release_service.IsErrProtectedTagName(err) {
- ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag")
+ ctx.APIError(http.StatusUnprocessableEntity, "user not allowed to delete protected tag")
return
}
- ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -316,7 +316,7 @@ func ListTagProtection(ctx *context.APIContext) {
repo := ctx.Repo.Repository
pts, err := git_model.GetProtectedTags(ctx, repo.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedTags", err)
+ ctx.APIErrorInternal(err)
return
}
apiPts := make([]*api.TagProtection, len(pts))
@@ -360,12 +360,12 @@ func GetTagProtection(ctx *context.APIContext) {
id := ctx.PathParamInt64("id")
pt, err := git_model.GetProtectedTagByID(ctx, id)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedTagByID", err)
+ ctx.APIErrorInternal(err)
return
}
if pt == nil || repo.ID != pt.RepoID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -413,21 +413,21 @@ func CreateTagProtection(ctx *context.APIContext) {
namePattern := strings.TrimSpace(form.NamePattern)
if namePattern == "" {
- ctx.Error(http.StatusBadRequest, "name_pattern are empty", "name_pattern are empty")
+ ctx.APIError(http.StatusBadRequest, "name_pattern are empty")
return
}
if len(form.WhitelistUsernames) == 0 && len(form.WhitelistTeams) == 0 {
- ctx.Error(http.StatusBadRequest, "both whitelist_usernames and whitelist_teams are empty", "both whitelist_usernames and whitelist_teams are empty")
+ ctx.APIError(http.StatusBadRequest, "both whitelist_usernames and whitelist_teams are empty")
return
}
pt, err := git_model.GetProtectedTagByNamePattern(ctx, repo.ID, namePattern)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectTagOfRepo", err)
+ ctx.APIErrorInternal(err)
return
} else if pt != nil {
- ctx.Error(http.StatusForbidden, "Create tag protection", "Tag protection already exist")
+ ctx.APIError(http.StatusForbidden, "Tag protection already exist")
return
}
@@ -435,10 +435,10 @@ func CreateTagProtection(ctx *context.APIContext) {
whitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.WhitelistUsernames, false)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -446,10 +446,10 @@ func CreateTagProtection(ctx *context.APIContext) {
whitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.WhitelistTeams, false)
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -461,18 +461,18 @@ func CreateTagProtection(ctx *context.APIContext) {
AllowlistTeamIDs: whitelistTeams,
}
if err := git_model.InsertProtectedTag(ctx, protectTag); err != nil {
- ctx.Error(http.StatusInternalServerError, "InsertProtectedTag", err)
+ ctx.APIErrorInternal(err)
return
}
pt, err = git_model.GetProtectedTagByID(ctx, protectTag.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedTagByID", err)
+ ctx.APIErrorInternal(err)
return
}
if pt == nil || pt.RepoID != repo.ID {
- ctx.Error(http.StatusInternalServerError, "New tag protection not found", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -524,12 +524,12 @@ func EditTagProtection(ctx *context.APIContext) {
id := ctx.PathParamInt64("id")
pt, err := git_model.GetProtectedTagByID(ctx, id)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedTagByID", err)
+ ctx.APIErrorInternal(err)
return
}
if pt == nil || pt.RepoID != repo.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -543,10 +543,10 @@ func EditTagProtection(ctx *context.APIContext) {
whitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.WhitelistTeams, false)
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -557,10 +557,10 @@ func EditTagProtection(ctx *context.APIContext) {
whitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.WhitelistUsernames, false)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
- ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
+ ctx.APIErrorInternal(err)
return
}
pt.AllowlistUserIDs = whitelistUsers
@@ -568,18 +568,18 @@ func EditTagProtection(ctx *context.APIContext) {
err = git_model.UpdateProtectedTag(ctx, pt)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateProtectedTag", err)
+ ctx.APIErrorInternal(err)
return
}
pt, err = git_model.GetProtectedTagByID(ctx, id)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedTagByID", err)
+ ctx.APIErrorInternal(err)
return
}
if pt == nil || pt.RepoID != repo.ID {
- ctx.Error(http.StatusInternalServerError, "New tag protection not found", "New tag protection not found")
+ ctx.APIErrorInternal(errors.New("new tag protection not found"))
return
}
@@ -619,18 +619,18 @@ func DeleteTagProtection(ctx *context.APIContext) {
id := ctx.PathParamInt64("id")
pt, err := git_model.GetProtectedTagByID(ctx, id)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetProtectedTagByID", err)
+ ctx.APIErrorInternal(err)
return
}
if pt == nil || pt.RepoID != repo.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
err = git_model.DeleteProtectedTag(ctx, pt)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteProtectedTag", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/repo/teams.go b/routers/api/v1/repo/teams.go
index e5a2d5c320..739a9e3892 100644
--- a/routers/api/v1/repo/teams.go
+++ b/routers/api/v1/repo/teams.go
@@ -38,19 +38,19 @@ func ListTeams(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if !ctx.Repo.Owner.IsOrganization() {
- ctx.Error(http.StatusMethodNotAllowed, "noOrg", "repo is not owned by an organization")
+ ctx.APIError(http.StatusMethodNotAllowed, "repo is not owned by an organization")
return
}
teams, err := organization.GetRepoTeams(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.ID)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
apiTeams, err := convert.ToTeams(ctx, teams, false)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -89,7 +89,7 @@ func IsTeam(ctx *context.APIContext) {
// "$ref": "#/responses/error"
if !ctx.Repo.Owner.IsOrganization() {
- ctx.Error(http.StatusMethodNotAllowed, "noOrg", "repo is not owned by an organization")
+ ctx.APIError(http.StatusMethodNotAllowed, "repo is not owned by an organization")
return
}
@@ -101,14 +101,14 @@ func IsTeam(ctx *context.APIContext) {
if repo_service.HasRepository(ctx, team, ctx.Repo.Repository.ID) {
apiTeam, err := convert.ToTeam(ctx, team)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, apiTeam)
return
}
- ctx.NotFound()
+ ctx.APIErrorNotFound()
}
// AddTeam add a team to a repository
@@ -185,10 +185,10 @@ func DeleteTeam(ctx *context.APIContext) {
func changeRepoTeam(ctx *context.APIContext, add bool) {
if !ctx.Repo.Owner.IsOrganization() {
- ctx.Error(http.StatusMethodNotAllowed, "noOrg", "repo is not owned by an organization")
+ ctx.APIError(http.StatusMethodNotAllowed, "repo is not owned by an organization")
}
if !ctx.Repo.Owner.RepoAdminChangeTeamAccess && !ctx.Repo.IsOwner() {
- ctx.Error(http.StatusForbidden, "noAdmin", "user is nor repo admin nor owner")
+ ctx.APIError(http.StatusForbidden, "user is nor repo admin nor owner")
return
}
@@ -201,19 +201,19 @@ func changeRepoTeam(ctx *context.APIContext, add bool) {
var err error
if add {
if repoHasTeam {
- ctx.Error(http.StatusUnprocessableEntity, "alreadyAdded", fmt.Errorf("team '%s' is already added to repo", team.Name))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("team '%s' is already added to repo", team.Name))
return
}
err = repo_service.TeamAddRepository(ctx, team, ctx.Repo.Repository)
} else {
if !repoHasTeam {
- ctx.Error(http.StatusUnprocessableEntity, "notAdded", fmt.Errorf("team '%s' was not added to repo", team.Name))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("team '%s' was not added to repo", team.Name))
return
}
err = repo_service.RemoveRepositoryFromTeam(ctx, team, ctx.Repo.Repository.ID)
}
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -224,10 +224,10 @@ func getTeamByParam(ctx *context.APIContext) *organization.Team {
team, err := organization.GetTeam(ctx, ctx.Repo.Owner.ID, ctx.PathParam("team"))
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusNotFound, "TeamNotExit", err)
+ ctx.APIError(http.StatusNotFound, err)
return nil
}
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return nil
}
return team
diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go
index a1a15e7f46..9c4c22e039 100644
--- a/routers/api/v1/repo/topic.go
+++ b/routers/api/v1/repo/topic.go
@@ -56,7 +56,7 @@ func ListTopics(ctx *context.APIContext) {
topics, total, err := db.FindAndCount[repo_model.Topic](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -124,7 +124,7 @@ func UpdateTopics(ctx *context.APIContext) {
err := repo_model.SaveTopics(ctx, ctx.Repo.Repository.ID, validTopics...)
if err != nil {
log.Error("SaveTopics failed: %v", err)
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -178,7 +178,7 @@ func AddTopic(ctx *context.APIContext) {
})
if err != nil {
log.Error("CountTopics failed: %v", err)
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
if count >= 25 {
@@ -191,7 +191,7 @@ func AddTopic(ctx *context.APIContext) {
_, err = repo_model.AddTopic(ctx, ctx.Repo.Repository.ID, topicName)
if err != nil {
log.Error("AddTopic failed: %v", err)
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -242,12 +242,12 @@ func DeleteTopic(ctx *context.APIContext) {
topic, err := repo_model.DeleteTopic(ctx, ctx.Repo.Repository.ID, topicName)
if err != nil {
log.Error("DeleteTopic failed: %v", err)
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
if topic == nil {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -290,7 +290,7 @@ func TopicSearch(ctx *context.APIContext) {
topics, total, err := db.FindAndCount[repo_model.Topic](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go
index b2090cac41..cbf3d10c39 100644
--- a/routers/api/v1/repo/transfer.go
+++ b/routers/api/v1/repo/transfer.go
@@ -15,6 +15,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
@@ -60,17 +61,17 @@ func Transfer(ctx *context.APIContext) {
newOwner, err := user_model.GetUserByName(ctx, opts.NewOwner)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusNotFound, "", "The new owner does not exist or cannot be found")
+ ctx.APIError(http.StatusNotFound, "The new owner does not exist or cannot be found")
return
}
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
if newOwner.Type == user_model.UserTypeOrganization {
if !ctx.Doer.IsAdmin && newOwner.Visibility == api.VisibleTypePrivate && !organization.OrgFromUser(newOwner).HasMemberWithUserID(ctx, ctx.Doer.ID) {
// The user shouldn't know about this organization
- ctx.Error(http.StatusNotFound, "", "The new owner does not exist or cannot be found")
+ ctx.APIError(http.StatusNotFound, "The new owner does not exist or cannot be found")
return
}
}
@@ -78,7 +79,7 @@ func Transfer(ctx *context.APIContext) {
var teams []*organization.Team
if opts.TeamIDs != nil {
if !newOwner.IsOrganization() {
- ctx.Error(http.StatusUnprocessableEntity, "repoTransfer", "Teams can only be added to organization-owned repositories")
+ ctx.APIError(http.StatusUnprocessableEntity, "Teams can only be added to organization-owned repositories")
return
}
@@ -86,12 +87,12 @@ func Transfer(ctx *context.APIContext) {
for _, tID := range *opts.TeamIDs {
team, err := organization.GetTeamByID(ctx, tID)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "team", fmt.Errorf("team %d not found", tID))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("team %d not found", tID))
return
}
if team.OrgID != org.ID {
- ctx.Error(http.StatusForbidden, "team", fmt.Errorf("team %d belongs not to org %d", tID, org.ID))
+ ctx.APIError(http.StatusForbidden, fmt.Errorf("team %d belongs not to org %d", tID, org.ID))
return
}
@@ -107,20 +108,17 @@ func Transfer(ctx *context.APIContext) {
oldFullname := ctx.Repo.Repository.FullName()
if err := repo_service.StartRepositoryTransfer(ctx, ctx.Doer, newOwner, ctx.Repo.Repository, teams); err != nil {
- if repo_model.IsErrRepoTransferInProgress(err) {
- ctx.Error(http.StatusConflict, "StartRepositoryTransfer", err)
- return
- }
-
- if repo_model.IsErrRepoAlreadyExist(err) {
- ctx.Error(http.StatusUnprocessableEntity, "StartRepositoryTransfer", err)
- return
- }
-
- if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, "BlockedUser", err)
- } else {
- ctx.InternalServerError(err)
+ switch {
+ case repo_model.IsErrRepoTransferInProgress(err):
+ ctx.APIError(http.StatusConflict, err)
+ case repo_model.IsErrRepoAlreadyExist(err):
+ ctx.APIError(http.StatusUnprocessableEntity, err)
+ case repo_service.IsRepositoryLimitReached(err):
+ ctx.APIError(http.StatusForbidden, err)
+ case errors.Is(err, user_model.ErrBlockedUser):
+ ctx.APIError(http.StatusForbidden, err)
+ default:
+ ctx.APIErrorInternal(err)
}
return
}
@@ -161,12 +159,18 @@ func AcceptTransfer(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
- err := acceptOrRejectRepoTransfer(ctx, true)
- if ctx.Written() {
- return
- }
+ err := repo_service.AcceptTransferOwnership(ctx, ctx.Repo.Repository, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "acceptOrRejectRepoTransfer", err)
+ switch {
+ case repo_model.IsErrNoPendingTransfer(err):
+ ctx.APIError(http.StatusNotFound, err)
+ case errors.Is(err, util.ErrPermissionDenied):
+ ctx.APIError(http.StatusForbidden, err)
+ case repo_service.IsRepositoryLimitReached(err):
+ ctx.APIError(http.StatusForbidden, err)
+ default:
+ ctx.APIErrorInternal(err)
+ }
return
}
@@ -199,40 +203,18 @@ func RejectTransfer(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
- err := acceptOrRejectRepoTransfer(ctx, false)
- if ctx.Written() {
- return
- }
+ err := repo_service.RejectRepositoryTransfer(ctx, ctx.Repo.Repository, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "acceptOrRejectRepoTransfer", err)
+ switch {
+ case repo_model.IsErrNoPendingTransfer(err):
+ ctx.APIError(http.StatusNotFound, err)
+ case errors.Is(err, util.ErrPermissionDenied):
+ ctx.APIError(http.StatusForbidden, err)
+ default:
+ ctx.APIErrorInternal(err)
+ }
return
}
ctx.JSON(http.StatusOK, convert.ToRepo(ctx, ctx.Repo.Repository, ctx.Repo.Permission))
}
-
-func acceptOrRejectRepoTransfer(ctx *context.APIContext, accept bool) error {
- repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository)
- if err != nil {
- if repo_model.IsErrNoPendingTransfer(err) {
- ctx.NotFound()
- return nil
- }
- return err
- }
-
- if err := repoTransfer.LoadAttributes(ctx); err != nil {
- return err
- }
-
- if !repoTransfer.CanUserAcceptTransfer(ctx, ctx.Doer) {
- ctx.Error(http.StatusForbidden, "CanUserAcceptTransfer", nil)
- return fmt.Errorf("user does not have permissions to do this")
- }
-
- if accept {
- return repo_service.TransferOwnership(ctx, repoTransfer.Doer, repoTransfer.Recipient, ctx.Repo.Repository, repoTransfer.Teams)
- }
-
- return repo_service.CancelRepositoryTransfer(ctx, ctx.Repo.Repository)
-}
diff --git a/routers/api/v1/repo/tree.go b/routers/api/v1/repo/tree.go
index 768e5d41c1..dfd69600fb 100644
--- a/routers/api/v1/repo/tree.go
+++ b/routers/api/v1/repo/tree.go
@@ -58,11 +58,11 @@ func GetTree(ctx *context.APIContext) {
sha := ctx.PathParam("sha")
if len(sha) == 0 {
- ctx.Error(http.StatusBadRequest, "", "sha not provided")
+ ctx.APIError(http.StatusBadRequest, "sha not provided")
return
}
if tree, err := files_service.GetTreeBySHA(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, sha, ctx.FormInt("page"), ctx.FormInt("per_page"), ctx.FormBool("recursive")); err != nil {
- ctx.Error(http.StatusBadRequest, "", err.Error())
+ ctx.APIError(http.StatusBadRequest, err.Error())
} else {
ctx.SetTotalCountHeader(int64(tree.TotalCount))
ctx.JSON(http.StatusOK, tree)
diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go
index 352d8f48fc..8e24ffa465 100644
--- a/routers/api/v1/repo/wiki.go
+++ b/routers/api/v1/repo/wiki.go
@@ -59,7 +59,7 @@ func NewWikiPage(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.CreateWikiPageOptions)
if util.IsEmptyString(form.Title) {
- ctx.Error(http.StatusBadRequest, "emptyTitle", nil)
+ ctx.APIError(http.StatusBadRequest, nil)
return
}
@@ -71,18 +71,18 @@ func NewWikiPage(ctx *context.APIContext) {
content, err := base64.StdEncoding.DecodeString(form.ContentBase64)
if err != nil {
- ctx.Error(http.StatusBadRequest, "invalid base64 encoding of content", err)
+ ctx.APIError(http.StatusBadRequest, err)
return
}
form.ContentBase64 = string(content)
if err := wiki_service.AddWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName, form.ContentBase64, form.Message); err != nil {
if repo_model.IsErrWikiReservedName(err) {
- ctx.Error(http.StatusBadRequest, "IsErrWikiReservedName", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else if repo_model.IsErrWikiAlreadyExist(err) {
- ctx.Error(http.StatusBadRequest, "IsErrWikiAlreadyExists", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- ctx.Error(http.StatusInternalServerError, "AddWikiPage", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -149,13 +149,13 @@ func EditWikiPage(ctx *context.APIContext) {
content, err := base64.StdEncoding.DecodeString(form.ContentBase64)
if err != nil {
- ctx.Error(http.StatusBadRequest, "invalid base64 encoding of content", err)
+ ctx.APIError(http.StatusBadRequest, err)
return
}
form.ContentBase64 = string(content)
if err := wiki_service.EditWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, oldWikiName, newWikiName, form.ContentBase64, form.Message); err != nil {
- ctx.Error(http.StatusInternalServerError, "EditWikiPage", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -193,12 +193,12 @@ func getWikiPage(ctx *context.APIContext, wikiName wiki_service.WebPath) *api.Wi
}
// get commit count - wiki revisions
- commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
+ commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
// Get last change information.
lastCommit, err := wikiRepo.GetCommitByPath(pageFilename)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetCommitByPath", err)
+ ctx.APIErrorInternal(err)
return nil
}
@@ -246,10 +246,10 @@ func DeleteWikiPage(ctx *context.APIContext) {
if err := wiki_service.DeleteWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName); err != nil {
if err.Error() == "file does not exist" {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
return
}
- ctx.Error(http.StatusInternalServerError, "DeleteWikiPage", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -298,10 +298,7 @@ func ListWikiPages(ctx *context.APIContext) {
return
}
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
limit := ctx.FormInt("limit")
if limit <= 1 {
limit = setting.API.DefaultPagingNum
@@ -312,7 +309,7 @@ func ListWikiPages(ctx *context.APIContext) {
entries, err := commit.ListEntries()
if err != nil {
- ctx.ServerError("ListEntries", err)
+ ctx.APIErrorInternal(err)
return
}
pages := make([]*api.WikiPageMetaData, 0, len(entries))
@@ -322,7 +319,7 @@ func ListWikiPages(ctx *context.APIContext) {
}
c, err := wikiRepo.GetCommitByPath(entry.Name())
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetCommit", err)
+ ctx.APIErrorInternal(err)
return
}
wikiName, err := wiki_service.GitPathToWebPath(entry.Name())
@@ -330,7 +327,7 @@ func ListWikiPages(ctx *context.APIContext) {
if repo_model.IsErrWikiInvalidFileName(err) {
continue
}
- ctx.Error(http.StatusInternalServerError, "WikiFilenameToName", err)
+ ctx.APIErrorInternal(err)
return
}
pages = append(pages, wiki_service.ToWikiPageMetaData(wikiName, c, ctx.Repo.Repository))
@@ -432,22 +429,19 @@ func ListPageRevisions(ctx *context.APIContext) {
}
// get commit count - wiki revisions
- commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
+ commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
// get Commit Count
commitsHistory, err := wikiRepo.CommitsByFileAndRange(
git.CommitsByFileAndRangeOptions{
- Revision: "master",
+ Revision: ctx.Repo.Repository.DefaultWikiBranch,
File: pageFilename,
Page: page,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "CommitsByFileAndRange", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -476,22 +470,22 @@ func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error)
// findWikiRepoCommit opens the wiki repo and returns the latest commit, writing to context on error.
// The caller is responsible for closing the returned repo again
func findWikiRepoCommit(ctx *context.APIContext) (*git.Repository, *git.Commit) {
- wikiRepo, err := gitrepo.OpenWikiRepository(ctx, ctx.Repo.Repository)
+ wikiRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository.WikiStorageRepo())
if err != nil {
if git.IsErrNotExist(err) || err.Error() == "no such file or directory" {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
+ ctx.APIErrorInternal(err)
}
return nil, nil
}
- commit, err := wikiRepo.GetBranchCommit("master")
+ commit, err := wikiRepo.GetBranchCommit(ctx.Repo.Repository.DefaultWikiBranch)
if err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound(err)
+ ctx.APIErrorNotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetBranchCommit", err)
+ ctx.APIErrorInternal(err)
}
return wikiRepo, nil
}
@@ -505,9 +499,9 @@ func wikiContentsByEntry(ctx *context.APIContext, entry *git.TreeEntry) string {
if blob.Size() > setting.API.DefaultMaxBlobSize {
return ""
}
- content, err := blob.GetBlobContentBase64()
+ content, err := blob.GetBlobContentBase64(nil)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBlobContentBase64", err)
+ ctx.APIErrorInternal(err)
return ""
}
return content
@@ -521,10 +515,10 @@ func wikiContentsByName(ctx *context.APIContext, commit *git.Commit, wikiName wi
if err != nil {
if git.IsErrNotExist(err) {
if !isSidebarOrFooter {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
}
} else {
- ctx.ServerError("findEntryForFile", err)
+ ctx.APIErrorInternal(err)
}
return "", ""
}
diff --git a/routers/api/v1/settings/settings.go b/routers/api/v1/settings/settings.go
index 0ee81b96d5..94fbadeab0 100644
--- a/routers/api/v1/settings/settings.go
+++ b/routers/api/v1/settings/settings.go
@@ -43,6 +43,7 @@ func GetGeneralAPISettings(ctx *context.APIContext) {
DefaultPagingNum: setting.API.DefaultPagingNum,
DefaultGitTreesPerPage: setting.API.DefaultGitTreesPerPage,
DefaultMaxBlobSize: setting.API.DefaultMaxBlobSize,
+ DefaultMaxResponseSize: setting.API.DefaultMaxResponseSize,
})
}
diff --git a/routers/api/v1/shared/action.go b/routers/api/v1/shared/action.go
new file mode 100644
index 0000000000..c97e9419fd
--- /dev/null
+++ b/routers/api/v1/shared/action.go
@@ -0,0 +1,187 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package shared
+
+import (
+ "fmt"
+ "net/http"
+
+ actions_model "code.gitea.io/gitea/models/actions"
+ "code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
+ api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/webhook"
+ "code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/convert"
+)
+
+// ListJobs lists jobs for api route validated ownerID and repoID
+// ownerID == 0 and repoID == 0 means all jobs
+// ownerID == 0 and repoID != 0 means all jobs for the given repo
+// ownerID != 0 and repoID == 0 means all jobs for the given user/org
+// ownerID != 0 and repoID != 0 undefined behavior
+// runID == 0 means all jobs
+// runID is used as an additional filter together with ownerID and repoID to only return jobs for the given run
+// Access rights are checked at the API route level
+func ListJobs(ctx *context.APIContext, ownerID, repoID, runID int64) {
+ if ownerID != 0 && repoID != 0 {
+ setting.PanicInDevOrTesting("ownerID and repoID should not be both set")
+ }
+ opts := actions_model.FindRunJobOptions{
+ OwnerID: ownerID,
+ RepoID: repoID,
+ RunID: runID,
+ ListOptions: utils.GetListOptions(ctx),
+ }
+ for _, status := range ctx.FormStrings("status") {
+ values, err := convertToInternal(status)
+ if err != nil {
+ ctx.APIError(http.StatusBadRequest, fmt.Errorf("Invalid status %s", status))
+ return
+ }
+ opts.Statuses = append(opts.Statuses, values...)
+ }
+
+ jobs, total, err := db.FindAndCount[actions_model.ActionRunJob](ctx, opts)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+
+ res := new(api.ActionWorkflowJobsResponse)
+ res.TotalCount = total
+
+ res.Entries = make([]*api.ActionWorkflowJob, len(jobs))
+
+ isRepoLevel := repoID != 0 && ctx.Repo != nil && ctx.Repo.Repository != nil && ctx.Repo.Repository.ID == repoID
+ for i := range jobs {
+ var repository *repo_model.Repository
+ if isRepoLevel {
+ repository = ctx.Repo.Repository
+ } else {
+ repository, err = repo_model.GetRepositoryByID(ctx, jobs[i].RepoID)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ }
+
+ convertedWorkflowJob, err := convert.ToActionWorkflowJob(ctx, repository, nil, jobs[i])
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ res.Entries[i] = convertedWorkflowJob
+ }
+
+ ctx.JSON(http.StatusOK, &res)
+}
+
+func convertToInternal(s string) ([]actions_model.Status, error) {
+ switch s {
+ case "pending", "waiting", "requested", "action_required":
+ return []actions_model.Status{actions_model.StatusBlocked}, nil
+ case "queued":
+ return []actions_model.Status{actions_model.StatusWaiting}, nil
+ case "in_progress":
+ return []actions_model.Status{actions_model.StatusRunning}, nil
+ case "completed":
+ return []actions_model.Status{
+ actions_model.StatusSuccess,
+ actions_model.StatusFailure,
+ actions_model.StatusSkipped,
+ actions_model.StatusCancelled,
+ }, nil
+ case "failure":
+ return []actions_model.Status{actions_model.StatusFailure}, nil
+ case "success":
+ return []actions_model.Status{actions_model.StatusSuccess}, nil
+ case "skipped", "neutral":
+ return []actions_model.Status{actions_model.StatusSkipped}, nil
+ case "cancelled", "timed_out":
+ return []actions_model.Status{actions_model.StatusCancelled}, nil
+ default:
+ return nil, fmt.Errorf("invalid status %s", s)
+ }
+}
+
+// ListRuns lists jobs for api route validated ownerID and repoID
+// ownerID == 0 and repoID == 0 means all runs
+// ownerID == 0 and repoID != 0 means all runs for the given repo
+// ownerID != 0 and repoID == 0 means all runs for the given user/org
+// ownerID != 0 and repoID != 0 undefined behavior
+// Access rights are checked at the API route level
+func ListRuns(ctx *context.APIContext, ownerID, repoID int64) {
+ if ownerID != 0 && repoID != 0 {
+ setting.PanicInDevOrTesting("ownerID and repoID should not be both set")
+ }
+ opts := actions_model.FindRunOptions{
+ OwnerID: ownerID,
+ RepoID: repoID,
+ ListOptions: utils.GetListOptions(ctx),
+ }
+
+ if event := ctx.FormString("event"); event != "" {
+ opts.TriggerEvent = webhook.HookEventType(event)
+ }
+ if branch := ctx.FormString("branch"); branch != "" {
+ opts.Ref = string(git.RefNameFromBranch(branch))
+ }
+ for _, status := range ctx.FormStrings("status") {
+ values, err := convertToInternal(status)
+ if err != nil {
+ ctx.APIError(http.StatusBadRequest, fmt.Errorf("Invalid status %s", status))
+ return
+ }
+ opts.Status = append(opts.Status, values...)
+ }
+ if actor := ctx.FormString("actor"); actor != "" {
+ user, err := user_model.GetUserByName(ctx, actor)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ opts.TriggerUserID = user.ID
+ }
+ if headSHA := ctx.FormString("head_sha"); headSHA != "" {
+ opts.CommitSHA = headSHA
+ }
+
+ runs, total, err := db.FindAndCount[actions_model.ActionRun](ctx, opts)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+
+ res := new(api.ActionWorkflowRunsResponse)
+ res.TotalCount = total
+
+ res.Entries = make([]*api.ActionWorkflowRun, len(runs))
+ isRepoLevel := repoID != 0 && ctx.Repo != nil && ctx.Repo.Repository != nil && ctx.Repo.Repository.ID == repoID
+ for i := range runs {
+ var repository *repo_model.Repository
+ if isRepoLevel {
+ repository = ctx.Repo.Repository
+ } else {
+ repository, err = repo_model.GetRepositoryByID(ctx, runs[i].RepoID)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ }
+
+ convertedRun, err := convert.ToActionWorkflowRun(ctx, repository, runs[i])
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ res.Entries[i] = convertedRun
+ }
+
+ ctx.JSON(http.StatusOK, &res)
+}
diff --git a/routers/api/v1/shared/block.go b/routers/api/v1/shared/block.go
index 490a48f81c..b22f8a74fd 100644
--- a/routers/api/v1/shared/block.go
+++ b/routers/api/v1/shared/block.go
@@ -21,12 +21,12 @@ func ListBlocks(ctx *context.APIContext, blocker *user_model.User) {
BlockerID: blocker.ID,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindBlockings", err)
+ ctx.APIErrorInternal(err)
return
}
if err := user_model.BlockingList(blocks).LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -42,14 +42,14 @@ func ListBlocks(ctx *context.APIContext, blocker *user_model.User) {
func CheckUserBlock(ctx *context.APIContext, blocker *user_model.User) {
blockee, err := user_model.GetUserByName(ctx, ctx.PathParam("username"))
if err != nil {
- ctx.NotFound("GetUserByName", err)
+ ctx.APIErrorNotFound("GetUserByName", err)
return
}
status := http.StatusNotFound
blocking, err := user_model.GetBlocking(ctx, blocker.ID, blockee.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetBlocking", err)
+ ctx.APIErrorInternal(err)
return
}
if blocking != nil {
@@ -62,15 +62,15 @@ func CheckUserBlock(ctx *context.APIContext, blocker *user_model.User) {
func BlockUser(ctx *context.APIContext, blocker *user_model.User) {
blockee, err := user_model.GetUserByName(ctx, ctx.PathParam("username"))
if err != nil {
- ctx.NotFound("GetUserByName", err)
+ ctx.APIErrorNotFound("GetUserByName", err)
return
}
if err := user_service.BlockUser(ctx, ctx.Doer, blocker, blockee, ctx.FormString("note")); err != nil {
if errors.Is(err, user_model.ErrCanNotBlock) || errors.Is(err, user_model.ErrBlockOrganization) {
- ctx.Error(http.StatusBadRequest, "BlockUser", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- ctx.Error(http.StatusInternalServerError, "BlockUser", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -81,15 +81,15 @@ func BlockUser(ctx *context.APIContext, blocker *user_model.User) {
func UnblockUser(ctx *context.APIContext, doer, blocker *user_model.User) {
blockee, err := user_model.GetUserByName(ctx, ctx.PathParam("username"))
if err != nil {
- ctx.NotFound("GetUserByName", err)
+ ctx.APIErrorNotFound("GetUserByName", err)
return
}
if err := user_service.UnblockUser(ctx, doer, blocker, blockee); err != nil {
if errors.Is(err, user_model.ErrCanNotUnblock) || errors.Is(err, user_model.ErrBlockOrganization) {
- ctx.Error(http.StatusBadRequest, "UnblockUser", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- ctx.Error(http.StatusInternalServerError, "UnblockUser", err)
+ ctx.APIErrorInternal(err)
}
return
}
diff --git a/routers/api/v1/shared/runners.go b/routers/api/v1/shared/runners.go
index f088e9a2d4..e9834aff9f 100644
--- a/routers/api/v1/shared/runners.go
+++ b/routers/api/v1/shared/runners.go
@@ -8,8 +8,13 @@ import (
"net/http"
actions_model "code.gitea.io/gitea/models/actions"
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/setting"
+ api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/routers/api/v1/utils"
"code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/convert"
)
// RegistrationToken is response related to registration token
@@ -24,9 +29,99 @@ func GetRegistrationToken(ctx *context.APIContext, ownerID, repoID int64) {
token, err = actions_model.NewRunnerToken(ctx, ownerID, repoID)
}
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, RegistrationToken{Token: token.Token})
}
+
+// ListRunners lists runners for api route validated ownerID and repoID
+// ownerID == 0 and repoID == 0 means all runners including global runners, does not appear in sql where clause
+// ownerID == 0 and repoID != 0 means all runners for the given repo
+// ownerID != 0 and repoID == 0 means all runners for the given user/org
+// ownerID != 0 and repoID != 0 undefined behavior
+// Access rights are checked at the API route level
+func ListRunners(ctx *context.APIContext, ownerID, repoID int64) {
+ if ownerID != 0 && repoID != 0 {
+ setting.PanicInDevOrTesting("ownerID and repoID should not be both set")
+ }
+ runners, total, err := db.FindAndCount[actions_model.ActionRunner](ctx, &actions_model.FindRunnerOptions{
+ OwnerID: ownerID,
+ RepoID: repoID,
+ ListOptions: utils.GetListOptions(ctx),
+ })
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+
+ res := new(api.ActionRunnersResponse)
+ res.TotalCount = total
+
+ res.Entries = make([]*api.ActionRunner, len(runners))
+ for i, runner := range runners {
+ res.Entries[i] = convert.ToActionRunner(ctx, runner)
+ }
+
+ ctx.JSON(http.StatusOK, &res)
+}
+
+func getRunnerByID(ctx *context.APIContext, ownerID, repoID, runnerID int64) (*actions_model.ActionRunner, bool) {
+ if ownerID != 0 && repoID != 0 {
+ setting.PanicInDevOrTesting("ownerID and repoID should not be both set")
+ }
+
+ runner, err := actions_model.GetRunnerByID(ctx, runnerID)
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.APIErrorNotFound("Runner not found")
+ } else {
+ ctx.APIErrorInternal(err)
+ }
+ return nil, false
+ }
+
+ if !runner.EditableInContext(ownerID, repoID) {
+ ctx.APIErrorNotFound("No permission to access this runner")
+ return nil, false
+ }
+ return runner, true
+}
+
+// GetRunner get the runner for api route validated ownerID and repoID
+// ownerID == 0 and repoID == 0 means any runner including global runners
+// ownerID == 0 and repoID != 0 means any runner for the given repo
+// ownerID != 0 and repoID == 0 means any runner for the given user/org
+// ownerID != 0 and repoID != 0 undefined behavior
+// Access rights are checked at the API route level
+func GetRunner(ctx *context.APIContext, ownerID, repoID, runnerID int64) {
+ if ownerID != 0 && repoID != 0 {
+ setting.PanicInDevOrTesting("ownerID and repoID should not be both set")
+ }
+ runner, ok := getRunnerByID(ctx, ownerID, repoID, runnerID)
+ if !ok {
+ return
+ }
+ ctx.JSON(http.StatusOK, convert.ToActionRunner(ctx, runner))
+}
+
+// DeleteRunner deletes the runner for api route validated ownerID and repoID
+// ownerID == 0 and repoID == 0 means any runner including global runners
+// ownerID == 0 and repoID != 0 means any runner for the given repo
+// ownerID != 0 and repoID == 0 means any runner for the given user/org
+// ownerID != 0 and repoID != 0 undefined behavior
+// Access rights are checked at the API route level
+func DeleteRunner(ctx *context.APIContext, ownerID, repoID, runnerID int64) {
+ runner, ok := getRunnerByID(ctx, ownerID, repoID, runnerID)
+ if !ok {
+ return
+ }
+
+ err := actions_model.DeleteRunner(ctx, runner.ID)
+ if err != nil {
+ ctx.APIErrorInternal(err)
+ return
+ }
+ ctx.Status(http.StatusNoContent)
+}
diff --git a/routers/api/v1/swagger/action.go b/routers/api/v1/swagger/action.go
index 665f4d0b85..16a250184a 100644
--- a/routers/api/v1/swagger/action.go
+++ b/routers/api/v1/swagger/action.go
@@ -32,3 +32,17 @@ type swaggerResponseVariableList struct {
// in:body
Body []api.ActionVariable `json:"body"`
}
+
+// ActionWorkflow
+// swagger:response ActionWorkflow
+type swaggerResponseActionWorkflow struct {
+ // in:body
+ Body api.ActionWorkflow `json:"body"`
+}
+
+// ActionWorkflowList
+// swagger:response ActionWorkflowList
+type swaggerResponseActionWorkflowList struct {
+ // in:body
+ Body []api.ActionWorkflow `json:"body"`
+}
diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go
index 125605d98f..bafd5e04a2 100644
--- a/routers/api/v1/swagger/options.go
+++ b/routers/api/v1/swagger/options.go
@@ -119,6 +119,9 @@ type swaggerParameterBodies struct {
EditAttachmentOptions api.EditAttachmentOptions
// in:body
+ GetFilesOptions api.GetFilesOptions
+
+ // in:body
ChangeFilesOptions api.ChangeFilesOptions
// in:body
@@ -209,5 +212,14 @@ type swaggerParameterBodies struct {
CreateVariableOption api.CreateVariableOption
// in:body
+ RenameOrgOption api.RenameOrgOption
+
+ // in:body
+ CreateActionWorkflowDispatch api.CreateActionWorkflowDispatch
+
+ // in:body
UpdateVariableOption api.UpdateVariableOption
+
+ // in:body
+ LockIssueOption api.LockIssueOption
}
diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go
index b9d2a0217c..9e20c0533b 100644
--- a/routers/api/v1/swagger/repo.go
+++ b/routers/api/v1/swagger/repo.go
@@ -331,6 +331,12 @@ type swaggerContentsListResponse struct {
Body []api.ContentsResponse `json:"body"`
}
+// swagger:response ContentsExtResponse
+type swaggerContentsExtResponse struct {
+ // in:body
+ Body api.ContentsExtResponse `json:"body"`
+}
+
// FileDeleteResponse
// swagger:response FileDeleteResponse
type swaggerFileDeleteResponse struct {
@@ -443,8 +449,76 @@ type swaggerRepoTasksList struct {
Body api.ActionTaskResponse `json:"body"`
}
+// WorkflowRunsList
+// swagger:response WorkflowRunsList
+type swaggerActionWorkflowRunsResponse struct {
+ // in:body
+ Body api.ActionWorkflowRunsResponse `json:"body"`
+}
+
+// WorkflowRun
+// swagger:response WorkflowRun
+type swaggerWorkflowRun struct {
+ // in:body
+ Body api.ActionWorkflowRun `json:"body"`
+}
+
+// WorkflowJobsList
+// swagger:response WorkflowJobsList
+type swaggerActionWorkflowJobsResponse struct {
+ // in:body
+ Body api.ActionWorkflowJobsResponse `json:"body"`
+}
+
+// WorkflowJob
+// swagger:response WorkflowJob
+type swaggerWorkflowJob struct {
+ // in:body
+ Body api.ActionWorkflowJob `json:"body"`
+}
+
+// ArtifactsList
+// swagger:response ArtifactsList
+type swaggerRepoArtifactsList struct {
+ // in:body
+ Body api.ActionArtifactsResponse `json:"body"`
+}
+
+// Artifact
+// swagger:response Artifact
+type swaggerRepoArtifact struct {
+ // in:body
+ Body api.ActionArtifact `json:"body"`
+}
+
+// RunnerList
+// swagger:response RunnerList
+type swaggerRunnerList struct {
+ // in:body
+ Body api.ActionRunnersResponse `json:"body"`
+}
+
+// Runner
+// swagger:response Runner
+type swaggerRunner struct {
+ // in:body
+ Body api.ActionRunner `json:"body"`
+}
+
// swagger:response Compare
type swaggerCompare struct {
// in:body
Body api.Compare `json:"body"`
}
+
+// swagger:response MergeUpstreamRequest
+type swaggerMergeUpstreamRequest struct {
+ // in:body
+ Body api.MergeUpstreamRequest `json:"body"`
+}
+
+// swagger:response MergeUpstreamResponse
+type swaggerMergeUpstreamResponse struct {
+ // in:body
+ Body api.MergeUpstreamResponse `json:"body"`
+}
diff --git a/routers/api/v1/user/action.go b/routers/api/v1/user/action.go
index 22707196f4..e934d02aa7 100644
--- a/routers/api/v1/user/action.go
+++ b/routers/api/v1/user/action.go
@@ -12,6 +12,7 @@ import (
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/routers/api/v1/shared"
"code.gitea.io/gitea/routers/api/v1/utils"
actions_service "code.gitea.io/gitea/services/actions"
"code.gitea.io/gitea/services/context"
@@ -49,14 +50,14 @@ func CreateOrUpdateSecret(ctx *context.APIContext) {
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
- _, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Doer.ID, 0, ctx.PathParam("secretname"), opt.Data)
+ _, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Doer.ID, 0, ctx.PathParam("secretname"), opt.Data, opt.Description)
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "CreateOrUpdateSecret", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -94,11 +95,11 @@ func DeleteSecret(ctx *context.APIContext) {
err := secret_service.DeleteSecretByName(ctx, ctx.Doer.ID, 0, ctx.PathParam("secretname"))
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "DeleteSecret", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "DeleteSecret", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteSecret", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -127,13 +128,11 @@ func CreateVariable(ctx *context.APIContext) {
// "$ref": "#/definitions/CreateVariableOption"
// responses:
// "201":
- // description: response when creating a variable
- // "204":
- // description: response when creating a variable
+ // description: successfully created the user-level variable
// "400":
// "$ref": "#/responses/error"
- // "404":
- // "$ref": "#/responses/notFound"
+ // "409":
+ // description: variable name already exists.
opt := web.GetForm(ctx).(*api.CreateVariableOption)
@@ -145,24 +144,24 @@ func CreateVariable(ctx *context.APIContext) {
Name: variableName,
})
if err != nil && !errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusInternalServerError, "GetVariable", err)
+ ctx.APIErrorInternal(err)
return
}
if v != nil && v.ID > 0 {
- ctx.Error(http.StatusConflict, "VariableNameAlreadyExists", util.NewAlreadyExistErrorf("variable name %s already exists", variableName))
+ ctx.APIError(http.StatusConflict, util.NewAlreadyExistErrorf("variable name %s already exists", variableName))
return
}
- if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value); err != nil {
+ if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value, opt.Description); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "CreateVariable", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- ctx.Error(http.StatusInternalServerError, "CreateVariable", err)
+ ctx.APIErrorInternal(err)
}
return
}
- ctx.Status(http.StatusNoContent)
+ ctx.Status(http.StatusCreated)
}
// UpdateVariable update a user-level variable which is created by current doer
@@ -202,9 +201,9 @@ func UpdateVariable(ctx *context.APIContext) {
})
if err != nil {
if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "GetVariable", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetVariable", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -212,11 +211,16 @@ func UpdateVariable(ctx *context.APIContext) {
if opt.Name == "" {
opt.Name = ctx.PathParam("variablename")
}
- if _, err := actions_service.UpdateVariable(ctx, v.ID, opt.Name, opt.Value); err != nil {
+
+ v.Name = opt.Name
+ v.Data = opt.Value
+ v.Description = opt.Description
+
+ if _, err := actions_service.UpdateVariableNameData(ctx, v); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "UpdateVariable", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else {
- ctx.Error(http.StatusInternalServerError, "UpdateVariable", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -249,11 +253,11 @@ func DeleteVariable(ctx *context.APIContext) {
if err := actions_service.DeleteVariableByName(ctx, ctx.Doer.ID, 0, ctx.PathParam("variablename")); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusBadRequest, "DeleteVariableByName", err)
+ ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "DeleteVariableByName", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteVariableByName", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -288,18 +292,19 @@ func GetVariable(ctx *context.APIContext) {
})
if err != nil {
if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, "GetVariable", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetVariable", err)
+ ctx.APIErrorInternal(err)
}
return
}
variable := &api.ActionVariable{
- OwnerID: v.OwnerID,
- RepoID: v.RepoID,
- Name: v.Name,
- Data: v.Data,
+ OwnerID: v.OwnerID,
+ RepoID: v.RepoID,
+ Name: v.Name,
+ Data: v.Data,
+ Description: v.Description,
}
ctx.JSON(http.StatusOK, variable)
@@ -334,20 +339,104 @@ func ListVariables(ctx *context.APIContext) {
ListOptions: utils.GetListOptions(ctx),
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindVariables", err)
+ ctx.APIErrorInternal(err)
return
}
variables := make([]*api.ActionVariable, len(vars))
for i, v := range vars {
variables[i] = &api.ActionVariable{
- OwnerID: v.OwnerID,
- RepoID: v.RepoID,
- Name: v.Name,
- Data: v.Data,
+ OwnerID: v.OwnerID,
+ RepoID: v.RepoID,
+ Name: v.Name,
+ Data: v.Data,
+ Description: v.Description,
}
}
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, variables)
}
+
+// ListWorkflowRuns lists workflow runs
+func ListWorkflowRuns(ctx *context.APIContext) {
+ // swagger:operation GET /user/actions/runs user getUserWorkflowRuns
+ // ---
+ // summary: Get workflow runs
+ // parameters:
+ // - name: event
+ // in: query
+ // description: workflow event name
+ // type: string
+ // required: false
+ // - name: branch
+ // in: query
+ // description: workflow branch
+ // type: string
+ // required: false
+ // - name: status
+ // in: query
+ // description: workflow status (pending, queued, in_progress, failure, success, skipped)
+ // type: string
+ // required: false
+ // - name: actor
+ // in: query
+ // description: triggered by user
+ // type: string
+ // required: false
+ // - name: head_sha
+ // in: query
+ // description: triggering sha of the workflow run
+ // type: string
+ // required: false
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results
+ // type: integer
+ // produces:
+ // - application/json
+ // responses:
+ // "200":
+ // "$ref": "#/responses/WorkflowRunsList"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.ListRuns(ctx, ctx.Doer.ID, 0)
+}
+
+// ListWorkflowJobs lists workflow jobs
+func ListWorkflowJobs(ctx *context.APIContext) {
+ // swagger:operation GET /user/actions/jobs user getUserWorkflowJobs
+ // ---
+ // summary: Get workflow jobs
+ // parameters:
+ // - name: status
+ // in: query
+ // description: workflow status (pending, queued, in_progress, failure, success, skipped)
+ // type: string
+ // required: false
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results
+ // type: integer
+ // produces:
+ // - application/json
+ // responses:
+ // "200":
+ // "$ref": "#/responses/WorkflowJobsList"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ shared.ListJobs(ctx, ctx.Doer.ID, 0, 0)
+}
diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go
index bfbc2ba622..6f1053e7ac 100644
--- a/routers/api/v1/user/app.go
+++ b/routers/api/v1/user/app.go
@@ -30,7 +30,7 @@ func ListAccessTokens(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of to user whose access tokens are to be listed
// type: string
// required: true
// - name: page
@@ -51,7 +51,7 @@ func ListAccessTokens(ctx *context.APIContext) {
tokens, count, err := db.FindAndCount[auth_model.AccessToken](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -62,6 +62,8 @@ func ListAccessTokens(ctx *context.APIContext) {
Name: tokens[i].Name,
TokenLastEight: tokens[i].TokenLastEight,
Scopes: tokens[i].Scope.StringSlice(),
+ Created: tokens[i].CreatedUnix.AsTime(),
+ Updated: tokens[i].UpdatedUnix.AsTime(),
}
}
@@ -81,7 +83,7 @@ func CreateAccessToken(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user whose token is to be created
// required: true
// type: string
// - name: body
@@ -105,27 +107,27 @@ func CreateAccessToken(ctx *context.APIContext) {
exist, err := auth_model.AccessTokenByNameExists(ctx, t)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
if exist {
- ctx.Error(http.StatusBadRequest, "AccessTokenByNameExists", errors.New("access token name has been used already"))
+ ctx.APIError(http.StatusBadRequest, errors.New("access token name has been used already"))
return
}
scope, err := auth_model.AccessTokenScope(strings.Join(form.Scopes, ",")).Normalize()
if err != nil {
- ctx.Error(http.StatusBadRequest, "AccessTokenScope.Normalize", fmt.Errorf("invalid access token scope provided: %w", err))
+ ctx.APIError(http.StatusBadRequest, fmt.Errorf("invalid access token scope provided: %w", err))
return
}
if scope == "" {
- ctx.Error(http.StatusBadRequest, "AccessTokenScope", "access token must have a scope")
+ ctx.APIError(http.StatusBadRequest, "access token must have a scope")
return
}
t.Scope = scope
if err := auth_model.NewAccessToken(ctx, t); err != nil {
- ctx.Error(http.StatusInternalServerError, "NewAccessToken", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusCreated, &api.AccessToken{
@@ -147,7 +149,7 @@ func DeleteAccessToken(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user whose token is to be deleted
// type: string
// required: true
// - name: token
@@ -174,31 +176,31 @@ func DeleteAccessToken(ctx *context.APIContext) {
UserID: ctx.ContextUser.ID,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ListAccessTokens", err)
+ ctx.APIErrorInternal(err)
return
}
switch len(tokens) {
case 0:
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
case 1:
tokenID = tokens[0].ID
default:
- ctx.Error(http.StatusUnprocessableEntity, "DeleteAccessTokenByID", fmt.Errorf("multiple matches for token name '%s'", token))
+ ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("multiple matches for token name '%s'", token))
return
}
}
if tokenID == 0 {
- ctx.Error(http.StatusInternalServerError, "Invalid TokenID", nil)
+ ctx.APIErrorInternal(nil)
return
}
if err := auth_model.DeleteAccessTokenByID(ctx, tokenID, ctx.ContextUser.ID); err != nil {
if auth_model.IsErrAccessTokenNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteAccessTokenByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -235,12 +237,12 @@ func CreateOauth2Application(ctx *context.APIContext) {
SkipSecondaryAuthorization: data.SkipSecondaryAuthorization,
})
if err != nil {
- ctx.Error(http.StatusBadRequest, "", "error creating oauth2 application")
+ ctx.APIError(http.StatusBadRequest, "error creating oauth2 application")
return
}
secret, err := app.GenerateClientSecret(ctx)
if err != nil {
- ctx.Error(http.StatusBadRequest, "", "error creating application secret")
+ ctx.APIError(http.StatusBadRequest, "error creating application secret")
return
}
app.ClientSecret = secret
@@ -273,7 +275,7 @@ func ListOauth2Applications(ctx *context.APIContext) {
OwnerID: ctx.Doer.ID,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ListOAuth2Applications", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -309,9 +311,9 @@ func DeleteOauth2Application(ctx *context.APIContext) {
appID := ctx.PathParamInt64("id")
if err := auth_model.DeleteOAuth2Application(ctx, appID, ctx.Doer.ID); err != nil {
if auth_model.IsErrOAuthApplicationNotFound(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteOauth2ApplicationByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -342,14 +344,14 @@ func GetOauth2Application(ctx *context.APIContext) {
app, err := auth_model.GetOAuth2ApplicationByID(ctx, appID)
if err != nil {
if auth_model.IsErrOauthClientIDInvalid(err) || auth_model.IsErrOAuthApplicationNotFound(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetOauth2ApplicationByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
if app.UID != ctx.Doer.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -396,15 +398,15 @@ func UpdateOauth2Application(ctx *context.APIContext) {
})
if err != nil {
if auth_model.IsErrOauthClientIDInvalid(err) || auth_model.IsErrOAuthApplicationNotFound(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "UpdateOauth2ApplicationByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
app.ClientSecret, err = app.GenerateClientSecret(ctx)
if err != nil {
- ctx.Error(http.StatusBadRequest, "", "error updating application secret")
+ ctx.APIError(http.StatusBadRequest, "error updating application secret")
return
}
diff --git a/routers/api/v1/user/avatar.go b/routers/api/v1/user/avatar.go
index 30ccb63587..9c7bd57bc0 100644
--- a/routers/api/v1/user/avatar.go
+++ b/routers/api/v1/user/avatar.go
@@ -32,13 +32,13 @@ func UpdateAvatar(ctx *context.APIContext) {
content, err := base64.StdEncoding.DecodeString(form.Image)
if err != nil {
- ctx.Error(http.StatusBadRequest, "DecodeImage", err)
+ ctx.APIError(http.StatusBadRequest, err)
return
}
err = user_service.UploadAvatar(ctx, ctx.Doer, content)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "UploadAvatar", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -57,7 +57,7 @@ func DeleteAvatar(ctx *context.APIContext) {
// "$ref": "#/responses/empty"
err := user_service.DeleteAvatar(ctx, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/user/block.go b/routers/api/v1/user/block.go
index 7231e9add7..8365188f60 100644
--- a/routers/api/v1/user/block.go
+++ b/routers/api/v1/user/block.go
@@ -37,7 +37,7 @@ func CheckUserBlock(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: user to check
+ // description: username of the user to check
// type: string
// required: true
// responses:
@@ -56,7 +56,7 @@ func BlockUser(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: user to block
+ // description: username of the user to block
// type: string
// required: true
// - name: note
@@ -81,7 +81,7 @@ func UnblockUser(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: user to unblock
+ // description: username of the user to unblock
// type: string
// required: true
// responses:
diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go
index 33aa851a80..055e5ea419 100644
--- a/routers/api/v1/user/email.go
+++ b/routers/api/v1/user/email.go
@@ -29,7 +29,7 @@ func ListEmails(ctx *context.APIContext) {
emails, err := user_model.GetEmailAddresses(ctx, ctx.Doer.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetEmailAddresses", err)
+ ctx.APIErrorInternal(err)
return
}
apiEmails := make([]*api.Email, len(emails))
@@ -59,13 +59,13 @@ func AddEmail(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.CreateEmailOption)
if len(form.Emails) == 0 {
- ctx.Error(http.StatusUnprocessableEntity, "", "Email list empty")
+ ctx.APIError(http.StatusUnprocessableEntity, "Email list empty")
return
}
if err := user_service.AddEmailAddresses(ctx, ctx.Doer, form.Emails); err != nil {
if user_model.IsErrEmailAlreadyUsed(err) {
- ctx.Error(http.StatusUnprocessableEntity, "", "Email address has been used: "+err.(user_model.ErrEmailAlreadyUsed).Email)
+ ctx.APIError(http.StatusUnprocessableEntity, "Email address has been used: "+err.(user_model.ErrEmailAlreadyUsed).Email)
} else if user_model.IsErrEmailCharIsNotSupported(err) || user_model.IsErrEmailInvalid(err) {
email := ""
if typedError, ok := err.(user_model.ErrEmailInvalid); ok {
@@ -76,16 +76,16 @@ func AddEmail(ctx *context.APIContext) {
}
errMsg := fmt.Sprintf("Email address %q invalid", email)
- ctx.Error(http.StatusUnprocessableEntity, "", errMsg)
+ ctx.APIError(http.StatusUnprocessableEntity, errMsg)
} else {
- ctx.Error(http.StatusInternalServerError, "AddEmailAddresses", err)
+ ctx.APIErrorInternal(err)
}
return
}
emails, err := user_model.GetEmailAddresses(ctx, ctx.Doer.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetEmailAddresses", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -122,9 +122,9 @@ func DeleteEmail(ctx *context.APIContext) {
if err := user_service.DeleteEmailAddresses(ctx, ctx.Doer, form.Emails); err != nil {
if user_model.IsErrEmailAddressNotExist(err) {
- ctx.Error(http.StatusNotFound, "DeleteEmailAddresses", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteEmailAddresses", err)
+ ctx.APIErrorInternal(err)
}
return
}
diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go
index 8f46808f9e..339b994af4 100644
--- a/routers/api/v1/user/follower.go
+++ b/routers/api/v1/user/follower.go
@@ -26,7 +26,7 @@ func responseAPIUsers(ctx *context.APIContext, users []*user_model.User) {
func listUserFollowers(ctx *context.APIContext, u *user_model.User) {
users, count, err := user_model.GetUserFollowers(ctx, u, ctx.Doer, utils.GetListOptions(ctx))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -67,7 +67,7 @@ func ListFollowers(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user whose followers are to be listed
// type: string
// required: true
// - name: page
@@ -90,7 +90,7 @@ func ListFollowers(ctx *context.APIContext) {
func listUserFollowing(ctx *context.APIContext, u *user_model.User) {
users, count, err := user_model.GetUserFollowing(ctx, u, ctx.Doer, utils.GetListOptions(ctx))
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserFollowing", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -131,7 +131,7 @@ func ListFollowing(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user whose followed users are to be listed
// type: string
// required: true
// - name: page
@@ -155,7 +155,7 @@ func checkUserFollowing(ctx *context.APIContext, u *user_model.User, followID in
if user_model.IsFollowing(ctx, u.ID, followID) {
ctx.Status(http.StatusNoContent)
} else {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
}
}
@@ -167,7 +167,7 @@ func CheckMyFollowing(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of followed user
+ // description: username of the user to check for authenticated followers
// type: string
// required: true
// responses:
@@ -187,12 +187,12 @@ func CheckFollowing(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of following user
+ // description: username of the following user
// type: string
// required: true
// - name: target
// in: path
- // description: username of followed user
+ // description: username of the followed user
// type: string
// required: true
// responses:
@@ -216,7 +216,7 @@ func Follow(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user to follow
+ // description: username of the user to follow
// type: string
// required: true
// responses:
@@ -229,9 +229,9 @@ func Follow(ctx *context.APIContext) {
if err := user_model.FollowUser(ctx, ctx.Doer, ctx.ContextUser); err != nil {
if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, "FollowUser", err)
+ ctx.APIError(http.StatusForbidden, err)
} else {
- ctx.Error(http.StatusInternalServerError, "FollowUser", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -246,7 +246,7 @@ func Unfollow(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user to unfollow
+ // description: username of the user to unfollow
// type: string
// required: true
// responses:
@@ -256,7 +256,7 @@ func Unfollow(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if err := user_model.UnfollowUser(ctx, ctx.Doer.ID, ctx.ContextUser.ID); err != nil {
- ctx.Error(http.StatusInternalServerError, "UnfollowUser", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go
index ef667a1883..9ec4d2c938 100644
--- a/routers/api/v1/user/gpg_key.go
+++ b/routers/api/v1/user/gpg_key.go
@@ -4,7 +4,7 @@
package user
import (
- "fmt"
+ "errors"
"net/http"
"strings"
@@ -25,12 +25,12 @@ func listGPGKeys(ctx *context.APIContext, uid int64, listOptions db.ListOptions)
OwnerID: uid,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ListGPGKeys", err)
+ ctx.APIErrorInternal(err)
return
}
if err := asymkey_model.GPGKeyList(keys).LoadSubKeys(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "ListGPGKeys", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -53,7 +53,7 @@ func ListGPGKeys(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user whose GPG key list is to be obtained
// type: string
// required: true
// - name: page
@@ -119,14 +119,14 @@ func GetGPGKey(ctx *context.APIContext) {
key, err := asymkey_model.GetGPGKeyForUserByID(ctx, ctx.Doer.ID, ctx.PathParamInt64("id"))
if err != nil {
if asymkey_model.IsErrGPGKeyNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetGPGKeyByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
if err := key.LoadSubKeys(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadSubKeys", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToGPGKey(key))
@@ -135,7 +135,7 @@ func GetGPGKey(ctx *context.APIContext) {
// CreateUserGPGKey creates new GPG key to given user by ID.
func CreateUserGPGKey(ctx *context.APIContext, form api.CreateGPGKeyOption, uid int64) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited"))
+ ctx.APIErrorNotFound("Not Found", errors.New("gpg keys setting is not allowed to be visited"))
return
}
@@ -194,7 +194,7 @@ func VerifyUserGPGKey(ctx *context.APIContext) {
form.KeyID = strings.TrimLeft(form.KeyID, "0")
if form.KeyID == "" {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
@@ -205,10 +205,10 @@ func VerifyUserGPGKey(ctx *context.APIContext) {
if err != nil {
if asymkey_model.IsErrGPGInvalidTokenSignature(err) {
- ctx.Error(http.StatusUnprocessableEntity, "GPGInvalidSignature", fmt.Sprintf("The provided GPG key, signature and token do not match or token is out of date. Provide a valid signature for the token: %s", token))
+ ctx.APIError(http.StatusUnprocessableEntity, "The provided GPG key, signature and token do not match or token is out of date. Provide a valid signature for the token: "+token)
return
}
- ctx.Error(http.StatusInternalServerError, "VerifyUserGPGKey", err)
+ ctx.APIErrorInternal(err)
}
keys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
@@ -217,9 +217,9 @@ func VerifyUserGPGKey(ctx *context.APIContext) {
})
if err != nil {
if asymkey_model.IsErrGPGKeyNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetGPGKeysByKeyID", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -276,15 +276,15 @@ func DeleteGPGKey(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited"))
+ ctx.APIErrorNotFound("Not Found", errors.New("gpg keys setting is not allowed to be visited"))
return
}
if err := asymkey_model.DeleteGPGKey(ctx, ctx.Doer, ctx.PathParamInt64("id")); err != nil {
if asymkey_model.IsErrGPGKeyAccessDenied(err) {
- ctx.Error(http.StatusForbidden, "", "You do not have access to this key")
+ ctx.APIError(http.StatusForbidden, "You do not have access to this key")
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteGPGKey", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -296,16 +296,16 @@ func DeleteGPGKey(ctx *context.APIContext) {
func HandleAddGPGKeyError(ctx *context.APIContext, err error, token string) {
switch {
case asymkey_model.IsErrGPGKeyAccessDenied(err):
- ctx.Error(http.StatusUnprocessableEntity, "GPGKeyAccessDenied", "You do not have access to this GPG key")
+ ctx.APIError(http.StatusUnprocessableEntity, "You do not have access to this GPG key")
case asymkey_model.IsErrGPGKeyIDAlreadyUsed(err):
- ctx.Error(http.StatusUnprocessableEntity, "GPGKeyIDAlreadyUsed", "A key with the same id already exists")
+ ctx.APIError(http.StatusUnprocessableEntity, "A key with the same id already exists")
case asymkey_model.IsErrGPGKeyParsing(err):
- ctx.Error(http.StatusUnprocessableEntity, "GPGKeyParsing", err)
+ ctx.APIError(http.StatusUnprocessableEntity, err)
case asymkey_model.IsErrGPGNoEmailFound(err):
- ctx.Error(http.StatusNotFound, "GPGNoEmailFound", fmt.Sprintf("None of the emails attached to the GPG key could be found. It may still be added if you provide a valid signature for the token: %s", token))
+ ctx.APIError(http.StatusNotFound, "None of the emails attached to the GPG key could be found. It may still be added if you provide a valid signature for the token: "+token)
case asymkey_model.IsErrGPGInvalidTokenSignature(err):
- ctx.Error(http.StatusUnprocessableEntity, "GPGInvalidSignature", fmt.Sprintf("The provided GPG key, signature and token do not match or token is out of date. Provide a valid signature for the token: %s", token))
+ ctx.APIError(http.StatusUnprocessableEntity, "The provided GPG key, signature and token do not match or token is out of date. Provide a valid signature for the token: "+token)
default:
- ctx.Error(http.StatusInternalServerError, "AddGPGKey", err)
+ ctx.APIErrorInternal(err)
}
}
diff --git a/routers/api/v1/user/helper.go b/routers/api/v1/user/helper.go
index 9a6f305700..f49bbbd6db 100644
--- a/routers/api/v1/user/helper.go
+++ b/routers/api/v1/user/helper.go
@@ -4,8 +4,6 @@
package user
import (
- "net/http"
-
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/services/context"
)
@@ -20,10 +18,10 @@ func GetUserByPathParam(ctx *context.APIContext, name string) *user_model.User {
if redirectUserID, err2 := user_model.LookupUserRedirect(ctx, username); err2 == nil {
context.RedirectToUser(ctx.Base, username, redirectUserID)
} else {
- ctx.NotFound("GetUserByName", err)
+ ctx.APIErrorNotFound("GetUserByName", err)
}
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ ctx.APIErrorInternal(err)
}
return nil
}
diff --git a/routers/api/v1/user/hook.go b/routers/api/v1/user/hook.go
index b4605c8a29..73c98ce746 100644
--- a/routers/api/v1/user/hook.go
+++ b/routers/api/v1/user/hook.go
@@ -63,13 +63,13 @@ func GetHook(ctx *context.APIContext) {
}
if !ctx.Doer.IsAdmin && hook.OwnerID != ctx.Doer.ID {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
return
}
apiHook, err := webhook_service.ToHook(ctx.Doer.HomeLink(), hook)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, apiHook)
diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go
index 5a9125b4f3..aa69245e49 100644
--- a/routers/api/v1/user/key.go
+++ b/routers/api/v1/user/key.go
@@ -5,7 +5,7 @@ package user
import (
std_ctx "context"
- "fmt"
+ "errors"
"net/http"
asymkey_model "code.gitea.io/gitea/models/asymkey"
@@ -24,9 +24,10 @@ import (
// appendPrivateInformation appends the owner and key type information to api.PublicKey
func appendPrivateInformation(ctx std_ctx.Context, apiKey *api.PublicKey, key *asymkey_model.PublicKey, defaultUser *user_model.User) (*api.PublicKey, error) {
- if key.Type == asymkey_model.KeyTypeDeploy {
+ switch key.Type {
+ case asymkey_model.KeyTypeDeploy:
apiKey.KeyType = "deploy"
- } else if key.Type == asymkey_model.KeyTypeUser {
+ case asymkey_model.KeyTypeUser:
apiKey.KeyType = "user"
if defaultUser.ID == key.OwnerID {
@@ -38,7 +39,7 @@ func appendPrivateInformation(ctx std_ctx.Context, apiKey *api.PublicKey, key *a
}
apiKey.Owner = convert.ToUser(ctx, user, user)
}
- } else {
+ default:
apiKey.KeyType = "unknown"
}
apiKey.ReadOnly = key.Mode == perm.AccessModeRead
@@ -81,7 +82,7 @@ func listPublicKeys(ctx *context.APIContext, user *user_model.User) {
}
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ListPublicKeys", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -135,7 +136,7 @@ func ListPublicKeys(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user whose public keys are to be listed
// type: string
// required: true
// - name: fingerprint
@@ -182,9 +183,9 @@ func GetPublicKey(ctx *context.APIContext) {
key, err := asymkey_model.GetPublicKeyByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if asymkey_model.IsErrKeyNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetPublicKeyByID", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -200,7 +201,7 @@ func GetPublicKey(ctx *context.APIContext) {
// CreateUserPublicKey creates new public key to given user by ID.
func CreateUserPublicKey(ctx *context.APIContext, form api.CreateKeyOption, uid int64) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited"))
+ ctx.APIErrorNotFound("Not Found", errors.New("ssh keys setting is not allowed to be visited"))
return
}
@@ -270,7 +271,7 @@ func DeletePublicKey(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited"))
+ ctx.APIErrorNotFound("Not Found", errors.New("ssh keys setting is not allowed to be visited"))
return
}
@@ -278,23 +279,23 @@ func DeletePublicKey(ctx *context.APIContext) {
externallyManaged, err := asymkey_model.PublicKeyIsExternallyManaged(ctx, id)
if err != nil {
if asymkey_model.IsErrKeyNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "PublicKeyIsExternallyManaged", err)
+ ctx.APIErrorInternal(err)
}
return
}
if externallyManaged {
- ctx.Error(http.StatusForbidden, "", "SSH Key is externally managed for this user")
+ ctx.APIError(http.StatusForbidden, "SSH Key is externally managed for this user")
return
}
if err := asymkey_service.DeletePublicKey(ctx, ctx.Doer, id); err != nil {
if asymkey_model.IsErrKeyAccessDenied(err) {
- ctx.Error(http.StatusForbidden, "", "You do not have access to this key")
+ ctx.APIError(http.StatusForbidden, "You do not have access to this key")
} else {
- ctx.Error(http.StatusInternalServerError, "DeletePublicKey", err)
+ ctx.APIErrorInternal(err)
}
return
}
diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go
index 6111341423..6d0129681e 100644
--- a/routers/api/v1/user/repo.go
+++ b/routers/api/v1/user/repo.go
@@ -19,19 +19,19 @@ import (
func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) {
opts := utils.GetListOptions(ctx)
- repos, count, err := repo_model.GetUserRepositories(ctx, &repo_model.SearchRepoOptions{
+ repos, count, err := repo_model.GetUserRepositories(ctx, repo_model.SearchRepoOptions{
Actor: u,
Private: private,
ListOptions: opts,
OrderBy: "id ASC",
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserRepositories", err)
+ ctx.APIErrorInternal(err)
return
}
if err := repos.LoadAttributes(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "RepositoryList.LoadAttributes", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -39,7 +39,7 @@ func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) {
for i := range repos {
permission, err := access_model.GetUserRepoPermission(ctx, repos[i], ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
+ ctx.APIErrorInternal(err)
return
}
if ctx.IsSigned && ctx.Doer.IsAdmin || permission.HasAnyUnitAccess() {
@@ -62,7 +62,7 @@ func ListUserRepos(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user whose owned repos are to be listed
// type: string
// required: true
// - name: page
@@ -103,7 +103,7 @@ func ListMyRepos(ctx *context.APIContext) {
// "200":
// "$ref": "#/responses/RepositoryList"
- opts := &repo_model.SearchRepoOptions{
+ opts := repo_model.SearchRepoOptions{
ListOptions: utils.GetListOptions(ctx),
Actor: ctx.Doer,
OwnerID: ctx.Doer.ID,
@@ -113,19 +113,19 @@ func ListMyRepos(ctx *context.APIContext) {
repos, count, err := repo_model.SearchRepository(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "SearchRepository", err)
+ ctx.APIErrorInternal(err)
return
}
results := make([]*api.Repository, len(repos))
for i, repo := range repos {
if err = repo.LoadOwner(ctx); err != nil {
- ctx.Error(http.StatusInternalServerError, "LoadOwner", err)
+ ctx.APIErrorInternal(err)
return
}
permission, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
+ ctx.APIErrorInternal(err)
}
results[i] = convert.ToRepo(ctx, repo, permission)
}
diff --git a/routers/api/v1/user/runners.go b/routers/api/v1/user/runners.go
index 899218473e..be3f63cc5e 100644
--- a/routers/api/v1/user/runners.go
+++ b/routers/api/v1/user/runners.go
@@ -24,3 +24,81 @@ func GetRegistrationToken(ctx *context.APIContext) {
shared.GetRegistrationToken(ctx, ctx.Doer.ID, 0)
}
+
+// CreateRegistrationToken returns the token to register user runners
+func CreateRegistrationToken(ctx *context.APIContext) {
+ // swagger:operation POST /user/actions/runners/registration-token user userCreateRunnerRegistrationToken
+ // ---
+ // summary: Get an user's actions runner registration token
+ // produces:
+ // - application/json
+ // parameters:
+ // responses:
+ // "200":
+ // "$ref": "#/responses/RegistrationToken"
+
+ shared.GetRegistrationToken(ctx, ctx.Doer.ID, 0)
+}
+
+// ListRunners get user-level runners
+func ListRunners(ctx *context.APIContext) {
+ // swagger:operation GET /user/actions/runners user getUserRunners
+ // ---
+ // summary: Get user-level runners
+ // produces:
+ // - application/json
+ // responses:
+ // "200":
+ // "$ref": "#/definitions/ActionRunnersResponse"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.ListRunners(ctx, ctx.Doer.ID, 0)
+}
+
+// GetRunner get an user-level runner
+func GetRunner(ctx *context.APIContext) {
+ // swagger:operation GET /user/actions/runners/{runner_id} user getUserRunner
+ // ---
+ // summary: Get an user-level runner
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: runner_id
+ // in: path
+ // description: id of the runner
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/definitions/ActionRunner"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.GetRunner(ctx, ctx.Doer.ID, 0, ctx.PathParamInt64("runner_id"))
+}
+
+// DeleteRunner delete an user-level runner
+func DeleteRunner(ctx *context.APIContext) {
+ // swagger:operation DELETE /user/actions/runners/{runner_id} user deleteUserRunner
+ // ---
+ // summary: Delete an user-level runner
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: runner_id
+ // in: path
+ // description: id of the runner
+ // type: string
+ // required: true
+ // responses:
+ // "204":
+ // description: runner has been deleted
+ // "400":
+ // "$ref": "#/responses/error"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ shared.DeleteRunner(ctx, ctx.Doer.ID, 0, ctx.PathParamInt64("runner_id"))
+}
diff --git a/routers/api/v1/user/settings.go b/routers/api/v1/user/settings.go
index d0a8daaa85..d67c54b339 100644
--- a/routers/api/v1/user/settings.go
+++ b/routers/api/v1/user/settings.go
@@ -57,7 +57,7 @@ func UpdateUserSettings(ctx *context.APIContext) {
KeepActivityPrivate: optional.FromPtr(form.HideActivity),
}
if err := user_service.UpdateUser(ctx, ctx.Doer, opts); err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go
index ad9ed9548d..ee5d63063b 100644
--- a/routers/api/v1/user/star.go
+++ b/routers/api/v1/user/star.go
@@ -50,7 +50,7 @@ func GetStarredRepos(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user whose starred repos are to be listed
// type: string
// required: true
// - name: page
@@ -66,11 +66,13 @@ func GetStarredRepos(ctx *context.APIContext) {
// "$ref": "#/responses/RepositoryList"
// "404":
// "$ref": "#/responses/notFound"
+ // "403":
+ // "$ref": "#/responses/forbidden"
private := ctx.ContextUser.ID == ctx.Doer.ID
repos, err := getStarredRepos(ctx, ctx.ContextUser, private)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "getStarredRepos", err)
+ ctx.APIErrorInternal(err)
return
}
@@ -97,10 +99,12 @@ func GetMyStarredRepos(ctx *context.APIContext) {
// responses:
// "200":
// "$ref": "#/responses/RepositoryList"
+ // "403":
+ // "$ref": "#/responses/forbidden"
repos, err := getStarredRepos(ctx, ctx.Doer, true)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "getStarredRepos", err)
+ ctx.APIErrorInternal(err)
}
ctx.SetTotalCountHeader(int64(ctx.Doer.NumStars))
@@ -128,11 +132,13 @@ func IsStarring(ctx *context.APIContext) {
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"
+ // "403":
+ // "$ref": "#/responses/forbidden"
if repo_model.IsStaring(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) {
ctx.Status(http.StatusNoContent)
} else {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
}
}
@@ -163,9 +169,9 @@ func Star(ctx *context.APIContext) {
err := repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, true)
if err != nil {
if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, "BlockedUser", err)
+ ctx.APIError(http.StatusForbidden, err)
} else {
- ctx.Error(http.StatusInternalServerError, "StarRepo", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -193,10 +199,12 @@ func Unstar(ctx *context.APIContext) {
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"
+ // "403":
+ // "$ref": "#/responses/forbidden"
err := repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, false)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "StarRepo", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go
index 43dabe1b60..6de1125c40 100644
--- a/routers/api/v1/user/user.go
+++ b/routers/api/v1/user/user.go
@@ -73,7 +73,7 @@ func Search(ctx *context.APIContext) {
if ctx.PublicOnly {
visible = []structs.VisibleType{structs.VisibleTypePublic}
}
- users, maxResults, err = user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
+ users, maxResults, err = user_model.SearchUsers(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer,
Keyword: ctx.FormTrim("q"),
UID: uid,
@@ -110,7 +110,7 @@ func GetInfo(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user to get
+ // description: username of the user whose data is to be listed
// type: string
// required: true
// responses:
@@ -121,7 +121,7 @@ func GetInfo(ctx *context.APIContext) {
if !user_model.IsUserVisibleToViewer(ctx, ctx.ContextUser, ctx.Doer) {
// fake ErrUserNotExist error message to not leak information about existence
- ctx.NotFound("GetUserByName", user_model.ErrUserNotExist{Name: ctx.PathParam("username")})
+ ctx.APIErrorNotFound("GetUserByName", user_model.ErrUserNotExist{Name: ctx.PathParam("username")})
return
}
ctx.JSON(http.StatusOK, convert.ToUser(ctx, ctx.ContextUser, ctx.Doer))
@@ -151,7 +151,7 @@ func GetUserHeatmapData(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user to get
+ // description: username of the user whose heatmap is to be obtained
// type: string
// required: true
// responses:
@@ -162,7 +162,7 @@ func GetUserHeatmapData(ctx *context.APIContext) {
heatmap, err := activities_model.GetUserHeatmapDataByUser(ctx, ctx.ContextUser, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserHeatmapDataByUser", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, heatmap)
@@ -177,7 +177,7 @@ func ListUserActivityFeeds(ctx *context.APIContext) {
// parameters:
// - name: username
// in: path
- // description: username of user
+ // description: username of the user whose activity feeds are to be listed
// type: string
// required: true
// - name: only-performed-by
@@ -217,7 +217,7 @@ func ListUserActivityFeeds(ctx *context.APIContext) {
feeds, count, err := feed_service.GetFeeds(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetFeeds", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.SetTotalCountHeader(count)
diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go
index 2cc23ae476..844eac2c67 100644
--- a/routers/api/v1/user/watch.go
+++ b/routers/api/v1/user/watch.go
@@ -49,7 +49,7 @@ func GetWatchedRepos(ctx *context.APIContext) {
// - name: username
// type: string
// in: path
- // description: username of the user
+ // description: username of the user whose watched repos are to be listed
// required: true
// - name: page
// in: query
@@ -68,7 +68,7 @@ func GetWatchedRepos(ctx *context.APIContext) {
private := ctx.ContextUser.ID == ctx.Doer.ID
repos, total, err := getWatchedRepos(ctx, ctx.ContextUser, private)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err)
+ ctx.APIErrorInternal(err)
}
ctx.SetTotalCountHeader(total)
@@ -97,7 +97,7 @@ func GetMyWatchedRepos(ctx *context.APIContext) {
repos, total, err := getWatchedRepos(ctx, ctx.Doer, true)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err)
+ ctx.APIErrorInternal(err)
}
ctx.SetTotalCountHeader(total)
@@ -137,7 +137,7 @@ func IsWatching(ctx *context.APIContext) {
RepositoryURL: ctx.Repo.Repository.APIURL(),
})
} else {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
}
}
@@ -168,9 +168,9 @@ func Watch(ctx *context.APIContext) {
err := repo_model.WatchRepo(ctx, ctx.Doer, ctx.Repo.Repository, true)
if err != nil {
if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Error(http.StatusForbidden, "BlockedUser", err)
+ ctx.APIError(http.StatusForbidden, err)
} else {
- ctx.Error(http.StatusInternalServerError, "WatchRepo", err)
+ ctx.APIErrorInternal(err)
}
return
}
@@ -208,7 +208,7 @@ func Unwatch(ctx *context.APIContext) {
err := repo_model.WatchRepo(ctx, ctx.Doer, ctx.Repo.Repository, false)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "UnwatchRepo", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
diff --git a/routers/api/v1/utils/git.go b/routers/api/v1/utils/git.go
index 4e25137817..1cfe01a639 100644
--- a/routers/api/v1/utils/git.go
+++ b/routers/api/v1/utils/git.go
@@ -4,53 +4,54 @@
package utils
import (
- gocontext "context"
- "fmt"
- "net/http"
+ "errors"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/services/context"
)
-// ResolveRefOrSha resolve ref to sha if exist
-func ResolveRefOrSha(ctx *context.APIContext, ref string) string {
- if len(ref) == 0 {
- ctx.Error(http.StatusBadRequest, "ref not given", nil)
- return ""
- }
+type RefCommit struct {
+ InputRef string
+ RefName git.RefName
+ Commit *git.Commit
+ CommitID string
+}
- sha := ref
- // Search branches and tags
- for _, refType := range []string{"heads", "tags"} {
- refSHA, lastMethodName, err := searchRefCommitByType(ctx, refType, ref)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, lastMethodName, err)
- return ""
- }
- if refSHA != "" {
- sha = refSHA
- break
- }
+// ResolveRefCommit resolve ref to a commit if exist
+func ResolveRefCommit(ctx reqctx.RequestContext, repo *repo_model.Repository, inputRef string, minCommitIDLen ...int) (_ *RefCommit, err error) {
+ gitRepo, err := gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo)
+ if err != nil {
+ return nil, err
}
-
- sha = MustConvertToSHA1(ctx, ctx.Repo, sha)
-
- if ctx.Repo.GitRepo != nil {
- err := ctx.Repo.GitRepo.AddLastCommitCache(ctx.Repo.Repository.GetCommitsCountCacheKey(ref, ref != sha), ctx.Repo.Repository.FullName(), sha)
- if err != nil {
- log.Error("Unable to get commits count for %s in %s. Error: %v", sha, ctx.Repo.Repository.FullName(), err)
- }
+ refCommit := RefCommit{InputRef: inputRef}
+ if gitrepo.IsBranchExist(ctx, repo, inputRef) {
+ refCommit.RefName = git.RefNameFromBranch(inputRef)
+ } else if gitrepo.IsTagExist(ctx, repo, inputRef) {
+ refCommit.RefName = git.RefNameFromTag(inputRef)
+ } else if git.IsStringLikelyCommitID(git.ObjectFormatFromName(repo.ObjectFormatName), inputRef, minCommitIDLen...) {
+ refCommit.RefName = git.RefNameFromCommit(inputRef)
+ }
+ if refCommit.RefName == "" {
+ return nil, git.ErrNotExist{ID: inputRef}
}
+ if refCommit.Commit, err = gitRepo.GetCommit(refCommit.RefName.String()); err != nil {
+ return nil, err
+ }
+ refCommit.CommitID = refCommit.Commit.ID.String()
+ return &refCommit, nil
+}
- return sha
+func NewRefCommit(refName git.RefName, commit *git.Commit) *RefCommit {
+ return &RefCommit{InputRef: refName.ShortName(), RefName: refName, Commit: commit, CommitID: commit.ID.String()}
}
// GetGitRefs return git references based on filter
func GetGitRefs(ctx *context.APIContext, filter string) ([]*git.Reference, string, error) {
if ctx.Repo.GitRepo == nil {
- return nil, "", fmt.Errorf("no open git repo found in context")
+ return nil, "", errors.New("no open git repo found in context")
}
if len(filter) > 0 {
filter = "refs/" + filter
@@ -58,42 +59,3 @@ func GetGitRefs(ctx *context.APIContext, filter string) ([]*git.Reference, strin
refs, err := ctx.Repo.GitRepo.GetRefsFiltered(filter)
return refs, "GetRefsFiltered", err
}
-
-func searchRefCommitByType(ctx *context.APIContext, refType, filter string) (string, string, error) {
- refs, lastMethodName, err := GetGitRefs(ctx, refType+"/"+filter) // Search by type
- if err != nil {
- return "", lastMethodName, err
- }
- if len(refs) > 0 {
- return refs[0].Object.String(), "", nil // Return found SHA
- }
- return "", "", nil
-}
-
-// ConvertToObjectID returns a full-length SHA1 from a potential ID string
-func ConvertToObjectID(ctx gocontext.Context, repo *context.Repository, commitID string) (git.ObjectID, error) {
- objectFormat := repo.GetObjectFormat()
- if len(commitID) == objectFormat.FullLength() && objectFormat.IsValid(commitID) {
- sha, err := git.NewIDFromString(commitID)
- if err == nil {
- return sha, nil
- }
- }
-
- gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo.Repository)
- if err != nil {
- return objectFormat.EmptyObjectID(), fmt.Errorf("RepositoryFromContextOrOpen: %w", err)
- }
- defer closer.Close()
-
- return gitRepo.ConvertToGitID(commitID)
-}
-
-// MustConvertToSHA1 returns a full-length SHA1 string from a potential ID string, or returns origin input if it can't convert to SHA1
-func MustConvertToSHA1(ctx gocontext.Context, repo *context.Repository, commitID string) string {
- sha, err := ConvertToObjectID(ctx, repo, commitID)
- if err != nil {
- return commitID
- }
- return sha.String()
-}
diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go
index 4328878e19..6f598f14c8 100644
--- a/routers/api/v1/utils/hook.go
+++ b/routers/api/v1/utils/hook.go
@@ -4,7 +4,6 @@
package utils
import (
- "fmt"
"net/http"
"strconv"
"strings"
@@ -16,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/validation"
webhook_module "code.gitea.io/gitea/modules/webhook"
"code.gitea.io/gitea/services/context"
webhook_service "code.gitea.io/gitea/services/webhook"
@@ -30,7 +30,7 @@ func ListOwnerHooks(ctx *context.APIContext, owner *user_model.User) {
hooks, count, err := db.FindAndCount[webhook.Webhook](ctx, opts)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
@@ -38,7 +38,7 @@ func ListOwnerHooks(ctx *context.APIContext, owner *user_model.User) {
for i, hook := range hooks {
apiHooks[i], err = webhook_service.ToHook(owner.HomeLink(), hook)
if err != nil {
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -52,9 +52,9 @@ func GetOwnerHook(ctx *context.APIContext, ownerID, hookID int64) (*webhook.Webh
w, err := webhook.GetWebhookByOwnerID(ctx, ownerID, hookID)
if err != nil {
if webhook.IsErrWebhookNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetWebhookByOwnerID", err)
+ ctx.APIErrorInternal(err)
}
return nil, err
}
@@ -67,9 +67,9 @@ func GetRepoHook(ctx *context.APIContext, repoID, hookID int64) (*webhook.Webhoo
w, err := webhook.GetWebhookByRepoID(ctx, repoID, hookID)
if err != nil {
if webhook.IsErrWebhookNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "GetWebhookByID", err)
+ ctx.APIErrorInternal(err)
}
return nil, err
}
@@ -80,17 +80,21 @@ func GetRepoHook(ctx *context.APIContext, repoID, hookID int64) (*webhook.Webhoo
// write the appropriate error to `ctx`. Return whether the form is valid
func checkCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption) bool {
if !webhook_service.IsValidHookTaskType(form.Type) {
- ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Invalid hook type: %s", form.Type))
+ ctx.APIError(http.StatusUnprocessableEntity, "Invalid hook type: "+form.Type)
return false
}
for _, name := range []string{"url", "content_type"} {
if _, ok := form.Config[name]; !ok {
- ctx.Error(http.StatusUnprocessableEntity, "", "Missing config option: "+name)
+ ctx.APIError(http.StatusUnprocessableEntity, "Missing config option: "+name)
return false
}
}
if !webhook.IsValidHookContentType(form.Config["content_type"]) {
- ctx.Error(http.StatusUnprocessableEntity, "", "Invalid content type")
+ ctx.APIError(http.StatusUnprocessableEntity, "Invalid content type")
+ return false
+ }
+ if !validation.IsValidURL(form.Config["url"]) {
+ ctx.APIError(http.StatusUnprocessableEntity, "Invalid url")
return false
}
return true
@@ -102,7 +106,7 @@ func AddSystemHook(ctx *context.APIContext, form *api.CreateHookOption) {
if ok {
h, err := webhook_service.ToHook(setting.AppSubURL+"/-/admin", hook)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "convert.ToHook", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusCreated, h)
@@ -141,7 +145,7 @@ func AddRepoHook(ctx *context.APIContext, form *api.CreateHookOption) {
func toAPIHook(ctx *context.APIContext, repoLink string, hook *webhook.Webhook) (*api.Hook, bool) {
apiHook, err := webhook_service.ToHook(repoLink, hook)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ToHook", err)
+ ctx.APIErrorInternal(err)
return nil, false
}
return apiHook, true
@@ -155,6 +159,42 @@ func pullHook(events []string, event string) bool {
return util.SliceContainsString(events, event, true) || util.SliceContainsString(events, string(webhook_module.HookEventPullRequest), true)
}
+func updateHookEvents(events []string) webhook_module.HookEvents {
+ if len(events) == 0 {
+ events = []string{"push"}
+ }
+ hookEvents := make(webhook_module.HookEvents)
+ hookEvents[webhook_module.HookEventCreate] = util.SliceContainsString(events, string(webhook_module.HookEventCreate), true)
+ hookEvents[webhook_module.HookEventPush] = util.SliceContainsString(events, string(webhook_module.HookEventPush), true)
+ hookEvents[webhook_module.HookEventDelete] = util.SliceContainsString(events, string(webhook_module.HookEventDelete), true)
+ hookEvents[webhook_module.HookEventFork] = util.SliceContainsString(events, string(webhook_module.HookEventFork), true)
+ hookEvents[webhook_module.HookEventRepository] = util.SliceContainsString(events, string(webhook_module.HookEventRepository), true)
+ hookEvents[webhook_module.HookEventWiki] = util.SliceContainsString(events, string(webhook_module.HookEventWiki), true)
+ hookEvents[webhook_module.HookEventRelease] = util.SliceContainsString(events, string(webhook_module.HookEventRelease), true)
+ hookEvents[webhook_module.HookEventPackage] = util.SliceContainsString(events, string(webhook_module.HookEventPackage), true)
+ hookEvents[webhook_module.HookEventStatus] = util.SliceContainsString(events, string(webhook_module.HookEventStatus), true)
+ hookEvents[webhook_module.HookEventWorkflowRun] = util.SliceContainsString(events, string(webhook_module.HookEventWorkflowRun), true)
+ hookEvents[webhook_module.HookEventWorkflowJob] = util.SliceContainsString(events, string(webhook_module.HookEventWorkflowJob), true)
+
+ // Issues
+ hookEvents[webhook_module.HookEventIssues] = issuesHook(events, "issues_only")
+ hookEvents[webhook_module.HookEventIssueAssign] = issuesHook(events, string(webhook_module.HookEventIssueAssign))
+ hookEvents[webhook_module.HookEventIssueLabel] = issuesHook(events, string(webhook_module.HookEventIssueLabel))
+ hookEvents[webhook_module.HookEventIssueMilestone] = issuesHook(events, string(webhook_module.HookEventIssueMilestone))
+ hookEvents[webhook_module.HookEventIssueComment] = issuesHook(events, string(webhook_module.HookEventIssueComment))
+
+ // Pull requests
+ hookEvents[webhook_module.HookEventPullRequest] = pullHook(events, "pull_request_only")
+ hookEvents[webhook_module.HookEventPullRequestAssign] = pullHook(events, string(webhook_module.HookEventPullRequestAssign))
+ hookEvents[webhook_module.HookEventPullRequestLabel] = pullHook(events, string(webhook_module.HookEventPullRequestLabel))
+ hookEvents[webhook_module.HookEventPullRequestMilestone] = pullHook(events, string(webhook_module.HookEventPullRequestMilestone))
+ hookEvents[webhook_module.HookEventPullRequestComment] = pullHook(events, string(webhook_module.HookEventPullRequestComment))
+ hookEvents[webhook_module.HookEventPullRequestReview] = pullHook(events, "pull_request_review")
+ hookEvents[webhook_module.HookEventPullRequestReviewRequest] = pullHook(events, string(webhook_module.HookEventPullRequestReviewRequest))
+ hookEvents[webhook_module.HookEventPullRequestSync] = pullHook(events, string(webhook_module.HookEventPullRequestSync))
+ return hookEvents
+}
+
// addHook add the hook specified by `form`, `ownerID` and `repoID`. If there is
// an error, write to `ctx` accordingly. Return (webhook, ok)
func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoID int64) (*webhook.Webhook, bool) {
@@ -163,13 +203,10 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoI
return nil, false
}
- if len(form.Events) == 0 {
- form.Events = []string{"push"}
- }
if form.Config["is_system_webhook"] != "" {
sw, err := strconv.ParseBool(form.Config["is_system_webhook"])
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, "", "Invalid is_system_webhook value")
+ ctx.APIError(http.StatusUnprocessableEntity, "Invalid is_system_webhook value")
return nil, false
}
isSystemWebhook = sw
@@ -184,28 +221,7 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoI
IsSystemWebhook: isSystemWebhook,
HookEvent: &webhook_module.HookEvent{
ChooseEvents: true,
- HookEvents: webhook_module.HookEvents{
- Create: util.SliceContainsString(form.Events, string(webhook_module.HookEventCreate), true),
- Delete: util.SliceContainsString(form.Events, string(webhook_module.HookEventDelete), true),
- Fork: util.SliceContainsString(form.Events, string(webhook_module.HookEventFork), true),
- Issues: issuesHook(form.Events, "issues_only"),
- IssueAssign: issuesHook(form.Events, string(webhook_module.HookEventIssueAssign)),
- IssueLabel: issuesHook(form.Events, string(webhook_module.HookEventIssueLabel)),
- IssueMilestone: issuesHook(form.Events, string(webhook_module.HookEventIssueMilestone)),
- IssueComment: issuesHook(form.Events, string(webhook_module.HookEventIssueComment)),
- Push: util.SliceContainsString(form.Events, string(webhook_module.HookEventPush), true),
- PullRequest: pullHook(form.Events, "pull_request_only"),
- PullRequestAssign: pullHook(form.Events, string(webhook_module.HookEventPullRequestAssign)),
- PullRequestLabel: pullHook(form.Events, string(webhook_module.HookEventPullRequestLabel)),
- PullRequestMilestone: pullHook(form.Events, string(webhook_module.HookEventPullRequestMilestone)),
- PullRequestComment: pullHook(form.Events, string(webhook_module.HookEventPullRequestComment)),
- PullRequestReview: pullHook(form.Events, "pull_request_review"),
- PullRequestReviewRequest: pullHook(form.Events, string(webhook_module.HookEventPullRequestReviewRequest)),
- PullRequestSync: pullHook(form.Events, string(webhook_module.HookEventPullRequestSync)),
- Wiki: util.SliceContainsString(form.Events, string(webhook_module.HookEventWiki), true),
- Repository: util.SliceContainsString(form.Events, string(webhook_module.HookEventRepository), true),
- Release: util.SliceContainsString(form.Events, string(webhook_module.HookEventRelease), true),
- },
+ HookEvents: updateHookEvents(form.Events),
BranchFilter: form.BranchFilter,
},
IsActive: form.Active,
@@ -213,19 +229,19 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoI
}
err := w.SetHeaderAuthorization(form.AuthorizationHeader)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "SetHeaderAuthorization", err)
+ ctx.APIErrorInternal(err)
return nil, false
}
if w.Type == webhook_module.SLACK {
channel, ok := form.Config["channel"]
if !ok {
- ctx.Error(http.StatusUnprocessableEntity, "", "Missing config option: channel")
+ ctx.APIError(http.StatusUnprocessableEntity, "Missing config option: channel")
return nil, false
}
channel = strings.TrimSpace(channel)
if !webhook_service.IsValidSlackChannel(channel) {
- ctx.Error(http.StatusBadRequest, "", "Invalid slack channel name")
+ ctx.APIError(http.StatusBadRequest, "Invalid slack channel name")
return nil, false
}
@@ -236,17 +252,17 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoI
Color: form.Config["color"],
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "slack: JSON marshal failed", err)
+ ctx.APIErrorInternal(err)
return nil, false
}
w.Meta = string(meta)
}
if err := w.UpdateEvent(); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateEvent", err)
+ ctx.APIErrorInternal(err)
return nil, false
} else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.Error(http.StatusInternalServerError, "CreateWebhook", err)
+ ctx.APIErrorInternal(err)
return nil, false
}
return w, true
@@ -256,21 +272,21 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoI
func EditSystemHook(ctx *context.APIContext, form *api.EditHookOption, hookID int64) {
hook, err := webhook.GetSystemOrDefaultWebhook(ctx, hookID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetSystemOrDefaultWebhook", err)
+ ctx.APIErrorInternal(err)
return
}
if !editHook(ctx, form, hook) {
- ctx.Error(http.StatusInternalServerError, "editHook", err)
+ ctx.APIErrorInternal(err)
return
}
updated, err := webhook.GetSystemOrDefaultWebhook(ctx, hookID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetSystemOrDefaultWebhook", err)
+ ctx.APIErrorInternal(err)
return
}
h, err := webhook_service.ToHook(setting.AppURL+"/-/admin", updated)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "convert.ToHook", err)
+ ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, h)
@@ -322,11 +338,15 @@ func EditRepoHook(ctx *context.APIContext, form *api.EditHookOption, hookID int6
func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webhook) bool {
if form.Config != nil {
if url, ok := form.Config["url"]; ok {
+ if !validation.IsValidURL(url) {
+ ctx.APIError(http.StatusUnprocessableEntity, "Invalid url")
+ return false
+ }
w.URL = url
}
if ct, ok := form.Config["content_type"]; ok {
if !webhook.IsValidHookContentType(ct) {
- ctx.Error(http.StatusUnprocessableEntity, "", "Invalid content type")
+ ctx.APIError(http.StatusUnprocessableEntity, "Invalid content type")
return false
}
w.ContentType = webhook.ToHookContentType(ct)
@@ -341,7 +361,7 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh
Color: form.Config["color"],
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, "slack: JSON marshal failed", err)
+ ctx.APIErrorInternal(err)
return false
}
w.Meta = string(meta)
@@ -350,47 +370,20 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh
}
// Update events
- if len(form.Events) == 0 {
- form.Events = []string{"push"}
- }
+ w.HookEvents = updateHookEvents(form.Events)
w.PushOnly = false
w.SendEverything = false
w.ChooseEvents = true
- w.Create = util.SliceContainsString(form.Events, string(webhook_module.HookEventCreate), true)
- w.Push = util.SliceContainsString(form.Events, string(webhook_module.HookEventPush), true)
- w.Create = util.SliceContainsString(form.Events, string(webhook_module.HookEventCreate), true)
- w.Delete = util.SliceContainsString(form.Events, string(webhook_module.HookEventDelete), true)
- w.Fork = util.SliceContainsString(form.Events, string(webhook_module.HookEventFork), true)
- w.Repository = util.SliceContainsString(form.Events, string(webhook_module.HookEventRepository), true)
- w.Wiki = util.SliceContainsString(form.Events, string(webhook_module.HookEventWiki), true)
- w.Release = util.SliceContainsString(form.Events, string(webhook_module.HookEventRelease), true)
w.BranchFilter = form.BranchFilter
err := w.SetHeaderAuthorization(form.AuthorizationHeader)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "SetHeaderAuthorization", err)
+ ctx.APIErrorInternal(err)
return false
}
- // Issues
- w.Issues = issuesHook(form.Events, "issues_only")
- w.IssueAssign = issuesHook(form.Events, string(webhook_module.HookEventIssueAssign))
- w.IssueLabel = issuesHook(form.Events, string(webhook_module.HookEventIssueLabel))
- w.IssueMilestone = issuesHook(form.Events, string(webhook_module.HookEventIssueMilestone))
- w.IssueComment = issuesHook(form.Events, string(webhook_module.HookEventIssueComment))
-
- // Pull requests
- w.PullRequest = pullHook(form.Events, "pull_request_only")
- w.PullRequestAssign = pullHook(form.Events, string(webhook_module.HookEventPullRequestAssign))
- w.PullRequestLabel = pullHook(form.Events, string(webhook_module.HookEventPullRequestLabel))
- w.PullRequestMilestone = pullHook(form.Events, string(webhook_module.HookEventPullRequestMilestone))
- w.PullRequestComment = pullHook(form.Events, string(webhook_module.HookEventPullRequestComment))
- w.PullRequestReview = pullHook(form.Events, "pull_request_review")
- w.PullRequestReviewRequest = pullHook(form.Events, string(webhook_module.HookEventPullRequestReviewRequest))
- w.PullRequestSync = pullHook(form.Events, string(webhook_module.HookEventPullRequestSync))
-
if err := w.UpdateEvent(); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateEvent", err)
+ ctx.APIErrorInternal(err)
return false
}
@@ -399,7 +392,7 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh
}
if err := webhook.UpdateWebhook(ctx, w); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateWebhook", err)
+ ctx.APIErrorInternal(err)
return false
}
return true
@@ -409,9 +402,9 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh
func DeleteOwnerHook(ctx *context.APIContext, owner *user_model.User, hookID int64) {
if err := webhook.DeleteWebhookByOwnerID(ctx, owner.ID, hookID); err != nil {
if webhook.IsErrWebhookNotExist(err) {
- ctx.NotFound()
+ ctx.APIErrorNotFound()
} else {
- ctx.Error(http.StatusInternalServerError, "DeleteWebhookByOwnerID", err)
+ ctx.APIErrorInternal(err)
}
return
}
diff --git a/routers/api/v1/utils/hook_test.go b/routers/api/v1/utils/hook_test.go
new file mode 100644
index 0000000000..e5e8ce07ce
--- /dev/null
+++ b/routers/api/v1/utils/hook_test.go
@@ -0,0 +1,82 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package utils
+
+import (
+ "net/http"
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/contexttest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTestHookValidation(t *testing.T) {
+ unittest.PrepareTestEnv(t)
+
+ t.Run("Test Validation", func(t *testing.T) {
+ ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+
+ checkCreateHookOption(ctx, &structs.CreateHookOption{
+ Type: "gitea",
+ Config: map[string]string{
+ "content_type": "json",
+ "url": "https://example.com/webhook",
+ },
+ })
+ assert.Equal(t, 0, ctx.Resp.WrittenStatus()) // not written yet
+ })
+
+ t.Run("Test Validation with invalid URL", func(t *testing.T) {
+ ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+
+ checkCreateHookOption(ctx, &structs.CreateHookOption{
+ Type: "gitea",
+ Config: map[string]string{
+ "content_type": "json",
+ "url": "example.com/webhook",
+ },
+ })
+ assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus())
+ })
+
+ t.Run("Test Validation with invalid webhook type", func(t *testing.T) {
+ ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+
+ checkCreateHookOption(ctx, &structs.CreateHookOption{
+ Type: "unknown",
+ Config: map[string]string{
+ "content_type": "json",
+ "url": "example.com/webhook",
+ },
+ })
+ assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus())
+ })
+
+ t.Run("Test Validation with empty content type", func(t *testing.T) {
+ ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+
+ checkCreateHookOption(ctx, &structs.CreateHookOption{
+ Type: "unknown",
+ Config: map[string]string{
+ "url": "https://example.com/webhook",
+ },
+ })
+ assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus())
+ })
+}
diff --git a/routers/api/v1/utils/main_test.go b/routers/api/v1/utils/main_test.go
new file mode 100644
index 0000000000..4eace1f369
--- /dev/null
+++ b/routers/api/v1/utils/main_test.go
@@ -0,0 +1,21 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package utils
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/setting"
+ webhook_service "code.gitea.io/gitea/services/webhook"
+)
+
+func TestMain(m *testing.M) {
+ unittest.MainTest(m, &unittest.TestOptions{
+ SetUp: func() error {
+ setting.LoadQueueSettings()
+ return webhook_service.Init()
+ },
+ })
+}
diff --git a/routers/common/actions.go b/routers/common/actions.go
new file mode 100644
index 0000000000..a4eabb6ba2
--- /dev/null
+++ b/routers/common/actions.go
@@ -0,0 +1,71 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package common
+
+import (
+ "fmt"
+ "strings"
+
+ actions_model "code.gitea.io/gitea/models/actions"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/actions"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
+)
+
+func DownloadActionsRunJobLogsWithIndex(ctx *context.Base, ctxRepo *repo_model.Repository, runID, jobIndex int64) error {
+ runJobs, err := actions_model.GetRunJobsByRunID(ctx, runID)
+ if err != nil {
+ return fmt.Errorf("GetRunJobsByRunID: %w", err)
+ }
+ if err = runJobs.LoadRepos(ctx); err != nil {
+ return fmt.Errorf("LoadRepos: %w", err)
+ }
+ if jobIndex < 0 || jobIndex >= int64(len(runJobs)) {
+ return util.NewNotExistErrorf("job index is out of range: %d", jobIndex)
+ }
+ return DownloadActionsRunJobLogs(ctx, ctxRepo, runJobs[jobIndex])
+}
+
+func DownloadActionsRunJobLogs(ctx *context.Base, ctxRepo *repo_model.Repository, curJob *actions_model.ActionRunJob) error {
+ if curJob.Repo.ID != ctxRepo.ID {
+ return util.NewNotExistErrorf("job not found")
+ }
+
+ if curJob.TaskID == 0 {
+ return util.NewNotExistErrorf("job not started")
+ }
+
+ if err := curJob.LoadRun(ctx); err != nil {
+ return fmt.Errorf("LoadRun: %w", err)
+ }
+
+ task, err := actions_model.GetTaskByID(ctx, curJob.TaskID)
+ if err != nil {
+ return fmt.Errorf("GetTaskByID: %w", err)
+ }
+
+ if task.LogExpired {
+ return util.NewNotExistErrorf("logs have been cleaned up")
+ }
+
+ reader, err := actions.OpenLogs(ctx, task.LogInStorage, task.LogFilename)
+ if err != nil {
+ return fmt.Errorf("OpenLogs: %w", err)
+ }
+ defer reader.Close()
+
+ workflowName := curJob.Run.WorkflowID
+ if p := strings.Index(workflowName, "."); p > 0 {
+ workflowName = workflowName[0:p]
+ }
+ ctx.ServeContent(reader, &context.ServeHeaderOptions{
+ Filename: fmt.Sprintf("%v-%v-%v.log", workflowName, curJob.Name, task.ID),
+ ContentLength: &task.LogSize,
+ ContentType: "text/plain",
+ ContentTypeCharset: "utf-8",
+ Disposition: "attachment",
+ })
+ return nil
+}
diff --git a/routers/common/blockexpensive.go b/routers/common/blockexpensive.go
new file mode 100644
index 0000000000..f52aa2b709
--- /dev/null
+++ b/routers/common/blockexpensive.go
@@ -0,0 +1,90 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package common
+
+import (
+ "net/http"
+ "strings"
+
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/reqctx"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/web/middleware"
+
+ "github.com/go-chi/chi/v5"
+)
+
+func BlockExpensive() func(next http.Handler) http.Handler {
+ if !setting.Service.BlockAnonymousAccessExpensive {
+ return nil
+ }
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ ret := determineRequestPriority(reqctx.FromContext(req.Context()))
+ if !ret.SignedIn {
+ if ret.Expensive || ret.LongPolling {
+ http.Redirect(w, req, setting.AppSubURL+"/user/login", http.StatusSeeOther)
+ return
+ }
+ }
+ next.ServeHTTP(w, req)
+ })
+ }
+}
+
+func isRoutePathExpensive(routePattern string) bool {
+ if strings.HasPrefix(routePattern, "/user/") || strings.HasPrefix(routePattern, "/login/") {
+ return false
+ }
+
+ expensivePaths := []string{
+ // code related
+ "/{username}/{reponame}/archive/",
+ "/{username}/{reponame}/blame/",
+ "/{username}/{reponame}/commit/",
+ "/{username}/{reponame}/commits/",
+ "/{username}/{reponame}/graph",
+ "/{username}/{reponame}/media/",
+ "/{username}/{reponame}/raw/",
+ "/{username}/{reponame}/src/",
+
+ // issue & PR related (no trailing slash)
+ "/{username}/{reponame}/issues",
+ "/{username}/{reponame}/{type:issues}",
+ "/{username}/{reponame}/pulls",
+ "/{username}/{reponame}/{type:pulls}",
+
+ // wiki
+ "/{username}/{reponame}/wiki/",
+
+ // activity
+ "/{username}/{reponame}/activity/",
+ }
+ for _, path := range expensivePaths {
+ if strings.HasPrefix(routePattern, path) {
+ return true
+ }
+ }
+ return false
+}
+
+func isRoutePathForLongPolling(routePattern string) bool {
+ return routePattern == "/user/events"
+}
+
+func determineRequestPriority(reqCtx reqctx.RequestContext) (ret struct {
+ SignedIn bool
+ Expensive bool
+ LongPolling bool
+},
+) {
+ chiRoutePath := chi.RouteContext(reqCtx).RoutePattern()
+ if _, ok := reqCtx.GetData()[middleware.ContextDataKeySignedUser].(*user_model.User); ok {
+ ret.SignedIn = true
+ } else {
+ ret.Expensive = isRoutePathExpensive(chiRoutePath)
+ ret.LongPolling = isRoutePathForLongPolling(chiRoutePath)
+ }
+ return ret
+}
diff --git a/routers/common/blockexpensive_test.go b/routers/common/blockexpensive_test.go
new file mode 100644
index 0000000000..db5c0db7dd
--- /dev/null
+++ b/routers/common/blockexpensive_test.go
@@ -0,0 +1,30 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package common
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestBlockExpensive(t *testing.T) {
+ cases := []struct {
+ expensive bool
+ routePath string
+ }{
+ {false, "/user/xxx"},
+ {false, "/login/xxx"},
+ {true, "/{username}/{reponame}/archive/xxx"},
+ {true, "/{username}/{reponame}/graph"},
+ {true, "/{username}/{reponame}/src/xxx"},
+ {true, "/{username}/{reponame}/wiki/xxx"},
+ {true, "/{username}/{reponame}/activity/xxx"},
+ }
+ for _, c := range cases {
+ assert.Equal(t, c.expensive, isRoutePathExpensive(c.routePath), "routePath: %s", c.routePath)
+ }
+
+ assert.True(t, isRoutePathForLongPolling("/user/events"))
+}
diff --git a/routers/common/codesearch.go b/routers/common/codesearch.go
index a14af126e5..9bec448d7e 100644
--- a/routers/common/codesearch.go
+++ b/routers/common/codesearch.go
@@ -4,36 +4,30 @@
package common
import (
+ "code.gitea.io/gitea/modules/indexer"
+ code_indexer "code.gitea.io/gitea/modules/indexer/code"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/context"
)
func PrepareCodeSearch(ctx *context.Context) (ret struct {
- Keyword string
- Language string
- IsFuzzy bool
+ Keyword string
+ Language string
+ SearchMode indexer.SearchModeType
},
) {
ret.Language = ctx.FormTrim("l")
ret.Keyword = ctx.FormTrim("q")
+ ret.SearchMode = indexer.SearchModeType(ctx.FormTrim("search_mode"))
- fuzzyDefault := setting.Indexer.RepoIndexerEnabled
- fuzzyAllow := true
- if setting.Indexer.RepoType == "bleve" && setting.Indexer.TypeBleveMaxFuzzniess == 0 {
- fuzzyDefault = false
- fuzzyAllow = false
- }
- isFuzzy := ctx.FormOptionalBool("fuzzy").ValueOrDefault(fuzzyDefault)
- if isFuzzy && !fuzzyAllow {
- ctx.Flash.Info("Fuzzy search is disabled by default due to performance reasons")
- isFuzzy = false
- }
-
- ctx.Data["IsBleveFuzzyDisabled"] = true
ctx.Data["Keyword"] = ret.Keyword
ctx.Data["Language"] = ret.Language
- ctx.Data["IsFuzzy"] = isFuzzy
-
+ ctx.Data["SelectedSearchMode"] = string(ret.SearchMode)
+ if setting.Indexer.RepoIndexerEnabled {
+ ctx.Data["SearchModes"] = code_indexer.SupportedSearchModes()
+ } else {
+ ctx.Data["SearchModes"] = indexer.GitGrepSupportedSearchModes()
+ }
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
return ret
}
diff --git a/routers/common/db.go b/routers/common/db.go
index 61b331760c..01c0261427 100644
--- a/routers/common/db.go
+++ b/routers/common/db.go
@@ -5,7 +5,7 @@ package common
import (
"context"
- "fmt"
+ "errors"
"time"
"code.gitea.io/gitea/models/db"
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/setting/config"
+ "code.gitea.io/gitea/services/versioned_migration"
"xorm.io/xorm"
)
@@ -24,7 +25,7 @@ func InitDBEngine(ctx context.Context) (err error) {
for i := 0; i < setting.Database.DBConnectRetries; i++ {
select {
case <-ctx.Done():
- return fmt.Errorf("Aborted due to shutdown:\nin retry ORM engine initialization")
+ return errors.New("Aborted due to shutdown:\nin retry ORM engine initialization")
default:
}
log.Info("ORM engine initialization attempt #%d/%d...", i+1, setting.Database.DBConnectRetries)
@@ -41,16 +42,16 @@ func InitDBEngine(ctx context.Context) (err error) {
return nil
}
-func migrateWithSetting(x *xorm.Engine) error {
+func migrateWithSetting(ctx context.Context, x *xorm.Engine) error {
if setting.Database.AutoMigration {
- return migrations.Migrate(x)
+ return versioned_migration.Migrate(ctx, x)
}
if current, err := migrations.GetCurrentDBVersion(x); err != nil {
return err
} else if current < 0 {
// execute migrations when the database isn't initialized even if AutoMigration is false
- return migrations.Migrate(x)
+ return versioned_migration.Migrate(ctx, x)
} else if expected := migrations.ExpectedDBVersion(); current != expected {
log.Fatal(`"database.AUTO_MIGRATION" is disabled, but current database version %d is not equal to the expected version %d.`+
`You can set "database.AUTO_MIGRATION" to true or migrate manually by running "gitea [--config /path/to/app.ini] migrate"`, current, expected)
diff --git a/routers/common/errpage.go b/routers/common/errpage.go
index c0b16dbdde..9ca309931b 100644
--- a/routers/common/errpage.go
+++ b/routers/common/errpage.go
@@ -32,7 +32,7 @@ func RenderPanicErrorPage(w http.ResponseWriter, req *http.Request, err any) {
routing.UpdatePanicError(req.Context(), err)
- httpcache.SetCacheControlInHeader(w.Header(), 0, "no-transform")
+ httpcache.SetCacheControlInHeader(w.Header(), &httpcache.CacheControlOptions{NoTransform: true})
w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
tmplCtx := context.TemplateContext{}
diff --git a/routers/common/errpage_test.go b/routers/common/errpage_test.go
index dfea55f510..33aa6bb339 100644
--- a/routers/common/errpage_test.go
+++ b/routers/common/errpage_test.go
@@ -4,7 +4,6 @@
package common
import (
- "context"
"errors"
"net/http"
"net/http/httptest"
@@ -21,7 +20,7 @@ import (
func TestRenderPanicErrorPage(t *testing.T) {
w := httptest.NewRecorder()
req := &http.Request{URL: &url.URL{}}
- req = req.WithContext(reqctx.NewRequestContextForTest(context.Background()))
+ req = req.WithContext(reqctx.NewRequestContextForTest(t.Context()))
RenderPanicErrorPage(w, req, errors.New("fake panic error (for test only)"))
respContent := w.Body.String()
assert.Contains(t, respContent, `class="page-content status-page-500"`)
diff --git a/routers/common/markup.go b/routers/common/markup.go
index 533b546a2a..00b2dd07c6 100644
--- a/routers/common/markup.go
+++ b/routers/common/markup.go
@@ -39,7 +39,7 @@ func RenderMarkup(ctx *context.Base, ctxRepo *context.Repository, mode, text, ur
rctx := renderhelper.NewRenderContextSimpleDocument(ctx, baseLink).WithUseAbsoluteLink(true).
WithMarkupType(markdown.MarkupName)
if err := markdown.RenderRaw(rctx, strings.NewReader(text), ctx.Resp); err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, err.Error())
}
return
}
@@ -76,7 +76,11 @@ func RenderMarkup(ctx *context.Base, ctxRepo *context.Repository, mode, text, ur
})
rctx = rctx.WithMarkupType(markdown.MarkupName)
case "comment":
- rctx = renderhelper.NewRenderContextRepoComment(ctx, repoModel, renderhelper.RepoCommentOptions{DeprecatedOwnerName: repoOwnerName, DeprecatedRepoName: repoName})
+ rctx = renderhelper.NewRenderContextRepoComment(ctx, repoModel, renderhelper.RepoCommentOptions{
+ DeprecatedOwnerName: repoOwnerName,
+ DeprecatedRepoName: repoName,
+ FootnoteContextID: "preview",
+ })
rctx = rctx.WithMarkupType(markdown.MarkupName)
case "wiki":
rctx = renderhelper.NewRenderContextRepoWiki(ctx, repoModel, renderhelper.RepoWikiOptions{DeprecatedOwnerName: repoOwnerName, DeprecatedRepoName: repoName})
@@ -88,15 +92,15 @@ func RenderMarkup(ctx *context.Base, ctxRepo *context.Repository, mode, text, ur
})
rctx = rctx.WithMarkupType("").WithRelativePath(filePath) // render the repo file content by its extension
default:
- ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Unknown mode: %s", mode))
+ ctx.HTTPError(http.StatusUnprocessableEntity, "Unknown mode: "+mode)
return
}
rctx = rctx.WithUseAbsoluteLink(true)
if err := markup.Render(rctx, strings.NewReader(text), ctx.Resp); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
- ctx.Error(http.StatusUnprocessableEntity, err.Error())
+ ctx.HTTPError(http.StatusUnprocessableEntity, err.Error())
} else {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, err.Error())
}
return
}
diff --git a/routers/common/middleware.go b/routers/common/middleware.go
index 12b0c67b01..2ba02de8ed 100644
--- a/routers/common/middleware.go
+++ b/routers/common/middleware.go
@@ -9,6 +9,7 @@ import (
"strings"
"code.gitea.io/gitea/modules/cache"
+ "code.gitea.io/gitea/modules/gtprof"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/modules/setting"
@@ -43,14 +44,26 @@ func ProtocolMiddlewares() (handlers []any) {
func RequestContextHandler() func(h http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
- return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
- profDesc := fmt.Sprintf("%s: %s", req.Method, req.RequestURI)
+ return http.HandlerFunc(func(respOrig http.ResponseWriter, req *http.Request) {
+ // this response writer might not be the same as the one in context.Base.Resp
+ // because there might be a "gzip writer" in the middle, so the "written size" here is the compressed size
+ respWriter := context.WrapResponseWriter(respOrig)
+
+ profDesc := fmt.Sprintf("HTTP: %s %s", req.Method, req.RequestURI)
ctx, finished := reqctx.NewRequestContext(req.Context(), profDesc)
defer finished()
+ ctx, span := gtprof.GetTracer().Start(ctx, gtprof.TraceSpanHTTP)
+ req = req.WithContext(ctx)
+ defer func() {
+ chiCtx := chi.RouteContext(req.Context())
+ span.SetAttributeString(gtprof.TraceAttrHTTPRoute, chiCtx.RoutePattern())
+ span.End()
+ }()
+
defer func() {
if err := recover(); err != nil {
- RenderPanicErrorPage(resp, req, err) // it should never panic
+ RenderPanicErrorPage(respWriter, req, err) // it should never panic
}
}()
@@ -62,7 +75,7 @@ func RequestContextHandler() func(h http.Handler) http.Handler {
_ = req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
}
})
- next.ServeHTTP(context.WrapResponseWriter(resp), req)
+ next.ServeHTTP(respWriter, req)
})
}
}
@@ -71,11 +84,11 @@ func ChiRoutePathHandler() func(h http.Handler) http.Handler {
// make sure chi uses EscapedPath(RawPath) as RoutePath, then "%2f" could be handled correctly
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
- ctx := chi.RouteContext(req.Context())
+ chiCtx := chi.RouteContext(req.Context())
if req.URL.RawPath == "" {
- ctx.RoutePath = req.URL.EscapedPath()
+ chiCtx.RoutePath = req.URL.EscapedPath()
} else {
- ctx.RoutePath = req.URL.RawPath
+ chiCtx.RoutePath = req.URL.RawPath
}
next.ServeHTTP(resp, req)
})
diff --git a/routers/common/pagetmpl.go b/routers/common/pagetmpl.go
new file mode 100644
index 0000000000..c48596d48b
--- /dev/null
+++ b/routers/common/pagetmpl.go
@@ -0,0 +1,83 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package common
+
+import (
+ goctx "context"
+ "errors"
+ "sync"
+
+ activities_model "code.gitea.io/gitea/models/activities"
+ "code.gitea.io/gitea/models/db"
+ issues_model "code.gitea.io/gitea/models/issues"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/services/context"
+)
+
+// StopwatchTmplInfo is a view on a stopwatch specifically for template rendering
+type StopwatchTmplInfo struct {
+ IssueLink string
+ RepoSlug string
+ IssueIndex int64
+ Seconds int64
+}
+
+func getActiveStopwatch(ctx *context.Context) *StopwatchTmplInfo {
+ if ctx.Doer == nil {
+ return nil
+ }
+
+ _, sw, issue, err := issues_model.HasUserStopwatch(ctx, ctx.Doer.ID)
+ if err != nil {
+ if !errors.Is(err, goctx.Canceled) {
+ log.Error("Unable to HasUserStopwatch for user:%-v: %v", ctx.Doer, err)
+ }
+ return nil
+ }
+
+ if sw == nil || sw.ID == 0 {
+ return nil
+ }
+
+ return &StopwatchTmplInfo{
+ issue.Link(),
+ issue.Repo.FullName(),
+ issue.Index,
+ sw.Seconds() + 1, // ensure time is never zero in ui
+ }
+}
+
+func notificationUnreadCount(ctx *context.Context) int64 {
+ if ctx.Doer == nil {
+ return 0
+ }
+ count, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{
+ UserID: ctx.Doer.ID,
+ Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread},
+ })
+ if err != nil {
+ if !errors.Is(err, goctx.Canceled) {
+ log.Error("Unable to find notification for user:%-v: %v", ctx.Doer, err)
+ }
+ return 0
+ }
+ return count
+}
+
+type pageGlobalDataType struct {
+ IsSigned bool
+ IsSiteAdmin bool
+
+ GetNotificationUnreadCount func() int64
+ GetActiveStopwatch func() *StopwatchTmplInfo
+}
+
+func PageGlobalData(ctx *context.Context) {
+ var data pageGlobalDataType
+ data.IsSigned = ctx.Doer != nil
+ data.IsSiteAdmin = ctx.Doer != nil && ctx.Doer.IsAdmin
+ data.GetNotificationUnreadCount = sync.OnceValue(func() int64 { return notificationUnreadCount(ctx) })
+ data.GetActiveStopwatch = sync.OnceValue(func() *StopwatchTmplInfo { return getActiveStopwatch(ctx) })
+ ctx.Data["PageGlobalData"] = data
+}
diff --git a/routers/common/qos.go b/routers/common/qos.go
new file mode 100644
index 0000000000..e50fbe4f69
--- /dev/null
+++ b/routers/common/qos.go
@@ -0,0 +1,145 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package common
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "strings"
+
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/modules/web/middleware"
+ giteacontext "code.gitea.io/gitea/services/context"
+
+ "github.com/bohde/codel"
+ "github.com/go-chi/chi/v5"
+)
+
+const tplStatus503 templates.TplName = "status/503"
+
+type Priority int
+
+func (p Priority) String() string {
+ switch p {
+ case HighPriority:
+ return "high"
+ case DefaultPriority:
+ return "default"
+ case LowPriority:
+ return "low"
+ default:
+ return fmt.Sprintf("%d", p)
+ }
+}
+
+const (
+ LowPriority = Priority(-10)
+ DefaultPriority = Priority(0)
+ HighPriority = Priority(10)
+)
+
+// QoS implements quality of service for requests, based upon whether
+// or not the user is logged in. All traffic may get dropped, and
+// anonymous users are deprioritized.
+func QoS() func(next http.Handler) http.Handler {
+ if !setting.Service.QoS.Enabled {
+ return nil
+ }
+
+ maxOutstanding := setting.Service.QoS.MaxInFlightRequests
+ if maxOutstanding <= 0 {
+ maxOutstanding = 10
+ }
+
+ c := codel.NewPriority(codel.Options{
+ // The maximum number of waiting requests.
+ MaxPending: setting.Service.QoS.MaxWaitingRequests,
+ // The maximum number of in-flight requests.
+ MaxOutstanding: maxOutstanding,
+ // The target latency that a blocked request should wait
+ // for. After this, it might be dropped.
+ TargetLatency: setting.Service.QoS.TargetWaitTime,
+ })
+
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ ctx := req.Context()
+
+ priority := requestPriority(ctx)
+
+ // Check if the request can begin processing.
+ err := c.Acquire(ctx, int(priority))
+ if err != nil {
+ log.Error("QoS error, dropping request of priority %s: %v", priority, err)
+ renderServiceUnavailable(w, req)
+ return
+ }
+
+ // Release long-polling immediately, so they don't always
+ // take up an in-flight request
+ if strings.Contains(req.URL.Path, "/user/events") {
+ c.Release()
+ } else {
+ defer c.Release()
+ }
+
+ next.ServeHTTP(w, req)
+ })
+ }
+}
+
+// requestPriority assigns a priority value for a request based upon
+// whether the user is logged in and how expensive the endpoint is
+func requestPriority(ctx context.Context) Priority {
+ // If the user is logged in, assign high priority.
+ data := middleware.GetContextData(ctx)
+ if _, ok := data[middleware.ContextDataKeySignedUser].(*user_model.User); ok {
+ return HighPriority
+ }
+
+ rctx := chi.RouteContext(ctx)
+ if rctx == nil {
+ return DefaultPriority
+ }
+
+ // If we're operating in the context of a repo, assign low priority
+ routePattern := rctx.RoutePattern()
+ if strings.HasPrefix(routePattern, "/{username}/{reponame}/") {
+ return LowPriority
+ }
+
+ return DefaultPriority
+}
+
+// renderServiceUnavailable will render an HTTP 503 Service
+// Unavailable page, providing HTML if the client accepts it.
+func renderServiceUnavailable(w http.ResponseWriter, req *http.Request) {
+ acceptsHTML := false
+ for _, part := range req.Header["Accept"] {
+ if strings.Contains(part, "text/html") {
+ acceptsHTML = true
+ break
+ }
+ }
+
+ // If the client doesn't accept HTML, then render a plain text response
+ if !acceptsHTML {
+ http.Error(w, "503 Service Unavailable", http.StatusServiceUnavailable)
+ return
+ }
+
+ tmplCtx := giteacontext.TemplateContext{}
+ tmplCtx["Locale"] = middleware.Locale(w, req)
+ ctxData := middleware.GetContextData(req.Context())
+ err := templates.HTMLRenderer().HTML(w, http.StatusServiceUnavailable, tplStatus503, ctxData, tmplCtx)
+ if err != nil {
+ log.Error("Error occurs again when rendering service unavailable page: %v", err)
+ w.WriteHeader(http.StatusInternalServerError)
+ _, _ = w.Write([]byte("Internal server error, please collect error logs and report to Gitea issue tracker"))
+ }
+}
diff --git a/routers/common/qos_test.go b/routers/common/qos_test.go
new file mode 100644
index 0000000000..850a5f51db
--- /dev/null
+++ b/routers/common/qos_test.go
@@ -0,0 +1,91 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package common
+
+import (
+ "net/http"
+ "testing"
+
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/services/contexttest"
+
+ "github.com/go-chi/chi/v5"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRequestPriority(t *testing.T) {
+ type test struct {
+ Name string
+ User *user_model.User
+ RoutePattern string
+ Expected Priority
+ }
+
+ cases := []test{
+ {
+ Name: "Logged In",
+ User: &user_model.User{},
+ Expected: HighPriority,
+ },
+ {
+ Name: "Sign In",
+ RoutePattern: "/user/login",
+ Expected: DefaultPriority,
+ },
+ {
+ Name: "Repo Home",
+ RoutePattern: "/{username}/{reponame}",
+ Expected: DefaultPriority,
+ },
+ {
+ Name: "User Repo",
+ RoutePattern: "/{username}/{reponame}/src/branch/main",
+ Expected: LowPriority,
+ },
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.Name, func(t *testing.T) {
+ ctx, _ := contexttest.MockContext(t, "")
+
+ if tc.User != nil {
+ data := middleware.GetContextData(ctx)
+ data[middleware.ContextDataKeySignedUser] = tc.User
+ }
+
+ rctx := chi.RouteContext(ctx)
+ rctx.RoutePatterns = []string{tc.RoutePattern}
+
+ assert.Exactly(t, tc.Expected, requestPriority(ctx))
+ })
+ }
+}
+
+func TestRenderServiceUnavailable(t *testing.T) {
+ t.Run("HTML", func(t *testing.T) {
+ ctx, resp := contexttest.MockContext(t, "")
+ ctx.Req.Header.Set("Accept", "text/html")
+
+ renderServiceUnavailable(resp, ctx.Req)
+ assert.Equal(t, http.StatusServiceUnavailable, resp.Code)
+ assert.Contains(t, resp.Header().Get("Content-Type"), "text/html")
+
+ body := resp.Body.String()
+ assert.Contains(t, body, `lang="en-US"`)
+ assert.Contains(t, body, "503 Service Unavailable")
+ })
+
+ t.Run("plain", func(t *testing.T) {
+ ctx, resp := contexttest.MockContext(t, "")
+ ctx.Req.Header.Set("Accept", "text/plain")
+
+ renderServiceUnavailable(resp, ctx.Req)
+ assert.Equal(t, http.StatusServiceUnavailable, resp.Code)
+ assert.Contains(t, resp.Header().Get("Content-Type"), "text/plain")
+
+ body := resp.Body.String()
+ assert.Contains(t, body, "503 Service Unavailable")
+ })
+}
diff --git a/routers/common/serve.go b/routers/common/serve.go
index 446908db75..862230b30f 100644
--- a/routers/common/serve.go
+++ b/routers/common/serve.go
@@ -5,17 +5,21 @@ package common
import (
"io"
+ "path"
"time"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/services/context"
)
// ServeBlob download a git.Blob
-func ServeBlob(ctx *context.Base, filePath string, blob *git.Blob, lastModified *time.Time) error {
+func ServeBlob(ctx *context.Base, repo *repo_model.Repository, filePath string, blob *git.Blob, lastModified *time.Time) error {
if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) {
return nil
}
@@ -30,14 +34,19 @@ func ServeBlob(ctx *context.Base, filePath string, blob *git.Blob, lastModified
}
}()
- httplib.ServeContentByReader(ctx.Req, ctx.Resp, filePath, blob.Size(), dataRc)
+ _ = repo.LoadOwner(ctx)
+ httplib.ServeContentByReader(ctx.Req, ctx.Resp, blob.Size(), dataRc, &httplib.ServeHeaderOptions{
+ Filename: path.Base(filePath),
+ CacheIsPublic: !repo.IsPrivate && repo.Owner != nil && repo.Owner.Visibility == structs.VisibleTypePublic,
+ CacheDuration: setting.StaticCacheTime,
+ })
return nil
}
func ServeContentByReader(ctx *context.Base, filePath string, size int64, reader io.Reader) {
- httplib.ServeContentByReader(ctx.Req, ctx.Resp, filePath, size, reader)
+ httplib.ServeContentByReader(ctx.Req, ctx.Resp, size, reader, &httplib.ServeHeaderOptions{Filename: path.Base(filePath)})
}
func ServeContentByReadSeeker(ctx *context.Base, filePath string, modTime *time.Time, reader io.ReadSeeker) {
- httplib.ServeContentByReadSeeker(ctx.Req, ctx.Resp, filePath, modTime, reader)
+ httplib.ServeContentByReadSeeker(ctx.Req, ctx.Resp, modTime, reader, &httplib.ServeHeaderOptions{Filename: path.Base(filePath)})
}
diff --git a/routers/init.go b/routers/init.go
index e7aa765bf0..744feee2f0 100644
--- a/routers/init.go
+++ b/routers/init.go
@@ -213,7 +213,7 @@ func NormalRoutes() *web.Router {
}
r.NotFound(func(w http.ResponseWriter, req *http.Request) {
- routing.UpdateFuncInfo(req.Context(), routing.GetFuncInfo(http.NotFound, "GlobalNotFound"))
+ defer routing.RecordFuncInfo(req.Context(), routing.GetFuncInfo(http.NotFound, "GlobalNotFound"))()
http.NotFound(w, req)
})
return r
diff --git a/routers/install/install.go b/routers/install/install.go
index 8a1d57aa0b..dc8f209f3b 100644
--- a/routers/install/install.go
+++ b/routers/install/install.go
@@ -5,19 +5,18 @@
package install
import (
- "fmt"
"net/http"
"net/mail"
"os"
"os/exec"
"path/filepath"
+ "slices"
"strconv"
"strings"
"time"
"code.gitea.io/gitea/models/db"
db_install "code.gitea.io/gitea/models/db/install"
- "code.gitea.io/gitea/models/migrations"
system_model "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/password/hash"
@@ -37,6 +36,7 @@ import (
auth_service "code.gitea.io/gitea/services/auth"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
+ "code.gitea.io/gitea/services/versioned_migration"
"gitea.com/go-chi/session"
)
@@ -64,7 +64,6 @@ func Contexter() func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
base := context.NewBaseContext(resp, req)
ctx := context.NewWebContext(base, rnd, session.GetSession(req))
- ctx.SetContextValue(context.WebContextKey, ctx)
ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
ctx.Data.MergeFrom(reqctx.ContextData{
"Title": ctx.Locale.Tr("install.install"),
@@ -100,14 +99,7 @@ func Install(ctx *context.Context) {
form.SSLMode = setting.Database.SSLMode
curDBType := setting.Database.Type.String()
- var isCurDBTypeSupported bool
- for _, dbType := range setting.SupportedDatabaseTypes {
- if dbType == curDBType {
- isCurDBTypeSupported = true
- break
- }
- }
- if !isCurDBTypeSupported {
+ if !slices.Contains(setting.SupportedDatabaseTypes, curDBType) {
curDBType = "mysql"
}
ctx.Data["CurDbType"] = curDBType
@@ -152,7 +144,7 @@ func Install(ctx *context.Context) {
form.DisableRegistration = setting.Service.DisableRegistration
form.AllowOnlyExternalRegistration = setting.Service.AllowOnlyExternalRegistration
form.EnableCaptcha = setting.Service.EnableCaptcha
- form.RequireSignInView = setting.Service.RequireSignInView
+ form.RequireSignInView = setting.Service.RequireSignInViewStrict
form.DefaultKeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate
form.DefaultAllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization
form.DefaultEnableTimetracking = setting.Service.DefaultEnableTimetracking
@@ -360,7 +352,7 @@ func SubmitInstall(ctx *context.Context) {
}
// Init the engine with migration
- if err = db.InitEngineWithMigration(ctx, migrations.Migrate); err != nil {
+ if err = db.InitEngineWithMigration(ctx, versioned_migration.Migrate); err != nil {
db.UnsetDefaultEngine()
ctx.Data["Err_DbSetting"] = true
ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, &form)
@@ -399,7 +391,7 @@ func SubmitInstall(ctx *context.Context) {
cfg.Section("server").Key("DISABLE_SSH").SetValue("true")
} else {
cfg.Section("server").Key("DISABLE_SSH").SetValue("false")
- cfg.Section("server").Key("SSH_PORT").SetValue(fmt.Sprint(form.SSHPort))
+ cfg.Section("server").Key("SSH_PORT").SetValue(strconv.Itoa(form.SSHPort))
}
if form.LFSRootPath != "" {
@@ -430,10 +422,10 @@ func SubmitInstall(ctx *context.Context) {
} else {
cfg.Section("mailer").Key("ENABLED").SetValue("false")
}
- cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").SetValue(fmt.Sprint(form.RegisterConfirm))
- cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(fmt.Sprint(form.MailNotify))
+ cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").SetValue(strconv.FormatBool(form.RegisterConfirm))
+ cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(strconv.FormatBool(form.MailNotify))
- cfg.Section("server").Key("OFFLINE_MODE").SetValue(fmt.Sprint(form.OfflineMode))
+ cfg.Section("server").Key("OFFLINE_MODE").SetValue(strconv.FormatBool(form.OfflineMode))
if err := system_model.SetSettings(ctx, map[string]string{
setting.Config().Picture.DisableGravatar.DynKey(): strconv.FormatBool(form.DisableGravatar),
setting.Config().Picture.EnableFederatedAvatar.DynKey(): strconv.FormatBool(form.EnableFederatedAvatar),
@@ -442,17 +434,17 @@ func SubmitInstall(ctx *context.Context) {
return
}
- cfg.Section("openid").Key("ENABLE_OPENID_SIGNIN").SetValue(fmt.Sprint(form.EnableOpenIDSignIn))
- cfg.Section("openid").Key("ENABLE_OPENID_SIGNUP").SetValue(fmt.Sprint(form.EnableOpenIDSignUp))
- cfg.Section("service").Key("DISABLE_REGISTRATION").SetValue(fmt.Sprint(form.DisableRegistration))
- cfg.Section("service").Key("ALLOW_ONLY_EXTERNAL_REGISTRATION").SetValue(fmt.Sprint(form.AllowOnlyExternalRegistration))
- cfg.Section("service").Key("ENABLE_CAPTCHA").SetValue(fmt.Sprint(form.EnableCaptcha))
- cfg.Section("service").Key("REQUIRE_SIGNIN_VIEW").SetValue(fmt.Sprint(form.RequireSignInView))
- cfg.Section("service").Key("DEFAULT_KEEP_EMAIL_PRIVATE").SetValue(fmt.Sprint(form.DefaultKeepEmailPrivate))
- cfg.Section("service").Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").SetValue(fmt.Sprint(form.DefaultAllowCreateOrganization))
- cfg.Section("service").Key("DEFAULT_ENABLE_TIMETRACKING").SetValue(fmt.Sprint(form.DefaultEnableTimetracking))
- cfg.Section("service").Key("NO_REPLY_ADDRESS").SetValue(fmt.Sprint(form.NoReplyAddress))
- cfg.Section("cron.update_checker").Key("ENABLED").SetValue(fmt.Sprint(form.EnableUpdateChecker))
+ cfg.Section("openid").Key("ENABLE_OPENID_SIGNIN").SetValue(strconv.FormatBool(form.EnableOpenIDSignIn))
+ cfg.Section("openid").Key("ENABLE_OPENID_SIGNUP").SetValue(strconv.FormatBool(form.EnableOpenIDSignUp))
+ cfg.Section("service").Key("DISABLE_REGISTRATION").SetValue(strconv.FormatBool(form.DisableRegistration))
+ cfg.Section("service").Key("ALLOW_ONLY_EXTERNAL_REGISTRATION").SetValue(strconv.FormatBool(form.AllowOnlyExternalRegistration))
+ cfg.Section("service").Key("ENABLE_CAPTCHA").SetValue(strconv.FormatBool(form.EnableCaptcha))
+ cfg.Section("service").Key("REQUIRE_SIGNIN_VIEW").SetValue(strconv.FormatBool(form.RequireSignInView))
+ cfg.Section("service").Key("DEFAULT_KEEP_EMAIL_PRIVATE").SetValue(strconv.FormatBool(form.DefaultKeepEmailPrivate))
+ cfg.Section("service").Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").SetValue(strconv.FormatBool(form.DefaultAllowCreateOrganization))
+ cfg.Section("service").Key("DEFAULT_ENABLE_TIMETRACKING").SetValue(strconv.FormatBool(form.DefaultEnableTimetracking))
+ cfg.Section("service").Key("NO_REPLY_ADDRESS").SetValue(form.NoReplyAddress)
+ cfg.Section("cron.update_checker").Key("ENABLED").SetValue(strconv.FormatBool(form.EnableUpdateChecker))
cfg.Section("session").Key("PROVIDER").SetValue("file")
@@ -608,6 +600,8 @@ func SubmitInstall(ctx *context.Context) {
// InstallDone shows the "post-install" page, makes it easier to develop the page.
// The name is not called as "PostInstall" to avoid misinterpretation as a handler for "POST /install"
-func InstallDone(ctx *context.Context) { //nolint
+func InstallDone(ctx *context.Context) { //nolint:revive // export stutter
+ hasUsers, _ := user_model.HasUsers(ctx)
+ ctx.Data["IsAccountCreated"] = hasUsers.HasAnyUser
ctx.HTML(http.StatusOK, tplPostInstall)
}
diff --git a/routers/install/routes.go b/routers/install/routes.go
index 7309a405d4..bc7a0eb48c 100644
--- a/routers/install/routes.go
+++ b/routers/install/routes.go
@@ -36,7 +36,7 @@ func Routes() *web.Router {
func installNotFound(w http.ResponseWriter, req *http.Request) {
w.Header().Add("Content-Type", "text/html; charset=utf-8")
- w.Header().Add("Refresh", fmt.Sprintf("1; url=%s", setting.AppSubURL+"/"))
+ w.Header().Add("Refresh", "1; url="+setting.AppSubURL+"/")
// do not use 30x status, because the "post-install" page needs to use 404/200 to detect if Gitea has been installed.
// the fetch API could follow 30x requests to the page with 200 status.
w.WriteHeader(http.StatusNotFound)
diff --git a/routers/install/routes_test.go b/routers/install/routes_test.go
index 2aa7f5d7b7..e8902ba3f1 100644
--- a/routers/install/routes_test.go
+++ b/routers/install/routes_test.go
@@ -4,6 +4,7 @@
package install
import (
+ "net/http"
"net/http/httptest"
"testing"
@@ -17,20 +18,20 @@ func TestRoutes(t *testing.T) {
assert.NotNil(t, r)
w := httptest.NewRecorder()
- req := httptest.NewRequest("GET", "/", nil)
+ req := httptest.NewRequest(http.MethodGet, "/", nil)
r.ServeHTTP(w, req)
- assert.EqualValues(t, 200, w.Code)
+ assert.Equal(t, 200, w.Code)
assert.Contains(t, w.Body.String(), `class="page-content install"`)
w = httptest.NewRecorder()
- req = httptest.NewRequest("GET", "/no-such", nil)
+ req = httptest.NewRequest(http.MethodGet, "/no-such", nil)
r.ServeHTTP(w, req)
- assert.EqualValues(t, 404, w.Code)
+ assert.Equal(t, 404, w.Code)
w = httptest.NewRecorder()
- req = httptest.NewRequest("GET", "/assets/img/gitea.svg", nil)
+ req = httptest.NewRequest(http.MethodGet, "/assets/img/gitea.svg", nil)
r.ServeHTTP(w, req)
- assert.EqualValues(t, 200, w.Code)
+ assert.Equal(t, 200, w.Code)
}
func TestMain(m *testing.M) {
diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go
index af40cb3988..e8bef7d6c1 100644
--- a/routers/private/hook_post_receive.go
+++ b/routers/private/hook_post_receive.go
@@ -8,21 +8,20 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
access_model "code.gitea.io/gitea/models/perm/access"
- pull_model "code.gitea.io/gitea/models/pull"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/cache"
+ "code.gitea.io/gitea/modules/cachegroup"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
- timeutil "code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
gitea_context "code.gitea.io/gitea/services/context"
@@ -208,25 +207,19 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
return
}
- cols := make([]string, 0, 2)
-
- if isPrivate.Has() {
+ // FIXME: these options are not quite right, for example: changing visibility should do more works than just setting the is_private flag
+ // These options should only be used for "push-to-create"
+ if isPrivate.Has() && repo.IsPrivate != isPrivate.Value() {
+ // TODO: it needs to do more work
repo.IsPrivate = isPrivate.Value()
- cols = append(cols, "is_private")
+ if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_private"); err != nil {
+ ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "Failed to change visibility"})
+ }
}
-
- if isTemplate.Has() {
+ if isTemplate.Has() && repo.IsTemplate != isTemplate.Value() {
repo.IsTemplate = isTemplate.Value()
- cols = append(cols, "is_template")
- }
-
- if len(cols) > 0 {
- if err := repo_model.UpdateRepositoryCols(ctx, repo, cols...); err != nil {
- log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
- ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
- Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
- })
- return
+ if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_template"); err != nil {
+ ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "Failed to change template status"})
}
}
}
@@ -305,14 +298,11 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
}
if pr == nil {
- if repo.IsFork {
- branch = fmt.Sprintf("%s:%s", repo.OwnerName, branch)
- }
results = append(results, private.HookPostReceiveBranchResult{
Message: setting.Git.PullRequestPushMessage && baseRepo.AllowsPulls(ctx),
Create: true,
Branch: branch,
- URL: fmt.Sprintf("%s/compare/%s...%s", baseRepo.HTMLURL(), util.PathEscapeSegments(baseRepo.DefaultBranch), util.PathEscapeSegments(branch)),
+ URL: fmt.Sprintf("%s/pulls/new/%s", repo.HTMLURL(), util.PathEscapeSegments(branch)),
})
} else {
results = append(results, private.HookPostReceiveBranchResult{
@@ -331,9 +321,7 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
}
func loadContextCacheUser(ctx context.Context, id int64) (*user_model.User, error) {
- return cache.GetWithContextCache(ctx, "hook_post_receive_user", id, func() (*user_model.User, error) {
- return user_model.GetUserByID(ctx, id)
- })
+ return cache.GetWithContextCache(ctx, cachegroup.User, id, user_model.GetUserByID)
}
// handlePullRequestMerging handle pull request merging, a pull request action should push at least 1 commit
@@ -359,21 +347,9 @@ func handlePullRequestMerging(ctx *gitea_context.PrivateContext, opts *private.H
return
}
- pr.MergedCommitID = updates[len(updates)-1].NewCommitID
- pr.MergedUnix = timeutil.TimeStampNow()
- pr.Merger = pusher
- pr.MergerID = pusher.ID
- err = db.WithTx(ctx, func(ctx context.Context) error {
- // Removing an auto merge pull and ignore if not exist
- if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) {
- return fmt.Errorf("DeleteScheduledAutoMerge[%d]: %v", opts.PullRequestID, err)
- }
- if _, err := pull_service.SetMerged(ctx, pr); err != nil {
- return fmt.Errorf("SetMerged failed: %s/%s Error: %v", ownerName, repoName, err)
- }
- return nil
- })
- if err != nil {
+ // FIXME: Maybe we need a `PullRequestStatusMerged` status for PRs that are merged, currently we use the previous status
+ // here to keep it as before, that maybe PullRequestStatusMergeable
+ if _, err := pull_service.SetMerged(ctx, pr, updates[len(updates)-1].NewCommitID, timeutil.TimeStampNow(), pusher, pr.Status); err != nil {
log.Error("Failed to update PR to merged: %v", err)
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "Failed to update PR to merged"})
}
diff --git a/routers/private/hook_post_receive_test.go b/routers/private/hook_post_receive_test.go
index a089739d15..ca721b16d1 100644
--- a/routers/private/hook_post_receive_test.go
+++ b/routers/private/hook_post_receive_test.go
@@ -27,7 +27,7 @@ func TestHandlePullRequestMerging(t *testing.T) {
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
- err = pull_model.ScheduleAutoMerge(db.DefaultContext, user1, pr.ID, repo_model.MergeStyleSquash, "squash merge a pr")
+ err = pull_model.ScheduleAutoMerge(db.DefaultContext, user1, pr.ID, repo_model.MergeStyleSquash, "squash merge a pr", false)
assert.NoError(t, err)
autoMerge := unittest.AssertExistsAndLoadBean(t, &pull_model.AutoMerge{PullID: pr.ID})
@@ -43,7 +43,7 @@ func TestHandlePullRequestMerging(t *testing.T) {
pr, err = issues_model.GetPullRequestByID(db.DefaultContext, pr.ID)
assert.NoError(t, err)
assert.True(t, pr.HasMerged)
- assert.EqualValues(t, "01234567", pr.MergedCommitID)
+ assert.Equal(t, "01234567", pr.MergedCommitID)
unittest.AssertNotExistsBean(t, &pull_model.AutoMerge{ID: autoMerge.ID})
}
diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go
index eb7bb2b480..dd9d0bc15e 100644
--- a/routers/private/hook_pre_receive.go
+++ b/routers/private/hook_pre_receive.go
@@ -4,6 +4,7 @@
package private
import (
+ "errors"
"fmt"
"net/http"
"os"
@@ -16,6 +17,7 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/web"
@@ -186,7 +188,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
// 2. Disallow force pushes to protected branches
if oldCommitID != objectFormat.EmptyObjectID().String() {
- output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+newCommitID).RunStdString(&git.RunOpts{Dir: repo.RepoPath(), Env: ctx.env})
+ output, _, err := git.NewCommand("rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+newCommitID).RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath(), Env: ctx.env})
if err != nil {
log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, newCommitID, repo, err)
ctx.JSON(http.StatusInternalServerError, private.Response{
@@ -310,13 +312,13 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
if isForcePush {
log.Warn("Forbidden: User %d is not allowed to force-push to protected branch: %s in %-v", ctx.opts.UserID, branchName, repo)
ctx.JSON(http.StatusForbidden, private.Response{
- UserMsg: fmt.Sprintf("Not allowed to force-push to protected branch %s", branchName),
+ UserMsg: "Not allowed to force-push to protected branch " + branchName,
})
return
}
log.Warn("Forbidden: User %d is not allowed to push to protected branch: %s in %-v", ctx.opts.UserID, branchName, repo)
ctx.JSON(http.StatusForbidden, private.Response{
- UserMsg: fmt.Sprintf("Not allowed to push to protected branch %s", branchName),
+ UserMsg: "Not allowed to push to protected branch " + branchName,
})
return
}
@@ -352,7 +354,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
if !allowedMerge {
log.Warn("Forbidden: User %d is not allowed to push to protected branch: %s in %-v and is not allowed to merge pr #%d", ctx.opts.UserID, branchName, repo, pr.Index)
ctx.JSON(http.StatusForbidden, private.Response{
- UserMsg: fmt.Sprintf("Not allowed to push to protected branch %s", branchName),
+ UserMsg: "Not allowed to push to protected branch " + branchName,
})
return
}
@@ -373,7 +375,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
// Check all status checks and reviews are ok
if err := pull_service.CheckPullBranchProtections(ctx, pr, true); err != nil {
- if pull_service.IsErrDisallowedToMerge(err) {
+ if errors.Is(err, pull_service.ErrNotReadyToMerge) {
log.Warn("Forbidden: User %d is not allowed push to protected branch %s in %-v and pr #%d is not ready to be merged: %s", ctx.opts.UserID, branchName, repo, pr.Index, err.Error())
ctx.JSON(http.StatusForbidden, private.Response{
UserMsg: fmt.Sprintf("Not allowed to push to protected branch %s and pr #%d is not ready to be merged: %s", branchName, ctx.opts.PullRequestID, err.Error()),
@@ -446,14 +448,11 @@ func preReceiveFor(ctx *preReceiveContext, refFullName git.RefName) {
baseBranchName := refFullName.ForBranchName()
- baseBranchExist := false
- if ctx.Repo.GitRepo.IsBranchExist(baseBranchName) {
- baseBranchExist = true
- }
+ baseBranchExist := gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, baseBranchName)
if !baseBranchExist {
for p, v := range baseBranchName {
- if v == '/' && ctx.Repo.GitRepo.IsBranchExist(baseBranchName[:p]) && p != len(baseBranchName)-1 {
+ if v == '/' && gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, baseBranchName[:p]) && p != len(baseBranchName)-1 {
baseBranchExist = true
break
}
diff --git a/routers/private/hook_proc_receive.go b/routers/private/hook_proc_receive.go
index efb3f5831e..4076a57dba 100644
--- a/routers/private/hook_proc_receive.go
+++ b/routers/private/hook_proc_receive.go
@@ -4,9 +4,11 @@
package private
import (
+ "errors"
"net/http"
- repo_model "code.gitea.io/gitea/models/repo"
+ issues_model "code.gitea.io/gitea/models/issues"
+ user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
@@ -25,10 +27,16 @@ func HookProcReceive(ctx *gitea_context.PrivateContext) {
results, err := agit.ProcReceive(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, opts)
if err != nil {
- if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) {
- ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err.Error())
+ if errors.Is(err, issues_model.ErrMustCollaborator) {
+ ctx.JSON(http.StatusUnauthorized, private.Response{
+ Err: err.Error(), UserMsg: "You must be a collaborator to create pull request.",
+ })
+ } else if errors.Is(err, user_model.ErrBlockedUser) {
+ ctx.JSON(http.StatusUnauthorized, private.Response{
+ Err: err.Error(), UserMsg: "Cannot create pull request because you are blocked by the repository owner.",
+ })
} else {
- log.Error(err.Error())
+ log.Error("agit.ProcReceive failed: %v", err)
ctx.JSON(http.StatusInternalServerError, private.Response{
Err: err.Error(),
})
diff --git a/routers/private/hook_verification.go b/routers/private/hook_verification.go
index 764c976fa9..57d0964ead 100644
--- a/routers/private/hook_verification.go
+++ b/routers/private/hook_verification.go
@@ -6,13 +6,12 @@ package private
import (
"bufio"
"context"
- "fmt"
"io"
"os"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
+ asymkey_service "code.gitea.io/gitea/services/asymkey"
)
// This file contains commit verification functions for refs passed across in hooks
@@ -34,12 +33,12 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []
// When creating a new branch, the oldCommitID is empty, by using "newCommitID --not --all":
// List commits that are reachable by following the newCommitID, exclude "all" existing heads/tags commits
// So, it only lists the new commits received, doesn't list the commits already present in the receiving repository
- command = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(newCommitID).AddArguments("--not", "--all")
+ command = git.NewCommand("rev-list").AddDynamicArguments(newCommitID).AddArguments("--not", "--all")
} else {
- command = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID)
+ command = git.NewCommand("rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID)
}
// This is safe as force pushes are already forbidden
- err = command.Run(&git.RunOpts{
+ err = command.Run(repo.Ctx, &git.RunOpts{
Env: env,
Dir: repo.Path,
Stdout: stdoutWriter,
@@ -85,8 +84,8 @@ func readAndVerifyCommit(sha string, repo *git.Repository, env []string) error {
commitID := git.MustIDFromString(sha)
- return git.NewCommand(repo.Ctx, "cat-file", "commit").AddDynamicArguments(sha).
- Run(&git.RunOpts{
+ return git.NewCommand("cat-file", "commit").AddDynamicArguments(sha).
+ Run(repo.Ctx, &git.RunOpts{
Env: env,
Dir: repo.Path,
Stdout: stdoutWriter,
@@ -96,7 +95,7 @@ func readAndVerifyCommit(sha string, repo *git.Repository, env []string) error {
if err != nil {
return err
}
- verification := asymkey_model.ParseCommitWithSignature(ctx, commit)
+ verification := asymkey_service.ParseCommitWithSignature(ctx, commit)
if !verification.Verified {
cancel()
return &errUnverifiedCommit{
@@ -113,7 +112,7 @@ type errUnverifiedCommit struct {
}
func (e *errUnverifiedCommit) Error() string {
- return fmt.Sprintf("Unverified commit: %s", e.sha)
+ return "Unverified commit: " + e.sha
}
func isErrUnverifiedCommit(err error) bool {
diff --git a/routers/private/hook_verification_test.go b/routers/private/hook_verification_test.go
index 04445b8eaf..8653e34daa 100644
--- a/routers/private/hook_verification_test.go
+++ b/routers/private/hook_verification_test.go
@@ -4,7 +4,6 @@
package private
import (
- "context"
"testing"
"code.gitea.io/gitea/models/unittest"
@@ -18,8 +17,10 @@ var testReposDir = "tests/repos/"
func TestVerifyCommits(t *testing.T) {
unittest.PrepareTestEnv(t)
- gitRepo, err := git.OpenRepository(context.Background(), testReposDir+"repo1_hook_verification")
- defer gitRepo.Close()
+ gitRepo, err := git.OpenRepository(t.Context(), testReposDir+"repo1_hook_verification")
+ if err != nil {
+ defer gitRepo.Close()
+ }
assert.NoError(t, err)
objectFormat, err := gitRepo.GetObjectFormat()
diff --git a/routers/private/internal.go b/routers/private/internal.go
index a78c76f897..55a11aa3dd 100644
--- a/routers/private/internal.go
+++ b/routers/private/internal.go
@@ -87,8 +87,8 @@ func Routes() *web.Router {
// FIXME: it is not right to use context.Contexter here because all routes here should use PrivateContext
// Fortunately, the LFS handlers are able to handle requests without a complete web context
common.AddOwnerRepoGitLFSRoutes(r, func(ctx *context.PrivateContext) {
- webContext := &context.Context{Base: ctx.Base}
- ctx.SetContextValue(context.WebContextKey, webContext)
+ webContext := &context.Context{Base: ctx.Base} // see above, it shouldn't manually construct the web context
+ ctx.SetContextValue(context.WebContextKey, webContext) // FIXME: this is not ideal but no other way at the moment
})
})
diff --git a/routers/private/manager.go b/routers/private/manager.go
index c712bbcf21..00e52d6511 100644
--- a/routers/private/manager.go
+++ b/routers/private/manager.go
@@ -180,7 +180,7 @@ func AddLogger(ctx *context.PrivateContext) {
writerOption.Addr, _ = opts.Config["address"].(string)
writerMode.WriterOption = writerOption
default:
- panic(fmt.Sprintf("invalid log writer mode: %s", writerType))
+ panic("invalid log writer mode: " + writerType)
}
writer, err := log.NewEventWriter(opts.Writer, writerType, writerMode)
if err != nil {
diff --git a/routers/private/serv.go b/routers/private/serv.go
index ecff3b7a53..b879be0dc2 100644
--- a/routers/private/serv.go
+++ b/routers/private/serv.go
@@ -81,6 +81,7 @@ func ServCommand(ctx *context.PrivateContext) {
ownerName := ctx.PathParam("owner")
repoName := ctx.PathParam("repo")
mode := perm.AccessMode(ctx.FormInt("mode"))
+ verb := ctx.FormString("verb")
// Set the basic parts of the results to return
results := private.ServCommandResults{
@@ -286,7 +287,7 @@ func ServCommand(ctx *context.PrivateContext) {
repo.IsPrivate ||
owner.Visibility.IsPrivate() ||
(user != nil && user.IsRestricted) || // user will be nil if the key is a deploykey
- setting.Service.RequireSignInView) {
+ setting.Service.RequireSignInViewStrict) {
if key.Type == asymkey_model.KeyTypeDeploy {
if deployKey.Mode < mode {
ctx.JSON(http.StatusUnauthorized, private.Response{
@@ -295,8 +296,11 @@ func ServCommand(ctx *context.PrivateContext) {
return
}
} else {
- // Because of the special ref "refs/for" we will need to delay write permission check
- if git.DefaultFeatures().SupportProcReceive && unitType == unit.TypeCode {
+ // Because of the special ref "refs/for" (AGit) we will need to delay write permission check,
+ // AGit flow needs to write its own ref when the doer has "reader" permission (allowing to create PR).
+ // The real permission check is done in HookPreReceive (routers/private/hook_pre_receive.go).
+ // Here it should relax the permission check for "git push (git-receive-pack)", but not for others like LFS operations.
+ if git.DefaultFeatures().SupportProcReceive && unitType == unit.TypeCode && verb == git.CmdVerbReceivePack {
mode = perm.AccessModeRead
}
diff --git a/routers/utils/utils_test.go b/routers/utils/utils_test.go
index 6e7f3c33cd..cc7c888a75 100644
--- a/routers/utils/utils_test.go
+++ b/routers/utils/utils_test.go
@@ -5,6 +5,8 @@ package utils
import (
"testing"
+
+ "github.com/stretchr/testify/assert"
)
func TestSanitizeFlashErrorString(t *testing.T) {
@@ -32,9 +34,8 @@ func TestSanitizeFlashErrorString(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- if got := SanitizeFlashErrorString(tt.arg); got != tt.want {
- t.Errorf("SanitizeFlashErrorString() = '%v', want '%v'", got, tt.want)
- }
+ got := SanitizeFlashErrorString(tt.arg)
+ assert.Equal(t, tt.want, got)
})
}
}
diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go
index 3902a1efb1..0cd13acf60 100644
--- a/routers/web/admin/admin.go
+++ b/routers/web/admin/admin.go
@@ -37,6 +37,7 @@ const (
tplSelfCheck templates.TplName = "admin/self_check"
tplCron templates.TplName = "admin/cron"
tplQueue templates.TplName = "admin/queue"
+ tplPerfTrace templates.TplName = "admin/perftrace"
tplStacktrace templates.TplName = "admin/stacktrace"
tplQueueManage templates.TplName = "admin/queue_manage"
tplStats templates.TplName = "admin/stats"
diff --git a/routers/web/admin/admin_test.go b/routers/web/admin/admin_test.go
index 6c38f0b509..a568c7c5c8 100644
--- a/routers/web/admin/admin_test.go
+++ b/routers/web/admin/admin_test.go
@@ -69,7 +69,7 @@ func TestShadowPassword(t *testing.T) {
}
for _, k := range kases {
- assert.EqualValues(t, k.Result, shadowPassword(k.Provider, k.CfgItem))
+ assert.Equal(t, k.Result, shadowPassword(k.Provider, k.CfgItem))
}
}
@@ -79,7 +79,7 @@ func TestSelfCheckPost(t *testing.T) {
ctx, resp := contexttest.MockContext(t, "GET http://host/sub/admin/self_check?location_origin=http://frontend")
SelfCheckPost(ctx)
- assert.EqualValues(t, http.StatusOK, resp.Code)
+ assert.Equal(t, http.StatusOK, resp.Code)
data := struct {
Problems []string `json:"problems"`
diff --git a/routers/web/admin/applications.go b/routers/web/admin/applications.go
index aec6349f21..79c3a08808 100644
--- a/routers/web/admin/applications.go
+++ b/routers/web/admin/applications.go
@@ -4,7 +4,6 @@
package admin
import (
- "fmt"
"net/http"
"code.gitea.io/gitea/models/auth"
@@ -23,8 +22,8 @@ var (
func newOAuth2CommonHandlers() *user_setting.OAuth2CommonHandlers {
return &user_setting.OAuth2CommonHandlers{
OwnerID: 0,
- BasePathList: fmt.Sprintf("%s/-/admin/applications", setting.AppSubURL),
- BasePathEditPrefix: fmt.Sprintf("%s/-/admin/applications/oauth2", setting.AppSubURL),
+ BasePathList: setting.AppSubURL + "/-/admin/applications",
+ BasePathEditPrefix: setting.AppSubURL + "/-/admin/applications/oauth2",
TplAppEdit: tplSettingsOauth2ApplicationEdit,
}
}
diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go
index 249347e835..0f6f31b884 100644
--- a/routers/web/admin/auths.go
+++ b/routers/web/admin/auths.go
@@ -28,8 +28,6 @@ import (
"code.gitea.io/gitea/services/auth/source/sspi"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
-
- "xorm.io/xorm/convert"
)
const (
@@ -149,7 +147,6 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source {
RestrictedFilter: form.RestrictedFilter,
AllowDeactivateAll: form.AllowDeactivateAll,
Enabled: true,
- SkipLocalTwoFA: form.SkipLocalTwoFA,
}
}
@@ -163,7 +160,6 @@ func parseSMTPConfig(form forms.AuthenticationForm) *smtp.Source {
SkipVerify: form.SkipVerify,
HeloHostname: form.HeloHostname,
DisableHelo: form.DisableHelo,
- SkipLocalTwoFA: form.SkipLocalTwoFA,
}
}
@@ -181,7 +177,7 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source {
customURLMapping = nil
}
var scopes []string
- for _, s := range strings.Split(form.Oauth2Scopes, ",") {
+ for s := range strings.SplitSeq(form.Oauth2Scopes, ",") {
s = strings.TrimSpace(s)
if s != "" {
scopes = append(scopes, s)
@@ -198,7 +194,6 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source {
Scopes: scopes,
RequiredClaimName: form.Oauth2RequiredClaimName,
RequiredClaimValue: form.Oauth2RequiredClaimValue,
- SkipLocalTwoFA: form.SkipLocalTwoFA,
GroupClaimName: form.Oauth2GroupClaimName,
RestrictedGroup: form.Oauth2RestrictedGroup,
AdminGroup: form.Oauth2AdminGroup,
@@ -252,7 +247,7 @@ func NewAuthSourcePost(ctx *context.Context) {
ctx.Data["SSPIDefaultLanguage"] = ""
hasTLS := false
- var config convert.Conversion
+ var config auth.Config
switch auth.Type(form.Type) {
case auth.LDAP, auth.DLDAP:
config = parseLDAPConfig(form)
@@ -262,9 +257,8 @@ func NewAuthSourcePost(ctx *context.Context) {
hasTLS = true
case auth.PAM:
config = &pam_service.Source{
- ServiceName: form.PAMServiceName,
- EmailDomain: form.PAMEmailDomain,
- SkipLocalTwoFA: form.SkipLocalTwoFA,
+ ServiceName: form.PAMServiceName,
+ EmailDomain: form.PAMEmailDomain,
}
case auth.OAuth2:
config = parseOAuth2Config(form)
@@ -291,7 +285,7 @@ func NewAuthSourcePost(ctx *context.Context) {
return
}
default:
- ctx.Error(http.StatusBadRequest)
+ ctx.HTTPError(http.StatusBadRequest)
return
}
ctx.Data["HasTLS"] = hasTLS
@@ -302,11 +296,12 @@ func NewAuthSourcePost(ctx *context.Context) {
}
if err := auth.CreateSource(ctx, &auth.Source{
- Type: auth.Type(form.Type),
- Name: form.Name,
- IsActive: form.IsActive,
- IsSyncEnabled: form.IsSyncEnabled,
- Cfg: config,
+ Type: auth.Type(form.Type),
+ Name: form.Name,
+ IsActive: form.IsActive,
+ IsSyncEnabled: form.IsSyncEnabled,
+ TwoFactorPolicy: form.TwoFactorPolicy,
+ Cfg: config,
}); err != nil {
if auth.IsErrSourceAlreadyExist(err) {
ctx.Data["Err_Name"] = true
@@ -384,7 +379,7 @@ func EditAuthSourcePost(ctx *context.Context) {
return
}
- var config convert.Conversion
+ var config auth.Config
switch auth.Type(form.Type) {
case auth.LDAP, auth.DLDAP:
config = parseLDAPConfig(form)
@@ -413,7 +408,7 @@ func EditAuthSourcePost(ctx *context.Context) {
return
}
default:
- ctx.Error(http.StatusBadRequest)
+ ctx.HTTPError(http.StatusBadRequest)
return
}
@@ -421,6 +416,7 @@ func EditAuthSourcePost(ctx *context.Context) {
source.IsActive = form.IsActive
source.IsSyncEnabled = form.IsSyncEnabled
source.Cfg = config
+ source.TwoFactorPolicy = form.TwoFactorPolicy
if err := auth.UpdateSource(ctx, source); err != nil {
if auth.IsErrSourceAlreadyExist(err) {
ctx.Data["Err_Name"] = true
diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go
index 520f14e89f..0e5b23db6d 100644
--- a/routers/web/admin/config.go
+++ b/routers/web/admin/config.go
@@ -61,7 +61,7 @@ func TestCache(ctx *context.Context) {
func shadowPasswordKV(cfgItem, splitter string) string {
fields := strings.Split(cfgItem, splitter)
- for i := 0; i < len(fields); i++ {
+ for i := range fields {
if strings.HasPrefix(fields[i], "password=") {
fields[i] = "password=******"
break
@@ -200,7 +200,7 @@ func ChangeConfig(ctx *context.Context) {
value := ctx.FormString("value")
cfg := setting.Config()
- marshalBool := func(v string) (string, error) { //nolint:unparam
+ marshalBool := func(v string) (string, error) { //nolint:unparam // error is always nil
if b, _ := strconv.ParseBool(v); b {
return "true", nil
}
diff --git a/routers/web/admin/diagnosis.go b/routers/web/admin/diagnosis.go
index 020554a35a..5395529d66 100644
--- a/routers/web/admin/diagnosis.go
+++ b/routers/web/admin/diagnosis.go
@@ -10,17 +10,13 @@ import (
"time"
"code.gitea.io/gitea/modules/httplib"
+ "code.gitea.io/gitea/modules/tailmsg"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context"
)
func MonitorDiagnosis(ctx *context.Context) {
- seconds := ctx.FormInt64("seconds")
- if seconds <= 5 {
- seconds = 5
- }
- if seconds > 300 {
- seconds = 300
- }
+ seconds := min(max(ctx.FormInt64("seconds"), 1), 300)
httplib.ServeSetHeaders(ctx.Resp, &httplib.ServeHeaderOptions{
ContentType: "application/zip",
@@ -65,4 +61,16 @@ func MonitorDiagnosis(ctx *context.Context) {
return
}
_ = pprof.Lookup("heap").WriteTo(f, 0)
+
+ f, err = zipWriter.CreateHeader(&zip.FileHeader{Name: "perftrace.txt", Method: zip.Deflate, Modified: time.Now()})
+ if err != nil {
+ ctx.ServerError("Failed to create zip file", err)
+ return
+ }
+ for _, record := range tailmsg.GetManager().GetTraceRecorder().GetRecords() {
+ _, _ = f.Write(util.UnsafeStringToBytes(record.Time.Format(time.RFC3339)))
+ _, _ = f.Write([]byte(" "))
+ _, _ = f.Write(util.UnsafeStringToBytes((record.Content)))
+ _, _ = f.Write([]byte("\n\n"))
+ }
}
diff --git a/routers/web/admin/emails.go b/routers/web/admin/emails.go
index 23ddfa583a..51b3d584f4 100644
--- a/routers/web/admin/emails.go
+++ b/routers/web/admin/emails.go
@@ -116,7 +116,7 @@ func ActivateEmail(ctx *context.Context) {
activate, oka := truefalse[ctx.FormString("activate")]
if uid == 0 || len(email) == 0 || !okp || !oka {
- ctx.Error(http.StatusBadRequest)
+ ctx.HTTPError(http.StatusBadRequest)
return
}
diff --git a/routers/web/admin/notice.go b/routers/web/admin/notice.go
index 21a8ab0d17..e9d6abbe92 100644
--- a/routers/web/admin/notice.go
+++ b/routers/web/admin/notice.go
@@ -26,10 +26,7 @@ func Notices(ctx *context.Context) {
ctx.Data["PageIsAdminNotices"] = true
total := system_model.CountNotices(ctx)
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
notices, err := system_model.Notices(ctx, page, setting.UI.Admin.NoticePagingNum)
if err != nil {
diff --git a/routers/web/admin/orgs.go b/routers/web/admin/orgs.go
index 35e61efa17..e34f203aaf 100644
--- a/routers/web/admin/orgs.go
+++ b/routers/web/admin/orgs.go
@@ -27,7 +27,7 @@ func Organizations(ctx *context.Context) {
ctx.SetFormString("sort", UserSearchDefaultAdminSort)
}
- explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{
+ explore.RenderUserSearch(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer,
Type: user_model.UserTypeOrganization,
IncludeReserved: true, // administrator needs to list all accounts include reserved
diff --git a/routers/web/admin/packages.go b/routers/web/admin/packages.go
index 5122342259..1904bfee11 100644
--- a/routers/web/admin/packages.go
+++ b/routers/web/admin/packages.go
@@ -24,10 +24,7 @@ const (
// Packages shows all packages
func Packages(ctx *context.Context) {
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
query := ctx.FormTrim("q")
packageType := ctx.FormTrim("type")
sort := ctx.FormTrim("sort")
diff --git a/routers/web/admin/perftrace.go b/routers/web/admin/perftrace.go
new file mode 100644
index 0000000000..51ee57da10
--- /dev/null
+++ b/routers/web/admin/perftrace.go
@@ -0,0 +1,18 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package admin
+
+import (
+ "net/http"
+
+ "code.gitea.io/gitea/modules/tailmsg"
+ "code.gitea.io/gitea/services/context"
+)
+
+func PerfTrace(ctx *context.Context) {
+ monitorTraceCommon(ctx)
+ ctx.Data["PageIsAdminMonitorPerfTrace"] = true
+ ctx.Data["PerfTraceRecords"] = tailmsg.GetManager().GetTraceRecorder().GetRecords()
+ ctx.HTML(http.StatusOK, tplPerfTrace)
+}
diff --git a/routers/web/admin/stacktrace.go b/routers/web/admin/stacktrace.go
index ff751be621..2b8c2fb4af 100644
--- a/routers/web/admin/stacktrace.go
+++ b/routers/web/admin/stacktrace.go
@@ -12,10 +12,17 @@ import (
"code.gitea.io/gitea/services/context"
)
+func monitorTraceCommon(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("admin.monitor")
+ ctx.Data["PageIsAdminMonitorTrace"] = true
+ // Hide the performance trace tab in production, because it shows a lot of SQLs and is not that useful for end users.
+ // To avoid confusing end users, do not let them know this tab. End users should "download diagnosis report" instead.
+ ctx.Data["ShowAdminPerformanceTraceTab"] = !setting.IsProd
+}
+
// Stacktrace show admin monitor goroutines page
func Stacktrace(ctx *context.Context) {
- ctx.Data["Title"] = ctx.Tr("admin.monitor")
- ctx.Data["PageIsAdminMonitorStacktrace"] = true
+ monitorTraceCommon(ctx)
ctx.Data["GoroutineCount"] = runtime.NumGoroutine()
diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go
index f6a3af1c86..27577cd35b 100644
--- a/routers/web/admin/users.go
+++ b/routers/web/admin/users.go
@@ -21,8 +21,8 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/web/explore"
user_setting "code.gitea.io/gitea/routers/web/user/setting"
@@ -65,18 +65,18 @@ func Users(ctx *context.Context) {
"SortType": sortType,
}
- explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{
+ explore.RenderUserSearch(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer,
Type: user_model.UserTypeIndividual,
ListOptions: db.ListOptions{
PageSize: setting.UI.Admin.UserPagingNum,
},
SearchByEmail: true,
- IsActive: util.OptionalBoolParse(statusFilterMap["is_active"]),
- IsAdmin: util.OptionalBoolParse(statusFilterMap["is_admin"]),
- IsRestricted: util.OptionalBoolParse(statusFilterMap["is_restricted"]),
- IsTwoFactorEnabled: util.OptionalBoolParse(statusFilterMap["is_2fa_enabled"]),
- IsProhibitLogin: util.OptionalBoolParse(statusFilterMap["is_prohibit_login"]),
+ IsActive: optional.ParseBool(statusFilterMap["is_active"]),
+ IsAdmin: optional.ParseBool(statusFilterMap["is_admin"]),
+ IsRestricted: optional.ParseBool(statusFilterMap["is_restricted"]),
+ IsTwoFactorEnabled: optional.ParseBool(statusFilterMap["is_2fa_enabled"]),
+ IsProhibitLogin: optional.ParseBool(statusFilterMap["is_prohibit_login"]),
IncludeReserved: true, // administrator needs to list all accounts include reserved, bot, remote ones
}, tplUsers)
}
@@ -269,7 +269,7 @@ func ViewUser(ctx *context.Context) {
return
}
- repos, count, err := repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
+ repos, count, err := repo_model.SearchRepository(ctx, repo_model.SearchRepoOptions{
ListOptions: db.ListOptionsAll,
OwnerID: u.ID,
OrderBy: db.SearchOrderByAlphabetically,
@@ -293,9 +293,9 @@ func ViewUser(ctx *context.Context) {
ctx.Data["EmailsTotal"] = len(emails)
orgs, err := db.Find[org_model.Organization](ctx, org_model.FindOrgOptions{
- ListOptions: db.ListOptionsAll,
- UserID: u.ID,
- IncludePrivate: true,
+ ListOptions: db.ListOptionsAll,
+ UserID: u.ID,
+ IncludeVisibility: structs.VisibleTypePrivate,
})
if err != nil {
ctx.ServerError("FindOrgs", err)
@@ -432,7 +432,7 @@ func EditUserPost(ctx *context.Context) {
Website: optional.Some(form.Website),
Location: optional.Some(form.Location),
IsActive: optional.Some(form.Active),
- IsAdmin: optional.Some(form.Admin),
+ IsAdmin: user_service.UpdateOptionFieldFromValue(form.Admin),
AllowGitHook: optional.Some(form.AllowGitHook),
AllowImportLocal: optional.Some(form.AllowImportLocal),
MaxRepoCreation: optional.Some(form.MaxRepoCreation),
diff --git a/routers/web/auth/2fa.go b/routers/web/auth/2fa.go
index fe363fe90a..d15d33dfd4 100644
--- a/routers/web/auth/2fa.go
+++ b/routers/web/auth/2fa.go
@@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/session"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/web"
@@ -87,6 +88,7 @@ func TwoFactorPost(ctx *context.Context) {
return
}
+ _ = ctx.Session.Set(session.KeyUserHasTwoFactorAuth, true)
handleSignIn(ctx, u, remember)
return
}
diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go
index 3fe1d5970e..94f75f69ff 100644
--- a/routers/web/auth/auth.go
+++ b/routers/web/auth/auth.go
@@ -76,6 +76,10 @@ func autoSignIn(ctx *context.Context) (bool, error) {
}
return false, nil
}
+ userHasTwoFactorAuth, err := auth.HasTwoFactorOrWebAuthn(ctx, u.ID)
+ if err != nil {
+ return false, fmt.Errorf("HasTwoFactorOrWebAuthn: %w", err)
+ }
isSucceed = true
@@ -87,9 +91,9 @@ func autoSignIn(ctx *context.Context) (bool, error) {
ctx.SetSiteCookie(setting.CookieRememberName, nt.ID+":"+token, setting.LogInRememberDays*timeutil.Day)
if err := updateSession(ctx, nil, map[string]any{
- // Set session IDs
- "uid": u.ID,
- "uname": u.Name,
+ session.KeyUID: u.ID,
+ session.KeyUname: u.Name,
+ session.KeyUserHasTwoFactorAuth: userHasTwoFactorAuth,
}); err != nil {
return false, fmt.Errorf("unable to updateSession: %w", err)
}
@@ -169,6 +173,7 @@ func prepareSignInPageData(ctx *context.Context) {
ctx.Data["PageIsLogin"] = true
ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx)
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
+ ctx.Data["EnablePasskeyAuth"] = setting.Service.EnablePasskeyAuth
if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin {
context.SetCaptchaData(ctx)
@@ -191,7 +196,7 @@ func SignIn(ctx *context.Context) {
// SignInPost response for sign in request
func SignInPost(ctx *context.Context) {
if !setting.Service.EnablePasswordSignInForm {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
@@ -238,9 +243,8 @@ func SignInPost(ctx *context.Context) {
}
// Now handle 2FA:
-
// First of all if the source can skip local two fa we're done
- if skipper, ok := source.Cfg.(auth_service.LocalTwoFASkipper); ok && skipper.IsSkipLocalTwoFA() {
+ if source.TwoFactorShouldSkip() {
handleSignIn(ctx, u, form.Remember)
return
}
@@ -261,7 +265,7 @@ func SignInPost(ctx *context.Context) {
}
if !hasTOTPtwofa && !hasWebAuthnTwofa {
- // No two factor auth configured we can sign in the user
+ // No two-factor auth configured we can sign in the user
handleSignIn(ctx, u, form.Remember)
return
}
@@ -310,8 +314,14 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
ctx.SetSiteCookie(setting.CookieRememberName, nt.ID+":"+token, setting.LogInRememberDays*timeutil.Day)
}
+ userHasTwoFactorAuth, err := auth.HasTwoFactorOrWebAuthn(ctx, u.ID)
+ if err != nil {
+ ctx.ServerError("HasTwoFactorOrWebAuthn", err)
+ return setting.AppSubURL + "/"
+ }
+
if err := updateSession(ctx, []string{
- // Delete the openid, 2fa and linkaccount data
+ // Delete the openid, 2fa and link_account data
"openid_verified_uri",
"openid_signin_remember",
"openid_determined_email",
@@ -320,8 +330,9 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
"twofaRemember",
"linkAccount",
}, map[string]any{
- "uid": u.ID,
- "uname": u.Name,
+ session.KeyUID: u.ID,
+ session.KeyUname: u.Name,
+ session.KeyUserHasTwoFactorAuth: userHasTwoFactorAuth,
}); err != nil {
ctx.ServerError("RegenerateSession", err)
return setting.AppSubURL + "/"
@@ -410,9 +421,11 @@ func SignOut(ctx *context.Context) {
// SignUp render the register page
func SignUp(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("sign_up")
-
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up"
+ hasUsers, _ := user_model.HasUsers(ctx)
+ ctx.Data["IsFirstTimeRegistration"] = !hasUsers.HasAnyUser
+
oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true))
if err != nil {
ctx.ServerError("UserSignUp", err)
@@ -455,7 +468,7 @@ func SignUpPost(ctx *context.Context) {
// Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true
if setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
@@ -533,7 +546,8 @@ func createUserInContext(ctx *context.Context, tpl templates.TplName, form any,
}
if err := user_model.CreateUser(ctx, u, meta, overwrites); err != nil {
if allowLink && (user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) {
- if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingAuto {
+ switch setting.OAuth2Client.AccountLinking {
+ case setting.OAuth2AccountLinkingAuto:
var user *user_model.User
user = &user_model.User{Name: u.Name}
hasUser, err := user_model.GetUser(ctx, user)
@@ -549,7 +563,7 @@ func createUserInContext(ctx *context.Context, tpl templates.TplName, form any,
// TODO: probably we should respect 'remember' user's choice...
linkAccount(ctx, user, *gothUser, true)
return false // user is already created here, all redirects are handled
- } else if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingLogin {
+ case setting.OAuth2AccountLinkingLogin:
showLinkingLogin(ctx, *gothUser)
return false // user will be created only after linking login
}
@@ -598,10 +612,16 @@ func createUserInContext(ctx *context.Context, tpl templates.TplName, form any,
// sends a confirmation email if required.
func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.User) (ok bool) {
// Auto-set admin for the only user.
- if user_model.CountUsers(ctx, nil) == 1 {
+ hasUsers, err := user_model.HasUsers(ctx)
+ if err != nil {
+ ctx.ServerError("HasUsers", err)
+ return false
+ }
+ if hasUsers.HasOnlyOneUser {
+ // the only user is the one just created, will set it as admin
opts := &user_service.UpdateOptions{
IsActive: optional.Some(true),
- IsAdmin: optional.Some(true),
+ IsAdmin: user_service.UpdateOptionFieldFromValue(true),
SetLastLogin: true,
}
if err := user_service.UpdateUser(ctx, u, opts); err != nil {
@@ -766,7 +786,7 @@ func handleAccountActivation(ctx *context.Context, user *user_model.User) {
}
if err := user_model.UpdateUserCols(ctx, user, "is_active", "rands"); err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.NotFound("UpdateUserCols", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("UpdateUser", err)
}
diff --git a/routers/web/auth/auth_test.go b/routers/web/auth/auth_test.go
index cbcb2a5222..e238125407 100644
--- a/routers/web/auth/auth_test.go
+++ b/routers/web/auth/auth_test.go
@@ -61,23 +61,35 @@ func TestUserLogin(t *testing.T) {
assert.Equal(t, "/", test.RedirectURL(resp))
}
-func TestSignUpOAuth2ButMissingFields(t *testing.T) {
+func TestSignUpOAuth2Login(t *testing.T) {
defer test.MockVariableValue(&setting.OAuth2Client.EnableAutoRegistration, true)()
- defer test.MockVariableValue(&gothic.CompleteUserAuth, func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
- return goth.User{Provider: "dummy-auth-source", UserID: "dummy-user"}, nil
- })()
addOAuth2Source(t, "dummy-auth-source", oauth2.Source{})
- mockOpt := contexttest.MockContextOption{SessionStore: session.NewMockStore("dummy-sid")}
- ctx, resp := contexttest.MockContext(t, "/user/oauth2/dummy-auth-source/callback?code=dummy-code", mockOpt)
- ctx.SetPathParam("provider", "dummy-auth-source")
- SignInOAuthCallback(ctx)
- assert.Equal(t, http.StatusSeeOther, resp.Code)
- assert.Equal(t, "/user/link_account", test.RedirectURL(resp))
+ t.Run("OAuth2MissingField", func(t *testing.T) {
+ defer test.MockVariableValue(&gothic.CompleteUserAuth, func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
+ return goth.User{Provider: "dummy-auth-source", UserID: "dummy-user"}, nil
+ })()
+ mockOpt := contexttest.MockContextOption{SessionStore: session.NewMockStore("dummy-sid")}
+ ctx, resp := contexttest.MockContext(t, "/user/oauth2/dummy-auth-source/callback?code=dummy-code", mockOpt)
+ ctx.SetPathParam("provider", "dummy-auth-source")
+ SignInOAuthCallback(ctx)
+ assert.Equal(t, http.StatusSeeOther, resp.Code)
+ assert.Equal(t, "/user/link_account", test.RedirectURL(resp))
+
+ // then the user will be redirected to the link account page, and see a message about the missing fields
+ ctx, _ = contexttest.MockContext(t, "/user/link_account", mockOpt)
+ LinkAccount(ctx)
+ assert.EqualValues(t, "auth.oauth_callback_unable_auto_reg:dummy-auth-source,email", ctx.Data["AutoRegistrationFailedPrompt"])
+ })
- // then the user will be redirected to the link account page, and see a message about the missing fields
- ctx, _ = contexttest.MockContext(t, "/user/link_account", mockOpt)
- LinkAccount(ctx)
- assert.EqualValues(t, "auth.oauth_callback_unable_auto_reg:dummy-auth-source,email", ctx.Data["AutoRegistrationFailedPrompt"])
+ t.Run("OAuth2CallbackError", func(t *testing.T) {
+ mockOpt := contexttest.MockContextOption{SessionStore: session.NewMockStore("dummy-sid")}
+ ctx, resp := contexttest.MockContext(t, "/user/oauth2/dummy-auth-source/callback", mockOpt)
+ ctx.SetPathParam("provider", "dummy-auth-source")
+ SignInOAuthCallback(ctx)
+ assert.Equal(t, http.StatusSeeOther, resp.Code)
+ assert.Equal(t, "/user/login", test.RedirectURL(resp))
+ assert.Contains(t, ctx.Flash.ErrorMsg, "auth.oauth.signin.error.general")
+ })
}
diff --git a/routers/web/auth/linkaccount.go b/routers/web/auth/linkaccount.go
index 147d8d3802..b3c61946b9 100644
--- a/routers/web/auth/linkaccount.go
+++ b/routers/web/auth/linkaccount.go
@@ -29,6 +29,7 @@ var tplLinkAccount templates.TplName = "user/auth/link_account"
// LinkAccount shows the page where the user can decide to login or create a new account
func LinkAccount(ctx *context.Context) {
+ // FIXME: these common template variables should be prepared in one common function, but not just copy-paste again and again.
ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration
ctx.Data["Title"] = ctx.Tr("link_account")
ctx.Data["LinkAccountMode"] = true
@@ -43,13 +44,20 @@ func LinkAccount(ctx *context.Context) {
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
+ ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
ctx.Data["ShowRegistrationButton"] = false
+ ctx.Data["EnablePasskeyAuth"] = setting.Service.EnablePasskeyAuth
// use this to set the right link into the signIn and signUp templates in the link_account template
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
gothUser, ok := ctx.Session.Get("linkAccountGothUser").(goth.User)
+
+ // If you'd like to quickly debug the "link account" page layout, just uncomment the blow line
+ // Don't worry, when the below line exists, the lint won't pass: ineffectual assignment to gothUser (ineffassign)
+ // gothUser, ok = goth.User{Email: "invalid-email", Name: "."}, true // intentionally use invalid data to avoid pass the registration check
+
if !ok {
// no account in session, so just redirect to the login page, then the user could restart the process
ctx.Redirect(setting.AppSubURL + "/user/login")
@@ -135,7 +143,10 @@ func LinkAccountPostSignIn(ctx *context.Context) {
ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
+ ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
+ ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
ctx.Data["ShowRegistrationButton"] = false
+ ctx.Data["EnablePasskeyAuth"] = setting.Service.EnablePasskeyAuth
// use this to set the right link into the signIn and signUp templates in the link_account template
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
@@ -223,7 +234,10 @@ func LinkAccountPostRegister(ctx *context.Context) {
ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
+ ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
+ ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
ctx.Data["ShowRegistrationButton"] = false
+ ctx.Data["EnablePasskeyAuth"] = setting.Service.EnablePasskeyAuth
// use this to set the right link into the signIn and signUp templates in the link_account template
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
@@ -246,7 +260,7 @@ func LinkAccountPostRegister(ctx *context.Context) {
}
if setting.Service.DisableRegistration || setting.Service.AllowOnlyInternalRegistration {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go
index 7a9721cf56..a13b987aab 100644
--- a/routers/web/auth/oauth.go
+++ b/routers/web/auth/oauth.go
@@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
+ "code.gitea.io/gitea/modules/session"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/web/middleware"
@@ -115,7 +116,7 @@ func SignInOAuthCallback(ctx *context.Context) {
case "temporarily_unavailable":
ctx.Flash.Error(ctx.Tr("auth.oauth.signin.error.temporarily_unavailable"))
default:
- ctx.Flash.Error(ctx.Tr("auth.oauth.signin.error"))
+ ctx.Flash.Error(ctx.Tr("auth.oauth.signin.error.general", callbackErr.Description))
}
ctx.Redirect(setting.AppSubURL + "/user/login")
return
@@ -155,9 +156,10 @@ func SignInOAuthCallback(ctx *context.Context) {
return
}
if uname == "" {
- if setting.OAuth2Client.Username == setting.OAuth2UsernameNickname {
+ switch setting.OAuth2Client.Username {
+ case setting.OAuth2UsernameNickname:
missingFields = append(missingFields, "nickname")
- } else if setting.OAuth2Client.Username == setting.OAuth2UsernamePreferredUsername {
+ case setting.OAuth2UsernamePreferredUsername:
missingFields = append(missingFields, "preferred_username")
} // else: "UserID" and "Email" have been handled above separately
}
@@ -191,8 +193,8 @@ func SignInOAuthCallback(ctx *context.Context) {
source := authSource.Cfg.(*oauth2.Source)
isAdmin, isRestricted := getUserAdminAndRestrictedFromGroupClaims(source, &gothUser)
- u.IsAdmin = isAdmin.ValueOrDefault(false)
- u.IsRestricted = isRestricted.ValueOrDefault(false)
+ u.IsAdmin = isAdmin.ValueOrDefault(user_service.UpdateOptionField[bool]{FieldValue: false}).FieldValue
+ u.IsRestricted = isRestricted.ValueOrDefault(setting.Service.DefaultUserIsRestricted)
if !createAndHandleCreatedUser(ctx, templates.TplName(""), nil, u, overwriteDefault, &gothUser, setting.OAuth2Client.AccountLinking != setting.OAuth2AccountLinkingDisabled) {
// error already handled
@@ -256,11 +258,11 @@ func getClaimedGroups(source *oauth2.Source, gothUser *goth.User) container.Set[
return claimValueToStringSet(groupClaims)
}
-func getUserAdminAndRestrictedFromGroupClaims(source *oauth2.Source, gothUser *goth.User) (isAdmin, isRestricted optional.Option[bool]) {
+func getUserAdminAndRestrictedFromGroupClaims(source *oauth2.Source, gothUser *goth.User) (isAdmin optional.Option[user_service.UpdateOptionField[bool]], isRestricted optional.Option[bool]) {
groups := getClaimedGroups(source, gothUser)
if source.AdminGroup != "" {
- isAdmin = optional.Some(groups.Contains(source.AdminGroup))
+ isAdmin = user_service.UpdateOptionFieldFromSync(groups.Contains(source.AdminGroup))
}
if source.RestrictedGroup != "" {
isRestricted = optional.Some(groups.Contains(source.RestrictedGroup))
@@ -301,7 +303,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model
updateAvatarIfNeed(ctx, gothUser.AvatarURL, u)
needs2FA := false
- if !source.Cfg.(*oauth2.Source).SkipLocalTwoFA {
+ if !source.TwoFactorShouldSkip() {
_, err := auth.GetTwoFactorByUID(ctx, u.ID)
if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
ctx.ServerError("UserSignIn", err)
@@ -351,10 +353,16 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model
ctx.ServerError("UpdateUser", err)
return
}
+ userHasTwoFactorAuth, err := auth.HasTwoFactorOrWebAuthn(ctx, u.ID)
+ if err != nil {
+ ctx.ServerError("UpdateUser", err)
+ return
+ }
if err := updateSession(ctx, nil, map[string]any{
- "uid": u.ID,
- "uname": u.Name,
+ session.KeyUID: u.ID,
+ session.KeyUname: u.Name,
+ session.KeyUserHasTwoFactorAuth: userHasTwoFactorAuth,
}); err != nil {
ctx.ServerError("updateSession", err)
return
@@ -431,8 +439,10 @@ func oAuth2UserLoginCallback(ctx *context.Context, authSource *auth.Source, requ
gothUser, err := oauth2Source.Callback(request, response)
if err != nil {
if err.Error() == "securecookie: the value is too long" || strings.Contains(err.Error(), "Data too long") {
- log.Error("OAuth2 Provider %s returned too long a token. Current max: %d. Either increase the [OAuth2] MAX_TOKEN_LENGTH or reduce the information returned from the OAuth2 provider", authSource.Name, setting.OAuth2.MaxTokenLength)
err = fmt.Errorf("OAuth2 Provider %s returned too long a token. Current max: %d. Either increase the [OAuth2] MAX_TOKEN_LENGTH or reduce the information returned from the OAuth2 provider", authSource.Name, setting.OAuth2.MaxTokenLength)
+ log.Error("oauth2Source.Callback failed: %v", err)
+ } else {
+ err = errCallback{Code: "internal", Description: err.Error()}
}
return nil, goth.User{}, err
}
diff --git a/routers/web/auth/oauth2_provider.go b/routers/web/auth/oauth2_provider.go
index 6262ad8a6d..79989d8fbe 100644
--- a/routers/web/auth/oauth2_provider.go
+++ b/routers/web/auth/oauth2_provider.go
@@ -4,17 +4,17 @@
package auth
import (
- "errors"
"fmt"
"html"
"html/template"
"net/http"
"net/url"
+ "strconv"
"strings"
"code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/auth/httpauth"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -98,7 +98,7 @@ func InfoOAuth(ctx *context.Context) {
}
response := &userInfoResponse{
- Sub: fmt.Sprint(ctx.Doer.ID),
+ Sub: strconv.FormatInt(ctx.Doer.ID, 10),
Name: ctx.Doer.DisplayName(),
PreferredUsername: ctx.Doer.Name,
Email: ctx.Doer.Email,
@@ -107,9 +107,8 @@ func InfoOAuth(ctx *context.Context) {
var accessTokenScope auth.AccessTokenScope
if auHead := ctx.Req.Header.Get("Authorization"); auHead != "" {
- auths := strings.Fields(auHead)
- if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") {
- accessTokenScope, _ = auth_service.GetOAuthAccessTokenScopeAndUserID(ctx, auths[1])
+ if parsed, ok := httpauth.ParseAuthorizationHeader(auHead); ok && parsed.BearerToken != nil {
+ accessTokenScope, _ = auth_service.GetOAuthAccessTokenScopeAndUserID(ctx, parsed.BearerToken.Token)
}
}
@@ -126,23 +125,17 @@ func InfoOAuth(ctx *context.Context) {
ctx.JSON(http.StatusOK, response)
}
-func parseBasicAuth(ctx *context.Context) (username, password string, err error) {
- authHeader := ctx.Req.Header.Get("Authorization")
- if authType, authData, ok := strings.Cut(authHeader, " "); ok && strings.EqualFold(authType, "Basic") {
- return base.BasicAuthDecode(authData)
- }
- return "", "", errors.New("invalid basic authentication")
-}
-
// IntrospectOAuth introspects an oauth token
func IntrospectOAuth(ctx *context.Context) {
clientIDValid := false
- if clientID, clientSecret, err := parseBasicAuth(ctx); err == nil {
+ authHeader := ctx.Req.Header.Get("Authorization")
+ if parsed, ok := httpauth.ParseAuthorizationHeader(authHeader); ok && parsed.BasicAuth != nil {
+ clientID, clientSecret := parsed.BasicAuth.Username, parsed.BasicAuth.Password
app, err := auth.GetOAuth2ApplicationByClientID(ctx, clientID)
if err != nil && !auth.IsErrOauthClientIDInvalid(err) {
// this is likely a database error; log it and respond without details
log.Error("Error retrieving client_id: %v", err)
- ctx.Error(http.StatusInternalServerError)
+ ctx.HTTPError(http.StatusInternalServerError)
return
}
clientIDValid = err == nil && app.ValidateClientSecret([]byte(clientSecret))
@@ -169,9 +162,7 @@ func IntrospectOAuth(ctx *context.Context) {
if err == nil && app != nil {
response.Active = true
response.Scope = grant.Scope
- response.Issuer = setting.AppURL
- response.Audience = []string{app.ClientID}
- response.Subject = fmt.Sprint(grant.UserID)
+ response.RegisteredClaims = oauth2_provider.NewJwtRegisteredClaimsFromUser(app.ClientID, grant.UserID, nil /*exp*/)
}
if user, err := user_model.GetUserByID(ctx, grant.UserID); err == nil {
response.Username = user.Name
@@ -249,7 +240,7 @@ func AuthorizeOAuth(ctx *context.Context) {
}, form.RedirectURI)
return
}
- if err := ctx.Session.Set("CodeChallengeMethod", form.CodeChallenge); err != nil {
+ if err := ctx.Session.Set("CodeChallenge", form.CodeChallenge); err != nil {
handleAuthorizeError(ctx, AuthorizeError{
ErrorCode: ErrorCodeServerError,
ErrorDescription: "cannot set code challenge",
@@ -363,7 +354,7 @@ func GrantApplicationOAuth(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.GrantApplicationForm)
if ctx.Session.Get("client_id") != form.ClientID || ctx.Session.Get("state") != form.State ||
ctx.Session.Get("redirect_uri") != form.RedirectURI {
- ctx.Error(http.StatusBadRequest)
+ ctx.HTTPError(http.StatusBadRequest)
return
}
@@ -431,7 +422,14 @@ func GrantApplicationOAuth(ctx *context.Context) {
// OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities
func OIDCWellKnown(ctx *context.Context) {
- ctx.Data["SigningKey"] = oauth2_provider.DefaultSigningKey
+ if !setting.OAuth2.Enabled {
+ http.NotFound(ctx.Resp, ctx.Req)
+ return
+ }
+ jwtRegisteredClaims := oauth2_provider.NewJwtRegisteredClaimsFromUser("well-known", 0, nil)
+ ctx.Data["OidcIssuer"] = jwtRegisteredClaims.Issuer // use the consistent issuer from the JWT registered claims
+ ctx.Data["OidcBaseUrl"] = strings.TrimSuffix(setting.AppURL, "/")
+ ctx.Data["SigningKeyMethodAlg"] = oauth2_provider.DefaultSigningKey.SigningMethod().Alg()
ctx.JSONTemplate("user/auth/oidc_wellknown")
}
@@ -440,7 +438,7 @@ func OIDCKeys(ctx *context.Context) {
jwk, err := oauth2_provider.DefaultSigningKey.ToJWK()
if err != nil {
log.Error("Error converting signing key to JWK: %v", err)
- ctx.Error(http.StatusInternalServerError)
+ ctx.HTTPError(http.StatusInternalServerError)
return
}
@@ -464,16 +462,16 @@ func AccessTokenOAuth(ctx *context.Context) {
form := *web.GetForm(ctx).(*forms.AccessTokenForm)
// if there is no ClientID or ClientSecret in the request body, fill these fields by the Authorization header and ensure the provided field matches the Authorization header
if form.ClientID == "" || form.ClientSecret == "" {
- authHeader := ctx.Req.Header.Get("Authorization")
- if authType, authData, ok := strings.Cut(authHeader, " "); ok && strings.EqualFold(authType, "Basic") {
- clientID, clientSecret, err := base.BasicAuthDecode(authData)
- if err != nil {
+ if authHeader := ctx.Req.Header.Get("Authorization"); authHeader != "" {
+ parsed, ok := httpauth.ParseAuthorizationHeader(authHeader)
+ if !ok || parsed.BasicAuth == nil {
handleAccessTokenError(ctx, oauth2_provider.AccessTokenError{
ErrorCode: oauth2_provider.AccessTokenErrorCodeInvalidRequest,
ErrorDescription: "cannot parse basic auth header",
})
return
}
+ clientID, clientSecret := parsed.BasicAuth.Username, parsed.BasicAuth.Password
// validate that any fields present in the form match the Basic auth header
if form.ClientID != "" && form.ClientID != clientID {
handleAccessTokenError(ctx, oauth2_provider.AccessTokenError{
diff --git a/routers/web/auth/openid.go b/routers/web/auth/openid.go
index 41d37ecb8b..2ef4a86022 100644
--- a/routers/web/auth/openid.go
+++ b/routers/web/auth/openid.go
@@ -4,7 +4,7 @@
package auth
import (
- "fmt"
+ "errors"
"net/http"
"net/url"
@@ -55,13 +55,13 @@ func allowedOpenIDURI(uri string) (err error) {
}
}
// must match one of this or be refused
- return fmt.Errorf("URI not allowed by whitelist")
+ return errors.New("URI not allowed by whitelist")
}
// A blacklist match expliclty forbids
for _, pat := range setting.Service.OpenIDBlacklist {
if pat.MatchString(uri) {
- return fmt.Errorf("URI forbidden by blacklist")
+ return errors.New("URI forbidden by blacklist")
}
}
@@ -99,7 +99,7 @@ func SignInOpenIDPost(ctx *context.Context) {
url, err := openid.RedirectURL(id, redirectTo, setting.AppURL)
if err != nil {
log.Error("Error in OpenID redirect URL: %s, %v", redirectTo, err.Error())
- ctx.RenderWithErr(fmt.Sprintf("Unable to find OpenID provider in %s", redirectTo), tplSignInOpenID, &form)
+ ctx.RenderWithErr("Unable to find OpenID provider in "+redirectTo, tplSignInOpenID, &form)
return
}
@@ -337,7 +337,7 @@ func RegisterOpenIDPost(ctx *context.Context) {
ctx.Data["OpenID"] = oid
if setting.Service.AllowOnlyInternalRegistration {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
@@ -349,10 +349,7 @@ func RegisterOpenIDPost(ctx *context.Context) {
context.VerifyCaptcha(ctx, tplSignUpOID, form)
}
- length := setting.MinPasswordLength
- if length < 256 {
- length = 256
- }
+ length := max(setting.MinPasswordLength, 256)
password, err := util.CryptoRandomString(int64(length))
if err != nil {
ctx.RenderWithErr(err.Error(), tplSignUpOID, form)
diff --git a/routers/web/auth/password.go b/routers/web/auth/password.go
index 614e086f77..537ad4b994 100644
--- a/routers/web/auth/password.go
+++ b/routers/web/auth/password.go
@@ -5,7 +5,6 @@ package auth
import (
"errors"
- "fmt"
"net/http"
"code.gitea.io/gitea/models/auth"
@@ -53,7 +52,7 @@ func ForgotPasswdPost(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("auth.forgot_password_title")
if setting.MailService == nil {
- ctx.NotFound("ForgotPasswdPost", nil)
+ ctx.NotFound(nil)
return
}
ctx.Data["IsResetRequest"] = true
@@ -108,21 +107,21 @@ func commonResetPassword(ctx *context.Context) (*user_model.User, *auth.TwoFacto
}
if len(code) == 0 {
- ctx.Flash.Error(ctx.Tr("auth.invalid_code_forgot_password", fmt.Sprintf("%s/user/forgot_password", setting.AppSubURL)), true)
+ ctx.Flash.Error(ctx.Tr("auth.invalid_code_forgot_password", setting.AppSubURL+"/user/forgot_password"), true)
return nil, nil
}
// Fail early, don't frustrate the user
u := user_model.VerifyUserTimeLimitCode(ctx, &user_model.TimeLimitCodeOptions{Purpose: user_model.TimeLimitCodeResetPassword}, code)
if u == nil {
- ctx.Flash.Error(ctx.Tr("auth.invalid_code_forgot_password", fmt.Sprintf("%s/user/forgot_password", setting.AppSubURL)), true)
+ ctx.Flash.Error(ctx.Tr("auth.invalid_code_forgot_password", setting.AppSubURL+"/user/forgot_password"), true)
return nil, nil
}
twofa, err := auth.GetTwoFactorByUID(ctx, u.ID)
if err != nil {
if !auth.IsErrTwoFactorNotEnrolled(err) {
- ctx.Error(http.StatusInternalServerError, "CommonResetPassword", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "CommonResetPassword", err.Error())
return nil, nil
}
} else {
@@ -181,7 +180,7 @@ func ResetPasswdPost(ctx *context.Context) {
passcode := ctx.FormString("passcode")
ok, err := twofa.ValidateTOTP(passcode)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "ValidateTOTP", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "ValidateTOTP", err.Error())
return
}
if !ok || twofa.LastUsedPasscode == passcode {
diff --git a/routers/web/auth/webauthn.go b/routers/web/auth/webauthn.go
index 69031adeaa..78f6c3b58e 100644
--- a/routers/web/auth/webauthn.go
+++ b/routers/web/auth/webauthn.go
@@ -50,6 +50,11 @@ func WebAuthn(ctx *context.Context) {
// WebAuthnPasskeyAssertion submits a WebAuthn challenge for the passkey login to the browser
func WebAuthnPasskeyAssertion(ctx *context.Context) {
+ if !setting.Service.EnablePasskeyAuth {
+ ctx.HTTPError(http.StatusForbidden)
+ return
+ }
+
assertion, sessionData, err := wa.WebAuthn.BeginDiscoverableLogin()
if err != nil {
ctx.ServerError("webauthn.BeginDiscoverableLogin", err)
@@ -66,6 +71,11 @@ func WebAuthnPasskeyAssertion(ctx *context.Context) {
// WebAuthnPasskeyLogin handles the WebAuthn login process using a Passkey
func WebAuthnPasskeyLogin(ctx *context.Context) {
+ if !setting.Service.EnablePasskeyAuth {
+ ctx.HTTPError(http.StatusForbidden)
+ return
+ }
+
sessionData, okData := ctx.Session.Get("webauthnPasskeyAssertion").(*webauthn.SessionData)
if !okData || sessionData == nil {
ctx.ServerError("ctx.Session.Get", errors.New("not in WebAuthn session"))
diff --git a/routers/web/base.go b/routers/web/base.go
index aa0b43c16a..e43f36a97b 100644
--- a/routers/web/base.go
+++ b/routers/web/base.go
@@ -19,13 +19,13 @@ import (
"code.gitea.io/gitea/modules/web/routing"
)
-func storageHandler(storageSetting *setting.Storage, prefix string, objStore storage.ObjectStorage) http.HandlerFunc {
+func avatarStorageHandler(storageSetting *setting.Storage, prefix string, objStore storage.ObjectStorage) http.HandlerFunc {
prefix = strings.Trim(prefix, "/")
- funcInfo := routing.GetFuncInfo(storageHandler, prefix)
+ funcInfo := routing.GetFuncInfo(avatarStorageHandler, prefix)
if storageSetting.ServeDirect() {
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- if req.Method != "GET" && req.Method != "HEAD" {
+ return func(w http.ResponseWriter, req *http.Request) {
+ if req.Method != http.MethodGet && req.Method != http.MethodHead {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
@@ -34,7 +34,7 @@ func storageHandler(storageSetting *setting.Storage, prefix string, objStore sto
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
- routing.UpdateFuncInfo(req.Context(), funcInfo)
+ defer routing.RecordFuncInfo(req.Context(), funcInfo)()
rPath := strings.TrimPrefix(req.URL.Path, "/"+prefix+"/")
rPath = util.PathJoinRelX(rPath)
@@ -52,11 +52,11 @@ func storageHandler(storageSetting *setting.Storage, prefix string, objStore sto
}
http.Redirect(w, req, u.String(), http.StatusTemporaryRedirect)
- })
+ }
}
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- if req.Method != "GET" && req.Method != "HEAD" {
+ return func(w http.ResponseWriter, req *http.Request) {
+ if req.Method != http.MethodGet && req.Method != http.MethodHead {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
@@ -65,7 +65,7 @@ func storageHandler(storageSetting *setting.Storage, prefix string, objStore sto
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
- routing.UpdateFuncInfo(req.Context(), funcInfo)
+ defer routing.RecordFuncInfo(req.Context(), funcInfo)()
rPath := strings.TrimPrefix(req.URL.Path, "/"+prefix+"/")
rPath = util.PathJoinRelX(rPath)
@@ -93,6 +93,8 @@ func storageHandler(storageSetting *setting.Storage, prefix string, objStore sto
return
}
defer fr.Close()
- httpcache.ServeContentWithCacheControl(w, req, path.Base(rPath), fi.ModTime(), fr)
- })
+
+ httpcache.SetCacheControlInHeader(w.Header(), httpcache.CacheControlForPublicStatic())
+ http.ServeContent(w, req, path.Base(rPath), fi.ModTime(), fr)
+ }
}
diff --git a/routers/web/devtest/devtest.go b/routers/web/devtest/devtest.go
index 1ea1398173..a22d376579 100644
--- a/routers/web/devtest/devtest.go
+++ b/routers/web/devtest/devtest.go
@@ -4,16 +4,22 @@
package devtest
import (
+ "fmt"
+ "html/template"
"net/http"
"path"
+ "strconv"
"strings"
"time"
+ "unicode"
"code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/badge"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context"
)
@@ -45,86 +51,135 @@ func FetchActionTest(ctx *context.Context) {
ctx.JSONRedirect("")
}
-func prepareMockData(ctx *context.Context) {
- if ctx.Req.URL.Path == "/devtest/gitea-ui" {
- now := time.Now()
- ctx.Data["TimeNow"] = now
- ctx.Data["TimePast5s"] = now.Add(-5 * time.Second)
- ctx.Data["TimeFuture5s"] = now.Add(5 * time.Second)
- ctx.Data["TimePast2m"] = now.Add(-2 * time.Minute)
- ctx.Data["TimeFuture2m"] = now.Add(2 * time.Minute)
- ctx.Data["TimePast1y"] = now.Add(-1 * 366 * 86400 * time.Second)
- ctx.Data["TimeFuture1y"] = now.Add(1 * 366 * 86400 * time.Second)
+func prepareMockDataGiteaUI(ctx *context.Context) {
+ now := time.Now()
+ ctx.Data["TimeNow"] = now
+ ctx.Data["TimePast5s"] = now.Add(-5 * time.Second)
+ ctx.Data["TimeFuture5s"] = now.Add(5 * time.Second)
+ ctx.Data["TimePast2m"] = now.Add(-2 * time.Minute)
+ ctx.Data["TimeFuture2m"] = now.Add(2 * time.Minute)
+ ctx.Data["TimePast1y"] = now.Add(-1 * 366 * 86400 * time.Second)
+ ctx.Data["TimeFuture1y"] = now.Add(1 * 366 * 86400 * time.Second)
+}
+
+func prepareMockDataBadgeCommitSign(ctx *context.Context) {
+ var commits []*asymkey.SignCommit
+ mockUsers, _ := db.Find[user_model.User](ctx, user_model.SearchUserOptions{ListOptions: db.ListOptions{PageSize: 1}})
+ mockUser := mockUsers[0]
+ commits = append(commits, &asymkey.SignCommit{
+ Verification: &asymkey.CommitVerification{},
+ UserCommit: &user_model.UserCommit{
+ Commit: &git.Commit{ID: git.Sha1ObjectFormat.EmptyObjectID()},
+ },
+ })
+ commits = append(commits, &asymkey.SignCommit{
+ Verification: &asymkey.CommitVerification{
+ Verified: true,
+ Reason: "name / key-id",
+ SigningUser: mockUser,
+ SigningKey: &asymkey.GPGKey{KeyID: "12345678"},
+ TrustStatus: "trusted",
+ },
+ UserCommit: &user_model.UserCommit{
+ User: mockUser,
+ Commit: &git.Commit{ID: git.Sha1ObjectFormat.EmptyObjectID()},
+ },
+ })
+ commits = append(commits, &asymkey.SignCommit{
+ Verification: &asymkey.CommitVerification{
+ Verified: true,
+ Reason: "name / key-id",
+ SigningUser: mockUser,
+ SigningSSHKey: &asymkey.PublicKey{Fingerprint: "aa:bb:cc:dd:ee"},
+ TrustStatus: "untrusted",
+ },
+ UserCommit: &user_model.UserCommit{
+ User: mockUser,
+ Commit: &git.Commit{ID: git.Sha1ObjectFormat.EmptyObjectID()},
+ },
+ })
+ commits = append(commits, &asymkey.SignCommit{
+ Verification: &asymkey.CommitVerification{
+ Verified: true,
+ Reason: "name / key-id",
+ SigningUser: mockUser,
+ SigningSSHKey: &asymkey.PublicKey{Fingerprint: "aa:bb:cc:dd:ee"},
+ TrustStatus: "other(unmatch)",
+ },
+ UserCommit: &user_model.UserCommit{
+ User: mockUser,
+ Commit: &git.Commit{ID: git.Sha1ObjectFormat.EmptyObjectID()},
+ },
+ })
+ commits = append(commits, &asymkey.SignCommit{
+ Verification: &asymkey.CommitVerification{
+ Warning: true,
+ Reason: "gpg.error",
+ SigningEmail: "test@example.com",
+ },
+ UserCommit: &user_model.UserCommit{
+ User: mockUser,
+ Commit: &git.Commit{ID: git.Sha1ObjectFormat.EmptyObjectID()},
+ },
+ })
+
+ ctx.Data["MockCommits"] = commits
+}
+
+func prepareMockDataBadgeActionsSvg(ctx *context.Context) {
+ fontFamilyNames := strings.Split(badge.DefaultFontFamily, ",")
+ selectedFontFamilyName := ctx.FormString("font", fontFamilyNames[0])
+ selectedStyle := ctx.FormString("style", badge.DefaultStyle)
+ var badges []badge.Badge
+ badges = append(badges, badge.GenerateBadge("啊啊啊啊啊啊啊啊啊啊啊啊", "🌞🌞🌞🌞🌞", "green"))
+ for r := range rune(256) {
+ if unicode.IsPrint(r) {
+ s := strings.Repeat(string(r), 15)
+ badges = append(badges, badge.GenerateBadge(s, util.TruncateRunes(s, 7), "green"))
+ }
}
- if ctx.Req.URL.Path == "/devtest/commit-sign-badge" {
- var commits []*asymkey.SignCommit
- mockUsers, _ := db.Find[user_model.User](ctx, user_model.SearchUserOptions{ListOptions: db.ListOptions{PageSize: 1}})
- mockUser := mockUsers[0]
- commits = append(commits, &asymkey.SignCommit{
- Verification: &asymkey.CommitVerification{},
- UserCommit: &user_model.UserCommit{
- Commit: &git.Commit{ID: git.Sha1ObjectFormat.EmptyObjectID()},
- },
- })
- commits = append(commits, &asymkey.SignCommit{
- Verification: &asymkey.CommitVerification{
- Verified: true,
- Reason: "name / key-id",
- SigningUser: mockUser,
- SigningKey: &asymkey.GPGKey{KeyID: "12345678"},
- TrustStatus: "trusted",
- },
- UserCommit: &user_model.UserCommit{
- User: mockUser,
- Commit: &git.Commit{ID: git.Sha1ObjectFormat.EmptyObjectID()},
- },
- })
- commits = append(commits, &asymkey.SignCommit{
- Verification: &asymkey.CommitVerification{
- Verified: true,
- Reason: "name / key-id",
- SigningUser: mockUser,
- SigningSSHKey: &asymkey.PublicKey{Fingerprint: "aa:bb:cc:dd:ee"},
- TrustStatus: "untrusted",
- },
- UserCommit: &user_model.UserCommit{
- User: mockUser,
- Commit: &git.Commit{ID: git.Sha1ObjectFormat.EmptyObjectID()},
- },
- })
- commits = append(commits, &asymkey.SignCommit{
- Verification: &asymkey.CommitVerification{
- Verified: true,
- Reason: "name / key-id",
- SigningUser: mockUser,
- SigningSSHKey: &asymkey.PublicKey{Fingerprint: "aa:bb:cc:dd:ee"},
- TrustStatus: "other(unmatch)",
- },
- UserCommit: &user_model.UserCommit{
- User: mockUser,
- Commit: &git.Commit{ID: git.Sha1ObjectFormat.EmptyObjectID()},
- },
- })
- commits = append(commits, &asymkey.SignCommit{
- Verification: &asymkey.CommitVerification{
- Warning: true,
- Reason: "gpg.error",
- SigningEmail: "test@example.com",
- },
- UserCommit: &user_model.UserCommit{
- User: mockUser,
- Commit: &git.Commit{ID: git.Sha1ObjectFormat.EmptyObjectID()},
- },
- })
+ var badgeSVGs []template.HTML
+ for i, b := range badges {
+ b.IDPrefix = "devtest-" + strconv.FormatInt(int64(i), 10) + "-"
+ b.FontFamily = selectedFontFamilyName
+ var h template.HTML
+ var err error
+ switch selectedStyle {
+ case badge.StyleFlat:
+ h, err = ctx.RenderToHTML("shared/actions/runner_badge_flat", map[string]any{"Badge": b})
+ case badge.StyleFlatSquare:
+ h, err = ctx.RenderToHTML("shared/actions/runner_badge_flat-square", map[string]any{"Badge": b})
+ default:
+ err = fmt.Errorf("unknown badge style: %s", selectedStyle)
+ }
+ if err != nil {
+ ctx.ServerError("RenderToHTML", err)
+ return
+ }
+ badgeSVGs = append(badgeSVGs, h)
+ }
+ ctx.Data["BadgeSVGs"] = badgeSVGs
+ ctx.Data["BadgeFontFamilyNames"] = fontFamilyNames
+ ctx.Data["SelectedFontFamilyName"] = selectedFontFamilyName
+ ctx.Data["BadgeStyles"] = badge.GlobalVars().AllStyles
+ ctx.Data["SelectedStyle"] = selectedStyle
+}
- ctx.Data["MockCommits"] = commits
+func prepareMockData(ctx *context.Context) {
+ switch ctx.Req.URL.Path {
+ case "/devtest/gitea-ui":
+ prepareMockDataGiteaUI(ctx)
+ case "/devtest/badge-commit-sign":
+ prepareMockDataBadgeCommitSign(ctx)
+ case "/devtest/badge-actions-svg":
+ prepareMockDataBadgeActionsSvg(ctx)
}
}
-func Tmpl(ctx *context.Context) {
+func TmplCommon(ctx *context.Context) {
prepareMockData(ctx)
- if ctx.Req.Method == "POST" {
+ if ctx.Req.Method == http.MethodPost {
_ = ctx.Req.ParseForm()
ctx.Flash.Info("form: "+ctx.Req.Method+" "+ctx.Req.RequestURI+"<br>"+
"Form: "+ctx.Req.Form.Encode()+"<br>"+
diff --git a/routers/web/devtest/mock_actions.go b/routers/web/devtest/mock_actions.go
index f29b8e4046..bc741ecd11 100644
--- a/routers/web/devtest/mock_actions.go
+++ b/routers/web/devtest/mock_actions.go
@@ -4,9 +4,9 @@
package devtest
import (
- "fmt"
mathRand "math/rand/v2"
"net/http"
+ "strconv"
"strings"
"time"
@@ -38,8 +38,8 @@ func generateMockStepsLog(logCur actions.LogCursor) (stepsLog []*actions.ViewSte
for i := 0; i < mockCount; i++ {
logStr := mockedLogs[int(cur)%len(mockedLogs)]
cur++
- logStr = strings.ReplaceAll(logStr, "{step}", fmt.Sprintf("%d", logCur.Step))
- logStr = strings.ReplaceAll(logStr, "{cursor}", fmt.Sprintf("%d", cur))
+ logStr = strings.ReplaceAll(logStr, "{step}", strconv.Itoa(logCur.Step))
+ logStr = strings.ReplaceAll(logStr, "{cursor}", strconv.FormatInt(cur, 10))
stepsLog = append(stepsLog, &actions.ViewStepLog{
Step: logCur.Step,
Cursor: cur,
@@ -52,13 +52,22 @@ func generateMockStepsLog(logCur actions.LogCursor) (stepsLog []*actions.ViewSte
return stepsLog
}
+func MockActionsView(ctx *context.Context) {
+ ctx.Data["RunID"] = ctx.PathParam("run")
+ ctx.Data["JobID"] = ctx.PathParam("job")
+ ctx.HTML(http.StatusOK, "devtest/repo-action-view")
+}
+
func MockActionsRunsJobs(ctx *context.Context) {
- req := web.GetForm(ctx).(*actions.ViewRequest)
+ runID := ctx.PathParamInt64("run")
+ req := web.GetForm(ctx).(*actions.ViewRequest)
resp := &actions.ViewResponse{}
resp.State.Run.TitleHTML = `mock run title <a href="/">link</a>`
resp.State.Run.Status = actions_model.StatusRunning.String()
- resp.State.Run.CanCancel = true
+ resp.State.Run.CanCancel = runID == 10
+ resp.State.Run.CanApprove = runID == 20
+ resp.State.Run.CanRerun = runID == 30
resp.State.Run.CanDeleteArtifact = true
resp.State.Run.WorkflowID = "workflow-id"
resp.State.Run.WorkflowLink = "./workflow-link"
@@ -85,6 +94,39 @@ func MockActionsRunsJobs(ctx *context.Context) {
Size: 1024 * 1024,
Status: "completed",
})
+ resp.Artifacts = append(resp.Artifacts, &actions.ArtifactsViewItem{
+ Name: "artifact-very-loooooooooooooooooooooooooooooooooooooooooooooooooooooooong",
+ Size: 100 * 1024,
+ Status: "expired",
+ })
+ resp.Artifacts = append(resp.Artifacts, &actions.ArtifactsViewItem{
+ Name: "artifact-really-loooooooooooooooooooooooooooooooooooooooooooooooooooooooong",
+ Size: 1024 * 1024,
+ Status: "completed",
+ })
+
+ resp.State.Run.Jobs = append(resp.State.Run.Jobs, &actions.ViewJob{
+ ID: runID * 10,
+ Name: "job 100",
+ Status: actions_model.StatusRunning.String(),
+ CanRerun: true,
+ Duration: "1h",
+ })
+ resp.State.Run.Jobs = append(resp.State.Run.Jobs, &actions.ViewJob{
+ ID: runID*10 + 1,
+ Name: "job 101",
+ Status: actions_model.StatusWaiting.String(),
+ CanRerun: false,
+ Duration: "2h",
+ })
+ resp.State.Run.Jobs = append(resp.State.Run.Jobs, &actions.ViewJob{
+ ID: runID*10 + 2,
+ Name: "job 102",
+ Status: actions_model.StatusFailure.String(),
+ CanRerun: false,
+ Duration: "3h",
+ })
+
resp.State.CurrentJob.Steps = append(resp.State.CurrentJob.Steps, &actions.ViewJobStep{
Summary: "step 0 (mock slow)",
Duration: time.Hour.String(),
@@ -118,7 +160,7 @@ func MockActionsRunsJobs(ctx *context.Context) {
}
if doErrorResponse {
if mathRand.Float64() > 0.5 {
- ctx.Error(http.StatusInternalServerError, "devtest mock error response")
+ ctx.HTTPError(http.StatusInternalServerError, "devtest mock error response")
return
}
}
diff --git a/routers/web/explore/code.go b/routers/web/explore/code.go
index ae5ff3db76..3bb50ef397 100644
--- a/routers/web/explore/code.go
+++ b/routers/web/explore/code.go
@@ -5,6 +5,7 @@ package explore
import (
"net/http"
+ "slices"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
@@ -72,10 +73,10 @@ func Code(ctx *context.Context) {
if (len(repoIDs) > 0) || isAdmin {
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{
- RepoIDs: repoIDs,
- Keyword: prepareSearch.Keyword,
- IsKeywordFuzzy: prepareSearch.IsFuzzy,
- Language: prepareSearch.Language,
+ RepoIDs: repoIDs,
+ Keyword: prepareSearch.Keyword,
+ SearchMode: prepareSearch.SearchMode,
+ Language: prepareSearch.Language,
Paginator: &db.ListOptions{
Page: page,
PageSize: setting.UI.RepoSearchPagingNum,
@@ -93,14 +94,7 @@ func Code(ctx *context.Context) {
loadRepoIDs := make([]int64, 0, len(searchResults))
for _, result := range searchResults {
- var find bool
- for _, id := range loadRepoIDs {
- if id == result.RepoID {
- find = true
- break
- }
- }
- if !find {
+ if !slices.Contains(loadRepoIDs, result.RepoID) {
loadRepoIDs = append(loadRepoIDs, result.RepoID)
}
}
diff --git a/routers/web/explore/org.go b/routers/web/explore/org.go
index 7bb71acfd7..f8f7f5c18c 100644
--- a/routers/web/explore/org.go
+++ b/routers/web/explore/org.go
@@ -44,7 +44,7 @@ func Organizations(ctx *context.Context) {
ctx.SetFormString("sort", sortOrder)
}
- RenderUserSearch(ctx, &user_model.SearchUserOptions{
+ RenderUserSearch(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer,
Type: user_model.UserTypeOrganization,
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
diff --git a/routers/web/explore/repo.go b/routers/web/explore/repo.go
index cf3128314b..f0d7d0ce7d 100644
--- a/routers/web/explore/repo.go
+++ b/routers/web/explore/repo.go
@@ -94,7 +94,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
private := ctx.FormOptionalBool("private")
ctx.Data["IsPrivate"] = private
- repos, count, err = repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepository(ctx, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: page,
PageSize: opts.PageSize,
@@ -151,6 +151,7 @@ func Repos(ctx *context.Context) {
ctx.Data["CodePageIsDisabled"] = setting.Service.Explore.DisableCodePage
ctx.Data["Title"] = ctx.Tr("explore")
ctx.Data["PageIsExplore"] = true
+ ctx.Data["ShowRepoOwnerOnList"] = true
ctx.Data["PageIsExploreRepositories"] = true
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
diff --git a/routers/web/explore/topic.go b/routers/web/explore/topic.go
index b4507ba28d..be457df587 100644
--- a/routers/web/explore/topic.go
+++ b/routers/web/explore/topic.go
@@ -25,7 +25,7 @@ func TopicSearch(ctx *context.Context) {
topics, total, err := db.FindAndCount[repo_model.Topic](ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError)
+ ctx.HTTPError(http.StatusInternalServerError)
return
}
diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go
index b14272c76a..af48e6fb79 100644
--- a/routers/web/explore/user.go
+++ b/routers/web/explore/user.go
@@ -32,7 +32,7 @@ func isKeywordValid(keyword string) bool {
}
// RenderUserSearch render user search page
-func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, tplName templates.TplName) {
+func RenderUserSearch(ctx *context.Context, opts user_model.SearchUserOptions, tplName templates.TplName) {
// Sitemap index for sitemap paths
opts.Page = int(ctx.PathParamInt64("idx"))
isSitemap := ctx.PathParam("idx") != ""
@@ -87,7 +87,7 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions,
}
if opts.SupportedSortOrders != nil && !opts.SupportedSortOrders.Contains(sortOrder) {
- ctx.NotFound("unsupported sort order", nil)
+ ctx.NotFound(nil)
return
}
@@ -151,7 +151,7 @@ func Users(ctx *context.Context) {
ctx.SetFormString("sort", sortOrder)
}
- RenderUserSearch(ctx, &user_model.SearchUserOptions{
+ RenderUserSearch(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer,
Type: user_model.UserTypeIndividual,
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
diff --git a/routers/web/feed/branch.go b/routers/web/feed/branch.go
index 80ce2ad198..094fd987ac 100644
--- a/routers/web/feed/branch.go
+++ b/routers/web/feed/branch.go
@@ -4,7 +4,6 @@
package feed
import (
- "fmt"
"strings"
"time"
@@ -16,14 +15,14 @@ import (
// ShowBranchFeed shows tags and/or releases on the repo as RSS / Atom feed
func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType string) {
- commits, err := ctx.Repo.Commit.CommitsByRange(0, 10, "")
+ commits, err := ctx.Repo.Commit.CommitsByRange(0, 10, "", "", "")
if err != nil {
ctx.ServerError("ShowBranchFeed", err)
return
}
- title := fmt.Sprintf("Latest commits for branch %s", ctx.Repo.BranchName)
- link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.BranchNameSubURL()}
+ title := "Latest commits for branch " + ctx.Repo.BranchName
+ link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.RefTypeNameSubURL()}
feed := &feeds.Feed{
Title: title,
@@ -43,6 +42,7 @@ func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType stri
},
Description: commit.Message(),
Content: commit.Message(),
+ Created: commit.Committer.When,
})
}
diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go
index b7f849dc65..7c59132841 100644
--- a/routers/web/feed/convert.go
+++ b/routers/web/feed/convert.go
@@ -50,7 +50,7 @@ func toReleaseLink(ctx *context.Context, act *activities_model.Action) string {
// renderCommentMarkdown renders the comment markdown to html
func renderCommentMarkdown(ctx *context.Context, act *activities_model.Action, content string) template.HTML {
- act.LoadRepo(ctx)
+ _ = act.LoadRepo(ctx)
if act.Repo == nil {
return ""
}
@@ -201,7 +201,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
switch act.OpType {
case activities_model.ActionCommitRepo, activities_model.ActionMirrorSyncPush:
push := templates.ActionContent2Commits(act)
-
+ _ = act.LoadRepo(ctx)
for _, commit := range push.Commits {
if len(desc) != 0 {
desc += "\n\n"
@@ -209,7 +209,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
desc += fmt.Sprintf("<a href=\"%s\">%s</a>\n%s",
html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoAbsoluteLink(ctx), commit.Sha1)),
commit.Sha1,
- renderUtils.RenderCommitMessage(commit.Message, nil),
+ renderUtils.RenderCommitMessage(commit.Message, act.Repo),
)
}
@@ -258,18 +258,18 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
}
// GetFeedType return if it is a feed request and altered name and feed type.
-func GetFeedType(name string, req *http.Request) (bool, string, string) {
+func GetFeedType(name string, req *http.Request) (showFeed bool, feedType string) {
if strings.HasSuffix(name, ".rss") ||
strings.Contains(req.Header.Get("Accept"), "application/rss+xml") {
- return true, strings.TrimSuffix(name, ".rss"), "rss"
+ return true, "rss"
}
if strings.HasSuffix(name, ".atom") ||
strings.Contains(req.Header.Get("Accept"), "application/atom+xml") {
- return true, strings.TrimSuffix(name, ".atom"), "atom"
+ return true, "atom"
}
- return false, name, ""
+ return false, ""
}
// feedActionsToFeedItems convert gitea's Repo's Releases to feeds Item
diff --git a/routers/web/feed/file.go b/routers/web/feed/file.go
index 1ab768ff27..026c15c43a 100644
--- a/routers/web/feed/file.go
+++ b/routers/web/feed/file.go
@@ -4,7 +4,6 @@
package feed
import (
- "fmt"
"strings"
"time"
@@ -24,7 +23,7 @@ func ShowFileFeed(ctx *context.Context, repo *repo.Repository, formatType string
}
commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(
git.CommitsByFileAndRangeOptions{
- Revision: ctx.Repo.RefName,
+ Revision: ctx.Repo.RefFullName.ShortName(), // FIXME: legacy code used ShortName
File: fileName,
Page: 1,
})
@@ -33,9 +32,9 @@ func ShowFileFeed(ctx *context.Context, repo *repo.Repository, formatType string
return
}
- title := fmt.Sprintf("Latest commits for file %s", ctx.Repo.TreePath)
+ title := "Latest commits for file " + ctx.Repo.TreePath
- link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)}
+ link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)}
feed := &feeds.Feed{
Title: title,
@@ -55,6 +54,7 @@ func ShowFileFeed(ctx *context.Context, repo *repo.Repository, formatType string
},
Description: commit.Message(),
Content: commit.Message(),
+ Created: commit.Committer.When,
})
}
diff --git a/routers/web/feed/profile.go b/routers/web/feed/profile.go
index 4ec46e302a..cecf9d409a 100644
--- a/routers/web/feed/profile.go
+++ b/routers/web/feed/profile.go
@@ -7,6 +7,7 @@ import (
"time"
activities_model "code.gitea.io/gitea/models/activities"
+ "code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/renderhelper"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/services/context"
@@ -28,12 +29,23 @@ func ShowUserFeedAtom(ctx *context.Context) {
// showUserFeed show user activity as RSS / Atom feed
func showUserFeed(ctx *context.Context, formatType string) {
includePrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
+ isOrganisation := ctx.ContextUser.IsOrganization()
+ if ctx.IsSigned && isOrganisation && !includePrivate {
+ // When feed is requested by a member of the organization,
+ // include the private repo's the member has access to.
+ isOrgMember, err := organization.IsOrganizationMember(ctx, ctx.ContextUser.ID, ctx.Doer.ID)
+ if err != nil {
+ ctx.ServerError("IsOrganizationMember", err)
+ return
+ }
+ includePrivate = isOrgMember
+ }
actions, _, err := feed_service.GetFeeds(ctx, activities_model.GetFeedsOptions{
RequestedUser: ctx.ContextUser,
Actor: ctx.Doer,
IncludePrivate: includePrivate,
- OnlyPerformedBy: !ctx.ContextUser.IsOrganization(),
+ OnlyPerformedBy: !isOrganisation,
IncludeDeleted: false,
Date: ctx.FormString("date"),
})
diff --git a/routers/web/feed/profile_test.go b/routers/web/feed/profile_test.go
new file mode 100644
index 0000000000..a0f1509269
--- /dev/null
+++ b/routers/web/feed/profile_test.go
@@ -0,0 +1,38 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+package feed_test
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/routers/web/feed"
+ "code.gitea.io/gitea/services/contexttest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMain(m *testing.M) {
+ unittest.MainTest(m)
+}
+
+func TestCheckGetOrgFeedsAsOrgMember(t *testing.T) {
+ unittest.PrepareTestEnv(t)
+ t.Run("OrgMember", func(t *testing.T) {
+ ctx, resp := contexttest.MockContext(t, "org3.atom")
+ ctx.ContextUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
+ contexttest.LoadUser(t, ctx, 2)
+ ctx.IsSigned = true
+ feed.ShowUserFeedAtom(ctx)
+ assert.Contains(t, resp.Body.String(), "<entry>") // Should contain 1 private entry
+ })
+ t.Run("NonOrgMember", func(t *testing.T) {
+ ctx, resp := contexttest.MockContext(t, "org3.atom")
+ ctx.ContextUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
+ contexttest.LoadUser(t, ctx, 5)
+ ctx.IsSigned = true
+ feed.ShowUserFeedAtom(ctx)
+ assert.NotContains(t, resp.Body.String(), "<entry>") // Should not contain any entries
+ })
+}
diff --git a/routers/web/feed/render.go b/routers/web/feed/render.go
index 462ebb97b5..d1a229e5b2 100644
--- a/routers/web/feed/render.go
+++ b/routers/web/feed/render.go
@@ -9,7 +9,7 @@ import (
// RenderBranchFeed render format for branch or file
func RenderBranchFeed(ctx *context.Context) {
- _, _, showFeedType := GetFeedType(ctx.PathParam("reponame"), ctx.Req)
+ _, showFeedType := GetFeedType(ctx.PathParam("reponame"), ctx.Req)
if ctx.Repo.TreePath == "" {
ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
} else {
diff --git a/routers/web/githttp.go b/routers/web/githttp.go
index 5831e6f523..06de811f16 100644
--- a/routers/web/githttp.go
+++ b/routers/web/githttp.go
@@ -4,26 +4,12 @@
package web
import (
- "net/http"
-
- "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/web/repo"
"code.gitea.io/gitea/services/context"
)
func addOwnerRepoGitHTTPRouters(m *web.Router) {
- reqGitSignIn := func(ctx *context.Context) {
- if !setting.Service.RequireSignInView {
- return
- }
- // rely on the results of Contexter
- if !ctx.IsSigned {
- // TODO: support digit auth - which would be Authorization header with digit
- ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea"`)
- ctx.Error(http.StatusUnauthorized)
- }
- }
m.Group("/{username}/{reponame}", func() {
m.Methods("POST,OPTIONS", "/git-upload-pack", repo.ServiceUploadPack)
m.Methods("POST,OPTIONS", "/git-receive-pack", repo.ServiceReceivePack)
@@ -36,5 +22,5 @@ func addOwnerRepoGitHTTPRouters(m *web.Router) {
m.Methods("GET,OPTIONS", "/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38,62}}", repo.GetLooseObject)
m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.pack", repo.GetPackFile)
m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.idx", repo.GetIdxFile)
- }, optSignInIgnoreCsrf, reqGitSignIn, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context.UserAssignmentWeb())
+ }, optSignInIgnoreCsrf, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context.UserAssignmentWeb())
}
diff --git a/routers/web/goget.go b/routers/web/goget.go
index 3714dd8eb0..67e0bee866 100644
--- a/routers/web/goget.go
+++ b/routers/web/goget.go
@@ -18,7 +18,7 @@ import (
)
func goGet(ctx *context.Context) {
- if ctx.Req.Method != "GET" || len(ctx.Req.URL.RawQuery) < 8 || ctx.FormString("go-get") != "1" {
+ if ctx.Req.Method != http.MethodGet || len(ctx.Req.URL.RawQuery) < 8 || ctx.FormString("go-get") != "1" {
return
}
@@ -69,9 +69,9 @@ func goGet(ctx *context.Context) {
var cloneURL string
if setting.Repository.GoGetCloneURLProtocol == "ssh" {
- cloneURL = repo_model.ComposeSSHCloneURL(ownerName, repoName)
+ cloneURL = repo_model.ComposeSSHCloneURL(ctx.Doer, ownerName, repoName)
} else {
- cloneURL = repo_model.ComposeHTTPSCloneURL(ownerName, repoName)
+ cloneURL = repo_model.ComposeHTTPSCloneURL(ctx, ownerName, repoName)
}
goImportContent := fmt.Sprintf("%s git %s", goGetImport, cloneURL /*CloneLink*/)
goSourceContent := fmt.Sprintf("%s _ %s %s", goGetImport, prefix+"{/dir}" /*GoDocDirectory*/, prefix+"{/dir}/{file}#L{line}" /*GoDocFile*/)
diff --git a/routers/web/home.go b/routers/web/home.go
index 9ad495d54f..4b15ee83c2 100644
--- a/routers/web/home.go
+++ b/routers/web/home.go
@@ -68,7 +68,7 @@ func Home(ctx *context.Context) {
func HomeSitemap(ctx *context.Context) {
m := sitemap.NewSitemapIndex()
if !setting.Service.Explore.DisableUsersPage {
- _, cnt, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
+ _, cnt, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
Type: user_model.UserTypeIndividual,
ListOptions: db.ListOptions{PageSize: 1},
IsActive: optional.Some(true),
@@ -86,7 +86,7 @@ func HomeSitemap(ctx *context.Context) {
}
}
- _, cnt, err := repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
+ _, cnt, err := repo_model.SearchRepository(ctx, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: 1,
},
@@ -113,5 +113,5 @@ func HomeSitemap(ctx *context.Context) {
// NotFound render 404 page
func NotFound(ctx *context.Context) {
ctx.Data["Title"] = "Page Not Found"
- ctx.NotFound("home.NotFound", nil)
+ ctx.NotFound(nil)
}
diff --git a/routers/web/misc/markup.go b/routers/web/misc/markup.go
index 0c7ec6c2eb..f90cf3ffed 100644
--- a/routers/web/misc/markup.go
+++ b/routers/web/misc/markup.go
@@ -15,6 +15,6 @@ import (
// Markup render markup document to HTML
func Markup(ctx *context.Context) {
form := web.GetForm(ctx).(*api.MarkupOption)
- mode := util.Iif(form.Wiki, "wiki", form.Mode) //nolint:staticcheck
+ mode := util.Iif(form.Wiki, "wiki", form.Mode) //nolint:staticcheck // form.Wiki is deprecated
common.RenderMarkup(ctx.Base, ctx.Repo, mode, form.Text, form.Context, form.FilePath)
}
diff --git a/routers/web/misc/misc.go b/routers/web/misc/misc.go
index caaca7f521..59b97c1717 100644
--- a/routers/web/misc/misc.go
+++ b/routers/web/misc/misc.go
@@ -20,7 +20,7 @@ func SSHInfo(rw http.ResponseWriter, req *http.Request) {
return
}
rw.Header().Set("content-type", "text/json;charset=UTF-8")
- _, err := rw.Write([]byte(`{"type":"gitea","version":1}`))
+ _, err := rw.Write([]byte(`{"type":"agit","version":1}`))
if err != nil {
log.Error("fail to write result: err: %v", err)
rw.WriteHeader(http.StatusInternalServerError)
@@ -38,7 +38,7 @@ func RobotsTxt(w http.ResponseWriter, req *http.Request) {
if ok, _ := util.IsExist(robotsTxt); !ok {
robotsTxt = util.FilePathJoinAbs(setting.CustomPath, "robots.txt") // the legacy "robots.txt"
}
- httpcache.SetCacheControlInHeader(w.Header(), setting.StaticCacheTime)
+ httpcache.SetCacheControlInHeader(w.Header(), httpcache.CacheControlForPublicStatic())
http.ServeFile(w, req, robotsTxt)
}
diff --git a/routers/web/nodeinfo.go b/routers/web/nodeinfo.go
index f1cc7bf530..47856bf98b 100644
--- a/routers/web/nodeinfo.go
+++ b/routers/web/nodeinfo.go
@@ -4,7 +4,6 @@
package web
import (
- "fmt"
"net/http"
"code.gitea.io/gitea/modules/setting"
@@ -24,7 +23,7 @@ type nodeInfoLink struct {
func NodeInfoLinks(ctx *context.Context) {
nodeinfolinks := &nodeInfoLinks{
Links: []nodeInfoLink{{
- fmt.Sprintf("%sapi/v1/nodeinfo", setting.AppURL),
+ setting.AppURL + "api/v1/nodeinfo",
"http://nodeinfo.diaspora.software/ns/schema/2.1",
}},
}
diff --git a/routers/web/org/block.go b/routers/web/org/block.go
index aeb4bd51a8..60f722dd39 100644
--- a/routers/web/org/block.go
+++ b/routers/web/org/block.go
@@ -20,6 +20,11 @@ func BlockedUsers(ctx *context.Context) {
ctx.Data["PageIsOrgSettings"] = true
ctx.Data["PageIsSettingsBlockedUsers"] = true
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
+
shared_user.BlockedUsers(ctx, ctx.ContextUser)
if ctx.Written() {
return
@@ -29,6 +34,11 @@ func BlockedUsers(ctx *context.Context) {
}
func BlockedUsersPost(ctx *context.Context) {
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
+
shared_user.BlockedUsersPost(ctx, ctx.ContextUser)
if ctx.Written() {
return
diff --git a/routers/web/org/home.go b/routers/web/org/home.go
index 277adb60ca..63ae6c683b 100644
--- a/routers/web/org/home.go
+++ b/routers/web/org/home.go
@@ -29,12 +29,12 @@ func Home(ctx *context.Context) {
uname := ctx.PathParam("username")
if strings.HasSuffix(uname, ".keys") || strings.HasSuffix(uname, ".gpg") {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
return
}
ctx.SetPathParam("org", uname)
- context.HandleOrgAssignment(ctx)
+ context.OrgAssignment(context.OrgAssignmentOptions{})(ctx)
if ctx.Written() {
return
}
@@ -86,12 +86,6 @@ func home(ctx *context.Context, viewRepositories bool) {
private := ctx.FormOptionalBool("private")
ctx.Data["IsPrivate"] = private
- err := shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
- return
- }
-
opts := &organization.FindOrgMembersOpts{
Doer: ctx.Doer,
OrgID: org.ID,
@@ -109,9 +103,9 @@ func home(ctx *context.Context, viewRepositories bool) {
ctx.Data["DisableNewPullMirrors"] = setting.Mirror.DisableNewPull
ctx.Data["ShowMemberAndTeamTab"] = ctx.Org.IsMember || len(members) > 0
- prepareResult, err := shared_user.PrepareOrgHeader(ctx)
+ prepareResult, err := shared_user.RenderUserOrgHeader(ctx)
if err != nil {
- ctx.ServerError("PrepareOrgHeader", err)
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
@@ -121,7 +115,7 @@ func home(ctx *context.Context, viewRepositories bool) {
ctx.Data["PageIsViewOverview"] = isViewOverview
ctx.Data["ShowOrgProfileReadmeSelector"] = isViewOverview && prepareResult.ProfilePublicReadmeBlob != nil && prepareResult.ProfilePrivateReadmeBlob != nil
- repos, count, err := repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
+ repos, count, err := repo_model.SearchRepository(ctx, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: setting.UI.User.RepoPagingNum,
Page: page,
@@ -154,7 +148,7 @@ func home(ctx *context.Context, viewRepositories bool) {
ctx.HTML(http.StatusOK, tplOrgHome)
}
-func prepareOrgProfileReadme(ctx *context.Context, prepareResult *shared_user.PrepareOrgHeaderResult) bool {
+func prepareOrgProfileReadme(ctx *context.Context, prepareResult *shared_user.PrepareOwnerHeaderResult) bool {
viewAs := ctx.FormString("view_as", util.Iif(ctx.Org.IsMember, "member", "public"))
viewAsMember := viewAs == "member"
diff --git a/routers/web/org/members.go b/routers/web/org/members.go
index 1665a12302..61022d3f09 100644
--- a/routers/web/org/members.go
+++ b/routers/web/org/members.go
@@ -28,10 +28,7 @@ func Members(ctx *context.Context) {
ctx.Data["Title"] = org.FullName
ctx.Data["PageIsOrgMembers"] = true
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
opts := &organization.FindOrgMembersOpts{
Doer: ctx.Doer,
@@ -41,7 +38,7 @@ func Members(ctx *context.Context) {
if ctx.Doer != nil {
isMember, err := ctx.Org.Organization.IsOrgMember(ctx, ctx.Doer.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "IsOrgMember")
+ ctx.HTTPError(http.StatusInternalServerError, "IsOrgMember")
return
}
opts.IsDoerMember = isMember
@@ -50,13 +47,12 @@ func Members(ctx *context.Context) {
total, err := organization.CountOrgMembers(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "CountOrgMembers")
+ ctx.HTTPError(http.StatusInternalServerError, "CountOrgMembers")
return
}
- _, err = shared_user.PrepareOrgHeader(ctx)
- if err != nil {
- ctx.ServerError("PrepareOrgHeader", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
@@ -93,19 +89,19 @@ func MembersAction(ctx *context.Context) {
switch ctx.PathParam("action") {
case "private":
if ctx.Doer.ID != member.ID && !ctx.Org.IsOwner {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
err = organization.ChangeOrgUserStatus(ctx, org.ID, member.ID, false)
case "public":
if ctx.Doer.ID != member.ID && !ctx.Org.IsOwner {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
err = organization.ChangeOrgUserStatus(ctx, org.ID, member.ID, true)
case "remove":
if !ctx.Org.IsOwner {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
err = org_service.RemoveOrgUser(ctx, org, member)
diff --git a/routers/web/org/org.go b/routers/web/org/org.go
index 856a605764..0540d5c591 100644
--- a/routers/web/org/org.go
+++ b/routers/web/org/org.go
@@ -27,11 +27,14 @@ const (
// Create render the page for create organization
func Create(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("new_org")
- ctx.Data["DefaultOrgVisibilityMode"] = setting.Service.DefaultOrgVisibilityMode
if !ctx.Doer.CanCreateOrganization() {
ctx.ServerError("Not allowed", errors.New(ctx.Locale.TrString("org.form.create_org_not_allowed")))
return
}
+
+ ctx.Data["visibility"] = setting.Service.DefaultOrgVisibilityMode
+ ctx.Data["repo_admin_change_team_access"] = true
+
ctx.HTML(http.StatusOK, tplCreateOrg)
}
diff --git a/routers/web/org/org_labels.go b/routers/web/org/org_labels.go
index 02eae8052e..2a4aa7f557 100644
--- a/routers/web/org/org_labels.go
+++ b/routers/web/org/org_labels.go
@@ -4,13 +4,15 @@
package org
import (
- "net/http"
+ "errors"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/label"
repo_module "code.gitea.io/gitea/modules/repository"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ shared_label "code.gitea.io/gitea/routers/web/shared/label"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
@@ -32,47 +34,45 @@ func RetrieveLabels(ctx *context.Context) {
// NewLabel create new label for organization
func NewLabel(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.CreateLabelForm)
- ctx.Data["Title"] = ctx.Tr("repo.labels")
- ctx.Data["PageIsLabels"] = true
- ctx.Data["PageIsOrgSettings"] = true
-
- if ctx.HasError() {
- ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
- ctx.Redirect(ctx.Org.OrgLink + "/settings/labels")
+ form := shared_label.GetLabelEditForm(ctx)
+ if ctx.Written() {
return
}
l := &issues_model.Label{
- OrgID: ctx.Org.Organization.ID,
- Name: form.Title,
- Exclusive: form.Exclusive,
- Description: form.Description,
- Color: form.Color,
+ OrgID: ctx.Org.Organization.ID,
+ Name: form.Title,
+ Exclusive: form.Exclusive,
+ Description: form.Description,
+ Color: form.Color,
+ ExclusiveOrder: form.ExclusiveOrder,
}
if err := issues_model.NewLabel(ctx, l); err != nil {
ctx.ServerError("NewLabel", err)
return
}
- ctx.Redirect(ctx.Org.OrgLink + "/settings/labels")
+ ctx.JSONRedirect(ctx.Org.OrgLink + "/settings/labels")
}
// UpdateLabel update a label's name and color
func UpdateLabel(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.CreateLabelForm)
+ form := shared_label.GetLabelEditForm(ctx)
+ if ctx.Written() {
+ return
+ }
+
l, err := issues_model.GetLabelInOrgByID(ctx, ctx.Org.Organization.ID, form.ID)
- if err != nil {
- switch {
- case issues_model.IsErrOrgLabelNotExist(err):
- ctx.Error(http.StatusNotFound)
- default:
- ctx.ServerError("UpdateLabel", err)
- }
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.JSONErrorNotFound()
+ return
+ } else if err != nil {
+ ctx.ServerError("GetLabelInOrgByID", err)
return
}
l.Name = form.Title
l.Exclusive = form.Exclusive
+ l.ExclusiveOrder = form.ExclusiveOrder
l.Description = form.Description
l.Color = form.Color
l.SetArchived(form.IsArchived)
@@ -80,7 +80,7 @@ func UpdateLabel(ctx *context.Context) {
ctx.ServerError("UpdateLabel", err)
return
}
- ctx.Redirect(ctx.Org.OrgLink + "/settings/labels")
+ ctx.JSONRedirect(ctx.Org.OrgLink + "/settings/labels")
}
// DeleteLabel delete a label
diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go
index 32da1b41d1..059cce8281 100644
--- a/routers/web/org/projects.go
+++ b/routers/web/org/projects.go
@@ -36,24 +36,24 @@ const (
// MustEnableProjects check if projects are enabled in settings
func MustEnableProjects(ctx *context.Context) {
if unit.TypeProjects.UnitGlobalDisabled() {
- ctx.NotFound("EnableProjects", nil)
+ ctx.NotFound(nil)
return
}
}
// Projects renders the home page of projects
func Projects(ctx *context.Context) {
- shared_user.PrepareContextForProfileBigAvatar(ctx)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
ctx.Data["Title"] = ctx.Tr("repo.projects")
sortType := ctx.FormTrim("sort")
isShowClosed := strings.ToLower(ctx.FormTrim("state")) == "closed"
keyword := ctx.FormTrim("q")
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
var projectType project_model.Type
if ctx.ContextUser.IsOrganization() {
@@ -77,6 +77,11 @@ func Projects(ctx *context.Context) {
return
}
+ if err := project_service.LoadIssueNumbersForProjects(ctx, projects, ctx.Doer); err != nil {
+ ctx.ServerError("LoadIssueNumbersForProjects", err)
+ return
+ }
+
opTotal, err := db.Count[project_model.Project](ctx, project_model.SearchOptions{
OwnerID: ctx.ContextUser.ID,
IsClosed: optional.Some(!isShowClosed),
@@ -96,7 +101,6 @@ func Projects(ctx *context.Context) {
}
ctx.Data["Projects"] = projects
- shared_user.RenderUserHeader(ctx)
if isShowClosed {
ctx.Data["State"] = "closed"
@@ -108,12 +112,6 @@ func Projects(ctx *context.Context) {
project.RenderedContent = renderUtils.MarkdownToHtml(project.Description)
}
- err = shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
- return
- }
-
numPages := 0
if total > 0 {
numPages = (int(total) - 1/setting.UI.IssuePagingNum)
@@ -147,11 +145,8 @@ func RenderNewProject(ctx *context.Context) {
ctx.Data["PageIsViewProjects"] = true
ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
ctx.Data["CancelLink"] = ctx.ContextUser.HomeLink() + "/-/projects"
- shared_user.RenderUserHeader(ctx)
-
- err := shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
@@ -162,7 +157,10 @@ func RenderNewProject(ctx *context.Context) {
func NewProjectPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CreateProjectForm)
ctx.Data["Title"] = ctx.Tr("repo.projects.new")
- shared_user.RenderUserHeader(ctx)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
if ctx.HasError() {
RenderNewProject(ctx)
@@ -222,7 +220,7 @@ func DeleteProject(ctx *context.Context) {
return
}
if p.OwnerID != ctx.ContextUser.ID {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
return
}
@@ -243,7 +241,10 @@ func RenderEditProject(ctx *context.Context) {
ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
ctx.Data["CardTypes"] = project_model.GetCardConfig()
- shared_user.RenderUserHeader(ctx)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
p, err := project_model.GetProjectByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
@@ -251,7 +252,7 @@ func RenderEditProject(ctx *context.Context) {
return
}
if p.OwnerID != ctx.ContextUser.ID {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
return
}
@@ -277,11 +278,8 @@ func EditProjectPost(ctx *context.Context) {
ctx.Data["CardTypes"] = project_model.GetCardConfig()
ctx.Data["CancelLink"] = project_model.ProjectLinkForOrg(ctx.ContextUser, projectID)
- shared_user.RenderUserHeader(ctx)
-
- err := shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
@@ -296,7 +294,7 @@ func EditProjectPost(ctx *context.Context) {
return
}
if p.OwnerID != ctx.ContextUser.ID {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
return
}
@@ -324,7 +322,11 @@ func ViewProject(ctx *context.Context) {
return
}
if project.OwnerID != ctx.ContextUser.ID {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
+ return
+ }
+ if err := project.LoadOwner(ctx); err != nil {
+ ctx.ServerError("LoadOwner", err)
return
}
@@ -334,20 +336,27 @@ func ViewProject(ctx *context.Context) {
return
}
- labelIDs := issue.PrepareFilterIssueLabels(ctx, project.RepoID, project.Owner)
+ preparedLabelFilter := issue.PrepareFilterIssueLabels(ctx, project.RepoID, project.Owner)
if ctx.Written() {
return
}
- assigneeID := ctx.FormInt64("assignee") // TODO: use "optional" but not 0 in the future
+ assigneeID := ctx.FormString("assignee")
- issuesMap, err := issues_model.LoadIssuesFromColumnList(ctx, columns, &issues_model.IssuesOptions{
- LabelIDs: labelIDs,
- AssigneeID: optional.Some(assigneeID),
- })
+ opts := issues_model.IssuesOptions{
+ LabelIDs: preparedLabelFilter.SelectedLabelIDs,
+ AssigneeID: assigneeID,
+ Owner: project.Owner,
+ Doer: ctx.Doer,
+ }
+
+ issuesMap, err := project_service.LoadIssuesFromProject(ctx, project, &opts)
if err != nil {
ctx.ServerError("LoadIssuesOfColumns", err)
return
}
+ for _, column := range columns {
+ column.NumIssues = int64(len(issuesMap[column.ID]))
+ }
if project.CardType != project_model.CardTypeTextOnly {
issuesAttachmentMap := make(map[int64][]*attachment_model.Attachment)
@@ -390,8 +399,8 @@ func ViewProject(ctx *context.Context) {
}
// Get the exclusive scope for every label ID
- labelExclusiveScopes := make([]string, 0, len(labelIDs))
- for _, labelID := range labelIDs {
+ labelExclusiveScopes := make([]string, 0, len(preparedLabelFilter.SelectedLabelIDs))
+ for _, labelID := range preparedLabelFilter.SelectedLabelIDs {
foundExclusiveScope := false
for _, label := range labels {
if label.ID == labelID || label.ID == -labelID {
@@ -406,7 +415,7 @@ func ViewProject(ctx *context.Context) {
}
for _, l := range labels {
- l.LoadSelectedLabelsAfterClick(labelIDs, labelExclusiveScopes)
+ l.LoadSelectedLabelsAfterClick(preparedLabelFilter.SelectedLabelIDs, labelExclusiveScopes)
}
ctx.Data["Labels"] = labels
ctx.Data["NumLabels"] = len(labels)
@@ -427,11 +436,9 @@ func ViewProject(ctx *context.Context) {
ctx.Data["Project"] = project
ctx.Data["IssuesMap"] = issuesMap
ctx.Data["Columns"] = columns
- shared_user.RenderUserHeader(ctx)
- err = shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
@@ -593,7 +600,7 @@ func MoveIssues(ctx *context.Context) {
return
}
if project.OwnerID != ctx.ContextUser.ID {
- ctx.NotFound("InvalidRepoID", nil)
+ ctx.NotFound(nil)
return
}
@@ -604,7 +611,7 @@ func MoveIssues(ctx *context.Context) {
}
if column.ProjectID != project.ID {
- ctx.NotFound("ColumnNotInProject", nil)
+ ctx.NotFound(nil)
return
}
diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go
index cb1c4213c9..2bc1e8bc43 100644
--- a/routers/web/org/setting.go
+++ b/routers/web/org/setting.go
@@ -18,6 +18,7 @@ import (
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
user_setting "code.gitea.io/gitea/routers/web/user/setting"
@@ -31,8 +32,6 @@ import (
const (
// tplSettingsOptions template path for render settings
tplSettingsOptions templates.TplName = "org/settings/options"
- // tplSettingsDelete template path for render delete repository
- tplSettingsDelete templates.TplName = "org/settings/delete"
// tplSettingsHooks template path for render hook settings
tplSettingsHooks templates.TplName = "org/settings/hooks"
// tplSettingsLabels template path for render labels settings
@@ -48,9 +47,8 @@ func Settings(ctx *context.Context) {
ctx.Data["RepoAdminChangeTeamAccess"] = ctx.Org.Organization.RepoAdminChangeTeamAccess
ctx.Data["ContextUser"] = ctx.ContextUser
- err := shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
@@ -72,26 +70,6 @@ func SettingsPost(ctx *context.Context) {
org := ctx.Org.Organization
- if org.Name != form.Name {
- if err := user_service.RenameUser(ctx, org.AsUser(), form.Name); err != nil {
- if user_model.IsErrUserAlreadyExist(err) {
- ctx.Data["Err_Name"] = true
- ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplSettingsOptions, &form)
- } else if db.IsErrNameReserved(err) {
- ctx.Data["Err_Name"] = true
- ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplSettingsOptions, &form)
- } else if db.IsErrNamePatternNotAllowed(err) {
- ctx.Data["Err_Name"] = true
- ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplSettingsOptions, &form)
- } else {
- ctx.ServerError("RenameUser", err)
- }
- return
- }
-
- ctx.Org.OrgLink = setting.AppSubURL + "/org/" + url.PathEscape(org.Name)
- }
-
if form.Email != "" {
if err := user_service.ReplacePrimaryEmailAddress(ctx, org.AsUser(), form.Email); err != nil {
ctx.Data["Err_Email"] = true
@@ -121,7 +99,7 @@ func SettingsPost(ctx *context.Context) {
// update forks visibility
if visibilityChanged {
- repos, _, err := repo_model.GetUserRepositories(ctx, &repo_model.SearchRepoOptions{
+ repos, _, err := repo_model.GetUserRepositories(ctx, repo_model.SearchRepoOptions{
Actor: org.AsUser(), Private: true, ListOptions: db.ListOptions{Page: 1, PageSize: org.NumRepos},
})
if err != nil {
@@ -164,43 +142,27 @@ func SettingsDeleteAvatar(ctx *context.Context) {
ctx.JSONRedirect(ctx.Org.OrgLink + "/settings")
}
-// SettingsDelete response for deleting an organization
-func SettingsDelete(ctx *context.Context) {
- ctx.Data["Title"] = ctx.Tr("org.settings")
- ctx.Data["PageIsOrgSettings"] = true
- ctx.Data["PageIsSettingsDelete"] = true
-
- if ctx.Req.Method == "POST" {
- if ctx.Org.Organization.Name != ctx.FormString("org_name") {
- ctx.Data["Err_OrgName"] = true
- ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_org_name"), tplSettingsDelete, nil)
- return
- }
-
- if err := org_service.DeleteOrganization(ctx, ctx.Org.Organization, false); err != nil {
- if repo_model.IsErrUserOwnRepos(err) {
- ctx.Flash.Error(ctx.Tr("form.org_still_own_repo"))
- ctx.Redirect(ctx.Org.OrgLink + "/settings/delete")
- } else if packages_model.IsErrUserOwnPackages(err) {
- ctx.Flash.Error(ctx.Tr("form.org_still_own_packages"))
- ctx.Redirect(ctx.Org.OrgLink + "/settings/delete")
- } else {
- ctx.ServerError("DeleteOrganization", err)
- }
- } else {
- log.Trace("Organization deleted: %s", ctx.Org.Organization.Name)
- ctx.Redirect(setting.AppSubURL + "/")
- }
+// SettingsDeleteOrgPost response for deleting an organization
+func SettingsDeleteOrgPost(ctx *context.Context) {
+ if ctx.Org.Organization.Name != ctx.FormString("org_name") {
+ ctx.JSONError(ctx.Tr("form.enterred_invalid_org_name"))
return
}
- err := shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if err := org_service.DeleteOrganization(ctx, ctx.Org.Organization, false /* no purge */); err != nil {
+ if repo_model.IsErrUserOwnRepos(err) {
+ ctx.JSONError(ctx.Tr("form.org_still_own_repo"))
+ } else if packages_model.IsErrUserOwnPackages(err) {
+ ctx.JSONError(ctx.Tr("form.org_still_own_packages"))
+ } else {
+ log.Error("DeleteOrganization: %v", err)
+ ctx.JSONError(util.Iif(ctx.Doer.IsAdmin, err.Error(), string(ctx.Tr("org.settings.delete_failed"))))
+ }
return
}
- ctx.HTML(http.StatusOK, tplSettingsDelete)
+ ctx.Flash.Success(ctx.Tr("org.settings.delete_successful", ctx.Org.Organization.Name))
+ ctx.JSONRedirect(setting.AppSubURL + "/")
}
// Webhooks render webhook list page
@@ -218,9 +180,8 @@ func Webhooks(ctx *context.Context) {
return
}
- err = shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
@@ -246,11 +207,47 @@ func Labels(ctx *context.Context) {
ctx.Data["PageIsOrgSettingsLabels"] = true
ctx.Data["LabelTemplateFiles"] = repo_module.LabelTemplateFiles
- err := shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
ctx.HTML(http.StatusOK, tplSettingsLabels)
}
+
+// SettingsRenamePost response for renaming organization
+func SettingsRenamePost(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RenameOrgForm)
+ if ctx.HasError() {
+ ctx.JSONError(ctx.GetErrMsg())
+ return
+ }
+
+ oldOrgName, newOrgName := ctx.Org.Organization.Name, form.NewOrgName
+
+ if form.OrgName != oldOrgName {
+ ctx.JSONError(ctx.Tr("form.enterred_invalid_org_name"))
+ return
+ }
+ if newOrgName == oldOrgName {
+ ctx.JSONError(ctx.Tr("org.settings.rename_no_change"))
+ return
+ }
+
+ if err := user_service.RenameUser(ctx, ctx.Org.Organization.AsUser(), newOrgName); err != nil {
+ if user_model.IsErrUserAlreadyExist(err) {
+ ctx.JSONError(ctx.Tr("org.form.name_been_taken", newOrgName))
+ } else if db.IsErrNameReserved(err) {
+ ctx.JSONError(ctx.Tr("org.form.name_reserved", newOrgName))
+ } else if db.IsErrNamePatternNotAllowed(err) {
+ ctx.JSONError(ctx.Tr("org.form.name_pattern_not_allowed", newOrgName))
+ } else {
+ log.Error("RenameOrganization: %v", err)
+ ctx.JSONError(util.Iif(ctx.Doer.IsAdmin, err.Error(), string(ctx.Tr("org.settings.rename_failed"))))
+ }
+ return
+ }
+
+ ctx.Flash.Success(ctx.Tr("org.settings.rename_success", oldOrgName, newOrgName))
+ ctx.JSONRedirect(setting.AppSubURL + "/org/" + url.PathEscape(newOrgName) + "/settings")
+}
diff --git a/routers/web/org/setting_oauth2.go b/routers/web/org/setting_oauth2.go
index c93058477e..47f653bf88 100644
--- a/routers/web/org/setting_oauth2.go
+++ b/routers/web/org/setting_oauth2.go
@@ -45,9 +45,8 @@ func Applications(ctx *context.Context) {
}
ctx.Data["Applications"] = apps
- err = shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
diff --git a/routers/web/org/setting_packages.go b/routers/web/org/setting_packages.go
index 0912a9e0fd..ec80e2867c 100644
--- a/routers/web/org/setting_packages.go
+++ b/routers/web/org/setting_packages.go
@@ -25,9 +25,8 @@ func Packages(ctx *context.Context) {
ctx.Data["PageIsOrgSettings"] = true
ctx.Data["PageIsSettingsPackages"] = true
- err := shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
@@ -41,9 +40,8 @@ func PackagesRuleAdd(ctx *context.Context) {
ctx.Data["PageIsOrgSettings"] = true
ctx.Data["PageIsSettingsPackages"] = true
- err := shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
@@ -57,9 +55,8 @@ func PackagesRuleEdit(ctx *context.Context) {
ctx.Data["PageIsOrgSettings"] = true
ctx.Data["PageIsSettingsPackages"] = true
- err := shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
@@ -99,9 +96,8 @@ func PackagesRulePreview(ctx *context.Context) {
ctx.Data["PageIsOrgSettings"] = true
ctx.Data["PageIsSettingsPackages"] = true
- err := shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go
index 26031029d6..0ec7cfddc5 100644
--- a/routers/web/org/teams.go
+++ b/routers/web/org/teams.go
@@ -46,6 +46,10 @@ const (
// Teams render teams list page
func Teams(ctx *context.Context) {
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
org := ctx.Org.Organization
ctx.Data["Title"] = org.FullName
ctx.Data["PageIsOrgTeams"] = true
@@ -58,12 +62,6 @@ func Teams(ctx *context.Context) {
}
ctx.Data["Teams"] = ctx.Org.Teams
- _, err := shared_user.PrepareOrgHeader(ctx)
- if err != nil {
- ctx.ServerError("PrepareOrgHeader", err)
- return
- }
-
ctx.HTML(http.StatusOK, tplTeams)
}
@@ -74,7 +72,7 @@ func TeamsAction(ctx *context.Context) {
switch ctx.PathParam("action") {
case "join":
if !ctx.Org.IsOwner {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
err = org_service.AddTeamMember(ctx, ctx.Org.Team, ctx.Doer)
@@ -96,7 +94,7 @@ func TeamsAction(ctx *context.Context) {
return
case "remove":
if !ctx.Org.IsOwner {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
@@ -123,7 +121,7 @@ func TeamsAction(ctx *context.Context) {
return
case "add":
if !ctx.Org.IsOwner {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
uname := strings.ToLower(ctx.FormString("uname"))
@@ -167,7 +165,7 @@ func TeamsAction(ctx *context.Context) {
page = "team"
case "remove_invite":
if !ctx.Org.IsOwner {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
@@ -228,7 +226,7 @@ func checkIsOrgMemberAndRedirect(ctx *context.Context, defaultRedirect string) {
// TeamsRepoAction operate team's repository
func TeamsRepoAction(ctx *context.Context) {
if !ctx.Org.IsOwner {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
@@ -272,22 +270,35 @@ func TeamsRepoAction(ctx *context.Context) {
// NewTeam render create new team page
func NewTeam(ctx *context.Context) {
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
ctx.Data["Title"] = ctx.Org.Organization.FullName
ctx.Data["PageIsOrgTeams"] = true
ctx.Data["PageIsOrgTeamsNew"] = true
ctx.Data["Team"] = &org_model.Team{}
ctx.Data["Units"] = unit_model.Units
- if err := shared_user.LoadHeaderCount(ctx); err != nil {
- ctx.ServerError("LoadHeaderCount", err)
- return
- }
ctx.HTML(http.StatusOK, tplTeamNew)
}
+// FIXME: TEAM-UNIT-PERMISSION: this design is not right, when a new unit is added in the future,
+// The existing teams won't inherit the correct admin permission for the new unit.
+// The full history is like this:
+// 1. There was only "team", no "team unit", so "team.authorize" was used to determine the team permission.
+// 2. Later, "team unit" was introduced, then the usage of "team.authorize" became inconsistent, and causes various bugs.
+// - Sometimes, "team.authorize" is used to determine the team permission, e.g. admin, owner
+// - Sometimes, "team unit" is used not really used and "team unit" is used.
+// - Some functions like `GetTeamsWithAccessToAnyRepoUnit` use both.
+//
+// 3. After introducing "team unit" and more unclear changes, it becomes difficult to maintain team permissions.
+// - Org owner need to click the permission for each unit, but can't just set a common "write" permission for all units.
+//
+// Ideally, "team.authorize=write" should mean the team has write access to all units including newly (future) added ones.
func getUnitPerms(forms url.Values, teamPermission perm.AccessMode) map[unit_model.Type]perm.AccessMode {
unitPerms := make(map[unit_model.Type]perm.AccessMode)
for _, ut := range unit_model.AllRepoUnitTypes {
- // Default accessmode is none
+ // Default access mode is none
unitPerms[ut] = perm.AccessModeNone
v, ok := forms[fmt.Sprintf("unit_%d", ut)]
@@ -314,19 +325,14 @@ func getUnitPerms(forms url.Values, teamPermission perm.AccessMode) map[unit_mod
func NewTeamPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CreateTeamForm)
includesAllRepositories := form.RepoAccess == "all"
- p := perm.ParseAccessMode(form.Permission)
- unitPerms := getUnitPerms(ctx.Req.Form, p)
- if p < perm.AccessModeAdmin {
- // if p is less than admin accessmode, then it should be general accessmode,
- // so we should calculate the minial accessmode from units accessmodes.
- p = unit_model.MinUnitAccessMode(unitPerms)
- }
+ teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin)
+ unitPerms := getUnitPerms(ctx.Req.Form, teamPermission)
t := &org_model.Team{
OrgID: ctx.Org.Organization.ID,
Name: form.TeamName,
Description: form.Description,
- AccessMode: p,
+ AccessMode: teamPermission,
IncludesAllRepositories: includesAllRepositories,
CanCreateOrgRepo: form.CanCreateOrgRepo,
}
@@ -373,15 +379,15 @@ func NewTeamPost(ctx *context.Context) {
// TeamMembers render team members page
func TeamMembers(ctx *context.Context) {
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
+
ctx.Data["Title"] = ctx.Org.Team.Name
ctx.Data["PageIsOrgTeams"] = true
ctx.Data["PageIsOrgTeamMembers"] = true
- if err := shared_user.LoadHeaderCount(ctx); err != nil {
- ctx.ServerError("LoadHeaderCount", err)
- return
- }
-
if err := ctx.Org.Team.LoadMembers(ctx); err != nil {
ctx.ServerError("GetMembers", err)
return
@@ -401,15 +407,15 @@ func TeamMembers(ctx *context.Context) {
// TeamRepositories show the repositories of team
func TeamRepositories(ctx *context.Context) {
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
+
ctx.Data["Title"] = ctx.Org.Team.Name
ctx.Data["PageIsOrgTeams"] = true
ctx.Data["PageIsOrgTeamRepos"] = true
- if err := shared_user.LoadHeaderCount(ctx); err != nil {
- ctx.ServerError("LoadHeaderCount", err)
- return
- }
-
repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
TeamID: ctx.Org.Team.ID,
})
@@ -466,16 +472,16 @@ func SearchTeam(ctx *context.Context) {
// EditTeam render team edit page
func EditTeam(ctx *context.Context) {
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
ctx.Data["Title"] = ctx.Org.Organization.FullName
ctx.Data["PageIsOrgTeams"] = true
if err := ctx.Org.Team.LoadUnits(ctx); err != nil {
ctx.ServerError("LoadUnits", err)
return
}
- if err := shared_user.LoadHeaderCount(ctx); err != nil {
- ctx.ServerError("LoadHeaderCount", err)
- return
- }
ctx.Data["Team"] = ctx.Org.Team
ctx.Data["Units"] = unit_model.Units
ctx.HTML(http.StatusOK, tplTeamNew)
@@ -485,13 +491,8 @@ func EditTeam(ctx *context.Context) {
func EditTeamPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CreateTeamForm)
t := ctx.Org.Team
- newAccessMode := perm.ParseAccessMode(form.Permission)
- unitPerms := getUnitPerms(ctx.Req.Form, newAccessMode)
- if newAccessMode < perm.AccessModeAdmin {
- // if newAccessMode is less than admin accessmode, then it should be general accessmode,
- // so we should calculate the minial accessmode from units accessmodes.
- newAccessMode = unit_model.MinUnitAccessMode(unitPerms)
- }
+ teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin)
+ unitPerms := getUnitPerms(ctx.Req.Form, teamPermission)
isAuthChanged := false
isIncludeAllChanged := false
includesAllRepositories := form.RepoAccess == "all"
@@ -503,9 +504,9 @@ func EditTeamPost(ctx *context.Context) {
if !t.IsOwnerTeam() {
t.Name = form.TeamName
- if t.AccessMode != newAccessMode {
+ if t.AccessMode != teamPermission {
isAuthChanged = true
- t.AccessMode = newAccessMode
+ t.AccessMode = teamPermission
}
if t.IncludesAllRepositories != includesAllRepositories {
@@ -568,7 +569,7 @@ func TeamInvite(ctx *context.Context) {
invite, org, team, inviter, err := getTeamInviteFromContext(ctx)
if err != nil {
if org_model.IsErrTeamInviteNotFound(err) {
- ctx.NotFound("ErrTeamInviteNotFound", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("getTeamInviteFromContext", err)
}
@@ -589,7 +590,7 @@ func TeamInvitePost(ctx *context.Context) {
invite, org, team, _, err := getTeamInviteFromContext(ctx)
if err != nil {
if org_model.IsErrTeamInviteNotFound(err) {
- ctx.NotFound("ErrTeamInviteNotFound", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("getTeamInviteFromContext", err)
}
diff --git a/routers/web/org/worktime.go b/routers/web/org/worktime.go
new file mode 100644
index 0000000000..c7b44baf7b
--- /dev/null
+++ b/routers/web/org/worktime.go
@@ -0,0 +1,82 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package org
+
+import (
+ "net/http"
+ "time"
+
+ "code.gitea.io/gitea/models/organization"
+ "code.gitea.io/gitea/modules/templates"
+ shared_user "code.gitea.io/gitea/routers/web/shared/user"
+ "code.gitea.io/gitea/services/context"
+)
+
+const tplByRepos templates.TplName = "org/worktime"
+
+// parseOrgTimes contains functionality that is required in all these functions,
+// like parsing the date from the request, setting default dates, etc.
+func parseOrgTimes(ctx *context.Context) (unixFrom, unixTo int64) {
+ rangeFrom := ctx.FormString("from")
+ rangeTo := ctx.FormString("to")
+ if rangeFrom == "" {
+ rangeFrom = time.Now().Format("2006-01") + "-01" // defaults to start of current month
+ }
+ if rangeTo == "" {
+ rangeTo = time.Now().Format("2006-01-02") // defaults to today
+ }
+
+ ctx.Data["RangeFrom"] = rangeFrom
+ ctx.Data["RangeTo"] = rangeTo
+
+ timeFrom, err := time.Parse("2006-01-02", rangeFrom)
+ if err != nil {
+ ctx.ServerError("time.Parse", err)
+ }
+ timeTo, err := time.Parse("2006-01-02", rangeTo)
+ if err != nil {
+ ctx.ServerError("time.Parse", err)
+ }
+ unixFrom = timeFrom.Unix()
+ unixTo = timeTo.Add(1440*time.Minute - 1*time.Second).Unix() // humans expect that we include the ending day too
+ return unixFrom, unixTo
+}
+
+func Worktime(ctx *context.Context) {
+ ctx.Data["PageIsOrgTimes"] = true
+
+ unixFrom, unixTo := parseOrgTimes(ctx)
+ if ctx.Written() {
+ return
+ }
+
+ worktimeBy := ctx.FormString("by")
+ ctx.Data["WorktimeBy"] = worktimeBy
+
+ var worktimeSumResult any
+ var err error
+ switch worktimeBy {
+ case "milestones":
+ worktimeSumResult, err = organization.GetWorktimeByMilestones(ctx.Org.Organization, unixFrom, unixTo)
+ ctx.Data["WorktimeByMilestones"] = true
+ case "members":
+ worktimeSumResult, err = organization.GetWorktimeByMembers(ctx.Org.Organization, unixFrom, unixTo)
+ ctx.Data["WorktimeByMembers"] = true
+ default: /* by repos */
+ worktimeSumResult, err = organization.GetWorktimeByRepos(ctx.Org.Organization, unixFrom, unixTo)
+ ctx.Data["WorktimeByRepos"] = true
+ }
+ if err != nil {
+ ctx.ServerError("GetWorktime", err)
+ return
+ }
+
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
+
+ ctx.Data["WorktimeSumResult"] = worktimeSumResult
+ ctx.HTML(http.StatusOK, tplByRepos)
+}
diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go
index f0d8d81fee..202da407d2 100644
--- a/routers/web/repo/actions/actions.go
+++ b/routers/web/repo/actions/actions.go
@@ -6,6 +6,7 @@ package actions
import (
"bytes"
stdCtx "context"
+ "errors"
"net/http"
"slices"
"strings"
@@ -32,8 +33,9 @@ import (
)
const (
- tplListActions templates.TplName = "repo/actions/list"
- tplViewActions templates.TplName = "repo/actions/view"
+ tplListActions templates.TplName = "repo/actions/list"
+ tplDispatchInputsActions templates.TplName = "repo/actions/workflow_dispatch_inputs"
+ tplViewActions templates.TplName = "repo/actions/view"
)
type Workflow struct {
@@ -44,18 +46,18 @@ type Workflow struct {
// MustEnableActions check if actions are enabled in settings
func MustEnableActions(ctx *context.Context) {
if !setting.Actions.Enabled {
- ctx.NotFound("MustEnableActions", nil)
+ ctx.NotFound(nil)
return
}
if unit.TypeActions.UnitGlobalDisabled() {
- ctx.NotFound("MustEnableActions", nil)
+ ctx.NotFound(nil)
return
}
if ctx.Repo.Repository != nil {
if !ctx.Repo.CanRead(unit.TypeActions) {
- ctx.NotFound("MustEnableActions", nil)
+ ctx.NotFound(nil)
return
}
}
@@ -64,107 +66,147 @@ func MustEnableActions(ctx *context.Context) {
func List(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("actions.actions")
ctx.Data["PageIsActions"] = true
+
+ commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.Data["NotFoundPrompt"] = ctx.Tr("repo.branch.default_branch_not_exist", ctx.Repo.Repository.DefaultBranch)
+ ctx.NotFound(nil)
+ return
+ } else if err != nil {
+ ctx.ServerError("GetBranchCommit", err)
+ return
+ }
+
+ workflows := prepareWorkflowDispatchTemplate(ctx, commit)
+ if ctx.Written() {
+ return
+ }
+
+ prepareWorkflowList(ctx, workflows)
+ if ctx.Written() {
+ return
+ }
+
+ ctx.HTML(http.StatusOK, tplListActions)
+}
+
+func WorkflowDispatchInputs(ctx *context.Context) {
+ ref := ctx.FormString("ref")
+ if ref == "" {
+ ctx.NotFound(nil)
+ return
+ }
+ // get target commit of run from specified ref
+ refName := git.RefName(ref)
+ var commit *git.Commit
+ var err error
+ if refName.IsTag() {
+ commit, err = ctx.Repo.GitRepo.GetTagCommit(refName.TagName())
+ } else if refName.IsBranch() {
+ commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName.BranchName())
+ } else {
+ ctx.ServerError("UnsupportedRefType", nil)
+ return
+ }
+ if err != nil {
+ ctx.ServerError("GetTagCommit/GetBranchCommit", err)
+ return
+ }
+ prepareWorkflowDispatchTemplate(ctx, commit)
+ if ctx.Written() {
+ return
+ }
+ ctx.HTML(http.StatusOK, tplDispatchInputsActions)
+}
+
+func prepareWorkflowDispatchTemplate(ctx *context.Context, commit *git.Commit) (workflows []Workflow) {
workflowID := ctx.FormString("workflow")
- actorID := ctx.FormInt64("actor")
- status := ctx.FormInt("status")
ctx.Data["CurWorkflow"] = workflowID
+ ctx.Data["CurWorkflowExists"] = false
- var workflows []Workflow
var curWorkflow *model.Workflow
- if empty, err := ctx.Repo.GitRepo.IsEmpty(); err != nil {
- ctx.ServerError("IsEmpty", err)
- return
- } else if !empty {
- commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
- if err != nil {
- ctx.ServerError("GetBranchCommit", err)
- return
- }
- entries, err := actions.ListWorkflows(commit)
- if err != nil {
- ctx.ServerError("ListWorkflows", err)
- return
- }
- // Get all runner labels
- runners, err := db.Find[actions_model.ActionRunner](ctx, actions_model.FindRunnerOptions{
- RepoID: ctx.Repo.Repository.ID,
- IsOnline: optional.Some(true),
- WithAvailable: true,
- })
+ _, entries, err := actions.ListWorkflows(commit)
+ if err != nil {
+ ctx.ServerError("ListWorkflows", err)
+ return nil
+ }
+
+ // Get all runner labels
+ runners, err := db.Find[actions_model.ActionRunner](ctx, actions_model.FindRunnerOptions{
+ RepoID: ctx.Repo.Repository.ID,
+ IsOnline: optional.Some(true),
+ WithAvailable: true,
+ })
+ if err != nil {
+ ctx.ServerError("FindRunners", err)
+ return nil
+ }
+ allRunnerLabels := make(container.Set[string])
+ for _, r := range runners {
+ allRunnerLabels.AddMultiple(r.AgentLabels...)
+ }
+
+ workflows = make([]Workflow, 0, len(entries))
+ for _, entry := range entries {
+ workflow := Workflow{Entry: *entry}
+ content, err := actions.GetContentFromEntry(entry)
if err != nil {
- ctx.ServerError("FindRunners", err)
- return
+ ctx.ServerError("GetContentFromEntry", err)
+ return nil
}
- allRunnerLabels := make(container.Set[string])
- for _, r := range runners {
- allRunnerLabels.AddMultiple(r.AgentLabels...)
+ wf, err := model.ReadWorkflow(bytes.NewReader(content))
+ if err != nil {
+ workflow.ErrMsg = ctx.Locale.TrString("actions.runs.invalid_workflow_helper", err.Error())
+ workflows = append(workflows, workflow)
+ continue
}
-
- workflows = make([]Workflow, 0, len(entries))
- for _, entry := range entries {
- workflow := Workflow{Entry: *entry}
- content, err := actions.GetContentFromEntry(entry)
- if err != nil {
- ctx.ServerError("GetContentFromEntry", err)
- return
- }
- wf, err := model.ReadWorkflow(bytes.NewReader(content))
- if err != nil {
- workflow.ErrMsg = ctx.Locale.TrString("actions.runs.invalid_workflow_helper", err.Error())
- workflows = append(workflows, workflow)
+ // The workflow must contain at least one job without "needs". Otherwise, a deadlock will occur and no jobs will be able to run.
+ hasJobWithoutNeeds := false
+ // Check whether you have matching runner and a job without "needs"
+ emptyJobsNumber := 0
+ for _, j := range wf.Jobs {
+ if j == nil {
+ emptyJobsNumber++
continue
}
- // The workflow must contain at least one job without "needs". Otherwise, a deadlock will occur and no jobs will be able to run.
- hasJobWithoutNeeds := false
- // Check whether have matching runner and a job without "needs"
- emptyJobsNumber := 0
- for _, j := range wf.Jobs {
- if j == nil {
- emptyJobsNumber++
+ if !hasJobWithoutNeeds && len(j.Needs()) == 0 {
+ hasJobWithoutNeeds = true
+ }
+ runsOnList := j.RunsOn()
+ for _, ro := range runsOnList {
+ if strings.Contains(ro, "${{") {
+ // Skip if it contains expressions.
+ // The expressions could be very complex and could not be evaluated here,
+ // so just skip it, it's OK since it's just a tooltip message.
continue
}
- if !hasJobWithoutNeeds && len(j.Needs()) == 0 {
- hasJobWithoutNeeds = true
- }
- runsOnList := j.RunsOn()
- for _, ro := range runsOnList {
- if strings.Contains(ro, "${{") {
- // Skip if it contains expressions.
- // The expressions could be very complex and could not be evaluated here,
- // so just skip it, it's OK since it's just a tooltip message.
- continue
- }
- if !allRunnerLabels.Contains(ro) {
- workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_matching_online_runner_helper", ro)
- break
- }
- }
- if workflow.ErrMsg != "" {
+ if !allRunnerLabels.Contains(ro) {
+ workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_matching_online_runner_helper", ro)
break
}
}
- if !hasJobWithoutNeeds {
- workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_job_without_needs")
- }
- if emptyJobsNumber == len(wf.Jobs) {
- workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_job")
+ if workflow.ErrMsg != "" {
+ break
}
- workflows = append(workflows, workflow)
+ }
+ if !hasJobWithoutNeeds {
+ workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_job_without_needs")
+ }
+ if emptyJobsNumber == len(wf.Jobs) {
+ workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_job")
+ }
+ workflows = append(workflows, workflow)
- if workflow.Entry.Name() == workflowID {
- curWorkflow = wf
- }
+ if workflow.Entry.Name() == workflowID {
+ curWorkflow = wf
+ ctx.Data["CurWorkflowExists"] = true
}
}
+
ctx.Data["workflows"] = workflows
ctx.Data["RepoLink"] = ctx.Repo.Repository.Link()
- page := ctx.FormInt("page")
- if page <= 0 {
- page = 1
- }
-
actionsConfig := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions).ActionsConfig()
ctx.Data["ActionsConfig"] = actionsConfig
@@ -188,7 +230,7 @@ func List(ctx *context.Context) {
branches, err := git_model.FindBranchNames(ctx, branchOpts)
if err != nil {
ctx.ServerError("FindBranchNames", err)
- return
+ return nil
}
// always put default branch on the top if it exists
if slices.Contains(branches, ctx.Repo.Repository.DefaultBranch) {
@@ -200,12 +242,23 @@ func List(ctx *context.Context) {
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
if err != nil {
ctx.ServerError("GetTagNamesByRepoID", err)
- return
+ return nil
}
ctx.Data["Tags"] = tags
}
}
}
+ return workflows
+}
+
+func prepareWorkflowList(ctx *context.Context, workflows []Workflow) {
+ actorID := ctx.FormInt64("actor")
+ status := ctx.FormInt("status")
+ workflowID := ctx.FormString("workflow")
+ page := ctx.FormInt("page")
+ if page <= 0 {
+ page = 1
+ }
// if status or actor query param is not given to frontend href, (href="/<repoLink>/actions")
// they will be 0 by default, which indicates get all status or actors
@@ -258,14 +311,14 @@ func List(ctx *context.Context) {
}
ctx.Data["Actors"] = shared_user.MakeSelfOnTop(ctx.Doer, actors)
- ctx.Data["StatusInfoList"] = actions_model.GetStatusInfoList(ctx)
+ ctx.Data["StatusInfoList"] = actions_model.GetStatusInfoList(ctx, ctx.Locale)
pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5)
pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager
ctx.Data["HasWorkflowsOrRuns"] = len(workflows) > 0 || len(runs) > 0
- ctx.HTML(http.StatusOK, tplListActions)
+ ctx.Data["CanWriteRepoUnitActions"] = ctx.Repo.CanWrite(unit.TypeActions)
}
// loadIsRefDeleted loads the IsRefDeleted field for each run in the list.
@@ -324,10 +377,8 @@ func workflowDispatchConfig(w *model.Workflow) *WorkflowDispatch {
if !decodeNode(w.RawOn, &val) {
return nil
}
- for _, v := range val {
- if v == "workflow_dispatch" {
- return &WorkflowDispatch{}
- }
+ if slices.Contains(val, "workflow_dispatch") {
+ return &WorkflowDispatch{}
}
case yaml.MappingNode:
var val map[string]yaml.Node
diff --git a/routers/web/repo/actions/badge.go b/routers/web/repo/actions/badge.go
index e920ecaf58..d268a8df8a 100644
--- a/routers/web/repo/actions/badge.go
+++ b/routers/web/repo/actions/badge.go
@@ -5,35 +5,38 @@ package actions
import (
"errors"
- "fmt"
"net/http"
"path/filepath"
"strings"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/modules/badge"
+ "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context"
)
func GetWorkflowBadge(ctx *context.Context) {
workflowFile := ctx.PathParam("workflow_name")
- branch := ctx.Req.URL.Query().Get("branch")
- if branch == "" {
- branch = ctx.Repo.Repository.DefaultBranch
- }
- branchRef := fmt.Sprintf("refs/heads/%s", branch)
- event := ctx.Req.URL.Query().Get("event")
+ branch := ctx.FormString("branch", ctx.Repo.Repository.DefaultBranch)
+ event := ctx.FormString("event")
+ style := ctx.FormString("style")
- badge, err := getWorkflowBadge(ctx, workflowFile, branchRef, event)
+ branchRef := git.RefNameFromBranch(branch)
+ b, err := getWorkflowBadge(ctx, workflowFile, branchRef.String(), event)
if err != nil {
ctx.ServerError("GetWorkflowBadge", err)
return
}
- ctx.Data["Badge"] = badge
+ ctx.Data["Badge"] = b
ctx.RespHeader().Set("Content-Type", "image/svg+xml")
- ctx.HTML(http.StatusOK, "shared/actions/runner_badge")
+ switch style {
+ case badge.StyleFlatSquare:
+ ctx.HTML(http.StatusOK, "shared/actions/runner_badge_flat-square")
+ default: // defaults to badge.StyleFlat
+ ctx.HTML(http.StatusOK, "shared/actions/runner_badge_flat")
+ }
}
func getWorkflowBadge(ctx *context.Context, workflowFile, branchName, event string) (badge.Badge, error) {
@@ -48,7 +51,7 @@ func getWorkflowBadge(ctx *context.Context, workflowFile, branchName, event stri
return badge.Badge{}, err
}
- color, ok := badge.StatusColorMap[run.Status]
+ color, ok := badge.GlobalVars().StatusColorMap[run.Status]
if !ok {
return badge.GenerateBadge(workflowName, "unknown status", badge.DefaultColor), nil
}
diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go
index ba17fa427d..7e1b923fa4 100644
--- a/routers/web/repo/actions/view.go
+++ b/routers/web/repo/actions/view.go
@@ -14,32 +14,27 @@ import (
"net/http"
"net/url"
"strconv"
- "strings"
"time"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/perm"
- access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
- api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/routers/common"
actions_service "code.gitea.io/gitea/services/actions"
context_module "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/convert"
+ notify_service "code.gitea.io/gitea/services/notify"
- "github.com/nektos/act/pkg/jobparser"
"github.com/nektos/act/pkg/model"
"xorm.io/builder"
)
@@ -69,6 +64,36 @@ func View(ctx *context_module.Context) {
ctx.HTML(http.StatusOK, tplViewActions)
}
+func ViewWorkflowFile(ctx *context_module.Context) {
+ runIndex := getRunIndex(ctx)
+ run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex)
+ if err != nil {
+ ctx.NotFoundOrServerError("GetRunByIndex", func(err error) bool {
+ return errors.Is(err, util.ErrNotExist)
+ }, err)
+ return
+ }
+ commit, err := ctx.Repo.GitRepo.GetCommit(run.CommitSHA)
+ if err != nil {
+ ctx.NotFoundOrServerError("GetCommit", func(err error) bool {
+ return errors.Is(err, util.ErrNotExist)
+ }, err)
+ return
+ }
+ rpath, entries, err := actions.ListWorkflows(commit)
+ if err != nil {
+ ctx.ServerError("ListWorkflows", err)
+ return
+ }
+ for _, entry := range entries {
+ if entry.Name() == run.WorkflowID {
+ ctx.Redirect(fmt.Sprintf("%s/src/commit/%s/%s/%s", ctx.Repo.RepoLink, url.PathEscape(run.CommitSHA), util.PathEscapeSegments(rpath), util.PathEscapeSegments(run.WorkflowID)))
+ return
+ }
+ }
+ ctx.NotFound(nil)
+}
+
type LogCursor struct {
Step int `json:"step"`
Cursor int64 `json:"cursor"`
@@ -205,13 +230,9 @@ func ViewPost(ctx *context_module.Context) {
}
}
- // TODO: "ComposeMetas" (usually for comment) is not quite right, but it is still the same as what template "RenderCommitMessage" does.
- // need to be refactored together in the future
- metas := ctx.Repo.Repository.ComposeMetas(ctx)
-
// the title for the "run" is from the commit message
resp.State.Run.Title = run.Title
- resp.State.Run.TitleHTML = templates.NewRenderUtils(ctx).RenderCommitMessage(run.Title, metas)
+ resp.State.Run.TitleHTML = templates.NewRenderUtils(ctx).RenderCommitMessage(run.Title, ctx.Repo.Repository)
resp.State.Run.Link = run.Link()
resp.State.Run.CanCancel = !run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions)
resp.State.Run.CanApprove = run.NeedApproval && ctx.Repo.CanWrite(unit.TypeActions)
@@ -281,84 +302,98 @@ func ViewPost(ctx *context_module.Context) {
resp.State.CurrentJob.Steps = make([]*ViewJobStep, 0) // marshal to '[]' instead fo 'null' in json
resp.Logs.StepsLog = make([]*ViewStepLog, 0) // marshal to '[]' instead fo 'null' in json
if task != nil {
- steps := actions.FullSteps(task)
-
- for _, v := range steps {
- resp.State.CurrentJob.Steps = append(resp.State.CurrentJob.Steps, &ViewJobStep{
- Summary: v.Name,
- Duration: v.Duration().String(),
- Status: v.Status.String(),
- })
+ steps, logs, err := convertToViewModel(ctx, req.LogCursors, task)
+ if err != nil {
+ ctx.ServerError("convertToViewModel", err)
+ return
}
+ resp.State.CurrentJob.Steps = append(resp.State.CurrentJob.Steps, steps...)
+ resp.Logs.StepsLog = append(resp.Logs.StepsLog, logs...)
+ }
- for _, cursor := range req.LogCursors {
- if !cursor.Expanded {
- continue
- }
+ ctx.JSON(http.StatusOK, resp)
+}
- step := steps[cursor.Step]
-
- // if task log is expired, return a consistent log line
- if task.LogExpired {
- if cursor.Cursor == 0 {
- resp.Logs.StepsLog = append(resp.Logs.StepsLog, &ViewStepLog{
- Step: cursor.Step,
- Cursor: 1,
- Lines: []*ViewStepLogLine{
- {
- Index: 1,
- Message: ctx.Locale.TrString("actions.runs.expire_log_message"),
- // Timestamp doesn't mean anything when the log is expired.
- // Set it to the task's updated time since it's probably the time when the log has expired.
- Timestamp: float64(task.Updated.AsTime().UnixNano()) / float64(time.Second),
- },
+func convertToViewModel(ctx *context_module.Context, cursors []LogCursor, task *actions_model.ActionTask) ([]*ViewJobStep, []*ViewStepLog, error) {
+ var viewJobs []*ViewJobStep
+ var logs []*ViewStepLog
+
+ steps := actions.FullSteps(task)
+
+ for _, v := range steps {
+ viewJobs = append(viewJobs, &ViewJobStep{
+ Summary: v.Name,
+ Duration: v.Duration().String(),
+ Status: v.Status.String(),
+ })
+ }
+
+ for _, cursor := range cursors {
+ if !cursor.Expanded {
+ continue
+ }
+
+ step := steps[cursor.Step]
+
+ // if task log is expired, return a consistent log line
+ if task.LogExpired {
+ if cursor.Cursor == 0 {
+ logs = append(logs, &ViewStepLog{
+ Step: cursor.Step,
+ Cursor: 1,
+ Lines: []*ViewStepLogLine{
+ {
+ Index: 1,
+ Message: ctx.Locale.TrString("actions.runs.expire_log_message"),
+ // Timestamp doesn't mean anything when the log is expired.
+ // Set it to the task's updated time since it's probably the time when the log has expired.
+ Timestamp: float64(task.Updated.AsTime().UnixNano()) / float64(time.Second),
},
- Started: int64(step.Started),
- })
- }
- continue
+ },
+ Started: int64(step.Started),
+ })
}
+ continue
+ }
- logLines := make([]*ViewStepLogLine, 0) // marshal to '[]' instead fo 'null' in json
-
- index := step.LogIndex + cursor.Cursor
- validCursor := cursor.Cursor >= 0 &&
- // !(cursor.Cursor < step.LogLength) when the frontend tries to fetch next line before it's ready.
- // So return the same cursor and empty lines to let the frontend retry.
- cursor.Cursor < step.LogLength &&
- // !(index < task.LogIndexes[index]) when task data is older than step data.
- // It can be fixed by making sure write/read tasks and steps in the same transaction,
- // but it's easier to just treat it as fetching the next line before it's ready.
- index < int64(len(task.LogIndexes))
-
- if validCursor {
- length := step.LogLength - cursor.Cursor
- offset := task.LogIndexes[index]
- logRows, err := actions.ReadLogs(ctx, task.LogInStorage, task.LogFilename, offset, length)
- if err != nil {
- ctx.ServerError("actions.ReadLogs", err)
- return
- }
-
- for i, row := range logRows {
- logLines = append(logLines, &ViewStepLogLine{
- Index: cursor.Cursor + int64(i) + 1, // start at 1
- Message: row.Content,
- Timestamp: float64(row.Time.AsTime().UnixNano()) / float64(time.Second),
- })
- }
+ logLines := make([]*ViewStepLogLine, 0) // marshal to '[]' instead fo 'null' in json
+
+ index := step.LogIndex + cursor.Cursor
+ validCursor := cursor.Cursor >= 0 &&
+ // !(cursor.Cursor < step.LogLength) when the frontend tries to fetch next line before it's ready.
+ // So return the same cursor and empty lines to let the frontend retry.
+ cursor.Cursor < step.LogLength &&
+ // !(index < task.LogIndexes[index]) when task data is older than step data.
+ // It can be fixed by making sure write/read tasks and steps in the same transaction,
+ // but it's easier to just treat it as fetching the next line before it's ready.
+ index < int64(len(task.LogIndexes))
+
+ if validCursor {
+ length := step.LogLength - cursor.Cursor
+ offset := task.LogIndexes[index]
+ logRows, err := actions.ReadLogs(ctx, task.LogInStorage, task.LogFilename, offset, length)
+ if err != nil {
+ return nil, nil, fmt.Errorf("actions.ReadLogs: %w", err)
}
- resp.Logs.StepsLog = append(resp.Logs.StepsLog, &ViewStepLog{
- Step: cursor.Step,
- Cursor: cursor.Cursor + int64(len(logLines)),
- Lines: logLines,
- Started: int64(step.Started),
- })
+ for i, row := range logRows {
+ logLines = append(logLines, &ViewStepLogLine{
+ Index: cursor.Cursor + int64(i) + 1, // start at 1
+ Message: row.Content,
+ Timestamp: float64(row.Time.AsTime().UnixNano()) / float64(time.Second),
+ })
+ }
}
+
+ logs = append(logs, &ViewStepLog{
+ Step: cursor.Step,
+ Cursor: cursor.Cursor + int64(len(logLines)),
+ Lines: logLines,
+ Started: int64(step.Started),
+ })
}
- ctx.JSON(http.StatusOK, resp)
+ return viewJobs, logs, nil
}
// Rerun will rerun jobs in the given run
@@ -373,7 +408,7 @@ func Rerun(ctx *context_module.Context) {
run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex)
if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("GetRunByIndex", err)
return
}
@@ -391,7 +426,7 @@ func Rerun(ctx *context_module.Context) {
run.Started = 0
run.Stopped = 0
if err := actions_model.UpdateRun(ctx, run, "started", "stopped", "previous_duration"); err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("UpdateRun", err)
return
}
}
@@ -406,7 +441,7 @@ func Rerun(ctx *context_module.Context) {
// if the job has needs, it should be set to "blocked" status to wait for other jobs
shouldBlock := len(j.Needs) > 0
if err := rerunJob(ctx, j, shouldBlock); err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("RerunJob", err)
return
}
}
@@ -420,7 +455,7 @@ func Rerun(ctx *context_module.Context) {
// jobs other than the specified one should be set to "blocked" status
shouldBlock := j.JobID != job.JobID
if err := rerunJob(ctx, j, shouldBlock); err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("RerunJob", err)
return
}
}
@@ -450,6 +485,9 @@ func rerunJob(ctx *context_module.Context, job *actions_model.ActionRunJob, shou
}
actions_service.CreateCommitStatus(ctx, job)
+ actions_service.NotifyWorkflowRunStatusUpdateWithReload(ctx, job)
+ notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil)
+
return nil
}
@@ -457,49 +495,19 @@ func Logs(ctx *context_module.Context) {
runIndex := getRunIndex(ctx)
jobIndex := ctx.PathParamInt64("job")
- job, _ := getRunJobs(ctx, runIndex, jobIndex)
- if ctx.Written() {
- return
- }
- if job.TaskID == 0 {
- ctx.Error(http.StatusNotFound, "job is not started")
- return
- }
-
- err := job.LoadRun(ctx)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
- return
- }
-
- task, err := actions_model.GetTaskByID(ctx, job.TaskID)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
- return
- }
- if task.LogExpired {
- ctx.Error(http.StatusNotFound, "logs have been cleaned up")
- return
- }
-
- reader, err := actions.OpenLogs(ctx, task.LogInStorage, task.LogFilename)
+ run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex)
if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.NotFoundOrServerError("GetRunByIndex", func(err error) bool {
+ return errors.Is(err, util.ErrNotExist)
+ }, err)
return
}
- defer reader.Close()
- workflowName := job.Run.WorkflowID
- if p := strings.Index(workflowName, "."); p > 0 {
- workflowName = workflowName[0:p]
+ if err = common.DownloadActionsRunJobLogsWithIndex(ctx.Base, ctx.Repo.Repository, run.ID, jobIndex); err != nil {
+ ctx.NotFoundOrServerError("DownloadActionsRunJobLogsWithIndex", func(err error) bool {
+ return errors.Is(err, util.ErrNotExist)
+ }, err)
}
- ctx.ServeContent(reader, &context_module.ServeHeaderOptions{
- Filename: fmt.Sprintf("%v-%v-%v.log", workflowName, job.Name, task.ID),
- ContentLength: &task.LogSize,
- ContentType: "text/plain",
- ContentTypeCharset: "utf-8",
- Disposition: "attachment",
- })
}
func Cancel(ctx *context_module.Context) {
@@ -510,6 +518,8 @@ func Cancel(ctx *context_module.Context) {
return
}
+ var updatedjobs []*actions_model.ActionRunJob
+
if err := db.WithTx(ctx, func(ctx context.Context) error {
for _, job := range jobs {
status := job.Status
@@ -524,7 +534,10 @@ func Cancel(ctx *context_module.Context) {
return err
}
if n == 0 {
- return fmt.Errorf("job has changed, try again")
+ return errors.New("job has changed, try again")
+ }
+ if n > 0 {
+ updatedjobs = append(updatedjobs, job)
}
continue
}
@@ -534,12 +547,21 @@ func Cancel(ctx *context_module.Context) {
}
return nil
}); err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("StopTask", err)
return
}
actions_service.CreateCommitStatus(ctx, jobs...)
+ for _, job := range updatedjobs {
+ _ = job.LoadAttributes(ctx)
+ notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil)
+ }
+ if len(updatedjobs) > 0 {
+ job := updatedjobs[0]
+ actions_service.NotifyWorkflowRunStatusUpdateWithReload(ctx, job)
+ notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run)
+ }
ctx.JSON(http.StatusOK, struct{}{})
}
@@ -553,6 +575,8 @@ func Approve(ctx *context_module.Context) {
run := current.Run
doer := ctx.Doer
+ var updatedjobs []*actions_model.ActionRunJob
+
if err := db.WithTx(ctx, func(ctx context.Context) error {
run.NeedApproval = false
run.ApprovedBy = doer.ID
@@ -562,23 +586,64 @@ func Approve(ctx *context_module.Context) {
for _, job := range jobs {
if len(job.Needs) == 0 && job.Status.IsBlocked() {
job.Status = actions_model.StatusWaiting
- _, err := actions_model.UpdateRunJob(ctx, job, nil, "status")
+ n, err := actions_model.UpdateRunJob(ctx, job, nil, "status")
if err != nil {
return err
}
+ if n > 0 {
+ updatedjobs = append(updatedjobs, job)
+ }
}
}
return nil
}); err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("UpdateRunJob", err)
return
}
actions_service.CreateCommitStatus(ctx, jobs...)
+ if len(updatedjobs) > 0 {
+ job := updatedjobs[0]
+ actions_service.NotifyWorkflowRunStatusUpdateWithReload(ctx, job)
+ notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run)
+ }
+
+ for _, job := range updatedjobs {
+ _ = job.LoadAttributes(ctx)
+ notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil)
+ }
+
ctx.JSON(http.StatusOK, struct{}{})
}
+func Delete(ctx *context_module.Context) {
+ runIndex := getRunIndex(ctx)
+ repoID := ctx.Repo.Repository.ID
+
+ run, err := actions_model.GetRunByIndex(ctx, repoID, runIndex)
+ if err != nil {
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.JSONErrorNotFound()
+ return
+ }
+ ctx.ServerError("GetRunByIndex", err)
+ return
+ }
+
+ if !run.Status.IsDone() {
+ ctx.JSONError(ctx.Tr("actions.runs.not_done"))
+ return
+ }
+
+ if err := actions_service.DeleteRun(ctx, run); err != nil {
+ ctx.ServerError("DeleteRun", err)
+ return
+ }
+
+ ctx.JSONOK()
+}
+
// getRunJobs gets the jobs of runIndex, and returns jobs[jobIndex], jobs.
// Any error will be written to the ctx.
// It never returns a nil job of an empty jobs, if the jobIndex is out of range, it will be treated as 0.
@@ -586,20 +651,20 @@ func getRunJobs(ctx *context_module.Context, runIndex, jobIndex int64) (*actions
run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, err.Error())
+ ctx.NotFound(nil)
return nil, nil
}
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("GetRunByIndex", err)
return nil, nil
}
run.Repo = ctx.Repo.Repository
jobs, err := actions_model.GetRunJobsByRunID(ctx, run.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("GetRunJobsByRunID", err)
return nil, nil
}
if len(jobs) == 0 {
- ctx.Error(http.StatusNotFound)
+ ctx.NotFound(nil)
return nil, nil
}
@@ -614,11 +679,6 @@ func getRunJobs(ctx *context_module.Context, runIndex, jobIndex int64) (*actions
}
func ArtifactsDeleteView(ctx *context_module.Context) {
- if !ctx.Repo.CanWrite(unit.TypeActions) {
- ctx.Error(http.StatusForbidden, "no permission")
- return
- }
-
runIndex := getRunIndex(ctx)
artifactName := ctx.PathParam("artifact_name")
@@ -630,7 +690,7 @@ func ArtifactsDeleteView(ctx *context_module.Context) {
return
}
if err = actions_model.SetArtifactNeedDelete(ctx, run.ID, artifactName); err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("SetArtifactNeedDelete", err)
return
}
ctx.JSON(http.StatusOK, struct{}{})
@@ -643,10 +703,10 @@ func ArtifactsDownloadView(ctx *context_module.Context) {
run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
- ctx.Error(http.StatusNotFound, err.Error())
+ ctx.HTTPError(http.StatusNotFound, err.Error())
return
}
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("GetRunByIndex", err)
return
}
@@ -655,41 +715,30 @@ func ArtifactsDownloadView(ctx *context_module.Context) {
ArtifactName: artifactName,
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("FindArtifacts", err)
return
}
if len(artifacts) == 0 {
- ctx.Error(http.StatusNotFound, "artifact not found")
+ ctx.HTTPError(http.StatusNotFound, "artifact not found")
return
}
// if artifacts status is not uploaded-confirmed, treat it as not found
for _, art := range artifacts {
- if art.Status != int64(actions_model.ArtifactStatusUploadConfirmed) {
- ctx.Error(http.StatusNotFound, "artifact not found")
+ if art.Status != actions_model.ArtifactStatusUploadConfirmed {
+ ctx.HTTPError(http.StatusNotFound, "artifact not found")
return
}
}
ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s.zip; filename*=UTF-8''%s.zip", url.PathEscape(artifactName), artifactName))
- // Artifacts using the v4 backend are stored as a single combined zip file per artifact on the backend
- // The v4 backend enshures ContentEncoding is set to "application/zip", which is not the case for the old backend
- if len(artifacts) == 1 && artifacts[0].ArtifactName+".zip" == artifacts[0].ArtifactPath && artifacts[0].ContentEncoding == "application/zip" {
- art := artifacts[0]
- if setting.Actions.ArtifactStorage.ServeDirect() {
- u, err := storage.ActionsArtifacts.URL(art.StoragePath, art.ArtifactPath, nil)
- if u != nil && err == nil {
- ctx.Redirect(u.String())
- return
- }
- }
- f, err := storage.ActionsArtifacts.Open(art.StoragePath)
+ if len(artifacts) == 1 && actions.IsArtifactV4(artifacts[0]) {
+ err := actions.DownloadArtifactV4(ctx.Base, artifacts[0])
if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("DownloadArtifactV4", err)
return
}
- _, _ = io.Copy(ctx.Resp, f)
return
}
@@ -700,7 +749,7 @@ func ArtifactsDownloadView(ctx *context_module.Context) {
for _, art := range artifacts {
f, err := storage.ActionsArtifacts.Open(art.StoragePath)
if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("ActionsArtifacts.Open", err)
return
}
@@ -708,7 +757,7 @@ func ArtifactsDownloadView(ctx *context_module.Context) {
if art.ContentEncoding == "gzip" {
r, err = gzip.NewReader(f)
if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("gzip.NewReader", err)
return
}
} else {
@@ -718,11 +767,11 @@ func ArtifactsDownloadView(ctx *context_module.Context) {
w, err := writer.Create(art.ArtifactPath)
if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("writer.Create", err)
return
}
if _, err := io.Copy(w, r); err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.ServerError("io.Copy", err)
return
}
}
@@ -783,147 +832,28 @@ func Run(ctx *context_module.Context) {
ctx.ServerError("ref", nil)
return
}
-
- // can not rerun job when workflow is disabled
- cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions)
- cfg := cfgUnit.ActionsConfig()
- if cfg.IsWorkflowDisabled(workflowID) {
- ctx.Flash.Error(ctx.Tr("actions.workflow.disabled"))
- ctx.Redirect(redirectURL)
- return
- }
-
- // get target commit of run from specified ref
- refName := git.RefName(ref)
- var runTargetCommit *git.Commit
- var err error
- if refName.IsTag() {
- runTargetCommit, err = ctx.Repo.GitRepo.GetTagCommit(refName.TagName())
- } else if refName.IsBranch() {
- runTargetCommit, err = ctx.Repo.GitRepo.GetBranchCommit(refName.BranchName())
- } else {
- ctx.Flash.Error(ctx.Tr("form.git_ref_name_error", ref))
- ctx.Redirect(redirectURL)
- return
- }
- if err != nil {
- ctx.Flash.Error(ctx.Tr("form.target_ref_not_exist", ref))
- ctx.Redirect(redirectURL)
- return
- }
-
- // get workflow entry from default branch commit
- defaultBranchCommit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
- return
- }
- entries, err := actions.ListWorkflows(defaultBranchCommit)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
- return
- }
-
- // find workflow from commit
- var workflows []*jobparser.SingleWorkflow
- for _, entry := range entries {
- if entry.Name() == workflowID {
- content, err := actions.GetContentFromEntry(entry)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
- return
- }
- workflows, err = jobparser.Parse(content)
- if err != nil {
- ctx.ServerError("workflow", err)
- return
- }
- break
- }
- }
-
- if len(workflows) == 0 {
- ctx.Flash.Error(ctx.Tr("actions.workflow.not_found", workflowID))
- ctx.Redirect(redirectURL)
- return
- }
-
- // get inputs from post
- workflow := &model.Workflow{
- RawOn: workflows[0].RawOn,
- }
- inputs := make(map[string]any)
- if workflowDispatch := workflow.WorkflowDispatchConfig(); workflowDispatch != nil {
+ err := actions_service.DispatchActionWorkflow(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, workflowID, ref, func(workflowDispatch *model.WorkflowDispatch, inputs map[string]any) error {
for name, config := range workflowDispatch.Inputs {
- value := ctx.Req.PostForm.Get(name)
+ value := ctx.Req.PostFormValue(name)
if config.Type == "boolean" {
- // https://www.w3.org/TR/html401/interact/forms.html
- // https://stackoverflow.com/questions/11424037/do-checkbox-inputs-only-post-data-if-theyre-checked
- // Checkboxes (and radio buttons) are on/off switches that may be toggled by the user.
- // A switch is "on" when the control element's checked attribute is set.
- // When a form is submitted, only "on" checkbox controls can become successful.
- inputs[name] = strconv.FormatBool(value == "on")
+ inputs[name] = strconv.FormatBool(ctx.FormBool(name))
} else if value != "" {
inputs[name] = value
} else {
inputs[name] = config.Default
}
}
- }
-
- // ctx.Req.PostForm -> WorkflowDispatchPayload.Inputs -> ActionRun.EventPayload -> runner: ghc.Event
- // https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
- // https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_dispatch
- workflowDispatchPayload := &api.WorkflowDispatchPayload{
- Workflow: workflowID,
- Ref: ref,
- Repository: convert.ToRepo(ctx, ctx.Repo.Repository, access_model.Permission{AccessMode: perm.AccessModeNone}),
- Inputs: inputs,
- Sender: convert.ToUserWithAccessMode(ctx, ctx.Doer, perm.AccessModeNone),
- }
- var eventPayload []byte
- if eventPayload, err = workflowDispatchPayload.JSONPayload(); err != nil {
- ctx.ServerError("JSONPayload", err)
- return
- }
-
- run := &actions_model.ActionRun{
- Title: strings.SplitN(runTargetCommit.CommitMessage, "\n", 2)[0],
- RepoID: ctx.Repo.Repository.ID,
- OwnerID: ctx.Repo.Repository.OwnerID,
- WorkflowID: workflowID,
- TriggerUserID: ctx.Doer.ID,
- Ref: ref,
- CommitSHA: runTargetCommit.ID.String(),
- IsForkPullRequest: false,
- Event: "workflow_dispatch",
- TriggerEvent: "workflow_dispatch",
- EventPayload: string(eventPayload),
- Status: actions_model.StatusWaiting,
- }
-
- // cancel running jobs of the same workflow
- if err := actions_model.CancelPreviousJobs(
- ctx,
- run.RepoID,
- run.Ref,
- run.WorkflowID,
- run.Event,
- ); err != nil {
- log.Error("CancelRunningJobs: %v", err)
- }
-
- // Insert the action run and its associated jobs into the database
- if err := actions_model.InsertRun(ctx, run, workflows); err != nil {
- ctx.ServerError("workflow", err)
- return
- }
-
- alljobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID})
+ return nil
+ })
if err != nil {
- log.Error("FindRunJobs: %v", err)
+ if errLocale := util.ErrorAsLocale(err); errLocale != nil {
+ ctx.Flash.Error(ctx.Tr(errLocale.TrKey, errLocale.TrArgs...))
+ ctx.Redirect(redirectURL)
+ } else {
+ ctx.ServerError("DispatchActionWorkflow", err)
+ }
+ return
}
- actions_service.CreateCommitStatus(ctx, alljobs...)
ctx.Flash.Success(ctx.Tr("actions.workflow.run_success", workflowID))
ctx.Redirect(redirectURL)
diff --git a/routers/web/repo/activity.go b/routers/web/repo/activity.go
index 1d809ad8e9..8232f0cc04 100644
--- a/routers/web/repo/activity.go
+++ b/routers/web/repo/activity.go
@@ -8,6 +8,7 @@ import (
"time"
activities_model "code.gitea.io/gitea/models/activities"
+ "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/services/context"
@@ -52,12 +53,26 @@ func Activity(ctx *context.Context) {
ctx.Data["DateUntil"] = timeUntil
ctx.Data["PeriodText"] = ctx.Tr("repo.activity.period." + ctx.Data["Period"].(string))
+ canReadCode := ctx.Repo.CanRead(unit.TypeCode)
+ if canReadCode {
+ // GetActivityStats needs to read the default branch to get some information
+ branchExist, _ := git.IsBranchExist(ctx, ctx.Repo.Repository.ID, ctx.Repo.Repository.DefaultBranch)
+ if !branchExist {
+ ctx.Data["NotFoundPrompt"] = ctx.Tr("repo.branch.default_branch_not_exist", ctx.Repo.Repository.DefaultBranch)
+ ctx.NotFound(nil)
+ return
+ }
+ }
+
var err error
- if ctx.Data["Activity"], err = activities_model.GetActivityStats(ctx, ctx.Repo.Repository, timeFrom,
+ // TODO: refactor these arguments to a struct
+ ctx.Data["Activity"], err = activities_model.GetActivityStats(ctx, ctx.Repo.Repository, timeFrom,
ctx.Repo.CanRead(unit.TypeReleases),
ctx.Repo.CanRead(unit.TypeIssues),
ctx.Repo.CanRead(unit.TypePullRequests),
- ctx.Repo.CanRead(unit.TypeCode)); err != nil {
+ canReadCode,
+ )
+ if err != nil {
ctx.ServerError("GetActivityStats", err)
return
}
diff --git a/routers/web/repo/attachment.go b/routers/web/repo/attachment.go
index f8e51521be..9eda926dad 100644
--- a/routers/web/repo/attachment.go
+++ b/routers/web/repo/attachment.go
@@ -34,13 +34,13 @@ func UploadReleaseAttachment(ctx *context.Context) {
// UploadAttachment response for uploading attachments
func uploadAttachment(ctx *context.Context, repoID int64, allowedTypes string) {
if !setting.Attachment.Enabled {
- ctx.Error(http.StatusNotFound, "attachment is not enabled")
+ ctx.HTTPError(http.StatusNotFound, "attachment is not enabled")
return
}
file, header, err := ctx.Req.FormFile("file")
if err != nil {
- ctx.Error(http.StatusInternalServerError, fmt.Sprintf("FormFile: %v", err))
+ ctx.HTTPError(http.StatusInternalServerError, fmt.Sprintf("FormFile: %v", err))
return
}
defer file.Close()
@@ -52,10 +52,10 @@ func uploadAttachment(ctx *context.Context, repoID int64, allowedTypes string) {
})
if err != nil {
if upload.IsErrFileTypeForbidden(err) {
- ctx.Error(http.StatusBadRequest, err.Error())
+ ctx.HTTPError(http.StatusBadRequest, err.Error())
return
}
- ctx.Error(http.StatusInternalServerError, fmt.Sprintf("NewAttachment: %v", err))
+ ctx.HTTPError(http.StatusInternalServerError, fmt.Sprintf("NewAttachment: %v", err))
return
}
@@ -70,16 +70,16 @@ func DeleteAttachment(ctx *context.Context) {
file := ctx.FormString("file")
attach, err := repo_model.GetAttachmentByUUID(ctx, file)
if err != nil {
- ctx.Error(http.StatusBadRequest, err.Error())
+ ctx.HTTPError(http.StatusBadRequest, err.Error())
return
}
if !ctx.IsSigned || (ctx.Doer.ID != attach.UploaderID) {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
err = repo_model.DeleteAttachment(ctx, attach, true)
if err != nil {
- ctx.Error(http.StatusInternalServerError, fmt.Sprintf("DeleteAttachment: %v", err))
+ ctx.HTTPError(http.StatusInternalServerError, fmt.Sprintf("DeleteAttachment: %v", err))
return
}
ctx.JSON(http.StatusOK, map[string]string{
@@ -92,7 +92,7 @@ func ServeAttachment(ctx *context.Context, uuid string) {
attach, err := repo_model.GetAttachmentByUUID(ctx, uuid)
if err != nil {
if repo_model.IsErrAttachmentNotExist(err) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
} else {
ctx.ServerError("GetAttachmentByUUID", err)
}
@@ -107,17 +107,17 @@ func ServeAttachment(ctx *context.Context, uuid string) {
if repository == nil { // If not linked
if !(ctx.IsSigned && attach.UploaderID == ctx.Doer.ID) { // We block if not the uploader
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
} else { // If we have the repository we check access
perm, err := access_model.GetUserRepoPermission(ctx, repository, ctx.Doer)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "GetUserRepoPermission", err.Error())
return
}
if !perm.CanRead(unitType) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
}
diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go
index ad79087513..e304633f95 100644
--- a/routers/web/repo/blame.go
+++ b/routers/web/repo/blame.go
@@ -8,19 +8,21 @@ import (
gotemplate "html/template"
"net/http"
"net/url"
+ "path"
"strconv"
"strings"
+ repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/git/languagestats"
"code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context"
- files_service "code.gitea.io/gitea/services/repository/files"
)
type blameRow struct {
@@ -40,74 +42,58 @@ type blameRow struct {
// RefBlame render blame page
func RefBlame(ctx *context.Context) {
- fileName := ctx.Repo.TreePath
- if len(fileName) == 0 {
- ctx.NotFound("Blame FileName", nil)
- return
- }
-
- branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
- treeLink := branchLink
- rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
-
- if len(ctx.Repo.TreePath) > 0 {
- treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
- }
-
- var treeNames []string
- paths := make([]string, 0, 5)
- if len(ctx.Repo.TreePath) > 0 {
- treeNames = strings.Split(ctx.Repo.TreePath, "/")
- for i := range treeNames {
- paths = append(paths, strings.Join(treeNames[:i+1], "/"))
- }
-
- ctx.Data["HasParentPath"] = true
- if len(paths)-2 >= 0 {
- ctx.Data["ParentPath"] = "/" + paths[len(paths)-1]
- }
- }
+ ctx.Data["PageIsViewCode"] = true
+ ctx.Data["IsBlame"] = true
// Get current entry user currently looking at.
+ if ctx.Repo.TreePath == "" {
+ ctx.NotFound(nil)
+ return
+ }
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
if err != nil {
HandleGitError(ctx, "Repo.Commit.GetTreeEntryByPath", err)
return
}
- blob := entry.Blob()
+ treeNames := strings.Split(ctx.Repo.TreePath, "/")
+ var paths []string
+ for i := range treeNames {
+ paths = append(paths, strings.Join(treeNames[:i+1], "/"))
+ }
ctx.Data["Paths"] = paths
- ctx.Data["TreeLink"] = treeLink
ctx.Data["TreeNames"] = treeNames
- ctx.Data["BranchLink"] = branchLink
-
- ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
- ctx.Data["PageIsViewCode"] = true
-
- ctx.Data["IsBlame"] = true
+ ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
+ ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
+ blob := entry.Blob()
fileSize := blob.Size()
ctx.Data["FileSize"] = fileSize
- ctx.Data["FileName"] = blob.Name()
+ ctx.Data["FileTreePath"] = ctx.Repo.TreePath
+
+ tplName := tplRepoViewContent
+ if !ctx.FormBool("only_content") {
+ prepareHomeTreeSideBarSwitch(ctx)
+ tplName = tplRepoView
+ }
if fileSize >= setting.UI.MaxDisplayFileSize {
ctx.Data["IsFileTooLarge"] = true
- ctx.HTML(http.StatusOK, tplRepoHome)
+ ctx.HTML(http.StatusOK, tplName)
return
}
- ctx.Data["NumLines"], err = blob.GetBlobLineCount()
+ ctx.Data["NumLines"], err = blob.GetBlobLineCount(nil)
if err != nil {
- ctx.NotFound("GetBlobLineCount", err)
+ ctx.NotFound(err)
return
}
bypassBlameIgnore, _ := strconv.ParseBool(ctx.FormString("bypass-blame-ignore"))
-
- result, err := performBlame(ctx, ctx.Repo.Repository.RepoPath(), ctx.Repo.Commit, fileName, bypassBlameIgnore)
+ result, err := performBlame(ctx, ctx.Repo.Repository, ctx.Repo.Commit, ctx.Repo.TreePath, bypassBlameIgnore)
if err != nil {
- ctx.NotFound("CreateBlameReader", err)
+ ctx.NotFound(err)
return
}
@@ -121,7 +107,7 @@ func RefBlame(ctx *context.Context) {
renderBlame(ctx, result.Parts, commitNames)
- ctx.HTML(http.StatusOK, tplRepoHome)
+ ctx.HTML(http.StatusOK, tplName)
}
type blameResult struct {
@@ -130,10 +116,10 @@ type blameResult struct {
FaultyIgnoreRevsFile bool
}
-func performBlame(ctx *context.Context, repoPath string, commit *git.Commit, file string, bypassBlameIgnore bool) (*blameResult, error) {
+func performBlame(ctx *context.Context, repo *repo_model.Repository, commit *git.Commit, file string, bypassBlameIgnore bool) (*blameResult, error) {
objectFormat := ctx.Repo.GetObjectFormat()
- blameReader, err := git.CreateBlameReader(ctx, objectFormat, repoPath, commit, file, bypassBlameIgnore)
+ blameReader, err := git.CreateBlameReader(ctx, objectFormat, repo.RepoPath(), commit, file, bypassBlameIgnore)
if err != nil {
return nil, err
}
@@ -149,7 +135,7 @@ func performBlame(ctx *context.Context, repoPath string, commit *git.Commit, fil
if len(r.Parts) == 0 && r.UsesIgnoreRevs {
// try again without ignored revs
- blameReader, err = git.CreateBlameReader(ctx, objectFormat, repoPath, commit, file, true)
+ blameReader, err = git.CreateBlameReader(ctx, objectFormat, repo.RepoPath(), commit, file, true)
if err != nil {
return nil, err
}
@@ -221,7 +207,7 @@ func processBlameParts(ctx *context.Context, blameParts []*git.BlamePart) map[st
commit, err = ctx.Repo.GitRepo.GetCommit(sha)
if err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound("Repo.GitRepo.GetCommit", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("Repo.GitRepo.GetCommit", err)
}
@@ -234,7 +220,12 @@ func processBlameParts(ctx *context.Context, blameParts []*git.BlamePart) map[st
}
// populate commit email addresses to later look up avatars.
- for _, c := range user_model.ValidateCommitsWithEmails(ctx, commits) {
+ validatedCommits, err := user_model.ValidateCommitsWithEmails(ctx, commits)
+ if err != nil {
+ ctx.ServerError("ValidateCommitsWithEmails", err)
+ return nil
+ }
+ for _, c := range validatedCommits {
commitNames[c.ID.String()] = c
}
@@ -244,7 +235,7 @@ func processBlameParts(ctx *context.Context, blameParts []*git.BlamePart) map[st
func renderBlame(ctx *context.Context, blameParts []*git.BlamePart, commitNames map[string]*user_model.UserCommit) {
repoLink := ctx.Repo.RepoLink
- language, err := files_service.TryGetContentLanguage(ctx.Repo.GitRepo, ctx.Repo.CommitID, ctx.Repo.TreePath)
+ language, err := languagestats.GetFileLanguage(ctx, ctx.Repo.GitRepo, ctx.Repo.CommitID, ctx.Repo.TreePath)
if err != nil {
log.Error("Unable to get file language for %-v:%s. Error: %v", ctx.Repo.Repository, ctx.Repo.TreePath, err)
}
@@ -295,8 +286,7 @@ func renderBlame(ctx *context.Context, blameParts []*git.BlamePart, commitNames
if i != len(lines)-1 {
line += "\n"
}
- fileName := fmt.Sprintf("%v", ctx.Data["FileName"])
- line, lexerNameForLine := highlight.Code(fileName, language, line)
+ line, lexerNameForLine := highlight.Code(path.Base(ctx.Repo.TreePath), language, line)
// set lexer name to the first detected lexer. this is certainly suboptimal and
// we should instead highlight the whole file at once
diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go
index 2bcd7821b4..96d1d87836 100644
--- a/routers/web/repo/branch.go
+++ b/routers/web/repo/branch.go
@@ -37,7 +37,6 @@ const (
// Branches render repository branch page
func Branches(ctx *context.Context) {
ctx.Data["Title"] = "Branches"
- ctx.Data["IsRepoToolbarBranches"] = true
ctx.Data["AllowsPulls"] = ctx.Repo.Repository.AllowsPulls(ctx)
ctx.Data["IsWriter"] = ctx.Repo.CanWrite(unit.TypeCode)
ctx.Data["IsMirror"] = ctx.Repo.Repository.IsMirror
@@ -46,10 +45,7 @@ func Branches(ctx *context.Context) {
ctx.Data["PageIsViewCode"] = true
ctx.Data["PageIsBranches"] = true
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
pageSize := setting.Git.BranchesRangeSize
kw := ctx.FormString("q")
@@ -94,10 +90,10 @@ func Branches(ctx *context.Context) {
// DeleteBranchPost responses for delete merged branch
func DeleteBranchPost(ctx *context.Context) {
- defer redirect(ctx)
+ defer jsonRedirectBranches(ctx)
branchName := ctx.FormString("name")
- if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
+ if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName, nil); err != nil {
switch {
case git.IsErrBranchNotExist(err):
log.Debug("DeleteBranch: Can't delete non existing branch '%s'", branchName)
@@ -121,7 +117,7 @@ func DeleteBranchPost(ctx *context.Context) {
// RestoreBranchPost responses for delete merged branch
func RestoreBranchPost(ctx *context.Context) {
- defer redirect(ctx)
+ defer jsonRedirectBranches(ctx)
branchID := ctx.FormInt64("branch_id")
branchName := ctx.FormString("name")
@@ -171,7 +167,7 @@ func RestoreBranchPost(ctx *context.Context) {
ctx.Flash.Success(ctx.Tr("repo.branch.restore_success", deletedBranch.Name))
}
-func redirect(ctx *context.Context) {
+func jsonRedirectBranches(ctx *context.Context) {
ctx.JSONRedirect(ctx.Repo.RepoLink + "/branches?page=" + url.QueryEscape(ctx.FormString("page")))
}
@@ -179,13 +175,13 @@ func redirect(ctx *context.Context) {
func CreateBranch(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.NewBranchForm)
if !ctx.Repo.CanCreateBranch() {
- ctx.NotFound("CreateBranch", nil)
+ ctx.NotFound(nil)
return
}
if ctx.HasError() {
ctx.Flash.Error(ctx.GetErrMsg())
- ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
+ ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
return
}
@@ -193,11 +189,11 @@ func CreateBranch(ctx *context.Context) {
if form.CreateTag {
target := ctx.Repo.CommitID
- if ctx.Repo.IsViewBranch {
+ if ctx.Repo.RefFullName.IsBranch() {
target = ctx.Repo.BranchName
}
err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, target, form.NewBranchName, "")
- } else if ctx.Repo.IsViewBranch {
+ } else if ctx.Repo.RefFullName.IsBranch() {
err = repo_service.CreateNewBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.BranchName, form.NewBranchName)
} else {
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.CommitID, form.NewBranchName)
@@ -205,25 +201,25 @@ func CreateBranch(ctx *context.Context) {
if err != nil {
if release_service.IsErrProtectedTagName(err) {
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
- ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
+ ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
return
}
if release_service.IsErrTagAlreadyExists(err) {
e := err.(release_service.ErrTagAlreadyExists)
ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName))
- ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
+ ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
return
}
if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", form.NewBranchName))
- ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
+ ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
return
}
if git_model.IsErrBranchNameConflict(err) {
e := err.(git_model.ErrBranchNameConflict)
ctx.Flash.Error(ctx.Tr("repo.branch.branch_name_conflict", form.NewBranchName, e.BranchName))
- ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
+ ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
return
}
if git.IsErrPushRejected(err) {
@@ -242,7 +238,7 @@ func CreateBranch(ctx *context.Context) {
}
ctx.Flash.Error(flashError)
}
- ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
+ ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
return
}
@@ -262,10 +258,10 @@ func CreateBranch(ctx *context.Context) {
func MergeUpstream(ctx *context.Context) {
branchName := ctx.FormString("branch")
- _, err := repo_service.MergeUpstream(ctx, ctx.Doer, ctx.Repo.Repository, branchName)
+ _, err := repo_service.MergeUpstream(ctx, ctx.Doer, ctx.Repo.Repository, branchName, false)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
- ctx.JSONError(ctx.Tr("error.not_found"))
+ ctx.JSONErrorNotFound()
return
} else if pull_service.IsErrMergeConflicts(err) {
ctx.JSONError(ctx.Tr("repo.pulls.merge_conflict"))
diff --git a/routers/web/repo/cherry_pick.go b/routers/web/repo/cherry_pick.go
deleted file mode 100644
index 35f158df52..0000000000
--- a/routers/web/repo/cherry_pick.go
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package repo
-
-import (
- "bytes"
- "errors"
- "strings"
-
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/repository/files"
-)
-
-var tplCherryPick templates.TplName = "repo/editor/cherry_pick"
-
-// CherryPick handles cherrypick GETs
-func CherryPick(ctx *context.Context) {
- ctx.Data["SHA"] = ctx.PathParam("sha")
- cherryPickCommit, err := ctx.Repo.GitRepo.GetCommit(ctx.PathParam("sha"))
- if err != nil {
- if git.IsErrNotExist(err) {
- ctx.NotFound("Missing Commit", err)
- return
- }
- ctx.ServerError("GetCommit", err)
- return
- }
-
- if ctx.FormString("cherry-pick-type") == "revert" {
- ctx.Data["CherryPickType"] = "revert"
- ctx.Data["commit_summary"] = "revert " + ctx.PathParam("sha")
- ctx.Data["commit_message"] = "revert " + cherryPickCommit.Message()
- } else {
- ctx.Data["CherryPickType"] = "cherry-pick"
- splits := strings.SplitN(cherryPickCommit.Message(), "\n", 2)
- ctx.Data["commit_summary"] = splits[0]
- ctx.Data["commit_message"] = splits[1]
- }
-
- canCommit := renderCommitRights(ctx)
- ctx.Data["TreePath"] = ""
-
- if canCommit {
- ctx.Data["commit_choice"] = frmCommitChoiceDirect
- } else {
- ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
- }
- ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx)
- ctx.Data["last_commit"] = ctx.Repo.CommitID
- ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
- ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
-
- ctx.HTML(200, tplCherryPick)
-}
-
-// CherryPickPost handles cherrypick POSTs
-func CherryPickPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.CherryPickForm)
-
- sha := ctx.PathParam("sha")
- ctx.Data["SHA"] = sha
- if form.Revert {
- ctx.Data["CherryPickType"] = "revert"
- } else {
- ctx.Data["CherryPickType"] = "cherry-pick"
- }
-
- canCommit := renderCommitRights(ctx)
- branchName := ctx.Repo.BranchName
- if form.CommitChoice == frmCommitChoiceNewBranch {
- branchName = form.NewBranchName
- }
- ctx.Data["commit_summary"] = form.CommitSummary
- ctx.Data["commit_message"] = form.CommitMessage
- ctx.Data["commit_choice"] = form.CommitChoice
- ctx.Data["new_branch_name"] = form.NewBranchName
- ctx.Data["last_commit"] = ctx.Repo.CommitID
- ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
- ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
-
- if ctx.HasError() {
- ctx.HTML(200, tplCherryPick)
- return
- }
-
- // Cannot commit to a an existing branch if user doesn't have rights
- if branchName == ctx.Repo.BranchName && !canCommit {
- ctx.Data["Err_NewBranchName"] = true
- ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
- ctx.RenderWithErr(ctx.Tr("repo.editor.cannot_commit_to_protected_branch", branchName), tplCherryPick, &form)
- return
- }
-
- message := strings.TrimSpace(form.CommitSummary)
- if message == "" {
- if form.Revert {
- message = ctx.Locale.TrString("repo.commit.revert-header", sha)
- } else {
- message = ctx.Locale.TrString("repo.commit.cherry-pick-header", sha)
- }
- }
-
- form.CommitMessage = strings.TrimSpace(form.CommitMessage)
- if len(form.CommitMessage) > 0 {
- message += "\n\n" + form.CommitMessage
- }
-
- opts := &files.ApplyDiffPatchOptions{
- LastCommitID: form.LastCommit,
- OldBranch: ctx.Repo.BranchName,
- NewBranch: branchName,
- Message: message,
- }
-
- // First lets try the simple plain read-tree -m approach
- opts.Content = sha
- if _, err := files.CherryPick(ctx, ctx.Repo.Repository, ctx.Doer, form.Revert, opts); err != nil {
- if git_model.IsErrBranchAlreadyExists(err) {
- // User has specified a branch that already exists
- branchErr := err.(git_model.ErrBranchAlreadyExists)
- ctx.Data["Err_NewBranchName"] = true
- ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
- return
- } else if files.IsErrCommitIDDoesNotMatch(err) {
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form)
- return
- }
- // Drop through to the apply technique
-
- buf := &bytes.Buffer{}
- if form.Revert {
- if err := git.GetReverseRawDiff(ctx, ctx.Repo.Repository.RepoPath(), sha, buf); err != nil {
- if git.IsErrNotExist(err) {
- ctx.NotFound("GetRawDiff", errors.New("commit "+ctx.PathParam("sha")+" does not exist."))
- return
- }
- ctx.ServerError("GetRawDiff", err)
- return
- }
- } else {
- if err := git.GetRawDiff(ctx.Repo.GitRepo, sha, git.RawDiffType("patch"), buf); err != nil {
- if git.IsErrNotExist(err) {
- ctx.NotFound("GetRawDiff", errors.New("commit "+ctx.PathParam("sha")+" does not exist."))
- return
- }
- ctx.ServerError("GetRawDiff", err)
- return
- }
- }
-
- opts.Content = buf.String()
- ctx.Data["FileContent"] = opts.Content
-
- if _, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, opts); err != nil {
- if git_model.IsErrBranchAlreadyExists(err) {
- // User has specified a branch that already exists
- branchErr := err.(git_model.ErrBranchAlreadyExists)
- ctx.Data["Err_NewBranchName"] = true
- ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
- return
- } else if files.IsErrCommitIDDoesNotMatch(err) {
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form)
- return
- }
- ctx.RenderWithErr(ctx.Tr("repo.editor.fail_to_apply_patch", err), tplPatchFile, &form)
- return
- }
- }
-
- if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(ctx, unit.TypePullRequests) {
- ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName))
- } else {
- ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName))
- }
-}
diff --git a/routers/web/repo/code_frequency.go b/routers/web/repo/code_frequency.go
index 6572adce74..2b2dd5744a 100644
--- a/routers/web/repo/code_frequency.go
+++ b/routers/web/repo/code_frequency.go
@@ -29,12 +29,12 @@ func CodeFrequency(ctx *context.Context) {
// CodeFrequencyData returns JSON of code frequency data
func CodeFrequencyData(ctx *context.Context) {
- if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.CommitID); err != nil {
+ if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch); err != nil {
if errors.Is(err, contributors_service.ErrAwaitGeneration) {
ctx.Status(http.StatusAccepted)
return
}
- ctx.ServerError("GetCodeFrequencyData", err)
+ ctx.ServerError("GetContributorStats", err)
} else {
ctx.JSON(http.StatusOK, contributorStats["total"].Weeks)
}
diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index 3655233312..8f5c6a42e6 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -21,17 +21,20 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
+ "code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitgraph"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/util"
+ asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/context"
+ git_service "code.gitea.io/gitea/services/git"
"code.gitea.io/gitea/services/gitdiff"
repo_service "code.gitea.io/gitea/services/repository"
+ "code.gitea.io/gitea/services/repository/gitgraph"
)
const (
@@ -57,21 +60,14 @@ func RefCommits(ctx *context.Context) {
func Commits(ctx *context.Context) {
ctx.Data["PageIsCommits"] = true
if ctx.Repo.Commit == nil {
- ctx.NotFound("Commit not found", nil)
+ ctx.NotFound(nil)
return
}
ctx.Data["PageIsViewCode"] = true
- commitsCount, err := ctx.Repo.GetCommitsCount()
- if err != nil {
- ctx.ServerError("GetCommitsCount", err)
- return
- }
+ commitsCount := ctx.Repo.CommitsCount
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
pageSize := ctx.FormInt("limit")
if pageSize <= 0 {
@@ -79,12 +75,16 @@ func Commits(ctx *context.Context) {
}
// Both `git log branchName` and `git log commitId` work.
- commits, err := ctx.Repo.Commit.CommitsByRange(page, pageSize, "")
+ commits, err := ctx.Repo.Commit.CommitsByRange(page, pageSize, "", "", "")
if err != nil {
ctx.ServerError("CommitsByRange", err)
return
}
- ctx.Data["Commits"] = processGitCommits(ctx, commits)
+ ctx.Data["Commits"], err = processGitCommits(ctx, commits)
+ if err != nil {
+ ctx.ServerError("processGitCommits", err)
+ return
+ }
commitIDs := make([]string, 0, len(commits))
for _, c := range commits {
commitIDs = append(commitIDs, c.ID.String())
@@ -129,12 +129,6 @@ func Graph(ctx *context.Context) {
ctx.Data["SelectedBranches"] = realBranches
files := ctx.FormStrings("file")
- commitsCount, err := ctx.Repo.GetCommitsCount()
- if err != nil {
- ctx.ServerError("GetCommitsCount", err)
- return
- }
-
graphCommitsCount, err := ctx.Repo.GetCommitGraphsCount(ctx, hidePRRefs, realBranches, files)
if err != nil {
log.Warn("GetCommitGraphsCount error for generate graph exclude prs: %t branches: %s in %-v, Will Ignore branches and try again. Underlying Error: %v", hidePRRefs, branches, ctx.Repo.Repository, err)
@@ -171,12 +165,14 @@ func Graph(ctx *context.Context) {
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
- ctx.Data["CommitCount"] = commitsCount
+ divOnly := ctx.FormBool("div-only")
+ queryParams := ctx.Req.URL.Query()
+ queryParams.Del("div-only")
paginator := context.NewPagination(int(graphCommitsCount), setting.UI.GraphMaxCommitNum, page, 5)
- paginator.AddParamFromRequest(ctx.Req)
+ paginator.AddParamFromQuery(queryParams)
ctx.Data["Page"] = paginator
- if ctx.FormBool("div-only") {
+ if divOnly {
ctx.HTML(http.StatusOK, tplGraphDiv)
return
}
@@ -191,7 +187,7 @@ func SearchCommits(ctx *context.Context) {
query := ctx.FormTrim("q")
if len(query) == 0 {
- ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchNameSubURL())
+ ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.RefTypeNameSubURL())
return
}
@@ -203,7 +199,11 @@ func SearchCommits(ctx *context.Context) {
return
}
ctx.Data["CommitCount"] = len(commits)
- ctx.Data["Commits"] = processGitCommits(ctx, commits)
+ ctx.Data["Commits"], err = processGitCommits(ctx, commits)
+ if err != nil {
+ ctx.ServerError("processGitCommits", err)
+ return
+ }
ctx.Data["Keyword"] = query
if all {
@@ -216,41 +216,41 @@ func SearchCommits(ctx *context.Context) {
// FileHistory show a file's reversions
func FileHistory(ctx *context.Context) {
- fileName := ctx.Repo.TreePath
- if len(fileName) == 0 {
+ if ctx.Repo.TreePath == "" {
Commits(ctx)
return
}
- commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(ctx.Repo.RefName, fileName)
+ commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(ctx.Repo.RefFullName.ShortName(), ctx.Repo.TreePath)
if err != nil {
ctx.ServerError("FileCommitsCount", err)
return
} else if commitsCount == 0 {
- ctx.NotFound("FileCommitsCount", nil)
+ ctx.NotFound(nil)
return
}
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(
git.CommitsByFileAndRangeOptions{
- Revision: ctx.Repo.RefName,
- File: fileName,
+ Revision: ctx.Repo.RefFullName.ShortName(), // FIXME: legacy code used ShortName
+ File: ctx.Repo.TreePath,
Page: page,
})
if err != nil {
ctx.ServerError("CommitsByFileAndRange", err)
return
}
- ctx.Data["Commits"] = processGitCommits(ctx, commits)
+ ctx.Data["Commits"], err = processGitCommits(ctx, commits)
+ if err != nil {
+ ctx.ServerError("processGitCommits", err)
+ return
+ }
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
- ctx.Data["FileName"] = fileName
+ ctx.Data["FileTreePath"] = ctx.Repo.TreePath
ctx.Data["CommitCount"] = commitsCount
pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5)
@@ -281,7 +281,7 @@ func Diff(ctx *context.Context) {
)
if ctx.Data["PageIsWiki"] != nil {
- gitRepo, err = gitrepo.OpenWikiRepository(ctx, ctx.Repo.Repository)
+ gitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository.WikiStorageRepo())
if err != nil {
ctx.ServerError("Repo.GitRepo.GetCommit", err)
return
@@ -294,7 +294,7 @@ func Diff(ctx *context.Context) {
commit, err := gitRepo.GetCommit(commitID)
if err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound("Repo.GitRepo.GetCommit", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("Repo.GitRepo.GetCommit", err)
}
@@ -311,25 +311,30 @@ func Diff(ctx *context.Context) {
maxLines, maxFiles = -1, -1
}
- diff, err := gitdiff.GetDiff(ctx, gitRepo, &gitdiff.DiffOptions{
+ diff, err := gitdiff.GetDiffForRender(ctx, gitRepo, &gitdiff.DiffOptions{
AfterCommitID: commitID,
SkipTo: ctx.FormString("skip-to"),
MaxLines: maxLines,
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
MaxFiles: maxFiles,
WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)),
- FileOnly: fileOnly,
}, files...)
if err != nil {
- ctx.NotFound("GetDiff", err)
+ ctx.NotFound(err)
return
}
+ diffShortStat, err := gitdiff.GetDiffShortStat(gitRepo, "", commitID)
+ if err != nil {
+ ctx.ServerError("GetDiffShortStat", err)
+ return
+ }
+ ctx.Data["DiffShortStat"] = diffShortStat
parents := make([]string, commit.ParentCount())
for i := 0; i < commit.ParentCount(); i++ {
sha, err := commit.ParentID(i)
if err != nil {
- ctx.NotFound("repo.Diff", err)
+ ctx.NotFound(err)
return
}
parents[i] = sha.String()
@@ -341,19 +346,35 @@ func Diff(ctx *context.Context) {
ctx.Data["Reponame"] = repoName
var parentCommit *git.Commit
+ var parentCommitID string
if commit.ParentCount() > 0 {
parentCommit, err = gitRepo.GetCommit(parents[0])
if err != nil {
- ctx.NotFound("GetParentCommit", err)
+ ctx.NotFound(err)
return
}
+ parentCommitID = parentCommit.ID.String()
}
setCompareContext(ctx, parentCommit, commit, userName, repoName)
ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitID)
ctx.Data["Commit"] = commit
ctx.Data["Diff"] = diff
- statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, commitID, db.ListOptionsAll)
+ if !fileOnly {
+ diffTree, err := gitdiff.GetDiffTree(ctx, gitRepo, false, parentCommitID, commitID)
+ if err != nil {
+ ctx.ServerError("GetDiffTree", err)
+ return
+ }
+
+ renderedIconPool := fileicon.NewRenderedIconPool()
+ ctx.PageData["DiffFileTree"] = transformDiffTreeForWeb(renderedIconPool, diffTree, nil)
+ ctx.PageData["FolderIcon"] = fileicon.RenderEntryIconHTML(renderedIconPool, fileicon.EntryInfoFolder())
+ ctx.PageData["FolderOpenIcon"] = fileicon.RenderEntryIconHTML(renderedIconPool, fileicon.EntryInfoFolderOpen())
+ ctx.Data["FileIconPoolHTML"] = renderedIconPool.RenderToHTML()
+ }
+
+ statuses, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, commitID, db.ListOptionsAll)
if err != nil {
log.Error("GetLatestCommitStatus: %v", err)
}
@@ -364,11 +385,11 @@ func Diff(ctx *context.Context) {
ctx.Data["CommitStatus"] = git_model.CalcCommitStatus(statuses)
ctx.Data["CommitStatuses"] = statuses
- verification := asymkey_model.ParseCommitWithSignature(ctx, commit)
+ verification := asymkey_service.ParseCommitWithSignature(ctx, commit)
ctx.Data["Verification"] = verification
ctx.Data["Author"] = user_model.ValidateCommitWithEmail(ctx, commit)
ctx.Data["Parents"] = parents
- ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0
+ ctx.Data["DiffNotAvailable"] = diffShortStat.NumFiles == 0
if err := asymkey_model.CalculateTrustStatus(verification, ctx.Repo.Repository.GetTrustModel(), func(user *user_model.User) (bool, error) {
return repo_model.IsOwnerMemberCollaborator(ctx, ctx.Repo.Repository, user.ID)
@@ -390,12 +411,6 @@ func Diff(ctx *context.Context) {
}
}
- ctx.Data["BranchName"], err = commit.GetBranchName()
- if err != nil {
- ctx.ServerError("commit.GetBranchName", err)
- return
- }
-
ctx.HTML(http.StatusOK, tplCommitPage)
}
@@ -403,7 +418,7 @@ func Diff(ctx *context.Context) {
func RawDiff(ctx *context.Context) {
var gitRepo *git.Repository
if ctx.Data["PageIsWiki"] != nil {
- wikiRepo, err := gitrepo.OpenWikiRepository(ctx, ctx.Repo.Repository)
+ wikiRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository.WikiStorageRepo())
if err != nil {
ctx.ServerError("OpenRepository", err)
return
@@ -424,8 +439,7 @@ func RawDiff(ctx *context.Context) {
ctx.Resp,
); err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound("GetRawDiff",
- errors.New("commit "+ctx.PathParam("sha")+" does not exist."))
+ ctx.NotFound(errors.New("commit " + ctx.PathParam("sha") + " does not exist."))
return
}
ctx.ServerError("GetRawDiff", err)
@@ -433,13 +447,19 @@ func RawDiff(ctx *context.Context) {
}
}
-func processGitCommits(ctx *context.Context, gitCommits []*git.Commit) []*git_model.SignCommitWithStatuses {
- commits := git_model.ConvertFromGitCommit(ctx, gitCommits, ctx.Repo.Repository)
+func processGitCommits(ctx *context.Context, gitCommits []*git.Commit) ([]*git_model.SignCommitWithStatuses, error) {
+ commits, err := git_service.ConvertFromGitCommit(ctx, gitCommits, ctx.Repo.Repository)
+ if err != nil {
+ return nil, err
+ }
if !ctx.Repo.CanRead(unit_model.TypeActions) {
for _, commit := range commits {
+ if commit.Status == nil {
+ continue
+ }
commit.Status.HideActionsURL(ctx)
git_model.CommitStatusesHideActionsURL(ctx, commit.Statuses)
}
}
- return commits
+ return commits, nil
}
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index b3c1eb7cb0..de34a9375c 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -26,6 +26,7 @@ import (
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
csv_module "code.gitea.io/gitea/modules/csv"
+ "code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
@@ -258,7 +259,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
ci.HeadUser, err = user_model.GetUserByName(ctx, headInfos[0])
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.NotFound("GetUserByName", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetUserByName", err)
}
@@ -273,7 +274,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
ci.HeadRepo, err = repo_model.GetRepositoryByOwnerAndName(ctx, headInfosSplit[0], headInfosSplit[1])
if err != nil {
if repo_model.IsErrRepoNotExist(err) {
- ctx.NotFound("GetRepositoryByOwnerAndName", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetRepositoryByOwnerAndName", err)
}
@@ -281,7 +282,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
}
if err := ci.HeadRepo.LoadOwner(ctx); err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.NotFound("GetUserByName", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetUserByName", err)
}
@@ -292,7 +293,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
isSameRepo = ci.HeadRepo.ID == ctx.Repo.Repository.ID
}
} else {
- ctx.NotFound("CompareAndPullRequest", nil)
+ ctx.NotFound(nil)
return nil
}
ctx.Data["HeadUser"] = ci.HeadUser
@@ -301,8 +302,8 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
// Check if base branch is valid.
baseIsCommit := ctx.Repo.GitRepo.IsCommitExist(ci.BaseBranch)
- baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(ci.BaseBranch)
- baseIsTag := ctx.Repo.GitRepo.IsTagExist(ci.BaseBranch)
+ baseIsBranch := gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, ci.BaseBranch)
+ baseIsTag := gitrepo.IsTagExist(ctx, ctx.Repo.Repository, ci.BaseBranch)
if !baseIsCommit && !baseIsBranch && !baseIsTag {
// Check if baseBranch is short sha commit hash
@@ -318,7 +319,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
}
return nil
} else {
- ctx.NotFound("IsRefExist", nil)
+ ctx.NotFound(nil)
return nil
}
}
@@ -401,14 +402,13 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
ci.HeadRepo = ctx.Repo.Repository
ci.HeadGitRepo = ctx.Repo.GitRepo
} else if has {
- ci.HeadGitRepo, err = gitrepo.OpenRepository(ctx, ci.HeadRepo)
+ ci.HeadGitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ci.HeadRepo)
if err != nil {
- ctx.ServerError("OpenRepository", err)
+ ctx.ServerError("RepositoryFromRequestContextOrOpen", err)
return nil
}
- defer ci.HeadGitRepo.Close()
} else {
- ctx.NotFound("ParseCompareInfo", nil)
+ ctx.NotFound(nil)
return nil
}
@@ -430,7 +430,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
baseRepo,
permBase)
}
- ctx.NotFound("ParseCompareInfo", nil)
+ ctx.NotFound(nil)
return nil
}
@@ -449,7 +449,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
ci.HeadRepo,
permHead)
}
- ctx.NotFound("ParseCompareInfo", nil)
+ ctx.NotFound(nil)
return nil
}
ctx.Data["CanWriteToHeadRepo"] = permHead.CanWrite(unit.TypeCode)
@@ -504,8 +504,8 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
// Check if head branch is valid.
headIsCommit := ci.HeadGitRepo.IsCommitExist(ci.HeadBranch)
- headIsBranch := ci.HeadGitRepo.IsBranchExist(ci.HeadBranch)
- headIsTag := ci.HeadGitRepo.IsTagExist(ci.HeadBranch)
+ headIsBranch := gitrepo.IsBranchExist(ctx, ci.HeadRepo, ci.HeadBranch)
+ headIsTag := gitrepo.IsTagExist(ctx, ci.HeadRepo, ci.HeadBranch)
if !headIsCommit && !headIsBranch && !headIsTag {
// Check if headBranch is short sha commit hash
if headCommit, _ := ci.HeadGitRepo.GetCommit(ci.HeadBranch); headCommit != nil {
@@ -513,7 +513,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
ctx.Data["HeadBranch"] = ci.HeadBranch
headIsCommit = true
} else {
- ctx.NotFound("IsRefExist", nil)
+ ctx.NotFound(nil)
return nil
}
}
@@ -533,7 +533,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
baseRepo,
permBase)
}
- ctx.NotFound("ParseCompareInfo", nil)
+ ctx.NotFound(nil)
return nil
}
@@ -569,20 +569,20 @@ func PrepareCompareDiff(
ctx *context.Context,
ci *common.CompareInfo,
whitespaceBehavior git.TrustedCmdArgs,
-) bool {
- var (
- repo = ctx.Repo.Repository
- err error
- title string
- )
-
- // Get diff information.
- ctx.Data["CommitRepoLink"] = ci.HeadRepo.Link()
-
+) (nothingToCompare bool) {
+ repo := ctx.Repo.Repository
headCommitID := ci.CompareInfo.HeadCommitID
+ ctx.Data["CommitRepoLink"] = ci.HeadRepo.Link()
ctx.Data["AfterCommitID"] = headCommitID
+ // follow GitHub's behavior: autofill the form and expand
+ newPrFormTitle := ctx.FormTrim("title")
+ newPrFormBody := ctx.FormTrim("body")
+ ctx.Data["ExpandNewPrForm"] = ctx.FormBool("expand") || ctx.FormBool("quick_pull") || newPrFormTitle != "" || newPrFormBody != ""
+ ctx.Data["TitleQuery"] = newPrFormTitle
+ ctx.Data["BodyQuery"] = newPrFormBody
+
if (headCommitID == ci.CompareInfo.MergeBase && !ci.DirectComparison) ||
headCommitID == ci.CompareInfo.BaseCommitID {
ctx.Data["IsNothingToCompare"] = true
@@ -614,7 +614,7 @@ func PrepareCompareDiff(
fileOnly := ctx.FormBool("file-only")
- diff, err := gitdiff.GetDiff(ctx, ci.HeadGitRepo,
+ diff, err := gitdiff.GetDiffForRender(ctx, ci.HeadGitRepo,
&gitdiff.DiffOptions{
BeforeCommitID: beforeCommitID,
AfterCommitID: headCommitID,
@@ -624,14 +624,33 @@ func PrepareCompareDiff(
MaxFiles: maxFiles,
WhitespaceBehavior: whitespaceBehavior,
DirectComparison: ci.DirectComparison,
- FileOnly: fileOnly,
}, ctx.FormStrings("files")...)
if err != nil {
- ctx.ServerError("GetDiffRangeWithWhitespaceBehavior", err)
+ ctx.ServerError("GetDiff", err)
+ return false
+ }
+ diffShortStat, err := gitdiff.GetDiffShortStat(ci.HeadGitRepo, beforeCommitID, headCommitID)
+ if err != nil {
+ ctx.ServerError("GetDiffShortStat", err)
return false
}
+ ctx.Data["DiffShortStat"] = diffShortStat
ctx.Data["Diff"] = diff
- ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0
+ ctx.Data["DiffNotAvailable"] = diffShortStat.NumFiles == 0
+
+ if !fileOnly {
+ diffTree, err := gitdiff.GetDiffTree(ctx, ci.HeadGitRepo, false, beforeCommitID, headCommitID)
+ if err != nil {
+ ctx.ServerError("GetDiffTree", err)
+ return false
+ }
+
+ renderedIconPool := fileicon.NewRenderedIconPool()
+ ctx.PageData["DiffFileTree"] = transformDiffTreeForWeb(renderedIconPool, diffTree, nil)
+ ctx.PageData["FolderIcon"] = fileicon.RenderEntryIconHTML(renderedIconPool, fileicon.EntryInfoFolder())
+ ctx.PageData["FolderOpenIcon"] = fileicon.RenderEntryIconHTML(renderedIconPool, fileicon.EntryInfoFolderOpen())
+ ctx.Data["FileIconPoolHTML"] = renderedIconPool.RenderToHTML()
+ }
headCommit, err := ci.HeadGitRepo.GetCommit(headCommitID)
if err != nil {
@@ -647,10 +666,15 @@ func PrepareCompareDiff(
return false
}
- commits := processGitCommits(ctx, ci.CompareInfo.Commits)
+ commits, err := processGitCommits(ctx, ci.CompareInfo.Commits)
+ if err != nil {
+ ctx.ServerError("processGitCommits", err)
+ return false
+ }
ctx.Data["Commits"] = commits
ctx.Data["CommitCount"] = len(commits)
+ title := ci.HeadBranch
if len(commits) == 1 {
c := commits[0]
title = strings.TrimSpace(c.UserCommit.Summary())
@@ -659,9 +683,8 @@ func PrepareCompareDiff(
if len(body) > 1 {
ctx.Data["content"] = strings.Join(body[1:], "\n")
}
- } else {
- title = ci.HeadBranch
}
+
if len(title) > 255 {
var trailer string
title, trailer = util.EllipsisDisplayStringX(title, 255)
@@ -708,11 +731,6 @@ func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repositor
// CompareDiff show different from one commit to another commit
func CompareDiff(ctx *context.Context) {
ci := ParseCompareInfo(ctx)
- defer func() {
- if ci != nil && ci.HeadGitRepo != nil {
- ci.HeadGitRepo.Close()
- }
- }()
if ctx.Written() {
return
}
@@ -726,8 +744,7 @@ func CompareDiff(ctx *context.Context) {
ctx.Data["OtherCompareSeparator"] = "..."
}
- nothingToCompare := PrepareCompareDiff(ctx, ci,
- gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)))
+ nothingToCompare := PrepareCompareDiff(ctx, ci, gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)))
if ctx.Written() {
return
}
@@ -866,7 +883,7 @@ func ExcerptBlob(ctx *context.Context) {
gitRepo := ctx.Repo.GitRepo
if ctx.Data["PageIsWiki"] == true {
var err error
- gitRepo, err = gitrepo.OpenWikiRepository(ctx, ctx.Repo.Repository)
+ gitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository.WikiStorageRepo())
if err != nil {
ctx.ServerError("OpenRepository", err)
return
@@ -876,12 +893,11 @@ func ExcerptBlob(ctx *context.Context) {
chunkSize := gitdiff.BlobExcerptChunkSize
commit, err := gitRepo.GetCommit(commitID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetCommit")
+ ctx.HTTPError(http.StatusInternalServerError, "GetCommit")
return
}
section := &gitdiff.DiffSection{
FileName: filePath,
- Name: filePath,
}
if direction == "up" && (idxLeft-lastLeft) > chunkSize {
idxLeft -= chunkSize
@@ -905,7 +921,7 @@ func ExcerptBlob(ctx *context.Context) {
idxRight = lastRight
}
if err != nil {
- ctx.Error(http.StatusInternalServerError, "getExcerptLines")
+ ctx.HTTPError(http.StatusInternalServerError, "getExcerptLines")
return
}
if idxRight > lastRight {
@@ -927,9 +943,10 @@ func ExcerptBlob(ctx *context.Context) {
RightHunkSize: rightHunkSize,
},
}
- if direction == "up" {
+ switch direction {
+ case "up":
section.Lines = append([]*gitdiff.DiffLine{lineSection}, section.Lines...)
- } else if direction == "down" {
+ case "down":
section.Lines = append(section.Lines, lineSection)
}
}
diff --git a/routers/web/repo/contributors.go b/routers/web/repo/contributors.go
index e9c0919955..93dec1f350 100644
--- a/routers/web/repo/contributors.go
+++ b/routers/web/repo/contributors.go
@@ -26,7 +26,7 @@ func Contributors(ctx *context.Context) {
// ContributorsData renders JSON of contributors along with their weekly commit statistics
func ContributorsData(ctx *context.Context) {
- if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.CommitID); err != nil {
+ if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch); err != nil {
if errors.Is(err, contributors_service.ErrAwaitGeneration) {
ctx.Status(http.StatusAccepted)
return
diff --git a/routers/web/repo/download.go b/routers/web/repo/download.go
index cb1163c70b..020cebf196 100644
--- a/routers/web/repo/download.go
+++ b/routers/web/repo/download.go
@@ -46,7 +46,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified *time.Tim
log.Error("ServeBlobOrLFS: Close: %v", err)
}
closed = true
- return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
+ return common.ServeBlob(ctx.Base, ctx.Repo.Repository, ctx.Repo.TreePath, blob, lastModified)
}
if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+pointer.Oid+`"`) {
return nil
@@ -78,14 +78,14 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified *time.Tim
}
closed = true
- return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
+ return common.ServeBlob(ctx.Base, ctx.Repo.Repository, ctx.Repo.TreePath, blob, lastModified)
}
func getBlobForEntry(ctx *context.Context) (*git.Blob, *time.Time) {
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
if err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound("GetTreeEntryByPath", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("GetTreeEntryByPath", err)
}
@@ -93,7 +93,7 @@ func getBlobForEntry(ctx *context.Context) (*git.Blob, *time.Time) {
}
if entry.IsDir() || entry.IsSubModule() {
- ctx.NotFound("getBlobForEntry", nil)
+ ctx.NotFound(nil)
return nil, nil
}
@@ -114,7 +114,7 @@ func SingleDownload(ctx *context.Context) {
return
}
- if err := common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified); err != nil {
+ if err := common.ServeBlob(ctx.Base, ctx.Repo.Repository, ctx.Repo.TreePath, blob, lastModified); err != nil {
ctx.ServerError("ServeBlob", err)
}
}
@@ -136,13 +136,13 @@ func DownloadByID(ctx *context.Context) {
blob, err := ctx.Repo.GitRepo.GetBlob(ctx.PathParam("sha"))
if err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound("GetBlob", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetBlob", err)
}
return
}
- if err = common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, nil); err != nil {
+ if err = common.ServeBlob(ctx.Base, ctx.Repo.Repository, ctx.Repo.TreePath, blob, nil); err != nil {
ctx.ServerError("ServeBlob", err)
}
}
@@ -152,7 +152,7 @@ func DownloadByIDOrLFS(ctx *context.Context) {
blob, err := ctx.Repo.GitRepo.GetBlob(ctx.PathParam("sha"))
if err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound("GetBlob", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetBlob", err)
}
diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go
index 5fbdeee27e..2a5ac10282 100644
--- a/routers/web/repo/editor.go
+++ b/routers/web/repo/editor.go
@@ -4,6 +4,7 @@
package repo
import (
+ "bytes"
"fmt"
"io"
"net/http"
@@ -11,19 +12,17 @@ import (
"strings"
git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/typesniffer"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/utils"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/context/upload"
"code.gitea.io/gitea/services/forms"
@@ -35,848 +34,422 @@ const (
tplEditDiffPreview templates.TplName = "repo/editor/diff_preview"
tplDeleteFile templates.TplName = "repo/editor/delete"
tplUploadFile templates.TplName = "repo/editor/upload"
+ tplPatchFile templates.TplName = "repo/editor/patch"
+ tplCherryPick templates.TplName = "repo/editor/cherry_pick"
- frmCommitChoiceDirect string = "direct"
- frmCommitChoiceNewBranch string = "commit-to-new-branch"
+ editorCommitChoiceDirect string = "direct"
+ editorCommitChoiceNewBranch string = "commit-to-new-branch"
)
-func canCreateBasePullRequest(ctx *context.Context) bool {
- baseRepo := ctx.Repo.Repository.BaseRepo
- return baseRepo != nil && baseRepo.UnitEnabled(ctx, unit.TypePullRequests)
-}
-
-func renderCommitRights(ctx *context.Context) bool {
- canCommitToBranch, err := ctx.Repo.CanCommitToBranch(ctx, ctx.Doer)
- if err != nil {
- log.Error("CanCommitToBranch: %v", err)
- }
- ctx.Data["CanCommitToBranch"] = canCommitToBranch
- ctx.Data["CanCreatePullRequest"] = ctx.Repo.Repository.UnitEnabled(ctx, unit.TypePullRequests) || canCreateBasePullRequest(ctx)
-
- return canCommitToBranch.CanCommitToBranch
-}
-
-// redirectForCommitChoice redirects after committing the edit to a branch
-func redirectForCommitChoice(ctx *context.Context, commitChoice, newBranchName, treePath string) {
- if commitChoice == frmCommitChoiceNewBranch {
- // Redirect to a pull request when possible
- redirectToPullRequest := false
- repo := ctx.Repo.Repository
- baseBranch := ctx.Repo.BranchName
- headBranch := newBranchName
- if repo.UnitEnabled(ctx, unit.TypePullRequests) {
- redirectToPullRequest = true
- } else if canCreateBasePullRequest(ctx) {
- redirectToPullRequest = true
- baseBranch = repo.BaseRepo.DefaultBranch
- headBranch = repo.Owner.Name + "/" + repo.Name + ":" + headBranch
- repo = repo.BaseRepo
- }
-
- if redirectToPullRequest {
- ctx.Redirect(repo.Link() + "/compare/" + util.PathEscapeSegments(baseBranch) + "..." + util.PathEscapeSegments(headBranch))
- return
+func prepareEditorCommitFormOptions(ctx *context.Context, editorAction string) *context.CommitFormOptions {
+ cleanedTreePath := files_service.CleanGitTreePath(ctx.Repo.TreePath)
+ if cleanedTreePath != ctx.Repo.TreePath {
+ redirectTo := fmt.Sprintf("%s/%s/%s/%s", ctx.Repo.RepoLink, editorAction, util.PathEscapeSegments(ctx.Repo.BranchName), util.PathEscapeSegments(cleanedTreePath))
+ if ctx.Req.URL.RawQuery != "" {
+ redirectTo += "?" + ctx.Req.URL.RawQuery
}
+ ctx.Redirect(redirectTo)
+ return nil
}
- returnURI := ctx.FormString("return_uri")
-
- ctx.RedirectToCurrentSite(
- returnURI,
- ctx.Repo.RepoLink+"/src/branch/"+util.PathEscapeSegments(newBranchName)+"/"+util.PathEscapeSegments(treePath),
- )
-}
-
-// getParentTreeFields returns list of parent tree names and corresponding tree paths
-// based on given tree path.
-func getParentTreeFields(treePath string) (treeNames, treePaths []string) {
- if len(treePath) == 0 {
- return treeNames, treePaths
+ commitFormOptions, err := context.PrepareCommitFormOptions(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.Permission, ctx.Repo.RefFullName)
+ if err != nil {
+ ctx.ServerError("PrepareCommitFormOptions", err)
+ return nil
}
- treeNames = strings.Split(treePath, "/")
- treePaths = make([]string, len(treeNames))
- for i := range treeNames {
- treePaths[i] = strings.Join(treeNames[:i+1], "/")
+ if commitFormOptions.NeedFork {
+ ForkToEdit(ctx)
+ return nil
}
- return treeNames, treePaths
-}
-
-func editFile(ctx *context.Context, isNewFile bool) {
- ctx.Data["PageIsViewCode"] = true
- ctx.Data["PageIsEdit"] = true
- ctx.Data["IsNewFile"] = isNewFile
- canCommit := renderCommitRights(ctx)
- treePath := cleanUploadFileName(ctx.Repo.TreePath)
- if treePath != ctx.Repo.TreePath {
- if isNewFile {
- ctx.Redirect(path.Join(ctx.Repo.RepoLink, "_new", util.PathEscapeSegments(ctx.Repo.BranchName), util.PathEscapeSegments(treePath)))
- } else {
- ctx.Redirect(path.Join(ctx.Repo.RepoLink, "_edit", util.PathEscapeSegments(ctx.Repo.BranchName), util.PathEscapeSegments(treePath)))
- }
- return
+ if commitFormOptions.WillSubmitToFork && !commitFormOptions.TargetRepo.CanEnableEditor() {
+ ctx.Data["NotFoundPrompt"] = ctx.Locale.Tr("repo.editor.fork_not_editable")
+ ctx.NotFound(nil)
}
- // Check if the filename (and additional path) is specified in the querystring
- // (filename is a misnomer, but kept for compatibility with GitHub)
- filePath, fileName := path.Split(ctx.Req.URL.Query().Get("filename"))
- filePath = strings.Trim(filePath, "/")
- treeNames, treePaths := getParentTreeFields(path.Join(ctx.Repo.TreePath, filePath))
-
- if !isNewFile {
- entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
- if err != nil {
- HandleGitError(ctx, "Repo.Commit.GetTreeEntryByPath", err)
- return
- }
-
- // No way to edit a directory online.
- if entry.IsDir() {
- ctx.NotFound("entry.IsDir", nil)
- return
- }
-
- blob := entry.Blob()
- if blob.Size() >= setting.UI.MaxDisplayFileSize {
- ctx.NotFound("blob.Size", err)
- return
- }
-
- dataRc, err := blob.DataAsync()
- if err != nil {
- ctx.NotFound("blob.Data", err)
- return
- }
-
- defer dataRc.Close()
-
- ctx.Data["FileSize"] = blob.Size()
- ctx.Data["FileName"] = blob.Name()
-
- buf := make([]byte, 1024)
- n, _ := util.ReadAtMost(dataRc, buf)
- buf = buf[:n]
-
- // Only some file types are editable online as text.
- if !typesniffer.DetectContentType(buf).IsRepresentableAsText() {
- ctx.NotFound("typesniffer.IsRepresentableAsText", nil)
- return
- }
-
- d, _ := io.ReadAll(dataRc)
-
- buf = append(buf, d...)
- if content, err := charset.ToUTF8(buf, charset.ConvertOpts{KeepBOM: true}); err != nil {
- log.Error("ToUTF8: %v", err)
- ctx.Data["FileContent"] = string(buf)
- } else {
- ctx.Data["FileContent"] = content
- }
- } else {
- // Append filename from query, or empty string to allow user name the new file.
- treeNames = append(treeNames, fileName)
- }
+ ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
+ ctx.Data["TreePath"] = ctx.Repo.TreePath
+ ctx.Data["CommitFormOptions"] = commitFormOptions
- ctx.Data["TreeNames"] = treeNames
- ctx.Data["TreePaths"] = treePaths
- ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
- ctx.Data["commit_summary"] = ""
- ctx.Data["commit_message"] = ""
- if canCommit {
- ctx.Data["commit_choice"] = frmCommitChoiceDirect
- } else {
- ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
- }
- ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx)
- ctx.Data["last_commit"] = ctx.Repo.CommitID
+ // for online editor
ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",")
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
- ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, treePath)
-
ctx.Data["IsEditingFileOnly"] = ctx.FormString("return_uri") != ""
ctx.Data["ReturnURI"] = ctx.FormString("return_uri")
- ctx.HTML(http.StatusOK, tplEditFile)
+ // form fields
+ ctx.Data["commit_summary"] = ""
+ ctx.Data["commit_message"] = ""
+ ctx.Data["commit_choice"] = util.Iif(commitFormOptions.CanCommitToBranch, editorCommitChoiceDirect, editorCommitChoiceNewBranch)
+ ctx.Data["new_branch_name"] = getUniquePatchBranchName(ctx, ctx.Doer.LowerName, commitFormOptions.TargetRepo)
+ ctx.Data["last_commit"] = ctx.Repo.CommitID
+ return commitFormOptions
}
-// GetEditorConfig returns a editorconfig JSON string for given treePath or "null"
-func GetEditorConfig(ctx *context.Context, treePath string) string {
- ec, _, err := ctx.Repo.GetEditorconfig()
- if err == nil {
- def, err := ec.GetDefinitionForFilename(treePath)
- if err == nil {
- jsonStr, _ := json.Marshal(def)
- return string(jsonStr)
- }
- }
- return "null"
+func prepareTreePathFieldsAndPaths(ctx *context.Context, treePath string) {
+ // show the tree path fields in the "breadcrumb" and help users to edit the target tree path
+ ctx.Data["TreeNames"], ctx.Data["TreePaths"] = getParentTreeFields(strings.TrimPrefix(treePath, "/"))
}
-// EditFile render edit file page
-func EditFile(ctx *context.Context) {
- editFile(ctx, false)
+type preparedEditorCommitForm[T any] struct {
+ form T
+ commonForm *forms.CommitCommonForm
+ CommitFormOptions *context.CommitFormOptions
+ OldBranchName string
+ NewBranchName string
+ GitCommitter *files_service.IdentityOptions
}
-// NewFile render create file page
-func NewFile(ctx *context.Context) {
- editFile(ctx, true)
+func (f *preparedEditorCommitForm[T]) GetCommitMessage(defaultCommitMessage string) string {
+ commitMessage := util.IfZero(strings.TrimSpace(f.commonForm.CommitSummary), defaultCommitMessage)
+ if body := strings.TrimSpace(f.commonForm.CommitMessage); body != "" {
+ commitMessage += "\n\n" + body
+ }
+ return commitMessage
}
-func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile bool) {
- canCommit := renderCommitRights(ctx)
- treeNames, treePaths := getParentTreeFields(form.TreePath)
- branchName := ctx.Repo.BranchName
- if form.CommitChoice == frmCommitChoiceNewBranch {
- branchName = form.NewBranchName
+func prepareEditorCommitSubmittedForm[T forms.CommitCommonFormInterface](ctx *context.Context) *preparedEditorCommitForm[T] {
+ form := web.GetForm(ctx).(T)
+ if ctx.HasError() {
+ ctx.JSONError(ctx.GetErrMsg())
+ return nil
}
- ctx.Data["PageIsEdit"] = true
- ctx.Data["PageHasPosted"] = true
- ctx.Data["IsNewFile"] = isNewFile
- ctx.Data["TreePath"] = form.TreePath
- ctx.Data["TreeNames"] = treeNames
- ctx.Data["TreePaths"] = treePaths
- ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName)
- ctx.Data["FileContent"] = form.Content
- ctx.Data["commit_summary"] = form.CommitSummary
- ctx.Data["commit_message"] = form.CommitMessage
- ctx.Data["commit_choice"] = form.CommitChoice
- ctx.Data["new_branch_name"] = form.NewBranchName
- ctx.Data["last_commit"] = ctx.Repo.CommitID
- ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",")
- ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
- ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, form.TreePath)
+ commonForm := form.GetCommitCommonForm()
+ commonForm.TreePath = files_service.CleanGitTreePath(commonForm.TreePath)
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, tplEditFile)
- return
+ commitFormOptions, err := context.PrepareCommitFormOptions(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.Permission, ctx.Repo.RefFullName)
+ if err != nil {
+ ctx.ServerError("PrepareCommitFormOptions", err)
+ return nil
}
-
- // Cannot commit to a an existing branch if user doesn't have rights
- if branchName == ctx.Repo.BranchName && !canCommit {
- ctx.Data["Err_NewBranchName"] = true
- ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
- ctx.RenderWithErr(ctx.Tr("repo.editor.cannot_commit_to_protected_branch", branchName), tplEditFile, &form)
- return
+ if commitFormOptions.NeedFork {
+ // It shouldn't happen, because we should have done the checks in the "GET" request. But just in case.
+ ctx.JSONError(ctx.Locale.TrString("error.not_found"))
+ return nil
}
- // CommitSummary is optional in the web form, if empty, give it a default message based on add or update
- // `message` will be both the summary and message combined
- message := strings.TrimSpace(form.CommitSummary)
- if len(message) == 0 {
- if isNewFile {
- message = ctx.Locale.TrString("repo.editor.add", form.TreePath)
- } else {
- message = ctx.Locale.TrString("repo.editor.update", form.TreePath)
- }
+ // check commit behavior
+ fromBaseBranch := ctx.FormString("from_base_branch")
+ commitToNewBranch := commonForm.CommitChoice == editorCommitChoiceNewBranch || fromBaseBranch != ""
+ targetBranchName := util.Iif(commitToNewBranch, commonForm.NewBranchName, ctx.Repo.BranchName)
+ if targetBranchName == ctx.Repo.BranchName && !commitFormOptions.CanCommitToBranch {
+ ctx.JSONError(ctx.Tr("repo.editor.cannot_commit_to_protected_branch", targetBranchName))
+ return nil
}
- form.CommitMessage = strings.TrimSpace(form.CommitMessage)
- if len(form.CommitMessage) > 0 {
- message += "\n\n" + form.CommitMessage
+
+ if !issues.CanMaintainerWriteToBranch(ctx, ctx.Repo.Permission, targetBranchName, ctx.Doer) {
+ ctx.NotFound(nil)
+ return nil
}
- operation := "update"
- if isNewFile {
- operation = "create"
+ // Committer user info
+ gitCommitter, valid := WebGitOperationGetCommitChosenEmailIdentity(ctx, commonForm.CommitEmail)
+ if !valid {
+ ctx.JSONError(ctx.Tr("repo.editor.invalid_commit_email"))
+ return nil
}
- if _, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.ChangeRepoFilesOptions{
- LastCommitID: form.LastCommit,
- OldBranch: ctx.Repo.BranchName,
- NewBranch: branchName,
- Message: message,
- Files: []*files_service.ChangeRepoFile{
- {
- Operation: operation,
- FromTreePath: ctx.Repo.TreePath,
- TreePath: form.TreePath,
- ContentReader: strings.NewReader(strings.ReplaceAll(form.Content, "\r", "")),
- },
- },
- Signoff: form.Signoff,
- }); err != nil {
- // This is where we handle all the errors thrown by files_service.ChangeRepoFiles
- if git.IsErrNotExist(err) {
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_editing_no_longer_exists", ctx.Repo.TreePath), tplEditFile, &form)
- } else if git_model.IsErrLFSFileLocked(err) {
- ctx.Data["Err_TreePath"] = true
- ctx.RenderWithErr(ctx.Tr("repo.editor.upload_file_is_locked", err.(git_model.ErrLFSFileLocked).Path, err.(git_model.ErrLFSFileLocked).UserName), tplEditFile, &form)
- } else if files_service.IsErrFilenameInvalid(err) {
- ctx.Data["Err_TreePath"] = true
- ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", form.TreePath), tplEditFile, &form)
- } else if files_service.IsErrFilePathInvalid(err) {
- ctx.Data["Err_TreePath"] = true
- if fileErr, ok := err.(files_service.ErrFilePathInvalid); ok {
- switch fileErr.Type {
- case git.EntryModeSymlink:
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path), tplEditFile, &form)
- case git.EntryModeTree:
- ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_a_directory", fileErr.Path), tplEditFile, &form)
- case git.EntryModeBlob:
- ctx.RenderWithErr(ctx.Tr("repo.editor.directory_is_a_file", fileErr.Path), tplEditFile, &form)
- default:
- ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", fileErr.Path), tplEditFile, &form)
- }
- } else {
- ctx.Error(http.StatusInternalServerError, err.Error())
- }
- } else if files_service.IsErrRepoFileAlreadyExists(err) {
- ctx.Data["Err_TreePath"] = true
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_already_exists", form.TreePath), tplEditFile, &form)
- } else if git.IsErrBranchNotExist(err) {
- // For when a user adds/updates a file to a branch that no longer exists
- if branchErr, ok := err.(git.ErrBranchNotExist); ok {
- ctx.RenderWithErr(ctx.Tr("repo.editor.branch_does_not_exist", branchErr.Name), tplEditFile, &form)
- } else {
- ctx.Error(http.StatusInternalServerError, err.Error())
- }
- } else if git_model.IsErrBranchAlreadyExists(err) {
- // For when a user specifies a new branch that already exists
- ctx.Data["Err_NewBranchName"] = true
- if branchErr, ok := err.(git_model.ErrBranchAlreadyExists); ok {
- ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplEditFile, &form)
- } else {
- ctx.Error(http.StatusInternalServerError, err.Error())
- }
- } else if files_service.IsErrCommitIDDoesNotMatch(err) {
- ctx.RenderWithErr(ctx.Tr("repo.editor.commit_id_not_matching"), tplEditFile, &form)
- } else if git.IsErrPushOutOfDate(err) {
- ctx.RenderWithErr(ctx.Tr("repo.editor.push_out_of_date"), tplEditFile, &form)
- } else if git.IsErrPushRejected(err) {
- errPushRej := err.(*git.ErrPushRejected)
- if len(errPushRej.Message) == 0 {
- ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected_no_message"), tplEditFile, &form)
+ if commitToNewBranch {
+ // if target branch exists, we should stop
+ targetBranchExists, err := git_model.IsBranchExist(ctx, commitFormOptions.TargetRepo.ID, targetBranchName)
+ if err != nil {
+ ctx.ServerError("IsBranchExist", err)
+ return nil
+ } else if targetBranchExists {
+ if fromBaseBranch != "" {
+ ctx.JSONError(ctx.Tr("repo.editor.fork_branch_exists", targetBranchName))
} else {
- flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
- "Message": ctx.Tr("repo.editor.push_rejected"),
- "Summary": ctx.Tr("repo.editor.push_rejected_summary"),
- "Details": utils.SanitizeFlashErrorString(errPushRej.Message),
- })
- if err != nil {
- ctx.ServerError("editFilePost.HTMLString", err)
- return
- }
- ctx.RenderWithErr(flashError, tplEditFile, &form)
+ ctx.JSONError(ctx.Tr("repo.editor.branch_already_exists", targetBranchName))
}
- } else {
- flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
- "Message": ctx.Tr("repo.editor.fail_to_update_file", form.TreePath),
- "Summary": ctx.Tr("repo.editor.fail_to_update_file_summary"),
- "Details": utils.SanitizeFlashErrorString(err.Error()),
- })
- if err != nil {
- ctx.ServerError("editFilePost.HTMLString", err)
- return
- }
- ctx.RenderWithErr(flashError, tplEditFile, &form)
+ return nil
}
}
- if ctx.Repo.Repository.IsEmpty {
- if isEmpty, err := ctx.Repo.GitRepo.IsEmpty(); err == nil && !isEmpty {
- _ = repo_model.UpdateRepositoryCols(ctx, &repo_model.Repository{ID: ctx.Repo.Repository.ID, IsEmpty: false}, "is_empty")
+ oldBranchName := ctx.Repo.BranchName
+ if fromBaseBranch != "" {
+ err = editorPushBranchToForkedRepository(ctx, ctx.Doer, ctx.Repo.Repository.BaseRepo, fromBaseBranch, commitFormOptions.TargetRepo, targetBranchName)
+ if err != nil {
+ log.Error("Unable to editorPushBranchToForkedRepository: %v", err)
+ ctx.JSONError(ctx.Tr("repo.editor.fork_failed_to_push_branch", targetBranchName))
+ return nil
}
+ // we have pushed the base branch as the new branch, now we need to commit the changes directly to the new branch
+ oldBranchName = targetBranchName
}
- redirectForCommitChoice(ctx, form.CommitChoice, branchName, form.TreePath)
-}
-
-// EditFilePost response for editing file
-func EditFilePost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.EditRepoFileForm)
- editFilePost(ctx, *form, false)
-}
-
-// NewFilePost response for creating file
-func NewFilePost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.EditRepoFileForm)
- editFilePost(ctx, *form, true)
-}
-
-// DiffPreviewPost render preview diff page
-func DiffPreviewPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.EditPreviewDiffForm)
- treePath := cleanUploadFileName(ctx.Repo.TreePath)
- if len(treePath) == 0 {
- ctx.Error(http.StatusInternalServerError, "file name to diff is invalid")
- return
- }
-
- entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treePath)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetTreeEntryByPath: "+err.Error())
- return
- } else if entry.IsDir() {
- ctx.Error(http.StatusUnprocessableEntity)
- return
+ return &preparedEditorCommitForm[T]{
+ form: form,
+ commonForm: commonForm,
+ CommitFormOptions: commitFormOptions,
+ OldBranchName: oldBranchName,
+ NewBranchName: targetBranchName,
+ GitCommitter: gitCommitter,
}
-
- diff, err := files_service.GetDiffPreview(ctx, ctx.Repo.Repository, ctx.Repo.BranchName, treePath, form.Content)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetDiffPreview: "+err.Error())
- return
- }
-
- if diff.NumFiles != 0 {
- ctx.Data["File"] = diff.Files[0]
- }
-
- ctx.HTML(http.StatusOK, tplEditDiffPreview)
}
-// DeleteFile render delete file page
-func DeleteFile(ctx *context.Context) {
- ctx.Data["PageIsDelete"] = true
- ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
- treePath := cleanUploadFileName(ctx.Repo.TreePath)
-
- if treePath != ctx.Repo.TreePath {
- ctx.Redirect(path.Join(ctx.Repo.RepoLink, "_delete", util.PathEscapeSegments(ctx.Repo.BranchName), util.PathEscapeSegments(treePath)))
+// redirectForCommitChoice redirects after committing the edit to a branch
+func redirectForCommitChoice[T any](ctx *context.Context, parsed *preparedEditorCommitForm[T], treePath string) {
+ // when editing a file in a PR, it should return to the origin location
+ if returnURI := ctx.FormString("return_uri"); returnURI != "" && httplib.IsCurrentGiteaSiteURL(ctx, returnURI) {
+ ctx.JSONRedirect(returnURI)
return
}
- ctx.Data["TreePath"] = treePath
- canCommit := renderCommitRights(ctx)
-
- ctx.Data["commit_summary"] = ""
- ctx.Data["commit_message"] = ""
- ctx.Data["last_commit"] = ctx.Repo.CommitID
- if canCommit {
- ctx.Data["commit_choice"] = frmCommitChoiceDirect
- } else {
- ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
+ if parsed.commonForm.CommitChoice == editorCommitChoiceNewBranch {
+ // Redirect to a pull request when possible
+ redirectToPullRequest := false
+ repo, baseBranch, headBranch := ctx.Repo.Repository, parsed.OldBranchName, parsed.NewBranchName
+ if ctx.Repo.Repository.IsFork && parsed.CommitFormOptions.CanCreateBasePullRequest {
+ redirectToPullRequest = true
+ baseBranch = repo.BaseRepo.DefaultBranch
+ headBranch = repo.Owner.Name + "/" + repo.Name + ":" + headBranch
+ repo = repo.BaseRepo
+ } else if repo.UnitEnabled(ctx, unit.TypePullRequests) {
+ redirectToPullRequest = true
+ }
+ if redirectToPullRequest {
+ ctx.JSONRedirect(repo.Link() + "/compare/" + util.PathEscapeSegments(baseBranch) + "..." + util.PathEscapeSegments(headBranch))
+ return
+ }
}
- ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx)
- ctx.HTML(http.StatusOK, tplDeleteFile)
+ // redirect to the newly updated file
+ redirectTo := util.URLJoin(ctx.Repo.RepoLink, "src/branch", util.PathEscapeSegments(parsed.NewBranchName), util.PathEscapeSegments(treePath))
+ ctx.JSONRedirect(redirectTo)
}
-// DeleteFilePost response for deleting file
-func DeleteFilePost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.DeleteRepoFileForm)
- canCommit := renderCommitRights(ctx)
- branchName := ctx.Repo.BranchName
- if form.CommitChoice == frmCommitChoiceNewBranch {
- branchName = form.NewBranchName
- }
-
- ctx.Data["PageIsDelete"] = true
- ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
- ctx.Data["TreePath"] = ctx.Repo.TreePath
- ctx.Data["commit_summary"] = form.CommitSummary
- ctx.Data["commit_message"] = form.CommitMessage
- ctx.Data["commit_choice"] = form.CommitChoice
- ctx.Data["new_branch_name"] = form.NewBranchName
- ctx.Data["last_commit"] = ctx.Repo.CommitID
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, tplDeleteFile)
- return
- }
-
- if branchName == ctx.Repo.BranchName && !canCommit {
- ctx.Data["Err_NewBranchName"] = true
- ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
- ctx.RenderWithErr(ctx.Tr("repo.editor.cannot_commit_to_protected_branch", branchName), tplDeleteFile, &form)
- return
+func editFileOpenExisting(ctx *context.Context) (prefetch []byte, dataRc io.ReadCloser, fInfo *fileInfo) {
+ entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
+ if err != nil {
+ HandleGitError(ctx, "GetTreeEntryByPath", err)
+ return nil, nil, nil
}
- message := strings.TrimSpace(form.CommitSummary)
- if len(message) == 0 {
- message = ctx.Locale.TrString("repo.editor.delete", ctx.Repo.TreePath)
- }
- form.CommitMessage = strings.TrimSpace(form.CommitMessage)
- if len(form.CommitMessage) > 0 {
- message += "\n\n" + form.CommitMessage
+ // No way to edit a directory online.
+ if entry.IsDir() {
+ ctx.NotFound(nil)
+ return nil, nil, nil
}
- if _, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.ChangeRepoFilesOptions{
- LastCommitID: form.LastCommit,
- OldBranch: ctx.Repo.BranchName,
- NewBranch: branchName,
- Files: []*files_service.ChangeRepoFile{
- {
- Operation: "delete",
- TreePath: ctx.Repo.TreePath,
- },
- },
- Message: message,
- Signoff: form.Signoff,
- }); err != nil {
- // This is where we handle all the errors thrown by repofiles.DeleteRepoFile
- if git.IsErrNotExist(err) || files_service.IsErrRepoFileDoesNotExist(err) {
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_deleting_no_longer_exists", ctx.Repo.TreePath), tplDeleteFile, &form)
- } else if files_service.IsErrFilenameInvalid(err) {
- ctx.Data["Err_TreePath"] = true
- ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", ctx.Repo.TreePath), tplDeleteFile, &form)
- } else if files_service.IsErrFilePathInvalid(err) {
- ctx.Data["Err_TreePath"] = true
- if fileErr, ok := err.(files_service.ErrFilePathInvalid); ok {
- switch fileErr.Type {
- case git.EntryModeSymlink:
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path), tplDeleteFile, &form)
- case git.EntryModeTree:
- ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_a_directory", fileErr.Path), tplDeleteFile, &form)
- case git.EntryModeBlob:
- ctx.RenderWithErr(ctx.Tr("repo.editor.directory_is_a_file", fileErr.Path), tplDeleteFile, &form)
- default:
- ctx.ServerError("DeleteRepoFile", err)
- }
- } else {
- ctx.ServerError("DeleteRepoFile", err)
- }
- } else if git.IsErrBranchNotExist(err) {
- // For when a user deletes a file to a branch that no longer exists
- if branchErr, ok := err.(git.ErrBranchNotExist); ok {
- ctx.RenderWithErr(ctx.Tr("repo.editor.branch_does_not_exist", branchErr.Name), tplDeleteFile, &form)
- } else {
- ctx.Error(http.StatusInternalServerError, err.Error())
- }
- } else if git_model.IsErrBranchAlreadyExists(err) {
- // For when a user specifies a new branch that already exists
- if branchErr, ok := err.(git_model.ErrBranchAlreadyExists); ok {
- ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplDeleteFile, &form)
- } else {
- ctx.Error(http.StatusInternalServerError, err.Error())
- }
- } else if files_service.IsErrCommitIDDoesNotMatch(err) || git.IsErrPushOutOfDate(err) {
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_deleting", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplDeleteFile, &form)
- } else if git.IsErrPushRejected(err) {
- errPushRej := err.(*git.ErrPushRejected)
- if len(errPushRej.Message) == 0 {
- ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected_no_message"), tplDeleteFile, &form)
- } else {
- flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
- "Message": ctx.Tr("repo.editor.push_rejected"),
- "Summary": ctx.Tr("repo.editor.push_rejected_summary"),
- "Details": utils.SanitizeFlashErrorString(errPushRej.Message),
- })
- if err != nil {
- ctx.ServerError("DeleteFilePost.HTMLString", err)
- return
- }
- ctx.RenderWithErr(flashError, tplDeleteFile, &form)
- }
+ blob := entry.Blob()
+ buf, dataRc, fInfo, err := getFileReader(ctx, ctx.Repo.Repository.ID, blob)
+ if err != nil {
+ if git.IsErrNotExist(err) {
+ ctx.NotFound(err)
} else {
- ctx.ServerError("DeleteRepoFile", err)
+ ctx.ServerError("getFileReader", err)
}
- return
+ return nil, nil, nil
}
- ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", ctx.Repo.TreePath))
- treePath := path.Dir(ctx.Repo.TreePath)
- if treePath == "." {
- treePath = "" // the file deleted was in the root, so we return the user to the root directory
- }
- if len(treePath) > 0 {
- // Need to get the latest commit since it changed
- commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.BranchName)
- if err == nil && commit != nil {
- // We have the comment, now find what directory we can return the user to
- // (must have entries)
- treePath = GetClosestParentWithFiles(treePath, commit)
- } else {
- treePath = "" // otherwise return them to the root of the repo
+ if fInfo.isLFSFile() {
+ lfsLock, err := git_model.GetTreePathLock(ctx, ctx.Repo.Repository.ID, ctx.Repo.TreePath)
+ if err != nil {
+ _ = dataRc.Close()
+ ctx.ServerError("GetTreePathLock", err)
+ return nil, nil, nil
+ } else if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID {
+ _ = dataRc.Close()
+ ctx.NotFound(nil)
+ return nil, nil, nil
}
}
- redirectForCommitChoice(ctx, form.CommitChoice, branchName, treePath)
+ return buf, dataRc, fInfo
}
-// UploadFile render upload file page
-func UploadFile(ctx *context.Context) {
- ctx.Data["PageIsUpload"] = true
- upload.AddUploadContext(ctx, "repo")
- canCommit := renderCommitRights(ctx)
- treePath := cleanUploadFileName(ctx.Repo.TreePath)
- if treePath != ctx.Repo.TreePath {
- ctx.Redirect(path.Join(ctx.Repo.RepoLink, "_upload", util.PathEscapeSegments(ctx.Repo.BranchName), util.PathEscapeSegments(treePath)))
- return
- }
- ctx.Repo.TreePath = treePath
-
- treeNames, treePaths := getParentTreeFields(ctx.Repo.TreePath)
- if len(treeNames) == 0 {
- // We must at least have one element for user to input.
- treeNames = []string{""}
- }
-
- ctx.Data["TreeNames"] = treeNames
- ctx.Data["TreePaths"] = treePaths
- ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
- ctx.Data["commit_summary"] = ""
- ctx.Data["commit_message"] = ""
- if canCommit {
- ctx.Data["commit_choice"] = frmCommitChoiceDirect
- } else {
- ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
- }
- ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx)
-
- ctx.HTML(http.StatusOK, tplUploadFile)
-}
-
-// UploadFilePost response for uploading file
-func UploadFilePost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.UploadRepoFileForm)
- ctx.Data["PageIsUpload"] = true
- upload.AddUploadContext(ctx, "repo")
- canCommit := renderCommitRights(ctx)
-
- oldBranchName := ctx.Repo.BranchName
- branchName := oldBranchName
-
- if form.CommitChoice == frmCommitChoiceNewBranch {
- branchName = form.NewBranchName
- }
-
- form.TreePath = cleanUploadFileName(form.TreePath)
+func EditFile(ctx *context.Context) {
+ editorAction := ctx.PathParam("editor_action")
+ isNewFile := editorAction == "_new"
+ ctx.Data["IsNewFile"] = isNewFile
- treeNames, treePaths := getParentTreeFields(form.TreePath)
- if len(treeNames) == 0 {
- // We must at least have one element for user to input.
- treeNames = []string{""}
+ // Check if the filename (and additional path) is specified in the querystring
+ // (filename is a misnomer, but kept for compatibility with GitHub)
+ urlQuery := ctx.Req.URL.Query()
+ queryFilename := urlQuery.Get("filename")
+ if queryFilename != "" {
+ newTreePath := path.Join(ctx.Repo.TreePath, queryFilename)
+ redirectTo := fmt.Sprintf("%s/%s/%s/%s", ctx.Repo.RepoLink, editorAction, util.PathEscapeSegments(ctx.Repo.BranchName), util.PathEscapeSegments(newTreePath))
+ urlQuery.Del("filename")
+ if newQueryParams := urlQuery.Encode(); newQueryParams != "" {
+ redirectTo += "?" + newQueryParams
+ }
+ ctx.Redirect(redirectTo)
+ return
}
- ctx.Data["TreePath"] = form.TreePath
- ctx.Data["TreeNames"] = treeNames
- ctx.Data["TreePaths"] = treePaths
- ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName)
- ctx.Data["commit_summary"] = form.CommitSummary
- ctx.Data["commit_message"] = form.CommitMessage
- ctx.Data["commit_choice"] = form.CommitChoice
- ctx.Data["new_branch_name"] = branchName
+ // on the "New File" page, we should add an empty path field to make end users could input a new name
+ prepareTreePathFieldsAndPaths(ctx, util.Iif(isNewFile, ctx.Repo.TreePath+"/", ctx.Repo.TreePath))
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, tplUploadFile)
+ prepareEditorCommitFormOptions(ctx, editorAction)
+ if ctx.Written() {
return
}
- if oldBranchName != branchName {
- if _, err := ctx.Repo.GitRepo.GetBranch(branchName); err == nil {
- ctx.Data["Err_NewBranchName"] = true
- ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchName), tplUploadFile, &form)
+ if !isNewFile {
+ prefetch, dataRc, fInfo := editFileOpenExisting(ctx)
+ if ctx.Written() {
return
}
- } else if !canCommit {
- ctx.Data["Err_NewBranchName"] = true
- ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
- ctx.RenderWithErr(ctx.Tr("repo.editor.cannot_commit_to_protected_branch", branchName), tplUploadFile, &form)
- return
- }
+ defer dataRc.Close()
- if !ctx.Repo.Repository.IsEmpty {
- var newTreePath string
- for _, part := range treeNames {
- newTreePath = path.Join(newTreePath, part)
- entry, err := ctx.Repo.Commit.GetTreeEntryByPath(newTreePath)
- if err != nil {
- if git.IsErrNotExist(err) {
- break // Means there is no item with that name, so we're good
- }
- ctx.ServerError("Repo.Commit.GetTreeEntryByPath", err)
- return
- }
+ ctx.Data["FileSize"] = fInfo.fileSize
- // User can only upload files to a directory, the directory name shouldn't be an existing file.
- if !entry.IsDir() {
- ctx.Data["Err_TreePath"] = true
- ctx.RenderWithErr(ctx.Tr("repo.editor.directory_is_a_file", part), tplUploadFile, &form)
- return
- }
+ // Only some file types are editable online as text.
+ if fInfo.isLFSFile() {
+ ctx.Data["NotEditableReason"] = ctx.Tr("repo.editor.cannot_edit_lfs_files")
+ } else if !fInfo.st.IsRepresentableAsText() {
+ ctx.Data["NotEditableReason"] = ctx.Tr("repo.editor.cannot_edit_non_text_files")
+ } else if fInfo.fileSize >= setting.UI.MaxDisplayFileSize {
+ ctx.Data["NotEditableReason"] = ctx.Tr("repo.editor.cannot_edit_too_large_file")
}
- }
- message := strings.TrimSpace(form.CommitSummary)
- if len(message) == 0 {
- dir := form.TreePath
- if dir == "" {
- dir = "/"
- }
- message = ctx.Locale.TrString("repo.editor.upload_files_to_dir", dir)
- }
-
- form.CommitMessage = strings.TrimSpace(form.CommitMessage)
- if len(form.CommitMessage) > 0 {
- message += "\n\n" + form.CommitMessage
- }
-
- if err := files_service.UploadRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.UploadRepoFileOptions{
- LastCommitID: ctx.Repo.CommitID,
- OldBranch: oldBranchName,
- NewBranch: branchName,
- TreePath: form.TreePath,
- Message: message,
- Files: form.Files,
- Signoff: form.Signoff,
- }); err != nil {
- if git_model.IsErrLFSFileLocked(err) {
- ctx.Data["Err_TreePath"] = true
- ctx.RenderWithErr(ctx.Tr("repo.editor.upload_file_is_locked", err.(git_model.ErrLFSFileLocked).Path, err.(git_model.ErrLFSFileLocked).UserName), tplUploadFile, &form)
- } else if files_service.IsErrFilenameInvalid(err) {
- ctx.Data["Err_TreePath"] = true
- ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", form.TreePath), tplUploadFile, &form)
- } else if files_service.IsErrFilePathInvalid(err) {
- ctx.Data["Err_TreePath"] = true
- fileErr := err.(files_service.ErrFilePathInvalid)
- switch fileErr.Type {
- case git.EntryModeSymlink:
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path), tplUploadFile, &form)
- case git.EntryModeTree:
- ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_a_directory", fileErr.Path), tplUploadFile, &form)
- case git.EntryModeBlob:
- ctx.RenderWithErr(ctx.Tr("repo.editor.directory_is_a_file", fileErr.Path), tplUploadFile, &form)
- default:
- ctx.Error(http.StatusInternalServerError, err.Error())
+ if ctx.Data["NotEditableReason"] == nil {
+ buf, err := io.ReadAll(io.MultiReader(bytes.NewReader(prefetch), dataRc))
+ if err != nil {
+ ctx.ServerError("ReadAll", err)
+ return
}
- } else if files_service.IsErrRepoFileAlreadyExists(err) {
- ctx.Data["Err_TreePath"] = true
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_already_exists", form.TreePath), tplUploadFile, &form)
- } else if git.IsErrBranchNotExist(err) {
- branchErr := err.(git.ErrBranchNotExist)
- ctx.RenderWithErr(ctx.Tr("repo.editor.branch_does_not_exist", branchErr.Name), tplUploadFile, &form)
- } else if git_model.IsErrBranchAlreadyExists(err) {
- // For when a user specifies a new branch that already exists
- ctx.Data["Err_NewBranchName"] = true
- branchErr := err.(git_model.ErrBranchAlreadyExists)
- ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplUploadFile, &form)
- } else if git.IsErrPushOutOfDate(err) {
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(ctx.Repo.CommitID)+"..."+util.PathEscapeSegments(form.NewBranchName)), tplUploadFile, &form)
- } else if git.IsErrPushRejected(err) {
- errPushRej := err.(*git.ErrPushRejected)
- if len(errPushRej.Message) == 0 {
- ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected_no_message"), tplUploadFile, &form)
+ if content, err := charset.ToUTF8(buf, charset.ConvertOpts{KeepBOM: true}); err != nil {
+ ctx.Data["FileContent"] = string(buf)
} else {
- flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
- "Message": ctx.Tr("repo.editor.push_rejected"),
- "Summary": ctx.Tr("repo.editor.push_rejected_summary"),
- "Details": utils.SanitizeFlashErrorString(errPushRej.Message),
- })
- if err != nil {
- ctx.ServerError("UploadFilePost.HTMLString", err)
- return
- }
- ctx.RenderWithErr(flashError, tplUploadFile, &form)
+ ctx.Data["FileContent"] = content
}
- } else {
- // os.ErrNotExist - upload file missing in the intervening time?!
- log.Error("Error during upload to repo: %-v to filepath: %s on %s from %s: %v", ctx.Repo.Repository, form.TreePath, oldBranchName, form.NewBranchName, err)
- ctx.RenderWithErr(ctx.Tr("repo.editor.unable_to_upload_files", form.TreePath, err), tplUploadFile, &form)
}
- return
}
- if ctx.Repo.Repository.IsEmpty {
- if isEmpty, err := ctx.Repo.GitRepo.IsEmpty(); err == nil && !isEmpty {
- _ = repo_model.UpdateRepositoryCols(ctx, &repo_model.Repository{ID: ctx.Repo.Repository.ID, IsEmpty: false}, "is_empty")
- }
+ ctx.Data["EditorconfigJson"] = getContextRepoEditorConfig(ctx, ctx.Repo.TreePath)
+ ctx.HTML(http.StatusOK, tplEditFile)
+}
+
+func EditFilePost(ctx *context.Context) {
+ editorAction := ctx.PathParam("editor_action")
+ isNewFile := editorAction == "_new"
+ parsed := prepareEditorCommitSubmittedForm[*forms.EditRepoFileForm](ctx)
+ if ctx.Written() {
+ return
}
- redirectForCommitChoice(ctx, form.CommitChoice, branchName, form.TreePath)
-}
+ defaultCommitMessage := util.Iif(isNewFile, ctx.Locale.TrString("repo.editor.add", parsed.form.TreePath), ctx.Locale.TrString("repo.editor.update", parsed.form.TreePath))
-func cleanUploadFileName(name string) string {
- // Rebase the filename
- name = util.PathJoinRel(name)
- // Git disallows any filenames to have a .git directory in them.
- for _, part := range strings.Split(name, "/") {
- if strings.ToLower(part) == ".git" {
- return ""
- }
+ var operation string
+ if isNewFile {
+ operation = "create"
+ } else if parsed.form.Content.Has() {
+ // The form content only has data if the file is representable as text, is not too large and not in lfs.
+ operation = "update"
+ } else if ctx.Repo.TreePath != parsed.form.TreePath {
+ // If it doesn't have data, the only possible operation is a "rename"
+ operation = "rename"
+ } else {
+ // It should never happen, just in case
+ ctx.JSONError(ctx.Tr("error.occurred"))
+ return
}
- return name
-}
-// UploadFileToServer upload file to server file dir not git
-func UploadFileToServer(ctx *context.Context) {
- file, header, err := ctx.Req.FormFile("file")
+ _, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.ChangeRepoFilesOptions{
+ LastCommitID: parsed.form.LastCommit,
+ OldBranch: parsed.OldBranchName,
+ NewBranch: parsed.NewBranchName,
+ Message: parsed.GetCommitMessage(defaultCommitMessage),
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: operation,
+ FromTreePath: ctx.Repo.TreePath,
+ TreePath: parsed.form.TreePath,
+ ContentReader: strings.NewReader(strings.ReplaceAll(parsed.form.Content.Value(), "\r", "")),
+ },
+ },
+ Signoff: parsed.form.Signoff,
+ Author: parsed.GitCommitter,
+ Committer: parsed.GitCommitter,
+ })
if err != nil {
- ctx.Error(http.StatusInternalServerError, fmt.Sprintf("FormFile: %v", err))
+ editorHandleFileOperationError(ctx, parsed.NewBranchName, err)
return
}
- defer file.Close()
- buf := make([]byte, 1024)
- n, _ := util.ReadAtMost(file, buf)
- if n > 0 {
- buf = buf[:n]
- }
+ redirectForCommitChoice(ctx, parsed, parsed.form.TreePath)
+}
- err = upload.Verify(buf, header.Filename, setting.Repository.Upload.AllowedTypes)
- if err != nil {
- ctx.Error(http.StatusBadRequest, err.Error())
+// DeleteFile render delete file page
+func DeleteFile(ctx *context.Context) {
+ prepareEditorCommitFormOptions(ctx, "_delete")
+ if ctx.Written() {
return
}
+ ctx.Data["PageIsDelete"] = true
+ ctx.HTML(http.StatusOK, tplDeleteFile)
+}
- name := cleanUploadFileName(header.Filename)
- if len(name) == 0 {
- ctx.Error(http.StatusInternalServerError, "Upload file name is invalid")
+// DeleteFilePost response for deleting file
+func DeleteFilePost(ctx *context.Context) {
+ parsed := prepareEditorCommitSubmittedForm[*forms.DeleteRepoFileForm](ctx)
+ if ctx.Written() {
return
}
- upload, err := repo_model.NewUpload(ctx, name, buf, file)
+ treePath := ctx.Repo.TreePath
+ _, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.ChangeRepoFilesOptions{
+ LastCommitID: parsed.form.LastCommit,
+ OldBranch: parsed.OldBranchName,
+ NewBranch: parsed.NewBranchName,
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "delete",
+ TreePath: treePath,
+ },
+ },
+ Message: parsed.GetCommitMessage(ctx.Locale.TrString("repo.editor.delete", treePath)),
+ Signoff: parsed.form.Signoff,
+ Author: parsed.GitCommitter,
+ Committer: parsed.GitCommitter,
+ })
if err != nil {
- ctx.Error(http.StatusInternalServerError, fmt.Sprintf("NewUpload: %v", err))
+ editorHandleFileOperationError(ctx, parsed.NewBranchName, err)
return
}
- log.Trace("New file uploaded: %s", upload.UUID)
- ctx.JSON(http.StatusOK, map[string]string{
- "uuid": upload.UUID,
- })
+ ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", treePath))
+ redirectTreePath := getClosestParentWithFiles(ctx.Repo.GitRepo, parsed.NewBranchName, treePath)
+ redirectForCommitChoice(ctx, parsed, redirectTreePath)
}
-// RemoveUploadFileFromServer remove file from server file dir
-func RemoveUploadFileFromServer(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.RemoveUploadFileForm)
- if len(form.File) == 0 {
- ctx.Status(http.StatusNoContent)
- return
- }
-
- if err := repo_model.DeleteUploadByUUID(ctx, form.File); err != nil {
- ctx.Error(http.StatusInternalServerError, fmt.Sprintf("DeleteUploadByUUID: %v", err))
+func UploadFile(ctx *context.Context) {
+ ctx.Data["PageIsUpload"] = true
+ prepareTreePathFieldsAndPaths(ctx, ctx.Repo.TreePath)
+ opts := prepareEditorCommitFormOptions(ctx, "_upload")
+ if ctx.Written() {
return
}
+ upload.AddUploadContextForRepo(ctx, opts.TargetRepo)
- log.Trace("Upload file removed: %s", form.File)
- ctx.Status(http.StatusNoContent)
+ ctx.HTML(http.StatusOK, tplUploadFile)
}
-// GetUniquePatchBranchName Gets a unique branch name for a new patch branch
-// It will be in the form of <username>-patch-<num> where <num> is the first branch of this format
-// that doesn't already exist. If we exceed 1000 tries or an error is thrown, we just return "" so the user has to
-// type in the branch name themselves (will be an empty field)
-func GetUniquePatchBranchName(ctx *context.Context) string {
- prefix := ctx.Doer.LowerName + "-patch-"
- for i := 1; i <= 1000; i++ {
- branchName := fmt.Sprintf("%s%d", prefix, i)
- if _, err := ctx.Repo.GitRepo.GetBranch(branchName); err != nil {
- if git.IsErrBranchNotExist(err) {
- return branchName
- }
- log.Error("GetUniquePatchBranchName: %v", err)
- return ""
- }
+func UploadFilePost(ctx *context.Context) {
+ parsed := prepareEditorCommitSubmittedForm[*forms.UploadRepoFileForm](ctx)
+ if ctx.Written() {
+ return
}
- return ""
-}
-// GetClosestParentWithFiles Recursively gets the path of parent in a tree that has files (used when file in a tree is
-// deleted). Returns "" for the root if no parents other than the root have files. If the given treePath isn't a
-// SubTree or it has no entries, we go up one dir and see if we can return the user to that listing.
-func GetClosestParentWithFiles(treePath string, commit *git.Commit) string {
- if len(treePath) == 0 || treePath == "." {
- return ""
- }
- // see if the tree has entries
- if tree, err := commit.SubTree(treePath); err != nil {
- // failed to get tree, going up a dir
- return GetClosestParentWithFiles(path.Dir(treePath), commit)
- } else if entries, err := tree.ListEntries(); err != nil || len(entries) == 0 {
- // no files in this dir, going up a dir
- return GetClosestParentWithFiles(path.Dir(treePath), commit)
- }
- return treePath
+ defaultCommitMessage := ctx.Locale.TrString("repo.editor.upload_files_to_dir", util.IfZero(parsed.form.TreePath, "/"))
+ err := files_service.UploadRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.UploadRepoFileOptions{
+ LastCommitID: parsed.form.LastCommit,
+ OldBranch: parsed.OldBranchName,
+ NewBranch: parsed.NewBranchName,
+ TreePath: parsed.form.TreePath,
+ Message: parsed.GetCommitMessage(defaultCommitMessage),
+ Files: parsed.form.Files,
+ Signoff: parsed.form.Signoff,
+ Author: parsed.GitCommitter,
+ Committer: parsed.GitCommitter,
+ })
+ if err != nil {
+ editorHandleFileOperationError(ctx, parsed.NewBranchName, err)
+ return
+ }
+ redirectForCommitChoice(ctx, parsed, parsed.form.TreePath)
}
diff --git a/routers/web/repo/editor_apply_patch.go b/routers/web/repo/editor_apply_patch.go
new file mode 100644
index 0000000000..bd2811cc5f
--- /dev/null
+++ b/routers/web/repo/editor_apply_patch.go
@@ -0,0 +1,51 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "net/http"
+ "strings"
+
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/forms"
+ "code.gitea.io/gitea/services/repository/files"
+)
+
+func NewDiffPatch(ctx *context.Context) {
+ prepareEditorCommitFormOptions(ctx, "_diffpatch")
+ if ctx.Written() {
+ return
+ }
+
+ ctx.Data["PageIsPatch"] = true
+ ctx.HTML(http.StatusOK, tplPatchFile)
+}
+
+// NewDiffPatchPost response for sending patch page
+func NewDiffPatchPost(ctx *context.Context) {
+ parsed := prepareEditorCommitSubmittedForm[*forms.EditRepoFileForm](ctx)
+ if ctx.Written() {
+ return
+ }
+
+ defaultCommitMessage := ctx.Locale.TrString("repo.editor.patch")
+ _, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, &files.ApplyDiffPatchOptions{
+ LastCommitID: parsed.form.LastCommit,
+ OldBranch: parsed.OldBranchName,
+ NewBranch: parsed.NewBranchName,
+ Message: parsed.GetCommitMessage(defaultCommitMessage),
+ Content: strings.ReplaceAll(parsed.form.Content.Value(), "\r\n", "\n"),
+ Author: parsed.GitCommitter,
+ Committer: parsed.GitCommitter,
+ })
+ if err != nil {
+ err = util.ErrorWrapLocale(err, "repo.editor.fail_to_apply_patch")
+ }
+ if err != nil {
+ editorHandleFileOperationError(ctx, parsed.NewBranchName, err)
+ return
+ }
+ redirectForCommitChoice(ctx, parsed, parsed.form.TreePath)
+}
diff --git a/routers/web/repo/editor_cherry_pick.go b/routers/web/repo/editor_cherry_pick.go
new file mode 100644
index 0000000000..10c2741b1c
--- /dev/null
+++ b/routers/web/repo/editor_cherry_pick.go
@@ -0,0 +1,86 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "bytes"
+ "net/http"
+ "strings"
+
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/forms"
+ "code.gitea.io/gitea/services/repository/files"
+)
+
+func CherryPick(ctx *context.Context) {
+ prepareEditorCommitFormOptions(ctx, "_cherrypick")
+ if ctx.Written() {
+ return
+ }
+
+ fromCommitID := ctx.PathParam("sha")
+ ctx.Data["FromCommitID"] = fromCommitID
+ cherryPickCommit, err := ctx.Repo.GitRepo.GetCommit(fromCommitID)
+ if err != nil {
+ HandleGitError(ctx, "GetCommit", err)
+ return
+ }
+
+ if ctx.FormString("cherry-pick-type") == "revert" {
+ ctx.Data["CherryPickType"] = "revert"
+ ctx.Data["commit_summary"] = "revert " + ctx.PathParam("sha")
+ ctx.Data["commit_message"] = "revert " + cherryPickCommit.Message()
+ } else {
+ ctx.Data["CherryPickType"] = "cherry-pick"
+ splits := strings.SplitN(cherryPickCommit.Message(), "\n", 2)
+ ctx.Data["commit_summary"] = splits[0]
+ ctx.Data["commit_message"] = splits[1]
+ }
+
+ ctx.HTML(http.StatusOK, tplCherryPick)
+}
+
+func CherryPickPost(ctx *context.Context) {
+ fromCommitID := ctx.PathParam("sha")
+ parsed := prepareEditorCommitSubmittedForm[*forms.CherryPickForm](ctx)
+ if ctx.Written() {
+ return
+ }
+
+ defaultCommitMessage := util.Iif(parsed.form.Revert, ctx.Locale.TrString("repo.commit.revert-header", fromCommitID), ctx.Locale.TrString("repo.commit.cherry-pick-header", fromCommitID))
+ opts := &files.ApplyDiffPatchOptions{
+ LastCommitID: parsed.form.LastCommit,
+ OldBranch: parsed.OldBranchName,
+ NewBranch: parsed.NewBranchName,
+ Message: parsed.GetCommitMessage(defaultCommitMessage),
+ Author: parsed.GitCommitter,
+ Committer: parsed.GitCommitter,
+ }
+
+ // First try the simple plain read-tree -m approach
+ opts.Content = fromCommitID
+ if _, err := files.CherryPick(ctx, ctx.Repo.Repository, ctx.Doer, parsed.form.Revert, opts); err != nil {
+ // Drop through to the "apply" method
+ buf := &bytes.Buffer{}
+ if parsed.form.Revert {
+ err = git.GetReverseRawDiff(ctx, ctx.Repo.Repository.RepoPath(), fromCommitID, buf)
+ } else {
+ err = git.GetRawDiff(ctx.Repo.GitRepo, fromCommitID, "patch", buf)
+ }
+ if err == nil {
+ opts.Content = buf.String()
+ _, err = files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, opts)
+ if err != nil {
+ err = util.ErrorWrapLocale(err, "repo.editor.fail_to_apply_patch")
+ }
+ }
+ if err != nil {
+ editorHandleFileOperationError(ctx, parsed.NewBranchName, err)
+ return
+ }
+ }
+ redirectForCommitChoice(ctx, parsed, parsed.form.TreePath)
+}
diff --git a/routers/web/repo/editor_error.go b/routers/web/repo/editor_error.go
new file mode 100644
index 0000000000..245226a039
--- /dev/null
+++ b/routers/web/repo/editor_error.go
@@ -0,0 +1,82 @@
+// Copyright 2025 Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "errors"
+
+ git_model "code.gitea.io/gitea/models/git"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/routers/utils"
+ context_service "code.gitea.io/gitea/services/context"
+ files_service "code.gitea.io/gitea/services/repository/files"
+)
+
+func errorAs[T error](v error) (e T, ok bool) {
+ if errors.As(v, &e) {
+ return e, true
+ }
+ return e, false
+}
+
+func editorHandleFileOperationErrorRender(ctx *context_service.Context, message, summary, details string) {
+ flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
+ "Message": message,
+ "Summary": summary,
+ "Details": utils.SanitizeFlashErrorString(details),
+ })
+ if err == nil {
+ ctx.JSONError(flashError)
+ } else {
+ log.Error("RenderToHTML: %v", err)
+ ctx.JSONError(message + "\n" + summary + "\n" + utils.SanitizeFlashErrorString(details))
+ }
+}
+
+func editorHandleFileOperationError(ctx *context_service.Context, targetBranchName string, err error) {
+ if errAs := util.ErrorAsLocale(err); errAs != nil {
+ ctx.JSONError(ctx.Tr(errAs.TrKey, errAs.TrArgs...))
+ } else if errAs, ok := errorAs[git.ErrNotExist](err); ok {
+ ctx.JSONError(ctx.Tr("repo.editor.file_modifying_no_longer_exists", errAs.RelPath))
+ } else if errAs, ok := errorAs[git_model.ErrLFSFileLocked](err); ok {
+ ctx.JSONError(ctx.Tr("repo.editor.upload_file_is_locked", errAs.Path, errAs.UserName))
+ } else if errAs, ok := errorAs[files_service.ErrFilenameInvalid](err); ok {
+ ctx.JSONError(ctx.Tr("repo.editor.filename_is_invalid", errAs.Path))
+ } else if errAs, ok := errorAs[files_service.ErrFilePathInvalid](err); ok {
+ switch errAs.Type {
+ case git.EntryModeSymlink:
+ ctx.JSONError(ctx.Tr("repo.editor.file_is_a_symlink", errAs.Path))
+ case git.EntryModeTree:
+ ctx.JSONError(ctx.Tr("repo.editor.filename_is_a_directory", errAs.Path))
+ case git.EntryModeBlob:
+ ctx.JSONError(ctx.Tr("repo.editor.directory_is_a_file", errAs.Path))
+ default:
+ ctx.JSONError(ctx.Tr("repo.editor.filename_is_invalid", errAs.Path))
+ }
+ } else if errAs, ok := errorAs[files_service.ErrRepoFileAlreadyExists](err); ok {
+ ctx.JSONError(ctx.Tr("repo.editor.file_already_exists", errAs.Path))
+ } else if errAs, ok := errorAs[git.ErrBranchNotExist](err); ok {
+ ctx.JSONError(ctx.Tr("repo.editor.branch_does_not_exist", errAs.Name))
+ } else if errAs, ok := errorAs[git_model.ErrBranchAlreadyExists](err); ok {
+ ctx.JSONError(ctx.Tr("repo.editor.branch_already_exists", errAs.BranchName))
+ } else if files_service.IsErrCommitIDDoesNotMatch(err) {
+ ctx.JSONError(ctx.Tr("repo.editor.commit_id_not_matching"))
+ } else if files_service.IsErrCommitIDDoesNotMatch(err) || git.IsErrPushOutOfDate(err) {
+ ctx.JSONError(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(ctx.Repo.CommitID)+"..."+util.PathEscapeSegments(targetBranchName)))
+ } else if errAs, ok := errorAs[*git.ErrPushRejected](err); ok {
+ if errAs.Message == "" {
+ ctx.JSONError(ctx.Tr("repo.editor.push_rejected_no_message"))
+ } else {
+ editorHandleFileOperationErrorRender(ctx, ctx.Locale.TrString("repo.editor.push_rejected"), ctx.Locale.TrString("repo.editor.push_rejected_summary"), errAs.Message)
+ }
+ } else if errors.Is(err, util.ErrNotExist) {
+ ctx.JSONError(ctx.Tr("error.not_found"))
+ } else {
+ setting.PanicInDevOrTesting("unclear err %T: %v", err, err)
+ editorHandleFileOperationErrorRender(ctx, ctx.Locale.TrString("repo.editor.failed_to_commit"), ctx.Locale.TrString("repo.editor.failed_to_commit_summary"), err.Error())
+ }
+}
diff --git a/routers/web/repo/editor_fork.go b/routers/web/repo/editor_fork.go
new file mode 100644
index 0000000000..b78a634c00
--- /dev/null
+++ b/routers/web/repo/editor_fork.go
@@ -0,0 +1,31 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "net/http"
+
+ "code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/services/context"
+ repo_service "code.gitea.io/gitea/services/repository"
+)
+
+const tplEditorFork templates.TplName = "repo/editor/fork"
+
+func ForkToEdit(ctx *context.Context) {
+ ctx.HTML(http.StatusOK, tplEditorFork)
+}
+
+func ForkToEditPost(ctx *context.Context) {
+ ForkRepoTo(ctx, ctx.Doer, repo_service.ForkRepoOptions{
+ BaseRepo: ctx.Repo.Repository,
+ Name: getUniqueRepositoryName(ctx, ctx.Doer.ID, ctx.Repo.Repository.Name),
+ Description: ctx.Repo.Repository.Description,
+ SingleBranch: ctx.Repo.Repository.DefaultBranch, // maybe we only need the default branch in the fork?
+ })
+ if ctx.Written() {
+ return
+ }
+ ctx.JSONRedirect("") // reload the page, the new fork should be editable now
+}
diff --git a/routers/web/repo/editor_preview.go b/routers/web/repo/editor_preview.go
new file mode 100644
index 0000000000..14be5b72b6
--- /dev/null
+++ b/routers/web/repo/editor_preview.go
@@ -0,0 +1,41 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "net/http"
+
+ "code.gitea.io/gitea/services/context"
+ files_service "code.gitea.io/gitea/services/repository/files"
+)
+
+func DiffPreviewPost(ctx *context.Context) {
+ content := ctx.FormString("content")
+ treePath := files_service.CleanGitTreePath(ctx.Repo.TreePath)
+ if treePath == "" {
+ ctx.HTTPError(http.StatusBadRequest, "file name to diff is invalid")
+ return
+ }
+
+ entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treePath)
+ if err != nil {
+ ctx.ServerError("GetTreeEntryByPath", err)
+ return
+ } else if entry.IsDir() {
+ ctx.HTTPError(http.StatusUnprocessableEntity)
+ return
+ }
+
+ diff, err := files_service.GetDiffPreview(ctx, ctx.Repo.Repository, ctx.Repo.BranchName, treePath, content)
+ if err != nil {
+ ctx.ServerError("GetDiffPreview", err)
+ return
+ }
+
+ if len(diff.Files) != 0 {
+ ctx.Data["File"] = diff.Files[0]
+ }
+
+ ctx.HTML(http.StatusOK, tplEditDiffPreview)
+}
diff --git a/routers/web/repo/editor_test.go b/routers/web/repo/editor_test.go
index 566db31693..6e2c1d6219 100644
--- a/routers/web/repo/editor_test.go
+++ b/routers/web/repo/editor_test.go
@@ -6,76 +6,27 @@ package repo
import (
"testing"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/services/contexttest"
"github.com/stretchr/testify/assert"
)
-func TestCleanUploadName(t *testing.T) {
+func TestEditorUtils(t *testing.T) {
unittest.PrepareTestEnv(t)
-
- kases := map[string]string{
- ".git/refs/master": "",
- "/root/abc": "root/abc",
- "./../../abc": "abc",
- "a/../.git": "",
- "a/../../../abc": "abc",
- "../../../acd": "acd",
- "../../.git/abc": "",
- "..\\..\\.git/abc": "..\\..\\.git/abc",
- "..\\../.git/abc": "",
- "..\\../.git": "",
- "abc/../def": "def",
- ".drone.yml": ".drone.yml",
- ".abc/def/.drone.yml": ".abc/def/.drone.yml",
- "..drone.yml.": "..drone.yml.",
- "..a.dotty...name...": "..a.dotty...name...",
- "..a.dotty../.folder../.name...": "..a.dotty../.folder../.name...",
- }
- for k, v := range kases {
- assert.EqualValues(t, cleanUploadFileName(k), v)
- }
-}
-
-func TestGetUniquePatchBranchName(t *testing.T) {
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user2/repo1")
- ctx.SetPathParam("id", "1")
- contexttest.LoadRepo(t, ctx, 1)
- contexttest.LoadRepoCommit(t, ctx)
- contexttest.LoadUser(t, ctx, 2)
- contexttest.LoadGitRepo(t, ctx)
- defer ctx.Repo.GitRepo.Close()
-
- expectedBranchName := "user2-patch-1"
- branchName := GetUniquePatchBranchName(ctx)
- assert.Equal(t, expectedBranchName, branchName)
-}
-
-func TestGetClosestParentWithFiles(t *testing.T) {
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user2/repo1")
- ctx.SetPathParam("id", "1")
- contexttest.LoadRepo(t, ctx, 1)
- contexttest.LoadRepoCommit(t, ctx)
- contexttest.LoadUser(t, ctx, 2)
- contexttest.LoadGitRepo(t, ctx)
- defer ctx.Repo.GitRepo.Close()
-
- repo := ctx.Repo.Repository
- branch := repo.DefaultBranch
- gitRepo, _ := gitrepo.OpenRepository(git.DefaultContext, repo)
- defer gitRepo.Close()
- commit, _ := gitRepo.GetBranchCommit(branch)
- var expectedTreePath string // Should return the root dir, empty string, since there are no subdirs in this repo
- for _, deletedFile := range []string{
- "dir1/dir2/dir3/file.txt",
- "file.txt",
- } {
- treePath := GetClosestParentWithFiles(deletedFile, commit)
- assert.Equal(t, expectedTreePath, treePath)
- }
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ t.Run("getUniquePatchBranchName", func(t *testing.T) {
+ branchName := getUniquePatchBranchName(t.Context(), "user2", repo)
+ assert.Equal(t, "user2-patch-1", branchName)
+ })
+ t.Run("getClosestParentWithFiles", func(t *testing.T) {
+ gitRepo, _ := gitrepo.OpenRepository(git.DefaultContext, repo)
+ defer gitRepo.Close()
+ treePath := getClosestParentWithFiles(gitRepo, "sub-home-md-img-check", "docs/foo/bar")
+ assert.Equal(t, "docs", treePath)
+ treePath = getClosestParentWithFiles(gitRepo, "sub-home-md-img-check", "any/other")
+ assert.Empty(t, treePath)
+ })
}
diff --git a/routers/web/repo/editor_uploader.go b/routers/web/repo/editor_uploader.go
new file mode 100644
index 0000000000..1ce9a1aca4
--- /dev/null
+++ b/routers/web/repo/editor_uploader.go
@@ -0,0 +1,61 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "net/http"
+
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/context/upload"
+ files_service "code.gitea.io/gitea/services/repository/files"
+)
+
+// UploadFileToServer upload file to server file dir not git
+func UploadFileToServer(ctx *context.Context) {
+ file, header, err := ctx.Req.FormFile("file")
+ if err != nil {
+ ctx.ServerError("FormFile", err)
+ return
+ }
+ defer file.Close()
+
+ buf := make([]byte, 1024)
+ n, _ := util.ReadAtMost(file, buf)
+ if n > 0 {
+ buf = buf[:n]
+ }
+
+ err = upload.Verify(buf, header.Filename, setting.Repository.Upload.AllowedTypes)
+ if err != nil {
+ ctx.HTTPError(http.StatusBadRequest, err.Error())
+ return
+ }
+
+ name := files_service.CleanGitTreePath(header.Filename)
+ if len(name) == 0 {
+ ctx.HTTPError(http.StatusBadRequest, "Upload file name is invalid")
+ return
+ }
+
+ uploaded, err := repo_model.NewUpload(ctx, name, buf, file)
+ if err != nil {
+ ctx.ServerError("NewUpload", err)
+ return
+ }
+
+ ctx.JSON(http.StatusOK, map[string]string{"uuid": uploaded.UUID})
+}
+
+// RemoveUploadFileFromServer remove file from server file dir
+func RemoveUploadFileFromServer(ctx *context.Context) {
+ fileUUID := ctx.FormString("file")
+ if err := repo_model.DeleteUploadByUUID(ctx, fileUUID); err != nil {
+ ctx.ServerError("DeleteUploadByUUID", err)
+ return
+ }
+ ctx.Status(http.StatusNoContent)
+}
diff --git a/routers/web/repo/editor_util.go b/routers/web/repo/editor_util.go
new file mode 100644
index 0000000000..f910f0bd40
--- /dev/null
+++ b/routers/web/repo/editor_util.go
@@ -0,0 +1,110 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "context"
+ "fmt"
+ "path"
+ "strings"
+
+ git_model "code.gitea.io/gitea/models/git"
+ repo_model "code.gitea.io/gitea/models/repo"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/log"
+ repo_module "code.gitea.io/gitea/modules/repository"
+ context_service "code.gitea.io/gitea/services/context"
+)
+
+// getUniquePatchBranchName Gets a unique branch name for a new patch branch
+// It will be in the form of <username>-patch-<num> where <num> is the first branch of this format
+// that doesn't already exist. If we exceed 1000 tries or an error is thrown, we just return "" so the user has to
+// type in the branch name themselves (will be an empty field)
+func getUniquePatchBranchName(ctx context.Context, prefixName string, repo *repo_model.Repository) string {
+ prefix := prefixName + "-patch-"
+ for i := 1; i <= 1000; i++ {
+ branchName := fmt.Sprintf("%s%d", prefix, i)
+ if exist, err := git_model.IsBranchExist(ctx, repo.ID, branchName); err != nil {
+ log.Error("getUniquePatchBranchName: %v", err)
+ return ""
+ } else if !exist {
+ return branchName
+ }
+ }
+ return ""
+}
+
+// getClosestParentWithFiles Recursively gets the closest path of parent in a tree that has files when a file in a tree is
+// deleted. It returns "" for the tree root if no parents other than the root have files.
+func getClosestParentWithFiles(gitRepo *git.Repository, branchName, originTreePath string) string {
+ var f func(treePath string, commit *git.Commit) string
+ f = func(treePath string, commit *git.Commit) string {
+ if treePath == "" || treePath == "." {
+ return ""
+ }
+ // see if the tree has entries
+ if tree, err := commit.SubTree(treePath); err != nil {
+ return f(path.Dir(treePath), commit) // failed to get the tree, going up a dir
+ } else if entries, err := tree.ListEntries(); err != nil || len(entries) == 0 {
+ return f(path.Dir(treePath), commit) // no files in this dir, going up a dir
+ }
+ return treePath
+ }
+ commit, err := gitRepo.GetBranchCommit(branchName) // must get the commit again to get the latest change
+ if err != nil {
+ log.Error("GetBranchCommit: %v", err)
+ return ""
+ }
+ return f(originTreePath, commit)
+}
+
+// getContextRepoEditorConfig returns the editorconfig JSON string for given treePath or "null"
+func getContextRepoEditorConfig(ctx *context_service.Context, treePath string) string {
+ ec, _, err := ctx.Repo.GetEditorconfig()
+ if err == nil {
+ def, err := ec.GetDefinitionForFilename(treePath)
+ if err == nil {
+ jsonStr, _ := json.Marshal(def)
+ return string(jsonStr)
+ }
+ }
+ return "null"
+}
+
+// getParentTreeFields returns list of parent tree names and corresponding tree paths based on given treePath.
+// eg: []{"a", "b", "c"}, []{"a", "a/b", "a/b/c"}
+// or: []{""}, []{""} for the root treePath
+func getParentTreeFields(treePath string) (treeNames, treePaths []string) {
+ treeNames = strings.Split(treePath, "/")
+ treePaths = make([]string, len(treeNames))
+ for i := range treeNames {
+ treePaths[i] = strings.Join(treeNames[:i+1], "/")
+ }
+ return treeNames, treePaths
+}
+
+// getUniqueRepositoryName Gets a unique repository name for a user
+// It will append a -<num> postfix if the name is already taken
+func getUniqueRepositoryName(ctx context.Context, ownerID int64, name string) string {
+ uniqueName := name
+ for i := 1; i < 1000; i++ {
+ _, err := repo_model.GetRepositoryByName(ctx, ownerID, uniqueName)
+ if err != nil || repo_model.IsErrRepoNotExist(err) {
+ return uniqueName
+ }
+ uniqueName = fmt.Sprintf("%s-%d", name, i)
+ i++
+ }
+ return ""
+}
+
+func editorPushBranchToForkedRepository(ctx context.Context, doer *user_model.User, baseRepo *repo_model.Repository, baseBranchName string, targetRepo *repo_model.Repository, targetBranchName string) error {
+ return git.Push(ctx, baseRepo.RepoPath(), git.PushOptions{
+ Remote: targetRepo.RepoPath(),
+ Branch: baseBranchName + ":" + targetBranchName,
+ Env: repo_module.PushingEnvironment(doer, targetRepo),
+ })
+}
diff --git a/routers/web/repo/fork.go b/routers/web/repo/fork.go
index 786b5d7e43..c2694e540f 100644
--- a/routers/web/repo/fork.go
+++ b/routers/web/repo/fork.go
@@ -37,7 +37,7 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
if forkRepo.IsEmpty {
log.Trace("Empty repository %-v", forkRepo)
- ctx.NotFound("getForkRepository", nil)
+ ctx.NotFound(nil)
return nil
}
@@ -91,12 +91,17 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
ctx.Data["CanForkToUser"] = canForkToUser
ctx.Data["Orgs"] = orgs
+ // TODO: this message should only be shown for the "current doer" when it is selected, just like the "new repo" page.
+ // msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", ctx.Doer.MaxCreationLimit())
+
if canForkToUser {
ctx.Data["ContextUser"] = ctx.Doer
+ ctx.Data["CanForkRepoInNewOwner"] = true
} else if len(orgs) > 0 {
ctx.Data["ContextUser"] = orgs[0]
+ ctx.Data["CanForkRepoInNewOwner"] = true
} else {
- ctx.Data["CanForkRepo"] = false
+ ctx.Data["CanForkRepoInNewOwner"] = false
ctx.Flash.Error(ctx.Tr("repo.fork_no_valid_owners"), true)
return nil
}
@@ -120,15 +125,6 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
// Fork render repository fork page
func Fork(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("new_fork")
-
- if ctx.Doer.CanForkRepo() {
- ctx.Data["CanForkRepo"] = true
- } else {
- maxCreationLimit := ctx.Doer.MaxCreationLimit()
- msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit)
- ctx.Flash.Error(msg, true)
- }
-
getForkRepository(ctx)
if ctx.Written() {
return
@@ -141,7 +137,6 @@ func Fork(ctx *context.Context) {
func ForkPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CreateRepoForm)
ctx.Data["Title"] = ctx.Tr("new_fork")
- ctx.Data["CanForkRepo"] = true
ctxUser := checkContextUser(ctx, form.UID)
if ctx.Written() {
@@ -156,7 +151,7 @@ func ForkPost(ctx *context.Context) {
ctx.Data["ContextUser"] = ctxUser
if ctx.HasError() {
- ctx.HTML(http.StatusOK, tplFork)
+ ctx.JSONError(ctx.GetErrMsg())
return
}
@@ -164,12 +159,12 @@ func ForkPost(ctx *context.Context) {
traverseParentRepo := forkRepo
for {
if !repository.CanUserForkBetweenOwners(ctxUser.ID, traverseParentRepo.OwnerID) {
- ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplFork, &form)
+ ctx.JSONError(ctx.Tr("repo.settings.new_owner_has_same_repo"))
return
}
repo := repo_model.GetForkedRepo(ctx, ctxUser.ID, traverseParentRepo.ID)
if repo != nil {
- ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
+ ctx.JSONRedirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
return
}
if !traverseParentRepo.IsFork {
@@ -189,49 +184,55 @@ func ForkPost(ctx *context.Context) {
ctx.ServerError("CanCreateOrgRepo", err)
return
} else if !isAllowedToFork {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
- repo, err := repo_service.ForkRepository(ctx, ctx.Doer, ctxUser, repo_service.ForkRepoOptions{
+ repo := ForkRepoTo(ctx, ctxUser, repo_service.ForkRepoOptions{
BaseRepo: forkRepo,
Name: form.RepoName,
Description: form.Description,
SingleBranch: form.ForkSingleBranch,
})
+ if ctx.Written() {
+ return
+ }
+ ctx.JSONRedirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
+}
+
+func ForkRepoTo(ctx *context.Context, owner *user_model.User, forkOpts repo_service.ForkRepoOptions) *repo_model.Repository {
+ repo, err := repo_service.ForkRepository(ctx, ctx.Doer, owner, forkOpts)
if err != nil {
ctx.Data["Err_RepoName"] = true
switch {
case repo_model.IsErrReachLimitOfRepo(err):
- maxCreationLimit := ctxUser.MaxCreationLimit()
+ maxCreationLimit := owner.MaxCreationLimit()
msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit)
- ctx.RenderWithErr(msg, tplFork, &form)
+ ctx.JSONError(msg)
case repo_model.IsErrRepoAlreadyExist(err):
- ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplFork, &form)
+ ctx.JSONError(ctx.Tr("repo.settings.new_owner_has_same_repo"))
case repo_model.IsErrRepoFilesAlreadyExist(err):
switch {
case ctx.IsUserSiteAdmin() || (setting.Repository.AllowAdoptionOfUnadoptedRepositories && setting.Repository.AllowDeleteOfUnadoptedRepositories):
- ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt_or_delete"), tplFork, form)
+ ctx.JSONError(ctx.Tr("form.repository_files_already_exist.adopt_or_delete"))
case setting.Repository.AllowAdoptionOfUnadoptedRepositories:
- ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt"), tplFork, form)
+ ctx.JSONError(ctx.Tr("form.repository_files_already_exist.adopt"))
case setting.Repository.AllowDeleteOfUnadoptedRepositories:
- ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.delete"), tplFork, form)
+ ctx.JSONError(ctx.Tr("form.repository_files_already_exist.delete"))
default:
- ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist"), tplFork, form)
+ ctx.JSONError(ctx.Tr("form.repository_files_already_exist"))
}
case db.IsErrNameReserved(err):
- ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplFork, &form)
+ ctx.JSONError(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name))
case db.IsErrNamePatternNotAllowed(err):
- ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplFork, &form)
+ ctx.JSONError(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern))
case errors.Is(err, user_model.ErrBlockedUser):
- ctx.RenderWithErr(ctx.Tr("repo.fork.blocked_user"), tplFork, form)
+ ctx.JSONError(ctx.Tr("repo.fork.blocked_user"))
default:
ctx.ServerError("ForkPost", err)
}
- return
+ return nil
}
-
- log.Trace("Repository forked[%d]: %s/%s", forkRepo.ID, ctxUser.Name, repo.Name)
- ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
+ return repo
}
diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go
index 6b2a7fd076..deb3ae4f3a 100644
--- a/routers/web/repo/githttp.go
+++ b/routers/web/repo/githttp.go
@@ -13,6 +13,7 @@ import (
"os"
"path/filepath"
"regexp"
+ "slices"
"strconv"
"strings"
"sync"
@@ -29,7 +30,6 @@ import (
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context"
repo_service "code.gitea.io/gitea/services/repository"
@@ -78,7 +78,7 @@ func httpBase(ctx *context.Context) *serviceHandler {
strings.HasSuffix(ctx.Req.URL.Path, "git-upload-archive") {
isPull = true
} else {
- isPull = ctx.Req.Method == "GET"
+ isPull = ctx.Req.Method == http.MethodHead || ctx.Req.Method == http.MethodGet
}
var accessMode perm.AccessMode
@@ -127,7 +127,7 @@ func httpBase(ctx *context.Context) *serviceHandler {
// Only public pull don't need auth.
isPublicPull := repoExist && !repo.IsPrivate && isPull
var (
- askAuth = !isPublicPull || setting.Service.RequireSignInView
+ askAuth = !isPublicPull || setting.Service.RequireSignInViewStrict
environ []string
)
@@ -147,7 +147,7 @@ func httpBase(ctx *context.Context) *serviceHandler {
if !ctx.IsSigned {
// TODO: support digit auth - which would be Authorization header with digit
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea"`)
- ctx.Error(http.StatusUnauthorized)
+ ctx.HTTPError(http.StatusUnauthorized)
return nil
}
@@ -303,24 +303,19 @@ var (
func dummyInfoRefs(ctx *context.Context) {
infoRefsOnce.Do(func() {
- tmpDir, err := os.MkdirTemp(os.TempDir(), "gitea-info-refs-cache")
+ tmpDir, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("gitea-info-refs-cache")
if err != nil {
log.Error("Failed to create temp dir for git-receive-pack cache: %v", err)
return
}
-
- defer func() {
- if err := util.RemoveAll(tmpDir); err != nil {
- log.Error("RemoveAll: %v", err)
- }
- }()
+ defer cleanup()
if err := git.InitRepository(ctx, tmpDir, true, git.Sha1ObjectFormat.Name()); err != nil {
log.Error("Failed to init bare repo for git-receive-pack cache: %v", err)
return
}
- refs, _, err := git.NewCommand(ctx, "receive-pack", "--stateless-rpc", "--advertise-refs", ".").RunStdBytes(&git.RunOpts{Dir: tmpDir})
+ refs, _, err := git.NewCommand("receive-pack", "--stateless-rpc", "--advertise-refs", ".").RunStdBytes(ctx, &git.RunOpts{Dir: tmpDir})
if err != nil {
log.Error(fmt.Sprintf("%v - %s", err, string(refs)))
}
@@ -360,8 +355,8 @@ func setHeaderNoCache(ctx *context.Context) {
func setHeaderCacheForever(ctx *context.Context) {
now := time.Now().Unix()
expires := now + 31536000
- ctx.Resp.Header().Set("Date", fmt.Sprintf("%d", now))
- ctx.Resp.Header().Set("Expires", fmt.Sprintf("%d", expires))
+ ctx.Resp.Header().Set("Date", strconv.FormatInt(now, 10))
+ ctx.Resp.Header().Set("Expires", strconv.FormatInt(expires, 10))
ctx.Resp.Header().Set("Cache-Control", "public, max-age=31536000")
}
@@ -369,12 +364,7 @@ func containsParentDirectorySeparator(v string) bool {
if !strings.Contains(v, "..") {
return false
}
- for _, ent := range strings.FieldsFunc(v, isSlashRune) {
- if ent == ".." {
- return true
- }
- }
- return false
+ return slices.Contains(strings.FieldsFunc(v, isSlashRune), "..")
}
func isSlashRune(r rune) bool { return r == '/' || r == '\\' }
@@ -394,7 +384,7 @@ func (h *serviceHandler) sendFile(ctx *context.Context, contentType, file string
}
ctx.Resp.Header().Set("Content-Type", contentType)
- ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", fi.Size()))
+ ctx.Resp.Header().Set("Content-Length", strconv.FormatInt(fi.Size(), 10))
// http.TimeFormat required a UTC time, refer to https://pkg.go.dev/net/http#TimeFormat
ctx.Resp.Header().Set("Last-Modified", fi.ModTime().UTC().Format(http.TimeFormat))
http.ServeFile(ctx.Resp, ctx.Req, reqFile)
@@ -403,12 +393,12 @@ func (h *serviceHandler) sendFile(ctx *context.Context, contentType, file string
// one or more key=value pairs separated by colons
var safeGitProtocolHeader = regexp.MustCompile(`^[0-9a-zA-Z]+=[0-9a-zA-Z]+(:[0-9a-zA-Z]+=[0-9a-zA-Z]+)*$`)
-func prepareGitCmdWithAllowedService(ctx *context.Context, service string) (*git.Command, error) {
+func prepareGitCmdWithAllowedService(service string) (*git.Command, error) {
if service == "receive-pack" {
- return git.NewCommand(ctx, "receive-pack"), nil
+ return git.NewCommand("receive-pack"), nil
}
if service == "upload-pack" {
- return git.NewCommand(ctx, "upload-pack"), nil
+ return git.NewCommand("upload-pack"), nil
}
return nil, fmt.Errorf("service %q is not allowed", service)
@@ -428,7 +418,7 @@ func serviceRPC(ctx *context.Context, h *serviceHandler, service string) {
return
}
- cmd, err := prepareGitCmdWithAllowedService(ctx, service)
+ cmd, err := prepareGitCmdWithAllowedService(service)
if err != nil {
log.Error("Failed to prepareGitCmdWithService: %v", err)
ctx.Resp.WriteHeader(http.StatusUnauthorized)
@@ -458,7 +448,7 @@ func serviceRPC(ctx *context.Context, h *serviceHandler, service string) {
var stderr bytes.Buffer
cmd.AddArguments("--stateless-rpc").AddDynamicArguments(h.getRepoDir())
- if err := cmd.Run(&git.RunOpts{
+ if err := cmd.Run(ctx, &git.RunOpts{
Dir: h.getRepoDir(),
Env: append(os.Environ(), h.environ...),
Stdout: ctx.Resp,
@@ -498,7 +488,7 @@ func getServiceType(ctx *context.Context) string {
}
func updateServerInfo(ctx gocontext.Context, dir string) []byte {
- out, _, err := git.NewCommand(ctx, "update-server-info").RunStdBytes(&git.RunOpts{Dir: dir})
+ out, _, err := git.NewCommand("update-server-info").RunStdBytes(ctx, &git.RunOpts{Dir: dir})
if err != nil {
log.Error(fmt.Sprintf("%v - %s", err, string(out)))
}
@@ -521,14 +511,14 @@ func GetInfoRefs(ctx *context.Context) {
}
setHeaderNoCache(ctx)
service := getServiceType(ctx)
- cmd, err := prepareGitCmdWithAllowedService(ctx, service)
+ cmd, err := prepareGitCmdWithAllowedService(service)
if err == nil {
if protocol := ctx.Req.Header.Get("Git-Protocol"); protocol != "" && safeGitProtocolHeader.MatchString(protocol) {
h.environ = append(h.environ, "GIT_PROTOCOL="+protocol)
}
h.environ = append(os.Environ(), h.environ...)
- refs, _, err := cmd.AddArguments("--stateless-rpc", "--advertise-refs", ".").RunStdBytes(&git.RunOpts{Env: h.environ, Dir: h.getRepoDir()})
+ refs, _, err := cmd.AddArguments("--stateless-rpc", "--advertise-refs", ".").RunStdBytes(ctx, &git.RunOpts{Env: h.environ, Dir: h.getRepoDir()})
if err != nil {
log.Error(fmt.Sprintf("%v - %s", err, string(refs)))
}
diff --git a/routers/web/repo/githttp_test.go b/routers/web/repo/githttp_test.go
index 5ba8de3d63..0164b11f66 100644
--- a/routers/web/repo/githttp_test.go
+++ b/routers/web/repo/githttp_test.go
@@ -37,6 +37,6 @@ func TestContainsParentDirectorySeparator(t *testing.T) {
}
for i := range tests {
- assert.EqualValues(t, tests[i].b, containsParentDirectorySeparator(tests[i].v))
+ assert.Equal(t, tests[i].b, containsParentDirectorySeparator(tests[i].v))
}
}
diff --git a/routers/web/repo/helper.go b/routers/web/repo/helper.go
index ed6216fa5c..7b92cba0fd 100644
--- a/routers/web/repo/helper.go
+++ b/routers/web/repo/helper.go
@@ -4,26 +4,15 @@
package repo
import (
- "net/url"
-
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/services/context"
)
func HandleGitError(ctx *context.Context, msg string, err error) {
if git.IsErrNotExist(err) {
- refType := ""
- switch {
- case ctx.Repo.IsViewBranch:
- refType = "branch"
- case ctx.Repo.IsViewTag:
- refType = "tag"
- case ctx.Repo.IsViewCommit:
- refType = "commit"
- }
- ctx.Data["NotFoundPrompt"] = ctx.Locale.Tr("repo.tree_path_not_found_"+refType, ctx.Repo.TreePath, url.PathEscape(ctx.Repo.RefName))
- ctx.Data["NotFoundGoBackURL"] = ctx.Repo.RepoLink + "/src/" + refType + "/" + url.PathEscape(ctx.Repo.RefName)
- ctx.NotFound(msg, err)
+ ctx.Data["NotFoundPrompt"] = ctx.Locale.Tr("repo.tree_path_not_found", ctx.Repo.TreePath, ctx.Repo.RefTypeNameSubURL())
+ ctx.Data["NotFoundGoBackURL"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
+ ctx.NotFound(err)
} else {
ctx.ServerError(msg, err)
}
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index f150897a2d..54b7e5df2a 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -43,7 +43,8 @@ const (
tplIssueChoose templates.TplName = "repo/issue/choose"
tplIssueView templates.TplName = "repo/issue/view"
- tplReactions templates.TplName = "repo/issue/view_content/reactions"
+ tplPullMergeBox templates.TplName = "repo/issue/view_content/pull_merge_box"
+ tplReactions templates.TplName = "repo/issue/view_content/reactions"
issueTemplateKey = "IssueTemplate"
issueTemplateTitleKey = "IssueTemplateTitle"
@@ -91,7 +92,7 @@ func MustAllowUserComment(ctx *context.Context) {
func MustEnableIssues(ctx *context.Context) {
if !ctx.Repo.CanRead(unit.TypeIssues) &&
!ctx.Repo.CanRead(unit.TypeExternalTracker) {
- ctx.NotFound("MustEnableIssues", nil)
+ ctx.NotFound(nil)
return
}
@@ -105,7 +106,7 @@ func MustEnableIssues(ctx *context.Context) {
// MustAllowPulls check if repository enable pull requests and user have right to do that
func MustAllowPulls(ctx *context.Context) {
if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(unit.TypePullRequests) {
- ctx.NotFound("MustAllowPulls", nil)
+ ctx.NotFound(nil)
return
}
@@ -201,7 +202,7 @@ func GetActionIssue(ctx *context.Context) *issues_model.Issue {
func checkIssueRights(ctx *context.Context, issue *issues_model.Issue) {
if issue.IsPull && !ctx.Repo.CanRead(unit.TypePullRequests) ||
!issue.IsPull && !ctx.Repo.CanRead(unit.TypeIssues) {
- ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil)
+ ctx.NotFound(nil)
}
}
@@ -211,7 +212,7 @@ func getActionIssues(ctx *context.Context) issues_model.IssueList {
return nil
}
issueIDs := make([]int64, 0, 10)
- for _, stringIssueID := range strings.Split(commaSeparatedIssueIDs, ",") {
+ for stringIssueID := range strings.SplitSeq(commaSeparatedIssueIDs, ",") {
issueID, err := strconv.ParseInt(stringIssueID, 10, 64)
if err != nil {
ctx.ServerError("ParseInt", err)
@@ -229,11 +230,11 @@ func getActionIssues(ctx *context.Context) issues_model.IssueList {
prUnitEnabled := ctx.Repo.CanRead(unit.TypePullRequests)
for _, issue := range issues {
if issue.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound("some issue's RepoID is incorrect", errors.New("some issue's RepoID is incorrect"))
+ ctx.NotFound(errors.New("some issue's RepoID is incorrect"))
return nil
}
if issue.IsPull && !prUnitEnabled || !issue.IsPull && !issueUnitEnabled {
- ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil)
+ ctx.NotFound(nil)
return nil
}
if err = issue.LoadAttributes(ctx); err != nil {
@@ -249,9 +250,9 @@ func GetIssueInfo(ctx *context.Context) {
issue, err := issues_model.GetIssueWithAttrsByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "GetIssueByIndex", err.Error())
}
return
}
@@ -259,13 +260,13 @@ func GetIssueInfo(ctx *context.Context) {
if issue.IsPull {
// Need to check if Pulls are enabled and we can read Pulls
if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(unit.TypePullRequests) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
} else {
// Need to check if Issues are enabled and we can read Issues
if !ctx.Repo.CanRead(unit.TypeIssues) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
}
@@ -284,13 +285,13 @@ func UpdateIssueTitle(ctx *context.Context) {
}
if !ctx.IsSigned || (!issue.IsPoster(ctx.Doer.ID) && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
title := ctx.FormTrim("title")
if len(title) == 0 {
- ctx.Error(http.StatusNoContent)
+ ctx.HTTPError(http.StatusNoContent)
return
}
@@ -312,7 +313,7 @@ func UpdateIssueRef(ctx *context.Context) {
}
if !ctx.IsSigned || (!issue.IsPoster(ctx.Doer.ID) && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) || issue.IsPull {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
@@ -336,7 +337,7 @@ func UpdateIssueContent(ctx *context.Context) {
}
if !ctx.IsSigned || (ctx.Doer.ID != issue.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
@@ -363,7 +364,9 @@ func UpdateIssueContent(ctx *context.Context) {
}
}
- rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository, renderhelper.RepoCommentOptions{
+ FootnoteContextID: "0",
+ })
content, err := markdown.RenderString(rctx, issue.Content)
if err != nil {
ctx.ServerError("RenderString", err)
@@ -382,21 +385,21 @@ func UpdateIssueDeadline(ctx *context.Context) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound("GetIssueByIndex", err)
+ ctx.NotFound(err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "GetIssueByIndex", err.Error())
}
return
}
if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
- ctx.Error(http.StatusForbidden, "", "Not repo writer")
+ ctx.HTTPError(http.StatusForbidden, "", "Not repo writer")
return
}
deadlineUnix, _ := common.ParseDeadlineDateToEndOfDay(ctx.FormString("deadline"))
if err := issues_model.UpdateIssueDeadline(ctx, issue, deadlineUnix, ctx.Doer); err != nil {
- ctx.Error(http.StatusInternalServerError, "UpdateIssueDeadline", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "UpdateIssueDeadline", err.Error())
return
}
@@ -417,6 +420,16 @@ func UpdateIssueMilestone(ctx *context.Context) {
continue
}
issue.MilestoneID = milestoneID
+ if milestoneID > 0 {
+ var err error
+ issue.Milestone, err = issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, milestoneID)
+ if err != nil {
+ ctx.ServerError("GetMilestoneByRepoID", err)
+ return
+ }
+ } else {
+ issue.Milestone = nil
+ }
if err := issue_service.ChangeMilestoneAssign(ctx, issue, ctx.Doer, oldMilestoneID); err != nil {
ctx.ServerError("ChangeMilestoneAssign", err)
return
@@ -497,7 +510,7 @@ func ChangeIssueReaction(ctx *context.Context) {
}
}
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
@@ -540,7 +553,7 @@ func ChangeIssueReaction(ctx *context.Context) {
log.Trace("Reaction for issue removed: %d/%d", ctx.Repo.Repository.ID, issue.ID)
default:
- ctx.NotFound(fmt.Sprintf("Unknown action %s", ctx.PathParam("action")), nil)
+ ctx.NotFound(nil)
return
}
diff --git a/routers/web/repo/issue_comment.go b/routers/web/repo/issue_comment.go
index 8564c613de..c2a7f6b682 100644
--- a/routers/web/repo/issue_comment.go
+++ b/routers/web/repo/issue_comment.go
@@ -8,11 +8,13 @@ import (
"fmt"
"html/template"
"net/http"
+ "strconv"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/renderhelper"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup/markdown"
repo_module "code.gitea.io/gitea/modules/repository"
@@ -53,7 +55,7 @@ func NewComment(ctx *context.Context) {
}
}
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
@@ -95,7 +97,7 @@ func NewComment(ctx *context.Context) {
// Regenerate patch and test conflict.
if pr == nil {
issue.PullRequest.HeadCommitID = ""
- pull_service.AddToTaskQueue(ctx, issue.PullRequest)
+ pull_service.StartPullRequestCheckImmediately(ctx, issue.PullRequest)
}
// check whether the ref of PR <refs/pulls/pr_index/head> in base repo is consistent with the head commit of head branch in the head repo
@@ -117,7 +119,7 @@ func NewComment(ctx *context.Context) {
ctx.ServerError("Unable to load head repo", err)
return
}
- if ok := git.IsBranchExist(ctx, pull.HeadRepo.RepoPath(), pull.BaseBranch); !ok {
+ if ok := gitrepo.IsBranchExist(ctx, pull.HeadRepo, pull.BaseBranch); !ok {
// todo localize
ctx.JSONError("The origin branch is delete, cannot reopen.")
return
@@ -224,35 +226,42 @@ func UpdateCommentContent(ctx *context.Context) {
}
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
+ ctx.NotFound(issues_model.ErrCommentNotExist{})
return
}
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
if !comment.Type.HasContentSupport() {
- ctx.Error(http.StatusNoContent)
+ ctx.HTTPError(http.StatusNoContent)
return
}
- oldContent := comment.Content
newContent := ctx.FormString("content")
contentVersion := ctx.FormInt("content_version")
+ if contentVersion != comment.ContentVersion {
+ ctx.JSONError(ctx.Tr("repo.comments.edit.already_changed"))
+ return
+ }
- // allow to save empty content
- comment.Content = newContent
- if err = issue_service.UpdateComment(ctx, comment, contentVersion, ctx.Doer, oldContent); err != nil {
- if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_user"))
- } else if errors.Is(err, issues_model.ErrCommentAlreadyChanged) {
- ctx.JSONError(ctx.Tr("repo.comments.edit.already_changed"))
- } else {
- ctx.ServerError("UpdateComment", err)
+ if newContent != comment.Content {
+ // allow to save empty content
+ oldContent := comment.Content
+ comment.Content = newContent
+
+ if err = issue_service.UpdateComment(ctx, comment, contentVersion, ctx.Doer, oldContent); err != nil {
+ if errors.Is(err, user_model.ErrBlockedUser) {
+ ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_user"))
+ } else if errors.Is(err, issues_model.ErrCommentAlreadyChanged) {
+ ctx.JSONError(ctx.Tr("repo.comments.edit.already_changed"))
+ } else {
+ ctx.ServerError("UpdateComment", err)
+ }
+ return
}
- return
}
if err := comment.LoadAttachments(ctx); err != nil {
@@ -270,7 +279,9 @@ func UpdateCommentContent(ctx *context.Context) {
var renderedContent template.HTML
if comment.Content != "" {
- rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository, renderhelper.RepoCommentOptions{
+ FootnoteContextID: strconv.FormatInt(comment.ID, 10),
+ })
renderedContent, err = markdown.RenderString(rctx, comment.Content)
if err != nil {
ctx.ServerError("RenderString", err)
@@ -302,15 +313,15 @@ func DeleteComment(ctx *context.Context) {
}
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
+ ctx.NotFound(issues_model.ErrCommentNotExist{})
return
}
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
} else if !comment.Type.HasContentSupport() {
- ctx.Error(http.StatusNoContent)
+ ctx.HTTPError(http.StatusNoContent)
return
}
@@ -337,7 +348,7 @@ func ChangeCommentReaction(ctx *context.Context) {
}
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
+ ctx.NotFound(issues_model.ErrCommentNotExist{})
return
}
@@ -360,12 +371,12 @@ func ChangeCommentReaction(ctx *context.Context) {
}
}
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
if !comment.Type.HasContentSupport() {
- ctx.Error(http.StatusNoContent)
+ ctx.HTTPError(http.StatusNoContent)
return
}
@@ -403,7 +414,7 @@ func ChangeCommentReaction(ctx *context.Context) {
log.Trace("Reaction for comment removed: %d/%d/%d", ctx.Repo.Repository.ID, comment.Issue.ID, comment.ID)
default:
- ctx.NotFound(fmt.Sprintf("Unknown action %s", ctx.PathParam("action")), nil)
+ ctx.NotFound(nil)
return
}
@@ -442,12 +453,12 @@ func GetCommentAttachments(ctx *context.Context) {
}
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
+ ctx.NotFound(issues_model.ErrCommentNotExist{})
return
}
if !ctx.Repo.Permission.CanReadIssuesOrPulls(comment.Issue.IsPull) {
- ctx.NotFound("CanReadIssuesOrPulls", issues_model.ErrCommentNotExist{})
+ ctx.NotFound(issues_model.ErrCommentNotExist{})
return
}
diff --git a/routers/web/repo/issue_content_history.go b/routers/web/repo/issue_content_history.go
index 13c6d89b6e..3602f4ec8a 100644
--- a/routers/web/repo/issue_content_history.go
+++ b/routers/web/repo/issue_content_history.go
@@ -157,15 +157,16 @@ func GetContentHistoryDetail(ctx *context.Context) {
diffHTMLBuf := bytes.Buffer{}
diffHTMLBuf.WriteString("<pre class='chroma'>")
for _, it := range diff {
- if it.Type == diffmatchpatch.DiffInsert {
+ switch it.Type {
+ case diffmatchpatch.DiffInsert:
diffHTMLBuf.WriteString("<span class='gi'>")
diffHTMLBuf.WriteString(html.EscapeString(it.Text))
diffHTMLBuf.WriteString("</span>")
- } else if it.Type == diffmatchpatch.DiffDelete {
+ case diffmatchpatch.DiffDelete:
diffHTMLBuf.WriteString("<span class='gd'>")
diffHTMLBuf.WriteString(html.EscapeString(it.Text))
diffHTMLBuf.WriteString("</span>")
- } else {
+ default:
diffHTMLBuf.WriteString(html.EscapeString(it.Text))
}
}
@@ -186,7 +187,7 @@ func SoftDeleteContentHistory(ctx *context.Context) {
return
}
if ctx.Doer == nil {
- ctx.NotFound("Require SignIn", nil)
+ ctx.NotFound(nil)
return
}
@@ -202,12 +203,12 @@ func SoftDeleteContentHistory(ctx *context.Context) {
return
}
if history.IssueID != issue.ID {
- ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
+ ctx.NotFound(issues_model.ErrCommentNotExist{})
return
}
if commentID != 0 {
if history.CommentID != commentID {
- ctx.NotFound("CompareCommentID", issues_model.ErrCommentNotExist{})
+ ctx.NotFound(issues_model.ErrCommentNotExist{})
return
}
@@ -216,7 +217,7 @@ func SoftDeleteContentHistory(ctx *context.Context) {
return
}
if comment.IssueID != issue.ID {
- ctx.NotFound("CompareIssueID", issues_model.ErrCommentNotExist{})
+ ctx.NotFound(issues_model.ErrCommentNotExist{})
return
}
}
diff --git a/routers/web/repo/issue_dependency.go b/routers/web/repo/issue_dependency.go
index f1d133edb0..73298958c0 100644
--- a/routers/web/repo/issue_dependency.go
+++ b/routers/web/repo/issue_dependency.go
@@ -23,7 +23,7 @@ func AddDependency(ctx *context.Context) {
// Check if the Repo is allowed to have dependencies
if !ctx.Repo.CanCreateIssueDependencies(ctx, ctx.Doer, issue.IsPull) {
- ctx.Error(http.StatusForbidden, "CanCreateIssueDependencies")
+ ctx.HTTPError(http.StatusForbidden, "CanCreateIssueDependencies")
return
}
@@ -97,7 +97,7 @@ func RemoveDependency(ctx *context.Context) {
// Check if the Repo is allowed to have dependencies
if !ctx.Repo.CanCreateIssueDependencies(ctx, ctx.Doer, issue.IsPull) {
- ctx.Error(http.StatusForbidden, "CanCreateIssueDependencies")
+ ctx.HTTPError(http.StatusForbidden, "CanCreateIssueDependencies")
return
}
@@ -109,7 +109,7 @@ func RemoveDependency(ctx *context.Context) {
}
// Dependency Type
- depTypeStr := ctx.Req.PostForm.Get("dependencyType")
+ depTypeStr := ctx.Req.PostFormValue("dependencyType")
var depType issues_model.DependencyType
@@ -119,7 +119,7 @@ func RemoveDependency(ctx *context.Context) {
case "blocking":
depType = issues_model.DependencyTypeBlocking
default:
- ctx.Error(http.StatusBadRequest, "GetDependecyType")
+ ctx.HTTPError(http.StatusBadRequest, "GetDependecyType")
return
}
diff --git a/routers/web/repo/issue_label.go b/routers/web/repo/issue_label.go
index 5ef6b09faa..72a316e98d 100644
--- a/routers/web/repo/issue_label.go
+++ b/routers/web/repo/issue_label.go
@@ -4,6 +4,7 @@
package repo
import (
+ "errors"
"net/http"
"code.gitea.io/gitea/models/db"
@@ -13,7 +14,9 @@ import (
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ shared_label "code.gitea.io/gitea/routers/web/shared/label"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
issue_service "code.gitea.io/gitea/services/issue"
@@ -100,54 +103,53 @@ func RetrieveLabelsForList(ctx *context.Context) {
// NewLabel create new label for repository
func NewLabel(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.CreateLabelForm)
- ctx.Data["Title"] = ctx.Tr("repo.labels")
- ctx.Data["PageIsLabels"] = true
-
- if ctx.HasError() {
- ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
- ctx.Redirect(ctx.Repo.RepoLink + "/labels")
+ form := shared_label.GetLabelEditForm(ctx)
+ if ctx.Written() {
return
}
l := &issues_model.Label{
- RepoID: ctx.Repo.Repository.ID,
- Name: form.Title,
- Exclusive: form.Exclusive,
- Description: form.Description,
- Color: form.Color,
+ RepoID: ctx.Repo.Repository.ID,
+ Name: form.Title,
+ Exclusive: form.Exclusive,
+ ExclusiveOrder: form.ExclusiveOrder,
+ Description: form.Description,
+ Color: form.Color,
}
if err := issues_model.NewLabel(ctx, l); err != nil {
ctx.ServerError("NewLabel", err)
return
}
- ctx.Redirect(ctx.Repo.RepoLink + "/labels")
+ ctx.JSONRedirect(ctx.Repo.RepoLink + "/labels")
}
// UpdateLabel update a label's name and color
func UpdateLabel(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.CreateLabelForm)
+ form := shared_label.GetLabelEditForm(ctx)
+ if ctx.Written() {
+ return
+ }
+
l, err := issues_model.GetLabelInRepoByID(ctx, ctx.Repo.Repository.ID, form.ID)
- if err != nil {
- switch {
- case issues_model.IsErrRepoLabelNotExist(err):
- ctx.Error(http.StatusNotFound)
- default:
- ctx.ServerError("UpdateLabel", err)
- }
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.JSONErrorNotFound()
+ return
+ } else if err != nil {
+ ctx.ServerError("GetLabelInRepoByID", err)
return
}
+
l.Name = form.Title
l.Exclusive = form.Exclusive
+ l.ExclusiveOrder = form.ExclusiveOrder
l.Description = form.Description
l.Color = form.Color
-
l.SetArchived(form.IsArchived)
if err := issues_model.UpdateLabel(ctx, l); err != nil {
ctx.ServerError("UpdateLabel", err)
return
}
- ctx.Redirect(ctx.Repo.RepoLink + "/labels")
+ ctx.JSONRedirect(ctx.Repo.RepoLink + "/labels")
}
// DeleteLabel delete a label
@@ -180,7 +182,7 @@ func UpdateIssueLabel(ctx *context.Context) {
label, err := issues_model.GetLabelByID(ctx, ctx.FormInt64("id"))
if err != nil {
if issues_model.IsErrRepoLabelNotExist(err) {
- ctx.Error(http.StatusNotFound, "GetLabelByID")
+ ctx.HTTPError(http.StatusNotFound, "GetLabelByID")
} else {
ctx.ServerError("GetLabelByID", err)
}
@@ -221,7 +223,7 @@ func UpdateIssueLabel(ctx *context.Context) {
}
default:
log.Warn("Unrecognized action: %s", action)
- ctx.Error(http.StatusInternalServerError)
+ ctx.HTTPError(http.StatusInternalServerError)
return
}
diff --git a/routers/web/repo/issue_label_test.go b/routers/web/repo/issue_label_test.go
index 8a613e2c7e..f4eca26f8e 100644
--- a/routers/web/repo/issue_label_test.go
+++ b/routers/web/repo/issue_label_test.go
@@ -6,10 +6,12 @@ package repo
import (
"net/http"
"strconv"
+ "strings"
"testing"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/web"
@@ -19,26 +21,27 @@ import (
"github.com/stretchr/testify/assert"
)
-func int64SliceToCommaSeparated(a []int64) string {
- s := ""
- for i, n := range a {
- if i > 0 {
- s += ","
- }
- s += strconv.Itoa(int(n))
- }
- return s
+func TestIssueLabel(t *testing.T) {
+ unittest.PrepareTestEnv(t)
+ t.Run("RetrieveLabels", testRetrieveLabels)
+ t.Run("NewLabel", testNewLabel)
+ t.Run("NewLabelInvalidColor", testNewLabelInvalidColor)
+ t.Run("UpdateLabel", testUpdateLabel)
+ t.Run("UpdateLabelInvalidColor", testUpdateLabelInvalidColor)
+ t.Run("UpdateIssueLabelClear", testUpdateIssueLabelClear)
+ t.Run("UpdateIssueLabelToggle", testUpdateIssueLabelToggle)
+ t.Run("InitializeLabels", testInitializeLabels)
+ t.Run("DeleteLabel", testDeleteLabel)
}
-func TestInitializeLabels(t *testing.T) {
- unittest.PrepareTestEnv(t)
+func testInitializeLabels(t *testing.T) {
assert.NoError(t, repository.LoadRepoConfig())
ctx, _ := contexttest.MockContext(t, "user2/repo1/labels/initialize")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 2)
web.SetForm(ctx, &forms.InitializeLabelsForm{TemplateName: "Default"})
InitializeLabels(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &issues_model.Label{
RepoID: 2,
Name: "enhancement",
@@ -47,8 +50,7 @@ func TestInitializeLabels(t *testing.T) {
assert.Equal(t, "/user2/repo2/labels", test.RedirectURL(ctx.Resp))
}
-func TestRetrieveLabels(t *testing.T) {
- unittest.PrepareTestEnv(t)
+func testRetrieveLabels(t *testing.T) {
for _, testCase := range []struct {
RepoID int64
Sort string
@@ -68,15 +70,14 @@ func TestRetrieveLabels(t *testing.T) {
assert.True(t, ok)
if assert.Len(t, labels, len(testCase.ExpectedLabelIDs)) {
for i, label := range labels {
- assert.EqualValues(t, testCase.ExpectedLabelIDs[i], label.ID)
+ assert.Equal(t, testCase.ExpectedLabelIDs[i], label.ID)
}
}
}
}
-func TestNewLabel(t *testing.T) {
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user2/repo1/labels/edit")
+func testNewLabel(t *testing.T) {
+ ctx, respWriter := contexttest.MockContext(t, "user2/repo1/labels/edit")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
web.SetForm(ctx, &forms.CreateLabelForm{
@@ -84,17 +85,32 @@ func TestNewLabel(t *testing.T) {
Color: "#abcdef",
})
NewLabel(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &issues_model.Label{
Name: "newlabel",
Color: "#abcdef",
})
- assert.Equal(t, "/user2/repo1/labels", test.RedirectURL(ctx.Resp))
+ assert.Equal(t, "/user2/repo1/labels", test.RedirectURL(respWriter))
}
-func TestUpdateLabel(t *testing.T) {
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user2/repo1/labels/edit")
+func testNewLabelInvalidColor(t *testing.T) {
+ ctx, respWriter := contexttest.MockContext(t, "user2/repo1/labels/edit")
+ contexttest.LoadUser(t, ctx, 2)
+ contexttest.LoadRepo(t, ctx, 1)
+ web.SetForm(ctx, &forms.CreateLabelForm{
+ Title: "newlabel-x",
+ Color: "bad-label-code",
+ })
+ NewLabel(ctx)
+ assert.Equal(t, http.StatusBadRequest, ctx.Resp.WrittenStatus())
+ assert.Equal(t, "repo.issues.label_color_invalid", test.ParseJSONError(respWriter.Body.Bytes()).ErrorMessage)
+ unittest.AssertNotExistsBean(t, &issues_model.Label{
+ Name: "newlabel-x",
+ })
+}
+
+func testUpdateLabel(t *testing.T) {
+ ctx, respWriter := contexttest.MockContext(t, "user2/repo1/labels/edit")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
web.SetForm(ctx, &forms.CreateLabelForm{
@@ -104,43 +120,62 @@ func TestUpdateLabel(t *testing.T) {
IsArchived: true,
})
UpdateLabel(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &issues_model.Label{
ID: 2,
Name: "newnameforlabel",
Color: "#abcdef",
})
- assert.Equal(t, "/user2/repo1/labels", test.RedirectURL(ctx.Resp))
+ assert.Equal(t, "/user2/repo1/labels", test.RedirectURL(respWriter))
}
-func TestDeleteLabel(t *testing.T) {
- unittest.PrepareTestEnv(t)
+func testUpdateLabelInvalidColor(t *testing.T) {
+ ctx, respWriter := contexttest.MockContext(t, "user2/repo1/labels/edit")
+ contexttest.LoadUser(t, ctx, 2)
+ contexttest.LoadRepo(t, ctx, 1)
+ web.SetForm(ctx, &forms.CreateLabelForm{
+ ID: 1,
+ Title: "label1",
+ Color: "bad-label-code",
+ })
+
+ UpdateLabel(ctx)
+
+ assert.Equal(t, http.StatusBadRequest, ctx.Resp.WrittenStatus())
+ assert.Equal(t, "repo.issues.label_color_invalid", test.ParseJSONError(respWriter.Body.Bytes()).ErrorMessage)
+ unittest.AssertExistsAndLoadBean(t, &issues_model.Label{
+ ID: 1,
+ Name: "label1",
+ Color: "#abcdef",
+ })
+}
+
+func testDeleteLabel(t *testing.T) {
ctx, _ := contexttest.MockContext(t, "user2/repo1/labels/delete")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
ctx.Req.Form.Set("id", "2")
DeleteLabel(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
unittest.AssertNotExistsBean(t, &issues_model.Label{ID: 2})
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{LabelID: 2})
assert.EqualValues(t, ctx.Tr("repo.issues.label_deletion_success"), ctx.Flash.SuccessMsg)
}
-func TestUpdateIssueLabel_Clear(t *testing.T) {
- unittest.PrepareTestEnv(t)
+func testUpdateIssueLabelClear(t *testing.T) {
ctx, _ := contexttest.MockContext(t, "user2/repo1/issues/labels")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
ctx.Req.Form.Set("issue_ids", "1,3")
ctx.Req.Form.Set("action", "clear")
UpdateIssueLabel(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: 1})
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: 3})
unittest.CheckConsistencyFor(t, &issues_model.Label{})
}
-func TestUpdateIssueLabel_Toggle(t *testing.T) {
+func testUpdateIssueLabelToggle(t *testing.T) {
for _, testCase := range []struct {
Action string
IssueIDs []int64
@@ -156,11 +191,12 @@ func TestUpdateIssueLabel_Toggle(t *testing.T) {
ctx, _ := contexttest.MockContext(t, "user2/repo1/issues/labels")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
- ctx.Req.Form.Set("issue_ids", int64SliceToCommaSeparated(testCase.IssueIDs))
+
+ ctx.Req.Form.Set("issue_ids", strings.Join(base.Int64sToStrings(testCase.IssueIDs), ","))
ctx.Req.Form.Set("action", testCase.Action)
ctx.Req.Form.Set("id", strconv.Itoa(int(testCase.LabelID)))
UpdateIssueLabel(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
for _, issueID := range testCase.IssueIDs {
if testCase.ExpectedAdd {
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: testCase.LabelID})
diff --git a/routers/web/repo/issue_list.go b/routers/web/repo/issue_list.go
index 2f615a100e..b55f4bcc90 100644
--- a/routers/web/repo/issue_list.go
+++ b/routers/web/repo/issue_list.go
@@ -5,8 +5,10 @@ package repo
import (
"bytes"
- "fmt"
+ "maps"
"net/http"
+ "slices"
+ "sort"
"strconv"
"strings"
@@ -18,6 +20,7 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
+ db_indexer "code.gitea.io/gitea/modules/indexer/issues/db"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
@@ -30,14 +33,6 @@ import (
pull_service "code.gitea.io/gitea/services/pull"
)
-func issueIDsFromSearch(ctx *context.Context, keyword string, opts *issues_model.IssuesOptions) ([]int64, error) {
- ids, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts))
- if err != nil {
- return nil, fmt.Errorf("SearchIssues: %w", err)
- }
- return ids, nil
-}
-
func retrieveProjectsForIssueList(ctx *context.Context, repo *repo_model.Repository) {
ctx.Data["OpenProjects"], ctx.Data["ClosedProjects"] = retrieveProjectsInternal(ctx, repo)
}
@@ -46,7 +41,7 @@ func retrieveProjectsForIssueList(ctx *context.Context, repo *repo_model.Reposit
func SearchIssues(ctx *context.Context) {
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, err.Error())
+ ctx.HTTPError(http.StatusUnprocessableEntity, err.Error())
return
}
@@ -66,7 +61,7 @@ func SearchIssues(ctx *context.Context) {
)
{
// find repos user can access (for issue search)
- opts := &repo_model.SearchRepoOptions{
+ opts := repo_model.SearchRepoOptions{
Private: false,
AllPublic: true,
TopicOnly: false,
@@ -84,9 +79,9 @@ func SearchIssues(ctx *context.Context) {
owner, err := user_model.GetUserByName(ctx, ctx.FormString("owner"))
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusBadRequest, "Owner not found", err.Error())
+ ctx.HTTPError(http.StatusBadRequest, "Owner not found", err.Error())
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "GetUserByName", err.Error())
}
return
}
@@ -97,15 +92,15 @@ func SearchIssues(ctx *context.Context) {
}
if ctx.FormString("team") != "" {
if ctx.FormString("owner") == "" {
- ctx.Error(http.StatusBadRequest, "", "Owner organisation is required for filtering on team")
+ ctx.HTTPError(http.StatusBadRequest, "", "Owner organisation is required for filtering on team")
return
}
team, err := organization.GetTeam(ctx, opts.OwnerID, ctx.FormString("team"))
if err != nil {
if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusBadRequest, "Team not found", err.Error())
+ ctx.HTTPError(http.StatusBadRequest, "Team not found", err.Error())
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "GetUserByName", err.Error())
}
return
}
@@ -118,7 +113,7 @@ func SearchIssues(ctx *context.Context) {
}
repoIDs, _, err = repo_model.SearchRepositoryIDs(ctx, opts)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "SearchRepositoryIDs", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "SearchRepositoryIDs", err.Error())
return
}
if len(repoIDs) == 0 {
@@ -149,7 +144,7 @@ func SearchIssues(ctx *context.Context) {
}
includedAnyLabels, err = issues_model.GetLabelIDsByNames(ctx, includedLabelNames)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetLabelIDsByNames", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "GetLabelIDsByNames", err.Error())
return
}
}
@@ -163,7 +158,7 @@ func SearchIssues(ctx *context.Context) {
}
includedMilestones, err = issues_model.GetMilestoneIDsByNames(ctx, includedMilestoneNames)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "GetMilestoneIDsByNames", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "GetMilestoneIDsByNames", err.Error())
return
}
}
@@ -208,10 +203,10 @@ func SearchIssues(ctx *context.Context) {
if ctx.IsSigned {
ctxUserID := ctx.Doer.ID
if ctx.FormBool("created") {
- searchOpt.PosterID = optional.Some(ctxUserID)
+ searchOpt.PosterID = strconv.FormatInt(ctxUserID, 10)
}
if ctx.FormBool("assigned") {
- searchOpt.AssigneeID = optional.Some(ctxUserID)
+ searchOpt.AssigneeID = strconv.FormatInt(ctxUserID, 10)
}
if ctx.FormBool("mentioned") {
searchOpt.MentionID = optional.Some(ctxUserID)
@@ -230,12 +225,12 @@ func SearchIssues(ctx *context.Context) {
ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "SearchIssues", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "SearchIssues", err.Error())
return
}
issues, err := issues_model.GetIssuesByIDs(ctx, ids, true)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindIssuesByIDs", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "FindIssuesByIDs", err.Error())
return
}
@@ -251,12 +246,12 @@ func getUserIDForFilter(ctx *context.Context, queryName string) int64 {
user, err := user_model.GetUserByName(ctx, userName)
if user_model.IsErrUserNotExist(err) {
- ctx.NotFound("", err)
+ ctx.NotFound(err)
return 0
}
if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, err.Error())
return 0
}
@@ -269,7 +264,7 @@ func getUserIDForFilter(ctx *context.Context, queryName string) int64 {
func SearchRepoIssuesJSON(ctx *context.Context) {
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil {
- ctx.Error(http.StatusUnprocessableEntity, err.Error())
+ ctx.HTTPError(http.StatusUnprocessableEntity, err.Error())
return
}
@@ -299,7 +294,7 @@ func SearchRepoIssuesJSON(ctx *context.Context) {
continue
}
if !issues_model.IsErrMilestoneNotExist(err) {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, err.Error())
return
}
id, err := strconv.ParseInt(part[i], 10, 64)
@@ -314,7 +309,7 @@ func SearchRepoIssuesJSON(ctx *context.Context) {
if issues_model.IsErrMilestoneNotExist(err) {
continue
}
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, err.Error())
}
}
@@ -373,10 +368,10 @@ func SearchRepoIssuesJSON(ctx *context.Context) {
}
if createdByID > 0 {
- searchOpt.PosterID = optional.Some(createdByID)
+ searchOpt.PosterID = strconv.FormatInt(createdByID, 10)
}
if assignedByID > 0 {
- searchOpt.AssigneeID = optional.Some(assignedByID)
+ searchOpt.AssigneeID = strconv.FormatInt(assignedByID, 10)
}
if mentionedByID > 0 {
searchOpt.MentionID = optional.Some(mentionedByID)
@@ -384,12 +379,12 @@ func SearchRepoIssuesJSON(ctx *context.Context) {
ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "SearchIssues", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "SearchIssues", err.Error())
return
}
issues, err := issues_model.GetIssuesByIDs(ctx, ids, true)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "FindIssuesByIDs", err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, "FindIssuesByIDs", err.Error())
return
}
@@ -459,6 +454,19 @@ func UpdateIssueStatus(ctx *context.Context) {
ctx.JSONOK()
}
+func prepareIssueFilterExclusiveOrderScopes(ctx *context.Context, allLabels []*issues_model.Label) {
+ scopeSet := make(map[string]bool)
+ for _, label := range allLabels {
+ scope := label.ExclusiveScope()
+ if len(scope) > 0 && label.ExclusiveOrder > 0 {
+ scopeSet[scope] = true
+ }
+ }
+ scopes := slices.Collect(maps.Keys(scopeSet))
+ sort.Strings(scopes)
+ ctx.Data["ExclusiveLabelScopes"] = scopes
+}
+
func renderMilestones(ctx *context.Context) {
// Get milestones
milestones, err := db.Find[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{
@@ -481,7 +489,7 @@ func renderMilestones(ctx *context.Context) {
ctx.Data["ClosedMilestones"] = closedMilestones
}
-func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption optional.Option[bool]) {
+func prepareIssueFilterAndList(ctx *context.Context, milestoneID, projectID int64, isPullOption optional.Option[bool]) {
var err error
viewType := ctx.FormString("type")
sortType := ctx.FormString("sort")
@@ -490,7 +498,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
viewType = "all"
}
- assigneeID := ctx.FormInt64("assignee") // TODO: use "optional" but not 0 in the future
+ assigneeID := ctx.FormString("assignee")
posterUsername := ctx.FormString("poster")
posterUserID := shared_user.GetFilterUserIDByName(ctx, posterUsername)
var mentionedID, reviewRequestedID, reviewedID int64
@@ -498,11 +506,11 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
if ctx.IsSigned {
switch viewType {
case "created_by":
- posterUserID = optional.Some(ctx.Doer.ID)
+ posterUserID = strconv.FormatInt(ctx.Doer.ID, 10)
case "mentioned":
mentionedID = ctx.Doer.ID
case "assigned":
- assigneeID = ctx.Doer.ID
+ assigneeID = strconv.FormatInt(ctx.Doer.ID, 10)
case "review_requested":
reviewRequestedID = ctx.Doer.ID
case "reviewed_by":
@@ -521,18 +529,21 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
mileIDs = []int64{milestoneID}
}
- labelIDs := issue.PrepareFilterIssueLabels(ctx, repo.ID, ctx.Repo.Owner)
+ preparedLabelFilter := issue.PrepareFilterIssueLabels(ctx, repo.ID, ctx.Repo.Owner)
if ctx.Written() {
return
}
+ prepareIssueFilterExclusiveOrderScopes(ctx, preparedLabelFilter.AllLabels)
+
+ var keywordMatchedIssueIDs []int64
var issueStats *issues_model.IssueStats
statsOpts := &issues_model.IssuesOptions{
RepoIDs: []int64{repo.ID},
- LabelIDs: labelIDs,
+ LabelIDs: preparedLabelFilter.SelectedLabelIDs,
MilestoneIDs: mileIDs,
ProjectID: projectID,
- AssigneeID: optional.Some(assigneeID),
+ AssigneeID: assigneeID,
MentionedID: mentionedID,
PosterID: posterUserID,
ReviewRequestedID: reviewRequestedID,
@@ -541,7 +552,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
IssueIDs: nil,
}
if keyword != "" {
- allIssueIDs, err := issueIDsFromSearch(ctx, keyword, statsOpts)
+ keywordMatchedIssueIDs, _, err = issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, statsOpts))
if err != nil {
if issue_indexer.IsAvailable(ctx) {
ctx.ServerError("issueIDsFromSearch", err)
@@ -550,14 +561,17 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
ctx.Data["IssueIndexerUnavailable"] = true
return
}
- statsOpts.IssueIDs = allIssueIDs
+ if len(keywordMatchedIssueIDs) == 0 {
+ // It did search with the keyword, but no issue found, just set issueStats to empty, then no need to do query again.
+ issueStats = &issues_model.IssueStats{}
+ // set keywordMatchedIssueIDs to empty slice, so we can distinguish it from "nil"
+ keywordMatchedIssueIDs = []int64{}
+ }
+ statsOpts.IssueIDs = keywordMatchedIssueIDs
}
- if keyword != "" && len(statsOpts.IssueIDs) == 0 {
- // So it did search with the keyword, but no issue found.
- // Just set issueStats to empty.
- issueStats = &issues_model.IssueStats{}
- } else {
- // So it did search with the keyword, and found some issues. It needs to get issueStats of these issues.
+
+ if issueStats == nil {
+ // Either it did search with the keyword, and found some issues, it needs to get issueStats of these issues.
// Or the keyword is empty, so it doesn't need issueIDs as filter, just get issueStats with statsOpts.
issueStats, err = issues_model.GetIssueStats(ctx, statsOpts)
if err != nil {
@@ -589,31 +603,27 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
ctx.Data["TotalTrackedTime"] = totalTrackedTime
}
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
-
- var total int
- switch {
- case isShowClosed.Value():
- total = int(issueStats.ClosedCount)
- case !isShowClosed.Has():
- total = int(issueStats.OpenCount + issueStats.ClosedCount)
- default:
- total = int(issueStats.OpenCount)
+ // prepare pager
+ total := int(issueStats.OpenCount + issueStats.ClosedCount)
+ if isShowClosed.Has() {
+ total = util.Iif(isShowClosed.Value(), int(issueStats.ClosedCount), int(issueStats.OpenCount))
}
+ page := max(ctx.FormInt("page"), 1)
pager := context.NewPagination(total, setting.UI.IssuePagingNum, page, 5)
+ // prepare real issue list:
var issues issues_model.IssueList
- {
- ids, err := issueIDsFromSearch(ctx, keyword, &issues_model.IssuesOptions{
+ if keywordMatchedIssueIDs == nil || len(keywordMatchedIssueIDs) > 0 {
+ // Either it did search with the keyword, and found some issues, then keywordMatchedIssueIDs is not null, it needs to use db indexer.
+ // Or the keyword is empty, it also needs to usd db indexer.
+ // In either case, no need to use keyword anymore
+ searchResult, err := db_indexer.GetIndexer().FindWithIssueOptions(ctx, &issues_model.IssuesOptions{
Paginator: &db.ListOptions{
Page: pager.Paginater.Current(),
PageSize: setting.UI.IssuePagingNum,
},
RepoIDs: []int64{repo.ID},
- AssigneeID: optional.Some(assigneeID),
+ AssigneeID: assigneeID,
PosterID: posterUserID,
MentionedID: mentionedID,
ReviewRequestedID: reviewRequestedID,
@@ -622,18 +632,16 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
ProjectID: projectID,
IsClosed: isShowClosed,
IsPull: isPullOption,
- LabelIDs: labelIDs,
+ LabelIDs: preparedLabelFilter.SelectedLabelIDs,
SortType: sortType,
+ IssueIDs: keywordMatchedIssueIDs,
})
if err != nil {
- if issue_indexer.IsAvailable(ctx) {
- ctx.ServerError("issueIDsFromSearch", err)
- return
- }
- ctx.Data["IssueIndexerUnavailable"] = true
+ ctx.ServerError("DBIndexer.Search", err)
return
}
- issues, err = issues_model.GetIssuesByIDs(ctx, ids, true)
+ issueIDs := issue_indexer.SearchResultToIDSlice(searchResult)
+ issues, err = issues_model.GetIssuesByIDs(ctx, issueIDs, true)
if err != nil {
ctx.ServerError("GetIssuesByIDs", err)
return
@@ -696,9 +704,10 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
return 0
}
reviewTyp := issues_model.ReviewTypeApprove
- if typ == "reject" {
+ switch typ {
+ case "reject":
reviewTyp = issues_model.ReviewTypeReject
- } else if typ == "waiting" {
+ case "waiting":
reviewTyp = issues_model.ReviewTypeRequest
}
for _, count := range counts {
@@ -727,7 +736,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
ctx.Data["IssueStats"] = issueStats
ctx.Data["OpenCount"] = issueStats.OpenCount
ctx.Data["ClosedCount"] = issueStats.ClosedCount
- ctx.Data["SelLabelIDs"] = labelIDs
+ ctx.Data["SelLabelIDs"] = preparedLabelFilter.SelectedLabelIDs
ctx.Data["ViewType"] = viewType
ctx.Data["SortType"] = sortType
ctx.Data["MilestoneID"] = milestoneID
@@ -768,7 +777,7 @@ func Issues(ctx *context.Context) {
ctx.Data["NewIssueChooseTemplate"] = issue_service.HasTemplatesOrContactLinks(ctx.Repo.Repository, ctx.Repo.GitRepo)
}
- issues(ctx, ctx.FormInt64("milestone"), ctx.FormInt64("project"), optional.Some(isPullList))
+ prepareIssueFilterAndList(ctx, ctx.FormInt64("milestone"), ctx.FormInt64("project"), optional.Some(isPullList))
if ctx.Written() {
return
}
diff --git a/routers/web/repo/issue_lock.go b/routers/web/repo/issue_lock.go
index 1d5fc8a5f3..bc8aabd90b 100644
--- a/routers/web/repo/issue_lock.go
+++ b/routers/web/repo/issue_lock.go
@@ -24,11 +24,6 @@ func LockIssue(ctx *context.Context) {
return
}
- if !form.HasValidReason() {
- ctx.JSONError(ctx.Tr("repo.issues.lock.unknown_reason"))
- return
- }
-
if err := issues_model.LockIssue(ctx, &issues_model.IssueLockOptions{
Doer: ctx.Doer,
Issue: issue,
diff --git a/routers/web/repo/issue_new.go b/routers/web/repo/issue_new.go
index 32daa3e48f..887019b146 100644
--- a/routers/web/repo/issue_new.go
+++ b/routers/web/repo/issue_new.go
@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"html/template"
+ "maps"
"net/http"
"slices"
"sort"
@@ -136,9 +137,7 @@ func NewIssue(ctx *context.Context) {
ret := issue_service.ParseTemplatesFromDefaultBranch(ctx.Repo.Repository, ctx.Repo.GitRepo)
templateLoaded, errs := setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates, pageMetaData)
- for k, v := range errs {
- ret.TemplateErrors[k] = v
- }
+ maps.Copy(ret.TemplateErrors, errs)
if ctx.Written() {
return
}
@@ -223,11 +222,11 @@ func DeleteIssue(ctx *context.Context) {
}
if issue.IsPull {
- ctx.Redirect(fmt.Sprintf("%s/pulls", ctx.Repo.Repository.Link()), http.StatusSeeOther)
+ ctx.Redirect(ctx.Repo.Repository.Link()+"/pulls", http.StatusSeeOther)
return
}
- ctx.Redirect(fmt.Sprintf("%s/issues", ctx.Repo.Repository.Link()), http.StatusSeeOther)
+ ctx.Redirect(ctx.Repo.Repository.Link()+"/issues", http.StatusSeeOther)
}
func toSet[ItemType any, KeyType comparable](slice []ItemType, keyFunc func(ItemType) KeyType) container.Set[KeyType] {
@@ -255,7 +254,7 @@ func ValidateRepoMetasForNewIssue(ctx *context.Context, form forms.CreateIssueFo
inputLabelIDs, _ := base.StringsToInt64s(strings.Split(form.LabelIDs, ","))
candidateLabels := toSet(pageMetaData.LabelsData.AllLabels, func(label *issues_model.Label) int64 { return label.ID })
if len(inputLabelIDs) > 0 && !candidateLabels.Contains(inputLabelIDs...) {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
return ret
}
pageMetaData.LabelsData.SetSelectedLabelIDs(inputLabelIDs)
@@ -263,7 +262,7 @@ func ValidateRepoMetasForNewIssue(ctx *context.Context, form forms.CreateIssueFo
allMilestones := append(slices.Clone(pageMetaData.MilestonesData.OpenMilestones), pageMetaData.MilestonesData.ClosedMilestones...)
candidateMilestones := toSet(allMilestones, func(milestone *issues_model.Milestone) int64 { return milestone.ID })
if form.MilestoneID > 0 && !candidateMilestones.Contains(form.MilestoneID) {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
return ret
}
pageMetaData.MilestonesData.SelectedMilestoneID = form.MilestoneID
@@ -271,18 +270,21 @@ func ValidateRepoMetasForNewIssue(ctx *context.Context, form forms.CreateIssueFo
allProjects := append(slices.Clone(pageMetaData.ProjectsData.OpenProjects), pageMetaData.ProjectsData.ClosedProjects...)
candidateProjects := toSet(allProjects, func(project *project_model.Project) int64 { return project.ID })
if form.ProjectID > 0 && !candidateProjects.Contains(form.ProjectID) {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
return ret
}
pageMetaData.ProjectsData.SelectedProjectID = form.ProjectID
+ // prepare assignees
candidateAssignees := toSet(pageMetaData.AssigneesData.CandidateAssignees, func(user *user_model.User) int64 { return user.ID })
inputAssigneeIDs, _ := base.StringsToInt64s(strings.Split(form.AssigneeIDs, ","))
- if len(inputAssigneeIDs) > 0 && !candidateAssignees.Contains(inputAssigneeIDs...) {
- ctx.NotFound("", nil)
- return ret
+ var assigneeIDStrings []string
+ for _, inputAssigneeID := range inputAssigneeIDs {
+ if candidateAssignees.Contains(inputAssigneeID) {
+ assigneeIDStrings = append(assigneeIDStrings, strconv.FormatInt(inputAssigneeID, 10))
+ }
}
- pageMetaData.AssigneesData.SelectedAssigneeIDs = form.AssigneeIDs
+ pageMetaData.AssigneesData.SelectedAssigneeIDs = strings.Join(assigneeIDStrings, ",")
// Check if the passed reviewers (user/team) actually exist
var reviewers []*user_model.User
@@ -301,14 +303,14 @@ func ValidateRepoMetasForNewIssue(ctx *context.Context, form forms.CreateIssueFo
if rID < 0 { // negative reviewIDs represent team requests
team, ok := teamReviewersMap[-rID]
if !ok {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
return ret
}
teamReviewers = append(teamReviewers, team)
} else {
user, ok := userReviewersMap[rID]
if !ok {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
return ret
}
reviewers = append(reviewers, user)
@@ -346,7 +348,7 @@ func NewIssuePost(ctx *context.Context) {
if projectID > 0 {
if !ctx.Repo.CanRead(unit.TypeProjects) {
// User must also be able to see the project.
- ctx.Error(http.StatusBadRequest, "user hasn't permissions to read projects")
+ ctx.HTTPError(http.StatusBadRequest, "user hasn't permissions to read projects")
return
}
}
@@ -385,7 +387,7 @@ func NewIssuePost(ctx *context.Context) {
if err := issue_service.NewIssue(ctx, repo, issue, labelIDs, attachments, assigneeIDs, projectID); err != nil {
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) {
- ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err.Error())
+ ctx.HTTPError(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err.Error())
} else if errors.Is(err, user_model.ErrBlockedUser) {
ctx.JSONError(ctx.Tr("repo.issues.new.blocked_user"))
} else {
diff --git a/routers/web/repo/issue_page_meta.go b/routers/web/repo/issue_page_meta.go
index b536b04d7c..93cc38bffa 100644
--- a/routers/web/repo/issue_page_meta.go
+++ b/routers/web/repo/issue_page_meta.go
@@ -79,27 +79,35 @@ func retrieveRepoIssueMetaData(ctx *context.Context, repo *repo_model.Repository
return data
}
- data.CanModifyIssueOrPull = ctx.Repo.CanWriteIssuesOrPulls(isPull) && !ctx.Repo.Repository.IsArchived
- if !data.CanModifyIssueOrPull {
+ // it sets "Branches" template data,
+ // it is used to render the "edit PR target branches" dropdown, and the "branch selector" in the issue's sidebar.
+ PrepareBranchList(ctx)
+ if ctx.Written() {
return data
}
- data.retrieveAssigneesDataForIssueWriter(ctx)
+ // it sets the "Assignees" template data, and the data is also used to "mention" users.
+ data.retrieveAssigneesData(ctx)
if ctx.Written() {
return data
}
- data.retrieveMilestonesDataForIssueWriter(ctx)
- if ctx.Written() {
+ // TODO: the issue/pull permissions are quite complex and unclear
+ // A reader could create an issue/PR with setting some meta (eg: assignees from issue template, reviewers, target branch)
+ // A reader(creator) could update some meta (eg: target branch), but can't change assignees anymore.
+ // For non-creator users, only writers could update some meta (eg: assignees, milestone, project)
+ // Need to clarify the logic and add some tests in the future
+ data.CanModifyIssueOrPull = ctx.Repo.CanWriteIssuesOrPulls(isPull) && !ctx.Repo.Repository.IsArchived
+ if !data.CanModifyIssueOrPull {
return data
}
- data.retrieveProjectsDataForIssueWriter(ctx)
+ data.retrieveMilestonesDataForIssueWriter(ctx)
if ctx.Written() {
return data
}
- PrepareBranchList(ctx)
+ data.retrieveProjectsDataForIssueWriter(ctx)
if ctx.Written() {
return data
}
@@ -131,7 +139,7 @@ func (d *IssuePageMetaData) retrieveMilestonesDataForIssueWriter(ctx *context.Co
}
}
-func (d *IssuePageMetaData) retrieveAssigneesDataForIssueWriter(ctx *context.Context) {
+func (d *IssuePageMetaData) retrieveAssigneesData(ctx *context.Context) {
var err error
d.AssigneesData.CandidateAssignees, err = repo_model.GetRepoAssignees(ctx, d.Repository)
if err != nil {
@@ -193,6 +201,7 @@ func (d *IssuePageMetaData) retrieveReviewersData(ctx *context.Context) {
var posterID int64
var isClosed bool
var reviews issues_model.ReviewList
+ var err error
if d.Issue == nil {
if ctx.Doer != nil {
@@ -206,14 +215,7 @@ func (d *IssuePageMetaData) retrieveReviewersData(ctx *context.Context) {
isClosed = d.Issue.IsClosed || d.Issue.PullRequest.HasMerged
- originalAuthorReviews, err := issues_model.GetReviewersFromOriginalAuthorsByIssueID(ctx, d.Issue.ID)
- if err != nil {
- ctx.ServerError("GetReviewersFromOriginalAuthorsByIssueID", err)
- return
- }
- data.OriginalReviews = originalAuthorReviews
-
- reviews, err = issues_model.GetReviewsByIssueID(ctx, d.Issue.ID)
+ reviews, data.OriginalReviews, err = issues_model.GetReviewsByIssueID(ctx, d.Issue.ID)
if err != nil {
ctx.ServerError("GetReviewersByIssueID", err)
return
diff --git a/routers/web/repo/issue_pin.go b/routers/web/repo/issue_pin.go
index d7d3205c37..8d3de90d25 100644
--- a/routers/web/repo/issue_pin.go
+++ b/routers/web/repo/issue_pin.go
@@ -6,6 +6,7 @@ package repo
import (
"net/http"
+ "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
@@ -22,15 +23,29 @@ func IssuePinOrUnpin(ctx *context.Context) {
// If we don't do this, it will crash when trying to add the pin event to the comment history
err := issue.LoadRepo(ctx)
if err != nil {
- ctx.Status(http.StatusInternalServerError)
- log.Error(err.Error())
+ ctx.ServerError("LoadRepo", err)
return
}
- err = issue.PinOrUnpin(ctx, ctx.Doer)
+ // PinOrUnpin pins or unpins a Issue
+ _, err = issues_model.GetIssuePin(ctx, issue)
+ if err != nil && !db.IsErrNotExist(err) {
+ ctx.ServerError("GetIssuePin", err)
+ return
+ }
+
+ if db.IsErrNotExist(err) {
+ err = issues_model.PinIssue(ctx, issue, ctx.Doer)
+ } else {
+ err = issues_model.UnpinIssue(ctx, issue, ctx.Doer)
+ }
+
if err != nil {
- ctx.Status(http.StatusInternalServerError)
- log.Error(err.Error())
+ if issues_model.IsErrIssueMaxPinReached(err) {
+ ctx.JSONError(ctx.Tr("repo.issues.max_pinned"))
+ } else {
+ ctx.ServerError("Pin/Unpin failed", err)
+ }
return
}
@@ -41,23 +56,20 @@ func IssuePinOrUnpin(ctx *context.Context) {
func IssueUnpin(ctx *context.Context) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
- ctx.Status(http.StatusInternalServerError)
- log.Error(err.Error())
+ ctx.ServerError("GetIssueByIndex", err)
return
}
// If we don't do this, it will crash when trying to add the pin event to the comment history
err = issue.LoadRepo(ctx)
if err != nil {
- ctx.Status(http.StatusInternalServerError)
- log.Error(err.Error())
+ ctx.ServerError("LoadRepo", err)
return
}
- err = issue.Unpin(ctx, ctx.Doer)
+ err = issues_model.UnpinIssue(ctx, issue, ctx.Doer)
if err != nil {
- ctx.Status(http.StatusInternalServerError)
- log.Error(err.Error())
+ ctx.ServerError("UnpinIssue", err)
return
}
@@ -78,15 +90,13 @@ func IssuePinMove(ctx *context.Context) {
form := &movePinIssueForm{}
if err := json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil {
- ctx.Status(http.StatusInternalServerError)
- log.Error(err.Error())
+ ctx.ServerError("Decode", err)
return
}
issue, err := issues_model.GetIssueByID(ctx, form.ID)
if err != nil {
- ctx.Status(http.StatusInternalServerError)
- log.Error(err.Error())
+ ctx.ServerError("GetIssueByID", err)
return
}
@@ -96,10 +106,9 @@ func IssuePinMove(ctx *context.Context) {
return
}
- err = issue.MovePin(ctx, form.Position)
+ err = issues_model.MovePin(ctx, issue, form.Position)
if err != nil {
- ctx.Status(http.StatusInternalServerError)
- log.Error(err.Error())
+ ctx.ServerError("MovePin", err)
return
}
diff --git a/routers/web/repo/issue_poster.go b/routers/web/repo/issue_poster.go
index 91ef947cb4..07059b9b7b 100644
--- a/routers/web/repo/issue_poster.go
+++ b/routers/web/repo/issue_poster.go
@@ -26,13 +26,9 @@ type userSearchResponse struct {
Results []*userSearchInfo `json:"results"`
}
-// IssuePosters get posters for current repo's issues/pull requests
-func IssuePosters(ctx *context.Context) {
- issuePosters(ctx, false)
-}
-
-func PullPosters(ctx *context.Context) {
- issuePosters(ctx, true)
+func IssuePullPosters(ctx *context.Context) {
+ isPullList := ctx.PathParam("type") == "pulls"
+ issuePosters(ctx, isPullList)
}
func issuePosters(ctx *context.Context, isPullList bool) {
diff --git a/routers/web/repo/issue_stopwatch.go b/routers/web/repo/issue_stopwatch.go
index 5d6e723311..2de3a7cfec 100644
--- a/routers/web/repo/issue_stopwatch.go
+++ b/routers/web/repo/issue_stopwatch.go
@@ -4,41 +4,53 @@
package repo
import (
- "strings"
-
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/eventsource"
"code.gitea.io/gitea/services/context"
)
-// IssueStopwatch creates or stops a stopwatch for the given issue.
-func IssueStopwatch(c *context.Context) {
+// IssueStartStopwatch creates a stopwatch for the given issue.
+func IssueStartStopwatch(c *context.Context) {
issue := GetActionIssue(c)
if c.Written() {
return
}
- var showSuccessMessage bool
-
- if !issues_model.StopwatchExists(c, c.Doer.ID, issue.ID) {
- showSuccessMessage = true
+ if !c.Repo.CanUseTimetracker(c, issue, c.Doer) {
+ c.NotFound(nil)
+ return
}
- if !c.Repo.CanUseTimetracker(c, issue, c.Doer) {
- c.NotFound("CanUseTimetracker", nil)
+ if ok, err := issues_model.CreateIssueStopwatch(c, c.Doer, issue); err != nil {
+ c.ServerError("CreateIssueStopwatch", err)
return
+ } else if !ok {
+ c.Flash.Warning(c.Tr("repo.issues.stopwatch_already_created"))
+ } else {
+ c.Flash.Success(c.Tr("repo.issues.tracker_auto_close"))
}
+ c.JSONRedirect("")
+}
- if err := issues_model.CreateOrStopIssueStopwatch(c, c.Doer, issue); err != nil {
- c.ServerError("CreateOrStopIssueStopwatch", err)
+// IssueStopStopwatch stops a stopwatch for the given issue.
+func IssueStopStopwatch(c *context.Context) {
+ issue := GetActionIssue(c)
+ if c.Written() {
return
}
- if showSuccessMessage {
- c.Flash.Success(c.Tr("repo.issues.tracker_auto_close"))
+ if !c.Repo.CanUseTimetracker(c, issue, c.Doer) {
+ c.NotFound(nil)
+ return
}
+ if ok, err := issues_model.FinishIssueStopwatch(c, c.Doer, issue); err != nil {
+ c.ServerError("FinishIssueStopwatch", err)
+ return
+ } else if !ok {
+ c.Flash.Warning(c.Tr("repo.issues.stopwatch_already_stopped"))
+ }
c.JSONRedirect("")
}
@@ -49,11 +61,11 @@ func CancelStopwatch(c *context.Context) {
return
}
if !c.Repo.CanUseTimetracker(c, issue, c.Doer) {
- c.NotFound("CanUseTimetracker", nil)
+ c.NotFound(nil)
return
}
- if err := issues_model.CancelStopwatch(c, c.Doer, issue); err != nil {
+ if _, err := issues_model.CancelStopwatch(c, c.Doer, issue); err != nil {
c.ServerError("CancelStopwatch", err)
return
}
@@ -72,39 +84,3 @@ func CancelStopwatch(c *context.Context) {
c.JSONRedirect("")
}
-
-// GetActiveStopwatch is the middleware that sets .ActiveStopwatch on context
-func GetActiveStopwatch(ctx *context.Context) {
- if strings.HasPrefix(ctx.Req.URL.Path, "/api") {
- return
- }
-
- if !ctx.IsSigned {
- return
- }
-
- _, sw, issue, err := issues_model.HasUserStopwatch(ctx, ctx.Doer.ID)
- if err != nil {
- ctx.ServerError("HasUserStopwatch", err)
- return
- }
-
- if sw == nil || sw.ID == 0 {
- return
- }
-
- ctx.Data["ActiveStopwatch"] = StopwatchTmplInfo{
- issue.Link(),
- issue.Repo.FullName(),
- issue.Index,
- sw.Seconds() + 1, // ensure time is never zero in ui
- }
-}
-
-// StopwatchTmplInfo is a view on a stopwatch specifically for template rendering
-type StopwatchTmplInfo struct {
- IssueLink string
- RepoSlug string
- IssueIndex int64
- Seconds int64
-}
diff --git a/routers/web/repo/issue_suggestions.go b/routers/web/repo/issue_suggestions.go
index 46e9f339a5..9ef3942504 100644
--- a/routers/web/repo/issue_suggestions.go
+++ b/routers/web/repo/issue_suggestions.go
@@ -6,13 +6,10 @@ package repo
import (
"net/http"
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unit"
- issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
"code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/services/context"
+ issue_service "code.gitea.io/gitea/services/issue"
)
// IssueSuggestions returns a list of issue suggestions
@@ -29,54 +26,11 @@ func IssueSuggestions(ctx *context.Context) {
isPull = optional.Some(false)
}
- searchOpt := &issue_indexer.SearchOptions{
- Paginator: &db.ListOptions{
- Page: 0,
- PageSize: 5,
- },
- Keyword: keyword,
- RepoIDs: []int64{ctx.Repo.Repository.ID},
- IsPull: isPull,
- IsClosed: nil,
- SortBy: issue_indexer.SortByUpdatedDesc,
- }
-
- ids, _, err := issue_indexer.SearchIssues(ctx, searchOpt)
- if err != nil {
- ctx.ServerError("SearchIssues", err)
- return
- }
- issues, err := issues_model.GetIssuesByIDs(ctx, ids, true)
+ suggestions, err := issue_service.GetSuggestion(ctx, ctx.Repo.Repository, isPull, keyword)
if err != nil {
- ctx.ServerError("FindIssuesByIDs", err)
+ ctx.ServerError("GetSuggestion", err)
return
}
- suggestions := make([]*structs.Issue, 0, len(issues))
-
- for _, issue := range issues {
- suggestion := &structs.Issue{
- ID: issue.ID,
- Index: issue.Index,
- Title: issue.Title,
- State: issue.State(),
- }
-
- if issue.IsPull {
- if err := issue.LoadPullRequest(ctx); err != nil {
- ctx.ServerError("LoadPullRequest", err)
- return
- }
- if issue.PullRequest != nil {
- suggestion.PullRequest = &structs.PullRequestMeta{
- HasMerged: issue.PullRequest.HasMerged,
- IsWorkInProgress: issue.PullRequest.IsWorkInProgress(ctx),
- }
- }
- }
-
- suggestions = append(suggestions, suggestion)
- }
-
ctx.JSON(http.StatusOK, suggestions)
}
diff --git a/routers/web/repo/issue_timetrack.go b/routers/web/repo/issue_timetrack.go
index 36e931a48f..985bfd6698 100644
--- a/routers/web/repo/issue_timetrack.go
+++ b/routers/web/repo/issue_timetrack.go
@@ -25,7 +25,7 @@ func AddTimeManually(c *context.Context) {
return
}
if !c.Repo.CanUseTimetracker(c, issue, c.Doer) {
- c.NotFound("CanUseTimetracker", nil)
+ c.NotFound(nil)
return
}
@@ -56,23 +56,23 @@ func DeleteTime(c *context.Context) {
return
}
if !c.Repo.CanUseTimetracker(c, issue, c.Doer) {
- c.NotFound("CanUseTimetracker", nil)
+ c.NotFound(nil)
return
}
t, err := issues_model.GetTrackedTimeByID(c, c.PathParamInt64("timeid"))
if err != nil {
if db.IsErrNotExist(err) {
- c.NotFound("time not found", err)
+ c.NotFound(err)
return
}
- c.Error(http.StatusInternalServerError, "GetTrackedTimeByID", err.Error())
+ c.HTTPError(http.StatusInternalServerError, "GetTrackedTimeByID", err.Error())
return
}
// only OP or admin may delete
if !c.IsSigned || (!c.IsUserSiteAdmin() && c.Doer.ID != t.UserID) {
- c.Error(http.StatusForbidden, "not allowed")
+ c.HTTPError(http.StatusForbidden, "not allowed")
return
}
@@ -81,7 +81,7 @@ func DeleteTime(c *context.Context) {
return
}
- c.Flash.Success(c.Tr("repo.issues.del_time_history", util.SecToTime(t.Time)))
+ c.Flash.Success(c.Tr("repo.issues.del_time_history", util.SecToHours(t.Time)))
c.JSONRedirect("")
}
@@ -92,7 +92,7 @@ func UpdateIssueTimeEstimate(ctx *context.Context) {
}
if !ctx.IsSigned || (!issue.IsPoster(ctx.Doer.ID) && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
diff --git a/routers/web/repo/issue_view.go b/routers/web/repo/issue_view.go
index 61e75e211b..d4458ed19e 100644
--- a/routers/web/repo/issue_view.go
+++ b/routers/web/repo/issue_view.go
@@ -4,12 +4,12 @@
package repo
import (
- stdCtx "context"
"fmt"
"math/big"
"net/http"
"net/url"
"sort"
+ "strconv"
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
@@ -25,12 +25,14 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/emoji"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/templates/vars"
+ "code.gitea.io/gitea/modules/util"
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/context/upload"
@@ -40,72 +42,80 @@ import (
)
// roleDescriptor returns the role descriptor for a comment in/with the given repo, poster and issue
-func roleDescriptor(ctx stdCtx.Context, repo *repo_model.Repository, poster *user_model.User, issue *issues_model.Issue, hasOriginalAuthor bool) (issues_model.RoleDescriptor, error) {
- roleDescriptor := issues_model.RoleDescriptor{}
-
+func roleDescriptor(ctx *context.Context, repo *repo_model.Repository, poster *user_model.User, permsCache map[int64]access_model.Permission, issue *issues_model.Issue, hasOriginalAuthor bool) (roleDesc issues_model.RoleDescriptor, err error) {
if hasOriginalAuthor {
- return roleDescriptor, nil
+ // the poster is a migrated user, so no need to detect the role
+ return roleDesc, nil
}
- perm, err := access_model.GetUserRepoPermission(ctx, repo, poster)
- if err != nil {
- return roleDescriptor, err
+ if poster.IsGhost() || !poster.IsIndividual() {
+ return roleDesc, nil
}
- // If the poster is the actual poster of the issue, enable Poster role.
- roleDescriptor.IsPoster = issue.IsPoster(poster.ID)
+ roleDesc.IsPoster = issue.IsPoster(poster.ID) // check whether the comment's poster is the issue's poster
+
+ // Guess the role of the poster in the repo by permission
+ perm, hasPermCache := permsCache[poster.ID]
+ if !hasPermCache {
+ perm, err = access_model.GetUserRepoPermission(ctx, repo, poster)
+ if err != nil {
+ return roleDesc, err
+ }
+ }
+ if permsCache != nil {
+ permsCache[poster.ID] = perm
+ }
// Check if the poster is owner of the repo.
if perm.IsOwner() {
- // If the poster isn't an admin, enable the owner role.
+ // If the poster isn't a site admin, then is must be the repo's owner
if !poster.IsAdmin {
- roleDescriptor.RoleInRepo = issues_model.RoleRepoOwner
- return roleDescriptor, nil
+ roleDesc.RoleInRepo = issues_model.RoleRepoOwner
+ return roleDesc, nil
}
-
- // Otherwise check if poster is the real repo admin.
- ok, err := access_model.IsUserRealRepoAdmin(ctx, repo, poster)
+ // Otherwise (poster is site admin), check if poster is the real repo admin.
+ isRealRepoAdmin, err := access_model.IsUserRealRepoAdmin(ctx, repo, poster)
if err != nil {
- return roleDescriptor, err
+ return roleDesc, err
}
- if ok {
- roleDescriptor.RoleInRepo = issues_model.RoleRepoOwner
- return roleDescriptor, nil
+ if isRealRepoAdmin {
+ roleDesc.RoleInRepo = issues_model.RoleRepoOwner
+ return roleDesc, nil
}
}
// If repo is organization, check Member role
- if err := repo.LoadOwner(ctx); err != nil {
- return roleDescriptor, err
+ if err = repo.LoadOwner(ctx); err != nil {
+ return roleDesc, err
}
if repo.Owner.IsOrganization() {
if isMember, err := organization.IsOrganizationMember(ctx, repo.Owner.ID, poster.ID); err != nil {
- return roleDescriptor, err
+ return roleDesc, err
} else if isMember {
- roleDescriptor.RoleInRepo = issues_model.RoleRepoMember
- return roleDescriptor, nil
+ roleDesc.RoleInRepo = issues_model.RoleRepoMember
+ return roleDesc, nil
}
}
// If the poster is the collaborator of the repo
if isCollaborator, err := repo_model.IsCollaborator(ctx, repo.ID, poster.ID); err != nil {
- return roleDescriptor, err
+ return roleDesc, err
} else if isCollaborator {
- roleDescriptor.RoleInRepo = issues_model.RoleRepoCollaborator
- return roleDescriptor, nil
+ roleDesc.RoleInRepo = issues_model.RoleRepoCollaborator
+ return roleDesc, nil
}
hasMergedPR, err := issues_model.HasMergedPullRequestInRepo(ctx, repo.ID, poster.ID)
if err != nil {
- return roleDescriptor, err
+ return roleDesc, err
} else if hasMergedPR {
- roleDescriptor.RoleInRepo = issues_model.RoleRepoContributor
+ roleDesc.RoleInRepo = issues_model.RoleRepoContributor
} else if issue.IsPull {
// only display first time contributor in the first opening pull request
- roleDescriptor.RoleInRepo = issues_model.RoleRepoFirstTimeContributor
+ roleDesc.RoleInRepo = issues_model.RoleRepoFirstTimeContributor
}
- return roleDescriptor, nil
+ return roleDesc, nil
}
func getBranchData(ctx *context.Context, issue *issues_model.Issue) {
@@ -263,14 +273,29 @@ func combineLabelComments(issue *issues_model.Issue) {
}
}
-// ViewIssue render issue view page
-func ViewIssue(ctx *context.Context) {
+func prepareIssueViewLoad(ctx *context.Context) *issues_model.Issue {
+ issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
+ if err != nil {
+ ctx.NotFoundOrServerError("GetIssueByIndex", issues_model.IsErrIssueNotExist, err)
+ return nil
+ }
+ issue.Repo = ctx.Repo.Repository
+ ctx.Data["Issue"] = issue
+
+ if err = issue.LoadPullRequest(ctx); err != nil {
+ ctx.ServerError("LoadPullRequest", err)
+ return nil
+ }
+ return issue
+}
+
+func handleViewIssueRedirectExternal(ctx *context.Context) {
if ctx.PathParam("type") == "issues" {
// If issue was requested we check if repo has external tracker and redirect
extIssueUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalTracker)
if err == nil && extIssueUnit != nil {
if extIssueUnit.ExternalTrackerConfig().ExternalTrackerStyle == markup.IssueNameStyleNumeric || extIssueUnit.ExternalTrackerConfig().ExternalTrackerStyle == "" {
- metas := ctx.Repo.Repository.ComposeMetas(ctx)
+ metas := ctx.Repo.Repository.ComposeCommentMetas(ctx)
metas["index"] = ctx.PathParam("index")
res, err := vars.Expand(extIssueUnit.ExternalTrackerConfig().ExternalTrackerFormat, metas)
if err != nil {
@@ -286,18 +311,18 @@ func ViewIssue(ctx *context.Context) {
return
}
}
+}
- issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
- if err != nil {
- if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound("GetIssueByIndex", err)
- } else {
- ctx.ServerError("GetIssueByIndex", err)
- }
+// ViewIssue render issue view page
+func ViewIssue(ctx *context.Context) {
+ handleViewIssueRedirectExternal(ctx)
+ if ctx.Written() {
return
}
- if issue.Repo == nil {
- issue.Repo = ctx.Repo.Repository
+
+ issue := prepareIssueViewLoad(ctx)
+ if ctx.Written() {
+ return
}
// Make sure type and URL matches.
@@ -329,12 +354,12 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "comment")
- if err = issue.LoadAttributes(ctx); err != nil {
+ if err := issue.LoadAttributes(ctx); err != nil {
ctx.ServerError("LoadAttributes", err)
return
}
- if err = filterXRefComments(ctx, issue); err != nil {
+ if err := filterXRefComments(ctx, issue); err != nil {
ctx.ServerError("filterXRefComments", err)
return
}
@@ -343,7 +368,7 @@ func ViewIssue(ctx *context.Context) {
if ctx.IsSigned {
// Update issue-user.
- if err = activities_model.SetIssueReadBy(ctx, issue.ID, ctx.Doer.ID); err != nil {
+ if err := activities_model.SetIssueReadBy(ctx, issue.ID, ctx.Doer.ID); err != nil {
ctx.ServerError("ReadBy", err)
return
}
@@ -357,15 +382,13 @@ func ViewIssue(ctx *context.Context) {
prepareFuncs := []func(*context.Context, *issues_model.Issue){
prepareIssueViewContent,
- func(ctx *context.Context, issue *issues_model.Issue) {
- preparePullViewPullInfo(ctx, issue)
- },
prepareIssueViewCommentsAndSidebarParticipants,
- preparePullViewReviewAndMerge,
prepareIssueViewSidebarWatch,
prepareIssueViewSidebarTimeTracker,
prepareIssueViewSidebarDependency,
prepareIssueViewSidebarPin,
+ func(ctx *context.Context, issue *issues_model.Issue) { preparePullViewPullInfo(ctx, issue) },
+ preparePullViewReviewAndMerge,
}
for _, prepareFunc := range prepareFuncs {
@@ -404,9 +427,29 @@ func ViewIssue(ctx *context.Context) {
return user_service.CanBlockUser(ctx, ctx.Doer, blocker, blockee)
}
+ if issue.PullRequest != nil && !issue.PullRequest.IsChecking() && !setting.IsProd {
+ ctx.Data["PullMergeBoxReloadingInterval"] = 1 // in dev env, force using the reloading logic to make sure it won't break
+ }
+
ctx.HTML(http.StatusOK, tplIssueView)
}
+func ViewPullMergeBox(ctx *context.Context) {
+ issue := prepareIssueViewLoad(ctx)
+ if !issue.IsPull {
+ ctx.NotFound(nil)
+ return
+ }
+ preparePullViewPullInfo(ctx, issue)
+ preparePullViewReviewAndMerge(ctx, issue)
+ ctx.Data["PullMergeBoxReloading"] = issue.PullRequest.IsChecking()
+
+ // TODO: it should use a dedicated struct to render the pull merge box, to make sure all data is prepared correctly
+ ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.Doer.ID)
+ ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
+ ctx.HTML(http.StatusOK, tplPullMergeBox)
+}
+
func prepareIssueViewSidebarDependency(ctx *context.Context, issue *issues_model.Issue) {
if issue.IsPull && !ctx.Repo.CanRead(unit.TypeIssues) {
ctx.Data["IssueDependencySearchType"] = "pulls"
@@ -519,7 +562,7 @@ func preparePullViewDeleteBranch(ctx *context.Context, issue *issues_model.Issue
pull := issue.PullRequest
isPullBranchDeletable := canDelete &&
pull.HeadRepo != nil &&
- git.IsBranchExist(ctx, pull.HeadRepo.RepoPath(), pull.HeadBranch) &&
+ gitrepo.IsBranchExist(ctx, pull.HeadRepo, pull.HeadBranch) &&
(!pull.HasMerged || ctx.Data["HeadBranchCommitID"] == ctx.Data["PullHeadCommitID"])
if isPullBranchDeletable && pull.HasMerged {
@@ -536,7 +579,11 @@ func preparePullViewDeleteBranch(ctx *context.Context, issue *issues_model.Issue
func prepareIssueViewSidebarPin(ctx *context.Context, issue *issues_model.Issue) {
var pinAllowed bool
- if !issue.IsPinned() {
+ if err := issue.LoadPinOrder(ctx); err != nil {
+ ctx.ServerError("LoadPinOrder", err)
+ return
+ }
+ if issue.PinOrder == 0 {
var err error
pinAllowed, err = issues_model.IsNewPinAllowed(ctx, issue.RepoID, issue.IsPull)
if err != nil {
@@ -576,11 +623,15 @@ func prepareIssueViewCommentsAndSidebarParticipants(ctx *context.Context, issue
return
}
+ permCache := make(map[int64]access_model.Permission)
+
for _, comment = range issue.Comments {
comment.Issue = issue
if comment.Type == issues_model.CommentTypeComment || comment.Type == issues_model.CommentTypeReview {
- rctx := renderhelper.NewRenderContextRepoComment(ctx, issue.Repo)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, issue.Repo, renderhelper.RepoCommentOptions{
+ FootnoteContextID: strconv.FormatInt(comment.ID, 10),
+ })
comment.RenderedContent, err = markdown.RenderString(rctx, comment.Content)
if err != nil {
ctx.ServerError("RenderString", err)
@@ -593,7 +644,7 @@ func prepareIssueViewCommentsAndSidebarParticipants(ctx *context.Context, issue
continue
}
- comment.ShowRole, err = roleDescriptor(ctx, issue.Repo, comment.Poster, issue, comment.HasOriginalAuthor())
+ comment.ShowRole, err = roleDescriptor(ctx, issue.Repo, comment.Poster, permCache, issue, comment.HasOriginalAuthor())
if err != nil {
ctx.ServerError("roleDescriptor", err)
return
@@ -656,7 +707,9 @@ func prepareIssueViewCommentsAndSidebarParticipants(ctx *context.Context, issue
}
}
} else if comment.Type.HasContentSupport() {
- rctx := renderhelper.NewRenderContextRepoComment(ctx, issue.Repo)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, issue.Repo, renderhelper.RepoCommentOptions{
+ FootnoteContextID: strconv.FormatInt(comment.ID, 10),
+ })
comment.RenderedContent, err = markdown.RenderString(rctx, comment.Content)
if err != nil {
ctx.ServerError("RenderString", err)
@@ -691,7 +744,7 @@ func prepareIssueViewCommentsAndSidebarParticipants(ctx *context.Context, issue
continue
}
- c.ShowRole, err = roleDescriptor(ctx, issue.Repo, c.Poster, issue, c.HasOriginalAuthor())
+ c.ShowRole, err = roleDescriptor(ctx, issue.Repo, c.Poster, permCache, issue, c.HasOriginalAuthor())
if err != nil {
ctx.ServerError("roleDescriptor", err)
return
@@ -707,12 +760,15 @@ func prepareIssueViewCommentsAndSidebarParticipants(ctx *context.Context, issue
}
} else if comment.Type == issues_model.CommentTypePullRequestPush {
participants = addParticipant(comment.Poster, participants)
- if err = comment.LoadPushCommits(ctx); err != nil {
- ctx.ServerError("LoadPushCommits", err)
+ if err = issue_service.LoadCommentPushCommits(ctx, comment); err != nil {
+ ctx.ServerError("LoadCommentPushCommits", err)
return
}
if !ctx.Repo.CanRead(unit.TypeActions) {
for _, commit := range comment.Commits {
+ if commit.Status == nil {
+ continue
+ }
commit.Status.HideActionsURL(ctx)
git_model.CommitStatusesHideActionsURL(ctx, commit.Statuses)
}
@@ -778,6 +834,8 @@ func preparePullViewReviewAndMerge(ctx *context.Context, issue *issues_model.Iss
allowMerge := false
canWriteToHeadRepo := false
+ pull_service.StartPullRequestCheckOnView(ctx, pull)
+
if ctx.IsSigned {
if err := pull.LoadHeadRepo(ctx); err != nil {
log.Error("LoadHeadRepo: %v", err)
@@ -824,6 +882,7 @@ func preparePullViewReviewAndMerge(ctx *context.Context, issue *issues_model.Iss
}
}
+ ctx.Data["PullMergeBoxReloadingInterval"] = util.Iif(pull != nil && pull.IsChecking(), 2000, 0)
ctx.Data["CanWriteToHeadRepo"] = canWriteToHeadRepo
ctx.Data["ShowMergeInstructions"] = canWriteToHeadRepo
ctx.Data["AllowMerge"] = allowMerge
@@ -934,15 +993,16 @@ func preparePullViewReviewAndMerge(ctx *context.Context, issue *issues_model.Iss
func prepareIssueViewContent(ctx *context.Context, issue *issues_model.Issue) {
var err error
- rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, ctx.Repo.Repository, renderhelper.RepoCommentOptions{
+ FootnoteContextID: "0", // Set footnote context ID to 0 for the issue content
+ })
issue.RenderedContent, err = markdown.RenderString(rctx, issue.Content)
if err != nil {
ctx.ServerError("RenderString", err)
return
}
- if issue.ShowRole, err = roleDescriptor(ctx, issue.Repo, issue.Poster, issue, issue.HasOriginalAuthor()); err != nil {
+ if issue.ShowRole, err = roleDescriptor(ctx, issue.Repo, issue.Poster, nil, issue, issue.HasOriginalAuthor()); err != nil {
ctx.ServerError("roleDescriptor", err)
return
}
- ctx.Data["Issue"] = issue
}
diff --git a/routers/web/repo/issue_watch.go b/routers/web/repo/issue_watch.go
index a2a4be1758..dfa3491786 100644
--- a/routers/web/repo/issue_watch.go
+++ b/routers/web/repo/issue_watch.go
@@ -42,11 +42,11 @@ func IssueWatch(ctx *context.Context) {
log.Trace("Permission Denied: Not logged in")
}
}
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
- watch, err := strconv.ParseBool(ctx.Req.PostForm.Get("watch"))
+ watch, err := strconv.ParseBool(ctx.Req.PostFormValue("watch"))
if err != nil {
ctx.ServerError("watch is not bool", err)
return
diff --git a/routers/web/repo/middlewares.go b/routers/web/repo/middlewares.go
index 420931c5fb..7518e6feae 100644
--- a/routers/web/repo/middlewares.go
+++ b/routers/web/repo/middlewares.go
@@ -4,12 +4,9 @@
package repo
import (
- "fmt"
"strconv"
- system_model "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/services/context"
user_service "code.gitea.io/gitea/services/user"
@@ -22,12 +19,9 @@ func SetEditorconfigIfExists(ctx *context.Context) {
}
ec, _, err := ctx.Repo.GetEditorconfig()
-
- if err != nil && !git.IsErrNotExist(err) {
- description := fmt.Sprintf("Error while getting .editorconfig file: %v", err)
- if err := system_model.CreateRepositoryNotice(description); err != nil {
- ctx.ServerError("ErrCreatingReporitoryNotice", err)
- }
+ if err != nil {
+ // it used to check `!git.IsErrNotExist(err)` and create a system notice, but it is quite annoying and useless
+ // because network errors also happen frequently, so we just ignore it
return
}
diff --git a/routers/web/repo/migrate.go b/routers/web/repo/migrate.go
index 3a7dc29466..ea15e90e5c 100644
--- a/routers/web/repo/migrate.go
+++ b/routers/web/repo/migrate.go
@@ -35,7 +35,7 @@ const (
// Migrate render migration of repository page
func Migrate(ctx *context.Context) {
if setting.Repository.DisableMigrations {
- ctx.Error(http.StatusForbidden, "Migrate: the site administrator has disabled migrations")
+ ctx.HTTPError(http.StatusForbidden, "Migrate: the site administrator has disabled migrations")
return
}
@@ -72,7 +72,7 @@ func Migrate(ctx *context.Context) {
func handleMigrateError(ctx *context.Context, owner *user_model.User, err error, name string, tpl templates.TplName, form *forms.MigrateRepoForm) {
if setting.Repository.DisableMigrations {
- ctx.Error(http.StatusForbidden, "MigrateError: the site administrator has disabled migrations")
+ ctx.HTTPError(http.StatusForbidden, "MigrateError: the site administrator has disabled migrations")
return
}
@@ -152,12 +152,12 @@ func handleMigrateRemoteAddrError(ctx *context.Context, err error, tpl templates
func MigratePost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.MigrateRepoForm)
if setting.Repository.DisableMigrations {
- ctx.Error(http.StatusForbidden, "MigratePost: the site administrator has disabled migrations")
+ ctx.HTTPError(http.StatusForbidden, "MigratePost: the site administrator has disabled migrations")
return
}
if form.Mirror && setting.Mirror.DisableNewPull {
- ctx.Error(http.StatusBadRequest, "MigratePost: the site administrator has disabled creation of new mirrors")
+ ctx.HTTPError(http.StatusBadRequest, "MigratePost: the site administrator has disabled creation of new mirrors")
return
}
diff --git a/routers/web/repo/milestone.go b/routers/web/repo/milestone.go
index 6a0e6b25a9..dd53b1d3f1 100644
--- a/routers/web/repo/milestone.go
+++ b/routers/web/repo/milestone.go
@@ -38,10 +38,7 @@ func Milestones(ctx *context.Context) {
isShowClosed := ctx.FormString("state") == "closed"
sortType := ctx.FormString("sort")
keyword := ctx.FormTrim("q")
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
miles, total, err := db.FindAndCount[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{
ListOptions: db.ListOptions{
@@ -148,7 +145,7 @@ func EditMilestone(ctx *context.Context) {
m, err := issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrMilestoneNotExist(err) {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetMilestoneByRepoID", err)
}
@@ -184,7 +181,7 @@ func EditMilestonePost(ctx *context.Context) {
m, err := issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrMilestoneNotExist(err) {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetMilestoneByRepoID", err)
}
@@ -218,7 +215,7 @@ func ChangeMilestoneStatus(ctx *context.Context) {
if err := issues_model.ChangeMilestoneStatusByRepoIDAndID(ctx, ctx.Repo.Repository.ID, id, toClose); err != nil {
if issues_model.IsErrMilestoneNotExist(err) {
- ctx.NotFound("", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("ChangeMilestoneStatusByIDAndRepoID", err)
}
@@ -245,7 +242,7 @@ func MilestoneIssuesAndPulls(ctx *context.Context) {
milestone, err := issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, milestoneID)
if err != nil {
if issues_model.IsErrMilestoneNotExist(err) {
- ctx.NotFound("GetMilestoneByID", err)
+ ctx.NotFound(err)
return
}
@@ -263,7 +260,7 @@ func MilestoneIssuesAndPulls(ctx *context.Context) {
ctx.Data["Title"] = milestone.Name
ctx.Data["Milestone"] = milestone
- issues(ctx, milestoneID, projectID, optional.None[bool]())
+ prepareIssueFilterAndList(ctx, milestoneID, projectID, optional.None[bool]())
ret := issue.ParseTemplatesFromDefaultBranch(ctx.Repo.Repository, ctx.Repo.GitRepo)
ctx.Data["NewIssueChooseTemplate"] = len(ret.IssueTemplates) > 0
diff --git a/routers/web/repo/packages.go b/routers/web/repo/packages.go
index 5dcfd0454c..d09a57c03f 100644
--- a/routers/web/repo/packages.go
+++ b/routers/web/repo/packages.go
@@ -21,10 +21,7 @@ const (
// Packages displays a list of all packages in the repository
func Packages(ctx *context.Context) {
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
query := ctx.FormTrim("q")
packageType := ctx.FormTrim("type")
@@ -62,9 +59,7 @@ func Packages(ctx *context.Context) {
ctx.Data["PackageType"] = packageType
ctx.Data["AvailableTypes"] = packages.TypeList
ctx.Data["HasPackages"] = hasPackages
- if ctx.Repo != nil {
- ctx.Data["CanWritePackages"] = ctx.IsUserRepoWriter([]unit.Type{unit.TypePackages}) || ctx.IsUserSiteAdmin()
- }
+ ctx.Data["CanWritePackages"] = ctx.Repo.CanWrite(unit.TypePackages) || ctx.IsUserSiteAdmin()
ctx.Data["PackageDescriptors"] = pds
ctx.Data["Total"] = total
ctx.Data["RepositoryAccessMap"] = map[int64]bool{ctx.Repo.Repository.ID: true} // There is only the current repository
diff --git a/routers/web/repo/patch.go b/routers/web/repo/patch.go
deleted file mode 100644
index 1807cf31a1..0000000000
--- a/routers/web/repo/patch.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package repo
-
-import (
- "strings"
-
- git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/repository/files"
-)
-
-const (
- tplPatchFile templates.TplName = "repo/editor/patch"
-)
-
-// NewDiffPatch render create patch page
-func NewDiffPatch(ctx *context.Context) {
- canCommit := renderCommitRights(ctx)
-
- ctx.Data["PageIsPatch"] = true
-
- ctx.Data["commit_summary"] = ""
- ctx.Data["commit_message"] = ""
- if canCommit {
- ctx.Data["commit_choice"] = frmCommitChoiceDirect
- } else {
- ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
- }
- ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx)
- ctx.Data["last_commit"] = ctx.Repo.CommitID
- ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
- ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
-
- ctx.HTML(200, tplPatchFile)
-}
-
-// NewDiffPatchPost response for sending patch page
-func NewDiffPatchPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.EditRepoFileForm)
-
- canCommit := renderCommitRights(ctx)
- branchName := ctx.Repo.BranchName
- if form.CommitChoice == frmCommitChoiceNewBranch {
- branchName = form.NewBranchName
- }
- ctx.Data["PageIsPatch"] = true
- ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
- ctx.Data["FileContent"] = form.Content
- ctx.Data["commit_summary"] = form.CommitSummary
- ctx.Data["commit_message"] = form.CommitMessage
- ctx.Data["commit_choice"] = form.CommitChoice
- ctx.Data["new_branch_name"] = form.NewBranchName
- ctx.Data["last_commit"] = ctx.Repo.CommitID
- ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
-
- if ctx.HasError() {
- ctx.HTML(200, tplPatchFile)
- return
- }
-
- // Cannot commit to a an existing branch if user doesn't have rights
- if branchName == ctx.Repo.BranchName && !canCommit {
- ctx.Data["Err_NewBranchName"] = true
- ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
- ctx.RenderWithErr(ctx.Tr("repo.editor.cannot_commit_to_protected_branch", branchName), tplEditFile, &form)
- return
- }
-
- // CommitSummary is optional in the web form, if empty, give it a default message based on add or update
- // `message` will be both the summary and message combined
- message := strings.TrimSpace(form.CommitSummary)
- if len(message) == 0 {
- message = ctx.Locale.TrString("repo.editor.patch")
- }
-
- form.CommitMessage = strings.TrimSpace(form.CommitMessage)
- if len(form.CommitMessage) > 0 {
- message += "\n\n" + form.CommitMessage
- }
-
- fileResponse, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, &files.ApplyDiffPatchOptions{
- LastCommitID: form.LastCommit,
- OldBranch: ctx.Repo.BranchName,
- NewBranch: branchName,
- Message: message,
- Content: strings.ReplaceAll(form.Content, "\r", ""),
- })
- if err != nil {
- if git_model.IsErrBranchAlreadyExists(err) {
- // User has specified a branch that already exists
- branchErr := err.(git_model.ErrBranchAlreadyExists)
- ctx.Data["Err_NewBranchName"] = true
- ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplEditFile, &form)
- return
- } else if files.IsErrCommitIDDoesNotMatch(err) {
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form)
- return
- }
- ctx.RenderWithErr(ctx.Tr("repo.editor.fail_to_apply_patch", err), tplPatchFile, &form)
- return
- }
-
- if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(ctx, unit.TypePullRequests) {
- ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName))
- } else {
- ctx.Redirect(ctx.Repo.RepoLink + "/commit/" + fileResponse.Commit.SHA)
- }
-}
diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index 346132102f..a57976b4ca 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -39,14 +39,14 @@ const (
// MustEnableRepoProjects check if repo projects are enabled in settings
func MustEnableRepoProjects(ctx *context.Context) {
if unit.TypeProjects.UnitGlobalDisabled() {
- ctx.NotFound("EnableRepoProjects", nil)
+ ctx.NotFound(nil)
return
}
if ctx.Repo.Repository != nil {
projectsUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeProjects)
if !ctx.Repo.CanRead(unit.TypeProjects) || !projectsUnit.ProjectsConfig().IsProjectsAllowed(repo_model.ProjectsModeRepo) {
- ctx.NotFound("MustEnableRepoProjects", nil)
+ ctx.NotFound(nil)
return
}
}
@@ -61,10 +61,7 @@ func Projects(ctx *context.Context) {
isShowClosed := strings.ToLower(ctx.FormTrim("state")) == "closed"
keyword := ctx.FormTrim("q")
repo := ctx.Repo.Repository
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
ctx.Data["OpenCount"] = repo.NumOpenProjects
ctx.Data["ClosedCount"] = repo.NumClosedProjects
@@ -92,6 +89,11 @@ func Projects(ctx *context.Context) {
return
}
+ if err := project_service.LoadIssueNumbersForProjects(ctx, projects, ctx.Doer); err != nil {
+ ctx.ServerError("LoadIssueNumbersForProjects", err)
+ return
+ }
+
for i := range projects {
rctx := renderhelper.NewRenderContextRepoComment(ctx, repo)
projects[i].RenderedContent, err = markdown.RenderString(rctx, projects[i].Description)
@@ -189,14 +191,14 @@ func DeleteProject(ctx *context.Context) {
p, err := project_model.GetProjectByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if project_model.IsErrProjectNotExist(err) {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetProjectByID", err)
}
return
}
if p.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
return
}
@@ -219,14 +221,14 @@ func RenderEditProject(ctx *context.Context) {
p, err := project_model.GetProjectByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if project_model.IsErrProjectNotExist(err) {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetProjectByID", err)
}
return
}
if p.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
return
}
@@ -259,14 +261,14 @@ func EditProjectPost(ctx *context.Context) {
p, err := project_model.GetProjectByID(ctx, projectID)
if err != nil {
if project_model.IsErrProjectNotExist(err) {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetProjectByID", err)
}
return
}
if p.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
return
}
@@ -291,14 +293,14 @@ func ViewProject(ctx *context.Context) {
project, err := project_model.GetProjectByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if project_model.IsErrProjectNotExist(err) {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetProjectByID", err)
}
return
}
if project.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
return
}
@@ -308,18 +310,22 @@ func ViewProject(ctx *context.Context) {
return
}
- labelIDs := issue.PrepareFilterIssueLabels(ctx, ctx.Repo.Repository.ID, ctx.Repo.Owner)
+ preparedLabelFilter := issue.PrepareFilterIssueLabels(ctx, ctx.Repo.Repository.ID, ctx.Repo.Owner)
- assigneeID := ctx.FormInt64("assignee") // TODO: use "optional" but not 0 in the future
+ assigneeID := ctx.FormString("assignee")
- issuesMap, err := issues_model.LoadIssuesFromColumnList(ctx, columns, &issues_model.IssuesOptions{
- LabelIDs: labelIDs,
- AssigneeID: optional.Some(assigneeID),
+ issuesMap, err := project_service.LoadIssuesFromProject(ctx, project, &issues_model.IssuesOptions{
+ RepoIDs: []int64{ctx.Repo.Repository.ID},
+ LabelIDs: preparedLabelFilter.SelectedLabelIDs,
+ AssigneeID: assigneeID,
})
if err != nil {
ctx.ServerError("LoadIssuesOfColumns", err)
return
}
+ for _, column := range columns {
+ column.NumIssues = int64(len(issuesMap[column.ID]))
+ }
if project.CardType != project_model.CardTypeTextOnly {
issuesAttachmentMap := make(map[int64][]*repo_model.Attachment)
@@ -372,8 +378,8 @@ func ViewProject(ctx *context.Context) {
}
// Get the exclusive scope for every label ID
- labelExclusiveScopes := make([]string, 0, len(labelIDs))
- for _, labelID := range labelIDs {
+ labelExclusiveScopes := make([]string, 0, len(preparedLabelFilter.SelectedLabelIDs))
+ for _, labelID := range preparedLabelFilter.SelectedLabelIDs {
foundExclusiveScope := false
for _, label := range labels {
if label.ID == labelID || label.ID == -labelID {
@@ -388,7 +394,7 @@ func ViewProject(ctx *context.Context) {
}
for _, l := range labels {
- l.LoadSelectedLabelsAfterClick(labelIDs, labelExclusiveScopes)
+ l.LoadSelectedLabelsAfterClick(preparedLabelFilter.SelectedLabelIDs, labelExclusiveScopes)
}
ctx.Data["Labels"] = labels
ctx.Data["NumLabels"] = len(labels)
@@ -471,7 +477,7 @@ func DeleteProjectColumn(ctx *context.Context) {
project, err := project_model.GetProjectByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if project_model.IsErrProjectNotExist(err) {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetProjectByID", err)
}
@@ -518,7 +524,7 @@ func AddColumnToProjectPost(ctx *context.Context) {
project, err := project_model.GetProjectForRepoByID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("id"))
if err != nil {
if project_model.IsErrProjectNotExist(err) {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetProjectByID", err)
}
@@ -556,7 +562,7 @@ func checkProjectColumnChangePermissions(ctx *context.Context) (*project_model.P
project, err := project_model.GetProjectByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if project_model.IsErrProjectNotExist(err) {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetProjectByID", err)
}
@@ -642,21 +648,21 @@ func MoveIssues(ctx *context.Context) {
project, err := project_model.GetProjectByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if project_model.IsErrProjectNotExist(err) {
- ctx.NotFound("ProjectNotExist", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetProjectByID", err)
}
return
}
if project.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound("InvalidRepoID", nil)
+ ctx.NotFound(nil)
return
}
column, err := project_model.GetColumn(ctx, ctx.PathParamInt64("columnID"))
if err != nil {
if project_model.IsErrProjectColumnNotExist(err) {
- ctx.NotFound("ProjectColumnNotExist", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetProjectColumn", err)
}
@@ -664,7 +670,7 @@ func MoveIssues(ctx *context.Context) {
}
if column.ProjectID != project.ID {
- ctx.NotFound("ColumnNotInProject", nil)
+ ctx.NotFound(nil)
return
}
@@ -689,7 +695,7 @@ func MoveIssues(ctx *context.Context) {
movedIssues, err := issues_model.GetIssuesByIDs(ctx, issueIDs)
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound("IssueNotExisting", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetIssueByID", err)
}
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index 9f3d1c1b7c..f662152e2e 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -24,8 +24,10 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/emoji"
+ "code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/graceful"
issue_template "code.gitea.io/gitea/modules/issue/template"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -81,7 +83,7 @@ func getRepository(ctx *context.Context, repoID int64) *repo_model.Repository {
repo, err := repo_model.GetRepositoryByID(ctx, repoID)
if err != nil {
if repo_model.IsErrRepoNotExist(err) {
- ctx.NotFound("GetRepositoryByID", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetRepositoryByID", err)
}
@@ -101,7 +103,7 @@ func getRepository(ctx *context.Context, repoID int64) *repo_model.Repository {
unit.TypeCode,
ctx.Repo,
perm)
- ctx.NotFound("getRepository", nil)
+ ctx.NotFound(nil)
return nil
}
return repo
@@ -111,7 +113,7 @@ func getPullInfo(ctx *context.Context) (issue *issues_model.Issue, ok bool) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
- ctx.NotFound("GetIssueByIndex", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("GetIssueByIndex", err)
}
@@ -129,7 +131,7 @@ func getPullInfo(ctx *context.Context) (issue *issues_model.Issue, ok bool) {
ctx.Data["Issue"] = issue
if !issue.IsPull {
- ctx.NotFound("ViewPullCommits", nil)
+ ctx.NotFound(nil)
return nil, false
}
@@ -181,6 +183,7 @@ func setMergeTarget(ctx *context.Context, pull *issues_model.PullRequest) {
// GetPullDiffStats get Pull Requests diff stats
func GetPullDiffStats(ctx *context.Context) {
+ // FIXME: this getPullInfo seems to be a duplicate call with other route handlers
issue, ok := getPullInfo(ctx)
if !ok {
return
@@ -188,34 +191,23 @@ func GetPullDiffStats(ctx *context.Context) {
pull := issue.PullRequest
mergeBaseCommitID := GetMergedBaseCommitID(ctx, issue)
-
if mergeBaseCommitID == "" {
- ctx.NotFound("PullFiles", nil)
- return
+ return // no merge base, do nothing, do not stop the route handler, see below
}
+ // do not report 500 server error to end users if error occurs, otherwise a PR missing ref won't be able to view.
headCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(pull.GetGitRefName())
if err != nil {
- ctx.ServerError("GetRefCommitID", err)
+ log.Error("Failed to GetRefCommitID: %v, repo: %v", err, ctx.Repo.Repository.FullName())
return
}
-
- diffOptions := &gitdiff.DiffOptions{
- BeforeCommitID: mergeBaseCommitID,
- AfterCommitID: headCommitID,
- MaxLines: setting.Git.MaxGitDiffLines,
- MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
- MaxFiles: setting.Git.MaxGitDiffFiles,
- WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)),
- }
-
- diff, err := gitdiff.GetPullDiffStats(ctx.Repo.GitRepo, diffOptions)
+ diffShortStat, err := gitdiff.GetDiffShortStat(ctx.Repo.GitRepo, mergeBaseCommitID, headCommitID)
if err != nil {
- ctx.ServerError("GetPullDiffStats", err)
+ log.Error("Failed to GetDiffShortStat: %v, repo: %v", err, ctx.Repo.Repository.FullName())
return
}
- ctx.Data["Diff"] = diff
+ ctx.Data["DiffShortStat"] = diffShortStat
}
func GetMergedBaseCommitID(ctx *context.Context, issue *issues_model.Issue) string {
@@ -242,7 +234,7 @@ func GetMergedBaseCommitID(ctx *context.Context, issue *issues_model.Issue) stri
}
if commitSHA != "" {
// Get immediate parent of the first commit in the patch, grab history back
- parentCommit, _, err = git.NewCommand(ctx, "rev-list", "-1", "--skip=1").AddDynamicArguments(commitSHA).RunStdString(&git.RunOpts{Dir: ctx.Repo.GitRepo.Path})
+ parentCommit, _, err = git.NewCommand("rev-list", "-1", "--skip=1").AddDynamicArguments(commitSHA).RunStdString(ctx, &git.RunOpts{Dir: ctx.Repo.GitRepo.Path})
if err == nil {
parentCommit = strings.TrimSpace(parentCommit)
}
@@ -300,7 +292,7 @@ func prepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
if len(compareInfo.Commits) != 0 {
sha := compareInfo.Commits[0].ID.String()
- commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, sha, db.ListOptionsAll)
+ commitStatuses, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, sha, db.ListOptionsAll)
if err != nil {
ctx.ServerError("GetLatestCommitStatus", err)
return nil
@@ -356,7 +348,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
defer baseGitRepo.Close()
}
- if !baseGitRepo.IsBranchExist(pull.BaseBranch) {
+ if !gitrepo.IsBranchExist(ctx, pull.BaseRepo, pull.BaseBranch) {
ctx.Data["BaseBranchNotExist"] = true
ctx.Data["IsPullRequestBroken"] = true
ctx.Data["BaseTarget"] = pull.BaseBranch
@@ -367,7 +359,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.ServerError(fmt.Sprintf("GetRefCommitID(%s)", pull.GetGitRefName()), err)
return nil
}
- commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll)
+ commitStatuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll)
if err != nil {
ctx.ServerError("GetLatestCommitStatus", err)
return nil
@@ -413,9 +405,9 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
defer closer.Close()
if pull.Flow == issues_model.PullRequestFlowGithub {
- headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch)
+ headBranchExist = gitrepo.IsBranchExist(ctx, pull.HeadRepo, pull.HeadBranch)
} else {
- headBranchExist = git.IsReferenceExist(ctx, baseGitRepo.Path, pull.GetGitRefName())
+ headBranchExist = gitrepo.IsReferenceExist(ctx, pull.BaseRepo, pull.GetGitRefName())
}
if headBranchExist {
@@ -463,7 +455,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
return nil
}
- commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll)
+ commitStatuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll)
if err != nil {
ctx.ServerError("GetLatestCommitStatus", err)
return nil
@@ -624,14 +616,18 @@ func ViewPullCommits(ctx *context.Context) {
if ctx.Written() {
return
} else if prInfo == nil {
- ctx.NotFound("ViewPullCommits", nil)
+ ctx.NotFound(nil)
return
}
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
- commits := processGitCommits(ctx, prInfo.Commits)
+ commits, err := processGitCommits(ctx, prInfo.Commits)
+ if err != nil {
+ ctx.ServerError("processGitCommits", err)
+ return
+ }
ctx.Data["Commits"] = commits
ctx.Data["CommitCount"] = len(commits)
@@ -668,7 +664,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
if ctx.Written() {
return
} else if prInfo == nil {
- ctx.NotFound("ViewPullFiles", nil)
+ ctx.NotFound(nil)
return
}
@@ -693,7 +689,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
}
if !(foundStartCommit && foundEndCommit) {
- ctx.NotFound("Given SHA1 not found for this PR", nil)
+ ctx.NotFound(nil)
return
}
}
@@ -748,34 +744,40 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
MaxFiles: maxFiles,
WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)),
- FileOnly: fileOnly,
}
if !willShowSpecifiedCommit {
diffOptions.BeforeCommitID = startCommitID
}
- var methodWithError string
- var diff *gitdiff.Diff
+ diff, err := gitdiff.GetDiffForRender(ctx, gitRepo, diffOptions, files...)
+ if err != nil {
+ ctx.ServerError("GetDiff", err)
+ return
+ }
// if we're not logged in or only a single commit (or commit range) is shown we
// have to load only the diff and not get the viewed information
// as the viewed information is designed to be loaded only on latest PR
// diff and if you're signed in.
- if !ctx.IsSigned || willShowSpecifiedCommit || willShowSpecifiedCommitRange {
- diff, err = gitdiff.GetDiff(ctx, gitRepo, diffOptions, files...)
- methodWithError = "GetDiff"
- } else {
- diff, err = gitdiff.SyncAndGetUserSpecificDiff(ctx, ctx.Doer.ID, pull, gitRepo, diffOptions, files...)
- methodWithError = "SyncAndGetUserSpecificDiff"
+ var reviewState *pull_model.ReviewState
+ if ctx.IsSigned && !willShowSpecifiedCommit && !willShowSpecifiedCommitRange {
+ reviewState, err = gitdiff.SyncUserSpecificDiff(ctx, ctx.Doer.ID, pull, gitRepo, diff, diffOptions)
+ if err != nil {
+ ctx.ServerError("SyncUserSpecificDiff", err)
+ return
+ }
}
+
+ diffShortStat, err := gitdiff.GetDiffShortStat(ctx.Repo.GitRepo, startCommitID, endCommitID)
if err != nil {
- ctx.ServerError(methodWithError, err)
+ ctx.ServerError("GetDiffShortStat", err)
return
}
+ ctx.Data["DiffShortStat"] = diffShortStat
ctx.PageData["prReview"] = map[string]any{
- "numberOfFiles": diff.NumFiles,
+ "numberOfFiles": diffShortStat.NumFiles,
"numberOfViewedFiles": diff.NumViewedFiles,
}
@@ -784,18 +786,18 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
return
}
+ allComments := issues_model.CommentList{}
for _, file := range diff.Files {
for _, section := range file.Sections {
for _, line := range section.Lines {
- for _, comment := range line.Comments {
- if err := comment.LoadAttachments(ctx); err != nil {
- ctx.ServerError("LoadAttachments", err)
- return
- }
- }
+ allComments = append(allComments, line.Comments...)
}
}
}
+ if err := allComments.LoadAttachments(ctx); err != nil {
+ ctx.ServerError("LoadAttachments", err)
+ return
+ }
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pull.BaseRepoID, pull.BaseBranch)
if err != nil {
@@ -812,8 +814,27 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
}
}
+ if !fileOnly {
+ // note: use mergeBase is set to false because we already have the merge base from the pull request info
+ diffTree, err := gitdiff.GetDiffTree(ctx, gitRepo, false, startCommitID, endCommitID)
+ if err != nil {
+ ctx.ServerError("GetDiffTree", err)
+ return
+ }
+ var filesViewedState map[string]pull_model.ViewedState
+ if reviewState != nil {
+ filesViewedState = reviewState.UpdatedFiles
+ }
+
+ renderedIconPool := fileicon.NewRenderedIconPool()
+ ctx.PageData["DiffFileTree"] = transformDiffTreeForWeb(renderedIconPool, diffTree, filesViewedState)
+ ctx.PageData["FolderIcon"] = fileicon.RenderEntryIconHTML(renderedIconPool, fileicon.EntryInfoFolder())
+ ctx.PageData["FolderOpenIcon"] = fileicon.RenderEntryIconHTML(renderedIconPool, fileicon.EntryInfoFolderOpen())
+ ctx.Data["FileIconPoolHTML"] = renderedIconPool.RenderToHTML()
+ }
+
ctx.Data["Diff"] = diff
- ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0
+ ctx.Data["DiffNotAvailable"] = diffShortStat.NumFiles == 0
baseCommit, err := ctx.Repo.GitRepo.GetCommit(startCommitID)
if err != nil {
@@ -931,11 +952,11 @@ func UpdatePullRequest(ctx *context.Context) {
return
}
if issue.IsClosed {
- ctx.NotFound("MergePullRequest", nil)
+ ctx.NotFound(nil)
return
}
if issue.PullRequest.HasMerged {
- ctx.NotFound("MergePullRequest", nil)
+ ctx.NotFound(nil)
return
}
@@ -966,7 +987,9 @@ func UpdatePullRequest(ctx *context.Context) {
// default merge commit message
message := fmt.Sprintf("Merge branch '%s' into %s", issue.PullRequest.BaseBranch, issue.PullRequest.HeadBranch)
- if err = pull_service.Update(ctx, issue.PullRequest, ctx.Doer, message, rebase); err != nil {
+ // The update process should not be cancelled by the user
+ // so we set the context to be a background context
+ if err = pull_service.Update(graceful.GetManager().ShutdownContext(), issue.PullRequest, ctx.Doer, message, rebase); err != nil {
if pull_service.IsErrMergeConflicts(err) {
conflictError := err.(pull_service.ErrMergeConflicts)
flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{
@@ -1038,7 +1061,7 @@ func MergePullRequest(ctx *context.Context) {
} else {
ctx.JSONError(ctx.Tr("repo.issues.closed_title"))
}
- case errors.Is(err, pull_service.ErrUserNotAllowedToMerge):
+ case errors.Is(err, pull_service.ErrNoPermissionToMerge):
ctx.JSONError(ctx.Tr("repo.pulls.update_not_allowed"))
case errors.Is(err, pull_service.ErrHasMerged):
ctx.JSONError(ctx.Tr("repo.pulls.has_merged"))
@@ -1046,7 +1069,7 @@ func MergePullRequest(ctx *context.Context) {
ctx.JSONError(ctx.Tr("repo.pulls.no_merge_wip"))
case errors.Is(err, pull_service.ErrNotMergeableState):
ctx.JSONError(ctx.Tr("repo.pulls.no_merge_not_ready"))
- case pull_service.IsErrDisallowedToMerge(err):
+ case errors.Is(err, pull_service.ErrNotReadyToMerge):
ctx.JSONError(ctx.Tr("repo.pulls.no_merge_not_ready"))
case asymkey_service.IsErrWontSign(err):
ctx.JSONError(err.Error()) // has no translation ...
@@ -1097,7 +1120,7 @@ func MergePullRequest(ctx *context.Context) {
// delete all scheduled auto merges
_ = pull_model.DeleteScheduledAutoMerge(ctx, pr.ID)
// schedule auto merge
- scheduled, err := automerge.ScheduleAutoMerge(ctx, ctx.Doer, pr, repo_model.MergeStyle(form.Do), message)
+ scheduled, err := automerge.ScheduleAutoMerge(ctx, ctx.Doer, pr, repo_model.MergeStyle(form.Do), message, form.DeleteBranchAfterMerge)
if err != nil {
ctx.ServerError("ScheduleAutoMerge", err)
return
@@ -1235,13 +1258,23 @@ func CancelAutoMergePullRequest(ctx *context.Context) {
}
func stopTimerIfAvailable(ctx *context.Context, user *user_model.User, issue *issues_model.Issue) error {
- if issues_model.StopwatchExists(ctx, user.ID, issue.ID) {
- if err := issues_model.CreateOrStopIssueStopwatch(ctx, user, issue); err != nil {
- return err
+ _, err := issues_model.FinishIssueStopwatch(ctx, user, issue)
+ return err
+}
+
+func PullsNewRedirect(ctx *context.Context) {
+ branch := ctx.PathParam("*")
+ redirectRepo := ctx.Repo.Repository
+ repo := ctx.Repo.Repository
+ if repo.IsFork {
+ if err := repo.GetBaseRepo(ctx); err != nil {
+ ctx.ServerError("GetBaseRepo", err)
+ return
}
+ redirectRepo = repo.BaseRepo
+ branch = fmt.Sprintf("%s:%s", repo.OwnerName, branch)
}
-
- return nil
+ ctx.Redirect(fmt.Sprintf("%s/compare/%s...%s?expand=1", redirectRepo.Link(), util.PathEscapeSegments(redirectRepo.DefaultBranch), util.PathEscapeSegments(branch)))
}
// CompareAndPullRequestPost response for creating pull request
@@ -1261,11 +1294,6 @@ func CompareAndPullRequestPost(ctx *context.Context) {
)
ci := ParseCompareInfo(ctx)
- defer func() {
- if ci != nil && ci.HeadGitRepo != nil {
- ci.HeadGitRepo.Close()
- }
- }()
if ctx.Written() {
return
}
@@ -1334,7 +1362,7 @@ func CompareAndPullRequestPost(ctx *context.Context) {
if err := pull_service.NewPullRequest(ctx, prOpts); err != nil {
switch {
case repo_model.IsErrUserDoesNotHaveAccessToRepo(err):
- ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err.Error())
+ ctx.HTTPError(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err.Error())
case git.IsErrPushRejected(err):
pushrejErr := err.(*git.ErrPushRejected)
message := pushrejErr.Message
@@ -1405,7 +1433,7 @@ func CleanUpPullRequest(ctx *context.Context) {
// Don't cleanup unmerged and unclosed PRs and agit PRs
if !pr.HasMerged && !issue.IsClosed && pr.Flow != issues_model.PullRequestFlowGithub {
- ctx.NotFound("CleanUpPullRequest", nil)
+ ctx.NotFound(nil)
return
}
@@ -1416,7 +1444,7 @@ func CleanUpPullRequest(ctx *context.Context) {
return
}
if exist {
- ctx.NotFound("CleanUpPullRequest", nil)
+ ctx.NotFound(nil)
return
}
@@ -1425,7 +1453,7 @@ func CleanUpPullRequest(ctx *context.Context) {
return
} else if pr.HeadRepo == nil {
// Forked repository has already been deleted
- ctx.NotFound("CleanUpPullRequest", nil)
+ ctx.NotFound(nil)
return
} else if err = pr.LoadBaseRepo(ctx); err != nil {
ctx.ServerError("LoadBaseRepo", err)
@@ -1437,7 +1465,7 @@ func CleanUpPullRequest(ctx *context.Context) {
if err := repo_service.CanDeleteBranch(ctx, pr.HeadRepo, pr.HeadBranch, ctx.Doer); err != nil {
if errors.Is(err, util.ErrPermissionDenied) {
- ctx.NotFound("CanDeleteBranch", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("CanDeleteBranch", err)
}
@@ -1504,12 +1532,7 @@ func CleanUpPullRequest(ctx *context.Context) {
func deleteBranch(ctx *context.Context, pr *issues_model.PullRequest, gitRepo *git.Repository) {
fullBranchName := pr.HeadRepo.FullName() + ":" + pr.HeadBranch
- if err := pull_service.RetargetChildrenOnMerge(ctx, ctx.Doer, pr); err != nil {
- ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", fullBranchName))
- return
- }
-
- if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, gitRepo, pr.HeadBranch); err != nil {
+ if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, gitRepo, pr.HeadBranch, pr); err != nil {
switch {
case git.IsErrBranchNotExist(err):
ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", fullBranchName))
@@ -1524,11 +1547,6 @@ func deleteBranch(ctx *context.Context, pr *issues_model.PullRequest, gitRepo *g
return
}
- if err := issues_model.AddDeletePRBranchComment(ctx, ctx.Doer, pr.BaseRepo, pr.IssueID, pr.HeadBranch); err != nil {
- // Do not fail here as branch has already been deleted
- log.Error("DeleteBranch: %v", err)
- }
-
ctx.Flash.Success(ctx.Tr("repo.branch.deletion_success", fullBranchName))
}
@@ -1547,7 +1565,7 @@ func DownloadPullDiffOrPatch(ctx *context.Context, patch bool) {
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound("GetPullRequestByIndex", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("GetPullRequestByIndex", err)
}
@@ -1570,18 +1588,18 @@ func UpdatePullRequestTarget(ctx *context.Context) {
}
pr := issue.PullRequest
if !issue.IsPull {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
if !ctx.IsSigned || (!issue.IsPoster(ctx.Doer.ID) && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
targetBranch := ctx.FormTrim("target_branch")
if len(targetBranch) == 0 {
- ctx.Error(http.StatusNoContent)
+ ctx.HTTPError(http.StatusNoContent)
return
}
@@ -1640,7 +1658,7 @@ func SetAllowEdits(ctx *context.Context) {
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
- ctx.NotFound("GetPullRequestByIndex", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("GetPullRequestByIndex", err)
}
@@ -1649,7 +1667,7 @@ func SetAllowEdits(ctx *context.Context) {
if err := pull_service.SetAllowEdits(ctx, ctx.Doer, pr, form.AllowMaintainerEdit); err != nil {
if errors.Is(err, pull_service.ErrUserHasNoPermissionForAction) {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
ctx.ServerError("SetAllowEdits", err)
diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go
index 3e9e615b15..929e131d61 100644
--- a/routers/web/repo/pull_review.go
+++ b/routers/web/repo/pull_review.go
@@ -133,7 +133,7 @@ func UpdateResolveConversation(ctx *context.Context) {
}
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
- ctx.NotFound("comment's repoID is incorrect", errors.New("comment's repoID is incorrect"))
+ ctx.NotFound(errors.New("comment's repoID is incorrect"))
return
}
@@ -143,12 +143,12 @@ func UpdateResolveConversation(ctx *context.Context) {
return
}
if !permResult {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
if !comment.Issue.IsPull {
- ctx.Error(http.StatusBadRequest)
+ ctx.HTTPError(http.StatusBadRequest)
return
}
@@ -159,7 +159,7 @@ func UpdateResolveConversation(ctx *context.Context) {
return
}
} else {
- ctx.Error(http.StatusBadRequest)
+ ctx.HTTPError(http.StatusBadRequest)
return
}
@@ -209,12 +209,13 @@ func renderConversation(ctx *context.Context, comment *issues_model.Comment, ori
return user_service.CanBlockUser(ctx, ctx.Doer, blocker, blockee)
}
- if origin == "diff" {
+ switch origin {
+ case "diff":
ctx.HTML(http.StatusOK, tplDiffConversation)
- } else if origin == "timeline" {
+ case "timeline":
ctx.HTML(http.StatusOK, tplTimelineConversation)
- } else {
- ctx.Error(http.StatusBadRequest, "Unknown origin: "+origin)
+ default:
+ ctx.HTTPError(http.StatusBadRequest, "Unknown origin: "+origin)
}
}
diff --git a/routers/web/repo/pull_review_test.go b/routers/web/repo/pull_review_test.go
index 8344ff4091..3d0997ab4d 100644
--- a/routers/web/repo/pull_review_test.go
+++ b/routers/web/repo/pull_review_test.go
@@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/services/pull"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestRenderConversation(t *testing.T) {
@@ -41,19 +42,16 @@ func TestRenderConversation(t *testing.T) {
var preparedComment *issues_model.Comment
run("prepare", func(t *testing.T, ctx *context.Context, resp *httptest.ResponseRecorder) {
comment, err := pull.CreateCodeComment(ctx, pr.Issue.Poster, ctx.Repo.GitRepo, pr.Issue, 1, "content", "", false, 0, pr.HeadCommitID, nil)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
+
comment.Invalidated = true
err = issues_model.UpdateCommentInvalidate(ctx, comment)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
+
preparedComment = comment
})
- if !assert.NotNil(t, preparedComment) {
- return
- }
+ require.NotNil(t, preparedComment)
+
run("diff with outdated", func(t *testing.T, ctx *context.Context, resp *httptest.ResponseRecorder) {
ctx.Data["ShowOutdatedComments"] = true
renderConversation(ctx, preparedComment, "diff")
diff --git a/routers/web/repo/recent_commits.go b/routers/web/repo/recent_commits.go
index dc72081900..2660116062 100644
--- a/routers/web/repo/recent_commits.go
+++ b/routers/web/repo/recent_commits.go
@@ -4,12 +4,10 @@
package repo
import (
- "errors"
"net/http"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/services/context"
- contributors_service "code.gitea.io/gitea/services/repository"
)
const (
@@ -26,16 +24,3 @@ func RecentCommits(ctx *context.Context) {
ctx.HTML(http.StatusOK, tplRecentCommits)
}
-
-// RecentCommitsData returns JSON of recent commits data
-func RecentCommitsData(ctx *context.Context) {
- if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.CommitID); err != nil {
- if errors.Is(err, contributors_service.ErrAwaitGeneration) {
- ctx.Status(http.StatusAccepted)
- return
- }
- ctx.ServerError("RecentCommitsData", err)
- } else {
- ctx.JSON(http.StatusOK, contributorStats["total"].Weeks)
- }
-}
diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index 5b099fe8d4..36ea20c23e 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"net/http"
+ "strconv"
"strings"
"code.gitea.io/gitea/models/db"
@@ -17,7 +18,7 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
@@ -102,7 +103,7 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)
releaseInfos := make([]*ReleaseInfo, 0, len(releases))
for _, r := range releases {
if r.Publisher, ok = cacheUsers[r.PublisherID]; !ok {
- r.Publisher, err = user_model.GetUserByID(ctx, r.PublisherID)
+ r.Publisher, err = user_model.GetPossibleUserByID(ctx, r.PublisherID)
if err != nil {
if user_model.IsErrUserNotExist(err) {
r.Publisher = user_model.NewGhostUser()
@@ -113,7 +114,9 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)
cacheUsers[r.PublisherID] = r.Publisher
}
- rctx := renderhelper.NewRenderContextRepoComment(ctx, r.Repo)
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, r.Repo, renderhelper.RepoCommentOptions{
+ FootnoteContextID: strconv.FormatInt(r.ID, 10),
+ })
r.RenderedNote, err = markdown.RenderString(rctx, r.Note)
if err != nil {
return nil, err
@@ -130,7 +133,7 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)
}
if canReadActions {
- statuses, _, err := git_model.GetLatestCommitStatus(ctx, r.Repo.ID, r.Sha1, db.ListOptionsAll)
+ statuses, err := git_model.GetLatestCommitStatus(ctx, r.Repo.ID, r.Sha1, db.ListOptionsAll)
if err != nil {
return nil, err
}
@@ -149,8 +152,6 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)
func Releases(ctx *context.Context) {
ctx.Data["PageIsReleaseList"] = true
ctx.Data["Title"] = ctx.Tr("repo.release.releases")
- ctx.Data["IsViewBranch"] = false
- ctx.Data["IsViewTag"] = true
listOptions := db.ListOptions{
Page: ctx.FormInt("page"),
@@ -195,8 +196,6 @@ func Releases(ctx *context.Context) {
func TagsList(ctx *context.Context) {
ctx.Data["PageIsTagList"] = true
ctx.Data["Title"] = ctx.Tr("repo.release.tags")
- ctx.Data["IsViewBranch"] = false
- ctx.Data["IsViewTag"] = true
ctx.Data["CanCreateRelease"] = ctx.Repo.CanWrite(unit.TypeReleases) && !ctx.Repo.Repository.IsArchived
namePattern := ctx.FormTrim("q")
@@ -290,7 +289,7 @@ func SingleRelease(ctx *context.Context) {
return
}
if len(releases) != 1 {
- ctx.NotFound("SingleRelease", err)
+ ctx.NotFound(err)
return
}
@@ -300,6 +299,7 @@ func SingleRelease(ctx *context.Context) {
}
ctx.Data["PageIsSingleTag"] = release.IsTag
+ ctx.Data["SingleReleaseTagName"] = release.TagName
if release.IsTag {
ctx.Data["Title"] = release.TagName
} else {
@@ -315,7 +315,7 @@ func LatestRelease(ctx *context.Context) {
release, err := repo_model.GetLatestReleaseByRepoID(ctx, ctx.Repo.Repository.ID)
if err != nil {
if repo_model.IsErrReleaseNotExist(err) {
- ctx.NotFound("LatestRelease", err)
+ ctx.NotFound(err)
return
}
ctx.ServerError("GetLatestReleaseByRepoID", err)
@@ -330,12 +330,42 @@ func LatestRelease(ctx *context.Context) {
ctx.Redirect(release.Link())
}
-// NewRelease render creating or edit release page
-func NewRelease(ctx *context.Context) {
+func newReleaseCommon(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
ctx.Data["PageIsReleaseList"] = true
+
+ tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
+ if err != nil {
+ ctx.ServerError("GetTagNamesByRepoID", err)
+ return
+ }
+ ctx.Data["Tags"] = tags
+
+ ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
+ assigneeUsers, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
+ if err != nil {
+ ctx.ServerError("GetRepoAssignees", err)
+ return
+ }
+ ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
+
+ upload.AddUploadContext(ctx, "release")
+
+ PrepareBranchList(ctx) // for New Release page
+}
+
+// NewRelease render creating or edit release page
+func NewRelease(ctx *context.Context) {
+ newReleaseCommon(ctx)
+ if ctx.Written() {
+ return
+ }
+
+ ctx.Data["ShowCreateTagOnlyButton"] = true
+
+ // pre-fill the form with the tag name, target branch and the existing release (if exists)
ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch
- if tagName := ctx.FormString("tag"); len(tagName) > 0 {
+ if tagName := ctx.FormString("tag"); tagName != "" {
rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, tagName)
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
ctx.ServerError("GetRelease", err)
@@ -344,122 +374,104 @@ func NewRelease(ctx *context.Context) {
if rel != nil {
rel.Repo = ctx.Repo.Repository
- if err := rel.LoadAttributes(ctx); err != nil {
+ if err = rel.LoadAttributes(ctx); err != nil {
ctx.ServerError("LoadAttributes", err)
return
}
+ ctx.Data["ShowCreateTagOnlyButton"] = false
ctx.Data["tag_name"] = rel.TagName
- if rel.Target != "" {
- ctx.Data["tag_target"] = rel.Target
- }
+ ctx.Data["tag_target"] = util.IfZero(rel.Target, ctx.Repo.Repository.DefaultBranch)
ctx.Data["title"] = rel.Title
ctx.Data["content"] = rel.Note
ctx.Data["attachments"] = rel.Attachments
}
}
- ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
- assigneeUsers, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
- if err != nil {
- ctx.ServerError("GetRepoAssignees", err)
- return
- }
- ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
-
- upload.AddUploadContext(ctx, "release")
-
- // For New Release page
- PrepareBranchList(ctx)
- if ctx.Written() {
- return
- }
-
- tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
- if err != nil {
- ctx.ServerError("GetTagNamesByRepoID", err)
- return
- }
- ctx.Data["Tags"] = tags
ctx.HTML(http.StatusOK, tplReleaseNew)
}
// NewReleasePost response for creating a release
func NewReleasePost(ctx *context.Context) {
+ newReleaseCommon(ctx)
+ if ctx.Written() {
+ return
+ }
+
form := web.GetForm(ctx).(*forms.NewReleaseForm)
- ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
- ctx.Data["PageIsReleaseList"] = true
- tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
- if err != nil {
- ctx.ServerError("GetTagNamesByRepoID", err)
+ // first, check whether the release exists, and prepare "ShowCreateTagOnlyButton"
+ // the logic should be done before the form error check to make the tmpl has correct variables
+ rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, form.TagName)
+ if err != nil && !repo_model.IsErrReleaseNotExist(err) {
+ ctx.ServerError("GetRelease", err)
return
}
- ctx.Data["Tags"] = tags
+ // We should still show the "tag only" button if the user clicks it, no matter the release exists or not.
+ // Because if error occurs, end users need to have the chance to edit the name and submit the form with "tag-only" again.
+ // It is still not completely right, because there could still be cases like this:
+ // * user visit "new release" page, see the "tag only" button
+ // * input something, click other buttons but not "tag only"
+ // * error occurs, the "new release" page is rendered again, but the "tag only" button is gone
+ // Such cases are not able to be handled by current code, it needs frontend code to toggle the "tag-only" button if the input changes.
+ // Or another choice is "always show the tag-only button" if error occurs.
+ ctx.Data["ShowCreateTagOnlyButton"] = form.TagOnly || rel == nil
+
+ // do some form checks
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplReleaseNew)
return
}
- if !ctx.Repo.GitRepo.IsBranchExist(form.Target) {
+ if !gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, form.Target) {
ctx.RenderWithErr(ctx.Tr("form.target_branch_not_exist"), tplReleaseNew, &form)
return
}
- // Title of release cannot be empty
- if len(form.TagOnly) == 0 && len(form.Title) == 0 {
+ if !form.TagOnly && form.Title == "" {
+ // if not "tag only", then the title of the release cannot be empty
ctx.RenderWithErr(ctx.Tr("repo.release.title_empty"), tplReleaseNew, &form)
return
}
- var attachmentUUIDs []string
- if setting.Attachment.Enabled {
- attachmentUUIDs = form.Files
- }
-
- rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, form.TagName)
- if err != nil {
- if !repo_model.IsErrReleaseNotExist(err) {
- ctx.ServerError("GetRelease", err)
- return
- }
-
- msg := ""
- if len(form.Title) > 0 && form.AddTagMsg {
- msg = form.Title + "\n\n" + form.Content
+ handleTagReleaseError := func(err error) {
+ ctx.Data["Err_TagName"] = true
+ switch {
+ case release_service.IsErrTagAlreadyExists(err):
+ ctx.RenderWithErr(ctx.Tr("repo.branch.tag_collision", form.TagName), tplReleaseNew, &form)
+ case repo_model.IsErrReleaseAlreadyExist(err):
+ ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
+ case release_service.IsErrInvalidTagName(err):
+ ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_invalid"), tplReleaseNew, &form)
+ case release_service.IsErrProtectedTagName(err):
+ ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_protected"), tplReleaseNew, &form)
+ default:
+ ctx.ServerError("handleTagReleaseError", err)
}
+ }
- if len(form.TagOnly) > 0 {
- if err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, form.Target, form.TagName, msg); err != nil {
- if release_service.IsErrTagAlreadyExists(err) {
- e := err.(release_service.ErrTagAlreadyExists)
- ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName))
- ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
- return
- }
-
- if release_service.IsErrInvalidTagName(err) {
- ctx.Flash.Error(ctx.Tr("repo.release.tag_name_invalid"))
- ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
- return
- }
-
- if release_service.IsErrProtectedTagName(err) {
- ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
- ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
- return
- }
-
- ctx.ServerError("release_service.CreateNewTag", err)
- return
- }
+ // prepare the git message for creating a new tag
+ newTagMsg := ""
+ if form.Title != "" && form.AddTagMsg {
+ newTagMsg = form.Title + "\n\n" + form.Content
+ }
- ctx.Flash.Success(ctx.Tr("repo.tag.create_success", form.TagName))
- ctx.Redirect(ctx.Repo.RepoLink + "/src/tag/" + util.PathEscapeSegments(form.TagName))
+ // no release, and tag only
+ if rel == nil && form.TagOnly {
+ if err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, form.Target, form.TagName, newTagMsg); err != nil {
+ handleTagReleaseError(err)
return
}
+ ctx.Flash.Success(ctx.Tr("repo.tag.create_success", form.TagName))
+ ctx.Redirect(ctx.Repo.RepoLink + "/src/tag/" + util.PathEscapeSegments(form.TagName))
+ return
+ }
+
+ attachmentUUIDs := util.Iif(setting.Attachment.Enabled, form.Files, nil)
+ // no existing release, create a new release
+ if rel == nil {
rel = &repo_model.Release{
RepoID: ctx.Repo.Repository.ID,
Repo: ctx.Repo.Repository,
@@ -469,48 +481,39 @@ func NewReleasePost(ctx *context.Context) {
TagName: form.TagName,
Target: form.Target,
Note: form.Content,
- IsDraft: len(form.Draft) > 0,
+ IsDraft: form.Draft,
IsPrerelease: form.Prerelease,
IsTag: false,
}
-
- if err = release_service.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs, msg); err != nil {
- ctx.Data["Err_TagName"] = true
- switch {
- case repo_model.IsErrReleaseAlreadyExist(err):
- ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
- case release_service.IsErrInvalidTagName(err):
- ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_invalid"), tplReleaseNew, &form)
- case release_service.IsErrProtectedTagName(err):
- ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_protected"), tplReleaseNew, &form)
- default:
- ctx.ServerError("CreateRelease", err)
- }
- return
- }
- } else {
- if !rel.IsTag {
- ctx.Data["Err_TagName"] = true
- ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
+ if err = release_service.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs, newTagMsg); err != nil {
+ handleTagReleaseError(err)
return
}
+ ctx.Redirect(ctx.Repo.RepoLink + "/releases")
+ return
+ }
- rel.Title = form.Title
- rel.Note = form.Content
- rel.Target = form.Target
- rel.IsDraft = len(form.Draft) > 0
- rel.IsPrerelease = form.Prerelease
- rel.PublisherID = ctx.Doer.ID
- rel.IsTag = false
-
- if err = release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, attachmentUUIDs, nil, nil); err != nil {
- ctx.Data["Err_TagName"] = true
- ctx.ServerError("UpdateRelease", err)
- return
- }
+ // tag exists, try to convert it to a real release
+ // old logic: if the release is not a tag (it is a real release), do not update it on the "new release" page
+ // add new logic: if tag-only, do not convert the tag to a release
+ if form.TagOnly || !rel.IsTag {
+ ctx.Data["Err_TagName"] = true
+ ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
+ return
}
- log.Trace("Release created: %s/%s:%s", ctx.Doer.LowerName, ctx.Repo.Repository.Name, form.TagName)
+ // convert a tag to a real release (set is_tag=false)
+ rel.Title = form.Title
+ rel.Note = form.Content
+ rel.Target = form.Target
+ rel.IsDraft = form.Draft
+ rel.IsPrerelease = form.Prerelease
+ rel.PublisherID = ctx.Doer.ID
+ rel.IsTag = false
+ if err = release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, attachmentUUIDs, nil, nil); err != nil {
+ handleTagReleaseError(err)
+ return
+ }
ctx.Redirect(ctx.Repo.RepoLink + "/releases")
}
@@ -526,7 +529,7 @@ func EditRelease(ctx *context.Context) {
rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, tagName)
if err != nil {
if repo_model.IsErrReleaseNotExist(err) {
- ctx.NotFound("GetRelease", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("GetRelease", err)
}
@@ -534,7 +537,7 @@ func EditRelease(ctx *context.Context) {
}
ctx.Data["ID"] = rel.ID
ctx.Data["tag_name"] = rel.TagName
- ctx.Data["tag_target"] = rel.Target
+ ctx.Data["tag_target"] = util.IfZero(rel.Target, ctx.Repo.Repository.DefaultBranch)
ctx.Data["title"] = rel.Title
ctx.Data["content"] = rel.Note
ctx.Data["prerelease"] = rel.IsPrerelease
@@ -569,18 +572,18 @@ func EditReleasePost(ctx *context.Context) {
rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, tagName)
if err != nil {
if repo_model.IsErrReleaseNotExist(err) {
- ctx.NotFound("GetRelease", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("GetRelease", err)
}
return
}
if rel.IsTag {
- ctx.NotFound("GetRelease", err)
+ ctx.NotFound(err)
return
}
ctx.Data["tag_name"] = rel.TagName
- ctx.Data["tag_target"] = rel.Target
+ ctx.Data["tag_target"] = util.IfZero(rel.Target, ctx.Repo.Repository.DefaultBranch)
ctx.Data["title"] = rel.Title
ctx.Data["content"] = rel.Note
ctx.Data["prerelease"] = rel.IsPrerelease
@@ -640,7 +643,7 @@ func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) {
rel, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, ctx.FormInt64("id"))
if err != nil {
if repo_model.IsErrReleaseNotExist(err) {
- ctx.NotFound("GetReleaseForRepoByID", err)
+ ctx.NotFound(err)
} else {
ctx.Flash.Error("DeleteReleaseByID: " + err.Error())
redirect()
diff --git a/routers/web/repo/release_test.go b/routers/web/repo/release_test.go
index 7ebea4c3fb..9f49fc7500 100644
--- a/routers/web/repo/release_test.go
+++ b/routers/web/repo/release_test.go
@@ -11,60 +11,135 @@ import (
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/contexttest"
"code.gitea.io/gitea/services/forms"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestNewReleasePost(t *testing.T) {
- for _, testCase := range []struct {
- RepoID int64
- UserID int64
- TagName string
- Form forms.NewReleaseForm
- }{
- {
- RepoID: 1,
- UserID: 2,
- TagName: "v1.1", // pre-existing tag
- Form: forms.NewReleaseForm{
- TagName: "newtag",
- Target: "master",
- Title: "title",
- Content: "content",
- },
- },
- {
- RepoID: 1,
- UserID: 2,
- TagName: "newtag",
- Form: forms.NewReleaseForm{
- TagName: "newtag",
- Target: "master",
- Title: "title",
- Content: "content",
- },
- },
- } {
- unittest.PrepareTestEnv(t)
+ unittest.PrepareTestEnv(t)
+ get := func(t *testing.T, tagName string) *context.Context {
+ ctx, _ := contexttest.MockContext(t, "user2/repo1/releases/new?tag="+tagName)
+ contexttest.LoadUser(t, ctx, 2)
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadGitRepo(t, ctx)
+ defer ctx.Repo.GitRepo.Close()
+ NewRelease(ctx)
+ return ctx
+ }
+
+ t.Run("NewReleasePage", func(t *testing.T) {
+ ctx := get(t, "v1.1")
+ assert.Empty(t, ctx.Data["ShowCreateTagOnlyButton"])
+ ctx = get(t, "new-tag-name")
+ assert.NotEmpty(t, ctx.Data["ShowCreateTagOnlyButton"])
+ })
+
+ post := func(t *testing.T, form forms.NewReleaseForm) *context.Context {
ctx, _ := contexttest.MockContext(t, "user2/repo1/releases/new")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
contexttest.LoadGitRepo(t, ctx)
- web.SetForm(ctx, &testCase.Form)
+ defer ctx.Repo.GitRepo.Close()
+ web.SetForm(ctx, &form)
NewReleasePost(ctx)
- unittest.AssertExistsAndLoadBean(t, &repo_model.Release{
- RepoID: 1,
- PublisherID: 2,
- TagName: testCase.Form.TagName,
- Target: testCase.Form.Target,
- Title: testCase.Form.Title,
- Note: testCase.Form.Content,
- }, unittest.Cond("is_draft=?", len(testCase.Form.Draft) > 0))
- ctx.Repo.GitRepo.Close()
+ return ctx
+ }
+
+ loadRelease := func(t *testing.T, tagName string) *repo_model.Release {
+ return unittest.GetBean(t, &repo_model.Release{}, unittest.Cond("repo_id=1 AND tag_name=?", tagName))
}
+
+ t.Run("NewTagRelease", func(t *testing.T) {
+ post(t, forms.NewReleaseForm{
+ TagName: "newtag",
+ Target: "master",
+ Title: "title",
+ Content: "content",
+ })
+ rel := loadRelease(t, "newtag")
+ require.NotNil(t, rel)
+ assert.False(t, rel.IsTag)
+ assert.Equal(t, "master", rel.Target)
+ assert.Equal(t, "title", rel.Title)
+ assert.Equal(t, "content", rel.Note)
+ })
+
+ t.Run("ReleaseExistsDoUpdate(non-tag)", func(t *testing.T) {
+ ctx := post(t, forms.NewReleaseForm{
+ TagName: "v1.1",
+ Target: "master",
+ Title: "updated-title",
+ Content: "updated-content",
+ })
+ rel := loadRelease(t, "v1.1")
+ require.NotNil(t, rel)
+ assert.False(t, rel.IsTag)
+ assert.Equal(t, "testing-release", rel.Title)
+ assert.NotEmpty(t, ctx.Flash.ErrorMsg)
+ })
+
+ t.Run("ReleaseExistsDoUpdate(tag-only)", func(t *testing.T) {
+ ctx := post(t, forms.NewReleaseForm{
+ TagName: "delete-tag", // a strange name, but it is the only "is_tag=true" fixture
+ Target: "master",
+ Title: "updated-title",
+ Content: "updated-content",
+ TagOnly: true,
+ })
+ rel := loadRelease(t, "delete-tag")
+ require.NotNil(t, rel)
+ assert.True(t, rel.IsTag) // the record should not be updated because the request is "tag-only". TODO: need to improve the logic?
+ assert.Equal(t, "delete-tag", rel.Title)
+ assert.NotEmpty(t, ctx.Flash.ErrorMsg)
+ assert.NotEmpty(t, ctx.Data["ShowCreateTagOnlyButton"]) // still show the "tag-only" button
+ })
+
+ t.Run("ReleaseExistsDoUpdate(tag-release)", func(t *testing.T) {
+ ctx := post(t, forms.NewReleaseForm{
+ TagName: "delete-tag", // a strange name, but it is the only "is_tag=true" fixture
+ Target: "master",
+ Title: "updated-title",
+ Content: "updated-content",
+ })
+ rel := loadRelease(t, "delete-tag")
+ require.NotNil(t, rel)
+ assert.False(t, rel.IsTag) // the tag has been "updated" to be a real "release"
+ assert.Equal(t, "updated-title", rel.Title)
+ assert.Empty(t, ctx.Flash.ErrorMsg)
+ })
+
+ t.Run("TagOnly", func(t *testing.T) {
+ ctx := post(t, forms.NewReleaseForm{
+ TagName: "new-tag-only",
+ Target: "master",
+ Title: "title",
+ Content: "content",
+ TagOnly: true,
+ })
+ rel := loadRelease(t, "new-tag-only")
+ require.NotNil(t, rel)
+ assert.True(t, rel.IsTag)
+ assert.Empty(t, ctx.Flash.ErrorMsg)
+ })
+
+ t.Run("TagOnlyConflict", func(t *testing.T) {
+ ctx := post(t, forms.NewReleaseForm{
+ TagName: "v1.1",
+ Target: "master",
+ Title: "title",
+ Content: "content",
+ TagOnly: true,
+ })
+ rel := loadRelease(t, "v1.1")
+ require.NotNil(t, rel)
+ assert.False(t, rel.IsTag)
+ assert.NotEmpty(t, ctx.Flash.ErrorMsg)
+ })
}
func TestCalReleaseNumCommitsBehind(t *testing.T) {
diff --git a/routers/web/repo/render.go b/routers/web/repo/render.go
index 856425ae35..689174dfa1 100644
--- a/routers/web/repo/render.go
+++ b/routers/web/repo/render.go
@@ -21,10 +21,16 @@ import (
// RenderFile renders a file by repos path
func RenderFile(ctx *context.Context) {
- blob, err := ctx.Repo.Commit.GetBlobByPath(ctx.Repo.TreePath)
+ var blob *git.Blob
+ var err error
+ if ctx.Repo.TreePath != "" {
+ blob, err = ctx.Repo.Commit.GetBlobByPath(ctx.Repo.TreePath)
+ } else {
+ blob, err = ctx.Repo.GitRepo.GetBlob(ctx.PathParam("sha"))
+ }
if err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound("GetBlobByPath", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("GetBlobByPath", err)
}
@@ -58,7 +64,7 @@ func RenderFile(ctx *context.Context) {
}
rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{
- CurrentRefPath: ctx.Repo.BranchNameSubURL(),
+ CurrentRefPath: ctx.Repo.RefTypeNameSubURL(),
CurrentTreePath: path.Dir(ctx.Repo.TreePath),
}).WithRelativePath(ctx.Repo.TreePath).WithInStandalonePage(true)
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index 85a745acbd..828ec08a8a 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -45,14 +45,14 @@ const (
// MustBeNotEmpty render when a repo is a empty git dir
func MustBeNotEmpty(ctx *context.Context) {
if ctx.Repo.Repository.IsEmpty {
- ctx.NotFound("MustBeNotEmpty", nil)
+ ctx.NotFound(nil)
}
}
// MustBeEditable check that repo can be edited
func MustBeEditable(ctx *context.Context) {
- if !ctx.Repo.Repository.CanEnableEditor() || ctx.Repo.IsViewCommit {
- ctx.NotFound("", nil)
+ if !ctx.Repo.Repository.CanEnableEditor() {
+ ctx.NotFound(nil)
return
}
}
@@ -60,7 +60,7 @@ func MustBeEditable(ctx *context.Context) {
// MustBeAbleToUpload check that repo can be uploaded to
func MustBeAbleToUpload(ctx *context.Context) {
if !setting.Repository.Upload.Enabled {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
}
}
@@ -87,17 +87,13 @@ func checkContextUser(ctx *context.Context, uid int64) *user_model.User {
return nil
}
- if !ctx.Doer.IsAdmin {
- orgsAvailable := []*organization.Organization{}
- for i := 0; i < len(orgs); i++ {
- if orgs[i].CanCreateRepo() {
- orgsAvailable = append(orgsAvailable, orgs[i])
- }
+ var orgsAvailable []*organization.Organization
+ for i := range orgs {
+ if ctx.Doer.CanCreateRepoIn(orgs[i].AsUser()) {
+ orgsAvailable = append(orgsAvailable, orgs[i])
}
- ctx.Data["Orgs"] = orgsAvailable
- } else {
- ctx.Data["Orgs"] = orgs
}
+ ctx.Data["Orgs"] = orgsAvailable
// Not equal means current user is an organization.
if uid == ctx.Doer.ID || uid == 0 {
@@ -116,7 +112,7 @@ func checkContextUser(ctx *context.Context, uid int64) *user_model.User {
// Check ownership of organization.
if !org.IsOrganization() {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return nil
}
if !ctx.Doer.IsAdmin {
@@ -125,7 +121,7 @@ func checkContextUser(ctx *context.Context, uid int64) *user_model.User {
ctx.ServerError("CanCreateOrgRepo", err)
return nil
} else if !canCreate {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return nil
}
} else {
@@ -147,27 +143,33 @@ func getRepoPrivate(ctx *context.Context) bool {
}
}
-// Create render creating repository page
-func Create(ctx *context.Context) {
+func createCommon(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("new_repo")
-
- // Give default value for template to render.
ctx.Data["Gitignores"] = repo_module.Gitignores
ctx.Data["LabelTemplateFiles"] = repo_module.LabelTemplateFiles
ctx.Data["Licenses"] = repo_module.Licenses
ctx.Data["Readmes"] = repo_module.Readmes
- ctx.Data["readme"] = "Default"
- ctx.Data["private"] = getRepoPrivate(ctx)
ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
- ctx.Data["default_branch"] = setting.Repository.DefaultBranch
+ ctx.Data["CanCreateRepoInDoer"] = ctx.Doer.CanCreateRepoIn(ctx.Doer)
+ ctx.Data["MaxCreationLimitOfDoer"] = ctx.Doer.MaxCreationLimit()
+ ctx.Data["SupportedObjectFormats"] = git.DefaultFeatures().SupportedObjectFormats
+ ctx.Data["DefaultObjectFormat"] = git.Sha1ObjectFormat
+}
+// Create render creating repository page
+func Create(ctx *context.Context) {
+ createCommon(ctx)
ctxUser := checkContextUser(ctx, ctx.FormInt64("org"))
if ctx.Written() {
return
}
ctx.Data["ContextUser"] = ctxUser
+ ctx.Data["readme"] = "Default"
+ ctx.Data["private"] = getRepoPrivate(ctx)
+ ctx.Data["default_branch"] = setting.Repository.DefaultBranch
ctx.Data["repo_template_name"] = ctx.Tr("repo.template_select")
+
templateID := ctx.FormInt64("template_id")
if templateID > 0 {
templateRepo, err := repo_model.GetRepositoryByID(ctx, templateID)
@@ -177,11 +179,6 @@ func Create(ctx *context.Context) {
}
}
- ctx.Data["CanCreateRepo"] = ctx.Doer.CanCreateRepo()
- ctx.Data["MaxCreationLimit"] = ctx.Doer.MaxCreationLimit()
- ctx.Data["SupportedObjectFormats"] = git.DefaultFeatures().SupportedObjectFormats
- ctx.Data["DefaultObjectFormat"] = git.Sha1ObjectFormat
-
ctx.HTML(http.StatusOK, tplCreate)
}
@@ -219,16 +216,8 @@ func handleCreateError(ctx *context.Context, owner *user_model.User, err error,
// CreatePost response for creating repository
func CreatePost(ctx *context.Context) {
+ createCommon(ctx)
form := web.GetForm(ctx).(*forms.CreateRepoForm)
- ctx.Data["Title"] = ctx.Tr("new_repo")
-
- ctx.Data["Gitignores"] = repo_module.Gitignores
- ctx.Data["LabelTemplateFiles"] = repo_module.LabelTemplateFiles
- ctx.Data["Licenses"] = repo_module.Licenses
- ctx.Data["Readmes"] = repo_module.Readmes
-
- ctx.Data["CanCreateRepo"] = ctx.Doer.CanCreateRepo()
- ctx.Data["MaxCreationLimit"] = ctx.Doer.MaxCreationLimit()
ctxUser := checkContextUser(ctx, form.UID)
if ctx.Written() {
@@ -236,6 +225,14 @@ func CreatePost(ctx *context.Context) {
}
ctx.Data["ContextUser"] = ctxUser
+ if form.RepoTemplate > 0 {
+ templateRepo, err := repo_model.GetRepositoryByID(ctx, form.RepoTemplate)
+ if err == nil && access_model.CheckRepoUnitUser(ctx, templateRepo, ctxUser, unit.TypeCode) {
+ ctx.Data["repo_template"] = form.RepoTemplate
+ ctx.Data["repo_template_name"] = templateRepo.Name
+ }
+ }
+
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplCreate)
return
@@ -303,112 +300,18 @@ func CreatePost(ctx *context.Context) {
handleCreateError(ctx, ctxUser, err, "CreatePost", tplCreate, &form)
}
-const (
- tplWatchUnwatch templates.TplName = "repo/watch_unwatch"
- tplStarUnstar templates.TplName = "repo/star_unstar"
-)
-
-// Action response for actions to a repository
-func Action(ctx *context.Context) {
- var err error
- switch ctx.PathParam("action") {
- case "watch":
- err = repo_model.WatchRepo(ctx, ctx.Doer, ctx.Repo.Repository, true)
- case "unwatch":
- err = repo_model.WatchRepo(ctx, ctx.Doer, ctx.Repo.Repository, false)
- case "star":
- err = repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, true)
- case "unstar":
- err = repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, false)
- case "accept_transfer":
- err = acceptOrRejectRepoTransfer(ctx, true)
- case "reject_transfer":
- err = acceptOrRejectRepoTransfer(ctx, false)
- case "desc": // FIXME: this is not used
- if !ctx.Repo.IsOwner() {
- ctx.Error(http.StatusNotFound)
- return
- }
-
- ctx.Repo.Repository.Description = ctx.FormString("desc")
- ctx.Repo.Repository.Website = ctx.FormString("site")
- err = repo_service.UpdateRepository(ctx, ctx.Repo.Repository, false)
- }
-
- if err != nil {
- if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.Flash.Error(ctx.Tr("repo.action.blocked_user"))
- } else {
- ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.PathParam("action")), err)
- return
- }
- }
-
- switch ctx.PathParam("action") {
- case "watch", "unwatch":
- ctx.Data["IsWatchingRepo"] = repo_model.IsWatching(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
- case "star", "unstar":
- ctx.Data["IsStaringRepo"] = repo_model.IsStaring(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
- }
-
- // see the `hx-trigger="refreshUserCards ..."` comments in tmpl
- ctx.RespHeader().Add("hx-trigger", "refreshUserCards")
-
- switch ctx.PathParam("action") {
- case "watch", "unwatch", "star", "unstar":
- // we have to reload the repository because NumStars or NumWatching (used in the templates) has just changed
- ctx.Data["Repository"], err = repo_model.GetRepositoryByName(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.Name)
- if err != nil {
- ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.PathParam("action")), err)
- return
- }
- }
-
- switch ctx.PathParam("action") {
- case "watch", "unwatch":
- ctx.HTML(http.StatusOK, tplWatchUnwatch)
- return
- case "star", "unstar":
- ctx.HTML(http.StatusOK, tplStarUnstar)
- return
- }
-
- ctx.RedirectToCurrentSite(ctx.FormString("redirect_to"), ctx.Repo.RepoLink)
-}
-
-func acceptOrRejectRepoTransfer(ctx *context.Context, accept bool) error {
- repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository)
- if err != nil {
- return err
- }
-
- if err := repoTransfer.LoadAttributes(ctx); err != nil {
- return err
- }
-
- if !repoTransfer.CanUserAcceptTransfer(ctx, ctx.Doer) {
- return errors.New("user does not have enough permissions")
- }
-
- if accept {
- if ctx.Repo.GitRepo != nil {
- ctx.Repo.GitRepo.Close()
- ctx.Repo.GitRepo = nil
- }
-
- if err := repo_service.TransferOwnership(ctx, repoTransfer.Doer, repoTransfer.Recipient, ctx.Repo.Repository, repoTransfer.Teams); err != nil {
- return err
- }
- ctx.Flash.Success(ctx.Tr("repo.settings.transfer.success"))
- } else {
- if err := repo_service.CancelRepositoryTransfer(ctx, ctx.Repo.Repository); err != nil {
- return err
- }
- ctx.Flash.Success(ctx.Tr("repo.settings.transfer.rejected"))
+func handleActionError(ctx *context.Context, err error) {
+ switch {
+ case errors.Is(err, user_model.ErrBlockedUser):
+ ctx.Flash.Error(ctx.Tr("repo.action.blocked_user"))
+ case repo_service.IsRepositoryLimitReached(err):
+ limit := err.(repo_service.LimitReachedError).Limit
+ ctx.Flash.Error(ctx.TrN(limit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", limit))
+ case errors.Is(err, util.ErrPermissionDenied):
+ ctx.HTTPError(http.StatusNotFound)
+ default:
+ ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.PathParam("action")), err)
}
-
- ctx.Redirect(ctx.Repo.Repository.Link())
- return nil
}
// RedirectDownload return a file based on the following infos:
@@ -432,7 +335,7 @@ func RedirectDownload(ctx *context.Context) {
release := releases[0]
att, err := repo_model.GetAttachmentByReleaseIDFileName(ctx, release.ID, fileName)
if err != nil {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
if att != nil {
@@ -444,12 +347,12 @@ func RedirectDownload(ctx *context.Context) {
// We only fetch the latest release if the tag is "latest" and no release with the tag "latest" exists
release, err := repo_model.GetLatestReleaseByRepoID(ctx, ctx.Repo.Repository.ID)
if err != nil {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
att, err := repo_model.GetAttachmentByReleaseIDFileName(ctx, release.ID, fileName)
if err != nil {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
if att != nil {
@@ -457,23 +360,17 @@ func RedirectDownload(ctx *context.Context) {
return
}
}
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
}
// Download an archive of a repository
func Download(ctx *context.Context) {
- uri := ctx.PathParam("*")
- ext, tp, err := archiver_service.ParseFileName(uri)
- if err != nil {
- ctx.ServerError("ParseFileName", err)
- return
- }
- aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp)
+ aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"))
if err != nil {
if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) {
- ctx.Error(http.StatusBadRequest, err.Error())
+ ctx.HTTPError(http.StatusBadRequest, err.Error())
} else if errors.Is(err, archiver_service.RepoRefNotFoundError{}) {
- ctx.Error(http.StatusNotFound, err.Error())
+ ctx.HTTPError(http.StatusNotFound, err.Error())
} else {
ctx.ServerError("archiver_service.NewRequest", err)
}
@@ -526,19 +423,13 @@ func download(ctx *context.Context, archiveName string, archiver *repo_model.Rep
// a request that's already in-progress, but the archiver service will just
// kind of drop it on the floor if this is the case.
func InitiateDownload(ctx *context.Context) {
- uri := ctx.PathParam("*")
- ext, tp, err := archiver_service.ParseFileName(uri)
- if err != nil {
- ctx.ServerError("ParseFileName", err)
- return
- }
- aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp)
+ aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"))
if err != nil {
- ctx.ServerError("archiver_service.NewRequest", err)
+ ctx.HTTPError(http.StatusBadRequest, "invalid archive request")
return
}
if aReq == nil {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
@@ -570,7 +461,7 @@ func SearchRepo(ctx *context.Context) {
if page <= 0 {
page = 1
}
- opts := &repo_model.SearchRepoOptions{
+ opts := repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: page,
PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")),
@@ -610,7 +501,7 @@ func SearchRepo(ctx *context.Context) {
opts.Collaborate = optional.Some(true)
case "":
default:
- ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Invalid search mode: \"%s\"", mode))
+ ctx.HTTPError(http.StatusUnprocessableEntity, fmt.Sprintf("Invalid search mode: \"%s\"", mode))
return
}
@@ -632,11 +523,11 @@ func SearchRepo(ctx *context.Context) {
if orderBy, ok := searchModeMap[sortMode]; ok {
opts.OrderBy = orderBy
} else {
- ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Invalid sort mode: \"%s\"", sortMode))
+ ctx.HTTPError(http.StatusUnprocessableEntity, fmt.Sprintf("Invalid sort mode: \"%s\"", sortMode))
return
}
} else {
- ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Invalid sort order: \"%s\"", sortOrder))
+ ctx.HTTPError(http.StatusUnprocessableEntity, fmt.Sprintf("Invalid sort order: \"%s\"", sortOrder))
return
}
}
diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go
index c60301475f..12216fc620 100644
--- a/routers/web/repo/search.go
+++ b/routers/web/repo/search.go
@@ -5,11 +5,11 @@ package repo
import (
"net/http"
- "strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/git"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
+ "code.gitea.io/gitea/modules/indexer/code/gitgrep"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/routers/common"
@@ -18,16 +18,6 @@ import (
const tplSearch templates.TplName = "repo/search"
-func indexSettingToGitGrepPathspecList() (list []string) {
- for _, expr := range setting.Indexer.IncludePatterns {
- list = append(list, ":(glob)"+expr.PatternString())
- }
- for _, expr := range setting.Indexer.ExcludePatterns {
- list = append(list, ":(glob,exclude)"+expr.PatternString())
- }
- return list
-}
-
// Search render repository search page
func Search(ctx *context.Context) {
ctx.Data["PageIsViewCode"] = true
@@ -48,10 +38,10 @@ func Search(ctx *context.Context) {
if setting.Indexer.RepoIndexerEnabled {
var err error
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{
- RepoIDs: []int64{ctx.Repo.Repository.ID},
- Keyword: prepareSearch.Keyword,
- IsKeywordFuzzy: prepareSearch.IsFuzzy,
- Language: prepareSearch.Language,
+ RepoIDs: []int64{ctx.Repo.Repository.ID},
+ Keyword: prepareSearch.Keyword,
+ SearchMode: prepareSearch.SearchMode,
+ Language: prepareSearch.Language,
Paginator: &db.ListOptions{
Page: page,
PageSize: setting.UI.RepoSearchPagingNum,
@@ -67,32 +57,14 @@ func Search(ctx *context.Context) {
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx)
}
} else {
- res, err := git.GrepSearch(ctx, ctx.Repo.GitRepo, prepareSearch.Keyword, git.GrepOptions{
- ContextLineNumber: 1,
- IsFuzzy: prepareSearch.IsFuzzy,
- RefName: git.RefNameFromBranch(ctx.Repo.BranchName).String(), // BranchName should be default branch or the first existing branch
- PathspecList: indexSettingToGitGrepPathspecList(),
- })
+ var err error
+ // ref should be default branch or the first existing branch
+ searchRef := git.RefNameFromBranch(ctx.Repo.Repository.DefaultBranch)
+ searchResults, total, err = gitgrep.PerformSearch(ctx, page, ctx.Repo.Repository.ID, ctx.Repo.GitRepo, searchRef, prepareSearch.Keyword, prepareSearch.SearchMode)
if err != nil {
- // TODO: if no branch exists, it reports: exit status 128, fatal: this operation must be run in a work tree.
- ctx.ServerError("GrepSearch", err)
+ ctx.ServerError("gitgrep.PerformSearch", err)
return
}
- total = len(res)
- pageStart := min((page-1)*setting.UI.RepoSearchPagingNum, len(res))
- pageEnd := min(page*setting.UI.RepoSearchPagingNum, len(res))
- res = res[pageStart:pageEnd]
- for _, r := range res {
- searchResults = append(searchResults, &code_indexer.Result{
- RepoID: ctx.Repo.Repository.ID,
- Filename: r.Filename,
- CommitID: ctx.Repo.CommitID,
- // UpdatedUnix: not supported yet
- // Language: not supported yet
- // Color: not supported yet
- Lines: code_indexer.HighlightSearchResultCode(r.Filename, "", r.LineNumbers, strings.Join(r.LineCodes, "\n")),
- })
- }
}
ctx.Data["Repo"] = ctx.Repo.Repository
diff --git a/routers/web/repo/setting/default_branch.go b/routers/web/repo/setting/default_branch.go
index 881d148afc..044c9e556a 100644
--- a/routers/web/repo/setting/default_branch.go
+++ b/routers/web/repo/setting/default_branch.go
@@ -34,7 +34,7 @@ func SetDefaultBranchPost(ctx *context.Context) {
}
branch := ctx.FormString("branch")
- if err := repo_service.SetRepoDefaultBranch(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, branch); err != nil {
+ if err := repo_service.SetRepoDefaultBranch(ctx, ctx.Repo.Repository, branch); err != nil {
switch {
case git_model.IsErrBranchNotExist(err):
ctx.Status(http.StatusNotFound)
@@ -49,6 +49,6 @@ func SetDefaultBranchPost(ctx *context.Context) {
ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
default:
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
}
}
diff --git a/routers/web/repo/setting/git_hooks.go b/routers/web/repo/setting/git_hooks.go
index 1d92211303..ba4b5e85b6 100644
--- a/routers/web/repo/setting/git_hooks.go
+++ b/routers/web/repo/setting/git_hooks.go
@@ -34,7 +34,7 @@ func GitHooksEdit(ctx *context.Context) {
hook, err := ctx.Repo.GitRepo.GetHook(name)
if err != nil {
if err == git.ErrNotValidHook {
- ctx.NotFound("GetHook", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("GetHook", err)
}
@@ -50,7 +50,7 @@ func GitHooksEditPost(ctx *context.Context) {
hook, err := ctx.Repo.GitRepo.GetHook(name)
if err != nil {
if err == git.ErrNotValidHook {
- ctx.NotFound("GetHook", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("GetHook", err)
}
diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go
index 2df483fa34..af6708e841 100644
--- a/routers/web/repo/setting/lfs.go
+++ b/routers/web/repo/setting/lfs.go
@@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/git/attribute"
"code.gitea.io/gitea/modules/git/pipeline"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
@@ -41,13 +42,10 @@ const (
// LFSFiles shows a repository's LFS files
func LFSFiles(ctx *context.Context) {
if !setting.LFS.StartServer {
- ctx.NotFound("LFSFiles", nil)
+ ctx.NotFound(nil)
return
}
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
total, err := git_model.CountLFSMetaObjects(ctx, ctx.Repo.Repository.ID)
if err != nil {
ctx.ServerError("LFSFiles", err)
@@ -71,15 +69,12 @@ func LFSFiles(ctx *context.Context) {
// LFSLocks shows a repository's LFS locks
func LFSLocks(ctx *context.Context) {
if !setting.LFS.StartServer {
- ctx.NotFound("LFSLocks", nil)
+ ctx.NotFound(nil)
return
}
ctx.Data["LFSFilesLink"] = ctx.Repo.RepoLink + "/settings/lfs"
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
total, err := git_model.CountLFSLockByRepoID(ctx, ctx.Repo.Repository.ID)
if err != nil {
ctx.ServerError("LFSLocks", err)
@@ -109,17 +104,13 @@ func LFSLocks(ctx *context.Context) {
}
// Clone base repo.
- tmpBasePath, err := repo_module.CreateTemporaryPath("locks")
+ tmpBasePath, cleanup, err := repo_module.CreateTemporaryPath("locks")
if err != nil {
log.Error("Failed to create temporary path: %v", err)
ctx.ServerError("LFSLocks", err)
return
}
- defer func() {
- if err := repo_module.RemoveTemporaryPath(tmpBasePath); err != nil {
- log.Error("LFSLocks: RemoveTemporaryPath: %v", err)
- }
- }()
+ defer cleanup()
if err := git.Clone(ctx, ctx.Repo.Repository.RepoPath(), tmpBasePath, git.CloneRepoOptions{
Bare: true,
@@ -138,39 +129,24 @@ func LFSLocks(ctx *context.Context) {
}
defer gitRepo.Close()
- filenames := make([]string, len(lfsLocks))
-
- for i, lock := range lfsLocks {
- filenames[i] = lock.Path
- }
-
- if err := gitRepo.ReadTreeToIndex(ctx.Repo.Repository.DefaultBranch); err != nil {
- log.Error("Unable to read the default branch to the index: %s (%v)", ctx.Repo.Repository.DefaultBranch, err)
- ctx.ServerError("LFSLocks", fmt.Errorf("unable to read the default branch to the index: %s (%w)", ctx.Repo.Repository.DefaultBranch, err))
- return
- }
-
- name2attribute2info, err := gitRepo.CheckAttribute(git.CheckAttributeOpts{
- Attributes: []string{"lockable"},
- Filenames: filenames,
- CachedOnly: true,
- })
+ checker, err := attribute.NewBatchChecker(gitRepo, ctx.Repo.Repository.DefaultBranch, []string{attribute.Lockable})
if err != nil {
log.Error("Unable to check attributes in %s (%v)", tmpBasePath, err)
ctx.ServerError("LFSLocks", err)
return
}
+ defer checker.Close()
lockables := make([]bool, len(lfsLocks))
+ filenames := make([]string, len(lfsLocks))
for i, lock := range lfsLocks {
- attribute2info, has := name2attribute2info[lock.Path]
- if !has {
- continue
- }
- if attribute2info["lockable"] != "set" {
+ filenames[i] = lock.Path
+ attrs, err := checker.CheckPath(lock.Path)
+ if err != nil {
+ log.Error("Unable to check attributes in %s: %s (%v)", tmpBasePath, lock.Path, err)
continue
}
- lockables[i] = true
+ lockables[i] = attrs.Get(attribute.Lockable).ToBool().Value()
}
ctx.Data["Lockables"] = lockables
@@ -197,7 +173,7 @@ func LFSLocks(ctx *context.Context) {
// LFSLockFile locks a file
func LFSLockFile(ctx *context.Context) {
if !setting.LFS.StartServer {
- ctx.NotFound("LFSLocks", nil)
+ ctx.NotFound(nil)
return
}
originalPath := ctx.FormString("path")
@@ -238,7 +214,7 @@ func LFSLockFile(ctx *context.Context) {
// LFSUnlock forcibly unlocks an LFS lock
func LFSUnlock(ctx *context.Context) {
if !setting.LFS.StartServer {
- ctx.NotFound("LFSUnlock", nil)
+ ctx.NotFound(nil)
return
}
_, err := git_model.DeleteLFSLockByID(ctx, ctx.PathParamInt64("lid"), ctx.Repo.Repository, ctx.Doer, true)
@@ -252,7 +228,7 @@ func LFSUnlock(ctx *context.Context) {
// LFSFileGet serves a single LFS file
func LFSFileGet(ctx *context.Context) {
if !setting.LFS.StartServer {
- ctx.NotFound("LFSFileGet", nil)
+ ctx.NotFound(nil)
return
}
ctx.Data["LFSFilesLink"] = ctx.Repo.RepoLink + "/settings/lfs"
@@ -260,7 +236,7 @@ func LFSFileGet(ctx *context.Context) {
p := lfs.Pointer{Oid: oid}
if !p.IsValid() {
- ctx.NotFound("LFSFileGet", nil)
+ ctx.NotFound(nil)
return
}
@@ -269,7 +245,7 @@ func LFSFileGet(ctx *context.Context) {
meta, err := git_model.GetLFSMetaObjectByOid(ctx, ctx.Repo.Repository.ID, oid)
if err != nil {
if err == git_model.ErrLFSObjectNotExist {
- ctx.NotFound("LFSFileGet", nil)
+ ctx.NotFound(nil)
return
}
ctx.ServerError("LFSFileGet", err)
@@ -291,8 +267,10 @@ func LFSFileGet(ctx *context.Context) {
buf = buf[:n]
st := typesniffer.DetectContentType(buf)
+ // FIXME: there is no IsPlainText set, but template uses it
ctx.Data["IsTextFile"] = st.IsText()
ctx.Data["FileSize"] = meta.Size
+ // FIXME: the last field is the URL-base64-encoded filename, it should not be "direct"
ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s/%s.git/info/lfs/objects/%s/%s", setting.AppURL, url.PathEscape(ctx.Repo.Repository.OwnerName), url.PathEscape(ctx.Repo.Repository.Name), url.PathEscape(meta.Oid), "direct")
switch {
case st.IsRepresentableAsText():
@@ -333,8 +311,6 @@ func LFSFileGet(ctx *context.Context) {
}
ctx.Data["LineNums"] = gotemplate.HTML(output.String())
- case st.IsPDF():
- ctx.Data["IsPDFFile"] = true
case st.IsVideo():
ctx.Data["IsVideoFile"] = true
case st.IsAudio():
@@ -350,13 +326,13 @@ func LFSFileGet(ctx *context.Context) {
// LFSDelete disassociates the provided oid from the repository and if the lfs file is no longer associated with any repositories - deletes it
func LFSDelete(ctx *context.Context) {
if !setting.LFS.StartServer {
- ctx.NotFound("LFSDelete", nil)
+ ctx.NotFound(nil)
return
}
oid := ctx.PathParam("oid")
p := lfs.Pointer{Oid: oid}
if !p.IsValid() {
- ctx.NotFound("LFSDelete", nil)
+ ctx.NotFound(nil)
return
}
@@ -381,13 +357,13 @@ func LFSDelete(ctx *context.Context) {
// LFSFileFind guesses a sha for the provided oid (or uses the provided sha) and then finds the commits that contain this sha
func LFSFileFind(ctx *context.Context) {
if !setting.LFS.StartServer {
- ctx.NotFound("LFSFind", nil)
+ ctx.NotFound(nil)
return
}
oid := ctx.FormString("oid")
size := ctx.FormInt64("size")
if len(oid) == 0 || size == 0 {
- ctx.NotFound("LFSFind", nil)
+ ctx.NotFound(nil)
return
}
sha := ctx.FormString("sha")
@@ -421,7 +397,7 @@ func LFSFileFind(ctx *context.Context) {
// LFSPointerFiles will search the repository for pointer files and report which are missing LFS files in the content store
func LFSPointerFiles(ctx *context.Context) {
if !setting.LFS.StartServer {
- ctx.NotFound("LFSFileGet", nil)
+ ctx.NotFound(nil)
return
}
ctx.Data["PageIsSettingsLFS"] = true
@@ -532,7 +508,7 @@ func LFSPointerFiles(ctx *context.Context) {
// LFSAutoAssociate auto associates accessible lfs files
func LFSAutoAssociate(ctx *context.Context) {
if !setting.LFS.StartServer {
- ctx.NotFound("LFSAutoAssociate", nil)
+ ctx.NotFound(nil)
return
}
oids := ctx.FormStrings("oid")
diff --git a/routers/web/repo/setting/protected_branch.go b/routers/web/repo/setting/protected_branch.go
index 022a24a9ad..0eea5e3f34 100644
--- a/routers/web/repo/setting/protected_branch.go
+++ b/routers/web/repo/setting/protected_branch.go
@@ -4,9 +4,11 @@
package setting
import (
+ "errors"
"fmt"
"net/http"
"net/url"
+ "strconv"
"strings"
"time"
@@ -14,6 +16,8 @@ import (
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/web"
@@ -86,7 +90,7 @@ func SettingsProtectedBranch(c *context.Context) {
c.Data["recent_status_checks"] = contexts
if c.Repo.Owner.IsOrganization() {
- teams, err := organization.OrgFromUser(c.Repo.Owner).TeamsWithAccessToRepo(c, c.Repo.Repository.ID, perm.AccessModeRead)
+ teams, err := organization.GetTeamsWithAccessToAnyRepoUnit(c, c.Repo.Owner.ID, c.Repo.Repository.ID, perm.AccessModeRead, unit.TypeCode, unit.TypePullRequests)
if err != nil {
c.ServerError("Repo.Owner.TeamsWithAccessToRepo", err)
return
@@ -108,7 +112,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
var protectBranch *git_model.ProtectedBranch
if f.RuleName == "" {
ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_required_rule_name"))
- ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit", ctx.Repo.RepoLink))
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings/branches/edit")
return
}
@@ -259,7 +263,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
protectBranch.BlockOnOutdatedBranch = f.BlockOnOutdatedBranch
protectBranch.BlockAdminMergeOverride = f.BlockAdminMergeOverride
- err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{
+ if err = pull_service.CreateOrUpdateProtectedBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{
UserIDs: whitelistUsers,
TeamIDs: whitelistTeams,
ForcePushUserIDs: forcePushAllowlistUsers,
@@ -268,24 +272,10 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
MergeTeamIDs: mergeWhitelistTeams,
ApprovalsUserIDs: approvalsWhitelistUsers,
ApprovalsTeamIDs: approvalsWhitelistTeams,
- })
- if err != nil {
- ctx.ServerError("UpdateProtectBranch", err)
- return
- }
-
- // FIXME: since we only need to recheck files protected rules, we could improve this
- matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.Repository.ID, protectBranch.RuleName)
- if err != nil {
- ctx.ServerError("FindAllMatchedBranches", err)
+ }); err != nil {
+ ctx.ServerError("CreateOrUpdateProtectedBranch", err)
return
}
- for _, branchName := range matchedBranches {
- if err = pull_service.CheckPRsForBaseBranch(ctx, ctx.Repo.Repository, branchName); err != nil {
- ctx.ServerError("CheckPRsForBaseBranch", err)
- return
- }
- }
ctx.Flash.Success(ctx.Tr("repo.settings.update_protect_branch_success", protectBranch.RuleName))
ctx.Redirect(fmt.Sprintf("%s/settings/branches?rule_name=%s", ctx.Repo.RepoLink, protectBranch.RuleName))
@@ -295,32 +285,32 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
func DeleteProtectedBranchRulePost(ctx *context.Context) {
ruleID := ctx.PathParamInt64("id")
if ruleID <= 0 {
- ctx.Flash.Error(ctx.Tr("repo.settings.remove_protected_branch_failed", fmt.Sprintf("%d", ruleID)))
- ctx.JSONRedirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink))
+ ctx.Flash.Error(ctx.Tr("repo.settings.remove_protected_branch_failed", strconv.FormatInt(ruleID, 10)))
+ ctx.JSONRedirect(ctx.Repo.RepoLink + "/settings/branches")
return
}
rule, err := git_model.GetProtectedBranchRuleByID(ctx, ctx.Repo.Repository.ID, ruleID)
if err != nil {
- ctx.Flash.Error(ctx.Tr("repo.settings.remove_protected_branch_failed", fmt.Sprintf("%d", ruleID)))
- ctx.JSONRedirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink))
+ ctx.Flash.Error(ctx.Tr("repo.settings.remove_protected_branch_failed", strconv.FormatInt(ruleID, 10)))
+ ctx.JSONRedirect(ctx.Repo.RepoLink + "/settings/branches")
return
}
if rule == nil {
- ctx.Flash.Error(ctx.Tr("repo.settings.remove_protected_branch_failed", fmt.Sprintf("%d", ruleID)))
- ctx.JSONRedirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink))
+ ctx.Flash.Error(ctx.Tr("repo.settings.remove_protected_branch_failed", strconv.FormatInt(ruleID, 10)))
+ ctx.JSONRedirect(ctx.Repo.RepoLink + "/settings/branches")
return
}
if err := git_model.DeleteProtectedBranch(ctx, ctx.Repo.Repository, ruleID); err != nil {
ctx.Flash.Error(ctx.Tr("repo.settings.remove_protected_branch_failed", rule.RuleName))
- ctx.JSONRedirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink))
+ ctx.JSONRedirect(ctx.Repo.RepoLink + "/settings/branches")
return
}
ctx.Flash.Success(ctx.Tr("repo.settings.remove_protected_branch_success", rule.RuleName))
- ctx.JSONRedirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink))
+ ctx.JSONRedirect(ctx.Repo.RepoLink + "/settings/branches")
}
func UpdateBranchProtectionPriories(ctx *context.Context) {
@@ -338,22 +328,28 @@ func RenameBranchPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.RenameBranchForm)
if !ctx.Repo.CanCreateBranch() {
- ctx.NotFound("RenameBranch", nil)
+ ctx.NotFound(nil)
return
}
if ctx.HasError() {
ctx.Flash.Error(ctx.GetErrMsg())
- ctx.Redirect(fmt.Sprintf("%s/branches", ctx.Repo.RepoLink))
+ ctx.Redirect(ctx.Repo.RepoLink + "/branches")
return
}
msg, err := repository.RenameBranch(ctx, ctx.Repo.Repository, ctx.Doer, ctx.Repo.GitRepo, form.From, form.To)
if err != nil {
switch {
+ case repo_model.IsErrUserDoesNotHaveAccessToRepo(err):
+ ctx.Flash.Error(ctx.Tr("repo.branch.rename_default_or_protected_branch_error"))
+ ctx.Redirect(ctx.Repo.RepoLink + "/branches")
case git_model.IsErrBranchAlreadyExists(err):
ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", form.To))
- ctx.Redirect(fmt.Sprintf("%s/branches", ctx.Repo.RepoLink))
+ ctx.Redirect(ctx.Repo.RepoLink + "/branches")
+ case errors.Is(err, git_model.ErrBranchIsProtected):
+ ctx.Flash.Error(ctx.Tr("repo.branch.rename_protected_branch_failed"))
+ ctx.Redirect(ctx.Repo.RepoLink + "/branches")
default:
ctx.ServerError("RenameBranch", err)
}
@@ -362,16 +358,16 @@ func RenameBranchPost(ctx *context.Context) {
if msg == "target_exist" {
ctx.Flash.Error(ctx.Tr("repo.settings.rename_branch_failed_exist", form.To))
- ctx.Redirect(fmt.Sprintf("%s/branches", ctx.Repo.RepoLink))
+ ctx.Redirect(ctx.Repo.RepoLink + "/branches")
return
}
if msg == "from_not_exist" {
ctx.Flash.Error(ctx.Tr("repo.settings.rename_branch_failed_not_exist", form.From))
- ctx.Redirect(fmt.Sprintf("%s/branches", ctx.Repo.RepoLink))
+ ctx.Redirect(ctx.Repo.RepoLink + "/branches")
return
}
ctx.Flash.Success(ctx.Tr("repo.settings.rename_branch_success", form.From, form.To))
- ctx.Redirect(fmt.Sprintf("%s/branches", ctx.Repo.RepoLink))
+ ctx.Redirect(ctx.Repo.RepoLink + "/branches")
}
diff --git a/routers/web/repo/setting/protected_tag.go b/routers/web/repo/setting/protected_tag.go
index 1730ad4a8b..50f5a28c4c 100644
--- a/routers/web/repo/setting/protected_tag.go
+++ b/routers/web/repo/setting/protected_tag.go
@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
+ "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
@@ -156,7 +157,7 @@ func setTagsContext(ctx *context.Context) error {
ctx.Data["Users"] = users
if ctx.Repo.Owner.IsOrganization() {
- teams, err := organization.OrgFromUser(ctx.Repo.Owner).TeamsWithAccessToRepo(ctx, ctx.Repo.Repository.ID, perm.AccessModeRead)
+ teams, err := organization.GetTeamsWithAccessToAnyRepoUnit(ctx, ctx.Repo.Owner.ID, ctx.Repo.Repository.ID, perm.AccessModeRead, unit.TypeCode, unit.TypePullRequests)
if err != nil {
ctx.ServerError("Repo.Owner.TeamsWithAccessToRepo", err)
return err
@@ -183,7 +184,7 @@ func selectProtectedTagByContext(ctx *context.Context) *git_model.ProtectedTag {
return tag
}
- ctx.NotFound("", fmt.Errorf("ProtectedTag[%v] not associated to repository %v", id, ctx.Repo.Repository))
+ ctx.NotFound(fmt.Errorf("ProtectedTag[%v] not associated to repository %v", id, ctx.Repo.Repository))
return nil
}
diff --git a/routers/web/repo/setting/public_access.go b/routers/web/repo/setting/public_access.go
new file mode 100644
index 0000000000..368d34294a
--- /dev/null
+++ b/routers/web/repo/setting/public_access.go
@@ -0,0 +1,155 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package setting
+
+import (
+ "net/http"
+ "slices"
+ "strconv"
+
+ "code.gitea.io/gitea/models/perm"
+ "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/services/context"
+)
+
+const tplRepoSettingsPublicAccess templates.TplName = "repo/settings/public_access"
+
+func parsePublicAccessMode(permission string, allowed []string) (ret struct {
+ AnonymousAccessMode, EveryoneAccessMode perm.AccessMode
+},
+) {
+ ret.AnonymousAccessMode = perm.AccessModeNone
+ ret.EveryoneAccessMode = perm.AccessModeNone
+
+ // if site admin forces repositories to be private, then do not allow any other access mode,
+ // otherwise the "force private" setting would be bypassed
+ if setting.Repository.ForcePrivate {
+ return ret
+ }
+ if !slices.Contains(allowed, permission) {
+ return ret
+ }
+ switch permission {
+ case paAnonymousRead:
+ ret.AnonymousAccessMode = perm.AccessModeRead
+ case paEveryoneRead:
+ ret.EveryoneAccessMode = perm.AccessModeRead
+ case paEveryoneWrite:
+ ret.EveryoneAccessMode = perm.AccessModeWrite
+ }
+ return ret
+}
+
+const (
+ paNotSet = "not-set"
+ paAnonymousRead = "anonymous-read"
+ paEveryoneRead = "everyone-read"
+ paEveryoneWrite = "everyone-write"
+)
+
+type repoUnitPublicAccess struct {
+ UnitType unit.Type
+ FormKey string
+ DisplayName string
+ PublicAccessTypes []string
+ UnitPublicAccess string
+}
+
+func repoUnitPublicAccesses(ctx *context.Context) []*repoUnitPublicAccess {
+ accesses := []*repoUnitPublicAccess{
+ {
+ UnitType: unit.TypeCode,
+ DisplayName: ctx.Locale.TrString("repo.code"),
+ PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead},
+ },
+ {
+ UnitType: unit.TypeIssues,
+ DisplayName: ctx.Locale.TrString("issues"),
+ PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead},
+ },
+ {
+ UnitType: unit.TypePullRequests,
+ DisplayName: ctx.Locale.TrString("pull_requests"),
+ PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead},
+ },
+ {
+ UnitType: unit.TypeReleases,
+ DisplayName: ctx.Locale.TrString("repo.releases"),
+ PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead},
+ },
+ {
+ UnitType: unit.TypeWiki,
+ DisplayName: ctx.Locale.TrString("repo.wiki"),
+ PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead, paEveryoneWrite},
+ },
+ {
+ UnitType: unit.TypeProjects,
+ DisplayName: ctx.Locale.TrString("repo.projects"),
+ PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead},
+ },
+ {
+ UnitType: unit.TypePackages,
+ DisplayName: ctx.Locale.TrString("repo.packages"),
+ PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead},
+ },
+ {
+ UnitType: unit.TypeActions,
+ DisplayName: ctx.Locale.TrString("repo.actions"),
+ PublicAccessTypes: []string{paAnonymousRead, paEveryoneRead},
+ },
+ }
+ for _, ua := range accesses {
+ ua.FormKey = "repo-unit-access-" + strconv.Itoa(int(ua.UnitType))
+ for _, u := range ctx.Repo.Repository.Units {
+ if u.Type == ua.UnitType {
+ ua.UnitPublicAccess = paNotSet
+ switch {
+ case u.EveryoneAccessMode == perm.AccessModeWrite:
+ ua.UnitPublicAccess = paEveryoneWrite
+ case u.EveryoneAccessMode == perm.AccessModeRead:
+ ua.UnitPublicAccess = paEveryoneRead
+ case u.AnonymousAccessMode == perm.AccessModeRead:
+ ua.UnitPublicAccess = paAnonymousRead
+ }
+ break
+ }
+ }
+ }
+ return slices.DeleteFunc(accesses, func(ua *repoUnitPublicAccess) bool {
+ return ua.UnitPublicAccess == ""
+ })
+}
+
+func PublicAccess(ctx *context.Context) {
+ ctx.Data["PageIsSettingsPublicAccess"] = true
+ ctx.Data["RepoUnitPublicAccesses"] = repoUnitPublicAccesses(ctx)
+ ctx.Data["GlobalForcePrivate"] = setting.Repository.ForcePrivate
+ if setting.Repository.ForcePrivate {
+ ctx.Flash.Error(ctx.Tr("form.repository_force_private"), true)
+ }
+ ctx.HTML(http.StatusOK, tplRepoSettingsPublicAccess)
+}
+
+func PublicAccessPost(ctx *context.Context) {
+ accesses := repoUnitPublicAccesses(ctx)
+ for _, ua := range accesses {
+ formVal := ctx.FormString(ua.FormKey)
+ parsed := parsePublicAccessMode(formVal, ua.PublicAccessTypes)
+ err := repo.UpdateRepoUnitPublicAccess(ctx, &repo.RepoUnit{
+ RepoID: ctx.Repo.Repository.ID,
+ Type: ua.UnitType,
+ AnonymousAccessMode: parsed.AnonymousAccessMode,
+ EveryoneAccessMode: parsed.EveryoneAccessMode,
+ })
+ if err != nil {
+ ctx.ServerError("UpdateRepoUnitPublicAccess", err)
+ return
+ }
+ }
+ ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
+ ctx.Redirect(ctx.Repo.Repository.Link() + "/settings/public_access")
+}
diff --git a/routers/web/repo/setting/runners.go b/routers/web/repo/setting/runners.go
deleted file mode 100644
index 94f2ae7a0c..0000000000
--- a/routers/web/repo/setting/runners.go
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package setting
-
-import (
- "errors"
- "net/http"
- "net/url"
-
- actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- actions_shared "code.gitea.io/gitea/routers/web/shared/actions"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
-)
-
-const (
- // TODO: Separate secrets from runners when layout is ready
- tplRepoRunners templates.TplName = "repo/settings/actions"
- tplOrgRunners templates.TplName = "org/settings/actions"
- tplAdminRunners templates.TplName = "admin/actions"
- tplUserRunners templates.TplName = "user/settings/actions"
- tplRepoRunnerEdit templates.TplName = "repo/settings/runner_edit"
- tplOrgRunnerEdit templates.TplName = "org/settings/runners_edit"
- tplAdminRunnerEdit templates.TplName = "admin/runners/edit"
- tplUserRunnerEdit templates.TplName = "user/settings/runner_edit"
-)
-
-type runnersCtx struct {
- OwnerID int64
- RepoID int64
- IsRepo bool
- IsOrg bool
- IsAdmin bool
- IsUser bool
- RunnersTemplate templates.TplName
- RunnerEditTemplate templates.TplName
- RedirectLink string
-}
-
-func getRunnersCtx(ctx *context.Context) (*runnersCtx, error) {
- if ctx.Data["PageIsRepoSettings"] == true {
- return &runnersCtx{
- RepoID: ctx.Repo.Repository.ID,
- OwnerID: 0,
- IsRepo: true,
- RunnersTemplate: tplRepoRunners,
- RunnerEditTemplate: tplRepoRunnerEdit,
- RedirectLink: ctx.Repo.RepoLink + "/settings/actions/runners/",
- }, nil
- }
-
- if ctx.Data["PageIsOrgSettings"] == true {
- err := shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
- return nil, nil
- }
- return &runnersCtx{
- RepoID: 0,
- OwnerID: ctx.Org.Organization.ID,
- IsOrg: true,
- RunnersTemplate: tplOrgRunners,
- RunnerEditTemplate: tplOrgRunnerEdit,
- RedirectLink: ctx.Org.OrgLink + "/settings/actions/runners/",
- }, nil
- }
-
- if ctx.Data["PageIsAdmin"] == true {
- return &runnersCtx{
- RepoID: 0,
- OwnerID: 0,
- IsAdmin: true,
- RunnersTemplate: tplAdminRunners,
- RunnerEditTemplate: tplAdminRunnerEdit,
- RedirectLink: setting.AppSubURL + "/-/admin/actions/runners/",
- }, nil
- }
-
- if ctx.Data["PageIsUserSettings"] == true {
- return &runnersCtx{
- OwnerID: ctx.Doer.ID,
- RepoID: 0,
- IsUser: true,
- RunnersTemplate: tplUserRunners,
- RunnerEditTemplate: tplUserRunnerEdit,
- RedirectLink: setting.AppSubURL + "/user/settings/actions/runners/",
- }, nil
- }
-
- return nil, errors.New("unable to set Runners context")
-}
-
-// Runners render settings/actions/runners page for repo level
-func Runners(ctx *context.Context) {
- ctx.Data["PageIsSharedSettingsRunners"] = true
- ctx.Data["Title"] = ctx.Tr("actions.actions")
- ctx.Data["PageType"] = "runners"
-
- rCtx, err := getRunnersCtx(ctx)
- if err != nil {
- ctx.ServerError("getRunnersCtx", err)
- return
- }
-
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
-
- opts := actions_model.FindRunnerOptions{
- ListOptions: db.ListOptions{
- Page: page,
- PageSize: 100,
- },
- Sort: ctx.Req.URL.Query().Get("sort"),
- Filter: ctx.Req.URL.Query().Get("q"),
- }
- if rCtx.IsRepo {
- opts.RepoID = rCtx.RepoID
- opts.WithAvailable = true
- } else if rCtx.IsOrg || rCtx.IsUser {
- opts.OwnerID = rCtx.OwnerID
- opts.WithAvailable = true
- }
- actions_shared.RunnersList(ctx, opts)
-
- ctx.HTML(http.StatusOK, rCtx.RunnersTemplate)
-}
-
-// RunnersEdit renders runner edit page for repository level
-func RunnersEdit(ctx *context.Context) {
- ctx.Data["PageIsSharedSettingsRunners"] = true
- ctx.Data["Title"] = ctx.Tr("actions.runners.edit_runner")
- rCtx, err := getRunnersCtx(ctx)
- if err != nil {
- ctx.ServerError("getRunnersCtx", err)
- return
- }
-
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
-
- actions_shared.RunnerDetails(ctx, page,
- ctx.PathParamInt64("runnerid"), rCtx.OwnerID, rCtx.RepoID,
- )
- ctx.HTML(http.StatusOK, rCtx.RunnerEditTemplate)
-}
-
-func RunnersEditPost(ctx *context.Context) {
- rCtx, err := getRunnersCtx(ctx)
- if err != nil {
- ctx.ServerError("getRunnersCtx", err)
- return
- }
- actions_shared.RunnerDetailsEditPost(ctx, ctx.PathParamInt64("runnerid"),
- rCtx.OwnerID, rCtx.RepoID,
- rCtx.RedirectLink+url.PathEscape(ctx.PathParam("runnerid")))
-}
-
-func ResetRunnerRegistrationToken(ctx *context.Context) {
- rCtx, err := getRunnersCtx(ctx)
- if err != nil {
- ctx.ServerError("getRunnersCtx", err)
- return
- }
- actions_shared.RunnerResetRegistrationToken(ctx, rCtx.OwnerID, rCtx.RepoID, rCtx.RedirectLink)
-}
-
-// RunnerDeletePost response for deleting runner
-func RunnerDeletePost(ctx *context.Context) {
- rCtx, err := getRunnersCtx(ctx)
- if err != nil {
- ctx.ServerError("getRunnersCtx", err)
- return
- }
- actions_shared.RunnerDeletePost(ctx, ctx.PathParamInt64("runnerid"), rCtx.RedirectLink, rCtx.RedirectLink+url.PathEscape(ctx.PathParam("runnerid")))
-}
-
-func RedirectToDefaultSetting(ctx *context.Context) {
- ctx.Redirect(ctx.Repo.RepoLink + "/settings/actions/runners")
-}
diff --git a/routers/web/repo/setting/secrets.go b/routers/web/repo/setting/secrets.go
index 46cb875f9b..c6e2d18249 100644
--- a/routers/web/repo/setting/secrets.go
+++ b/routers/web/repo/setting/secrets.go
@@ -44,9 +44,8 @@ func getSecretsCtx(ctx *context.Context) (*secretsCtx, error) {
}
if ctx.Data["PageIsOrgSettings"] == true {
- err := shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return nil, nil
}
return &secretsCtx{
diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go
index 7399c681e2..6e16ead183 100644
--- a/routers/web/repo/setting/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -6,15 +6,12 @@ package setting
import (
"errors"
- "fmt"
"net/http"
"strings"
"time"
- actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
@@ -38,6 +35,8 @@ import (
mirror_service "code.gitea.io/gitea/services/mirror"
repo_service "code.gitea.io/gitea/services/repository"
wiki_service "code.gitea.io/gitea/services/wiki"
+
+ "xorm.io/xorm/convert"
)
const (
@@ -60,9 +59,10 @@ func SettingsCtxData(ctx *context.Context) {
ctx.Data["DisableNewPushMirrors"] = setting.Mirror.DisableNewPush
ctx.Data["DefaultMirrorInterval"] = setting.Mirror.DefaultInterval
ctx.Data["MinimumMirrorInterval"] = setting.Mirror.MinInterval
+ ctx.Data["CanConvertFork"] = ctx.Repo.Repository.IsFork && ctx.Doer.CanCreateRepoIn(ctx.Repo.Repository.Owner)
signing, _ := asymkey_service.SigningKey(ctx, ctx.Repo.Repository.RepoPath())
- ctx.Data["SigningKeyAvailable"] = len(signing) > 0
+ ctx.Data["SigningKeyAvailable"] = signing != nil
ctx.Data["SigningSettings"] = setting.Repository.Signing
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
@@ -97,8 +97,6 @@ func Settings(ctx *context.Context) {
// SettingsPost response for changes of a repository
func SettingsPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.RepoSettingForm)
-
ctx.Data["ForcePrivate"] = setting.Repository.ForcePrivate
ctx.Data["MirrorsEnabled"] = setting.Mirror.Enabled
ctx.Data["DisableNewPullMirrors"] = setting.Mirror.DisableNewPull
@@ -107,875 +105,937 @@ func SettingsPost(ctx *context.Context) {
ctx.Data["MinimumMirrorInterval"] = setting.Mirror.MinInterval
signing, _ := asymkey_service.SigningKey(ctx, ctx.Repo.Repository.RepoPath())
- ctx.Data["SigningKeyAvailable"] = len(signing) > 0
+ ctx.Data["SigningKeyAvailable"] = signing != nil
ctx.Data["SigningSettings"] = setting.Repository.Signing
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
- repo := ctx.Repo.Repository
-
switch ctx.FormString("action") {
case "update":
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, tplSettingsOptions)
- return
- }
+ handleSettingsPostUpdate(ctx)
+ case "mirror":
+ handleSettingsPostMirror(ctx)
+ case "mirror-sync":
+ handleSettingsPostMirrorSync(ctx)
+ case "push-mirror-sync":
+ handleSettingsPostPushMirrorSync(ctx)
+ case "push-mirror-update":
+ handleSettingsPostPushMirrorUpdate(ctx)
+ case "push-mirror-remove":
+ handleSettingsPostPushMirrorRemove(ctx)
+ case "push-mirror-add":
+ handleSettingsPostPushMirrorAdd(ctx)
+ case "advanced":
+ handleSettingsPostAdvanced(ctx)
+ case "signing":
+ handleSettingsPostSigning(ctx)
+ case "admin":
+ handleSettingsPostAdmin(ctx)
+ case "admin_index":
+ handleSettingsPostAdminIndex(ctx)
+ case "convert":
+ handleSettingsPostConvert(ctx)
+ case "convert_fork":
+ handleSettingsPostConvertFork(ctx)
+ case "transfer":
+ handleSettingsPostTransfer(ctx)
+ case "cancel_transfer":
+ handleSettingsPostCancelTransfer(ctx)
+ case "delete":
+ handleSettingsPostDelete(ctx)
+ case "delete-wiki":
+ handleSettingsPostDeleteWiki(ctx)
+ case "archive":
+ handleSettingsPostArchive(ctx)
+ case "unarchive":
+ handleSettingsPostUnarchive(ctx)
+ case "visibility":
+ handleSettingsPostVisibility(ctx)
+ default:
+ ctx.NotFound(nil)
+ }
+}
- newRepoName := form.RepoName
- // Check if repository name has been changed.
- if repo.LowerName != strings.ToLower(newRepoName) {
- // Close the GitRepo if open
- if ctx.Repo.GitRepo != nil {
- ctx.Repo.GitRepo.Close()
- ctx.Repo.GitRepo = nil
- }
- if err := repo_service.ChangeRepositoryName(ctx, ctx.Doer, repo, newRepoName); err != nil {
+func handleSettingsPostUpdate(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
+ if ctx.HasError() {
+ ctx.HTML(http.StatusOK, tplSettingsOptions)
+ return
+ }
+
+ newRepoName := form.RepoName
+ // Check if repository name has been changed.
+ if repo.LowerName != strings.ToLower(newRepoName) {
+ // Close the GitRepo if open
+ if ctx.Repo.GitRepo != nil {
+ ctx.Repo.GitRepo.Close()
+ ctx.Repo.GitRepo = nil
+ }
+ if err := repo_service.ChangeRepositoryName(ctx, ctx.Doer, repo, newRepoName); err != nil {
+ ctx.Data["Err_RepoName"] = true
+ switch {
+ case repo_model.IsErrRepoAlreadyExist(err):
+ ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tplSettingsOptions, &form)
+ case db.IsErrNameReserved(err):
+ ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplSettingsOptions, &form)
+ case repo_model.IsErrRepoFilesAlreadyExist(err):
ctx.Data["Err_RepoName"] = true
switch {
- case repo_model.IsErrRepoAlreadyExist(err):
- ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tplSettingsOptions, &form)
- case db.IsErrNameReserved(err):
- ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplSettingsOptions, &form)
- case repo_model.IsErrRepoFilesAlreadyExist(err):
- ctx.Data["Err_RepoName"] = true
- switch {
- case ctx.IsUserSiteAdmin() || (setting.Repository.AllowAdoptionOfUnadoptedRepositories && setting.Repository.AllowDeleteOfUnadoptedRepositories):
- ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt_or_delete"), tplSettingsOptions, form)
- case setting.Repository.AllowAdoptionOfUnadoptedRepositories:
- ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt"), tplSettingsOptions, form)
- case setting.Repository.AllowDeleteOfUnadoptedRepositories:
- ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.delete"), tplSettingsOptions, form)
- default:
- ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist"), tplSettingsOptions, form)
- }
- case db.IsErrNamePatternNotAllowed(err):
- ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplSettingsOptions, &form)
+ case ctx.IsUserSiteAdmin() || (setting.Repository.AllowAdoptionOfUnadoptedRepositories && setting.Repository.AllowDeleteOfUnadoptedRepositories):
+ ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt_or_delete"), tplSettingsOptions, form)
+ case setting.Repository.AllowAdoptionOfUnadoptedRepositories:
+ ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt"), tplSettingsOptions, form)
+ case setting.Repository.AllowDeleteOfUnadoptedRepositories:
+ ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.delete"), tplSettingsOptions, form)
default:
- ctx.ServerError("ChangeRepositoryName", err)
+ ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist"), tplSettingsOptions, form)
}
- return
+ case db.IsErrNamePatternNotAllowed(err):
+ ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplSettingsOptions, &form)
+ default:
+ ctx.ServerError("ChangeRepositoryName", err)
}
-
- log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName)
- }
- // In case it's just a case change.
- repo.Name = newRepoName
- repo.LowerName = strings.ToLower(newRepoName)
- repo.Description = form.Description
- repo.Website = form.Website
- repo.IsTemplate = form.Template
-
- // Visibility of forked repository is forced sync with base repository.
- if repo.IsFork {
- form.Private = repo.BaseRepo.IsPrivate || repo.BaseRepo.Owner.Visibility == structs.VisibleTypePrivate
- }
-
- if err := repo_service.UpdateRepository(ctx, repo, false); err != nil {
- ctx.ServerError("UpdateRepository", err)
return
}
- log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
- ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
- ctx.Redirect(repo.Link() + "/settings")
+ log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName)
+ }
+ // In case it's just a case change.
+ repo.Name = newRepoName
+ repo.LowerName = strings.ToLower(newRepoName)
+ repo.Description = form.Description
+ repo.Website = form.Website
+ repo.IsTemplate = form.Template
+
+ // Visibility of forked repository is forced sync with base repository.
+ if repo.IsFork {
+ form.Private = repo.BaseRepo.IsPrivate || repo.BaseRepo.Owner.Visibility == structs.VisibleTypePrivate
+ }
- case "mirror":
- if !setting.Mirror.Enabled || !repo.IsMirror || repo.IsArchived {
- ctx.NotFound("", nil)
- return
- }
+ if err := repo_service.UpdateRepository(ctx, repo, false); err != nil {
+ ctx.ServerError("UpdateRepository", err)
+ return
+ }
+ log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
- pullMirror, err := repo_model.GetMirrorByRepoID(ctx, ctx.Repo.Repository.ID)
- if err == repo_model.ErrMirrorNotExist {
- ctx.NotFound("", nil)
- return
- }
- if err != nil {
- ctx.ServerError("GetMirrorByRepoID", err)
- return
- }
- // This section doesn't require repo_name/RepoName to be set in the form, don't show it
- // as an error on the UI for this action
- ctx.Data["Err_RepoName"] = nil
-
- interval, err := time.ParseDuration(form.Interval)
- if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) {
- ctx.Data["Err_Interval"] = true
- ctx.RenderWithErr(ctx.Tr("repo.mirror_interval_invalid"), tplSettingsOptions, &form)
- return
- }
+ ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
+ ctx.Redirect(repo.Link() + "/settings")
+}
- pullMirror.EnablePrune = form.EnablePrune
- pullMirror.Interval = interval
- pullMirror.ScheduleNextUpdate()
- if err := repo_model.UpdateMirror(ctx, pullMirror); err != nil {
- ctx.ServerError("UpdateMirror", err)
- return
- }
+func handleSettingsPostMirror(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
+ if !setting.Mirror.Enabled || !repo.IsMirror || repo.IsArchived {
+ ctx.NotFound(nil)
+ return
+ }
- u, err := git.GetRemoteURL(ctx, ctx.Repo.Repository.RepoPath(), pullMirror.GetRemoteName())
- if err != nil {
- ctx.Data["Err_MirrorAddress"] = true
- handleSettingRemoteAddrError(ctx, err, form)
- return
- }
- if u.User != nil && form.MirrorPassword == "" && form.MirrorUsername == u.User.Username() {
- form.MirrorPassword, _ = u.User.Password()
- }
+ pullMirror, err := repo_model.GetMirrorByRepoID(ctx, ctx.Repo.Repository.ID)
+ if err == repo_model.ErrMirrorNotExist {
+ ctx.NotFound(nil)
+ return
+ }
+ if err != nil {
+ ctx.ServerError("GetMirrorByRepoID", err)
+ return
+ }
+ // This section doesn't require repo_name/RepoName to be set in the form, don't show it
+ // as an error on the UI for this action
+ ctx.Data["Err_RepoName"] = nil
+
+ interval, err := time.ParseDuration(form.Interval)
+ if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) {
+ ctx.Data["Err_Interval"] = true
+ ctx.RenderWithErr(ctx.Tr("repo.mirror_interval_invalid"), tplSettingsOptions, &form)
+ return
+ }
- address, err := git.ParseRemoteAddr(form.MirrorAddress, form.MirrorUsername, form.MirrorPassword)
- if err == nil {
- err = migrations.IsMigrateURLAllowed(address, ctx.Doer)
- }
- if err != nil {
- ctx.Data["Err_MirrorAddress"] = true
- handleSettingRemoteAddrError(ctx, err, form)
- return
- }
+ pullMirror.EnablePrune = form.EnablePrune
+ pullMirror.Interval = interval
+ pullMirror.ScheduleNextUpdate()
+ if err := repo_model.UpdateMirror(ctx, pullMirror); err != nil {
+ ctx.ServerError("UpdateMirror", err)
+ return
+ }
- if err := mirror_service.UpdateAddress(ctx, pullMirror, address); err != nil {
- ctx.ServerError("UpdateAddress", err)
- return
- }
+ u, err := git.GetRemoteURL(ctx, ctx.Repo.Repository.RepoPath(), pullMirror.GetRemoteName())
+ if err != nil {
+ ctx.Data["Err_MirrorAddress"] = true
+ handleSettingRemoteAddrError(ctx, err, form)
+ return
+ }
+ if u.User != nil && form.MirrorPassword == "" && form.MirrorUsername == u.User.Username() {
+ form.MirrorPassword, _ = u.User.Password()
+ }
- remoteAddress, err := util.SanitizeURL(form.MirrorAddress)
- if err != nil {
- ctx.Data["Err_MirrorAddress"] = true
- handleSettingRemoteAddrError(ctx, err, form)
- return
- }
- pullMirror.RemoteAddress = remoteAddress
+ address, err := git.ParseRemoteAddr(form.MirrorAddress, form.MirrorUsername, form.MirrorPassword)
+ if err == nil {
+ err = migrations.IsMigrateURLAllowed(address, ctx.Doer)
+ }
+ if err != nil {
+ ctx.Data["Err_MirrorAddress"] = true
+ handleSettingRemoteAddrError(ctx, err, form)
+ return
+ }
- form.LFS = form.LFS && setting.LFS.StartServer
+ if err := mirror_service.UpdateAddress(ctx, pullMirror, address); err != nil {
+ ctx.ServerError("UpdateAddress", err)
+ return
+ }
- if len(form.LFSEndpoint) > 0 {
- ep := lfs.DetermineEndpoint("", form.LFSEndpoint)
- if ep == nil {
- ctx.Data["Err_LFSEndpoint"] = true
- ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_lfs_endpoint"), tplSettingsOptions, &form)
- return
- }
- err = migrations.IsMigrateURLAllowed(ep.String(), ctx.Doer)
- if err != nil {
- ctx.Data["Err_LFSEndpoint"] = true
- handleSettingRemoteAddrError(ctx, err, form)
- return
- }
- }
+ remoteAddress, err := util.SanitizeURL(form.MirrorAddress)
+ if err != nil {
+ ctx.Data["Err_MirrorAddress"] = true
+ handleSettingRemoteAddrError(ctx, err, form)
+ return
+ }
+ pullMirror.RemoteAddress = remoteAddress
- pullMirror.LFS = form.LFS
- pullMirror.LFSEndpoint = form.LFSEndpoint
- if err := repo_model.UpdateMirror(ctx, pullMirror); err != nil {
- ctx.ServerError("UpdateMirror", err)
+ form.LFS = form.LFS && setting.LFS.StartServer
+
+ if len(form.LFSEndpoint) > 0 {
+ ep := lfs.DetermineEndpoint("", form.LFSEndpoint)
+ if ep == nil {
+ ctx.Data["Err_LFSEndpoint"] = true
+ ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_lfs_endpoint"), tplSettingsOptions, &form)
return
}
-
- ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
- ctx.Redirect(repo.Link() + "/settings")
-
- case "mirror-sync":
- if !setting.Mirror.Enabled || !repo.IsMirror || repo.IsArchived {
- ctx.NotFound("", nil)
+ err = migrations.IsMigrateURLAllowed(ep.String(), ctx.Doer)
+ if err != nil {
+ ctx.Data["Err_LFSEndpoint"] = true
+ handleSettingRemoteAddrError(ctx, err, form)
return
}
+ }
- mirror_service.AddPullMirrorToQueue(repo.ID)
-
- ctx.Flash.Info(ctx.Tr("repo.settings.pull_mirror_sync_in_progress", repo.OriginalURL))
- ctx.Redirect(repo.Link() + "/settings")
+ pullMirror.LFS = form.LFS
+ pullMirror.LFSEndpoint = form.LFSEndpoint
+ if err := repo_model.UpdateMirror(ctx, pullMirror); err != nil {
+ ctx.ServerError("UpdateMirror", err)
+ return
+ }
- case "push-mirror-sync":
- if !setting.Mirror.Enabled {
- ctx.NotFound("", nil)
- return
- }
+ ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
+ ctx.Redirect(repo.Link() + "/settings")
+}
- m, _, _ := repo_model.GetPushMirrorByIDAndRepoID(ctx, form.PushMirrorID, repo.ID)
- if m == nil {
- ctx.NotFound("", nil)
- return
- }
+func handleSettingsPostMirrorSync(ctx *context.Context) {
+ repo := ctx.Repo.Repository
+ if !setting.Mirror.Enabled || !repo.IsMirror || repo.IsArchived {
+ ctx.NotFound(nil)
+ return
+ }
- mirror_service.AddPushMirrorToQueue(m.ID)
+ mirror_service.AddPullMirrorToQueue(repo.ID)
- ctx.Flash.Info(ctx.Tr("repo.settings.push_mirror_sync_in_progress", m.RemoteAddress))
- ctx.Redirect(repo.Link() + "/settings")
+ ctx.Flash.Info(ctx.Tr("repo.settings.pull_mirror_sync_in_progress", repo.OriginalURL))
+ ctx.Redirect(repo.Link() + "/settings")
+}
- case "push-mirror-update":
- if !setting.Mirror.Enabled || repo.IsArchived {
- ctx.NotFound("", nil)
- return
- }
+func handleSettingsPostPushMirrorSync(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
- // This section doesn't require repo_name/RepoName to be set in the form, don't show it
- // as an error on the UI for this action
- ctx.Data["Err_RepoName"] = nil
+ if !setting.Mirror.Enabled {
+ ctx.NotFound(nil)
+ return
+ }
- interval, err := time.ParseDuration(form.PushMirrorInterval)
- if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) {
- ctx.RenderWithErr(ctx.Tr("repo.mirror_interval_invalid"), tplSettingsOptions, &forms.RepoSettingForm{})
- return
- }
+ m, _, _ := repo_model.GetPushMirrorByIDAndRepoID(ctx, form.PushMirrorID, repo.ID)
+ if m == nil {
+ ctx.NotFound(nil)
+ return
+ }
- m, _, _ := repo_model.GetPushMirrorByIDAndRepoID(ctx, form.PushMirrorID, repo.ID)
- if m == nil {
- ctx.NotFound("", nil)
- return
- }
+ mirror_service.AddPushMirrorToQueue(m.ID)
- m.Interval = interval
- if err := repo_model.UpdatePushMirrorInterval(ctx, m); err != nil {
- ctx.ServerError("UpdatePushMirrorInterval", err)
- return
- }
- // Background why we are adding it to Queue
- // If we observed its implementation in the context of `push-mirror-sync` where it
- // is evident that pushing to the queue is necessary for updates.
- // So, there are updates within the given interval, it is necessary to update the queue accordingly.
- if !ctx.FormBool("push_mirror_defer_sync") {
- // push_mirror_defer_sync is mainly for testing purpose, we do not really want to sync the push mirror immediately
- mirror_service.AddPushMirrorToQueue(m.ID)
- }
- ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
- ctx.Redirect(repo.Link() + "/settings")
+ ctx.Flash.Info(ctx.Tr("repo.settings.push_mirror_sync_in_progress", m.RemoteAddress))
+ ctx.Redirect(repo.Link() + "/settings")
+}
- case "push-mirror-remove":
- if !setting.Mirror.Enabled || repo.IsArchived {
- ctx.NotFound("", nil)
- return
- }
+func handleSettingsPostPushMirrorUpdate(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
- // This section doesn't require repo_name/RepoName to be set in the form, don't show it
- // as an error on the UI for this action
- ctx.Data["Err_RepoName"] = nil
+ if !setting.Mirror.Enabled || repo.IsArchived {
+ ctx.NotFound(nil)
+ return
+ }
- m, _, _ := repo_model.GetPushMirrorByIDAndRepoID(ctx, form.PushMirrorID, repo.ID)
- if m == nil {
- ctx.NotFound("", nil)
- return
- }
+ // This section doesn't require repo_name/RepoName to be set in the form, don't show it
+ // as an error on the UI for this action
+ ctx.Data["Err_RepoName"] = nil
- if err := mirror_service.RemovePushMirrorRemote(ctx, m); err != nil {
- ctx.ServerError("RemovePushMirrorRemote", err)
- return
- }
+ interval, err := time.ParseDuration(form.PushMirrorInterval)
+ if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) {
+ ctx.RenderWithErr(ctx.Tr("repo.mirror_interval_invalid"), tplSettingsOptions, &forms.RepoSettingForm{})
+ return
+ }
- if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: m.ID, RepoID: m.RepoID}); err != nil {
- ctx.ServerError("DeletePushMirrorByID", err)
- return
- }
+ m, _, _ := repo_model.GetPushMirrorByIDAndRepoID(ctx, form.PushMirrorID, repo.ID)
+ if m == nil {
+ ctx.NotFound(nil)
+ return
+ }
- ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
- ctx.Redirect(repo.Link() + "/settings")
+ m.Interval = interval
+ if err := repo_model.UpdatePushMirrorInterval(ctx, m); err != nil {
+ ctx.ServerError("UpdatePushMirrorInterval", err)
+ return
+ }
+ // Background why we are adding it to Queue
+ // If we observed its implementation in the context of `push-mirror-sync` where it
+ // is evident that pushing to the queue is necessary for updates.
+ // So, there are updates within the given interval, it is necessary to update the queue accordingly.
+ if !ctx.FormBool("push_mirror_defer_sync") {
+ // push_mirror_defer_sync is mainly for testing purpose, we do not really want to sync the push mirror immediately
+ mirror_service.AddPushMirrorToQueue(m.ID)
+ }
+ ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
+ ctx.Redirect(repo.Link() + "/settings")
+}
- case "push-mirror-add":
- if setting.Mirror.DisableNewPush || repo.IsArchived {
- ctx.NotFound("", nil)
- return
- }
+func handleSettingsPostPushMirrorRemove(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
- // This section doesn't require repo_name/RepoName to be set in the form, don't show it
- // as an error on the UI for this action
- ctx.Data["Err_RepoName"] = nil
+ if !setting.Mirror.Enabled || repo.IsArchived {
+ ctx.NotFound(nil)
+ return
+ }
- interval, err := time.ParseDuration(form.PushMirrorInterval)
- if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) {
- ctx.Data["Err_PushMirrorInterval"] = true
- ctx.RenderWithErr(ctx.Tr("repo.mirror_interval_invalid"), tplSettingsOptions, &form)
- return
- }
+ // This section doesn't require repo_name/RepoName to be set in the form, don't show it
+ // as an error on the UI for this action
+ ctx.Data["Err_RepoName"] = nil
- address, err := git.ParseRemoteAddr(form.PushMirrorAddress, form.PushMirrorUsername, form.PushMirrorPassword)
- if err == nil {
- err = migrations.IsMigrateURLAllowed(address, ctx.Doer)
- }
- if err != nil {
- ctx.Data["Err_PushMirrorAddress"] = true
- handleSettingRemoteAddrError(ctx, err, form)
- return
- }
+ m, _, _ := repo_model.GetPushMirrorByIDAndRepoID(ctx, form.PushMirrorID, repo.ID)
+ if m == nil {
+ ctx.NotFound(nil)
+ return
+ }
- remoteSuffix, err := util.CryptoRandomString(10)
- if err != nil {
- ctx.ServerError("RandomString", err)
- return
- }
+ if err := mirror_service.RemovePushMirrorRemote(ctx, m); err != nil {
+ ctx.ServerError("RemovePushMirrorRemote", err)
+ return
+ }
- remoteAddress, err := util.SanitizeURL(form.PushMirrorAddress)
- if err != nil {
- ctx.Data["Err_PushMirrorAddress"] = true
- handleSettingRemoteAddrError(ctx, err, form)
- return
- }
+ if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: m.ID, RepoID: m.RepoID}); err != nil {
+ ctx.ServerError("DeletePushMirrorByID", err)
+ return
+ }
- m := &repo_model.PushMirror{
- RepoID: repo.ID,
- Repo: repo,
- RemoteName: fmt.Sprintf("remote_mirror_%s", remoteSuffix),
- SyncOnCommit: form.PushMirrorSyncOnCommit,
- Interval: interval,
- RemoteAddress: remoteAddress,
- }
- if err := db.Insert(ctx, m); err != nil {
- ctx.ServerError("InsertPushMirror", err)
- return
- }
+ ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
+ ctx.Redirect(repo.Link() + "/settings")
+}
- if err := mirror_service.AddPushMirrorRemote(ctx, m, address); err != nil {
- if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: m.ID, RepoID: m.RepoID}); err != nil {
- log.Error("DeletePushMirrors %v", err)
- }
- ctx.ServerError("AddPushMirrorRemote", err)
- return
- }
+func handleSettingsPostPushMirrorAdd(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
- ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
- ctx.Redirect(repo.Link() + "/settings")
+ if setting.Mirror.DisableNewPush || repo.IsArchived {
+ ctx.NotFound(nil)
+ return
+ }
- case "advanced":
- var repoChanged bool
- var units []repo_model.RepoUnit
- var deleteUnitTypes []unit_model.Type
+ // This section doesn't require repo_name/RepoName to be set in the form, don't show it
+ // as an error on the UI for this action
+ ctx.Data["Err_RepoName"] = nil
- // This section doesn't require repo_name/RepoName to be set in the form, don't show it
- // as an error on the UI for this action
- ctx.Data["Err_RepoName"] = nil
+ interval, err := time.ParseDuration(form.PushMirrorInterval)
+ if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) {
+ ctx.Data["Err_PushMirrorInterval"] = true
+ ctx.RenderWithErr(ctx.Tr("repo.mirror_interval_invalid"), tplSettingsOptions, &form)
+ return
+ }
- if repo.CloseIssuesViaCommitInAnyBranch != form.EnableCloseIssuesViaCommitInAnyBranch {
- repo.CloseIssuesViaCommitInAnyBranch = form.EnableCloseIssuesViaCommitInAnyBranch
- repoChanged = true
- }
+ address, err := git.ParseRemoteAddr(form.PushMirrorAddress, form.PushMirrorUsername, form.PushMirrorPassword)
+ if err == nil {
+ err = migrations.IsMigrateURLAllowed(address, ctx.Doer)
+ }
+ if err != nil {
+ ctx.Data["Err_PushMirrorAddress"] = true
+ handleSettingRemoteAddrError(ctx, err, form)
+ return
+ }
- if form.EnableCode && !unit_model.TypeCode.UnitGlobalDisabled() {
- units = append(units, repo_model.RepoUnit{
- RepoID: repo.ID,
- Type: unit_model.TypeCode,
- })
- } else if !unit_model.TypeCode.UnitGlobalDisabled() {
- deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode)
- }
+ remoteSuffix, err := util.CryptoRandomString(10)
+ if err != nil {
+ ctx.ServerError("RandomString", err)
+ return
+ }
- if form.EnableWiki && form.EnableExternalWiki && !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
- if !validation.IsValidExternalURL(form.ExternalWikiURL) {
- ctx.Flash.Error(ctx.Tr("repo.settings.external_wiki_url_error"))
- ctx.Redirect(repo.Link() + "/settings")
- return
- }
+ remoteAddress, err := util.SanitizeURL(form.PushMirrorAddress)
+ if err != nil {
+ ctx.Data["Err_PushMirrorAddress"] = true
+ handleSettingRemoteAddrError(ctx, err, form)
+ return
+ }
- units = append(units, repo_model.RepoUnit{
- RepoID: repo.ID,
- Type: unit_model.TypeExternalWiki,
- Config: &repo_model.ExternalWikiConfig{
- ExternalWikiURL: form.ExternalWikiURL,
- },
- })
- deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
- } else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
- units = append(units, repo_model.RepoUnit{
- RepoID: repo.ID,
- Type: unit_model.TypeWiki,
- Config: new(repo_model.UnitConfig),
- EveryoneAccessMode: perm.ParseAccessMode(form.DefaultWikiEveryoneAccess, perm.AccessModeNone, perm.AccessModeRead, perm.AccessModeWrite),
- })
- deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
- } else {
- if !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
- deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
- }
- if !unit_model.TypeWiki.UnitGlobalDisabled() {
- deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
- }
- }
+ m := &repo_model.PushMirror{
+ RepoID: repo.ID,
+ Repo: repo,
+ RemoteName: "remote_mirror_" + remoteSuffix,
+ SyncOnCommit: form.PushMirrorSyncOnCommit,
+ Interval: interval,
+ RemoteAddress: remoteAddress,
+ }
+ if err := db.Insert(ctx, m); err != nil {
+ ctx.ServerError("InsertPushMirror", err)
+ return
+ }
- if form.DefaultWikiBranch != "" {
- if err := wiki_service.ChangeDefaultWikiBranch(ctx, repo, form.DefaultWikiBranch); err != nil {
- log.Error("ChangeDefaultWikiBranch failed, err: %v", err)
- ctx.Flash.Warning(ctx.Tr("repo.settings.failed_to_change_default_wiki_branch"))
- }
+ if err := mirror_service.AddPushMirrorRemote(ctx, m, address); err != nil {
+ if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: m.ID, RepoID: m.RepoID}); err != nil {
+ log.Error("DeletePushMirrors %v", err)
}
+ ctx.ServerError("AddPushMirrorRemote", err)
+ return
+ }
- if form.EnableIssues && form.EnableExternalTracker && !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
- if !validation.IsValidExternalURL(form.ExternalTrackerURL) {
- ctx.Flash.Error(ctx.Tr("repo.settings.external_tracker_url_error"))
- ctx.Redirect(repo.Link() + "/settings")
- return
- }
- if len(form.TrackerURLFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(form.TrackerURLFormat) {
- ctx.Flash.Error(ctx.Tr("repo.settings.tracker_url_format_error"))
- ctx.Redirect(repo.Link() + "/settings")
- return
- }
- units = append(units, repo_model.RepoUnit{
- RepoID: repo.ID,
- Type: unit_model.TypeExternalTracker,
- Config: &repo_model.ExternalTrackerConfig{
- ExternalTrackerURL: form.ExternalTrackerURL,
- ExternalTrackerFormat: form.TrackerURLFormat,
- ExternalTrackerStyle: form.TrackerIssueStyle,
- ExternalTrackerRegexpPattern: form.ExternalTrackerRegexpPattern,
- },
- })
- deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues)
- } else if form.EnableIssues && !form.EnableExternalTracker && !unit_model.TypeIssues.UnitGlobalDisabled() {
- units = append(units, repo_model.RepoUnit{
- RepoID: repo.ID,
- Type: unit_model.TypeIssues,
- Config: &repo_model.IssuesConfig{
- EnableTimetracker: form.EnableTimetracker,
- AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime,
- EnableDependencies: form.EnableIssueDependencies,
- },
- })
- deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
- } else {
- if !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
- deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
- }
- if !unit_model.TypeIssues.UnitGlobalDisabled() {
- deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues)
- }
- }
+ ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
+ ctx.Redirect(repo.Link() + "/settings")
+}
- if form.EnableProjects && !unit_model.TypeProjects.UnitGlobalDisabled() {
- units = append(units, repo_model.RepoUnit{
- RepoID: repo.ID,
- Type: unit_model.TypeProjects,
- Config: &repo_model.ProjectsConfig{
- ProjectsMode: repo_model.ProjectsMode(form.ProjectsMode),
- },
- })
- } else if !unit_model.TypeProjects.UnitGlobalDisabled() {
- deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
+func newRepoUnit(repo *repo_model.Repository, unitType unit_model.Type, config convert.Conversion) repo_model.RepoUnit {
+ repoUnit := repo_model.RepoUnit{RepoID: repo.ID, Type: unitType, Config: config}
+ for _, u := range repo.Units {
+ if u.Type == unitType {
+ repoUnit.EveryoneAccessMode = u.EveryoneAccessMode
+ repoUnit.AnonymousAccessMode = u.AnonymousAccessMode
}
+ }
+ return repoUnit
+}
- if form.EnableReleases && !unit_model.TypeReleases.UnitGlobalDisabled() {
- units = append(units, repo_model.RepoUnit{
- RepoID: repo.ID,
- Type: unit_model.TypeReleases,
- })
- } else if !unit_model.TypeReleases.UnitGlobalDisabled() {
- deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeReleases)
- }
+func handleSettingsPostAdvanced(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
+ var repoChanged bool
+ var units []repo_model.RepoUnit
+ var deleteUnitTypes []unit_model.Type
- if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() {
- units = append(units, repo_model.RepoUnit{
- RepoID: repo.ID,
- Type: unit_model.TypePackages,
- })
- } else if !unit_model.TypePackages.UnitGlobalDisabled() {
- deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages)
- }
+ // This section doesn't require repo_name/RepoName to be set in the form, don't show it
+ // as an error on the UI for this action
+ ctx.Data["Err_RepoName"] = nil
- if form.EnableActions && !unit_model.TypeActions.UnitGlobalDisabled() {
- units = append(units, repo_model.RepoUnit{
- RepoID: repo.ID,
- Type: unit_model.TypeActions,
- })
- } else if !unit_model.TypeActions.UnitGlobalDisabled() {
- deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeActions)
- }
+ if repo.CloseIssuesViaCommitInAnyBranch != form.EnableCloseIssuesViaCommitInAnyBranch {
+ repo.CloseIssuesViaCommitInAnyBranch = form.EnableCloseIssuesViaCommitInAnyBranch
+ repoChanged = true
+ }
- if form.EnablePulls && !unit_model.TypePullRequests.UnitGlobalDisabled() {
- units = append(units, repo_model.RepoUnit{
- RepoID: repo.ID,
- Type: unit_model.TypePullRequests,
- Config: &repo_model.PullRequestsConfig{
- IgnoreWhitespaceConflicts: form.PullsIgnoreWhitespace,
- AllowMerge: form.PullsAllowMerge,
- AllowRebase: form.PullsAllowRebase,
- AllowRebaseMerge: form.PullsAllowRebaseMerge,
- AllowSquash: form.PullsAllowSquash,
- AllowFastForwardOnly: form.PullsAllowFastForwardOnly,
- AllowManualMerge: form.PullsAllowManualMerge,
- AutodetectManualMerge: form.EnableAutodetectManualMerge,
- AllowRebaseUpdate: form.PullsAllowRebaseUpdate,
- DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge,
- DefaultMergeStyle: repo_model.MergeStyle(form.PullsDefaultMergeStyle),
- DefaultAllowMaintainerEdit: form.DefaultAllowMaintainerEdit,
- },
- })
- } else if !unit_model.TypePullRequests.UnitGlobalDisabled() {
- deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests)
- }
+ if form.EnableCode && !unit_model.TypeCode.UnitGlobalDisabled() {
+ units = append(units, newRepoUnit(repo, unit_model.TypeCode, nil))
+ } else if !unit_model.TypeCode.UnitGlobalDisabled() {
+ deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode)
+ }
- if len(units) == 0 {
- ctx.Flash.Error(ctx.Tr("repo.settings.update_settings_no_unit"))
- ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+ if form.EnableWiki && form.EnableExternalWiki && !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
+ if !validation.IsValidExternalURL(form.ExternalWikiURL) {
+ ctx.Flash.Error(ctx.Tr("repo.settings.external_wiki_url_error"))
+ ctx.Redirect(repo.Link() + "/settings")
return
}
- if err := repo_service.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil {
- ctx.ServerError("UpdateRepositoryUnits", err)
- return
+ units = append(units, newRepoUnit(repo, unit_model.TypeExternalWiki, &repo_model.ExternalWikiConfig{
+ ExternalWikiURL: form.ExternalWikiURL,
+ }))
+ deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
+ } else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
+ units = append(units, newRepoUnit(repo, unit_model.TypeWiki, new(repo_model.UnitConfig)))
+ deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
+ } else {
+ if !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
+ deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
}
- if repoChanged {
- if err := repo_service.UpdateRepository(ctx, repo, false); err != nil {
- ctx.ServerError("UpdateRepository", err)
- return
- }
+ if !unit_model.TypeWiki.UnitGlobalDisabled() {
+ deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
}
- log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
-
- ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
- ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+ }
- case "signing":
- changed := false
- trustModel := repo_model.ToTrustModel(form.TrustModel)
- if trustModel != repo.TrustModel {
- repo.TrustModel = trustModel
- changed = true
+ if form.DefaultWikiBranch != "" {
+ if err := wiki_service.ChangeDefaultWikiBranch(ctx, repo, form.DefaultWikiBranch); err != nil {
+ log.Error("ChangeDefaultWikiBranch failed, err: %v", err)
+ ctx.Flash.Warning(ctx.Tr("repo.settings.failed_to_change_default_wiki_branch"))
}
+ }
- if changed {
- if err := repo_service.UpdateRepository(ctx, repo, false); err != nil {
- ctx.ServerError("UpdateRepository", err)
- return
- }
+ if form.EnableIssues && form.EnableExternalTracker && !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
+ if !validation.IsValidExternalURL(form.ExternalTrackerURL) {
+ ctx.Flash.Error(ctx.Tr("repo.settings.external_tracker_url_error"))
+ ctx.Redirect(repo.Link() + "/settings")
+ return
}
- log.Trace("Repository signing settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
-
- ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
- ctx.Redirect(ctx.Repo.RepoLink + "/settings")
-
- case "admin":
- if !ctx.Doer.IsAdmin {
- ctx.Error(http.StatusForbidden)
+ if len(form.TrackerURLFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(form.TrackerURLFormat) {
+ ctx.Flash.Error(ctx.Tr("repo.settings.tracker_url_format_error"))
+ ctx.Redirect(repo.Link() + "/settings")
return
}
-
- if repo.IsFsckEnabled != form.EnableHealthCheck {
- repo.IsFsckEnabled = form.EnableHealthCheck
+ units = append(units, newRepoUnit(repo, unit_model.TypeExternalTracker, &repo_model.ExternalTrackerConfig{
+ ExternalTrackerURL: form.ExternalTrackerURL,
+ ExternalTrackerFormat: form.TrackerURLFormat,
+ ExternalTrackerStyle: form.TrackerIssueStyle,
+ ExternalTrackerRegexpPattern: form.ExternalTrackerRegexpPattern,
+ }))
+ deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues)
+ } else if form.EnableIssues && !form.EnableExternalTracker && !unit_model.TypeIssues.UnitGlobalDisabled() {
+ units = append(units, newRepoUnit(repo, unit_model.TypeIssues, &repo_model.IssuesConfig{
+ EnableTimetracker: form.EnableTimetracker,
+ AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime,
+ EnableDependencies: form.EnableIssueDependencies,
+ }))
+ deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
+ } else {
+ if !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
+ deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
}
-
- if err := repo_service.UpdateRepository(ctx, repo, false); err != nil {
- ctx.ServerError("UpdateRepository", err)
- return
+ if !unit_model.TypeIssues.UnitGlobalDisabled() {
+ deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues)
}
+ }
- log.Trace("Repository admin settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+ if form.EnableProjects && !unit_model.TypeProjects.UnitGlobalDisabled() {
+ units = append(units, newRepoUnit(repo, unit_model.TypeProjects, &repo_model.ProjectsConfig{
+ ProjectsMode: repo_model.ProjectsMode(form.ProjectsMode),
+ }))
+ } else if !unit_model.TypeProjects.UnitGlobalDisabled() {
+ deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
+ }
- ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
- ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+ if form.EnableReleases && !unit_model.TypeReleases.UnitGlobalDisabled() {
+ units = append(units, newRepoUnit(repo, unit_model.TypeReleases, nil))
+ } else if !unit_model.TypeReleases.UnitGlobalDisabled() {
+ deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeReleases)
+ }
- case "admin_index":
- if !ctx.Doer.IsAdmin {
- ctx.Error(http.StatusForbidden)
- return
- }
+ if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() {
+ units = append(units, newRepoUnit(repo, unit_model.TypePackages, nil))
+ } else if !unit_model.TypePackages.UnitGlobalDisabled() {
+ deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages)
+ }
- switch form.RequestReindexType {
- case "stats":
- if err := stats.UpdateRepoIndexer(ctx.Repo.Repository); err != nil {
- ctx.ServerError("UpdateStatsRepondexer", err)
- return
- }
- case "code":
- if !setting.Indexer.RepoIndexerEnabled {
- ctx.Error(http.StatusForbidden)
- return
- }
- code.UpdateRepoIndexer(ctx.Repo.Repository)
- default:
- ctx.NotFound("", nil)
- return
- }
+ if form.EnableActions && !unit_model.TypeActions.UnitGlobalDisabled() {
+ units = append(units, newRepoUnit(repo, unit_model.TypeActions, nil))
+ } else if !unit_model.TypeActions.UnitGlobalDisabled() {
+ deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeActions)
+ }
- log.Trace("Repository reindex for %s requested: %s/%s", form.RequestReindexType, ctx.Repo.Owner.Name, repo.Name)
+ if form.EnablePulls && !unit_model.TypePullRequests.UnitGlobalDisabled() {
+ units = append(units, newRepoUnit(repo, unit_model.TypePullRequests, &repo_model.PullRequestsConfig{
+ IgnoreWhitespaceConflicts: form.PullsIgnoreWhitespace,
+ AllowMerge: form.PullsAllowMerge,
+ AllowRebase: form.PullsAllowRebase,
+ AllowRebaseMerge: form.PullsAllowRebaseMerge,
+ AllowSquash: form.PullsAllowSquash,
+ AllowFastForwardOnly: form.PullsAllowFastForwardOnly,
+ AllowManualMerge: form.PullsAllowManualMerge,
+ AutodetectManualMerge: form.EnableAutodetectManualMerge,
+ AllowRebaseUpdate: form.PullsAllowRebaseUpdate,
+ DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge,
+ DefaultMergeStyle: repo_model.MergeStyle(form.PullsDefaultMergeStyle),
+ DefaultAllowMaintainerEdit: form.DefaultAllowMaintainerEdit,
+ }))
+ } else if !unit_model.TypePullRequests.UnitGlobalDisabled() {
+ deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests)
+ }
- ctx.Flash.Success(ctx.Tr("repo.settings.reindex_requested"))
+ if len(units) == 0 {
+ ctx.Flash.Error(ctx.Tr("repo.settings.update_settings_no_unit"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+ return
+ }
- case "convert":
- if !ctx.Repo.IsOwner() {
- ctx.Error(http.StatusNotFound)
- return
- }
- if repo.Name != form.RepoName {
- ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
+ if err := repo_service.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil {
+ ctx.ServerError("UpdateRepositoryUnits", err)
+ return
+ }
+ if repoChanged {
+ if err := repo_service.UpdateRepository(ctx, repo, false); err != nil {
+ ctx.ServerError("UpdateRepository", err)
return
}
+ }
+ log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
- if !repo.IsMirror {
- ctx.Error(http.StatusNotFound)
- return
- }
- repo.IsMirror = false
+ ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+}
- if _, err := repo_service.CleanUpMigrateInfo(ctx, repo); err != nil {
- ctx.ServerError("CleanUpMigrateInfo", err)
- return
- } else if err = repo_model.DeleteMirrorByRepoID(ctx, ctx.Repo.Repository.ID); err != nil {
- ctx.ServerError("DeleteMirrorByRepoID", err)
+func handleSettingsPostSigning(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
+ trustModel := repo_model.ToTrustModel(form.TrustModel)
+ if trustModel != repo.TrustModel {
+ repo.TrustModel = trustModel
+ if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "trust_model"); err != nil {
+ ctx.ServerError("UpdateRepositoryColsNoAutoTime", err)
return
}
- log.Trace("Repository converted from mirror to regular: %s", repo.FullName())
- ctx.Flash.Success(ctx.Tr("repo.settings.convert_succeed"))
- ctx.Redirect(repo.Link())
+ log.Trace("Repository signing settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+ }
- case "convert_fork":
- if !ctx.Repo.IsOwner() {
- ctx.Error(http.StatusNotFound)
- return
- }
- if err := repo.LoadOwner(ctx); err != nil {
- ctx.ServerError("Convert Fork", err)
- return
- }
- if repo.Name != form.RepoName {
- ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
- return
- }
+ ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+}
- if !repo.IsFork {
- ctx.Error(http.StatusNotFound)
- return
- }
+func handleSettingsPostAdmin(ctx *context.Context) {
+ if !ctx.Doer.IsAdmin {
+ ctx.HTTPError(http.StatusForbidden)
+ return
+ }
- if !ctx.Repo.Owner.CanCreateRepo() {
- maxCreationLimit := ctx.Repo.Owner.MaxCreationLimit()
- msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit)
- ctx.Flash.Error(msg)
- ctx.Redirect(repo.Link() + "/settings")
+ repo := ctx.Repo.Repository
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ if repo.IsFsckEnabled != form.EnableHealthCheck {
+ repo.IsFsckEnabled = form.EnableHealthCheck
+ if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_fsck_enabled"); err != nil {
+ ctx.ServerError("UpdateRepositoryColsNoAutoTime", err)
return
}
+ log.Trace("Repository admin settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+ }
- if err := repo_service.ConvertForkToNormalRepository(ctx, repo); err != nil {
- log.Error("Unable to convert repository %-v from fork. Error: %v", repo, err)
- ctx.ServerError("Convert Fork", err)
- return
- }
+ ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+}
- log.Trace("Repository converted from fork to regular: %s", repo.FullName())
- ctx.Flash.Success(ctx.Tr("repo.settings.convert_fork_succeed"))
- ctx.Redirect(repo.Link())
+func handleSettingsPostAdminIndex(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
+ if !ctx.Doer.IsAdmin {
+ ctx.HTTPError(http.StatusForbidden)
+ return
+ }
- case "transfer":
- if !ctx.Repo.IsOwner() {
- ctx.Error(http.StatusNotFound)
+ switch form.RequestReindexType {
+ case "stats":
+ if err := stats.UpdateRepoIndexer(ctx.Repo.Repository); err != nil {
+ ctx.ServerError("UpdateStatsRepondexer", err)
return
}
- if repo.Name != form.RepoName {
- ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
+ case "code":
+ if !setting.Indexer.RepoIndexerEnabled {
+ ctx.HTTPError(http.StatusForbidden)
return
}
+ code.UpdateRepoIndexer(ctx.Repo.Repository)
+ default:
+ ctx.NotFound(nil)
+ return
+ }
- newOwner, err := user_model.GetUserByName(ctx, ctx.FormString("new_owner_name"))
- if err != nil {
- if user_model.IsErrUserNotExist(err) {
- ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil)
- return
- }
- ctx.ServerError("IsUserExist", err)
- return
- }
+ log.Trace("Repository reindex for %s requested: %s/%s", form.RequestReindexType, ctx.Repo.Owner.Name, repo.Name)
- if newOwner.Type == user_model.UserTypeOrganization {
- if !ctx.Doer.IsAdmin && newOwner.Visibility == structs.VisibleTypePrivate && !organization.OrgFromUser(newOwner).HasMemberWithUserID(ctx, ctx.Doer.ID) {
- // The user shouldn't know about this organization
- ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil)
- return
- }
- }
+ ctx.Flash.Success(ctx.Tr("repo.settings.reindex_requested"))
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+}
- // Close the GitRepo if open
- if ctx.Repo.GitRepo != nil {
- ctx.Repo.GitRepo.Close()
- ctx.Repo.GitRepo = nil
- }
+func handleSettingsPostConvert(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
+ if !ctx.Repo.IsOwner() {
+ ctx.HTTPError(http.StatusNotFound)
+ return
+ }
+ if repo.Name != form.RepoName {
+ ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
+ return
+ }
- oldFullname := repo.FullName()
- if err := repo_service.StartRepositoryTransfer(ctx, ctx.Doer, newOwner, repo, nil); err != nil {
- if repo_model.IsErrRepoAlreadyExist(err) {
- ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil)
- } else if repo_model.IsErrRepoTransferInProgress(err) {
- ctx.RenderWithErr(ctx.Tr("repo.settings.transfer_in_progress"), tplSettingsOptions, nil)
- } else if errors.Is(err, user_model.ErrBlockedUser) {
- ctx.RenderWithErr(ctx.Tr("repo.settings.transfer.blocked_user"), tplSettingsOptions, nil)
- } else {
- ctx.ServerError("TransferOwnership", err)
- }
+ if !repo.IsMirror {
+ ctx.HTTPError(http.StatusNotFound)
+ return
+ }
+ repo.IsMirror = false
- return
- }
+ if _, err := repo_service.CleanUpMigrateInfo(ctx, repo); err != nil {
+ ctx.ServerError("CleanUpMigrateInfo", err)
+ return
+ } else if err = repo_model.DeleteMirrorByRepoID(ctx, ctx.Repo.Repository.ID); err != nil {
+ ctx.ServerError("DeleteMirrorByRepoID", err)
+ return
+ }
+ log.Trace("Repository converted from mirror to regular: %s", repo.FullName())
+ ctx.Flash.Success(ctx.Tr("repo.settings.convert_succeed"))
+ ctx.Redirect(repo.Link())
+}
- if ctx.Repo.Repository.Status == repo_model.RepositoryPendingTransfer {
- log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner)
- ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner.DisplayName()))
- } else {
- log.Trace("Repository transferred: %s -> %s", oldFullname, ctx.Repo.Repository.FullName())
- ctx.Flash.Success(ctx.Tr("repo.settings.transfer_succeed"))
- }
+func handleSettingsPostConvertFork(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
+ if !ctx.Repo.IsOwner() {
+ ctx.HTTPError(http.StatusNotFound)
+ return
+ }
+ if err := repo.LoadOwner(ctx); err != nil {
+ ctx.ServerError("Convert Fork", err)
+ return
+ }
+ if repo.Name != form.RepoName {
+ ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
+ return
+ }
+
+ if !repo.IsFork {
+ ctx.HTTPError(http.StatusNotFound)
+ return
+ }
+
+ if !ctx.Doer.CanForkRepoIn(ctx.Repo.Owner) {
+ maxCreationLimit := ctx.Repo.Owner.MaxCreationLimit()
+ msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit)
+ ctx.Flash.Error(msg)
ctx.Redirect(repo.Link() + "/settings")
+ return
+ }
- case "cancel_transfer":
- if !ctx.Repo.IsOwner() {
- ctx.Error(http.StatusNotFound)
- return
- }
+ if err := repo_service.ConvertForkToNormalRepository(ctx, repo); err != nil {
+ log.Error("Unable to convert repository %-v from fork. Error: %v", repo, err)
+ ctx.ServerError("Convert Fork", err)
+ return
+ }
- repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository)
- if err != nil {
- if repo_model.IsErrNoPendingTransfer(err) {
- ctx.Flash.Error("repo.settings.transfer_abort_invalid")
- ctx.Redirect(repo.Link() + "/settings")
- } else {
- ctx.ServerError("GetPendingRepositoryTransfer", err)
- }
+ log.Trace("Repository converted from fork to regular: %s", repo.FullName())
+ ctx.Flash.Success(ctx.Tr("repo.settings.convert_fork_succeed"))
+ ctx.Redirect(repo.Link())
+}
- return
- }
+func handleSettingsPostTransfer(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
+ if !ctx.Repo.IsOwner() {
+ ctx.HTTPError(http.StatusNotFound)
+ return
+ }
+ if repo.Name != form.RepoName {
+ ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
+ return
+ }
- if err := repoTransfer.LoadAttributes(ctx); err != nil {
- ctx.ServerError("LoadRecipient", err)
+ newOwner, err := user_model.GetUserByName(ctx, ctx.FormString("new_owner_name"))
+ if err != nil {
+ if user_model.IsErrUserNotExist(err) {
+ ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil)
return
}
+ ctx.ServerError("IsUserExist", err)
+ return
+ }
- if err := repo_service.CancelRepositoryTransfer(ctx, ctx.Repo.Repository); err != nil {
- ctx.ServerError("CancelRepositoryTransfer", err)
+ if newOwner.Type == user_model.UserTypeOrganization {
+ if !ctx.Doer.IsAdmin && newOwner.Visibility == structs.VisibleTypePrivate && !organization.OrgFromUser(newOwner).HasMemberWithUserID(ctx, ctx.Doer.ID) {
+ // The user shouldn't know about this organization
+ ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil)
return
}
+ }
- log.Trace("Repository transfer process was cancelled: %s/%s ", ctx.Repo.Owner.Name, repo.Name)
- ctx.Flash.Success(ctx.Tr("repo.settings.transfer_abort_success", repoTransfer.Recipient.Name))
- ctx.Redirect(repo.Link() + "/settings")
+ // Close the GitRepo if open
+ if ctx.Repo.GitRepo != nil {
+ ctx.Repo.GitRepo.Close()
+ ctx.Repo.GitRepo = nil
+ }
- case "delete":
- if !ctx.Repo.IsOwner() {
- ctx.Error(http.StatusNotFound)
- return
- }
- if repo.Name != form.RepoName {
- ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
- return
+ oldFullname := repo.FullName()
+ if err := repo_service.StartRepositoryTransfer(ctx, ctx.Doer, newOwner, repo, nil); err != nil {
+ if repo_model.IsErrRepoAlreadyExist(err) {
+ ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil)
+ } else if repo_model.IsErrRepoTransferInProgress(err) {
+ ctx.RenderWithErr(ctx.Tr("repo.settings.transfer_in_progress"), tplSettingsOptions, nil)
+ } else if repo_service.IsRepositoryLimitReached(err) {
+ limit := err.(repo_service.LimitReachedError).Limit
+ ctx.RenderWithErr(ctx.TrN(limit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", limit), tplSettingsOptions, nil)
+ } else if errors.Is(err, user_model.ErrBlockedUser) {
+ ctx.RenderWithErr(ctx.Tr("repo.settings.transfer.blocked_user"), tplSettingsOptions, nil)
+ } else {
+ ctx.ServerError("TransferOwnership", err)
}
- // Close the gitrepository before doing this.
- if ctx.Repo.GitRepo != nil {
- ctx.Repo.GitRepo.Close()
- }
+ return
+ }
- if err := repo_service.DeleteRepository(ctx, ctx.Doer, ctx.Repo.Repository, true); err != nil {
- ctx.ServerError("DeleteRepository", err)
- return
- }
- log.Trace("Repository deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+ if ctx.Repo.Repository.Status == repo_model.RepositoryPendingTransfer {
+ log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner)
+ ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner.DisplayName()))
+ } else {
+ log.Trace("Repository transferred: %s -> %s", oldFullname, ctx.Repo.Repository.FullName())
+ ctx.Flash.Success(ctx.Tr("repo.settings.transfer_succeed"))
+ }
+ ctx.Redirect(repo.Link() + "/settings")
+}
- ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success"))
- ctx.Redirect(ctx.Repo.Owner.DashboardLink())
+func handleSettingsPostCancelTransfer(ctx *context.Context) {
+ repo := ctx.Repo.Repository
+ if !ctx.Repo.IsOwner() {
+ ctx.HTTPError(http.StatusNotFound)
+ return
+ }
- case "delete-wiki":
- if !ctx.Repo.IsOwner() {
- ctx.Error(http.StatusNotFound)
- return
- }
- if repo.Name != form.RepoName {
- ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
- return
+ repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository)
+ if err != nil {
+ if repo_model.IsErrNoPendingTransfer(err) {
+ ctx.Flash.Error("repo.settings.transfer_abort_invalid")
+ ctx.Redirect(repo.Link() + "/settings")
+ } else {
+ ctx.ServerError("GetPendingRepositoryTransfer", err)
}
+ return
+ }
- err := wiki_service.DeleteWiki(ctx, repo)
- if err != nil {
- log.Error("Delete Wiki: %v", err.Error())
- }
- log.Trace("Repository wiki deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+ if err := repo_service.CancelRepositoryTransfer(ctx, repoTransfer, ctx.Doer); err != nil {
+ ctx.ServerError("CancelRepositoryTransfer", err)
+ return
+ }
- ctx.Flash.Success(ctx.Tr("repo.settings.wiki_deletion_success"))
- ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+ log.Trace("Repository transfer process was cancelled: %s/%s ", ctx.Repo.Owner.Name, repo.Name)
+ ctx.Flash.Success(ctx.Tr("repo.settings.transfer_abort_success", repoTransfer.Recipient.Name))
+ ctx.Redirect(repo.Link() + "/settings")
+}
- case "archive":
- if !ctx.Repo.IsOwner() {
- ctx.Error(http.StatusForbidden)
- return
- }
+func handleSettingsPostDelete(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
+ if !ctx.Repo.IsOwner() {
+ ctx.HTTPError(http.StatusNotFound)
+ return
+ }
+ if repo.Name != form.RepoName {
+ ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
+ return
+ }
- if repo.IsMirror {
- ctx.Flash.Error(ctx.Tr("repo.settings.archive.error_ismirror"))
- ctx.Redirect(ctx.Repo.RepoLink + "/settings")
- return
- }
+ // Close the gitrepository before doing this.
+ if ctx.Repo.GitRepo != nil {
+ ctx.Repo.GitRepo.Close()
+ }
- if err := repo_model.SetArchiveRepoState(ctx, repo, true); err != nil {
- log.Error("Tried to archive a repo: %s", err)
- ctx.Flash.Error(ctx.Tr("repo.settings.archive.error"))
- ctx.Redirect(ctx.Repo.RepoLink + "/settings")
- return
- }
+ if err := repo_service.DeleteRepository(ctx, ctx.Doer, ctx.Repo.Repository, true); err != nil {
+ ctx.ServerError("DeleteRepository", err)
+ return
+ }
+ log.Trace("Repository deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name)
- if err := actions_model.CleanRepoScheduleTasks(ctx, repo); err != nil {
- log.Error("CleanRepoScheduleTasks for archived repo %s/%s: %v", ctx.Repo.Owner.Name, repo.Name, err)
- }
+ ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success"))
+ ctx.Redirect(ctx.Repo.Owner.DashboardLink())
+}
- // update issue indexer
- issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
+func handleSettingsPostDeleteWiki(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
+ if !ctx.Repo.IsOwner() {
+ ctx.HTTPError(http.StatusNotFound)
+ return
+ }
+ if repo.Name != form.RepoName {
+ ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
+ return
+ }
+
+ err := wiki_service.DeleteWiki(ctx, repo)
+ if err != nil {
+ log.Error("Delete Wiki: %v", err.Error())
+ }
+ log.Trace("Repository wiki deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+
+ ctx.Flash.Success(ctx.Tr("repo.settings.wiki_deletion_success"))
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+}
- ctx.Flash.Success(ctx.Tr("repo.settings.archive.success"))
+func handleSettingsPostArchive(ctx *context.Context) {
+ repo := ctx.Repo.Repository
+ if !ctx.Repo.IsOwner() {
+ ctx.HTTPError(http.StatusForbidden)
+ return
+ }
- log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+ if repo.IsMirror {
+ ctx.Flash.Error(ctx.Tr("repo.settings.archive.error_ismirror"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+ return
+ }
- case "unarchive":
- if !ctx.Repo.IsOwner() {
- ctx.Error(http.StatusForbidden)
- return
- }
+ if err := repo_model.SetArchiveRepoState(ctx, repo, true); err != nil {
+ log.Error("Tried to archive a repo: %s", err)
+ ctx.Flash.Error(ctx.Tr("repo.settings.archive.error"))
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+ return
+ }
- if err := repo_model.SetArchiveRepoState(ctx, repo, false); err != nil {
- log.Error("Tried to unarchive a repo: %s", err)
- ctx.Flash.Error(ctx.Tr("repo.settings.unarchive.error"))
- ctx.Redirect(ctx.Repo.RepoLink + "/settings")
- return
- }
+ if err := actions_service.CleanRepoScheduleTasks(ctx, repo); err != nil {
+ log.Error("CleanRepoScheduleTasks for archived repo %s/%s: %v", ctx.Repo.Owner.Name, repo.Name, err)
+ }
- if ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypeActions) {
- if err := actions_service.DetectAndHandleSchedules(ctx, repo); err != nil {
- log.Error("DetectAndHandleSchedules for un-archived repo %s/%s: %v", ctx.Repo.Owner.Name, repo.Name, err)
- }
- }
+ // update issue indexer
+ issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
- // update issue indexer
- issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
+ ctx.Flash.Success(ctx.Tr("repo.settings.archive.success"))
- ctx.Flash.Success(ctx.Tr("repo.settings.unarchive.success"))
+ log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+}
- log.Trace("Repository was un-archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+func handleSettingsPostUnarchive(ctx *context.Context) {
+ repo := ctx.Repo.Repository
+ if !ctx.Repo.IsOwner() {
+ ctx.HTTPError(http.StatusForbidden)
+ return
+ }
+
+ if err := repo_model.SetArchiveRepoState(ctx, repo, false); err != nil {
+ log.Error("Tried to unarchive a repo: %s", err)
+ ctx.Flash.Error(ctx.Tr("repo.settings.unarchive.error"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+ return
+ }
- case "visibility":
- if repo.IsFork {
- ctx.Flash.Error(ctx.Tr("repo.settings.visibility.fork_error"))
- ctx.Redirect(ctx.Repo.RepoLink + "/settings")
- return
+ if ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypeActions) {
+ if err := actions_service.DetectAndHandleSchedules(ctx, repo); err != nil {
+ log.Error("DetectAndHandleSchedules for un-archived repo %s/%s: %v", ctx.Repo.Owner.Name, repo.Name, err)
}
+ }
- var err error
+ // update issue indexer
+ issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
- // when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
- if setting.Repository.ForcePrivate && repo.IsPrivate && !ctx.Doer.IsAdmin {
- ctx.RenderWithErr(ctx.Tr("form.repository_force_private"), tplSettingsOptions, form)
- return
- }
+ ctx.Flash.Success(ctx.Tr("repo.settings.unarchive.success"))
- if repo.IsPrivate {
- err = repo_service.MakeRepoPublic(ctx, repo)
- } else {
- err = repo_service.MakeRepoPrivate(ctx, repo)
- }
+ log.Trace("Repository was un-archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+}
- if err != nil {
- log.Error("Tried to change the visibility of the repo: %s", err)
- ctx.Flash.Error(ctx.Tr("repo.settings.visibility.error"))
- ctx.Redirect(ctx.Repo.RepoLink + "/settings")
- return
- }
+func handleSettingsPostVisibility(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.RepoSettingForm)
+ repo := ctx.Repo.Repository
+ if repo.IsFork {
+ ctx.Flash.Error(ctx.Tr("repo.settings.visibility.fork_error"))
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+ return
+ }
- ctx.Flash.Success(ctx.Tr("repo.settings.visibility.success"))
+ var err error
- log.Trace("Repository visibility changed: %s/%s", ctx.Repo.Owner.Name, repo.Name)
- ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+ // when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
+ if setting.Repository.ForcePrivate && repo.IsPrivate && !ctx.Doer.IsAdmin {
+ ctx.RenderWithErr(ctx.Tr("form.repository_force_private"), tplSettingsOptions, form)
+ return
+ }
- default:
- ctx.NotFound("", nil)
+ if repo.IsPrivate {
+ err = repo_service.MakeRepoPublic(ctx, repo)
+ } else {
+ err = repo_service.MakeRepoPrivate(ctx, repo)
+ }
+
+ if err != nil {
+ log.Error("Tried to change the visibility of the repo: %s", err)
+ ctx.Flash.Error(ctx.Tr("repo.settings.visibility.error"))
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+ return
}
+
+ ctx.Flash.Success(ctx.Tr("repo.settings.visibility.success"))
+
+ log.Trace("Repository visibility changed: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings")
}
func handleSettingRemoteAddrError(ctx *context.Context, err error, form *forms.RepoSettingForm) {
diff --git a/routers/web/repo/setting/settings_test.go b/routers/web/repo/setting/settings_test.go
index 09586cc68d..15ebea888c 100644
--- a/routers/web/repo/setting/settings_test.go
+++ b/routers/web/repo/setting/settings_test.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/contexttest"
@@ -24,23 +25,8 @@ import (
"github.com/stretchr/testify/assert"
)
-func createSSHAuthorizedKeysTmpPath(t *testing.T) func() {
- tmpDir := t.TempDir()
-
- oldPath := setting.SSH.RootPath
- setting.SSH.RootPath = tmpDir
-
- return func() {
- setting.SSH.RootPath = oldPath
- }
-}
-
func TestAddReadOnlyDeployKey(t *testing.T) {
- if deferable := createSSHAuthorizedKeysTmpPath(t); deferable != nil {
- defer deferable()
- } else {
- return
- }
+ defer test.MockVariableValue(&setting.SSH.RootPath, t.TempDir())()
unittest.PrepareTestEnv(t)
ctx, _ := contexttest.MockContext(t, "user2/repo1/settings/keys")
@@ -54,7 +40,7 @@ func TestAddReadOnlyDeployKey(t *testing.T) {
}
web.SetForm(ctx, &addKeyForm)
DeployKeysPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &asymkey_model.DeployKey{
Name: addKeyForm.Title,
@@ -64,11 +50,7 @@ func TestAddReadOnlyDeployKey(t *testing.T) {
}
func TestAddReadWriteOnlyDeployKey(t *testing.T) {
- if deferable := createSSHAuthorizedKeysTmpPath(t); deferable != nil {
- defer deferable()
- } else {
- return
- }
+ defer test.MockVariableValue(&setting.SSH.RootPath, t.TempDir())()
unittest.PrepareTestEnv(t)
@@ -84,7 +66,7 @@ func TestAddReadWriteOnlyDeployKey(t *testing.T) {
}
web.SetForm(ctx, &addKeyForm)
DeployKeysPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &asymkey_model.DeployKey{
Name: addKeyForm.Title,
@@ -121,7 +103,7 @@ func TestCollaborationPost(t *testing.T) {
CollaborationPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
exists, err := repo_model.IsCollaborator(ctx, re.ID, 4)
assert.NoError(t, err)
@@ -147,7 +129,7 @@ func TestCollaborationPost_InactiveUser(t *testing.T) {
CollaborationPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
}
@@ -179,7 +161,7 @@ func TestCollaborationPost_AddCollaboratorTwice(t *testing.T) {
CollaborationPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
exists, err := repo_model.IsCollaborator(ctx, re.ID, 4)
assert.NoError(t, err)
@@ -188,7 +170,7 @@ func TestCollaborationPost_AddCollaboratorTwice(t *testing.T) {
// Try adding the same collaborator again
CollaborationPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
}
@@ -210,7 +192,7 @@ func TestCollaborationPost_NonExistentUser(t *testing.T) {
CollaborationPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
}
@@ -250,7 +232,7 @@ func TestAddTeamPost(t *testing.T) {
AddTeamPost(ctx)
assert.True(t, repo_service.HasRepository(db.DefaultContext, team, re.ID))
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assert.Empty(t, ctx.Flash.ErrorMsg)
}
@@ -290,7 +272,7 @@ func TestAddTeamPost_NotAllowed(t *testing.T) {
AddTeamPost(ctx)
assert.False(t, repo_service.HasRepository(db.DefaultContext, team, re.ID))
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
}
@@ -331,7 +313,7 @@ func TestAddTeamPost_AddTeamTwice(t *testing.T) {
AddTeamPost(ctx)
assert.True(t, repo_service.HasRepository(db.DefaultContext, team, re.ID))
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
}
@@ -364,7 +346,7 @@ func TestAddTeamPost_NonExistentTeam(t *testing.T) {
ctx.Repo = repo
AddTeamPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
}
diff --git a/routers/web/repo/setting/variables.go b/routers/web/repo/setting/variables.go
deleted file mode 100644
index 9b5453f043..0000000000
--- a/routers/web/repo/setting/variables.go
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package setting
-
-import (
- "errors"
- "net/http"
-
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- shared "code.gitea.io/gitea/routers/web/shared/actions"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
-)
-
-const (
- tplRepoVariables templates.TplName = "repo/settings/actions"
- tplOrgVariables templates.TplName = "org/settings/actions"
- tplUserVariables templates.TplName = "user/settings/actions"
- tplAdminVariables templates.TplName = "admin/actions"
-)
-
-type variablesCtx struct {
- OwnerID int64
- RepoID int64
- IsRepo bool
- IsOrg bool
- IsUser bool
- IsGlobal bool
- VariablesTemplate templates.TplName
- RedirectLink string
-}
-
-func getVariablesCtx(ctx *context.Context) (*variablesCtx, error) {
- if ctx.Data["PageIsRepoSettings"] == true {
- return &variablesCtx{
- OwnerID: 0,
- RepoID: ctx.Repo.Repository.ID,
- IsRepo: true,
- VariablesTemplate: tplRepoVariables,
- RedirectLink: ctx.Repo.RepoLink + "/settings/actions/variables",
- }, nil
- }
-
- if ctx.Data["PageIsOrgSettings"] == true {
- err := shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
- return nil, nil
- }
- return &variablesCtx{
- OwnerID: ctx.ContextUser.ID,
- RepoID: 0,
- IsOrg: true,
- VariablesTemplate: tplOrgVariables,
- RedirectLink: ctx.Org.OrgLink + "/settings/actions/variables",
- }, nil
- }
-
- if ctx.Data["PageIsUserSettings"] == true {
- return &variablesCtx{
- OwnerID: ctx.Doer.ID,
- RepoID: 0,
- IsUser: true,
- VariablesTemplate: tplUserVariables,
- RedirectLink: setting.AppSubURL + "/user/settings/actions/variables",
- }, nil
- }
-
- if ctx.Data["PageIsAdmin"] == true {
- return &variablesCtx{
- OwnerID: 0,
- RepoID: 0,
- IsGlobal: true,
- VariablesTemplate: tplAdminVariables,
- RedirectLink: setting.AppSubURL + "/-/admin/actions/variables",
- }, nil
- }
-
- return nil, errors.New("unable to set Variables context")
-}
-
-func Variables(ctx *context.Context) {
- ctx.Data["Title"] = ctx.Tr("actions.variables")
- ctx.Data["PageType"] = "variables"
- ctx.Data["PageIsSharedSettingsVariables"] = true
-
- vCtx, err := getVariablesCtx(ctx)
- if err != nil {
- ctx.ServerError("getVariablesCtx", err)
- return
- }
-
- shared.SetVariablesContext(ctx, vCtx.OwnerID, vCtx.RepoID)
- if ctx.Written() {
- return
- }
-
- ctx.HTML(http.StatusOK, vCtx.VariablesTemplate)
-}
-
-func VariableCreate(ctx *context.Context) {
- vCtx, err := getVariablesCtx(ctx)
- if err != nil {
- ctx.ServerError("getVariablesCtx", err)
- return
- }
-
- if ctx.HasError() { // form binding validation error
- ctx.JSONError(ctx.GetErrMsg())
- return
- }
-
- shared.CreateVariable(ctx, vCtx.OwnerID, vCtx.RepoID, vCtx.RedirectLink)
-}
-
-func VariableUpdate(ctx *context.Context) {
- vCtx, err := getVariablesCtx(ctx)
- if err != nil {
- ctx.ServerError("getVariablesCtx", err)
- return
- }
-
- if ctx.HasError() { // form binding validation error
- ctx.JSONError(ctx.GetErrMsg())
- return
- }
-
- shared.UpdateVariable(ctx, vCtx.RedirectLink)
-}
-
-func VariableDelete(ctx *context.Context) {
- vCtx, err := getVariablesCtx(ctx)
- if err != nil {
- ctx.ServerError("getVariablesCtx", err)
- return
- }
- shared.DeleteVariable(ctx, vCtx.RedirectLink)
-}
diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go
index 1b0ba83af4..f107449749 100644
--- a/routers/web/repo/setting/webhook.go
+++ b/routers/web/repo/setting/webhook.go
@@ -112,7 +112,7 @@ func getOwnerRepoCtx(ctx *context.Context) (*ownerRepoCtx, error) {
func checkHookType(ctx *context.Context) string {
hookType := strings.ToLower(ctx.PathParam("type"))
if !util.SliceContainsString(setting.Webhook.Types, hookType, true) {
- ctx.NotFound("checkHookType", nil)
+ ctx.NotFound(nil)
return ""
}
return hookType
@@ -163,27 +163,30 @@ func ParseHookEvent(form forms.WebhookForm) *webhook_module.HookEvent {
SendEverything: form.SendEverything(),
ChooseEvents: form.ChooseEvents(),
HookEvents: webhook_module.HookEvents{
- Create: form.Create,
- Delete: form.Delete,
- Fork: form.Fork,
- Issues: form.Issues,
- IssueAssign: form.IssueAssign,
- IssueLabel: form.IssueLabel,
- IssueMilestone: form.IssueMilestone,
- IssueComment: form.IssueComment,
- Release: form.Release,
- Push: form.Push,
- PullRequest: form.PullRequest,
- PullRequestAssign: form.PullRequestAssign,
- PullRequestLabel: form.PullRequestLabel,
- PullRequestMilestone: form.PullRequestMilestone,
- PullRequestComment: form.PullRequestComment,
- PullRequestReview: form.PullRequestReview,
- PullRequestSync: form.PullRequestSync,
- PullRequestReviewRequest: form.PullRequestReviewRequest,
- Wiki: form.Wiki,
- Repository: form.Repository,
- Package: form.Package,
+ webhook_module.HookEventCreate: form.Create,
+ webhook_module.HookEventDelete: form.Delete,
+ webhook_module.HookEventFork: form.Fork,
+ webhook_module.HookEventIssues: form.Issues,
+ webhook_module.HookEventIssueAssign: form.IssueAssign,
+ webhook_module.HookEventIssueLabel: form.IssueLabel,
+ webhook_module.HookEventIssueMilestone: form.IssueMilestone,
+ webhook_module.HookEventIssueComment: form.IssueComment,
+ webhook_module.HookEventRelease: form.Release,
+ webhook_module.HookEventPush: form.Push,
+ webhook_module.HookEventPullRequest: form.PullRequest,
+ webhook_module.HookEventPullRequestAssign: form.PullRequestAssign,
+ webhook_module.HookEventPullRequestLabel: form.PullRequestLabel,
+ webhook_module.HookEventPullRequestMilestone: form.PullRequestMilestone,
+ webhook_module.HookEventPullRequestComment: form.PullRequestComment,
+ webhook_module.HookEventPullRequestReview: form.PullRequestReview,
+ webhook_module.HookEventPullRequestSync: form.PullRequestSync,
+ webhook_module.HookEventPullRequestReviewRequest: form.PullRequestReviewRequest,
+ webhook_module.HookEventWiki: form.Wiki,
+ webhook_module.HookEventRepository: form.Repository,
+ webhook_module.HookEventPackage: form.Package,
+ webhook_module.HookEventStatus: form.Status,
+ webhook_module.HookEventWorkflowRun: form.WorkflowRun,
+ webhook_module.HookEventWorkflowJob: form.WorkflowJob,
},
BranchFilter: form.BranchFilter,
}
@@ -195,7 +198,6 @@ type webhookParams struct {
URL string
ContentType webhook.HookContentType
- Secret string
HTTPMethod string
WebhookForm forms.WebhookForm
Meta any
@@ -234,7 +236,7 @@ func createWebhook(ctx *context.Context, params webhookParams) {
URL: params.URL,
HTTPMethod: params.HTTPMethod,
ContentType: params.ContentType,
- Secret: params.Secret,
+ Secret: params.WebhookForm.Secret,
HookEvent: ParseHookEvent(params.WebhookForm),
IsActive: params.WebhookForm.Active,
Type: params.Type,
@@ -287,7 +289,7 @@ func editWebhook(ctx *context.Context, params webhookParams) {
w.URL = params.URL
w.ContentType = params.ContentType
- w.Secret = params.Secret
+ w.Secret = params.WebhookForm.Secret
w.HookEvent = ParseHookEvent(params.WebhookForm)
w.IsActive = params.WebhookForm.Active
w.HTTPMethod = params.HTTPMethod
@@ -333,7 +335,6 @@ func giteaHookParams(ctx *context.Context) webhookParams {
Type: webhook_module.GITEA,
URL: form.PayloadURL,
ContentType: contentType,
- Secret: form.Secret,
HTTPMethod: form.HTTPMethod,
WebhookForm: form.WebhookForm,
}
@@ -361,7 +362,6 @@ func gogsHookParams(ctx *context.Context) webhookParams {
Type: webhook_module.GOGS,
URL: form.PayloadURL,
ContentType: contentType,
- Secret: form.Secret,
WebhookForm: form.WebhookForm,
}
}
@@ -600,7 +600,7 @@ func checkWebhook(ctx *context.Context) (*ownerRepoCtx, *webhook.Webhook) {
}
if err != nil || w == nil {
if webhook.IsErrWebhookNotExist(err) {
- ctx.NotFound("GetWebhookByID", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("GetWebhookByID", err)
}
@@ -654,6 +654,8 @@ func TestWebhook(ctx *context.Context) {
}
// Grab latest commit or fake one if it's empty repository.
+ // Note: in old code, the "ctx.Repo.Commit" is the last commit of the default branch.
+ // New code doesn't set that commit, so it always uses the fake commit to test webhook.
commit := ctx.Repo.Commit
if commit == nil {
ghost := user_model.NewGhostUser()
@@ -715,7 +717,7 @@ func ReplayWebhook(ctx *context.Context) {
if err := webhook_service.ReplayHookTask(ctx, w, hookTaskUUID); err != nil {
if webhook.IsErrHookTaskNotExist(err) {
- ctx.NotFound("ReplayHookTask", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("ReplayHookTask", err)
}
diff --git a/routers/web/repo/star.go b/routers/web/repo/star.go
new file mode 100644
index 0000000000..00c06b7d02
--- /dev/null
+++ b/routers/web/repo/star.go
@@ -0,0 +1,31 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "net/http"
+
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/services/context"
+)
+
+const tplStarUnstar templates.TplName = "repo/star_unstar"
+
+func ActionStar(ctx *context.Context) {
+ err := repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, ctx.PathParam("action") == "star")
+ if err != nil {
+ handleActionError(ctx, err)
+ return
+ }
+
+ ctx.Data["IsStaringRepo"] = repo_model.IsStaring(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
+ ctx.Data["Repository"], err = repo_model.GetRepositoryByName(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.Name)
+ if err != nil {
+ ctx.ServerError("GetRepositoryByName", err)
+ return
+ }
+ ctx.RespHeader().Add("hx-trigger", "refreshUserCards") // see the `hx-trigger="refreshUserCards ..."` comments in tmpl
+ ctx.HTML(http.StatusOK, tplStarUnstar)
+}
diff --git a/routers/web/repo/transfer.go b/routers/web/repo/transfer.go
new file mode 100644
index 0000000000..5553eee674
--- /dev/null
+++ b/routers/web/repo/transfer.go
@@ -0,0 +1,38 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "code.gitea.io/gitea/services/context"
+ repo_service "code.gitea.io/gitea/services/repository"
+)
+
+func acceptTransfer(ctx *context.Context) {
+ err := repo_service.AcceptTransferOwnership(ctx, ctx.Repo.Repository, ctx.Doer)
+ if err == nil {
+ ctx.Flash.Success(ctx.Tr("repo.settings.transfer.success"))
+ ctx.Redirect(ctx.Repo.Repository.Link())
+ return
+ }
+ handleActionError(ctx, err)
+}
+
+func rejectTransfer(ctx *context.Context) {
+ err := repo_service.RejectRepositoryTransfer(ctx, ctx.Repo.Repository, ctx.Doer)
+ if err == nil {
+ ctx.Flash.Success(ctx.Tr("repo.settings.transfer.rejected"))
+ ctx.Redirect(ctx.Repo.Repository.Link())
+ return
+ }
+ handleActionError(ctx, err)
+}
+
+func ActionTransfer(ctx *context.Context) {
+ switch ctx.PathParam("action") {
+ case "accept_transfer":
+ acceptTransfer(ctx)
+ case "reject_transfer":
+ rejectTransfer(ctx)
+ }
+}
diff --git a/routers/web/repo/treelist.go b/routers/web/repo/treelist.go
index d11af4669f..7d7f5a1473 100644
--- a/routers/web/repo/treelist.go
+++ b/routers/web/repo/treelist.go
@@ -4,11 +4,18 @@
package repo
import (
+ "html/template"
"net/http"
+ "path"
+ "strings"
+ pull_model "code.gitea.io/gitea/models/pull"
"code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/gitdiff"
+ files_service "code.gitea.io/gitea/services/repository/files"
"github.com/go-enry/go-enry/v2"
)
@@ -52,3 +59,95 @@ func isExcludedEntry(entry *git.TreeEntry) bool {
return false
}
+
+// WebDiffFileItem is used by frontend, check the field names in frontend before changing
+type WebDiffFileItem struct {
+ FullName string
+ DisplayName string
+ NameHash string
+ DiffStatus string
+ EntryMode string
+ IsViewed bool
+ Children []*WebDiffFileItem
+ FileIcon template.HTML
+}
+
+// WebDiffFileTree is used by frontend, check the field names in frontend before changing
+type WebDiffFileTree struct {
+ TreeRoot WebDiffFileItem
+}
+
+// transformDiffTreeForWeb transforms a gitdiff.DiffTree into a WebDiffFileTree for Web UI rendering
+// it also takes a map of file names to their viewed state, which is used to mark files as viewed
+func transformDiffTreeForWeb(renderedIconPool *fileicon.RenderedIconPool, diffTree *gitdiff.DiffTree, filesViewedState map[string]pull_model.ViewedState) (dft WebDiffFileTree) {
+ dirNodes := map[string]*WebDiffFileItem{"": &dft.TreeRoot}
+ addItem := func(item *WebDiffFileItem) {
+ var parentPath string
+ pos := strings.LastIndexByte(item.FullName, '/')
+ if pos == -1 {
+ item.DisplayName = item.FullName
+ } else {
+ parentPath = item.FullName[:pos]
+ item.DisplayName = item.FullName[pos+1:]
+ }
+ parentNode, parentExists := dirNodes[parentPath]
+ if !parentExists {
+ parentNode = &dft.TreeRoot
+ fields := strings.Split(parentPath, "/")
+ for idx, field := range fields {
+ nodePath := strings.Join(fields[:idx+1], "/")
+ node, ok := dirNodes[nodePath]
+ if !ok {
+ node = &WebDiffFileItem{EntryMode: "tree", DisplayName: field, FullName: nodePath}
+ dirNodes[nodePath] = node
+ parentNode.Children = append(parentNode.Children, node)
+ }
+ parentNode = node
+ }
+ }
+ parentNode.Children = append(parentNode.Children, item)
+ }
+
+ for _, file := range diffTree.Files {
+ item := &WebDiffFileItem{FullName: file.HeadPath, DiffStatus: file.Status}
+ item.IsViewed = filesViewedState[item.FullName] == pull_model.Viewed
+ item.NameHash = git.HashFilePathForWebUI(item.FullName)
+ item.FileIcon = fileicon.RenderEntryIconHTML(renderedIconPool, &fileicon.EntryInfo{BaseName: path.Base(file.HeadPath), EntryMode: file.HeadMode})
+
+ switch file.HeadMode {
+ case git.EntryModeTree:
+ item.EntryMode = "tree"
+ case git.EntryModeCommit:
+ item.EntryMode = "commit" // submodule
+ default:
+ // default to empty, and will be treated as "blob" file because there is no "symlink" support yet
+ }
+ addItem(item)
+ }
+
+ var mergeSingleDir func(node *WebDiffFileItem)
+ mergeSingleDir = func(node *WebDiffFileItem) {
+ if len(node.Children) == 1 {
+ if child := node.Children[0]; child.EntryMode == "tree" {
+ node.FullName = child.FullName
+ node.DisplayName = node.DisplayName + "/" + child.DisplayName
+ node.Children = child.Children
+ mergeSingleDir(node)
+ }
+ }
+ }
+ for _, node := range dft.TreeRoot.Children {
+ mergeSingleDir(node)
+ }
+ return dft
+}
+
+func TreeViewNodes(ctx *context.Context) {
+ renderedIconPool := fileicon.NewRenderedIconPool()
+ results, err := files_service.GetTreeViewNodes(ctx, renderedIconPool, ctx.Repo.Commit, ctx.Repo.TreePath, ctx.FormString("sub_path"))
+ if err != nil {
+ ctx.ServerError("GetTreeViewNodes", err)
+ return
+ }
+ ctx.JSON(http.StatusOK, map[string]any{"fileTreeNodes": results, "renderedIconPool": renderedIconPool.IconSVGs})
+}
diff --git a/routers/web/repo/treelist_test.go b/routers/web/repo/treelist_test.go
new file mode 100644
index 0000000000..94ba60661b
--- /dev/null
+++ b/routers/web/repo/treelist_test.go
@@ -0,0 +1,68 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "html/template"
+ "testing"
+
+ pull_model "code.gitea.io/gitea/models/pull"
+ "code.gitea.io/gitea/modules/fileicon"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/services/gitdiff"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTransformDiffTreeForWeb(t *testing.T) {
+ renderedIconPool := fileicon.NewRenderedIconPool()
+ ret := transformDiffTreeForWeb(renderedIconPool, &gitdiff.DiffTree{Files: []*gitdiff.DiffTreeRecord{
+ {
+ Status: "changed",
+ HeadPath: "dir-a/dir-a-x/file-deep",
+ HeadMode: git.EntryModeBlob,
+ },
+ {
+ Status: "added",
+ HeadPath: "file1",
+ HeadMode: git.EntryModeBlob,
+ },
+ }}, map[string]pull_model.ViewedState{
+ "dir-a/dir-a-x/file-deep": pull_model.Viewed,
+ })
+
+ mockIconForFile := func(id string) template.HTML {
+ return template.HTML(`<svg class="svg git-entry-icon octicon-file" width="16" height="16" aria-hidden="true"><use xlink:href="#` + id + `"></use></svg>`)
+ }
+ assert.Equal(t, WebDiffFileTree{
+ TreeRoot: WebDiffFileItem{
+ Children: []*WebDiffFileItem{
+ {
+ EntryMode: "tree",
+ DisplayName: "dir-a/dir-a-x",
+ FullName: "dir-a/dir-a-x",
+ Children: []*WebDiffFileItem{
+ {
+ EntryMode: "",
+ DisplayName: "file-deep",
+ FullName: "dir-a/dir-a-x/file-deep",
+ NameHash: "4acf7eef1c943a09e9f754e93ff190db8583236b",
+ DiffStatus: "changed",
+ IsViewed: true,
+ FileIcon: mockIconForFile(`svg-mfi-file`),
+ },
+ },
+ },
+ {
+ EntryMode: "",
+ DisplayName: "file1",
+ FullName: "file1",
+ NameHash: "60b27f004e454aca81b0480209cce5081ec52390",
+ DiffStatus: "added",
+ FileIcon: mockIconForFile(`svg-mfi-file`),
+ },
+ },
+ },
+ }, ret)
+}
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 14fc9038f3..773919c054 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -12,6 +12,7 @@ import (
"io"
"net/http"
"net/url"
+ "path"
"strings"
"time"
@@ -29,6 +30,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
+ "code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
@@ -38,6 +40,7 @@ import (
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/typesniffer"
"code.gitea.io/gitea/modules/util"
+ asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/context"
repo_service "code.gitea.io/gitea/services/repository"
@@ -46,69 +49,74 @@ import (
)
const (
- tplRepoEMPTY templates.TplName = "repo/empty"
- tplRepoHome templates.TplName = "repo/home"
- tplRepoViewList templates.TplName = "repo/view_list"
- tplWatchers templates.TplName = "repo/watchers"
- tplForks templates.TplName = "repo/forks"
- tplMigrating templates.TplName = "repo/migrate/migrating"
+ tplRepoEMPTY templates.TplName = "repo/empty"
+ tplRepoHome templates.TplName = "repo/home"
+ tplRepoView templates.TplName = "repo/view"
+ tplRepoViewContent templates.TplName = "repo/view_content"
+ tplRepoViewList templates.TplName = "repo/view_list"
+ tplWatchers templates.TplName = "repo/watchers"
+ tplForks templates.TplName = "repo/forks"
+ tplMigrating templates.TplName = "repo/migrate/migrating"
)
type fileInfo struct {
- isTextFile bool
- isLFSFile bool
- fileSize int64
- lfsMeta *lfs.Pointer
- st typesniffer.SniffedType
+ fileSize int64
+ lfsMeta *lfs.Pointer
+ st typesniffer.SniffedType
}
-func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte, io.ReadCloser, *fileInfo, error) {
- dataRc, err := blob.DataAsync()
+func (fi *fileInfo) isLFSFile() bool {
+ return fi.lfsMeta != nil && fi.lfsMeta.Oid != ""
+}
+
+func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) (buf []byte, dataRc io.ReadCloser, fi *fileInfo, err error) {
+ dataRc, err = blob.DataAsync()
if err != nil {
return nil, nil, nil, err
}
- buf := make([]byte, 1024)
+ const prefetchSize = lfs.MetaFileMaxSize
+
+ buf = make([]byte, prefetchSize)
n, _ := util.ReadAtMost(dataRc, buf)
buf = buf[:n]
- st := typesniffer.DetectContentType(buf)
- isTextFile := st.IsText()
+ fi = &fileInfo{fileSize: blob.Size(), st: typesniffer.DetectContentType(buf)}
// FIXME: what happens when README file is an image?
- if !isTextFile || !setting.LFS.StartServer {
- return buf, dataRc, &fileInfo{isTextFile, false, blob.Size(), nil, st}, nil
+ if !fi.st.IsText() || !setting.LFS.StartServer {
+ return buf, dataRc, fi, nil
}
pointer, _ := lfs.ReadPointerFromBuffer(buf)
- if !pointer.IsValid() { // fallback to plain file
- return buf, dataRc, &fileInfo{isTextFile, false, blob.Size(), nil, st}, nil
+ if !pointer.IsValid() { // fallback to a plain file
+ return buf, dataRc, fi, nil
}
meta, err := git_model.GetLFSMetaObjectByOid(ctx, repoID, pointer.Oid)
- if err != nil { // fallback to plain file
+ if err != nil { // fallback to a plain file
log.Warn("Unable to access LFS pointer %s in repo %d: %v", pointer.Oid, repoID, err)
- return buf, dataRc, &fileInfo{isTextFile, false, blob.Size(), nil, st}, nil
+ return buf, dataRc, fi, nil
}
- dataRc.Close()
-
+ // close the old dataRc and open the real LFS target
+ _ = dataRc.Close()
dataRc, err = lfs.ReadMetaObject(pointer)
if err != nil {
return nil, nil, nil, err
}
- buf = make([]byte, 1024)
+ buf = make([]byte, prefetchSize)
n, err = util.ReadAtMost(dataRc, buf)
if err != nil {
- dataRc.Close()
- return nil, nil, nil, err
+ _ = dataRc.Close()
+ return nil, nil, fi, err
}
buf = buf[:n]
-
- st = typesniffer.DetectContentType(buf)
-
- return buf, dataRc, &fileInfo{st.IsText(), true, meta.Size, &meta.Pointer, st}, nil
+ fi.st = typesniffer.DetectContentType(buf)
+ fi.fileSize = blob.Size()
+ fi.lfsMeta = &meta.Pointer
+ return buf, dataRc, fi, nil
}
func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool {
@@ -116,7 +124,7 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool {
// or of directory if not in root directory.
ctx.Data["LatestCommit"] = latestCommit
if latestCommit != nil {
- verification := asymkey_model.ParseCommitWithSignature(ctx, latestCommit)
+ verification := asymkey_service.ParseCommitWithSignature(ctx, latestCommit)
if err := asymkey_model.CalculateTrustStatus(verification, ctx.Repo.Repository.GetTrustModel(), func(user *user_model.User) (bool, error) {
return repo_model.IsOwnerMemberCollaborator(ctx, ctx.Repo.Repository, user.ID)
@@ -127,7 +135,7 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool {
ctx.Data["LatestCommitVerification"] = verification
ctx.Data["LatestCommitUser"] = user_model.ValidateCommitWithEmail(ctx, latestCommit)
- statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, latestCommit.ID.String(), db.ListOptionsAll)
+ statuses, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, latestCommit.ID.String(), db.ListOptionsAll)
if err != nil {
log.Error("GetLatestCommitStatus: %v", err)
}
@@ -215,7 +223,7 @@ func checkHomeCodeViewable(ctx *context.Context) {
}
}
- ctx.NotFound("Home", errors.New(ctx.Locale.TrString("units.error.no_unit_allowed_repo")))
+ ctx.NotFound(errors.New(ctx.Locale.TrString("units.error.no_unit_allowed_repo")))
}
// LastCommit returns lastCommit data for the provided branch/tag/commit and directory (in url) and filenames in body
@@ -243,12 +251,25 @@ func LastCommit(ctx *context.Context) {
ctx.Data["ParentPath"] = "/" + paths[len(paths)-2]
}
}
- branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
+ branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
ctx.Data["BranchLink"] = branchLink
ctx.HTML(http.StatusOK, tplRepoViewList)
}
+func prepareDirectoryFileIcons(ctx *context.Context, files []git.CommitInfo) {
+ renderedIconPool := fileicon.NewRenderedIconPool()
+ fileIcons := map[string]template.HTML{}
+ for _, f := range files {
+ fullPath := path.Join(ctx.Repo.TreePath, f.Entry.Name())
+ entryInfo := fileicon.EntryInfoFromGitTreeEntry(ctx.Repo.Commit, fullPath, f.Entry)
+ fileIcons[f.Entry.Name()] = fileicon.RenderEntryIconHTML(renderedIconPool, entryInfo)
+ }
+ fileIcons[".."] = fileicon.RenderEntryIconHTML(renderedIconPool, fileicon.EntryInfoFolder())
+ ctx.Data["FileIcons"] = fileIcons
+ ctx.Data["FileIconPoolHTML"] = renderedIconPool.RenderToHTML()
+}
+
func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entries {
tree, err := ctx.Repo.Commit.SubTree(ctx.Repo.TreePath)
if err != nil {
@@ -290,6 +311,7 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
return nil
}
ctx.Data["Files"] = files
+ prepareDirectoryFileIcons(ctx, files)
for _, f := range files {
if f.Commit == nil {
ctx.Data["HasFilesWithoutLatestCommit"] = true
@@ -301,7 +323,7 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
return nil
}
- branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
+ branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
treeLink := branchLink
if len(ctx.Repo.TreePath) > 0 {
@@ -309,7 +331,6 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
}
ctx.Data["TreeLink"] = treeLink
- ctx.Data["SSHDomain"] = setting.SSH.Domain
return allEntries
}
@@ -379,9 +400,10 @@ func Forks(ctx *context.Context) {
}
pager := context.NewPagination(int(total), pageSize, page, 5)
+ ctx.Data["ShowRepoOwnerAvatar"] = true
+ ctx.Data["ShowRepoOwnerOnList"] = true
ctx.Data["Page"] = pager
-
- ctx.Data["Forks"] = forks
+ ctx.Data["Repos"] = forks
ctx.HTML(http.StatusOK, tplForks)
}
diff --git a/routers/web/repo/view_file.go b/routers/web/repo/view_file.go
index 17c2821824..2d5bddd939 100644
--- a/routers/web/repo/view_file.go
+++ b/routers/web/repo/view_file.go
@@ -9,7 +9,6 @@ import (
"image"
"io"
"path"
- "slices"
"strings"
git_model "code.gitea.io/gitea/models/git"
@@ -19,44 +18,165 @@ import (
"code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/git/attribute"
"code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/typesniffer"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context"
issue_service "code.gitea.io/gitea/services/issue"
- files_service "code.gitea.io/gitea/services/repository/files"
"github.com/nektos/act/pkg/model"
)
-func prepareToRenderFile(ctx *context.Context, entry *git.TreeEntry) {
- ctx.Data["IsViewFile"] = true
- ctx.Data["HideRepoInfo"] = true
- blob := entry.Blob()
- buf, dataRc, fInfo, err := getFileReader(ctx, ctx.Repo.Repository.ID, blob)
+func prepareLatestCommitInfo(ctx *context.Context) bool {
+ commit, err := ctx.Repo.Commit.GetCommitByPath(ctx.Repo.TreePath)
if err != nil {
- ctx.ServerError("getFileReader", err)
- return
+ ctx.ServerError("GetCommitByPath", err)
+ return false
}
- defer dataRc.Close()
- ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
- ctx.Data["FileIsSymlink"] = entry.IsLink()
- ctx.Data["FileName"] = blob.Name()
- ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
+ return loadLatestCommitData(ctx, commit)
+}
- commit, err := ctx.Repo.Commit.GetCommitByPath(ctx.Repo.TreePath)
+func prepareFileViewLfsAttrs(ctx *context.Context) (*attribute.Attributes, bool) {
+ attrsMap, err := attribute.CheckAttributes(ctx, ctx.Repo.GitRepo, ctx.Repo.CommitID, attribute.CheckAttributeOpts{
+ Filenames: []string{ctx.Repo.TreePath},
+ Attributes: []string{attribute.LinguistGenerated, attribute.LinguistVendored, attribute.LinguistLanguage, attribute.GitlabLanguage},
+ })
if err != nil {
- ctx.ServerError("GetCommitByPath", err)
- return
+ ctx.ServerError("attribute.CheckAttributes", err)
+ return nil, false
+ }
+ attrs := attrsMap[ctx.Repo.TreePath]
+ if attrs == nil {
+ // this case shouldn't happen, just in case.
+ setting.PanicInDevOrTesting("no attributes found for %s", ctx.Repo.TreePath)
+ attrs = attribute.NewAttributes()
+ }
+ ctx.Data["IsVendored"], ctx.Data["IsGenerated"] = attrs.GetVendored().Value(), attrs.GetGenerated().Value()
+ return attrs, true
+}
+
+func handleFileViewRenderMarkup(ctx *context.Context, filename string, sniffedType typesniffer.SniffedType, prefetchBuf []byte, utf8Reader io.Reader) bool {
+ markupType := markup.DetectMarkupTypeByFileName(filename)
+ if markupType == "" {
+ markupType = markup.DetectRendererType(filename, sniffedType, prefetchBuf)
+ }
+ if markupType == "" {
+ return false
+ }
+
+ ctx.Data["HasSourceRenderedToggle"] = true
+
+ if ctx.FormString("display") == "source" {
+ return false
+ }
+
+ ctx.Data["MarkupType"] = markupType
+ metas := ctx.Repo.Repository.ComposeRepoFileMetas(ctx)
+ metas["RefTypeNameSubURL"] = ctx.Repo.RefTypeNameSubURL()
+ rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{
+ CurrentRefPath: ctx.Repo.RefTypeNameSubURL(),
+ CurrentTreePath: path.Dir(ctx.Repo.TreePath),
+ }).
+ WithMarkupType(markupType).
+ WithRelativePath(ctx.Repo.TreePath).
+ WithMetas(metas)
+
+ var err error
+ ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, rctx, utf8Reader)
+ if err != nil {
+ ctx.ServerError("Render", err)
+ return true
+ }
+ // to prevent iframe from loading third-party url
+ ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'")
+ return true
+}
+
+func handleFileViewRenderSource(ctx *context.Context, filename string, attrs *attribute.Attributes, fInfo *fileInfo, utf8Reader io.Reader) bool {
+ if ctx.FormString("display") == "rendered" || !fInfo.st.IsRepresentableAsText() {
+ return false
+ }
+
+ if !fInfo.st.IsText() {
+ if ctx.FormString("display") == "" {
+ // not text but representable as text, e.g. SVG
+ // since there is no "display" is specified, let other renders to handle
+ return false
+ }
+ ctx.Data["HasSourceRenderedToggle"] = true
+ }
+
+ buf, _ := io.ReadAll(utf8Reader)
+ // The Open Group Base Specification: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html
+ // empty: 0 lines; "a": 1 incomplete-line; "a\n": 1 line; "a\nb": 1 line, 1 incomplete-line;
+ // Gitea uses the definition (like most modern editors):
+ // empty: 0 lines; "a": 1 line; "a\n": 2 lines; "a\nb": 2 lines;
+ // When rendering, the last empty line is not rendered in UI, while the line-number is still counted, to tell users that the file contains a trailing EOL.
+ // To make the UI more consistent, it could use an icon mark to indicate that there is no trailing EOL, and show line-number as the rendered lines.
+ // This NumLines is only used for the display on the UI: "xxx lines"
+ if len(buf) == 0 {
+ ctx.Data["NumLines"] = 0
+ } else {
+ ctx.Data["NumLines"] = bytes.Count(buf, []byte{'\n'}) + 1
+ }
+
+ language := attrs.GetLanguage().Value()
+ fileContent, lexerName, err := highlight.File(filename, language, buf)
+ ctx.Data["LexerName"] = lexerName
+ if err != nil {
+ log.Error("highlight.File failed, fallback to plain text: %v", err)
+ fileContent = highlight.PlainText(buf)
+ }
+ status := &charset.EscapeStatus{}
+ statuses := make([]*charset.EscapeStatus, len(fileContent))
+ for i, line := range fileContent {
+ statuses[i], fileContent[i] = charset.EscapeControlHTML(line, ctx.Locale)
+ status = status.Or(statuses[i])
+ }
+ ctx.Data["EscapeStatus"] = status
+ ctx.Data["FileContent"] = fileContent
+ ctx.Data["LineEscapeStatus"] = statuses
+ return true
+}
+
+func handleFileViewRenderImage(ctx *context.Context, fInfo *fileInfo, prefetchBuf []byte) bool {
+ if !fInfo.st.IsImage() {
+ return false
+ }
+ if fInfo.st.IsSvgImage() && !setting.UI.SVG.Enabled {
+ return false
+ }
+ if fInfo.st.IsSvgImage() {
+ ctx.Data["HasSourceRenderedToggle"] = true
+ } else {
+ img, _, err := image.DecodeConfig(bytes.NewReader(prefetchBuf))
+ if err == nil { // ignore the error for the formats that are not supported by image.DecodeConfig
+ ctx.Data["ImageSize"] = fmt.Sprintf("%dx%dpx", img.Width, img.Height)
+ }
}
+ return true
+}
- if !loadLatestCommitData(ctx, commit) {
+func prepareFileView(ctx *context.Context, entry *git.TreeEntry) {
+ ctx.Data["IsViewFile"] = true
+ ctx.Data["HideRepoInfo"] = true
+
+ if !prepareLatestCommitInfo(ctx) {
return
}
+ blob := entry.Blob()
+
+ ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefFullName.ShortName())
+ ctx.Data["FileIsSymlink"] = entry.IsLink()
+ ctx.Data["FileTreePath"] = ctx.Repo.TreePath
+ ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
+
if ctx.Repo.TreePath == ".editorconfig" {
_, editorconfigWarning, editorconfigErr := ctx.Repo.GetEditorconfig(ctx.Repo.Commit)
if editorconfigWarning != nil {
@@ -79,7 +199,7 @@ func prepareToRenderFile(ctx *context.Context, entry *git.TreeEntry) {
if workFlowErr != nil {
ctx.Data["FileError"] = ctx.Locale.Tr("actions.runs.invalid_workflow_helper", workFlowErr.Error())
}
- } else if slices.Contains([]string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"}, ctx.Repo.TreePath) {
+ } else if issue_service.IsCodeOwnerFile(ctx.Repo.TreePath) {
if data, err := blob.GetBlobContent(setting.UI.MaxDisplayFileSize); err == nil {
_, warnings := issue_model.GetCodeOwnersFromContent(ctx, data)
if len(warnings) > 0 {
@@ -88,226 +208,103 @@ func prepareToRenderFile(ctx *context.Context, entry *git.TreeEntry) {
}
}
- isDisplayingSource := ctx.FormString("display") == "source"
- isDisplayingRendered := !isDisplayingSource
+ // Don't call any other repository functions depends on git.Repository until the dataRc closed to
+ // avoid creating an unnecessary temporary cat file.
+ buf, dataRc, fInfo, err := getFileReader(ctx, ctx.Repo.Repository.ID, blob)
+ if err != nil {
+ ctx.ServerError("getFileReader", err)
+ return
+ }
+ defer dataRc.Close()
- if fInfo.isLFSFile {
- ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/media/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
+ if fInfo.isLFSFile() {
+ ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/media/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
}
- isRepresentableAsText := fInfo.st.IsRepresentableAsText()
- if !isRepresentableAsText {
- // If we can't show plain text, always try to render.
- isDisplayingSource = false
- isDisplayingRendered = true
+ if !prepareFileViewEditorButtons(ctx) {
+ return
}
- ctx.Data["IsLFSFile"] = fInfo.isLFSFile
+
+ ctx.Data["IsLFSFile"] = fInfo.isLFSFile()
ctx.Data["FileSize"] = fInfo.fileSize
- ctx.Data["IsTextFile"] = fInfo.isTextFile
- ctx.Data["IsRepresentableAsText"] = isRepresentableAsText
- ctx.Data["IsDisplayingSource"] = isDisplayingSource
- ctx.Data["IsDisplayingRendered"] = isDisplayingRendered
+ ctx.Data["IsRepresentableAsText"] = fInfo.st.IsRepresentableAsText()
ctx.Data["IsExecutable"] = entry.IsExecutable()
+ ctx.Data["CanCopyContent"] = fInfo.st.IsRepresentableAsText() || fInfo.st.IsImage()
- isTextSource := fInfo.isTextFile || isDisplayingSource
- ctx.Data["IsTextSource"] = isTextSource
- if isTextSource {
- ctx.Data["CanCopyContent"] = true
- }
-
- // Check LFS Lock
- lfsLock, err := git_model.GetTreePathLock(ctx, ctx.Repo.Repository.ID, ctx.Repo.TreePath)
- ctx.Data["LFSLock"] = lfsLock
- if err != nil {
- ctx.ServerError("GetTreePathLock", err)
+ attrs, ok := prepareFileViewLfsAttrs(ctx)
+ if !ok {
return
}
- if lfsLock != nil {
- u, err := user_model.GetUserByID(ctx, lfsLock.OwnerID)
- if err != nil {
- ctx.ServerError("GetTreePathLock", err)
- return
- }
- ctx.Data["LFSLockOwner"] = u.Name
- ctx.Data["LFSLockOwnerHomeLink"] = u.HomeLink()
- ctx.Data["LFSLockHint"] = ctx.Tr("repo.editor.this_file_locked")
- }
- // Assume file is not editable first.
- if fInfo.isLFSFile {
- ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_lfs_files")
- } else if !isRepresentableAsText {
- ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_non_text_files")
- }
+ // TODO: in the future maybe we need more accurate flags, for example:
+ // * IsRepresentableAsText: some files are text, some are not
+ // * IsRenderableXxx: some files are rendered by backend "markup" engine, some are rendered by frontend (pdf, 3d)
+ // * DefaultViewMode: when there is no "display" query parameter, which view mode should be used by default, source or rendered
+ utf8Reader := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{})
switch {
- case isRepresentableAsText:
- if fInfo.fileSize >= setting.UI.MaxDisplayFileSize {
- ctx.Data["IsFileTooLarge"] = true
- break
- }
-
- if fInfo.st.IsSvgImage() {
- ctx.Data["IsImageFile"] = true
- ctx.Data["CanCopyContent"] = true
- ctx.Data["HasSourceRenderedToggle"] = true
- }
-
- rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{})
-
- shouldRenderSource := ctx.FormString("display") == "source"
- readmeExist := util.IsReadmeFileName(blob.Name())
- ctx.Data["ReadmeExist"] = readmeExist
-
- markupType := markup.DetectMarkupTypeByFileName(blob.Name())
- if markupType == "" {
- markupType = markup.DetectRendererType(blob.Name(), bytes.NewReader(buf))
- }
- if markupType != "" {
- ctx.Data["HasSourceRenderedToggle"] = true
- }
- if markupType != "" && !shouldRenderSource {
- ctx.Data["IsMarkup"] = true
- ctx.Data["MarkupType"] = markupType
- metas := ctx.Repo.Repository.ComposeDocumentMetas(ctx)
- metas["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
- rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{
- CurrentRefPath: ctx.Repo.BranchNameSubURL(),
- CurrentTreePath: path.Dir(ctx.Repo.TreePath),
- }).
- WithMarkupType(markupType).
- WithRelativePath(ctx.Repo.TreePath).
- WithMetas(metas)
-
- ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, rctx, rd)
- if err != nil {
- ctx.ServerError("Render", err)
- return
- }
- // to prevent iframe load third-party url
- ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'")
- } else {
- buf, _ := io.ReadAll(rd)
-
- // The Open Group Base Specification: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html
- // empty: 0 lines; "a": 1 incomplete-line; "a\n": 1 line; "a\nb": 1 line, 1 incomplete-line;
- // Gitea uses the definition (like most modern editors):
- // empty: 0 lines; "a": 1 line; "a\n": 2 lines; "a\nb": 2 lines;
- // When rendering, the last empty line is not rendered in UI, while the line-number is still counted, to tell users that the file contains a trailing EOL.
- // To make the UI more consistent, it could use an icon mark to indicate that there is no trailing EOL, and show line-number as the rendered lines.
- // This NumLines is only used for the display on the UI: "xxx lines"
- if len(buf) == 0 {
- ctx.Data["NumLines"] = 0
- } else {
- ctx.Data["NumLines"] = bytes.Count(buf, []byte{'\n'}) + 1
- }
-
- language, err := files_service.TryGetContentLanguage(ctx.Repo.GitRepo, ctx.Repo.CommitID, ctx.Repo.TreePath)
- if err != nil {
- log.Error("Unable to get file language for %-v:%s. Error: %v", ctx.Repo.Repository, ctx.Repo.TreePath, err)
- }
-
- fileContent, lexerName, err := highlight.File(blob.Name(), language, buf)
- ctx.Data["LexerName"] = lexerName
- if err != nil {
- log.Error("highlight.File failed, fallback to plain text: %v", err)
- fileContent = highlight.PlainText(buf)
- }
- status := &charset.EscapeStatus{}
- statuses := make([]*charset.EscapeStatus, len(fileContent))
- for i, line := range fileContent {
- statuses[i], fileContent[i] = charset.EscapeControlHTML(line, ctx.Locale)
- status = status.Or(statuses[i])
- }
- ctx.Data["EscapeStatus"] = status
- ctx.Data["FileContent"] = fileContent
- ctx.Data["LineEscapeStatus"] = statuses
- }
- if !fInfo.isLFSFile {
- if ctx.Repo.CanEnableEditor(ctx, ctx.Doer) {
- if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID {
- ctx.Data["CanEditFile"] = false
- ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.this_file_locked")
- } else {
- ctx.Data["CanEditFile"] = true
- ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.edit_this_file")
- }
- } else if !ctx.Repo.IsViewBranch {
- ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch")
- } else if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) {
- ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.fork_before_edit")
- }
- }
-
- case fInfo.st.IsPDF():
- ctx.Data["IsPDFFile"] = true
+ case fInfo.fileSize >= setting.UI.MaxDisplayFileSize:
+ ctx.Data["IsFileTooLarge"] = true
+ case handleFileViewRenderMarkup(ctx, entry.Name(), fInfo.st, buf, utf8Reader):
+ // it also sets ctx.Data["FileContent"] and more
+ ctx.Data["IsMarkup"] = true
+ case handleFileViewRenderSource(ctx, entry.Name(), attrs, fInfo, utf8Reader):
+ // it also sets ctx.Data["FileContent"] and more
+ ctx.Data["IsDisplayingSource"] = true
+ case handleFileViewRenderImage(ctx, fInfo, buf):
+ ctx.Data["IsImageFile"] = true
case fInfo.st.IsVideo():
ctx.Data["IsVideoFile"] = true
case fInfo.st.IsAudio():
ctx.Data["IsAudioFile"] = true
- case fInfo.st.IsImage() && (setting.UI.SVG.Enabled || !fInfo.st.IsSvgImage()):
- ctx.Data["IsImageFile"] = true
- ctx.Data["CanCopyContent"] = true
default:
- if fInfo.fileSize >= setting.UI.MaxDisplayFileSize {
- ctx.Data["IsFileTooLarge"] = true
- break
- }
+ // unable to render anything, show the "view raw" or let frontend handle it
+ }
+}
- // TODO: this logic duplicates with "isRepresentableAsText=true", it is not the same as "LFSFileGet" in "lfs.go"
- // It is used by "external renders", markupRender will execute external programs to get rendered content.
- if markupType := markup.DetectMarkupTypeByFileName(blob.Name()); markupType != "" {
- rd := io.MultiReader(bytes.NewReader(buf), dataRc)
- ctx.Data["IsMarkup"] = true
- ctx.Data["MarkupType"] = markupType
-
- rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{
- CurrentRefPath: ctx.Repo.BranchNameSubURL(),
- CurrentTreePath: path.Dir(ctx.Repo.TreePath),
- }).
- WithMarkupType(markupType).
- WithRelativePath(ctx.Repo.TreePath)
-
- ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, rctx, rd)
- if err != nil {
- ctx.ServerError("Render", err)
- return
- }
- }
+func prepareFileViewEditorButtons(ctx *context.Context) bool {
+ // archived or mirror repository, the buttons should not be shown
+ if !ctx.Repo.Repository.CanEnableEditor() {
+ return true
}
- if ctx.Repo.GitRepo != nil {
- checker, deferable := ctx.Repo.GitRepo.CheckAttributeReader(ctx.Repo.CommitID)
- if checker != nil {
- defer deferable()
- attrs, err := checker.CheckPath(ctx.Repo.TreePath)
- if err == nil {
- ctx.Data["IsVendored"] = git.AttributeToBool(attrs, git.AttributeLinguistVendored).Value()
- ctx.Data["IsGenerated"] = git.AttributeToBool(attrs, git.AttributeLinguistGenerated).Value()
- }
- }
+ // The buttons should not be shown if it's not a branch
+ if !ctx.Repo.RefFullName.IsBranch() {
+ ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch")
+ ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch")
+ return true
}
- if fInfo.st.IsImage() && !fInfo.st.IsSvgImage() {
- img, _, err := image.DecodeConfig(bytes.NewReader(buf))
- if err == nil {
- // There are Image formats go can't decode
- // Instead of throwing an error in that case, we show the size only when we can decode
- ctx.Data["ImageSize"] = fmt.Sprintf("%dx%dpx", img.Width, img.Height)
- }
+ if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) {
+ ctx.Data["CanEditFile"] = true
+ ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.fork_before_edit")
+ ctx.Data["CanDeleteFile"] = true
+ ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_have_write_access")
+ return true
}
- if ctx.Repo.CanEnableEditor(ctx, ctx.Doer) {
- if lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID {
- ctx.Data["CanDeleteFile"] = false
- ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.this_file_locked")
- } else {
- ctx.Data["CanDeleteFile"] = true
- ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.delete_this_file")
+ lfsLock, err := git_model.GetTreePathLock(ctx, ctx.Repo.Repository.ID, ctx.Repo.TreePath)
+ ctx.Data["LFSLock"] = lfsLock
+ if err != nil {
+ ctx.ServerError("GetTreePathLock", err)
+ return false
+ }
+ if lfsLock != nil {
+ u, err := user_model.GetUserByID(ctx, lfsLock.OwnerID)
+ if err != nil {
+ ctx.ServerError("GetTreePathLock", err)
+ return false
}
- } else if !ctx.Repo.IsViewBranch {
- ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch")
- } else if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) {
- ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_have_write_access")
+ ctx.Data["LFSLockOwner"] = u.Name
+ ctx.Data["LFSLockOwnerHomeLink"] = u.HomeLink()
+ ctx.Data["LFSLockHint"] = ctx.Tr("repo.editor.this_file_locked")
}
+
+ // it's a lfs file and the user is not the owner of the lock
+ isLFSLocked := lfsLock != nil && lfsLock.OwnerID != ctx.Doer.ID
+ ctx.Data["CanEditFile"] = !isLFSLocked
+ ctx.Data["EditFileTooltip"] = util.Iif(isLFSLocked, ctx.Tr("repo.editor.this_file_locked"), ctx.Tr("repo.editor.edit_this_file"))
+ ctx.Data["CanDeleteFile"] = !isLFSLocked
+ ctx.Data["DeleteFileTooltip"] = util.Iif(isLFSLocked, ctx.Tr("repo.editor.this_file_locked"), ctx.Tr("repo.editor.delete_this_file"))
+ return true
}
diff --git a/routers/web/repo/view_home.go b/routers/web/repo/view_home.go
index 70ba07f9a8..c7396d44e3 100644
--- a/routers/web/repo/view_home.go
+++ b/routers/web/repo/view_home.go
@@ -9,6 +9,7 @@ import (
"html/template"
"net/http"
"path"
+ "strconv"
"strings"
"time"
@@ -17,7 +18,11 @@ import (
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
+ giturl "code.gitea.io/gitea/modules/git/url"
+ "code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
@@ -74,7 +79,7 @@ func prepareOpenWithEditorApps(ctx *context.Context) {
schema, _, _ := strings.Cut(app.OpenURL, ":")
var iconHTML template.HTML
if schema == "vscode" || schema == "vscodium" || schema == "jetbrains" {
- iconHTML = svg.RenderHTML(fmt.Sprintf("gitea-%s", schema), 16)
+ iconHTML = svg.RenderHTML("gitea-"+schema, 16)
} else {
iconHTML = svg.RenderHTML("gitea-git", 16) // TODO: it could support user's customized icon in the future
}
@@ -135,10 +140,10 @@ func prepareToRenderDirectory(ctx *context.Context) {
if ctx.Repo.TreePath != "" {
ctx.Data["HideRepoInfo"] = true
- ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
+ ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefFullName.ShortName())
}
- subfolder, readmeFile, err := findReadmeFileInEntries(ctx, entries, true)
+ subfolder, readmeFile, err := findReadmeFileInEntries(ctx, ctx.Repo.TreePath, entries, true)
if err != nil {
ctx.ServerError("findReadmeFileInEntries", err)
return
@@ -178,7 +183,7 @@ func prepareHomeSidebarLatestRelease(ctx *context.Context) {
}
func prepareUpstreamDivergingInfo(ctx *context.Context) {
- if !ctx.Repo.Repository.IsFork || !ctx.Repo.IsViewBranch || ctx.Repo.TreePath != "" {
+ if !ctx.Repo.Repository.IsFork || !ctx.Repo.RefFullName.IsBranch() || ctx.Repo.TreePath != "" {
return
}
upstreamDivergingInfo, err := repo_service.GetUpstreamDivergingInfo(ctx, ctx.Repo.Repository, ctx.Repo.BranchName)
@@ -215,24 +220,71 @@ func prepareRecentlyPushedNewBranches(ctx *context.Context) {
if !opts.Repo.IsMirror && !opts.BaseRepo.IsMirror &&
opts.BaseRepo.UnitEnabled(ctx, unit_model.TypePullRequests) &&
baseRepoPerm.CanRead(unit_model.TypePullRequests) {
- ctx.Data["RecentlyPushedNewBranches"], err = git_model.FindRecentlyPushedNewBranches(ctx, ctx.Doer, opts)
+ var finalBranches []*git_model.RecentlyPushedNewBranch
+ branches, err := git_model.FindRecentlyPushedNewBranches(ctx, ctx.Doer, opts)
if err != nil {
log.Error("FindRecentlyPushedNewBranches failed: %v", err)
}
+
+ for _, branch := range branches {
+ divergingInfo, err := repo_service.GetBranchDivergingInfo(ctx,
+ branch.BranchRepo, branch.BranchName, // "base" repo for diverging info
+ opts.BaseRepo, opts.BaseRepo.DefaultBranch, // "head" repo for diverging info
+ )
+ if err != nil {
+ log.Error("GetBranchDivergingInfo failed: %v", err)
+ continue
+ }
+ branchRepoHasNewCommits := divergingInfo.BaseHasNewCommits
+ baseRepoCommitsBehind := divergingInfo.HeadCommitsBehind
+ if branchRepoHasNewCommits || baseRepoCommitsBehind > 0 {
+ finalBranches = append(finalBranches, branch)
+ }
+ }
+ ctx.Data["RecentlyPushedNewBranches"] = finalBranches
}
}
}
+func updateContextRepoEmptyAndStatus(ctx *context.Context, empty bool, status repo_model.RepositoryStatus) {
+ if ctx.Repo.Repository.IsEmpty == empty && ctx.Repo.Repository.Status == status {
+ return
+ }
+ ctx.Repo.Repository.IsEmpty = empty
+ if ctx.Repo.Repository.Status == repo_model.RepositoryReady || ctx.Repo.Repository.Status == repo_model.RepositoryBroken {
+ ctx.Repo.Repository.Status = status // only handle ready and broken status, leave other status as-is
+ }
+ if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, ctx.Repo.Repository, "is_empty", "status"); err != nil {
+ ctx.ServerError("updateContextRepoEmptyAndStatus: UpdateRepositoryCols", err)
+ return
+ }
+}
+
func handleRepoEmptyOrBroken(ctx *context.Context) {
showEmpty := true
- var err error
+ if ctx.Repo.GitRepo == nil {
+ // in case the repo really exists and works, but the status was incorrectly marked as "broken", we need to open and check it again
+ ctx.Repo.GitRepo, _ = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
+ }
if ctx.Repo.GitRepo != nil {
- showEmpty, err = ctx.Repo.GitRepo.IsEmpty()
+ reallyEmpty, err := ctx.Repo.GitRepo.IsEmpty()
if err != nil {
+ showEmpty = true // the repo is broken
+ updateContextRepoEmptyAndStatus(ctx, true, repo_model.RepositoryBroken)
log.Error("GitRepo.IsEmpty: %v", err)
- ctx.Repo.Repository.Status = repo_model.RepositoryBroken
- showEmpty = true
ctx.Flash.Error(ctx.Tr("error.occurred"), true)
+ } else if reallyEmpty {
+ showEmpty = true // the repo is really empty
+ updateContextRepoEmptyAndStatus(ctx, true, repo_model.RepositoryReady)
+ } else if branches, _, _ := ctx.Repo.GitRepo.GetBranchNames(0, 1); len(branches) == 0 {
+ showEmpty = true // it is not really empty, but there is no branch
+ // at the moment, other repo units like "actions" are not able to handle such case,
+ // so we just mark the repo as empty to prevent from displaying these units.
+ ctx.Data["RepoHasContentsWithoutBranch"] = true
+ updateContextRepoEmptyAndStatus(ctx, true, repo_model.RepositoryReady)
+ } else {
+ // the repo is actually not empty and has branches, need to update the database later
+ showEmpty = false
}
}
if showEmpty {
@@ -240,18 +292,11 @@ func handleRepoEmptyOrBroken(ctx *context.Context) {
return
}
- // the repo is not really empty, so we should update the modal in database
- // such problem may be caused by:
- // 1) an error occurs during pushing/receiving. 2) the user replaces an empty git repo manually
- // and even more: the IsEmpty flag is deeply broken and should be removed with the UI changed to manage to cope with empty repos.
- // it's possible for a repository to be non-empty by that flag but still 500
- // because there are no branches - only tags -or the default branch is non-extant as it has been 0-pushed.
- ctx.Repo.Repository.IsEmpty = false
- if err = repo_model.UpdateRepositoryCols(ctx, ctx.Repo.Repository, "is_empty"); err != nil {
- ctx.ServerError("UpdateRepositoryCols", err)
- return
- }
- if err = repo_module.UpdateRepoSize(ctx, ctx.Repo.Repository); err != nil {
+ // The repo is not really empty, so we should update the model in database, such problem may be caused by:
+ // 1) an error occurs during pushing/receiving.
+ // 2) the user replaces an empty git repo manually.
+ updateContextRepoEmptyAndStatus(ctx, false, repo_model.RepositoryReady)
+ if err := repo_module.UpdateRepoSize(ctx, ctx.Repo.Repository); err != nil {
ctx.ServerError("UpdateRepoSize", err)
return
}
@@ -264,31 +309,94 @@ func handleRepoEmptyOrBroken(ctx *context.Context) {
ctx.Redirect(link)
}
+func handleRepoViewSubmodule(ctx *context.Context, submodule *git.SubModule) {
+ submoduleRepoURL, err := giturl.ParseRepositoryURL(ctx, submodule.URL)
+ if err != nil {
+ HandleGitError(ctx, "prepareToRenderDirOrFile: ParseRepositoryURL", err)
+ return
+ }
+ submoduleURL := giturl.MakeRepositoryWebLink(submoduleRepoURL)
+ if httplib.IsCurrentGiteaSiteURL(ctx, submoduleURL) {
+ ctx.RedirectToCurrentSite(submoduleURL)
+ } else {
+ // don't auto-redirect to external URL, to avoid open redirect or phishing
+ ctx.Data["NotFoundPrompt"] = submoduleURL
+ ctx.NotFound(nil)
+ }
+}
+
func prepareToRenderDirOrFile(entry *git.TreeEntry) func(ctx *context.Context) {
return func(ctx *context.Context) {
+ if entry.IsSubModule() {
+ submodule, err := ctx.Repo.Commit.GetSubModule(entry.Name())
+ if err != nil {
+ HandleGitError(ctx, "prepareToRenderDirOrFile: GetSubModule", err)
+ return
+ }
+ handleRepoViewSubmodule(ctx, submodule)
+ return
+ }
if entry.IsDir() {
prepareToRenderDirectory(ctx)
} else {
- prepareToRenderFile(ctx, entry)
+ prepareFileView(ctx, entry)
}
}
}
func handleRepoHomeFeed(ctx *context.Context) bool {
- if setting.Other.EnableFeed {
- isFeed, _, showFeedType := feed.GetFeedType(ctx.PathParam("reponame"), ctx.Req)
- if isFeed {
- switch {
- case ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType):
- feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
- case ctx.Repo.TreePath == "":
- feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
- case ctx.Repo.TreePath != "":
- feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
- }
- return true
+ if !setting.Other.EnableFeed {
+ return false
+ }
+ isFeed, showFeedType := feed.GetFeedType(ctx.PathParam("reponame"), ctx.Req)
+ if !isFeed {
+ return false
+ }
+ if ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType) {
+ feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
+ } else if ctx.Repo.TreePath == "" {
+ feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
+ } else {
+ feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
+ }
+ return true
+}
+
+func prepareHomeTreeSideBarSwitch(ctx *context.Context) {
+ showFileTree := true
+ if ctx.Doer != nil {
+ v, err := user_model.GetUserSetting(ctx, ctx.Doer.ID, user_model.SettingsKeyCodeViewShowFileTree, "true")
+ if err != nil {
+ log.Error("GetUserSetting: %v", err)
+ } else {
+ showFileTree, _ = strconv.ParseBool(v)
}
}
+ ctx.Data["UserSettingCodeViewShowFileTree"] = showFileTree
+}
+
+func redirectSrcToRaw(ctx *context.Context) bool {
+ // GitHub redirects a tree path with "?raw=1" to the raw path
+ // It is useful to embed some raw contents into Markdown files,
+ // then viewing the Markdown in "src" path could embed the raw content correctly.
+ if ctx.Repo.TreePath != "" && ctx.FormBool("raw") {
+ ctx.Redirect(ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath))
+ return true
+ }
+ return false
+}
+
+func redirectFollowSymlink(ctx *context.Context, treePathEntry *git.TreeEntry) bool {
+ if ctx.Repo.TreePath == "" || !ctx.FormBool("follow_symlink") {
+ return false
+ }
+ if treePathEntry.IsLink() {
+ if res, err := git.EntryFollowLinks(ctx.Repo.Commit, ctx.Repo.TreePath, treePathEntry); err == nil {
+ redirect := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(res.TargetFullPath) + "?" + ctx.Req.URL.RawQuery
+ ctx.Redirect(redirect)
+ return true
+ } // else: don't handle the links we cannot resolve, so ignore the error
+ }
return false
}
@@ -297,6 +405,9 @@ func Home(ctx *context.Context) {
if handleRepoHomeFeed(ctx) {
return
}
+ if redirectSrcToRaw(ctx) {
+ return
+ }
// Check whether the repo is viewable: not in migration, and the code unit should be enabled
// Ideally the "feed" logic should be after this, but old code did so, so keep it as-is.
@@ -306,7 +417,7 @@ func Home(ctx *context.Context) {
}
title := ctx.Repo.Repository.Owner.Name + "/" + ctx.Repo.Repository.Name
- if len(ctx.Repo.Repository.Description) > 0 {
+ if ctx.Repo.Repository.Description != "" {
title += ": " + ctx.Repo.Repository.Description
}
ctx.Data["Title"] = title
@@ -319,6 +430,8 @@ func Home(ctx *context.Context) {
return
}
+ prepareHomeTreeSideBarSwitch(ctx)
+
// get the current git entry which doer user is currently looking at.
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
if err != nil {
@@ -326,9 +439,13 @@ func Home(ctx *context.Context) {
return
}
+ if redirectFollowSymlink(ctx, entry) {
+ return
+ }
+
// prepare the tree path
var treeNames, paths []string
- branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
+ branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
treeLink := branchLink
if ctx.Repo.TreePath != "" {
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
@@ -374,5 +491,39 @@ func Home(ctx *context.Context) {
}
}
- ctx.HTML(http.StatusOK, tplRepoHome)
+ if ctx.FormBool("only_content") {
+ ctx.HTML(http.StatusOK, tplRepoViewContent)
+ } else if len(treeNames) != 0 {
+ ctx.HTML(http.StatusOK, tplRepoView)
+ } else {
+ ctx.HTML(http.StatusOK, tplRepoHome)
+ }
+}
+
+func RedirectRepoTreeToSrc(ctx *context.Context) {
+ // Redirect "/owner/repo/tree/*" requests to "/owner/repo/src/*",
+ // then use the deprecated "/src/*" handler to guess the ref type and render a file list page.
+ // This is done intentionally so that Gitea's repo URL structure matches other forges (GitHub/GitLab) provide,
+ // allowing us to construct submodule URLs across forges easily.
+ // For example, when viewing a submodule, we can simply construct the link as:
+ // * "https://gitea/owner/repo/tree/{CommitID}"
+ // * "https://github/owner/repo/tree/{CommitID}"
+ // * "https://gitlab/owner/repo/tree/{CommitID}"
+ // Then no matter which forge the submodule is using, the link works.
+ redirect := ctx.Repo.RepoLink + "/src/" + ctx.PathParamRaw("*")
+ if ctx.Req.URL.RawQuery != "" {
+ redirect += "?" + ctx.Req.URL.RawQuery
+ }
+ ctx.Redirect(redirect)
+}
+
+func RedirectRepoBlobToCommit(ctx *context.Context) {
+ // redirect "/owner/repo/blob/*" requests to "/owner/repo/src/commit/*"
+ // just like GitHub: browse files of a commit by "https://github/owner/repo/blob/{CommitID}"
+ // TODO: maybe we could guess more types to redirect to the related pages in the future
+ redirect := ctx.Repo.RepoLink + "/src/commit/" + ctx.PathParamRaw("*")
+ if ctx.Req.URL.RawQuery != "" {
+ redirect += "?" + ctx.Req.URL.RawQuery
+ }
+ ctx.Redirect(redirect)
}
diff --git a/routers/web/repo/view_home_test.go b/routers/web/repo/view_home_test.go
new file mode 100644
index 0000000000..6264dba71c
--- /dev/null
+++ b/routers/web/repo/view_home_test.go
@@ -0,0 +1,32 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "net/http"
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+ git_module "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/contexttest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestViewHomeSubmoduleRedirect(t *testing.T) {
+ unittest.PrepareTestEnv(t)
+
+ ctx, _ := contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule")
+ submodule := &git_module.SubModule{Path: "test-submodule", URL: setting.AppURL + "user2/repo-other.git"}
+ handleRepoViewSubmodule(ctx, submodule)
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
+ assert.Equal(t, "/user2/repo-other", ctx.Resp.Header().Get("Location"))
+
+ ctx, _ = contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule")
+ submodule = &git_module.SubModule{Path: "test-submodule", URL: "https://other/user2/repo-other.git"}
+ handleRepoViewSubmodule(ctx, submodule)
+ // do not auto-redirect for external URLs, to avoid open redirect or phishing
+ assert.Equal(t, http.StatusNotFound, ctx.Resp.WrittenStatus())
+}
diff --git a/routers/web/repo/view_readme.go b/routers/web/repo/view_readme.go
index 5bd39de963..ba03febff3 100644
--- a/routers/web/repo/view_readme.go
+++ b/routers/web/repo/view_readme.go
@@ -32,15 +32,7 @@ import (
// entries == ctx.Repo.Commit.SubTree(ctx.Repo.TreePath).ListEntries()
//
// FIXME: There has to be a more efficient way of doing this
-func findReadmeFileInEntries(ctx *context.Context, entries []*git.TreeEntry, tryWellKnownDirs bool) (string, *git.TreeEntry, error) {
- // Create a list of extensions in priority order
- // 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md
- // 2. Txt files - e.g. README.txt
- // 3. No extension - e.g. README
- exts := append(localizedExtensions(".md", ctx.Locale.Language()), ".txt", "") // sorted by priority
- extCount := len(exts)
- readmeFiles := make([]*git.TreeEntry, extCount+1)
-
+func findReadmeFileInEntries(ctx *context.Context, parentDir string, entries []*git.TreeEntry, tryWellKnownDirs bool) (string, *git.TreeEntry, error) {
docsEntries := make([]*git.TreeEntry, 3) // (one of docs/, .gitea/ or .github/)
for _, entry := range entries {
if tryWellKnownDirs && entry.IsDir() {
@@ -62,16 +54,23 @@ func findReadmeFileInEntries(ctx *context.Context, entries []*git.TreeEntry, try
docsEntries[2] = entry
}
}
- continue
}
+ }
+
+ // Create a list of extensions in priority order
+ // 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md
+ // 2. Txt files - e.g. README.txt
+ // 3. No extension - e.g. README
+ exts := append(localizedExtensions(".md", ctx.Locale.Language()), ".txt", "") // sorted by priority
+ extCount := len(exts)
+ readmeFiles := make([]*git.TreeEntry, extCount+1)
+ for _, entry := range entries {
if i, ok := util.IsReadmeFileExtension(entry.Name(), exts...); ok {
- log.Debug("Potential readme file: %s", entry.Name())
+ fullPath := path.Join(parentDir, entry.Name())
if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].Name(), entry.Blob().Name()) {
if entry.IsLink() {
- target, err := entry.FollowLinks()
- if err != nil && !git.IsErrBadLink(err) {
- return "", nil, err
- } else if target != nil && (target.IsExecutable() || target.IsRegular()) {
+ res, err := git.EntryFollowLinks(ctx.Repo.Commit, fullPath, entry)
+ if err == nil && (res.TargetEntry.IsExecutable() || res.TargetEntry.IsRegular()) {
readmeFiles[i] = entry
}
} else {
@@ -80,6 +79,7 @@ func findReadmeFileInEntries(ctx *context.Context, entries []*git.TreeEntry, try
}
}
}
+
var readmeFile *git.TreeEntry
for _, f := range readmeFiles {
if f != nil {
@@ -103,7 +103,7 @@ func findReadmeFileInEntries(ctx *context.Context, entries []*git.TreeEntry, try
return "", nil, err
}
- subfolder, readmeFile, err := findReadmeFileInEntries(ctx, childEntries, false)
+ subfolder, readmeFile, err := findReadmeFileInEntries(ctx, parentDir, childEntries, false)
if err != nil && !git.IsErrNotExist(err) {
return "", nil, err
}
@@ -139,46 +139,52 @@ func localizedExtensions(ext, languageCode string) (localizedExts []string) {
}
func prepareToRenderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.TreeEntry) {
- target := readmeFile
- if readmeFile != nil && readmeFile.IsLink() {
- target, _ = readmeFile.FollowLinks()
- }
- if target == nil {
- // if findReadmeFile() failed and/or gave us a broken symlink (which it shouldn't)
- // simply skip rendering the README
+ if readmeFile == nil {
return
}
+ readmeFullPath := path.Join(ctx.Repo.TreePath, subfolder, readmeFile.Name())
+ readmeTargetEntry := readmeFile
+ if readmeFile.IsLink() {
+ if res, err := git.EntryFollowLinks(ctx.Repo.Commit, readmeFullPath, readmeFile); err == nil {
+ readmeTargetEntry = res.TargetEntry
+ } else {
+ readmeTargetEntry = nil // if we cannot resolve the symlink, we cannot render the readme, ignore the error
+ }
+ }
+ if readmeTargetEntry == nil {
+ return // if no valid README entry found, skip rendering the README
+ }
+
ctx.Data["RawFileLink"] = ""
- ctx.Data["ReadmeInList"] = true
+ ctx.Data["ReadmeInList"] = path.Join(subfolder, readmeFile.Name()) // the relative path to the readme file to the current tree path
ctx.Data["ReadmeExist"] = true
ctx.Data["FileIsSymlink"] = readmeFile.IsLink()
- buf, dataRc, fInfo, err := getFileReader(ctx, ctx.Repo.Repository.ID, target.Blob())
+ buf, dataRc, fInfo, err := getFileReader(ctx, ctx.Repo.Repository.ID, readmeTargetEntry.Blob())
if err != nil {
ctx.ServerError("getFileReader", err)
return
}
defer dataRc.Close()
- ctx.Data["FileIsText"] = fInfo.isTextFile
- ctx.Data["FileName"] = path.Join(subfolder, readmeFile.Name())
+ ctx.Data["FileIsText"] = fInfo.st.IsText()
+ ctx.Data["FileTreePath"] = readmeFullPath
ctx.Data["FileSize"] = fInfo.fileSize
- ctx.Data["IsLFSFile"] = fInfo.isLFSFile
+ ctx.Data["IsLFSFile"] = fInfo.isLFSFile()
- if fInfo.isLFSFile {
+ if fInfo.isLFSFile() {
filenameBase64 := base64.RawURLEncoding.EncodeToString([]byte(readmeFile.Name()))
ctx.Data["RawFileLink"] = fmt.Sprintf("%s.git/info/lfs/objects/%s/%s", ctx.Repo.Repository.Link(), url.PathEscape(fInfo.lfsMeta.Oid), url.PathEscape(filenameBase64))
}
- if !fInfo.isTextFile {
+ if !fInfo.st.IsText() {
return
}
if fInfo.fileSize >= setting.UI.MaxDisplayFileSize {
// Pretend that this is a normal text file to display 'This file is too large to be shown'
ctx.Data["IsFileTooLarge"] = true
- ctx.Data["IsTextFile"] = true
return
}
@@ -189,11 +195,11 @@ func prepareToRenderReadmeFile(ctx *context.Context, subfolder string, readmeFil
ctx.Data["MarkupType"] = markupType
rctx := renderhelper.NewRenderContextRepoFile(ctx, ctx.Repo.Repository, renderhelper.RepoFileOptions{
- CurrentRefPath: ctx.Repo.BranchNameSubURL(),
- CurrentTreePath: path.Join(ctx.Repo.TreePath, subfolder),
+ CurrentRefPath: ctx.Repo.RefTypeNameSubURL(),
+ CurrentTreePath: path.Dir(readmeFullPath),
}).
WithMarkupType(markupType).
- WithRelativePath(path.Join(ctx.Repo.TreePath, subfolder, readmeFile.Name())) // ctx.Repo.TreePath is the directory not the Readme so we must append the Readme filename (and path).
+ WithRelativePath(readmeFullPath)
ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, rctx, rd)
if err != nil {
@@ -212,7 +218,7 @@ func prepareToRenderReadmeFile(ctx *context.Context, subfolder string, readmeFil
ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlHTML(template.HTML(contentEscaped), ctx.Locale)
}
- if !fInfo.isLFSFile && ctx.Repo.CanEnableEditor(ctx, ctx.Doer) {
+ if !fInfo.isLFSFile() && ctx.Repo.Repository.CanEnableEditor() {
ctx.Data["CanEditReadmeFile"] = true
}
}
diff --git a/routers/web/repo/watch.go b/routers/web/repo/watch.go
new file mode 100644
index 0000000000..70c548b8ce
--- /dev/null
+++ b/routers/web/repo/watch.go
@@ -0,0 +1,31 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "net/http"
+
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/services/context"
+)
+
+const tplWatchUnwatch templates.TplName = "repo/watch_unwatch"
+
+func ActionWatch(ctx *context.Context) {
+ err := repo_model.WatchRepo(ctx, ctx.Doer, ctx.Repo.Repository, ctx.PathParam("action") == "watch")
+ if err != nil {
+ handleActionError(ctx, err)
+ return
+ }
+
+ ctx.Data["IsWatchingRepo"] = repo_model.IsWatching(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
+ ctx.Data["Repository"], err = repo_model.GetRepositoryByName(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.Name)
+ if err != nil {
+ ctx.ServerError("GetRepositoryByName", err)
+ return
+ }
+ ctx.RespHeader().Add("hx-trigger", "refreshUserCards") // see the `hx-trigger="refreshUserCards ..."` comments in tmpl
+ ctx.HTML(http.StatusOK, tplWatchUnwatch)
+}
diff --git a/routers/web/repo/webgit.go b/routers/web/repo/webgit.go
new file mode 100644
index 0000000000..5f390197e7
--- /dev/null
+++ b/routers/web/repo/webgit.go
@@ -0,0 +1,40 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
+ files_service "code.gitea.io/gitea/services/repository/files"
+)
+
+func WebGitOperationCommonData(ctx *context.Context) {
+ // TODO: more places like "wiki page" and "merging a pull request or creating an auto merge merging task"
+ emails, err := user_model.GetActivatedEmailAddresses(ctx, ctx.Doer.ID)
+ if err != nil {
+ log.Error("WebGitOperationCommonData: GetActivatedEmailAddresses: %v", err)
+ }
+ if ctx.Doer.KeepEmailPrivate {
+ emails = append([]string{ctx.Doer.GetPlaceholderEmail()}, emails...)
+ }
+ ctx.Data["CommitCandidateEmails"] = emails
+ ctx.Data["CommitDefaultEmail"] = ctx.Doer.GetEmail()
+}
+
+func WebGitOperationGetCommitChosenEmailIdentity(ctx *context.Context, email string) (_ *files_service.IdentityOptions, valid bool) {
+ if ctx.Data["CommitCandidateEmails"] == nil {
+ setting.PanicInDevOrTesting("no CommitCandidateEmails in context data")
+ }
+ emails, _ := ctx.Data["CommitCandidateEmails"].([]string)
+ if email == "" {
+ return nil, true
+ }
+ if util.SliceContainsString(emails, email, true) {
+ return &files_service.IdentityOptions{GitUserEmail: email}, true
+ }
+ return nil, false
+}
diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go
index 19366c0104..69858c9692 100644
--- a/routers/web/repo/wiki.go
+++ b/routers/web/repo/wiki.go
@@ -7,14 +7,13 @@ package repo
import (
"bytes"
gocontext "context"
- "fmt"
+ "html/template"
"io"
"net/http"
"net/url"
"path/filepath"
"strings"
- git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
@@ -33,6 +32,7 @@ import (
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
+ git_service "code.gitea.io/gitea/services/git"
notify_service "code.gitea.io/gitea/services/notify"
wiki_service "code.gitea.io/gitea/services/wiki"
)
@@ -58,13 +58,13 @@ func MustEnableWiki(ctx *context.Context) {
ctx.Repo.Repository,
ctx.Repo.Permission)
}
- ctx.NotFound("MustEnableWiki", nil)
+ ctx.NotFound(nil)
return
}
- unit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalWiki)
+ repoUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalWiki)
if err == nil {
- ctx.Redirect(unit.ExternalWikiConfig().ExternalWikiURL)
+ ctx.Redirect(repoUnit.ExternalWikiConfig().ExternalWikiURL)
return
}
}
@@ -96,7 +96,7 @@ func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error)
}
func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) {
- wikiGitRepo, errGitRepo := gitrepo.OpenWikiRepository(ctx, ctx.Repo.Repository)
+ wikiGitRepo, errGitRepo := gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository.WikiStorageRepo())
if errGitRepo != nil {
ctx.ServerError("OpenRepository", errGitRepo)
return nil, nil, errGitRepo
@@ -105,12 +105,12 @@ func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, err
commit, errCommit := wikiGitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultWikiBranch)
if git.IsErrNotExist(errCommit) {
// if the default branch recorded in database is out of sync, then re-sync it
- gitRepoDefaultBranch, errBranch := gitrepo.GetWikiDefaultBranch(ctx, ctx.Repo.Repository)
+ gitRepoDefaultBranch, errBranch := gitrepo.GetDefaultBranch(ctx, ctx.Repo.Repository.WikiStorageRepo())
if errBranch != nil {
return wikiGitRepo, nil, errBranch
}
// update the default branch in the database
- errDb := repo_model.UpdateRepositoryCols(ctx, &repo_model.Repository{ID: ctx.Repo.Repository.ID, DefaultWikiBranch: gitRepoDefaultBranch}, "default_wiki_branch")
+ errDb := repo_model.UpdateRepositoryColsNoAutoTime(ctx, &repo_model.Repository{ID: ctx.Repo.Repository.ID, DefaultWikiBranch: gitRepoDefaultBranch}, "default_wiki_branch")
if errDb != nil {
return wikiGitRepo, nil, errDb
}
@@ -179,23 +179,17 @@ func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName wiki_
}
func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
- wikiRepo, commit, err := findWikiRepoCommit(ctx)
+ wikiGitRepo, commit, err := findWikiRepoCommit(ctx)
if err != nil {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
if !git.IsErrNotExist(err) {
ctx.ServerError("GetBranchCommit", err)
}
return nil, nil
}
- // Get page list.
+ // get the wiki pages list.
entries, err := commit.ListEntries()
if err != nil {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
ctx.ServerError("ListEntries", err)
return nil, nil
}
@@ -209,9 +203,6 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
if repo_model.IsErrWikiInvalidFileName(err) {
continue
}
- if wikiRepo != nil {
- wikiRepo.Close()
- }
ctx.ServerError("WikiFilenameToName", err)
return nil, nil
} else if wikiName == "_Sidebar" || wikiName == "_Footer" {
@@ -250,58 +241,26 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
ctx.Redirect(util.URLJoin(ctx.Repo.RepoLink, "wiki/raw", string(pageName)))
}
if entry == nil || ctx.Written() {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
return nil, nil
}
- // get filecontent
+ // get page content
data := wikiContentsByEntry(ctx, entry)
if ctx.Written() {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
return nil, nil
}
- var sidebarContent []byte
- if !isSideBar {
- sidebarContent, _, _, _ = wikiContentsByName(ctx, commit, "_Sidebar")
- if ctx.Written() {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
- return nil, nil
- }
- } else {
- sidebarContent = data
- }
-
- var footerContent []byte
- if !isFooter {
- footerContent, _, _, _ = wikiContentsByName(ctx, commit, "_Footer")
- if ctx.Written() {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
- return nil, nil
- }
- } else {
- footerContent = data
- }
-
rctx := renderhelper.NewRenderContextRepoWiki(ctx, ctx.Repo.Repository)
- buf := &strings.Builder{}
- renderFn := func(data []byte) (escaped *charset.EscapeStatus, output string, err error) {
+ renderFn := func(data []byte) (escaped *charset.EscapeStatus, output template.HTML, err error) {
+ buf := &strings.Builder{}
markupRd, markupWr := io.Pipe()
defer markupWr.Close()
done := make(chan struct{})
go func() {
// We allow NBSP here this is rendered
escaped, _ = charset.EscapeControlReader(markupRd, buf, ctx.Locale, charset.RuneNBSP)
- output = buf.String()
+ output = template.HTML(buf.String())
buf.Reset()
close(done)
}()
@@ -312,75 +271,61 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
return escaped, output, err
}
- ctx.Data["EscapeStatus"], ctx.Data["content"], err = renderFn(data)
+ ctx.Data["EscapeStatus"], ctx.Data["WikiContentHTML"], err = renderFn(data)
if err != nil {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
ctx.ServerError("Render", err)
return nil, nil
}
if rctx.SidebarTocNode != nil {
- sb := &strings.Builder{}
- err = markdown.SpecializedMarkdown(rctx).Renderer().Render(sb, nil, rctx.SidebarTocNode)
- if err != nil {
+ sb := strings.Builder{}
+ if err = markdown.SpecializedMarkdown(rctx).Renderer().Render(&sb, nil, rctx.SidebarTocNode); err != nil {
log.Error("Failed to render wiki sidebar TOC: %v", err)
- } else {
- ctx.Data["sidebarTocContent"] = sb.String()
}
+ ctx.Data["WikiSidebarTocHTML"] = templates.SanitizeHTML(sb.String())
}
if !isSideBar {
- buf.Reset()
- ctx.Data["sidebarEscapeStatus"], ctx.Data["sidebarContent"], err = renderFn(sidebarContent)
+ sidebarContent, _, _, _ := wikiContentsByName(ctx, commit, "_Sidebar")
+ if ctx.Written() {
+ return nil, nil
+ }
+ ctx.Data["WikiSidebarEscapeStatus"], ctx.Data["WikiSidebarHTML"], err = renderFn(sidebarContent)
if err != nil {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
ctx.ServerError("Render", err)
return nil, nil
}
- ctx.Data["sidebarPresent"] = sidebarContent != nil
- } else {
- ctx.Data["sidebarPresent"] = false
}
if !isFooter {
- buf.Reset()
- ctx.Data["footerEscapeStatus"], ctx.Data["footerContent"], err = renderFn(footerContent)
+ footerContent, _, _, _ := wikiContentsByName(ctx, commit, "_Footer")
+ if ctx.Written() {
+ return nil, nil
+ }
+ ctx.Data["WikiFooterEscapeStatus"], ctx.Data["WikiFooterHTML"], err = renderFn(footerContent)
if err != nil {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
ctx.ServerError("Render", err)
return nil, nil
}
- ctx.Data["footerPresent"] = footerContent != nil
- } else {
- ctx.Data["footerPresent"] = false
}
// get commit count - wiki revisions
- commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
+ commitsCount, _ := wikiGitRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
ctx.Data["CommitCount"] = commitsCount
- return wikiRepo, entry
+ return wikiGitRepo, entry
}
func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
- wikiRepo, commit, err := findWikiRepoCommit(ctx)
+ wikiGitRepo, commit, err := findWikiRepoCommit(ctx)
if err != nil {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
if !git.IsErrNotExist(err) {
ctx.ServerError("GetBranchCommit", err)
}
return nil, nil
}
- // get requested pagename
+ // get requested page name
pageName := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*"))
if len(pageName) == 0 {
pageName = "Home"
@@ -395,64 +340,48 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
- // lookup filename in wiki - get filecontent, gitTree entry , real filename
- data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
+ // lookup filename in wiki - get page content, gitTree entry , real filename
+ _, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
if noEntry {
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
}
if entry == nil || ctx.Written() {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
return nil, nil
}
- ctx.Data["content"] = string(data)
- ctx.Data["sidebarPresent"] = false
- ctx.Data["sidebarContent"] = ""
- ctx.Data["footerPresent"] = false
- ctx.Data["footerContent"] = ""
-
// get commit count - wiki revisions
- commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
+ commitsCount, _ := wikiGitRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
ctx.Data["CommitCount"] = commitsCount
// get page
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
// get Commit Count
- commitsHistory, err := wikiRepo.CommitsByFileAndRange(
+ commitsHistory, err := wikiGitRepo.CommitsByFileAndRange(
git.CommitsByFileAndRangeOptions{
Revision: ctx.Repo.Repository.DefaultWikiBranch,
File: pageFilename,
Page: page,
})
if err != nil {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
ctx.ServerError("CommitsByFileAndRange", err)
return nil, nil
}
- ctx.Data["Commits"] = git_model.ConvertFromGitCommit(ctx, commitsHistory, ctx.Repo.Repository)
+ ctx.Data["Commits"], err = git_service.ConvertFromGitCommit(ctx, commitsHistory, ctx.Repo.Repository)
+ if err != nil {
+ ctx.ServerError("ConvertFromGitCommit", err)
+ return nil, nil
+ }
pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5)
pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager
- return wikiRepo, entry
+ return wikiGitRepo, entry
}
func renderEditPage(ctx *context.Context) {
- wikiRepo, commit, err := findWikiRepoCommit(ctx)
- defer func() {
- if wikiRepo != nil {
- _ = wikiRepo.Close()
- }
- }()
+ _, commit, err := findWikiRepoCommit(ctx)
if err != nil {
if !git.IsErrNotExist(err) {
ctx.ServerError("GetBranchCommit", err)
@@ -460,7 +389,7 @@ func renderEditPage(ctx *context.Context) {
return
}
- // get requested pagename
+ // get requested page name
pageName := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*"))
if len(pageName) == 0 {
pageName = "Home"
@@ -478,23 +407,19 @@ func renderEditPage(ctx *context.Context) {
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
}
if isRaw {
- ctx.Error(http.StatusForbidden, "Editing of raw wiki files is not allowed")
+ ctx.HTTPError(http.StatusForbidden, "Editing of raw wiki files is not allowed")
}
if entry == nil || ctx.Written() {
return
}
- // get filecontent
+ // get wiki page content
data := wikiContentsByEntry(ctx, entry)
if ctx.Written() {
return
}
- ctx.Data["content"] = string(data)
- ctx.Data["sidebarPresent"] = false
- ctx.Data["sidebarContent"] = ""
- ctx.Data["footerPresent"] = false
- ctx.Data["footerContent"] = ""
+ ctx.Data["WikiEditContent"] = string(data)
}
// WikiPost renders post of wiki page
@@ -502,14 +427,14 @@ func WikiPost(ctx *context.Context) {
switch ctx.FormString("action") {
case "_new":
if !ctx.Repo.CanWrite(unit.TypeWiki) {
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ ctx.NotFound(nil)
return
}
NewWikiPost(ctx)
return
case "_delete":
if !ctx.Repo.CanWrite(unit.TypeWiki) {
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ ctx.NotFound(nil)
return
}
DeleteWikiPagePost(ctx)
@@ -517,7 +442,7 @@ func WikiPost(ctx *context.Context) {
}
if !ctx.Repo.CanWrite(unit.TypeWiki) {
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ ctx.NotFound(nil)
return
}
EditWikiPost(ctx)
@@ -536,14 +461,14 @@ func Wiki(ctx *context.Context) {
return
case "_edit":
if !ctx.Repo.CanWrite(unit.TypeWiki) {
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ ctx.NotFound(nil)
return
}
EditWiki(ctx)
return
case "_new":
if !ctx.Repo.CanWrite(unit.TypeWiki) {
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ ctx.NotFound(nil)
return
}
NewWiki(ctx)
@@ -556,12 +481,7 @@ func Wiki(ctx *context.Context) {
return
}
- wikiRepo, entry := renderViewPage(ctx)
- defer func() {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
- }()
+ wikiGitRepo, entry := renderViewPage(ctx)
if ctx.Written() {
return
}
@@ -574,10 +494,10 @@ func Wiki(ctx *context.Context) {
wikiPath := entry.Name()
if markup.DetectMarkupTypeByFileName(wikiPath) != markdown.MarkupName {
ext := strings.ToUpper(filepath.Ext(wikiPath))
- ctx.Data["FormatWarning"] = fmt.Sprintf("%s rendering is not supported at the moment. Rendered as Markdown.", ext)
+ ctx.Data["FormatWarning"] = ext + " rendering is not supported at the moment. Rendered as Markdown."
}
// Get last change information.
- lastCommit, err := wikiRepo.GetCommitByPath(wikiPath)
+ lastCommit, err := wikiGitRepo.GetCommitByPath(wikiPath)
if err != nil {
ctx.ServerError("GetCommitByPath", err)
return
@@ -597,13 +517,7 @@ func WikiRevision(ctx *context.Context) {
return
}
- wikiRepo, entry := renderRevisionPage(ctx)
- defer func() {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
- }()
-
+ wikiGitRepo, entry := renderRevisionPage(ctx)
if ctx.Written() {
return
}
@@ -615,7 +529,7 @@ func WikiRevision(ctx *context.Context) {
// Get last change information.
wikiPath := entry.Name()
- lastCommit, err := wikiRepo.GetCommitByPath(wikiPath)
+ lastCommit, err := wikiGitRepo.GetCommitByPath(wikiPath)
if err != nil {
ctx.ServerError("GetCommitByPath", err)
return
@@ -635,12 +549,7 @@ func WikiPages(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.wiki.pages")
ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived
- wikiRepo, commit, err := findWikiRepoCommit(ctx)
- defer func() {
- if wikiRepo != nil {
- _ = wikiRepo.Close()
- }
- }()
+ _, commit, err := findWikiRepoCommit(ctx)
if err != nil {
ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
return
@@ -694,16 +603,10 @@ func WikiPages(ctx *context.Context) {
// WikiRaw outputs raw blob requested by user (image for example)
func WikiRaw(ctx *context.Context) {
- wikiRepo, commit, err := findWikiRepoCommit(ctx)
- defer func() {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
- }()
-
+ _, commit, err := findWikiRepoCommit(ctx)
if err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound("findEntryForFile", nil)
+ ctx.NotFound(nil)
return
}
ctx.ServerError("findEntryForfile", err)
@@ -733,13 +636,13 @@ func WikiRaw(ctx *context.Context) {
}
if entry != nil {
- if err = common.ServeBlob(ctx.Base, ctx.Repo.TreePath, entry.Blob(), nil); err != nil {
+ if err = common.ServeBlob(ctx.Base, ctx.Repo.Repository, ctx.Repo.TreePath, entry.Blob(), nil); err != nil {
ctx.ServerError("ServeBlob", err)
}
return
}
- ctx.NotFound("findEntryForFile", nil)
+ ctx.NotFound(nil)
}
// NewWiki render wiki create page
diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go
index 958ff802d4..59bf6ed79b 100644
--- a/routers/web/repo/wiki_test.go
+++ b/routers/web/repo/wiki_test.go
@@ -20,6 +20,7 @@ import (
wiki_service "code.gitea.io/gitea/services/wiki"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
const (
@@ -28,7 +29,7 @@ const (
)
func wikiEntry(t *testing.T, repo *repo_model.Repository, wikiName wiki_service.WebPath) *git.TreeEntry {
- wikiRepo, err := gitrepo.OpenWikiRepository(git.DefaultContext, repo)
+ wikiRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo.WikiStorageRepo())
assert.NoError(t, err)
defer wikiRepo.Close()
commit, err := wikiRepo.GetBranchCommit("master")
@@ -66,14 +67,11 @@ func assertWikiNotExists(t *testing.T, repo *repo_model.Repository, wikiName wik
func assertPagesMetas(t *testing.T, expectedNames []string, metas any) {
pageMetas, ok := metas.([]PageMeta)
- if !assert.True(t, ok) {
- return
- }
- if !assert.Len(t, pageMetas, len(expectedNames)) {
- return
- }
+ require.True(t, ok)
+ require.Len(t, pageMetas, len(expectedNames))
+
for i, pageMeta := range pageMetas {
- assert.EqualValues(t, expectedNames[i], pageMeta.Name)
+ assert.Equal(t, expectedNames[i], pageMeta.Name)
}
}
@@ -84,7 +82,7 @@ func TestWiki(t *testing.T) {
ctx.SetPathParam("*", "Home")
contexttest.LoadRepo(t, ctx, 1)
Wiki(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
assert.EqualValues(t, "Home", ctx.Data["Title"])
assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name", "Unescaped File"}, ctx.Data["Pages"])
@@ -92,7 +90,7 @@ func TestWiki(t *testing.T) {
ctx.SetPathParam("*", "jpeg.jpg")
contexttest.LoadRepo(t, ctx, 1)
Wiki(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assert.Equal(t, "/user2/repo1/wiki/raw/jpeg.jpg", ctx.Resp.Header().Get("Location"))
}
@@ -102,7 +100,7 @@ func TestWikiPages(t *testing.T) {
ctx, _ := contexttest.MockContext(t, "user2/repo1/wiki/?action=_pages")
contexttest.LoadRepo(t, ctx, 1)
WikiPages(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name", "Unescaped File"}, ctx.Data["Pages"])
}
@@ -113,7 +111,7 @@ func TestNewWiki(t *testing.T) {
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
NewWiki(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
assert.EqualValues(t, ctx.Tr("repo.wiki.new_page"), ctx.Data["Title"])
}
@@ -133,7 +131,7 @@ func TestNewWikiPost(t *testing.T) {
Message: message,
})
NewWikiPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assertWikiExists(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title))
assert.Equal(t, content, wikiContent(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title)))
}
@@ -151,7 +149,7 @@ func TestNewWikiPost_ReservedName(t *testing.T) {
Message: message,
})
NewWikiPost(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
assert.EqualValues(t, ctx.Tr("repo.wiki.reserved_page", "_edit"), ctx.Flash.ErrorMsg)
assertWikiNotExists(t, ctx.Repo.Repository, "_edit")
}
@@ -164,16 +162,16 @@ func TestEditWiki(t *testing.T) {
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
EditWiki(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
assert.EqualValues(t, "Home", ctx.Data["Title"])
- assert.Equal(t, wikiContent(t, ctx.Repo.Repository, "Home"), ctx.Data["content"])
+ assert.Equal(t, wikiContent(t, ctx.Repo.Repository, "Home"), ctx.Data["WikiEditContent"])
ctx, _ = contexttest.MockContext(t, "user2/repo1/wiki/jpeg.jpg?action=_edit")
ctx.SetPathParam("*", "jpeg.jpg")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
EditWiki(ctx)
- assert.EqualValues(t, http.StatusForbidden, ctx.Resp.Status())
+ assert.Equal(t, http.StatusForbidden, ctx.Resp.WrittenStatus())
}
func TestEditWikiPost(t *testing.T) {
@@ -192,7 +190,7 @@ func TestEditWikiPost(t *testing.T) {
Message: message,
})
EditWikiPost(ctx)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assertWikiExists(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title))
assert.Equal(t, content, wikiContent(t, ctx.Repo.Repository, wiki_service.UserTitleToWebPath("", title)))
if title != "Home" {
@@ -208,7 +206,7 @@ func TestDeleteWikiPagePost(t *testing.T) {
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
DeleteWikiPagePost(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
assertWikiNotExists(t, ctx.Repo.Repository, "Home")
}
@@ -230,10 +228,10 @@ func TestWikiRaw(t *testing.T) {
contexttest.LoadRepo(t, ctx, 1)
WikiRaw(ctx)
if filetype == "" {
- assert.EqualValues(t, http.StatusNotFound, ctx.Resp.Status(), "filepath: %s", filepath)
+ assert.Equal(t, http.StatusNotFound, ctx.Resp.WrittenStatus(), "filepath: %s", filepath)
} else {
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status(), "filepath: %s", filepath)
- assert.EqualValues(t, filetype, ctx.Resp.Header().Get("Content-Type"), "filepath: %s", filepath)
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus(), "filepath: %s", filepath)
+ assert.Equal(t, filetype, ctx.Resp.Header().Get("Content-Type"), "filepath: %s", filepath)
}
}
}
@@ -247,7 +245,12 @@ func TestDefaultWikiBranch(t *testing.T) {
assert.NoError(t, wiki_service.ChangeDefaultWikiBranch(db.DefaultContext, repoWithNoWiki, "main"))
// repo with wiki
- assert.NoError(t, repo_model.UpdateRepositoryCols(db.DefaultContext, &repo_model.Repository{ID: 1, DefaultWikiBranch: "wrong-branch"}))
+ assert.NoError(t, repo_model.UpdateRepositoryColsNoAutoTime(
+ db.DefaultContext,
+ &repo_model.Repository{ID: 1, DefaultWikiBranch: "wrong-branch"},
+ "default_wiki_branch",
+ ),
+ )
ctx, _ := contexttest.MockContext(t, "user2/repo1/wiki")
ctx.SetPathParam("*", "Home")
diff --git a/routers/web/shared/actions/runners.go b/routers/web/shared/actions/runners.go
index 6d77bdd2fa..648f8046a4 100644
--- a/routers/web/shared/actions/runners.go
+++ b/routers/web/shared/actions/runners.go
@@ -5,18 +5,127 @@ package actions
import (
"errors"
+ "net/http"
+ "net/url"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ shared_user "code.gitea.io/gitea/routers/web/shared/user"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
-// RunnersList prepares data for runners list
-func RunnersList(ctx *context.Context, opts actions_model.FindRunnerOptions) {
+const (
+ // TODO: Separate secrets from runners when layout is ready
+ tplRepoRunners templates.TplName = "repo/settings/actions"
+ tplOrgRunners templates.TplName = "org/settings/actions"
+ tplAdminRunners templates.TplName = "admin/actions"
+ tplUserRunners templates.TplName = "user/settings/actions"
+ tplRepoRunnerEdit templates.TplName = "repo/settings/runner_edit"
+ tplOrgRunnerEdit templates.TplName = "org/settings/runners_edit"
+ tplAdminRunnerEdit templates.TplName = "admin/runners/edit"
+ tplUserRunnerEdit templates.TplName = "user/settings/runner_edit"
+)
+
+type runnersCtx struct {
+ OwnerID int64
+ RepoID int64
+ IsRepo bool
+ IsOrg bool
+ IsAdmin bool
+ IsUser bool
+ RunnersTemplate templates.TplName
+ RunnerEditTemplate templates.TplName
+ RedirectLink string
+}
+
+func getRunnersCtx(ctx *context.Context) (*runnersCtx, error) {
+ if ctx.Data["PageIsRepoSettings"] == true {
+ return &runnersCtx{
+ RepoID: ctx.Repo.Repository.ID,
+ OwnerID: 0,
+ IsRepo: true,
+ RunnersTemplate: tplRepoRunners,
+ RunnerEditTemplate: tplRepoRunnerEdit,
+ RedirectLink: ctx.Repo.RepoLink + "/settings/actions/runners/",
+ }, nil
+ }
+
+ if ctx.Data["PageIsOrgSettings"] == true {
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return nil, nil
+ }
+ return &runnersCtx{
+ RepoID: 0,
+ OwnerID: ctx.Org.Organization.ID,
+ IsOrg: true,
+ RunnersTemplate: tplOrgRunners,
+ RunnerEditTemplate: tplOrgRunnerEdit,
+ RedirectLink: ctx.Org.OrgLink + "/settings/actions/runners/",
+ }, nil
+ }
+
+ if ctx.Data["PageIsAdmin"] == true {
+ return &runnersCtx{
+ RepoID: 0,
+ OwnerID: 0,
+ IsAdmin: true,
+ RunnersTemplate: tplAdminRunners,
+ RunnerEditTemplate: tplAdminRunnerEdit,
+ RedirectLink: setting.AppSubURL + "/-/admin/actions/runners/",
+ }, nil
+ }
+
+ if ctx.Data["PageIsUserSettings"] == true {
+ return &runnersCtx{
+ OwnerID: ctx.Doer.ID,
+ RepoID: 0,
+ IsUser: true,
+ RunnersTemplate: tplUserRunners,
+ RunnerEditTemplate: tplUserRunnerEdit,
+ RedirectLink: setting.AppSubURL + "/user/settings/actions/runners/",
+ }, nil
+ }
+
+ return nil, errors.New("unable to set Runners context")
+}
+
+// Runners render settings/actions/runners page for repo level
+func Runners(ctx *context.Context) {
+ ctx.Data["PageIsSharedSettingsRunners"] = true
+ ctx.Data["Title"] = ctx.Tr("actions.actions")
+ ctx.Data["PageType"] = "runners"
+
+ rCtx, err := getRunnersCtx(ctx)
+ if err != nil {
+ ctx.ServerError("getRunnersCtx", err)
+ return
+ }
+
+ page := max(ctx.FormInt("page"), 1)
+
+ opts := actions_model.FindRunnerOptions{
+ ListOptions: db.ListOptions{
+ Page: page,
+ PageSize: 100,
+ },
+ Sort: ctx.Req.URL.Query().Get("sort"),
+ Filter: ctx.Req.URL.Query().Get("q"),
+ }
+ if rCtx.IsRepo {
+ opts.RepoID = rCtx.RepoID
+ opts.WithAvailable = true
+ } else if rCtx.IsOrg || rCtx.IsUser {
+ opts.OwnerID = rCtx.OwnerID
+ opts.WithAvailable = true
+ }
+
runners, count, err := db.FindAndCount[actions_model.ActionRunner](ctx, opts)
if err != nil {
ctx.ServerError("CountRunners", err)
@@ -53,10 +162,26 @@ func RunnersList(ctx *context.Context, opts actions_model.FindRunnerOptions) {
pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
ctx.Data["Page"] = pager
+
+ ctx.HTML(http.StatusOK, rCtx.RunnersTemplate)
}
-// RunnerDetails prepares data for runners edit page
-func RunnerDetails(ctx *context.Context, page int, runnerID, ownerID, repoID int64) {
+// RunnersEdit renders runner edit page for repository level
+func RunnersEdit(ctx *context.Context) {
+ ctx.Data["PageIsSharedSettingsRunners"] = true
+ ctx.Data["Title"] = ctx.Tr("actions.runners.edit_runner")
+ rCtx, err := getRunnersCtx(ctx)
+ if err != nil {
+ ctx.ServerError("getRunnersCtx", err)
+ return
+ }
+
+ page := max(ctx.FormInt("page"), 1)
+
+ runnerID := ctx.PathParamInt64("runnerid")
+ ownerID := rCtx.OwnerID
+ repoID := rCtx.RepoID
+
runner, err := actions_model.GetRunnerByID(ctx, runnerID)
if err != nil {
ctx.ServerError("GetRunnerByID", err)
@@ -66,9 +191,9 @@ func RunnerDetails(ctx *context.Context, page int, runnerID, ownerID, repoID int
ctx.ServerError("LoadAttributes", err)
return
}
- if !runner.Editable(ownerID, repoID) {
+ if !runner.EditableInContext(ownerID, repoID) {
err = errors.New("no permission to edit this runner")
- ctx.NotFound("RunnerDetails", err)
+ ctx.NotFound(err)
return
}
@@ -97,18 +222,30 @@ func RunnerDetails(ctx *context.Context, page int, runnerID, ownerID, repoID int
ctx.Data["Tasks"] = tasks
pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
ctx.Data["Page"] = pager
+
+ ctx.HTML(http.StatusOK, rCtx.RunnerEditTemplate)
}
-// RunnerDetailsEditPost response for edit runner details
-func RunnerDetailsEditPost(ctx *context.Context, runnerID, ownerID, repoID int64, redirectTo string) {
+func RunnersEditPost(ctx *context.Context) {
+ rCtx, err := getRunnersCtx(ctx)
+ if err != nil {
+ ctx.ServerError("getRunnersCtx", err)
+ return
+ }
+
+ runnerID := ctx.PathParamInt64("runnerid")
+ ownerID := rCtx.OwnerID
+ repoID := rCtx.RepoID
+ redirectTo := rCtx.RedirectLink
+
runner, err := actions_model.GetRunnerByID(ctx, runnerID)
if err != nil {
log.Warn("RunnerDetailsEditPost.GetRunnerByID failed: %v, url: %s", err, ctx.Req.URL)
ctx.ServerError("RunnerDetailsEditPost.GetRunnerByID", err)
return
}
- if !runner.Editable(ownerID, repoID) {
- ctx.NotFound("RunnerDetailsEditPost.Editable", util.NewPermissionDeniedErrorf("no permission to edit this runner"))
+ if !runner.EditableInContext(ownerID, repoID) {
+ ctx.NotFound(util.NewPermissionDeniedErrorf("no permission to edit this runner"))
return
}
@@ -129,10 +266,18 @@ func RunnerDetailsEditPost(ctx *context.Context, runnerID, ownerID, repoID int64
ctx.Redirect(redirectTo)
}
-// RunnerResetRegistrationToken reset registration token
-func RunnerResetRegistrationToken(ctx *context.Context, ownerID, repoID int64, redirectTo string) {
- _, err := actions_model.NewRunnerToken(ctx, ownerID, repoID)
+func ResetRunnerRegistrationToken(ctx *context.Context) {
+ rCtx, err := getRunnersCtx(ctx)
if err != nil {
+ ctx.ServerError("getRunnersCtx", err)
+ return
+ }
+
+ ownerID := rCtx.OwnerID
+ repoID := rCtx.RepoID
+ redirectTo := rCtx.RedirectLink
+
+ if _, err := actions_model.NewRunnerToken(ctx, ownerID, repoID); err != nil {
ctx.ServerError("ResetRunnerRegistrationToken", err)
return
}
@@ -140,11 +285,28 @@ func RunnerResetRegistrationToken(ctx *context.Context, ownerID, repoID int64, r
ctx.JSONRedirect(redirectTo)
}
-// RunnerDeletePost response for deleting a runner
-func RunnerDeletePost(ctx *context.Context, runnerID int64,
- successRedirectTo, failedRedirectTo string,
-) {
- if err := actions_model.DeleteRunner(ctx, runnerID); err != nil {
+// RunnerDeletePost response for deleting runner
+func RunnerDeletePost(ctx *context.Context) {
+ rCtx, err := getRunnersCtx(ctx)
+ if err != nil {
+ ctx.ServerError("getRunnersCtx", err)
+ return
+ }
+
+ runner := findActionsRunner(ctx, rCtx)
+ if ctx.Written() {
+ return
+ }
+
+ if !runner.EditableInContext(rCtx.OwnerID, rCtx.RepoID) {
+ ctx.NotFound(util.NewPermissionDeniedErrorf("no permission to delete this runner"))
+ return
+ }
+
+ successRedirectTo := rCtx.RedirectLink
+ failedRedirectTo := rCtx.RedirectLink + url.PathEscape(ctx.PathParam("runnerid"))
+
+ if err := actions_model.DeleteRunner(ctx, runner.ID); err != nil {
log.Warn("DeleteRunnerPost.UpdateRunner failed: %v, url: %s", err, ctx.Req.URL)
ctx.Flash.Warning(ctx.Tr("actions.runners.delete_runner_failed"))
@@ -158,3 +320,41 @@ func RunnerDeletePost(ctx *context.Context, runnerID int64,
ctx.JSONRedirect(successRedirectTo)
}
+
+func RedirectToDefaultSetting(ctx *context.Context) {
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings/actions/runners")
+}
+
+func findActionsRunner(ctx *context.Context, rCtx *runnersCtx) *actions_model.ActionRunner {
+ runnerID := ctx.PathParamInt64("runnerid")
+ opts := &actions_model.FindRunnerOptions{
+ IDs: []int64{runnerID},
+ }
+ switch {
+ case rCtx.IsRepo:
+ opts.RepoID = rCtx.RepoID
+ if opts.RepoID == 0 {
+ panic("repoID is 0")
+ }
+ case rCtx.IsOrg, rCtx.IsUser:
+ opts.OwnerID = rCtx.OwnerID
+ if opts.OwnerID == 0 {
+ panic("ownerID is 0")
+ }
+ case rCtx.IsAdmin:
+ // do nothing
+ default:
+ panic("invalid actions runner context")
+ }
+
+ got, err := db.Find[actions_model.ActionRunner](ctx, opts)
+ if err != nil {
+ ctx.ServerError("FindRunner", err)
+ return nil
+ } else if len(got) == 0 {
+ ctx.NotFound(errors.New("runner not found"))
+ return nil
+ }
+
+ return got[0]
+}
diff --git a/routers/web/shared/actions/variables.go b/routers/web/shared/actions/variables.go
index f895475748..a43c2c2690 100644
--- a/routers/web/shared/actions/variables.go
+++ b/routers/web/shared/actions/variables.go
@@ -4,31 +4,127 @@
package actions
import (
+ "errors"
+ "net/http"
+
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/web"
+ shared_user "code.gitea.io/gitea/routers/web/shared/user"
actions_service "code.gitea.io/gitea/services/actions"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
-func SetVariablesContext(ctx *context.Context, ownerID, repoID int64) {
+const (
+ tplRepoVariables templates.TplName = "repo/settings/actions"
+ tplOrgVariables templates.TplName = "org/settings/actions"
+ tplUserVariables templates.TplName = "user/settings/actions"
+ tplAdminVariables templates.TplName = "admin/actions"
+)
+
+type variablesCtx struct {
+ OwnerID int64
+ RepoID int64
+ IsRepo bool
+ IsOrg bool
+ IsUser bool
+ IsGlobal bool
+ VariablesTemplate templates.TplName
+ RedirectLink string
+}
+
+func getVariablesCtx(ctx *context.Context) (*variablesCtx, error) {
+ if ctx.Data["PageIsRepoSettings"] == true {
+ return &variablesCtx{
+ OwnerID: 0,
+ RepoID: ctx.Repo.Repository.ID,
+ IsRepo: true,
+ VariablesTemplate: tplRepoVariables,
+ RedirectLink: ctx.Repo.RepoLink + "/settings/actions/variables",
+ }, nil
+ }
+
+ if ctx.Data["PageIsOrgSettings"] == true {
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return nil, nil
+ }
+ return &variablesCtx{
+ OwnerID: ctx.ContextUser.ID,
+ RepoID: 0,
+ IsOrg: true,
+ VariablesTemplate: tplOrgVariables,
+ RedirectLink: ctx.Org.OrgLink + "/settings/actions/variables",
+ }, nil
+ }
+
+ if ctx.Data["PageIsUserSettings"] == true {
+ return &variablesCtx{
+ OwnerID: ctx.Doer.ID,
+ RepoID: 0,
+ IsUser: true,
+ VariablesTemplate: tplUserVariables,
+ RedirectLink: setting.AppSubURL + "/user/settings/actions/variables",
+ }, nil
+ }
+
+ if ctx.Data["PageIsAdmin"] == true {
+ return &variablesCtx{
+ OwnerID: 0,
+ RepoID: 0,
+ IsGlobal: true,
+ VariablesTemplate: tplAdminVariables,
+ RedirectLink: setting.AppSubURL + "/-/admin/actions/variables",
+ }, nil
+ }
+
+ return nil, errors.New("unable to set Variables context")
+}
+
+func Variables(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("actions.variables")
+ ctx.Data["PageType"] = "variables"
+ ctx.Data["PageIsSharedSettingsVariables"] = true
+
+ vCtx, err := getVariablesCtx(ctx)
+ if err != nil {
+ ctx.ServerError("getVariablesCtx", err)
+ return
+ }
+
variables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{
- OwnerID: ownerID,
- RepoID: repoID,
+ OwnerID: vCtx.OwnerID,
+ RepoID: vCtx.RepoID,
})
if err != nil {
ctx.ServerError("FindVariables", err)
return
}
ctx.Data["Variables"] = variables
+ ctx.Data["DataMaxLength"] = actions_model.VariableDataMaxLength
+ ctx.Data["DescriptionMaxLength"] = actions_model.VariableDescriptionMaxLength
+ ctx.HTML(http.StatusOK, vCtx.VariablesTemplate)
}
-func CreateVariable(ctx *context.Context, ownerID, repoID int64, redirectURL string) {
+func VariableCreate(ctx *context.Context) {
+ vCtx, err := getVariablesCtx(ctx)
+ if err != nil {
+ ctx.ServerError("getVariablesCtx", err)
+ return
+ }
+
+ if ctx.HasError() { // form binding validation error
+ ctx.JSONError(ctx.GetErrMsg())
+ return
+ }
+
form := web.GetForm(ctx).(*forms.EditVariableForm)
- v, err := actions_service.CreateVariable(ctx, ownerID, repoID, form.Name, form.Data)
+ v, err := actions_service.CreateVariable(ctx, vCtx.OwnerID, vCtx.RepoID, form.Name, form.Data, form.Description)
if err != nil {
log.Error("CreateVariable: %v", err)
ctx.JSONError(ctx.Tr("actions.variables.creation.failed"))
@@ -36,30 +132,93 @@ func CreateVariable(ctx *context.Context, ownerID, repoID int64, redirectURL str
}
ctx.Flash.Success(ctx.Tr("actions.variables.creation.success", v.Name))
- ctx.JSONRedirect(redirectURL)
+ ctx.JSONRedirect(vCtx.RedirectLink)
}
-func UpdateVariable(ctx *context.Context, redirectURL string) {
+func VariableUpdate(ctx *context.Context) {
+ vCtx, err := getVariablesCtx(ctx)
+ if err != nil {
+ ctx.ServerError("getVariablesCtx", err)
+ return
+ }
+
+ if ctx.HasError() { // form binding validation error
+ ctx.JSONError(ctx.GetErrMsg())
+ return
+ }
+
id := ctx.PathParamInt64("variable_id")
+
+ variable := findActionsVariable(ctx, id, vCtx)
+ if ctx.Written() {
+ return
+ }
+
form := web.GetForm(ctx).(*forms.EditVariableForm)
+ variable.Name = form.Name
+ variable.Data = form.Data
+ variable.Description = form.Description
- if ok, err := actions_service.UpdateVariable(ctx, id, form.Name, form.Data); err != nil || !ok {
+ if ok, err := actions_service.UpdateVariableNameData(ctx, variable); err != nil || !ok {
log.Error("UpdateVariable: %v", err)
ctx.JSONError(ctx.Tr("actions.variables.update.failed"))
return
}
ctx.Flash.Success(ctx.Tr("actions.variables.update.success"))
- ctx.JSONRedirect(redirectURL)
+ ctx.JSONRedirect(vCtx.RedirectLink)
+}
+
+func findActionsVariable(ctx *context.Context, id int64, vCtx *variablesCtx) *actions_model.ActionVariable {
+ opts := actions_model.FindVariablesOpts{
+ IDs: []int64{id},
+ }
+ switch {
+ case vCtx.IsRepo:
+ opts.RepoID = vCtx.RepoID
+ if opts.RepoID == 0 {
+ panic("RepoID is 0")
+ }
+ case vCtx.IsOrg, vCtx.IsUser:
+ opts.OwnerID = vCtx.OwnerID
+ if opts.OwnerID == 0 {
+ panic("OwnerID is 0")
+ }
+ case vCtx.IsGlobal:
+ // do nothing
+ default:
+ panic("invalid actions variable")
+ }
+
+ got, err := actions_model.FindVariables(ctx, opts)
+ if err != nil {
+ ctx.ServerError("FindVariables", err)
+ return nil
+ } else if len(got) == 0 {
+ ctx.NotFound(nil)
+ return nil
+ }
+ return got[0]
}
-func DeleteVariable(ctx *context.Context, redirectURL string) {
+func VariableDelete(ctx *context.Context) {
+ vCtx, err := getVariablesCtx(ctx)
+ if err != nil {
+ ctx.ServerError("getVariablesCtx", err)
+ return
+ }
+
id := ctx.PathParamInt64("variable_id")
- if err := actions_service.DeleteVariableByID(ctx, id); err != nil {
+ variable := findActionsVariable(ctx, id, vCtx)
+ if ctx.Written() {
+ return
+ }
+
+ if err := actions_service.DeleteVariableByID(ctx, variable.ID); err != nil {
log.Error("Delete variable [%d] failed: %v", id, err)
ctx.JSONError(ctx.Tr("actions.variables.deletion.failed"))
return
}
ctx.Flash.Success(ctx.Tr("actions.variables.deletion.success"))
- ctx.JSONRedirect(redirectURL)
+ ctx.JSONRedirect(vCtx.RedirectLink)
}
diff --git a/routers/web/shared/issue/issue_label.go b/routers/web/shared/issue/issue_label.go
index eacea36b02..e2eeaaf0af 100644
--- a/routers/web/shared/issue/issue_label.go
+++ b/routers/web/shared/issue/issue_label.go
@@ -14,14 +14,18 @@ import (
)
// PrepareFilterIssueLabels reads the "labels" query parameter, sets `ctx.Data["Labels"]` and `ctx.Data["SelectLabels"]`
-func PrepareFilterIssueLabels(ctx *context.Context, repoID int64, owner *user_model.User) (labelIDs []int64) {
+func PrepareFilterIssueLabels(ctx *context.Context, repoID int64, owner *user_model.User) (ret struct {
+ AllLabels []*issues_model.Label
+ SelectedLabelIDs []int64
+},
+) {
// 1,-2 means including label 1 and excluding label 2
// 0 means issues with no label
// blank means labels will not be filtered for issues
selectLabels := ctx.FormString("labels")
if selectLabels != "" {
var err error
- labelIDs, err = base.StringsToInt64s(strings.Split(selectLabels, ","))
+ ret.SelectedLabelIDs, err = base.StringsToInt64s(strings.Split(selectLabels, ","))
if err != nil {
ctx.Flash.Error(ctx.Tr("invalid_data", selectLabels), true)
}
@@ -32,7 +36,7 @@ func PrepareFilterIssueLabels(ctx *context.Context, repoID int64, owner *user_mo
repoLabels, err := issues_model.GetLabelsByRepoID(ctx, repoID, "", db.ListOptions{})
if err != nil {
ctx.ServerError("GetLabelsByRepoID", err)
- return nil
+ return ret
}
allLabels = append(allLabels, repoLabels...)
}
@@ -41,14 +45,14 @@ func PrepareFilterIssueLabels(ctx *context.Context, repoID int64, owner *user_mo
orgLabels, err := issues_model.GetLabelsByOrgID(ctx, owner.ID, "", db.ListOptions{})
if err != nil {
ctx.ServerError("GetLabelsByOrgID", err)
- return nil
+ return ret
}
allLabels = append(allLabels, orgLabels...)
}
// Get the exclusive scope for every label ID
- labelExclusiveScopes := make([]string, 0, len(labelIDs))
- for _, labelID := range labelIDs {
+ labelExclusiveScopes := make([]string, 0, len(ret.SelectedLabelIDs))
+ for _, labelID := range ret.SelectedLabelIDs {
foundExclusiveScope := false
for _, label := range allLabels {
if label.ID == labelID || label.ID == -labelID {
@@ -63,9 +67,10 @@ func PrepareFilterIssueLabels(ctx *context.Context, repoID int64, owner *user_mo
}
for _, l := range allLabels {
- l.LoadSelectedLabelsAfterClick(labelIDs, labelExclusiveScopes)
+ l.LoadSelectedLabelsAfterClick(ret.SelectedLabelIDs, labelExclusiveScopes)
}
ctx.Data["Labels"] = allLabels
ctx.Data["SelectLabels"] = selectLabels
- return labelIDs
+ ret.AllLabels = allLabels
+ return ret
}
diff --git a/routers/web/shared/label/label.go b/routers/web/shared/label/label.go
new file mode 100644
index 0000000000..6968a318c4
--- /dev/null
+++ b/routers/web/shared/label/label.go
@@ -0,0 +1,26 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package label
+
+import (
+ "code.gitea.io/gitea/modules/label"
+ "code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/forms"
+)
+
+func GetLabelEditForm(ctx *context.Context) *forms.CreateLabelForm {
+ form := web.GetForm(ctx).(*forms.CreateLabelForm)
+ if ctx.HasError() {
+ ctx.JSONError(ctx.Data["ErrorMsg"].(string))
+ return nil
+ }
+ var err error
+ form.Color, err = label.NormalizeColor(form.Color)
+ if err != nil {
+ ctx.JSONError(ctx.Tr("repo.issues.label_color_invalid"))
+ return nil
+ }
+ return form
+}
diff --git a/routers/web/shared/packages/packages.go b/routers/web/shared/packages/packages.go
index a1bcb09850..a18dedf89c 100644
--- a/routers/web/shared/packages/packages.go
+++ b/routers/web/shared/packages/packages.go
@@ -8,7 +8,6 @@ import (
"net/http"
"time"
- "code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
@@ -159,12 +158,18 @@ func SetRulePreviewContext(ctx *context.Context, owner *user_model.User) {
PackageID: p.ID,
IsInternal: optional.Some(false),
Sort: packages_model.SortCreatedDesc,
- Paginator: db.NewAbsoluteListOptions(pcr.KeepCount, 200),
})
if err != nil {
ctx.ServerError("SearchVersions", err)
return
}
+ if pcr.KeepCount > 0 {
+ if pcr.KeepCount < len(pvs) {
+ pvs = pvs[pcr.KeepCount:]
+ } else {
+ pvs = nil
+ }
+ }
for _, pv := range pvs {
if skip, err := container_service.ShouldBeSkipped(ctx, pcr, p, pv); err != nil {
ctx.ServerError("ShouldBeSkipped", err)
@@ -177,7 +182,6 @@ func SetRulePreviewContext(ctx *context.Context, owner *user_model.User) {
if pcr.MatchFullName {
toMatch = p.LowerName + "/" + pv.LowerVersion
}
-
if pcr.KeepPatternMatcher != nil && pcr.KeepPatternMatcher.MatchString(toMatch) {
continue
}
@@ -210,7 +214,7 @@ func getCleanupRuleByContext(ctx *context.Context, owner *user_model.User) *pack
pcr, err := packages_model.GetCleanupRuleByID(ctx, id)
if err != nil {
if err == packages_model.ErrPackageCleanupRuleNotExist {
- ctx.NotFound("", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("GetCleanupRuleByID", err)
}
@@ -221,7 +225,7 @@ func getCleanupRuleByContext(ctx *context.Context, owner *user_model.User) *pack
return pcr
}
- ctx.NotFound("", fmt.Errorf("PackageCleanupRule[%v] not associated to owner %v", id, owner))
+ ctx.NotFound(fmt.Errorf("PackageCleanupRule[%v] not associated to owner %v", id, owner))
return nil
}
diff --git a/routers/web/shared/project/column.go b/routers/web/shared/project/column.go
index 141c80716f..b6ffaea7f8 100644
--- a/routers/web/shared/project/column.go
+++ b/routers/web/shared/project/column.go
@@ -17,7 +17,7 @@ func MoveColumns(ctx *context.Context) {
return
}
if !project.CanBeAccessedByOwnerRepo(ctx.ContextUser.ID, ctx.Repo.Repository) {
- ctx.NotFound("CanBeAccessedByOwnerRepo", nil)
+ ctx.NotFound(nil)
return
}
diff --git a/routers/web/shared/secrets/secrets.go b/routers/web/shared/secrets/secrets.go
index 3bd421f86a..29f4e9520d 100644
--- a/routers/web/shared/secrets/secrets.go
+++ b/routers/web/shared/secrets/secrets.go
@@ -22,19 +22,21 @@ func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) {
}
ctx.Data["Secrets"] = secrets
+ ctx.Data["DataMaxLength"] = secret_model.SecretDataMaxLength
+ ctx.Data["DescriptionMaxLength"] = secret_model.SecretDescriptionMaxLength
}
func PerformSecretsPost(ctx *context.Context, ownerID, repoID int64, redirectURL string) {
form := web.GetForm(ctx).(*forms.AddSecretForm)
- s, _, err := secret_service.CreateOrUpdateSecret(ctx, ownerID, repoID, form.Name, util.ReserveLineBreakForTextarea(form.Data))
+ s, _, err := secret_service.CreateOrUpdateSecret(ctx, ownerID, repoID, form.Name, util.ReserveLineBreakForTextarea(form.Data), form.Description)
if err != nil {
log.Error("CreateOrUpdateSecret failed: %v", err)
- ctx.JSONError(ctx.Tr("secrets.creation.failed"))
+ ctx.JSONError(ctx.Tr("secrets.save_failed"))
return
}
- ctx.Flash.Success(ctx.Tr("secrets.creation.success", s.Name))
+ ctx.Flash.Success(ctx.Tr("secrets.save_success", s.Name))
ctx.JSONRedirect(redirectURL)
}
diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go
index 62b146c7f3..2bd0abc4c0 100644
--- a/routers/web/shared/user/header.go
+++ b/routers/web/shared/user/header.go
@@ -24,19 +24,8 @@ import (
"code.gitea.io/gitea/services/context"
)
-// prepareContextForCommonProfile store some common data into context data for user's profile related pages (including the nav menu)
-// It is designed to be fast and safe to be called multiple times in one request
-func prepareContextForCommonProfile(ctx *context.Context) {
- ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
- ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
- ctx.Data["EnableFeed"] = setting.Other.EnableFeed
- ctx.Data["FeedURL"] = ctx.ContextUser.HomeLink()
-}
-
-// PrepareContextForProfileBigAvatar set the context for big avatar view on the profile page
-func PrepareContextForProfileBigAvatar(ctx *context.Context) {
- prepareContextForCommonProfile(ctx)
-
+// prepareContextForProfileBigAvatar set the context for big avatar view on the profile page
+func prepareContextForProfileBigAvatar(ctx *context.Context) {
ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate
if setting.Service.UserLocationMapURL != "" {
@@ -58,13 +47,12 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
ctx.Data["RenderedDescription"] = content
}
- showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
orgs, err := db.Find[organization.Organization](ctx, organization.FindOrgOptions{
- UserID: ctx.ContextUser.ID,
- IncludePrivate: showPrivate,
+ UserID: ctx.ContextUser.ID,
+ IncludeVisibility: organization.DoerViewOtherVisibility(ctx.Doer, ctx.ContextUser),
ListOptions: db.ListOptions{
Page: 1,
- // query one more results (without a separate counting) to see whether we need to add the "show more orgs" link
+ // query one more result (without a separate counting) to see whether we need to add the "show more orgs" link
PageSize: setting.UI.User.OrgPagingNum + 1,
},
})
@@ -138,17 +126,45 @@ func FindOwnerProfileReadme(ctx *context.Context, doer *user_model.User, optProf
return profileDbRepo, profileReadmeBlob
}
-func RenderUserHeader(ctx *context.Context) {
- prepareContextForCommonProfile(ctx)
-
- _, profileReadmeBlob := FindOwnerProfileReadme(ctx, ctx.Doer)
- ctx.Data["HasUserProfileReadme"] = profileReadmeBlob != nil
+type PrepareOwnerHeaderResult struct {
+ ProfilePublicRepo *repo_model.Repository
+ ProfilePublicReadmeBlob *git.Blob
+ ProfilePrivateRepo *repo_model.Repository
+ ProfilePrivateReadmeBlob *git.Blob
+ HasOrgProfileReadme bool
}
-func LoadHeaderCount(ctx *context.Context) error {
- prepareContextForCommonProfile(ctx)
+const (
+ RepoNameProfilePrivate = ".profile-private"
+ RepoNameProfile = ".profile"
+)
+
+func RenderUserOrgHeader(ctx *context.Context) (result *PrepareOwnerHeaderResult, err error) {
+ ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
+ ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
+ ctx.Data["EnableFeed"] = setting.Other.EnableFeed
+ ctx.Data["FeedURL"] = ctx.ContextUser.HomeLink()
+
+ if err := loadHeaderCount(ctx); err != nil {
+ return nil, err
+ }
- repoCount, err := repo_model.CountRepository(ctx, &repo_model.SearchRepoOptions{
+ result = &PrepareOwnerHeaderResult{}
+ if ctx.ContextUser.IsOrganization() {
+ result.ProfilePublicRepo, result.ProfilePublicReadmeBlob = FindOwnerProfileReadme(ctx, ctx.Doer)
+ result.ProfilePrivateRepo, result.ProfilePrivateReadmeBlob = FindOwnerProfileReadme(ctx, ctx.Doer, RepoNameProfilePrivate)
+ result.HasOrgProfileReadme = result.ProfilePublicReadmeBlob != nil || result.ProfilePrivateReadmeBlob != nil
+ ctx.Data["HasOrgProfileReadme"] = result.HasOrgProfileReadme // many pages need it to show the "overview" tab
+ } else {
+ _, profileReadmeBlob := FindOwnerProfileReadme(ctx, ctx.Doer)
+ ctx.Data["HasUserProfileReadme"] = profileReadmeBlob != nil
+ prepareContextForProfileBigAvatar(ctx)
+ }
+ return result, nil
+}
+
+func loadHeaderCount(ctx *context.Context) error {
+ repoCount, err := repo_model.CountRepository(ctx, repo_model.SearchRepoOptions{
Actor: ctx.Doer,
OwnerID: ctx.ContextUser.ID,
Private: ctx.IsSigned,
@@ -178,29 +194,3 @@ func LoadHeaderCount(ctx *context.Context) error {
return nil
}
-
-const (
- RepoNameProfilePrivate = ".profile-private"
- RepoNameProfile = ".profile"
-)
-
-type PrepareOrgHeaderResult struct {
- ProfilePublicRepo *repo_model.Repository
- ProfilePublicReadmeBlob *git.Blob
- ProfilePrivateRepo *repo_model.Repository
- ProfilePrivateReadmeBlob *git.Blob
- HasOrgProfileReadme bool
-}
-
-func PrepareOrgHeader(ctx *context.Context) (result *PrepareOrgHeaderResult, err error) {
- if err = LoadHeaderCount(ctx); err != nil {
- return nil, err
- }
-
- result = &PrepareOrgHeaderResult{}
- result.ProfilePublicRepo, result.ProfilePublicReadmeBlob = FindOwnerProfileReadme(ctx, ctx.Doer)
- result.ProfilePrivateRepo, result.ProfilePrivateReadmeBlob = FindOwnerProfileReadme(ctx, ctx.Doer, RepoNameProfilePrivate)
- result.HasOrgProfileReadme = result.ProfilePublicReadmeBlob != nil || result.ProfilePrivateReadmeBlob != nil
- ctx.Data["HasOrgProfileReadme"] = result.HasOrgProfileReadme // many pages need it to show the "overview" tab
- return result, nil
-}
diff --git a/routers/web/shared/user/helper.go b/routers/web/shared/user/helper.go
index b82181a1df..3fc39fd3ab 100644
--- a/routers/web/shared/user/helper.go
+++ b/routers/web/shared/user/helper.go
@@ -8,9 +8,7 @@ import (
"slices"
"strconv"
- "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/optional"
)
func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User {
@@ -34,19 +32,20 @@ func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User {
// So it's better to make it work like GitHub: users could input username directly.
// Since it only converts the username to ID directly and is only used internally (to search issues), so no permission check is needed.
// Return values:
-// * nil: no filter
-// * some(id): match the id, the id could be -1 to match the issues without assignee
-// * some(NonExistingID): match no issue (due to the user doesn't exist)
-func GetFilterUserIDByName(ctx context.Context, name string) optional.Option[int64] {
+// * "": no filter
+// * "{the-id}": match the id
+// * "(none)": match no issue (due to the user doesn't exist)
+func GetFilterUserIDByName(ctx context.Context, name string) string {
if name == "" {
- return optional.None[int64]()
+ return ""
}
u, err := user.GetUserByName(ctx, name)
if err != nil {
if id, err := strconv.ParseInt(name, 10, 64); err == nil {
- return optional.Some(id)
+ return strconv.FormatInt(id, 10)
}
- return optional.Some(db.NonExistingID)
+ // The "(none)" is for internal usage only: when doer tries to search non-existing user, use "(none)" to return empty result.
+ return "(none)"
}
- return optional.Some(u.ID)
+ return strconv.FormatInt(u.ID, 10)
}
diff --git a/routers/web/swagger_json.go b/routers/web/swagger_json.go
index fc39b504a9..52f6beaf59 100644
--- a/routers/web/swagger_json.go
+++ b/routers/web/swagger_json.go
@@ -4,10 +4,15 @@
package web
import (
+ "html/template"
+
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/context"
)
// SwaggerV1Json render swagger v1 json
func SwaggerV1Json(ctx *context.Context) {
+ ctx.Data["SwaggerAppVer"] = template.HTML(template.JSEscapeString(setting.AppVer))
+ ctx.Data["SwaggerAppSubUrl"] = setting.AppSubURL // it is JS-safe
ctx.JSONTemplate("swagger/v1_json")
}
diff --git a/routers/web/user/avatar.go b/routers/web/user/avatar.go
index f77bd602b3..6d3179bc48 100644
--- a/routers/web/user/avatar.go
+++ b/routers/web/user/avatar.go
@@ -4,7 +4,6 @@
package user
import (
- "strings"
"time"
"code.gitea.io/gitea/models/avatars"
@@ -17,31 +16,22 @@ func cacheableRedirect(ctx *context.Context, location string) {
// here we should not use `setting.StaticCacheTime`, it is pretty long (default: 6 hours)
// we must make sure the redirection cache time is short enough, otherwise a user won't see the updated avatar in 6 hours
// it's OK to make the cache time short, it is only a redirection, and doesn't cost much to make a new request
- httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 5*time.Minute)
+ httpcache.SetCacheControlInHeader(ctx.Resp.Header(), &httpcache.CacheControlOptions{MaxAge: 5 * time.Minute})
ctx.Redirect(location)
}
-// AvatarByUserName redirect browser to user avatar of requested size
-func AvatarByUserName(ctx *context.Context) {
- userName := ctx.PathParam("username")
- size := int(ctx.PathParamInt64("size"))
-
- var user *user_model.User
- if strings.ToLower(userName) != user_model.GhostUserLowerName {
+// AvatarByUsernameSize redirect browser to user avatar of requested size
+func AvatarByUsernameSize(ctx *context.Context) {
+ username := ctx.PathParam("username")
+ user := user_model.GetSystemUserByName(username)
+ if user == nil {
var err error
- if user, err = user_model.GetUserByName(ctx, userName); err != nil {
- if user_model.IsErrUserNotExist(err) {
- ctx.NotFound("GetUserByName", err)
- return
- }
- ctx.ServerError("Invalid user: "+userName, err)
+ if user, err = user_model.GetUserByName(ctx, username); err != nil {
+ ctx.NotFoundOrServerError("GetUserByName", user_model.IsErrUserNotExist, err)
return
}
- } else {
- user = user_model.NewGhostUser()
}
-
- cacheableRedirect(ctx, user.AvatarLinkWithSize(ctx, size))
+ cacheableRedirect(ctx, user.AvatarLinkWithSize(ctx, int(ctx.PathParamInt64("size"))))
}
// AvatarByEmailHash redirects the browser to the email avatar link
diff --git a/routers/web/user/code.go b/routers/web/user/code.go
index 665ce1a6a6..11579c40a6 100644
--- a/routers/web/user/code.go
+++ b/routers/web/user/code.go
@@ -5,6 +5,7 @@ package user
import (
"net/http"
+ "slices"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
@@ -26,11 +27,8 @@ func CodeSearch(ctx *context.Context) {
ctx.Redirect(ctx.ContextUser.HomeLink())
return
}
- shared_user.PrepareContextForProfileBigAvatar(ctx)
- shared_user.RenderUserHeader(ctx)
-
- if err := shared_user.LoadHeaderCount(ctx); err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
@@ -68,10 +66,10 @@ func CodeSearch(ctx *context.Context) {
if len(repoIDs) > 0 {
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{
- RepoIDs: repoIDs,
- Keyword: prepareSearch.Keyword,
- IsKeywordFuzzy: prepareSearch.IsFuzzy,
- Language: prepareSearch.Language,
+ RepoIDs: repoIDs,
+ Keyword: prepareSearch.Keyword,
+ SearchMode: prepareSearch.SearchMode,
+ Language: prepareSearch.Language,
Paginator: &db.ListOptions{
Page: page,
PageSize: setting.UI.RepoSearchPagingNum,
@@ -89,14 +87,7 @@ func CodeSearch(ctx *context.Context) {
loadRepoIDs := make([]int64, 0, len(searchResults))
for _, result := range searchResults {
- var find bool
- for _, id := range loadRepoIDs {
- if id == result.RepoID {
- find = true
- break
- }
- }
- if !find {
+ if !slices.Contains(loadRepoIDs, result.RepoID) {
loadRepoIDs = append(loadRepoIDs, result.RepoID)
}
}
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index c79648a455..b53a3daedb 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -26,6 +26,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/container"
+ "code.gitea.io/gitea/modules/indexer"
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup/markdown"
@@ -41,8 +42,8 @@ import (
issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull"
- "github.com/keybase/go-crypto/openpgp"
- "github.com/keybase/go-crypto/openpgp/armor"
+ "github.com/ProtonMail/go-crypto/openpgp"
+ "github.com/ProtonMail/go-crypto/openpgp/armor"
"xorm.io/builder"
)
@@ -118,7 +119,7 @@ func Dashboard(ctx *context.Context) {
ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data)
}
- feeds, count, err := feed_service.GetFeeds(ctx, activities_model.GetFeedsOptions{
+ feeds, count, err := feed_service.GetFeedsForDashboard(ctx, activities_model.GetFeedsOptions{
RequestedUser: ctxUser,
RequestedTeam: ctx.Org.Team,
Actor: ctx.Doer,
@@ -136,11 +137,10 @@ func Dashboard(ctx *context.Context) {
return
}
- ctx.Data["Feeds"] = feeds
-
- pager := context.NewPagination(int(count), setting.UI.FeedPagingNum, page, 5)
+ pager := context.NewPagination(count, setting.UI.FeedPagingNum, page, 5).WithCurRows(len(feeds))
pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager
+ ctx.Data["Feeds"] = feeds
ctx.HTML(http.StatusOK, tplDashboard)
}
@@ -176,7 +176,7 @@ func Milestones(ctx *context.Context) {
}
var (
- userRepoCond = repo_model.SearchRepositoryCondition(&repoOpts) // all repo condition user could visit
+ userRepoCond = repo_model.SearchRepositoryCondition(repoOpts) // all repo condition user could visit
repoCond = userRepoCond
repoIDs []int64
@@ -197,7 +197,7 @@ func Milestones(ctx *context.Context) {
reposQuery = reposQuery[1 : len(reposQuery)-1]
// for each ID (delimiter ",") add to int to repoIDs
- for _, rID := range strings.Split(reposQuery, ",") {
+ for rID := range strings.SplitSeq(reposQuery, ",") {
// Ensure nonempty string entries
if rID != "" && rID != "0" {
rIDint64, err := strconv.ParseInt(rID, 10, 64)
@@ -242,7 +242,7 @@ func Milestones(ctx *context.Context) {
return
}
- showRepos, _, err := repo_model.SearchRepositoryByCondition(ctx, &repoOpts, userRepoCond, false)
+ showRepos, _, err := repo_model.SearchRepositoryByCondition(ctx, repoOpts, userRepoCond, false)
if err != nil {
ctx.ServerError("SearchRepositoryByCondition", err)
return
@@ -417,7 +417,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
IsPull: optional.Some(isPullList),
SortType: sortType,
IsArchived: optional.Some(false),
- User: ctx.Doer,
+ Doer: ctx.Doer,
}
// --------------------------------------------------------------------------
// Build opts (IssuesOptions), which contains filter information.
@@ -429,7 +429,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
// Get repository IDs where User/Org/Team has access.
if ctx.Org != nil && ctx.Org.Organization != nil {
- opts.Org = ctx.Org.Organization
+ opts.Owner = ctx.Org.Organization.AsUser()
opts.Team = ctx.Org.Team
issue.PrepareFilterIssueLabels(ctx, 0, ctx.Org.Organization.AsUser())
@@ -447,7 +447,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
ctx.Data["FilterAssigneeUsername"] = assigneeUsername
opts.AssigneeID = user.GetFilterUserIDByName(ctx, assigneeUsername)
- isFuzzy := ctx.FormBool("fuzzy")
+ searchMode := ctx.FormString("search_mode")
// Search all repositories which
//
@@ -461,7 +461,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
// As team:
// - Team org's owns the repository.
// - Team has read permission to repository.
- repoOpts := &repo_model.SearchRepoOptions{
+ repoOpts := repo_model.SearchRepoOptions{
Actor: ctx.Doer,
OwnerID: ctxUser.ID,
Private: true,
@@ -500,9 +500,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
case issues_model.FilterModeAll:
case issues_model.FilterModeYourRepositories:
case issues_model.FilterModeAssign:
- opts.AssigneeID = optional.Some(ctx.Doer.ID)
+ opts.AssigneeID = strconv.FormatInt(ctx.Doer.ID, 10)
case issues_model.FilterModeCreate:
- opts.PosterID = optional.Some(ctx.Doer.ID)
+ opts.PosterID = strconv.FormatInt(ctx.Doer.ID, 10)
case issues_model.FilterModeMention:
opts.MentionedID = ctx.Doer.ID
case issues_model.FilterModeReviewRequested:
@@ -520,10 +520,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
opts.IsClosed = optional.Some(isShowClosed)
// Make sure page number is at least 1. Will be posted to ctx.Data.
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
opts.Paginator = &db.ListOptions{
Page: page,
PageSize: setting.UI.IssuePagingNum,
@@ -549,7 +546,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
var issues issues_model.IssueList
{
issueIDs, _, err := issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, opts).Copy(
- func(o *issue_indexer.SearchOptions) { o.IsFuzzyKeyword = isFuzzy },
+ func(o *issue_indexer.SearchOptions) {
+ o.SearchMode = indexer.SearchModeType(searchMode)
+ },
))
if err != nil {
ctx.ServerError("issueIDsFromSearch", err)
@@ -576,17 +575,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
// -------------------------------
// Fill stats to post to ctx.Data.
// -------------------------------
- issueStats, err := getUserIssueStats(ctx, filterMode, issue_indexer.ToSearchOptions(keyword, opts).Copy(
+ issueStats, err := getUserIssueStats(ctx, ctxUser, filterMode, issue_indexer.ToSearchOptions(keyword, opts).Copy(
func(o *issue_indexer.SearchOptions) {
- o.IsFuzzyKeyword = isFuzzy
- // If the doer is the same as the context user, which means the doer is viewing his own dashboard,
- // it's not enough to show the repos that the doer owns or has been explicitly granted access to,
- // because the doer may create issues or be mentioned in any public repo.
- // So we need search issues in all public repos.
- o.AllPublic = ctx.Doer.ID == ctxUser.ID
- o.MentionID = nil
- o.ReviewRequestedID = nil
- o.ReviewedID = nil
+ o.SearchMode = indexer.SearchModeType(searchMode)
},
))
if err != nil {
@@ -623,9 +614,10 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
return 0
}
reviewTyp := issues_model.ReviewTypeApprove
- if typ == "reject" {
+ switch typ {
+ case "reject":
reviewTyp = issues_model.ReviewTypeReject
- } else if typ == "waiting" {
+ case "waiting":
reviewTyp = issues_model.ReviewTypeRequest
}
for _, count := range counts {
@@ -641,7 +633,8 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
ctx.Data["ViewType"] = viewType
ctx.Data["SortType"] = sortType
ctx.Data["IsShowClosed"] = isShowClosed
- ctx.Data["IsFuzzy"] = isFuzzy
+ ctx.Data["SearchModes"] = issue_indexer.SupportedSearchModes()
+ ctx.Data["SelectedSearchMode"] = ctx.FormTrim("search_mode")
if isShowClosed {
ctx.Data["State"] = "closed"
@@ -703,7 +696,7 @@ func ShowGPGKeys(ctx *context.Context) {
headers := make(map[string]string)
if len(failedEntitiesID) > 0 { // If some key need re-import to be exported
- headers["Note"] = fmt.Sprintf("The keys with the following IDs couldn't be exported and need to be reuploaded %s", strings.Join(failedEntitiesID, ", "))
+ headers["Note"] = "The keys with the following IDs couldn't be exported and need to be reuploaded " + strings.Join(failedEntitiesID, ", ")
} else if len(entities) == 0 {
headers["Note"] = "This user hasn't uploaded any GPG keys."
}
@@ -732,7 +725,7 @@ func UsernameSubRoute(ctx *context.Context) {
// check view permissions
if !user_model.IsUserVisibleToViewer(ctx, ctx.ContextUser, ctx.Doer) {
- ctx.NotFound("user", fmt.Errorf("%s", ctx.ContextUser.Name))
+ ctx.NotFound(fmt.Errorf("%s", ctx.ContextUser.Name))
return false
}
return true
@@ -740,7 +733,7 @@ func UsernameSubRoute(ctx *context.Context) {
switch {
case strings.HasSuffix(username, ".png"):
if reloadParam(".png") {
- AvatarByUserName(ctx)
+ AvatarByUsernameSize(ctx)
}
case strings.HasSuffix(username, ".keys"):
if reloadParam(".keys") {
@@ -752,7 +745,7 @@ func UsernameSubRoute(ctx *context.Context) {
}
case strings.HasSuffix(username, ".rss"):
if !setting.Other.EnableFeed {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
if reloadParam(".rss") {
@@ -760,7 +753,7 @@ func UsernameSubRoute(ctx *context.Context) {
}
case strings.HasSuffix(username, ".atom"):
if !setting.Other.EnableFeed {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
if reloadParam(".atom") {
@@ -775,10 +768,19 @@ func UsernameSubRoute(ctx *context.Context) {
}
}
-func getUserIssueStats(ctx *context.Context, filterMode int, opts *issue_indexer.SearchOptions) (ret *issues_model.IssueStats, err error) {
+func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMode int, opts *issue_indexer.SearchOptions) (ret *issues_model.IssueStats, err error) {
ret = &issues_model.IssueStats{}
doerID := ctx.Doer.ID
+ opts = opts.Copy(func(o *issue_indexer.SearchOptions) {
+ // If the doer is the same as the context user, which means the doer is viewing his own dashboard,
+ // it's not enough to show the repos that the doer owns or has been explicitly granted access to,
+ // because the doer may create issues or be mentioned in any public repo.
+ // So we need search issues in all public repos.
+ o.AllPublic = doerID == ctxUser.ID
+ })
+
+ // Open/Closed are for the tabs of the issue list
{
openClosedOpts := opts.Copy()
switch filterMode {
@@ -787,9 +789,9 @@ func getUserIssueStats(ctx *context.Context, filterMode int, opts *issue_indexer
case issues_model.FilterModeYourRepositories:
openClosedOpts.AllPublic = false
case issues_model.FilterModeAssign:
- openClosedOpts.AssigneeID = optional.Some(doerID)
+ openClosedOpts.AssigneeID = strconv.FormatInt(doerID, 10)
case issues_model.FilterModeCreate:
- openClosedOpts.PosterID = optional.Some(doerID)
+ openClosedOpts.PosterID = strconv.FormatInt(doerID, 10)
case issues_model.FilterModeMention:
openClosedOpts.MentionID = optional.Some(doerID)
case issues_model.FilterModeReviewRequested:
@@ -809,15 +811,24 @@ func getUserIssueStats(ctx *context.Context, filterMode int, opts *issue_indexer
}
}
+ // Below stats are for the left sidebar
+ opts = opts.Copy(func(o *issue_indexer.SearchOptions) {
+ o.AssigneeID = ""
+ o.PosterID = ""
+ o.MentionID = nil
+ o.ReviewRequestedID = nil
+ o.ReviewedID = nil
+ })
+
ret.YourRepositoriesCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AllPublic = false }))
if err != nil {
return nil, err
}
- ret.AssignCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AssigneeID = optional.Some(doerID) }))
+ ret.AssignCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AssigneeID = strconv.FormatInt(doerID, 10) }))
if err != nil {
return nil, err
}
- ret.CreateCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.PosterID = optional.Some(doerID) }))
+ ret.CreateCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.PosterID = strconv.FormatInt(doerID, 10) }))
if err != nil {
return nil, err
}
diff --git a/routers/web/user/home_test.go b/routers/web/user/home_test.go
index 51246551ea..68ad79b11e 100644
--- a/routers/web/user/home_test.go
+++ b/routers/web/user/home_test.go
@@ -28,7 +28,7 @@ func TestArchivedIssues(t *testing.T) {
ctx.Req.Form.Set("state", "open")
// Assume: User 30 has access to two Repos with Issues, one of the Repos being archived.
- repos, _, _ := repo_model.GetUserRepositories(db.DefaultContext, &repo_model.SearchRepoOptions{Actor: ctx.Doer})
+ repos, _, _ := repo_model.GetUserRepositories(db.DefaultContext, repo_model.SearchRepoOptions{Actor: ctx.Doer})
assert.Len(t, repos, 3)
IsArchived := make(map[int64]bool)
NumIssues := make(map[int64]int)
@@ -37,15 +37,15 @@ func TestArchivedIssues(t *testing.T) {
NumIssues[repo.ID] = repo.NumIssues
}
assert.False(t, IsArchived[50])
- assert.EqualValues(t, 1, NumIssues[50])
+ assert.Equal(t, 1, NumIssues[50])
assert.True(t, IsArchived[51])
- assert.EqualValues(t, 1, NumIssues[51])
+ assert.Equal(t, 1, NumIssues[51])
// Act
Issues(ctx)
// Assert: One Issue (ID 30) from one Repo (ID 50) is retrieved, while nothing from archived Repo 51 is retrieved
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
assert.Len(t, ctx.Data["Issues"], 1)
}
@@ -58,7 +58,7 @@ func TestIssues(t *testing.T) {
contexttest.LoadUser(t, ctx, 2)
ctx.Req.Form.Set("state", "closed")
Issues(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
assert.EqualValues(t, true, ctx.Data["IsShowClosed"])
assert.Len(t, ctx.Data["Issues"], 1)
@@ -72,7 +72,7 @@ func TestPulls(t *testing.T) {
contexttest.LoadUser(t, ctx, 2)
ctx.Req.Form.Set("state", "open")
Pulls(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
assert.Len(t, ctx.Data["Issues"], 5)
}
@@ -87,7 +87,7 @@ func TestMilestones(t *testing.T) {
ctx.Req.Form.Set("state", "closed")
ctx.Req.Form.Set("sort", "furthestduedate")
Milestones(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
assert.EqualValues(t, map[int64]int64{1: 1}, ctx.Data["Counts"])
assert.EqualValues(t, true, ctx.Data["IsShowClosed"])
assert.EqualValues(t, "furthestduedate", ctx.Data["SortType"])
@@ -107,7 +107,7 @@ func TestMilestonesForSpecificRepo(t *testing.T) {
ctx.Req.Form.Set("state", "closed")
ctx.Req.Form.Set("sort", "furthestduedate")
Milestones(ctx)
- assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+ assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
assert.EqualValues(t, map[int64]int64{1: 1}, ctx.Data["Counts"])
assert.EqualValues(t, true, ctx.Data["IsShowClosed"])
assert.EqualValues(t, "furthestduedate", ctx.Data["SortType"])
diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go
index 1c91ff6364..610a9b8076 100644
--- a/routers/web/user/notification.go
+++ b/routers/web/user/notification.go
@@ -4,7 +4,6 @@
package user
import (
- goctx "context"
"errors"
"fmt"
"net/http"
@@ -35,32 +34,6 @@ const (
tplNotificationSubscriptions templates.TplName = "user/notification/notification_subscriptions"
)
-// GetNotificationCount is the middleware that sets the notification count in the context
-func GetNotificationCount(ctx *context.Context) {
- if strings.HasPrefix(ctx.Req.URL.Path, "/api") {
- return
- }
-
- if !ctx.IsSigned {
- return
- }
-
- ctx.Data["NotificationUnreadCount"] = func() int64 {
- count, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{
- UserID: ctx.Doer.ID,
- Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread},
- })
- if err != nil {
- if err != goctx.Canceled {
- log.Error("Unable to GetNotificationCount for user:%-v: %v", ctx.Doer, err)
- }
- return -1
- }
-
- return count
- }
-}
-
// Notifications is the notifications page
func Notifications(ctx *context.Context) {
getNotifications(ctx)
@@ -230,10 +203,7 @@ func NotificationPurgePost(ctx *context.Context) {
// NotificationSubscriptions returns the list of subscribed issues
func NotificationSubscriptions(ctx *context.Context) {
- page := ctx.FormInt("page")
- if page < 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
sortType := ctx.FormString("sort")
ctx.Data["SortType"] = sortType
@@ -314,16 +284,8 @@ func NotificationSubscriptions(ctx *context.Context) {
ctx.Data["CommitLastStatus"] = lastStatus
ctx.Data["CommitStatuses"] = commitStatuses
ctx.Data["Issues"] = issues
-
ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] = issue_service.GetRefEndNamesAndURLs(issues, "")
- commitStatus, err := pull_service.GetIssuesLastCommitStatus(ctx, issues)
- if err != nil {
- ctx.ServerError("GetIssuesLastCommitStatus", err)
- return
- }
- ctx.Data["CommitStatus"] = commitStatus
-
approvalCounts, err := issues.GetApprovalCounts(ctx)
if err != nil {
ctx.ServerError("ApprovalCounts", err)
@@ -335,9 +297,10 @@ func NotificationSubscriptions(ctx *context.Context) {
return 0
}
reviewTyp := issues_model.ReviewTypeApprove
- if typ == "reject" {
+ switch typ {
+ case "reject":
reviewTyp = issues_model.ReviewTypeReject
- } else if typ == "waiting" {
+ case "waiting":
reviewTyp = issues_model.ReviewTypeRequest
}
for _, count := range counts {
@@ -365,10 +328,7 @@ func NotificationSubscriptions(ctx *context.Context) {
// NotificationWatching returns the list of watching repos
func NotificationWatching(ctx *context.Context) {
- page := ctx.FormInt("page")
- if page < 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
keyword := ctx.FormTrim("q")
ctx.Data["Keyword"] = keyword
@@ -416,7 +376,7 @@ func NotificationWatching(ctx *context.Context) {
private := ctx.FormOptionalBool("private")
ctx.Data["IsPrivate"] = private
- repos, count, err := repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
+ repos, count, err := repo_model.SearchRepository(ctx, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: setting.UI.User.RepoPagingNum,
Page: page,
diff --git a/routers/web/user/package.go b/routers/web/user/package.go
index 1f75faf1c6..216acdf927 100644
--- a/routers/web/user/package.go
+++ b/routers/web/user/package.go
@@ -4,6 +4,8 @@
package user
import (
+ gocontext "context"
+ "errors"
"net/http"
"net/url"
@@ -20,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/optional"
alpine_module "code.gitea.io/gitea/modules/packages/alpine"
arch_module "code.gitea.io/gitea/modules/packages/arch"
+ container_module "code.gitea.io/gitea/modules/packages/container"
debian_module "code.gitea.io/gitea/modules/packages/debian"
rpm_module "code.gitea.io/gitea/modules/packages/rpm"
"code.gitea.io/gitea/modules/setting"
@@ -31,6 +34,7 @@ import (
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
packages_service "code.gitea.io/gitea/services/packages"
+ container_service "code.gitea.io/gitea/services/packages/container"
)
const (
@@ -42,11 +46,11 @@ const (
// ListPackages displays a list of all packages of the context user
func ListPackages(ctx *context.Context) {
- shared_user.PrepareContextForProfileBigAvatar(ctx)
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
}
+ page := max(ctx.FormInt("page"), 1)
query := ctx.FormTrim("q")
packageType := ctx.FormTrim("type")
@@ -94,8 +98,6 @@ func ListPackages(ctx *context.Context) {
return
}
- shared_user.RenderUserHeader(ctx)
-
ctx.Data["Title"] = ctx.Tr("packages.title")
ctx.Data["IsPackagesPage"] = true
ctx.Data["Query"] = query
@@ -106,9 +108,8 @@ func ListPackages(ctx *context.Context) {
ctx.Data["Total"] = total
ctx.Data["RepositoryAccessMap"] = repositoryAccessMap
- err = shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
@@ -126,11 +127,9 @@ func ListPackages(ctx *context.Context) {
ctx.Data["IsOrganizationOwner"] = false
}
}
-
pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager
-
ctx.HTML(http.StatusOK, tplPackagesList)
}
@@ -139,7 +138,7 @@ func RedirectToLastVersion(ctx *context.Context) {
p, err := packages_model.GetPackageByName(ctx, ctx.Package.Owner.ID, packages_model.Type(ctx.PathParam("type")), ctx.PathParam("name"))
if err != nil {
if err == packages_model.ErrPackageNotExist {
- ctx.NotFound("GetPackageByName", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("GetPackageByName", err)
}
@@ -155,7 +154,7 @@ func RedirectToLastVersion(ctx *context.Context) {
return
}
if len(pvs) == 0 {
- ctx.NotFound("", err)
+ ctx.NotFound(err)
return
}
@@ -164,16 +163,36 @@ func RedirectToLastVersion(ctx *context.Context) {
ctx.ServerError("GetPackageDescriptor", err)
return
}
-
ctx.Redirect(pd.VersionWebLink())
}
+func viewPackageContainerImage(ctx gocontext.Context, pd *packages_model.PackageDescriptor, digest string) (*container_module.Metadata, error) {
+ manifestBlob, err := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
+ OwnerID: pd.Owner.ID,
+ Image: pd.Package.LowerName,
+ Digest: digest,
+ })
+ if err != nil {
+ return nil, err
+ }
+ manifestReader, err := packages_service.OpenBlobStream(manifestBlob.Blob)
+ if err != nil {
+ return nil, err
+ }
+ defer manifestReader.Close()
+ _, _, metadata, err := container_service.ParseManifestMetadata(ctx, manifestReader, pd.Owner.ID, pd.Package.LowerName)
+ return metadata, err
+}
+
// ViewPackageVersion displays a single package version
func ViewPackageVersion(ctx *context.Context) {
- pd := ctx.Package.Descriptor
-
- shared_user.RenderUserHeader(ctx)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
+ versionSub := ctx.PathParam("version_sub")
+ pd := ctx.Package.Descriptor
ctx.Data["Title"] = pd.Package.Name
ctx.Data["IsPackagesPage"] = true
ctx.Data["PackageDescriptor"] = pd
@@ -261,21 +280,30 @@ func ViewPackageVersion(ctx *context.Context) {
ctx.Data["Groups"] = util.Sorted(groups.Values())
ctx.Data["Architectures"] = util.Sorted(architectures.Values())
- }
-
- var (
- total int64
- pvs []*packages_model.PackageVersion
- )
- switch pd.Package.Type {
case packages_model.TypeContainer:
- pvs, total, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
+ imageMetadata := pd.Metadata
+ if versionSub != "" {
+ imageMetadata, err = viewPackageContainerImage(ctx, pd, versionSub)
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.NotFound(nil)
+ return
+ } else if err != nil {
+ ctx.ServerError("viewPackageContainerImage", err)
+ return
+ }
+ }
+ ctx.Data["ContainerImageMetadata"] = imageMetadata
+ }
+ var pvs []*packages_model.PackageVersion
+ var pvsTotal int64
+ if pd.Package.Type == packages_model.TypeContainer {
+ pvs, pvsTotal, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
Paginator: db.NewAbsoluteListOptions(0, 5),
PackageID: pd.Package.ID,
IsTagged: true,
})
- default:
- pvs, total, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
+ } else {
+ pvs, pvsTotal, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
Paginator: db.NewAbsoluteListOptions(0, 5),
PackageID: pd.Package.ID,
IsInternal: optional.Some(false),
@@ -285,9 +313,8 @@ func ViewPackageVersion(ctx *context.Context) {
ctx.ServerError("", err)
return
}
-
ctx.Data["LatestVersions"] = pvs
- ctx.Data["TotalVersionCount"] = total
+ ctx.Data["TotalVersionCount"] = pvsTotal
ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin()
@@ -301,33 +328,27 @@ func ViewPackageVersion(ctx *context.Context) {
hasRepositoryAccess = permission.HasAnyUnitAccess()
}
ctx.Data["HasRepositoryAccess"] = hasRepositoryAccess
-
- err = shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
- return
- }
-
ctx.HTML(http.StatusOK, tplPackagesView)
}
// ListPackageVersions lists all versions of a package
func ListPackageVersions(ctx *context.Context) {
- shared_user.PrepareContextForProfileBigAvatar(ctx)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
+
p, err := packages_model.GetPackageByName(ctx, ctx.Package.Owner.ID, packages_model.Type(ctx.PathParam("type")), ctx.PathParam("name"))
if err != nil {
if err == packages_model.ErrPackageNotExist {
- ctx.NotFound("GetPackageByName", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("GetPackageByName", err)
}
return
}
- page := ctx.FormInt("page")
- if page <= 1 {
- page = 1
- }
+ page := max(ctx.FormInt("page"), 1)
pagination := &db.ListOptions{
PageSize: setting.UI.PackagesPagingNum,
Page: page,
@@ -336,8 +357,6 @@ func ListPackageVersions(ctx *context.Context) {
query := ctx.FormTrim("q")
sort := ctx.FormTrim("sort")
- shared_user.RenderUserHeader(ctx)
-
ctx.Data["Title"] = ctx.Tr("packages.title")
ctx.Data["IsPackagesPage"] = true
ctx.Data["PackageDescriptor"] = &packages_model.PackageDescriptor{
@@ -393,12 +412,6 @@ func ListPackageVersions(ctx *context.Context) {
ctx.Data["Total"] = total
- err = shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
- return
- }
-
pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager
@@ -410,25 +423,22 @@ func ListPackageVersions(ctx *context.Context) {
func PackageSettings(ctx *context.Context) {
pd := ctx.Package.Descriptor
- shared_user.RenderUserHeader(ctx)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
ctx.Data["Title"] = pd.Package.Name
ctx.Data["IsPackagesPage"] = true
ctx.Data["PackageDescriptor"] = pd
- repos, _, _ := repo_model.GetUserRepositories(ctx, &repo_model.SearchRepoOptions{
+ repos, _, _ := repo_model.GetUserRepositories(ctx, repo_model.SearchRepoOptions{
Actor: pd.Owner,
Private: true,
})
ctx.Data["Repos"] = repos
ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin()
- err := shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
- return
- }
-
ctx.HTML(http.StatusOK, tplPackagesSettings)
}
@@ -496,16 +506,16 @@ func DownloadPackageFile(ctx *context.Context) {
pf, err := packages_model.GetFileForVersionByID(ctx, ctx.Package.Descriptor.Version.ID, ctx.PathParamInt64("fileid"))
if err != nil {
if err == packages_model.ErrPackageFileNotExist {
- ctx.NotFound("", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("GetFileForVersionByID", err)
}
return
}
- s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
+ s, u, _, err := packages_service.OpenFileForDownload(ctx, pf)
if err != nil {
- ctx.ServerError("GetPackageFileStream", err)
+ ctx.ServerError("OpenFileForDownload", err)
return
}
diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index 006ffdcf7e..d7052914b6 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -56,34 +56,29 @@ func OwnerProfile(ctx *context.Context) {
func userProfile(ctx *context.Context) {
// check view permissions
if !user_model.IsUserVisibleToViewer(ctx, ctx.ContextUser, ctx.Doer) {
- ctx.NotFound("user", fmt.Errorf("%s", ctx.ContextUser.Name))
+ ctx.NotFound(fmt.Errorf("%s", ctx.ContextUser.Name))
return
}
ctx.Data["Title"] = ctx.ContextUser.DisplayName()
ctx.Data["PageIsUserProfile"] = true
- // prepare heatmap data
- if setting.Service.EnableUserHeatmap {
- data, err := activities_model.GetUserHeatmapDataByUser(ctx, ctx.ContextUser, ctx.Doer)
- if err != nil {
- ctx.ServerError("GetUserHeatmapDataByUser", err)
- return
- }
- ctx.Data["HeatmapData"] = data
- ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data)
- }
-
profileDbRepo, profileReadmeBlob := shared_user.FindOwnerProfileReadme(ctx, ctx.Doer)
- showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
- prepareUserProfileTabData(ctx, showPrivate, profileDbRepo, profileReadmeBlob)
- // call PrepareContextForProfileBigAvatar later to avoid re-querying the NumFollowers & NumFollowing
- shared_user.PrepareContextForProfileBigAvatar(ctx)
+ prepareUserProfileTabData(ctx, profileDbRepo, profileReadmeBlob)
+
+ // prepare the user nav header data after "prepareUserProfileTabData" to avoid re-querying the NumFollowers & NumFollowing
+ // because ctx.Data["NumFollowers"] and "NumFollowing" logic duplicates in both of them
+ // and the "profile readme" related logic also duplicates in both of FindOwnerProfileReadme and RenderUserOrgHeader
+ // TODO: it is a bad design and should be refactored later,
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
+ return
+ }
ctx.HTML(http.StatusOK, tplProfile)
}
-func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDbRepo *repo_model.Repository, profileReadme *git.Blob) {
+func prepareUserProfileTabData(ctx *context.Context, profileDbRepo *repo_model.Repository, profileReadme *git.Blob) {
// if there is a profile readme, default to "overview" page, otherwise, default to "repositories" page
// if there is not a profile readme, the overview tab should be treated as the repositories tab
tab := ctx.FormString("tab")
@@ -166,8 +161,20 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
ctx.Data["Cards"] = following
total = int(numFollowing)
case "activity":
+ // prepare heatmap data
+ if setting.Service.EnableUserHeatmap {
+ data, err := activities_model.GetUserHeatmapDataByUser(ctx, ctx.ContextUser, ctx.Doer)
+ if err != nil {
+ ctx.ServerError("GetUserHeatmapDataByUser", err)
+ return
+ }
+ ctx.Data["HeatmapData"] = data
+ ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data)
+ }
+
date := ctx.FormString("date")
pagingNum = setting.UI.FeedPagingNum
+ showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
items, count, err := feed_service.GetFeeds(ctx, activities_model.GetFeedsOptions{
RequestedUser: ctx.ContextUser,
Actor: ctx.Doer,
@@ -190,7 +197,8 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
total = int(count)
case "stars":
ctx.Data["PageIsProfileStarList"] = true
- repos, count, err = repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
+ ctx.Data["ShowRepoOwnerOnList"] = true
+ repos, count, err = repo_model.SearchRepository(ctx, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: pagingNum,
Page: page,
@@ -217,7 +225,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
total = int(count)
case "watching":
- repos, count, err = repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepository(ctx, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: pagingNum,
Page: page,
@@ -258,8 +266,8 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
}
case "organizations":
orgs, count, err := db.FindAndCount[organization.Organization](ctx, organization.FindOrgOptions{
- UserID: ctx.ContextUser.ID,
- IncludePrivate: showPrivate,
+ UserID: ctx.ContextUser.ID,
+ IncludeVisibility: organization.DoerViewOtherVisibility(ctx.Doer, ctx.ContextUser),
ListOptions: db.ListOptions{
Page: page,
PageSize: pagingNum,
@@ -272,7 +280,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
ctx.Data["Cards"] = orgs
total = int(count)
default: // default to "repositories"
- repos, count, err = repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepository(ctx, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: pagingNum,
Page: page,
@@ -302,9 +310,8 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
ctx.Data["Repos"] = repos
ctx.Data["Total"] = total
- err = shared_user.LoadHeaderCount(ctx)
- if err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
@@ -313,8 +320,8 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
ctx.Data["Page"] = pager
}
-// Action response for follow/unfollow user request
-func Action(ctx *context.Context) {
+// ActionUserFollow is for follow/unfollow user request
+func ActionUserFollow(ctx *context.Context) {
var err error
switch ctx.FormString("action") {
case "follow":
@@ -325,12 +332,14 @@ func Action(ctx *context.Context) {
if err != nil {
log.Error("Failed to apply action %q: %v", ctx.FormString("action"), err)
- ctx.Error(http.StatusBadRequest, fmt.Sprintf("Action %q failed", ctx.FormString("action")))
+ ctx.HTTPError(http.StatusBadRequest, fmt.Sprintf("Action %q failed", ctx.FormString("action")))
+ return
+ }
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
-
if ctx.ContextUser.IsIndividual() {
- shared_user.PrepareContextForProfileBigAvatar(ctx)
ctx.HTML(http.StatusOK, tplProfileBigAvatar)
return
} else if ctx.ContextUser.IsOrganization() {
@@ -339,6 +348,6 @@ func Action(ctx *context.Context) {
ctx.HTML(http.StatusOK, tplFollowUnfollow)
return
}
- log.Error("Failed to apply action %q: unsupport context user type: %s", ctx.FormString("action"), ctx.ContextUser.Type)
- ctx.Error(http.StatusBadRequest, fmt.Sprintf("Action %q failed", ctx.FormString("action")))
+ log.Error("Failed to apply action %q: unsupported context user type: %s", ctx.FormString("action"), ctx.ContextUser.Type)
+ ctx.HTTPError(http.StatusBadRequest, fmt.Sprintf("Action %q failed", ctx.FormString("action")))
}
diff --git a/routers/web/user/search.go b/routers/web/user/search.go
index be5eee90a9..9acb9694d7 100644
--- a/routers/web/user/search.go
+++ b/routers/web/user/search.go
@@ -16,7 +16,7 @@ import (
// SearchCandidates searches candidate users for dropdown list
func SearchCandidates(ctx *context.Context) {
- users, _, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
+ users, _, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer,
Keyword: ctx.FormTrim("q"),
Type: user_model.UserTypeIndividual,
diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go
index 3acc3c7a54..b124d5e1de 100644
--- a/routers/web/user/setting/account.go
+++ b/routers/web/user/setting/account.go
@@ -6,7 +6,6 @@ package setting
import (
"errors"
- "fmt"
"net/http"
"time"
@@ -37,7 +36,7 @@ const (
// Account renders change user's password, user's email and user suicide page
func Account(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageCredentials, setting.UserFeatureDeletion) && !setting.Service.EnableNotifyMail {
- ctx.NotFound("Not Found", fmt.Errorf("account setting are not allowed to be changed"))
+ ctx.NotFound(errors.New("account setting are not allowed to be changed"))
return
}
@@ -54,7 +53,7 @@ func Account(ctx *context.Context) {
// AccountPost response for change user's password
func AccountPost(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageCredentials) {
- ctx.NotFound("Not Found", fmt.Errorf("password setting is not allowed to be changed"))
+ ctx.NotFound(errors.New("password setting is not allowed to be changed"))
return
}
@@ -105,7 +104,7 @@ func AccountPost(ctx *context.Context) {
// EmailPost response for change user's email
func EmailPost(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageCredentials) {
- ctx.NotFound("Not Found", fmt.Errorf("emails are not allowed to be changed"))
+ ctx.NotFound(errors.New("emails are not allowed to be changed"))
return
}
@@ -239,7 +238,7 @@ func EmailPost(ctx *context.Context) {
// DeleteEmail response for delete user's email
func DeleteEmail(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageCredentials) {
- ctx.NotFound("Not Found", fmt.Errorf("emails are not allowed to be changed"))
+ ctx.NotFound(errors.New("emails are not allowed to be changed"))
return
}
email, err := user_model.GetEmailAddressByID(ctx, ctx.Doer.ID, ctx.FormInt64("id"))
@@ -261,7 +260,7 @@ func DeleteEmail(ctx *context.Context) {
// DeleteAccount render user suicide page and response for delete user himself
func DeleteAccount(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureDeletion) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
diff --git a/routers/web/user/setting/account_test.go b/routers/web/user/setting/account_test.go
index 9fdc5e4d53..9b8cffc868 100644
--- a/routers/web/user/setting/account_test.go
+++ b/routers/web/user/setting/account_test.go
@@ -95,7 +95,7 @@ func TestChangePassword(t *testing.T) {
AccountPost(ctx)
assert.Contains(t, ctx.Flash.ErrorMsg, req.Message)
- assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
+ assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
})
}
}
diff --git a/routers/web/user/setting/applications.go b/routers/web/user/setting/applications.go
index cf71d01dc1..9c43ddd3ea 100644
--- a/routers/web/user/setting/applications.go
+++ b/routers/web/user/setting/applications.go
@@ -6,12 +6,14 @@ package setting
import (
"net/http"
+ "strings"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
@@ -39,18 +41,30 @@ func ApplicationsPost(ctx *context.Context) {
ctx.Data["PageIsSettingsApplications"] = true
ctx.Data["UserDisabledFeatures"] = user_model.DisabledFeaturesWithLoginType(ctx.Doer)
- if ctx.HasError() {
- loadApplicationsData(ctx)
-
- ctx.HTML(http.StatusOK, tplSettingsApplications)
- return
+ _ = ctx.Req.ParseForm()
+ var scopeNames []string
+ const accessTokenScopePrefix = "scope-"
+ for k, v := range ctx.Req.Form {
+ if strings.HasPrefix(k, accessTokenScopePrefix) {
+ scopeNames = append(scopeNames, v...)
+ }
}
- scope, err := form.GetScope()
+ scope, err := auth_model.AccessTokenScope(strings.Join(scopeNames, ",")).Normalize()
if err != nil {
ctx.ServerError("GetScope", err)
return
}
+ if !scope.HasPermissionScope() {
+ ctx.Flash.Error(ctx.Tr("settings.at_least_one_permission"), true)
+ }
+
+ if ctx.HasError() {
+ loadApplicationsData(ctx)
+ ctx.HTML(http.StatusOK, tplSettingsApplications)
+ return
+ }
+
t := &auth_model.AccessToken{
UID: ctx.Doer.ID,
Name: form.Name,
@@ -99,7 +113,14 @@ func loadApplicationsData(ctx *context.Context) {
}
ctx.Data["Tokens"] = tokens
ctx.Data["EnableOAuth2"] = setting.OAuth2.Enabled
- ctx.Data["IsAdmin"] = ctx.Doer.IsAdmin
+
+ // Handle specific ordered token categories for admin or non-admin users
+ tokenCategoryNames := auth_model.GetAccessTokenCategories()
+ if !ctx.Doer.IsAdmin {
+ tokenCategoryNames = util.SliceRemoveAll(tokenCategoryNames, "admin")
+ }
+ ctx.Data["TokenCategories"] = tokenCategoryNames
+
if setting.OAuth2.Enabled {
ctx.Data["Applications"], err = db.Find[auth_model.OAuth2Application](ctx, auth_model.FindOAuth2ApplicationsOptions{
OwnerID: ctx.Doer.ID,
diff --git a/routers/web/user/setting/keys.go b/routers/web/user/setting/keys.go
index 127aed9845..6b5a7a2e2a 100644
--- a/routers/web/user/setting/keys.go
+++ b/routers/web/user/setting/keys.go
@@ -5,7 +5,7 @@
package setting
import (
- "fmt"
+ "errors"
"net/http"
asymkey_model "code.gitea.io/gitea/models/asymkey"
@@ -26,7 +26,7 @@ const (
// Keys render user's SSH/GPG public keys page
func Keys(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys, setting.UserFeatureManageGPGKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("keys setting is not allowed to be changed"))
+ ctx.NotFound(errors.New("keys setting is not allowed to be changed"))
return
}
@@ -87,7 +87,7 @@ func KeysPost(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
case "gpg":
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited"))
+ ctx.NotFound(errors.New("gpg keys setting is not allowed to be visited"))
return
}
@@ -168,7 +168,7 @@ func KeysPost(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
case "ssh":
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited"))
+ ctx.NotFound(errors.New("ssh keys setting is not allowed to be visited"))
return
}
@@ -212,7 +212,7 @@ func KeysPost(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
case "verify_ssh":
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited"))
+ ctx.NotFound(errors.New("ssh keys setting is not allowed to be visited"))
return
}
@@ -249,7 +249,7 @@ func DeleteKey(ctx *context.Context) {
switch ctx.FormString("type") {
case "gpg":
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited"))
+ ctx.NotFound(errors.New("gpg keys setting is not allowed to be visited"))
return
}
if err := asymkey_model.DeleteGPGKey(ctx, ctx.Doer, ctx.FormInt64("id")); err != nil {
@@ -259,7 +259,7 @@ func DeleteKey(ctx *context.Context) {
}
case "ssh":
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) {
- ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited"))
+ ctx.NotFound(errors.New("ssh keys setting is not allowed to be visited"))
return
}
diff --git a/routers/web/user/setting/oauth2_common.go b/routers/web/user/setting/oauth2_common.go
index 783deca710..f460acce10 100644
--- a/routers/web/user/setting/oauth2_common.go
+++ b/routers/web/user/setting/oauth2_common.go
@@ -28,8 +28,8 @@ func (oa *OAuth2CommonHandlers) renderEditPage(ctx *context.Context) {
ctx.Data["FormActionPath"] = fmt.Sprintf("%s/%d", oa.BasePathEditPrefix, app.ID)
if ctx.ContextUser != nil && ctx.ContextUser.IsOrganization() {
- if err := shared_user.LoadHeaderCount(ctx); err != nil {
- ctx.ServerError("LoadHeaderCount", err)
+ if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
+ ctx.ServerError("RenderUserOrgHeader", err)
return
}
}
@@ -76,14 +76,14 @@ func (oa *OAuth2CommonHandlers) EditShow(ctx *context.Context) {
app, err := auth.GetOAuth2ApplicationByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if auth.IsErrOAuthApplicationNotFound(err) {
- ctx.NotFound("Application not found", err)
+ ctx.NotFound(err)
return
}
ctx.ServerError("GetOAuth2ApplicationByID", err)
return
}
if app.UID != oa.OwnerID {
- ctx.NotFound("Application not found", nil)
+ ctx.NotFound(nil)
return
}
ctx.Data["App"] = app
@@ -98,14 +98,14 @@ func (oa *OAuth2CommonHandlers) EditSave(ctx *context.Context) {
app, err := auth.GetOAuth2ApplicationByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if auth.IsErrOAuthApplicationNotFound(err) {
- ctx.NotFound("Application not found", err)
+ ctx.NotFound(err)
return
}
ctx.ServerError("GetOAuth2ApplicationByID", err)
return
}
if app.UID != oa.OwnerID {
- ctx.NotFound("Application not found", nil)
+ ctx.NotFound(nil)
return
}
ctx.Data["App"] = app
@@ -135,14 +135,14 @@ func (oa *OAuth2CommonHandlers) RegenerateSecret(ctx *context.Context) {
app, err := auth.GetOAuth2ApplicationByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if auth.IsErrOAuthApplicationNotFound(err) {
- ctx.NotFound("Application not found", err)
+ ctx.NotFound(err)
return
}
ctx.ServerError("GetOAuth2ApplicationByID", err)
return
}
if app.UID != oa.OwnerID {
- ctx.NotFound("Application not found", nil)
+ ctx.NotFound(nil)
return
}
ctx.Data["App"] = app
diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go
index ebf682bf58..98995cd69c 100644
--- a/routers/web/user/setting/profile.go
+++ b/routers/web/user/setting/profile.go
@@ -22,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/typesniffer"
@@ -206,8 +207,8 @@ func Organization(ctx *context.Context) {
PageSize: setting.UI.Admin.UserPagingNum,
Page: ctx.FormInt("page"),
},
- UserID: ctx.Doer.ID,
- IncludePrivate: ctx.IsSigned,
+ UserID: ctx.Doer.ID,
+ IncludeVisibility: structs.VisibleTypePrivate,
}
if opts.Page <= 0 {
@@ -284,7 +285,7 @@ func Repos(ctx *context.Context) {
return
}
- userRepos, _, err := repo_model.GetUserRepositories(ctx, &repo_model.SearchRepoOptions{
+ userRepos, _, err := repo_model.GetUserRepositories(ctx, repo_model.SearchRepoOptions{
Actor: ctxUser,
Private: true,
ListOptions: db.ListOptions{
@@ -309,7 +310,7 @@ func Repos(ctx *context.Context) {
ctx.Data["Dirs"] = repoNames
ctx.Data["ReposMap"] = repos
} else {
- repos, count64, err := repo_model.GetUserRepositories(ctx, &repo_model.SearchRepoOptions{Actor: ctxUser, Private: true, ListOptions: opts})
+ repos, count64, err := repo_model.GetUserRepositories(ctx, repo_model.SearchRepoOptions{Actor: ctxUser, Private: true, ListOptions: opts})
if err != nil {
ctx.ServerError("GetUserRepositories", err)
return
@@ -338,13 +339,7 @@ func Repos(ctx *context.Context) {
func Appearance(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings.appearance")
ctx.Data["PageIsSettingsAppearance"] = true
-
- allThemes := webtheme.GetAvailableThemes()
- if webtheme.IsThemeAvailable(setting.UI.DefaultTheme) {
- allThemes = util.SliceRemoveAll(allThemes, setting.UI.DefaultTheme)
- allThemes = append([]string{setting.UI.DefaultTheme}, allThemes...) // move the default theme to the top
- }
- ctx.Data["AllThemes"] = allThemes
+ ctx.Data["AllThemes"] = webtheme.GetAvailableThemes()
ctx.Data["UserDisabledFeatures"] = user_model.DisabledFeaturesWithLoginType(ctx.Doer)
var hiddenCommentTypes *big.Int
diff --git a/routers/web/user/setting/security/2fa.go b/routers/web/user/setting/security/2fa.go
index 7bb10248e8..e5e23c820c 100644
--- a/routers/web/user/setting/security/2fa.go
+++ b/routers/web/user/setting/security/2fa.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/session"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/context"
@@ -27,7 +28,7 @@ import (
// RegenerateScratchTwoFactor regenerates the user's 2FA scratch code.
func RegenerateScratchTwoFactor(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageMFA) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
@@ -63,7 +64,7 @@ func RegenerateScratchTwoFactor(ctx *context.Context) {
// DisableTwoFactor deletes the user's 2FA settings.
func DisableTwoFactor(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageMFA) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
@@ -157,12 +158,13 @@ func twofaGenerateSecretAndQr(ctx *context.Context) bool {
// EnrollTwoFactor shows the page where the user can enroll into 2FA.
func EnrollTwoFactor(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageMFA) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsSecurity"] = true
+ ctx.Data["ShowTwoFactorRequiredMessage"] = false
t, err := auth.GetTwoFactorByUID(ctx, ctx.Doer.ID)
if t != nil {
@@ -187,13 +189,14 @@ func EnrollTwoFactor(ctx *context.Context) {
// EnrollTwoFactorPost handles enrolling the user into 2FA.
func EnrollTwoFactorPost(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageMFA) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
form := web.GetForm(ctx).(*forms.TwoFactorAuthForm)
ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsSecurity"] = true
+ ctx.Data["ShowTwoFactorRequiredMessage"] = false
t, err := auth.GetTwoFactorByUID(ctx, ctx.Doer.ID)
if t != nil {
@@ -246,6 +249,10 @@ func EnrollTwoFactorPost(ctx *context.Context) {
return
}
+ newTwoFactorErr := auth.NewTwoFactor(ctx, t)
+ if newTwoFactorErr == nil {
+ _ = ctx.Session.Set(session.KeyUserHasTwoFactorAuth, true)
+ }
// Now we have to delete the secrets - because if we fail to insert then it's highly likely that they have already been used
// If we can detect the unique constraint failure below we can move this to after the NewTwoFactor
if err := ctx.Session.Delete("twofaSecret"); err != nil {
@@ -261,10 +268,10 @@ func EnrollTwoFactorPost(ctx *context.Context) {
log.Error("Unable to save changes to the session: %v", err)
}
- if err = auth.NewTwoFactor(ctx, t); err != nil {
+ if newTwoFactorErr != nil {
// FIXME: We need to handle a unique constraint fail here it's entirely possible that another request has beaten us.
// If there is a unique constraint fail we should just tolerate the error
- ctx.ServerError("SettingsTwoFactor: Failed to save two factor", err)
+ ctx.ServerError("SettingsTwoFactor: Failed to save two factor", newTwoFactorErr)
return
}
diff --git a/routers/web/user/setting/security/openid.go b/routers/web/user/setting/security/openid.go
index 30eb6f63f8..9a57657912 100644
--- a/routers/web/user/setting/security/openid.go
+++ b/routers/web/user/setting/security/openid.go
@@ -18,7 +18,7 @@ import (
// OpenIDPost response for change user's openid
func OpenIDPost(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageCredentials) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
@@ -111,7 +111,7 @@ func settingsOpenIDVerify(ctx *context.Context) {
// DeleteOpenID response for delete user's openid
func DeleteOpenID(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageCredentials) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
@@ -128,7 +128,7 @@ func DeleteOpenID(ctx *context.Context) {
// ToggleOpenIDVisibility response for toggle visibility of user's openid
func ToggleOpenIDVisibility(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageCredentials) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
diff --git a/routers/web/user/setting/security/security.go b/routers/web/user/setting/security/security.go
index 38d1910e2d..cc4c44993a 100644
--- a/routers/web/user/setting/security/security.go
+++ b/routers/web/user/setting/security/security.go
@@ -27,7 +27,7 @@ const (
func Security(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer,
setting.UserFeatureManageMFA, setting.UserFeatureManageCredentials) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
@@ -47,7 +47,7 @@ func Security(ctx *context.Context) {
// DeleteAccountLink delete a single account link
func DeleteAccountLink(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageCredentials) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
diff --git a/routers/web/user/setting/security/webauthn.go b/routers/web/user/setting/security/webauthn.go
index 70bfaac6e0..eb9f46af52 100644
--- a/routers/web/user/setting/security/webauthn.go
+++ b/routers/web/user/setting/security/webauthn.go
@@ -13,6 +13,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
wa "code.gitea.io/gitea/modules/auth/webauthn"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/session"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/context"
@@ -25,7 +26,7 @@ import (
// WebAuthnRegister initializes the webauthn registration procedure
func WebAuthnRegister(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageMFA) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
@@ -41,7 +42,7 @@ func WebAuthnRegister(ctx *context.Context) {
return
}
if cred != nil {
- ctx.Error(http.StatusConflict, "Name already taken")
+ ctx.HTTPError(http.StatusConflict, "Name already taken")
return
}
@@ -72,7 +73,7 @@ func WebAuthnRegister(ctx *context.Context) {
// WebauthnRegisterPost receives the response of the security key
func WebauthnRegisterPost(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageMFA) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
@@ -109,7 +110,7 @@ func WebauthnRegisterPost(ctx *context.Context) {
return
}
if dbCred != nil {
- ctx.Error(http.StatusConflict, "Name already taken")
+ ctx.HTTPError(http.StatusConflict, "Name already taken")
return
}
@@ -120,14 +121,14 @@ func WebauthnRegisterPost(ctx *context.Context) {
return
}
_ = ctx.Session.Delete("webauthnName")
-
+ _ = ctx.Session.Set(session.KeyUserHasTwoFactorAuth, true)
ctx.JSON(http.StatusCreated, cred)
}
// WebauthnDelete deletes an security key by id
func WebauthnDelete(ctx *context.Context) {
if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageMFA) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
diff --git a/routers/web/user/setting/settings.go b/routers/web/user/setting/settings.go
new file mode 100644
index 0000000000..111931633d
--- /dev/null
+++ b/routers/web/user/setting/settings.go
@@ -0,0 +1,26 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package setting
+
+import (
+ "net/http"
+ "strconv"
+
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/services/context"
+)
+
+func UpdatePreferences(ctx *context.Context) {
+ type preferencesForm struct {
+ CodeViewShowFileTree bool `json:"codeViewShowFileTree"`
+ }
+ form := &preferencesForm{}
+ if err := json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil {
+ ctx.HTTPError(http.StatusBadRequest, "json decode failed")
+ return
+ }
+ _ = user_model.SetUserSetting(ctx, ctx.Doer.ID, user_model.SettingsKeyCodeViewShowFileTree, strconv.FormatBool(form.CodeViewShowFileTree))
+ ctx.JSONOK()
+}
diff --git a/routers/web/user/stop_watch.go b/routers/web/user/stop_watch.go
index 38f74ea455..1d1cc61cc9 100644
--- a/routers/web/user/stop_watch.go
+++ b/routers/web/user/stop_watch.go
@@ -19,19 +19,19 @@ func GetStopwatches(ctx *context.Context) {
PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")),
})
if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, err.Error())
return
}
count, err := issues_model.CountUserStopwatches(ctx, ctx.Doer.ID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, err.Error())
return
}
apiSWs, err := convert.ToStopWatches(ctx, sws)
if err != nil {
- ctx.Error(http.StatusInternalServerError, err.Error())
+ ctx.HTTPError(http.StatusInternalServerError, err.Error())
return
}
diff --git a/routers/web/web.go b/routers/web/web.go
index 5e0995545e..66c3a2da09 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
+ "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/metrics"
"code.gitea.io/gitea/modules/public"
@@ -36,6 +37,7 @@ import (
"code.gitea.io/gitea/routers/web/repo"
"code.gitea.io/gitea/routers/web/repo/actions"
repo_setting "code.gitea.io/gitea/routers/web/repo/setting"
+ shared_actions "code.gitea.io/gitea/routers/web/shared/actions"
"code.gitea.io/gitea/routers/web/shared/project"
"code.gitea.io/gitea/routers/web/user"
user_setting "code.gitea.io/gitea/routers/web/user/setting"
@@ -101,7 +103,7 @@ func buildAuthGroup() *auth_service.Group {
group.Add(&auth_service.Basic{}) // FIXME: this should be removed and only applied in download and git/lfs routers
if setting.Service.EnableReverseProxyAuth {
- group.Add(&auth_service.ReverseProxy{}) // reverseproxy should before Session, otherwise the header will be ignored if user has login
+ group.Add(&auth_service.ReverseProxy{}) // reverse-proxy should before Session, otherwise the header will be ignored if user has login
}
group.Add(&auth_service.Session{})
@@ -117,7 +119,7 @@ func webAuth(authMethod auth_service.Method) func(*context.Context) {
ar, err := common.AuthShared(ctx.Base, ctx.Session, authMethod)
if err != nil {
log.Error("Failed to verify user: %v", err)
- ctx.Error(http.StatusUnauthorized, "Verify")
+ ctx.HTTPError(http.StatusUnauthorized, "Failed to authenticate user")
return
}
ctx.Doer = ar.Doer
@@ -152,7 +154,7 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont
if ctx.Doer.MustChangePassword {
if ctx.Req.URL.Path != "/user/settings/change_password" {
if strings.HasPrefix(ctx.Req.UserAgent(), "git") {
- ctx.Error(http.StatusUnauthorized, ctx.Locale.TrString("auth.must_change_password"))
+ ctx.HTTPError(http.StatusUnauthorized, ctx.Locale.TrString("auth.must_change_password"))
return
}
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
@@ -176,7 +178,7 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont
return
}
- if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == "POST" {
+ if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == http.MethodPost {
ctx.Csrf.Validate(ctx)
if ctx.Written() {
return
@@ -209,7 +211,7 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont
if options.AdminRequired {
if !ctx.Doer.IsAdmin {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
ctx.Data["PageIsAdmin"] = true
@@ -231,8 +233,8 @@ func Routes() *web.Router {
routes.Head("/", misc.DummyOK) // for health check - doesn't need to be passed through gzip handler
routes.Methods("GET, HEAD, OPTIONS", "/assets/*", optionsCorsHandler(), public.FileHandlerFunc())
- routes.Methods("GET, HEAD", "/avatars/*", storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
- routes.Methods("GET, HEAD", "/repo-avatars/*", storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars))
+ routes.Methods("GET, HEAD", "/avatars/*", avatarStorageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
+ routes.Methods("GET, HEAD", "/repo-avatars/*", avatarStorageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars))
routes.Methods("GET, HEAD", "/apple-touch-icon.png", misc.StaticRedirect("/assets/img/apple-touch-icon.png"))
routes.Methods("GET, HEAD", "/apple-touch-icon-precomposed.png", misc.StaticRedirect("/assets/img/apple-touch-icon.png"))
routes.Methods("GET, HEAD", "/favicon.ico", misc.StaticRedirect("/assets/img/favicon.png"))
@@ -278,62 +280,60 @@ func Routes() *web.Router {
routes.Get("/api/swagger", append(mid, misc.Swagger)...) // Render V1 by default
}
- // TODO: These really seem like things that could be folded into Contexter or as helper functions
- mid = append(mid, user.GetNotificationCount)
- mid = append(mid, repo.GetActiveStopwatch)
mid = append(mid, goGet)
+ mid = append(mid, common.PageGlobalData)
- others := web.NewRouter()
- others.Use(mid...)
- registerRoutes(others)
- routes.Mount("", others)
+ webRoutes := web.NewRouter()
+ webRoutes.Use(mid...)
+ webRoutes.Group("", func() { registerWebRoutes(webRoutes) }, common.BlockExpensive(), common.QoS())
+ routes.Mount("", webRoutes)
return routes
}
var optSignInIgnoreCsrf = verifyAuthWithOptions(&common.VerifyOptions{DisableCSRF: true})
-// registerRoutes register routes
-func registerRoutes(m *web.Router) {
+// registerWebRoutes register routes
+func registerWebRoutes(m *web.Router) {
// required to be signed in or signed out
reqSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: true})
reqSignOut := verifyAuthWithOptions(&common.VerifyOptions{SignOutRequired: true})
// optional sign in (if signed in, use the user as doer, if not, no doer)
- optSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView})
- optExploreSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView})
+ optSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInViewStrict})
+ optExploreSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInViewStrict || setting.Service.Explore.RequireSigninView})
validation.AddBindingRules()
linkAccountEnabled := func(ctx *context.Context) {
if !setting.Service.EnableOpenIDSignIn && !setting.Service.EnableOpenIDSignUp && !setting.OAuth2.Enabled {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
openIDSignInEnabled := func(ctx *context.Context) {
if !setting.Service.EnableOpenIDSignIn {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
openIDSignUpEnabled := func(ctx *context.Context) {
if !setting.Service.EnableOpenIDSignUp {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
oauth2Enabled := func(ctx *context.Context) {
if !setting.OAuth2.Enabled {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
reqMilestonesDashboardPageEnabled := func(ctx *context.Context) {
if !setting.Service.ShowMilestonesDashboardPage {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
@@ -341,49 +341,56 @@ func registerRoutes(m *web.Router) {
// webhooksEnabled requires webhooks to be enabled by admin.
webhooksEnabled := func(ctx *context.Context) {
if setting.DisableWebhooks {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
+ return
+ }
+ }
+
+ starsEnabled := func(ctx *context.Context) {
+ if setting.Repository.DisableStars {
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
lfsServerEnabled := func(ctx *context.Context) {
if !setting.LFS.StartServer {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
}
federationEnabled := func(ctx *context.Context) {
if !setting.Federation.Enabled {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
}
dlSourceEnabled := func(ctx *context.Context) {
if setting.Repository.DisableDownloadSourceArchives {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
}
sitemapEnabled := func(ctx *context.Context) {
if !setting.Other.EnableSitemap {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
}
packagesEnabled := func(ctx *context.Context) {
if !setting.Packages.Enabled {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
feedEnabled := func(ctx *context.Context) {
if !setting.Other.EnableFeed {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
}
@@ -392,18 +399,18 @@ func registerRoutes(m *web.Router) {
return func(ctx *context.Context) {
// only check global disabled units when ignoreGlobal is false
if !ignoreGlobal && unitType.UnitGlobalDisabled() {
- ctx.NotFound("Repo unit is is disabled: "+unitType.LogString(), nil)
+ ctx.NotFound(nil)
return
}
if ctx.ContextUser == nil {
- ctx.NotFound("ContextUser is nil", nil)
+ ctx.NotFound(nil)
return
}
if ctx.ContextUser.IsOrganization() {
if ctx.Org.Organization.UnitPermission(ctx, ctx.Doer, unitType) < accessMode {
- ctx.NotFound("ContextUser is org but doer has no access to unit", nil)
+ ctx.NotFound(nil)
return
}
}
@@ -441,10 +448,10 @@ func registerRoutes(m *web.Router) {
addSettingsVariablesRoutes := func() {
m.Group("/variables", func() {
- m.Get("", repo_setting.Variables)
- m.Post("/new", web.Bind(forms.EditVariableForm{}), repo_setting.VariableCreate)
- m.Post("/{variable_id}/edit", web.Bind(forms.EditVariableForm{}), repo_setting.VariableUpdate)
- m.Post("/{variable_id}/delete", repo_setting.VariableDelete)
+ m.Get("", shared_actions.Variables)
+ m.Post("/new", web.Bind(forms.EditVariableForm{}), shared_actions.VariableCreate)
+ m.Post("/{variable_id}/edit", web.Bind(forms.EditVariableForm{}), shared_actions.VariableUpdate)
+ m.Post("/{variable_id}/delete", shared_actions.VariableDelete)
})
}
@@ -458,11 +465,11 @@ func registerRoutes(m *web.Router) {
addSettingsRunnersRoutes := func() {
m.Group("/runners", func() {
- m.Get("", repo_setting.Runners)
- m.Combo("/{runnerid}").Get(repo_setting.RunnersEdit).
- Post(web.Bind(forms.EditRunnerForm{}), repo_setting.RunnersEditPost)
- m.Post("/{runnerid}/delete", repo_setting.RunnerDeletePost)
- m.Post("/reset_registration_token", repo_setting.ResetRunnerRegistrationToken)
+ m.Get("", shared_actions.Runners)
+ m.Combo("/{runnerid}").Get(shared_actions.RunnersEdit).
+ Post(web.Bind(forms.EditRunnerForm{}), shared_actions.RunnersEditPost)
+ m.Post("/{runnerid}/delete", shared_actions.RunnerDeletePost)
+ m.Post("/reset_registration_token", shared_actions.ResetRunnerRegistrationToken)
})
}
@@ -497,7 +504,7 @@ func registerRoutes(m *web.Router) {
m.Get("/organizations", explore.Organizations)
m.Get("/code", func(ctx *context.Context) {
if unit.TypeCode.UnitGlobalDisabled() {
- ctx.NotFound("Repo unit code is disabled", nil)
+ ctx.NotFound(nil)
return
}
}, explore.Code)
@@ -571,6 +578,7 @@ func registerRoutes(m *web.Router) {
m.Group("/user/settings", func() {
m.Get("", user_setting.Profile)
m.Post("", web.Bind(forms.UpdateProfileForm{}), user_setting.ProfilePost)
+ m.Post("/update_preferences", user_setting.UpdatePreferences)
m.Get("/change_password", auth.MustChangePassword)
m.Post("/change_password", web.Bind(forms.MustChangePasswordForm{}), auth.MustChangePasswordPost)
m.Post("/avatar", web.Bind(forms.AvatarForm{}), user_setting.AvatarPost)
@@ -680,7 +688,7 @@ func registerRoutes(m *web.Router) {
m.Get("/activate", auth.Activate)
m.Post("/activate", auth.ActivatePost)
m.Any("/activate_email", auth.ActivateEmail)
- m.Get("/avatar/{username}/{size}", user.AvatarByUserName)
+ m.Get("/avatar/{username}/{size}", user.AvatarByUsernameSize)
m.Get("/recover_account", auth.ResetPasswd)
m.Post("/recover_account", auth.ResetPasswdPost)
m.Get("/forgot_password", auth.ForgotPasswd)
@@ -719,6 +727,7 @@ func registerRoutes(m *web.Router) {
m.Group("/monitor", func() {
m.Get("/stats", admin.MonitorStats)
m.Get("/cron", admin.CronTasks)
+ m.Get("/perftrace", admin.PerfTrace)
m.Get("/stacktrace", admin.Stacktrace)
m.Post("/stacktrace/cancel/{pid}", admin.StacktraceCancel)
m.Get("/queue", admin.Queues)
@@ -813,29 +822,31 @@ func registerRoutes(m *web.Router) {
m.Methods("GET, OPTIONS", "/attachments/{uuid}", optionsCorsHandler(), repo.GetAttachment)
}, optSignIn)
- m.Post("/{username}", reqSignIn, context.UserAssignmentWeb(), user.Action)
+ m.Post("/{username}", reqSignIn, context.UserAssignmentWeb(), user.ActionUserFollow)
reqRepoAdmin := context.RequireRepoAdmin()
- reqRepoCodeWriter := context.RequireRepoWriter(unit.TypeCode)
- canEnableEditor := context.CanEnableEditor()
- reqRepoCodeReader := context.RequireRepoReader(unit.TypeCode)
- reqRepoReleaseWriter := context.RequireRepoWriter(unit.TypeReleases)
- reqRepoReleaseReader := context.RequireRepoReader(unit.TypeReleases)
- reqRepoWikiReader := context.RequireRepoReader(unit.TypeWiki)
- reqRepoWikiWriter := context.RequireRepoWriter(unit.TypeWiki)
- reqRepoIssueReader := context.RequireRepoReader(unit.TypeIssues)
- reqRepoPullsReader := context.RequireRepoReader(unit.TypePullRequests)
- reqRepoIssuesOrPullsWriter := context.RequireRepoWriterOr(unit.TypeIssues, unit.TypePullRequests)
- reqRepoIssuesOrPullsReader := context.RequireRepoReaderOr(unit.TypeIssues, unit.TypePullRequests)
- reqRepoProjectsReader := context.RequireRepoReader(unit.TypeProjects)
- reqRepoProjectsWriter := context.RequireRepoWriter(unit.TypeProjects)
- reqRepoActionsReader := context.RequireRepoReader(unit.TypeActions)
- reqRepoActionsWriter := context.RequireRepoWriter(unit.TypeActions)
+ reqRepoCodeWriter := context.RequireUnitWriter(unit.TypeCode)
+ reqRepoReleaseWriter := context.RequireUnitWriter(unit.TypeReleases)
+ reqRepoReleaseReader := context.RequireUnitReader(unit.TypeReleases)
+ reqRepoIssuesOrPullsWriter := context.RequireUnitWriter(unit.TypeIssues, unit.TypePullRequests)
+ reqRepoIssuesOrPullsReader := context.RequireUnitReader(unit.TypeIssues, unit.TypePullRequests)
+ reqRepoProjectsReader := context.RequireUnitReader(unit.TypeProjects)
+ reqRepoProjectsWriter := context.RequireUnitWriter(unit.TypeProjects)
+ reqRepoActionsReader := context.RequireUnitReader(unit.TypeActions)
+ reqRepoActionsWriter := context.RequireUnitWriter(unit.TypeActions)
+
+ // the legacy names "reqRepoXxx" should be renamed to the correct name "reqUnitXxx", these permissions are for units, not repos
+ reqUnitsWithMarkdown := context.RequireUnitReader(unit.TypeCode, unit.TypeIssues, unit.TypePullRequests, unit.TypeReleases, unit.TypeWiki)
+ reqUnitCodeReader := context.RequireUnitReader(unit.TypeCode)
+ reqUnitIssuesReader := context.RequireUnitReader(unit.TypeIssues)
+ reqUnitPullsReader := context.RequireUnitReader(unit.TypePullRequests)
+ reqUnitWikiReader := context.RequireUnitReader(unit.TypeWiki)
+ reqUnitWikiWriter := context.RequireUnitWriter(unit.TypeWiki)
reqPackageAccess := func(accessMode perm.AccessMode) func(ctx *context.Context) {
return func(ctx *context.Context) {
if ctx.Package.AccessMode < accessMode && !ctx.IsUserSiteAdmin() {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
}
}
}
@@ -843,15 +854,15 @@ func registerRoutes(m *web.Router) {
individualPermsChecker := func(ctx *context.Context) {
// org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked.
if ctx.ContextUser.IsIndividual() {
- switch {
- case ctx.ContextUser.Visibility == structs.VisibleTypePrivate:
+ switch ctx.ContextUser.Visibility {
+ case structs.VisibleTypePrivate:
if ctx.Doer == nil || (ctx.ContextUser.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin) {
- ctx.NotFound("Visit Project", nil)
+ ctx.NotFound(nil)
return
}
- case ctx.ContextUser.Visibility == structs.VisibleTypeLimited:
+ case structs.VisibleTypeLimited:
if ctx.Doer == nil {
- ctx.NotFound("Visit Project", nil)
+ ctx.NotFound(nil)
return
}
}
@@ -861,7 +872,7 @@ func registerRoutes(m *web.Router) {
m.Group("/org", func() {
m.Group("/{org}", func() {
m.Get("/members", org.Members)
- }, context.OrgAssignment())
+ }, context.OrgAssignment(context.OrgAssignmentOptions{}))
}, optSignIn)
// end "/org": members
@@ -887,19 +898,20 @@ func registerRoutes(m *web.Router) {
m.Get("/milestones/{team}", reqMilestonesDashboardPageEnabled, user.Milestones)
m.Post("/members/action/{action}", org.MembersAction)
m.Get("/teams", org.Teams)
- }, context.OrgAssignment(true, false, true))
+ }, context.OrgAssignment(context.OrgAssignmentOptions{RequireMember: true, RequireTeamMember: true}))
m.Group("/{org}", func() {
m.Get("/teams/{team}", org.TeamMembers)
m.Get("/teams/{team}/repositories", org.TeamRepositories)
m.Post("/teams/{team}/action/{action}", org.TeamsAction)
m.Post("/teams/{team}/action/repo/{action}", org.TeamsRepoAction)
- }, context.OrgAssignment(true, false, true))
+ }, context.OrgAssignment(context.OrgAssignmentOptions{RequireMember: true, RequireTeamMember: true}))
- // require admin permission
+ // require member/team-admin permission (old logic is: requireMember=true, requireTeamAdmin=true)
+ // but it doesn't seem right: requireTeamAdmin does nothing
m.Group("/{org}", func() {
m.Get("/teams/-/search", org.SearchTeam)
- }, context.OrgAssignment(true, false, false, true))
+ }, context.OrgAssignment(context.OrgAssignmentOptions{RequireMember: true, RequireTeamAdmin: true}))
// require owner permission
m.Group("/{org}", func() {
@@ -909,6 +921,8 @@ func registerRoutes(m *web.Router) {
m.Post("/teams/{team}/edit", web.Bind(forms.CreateTeamForm{}), org.EditTeamPost)
m.Post("/teams/{team}/delete", org.DeleteTeam)
+ m.Get("/worktime", context.OrgAssignment(context.OrgAssignmentOptions{RequireOwner: true}), org.Worktime)
+
m.Group("/settings", func() {
m.Combo("").Get(org.Settings).
Post(web.Bind(forms.UpdateOrgSettingForm{}), org.SettingsPost)
@@ -950,7 +964,8 @@ func registerRoutes(m *web.Router) {
addSettingsVariablesRoutes()
}, actions.MustEnableActions)
- m.Methods("GET,POST", "/delete", org.SettingsDelete)
+ m.Post("/rename", web.Bind(forms.RenameOrgForm{}), org.SettingsRenamePost)
+ m.Post("/delete", org.SettingsDeleteOrgPost)
m.Group("/packages", func() {
m.Get("", org.Packages)
@@ -976,7 +991,7 @@ func registerRoutes(m *web.Router) {
m.Post("", web.Bind(forms.BlockUserForm{}), org.BlockedUsersPost)
})
}, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled, "PageIsOrgSettings", true))
- }, context.OrgAssignment(true, true))
+ }, context.OrgAssignment(context.OrgAssignmentOptions{RequireOwner: true}))
}, reqSignIn)
// end "/org": most org routes
@@ -998,6 +1013,7 @@ func registerRoutes(m *web.Router) {
m.Get("/versions", user.ListPackageVersions)
m.Group("/{version}", func() {
m.Get("", user.ViewPackageVersion)
+ m.Get("/{version_sub}", user.ViewPackageVersion)
m.Get("/files/{fileid}", user.DownloadPackageFile)
m.Group("/settings", func() {
m.Get("", user.PackageSettings)
@@ -1015,7 +1031,7 @@ func registerRoutes(m *web.Router) {
m.Get("", org.Projects)
m.Get("/{id}", org.ViewProject)
}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true))
- m.Group("", func() { //nolint:dupl
+ m.Group("", func() { //nolint:dupl // duplicates lines 1421-1441
m.Get("/new", org.RenderNewProject)
m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost)
m.Group("/{id}", func() {
@@ -1037,7 +1053,7 @@ func registerRoutes(m *web.Router) {
})
}, reqSignIn, reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true), func(ctx *context.Context) {
if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID {
- ctx.NotFound("NewProject", nil)
+ ctx.NotFound(nil)
return
}
})
@@ -1046,14 +1062,14 @@ func registerRoutes(m *web.Router) {
m.Group("", func() {
m.Get("/code", user.CodeSearch)
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false), individualPermsChecker)
- }, optSignIn, context.UserAssignmentWeb(), context.OrgAssignment())
+ }, optSignIn, context.UserAssignmentWeb(), context.OrgAssignment(context.OrgAssignmentOptions{}))
// end "/{username}/-": packages, projects, code
m.Group("/{username}/{reponame}/-", func() {
m.Group("/migrate", func() {
m.Get("/status", repo.MigrateStatus)
})
- }, optSignIn, context.RepoAssignment, reqRepoCodeReader)
+ }, optSignIn, context.RepoAssignment, reqUnitCodeReader)
// end "/{username}/{reponame}/-": migrate
m.Group("/{username}/{reponame}/settings", func() {
@@ -1064,6 +1080,8 @@ func registerRoutes(m *web.Router) {
m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo_setting.SettingsAvatar)
m.Post("/avatar/delete", repo_setting.SettingsDeleteAvatar)
+ m.Combo("/public_access").Get(repo_setting.PublicAccess).Post(repo_setting.PublicAccessPost)
+
m.Group("/collaboration", func() {
m.Combo("").Get(repo_setting.Collaboration).Post(repo_setting.CollaborationPost)
m.Post("/access_mode", repo_setting.ChangeCollaborationAccessMode)
@@ -1132,7 +1150,7 @@ func registerRoutes(m *web.Router) {
})
})
m.Group("/actions", func() {
- m.Get("", repo_setting.RedirectToDefaultSetting)
+ m.Get("", shared_actions.RedirectToDefaultSetting)
addSettingsRunnersRoutes()
addSettingsSecretsRoutes()
addSettingsVariablesRoutes()
@@ -1143,77 +1161,81 @@ func registerRoutes(m *web.Router) {
m.Post("/cancel", repo.MigrateCancelPost)
})
},
- reqSignIn, context.RepoAssignment, reqRepoAdmin, context.RepoRef(),
+ reqSignIn, context.RepoAssignment, reqRepoAdmin,
ctxDataSet("PageIsRepoSettings", true, "LFSStartServer", setting.LFS.StartServer),
)
// end "/{username}/{reponame}/settings"
- // user/org home, including rss feeds
- m.Get("/{username}/{reponame}", optSignIn, context.RepoAssignment, context.RepoRef(), repo.SetEditorconfigIfExists, repo.Home)
+ // user/org home, including rss feeds like "/{username}/{reponame}.rss"
+ m.Get("/{username}/{reponame}", optSignIn, context.RepoAssignment, context.RepoRefByType(git.RefTypeBranch), repo.SetEditorconfigIfExists, repo.Home)
- // TODO: maybe it should relax the permission to allow "any access"
- m.Post("/{username}/{reponame}/markup", optSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeCode, unit.TypeIssues, unit.TypePullRequests, unit.TypeReleases, unit.TypeWiki), web.Bind(structs.MarkupOption{}), misc.Markup)
+ m.Post("/{username}/{reponame}/markup", optSignIn, context.RepoAssignment, reqUnitsWithMarkdown, web.Bind(structs.MarkupOption{}), misc.Markup)
m.Group("/{username}/{reponame}", func() {
m.Get("/find/*", repo.FindFiles)
m.Group("/tree-list", func() {
- m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.TreeList)
- m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.TreeList)
- m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.TreeList)
+ m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.TreeList)
+ m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.TreeList)
+ m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.TreeList)
+ })
+ m.Group("/tree-view", func() {
+ m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.TreeViewNodes)
+ m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.TreeViewNodes)
+ m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.TreeViewNodes)
})
m.Get("/compare", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff)
m.Combo("/compare/*", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists).
Get(repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff).
- Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, web.Bind(forms.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost)
- }, optSignIn, context.RepoAssignment, reqRepoCodeReader)
+ Post(reqSignIn, context.RepoMustNotBeArchived(), reqUnitPullsReader, repo.MustAllowPulls, web.Bind(forms.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost)
+ m.Get("/pulls/new/*", repo.PullsNewRedirect)
+ }, optSignIn, context.RepoAssignment, reqUnitCodeReader)
// end "/{username}/{reponame}": repo code: find, compare, list
+ addIssuesPullsViewRoutes := func() {
+ // for /{username}/{reponame}/issues" or "/{username}/{reponame}/pulls"
+ m.Get("/posters", repo.IssuePullPosters)
+ m.Group("/{index}", func() {
+ m.Get("/info", repo.GetIssueInfo)
+ m.Get("/attachments", repo.GetIssueAttachments)
+ m.Get("/attachments/{uuid}", repo.GetAttachment)
+ m.Group("/content-history", func() {
+ m.Get("/overview", repo.GetContentHistoryOverview)
+ m.Get("/list", repo.GetContentHistoryList)
+ m.Get("/detail", repo.GetContentHistoryDetail)
+ })
+ })
+ }
+ // FIXME: many "pulls" requests are sent to "issues" endpoints correctly, so the issue endpoints have to tolerate pull request permissions at the moment
+ m.Group("/{username}/{reponame}/{type:issues}", addIssuesPullsViewRoutes, optSignIn, context.RepoAssignment, context.RequireUnitReader(unit.TypeIssues, unit.TypePullRequests))
+ m.Group("/{username}/{reponame}/{type:pulls}", addIssuesPullsViewRoutes, optSignIn, context.RepoAssignment, reqUnitPullsReader)
+
m.Group("/{username}/{reponame}", func() {
- m.Get("/issues/posters", repo.IssuePosters) // it can't use {type:issues|pulls} because it would conflict with other routes like "/pulls/{index}"
- m.Get("/pulls/posters", repo.PullPosters)
m.Get("/comments/{id}/attachments", repo.GetCommentAttachments)
m.Get("/labels", repo.RetrieveLabelsForList, repo.Labels)
m.Get("/milestones", repo.Milestones)
- m.Get("/milestone/{id}", context.RepoRef(), repo.MilestoneIssuesAndPulls)
- m.Group("/{type:issues|pulls}", func() {
- m.Group("/{index}", func() {
- m.Get("/info", repo.GetIssueInfo)
- m.Get("/attachments", repo.GetIssueAttachments)
- m.Get("/attachments/{uuid}", repo.GetAttachment)
- m.Group("/content-history", func() {
- m.Get("/overview", repo.GetContentHistoryOverview)
- m.Get("/list", repo.GetContentHistoryList)
- m.Get("/detail", repo.GetContentHistoryDetail)
- })
- })
- }, context.RepoRef())
+ m.Get("/milestone/{id}", repo.MilestoneIssuesAndPulls)
m.Get("/issues/suggestions", repo.IssueSuggestions)
- }, optSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader)
+ }, optSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader) // issue/pull attachments, labels, milestones
// end "/{username}/{reponame}": view milestone, label, issue, pull, etc
- m.Group("/{username}/{reponame}", func() {
- m.Group("/{type:issues|pulls}", func() {
- m.Get("", repo.Issues)
- m.Group("/{index}", func() {
- m.Get("", repo.ViewIssue)
- })
- })
- }, optSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypeIssues, unit.TypePullRequests, unit.TypeExternalTracker))
+ m.Group("/{username}/{reponame}/{type:issues}", func() {
+ m.Get("", repo.Issues)
+ m.Get("/{index}", repo.ViewIssue)
+ }, optSignIn, context.RepoAssignment, context.RequireUnitReader(unit.TypeIssues, unit.TypeExternalTracker))
// end "/{username}/{reponame}": issue/pull list, issue/pull view, external tracker
m.Group("/{username}/{reponame}", func() { // edit issues, pulls, labels, milestones, etc
m.Group("/issues", func() {
m.Group("/new", func() {
- m.Combo("").Get(context.RepoRef(), repo.NewIssue).
+ m.Combo("").Get(repo.NewIssue).
Post(web.Bind(forms.CreateIssueForm{}), repo.NewIssuePost)
- m.Get("/choose", context.RepoRef(), repo.NewIssueChooseTemplate)
+ m.Get("/choose", repo.NewIssueChooseTemplate)
})
m.Get("/search", repo.SearchRepoIssuesJSON)
- }, context.RepoMustNotBeArchived(), reqRepoIssueReader)
+ }, reqUnitIssuesReader)
- // FIXME: should use different URLs but mostly same logic for comments of issue and pull request.
- // So they can apply their own enable/disable logic on routers.
- m.Group("/{type:issues|pulls}", func() {
+ addIssuesPullsUpdateRoutes := func() {
+ // for "/{username}/{reponame}/issues" or "/{username}/{reponame}/pulls"
m.Group("/{index}", func() {
m.Post("/title", repo.UpdateIssueTitle)
m.Post("/content", repo.UpdateIssueContent)
@@ -1231,7 +1253,8 @@ func registerRoutes(m *web.Router) {
m.Post("/add", web.Bind(forms.AddTimeManuallyForm{}), repo.AddTimeManually)
m.Post("/{timeid}/delete", repo.DeleteTime)
m.Group("/stopwatch", func() {
- m.Post("/toggle", repo.IssueStopwatch)
+ m.Post("/start", repo.IssueStartStopwatch)
+ m.Post("/stop", repo.IssueStopStopwatch)
m.Post("/cancel", repo.CancelStopwatch)
})
})
@@ -1240,39 +1263,38 @@ func registerRoutes(m *web.Router) {
m.Post("/lock", reqRepoIssuesOrPullsWriter, web.Bind(forms.IssueLockForm{}), repo.LockIssue)
m.Post("/unlock", reqRepoIssuesOrPullsWriter, repo.UnlockIssue)
m.Post("/delete", reqRepoAdmin, repo.DeleteIssue)
- }, context.RepoMustNotBeArchived())
-
- m.Group("/{index}", func() {
m.Post("/content-history/soft-delete", repo.SoftDeleteContentHistory)
})
+ m.Post("/attachments", repo.UploadIssueAttachment)
+ m.Post("/attachments/remove", repo.DeleteAttachment)
+
m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel)
m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone)
m.Post("/projects", reqRepoIssuesOrPullsWriter, reqRepoProjectsReader, repo.UpdateIssueProject)
m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee)
- m.Post("/request_review", repo.UpdatePullReviewRequest)
- m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview)
m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus)
m.Post("/delete", reqRepoAdmin, repo.BatchDeleteIssues)
- m.Post("/resolve_conversation", repo.SetShowOutdatedComments, repo.UpdateResolveConversation)
- m.Post("/attachments", repo.UploadIssueAttachment)
- m.Post("/attachments/remove", repo.DeleteAttachment)
m.Delete("/unpin/{index}", reqRepoAdmin, repo.IssueUnpin)
m.Post("/move_pin", reqRepoAdmin, repo.IssuePinMove)
- }, context.RepoMustNotBeArchived())
+ }
+ // FIXME: many "pulls" requests are sent to "issues" endpoints incorrectly, so the issue endpoints have to tolerate pull request permissions at the moment
+ m.Group("/{type:issues}", addIssuesPullsUpdateRoutes, context.RequireUnitReader(unit.TypeIssues, unit.TypePullRequests), context.RepoMustNotBeArchived())
+ m.Group("/{type:pulls}", addIssuesPullsUpdateRoutes, reqUnitPullsReader, context.RepoMustNotBeArchived())
m.Group("/comments/{id}", func() {
m.Post("", repo.UpdateCommentContent)
m.Post("/delete", repo.DeleteComment)
m.Post("/reactions/{action}", web.Bind(forms.ReactionForm{}), repo.ChangeCommentReaction)
- }, context.RepoMustNotBeArchived())
+ }, reqRepoIssuesOrPullsReader) // edit issue/pull comment
m.Group("/labels", func() {
m.Post("/new", web.Bind(forms.CreateLabelForm{}), repo.NewLabel)
m.Post("/edit", web.Bind(forms.CreateLabelForm{}), repo.UpdateLabel)
m.Post("/delete", repo.DeleteLabel)
m.Post("/initialize", web.Bind(forms.InitializeLabelsForm{}), repo.InitializeLabels)
- }, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef())
+ }, reqRepoIssuesOrPullsWriter)
+
m.Group("/milestones", func() {
m.Combo("/new").Get(repo.NewMilestone).
Post(web.Bind(forms.CreateMilestoneForm{}), repo.NewMilestonePost)
@@ -1280,42 +1302,58 @@ func registerRoutes(m *web.Router) {
m.Post("/{id}/edit", web.Bind(forms.CreateMilestoneForm{}), repo.EditMilestonePost)
m.Post("/{id}/{action}", repo.ChangeMilestoneStatus)
m.Post("/delete", repo.DeleteMilestone)
- }, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef())
- m.Group("/pull", func() {
- m.Post("/{index}/target_branch", repo.UpdatePullRequestTarget)
- }, context.RepoMustNotBeArchived())
- }, reqSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader)
+ }, reqRepoIssuesOrPullsWriter)
+
+ // FIXME: many "pulls" requests are sent to "issues" endpoints incorrectly, need to move these routes to the proper place
+ m.Group("/issues", func() {
+ m.Post("/request_review", repo.UpdatePullReviewRequest)
+ m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview)
+ m.Post("/resolve_conversation", repo.SetShowOutdatedComments, repo.UpdateResolveConversation)
+ }, reqUnitPullsReader)
+ m.Post("/pull/{index}/target_branch", reqUnitPullsReader, repo.UpdatePullRequestTarget)
+ }, reqSignIn, context.RepoAssignment, context.RepoMustNotBeArchived())
// end "/{username}/{reponame}": create or edit issues, pulls, labels, milestones
- m.Group("/{username}/{reponame}", func() { // repo code
+ m.Group("/{username}/{reponame}", func() { // repo code (at least "code reader")
m.Group("", func() {
m.Group("", func() {
- m.Combo("/_edit/*").Get(repo.EditFile).
- Post(web.Bind(forms.EditRepoFileForm{}), repo.EditFilePost)
- m.Combo("/_new/*").Get(repo.NewFile).
- Post(web.Bind(forms.EditRepoFileForm{}), repo.NewFilePost)
- m.Post("/_preview/*", web.Bind(forms.EditPreviewDiffForm{}), repo.DiffPreviewPost)
- m.Combo("/_delete/*").Get(repo.DeleteFile).
- Post(web.Bind(forms.DeleteRepoFileForm{}), repo.DeleteFilePost)
- m.Combo("/_upload/*", repo.MustBeAbleToUpload).
+ // "GET" requests only need "code reader" permission, "POST" requests need "code writer" permission.
+ // Because reader can "fork and edit"
+ canWriteToBranch := context.CanWriteToBranch()
+ m.Post("/_preview/*", repo.DiffPreviewPost) // read-only, fine with "code reader"
+ m.Post("/_fork/*", repo.ForkToEditPost) // read-only, fork to own repo, fine with "code reader"
+
+ // the path params are used in PrepareCommitFormOptions to construct the correct form action URL
+ m.Combo("/{editor_action:_edit}/*").
+ Get(repo.EditFile).
+ Post(web.Bind(forms.EditRepoFileForm{}), canWriteToBranch, repo.EditFilePost)
+ m.Combo("/{editor_action:_new}/*").
+ Get(repo.EditFile).
+ Post(web.Bind(forms.EditRepoFileForm{}), canWriteToBranch, repo.EditFilePost)
+ m.Combo("/{editor_action:_delete}/*").
+ Get(repo.DeleteFile).
+ Post(web.Bind(forms.DeleteRepoFileForm{}), canWriteToBranch, repo.DeleteFilePost)
+ m.Combo("/{editor_action:_upload}/*", repo.MustBeAbleToUpload).
Get(repo.UploadFile).
- Post(web.Bind(forms.UploadRepoFileForm{}), repo.UploadFilePost)
- m.Combo("/_diffpatch/*").Get(repo.NewDiffPatch).
- Post(web.Bind(forms.EditRepoFileForm{}), repo.NewDiffPatchPost)
- m.Combo("/_cherrypick/{sha:([a-f0-9]{7,64})}/*").Get(repo.CherryPick).
- Post(web.Bind(forms.CherryPickForm{}), repo.CherryPickPost)
- }, repo.MustBeEditable)
+ Post(web.Bind(forms.UploadRepoFileForm{}), canWriteToBranch, repo.UploadFilePost)
+ m.Combo("/{editor_action:_diffpatch}/*").
+ Get(repo.NewDiffPatch).
+ Post(web.Bind(forms.EditRepoFileForm{}), canWriteToBranch, repo.NewDiffPatchPost)
+ m.Combo("/{editor_action:_cherrypick}/{sha:([a-f0-9]{7,64})}/*").
+ Get(repo.CherryPick).
+ Post(web.Bind(forms.CherryPickForm{}), canWriteToBranch, repo.CherryPickPost)
+ }, context.RepoRefByType(git.RefTypeBranch), repo.WebGitOperationCommonData)
m.Group("", func() {
m.Post("/upload-file", repo.UploadFileToServer)
- m.Post("/upload-remove", web.Bind(forms.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer)
- }, repo.MustBeEditable, repo.MustBeAbleToUpload)
- }, context.RepoRef(), canEnableEditor, context.RepoMustNotBeArchived())
+ m.Post("/upload-remove", repo.RemoveUploadFileFromServer)
+ }, repo.MustBeAbleToUpload, reqRepoCodeWriter)
+ }, repo.MustBeEditable, context.RepoMustNotBeArchived())
m.Group("/branches", func() {
m.Group("/_new", func() {
- m.Post("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.CreateBranch)
- m.Post("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.CreateBranch)
- m.Post("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.CreateBranch)
+ m.Post("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.CreateBranch)
+ m.Post("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.CreateBranch)
+ m.Post("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.CreateBranch)
}, web.Bind(forms.NewBranchForm{}))
m.Post("/delete", repo.DeleteBranchPost)
m.Post("/restore", repo.RestoreBranchPost)
@@ -1324,45 +1362,42 @@ func registerRoutes(m *web.Router) {
}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)
m.Combo("/fork").Get(repo.Fork).Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost)
- }, reqSignIn, context.RepoAssignment, reqRepoCodeReader)
+ }, reqSignIn, context.RepoAssignment, reqUnitCodeReader)
// end "/{username}/{reponame}": repo code
m.Group("/{username}/{reponame}", func() { // repo tags
m.Group("/tags", func() {
- m.Get("", repo.TagsList)
- m.Get("/list", repo.GetTagList)
+ m.Get("", context.RepoRefByDefaultBranch() /* for the "commits" tab */, repo.TagsList)
m.Get(".rss", feedEnabled, repo.TagsListFeedRSS)
m.Get(".atom", feedEnabled, repo.TagsListFeedAtom)
- }, ctxDataSet("EnableFeed", setting.Other.EnableFeed),
- repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, context.RepoRefByTypeOptions{IgnoreNotExistErr: true}))
- m.Post("/tags/delete", repo.DeleteTag, reqSignIn,
- repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef())
- }, optSignIn, context.RepoAssignment, reqRepoCodeReader)
+ m.Get("/list", repo.GetTagList)
+ }, ctxDataSet("EnableFeed", setting.Other.EnableFeed))
+ m.Post("/tags/delete", reqSignIn, reqRepoCodeWriter, context.RepoMustNotBeArchived(), repo.DeleteTag)
+ }, optSignIn, context.RepoAssignment, repo.MustBeNotEmpty, reqUnitCodeReader)
// end "/{username}/{reponame}": repo tags
m.Group("/{username}/{reponame}", func() { // repo releases
m.Group("/releases", func() {
m.Get("", repo.Releases)
- m.Get("/tag/*", repo.SingleRelease)
- m.Get("/latest", repo.LatestRelease)
m.Get(".rss", feedEnabled, repo.ReleasesFeedRSS)
m.Get(".atom", feedEnabled, repo.ReleasesFeedAtom)
- }, ctxDataSet("EnableFeed", setting.Other.EnableFeed),
- repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, context.RepoRefByTypeOptions{IgnoreNotExistErr: true}))
- m.Get("/releases/attachments/{uuid}", repo.MustBeNotEmpty, repo.GetAttachment)
- m.Get("/releases/download/{vTag}/{fileName}", repo.MustBeNotEmpty, repo.RedirectDownload)
+ m.Get("/tag/*", repo.SingleRelease)
+ m.Get("/latest", repo.LatestRelease)
+ }, ctxDataSet("EnableFeed", setting.Other.EnableFeed))
+ m.Get("/releases/attachments/{uuid}", repo.GetAttachment)
+ m.Get("/releases/download/{vTag}/{fileName}", repo.RedirectDownload)
m.Group("/releases", func() {
m.Get("/new", repo.NewRelease)
m.Post("/new", web.Bind(forms.NewReleaseForm{}), repo.NewReleasePost)
m.Post("/delete", repo.DeleteRelease)
m.Post("/attachments", repo.UploadReleaseAttachment)
m.Post("/attachments/remove", repo.DeleteAttachment)
- }, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, context.RepoRef())
+ }, reqSignIn, context.RepoMustNotBeArchived(), reqRepoReleaseWriter)
m.Group("/releases", func() {
m.Get("/edit/*", repo.EditRelease)
m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost)
- }, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, repo.CommitInfoCache)
- }, optSignIn, context.RepoAssignment, reqRepoReleaseReader)
+ }, reqSignIn, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, repo.CommitInfoCache)
+ }, optSignIn, context.RepoAssignment, repo.MustBeNotEmpty, reqRepoReleaseReader)
// end "/{username}/{reponame}": repo releases
m.Group("/{username}/{reponame}", func() { // to maintain compatibility with old attachments
@@ -1383,7 +1418,7 @@ func registerRoutes(m *web.Router) {
m.Group("/{username}/{reponame}/projects", func() {
m.Get("", repo.Projects)
m.Get("/{id}", repo.ViewProject)
- m.Group("", func() { //nolint:dupl
+ m.Group("", func() { //nolint:dupl // duplicates lines 1034-1054
m.Get("/new", repo.RenderNewProject)
m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost)
m.Group("/{id}", func() {
@@ -1412,6 +1447,7 @@ func registerRoutes(m *web.Router) {
m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile)
m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
m.Post("/run", reqRepoActionsWriter, actions.Run)
+ m.Get("/workflow-dispatch-inputs", reqRepoActionsWriter, actions.WorkflowDispatchInputs)
m.Group("/runs/{run}", func() {
m.Combo("").
@@ -1424,75 +1460,83 @@ func registerRoutes(m *web.Router) {
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
m.Get("/logs", actions.Logs)
})
+ m.Get("/workflow", actions.ViewWorkflowFile)
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
m.Post("/approve", reqRepoActionsWriter, actions.Approve)
+ m.Post("/delete", reqRepoActionsWriter, actions.Delete)
m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView)
- m.Delete("/artifacts/{artifact_name}", actions.ArtifactsDeleteView)
+ m.Delete("/artifacts/{artifact_name}", reqRepoActionsWriter, actions.ArtifactsDeleteView)
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
})
m.Group("/workflows/{workflow_name}", func() {
m.Get("/badge.svg", actions.GetWorkflowBadge)
})
- }, optSignIn, context.RepoAssignment, reqRepoActionsReader, actions.MustEnableActions)
+ }, optSignIn, context.RepoAssignment, repo.MustBeNotEmpty, reqRepoActionsReader, actions.MustEnableActions)
// end "/{username}/{reponame}/actions"
m.Group("/{username}/{reponame}/wiki", func() {
m.Combo("").
Get(repo.Wiki).
- Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
+ Post(context.RepoMustNotBeArchived(), reqSignIn, reqUnitWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
m.Combo("/*").
Get(repo.Wiki).
- Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
+ Post(context.RepoMustNotBeArchived(), reqSignIn, reqUnitWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
m.Get("/blob_excerpt/{sha}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob)
m.Get("/commit/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
m.Get("/commit/{sha:[a-f0-9]{7,64}}.{ext:patch|diff}", repo.RawDiff)
m.Get("/raw/*", repo.WikiRaw)
- }, optSignIn, context.RepoAssignment, repo.MustEnableWiki, reqRepoWikiReader, func(ctx *context.Context) {
+ }, optSignIn, context.RepoAssignment, repo.MustEnableWiki, reqUnitWikiReader, func(ctx *context.Context) {
ctx.Data["PageIsWiki"] = true
- ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink()
+ ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink(ctx, ctx.Doer)
})
// end "/{username}/{reponame}/wiki"
m.Group("/{username}/{reponame}/activity", func() {
+ // activity has its own permission checks
m.Get("", repo.Activity)
m.Get("/{period}", repo.Activity)
- m.Group("/contributors", func() {
- m.Get("", repo.Contributors)
- m.Get("/data", repo.ContributorsData)
- })
- m.Group("/code-frequency", func() {
- m.Get("", repo.CodeFrequency)
- m.Get("/data", repo.CodeFrequencyData)
- })
- m.Group("/recent-commits", func() {
- m.Get("", repo.RecentCommits)
- m.Get("/data", repo.RecentCommitsData)
- })
+
+ m.Group("", func() {
+ m.Group("/contributors", func() {
+ m.Get("", repo.Contributors)
+ m.Get("/data", repo.ContributorsData)
+ })
+ m.Group("/code-frequency", func() {
+ m.Get("", repo.CodeFrequency)
+ m.Get("/data", repo.CodeFrequencyData)
+ })
+ m.Group("/recent-commits", func() {
+ m.Get("", repo.RecentCommits)
+ m.Get("/data", repo.CodeFrequencyData) // "recent-commits" also uses the same data as "code-frequency"
+ })
+ }, reqUnitCodeReader)
},
- optSignIn, context.RepoAssignment, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases),
- context.RepoRef(), repo.MustBeNotEmpty,
+ optSignIn, context.RepoAssignment, repo.MustBeNotEmpty,
+ context.RequireUnitReader(unit.TypeCode, unit.TypeIssues, unit.TypePullRequests, unit.TypeReleases),
)
// end "/{username}/{reponame}/activity"
m.Group("/{username}/{reponame}", func() {
- m.Group("/pulls/{index}", func() {
+ m.Get("/{type:pulls}", repo.Issues)
+ m.Group("/{type:pulls}/{index}", func() {
m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewIssue)
m.Get(".diff", repo.DownloadPullDiff)
m.Get(".patch", repo.DownloadPullPatch)
+ m.Get("/merge_box", repo.ViewPullMergeBox)
m.Group("/commits", func() {
- m.Get("", context.RepoRef(), repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits)
- m.Get("/list", context.RepoRef(), repo.GetPullCommits)
- m.Get("/{sha:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit)
+ m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits)
+ m.Get("/list", repo.GetPullCommits)
+ m.Get("/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit)
})
m.Post("/merge", context.RepoMustNotBeArchived(), web.Bind(forms.MergePullRequestForm{}), repo.MergePullRequest)
m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest)
m.Post("/update", repo.UpdatePullRequest)
m.Post("/set_allow_maintainer_edit", web.Bind(forms.UpdateAllowEditsForm{}), repo.SetAllowEdits)
- m.Post("/cleanup", context.RepoMustNotBeArchived(), context.RepoRef(), repo.CleanUpPullRequest)
+ m.Post("/cleanup", context.RepoMustNotBeArchived(), repo.CleanUpPullRequest)
m.Group("/files", func() {
- m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForAllCommitsOfPr)
- m.Get("/{sha:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesStartingFromCommit)
- m.Get("/{shaFrom:[a-f0-9]{7,40}}..{shaTo:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForRange)
+ m.Get("", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForAllCommitsOfPr)
+ m.Get("/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesStartingFromCommit)
+ m.Get("/{shaFrom:[a-f0-9]{7,40}}..{shaTo:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForRange)
m.Group("/reviews", func() {
m.Get("/new_comment", repo.RenderNewCodeCommentForm)
m.Post("/comments", web.Bind(forms.CodeCommentForm{}), repo.SetShowOutdatedComments, repo.CreateCodeComment)
@@ -1500,14 +1544,14 @@ func registerRoutes(m *web.Router) {
}, context.RepoMustNotBeArchived())
})
})
- }, optSignIn, context.RepoAssignment, repo.MustAllowPulls, reqRepoPullsReader)
+ }, optSignIn, context.RepoAssignment, repo.MustAllowPulls, reqUnitPullsReader)
// end "/{username}/{reponame}/pulls/{index}": repo pull request
m.Group("/{username}/{reponame}", func() {
m.Group("/activity_author_data", func() {
m.Get("", repo.ActivityAuthors)
m.Get("/{period}", repo.ActivityAuthors)
- }, context.RepoRef(), repo.MustBeNotEmpty)
+ }, repo.MustBeNotEmpty)
m.Group("/archive", func() {
m.Get("/*", repo.Download)
@@ -1516,46 +1560,43 @@ func registerRoutes(m *web.Router) {
m.Group("/branches", func() {
m.Get("/list", repo.GetBranchesList)
- m.Get("", repo.Branches)
- }, repo.MustBeNotEmpty, context.RepoRef())
+ m.Get("", context.RepoRefByDefaultBranch() /* for the "commits" tab */, repo.Branches)
+ }, repo.MustBeNotEmpty)
m.Group("/media", func() {
- m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownloadOrLFS)
- m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.SingleDownloadOrLFS)
- m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.SingleDownloadOrLFS)
- m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByIDOrLFS)
- // "/*" route is deprecated, and kept for backward compatibility
- m.Get("/*", context.RepoRefByType(context.RepoRefUnknown), repo.SingleDownloadOrLFS)
+ m.Get("/blob/{sha}", repo.DownloadByIDOrLFS)
+ m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.SingleDownloadOrLFS)
+ m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.SingleDownloadOrLFS)
+ m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.SingleDownloadOrLFS)
+ m.Get("/*", context.RepoRefByType(""), repo.SingleDownloadOrLFS) // "/*" route is deprecated, and kept for backward compatibility
}, repo.MustBeNotEmpty)
m.Group("/raw", func() {
- m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownload)
- m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.SingleDownload)
- m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.SingleDownload)
- m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByID)
- // "/*" route is deprecated, and kept for backward compatibility
- m.Get("/*", context.RepoRefByType(context.RepoRefUnknown), repo.SingleDownload)
+ m.Get("/blob/{sha}", repo.DownloadByID)
+ m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.SingleDownload)
+ m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.SingleDownload)
+ m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.SingleDownload)
+ m.Get("/*", context.RepoRefByType(""), repo.SingleDownload) // "/*" route is deprecated, and kept for backward compatibility
}, repo.MustBeNotEmpty)
m.Group("/render", func() {
- m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RenderFile)
- m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RenderFile)
- m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RenderFile)
- m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.RenderFile)
+ m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.RenderFile)
+ m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.RenderFile)
+ m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.RenderFile)
+ m.Get("/blob/{sha}", repo.RenderFile)
}, repo.MustBeNotEmpty)
m.Group("/commits", func() {
- m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefCommits)
- m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefCommits)
- m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefCommits)
- // "/*" route is deprecated, and kept for backward compatibility
- m.Get("/*", context.RepoRefByType(context.RepoRefUnknown), repo.RefCommits)
+ m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.RefCommits)
+ m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.RefCommits)
+ m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.RefCommits)
+ m.Get("/*", context.RepoRefByType(""), repo.RefCommits) // "/*" route is deprecated, and kept for backward compatibility
}, repo.MustBeNotEmpty)
m.Group("/blame", func() {
- m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefBlame)
- m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefBlame)
- m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefBlame)
+ m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.RefBlame)
+ m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.RefBlame)
+ m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.RefBlame)
}, repo.MustBeNotEmpty)
m.Get("/blob_excerpt/{sha}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob)
@@ -1564,31 +1605,38 @@ func registerRoutes(m *web.Router) {
m.Get("/graph", repo.Graph)
m.Get("/commit/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
m.Get("/commit/{sha:([a-f0-9]{7,64})$}/load-branches-and-tags", repo.LoadBranchesAndTags)
- m.Get("/cherry-pick/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, repo.CherryPick)
- }, repo.MustBeNotEmpty, context.RepoRef())
- m.Get("/rss/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
- m.Get("/atom/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
+ // FIXME: this route `/cherry-pick/{sha}` doesn't seem useful or right, the new code always uses `/_cherrypick/` which could handle branch name correctly
+ m.Get("/cherry-pick/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, context.RepoRefByDefaultBranch(), repo.CherryPick)
+ }, repo.MustBeNotEmpty)
+
+ m.Get("/rss/branch/*", context.RepoRefByType(git.RefTypeBranch), feedEnabled, feed.RenderBranchFeed)
+ m.Get("/atom/branch/*", context.RepoRefByType(git.RefTypeBranch), feedEnabled, feed.RenderBranchFeed)
m.Group("/src", func() {
- m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
- m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home)
- m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home)
- m.Get("/*", context.RepoRefByType(context.RepoRefUnknown), repo.Home) // "/*" route is deprecated, and kept for backward compatibility
+ m.Get("", func(ctx *context.Context) { ctx.Redirect(ctx.Repo.RepoLink) }) // there is no "{owner}/{repo}/src" page, so redirect to "{owner}/{repo}" to avoid 404
+ m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.Home)
+ m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.Home)
+ m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.Home)
+ m.Get("/*", context.RepoRefByType(""), repo.Home) // "/*" route is deprecated, and kept for backward compatibility
}, repo.SetEditorconfigIfExists)
+ m.Get("/tree/*", repo.RedirectRepoTreeToSrc) // redirect "/owner/repo/tree/*" requests to "/owner/repo/src/*"
+ m.Get("/blob/*", repo.RedirectRepoBlobToCommit) // redirect "/owner/repo/blob/*" requests to "/owner/repo/src/commit/*"
- m.Get("/forks", context.RepoRef(), repo.Forks)
+ m.Get("/forks", repo.Forks)
m.Get("/commit/{sha:([a-f0-9]{7,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, repo.RawDiff)
- m.Post("/lastcommit/*", context.RepoRefByType(context.RepoRefCommit), repo.LastCommit)
- }, optSignIn, context.RepoAssignment, reqRepoCodeReader)
+ m.Post("/lastcommit/*", context.RepoRefByType(git.RefTypeCommit), repo.LastCommit)
+ }, optSignIn, context.RepoAssignment, reqUnitCodeReader)
// end "/{username}/{reponame}": repo code
m.Group("/{username}/{reponame}", func() {
- m.Get("/stars", repo.Stars)
+ m.Get("/stars", starsEnabled, repo.Stars)
m.Get("/watchers", repo.Watchers)
- m.Get("/search", reqRepoCodeReader, repo.Search)
- m.Post("/action/{action}", reqSignIn, repo.Action)
- }, optSignIn, context.RepoAssignment, context.RepoRef())
+ m.Get("/search", reqUnitCodeReader, repo.Search)
+ m.Post("/action/{action:star|unstar}", reqSignIn, starsEnabled, repo.ActionStar)
+ m.Post("/action/{action:watch|unwatch}", reqSignIn, repo.ActionWatch)
+ m.Post("/action/{action:accept_transfer|reject_transfer}", reqSignIn, repo.ActionTransfer)
+ }, optSignIn, context.RepoAssignment)
common.AddOwnerRepoGitLFSRoutes(m, optSignInIgnoreCsrf, lfsServerEnabled) // "/{username}/{reponame}/{lfs-paths}": git-lfs support
@@ -1611,14 +1659,15 @@ func registerRoutes(m *web.Router) {
m.Group("/devtest", func() {
m.Any("", devtest.List)
m.Any("/fetch-action-test", devtest.FetchActionTest)
- m.Any("/{sub}", devtest.Tmpl)
+ m.Any("/{sub}", devtest.TmplCommon)
+ m.Get("/repo-action-view/{run}/{job}", devtest.MockActionsView)
m.Post("/actions-mock/runs/{run}/jobs/{job}", web.Bind(actions.ViewRequest{}), devtest.MockActionsRunsJobs)
})
}
m.NotFound(func(w http.ResponseWriter, req *http.Request) {
- ctx := context.GetWebContext(req)
- routing.UpdateFuncInfo(ctx, routing.GetFuncInfo(ctx.NotFound, "WebNotFound"))
- ctx.NotFound("", nil)
+ ctx := context.GetWebContext(req.Context())
+ defer routing.RecordFuncInfo(ctx, routing.GetFuncInfo(ctx.NotFound, "WebNotFound"))()
+ ctx.NotFound(nil)
})
}
diff --git a/routers/web/webfinger.go b/routers/web/webfinger.go
index a87c426b3b..a4c9bf902b 100644
--- a/routers/web/webfinger.go
+++ b/routers/web/webfinger.go
@@ -7,6 +7,7 @@ import (
"fmt"
"net/http"
"net/url"
+ "strconv"
"strings"
user_model "code.gitea.io/gitea/models/user"
@@ -39,7 +40,7 @@ func WebfingerQuery(ctx *context.Context) {
resource, err := url.Parse(ctx.FormTrim("resource"))
if err != nil {
- ctx.Error(http.StatusBadRequest)
+ ctx.HTTPError(http.StatusBadRequest)
return
}
@@ -50,11 +51,11 @@ func WebfingerQuery(ctx *context.Context) {
// allow only the current host
parts := strings.SplitN(resource.Opaque, "@", 2)
if len(parts) != 2 {
- ctx.Error(http.StatusBadRequest)
+ ctx.HTTPError(http.StatusBadRequest)
return
}
if parts[1] != appURL.Host {
- ctx.Error(http.StatusBadRequest)
+ ctx.HTTPError(http.StatusBadRequest)
return
}
@@ -65,30 +66,30 @@ func WebfingerQuery(ctx *context.Context) {
err = user_model.ErrUserNotExist{}
}
default:
- ctx.Error(http.StatusBadRequest)
+ ctx.HTTPError(http.StatusBadRequest)
return
}
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
} else {
log.Error("Error getting user: %s Error: %v", resource.Opaque, err)
- ctx.Error(http.StatusInternalServerError)
+ ctx.HTTPError(http.StatusInternalServerError)
}
return
}
if !user_model.IsUserVisibleToViewer(ctx, u, ctx.Doer) {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
aliases := []string{
u.HTMLURL(),
- appURL.String() + "api/v1/activitypub/user-id/" + fmt.Sprint(u.ID),
+ appURL.String() + "api/v1/activitypub/user-id/" + strconv.FormatInt(u.ID, 10),
}
if !u.KeepEmailPrivate {
- aliases = append(aliases, fmt.Sprintf("mailto:%s", u.Email))
+ aliases = append(aliases, "mailto:"+u.Email)
}
links := []*webfingerLink{
@@ -104,7 +105,7 @@ func WebfingerQuery(ctx *context.Context) {
{
Rel: "self",
Type: "application/activity+json",
- Href: appURL.String() + "api/v1/activitypub/user-id/" + fmt.Sprint(u.ID),
+ Href: appURL.String() + "api/v1/activitypub/user-id/" + strconv.FormatInt(u.ID, 10),
},
{
Rel: "http://openid.net/specs/connect/1.0/issuer",
diff --git a/services/actions/auth.go b/services/actions/auth.go
index 1ef21f6e0e..12a8fba53f 100644
--- a/services/actions/auth.go
+++ b/services/actions/auth.go
@@ -4,6 +4,7 @@
package actions
import (
+ "errors"
"fmt"
"net/http"
"strings"
@@ -80,7 +81,7 @@ func ParseAuthorizationToken(req *http.Request) (int64, error) {
parts := strings.SplitN(h, " ", 2)
if len(parts) != 2 {
log.Error("split token failed: %s", h)
- return 0, fmt.Errorf("split token failed")
+ return 0, errors.New("split token failed")
}
return TokenToTaskID(parts[1])
@@ -100,7 +101,7 @@ func TokenToTaskID(token string) (int64, error) {
c, ok := parsedToken.Claims.(*actionsClaims)
if !parsedToken.Valid || !ok {
- return 0, fmt.Errorf("invalid token claim")
+ return 0, errors.New("invalid token claim")
}
return c.TaskID, nil
diff --git a/services/actions/auth_test.go b/services/actions/auth_test.go
index 85e7409105..38d0ba7f82 100644
--- a/services/actions/auth_test.go
+++ b/services/actions/auth_test.go
@@ -18,7 +18,7 @@ func TestCreateAuthorizationToken(t *testing.T) {
var taskID int64 = 23
token, err := CreateAuthorizationToken(taskID, 1, 2)
assert.NoError(t, err)
- assert.NotEqual(t, "", token)
+ assert.NotEmpty(t, token)
claims := jwt.MapClaims{}
_, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (any, error) {
return setting.GetGeneralTokenSigningSecret(), nil
@@ -44,7 +44,7 @@ func TestParseAuthorizationToken(t *testing.T) {
var taskID int64 = 23
token, err := CreateAuthorizationToken(taskID, 1, 2)
assert.NoError(t, err)
- assert.NotEqual(t, "", token)
+ assert.NotEmpty(t, token)
headers := http.Header{}
headers.Set("Authorization", "Bearer "+token)
rTaskID, err := ParseAuthorizationToken(&http.Request{
diff --git a/services/actions/cleanup.go b/services/actions/cleanup.go
index 1223ebcab6..d0cc63e538 100644
--- a/services/actions/cleanup.go
+++ b/services/actions/cleanup.go
@@ -5,18 +5,23 @@ package actions
import (
"context"
+ "errors"
"fmt"
"time"
actions_model "code.gitea.io/gitea/models/actions"
+ "code.gitea.io/gitea/models/db"
actions_module "code.gitea.io/gitea/modules/actions"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/timeutil"
+
+ "xorm.io/builder"
)
-// Cleanup removes expired actions logs, data and artifacts
+// Cleanup removes expired actions logs, data, artifacts and used ephemeral runners
func Cleanup(ctx context.Context) error {
// clean up expired artifacts
if err := CleanupArtifacts(ctx); err != nil {
@@ -24,10 +29,15 @@ func Cleanup(ctx context.Context) error {
}
// clean up old logs
- if err := CleanupLogs(ctx); err != nil {
+ if err := CleanupExpiredLogs(ctx); err != nil {
return fmt.Errorf("cleanup logs: %w", err)
}
+ // clean up old ephemeral runners
+ if err := CleanupEphemeralRunners(ctx); err != nil {
+ return fmt.Errorf("cleanup old ephemeral runners: %w", err)
+ }
+
return nil
}
@@ -52,9 +62,9 @@ func cleanExpiredArtifacts(taskCtx context.Context) error {
}
if err := storage.ActionsArtifacts.Delete(artifact.StoragePath); err != nil {
log.Error("Cannot delete artifact %d: %v", artifact.ID, err)
- continue
+ // go on
}
- log.Info("Artifact %d set expired", artifact.ID)
+ log.Info("Artifact %d is deleted (due to expiration)", artifact.ID)
}
return nil
}
@@ -76,9 +86,9 @@ func cleanNeedDeleteArtifacts(taskCtx context.Context) error {
}
if err := storage.ActionsArtifacts.Delete(artifact.StoragePath); err != nil {
log.Error("Cannot delete artifact %d: %v", artifact.ID, err)
- continue
+ // go on
}
- log.Info("Artifact %d set deleted", artifact.ID)
+ log.Info("Artifact %d is deleted (due to pending deletion)", artifact.ID)
}
if len(artifacts) < deleteArtifactBatchSize {
log.Debug("No more artifacts pending deletion")
@@ -90,8 +100,15 @@ func cleanNeedDeleteArtifacts(taskCtx context.Context) error {
const deleteLogBatchSize = 100
-// CleanupLogs removes logs which are older than the configured retention time
-func CleanupLogs(ctx context.Context) error {
+func removeTaskLog(ctx context.Context, task *actions_model.ActionTask) {
+ if err := actions_module.RemoveLogs(ctx, task.LogInStorage, task.LogFilename); err != nil {
+ log.Error("Failed to remove log %s (in storage %v) of task %v: %v", task.LogFilename, task.LogInStorage, task.ID, err)
+ // do not return error here, go on
+ }
+}
+
+// CleanupExpiredLogs removes logs which are older than the configured retention time
+func CleanupExpiredLogs(ctx context.Context) error {
olderThan := timeutil.TimeStampNow().AddDuration(-time.Duration(setting.Actions.LogRetentionDays) * 24 * time.Hour)
count := 0
@@ -101,11 +118,7 @@ func CleanupLogs(ctx context.Context) error {
return fmt.Errorf("find old tasks: %w", err)
}
for _, task := range tasks {
- if err := actions_module.RemoveLogs(ctx, task.LogInStorage, task.LogFilename); err != nil {
- log.Error("Failed to remove log %s (in storage %v) of task %v: %v", task.LogFilename, task.LogInStorage, task.ID, err)
- // do not return error here, continue to next task
- continue
- }
+ removeTaskLog(ctx, task)
task.LogIndexes = nil // clear log indexes since it's a heavy field
task.LogExpired = true
if err := actions_model.UpdateTask(ctx, task, "log_indexes", "log_expired"); err != nil {
@@ -124,3 +137,124 @@ func CleanupLogs(ctx context.Context) error {
log.Info("Removed %d logs", count)
return nil
}
+
+// CleanupEphemeralRunners removes used ephemeral runners which are no longer able to process jobs
+func CleanupEphemeralRunners(ctx context.Context) error {
+ subQuery := builder.Select("`action_runner`.id").
+ From(builder.Select("*").From("`action_runner`"), "`action_runner`"). // mysql needs this redundant subquery
+ Join("INNER", "`action_task`", "`action_task`.`runner_id` = `action_runner`.`id`").
+ Where(builder.Eq{"`action_runner`.`ephemeral`": true}).
+ And(builder.NotIn("`action_task`.`status`", actions_model.StatusWaiting, actions_model.StatusRunning, actions_model.StatusBlocked))
+ b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
+ res, err := db.GetEngine(ctx).Exec(b)
+ if err != nil {
+ return fmt.Errorf("find runners: %w", err)
+ }
+ affected, _ := res.RowsAffected()
+ log.Info("Removed %d runners", affected)
+ return nil
+}
+
+// CleanupEphemeralRunnersByPickedTaskOfRepo removes all ephemeral runners that have active/finished tasks on the given repository
+func CleanupEphemeralRunnersByPickedTaskOfRepo(ctx context.Context, repoID int64) error {
+ subQuery := builder.Select("`action_runner`.id").
+ From(builder.Select("*").From("`action_runner`"), "`action_runner`"). // mysql needs this redundant subquery
+ Join("INNER", "`action_task`", "`action_task`.`runner_id` = `action_runner`.`id`").
+ Where(builder.And(builder.Eq{"`action_runner`.`ephemeral`": true}, builder.Eq{"`action_task`.`repo_id`": repoID}))
+ b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
+ res, err := db.GetEngine(ctx).Exec(b)
+ if err != nil {
+ return fmt.Errorf("find runners: %w", err)
+ }
+ affected, _ := res.RowsAffected()
+ log.Info("Removed %d runners", affected)
+ return nil
+}
+
+// DeleteRun deletes workflow run, including all logs and artifacts.
+func DeleteRun(ctx context.Context, run *actions_model.ActionRun) error {
+ if !run.Status.IsDone() {
+ return errors.New("run is not done")
+ }
+
+ repoID := run.RepoID
+
+ jobs, err := actions_model.GetRunJobsByRunID(ctx, run.ID)
+ if err != nil {
+ return err
+ }
+ jobIDs := container.FilterSlice(jobs, func(j *actions_model.ActionRunJob) (int64, bool) {
+ return j.ID, true
+ })
+ tasks := make(actions_model.TaskList, 0)
+ if len(jobIDs) > 0 {
+ if err := db.GetEngine(ctx).Where("repo_id = ?", repoID).In("job_id", jobIDs).Find(&tasks); err != nil {
+ return err
+ }
+ }
+
+ artifacts, err := db.Find[actions_model.ActionArtifact](ctx, actions_model.FindArtifactsOptions{
+ RepoID: repoID,
+ RunID: run.ID,
+ })
+ if err != nil {
+ return err
+ }
+
+ var recordsToDelete []any
+
+ recordsToDelete = append(recordsToDelete, &actions_model.ActionRun{
+ RepoID: repoID,
+ ID: run.ID,
+ })
+ recordsToDelete = append(recordsToDelete, &actions_model.ActionRunJob{
+ RepoID: repoID,
+ RunID: run.ID,
+ })
+ for _, tas := range tasks {
+ recordsToDelete = append(recordsToDelete, &actions_model.ActionTask{
+ RepoID: repoID,
+ ID: tas.ID,
+ })
+ recordsToDelete = append(recordsToDelete, &actions_model.ActionTaskStep{
+ RepoID: repoID,
+ TaskID: tas.ID,
+ })
+ recordsToDelete = append(recordsToDelete, &actions_model.ActionTaskOutput{
+ TaskID: tas.ID,
+ })
+ }
+ recordsToDelete = append(recordsToDelete, &actions_model.ActionArtifact{
+ RepoID: repoID,
+ RunID: run.ID,
+ })
+
+ if err := db.WithTx(ctx, func(ctx context.Context) error {
+ // TODO: Deleting task records could break current ephemeral runner implementation. This is a temporary workaround suggested by ChristopherHX.
+ // Since you delete potentially the only task an ephemeral act_runner has ever run, please delete the affected runners first.
+ // one of
+ // call cleanup ephemeral runners first
+ // delete affected ephemeral act_runners
+ // I would make ephemeral runners fully delete directly before formally finishing the task
+ //
+ // See also: https://github.com/go-gitea/gitea/pull/34337#issuecomment-2862222788
+ if err := CleanupEphemeralRunners(ctx); err != nil {
+ return err
+ }
+ return db.DeleteBeans(ctx, recordsToDelete...)
+ }); err != nil {
+ return err
+ }
+
+ // Delete files on storage
+ for _, tas := range tasks {
+ removeTaskLog(ctx, tas)
+ }
+ for _, art := range artifacts {
+ if err := storage.ActionsArtifacts.Delete(art.StoragePath); err != nil {
+ log.Error("remove artifact file %q: %v", art.StoragePath, err)
+ }
+ }
+
+ return nil
+}
diff --git a/services/actions/clear_tasks.go b/services/actions/clear_tasks.go
index 67373782d5..274c04aa57 100644
--- a/services/actions/clear_tasks.go
+++ b/services/actions/clear_tasks.go
@@ -10,10 +10,13 @@ import (
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
+ webhook_module "code.gitea.io/gitea/modules/webhook"
+ notify_service "code.gitea.io/gitea/services/notify"
)
// StopZombieTasks stops the task which have running status, but haven't been updated for a long time
@@ -32,6 +35,32 @@ func StopEndlessTasks(ctx context.Context) error {
})
}
+func notifyWorkflowJobStatusUpdate(ctx context.Context, jobs []*actions_model.ActionRunJob) {
+ if len(jobs) > 0 {
+ CreateCommitStatus(ctx, jobs...)
+ for _, job := range jobs {
+ _ = job.LoadAttributes(ctx)
+ notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil)
+ }
+ if len(jobs) > 0 {
+ job := jobs[0]
+ notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run)
+ }
+ }
+}
+
+func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) error {
+ jobs, err := actions_model.CancelPreviousJobs(ctx, repoID, ref, workflowID, event)
+ notifyWorkflowJobStatusUpdate(ctx, jobs)
+ return err
+}
+
+func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) error {
+ jobs, err := actions_model.CleanRepoScheduleTasks(ctx, repo)
+ notifyWorkflowJobStatusUpdate(ctx, jobs)
+ return err
+}
+
func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) error {
tasks, err := db.Find[actions_model.ActionTask](ctx, opts)
if err != nil {
@@ -67,7 +96,7 @@ func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) error {
remove()
}
- CreateCommitStatus(ctx, jobs...)
+ notifyWorkflowJobStatusUpdate(ctx, jobs)
return nil
}
@@ -87,14 +116,20 @@ func CancelAbandonedJobs(ctx context.Context) error {
for _, job := range jobs {
job.Status = actions_model.StatusCancelled
job.Stopped = now
+ updated := false
if err := db.WithTx(ctx, func(ctx context.Context) error {
- _, err := actions_model.UpdateRunJob(ctx, job, nil, "status", "stopped")
+ n, err := actions_model.UpdateRunJob(ctx, job, nil, "status", "stopped")
+ updated = err == nil && n > 0
return err
}); err != nil {
log.Warn("cancel abandoned job %v: %v", job.ID, err)
// go on
}
CreateCommitStatus(ctx, job)
+ if updated {
+ NotifyWorkflowRunStatusUpdateWithReload(ctx, job)
+ notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil)
+ }
}
return nil
diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go
index 7f52c9d31b..ef241e5091 100644
--- a/services/actions/commit_status.go
+++ b/services/actions/commit_status.go
@@ -5,6 +5,7 @@ package actions
import (
"context"
+ "errors"
"fmt"
"path"
@@ -13,9 +14,9 @@ import (
git_model "code.gitea.io/gitea/models/git"
user_model "code.gitea.io/gitea/models/user"
actions_module "code.gitea.io/gitea/modules/actions"
+ "code.gitea.io/gitea/modules/commitstatus"
git "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
- api "code.gitea.io/gitea/modules/structs"
webhook_module "code.gitea.io/gitea/modules/webhook"
commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus"
@@ -51,10 +52,16 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
return fmt.Errorf("GetPushEventPayload: %w", err)
}
if payload.HeadCommit == nil {
- return fmt.Errorf("head commit is missing in event payload")
+ return errors.New("head commit is missing in event payload")
}
sha = payload.HeadCommit.ID
- case webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestSync:
+ case // pull_request
+ webhook_module.HookEventPullRequest,
+ webhook_module.HookEventPullRequestSync,
+ webhook_module.HookEventPullRequestAssign,
+ webhook_module.HookEventPullRequestLabel,
+ webhook_module.HookEventPullRequestReviewRequest,
+ webhook_module.HookEventPullRequestMilestone:
if run.TriggerEvent == actions_module.GithubEventPullRequestTarget {
event = "pull_request_target"
} else {
@@ -65,9 +72,9 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
return fmt.Errorf("GetPullRequestEventPayload: %w", err)
}
if payload.PullRequest == nil {
- return fmt.Errorf("pull request is missing in event payload")
+ return errors.New("pull request is missing in event payload")
} else if payload.PullRequest.Head == nil {
- return fmt.Errorf("head of pull request is missing in event payload")
+ return errors.New("head of pull request is missing in event payload")
}
sha = payload.PullRequest.Head.Sha
case webhook_module.HookEventRelease:
@@ -85,7 +92,7 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
}
ctxname := fmt.Sprintf("%s / %s (%s)", runName, job.Name, event)
state := toCommitStatus(job.Status)
- if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll); err == nil {
+ if statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll); err == nil {
for _, v := range statuses {
if v.Context == ctxname {
if v.State == state {
@@ -140,16 +147,18 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
return commitstatus_service.CreateCommitStatus(ctx, repo, creator, commitID.String(), &status)
}
-func toCommitStatus(status actions_model.Status) api.CommitStatusState {
+func toCommitStatus(status actions_model.Status) commitstatus.CommitStatusState {
switch status {
- case actions_model.StatusSuccess, actions_model.StatusSkipped:
- return api.CommitStatusSuccess
+ case actions_model.StatusSuccess:
+ return commitstatus.CommitStatusSuccess
case actions_model.StatusFailure, actions_model.StatusCancelled:
- return api.CommitStatusFailure
+ return commitstatus.CommitStatusFailure
case actions_model.StatusWaiting, actions_model.StatusBlocked, actions_model.StatusRunning:
- return api.CommitStatusPending
+ return commitstatus.CommitStatusPending
+ case actions_model.StatusSkipped:
+ return commitstatus.CommitStatusSkipped
default:
- return api.CommitStatusError
+ return commitstatus.CommitStatusError
}
}
diff --git a/services/actions/context.go b/services/actions/context.go
new file mode 100644
index 0000000000..b6de429ccf
--- /dev/null
+++ b/services/actions/context.go
@@ -0,0 +1,201 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package actions
+
+import (
+ "context"
+ "fmt"
+ "strconv"
+
+ actions_model "code.gitea.io/gitea/models/actions"
+ "code.gitea.io/gitea/models/db"
+ actions_module "code.gitea.io/gitea/modules/actions"
+ "code.gitea.io/gitea/modules/container"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+
+ "github.com/nektos/act/pkg/model"
+)
+
+type GiteaContext map[string]any
+
+// GenerateGiteaContext generate the gitea context without token and gitea_runtime_token
+// job can be nil when generating a context for parsing workflow-level expressions
+func GenerateGiteaContext(run *actions_model.ActionRun, job *actions_model.ActionRunJob) GiteaContext {
+ event := map[string]any{}
+ _ = json.Unmarshal([]byte(run.EventPayload), &event)
+
+ baseRef := ""
+ headRef := ""
+ ref := run.Ref
+ sha := run.CommitSHA
+ if pullPayload, err := run.GetPullRequestEventPayload(); err == nil && pullPayload.PullRequest != nil && pullPayload.PullRequest.Base != nil && pullPayload.PullRequest.Head != nil {
+ baseRef = pullPayload.PullRequest.Base.Ref
+ headRef = pullPayload.PullRequest.Head.Ref
+
+ // if the TriggerEvent is pull_request_target, ref and sha need to be set according to the base of pull request
+ // In GitHub's documentation, ref should be the branch or tag that triggered workflow. But when the TriggerEvent is pull_request_target,
+ // the ref will be the base branch.
+ if run.TriggerEvent == actions_module.GithubEventPullRequestTarget {
+ ref = git.BranchPrefix + pullPayload.PullRequest.Base.Name
+ sha = pullPayload.PullRequest.Base.Sha
+ }
+ }
+
+ refName := git.RefName(ref)
+
+ gitContext := GiteaContext{
+ // standard contexts, see https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
+ "action": "", // string, The name of the action currently running, or the id of a step. GitHub removes special characters, and uses the name __run when the current step runs a script without an id. If you use the same action more than once in the same job, the name will include a suffix with the sequence number with underscore before it. For example, the first script you run will have the name __run, and the second script will be named __run_2. Similarly, the second invocation of actions/checkout will be actionscheckout2.
+ "action_path": "", // string, The path where an action is located. This property is only supported in composite actions. You can use this path to access files located in the same repository as the action.
+ "action_ref": "", // string, For a step executing an action, this is the ref of the action being executed. For example, v2.
+ "action_repository": "", // string, For a step executing an action, this is the owner and repository name of the action. For example, actions/checkout.
+ "action_status": "", // string, For a composite action, the current result of the composite action.
+ "actor": run.TriggerUser.Name, // string, The username of the user that triggered the initial workflow run. If the workflow run is a re-run, this value may differ from github.triggering_actor. Any workflow re-runs will use the privileges of github.actor, even if the actor initiating the re-run (github.triggering_actor) has different privileges.
+ "api_url": setting.AppURL + "api/v1", // string, The URL of the GitHub REST API.
+ "base_ref": baseRef, // string, The base_ref or target branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target.
+ "env": "", // string, Path on the runner to the file that sets environment variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions."
+ "event": event, // object, The full event webhook payload. You can access individual properties of the event using this context. This object is identical to the webhook payload of the event that triggered the workflow run, and is different for each event. The webhooks for each GitHub Actions event is linked in "Events that trigger workflows." For example, for a workflow run triggered by the push event, this object contains the contents of the push webhook payload.
+ "event_name": run.TriggerEvent, // string, The name of the event that triggered the workflow run.
+ "event_path": "", // string, The path to the file on the runner that contains the full event webhook payload.
+ "graphql_url": "", // string, The URL of the GitHub GraphQL API.
+ "head_ref": headRef, // string, The head_ref or source branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target.
+ "job": "", // string, The job_id of the current job.
+ "ref": ref, // string, The fully-formed ref of the branch or tag that triggered the workflow run. For workflows triggered by push, this is the branch or tag ref that was pushed. For workflows triggered by pull_request, this is the pull request merge branch. For workflows triggered by release, this is the release tag created. For other triggers, this is the branch or tag ref that triggered the workflow run. This is only set if a branch or tag is available for the event type. The ref given is fully-formed, meaning that for branches the format is refs/heads/<branch_name>, for pull requests it is refs/pull/<pr_number>/merge, and for tags it is refs/tags/<tag_name>. For example, refs/heads/feature-branch-1.
+ "ref_name": refName.ShortName(), // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1.
+ "ref_protected": false, // boolean, true if branch protections are configured for the ref that triggered the workflow run.
+ "ref_type": string(refName.RefType()), // string, The type of ref that triggered the workflow run. Valid values are branch or tag.
+ "path": "", // string, Path on the runner to the file that sets system PATH variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions."
+ "repository": run.Repo.OwnerName + "/" + run.Repo.Name, // string, The owner and repository name. For example, Codertocat/Hello-World.
+ "repository_owner": run.Repo.OwnerName, // string, The repository owner's name. For example, Codertocat.
+ "repositoryUrl": run.Repo.HTMLURL(), // string, The Git URL to the repository. For example, git://github.com/codertocat/hello-world.git.
+ "retention_days": "", // string, The number of days that workflow run logs and artifacts are kept.
+ "run_id": "", // string, A unique number for each workflow run within a repository. This number does not change if you re-run the workflow run.
+ "run_number": strconv.FormatInt(run.Index, 10), // string, A unique number for each run of a particular workflow in a repository. This number begins at 1 for the workflow's first run, and increments with each new run. This number does not change if you re-run the workflow run.
+ "run_attempt": "", // string, A unique number for each attempt of a particular workflow run in a repository. This number begins at 1 for the workflow run's first attempt, and increments with each re-run.
+ "secret_source": "Actions", // string, The source of a secret used in a workflow. Possible values are None, Actions, Dependabot, or Codespaces.
+ "server_url": setting.AppURL, // string, The URL of the GitHub server. For example: https://github.com.
+ "sha": sha, // string, The commit SHA that triggered the workflow. The value of this commit SHA depends on the event that triggered the workflow. For more information, see "Events that trigger workflows." For example, ffac537e6cbbf934b08745a378932722df287a53.
+ "triggering_actor": "", // string, The username of the user that initiated the workflow run. If the workflow run is a re-run, this value may differ from github.actor. Any workflow re-runs will use the privileges of github.actor, even if the actor initiating the re-run (github.triggering_actor) has different privileges.
+ "workflow": run.WorkflowID, // string, The name of the workflow. If the workflow file doesn't specify a name, the value of this property is the full path of the workflow file in the repository.
+ "workspace": "", // string, The default working directory on the runner for steps, and the default location of your repository when using the checkout action.
+
+ // additional contexts
+ "gitea_default_actions_url": setting.Actions.DefaultActionsURL.URL(),
+ }
+
+ if job != nil {
+ gitContext["job"] = job.JobID
+ gitContext["run_id"] = strconv.FormatInt(job.RunID, 10)
+ gitContext["run_attempt"] = strconv.FormatInt(job.Attempt, 10)
+ }
+
+ return gitContext
+}
+
+type TaskNeed struct {
+ Result actions_model.Status
+ Outputs map[string]string
+}
+
+// FindTaskNeeds finds the `needs` for the task by the task's job
+func FindTaskNeeds(ctx context.Context, job *actions_model.ActionRunJob) (map[string]*TaskNeed, error) {
+ if len(job.Needs) == 0 {
+ return nil, nil
+ }
+ needs := container.SetOf(job.Needs...)
+
+ jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: job.RunID})
+ if err != nil {
+ return nil, fmt.Errorf("FindRunJobs: %w", err)
+ }
+
+ jobIDJobs := make(map[string][]*actions_model.ActionRunJob)
+ for _, job := range jobs {
+ jobIDJobs[job.JobID] = append(jobIDJobs[job.JobID], job)
+ }
+
+ ret := make(map[string]*TaskNeed, len(needs))
+ for jobID, jobsWithSameID := range jobIDJobs {
+ if !needs.Contains(jobID) {
+ continue
+ }
+ var jobOutputs map[string]string
+ for _, job := range jobsWithSameID {
+ if job.TaskID == 0 || !job.Status.IsDone() {
+ // it shouldn't happen, or the job has been rerun
+ continue
+ }
+ got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID)
+ if err != nil {
+ return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err)
+ }
+ outputs := make(map[string]string, len(got))
+ for _, v := range got {
+ outputs[v.OutputKey] = v.OutputValue
+ }
+ if len(jobOutputs) == 0 {
+ jobOutputs = outputs
+ } else {
+ jobOutputs = mergeTwoOutputs(outputs, jobOutputs)
+ }
+ }
+ ret[jobID] = &TaskNeed{
+ Outputs: jobOutputs,
+ Result: actions_model.AggregateJobStatus(jobsWithSameID),
+ }
+ }
+ return ret, nil
+}
+
+// mergeTwoOutputs merges two outputs from two different ActionRunJobs
+// Values with the same output name may be overridden. The user should ensure the output names are unique.
+// See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#using-job-outputs-in-a-matrix-job
+func mergeTwoOutputs(o1, o2 map[string]string) map[string]string {
+ ret := make(map[string]string, len(o1))
+ for k1, v1 := range o1 {
+ if len(v1) > 0 {
+ ret[k1] = v1
+ } else {
+ ret[k1] = o2[k1]
+ }
+ }
+ return ret
+}
+
+func (g *GiteaContext) ToGitHubContext() *model.GithubContext {
+ return &model.GithubContext{
+ Event: util.GetMapValueOrDefault(*g, "event", map[string]any(nil)),
+ EventPath: util.GetMapValueOrDefault(*g, "event_path", ""),
+ Workflow: util.GetMapValueOrDefault(*g, "workflow", ""),
+ RunID: util.GetMapValueOrDefault(*g, "run_id", ""),
+ RunNumber: util.GetMapValueOrDefault(*g, "run_number", ""),
+ Actor: util.GetMapValueOrDefault(*g, "actor", ""),
+ Repository: util.GetMapValueOrDefault(*g, "repository", ""),
+ EventName: util.GetMapValueOrDefault(*g, "event_name", ""),
+ Sha: util.GetMapValueOrDefault(*g, "sha", ""),
+ Ref: util.GetMapValueOrDefault(*g, "ref", ""),
+ RefName: util.GetMapValueOrDefault(*g, "ref_name", ""),
+ RefType: util.GetMapValueOrDefault(*g, "ref_type", ""),
+ HeadRef: util.GetMapValueOrDefault(*g, "head_ref", ""),
+ BaseRef: util.GetMapValueOrDefault(*g, "base_ref", ""),
+ Token: "", // deliberately omitted for security
+ Workspace: util.GetMapValueOrDefault(*g, "workspace", ""),
+ Action: util.GetMapValueOrDefault(*g, "action", ""),
+ ActionPath: util.GetMapValueOrDefault(*g, "action_path", ""),
+ ActionRef: util.GetMapValueOrDefault(*g, "action_ref", ""),
+ ActionRepository: util.GetMapValueOrDefault(*g, "action_repository", ""),
+ Job: util.GetMapValueOrDefault(*g, "job", ""),
+ JobName: "", // not present in GiteaContext
+ RepositoryOwner: util.GetMapValueOrDefault(*g, "repository_owner", ""),
+ RetentionDays: util.GetMapValueOrDefault(*g, "retention_days", ""),
+ RunnerPerflog: "", // not present in GiteaContext
+ RunnerTrackingID: "", // not present in GiteaContext
+ ServerURL: util.GetMapValueOrDefault(*g, "server_url", ""),
+ APIURL: util.GetMapValueOrDefault(*g, "api_url", ""),
+ GraphQLURL: util.GetMapValueOrDefault(*g, "graphql_url", ""),
+ }
+}
diff --git a/routers/api/actions/runner/utils_test.go b/services/actions/context_test.go
index d7a6f84550..74ef694021 100644
--- a/routers/api/actions/runner/utils_test.go
+++ b/services/actions/context_test.go
@@ -1,10 +1,9 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package runner
+package actions
import (
- "context"
"testing"
actions_model "code.gitea.io/gitea/models/actions"
@@ -13,12 +12,13 @@ import (
"github.com/stretchr/testify/assert"
)
-func Test_findTaskNeeds(t *testing.T) {
+func TestFindTaskNeeds(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 51})
+ job := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: task.JobID})
- ret, err := findTaskNeeds(context.Background(), task)
+ ret, err := FindTaskNeeds(t.Context(), job)
assert.NoError(t, err)
assert.Len(t, ret, 1)
assert.Contains(t, ret, "job1")
diff --git a/services/actions/init_test.go b/services/actions/init_test.go
index 59c321ccd7..7ef0702204 100644
--- a/services/actions/init_test.go
+++ b/services/actions/init_test.go
@@ -17,9 +17,7 @@ import (
)
func TestMain(m *testing.M) {
- unittest.MainTest(m, &unittest.TestOptions{
- FixtureFiles: []string{"action_runner_token.yml"},
- })
+ unittest.MainTest(m)
os.Exit(m.Run())
}
diff --git a/services/actions/interface.go b/services/actions/interface.go
index d4fa782fec..a054c38e4f 100644
--- a/services/actions/interface.go
+++ b/services/actions/interface.go
@@ -25,4 +25,16 @@ type API interface {
UpdateVariable(*context.APIContext)
// GetRegistrationToken get registration token
GetRegistrationToken(*context.APIContext)
+ // CreateRegistrationToken get registration token
+ CreateRegistrationToken(*context.APIContext)
+ // ListRunners list runners
+ ListRunners(*context.APIContext)
+ // GetRunner get a runner
+ GetRunner(*context.APIContext)
+ // DeleteRunner delete runner
+ DeleteRunner(*context.APIContext)
+ // ListWorkflowJobs list jobs
+ ListWorkflowJobs(*context.APIContext)
+ // ListWorkflowRuns list runs
+ ListWorkflowRuns(*context.APIContext)
}
diff --git a/services/actions/job_emitter.go b/services/actions/job_emitter.go
index 1f859fcf70..47c9f59094 100644
--- a/services/actions/job_emitter.go
+++ b/services/actions/job_emitter.go
@@ -11,7 +11,9 @@ import (
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/graceful"
+ "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/queue"
+ notify_service "code.gitea.io/gitea/services/notify"
"github.com/nektos/act/pkg/jobparser"
"xorm.io/builder"
@@ -49,6 +51,7 @@ func checkJobsOfRun(ctx context.Context, runID int64) error {
if err != nil {
return err
}
+ var updatedjobs []*actions_model.ActionRunJob
if err := db.WithTx(ctx, func(ctx context.Context) error {
idToJobs := make(map[string][]*actions_model.ActionRunJob, len(jobs))
for _, job := range jobs {
@@ -64,6 +67,7 @@ func checkJobsOfRun(ctx context.Context, runID int64) error {
} else if n != 1 {
return fmt.Errorf("no affected for updating blocked job %v", job.ID)
}
+ updatedjobs = append(updatedjobs, job)
}
}
return nil
@@ -71,9 +75,34 @@ func checkJobsOfRun(ctx context.Context, runID int64) error {
return err
}
CreateCommitStatus(ctx, jobs...)
+ for _, job := range updatedjobs {
+ _ = job.LoadAttributes(ctx)
+ notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil)
+ }
+ if len(jobs) > 0 {
+ runUpdated := true
+ for _, job := range jobs {
+ if !job.Status.IsDone() {
+ runUpdated = false
+ break
+ }
+ }
+ if runUpdated {
+ NotifyWorkflowRunStatusUpdateWithReload(ctx, jobs[0])
+ }
+ }
return nil
}
+func NotifyWorkflowRunStatusUpdateWithReload(ctx context.Context, job *actions_model.ActionRunJob) {
+ job.Run = nil
+ if err := job.LoadAttributes(ctx); err != nil {
+ log.Error("LoadAttributes: %v", err)
+ return
+ }
+ notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run)
+}
+
type jobStatusResolver struct {
statuses map[int64]actions_model.Status
needs map[int64][]int64
diff --git a/services/actions/notifier.go b/services/actions/notifier.go
index 67e33e7cce..d10cc0ab34 100644
--- a/services/actions/notifier.go
+++ b/services/actions/notifier.go
@@ -6,13 +6,16 @@ package actions
import (
"context"
+ actions_model "code.gitea.io/gitea/models/actions"
issues_model "code.gitea.io/gitea/models/issues"
+ "code.gitea.io/gitea/models/organization"
packages_model "code.gitea.io/gitea/models/packages"
perm_model "code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
@@ -532,7 +535,7 @@ func (n *actionsNotifier) PushCommits(ctx context.Context, pusher *user_model.Us
ctx = withMethod(ctx, "PushCommits")
apiPusher := convert.ToUser(ctx, pusher, nil)
- apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL())
+ apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo)
if err != nil {
log.Error("commits.ToAPIPayloadCommits failed: %v", err)
return
@@ -563,9 +566,9 @@ func (n *actionsNotifier) CreateRef(ctx context.Context, pusher *user_model.User
newNotifyInput(repo, pusher, webhook_module.HookEventCreate).
WithRef(refFullName.String()).
WithPayload(&api.CreatePayload{
- Ref: refFullName.String(),
+ Ref: refFullName.String(), // HINT: here is inconsistent with the Webhook's payload: webhook uses ShortName
Sha: refID,
- RefType: refFullName.RefType(),
+ RefType: string(refFullName.RefType()),
Repo: apiRepo,
Sender: apiPusher,
}).
@@ -580,8 +583,8 @@ func (n *actionsNotifier) DeleteRef(ctx context.Context, pusher *user_model.User
newNotifyInput(repo, pusher, webhook_module.HookEventDelete).
WithPayload(&api.DeletePayload{
- Ref: refFullName.String(),
- RefType: refFullName.RefType(),
+ Ref: refFullName.String(), // HINT: here is inconsistent with the Webhook's payload: webhook uses ShortName
+ RefType: string(refFullName.RefType()),
PusherType: api.PusherTypeUser,
Repo: apiRepo,
Sender: apiPusher,
@@ -593,7 +596,7 @@ func (n *actionsNotifier) SyncPushCommits(ctx context.Context, pusher *user_mode
ctx = withMethod(ctx, "SyncPushCommits")
apiPusher := convert.ToUser(ctx, pusher, nil)
- apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL())
+ apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo)
if err != nil {
log.Error("commits.ToAPIPayloadCommits failed: %v", err)
return
@@ -762,3 +765,41 @@ func (n *actionsNotifier) MigrateRepository(ctx context.Context, doer, u *user_m
Sender: convert.ToUser(ctx, doer, nil),
}).Notify(ctx)
}
+
+func (n *actionsNotifier) WorkflowRunStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, run *actions_model.ActionRun) {
+ ctx = withMethod(ctx, "WorkflowRunStatusUpdate")
+
+ var org *api.Organization
+ if repo.Owner.IsOrganization() {
+ org = convert.ToOrganization(ctx, organization.OrgFromUser(repo.Owner))
+ }
+
+ status := convert.ToWorkflowRunAction(run.Status)
+
+ gitRepo, err := gitrepo.OpenRepository(ctx, repo)
+ if err != nil {
+ log.Error("OpenRepository: %v", err)
+ return
+ }
+ defer gitRepo.Close()
+
+ convertedWorkflow, err := convert.GetActionWorkflow(ctx, gitRepo, repo, run.WorkflowID)
+ if err != nil {
+ log.Error("GetActionWorkflow: %v", err)
+ return
+ }
+ convertedRun, err := convert.ToActionWorkflowRun(ctx, repo, run)
+ if err != nil {
+ log.Error("ToActionWorkflowRun: %v", err)
+ return
+ }
+
+ newNotifyInput(repo, sender, webhook_module.HookEventWorkflowRun).WithPayload(&api.WorkflowRunPayload{
+ Action: status,
+ Workflow: convertedWorkflow,
+ WorkflowRun: convertedRun,
+ Organization: org,
+ Repo: convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm_model.AccessModeOwner}),
+ Sender: convert.ToUser(ctx, sender, nil),
+ }).Notify(ctx)
+}
diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go
index 323c6a76e4..8010f51a86 100644
--- a/services/actions/notifier_helper.go
+++ b/services/actions/notifier_helper.go
@@ -27,12 +27,15 @@ import (
api "code.gitea.io/gitea/modules/structs"
webhook_module "code.gitea.io/gitea/modules/webhook"
"code.gitea.io/gitea/services/convert"
+ notify_service "code.gitea.io/gitea/services/notify"
"github.com/nektos/act/pkg/jobparser"
"github.com/nektos/act/pkg/model"
)
-var methodCtxKey struct{}
+type methodCtxKeyType struct{}
+
+var methodCtxKey methodCtxKeyType
// withMethod sets the notification method that this context currently executes.
// Used for debugging/ troubleshooting purposes.
@@ -43,8 +46,7 @@ func withMethod(ctx context.Context, method string) context.Context {
return ctx
}
}
- // FIXME: review the use of this nolint directive
- return context.WithValue(ctx, methodCtxKey, method) //nolint:staticcheck
+ return context.WithValue(ctx, methodCtxKey, method)
}
// getMethod gets the notification method that this context currently executes.
@@ -117,7 +119,7 @@ func (input *notifyInput) Notify(ctx context.Context) {
func notify(ctx context.Context, input *notifyInput) error {
shouldDetectSchedules := input.Event == webhook_module.HookEventPush && input.Ref.BranchName() == input.Repo.DefaultBranch
- if input.Doer.IsActions() {
+ if input.Doer.IsGiteaActions() {
// avoiding triggering cyclically, for example:
// a comment of an issue will trigger the runner to add a new comment as reply,
// and the new comment will trigger the runner again.
@@ -136,7 +138,7 @@ func notify(ctx context.Context, input *notifyInput) error {
return nil
}
if unit_model.TypeActions.UnitGlobalDisabled() {
- if err := actions_model.CleanRepoScheduleTasks(ctx, input.Repo); err != nil {
+ if err := CleanRepoScheduleTasks(ctx, input.Repo); err != nil {
log.Error("CleanRepoScheduleTasks: %v", err)
}
return nil
@@ -177,7 +179,7 @@ func notify(ctx context.Context, input *notifyInput) error {
return fmt.Errorf("gitRepo.GetCommit: %w", err)
}
- if skipWorkflows(input, commit) {
+ if skipWorkflows(ctx, input, commit) {
return nil
}
@@ -242,7 +244,7 @@ func notify(ctx context.Context, input *notifyInput) error {
return handleWorkflows(ctx, detectedWorkflows, commit, input, ref.String())
}
-func skipWorkflows(input *notifyInput, commit *git.Commit) bool {
+func skipWorkflows(ctx context.Context, input *notifyInput, commit *git.Commit) bool {
// skip workflow runs with a configured skip-ci string in commit message or pr title if the event is push or pull_request(_sync)
// https://docs.github.com/en/actions/managing-workflow-runs/skipping-workflow-runs
skipWorkflowEvents := []webhook_module.HookEventType{
@@ -262,6 +264,27 @@ func skipWorkflows(input *notifyInput, commit *git.Commit) bool {
}
}
}
+ if input.Event == webhook_module.HookEventWorkflowRun {
+ wrun, ok := input.Payload.(*api.WorkflowRunPayload)
+ for i := 0; i < 5 && ok && wrun.WorkflowRun != nil; i++ {
+ if wrun.WorkflowRun.Event != "workflow_run" {
+ return false
+ }
+ r, err := actions_model.GetRunByRepoAndID(ctx, input.Repo.ID, wrun.WorkflowRun.ID)
+ if err != nil {
+ log.Error("GetRunByRepoAndID: %v", err)
+ return true
+ }
+ wrun, err = r.GetWorkflowRunEventPayload()
+ if err != nil {
+ log.Error("GetWorkflowRunEventPayload: %v", err)
+ return true
+ }
+ }
+ // skip workflow runs events exceeding the maxiumum of 5 recursive events
+ log.Debug("repo %s: skipped workflow_run because of recursive event of 5", input.Repo.RepoPath())
+ return true
+ }
return false
}
@@ -301,9 +324,11 @@ func handleWorkflows(
run := &actions_model.ActionRun{
Title: strings.SplitN(commit.CommitMessage, "\n", 2)[0],
RepoID: input.Repo.ID,
+ Repo: input.Repo,
OwnerID: input.Repo.OwnerID,
WorkflowID: dwf.EntryName,
TriggerUserID: input.Doer.ID,
+ TriggerUser: input.Doer,
Ref: ref,
CommitSHA: commit.ID.String(),
IsForkPullRequest: isForkPullRequest,
@@ -332,16 +357,22 @@ func handleWorkflows(
continue
}
- jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars))
+ giteaCtx := GenerateGiteaContext(run, nil)
+
+ jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars), jobparser.WithGitContext(giteaCtx.ToGitHubContext()))
if err != nil {
log.Error("jobparser.Parse: %v", err)
continue
}
+ if len(jobs) > 0 && jobs[0].RunName != "" {
+ run.Title = jobs[0].RunName
+ }
+
// cancel running jobs if the event is push or pull_request_sync
if run.Event == webhook_module.HookEventPush ||
run.Event == webhook_module.HookEventPullRequestSync {
- if err := actions_model.CancelPreviousJobs(
+ if err := CancelPreviousJobs(
ctx,
run.RepoID,
run.Ref,
@@ -363,6 +394,18 @@ func handleWorkflows(
continue
}
CreateCommitStatus(ctx, alljobs...)
+ if len(alljobs) > 0 {
+ job := alljobs[0]
+ err := job.LoadRun(ctx)
+ if err != nil {
+ log.Error("LoadRun: %v", err)
+ continue
+ }
+ notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run)
+ }
+ for _, job := range alljobs {
+ notify_service.WorkflowJobStatusUpdate(ctx, input.Repo, input.Doer, job, nil)
+ }
}
return nil
}
@@ -472,7 +515,7 @@ func handleSchedules(
log.Error("CountSchedules: %v", err)
return err
} else if count > 0 {
- if err := actions_model.CleanRepoScheduleTasks(ctx, input.Repo); err != nil {
+ if err := CleanRepoScheduleTasks(ctx, input.Repo); err != nil {
log.Error("CleanRepoScheduleTasks: %v", err)
}
}
@@ -504,9 +547,11 @@ func handleSchedules(
run := &actions_model.ActionSchedule{
Title: strings.SplitN(commit.CommitMessage, "\n", 2)[0],
RepoID: input.Repo.ID,
+ Repo: input.Repo,
OwnerID: input.Repo.OwnerID,
WorkflowID: dwf.EntryName,
TriggerUserID: user_model.ActionsUserID,
+ TriggerUser: user_model.NewActionsUser(),
Ref: ref,
CommitSHA: commit.ID.String(),
Event: input.Event,
@@ -514,6 +559,25 @@ func handleSchedules(
Specs: schedules,
Content: dwf.Content,
}
+
+ vars, err := actions_model.GetVariablesOfRun(ctx, run.ToActionRun())
+ if err != nil {
+ log.Error("GetVariablesOfRun: %v", err)
+ continue
+ }
+
+ giteaCtx := GenerateGiteaContext(run.ToActionRun(), nil)
+
+ jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars), jobparser.WithGitContext(giteaCtx.ToGitHubContext()))
+ if err != nil {
+ log.Error("jobparser.Parse: %v", err)
+ continue
+ }
+
+ if len(jobs) > 0 && jobs[0].RunName != "" {
+ run.Title = jobs[0].RunName
+ }
+
crons = append(crons, run)
}
diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go
index 18f3324fd2..c029c5a1a2 100644
--- a/services/actions/schedule_tasks.go
+++ b/services/actions/schedule_tasks.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
webhook_module "code.gitea.io/gitea/modules/webhook"
+ notify_service "code.gitea.io/gitea/services/notify"
"github.com/nektos/act/pkg/jobparser"
)
@@ -55,7 +56,7 @@ func startTasks(ctx context.Context) error {
// cancel running jobs if the event is push
if row.Schedule.Event == webhook_module.HookEventPush {
// cancel running jobs of the same workflow
- if err := actions_model.CancelPreviousJobs(
+ if err := CancelPreviousJobs(
ctx,
row.RepoID,
row.Schedule.Ref,
@@ -148,6 +149,18 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule)
if err := actions_model.InsertRun(ctx, run, workflows); err != nil {
return err
}
+ allJobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID})
+ if err != nil {
+ log.Error("FindRunJobs: %v", err)
+ }
+ err = run.LoadAttributes(ctx)
+ if err != nil {
+ log.Error("LoadAttributes: %v", err)
+ }
+ notify_service.WorkflowRunStatusUpdate(ctx, run.Repo, run.TriggerUser, run)
+ for _, job := range allJobs {
+ notify_service.WorkflowJobStatusUpdate(ctx, run.Repo, run.TriggerUser, job, nil)
+ }
// Return nil if no errors occurred
return nil
diff --git a/services/actions/task.go b/services/actions/task.go
new file mode 100644
index 0000000000..6a547c1c12
--- /dev/null
+++ b/services/actions/task.go
@@ -0,0 +1,132 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package actions
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ actions_model "code.gitea.io/gitea/models/actions"
+ "code.gitea.io/gitea/models/db"
+ secret_model "code.gitea.io/gitea/models/secret"
+ notify_service "code.gitea.io/gitea/services/notify"
+
+ runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
+ "google.golang.org/protobuf/types/known/structpb"
+)
+
+func PickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv1.Task, bool, error) {
+ var (
+ task *runnerv1.Task
+ job *actions_model.ActionRunJob
+ actionTask *actions_model.ActionTask
+ )
+
+ if runner.Ephemeral {
+ var task actions_model.ActionTask
+ has, err := db.GetEngine(ctx).Where("runner_id = ?", runner.ID).Get(&task)
+ // Let the runner retry the request, do not allow to proceed
+ if err != nil {
+ return nil, false, err
+ }
+ if has {
+ if task.Status == actions_model.StatusWaiting || task.Status == actions_model.StatusRunning || task.Status == actions_model.StatusBlocked {
+ return nil, false, nil
+ }
+ // task has been finished, remove it
+ _, err = db.DeleteByID[actions_model.ActionRunner](ctx, runner.ID)
+ if err != nil {
+ return nil, false, err
+ }
+ return nil, false, errors.New("runner has been removed")
+ }
+ }
+
+ if err := db.WithTx(ctx, func(ctx context.Context) error {
+ t, ok, err := actions_model.CreateTaskForRunner(ctx, runner)
+ if err != nil {
+ return fmt.Errorf("CreateTaskForRunner: %w", err)
+ }
+ if !ok {
+ return nil
+ }
+
+ if err := t.LoadAttributes(ctx); err != nil {
+ return fmt.Errorf("task LoadAttributes: %w", err)
+ }
+ job = t.Job
+ actionTask = t
+
+ secrets, err := secret_model.GetSecretsOfTask(ctx, t)
+ if err != nil {
+ return fmt.Errorf("GetSecretsOfTask: %w", err)
+ }
+
+ vars, err := actions_model.GetVariablesOfRun(ctx, t.Job.Run)
+ if err != nil {
+ return fmt.Errorf("GetVariablesOfRun: %w", err)
+ }
+
+ needs, err := findTaskNeeds(ctx, job)
+ if err != nil {
+ return fmt.Errorf("findTaskNeeds: %w", err)
+ }
+
+ taskContext, err := generateTaskContext(t)
+ if err != nil {
+ return fmt.Errorf("generateTaskContext: %w", err)
+ }
+
+ task = &runnerv1.Task{
+ Id: t.ID,
+ WorkflowPayload: t.Job.WorkflowPayload,
+ Context: taskContext,
+ Secrets: secrets,
+ Vars: vars,
+ Needs: needs,
+ }
+
+ return nil
+ }); err != nil {
+ return nil, false, err
+ }
+
+ if task == nil {
+ return nil, false, nil
+ }
+
+ CreateCommitStatus(ctx, job)
+ notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, actionTask)
+
+ return task, true, nil
+}
+
+func generateTaskContext(t *actions_model.ActionTask) (*structpb.Struct, error) {
+ giteaRuntimeToken, err := CreateAuthorizationToken(t.ID, t.Job.RunID, t.JobID)
+ if err != nil {
+ return nil, err
+ }
+
+ gitCtx := GenerateGiteaContext(t.Job.Run, t.Job)
+ gitCtx["token"] = t.Token
+ gitCtx["gitea_runtime_token"] = giteaRuntimeToken
+
+ return structpb.NewStruct(gitCtx)
+}
+
+func findTaskNeeds(ctx context.Context, taskJob *actions_model.ActionRunJob) (map[string]*runnerv1.TaskNeed, error) {
+ taskNeeds, err := FindTaskNeeds(ctx, taskJob)
+ if err != nil {
+ return nil, err
+ }
+ ret := make(map[string]*runnerv1.TaskNeed, len(taskNeeds))
+ for jobID, taskNeed := range taskNeeds {
+ ret[jobID] = &runnerv1.TaskNeed{
+ Outputs: taskNeed.Outputs,
+ Result: runnerv1.Result(taskNeed.Result),
+ }
+ }
+ return ret, nil
+}
diff --git a/services/actions/variables.go b/services/actions/variables.go
index 8dde9c4af5..2603f1d461 100644
--- a/services/actions/variables.go
+++ b/services/actions/variables.go
@@ -6,7 +6,6 @@ package actions
import (
"context"
"regexp"
- "strings"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/modules/log"
@@ -14,7 +13,7 @@ import (
secret_service "code.gitea.io/gitea/services/secrets"
)
-func CreateVariable(ctx context.Context, ownerID, repoID int64, name, data string) (*actions_model.ActionVariable, error) {
+func CreateVariable(ctx context.Context, ownerID, repoID int64, name, data, description string) (*actions_model.ActionVariable, error) {
if err := secret_service.ValidateName(name); err != nil {
return nil, err
}
@@ -23,7 +22,7 @@ func CreateVariable(ctx context.Context, ownerID, repoID int64, name, data strin
return nil, err
}
- v, err := actions_model.InsertVariable(ctx, ownerID, repoID, name, util.ReserveLineBreakForTextarea(data))
+ v, err := actions_model.InsertVariable(ctx, ownerID, repoID, name, util.ReserveLineBreakForTextarea(data), description)
if err != nil {
return nil, err
}
@@ -31,20 +30,18 @@ func CreateVariable(ctx context.Context, ownerID, repoID int64, name, data strin
return v, nil
}
-func UpdateVariable(ctx context.Context, variableID int64, name, data string) (bool, error) {
- if err := secret_service.ValidateName(name); err != nil {
+func UpdateVariableNameData(ctx context.Context, variable *actions_model.ActionVariable) (bool, error) {
+ if err := secret_service.ValidateName(variable.Name); err != nil {
return false, err
}
- if err := envNameCIRegexMatch(name); err != nil {
+ if err := envNameCIRegexMatch(variable.Name); err != nil {
return false, err
}
- return actions_model.UpdateVariable(ctx, &actions_model.ActionVariable{
- ID: variableID,
- Name: strings.ToUpper(name),
- Data: util.ReserveLineBreakForTextarea(data),
- })
+ variable.Data = util.ReserveLineBreakForTextarea(variable.Data)
+
+ return actions_model.UpdateVariableCols(ctx, variable, "name", "data", "description")
}
func DeleteVariableByID(ctx context.Context, variableID int64) error {
diff --git a/services/actions/workflow.go b/services/actions/workflow.go
new file mode 100644
index 0000000000..233e22b5dd
--- /dev/null
+++ b/services/actions/workflow.go
@@ -0,0 +1,219 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package actions
+
+import (
+ "fmt"
+ "strings"
+
+ actions_model "code.gitea.io/gitea/models/actions"
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/perm"
+ access_model "code.gitea.io/gitea/models/perm/access"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/actions"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/reqctx"
+ api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/convert"
+ notify_service "code.gitea.io/gitea/services/notify"
+
+ "github.com/nektos/act/pkg/jobparser"
+ "github.com/nektos/act/pkg/model"
+)
+
+func EnableOrDisableWorkflow(ctx *context.APIContext, workflowID string, isEnable bool) error {
+ workflow, err := convert.GetActionWorkflow(ctx, ctx.Repo.GitRepo, ctx.Repo.Repository, workflowID)
+ if err != nil {
+ return err
+ }
+
+ cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions)
+ cfg := cfgUnit.ActionsConfig()
+
+ if isEnable {
+ cfg.EnableWorkflow(workflow.ID)
+ } else {
+ cfg.DisableWorkflow(workflow.ID)
+ }
+
+ return repo_model.UpdateRepoUnit(ctx, cfgUnit)
+}
+
+func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, workflowID, ref string, processInputs func(model *model.WorkflowDispatch, inputs map[string]any) error) error {
+ if workflowID == "" {
+ return util.ErrorWrapLocale(
+ util.NewNotExistErrorf("workflowID is empty"),
+ "actions.workflow.not_found", workflowID,
+ )
+ }
+
+ if ref == "" {
+ return util.ErrorWrapLocale(
+ util.NewNotExistErrorf("ref is empty"),
+ "form.target_ref_not_exist", ref,
+ )
+ }
+
+ // can not rerun job when workflow is disabled
+ cfgUnit := repo.MustGetUnit(ctx, unit.TypeActions)
+ cfg := cfgUnit.ActionsConfig()
+ if cfg.IsWorkflowDisabled(workflowID) {
+ return util.ErrorWrapLocale(
+ util.NewPermissionDeniedErrorf("workflow is disabled"),
+ "actions.workflow.disabled",
+ )
+ }
+
+ // get target commit of run from specified ref
+ refName := git.RefName(ref)
+ var runTargetCommit *git.Commit
+ var err error
+ if refName.IsTag() {
+ runTargetCommit, err = gitRepo.GetTagCommit(refName.TagName())
+ } else if refName.IsBranch() {
+ runTargetCommit, err = gitRepo.GetBranchCommit(refName.BranchName())
+ } else {
+ refName = git.RefNameFromBranch(ref)
+ runTargetCommit, err = gitRepo.GetBranchCommit(ref)
+ }
+ if err != nil {
+ return util.ErrorWrapLocale(
+ util.NewNotExistErrorf("ref %q doesn't exist", ref),
+ "form.target_ref_not_exist", ref,
+ )
+ }
+
+ // get workflow entry from runTargetCommit
+ _, entries, err := actions.ListWorkflows(runTargetCommit)
+ if err != nil {
+ return err
+ }
+
+ // find workflow from commit
+ var workflows []*jobparser.SingleWorkflow
+ var entry *git.TreeEntry
+
+ run := &actions_model.ActionRun{
+ Title: strings.SplitN(runTargetCommit.CommitMessage, "\n", 2)[0],
+ RepoID: repo.ID,
+ Repo: repo,
+ OwnerID: repo.OwnerID,
+ WorkflowID: workflowID,
+ TriggerUserID: doer.ID,
+ TriggerUser: doer,
+ Ref: string(refName),
+ CommitSHA: runTargetCommit.ID.String(),
+ IsForkPullRequest: false,
+ Event: "workflow_dispatch",
+ TriggerEvent: "workflow_dispatch",
+ Status: actions_model.StatusWaiting,
+ }
+
+ for _, e := range entries {
+ if e.Name() != workflowID {
+ continue
+ }
+ entry = e
+ break
+ }
+
+ if entry == nil {
+ return util.ErrorWrapLocale(
+ util.NewNotExistErrorf("workflow %q doesn't exist", workflowID),
+ "actions.workflow.not_found", workflowID,
+ )
+ }
+
+ content, err := actions.GetContentFromEntry(entry)
+ if err != nil {
+ return err
+ }
+
+ giteaCtx := GenerateGiteaContext(run, nil)
+
+ workflows, err = jobparser.Parse(content, jobparser.WithGitContext(giteaCtx.ToGitHubContext()))
+ if err != nil {
+ return err
+ }
+
+ if len(workflows) > 0 && workflows[0].RunName != "" {
+ run.Title = workflows[0].RunName
+ }
+
+ if len(workflows) == 0 {
+ return util.ErrorWrapLocale(
+ util.NewNotExistErrorf("workflow %q doesn't exist", workflowID),
+ "actions.workflow.not_found", workflowID,
+ )
+ }
+
+ // get inputs from post
+ workflow := &model.Workflow{
+ RawOn: workflows[0].RawOn,
+ }
+ inputsWithDefaults := make(map[string]any)
+ if workflowDispatch := workflow.WorkflowDispatchConfig(); workflowDispatch != nil {
+ if err = processInputs(workflowDispatch, inputsWithDefaults); err != nil {
+ return err
+ }
+ }
+
+ // ctx.Req.PostForm -> WorkflowDispatchPayload.Inputs -> ActionRun.EventPayload -> runner: ghc.Event
+ // https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
+ // https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_dispatch
+ workflowDispatchPayload := &api.WorkflowDispatchPayload{
+ Workflow: workflowID,
+ Ref: ref,
+ Repository: convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeNone}),
+ Inputs: inputsWithDefaults,
+ Sender: convert.ToUserWithAccessMode(ctx, doer, perm.AccessModeNone),
+ }
+
+ var eventPayload []byte
+ if eventPayload, err = workflowDispatchPayload.JSONPayload(); err != nil {
+ return fmt.Errorf("JSONPayload: %w", err)
+ }
+ run.EventPayload = string(eventPayload)
+
+ // cancel running jobs of the same workflow
+ if err := CancelPreviousJobs(
+ ctx,
+ run.RepoID,
+ run.Ref,
+ run.WorkflowID,
+ run.Event,
+ ); err != nil {
+ log.Error("CancelRunningJobs: %v", err)
+ }
+
+ // Insert the action run and its associated jobs into the database
+ if err := actions_model.InsertRun(ctx, run, workflows); err != nil {
+ return fmt.Errorf("InsertRun: %w", err)
+ }
+
+ allJobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID})
+ if err != nil {
+ log.Error("FindRunJobs: %v", err)
+ }
+ CreateCommitStatus(ctx, allJobs...)
+ if len(allJobs) > 0 {
+ job := allJobs[0]
+ err := job.LoadRun(ctx)
+ if err != nil {
+ log.Error("LoadRun: %v", err)
+ } else {
+ notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run)
+ }
+ }
+ for _, job := range allJobs {
+ notify_service.WorkflowJobStatusUpdate(ctx, repo, doer, job, nil)
+ }
+ return nil
+}
diff --git a/services/agit/agit.go b/services/agit/agit.go
index 83b12dfcdb..b27dfc8ecd 100644
--- a/services/agit/agit.go
+++ b/services/agit/agit.go
@@ -9,10 +9,12 @@ import (
"os"
"strings"
+ git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
@@ -56,10 +58,10 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
baseBranchName := opts.RefFullNames[i].ForBranchName()
currentTopicBranch := ""
- if !gitRepo.IsBranchExist(baseBranchName) {
+ if !gitrepo.IsBranchExist(ctx, repo, baseBranchName) {
// try match refs/for/<target-branch>/<topic-branch>
for p, v := range baseBranchName {
- if v == '/' && gitRepo.IsBranchExist(baseBranchName[:p]) && p != len(baseBranchName)-1 {
+ if v == '/' && gitrepo.IsBranchExist(ctx, repo, baseBranchName[:p]) && p != len(baseBranchName)-1 {
currentTopicBranch = baseBranchName[p+1:]
baseBranchName = baseBranchName[:p]
break
@@ -153,7 +155,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
OriginalRef: opts.RefFullNames[i],
OldOID: objectFormat.EmptyObjectID().String(),
NewOID: opts.NewCommitIDs[i],
- IsCreatePR: true,
+ IsCreatePR: false, // AGit always creates a pull request so there is no point in prompting user to create one
URL: fmt.Sprintf("%s/pulls/%d", repo.HTMLURL(), pr.Index),
ShouldShowMessage: setting.Git.PullRequestPushMessage && repo.AllowsPulls(ctx),
HeadBranch: headBranch,
@@ -182,9 +184,9 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
}
if !forcePush.Value() {
- output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1").
+ output, _, err := git.NewCommand("rev-list", "--max-count=1").
AddDynamicArguments(oldCommitID, "^"+opts.NewCommitIDs[i]).
- RunStdString(&git.RunOpts{Dir: repo.RepoPath(), Env: os.Environ()})
+ RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath(), Env: os.Environ()})
if err != nil {
return nil, fmt.Errorf("failed to detect force push: %w", err)
} else if len(output) > 0 {
@@ -198,12 +200,38 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
}
}
+ // Store old commit ID for review staleness checking
+ oldHeadCommitID := pr.HeadCommitID
+
pr.HeadCommitID = opts.NewCommitIDs[i]
if err = pull_service.UpdateRef(ctx, pr); err != nil {
return nil, fmt.Errorf("failed to update pull ref. Error: %w", err)
}
- pull_service.AddToTaskQueue(ctx, pr)
+ // Mark existing reviews as stale when PR content changes (same as regular GitHub flow)
+ if oldHeadCommitID != opts.NewCommitIDs[i] {
+ if err := issues_model.MarkReviewsAsStale(ctx, pr.IssueID); err != nil {
+ log.Error("MarkReviewsAsStale: %v", err)
+ }
+
+ // Dismiss all approval reviews if protected branch rule item enabled
+ pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
+ if err != nil {
+ log.Error("GetFirstMatchProtectedBranchRule: %v", err)
+ }
+ if pb != nil && pb.DismissStaleApprovals {
+ if err := pull_service.DismissApprovalReviews(ctx, pusher, pr); err != nil {
+ log.Error("DismissApprovalReviews: %v", err)
+ }
+ }
+
+ // Mark reviews for the new commit as not stale
+ if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, opts.NewCommitIDs[i]); err != nil {
+ log.Error("MarkReviewsAsNotStale: %v", err)
+ }
+ }
+
+ pull_service.StartPullRequestCheckImmediately(ctx, pr)
err = pr.LoadIssue(ctx)
if err != nil {
return nil, fmt.Errorf("failed to load pull issue. Error: %w", err)
diff --git a/services/asymkey/commit.go b/services/asymkey/commit.go
new file mode 100644
index 0000000000..148f51fd10
--- /dev/null
+++ b/services/asymkey/commit.go
@@ -0,0 +1,475 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package asymkey
+
+import (
+ "context"
+ "fmt"
+ "slices"
+ "strings"
+
+ asymkey_model "code.gitea.io/gitea/models/asymkey"
+ "code.gitea.io/gitea/models/db"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/cache"
+ "code.gitea.io/gitea/modules/cachegroup"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+
+ "github.com/42wim/sshsig"
+ "github.com/ProtonMail/go-crypto/openpgp/packet"
+)
+
+// ParseCommitWithSignature check if signature is good against keystore.
+func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *asymkey_model.CommitVerification {
+ var committer *user_model.User
+ if c.Committer != nil {
+ var err error
+ // Find Committer account
+ committer, err = user_model.GetUserByEmail(ctx, c.Committer.Email) // This finds the user by primary email or activated email so commit will not be valid if email is not
+ if err != nil { // Skipping not user for committer
+ committer = &user_model.User{
+ Name: c.Committer.Name,
+ Email: c.Committer.Email,
+ }
+ // We can expect this to often be an ErrUserNotExist. in the case
+ // it is not, however, it is important to log it.
+ if !user_model.IsErrUserNotExist(err) {
+ log.Error("GetUserByEmail: %v", err)
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committer,
+ Verified: false,
+ Reason: "gpg.error.no_committer_account",
+ }
+ }
+ }
+ }
+
+ return ParseCommitWithSignatureCommitter(ctx, c, committer)
+}
+
+func ParseCommitWithSignatureCommitter(ctx context.Context, c *git.Commit, committer *user_model.User) *asymkey_model.CommitVerification {
+ // If no signature just report the committer
+ if c.Signature == nil {
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committer,
+ Verified: false, // Default value
+ Reason: "gpg.error.not_signed_commit", // Default value
+ }
+ }
+
+ // If this a SSH signature handle it differently
+ if strings.HasPrefix(c.Signature.Signature, "-----BEGIN SSH SIGNATURE-----") {
+ return ParseCommitWithSSHSignature(ctx, c, committer)
+ }
+
+ // Parsing signature
+ sig, err := asymkey_model.ExtractSignature(c.Signature.Signature)
+ if err != nil { // Skipping failed to extract sign
+ log.Error("SignatureRead err: %v", err)
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committer,
+ Verified: false,
+ Reason: "gpg.error.extract_sign",
+ }
+ }
+
+ keyID := asymkey_model.TryGetKeyIDFromSignature(sig)
+ defaultReason := asymkey_model.NoKeyFound
+
+ // First check if the sig has a keyID and if so just look at that
+ if commitVerification := HashAndVerifyForKeyID(
+ ctx,
+ sig,
+ c.Signature.Payload,
+ committer,
+ keyID,
+ setting.AppName,
+ ""); commitVerification != nil {
+ if commitVerification.Reason == asymkey_model.BadSignature {
+ defaultReason = asymkey_model.BadSignature
+ } else {
+ return commitVerification
+ }
+ }
+
+ // Now try to associate the signature with the committer, if present
+ if committer.ID != 0 {
+ keys, err := db.Find[asymkey_model.GPGKey](ctx, asymkey_model.FindGPGKeyOptions{
+ OwnerID: committer.ID,
+ })
+ if err != nil { // Skipping failed to get gpg keys of user
+ log.Error("ListGPGKeys: %v", err)
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committer,
+ Verified: false,
+ Reason: "gpg.error.failed_retrieval_gpg_keys",
+ }
+ }
+
+ if err := asymkey_model.GPGKeyList(keys).LoadSubKeys(ctx); err != nil {
+ log.Error("LoadSubKeys: %v", err)
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committer,
+ Verified: false,
+ Reason: "gpg.error.failed_retrieval_gpg_keys",
+ }
+ }
+
+ committerEmailAddresses, _ := cache.GetWithContextCache(ctx, cachegroup.UserEmailAddresses, committer.ID, user_model.GetEmailAddresses)
+ activated := false
+ for _, e := range committerEmailAddresses {
+ if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
+ activated = true
+ break
+ }
+ }
+
+ for _, k := range keys {
+ // Pre-check (& optimization) that emails attached to key can be attached to the committer email and can validate
+ canValidate := false
+ email := ""
+ if k.Verified && activated {
+ canValidate = true
+ email = c.Committer.Email
+ }
+ if !canValidate {
+ for _, e := range k.Emails {
+ if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
+ canValidate = true
+ email = e.Email
+ break
+ }
+ }
+ }
+ if !canValidate {
+ continue // Skip this key
+ }
+
+ commitVerification := asymkey_model.HashAndVerifyWithSubKeysCommitVerification(sig, c.Signature.Payload, k, committer, committer, email)
+ if commitVerification != nil {
+ return commitVerification
+ }
+ }
+ }
+
+ if setting.Repository.Signing.SigningKey != "" && setting.Repository.Signing.SigningKey != "default" && setting.Repository.Signing.SigningKey != "none" {
+ // OK we should try the default key
+ gpgSettings := git.GPGSettings{
+ Sign: true,
+ KeyID: setting.Repository.Signing.SigningKey,
+ Name: setting.Repository.Signing.SigningName,
+ Email: setting.Repository.Signing.SigningEmail,
+ }
+ if err := gpgSettings.LoadPublicKeyContent(); err != nil {
+ log.Error("Error getting default signing key: %s %v", gpgSettings.KeyID, err)
+ } else if commitVerification := VerifyWithGPGSettings(ctx, &gpgSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
+ if commitVerification.Reason == asymkey_model.BadSignature {
+ defaultReason = asymkey_model.BadSignature
+ } else {
+ return commitVerification
+ }
+ }
+ }
+
+ defaultGPGSettings, err := c.GetRepositoryDefaultPublicGPGKey(false)
+ if err != nil {
+ log.Error("Error getting default public gpg key: %v", err)
+ } else if defaultGPGSettings == nil {
+ log.Warn("Unable to get defaultGPGSettings for unattached commit: %s", c.ID.String())
+ } else if defaultGPGSettings.Sign {
+ if commitVerification := VerifyWithGPGSettings(ctx, defaultGPGSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
+ if commitVerification.Reason == asymkey_model.BadSignature {
+ defaultReason = asymkey_model.BadSignature
+ } else {
+ return commitVerification
+ }
+ }
+ }
+
+ return &asymkey_model.CommitVerification{ // Default at this stage
+ CommittingUser: committer,
+ Verified: false,
+ Warning: defaultReason != asymkey_model.NoKeyFound,
+ Reason: defaultReason,
+ SigningKey: &asymkey_model.GPGKey{
+ KeyID: keyID,
+ },
+ }
+}
+
+func checkKeyEmails(ctx context.Context, email string, keys ...*asymkey_model.GPGKey) (bool, string) {
+ uid := int64(0)
+ var userEmails []*user_model.EmailAddress
+ var user *user_model.User
+ for _, key := range keys {
+ for _, e := range key.Emails {
+ if e.IsActivated && (email == "" || strings.EqualFold(e.Email, email)) {
+ return true, e.Email
+ }
+ }
+ if key.Verified && key.OwnerID != 0 {
+ if uid != key.OwnerID {
+ userEmails, _ = cache.GetWithContextCache(ctx, cachegroup.UserEmailAddresses, key.OwnerID, user_model.GetEmailAddresses)
+ uid = key.OwnerID
+ user, _ = cache.GetWithContextCache(ctx, cachegroup.User, uid, user_model.GetUserByID)
+ }
+ for _, e := range userEmails {
+ if e.IsActivated && (email == "" || strings.EqualFold(e.Email, email)) {
+ return true, e.Email
+ }
+ }
+ if user.KeepEmailPrivate && strings.EqualFold(email, user.GetEmail()) {
+ return true, user.GetEmail()
+ }
+ }
+ }
+ return false, email
+}
+
+func HashAndVerifyForKeyID(ctx context.Context, sig *packet.Signature, payload string, committer *user_model.User, keyID, name, email string) *asymkey_model.CommitVerification {
+ if keyID == "" {
+ return nil
+ }
+ keys, err := cache.GetWithContextCache(ctx, cachegroup.GPGKeyWithSubKeys, keyID, asymkey_model.FindGPGKeyWithSubKeys)
+ if err != nil {
+ log.Error("GetGPGKeysByKeyID: %v", err)
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committer,
+ Verified: false,
+ Reason: "gpg.error.failed_retrieval_gpg_keys",
+ }
+ }
+ if len(keys) == 0 {
+ return nil
+ }
+ for _, key := range keys {
+ var primaryKeys []*asymkey_model.GPGKey
+ if key.PrimaryKeyID != "" {
+ primaryKeys, err = cache.GetWithContextCache(ctx, cachegroup.GPGKeyWithSubKeys, key.PrimaryKeyID, asymkey_model.FindGPGKeyWithSubKeys)
+ if err != nil {
+ log.Error("GetGPGKeysByKeyID: %v", err)
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committer,
+ Verified: false,
+ Reason: "gpg.error.failed_retrieval_gpg_keys",
+ }
+ }
+ }
+
+ activated, email := checkKeyEmails(ctx, email, append([]*asymkey_model.GPGKey{key}, primaryKeys...)...)
+ if !activated {
+ continue
+ }
+
+ signer := &user_model.User{
+ Name: name,
+ Email: email,
+ }
+ if key.OwnerID > 0 {
+ owner, err := cache.GetWithContextCache(ctx, cachegroup.User, key.OwnerID, user_model.GetUserByID)
+ if err == nil {
+ signer = owner
+ } else if !user_model.IsErrUserNotExist(err) {
+ log.Error("Failed to user_model.GetUserByID: %d for key ID: %d (%s) %v", key.OwnerID, key.ID, key.KeyID, err)
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committer,
+ Verified: false,
+ Reason: "gpg.error.no_committer_account",
+ }
+ }
+ }
+ commitVerification := asymkey_model.HashAndVerifyWithSubKeysCommitVerification(sig, payload, key, committer, signer, email)
+ if commitVerification != nil {
+ return commitVerification
+ }
+ }
+ // This is a bad situation ... We have a key id that is in our database but the signature doesn't match.
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committer,
+ Verified: false,
+ Warning: true,
+ Reason: asymkey_model.BadSignature,
+ }
+}
+
+func VerifyWithGPGSettings(ctx context.Context, gpgSettings *git.GPGSettings, sig *packet.Signature, payload string, committer *user_model.User, keyID string) *asymkey_model.CommitVerification {
+ // First try to find the key in the db
+ if commitVerification := HashAndVerifyForKeyID(ctx, sig, payload, committer, gpgSettings.KeyID, gpgSettings.Name, gpgSettings.Email); commitVerification != nil {
+ return commitVerification
+ }
+
+ // Otherwise we have to parse the key
+ ekeys, err := asymkey_model.CheckArmoredGPGKeyString(gpgSettings.PublicKeyContent)
+ if err != nil {
+ log.Error("Unable to get default signing key: %v", err)
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committer,
+ Verified: false,
+ Reason: "gpg.error.generate_hash",
+ }
+ }
+ for _, ekey := range ekeys {
+ pubkey := ekey.PrimaryKey
+ content, err := asymkey_model.Base64EncPubKey(pubkey)
+ if err != nil {
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committer,
+ Verified: false,
+ Reason: "gpg.error.generate_hash",
+ }
+ }
+ k := &asymkey_model.GPGKey{
+ Content: content,
+ CanSign: pubkey.CanSign(),
+ KeyID: pubkey.KeyIdString(),
+ }
+ for _, subKey := range ekey.Subkeys {
+ content, err := asymkey_model.Base64EncPubKey(subKey.PublicKey)
+ if err != nil {
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committer,
+ Verified: false,
+ Reason: "gpg.error.generate_hash",
+ }
+ }
+ k.SubsKey = append(k.SubsKey, &asymkey_model.GPGKey{
+ Content: content,
+ CanSign: subKey.PublicKey.CanSign(),
+ KeyID: subKey.PublicKey.KeyIdString(),
+ })
+ }
+ if commitVerification := asymkey_model.HashAndVerifyWithSubKeysCommitVerification(sig, payload, k, committer, &user_model.User{
+ Name: gpgSettings.Name,
+ Email: gpgSettings.Email,
+ }, gpgSettings.Email); commitVerification != nil {
+ return commitVerification
+ }
+ if keyID == k.KeyID {
+ // This is a bad situation ... We have a key id that matches our default key but the signature doesn't match.
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committer,
+ Verified: false,
+ Warning: true,
+ Reason: asymkey_model.BadSignature,
+ }
+ }
+ }
+ return nil
+}
+
+func verifySSHCommitVerificationByInstanceKey(c *git.Commit, committerUser, signerUser *user_model.User, committerGitEmail, publicKeyContent string) *asymkey_model.CommitVerification {
+ fingerprint, err := asymkey_model.CalcFingerprint(publicKeyContent)
+ if err != nil {
+ log.Error("Error calculating the fingerprint public key %q, err: %v", publicKeyContent, err)
+ return nil
+ }
+ sshPubKey := &asymkey_model.PublicKey{
+ Verified: true,
+ Content: publicKeyContent,
+ Fingerprint: fingerprint,
+ HasUsed: true,
+ }
+ return verifySSHCommitVerification(c.Signature.Signature, c.Signature.Payload, sshPubKey, committerUser, signerUser, committerGitEmail)
+}
+
+// ParseCommitWithSSHSignature check if signature is good against keystore.
+func ParseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committerUser *user_model.User) *asymkey_model.CommitVerification {
+ // Now try to associate the signature with the committer, if present
+ if committerUser.ID != 0 {
+ keys, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{
+ OwnerID: committerUser.ID,
+ NotKeytype: asymkey_model.KeyTypePrincipal,
+ })
+ if err != nil { // Skipping failed to get ssh keys of user
+ log.Error("ListPublicKeys: %v", err)
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committerUser,
+ Verified: false,
+ Reason: "gpg.error.failed_retrieval_gpg_keys",
+ }
+ }
+
+ committerEmailAddresses, err := cache.GetWithContextCache(ctx, cachegroup.UserEmailAddresses, committerUser.ID, user_model.GetEmailAddresses)
+ if err != nil {
+ log.Error("GetEmailAddresses: %v", err)
+ }
+
+ activated := false
+ for _, e := range committerEmailAddresses {
+ if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
+ activated = true
+ break
+ }
+ }
+
+ for _, k := range keys {
+ if k.Verified && activated {
+ commitVerification := verifySSHCommitVerification(c.Signature.Signature, c.Signature.Payload, k, committerUser, committerUser, c.Committer.Email)
+ if commitVerification != nil {
+ return commitVerification
+ }
+ }
+ }
+ }
+
+ // Try the pre-set trusted keys (for key-rotation purpose)
+ // At the moment, we still use the SigningName&SigningEmail for the rotated keys.
+ // Maybe in the future we can extend the key format to "ssh-xxx .... old-user@example.com" to support different signer emails.
+ for _, k := range setting.Repository.Signing.TrustedSSHKeys {
+ signerUser := &user_model.User{
+ Name: setting.Repository.Signing.SigningName,
+ Email: setting.Repository.Signing.SigningEmail,
+ }
+ commitVerification := verifySSHCommitVerificationByInstanceKey(c, committerUser, signerUser, c.Committer.Email, k)
+ if commitVerification != nil && commitVerification.Verified {
+ return commitVerification
+ }
+ }
+
+ // Try the configured instance-wide SSH public key
+ if setting.Repository.Signing.SigningFormat == git.SigningKeyFormatSSH && !slices.Contains([]string{"", "default", "none"}, setting.Repository.Signing.SigningKey) {
+ gpgSettings := git.GPGSettings{
+ Sign: true,
+ KeyID: setting.Repository.Signing.SigningKey,
+ Name: setting.Repository.Signing.SigningName,
+ Email: setting.Repository.Signing.SigningEmail,
+ Format: setting.Repository.Signing.SigningFormat,
+ }
+ signerUser := &user_model.User{
+ Name: gpgSettings.Name,
+ Email: gpgSettings.Email,
+ }
+ if err := gpgSettings.LoadPublicKeyContent(); err != nil {
+ log.Error("Error getting instance-wide SSH signing key %q, err: %v", gpgSettings.KeyID, err)
+ } else {
+ commitVerification := verifySSHCommitVerificationByInstanceKey(c, committerUser, signerUser, gpgSettings.Email, gpgSettings.PublicKeyContent)
+ if commitVerification != nil && commitVerification.Verified {
+ return commitVerification
+ }
+ }
+ }
+
+ return &asymkey_model.CommitVerification{
+ CommittingUser: committerUser,
+ Verified: false,
+ Reason: asymkey_model.NoKeyFound,
+ }
+}
+
+func verifySSHCommitVerification(sig, payload string, k *asymkey_model.PublicKey, committer, signer *user_model.User, email string) *asymkey_model.CommitVerification {
+ if err := sshsig.Verify(strings.NewReader(payload), []byte(sig), []byte(k.Content), "git"); err != nil {
+ return nil
+ }
+
+ return &asymkey_model.CommitVerification{ // Everything is ok
+ CommittingUser: committer,
+ Verified: true,
+ Reason: fmt.Sprintf("%s / %s", signer.Name, k.Fingerprint),
+ SigningUser: signer,
+ SigningSSHKey: k,
+ SigningEmail: email,
+ }
+}
diff --git a/services/asymkey/commit_test.go b/services/asymkey/commit_test.go
new file mode 100644
index 0000000000..0438209a61
--- /dev/null
+++ b/services/asymkey/commit_test.go
@@ -0,0 +1,54 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package asymkey
+
+import (
+ "strings"
+ "testing"
+
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestParseCommitWithSSHSignature(t *testing.T) {
+ // Here we only test the TrustedSSHKeys. The complete signing test is in tests/integration/gpg_ssh_git_test.go
+ t.Run("TrustedSSHKey", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Repository.Signing.SigningName, "gitea")()
+ defer test.MockVariableValue(&setting.Repository.Signing.SigningEmail, "gitea@fake.local")()
+ defer test.MockVariableValue(&setting.Repository.Signing.TrustedSSHKeys, []string{"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH6Y4idVaW3E+bLw1uqoAfJD7o5Siu+HqS51E9oQLPE9"})()
+
+ commit, err := git.CommitFromReader(nil, git.Sha1ObjectFormat.EmptyObjectID(), strings.NewReader(`tree 9a93ffa76e8b72bdb6431910b3a506fa2b39f42e
+author User Two <user2@example.com> 1749230009 +0200
+committer User Two <user2@example.com> 1749230009 +0200
+gpgsig -----BEGIN SSH SIGNATURE-----
+ U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgfpjiJ1VpbcT5svDW6qgB8kPujl
+ KK74epLnUT2hAs8T0AAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
+ AAAAQDX2t2iHuuLxEWHLJetYXKsgayv3c43r0pJNfAzdLN55Q65pC5M7rG6++gT2bxcpOu
+ Y6EXbpLqia9sunEF3+LQY=
+ -----END SSH SIGNATURE-----
+
+Initial commit with signed file
+`))
+ require.NoError(t, err)
+ committingUser := &user_model.User{
+ ID: 2,
+ Name: "User Two",
+ Email: "user2@example.com",
+ }
+ ret := ParseCommitWithSSHSignature(t.Context(), commit, committingUser)
+ require.NotNil(t, ret)
+ assert.True(t, ret.Verified)
+ assert.False(t, ret.Warning)
+ assert.Equal(t, committingUser, ret.CommittingUser)
+ if assert.NotNil(t, ret.SigningUser) {
+ assert.Equal(t, "gitea", ret.SigningUser.Name)
+ assert.Equal(t, "gitea@fake.local", ret.SigningUser.Email)
+ }
+ })
+}
diff --git a/services/asymkey/sign.go b/services/asymkey/sign.go
index 2f5d76a293..f94462ea46 100644
--- a/services/asymkey/sign.go
+++ b/services/asymkey/sign.go
@@ -6,6 +6,7 @@ package asymkey
import (
"context"
"fmt"
+ "os"
"strings"
asymkey_model "code.gitea.io/gitea/models/asymkey"
@@ -85,63 +86,87 @@ func IsErrWontSign(err error) bool {
}
// SigningKey returns the KeyID and git Signature for the repo
-func SigningKey(ctx context.Context, repoPath string) (string, *git.Signature) {
+func SigningKey(ctx context.Context, repoPath string) (*git.SigningKey, *git.Signature) {
if setting.Repository.Signing.SigningKey == "none" {
- return "", nil
+ return nil, nil
}
if setting.Repository.Signing.SigningKey == "default" || setting.Repository.Signing.SigningKey == "" {
// Can ignore the error here as it means that commit.gpgsign is not set
- value, _, _ := git.NewCommand(ctx, "config", "--get", "commit.gpgsign").RunStdString(&git.RunOpts{Dir: repoPath})
+ value, _, _ := git.NewCommand("config", "--get", "commit.gpgsign").RunStdString(ctx, &git.RunOpts{Dir: repoPath})
sign, valid := git.ParseBool(strings.TrimSpace(value))
if !sign || !valid {
- return "", nil
+ return nil, nil
}
- signingKey, _, _ := git.NewCommand(ctx, "config", "--get", "user.signingkey").RunStdString(&git.RunOpts{Dir: repoPath})
- signingName, _, _ := git.NewCommand(ctx, "config", "--get", "user.name").RunStdString(&git.RunOpts{Dir: repoPath})
- signingEmail, _, _ := git.NewCommand(ctx, "config", "--get", "user.email").RunStdString(&git.RunOpts{Dir: repoPath})
- return strings.TrimSpace(signingKey), &git.Signature{
- Name: strings.TrimSpace(signingName),
- Email: strings.TrimSpace(signingEmail),
+ format, _, _ := git.NewCommand("config", "--default", git.SigningKeyFormatOpenPGP, "--get", "gpg.format").RunStdString(ctx, &git.RunOpts{Dir: repoPath})
+ signingKey, _, _ := git.NewCommand("config", "--get", "user.signingkey").RunStdString(ctx, &git.RunOpts{Dir: repoPath})
+ signingName, _, _ := git.NewCommand("config", "--get", "user.name").RunStdString(ctx, &git.RunOpts{Dir: repoPath})
+ signingEmail, _, _ := git.NewCommand("config", "--get", "user.email").RunStdString(ctx, &git.RunOpts{Dir: repoPath})
+
+ if strings.TrimSpace(signingKey) == "" {
+ return nil, nil
}
+
+ return &git.SigningKey{
+ KeyID: strings.TrimSpace(signingKey),
+ Format: strings.TrimSpace(format),
+ }, &git.Signature{
+ Name: strings.TrimSpace(signingName),
+ Email: strings.TrimSpace(signingEmail),
+ }
}
- return setting.Repository.Signing.SigningKey, &git.Signature{
- Name: setting.Repository.Signing.SigningName,
- Email: setting.Repository.Signing.SigningEmail,
+ if setting.Repository.Signing.SigningKey == "" {
+ return nil, nil
}
+
+ return &git.SigningKey{
+ KeyID: setting.Repository.Signing.SigningKey,
+ Format: setting.Repository.Signing.SigningFormat,
+ }, &git.Signature{
+ Name: setting.Repository.Signing.SigningName,
+ Email: setting.Repository.Signing.SigningEmail,
+ }
}
// PublicSigningKey gets the public signing key within a provided repository directory
-func PublicSigningKey(ctx context.Context, repoPath string) (string, error) {
+func PublicSigningKey(ctx context.Context, repoPath string) (content, format string, err error) {
signingKey, _ := SigningKey(ctx, repoPath)
- if signingKey == "" {
- return "", nil
+ if signingKey == nil {
+ return "", "", nil
+ }
+ if signingKey.Format == git.SigningKeyFormatSSH {
+ content, err := os.ReadFile(signingKey.KeyID)
+ if err != nil {
+ log.Error("Unable to read SSH public key file in %s: %s, %v", repoPath, signingKey, err)
+ return "", signingKey.Format, err
+ }
+ return string(content), signingKey.Format, nil
}
content, stderr, err := process.GetManager().ExecDir(ctx, -1, repoPath,
- "gpg --export -a", "gpg", "--export", "-a", signingKey)
+ "gpg --export -a", "gpg", "--export", "-a", signingKey.KeyID)
if err != nil {
log.Error("Unable to get default signing key in %s: %s, %s, %v", repoPath, signingKey, stderr, err)
- return "", err
+ return "", signingKey.Format, err
}
- return content, nil
+ return content, signingKey.Format, nil
}
// SignInitialCommit determines if we should sign the initial commit to this repository
-func SignInitialCommit(ctx context.Context, repoPath string, u *user_model.User) (bool, string, *git.Signature, error) {
+func SignInitialCommit(ctx context.Context, repoPath string, u *user_model.User) (bool, *git.SigningKey, *git.Signature, error) {
rules := signingModeFromStrings(setting.Repository.Signing.InitialCommit)
signingKey, sig := SigningKey(ctx, repoPath)
- if signingKey == "" {
- return false, "", nil, &ErrWontSign{noKey}
+ if signingKey == nil {
+ return false, nil, nil, &ErrWontSign{noKey}
}
Loop:
for _, rule := range rules {
switch rule {
case never:
- return false, "", nil, &ErrWontSign{never}
+ return false, nil, nil, &ErrWontSign{never}
case always:
break Loop
case pubkey:
@@ -150,18 +175,18 @@ Loop:
IncludeSubKeys: true,
})
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
if len(keys) == 0 {
- return false, "", nil, &ErrWontSign{pubkey}
+ return false, nil, nil, &ErrWontSign{pubkey}
}
case twofa:
twofaModel, err := auth.GetTwoFactorByUID(ctx, u.ID)
if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
- return false, "", nil, err
+ return false, nil, nil, err
}
if twofaModel == nil {
- return false, "", nil, &ErrWontSign{twofa}
+ return false, nil, nil, &ErrWontSign{twofa}
}
}
}
@@ -169,19 +194,19 @@ Loop:
}
// SignWikiCommit determines if we should sign the commits to this repository wiki
-func SignWikiCommit(ctx context.Context, repo *repo_model.Repository, u *user_model.User) (bool, string, *git.Signature, error) {
+func SignWikiCommit(ctx context.Context, repo *repo_model.Repository, u *user_model.User) (bool, *git.SigningKey, *git.Signature, error) {
repoWikiPath := repo.WikiPath()
rules := signingModeFromStrings(setting.Repository.Signing.Wiki)
signingKey, sig := SigningKey(ctx, repoWikiPath)
- if signingKey == "" {
- return false, "", nil, &ErrWontSign{noKey}
+ if signingKey == nil {
+ return false, nil, nil, &ErrWontSign{noKey}
}
Loop:
for _, rule := range rules {
switch rule {
case never:
- return false, "", nil, &ErrWontSign{never}
+ return false, nil, nil, &ErrWontSign{never}
case always:
break Loop
case pubkey:
@@ -190,35 +215,35 @@ Loop:
IncludeSubKeys: true,
})
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
if len(keys) == 0 {
- return false, "", nil, &ErrWontSign{pubkey}
+ return false, nil, nil, &ErrWontSign{pubkey}
}
case twofa:
twofaModel, err := auth.GetTwoFactorByUID(ctx, u.ID)
if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
- return false, "", nil, err
+ return false, nil, nil, err
}
if twofaModel == nil {
- return false, "", nil, &ErrWontSign{twofa}
+ return false, nil, nil, &ErrWontSign{twofa}
}
case parentSigned:
- gitRepo, err := gitrepo.OpenWikiRepository(ctx, repo)
+ gitRepo, err := gitrepo.OpenRepository(ctx, repo.WikiStorageRepo())
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
defer gitRepo.Close()
commit, err := gitRepo.GetCommit("HEAD")
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
if commit.Signature == nil {
- return false, "", nil, &ErrWontSign{parentSigned}
+ return false, nil, nil, &ErrWontSign{parentSigned}
}
- verification := asymkey_model.ParseCommitWithSignature(ctx, commit)
+ verification := ParseCommitWithSignature(ctx, commit)
if !verification.Verified {
- return false, "", nil, &ErrWontSign{parentSigned}
+ return false, nil, nil, &ErrWontSign{parentSigned}
}
}
}
@@ -226,18 +251,18 @@ Loop:
}
// SignCRUDAction determines if we should sign a CRUD commit to this repository
-func SignCRUDAction(ctx context.Context, repoPath string, u *user_model.User, tmpBasePath, parentCommit string) (bool, string, *git.Signature, error) {
+func SignCRUDAction(ctx context.Context, repoPath string, u *user_model.User, tmpBasePath, parentCommit string) (bool, *git.SigningKey, *git.Signature, error) {
rules := signingModeFromStrings(setting.Repository.Signing.CRUDActions)
signingKey, sig := SigningKey(ctx, repoPath)
- if signingKey == "" {
- return false, "", nil, &ErrWontSign{noKey}
+ if signingKey == nil {
+ return false, nil, nil, &ErrWontSign{noKey}
}
Loop:
for _, rule := range rules {
switch rule {
case never:
- return false, "", nil, &ErrWontSign{never}
+ return false, nil, nil, &ErrWontSign{never}
case always:
break Loop
case pubkey:
@@ -246,35 +271,35 @@ Loop:
IncludeSubKeys: true,
})
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
if len(keys) == 0 {
- return false, "", nil, &ErrWontSign{pubkey}
+ return false, nil, nil, &ErrWontSign{pubkey}
}
case twofa:
twofaModel, err := auth.GetTwoFactorByUID(ctx, u.ID)
if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
- return false, "", nil, err
+ return false, nil, nil, err
}
if twofaModel == nil {
- return false, "", nil, &ErrWontSign{twofa}
+ return false, nil, nil, &ErrWontSign{twofa}
}
case parentSigned:
gitRepo, err := git.OpenRepository(ctx, tmpBasePath)
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
defer gitRepo.Close()
commit, err := gitRepo.GetCommit(parentCommit)
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
if commit.Signature == nil {
- return false, "", nil, &ErrWontSign{parentSigned}
+ return false, nil, nil, &ErrWontSign{parentSigned}
}
- verification := asymkey_model.ParseCommitWithSignature(ctx, commit)
+ verification := ParseCommitWithSignature(ctx, commit)
if !verification.Verified {
- return false, "", nil, &ErrWontSign{parentSigned}
+ return false, nil, nil, &ErrWontSign{parentSigned}
}
}
}
@@ -282,16 +307,16 @@ Loop:
}
// SignMerge determines if we should sign a PR merge commit to the base repository
-func SignMerge(ctx context.Context, pr *issues_model.PullRequest, u *user_model.User, tmpBasePath, baseCommit, headCommit string) (bool, string, *git.Signature, error) {
+func SignMerge(ctx context.Context, pr *issues_model.PullRequest, u *user_model.User, tmpBasePath, baseCommit, headCommit string) (bool, *git.SigningKey, *git.Signature, error) {
if err := pr.LoadBaseRepo(ctx); err != nil {
log.Error("Unable to get Base Repo for pull request")
- return false, "", nil, err
+ return false, nil, nil, err
}
repo := pr.BaseRepo
signingKey, signer := SigningKey(ctx, repo.RepoPath())
- if signingKey == "" {
- return false, "", nil, &ErrWontSign{noKey}
+ if signingKey == nil {
+ return false, nil, nil, &ErrWontSign{noKey}
}
rules := signingModeFromStrings(setting.Repository.Signing.Merges)
@@ -302,7 +327,7 @@ Loop:
for _, rule := range rules {
switch rule {
case never:
- return false, "", nil, &ErrWontSign{never}
+ return false, nil, nil, &ErrWontSign{never}
case always:
break Loop
case pubkey:
@@ -311,91 +336,91 @@ Loop:
IncludeSubKeys: true,
})
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
if len(keys) == 0 {
- return false, "", nil, &ErrWontSign{pubkey}
+ return false, nil, nil, &ErrWontSign{pubkey}
}
case twofa:
twofaModel, err := auth.GetTwoFactorByUID(ctx, u.ID)
if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
- return false, "", nil, err
+ return false, nil, nil, err
}
if twofaModel == nil {
- return false, "", nil, &ErrWontSign{twofa}
+ return false, nil, nil, &ErrWontSign{twofa}
}
case approved:
protectedBranch, err := git_model.GetFirstMatchProtectedBranchRule(ctx, repo.ID, pr.BaseBranch)
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
if protectedBranch == nil {
- return false, "", nil, &ErrWontSign{approved}
+ return false, nil, nil, &ErrWontSign{approved}
}
if issues_model.GetGrantedApprovalsCount(ctx, protectedBranch, pr) < 1 {
- return false, "", nil, &ErrWontSign{approved}
+ return false, nil, nil, &ErrWontSign{approved}
}
case baseSigned:
if gitRepo == nil {
gitRepo, err = git.OpenRepository(ctx, tmpBasePath)
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
defer gitRepo.Close()
}
commit, err := gitRepo.GetCommit(baseCommit)
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
- verification := asymkey_model.ParseCommitWithSignature(ctx, commit)
+ verification := ParseCommitWithSignature(ctx, commit)
if !verification.Verified {
- return false, "", nil, &ErrWontSign{baseSigned}
+ return false, nil, nil, &ErrWontSign{baseSigned}
}
case headSigned:
if gitRepo == nil {
gitRepo, err = git.OpenRepository(ctx, tmpBasePath)
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
defer gitRepo.Close()
}
commit, err := gitRepo.GetCommit(headCommit)
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
- verification := asymkey_model.ParseCommitWithSignature(ctx, commit)
+ verification := ParseCommitWithSignature(ctx, commit)
if !verification.Verified {
- return false, "", nil, &ErrWontSign{headSigned}
+ return false, nil, nil, &ErrWontSign{headSigned}
}
case commitsSigned:
if gitRepo == nil {
gitRepo, err = git.OpenRepository(ctx, tmpBasePath)
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
defer gitRepo.Close()
}
commit, err := gitRepo.GetCommit(headCommit)
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
- verification := asymkey_model.ParseCommitWithSignature(ctx, commit)
+ verification := ParseCommitWithSignature(ctx, commit)
if !verification.Verified {
- return false, "", nil, &ErrWontSign{commitsSigned}
+ return false, nil, nil, &ErrWontSign{commitsSigned}
}
// need to work out merge-base
mergeBaseCommit, _, err := gitRepo.GetMergeBase("", baseCommit, headCommit)
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
commitList, err := commit.CommitsBeforeUntil(mergeBaseCommit)
if err != nil {
- return false, "", nil, err
+ return false, nil, nil, err
}
for _, commit := range commitList {
- verification := asymkey_model.ParseCommitWithSignature(ctx, commit)
+ verification := ParseCommitWithSignature(ctx, commit)
if !verification.Verified {
- return false, "", nil, &ErrWontSign{commitsSigned}
+ return false, nil, nil, &ErrWontSign{commitsSigned}
}
}
}
diff --git a/services/attachment/attachment_test.go b/services/attachment/attachment_test.go
index 142bcfe629..65475836be 100644
--- a/services/attachment/attachment_test.go
+++ b/services/attachment/attachment_test.go
@@ -41,6 +41,6 @@ func TestUploadAttachment(t *testing.T) {
attachment, err := repo_model.GetAttachmentByUUID(db.DefaultContext, attach.UUID)
assert.NoError(t, err)
- assert.EqualValues(t, user.ID, attachment.UploaderID)
+ assert.Equal(t, user.ID, attachment.UploaderID)
assert.Equal(t, int64(0), attachment.DownloadCount)
}
diff --git a/services/auth/auth.go b/services/auth/auth.go
index 43ff95f053..fb6612290b 100644
--- a/services/auth/auth.go
+++ b/services/auth/auth.go
@@ -9,6 +9,7 @@ import (
"net/http"
"regexp"
"strings"
+ "sync"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/webauthn"
@@ -21,44 +22,88 @@ import (
user_service "code.gitea.io/gitea/services/user"
)
+type globalVarsStruct struct {
+ gitRawOrAttachPathRe *regexp.Regexp
+ lfsPathRe *regexp.Regexp
+ archivePathRe *regexp.Regexp
+ feedPathRe *regexp.Regexp
+ feedRefPathRe *regexp.Regexp
+}
+
+var globalVars = sync.OnceValue(func() *globalVarsStruct {
+ return &globalVarsStruct{
+ gitRawOrAttachPathRe: regexp.MustCompile(`^/[-.\w]+/[-.\w]+/(?:(?:git-(?:(?:upload)|(?:receive))-pack$)|(?:info/refs$)|(?:HEAD$)|(?:objects/)|(?:raw/)|(?:releases/download/)|(?:attachments/))`),
+ lfsPathRe: regexp.MustCompile(`^/[-.\w]+/[-.\w]+/info/lfs/`),
+ archivePathRe: regexp.MustCompile(`^/[-.\w]+/[-.\w]+/archive/`),
+ feedPathRe: regexp.MustCompile(`^/[-.\w]+(/[-.\w]+)?\.(rss|atom)$`), // "/owner.rss" or "/owner/repo.atom"
+ feedRefPathRe: regexp.MustCompile(`^/[-.\w]+/[-.\w]+/(rss|atom)/`), // "/owner/repo/rss/branch/..."
+ }
+})
+
// Init should be called exactly once when the application starts to allow plugins
// to allocate necessary resources
func Init() {
webauthn.Init()
}
+type authPathDetector struct {
+ req *http.Request
+ vars *globalVarsStruct
+}
+
+func newAuthPathDetector(req *http.Request) *authPathDetector {
+ return &authPathDetector{req: req, vars: globalVars()}
+}
+
+// isAPIPath returns true if the specified URL is an API path
+func (a *authPathDetector) isAPIPath() bool {
+ return strings.HasPrefix(a.req.URL.Path, "/api/")
+}
+
// isAttachmentDownload check if request is a file download (GET) with URL to an attachment
-func isAttachmentDownload(req *http.Request) bool {
- return strings.HasPrefix(req.URL.Path, "/attachments/") && req.Method == "GET"
+func (a *authPathDetector) isAttachmentDownload() bool {
+ return strings.HasPrefix(a.req.URL.Path, "/attachments/") && a.req.Method == http.MethodGet
}
-// isContainerPath checks if the request targets the container endpoint
-func isContainerPath(req *http.Request) bool {
- return strings.HasPrefix(req.URL.Path, "/v2/")
+func (a *authPathDetector) isFeedRequest(req *http.Request) bool {
+ if !setting.Other.EnableFeed {
+ return false
+ }
+ if req.Method != http.MethodGet {
+ return false
+ }
+ return a.vars.feedPathRe.MatchString(req.URL.Path) || a.vars.feedRefPathRe.MatchString(req.URL.Path)
}
-var (
- gitRawOrAttachPathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/(?:(?:git-(?:(?:upload)|(?:receive))-pack$)|(?:info/refs$)|(?:HEAD$)|(?:objects/)|(?:raw/)|(?:releases/download/)|(?:attachments/))`)
- lfsPathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/info/lfs/`)
- archivePathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/archive/`)
-)
+// isContainerPath checks if the request targets the container endpoint
+func (a *authPathDetector) isContainerPath() bool {
+ return strings.HasPrefix(a.req.URL.Path, "/v2/")
+}
-func isGitRawOrAttachPath(req *http.Request) bool {
- return gitRawOrAttachPathRe.MatchString(req.URL.Path)
+func (a *authPathDetector) isGitRawOrAttachPath() bool {
+ return a.vars.gitRawOrAttachPathRe.MatchString(a.req.URL.Path)
}
-func isGitRawOrAttachOrLFSPath(req *http.Request) bool {
- if isGitRawOrAttachPath(req) {
+func (a *authPathDetector) isGitRawOrAttachOrLFSPath() bool {
+ if a.isGitRawOrAttachPath() {
return true
}
if setting.LFS.StartServer {
- return lfsPathRe.MatchString(req.URL.Path)
+ return a.vars.lfsPathRe.MatchString(a.req.URL.Path)
}
return false
}
-func isArchivePath(req *http.Request) bool {
- return archivePathRe.MatchString(req.URL.Path)
+func (a *authPathDetector) isArchivePath() bool {
+ return a.vars.archivePathRe.MatchString(a.req.URL.Path)
+}
+
+func (a *authPathDetector) isAuthenticatedTokenRequest() bool {
+ switch a.req.URL.Path {
+ case "/login/oauth/userinfo", "/login/oauth/introspect":
+ return true
+ }
+ return false
}
// handleSignIn clears existing session variables and stores new ones for the specified user object
@@ -104,7 +149,7 @@ func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore
middleware.SetLocaleCookie(resp, user.Language, 0)
// force to generate a new CSRF token
- if ctx := gitea_context.GetWebContext(req); ctx != nil {
+ if ctx := gitea_context.GetWebContext(req.Context()); ctx != nil {
ctx.Csrf.PrepareForSessionUser(ctx)
}
}
diff --git a/services/auth/auth_test.go b/services/auth/auth_test.go
index 3adaa28664..c45f312c90 100644
--- a/services/auth/auth_test.go
+++ b/services/auth/auth_test.go
@@ -9,6 +9,9 @@ import (
"testing"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
+
+ "github.com/stretchr/testify/assert"
)
func Test_isGitRawOrLFSPath(t *testing.T) {
@@ -90,6 +93,19 @@ func Test_isGitRawOrLFSPath(t *testing.T) {
true,
},
}
+
+ defer test.MockVariableValue(&setting.LFS.StartServer)()
+ for _, tt := range tests {
+ t.Run(tt.path, func(t *testing.T) {
+ req, _ := http.NewRequest(http.MethodPost, "http://localhost"+tt.path, nil)
+ setting.LFS.StartServer = false
+ assert.Equal(t, tt.want, newAuthPathDetector(req).isGitRawOrAttachOrLFSPath())
+
+ setting.LFS.StartServer = true
+ assert.Equal(t, tt.want, newAuthPathDetector(req).isGitRawOrAttachOrLFSPath())
+ })
+ }
+
lfsTests := []string{
"/owner/repo/info/lfs/",
"/owner/repo/info/lfs/objects/batch",
@@ -101,34 +117,39 @@ func Test_isGitRawOrLFSPath(t *testing.T) {
"/owner/repo/info/lfs/locks/verify",
"/owner/repo/info/lfs/locks/123/unlock",
}
-
- origLFSStartServer := setting.LFS.StartServer
-
- for _, tt := range tests {
- t.Run(tt.path, func(t *testing.T) {
- req, _ := http.NewRequest("POST", "http://localhost"+tt.path, nil)
- setting.LFS.StartServer = false
- if got := isGitRawOrAttachOrLFSPath(req); got != tt.want {
- t.Errorf("isGitOrLFSPath() = %v, want %v", got, tt.want)
- }
- setting.LFS.StartServer = true
- if got := isGitRawOrAttachOrLFSPath(req); got != tt.want {
- t.Errorf("isGitOrLFSPath() = %v, want %v", got, tt.want)
- }
- })
- }
for _, tt := range lfsTests {
t.Run(tt, func(t *testing.T) {
- req, _ := http.NewRequest("POST", tt, nil)
+ req, _ := http.NewRequest(http.MethodPost, tt, nil)
setting.LFS.StartServer = false
- if got := isGitRawOrAttachOrLFSPath(req); got != setting.LFS.StartServer {
- t.Errorf("isGitOrLFSPath(%q) = %v, want %v, %v", tt, got, setting.LFS.StartServer, gitRawOrAttachPathRe.MatchString(tt))
- }
+ got := newAuthPathDetector(req).isGitRawOrAttachOrLFSPath()
+ assert.Equalf(t, setting.LFS.StartServer, got, "isGitOrLFSPath(%q) = %v, want %v, %v", tt, got, setting.LFS.StartServer, globalVars().gitRawOrAttachPathRe.MatchString(tt))
+
setting.LFS.StartServer = true
- if got := isGitRawOrAttachOrLFSPath(req); got != setting.LFS.StartServer {
- t.Errorf("isGitOrLFSPath(%q) = %v, want %v", tt, got, setting.LFS.StartServer)
- }
+ got = newAuthPathDetector(req).isGitRawOrAttachOrLFSPath()
+ assert.Equalf(t, setting.LFS.StartServer, got, "isGitOrLFSPath(%q) = %v, want %v", tt, got, setting.LFS.StartServer)
+ })
+ }
+}
+
+func Test_isFeedRequest(t *testing.T) {
+ tests := []struct {
+ want bool
+ path string
+ }{
+ {true, "/user.rss"},
+ {true, "/user/repo.atom"},
+ {false, "/user/repo"},
+ {false, "/use/repo/file.rss"},
+
+ {true, "/org/repo/rss/branch/xxx"},
+ {true, "/org/repo/atom/tag/xxx"},
+ {false, "/org/repo/branch/main/rss/any"},
+ {false, "/org/atom/any"},
+ }
+ for _, tt := range tests {
+ t.Run(tt.path, func(t *testing.T) {
+ req, _ := http.NewRequest(http.MethodGet, "http://localhost"+tt.path, nil)
+ assert.Equal(t, tt.want, newAuthPathDetector(req).isFeedRequest(req))
})
}
- setting.LFS.StartServer = origLFSStartServer
}
diff --git a/services/auth/basic.go b/services/auth/basic.go
index 6a05b2fe53..b2bd14ef5d 100644
--- a/services/auth/basic.go
+++ b/services/auth/basic.go
@@ -7,17 +7,15 @@ package auth
import (
"errors"
"net/http"
- "strings"
actions_model "code.gitea.io/gitea/models/actions"
auth_model "code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/auth/httpauth"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web/middleware"
)
// Ensure the struct implements the interface.
@@ -48,22 +46,22 @@ func (b *Basic) Name() string {
// name/token on successful validation.
// Returns nil if header is empty or validation fails.
func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
- // Basic authentication should only fire on API, Download or on Git or LFSPaths
- if !middleware.IsAPIPath(req) && !isContainerPath(req) && !isAttachmentDownload(req) && !isGitRawOrAttachOrLFSPath(req) {
+ // Basic authentication should only fire on API, Feed, Download or on Git or LFSPaths
+ // Not all feed (rss/atom) clients feature the ability to add cookies or headers, so we need to allow basic auth for feeds
+ detector := newAuthPathDetector(req)
+ if !detector.isAPIPath() && !detector.isFeedRequest(req) && !detector.isContainerPath() && !detector.isAttachmentDownload() && !detector.isGitRawOrAttachOrLFSPath() {
return nil, nil
}
- baHead := req.Header.Get("Authorization")
- if len(baHead) == 0 {
+ authHeader := req.Header.Get("Authorization")
+ if authHeader == "" {
return nil, nil
}
-
- auths := strings.SplitN(baHead, " ", 2)
- if len(auths) != 2 || (strings.ToLower(auths[0]) != "basic") {
+ parsed, ok := httpauth.ParseAuthorizationHeader(authHeader)
+ if !ok || parsed.BasicAuth == nil {
return nil, nil
}
-
- uname, passwd, _ := base.BasicAuthDecode(auths[1])
+ uname, passwd := parsed.BasicAuth.Username, parsed.BasicAuth.Password
// Check if username or password is a token
isUsernameToken := len(passwd) == 0 || passwd == "x-oauth-basic"
@@ -141,14 +139,14 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
return nil, err
}
- if skipper, ok := source.Cfg.(LocalTwoFASkipper); !ok || !skipper.IsSkipLocalTwoFA() {
- // Check if the user has webAuthn registration
+ if !source.TwoFactorShouldSkip() {
+ // Check if the user has WebAuthn registration
hasWebAuthn, err := auth_model.HasWebAuthnRegistrationsByUID(req.Context(), u.ID)
if err != nil {
return nil, err
}
if hasWebAuthn {
- return nil, errors.New("Basic authorization is not allowed while webAuthn enrolled")
+ return nil, errors.New("basic authorization is not allowed while WebAuthn enrolled")
}
if err := validateTOTP(req, u); err != nil {
diff --git a/services/auth/httpsign.go b/services/auth/httpsign.go
index 83a36bef23..25e96ff32d 100644
--- a/services/auth/httpsign.go
+++ b/services/auth/httpsign.go
@@ -134,7 +134,7 @@ func VerifyCert(r *http.Request) (*asymkey_model.PublicKey, error) {
// Check if it's really a ssh certificate
cert, ok := pk.(*ssh.Certificate)
if !ok {
- return nil, fmt.Errorf("no certificate found")
+ return nil, errors.New("no certificate found")
}
c := &ssh.CertChecker{
@@ -153,7 +153,7 @@ func VerifyCert(r *http.Request) (*asymkey_model.PublicKey, error) {
// check the CA of the cert
if !c.IsUserAuthority(cert.SignatureKey) {
- return nil, fmt.Errorf("CA check failed")
+ return nil, errors.New("CA check failed")
}
// Create a verifier
@@ -191,7 +191,7 @@ func VerifyCert(r *http.Request) (*asymkey_model.PublicKey, error) {
}
// No public key matching a principal in the certificate is registered in gitea
- return nil, fmt.Errorf("no valid principal found")
+ return nil, errors.New("no valid principal found")
}
// doVerify iterates across the provided public keys attempting the verify the current request against each key in turn
diff --git a/services/auth/interface.go b/services/auth/interface.go
index 275b4dd56c..e7eccecea0 100644
--- a/services/auth/interface.go
+++ b/services/auth/interface.go
@@ -35,11 +35,6 @@ type PasswordAuthenticator interface {
Authenticate(ctx context.Context, user *user_model.User, login, password string) (*user_model.User, error)
}
-// LocalTwoFASkipper represents a source of authentication that can skip local 2fa
-type LocalTwoFASkipper interface {
- IsSkipLocalTwoFA() bool
-}
-
// SynchronizableSource represents a source that can synchronize users
type SynchronizableSource interface {
Sync(ctx context.Context, updateExisting bool) error
diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go
index 6f2cadd4ab..7df6f4638e 100644
--- a/services/auth/oauth2.go
+++ b/services/auth/oauth2.go
@@ -13,10 +13,10 @@ import (
actions_model "code.gitea.io/gitea/models/actions"
auth_model "code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/auth/httpauth"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/services/actions"
"code.gitea.io/gitea/services/oauth2_provider"
)
@@ -98,9 +98,9 @@ func parseToken(req *http.Request) (string, bool) {
// check header token
if auHead := req.Header.Get("Authorization"); auHead != "" {
- auths := strings.Fields(auHead)
- if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") {
- return auths[1], true
+ parsed, ok := httpauth.ParseAuthorizationHeader(auHead)
+ if ok && parsed.BearerToken != nil {
+ return parsed.BearerToken.Token, true
}
}
return "", false
@@ -162,8 +162,9 @@ func (o *OAuth2) userIDFromToken(ctx context.Context, tokenSHA string, store Dat
// Returns nil if verification fails.
func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
// These paths are not API paths, but we still want to check for tokens because they maybe in the API returned URLs
- if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isAuthenticatedTokenRequest(req) &&
- !isGitRawOrAttachPath(req) && !isArchivePath(req) {
+ detector := newAuthPathDetector(req)
+ if !detector.isAPIPath() && !detector.isAttachmentDownload() && !detector.isAuthenticatedTokenRequest() &&
+ !detector.isGitRawOrAttachPath() && !detector.isArchivePath() {
return nil, nil
}
@@ -190,13 +191,3 @@ func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStor
log.Trace("OAuth2 Authorization: Logged in user %-v", user)
return user, nil
}
-
-func isAuthenticatedTokenRequest(req *http.Request) bool {
- switch req.URL.Path {
- case "/login/oauth/userinfo":
- fallthrough
- case "/login/oauth/introspect":
- return true
- }
- return false
-}
diff --git a/services/auth/oauth2_test.go b/services/auth/oauth2_test.go
index 0d9e793cf3..f003742a94 100644
--- a/services/auth/oauth2_test.go
+++ b/services/auth/oauth2_test.go
@@ -4,7 +4,6 @@
package auth
import (
- "context"
"testing"
"code.gitea.io/gitea/models/unittest"
@@ -26,8 +25,8 @@ func TestUserIDFromToken(t *testing.T) {
ds := make(reqctx.ContextData)
o := OAuth2{}
- uid := o.userIDFromToken(context.Background(), token, ds)
- assert.Equal(t, int64(user_model.ActionsUserID), uid)
+ uid := o.userIDFromToken(t.Context(), token, ds)
+ assert.Equal(t, user_model.ActionsUserID, uid)
assert.Equal(t, true, ds["IsActionsToken"])
assert.Equal(t, ds["ActionsTaskID"], int64(RunningTaskID))
})
@@ -48,7 +47,7 @@ func TestCheckTaskIsRunning(t *testing.T) {
for name := range cases {
c := cases[name]
t.Run(name, func(t *testing.T) {
- actual := CheckTaskIsRunning(context.Background(), c.TaskID)
+ actual := CheckTaskIsRunning(t.Context(), c.TaskID)
assert.Equal(t, c.Expected, actual)
})
}
diff --git a/services/auth/reverseproxy.go b/services/auth/reverseproxy.go
index 36b4ef68f4..d6664d738d 100644
--- a/services/auth/reverseproxy.go
+++ b/services/auth/reverseproxy.go
@@ -12,7 +12,6 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web/middleware"
gouuid "github.com/google/uuid"
)
@@ -117,7 +116,8 @@ func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store Da
}
// Make sure requests to API paths, attachment downloads, git and LFS do not create a new session
- if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isGitRawOrAttachOrLFSPath(req) {
+ detector := newAuthPathDetector(req)
+ if !detector.isAPIPath() && !detector.isAttachmentDownload() && !detector.isGitRawOrAttachOrLFSPath() {
if sess != nil && (sess.Get("uid") == nil || sess.Get("uid").(int64) != user.ID) {
handleSignIn(w, req, sess, user)
}
diff --git a/services/auth/source/db/source.go b/services/auth/source/db/source.go
index bb2270cbd6..90baa61f5b 100644
--- a/services/auth/source/db/source.go
+++ b/services/auth/source/db/source.go
@@ -11,7 +11,9 @@ import (
)
// Source is a password authentication service
-type Source struct{}
+type Source struct {
+ auth.ConfigBase `json:"-"`
+}
// FromDB fills up an OAuth2Config from serialized format.
func (source *Source) FromDB(bs []byte) error {
diff --git a/services/auth/source/ldap/assert_interface_test.go b/services/auth/source/ldap/assert_interface_test.go
index 33347687dc..8e8accd668 100644
--- a/services/auth/source/ldap/assert_interface_test.go
+++ b/services/auth/source/ldap/assert_interface_test.go
@@ -15,13 +15,11 @@ import (
type sourceInterface interface {
auth.PasswordAuthenticator
auth.SynchronizableSource
- auth.LocalTwoFASkipper
auth_model.SSHKeyProvider
auth_model.Config
auth_model.SkipVerifiable
auth_model.HasTLSer
auth_model.UseTLSer
- auth_model.SourceSettable
}
var _ (sourceInterface) = &ldap.Source{}
diff --git a/services/auth/source/ldap/source.go b/services/auth/source/ldap/source.go
index 963cdba7c2..d49dbc45ce 100644
--- a/services/auth/source/ldap/source.go
+++ b/services/auth/source/ldap/source.go
@@ -24,6 +24,8 @@ import (
// Source Basic LDAP authentication service
type Source struct {
+ auth.ConfigBase `json:"-"`
+
Name string // canonical name (ie. corporate.ad)
Host string // LDAP host
Port int // port number
@@ -54,9 +56,6 @@ type Source struct {
GroupTeamMap string // Map LDAP groups to teams
GroupTeamMapRemoval bool // Remove user from teams which are synchronized and user is not a member of the corresponding LDAP group
UserUID string // User Attribute listed in Group
- SkipLocalTwoFA bool `json:",omitempty"` // Skip Local 2fa for users authenticated with this source
-
- authSource *auth.Source // reference to the authSource
}
// FromDB fills up a LDAPConfig from serialized format.
@@ -109,11 +108,6 @@ func (source *Source) ProvidesSSHKeys() bool {
return strings.TrimSpace(source.AttributeSSHPublicKey) != ""
}
-// SetAuthSource sets the related AuthSource
-func (source *Source) SetAuthSource(authSource *auth.Source) {
- source.authSource = authSource
-}
-
func init() {
auth.RegisterTypeConfig(auth.LDAP, &Source{})
auth.RegisterTypeConfig(auth.DLDAP, &Source{})
diff --git a/services/auth/source/ldap/source_authenticate.go b/services/auth/source/ldap/source_authenticate.go
index 6a6c60cd40..6005a4744a 100644
--- a/services/auth/source/ldap/source_authenticate.go
+++ b/services/auth/source/ldap/source_authenticate.go
@@ -5,7 +5,6 @@ package ldap
import (
"context"
- "fmt"
"strings"
asymkey_model "code.gitea.io/gitea/models/asymkey"
@@ -26,7 +25,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
if user != nil {
loginName = user.LoginName
}
- sr := source.SearchEntry(loginName, password, source.authSource.Type == auth.DLDAP)
+ sr := source.SearchEntry(loginName, password, source.AuthSource.Type == auth.DLDAP)
if sr == nil {
// User not in LDAP, do nothing
return nil, user_model.ErrUserNotExist{Name: loginName}
@@ -41,7 +40,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
sr.Username = userName
}
if sr.Mail == "" {
- sr.Mail = fmt.Sprintf("%s@localhost.local", sr.Username)
+ sr.Mail = sr.Username + "@localhost.local"
}
isAttributeSSHPublicKeySet := strings.TrimSpace(source.AttributeSSHPublicKey) != ""
@@ -59,7 +58,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
opts := &user_service.UpdateOptions{}
if source.AdminFilter != "" && user.IsAdmin != sr.IsAdmin {
// Change existing admin flag only if AdminFilter option is set
- opts.IsAdmin = optional.Some(sr.IsAdmin)
+ opts.IsAdmin = user_service.UpdateOptionFieldFromSync(sr.IsAdmin)
}
if !sr.IsAdmin && source.RestrictedFilter != "" && user.IsRestricted != sr.IsRestricted {
// Change existing restricted flag only if RestrictedFilter option is set
@@ -74,7 +73,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
}
if user != nil {
- if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(ctx, user, source.authSource, sr.SSHPublicKey) {
+ if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(ctx, user, source.AuthSource, sr.SSHPublicKey) {
if err := asymkey_service.RewriteAllPublicKeys(ctx); err != nil {
return user, err
}
@@ -85,8 +84,8 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
Name: sr.Username,
FullName: composeFullName(sr.Name, sr.Surname, sr.Username),
Email: sr.Mail,
- LoginType: source.authSource.Type,
- LoginSource: source.authSource.ID,
+ LoginType: source.AuthSource.Type,
+ LoginSource: source.AuthSource.ID,
LoginName: userName,
IsAdmin: sr.IsAdmin,
}
@@ -100,7 +99,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
return user, err
}
- if isAttributeSSHPublicKeySet && asymkey_model.AddPublicKeysBySource(ctx, user, source.authSource, sr.SSHPublicKey) {
+ if isAttributeSSHPublicKeySet && asymkey_model.AddPublicKeysBySource(ctx, user, source.AuthSource, sr.SSHPublicKey) {
if err := asymkey_service.RewriteAllPublicKeys(ctx); err != nil {
return user, err
}
@@ -124,8 +123,3 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
return user, nil
}
-
-// IsSkipLocalTwoFA returns if this source should skip local 2fa for password authentication
-func (source *Source) IsSkipLocalTwoFA() bool {
- return source.SkipLocalTwoFA
-}
diff --git a/services/auth/source/ldap/source_search.go b/services/auth/source/ldap/source_search.go
index fa2c45ce4a..e6bce04a83 100644
--- a/services/auth/source/ldap/source_search.go
+++ b/services/auth/source/ldap/source_search.go
@@ -117,10 +117,10 @@ func dial(source *Source) (*ldap.Conn, error) {
}
if source.SecurityProtocol == SecurityProtocolLDAPS {
- return ldap.DialTLS("tcp", net.JoinHostPort(source.Host, strconv.Itoa(source.Port)), tlsConfig) //nolint:staticcheck
+ return ldap.DialTLS("tcp", net.JoinHostPort(source.Host, strconv.Itoa(source.Port)), tlsConfig) //nolint:staticcheck // DialTLS is deprecated
}
- conn, err := ldap.Dial("tcp", net.JoinHostPort(source.Host, strconv.Itoa(source.Port))) //nolint:staticcheck
+ conn, err := ldap.Dial("tcp", net.JoinHostPort(source.Host, strconv.Itoa(source.Port))) //nolint:staticcheck // Dial is deprecated
if err != nil {
return nil, fmt.Errorf("error during Dial: %w", err)
}
diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go
index e817bf1fa9..7b401c5c96 100644
--- a/services/auth/source/ldap/source_sync.go
+++ b/services/auth/source/ldap/source_sync.go
@@ -5,7 +5,6 @@ package ldap
import (
"context"
- "fmt"
"strings"
asymkey_model "code.gitea.io/gitea/models/asymkey"
@@ -23,21 +22,21 @@ import (
// Sync causes this ldap source to synchronize its users with the db
func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
- log.Trace("Doing: SyncExternalUsers[%s]", source.authSource.Name)
+ log.Trace("Doing: SyncExternalUsers[%s]", source.AuthSource.Name)
isAttributeSSHPublicKeySet := strings.TrimSpace(source.AttributeSSHPublicKey) != ""
var sshKeysNeedUpdate bool
// Find all users with this login type - FIXME: Should this be an iterator?
- users, err := user_model.GetUsersBySource(ctx, source.authSource)
+ users, err := user_model.GetUsersBySource(ctx, source.AuthSource)
if err != nil {
log.Error("SyncExternalUsers: %v", err)
return err
}
select {
case <-ctx.Done():
- log.Warn("SyncExternalUsers: Cancelled before update of %s", source.authSource.Name)
- return db.ErrCancelledf("Before update of %s", source.authSource.Name)
+ log.Warn("SyncExternalUsers: Cancelled before update of %s", source.AuthSource.Name)
+ return db.ErrCancelledf("Before update of %s", source.AuthSource.Name)
default:
}
@@ -52,7 +51,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
sr, err := source.SearchEntries()
if err != nil {
- log.Error("SyncExternalUsers LDAP source failure [%s], skipped", source.authSource.Name)
+ log.Error("SyncExternalUsers LDAP source failure [%s], skipped", source.AuthSource.Name)
return nil
}
@@ -75,7 +74,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
for _, su := range sr {
select {
case <-ctx.Done():
- log.Warn("SyncExternalUsers: Cancelled at update of %s before completed update of users", source.authSource.Name)
+ log.Warn("SyncExternalUsers: Cancelled at update of %s before completed update of users", source.AuthSource.Name)
// Rewrite authorized_keys file if LDAP Public SSH Key attribute is set and any key was added or removed
if sshKeysNeedUpdate {
err = asymkey_service.RewriteAllPublicKeys(ctx)
@@ -83,7 +82,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
log.Error("RewriteAllPublicKeys: %v", err)
}
}
- return db.ErrCancelledf("During update of %s before completed update of users", source.authSource.Name)
+ return db.ErrCancelledf("During update of %s before completed update of users", source.AuthSource.Name)
default:
}
if su.Username == "" && su.Mail == "" {
@@ -106,20 +105,20 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
}
if su.Mail == "" {
- su.Mail = fmt.Sprintf("%s@localhost.local", su.Username)
+ su.Mail = su.Username + "@localhost.local"
}
fullName := composeFullName(su.Name, su.Surname, su.Username)
// If no existing user found, create one
if usr == nil {
- log.Trace("SyncExternalUsers[%s]: Creating user %s", source.authSource.Name, su.Username)
+ log.Trace("SyncExternalUsers[%s]: Creating user %s", source.AuthSource.Name, su.Username)
usr = &user_model.User{
LowerName: su.LowerName,
Name: su.Username,
FullName: fullName,
- LoginType: source.authSource.Type,
- LoginSource: source.authSource.ID,
+ LoginType: source.AuthSource.Type,
+ LoginSource: source.AuthSource.ID,
LoginName: su.Username,
Email: su.Mail,
IsAdmin: su.IsAdmin,
@@ -131,12 +130,12 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
err = user_model.CreateUser(ctx, usr, &user_model.Meta{}, overwriteDefault)
if err != nil {
- log.Error("SyncExternalUsers[%s]: Error creating user %s: %v", source.authSource.Name, su.Username, err)
+ log.Error("SyncExternalUsers[%s]: Error creating user %s: %v", source.AuthSource.Name, su.Username, err)
}
if err == nil && isAttributeSSHPublicKeySet {
- log.Trace("SyncExternalUsers[%s]: Adding LDAP Public SSH Keys for user %s", source.authSource.Name, usr.Name)
- if asymkey_model.AddPublicKeysBySource(ctx, usr, source.authSource, su.SSHPublicKey) {
+ log.Trace("SyncExternalUsers[%s]: Adding LDAP Public SSH Keys for user %s", source.AuthSource.Name, usr.Name)
+ if asymkey_model.AddPublicKeysBySource(ctx, usr, source.AuthSource, su.SSHPublicKey) {
sshKeysNeedUpdate = true
}
}
@@ -146,7 +145,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
}
} else if updateExisting {
// Synchronize SSH Public Key if that attribute is set
- if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(ctx, usr, source.authSource, su.SSHPublicKey) {
+ if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(ctx, usr, source.AuthSource, su.SSHPublicKey) {
sshKeysNeedUpdate = true
}
@@ -156,14 +155,14 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
!strings.EqualFold(usr.Email, su.Mail) ||
usr.FullName != fullName ||
!usr.IsActive {
- log.Trace("SyncExternalUsers[%s]: Updating user %s", source.authSource.Name, usr.Name)
+ log.Trace("SyncExternalUsers[%s]: Updating user %s", source.AuthSource.Name, usr.Name)
opts := &user_service.UpdateOptions{
FullName: optional.Some(fullName),
IsActive: optional.Some(true),
}
if source.AdminFilter != "" {
- opts.IsAdmin = optional.Some(su.IsAdmin)
+ opts.IsAdmin = user_service.UpdateOptionFieldFromSync(su.IsAdmin)
}
// Change existing restricted flag only if RestrictedFilter option is set
if !su.IsAdmin && source.RestrictedFilter != "" {
@@ -171,16 +170,17 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
}
if err := user_service.UpdateUser(ctx, usr, opts); err != nil {
- log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", source.authSource.Name, usr.Name, err)
+ log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", source.AuthSource.Name, usr.Name, err)
}
if err := user_service.ReplacePrimaryEmailAddress(ctx, usr, su.Mail); err != nil {
- log.Error("SyncExternalUsers[%s]: Error updating user %s primary email %s: %v", source.authSource.Name, usr.Name, su.Mail, err)
+ log.Error("SyncExternalUsers[%s]: Error updating user %s primary email %s: %v", source.AuthSource.Name, usr.Name, su.Mail, err)
}
}
- if usr.IsUploadAvatarChanged(su.Avatar) {
- if err == nil && source.AttributeAvatar != "" {
+ if source.AttributeAvatar != "" {
+ if len(su.Avatar) > 0 && usr.IsUploadAvatarChanged(su.Avatar) {
+ log.Trace("SyncExternalUsers[%s]: Uploading new avatar for %s", source.AuthSource.Name, usr.Name)
_ = user_service.UploadAvatar(ctx, usr, su.Avatar)
}
}
@@ -203,8 +203,8 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
select {
case <-ctx.Done():
- log.Warn("SyncExternalUsers: Cancelled during update of %s before delete users", source.authSource.Name)
- return db.ErrCancelledf("During update of %s before delete users", source.authSource.Name)
+ log.Warn("SyncExternalUsers: Cancelled during update of %s before delete users", source.AuthSource.Name)
+ return db.ErrCancelledf("During update of %s before delete users", source.AuthSource.Name)
default:
}
@@ -215,13 +215,13 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
continue
}
- log.Trace("SyncExternalUsers[%s]: Deactivating user %s", source.authSource.Name, usr.Name)
+ log.Trace("SyncExternalUsers[%s]: Deactivating user %s", source.AuthSource.Name, usr.Name)
opts := &user_service.UpdateOptions{
IsActive: optional.Some(false),
}
if err := user_service.UpdateUser(ctx, usr, opts); err != nil {
- log.Error("SyncExternalUsers[%s]: Error deactivating user %s: %v", source.authSource.Name, usr.Name, err)
+ log.Error("SyncExternalUsers[%s]: Error deactivating user %s: %v", source.AuthSource.Name, usr.Name, err)
}
}
}
diff --git a/services/auth/source/oauth2/assert_interface_test.go b/services/auth/source/oauth2/assert_interface_test.go
index 56fe0e4aa8..d870ac1dcd 100644
--- a/services/auth/source/oauth2/assert_interface_test.go
+++ b/services/auth/source/oauth2/assert_interface_test.go
@@ -14,7 +14,6 @@ import (
type sourceInterface interface {
auth_model.Config
- auth_model.SourceSettable
auth_model.RegisterableSource
auth.PasswordAuthenticator
}
diff --git a/services/auth/source/oauth2/source.go b/services/auth/source/oauth2/source.go
index 3454c9ad55..08837de377 100644
--- a/services/auth/source/oauth2/source.go
+++ b/services/auth/source/oauth2/source.go
@@ -10,6 +10,8 @@ import (
// Source holds configuration for the OAuth2 login source.
type Source struct {
+ auth.ConfigBase `json:"-"`
+
Provider string
ClientID string
ClientSecret string
@@ -25,10 +27,6 @@ type Source struct {
GroupTeamMap string
GroupTeamMapRemoval bool
RestrictedGroup string
- SkipLocalTwoFA bool `json:",omitempty"`
-
- // reference to the authSource
- authSource *auth.Source
}
// FromDB fills up an OAuth2Config from serialized format.
@@ -41,11 +39,6 @@ func (source *Source) ToDB() ([]byte, error) {
return json.Marshal(source)
}
-// SetAuthSource sets the related AuthSource
-func (source *Source) SetAuthSource(authSource *auth.Source) {
- source.authSource = authSource
-}
-
func init() {
auth.RegisterTypeConfig(auth.OAuth2, &Source{})
}
diff --git a/services/auth/source/oauth2/source_callout.go b/services/auth/source/oauth2/source_callout.go
index 8d70bee248..f09d25c772 100644
--- a/services/auth/source/oauth2/source_callout.go
+++ b/services/auth/source/oauth2/source_callout.go
@@ -13,7 +13,7 @@ import (
// Callout redirects request/response pair to authenticate against the provider
func (source *Source) Callout(request *http.Request, response http.ResponseWriter) error {
// not sure if goth is thread safe (?) when using multiple providers
- request.Header.Set(ProviderHeaderKey, source.authSource.Name)
+ request.Header.Set(ProviderHeaderKey, source.AuthSource.Name)
// don't use the default gothic begin handler to prevent issues when some error occurs
// normally the gothic library will write some custom stuff to the response instead of our own nice error page
@@ -33,7 +33,7 @@ func (source *Source) Callout(request *http.Request, response http.ResponseWrite
// this will trigger a new authentication request, but because we save it in the session we can use that
func (source *Source) Callback(request *http.Request, response http.ResponseWriter) (goth.User, error) {
// not sure if goth is thread safe (?) when using multiple providers
- request.Header.Set(ProviderHeaderKey, source.authSource.Name)
+ request.Header.Set(ProviderHeaderKey, source.AuthSource.Name)
gothRWMutex.RLock()
defer gothRWMutex.RUnlock()
diff --git a/services/auth/source/oauth2/source_register.go b/services/auth/source/oauth2/source_register.go
index 82a36acaa6..12da56c11b 100644
--- a/services/auth/source/oauth2/source_register.go
+++ b/services/auth/source/oauth2/source_register.go
@@ -9,13 +9,13 @@ import (
// RegisterSource causes an OAuth2 configuration to be registered
func (source *Source) RegisterSource() error {
- err := RegisterProviderWithGothic(source.authSource.Name, source)
- return wrapOpenIDConnectInitializeError(err, source.authSource.Name, source)
+ err := RegisterProviderWithGothic(source.AuthSource.Name, source)
+ return wrapOpenIDConnectInitializeError(err, source.AuthSource.Name, source)
}
// UnregisterSource causes an OAuth2 configuration to be unregistered
func (source *Source) UnregisterSource() error {
- RemoveProviderFromGothic(source.authSource.Name)
+ RemoveProviderFromGothic(source.AuthSource.Name)
return nil
}
diff --git a/services/auth/source/oauth2/source_sync.go b/services/auth/source/oauth2/source_sync.go
index 5e30313c8f..c2e3dfb1a8 100644
--- a/services/auth/source/oauth2/source_sync.go
+++ b/services/auth/source/oauth2/source_sync.go
@@ -18,27 +18,27 @@ import (
// Sync causes this OAuth2 source to synchronize its users with the db.
func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
- log.Trace("Doing: SyncExternalUsers[%s] %d", source.authSource.Name, source.authSource.ID)
+ log.Trace("Doing: SyncExternalUsers[%s] %d", source.AuthSource.Name, source.AuthSource.ID)
if !updateExisting {
- log.Info("SyncExternalUsers[%s] not running since updateExisting is false", source.authSource.Name)
+ log.Info("SyncExternalUsers[%s] not running since updateExisting is false", source.AuthSource.Name)
return nil
}
- provider, err := createProvider(source.authSource.Name, source)
+ provider, err := createProvider(source.AuthSource.Name, source)
if err != nil {
return err
}
if !provider.RefreshTokenAvailable() {
- log.Trace("SyncExternalUsers[%s] provider doesn't support refresh tokens, can't synchronize", source.authSource.Name)
+ log.Trace("SyncExternalUsers[%s] provider doesn't support refresh tokens, can't synchronize", source.AuthSource.Name)
return nil
}
opts := user_model.FindExternalUserOptions{
HasRefreshToken: true,
Expired: true,
- LoginSourceID: source.authSource.ID,
+ LoginSourceID: source.AuthSource.ID,
}
return user_model.IterateExternalLogin(ctx, opts, func(ctx context.Context, u *user_model.ExternalLoginUser) error {
@@ -77,7 +77,7 @@ func (source *Source) refresh(ctx context.Context, provider goth.Provider, u *us
// recognizes them as a valid user, they will be able to login
// via their provider and reactivate their account.
if shouldDisable {
- log.Info("SyncExternalUsers[%s] disabling user %d", source.authSource.Name, user.ID)
+ log.Info("SyncExternalUsers[%s] disabling user %d", source.AuthSource.Name, user.ID)
return db.WithTx(ctx, func(ctx context.Context) error {
if hasUser {
diff --git a/services/auth/source/oauth2/source_sync_test.go b/services/auth/source/oauth2/source_sync_test.go
index 893ed62502..2927f3634b 100644
--- a/services/auth/source/oauth2/source_sync_test.go
+++ b/services/auth/source/oauth2/source_sync_test.go
@@ -4,7 +4,6 @@
package oauth2
import (
- "context"
"testing"
"code.gitea.io/gitea/models/auth"
@@ -19,24 +18,26 @@ func TestSource(t *testing.T) {
source := &Source{
Provider: "fake",
- authSource: &auth.Source{
- ID: 12,
- Type: auth.OAuth2,
- Name: "fake",
- IsActive: true,
- IsSyncEnabled: true,
+ ConfigBase: auth.ConfigBase{
+ AuthSource: &auth.Source{
+ ID: 12,
+ Type: auth.OAuth2,
+ Name: "fake",
+ IsActive: true,
+ IsSyncEnabled: true,
+ },
},
}
user := &user_model.User{
LoginName: "external",
LoginType: auth.OAuth2,
- LoginSource: source.authSource.ID,
+ LoginSource: source.AuthSource.ID,
Name: "test",
Email: "external@example.com",
}
- err := user_model.CreateUser(context.Background(), user, &user_model.Meta{}, &user_model.CreateUserOverwriteOptions{})
+ err := user_model.CreateUser(t.Context(), user, &user_model.Meta{}, &user_model.CreateUserOverwriteOptions{})
assert.NoError(t, err)
e := &user_model.ExternalLoginUser{
@@ -45,15 +46,15 @@ func TestSource(t *testing.T) {
LoginSourceID: user.LoginSource,
RefreshToken: "valid",
}
- err = user_model.LinkExternalToUser(context.Background(), user, e)
+ err = user_model.LinkExternalToUser(t.Context(), user, e)
assert.NoError(t, err)
- provider, err := createProvider(source.authSource.Name, source)
+ provider, err := createProvider(source.AuthSource.Name, source)
assert.NoError(t, err)
t.Run("refresh", func(t *testing.T) {
t.Run("valid", func(t *testing.T) {
- err := source.refresh(context.Background(), provider, e)
+ err := source.refresh(t.Context(), provider, e)
assert.NoError(t, err)
e := &user_model.ExternalLoginUser{
@@ -61,19 +62,19 @@ func TestSource(t *testing.T) {
LoginSourceID: e.LoginSourceID,
}
- ok, err := user_model.GetExternalLogin(context.Background(), e)
+ ok, err := user_model.GetExternalLogin(t.Context(), e)
assert.NoError(t, err)
assert.True(t, ok)
assert.Equal(t, "refresh", e.RefreshToken)
assert.Equal(t, "token", e.AccessToken)
- u, err := user_model.GetUserByID(context.Background(), user.ID)
+ u, err := user_model.GetUserByID(t.Context(), user.ID)
assert.NoError(t, err)
assert.True(t, u.IsActive)
})
t.Run("expired", func(t *testing.T) {
- err := source.refresh(context.Background(), provider, &user_model.ExternalLoginUser{
+ err := source.refresh(t.Context(), provider, &user_model.ExternalLoginUser{
ExternalID: "external",
UserID: user.ID,
LoginSourceID: user.LoginSource,
@@ -86,13 +87,13 @@ func TestSource(t *testing.T) {
LoginSourceID: e.LoginSourceID,
}
- ok, err := user_model.GetExternalLogin(context.Background(), e)
+ ok, err := user_model.GetExternalLogin(t.Context(), e)
assert.NoError(t, err)
assert.True(t, ok)
- assert.Equal(t, "", e.RefreshToken)
- assert.Equal(t, "", e.AccessToken)
+ assert.Empty(t, e.RefreshToken)
+ assert.Empty(t, e.AccessToken)
- u, err := user_model.GetUserByID(context.Background(), user.ID)
+ u, err := user_model.GetUserByID(t.Context(), user.ID)
assert.NoError(t, err)
assert.False(t, u.IsActive)
})
diff --git a/services/auth/source/oauth2/urlmapping.go b/services/auth/source/oauth2/urlmapping.go
index d0442d58a8..b9f445caa7 100644
--- a/services/auth/source/oauth2/urlmapping.go
+++ b/services/auth/source/oauth2/urlmapping.go
@@ -14,11 +14,11 @@ type CustomURLMapping struct {
// CustomURLSettings describes the urls values and availability to use when customizing OAuth2 provider URLs
type CustomURLSettings struct {
- AuthURL Attribute `json:",omitempty"`
- TokenURL Attribute `json:",omitempty"`
- ProfileURL Attribute `json:",omitempty"`
- EmailURL Attribute `json:",omitempty"`
- Tenant Attribute `json:",omitempty"`
+ AuthURL Attribute
+ TokenURL Attribute
+ ProfileURL Attribute
+ EmailURL Attribute
+ Tenant Attribute
}
// Attribute describes the availability, and required status for a custom url configuration
diff --git a/services/auth/source/pam/assert_interface_test.go b/services/auth/source/pam/assert_interface_test.go
index 8e7648b8d3..908d097d96 100644
--- a/services/auth/source/pam/assert_interface_test.go
+++ b/services/auth/source/pam/assert_interface_test.go
@@ -15,7 +15,6 @@ import (
type sourceInterface interface {
auth.PasswordAuthenticator
auth_model.Config
- auth_model.SourceSettable
}
var _ (sourceInterface) = &pam.Source{}
diff --git a/services/auth/source/pam/source.go b/services/auth/source/pam/source.go
index 96b182e185..d1db6db9b7 100644
--- a/services/auth/source/pam/source.go
+++ b/services/auth/source/pam/source.go
@@ -17,12 +17,10 @@ import (
// Source holds configuration for the PAM login source.
type Source struct {
- ServiceName string // pam service (e.g. system-auth)
- EmailDomain string
- SkipLocalTwoFA bool `json:",omitempty"` // Skip Local 2fa for users authenticated with this source
+ auth.ConfigBase `json:"-"`
- // reference to the authSource
- authSource *auth.Source
+ ServiceName string // pam service (e.g. system-auth)
+ EmailDomain string
}
// FromDB fills up a PAMConfig from serialized format.
@@ -35,11 +33,6 @@ func (source *Source) ToDB() ([]byte, error) {
return json.Marshal(source)
}
-// SetAuthSource sets the related AuthSource
-func (source *Source) SetAuthSource(authSource *auth.Source) {
- source.authSource = authSource
-}
-
func init() {
auth.RegisterTypeConfig(auth.PAM, &Source{})
}
diff --git a/services/auth/source/pam/source_authenticate.go b/services/auth/source/pam/source_authenticate.go
index 6fd02dc29f..db7c6aab96 100644
--- a/services/auth/source/pam/source_authenticate.go
+++ b/services/auth/source/pam/source_authenticate.go
@@ -56,7 +56,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
Email: email,
Passwd: password,
LoginType: auth.PAM,
- LoginSource: source.authSource.ID,
+ LoginSource: source.AuthSource.ID,
LoginName: userName, // This is what the user typed in
}
overwriteDefault := &user_model.CreateUserOverwriteOptions{
@@ -69,8 +69,3 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
return user, nil
}
-
-// IsSkipLocalTwoFA returns if this source should skip local 2fa for password authentication
-func (source *Source) IsSkipLocalTwoFA() bool {
- return source.SkipLocalTwoFA
-}
diff --git a/services/auth/source/smtp/assert_interface_test.go b/services/auth/source/smtp/assert_interface_test.go
index 6c9cde66e1..56edad0c71 100644
--- a/services/auth/source/smtp/assert_interface_test.go
+++ b/services/auth/source/smtp/assert_interface_test.go
@@ -18,7 +18,6 @@ type sourceInterface interface {
auth_model.SkipVerifiable
auth_model.HasTLSer
auth_model.UseTLSer
- auth_model.SourceSettable
}
var _ (sourceInterface) = &smtp.Source{}
diff --git a/services/auth/source/smtp/source.go b/services/auth/source/smtp/source.go
index 2a648e421e..2ae81ad4f2 100644
--- a/services/auth/source/smtp/source.go
+++ b/services/auth/source/smtp/source.go
@@ -17,6 +17,8 @@ import (
// Source holds configuration for the SMTP login source.
type Source struct {
+ auth.ConfigBase `json:"-"`
+
Auth string
Host string
Port int
@@ -25,10 +27,6 @@ type Source struct {
SkipVerify bool
HeloHostname string
DisableHelo bool
- SkipLocalTwoFA bool `json:",omitempty"`
-
- // reference to the authSource
- authSource *auth.Source
}
// FromDB fills up an SMTPConfig from serialized format.
@@ -56,11 +54,6 @@ func (source *Source) UseTLS() bool {
return source.ForceSMTPS || source.Port == 465
}
-// SetAuthSource sets the related AuthSource
-func (source *Source) SetAuthSource(authSource *auth.Source) {
- source.authSource = authSource
-}
-
func init() {
auth.RegisterTypeConfig(auth.SMTP, &Source{})
}
diff --git a/services/auth/source/smtp/source_authenticate.go b/services/auth/source/smtp/source_authenticate.go
index b2e94933a6..b8e668f5f9 100644
--- a/services/auth/source/smtp/source_authenticate.go
+++ b/services/auth/source/smtp/source_authenticate.go
@@ -72,7 +72,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
Email: userName,
Passwd: password,
LoginType: auth_model.SMTP,
- LoginSource: source.authSource.ID,
+ LoginSource: source.AuthSource.ID,
LoginName: userName,
}
overwriteDefault := &user_model.CreateUserOverwriteOptions{
@@ -85,8 +85,3 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
return user, nil
}
-
-// IsSkipLocalTwoFA returns if this source should skip local 2fa for password authentication
-func (source *Source) IsSkipLocalTwoFA() bool {
- return source.SkipLocalTwoFA
-}
diff --git a/services/auth/source/sspi/source.go b/services/auth/source/sspi/source.go
index bdd6ef451c..3b7b5cb033 100644
--- a/services/auth/source/sspi/source.go
+++ b/services/auth/source/sspi/source.go
@@ -17,6 +17,8 @@ import (
// Source holds configuration for SSPI single sign-on.
type Source struct {
+ auth.ConfigBase `json:"-"`
+
AutoCreateUsers bool
AutoActivateUsers bool
StripDomainNames bool
diff --git a/services/auth/sspi.go b/services/auth/sspi.go
index 8ac83dcb04..8cb39886c4 100644
--- a/services/auth/sspi.go
+++ b/services/auth/sspi.go
@@ -17,7 +17,6 @@ import (
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/services/auth/source/sspi"
gitea_context "code.gitea.io/gitea/services/context"
@@ -89,7 +88,7 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore,
store.GetData()["EnableSSPI"] = true
// in this case, the Verify function is called in Gitea's web context
// FIXME: it doesn't look good to render the page here, why not redirect?
- gitea_context.GetWebContext(req).HTML(http.StatusUnauthorized, tplSignIn)
+ gitea_context.GetWebContext(req.Context()).HTML(http.StatusUnauthorized, tplSignIn)
return nil, err
}
if outToken != "" {
@@ -120,7 +119,8 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore,
}
// Make sure requests to API paths and PWA resources do not create a new session
- if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) {
+ detector := newAuthPathDetector(req)
+ if !detector.isAPIPath() && !detector.isAttachmentDownload() {
handleSignIn(w, req, sess, user)
}
@@ -155,8 +155,9 @@ func (s *SSPI) shouldAuthenticate(req *http.Request) (shouldAuth bool) {
} else if req.FormValue("auth_with_sspi") == "1" {
shouldAuth = true
}
- } else if middleware.IsAPIPath(req) || isAttachmentDownload(req) {
- shouldAuth = true
+ } else {
+ detector := newAuthPathDetector(req)
+ shouldAuth = detector.isAPIPath() || detector.isAttachmentDownload()
}
return shouldAuth
}
diff --git a/services/automerge/automerge.go b/services/automerge/automerge.go
index a1ee204882..0520a097d3 100644
--- a/services/automerge/automerge.go
+++ b/services/automerge/automerge.go
@@ -24,6 +24,7 @@ import (
"code.gitea.io/gitea/modules/queue"
notify_service "code.gitea.io/gitea/services/notify"
pull_service "code.gitea.io/gitea/services/pull"
+ repo_service "code.gitea.io/gitea/services/repository"
)
// prAutoMergeQueue represents a queue to handle update pull request tests
@@ -35,7 +36,7 @@ func Init() error {
prAutoMergeQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "pr_auto_merge", handler)
if prAutoMergeQueue == nil {
- return fmt.Errorf("unable to create pr_auto_merge queue")
+ return errors.New("unable to create pr_auto_merge queue")
}
go graceful.GetManager().RunWithCancel(prAutoMergeQueue)
return nil
@@ -63,9 +64,9 @@ func addToQueue(pr *issues_model.PullRequest, sha string) {
}
// ScheduleAutoMerge if schedule is false and no error, pull can be merged directly
-func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pull *issues_model.PullRequest, style repo_model.MergeStyle, message string) (scheduled bool, err error) {
+func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pull *issues_model.PullRequest, style repo_model.MergeStyle, message string, deleteBranchAfterMerge bool) (scheduled bool, err error) {
err = db.WithTx(ctx, func(ctx context.Context) error {
- if err := pull_model.ScheduleAutoMerge(ctx, doer, pull.ID, style, message); err != nil {
+ if err := pull_model.ScheduleAutoMerge(ctx, doer, pull.ID, style, message, deleteBranchAfterMerge); err != nil {
return err
}
scheduled = true
@@ -247,13 +248,13 @@ func handlePullRequestAutoMerge(pullID int64, sha string) {
switch pr.Flow {
case issues_model.PullRequestFlowGithub:
- headBranchExist := headGitRepo.IsBranchExist(pr.HeadBranch)
- if pr.HeadRepo == nil || !headBranchExist {
+ headBranchExist := pr.HeadRepo != nil && gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch)
+ if !headBranchExist {
log.Warn("Head branch of auto merge %-v does not exist [HeadRepoID: %d, Branch: %s]", pr, pr.HeadRepoID, pr.HeadBranch)
return
}
case issues_model.PullRequestFlowAGit:
- headBranchExist := git.IsReferenceExist(ctx, baseGitRepo.Path, pr.GetGitRefName())
+ headBranchExist := gitrepo.IsReferenceExist(ctx, pr.BaseRepo, pr.GetGitRefName())
if !headBranchExist {
log.Warn("Head branch of auto merge %-v does not exist [HeadRepoID: %d, Branch(Agit): %s]", pr, pr.HeadRepoID, pr.HeadBranch)
return
@@ -288,7 +289,7 @@ func handlePullRequestAutoMerge(pullID int64, sha string) {
}
if err := pull_service.CheckPullMergeable(ctx, doer, &perm, pr, pull_service.MergeCheckTypeGeneral, false); err != nil {
- if errors.Is(err, pull_service.ErrUserNotAllowedToMerge) {
+ if errors.Is(err, pull_service.ErrNotReadyToMerge) {
log.Info("%-v was scheduled to automerge by an unauthorized user", pr)
return
}
@@ -303,4 +304,10 @@ func handlePullRequestAutoMerge(pullID int64, sha string) {
// on the pull request page. But this should not be finished in a bug fix PR which will be backport to release branch.
return
}
+
+ if pr.Flow == issues_model.PullRequestFlowGithub && scheduledPRM.DeleteBranchAfterMerge {
+ if err := repo_service.DeleteBranch(ctx, doer, pr.HeadRepo, headGitRepo, pr.HeadBranch, pr); err != nil {
+ log.Error("DeletePullRequestHeadBranch: %v", err)
+ }
+ }
}
diff --git a/services/context/access_log.go b/services/context/access_log.go
index 0926748ac5..caade113a7 100644
--- a/services/context/access_log.go
+++ b/services/context/access_log.go
@@ -5,7 +5,6 @@ package context
import (
"bytes"
- "fmt"
"net"
"net/http"
"strings"
@@ -18,13 +17,14 @@ import (
"code.gitea.io/gitea/modules/web/middleware"
)
-type routerLoggerOptions struct {
- req *http.Request
+type accessLoggerTmplData struct {
Identity *string
Start *time.Time
- ResponseWriter http.ResponseWriter
- Ctx map[string]any
- RequestID *string
+ ResponseWriter struct {
+ Status, Size int
+ }
+ Ctx map[string]any
+ RequestID *string
}
const keyOfRequestIDInTemplate = ".RequestID"
@@ -46,56 +46,70 @@ func parseRequestIDFromRequestHeader(req *http.Request) string {
}
}
if len(requestID) > maxRequestIDByteLength {
- requestID = fmt.Sprintf("%s...", requestID[:maxRequestIDByteLength])
+ requestID = requestID[:maxRequestIDByteLength] + "..."
}
return requestID
}
+type accessLogRecorder struct {
+ logger log.BaseLogger
+ logTemplate *template.Template
+ needRequestID bool
+}
+
+func (lr *accessLogRecorder) record(start time.Time, respWriter ResponseWriter, req *http.Request) {
+ var requestID string
+ if lr.needRequestID {
+ requestID = parseRequestIDFromRequestHeader(req)
+ }
+
+ reqHost, _, err := net.SplitHostPort(req.RemoteAddr)
+ if err != nil {
+ reqHost = req.RemoteAddr
+ }
+
+ identity := "-"
+ data := middleware.GetContextData(req.Context())
+ if signedUser, ok := data[middleware.ContextDataKeySignedUser].(*user_model.User); ok {
+ identity = signedUser.Name
+ }
+ buf := bytes.NewBuffer([]byte{})
+ tmplData := accessLoggerTmplData{
+ Identity: &identity,
+ Start: &start,
+ Ctx: map[string]any{
+ "RemoteAddr": req.RemoteAddr,
+ "RemoteHost": reqHost,
+ "Req": req,
+ },
+ RequestID: &requestID,
+ }
+ tmplData.ResponseWriter.Status = respWriter.WrittenStatus()
+ tmplData.ResponseWriter.Size = respWriter.WrittenSize()
+ err = lr.logTemplate.Execute(buf, tmplData)
+ if err != nil {
+ log.Error("Could not execute access logger template: %v", err.Error())
+ }
+
+ lr.logger.Log(1, &log.Event{Level: log.INFO}, "%s", buf.String())
+}
+
+func newAccessLogRecorder() *accessLogRecorder {
+ return &accessLogRecorder{
+ logger: log.GetLogger("access"),
+ logTemplate: template.Must(template.New("log").Parse(setting.Log.AccessLogTemplate)),
+ needRequestID: len(setting.Log.RequestIDHeaders) > 0 && strings.Contains(setting.Log.AccessLogTemplate, keyOfRequestIDInTemplate),
+ }
+}
+
// AccessLogger returns a middleware to log access logger
func AccessLogger() func(http.Handler) http.Handler {
- logger := log.GetLogger("access")
- needRequestID := len(setting.Log.RequestIDHeaders) > 0 && strings.Contains(setting.Log.AccessLogTemplate, keyOfRequestIDInTemplate)
- logTemplate, _ := template.New("log").Parse(setting.Log.AccessLogTemplate)
+ recorder := newAccessLogRecorder()
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
start := time.Now()
-
- var requestID string
- if needRequestID {
- requestID = parseRequestIDFromRequestHeader(req)
- }
-
- reqHost, _, err := net.SplitHostPort(req.RemoteAddr)
- if err != nil {
- reqHost = req.RemoteAddr
- }
-
next.ServeHTTP(w, req)
- rw := w.(ResponseWriter)
-
- identity := "-"
- data := middleware.GetContextData(req.Context())
- if signedUser, ok := data[middleware.ContextDataKeySignedUser].(*user_model.User); ok {
- identity = signedUser.Name
- }
- buf := bytes.NewBuffer([]byte{})
- err = logTemplate.Execute(buf, routerLoggerOptions{
- req: req,
- Identity: &identity,
- Start: &start,
- ResponseWriter: rw,
- Ctx: map[string]any{
- "RemoteAddr": req.RemoteAddr,
- "RemoteHost": reqHost,
- "Req": req,
- },
- RequestID: &requestID,
- })
- if err != nil {
- log.Error("Could not execute access logger template: %v", err.Error())
- }
-
- logger.Info("%s", buf.String())
+ recorder.record(start, w.(ResponseWriter), req)
})
}
}
diff --git a/services/context/access_log_test.go b/services/context/access_log_test.go
new file mode 100644
index 0000000000..139a6eb217
--- /dev/null
+++ b/services/context/access_log_test.go
@@ -0,0 +1,71 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "fmt"
+ "net/http"
+ "net/url"
+ "testing"
+ "time"
+
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+
+ "github.com/stretchr/testify/assert"
+)
+
+type testAccessLoggerMock struct {
+ logs []string
+}
+
+func (t *testAccessLoggerMock) Log(skip int, event *log.Event, format string, v ...any) {
+ t.logs = append(t.logs, fmt.Sprintf(format, v...))
+}
+
+func (t *testAccessLoggerMock) GetLevel() log.Level {
+ return log.INFO
+}
+
+type testAccessLoggerResponseWriterMock struct{}
+
+func (t testAccessLoggerResponseWriterMock) Header() http.Header {
+ return nil
+}
+
+func (t testAccessLoggerResponseWriterMock) Before(f func(ResponseWriter)) {}
+
+func (t testAccessLoggerResponseWriterMock) WriteHeader(statusCode int) {}
+
+func (t testAccessLoggerResponseWriterMock) Write(bytes []byte) (int, error) {
+ return 0, nil
+}
+
+func (t testAccessLoggerResponseWriterMock) Flush() {}
+
+func (t testAccessLoggerResponseWriterMock) WrittenStatus() int {
+ return http.StatusOK
+}
+
+func (t testAccessLoggerResponseWriterMock) WrittenSize() int {
+ return 123123
+}
+
+func TestAccessLogger(t *testing.T) {
+ setting.Log.AccessLogTemplate = `{{.Ctx.RemoteHost}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}" "{{.Ctx.Req.UserAgent}}"`
+ recorder := newAccessLogRecorder()
+ mockLogger := &testAccessLoggerMock{}
+ recorder.logger = mockLogger
+ req := &http.Request{
+ RemoteAddr: "remote-addr",
+ Method: http.MethodGet,
+ Proto: "https",
+ URL: &url.URL{Path: "/path"},
+ }
+ req.Header = http.Header{}
+ req.Header.Add("Referer", "referer")
+ req.Header.Add("User-Agent", "user-agent")
+ recorder.record(time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC), &testAccessLoggerResponseWriterMock{}, req)
+ assert.Equal(t, []string{`remote-addr - - [02/Jan/2000:03:04:05 +0000] "GET /path https" 200 123123 "referer" "user-agent"`}, mockLogger.logs)
+}
diff --git a/services/context/api.go b/services/context/api.go
index bda705cb48..ab50a360f4 100644
--- a/services/context/api.go
+++ b/services/context/api.go
@@ -5,23 +5,31 @@
package context
import (
+ "errors"
"fmt"
"net/http"
"net/url"
+ "slices"
+ "strconv"
"strings"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/cache"
+ "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
web_types "code.gitea.io/gitea/modules/web/types"
)
// APIContext is a specific context for API service
+// ATTENTION: This struct should never be manually constructed in routes/services,
+// it has many internal details which should be carefully prepared by the framework.
+// If it is abused, it would cause strange bugs like panic/resource-leak.
type APIContext struct {
*Base
@@ -103,14 +111,28 @@ type APIRepoArchivedError struct {
APIError
}
-// ServerError responds with error message, status is 500
-func (ctx *APIContext) ServerError(title string, err error) {
- ctx.Error(http.StatusInternalServerError, title, err)
+// APIErrorInternal responds with error message, status is 500
+func (ctx *APIContext) APIErrorInternal(err error) {
+ ctx.apiErrorInternal(1, err)
}
-// Error responds with an error message to client with given obj as the message.
+func (ctx *APIContext) apiErrorInternal(skip int, err error) {
+ log.ErrorWithSkip(skip+1, "InternalServerError: %v", err)
+
+ var message string
+ if !setting.IsProd || (ctx.Doer != nil && ctx.Doer.IsAdmin) {
+ message = err.Error()
+ }
+
+ ctx.JSON(http.StatusInternalServerError, APIError{
+ Message: message,
+ URL: setting.API.SwaggerURL,
+ })
+}
+
+// APIError responds with an error message to client with given obj as the message.
// If status is 500, also it prints error to log.
-func (ctx *APIContext) Error(status int, title string, obj any) {
+func (ctx *APIContext) APIError(status int, obj any) {
var message string
if err, ok := obj.(error); ok {
message = err.Error()
@@ -119,7 +141,7 @@ func (ctx *APIContext) Error(status int, title string, obj any) {
}
if status == http.StatusInternalServerError {
- log.ErrorWithSkip(1, "%s: %s", title, message)
+ log.ErrorWithSkip(1, "APIError: %s", message)
if setting.IsProd && !(ctx.Doer != nil && ctx.Doer.IsAdmin) {
message = ""
@@ -132,22 +154,6 @@ func (ctx *APIContext) Error(status int, title string, obj any) {
})
}
-// InternalServerError responds with an error message to the client with the error as a message
-// and the file and line of the caller.
-func (ctx *APIContext) InternalServerError(err error) {
- log.ErrorWithSkip(1, "InternalServerError: %v", err)
-
- var message string
- if !setting.IsProd || (ctx.Doer != nil && ctx.Doer.IsAdmin) {
- message = err.Error()
- }
-
- ctx.JSON(http.StatusInternalServerError, APIError{
- Message: message,
- URL: setting.API.SwaggerURL,
- })
-}
-
type apiContextKeyType struct{}
var apiContextKey = apiContextKeyType{}
@@ -165,7 +171,7 @@ func genAPILinks(curURL *url.URL, total, pageSize, curPage int) []string {
if paginater.HasNext() {
u := *curURL
queries := u.Query()
- queries.Set("page", fmt.Sprintf("%d", paginater.Next()))
+ queries.Set("page", strconv.Itoa(paginater.Next()))
u.RawQuery = queries.Encode()
links = append(links, fmt.Sprintf("<%s%s>; rel=\"next\"", setting.AppURL, u.RequestURI()[1:]))
@@ -173,7 +179,7 @@ func genAPILinks(curURL *url.URL, total, pageSize, curPage int) []string {
if !paginater.IsLast() {
u := *curURL
queries := u.Query()
- queries.Set("page", fmt.Sprintf("%d", paginater.TotalPages()))
+ queries.Set("page", strconv.Itoa(paginater.TotalPages()))
u.RawQuery = queries.Encode()
links = append(links, fmt.Sprintf("<%s%s>; rel=\"last\"", setting.AppURL, u.RequestURI()[1:]))
@@ -189,7 +195,7 @@ func genAPILinks(curURL *url.URL, total, pageSize, curPage int) []string {
if paginater.HasPrevious() {
u := *curURL
queries := u.Query()
- queries.Set("page", fmt.Sprintf("%d", paginater.Previous()))
+ queries.Set("page", strconv.Itoa(paginater.Previous()))
u.RawQuery = queries.Encode()
links = append(links, fmt.Sprintf("<%s%s>; rel=\"prev\"", setting.AppURL, u.RequestURI()[1:]))
@@ -207,7 +213,7 @@ func (ctx *APIContext) SetLinkHeader(total, pageSize int) {
}
}
-// APIContexter returns apicontext as middleware
+// APIContexter returns APIContext middleware
func APIContexter() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
@@ -222,14 +228,14 @@ func APIContexter() func(http.Handler) http.Handler {
ctx.SetContextValue(apiContextKey, ctx)
// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
- if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
+ if ctx.Req.Method == http.MethodPost && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
if err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
- ctx.InternalServerError(err)
+ ctx.APIErrorInternal(err)
return
}
}
- httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
+ httpcache.SetCacheControlInHeader(ctx.Resp.Header(), &httpcache.CacheControlOptions{NoTransform: true})
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
next.ServeHTTP(ctx.Resp, ctx.Req)
@@ -237,11 +243,11 @@ func APIContexter() func(http.Handler) http.Handler {
}
}
-// NotFound handles 404s for APIContext
+// APIErrorNotFound handles 404s for APIContext
// String will replace message, errors will be added to a slice
-func (ctx *APIContext) NotFound(objs ...any) {
- message := ctx.Locale.TrString("error.not_found")
- var errors []string
+func (ctx *APIContext) APIErrorNotFound(objs ...any) {
+ var message string
+ var errs []string
for _, obj := range objs {
// Ignore nil
if obj == nil {
@@ -249,16 +255,15 @@ func (ctx *APIContext) NotFound(objs ...any) {
}
if err, ok := obj.(error); ok {
- errors = append(errors, err.Error())
+ errs = append(errs, err.Error())
} else {
message = obj.(string)
}
}
-
ctx.JSON(http.StatusNotFound, map[string]any{
- "message": message,
+ "message": util.IfZero(message, "not found"), // do not use locale in API
"url": setting.API.SwaggerURL,
- "errors": errors,
+ "errors": errs,
})
}
@@ -276,7 +281,7 @@ func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) {
var err error
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
if err != nil {
- ctx.Error(http.StatusInternalServerError, fmt.Sprintf("Open Repository %v failed", ctx.Repo.Repository.FullName()), err)
+ ctx.APIErrorInternal(err)
return
}
}
@@ -288,41 +293,33 @@ func RepoRefForAPI(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
ctx := GetAPIContext(req)
- if ctx.Repo.GitRepo == nil {
- ctx.InternalServerError(fmt.Errorf("no open git repo"))
+ if ctx.Repo.Repository.IsEmpty {
+ ctx.APIErrorNotFound("repository is empty")
return
}
- // NOTICE: the "ref" here for internal usage only (e.g. woodpecker)
- refName, _ := getRefNameLegacy(ctx.Base, ctx.Repo, ctx.FormTrim("ref"))
- var err error
+ if ctx.Repo.GitRepo == nil {
+ panic("no GitRepo, forgot to call the middleware?") // it is a programming error
+ }
- if ctx.Repo.GitRepo.IsBranchExist(refName) {
+ refName, refType, _ := getRefNameLegacy(ctx.Base, ctx.Repo, ctx.PathParam("*"), ctx.FormTrim("ref"))
+ var err error
+ switch refType {
+ case git.RefTypeBranch:
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
- if err != nil {
- ctx.InternalServerError(err)
- return
- }
- ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
- } else if ctx.Repo.GitRepo.IsTagExist(refName) {
+ case git.RefTypeTag:
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
- if err != nil {
- ctx.InternalServerError(err)
- return
- }
- ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
- } else if len(refName) == ctx.Repo.GetObjectFormat().FullLength() {
- ctx.Repo.CommitID = refName
+ case git.RefTypeCommit:
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
- if err != nil {
- ctx.NotFound("GetCommit", err)
- return
- }
- } else {
- ctx.NotFound(fmt.Errorf("not exist: '%s'", ctx.PathParam("*")))
+ }
+ if ctx.Repo.Commit == nil || errors.Is(err, util.ErrNotExist) {
+ ctx.APIErrorNotFound("unable to find a git ref")
+ return
+ } else if err != nil {
+ ctx.APIErrorInternal(err)
return
}
-
+ ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
next.ServeHTTP(w, req)
})
}
@@ -348,12 +345,12 @@ func (ctx *APIContext) GetErrMsg() string {
// NotFoundOrServerError use error check function to determine if the error
// is about not found. It responds with 404 status code for not found error,
// or error context description for logging purpose of 500 server error.
-func (ctx *APIContext) NotFoundOrServerError(logMsg string, errCheck func(error) bool, logErr error) {
- if errCheck(logErr) {
+func (ctx *APIContext) NotFoundOrServerError(err error) {
+ if errors.Is(err, util.ErrNotExist) {
ctx.JSON(http.StatusNotFound, nil)
return
}
- ctx.Error(http.StatusInternalServerError, "NotFoundOrServerError", logMsg)
+ ctx.APIErrorInternal(err)
}
// IsUserSiteAdmin returns true if current user is a site admin
@@ -366,13 +363,7 @@ func (ctx *APIContext) IsUserRepoAdmin() bool {
return ctx.Repo.IsAdmin()
}
-// IsUserRepoWriter returns true if current user has write privilege in current repo
+// IsUserRepoWriter returns true if current user has "write" privilege in current repo
func (ctx *APIContext) IsUserRepoWriter(unitTypes []unit.Type) bool {
- for _, unitType := range unitTypes {
- if ctx.Repo.CanWrite(unitType) {
- return true
- }
- }
-
- return false
+ return slices.ContainsFunc(unitTypes, ctx.Repo.CanWrite)
}
diff --git a/services/context/api_test.go b/services/context/api_test.go
index 911a49949e..87d74004db 100644
--- a/services/context/api_test.go
+++ b/services/context/api_test.go
@@ -45,6 +45,6 @@ func TestGenAPILinks(t *testing.T) {
links := genAPILinks(u, 100, 20, curPage)
- assert.EqualValues(t, links, response)
+ assert.Equal(t, links, response)
}
}
diff --git a/services/context/base.go b/services/context/base.go
index 7a39353e09..f3f92b7eeb 100644
--- a/services/context/base.go
+++ b/services/context/base.go
@@ -4,11 +4,11 @@
package context
import (
- "context"
"fmt"
"html/template"
"io"
"net/http"
+ "strconv"
"strings"
"code.gitea.io/gitea/modules/httplib"
@@ -24,9 +24,12 @@ type BaseContextKeyType struct{}
var BaseContextKey BaseContextKeyType
+// Base is the base context for all web handlers
+// ATTENTION: This struct should never be manually constructed in routes/services,
+// it has many internal details which should be carefully prepared by the framework.
+// If it is abused, it would cause strange bugs like panic/resource-leak.
type Base struct {
- context.Context
- reqctx.RequestDataStore
+ reqctx.RequestContext
Resp ResponseWriter
Req *http.Request
@@ -51,7 +54,7 @@ func (b *Base) AppendAccessControlExposeHeaders(names ...string) {
// SetTotalCountHeader set "X-Total-Count" header
func (b *Base) SetTotalCountHeader(total int64) {
- b.RespHeader().Set("X-Total-Count", fmt.Sprint(total))
+ b.RespHeader().Set("X-Total-Count", strconv.FormatInt(total, 10))
b.AppendAccessControlExposeHeaders("X-Total-Count")
}
@@ -79,8 +82,8 @@ func (b *Base) RespHeader() http.Header {
return b.Resp.Header()
}
-// Error returned an error to web browser
-func (b *Base) Error(status int, contents ...string) {
+// HTTPError returned an error to web browser
+func (b *Base) HTTPError(status int, contents ...string) {
v := http.StatusText(status)
if len(contents) > 0 {
v = contents[0]
@@ -172,19 +175,19 @@ func (b *Base) TrN(cnt any, key1, keyN string, args ...any) template.HTML {
}
func NewBaseContext(resp http.ResponseWriter, req *http.Request) *Base {
- ds := reqctx.GetRequestDataStore(req.Context())
+ reqCtx := reqctx.FromContext(req.Context())
b := &Base{
- Context: req.Context(),
- RequestDataStore: ds,
- Req: req,
- Resp: WrapResponseWriter(resp),
- Locale: middleware.Locale(resp, req),
- Data: ds.GetData(),
+ RequestContext: reqCtx,
+
+ Req: req,
+ Resp: WrapResponseWriter(resp),
+ Locale: middleware.Locale(resp, req),
+ Data: reqCtx.GetData(),
}
b.Req = b.Req.WithContext(b)
- ds.SetContextValue(BaseContextKey, b)
- ds.SetContextValue(translation.ContextKey, b.Locale)
- ds.SetContextValue(httplib.RequestContextKey, b.Req)
+ reqCtx.SetContextValue(BaseContextKey, b)
+ reqCtx.SetContextValue(translation.ContextKey, b.Locale)
+ reqCtx.SetContextValue(httplib.RequestContextKey, b.Req)
return b
}
diff --git a/services/context/base_form.go b/services/context/base_form.go
index 5b8cae9e99..81fd7cd328 100644
--- a/services/context/base_form.go
+++ b/services/context/base_form.go
@@ -12,6 +12,8 @@ import (
)
// FormString returns the first value matching the provided key in the form as a string
+// It works the same as http.Request.FormValue:
+// try urlencoded request body first, then query string, then multipart form body
func (b *Base) FormString(key string, def ...string) string {
s := b.Req.FormValue(key)
if s == "" {
@@ -20,7 +22,7 @@ func (b *Base) FormString(key string, def ...string) string {
return s
}
-// FormStrings returns a string slice for the provided key from the form
+// FormStrings returns a values for the key in the form (including query parameters), similar to FormString
func (b *Base) FormStrings(key string) []string {
if b.Req.Form == nil {
if err := b.Req.ParseMultipartForm(32 << 20); err != nil {
diff --git a/services/context/base_test.go b/services/context/base_test.go
index b936b76f58..2a4f86dddf 100644
--- a/services/context/base_test.go
+++ b/services/context/base_test.go
@@ -15,7 +15,7 @@ import (
func TestRedirect(t *testing.T) {
setting.IsInTesting = true
- req, _ := http.NewRequest("GET", "/", nil)
+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
cases := []struct {
url string
@@ -36,7 +36,7 @@ func TestRedirect(t *testing.T) {
assert.Equal(t, c.keep, has, "url = %q", c.url)
}
- req, _ = http.NewRequest("GET", "/", nil)
+ req, _ = http.NewRequest(http.MethodGet, "/", nil)
resp := httptest.NewRecorder()
req.Header.Add("HX-Request", "true")
b := NewBaseContextForTest(resp, req)
diff --git a/services/context/context.go b/services/context/context.go
index 6715c5663d..32ec260aab 100644
--- a/services/context/context.go
+++ b/services/context/context.go
@@ -23,6 +23,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/translation"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/modules/web/middleware"
web_types "code.gitea.io/gitea/modules/web/types"
@@ -34,7 +35,10 @@ type Render interface {
HTML(w io.Writer, status int, name templates.TplName, data any, templateCtx context.Context) error
}
-// Context represents context of a request.
+// Context represents context of a web request.
+// ATTENTION: This struct should never be manually constructed in routes/services,
+// it has many internal details which should be carefully prepared by the framework.
+// If it is abused, it would cause strange bugs like panic/resource-leak.
type Context struct {
*Base
@@ -76,9 +80,9 @@ type webContextKeyType struct{}
var WebContextKey = webContextKeyType{}
-func GetWebContext(req *http.Request) *Context {
- ctx, _ := req.Context().Value(WebContextKey).(*Context)
- return ctx
+func GetWebContext(ctx context.Context) *Context {
+ webCtx, _ := ctx.Value(WebContextKey).(*Context)
+ return webCtx
}
// ValidateContext is a special context for form validation middleware. It may be different from other contexts.
@@ -132,6 +136,7 @@ func NewWebContext(base *Base, render Render, session session.Store) *Context {
}
ctx.TemplateContext = NewTemplateContextForWeb(ctx)
ctx.Flash = &middleware.Flash{DataStore: ctx, Values: url.Values{}}
+ ctx.SetContextValue(WebContextKey, ctx)
return ctx
}
@@ -162,21 +167,12 @@ func Contexter() func(next http.Handler) http.Handler {
ctx.PageData = map[string]any{}
ctx.Data["PageData"] = ctx.PageData
- ctx.Base.SetContextValue(WebContextKey, ctx)
ctx.Csrf = NewCSRFProtector(csrfOpts)
- // Get the last flash message from cookie
- lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
+ // get the last flash message from cookie
+ lastFlashCookie, lastFlashMsg := middleware.GetSiteCookieFlashMessage(ctx, ctx.Req, CookieNameFlash)
if vals, _ := url.ParseQuery(lastFlashCookie); len(vals) > 0 {
- // store last Flash message into the template data, to render it
- ctx.Data["Flash"] = &middleware.Flash{
- DataStore: ctx,
- Values: vals,
- ErrorMsg: vals.Get("error"),
- SuccessMsg: vals.Get("success"),
- InfoMsg: vals.Get("info"),
- WarningMsg: vals.Get("warning"),
- }
+ ctx.Data["Flash"] = lastFlashMsg // store last Flash message into the template data, to render it
}
// if there are new messages in the ctx.Flash, write them into cookie
@@ -189,18 +185,20 @@ func Contexter() func(next http.Handler) http.Handler {
})
// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
- if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
+ if ctx.Req.Method == http.MethodPost && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
if err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
ctx.ServerError("ParseMultipartForm", err)
return
}
}
- httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
+ httpcache.SetCacheControlInHeader(ctx.Resp.Header(), &httpcache.CacheControlOptions{NoTransform: true})
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
ctx.Data["SystemConfig"] = setting.Config()
+ ctx.Data["ShowTwoFactorRequiredMessage"] = ctx.DoerNeedTwoFactorAuth()
+
// FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
ctx.Data["DisableStars"] = setting.Repository.DisableStars
@@ -214,17 +212,27 @@ func Contexter() func(next http.Handler) http.Handler {
}
}
+func (ctx *Context) DoerNeedTwoFactorAuth() bool {
+ if !setting.TwoFactorAuthEnforced {
+ return false
+ }
+ return ctx.Session.Get(session.KeyUserHasTwoFactorAuth) == false
+}
+
// HasError returns true if error occurs in form validation.
// Attention: this function changes ctx.Data and ctx.Flash
// If HasError is called, then before Redirect, the error message should be stored by ctx.Flash.Error(ctx.GetErrMsg()) again.
func (ctx *Context) HasError() bool {
- hasErr, ok := ctx.Data["HasError"]
- if !ok {
+ hasErr, _ := ctx.Data["HasError"].(bool)
+ hasErr = hasErr || ctx.Flash.ErrorMsg != ""
+ if !hasErr {
return false
}
- ctx.Flash.ErrorMsg = ctx.GetErrMsg()
+ if ctx.Flash.ErrorMsg == "" {
+ ctx.Flash.ErrorMsg = ctx.GetErrMsg()
+ }
ctx.Data["Flash"] = ctx.Flash
- return hasErr.(bool)
+ return hasErr
}
// GetErrMsg returns error message in form validation.
@@ -254,3 +262,11 @@ func (ctx *Context) JSONError(msg any) {
panic(fmt.Sprintf("unsupported type: %T", msg))
}
}
+
+func (ctx *Context) JSONErrorNotFound(optMsg ...string) {
+ msg := util.OptionalArg(optMsg)
+ if msg == "" {
+ msg = ctx.Locale.TrString("error.not_found")
+ }
+ ctx.JSON(http.StatusNotFound, map[string]any{"errorMessage": msg, "renderFormat": "text"})
+}
diff --git a/services/context/context_model.go b/services/context/context_model.go
index 4f70aac516..3a1776102f 100644
--- a/services/context/context_model.go
+++ b/services/context/context_model.go
@@ -3,27 +3,7 @@
package context
-import (
- "code.gitea.io/gitea/models/unit"
-)
-
// IsUserSiteAdmin returns true if current user is a site admin
func (ctx *Context) IsUserSiteAdmin() bool {
return ctx.IsSigned && ctx.Doer.IsAdmin
}
-
-// IsUserRepoAdmin returns true if current user is admin in current repo
-func (ctx *Context) IsUserRepoAdmin() bool {
- return ctx.Repo.IsAdmin()
-}
-
-// IsUserRepoWriter returns true if current user has write privilege in current repo
-func (ctx *Context) IsUserRepoWriter(unitTypes []unit.Type) bool {
- for _, unitType := range unitTypes {
- if ctx.Repo.CanWrite(unitType) {
- return true
- }
- }
-
- return false
-}
diff --git a/services/context/context_response.go b/services/context/context_response.go
index c7044791eb..3f64fc7352 100644
--- a/services/context/context_response.go
+++ b/services/context/context_response.go
@@ -28,7 +28,7 @@ import (
func RedirectToUser(ctx *Base, userName string, redirectUserID int64) {
user, err := user_model.GetUserByID(ctx, redirectUserID)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "unable to get user")
+ ctx.HTTPError(http.StatusInternalServerError, "unable to get user")
return
}
@@ -92,7 +92,7 @@ func (ctx *Context) HTML(status int, name templates.TplName) {
}
// JSONTemplate renders the template as JSON response
-// keep in mind that the template is processed in HTML context, so JSON-things should be handled carefully, eg: by JSEscape
+// keep in mind that the template is processed in HTML context, so JSON things should be handled carefully, e.g.: use JSEscape
func (ctx *Context) JSONTemplate(tmpl templates.TplName) {
t, err := ctx.Render.TemplateLookup(string(tmpl), nil)
if err != nil {
@@ -122,8 +122,8 @@ func (ctx *Context) RenderWithErr(msg any, tpl templates.TplName, form any) {
}
// NotFound displays a 404 (Not Found) page and prints the given error, if any.
-func (ctx *Context) NotFound(logMsg string, logErr error) {
- ctx.notFoundInternal(logMsg, logErr)
+func (ctx *Context) NotFound(logErr error) {
+ ctx.notFoundInternal("", logErr)
}
func (ctx *Context) notFoundInternal(logMsg string, logErr error) {
@@ -150,7 +150,7 @@ func (ctx *Context) notFoundInternal(logMsg string, logErr error) {
ctx.Data["IsRepo"] = ctx.Repo.Repository != nil
ctx.Data["Title"] = "Page Not Found"
- ctx.HTML(http.StatusNotFound, templates.TplName("status/404"))
+ ctx.HTML(http.StatusNotFound, "status/404")
}
// ServerError displays a 500 (Internal Server Error) page and prints the given error, if any.
diff --git a/services/context/org.go b/services/context/org.go
index be87cef7a3..c8b6ed09b7 100644
--- a/services/context/org.go
+++ b/services/context/org.go
@@ -51,7 +51,7 @@ func GetOrganizationByParams(ctx *Context) {
if err == nil {
RedirectToUser(ctx.Base, orgName, redirectUserID)
} else if user_model.IsErrUserRedirectNotExist(err) {
- ctx.NotFound("GetUserByName", err)
+ ctx.NotFound(err)
} else {
ctx.ServerError("LookupUserRedirect", err)
}
@@ -62,215 +62,193 @@ func GetOrganizationByParams(ctx *Context) {
}
}
-// HandleOrgAssignment handles organization assignment
-func HandleOrgAssignment(ctx *Context, args ...bool) {
- var (
- requireMember bool
- requireOwner bool
- requireTeamMember bool
- requireTeamAdmin bool
- )
- if len(args) >= 1 {
- requireMember = args[0]
- }
- if len(args) >= 2 {
- requireOwner = args[1]
- }
- if len(args) >= 3 {
- requireTeamMember = args[2]
- }
- if len(args) >= 4 {
- requireTeamAdmin = args[3]
- }
-
- var err error
+type OrgAssignmentOptions struct {
+ RequireMember bool
+ RequireOwner bool
+ RequireTeamMember bool
+ RequireTeamAdmin bool
+}
- if ctx.ContextUser == nil {
- // if Organization is not defined, get it from params
- if ctx.Org.Organization == nil {
- GetOrganizationByParams(ctx)
- if ctx.Written() {
- return
+// OrgAssignment returns a middleware to handle organization assignment
+func OrgAssignment(opts OrgAssignmentOptions) func(ctx *Context) {
+ return func(ctx *Context) {
+ var err error
+ if ctx.ContextUser == nil {
+ // if Organization is not defined, get it from params
+ if ctx.Org.Organization == nil {
+ GetOrganizationByParams(ctx)
+ if ctx.Written() {
+ return
+ }
}
+ } else if ctx.ContextUser.IsOrganization() {
+ ctx.Org.Organization = (*organization.Organization)(ctx.ContextUser)
+ } else {
+ // ContextUser is an individual User
+ return
}
- } else if ctx.ContextUser.IsOrganization() {
- if ctx.Org == nil {
- ctx.Org = &Organization{}
- }
- ctx.Org.Organization = (*organization.Organization)(ctx.ContextUser)
- } else {
- // ContextUser is an individual User
- return
- }
- org := ctx.Org.Organization
+ org := ctx.Org.Organization
- // Handle Visibility
- if org.Visibility != structs.VisibleTypePublic && !ctx.IsSigned {
- // We must be signed in to see limited or private organizations
- ctx.NotFound("OrgAssignment", err)
- return
- }
-
- if org.Visibility == structs.VisibleTypePrivate {
- requireMember = true
- } else if ctx.IsSigned && ctx.Doer.IsRestricted {
- requireMember = true
- }
-
- ctx.ContextUser = org.AsUser()
- ctx.Data["Org"] = org
-
- // Admin has super access.
- if ctx.IsSigned && ctx.Doer.IsAdmin {
- ctx.Org.IsOwner = true
- ctx.Org.IsMember = true
- ctx.Org.IsTeamMember = true
- ctx.Org.IsTeamAdmin = true
- ctx.Org.CanCreateOrgRepo = true
- } else if ctx.IsSigned {
- ctx.Org.IsOwner, err = org.IsOwnedBy(ctx, ctx.Doer.ID)
- if err != nil {
- ctx.ServerError("IsOwnedBy", err)
+ // Handle Visibility
+ if org.Visibility != structs.VisibleTypePublic && !ctx.IsSigned {
+ // We must be signed in to see limited or private organizations
+ ctx.NotFound(err)
return
}
- if ctx.Org.IsOwner {
+ if org.Visibility == structs.VisibleTypePrivate {
+ opts.RequireMember = true
+ } else if ctx.IsSigned && ctx.Doer.IsRestricted {
+ opts.RequireMember = true
+ }
+
+ ctx.ContextUser = org.AsUser()
+ ctx.Data["Org"] = org
+
+ // Admin has super access.
+ if ctx.IsSigned && ctx.Doer.IsAdmin {
+ ctx.Org.IsOwner = true
ctx.Org.IsMember = true
ctx.Org.IsTeamMember = true
ctx.Org.IsTeamAdmin = true
ctx.Org.CanCreateOrgRepo = true
- } else {
- ctx.Org.IsMember, err = org.IsOrgMember(ctx, ctx.Doer.ID)
+ } else if ctx.IsSigned {
+ ctx.Org.IsOwner, err = org.IsOwnedBy(ctx, ctx.Doer.ID)
if err != nil {
- ctx.ServerError("IsOrgMember", err)
+ ctx.ServerError("IsOwnedBy", err)
return
}
- ctx.Org.CanCreateOrgRepo, err = org.CanCreateOrgRepo(ctx, ctx.Doer.ID)
- if err != nil {
- ctx.ServerError("CanCreateOrgRepo", err)
- return
+
+ if ctx.Org.IsOwner {
+ ctx.Org.IsMember = true
+ ctx.Org.IsTeamMember = true
+ ctx.Org.IsTeamAdmin = true
+ ctx.Org.CanCreateOrgRepo = true
+ } else {
+ ctx.Org.IsMember, err = org.IsOrgMember(ctx, ctx.Doer.ID)
+ if err != nil {
+ ctx.ServerError("IsOrgMember", err)
+ return
+ }
+ ctx.Org.CanCreateOrgRepo, err = org.CanCreateOrgRepo(ctx, ctx.Doer.ID)
+ if err != nil {
+ ctx.ServerError("CanCreateOrgRepo", err)
+ return
+ }
}
+ } else {
+ // Fake data.
+ ctx.Data["SignedUser"] = &user_model.User{}
}
- } else {
- // Fake data.
- ctx.Data["SignedUser"] = &user_model.User{}
- }
- if (requireMember && !ctx.Org.IsMember) ||
- (requireOwner && !ctx.Org.IsOwner) {
- ctx.NotFound("OrgAssignment", err)
- return
- }
- ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
- ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
- ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
- ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
- ctx.Data["IsPublicMember"] = func(uid int64) bool {
- is, _ := organization.IsPublicMembership(ctx, ctx.Org.Organization.ID, uid)
- return is
- }
- ctx.Data["CanCreateOrgRepo"] = ctx.Org.CanCreateOrgRepo
+ if (opts.RequireMember && !ctx.Org.IsMember) || (opts.RequireOwner && !ctx.Org.IsOwner) {
+ ctx.NotFound(err)
+ return
+ }
+ ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
+ ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
+ ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
+ ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
+ ctx.Data["IsPublicMember"] = func(uid int64) bool {
+ is, _ := organization.IsPublicMembership(ctx, ctx.Org.Organization.ID, uid)
+ return is
+ }
+ ctx.Data["CanCreateOrgRepo"] = ctx.Org.CanCreateOrgRepo
- ctx.Org.OrgLink = org.AsUser().OrganisationLink()
- ctx.Data["OrgLink"] = ctx.Org.OrgLink
+ ctx.Org.OrgLink = org.AsUser().OrganisationLink()
+ ctx.Data["OrgLink"] = ctx.Org.OrgLink
- // Member
- opts := &organization.FindOrgMembersOpts{
- Doer: ctx.Doer,
- OrgID: org.ID,
- IsDoerMember: ctx.Org.IsMember,
- }
- ctx.Data["NumMembers"], err = organization.CountOrgMembers(ctx, opts)
- if err != nil {
- ctx.ServerError("CountOrgMembers", err)
- return
- }
+ // Member
+ findMembersOpts := &organization.FindOrgMembersOpts{
+ Doer: ctx.Doer,
+ OrgID: org.ID,
+ IsDoerMember: ctx.Org.IsMember,
+ }
+ ctx.Data["NumMembers"], err = organization.CountOrgMembers(ctx, findMembersOpts)
+ if err != nil {
+ ctx.ServerError("CountOrgMembers", err)
+ return
+ }
- // Team.
- if ctx.Org.IsMember {
- shouldSeeAllTeams := false
- if ctx.Org.IsOwner {
- shouldSeeAllTeams = true
- } else {
- teams, err := org.GetUserTeams(ctx, ctx.Doer.ID)
- if err != nil {
- ctx.ServerError("GetUserTeams", err)
- return
+ // Team.
+ if ctx.Org.IsMember {
+ shouldSeeAllTeams := false
+ if ctx.Org.IsOwner {
+ shouldSeeAllTeams = true
+ } else {
+ teams, err := org.GetUserTeams(ctx, ctx.Doer.ID)
+ if err != nil {
+ ctx.ServerError("GetUserTeams", err)
+ return
+ }
+ for _, team := range teams {
+ if team.IncludesAllRepositories && team.HasAdminAccess() {
+ shouldSeeAllTeams = true
+ break
+ }
+ }
}
- for _, team := range teams {
- if team.IncludesAllRepositories && team.AccessMode >= perm.AccessModeAdmin {
- shouldSeeAllTeams = true
- break
+ if shouldSeeAllTeams {
+ ctx.Org.Teams, err = org.LoadTeams(ctx)
+ if err != nil {
+ ctx.ServerError("LoadTeams", err)
+ return
+ }
+ } else {
+ ctx.Org.Teams, err = org.GetUserTeams(ctx, ctx.Doer.ID)
+ if err != nil {
+ ctx.ServerError("GetUserTeams", err)
+ return
}
}
+ ctx.Data["NumTeams"] = len(ctx.Org.Teams)
}
- if shouldSeeAllTeams {
- ctx.Org.Teams, err = org.LoadTeams(ctx)
- if err != nil {
- ctx.ServerError("LoadTeams", err)
- return
+
+ teamName := ctx.PathParam("team")
+ if len(teamName) > 0 {
+ teamExists := false
+ for _, team := range ctx.Org.Teams {
+ if team.LowerName == strings.ToLower(teamName) {
+ teamExists = true
+ ctx.Org.Team = team
+ ctx.Org.IsTeamMember = true
+ ctx.Data["Team"] = ctx.Org.Team
+ break
+ }
}
- } else {
- ctx.Org.Teams, err = org.GetUserTeams(ctx, ctx.Doer.ID)
- if err != nil {
- ctx.ServerError("GetUserTeams", err)
+
+ if !teamExists {
+ ctx.NotFound(err)
return
}
- }
- ctx.Data["NumTeams"] = len(ctx.Org.Teams)
- }
- teamName := ctx.PathParam("team")
- if len(teamName) > 0 {
- teamExists := false
- for _, team := range ctx.Org.Teams {
- if team.LowerName == strings.ToLower(teamName) {
- teamExists = true
- ctx.Org.Team = team
- ctx.Org.IsTeamMember = true
- ctx.Data["Team"] = ctx.Org.Team
- break
+ ctx.Data["IsTeamMember"] = ctx.Org.IsTeamMember
+ if opts.RequireTeamMember && !ctx.Org.IsTeamMember {
+ ctx.NotFound(err)
+ return
}
- }
-
- if !teamExists {
- ctx.NotFound("OrgAssignment", err)
- return
- }
- ctx.Data["IsTeamMember"] = ctx.Org.IsTeamMember
- if requireTeamMember && !ctx.Org.IsTeamMember {
- ctx.NotFound("OrgAssignment", err)
- return
+ ctx.Org.IsTeamAdmin = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.HasAdminAccess()
+ ctx.Data["IsTeamAdmin"] = ctx.Org.IsTeamAdmin
+ if opts.RequireTeamAdmin && !ctx.Org.IsTeamAdmin {
+ ctx.NotFound(err)
+ return
+ }
}
+ ctx.Data["ContextUser"] = ctx.ContextUser
- ctx.Org.IsTeamAdmin = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.AccessMode >= perm.AccessModeAdmin
- ctx.Data["IsTeamAdmin"] = ctx.Org.IsTeamAdmin
- if requireTeamAdmin && !ctx.Org.IsTeamAdmin {
- ctx.NotFound("OrgAssignment", err)
- return
- }
- }
- ctx.Data["ContextUser"] = ctx.ContextUser
+ ctx.Data["CanReadProjects"] = ctx.Org.CanReadUnit(ctx, unit.TypeProjects)
+ ctx.Data["CanReadPackages"] = ctx.Org.CanReadUnit(ctx, unit.TypePackages)
+ ctx.Data["CanReadCode"] = ctx.Org.CanReadUnit(ctx, unit.TypeCode)
- ctx.Data["CanReadProjects"] = ctx.Org.CanReadUnit(ctx, unit.TypeProjects)
- ctx.Data["CanReadPackages"] = ctx.Org.CanReadUnit(ctx, unit.TypePackages)
- ctx.Data["CanReadCode"] = ctx.Org.CanReadUnit(ctx, unit.TypeCode)
-
- ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
- if len(ctx.ContextUser.Description) != 0 {
- content, err := markdown.RenderString(markup.NewRenderContext(ctx), ctx.ContextUser.Description)
- if err != nil {
- ctx.ServerError("RenderString", err)
- return
+ ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
+ if len(ctx.ContextUser.Description) != 0 {
+ content, err := markdown.RenderString(markup.NewRenderContext(ctx), ctx.ContextUser.Description)
+ if err != nil {
+ ctx.ServerError("RenderString", err)
+ return
+ }
+ ctx.Data["RenderedDescription"] = content
}
- ctx.Data["RenderedDescription"] = content
- }
-}
-
-// OrgAssignment returns a middleware to handle organization assignment
-func OrgAssignment(args ...bool) func(ctx *Context) {
- return func(ctx *Context) {
- HandleOrgAssignment(ctx, args...)
}
}
diff --git a/services/context/package.go b/services/context/package.go
index e98e01acbb..8b722932b1 100644
--- a/services/context/package.go
+++ b/services/context/package.go
@@ -33,15 +33,15 @@ type packageAssignmentCtx struct {
// PackageAssignment returns a middleware to handle Context.Package assignment
func PackageAssignment() func(ctx *Context) {
return func(ctx *Context) {
- errorFn := func(status int, title string, obj any) {
+ errorFn := func(status int, obj any) {
err, ok := obj.(error)
if !ok {
err = fmt.Errorf("%s", obj)
}
if status == http.StatusNotFound {
- ctx.NotFound(title, err)
+ ctx.NotFound(err)
} else {
- ctx.ServerError(title, err)
+ ctx.ServerError("PackageAssignment", err)
}
}
paCtx := &packageAssignmentCtx{Base: ctx.Base, Doer: ctx.Doer, ContextUser: ctx.ContextUser}
@@ -53,18 +53,18 @@ func PackageAssignment() func(ctx *Context) {
func PackageAssignmentAPI() func(ctx *APIContext) {
return func(ctx *APIContext) {
paCtx := &packageAssignmentCtx{Base: ctx.Base, Doer: ctx.Doer, ContextUser: ctx.ContextUser}
- ctx.Package = packageAssignment(paCtx, ctx.Error)
+ ctx.Package = packageAssignment(paCtx, ctx.APIError)
}
}
-func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, any)) *Package {
+func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, any)) *Package {
pkg := &Package{
Owner: ctx.ContextUser,
}
var err error
pkg.AccessMode, err = determineAccessMode(ctx.Base, pkg, ctx.Doer)
if err != nil {
- errCb(http.StatusInternalServerError, "determineAccessMode", err)
+ errCb(http.StatusInternalServerError, fmt.Errorf("determineAccessMode: %w", err))
return pkg
}
@@ -75,16 +75,16 @@ func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, any))
pv, err := packages_model.GetVersionByNameAndVersion(ctx, pkg.Owner.ID, packages_model.Type(packageType), name, version)
if err != nil {
if err == packages_model.ErrPackageNotExist {
- errCb(http.StatusNotFound, "GetVersionByNameAndVersion", err)
+ errCb(http.StatusNotFound, fmt.Errorf("GetVersionByNameAndVersion: %w", err))
} else {
- errCb(http.StatusInternalServerError, "GetVersionByNameAndVersion", err)
+ errCb(http.StatusInternalServerError, fmt.Errorf("GetVersionByNameAndVersion: %w", err))
}
return pkg
}
pkg.Descriptor, err = packages_model.GetPackageDescriptor(ctx, pv)
if err != nil {
- errCb(http.StatusInternalServerError, "GetPackageDescriptor", err)
+ errCb(http.StatusInternalServerError, fmt.Errorf("GetPackageDescriptor: %w", err))
return pkg
}
}
@@ -93,7 +93,7 @@ func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, any))
}
func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.AccessMode, error) {
- if setting.Service.RequireSignInView && (doer == nil || doer.IsGhost()) {
+ if setting.Service.RequireSignInViewStrict && (doer == nil || doer.IsGhost()) {
return perm.AccessModeNone, nil
}
@@ -154,9 +154,9 @@ func PackageContexter() func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
base := NewBaseContext(resp, req)
- // it is still needed when rendering 500 page in a package handler
+ // FIXME: web Context is still needed when rendering 500 page in a package handler
+ // It should be refactored to use new error handling mechanisms
ctx := NewWebContext(base, renderer, nil)
- ctx.SetContextValue(WebContextKey, ctx)
next.ServeHTTP(ctx.Resp, ctx.Req)
})
}
diff --git a/services/context/pagination.go b/services/context/pagination.go
index d33dd217d0..2a9805db05 100644
--- a/services/context/pagination.go
+++ b/services/context/pagination.go
@@ -21,14 +21,20 @@ type Pagination struct {
// NewPagination creates a new instance of the Pagination struct.
// "pagingNum" is "page size" or "limit", "current" is "page"
+// total=-1 means only showing prev/next
func NewPagination(total, pagingNum, current, numPages int) *Pagination {
p := &Pagination{}
p.Paginater = paginator.New(total, pagingNum, current, numPages)
return p
}
-func (p *Pagination) AddParamFromRequest(req *http.Request) {
- for key, values := range req.URL.Query() {
+func (p *Pagination) WithCurRows(n int) *Pagination {
+ p.Paginater.SetCurRows(n)
+ return p
+}
+
+func (p *Pagination) AddParamFromQuery(q url.Values) {
+ for key, values := range q {
if key == "page" || len(values) == 0 || (len(values) == 1 && values[0] == "") {
continue
}
@@ -39,6 +45,10 @@ func (p *Pagination) AddParamFromRequest(req *http.Request) {
}
}
+func (p *Pagination) AddParamFromRequest(req *http.Request) {
+ p.AddParamFromQuery(req.URL.Query())
+}
+
// GetParams returns the configured URL params
func (p *Pagination) GetParams() template.URL {
return template.URL(strings.Join(p.urlParams, "&"))
diff --git a/services/context/permission.go b/services/context/permission.go
index 9338587257..c0a5a98724 100644
--- a/services/context/permission.go
+++ b/services/context/permission.go
@@ -5,112 +5,55 @@ package context
import (
"net/http"
+ "slices"
auth_model "code.gitea.io/gitea/models/auth"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/log"
)
// RequireRepoAdmin returns a middleware for requiring repository admin permission
func RequireRepoAdmin() func(ctx *Context) {
return func(ctx *Context) {
if !ctx.IsSigned || !ctx.Repo.IsAdmin() {
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ ctx.NotFound(nil)
return
}
}
}
-// RequireRepoWriter returns a middleware for requiring repository write to the specify unitType
-func RequireRepoWriter(unitType unit.Type) func(ctx *Context) {
- return func(ctx *Context) {
- if !ctx.Repo.CanWrite(unitType) {
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
- return
- }
- }
-}
-
-// CanEnableEditor checks if the user is allowed to write to the branch of the repo
-func CanEnableEditor() func(ctx *Context) {
+// CanWriteToBranch checks if the user is allowed to write to the branch of the repo
+func CanWriteToBranch() func(ctx *Context) {
return func(ctx *Context) {
if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) {
- ctx.NotFound("CanWriteToBranch denies permission", nil)
+ ctx.NotFound(nil)
return
}
}
}
-// RequireRepoWriterOr returns a middleware for requiring repository write to one of the unit permission
-func RequireRepoWriterOr(unitTypes ...unit.Type) func(ctx *Context) {
+// RequireUnitWriter returns a middleware for requiring repository write to one of the unit permission
+func RequireUnitWriter(unitTypes ...unit.Type) func(ctx *Context) {
return func(ctx *Context) {
- for _, unitType := range unitTypes {
- if ctx.Repo.CanWrite(unitType) {
- return
- }
- }
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
- }
-}
-
-// RequireRepoReader returns a middleware for requiring repository read to the specify unitType
-func RequireRepoReader(unitType unit.Type) func(ctx *Context) {
- return func(ctx *Context) {
- if !ctx.Repo.CanRead(unitType) {
- if unitType == unit.TypeCode && canWriteAsMaintainer(ctx) {
- return
- }
- if log.IsTrace() {
- if ctx.IsSigned {
- log.Trace("Permission Denied: User %-v cannot read %-v in Repo %-v\n"+
- "User in Repo has Permissions: %-+v",
- ctx.Doer,
- unitType,
- ctx.Repo.Repository,
- ctx.Repo.Permission)
- } else {
- log.Trace("Permission Denied: Anonymous user cannot read %-v in Repo %-v\n"+
- "Anonymous user in Repo has Permissions: %-+v",
- unitType,
- ctx.Repo.Repository,
- ctx.Repo.Permission)
- }
- }
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ if slices.ContainsFunc(unitTypes, ctx.Repo.CanWrite) {
return
}
+ ctx.NotFound(nil)
}
}
-// RequireRepoReaderOr returns a middleware for requiring repository write to one of the unit permission
-func RequireRepoReaderOr(unitTypes ...unit.Type) func(ctx *Context) {
+// RequireUnitReader returns a middleware for requiring repository write to one of the unit permission
+func RequireUnitReader(unitTypes ...unit.Type) func(ctx *Context) {
return func(ctx *Context) {
for _, unitType := range unitTypes {
if ctx.Repo.CanRead(unitType) {
return
}
- }
- if log.IsTrace() {
- var format string
- var args []any
- if ctx.IsSigned {
- format = "Permission Denied: User %-v cannot read ["
- args = append(args, ctx.Doer)
- } else {
- format = "Permission Denied: Anonymous user cannot read ["
- }
- for _, unit := range unitTypes {
- format += "%-v, "
- args = append(args, unit)
+ if unitType == unit.TypeCode && canWriteAsMaintainer(ctx) {
+ return
}
-
- format = format[:len(format)-2] + "] in Repo %-v\n" +
- "User in Repo has Permissions: %-+v"
- args = append(args, ctx.Repo.Repository, ctx.Repo.Permission)
- log.Trace(format, args...)
}
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ ctx.NotFound(nil)
}
}
@@ -134,7 +77,7 @@ func CheckRepoScopedToken(ctx *Context, repo *repo_model.Repository, level auth_
}
if publicOnly && repo.IsPrivate {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
@@ -145,7 +88,7 @@ func CheckRepoScopedToken(ctx *Context, repo *repo_model.Repository, level auth_
}
if !scopeMatched {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
diff --git a/services/context/private.go b/services/context/private.go
index 51857da8fe..d20e49f588 100644
--- a/services/context/private.go
+++ b/services/context/private.go
@@ -5,7 +5,6 @@ package context
import (
"context"
- "fmt"
"net/http"
"time"
@@ -29,7 +28,6 @@ func init() {
})
}
-// Deadline is part of the interface for context.Context and we pass this to the request context
func (ctx *PrivateContext) Deadline() (deadline time.Time, ok bool) {
if ctx.Override != nil {
return ctx.Override.Deadline()
@@ -37,7 +35,6 @@ func (ctx *PrivateContext) Deadline() (deadline time.Time, ok bool) {
return ctx.Base.Deadline()
}
-// Done is part of the interface for context.Context and we pass this to the request context
func (ctx *PrivateContext) Done() <-chan struct{} {
if ctx.Override != nil {
return ctx.Override.Done()
@@ -45,7 +42,6 @@ func (ctx *PrivateContext) Done() <-chan struct{} {
return ctx.Base.Done()
}
-// Err is part of the interface for context.Context and we pass this to the request context
func (ctx *PrivateContext) Err() error {
if ctx.Override != nil {
return ctx.Override.Err()
@@ -53,14 +49,14 @@ func (ctx *PrivateContext) Err() error {
return ctx.Base.Err()
}
-var privateContextKey any = "default_private_context"
+type privateContextKeyType struct{}
+
+var privateContextKey privateContextKeyType
-// GetPrivateContext returns a context for Private routes
func GetPrivateContext(req *http.Request) *PrivateContext {
return req.Context().Value(privateContextKey).(*PrivateContext)
}
-// PrivateContexter returns apicontext as middleware
func PrivateContexter() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
@@ -82,7 +78,7 @@ func OverrideContext() func(http.Handler) http.Handler {
// We now need to override the request context as the base for our work because even if the request is cancelled we have to continue this work
ctx := GetPrivateContext(req)
var finished func()
- ctx.Override, _, finished = process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), fmt.Sprintf("PrivateContext: %s", ctx.Req.RequestURI), process.RequestProcessType, true)
+ ctx.Override, _, finished = process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "PrivateContext: "+ctx.Req.RequestURI, process.RequestProcessType, true)
defer finished()
next.ServeHTTP(ctx.Resp, ctx.Req)
})
diff --git a/services/context/repo.go b/services/context/repo.go
index 2a473f4a54..572211712b 100644
--- a/services/context/repo.go
+++ b/services/context/repo.go
@@ -46,22 +46,21 @@ type PullRequest struct {
// Repository contains information to operate a repository
type Repository struct {
access_model.Permission
- IsWatching bool
- IsViewBranch bool
- IsViewTag bool
- IsViewCommit bool
- Repository *repo_model.Repository
- Owner *user_model.User
+
+ Repository *repo_model.Repository
+ Owner *user_model.User
+
+ RepoLink string
+ GitRepo *git.Repository
+
+ // RefFullName is the full ref name that the user is viewing
+ RefFullName git.RefName
+ BranchName string // it is the RefFullName's short name if its type is "branch"
+ TreePath string
+
+ // Commit it is always set to the commit for the branch or tag, or just the commit that the user is viewing
Commit *git.Commit
- Tag *git.Tag
- GitRepo *git.Repository
- RefName string
- BranchName string
- TagName string
- TreePath string
CommitID string
- RepoLink string
- CloneLink repo_model.CloneLink
CommitsCount int64
PullRequest *PullRequest
@@ -72,11 +71,6 @@ func (r *Repository) CanWriteToBranch(ctx context.Context, user *user_model.User
return issues_model.CanMaintainerWriteToBranch(ctx, r.Permission, branch, user)
}
-// CanEnableEditor returns true if repository is editable and user has proper access level.
-func (r *Repository) CanEnableEditor(ctx context.Context, user *user_model.User) bool {
- return r.IsViewBranch && r.CanWriteToBranch(ctx, user, r.BranchName) && r.Repository.CanEnableEditor() && !r.Repository.IsArchived
-}
-
// CanCreateBranch returns true if repository is editable and user has proper access level.
func (r *Repository) CanCreateBranch() bool {
return r.Permission.CanWrite(unit_model.TypeCode) && r.Repository.CanCreateBranch()
@@ -90,66 +84,108 @@ func (r *Repository) GetObjectFormat() git.ObjectFormat {
func RepoMustNotBeArchived() func(ctx *Context) {
return func(ctx *Context) {
if ctx.Repo.Repository.IsArchived {
- ctx.NotFound("IsArchived", errors.New(ctx.Locale.TrString("repo.archive.title")))
+ ctx.NotFound(errors.New(ctx.Locale.TrString("repo.archive.title")))
}
}
}
-// CanCommitToBranchResults represents the results of CanCommitToBranch
-type CanCommitToBranchResults struct {
- CanCommitToBranch bool
- EditorEnabled bool
- UserCanPush bool
- RequireSigned bool
- WillSign bool
- SigningKey string
- WontSignReason string
+type CommitFormOptions struct {
+ NeedFork bool
+
+ TargetRepo *repo_model.Repository
+ TargetFormAction string
+ WillSubmitToFork bool
+ CanCommitToBranch bool
+ UserCanPush bool
+ RequireSigned bool
+ WillSign bool
+ SigningKey *git.SigningKey
+ WontSignReason string
+ CanCreatePullRequest bool
+ CanCreateBasePullRequest bool
}
-// CanCommitToBranch returns true if repository is editable and user has proper access level
-//
-// and branch is not protected for push
-func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.User) (CanCommitToBranchResults, error) {
- protectedBranch, err := git_model.GetFirstMatchProtectedBranchRule(ctx, r.Repository.ID, r.BranchName)
+func PrepareCommitFormOptions(ctx *Context, doer *user_model.User, targetRepo *repo_model.Repository, doerRepoPerm access_model.Permission, refName git.RefName) (*CommitFormOptions, error) {
+ if !refName.IsBranch() {
+ // it shouldn't happen because middleware already checks
+ return nil, util.NewInvalidArgumentErrorf("ref %q is not a branch", refName)
+ }
+
+ originRepo := targetRepo
+ branchName := refName.ShortName()
+ // TODO: CanMaintainerWriteToBranch is a bad name, but it really does what "CanWriteToBranch" does
+ if !issues_model.CanMaintainerWriteToBranch(ctx, doerRepoPerm, branchName, doer) {
+ targetRepo = repo_model.GetForkedRepo(ctx, doer.ID, targetRepo.ID)
+ if targetRepo == nil {
+ return &CommitFormOptions{NeedFork: true}, nil
+ }
+ // now, we get our own forked repo; it must be writable by us.
+ }
+ submitToForkedRepo := targetRepo.ID != originRepo.ID
+ err := targetRepo.GetBaseRepo(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ protectedBranch, err := git_model.GetFirstMatchProtectedBranchRule(ctx, targetRepo.ID, branchName)
if err != nil {
- return CanCommitToBranchResults{}, err
+ return nil, err
}
- userCanPush := true
- requireSigned := false
+ canPushWithProtection := true
+ protectionRequireSigned := false
if protectedBranch != nil {
- protectedBranch.Repo = r.Repository
- userCanPush = protectedBranch.CanUserPush(ctx, doer)
- requireSigned = protectedBranch.RequireSignedCommits
+ protectedBranch.Repo = targetRepo
+ canPushWithProtection = protectedBranch.CanUserPush(ctx, doer)
+ protectionRequireSigned = protectedBranch.RequireSignedCommits
}
- sign, keyID, _, err := asymkey_service.SignCRUDAction(ctx, r.Repository.RepoPath(), doer, r.Repository.RepoPath(), git.BranchPrefix+r.BranchName)
-
- canCommit := r.CanEnableEditor(ctx, doer) && userCanPush
- if requireSigned {
- canCommit = canCommit && sign
- }
+ willSign, signKeyID, _, err := asymkey_service.SignCRUDAction(ctx, targetRepo.RepoPath(), doer, targetRepo.RepoPath(), refName.String())
wontSignReason := ""
- if err != nil {
- if asymkey_service.IsErrWontSign(err) {
- wontSignReason = string(err.(*asymkey_service.ErrWontSign).Reason)
- err = nil
- } else {
- wontSignReason = "error"
- }
+ if asymkey_service.IsErrWontSign(err) {
+ wontSignReason = string(err.(*asymkey_service.ErrWontSign).Reason)
+ } else if err != nil {
+ return nil, err
+ }
+
+ canCommitToBranch := !submitToForkedRepo /* same repo */ && targetRepo.CanEnableEditor() && canPushWithProtection
+ if protectionRequireSigned {
+ canCommitToBranch = canCommitToBranch && willSign
}
- return CanCommitToBranchResults{
- CanCommitToBranch: canCommit,
- EditorEnabled: r.CanEnableEditor(ctx, doer),
- UserCanPush: userCanPush,
- RequireSigned: requireSigned,
- WillSign: sign,
- SigningKey: keyID,
+ canCreateBasePullRequest := targetRepo.BaseRepo != nil && targetRepo.BaseRepo.UnitEnabled(ctx, unit_model.TypePullRequests)
+ canCreatePullRequest := targetRepo.UnitEnabled(ctx, unit_model.TypePullRequests) || canCreateBasePullRequest
+
+ opts := &CommitFormOptions{
+ TargetRepo: targetRepo,
+ WillSubmitToFork: submitToForkedRepo,
+ CanCommitToBranch: canCommitToBranch,
+ UserCanPush: canPushWithProtection,
+ RequireSigned: protectionRequireSigned,
+ WillSign: willSign,
+ SigningKey: signKeyID,
WontSignReason: wontSignReason,
- }, err
+
+ CanCreatePullRequest: canCreatePullRequest,
+ CanCreateBasePullRequest: canCreateBasePullRequest,
+ }
+ editorAction := ctx.PathParam("editor_action")
+ editorPathParamRemaining := util.PathEscapeSegments(branchName) + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
+ if submitToForkedRepo {
+ // there is only "default branch" in forked repo, we will use "from_base_branch" to get a new branch from base repo
+ editorPathParamRemaining = util.PathEscapeSegments(targetRepo.DefaultBranch) + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) + "?from_base_branch=" + url.QueryEscape(branchName)
+ }
+ if editorAction == "_cherrypick" {
+ opts.TargetFormAction = targetRepo.Link() + "/" + editorAction + "/" + ctx.PathParam("sha") + "/" + editorPathParamRemaining
+ } else {
+ opts.TargetFormAction = targetRepo.Link() + "/" + editorAction + "/" + editorPathParamRemaining
+ }
+ if ctx.Req.URL.RawQuery != "" {
+ opts.TargetFormAction += util.Iif(strings.Contains(opts.TargetFormAction, "?"), "&", "?") + ctx.Req.URL.RawQuery
+ }
+ return opts, nil
}
-// CanUseTimetracker returns whether or not a user can use the timetracker.
+// CanUseTimetracker returns whether a user can use the timetracker.
func (r *Repository) CanUseTimetracker(ctx context.Context, issue *issues_model.Issue, user *user_model.User) bool {
// Checking for following:
// 1. Is timetracker enabled
@@ -169,15 +205,9 @@ func (r *Repository) GetCommitsCount() (int64, error) {
if r.Commit == nil {
return 0, nil
}
- var contextName string
- if r.IsViewBranch {
- contextName = r.BranchName
- } else if r.IsViewTag {
- contextName = r.TagName
- } else {
- contextName = r.CommitID
- }
- return cache.GetInt64(r.Repository.GetCommitsCountCacheKey(contextName, r.IsViewBranch || r.IsViewTag), func() (int64, error) {
+ contextName := r.RefFullName.ShortName()
+ isRef := r.RefFullName.IsBranch() || r.RefFullName.IsTag()
+ return cache.GetInt64(r.Repository.GetCommitsCountCacheKey(contextName, isRef), func() (int64, error) {
return r.Commit.CommitsCount()
})
}
@@ -199,33 +229,13 @@ func (r *Repository) GetCommitGraphsCount(ctx context.Context, hidePRRefs bool,
})
}
-// BranchNameSubURL sub-URL for the BranchName field
-func (r *Repository) BranchNameSubURL() string {
- switch {
- case r.IsViewBranch:
- return "branch/" + util.PathEscapeSegments(r.BranchName)
- case r.IsViewTag:
- return "tag/" + util.PathEscapeSegments(r.TagName)
- case r.IsViewCommit:
- return "commit/" + util.PathEscapeSegments(r.CommitID)
- }
- log.Error("Unknown view type for repo: %v", r)
- return ""
-}
-
-// FileExists returns true if a file exists in the given repo branch
-func (r *Repository) FileExists(path, branch string) (bool, error) {
- if branch == "" {
- branch = r.Repository.DefaultBranch
- }
- commit, err := r.GitRepo.GetBranchCommit(branch)
- if err != nil {
- return false, err
- }
- if _, err := commit.GetTreeEntryByPath(path); err != nil {
- return false, err
- }
- return true, nil
+// RefTypeNameSubURL makes a sub-url for the current ref (branch/tag/commit) field, for example:
+// * "branch/master"
+// * "tag/v1.0.0"
+// * "commit/123456"
+// It is usually used to construct a link like ".../src/{{RefTypeNameSubURL}}/{{PathEscapeSegments TreePath}}"
+func (r *Repository) RefTypeNameSubURL() string {
+ return r.RefFullName.RefWebLinkPath()
}
// GetEditorconfig returns the .editorconfig definition if found in the
@@ -325,9 +335,9 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
var cloneURL string
if setting.Repository.GoGetCloneURLProtocol == "ssh" {
- cloneURL = repo_model.ComposeSSHCloneURL(username, reponame)
+ cloneURL = repo_model.ComposeSSHCloneURL(ctx.Doer, username, reponame)
} else {
- cloneURL = repo_model.ComposeHTTPSCloneURL(username, reponame)
+ cloneURL = repo_model.ComposeHTTPSCloneURL(ctx, username, reponame)
}
goImportContent := fmt.Sprintf("%s git %s", ComposeGoGetImport(ctx, username, reponame), cloneURL)
htmlMeta := fmt.Sprintf(`<meta name="go-import" content="%s">`, html.EscapeString(goImportContent))
@@ -342,7 +352,7 @@ func RedirectToRepo(ctx *Base, redirectRepoID int64) {
repo, err := repo_model.GetRepositoryByID(ctx, redirectRepoID)
if err != nil {
log.Error("GetRepositoryByID: %v", err)
- ctx.Error(http.StatusInternalServerError, "GetRepositoryByID")
+ ctx.HTTPError(http.StatusInternalServerError, "GetRepositoryByID")
return
}
@@ -355,7 +365,9 @@ func RedirectToRepo(ctx *Base, redirectRepoID int64) {
if ctx.Req.URL.RawQuery != "" {
redirectPath += "?" + ctx.Req.URL.RawQuery
}
- ctx.Redirect(path.Join(setting.AppSubURL, redirectPath), http.StatusTemporaryRedirect)
+ // Git client needs a 301 redirect by default to follow the new location
+ // It's not documentated in git documentation, but it's the behavior of git client
+ ctx.Redirect(path.Join(setting.AppSubURL, redirectPath), http.StatusMovedPermanently)
}
func repoAssignment(ctx *Context, repo *repo_model.Repository) {
@@ -365,18 +377,22 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
return
}
- ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
- if err != nil {
- ctx.ServerError("GetUserRepoPermission", err)
- return
+ if ctx.DoerNeedTwoFactorAuth() {
+ ctx.Repo.Permission = access_model.PermissionNoAccess()
+ } else {
+ ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
+ if err != nil {
+ ctx.ServerError("GetUserRepoPermission", err)
+ return
+ }
}
- if !ctx.Repo.Permission.HasAnyUnitAccessOrEveryoneAccess() && !canWriteAsMaintainer(ctx) {
+ if !ctx.Repo.Permission.HasAnyUnitAccessOrPublicAccess() && !canWriteAsMaintainer(ctx) {
if ctx.FormString("go-get") == "1" {
EarlyResponseForGoGetMeta(ctx)
return
}
- ctx.NotFound("no access right", nil)
+ ctx.NotFound(nil)
return
}
ctx.Data["Permission"] = &ctx.Repo.Permission
@@ -398,33 +414,25 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
// RepoAssignment returns a middleware to handle repository assignment
func RepoAssignment(ctx *Context) {
- if _, repoAssignmentOnce := ctx.Data["repoAssignmentExecuted"]; repoAssignmentOnce {
- // FIXME: it should panic in dev/test modes to have a clear behavior
- if !setting.IsProd || setting.IsInTesting {
- panic("RepoAssignment should not be executed twice")
- }
- return
+ if ctx.Data["Repository"] != nil {
+ setting.PanicInDevOrTesting("RepoAssignment should not be executed twice")
}
- ctx.Data["repoAssignmentExecuted"] = true
-
- var (
- owner *user_model.User
- err error
- )
+ var err error
userName := ctx.PathParam("username")
repoName := ctx.PathParam("reponame")
repoName = strings.TrimSuffix(repoName, ".git")
if setting.Other.EnableFeed {
+ ctx.Data["EnableFeed"] = true
repoName = strings.TrimSuffix(repoName, ".rss")
repoName = strings.TrimSuffix(repoName, ".atom")
}
// Check if the user is the same as the repository owner
if ctx.IsSigned && ctx.Doer.LowerName == strings.ToLower(userName) {
- owner = ctx.Doer
+ ctx.Repo.Owner = ctx.Doer
} else {
- owner, err = user_model.GetUserByName(ctx, userName)
+ ctx.Repo.Owner, err = user_model.GetUserByName(ctx, userName)
if err != nil {
if user_model.IsErrUserNotExist(err) {
// go-get does not support redirects
@@ -437,7 +445,7 @@ func RepoAssignment(ctx *Context) {
if redirectUserID, err := user_model.LookupUserRedirect(ctx, userName); err == nil {
RedirectToUser(ctx.Base, userName, redirectUserID)
} else if user_model.IsErrUserRedirectNotExist(err) {
- ctx.NotFound("GetUserByName", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("LookupUserRedirect", err)
}
@@ -447,10 +455,8 @@ func RepoAssignment(ctx *Context) {
return
}
}
- ctx.Repo.Owner = owner
- ctx.ContextUser = owner
+ ctx.ContextUser = ctx.Repo.Owner
ctx.Data["ContextUser"] = ctx.ContextUser
- ctx.Data["Username"] = ctx.Repo.Owner.Name
// redirect link to wiki
if strings.HasSuffix(repoName, ".wiki") {
@@ -473,10 +479,10 @@ func RepoAssignment(ctx *Context) {
}
// Get repository.
- repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName)
+ repo, err := repo_model.GetRepositoryByName(ctx, ctx.Repo.Owner.ID, repoName)
if err != nil {
if repo_model.IsErrRepoNotExist(err) {
- redirectRepoID, err := repo_model.LookupRedirect(ctx, owner.ID, repoName)
+ redirectRepoID, err := repo_model.LookupRedirect(ctx, ctx.Repo.Owner.ID, repoName)
if err == nil {
RedirectToRepo(ctx.Base, redirectRepoID)
} else if repo_model.IsErrRedirectNotExist(err) {
@@ -484,7 +490,7 @@ func RepoAssignment(ctx *Context) {
EarlyResponseForGoGetMeta(ctx)
return
}
- ctx.NotFound("GetRepositoryByName", nil)
+ ctx.NotFound(nil)
} else {
ctx.ServerError("LookupRepoRedirect", err)
}
@@ -493,7 +499,7 @@ func RepoAssignment(ctx *Context) {
}
return
}
- repo.Owner = owner
+ repo.Owner = ctx.Repo.Owner
repoAssignment(ctx, repo)
if ctx.Written() {
@@ -502,12 +508,7 @@ func RepoAssignment(ctx *Context) {
ctx.Repo.RepoLink = repo.Link()
ctx.Data["RepoLink"] = ctx.Repo.RepoLink
- ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
-
- if setting.Other.EnableFeed {
- ctx.Data["EnableFeed"] = true
- ctx.Data["FeedURL"] = ctx.Repo.RepoLink
- }
+ ctx.Data["FeedURL"] = ctx.Repo.RepoLink
unit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeExternalTracker)
if err == nil {
@@ -534,12 +535,9 @@ func RepoAssignment(ctx *Context) {
return
}
- ctx.Data["Title"] = owner.Name + "/" + repo.Name
+ ctx.Data["Title"] = repo.Owner.Name + "/" + repo.Name
ctx.Data["Repository"] = repo
ctx.Data["Owner"] = ctx.Repo.Repository.Owner
- ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner()
- ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin()
- ctx.Data["RepoOwnerIsOrganization"] = repo.Owner.IsOrganization()
ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(unit_model.TypeCode)
ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(unit_model.TypeIssues)
ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(unit_model.TypePullRequests)
@@ -564,7 +562,7 @@ func RepoAssignment(ctx *Context) {
// If multiple forks are available or if the user can fork to another account, but there is already a fork: open selection dialog
ctx.Data["ShowForkModal"] = len(userAndOrgForks) > 1 || (canSignedUserFork && len(userAndOrgForks) > 0)
- ctx.Data["RepoCloneLink"] = repo.CloneLink()
+ ctx.Data["RepoCloneLink"] = repo.CloneLink(ctx, ctx.Doer)
cloneButtonShowHTTPS := !setting.Repository.DisableHTTPGit
cloneButtonShowSSH := !setting.SSH.Disabled && (ctx.IsSigned || setting.SSH.ExposeAnonymous)
@@ -607,7 +605,6 @@ func RepoAssignment(ctx *Context) {
// Disable everything when the repo is being created
if ctx.Repo.Repository.IsBeingCreated() || ctx.Repo.Repository.IsBroken() {
- ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
if !isHomeOrSettings {
ctx.Redirect(ctx.Repo.RepoLink)
}
@@ -615,9 +612,7 @@ func RepoAssignment(ctx *Context) {
}
if ctx.Repo.GitRepo != nil {
- if !setting.IsProd || setting.IsInTesting {
- panic("RepoAssignment: GitRepo should be nil")
- }
+ setting.PanicInDevOrTesting("RepoAssignment: GitRepo should be nil")
_ = ctx.Repo.GitRepo.Close()
ctx.Repo.GitRepo = nil
}
@@ -627,7 +622,6 @@ func RepoAssignment(ctx *Context) {
if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") {
log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err)
ctx.Repo.Repository.MarkAsBrokenEmpty()
- ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
// Only allow access to base of repo or settings
if !isHomeOrSettings {
ctx.Redirect(ctx.Repo.RepoLink)
@@ -640,7 +634,6 @@ func RepoAssignment(ctx *Context) {
// Stop at this point when the repo is empty.
if ctx.Repo.Repository.IsEmpty {
- ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
return
}
@@ -666,22 +659,6 @@ func RepoAssignment(ctx *Context) {
ctx.Data["BranchesCount"] = branchesTotal
- // If no branch is set in the request URL, try to guess a default one.
- if len(ctx.Repo.BranchName) == 0 {
- if len(ctx.Repo.Repository.DefaultBranch) > 0 && ctx.Repo.GitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
- ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
- } else {
- ctx.Repo.BranchName, _ = gitrepo.GetDefaultBranch(ctx, ctx.Repo.Repository)
- if ctx.Repo.BranchName == "" {
- // If it still can't get a default branch, fall back to default branch from setting.
- // Something might be wrong. Either site admin should fix the repo sync or Gitea should fix a potential bug.
- ctx.Repo.BranchName = setting.Repository.DefaultBranch
- }
- }
- ctx.Repo.RefName = ctx.Repo.BranchName
- }
- ctx.Data["BranchName"] = ctx.Repo.BranchName
-
// People who have push access or have forked repository can propose a new pull request.
canPush := ctx.Repo.CanWrite(unit_model.TypeCode) ||
(ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID))
@@ -719,39 +696,20 @@ func RepoAssignment(ctx *Context) {
ctx.Data["RepoTransfer"] = repoTransfer
if ctx.Doer != nil {
- ctx.Data["CanUserAcceptTransfer"] = repoTransfer.CanUserAcceptTransfer(ctx, ctx.Doer)
+ ctx.Data["CanUserAcceptOrRejectTransfer"] = repoTransfer.CanUserAcceptOrRejectTransfer(ctx, ctx.Doer)
}
}
if ctx.FormString("go-get") == "1" {
- ctx.Data["GoGetImport"] = ComposeGoGetImport(ctx, owner.Name, repo.Name)
+ ctx.Data["GoGetImport"] = ComposeGoGetImport(ctx, repo.Owner.Name, repo.Name)
fullURLPrefix := repo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName)
ctx.Data["GoDocDirectory"] = fullURLPrefix + "{/dir}"
ctx.Data["GoDocFile"] = fullURLPrefix + "{/dir}/{file}#L{line}"
}
}
-// RepoRefType type of repo reference
-type RepoRefType int
-
-const (
- // RepoRefUnknown is for legacy support, makes the code to "guess" the ref type
- RepoRefUnknown RepoRefType = iota
- RepoRefBranch
- RepoRefTag
- RepoRefCommit
- RepoRefBlob
-)
-
const headRefName = "HEAD"
-// RepoRef handles repository reference names when the ref name is not
-// explicitly given
-func RepoRef() func(*Context) {
- // since no ref name is explicitly specified, ok to just use branch
- return RepoRefByType(RepoRefBranch)
-}
-
func getRefNameFromPath(repo *Repository, path string, isExist func(string) bool) string {
refName := ""
parts := strings.Split(path, "/")
@@ -765,37 +723,29 @@ func getRefNameFromPath(repo *Repository, path string, isExist func(string) bool
return ""
}
-func getRefNameLegacy(ctx *Base, repo *Repository, optionalExtraRef ...string) (string, RepoRefType) {
- extraRef := util.OptionalArg(optionalExtraRef)
- reqPath := ctx.PathParam("*")
- reqPath = path.Join(extraRef, reqPath)
-
- if refName := getRefName(ctx, repo, RepoRefBranch); refName != "" {
- return refName, RepoRefBranch
+func getRefNameLegacy(ctx *Base, repo *Repository, reqPath, extraRef string) (refName string, refType git.RefType, fallbackDefaultBranch bool) {
+ reqRefPath := path.Join(extraRef, reqPath)
+ reqRefPathParts := strings.Split(reqRefPath, "/")
+ if refName := getRefName(ctx, repo, reqRefPath, git.RefTypeBranch); refName != "" {
+ return refName, git.RefTypeBranch, false
}
- if refName := getRefName(ctx, repo, RepoRefTag); refName != "" {
- return refName, RepoRefTag
+ if refName := getRefName(ctx, repo, reqRefPath, git.RefTypeTag); refName != "" {
+ return refName, git.RefTypeTag, false
}
-
- // For legacy support only full commit sha
- parts := strings.Split(reqPath, "/")
- if git.IsStringLikelyCommitID(git.ObjectFormatFromName(repo.Repository.ObjectFormatName), parts[0]) {
+ if git.IsStringLikelyCommitID(git.ObjectFormatFromName(repo.Repository.ObjectFormatName), reqRefPathParts[0]) {
// FIXME: this logic is different from other types. Ideally, it should also try to GetCommit to check if it exists
- repo.TreePath = strings.Join(parts[1:], "/")
- return parts[0], RepoRefCommit
- }
-
- if refName := getRefName(ctx, repo, RepoRefBlob); len(refName) > 0 {
- return refName, RepoRefBlob
+ repo.TreePath = strings.Join(reqRefPathParts[1:], "/")
+ return reqRefPathParts[0], git.RefTypeCommit, false
}
+ // FIXME: the old code falls back to default branch if "ref" doesn't exist, there could be an edge case:
+ // "README?ref=no-such" would read the README file from the default branch, but the user might expect a 404
repo.TreePath = reqPath
- return repo.Repository.DefaultBranch, RepoRefBranch
+ return repo.Repository.DefaultBranch, git.RefTypeBranch, true
}
-func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
- path := ctx.PathParam("*")
- switch pathType {
- case RepoRefBranch:
+func getRefName(ctx *Base, repo *Repository, path string, refType git.RefType) string {
+ switch refType {
+ case git.RefTypeBranch:
ref := getRefNameFromPath(repo, path, repo.GitRepo.IsBranchExist)
if len(ref) == 0 {
// check if ref is HEAD
@@ -825,9 +775,9 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
}
return ref
- case RepoRefTag:
+ case git.RefTypeTag:
return getRefNameFromPath(repo, path, repo.GitRepo.IsTagExist)
- case RepoRefCommit:
+ case git.RefTypeCommit:
parts := strings.Split(path, "/")
if git.IsStringLikelyCommitID(repo.GetObjectFormat(), parts[0], 7) {
// FIXME: this logic is different from other types. Ideally, it should also try to GetCommit to check if it exists
@@ -844,164 +794,178 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
repo.TreePath = strings.Join(parts[1:], "/")
return commit.ID.String()
}
- case RepoRefBlob:
- _, err := repo.GitRepo.GetBlob(path)
- if err != nil {
- return ""
- }
- return path
default:
- panic(fmt.Sprintf("Unrecognized path type: %v", pathType))
+ panic(fmt.Sprintf("Unrecognized ref type: %v", refType))
}
return ""
}
-type RepoRefByTypeOptions struct {
- IgnoreNotExistErr bool
+func repoRefFullName(typ git.RefType, shortName string) git.RefName {
+ switch typ {
+ case git.RefTypeBranch:
+ return git.RefNameFromBranch(shortName)
+ case git.RefTypeTag:
+ return git.RefNameFromTag(shortName)
+ case git.RefTypeCommit:
+ return git.RefNameFromCommit(shortName)
+ default:
+ setting.PanicInDevOrTesting("Unknown RepoRefType: %v", typ)
+ return git.RefNameFromBranch("main") // just a dummy result, it shouldn't happen
+ }
+}
+
+func RepoRefByDefaultBranch() func(*Context) {
+ return func(ctx *Context) {
+ ctx.Repo.RefFullName = git.RefNameFromBranch(ctx.Repo.Repository.DefaultBranch)
+ ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
+ ctx.Repo.Commit, _ = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.BranchName)
+ ctx.Repo.CommitsCount, _ = ctx.Repo.GetCommitsCount()
+ ctx.Data["RefFullName"] = ctx.Repo.RefFullName
+ ctx.Data["BranchName"] = ctx.Repo.BranchName
+ ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
+ }
}
// RepoRefByType handles repository reference name for a specific type
// of repository reference
-func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func(*Context) {
- opt := util.OptionalArg(opts)
+func RepoRefByType(detectRefType git.RefType) func(*Context) {
return func(ctx *Context) {
+ var err error
refType := detectRefType
+ if ctx.Repo.Repository.IsBeingCreated() || ctx.Repo.Repository.IsBroken() {
+ return // no git repo, so do nothing, users will see a "migrating" UI provided by "migrate/migrating.tmpl", or empty repo guide
+ }
// Empty repository does not have reference information.
if ctx.Repo.Repository.IsEmpty {
// assume the user is viewing the (non-existent) default branch
- ctx.Repo.IsViewBranch = true
ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
+ ctx.Repo.RefFullName = git.RefNameFromBranch(ctx.Repo.BranchName)
+ // these variables are used by the template to "add/upload" new files
+ ctx.Data["BranchName"] = ctx.Repo.BranchName
ctx.Data["TreePath"] = ""
return
}
- var (
- refName string
- err error
- )
-
- if ctx.Repo.GitRepo == nil {
- ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository)
- if err != nil {
- ctx.ServerError(fmt.Sprintf("Open Repository %v failed", ctx.Repo.Repository.FullName()), err)
- return
- }
- }
-
// Get default branch.
- if len(ctx.PathParam("*")) == 0 {
- refName = ctx.Repo.Repository.DefaultBranch
- if !ctx.Repo.GitRepo.IsBranchExist(refName) {
- brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 1)
+ var refShortName string
+ reqPath := ctx.PathParam("*")
+ if reqPath == "" {
+ refShortName = ctx.Repo.Repository.DefaultBranch
+ if !gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, refShortName) {
+ brs, _, err := ctx.Repo.GitRepo.GetBranchNames(0, 1)
if err == nil && len(brs) != 0 {
- refName = brs[0].Name
+ refShortName = brs[0]
} else if len(brs) == 0 {
log.Error("No branches in non-empty repository %s", ctx.Repo.GitRepo.Path)
- ctx.Repo.Repository.MarkAsBrokenEmpty()
} else {
log.Error("GetBranches error: %v", err)
- ctx.Repo.Repository.MarkAsBrokenEmpty()
}
}
- ctx.Repo.RefName = refName
- ctx.Repo.BranchName = refName
- ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
+ ctx.Repo.RefFullName = git.RefNameFromBranch(refShortName)
+ ctx.Repo.BranchName = refShortName
+ ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refShortName)
if err == nil {
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
} else if strings.Contains(err.Error(), "fatal: not a git repository") || strings.Contains(err.Error(), "object does not exist") {
// if the repository is broken, we can continue to the handler code, to show "Settings -> Delete Repository" for end users
log.Error("GetBranchCommit: %v", err)
- ctx.Repo.Repository.MarkAsBrokenEmpty()
} else {
ctx.ServerError("GetBranchCommit", err)
return
}
- ctx.Repo.IsViewBranch = true
- } else {
- guessLegacyPath := refType == RepoRefUnknown
+ } else { // there is a path in request
+ guessLegacyPath := refType == ""
+ fallbackDefaultBranch := false
if guessLegacyPath {
- refName, refType = getRefNameLegacy(ctx.Base, ctx.Repo)
+ refShortName, refType, fallbackDefaultBranch = getRefNameLegacy(ctx.Base, ctx.Repo, reqPath, "")
} else {
- refName = getRefName(ctx.Base, ctx.Repo, refType)
+ refShortName = getRefName(ctx.Base, ctx.Repo, reqPath, refType)
}
- ctx.Repo.RefName = refName
+ ctx.Repo.RefFullName = repoRefFullName(refType, refShortName)
isRenamedBranch, has := ctx.Data["IsRenamedBranch"].(bool)
if isRenamedBranch && has {
renamedBranchName := ctx.Data["RenamedBranchName"].(string)
- ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, renamedBranchName))
- link := setting.AppSubURL + strings.Replace(ctx.Req.URL.EscapedPath(), util.PathEscapeSegments(refName), util.PathEscapeSegments(renamedBranchName), 1)
+ ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refShortName, renamedBranchName))
+ link := setting.AppSubURL + strings.Replace(ctx.Req.URL.EscapedPath(), util.PathEscapeSegments(refShortName), util.PathEscapeSegments(renamedBranchName), 1)
ctx.Redirect(link)
return
}
- if refType == RepoRefBranch && ctx.Repo.GitRepo.IsBranchExist(refName) {
- ctx.Repo.IsViewBranch = true
- ctx.Repo.BranchName = refName
+ if refType == git.RefTypeBranch && gitrepo.IsBranchExist(ctx, ctx.Repo.Repository, refShortName) {
+ ctx.Repo.BranchName = refShortName
+ ctx.Repo.RefFullName = git.RefNameFromBranch(refShortName)
- ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
+ ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refShortName)
if err != nil {
ctx.ServerError("GetBranchCommit", err)
return
}
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
- } else if refType == RepoRefTag && ctx.Repo.GitRepo.IsTagExist(refName) {
- ctx.Repo.IsViewTag = true
- ctx.Repo.TagName = refName
+ } else if refType == git.RefTypeTag && gitrepo.IsTagExist(ctx, ctx.Repo.Repository, refShortName) {
+ ctx.Repo.RefFullName = git.RefNameFromTag(refShortName)
- ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
+ ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refShortName)
if err != nil {
if git.IsErrNotExist(err) {
- ctx.NotFound("GetTagCommit", err)
+ ctx.NotFound(err)
return
}
ctx.ServerError("GetTagCommit", err)
return
}
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
- } else if git.IsStringLikelyCommitID(ctx.Repo.GetObjectFormat(), refName, 7) {
- ctx.Repo.IsViewCommit = true
- ctx.Repo.CommitID = refName
+ } else if git.IsStringLikelyCommitID(ctx.Repo.GetObjectFormat(), refShortName, 7) {
+ ctx.Repo.RefFullName = git.RefNameFromCommit(refShortName)
+ ctx.Repo.CommitID = refShortName
- ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
+ ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refShortName)
if err != nil {
- ctx.NotFound("GetCommit", err)
+ ctx.NotFound(err)
return
}
// If short commit ID add canonical link header
- if len(refName) < ctx.Repo.GetObjectFormat().FullLength() {
- canonicalURL := util.URLJoin(httplib.GuessCurrentAppURL(ctx), strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))
+ if len(refShortName) < ctx.Repo.GetObjectFormat().FullLength() {
+ canonicalURL := util.URLJoin(httplib.GuessCurrentAppURL(ctx), strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refShortName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))
ctx.RespHeader().Set("Link", fmt.Sprintf(`<%s>; rel="canonical"`, canonicalURL))
}
} else {
- if opt.IgnoreNotExistErr {
- return
- }
- ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName))
+ ctx.NotFound(fmt.Errorf("branch or tag not exist: %s", refShortName))
return
}
if guessLegacyPath {
// redirect from old URL scheme to new URL scheme
- prefix := strings.TrimPrefix(setting.AppSubURL+strings.ToLower(strings.TrimSuffix(ctx.Req.URL.Path, ctx.PathParam("*"))), strings.ToLower(ctx.Repo.RepoLink))
- redirect := path.Join(
- ctx.Repo.RepoLink,
- util.PathEscapeSegments(prefix),
- ctx.Repo.BranchNameSubURL(),
- util.PathEscapeSegments(ctx.Repo.TreePath))
+ // * /user2/repo1/commits/master => /user2/repo1/commits/branch/master
+ // * /user2/repo1/src/master => /user2/repo1/src/branch/master
+ // * /user2/repo1/src/README.md => /user2/repo1/src/branch/master/README.md (fallback to default branch)
+ var redirect string
+ refSubPath := "src"
+ // remove the "/subpath/owner/repo/" prefix, the names are case-insensitive
+ remainingLowerPath, cut := strings.CutPrefix(setting.AppSubURL+strings.ToLower(ctx.Req.URL.Path), strings.ToLower(ctx.Repo.RepoLink)+"/")
+ if cut {
+ refSubPath, _, _ = strings.Cut(remainingLowerPath, "/") // it could be "src" or "commits"
+ }
+ if fallbackDefaultBranch {
+ redirect = fmt.Sprintf("%s/%s/%s/%s/%s", ctx.Repo.RepoLink, refSubPath, refType, util.PathEscapeSegments(refShortName), ctx.PathParamRaw("*"))
+ } else {
+ redirect = fmt.Sprintf("%s/%s/%s/%s", ctx.Repo.RepoLink, refSubPath, refType, ctx.PathParamRaw("*"))
+ }
+ if ctx.Req.URL.RawQuery != "" {
+ redirect += "?" + ctx.Req.URL.RawQuery
+ }
ctx.Redirect(redirect)
return
}
}
+ ctx.Data["RefFullName"] = ctx.Repo.RefFullName
+ ctx.Data["RefTypeNameSubURL"] = ctx.Repo.RefTypeNameSubURL()
+ ctx.Data["TreePath"] = ctx.Repo.TreePath
+
ctx.Data["BranchName"] = ctx.Repo.BranchName
- ctx.Data["RefName"] = ctx.Repo.RefName
- ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
- ctx.Data["TagName"] = ctx.Repo.TagName
+
ctx.Data["CommitID"] = ctx.Repo.CommitID
- ctx.Data["TreePath"] = ctx.Repo.TreePath
- ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch
- ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag
- ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit
+
ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() // only used by the branch selector dropdown: AllowCreateNewRef
ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()
@@ -1009,6 +973,15 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
ctx.ServerError("GetCommitsCount", err)
return
}
+ if ctx.Repo.RefFullName.IsTag() {
+ rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, ctx.Repo.RefFullName.TagName())
+ if err == nil && rel.NumCommits <= 0 {
+ rel.NumCommits = ctx.Repo.CommitsCount
+ if err := repo_model.UpdateReleaseNumCommits(ctx, rel); err != nil {
+ log.Error("UpdateReleaseNumCommits", err)
+ }
+ }
+ }
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
ctx.Repo.GitRepo.LastCommitCache = git.NewLastCommitCache(ctx.Repo.CommitsCount, ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, cache.GetCache())
}
@@ -1018,7 +991,7 @@ func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func
func GitHookService() func(ctx *Context) {
return func(ctx *Context) {
if !ctx.Doer.CanEditGitHook() {
- ctx.NotFound("GitHookService", nil)
+ ctx.NotFound(nil)
return
}
}
diff --git a/services/context/response.go b/services/context/response.go
index 2f271f211b..c7368ebc6f 100644
--- a/services/context/response.go
+++ b/services/context/response.go
@@ -11,31 +11,29 @@ import (
// ResponseWriter represents a response writer for HTTP
type ResponseWriter interface {
- http.ResponseWriter
- http.Flusher
- web_types.ResponseStatusProvider
-
- Before(func(ResponseWriter))
+ http.ResponseWriter // provides Header/Write/WriteHeader
+ http.Flusher // provides Flush
+ web_types.ResponseStatusProvider // provides WrittenStatus
- Status() int // used by access logger template
- Size() int // used by access logger template
+ Before(fn func(ResponseWriter))
+ WrittenSize() int
}
-var _ ResponseWriter = &Response{}
+var _ ResponseWriter = (*Response)(nil)
// Response represents a response
type Response struct {
http.ResponseWriter
written int
status int
- befores []func(ResponseWriter)
+ beforeFuncs []func(ResponseWriter)
beforeExecuted bool
}
// Write writes bytes to HTTP endpoint
func (r *Response) Write(bs []byte) (int, error) {
if !r.beforeExecuted {
- for _, before := range r.befores {
+ for _, before := range r.beforeFuncs {
before(r)
}
r.beforeExecuted = true
@@ -51,18 +49,14 @@ func (r *Response) Write(bs []byte) (int, error) {
return size, nil
}
-func (r *Response) Status() int {
- return r.status
-}
-
-func (r *Response) Size() int {
+func (r *Response) WrittenSize() int {
return r.written
}
// WriteHeader write status code
func (r *Response) WriteHeader(statusCode int) {
if !r.beforeExecuted {
- for _, before := range r.befores {
+ for _, before := range r.beforeFuncs {
before(r)
}
r.beforeExecuted = true
@@ -87,17 +81,13 @@ func (r *Response) WrittenStatus() int {
// Before allows for a function to be called before the ResponseWriter has been written to. This is
// useful for setting headers or any other operations that must happen before a response has been written.
-func (r *Response) Before(f func(ResponseWriter)) {
- r.befores = append(r.befores, f)
+func (r *Response) Before(fn func(ResponseWriter)) {
+ r.beforeFuncs = append(r.beforeFuncs, fn)
}
func WrapResponseWriter(resp http.ResponseWriter) *Response {
if v, ok := resp.(*Response); ok {
return v
}
- return &Response{
- ResponseWriter: resp,
- status: 0,
- befores: make([]func(ResponseWriter), 0),
- }
+ return &Response{ResponseWriter: resp}
}
diff --git a/services/context/upload/upload.go b/services/context/upload/upload.go
index da4370a433..23707950d4 100644
--- a/services/context/upload/upload.go
+++ b/services/context/upload/upload.go
@@ -11,7 +11,9 @@ import (
"regexp"
"strings"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/context"
)
@@ -39,7 +41,7 @@ func Verify(buf []byte, fileName, allowedTypesStr string) error {
allowedTypesStr = strings.ReplaceAll(allowedTypesStr, "|", ",") // compat for old config format
allowedTypes := []string{}
- for _, entry := range strings.Split(allowedTypesStr, ",") {
+ for entry := range strings.SplitSeq(allowedTypesStr, ",") {
entry = strings.ToLower(strings.TrimSpace(entry))
if entry != "" {
allowedTypes = append(allowedTypes, entry)
@@ -87,14 +89,15 @@ func Verify(buf []byte, fileName, allowedTypesStr string) error {
// AddUploadContext renders template values for dropzone
func AddUploadContext(ctx *context.Context, uploadType string) {
- if uploadType == "release" {
+ switch uploadType {
+ case "release":
ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/releases/attachments"
ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/releases/attachments/remove"
ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/releases/attachments"
ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Repository.Release.AllowedTypes, "|", ",")
ctx.Data["UploadMaxFiles"] = setting.Attachment.MaxFiles
ctx.Data["UploadMaxSize"] = setting.Attachment.MaxSize
- } else if uploadType == "comment" {
+ case "comment":
ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/issues/attachments"
ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/issues/attachments/remove"
if len(ctx.PathParam("index")) > 0 {
@@ -105,12 +108,17 @@ func AddUploadContext(ctx *context.Context, uploadType string) {
ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Attachment.AllowedTypes, "|", ",")
ctx.Data["UploadMaxFiles"] = setting.Attachment.MaxFiles
ctx.Data["UploadMaxSize"] = setting.Attachment.MaxSize
- } else if uploadType == "repo" {
- ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/upload-file"
- ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/upload-remove"
- ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/upload-file"
- ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Repository.Upload.AllowedTypes, "|", ",")
- ctx.Data["UploadMaxFiles"] = setting.Repository.Upload.MaxFiles
- ctx.Data["UploadMaxSize"] = setting.Repository.Upload.FileMaxSize
+ default:
+ setting.PanicInDevOrTesting("Invalid upload type: %s", uploadType)
}
}
+
+func AddUploadContextForRepo(ctx reqctx.RequestContext, repo *repo_model.Repository) {
+ ctxData, repoLink := ctx.GetData(), repo.Link()
+ ctxData["UploadUrl"] = repoLink + "/upload-file"
+ ctxData["UploadRemoveUrl"] = repoLink + "/upload-remove"
+ ctxData["UploadLinkUrl"] = repoLink + "/upload-file"
+ ctxData["UploadAccepts"] = strings.ReplaceAll(setting.Repository.Upload.AllowedTypes, "|", ",")
+ ctxData["UploadMaxFiles"] = setting.Repository.Upload.MaxFiles
+ ctxData["UploadMaxSize"] = setting.Repository.Upload.FileMaxSize
+}
diff --git a/services/context/user.go b/services/context/user.go
index dbc35e198d..c09ded8339 100644
--- a/services/context/user.go
+++ b/services/context/user.go
@@ -14,15 +14,15 @@ import (
// UserAssignmentWeb returns a middleware to handle context-user assignment for web routes
func UserAssignmentWeb() func(ctx *Context) {
return func(ctx *Context) {
- errorFn := func(status int, title string, obj any) {
+ errorFn := func(status int, obj any) {
err, ok := obj.(error)
if !ok {
err = fmt.Errorf("%s", obj)
}
if status == http.StatusNotFound {
- ctx.NotFound(title, err)
+ ctx.NotFound(err)
} else {
- ctx.ServerError(title, err)
+ ctx.ServerError("UserAssignmentWeb", err)
}
}
ctx.ContextUser = userAssignment(ctx.Base, ctx.Doer, errorFn)
@@ -42,9 +42,9 @@ func UserIDAssignmentAPI() func(ctx *APIContext) {
ctx.ContextUser, err = user_model.GetUserByID(ctx, userID)
if err != nil {
if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusNotFound, "GetUserByID", err)
+ ctx.APIError(http.StatusNotFound, err)
} else {
- ctx.Error(http.StatusInternalServerError, "GetUserByID", err)
+ ctx.APIErrorInternal(err)
}
}
}
@@ -54,11 +54,11 @@ func UserIDAssignmentAPI() func(ctx *APIContext) {
// UserAssignmentAPI returns a middleware to handle context-user assignment for api routes
func UserAssignmentAPI() func(ctx *APIContext) {
return func(ctx *APIContext) {
- ctx.ContextUser = userAssignment(ctx.Base, ctx.Doer, ctx.Error)
+ ctx.ContextUser = userAssignment(ctx.Base, ctx.Doer, ctx.APIError)
}
}
-func userAssignment(ctx *Base, doer *user_model.User, errCb func(int, string, any)) (contextUser *user_model.User) {
+func userAssignment(ctx *Base, doer *user_model.User, errCb func(int, any)) (contextUser *user_model.User) {
username := ctx.PathParam("username")
if doer != nil && doer.LowerName == strings.ToLower(username) {
@@ -71,12 +71,12 @@ func userAssignment(ctx *Base, doer *user_model.User, errCb func(int, string, an
if redirectUserID, err := user_model.LookupUserRedirect(ctx, username); err == nil {
RedirectToUser(ctx, username, redirectUserID)
} else if user_model.IsErrUserRedirectNotExist(err) {
- errCb(http.StatusNotFound, "GetUserByName", err)
+ errCb(http.StatusNotFound, err)
} else {
- errCb(http.StatusInternalServerError, "LookupUserRedirect", err)
+ errCb(http.StatusInternalServerError, fmt.Errorf("LookupUserRedirect: %w", err))
}
} else {
- errCb(http.StatusInternalServerError, "GetUserByName", err)
+ errCb(http.StatusInternalServerError, fmt.Errorf("GetUserByName: %w", err))
}
}
}
diff --git a/services/contexttest/context_tests.go b/services/contexttest/context_tests.go
index b0f71cad20..b54023897b 100644
--- a/services/contexttest/context_tests.go
+++ b/services/contexttest/context_tests.go
@@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/cache"
+ git_module "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/modules/session"
@@ -30,6 +31,7 @@ import (
"github.com/go-chi/chi/v5"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func mockRequest(t *testing.T, reqPath string) *http.Request {
@@ -67,7 +69,6 @@ func MockContext(t *testing.T, reqPath string, opts ...MockContextOption) (*cont
chiCtx := chi.NewRouteContext()
ctx := context.NewWebContext(base, opt.Render, nil)
- ctx.SetContextValue(context.WebContextKey, ctx)
ctx.SetContextValue(chi.RouteCtxKey, chiCtx)
if opt.SessionStore != nil {
ctx.SetContextValue(session.MockStoreContextKey, opt.SessionStore)
@@ -86,7 +87,7 @@ func MockAPIContext(t *testing.T, reqPath string) (*context.APIContext, *httptes
base := context.NewBaseContext(resp, req)
base.Data = middleware.GetContextData(req.Context())
base.Locale = &translation.MockLocale{}
- ctx := &context.APIContext{Base: base}
+ ctx := &context.APIContext{Base: base, Repo: &context.Repository{}}
chiCtx := chi.NewRouteContext()
ctx.SetContextValue(chi.RouteCtxKey, chiCtx)
return ctx, resp
@@ -107,13 +108,13 @@ func MockPrivateContext(t *testing.T, reqPath string) (*context.PrivateContext,
// LoadRepo load a repo into a test context.
func LoadRepo(t *testing.T, ctx gocontext.Context, repoID int64) {
var doer *user_model.User
- repo := &context.Repository{}
+ var repo *context.Repository
switch ctx := ctx.(type) {
case *context.Context:
- ctx.Repo = repo
+ repo = ctx.Repo
doer = ctx.Doer
case *context.APIContext:
- ctx.Repo = repo
+ repo = ctx.Repo
doer = ctx.Doer
default:
assert.FailNow(t, "context is not *context.Context or *context.APIContext")
@@ -141,15 +142,17 @@ func LoadRepoCommit(t *testing.T, ctx gocontext.Context) {
}
gitRepo, err := gitrepo.OpenRepository(ctx, repo.Repository)
- assert.NoError(t, err)
+ require.NoError(t, err)
defer gitRepo.Close()
- branch, err := gitRepo.GetHEADBranch()
- assert.NoError(t, err)
- assert.NotNil(t, branch)
- if branch != nil {
- repo.Commit, err = gitRepo.GetBranchCommit(branch.Name)
- assert.NoError(t, err)
+
+ if repo.RefFullName == "" {
+ repo.RefFullName = git_module.RefNameFromBranch(repo.Repository.DefaultBranch)
}
+ if repo.RefFullName.IsPull() {
+ repo.BranchName = repo.RefFullName.ShortName()
+ }
+ repo.Commit, err = gitRepo.GetCommit(repo.RefFullName.String())
+ require.NoError(t, err)
}
// LoadUser load a user into a test context
@@ -167,10 +170,19 @@ func LoadUser(t *testing.T, ctx gocontext.Context, userID int64) {
// LoadGitRepo load a git repo into a test context. Requires that ctx.Repo has
// already been populated.
-func LoadGitRepo(t *testing.T, ctx *context.Context) {
- assert.NoError(t, ctx.Repo.Repository.LoadOwner(ctx))
+func LoadGitRepo(t *testing.T, ctx gocontext.Context) {
+ var repo *context.Repository
+ switch ctx := any(ctx).(type) {
+ case *context.Context:
+ repo = ctx.Repo
+ case *context.APIContext:
+ repo = ctx.Repo
+ default:
+ assert.FailNow(t, "context is not *context.Context or *context.APIContext")
+ }
+ assert.NoError(t, repo.Repository.LoadOwner(ctx))
var err error
- ctx.Repo.GitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
+ repo.GitRepo, err = gitrepo.OpenRepository(ctx, repo.Repository)
assert.NoError(t, err)
}
diff --git a/services/convert/convert.go b/services/convert/convert.go
index c8cad2a2ad..0de3822140 100644
--- a/services/convert/convert.go
+++ b/services/convert/convert.go
@@ -5,8 +5,11 @@
package convert
import (
+ "bytes"
"context"
"fmt"
+ "net/url"
+ "path"
"strconv"
"strings"
"time"
@@ -14,6 +17,7 @@ import (
actions_model "code.gitea.io/gitea/models/actions"
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
@@ -22,13 +26,18 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
+ asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/gitdiff"
+
+ runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
+ "github.com/nektos/act/pkg/model"
)
// ToEmail convert models.EmailAddress to api.Email
@@ -140,7 +149,7 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch, repo
mergeWhitelistUsernames := getWhitelistEntities(readers, bp.MergeWhitelistUserIDs)
approvalsWhitelistUsernames := getWhitelistEntities(readers, bp.ApprovalsWhitelistUserIDs)
- teamReaders, err := organization.OrgFromUser(repo.Owner).TeamsWithAccessToRepo(ctx, repo.ID, perm.AccessModeRead)
+ teamReaders, err := organization.GetTeamsWithAccessToAnyRepoUnit(ctx, repo.Owner.ID, repo.ID, perm.AccessModeRead, unit.TypeCode, unit.TypePullRequests)
if err != nil {
log.Error("Repo.Owner.TeamsWithAccessToRepo: %v", err)
}
@@ -194,13 +203,22 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch, repo
// ToTag convert a git.Tag to an api.Tag
func ToTag(repo *repo_model.Repository, t *git.Tag) *api.Tag {
+ tarballURL := util.URLJoin(repo.HTMLURL(), "archive", t.Name+".tar.gz")
+ zipballURL := util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip")
+
+ // Archive URLs are "" if the download feature is disabled
+ if setting.Repository.DisableDownloadSourceArchives {
+ tarballURL = ""
+ zipballURL = ""
+ }
+
return &api.Tag{
Name: t.Name,
Message: strings.TrimSpace(t.Message),
ID: t.ID.String(),
Commit: ToCommitMeta(repo, t),
- ZipballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip"),
- TarballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".tar.gz"),
+ ZipballURL: zipballURL,
+ TarballURL: tarballURL,
}
}
@@ -229,9 +247,291 @@ func ToActionTask(ctx context.Context, t *actions_model.ActionTask) (*api.Action
}, nil
}
+func ToActionWorkflowRun(ctx context.Context, repo *repo_model.Repository, run *actions_model.ActionRun) (*api.ActionWorkflowRun, error) {
+ err := run.LoadAttributes(ctx)
+ if err != nil {
+ return nil, err
+ }
+ status, conclusion := ToActionsStatus(run.Status)
+ return &api.ActionWorkflowRun{
+ ID: run.ID,
+ URL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(), run.ID),
+ HTMLURL: run.HTMLURL(),
+ RunNumber: run.Index,
+ StartedAt: run.Started.AsLocalTime(),
+ CompletedAt: run.Stopped.AsLocalTime(),
+ Event: string(run.Event),
+ DisplayTitle: run.Title,
+ HeadBranch: git.RefName(run.Ref).BranchName(),
+ HeadSha: run.CommitSHA,
+ Status: status,
+ Conclusion: conclusion,
+ Path: fmt.Sprintf("%s@%s", run.WorkflowID, run.Ref),
+ Repository: ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeNone}),
+ TriggerActor: ToUser(ctx, run.TriggerUser, nil),
+ // We do not have a way to get a different User for the actor than the trigger user
+ Actor: ToUser(ctx, run.TriggerUser, nil),
+ }, nil
+}
+
+func ToWorkflowRunAction(status actions_model.Status) string {
+ var action string
+ switch status {
+ case actions_model.StatusWaiting, actions_model.StatusBlocked:
+ action = "requested"
+ case actions_model.StatusRunning:
+ action = "in_progress"
+ }
+ if status.IsDone() {
+ action = "completed"
+ }
+ return action
+}
+
+func ToActionsStatus(status actions_model.Status) (string, string) {
+ var action string
+ var conclusion string
+ switch status {
+ // This is a naming conflict of the webhook between Gitea and GitHub Actions
+ case actions_model.StatusWaiting:
+ action = "queued"
+ case actions_model.StatusBlocked:
+ action = "waiting"
+ case actions_model.StatusRunning:
+ action = "in_progress"
+ }
+ if status.IsDone() {
+ action = "completed"
+ switch status {
+ case actions_model.StatusSuccess:
+ conclusion = "success"
+ case actions_model.StatusCancelled:
+ conclusion = "cancelled"
+ case actions_model.StatusFailure:
+ conclusion = "failure"
+ case actions_model.StatusSkipped:
+ conclusion = "skipped"
+ }
+ }
+ return action, conclusion
+}
+
+// ToActionWorkflowJob convert a actions_model.ActionRunJob to an api.ActionWorkflowJob
+// task is optional and can be nil
+func ToActionWorkflowJob(ctx context.Context, repo *repo_model.Repository, task *actions_model.ActionTask, job *actions_model.ActionRunJob) (*api.ActionWorkflowJob, error) {
+ err := job.LoadAttributes(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ jobIndex := 0
+ jobs, err := actions_model.GetRunJobsByRunID(ctx, job.RunID)
+ if err != nil {
+ return nil, err
+ }
+ for i, j := range jobs {
+ if j.ID == job.ID {
+ jobIndex = i
+ break
+ }
+ }
+
+ status, conclusion := ToActionsStatus(job.Status)
+ var runnerID int64
+ var runnerName string
+ var steps []*api.ActionWorkflowStep
+
+ if job.TaskID != 0 {
+ if task == nil {
+ task, _, err = db.GetByID[actions_model.ActionTask](ctx, job.TaskID)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ runnerID = task.RunnerID
+ if runner, ok, _ := db.GetByID[actions_model.ActionRunner](ctx, runnerID); ok {
+ runnerName = runner.Name
+ }
+ for i, step := range task.Steps {
+ stepStatus, stepConclusion := ToActionsStatus(job.Status)
+ steps = append(steps, &api.ActionWorkflowStep{
+ Name: step.Name,
+ Number: int64(i),
+ Status: stepStatus,
+ Conclusion: stepConclusion,
+ StartedAt: step.Started.AsTime().UTC(),
+ CompletedAt: step.Stopped.AsTime().UTC(),
+ })
+ }
+ }
+
+ return &api.ActionWorkflowJob{
+ ID: job.ID,
+ // missing api endpoint for this location
+ URL: fmt.Sprintf("%s/actions/jobs/%d", repo.APIURL(), job.ID),
+ HTMLURL: fmt.Sprintf("%s/jobs/%d", job.Run.HTMLURL(), jobIndex),
+ RunID: job.RunID,
+ // Missing api endpoint for this location, artifacts are available under a nested url
+ RunURL: fmt.Sprintf("%s/actions/runs/%d", repo.APIURL(), job.RunID),
+ Name: job.Name,
+ Labels: job.RunsOn,
+ RunAttempt: job.Attempt,
+ HeadSha: job.Run.CommitSHA,
+ HeadBranch: git.RefName(job.Run.Ref).BranchName(),
+ Status: status,
+ Conclusion: conclusion,
+ RunnerID: runnerID,
+ RunnerName: runnerName,
+ Steps: steps,
+ CreatedAt: job.Created.AsTime().UTC(),
+ StartedAt: job.Started.AsTime().UTC(),
+ CompletedAt: job.Stopped.AsTime().UTC(),
+ }, nil
+}
+
+func getActionWorkflowEntry(ctx context.Context, repo *repo_model.Repository, commit *git.Commit, folder string, entry *git.TreeEntry) *api.ActionWorkflow {
+ cfgUnit := repo.MustGetUnit(ctx, unit.TypeActions)
+ cfg := cfgUnit.ActionsConfig()
+
+ defaultBranch, _ := commit.GetBranchName()
+
+ workflowURL := fmt.Sprintf("%s/actions/workflows/%s", repo.APIURL(), util.PathEscapeSegments(entry.Name()))
+ workflowRepoURL := fmt.Sprintf("%s/src/branch/%s/%s/%s", repo.HTMLURL(ctx), util.PathEscapeSegments(defaultBranch), util.PathEscapeSegments(folder), util.PathEscapeSegments(entry.Name()))
+ badgeURL := fmt.Sprintf("%s/actions/workflows/%s/badge.svg?branch=%s", repo.HTMLURL(ctx), util.PathEscapeSegments(entry.Name()), url.QueryEscape(repo.DefaultBranch))
+
+ // See https://docs.github.com/en/rest/actions/workflows?apiVersion=2022-11-28#get-a-workflow
+ // State types:
+ // - active
+ // - deleted
+ // - disabled_fork
+ // - disabled_inactivity
+ // - disabled_manually
+ state := "active"
+ if cfg.IsWorkflowDisabled(entry.Name()) {
+ state = "disabled_manually"
+ }
+
+ // The CreatedAt and UpdatedAt fields currently reflect the timestamp of the latest commit, which can later be refined
+ // by retrieving the first and last commits for the file history. The first commit would indicate the creation date,
+ // while the last commit would represent the modification date. The DeletedAt could be determined by identifying
+ // the last commit where the file existed. However, this implementation has not been done here yet, as it would likely
+ // cause a significant performance degradation.
+ createdAt := commit.Author.When
+ updatedAt := commit.Author.When
+
+ content, err := actions.GetContentFromEntry(entry)
+ name := entry.Name()
+ if err == nil {
+ workflow, err := model.ReadWorkflow(bytes.NewReader(content))
+ if err == nil {
+ // Only use the name when specified in the workflow file
+ if workflow.Name != "" {
+ name = workflow.Name
+ }
+ } else {
+ log.Error("getActionWorkflowEntry: Failed to parse workflow: %v", err)
+ }
+ } else {
+ log.Error("getActionWorkflowEntry: Failed to get content from entry: %v", err)
+ }
+
+ return &api.ActionWorkflow{
+ ID: entry.Name(),
+ Name: name,
+ Path: path.Join(folder, entry.Name()),
+ State: state,
+ CreatedAt: createdAt,
+ UpdatedAt: updatedAt,
+ URL: workflowURL,
+ HTMLURL: workflowRepoURL,
+ BadgeURL: badgeURL,
+ }
+}
+
+func ListActionWorkflows(ctx context.Context, gitrepo *git.Repository, repo *repo_model.Repository) ([]*api.ActionWorkflow, error) {
+ defaultBranchCommit, err := gitrepo.GetBranchCommit(repo.DefaultBranch)
+ if err != nil {
+ return nil, err
+ }
+
+ folder, entries, err := actions.ListWorkflows(defaultBranchCommit)
+ if err != nil {
+ return nil, err
+ }
+
+ workflows := make([]*api.ActionWorkflow, len(entries))
+ for i, entry := range entries {
+ workflows[i] = getActionWorkflowEntry(ctx, repo, defaultBranchCommit, folder, entry)
+ }
+
+ return workflows, nil
+}
+
+func GetActionWorkflow(ctx context.Context, gitrepo *git.Repository, repo *repo_model.Repository, workflowID string) (*api.ActionWorkflow, error) {
+ entries, err := ListActionWorkflows(ctx, gitrepo, repo)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, entry := range entries {
+ if entry.ID == workflowID {
+ return entry, nil
+ }
+ }
+
+ return nil, util.NewNotExistErrorf("workflow %q not found", workflowID)
+}
+
+// ToActionArtifact convert a actions_model.ActionArtifact to an api.ActionArtifact
+func ToActionArtifact(repo *repo_model.Repository, art *actions_model.ActionArtifact) (*api.ActionArtifact, error) {
+ url := fmt.Sprintf("%s/actions/artifacts/%d", repo.APIURL(), art.ID)
+
+ return &api.ActionArtifact{
+ ID: art.ID,
+ Name: art.ArtifactName,
+ SizeInBytes: art.FileSize,
+ Expired: art.Status == actions_model.ArtifactStatusExpired,
+ URL: url,
+ ArchiveDownloadURL: url + "/zip",
+ CreatedAt: art.CreatedUnix.AsLocalTime(),
+ UpdatedAt: art.UpdatedUnix.AsLocalTime(),
+ ExpiresAt: art.ExpiredUnix.AsLocalTime(),
+ WorkflowRun: &api.ActionWorkflowRun{
+ ID: art.RunID,
+ RepositoryID: art.RepoID,
+ HeadSha: art.CommitSHA,
+ },
+ }, nil
+}
+
+func ToActionRunner(ctx context.Context, runner *actions_model.ActionRunner) *api.ActionRunner {
+ status := runner.Status()
+ apiStatus := "offline"
+ if runner.IsOnline() {
+ apiStatus = "online"
+ }
+ labels := make([]*api.ActionRunnerLabel, len(runner.AgentLabels))
+ for i, label := range runner.AgentLabels {
+ labels[i] = &api.ActionRunnerLabel{
+ ID: int64(i),
+ Name: label,
+ Type: "custom",
+ }
+ }
+ return &api.ActionRunner{
+ ID: runner.ID,
+ Name: runner.Name,
+ Status: apiStatus,
+ Busy: status == runnerv1.RunnerStatus_RUNNER_STATUS_ACTIVE,
+ Ephemeral: runner.Ephemeral,
+ Labels: labels,
+ }
+}
+
// ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification
func ToVerification(ctx context.Context, c *git.Commit) *api.PayloadCommitVerification {
- verif := asymkey_model.ParseCommitWithSignature(ctx, c)
+ verif := asymkey_service.ParseCommitWithSignature(ctx, c)
commitVerification := &api.PayloadCommitVerification{
Verified: verif.Verified,
Reason: verif.Reason,
@@ -258,6 +558,7 @@ func ToPublicKey(apiLink string, key *asymkey_model.PublicKey) *api.PublicKey {
Title: key.Name,
Fingerprint: key.Fingerprint,
Created: key.CreatedUnix.AsTime(),
+ Updated: key.UpdatedUnix.AsTime(),
}
}
@@ -426,7 +727,7 @@ func ToTagProtection(ctx context.Context, pt *git_model.ProtectedTag, repo *repo
whitelistUsernames := getWhitelistEntities(readers, pt.AllowlistUserIDs)
- teamReaders, err := organization.OrgFromUser(repo.Owner).TeamsWithAccessToRepo(ctx, repo.ID, perm.AccessModeRead)
+ teamReaders, err := organization.GetTeamsWithAccessToAnyRepoUnit(ctx, repo.Owner.ID, repo.ID, perm.AccessModeRead, unit.TypeCode, unit.TypePullRequests)
if err != nil {
log.Error("Repo.Owner.TeamsWithAccessToRepo: %v", err)
}
diff --git a/services/convert/git_commit.go b/services/convert/git_commit.go
index e0efcddbcb..3ec81b52ee 100644
--- a/services/convert/git_commit.go
+++ b/services/convert/git_commit.go
@@ -210,17 +210,15 @@ func ToCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Rep
// Get diff stats for commit
if opts.Stat {
- diff, err := gitdiff.GetDiff(ctx, gitRepo, &gitdiff.DiffOptions{
- AfterCommitID: commit.ID.String(),
- })
+ diffShortStat, err := gitdiff.GetDiffShortStat(gitRepo, "", commit.ID.String())
if err != nil {
return nil, err
}
res.Stats = &api.CommitStats{
- Total: diff.TotalAddition + diff.TotalDeletion,
- Additions: diff.TotalAddition,
- Deletions: diff.TotalDeletion,
+ Total: diffShortStat.TotalAddition + diffShortStat.TotalDeletion,
+ Additions: diffShortStat.TotalAddition,
+ Deletions: diffShortStat.TotalDeletion,
}
}
diff --git a/services/convert/git_commit_test.go b/services/convert/git_commit_test.go
index 73cb5e8c71..ad1cc0eca3 100644
--- a/services/convert/git_commit_test.go
+++ b/services/convert/git_commit_test.go
@@ -33,7 +33,7 @@ func TestToCommitMeta(t *testing.T) {
commitMeta := ToCommitMeta(headRepo, tag)
assert.NotNil(t, commitMeta)
- assert.EqualValues(t, &api.CommitMeta{
+ assert.Equal(t, &api.CommitMeta{
SHA: sha1.EmptyObjectID().String(),
URL: util.URLJoin(headRepo.APIURL(), "git/commits", sha1.EmptyObjectID().String()),
Created: time.Unix(0, 0),
diff --git a/services/convert/issue.go b/services/convert/issue.go
index e3124efd64..7f386e6293 100644
--- a/services/convert/issue.go
+++ b/services/convert/issue.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
)
func ToIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) *api.Issue {
@@ -40,6 +41,9 @@ func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Iss
if err := issue.LoadAttachments(ctx); err != nil {
return &api.Issue{}
}
+ if err := issue.LoadPinOrder(ctx); err != nil {
+ return &api.Issue{}
+ }
apiIssue := &api.Issue{
ID: issue.ID,
@@ -54,7 +58,7 @@ func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Iss
Comments: issue.NumComments,
Created: issue.CreatedUnix.AsTime(),
Updated: issue.UpdatedUnix.AsTime(),
- PinOrder: issue.PinOrder,
+ PinOrder: util.Iif(issue.PinOrder == -1, 0, issue.PinOrder), // -1 means loaded with no pin order
}
if issue.Repo != nil {
@@ -66,7 +70,7 @@ func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Iss
if err := issue.LoadLabels(ctx); err != nil {
return &api.Issue{}
}
- apiIssue.Labels = ToLabelList(issue.Labels, issue.Repo, issue.Repo.Owner)
+ apiIssue.Labels = util.SliceNilAsEmpty(ToLabelList(issue.Labels, issue.Repo, issue.Repo.Owner))
apiIssue.Repo = &api.RepositoryMeta{
ID: issue.Repo.ID,
Name: issue.Repo.Name,
@@ -121,6 +125,7 @@ func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Iss
// ToIssueList converts an IssueList to API format
func ToIssueList(ctx context.Context, doer *user_model.User, il issues_model.IssueList) []*api.Issue {
result := make([]*api.Issue, len(il))
+ _ = il.LoadPinOrder(ctx)
for i := range il {
result[i] = ToIssue(ctx, doer, il[i])
}
@@ -130,6 +135,7 @@ func ToIssueList(ctx context.Context, doer *user_model.User, il issues_model.Iss
// ToAPIIssueList converts an IssueList to API format
func ToAPIIssueList(ctx context.Context, doer *user_model.User, il issues_model.IssueList) []*api.Issue {
result := make([]*api.Issue, len(il))
+ _ = il.LoadPinOrder(ctx)
for i := range il {
result[i] = ToAPIIssue(ctx, doer, il[i])
}
@@ -186,7 +192,7 @@ func ToStopWatches(ctx context.Context, sws []*issues_model.Stopwatch) (api.Stop
result = append(result, api.StopWatch{
Created: sw.CreatedUnix.AsTime(),
Seconds: sw.Seconds(),
- Duration: sw.Duration(),
+ Duration: util.SecToHours(sw.Seconds()),
IssueIndex: issue.Index,
IssueTitle: issue.Title,
RepoOwnerName: repo.OwnerName,
diff --git a/services/convert/issue_comment.go b/services/convert/issue_comment.go
index b8527ae233..9ad584a62f 100644
--- a/services/convert/issue_comment.go
+++ b/services/convert/issue_comment.go
@@ -74,7 +74,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu
c.Content[0] == '|' {
// TimeTracking Comments from v1.21 on store the seconds instead of an formatted string
// so we check for the "|" delimiter and convert new to legacy format on demand
- c.Content = util.SecToTime(c.Content[1:])
+ c.Content = util.SecToHours(c.Content[1:])
}
if c.Type == issues_model.CommentTypeChangeTimeEstimate {
diff --git a/services/convert/pull.go b/services/convert/pull.go
index a1ab7eeb8e..8f9679f649 100644
--- a/services/convert/pull.go
+++ b/services/convert/pull.go
@@ -14,10 +14,14 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/cache"
+ "code.gitea.io/gitea/modules/cachegroup"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/gitdiff"
)
// ToAPIPullRequest assumes following fields have been assigned with valid values:
@@ -25,8 +29,8 @@ import (
// Optional - Merger
func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User) *api.PullRequest {
var (
- baseBranch *git.Branch
- headBranch *git.Branch
+ baseBranch string
+ headBranch string
baseCommit *git.Commit
err error
)
@@ -57,14 +61,14 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
doerID = doer.ID
}
- const repoDoerPermCacheKey = "repo_doer_perm_cache"
- p, err := cache.GetWithContextCache(ctx, repoDoerPermCacheKey, fmt.Sprintf("%d_%d", pr.BaseRepoID, doerID),
- func() (access_model.Permission, error) {
+ repoUserPerm, err := cache.GetWithContextCache(ctx, cachegroup.RepoUserPermission, fmt.Sprintf("%d-%d", pr.BaseRepoID, doerID),
+ func(ctx context.Context, _ string) (access_model.Permission, error) {
return access_model.GetUserRepoPermission(ctx, pr.BaseRepo, doer)
- })
+ },
+ )
if err != nil {
log.Error("GetUserRepoPermission[%d]: %v", pr.BaseRepoID, err)
- p.AccessMode = perm.AccessModeNone
+ repoUserPerm.AccessMode = perm.AccessModeNone
}
apiPullRequest := &api.PullRequest{
@@ -77,7 +81,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
Labels: apiIssue.Labels,
Milestone: apiIssue.Milestone,
Assignee: apiIssue.Assignee,
- Assignees: apiIssue.Assignees,
+ Assignees: util.SliceNilAsEmpty(apiIssue.Assignees),
State: apiIssue.State,
Draft: pr.IsWorkInProgress(ctx),
IsLocked: apiIssue.IsLocked,
@@ -92,7 +96,11 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
Deadline: apiIssue.Deadline,
Created: pr.Issue.CreatedUnix.AsTimePtr(),
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
- PinOrder: apiIssue.PinOrder,
+ PinOrder: util.Iif(apiIssue.PinOrder == -1, 0, apiIssue.PinOrder),
+
+ // output "[]" rather than null to align to github outputs
+ RequestedReviewers: []*api.User{},
+ RequestedReviewersTeams: []*api.Team{},
AllowMaintainerEdit: pr.AllowMaintainerEdit,
@@ -100,7 +108,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
Name: pr.BaseBranch,
Ref: pr.BaseBranch,
RepoID: pr.BaseRepoID,
- Repository: ToRepo(ctx, pr.BaseRepo, p),
+ Repository: ToRepo(ctx, pr.BaseRepo, repoUserPerm),
},
Head: &api.PRBranchInfo{
Name: pr.HeadBranch,
@@ -143,16 +151,16 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
}
defer gitRepo.Close()
- baseBranch, err = gitRepo.GetBranch(pr.BaseBranch)
- if err != nil && !git.IsErrBranchNotExist(err) {
+ exist, err := git_model.IsBranchExist(ctx, pr.BaseRepoID, pr.BaseBranch)
+ if err != nil {
log.Error("GetBranch[%s]: %v", pr.BaseBranch, err)
return nil
}
- if err == nil {
- baseCommit, err = baseBranch.GetCommit()
+ if exist {
+ baseCommit, err = gitRepo.GetBranchCommit(pr.BaseBranch)
if err != nil && !git.IsErrNotExist(err) {
- log.Error("GetCommit[%s]: %v", baseBranch.Name, err)
+ log.Error("GetCommit[%s]: %v", baseBranch, err)
return nil
}
@@ -162,13 +170,6 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
}
if pr.Flow == issues_model.PullRequestFlowAGit {
- gitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo)
- if err != nil {
- log.Error("OpenRepository[%s]: %v", pr.GetGitRefName(), err)
- return nil
- }
- defer gitRepo.Close()
-
apiPullRequest.Head.Sha, err = gitRepo.GetRefCommitID(pr.GetGitRefName())
if err != nil {
log.Error("GetRefCommitID[%s]: %v", pr.GetGitRefName(), err)
@@ -196,8 +197,8 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
}
defer headGitRepo.Close()
- headBranch, err = headGitRepo.GetBranch(pr.HeadBranch)
- if err != nil && !git.IsErrBranchNotExist(err) {
+ exist, err = git_model.IsBranchExist(ctx, pr.HeadRepoID, pr.HeadBranch)
+ if err != nil {
log.Error("GetBranch[%s]: %v", pr.HeadBranch, err)
return nil
}
@@ -208,7 +209,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
endCommitID string
)
- if git.IsErrBranchNotExist(err) {
+ if !exist {
headCommitID, err := headGitRepo.GetRefCommitID(apiPullRequest.Head.Ref)
if err != nil && !git.IsErrNotExist(err) {
log.Error("GetCommit[%s]: %v", pr.HeadBranch, err)
@@ -219,9 +220,9 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
endCommitID = headCommitID
}
} else {
- commit, err := headBranch.GetCommit()
+ commit, err := headGitRepo.GetBranchCommit(pr.HeadBranch)
if err != nil && !git.IsErrNotExist(err) {
- log.Error("GetCommit[%s]: %v", headBranch.Name, err)
+ log.Error("GetCommit[%s]: %v", headBranch, err)
return nil
}
if err == nil {
@@ -234,9 +235,13 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
// Calculate diff
startCommitID = pr.MergeBase
- apiPullRequest.ChangedFiles, apiPullRequest.Additions, apiPullRequest.Deletions, err = gitRepo.GetDiffShortStat(startCommitID, endCommitID)
+ diffShortStats, err := gitdiff.GetDiffShortStat(gitRepo, startCommitID, endCommitID)
if err != nil {
log.Error("GetDiffShortStat: %v", err)
+ } else {
+ apiPullRequest.ChangedFiles = &diffShortStats.NumFiles
+ apiPullRequest.Additions = &diffShortStats.TotalAddition
+ apiPullRequest.Deletions = &diffShortStats.TotalDeletion
}
}
@@ -299,6 +304,9 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
if err := issueList.LoadAssignees(ctx); err != nil {
return nil, err
}
+ if err = issueList.LoadPinOrder(ctx); err != nil {
+ return nil, err
+ }
reviews, err := prs.LoadReviews(ctx)
if err != nil {
@@ -363,7 +371,7 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
Deadline: apiIssue.Deadline,
Created: pr.Issue.CreatedUnix.AsTimePtr(),
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
- PinOrder: apiIssue.PinOrder,
+ PinOrder: util.Iif(apiIssue.PinOrder == -1, 0, apiIssue.PinOrder),
AllowMaintainerEdit: pr.AllowMaintainerEdit,
@@ -375,7 +383,7 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
},
Head: &api.PRBranchInfo{
Name: pr.HeadBranch,
- Ref: fmt.Sprintf("%s%d/head", git.PullPrefix, pr.Index),
+ Ref: pr.GetGitRefName(),
RepoID: -1,
},
}
@@ -408,88 +416,43 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
return nil, err
}
}
-
if baseBranch != nil {
apiPullRequest.Base.Sha = baseBranch.CommitID
}
+ if pr.HeadRepoID == pr.BaseRepoID {
+ apiPullRequest.Head.Repository = apiPullRequest.Base.Repository
+ }
- if pr.Flow == issues_model.PullRequestFlowAGit {
- apiPullRequest.Head.Sha, err = gitRepo.GetRefCommitID(pr.GetGitRefName())
+ // pull request head branch, both repository and branch could not exist
+ if pr.HeadRepo != nil {
+ apiPullRequest.Head.RepoID = pr.HeadRepo.ID
+ exist, err := git_model.IsBranchExist(ctx, pr.HeadRepo.ID, pr.HeadBranch)
if err != nil {
- log.Error("GetRefCommitID[%s]: %v", pr.GetGitRefName(), err)
+ log.Error("IsBranchExist[%d]: %v", pr.HeadRepo.ID, err)
return nil, err
}
- apiPullRequest.Head.RepoID = pr.BaseRepoID
- apiPullRequest.Head.Repository = apiPullRequest.Base.Repository
- apiPullRequest.Head.Name = ""
- }
-
- var headGitRepo *git.Repository
- if pr.HeadRepo != nil && pr.Flow == issues_model.PullRequestFlowGithub {
- if pr.HeadRepoID == pr.BaseRepoID {
- apiPullRequest.Head.RepoID = pr.HeadRepo.ID
- apiPullRequest.Head.Repository = apiRepo
- headGitRepo = gitRepo
- } else {
+ if exist {
+ apiPullRequest.Head.Ref = pr.HeadBranch
+ }
+ if pr.HeadRepoID != pr.BaseRepoID {
p, err := access_model.GetUserRepoPermission(ctx, pr.HeadRepo, doer)
if err != nil {
log.Error("GetUserRepoPermission[%d]: %v", pr.HeadRepoID, err)
p.AccessMode = perm.AccessModeNone
}
-
- apiPullRequest.Head.RepoID = pr.HeadRepo.ID
apiPullRequest.Head.Repository = ToRepo(ctx, pr.HeadRepo, p)
-
- headGitRepo, err = gitrepo.OpenRepository(ctx, pr.HeadRepo)
- if err != nil {
- log.Error("OpenRepository[%s]: %v", pr.HeadRepo.RepoPath(), err)
- return nil, err
- }
- defer headGitRepo.Close()
}
+ }
+ if apiPullRequest.Head.Ref == "" {
+ apiPullRequest.Head.Ref = pr.GetGitRefName()
+ }
- headBranch, err := headGitRepo.GetBranch(pr.HeadBranch)
- if err != nil && !git.IsErrBranchNotExist(err) {
- log.Error("GetBranch[%s]: %v", pr.HeadBranch, err)
- return nil, err
- }
-
- // Outer scope variables to be used in diff calculation
- var (
- startCommitID string
- endCommitID string
- )
-
- if git.IsErrBranchNotExist(err) {
- headCommitID, err := headGitRepo.GetRefCommitID(apiPullRequest.Head.Ref)
- if err != nil && !git.IsErrNotExist(err) {
- log.Error("GetCommit[%s]: %v", pr.HeadBranch, err)
- return nil, err
- }
- if err == nil {
- apiPullRequest.Head.Sha = headCommitID
- endCommitID = headCommitID
- }
- } else {
- commit, err := headBranch.GetCommit()
- if err != nil && !git.IsErrNotExist(err) {
- log.Error("GetCommit[%s]: %v", headBranch.Name, err)
- return nil, err
- }
- if err == nil {
- apiPullRequest.Head.Ref = pr.HeadBranch
- apiPullRequest.Head.Sha = commit.ID.String()
- endCommitID = commit.ID.String()
- }
- }
-
- // Calculate diff
- startCommitID = pr.MergeBase
-
- apiPullRequest.ChangedFiles, apiPullRequest.Additions, apiPullRequest.Deletions, err = gitRepo.GetDiffShortStat(startCommitID, endCommitID)
- if err != nil {
- log.Error("GetDiffShortStat: %v", err)
- }
+ if pr.Flow == issues_model.PullRequestFlowAGit {
+ apiPullRequest.Head.Name = ""
+ }
+ apiPullRequest.Head.Sha, err = gitRepo.GetRefCommitID(pr.GetGitRefName())
+ if err != nil {
+ log.Error("GetRefCommitID[%s]: %v", pr.GetGitRefName(), err)
}
if len(apiPullRequest.Head.Sha) == 0 && len(apiPullRequest.Head.Ref) != 0 {
@@ -510,6 +473,12 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
apiPullRequest.MergedBy = ToUser(ctx, pr.Merger, nil)
}
+ // Do not provide "ChangeFiles/Additions/Deletions" for the PR list, because the "diff" is quite slow
+ // If callers are interested in these values, they should do a separate request to get the PR details
+ if apiPullRequest.ChangedFiles != nil || apiPullRequest.Additions != nil || apiPullRequest.Deletions != nil {
+ setting.PanicInDevOrTesting("ChangedFiles/Additions/Deletions should not be set in PR list")
+ }
+
apiPullRequests = append(apiPullRequests, apiPullRequest)
}
diff --git a/services/convert/pull_review_test.go b/services/convert/pull_review_test.go
index a1296fafd4..d0a077ab24 100644
--- a/services/convert/pull_review_test.go
+++ b/services/convert/pull_review_test.go
@@ -19,8 +19,8 @@ func Test_ToPullReview(t *testing.T) {
reviewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 6})
- assert.EqualValues(t, reviewer.ID, review.ReviewerID)
- assert.EqualValues(t, issues_model.ReviewTypePending, review.Type)
+ assert.Equal(t, reviewer.ID, review.ReviewerID)
+ assert.Equal(t, issues_model.ReviewTypePending, review.Type)
reviewList := []*issues_model.Review{review}
diff --git a/services/convert/pull_test.go b/services/convert/pull_test.go
index e069fa4a68..dfbe24d184 100644
--- a/services/convert/pull_test.go
+++ b/services/convert/pull_test.go
@@ -27,7 +27,7 @@ func TestPullRequest_APIFormat(t *testing.T) {
assert.NoError(t, pr.LoadIssue(db.DefaultContext))
apiPullRequest := ToAPIPullRequest(git.DefaultContext, pr, nil)
assert.NotNil(t, apiPullRequest)
- assert.EqualValues(t, &structs.PRBranchInfo{
+ assert.Equal(t, &structs.PRBranchInfo{
Name: "branch1",
Ref: "refs/pull/2/head",
Sha: "4a357436d925b5c974181ff12a994538ddc5a269",
@@ -46,4 +46,11 @@ func TestPullRequest_APIFormat(t *testing.T) {
assert.NotNil(t, apiPullRequest)
assert.Nil(t, apiPullRequest.Head.Repository)
assert.EqualValues(t, -1, apiPullRequest.Head.RepoID)
+
+ apiPullRequests, err := ToAPIPullRequests(git.DefaultContext, pr.BaseRepo, []*issues_model.PullRequest{pr}, nil)
+ assert.NoError(t, err)
+ assert.Len(t, apiPullRequests, 1)
+ assert.NotNil(t, apiPullRequests[0])
+ assert.Nil(t, apiPullRequests[0].Head.Repository)
+ assert.EqualValues(t, -1, apiPullRequests[0].Head.RepoID)
}
diff --git a/services/convert/release_test.go b/services/convert/release_test.go
index 201b27e16d..bb618c9ca3 100644
--- a/services/convert/release_test.go
+++ b/services/convert/release_test.go
@@ -23,6 +23,6 @@ func TestRelease_ToRelease(t *testing.T) {
apiRelease := ToAPIRelease(db.DefaultContext, repo1, release1)
assert.NotNil(t, apiRelease)
assert.EqualValues(t, 1, apiRelease.ID)
- assert.EqualValues(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1", apiRelease.URL)
- assert.EqualValues(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1/assets", apiRelease.UploadURL)
+ assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1", apiRelease.URL)
+ assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1/assets", apiRelease.UploadURL)
}
diff --git a/services/convert/repository.go b/services/convert/repository.go
index 88ccd88fcf..614eb58a88 100644
--- a/services/convert/repository.go
+++ b/services/convert/repository.go
@@ -14,6 +14,7 @@ import (
unit_model "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
)
// ToRepo converts a Repository to api.Repository
@@ -33,7 +34,9 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
permissionInRepo.SetUnitsWithDefaultAccessMode(repo.Units, permissionInRepo.AccessMode)
}
- cloneLink := repo.CloneLink()
+ // TODO: ideally we should pass "doer" into "ToRepo" to to make CloneLink could generate user-related links
+ // And passing "doer" in will also fix other FIXMEs in this file.
+ cloneLink := repo.CloneLinkGeneral(ctx) // no doer at the moment
permission := &api.Permission{
Admin: permissionInRepo.AccessMode >= perm.AccessModeAdmin,
Push: permissionInRepo.UnitAccessMode(unit_model.TypeCode) >= perm.AccessModeWrite,
@@ -95,6 +98,8 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
allowSquash := false
allowFastForwardOnly := false
allowRebaseUpdate := false
+ allowManualMerge := true
+ autodetectManualMerge := false
defaultDeleteBranchAfterMerge := false
defaultMergeStyle := repo_model.MergeStyleMerge
defaultAllowMaintainerEdit := false
@@ -108,6 +113,8 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
allowSquash = config.AllowSquash
allowFastForwardOnly = config.AllowFastForwardOnly
allowRebaseUpdate = config.AllowRebaseUpdate
+ allowManualMerge = config.AllowManualMerge
+ autodetectManualMerge = config.AutodetectManualMerge
defaultDeleteBranchAfterMerge = config.DefaultDeleteBranchAfterMerge
defaultMergeStyle = config.GetDefaultMergeStyle()
defaultAllowMaintainerEdit = config.DefaultAllowMaintainerEdit
@@ -232,6 +239,8 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
AllowSquash: allowSquash,
AllowFastForwardOnly: allowFastForwardOnly,
AllowRebaseUpdate: allowRebaseUpdate,
+ AllowManualMerge: allowManualMerge,
+ AutodetectManualMerge: autodetectManualMerge,
DefaultDeleteBranchAfterMerge: defaultDeleteBranchAfterMerge,
DefaultMergeStyle: string(defaultMergeStyle),
DefaultAllowMaintainerEdit: defaultAllowMaintainerEdit,
@@ -240,7 +249,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR
MirrorInterval: mirrorInterval,
MirrorUpdated: mirrorUpdated,
RepoTransfer: transfer,
- Topics: repo.Topics,
+ Topics: util.SliceNilAsEmpty(repo.Topics),
ObjectFormatName: repo.ObjectFormatName,
Licenses: repoLicenses.StringList(),
}
diff --git a/services/convert/status.go b/services/convert/status.go
index 6cef63c1cd..b4864a0307 100644
--- a/services/convert/status.go
+++ b/services/convert/status.go
@@ -5,6 +5,7 @@ package convert
import (
"context"
+ "net/url"
git_model "code.gitea.io/gitea/models/git"
user_model "code.gitea.io/gitea/models/user"
@@ -32,34 +33,29 @@ func ToCommitStatus(ctx context.Context, status *git_model.CommitStatus) *api.Co
return apiStatus
}
+func ToCommitStatuses(ctx context.Context, statuses []*git_model.CommitStatus) []*api.CommitStatus {
+ apiStatuses := make([]*api.CommitStatus, len(statuses))
+ for i, status := range statuses {
+ apiStatuses[i] = ToCommitStatus(ctx, status)
+ }
+ return apiStatuses
+}
+
// ToCombinedStatus converts List of CommitStatus to a CombinedStatus
func ToCombinedStatus(ctx context.Context, statuses []*git_model.CommitStatus, repo *api.Repository) *api.CombinedStatus {
if len(statuses) == 0 {
return nil
}
- retStatus := &api.CombinedStatus{
- SHA: statuses[0].SHA,
+ combinedStatus := git_model.CalcCommitStatus(statuses)
+
+ return &api.CombinedStatus{
+ State: combinedStatus.State,
+ Statuses: ToCommitStatuses(ctx, statuses),
+ SHA: combinedStatus.SHA,
TotalCount: len(statuses),
Repository: repo,
- URL: "",
+ CommitURL: repo.URL + "/commits/" + url.PathEscape(combinedStatus.SHA),
+ URL: repo.URL + "/commits/" + url.PathEscape(combinedStatus.SHA) + "/status",
}
-
- retStatus.Statuses = make([]*api.CommitStatus, 0, len(statuses))
- for _, status := range statuses {
- retStatus.Statuses = append(retStatus.Statuses, ToCommitStatus(ctx, status))
- if retStatus.State == "" || status.State.NoBetterThan(retStatus.State) {
- retStatus.State = status.State
- }
- }
- // According to https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#get-the-combined-status-for-a-specific-reference
- // > Additionally, a combined state is returned. The state is one of:
- // > failure if any of the contexts report as error or failure
- // > pending if there are no statuses or a context is pending
- // > success if the latest status for all contexts is success
- if retStatus.State.IsError() {
- retStatus.State = api.CommitStatusFailure
- }
-
- return retStatus
}
diff --git a/services/convert/user_test.go b/services/convert/user_test.go
index 4b1effc7aa..199d500732 100644
--- a/services/convert/user_test.go
+++ b/services/convert/user_test.go
@@ -30,11 +30,11 @@ func TestUser_ToUser(t *testing.T) {
apiUser = toUser(db.DefaultContext, user1, false, false)
assert.False(t, apiUser.IsAdmin)
- assert.EqualValues(t, api.VisibleTypePublic.String(), apiUser.Visibility)
+ assert.Equal(t, api.VisibleTypePublic.String(), apiUser.Visibility)
user31 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31, IsAdmin: false, Visibility: api.VisibleTypePrivate})
apiUser = toUser(db.DefaultContext, user31, true, true)
assert.False(t, apiUser.IsAdmin)
- assert.EqualValues(t, api.VisibleTypePrivate.String(), apiUser.Visibility)
+ assert.Equal(t, api.VisibleTypePrivate.String(), apiUser.Visibility)
}
diff --git a/services/convert/utils.go b/services/convert/utils.go
index 5e9d32cc8e..b59884ec50 100644
--- a/services/convert/utils.go
+++ b/services/convert/utils.go
@@ -36,6 +36,8 @@ func ToGitServiceType(value string) structs.GitServiceType {
return structs.OneDevService
case "gitbucket":
return structs.GitBucketService
+ case "codebase":
+ return structs.CodebaseService
case "codecommit":
return structs.CodeCommitService
default:
diff --git a/services/convert/utils_test.go b/services/convert/utils_test.go
index 1ac03a3097..7965624e2b 100644
--- a/services/convert/utils_test.go
+++ b/services/convert/utils_test.go
@@ -10,10 +10,10 @@ import (
)
func TestToCorrectPageSize(t *testing.T) {
- assert.EqualValues(t, 30, ToCorrectPageSize(0))
- assert.EqualValues(t, 30, ToCorrectPageSize(-10))
- assert.EqualValues(t, 20, ToCorrectPageSize(20))
- assert.EqualValues(t, 50, ToCorrectPageSize(100))
+ assert.Equal(t, 30, ToCorrectPageSize(0))
+ assert.Equal(t, 30, ToCorrectPageSize(-10))
+ assert.Equal(t, 20, ToCorrectPageSize(20))
+ assert.Equal(t, 50, ToCorrectPageSize(100))
}
func TestToGitServiceType(t *testing.T) {
@@ -21,6 +21,8 @@ func TestToGitServiceType(t *testing.T) {
typ string
enum int
}{{
+ typ: "trash", enum: 1,
+ }, {
typ: "github", enum: 2,
}, {
typ: "gitea", enum: 3,
@@ -29,7 +31,13 @@ func TestToGitServiceType(t *testing.T) {
}, {
typ: "gogs", enum: 5,
}, {
- typ: "trash", enum: 1,
+ typ: "onedev", enum: 6,
+ }, {
+ typ: "gitbucket", enum: 7,
+ }, {
+ typ: "codebase", enum: 8,
+ }, {
+ typ: "codecommit", enum: 9,
}}
for _, test := range tc {
assert.EqualValues(t, test.enum, ToGitServiceType(test.typ))
diff --git a/services/cron/tasks_basic.go b/services/cron/tasks_basic.go
index fb5938745e..841981787d 100644
--- a/services/cron/tasks_basic.go
+++ b/services/cron/tasks_basic.go
@@ -54,7 +54,7 @@ func registerRepoHealthCheck() {
RunAtStart: false,
Schedule: "@midnight",
},
- Timeout: 60 * time.Second,
+ Timeout: time.Duration(setting.Git.Timeout.Default) * time.Second,
Args: []string{},
}, func(ctx context.Context, _ *user_model.User, config Config) error {
rhcConfig := config.(*RepoHealthCheckConfig)
diff --git a/services/doctor/actions.go b/services/doctor/actions.go
index 7c44fb8392..28e26c88eb 100644
--- a/services/doctor/actions.go
+++ b/services/doctor/actions.go
@@ -19,7 +19,7 @@ func disableMirrorActionsUnit(ctx context.Context, logger log.Logger, autofix bo
var reposToFix []*repo_model.Repository
for page := 1; ; page++ {
- repos, _, err := repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
+ repos, _, err := repo_model.SearchRepository(ctx, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: repo_model.RepositoryListDefaultPageSize,
Page: page,
diff --git a/services/doctor/authorizedkeys.go b/services/doctor/authorizedkeys.go
index 8d6fc9cb5e..46e7099dce 100644
--- a/services/doctor/authorizedkeys.go
+++ b/services/doctor/authorizedkeys.go
@@ -7,6 +7,7 @@ import (
"bufio"
"bytes"
"context"
+ "errors"
"fmt"
"os"
"path/filepath"
@@ -78,7 +79,7 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e
fPath,
"gitea admin regenerate keys",
"gitea doctor --run authorized-keys --fix")
- return fmt.Errorf(`authorized_keys is out of date and should be regenerated with "gitea admin regenerate keys" or "gitea doctor --run authorized-keys --fix"`)
+ return errors.New(`authorized_keys is out of date and should be regenerated with "gitea admin regenerate keys" or "gitea doctor --run authorized-keys --fix"`)
}
logger.Warn("authorized_keys is out of date. Attempting rewrite...")
err = asymkey_service.RewriteAllPublicKeys(ctx)
diff --git a/services/doctor/dbconsistency.go b/services/doctor/dbconsistency.go
index 7cb7445148..d5a133d8b2 100644
--- a/services/doctor/dbconsistency.go
+++ b/services/doctor/dbconsistency.go
@@ -12,8 +12,10 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/migrations"
repo_model "code.gitea.io/gitea/models/repo"
+ secret_model "code.gitea.io/gitea/models/secret"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ issue_service "code.gitea.io/gitea/services/issue"
)
type consistencyCheck struct {
@@ -92,7 +94,7 @@ func prepareDBConsistencyChecks() []consistencyCheck {
// find issues without existing repository
Name: "Orphaned Issues without existing repository",
Counter: issues_model.CountOrphanedIssues,
- Fixer: asFixer(issues_model.DeleteOrphanedIssues),
+ Fixer: asFixer(issue_service.DeleteOrphanedIssues),
},
// find releases without existing repository
genericOrphanCheck("Orphaned Releases without existing repository",
@@ -164,6 +166,24 @@ func prepareDBConsistencyChecks() []consistencyCheck {
Fixer: repo_model.DeleteOrphanedTopics,
FixedMessage: "Removed",
},
+ {
+ Name: "Repository level Runners with non-zero owner_id",
+ Counter: actions_model.CountWrongRepoLevelRunners,
+ Fixer: actions_model.UpdateWrongRepoLevelRunners,
+ FixedMessage: "Corrected",
+ },
+ {
+ Name: "Repository level Variables with non-zero owner_id",
+ Counter: actions_model.CountWrongRepoLevelVariables,
+ Fixer: actions_model.UpdateWrongRepoLevelVariables,
+ FixedMessage: "Corrected",
+ },
+ {
+ Name: "Repository level Secrets with non-zero owner_id",
+ Counter: secret_model.CountWrongRepoLevelSecrets,
+ Fixer: secret_model.UpdateWrongRepoLevelSecrets,
+ FixedMessage: "Corrected",
+ },
}
// TODO: function to recalc all counters
diff --git a/services/doctor/dbconsistency_test.go b/services/doctor/dbconsistency_test.go
index 4e4ac535b7..eb427dee73 100644
--- a/services/doctor/dbconsistency_test.go
+++ b/services/doctor/dbconsistency_test.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/log"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestConsistencyCheck(t *testing.T) {
@@ -21,9 +22,7 @@ func TestConsistencyCheck(t *testing.T) {
idx := slices.IndexFunc(checks, func(check consistencyCheck) bool {
return check.Name == "Orphaned OAuth2Application without existing User"
})
- if !assert.NotEqual(t, -1, idx) {
- return
- }
+ require.NotEqual(t, -1, idx)
_ = db.TruncateBeans(db.DefaultContext, &auth.OAuth2Application{}, &user.User{})
_ = db.TruncateBeans(db.DefaultContext, &auth.OAuth2Application{}, &auth.OAuth2Application{})
diff --git a/services/doctor/dbversion.go b/services/doctor/dbversion.go
index 2a102b2194..34279a45e7 100644
--- a/services/doctor/dbversion.go
+++ b/services/doctor/dbversion.go
@@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/migrations"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/services/versioned_migration"
)
func checkDBVersion(ctx context.Context, logger log.Logger, autofix bool) error {
@@ -21,7 +22,7 @@ func checkDBVersion(ctx context.Context, logger log.Logger, autofix bool) error
logger.Warn("Got Error: %v during ensure up to date", err)
logger.Warn("Attempting to migrate to the latest DB version to fix this.")
- err = db.InitEngineWithMigration(ctx, migrations.Migrate)
+ err = db.InitEngineWithMigration(ctx, versioned_migration.Migrate)
if err != nil {
logger.Critical("Error: %v during migration", err)
}
diff --git a/services/doctor/doctor.go b/services/doctor/doctor.go
index a4eb5e16b9..c6810a5fa0 100644
--- a/services/doctor/doctor.go
+++ b/services/doctor/doctor.go
@@ -48,7 +48,7 @@ type doctorCheckLogger struct {
var _ log.BaseLogger = (*doctorCheckLogger)(nil)
-func (d *doctorCheckLogger) Log(skip int, level log.Level, format string, v ...any) {
+func (d *doctorCheckLogger) Log(skip int, event *log.Event, format string, v ...any) {
_, _ = fmt.Fprintf(os.Stdout, format+"\n", v...)
}
@@ -62,11 +62,11 @@ type doctorCheckStepLogger struct {
var _ log.BaseLogger = (*doctorCheckStepLogger)(nil)
-func (d *doctorCheckStepLogger) Log(skip int, level log.Level, format string, v ...any) {
- levelChar := fmt.Sprintf("[%s]", strings.ToUpper(level.String()[0:1]))
+func (d *doctorCheckStepLogger) Log(skip int, event *log.Event, format string, v ...any) {
+ levelChar := fmt.Sprintf("[%s]", strings.ToUpper(event.Level.String()[0:1]))
var levelArg any = levelChar
if d.colorize {
- levelArg = log.NewColoredValue(levelChar, level.ColorAttributes()...)
+ levelArg = log.NewColoredValue(levelChar, event.Level.ColorAttributes()...)
}
args := append([]any{levelArg}, v...)
_, _ = fmt.Fprintf(os.Stdout, " - %s "+format+"\n", args...)
diff --git a/services/doctor/fix16961_test.go b/services/doctor/fix16961_test.go
index 498ed9c8d5..11a128620c 100644
--- a/services/doctor/fix16961_test.go
+++ b/services/doctor/fix16961_test.go
@@ -19,12 +19,6 @@ func Test_fixUnitConfig_16961(t *testing.T) {
wantErr bool
}{
{
- name: "empty",
- bs: "",
- wantFixed: true,
- wantErr: false,
- },
- {
name: "normal: {}",
bs: "{}",
wantFixed: false,
@@ -221,7 +215,7 @@ func Test_fixPullRequestsConfig_16961(t *testing.T) {
if gotFixed != tt.wantFixed {
t.Errorf("fixPullRequestsConfig_16961() = %v, want %v", gotFixed, tt.wantFixed)
}
- assert.EqualValues(t, &tt.expected, cfg)
+ assert.Equal(t, &tt.expected, cfg)
})
}
}
@@ -265,7 +259,7 @@ func Test_fixIssuesConfig_16961(t *testing.T) {
if gotFixed != tt.wantFixed {
t.Errorf("fixIssuesConfig_16961() = %v, want %v", gotFixed, tt.wantFixed)
}
- assert.EqualValues(t, &tt.expected, cfg)
+ assert.Equal(t, &tt.expected, cfg)
})
}
}
diff --git a/services/doctor/heads.go b/services/doctor/heads.go
index 41fca01d57..bbfd40da5e 100644
--- a/services/doctor/heads.go
+++ b/services/doctor/heads.go
@@ -18,9 +18,9 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool)
numReposUpdated := 0
err := iterateRepositories(ctx, func(repo *repo_model.Repository) error {
numRepos++
- _, _, defaultBranchErr := git.NewCommand(ctx, "rev-parse").AddDashesAndList(repo.DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
+ _, _, defaultBranchErr := git.NewCommand("rev-parse").AddDashesAndList(repo.DefaultBranch).RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
- head, _, headErr := git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD").RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
+ head, _, headErr := git.NewCommand("symbolic-ref", "--short", "HEAD").RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
// what we expect: default branch is valid, and HEAD points to it
if headErr == nil && defaultBranchErr == nil && head == repo.DefaultBranch {
@@ -46,7 +46,7 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool)
}
// otherwise, let's try fixing HEAD
- err := git.NewCommand(ctx, "symbolic-ref").AddDashesAndList("HEAD", git.BranchPrefix+repo.DefaultBranch).Run(&git.RunOpts{Dir: repo.RepoPath()})
+ err := git.NewCommand("symbolic-ref").AddDashesAndList("HEAD", git.BranchPrefix+repo.DefaultBranch).Run(ctx, &git.RunOpts{Dir: repo.RepoPath()})
if err != nil {
logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err)
return nil
diff --git a/services/doctor/lfs.go b/services/doctor/lfs.go
index 5f110b8f97..a90f394450 100644
--- a/services/doctor/lfs.go
+++ b/services/doctor/lfs.go
@@ -5,7 +5,7 @@ package doctor
import (
"context"
- "fmt"
+ "errors"
"time"
"code.gitea.io/gitea/modules/log"
@@ -27,7 +27,7 @@ func init() {
func garbageCollectLFSCheck(ctx context.Context, logger log.Logger, autofix bool) error {
if !setting.LFS.StartServer {
- return fmt.Errorf("LFS support is disabled")
+ return errors.New("LFS support is disabled")
}
if err := repository.GarbageCollectLFSMetaObjects(ctx, repository.GarbageCollectLFSMetaObjectsOptions{
diff --git a/services/doctor/mergebase.go b/services/doctor/mergebase.go
index de460c4190..482bcd0a46 100644
--- a/services/doctor/mergebase.go
+++ b/services/doctor/mergebase.go
@@ -42,17 +42,17 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro
if !pr.HasMerged {
var err error
- pr.MergeBase, _, err = git.NewCommand(ctx, "merge-base").AddDashesAndList(pr.BaseBranch, pr.GetGitRefName()).RunStdString(&git.RunOpts{Dir: repoPath})
+ pr.MergeBase, _, err = git.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, pr.GetGitRefName()).RunStdString(ctx, &git.RunOpts{Dir: repoPath})
if err != nil {
var err2 error
- pr.MergeBase, _, err2 = git.NewCommand(ctx, "rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).RunStdString(&git.RunOpts{Dir: repoPath})
+ pr.MergeBase, _, err2 = git.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix+pr.BaseBranch).RunStdString(ctx, &git.RunOpts{Dir: repoPath})
if err2 != nil {
logger.Warn("Unable to get merge base for PR ID %d, #%d onto %s in %s/%s. Error: %v & %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err, err2)
return nil
}
}
} else {
- parentsString, _, err := git.NewCommand(ctx, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
+ parentsString, _, err := git.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(ctx, &git.RunOpts{Dir: repoPath})
if err != nil {
logger.Warn("Unable to get parents for merged PR ID %d, #%d onto %s in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err)
return nil
@@ -64,8 +64,8 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro
refs := append([]string{}, parents[1:]...)
refs = append(refs, pr.GetGitRefName())
- cmd := git.NewCommand(ctx, "merge-base").AddDashesAndList(refs...)
- pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
+ cmd := git.NewCommand("merge-base").AddDashesAndList(refs...)
+ pr.MergeBase, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath})
if err != nil {
logger.Warn("Unable to get merge base for merged PR ID %d, #%d onto %s in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err)
return nil
diff --git a/services/doctor/misc.go b/services/doctor/misc.go
index 9300c3a25c..1269d088c3 100644
--- a/services/doctor/misc.go
+++ b/services/doctor/misc.go
@@ -8,7 +8,7 @@ import (
"fmt"
"os"
"os/exec"
- "path"
+ "path/filepath"
"strings"
"code.gitea.io/gitea/models"
@@ -18,7 +18,6 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
@@ -50,14 +49,14 @@ func checkScriptType(ctx context.Context, logger log.Logger, autofix bool) error
func checkHooks(ctx context.Context, logger log.Logger, autofix bool) error {
if err := iterateRepositories(ctx, func(repo *repo_model.Repository) error {
- results, err := repository.CheckDelegateHooks(repo.RepoPath())
+ results, err := gitrepo.CheckDelegateHooks(ctx, repo)
if err != nil {
logger.Critical("Unable to check delegate hooks for repo %-v. ERROR: %v", repo, err)
return fmt.Errorf("Unable to check delegate hooks for repo %-v. ERROR: %w", repo, err)
}
if len(results) > 0 && autofix {
logger.Warn("Regenerated hooks for %s", repo.FullName())
- if err := repository.CreateDelegateHooks(repo.RepoPath()); err != nil {
+ if err := gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
logger.Critical("Unable to recreate delegate hooks for %-v. ERROR: %v", repo, err)
return fmt.Errorf("Unable to recreate delegate hooks for %-v. ERROR: %w", repo, err)
}
@@ -99,11 +98,11 @@ func checkEnablePushOptions(ctx context.Context, logger log.Logger, autofix bool
defer r.Close()
if autofix {
- _, _, err := git.NewCommand(ctx, "config", "receive.advertisePushOptions", "true").RunStdString(&git.RunOpts{Dir: r.Path})
+ _, _, err := git.NewCommand("config", "receive.advertisePushOptions", "true").RunStdString(ctx, &git.RunOpts{Dir: r.Path})
return err
}
- value, _, err := git.NewCommand(ctx, "config", "receive.advertisePushOptions").RunStdString(&git.RunOpts{Dir: r.Path})
+ value, _, err := git.NewCommand("config", "receive.advertisePushOptions").RunStdString(ctx, &git.RunOpts{Dir: r.Path})
if err != nil {
return err
}
@@ -149,7 +148,7 @@ func checkDaemonExport(ctx context.Context, logger log.Logger, autofix bool) err
}
// Create/Remove git-daemon-export-ok for git-daemon...
- daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`)
+ daemonExportFile := filepath.Join(repo.RepoPath(), `git-daemon-export-ok`)
isExist, err := util.IsExist(daemonExportFile)
if err != nil {
log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err)
@@ -197,7 +196,7 @@ func checkCommitGraph(ctx context.Context, logger log.Logger, autofix bool) erro
commitGraphExists := func() (bool, error) {
// Check commit-graph exists
- commitGraphFile := path.Join(repo.RepoPath(), `objects/info/commit-graph`)
+ commitGraphFile := filepath.Join(repo.RepoPath(), `objects/info/commit-graph`)
isExist, err := util.IsExist(commitGraphFile)
if err != nil {
logger.Error("Unable to check if %s exists. Error: %v", commitGraphFile, err)
@@ -205,7 +204,7 @@ func checkCommitGraph(ctx context.Context, logger log.Logger, autofix bool) erro
}
if !isExist {
- commitGraphsDir := path.Join(repo.RepoPath(), `objects/info/commit-graphs`)
+ commitGraphsDir := filepath.Join(repo.RepoPath(), `objects/info/commit-graphs`)
isExist, err = util.IsExist(commitGraphsDir)
if err != nil {
logger.Error("Unable to check if %s exists. Error: %v", commitGraphsDir, err)
diff --git a/services/doctor/paths.go b/services/doctor/paths.go
index 3f62d587ab..4214c36b1a 100644
--- a/services/doctor/paths.go
+++ b/services/doctor/paths.go
@@ -99,15 +99,14 @@ func checkConfigurationFiles(ctx context.Context, logger log.Logger, autofix boo
func isWritableDir(path string) error {
// There's no platform-independent way of checking if a directory is writable
// https://stackoverflow.com/questions/20026320/how-to-tell-if-folder-exists-and-is-writable
-
tmpFile, err := os.CreateTemp(path, "doctors-order")
if err != nil {
return err
}
if err := os.Remove(tmpFile.Name()); err != nil {
- fmt.Printf("Warning: can't remove temporary file: '%s'\n", tmpFile.Name()) //nolint:forbidigo
+ log.Warn("can't remove temporary file: %q", tmpFile.Name())
}
- tmpFile.Close()
+ _ = tmpFile.Close()
return nil
}
diff --git a/services/doctor/repository.go b/services/doctor/repository.go
index 6c33426636..359c4a17e0 100644
--- a/services/doctor/repository.go
+++ b/services/doctor/repository.go
@@ -7,7 +7,6 @@ import (
"context"
"code.gitea.io/gitea/models/db"
- user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/storage"
repo_service "code.gitea.io/gitea/services/repository"
@@ -39,7 +38,6 @@ func deleteOrphanedRepos(ctx context.Context) (int64, error) {
batchSize := db.MaxBatchInsertSize("repository")
e := db.GetEngine(ctx)
var deleted int64
- adminUser := &user_model.User{IsAdmin: true}
for {
select {
@@ -60,7 +58,7 @@ func deleteOrphanedRepos(ctx context.Context) (int64, error) {
}
for _, id := range ids {
- if err := repo_service.DeleteRepositoryDirectly(ctx, adminUser, id, true); err != nil {
+ if err := repo_service.DeleteRepositoryDirectly(ctx, id, true); err != nil {
return deleted, err
}
deleted++
diff --git a/services/doctor/storage.go b/services/doctor/storage.go
index 3f3b562c37..77fc6d65df 100644
--- a/services/doctor/storage.go
+++ b/services/doctor/storage.go
@@ -121,7 +121,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo
storer: storage.LFS,
isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) {
// The oid of an LFS stored object is the name but with all the path.Separators removed
- oid := strings.ReplaceAll(path, "/", "")
+ oid := strings.ReplaceAll(strings.ReplaceAll(path, "\\", ""), "/", "")
exists, err := git.ExistsLFSObject(ctx, oid)
return !exists, err
},
diff --git a/services/externalaccount/link.go b/services/externalaccount/link.go
index d6e2ea7e94..ab853140cb 100644
--- a/services/externalaccount/link.go
+++ b/services/externalaccount/link.go
@@ -5,7 +5,7 @@ package externalaccount
import (
"context"
- "fmt"
+ "errors"
user_model "code.gitea.io/gitea/models/user"
@@ -23,7 +23,7 @@ type Store interface {
func LinkAccountFromStore(ctx context.Context, store Store, user *user_model.User) error {
gothUser := store.Get("linkAccountGothUser")
if gothUser == nil {
- return fmt.Errorf("not in LinkAccount session")
+ return errors.New("not in LinkAccount session")
}
return LinkAccountToUser(ctx, user, gothUser.(goth.User))
diff --git a/services/feed/feed.go b/services/feed/feed.go
index 93bf875fd0..1dbd2e0e26 100644
--- a/services/feed/feed.go
+++ b/services/feed/feed.go
@@ -5,11 +5,158 @@ package feed
import (
"context"
+ "fmt"
+ "strings"
activities_model "code.gitea.io/gitea/models/activities"
+ "code.gitea.io/gitea/models/db"
+ access_model "code.gitea.io/gitea/models/perm/access"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
)
+func GetFeedsForDashboard(ctx context.Context, opts activities_model.GetFeedsOptions) (activities_model.ActionList, int, error) {
+ opts.DontCount = opts.RequestedTeam == nil && opts.Date == ""
+ results, cnt, err := activities_model.GetFeeds(ctx, opts)
+ return results, util.Iif(opts.DontCount, -1, int(cnt)), err
+}
+
// GetFeeds returns actions according to the provided options
func GetFeeds(ctx context.Context, opts activities_model.GetFeedsOptions) (activities_model.ActionList, int64, error) {
return activities_model.GetFeeds(ctx, opts)
}
+
+// notifyWatchers creates batch of actions for every watcher.
+// It could insert duplicate actions for a repository action, like this:
+// * Original action: UserID=1 (the real actor), ActUserID=1
+// * Organization action: UserID=100 (the repo's org), ActUserID=1
+// * Watcher action: UserID=20 (a user who is watching a repo), ActUserID=1
+func notifyWatchers(ctx context.Context, act *activities_model.Action, watchers []*repo_model.Watch, permCode, permIssue, permPR []bool) error {
+ // MySQL has TEXT length limit 65535.
+ // Sometimes the content is "field1|field2|field3", sometimes the content is JSON (ActionMirrorSyncPush, ActionCommitRepo, ActionPushTag, etc...)
+ if left, right := util.EllipsisDisplayStringX(act.Content, 65535); right != "" {
+ if strings.HasPrefix(act.Content, `{"`) && strings.HasSuffix(act.Content, `}`) {
+ // FIXME: at the moment we can do nothing if the content is JSON and it is too long
+ act.Content = "{}"
+ } else {
+ act.Content = left
+ }
+ }
+
+ // Add feed for actor.
+ act.UserID = act.ActUserID
+ if err := db.Insert(ctx, act); err != nil {
+ return fmt.Errorf("insert new actioner: %w", err)
+ }
+
+ // Add feed for organization
+ if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
+ act.ID = 0
+ act.UserID = act.Repo.Owner.ID
+ if err := db.Insert(ctx, act); err != nil {
+ return fmt.Errorf("insert new actioner: %w", err)
+ }
+ }
+
+ for i, watcher := range watchers {
+ if act.ActUserID == watcher.UserID {
+ continue
+ }
+ act.ID = 0
+ act.UserID = watcher.UserID
+ act.Repo.Units = nil
+
+ switch act.OpType {
+ case activities_model.ActionCommitRepo, activities_model.ActionPushTag, activities_model.ActionDeleteTag, activities_model.ActionPublishRelease, activities_model.ActionDeleteBranch:
+ if !permCode[i] {
+ continue
+ }
+ case activities_model.ActionCreateIssue, activities_model.ActionCommentIssue, activities_model.ActionCloseIssue, activities_model.ActionReopenIssue:
+ if !permIssue[i] {
+ continue
+ }
+ case activities_model.ActionCreatePullRequest, activities_model.ActionCommentPull, activities_model.ActionMergePullRequest, activities_model.ActionClosePullRequest, activities_model.ActionReopenPullRequest, activities_model.ActionAutoMergePullRequest:
+ if !permPR[i] {
+ continue
+ }
+ default:
+ }
+
+ if err := db.Insert(ctx, act); err != nil {
+ return fmt.Errorf("insert new action: %w", err)
+ }
+ }
+
+ return nil
+}
+
+// NotifyWatchers creates batch of actions for every watcher.
+func NotifyWatchers(ctx context.Context, acts ...*activities_model.Action) error {
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ if len(acts) == 0 {
+ return nil
+ }
+
+ repoID := acts[0].RepoID
+ if repoID == 0 {
+ setting.PanicInDevOrTesting("action should belong to a repo")
+ return nil
+ }
+ if err := acts[0].LoadRepo(ctx); err != nil {
+ return err
+ }
+ repo := acts[0].Repo
+ if err := repo.LoadOwner(ctx); err != nil {
+ return err
+ }
+
+ actUserID := acts[0].ActUserID
+
+ // Add feeds for user self and all watchers.
+ watchers, err := repo_model.GetWatchers(ctx, repoID)
+ if err != nil {
+ return fmt.Errorf("get watchers: %w", err)
+ }
+
+ permCode := make([]bool, len(watchers))
+ permIssue := make([]bool, len(watchers))
+ permPR := make([]bool, len(watchers))
+ for i, watcher := range watchers {
+ user, err := user_model.GetUserByID(ctx, watcher.UserID)
+ if err != nil {
+ permCode[i] = false
+ permIssue[i] = false
+ permPR[i] = false
+ continue
+ }
+ perm, err := access_model.GetUserRepoPermission(ctx, repo, user)
+ if err != nil {
+ permCode[i] = false
+ permIssue[i] = false
+ permPR[i] = false
+ continue
+ }
+ permCode[i] = perm.CanRead(unit.TypeCode)
+ permIssue[i] = perm.CanRead(unit.TypeIssues)
+ permPR[i] = perm.CanRead(unit.TypePullRequests)
+ }
+
+ for _, act := range acts {
+ if act.RepoID != repoID {
+ setting.PanicInDevOrTesting("action should belong to the same repo, expected[%d], got[%d] ", repoID, act.RepoID)
+ }
+ if act.ActUserID != actUserID {
+ setting.PanicInDevOrTesting("action should have the same actor, expected[%d], got[%d] ", actUserID, act.ActUserID)
+ }
+
+ act.Repo = repo
+ if err := notifyWatchers(ctx, act, watchers, permCode, permIssue, permPR); err != nil {
+ return err
+ }
+ }
+ return nil
+ })
+}
diff --git a/services/feed/feed_test.go b/services/feed/feed_test.go
index 1e4d029e18..705d42a2eb 100644
--- a/services/feed/feed_test.go
+++ b/services/feed/feed_test.go
@@ -30,7 +30,7 @@ func TestGetFeeds(t *testing.T) {
assert.NoError(t, err)
if assert.Len(t, actions, 1) {
assert.EqualValues(t, 1, actions[0].ID)
- assert.EqualValues(t, user.ID, actions[0].UserID)
+ assert.Equal(t, user.ID, actions[0].UserID)
}
assert.Equal(t, int64(1), count)
@@ -107,7 +107,7 @@ func TestGetFeeds2(t *testing.T) {
assert.Len(t, actions, 1)
if assert.Len(t, actions, 1) {
assert.EqualValues(t, 2, actions[0].ID)
- assert.EqualValues(t, org.ID, actions[0].UserID)
+ assert.Equal(t, org.ID, actions[0].UserID)
}
assert.Equal(t, int64(1), count)
@@ -147,7 +147,7 @@ func TestRepoActions(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
_ = db.TruncateBeans(db.DefaultContext, &activities_model.Action{})
- for i := 0; i < 3; i++ {
+ for i := range 3 {
_ = db.Insert(db.DefaultContext, &activities_model.Action{
UserID: 2 + int64(i),
ActUserID: 2,
@@ -163,3 +163,40 @@ func TestRepoActions(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, actions, 1)
}
+
+func TestNotifyWatchers(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ action := &activities_model.Action{
+ ActUserID: 8,
+ RepoID: 1,
+ OpType: activities_model.ActionStarRepo,
+ }
+ assert.NoError(t, NotifyWatchers(db.DefaultContext, action))
+
+ // One watchers are inactive, thus action is only created for user 8, 1, 4, 11
+ unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
+ ActUserID: action.ActUserID,
+ UserID: 8,
+ RepoID: action.RepoID,
+ OpType: action.OpType,
+ })
+ unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
+ ActUserID: action.ActUserID,
+ UserID: 1,
+ RepoID: action.RepoID,
+ OpType: action.OpType,
+ })
+ unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
+ ActUserID: action.ActUserID,
+ UserID: 4,
+ RepoID: action.RepoID,
+ OpType: action.OpType,
+ })
+ unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
+ ActUserID: action.ActUserID,
+ UserID: 11,
+ RepoID: action.RepoID,
+ OpType: action.OpType,
+ })
+}
diff --git a/services/feed/notifier.go b/services/feed/notifier.go
index 702eb5ad53..64aeccdfd2 100644
--- a/services/feed/notifier.go
+++ b/services/feed/notifier.go
@@ -49,7 +49,7 @@ func (a *actionNotifier) NewIssue(ctx context.Context, issue *issues_model.Issue
}
repo := issue.Repo
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: issue.Poster.ID,
ActUser: issue.Poster,
OpType: activities_model.ActionCreateIssue,
@@ -90,7 +90,7 @@ func (a *actionNotifier) IssueChangeStatus(ctx context.Context, doer *user_model
}
// Notify watchers for whatever action comes in, ignore if no action type.
- if err := activities_model.NotifyWatchers(ctx, act); err != nil {
+ if err := NotifyWatchers(ctx, act); err != nil {
log.Error("NotifyWatchers: %v", err)
}
}
@@ -126,7 +126,7 @@ func (a *actionNotifier) CreateIssueComment(ctx context.Context, doer *user_mode
}
// Notify watchers for whatever action comes in, ignore if no action type.
- if err := activities_model.NotifyWatchers(ctx, act); err != nil {
+ if err := NotifyWatchers(ctx, act); err != nil {
log.Error("NotifyWatchers: %v", err)
}
}
@@ -145,7 +145,7 @@ func (a *actionNotifier) NewPullRequest(ctx context.Context, pull *issues_model.
return
}
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: pull.Issue.Poster.ID,
ActUser: pull.Issue.Poster,
OpType: activities_model.ActionCreatePullRequest,
@@ -159,7 +159,7 @@ func (a *actionNotifier) NewPullRequest(ctx context.Context, pull *issues_model.
}
func (a *actionNotifier) RenameRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldRepoName string) {
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: activities_model.ActionRenameRepo,
@@ -173,7 +173,7 @@ func (a *actionNotifier) RenameRepository(ctx context.Context, doer *user_model.
}
func (a *actionNotifier) TransferRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldOwnerName string) {
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: activities_model.ActionTransferRepo,
@@ -187,7 +187,7 @@ func (a *actionNotifier) TransferRepository(ctx context.Context, doer *user_mode
}
func (a *actionNotifier) CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository) {
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: activities_model.ActionCreateRepo,
@@ -200,7 +200,7 @@ func (a *actionNotifier) CreateRepository(ctx context.Context, doer, u *user_mod
}
func (a *actionNotifier) ForkRepository(ctx context.Context, doer *user_model.User, oldRepo, repo *repo_model.Repository) {
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: activities_model.ActionCreateRepo,
@@ -265,13 +265,13 @@ func (a *actionNotifier) PullRequestReview(ctx context.Context, pr *issues_model
actions = append(actions, action)
}
- if err := activities_model.NotifyWatchersActions(ctx, actions); err != nil {
+ if err := NotifyWatchers(ctx, actions...); err != nil {
log.Error("notify watchers '%d/%d': %v", review.Reviewer.ID, review.Issue.RepoID, err)
}
}
func (*actionNotifier) MergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) {
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: activities_model.ActionMergePullRequest,
@@ -285,7 +285,7 @@ func (*actionNotifier) MergePullRequest(ctx context.Context, doer *user_model.Us
}
func (*actionNotifier) AutoMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) {
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: activities_model.ActionAutoMergePullRequest,
@@ -303,7 +303,7 @@ func (*actionNotifier) NotifyPullRevieweDismiss(ctx context.Context, doer *user_
if len(review.OriginalAuthor) > 0 {
reviewerName = review.OriginalAuthor
}
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: activities_model.ActionPullReviewDismissed,
@@ -337,7 +337,7 @@ func (a *actionNotifier) PushCommits(ctx context.Context, pusher *user_model.Use
opType = activities_model.ActionDeleteBranch
}
- if err = activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err = NotifyWatchers(ctx, &activities_model.Action{
ActUserID: pusher.ID,
ActUser: pusher,
OpType: opType,
@@ -357,7 +357,7 @@ func (a *actionNotifier) CreateRef(ctx context.Context, doer *user_model.User, r
// has sent same action in `PushCommits`, so skip it.
return
}
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: opType,
@@ -376,7 +376,7 @@ func (a *actionNotifier) DeleteRef(ctx context.Context, doer *user_model.User, r
// has sent same action in `PushCommits`, so skip it.
return
}
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: opType,
@@ -402,7 +402,7 @@ func (a *actionNotifier) SyncPushCommits(ctx context.Context, pusher *user_model
return
}
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(ctx),
OpType: activities_model.ActionMirrorSyncPush,
@@ -423,7 +423,7 @@ func (a *actionNotifier) SyncCreateRef(ctx context.Context, doer *user_model.Use
return
}
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(ctx),
OpType: activities_model.ActionMirrorSyncCreate,
@@ -443,7 +443,7 @@ func (a *actionNotifier) SyncDeleteRef(ctx context.Context, doer *user_model.Use
return
}
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(ctx),
OpType: activities_model.ActionMirrorSyncDelete,
@@ -461,7 +461,7 @@ func (a *actionNotifier) NewRelease(ctx context.Context, rel *repo_model.Release
log.Error("LoadAttributes: %v", err)
return
}
- if err := activities_model.NotifyWatchers(ctx, &activities_model.Action{
+ if err := NotifyWatchers(ctx, &activities_model.Action{
ActUserID: rel.PublisherID,
ActUser: rel.Publisher,
OpType: activities_model.ActionPublishRelease,
@@ -469,7 +469,7 @@ func (a *actionNotifier) NewRelease(ctx context.Context, rel *repo_model.Release
Repo: rel.Repo,
IsPrivate: rel.Repo.IsPrivate,
Content: rel.Title,
- RefName: rel.TagName, // FIXME: use a full ref name?
+ RefName: git.RefNameFromTag(rel.TagName).String(), // Other functions in this file all use "refFullName.String()"
}); err != nil {
log.Error("NotifyWatchers: %v", err)
}
diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go
index c9f3182b3a..a8f97572b1 100644
--- a/services/forms/auth_form.go
+++ b/services/forms/auth_form.go
@@ -14,9 +14,11 @@ import (
// AuthenticationForm form for authentication
type AuthenticationForm struct {
- ID int64
- Type int `binding:"Range(2,7)"`
- Name string `binding:"Required;MaxSize(30)"`
+ ID int64
+ Type int `binding:"Range(2,7)"`
+ Name string `binding:"Required;MaxSize(30)"`
+ TwoFactorPolicy string
+
Host string
Port int
BindDN string
@@ -74,7 +76,6 @@ type AuthenticationForm struct {
Oauth2RestrictedGroup string
Oauth2GroupTeamMap string `binding:"ValidGroupTeamMap"`
Oauth2GroupTeamMapRemoval bool
- SkipLocalTwoFA bool
SSPIAutoCreateUsers bool
SSPIAutoActivateUsers bool
SSPIStripDomainNames bool
diff --git a/services/forms/org.go b/services/forms/org.go
index db182f7e96..2ac18ef25c 100644
--- a/services/forms/org.go
+++ b/services/forms/org.go
@@ -36,7 +36,6 @@ func (f *CreateOrgForm) Validate(req *http.Request, errs binding.Errors) binding
// UpdateOrgSettingForm form for updating organization settings
type UpdateOrgSettingForm struct {
- Name string `binding:"Required;Username;MaxSize(40)" locale:"org.org_name_holder"`
FullName string `binding:"MaxSize(100)"`
Email string `binding:"MaxSize(255)"`
Description string `binding:"MaxSize(255)"`
@@ -53,6 +52,11 @@ func (f *UpdateOrgSettingForm) Validate(req *http.Request, errs binding.Errors)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
}
+type RenameOrgForm struct {
+ OrgName string `binding:"Required"`
+ NewOrgName string `binding:"Required;Username;MaxSize(40)" locale:"org.org_name_holder"`
+}
+
// ___________
// \__ ___/___ _____ _____
// | |_/ __ \\__ \ / \
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index b14171787e..cb267f891c 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -10,7 +10,6 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
project_model "code.gitea.io/gitea/models/project"
- "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/services/context"
@@ -110,12 +109,13 @@ type RepoSettingForm struct {
EnablePrune bool
// Advanced settings
- EnableCode bool
- EnableWiki bool
- EnableExternalWiki bool
- DefaultWikiBranch string
- DefaultWikiEveryoneAccess string
- ExternalWikiURL string
+ EnableCode bool
+
+ EnableWiki bool
+ EnableExternalWiki bool
+ DefaultWikiBranch string
+ ExternalWikiURL string
+
EnableIssues bool
EnableExternalTracker bool
ExternalTrackerURL string
@@ -123,28 +123,34 @@ type RepoSettingForm struct {
TrackerIssueStyle string
ExternalTrackerRegexpPattern string
EnableCloseIssuesViaCommitInAnyBranch bool
- EnableProjects bool
- ProjectsMode string
- EnableReleases bool
- EnablePackages bool
- EnablePulls bool
- EnableActions bool
- PullsIgnoreWhitespace bool
- PullsAllowMerge bool
- PullsAllowRebase bool
- PullsAllowRebaseMerge bool
- PullsAllowSquash bool
- PullsAllowFastForwardOnly bool
- PullsAllowManualMerge bool
- PullsDefaultMergeStyle string
- EnableAutodetectManualMerge bool
- PullsAllowRebaseUpdate bool
- DefaultDeleteBranchAfterMerge bool
- DefaultAllowMaintainerEdit bool
- EnableTimetracker bool
- AllowOnlyContributorsToTrackTime bool
- EnableIssueDependencies bool
- IsArchived bool
+
+ EnableProjects bool
+ ProjectsMode string
+
+ EnableReleases bool
+
+ EnablePackages bool
+
+ EnablePulls bool
+ PullsIgnoreWhitespace bool
+ PullsAllowMerge bool
+ PullsAllowRebase bool
+ PullsAllowRebaseMerge bool
+ PullsAllowSquash bool
+ PullsAllowFastForwardOnly bool
+ PullsAllowManualMerge bool
+ PullsDefaultMergeStyle string
+ EnableAutodetectManualMerge bool
+ PullsAllowRebaseUpdate bool
+ DefaultDeleteBranchAfterMerge bool
+ DefaultAllowMaintainerEdit bool
+ EnableTimetracker bool
+ AllowOnlyContributorsToTrackTime bool
+ EnableIssueDependencies bool
+
+ EnableActions bool
+
+ IsArchived bool
// Signing Settings
TrustModel string
@@ -160,13 +166,6 @@ func (f *RepoSettingForm) Validate(req *http.Request, errs binding.Errors) bindi
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
}
-// __________ .__
-// \______ \____________ ____ ____ | |__
-// | | _/\_ __ \__ \ / \_/ ___\| | \
-// | | \ | | \// __ \| | \ \___| Y \
-// |______ / |__| (____ /___| /\___ >___| /
-// \/ \/ \/ \/ \/
-
// ProtectBranchForm form for changing protected branch settings
type ProtectBranchForm struct {
RuleName string `binding:"Required"`
@@ -209,26 +208,18 @@ type ProtectBranchPriorityForm struct {
IDs []int64
}
-// __ __ ___. .__ __
-// / \ / \ ____\_ |__ | |__ ____ ____ | | __
-// \ \/\/ // __ \| __ \| | \ / _ \ / _ \| |/ /
-// \ /\ ___/| \_\ \ Y ( <_> | <_> ) <
-// \__/\ / \___ >___ /___| /\____/ \____/|__|_ \
-// \/ \/ \/ \/ \/
-
// WebhookForm form for changing web hook
type WebhookForm struct {
Events string
Create bool
Delete bool
Fork bool
+ Push bool
Issues bool
IssueAssign bool
IssueLabel bool
IssueMilestone bool
IssueComment bool
- Release bool
- Push bool
PullRequest bool
PullRequestAssign bool
PullRequestLabel bool
@@ -239,10 +230,15 @@ type WebhookForm struct {
PullRequestReviewRequest bool
Wiki bool
Repository bool
+ Release bool
Package bool
+ Status bool
+ WorkflowRun bool
+ WorkflowJob bool
Active bool
BranchFilter string `binding:"GlobPattern"`
AuthorizationHeader string
+ Secret string
}
// PushOnly if the hook will be triggered when push
@@ -265,7 +261,6 @@ type NewWebhookForm struct {
PayloadURL string `binding:"Required;ValidUrl"`
HTTPMethod string `binding:"Required;In(POST,GET)"`
ContentType int `binding:"Required"`
- Secret string
WebhookForm
}
@@ -279,7 +274,6 @@ func (f *NewWebhookForm) Validate(req *http.Request, errs binding.Errors) bindin
type NewGogshookForm struct {
PayloadURL string `binding:"Required;ValidUrl"`
ContentType int `binding:"Required"`
- Secret string
WebhookForm
}
@@ -478,22 +472,6 @@ func (i *IssueLockForm) Validate(req *http.Request, errs binding.Errors) binding
return middleware.Validate(errs, ctx.Data, i, ctx.Locale)
}
-// HasValidReason checks to make sure that the reason submitted in
-// the form matches any of the values in the config
-func (i IssueLockForm) HasValidReason() bool {
- if strings.TrimSpace(i.Reason) == "" {
- return true
- }
-
- for _, v := range setting.Repository.Issue.LockReasons {
- if v == i.Reason {
- return true
- }
- }
-
- return false
-}
-
// CreateProjectForm form for creating a project
type CreateProjectForm struct {
Title string `binding:"Required;MaxSize(100)"`
@@ -524,12 +502,13 @@ func (f *CreateMilestoneForm) Validate(req *http.Request, errs binding.Errors) b
// CreateLabelForm form for creating label
type CreateLabelForm struct {
- ID int64
- Title string `binding:"Required;MaxSize(50)" locale:"repo.issues.label_title"`
- Exclusive bool `form:"exclusive"`
- IsArchived bool `form:"is_archived"`
- Description string `binding:"MaxSize(200)" locale:"repo.issues.label_description"`
- Color string `binding:"Required;MaxSize(7)" locale:"repo.issues.label_color"`
+ ID int64
+ Title string `binding:"Required;MaxSize(50)" locale:"repo.issues.label_title"`
+ Exclusive bool `form:"exclusive"`
+ ExclusiveOrder int `form:"exclusive_order"`
+ IsArchived bool `form:"is_archived"`
+ Description string `binding:"MaxSize(200)" locale:"repo.issues.label_description"`
+ Color string `binding:"Required;MaxSize(7)" locale:"repo.issues.label_color"`
}
// Validate validates the fields
@@ -651,8 +630,8 @@ type NewReleaseForm struct {
Target string `form:"tag_target" binding:"Required;MaxSize(255)"`
Title string `binding:"MaxSize(255)"`
Content string
- Draft string
- TagOnly string
+ Draft bool
+ TagOnly bool
Prerelease bool
AddTagMsg bool
Files []string
@@ -700,125 +679,6 @@ func (f *NewWikiForm) Validate(req *http.Request, errs binding.Errors) binding.E
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
}
-// ___________ .___.__ __
-// \_ _____/ __| _/|__|/ |_
-// | __)_ / __ | | \ __\
-// | \/ /_/ | | || |
-// /_______ /\____ | |__||__|
-// \/ \/
-
-// EditRepoFileForm form for changing repository file
-type EditRepoFileForm struct {
- TreePath string `binding:"Required;MaxSize(500)"`
- Content string
- CommitSummary string `binding:"MaxSize(100)"`
- CommitMessage string
- CommitChoice string `binding:"Required;MaxSize(50)"`
- NewBranchName string `binding:"GitRefName;MaxSize(100)"`
- LastCommit string
- Signoff bool
-}
-
-// Validate validates the fields
-func (f *EditRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
- ctx := context.GetValidateContext(req)
- return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
-}
-
-// EditPreviewDiffForm form for changing preview diff
-type EditPreviewDiffForm struct {
- Content string
-}
-
-// Validate validates the fields
-func (f *EditPreviewDiffForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
- ctx := context.GetValidateContext(req)
- return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
-}
-
-// _________ .__ __________.__ __
-// \_ ___ \| |__ __________________ ___.__. \______ \__| ____ | | __
-// / \ \/| | \_/ __ \_ __ \_ __ < | | | ___/ |/ ___\| |/ /
-// \ \___| Y \ ___/| | \/| | \/\___ | | | | \ \___| <
-// \______ /___| /\___ >__| |__| / ____| |____| |__|\___ >__|_ \
-// \/ \/ \/ \/ \/ \/
-
-// CherryPickForm form for changing repository file
-type CherryPickForm struct {
- CommitSummary string `binding:"MaxSize(100)"`
- CommitMessage string
- CommitChoice string `binding:"Required;MaxSize(50)"`
- NewBranchName string `binding:"GitRefName;MaxSize(100)"`
- LastCommit string
- Revert bool
- Signoff bool
-}
-
-// Validate validates the fields
-func (f *CherryPickForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
- ctx := context.GetValidateContext(req)
- return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
-}
-
-// ____ ___ .__ .___
-// | | \______ | | _________ __| _/
-// | | /\____ \| | / _ \__ \ / __ |
-// | | / | |_> > |_( <_> ) __ \_/ /_/ |
-// |______/ | __/|____/\____(____ /\____ |
-// |__| \/ \/
-//
-
-// UploadRepoFileForm form for uploading repository file
-type UploadRepoFileForm struct {
- TreePath string `binding:"MaxSize(500)"`
- CommitSummary string `binding:"MaxSize(100)"`
- CommitMessage string
- CommitChoice string `binding:"Required;MaxSize(50)"`
- NewBranchName string `binding:"GitRefName;MaxSize(100)"`
- Files []string
- Signoff bool
-}
-
-// Validate validates the fields
-func (f *UploadRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
- ctx := context.GetValidateContext(req)
- return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
-}
-
-// RemoveUploadFileForm form for removing uploaded file
-type RemoveUploadFileForm struct {
- File string `binding:"Required;MaxSize(50)"`
-}
-
-// Validate validates the fields
-func (f *RemoveUploadFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
- ctx := context.GetValidateContext(req)
- return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
-}
-
-// ________ .__ __
-// \______ \ ____ | | _____/ |_ ____
-// | | \_/ __ \| | _/ __ \ __\/ __ \
-// | ` \ ___/| |_\ ___/| | \ ___/
-// /_______ /\___ >____/\___ >__| \___ >
-// \/ \/ \/ \/
-
-// DeleteRepoFileForm form for deleting repository file
-type DeleteRepoFileForm struct {
- CommitSummary string `binding:"MaxSize(100)"`
- CommitMessage string
- CommitChoice string `binding:"Required;MaxSize(50)"`
- NewBranchName string `binding:"GitRefName;MaxSize(100)"`
- LastCommit string
- Signoff bool
-}
-
-// Validate validates the fields
-func (f *DeleteRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
- ctx := context.GetValidateContext(req)
- return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
-}
-
// ___________.__ ___________ __
// \__ ___/|__| _____ ____ \__ ___/___________ ____ | | __ ___________
// | | | |/ \_/ __ \ | | \_ __ \__ \ _/ ___\| |/ // __ \_ __ \
diff --git a/services/forms/repo_form_editor.go b/services/forms/repo_form_editor.go
new file mode 100644
index 0000000000..3ad2eae75d
--- /dev/null
+++ b/services/forms/repo_form_editor.go
@@ -0,0 +1,57 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package forms
+
+import (
+ "net/http"
+
+ "code.gitea.io/gitea/modules/optional"
+ "code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/services/context"
+
+ "gitea.com/go-chi/binding"
+)
+
+type CommitCommonForm struct {
+ TreePath string `binding:"MaxSize(500)"`
+ CommitSummary string `binding:"MaxSize(100)"`
+ CommitMessage string
+ CommitChoice string `binding:"Required;MaxSize(50)"`
+ NewBranchName string `binding:"GitRefName;MaxSize(100)"`
+ LastCommit string
+ Signoff bool
+ CommitEmail string
+}
+
+func (f *CommitCommonForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
+ ctx := context.GetValidateContext(req)
+ return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
+}
+
+type CommitCommonFormInterface interface {
+ GetCommitCommonForm() *CommitCommonForm
+}
+
+func (f *CommitCommonForm) GetCommitCommonForm() *CommitCommonForm {
+ return f
+}
+
+type EditRepoFileForm struct {
+ CommitCommonForm
+ Content optional.Option[string]
+}
+
+type DeleteRepoFileForm struct {
+ CommitCommonForm
+}
+
+type UploadRepoFileForm struct {
+ CommitCommonForm
+ Files []string
+}
+
+type CherryPickForm struct {
+ CommitCommonForm
+ Revert bool
+}
diff --git a/services/forms/repo_form_test.go b/services/forms/repo_form_test.go
index 2c5a8e2c0f..a0c67fe0f8 100644
--- a/services/forms/repo_form_test.go
+++ b/services/forms/repo_form_test.go
@@ -6,8 +6,6 @@ package forms
import (
"testing"
- "code.gitea.io/gitea/modules/setting"
-
"github.com/stretchr/testify/assert"
)
@@ -39,26 +37,3 @@ func TestSubmitReviewForm_IsEmpty(t *testing.T) {
assert.Equal(t, v.expected, v.form.HasEmptyContent())
}
}
-
-func TestIssueLock_HasValidReason(t *testing.T) {
- // Init settings
- _ = setting.Repository
-
- cases := []struct {
- form IssueLockForm
- expected bool
- }{
- {IssueLockForm{""}, true}, // an empty reason is accepted
- {IssueLockForm{"Off-topic"}, true},
- {IssueLockForm{"Too heated"}, true},
- {IssueLockForm{"Spam"}, true},
- {IssueLockForm{"Resolved"}, true},
-
- {IssueLockForm{"ZZZZ"}, false},
- {IssueLockForm{"I want to lock this issue"}, false},
- }
-
- for _, v := range cases {
- assert.Equal(t, v.expected, v.form.HasValidReason())
- }
-}
diff --git a/services/forms/user_form.go b/services/forms/user_form.go
index ed79936add..ddf2bd09b0 100644
--- a/services/forms/user_form.go
+++ b/services/forms/user_form.go
@@ -7,9 +7,7 @@ package forms
import (
"mime/multipart"
"net/http"
- "strings"
- auth_model "code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web/middleware"
@@ -325,8 +323,9 @@ func (f *AddKeyForm) Validate(req *http.Request, errs binding.Errors) binding.Er
// AddSecretForm for adding secrets
type AddSecretForm struct {
- Name string `binding:"Required;MaxSize(255)"`
- Data string `binding:"Required;MaxSize(65535)"`
+ Name string `binding:"Required;MaxSize(255)"`
+ Data string `binding:"Required;MaxSize(65535)"`
+ Description string `binding:"MaxSize(65535)"`
}
// Validate validates the fields
@@ -336,8 +335,9 @@ func (f *AddSecretForm) Validate(req *http.Request, errs binding.Errors) binding
}
type EditVariableForm struct {
- Name string `binding:"Required;MaxSize(255)"`
- Data string `binding:"Required;MaxSize(65535)"`
+ Name string `binding:"Required;MaxSize(255)"`
+ Data string `binding:"Required;MaxSize(65535)"`
+ Description string `binding:"MaxSize(65535)"`
}
func (f *EditVariableForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
@@ -347,8 +347,7 @@ func (f *EditVariableForm) Validate(req *http.Request, errs binding.Errors) bind
// NewAccessTokenForm form for creating access token
type NewAccessTokenForm struct {
- Name string `binding:"Required;MaxSize(255)" locale:"settings.token_name"`
- Scope []string
+ Name string `binding:"Required;MaxSize(255)" locale:"settings.token_name"`
}
// Validate validates the fields
@@ -357,12 +356,6 @@ func (f *NewAccessTokenForm) Validate(req *http.Request, errs binding.Errors) bi
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
}
-func (f *NewAccessTokenForm) GetScope() (auth_model.AccessTokenScope, error) {
- scope := strings.Join(f.Scope, ",")
- s, err := auth_model.AccessTokenScope(scope).Normalize()
- return s, err
-}
-
// EditOAuth2ApplicationForm form for editing oauth2 applications
type EditOAuth2ApplicationForm struct {
Name string `binding:"Required;MaxSize(255)" form:"application_name"`
diff --git a/services/forms/user_form_test.go b/services/forms/user_form_test.go
index 66050187c9..09e9ec0f65 100644
--- a/services/forms/user_form_test.go
+++ b/services/forms/user_form_test.go
@@ -4,11 +4,10 @@
package forms
import (
- "strconv"
"testing"
- auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
"github.com/gobwas/glob"
"github.com/stretchr/testify/assert"
@@ -28,12 +27,7 @@ func TestRegisterForm_IsDomainAllowed_Empty(t *testing.T) {
}
func TestRegisterForm_IsDomainAllowed_InvalidEmail(t *testing.T) {
- oldService := setting.Service
- defer func() {
- setting.Service = oldService
- }()
-
- setting.Service.EmailDomainAllowList = []glob.Glob{glob.MustCompile("gitea.io")}
+ defer test.MockVariableValue(&setting.Service.EmailDomainAllowList, []glob.Glob{glob.MustCompile("gitea.io")})()
tt := []struct {
email string
@@ -50,12 +44,7 @@ func TestRegisterForm_IsDomainAllowed_InvalidEmail(t *testing.T) {
}
func TestRegisterForm_IsDomainAllowed_AllowedEmail(t *testing.T) {
- oldService := setting.Service
- defer func() {
- setting.Service = oldService
- }()
-
- setting.Service.EmailDomainAllowList = []glob.Glob{glob.MustCompile("gitea.io"), glob.MustCompile("*.allow")}
+ defer test.MockVariableValue(&setting.Service.EmailDomainAllowList, []glob.Glob{glob.MustCompile("gitea.io"), glob.MustCompile("*.allow")})()
tt := []struct {
email string
@@ -78,13 +67,7 @@ func TestRegisterForm_IsDomainAllowed_AllowedEmail(t *testing.T) {
}
func TestRegisterForm_IsDomainAllowed_BlockedEmail(t *testing.T) {
- oldService := setting.Service
- defer func() {
- setting.Service = oldService
- }()
-
- setting.Service.EmailDomainAllowList = nil
- setting.Service.EmailDomainBlockList = []glob.Glob{glob.MustCompile("gitea.io"), glob.MustCompile("*.block")}
+ defer test.MockVariableValue(&setting.Service.EmailDomainBlockList, []glob.Glob{glob.MustCompile("gitea.io"), glob.MustCompile("*.block")})()
tt := []struct {
email string
@@ -104,28 +87,3 @@ func TestRegisterForm_IsDomainAllowed_BlockedEmail(t *testing.T) {
assert.Equal(t, v.valid, form.IsEmailDomainAllowed())
}
}
-
-func TestNewAccessTokenForm_GetScope(t *testing.T) {
- tests := []struct {
- form NewAccessTokenForm
- scope auth_model.AccessTokenScope
- expectedErr error
- }{
- {
- form: NewAccessTokenForm{Name: "test", Scope: []string{"read:repository"}},
- scope: "read:repository",
- },
- {
- form: NewAccessTokenForm{Name: "test", Scope: []string{"read:repository", "write:user"}},
- scope: "read:repository,write:user",
- },
- }
-
- for i, test := range tests {
- t.Run(strconv.Itoa(i), func(t *testing.T) {
- scope, err := test.form.GetScope()
- assert.Equal(t, test.expectedErr, err)
- assert.Equal(t, test.scope, scope)
- })
- }
-}
diff --git a/services/git/commit.go b/services/git/commit.go
new file mode 100644
index 0000000000..2e0e8a5096
--- /dev/null
+++ b/services/git/commit.go
@@ -0,0 +1,97 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package git
+
+import (
+ "context"
+
+ asymkey_model "code.gitea.io/gitea/models/asymkey"
+ "code.gitea.io/gitea/models/db"
+ git_model "code.gitea.io/gitea/models/git"
+ repo_model "code.gitea.io/gitea/models/repo"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/container"
+ "code.gitea.io/gitea/modules/git"
+ asymkey_service "code.gitea.io/gitea/services/asymkey"
+)
+
+// ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys.
+func ParseCommitsWithSignature(ctx context.Context, repo *repo_model.Repository, oldCommits []*user_model.UserCommit, repoTrustModel repo_model.TrustModelType) ([]*asymkey_model.SignCommit, error) {
+ newCommits := make([]*asymkey_model.SignCommit, 0, len(oldCommits))
+ keyMap := map[string]bool{}
+
+ emails := make(container.Set[string])
+ for _, c := range oldCommits {
+ if c.Committer != nil {
+ emails.Add(c.Committer.Email)
+ }
+ }
+
+ emailUsers, err := user_model.GetUsersByEmails(ctx, emails.Values())
+ if err != nil {
+ return nil, err
+ }
+
+ for _, c := range oldCommits {
+ committerUser := emailUsers.GetByEmail(c.Committer.Email) // FIXME: why ValidateCommitsWithEmails uses "Author", but ParseCommitsWithSignature uses "Committer"?
+ if committerUser == nil {
+ committerUser = &user_model.User{
+ Name: c.Committer.Name,
+ Email: c.Committer.Email,
+ }
+ }
+
+ signCommit := &asymkey_model.SignCommit{
+ UserCommit: c,
+ Verification: asymkey_service.ParseCommitWithSignatureCommitter(ctx, c.Commit, committerUser),
+ }
+
+ isOwnerMemberCollaborator := func(user *user_model.User) (bool, error) {
+ return repo_model.IsOwnerMemberCollaborator(ctx, repo, user.ID)
+ }
+
+ _ = asymkey_model.CalculateTrustStatus(signCommit.Verification, repoTrustModel, isOwnerMemberCollaborator, &keyMap)
+
+ newCommits = append(newCommits, signCommit)
+ }
+ return newCommits, nil
+}
+
+// ConvertFromGitCommit converts git commits into SignCommitWithStatuses
+func ConvertFromGitCommit(ctx context.Context, commits []*git.Commit, repo *repo_model.Repository) ([]*git_model.SignCommitWithStatuses, error) {
+ validatedCommits, err := user_model.ValidateCommitsWithEmails(ctx, commits)
+ if err != nil {
+ return nil, err
+ }
+ signedCommits, err := ParseCommitsWithSignature(
+ ctx,
+ repo,
+ validatedCommits,
+ repo.GetTrustModel(),
+ )
+ if err != nil {
+ return nil, err
+ }
+ return ParseCommitsWithStatus(ctx, signedCommits, repo)
+}
+
+// ParseCommitsWithStatus checks commits latest statuses and calculates its worst status state
+func ParseCommitsWithStatus(ctx context.Context, oldCommits []*asymkey_model.SignCommit, repo *repo_model.Repository) ([]*git_model.SignCommitWithStatuses, error) {
+ newCommits := make([]*git_model.SignCommitWithStatuses, 0, len(oldCommits))
+
+ for _, c := range oldCommits {
+ commit := &git_model.SignCommitWithStatuses{
+ SignCommit: c,
+ }
+ statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, commit.ID.String(), db.ListOptionsAll)
+ if err != nil {
+ return nil, err
+ }
+
+ commit.Statuses = statuses
+ commit.Status = git_model.CalcCommitStatus(statuses)
+ newCommits = append(newCommits, commit)
+ }
+ return newCommits, nil
+}
diff --git a/services/gitdiff/csv.go b/services/gitdiff/csv.go
index 8db73c56a3..c10ee14490 100644
--- a/services/gitdiff/csv.go
+++ b/services/gitdiff/csv.go
@@ -134,7 +134,7 @@ func createCsvDiffSingle(reader *csv.Reader, celltype TableDiffCellType) ([]*Tab
return nil, err
}
cells := make([]*TableDiffCell, len(row))
- for j := 0; j < len(row); j++ {
+ for j := range row {
if celltype == TableDiffCellDel {
cells[j] = &TableDiffCell{LeftCell: row[j], Type: celltype}
} else {
@@ -365,11 +365,11 @@ func getColumnMapping(baseCSVReader, headCSVReader *csvReader) ([]int, []int) {
}
// Loops through the baseRow and see if there is a match in the head row
- for i := 0; i < len(baseRow); i++ {
+ for i := range baseRow {
base2HeadColMap[i] = unmappedColumn
baseCell, err := getCell(baseRow, i)
if err == nil {
- for j := 0; j < len(headRow); j++ {
+ for j := range headRow {
if head2BaseColMap[j] == -1 {
headCell, err := getCell(headRow, j)
if err == nil && baseCell == headCell {
@@ -390,7 +390,7 @@ func getColumnMapping(baseCSVReader, headCSVReader *csvReader) ([]int, []int) {
// tryMapColumnsByContent tries to map missing columns by the content of the first lines.
func tryMapColumnsByContent(baseCSVReader *csvReader, base2HeadColMap []int, headCSVReader *csvReader, head2BaseColMap []int) {
- for i := 0; i < len(base2HeadColMap); i++ {
+ for i := range base2HeadColMap {
headStart := 0
for base2HeadColMap[i] == unmappedColumn && headStart < len(head2BaseColMap) {
if head2BaseColMap[headStart] == unmappedColumn {
@@ -424,7 +424,7 @@ func getCell(row []string, column int) (string, error) {
// countUnmappedColumns returns the count of unmapped columns.
func countUnmappedColumns(mapping []int) int {
count := 0
- for i := 0; i < len(mapping); i++ {
+ for i := range mapping {
if mapping[i] == unmappedColumn {
count++
}
diff --git a/services/gitdiff/csv_test.go b/services/gitdiff/csv_test.go
index c006a7c2bd..f91e0e9aa7 100644
--- a/services/gitdiff/csv_test.go
+++ b/services/gitdiff/csv_test.go
@@ -192,23 +192,18 @@ c,d,e`,
for n, c := range cases {
diff, err := ParsePatch(db.DefaultContext, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(c.diff), "")
- if err != nil {
- t.Errorf("ParsePatch failed: %s", err)
- }
+ assert.NoError(t, err)
var baseReader *csv.Reader
if len(c.base) > 0 {
baseReader, err = csv_module.CreateReaderAndDetermineDelimiter(nil, strings.NewReader(c.base))
- if err != nil {
- t.Errorf("CreateReaderAndDetermineDelimiter failed: %s", err)
- }
+ assert.NoError(t, err)
}
+
var headReader *csv.Reader
if len(c.head) > 0 {
headReader, err = csv_module.CreateReaderAndDetermineDelimiter(nil, strings.NewReader(c.head))
- if err != nil {
- t.Errorf("CreateReaderAndDetermineDelimiter failed: %s", err)
- }
+ assert.NoError(t, err)
}
result, err := CreateCsvDiff(diff.Files[0], baseReader, headReader)
diff --git a/services/gitdiff/git_diff_tree.go b/services/gitdiff/git_diff_tree.go
new file mode 100644
index 0000000000..ed94bfbfe4
--- /dev/null
+++ b/services/gitdiff/git_diff_tree.go
@@ -0,0 +1,250 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package gitdiff
+
+import (
+ "bufio"
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/log"
+)
+
+type DiffTree struct {
+ Files []*DiffTreeRecord
+}
+
+type DiffTreeRecord struct {
+ // Status is one of 'added', 'deleted', 'modified', 'renamed', 'copied', 'typechanged', 'unmerged', 'unknown'
+ Status string
+
+ // For renames and copies, the percentage of similarity between the source and target of the move/rename.
+ Score uint8
+
+ HeadPath string
+ BasePath string
+ HeadMode git.EntryMode
+ BaseMode git.EntryMode
+ HeadBlobID string
+ BaseBlobID string
+}
+
+// GetDiffTree returns the list of path of the files that have changed between the two commits.
+// If useMergeBase is true, the diff will be calculated using the merge base of the two commits.
+// This is the same behavior as using a three-dot diff in git diff.
+func GetDiffTree(ctx context.Context, gitRepo *git.Repository, useMergeBase bool, baseSha, headSha string) (*DiffTree, error) {
+ gitDiffTreeRecords, err := runGitDiffTree(ctx, gitRepo, useMergeBase, baseSha, headSha)
+ if err != nil {
+ return nil, err
+ }
+
+ return &DiffTree{
+ Files: gitDiffTreeRecords,
+ }, nil
+}
+
+func runGitDiffTree(ctx context.Context, gitRepo *git.Repository, useMergeBase bool, baseSha, headSha string) ([]*DiffTreeRecord, error) {
+ useMergeBase, baseCommitID, headCommitID, err := validateGitDiffTreeArguments(gitRepo, useMergeBase, baseSha, headSha)
+ if err != nil {
+ return nil, err
+ }
+
+ cmd := git.NewCommand("diff-tree", "--raw", "-r", "--find-renames", "--root")
+ if useMergeBase {
+ cmd.AddArguments("--merge-base")
+ }
+ cmd.AddDynamicArguments(baseCommitID, headCommitID)
+ stdout, _, runErr := cmd.RunStdString(ctx, &git.RunOpts{Dir: gitRepo.Path})
+ if runErr != nil {
+ log.Warn("git diff-tree: %v", runErr)
+ return nil, runErr
+ }
+
+ return parseGitDiffTree(strings.NewReader(stdout))
+}
+
+func validateGitDiffTreeArguments(gitRepo *git.Repository, useMergeBase bool, baseSha, headSha string) (shouldUseMergeBase bool, resolvedBaseSha, resolvedHeadSha string, err error) {
+ // if the head is empty its an error
+ if headSha == "" {
+ return false, "", "", errors.New("headSha is empty")
+ }
+
+ // if the head commit doesn't exist its and error
+ headCommit, err := gitRepo.GetCommit(headSha)
+ if err != nil {
+ return false, "", "", fmt.Errorf("failed to get commit headSha: %v", err)
+ }
+ headCommitID := headCommit.ID.String()
+
+ // if the base is empty we should use the parent of the head commit
+ if baseSha == "" {
+ // if the headCommit has no parent we should use an empty commit
+ // this can happen when we are generating a diff against an orphaned commit
+ if headCommit.ParentCount() == 0 {
+ objectFormat, err := gitRepo.GetObjectFormat()
+ if err != nil {
+ return false, "", "", err
+ }
+
+ // We set use merge base to false because we have no base commit
+ return false, objectFormat.EmptyTree().String(), headCommitID, nil
+ }
+
+ baseCommit, err := headCommit.Parent(0)
+ if err != nil {
+ return false, "", "", fmt.Errorf("baseSha is '', attempted to use parent of commit %s, got error: %v", headCommit.ID.String(), err)
+ }
+ return useMergeBase, baseCommit.ID.String(), headCommitID, nil
+ }
+
+ // try and get the base commit
+ baseCommit, err := gitRepo.GetCommit(baseSha)
+ // propagate the error if we couldn't get the base commit
+ if err != nil {
+ return useMergeBase, "", "", fmt.Errorf("failed to get base commit %s: %v", baseSha, err)
+ }
+
+ return useMergeBase, baseCommit.ID.String(), headCommit.ID.String(), nil
+}
+
+func parseGitDiffTree(gitOutput io.Reader) ([]*DiffTreeRecord, error) {
+ /*
+ The output of `git diff-tree --raw -r --find-renames` is of the form:
+
+ :<old_mode> <new_mode> <old_sha> <new_sha> <status>\t<path>
+
+ or for renames:
+
+ :<old_mode> <new_mode> <old_sha> <new_sha> <status>\t<old_path>\t<new_path>
+
+ See: <https://git-scm.com/docs/git-diff-tree#_raw_output_format> for more details
+ */
+ results := make([]*DiffTreeRecord, 0)
+
+ lines := bufio.NewScanner(gitOutput)
+ for lines.Scan() {
+ line := lines.Text()
+
+ if len(line) == 0 {
+ continue
+ }
+
+ record, err := parseGitDiffTreeLine(line)
+ if err != nil {
+ return nil, err
+ }
+
+ results = append(results, record)
+ }
+
+ if err := lines.Err(); err != nil {
+ return nil, err
+ }
+
+ return results, nil
+}
+
+func parseGitDiffTreeLine(line string) (*DiffTreeRecord, error) {
+ line = strings.TrimPrefix(line, ":")
+ splitSections := strings.SplitN(line, "\t", 2)
+ if len(splitSections) < 2 {
+ return nil, fmt.Errorf("unparsable output for diff-tree --raw: `%s`)", line)
+ }
+
+ fields := strings.Fields(splitSections[0])
+ if len(fields) < 5 {
+ return nil, fmt.Errorf("unparsable output for diff-tree --raw: `%s`, expected 5 space delimited values got %d)", line, len(fields))
+ }
+
+ baseMode, err := git.ParseEntryMode(fields[0])
+ if err != nil {
+ return nil, err
+ }
+
+ headMode, err := git.ParseEntryMode(fields[1])
+ if err != nil {
+ return nil, err
+ }
+
+ baseBlobID := fields[2]
+ headBlobID := fields[3]
+
+ status, score, err := statusFromLetter(fields[4])
+ if err != nil {
+ return nil, fmt.Errorf("unparsable output for diff-tree --raw: %s, error: %s", line, err)
+ }
+
+ filePaths := strings.Split(splitSections[1], "\t")
+
+ var headPath, basePath string
+ if status == "renamed" {
+ if len(filePaths) != 2 {
+ return nil, fmt.Errorf("unparsable output for diff-tree --raw: `%s`, expected 2 paths found %d", line, len(filePaths))
+ }
+ basePath = filePaths[0]
+ headPath = filePaths[1]
+ } else {
+ basePath = filePaths[0]
+ headPath = filePaths[0]
+ }
+
+ return &DiffTreeRecord{
+ Status: status,
+ Score: score,
+ BaseMode: baseMode,
+ HeadMode: headMode,
+ BaseBlobID: baseBlobID,
+ HeadBlobID: headBlobID,
+ BasePath: basePath,
+ HeadPath: headPath,
+ }, nil
+}
+
+func statusFromLetter(rawStatus string) (status string, score uint8, err error) {
+ if len(rawStatus) < 1 {
+ return "", 0, errors.New("empty status letter")
+ }
+ switch rawStatus[0] {
+ case 'A':
+ return "added", 0, nil
+ case 'D':
+ return "deleted", 0, nil
+ case 'M':
+ return "modified", 0, nil
+ case 'R':
+ score, err = tryParseStatusScore(rawStatus)
+ return "renamed", score, err
+ case 'C':
+ score, err = tryParseStatusScore(rawStatus)
+ return "copied", score, err
+ case 'T':
+ return "typechanged", 0, nil
+ case 'U':
+ return "unmerged", 0, nil
+ case 'X':
+ return "unknown", 0, nil
+ default:
+ return "", 0, fmt.Errorf("unknown status letter: '%s'", rawStatus)
+ }
+}
+
+func tryParseStatusScore(rawStatus string) (uint8, error) {
+ if len(rawStatus) < 2 {
+ return 0, errors.New("status score missing")
+ }
+
+ score, err := strconv.ParseUint(rawStatus[1:], 10, 8)
+ if err != nil {
+ return 0, fmt.Errorf("failed to parse status score: %w", err)
+ } else if score > 100 {
+ return 0, fmt.Errorf("status score out of range: %d", score)
+ }
+
+ return uint8(score), nil
+}
diff --git a/services/gitdiff/git_diff_tree_test.go b/services/gitdiff/git_diff_tree_test.go
new file mode 100644
index 0000000000..313d279e95
--- /dev/null
+++ b/services/gitdiff/git_diff_tree_test.go
@@ -0,0 +1,427 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package gitdiff
+
+import (
+ "strings"
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/git"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestGitDiffTree(t *testing.T) {
+ test := []struct {
+ Name string
+ RepoPath string
+ BaseSha string
+ HeadSha string
+ useMergeBase bool
+ Expected *DiffTree
+ }{
+ {
+ Name: "happy path",
+ RepoPath: "../../modules/git/tests/repos/repo5_pulls",
+ BaseSha: "72866af952e98d02a73003501836074b286a78f6",
+ HeadSha: "d8e0bbb45f200e67d9a784ce55bd90821af45ebd",
+ Expected: &DiffTree{
+ Files: []*DiffTreeRecord{
+ {
+ Status: "modified",
+ HeadPath: "LICENSE",
+ BasePath: "LICENSE",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeBlob,
+ HeadBlobID: "ee469963e76ae1bb7ee83d7510df2864e6c8c640",
+ BaseBlobID: "c996f4725be8fc8c1d1c776e58c97ddc5d03b336",
+ },
+ {
+ Status: "modified",
+ HeadPath: "README.md",
+ BasePath: "README.md",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeBlob,
+ HeadBlobID: "9dfc0a6257d8eff526f0cfaf6a8ea950f55a9dba",
+ BaseBlobID: "074e590b8e64898b02beef03ece83f962c94f54c",
+ },
+ },
+ },
+ },
+ {
+ Name: "first commit (no parent)",
+ RepoPath: "../../modules/git/tests/repos/repo5_pulls",
+ HeadSha: "72866af952e98d02a73003501836074b286a78f6",
+ Expected: &DiffTree{
+ Files: []*DiffTreeRecord{
+ {
+ Status: "added",
+ HeadPath: ".gitignore",
+ BasePath: ".gitignore",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeNoEntry,
+ HeadBlobID: "f1c181ec9c5c921245027c6b452ecfc1d3626364",
+ BaseBlobID: "0000000000000000000000000000000000000000",
+ },
+ {
+ Status: "added",
+ HeadPath: "LICENSE",
+ BasePath: "LICENSE",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeNoEntry,
+ HeadBlobID: "c996f4725be8fc8c1d1c776e58c97ddc5d03b336",
+ BaseBlobID: "0000000000000000000000000000000000000000",
+ },
+ {
+ Status: "added",
+ HeadPath: "README.md",
+ BasePath: "README.md",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeNoEntry,
+ HeadBlobID: "074e590b8e64898b02beef03ece83f962c94f54c",
+ BaseBlobID: "0000000000000000000000000000000000000000",
+ },
+ },
+ },
+ },
+ {
+ Name: "first commit (no parent), merge base = true",
+ RepoPath: "../../modules/git/tests/repos/repo5_pulls",
+ HeadSha: "72866af952e98d02a73003501836074b286a78f6",
+ useMergeBase: true,
+ Expected: &DiffTree{
+ Files: []*DiffTreeRecord{
+ {
+ Status: "added",
+ HeadPath: ".gitignore",
+ BasePath: ".gitignore",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeNoEntry,
+ HeadBlobID: "f1c181ec9c5c921245027c6b452ecfc1d3626364",
+ BaseBlobID: "0000000000000000000000000000000000000000",
+ },
+ {
+ Status: "added",
+ HeadPath: "LICENSE",
+ BasePath: "LICENSE",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeNoEntry,
+ HeadBlobID: "c996f4725be8fc8c1d1c776e58c97ddc5d03b336",
+ BaseBlobID: "0000000000000000000000000000000000000000",
+ },
+ {
+ Status: "added",
+ HeadPath: "README.md",
+ BasePath: "README.md",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeNoEntry,
+ HeadBlobID: "074e590b8e64898b02beef03ece83f962c94f54c",
+ BaseBlobID: "0000000000000000000000000000000000000000",
+ },
+ },
+ },
+ },
+ {
+ Name: "base and head same",
+ RepoPath: "../../modules/git/tests/repos/repo5_pulls",
+ BaseSha: "ed8f4d2fa5b2420706580d191f5dd50c4e491f3f",
+ HeadSha: "ed8f4d2fa5b2420706580d191f5dd50c4e491f3f",
+ Expected: &DiffTree{
+ Files: []*DiffTreeRecord{},
+ },
+ },
+ {
+ Name: "useMergeBase false",
+ RepoPath: "../../modules/git/tests/repos/repo5_pulls",
+ BaseSha: "ed8f4d2fa5b2420706580d191f5dd50c4e491f3f",
+ HeadSha: "111cac04bd7d20301964e27a93698aabb5781b80", // this commit can be found on the update-readme branch
+ useMergeBase: false,
+ Expected: &DiffTree{
+ Files: []*DiffTreeRecord{
+ {
+ Status: "modified",
+ HeadPath: "LICENSE",
+ BasePath: "LICENSE",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeBlob,
+ HeadBlobID: "c996f4725be8fc8c1d1c776e58c97ddc5d03b336",
+ BaseBlobID: "ed5119b3c1f45547b6785bc03eac7f87570fa17f",
+ },
+
+ {
+ Status: "modified",
+ HeadPath: "README.md",
+ BasePath: "README.md",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeBlob,
+ HeadBlobID: "fb39771a8865c9a67f2ab9b616c854805664553c",
+ BaseBlobID: "9dfc0a6257d8eff526f0cfaf6a8ea950f55a9dba",
+ },
+ },
+ },
+ },
+ {
+ Name: "useMergeBase true",
+ RepoPath: "../../modules/git/tests/repos/repo5_pulls",
+ BaseSha: "ed8f4d2fa5b2420706580d191f5dd50c4e491f3f",
+ HeadSha: "111cac04bd7d20301964e27a93698aabb5781b80", // this commit can be found on the update-readme branch
+ useMergeBase: true,
+ Expected: &DiffTree{
+ Files: []*DiffTreeRecord{
+ {
+ Status: "modified",
+ HeadPath: "README.md",
+ BasePath: "README.md",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeBlob,
+ HeadBlobID: "fb39771a8865c9a67f2ab9b616c854805664553c",
+ BaseBlobID: "9dfc0a6257d8eff526f0cfaf6a8ea950f55a9dba",
+ },
+ },
+ },
+ },
+ {
+ Name: "no base set",
+ RepoPath: "../../modules/git/tests/repos/repo5_pulls",
+ HeadSha: "d8e0bbb45f200e67d9a784ce55bd90821af45ebd", // this commit can be found on the update-readme branch
+ useMergeBase: false,
+ Expected: &DiffTree{
+ Files: []*DiffTreeRecord{
+ {
+ Status: "modified",
+ HeadPath: "LICENSE",
+ BasePath: "LICENSE",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeBlob,
+ HeadBlobID: "ee469963e76ae1bb7ee83d7510df2864e6c8c640",
+ BaseBlobID: "ed5119b3c1f45547b6785bc03eac7f87570fa17f",
+ },
+ },
+ },
+ },
+ }
+
+ for _, tt := range test {
+ t.Run(tt.Name, func(t *testing.T) {
+ gitRepo, err := git.OpenRepository(git.DefaultContext, tt.RepoPath)
+ assert.NoError(t, err)
+ defer gitRepo.Close()
+
+ diffPaths, err := GetDiffTree(db.DefaultContext, gitRepo, tt.useMergeBase, tt.BaseSha, tt.HeadSha)
+ require.NoError(t, err)
+
+ assert.Equal(t, tt.Expected, diffPaths)
+ })
+ }
+}
+
+func TestParseGitDiffTree(t *testing.T) {
+ test := []struct {
+ Name string
+ GitOutput string
+ Expected []*DiffTreeRecord
+ }{
+ {
+ Name: "file change",
+ GitOutput: ":100644 100644 64e43d23bcd08db12563a0a4d84309cadb437e1a 5dbc7792b5bb228647cfcc8dfe65fc649119dedc M\tResources/views/curriculum/edit.blade.php",
+ Expected: []*DiffTreeRecord{
+ {
+ Status: "modified",
+ HeadPath: "Resources/views/curriculum/edit.blade.php",
+ BasePath: "Resources/views/curriculum/edit.blade.php",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeBlob,
+ HeadBlobID: "5dbc7792b5bb228647cfcc8dfe65fc649119dedc",
+ BaseBlobID: "64e43d23bcd08db12563a0a4d84309cadb437e1a",
+ },
+ },
+ },
+ {
+ Name: "file added",
+ GitOutput: ":000000 100644 0000000000000000000000000000000000000000 0063162fb403db15ceb0517b34ab782e4e58b619 A\tResources/views/class/index.blade.php",
+ Expected: []*DiffTreeRecord{
+ {
+ Status: "added",
+ HeadPath: "Resources/views/class/index.blade.php",
+ BasePath: "Resources/views/class/index.blade.php",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeNoEntry,
+ HeadBlobID: "0063162fb403db15ceb0517b34ab782e4e58b619",
+ BaseBlobID: "0000000000000000000000000000000000000000",
+ },
+ },
+ },
+ {
+ Name: "file deleted",
+ GitOutput: ":100644 000000 bac4286303c8c0017ea2f0a48c561ddcc0330a14 0000000000000000000000000000000000000000 D\tResources/views/classes/index.blade.php",
+ Expected: []*DiffTreeRecord{
+ {
+ Status: "deleted",
+ HeadPath: "Resources/views/classes/index.blade.php",
+ BasePath: "Resources/views/classes/index.blade.php",
+ HeadMode: git.EntryModeNoEntry,
+ BaseMode: git.EntryModeBlob,
+ HeadBlobID: "0000000000000000000000000000000000000000",
+ BaseBlobID: "bac4286303c8c0017ea2f0a48c561ddcc0330a14",
+ },
+ },
+ },
+ {
+ Name: "file renamed",
+ GitOutput: ":100644 100644 c8a055cfb45cd39747292983ad1797ceab40f5b1 97248f79a90aaf81fe7fd74b33c1cb182dd41783 R087\tDatabase/Seeders/AdminDatabaseSeeder.php\tDatabase/Seeders/AcademicDatabaseSeeder.php",
+ Expected: []*DiffTreeRecord{
+ {
+ Status: "renamed",
+ Score: 87,
+ HeadPath: "Database/Seeders/AcademicDatabaseSeeder.php",
+ BasePath: "Database/Seeders/AdminDatabaseSeeder.php",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeBlob,
+ HeadBlobID: "97248f79a90aaf81fe7fd74b33c1cb182dd41783",
+ BaseBlobID: "c8a055cfb45cd39747292983ad1797ceab40f5b1",
+ },
+ },
+ },
+ {
+ Name: "no changes",
+ GitOutput: ``,
+ Expected: []*DiffTreeRecord{},
+ },
+ {
+ Name: "multiple changes",
+ GitOutput: ":000000 100644 0000000000000000000000000000000000000000 db736b44533a840981f1f17b7029d0f612b69550 A\tHttp/Controllers/ClassController.php\n" +
+ ":100644 000000 9a4d2344d4d0145db7c91b3f3e123c74367d4ef4 0000000000000000000000000000000000000000 D\tHttp/Controllers/ClassesController.php\n" +
+ ":100644 100644 f060d6aede65d423f49e7dc248dfa0d8835ef920 b82c8e39a3602dedadb44669956d6eb5b6a7cc86 M\tHttp/Controllers/ProgramDirectorController.php\n",
+ Expected: []*DiffTreeRecord{
+ {
+ Status: "added",
+ HeadPath: "Http/Controllers/ClassController.php",
+ BasePath: "Http/Controllers/ClassController.php",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeNoEntry,
+ HeadBlobID: "db736b44533a840981f1f17b7029d0f612b69550",
+ BaseBlobID: "0000000000000000000000000000000000000000",
+ },
+ {
+ Status: "deleted",
+ HeadPath: "Http/Controllers/ClassesController.php",
+ BasePath: "Http/Controllers/ClassesController.php",
+ HeadMode: git.EntryModeNoEntry,
+ BaseMode: git.EntryModeBlob,
+ HeadBlobID: "0000000000000000000000000000000000000000",
+ BaseBlobID: "9a4d2344d4d0145db7c91b3f3e123c74367d4ef4",
+ },
+ {
+ Status: "modified",
+ HeadPath: "Http/Controllers/ProgramDirectorController.php",
+ BasePath: "Http/Controllers/ProgramDirectorController.php",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeBlob,
+ HeadBlobID: "b82c8e39a3602dedadb44669956d6eb5b6a7cc86",
+ BaseBlobID: "f060d6aede65d423f49e7dc248dfa0d8835ef920",
+ },
+ },
+ },
+ {
+ Name: "spaces in file path",
+ GitOutput: ":000000 100644 0000000000000000000000000000000000000000 db736b44533a840981f1f17b7029d0f612b69550 A\tHttp /Controllers/Class Controller.php\n" +
+ ":100644 000000 9a4d2344d4d0145db7c91b3f3e123c74367d4ef4 0000000000000000000000000000000000000000 D\tHttp/Cont rollers/Classes Controller.php\n" +
+ ":100644 100644 f060d6aede65d423f49e7dc248dfa0d8835ef920 b82c8e39a3602dedadb44669956d6eb5b6a7cc86 R010\tHttp/Controllers/Program Director Controller.php\tHttp/Cont rollers/ProgramDirectorController.php\n",
+ Expected: []*DiffTreeRecord{
+ {
+ Status: "added",
+ HeadPath: "Http /Controllers/Class Controller.php",
+ BasePath: "Http /Controllers/Class Controller.php",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeNoEntry,
+ HeadBlobID: "db736b44533a840981f1f17b7029d0f612b69550",
+ BaseBlobID: "0000000000000000000000000000000000000000",
+ },
+ {
+ Status: "deleted",
+ HeadPath: "Http/Cont rollers/Classes Controller.php",
+ BasePath: "Http/Cont rollers/Classes Controller.php",
+ HeadMode: git.EntryModeNoEntry,
+ BaseMode: git.EntryModeBlob,
+ HeadBlobID: "0000000000000000000000000000000000000000",
+ BaseBlobID: "9a4d2344d4d0145db7c91b3f3e123c74367d4ef4",
+ },
+ {
+ Status: "renamed",
+ Score: 10,
+ HeadPath: "Http/Cont rollers/ProgramDirectorController.php",
+ BasePath: "Http/Controllers/Program Director Controller.php",
+ HeadMode: git.EntryModeBlob,
+ BaseMode: git.EntryModeBlob,
+ HeadBlobID: "b82c8e39a3602dedadb44669956d6eb5b6a7cc86",
+ BaseBlobID: "f060d6aede65d423f49e7dc248dfa0d8835ef920",
+ },
+ },
+ },
+ {
+ Name: "file type changed",
+ GitOutput: ":100644 120000 344e0ca8aa791cc4164fb0ea645f334fd40d00f0 a7c2973de00bfdc6ca51d315f401b5199fe01dc3 T\twebpack.mix.js",
+ Expected: []*DiffTreeRecord{
+ {
+ Status: "typechanged",
+ HeadPath: "webpack.mix.js",
+ BasePath: "webpack.mix.js",
+ HeadMode: git.EntryModeSymlink,
+ BaseMode: git.EntryModeBlob,
+ HeadBlobID: "a7c2973de00bfdc6ca51d315f401b5199fe01dc3",
+ BaseBlobID: "344e0ca8aa791cc4164fb0ea645f334fd40d00f0",
+ },
+ },
+ },
+ }
+
+ for _, tt := range test {
+ t.Run(tt.Name, func(t *testing.T) {
+ entries, err := parseGitDiffTree(strings.NewReader(tt.GitOutput))
+ assert.NoError(t, err)
+ assert.Equal(t, tt.Expected, entries)
+ })
+ }
+}
+
+func TestGitDiffTreeErrors(t *testing.T) {
+ test := []struct {
+ Name string
+ RepoPath string
+ BaseSha string
+ HeadSha string
+ }{
+ {
+ Name: "head doesn't exist",
+ RepoPath: "../../modules/git/tests/repos/repo5_pulls",
+ BaseSha: "f32b0a9dfd09a60f616f29158f772cedd89942d2",
+ HeadSha: "asdfasdfasdf",
+ },
+ {
+ Name: "base doesn't exist",
+ RepoPath: "../../modules/git/tests/repos/repo5_pulls",
+ BaseSha: "asdfasdfasdf",
+ HeadSha: "f32b0a9dfd09a60f616f29158f772cedd89942d2",
+ },
+ {
+ Name: "head not set",
+ RepoPath: "../../modules/git/tests/repos/repo5_pulls",
+ BaseSha: "f32b0a9dfd09a60f616f29158f772cedd89942d2",
+ },
+ }
+
+ for _, tt := range test {
+ t.Run(tt.Name, func(t *testing.T) {
+ gitRepo, err := git.OpenRepository(git.DefaultContext, tt.RepoPath)
+ assert.NoError(t, err)
+ defer gitRepo.Close()
+
+ diffPaths, err := GetDiffTree(db.DefaultContext, gitRepo, true, tt.BaseSha, tt.HeadSha)
+ assert.Error(t, err)
+ assert.Nil(t, diffPaths)
+ })
+ }
+}
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index 0b5a855d42..9964329876 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -25,12 +25,14 @@ import (
"code.gitea.io/gitea/modules/analyze"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/git/attribute"
"code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/translation"
+ "code.gitea.io/gitea/modules/util"
"github.com/sergi/go-diff/diffmatchpatch"
stdcharset "golang.org/x/net/html/charset"
@@ -75,12 +77,12 @@ const (
// DiffLine represents a line difference in a DiffSection.
type DiffLine struct {
- LeftIdx int
- RightIdx int
- Match int
+ LeftIdx int // line number, 1-based
+ RightIdx int // line number, 1-based
+ Match int // the diff matched index. -1: no match. 0: plain and no need to match. >0: for add/del, "Lines" slice index of the other side
Type DiffLineType
Content string
- Comments []*issues_model.Comment
+ Comments issues_model.CommentList // related PR code comments
SectionInfo *DiffLineSectionInfo
}
@@ -95,9 +97,18 @@ type DiffLineSectionInfo struct {
RightHunkSize int
}
+// DiffHTMLOperation is the HTML version of diffmatchpatch.Diff
+type DiffHTMLOperation struct {
+ Type diffmatchpatch.Operation
+ HTML template.HTML
+}
+
// BlobExcerptChunkSize represent max lines of excerpt
const BlobExcerptChunkSize = 20
+// MaxDiffHighlightEntireFileSize is the maximum file size that will be highlighted with "entire file diff"
+const MaxDiffHighlightEntireFileSize = 1 * 1024 * 1024
+
// GetType returns the type of DiffLine.
func (d *DiffLine) GetType() int {
return int(d.Type)
@@ -112,8 +123,9 @@ func (d *DiffLine) GetHTMLDiffLineType() string {
return "del"
case DiffLineSection:
return "tag"
+ default:
+ return "same"
}
- return "same"
}
// CanComment returns whether a line can get commented
@@ -192,89 +204,20 @@ func getLineContent(content string, locale translation.Locale) DiffInline {
type DiffSection struct {
file *DiffFile
FileName string
- Name string
Lines []*DiffLine
}
-var (
- addedCodePrefix = []byte(`<span class="added-code">`)
- removedCodePrefix = []byte(`<span class="removed-code">`)
- codeTagSuffix = []byte(`</span>`)
-)
-
-func diffToHTML(lineWrapperTags []string, diffs []diffmatchpatch.Diff, lineType DiffLineType) string {
- buf := bytes.NewBuffer(nil)
- // restore the line wrapper tags <span class="line"> and <span class="cl">, if necessary
- for _, tag := range lineWrapperTags {
- buf.WriteString(tag)
- }
- for _, diff := range diffs {
- switch {
- case diff.Type == diffmatchpatch.DiffEqual:
- buf.WriteString(diff.Text)
- case diff.Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd:
- buf.Write(addedCodePrefix)
- buf.WriteString(diff.Text)
- buf.Write(codeTagSuffix)
- case diff.Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel:
- buf.Write(removedCodePrefix)
- buf.WriteString(diff.Text)
- buf.Write(codeTagSuffix)
- }
- }
- for range lineWrapperTags {
- buf.WriteString("</span>")
- }
- return buf.String()
-}
-
-// GetLine gets a specific line by type (add or del) and file line number
-func (diffSection *DiffSection) GetLine(lineType DiffLineType, idx int) *DiffLine {
- var (
- difference = 0
- addCount = 0
- delCount = 0
- matchDiffLine *DiffLine
- )
-
-LOOP:
- for _, diffLine := range diffSection.Lines {
- switch diffLine.Type {
- case DiffLineAdd:
- addCount++
- case DiffLineDel:
- delCount++
- default:
- if matchDiffLine != nil {
- break LOOP
- }
- difference = diffLine.RightIdx - diffLine.LeftIdx
- addCount = 0
- delCount = 0
- }
-
- switch lineType {
- case DiffLineDel:
- if diffLine.RightIdx == 0 && diffLine.LeftIdx == idx-difference {
- matchDiffLine = diffLine
- }
- case DiffLineAdd:
- if diffLine.LeftIdx == 0 && diffLine.RightIdx == idx+difference {
- matchDiffLine = diffLine
- }
- }
- }
-
- if addCount == delCount {
- return matchDiffLine
+func (diffSection *DiffSection) GetLine(idx int) *DiffLine {
+ if idx <= 0 {
+ return nil
}
- return nil
+ return diffSection.Lines[idx]
}
-var diffMatchPatch = diffmatchpatch.New()
-
-func init() {
- diffMatchPatch.DiffEditCost = 100
+func defaultDiffMatchPatch() *diffmatchpatch.DiffMatchPatch {
+ dmp := diffmatchpatch.New()
+ dmp.DiffEditCost = 100
+ return dmp
}
// DiffInline is a struct that has a content and escape status
@@ -283,95 +226,125 @@ type DiffInline struct {
Content template.HTML
}
-// DiffInlineWithUnicodeEscape makes a DiffInline with hidden unicode characters escaped
+// DiffInlineWithUnicodeEscape makes a DiffInline with hidden Unicode characters escaped
func DiffInlineWithUnicodeEscape(s template.HTML, locale translation.Locale) DiffInline {
status, content := charset.EscapeControlHTML(s, locale)
return DiffInline{EscapeStatus: status, Content: content}
}
-// DiffInlineWithHighlightCode makes a DiffInline with code highlight and hidden unicode characters escaped
-func DiffInlineWithHighlightCode(fileName, language, code string, locale translation.Locale) DiffInline {
- highlighted, _ := highlight.Code(fileName, language, code)
- status, content := charset.EscapeControlHTML(highlighted, locale)
- return DiffInline{EscapeStatus: status, Content: content}
-}
-
-// GetComputedInlineDiffFor computes inline diff for the given line.
-func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine, locale translation.Locale) DiffInline {
+func (diffSection *DiffSection) getLineContentForRender(lineIdx int, diffLine *DiffLine, fileLanguage string, highlightLines map[int]template.HTML) template.HTML {
+ h, ok := highlightLines[lineIdx-1]
+ if ok {
+ return h
+ }
+ if diffLine.Content == "" {
+ return ""
+ }
if setting.Git.DisableDiffHighlight {
- return getLineContent(diffLine.Content[1:], locale)
+ return template.HTML(html.EscapeString(diffLine.Content[1:]))
}
+ h, _ = highlight.Code(diffSection.FileName, fileLanguage, diffLine.Content[1:])
+ return h
+}
- var (
- compareDiffLine *DiffLine
- diff1 string
- diff2 string
- )
-
- language := ""
+func (diffSection *DiffSection) getDiffLineForRender(diffLineType DiffLineType, leftLine, rightLine *DiffLine, locale translation.Locale) DiffInline {
+ var fileLanguage string
+ var highlightedLeftLines, highlightedRightLines map[int]template.HTML
+ // when a "diff section" is manually prepared by ExcerptBlob, it doesn't have "file" information
if diffSection.file != nil {
- language = diffSection.file.Language
+ fileLanguage = diffSection.file.Language
+ highlightedLeftLines, highlightedRightLines = diffSection.file.highlightedLeftLines, diffSection.file.highlightedRightLines
+ }
+
+ var lineHTML template.HTML
+ hcd := newHighlightCodeDiff()
+ if diffLineType == DiffLinePlain {
+ // left and right are the same, no need to do line-level diff
+ if leftLine != nil {
+ lineHTML = diffSection.getLineContentForRender(leftLine.LeftIdx, leftLine, fileLanguage, highlightedLeftLines)
+ } else if rightLine != nil {
+ lineHTML = diffSection.getLineContentForRender(rightLine.RightIdx, rightLine, fileLanguage, highlightedRightLines)
+ }
+ } else {
+ var diff1, diff2 template.HTML
+ if leftLine != nil {
+ diff1 = diffSection.getLineContentForRender(leftLine.LeftIdx, leftLine, fileLanguage, highlightedLeftLines)
+ }
+ if rightLine != nil {
+ diff2 = diffSection.getLineContentForRender(rightLine.RightIdx, rightLine, fileLanguage, highlightedRightLines)
+ }
+ if diff1 != "" && diff2 != "" {
+ // if only some parts of a line are changed, highlight these changed parts as "deleted/added".
+ lineHTML = hcd.diffLineWithHighlight(diffLineType, diff1, diff2)
+ } else {
+ // if left is empty or right is empty (a line is fully deleted or added), then we do not need to diff anymore.
+ // the tmpl code already adds background colors for these cases.
+ lineHTML = util.Iif(diffLineType == DiffLineDel, diff1, diff2)
+ }
}
+ return DiffInlineWithUnicodeEscape(lineHTML, locale)
+}
+// GetComputedInlineDiffFor computes inline diff for the given line.
+func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine, locale translation.Locale) DiffInline {
// try to find equivalent diff line. ignore, otherwise
switch diffLine.Type {
case DiffLineSection:
return getLineContent(diffLine.Content[1:], locale)
case DiffLineAdd:
- compareDiffLine = diffSection.GetLine(DiffLineDel, diffLine.RightIdx)
- if compareDiffLine == nil {
- return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content[1:], locale)
- }
- diff1 = compareDiffLine.Content
- diff2 = diffLine.Content
+ compareDiffLine := diffSection.GetLine(diffLine.Match)
+ return diffSection.getDiffLineForRender(DiffLineAdd, compareDiffLine, diffLine, locale)
case DiffLineDel:
- compareDiffLine = diffSection.GetLine(DiffLineAdd, diffLine.LeftIdx)
- if compareDiffLine == nil {
- return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content[1:], locale)
- }
- diff1 = diffLine.Content
- diff2 = compareDiffLine.Content
- default:
- if strings.IndexByte(" +-", diffLine.Content[0]) > -1 {
- return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content[1:], locale)
- }
- return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content, locale)
+ compareDiffLine := diffSection.GetLine(diffLine.Match)
+ return diffSection.getDiffLineForRender(DiffLineDel, diffLine, compareDiffLine, locale)
+ default: // Plain
+ // TODO: there was an "if" check: `if diffLine.Content >strings.IndexByte(" +-", diffLine.Content[0]) > -1 { ... } else { ... }`
+ // no idea why it needs that check, it seems that the "if" should be always true, so try to simplify the code
+ return diffSection.getDiffLineForRender(DiffLinePlain, nil, diffLine, locale)
}
-
- hcd := newHighlightCodeDiff()
- diffRecord := hcd.diffWithHighlight(diffSection.FileName, language, diff1[1:], diff2[1:])
- // it seems that Gitea doesn't need the line wrapper of Chroma, so do not add them back
- // if the line wrappers are still needed in the future, it can be added back by "diffToHTML(hcd.lineWrapperTags. ...)"
- diffHTML := diffToHTML(nil, diffRecord, diffLine.Type)
- return DiffInlineWithUnicodeEscape(template.HTML(diffHTML), locale)
}
// DiffFile represents a file diff.
type DiffFile struct {
- Name string
- NameHash string
- OldName string
- Index int
- Addition, Deletion int
- Type DiffFileType
- IsCreated bool
- IsDeleted bool
- IsBin bool
- IsLFSFile bool
- IsRenamed bool
- IsAmbiguous bool
- IsSubmodule bool
- Sections []*DiffSection
- IsIncomplete bool
- IsIncompleteLineTooLong bool
- IsProtected bool
- IsGenerated bool
- IsVendored bool
+ // only used internally to parse Ambiguous filenames
+ isAmbiguous bool
+
+ // basic fields (parsed from diff result)
+ Name string
+ NameHash string
+ OldName string
+ Addition int
+ Deletion int
+ Type DiffFileType
+ Mode string
+ OldMode string
+ IsCreated bool
+ IsDeleted bool
+ IsBin bool
+ IsLFSFile bool
+ IsRenamed bool
+ IsSubmodule bool
+ // basic fields but for render purpose only
+ Sections []*DiffSection
+ IsIncomplete bool
+ IsIncompleteLineTooLong bool
+
+ // will be filled by the extra loop in GitDiffForRender
+ Language string
+ IsGenerated bool
+ IsVendored bool
+ SubmoduleDiffInfo *SubmoduleDiffInfo // IsSubmodule==true, then there must be a SubmoduleDiffInfo
+
+ // will be filled by route handler
+ IsProtected bool
+
+ // will be filled by SyncUserSpecificDiff
IsViewed bool // User specific
HasChangedSinceLastReview bool // User specific
- Language string
- Mode string
- OldMode string
+
+ // for render purpose only, will be filled by the extra loop in GitDiffForRender
+ highlightedLeftLines map[int]template.HTML
+ highlightedRightLines map[int]template.HTML
}
// GetType returns type of diff file.
@@ -379,18 +352,30 @@ func (diffFile *DiffFile) GetType() int {
return int(diffFile.Type)
}
-// GetTailSection creates a fake DiffLineSection if the last section is not the end of the file
-func (diffFile *DiffFile) GetTailSection(gitRepo *git.Repository, leftCommit, rightCommit *git.Commit) *DiffSection {
- if len(diffFile.Sections) == 0 || diffFile.Type != DiffFileChange || diffFile.IsBin || diffFile.IsLFSFile {
- return nil
- }
+type DiffLimitedContent struct {
+ LeftContent, RightContent *limitByteWriter
+}
+// GetTailSectionAndLimitedContent creates a fake DiffLineSection if the last section is not the end of the file
+func (diffFile *DiffFile) GetTailSectionAndLimitedContent(leftCommit, rightCommit *git.Commit) (_ *DiffSection, diffLimitedContent DiffLimitedContent) {
+ var leftLineCount, rightLineCount int
+ diffLimitedContent = DiffLimitedContent{}
+ if diffFile.IsBin || diffFile.IsLFSFile {
+ return nil, diffLimitedContent
+ }
+ if (diffFile.Type == DiffFileDel || diffFile.Type == DiffFileChange) && leftCommit != nil {
+ leftLineCount, diffLimitedContent.LeftContent = getCommitFileLineCountAndLimitedContent(leftCommit, diffFile.OldName)
+ }
+ if (diffFile.Type == DiffFileAdd || diffFile.Type == DiffFileChange) && rightCommit != nil {
+ rightLineCount, diffLimitedContent.RightContent = getCommitFileLineCountAndLimitedContent(rightCommit, diffFile.OldName)
+ }
+ if len(diffFile.Sections) == 0 || diffFile.Type != DiffFileChange {
+ return nil, diffLimitedContent
+ }
lastSection := diffFile.Sections[len(diffFile.Sections)-1]
lastLine := lastSection.Lines[len(lastSection.Lines)-1]
- leftLineCount := getCommitFileLineCount(leftCommit, diffFile.Name)
- rightLineCount := getCommitFileLineCount(rightCommit, diffFile.Name)
if leftLineCount <= lastLine.LeftIdx || rightLineCount <= lastLine.RightIdx {
- return nil
+ return nil, diffLimitedContent
}
tailDiffLine := &DiffLine{
Type: DiffLineSection,
@@ -404,7 +389,7 @@ func (diffFile *DiffFile) GetTailSection(gitRepo *git.Repository, leftCommit, ri
},
}
tailSection := &DiffSection{FileName: diffFile.Name, Lines: []*DiffLine{tailDiffLine}}
- return tailSection
+ return tailSection, diffLimitedContent
}
// GetDiffFileName returns the name of the diff file, or its old name in case it was deleted
@@ -436,26 +421,37 @@ func (diffFile *DiffFile) ModeTranslationKey(mode string) string {
}
}
-func getCommitFileLineCount(commit *git.Commit, filePath string) int {
+type limitByteWriter struct {
+ buf bytes.Buffer
+ limit int
+}
+
+func (l *limitByteWriter) Write(p []byte) (n int, err error) {
+ if l.buf.Len()+len(p) > l.limit {
+ p = p[:l.limit-l.buf.Len()]
+ }
+ return l.buf.Write(p)
+}
+
+func getCommitFileLineCountAndLimitedContent(commit *git.Commit, filePath string) (lineCount int, limitWriter *limitByteWriter) {
blob, err := commit.GetBlobByPath(filePath)
if err != nil {
- return 0
+ return 0, nil
}
- lineCount, err := blob.GetBlobLineCount()
+ w := &limitByteWriter{limit: MaxDiffHighlightEntireFileSize + 1}
+ lineCount, err = blob.GetBlobLineCount(w)
if err != nil {
- return 0
+ return 0, nil
}
- return lineCount
+ return lineCount, w
}
// Diff represents a difference between two git trees.
type Diff struct {
- Start, End string
- NumFiles int
- TotalAddition, TotalDeletion int
- Files []*DiffFile
- IsIncomplete bool
- NumViewedFiles int // user-specific
+ Start, End string
+ Files []*DiffFile
+ IsIncomplete bool
+ NumViewedFiles int // user-specific
}
// LoadComments loads comments into each line
@@ -499,10 +495,7 @@ func ParsePatch(ctx context.Context, maxLines, maxLineCharacters, maxFiles int,
// OK let's set a reasonable buffer size.
// This should be at least the size of maxLineCharacters or 4096 whichever is larger.
- readerSize := maxLineCharacters
- if readerSize < 4096 {
- readerSize = 4096
- }
+ readerSize := max(maxLineCharacters, 4096)
input := bufio.NewReaderSize(reader, readerSize)
line, err := input.ReadString('\n')
@@ -526,13 +519,13 @@ parsingLoop:
}
if maxFiles > -1 && len(diff.Files) >= maxFiles {
- lastFile := createDiffFile(diff, line)
+ lastFile := createDiffFile(line)
diff.End = lastFile.Name
diff.IsIncomplete = true
break parsingLoop
}
- curFile = createDiffFile(diff, line)
+ curFile = createDiffFile(line)
if skipping {
if curFile.Name != skipToFile {
line, err = skipToNextDiffHead(input)
@@ -609,35 +602,34 @@ parsingLoop:
if strings.HasPrefix(line, "new mode ") {
curFile.Mode = prepareValue(line, "new mode ")
}
-
if strings.HasSuffix(line, " 160000\n") {
- curFile.IsSubmodule = true
+ curFile.IsSubmodule, curFile.SubmoduleDiffInfo = true, &SubmoduleDiffInfo{}
}
case strings.HasPrefix(line, "rename from "):
curFile.IsRenamed = true
curFile.Type = DiffFileRename
- if curFile.IsAmbiguous {
+ if curFile.isAmbiguous {
curFile.OldName = prepareValue(line, "rename from ")
}
case strings.HasPrefix(line, "rename to "):
curFile.IsRenamed = true
curFile.Type = DiffFileRename
- if curFile.IsAmbiguous {
+ if curFile.isAmbiguous {
curFile.Name = prepareValue(line, "rename to ")
- curFile.IsAmbiguous = false
+ curFile.isAmbiguous = false
}
case strings.HasPrefix(line, "copy from "):
curFile.IsRenamed = true
curFile.Type = DiffFileCopy
- if curFile.IsAmbiguous {
+ if curFile.isAmbiguous {
curFile.OldName = prepareValue(line, "copy from ")
}
case strings.HasPrefix(line, "copy to "):
curFile.IsRenamed = true
curFile.Type = DiffFileCopy
- if curFile.IsAmbiguous {
+ if curFile.isAmbiguous {
curFile.Name = prepareValue(line, "copy to ")
- curFile.IsAmbiguous = false
+ curFile.isAmbiguous = false
}
case strings.HasPrefix(line, "new file"):
curFile.Type = DiffFileAdd
@@ -646,17 +638,17 @@ parsingLoop:
curFile.Mode = prepareValue(line, "new file mode ")
}
if strings.HasSuffix(line, " 160000\n") {
- curFile.IsSubmodule = true
+ curFile.IsSubmodule, curFile.SubmoduleDiffInfo = true, &SubmoduleDiffInfo{}
}
case strings.HasPrefix(line, "deleted"):
curFile.Type = DiffFileDel
curFile.IsDeleted = true
if strings.HasSuffix(line, " 160000\n") {
- curFile.IsSubmodule = true
+ curFile.IsSubmodule, curFile.SubmoduleDiffInfo = true, &SubmoduleDiffInfo{}
}
case strings.HasPrefix(line, "index"):
if strings.HasSuffix(line, " 160000\n") {
- curFile.IsSubmodule = true
+ curFile.IsSubmodule, curFile.SubmoduleDiffInfo = true, &SubmoduleDiffInfo{}
}
case strings.HasPrefix(line, "similarity index 100%"):
curFile.Type = DiffFileRename
@@ -664,7 +656,7 @@ parsingLoop:
curFile.IsBin = true
case strings.HasPrefix(line, "--- "):
// Handle ambiguous filenames
- if curFile.IsAmbiguous {
+ if curFile.isAmbiguous {
// The shortest string that can end up here is:
// "--- a\t\n" without the quotes.
// This line has a len() of 7 but doesn't contain a oldName.
@@ -682,7 +674,7 @@ parsingLoop:
// Otherwise do nothing with this line
case strings.HasPrefix(line, "+++ "):
// Handle ambiguous filenames
- if curFile.IsAmbiguous {
+ if curFile.isAmbiguous {
if len(line) > 6 && line[4] == 'b' {
curFile.Name = line[6 : len(line)-1]
if line[len(line)-2] == '\t' {
@@ -694,12 +686,10 @@ parsingLoop:
} else {
curFile.Name = curFile.OldName
}
- curFile.IsAmbiguous = false
+ curFile.isAmbiguous = false
}
// Otherwise do nothing with this line, but now switch to parsing hunks
lineBytes, isFragment, err := parseHunks(ctx, curFile, maxLines, maxLineCharacters, input)
- diff.TotalAddition += curFile.Addition
- diff.TotalDeletion += curFile.Deletion
if err != nil {
if err != io.EOF {
return diff, err
@@ -772,7 +762,6 @@ parsingLoop:
}
}
- diff.NumFiles = len(diff.Files)
return diff, nil
}
@@ -915,6 +904,13 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact
}
}
curSection.Lines = append(curSection.Lines, diffLine)
+
+ // Parse submodule additions
+ if curFile.SubmoduleDiffInfo != nil {
+ if ref, found := bytes.CutPrefix(lineBytes, []byte("+Subproject commit ")); found {
+ curFile.SubmoduleDiffInfo.NewRefID = string(bytes.TrimSpace(ref))
+ }
+ }
case '-':
curFileLinesCount++
curFile.Deletion++
@@ -936,6 +932,13 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact
lastLeftIdx = len(curSection.Lines)
}
curSection.Lines = append(curSection.Lines, diffLine)
+
+ // Parse submodule deletion
+ if curFile.SubmoduleDiffInfo != nil {
+ if ref, found := bytes.CutPrefix(lineBytes, []byte("-Subproject commit ")); found {
+ curFile.SubmoduleDiffInfo.PreviousRefID = string(bytes.TrimSpace(ref))
+ }
+ }
case ' ':
curFileLinesCount++
if maxLines > -1 && curFileLinesCount >= maxLines {
@@ -996,7 +999,7 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact
}
}
-func createDiffFile(diff *Diff, line string) *DiffFile {
+func createDiffFile(line string) *DiffFile {
// The a/ and b/ filenames are the same unless rename/copy is involved.
// Especially, even for a creation or a deletion, /dev/null is not used
// in place of the a/ or b/ filenames.
@@ -1007,12 +1010,11 @@ func createDiffFile(diff *Diff, line string) *DiffFile {
//
// Path names are quoted if necessary.
//
- // This means that you should always be able to determine the file name even when there
+ // This means that you should always be able to determine the file name even when
// there is potential ambiguity...
//
// but we can be simpler with our heuristics by just forcing git to prefix things nicely
curFile := &DiffFile{
- Index: len(diff.Files) + 1,
Type: DiffFileChange,
Sections: make([]*DiffSection, 0, 10),
}
@@ -1024,7 +1026,7 @@ func createDiffFile(diff *Diff, line string) *DiffFile {
curFile.OldName, oldNameAmbiguity = readFileName(rd)
curFile.Name, newNameAmbiguity = readFileName(rd)
if oldNameAmbiguity && newNameAmbiguity {
- curFile.IsAmbiguous = true
+ curFile.isAmbiguous = true
// OK we should bet that the oldName and the newName are the same if they can be made to be same
// So we need to start again ...
if (len(line)-len(cmdDiffHead)-1)%2 == 0 {
@@ -1089,53 +1091,48 @@ type DiffOptions struct {
MaxFiles int
WhitespaceBehavior git.TrustedCmdArgs
DirectComparison bool
- FileOnly bool
}
-// GetDiff builds a Diff between two commits of a repository.
+func guessBeforeCommitForDiff(gitRepo *git.Repository, beforeCommitID string, afterCommit *git.Commit) (actualBeforeCommit *git.Commit, actualBeforeCommitID git.ObjectID, err error) {
+ commitObjectFormat := afterCommit.ID.Type()
+ isBeforeCommitIDEmpty := beforeCommitID == "" || beforeCommitID == commitObjectFormat.EmptyObjectID().String()
+
+ if isBeforeCommitIDEmpty && afterCommit.ParentCount() == 0 {
+ actualBeforeCommitID = commitObjectFormat.EmptyTree()
+ } else {
+ if isBeforeCommitIDEmpty {
+ actualBeforeCommit, err = afterCommit.Parent(0)
+ } else {
+ actualBeforeCommit, err = gitRepo.GetCommit(beforeCommitID)
+ }
+ if err != nil {
+ return nil, nil, err
+ }
+ actualBeforeCommitID = actualBeforeCommit.ID
+ }
+ return actualBeforeCommit, actualBeforeCommitID, nil
+}
+
+// getDiffBasic builds a Diff between two commits of a repository.
// Passing the empty string as beforeCommitID returns a diff from the parent commit.
// The whitespaceBehavior is either an empty string or a git flag
-func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff, error) {
+// Returned beforeCommit could be nil if the afterCommit doesn't have parent commit
+func getDiffBasic(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, files ...string) (_ *Diff, beforeCommit, afterCommit *git.Commit, err error) {
repoPath := gitRepo.Path
- var beforeCommit *git.Commit
- commit, err := gitRepo.GetCommit(opts.AfterCommitID)
+ afterCommit, err = gitRepo.GetCommit(opts.AfterCommitID)
if err != nil {
- return nil, err
+ return nil, nil, nil, err
}
- cmdCtx, cmdCancel := context.WithCancel(ctx)
- defer cmdCancel()
-
- cmdDiff := git.NewCommand(cmdCtx)
- objectFormat, err := gitRepo.GetObjectFormat()
+ beforeCommit, beforeCommitID, err := guessBeforeCommitForDiff(gitRepo, opts.BeforeCommitID, afterCommit)
if err != nil {
- return nil, err
+ return nil, nil, nil, err
}
- if (len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.EmptyObjectID().String()) && commit.ParentCount() == 0 {
- cmdDiff.AddArguments("diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M").
- AddArguments(opts.WhitespaceBehavior...).
- AddDynamicArguments(objectFormat.EmptyTree().String()).
- AddDynamicArguments(opts.AfterCommitID)
- } else {
- actualBeforeCommitID := opts.BeforeCommitID
- if len(actualBeforeCommitID) == 0 {
- parentCommit, _ := commit.Parent(0)
- actualBeforeCommitID = parentCommit.ID.String()
- }
-
- cmdDiff.AddArguments("diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M").
- AddArguments(opts.WhitespaceBehavior...).
- AddDynamicArguments(actualBeforeCommitID, opts.AfterCommitID)
- opts.BeforeCommitID = actualBeforeCommitID
-
- var err error
- beforeCommit, err = gitRepo.GetCommit(opts.BeforeCommitID)
- if err != nil {
- return nil, err
- }
- }
+ cmdDiff := git.NewCommand().
+ AddArguments("diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M").
+ AddArguments(opts.WhitespaceBehavior...)
// In git 2.31, git diff learned --skip-to which we can use to shortcut skip to file
// so if we are using at least this version of git we don't have to tell ParsePatch to do
@@ -1146,8 +1143,12 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
parsePatchSkipToFile = ""
}
+ cmdDiff.AddDynamicArguments(beforeCommitID.String(), opts.AfterCommitID)
cmdDiff.AddDashesAndList(files...)
+ cmdCtx, cmdCancel := context.WithCancel(ctx)
+ defer cmdCancel()
+
reader, writer := io.Pipe()
defer func() {
_ = reader.Close()
@@ -1156,7 +1157,7 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
go func() {
stderr := &bytes.Buffer{}
- if err := cmdDiff.Run(&git.RunOpts{
+ if err := cmdDiff.Run(cmdCtx, &git.RunOpts{
Timeout: time.Duration(setting.Git.Timeout.Default) * time.Second,
Dir: repoPath,
Stdout: writer,
@@ -1172,29 +1173,46 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
// Ensure the git process is killed if it didn't exit already
cmdCancel()
if err != nil {
- return nil, fmt.Errorf("unable to ParsePatch: %w", err)
+ return nil, nil, nil, fmt.Errorf("unable to ParsePatch: %w", err)
}
diff.Start = opts.SkipTo
+ return diff, beforeCommit, afterCommit, nil
+}
- checker, deferable := gitRepo.CheckAttributeReader(opts.AfterCommitID)
- defer deferable()
+func GetDiffForAPI(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff, error) {
+ diff, _, _, err := getDiffBasic(ctx, gitRepo, opts, files...)
+ return diff, err
+}
+
+func GetDiffForRender(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff, error) {
+ diff, beforeCommit, afterCommit, err := getDiffBasic(ctx, gitRepo, opts, files...)
+ if err != nil {
+ return nil, err
+ }
+
+ checker, err := attribute.NewBatchChecker(gitRepo, opts.AfterCommitID, []string{attribute.LinguistVendored, attribute.LinguistGenerated, attribute.LinguistLanguage, attribute.GitlabLanguage})
+ if err != nil {
+ return nil, err
+ }
+ defer checker.Close()
for _, diffFile := range diff.Files {
isVendored := optional.None[bool]()
isGenerated := optional.None[bool]()
- if checker != nil {
- attrs, err := checker.CheckPath(diffFile.Name)
- if err == nil {
- isVendored = git.AttributeToBool(attrs, git.AttributeLinguistVendored)
- isGenerated = git.AttributeToBool(attrs, git.AttributeLinguistGenerated)
-
- language := git.TryReadLanguageAttribute(attrs)
- if language.Has() {
- diffFile.Language = language.Value()
- }
+ attrs, err := checker.CheckPath(diffFile.Name)
+ if err == nil {
+ isVendored, isGenerated = attrs.GetVendored(), attrs.GetGenerated()
+ language := attrs.GetLanguage()
+ if language.Has() {
+ diffFile.Language = language.Value()
}
}
+ // Populate Submodule URLs
+ if diffFile.SubmoduleDiffInfo != nil {
+ diffFile.SubmoduleDiffInfo.PopulateURL(diffFile, beforeCommit, afterCommit)
+ }
+
if !isVendored.Has() {
isVendored = optional.Some(analyze.IsVendor(diffFile.Name))
}
@@ -1204,76 +1222,80 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
isGenerated = optional.Some(analyze.IsGenerated(diffFile.Name))
}
diffFile.IsGenerated = isGenerated.Value()
-
- tailSection := diffFile.GetTailSection(gitRepo, beforeCommit, commit)
+ tailSection, limitedContent := diffFile.GetTailSectionAndLimitedContent(beforeCommit, afterCommit)
if tailSection != nil {
diffFile.Sections = append(diffFile.Sections, tailSection)
}
- }
-
- if opts.FileOnly {
- return diff, nil
- }
- stats, err := GetPullDiffStats(gitRepo, opts)
- if err != nil {
- return nil, err
+ if !setting.Git.DisableDiffHighlight {
+ if limitedContent.LeftContent != nil && limitedContent.LeftContent.buf.Len() < MaxDiffHighlightEntireFileSize {
+ diffFile.highlightedLeftLines = highlightCodeLines(diffFile, true /* left */, limitedContent.LeftContent.buf.String())
+ }
+ if limitedContent.RightContent != nil && limitedContent.RightContent.buf.Len() < MaxDiffHighlightEntireFileSize {
+ diffFile.highlightedRightLines = highlightCodeLines(diffFile, false /* right */, limitedContent.RightContent.buf.String())
+ }
+ }
}
- diff.NumFiles, diff.TotalAddition, diff.TotalDeletion = stats.NumFiles, stats.TotalAddition, stats.TotalDeletion
-
return diff, nil
}
-type PullDiffStats struct {
+func highlightCodeLines(diffFile *DiffFile, isLeft bool, content string) map[int]template.HTML {
+ highlightedNewContent, _ := highlight.Code(diffFile.Name, diffFile.Language, content)
+ splitLines := strings.Split(string(highlightedNewContent), "\n")
+ lines := make(map[int]template.HTML, len(splitLines))
+ // only save the highlighted lines we need, but not the whole file, to save memory
+ for _, sec := range diffFile.Sections {
+ for _, ln := range sec.Lines {
+ lineIdx := ln.LeftIdx
+ if !isLeft {
+ lineIdx = ln.RightIdx
+ }
+ if lineIdx >= 1 {
+ idx := lineIdx - 1
+ if idx < len(splitLines) {
+ lines[idx] = template.HTML(splitLines[idx])
+ }
+ }
+ }
+ }
+ return lines
+}
+
+type DiffShortStat struct {
NumFiles, TotalAddition, TotalDeletion int
}
-// GetPullDiffStats
-func GetPullDiffStats(gitRepo *git.Repository, opts *DiffOptions) (*PullDiffStats, error) {
+func GetDiffShortStat(gitRepo *git.Repository, beforeCommitID, afterCommitID string) (*DiffShortStat, error) {
repoPath := gitRepo.Path
- diff := &PullDiffStats{}
-
- separator := "..."
- if opts.DirectComparison {
- separator = ".."
- }
-
- objectFormat, err := gitRepo.GetObjectFormat()
+ afterCommit, err := gitRepo.GetCommit(afterCommitID)
if err != nil {
return nil, err
}
- diffPaths := []string{opts.BeforeCommitID + separator + opts.AfterCommitID}
- if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.EmptyObjectID().String() {
- diffPaths = []string{objectFormat.EmptyTree().String(), opts.AfterCommitID}
+ _, actualBeforeCommitID, err := guessBeforeCommitForDiff(gitRepo, beforeCommitID, afterCommit)
+ if err != nil {
+ return nil, err
}
- diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...)
- if err != nil && strings.Contains(err.Error(), "no merge base") {
- // git >= 2.28 now returns an error if base and head have become unrelated.
- // previously it would return the results of git diff --shortstat base head so let's try that...
- diffPaths = []string{opts.BeforeCommitID, opts.AfterCommitID}
- diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...)
- }
+ diff := &DiffShortStat{}
+ diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStatByCmdArgs(gitRepo.Ctx, repoPath, nil, actualBeforeCommitID.String(), afterCommitID)
if err != nil {
return nil, err
}
-
return diff, nil
}
-// SyncAndGetUserSpecificDiff is like GetDiff, except that user specific data such as which files the given user has already viewed on the given PR will also be set
-// Additionally, the database asynchronously is updated if files have changed since the last review
-func SyncAndGetUserSpecificDiff(ctx context.Context, userID int64, pull *issues_model.PullRequest, gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff, error) {
- diff, err := GetDiff(ctx, gitRepo, opts, files...)
+// SyncUserSpecificDiff inserts user-specific data such as which files the user has already viewed on the given diff
+// Additionally, the database is updated asynchronously if files have changed since the last review
+func SyncUserSpecificDiff(ctx context.Context, userID int64, pull *issues_model.PullRequest, gitRepo *git.Repository, diff *Diff, opts *DiffOptions) (*pull_model.ReviewState, error) {
+ review, err := pull_model.GetNewestReviewState(ctx, userID, pull.ID)
if err != nil {
return nil, err
}
- review, err := pull_model.GetNewestReviewState(ctx, userID, pull.ID)
- if err != nil || review == nil || review.UpdatedFiles == nil {
- return diff, err
+ if review == nil || len(review.UpdatedFiles) == 0 {
+ return review, nil
}
latestCommit := opts.AfterCommitID
@@ -1281,13 +1303,13 @@ func SyncAndGetUserSpecificDiff(ctx context.Context, userID int64, pull *issues_
latestCommit = pull.HeadBranch // opts.AfterCommitID is preferred because it handles PRs from forks correctly and the branch name doesn't
}
- changedFiles, err := gitRepo.GetFilesChangedBetween(review.CommitSHA, latestCommit)
+ changedFiles, errIgnored := gitRepo.GetFilesChangedBetween(review.CommitSHA, latestCommit)
// There are way too many possible errors.
// Examples are various git errors such as the commit the review was based on was gc'ed and hence doesn't exist anymore as well as unrecoverable errors where we should serve a 500 response
// Due to the current architecture and physical limitation of needing to compare explicit error messages, we can only choose one approach without the code getting ugly
// For SOME of the errors such as the gc'ed commit, it would be best to mark all files as changed
// But as that does not work for all potential errors, we simply mark all files as unchanged and drop the error which always works, even if not as good as possible
- if err != nil {
+ if errIgnored != nil {
log.Error("Could not get changed files between %s and %s for pull request %d in repo with path %s. Assuming no changes. Error: %w", review.CommitSHA, latestCommit, pull.Index, gitRepo.Path, err)
}
@@ -1330,7 +1352,7 @@ outer:
}
}
- return diff, nil
+ return review, nil
}
// CommentAsDiff returns c.Patch as *Diff
@@ -1376,10 +1398,8 @@ func GetWhitespaceFlag(whitespaceBehavior string) git.TrustedCmdArgs {
"ignore-eol": {"--ignore-space-at-eol"},
"show-all": nil,
}
-
if flag, ok := whitespaceFlags[whitespaceBehavior]; ok {
return flag
}
- log.Warn("unknown whitespace behavior: %q, default to 'show-all'", whitespaceBehavior)
return nil
}
diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go
index 2351c5da87..b84530043a 100644
--- a/services/gitdiff/gitdiff_test.go
+++ b/services/gitdiff/gitdiff_test.go
@@ -17,26 +17,10 @@ import (
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
- dmp "github.com/sergi/go-diff/diffmatchpatch"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
-func TestDiffToHTML(t *testing.T) {
- assert.Equal(t, "foo <span class=\"added-code\">bar</span> biz", diffToHTML(nil, []dmp.Diff{
- {Type: dmp.DiffEqual, Text: "foo "},
- {Type: dmp.DiffInsert, Text: "bar"},
- {Type: dmp.DiffDelete, Text: " baz"},
- {Type: dmp.DiffEqual, Text: " biz"},
- }, DiffLineAdd))
-
- assert.Equal(t, "foo <span class=\"removed-code\">bar</span> biz", diffToHTML(nil, []dmp.Diff{
- {Type: dmp.DiffEqual, Text: "foo "},
- {Type: dmp.DiffDelete, Text: "bar"},
- {Type: dmp.DiffInsert, Text: " baz"},
- {Type: dmp.DiffEqual, Text: " biz"},
- }, DiffLineDel))
-}
-
func TestParsePatch_skipTo(t *testing.T) {
type testcase struct {
name string
@@ -181,16 +165,10 @@ diff --git "\\a/README.md" "\\b/README.md"
}
gotMarshaled, _ := json.MarshalIndent(got, "", " ")
- if got.NumFiles != 1 {
+ if len(got.Files) != 1 {
t.Errorf("ParsePath(%q) did not receive 1 file:\n%s", testcase.name, string(gotMarshaled))
return
}
- if got.TotalAddition != testcase.addition {
- t.Errorf("ParsePath(%q) does not have correct totalAddition %d, wanted %d", testcase.name, got.TotalAddition, testcase.addition)
- }
- if got.TotalDeletion != testcase.deletion {
- t.Errorf("ParsePath(%q) did not have correct totalDeletion %d, wanted %d", testcase.name, got.TotalDeletion, testcase.deletion)
- }
file := got.Files[0]
if file.Addition != testcase.addition {
t.Errorf("ParsePath(%q) does not have correct file addition %d, wanted %d", testcase.name, file.Addition, testcase.addition)
@@ -406,16 +384,10 @@ index 6961180..9ba1a00 100644
}
gotMarshaled, _ := json.MarshalIndent(got, "", " ")
- if got.NumFiles != 1 {
+ if len(got.Files) != 1 {
t.Errorf("ParsePath(%q) did not receive 1 file:\n%s", testcase.name, string(gotMarshaled))
return
}
- if got.TotalAddition != testcase.addition {
- t.Errorf("ParsePath(%q) does not have correct totalAddition %d, wanted %d", testcase.name, got.TotalAddition, testcase.addition)
- }
- if got.TotalDeletion != testcase.deletion {
- t.Errorf("ParsePath(%q) did not have correct totalDeletion %d, wanted %d", testcase.name, got.TotalDeletion, testcase.deletion)
- }
file := got.Files[0]
if file.Addition != testcase.addition {
t.Errorf("ParsePath(%q) does not have correct file addition %d, wanted %d", testcase.name, file.Addition, testcase.addition)
@@ -444,7 +416,7 @@ index 0000000..6bb8f39
`
diffBuilder.WriteString(diff)
- for i := 0; i < 35; i++ {
+ for i := range 35 {
diffBuilder.WriteString("+line" + strconv.Itoa(i) + "\n")
}
diff = diffBuilder.String()
@@ -481,11 +453,11 @@ index 0000000..6bb8f39
diffBuilder.Reset()
diffBuilder.WriteString(diff)
- for i := 0; i < 33; i++ {
+ for i := range 33 {
diffBuilder.WriteString("+line" + strconv.Itoa(i) + "\n")
}
diffBuilder.WriteString("+line33")
- for i := 0; i < 512; i++ {
+ for range 512 {
diffBuilder.WriteString("0123456789ABCDEF")
}
diffBuilder.WriteByte('\n')
@@ -627,24 +599,25 @@ func TestDiffLine_GetCommentSide(t *testing.T) {
}
func TestGetDiffRangeWithWhitespaceBehavior(t *testing.T) {
- gitRepo, err := git.OpenRepository(git.DefaultContext, "./testdata/academic-module")
- if !assert.NoError(t, err) {
- return
- }
+ gitRepo, err := git.OpenRepository(t.Context(), "../../modules/git/tests/repos/repo5_pulls")
+ require.NoError(t, err)
+
defer gitRepo.Close()
for _, behavior := range []git.TrustedCmdArgs{{"-w"}, {"--ignore-space-at-eol"}, {"-b"}, nil} {
- diffs, err := GetDiff(db.DefaultContext, gitRepo,
+ diffs, err := GetDiffForAPI(t.Context(), gitRepo,
&DiffOptions{
- AfterCommitID: "bd7063cc7c04689c4d082183d32a604ed27a24f9",
- BeforeCommitID: "559c156f8e0178b71cb44355428f24001b08fc68",
+ AfterCommitID: "d8e0bbb45f200e67d9a784ce55bd90821af45ebd",
+ BeforeCommitID: "72866af952e98d02a73003501836074b286a78f6",
MaxLines: setting.Git.MaxGitDiffLines,
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
- MaxFiles: setting.Git.MaxGitDiffFiles,
+ MaxFiles: 1,
WhitespaceBehavior: behavior,
})
- assert.NoError(t, err, "Error when diff with %s", behavior)
+ require.NoError(t, err, "Error when diff with WhitespaceBehavior=%s", behavior)
+ assert.True(t, diffs.IsIncomplete)
+ assert.Len(t, diffs.Files, 1)
for _, f := range diffs.Files {
- assert.NotEmpty(t, f.Sections, "%s should have sections", f.Name)
+ assert.NotEmpty(t, f.Sections, "Diff file %q should have sections", f.Name)
}
}
}
diff --git a/services/gitdiff/highlightdiff.go b/services/gitdiff/highlightdiff.go
index 35d4844550..e8be063e69 100644
--- a/services/gitdiff/highlightdiff.go
+++ b/services/gitdiff/highlightdiff.go
@@ -4,23 +4,24 @@
package gitdiff
import (
+ "bytes"
+ "html/template"
"strings"
- "code.gitea.io/gitea/modules/highlight"
-
"github.com/sergi/go-diff/diffmatchpatch"
)
// token is a html tag or entity, eg: "<span ...>", "</span>", "&lt;"
func extractHTMLToken(s string) (before, token, after string, valid bool) {
for pos1 := 0; pos1 < len(s); pos1++ {
- if s[pos1] == '<' {
+ switch s[pos1] {
+ case '<':
pos2 := strings.IndexByte(s[pos1:], '>')
if pos2 == -1 {
return "", "", s, false
}
return s[:pos1], s[pos1 : pos1+pos2+1], s[pos1+pos2+1:], true
- } else if s[pos1] == '&' {
+ case '&':
pos2 := strings.IndexByte(s[pos1:], ';')
if pos2 == -1 {
return "", "", s, false
@@ -77,7 +78,7 @@ func (hcd *highlightCodeDiff) isInPlaceholderRange(r rune) bool {
return hcd.placeholderBegin <= r && r < hcd.placeholderBegin+rune(hcd.placeholderMaxCount)
}
-func (hcd *highlightCodeDiff) collectUsedRunes(code string) {
+func (hcd *highlightCodeDiff) collectUsedRunes(code template.HTML) {
for _, r := range code {
if hcd.isInPlaceholderRange(r) {
// put the existing rune (used by code) in map, then this rune won't be used a placeholder anymore.
@@ -86,27 +87,76 @@ func (hcd *highlightCodeDiff) collectUsedRunes(code string) {
}
}
-func (hcd *highlightCodeDiff) diffWithHighlight(filename, language, codeA, codeB string) []diffmatchpatch.Diff {
+func (hcd *highlightCodeDiff) diffLineWithHighlight(lineType DiffLineType, codeA, codeB template.HTML) template.HTML {
+ return hcd.diffLineWithHighlightWrapper(nil, lineType, codeA, codeB)
+}
+
+func (hcd *highlightCodeDiff) diffLineWithHighlightWrapper(lineWrapperTags []string, lineType DiffLineType, codeA, codeB template.HTML) template.HTML {
hcd.collectUsedRunes(codeA)
hcd.collectUsedRunes(codeB)
- highlightCodeA, _ := highlight.Code(filename, language, codeA)
- highlightCodeB, _ := highlight.Code(filename, language, codeB)
+ convertedCodeA := hcd.convertToPlaceholders(codeA)
+ convertedCodeB := hcd.convertToPlaceholders(codeB)
+
+ dmp := defaultDiffMatchPatch()
+ diffs := dmp.DiffMain(convertedCodeA, convertedCodeB, true)
+ diffs = dmp.DiffCleanupSemantic(diffs)
+
+ buf := bytes.NewBuffer(nil)
- convertedCodeA := hcd.convertToPlaceholders(string(highlightCodeA))
- convertedCodeB := hcd.convertToPlaceholders(string(highlightCodeB))
+ // restore the line wrapper tags <span class="line"> and <span class="cl">, if necessary
+ for _, tag := range lineWrapperTags {
+ buf.WriteString(tag)
+ }
- diffs := diffMatchPatch.DiffMain(convertedCodeA, convertedCodeB, true)
- diffs = diffMatchPatch.DiffCleanupEfficiency(diffs)
+ addedCodePrefix := hcd.registerTokenAsPlaceholder(`<span class="added-code">`)
+ removedCodePrefix := hcd.registerTokenAsPlaceholder(`<span class="removed-code">`)
+ codeTagSuffix := hcd.registerTokenAsPlaceholder(`</span>`)
- for i := range diffs {
- hcd.recoverOneDiff(&diffs[i])
+ if codeTagSuffix != 0 {
+ for _, diff := range diffs {
+ switch {
+ case diff.Type == diffmatchpatch.DiffEqual:
+ buf.WriteString(diff.Text)
+ case diff.Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd:
+ buf.WriteRune(addedCodePrefix)
+ buf.WriteString(diff.Text)
+ buf.WriteRune(codeTagSuffix)
+ case diff.Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel:
+ buf.WriteRune(removedCodePrefix)
+ buf.WriteString(diff.Text)
+ buf.WriteRune(codeTagSuffix)
+ }
+ }
+ } else {
+ // placeholder map space is exhausted
+ for _, diff := range diffs {
+ take := diff.Type == diffmatchpatch.DiffEqual || (diff.Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd) || (diff.Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel)
+ if take {
+ buf.WriteString(diff.Text)
+ }
+ }
}
- return diffs
+ for range lineWrapperTags {
+ buf.WriteString("</span>")
+ }
+ return hcd.recoverOneDiff(buf.String())
+}
+
+func (hcd *highlightCodeDiff) registerTokenAsPlaceholder(token string) rune {
+ placeholder, ok := hcd.tokenPlaceholderMap[token]
+ if !ok {
+ placeholder = hcd.nextPlaceholder()
+ if placeholder != 0 {
+ hcd.tokenPlaceholderMap[token] = placeholder
+ hcd.placeholderTokenMap[placeholder] = token
+ }
+ }
+ return placeholder
}
// convertToPlaceholders totally depends on Chroma's valid HTML output and its structure, do not use these functions for other purposes.
-func (hcd *highlightCodeDiff) convertToPlaceholders(htmlCode string) string {
+func (hcd *highlightCodeDiff) convertToPlaceholders(htmlContent template.HTML) string {
var tagStack []string
res := strings.Builder{}
@@ -115,6 +165,7 @@ func (hcd *highlightCodeDiff) convertToPlaceholders(htmlCode string) string {
var beforeToken, token string
var valid bool
+ htmlCode := string(htmlContent)
// the standard chroma highlight HTML is "<span class="line [hl]"><span class="cl"> ... </span></span>"
for {
beforeToken, token, htmlCode, valid = extractHTMLToken(htmlCode)
@@ -151,14 +202,7 @@ func (hcd *highlightCodeDiff) convertToPlaceholders(htmlCode string) string {
} // else: impossible
// remember the placeholder and token in the map
- placeholder, ok := hcd.tokenPlaceholderMap[tokenInMap]
- if !ok {
- placeholder = hcd.nextPlaceholder()
- if placeholder != 0 {
- hcd.tokenPlaceholderMap[tokenInMap] = placeholder
- hcd.placeholderTokenMap[placeholder] = tokenInMap
- }
- }
+ placeholder := hcd.registerTokenAsPlaceholder(tokenInMap)
if placeholder != 0 {
res.WriteRune(placeholder) // use the placeholder to replace the token
@@ -179,11 +223,11 @@ func (hcd *highlightCodeDiff) convertToPlaceholders(htmlCode string) string {
return res.String()
}
-func (hcd *highlightCodeDiff) recoverOneDiff(diff *diffmatchpatch.Diff) {
+func (hcd *highlightCodeDiff) recoverOneDiff(str string) template.HTML {
sb := strings.Builder{}
var tagStack []string
- for _, r := range diff.Text {
+ for _, r := range str {
token, ok := hcd.placeholderTokenMap[r]
if !ok || token == "" {
sb.WriteRune(r) // if the rune is not a placeholder, write it as it is
@@ -217,6 +261,5 @@ func (hcd *highlightCodeDiff) recoverOneDiff(diff *diffmatchpatch.Diff) {
} // else: impossible. every tag was pushed into the stack by the code above and is valid HTML opening tag
}
}
-
- diff.Text = sb.String()
+ return template.HTML(sb.String())
}
diff --git a/services/gitdiff/highlightdiff_test.go b/services/gitdiff/highlightdiff_test.go
index 545a060e20..aebe38ae7c 100644
--- a/services/gitdiff/highlightdiff_test.go
+++ b/services/gitdiff/highlightdiff_test.go
@@ -5,121 +5,82 @@ package gitdiff
import (
"fmt"
+ "html/template"
"strings"
"testing"
- "github.com/sergi/go-diff/diffmatchpatch"
"github.com/stretchr/testify/assert"
)
func TestDiffWithHighlight(t *testing.T) {
- hcd := newHighlightCodeDiff()
- diffs := hcd.diffWithHighlight(
- "main.v", "",
- " run('<>')\n",
- " run(db)\n",
- )
-
- expected := ` <span class="n">run</span><span class="o">(</span><span class="removed-code"><span class="k">&#39;</span><span class="o">&lt;</span><span class="o">&gt;</span><span class="k">&#39;</span></span><span class="o">)</span>`
- output := diffToHTML(nil, diffs, DiffLineDel)
- assert.Equal(t, expected, output)
-
- expected = ` <span class="n">run</span><span class="o">(</span><span class="added-code"><span class="n">db</span></span><span class="o">)</span>`
- output = diffToHTML(nil, diffs, DiffLineAdd)
- assert.Equal(t, expected, output)
-
- hcd = newHighlightCodeDiff()
- hcd.placeholderTokenMap['O'] = "<span>"
- hcd.placeholderTokenMap['C'] = "</span>"
- diff := diffmatchpatch.Diff{}
-
- diff.Text = "OC"
- hcd.recoverOneDiff(&diff)
- assert.Equal(t, "<span></span>", diff.Text)
-
- diff.Text = "O"
- hcd.recoverOneDiff(&diff)
- assert.Equal(t, "<span></span>", diff.Text)
-
- diff.Text = "C"
- hcd.recoverOneDiff(&diff)
- assert.Equal(t, "", diff.Text)
+ t.Run("DiffLineAddDel", func(t *testing.T) {
+ hcd := newHighlightCodeDiff()
+ codeA := template.HTML(`x <span class="k">foo</span> y`)
+ codeB := template.HTML(`x <span class="k">bar</span> y`)
+ outDel := hcd.diffLineWithHighlight(DiffLineDel, codeA, codeB)
+ assert.Equal(t, `x <span class="k"><span class="removed-code">foo</span></span> y`, string(outDel))
+ outAdd := hcd.diffLineWithHighlight(DiffLineAdd, codeA, codeB)
+ assert.Equal(t, `x <span class="k"><span class="added-code">bar</span></span> y`, string(outAdd))
+ })
+
+ t.Run("CleanUp", func(t *testing.T) {
+ hcd := newHighlightCodeDiff()
+ codeA := template.HTML(`<span class="cm>this is a comment</span>`)
+ codeB := template.HTML(`<span class="cm>this is updated comment</span>`)
+ outDel := hcd.diffLineWithHighlight(DiffLineDel, codeA, codeB)
+ assert.Equal(t, `<span class="cm>this is <span class="removed-code">a</span> comment</span>`, string(outDel))
+ outAdd := hcd.diffLineWithHighlight(DiffLineAdd, codeA, codeB)
+ assert.Equal(t, `<span class="cm>this is <span class="added-code">updated</span> comment</span>`, string(outAdd))
+ })
+
+ t.Run("OpenCloseTags", func(t *testing.T) {
+ hcd := newHighlightCodeDiff()
+ hcd.placeholderTokenMap['O'], hcd.placeholderTokenMap['C'] = "<span>", "</span>"
+ assert.Equal(t, "<span></span>", string(hcd.recoverOneDiff("OC")))
+ assert.Equal(t, "<span></span>", string(hcd.recoverOneDiff("O")))
+ assert.Empty(t, string(hcd.recoverOneDiff("C")))
+ })
}
func TestDiffWithHighlightPlaceholder(t *testing.T) {
hcd := newHighlightCodeDiff()
- diffs := hcd.diffWithHighlight(
- "main.js", "",
- "a='\U00100000'",
- "a='\U0010FFFD''",
- )
- assert.Equal(t, "", hcd.placeholderTokenMap[0x00100000])
- assert.Equal(t, "", hcd.placeholderTokenMap[0x0010FFFD])
-
- expected := fmt.Sprintf(`<span class="nx">a</span><span class="o">=</span><span class="s1">&#39;</span><span class="removed-code">%s</span>&#39;`, "\U00100000")
- output := diffToHTML(hcd.lineWrapperTags, diffs, DiffLineDel)
- assert.Equal(t, expected, output)
+ output := hcd.diffLineWithHighlight(DiffLineDel, "a='\U00100000'", "a='\U0010FFFD''")
+ assert.Empty(t, hcd.placeholderTokenMap[0x00100000])
+ assert.Empty(t, hcd.placeholderTokenMap[0x0010FFFD])
+ expected := fmt.Sprintf(`a='<span class="removed-code">%s</span>'`, "\U00100000")
+ assert.Equal(t, expected, string(output))
hcd = newHighlightCodeDiff()
- diffs = hcd.diffWithHighlight(
- "main.js", "",
- "a='\U00100000'",
- "a='\U0010FFFD'",
- )
- expected = fmt.Sprintf(`<span class="nx">a</span><span class="o">=</span><span class="s1">&#39;</span><span class="added-code">%s</span>&#39;`, "\U0010FFFD")
- output = diffToHTML(nil, diffs, DiffLineAdd)
- assert.Equal(t, expected, output)
+ output = hcd.diffLineWithHighlight(DiffLineAdd, "a='\U00100000'", "a='\U0010FFFD'")
+ expected = fmt.Sprintf(`a='<span class="added-code">%s</span>'`, "\U0010FFFD")
+ assert.Equal(t, expected, string(output))
}
func TestDiffWithHighlightPlaceholderExhausted(t *testing.T) {
hcd := newHighlightCodeDiff()
hcd.placeholderMaxCount = 0
- diffs := hcd.diffWithHighlight(
- "main.js", "",
- "'",
- ``,
- )
- output := diffToHTML(nil, diffs, DiffLineDel)
- expected := fmt.Sprintf(`<span class="removed-code">%s#39;</span>`, "\uFFFD")
- assert.Equal(t, expected, output)
-
- hcd = newHighlightCodeDiff()
- hcd.placeholderMaxCount = 0
- diffs = hcd.diffWithHighlight(
- "main.js", "",
- "a < b",
- "a > b",
- )
- output = diffToHTML(nil, diffs, DiffLineDel)
- expected = fmt.Sprintf(`a %s<span class="removed-code">l</span>t; b`, "\uFFFD")
- assert.Equal(t, expected, output)
-
- output = diffToHTML(nil, diffs, DiffLineAdd)
- expected = fmt.Sprintf(`a %s<span class="added-code">g</span>t; b`, "\uFFFD")
- assert.Equal(t, expected, output)
+ placeHolderAmp := string(rune(0xFFFD))
+ output := hcd.diffLineWithHighlight(DiffLineDel, `<span class="k">&lt;</span>`, `<span class="k">&gt;</span>`)
+ assert.Equal(t, placeHolderAmp+"lt;", string(output))
+ output = hcd.diffLineWithHighlight(DiffLineAdd, `<span class="k">&lt;</span>`, `<span class="k">&gt;</span>`)
+ assert.Equal(t, placeHolderAmp+"gt;", string(output))
}
func TestDiffWithHighlightTagMatch(t *testing.T) {
- totalOverflow := 0
- for i := 0; i < 100; i++ {
- hcd := newHighlightCodeDiff()
- hcd.placeholderMaxCount = i
- diffs := hcd.diffWithHighlight(
- "main.js", "",
- "a='1'",
- "b='2'",
- )
- totalOverflow += hcd.placeholderOverflowCount
-
- output := diffToHTML(nil, diffs, DiffLineDel)
- c1 := strings.Count(output, "<span")
- c2 := strings.Count(output, "</span")
- assert.Equal(t, c1, c2)
-
- output = diffToHTML(nil, diffs, DiffLineAdd)
- c1 = strings.Count(output, "<span")
- c2 = strings.Count(output, "</span")
- assert.Equal(t, c1, c2)
+ f := func(t *testing.T, lineType DiffLineType) {
+ totalOverflow := 0
+ for i := 0; ; i++ {
+ hcd := newHighlightCodeDiff()
+ hcd.placeholderMaxCount = i
+ output := string(hcd.diffLineWithHighlight(lineType, `<span class="k">&lt;</span>`, `<span class="k">&gt;</span>`))
+ totalOverflow += hcd.placeholderOverflowCount
+ assert.Equal(t, strings.Count(output, "<span"), strings.Count(output, "</span"))
+ if hcd.placeholderOverflowCount == 0 {
+ break
+ }
+ }
+ assert.NotZero(t, totalOverflow)
}
- assert.NotZero(t, totalOverflow)
+ t.Run("DiffLineAdd", func(t *testing.T) { f(t, DiffLineAdd) })
+ t.Run("DiffLineDel", func(t *testing.T) { f(t, DiffLineDel) })
}
diff --git a/services/gitdiff/submodule.go b/services/gitdiff/submodule.go
new file mode 100644
index 0000000000..02ca666544
--- /dev/null
+++ b/services/gitdiff/submodule.go
@@ -0,0 +1,65 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package gitdiff
+
+import (
+ "context"
+ "html/template"
+
+ "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/htmlutil"
+ "code.gitea.io/gitea/modules/log"
+)
+
+type SubmoduleDiffInfo struct {
+ SubmoduleName string
+ SubmoduleFile *git.CommitSubmoduleFile // it might be nil if the submodule is not found or unable to parse
+ NewRefID string
+ PreviousRefID string
+}
+
+func (si *SubmoduleDiffInfo) PopulateURL(diffFile *DiffFile, leftCommit, rightCommit *git.Commit) {
+ si.SubmoduleName = diffFile.Name
+ submoduleCommit := rightCommit // If the submodule is added or updated, check at the right commit
+ if diffFile.IsDeleted {
+ submoduleCommit = leftCommit // If the submodule is deleted, check at the left commit
+ }
+ if submoduleCommit == nil {
+ return
+ }
+
+ submodule, err := submoduleCommit.GetSubModule(diffFile.GetDiffFileName())
+ if err != nil {
+ log.Error("Unable to PopulateURL for submodule %q: GetSubModule: %v", diffFile.GetDiffFileName(), err)
+ return // ignore the error, do not cause 500 errors for end users
+ }
+ if submodule != nil {
+ si.SubmoduleFile = git.NewCommitSubmoduleFile(submodule.URL, submoduleCommit.ID.String())
+ }
+}
+
+func (si *SubmoduleDiffInfo) CommitRefIDLinkHTML(ctx context.Context, commitID string) template.HTML {
+ webLink := si.SubmoduleFile.SubmoduleWebLink(ctx, commitID)
+ if webLink == nil {
+ return htmlutil.HTMLFormat("%s", base.ShortSha(commitID))
+ }
+ return htmlutil.HTMLFormat(`<a href="%s">%s</a>`, webLink.CommitWebLink, base.ShortSha(commitID))
+}
+
+func (si *SubmoduleDiffInfo) CompareRefIDLinkHTML(ctx context.Context) template.HTML {
+ webLink := si.SubmoduleFile.SubmoduleWebLink(ctx, si.PreviousRefID, si.NewRefID)
+ if webLink == nil {
+ return htmlutil.HTMLFormat("%s...%s", base.ShortSha(si.PreviousRefID), base.ShortSha(si.NewRefID))
+ }
+ return htmlutil.HTMLFormat(`<a href="%s">%s...%s</a>`, webLink.CommitWebLink, base.ShortSha(si.PreviousRefID), base.ShortSha(si.NewRefID))
+}
+
+func (si *SubmoduleDiffInfo) SubmoduleRepoLinkHTML(ctx context.Context) template.HTML {
+ webLink := si.SubmoduleFile.SubmoduleWebLink(ctx)
+ if webLink == nil {
+ return htmlutil.HTMLFormat("%s", si.SubmoduleName)
+ }
+ return htmlutil.HTMLFormat(`<a href="%s">%s</a>`, webLink.RepoWebLink, si.SubmoduleName)
+}
diff --git a/services/gitdiff/submodule_test.go b/services/gitdiff/submodule_test.go
new file mode 100644
index 0000000000..152c5b7066
--- /dev/null
+++ b/services/gitdiff/submodule_test.go
@@ -0,0 +1,234 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package gitdiff
+
+import (
+ "strings"
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestParseSubmoduleInfo(t *testing.T) {
+ type testcase struct {
+ name string
+ gitdiff string
+ infos map[int]SubmoduleDiffInfo
+ }
+
+ tests := []testcase{
+ {
+ name: "added",
+ gitdiff: `diff --git a/.gitmodules b/.gitmodules
+new file mode 100644
+index 0000000..4ac13c1
+--- /dev/null
++++ b/.gitmodules
+@@ -0,0 +1,3 @@
++[submodule "gitea-mirror"]
++ path = gitea-mirror
++ url = https://gitea.com/gitea/gitea-mirror
+diff --git a/gitea-mirror b/gitea-mirror
+new file mode 160000
+index 0000000..68972a9
+--- /dev/null
++++ b/gitea-mirror
+@@ -0,0 +1 @@
++Subproject commit 68972a994719ae5c74e28d8fa82fa27c23399bc8
+`,
+ infos: map[int]SubmoduleDiffInfo{
+ 1: {NewRefID: "68972a994719ae5c74e28d8fa82fa27c23399bc8"},
+ },
+ },
+ {
+ name: "updated",
+ gitdiff: `diff --git a/gitea-mirror b/gitea-mirror
+index 68972a9..c8ffe77 160000
+--- a/gitea-mirror
++++ b/gitea-mirror
+@@ -1 +1 @@
+-Subproject commit 68972a994719ae5c74e28d8fa82fa27c23399bc8
++Subproject commit c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d
+`,
+ infos: map[int]SubmoduleDiffInfo{
+ 0: {
+ PreviousRefID: "68972a994719ae5c74e28d8fa82fa27c23399bc8",
+ NewRefID: "c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d",
+ },
+ },
+ },
+ {
+ name: "rename",
+ gitdiff: `diff --git a/.gitmodules b/.gitmodules
+index 4ac13c1..0510edd 100644
+--- a/.gitmodules
++++ b/.gitmodules
+@@ -1,3 +1,3 @@
+ [submodule "gitea-mirror"]
+- path = gitea-mirror
++ path = gitea
+ url = https://gitea.com/gitea/gitea-mirror
+diff --git a/gitea-mirror b/gitea
+similarity index 100%
+rename from gitea-mirror
+rename to gitea
+`,
+ },
+ {
+ name: "deleted",
+ gitdiff: `diff --git a/.gitmodules b/.gitmodules
+index 0510edd..e69de29 100644
+--- a/.gitmodules
++++ b/.gitmodules
+@@ -1,3 +0,0 @@
+-[submodule "gitea-mirror"]
+- path = gitea
+- url = https://gitea.com/gitea/gitea-mirror
+diff --git a/gitea b/gitea
+deleted file mode 160000
+index c8ffe77..0000000
+--- a/gitea
++++ /dev/null
+@@ -1 +0,0 @@
+-Subproject commit c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d
+`,
+ infos: map[int]SubmoduleDiffInfo{
+ 1: {
+ PreviousRefID: "c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d",
+ },
+ },
+ },
+ {
+ name: "moved and updated",
+ gitdiff: `diff --git a/.gitmodules b/.gitmodules
+index 0510edd..bced3d8 100644
+--- a/.gitmodules
++++ b/.gitmodules
+@@ -1,3 +1,3 @@
+ [submodule "gitea-mirror"]
+- path = gitea
++ path = gitea-1.22
+ url = https://gitea.com/gitea/gitea-mirror
+diff --git a/gitea b/gitea
+deleted file mode 160000
+index c8ffe77..0000000
+--- a/gitea
++++ /dev/null
+@@ -1 +0,0 @@
+-Subproject commit c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d
+diff --git a/gitea-1.22 b/gitea-1.22
+new file mode 160000
+index 0000000..8eefa1f
+--- /dev/null
++++ b/gitea-1.22
+@@ -0,0 +1 @@
++Subproject commit 8eefa1f6dedf2488db2c9e12c916e8e51f673160
+`,
+ infos: map[int]SubmoduleDiffInfo{
+ 1: {
+ PreviousRefID: "c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d",
+ },
+ 2: {
+ NewRefID: "8eefa1f6dedf2488db2c9e12c916e8e51f673160",
+ },
+ },
+ },
+ {
+ name: "converted to file",
+ gitdiff: `diff --git a/.gitmodules b/.gitmodules
+index 0510edd..e69de29 100644
+--- a/.gitmodules
++++ b/.gitmodules
+@@ -1,3 +0,0 @@
+-[submodule "gitea-mirror"]
+- path = gitea
+- url = https://gitea.com/gitea/gitea-mirror
+diff --git a/gitea b/gitea
+deleted file mode 160000
+index c8ffe77..0000000
+--- a/gitea
++++ /dev/null
+@@ -1 +0,0 @@
+-Subproject commit c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d
+diff --git a/gitea b/gitea
+new file mode 100644
+index 0000000..33a9488
+--- /dev/null
++++ b/gitea
+@@ -0,0 +1 @@
++example
+`,
+ infos: map[int]SubmoduleDiffInfo{
+ 1: {
+ PreviousRefID: "c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d",
+ },
+ },
+ },
+ {
+ name: "converted to submodule",
+ gitdiff: `diff --git a/.gitmodules b/.gitmodules
+index e69de29..14ee267 100644
+--- a/.gitmodules
++++ b/.gitmodules
+@@ -0,0 +1,3 @@
++[submodule "gitea"]
++ path = gitea
++ url = https://gitea.com/gitea/gitea-mirror
+diff --git a/gitea b/gitea
+deleted file mode 100644
+index 33a9488..0000000
+--- a/gitea
++++ /dev/null
+@@ -1 +0,0 @@
+-example
+diff --git a/gitea b/gitea
+new file mode 160000
+index 0000000..68972a9
+--- /dev/null
++++ b/gitea
+@@ -0,0 +1 @@
++Subproject commit 68972a994719ae5c74e28d8fa82fa27c23399bc8
+`,
+ infos: map[int]SubmoduleDiffInfo{
+ 2: {
+ NewRefID: "68972a994719ae5c74e28d8fa82fa27c23399bc8",
+ },
+ },
+ },
+ }
+
+ for _, testcase := range tests {
+ t.Run(testcase.name, func(t *testing.T) {
+ diff, err := ParsePatch(db.DefaultContext, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(testcase.gitdiff), "")
+ assert.NoError(t, err)
+
+ for i, expected := range testcase.infos {
+ actual := diff.Files[i]
+ assert.NotNil(t, actual)
+ assert.Equal(t, expected, *actual.SubmoduleDiffInfo)
+ }
+ })
+ }
+}
+
+func TestSubmoduleInfo(t *testing.T) {
+ sdi := &SubmoduleDiffInfo{
+ SubmoduleName: "name",
+ PreviousRefID: "aaaa",
+ NewRefID: "bbbb",
+ }
+ ctx := t.Context()
+ assert.EqualValues(t, "1111", sdi.CommitRefIDLinkHTML(ctx, "1111"))
+ assert.EqualValues(t, "aaaa...bbbb", sdi.CompareRefIDLinkHTML(ctx))
+ assert.EqualValues(t, "name", sdi.SubmoduleRepoLinkHTML(ctx))
+
+ sdi.SubmoduleFile = git.NewCommitSubmoduleFile("https://github.com/owner/repo", "1234")
+ assert.EqualValues(t, `<a href="https://github.com/owner/repo/tree/1111">1111</a>`, sdi.CommitRefIDLinkHTML(ctx, "1111"))
+ assert.EqualValues(t, `<a href="https://github.com/owner/repo/compare/aaaa...bbbb">aaaa...bbbb</a>`, sdi.CompareRefIDLinkHTML(ctx))
+ assert.EqualValues(t, `<a href="https://github.com/owner/repo">name</a>`, sdi.SubmoduleRepoLinkHTML(ctx))
+}
diff --git a/services/gitdiff/testdata/academic-module/HEAD b/services/gitdiff/testdata/academic-module/HEAD
deleted file mode 100644
index cb089cd89a..0000000000
--- a/services/gitdiff/testdata/academic-module/HEAD
+++ /dev/null
@@ -1 +0,0 @@
-ref: refs/heads/master
diff --git a/services/gitdiff/testdata/academic-module/config b/services/gitdiff/testdata/academic-module/config
deleted file mode 100644
index 1bc26be514..0000000000
--- a/services/gitdiff/testdata/academic-module/config
+++ /dev/null
@@ -1,10 +0,0 @@
-[core]
- repositoryformatversion = 0
- filemode = true
- bare = false
- logallrefupdates = true
- ignorecase = true
- precomposeunicode = true
-[branch "master"]
- remote = origin
- merge = refs/heads/master
diff --git a/services/gitdiff/testdata/academic-module/index b/services/gitdiff/testdata/academic-module/index
deleted file mode 100644
index e712c906e3..0000000000
--- a/services/gitdiff/testdata/academic-module/index
+++ /dev/null
Binary files differ
diff --git a/services/gitdiff/testdata/academic-module/logs/HEAD b/services/gitdiff/testdata/academic-module/logs/HEAD
deleted file mode 100644
index 16b2e1c0f6..0000000000
--- a/services/gitdiff/testdata/academic-module/logs/HEAD
+++ /dev/null
@@ -1 +0,0 @@
-0000000000000000000000000000000000000000 bd7063cc7c04689c4d082183d32a604ed27a24f9 Lunny Xiao <xiaolunwen@gmail.com> 1574829684 +0800 clone: from https://try.gitea.io/shemgp-aiias/academic-module
diff --git a/services/gitdiff/testdata/academic-module/logs/refs/heads/master b/services/gitdiff/testdata/academic-module/logs/refs/heads/master
deleted file mode 100644
index 16b2e1c0f6..0000000000
--- a/services/gitdiff/testdata/academic-module/logs/refs/heads/master
+++ /dev/null
@@ -1 +0,0 @@
-0000000000000000000000000000000000000000 bd7063cc7c04689c4d082183d32a604ed27a24f9 Lunny Xiao <xiaolunwen@gmail.com> 1574829684 +0800 clone: from https://try.gitea.io/shemgp-aiias/academic-module
diff --git a/services/gitdiff/testdata/academic-module/logs/refs/remotes/origin/HEAD b/services/gitdiff/testdata/academic-module/logs/refs/remotes/origin/HEAD
deleted file mode 100644
index 16b2e1c0f6..0000000000
--- a/services/gitdiff/testdata/academic-module/logs/refs/remotes/origin/HEAD
+++ /dev/null
@@ -1 +0,0 @@
-0000000000000000000000000000000000000000 bd7063cc7c04689c4d082183d32a604ed27a24f9 Lunny Xiao <xiaolunwen@gmail.com> 1574829684 +0800 clone: from https://try.gitea.io/shemgp-aiias/academic-module
diff --git a/services/gitdiff/testdata/academic-module/objects/pack/pack-597efbc3613c7ba790e33b178fd9fc1fe17b4245.idx b/services/gitdiff/testdata/academic-module/objects/pack/pack-597efbc3613c7ba790e33b178fd9fc1fe17b4245.idx
deleted file mode 100644
index 4d759aa504..0000000000
--- a/services/gitdiff/testdata/academic-module/objects/pack/pack-597efbc3613c7ba790e33b178fd9fc1fe17b4245.idx
+++ /dev/null
Binary files differ
diff --git a/services/gitdiff/testdata/academic-module/objects/pack/pack-597efbc3613c7ba790e33b178fd9fc1fe17b4245.pack b/services/gitdiff/testdata/academic-module/objects/pack/pack-597efbc3613c7ba790e33b178fd9fc1fe17b4245.pack
deleted file mode 100644
index 2dc49cfded..0000000000
--- a/services/gitdiff/testdata/academic-module/objects/pack/pack-597efbc3613c7ba790e33b178fd9fc1fe17b4245.pack
+++ /dev/null
Binary files differ
diff --git a/services/gitdiff/testdata/academic-module/packed-refs b/services/gitdiff/testdata/academic-module/packed-refs
deleted file mode 100644
index 13b5611650..0000000000
--- a/services/gitdiff/testdata/academic-module/packed-refs
+++ /dev/null
@@ -1,2 +0,0 @@
-# pack-refs with: peeled fully-peeled sorted
-bd7063cc7c04689c4d082183d32a604ed27a24f9 refs/remotes/origin/master
diff --git a/services/gitdiff/testdata/academic-module/refs/heads/master b/services/gitdiff/testdata/academic-module/refs/heads/master
deleted file mode 100644
index bd2b56eaf4..0000000000
--- a/services/gitdiff/testdata/academic-module/refs/heads/master
+++ /dev/null
@@ -1 +0,0 @@
-bd7063cc7c04689c4d082183d32a604ed27a24f9
diff --git a/services/gitdiff/testdata/academic-module/refs/remotes/origin/HEAD b/services/gitdiff/testdata/academic-module/refs/remotes/origin/HEAD
deleted file mode 100644
index 6efe28fff8..0000000000
--- a/services/gitdiff/testdata/academic-module/refs/remotes/origin/HEAD
+++ /dev/null
@@ -1 +0,0 @@
-ref: refs/remotes/origin/master
diff --git a/services/issue/assignee.go b/services/issue/assignee.go
index c7e2495568..ba9c91e0ed 100644
--- a/services/issue/assignee.go
+++ b/services/issue/assignee.go
@@ -54,6 +54,8 @@ func ToggleAssigneeWithNotify(ctx context.Context, issue *issues_model.Issue, do
if err != nil {
return false, nil, err
}
+ issue.AssigneeID = assigneeID
+ issue.Assignee = assignee
notify_service.IssueChangeAssignee(ctx, doer, issue, assignee, removed, comment)
@@ -302,7 +304,7 @@ func CanDoerChangeReviewRequests(ctx context.Context, doer *user_model.User, rep
// If the repo's owner is an organization, members of teams with read permission on pull requests can change reviewers
if repo.Owner.IsOrganization() {
- teams, err := organization.GetTeamsWithAccessToRepo(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead)
+ teams, err := organization.GetTeamsWithAccessToAnyRepoUnit(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead, unit.TypePullRequests)
if err != nil {
log.Error("GetTeamsWithAccessToRepo: %v", err)
return false
diff --git a/services/issue/comments.go b/services/issue/comments.go
index 33b5702a00..10c81198d5 100644
--- a/services/issue/comments.go
+++ b/services/issue/comments.go
@@ -5,6 +5,7 @@ package issue
import (
"context"
+ "errors"
"fmt"
"code.gitea.io/gitea/models/db"
@@ -12,14 +13,17 @@ import (
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/timeutil"
+ git_service "code.gitea.io/gitea/services/git"
notify_service "code.gitea.io/gitea/services/notify"
)
// CreateRefComment creates a commit reference comment to issue.
func CreateRefComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, content, commitSHA string) error {
if len(commitSHA) == 0 {
- return fmt.Errorf("cannot create reference with empty commit SHA")
+ return errors.New("cannot create reference with empty commit SHA")
}
if user_model.IsUserBlockedBy(ctx, doer, issue.PosterID, repo.OwnerID) {
@@ -139,3 +143,40 @@ func DeleteComment(ctx context.Context, doer *user_model.User, comment *issues_m
return nil
}
+
+// LoadCommentPushCommits Load push commits
+func LoadCommentPushCommits(ctx context.Context, c *issues_model.Comment) (err error) {
+ if c.Content == "" || c.Commits != nil || c.Type != issues_model.CommentTypePullRequestPush {
+ return nil
+ }
+
+ var data issues_model.PushActionContent
+ err = json.Unmarshal([]byte(c.Content), &data)
+ if err != nil {
+ return err
+ }
+
+ c.IsForcePush = data.IsForcePush
+
+ if c.IsForcePush {
+ if len(data.CommitIDs) != 2 {
+ return nil
+ }
+ c.OldCommit = data.CommitIDs[0]
+ c.NewCommit = data.CommitIDs[1]
+ } else {
+ gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, c.Issue.Repo)
+ if err != nil {
+ return err
+ }
+ defer closer.Close()
+
+ c.Commits, err = git_service.ConvertFromGitCommit(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo)
+ if err != nil {
+ return err
+ }
+ c.CommitsNum = int64(len(c.Commits))
+ }
+
+ return err
+}
diff --git a/services/issue/issue.go b/services/issue/issue.go
index c6a52cc0fe..2cb5f2801d 100644
--- a/services/issue/issue.go
+++ b/services/issue/issue.go
@@ -92,8 +92,12 @@ func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_mode
var reviewNotifiers []*ReviewRequestNotifier
if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issues_model.HasWorkInProgressPrefix(title) {
+ if err := issue.LoadPullRequest(ctx); err != nil {
+ return err
+ }
+
var err error
- reviewNotifiers, err = PullRequestCodeOwnersReview(ctx, issue, issue.PullRequest)
+ reviewNotifiers, err = PullRequestCodeOwnersReview(ctx, issue.PullRequest)
if err != nil {
log.Error("PullRequestCodeOwnersReview: %v", err)
}
@@ -186,9 +190,13 @@ func DeleteIssue(ctx context.Context, doer *user_model.User, gitRepo *git.Reposi
}
// delete entries in database
- if err := deleteIssue(ctx, issue); err != nil {
+ attachmentPaths, err := deleteIssue(ctx, issue)
+ if err != nil {
return err
}
+ for _, attachmentPath := range attachmentPaths {
+ system_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", attachmentPath)
+ }
// delete pull request related git data
if issue.IsPull && gitRepo != nil {
@@ -197,13 +205,6 @@ func DeleteIssue(ctx context.Context, doer *user_model.User, gitRepo *git.Reposi
}
}
- // If the Issue is pinned, we should unpin it before deletion to avoid problems with other pinned Issues
- if issue.IsPinned() {
- if err := issue.Unpin(ctx, doer); err != nil {
- return err
- }
- }
-
notify_service.DeleteIssue(ctx, doer, issue)
return nil
@@ -250,53 +251,54 @@ func GetRefEndNamesAndURLs(issues []*issues_model.Issue, repoLink string) (map[i
issueRefURLs := make(map[int64]string, len(issues))
for _, issue := range issues {
if issue.Ref != "" {
- issueRefEndNames[issue.ID] = git.RefName(issue.Ref).ShortName()
- issueRefURLs[issue.ID] = git.RefURL(repoLink, issue.Ref)
+ ref := git.RefName(issue.Ref)
+ issueRefEndNames[issue.ID] = ref.ShortName()
+ issueRefURLs[issue.ID] = repoLink + "/src/" + ref.RefWebLinkPath()
}
}
return issueRefEndNames, issueRefURLs
}
// deleteIssue deletes the issue
-func deleteIssue(ctx context.Context, issue *issues_model.Issue) error {
+func deleteIssue(ctx context.Context, issue *issues_model.Issue) ([]string, error) {
ctx, committer, err := db.TxContext(ctx)
if err != nil {
- return err
+ return nil, err
}
defer committer.Close()
- e := db.GetEngine(ctx)
- if _, err := e.ID(issue.ID).NoAutoCondition().Delete(issue); err != nil {
- return err
+ if _, err := db.GetEngine(ctx).ID(issue.ID).NoAutoCondition().Delete(issue); err != nil {
+ return nil, err
}
// update the total issue numbers
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, false); err != nil {
- return err
+ return nil, err
}
// if the issue is closed, update the closed issue numbers
if issue.IsClosed {
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, true); err != nil {
- return err
+ return nil, err
}
}
if err := issues_model.UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil {
- return fmt.Errorf("error updating counters for milestone id %d: %w",
+ return nil, fmt.Errorf("error updating counters for milestone id %d: %w",
issue.MilestoneID, err)
}
if err := activities_model.DeleteIssueActions(ctx, issue.RepoID, issue.ID, issue.Index); err != nil {
- return err
+ return nil, err
}
// find attachments related to this issue and remove them
- if err := issue.LoadAttributes(ctx); err != nil {
- return err
+ if err := issue.LoadAttachments(ctx); err != nil {
+ return nil, err
}
+ var attachmentPaths []string
for i := range issue.Attachments {
- system_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", issue.Attachments[i].RelativePath())
+ attachmentPaths = append(attachmentPaths, issue.Attachments[i].RelativePath())
}
// delete all database data still assigned to this issue
@@ -318,9 +320,70 @@ func deleteIssue(ctx context.Context, issue *issues_model.Issue) error {
&issues_model.Comment{RefIssueID: issue.ID},
&issues_model.IssueDependency{DependencyID: issue.ID},
&issues_model.Comment{DependentIssueID: issue.ID},
+ &issues_model.IssuePin{IssueID: issue.ID},
); err != nil {
+ return nil, err
+ }
+
+ if err := committer.Commit(); err != nil {
+ return nil, err
+ }
+ return attachmentPaths, nil
+}
+
+// DeleteOrphanedIssues delete issues without a repo
+func DeleteOrphanedIssues(ctx context.Context) error {
+ var attachmentPaths []string
+ err := db.WithTx(ctx, func(ctx context.Context) error {
+ repoIDs, err := issues_model.GetOrphanedIssueRepoIDs(ctx)
+ if err != nil {
+ return err
+ }
+ for i := range repoIDs {
+ paths, err := DeleteIssuesByRepoID(ctx, repoIDs[i])
+ if err != nil {
+ return err
+ }
+ attachmentPaths = append(attachmentPaths, paths...)
+ }
+ return nil
+ })
+ if err != nil {
return err
}
- return committer.Commit()
+ // Remove issue attachment files.
+ for i := range attachmentPaths {
+ system_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", attachmentPaths[i])
+ }
+ return nil
+}
+
+// DeleteIssuesByRepoID deletes issues by repositories id
+func DeleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []string, err error) {
+ for {
+ issues := make([]*issues_model.Issue, 0, db.DefaultMaxInSize)
+ if err := db.GetEngine(ctx).
+ Where("repo_id = ?", repoID).
+ OrderBy("id").
+ Limit(db.DefaultMaxInSize).
+ Find(&issues); err != nil {
+ return nil, err
+ }
+
+ if len(issues) == 0 {
+ break
+ }
+
+ for _, issue := range issues {
+ issueAttachPaths, err := deleteIssue(ctx, issue)
+ if err != nil {
+ return nil, fmt.Errorf("deleteIssue [issue_id: %d]: %w", issue.ID, err)
+ }
+
+ attachmentPaths = append(attachmentPaths, issueAttachPaths...)
+ }
+ }
+
+ return attachmentPaths, err
}
diff --git a/services/issue/issue_test.go b/services/issue/issue_test.go
index 8806cec0e7..bad0d65d1e 100644
--- a/services/issue/issue_test.go
+++ b/services/issue/issue_test.go
@@ -24,8 +24,8 @@ func TestGetRefEndNamesAndURLs(t *testing.T) {
repoLink := "/foo/bar"
endNames, urls := GetRefEndNamesAndURLs(issues, repoLink)
- assert.EqualValues(t, map[int64]string{1: "branch1", 2: "tag1", 3: "c0ffee"}, endNames)
- assert.EqualValues(t, map[int64]string{
+ assert.Equal(t, map[int64]string{1: "branch1", 2: "tag1", 3: "c0ffee"}, endNames)
+ assert.Equal(t, map[int64]string{
1: repoLink + "/src/branch/branch1",
2: repoLink + "/src/tag/tag1",
3: repoLink + "/src/commit/c0ffee",
@@ -44,7 +44,7 @@ func TestIssue_DeleteIssue(t *testing.T) {
ID: issueIDs[2],
}
- err = deleteIssue(db.DefaultContext, issue)
+ _, err = deleteIssue(db.DefaultContext, issue)
assert.NoError(t, err)
issueIDs, err = issues_model.GetIssueIDsByRepoID(db.DefaultContext, 1)
assert.NoError(t, err)
@@ -55,7 +55,7 @@ func TestIssue_DeleteIssue(t *testing.T) {
assert.NoError(t, err)
issue, err = issues_model.GetIssueByID(db.DefaultContext, 4)
assert.NoError(t, err)
- err = deleteIssue(db.DefaultContext, issue)
+ _, err = deleteIssue(db.DefaultContext, issue)
assert.NoError(t, err)
assert.Len(t, attachments, 2)
for i := range attachments {
@@ -78,7 +78,7 @@ func TestIssue_DeleteIssue(t *testing.T) {
assert.NoError(t, err)
assert.False(t, left)
- err = deleteIssue(db.DefaultContext, issue2)
+ _, err = deleteIssue(db.DefaultContext, issue2)
assert.NoError(t, err)
left, err = issues_model.IssueNoDependenciesLeft(db.DefaultContext, issue1)
assert.NoError(t, err)
diff --git a/services/issue/milestone.go b/services/issue/milestone.go
index beb6f131a9..afca70794d 100644
--- a/services/issue/milestone.go
+++ b/services/issue/milestone.go
@@ -5,6 +5,7 @@ package issue
import (
"context"
+ "errors"
"fmt"
"code.gitea.io/gitea/models/db"
@@ -21,7 +22,7 @@ func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *is
return fmt.Errorf("HasMilestoneByRepoID: %w", err)
}
if !has {
- return fmt.Errorf("HasMilestoneByRepoID: issue doesn't exist")
+ return errors.New("HasMilestoneByRepoID: issue doesn't exist")
}
}
diff --git a/services/issue/pull.go b/services/issue/pull.go
index 896802108d..3543b05b18 100644
--- a/services/issue/pull.go
+++ b/services/issue/pull.go
@@ -6,6 +6,7 @@ package issue
import (
"context"
"fmt"
+ "slices"
"time"
issues_model "code.gitea.io/gitea/models/issues"
@@ -40,20 +41,27 @@ type ReviewRequestNotifier struct {
ReviewTeam *org_model.Team
}
-func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue, pr *issues_model.PullRequest) ([]*ReviewRequestNotifier, error) {
- files := []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"}
+var codeOwnerFiles = []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"}
+func IsCodeOwnerFile(f string) bool {
+ return slices.Contains(codeOwnerFiles, f)
+}
+
+func PullRequestCodeOwnersReview(ctx context.Context, pr *issues_model.PullRequest) ([]*ReviewRequestNotifier, error) {
+ if err := pr.LoadIssue(ctx); err != nil {
+ return nil, err
+ }
+ issue := pr.Issue
if pr.IsWorkInProgress(ctx) {
return nil, nil
}
-
if err := pr.LoadHeadRepo(ctx); err != nil {
return nil, err
}
-
if err := pr.LoadBaseRepo(ctx); err != nil {
return nil, err
}
+ pr.Issue.Repo = pr.BaseRepo
if pr.BaseRepo.IsFork {
return nil, nil
@@ -71,7 +79,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue,
}
var data string
- for _, file := range files {
+ for _, file := range codeOwnerFiles {
if blob, err := commit.GetBlobByPath(file); err == nil {
data, err = blob.GetBlobContent(setting.UI.MaxDisplayFileSize)
if err == nil {
@@ -79,8 +87,14 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue,
}
}
}
+ if data == "" {
+ return nil, nil
+ }
rules, _ := issues_model.GetCodeOwnersFromContent(ctx, data)
+ if len(rules) == 0 {
+ return nil, nil
+ }
// get the mergebase
mergeBase, err := getMergeBase(repo, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName())
@@ -116,13 +130,31 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue,
return nil, err
}
+ // load all reviews from database
+ latestReivews, _, err := issues_model.GetReviewsByIssueID(ctx, pr.IssueID)
+ if err != nil {
+ return nil, err
+ }
+
+ contain := func(list issues_model.ReviewList, u *user_model.User) bool {
+ for _, review := range list {
+ if review.ReviewerTeamID == 0 && review.ReviewerID == u.ID {
+ return true
+ }
+ }
+ return false
+ }
+
for _, u := range uniqUsers {
- if u.ID != issue.Poster.ID {
+ if u.ID != issue.Poster.ID && !contain(latestReivews, u) {
comment, err := issues_model.AddReviewRequest(ctx, issue, u, issue.Poster)
if err != nil {
log.Warn("Failed add assignee user: %s to PR review: %s#%d, error: %s", u.Name, pr.BaseRepo.Name, pr.ID, err)
return nil, err
}
+ if comment == nil { // comment maybe nil if review type is ReviewTypeRequest
+ continue
+ }
notifiers = append(notifiers, &ReviewRequestNotifier{
Comment: comment,
IsAdd: true,
@@ -130,12 +162,16 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue,
})
}
}
+
for _, t := range uniqTeams {
comment, err := issues_model.AddTeamReviewRequest(ctx, issue, t, issue.Poster)
if err != nil {
log.Warn("Failed add assignee team: %s to PR review: %s#%d, error: %s", t.Name, pr.BaseRepo.Name, pr.ID, err)
return nil, err
}
+ if comment == nil { // comment maybe nil if review type is ReviewTypeRequest
+ continue
+ }
notifiers = append(notifiers, &ReviewRequestNotifier{
Comment: comment,
IsAdd: true,
diff --git a/services/issue/status.go b/services/issue/status.go
index e18b891175..f9d7dca841 100644
--- a/services/issue/status.go
+++ b/services/issue/status.go
@@ -24,14 +24,14 @@ func CloseIssue(ctx context.Context, issue *issues_model.Issue, doer *user_model
comment, err := issues_model.CloseIssue(dbCtx, issue, doer)
if err != nil {
if issues_model.IsErrDependenciesLeft(err) {
- if err := issues_model.FinishIssueStopwatchIfPossible(dbCtx, doer, issue); err != nil {
+ if _, err := issues_model.FinishIssueStopwatch(dbCtx, doer, issue); err != nil {
log.Error("Unable to stop stopwatch for issue[%d]#%d: %v", issue.ID, issue.Index, err)
}
}
return err
}
- if err := issues_model.FinishIssueStopwatchIfPossible(dbCtx, doer, issue); err != nil {
+ if _, err := issues_model.FinishIssueStopwatch(dbCtx, doer, issue); err != nil {
return err
}
diff --git a/services/issue/suggestion.go b/services/issue/suggestion.go
new file mode 100644
index 0000000000..22eddb1904
--- /dev/null
+++ b/services/issue/suggestion.go
@@ -0,0 +1,73 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package issue
+
+import (
+ "context"
+ "strconv"
+
+ issues_model "code.gitea.io/gitea/models/issues"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/optional"
+ "code.gitea.io/gitea/modules/structs"
+)
+
+func GetSuggestion(ctx context.Context, repo *repo_model.Repository, isPull optional.Option[bool], keyword string) ([]*structs.Issue, error) {
+ var issues issues_model.IssueList
+ var err error
+ pageSize := 5
+ if keyword == "" {
+ issues, err = issues_model.FindLatestUpdatedIssues(ctx, repo.ID, isPull, pageSize)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ indexKeyword, _ := strconv.ParseInt(keyword, 10, 64)
+ var issueByIndex *issues_model.Issue
+ var excludedID int64
+ if indexKeyword > 0 {
+ issueByIndex, err = issues_model.GetIssueByIndex(ctx, repo.ID, indexKeyword)
+ if err != nil && !issues_model.IsErrIssueNotExist(err) {
+ return nil, err
+ }
+ if issueByIndex != nil {
+ excludedID = issueByIndex.ID
+ pageSize--
+ }
+ }
+
+ issues, err = issues_model.FindIssuesSuggestionByKeyword(ctx, repo.ID, keyword, isPull, excludedID, pageSize)
+ if err != nil {
+ return nil, err
+ }
+
+ if issueByIndex != nil {
+ issues = append([]*issues_model.Issue{issueByIndex}, issues...)
+ }
+ }
+
+ if err := issues.LoadPullRequests(ctx); err != nil {
+ return nil, err
+ }
+
+ suggestions := make([]*structs.Issue, 0, len(issues))
+ for _, issue := range issues {
+ suggestion := &structs.Issue{
+ ID: issue.ID,
+ Index: issue.Index,
+ Title: issue.Title,
+ State: issue.State(),
+ }
+
+ if issue.IsPull && issue.PullRequest != nil {
+ suggestion.PullRequest = &structs.PullRequestMeta{
+ HasMerged: issue.PullRequest.HasMerged,
+ IsWorkInProgress: issue.PullRequest.IsWorkInProgress(ctx),
+ }
+ }
+ suggestions = append(suggestions, suggestion)
+ }
+
+ return suggestions, nil
+}
diff --git a/services/issue/suggestion_test.go b/services/issue/suggestion_test.go
new file mode 100644
index 0000000000..a5b39d27bb
--- /dev/null
+++ b/services/issue/suggestion_test.go
@@ -0,0 +1,57 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package issue
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/optional"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_Suggestion(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+
+ testCases := []struct {
+ keyword string
+ isPull optional.Option[bool]
+ expectedIndexes []int64
+ }{
+ {
+ keyword: "",
+ expectedIndexes: []int64{5, 1, 4, 2, 3},
+ },
+ {
+ keyword: "1",
+ expectedIndexes: []int64{1},
+ },
+ {
+ keyword: "issue",
+ expectedIndexes: []int64{4, 1, 2, 3},
+ },
+ {
+ keyword: "pull",
+ expectedIndexes: []int64{5},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.keyword, func(t *testing.T) {
+ issues, err := GetSuggestion(db.DefaultContext, repo1, testCase.isPull, testCase.keyword)
+ assert.NoError(t, err)
+
+ issueIndexes := make([]int64, 0, len(issues))
+ for _, issue := range issues {
+ issueIndexes = append(issueIndexes, issue.Index)
+ }
+ assert.Equal(t, testCase.expectedIndexes, issueIndexes)
+ })
+ }
+}
diff --git a/services/lfs/locks.go b/services/lfs/locks.go
index 1d464f4a66..264001f0f9 100644
--- a/services/lfs/locks.go
+++ b/services/lfs/locks.go
@@ -74,10 +74,7 @@ func GetListLockHandler(ctx *context.Context) {
}
ctx.Resp.Header().Set("Content-Type", lfs_module.MediaType)
- cursor := ctx.FormInt("cursor")
- if cursor < 0 {
- cursor = 0
- }
+ cursor := max(ctx.FormInt("cursor"), 0)
limit := ctx.FormInt("limit")
if limit > setting.LFS.LocksPagingNum && setting.LFS.LocksPagingNum > 0 {
limit = setting.LFS.LocksPagingNum
@@ -239,10 +236,7 @@ func VerifyLockHandler(ctx *context.Context) {
ctx.Resp.Header().Set("Content-Type", lfs_module.MediaType)
- cursor := ctx.FormInt("cursor")
- if cursor < 0 {
- cursor = 0
- }
+ cursor := max(ctx.FormInt("cursor"), 0)
limit := ctx.FormInt("limit")
if limit > setting.LFS.LocksPagingNum && setting.LFS.LocksPagingNum > 0 {
limit = setting.LFS.LocksPagingNum
diff --git a/services/lfs/server.go b/services/lfs/server.go
index a77623fdc1..c44cc35e53 100644
--- a/services/lfs/server.go
+++ b/services/lfs/server.go
@@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"io"
+ "maps"
"net/http"
"net/url"
"path"
@@ -26,6 +27,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/auth/httpauth"
"code.gitea.io/gitea/modules/json"
lfs_module "code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
@@ -134,7 +136,9 @@ func DownloadHandler(ctx *context.Context) {
}
contentLength := toByte + 1 - fromByte
- ctx.Resp.Header().Set("Content-Length", strconv.FormatInt(contentLength, 10))
+ contentLengthStr := strconv.FormatInt(contentLength, 10)
+ ctx.Resp.Header().Set("Content-Length", contentLengthStr)
+ ctx.Resp.Header().Set("X-Gitea-LFS-Content-Length", contentLengthStr) // we need this header to make sure it won't be affected by reverse proxy or compression
ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
filename := ctx.PathParam("filename")
@@ -162,11 +166,12 @@ func BatchHandler(ctx *context.Context) {
}
var isUpload bool
- if br.Operation == "upload" {
+ switch br.Operation {
+ case "upload":
isUpload = true
- } else if br.Operation == "download" {
+ case "download":
isUpload = false
- } else {
+ default:
log.Trace("Attempt to BATCH with invalid operation: %s", br.Operation)
writeStatus(ctx, http.StatusBadRequest)
return
@@ -199,7 +204,7 @@ func BatchHandler(ctx *context.Context) {
exists, err := contentStore.Exists(p)
if err != nil {
- log.Error("Unable to check if LFS OID[%s] exist. Error: %v", p.Oid, rc.User, rc.Repo, err)
+ log.Error("Unable to check if LFS object with ID '%s' exists for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err)
writeStatus(ctx, http.StatusInternalServerError)
return
}
@@ -477,9 +482,7 @@ func buildObjectResponse(rc *requestContext, pointer lfs_module.Pointer, downloa
rep.Actions["upload"] = &lfs_module.Link{Href: rc.UploadLink(pointer), Header: header}
verifyHeader := make(map[string]string)
- for key, value := range header {
- verifyHeader[key] = value
- }
+ maps.Copy(verifyHeader, header)
// This is only needed to workaround https://github.com/git-lfs/git-lfs/issues/3662
verifyHeader["Accept"] = lfs_module.AcceptHeader
@@ -569,15 +572,15 @@ func handleLFSToken(ctx stdCtx.Context, tokenSHA string, target *repo_model.Repo
claims, claimsOk := token.Claims.(*Claims)
if !token.Valid || !claimsOk {
- return nil, fmt.Errorf("invalid token claim")
+ return nil, errors.New("invalid token claim")
}
if claims.RepoID != target.ID {
- return nil, fmt.Errorf("invalid token claim")
+ return nil, errors.New("invalid token claim")
}
if mode == perm_model.AccessModeWrite && claims.Op != "upload" {
- return nil, fmt.Errorf("invalid token claim")
+ return nil, errors.New("invalid token claim")
}
u, err := user_model.GetUserByID(ctx, claims.UserID)
@@ -590,21 +593,13 @@ func handleLFSToken(ctx stdCtx.Context, tokenSHA string, target *repo_model.Repo
func parseToken(ctx stdCtx.Context, authorization string, target *repo_model.Repository, mode perm_model.AccessMode) (*user_model.User, error) {
if authorization == "" {
- return nil, fmt.Errorf("no token")
- }
-
- parts := strings.SplitN(authorization, " ", 2)
- if len(parts) != 2 {
- return nil, fmt.Errorf("no token")
+ return nil, errors.New("no token")
}
- tokenSHA := parts[1]
- switch strings.ToLower(parts[0]) {
- case "bearer":
- fallthrough
- case "token":
- return handleLFSToken(ctx, tokenSHA, target, mode)
+ parsed, ok := httpauth.ParseAuthorizationHeader(authorization)
+ if !ok || parsed.BearerToken == nil {
+ return nil, errors.New("token not found")
}
- return nil, fmt.Errorf("token not found")
+ return handleLFSToken(ctx, parsed.BearerToken.Token, target, mode)
}
func requireAuth(ctx *context.Context) {
diff --git a/services/mailer/mail.go b/services/mailer/mail.go
index 52e19bde6f..aa51cbdbcf 100644
--- a/services/mailer/mail.go
+++ b/services/mailer/mail.go
@@ -7,46 +7,30 @@ package mailer
import (
"bytes"
"context"
+ "encoding/base64"
+ "errors"
"fmt"
"html/template"
+ "io"
"mime"
"regexp"
- "strconv"
"strings"
texttmpl "text/template"
- "time"
- activities_model "code.gitea.io/gitea/models/activities"
- issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/emoji"
+ "code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/translation"
- incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload"
+ "code.gitea.io/gitea/modules/storage"
+ "code.gitea.io/gitea/modules/typesniffer"
sender_service "code.gitea.io/gitea/services/mailer/sender"
- "code.gitea.io/gitea/services/mailer/token"
-)
-
-const (
- mailAuthActivate templates.TplName = "auth/activate"
- mailAuthActivateEmail templates.TplName = "auth/activate_email"
- mailAuthResetPassword templates.TplName = "auth/reset_passwd"
- mailAuthRegisterNotify templates.TplName = "auth/register_notify"
-
- mailNotifyCollaborator templates.TplName = "notify/collaborator"
- mailRepoTransferNotify templates.TplName = "notify/repo_transfer"
-
- // There's no actual limit for subject in RFC 5322
- mailMaxSubjectRunes = 256
+ "golang.org/x/net/html"
)
+const mailMaxSubjectRunes = 256 // There's no actual limit for subject in RFC 5322
+
var (
bodyTemplates *template.Template
subjectTemplates *texttmpl.Template
@@ -62,475 +46,114 @@ func SendTestMail(email string) error {
return sender_service.Send(sender, sender_service.NewMessage(email, "Gitea Test Email!", "Gitea Test Email!"))
}
-// sendUserMail sends a mail to the user
-func sendUserMail(language string, u *user_model.User, tpl templates.TplName, code, subject, info string) {
- locale := translation.NewLocale(language)
- data := map[string]any{
- "locale": locale,
- "DisplayName": u.DisplayName(),
- "ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale),
- "ResetPwdCodeLives": timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, locale),
- "Code": code,
- "Language": locale.Language(),
- }
-
- var content bytes.Buffer
-
- if err := bodyTemplates.ExecuteTemplate(&content, string(tpl), data); err != nil {
- log.Error("Template: %v", err)
- return
- }
-
- msg := sender_service.NewMessage(u.EmailTo(), subject, content.String())
- msg.Info = fmt.Sprintf("UID: %d, %s", u.ID, info)
-
- SendAsync(msg)
-}
-
-// SendActivateAccountMail sends an activation mail to the user (new user registration)
-func SendActivateAccountMail(locale translation.Locale, u *user_model.User) {
- if setting.MailService == nil {
- // No mail service configured
- return
- }
- opts := &user_model.TimeLimitCodeOptions{Purpose: user_model.TimeLimitCodeActivateAccount}
- sendUserMail(locale.Language(), u, mailAuthActivate, user_model.GenerateUserTimeLimitCode(opts, u), locale.TrString("mail.activate_account"), "activate account")
-}
-
-// SendResetPasswordMail sends a password reset mail to the user
-func SendResetPasswordMail(u *user_model.User) {
- if setting.MailService == nil {
- // No mail service configured
- return
- }
- locale := translation.NewLocale(u.Language)
- opts := &user_model.TimeLimitCodeOptions{Purpose: user_model.TimeLimitCodeResetPassword}
- sendUserMail(u.Language, u, mailAuthResetPassword, user_model.GenerateUserTimeLimitCode(opts, u), locale.TrString("mail.reset_password"), "recover account")
-}
-
-// SendActivateEmailMail sends confirmation email to confirm new email address
-func SendActivateEmailMail(u *user_model.User, email string) {
- if setting.MailService == nil {
- // No mail service configured
- return
- }
- locale := translation.NewLocale(u.Language)
- opts := &user_model.TimeLimitCodeOptions{Purpose: user_model.TimeLimitCodeActivateEmail, NewEmail: email}
- data := map[string]any{
- "locale": locale,
- "DisplayName": u.DisplayName(),
- "ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale),
- "Code": user_model.GenerateUserTimeLimitCode(opts, u),
- "Email": email,
- "Language": locale.Language(),
- }
-
- var content bytes.Buffer
-
- if err := bodyTemplates.ExecuteTemplate(&content, string(mailAuthActivateEmail), data); err != nil {
- log.Error("Template: %v", err)
- return
+func sanitizeSubject(subject string) string {
+ runes := []rune(strings.TrimSpace(subjectRemoveSpaces.ReplaceAllLiteralString(subject, " ")))
+ if len(runes) > mailMaxSubjectRunes {
+ runes = runes[:mailMaxSubjectRunes]
}
-
- msg := sender_service.NewMessage(email, locale.TrString("mail.activate_email"), content.String())
- msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID)
-
- SendAsync(msg)
+ // Encode non-ASCII characters
+ return mime.QEncoding.Encode("utf-8", string(runes))
}
-// SendRegisterNotifyMail triggers a notify e-mail by admin created a account.
-func SendRegisterNotifyMail(u *user_model.User) {
- if setting.MailService == nil || !u.IsActive {
- // No mail service configured OR user is inactive
- return
- }
- locale := translation.NewLocale(u.Language)
-
- data := map[string]any{
- "locale": locale,
- "DisplayName": u.DisplayName(),
- "Username": u.Name,
- "Language": locale.Language(),
- }
-
- var content bytes.Buffer
-
- if err := bodyTemplates.ExecuteTemplate(&content, string(mailAuthRegisterNotify), data); err != nil {
- log.Error("Template: %v", err)
- return
- }
-
- msg := sender_service.NewMessage(u.EmailTo(), locale.TrString("mail.register_notify", setting.AppName), content.String())
- msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID)
-
- SendAsync(msg)
+type mailAttachmentBase64Embedder struct {
+ doer *user_model.User
+ repo *repo_model.Repository
+ maxSize int64
+ estimateSize int64
}
-// SendCollaboratorMail sends mail notification to new collaborator.
-func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository) {
- if setting.MailService == nil || !u.IsActive {
- // No mail service configured OR the user is inactive
- return
- }
- locale := translation.NewLocale(u.Language)
- repoName := repo.FullName()
-
- subject := locale.TrString("mail.repo.collaborator.added.subject", doer.DisplayName(), repoName)
- data := map[string]any{
- "locale": locale,
- "Subject": subject,
- "RepoName": repoName,
- "Link": repo.HTMLURL(),
- "Language": locale.Language(),
- }
-
- var content bytes.Buffer
-
- if err := bodyTemplates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil {
- log.Error("Template: %v", err)
- return
- }
-
- msg := sender_service.NewMessage(u.EmailTo(), subject, content.String())
- msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID)
-
- SendAsync(msg)
+func newMailAttachmentBase64Embedder(doer *user_model.User, repo *repo_model.Repository, maxSize int64) *mailAttachmentBase64Embedder {
+ return &mailAttachmentBase64Embedder{doer: doer, repo: repo, maxSize: maxSize}
}
-func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipients []*user_model.User, fromMention bool, info string) ([]*sender_service.Message, error) {
- var (
- subject string
- link string
- prefix string
- // Fall back subject for bad templates, make sure subject is never empty
- fallback string
- reviewComments []*issues_model.Comment
- )
-
- commentType := issues_model.CommentTypeComment
- if ctx.Comment != nil {
- commentType = ctx.Comment.Type
- link = ctx.Issue.HTMLURL() + "#" + ctx.Comment.HashTag()
- } else {
- link = ctx.Issue.HTMLURL()
- }
-
- reviewType := issues_model.ReviewTypeComment
- if ctx.Comment != nil && ctx.Comment.Review != nil {
- reviewType = ctx.Comment.Review.Type
- }
-
- // This is the body of the new issue or comment, not the mail body
- rctx := renderhelper.NewRenderContextRepoComment(ctx.Context, ctx.Issue.Repo).WithUseAbsoluteLink(true)
- body, err := markdown.RenderString(rctx,
- ctx.Content)
+func (b64embedder *mailAttachmentBase64Embedder) Base64InlineImages(ctx context.Context, body template.HTML) (template.HTML, error) {
+ doc, err := html.Parse(strings.NewReader(string(body)))
if err != nil {
- return nil, err
- }
-
- actType, actName, tplName := actionToTemplate(ctx.Issue, ctx.ActionType, commentType, reviewType)
-
- if actName != "new" {
- prefix = "Re: "
- }
- fallback = prefix + fallbackMailSubject(ctx.Issue)
-
- if ctx.Comment != nil && ctx.Comment.Review != nil {
- reviewComments = make([]*issues_model.Comment, 0, 10)
- for _, lines := range ctx.Comment.Review.CodeComments {
- for _, comments := range lines {
- reviewComments = append(reviewComments, comments...)
+ return "", fmt.Errorf("html.Parse failed: %w", err)
+ }
+
+ b64embedder.estimateSize = int64(len(string(body)))
+
+ var processNode func(*html.Node)
+ processNode = func(n *html.Node) {
+ if n.Type == html.ElementNode {
+ if n.Data == "img" {
+ for i, attr := range n.Attr {
+ if attr.Key == "src" {
+ attachmentSrc := attr.Val
+ dataURI, err := b64embedder.AttachmentSrcToBase64DataURI(ctx, attachmentSrc)
+ if err != nil {
+ // Not an error, just skip. This is probably an image from outside the gitea instance.
+ log.Trace("Unable to embed attachment %q to mail body: %v", attachmentSrc, err)
+ } else {
+ n.Attr[i].Val = dataURI
+ }
+ break
+ }
+ }
}
}
- }
- locale := translation.NewLocale(lang)
-
- mailMeta := map[string]any{
- "locale": locale,
- "FallbackSubject": fallback,
- "Body": body,
- "Link": link,
- "Issue": ctx.Issue,
- "Comment": ctx.Comment,
- "IsPull": ctx.Issue.IsPull,
- "User": ctx.Issue.Repo.MustOwner(ctx),
- "Repo": ctx.Issue.Repo.FullName(),
- "Doer": ctx.Doer,
- "IsMention": fromMention,
- "SubjectPrefix": prefix,
- "ActionType": actType,
- "ActionName": actName,
- "ReviewComments": reviewComments,
- "Language": locale.Language(),
- "CanReply": setting.IncomingEmail.Enabled && commentType != issues_model.CommentTypePullRequestPush,
- }
-
- var mailSubject bytes.Buffer
- if err := subjectTemplates.ExecuteTemplate(&mailSubject, tplName, mailMeta); err == nil {
- subject = sanitizeSubject(mailSubject.String())
- if subject == "" {
- subject = fallback
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ processNode(c)
}
- } else {
- log.Error("ExecuteTemplate [%s]: %v", tplName+"/subject", err)
}
- subject = emoji.ReplaceAliases(subject)
-
- mailMeta["Subject"] = subject
-
- var mailBody bytes.Buffer
-
- if err := bodyTemplates.ExecuteTemplate(&mailBody, tplName, mailMeta); err != nil {
- log.Error("ExecuteTemplate [%s]: %v", tplName+"/body", err)
- }
+ processNode(doc)
- // Make sure to compose independent messages to avoid leaking user emails
- msgID := generateMessageIDForIssue(ctx.Issue, ctx.Comment, ctx.ActionType)
- reference := generateMessageIDForIssue(ctx.Issue, nil, activities_model.ActionType(0))
-
- var replyPayload []byte
- if ctx.Comment != nil {
- if ctx.Comment.Type.HasMailReplySupport() {
- replyPayload, err = incoming_payload.CreateReferencePayload(ctx.Comment)
- }
- } else {
- replyPayload, err = incoming_payload.CreateReferencePayload(ctx.Issue)
- }
+ var buf bytes.Buffer
+ err = html.Render(&buf, doc)
if err != nil {
- return nil, err
+ return "", fmt.Errorf("html.Render failed: %w", err)
}
-
- unsubscribePayload, err := incoming_payload.CreateReferencePayload(ctx.Issue)
- if err != nil {
- return nil, err
- }
-
- msgs := make([]*sender_service.Message, 0, len(recipients))
- for _, recipient := range recipients {
- msg := sender_service.NewMessageFrom(
- recipient.Email,
- fromDisplayName(ctx.Doer),
- setting.MailService.FromEmail,
- subject,
- mailBody.String(),
- )
- msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info)
-
- msg.SetHeader("Message-ID", msgID)
- msg.SetHeader("In-Reply-To", reference)
-
- references := []string{reference}
- listUnsubscribe := []string{"<" + ctx.Issue.HTMLURL() + ">"}
-
- if setting.IncomingEmail.Enabled {
- if replyPayload != nil {
- token, err := token.CreateToken(token.ReplyHandlerType, recipient, replyPayload)
- if err != nil {
- log.Error("CreateToken failed: %v", err)
- } else {
- replyAddress := strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1)
- msg.ReplyTo = replyAddress
- msg.SetHeader("List-Post", fmt.Sprintf("<mailto:%s>", replyAddress))
-
- references = append(references, fmt.Sprintf("<reply-%s@%s>", token, setting.Domain))
- }
- }
-
- token, err := token.CreateToken(token.UnsubscribeHandlerType, recipient, unsubscribePayload)
- if err != nil {
- log.Error("CreateToken failed: %v", err)
- } else {
- unsubAddress := strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1)
- listUnsubscribe = append(listUnsubscribe, "<mailto:"+unsubAddress+">")
- }
- }
-
- msg.SetHeader("References", references...)
- msg.SetHeader("List-Unsubscribe", listUnsubscribe...)
-
- for key, value := range generateAdditionalHeaders(ctx, actType, recipient) {
- msg.SetHeader(key, value)
- }
-
- msgs = append(msgs, msg)
- }
-
- return msgs, nil
+ return template.HTML(buf.String()), nil
}
-func generateMessageIDForIssue(issue *issues_model.Issue, comment *issues_model.Comment, actionType activities_model.ActionType) string {
- var path string
- if issue.IsPull {
- path = "pulls"
- } else {
- path = "issues"
- }
-
- var extra string
- if comment != nil {
- extra = fmt.Sprintf("/comment/%d", comment.ID)
- } else {
- switch actionType {
- case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
- extra = fmt.Sprintf("/close/%d", time.Now().UnixNano()/1e6)
- case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
- extra = fmt.Sprintf("/reopen/%d", time.Now().UnixNano()/1e6)
- case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
- extra = fmt.Sprintf("/merge/%d", time.Now().UnixNano()/1e6)
- case activities_model.ActionPullRequestReadyForReview:
- extra = fmt.Sprintf("/ready/%d", time.Now().UnixNano()/1e6)
+func (b64embedder *mailAttachmentBase64Embedder) AttachmentSrcToBase64DataURI(ctx context.Context, attachmentSrc string) (string, error) {
+ parsedSrc := httplib.ParseGiteaSiteURL(ctx, attachmentSrc)
+ var attachmentUUID string
+ if parsedSrc != nil {
+ var ok bool
+ attachmentUUID, ok = strings.CutPrefix(parsedSrc.RoutePath, "/attachments/")
+ if !ok {
+ attachmentUUID, ok = strings.CutPrefix(parsedSrc.RepoSubPath, "/attachments/")
+ }
+ if !ok {
+ return "", errors.New("not an attachment")
}
}
-
- return fmt.Sprintf("<%s/%s/%d%s@%s>", issue.Repo.FullName(), path, issue.Index, extra, setting.Domain)
-}
-
-func generateMessageIDForRelease(release *repo_model.Release) string {
- return fmt.Sprintf("<%s/releases/%d@%s>", release.Repo.FullName(), release.ID, setting.Domain)
-}
-
-func generateAdditionalHeaders(ctx *mailCommentContext, reason string, recipient *user_model.User) map[string]string {
- repo := ctx.Issue.Repo
-
- return map[string]string{
- // https://datatracker.ietf.org/doc/html/rfc2919
- "List-ID": fmt.Sprintf("%s <%s.%s.%s>", repo.FullName(), repo.Name, repo.OwnerName, setting.Domain),
-
- // https://datatracker.ietf.org/doc/html/rfc2369
- "List-Archive": fmt.Sprintf("<%s>", repo.HTMLURL()),
-
- "X-Mailer": "Gitea",
- "X-Gitea-Reason": reason,
- "X-Gitea-Sender": ctx.Doer.Name,
- "X-Gitea-Recipient": recipient.Name,
- "X-Gitea-Recipient-Address": recipient.Email,
- "X-Gitea-Repository": repo.Name,
- "X-Gitea-Repository-Path": repo.FullName(),
- "X-Gitea-Repository-Link": repo.HTMLURL(),
- "X-Gitea-Issue-ID": strconv.FormatInt(ctx.Issue.Index, 10),
- "X-Gitea-Issue-Link": ctx.Issue.HTMLURL(),
-
- "X-GitHub-Reason": reason,
- "X-GitHub-Sender": ctx.Doer.Name,
- "X-GitHub-Recipient": recipient.Name,
- "X-GitHub-Recipient-Address": recipient.Email,
-
- "X-GitLab-NotificationReason": reason,
- "X-GitLab-Project": repo.Name,
- "X-GitLab-Project-Path": repo.FullName(),
- "X-GitLab-Issue-IID": strconv.FormatInt(ctx.Issue.Index, 10),
- }
-}
-
-func sanitizeSubject(subject string) string {
- runes := []rune(strings.TrimSpace(subjectRemoveSpaces.ReplaceAllLiteralString(subject, " ")))
- if len(runes) > mailMaxSubjectRunes {
- runes = runes[:mailMaxSubjectRunes]
+ attachment, err := repo_model.GetAttachmentByUUID(ctx, attachmentUUID)
+ if err != nil {
+ return "", err
}
- // Encode non-ASCII characters
- return mime.QEncoding.Encode("utf-8", string(runes))
-}
-// SendIssueAssignedMail composes and sends issue assigned email
-func SendIssueAssignedMail(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, content string, comment *issues_model.Comment, recipients []*user_model.User) error {
- if setting.MailService == nil {
- // No mail service configured
- return nil
+ if attachment.RepoID != b64embedder.repo.ID {
+ return "", errors.New("attachment does not belong to the repository")
}
-
- if err := issue.LoadRepo(ctx); err != nil {
- log.Error("Unable to load repo [%d] for issue #%d [%d]. Error: %v", issue.RepoID, issue.Index, issue.ID, err)
- return err
+ if attachment.Size+b64embedder.estimateSize > b64embedder.maxSize {
+ return "", errors.New("total embedded images exceed max limit")
}
- langMap := make(map[string][]*user_model.User)
- for _, user := range recipients {
- if !user.IsActive {
- // don't send emails to inactive users
- continue
- }
- langMap[user.Language] = append(langMap[user.Language], user)
+ fr, err := storage.Attachments.Open(attachment.RelativePath())
+ if err != nil {
+ return "", err
}
+ defer fr.Close()
- for lang, tos := range langMap {
- msgs, err := composeIssueCommentMessages(&mailCommentContext{
- Context: ctx,
- Issue: issue,
- Doer: doer,
- ActionType: activities_model.ActionType(0),
- Content: content,
- Comment: comment,
- }, lang, tos, false, "issue assigned")
- if err != nil {
- return err
- }
- SendAsync(msgs...)
+ lr := &io.LimitedReader{R: fr, N: b64embedder.maxSize + 1}
+ content, err := io.ReadAll(lr)
+ if err != nil {
+ return "", fmt.Errorf("LimitedReader ReadAll: %w", err)
}
- return nil
-}
-// actionToTemplate returns the type and name of the action facing the user
-// (slightly different from activities_model.ActionType) and the name of the template to use (based on availability)
-func actionToTemplate(issue *issues_model.Issue, actionType activities_model.ActionType,
- commentType issues_model.CommentType, reviewType issues_model.ReviewType,
-) (typeName, name, template string) {
- if issue.IsPull {
- typeName = "pull"
- } else {
- typeName = "issue"
- }
- switch actionType {
- case activities_model.ActionCreateIssue, activities_model.ActionCreatePullRequest:
- name = "new"
- case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
- name = "comment"
- case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
- name = "close"
- case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
- name = "reopen"
- case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
- name = "merge"
- case activities_model.ActionPullReviewDismissed:
- name = "review_dismissed"
- case activities_model.ActionPullRequestReadyForReview:
- name = "ready_for_review"
- default:
- switch commentType {
- case issues_model.CommentTypeReview:
- switch reviewType {
- case issues_model.ReviewTypeApprove:
- name = "approve"
- case issues_model.ReviewTypeReject:
- name = "reject"
- default:
- name = "review"
- }
- case issues_model.CommentTypeCode:
- name = "code"
- case issues_model.CommentTypeAssignees:
- name = "assigned"
- case issues_model.CommentTypePullRequestPush:
- name = "push"
- default:
- name = "default"
- }
+ mimeType := typesniffer.DetectContentType(content)
+ if !mimeType.IsImage() {
+ return "", errors.New("not an image")
}
- template = typeName + "/" + name
- ok := bodyTemplates.Lookup(template) != nil
- if !ok && typeName != "issue" {
- template = "issue/" + name
- ok = bodyTemplates.Lookup(template) != nil
- }
- if !ok {
- template = typeName + "/default"
- ok = bodyTemplates.Lookup(template) != nil
- }
- if !ok {
- template = "issue/default"
- }
- return typeName, name, template
+ encoded := base64.StdEncoding.EncodeToString(content)
+ dataURI := fmt.Sprintf("data:%s;base64,%s", mimeType.GetMimeType(), encoded)
+ b64embedder.estimateSize += int64(len(dataURI))
+ return dataURI, nil
}
func fromDisplayName(u *user_model.User) string {
diff --git a/services/mailer/mail_comment.go b/services/mailer/mail_comment.go
index 1812441d5a..e8d12e429d 100644
--- a/services/mailer/mail_comment.go
+++ b/services/mailer/mail_comment.go
@@ -25,9 +25,8 @@ func MailParticipantsComment(ctx context.Context, c *issues_model.Comment, opTyp
if c.Type == issues_model.CommentTypePullRequestPush {
content = ""
}
- if err := mailIssueCommentToParticipants(
- &mailCommentContext{
- Context: ctx,
+ if err := mailIssueCommentToParticipants(ctx,
+ &mailComment{
Issue: issue,
Doer: c.Poster,
ActionType: opType,
@@ -48,9 +47,8 @@ func MailMentionsComment(ctx context.Context, pr *issues_model.PullRequest, c *i
visited := make(container.Set[int64], len(mentions)+1)
visited.Add(c.Poster.ID)
- if err = mailIssueCommentBatch(
- &mailCommentContext{
- Context: ctx,
+ if err = mailIssueCommentBatch(ctx,
+ &mailComment{
Issue: pr.Issue,
Doer: c.Poster,
ActionType: activities_model.ActionCommentPull,
diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go
index fab3315be2..b854d61a1a 100644
--- a/services/mailer/mail_issue.go
+++ b/services/mailer/mail_issue.go
@@ -18,38 +18,21 @@ import (
"code.gitea.io/gitea/modules/setting"
)
-func fallbackMailSubject(issue *issues_model.Issue) string {
- return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.FullName(), issue.Title, issue.Index)
-}
-
-type mailCommentContext struct {
- context.Context
- Issue *issues_model.Issue
- Doer *user_model.User
- ActionType activities_model.ActionType
- Content string
- Comment *issues_model.Comment
- ForceDoerNotification bool
-}
-
-const (
- // MailBatchSize set the batch size used in mailIssueCommentBatch
- MailBatchSize = 100
-)
+const MailBatchSize = 100 // batch size used in mailIssueCommentBatch
// mailIssueCommentToParticipants can be used for both new issue creation and comment.
// This function sends two list of emails:
// 1. Repository watchers (except for WIP pull requests) and users who are participated in comments.
// 2. Users who are not in 1. but get mentioned in current issue/comment.
-func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_model.User) error {
+func mailIssueCommentToParticipants(ctx context.Context, comment *mailComment, mentions []*user_model.User) error {
// Required by the mail composer; make sure to load these before calling the async function
- if err := ctx.Issue.LoadRepo(ctx); err != nil {
+ if err := comment.Issue.LoadRepo(ctx); err != nil {
return fmt.Errorf("LoadRepo: %w", err)
}
- if err := ctx.Issue.LoadPoster(ctx); err != nil {
+ if err := comment.Issue.LoadPoster(ctx); err != nil {
return fmt.Errorf("LoadPoster: %w", err)
}
- if err := ctx.Issue.LoadPullRequest(ctx); err != nil {
+ if err := comment.Issue.LoadPullRequest(ctx); err != nil {
return fmt.Errorf("LoadPullRequest: %w", err)
}
@@ -57,35 +40,35 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
unfiltered := make([]int64, 1, 64)
// =========== Original poster ===========
- unfiltered[0] = ctx.Issue.PosterID
+ unfiltered[0] = comment.Issue.PosterID
// =========== Assignees ===========
- ids, err := issues_model.GetAssigneeIDsByIssue(ctx, ctx.Issue.ID)
+ ids, err := issues_model.GetAssigneeIDsByIssue(ctx, comment.Issue.ID)
if err != nil {
- return fmt.Errorf("GetAssigneeIDsByIssue(%d): %w", ctx.Issue.ID, err)
+ return fmt.Errorf("GetAssigneeIDsByIssue(%d): %w", comment.Issue.ID, err)
}
unfiltered = append(unfiltered, ids...)
// =========== Participants (i.e. commenters, reviewers) ===========
- ids, err = issues_model.GetParticipantsIDsByIssueID(ctx, ctx.Issue.ID)
+ ids, err = issues_model.GetParticipantsIDsByIssueID(ctx, comment.Issue.ID)
if err != nil {
- return fmt.Errorf("GetParticipantsIDsByIssueID(%d): %w", ctx.Issue.ID, err)
+ return fmt.Errorf("GetParticipantsIDsByIssueID(%d): %w", comment.Issue.ID, err)
}
unfiltered = append(unfiltered, ids...)
// =========== Issue watchers ===========
- ids, err = issues_model.GetIssueWatchersIDs(ctx, ctx.Issue.ID, true)
+ ids, err = issues_model.GetIssueWatchersIDs(ctx, comment.Issue.ID, true)
if err != nil {
- return fmt.Errorf("GetIssueWatchersIDs(%d): %w", ctx.Issue.ID, err)
+ return fmt.Errorf("GetIssueWatchersIDs(%d): %w", comment.Issue.ID, err)
}
unfiltered = append(unfiltered, ids...)
// =========== Repo watchers ===========
// Make repo watchers last, since it's likely the list with the most users
- if !(ctx.Issue.IsPull && ctx.Issue.PullRequest.IsWorkInProgress(ctx) && ctx.ActionType != activities_model.ActionCreatePullRequest) {
- ids, err = repo_model.GetRepoWatchersIDs(ctx, ctx.Issue.RepoID)
+ if !(comment.Issue.IsPull && comment.Issue.PullRequest.IsWorkInProgress(ctx) && comment.ActionType != activities_model.ActionCreatePullRequest) {
+ ids, err = repo_model.GetRepoWatchersIDs(ctx, comment.Issue.RepoID)
if err != nil {
- return fmt.Errorf("GetRepoWatchersIDs(%d): %w", ctx.Issue.RepoID, err)
+ return fmt.Errorf("GetRepoWatchersIDs(%d): %w", comment.Issue.RepoID, err)
}
unfiltered = append(ids, unfiltered...)
}
@@ -93,36 +76,36 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
visited := make(container.Set[int64], len(unfiltered)+len(mentions)+1)
// Avoid mailing the doer
- if ctx.Doer.EmailNotificationsPreference != user_model.EmailNotificationsAndYourOwn && !ctx.ForceDoerNotification {
- visited.Add(ctx.Doer.ID)
+ if comment.Doer.EmailNotificationsPreference != user_model.EmailNotificationsAndYourOwn && !comment.ForceDoerNotification {
+ visited.Add(comment.Doer.ID)
}
// =========== Mentions ===========
- if err = mailIssueCommentBatch(ctx, mentions, visited, true); err != nil {
+ if err = mailIssueCommentBatch(ctx, comment, mentions, visited, true); err != nil {
return fmt.Errorf("mailIssueCommentBatch() mentions: %w", err)
}
// Avoid mailing explicit unwatched
- ids, err = issues_model.GetIssueWatchersIDs(ctx, ctx.Issue.ID, false)
+ ids, err = issues_model.GetIssueWatchersIDs(ctx, comment.Issue.ID, false)
if err != nil {
- return fmt.Errorf("GetIssueWatchersIDs(%d): %w", ctx.Issue.ID, err)
+ return fmt.Errorf("GetIssueWatchersIDs(%d): %w", comment.Issue.ID, err)
}
visited.AddMultiple(ids...)
- unfilteredUsers, err := user_model.GetMaileableUsersByIDs(ctx, unfiltered, false)
+ unfilteredUsers, err := user_model.GetMailableUsersByIDs(ctx, unfiltered, false)
if err != nil {
return err
}
- if err = mailIssueCommentBatch(ctx, unfilteredUsers, visited, false); err != nil {
+ if err = mailIssueCommentBatch(ctx, comment, unfilteredUsers, visited, false); err != nil {
return fmt.Errorf("mailIssueCommentBatch(): %w", err)
}
return nil
}
-func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, visited container.Set[int64], fromMention bool) error {
+func mailIssueCommentBatch(ctx context.Context, comment *mailComment, users []*user_model.User, visited container.Set[int64], fromMention bool) error {
checkUnit := unit.TypeIssues
- if ctx.Issue.IsPull {
+ if comment.Issue.IsPull {
checkUnit = unit.TypePullRequests
}
@@ -146,7 +129,7 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi
}
// test if this user is allowed to see the issue/pull
- if !access_model.CheckRepoUnitUser(ctx, ctx.Issue.Repo, user, checkUnit) {
+ if !access_model.CheckRepoUnitUser(ctx, comment.Issue.Repo, user, checkUnit) {
continue
}
@@ -158,7 +141,7 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi
// working backwards from the last (possibly) incomplete batch. If len(receivers) can be 0 this
// starting condition will need to be changed slightly
for i := ((len(receivers) - 1) / MailBatchSize) * MailBatchSize; i >= 0; i -= MailBatchSize {
- msgs, err := composeIssueCommentMessages(ctx, lang, receivers[i:], fromMention, "issue comments")
+ msgs, err := composeIssueCommentMessages(ctx, comment, lang, receivers[i:], fromMention, "issue comments")
if err != nil {
return err
}
@@ -185,9 +168,8 @@ func MailParticipants(ctx context.Context, issue *issues_model.Issue, doer *user
content = ""
}
forceDoerNotification := opType == activities_model.ActionAutoMergePullRequest
- if err := mailIssueCommentToParticipants(
- &mailCommentContext{
- Context: ctx,
+ if err := mailIssueCommentToParticipants(ctx,
+ &mailComment{
Issue: issue,
Doer: doer,
ActionType: opType,
@@ -199,3 +181,40 @@ func MailParticipants(ctx context.Context, issue *issues_model.Issue, doer *user
}
return nil
}
+
+// SendIssueAssignedMail composes and sends issue assigned email
+func SendIssueAssignedMail(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, content string, comment *issues_model.Comment, recipients []*user_model.User) error {
+ if setting.MailService == nil {
+ // No mail service configured
+ return nil
+ }
+
+ if err := issue.LoadRepo(ctx); err != nil {
+ log.Error("Unable to load repo [%d] for issue #%d [%d]. Error: %v", issue.RepoID, issue.Index, issue.ID, err)
+ return err
+ }
+
+ langMap := make(map[string][]*user_model.User)
+ for _, user := range recipients {
+ if !user.IsActive {
+ // don't send emails to inactive users
+ continue
+ }
+ langMap[user.Language] = append(langMap[user.Language], user)
+ }
+
+ for lang, tos := range langMap {
+ msgs, err := composeIssueCommentMessages(ctx, &mailComment{
+ Issue: issue,
+ Doer: doer,
+ ActionType: activities_model.ActionType(0),
+ Content: content,
+ Comment: comment,
+ }, lang, tos, false, "issue assigned")
+ if err != nil {
+ return err
+ }
+ SendAsync(msgs...)
+ }
+ return nil
+}
diff --git a/services/mailer/mail_issue_common.go b/services/mailer/mail_issue_common.go
new file mode 100644
index 0000000000..ebfd52162c
--- /dev/null
+++ b/services/mailer/mail_issue_common.go
@@ -0,0 +1,336 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package mailer
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "strconv"
+ "strings"
+ "time"
+
+ activities_model "code.gitea.io/gitea/models/activities"
+ issues_model "code.gitea.io/gitea/models/issues"
+ "code.gitea.io/gitea/models/renderhelper"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/emoji"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/markup/markdown"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/translation"
+ incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload"
+ sender_service "code.gitea.io/gitea/services/mailer/sender"
+ "code.gitea.io/gitea/services/mailer/token"
+)
+
+// maxEmailBodySize is the approximate maximum size of an email body in bytes
+// Many e-mail service providers have limitations on the size of the email body, it's usually from 10MB to 25MB
+const maxEmailBodySize = 9_000_000
+
+func fallbackMailSubject(issue *issues_model.Issue) string {
+ return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.FullName(), issue.Title, issue.Index)
+}
+
+type mailComment struct {
+ Issue *issues_model.Issue
+ Doer *user_model.User
+ ActionType activities_model.ActionType
+ Content string
+ Comment *issues_model.Comment
+ ForceDoerNotification bool
+}
+
+func composeIssueCommentMessages(ctx context.Context, comment *mailComment, lang string, recipients []*user_model.User, fromMention bool, info string) ([]*sender_service.Message, error) {
+ var (
+ subject string
+ link string
+ prefix string
+ // Fall back subject for bad templates, make sure subject is never empty
+ fallback string
+ reviewComments []*issues_model.Comment
+ )
+
+ commentType := issues_model.CommentTypeComment
+ if comment.Comment != nil {
+ commentType = comment.Comment.Type
+ link = comment.Issue.HTMLURL() + "#" + comment.Comment.HashTag()
+ } else {
+ link = comment.Issue.HTMLURL()
+ }
+
+ reviewType := issues_model.ReviewTypeComment
+ if comment.Comment != nil && comment.Comment.Review != nil {
+ reviewType = comment.Comment.Review.Type
+ }
+
+ // This is the body of the new issue or comment, not the mail body
+ rctx := renderhelper.NewRenderContextRepoComment(ctx, comment.Issue.Repo).WithUseAbsoluteLink(true)
+ body, err := markdown.RenderString(rctx, comment.Content)
+ if err != nil {
+ return nil, err
+ }
+
+ if setting.MailService.EmbedAttachmentImages {
+ attEmbedder := newMailAttachmentBase64Embedder(comment.Doer, comment.Issue.Repo, maxEmailBodySize)
+ bodyAfterEmbedding, err := attEmbedder.Base64InlineImages(ctx, body)
+ if err != nil {
+ log.Error("Failed to embed images in mail body: %v", err)
+ } else {
+ body = bodyAfterEmbedding
+ }
+ }
+ actType, actName, tplName := actionToTemplate(comment.Issue, comment.ActionType, commentType, reviewType)
+
+ if actName != "new" {
+ prefix = "Re: "
+ }
+ fallback = prefix + fallbackMailSubject(comment.Issue)
+
+ if comment.Comment != nil && comment.Comment.Review != nil {
+ reviewComments = make([]*issues_model.Comment, 0, 10)
+ for _, lines := range comment.Comment.Review.CodeComments {
+ for _, comments := range lines {
+ reviewComments = append(reviewComments, comments...)
+ }
+ }
+ }
+ locale := translation.NewLocale(lang)
+
+ mailMeta := map[string]any{
+ "locale": locale,
+ "FallbackSubject": fallback,
+ "Body": body,
+ "Link": link,
+ "Issue": comment.Issue,
+ "Comment": comment.Comment,
+ "IsPull": comment.Issue.IsPull,
+ "User": comment.Issue.Repo.MustOwner(ctx),
+ "Repo": comment.Issue.Repo.FullName(),
+ "Doer": comment.Doer,
+ "IsMention": fromMention,
+ "SubjectPrefix": prefix,
+ "ActionType": actType,
+ "ActionName": actName,
+ "ReviewComments": reviewComments,
+ "Language": locale.Language(),
+ "CanReply": setting.IncomingEmail.Enabled && commentType != issues_model.CommentTypePullRequestPush,
+ }
+
+ var mailSubject bytes.Buffer
+ if err := subjectTemplates.ExecuteTemplate(&mailSubject, tplName, mailMeta); err == nil {
+ subject = sanitizeSubject(mailSubject.String())
+ if subject == "" {
+ subject = fallback
+ }
+ } else {
+ log.Error("ExecuteTemplate [%s]: %v", tplName+"/subject", err)
+ }
+
+ subject = emoji.ReplaceAliases(subject)
+
+ mailMeta["Subject"] = subject
+
+ var mailBody bytes.Buffer
+
+ if err := bodyTemplates.ExecuteTemplate(&mailBody, tplName, mailMeta); err != nil {
+ log.Error("ExecuteTemplate [%s]: %v", tplName+"/body", err)
+ }
+
+ // Make sure to compose independent messages to avoid leaking user emails
+ msgID := generateMessageIDForIssue(comment.Issue, comment.Comment, comment.ActionType)
+ reference := generateMessageIDForIssue(comment.Issue, nil, activities_model.ActionType(0))
+
+ var replyPayload []byte
+ if comment.Comment != nil {
+ if comment.Comment.Type.HasMailReplySupport() {
+ replyPayload, err = incoming_payload.CreateReferencePayload(comment.Comment)
+ }
+ } else {
+ replyPayload, err = incoming_payload.CreateReferencePayload(comment.Issue)
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ unsubscribePayload, err := incoming_payload.CreateReferencePayload(comment.Issue)
+ if err != nil {
+ return nil, err
+ }
+
+ msgs := make([]*sender_service.Message, 0, len(recipients))
+ for _, recipient := range recipients {
+ msg := sender_service.NewMessageFrom(
+ recipient.Email,
+ fromDisplayName(comment.Doer),
+ setting.MailService.FromEmail,
+ subject,
+ mailBody.String(),
+ )
+ msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info)
+
+ msg.SetHeader("Message-ID", msgID)
+ msg.SetHeader("In-Reply-To", reference)
+
+ references := []string{reference}
+ listUnsubscribe := []string{"<" + comment.Issue.HTMLURL() + ">"}
+
+ if setting.IncomingEmail.Enabled {
+ if replyPayload != nil {
+ token, err := token.CreateToken(token.ReplyHandlerType, recipient, replyPayload)
+ if err != nil {
+ log.Error("CreateToken failed: %v", err)
+ } else {
+ replyAddress := strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1)
+ msg.ReplyTo = replyAddress
+ msg.SetHeader("List-Post", fmt.Sprintf("<mailto:%s>", replyAddress))
+
+ references = append(references, fmt.Sprintf("<reply-%s@%s>", token, setting.Domain))
+ }
+ }
+
+ token, err := token.CreateToken(token.UnsubscribeHandlerType, recipient, unsubscribePayload)
+ if err != nil {
+ log.Error("CreateToken failed: %v", err)
+ } else {
+ unsubAddress := strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1)
+ listUnsubscribe = append(listUnsubscribe, "<mailto:"+unsubAddress+">")
+ }
+ }
+
+ msg.SetHeader("References", references...)
+ msg.SetHeader("List-Unsubscribe", listUnsubscribe...)
+
+ for key, value := range generateAdditionalHeaders(comment, actType, recipient) {
+ msg.SetHeader(key, value)
+ }
+
+ msgs = append(msgs, msg)
+ }
+
+ return msgs, nil
+}
+
+// actionToTemplate returns the type and name of the action facing the user
+// (slightly different from activities_model.ActionType) and the name of the template to use (based on availability)
+func actionToTemplate(issue *issues_model.Issue, actionType activities_model.ActionType,
+ commentType issues_model.CommentType, reviewType issues_model.ReviewType,
+) (typeName, name, template string) {
+ if issue.IsPull {
+ typeName = "pull"
+ } else {
+ typeName = "issue"
+ }
+ switch actionType {
+ case activities_model.ActionCreateIssue, activities_model.ActionCreatePullRequest:
+ name = "new"
+ case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
+ name = "comment"
+ case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
+ name = "close"
+ case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
+ name = "reopen"
+ case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
+ name = "merge"
+ case activities_model.ActionPullReviewDismissed:
+ name = "review_dismissed"
+ case activities_model.ActionPullRequestReadyForReview:
+ name = "ready_for_review"
+ default:
+ switch commentType {
+ case issues_model.CommentTypeReview:
+ switch reviewType {
+ case issues_model.ReviewTypeApprove:
+ name = "approve"
+ case issues_model.ReviewTypeReject:
+ name = "reject"
+ default:
+ name = "review"
+ }
+ case issues_model.CommentTypeCode:
+ name = "code"
+ case issues_model.CommentTypeAssignees:
+ name = "assigned"
+ case issues_model.CommentTypePullRequestPush:
+ name = "push"
+ default:
+ name = "default"
+ }
+ }
+
+ template = typeName + "/" + name
+ ok := bodyTemplates.Lookup(template) != nil
+ if !ok && typeName != "issue" {
+ template = "issue/" + name
+ ok = bodyTemplates.Lookup(template) != nil
+ }
+ if !ok {
+ template = typeName + "/default"
+ ok = bodyTemplates.Lookup(template) != nil
+ }
+ if !ok {
+ template = "issue/default"
+ }
+ return typeName, name, template
+}
+
+func generateMessageIDForIssue(issue *issues_model.Issue, comment *issues_model.Comment, actionType activities_model.ActionType) string {
+ var path string
+ if issue.IsPull {
+ path = "pulls"
+ } else {
+ path = "issues"
+ }
+
+ var extra string
+ if comment != nil {
+ extra = fmt.Sprintf("/comment/%d", comment.ID)
+ } else {
+ switch actionType {
+ case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
+ extra = fmt.Sprintf("/close/%d", time.Now().UnixNano()/1e6)
+ case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
+ extra = fmt.Sprintf("/reopen/%d", time.Now().UnixNano()/1e6)
+ case activities_model.ActionMergePullRequest, activities_model.ActionAutoMergePullRequest:
+ extra = fmt.Sprintf("/merge/%d", time.Now().UnixNano()/1e6)
+ case activities_model.ActionPullRequestReadyForReview:
+ extra = fmt.Sprintf("/ready/%d", time.Now().UnixNano()/1e6)
+ }
+ }
+
+ return fmt.Sprintf("<%s/%s/%d%s@%s>", issue.Repo.FullName(), path, issue.Index, extra, setting.Domain)
+}
+
+func generateAdditionalHeaders(ctx *mailComment, reason string, recipient *user_model.User) map[string]string {
+ repo := ctx.Issue.Repo
+
+ return map[string]string{
+ // https://datatracker.ietf.org/doc/html/rfc2919
+ "List-ID": fmt.Sprintf("%s <%s.%s.%s>", repo.FullName(), repo.Name, repo.OwnerName, setting.Domain),
+
+ // https://datatracker.ietf.org/doc/html/rfc2369
+ "List-Archive": fmt.Sprintf("<%s>", repo.HTMLURL()),
+
+ "X-Mailer": "Gitea",
+ "X-Gitea-Reason": reason,
+ "X-Gitea-Sender": ctx.Doer.Name,
+ "X-Gitea-Recipient": recipient.Name,
+ "X-Gitea-Recipient-Address": recipient.Email,
+ "X-Gitea-Repository": repo.Name,
+ "X-Gitea-Repository-Path": repo.FullName(),
+ "X-Gitea-Repository-Link": repo.HTMLURL(),
+ "X-Gitea-Issue-ID": strconv.FormatInt(ctx.Issue.Index, 10),
+ "X-Gitea-Issue-Link": ctx.Issue.HTMLURL(),
+
+ "X-GitHub-Reason": reason,
+ "X-GitHub-Sender": ctx.Doer.Name,
+ "X-GitHub-Recipient": recipient.Name,
+ "X-GitHub-Recipient-Address": recipient.Email,
+
+ "X-GitLab-NotificationReason": reason,
+ "X-GitLab-Project": repo.Name,
+ "X-GitLab-Project-Path": repo.FullName(),
+ "X-GitLab-Issue-IID": strconv.FormatInt(ctx.Issue.Index, 10),
+ }
+}
diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go
index 796d63d27a..bfff73c39c 100644
--- a/services/mailer/mail_release.go
+++ b/services/mailer/mail_release.go
@@ -6,6 +6,7 @@ package mailer
import (
"bytes"
"context"
+ "fmt"
"code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
@@ -18,9 +19,11 @@ import (
sender_service "code.gitea.io/gitea/services/mailer/sender"
)
-const (
- tplNewReleaseMail templates.TplName = "release"
-)
+const tplNewReleaseMail templates.TplName = "release"
+
+func generateMessageIDForRelease(release *repo_model.Release) string {
+ return fmt.Sprintf("<%s/releases/%d@%s>", release.Repo.FullName(), release.ID, setting.Domain)
+}
// MailNewRelease send new release notify to all repo watchers.
func MailNewRelease(ctx context.Context, rel *repo_model.Release) {
@@ -35,9 +38,9 @@ func MailNewRelease(ctx context.Context, rel *repo_model.Release) {
return
}
- recipients, err := user_model.GetMaileableUsersByIDs(ctx, watcherIDList, false)
+ recipients, err := user_model.GetMailableUsersByIDs(ctx, watcherIDList, false)
if err != nil {
- log.Error("user_model.GetMaileableUsersByIDs: %v", err)
+ log.Error("user_model.GetMailableUsersByIDs: %v", err)
return
}
diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go
index 5f80654bcd..b6b2d5ca07 100644
--- a/services/mailer/mail_repo.go
+++ b/services/mailer/mail_repo.go
@@ -12,10 +12,13 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/translation"
sender_service "code.gitea.io/gitea/services/mailer/sender"
)
+const mailRepoTransferNotify templates.TplName = "notify/repo_transfer"
+
// SendRepoTransferNotifyMail triggers a notification e-mail when a pending repository transfer was created
func SendRepoTransferNotifyMail(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository) error {
if setting.MailService == nil {
diff --git a/services/mailer/mail_team_invite.go b/services/mailer/mail_team_invite.go
index 5ca44442f3..f4aa788dec 100644
--- a/services/mailer/mail_team_invite.go
+++ b/services/mailer/mail_team_invite.go
@@ -6,6 +6,7 @@ package mailer
import (
"bytes"
"context"
+ "errors"
"fmt"
"net/url"
@@ -18,9 +19,7 @@ import (
sender_service "code.gitea.io/gitea/services/mailer/sender"
)
-const (
- tplTeamInviteMail templates.TplName = "team_invite"
-)
+const tplTeamInviteMail templates.TplName = "team_invite"
// MailTeamInvite sends team invites
func MailTeamInvite(ctx context.Context, inviter *user_model.User, team *org_model.Team, invite *org_model.TeamInvite) error {
@@ -40,10 +39,10 @@ func MailTeamInvite(ctx context.Context, inviter *user_model.User, team *org_mod
if err != nil && !user_model.IsErrUserNotExist(err) {
return err
} else if user != nil && user.ProhibitLogin {
- return fmt.Errorf("login is prohibited for the invited user")
+ return errors.New("login is prohibited for the invited user")
}
- inviteRedirect := url.QueryEscape(fmt.Sprintf("/org/invite/%s", invite.Token))
+ inviteRedirect := url.QueryEscape("/org/invite/" + invite.Token)
inviteURL := fmt.Sprintf("%suser/sign_up?redirect_to=%s", setting.AppURL, inviteRedirect)
if (err == nil && user != nil) || setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration {
diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go
index 185b72f069..b15949f352 100644
--- a/services/mailer/mail_test.go
+++ b/services/mailer/mail_test.go
@@ -6,6 +6,7 @@ package mailer
import (
"bytes"
"context"
+ "encoding/base64"
"fmt"
"html/template"
"io"
@@ -23,9 +24,13 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/storage"
+ "code.gitea.io/gitea/modules/test"
+ "code.gitea.io/gitea/services/attachment"
sender_service "code.gitea.io/gitea/services/mailer/sender"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
const subjectTpl = `
@@ -53,22 +58,44 @@ const bodyTpl = `
func prepareMailerTest(t *testing.T) (doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, comment *issues_model.Comment) {
assert.NoError(t, unittest.PrepareTestDatabase())
- mailService := setting.Mailer{
- From: "test@gitea.com",
- }
-
- setting.MailService = &mailService
+ setting.MailService = &setting.Mailer{From: "test@gitea.com"}
setting.Domain = "localhost"
+ setting.AppURL = "https://try.gitea.io/"
doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1, Owner: doer})
issue = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1, Repo: repo, Poster: doer})
- assert.NoError(t, issue.LoadRepo(db.DefaultContext))
comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2, Issue: issue})
+ require.NoError(t, issue.LoadRepo(db.DefaultContext))
return doer, repo, issue, comment
}
-func TestComposeIssueCommentMessage(t *testing.T) {
+func prepareMailerBase64Test(t *testing.T) (doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, att1, att2 *repo_model.Attachment) {
+ user, repo, issue, comment := prepareMailerTest(t)
+ setting.MailService.EmbedAttachmentImages = true
+
+ att1, err := attachment.NewAttachment(t.Context(), &repo_model.Attachment{
+ RepoID: repo.ID,
+ IssueID: issue.ID,
+ UploaderID: user.ID,
+ CommentID: comment.ID,
+ Name: "test.png",
+ }, bytes.NewReader([]byte("\x89\x50\x4e\x47\x0d\x0a\x1a\x0a")), 8)
+ require.NoError(t, err)
+
+ att2, err = attachment.NewAttachment(t.Context(), &repo_model.Attachment{
+ RepoID: repo.ID,
+ IssueID: issue.ID,
+ UploaderID: user.ID,
+ CommentID: comment.ID,
+ Name: "test.png",
+ }, bytes.NewReader([]byte("\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"+strings.Repeat("\x00", 1024))), 8+1024)
+ require.NoError(t, err)
+
+ return user, repo, issue, att1, att2
+}
+
+func TestComposeIssueComment(t *testing.T) {
doer, _, issue, comment := prepareMailerTest(t)
markup.Init(&markup.RenderHelperFuncs{
@@ -84,9 +111,8 @@ func TestComposeIssueCommentMessage(t *testing.T) {
bodyTemplates = template.Must(template.New("issue/comment").Parse(bodyTpl))
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
- msgs, err := composeIssueCommentMessages(&mailCommentContext{
- Context: context.TODO(), // TODO: use a correct context
- Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
+ msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
+ Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
Content: fmt.Sprintf("test @%s %s#%d body", doer.Name, issue.Repo.FullName(), issue.Index),
Comment: comment,
}, "en-US", recipients, false, "issue comment")
@@ -109,7 +135,8 @@ func TestComposeIssueCommentMessage(t *testing.T) {
assert.Len(t, gomailMsg.GetGenHeader("List-Unsubscribe"), 2) // url + mailto
var buf bytes.Buffer
- gomailMsg.WriteTo(&buf)
+ _, err = gomailMsg.WriteTo(&buf)
+ require.NoError(t, err)
b, err := io.ReadAll(quotedprintable.NewReader(&buf))
assert.NoError(t, err)
@@ -123,6 +150,22 @@ func TestComposeIssueCommentMessage(t *testing.T) {
assert.Contains(t, string(b), fmt.Sprintf(`href="%s"`, issue.HTMLURL()))
}
+func TestMailMentionsComment(t *testing.T) {
+ doer, _, issue, comment := prepareMailerTest(t)
+ comment.Poster = doer
+ subjectTemplates = texttmpl.Must(texttmpl.New("issue/comment").Parse(subjectTpl))
+ bodyTemplates = template.Must(template.New("issue/comment").Parse(bodyTpl))
+ mails := 0
+
+ defer test.MockVariableValue(&SendAsync, func(msgs ...*sender_service.Message) {
+ mails = len(msgs)
+ })()
+
+ err := MailParticipantsComment(t.Context(), comment, activities_model.ActionCommentIssue, issue, []*user_model.User{})
+ require.NoError(t, err)
+ assert.Equal(t, 3, mails)
+}
+
func TestComposeIssueMessage(t *testing.T) {
doer, _, issue, _ := prepareMailerTest(t)
@@ -130,9 +173,8 @@ func TestComposeIssueMessage(t *testing.T) {
bodyTemplates = template.Must(template.New("issue/new").Parse(bodyTpl))
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
- msgs, err := composeIssueCommentMessages(&mailCommentContext{
- Context: context.TODO(), // TODO: use a correct context
- Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
+ msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
+ Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
Content: "test body",
}, "en-US", recipients, false, "issue create")
assert.NoError(t, err)
@@ -177,32 +219,28 @@ func TestTemplateSelection(t *testing.T) {
assert.Contains(t, wholemsg, expBody)
}
- msg := testComposeIssueCommentMessage(t, &mailCommentContext{
- Context: context.TODO(), // TODO: use a correct context
- Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
+ msg := testComposeIssueCommentMessage(t, &mailComment{
+ Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
Content: "test body",
}, recipients, false, "TestTemplateSelection")
expect(t, msg, "issue/new/subject", "issue/new/body")
- msg = testComposeIssueCommentMessage(t, &mailCommentContext{
- Context: context.TODO(), // TODO: use a correct context
- Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
+ msg = testComposeIssueCommentMessage(t, &mailComment{
+ Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
Content: "test body", Comment: comment,
}, recipients, false, "TestTemplateSelection")
expect(t, msg, "issue/default/subject", "issue/default/body")
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2, Repo: repo, Poster: doer})
comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4, Issue: pull})
- msg = testComposeIssueCommentMessage(t, &mailCommentContext{
- Context: context.TODO(), // TODO: use a correct context
- Issue: pull, Doer: doer, ActionType: activities_model.ActionCommentPull,
+ msg = testComposeIssueCommentMessage(t, &mailComment{
+ Issue: pull, Doer: doer, ActionType: activities_model.ActionCommentPull,
Content: "test body", Comment: comment,
}, recipients, false, "TestTemplateSelection")
expect(t, msg, "pull/comment/subject", "pull/comment/body")
- msg = testComposeIssueCommentMessage(t, &mailCommentContext{
- Context: context.TODO(), // TODO: use a correct context
- Issue: issue, Doer: doer, ActionType: activities_model.ActionCloseIssue,
+ msg = testComposeIssueCommentMessage(t, &mailComment{
+ Issue: issue, Doer: doer, ActionType: activities_model.ActionCloseIssue,
Content: "test body", Comment: comment,
}, recipients, false, "TestTemplateSelection")
expect(t, msg, "Re: [user2/repo1] issue1 (#1)", "issue/close/body")
@@ -219,9 +257,8 @@ func TestTemplateServices(t *testing.T) {
bodyTemplates = template.Must(template.New("issue/default").Parse(tplBody))
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
- msg := testComposeIssueCommentMessage(t, &mailCommentContext{
- Context: context.TODO(), // TODO: use a correct context
- Issue: issue, Doer: doer, ActionType: actionType,
+ msg := testComposeIssueCommentMessage(t, &mailComment{
+ Issue: issue, Doer: doer, ActionType: actionType,
Content: "test body", Comment: comment,
}, recipients, fromMention, "TestTemplateServices")
@@ -253,8 +290,8 @@ func TestTemplateServices(t *testing.T) {
"//Re: //")
}
-func testComposeIssueCommentMessage(t *testing.T, ctx *mailCommentContext, recipients []*user_model.User, fromMention bool, info string) *sender_service.Message {
- msgs, err := composeIssueCommentMessages(ctx, "en-US", recipients, fromMention, info)
+func testComposeIssueCommentMessage(t *testing.T, ctx *mailComment, recipients []*user_model.User, fromMention bool, info string) *sender_service.Message {
+ msgs, err := composeIssueCommentMessages(t.Context(), ctx, "en-US", recipients, fromMention, info)
assert.NoError(t, err)
assert.Len(t, msgs, 1)
return msgs[0]
@@ -263,10 +300,10 @@ func testComposeIssueCommentMessage(t *testing.T, ctx *mailCommentContext, recip
func TestGenerateAdditionalHeaders(t *testing.T) {
doer, _, issue, _ := prepareMailerTest(t)
- ctx := &mailCommentContext{Context: context.TODO() /* TODO: use a correct context */, Issue: issue, Doer: doer}
+ comment := &mailComment{Issue: issue, Doer: doer}
recipient := &user_model.User{Name: "test", Email: "test@gitea.com"}
- headers := generateAdditionalHeaders(ctx, "dummy-reason", recipient)
+ headers := generateAdditionalHeaders(comment, "dummy-reason", recipient)
expected := map[string]string{
"List-ID": "user2/repo1 <repo1.user2.localhost>",
@@ -390,9 +427,7 @@ func TestGenerateMessageIDForIssue(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := generateMessageIDForIssue(tt.args.issue, tt.args.comment, tt.args.actionType)
- if !strings.HasPrefix(got, tt.prefix) {
- t.Errorf("generateMessageIDForIssue() = %v, want %v", got, tt.prefix)
- }
+ assert.True(t, strings.HasPrefix(got, tt.prefix), "%v, want %v", got, tt.prefix)
})
}
}
@@ -406,9 +441,9 @@ func TestGenerateMessageIDForRelease(t *testing.T) {
}
func TestFromDisplayName(t *testing.T) {
- template, err := texttmpl.New("mailFrom").Parse("{{ .DisplayName }}")
+ tmpl, err := texttmpl.New("mailFrom").Parse("{{ .DisplayName }}")
assert.NoError(t, err)
- setting.MailService = &setting.Mailer{FromDisplayNameFormatTemplate: template}
+ setting.MailService = &setting.Mailer{FromDisplayNameFormatTemplate: tmpl}
defer func() { setting.MailService = nil }()
tests := []struct {
@@ -432,14 +467,14 @@ func TestFromDisplayName(t *testing.T) {
t.Run(tc.userDisplayName, func(t *testing.T) {
user := &user_model.User{FullName: tc.userDisplayName, Name: "tmp"}
got := fromDisplayName(user)
- assert.EqualValues(t, tc.fromDisplayName, got)
+ assert.Equal(t, tc.fromDisplayName, got)
})
}
t.Run("template with all available vars", func(t *testing.T) {
- template, err = texttmpl.New("mailFrom").Parse("{{ .DisplayName }} (by {{ .AppName }} on [{{ .Domain }}])")
+ tmpl, err = texttmpl.New("mailFrom").Parse("{{ .DisplayName }} (by {{ .AppName }} on [{{ .Domain }}])")
assert.NoError(t, err)
- setting.MailService = &setting.Mailer{FromDisplayNameFormatTemplate: template}
+ setting.MailService = &setting.Mailer{FromDisplayNameFormatTemplate: tmpl}
oldAppName := setting.AppName
setting.AppName = "Code IT"
oldDomain := setting.Domain
@@ -449,6 +484,74 @@ func TestFromDisplayName(t *testing.T) {
setting.Domain = oldDomain
}()
- assert.EqualValues(t, "Mister X (by Code IT on [code.it])", fromDisplayName(&user_model.User{FullName: "Mister X", Name: "tmp"}))
+ assert.Equal(t, "Mister X (by Code IT on [code.it])", fromDisplayName(&user_model.User{FullName: "Mister X", Name: "tmp"}))
+ })
+}
+
+func TestEmbedBase64Images(t *testing.T) {
+ user, repo, issue, att1, att2 := prepareMailerBase64Test(t)
+ // comment := &mailComment{Issue: issue, Doer: user}
+
+ imgExternalURL := "https://via.placeholder.com/10"
+ imgExternalImg := fmt.Sprintf(`<img src="%s"/>`, imgExternalURL)
+
+ att1URL := setting.AppURL + repo.Owner.Name + "/" + repo.Name + "/attachments/" + att1.UUID
+ att1Img := fmt.Sprintf(`<img src="%s"/>`, att1URL)
+ att1Base64 := "data:image/png;base64,iVBORw0KGgo="
+ att1ImgBase64 := fmt.Sprintf(`<img src="%s"/>`, att1Base64)
+
+ att2URL := setting.AppURL + repo.Owner.Name + "/" + repo.Name + "/attachments/" + att2.UUID
+ att2Img := fmt.Sprintf(`<img src="%s"/>`, att2URL)
+ att2File, err := storage.Attachments.Open(att2.RelativePath())
+ require.NoError(t, err)
+ defer att2File.Close()
+ att2Bytes, err := io.ReadAll(att2File)
+ require.NoError(t, err)
+ require.Greater(t, len(att2Bytes), 1024)
+ att2Base64 := "data:image/png;base64," + base64.StdEncoding.EncodeToString(att2Bytes)
+ att2ImgBase64 := fmt.Sprintf(`<img src="%s"/>`, att2Base64)
+
+ t.Run("ComposeMessage", func(t *testing.T) {
+ subjectTemplates = texttmpl.Must(texttmpl.New("issue/new").Parse(subjectTpl))
+ bodyTemplates = template.Must(template.New("issue/new").Parse(bodyTpl))
+
+ issue.Content = fmt.Sprintf(`MSG-BEFORE <image src="attachments/%s"> MSG-AFTER`, att1.UUID)
+ require.NoError(t, issues_model.UpdateIssueCols(t.Context(), issue, "content"))
+
+ recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
+ msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
+ Issue: issue,
+ Doer: user,
+ ActionType: activities_model.ActionCreateIssue,
+ Content: issue.Content,
+ }, "en-US", recipients, false, "issue create")
+ require.NoError(t, err)
+
+ mailBody := msgs[0].Body
+ assert.Regexp(t, `MSG-BEFORE <a[^>]+><img src="data:image/png;base64,iVBORw0KGgo=".*/></a> MSG-AFTER`, mailBody)
+ })
+
+ t.Run("EmbedInstanceImageSkipExternalImage", func(t *testing.T) {
+ mailBody := "<html><head></head><body><p>Test1</p>" + imgExternalImg + "<p>Test2</p>" + att1Img + "<p>Test3</p></body></html>"
+ expectedMailBody := "<html><head></head><body><p>Test1</p>" + imgExternalImg + "<p>Test2</p>" + att1ImgBase64 + "<p>Test3</p></body></html>"
+ b64embedder := newMailAttachmentBase64Embedder(user, repo, 1024)
+ resultMailBody, err := b64embedder.Base64InlineImages(t.Context(), template.HTML(mailBody))
+ require.NoError(t, err)
+ assert.Equal(t, expectedMailBody, string(resultMailBody))
+ })
+
+ t.Run("LimitedEmailBodySize", func(t *testing.T) {
+ mailBody := fmt.Sprintf("<html><head></head><body>%s%s</body></html>", att1Img, att2Img)
+ b64embedder := newMailAttachmentBase64Embedder(user, repo, 1024)
+ resultMailBody, err := b64embedder.Base64InlineImages(t.Context(), template.HTML(mailBody))
+ require.NoError(t, err)
+ expected := fmt.Sprintf("<html><head></head><body>%s%s</body></html>", att1ImgBase64, att2Img)
+ assert.Equal(t, expected, string(resultMailBody))
+
+ b64embedder = newMailAttachmentBase64Embedder(user, repo, 4096)
+ resultMailBody, err = b64embedder.Base64InlineImages(t.Context(), template.HTML(mailBody))
+ require.NoError(t, err)
+ expected = fmt.Sprintf("<html><head></head><body>%s%s</body></html>", att1ImgBase64, att2ImgBase64)
+ assert.Equal(t, expected, string(resultMailBody))
})
}
diff --git a/services/mailer/mail_user.go b/services/mailer/mail_user.go
new file mode 100644
index 0000000000..5a200a5fa7
--- /dev/null
+++ b/services/mailer/mail_user.go
@@ -0,0 +1,161 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package mailer
+
+import (
+ "bytes"
+ "fmt"
+
+ repo_model "code.gitea.io/gitea/models/repo"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/translation"
+ sender_service "code.gitea.io/gitea/services/mailer/sender"
+)
+
+const (
+ mailAuthActivate templates.TplName = "auth/activate"
+ mailAuthActivateEmail templates.TplName = "auth/activate_email"
+ mailAuthResetPassword templates.TplName = "auth/reset_passwd"
+ mailAuthRegisterNotify templates.TplName = "auth/register_notify"
+ mailNotifyCollaborator templates.TplName = "notify/collaborator"
+)
+
+// sendUserMail sends a mail to the user
+func sendUserMail(language string, u *user_model.User, tpl templates.TplName, code, subject, info string) {
+ locale := translation.NewLocale(language)
+ data := map[string]any{
+ "locale": locale,
+ "DisplayName": u.DisplayName(),
+ "ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale),
+ "ResetPwdCodeLives": timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, locale),
+ "Code": code,
+ "Language": locale.Language(),
+ }
+
+ var content bytes.Buffer
+
+ if err := bodyTemplates.ExecuteTemplate(&content, string(tpl), data); err != nil {
+ log.Error("Template: %v", err)
+ return
+ }
+
+ msg := sender_service.NewMessage(u.EmailTo(), subject, content.String())
+ msg.Info = fmt.Sprintf("UID: %d, %s", u.ID, info)
+
+ SendAsync(msg)
+}
+
+// SendActivateAccountMail sends an activation mail to the user (new user registration)
+func SendActivateAccountMail(locale translation.Locale, u *user_model.User) {
+ if setting.MailService == nil {
+ // No mail service configured
+ return
+ }
+ opts := &user_model.TimeLimitCodeOptions{Purpose: user_model.TimeLimitCodeActivateAccount}
+ sendUserMail(locale.Language(), u, mailAuthActivate, user_model.GenerateUserTimeLimitCode(opts, u), locale.TrString("mail.activate_account"), "activate account")
+}
+
+// SendResetPasswordMail sends a password reset mail to the user
+func SendResetPasswordMail(u *user_model.User) {
+ if setting.MailService == nil {
+ // No mail service configured
+ return
+ }
+ locale := translation.NewLocale(u.Language)
+ opts := &user_model.TimeLimitCodeOptions{Purpose: user_model.TimeLimitCodeResetPassword}
+ sendUserMail(u.Language, u, mailAuthResetPassword, user_model.GenerateUserTimeLimitCode(opts, u), locale.TrString("mail.reset_password"), "recover account")
+}
+
+// SendActivateEmailMail sends confirmation email to confirm new email address
+func SendActivateEmailMail(u *user_model.User, email string) {
+ if setting.MailService == nil {
+ // No mail service configured
+ return
+ }
+ locale := translation.NewLocale(u.Language)
+ opts := &user_model.TimeLimitCodeOptions{Purpose: user_model.TimeLimitCodeActivateEmail, NewEmail: email}
+ data := map[string]any{
+ "locale": locale,
+ "DisplayName": u.DisplayName(),
+ "ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale),
+ "Code": user_model.GenerateUserTimeLimitCode(opts, u),
+ "Email": email,
+ "Language": locale.Language(),
+ }
+
+ var content bytes.Buffer
+
+ if err := bodyTemplates.ExecuteTemplate(&content, string(mailAuthActivateEmail), data); err != nil {
+ log.Error("Template: %v", err)
+ return
+ }
+
+ msg := sender_service.NewMessage(email, locale.TrString("mail.activate_email"), content.String())
+ msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID)
+
+ SendAsync(msg)
+}
+
+// SendRegisterNotifyMail triggers a notify e-mail by admin created a account.
+func SendRegisterNotifyMail(u *user_model.User) {
+ if setting.MailService == nil || !u.IsActive {
+ // No mail service configured OR user is inactive
+ return
+ }
+ locale := translation.NewLocale(u.Language)
+
+ data := map[string]any{
+ "locale": locale,
+ "DisplayName": u.DisplayName(),
+ "Username": u.Name,
+ "Language": locale.Language(),
+ }
+
+ var content bytes.Buffer
+
+ if err := bodyTemplates.ExecuteTemplate(&content, string(mailAuthRegisterNotify), data); err != nil {
+ log.Error("Template: %v", err)
+ return
+ }
+
+ msg := sender_service.NewMessage(u.EmailTo(), locale.TrString("mail.register_notify", setting.AppName), content.String())
+ msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID)
+
+ SendAsync(msg)
+}
+
+// SendCollaboratorMail sends mail notification to new collaborator.
+func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository) {
+ if setting.MailService == nil || !u.IsActive {
+ // No mail service configured OR the user is inactive
+ return
+ }
+ locale := translation.NewLocale(u.Language)
+ repoName := repo.FullName()
+
+ subject := locale.TrString("mail.repo.collaborator.added.subject", doer.DisplayName(), repoName)
+ data := map[string]any{
+ "locale": locale,
+ "Subject": subject,
+ "RepoName": repoName,
+ "Link": repo.HTMLURL(),
+ "Language": locale.Language(),
+ }
+
+ var content bytes.Buffer
+
+ if err := bodyTemplates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil {
+ log.Error("Template: %v", err)
+ return
+ }
+
+ msg := sender_service.NewMessage(u.EmailTo(), subject, content.String())
+ msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID)
+
+ SendAsync(msg)
+}
diff --git a/services/mailer/notify.go b/services/mailer/notify.go
index e48b5d399d..77c366fe31 100644
--- a/services/mailer/notify.go
+++ b/services/mailer/notify.go
@@ -12,6 +12,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
+ issue_service "code.gitea.io/gitea/services/issue"
notify_service "code.gitea.io/gitea/services/notify"
)
@@ -30,15 +31,16 @@ func (m *mailNotifier) CreateIssueComment(ctx context.Context, doer *user_model.
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
) {
var act activities_model.ActionType
- if comment.Type == issues_model.CommentTypeClose {
+ switch comment.Type {
+ case issues_model.CommentTypeClose:
act = activities_model.ActionCloseIssue
- } else if comment.Type == issues_model.CommentTypeReopen {
+ case issues_model.CommentTypeReopen:
act = activities_model.ActionReopenIssue
- } else if comment.Type == issues_model.CommentTypeComment {
+ case issues_model.CommentTypeComment:
act = activities_model.ActionCommentIssue
- } else if comment.Type == issues_model.CommentTypeCode {
+ case issues_model.CommentTypeCode:
act = activities_model.ActionCommentIssue
- } else if comment.Type == issues_model.CommentTypePullRequestPush {
+ case issues_model.CommentTypePullRequestPush:
act = 0
}
@@ -94,11 +96,12 @@ func (m *mailNotifier) NewPullRequest(ctx context.Context, pr *issues_model.Pull
func (m *mailNotifier) PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, r *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) {
var act activities_model.ActionType
- if comment.Type == issues_model.CommentTypeClose {
+ switch comment.Type {
+ case issues_model.CommentTypeClose:
act = activities_model.ActionCloseIssue
- } else if comment.Type == issues_model.CommentTypeReopen {
+ case issues_model.CommentTypeReopen:
act = activities_model.ActionReopenIssue
- } else if comment.Type == issues_model.CommentTypeComment {
+ case issues_model.CommentTypeComment:
act = activities_model.ActionCommentPull
}
if err := MailParticipantsComment(ctx, comment, act, pr.Issue, mentions); err != nil {
@@ -169,7 +172,7 @@ func (m *mailNotifier) PullRequestPushCommits(ctx context.Context, doer *user_mo
log.Error("comment.Issue.PullRequest.LoadBaseRepo: %v", err)
return
}
- if err := comment.LoadPushCommits(ctx); err != nil {
+ if err := issue_service.LoadCommentPushCommits(ctx, comment); err != nil {
log.Error("comment.LoadPushCommits: %v", err)
}
m.CreateIssueComment(ctx, doer, comment.Issue.Repo, comment.Issue, comment, nil)
diff --git a/services/mailer/sender/message_test.go b/services/mailer/sender/message_test.go
index 63d0bc349a..ae153ebf05 100644
--- a/services/mailer/sender/message_test.go
+++ b/services/mailer/sender/message_test.go
@@ -108,9 +108,9 @@ func extractMailHeaderAndContent(t *testing.T, mail string) (map[string]string,
}
content := strings.TrimSpace("boundary=" + parts[1])
- hParts := strings.Split(parts[0], "\n")
+ hParts := strings.SplitSeq(parts[0], "\n")
- for _, hPart := range hParts {
+ for hPart := range hParts {
parts := strings.SplitN(hPart, ":", 2)
hk := strings.TrimSpace(parts[0])
if hk != "" {
diff --git a/services/mailer/sender/smtp.go b/services/mailer/sender/smtp.go
index c53c3da997..8dc1b40b74 100644
--- a/services/mailer/sender/smtp.go
+++ b/services/mailer/sender/smtp.go
@@ -5,6 +5,7 @@ package sender
import (
"crypto/tls"
+ "errors"
"fmt"
"io"
"net"
@@ -99,7 +100,7 @@ func (s *SMTPSender) Send(from string, to []string, msg io.WriterTo) error {
canAuth, options := client.Extension("AUTH")
if len(opts.User) > 0 {
if !canAuth {
- return fmt.Errorf("SMTP server does not support AUTH, but credentials provided")
+ return errors.New("SMTP server does not support AUTH, but credentials provided")
}
var auth smtp.Auth
diff --git a/services/mailer/sender/smtp_auth.go b/services/mailer/sender/smtp_auth.go
index 260b12437b..c60e0dbfbb 100644
--- a/services/mailer/sender/smtp_auth.go
+++ b/services/mailer/sender/smtp_auth.go
@@ -4,6 +4,7 @@
package sender
import (
+ "errors"
"fmt"
"github.com/Azure/go-ntlmssp"
@@ -60,7 +61,7 @@ func (a *ntlmAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
func (a *ntlmAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if more {
if len(fromServer) == 0 {
- return nil, fmt.Errorf("ntlm ChallengeMessage is empty")
+ return nil, errors.New("ntlm ChallengeMessage is empty")
}
authenticateMessage, err := ntlmssp.ProcessChallenge(fromServer, a.username, a.password, a.domainNeeded)
return authenticateMessage, err
diff --git a/services/markup/renderhelper.go b/services/markup/renderhelper.go
index 4b9852b48b..ea494146a7 100644
--- a/services/markup/renderhelper.go
+++ b/services/markup/renderhelper.go
@@ -21,8 +21,8 @@ func FormalRenderHelperFuncs() *markup.RenderHelperFuncs {
return false
}
- giteaCtx, ok := ctx.(*gitea_context.Context)
- if !ok {
+ giteaCtx := gitea_context.GetWebContext(ctx)
+ if giteaCtx == nil {
// when using general context, use user's visibility to check
return mentionedUser.Visibility.IsPublic()
}
diff --git a/services/markup/renderhelper_codepreview.go b/services/markup/renderhelper_codepreview.go
index 170c70c409..fa1eb824a2 100644
--- a/services/markup/renderhelper_codepreview.go
+++ b/services/markup/renderhelper_codepreview.go
@@ -6,7 +6,7 @@ package markup
import (
"bufio"
"context"
- "fmt"
+ "errors"
"html/template"
"strings"
@@ -14,13 +14,13 @@ import (
"code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/charset"
+ "code.gitea.io/gitea/modules/git/languagestats"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/indexer/code"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
gitea_context "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/repository/files"
)
func renderRepoFileCodePreview(ctx context.Context, opts markup.RenderCodePreviewOptions) (template.HTML, error) {
@@ -36,9 +36,9 @@ func renderRepoFileCodePreview(ctx context.Context, opts markup.RenderCodePrevie
return "", err
}
- webCtx, ok := ctx.Value(gitea_context.WebContextKey).(*gitea_context.Context)
- if !ok {
- return "", fmt.Errorf("context is not a web context")
+ webCtx := gitea_context.GetWebContext(ctx)
+ if webCtx == nil {
+ return "", errors.New("context is not a web context")
}
doer := webCtx.Doer
@@ -61,14 +61,14 @@ func renderRepoFileCodePreview(ctx context.Context, opts markup.RenderCodePrevie
return "", err
}
- language, _ := files.TryGetContentLanguage(gitRepo, opts.CommitID, opts.FilePath)
+ language, _ := languagestats.GetFileLanguage(ctx, gitRepo, opts.CommitID, opts.FilePath)
blob, err := commit.GetBlobByPath(opts.FilePath)
if err != nil {
return "", err
}
if blob.Size() > setting.UI.MaxDisplayFileSize {
- return "", fmt.Errorf("file is too large")
+ return "", errors.New("file is too large")
}
dataRc, err := blob.DataAsync()
diff --git a/services/markup/renderhelper_issueicontitle.go b/services/markup/renderhelper_issueicontitle.go
index 53a508e908..27b5595fa9 100644
--- a/services/markup/renderhelper_issueicontitle.go
+++ b/services/markup/renderhelper_issueicontitle.go
@@ -5,6 +5,7 @@ package markup
import (
"context"
+ "errors"
"fmt"
"html/template"
@@ -18,9 +19,9 @@ import (
)
func renderRepoIssueIconTitle(ctx context.Context, opts markup.RenderIssueIconTitleOptions) (_ template.HTML, err error) {
- webCtx, ok := ctx.Value(gitea_context.WebContextKey).(*gitea_context.Context)
- if !ok {
- return "", fmt.Errorf("context is not a web context")
+ webCtx := gitea_context.GetWebContext(ctx)
+ if webCtx == nil {
+ return "", errors.New("context is not a web context")
}
textIssueIndex := fmt.Sprintf("(#%d)", opts.IssueIndex)
diff --git a/services/markup/renderhelper_mention_test.go b/services/markup/renderhelper_mention_test.go
index c244fa3d21..d54ab13a48 100644
--- a/services/markup/renderhelper_mention_test.go
+++ b/services/markup/renderhelper_mention_test.go
@@ -4,7 +4,6 @@
package markup
import (
- "context"
"net/http"
"net/http/httptest"
"testing"
@@ -32,13 +31,13 @@ func TestRenderHelperMention(t *testing.T) {
unittest.AssertCount(t, &user.User{Name: userNoSuch}, 0)
// when using general context, use user's visibility to check
- assert.True(t, FormalRenderHelperFuncs().IsUsernameMentionable(context.Background(), userPublic))
- assert.False(t, FormalRenderHelperFuncs().IsUsernameMentionable(context.Background(), userLimited))
- assert.False(t, FormalRenderHelperFuncs().IsUsernameMentionable(context.Background(), userPrivate))
- assert.False(t, FormalRenderHelperFuncs().IsUsernameMentionable(context.Background(), userNoSuch))
+ assert.True(t, FormalRenderHelperFuncs().IsUsernameMentionable(t.Context(), userPublic))
+ assert.False(t, FormalRenderHelperFuncs().IsUsernameMentionable(t.Context(), userLimited))
+ assert.False(t, FormalRenderHelperFuncs().IsUsernameMentionable(t.Context(), userPrivate))
+ assert.False(t, FormalRenderHelperFuncs().IsUsernameMentionable(t.Context(), userNoSuch))
// when using web context, use user.IsUserVisibleToViewer to check
- req, err := http.NewRequest("GET", "/", nil)
+ req, err := http.NewRequest(http.MethodGet, "/", nil)
assert.NoError(t, err)
base := gitea_context.NewBaseContextForTest(httptest.NewRecorder(), req)
giteaCtx := gitea_context.NewWebContext(base, &contexttest.MockRender{}, nil)
diff --git a/services/migrations/codebase.go b/services/migrations/codebase.go
index 492fc908e9..240c7bcdc9 100644
--- a/services/migrations/codebase.go
+++ b/services/migrations/codebase.go
@@ -66,7 +66,6 @@ type codebaseUser struct {
// from Codebase
type CodebaseDownloader struct {
base.NullDownloader
- ctx context.Context
client *http.Client
baseURL *url.URL
projectURL *url.URL
@@ -77,17 +76,11 @@ type CodebaseDownloader struct {
commitMap map[string]string
}
-// SetContext set context
-func (d *CodebaseDownloader) SetContext(ctx context.Context) {
- d.ctx = ctx
-}
-
// NewCodebaseDownloader creates a new downloader
-func NewCodebaseDownloader(ctx context.Context, projectURL *url.URL, project, repoName, username, password string) *CodebaseDownloader {
+func NewCodebaseDownloader(_ context.Context, projectURL *url.URL, project, repoName, username, password string) *CodebaseDownloader {
baseURL, _ := url.Parse("https://api3.codebasehq.com")
downloader := &CodebaseDownloader{
- ctx: ctx,
baseURL: baseURL,
projectURL: projectURL,
project: project,
@@ -127,7 +120,7 @@ func (d *CodebaseDownloader) FormatCloneURL(opts base.MigrateOptions, remoteAddr
return opts.CloneAddr, nil
}
-func (d *CodebaseDownloader) callAPI(endpoint string, parameter map[string]string, result any) error {
+func (d *CodebaseDownloader) callAPI(ctx context.Context, endpoint string, parameter map[string]string, result any) error {
u, err := d.baseURL.Parse(endpoint)
if err != nil {
return err
@@ -141,7 +134,7 @@ func (d *CodebaseDownloader) callAPI(endpoint string, parameter map[string]strin
u.RawQuery = query.Encode()
}
- req, err := http.NewRequestWithContext(d.ctx, "GET", u.String(), nil)
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
if err != nil {
return err
}
@@ -158,7 +151,7 @@ func (d *CodebaseDownloader) callAPI(endpoint string, parameter map[string]strin
// GetRepoInfo returns repository information
// https://support.codebasehq.com/kb/projects
-func (d *CodebaseDownloader) GetRepoInfo() (*base.Repository, error) {
+func (d *CodebaseDownloader) GetRepoInfo(ctx context.Context) (*base.Repository, error) {
var rawRepository struct {
XMLName xml.Name `xml:"repository"`
Name string `xml:"name"`
@@ -169,6 +162,7 @@ func (d *CodebaseDownloader) GetRepoInfo() (*base.Repository, error) {
}
err := d.callAPI(
+ ctx,
fmt.Sprintf("/%s/%s", d.project, d.repoName),
nil,
&rawRepository,
@@ -187,7 +181,7 @@ func (d *CodebaseDownloader) GetRepoInfo() (*base.Repository, error) {
// GetMilestones returns milestones
// https://support.codebasehq.com/kb/tickets-and-milestones/milestones
-func (d *CodebaseDownloader) GetMilestones() ([]*base.Milestone, error) {
+func (d *CodebaseDownloader) GetMilestones(ctx context.Context) ([]*base.Milestone, error) {
var rawMilestones struct {
XMLName xml.Name `xml:"ticketing-milestone"`
Type string `xml:"type,attr"`
@@ -209,6 +203,7 @@ func (d *CodebaseDownloader) GetMilestones() ([]*base.Milestone, error) {
}
err := d.callAPI(
+ ctx,
fmt.Sprintf("/%s/milestones", d.project),
nil,
&rawMilestones,
@@ -245,7 +240,7 @@ func (d *CodebaseDownloader) GetMilestones() ([]*base.Milestone, error) {
// GetLabels returns labels
// https://support.codebasehq.com/kb/tickets-and-milestones/statuses-priorities-and-categories
-func (d *CodebaseDownloader) GetLabels() ([]*base.Label, error) {
+func (d *CodebaseDownloader) GetLabels(ctx context.Context) ([]*base.Label, error) {
var rawTypes struct {
XMLName xml.Name `xml:"ticketing-types"`
Type string `xml:"type,attr"`
@@ -259,6 +254,7 @@ func (d *CodebaseDownloader) GetLabels() ([]*base.Label, error) {
}
err := d.callAPI(
+ ctx,
fmt.Sprintf("/%s/tickets/types", d.project),
nil,
&rawTypes,
@@ -284,7 +280,7 @@ type codebaseIssueContext struct {
// GetIssues returns issues, limits are not supported
// https://support.codebasehq.com/kb/tickets-and-milestones
// https://support.codebasehq.com/kb/tickets-and-milestones/updating-tickets
-func (d *CodebaseDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, error) {
+func (d *CodebaseDownloader) GetIssues(ctx context.Context, _, _ int) ([]*base.Issue, bool, error) {
var rawIssues struct {
XMLName xml.Name `xml:"tickets"`
Type string `xml:"type,attr"`
@@ -324,6 +320,7 @@ func (d *CodebaseDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool,
}
err := d.callAPI(
+ ctx,
fmt.Sprintf("/%s/tickets", d.project),
nil,
&rawIssues,
@@ -358,6 +355,7 @@ func (d *CodebaseDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool,
} `xml:"ticket-note"`
}
err := d.callAPI(
+ ctx,
fmt.Sprintf("/%s/tickets/%d/notes", d.project, issue.TicketID.Value),
nil,
&notes,
@@ -370,7 +368,7 @@ func (d *CodebaseDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool,
if len(note.Content) == 0 {
continue
}
- poster := d.tryGetUser(note.UserID.Value)
+ poster := d.tryGetUser(ctx, note.UserID.Value)
comments = append(comments, &base.Comment{
IssueIndex: issue.TicketID.Value,
Index: note.ID.Value,
@@ -390,7 +388,7 @@ func (d *CodebaseDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool,
if issue.Status.TreatAsClosed.Value {
state = "closed"
}
- poster := d.tryGetUser(issue.ReporterID.Value)
+ poster := d.tryGetUser(ctx, issue.ReporterID.Value)
issues = append(issues, &base.Issue{
Title: issue.Summary,
Number: issue.TicketID.Value,
@@ -419,7 +417,7 @@ func (d *CodebaseDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool,
}
// GetComments returns comments
-func (d *CodebaseDownloader) GetComments(commentable base.Commentable) ([]*base.Comment, bool, error) {
+func (d *CodebaseDownloader) GetComments(_ context.Context, commentable base.Commentable) ([]*base.Comment, bool, error) {
context, ok := commentable.GetContext().(codebaseIssueContext)
if !ok {
return nil, false, fmt.Errorf("unexpected context: %+v", commentable.GetContext())
@@ -430,7 +428,7 @@ func (d *CodebaseDownloader) GetComments(commentable base.Commentable) ([]*base.
// GetPullRequests returns pull requests
// https://support.codebasehq.com/kb/repositories/merge-requests
-func (d *CodebaseDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) {
+func (d *CodebaseDownloader) GetPullRequests(ctx context.Context, page, perPage int) ([]*base.PullRequest, bool, error) {
var rawMergeRequests struct {
XMLName xml.Name `xml:"merge-requests"`
Type string `xml:"type,attr"`
@@ -443,6 +441,7 @@ func (d *CodebaseDownloader) GetPullRequests(page, perPage int) ([]*base.PullReq
}
err := d.callAPI(
+ ctx,
fmt.Sprintf("/%s/%s/merge_requests", d.project, d.repoName),
map[string]string{
"query": `"Target Project" is "` + d.repoName + `"`,
@@ -503,6 +502,7 @@ func (d *CodebaseDownloader) GetPullRequests(page, perPage int) ([]*base.PullReq
} `xml:"comments"`
}
err := d.callAPI(
+ ctx,
fmt.Sprintf("/%s/%s/merge_requests/%d", d.project, d.repoName, mr.ID.Value),
nil,
&rawMergeRequest,
@@ -531,7 +531,7 @@ func (d *CodebaseDownloader) GetPullRequests(page, perPage int) ([]*base.PullReq
}
continue
}
- poster := d.tryGetUser(comment.UserID.Value)
+ poster := d.tryGetUser(ctx, comment.UserID.Value)
comments = append(comments, &base.Comment{
IssueIndex: number,
Index: comment.ID.Value,
@@ -547,7 +547,7 @@ func (d *CodebaseDownloader) GetPullRequests(page, perPage int) ([]*base.PullReq
comments = append(comments, &base.Comment{})
}
- poster := d.tryGetUser(rawMergeRequest.UserID.Value)
+ poster := d.tryGetUser(ctx, rawMergeRequest.UserID.Value)
pullRequests = append(pullRequests, &base.PullRequest{
Title: rawMergeRequest.Subject,
@@ -563,12 +563,12 @@ func (d *CodebaseDownloader) GetPullRequests(page, perPage int) ([]*base.PullReq
MergedTime: mergedTime,
Head: base.PullRequestBranch{
Ref: rawMergeRequest.SourceRef,
- SHA: d.getHeadCommit(rawMergeRequest.SourceRef),
+ SHA: d.getHeadCommit(ctx, rawMergeRequest.SourceRef),
RepoName: d.repoName,
},
Base: base.PullRequestBranch{
Ref: rawMergeRequest.TargetRef,
- SHA: d.getHeadCommit(rawMergeRequest.TargetRef),
+ SHA: d.getHeadCommit(ctx, rawMergeRequest.TargetRef),
RepoName: d.repoName,
},
ForeignIndex: rawMergeRequest.ID.Value,
@@ -584,7 +584,7 @@ func (d *CodebaseDownloader) GetPullRequests(page, perPage int) ([]*base.PullReq
return pullRequests, true, nil
}
-func (d *CodebaseDownloader) tryGetUser(userID int64) *codebaseUser {
+func (d *CodebaseDownloader) tryGetUser(ctx context.Context, userID int64) *codebaseUser {
if len(d.userMap) == 0 {
var rawUsers struct {
XMLName xml.Name `xml:"users"`
@@ -602,6 +602,7 @@ func (d *CodebaseDownloader) tryGetUser(userID int64) *codebaseUser {
}
err := d.callAPI(
+ ctx,
"/users",
nil,
&rawUsers,
@@ -627,7 +628,7 @@ func (d *CodebaseDownloader) tryGetUser(userID int64) *codebaseUser {
return user
}
-func (d *CodebaseDownloader) getHeadCommit(ref string) string {
+func (d *CodebaseDownloader) getHeadCommit(ctx context.Context, ref string) string {
commitRef, ok := d.commitMap[ref]
if !ok {
var rawCommits struct {
@@ -638,6 +639,7 @@ func (d *CodebaseDownloader) getHeadCommit(ref string) string {
} `xml:"commit"`
}
err := d.callAPI(
+ ctx,
fmt.Sprintf("/%s/%s/commits/%s", d.project, d.repoName, ref),
nil,
&rawCommits,
diff --git a/services/migrations/codebase_test.go b/services/migrations/codebase_test.go
index 68721e0641..6cd52e5e59 100644
--- a/services/migrations/codebase_test.go
+++ b/services/migrations/codebase_test.go
@@ -4,7 +4,6 @@
package migrations
import (
- "context"
"net/url"
"os"
"testing"
@@ -30,9 +29,9 @@ func TestCodebaseDownloadRepo(t *testing.T) {
if cloneUser != "" {
u.User = url.UserPassword(cloneUser, clonePassword)
}
-
+ ctx := t.Context()
factory := &CodebaseDownloaderFactory{}
- downloader, err := factory.New(context.Background(), base.MigrateOptions{
+ downloader, err := factory.New(ctx, base.MigrateOptions{
CloneAddr: u.String(),
AuthUsername: apiUser,
AuthPassword: apiPassword,
@@ -40,7 +39,7 @@ func TestCodebaseDownloadRepo(t *testing.T) {
if err != nil {
t.Fatalf("Error creating Codebase downloader: %v", err)
}
- repo, err := downloader.GetRepoInfo()
+ repo, err := downloader.GetRepoInfo(ctx)
assert.NoError(t, err)
assertRepositoryEqual(t, &base.Repository{
Name: "test",
@@ -50,7 +49,7 @@ func TestCodebaseDownloadRepo(t *testing.T) {
OriginalURL: cloneAddr,
}, repo)
- milestones, err := downloader.GetMilestones()
+ milestones, err := downloader.GetMilestones(ctx)
assert.NoError(t, err)
assertMilestonesEqual(t, []*base.Milestone{
{
@@ -65,11 +64,11 @@ func TestCodebaseDownloadRepo(t *testing.T) {
},
}, milestones)
- labels, err := downloader.GetLabels()
+ labels, err := downloader.GetLabels(ctx)
assert.NoError(t, err)
assert.Len(t, labels, 4)
- issues, isEnd, err := downloader.GetIssues(1, 2)
+ issues, isEnd, err := downloader.GetIssues(ctx, 1, 2)
assert.NoError(t, err)
assert.True(t, isEnd)
assertIssuesEqual(t, []*base.Issue{
@@ -106,7 +105,7 @@ func TestCodebaseDownloadRepo(t *testing.T) {
},
}, issues)
- comments, _, err := downloader.GetComments(issues[0])
+ comments, _, err := downloader.GetComments(ctx, issues[0])
assert.NoError(t, err)
assertCommentsEqual(t, []*base.Comment{
{
@@ -119,7 +118,7 @@ func TestCodebaseDownloadRepo(t *testing.T) {
},
}, comments)
- prs, _, err := downloader.GetPullRequests(1, 1)
+ prs, _, err := downloader.GetPullRequests(ctx, 1, 1)
assert.NoError(t, err)
assertPullRequestsEqual(t, []*base.PullRequest{
{
@@ -144,7 +143,7 @@ func TestCodebaseDownloadRepo(t *testing.T) {
},
}, prs)
- rvs, err := downloader.GetReviews(prs[0])
+ rvs, err := downloader.GetReviews(ctx, prs[0])
assert.NoError(t, err)
assert.Empty(t, rvs)
}
diff --git a/services/migrations/codecommit.go b/services/migrations/codecommit.go
index fead527f5b..d08b2e6d4a 100644
--- a/services/migrations/codecommit.go
+++ b/services/migrations/codecommit.go
@@ -5,7 +5,7 @@ package migrations
import (
"context"
- "fmt"
+ "errors"
"net/url"
"strconv"
"strings"
@@ -42,13 +42,13 @@ func (c *CodeCommitDownloaderFactory) New(ctx context.Context, opts base.Migrate
hostElems := strings.Split(u.Host, ".")
if len(hostElems) != 4 {
- return nil, fmt.Errorf("cannot get the region from clone URL")
+ return nil, errors.New("cannot get the region from clone URL")
}
region := hostElems[1]
pathElems := strings.Split(u.Path, "/")
if len(pathElems) == 0 {
- return nil, fmt.Errorf("cannot get the repo name from clone URL")
+ return nil, errors.New("cannot get the repo name from clone URL")
}
repoName := pathElems[len(pathElems)-1]
@@ -62,9 +62,8 @@ func (c *CodeCommitDownloaderFactory) GitServiceType() structs.GitServiceType {
return structs.CodeCommitService
}
-func NewCodeCommitDownloader(ctx context.Context, repoName, baseURL, accessKeyID, secretAccessKey, region string) *CodeCommitDownloader {
+func NewCodeCommitDownloader(_ context.Context, repoName, baseURL, accessKeyID, secretAccessKey, region string) *CodeCommitDownloader {
downloader := CodeCommitDownloader{
- ctx: ctx,
repoName: repoName,
baseURL: baseURL,
codeCommitClient: codecommit.New(codecommit.Options{
@@ -79,21 +78,15 @@ func NewCodeCommitDownloader(ctx context.Context, repoName, baseURL, accessKeyID
// CodeCommitDownloader implements a downloader for AWS CodeCommit
type CodeCommitDownloader struct {
base.NullDownloader
- ctx context.Context
codeCommitClient *codecommit.Client
repoName string
baseURL string
allPullRequestIDs []string
}
-// SetContext set context
-func (c *CodeCommitDownloader) SetContext(ctx context.Context) {
- c.ctx = ctx
-}
-
// GetRepoInfo returns a repository information
-func (c *CodeCommitDownloader) GetRepoInfo() (*base.Repository, error) {
- output, err := c.codeCommitClient.GetRepository(c.ctx, &codecommit.GetRepositoryInput{
+func (c *CodeCommitDownloader) GetRepoInfo(ctx context.Context) (*base.Repository, error) {
+ output, err := c.codeCommitClient.GetRepository(ctx, &codecommit.GetRepositoryInput{
RepositoryName: util.ToPointer(c.repoName),
})
if err != nil {
@@ -117,14 +110,14 @@ func (c *CodeCommitDownloader) GetRepoInfo() (*base.Repository, error) {
}
// GetComments returns comments of an issue or PR
-func (c *CodeCommitDownloader) GetComments(commentable base.Commentable) ([]*base.Comment, bool, error) {
+func (c *CodeCommitDownloader) GetComments(ctx context.Context, commentable base.Commentable) ([]*base.Comment, bool, error) {
var (
nextToken *string
comments []*base.Comment
)
for {
- resp, err := c.codeCommitClient.GetCommentsForPullRequest(c.ctx, &codecommit.GetCommentsForPullRequestInput{
+ resp, err := c.codeCommitClient.GetCommentsForPullRequest(ctx, &codecommit.GetCommentsForPullRequestInput{
NextToken: nextToken,
PullRequestId: util.ToPointer(strconv.FormatInt(commentable.GetForeignIndex(), 10)),
})
@@ -155,22 +148,19 @@ func (c *CodeCommitDownloader) GetComments(commentable base.Commentable) ([]*bas
}
// GetPullRequests returns pull requests according page and perPage
-func (c *CodeCommitDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) {
- allPullRequestIDs, err := c.getAllPullRequestIDs()
+func (c *CodeCommitDownloader) GetPullRequests(ctx context.Context, page, perPage int) ([]*base.PullRequest, bool, error) {
+ allPullRequestIDs, err := c.getAllPullRequestIDs(ctx)
if err != nil {
return nil, false, err
}
startIndex := (page - 1) * perPage
- endIndex := page * perPage
- if endIndex > len(allPullRequestIDs) {
- endIndex = len(allPullRequestIDs)
- }
+ endIndex := min(page*perPage, len(allPullRequestIDs))
batch := allPullRequestIDs[startIndex:endIndex]
prs := make([]*base.PullRequest, 0, len(batch))
for _, id := range batch {
- output, err := c.codeCommitClient.GetPullRequest(c.ctx, &codecommit.GetPullRequestInput{
+ output, err := c.codeCommitClient.GetPullRequest(ctx, &codecommit.GetPullRequestInput{
PullRequestId: util.ToPointer(id),
})
if err != nil {
@@ -187,11 +177,15 @@ func (c *CodeCommitDownloader) GetPullRequests(page, perPage int) ([]*base.PullR
continue
}
target := orig.PullRequestTargets[0]
+ description := ""
+ if orig.Description != nil {
+ description = *orig.Description
+ }
pr := &base.PullRequest{
Number: number,
Title: *orig.Title,
PosterName: c.getUsernameFromARN(*orig.AuthorArn),
- Content: *orig.Description,
+ Content: description,
State: "open",
Created: *orig.CreationDate,
Updated: *orig.LastActivityDate,
@@ -213,6 +207,10 @@ func (c *CodeCommitDownloader) GetPullRequests(page, perPage int) ([]*base.PullR
pr.State = "closed"
pr.Closed = orig.LastActivityDate
}
+ if pr.Merged {
+ pr.MergeCommitSHA = *target.MergeMetadata.MergeCommitId
+ pr.MergedTime = orig.LastActivityDate
+ }
_ = CheckAndEnsureSafePR(pr, c.baseURL, c)
prs = append(prs, pr)
@@ -231,7 +229,7 @@ func (c *CodeCommitDownloader) FormatCloneURL(opts MigrateOptions, remoteAddr st
return u.String(), nil
}
-func (c *CodeCommitDownloader) getAllPullRequestIDs() ([]string, error) {
+func (c *CodeCommitDownloader) getAllPullRequestIDs(ctx context.Context) ([]string, error) {
if len(c.allPullRequestIDs) > 0 {
return c.allPullRequestIDs, nil
}
@@ -242,7 +240,7 @@ func (c *CodeCommitDownloader) getAllPullRequestIDs() ([]string, error) {
)
for {
- output, err := c.codeCommitClient.ListPullRequests(c.ctx, &codecommit.ListPullRequestsInput{
+ output, err := c.codeCommitClient.ListPullRequests(ctx, &codecommit.ListPullRequestsInput{
RepositoryName: util.ToPointer(c.repoName),
NextToken: nextToken,
})
diff --git a/services/migrations/dump.go b/services/migrations/dump.go
index 07812002af..b4ca1e41e0 100644
--- a/services/migrations/dump.go
+++ b/services/migrations/dump.go
@@ -32,7 +32,6 @@ var _ base.Uploader = &RepositoryDumper{}
// RepositoryDumper implements an Uploader to the local directory
type RepositoryDumper struct {
- ctx context.Context
baseDir string
repoOwner string
repoName string
@@ -56,7 +55,6 @@ func NewRepositoryDumper(ctx context.Context, baseDir, repoOwner, repoName strin
return nil, err
}
return &RepositoryDumper{
- ctx: ctx,
opts: opts,
baseDir: baseDir,
repoOwner: repoOwner,
@@ -105,7 +103,7 @@ func (g *RepositoryDumper) setURLToken(remoteAddr string) (string, error) {
}
// CreateRepo creates a repository
-func (g *RepositoryDumper) CreateRepo(repo *base.Repository, opts base.MigrateOptions) error {
+func (g *RepositoryDumper) CreateRepo(ctx context.Context, repo *base.Repository, opts base.MigrateOptions) error {
f, err := os.Create(filepath.Join(g.baseDir, "repo.yml"))
if err != nil {
return err
@@ -149,7 +147,7 @@ func (g *RepositoryDumper) CreateRepo(repo *base.Repository, opts base.MigrateOp
return err
}
- err = git.Clone(g.ctx, remoteAddr, repoPath, git.CloneRepoOptions{
+ err = git.Clone(ctx, remoteAddr, repoPath, git.CloneRepoOptions{
Mirror: true,
Quiet: true,
Timeout: migrateTimeout,
@@ -158,19 +156,19 @@ func (g *RepositoryDumper) CreateRepo(repo *base.Repository, opts base.MigrateOp
if err != nil {
return fmt.Errorf("Clone: %w", err)
}
- if err := git.WriteCommitGraph(g.ctx, repoPath); err != nil {
+ if err := git.WriteCommitGraph(ctx, repoPath); err != nil {
return err
}
if opts.Wiki {
wikiPath := g.wikiPath()
- wikiRemotePath := repository.WikiRemoteURL(g.ctx, remoteAddr)
+ wikiRemotePath := repository.WikiRemoteURL(ctx, remoteAddr)
if len(wikiRemotePath) > 0 {
if err := os.MkdirAll(wikiPath, os.ModePerm); err != nil {
return fmt.Errorf("Failed to remove %s: %w", wikiPath, err)
}
- if err := git.Clone(g.ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{
+ if err := git.Clone(ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{
Mirror: true,
Quiet: true,
Timeout: migrateTimeout,
@@ -181,13 +179,13 @@ func (g *RepositoryDumper) CreateRepo(repo *base.Repository, opts base.MigrateOp
if err := os.RemoveAll(wikiPath); err != nil {
return fmt.Errorf("Failed to remove %s: %w", wikiPath, err)
}
- } else if err := git.WriteCommitGraph(g.ctx, wikiPath); err != nil {
+ } else if err := git.WriteCommitGraph(ctx, wikiPath); err != nil {
return err
}
}
}
- g.gitRepo, err = git.OpenRepository(g.ctx, g.gitPath())
+ g.gitRepo, err = git.OpenRepository(ctx, g.gitPath())
return err
}
@@ -220,7 +218,7 @@ func (g *RepositoryDumper) Close() {
}
// CreateTopics creates topics
-func (g *RepositoryDumper) CreateTopics(topics ...string) error {
+func (g *RepositoryDumper) CreateTopics(_ context.Context, topics ...string) error {
f, err := os.Create(filepath.Join(g.baseDir, "topic.yml"))
if err != nil {
return err
@@ -242,7 +240,7 @@ func (g *RepositoryDumper) CreateTopics(topics ...string) error {
}
// CreateMilestones creates milestones
-func (g *RepositoryDumper) CreateMilestones(milestones ...*base.Milestone) error {
+func (g *RepositoryDumper) CreateMilestones(_ context.Context, milestones ...*base.Milestone) error {
var err error
if g.milestoneFile == nil {
g.milestoneFile, err = os.Create(filepath.Join(g.baseDir, "milestone.yml"))
@@ -264,7 +262,7 @@ func (g *RepositoryDumper) CreateMilestones(milestones ...*base.Milestone) error
}
// CreateLabels creates labels
-func (g *RepositoryDumper) CreateLabels(labels ...*base.Label) error {
+func (g *RepositoryDumper) CreateLabels(_ context.Context, labels ...*base.Label) error {
var err error
if g.labelFile == nil {
g.labelFile, err = os.Create(filepath.Join(g.baseDir, "label.yml"))
@@ -286,7 +284,7 @@ func (g *RepositoryDumper) CreateLabels(labels ...*base.Label) error {
}
// CreateReleases creates releases
-func (g *RepositoryDumper) CreateReleases(releases ...*base.Release) error {
+func (g *RepositoryDumper) CreateReleases(_ context.Context, releases ...*base.Release) error {
if g.opts.ReleaseAssets {
for _, release := range releases {
attachDir := filepath.Join("release_assets", release.TagName)
@@ -354,12 +352,12 @@ func (g *RepositoryDumper) CreateReleases(releases ...*base.Release) error {
}
// SyncTags syncs releases with tags in the database
-func (g *RepositoryDumper) SyncTags() error {
+func (g *RepositoryDumper) SyncTags(ctx context.Context) error {
return nil
}
// CreateIssues creates issues
-func (g *RepositoryDumper) CreateIssues(issues ...*base.Issue) error {
+func (g *RepositoryDumper) CreateIssues(_ context.Context, issues ...*base.Issue) error {
var err error
if g.issueFile == nil {
g.issueFile, err = os.Create(filepath.Join(g.baseDir, "issue.yml"))
@@ -412,7 +410,7 @@ func (g *RepositoryDumper) encodeItems(number int64, items []any, dir string, it
}
// CreateComments creates comments of issues
-func (g *RepositoryDumper) CreateComments(comments ...*base.Comment) error {
+func (g *RepositoryDumper) CreateComments(_ context.Context, comments ...*base.Comment) error {
commentsMap := make(map[int64][]any, len(comments))
for _, comment := range comments {
commentsMap[comment.IssueIndex] = append(commentsMap[comment.IssueIndex], comment)
@@ -421,7 +419,7 @@ func (g *RepositoryDumper) CreateComments(comments ...*base.Comment) error {
return g.createItems(g.commentDir(), g.commentFiles, commentsMap)
}
-func (g *RepositoryDumper) handlePullRequest(pr *base.PullRequest) error {
+func (g *RepositoryDumper) handlePullRequest(ctx context.Context, pr *base.PullRequest) error {
// SECURITY: this pr must have been ensured safe
if !pr.EnsuredSafe {
log.Error("PR #%d in %s/%s has not been checked for safety ... We will ignore this.", pr.Number, g.repoOwner, g.repoName)
@@ -490,7 +488,7 @@ func (g *RepositoryDumper) handlePullRequest(pr *base.PullRequest) error {
if pr.Head.CloneURL == "" || pr.Head.Ref == "" {
// Set head information if pr.Head.SHA is available
if pr.Head.SHA != "" {
- _, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.gitPath()})
+ _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.gitPath()})
if err != nil {
log.Error("PR #%d in %s/%s unable to update-ref for pr HEAD: %v", pr.Number, g.repoOwner, g.repoName, err)
}
@@ -520,7 +518,7 @@ func (g *RepositoryDumper) handlePullRequest(pr *base.PullRequest) error {
if !ok {
// Set head information if pr.Head.SHA is available
if pr.Head.SHA != "" {
- _, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.gitPath()})
+ _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.gitPath()})
if err != nil {
log.Error("PR #%d in %s/%s unable to update-ref for pr HEAD: %v", pr.Number, g.repoOwner, g.repoName, err)
}
@@ -555,7 +553,7 @@ func (g *RepositoryDumper) handlePullRequest(pr *base.PullRequest) error {
fetchArg = git.BranchPrefix + fetchArg
}
- _, _, err = git.NewCommand(g.ctx, "fetch", "--no-tags").AddDashesAndList(remote, fetchArg).RunStdString(&git.RunOpts{Dir: g.gitPath()})
+ _, _, err = git.NewCommand("fetch", "--no-tags").AddDashesAndList(remote, fetchArg).RunStdString(ctx, &git.RunOpts{Dir: g.gitPath()})
if err != nil {
log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err)
// We need to continue here so that the Head.Ref is reset and we attempt to set the gitref for the PR
@@ -579,7 +577,7 @@ func (g *RepositoryDumper) handlePullRequest(pr *base.PullRequest) error {
pr.Head.SHA = headSha
}
if pr.Head.SHA != "" {
- _, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.gitPath()})
+ _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.gitPath()})
if err != nil {
log.Error("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v", pr.Head.SHA, pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err)
}
@@ -589,7 +587,7 @@ func (g *RepositoryDumper) handlePullRequest(pr *base.PullRequest) error {
}
// CreatePullRequests creates pull requests
-func (g *RepositoryDumper) CreatePullRequests(prs ...*base.PullRequest) error {
+func (g *RepositoryDumper) CreatePullRequests(ctx context.Context, prs ...*base.PullRequest) error {
var err error
if g.pullrequestFile == nil {
if err := os.MkdirAll(g.baseDir, os.ModePerm); err != nil {
@@ -607,7 +605,7 @@ func (g *RepositoryDumper) CreatePullRequests(prs ...*base.PullRequest) error {
count := 0
for i := 0; i < len(prs); i++ {
pr := prs[i]
- if err := g.handlePullRequest(pr); err != nil {
+ if err := g.handlePullRequest(ctx, pr); err != nil {
log.Error("PR #%d in %s/%s failed - skipping", pr.Number, g.repoOwner, g.repoName, err)
continue
}
@@ -620,7 +618,7 @@ func (g *RepositoryDumper) CreatePullRequests(prs ...*base.PullRequest) error {
}
// CreateReviews create pull request reviews
-func (g *RepositoryDumper) CreateReviews(reviews ...*base.Review) error {
+func (g *RepositoryDumper) CreateReviews(_ context.Context, reviews ...*base.Review) error {
reviewsMap := make(map[int64][]any, len(reviews))
for _, review := range reviews {
reviewsMap[review.IssueIndex] = append(reviewsMap[review.IssueIndex], review)
@@ -636,7 +634,7 @@ func (g *RepositoryDumper) Rollback() error {
}
// Finish when migrating succeed, this will update something.
-func (g *RepositoryDumper) Finish() error {
+func (g *RepositoryDumper) Finish(_ context.Context) error {
return nil
}
diff --git a/services/migrations/error.go b/services/migrations/error.go
index c7d912f50b..9b470149bf 100644
--- a/services/migrations/error.go
+++ b/services/migrations/error.go
@@ -7,7 +7,7 @@ package migrations
import (
"errors"
- "github.com/google/go-github/v61/github"
+ "github.com/google/go-github/v71/github"
)
// ErrRepoNotCreated returns the error that repository not created
diff --git a/services/migrations/git.go b/services/migrations/git.go
index 22ffd5e765..1ed99499a1 100644
--- a/services/migrations/git.go
+++ b/services/migrations/git.go
@@ -28,12 +28,8 @@ func NewPlainGitDownloader(ownerName, repoName, remoteURL string) *PlainGitDownl
}
}
-// SetContext set context
-func (g *PlainGitDownloader) SetContext(ctx context.Context) {
-}
-
// GetRepoInfo returns a repository information
-func (g *PlainGitDownloader) GetRepoInfo() (*base.Repository, error) {
+func (g *PlainGitDownloader) GetRepoInfo(_ context.Context) (*base.Repository, error) {
// convert github repo to stand Repo
return &base.Repository{
Owner: g.ownerName,
@@ -43,6 +39,6 @@ func (g *PlainGitDownloader) GetRepoInfo() (*base.Repository, error) {
}
// GetTopics return empty string slice
-func (g PlainGitDownloader) GetTopics() ([]string, error) {
+func (g PlainGitDownloader) GetTopics(_ context.Context) ([]string, error) {
return []string{}, nil
}
diff --git a/services/migrations/gitea_downloader.go b/services/migrations/gitea_downloader.go
index 272bf02e11..5d48d2f003 100644
--- a/services/migrations/gitea_downloader.go
+++ b/services/migrations/gitea_downloader.go
@@ -67,7 +67,6 @@ func (f *GiteaDownloaderFactory) GitServiceType() structs.GitServiceType {
// GiteaDownloader implements a Downloader interface to get repository information's
type GiteaDownloader struct {
base.NullDownloader
- ctx context.Context
client *gitea_sdk.Client
baseURL string
repoOwner string
@@ -114,7 +113,6 @@ func NewGiteaDownloader(ctx context.Context, baseURL, repoPath, username, passwo
}
return &GiteaDownloader{
- ctx: ctx,
client: giteaClient,
baseURL: baseURL,
repoOwner: path[0],
@@ -124,11 +122,6 @@ func NewGiteaDownloader(ctx context.Context, baseURL, repoPath, username, passwo
}, nil
}
-// SetContext set context
-func (g *GiteaDownloader) SetContext(ctx context.Context) {
- g.ctx = ctx
-}
-
// String implements Stringer
func (g *GiteaDownloader) String() string {
return fmt.Sprintf("migration from gitea server %s %s/%s", g.baseURL, g.repoOwner, g.repoName)
@@ -142,7 +135,7 @@ func (g *GiteaDownloader) LogString() string {
}
// GetRepoInfo returns a repository information
-func (g *GiteaDownloader) GetRepoInfo() (*base.Repository, error) {
+func (g *GiteaDownloader) GetRepoInfo(_ context.Context) (*base.Repository, error) {
if g == nil {
return nil, errors.New("error: GiteaDownloader is nil")
}
@@ -164,19 +157,19 @@ func (g *GiteaDownloader) GetRepoInfo() (*base.Repository, error) {
}
// GetTopics return gitea topics
-func (g *GiteaDownloader) GetTopics() ([]string, error) {
+func (g *GiteaDownloader) GetTopics(_ context.Context) ([]string, error) {
topics, _, err := g.client.ListRepoTopics(g.repoOwner, g.repoName, gitea_sdk.ListRepoTopicsOptions{})
return topics, err
}
// GetMilestones returns milestones
-func (g *GiteaDownloader) GetMilestones() ([]*base.Milestone, error) {
+func (g *GiteaDownloader) GetMilestones(ctx context.Context) ([]*base.Milestone, error) {
milestones := make([]*base.Milestone, 0, g.maxPerPage)
for i := 1; ; i++ {
// make sure gitea can shutdown gracefully
select {
- case <-g.ctx.Done():
+ case <-ctx.Done():
return nil, nil
default:
}
@@ -235,13 +228,13 @@ func (g *GiteaDownloader) convertGiteaLabel(label *gitea_sdk.Label) *base.Label
}
// GetLabels returns labels
-func (g *GiteaDownloader) GetLabels() ([]*base.Label, error) {
+func (g *GiteaDownloader) GetLabels(ctx context.Context) ([]*base.Label, error) {
labels := make([]*base.Label, 0, g.maxPerPage)
for i := 1; ; i++ {
// make sure gitea can shutdown gracefully
select {
- case <-g.ctx.Done():
+ case <-ctx.Done():
return nil, nil
default:
}
@@ -305,7 +298,7 @@ func (g *GiteaDownloader) convertGiteaRelease(rel *gitea_sdk.Release) *base.Rele
}
// FIXME: for a private download?
- req, err := http.NewRequest("GET", assetDownloadURL, nil)
+ req, err := http.NewRequest(http.MethodGet, assetDownloadURL, nil)
if err != nil {
return nil, err
}
@@ -323,13 +316,13 @@ func (g *GiteaDownloader) convertGiteaRelease(rel *gitea_sdk.Release) *base.Rele
}
// GetReleases returns releases
-func (g *GiteaDownloader) GetReleases() ([]*base.Release, error) {
+func (g *GiteaDownloader) GetReleases(ctx context.Context) ([]*base.Release, error) {
releases := make([]*base.Release, 0, g.maxPerPage)
for i := 1; ; i++ {
// make sure gitea can shutdown gracefully
select {
- case <-g.ctx.Done():
+ case <-ctx.Done():
return nil, nil
default:
}
@@ -395,7 +388,7 @@ func (g *GiteaDownloader) getCommentReactions(commentID int64) ([]*base.Reaction
}
// GetIssues returns issues according start and limit
-func (g *GiteaDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, error) {
+func (g *GiteaDownloader) GetIssues(_ context.Context, page, perPage int) ([]*base.Issue, bool, error) {
if perPage > g.maxPerPage {
perPage = g.maxPerPage
}
@@ -458,13 +451,13 @@ func (g *GiteaDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, err
}
// GetComments returns comments according issueNumber
-func (g *GiteaDownloader) GetComments(commentable base.Commentable) ([]*base.Comment, bool, error) {
+func (g *GiteaDownloader) GetComments(ctx context.Context, commentable base.Commentable) ([]*base.Comment, bool, error) {
allComments := make([]*base.Comment, 0, g.maxPerPage)
for i := 1; ; i++ {
// make sure gitea can shutdown gracefully
select {
- case <-g.ctx.Done():
+ case <-ctx.Done():
return nil, false, nil
default:
}
@@ -504,7 +497,7 @@ func (g *GiteaDownloader) GetComments(commentable base.Commentable) ([]*base.Com
}
// GetPullRequests returns pull requests according page and perPage
-func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) {
+func (g *GiteaDownloader) GetPullRequests(_ context.Context, page, perPage int) ([]*base.PullRequest, bool, error) {
if perPage > g.maxPerPage {
perPage = g.maxPerPage
}
@@ -624,7 +617,7 @@ func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullReques
}
// GetReviews returns pull requests review
-func (g *GiteaDownloader) GetReviews(reviewable base.Reviewable) ([]*base.Review, error) {
+func (g *GiteaDownloader) GetReviews(ctx context.Context, reviewable base.Reviewable) ([]*base.Review, error) {
if err := g.client.CheckServerVersionConstraint(">=1.12"); err != nil {
log.Info("GiteaDownloader: instance to old, skip GetReviews")
return nil, nil
@@ -635,7 +628,7 @@ func (g *GiteaDownloader) GetReviews(reviewable base.Reviewable) ([]*base.Review
for i := 1; ; i++ {
// make sure gitea can shutdown gracefully
select {
- case <-g.ctx.Done():
+ case <-ctx.Done():
return nil, nil
default:
}
diff --git a/services/migrations/gitea_downloader_test.go b/services/migrations/gitea_downloader_test.go
index c37c70947e..bb1760e889 100644
--- a/services/migrations/gitea_downloader_test.go
+++ b/services/migrations/gitea_downloader_test.go
@@ -4,7 +4,6 @@
package migrations
import (
- "context"
"net/http"
"os"
"sort"
@@ -14,6 +13,7 @@ import (
base "code.gitea.io/gitea/modules/migration"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestGiteaDownloadRepo(t *testing.T) {
@@ -27,16 +27,12 @@ func TestGiteaDownloadRepo(t *testing.T) {
if err != nil || resp.StatusCode != http.StatusOK {
t.Skipf("Can't reach https://gitea.com, skipping %s", t.Name())
}
+ ctx := t.Context()
+ downloader, err := NewGiteaDownloader(ctx, "https://gitea.com", "gitea/test_repo", "", "", giteaToken)
+ require.NoError(t, err, "NewGiteaDownloader error occur")
+ require.NotNil(t, downloader, "NewGiteaDownloader is nil")
- downloader, err := NewGiteaDownloader(context.Background(), "https://gitea.com", "gitea/test_repo", "", "", giteaToken)
- if downloader == nil {
- t.Fatal("NewGitlabDownloader is nil")
- }
- if !assert.NoError(t, err) {
- t.Fatal("NewGitlabDownloader error occur")
- }
-
- repo, err := downloader.GetRepoInfo()
+ repo, err := downloader.GetRepoInfo(ctx)
assert.NoError(t, err)
assertRepositoryEqual(t, &base.Repository{
Name: "test_repo",
@@ -48,12 +44,12 @@ func TestGiteaDownloadRepo(t *testing.T) {
DefaultBranch: "master",
}, repo)
- topics, err := downloader.GetTopics()
+ topics, err := downloader.GetTopics(ctx)
assert.NoError(t, err)
sort.Strings(topics)
- assert.EqualValues(t, []string{"ci", "gitea", "migration", "test"}, topics)
+ assert.Equal(t, []string{"ci", "gitea", "migration", "test"}, topics)
- labels, err := downloader.GetLabels()
+ labels, err := downloader.GetLabels(ctx)
assert.NoError(t, err)
assertLabelsEqual(t, []*base.Label{
{
@@ -83,7 +79,7 @@ func TestGiteaDownloadRepo(t *testing.T) {
},
}, labels)
- milestones, err := downloader.GetMilestones()
+ milestones, err := downloader.GetMilestones(ctx)
assert.NoError(t, err)
assertMilestonesEqual(t, []*base.Milestone{
{
@@ -103,7 +99,7 @@ func TestGiteaDownloadRepo(t *testing.T) {
},
}, milestones)
- releases, err := downloader.GetReleases()
+ releases, err := downloader.GetReleases(ctx)
assert.NoError(t, err)
assertReleasesEqual(t, []*base.Release{
{
@@ -134,13 +130,13 @@ func TestGiteaDownloadRepo(t *testing.T) {
},
}, releases)
- issues, isEnd, err := downloader.GetIssues(1, 50)
+ issues, isEnd, err := downloader.GetIssues(ctx, 1, 50)
assert.NoError(t, err)
assert.True(t, isEnd)
assert.Len(t, issues, 7)
- assert.EqualValues(t, "open", issues[0].State)
+ assert.Equal(t, "open", issues[0].State)
- issues, isEnd, err = downloader.GetIssues(3, 2)
+ issues, isEnd, err = downloader.GetIssues(ctx, 3, 2)
assert.NoError(t, err)
assert.False(t, isEnd)
@@ -197,7 +193,7 @@ func TestGiteaDownloadRepo(t *testing.T) {
},
}, issues)
- comments, _, err := downloader.GetComments(&base.Issue{Number: 4, ForeignIndex: 4})
+ comments, _, err := downloader.GetComments(ctx, &base.Issue{Number: 4, ForeignIndex: 4})
assert.NoError(t, err)
assertCommentsEqual(t, []*base.Comment{
{
@@ -220,11 +216,11 @@ func TestGiteaDownloadRepo(t *testing.T) {
},
}, comments)
- prs, isEnd, err := downloader.GetPullRequests(1, 50)
+ prs, isEnd, err := downloader.GetPullRequests(ctx, 1, 50)
assert.NoError(t, err)
assert.True(t, isEnd)
assert.Len(t, prs, 6)
- prs, isEnd, err = downloader.GetPullRequests(1, 3)
+ prs, isEnd, err = downloader.GetPullRequests(ctx, 1, 3)
assert.NoError(t, err)
assert.False(t, isEnd)
assert.Len(t, prs, 3)
@@ -262,7 +258,7 @@ func TestGiteaDownloadRepo(t *testing.T) {
PatchURL: "https://gitea.com/gitea/test_repo/pulls/12.patch",
}, prs[1])
- reviews, err := downloader.GetReviews(&base.Issue{Number: 7, ForeignIndex: 7})
+ reviews, err := downloader.GetReviews(ctx, &base.Issue{Number: 7, ForeignIndex: 7})
assert.NoError(t, err)
assertReviewsEqual(t, []*base.Review{
{
diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go
index 9e06b77b66..737bff24d0 100644
--- a/services/migrations/gitea_uploader.go
+++ b/services/migrations/gitea_uploader.go
@@ -41,7 +41,6 @@ var _ base.Uploader = &GiteaLocalUploader{}
// GiteaLocalUploader implements an Uploader to gitea sites
type GiteaLocalUploader struct {
- ctx context.Context
doer *user_model.User
repoOwner string
repoName string
@@ -58,9 +57,8 @@ type GiteaLocalUploader struct {
}
// NewGiteaLocalUploader creates an gitea Uploader via gitea API v1
-func NewGiteaLocalUploader(ctx context.Context, doer *user_model.User, repoOwner, repoName string) *GiteaLocalUploader {
+func NewGiteaLocalUploader(_ context.Context, doer *user_model.User, repoOwner, repoName string) *GiteaLocalUploader {
return &GiteaLocalUploader{
- ctx: ctx,
doer: doer,
repoOwner: repoOwner,
repoName: repoName,
@@ -93,15 +91,15 @@ func (g *GiteaLocalUploader) MaxBatchInsertSize(tp string) int {
}
// CreateRepo creates a repository
-func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.MigrateOptions) error {
- owner, err := user_model.GetUserByName(g.ctx, g.repoOwner)
+func (g *GiteaLocalUploader) CreateRepo(ctx context.Context, repo *base.Repository, opts base.MigrateOptions) error {
+ owner, err := user_model.GetUserByName(ctx, g.repoOwner)
if err != nil {
return err
}
var r *repo_model.Repository
if opts.MigrateToRepoID <= 0 {
- r, err = repo_service.CreateRepositoryDirectly(g.ctx, g.doer, owner, repo_service.CreateRepoOptions{
+ r, err = repo_service.CreateRepositoryDirectly(ctx, g.doer, owner, repo_service.CreateRepoOptions{
Name: g.repoName,
Description: repo.Description,
OriginalURL: repo.OriginalURL,
@@ -109,9 +107,9 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
IsPrivate: opts.Private || setting.Repository.ForcePrivate,
IsMirror: opts.Mirror,
Status: repo_model.RepositoryBeingMigrated,
- })
+ }, false)
} else {
- r, err = repo_model.GetRepositoryByID(g.ctx, opts.MigrateToRepoID)
+ r, err = repo_model.GetRepositoryByID(ctx, opts.MigrateToRepoID)
}
if err != nil {
return err
@@ -119,7 +117,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
r.DefaultBranch = repo.DefaultBranch
r.Description = repo.Description
- r, err = repo_service.MigrateRepositoryGitData(g.ctx, owner, r, base.MigrateOptions{
+ r, err = repo_service.MigrateRepositoryGitData(ctx, owner, r, base.MigrateOptions{
RepoName: g.repoName,
Description: repo.Description,
OriginalURL: repo.OriginalURL,
@@ -139,7 +137,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
if err != nil {
return err
}
- g.gitRepo, err = gitrepo.OpenRepository(g.ctx, g.repo)
+ g.gitRepo, err = gitrepo.OpenRepository(ctx, g.repo)
if err != nil {
return err
}
@@ -150,7 +148,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
return err
}
g.repo.ObjectFormatName = objectFormat.Name()
- return repo_model.UpdateRepositoryCols(g.ctx, g.repo, "object_format_name")
+ return repo_model.UpdateRepositoryColsNoAutoTime(ctx, g.repo, "object_format_name")
}
// Close closes this uploader
@@ -161,7 +159,7 @@ func (g *GiteaLocalUploader) Close() {
}
// CreateTopics creates topics
-func (g *GiteaLocalUploader) CreateTopics(topics ...string) error {
+func (g *GiteaLocalUploader) CreateTopics(ctx context.Context, topics ...string) error {
// Ignore topics too long for the db
c := 0
for _, topic := range topics {
@@ -173,11 +171,11 @@ func (g *GiteaLocalUploader) CreateTopics(topics ...string) error {
c++
}
topics = topics[:c]
- return repo_model.SaveTopics(g.ctx, g.repo.ID, topics...)
+ return repo_model.SaveTopics(ctx, g.repo.ID, topics...)
}
// CreateMilestones creates milestones
-func (g *GiteaLocalUploader) CreateMilestones(milestones ...*base.Milestone) error {
+func (g *GiteaLocalUploader) CreateMilestones(ctx context.Context, milestones ...*base.Milestone) error {
mss := make([]*issues_model.Milestone, 0, len(milestones))
for _, milestone := range milestones {
var deadline timeutil.TimeStamp
@@ -216,7 +214,7 @@ func (g *GiteaLocalUploader) CreateMilestones(milestones ...*base.Milestone) err
mss = append(mss, &ms)
}
- err := issues_model.InsertMilestones(g.ctx, mss...)
+ err := issues_model.InsertMilestones(ctx, mss...)
if err != nil {
return err
}
@@ -228,7 +226,7 @@ func (g *GiteaLocalUploader) CreateMilestones(milestones ...*base.Milestone) err
}
// CreateLabels creates labels
-func (g *GiteaLocalUploader) CreateLabels(labels ...*base.Label) error {
+func (g *GiteaLocalUploader) CreateLabels(ctx context.Context, labels ...*base.Label) error {
lbs := make([]*issues_model.Label, 0, len(labels))
for _, l := range labels {
if color, err := label.NormalizeColor(l.Color); err != nil {
@@ -247,7 +245,7 @@ func (g *GiteaLocalUploader) CreateLabels(labels ...*base.Label) error {
})
}
- err := issues_model.NewLabels(g.ctx, lbs...)
+ err := issues_model.NewLabels(ctx, lbs...)
if err != nil {
return err
}
@@ -258,7 +256,7 @@ func (g *GiteaLocalUploader) CreateLabels(labels ...*base.Label) error {
}
// CreateReleases creates releases
-func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
+func (g *GiteaLocalUploader) CreateReleases(ctx context.Context, releases ...*base.Release) error {
rels := make([]*repo_model.Release, 0, len(releases))
for _, release := range releases {
if release.Created.IsZero() {
@@ -292,7 +290,7 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
CreatedUnix: timeutil.TimeStamp(release.Created.Unix()),
}
- if err := g.remapUser(release, &rel); err != nil {
+ if err := g.remapUser(ctx, release, &rel); err != nil {
return err
}
@@ -361,16 +359,16 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
rels = append(rels, &rel)
}
- return repo_model.InsertReleases(g.ctx, rels...)
+ return repo_model.InsertReleases(ctx, rels...)
}
// SyncTags syncs releases with tags in the database
-func (g *GiteaLocalUploader) SyncTags() error {
- return repo_module.SyncReleasesWithTags(g.ctx, g.repo, g.gitRepo)
+func (g *GiteaLocalUploader) SyncTags(ctx context.Context) error {
+ return repo_module.SyncReleasesWithTags(ctx, g.repo, g.gitRepo)
}
// CreateIssues creates issues
-func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error {
+func (g *GiteaLocalUploader) CreateIssues(ctx context.Context, issues ...*base.Issue) error {
iss := make([]*issues_model.Issue, 0, len(issues))
for _, issue := range issues {
var labels []*issues_model.Label
@@ -419,7 +417,7 @@ func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error {
UpdatedUnix: timeutil.TimeStamp(issue.Updated.Unix()),
}
- if err := g.remapUser(issue, &is); err != nil {
+ if err := g.remapUser(ctx, issue, &is); err != nil {
return err
}
@@ -432,7 +430,7 @@ func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error {
Type: reaction.Content,
CreatedUnix: timeutil.TimeStampNow(),
}
- if err := g.remapUser(reaction, &res); err != nil {
+ if err := g.remapUser(ctx, reaction, &res); err != nil {
return err
}
is.Reactions = append(is.Reactions, &res)
@@ -441,7 +439,7 @@ func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error {
}
if len(iss) > 0 {
- if err := issues_model.InsertIssues(g.ctx, iss...); err != nil {
+ if err := issues_model.InsertIssues(ctx, iss...); err != nil {
return err
}
@@ -454,7 +452,7 @@ func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error {
}
// CreateComments creates comments of issues
-func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error {
+func (g *GiteaLocalUploader) CreateComments(ctx context.Context, comments ...*base.Comment) error {
cms := make([]*issues_model.Comment, 0, len(comments))
for _, comment := range comments {
var issue *issues_model.Issue
@@ -513,7 +511,7 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error {
default:
}
- if err := g.remapUser(comment, &cm); err != nil {
+ if err := g.remapUser(ctx, comment, &cm); err != nil {
return err
}
@@ -523,7 +521,7 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error {
Type: reaction.Content,
CreatedUnix: timeutil.TimeStampNow(),
}
- if err := g.remapUser(reaction, &res); err != nil {
+ if err := g.remapUser(ctx, reaction, &res); err != nil {
return err
}
cm.Reactions = append(cm.Reactions, &res)
@@ -535,35 +533,35 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error {
if len(cms) == 0 {
return nil
}
- return issues_model.InsertIssueComments(g.ctx, cms)
+ return issues_model.InsertIssueComments(ctx, cms)
}
// CreatePullRequests creates pull requests
-func (g *GiteaLocalUploader) CreatePullRequests(prs ...*base.PullRequest) error {
+func (g *GiteaLocalUploader) CreatePullRequests(ctx context.Context, prs ...*base.PullRequest) error {
gprs := make([]*issues_model.PullRequest, 0, len(prs))
for _, pr := range prs {
- gpr, err := g.newPullRequest(pr)
+ gpr, err := g.newPullRequest(ctx, pr)
if err != nil {
return err
}
- if err := g.remapUser(pr, gpr.Issue); err != nil {
+ if err := g.remapUser(ctx, pr, gpr.Issue); err != nil {
return err
}
gprs = append(gprs, gpr)
}
- if err := issues_model.InsertPullRequests(g.ctx, gprs...); err != nil {
+ if err := issues_model.InsertPullRequests(ctx, gprs...); err != nil {
return err
}
for _, pr := range gprs {
g.issues[pr.Issue.Index] = pr.Issue
- pull.AddToTaskQueue(g.ctx, pr)
+ pull.StartPullRequestCheckImmediately(ctx, pr)
}
return nil
}
-func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head string, err error) {
+func (g *GiteaLocalUploader) updateGitForPullRequest(ctx context.Context, pr *base.PullRequest) (head string, err error) {
// SECURITY: this pr must have been must have been ensured safe
if !pr.EnsuredSafe {
log.Error("PR #%d in %s/%s has not been checked for safety.", pr.Number, g.repoOwner, g.repoName)
@@ -664,7 +662,7 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head
fetchArg = git.BranchPrefix + fetchArg
}
- _, _, err = git.NewCommand(g.ctx, "fetch", "--no-tags").AddDashesAndList(remote, fetchArg).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()})
+ _, _, err = git.NewCommand("fetch", "--no-tags").AddDashesAndList(remote, fetchArg).RunStdString(ctx, &git.RunOpts{Dir: g.repo.RepoPath()})
if err != nil {
log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err)
return head, nil
@@ -683,7 +681,7 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head
pr.Head.SHA = headSha
}
- _, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()})
+ _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.repo.RepoPath()})
if err != nil {
return "", err
}
@@ -700,13 +698,13 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head
// The SHA is empty
log.Warn("Empty reference, no pull head for PR #%d in %s/%s", pr.Number, g.repoOwner, g.repoName)
} else {
- _, _, err = git.NewCommand(g.ctx, "rev-list", "--quiet", "-1").AddDynamicArguments(pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()})
+ _, _, err = git.NewCommand("rev-list", "--quiet", "-1").AddDynamicArguments(pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.repo.RepoPath()})
if err != nil {
// Git update-ref remove bad references with a relative path
log.Warn("Deprecated local head %s for PR #%d in %s/%s, removing %s", pr.Head.SHA, pr.Number, g.repoOwner, g.repoName, pr.GetGitRefName())
} else {
// set head information
- _, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()})
+ _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.repo.RepoPath()})
if err != nil {
log.Error("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v", pr.Head.SHA, pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err)
}
@@ -716,7 +714,7 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head
return head, nil
}
-func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*issues_model.PullRequest, error) {
+func (g *GiteaLocalUploader) newPullRequest(ctx context.Context, pr *base.PullRequest) (*issues_model.PullRequest, error) {
var labels []*issues_model.Label
for _, label := range pr.Labels {
lb, ok := g.labels[label.Name]
@@ -727,7 +725,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*issues_model
milestoneID := g.milestones[pr.Milestone]
- head, err := g.updateGitForPullRequest(pr)
+ head, err := g.updateGitForPullRequest(ctx, pr)
if err != nil {
return nil, fmt.Errorf("updateGitForPullRequest: %w", err)
}
@@ -767,7 +765,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*issues_model
issue := issues_model.Issue{
RepoID: g.repo.ID,
Repo: g.repo,
- Title: prTitle,
+ Title: util.TruncateRunes(prTitle, 255),
Index: pr.Number,
Content: pr.Content,
MilestoneID: milestoneID,
@@ -779,7 +777,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*issues_model
UpdatedUnix: timeutil.TimeStamp(pr.Updated.Unix()),
}
- if err := g.remapUser(pr, &issue); err != nil {
+ if err := g.remapUser(ctx, pr, &issue); err != nil {
return nil, err
}
@@ -789,7 +787,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*issues_model
Type: reaction.Content,
CreatedUnix: timeutil.TimeStampNow(),
}
- if err := g.remapUser(reaction, &res); err != nil {
+ if err := g.remapUser(ctx, reaction, &res); err != nil {
return nil, err
}
issue.Reactions = append(issue.Reactions, &res)
@@ -839,7 +837,7 @@ func convertReviewState(state string) issues_model.ReviewType {
}
// CreateReviews create pull request reviews of currently migrated issues
-func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
+func (g *GiteaLocalUploader) CreateReviews(ctx context.Context, reviews ...*base.Review) error {
cms := make([]*issues_model.Review, 0, len(reviews))
for _, review := range reviews {
var issue *issues_model.Issue
@@ -860,7 +858,7 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
UpdatedUnix: timeutil.TimeStamp(review.CreatedAt.Unix()),
}
- if err := g.remapUser(review, &cm); err != nil {
+ if err := g.remapUser(ctx, review, &cm); err != nil {
return err
}
@@ -870,7 +868,7 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
pr, ok := g.prCache[issue.ID]
if !ok {
var err error
- pr, err = issues_model.GetPullRequestByIssueIDWithNoAttributes(g.ctx, issue.ID)
+ pr, err = issues_model.GetPullRequestByIssueIDWithNoAttributes(ctx, issue.ID)
if err != nil {
return err
}
@@ -940,7 +938,7 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
UpdatedUnix: timeutil.TimeStamp(comment.UpdatedAt.Unix()),
}
- if err := g.remapUser(review, &c); err != nil {
+ if err := g.remapUser(ctx, review, &c); err != nil {
return err
}
@@ -948,7 +946,7 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
}
}
- return issues_model.InsertReviews(g.ctx, cms)
+ return issues_model.InsertReviews(ctx, cms)
}
// Rollback when migrating failed, this will rollback all the changes.
@@ -962,31 +960,31 @@ func (g *GiteaLocalUploader) Rollback() error {
}
// Finish when migrating success, this will do some status update things.
-func (g *GiteaLocalUploader) Finish() error {
+func (g *GiteaLocalUploader) Finish(ctx context.Context) error {
if g.repo == nil || g.repo.ID <= 0 {
return ErrRepoNotCreated
}
// update issue_index
- if err := issues_model.RecalculateIssueIndexForRepo(g.ctx, g.repo.ID); err != nil {
+ if err := issues_model.RecalculateIssueIndexForRepo(ctx, g.repo.ID); err != nil {
return err
}
- if err := models.UpdateRepoStats(g.ctx, g.repo.ID); err != nil {
+ if err := models.UpdateRepoStats(ctx, g.repo.ID); err != nil {
return err
}
g.repo.Status = repo_model.RepositoryReady
- return repo_model.UpdateRepositoryCols(g.ctx, g.repo, "status")
+ return repo_model.UpdateRepositoryColsWithAutoTime(ctx, g.repo, "status")
}
-func (g *GiteaLocalUploader) remapUser(source user_model.ExternalUserMigrated, target user_model.ExternalUserRemappable) error {
+func (g *GiteaLocalUploader) remapUser(ctx context.Context, source user_model.ExternalUserMigrated, target user_model.ExternalUserRemappable) error {
var userID int64
var err error
if g.sameApp {
- userID, err = g.remapLocalUser(source)
+ userID, err = g.remapLocalUser(ctx, source)
} else {
- userID, err = g.remapExternalUser(source)
+ userID, err = g.remapExternalUser(ctx, source)
}
if err != nil {
return err
@@ -998,10 +996,10 @@ func (g *GiteaLocalUploader) remapUser(source user_model.ExternalUserMigrated, t
return target.RemapExternalUser(source.GetExternalName(), source.GetExternalID(), g.doer.ID)
}
-func (g *GiteaLocalUploader) remapLocalUser(source user_model.ExternalUserMigrated) (int64, error) {
+func (g *GiteaLocalUploader) remapLocalUser(ctx context.Context, source user_model.ExternalUserMigrated) (int64, error) {
userid, ok := g.userMap[source.GetExternalID()]
if !ok {
- name, err := user_model.GetUserNameByID(g.ctx, source.GetExternalID())
+ name, err := user_model.GetUserNameByID(ctx, source.GetExternalID())
if err != nil {
return 0, err
}
@@ -1016,10 +1014,10 @@ func (g *GiteaLocalUploader) remapLocalUser(source user_model.ExternalUserMigrat
return userid, nil
}
-func (g *GiteaLocalUploader) remapExternalUser(source user_model.ExternalUserMigrated) (userid int64, err error) {
+func (g *GiteaLocalUploader) remapExternalUser(ctx context.Context, source user_model.ExternalUserMigrated) (userid int64, err error) {
userid, ok := g.userMap[source.GetExternalID()]
if !ok {
- userid, err = user_model.GetUserIDByExternalUserID(g.ctx, g.gitServiceType.Name(), fmt.Sprintf("%d", source.GetExternalID()))
+ userid, err = user_model.GetUserIDByExternalUserID(ctx, g.gitServiceType.Name(), strconv.FormatInt(source.GetExternalID(), 10))
if err != nil {
log.Error("GetUserIDByExternalUserID: %v", err)
return 0, err
diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go
index f2379dadf8..1970c0550c 100644
--- a/services/migrations/gitea_uploader_test.go
+++ b/services/migrations/gitea_uploader_test.go
@@ -5,7 +5,6 @@
package migrations
import (
- "context"
"fmt"
"os"
"path/filepath"
@@ -40,7 +39,7 @@ func TestGiteaUploadRepo(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
var (
- ctx = context.Background()
+ ctx = t.Context()
downloader = NewGithubDownloaderV3(ctx, "https://github.com", "", "", "", "go-xorm", "builder")
repoName = "builder-" + time.Now().Format("2006-01-02-15-04-05")
uploader = NewGiteaLocalUploader(graceful.GetManager().HammerContext(), user, user.Name, repoName)
@@ -65,7 +64,7 @@ func TestGiteaUploadRepo(t *testing.T) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, Name: repoName})
assert.True(t, repo.HasWiki())
- assert.EqualValues(t, repo_model.RepositoryReady, repo.Status)
+ assert.Equal(t, repo_model.RepositoryReady, repo.Status)
milestones, err := db.Find[issues_model.Milestone](db.DefaultContext, issues_model.FindMilestoneOptions{
RepoID: repo.ID,
@@ -132,8 +131,9 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) {
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ ctx := t.Context()
repoName := "migrated"
- uploader := NewGiteaLocalUploader(context.Background(), doer, doer.Name, repoName)
+ uploader := NewGiteaLocalUploader(ctx, doer, doer.Name, repoName)
// call remapLocalUser
uploader.sameApp = true
@@ -150,9 +150,9 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) {
//
target := repo_model.Release{}
uploader.userMap = make(map[int64]int64)
- err := uploader.remapUser(&source, &target)
+ err := uploader.remapUser(ctx, &source, &target)
assert.NoError(t, err)
- assert.EqualValues(t, doer.ID, target.GetUserID())
+ assert.Equal(t, doer.ID, target.GetUserID())
//
// The externalID matches a known user but the name does not match,
@@ -161,9 +161,9 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) {
source.PublisherID = user.ID
target = repo_model.Release{}
uploader.userMap = make(map[int64]int64)
- err = uploader.remapUser(&source, &target)
+ err = uploader.remapUser(ctx, &source, &target)
assert.NoError(t, err)
- assert.EqualValues(t, doer.ID, target.GetUserID())
+ assert.Equal(t, doer.ID, target.GetUserID())
//
// The externalID and externalName match an existing user, everything
@@ -172,17 +172,17 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) {
source.PublisherName = user.Name
target = repo_model.Release{}
uploader.userMap = make(map[int64]int64)
- err = uploader.remapUser(&source, &target)
+ err = uploader.remapUser(ctx, &source, &target)
assert.NoError(t, err)
- assert.EqualValues(t, user.ID, target.GetUserID())
+ assert.Equal(t, user.ID, target.GetUserID())
}
func TestGiteaUploadRemapExternalUser(t *testing.T) {
unittest.PrepareTestEnv(t)
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-
+ ctx := t.Context()
repoName := "migrated"
- uploader := NewGiteaLocalUploader(context.Background(), doer, doer.Name, repoName)
+ uploader := NewGiteaLocalUploader(ctx, doer, doer.Name, repoName)
uploader.gitServiceType = structs.GiteaService
// call remapExternalUser
uploader.sameApp = false
@@ -200,9 +200,9 @@ func TestGiteaUploadRemapExternalUser(t *testing.T) {
//
uploader.userMap = make(map[int64]int64)
target := repo_model.Release{}
- err := uploader.remapUser(&source, &target)
+ err := uploader.remapUser(ctx, &source, &target)
assert.NoError(t, err)
- assert.EqualValues(t, doer.ID, target.GetUserID())
+ assert.Equal(t, doer.ID, target.GetUserID())
//
// Link the external ID to an existing user
@@ -223,9 +223,9 @@ func TestGiteaUploadRemapExternalUser(t *testing.T) {
//
uploader.userMap = make(map[int64]int64)
target = repo_model.Release{}
- err = uploader.remapUser(&source, &target)
+ err = uploader.remapUser(ctx, &source, &target)
assert.NoError(t, err)
- assert.EqualValues(t, linkedUser.ID, target.GetUserID())
+ assert.Equal(t, linkedUser.ID, target.GetUserID())
}
func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
@@ -237,9 +237,9 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
fromRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
baseRef := "master"
assert.NoError(t, git.InitRepository(git.DefaultContext, fromRepo.RepoPath(), false, fromRepo.ObjectFormatName))
- err := git.NewCommand(git.DefaultContext, "symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseRef).Run(&git.RunOpts{Dir: fromRepo.RepoPath()})
+ err := git.NewCommand("symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseRef).Run(git.DefaultContext, &git.RunOpts{Dir: fromRepo.RepoPath()})
assert.NoError(t, err)
- assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s", fromRepo.RepoPath())), 0o644))
+ assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte("# Testing Repository\n\nOriginally created in: "+fromRepo.RepoPath()), 0o644))
assert.NoError(t, git.AddChanges(fromRepo.RepoPath(), true))
signature := git.Signature{
Email: "test@example.com",
@@ -261,7 +261,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
// fromRepo branch1
//
headRef := "branch1"
- _, _, err = git.NewCommand(git.DefaultContext, "checkout", "-b").AddDynamicArguments(headRef).RunStdString(&git.RunOpts{Dir: fromRepo.RepoPath()})
+ _, _, err = git.NewCommand("checkout", "-b").AddDynamicArguments(headRef).RunStdString(git.DefaultContext, &git.RunOpts{Dir: fromRepo.RepoPath()})
assert.NoError(t, err)
assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte("SOMETHING"), 0o644))
assert.NoError(t, git.AddChanges(fromRepo.RepoPath(), true))
@@ -285,9 +285,9 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
assert.NoError(t, git.CloneWithArgs(git.DefaultContext, nil, fromRepo.RepoPath(), forkRepo.RepoPath(), git.CloneRepoOptions{
Branch: headRef,
}))
- _, _, err = git.NewCommand(git.DefaultContext, "checkout", "-b").AddDynamicArguments(forkHeadRef).RunStdString(&git.RunOpts{Dir: forkRepo.RepoPath()})
+ _, _, err = git.NewCommand("checkout", "-b").AddDynamicArguments(forkHeadRef).RunStdString(git.DefaultContext, &git.RunOpts{Dir: forkRepo.RepoPath()})
assert.NoError(t, err)
- assert.NoError(t, os.WriteFile(filepath.Join(forkRepo.RepoPath(), "README.md"), []byte(fmt.Sprintf("# branch2 %s", forkRepo.RepoPath())), 0o644))
+ assert.NoError(t, os.WriteFile(filepath.Join(forkRepo.RepoPath(), "README.md"), []byte("# branch2 "+forkRepo.RepoPath()), 0o644))
assert.NoError(t, git.AddChanges(forkRepo.RepoPath(), true))
assert.NoError(t, git.CommitChanges(forkRepo.RepoPath(), git.CommitChangesOptions{
Committer: &signature,
@@ -301,11 +301,12 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
assert.NoError(t, err)
toRepoName := "migrated"
- uploader := NewGiteaLocalUploader(context.Background(), fromRepoOwner, fromRepoOwner.Name, toRepoName)
+ ctx := t.Context()
+ uploader := NewGiteaLocalUploader(ctx, fromRepoOwner, fromRepoOwner.Name, toRepoName)
uploader.gitServiceType = structs.GiteaService
- assert.NoError(t, repo_service.Init(context.Background()))
- assert.NoError(t, uploader.CreateRepo(&base.Repository{
+ assert.NoError(t, repo_service.Init(t.Context()))
+ assert.NoError(t, uploader.CreateRepo(ctx, &base.Repository{
Description: "description",
OriginalURL: fromRepo.RepoPath(),
CloneURL: fromRepo.RepoPath(),
@@ -505,16 +506,16 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
testCase.pr.EnsuredSafe = true
- head, err := uploader.updateGitForPullRequest(&testCase.pr)
+ head, err := uploader.updateGitForPullRequest(ctx, &testCase.pr)
assert.NoError(t, err)
- assert.EqualValues(t, testCase.head, head)
+ assert.Equal(t, testCase.head, head)
log.Info(stopMark)
logFiltered, logStopped := logChecker.Check(5 * time.Second)
assert.True(t, logStopped)
if len(testCase.logFilter) > 0 {
- assert.EqualValues(t, testCase.logFiltered, logFiltered, "for log message filters: %v", testCase.logFilter)
+ assert.Equal(t, testCase.logFiltered, logFiltered, "for log message filters: %v", testCase.logFilter)
}
})
}
diff --git a/services/migrations/github.go b/services/migrations/github.go
index 604ab84b39..2ce11615c6 100644
--- a/services/migrations/github.go
+++ b/services/migrations/github.go
@@ -20,7 +20,7 @@ import (
"code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/structs"
- "github.com/google/go-github/v61/github"
+ "github.com/google/go-github/v71/github"
"golang.org/x/oauth2"
)
@@ -64,7 +64,6 @@ func (f *GithubDownloaderV3Factory) GitServiceType() structs.GitServiceType {
// from github via APIv3
type GithubDownloaderV3 struct {
base.NullDownloader
- ctx context.Context
clients []*github.Client
baseURL string
repoOwner string
@@ -79,20 +78,19 @@ type GithubDownloaderV3 struct {
}
// NewGithubDownloaderV3 creates a github Downloader via github v3 API
-func NewGithubDownloaderV3(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GithubDownloaderV3 {
+func NewGithubDownloaderV3(_ context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GithubDownloaderV3 {
downloader := GithubDownloaderV3{
userName: userName,
baseURL: baseURL,
password: password,
- ctx: ctx,
repoOwner: repoOwner,
repoName: repoName,
maxPerPage: 100,
}
if token != "" {
- tokens := strings.Split(token, ",")
- for _, token := range tokens {
+ tokens := strings.SplitSeq(token, ",")
+ for token := range tokens {
token = strings.TrimSpace(token)
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
@@ -135,18 +133,13 @@ func (g *GithubDownloaderV3) LogString() string {
func (g *GithubDownloaderV3) addClient(client *http.Client, baseURL string) {
githubClient := github.NewClient(client)
if baseURL != "https://github.com" {
- githubClient, _ = github.NewClient(client).WithEnterpriseURLs(baseURL, baseURL)
+ githubClient, _ = githubClient.WithEnterpriseURLs(baseURL, baseURL)
}
g.clients = append(g.clients, githubClient)
g.rates = append(g.rates, nil)
}
-// SetContext set context
-func (g *GithubDownloaderV3) SetContext(ctx context.Context) {
- g.ctx = ctx
-}
-
-func (g *GithubDownloaderV3) waitAndPickClient() {
+func (g *GithubDownloaderV3) waitAndPickClient(ctx context.Context) {
var recentIdx int
var maxRemaining int
for i := 0; i < len(g.clients); i++ {
@@ -160,13 +153,13 @@ func (g *GithubDownloaderV3) waitAndPickClient() {
for g.rates[g.curClientIdx] != nil && g.rates[g.curClientIdx].Remaining <= GithubLimitRateRemaining {
timer := time.NewTimer(time.Until(g.rates[g.curClientIdx].Reset.Time))
select {
- case <-g.ctx.Done():
+ case <-ctx.Done():
timer.Stop()
return
case <-timer.C:
}
- err := g.RefreshRate()
+ err := g.RefreshRate(ctx)
if err != nil {
log.Error("g.getClient().RateLimit.Get: %s", err)
}
@@ -174,8 +167,8 @@ func (g *GithubDownloaderV3) waitAndPickClient() {
}
// RefreshRate update the current rate (doesn't count in rate limit)
-func (g *GithubDownloaderV3) RefreshRate() error {
- rates, _, err := g.getClient().RateLimit.Get(g.ctx)
+func (g *GithubDownloaderV3) RefreshRate(ctx context.Context) error {
+ rates, _, err := g.getClient().RateLimit.Get(ctx)
if err != nil {
// if rate limit is not enabled, ignore it
if strings.Contains(err.Error(), "404") {
@@ -198,9 +191,9 @@ func (g *GithubDownloaderV3) setRate(rate *github.Rate) {
}
// GetRepoInfo returns a repository information
-func (g *GithubDownloaderV3) GetRepoInfo() (*base.Repository, error) {
- g.waitAndPickClient()
- gr, resp, err := g.getClient().Repositories.Get(g.ctx, g.repoOwner, g.repoName)
+func (g *GithubDownloaderV3) GetRepoInfo(ctx context.Context) (*base.Repository, error) {
+ g.waitAndPickClient(ctx)
+ gr, resp, err := g.getClient().Repositories.Get(ctx, g.repoOwner, g.repoName)
if err != nil {
return nil, err
}
@@ -219,9 +212,9 @@ func (g *GithubDownloaderV3) GetRepoInfo() (*base.Repository, error) {
}
// GetTopics return github topics
-func (g *GithubDownloaderV3) GetTopics() ([]string, error) {
- g.waitAndPickClient()
- r, resp, err := g.getClient().Repositories.Get(g.ctx, g.repoOwner, g.repoName)
+func (g *GithubDownloaderV3) GetTopics(ctx context.Context) ([]string, error) {
+ g.waitAndPickClient(ctx)
+ r, resp, err := g.getClient().Repositories.Get(ctx, g.repoOwner, g.repoName)
if err != nil {
return nil, err
}
@@ -230,12 +223,12 @@ func (g *GithubDownloaderV3) GetTopics() ([]string, error) {
}
// GetMilestones returns milestones
-func (g *GithubDownloaderV3) GetMilestones() ([]*base.Milestone, error) {
+func (g *GithubDownloaderV3) GetMilestones(ctx context.Context) ([]*base.Milestone, error) {
perPage := g.maxPerPage
milestones := make([]*base.Milestone, 0, perPage)
for i := 1; ; i++ {
- g.waitAndPickClient()
- ms, resp, err := g.getClient().Issues.ListMilestones(g.ctx, g.repoOwner, g.repoName,
+ g.waitAndPickClient(ctx)
+ ms, resp, err := g.getClient().Issues.ListMilestones(ctx, g.repoOwner, g.repoName,
&github.MilestoneListOptions{
State: "all",
ListOptions: github.ListOptions{
@@ -279,12 +272,12 @@ func convertGithubLabel(label *github.Label) *base.Label {
}
// GetLabels returns labels
-func (g *GithubDownloaderV3) GetLabels() ([]*base.Label, error) {
+func (g *GithubDownloaderV3) GetLabels(ctx context.Context) ([]*base.Label, error) {
perPage := g.maxPerPage
labels := make([]*base.Label, 0, perPage)
for i := 1; ; i++ {
- g.waitAndPickClient()
- ls, resp, err := g.getClient().Issues.ListLabels(g.ctx, g.repoOwner, g.repoName,
+ g.waitAndPickClient(ctx)
+ ls, resp, err := g.getClient().Issues.ListLabels(ctx, g.repoOwner, g.repoName,
&github.ListOptions{
Page: i,
PerPage: perPage,
@@ -304,7 +297,7 @@ func (g *GithubDownloaderV3) GetLabels() ([]*base.Label, error) {
return labels, nil
}
-func (g *GithubDownloaderV3) convertGithubRelease(rel *github.RepositoryRelease) *base.Release {
+func (g *GithubDownloaderV3) convertGithubRelease(ctx context.Context, rel *github.RepositoryRelease) *base.Release {
// GitHub allows commitish to be a reference.
// In this case, we need to remove the prefix, i.e. convert "refs/heads/main" to "main".
targetCommitish := strings.TrimPrefix(rel.GetTargetCommitish(), git.BranchPrefix)
@@ -339,12 +332,12 @@ func (g *GithubDownloaderV3) convertGithubRelease(rel *github.RepositoryRelease)
Created: asset.CreatedAt.Time,
Updated: asset.UpdatedAt.Time,
DownloadFunc: func() (io.ReadCloser, error) {
- g.waitAndPickClient()
- readCloser, redirectURL, err := g.getClient().Repositories.DownloadReleaseAsset(g.ctx, g.repoOwner, g.repoName, assetID, nil)
+ g.waitAndPickClient(ctx)
+ readCloser, redirectURL, err := g.getClient().Repositories.DownloadReleaseAsset(ctx, g.repoOwner, g.repoName, assetID, nil)
if err != nil {
return nil, err
}
- if err := g.RefreshRate(); err != nil {
+ if err := g.RefreshRate(ctx); err != nil {
log.Error("g.getClient().RateLimits: %s", err)
}
@@ -364,13 +357,13 @@ func (g *GithubDownloaderV3) convertGithubRelease(rel *github.RepositoryRelease)
return io.NopCloser(strings.NewReader(redirectURL)), nil
}
- g.waitAndPickClient()
- req, err := http.NewRequestWithContext(g.ctx, "GET", redirectURL, nil)
+ g.waitAndPickClient(ctx)
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, redirectURL, nil)
if err != nil {
return nil, err
}
resp, err := httpClient.Do(req)
- err1 := g.RefreshRate()
+ err1 := g.RefreshRate(ctx)
if err1 != nil {
log.Error("g.RefreshRate(): %s", err1)
}
@@ -385,12 +378,12 @@ func (g *GithubDownloaderV3) convertGithubRelease(rel *github.RepositoryRelease)
}
// GetReleases returns releases
-func (g *GithubDownloaderV3) GetReleases() ([]*base.Release, error) {
+func (g *GithubDownloaderV3) GetReleases(ctx context.Context) ([]*base.Release, error) {
perPage := g.maxPerPage
releases := make([]*base.Release, 0, perPage)
for i := 1; ; i++ {
- g.waitAndPickClient()
- ls, resp, err := g.getClient().Repositories.ListReleases(g.ctx, g.repoOwner, g.repoName,
+ g.waitAndPickClient(ctx)
+ ls, resp, err := g.getClient().Repositories.ListReleases(ctx, g.repoOwner, g.repoName,
&github.ListOptions{
Page: i,
PerPage: perPage,
@@ -401,7 +394,7 @@ func (g *GithubDownloaderV3) GetReleases() ([]*base.Release, error) {
g.setRate(&resp.Rate)
for _, release := range ls {
- releases = append(releases, g.convertGithubRelease(release))
+ releases = append(releases, g.convertGithubRelease(ctx, release))
}
if len(ls) < perPage {
break
@@ -411,7 +404,7 @@ func (g *GithubDownloaderV3) GetReleases() ([]*base.Release, error) {
}
// GetIssues returns issues according start and limit
-func (g *GithubDownloaderV3) GetIssues(page, perPage int) ([]*base.Issue, bool, error) {
+func (g *GithubDownloaderV3) GetIssues(ctx context.Context, page, perPage int) ([]*base.Issue, bool, error) {
if perPage > g.maxPerPage {
perPage = g.maxPerPage
}
@@ -426,8 +419,8 @@ func (g *GithubDownloaderV3) GetIssues(page, perPage int) ([]*base.Issue, bool,
}
allIssues := make([]*base.Issue, 0, perPage)
- g.waitAndPickClient()
- issues, resp, err := g.getClient().Issues.ListByRepo(g.ctx, g.repoOwner, g.repoName, opt)
+ g.waitAndPickClient(ctx)
+ issues, resp, err := g.getClient().Issues.ListByRepo(ctx, g.repoOwner, g.repoName, opt)
if err != nil {
return nil, false, fmt.Errorf("error while listing repos: %w", err)
}
@@ -447,10 +440,12 @@ func (g *GithubDownloaderV3) GetIssues(page, perPage int) ([]*base.Issue, bool,
var reactions []*base.Reaction
if !g.SkipReactions {
for i := 1; ; i++ {
- g.waitAndPickClient()
- res, resp, err := g.getClient().Reactions.ListIssueReactions(g.ctx, g.repoOwner, g.repoName, issue.GetNumber(), &github.ListOptions{
- Page: i,
- PerPage: perPage,
+ g.waitAndPickClient(ctx)
+ res, resp, err := g.getClient().Reactions.ListIssueReactions(ctx, g.repoOwner, g.repoName, issue.GetNumber(), &github.ListReactionOptions{
+ ListOptions: github.ListOptions{
+ Page: i,
+ PerPage: perPage,
+ },
})
if err != nil {
return nil, false, err
@@ -503,12 +498,12 @@ func (g *GithubDownloaderV3) SupportGetRepoComments() bool {
}
// GetComments returns comments according issueNumber
-func (g *GithubDownloaderV3) GetComments(commentable base.Commentable) ([]*base.Comment, bool, error) {
- comments, err := g.getComments(commentable)
+func (g *GithubDownloaderV3) GetComments(ctx context.Context, commentable base.Commentable) ([]*base.Comment, bool, error) {
+ comments, err := g.getComments(ctx, commentable)
return comments, false, err
}
-func (g *GithubDownloaderV3) getComments(commentable base.Commentable) ([]*base.Comment, error) {
+func (g *GithubDownloaderV3) getComments(ctx context.Context, commentable base.Commentable) ([]*base.Comment, error) {
var (
allComments = make([]*base.Comment, 0, g.maxPerPage)
created = "created"
@@ -522,8 +517,8 @@ func (g *GithubDownloaderV3) getComments(commentable base.Commentable) ([]*base.
},
}
for {
- g.waitAndPickClient()
- comments, resp, err := g.getClient().Issues.ListComments(g.ctx, g.repoOwner, g.repoName, int(commentable.GetForeignIndex()), opt)
+ g.waitAndPickClient(ctx)
+ comments, resp, err := g.getClient().Issues.ListComments(ctx, g.repoOwner, g.repoName, int(commentable.GetForeignIndex()), opt)
if err != nil {
return nil, fmt.Errorf("error while listing repos: %w", err)
}
@@ -533,10 +528,12 @@ func (g *GithubDownloaderV3) getComments(commentable base.Commentable) ([]*base.
var reactions []*base.Reaction
if !g.SkipReactions {
for i := 1; ; i++ {
- g.waitAndPickClient()
- res, resp, err := g.getClient().Reactions.ListIssueCommentReactions(g.ctx, g.repoOwner, g.repoName, comment.GetID(), &github.ListOptions{
- Page: i,
- PerPage: g.maxPerPage,
+ g.waitAndPickClient(ctx)
+ res, resp, err := g.getClient().Reactions.ListIssueCommentReactions(ctx, g.repoOwner, g.repoName, comment.GetID(), &github.ListReactionOptions{
+ ListOptions: github.ListOptions{
+ Page: i,
+ PerPage: g.maxPerPage,
+ },
})
if err != nil {
return nil, err
@@ -576,7 +573,7 @@ func (g *GithubDownloaderV3) getComments(commentable base.Commentable) ([]*base.
}
// GetAllComments returns repository comments according page and perPageSize
-func (g *GithubDownloaderV3) GetAllComments(page, perPage int) ([]*base.Comment, bool, error) {
+func (g *GithubDownloaderV3) GetAllComments(ctx context.Context, page, perPage int) ([]*base.Comment, bool, error) {
var (
allComments = make([]*base.Comment, 0, perPage)
created = "created"
@@ -594,8 +591,8 @@ func (g *GithubDownloaderV3) GetAllComments(page, perPage int) ([]*base.Comment,
},
}
- g.waitAndPickClient()
- comments, resp, err := g.getClient().Issues.ListComments(g.ctx, g.repoOwner, g.repoName, 0, opt)
+ g.waitAndPickClient(ctx)
+ comments, resp, err := g.getClient().Issues.ListComments(ctx, g.repoOwner, g.repoName, 0, opt)
if err != nil {
return nil, false, fmt.Errorf("error while listing repos: %w", err)
}
@@ -608,10 +605,12 @@ func (g *GithubDownloaderV3) GetAllComments(page, perPage int) ([]*base.Comment,
var reactions []*base.Reaction
if !g.SkipReactions {
for i := 1; ; i++ {
- g.waitAndPickClient()
- res, resp, err := g.getClient().Reactions.ListIssueCommentReactions(g.ctx, g.repoOwner, g.repoName, comment.GetID(), &github.ListOptions{
- Page: i,
- PerPage: g.maxPerPage,
+ g.waitAndPickClient(ctx)
+ res, resp, err := g.getClient().Reactions.ListIssueCommentReactions(ctx, g.repoOwner, g.repoName, comment.GetID(), &github.ListReactionOptions{
+ ListOptions: github.ListOptions{
+ Page: i,
+ PerPage: g.maxPerPage,
+ },
})
if err != nil {
return nil, false, err
@@ -648,7 +647,7 @@ func (g *GithubDownloaderV3) GetAllComments(page, perPage int) ([]*base.Comment,
}
// GetPullRequests returns pull requests according page and perPage
-func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) {
+func (g *GithubDownloaderV3) GetPullRequests(ctx context.Context, page, perPage int) ([]*base.PullRequest, bool, error) {
if perPage > g.maxPerPage {
perPage = g.maxPerPage
}
@@ -662,8 +661,8 @@ func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullReq
},
}
allPRs := make([]*base.PullRequest, 0, perPage)
- g.waitAndPickClient()
- prs, resp, err := g.getClient().PullRequests.List(g.ctx, g.repoOwner, g.repoName, opt)
+ g.waitAndPickClient(ctx)
+ prs, resp, err := g.getClient().PullRequests.List(ctx, g.repoOwner, g.repoName, opt)
if err != nil {
return nil, false, fmt.Errorf("error while listing repos: %w", err)
}
@@ -679,10 +678,12 @@ func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullReq
var reactions []*base.Reaction
if !g.SkipReactions {
for i := 1; ; i++ {
- g.waitAndPickClient()
- res, resp, err := g.getClient().Reactions.ListIssueReactions(g.ctx, g.repoOwner, g.repoName, pr.GetNumber(), &github.ListOptions{
- Page: i,
- PerPage: perPage,
+ g.waitAndPickClient(ctx)
+ res, resp, err := g.getClient().Reactions.ListIssueReactions(ctx, g.repoOwner, g.repoName, pr.GetNumber(), &github.ListReactionOptions{
+ ListOptions: github.ListOptions{
+ Page: i,
+ PerPage: perPage,
+ },
})
if err != nil {
return nil, false, err
@@ -702,7 +703,7 @@ func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullReq
}
// download patch and saved as tmp file
- g.waitAndPickClient()
+ g.waitAndPickClient(ctx)
allPRs = append(allPRs, &base.PullRequest{
Title: pr.GetTitle(),
@@ -759,17 +760,19 @@ func convertGithubReview(r *github.PullRequestReview) *base.Review {
}
}
-func (g *GithubDownloaderV3) convertGithubReviewComments(cs []*github.PullRequestComment) ([]*base.ReviewComment, error) {
+func (g *GithubDownloaderV3) convertGithubReviewComments(ctx context.Context, cs []*github.PullRequestComment) ([]*base.ReviewComment, error) {
rcs := make([]*base.ReviewComment, 0, len(cs))
for _, c := range cs {
// get reactions
var reactions []*base.Reaction
if !g.SkipReactions {
for i := 1; ; i++ {
- g.waitAndPickClient()
- res, resp, err := g.getClient().Reactions.ListPullRequestCommentReactions(g.ctx, g.repoOwner, g.repoName, c.GetID(), &github.ListOptions{
- Page: i,
- PerPage: g.maxPerPage,
+ g.waitAndPickClient(ctx)
+ res, resp, err := g.getClient().Reactions.ListPullRequestCommentReactions(ctx, g.repoOwner, g.repoName, c.GetID(), &github.ListReactionOptions{
+ ListOptions: github.ListOptions{
+ Page: i,
+ PerPage: g.maxPerPage,
+ },
})
if err != nil {
return nil, err
@@ -806,7 +809,7 @@ func (g *GithubDownloaderV3) convertGithubReviewComments(cs []*github.PullReques
}
// GetReviews returns pull requests review
-func (g *GithubDownloaderV3) GetReviews(reviewable base.Reviewable) ([]*base.Review, error) {
+func (g *GithubDownloaderV3) GetReviews(ctx context.Context, reviewable base.Reviewable) ([]*base.Review, error) {
allReviews := make([]*base.Review, 0, g.maxPerPage)
if g.SkipReviews {
return allReviews, nil
@@ -816,8 +819,8 @@ func (g *GithubDownloaderV3) GetReviews(reviewable base.Reviewable) ([]*base.Rev
}
// Get approve/request change reviews
for {
- g.waitAndPickClient()
- reviews, resp, err := g.getClient().PullRequests.ListReviews(g.ctx, g.repoOwner, g.repoName, int(reviewable.GetForeignIndex()), opt)
+ g.waitAndPickClient(ctx)
+ reviews, resp, err := g.getClient().PullRequests.ListReviews(ctx, g.repoOwner, g.repoName, int(reviewable.GetForeignIndex()), opt)
if err != nil {
return nil, fmt.Errorf("error while listing repos: %w", err)
}
@@ -830,14 +833,14 @@ func (g *GithubDownloaderV3) GetReviews(reviewable base.Reviewable) ([]*base.Rev
PerPage: g.maxPerPage,
}
for {
- g.waitAndPickClient()
- reviewComments, resp, err := g.getClient().PullRequests.ListReviewComments(g.ctx, g.repoOwner, g.repoName, int(reviewable.GetForeignIndex()), review.GetID(), opt2)
+ g.waitAndPickClient(ctx)
+ reviewComments, resp, err := g.getClient().PullRequests.ListReviewComments(ctx, g.repoOwner, g.repoName, int(reviewable.GetForeignIndex()), review.GetID(), opt2)
if err != nil {
return nil, fmt.Errorf("error while listing repos: %w", err)
}
g.setRate(&resp.Rate)
- cs, err := g.convertGithubReviewComments(reviewComments)
+ cs, err := g.convertGithubReviewComments(ctx, reviewComments)
if err != nil {
return nil, err
}
@@ -856,8 +859,8 @@ func (g *GithubDownloaderV3) GetReviews(reviewable base.Reviewable) ([]*base.Rev
}
// Get requested reviews
for {
- g.waitAndPickClient()
- reviewers, resp, err := g.getClient().PullRequests.ListReviewers(g.ctx, g.repoOwner, g.repoName, int(reviewable.GetForeignIndex()), opt)
+ g.waitAndPickClient(ctx)
+ reviewers, resp, err := g.getClient().PullRequests.ListReviewers(ctx, g.repoOwner, g.repoName, int(reviewable.GetForeignIndex()), opt)
if err != nil {
return nil, fmt.Errorf("error while listing repos: %w", err)
}
@@ -879,3 +882,18 @@ func (g *GithubDownloaderV3) GetReviews(reviewable base.Reviewable) ([]*base.Rev
}
return allReviews, nil
}
+
+// FormatCloneURL add authentication into remote URLs
+func (g *GithubDownloaderV3) FormatCloneURL(opts MigrateOptions, remoteAddr string) (string, error) {
+ u, err := url.Parse(remoteAddr)
+ if err != nil {
+ return "", err
+ }
+ if len(opts.AuthToken) > 0 {
+ // "multiple tokens" are used to benefit more "API rate limit quota"
+ // git clone doesn't count for rate limits, so only use the first token.
+ // source: https://github.com/orgs/community/discussions/44515
+ u.User = url.UserPassword("oauth2", strings.Split(opts.AuthToken, ",")[0])
+ }
+ return u.String(), nil
+}
diff --git a/services/migrations/github_test.go b/services/migrations/github_test.go
index 2b89e6dc0f..6d1a5378b9 100644
--- a/services/migrations/github_test.go
+++ b/services/migrations/github_test.go
@@ -5,7 +5,6 @@
package migrations
import (
- "context"
"os"
"testing"
"time"
@@ -13,6 +12,7 @@ import (
base "code.gitea.io/gitea/modules/migration"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestGitHubDownloadRepo(t *testing.T) {
@@ -21,11 +21,12 @@ func TestGitHubDownloadRepo(t *testing.T) {
if token == "" {
t.Skip("Skipping GitHub migration test because GITHUB_READ_TOKEN is empty")
}
- downloader := NewGithubDownloaderV3(context.Background(), "https://github.com", "", "", token, "go-gitea", "test_repo")
- err := downloader.RefreshRate()
+ ctx := t.Context()
+ downloader := NewGithubDownloaderV3(ctx, "https://github.com", "", "", token, "go-gitea", "test_repo")
+ err := downloader.RefreshRate(ctx)
assert.NoError(t, err)
- repo, err := downloader.GetRepoInfo()
+ repo, err := downloader.GetRepoInfo(ctx)
assert.NoError(t, err)
assertRepositoryEqual(t, &base.Repository{
Name: "test_repo",
@@ -36,11 +37,11 @@ func TestGitHubDownloadRepo(t *testing.T) {
DefaultBranch: "master",
}, repo)
- topics, err := downloader.GetTopics()
+ topics, err := downloader.GetTopics(ctx)
assert.NoError(t, err)
assert.Contains(t, topics, "gitea")
- milestones, err := downloader.GetMilestones()
+ milestones, err := downloader.GetMilestones(ctx)
assert.NoError(t, err)
assertMilestonesEqual(t, []*base.Milestone{
{
@@ -63,7 +64,7 @@ func TestGitHubDownloadRepo(t *testing.T) {
},
}, milestones)
- labels, err := downloader.GetLabels()
+ labels, err := downloader.GetLabels(ctx)
assert.NoError(t, err)
assertLabelsEqual(t, []*base.Label{
{
@@ -113,7 +114,7 @@ func TestGitHubDownloadRepo(t *testing.T) {
},
}, labels)
- releases, err := downloader.GetReleases()
+ releases, err := downloader.GetReleases(ctx)
assert.NoError(t, err)
assertReleasesEqual(t, []*base.Release{
{
@@ -129,7 +130,7 @@ func TestGitHubDownloadRepo(t *testing.T) {
}, releases)
// downloader.GetIssues()
- issues, isEnd, err := downloader.GetIssues(1, 2)
+ issues, isEnd, err := downloader.GetIssues(ctx, 1, 2)
assert.NoError(t, err)
assert.False(t, isEnd)
assertIssuesEqual(t, []*base.Issue{
@@ -218,7 +219,7 @@ func TestGitHubDownloadRepo(t *testing.T) {
}, issues)
// downloader.GetComments()
- comments, _, err := downloader.GetComments(&base.Issue{Number: 2, ForeignIndex: 2})
+ comments, _, err := downloader.GetComments(ctx, &base.Issue{Number: 2, ForeignIndex: 2})
assert.NoError(t, err)
assertCommentsEqual(t, []*base.Comment{
{
@@ -248,7 +249,7 @@ func TestGitHubDownloadRepo(t *testing.T) {
}, comments)
// downloader.GetPullRequests()
- prs, _, err := downloader.GetPullRequests(1, 2)
+ prs, _, err := downloader.GetPullRequests(ctx, 1, 2)
assert.NoError(t, err)
assertPullRequestsEqual(t, []*base.PullRequest{
{
@@ -338,7 +339,7 @@ func TestGitHubDownloadRepo(t *testing.T) {
},
}, prs)
- reviews, err := downloader.GetReviews(&base.PullRequest{Number: 3, ForeignIndex: 3})
+ reviews, err := downloader.GetReviews(ctx, &base.PullRequest{Number: 3, ForeignIndex: 3})
assert.NoError(t, err)
assertReviewsEqual(t, []*base.Review{
{
@@ -370,7 +371,7 @@ func TestGitHubDownloadRepo(t *testing.T) {
},
}, reviews)
- reviews, err = downloader.GetReviews(&base.PullRequest{Number: 4, ForeignIndex: 4})
+ reviews, err = downloader.GetReviews(ctx, &base.PullRequest{Number: 4, ForeignIndex: 4})
assert.NoError(t, err)
assertReviewsEqual(t, []*base.Review{
{
@@ -429,3 +430,36 @@ func TestGitHubDownloadRepo(t *testing.T) {
},
}, reviews)
}
+
+func TestGithubMultiToken(t *testing.T) {
+ testCases := []struct {
+ desc string
+ token string
+ expectedCloneURL string
+ }{
+ {
+ desc: "Single Token",
+ token: "single_token",
+ expectedCloneURL: "https://oauth2:single_token@github.com",
+ },
+ {
+ desc: "Multi Token",
+ token: "token1,token2",
+ expectedCloneURL: "https://oauth2:token1@github.com",
+ },
+ }
+ factory := GithubDownloaderV3Factory{}
+
+ for _, tC := range testCases {
+ t.Run(tC.desc, func(t *testing.T) {
+ opts := base.MigrateOptions{CloneAddr: "https://github.com/go-gitea/gitea", AuthToken: tC.token}
+ client, err := factory.New(t.Context(), opts)
+ require.NoError(t, err)
+
+ cloneURL, err := client.FormatCloneURL(opts, "https://github.com")
+ require.NoError(t, err)
+
+ assert.Equal(t, tC.expectedCloneURL, cloneURL)
+ })
+ }
+}
diff --git a/services/migrations/gitlab.go b/services/migrations/gitlab.go
index 295bc7c29f..a19a04bc44 100644
--- a/services/migrations/gitlab.go
+++ b/services/migrations/gitlab.go
@@ -16,12 +16,13 @@ import (
"time"
issues_model "code.gitea.io/gitea/models/issues"
+ "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
base "code.gitea.io/gitea/modules/migration"
"code.gitea.io/gitea/modules/structs"
- "github.com/xanzy/go-gitlab"
+ gitlab "gitlab.com/gitlab-org/api/client-go"
)
var (
@@ -80,7 +81,6 @@ func (r *gitlabIIDResolver) generatePullRequestNumber(mrIID int) int64 {
// because Gitlab has individual Issue and Pull Request numbers.
type GitlabDownloader struct {
base.NullDownloader
- ctx context.Context
client *gitlab.Client
baseURL string
repoID int
@@ -143,7 +143,6 @@ func NewGitlabDownloader(ctx context.Context, baseURL, repoPath, username, passw
}
return &GitlabDownloader{
- ctx: ctx,
client: gitlabClient,
baseURL: baseURL,
repoID: gr.ID,
@@ -164,14 +163,9 @@ func (g *GitlabDownloader) LogString() string {
return fmt.Sprintf("<GitlabDownloader %s [%d]/%s>", g.baseURL, g.repoID, g.repoName)
}
-// SetContext set context
-func (g *GitlabDownloader) SetContext(ctx context.Context) {
- g.ctx = ctx
-}
-
// GetRepoInfo returns a repository information
-func (g *GitlabDownloader) GetRepoInfo() (*base.Repository, error) {
- gr, _, err := g.client.Projects.GetProject(g.repoID, nil, nil, gitlab.WithContext(g.ctx))
+func (g *GitlabDownloader) GetRepoInfo(ctx context.Context) (*base.Repository, error) {
+ gr, _, err := g.client.Projects.GetProject(g.repoID, nil, nil, gitlab.WithContext(ctx))
if err != nil {
return nil, err
}
@@ -207,8 +201,8 @@ func (g *GitlabDownloader) GetRepoInfo() (*base.Repository, error) {
}
// GetTopics return gitlab topics
-func (g *GitlabDownloader) GetTopics() ([]string, error) {
- gr, _, err := g.client.Projects.GetProject(g.repoID, nil, nil, gitlab.WithContext(g.ctx))
+func (g *GitlabDownloader) GetTopics(ctx context.Context) ([]string, error) {
+ gr, _, err := g.client.Projects.GetProject(g.repoID, nil, nil, gitlab.WithContext(ctx))
if err != nil {
return nil, err
}
@@ -216,7 +210,7 @@ func (g *GitlabDownloader) GetTopics() ([]string, error) {
}
// GetMilestones returns milestones
-func (g *GitlabDownloader) GetMilestones() ([]*base.Milestone, error) {
+func (g *GitlabDownloader) GetMilestones(ctx context.Context) ([]*base.Milestone, error) {
perPage := g.maxPerPage
state := "all"
milestones := make([]*base.Milestone, 0, perPage)
@@ -227,7 +221,7 @@ func (g *GitlabDownloader) GetMilestones() ([]*base.Milestone, error) {
Page: i,
PerPage: perPage,
},
- }, nil, gitlab.WithContext(g.ctx))
+ }, nil, gitlab.WithContext(ctx))
if err != nil {
return nil, err
}
@@ -288,14 +282,14 @@ func (g *GitlabDownloader) normalizeColor(val string) string {
}
// GetLabels returns labels
-func (g *GitlabDownloader) GetLabels() ([]*base.Label, error) {
+func (g *GitlabDownloader) GetLabels(ctx context.Context) ([]*base.Label, error) {
perPage := g.maxPerPage
labels := make([]*base.Label, 0, perPage)
for i := 1; ; i++ {
ls, _, err := g.client.Labels.ListLabels(g.repoID, &gitlab.ListLabelsOptions{ListOptions: gitlab.ListOptions{
Page: i,
PerPage: perPage,
- }}, nil, gitlab.WithContext(g.ctx))
+ }}, nil, gitlab.WithContext(ctx))
if err != nil {
return nil, err
}
@@ -314,7 +308,7 @@ func (g *GitlabDownloader) GetLabels() ([]*base.Label, error) {
return labels, nil
}
-func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Release {
+func (g *GitlabDownloader) convertGitlabRelease(ctx context.Context, rel *gitlab.Release) *base.Release {
var zero int
r := &base.Release{
TagName: rel.TagName,
@@ -337,7 +331,7 @@ func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Relea
Size: &zero,
DownloadCount: &zero,
DownloadFunc: func() (io.ReadCloser, error) {
- link, _, err := g.client.ReleaseLinks.GetReleaseLink(g.repoID, rel.TagName, assetID, gitlab.WithContext(g.ctx))
+ link, _, err := g.client.ReleaseLinks.GetReleaseLink(g.repoID, rel.TagName, assetID, gitlab.WithContext(ctx))
if err != nil {
return nil, err
}
@@ -347,11 +341,11 @@ func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Relea
return io.NopCloser(strings.NewReader(link.URL)), nil
}
- req, err := http.NewRequest("GET", link.URL, nil)
+ req, err := http.NewRequest(http.MethodGet, link.URL, nil)
if err != nil {
return nil, err
}
- req = req.WithContext(g.ctx)
+ req = req.WithContext(ctx)
resp, err := httpClient.Do(req)
if err != nil {
return nil, err
@@ -366,7 +360,7 @@ func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Relea
}
// GetReleases returns releases
-func (g *GitlabDownloader) GetReleases() ([]*base.Release, error) {
+func (g *GitlabDownloader) GetReleases(ctx context.Context) ([]*base.Release, error) {
perPage := g.maxPerPage
releases := make([]*base.Release, 0, perPage)
for i := 1; ; i++ {
@@ -375,13 +369,13 @@ func (g *GitlabDownloader) GetReleases() ([]*base.Release, error) {
Page: i,
PerPage: perPage,
},
- }, nil, gitlab.WithContext(g.ctx))
+ }, nil, gitlab.WithContext(ctx))
if err != nil {
return nil, err
}
for _, release := range ls {
- releases = append(releases, g.convertGitlabRelease(release))
+ releases = append(releases, g.convertGitlabRelease(ctx, release))
}
if len(ls) < perPage {
break
@@ -397,7 +391,7 @@ type gitlabIssueContext struct {
// GetIssues returns issues according start and limit
//
// Note: issue label description and colors are not supported by the go-gitlab library at this time
-func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, error) {
+func (g *GitlabDownloader) GetIssues(ctx context.Context, page, perPage int) ([]*base.Issue, bool, error) {
state := "all"
sort := "asc"
@@ -416,7 +410,7 @@ func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
allIssues := make([]*base.Issue, 0, perPage)
- issues, _, err := g.client.Issues.ListProjectIssues(g.repoID, opt, nil, gitlab.WithContext(g.ctx))
+ issues, _, err := g.client.Issues.ListProjectIssues(g.repoID, opt, nil, gitlab.WithContext(ctx))
if err != nil {
return nil, false, fmt.Errorf("error while listing issues: %w", err)
}
@@ -436,7 +430,7 @@ func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
var reactions []*gitlab.AwardEmoji
awardPage := 1
for {
- awards, _, err := g.client.AwardEmoji.ListIssueAwardEmoji(g.repoID, issue.IID, &gitlab.ListAwardEmojiOptions{Page: awardPage, PerPage: perPage}, gitlab.WithContext(g.ctx))
+ awards, _, err := g.client.AwardEmoji.ListIssueAwardEmoji(g.repoID, issue.IID, &gitlab.ListAwardEmojiOptions{Page: awardPage, PerPage: perPage}, gitlab.WithContext(ctx))
if err != nil {
return nil, false, fmt.Errorf("error while listing issue awards: %w", err)
}
@@ -477,7 +471,7 @@ func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
// GetComments returns comments according issueNumber
// TODO: figure out how to transfer comment reactions
-func (g *GitlabDownloader) GetComments(commentable base.Commentable) ([]*base.Comment, bool, error) {
+func (g *GitlabDownloader) GetComments(ctx context.Context, commentable base.Commentable) ([]*base.Comment, bool, error) {
context, ok := commentable.GetContext().(gitlabIssueContext)
if !ok {
return nil, false, fmt.Errorf("unexpected context: %+v", commentable.GetContext())
@@ -495,12 +489,12 @@ func (g *GitlabDownloader) GetComments(commentable base.Commentable) ([]*base.Co
comments, resp, err = g.client.Discussions.ListIssueDiscussions(g.repoID, int(commentable.GetForeignIndex()), &gitlab.ListIssueDiscussionsOptions{
Page: page,
PerPage: g.maxPerPage,
- }, nil, gitlab.WithContext(g.ctx))
+ }, nil, gitlab.WithContext(ctx))
} else {
comments, resp, err = g.client.Discussions.ListMergeRequestDiscussions(g.repoID, int(commentable.GetForeignIndex()), &gitlab.ListMergeRequestDiscussionsOptions{
Page: page,
PerPage: g.maxPerPage,
- }, nil, gitlab.WithContext(g.ctx))
+ }, nil, gitlab.WithContext(ctx))
}
if err != nil {
@@ -528,25 +522,29 @@ func (g *GitlabDownloader) GetComments(commentable base.Commentable) ([]*base.Co
Page: page,
PerPage: g.maxPerPage,
},
- }, nil, gitlab.WithContext(g.ctx))
+ }, nil, gitlab.WithContext(ctx))
} else {
stateEvents, resp, err = g.client.ResourceStateEvents.ListIssueStateEvents(g.repoID, int(commentable.GetForeignIndex()), &gitlab.ListStateEventsOptions{
ListOptions: gitlab.ListOptions{
Page: page,
PerPage: g.maxPerPage,
},
- }, nil, gitlab.WithContext(g.ctx))
+ }, nil, gitlab.WithContext(ctx))
}
if err != nil {
return nil, false, fmt.Errorf("error while listing state events: %v %w", g.repoID, err)
}
for _, stateEvent := range stateEvents {
+ posterUserID, posterUsername := user.GhostUserID, user.GhostUserName
+ if stateEvent.User != nil {
+ posterUserID, posterUsername = int64(stateEvent.User.ID), stateEvent.User.Username
+ }
comment := &base.Comment{
IssueIndex: commentable.GetLocalIndex(),
Index: int64(stateEvent.ID),
- PosterID: int64(stateEvent.User.ID),
- PosterName: stateEvent.User.Username,
+ PosterID: posterUserID,
+ PosterName: posterUsername,
Content: "",
Created: *stateEvent.CreatedAt,
}
@@ -604,7 +602,7 @@ func (g *GitlabDownloader) convertNoteToComment(localIndex int64, note *gitlab.N
}
// GetPullRequests returns pull requests according page and perPage
-func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) {
+func (g *GitlabDownloader) GetPullRequests(ctx context.Context, page, perPage int) ([]*base.PullRequest, bool, error) {
if perPage > g.maxPerPage {
perPage = g.maxPerPage
}
@@ -620,7 +618,7 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
allPRs := make([]*base.PullRequest, 0, perPage)
- prs, _, err := g.client.MergeRequests.ListProjectMergeRequests(g.repoID, opt, nil, gitlab.WithContext(g.ctx))
+ prs, _, err := g.client.MergeRequests.ListProjectMergeRequests(g.repoID, opt, nil, gitlab.WithContext(ctx))
if err != nil {
return nil, false, fmt.Errorf("error while listing merge requests: %w", err)
}
@@ -673,7 +671,7 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
var reactions []*gitlab.AwardEmoji
awardPage := 1
for {
- awards, _, err := g.client.AwardEmoji.ListMergeRequestAwardEmoji(g.repoID, pr.IID, &gitlab.ListAwardEmojiOptions{Page: awardPage, PerPage: perPage}, gitlab.WithContext(g.ctx))
+ awards, _, err := g.client.AwardEmoji.ListMergeRequestAwardEmoji(g.repoID, pr.IID, &gitlab.ListAwardEmojiOptions{Page: awardPage, PerPage: perPage}, gitlab.WithContext(ctx))
if err != nil {
return nil, false, fmt.Errorf("error while listing merge requests awards: %w", err)
}
@@ -733,8 +731,8 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
}
// GetReviews returns pull requests review
-func (g *GitlabDownloader) GetReviews(reviewable base.Reviewable) ([]*base.Review, error) {
- approvals, resp, err := g.client.MergeRequestApprovals.GetConfiguration(g.repoID, int(reviewable.GetForeignIndex()), gitlab.WithContext(g.ctx))
+func (g *GitlabDownloader) GetReviews(ctx context.Context, reviewable base.Reviewable) ([]*base.Review, error) {
+ approvals, resp, err := g.client.MergeRequestApprovals.GetConfiguration(g.repoID, int(reviewable.GetForeignIndex()), gitlab.WithContext(ctx))
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
log.Error(fmt.Sprintf("GitlabDownloader: while migrating a error occurred: '%s'", err.Error()))
diff --git a/services/migrations/gitlab_test.go b/services/migrations/gitlab_test.go
index eccfc4def1..73a1b6a276 100644
--- a/services/migrations/gitlab_test.go
+++ b/services/migrations/gitlab_test.go
@@ -4,7 +4,6 @@
package migrations
import (
- "context"
"fmt"
"net/http"
"net/http/httptest"
@@ -17,7 +16,7 @@ import (
base "code.gitea.io/gitea/modules/migration"
"github.com/stretchr/testify/assert"
- "github.com/xanzy/go-gitlab"
+ gitlab "gitlab.com/gitlab-org/api/client-go"
)
func TestGitlabDownloadRepo(t *testing.T) {
@@ -31,12 +30,12 @@ func TestGitlabDownloadRepo(t *testing.T) {
if err != nil || resp.StatusCode != http.StatusOK {
t.Skipf("Can't access test repo, skipping %s", t.Name())
}
-
- downloader, err := NewGitlabDownloader(context.Background(), "https://gitlab.com", "gitea/test_repo", "", "", gitlabPersonalAccessToken)
+ ctx := t.Context()
+ downloader, err := NewGitlabDownloader(ctx, "https://gitlab.com", "gitea/test_repo", "", "", gitlabPersonalAccessToken)
if err != nil {
t.Fatalf("NewGitlabDownloader is nil: %v", err)
}
- repo, err := downloader.GetRepoInfo()
+ repo, err := downloader.GetRepoInfo(ctx)
assert.NoError(t, err)
// Repo Owner is blank in Gitlab Group repos
assertRepositoryEqual(t, &base.Repository{
@@ -48,12 +47,12 @@ func TestGitlabDownloadRepo(t *testing.T) {
DefaultBranch: "master",
}, repo)
- topics, err := downloader.GetTopics()
+ topics, err := downloader.GetTopics(ctx)
assert.NoError(t, err)
assert.Len(t, topics, 2)
- assert.EqualValues(t, []string{"migration", "test"}, topics)
+ assert.Equal(t, []string{"migration", "test"}, topics)
- milestones, err := downloader.GetMilestones()
+ milestones, err := downloader.GetMilestones(ctx)
assert.NoError(t, err)
assertMilestonesEqual(t, []*base.Milestone{
{
@@ -71,7 +70,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
},
}, milestones)
- labels, err := downloader.GetLabels()
+ labels, err := downloader.GetLabels(ctx)
assert.NoError(t, err)
assertLabelsEqual(t, []*base.Label{
{
@@ -112,7 +111,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
},
}, labels)
- releases, err := downloader.GetReleases()
+ releases, err := downloader.GetReleases(ctx)
assert.NoError(t, err)
assertReleasesEqual(t, []*base.Release{
{
@@ -126,7 +125,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
},
}, releases)
- issues, isEnd, err := downloader.GetIssues(1, 2)
+ issues, isEnd, err := downloader.GetIssues(ctx, 1, 2)
assert.NoError(t, err)
assert.False(t, isEnd)
@@ -214,7 +213,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
},
}, issues)
- comments, _, err := downloader.GetComments(&base.Issue{
+ comments, _, err := downloader.GetComments(ctx, &base.Issue{
Number: 2,
ForeignIndex: 2,
Context: gitlabIssueContext{IsMergeRequest: false},
@@ -255,7 +254,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
},
}, comments)
- prs, _, err := downloader.GetPullRequests(1, 1)
+ prs, _, err := downloader.GetPullRequests(ctx, 1, 1)
assert.NoError(t, err)
assertPullRequestsEqual(t, []*base.PullRequest{
{
@@ -304,7 +303,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
},
}, prs)
- rvs, err := downloader.GetReviews(&base.PullRequest{Number: 1, ForeignIndex: 1})
+ rvs, err := downloader.GetReviews(ctx, &base.PullRequest{Number: 1, ForeignIndex: 1})
assert.NoError(t, err)
assertReviewsEqual(t, []*base.Review{
{
@@ -323,7 +322,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
},
}, rvs)
- rvs, err = downloader.GetReviews(&base.PullRequest{Number: 2, ForeignIndex: 2})
+ rvs, err = downloader.GetReviews(ctx, &base.PullRequest{Number: 2, ForeignIndex: 2})
assert.NoError(t, err)
assertReviewsEqual(t, []*base.Review{
{
@@ -423,9 +422,8 @@ func TestGitlabGetReviews(t *testing.T) {
defer gitlabClientMockTeardown(server)
repoID := 1324
-
+ ctx := t.Context()
downloader := &GitlabDownloader{
- ctx: context.Background(),
client: client,
repoID: repoID,
}
@@ -465,7 +463,7 @@ func TestGitlabGetReviews(t *testing.T) {
mux.HandleFunc(fmt.Sprintf("/api/v4/projects/%d/merge_requests/%d/approvals", testCase.repoID, testCase.prID), mock)
id := int64(testCase.prID)
- rvs, err := downloader.GetReviews(&base.Issue{Number: id, ForeignIndex: id})
+ rvs, err := downloader.GetReviews(ctx, &base.Issue{Number: id, ForeignIndex: id})
assert.NoError(t, err)
assertReviewsEqual(t, []*base.Review{&review}, rvs)
}
@@ -503,7 +501,7 @@ func TestAwardsToReactions(t *testing.T) {
assert.NoError(t, json.Unmarshal([]byte(testResponse), &awards))
reactions := downloader.awardsToReactions(awards)
- assert.EqualValues(t, []*base.Reaction{
+ assert.Equal(t, []*base.Reaction{
{
UserName: "lafriks",
UserID: 1241334,
@@ -595,7 +593,7 @@ func TestNoteToComment(t *testing.T) {
for i, note := range notes {
actualComment := *downloader.convertNoteToComment(17, &note)
- assert.EqualValues(t, actualComment, comments[i])
+ assert.Equal(t, actualComment, comments[i])
}
}
diff --git a/services/migrations/gogs.go b/services/migrations/gogs.go
index 72c52d180b..a4f84dbf72 100644
--- a/services/migrations/gogs.go
+++ b/services/migrations/gogs.go
@@ -13,7 +13,6 @@ import (
"code.gitea.io/gitea/modules/log"
base "code.gitea.io/gitea/modules/migration"
- "code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/structs"
"github.com/gogs/go-gogs-client"
@@ -60,16 +59,14 @@ func (f *GogsDownloaderFactory) GitServiceType() structs.GitServiceType {
// from gogs via API
type GogsDownloader struct {
base.NullDownloader
- ctx context.Context
- client *gogs.Client
baseURL string
repoOwner string
repoName string
userName string
password string
+ token string
openIssuesFinished bool
openIssuesPages int
- transport http.RoundTripper
}
// String implements Stringer
@@ -84,53 +81,45 @@ func (g *GogsDownloader) LogString() string {
return fmt.Sprintf("<GogsDownloader %s %s/%s>", g.baseURL, g.repoOwner, g.repoName)
}
-// SetContext set context
-func (g *GogsDownloader) SetContext(ctx context.Context) {
- g.ctx = ctx
-}
-
// NewGogsDownloader creates a gogs Downloader via gogs API
-func NewGogsDownloader(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GogsDownloader {
+func NewGogsDownloader(_ context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GogsDownloader {
downloader := GogsDownloader{
- ctx: ctx,
baseURL: baseURL,
userName: userName,
password: password,
+ token: token,
repoOwner: repoOwner,
repoName: repoName,
}
+ return &downloader
+}
- var client *gogs.Client
- if len(token) != 0 {
- client = gogs.NewClient(baseURL, token)
- downloader.userName = token
- } else {
- transport := NewMigrationHTTPTransport()
- transport.Proxy = func(req *http.Request) (*url.URL, error) {
- req.SetBasicAuth(userName, password)
- return proxy.Proxy()(req)
- }
- downloader.transport = transport
-
- client = gogs.NewClient(baseURL, "")
- client.SetHTTPClient(&http.Client{
- Transport: &downloader,
- })
- }
+type roundTripperFunc func(req *http.Request) (*http.Response, error)
- downloader.client = client
- return &downloader
+func (rt roundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
+ return rt(r)
}
-// RoundTrip wraps the provided request within this downloader's context and passes it to our internal http.Transport.
-// This implements http.RoundTripper and makes the gogs client requests cancellable even though it is not cancellable itself
-func (g *GogsDownloader) RoundTrip(req *http.Request) (*http.Response, error) {
- return g.transport.RoundTrip(req.WithContext(g.ctx))
+func (g *GogsDownloader) client(ctx context.Context) *gogs.Client {
+ // Gogs client lacks the context support, so we use a custom transport
+ // Then each request uses a dedicated client with its own context
+ httpTransport := NewMigrationHTTPTransport()
+ gogsClient := gogs.NewClient(g.baseURL, g.token)
+ gogsClient.SetHTTPClient(&http.Client{
+ Transport: roundTripperFunc(func(req *http.Request) (*http.Response, error) {
+ if g.password != "" {
+ // Gogs client lacks the support for basic auth, this is the only way to set it
+ req.SetBasicAuth(g.userName, g.password)
+ }
+ return httpTransport.RoundTrip(req.WithContext(ctx))
+ }),
+ })
+ return gogsClient
}
// GetRepoInfo returns a repository information
-func (g *GogsDownloader) GetRepoInfo() (*base.Repository, error) {
- gr, err := g.client.GetRepo(g.repoOwner, g.repoName)
+func (g *GogsDownloader) GetRepoInfo(ctx context.Context) (*base.Repository, error) {
+ gr, err := g.client(ctx).GetRepo(g.repoOwner, g.repoName)
if err != nil {
return nil, err
}
@@ -148,11 +137,11 @@ func (g *GogsDownloader) GetRepoInfo() (*base.Repository, error) {
}
// GetMilestones returns milestones
-func (g *GogsDownloader) GetMilestones() ([]*base.Milestone, error) {
+func (g *GogsDownloader) GetMilestones(ctx context.Context) ([]*base.Milestone, error) {
perPage := 100
milestones := make([]*base.Milestone, 0, perPage)
- ms, err := g.client.ListRepoMilestones(g.repoOwner, g.repoName)
+ ms, err := g.client(ctx).ListRepoMilestones(g.repoOwner, g.repoName)
if err != nil {
return nil, err
}
@@ -171,10 +160,10 @@ func (g *GogsDownloader) GetMilestones() ([]*base.Milestone, error) {
}
// GetLabels returns labels
-func (g *GogsDownloader) GetLabels() ([]*base.Label, error) {
+func (g *GogsDownloader) GetLabels(ctx context.Context) ([]*base.Label, error) {
perPage := 100
labels := make([]*base.Label, 0, perPage)
- ls, err := g.client.ListRepoLabels(g.repoOwner, g.repoName)
+ ls, err := g.client(ctx).ListRepoLabels(g.repoOwner, g.repoName)
if err != nil {
return nil, err
}
@@ -187,7 +176,7 @@ func (g *GogsDownloader) GetLabels() ([]*base.Label, error) {
}
// GetIssues returns issues according start and limit, perPage is not supported
-func (g *GogsDownloader) GetIssues(page, _ int) ([]*base.Issue, bool, error) {
+func (g *GogsDownloader) GetIssues(ctx context.Context, page, _ int) ([]*base.Issue, bool, error) {
var state string
if g.openIssuesFinished {
state = string(gogs.STATE_CLOSED)
@@ -197,7 +186,7 @@ func (g *GogsDownloader) GetIssues(page, _ int) ([]*base.Issue, bool, error) {
g.openIssuesPages = page
}
- issues, isEnd, err := g.getIssues(page, state)
+ issues, isEnd, err := g.getIssues(ctx, page, state)
if err != nil {
return nil, false, err
}
@@ -212,10 +201,10 @@ func (g *GogsDownloader) GetIssues(page, _ int) ([]*base.Issue, bool, error) {
return issues, false, nil
}
-func (g *GogsDownloader) getIssues(page int, state string) ([]*base.Issue, bool, error) {
+func (g *GogsDownloader) getIssues(ctx context.Context, page int, state string) ([]*base.Issue, bool, error) {
allIssues := make([]*base.Issue, 0, 10)
- issues, err := g.client.ListRepoIssues(g.repoOwner, g.repoName, gogs.ListIssueOption{
+ issues, err := g.client(ctx).ListRepoIssues(g.repoOwner, g.repoName, gogs.ListIssueOption{
Page: page,
State: state,
})
@@ -234,10 +223,10 @@ func (g *GogsDownloader) getIssues(page int, state string) ([]*base.Issue, bool,
}
// GetComments returns comments according issueNumber
-func (g *GogsDownloader) GetComments(commentable base.Commentable) ([]*base.Comment, bool, error) {
+func (g *GogsDownloader) GetComments(ctx context.Context, commentable base.Commentable) ([]*base.Comment, bool, error) {
allComments := make([]*base.Comment, 0, 100)
- comments, err := g.client.ListIssueComments(g.repoOwner, g.repoName, commentable.GetForeignIndex())
+ comments, err := g.client(ctx).ListIssueComments(g.repoOwner, g.repoName, commentable.GetForeignIndex())
if err != nil {
return nil, false, fmt.Errorf("error while listing repos: %w", err)
}
@@ -261,7 +250,7 @@ func (g *GogsDownloader) GetComments(commentable base.Commentable) ([]*base.Comm
}
// GetTopics return repository topics
-func (g *GogsDownloader) GetTopics() ([]string, error) {
+func (g *GogsDownloader) GetTopics(_ context.Context) ([]string, error) {
return []string{}, nil
}
diff --git a/services/migrations/gogs_test.go b/services/migrations/gogs_test.go
index 610af183de..503b669f8e 100644
--- a/services/migrations/gogs_test.go
+++ b/services/migrations/gogs_test.go
@@ -4,7 +4,6 @@
package migrations
import (
- "context"
"net/http"
"os"
"testing"
@@ -28,9 +27,9 @@ func TestGogsDownloadRepo(t *testing.T) {
t.Skipf("visit test repo failed, ignored")
return
}
-
- downloader := NewGogsDownloader(context.Background(), "https://try.gogs.io", "", "", gogsPersonalAccessToken, "lunnytest", "TESTREPO")
- repo, err := downloader.GetRepoInfo()
+ ctx := t.Context()
+ downloader := NewGogsDownloader(ctx, "https://try.gogs.io", "", "", gogsPersonalAccessToken, "lunnytest", "TESTREPO")
+ repo, err := downloader.GetRepoInfo(ctx)
assert.NoError(t, err)
assertRepositoryEqual(t, &base.Repository{
@@ -42,7 +41,7 @@ func TestGogsDownloadRepo(t *testing.T) {
DefaultBranch: "master",
}, repo)
- milestones, err := downloader.GetMilestones()
+ milestones, err := downloader.GetMilestones(ctx)
assert.NoError(t, err)
assertMilestonesEqual(t, []*base.Milestone{
{
@@ -51,7 +50,7 @@ func TestGogsDownloadRepo(t *testing.T) {
},
}, milestones)
- labels, err := downloader.GetLabels()
+ labels, err := downloader.GetLabels(ctx)
assert.NoError(t, err)
assertLabelsEqual(t, []*base.Label{
{
@@ -85,7 +84,7 @@ func TestGogsDownloadRepo(t *testing.T) {
}, labels)
// downloader.GetIssues()
- issues, isEnd, err := downloader.GetIssues(1, 8)
+ issues, isEnd, err := downloader.GetIssues(ctx, 1, 8)
assert.NoError(t, err)
assert.False(t, isEnd)
assertIssuesEqual(t, []*base.Issue{
@@ -110,7 +109,7 @@ func TestGogsDownloadRepo(t *testing.T) {
}, issues)
// downloader.GetComments()
- comments, _, err := downloader.GetComments(&base.Issue{Number: 1, ForeignIndex: 1})
+ comments, _, err := downloader.GetComments(ctx, &base.Issue{Number: 1, ForeignIndex: 1})
assert.NoError(t, err)
assertCommentsEqual(t, []*base.Comment{
{
@@ -134,6 +133,6 @@ func TestGogsDownloadRepo(t *testing.T) {
}, comments)
// downloader.GetPullRequests()
- _, _, err = downloader.GetPullRequests(1, 3)
+ _, _, err = downloader.GetPullRequests(ctx, 1, 3)
assert.Error(t, err)
}
diff --git a/services/migrations/migrate.go b/services/migrations/migrate.go
index 51b22d6111..eba9c79df5 100644
--- a/services/migrations/migrate.go
+++ b/services/migrations/migrate.go
@@ -6,6 +6,7 @@ package migrations
import (
"context"
+ "errors"
"fmt"
"net"
"net/url"
@@ -74,11 +75,9 @@ func IsMigrateURLAllowed(remoteURL string, doer *user_model.User) error {
return &git.ErrInvalidCloneAddr{Host: u.Host, IsProtocolInvalid: true, IsPermissionDenied: true, IsURLError: true}
}
- hostName, _, err := net.SplitHostPort(u.Host)
- if err != nil {
- // u.Host can be "host" or "host:port"
- err = nil //nolint
- hostName = u.Host
+ hostName, _, errIgnored := net.SplitHostPort(u.Host)
+ if errIgnored != nil {
+ hostName = u.Host // u.Host can be "host" or "host:port"
}
// some users only use proxy, there is no DNS resolver. it's safe to ignore the LookupIP error
@@ -168,7 +167,7 @@ func newDownloader(ctx context.Context, ownerName string, opts base.MigrateOptio
}
if setting.Migrations.MaxAttempts > 1 {
- downloader = base.NewRetryDownloader(ctx, downloader, setting.Migrations.MaxAttempts, setting.Migrations.RetryBackoff)
+ downloader = base.NewRetryDownloader(downloader, setting.Migrations.MaxAttempts, setting.Migrations.RetryBackoff)
}
return downloader, nil
}
@@ -176,12 +175,12 @@ func newDownloader(ctx context.Context, ownerName string, opts base.MigrateOptio
// migrateRepository will download information and then upload it to Uploader, this is a simple
// process for small repository. For a big repository, save all the data to disk
// before upload is better
-func migrateRepository(_ context.Context, doer *user_model.User, downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions, messenger base.Messenger) error {
+func migrateRepository(ctx context.Context, doer *user_model.User, downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions, messenger base.Messenger) error {
if messenger == nil {
messenger = base.NilMessenger
}
- repo, err := downloader.GetRepoInfo()
+ repo, err := downloader.GetRepoInfo(ctx)
if err != nil {
if !base.IsErrNotSupported(err) {
return err
@@ -211,7 +210,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
if cloneURL.Scheme == "file" || cloneURL.Scheme == "" {
if cloneAddrURL.Scheme != "file" && cloneAddrURL.Scheme != "" {
- return fmt.Errorf("repo info has changed from external to local filesystem")
+ return errors.New("repo info has changed from external to local filesystem")
}
}
@@ -220,14 +219,14 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
log.Trace("migrating git data from %s", repo.CloneURL)
messenger("repo.migrate.migrating_git")
- if err = uploader.CreateRepo(repo, opts); err != nil {
+ if err = uploader.CreateRepo(ctx, repo, opts); err != nil {
return err
}
defer uploader.Close()
log.Trace("migrating topics")
messenger("repo.migrate.migrating_topics")
- topics, err := downloader.GetTopics()
+ topics, err := downloader.GetTopics(ctx)
if err != nil {
if !base.IsErrNotSupported(err) {
return err
@@ -235,7 +234,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
log.Warn("migrating topics is not supported, ignored")
}
if len(topics) != 0 {
- if err = uploader.CreateTopics(topics...); err != nil {
+ if err = uploader.CreateTopics(ctx, topics...); err != nil {
return err
}
}
@@ -243,7 +242,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
if opts.Milestones {
log.Trace("migrating milestones")
messenger("repo.migrate.migrating_milestones")
- milestones, err := downloader.GetMilestones()
+ milestones, err := downloader.GetMilestones(ctx)
if err != nil {
if !base.IsErrNotSupported(err) {
return err
@@ -256,7 +255,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
msBatchSize = len(milestones)
}
- if err := uploader.CreateMilestones(milestones[:msBatchSize]...); err != nil {
+ if err := uploader.CreateMilestones(ctx, milestones[:msBatchSize]...); err != nil {
return err
}
milestones = milestones[msBatchSize:]
@@ -266,7 +265,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
if opts.Labels {
log.Trace("migrating labels")
messenger("repo.migrate.migrating_labels")
- labels, err := downloader.GetLabels()
+ labels, err := downloader.GetLabels(ctx)
if err != nil {
if !base.IsErrNotSupported(err) {
return err
@@ -280,7 +279,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
lbBatchSize = len(labels)
}
- if err := uploader.CreateLabels(labels[:lbBatchSize]...); err != nil {
+ if err := uploader.CreateLabels(ctx, labels[:lbBatchSize]...); err != nil {
return err
}
labels = labels[lbBatchSize:]
@@ -290,7 +289,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
if opts.Releases {
log.Trace("migrating releases")
messenger("repo.migrate.migrating_releases")
- releases, err := downloader.GetReleases()
+ releases, err := downloader.GetReleases(ctx)
if err != nil {
if !base.IsErrNotSupported(err) {
return err
@@ -304,14 +303,14 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
relBatchSize = len(releases)
}
- if err = uploader.CreateReleases(releases[:relBatchSize]...); err != nil {
+ if err = uploader.CreateReleases(ctx, releases[:relBatchSize]...); err != nil {
return err
}
releases = releases[relBatchSize:]
}
// Once all releases (if any) are inserted, sync any remaining non-release tags
- if err = uploader.SyncTags(); err != nil {
+ if err = uploader.SyncTags(ctx); err != nil {
return err
}
}
@@ -329,7 +328,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
issueBatchSize := uploader.MaxBatchInsertSize("issue")
for i := 1; ; i++ {
- issues, isEnd, err := downloader.GetIssues(i, issueBatchSize)
+ issues, isEnd, err := downloader.GetIssues(ctx, i, issueBatchSize)
if err != nil {
if !base.IsErrNotSupported(err) {
return err
@@ -338,7 +337,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
break
}
- if err := uploader.CreateIssues(issues...); err != nil {
+ if err := uploader.CreateIssues(ctx, issues...); err != nil {
return err
}
@@ -346,7 +345,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
allComments := make([]*base.Comment, 0, commentBatchSize)
for _, issue := range issues {
log.Trace("migrating issue %d's comments", issue.Number)
- comments, _, err := downloader.GetComments(issue)
+ comments, _, err := downloader.GetComments(ctx, issue)
if err != nil {
if !base.IsErrNotSupported(err) {
return err
@@ -357,7 +356,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
allComments = append(allComments, comments...)
if len(allComments) >= commentBatchSize {
- if err = uploader.CreateComments(allComments[:commentBatchSize]...); err != nil {
+ if err = uploader.CreateComments(ctx, allComments[:commentBatchSize]...); err != nil {
return err
}
@@ -366,7 +365,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
}
if len(allComments) > 0 {
- if err = uploader.CreateComments(allComments...); err != nil {
+ if err = uploader.CreateComments(ctx, allComments...); err != nil {
return err
}
}
@@ -383,7 +382,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
messenger("repo.migrate.migrating_pulls")
prBatchSize := uploader.MaxBatchInsertSize("pullrequest")
for i := 1; ; i++ {
- prs, isEnd, err := downloader.GetPullRequests(i, prBatchSize)
+ prs, isEnd, err := downloader.GetPullRequests(ctx, i, prBatchSize)
if err != nil {
if !base.IsErrNotSupported(err) {
return err
@@ -392,7 +391,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
break
}
- if err := uploader.CreatePullRequests(prs...); err != nil {
+ if err := uploader.CreatePullRequests(ctx, prs...); err != nil {
return err
}
@@ -402,7 +401,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
allComments := make([]*base.Comment, 0, commentBatchSize)
for _, pr := range prs {
log.Trace("migrating pull request %d's comments", pr.Number)
- comments, _, err := downloader.GetComments(pr)
+ comments, _, err := downloader.GetComments(ctx, pr)
if err != nil {
if !base.IsErrNotSupported(err) {
return err
@@ -413,14 +412,14 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
allComments = append(allComments, comments...)
if len(allComments) >= commentBatchSize {
- if err = uploader.CreateComments(allComments[:commentBatchSize]...); err != nil {
+ if err = uploader.CreateComments(ctx, allComments[:commentBatchSize]...); err != nil {
return err
}
allComments = allComments[commentBatchSize:]
}
}
if len(allComments) > 0 {
- if err = uploader.CreateComments(allComments...); err != nil {
+ if err = uploader.CreateComments(ctx, allComments...); err != nil {
return err
}
}
@@ -429,7 +428,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
// migrate reviews
allReviews := make([]*base.Review, 0, reviewBatchSize)
for _, pr := range prs {
- reviews, err := downloader.GetReviews(pr)
+ reviews, err := downloader.GetReviews(ctx, pr)
if err != nil {
if !base.IsErrNotSupported(err) {
return err
@@ -441,14 +440,14 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
allReviews = append(allReviews, reviews...)
if len(allReviews) >= reviewBatchSize {
- if err = uploader.CreateReviews(allReviews[:reviewBatchSize]...); err != nil {
+ if err = uploader.CreateReviews(ctx, allReviews[:reviewBatchSize]...); err != nil {
return err
}
allReviews = allReviews[reviewBatchSize:]
}
}
if len(allReviews) > 0 {
- if err = uploader.CreateReviews(allReviews...); err != nil {
+ if err = uploader.CreateReviews(ctx, allReviews...); err != nil {
return err
}
}
@@ -463,12 +462,12 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
if opts.Comments && supportAllComments {
log.Trace("migrating comments")
for i := 1; ; i++ {
- comments, isEnd, err := downloader.GetAllComments(i, commentBatchSize)
+ comments, isEnd, err := downloader.GetAllComments(ctx, i, commentBatchSize)
if err != nil {
return err
}
- if err := uploader.CreateComments(comments...); err != nil {
+ if err := uploader.CreateComments(ctx, comments...); err != nil {
return err
}
@@ -478,7 +477,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base
}
}
- return uploader.Finish()
+ return uploader.Finish(ctx)
}
// Init migrations service
diff --git a/services/migrations/onedev.go b/services/migrations/onedev.go
index e2f7b771f3..e052cba0cc 100644
--- a/services/migrations/onedev.go
+++ b/services/migrations/onedev.go
@@ -71,7 +71,6 @@ type onedevUser struct {
// from OneDev
type OneDevDownloader struct {
base.NullDownloader
- ctx context.Context
client *http.Client
baseURL *url.URL
repoName string
@@ -81,15 +80,9 @@ type OneDevDownloader struct {
milestoneMap map[int64]string
}
-// SetContext set context
-func (d *OneDevDownloader) SetContext(ctx context.Context) {
- d.ctx = ctx
-}
-
// NewOneDevDownloader creates a new downloader
-func NewOneDevDownloader(ctx context.Context, baseURL *url.URL, username, password, repoName string) *OneDevDownloader {
+func NewOneDevDownloader(_ context.Context, baseURL *url.URL, username, password, repoName string) *OneDevDownloader {
downloader := &OneDevDownloader{
- ctx: ctx,
baseURL: baseURL,
repoName: repoName,
client: &http.Client{
@@ -121,7 +114,7 @@ func (d *OneDevDownloader) LogString() string {
return fmt.Sprintf("<OneDevDownloader %s [%d]/%s>", d.baseURL, d.repoID, d.repoName)
}
-func (d *OneDevDownloader) callAPI(endpoint string, parameter map[string]string, result any) error {
+func (d *OneDevDownloader) callAPI(ctx context.Context, endpoint string, parameter map[string]string, result any) error {
u, err := d.baseURL.Parse(endpoint)
if err != nil {
return err
@@ -135,7 +128,7 @@ func (d *OneDevDownloader) callAPI(endpoint string, parameter map[string]string,
u.RawQuery = query.Encode()
}
- req, err := http.NewRequestWithContext(d.ctx, "GET", u.String(), nil)
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
if err != nil {
return err
}
@@ -151,7 +144,7 @@ func (d *OneDevDownloader) callAPI(endpoint string, parameter map[string]string,
}
// GetRepoInfo returns repository information
-func (d *OneDevDownloader) GetRepoInfo() (*base.Repository, error) {
+func (d *OneDevDownloader) GetRepoInfo(ctx context.Context) (*base.Repository, error) {
info := make([]struct {
ID int64 `json:"id"`
Name string `json:"name"`
@@ -159,6 +152,7 @@ func (d *OneDevDownloader) GetRepoInfo() (*base.Repository, error) {
}, 0, 1)
err := d.callAPI(
+ ctx,
"/api/projects",
map[string]string{
"query": `"Name" is "` + d.repoName + `"`,
@@ -194,7 +188,7 @@ func (d *OneDevDownloader) GetRepoInfo() (*base.Repository, error) {
}
// GetMilestones returns milestones
-func (d *OneDevDownloader) GetMilestones() ([]*base.Milestone, error) {
+func (d *OneDevDownloader) GetMilestones(ctx context.Context) ([]*base.Milestone, error) {
rawMilestones := make([]struct {
ID int64 `json:"id"`
Name string `json:"name"`
@@ -209,6 +203,7 @@ func (d *OneDevDownloader) GetMilestones() ([]*base.Milestone, error) {
offset := 0
for {
err := d.callAPI(
+ ctx,
endpoint,
map[string]string{
"offset": strconv.Itoa(offset),
@@ -243,7 +238,7 @@ func (d *OneDevDownloader) GetMilestones() ([]*base.Milestone, error) {
}
// GetLabels returns labels
-func (d *OneDevDownloader) GetLabels() ([]*base.Label, error) {
+func (d *OneDevDownloader) GetLabels(_ context.Context) ([]*base.Label, error) {
return []*base.Label{
{
Name: "Bug",
@@ -277,7 +272,7 @@ type onedevIssueContext struct {
}
// GetIssues returns issues
-func (d *OneDevDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, error) {
+func (d *OneDevDownloader) GetIssues(ctx context.Context, page, perPage int) ([]*base.Issue, bool, error) {
rawIssues := make([]struct {
ID int64 `json:"id"`
Number int64 `json:"number"`
@@ -289,6 +284,7 @@ func (d *OneDevDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
}, 0, perPage)
err := d.callAPI(
+ ctx,
"/api/issues",
map[string]string{
"query": `"Project" is "` + d.repoName + `"`,
@@ -308,6 +304,7 @@ func (d *OneDevDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
Value string `json:"value"`
}, 0, 10)
err := d.callAPI(
+ ctx,
fmt.Sprintf("/api/issues/%d/fields", issue.ID),
nil,
&fields,
@@ -329,6 +326,7 @@ func (d *OneDevDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
Name string `json:"name"`
}, 0, 10)
err = d.callAPI(
+ ctx,
fmt.Sprintf("/api/issues/%d/milestones", issue.ID),
nil,
&milestones,
@@ -345,7 +343,7 @@ func (d *OneDevDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
if state == "released" {
state = "closed"
}
- poster := d.tryGetUser(issue.SubmitterID)
+ poster := d.tryGetUser(ctx, issue.SubmitterID)
issues = append(issues, &base.Issue{
Title: issue.Title,
Number: issue.Number,
@@ -370,7 +368,7 @@ func (d *OneDevDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
}
// GetComments returns comments
-func (d *OneDevDownloader) GetComments(commentable base.Commentable) ([]*base.Comment, bool, error) {
+func (d *OneDevDownloader) GetComments(ctx context.Context, commentable base.Commentable) ([]*base.Comment, bool, error) {
context, ok := commentable.GetContext().(onedevIssueContext)
if !ok {
return nil, false, fmt.Errorf("unexpected context: %+v", commentable.GetContext())
@@ -391,6 +389,7 @@ func (d *OneDevDownloader) GetComments(commentable base.Commentable) ([]*base.Co
}
err := d.callAPI(
+ ctx,
endpoint,
nil,
&rawComments,
@@ -412,6 +411,7 @@ func (d *OneDevDownloader) GetComments(commentable base.Commentable) ([]*base.Co
}
err = d.callAPI(
+ ctx,
endpoint,
nil,
&rawChanges,
@@ -425,7 +425,7 @@ func (d *OneDevDownloader) GetComments(commentable base.Commentable) ([]*base.Co
if len(comment.Content) == 0 {
continue
}
- poster := d.tryGetUser(comment.UserID)
+ poster := d.tryGetUser(ctx, comment.UserID)
comments = append(comments, &base.Comment{
IssueIndex: commentable.GetLocalIndex(),
Index: comment.ID,
@@ -450,7 +450,7 @@ func (d *OneDevDownloader) GetComments(commentable base.Commentable) ([]*base.Co
continue
}
- poster := d.tryGetUser(change.UserID)
+ poster := d.tryGetUser(ctx, change.UserID)
comments = append(comments, &base.Comment{
IssueIndex: commentable.GetLocalIndex(),
PosterID: poster.ID,
@@ -466,7 +466,7 @@ func (d *OneDevDownloader) GetComments(commentable base.Commentable) ([]*base.Co
}
// GetPullRequests returns pull requests
-func (d *OneDevDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) {
+func (d *OneDevDownloader) GetPullRequests(ctx context.Context, page, perPage int) ([]*base.PullRequest, bool, error) {
rawPullRequests := make([]struct {
ID int64 `json:"id"`
Number int64 `json:"number"`
@@ -484,6 +484,7 @@ func (d *OneDevDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
}, 0, perPage)
err := d.callAPI(
+ ctx,
"/api/pull-requests",
map[string]string{
"query": `"Target Project" is "` + d.repoName + `"`,
@@ -505,6 +506,7 @@ func (d *OneDevDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
MergeCommitHash string `json:"mergeCommitHash"`
}
err := d.callAPI(
+ ctx,
fmt.Sprintf("/api/pull-requests/%d/merge-preview", pr.ID),
nil,
&mergePreview,
@@ -525,7 +527,7 @@ func (d *OneDevDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
mergedTime = pr.CloseInfo.Date
}
}
- poster := d.tryGetUser(pr.SubmitterID)
+ poster := d.tryGetUser(ctx, pr.SubmitterID)
number := pr.Number + d.maxIssueIndex
pullRequests = append(pullRequests, &base.PullRequest{
@@ -562,7 +564,7 @@ func (d *OneDevDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
}
// GetReviews returns pull requests reviews
-func (d *OneDevDownloader) GetReviews(reviewable base.Reviewable) ([]*base.Review, error) {
+func (d *OneDevDownloader) GetReviews(ctx context.Context, reviewable base.Reviewable) ([]*base.Review, error) {
rawReviews := make([]struct {
ID int64 `json:"id"`
UserID int64 `json:"userId"`
@@ -574,6 +576,7 @@ func (d *OneDevDownloader) GetReviews(reviewable base.Reviewable) ([]*base.Revie
}, 0, 100)
err := d.callAPI(
+ ctx,
fmt.Sprintf("/api/pull-requests/%d/reviews", reviewable.GetForeignIndex()),
nil,
&rawReviews,
@@ -596,7 +599,7 @@ func (d *OneDevDownloader) GetReviews(reviewable base.Reviewable) ([]*base.Revie
}
}
- poster := d.tryGetUser(review.UserID)
+ poster := d.tryGetUser(ctx, review.UserID)
reviews = append(reviews, &base.Review{
IssueIndex: reviewable.GetLocalIndex(),
ReviewerID: poster.ID,
@@ -610,14 +613,15 @@ func (d *OneDevDownloader) GetReviews(reviewable base.Reviewable) ([]*base.Revie
}
// GetTopics return repository topics
-func (d *OneDevDownloader) GetTopics() ([]string, error) {
+func (d *OneDevDownloader) GetTopics(_ context.Context) ([]string, error) {
return []string{}, nil
}
-func (d *OneDevDownloader) tryGetUser(userID int64) *onedevUser {
+func (d *OneDevDownloader) tryGetUser(ctx context.Context, userID int64) *onedevUser {
user, ok := d.userMap[userID]
if !ok {
err := d.callAPI(
+ ctx,
fmt.Sprintf("/api/users/%d", userID),
nil,
&user,
diff --git a/services/migrations/onedev_test.go b/services/migrations/onedev_test.go
index 48412fec64..a05d6cac6e 100644
--- a/services/migrations/onedev_test.go
+++ b/services/migrations/onedev_test.go
@@ -4,7 +4,6 @@
package migrations
import (
- "context"
"net/http"
"net/url"
"testing"
@@ -22,11 +21,12 @@ func TestOneDevDownloadRepo(t *testing.T) {
}
u, _ := url.Parse("https://code.onedev.io")
- downloader := NewOneDevDownloader(context.Background(), u, "", "", "go-gitea-test_repo")
+ ctx := t.Context()
+ downloader := NewOneDevDownloader(ctx, u, "", "", "go-gitea-test_repo")
if err != nil {
t.Fatalf("NewOneDevDownloader is nil: %v", err)
}
- repo, err := downloader.GetRepoInfo()
+ repo, err := downloader.GetRepoInfo(ctx)
assert.NoError(t, err)
assertRepositoryEqual(t, &base.Repository{
Name: "go-gitea-test_repo",
@@ -36,7 +36,7 @@ func TestOneDevDownloadRepo(t *testing.T) {
OriginalURL: "https://code.onedev.io/projects/go-gitea-test_repo",
}, repo)
- milestones, err := downloader.GetMilestones()
+ milestones, err := downloader.GetMilestones(ctx)
assert.NoError(t, err)
deadline := time.Unix(1620086400, 0)
assertMilestonesEqual(t, []*base.Milestone{
@@ -51,11 +51,11 @@ func TestOneDevDownloadRepo(t *testing.T) {
},
}, milestones)
- labels, err := downloader.GetLabels()
+ labels, err := downloader.GetLabels(ctx)
assert.NoError(t, err)
assert.Len(t, labels, 6)
- issues, isEnd, err := downloader.GetIssues(1, 2)
+ issues, isEnd, err := downloader.GetIssues(ctx, 1, 2)
assert.NoError(t, err)
assert.False(t, isEnd)
assertIssuesEqual(t, []*base.Issue{
@@ -94,7 +94,7 @@ func TestOneDevDownloadRepo(t *testing.T) {
},
}, issues)
- comments, _, err := downloader.GetComments(&base.Issue{
+ comments, _, err := downloader.GetComments(ctx, &base.Issue{
Number: 4,
ForeignIndex: 398,
Context: onedevIssueContext{IsPullRequest: false},
@@ -110,7 +110,7 @@ func TestOneDevDownloadRepo(t *testing.T) {
},
}, comments)
- prs, _, err := downloader.GetPullRequests(1, 1)
+ prs, _, err := downloader.GetPullRequests(ctx, 1, 1)
assert.NoError(t, err)
assertPullRequestsEqual(t, []*base.PullRequest{
{
@@ -136,7 +136,7 @@ func TestOneDevDownloadRepo(t *testing.T) {
},
}, prs)
- rvs, err := downloader.GetReviews(&base.PullRequest{Number: 5, ForeignIndex: 186})
+ rvs, err := downloader.GetReviews(ctx, &base.PullRequest{Number: 5, ForeignIndex: 186})
assert.NoError(t, err)
assertReviewsEqual(t, []*base.Review{
{
diff --git a/services/migrations/restore.go b/services/migrations/restore.go
index fd337b22c7..5686285935 100644
--- a/services/migrations/restore.go
+++ b/services/migrations/restore.go
@@ -18,7 +18,6 @@ import (
// RepositoryRestorer implements an Downloader from the local directory
type RepositoryRestorer struct {
base.NullDownloader
- ctx context.Context
baseDir string
repoOwner string
repoName string
@@ -26,13 +25,12 @@ type RepositoryRestorer struct {
}
// NewRepositoryRestorer creates a repository restorer which could restore repository from a dumped folder
-func NewRepositoryRestorer(ctx context.Context, baseDir, owner, repoName string, validation bool) (*RepositoryRestorer, error) {
+func NewRepositoryRestorer(_ context.Context, baseDir, owner, repoName string, validation bool) (*RepositoryRestorer, error) {
baseDir, err := filepath.Abs(baseDir)
if err != nil {
return nil, err
}
return &RepositoryRestorer{
- ctx: ctx,
baseDir: baseDir,
repoOwner: owner,
repoName: repoName,
@@ -48,11 +46,6 @@ func (r *RepositoryRestorer) reviewDir() string {
return filepath.Join(r.baseDir, "reviews")
}
-// SetContext set context
-func (r *RepositoryRestorer) SetContext(ctx context.Context) {
- r.ctx = ctx
-}
-
func (r *RepositoryRestorer) getRepoOptions() (map[string]string, error) {
p := filepath.Join(r.baseDir, "repo.yml")
bs, err := os.ReadFile(p)
@@ -69,7 +62,7 @@ func (r *RepositoryRestorer) getRepoOptions() (map[string]string, error) {
}
// GetRepoInfo returns a repository information
-func (r *RepositoryRestorer) GetRepoInfo() (*base.Repository, error) {
+func (r *RepositoryRestorer) GetRepoInfo(_ context.Context) (*base.Repository, error) {
opts, err := r.getRepoOptions()
if err != nil {
return nil, err
@@ -89,7 +82,7 @@ func (r *RepositoryRestorer) GetRepoInfo() (*base.Repository, error) {
}
// GetTopics return github topics
-func (r *RepositoryRestorer) GetTopics() ([]string, error) {
+func (r *RepositoryRestorer) GetTopics(_ context.Context) ([]string, error) {
p := filepath.Join(r.baseDir, "topic.yml")
topics := struct {
@@ -112,7 +105,7 @@ func (r *RepositoryRestorer) GetTopics() ([]string, error) {
}
// GetMilestones returns milestones
-func (r *RepositoryRestorer) GetMilestones() ([]*base.Milestone, error) {
+func (r *RepositoryRestorer) GetMilestones(_ context.Context) ([]*base.Milestone, error) {
milestones := make([]*base.Milestone, 0, 10)
p := filepath.Join(r.baseDir, "milestone.yml")
err := base.Load(p, &milestones, r.validation)
@@ -127,7 +120,7 @@ func (r *RepositoryRestorer) GetMilestones() ([]*base.Milestone, error) {
}
// GetReleases returns releases
-func (r *RepositoryRestorer) GetReleases() ([]*base.Release, error) {
+func (r *RepositoryRestorer) GetReleases(_ context.Context) ([]*base.Release, error) {
releases := make([]*base.Release, 0, 10)
p := filepath.Join(r.baseDir, "release.yml")
_, err := os.Stat(p)
@@ -158,7 +151,7 @@ func (r *RepositoryRestorer) GetReleases() ([]*base.Release, error) {
}
// GetLabels returns labels
-func (r *RepositoryRestorer) GetLabels() ([]*base.Label, error) {
+func (r *RepositoryRestorer) GetLabels(_ context.Context) ([]*base.Label, error) {
labels := make([]*base.Label, 0, 10)
p := filepath.Join(r.baseDir, "label.yml")
_, err := os.Stat(p)
@@ -182,7 +175,7 @@ func (r *RepositoryRestorer) GetLabels() ([]*base.Label, error) {
}
// GetIssues returns issues according start and limit
-func (r *RepositoryRestorer) GetIssues(page, perPage int) ([]*base.Issue, bool, error) {
+func (r *RepositoryRestorer) GetIssues(_ context.Context, _, _ int) ([]*base.Issue, bool, error) {
issues := make([]*base.Issue, 0, 10)
p := filepath.Join(r.baseDir, "issue.yml")
err := base.Load(p, &issues, r.validation)
@@ -196,7 +189,7 @@ func (r *RepositoryRestorer) GetIssues(page, perPage int) ([]*base.Issue, bool,
}
// GetComments returns comments according issueNumber
-func (r *RepositoryRestorer) GetComments(commentable base.Commentable) ([]*base.Comment, bool, error) {
+func (r *RepositoryRestorer) GetComments(_ context.Context, commentable base.Commentable) ([]*base.Comment, bool, error) {
comments := make([]*base.Comment, 0, 10)
p := filepath.Join(r.commentDir(), fmt.Sprintf("%d.yml", commentable.GetForeignIndex()))
_, err := os.Stat(p)
@@ -220,7 +213,7 @@ func (r *RepositoryRestorer) GetComments(commentable base.Commentable) ([]*base.
}
// GetPullRequests returns pull requests according page and perPage
-func (r *RepositoryRestorer) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) {
+func (r *RepositoryRestorer) GetPullRequests(_ context.Context, page, perPage int) ([]*base.PullRequest, bool, error) {
pulls := make([]*base.PullRequest, 0, 10)
p := filepath.Join(r.baseDir, "pull_request.yml")
_, err := os.Stat(p)
@@ -248,7 +241,7 @@ func (r *RepositoryRestorer) GetPullRequests(page, perPage int) ([]*base.PullReq
}
// GetReviews returns pull requests review
-func (r *RepositoryRestorer) GetReviews(reviewable base.Reviewable) ([]*base.Review, error) {
+func (r *RepositoryRestorer) GetReviews(ctx context.Context, reviewable base.Reviewable) ([]*base.Review, error) {
reviews := make([]*base.Review, 0, 10)
p := filepath.Join(r.reviewDir(), fmt.Sprintf("%d.yml", reviewable.GetForeignIndex()))
_, err := os.Stat(p)
diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go
index e029bbb1d6..7fb7fabb75 100644
--- a/services/mirror/mirror.go
+++ b/services/mirror/mirror.go
@@ -5,7 +5,7 @@ package mirror
import (
"context"
- "fmt"
+ "errors"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/log"
@@ -29,7 +29,7 @@ func doMirrorSync(ctx context.Context, req *SyncRequest) {
}
}
-var errLimit = fmt.Errorf("reached limit")
+var errLimit = errors.New("reached limit")
// Update checks and updates mirror repositories.
func Update(ctx context.Context, pullLimit, pushLimit int) error {
@@ -68,7 +68,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
// Check we've not been cancelled
select {
case <-ctx.Done():
- return fmt.Errorf("aborted")
+ return errors.New("aborted")
default:
}
diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go
index 22d380e8e6..cb90af5894 100644
--- a/services/mirror/mirror_pull.go
+++ b/services/mirror/mirror_pull.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/git"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/globallock"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
@@ -32,7 +33,7 @@ const gitShortEmptySha = "0000000"
// UpdateAddress writes new address to Git repository and database
func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error {
- u, err := giturl.Parse(addr)
+ u, err := giturl.ParseGitURL(addr)
if err != nil {
return fmt.Errorf("invalid addr: %v", err)
}
@@ -40,14 +41,14 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
remoteName := m.GetRemoteName()
repoPath := m.GetRepository(ctx).RepoPath()
// Remove old remote
- _, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath})
- if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
+ _, _, err = git.NewCommand("remote", "rm").AddDynamicArguments(remoteName).RunStdString(ctx, &git.RunOpts{Dir: repoPath})
+ if err != nil && !git.IsRemoteNotExistError(err) {
return err
}
- cmd := git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(addr)
- _, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
- if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
+ cmd := git.NewCommand("remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(addr)
+ _, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath})
+ if err != nil && !git.IsRemoteNotExistError(err) {
return err
}
@@ -55,14 +56,14 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
wikiPath := m.Repo.WikiPath()
wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr)
// Remove old remote of wiki
- _, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: wikiPath})
- if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
+ _, _, err = git.NewCommand("remote", "rm").AddDynamicArguments(remoteName).RunStdString(ctx, &git.RunOpts{Dir: wikiPath})
+ if err != nil && !git.IsRemoteNotExistError(err) {
return err
}
- cmd = git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(wikiRemotePath)
- _, _, err = cmd.RunStdString(&git.RunOpts{Dir: wikiPath})
- if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
+ cmd = git.NewCommand("remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(wikiRemotePath)
+ _, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: wikiPath})
+ if err != nil && !git.IsRemoteNotExistError(err) {
return err
}
}
@@ -70,7 +71,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error
// erase authentication before storing in database
u.User = nil
m.Repo.OriginalURL = u.String()
- return repo_model.UpdateRepositoryCols(ctx, m.Repo, "original_url")
+ return repo_model.UpdateRepositoryColsNoAutoTime(ctx, m.Repo, "original_url")
}
// mirrorSyncResult contains information of a updated reference.
@@ -87,6 +88,7 @@ type mirrorSyncResult struct {
/*
// * [new tag] v0.1.8 -> v0.1.8
// * [new branch] master -> origin/master
+// * [new ref] refs/pull/2/head -> refs/pull/2/head"
// - [deleted] (none) -> origin/test // delete a branch
// - [deleted] (none) -> 1 // delete a tag
// 957a993..a87ba5f test -> origin/test
@@ -117,10 +119,17 @@ func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult {
refName: git.RefNameFromBranch(refName),
oldCommitID: gitShortEmptySha,
})
+ case strings.HasPrefix(lines[i], " * [new ref]"): // new reference
+ results = append(results, &mirrorSyncResult{
+ refName: git.RefName(refName),
+ oldCommitID: gitShortEmptySha,
+ })
case strings.HasPrefix(lines[i], " - "): // Delete reference
isTag := !strings.HasPrefix(refName, remoteName+"/")
var refFullName git.RefName
- if isTag {
+ if strings.HasPrefix(refName, "refs/") {
+ refFullName = git.RefName(refName)
+ } else if isTag {
refFullName = git.RefNameFromTag(refName)
} else {
refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/"))
@@ -143,8 +152,15 @@ func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult {
log.Error("Expect two SHAs but not what found: %q", lines[i])
continue
}
+ var refFullName git.RefName
+ if strings.HasPrefix(refName, "refs/") {
+ refFullName = git.RefName(refName)
+ } else {
+ refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/"))
+ }
+
results = append(results, &mirrorSyncResult{
- refName: git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")),
+ refName: refFullName,
oldCommitID: shas[0],
newCommitID: shas[1],
})
@@ -159,8 +175,15 @@ func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult {
log.Error("Expect two SHAs but not what found: %q", lines[i])
continue
}
+ var refFullName git.RefName
+ if strings.HasPrefix(refName, "refs/") {
+ refFullName = git.RefName(refName)
+ } else {
+ refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/"))
+ }
+
results = append(results, &mirrorSyncResult{
- refName: git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")),
+ refName: refFullName,
oldCommitID: shas[0],
newCommitID: shas[1],
})
@@ -186,8 +209,8 @@ func pruneBrokenReferences(ctx context.Context,
stderrBuilder.Reset()
stdoutBuilder.Reset()
- pruneErr := git.NewCommand(ctx, "remote", "prune").AddDynamicArguments(m.GetRemoteName()).
- Run(&git.RunOpts{
+ pruneErr := git.NewCommand("remote", "prune").AddDynamicArguments(m.GetRemoteName()).
+ Run(ctx, &git.RunOpts{
Timeout: timeout,
Dir: repoPath,
Stdout: stdoutBuilder,
@@ -212,6 +235,24 @@ func pruneBrokenReferences(ctx context.Context,
return pruneErr
}
+// checkRecoverableSyncError takes an error message from a git fetch command and returns false if it should be a fatal/blocking error
+func checkRecoverableSyncError(stderrMessage string) bool {
+ switch {
+ case strings.Contains(stderrMessage, "unable to resolve reference") && strings.Contains(stderrMessage, "reference broken"):
+ return true
+ case strings.Contains(stderrMessage, "remote error") && strings.Contains(stderrMessage, "not our ref"):
+ return true
+ case strings.Contains(stderrMessage, "cannot lock ref") && strings.Contains(stderrMessage, "but expected"):
+ return true
+ case strings.Contains(stderrMessage, "cannot lock ref") && strings.Contains(stderrMessage, "unable to resolve reference"):
+ return true
+ case strings.Contains(stderrMessage, "Unable to create") && strings.Contains(stderrMessage, ".lock"):
+ return true
+ default:
+ return false
+ }
+}
+
// runSync returns true if sync finished without error.
func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bool) {
repoPath := m.Repo.RepoPath()
@@ -221,7 +262,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
log.Trace("SyncMirrors [repo: %-v]: running git remote update...", m.Repo)
// use fetch but not remote update because git fetch support --tags but remote update doesn't
- cmd := git.NewCommand(ctx, "fetch")
+ cmd := git.NewCommand("fetch")
if m.EnablePrune {
cmd.AddArguments("--prune")
}
@@ -237,7 +278,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
stdoutBuilder := strings.Builder{}
stderrBuilder := strings.Builder{}
- if err := cmd.Run(&git.RunOpts{
+ if err := cmd.Run(ctx, &git.RunOpts{
Timeout: timeout,
Dir: repoPath,
Env: envs,
@@ -252,7 +293,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
stdoutMessage := util.SanitizeCredentialURLs(stdout)
// Now check if the error is a resolve reference due to broken reference
- if strings.Contains(stderr, "unable to resolve reference") && strings.Contains(stderr, "reference broken") {
+ if checkRecoverableSyncError(stderr) {
log.Warn("SyncMirrors [repo: %-v]: failed to update mirror repository due to broken references:\nStdout: %s\nStderr: %s\nErr: %v\nAttempting Prune", m.Repo, stdoutMessage, stderrMessage, err)
err = nil
@@ -262,7 +303,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
// Successful prune - reattempt mirror
stderrBuilder.Reset()
stdoutBuilder.Reset()
- if err = cmd.Run(&git.RunOpts{
+ if err = cmd.Run(ctx, &git.RunOpts{
Timeout: timeout,
Dir: repoPath,
Stdout: &stdoutBuilder,
@@ -301,6 +342,15 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
return nil, false
}
+ if m.LFS && setting.LFS.StartServer {
+ log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo)
+ endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint)
+ lfsClient := lfs.NewClient(endpoint, nil)
+ if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil {
+ log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo, err)
+ }
+ }
+
log.Trace("SyncMirrors [repo: %-v]: syncing branches...", m.Repo)
if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, m.Repo, gitRepo, 0); err != nil {
log.Error("SyncMirrors [repo: %-v]: failed to synchronize branches: %v", m.Repo, err)
@@ -310,15 +360,6 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
if err = repo_module.SyncReleasesWithTags(ctx, m.Repo, gitRepo); err != nil {
log.Error("SyncMirrors [repo: %-v]: failed to synchronize tags to releases: %v", m.Repo, err)
}
-
- if m.LFS && setting.LFS.StartServer {
- log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo)
- endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint)
- lfsClient := lfs.NewClient(endpoint, nil)
- if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil {
- log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo, err)
- }
- }
gitRepo.Close()
log.Trace("SyncMirrors [repo: %-v]: updating size of repository", m.Repo)
@@ -330,8 +371,8 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
log.Trace("SyncMirrors [repo: %-v Wiki]: running git remote update...", m.Repo)
stderrBuilder.Reset()
stdoutBuilder.Reset()
- if err := git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()).
- Run(&git.RunOpts{
+ if err := git.NewCommand("remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()).
+ Run(ctx, &git.RunOpts{
Timeout: timeout,
Dir: wikiPath,
Stdout: &stdoutBuilder,
@@ -345,7 +386,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
stdoutMessage := util.SanitizeCredentialURLs(stdout)
// Now check if the error is a resolve reference due to broken reference
- if strings.Contains(stderrMessage, "unable to resolve reference") && strings.Contains(stderrMessage, "reference broken") {
+ if checkRecoverableSyncError(stderrMessage) {
log.Warn("SyncMirrors [repo: %-v Wiki]: failed to update mirror wiki repository due to broken references:\nStdout: %s\nStderr: %s\nErr: %v\nAttempting Prune", m.Repo, stdoutMessage, stderrMessage, err)
err = nil
@@ -356,8 +397,8 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
stderrBuilder.Reset()
stdoutBuilder.Reset()
- if err = git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()).
- Run(&git.RunOpts{
+ if err = git.NewCommand("remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()).
+ Run(ctx, &git.RunOpts{
Timeout: timeout,
Dir: wikiPath,
Stdout: &stdoutBuilder,
@@ -396,13 +437,17 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
}
for _, branch := range branches {
- cache.Remove(m.Repo.GetCommitsCountCacheKey(branch.Name, true))
+ cache.Remove(m.Repo.GetCommitsCountCacheKey(branch, true))
}
m.UpdatedUnix = timeutil.TimeStampNow()
return parseRemoteUpdateOutput(output, m.GetRemoteName()), true
}
+func getRepoPullMirrorLockKey(repoID int64) string {
+ return fmt.Sprintf("repo_pull_mirror_%d", repoID)
+}
+
// SyncPullMirror starts the sync of the pull mirror and schedules the next run.
func SyncPullMirror(ctx context.Context, repoID int64) bool {
log.Trace("SyncMirrors [repo_id: %v]", repoID)
@@ -415,6 +460,13 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
log.Error("PANIC whilst SyncMirrors[repo_id: %d] Panic: %v\nStacktrace: %s", repoID, err, log.Stack(2))
}()
+ releaser, err := globallock.Lock(ctx, getRepoPullMirrorLockKey(repoID))
+ if err != nil {
+ log.Error("globallock.Lock(): %v", err)
+ return false
+ }
+ defer releaser()
+
m, err := repo_model.GetMirrorByRepoID(ctx, repoID)
if err != nil {
log.Error("SyncMirrors [repo_id: %v]: unable to GetMirrorByRepoID: %v", repoID, err)
@@ -601,7 +653,7 @@ func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, re
}
m.Repo.IsEmpty = false
// Update the is empty and default_branch columns
- if err := repo_model.UpdateRepositoryCols(ctx, m.Repo, "default_branch", "is_empty"); err != nil {
+ if err := repo_model.UpdateRepositoryColsWithAutoTime(ctx, m.Repo, "default_branch", "is_empty"); err != nil {
log.Error("Failed to update default branch of repository %-v. Error: %v", m.Repo, err)
desc := fmt.Sprintf("Failed to update default branch of repository '%s': %v", m.Repo.RepoPath(), err)
if err = system_model.CreateRepositoryNotice(desc); err != nil {
diff --git a/services/mirror/mirror_pull_test.go b/services/mirror/mirror_pull_test.go
new file mode 100644
index 0000000000..97859be5b0
--- /dev/null
+++ b/services/mirror/mirror_pull_test.go
@@ -0,0 +1,94 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package mirror
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_parseRemoteUpdateOutput(t *testing.T) {
+ output := `
+ * [new tag] v0.1.8 -> v0.1.8
+ * [new branch] master -> origin/master
+ - [deleted] (none) -> origin/test1
+ - [deleted] (none) -> tag1
+ + f895a1e...957a993 test2 -> origin/test2 (forced update)
+ 957a993..a87ba5f test3 -> origin/test3
+ * [new ref] refs/pull/26595/head -> refs/pull/26595/head
+ * [new ref] refs/pull/26595/merge -> refs/pull/26595/merge
+ e0639e38fb..6db2410489 refs/pull/25873/head -> refs/pull/25873/head
+ + 1c97ebc746...976d27d52f refs/pull/25873/merge -> refs/pull/25873/merge (forced update)
+`
+ results := parseRemoteUpdateOutput(output, "origin")
+ assert.Len(t, results, 10)
+ assert.Equal(t, "refs/tags/v0.1.8", results[0].refName.String())
+ assert.Equal(t, gitShortEmptySha, results[0].oldCommitID)
+ assert.Empty(t, results[0].newCommitID)
+
+ assert.Equal(t, "refs/heads/master", results[1].refName.String())
+ assert.Equal(t, gitShortEmptySha, results[1].oldCommitID)
+ assert.Empty(t, results[1].newCommitID)
+
+ assert.Equal(t, "refs/heads/test1", results[2].refName.String())
+ assert.Empty(t, results[2].oldCommitID)
+ assert.Equal(t, gitShortEmptySha, results[2].newCommitID)
+
+ assert.Equal(t, "refs/tags/tag1", results[3].refName.String())
+ assert.Empty(t, results[3].oldCommitID)
+ assert.Equal(t, gitShortEmptySha, results[3].newCommitID)
+
+ assert.Equal(t, "refs/heads/test2", results[4].refName.String())
+ assert.Equal(t, "f895a1e", results[4].oldCommitID)
+ assert.Equal(t, "957a993", results[4].newCommitID)
+
+ assert.Equal(t, "refs/heads/test3", results[5].refName.String())
+ assert.Equal(t, "957a993", results[5].oldCommitID)
+ assert.Equal(t, "a87ba5f", results[5].newCommitID)
+
+ assert.Equal(t, "refs/pull/26595/head", results[6].refName.String())
+ assert.Equal(t, gitShortEmptySha, results[6].oldCommitID)
+ assert.Empty(t, results[6].newCommitID)
+
+ assert.Equal(t, "refs/pull/26595/merge", results[7].refName.String())
+ assert.Equal(t, gitShortEmptySha, results[7].oldCommitID)
+ assert.Empty(t, results[7].newCommitID)
+
+ assert.Equal(t, "refs/pull/25873/head", results[8].refName.String())
+ assert.Equal(t, "e0639e38fb", results[8].oldCommitID)
+ assert.Equal(t, "6db2410489", results[8].newCommitID)
+
+ assert.Equal(t, "refs/pull/25873/merge", results[9].refName.String())
+ assert.Equal(t, "1c97ebc746", results[9].oldCommitID)
+ assert.Equal(t, "976d27d52f", results[9].newCommitID)
+}
+
+func Test_checkRecoverableSyncError(t *testing.T) {
+ cases := []struct {
+ recoverable bool
+ message string
+ }{
+ // A race condition in http git-fetch where certain refs were listed on the remote and are no longer there, would exit status 128
+ {true, "fatal: remote error: upload-pack: not our ref 988881adc9fc3655077dc2d4d757d480b5ea0e11"},
+ // A race condition where a local gc/prune removes a named ref during a git-fetch would exit status 1
+ {true, "cannot lock ref 'refs/pull/123456/merge': unable to resolve reference 'refs/pull/134153/merge'"},
+ // A race condition in http git-fetch where named refs were listed on the remote and are no longer there
+ {true, "error: cannot lock ref 'refs/remotes/origin/foo': unable to resolve reference 'refs/remotes/origin/foo': reference broken"},
+ // A race condition in http git-fetch where named refs were force-pushed during the update, would exit status 128
+ {true, "error: cannot lock ref 'refs/pull/123456/merge': is at 988881adc9fc3655077dc2d4d757d480b5ea0e11 but expected 7f894307ffc9553edbd0b671cab829786866f7b2"},
+ // A race condition with other local git operations, such as git-maintenance, would exit status 128 (well, "Unable" the "U" is uppercase)
+ {true, "fatal: Unable to create '/data/gitea-repositories/foo-org/bar-repo.git/./objects/info/commit-graphs/commit-graph-chain.lock': File exists."},
+ // Missing or unauthorized credentials, would exit status 128
+ {false, "fatal: Authentication failed for 'https://example.com/foo-does-not-exist/bar.git/'"},
+ // A non-existent remote repository, would exit status 128
+ {false, "fatal: Could not read from remote repository."},
+ // A non-functioning proxy, would exit status 128
+ {false, "fatal: unable to access 'https://example.com/foo-does-not-exist/bar.git/': Failed to connect to configured-https-proxy port 1080 after 0 ms: Couldn't connect to server"},
+ }
+
+ for _, c := range cases {
+ assert.Equal(t, c.recoverable, checkRecoverableSyncError(c.message), "test case: %s", c.message)
+ }
+}
diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go
index 02ff97b1f0..9b57427d98 100644
--- a/services/mirror/mirror_push.go
+++ b/services/mirror/mirror_push.go
@@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
+ "code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
@@ -29,14 +30,14 @@ var stripExitStatus = regexp.MustCompile(`exit status \d+ - `)
// AddPushMirrorRemote registers the push mirror remote.
func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error {
addRemoteAndConfig := func(addr, path string) error {
- cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr)
- if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: path}); err != nil {
+ cmd := git.NewCommand("remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr)
+ if _, _, err := cmd.RunStdString(ctx, &git.RunOpts{Dir: path}); err != nil {
return err
}
- if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunStdString(&git.RunOpts{Dir: path}); err != nil {
+ if _, _, err := git.NewCommand("config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunStdString(ctx, &git.RunOpts{Dir: path}); err != nil {
return err
}
- if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunStdString(&git.RunOpts{Dir: path}); err != nil {
+ if _, _, err := git.NewCommand("config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunStdString(ctx, &git.RunOpts{Dir: path}); err != nil {
return err
}
return nil
@@ -60,15 +61,15 @@ func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str
// RemovePushMirrorRemote removes the push mirror remote.
func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error {
- cmd := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(m.RemoteName)
+ cmd := git.NewCommand("remote", "rm").AddDynamicArguments(m.RemoteName)
_ = m.GetRepository(ctx)
- if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: m.Repo.RepoPath()}); err != nil {
+ if _, _, err := cmd.RunStdString(ctx, &git.RunOpts{Dir: m.Repo.RepoPath()}); err != nil {
return err
}
if m.Repo.HasWiki() {
- if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: m.Repo.WikiPath()}); err != nil {
+ if _, _, err := cmd.RunStdString(ctx, &git.RunOpts{Dir: m.Repo.WikiPath()}); err != nil {
// The wiki remote may not exist
log.Warn("Wiki Remote[%d] could not be removed: %v", m.ID, err)
}
@@ -142,7 +143,7 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
var gitRepo *git.Repository
if isWiki {
- gitRepo, err = gitrepo.OpenWikiRepository(ctx, repo)
+ gitRepo, err = gitrepo.OpenRepository(ctx, repo.WikiStorageRepo())
} else {
gitRepo, err = gitrepo.OpenRepository(ctx, repo)
}
@@ -161,11 +162,13 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
log.Trace("Pushing %s mirror[%d] remote %s", path, m.ID, m.RemoteName)
+ envs := proxy.EnvWithProxy(remoteURL.URL)
if err := git.Push(ctx, path, git.PushOptions{
Remote: m.RemoteName,
Force: true,
Mirror: true,
Timeout: timeout,
+ Env: envs,
}); err != nil {
log.Error("Error pushing %s mirror[%d] remote %s: %v", path, m.ID, m.RemoteName, err)
diff --git a/services/mirror/mirror_test.go b/services/mirror/mirror_test.go
deleted file mode 100644
index 8ad524b608..0000000000
--- a/services/mirror/mirror_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package mirror
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func Test_parseRemoteUpdateOutput(t *testing.T) {
- output := `
- * [new tag] v0.1.8 -> v0.1.8
- * [new branch] master -> origin/master
- - [deleted] (none) -> origin/test1
- - [deleted] (none) -> tag1
- + f895a1e...957a993 test2 -> origin/test2 (forced update)
- 957a993..a87ba5f test3 -> origin/test3
-`
- results := parseRemoteUpdateOutput(output, "origin")
- assert.Len(t, results, 6)
- assert.EqualValues(t, "refs/tags/v0.1.8", results[0].refName.String())
- assert.EqualValues(t, gitShortEmptySha, results[0].oldCommitID)
- assert.EqualValues(t, "", results[0].newCommitID)
-
- assert.EqualValues(t, "refs/heads/master", results[1].refName.String())
- assert.EqualValues(t, gitShortEmptySha, results[1].oldCommitID)
- assert.EqualValues(t, "", results[1].newCommitID)
-
- assert.EqualValues(t, "refs/heads/test1", results[2].refName.String())
- assert.EqualValues(t, "", results[2].oldCommitID)
- assert.EqualValues(t, gitShortEmptySha, results[2].newCommitID)
-
- assert.EqualValues(t, "refs/tags/tag1", results[3].refName.String())
- assert.EqualValues(t, "", results[3].oldCommitID)
- assert.EqualValues(t, gitShortEmptySha, results[3].newCommitID)
-
- assert.EqualValues(t, "refs/heads/test2", results[4].refName.String())
- assert.EqualValues(t, "f895a1e", results[4].oldCommitID)
- assert.EqualValues(t, "957a993", results[4].newCommitID)
-
- assert.EqualValues(t, "refs/heads/test3", results[5].refName.String())
- assert.EqualValues(t, "957a993", results[5].oldCommitID)
- assert.EqualValues(t, "a87ba5f", results[5].newCommitID)
-}
diff --git a/services/notify/notifier.go b/services/notify/notifier.go
index 29bbb5702b..875a70e564 100644
--- a/services/notify/notifier.go
+++ b/services/notify/notifier.go
@@ -6,6 +6,7 @@ package notify
import (
"context"
+ actions_model "code.gitea.io/gitea/models/actions"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
packages_model "code.gitea.io/gitea/models/packages"
@@ -77,4 +78,8 @@ type Notifier interface {
ChangeDefaultBranch(ctx context.Context, repo *repo_model.Repository)
CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, commit *repository.PushCommit, sender *user_model.User, status *git_model.CommitStatus)
+
+ WorkflowRunStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, run *actions_model.ActionRun)
+
+ WorkflowJobStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, job *actions_model.ActionRunJob, task *actions_model.ActionTask)
}
diff --git a/services/notify/notify.go b/services/notify/notify.go
index 3b5f24340b..2416cbd2e0 100644
--- a/services/notify/notify.go
+++ b/services/notify/notify.go
@@ -6,6 +6,7 @@ package notify
import (
"context"
+ actions_model "code.gitea.io/gitea/models/actions"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
packages_model "code.gitea.io/gitea/models/packages"
@@ -45,10 +46,25 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
}
}
+func shouldSendCommentChangeNotification(ctx context.Context, comment *issues_model.Comment) bool {
+ if err := comment.LoadReview(ctx); err != nil {
+ log.Error("LoadReview: %v", err)
+ return false
+ } else if comment.Review != nil && comment.Review.Type == issues_model.ReviewTypePending {
+ // Pending review comments updating should not triggered
+ return false
+ }
+ return true
+}
+
// CreateIssueComment notifies issue comment related message to notifiers
func CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository,
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
) {
+ if !shouldSendCommentChangeNotification(ctx, comment) {
+ return
+ }
+
for _, notifier := range notifiers {
notifier.CreateIssueComment(ctx, doer, repo, issue, comment, mentions)
}
@@ -155,6 +171,10 @@ func PullReviewDismiss(ctx context.Context, doer *user_model.User, review *issue
// UpdateComment notifies update comment to notifiers
func UpdateComment(ctx context.Context, doer *user_model.User, c *issues_model.Comment, oldContent string) {
+ if !shouldSendCommentChangeNotification(ctx, c) {
+ return
+ }
+
for _, notifier := range notifiers {
notifier.UpdateComment(ctx, doer, c, oldContent)
}
@@ -162,6 +182,10 @@ func UpdateComment(ctx context.Context, doer *user_model.User, c *issues_model.C
// DeleteComment notifies delete comment to notifiers
func DeleteComment(ctx context.Context, doer *user_model.User, c *issues_model.Comment) {
+ if !shouldSendCommentChangeNotification(ctx, c) {
+ return
+ }
+
for _, notifier := range notifiers {
notifier.DeleteComment(ctx, doer, c)
}
@@ -272,9 +296,9 @@ func MigrateRepository(ctx context.Context, doer, u *user_model.User, repo *repo
}
// TransferRepository notifies create repository to notifiers
-func TransferRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, newOwnerName string) {
+func TransferRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldOwnerName string) {
for _, notifier := range notifiers {
- notifier.TransferRepository(ctx, doer, repo, newOwnerName)
+ notifier.TransferRepository(ctx, doer, repo, oldOwnerName)
}
}
@@ -374,3 +398,15 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, commit
notifier.CreateCommitStatus(ctx, repo, commit, sender, status)
}
}
+
+func WorkflowRunStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, run *actions_model.ActionRun) {
+ for _, notifier := range notifiers {
+ notifier.WorkflowRunStatusUpdate(ctx, repo, sender, run)
+ }
+}
+
+func WorkflowJobStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, job *actions_model.ActionRunJob, task *actions_model.ActionTask) {
+ for _, notifier := range notifiers {
+ notifier.WorkflowJobStatusUpdate(ctx, repo, sender, job, task)
+ }
+}
diff --git a/services/notify/null.go b/services/notify/null.go
index 7354efd701..c3085d7c9e 100644
--- a/services/notify/null.go
+++ b/services/notify/null.go
@@ -6,6 +6,7 @@ package notify
import (
"context"
+ actions_model "code.gitea.io/gitea/models/actions"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
packages_model "code.gitea.io/gitea/models/packages"
@@ -212,3 +213,9 @@ func (*NullNotifier) ChangeDefaultBranch(ctx context.Context, repo *repo_model.R
func (*NullNotifier) CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, commit *repository.PushCommit, sender *user_model.User, status *git_model.CommitStatus) {
}
+
+func (*NullNotifier) WorkflowRunStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, run *actions_model.ActionRun) {
+}
+
+func (*NullNotifier) WorkflowJobStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, job *actions_model.ActionRunJob, task *actions_model.ActionTask) {
+}
diff --git a/services/oauth2_provider/access_token.go b/services/oauth2_provider/access_token.go
index 5cb6fb64c5..5a190d8616 100644
--- a/services/oauth2_provider/access_token.go
+++ b/services/oauth2_provider/access_token.go
@@ -1,12 +1,13 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package oauth2_provider //nolint
+package oauth2_provider
import (
"context"
"fmt"
"slices"
+ "strconv"
"strings"
auth "code.gitea.io/gitea/models/auth"
@@ -15,7 +16,9 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"github.com/golang-jwt/jwt/v5"
)
@@ -82,7 +85,7 @@ func GrantAdditionalScopes(grantScopes string) auth.AccessTokenScope {
}
var accessScopes []string // the scopes for access control, but not for general information
- for _, scope := range strings.Split(grantScopes, " ") {
+ for scope := range strings.SplitSeq(grantScopes, " ") {
if scope != "" && !slices.Contains(generalScopesSupported, scope) {
accessScopes = append(accessScopes, scope)
}
@@ -103,6 +106,20 @@ func GrantAdditionalScopes(grantScopes string) auth.AccessTokenScope {
return auth.AccessTokenScopeAll
}
+func NewJwtRegisteredClaimsFromUser(clientID string, grantUserID int64, exp *jwt.NumericDate) jwt.RegisteredClaims {
+ // https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig
+ // The issuer value returned MUST be identical to the Issuer URL that was used as the prefix to /.well-known/openid-configuration
+ // to retrieve the configuration information. This MUST also be identical to the "iss" Claim value in ID Tokens issued from this Issuer.
+ // * https://accounts.google.com/.well-known/openid-configuration
+ // * https://github.com/login/oauth/.well-known/openid-configuration
+ return jwt.RegisteredClaims{
+ Issuer: strings.TrimSuffix(setting.AppURL, "/"),
+ Audience: []string{clientID},
+ Subject: strconv.FormatInt(grantUserID, 10),
+ ExpiresAt: exp,
+ }
+}
+
func NewAccessTokenResponse(ctx context.Context, grant *auth.OAuth2Grant, serverKey, clientKey JWTSigningKey) (*AccessTokenResponse, *AccessTokenError) {
if setting.OAuth2.InvalidateRefreshTokens {
if err := grant.IncreaseCounter(ctx); err != nil {
@@ -173,13 +190,8 @@ func NewAccessTokenResponse(ctx context.Context, grant *auth.OAuth2Grant, server
}
idToken := &OIDCToken{
- RegisteredClaims: jwt.RegisteredClaims{
- ExpiresAt: jwt.NewNumericDate(expirationDate.AsTime()),
- Issuer: setting.AppURL,
- Audience: []string{app.ClientID},
- Subject: fmt.Sprint(grant.UserID),
- },
- Nonce: grant.Nonce,
+ RegisteredClaims: NewJwtRegisteredClaimsFromUser(app.ClientID, grant.UserID, jwt.NewNumericDate(expirationDate.AsTime())),
+ Nonce: grant.Nonce,
}
if grant.ScopeContains("profile") {
idToken.Name = user.DisplayName()
@@ -230,12 +242,11 @@ func NewAccessTokenResponse(ctx context.Context, grant *auth.OAuth2Grant, server
}, nil
}
-// returns a list of "org" and "org:team" strings,
-// that the given user is a part of.
+// GetOAuthGroupsForUser returns a list of "org" and "org:team" strings, that the given user is a part of.
func GetOAuthGroupsForUser(ctx context.Context, user *user_model.User, onlyPublicGroups bool) ([]string, error) {
orgs, err := db.Find[org_model.Organization](ctx, org_model.FindOrgOptions{
- UserID: user.ID,
- IncludePrivate: !onlyPublicGroups,
+ UserID: user.ID,
+ IncludeVisibility: util.Iif(onlyPublicGroups, api.VisibleTypePublic, api.VisibleTypePrivate),
})
if err != nil {
return nil, fmt.Errorf("GetUserOrgList: %w", err)
diff --git a/services/oauth2_provider/additional_scopes_test.go b/services/oauth2_provider/additional_scopes_test.go
index 2d4df7aea2..5f375346dc 100644
--- a/services/oauth2_provider/additional_scopes_test.go
+++ b/services/oauth2_provider/additional_scopes_test.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package oauth2_provider //nolint
+package oauth2_provider
import (
"testing"
diff --git a/services/oauth2_provider/init.go b/services/oauth2_provider/init.go
index e5958099a6..c412bd6433 100644
--- a/services/oauth2_provider/init.go
+++ b/services/oauth2_provider/init.go
@@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package oauth2_provider //nolint
+package oauth2_provider
import (
"context"
diff --git a/services/oauth2_provider/jwtsigningkey.go b/services/oauth2_provider/jwtsigningkey.go
index 6c668db463..03c7403f75 100644
--- a/services/oauth2_provider/jwtsigningkey.go
+++ b/services/oauth2_provider/jwtsigningkey.go
@@ -1,7 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package oauth2_provider //nolint
+package oauth2_provider
import (
"crypto/ecdsa"
@@ -31,7 +31,7 @@ type ErrInvalidAlgorithmType struct {
}
func (err ErrInvalidAlgorithmType) Error() string {
- return fmt.Sprintf("JWT signing algorithm is not supported: %s", err.Algorithm)
+ return "JWT signing algorithm is not supported: " + err.Algorithm
}
// JWTSigningKey represents a algorithm/key pair to sign JWTs
diff --git a/services/oauth2_provider/token.go b/services/oauth2_provider/token.go
index b71b11906e..935c4cc01f 100644
--- a/services/oauth2_provider/token.go
+++ b/services/oauth2_provider/token.go
@@ -1,9 +1,10 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package oauth2_provider //nolint
+package oauth2_provider
import (
+ "errors"
"fmt"
"time"
@@ -44,12 +45,12 @@ func ParseToken(jwtToken string, signingKey JWTSigningKey) (*Token, error) {
return nil, err
}
if !parsedToken.Valid {
- return nil, fmt.Errorf("invalid token")
+ return nil, errors.New("invalid token")
}
var token *Token
var ok bool
if token, ok = parsedToken.Claims.(*Token); !ok || !parsedToken.Valid {
- return nil, fmt.Errorf("invalid token")
+ return nil, errors.New("invalid token")
}
return token, nil
}
diff --git a/services/org/team.go b/services/org/team.go
index ee3bd898ea..6890dafd90 100644
--- a/services/org/team.go
+++ b/services/org/team.go
@@ -259,37 +259,6 @@ func AddTeamMember(ctx context.Context, team *organization.Team, user *user_mode
}
team.NumMembers++
-
- // Give access to team repositories.
- // update exist access if mode become bigger
- subQuery := builder.Select("repo_id").From("team_repo").
- Where(builder.Eq{"team_id": team.ID})
-
- if _, err := sess.Where("user_id=?", user.ID).
- In("repo_id", subQuery).
- And("mode < ?", team.AccessMode).
- SetExpr("mode", team.AccessMode).
- Update(new(access_model.Access)); err != nil {
- return fmt.Errorf("update user accesses: %w", err)
- }
-
- // for not exist access
- var repoIDs []int64
- accessSubQuery := builder.Select("repo_id").From("access").Where(builder.Eq{"user_id": user.ID})
- if err := sess.SQL(subQuery.And(builder.NotIn("repo_id", accessSubQuery))).Find(&repoIDs); err != nil {
- return fmt.Errorf("select id accesses: %w", err)
- }
-
- accesses := make([]*access_model.Access, 0, 100)
- for i, repoID := range repoIDs {
- accesses = append(accesses, &access_model.Access{RepoID: repoID, UserID: user.ID, Mode: team.AccessMode})
- if (i%100 == 0 || i == len(repoIDs)-1) && len(accesses) > 0 {
- if err = db.Insert(ctx, accesses); err != nil {
- return fmt.Errorf("insert new user accesses: %w", err)
- }
- accesses = accesses[:0]
- }
- }
return nil
})
if err != nil {
diff --git a/services/org/team_test.go b/services/org/team_test.go
index 3791776e46..c1a69d8ee7 100644
--- a/services/org/team_test.go
+++ b/services/org/team_test.go
@@ -88,7 +88,7 @@ func TestUpdateTeam(t *testing.T) {
assert.True(t, strings.HasPrefix(team.Description, "A long description!"))
access := unittest.AssertExistsAndLoadBean(t, &access_model.Access{UserID: 4, RepoID: 3})
- assert.EqualValues(t, perm.AccessModeAdmin, access.Mode)
+ assert.Equal(t, perm.AccessModeAdmin, access.Mode)
unittest.CheckConsistencyFor(t, &organization.Team{ID: team.ID})
}
@@ -166,24 +166,6 @@ func TestRemoveTeamMember(t *testing.T) {
assert.True(t, organization.IsErrLastOrgOwner(err))
}
-func TestRepository_RecalculateAccesses3(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
- team5 := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 5})
- user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29})
-
- has, err := db.GetEngine(db.DefaultContext).Get(&access_model.Access{UserID: user29.ID, RepoID: 23})
- assert.NoError(t, err)
- assert.False(t, has)
-
- // adding user29 to team5 should add an explicit access row for repo 23
- // even though repo 23 is public
- assert.NoError(t, AddTeamMember(db.DefaultContext, team5, user29))
-
- has, err = db.GetEngine(db.DefaultContext).Get(&access_model.Access{UserID: user29.ID, RepoID: 23})
- assert.NoError(t, err)
- assert.True(t, has)
-}
-
func TestIncludesAllRepositoriesTeams(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
@@ -222,8 +204,9 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
// Create repos.
repoIDs := make([]int64, 0)
- for i := 0; i < 3; i++ {
- r, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), repo_service.CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)})
+ for i := range 3 {
+ r, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(),
+ repo_service.CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)}, true)
assert.NoError(t, err, "CreateRepository %d", i)
if r != nil {
repoIDs = append(repoIDs, r.ID)
@@ -285,7 +268,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
}
// Create repo and check teams repositories.
- r, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), repo_service.CreateRepoOptions{Name: "repo-last"})
+ r, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), repo_service.CreateRepoOptions{Name: "repo-last"}, true)
assert.NoError(t, err, "CreateRepository last")
if r != nil {
repoIDs = append(repoIDs, r.ID)
@@ -298,7 +281,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
}
// Remove repo and check teams repositories.
- assert.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, repoIDs[0]), "DeleteRepository")
+ assert.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, repoIDs[0]), "DeleteRepository")
teamRepos[0] = repoIDs[1:]
teamRepos[1] = repoIDs[1:]
teamRepos[3] = repoIDs[1:3]
@@ -310,7 +293,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
// Wipe created items.
for i, rid := range repoIDs {
if i > 0 { // first repo already deleted.
- assert.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, rid), "DeleteRepository %d", i)
+ assert.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, rid), "DeleteRepository %d", i)
}
}
assert.NoError(t, DeleteOrganization(db.DefaultContext, org, false), "DeleteOrganization")
diff --git a/services/org/user.go b/services/org/user.go
index 0e74d006bb..26927253d2 100644
--- a/services/org/user.go
+++ b/services/org/user.go
@@ -64,10 +64,11 @@ func RemoveOrgUser(ctx context.Context, org *organization.Organization, user *us
if err != nil {
return fmt.Errorf("AccessibleReposEnv: %w", err)
}
- repoIDs, err := env.RepoIDs(1, org.NumRepos)
+ repoIDs, err := env.RepoIDs(ctx)
if err != nil {
return fmt.Errorf("GetUserRepositories [%d]: %w", user.ID, err)
}
+
for _, repoID := range repoIDs {
repo, err := repo_model.GetRepositoryByID(ctx, repoID)
if err != nil {
diff --git a/services/org/user_test.go b/services/org/user_test.go
index 96d1a1c8ca..c61d600d90 100644
--- a/services/org/user_test.go
+++ b/services/org/user_test.go
@@ -53,7 +53,7 @@ func TestRemoveOrgUser(t *testing.T) {
assert.NoError(t, RemoveOrgUser(db.DefaultContext, org, user))
unittest.AssertNotExistsBean(t, &organization.OrgUser{OrgID: org.ID, UID: user.ID})
org = unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: org.ID})
- assert.EqualValues(t, expectedNumMembers, org.NumMembers)
+ assert.Equal(t, expectedNumMembers, org.NumMembers)
}
org3 := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
diff --git a/services/packages/alpine/repository.go b/services/packages/alpine/repository.go
index 27e6391980..277c188874 100644
--- a/services/packages/alpine/repository.go
+++ b/services/packages/alpine/repository.go
@@ -290,7 +290,7 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package
privPem, _ := pem.Decode([]byte(priv))
if privPem == nil {
- return fmt.Errorf("failed to decode private key pem")
+ return errors.New("failed to decode private key pem")
}
privKey, err := x509.ParsePKCS1PrivateKey(privPem.Bytes)
diff --git a/services/packages/arch/repository.go b/services/packages/arch/repository.go
index 6731d9a1ac..438bb10837 100644
--- a/services/packages/arch/repository.go
+++ b/services/packages/arch/repository.go
@@ -13,6 +13,7 @@ import (
"fmt"
"io"
"os"
+ "strconv"
"strings"
packages_model "code.gitea.io/gitea/models/packages"
@@ -26,9 +27,9 @@ import (
"code.gitea.io/gitea/modules/util"
packages_service "code.gitea.io/gitea/services/packages"
- "github.com/keybase/go-crypto/openpgp"
- "github.com/keybase/go-crypto/openpgp/armor"
- "github.com/keybase/go-crypto/openpgp/packet"
+ "github.com/ProtonMail/go-crypto/openpgp"
+ "github.com/ProtonMail/go-crypto/openpgp/armor"
+ "github.com/ProtonMail/go-crypto/openpgp/packet"
)
const (
@@ -235,6 +236,28 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package
return packages_service.DeletePackageFile(ctx, pf)
}
+ vpfs := make(map[int64]*entryOptions)
+ for _, pf := range pfs {
+ current := &entryOptions{
+ File: pf,
+ }
+ current.Version, err = packages_model.GetVersionByID(ctx, pf.VersionID)
+ if err != nil {
+ return err
+ }
+
+ // here we compare the versions but not using SearchLatestVersions because we shouldn't allow "downgrading" to a older version by "latest" one.
+ // https://wiki.archlinux.org/title/Downgrading_packages : randomly downgrading can mess up dependencies:
+ // If a downgrade involves a soname change, all dependencies may need downgrading or rebuilding too.
+ if old, ok := vpfs[current.Version.PackageID]; ok {
+ if compareVersions(old.Version.Version, current.Version.Version) == -1 {
+ vpfs[current.Version.PackageID] = current
+ }
+ } else {
+ vpfs[current.Version.PackageID] = current
+ }
+ }
+
indexContent, _ := packages_module.NewHashedBuffer()
defer indexContent.Close()
@@ -243,15 +266,7 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package
cache := make(map[int64]*packages_model.Package)
- for _, pf := range pfs {
- opts := &entryOptions{
- File: pf,
- }
-
- opts.Version, err = packages_model.GetVersionByID(ctx, pf.VersionID)
- if err != nil {
- return err
- }
+ for _, opts := range vpfs {
if err := json.Unmarshal([]byte(opts.Version.MetadataJSON), &opts.VersionMetadata); err != nil {
return err
}
@@ -263,12 +278,12 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package
}
cache[opts.Package.ID] = opts.Package
}
- opts.Blob, err = packages_model.GetBlobByID(ctx, pf.BlobID)
+ opts.Blob, err = packages_model.GetBlobByID(ctx, opts.File.BlobID)
if err != nil {
return err
}
- sig, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeFile, pf.ID, arch_module.PropertySignature)
+ sig, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeFile, opts.File.ID, arch_module.PropertySignature)
if err != nil {
return err
}
@@ -277,7 +292,7 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package
}
opts.Signature = sig[0].Value
- meta, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeFile, pf.ID, arch_module.PropertyMetadata)
+ meta, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeFile, opts.File.ID, arch_module.PropertyMetadata)
if err != nil {
return err
}
@@ -358,8 +373,8 @@ func writeDescription(tw *tar.Writer, opts *entryOptions) error {
{"MD5SUM", opts.Blob.HashMD5},
{"SHA256SUM", opts.Blob.HashSHA256},
{"PGPSIG", opts.Signature},
- {"CSIZE", fmt.Sprintf("%d", opts.Blob.Size)},
- {"ISIZE", fmt.Sprintf("%d", opts.FileMetadata.InstalledSize)},
+ {"CSIZE", strconv.FormatInt(opts.Blob.Size, 10)},
+ {"ISIZE", strconv.FormatInt(opts.FileMetadata.InstalledSize, 10)},
{"NAME", opts.Package.Name},
{"BASE", opts.FileMetadata.Base},
{"ARCH", opts.FileMetadata.Architecture},
@@ -368,7 +383,7 @@ func writeDescription(tw *tar.Writer, opts *entryOptions) error {
{"URL", opts.VersionMetadata.ProjectURL},
{"LICENSE", strings.Join(opts.VersionMetadata.Licenses, "\n")},
{"GROUPS", strings.Join(opts.FileMetadata.Groups, "\n")},
- {"BUILDDATE", fmt.Sprintf("%d", opts.FileMetadata.BuildDate)},
+ {"BUILDDATE", strconv.FormatInt(opts.FileMetadata.BuildDate, 10)},
{"PACKAGER", opts.FileMetadata.Packager},
{"PROVIDES", strings.Join(opts.FileMetadata.Provides, "\n")},
{"REPLACES", strings.Join(opts.FileMetadata.Replaces, "\n")},
diff --git a/services/packages/arch/vercmp.go b/services/packages/arch/vercmp.go
new file mode 100644
index 0000000000..d44aa530f0
--- /dev/null
+++ b/services/packages/arch/vercmp.go
@@ -0,0 +1,108 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package arch
+
+import (
+ "strings"
+ "unicode"
+)
+
+// https://gitlab.archlinux.org/pacman/pacman/-/blob/d55b47e5512808b67bc944feb20c2bcc6c1a4c45/lib/libalpm/version.c
+
+import (
+ "strconv"
+)
+
+func parseEVR(evr string) (epoch, version, release string) {
+ if before, after, f := strings.Cut(evr, ":"); f {
+ epoch = before
+ evr = after
+ } else {
+ epoch = "0"
+ }
+
+ if before, after, f := strings.Cut(evr, "-"); f {
+ version = before
+ release = after
+ } else {
+ version = evr
+ release = "1"
+ }
+ return epoch, version, release
+}
+
+func compareSegments(a, b []string) int {
+ lenA, lenB := len(a), len(b)
+ l := min(lenA, lenB)
+ for i := range l {
+ if r := compare(a[i], b[i]); r != 0 {
+ return r
+ }
+ }
+ if lenA == lenB {
+ return 0
+ } else if l == lenA {
+ return -1
+ }
+ return 1
+}
+
+func compare(a, b string) int {
+ if a == b {
+ return 0
+ }
+
+ aNumeric := isNumeric(a)
+ bNumeric := isNumeric(b)
+
+ if aNumeric && bNumeric {
+ aInt, _ := strconv.Atoi(a)
+ bInt, _ := strconv.Atoi(b)
+ switch {
+ case aInt < bInt:
+ return -1
+ case aInt > bInt:
+ return 1
+ default:
+ return 0
+ }
+ }
+
+ if aNumeric {
+ return 1
+ }
+ if bNumeric {
+ return -1
+ }
+
+ return strings.Compare(a, b)
+}
+
+func isNumeric(s string) bool {
+ for _, c := range s {
+ if !unicode.IsDigit(c) {
+ return false
+ }
+ }
+ return true
+}
+
+func compareVersions(a, b string) int {
+ if a == b {
+ return 0
+ }
+
+ epochA, versionA, releaseA := parseEVR(a)
+ epochB, versionB, releaseB := parseEVR(b)
+
+ if res := compareSegments([]string{epochA}, []string{epochB}); res != 0 {
+ return res
+ }
+
+ if res := compareSegments(strings.Split(versionA, "."), strings.Split(versionB, ".")); res != 0 {
+ return res
+ }
+
+ return compareSegments([]string{releaseA}, []string{releaseB})
+}
diff --git a/services/packages/arch/vercmp_test.go b/services/packages/arch/vercmp_test.go
new file mode 100644
index 0000000000..2014a6d429
--- /dev/null
+++ b/services/packages/arch/vercmp_test.go
@@ -0,0 +1,27 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package arch
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestCompareVersions(t *testing.T) {
+ // https://man.archlinux.org/man/vercmp.8.en
+ checks := [][]string{
+ {"1.0a", "1.0b", "1.0beta", "1.0p", "1.0pre", "1.0rc", "1.0", "1.0.a", "1.0.1"},
+ {"1", "1.0", "1.1", "1.1.1", "1.2", "2.0", "3.0.0"},
+ }
+ for _, check := range checks {
+ for i := 0; i < len(check)-1; i++ {
+ require.Equal(t, -1, compareVersions(check[i], check[i+1]))
+ require.Equal(t, 1, compareVersions(check[i+1], check[i]))
+ }
+ }
+ require.Equal(t, 1, compareVersions("1.0-2", "1.0"))
+ require.Equal(t, 0, compareVersions("0:1.0-1", "1.0"))
+ require.Equal(t, 1, compareVersions("1:1.0-1", "2.0"))
+}
diff --git a/services/packages/auth.go b/services/packages/auth.go
index 4526a8e303..6e87643e29 100644
--- a/services/packages/auth.go
+++ b/services/packages/auth.go
@@ -4,6 +4,7 @@
package packages
import (
+ "errors"
"fmt"
"net/http"
"strings"
@@ -58,7 +59,7 @@ func ParseAuthorizationRequest(req *http.Request) (*PackageMeta, error) {
parts := strings.SplitN(h, " ", 2)
if len(parts) != 2 {
log.Error("split token failed: %s", h)
- return nil, fmt.Errorf("split token failed")
+ return nil, errors.New("split token failed")
}
return ParseAuthorizationToken(parts[1])
@@ -77,7 +78,7 @@ func ParseAuthorizationToken(tokenStr string) (*PackageMeta, error) {
c, ok := token.Claims.(*packageClaims)
if !token.Valid || !ok {
- return nil, fmt.Errorf("invalid token claim")
+ return nil, errors.New("invalid token claim")
}
return &c.PackageMeta, nil
diff --git a/services/packages/cargo/index.go b/services/packages/cargo/index.go
index e8a8313625..605335d0f1 100644
--- a/services/packages/cargo/index.go
+++ b/services/packages/cargo/index.go
@@ -11,7 +11,6 @@ import (
"io"
"path"
"strconv"
- "time"
packages_model "code.gitea.io/gitea/models/packages"
repo_model "code.gitea.io/gitea/models/repo"
@@ -79,7 +78,7 @@ func RebuildIndex(ctx context.Context, doer, owner *user_model.User) error {
"Rebuild Cargo Index",
func(t *files_service.TemporaryUploadRepository) error {
// Remove all existing content but the Cargo config
- files, err := t.LsFiles()
+ files, err := t.LsFiles(ctx)
if err != nil {
return err
}
@@ -90,7 +89,7 @@ func RebuildIndex(ctx context.Context, doer, owner *user_model.User) error {
break
}
}
- if err := t.RemoveFilesFromIndex(files...); err != nil {
+ if err := t.RemoveFilesFromIndex(ctx, files...); err != nil {
return err
}
@@ -205,7 +204,7 @@ func addOrUpdatePackageIndex(ctx context.Context, t *files_service.TemporaryUplo
return nil
}
- return writeObjectToIndex(t, BuildPackagePath(p.LowerName), b)
+ return writeObjectToIndex(ctx, t, BuildPackagePath(p.LowerName), b)
}
func getOrCreateIndexRepository(ctx context.Context, doer, owner *user_model.User) (*repo_model.Repository, error) {
@@ -214,7 +213,7 @@ func getOrCreateIndexRepository(ctx context.Context, doer, owner *user_model.Use
if errors.Is(err, util.ErrNotExist) {
repo, err = repo_service.CreateRepositoryDirectly(ctx, doer, owner, repo_service.CreateRepoOptions{
Name: IndexRepositoryName,
- })
+ }, true)
if err != nil {
return nil, fmt.Errorf("CreateRepository: %w", err)
}
@@ -248,34 +247,34 @@ func createOrUpdateConfigFile(ctx context.Context, repo *repo_model.Repository,
"Initialize Cargo Config",
func(t *files_service.TemporaryUploadRepository) error {
var b bytes.Buffer
- err := json.NewEncoder(&b).Encode(BuildConfig(owner, setting.Service.RequireSignInView || owner.Visibility != structs.VisibleTypePublic || repo.IsPrivate))
+ err := json.NewEncoder(&b).Encode(BuildConfig(owner, setting.Service.RequireSignInViewStrict || owner.Visibility != structs.VisibleTypePublic || repo.IsPrivate))
if err != nil {
return err
}
- return writeObjectToIndex(t, ConfigFileName, &b)
+ return writeObjectToIndex(ctx, t, ConfigFileName, &b)
},
)
}
// This is a shorter version of CreateOrUpdateRepoFile which allows to perform multiple actions on a git repository
func alterRepositoryContent(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, commitMessage string, fn func(*files_service.TemporaryUploadRepository) error) error {
- t, err := files_service.NewTemporaryUploadRepository(ctx, repo)
+ t, err := files_service.NewTemporaryUploadRepository(repo)
if err != nil {
return err
}
defer t.Close()
var lastCommitID string
- if err := t.Clone(repo.DefaultBranch, true); err != nil {
+ if err := t.Clone(ctx, repo.DefaultBranch, true); err != nil {
if !git.IsErrBranchNotExist(err) || !repo.IsEmpty {
return err
}
- if err := t.Init(repo.ObjectFormatName); err != nil {
+ if err := t.Init(ctx, repo.ObjectFormatName); err != nil {
return err
}
} else {
- if err := t.SetDefaultIndex(); err != nil {
+ if err := t.SetDefaultIndex(ctx); err != nil {
return err
}
@@ -291,25 +290,30 @@ func alterRepositoryContent(ctx context.Context, doer *user_model.User, repo *re
return err
}
- treeHash, err := t.WriteTree()
+ treeHash, err := t.WriteTree(ctx)
if err != nil {
return err
}
- now := time.Now()
- commitHash, err := t.CommitTreeWithDate(lastCommitID, doer, doer, treeHash, commitMessage, false, now, now)
+ commitOpts := &files_service.CommitTreeUserOptions{
+ ParentCommitID: lastCommitID,
+ TreeHash: treeHash,
+ CommitMessage: commitMessage,
+ DoerUser: doer,
+ }
+ commitHash, err := t.CommitTree(ctx, commitOpts)
if err != nil {
return err
}
- return t.Push(doer, commitHash, repo.DefaultBranch)
+ return t.Push(ctx, doer, commitHash, repo.DefaultBranch)
}
-func writeObjectToIndex(t *files_service.TemporaryUploadRepository, path string, r io.Reader) error {
- hash, err := t.HashObject(r)
+func writeObjectToIndex(ctx context.Context, t *files_service.TemporaryUploadRepository, path string, r io.Reader) error {
+ hash, err := t.HashObjectAndWrite(ctx, r)
if err != nil {
return err
}
- return t.AddObjectToIndex("100644", hash, path)
+ return t.AddObjectToIndex(ctx, "100644", hash, path)
}
diff --git a/services/packages/cleanup/cleanup.go b/services/packages/cleanup/cleanup.go
index b7ba2b6ac4..959babe7cd 100644
--- a/services/packages/cleanup/cleanup.go
+++ b/services/packages/cleanup/cleanup.go
@@ -32,127 +32,136 @@ func CleanupTask(ctx context.Context, olderThan time.Duration) error {
return CleanupExpiredData(ctx, olderThan)
}
-func ExecuteCleanupRules(outerCtx context.Context) error {
- ctx, committer, err := db.TxContext(outerCtx)
+func executeCleanupOneRulePackage(ctx context.Context, pcr *packages_model.PackageCleanupRule, p *packages_model.Package) (versionDeleted bool, err error) {
+ olderThan := time.Now().AddDate(0, 0, -pcr.RemoveDays)
+ pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
+ PackageID: p.ID,
+ IsInternal: optional.Some(false),
+ Sort: packages_model.SortCreatedDesc,
+ })
if err != nil {
- return err
+ return false, fmt.Errorf("CleanupRule [%d]: SearchVersions failed: %w", pcr.ID, err)
}
- defer committer.Close()
-
- err = packages_model.IterateEnabledCleanupRules(ctx, func(ctx context.Context, pcr *packages_model.PackageCleanupRule) error {
- select {
- case <-outerCtx.Done():
- return db.ErrCancelledf("While processing package cleanup rules")
- default:
+ if pcr.KeepCount > 0 {
+ if pcr.KeepCount < len(pvs) {
+ pvs = pvs[pcr.KeepCount:]
+ } else {
+ pvs = nil
}
-
- if err := pcr.CompiledPattern(); err != nil {
- return fmt.Errorf("CleanupRule [%d]: CompilePattern failed: %w", pcr.ID, err)
+ }
+ for _, pv := range pvs {
+ if pcr.Type == packages_model.TypeContainer {
+ if skip, err := container_service.ShouldBeSkipped(ctx, pcr, p, pv); err != nil {
+ return false, fmt.Errorf("CleanupRule [%d]: container.ShouldBeSkipped failed: %w", pcr.ID, err)
+ } else if skip {
+ log.Debug("Rule[%d]: keep '%s/%s' (container)", pcr.ID, p.Name, pv.Version)
+ continue
+ }
}
-
- olderThan := time.Now().AddDate(0, 0, -pcr.RemoveDays)
-
- packages, err := packages_model.GetPackagesByType(ctx, pcr.OwnerID, pcr.Type)
- if err != nil {
- return fmt.Errorf("CleanupRule [%d]: GetPackagesByType failed: %w", pcr.ID, err)
+ toMatch := pv.LowerVersion
+ if pcr.MatchFullName {
+ toMatch = p.LowerName + "/" + pv.LowerVersion
+ }
+ if pcr.KeepPatternMatcher != nil && pcr.KeepPatternMatcher.MatchString(toMatch) {
+ log.Debug("Rule[%d]: keep '%s/%s' (keep pattern)", pcr.ID, p.Name, pv.Version)
+ continue
+ }
+ if pv.CreatedUnix.AsLocalTime().After(olderThan) {
+ log.Debug("Rule[%d]: keep '%s/%s' (remove days) %v", pcr.ID, p.Name, pv.Version, pv.CreatedUnix.FormatDate())
+ continue
}
+ if pcr.RemovePatternMatcher != nil && !pcr.RemovePatternMatcher.MatchString(toMatch) {
+ log.Debug("Rule[%d]: keep '%s/%s' (remove pattern)", pcr.ID, p.Name, pv.Version)
+ continue
+ }
+ log.Debug("Rule[%d]: remove '%s/%s'", pcr.ID, p.Name, pv.Version)
+ if err := packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil {
+ log.Error("CleanupRule [%d]: DeletePackageVersionAndReferences failed: %v", pcr.ID, err)
+ continue
+ }
+ versionDeleted = true
+ }
+ return versionDeleted, nil
+}
- anyVersionDeleted := false
- for _, p := range packages {
- pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
- PackageID: p.ID,
- IsInternal: optional.Some(false),
- Sort: packages_model.SortCreatedDesc,
- Paginator: db.NewAbsoluteListOptions(pcr.KeepCount, 200),
- })
- if err != nil {
- return fmt.Errorf("CleanupRule [%d]: SearchVersions failed: %w", pcr.ID, err)
- }
- versionDeleted := false
- for _, pv := range pvs {
- if pcr.Type == packages_model.TypeContainer {
- if skip, err := container_service.ShouldBeSkipped(ctx, pcr, p, pv); err != nil {
- return fmt.Errorf("CleanupRule [%d]: container.ShouldBeSkipped failed: %w", pcr.ID, err)
- } else if skip {
- log.Debug("Rule[%d]: keep '%s/%s' (container)", pcr.ID, p.Name, pv.Version)
- continue
- }
- }
+func executeCleanupOneRule(ctx context.Context, pcr *packages_model.PackageCleanupRule) error {
+ if err := pcr.CompiledPattern(); err != nil {
+ return fmt.Errorf("CleanupRule [%d]: CompilePattern failed: %w", pcr.ID, err)
+ }
- toMatch := pv.LowerVersion
- if pcr.MatchFullName {
- toMatch = p.LowerName + "/" + pv.LowerVersion
- }
+ packages, err := packages_model.GetPackagesByType(ctx, pcr.OwnerID, pcr.Type)
+ if err != nil {
+ return fmt.Errorf("CleanupRule [%d]: GetPackagesByType failed: %w", pcr.ID, err)
+ }
- if pcr.KeepPatternMatcher != nil && pcr.KeepPatternMatcher.MatchString(toMatch) {
- log.Debug("Rule[%d]: keep '%s/%s' (keep pattern)", pcr.ID, p.Name, pv.Version)
- continue
- }
- if pv.CreatedUnix.AsLocalTime().After(olderThan) {
- log.Debug("Rule[%d]: keep '%s/%s' (remove days)", pcr.ID, p.Name, pv.Version)
- continue
- }
- if pcr.RemovePatternMatcher != nil && !pcr.RemovePatternMatcher.MatchString(toMatch) {
- log.Debug("Rule[%d]: keep '%s/%s' (remove pattern)", pcr.ID, p.Name, pv.Version)
- continue
+ anyVersionDeleted := false
+ for _, p := range packages {
+ versionDeleted := false
+ err = db.WithTx(ctx, func(ctx context.Context) (err error) {
+ versionDeleted, err = executeCleanupOneRulePackage(ctx, pcr, p)
+ return err
+ })
+ if err != nil {
+ log.Error("CleanupRule [%d]: executeCleanupOneRulePackage(%d) failed: %v", pcr.ID, p.ID, err)
+ continue
+ }
+ anyVersionDeleted = anyVersionDeleted || versionDeleted
+ if versionDeleted {
+ if pcr.Type == packages_model.TypeCargo {
+ owner, err := user_model.GetUserByID(ctx, pcr.OwnerID)
+ if err != nil {
+ return fmt.Errorf("GetUserByID failed: %w", err)
}
-
- log.Debug("Rule[%d]: remove '%s/%s'", pcr.ID, p.Name, pv.Version)
-
- if err := packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil {
- return fmt.Errorf("CleanupRule [%d]: DeletePackageVersionAndReferences failed: %w", pcr.ID, err)
+ if err := cargo_service.UpdatePackageIndexIfExists(ctx, owner, owner, p.ID); err != nil {
+ return fmt.Errorf("CleanupRule [%d]: cargo.UpdatePackageIndexIfExists failed: %w", pcr.ID, err)
}
+ }
+ }
+ }
- versionDeleted = true
- anyVersionDeleted = true
+ if anyVersionDeleted {
+ switch pcr.Type {
+ case packages_model.TypeDebian:
+ if err := debian_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
+ return fmt.Errorf("CleanupRule [%d]: debian.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
+ }
+ case packages_model.TypeAlpine:
+ if err := alpine_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
+ return fmt.Errorf("CleanupRule [%d]: alpine.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
}
+ case packages_model.TypeRpm:
+ if err := rpm_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
+ return fmt.Errorf("CleanupRule [%d]: rpm.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
+ }
+ case packages_model.TypeArch:
+ release, err := arch_service.AquireRegistryLock(ctx, pcr.OwnerID)
+ if err != nil {
+ return err
+ }
+ defer release()
- if versionDeleted {
- if pcr.Type == packages_model.TypeCargo {
- owner, err := user_model.GetUserByID(ctx, pcr.OwnerID)
- if err != nil {
- return fmt.Errorf("GetUserByID failed: %w", err)
- }
- if err := cargo_service.UpdatePackageIndexIfExists(ctx, owner, owner, p.ID); err != nil {
- return fmt.Errorf("CleanupRule [%d]: cargo.UpdatePackageIndexIfExists failed: %w", pcr.ID, err)
- }
- }
+ if err := arch_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
+ return fmt.Errorf("CleanupRule [%d]: arch.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
}
}
+ }
+ return nil
+}
- if anyVersionDeleted {
- switch pcr.Type {
- case packages_model.TypeDebian:
- if err := debian_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
- return fmt.Errorf("CleanupRule [%d]: debian.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
- }
- case packages_model.TypeAlpine:
- if err := alpine_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
- return fmt.Errorf("CleanupRule [%d]: alpine.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
- }
- case packages_model.TypeRpm:
- if err := rpm_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
- return fmt.Errorf("CleanupRule [%d]: rpm.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
- }
- case packages_model.TypeArch:
- release, err := arch_service.AquireRegistryLock(ctx, pcr.OwnerID)
- if err != nil {
- return err
- }
- defer release()
+func ExecuteCleanupRules(ctx context.Context) error {
+ return packages_model.IterateEnabledCleanupRules(ctx, func(ctx context.Context, pcr *packages_model.PackageCleanupRule) error {
+ select {
+ case <-ctx.Done():
+ return db.ErrCancelledf("While processing package cleanup rules")
+ default:
+ }
- if err := arch_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
- return fmt.Errorf("CleanupRule [%d]: arch.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
- }
- }
+ err := executeCleanupOneRule(ctx, pcr)
+ if err != nil {
+ log.Error("CleanupRule [%d]: executeCleanupOneRule failed: %v", pcr.ID, err)
}
return nil
})
- if err != nil {
- return err
- }
-
- return committer.Commit()
}
func CleanupExpiredData(outerCtx context.Context, olderThan time.Duration) error {
diff --git a/services/packages/container/blob_uploader.go b/services/packages/container/blob_uploader.go
index bae2e2d6af..27bc4a5421 100644
--- a/services/packages/container/blob_uploader.go
+++ b/services/packages/container/blob_uploader.go
@@ -12,7 +12,7 @@ import (
packages_model "code.gitea.io/gitea/models/packages"
packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/tempdir"
)
var (
@@ -30,8 +30,12 @@ type BlobUploader struct {
reading bool
}
-func buildFilePath(id string) string {
- return util.FilePathJoinAbs(setting.Packages.ChunkedUploadPath, id)
+func uploadPathTempDir() *tempdir.TempDir {
+ return setting.AppDataTempDir("package-upload")
+}
+
+func buildFilePath(uploadPath *tempdir.TempDir, id string) string {
+ return uploadPath.JoinPath(id)
}
// NewBlobUploader creates a new blob uploader for the given id
@@ -48,7 +52,12 @@ func NewBlobUploader(ctx context.Context, id string) (*BlobUploader, error) {
}
}
- f, err := os.OpenFile(buildFilePath(model.ID), os.O_RDWR|os.O_CREATE, 0o666)
+ uploadPath := uploadPathTempDir()
+ _, err = uploadPath.MkdirAllSub("")
+ if err != nil {
+ return nil, err
+ }
+ f, err := os.OpenFile(buildFilePath(uploadPath, model.ID), os.O_RDWR|os.O_CREATE, 0o666)
if err != nil {
return nil, err
}
@@ -118,13 +127,13 @@ func (u *BlobUploader) Read(p []byte) (int, error) {
return u.file.Read(p)
}
-// Remove deletes the data and the model of a blob upload
+// RemoveBlobUploadByID Remove deletes the data and the model of a blob upload
func RemoveBlobUploadByID(ctx context.Context, id string) error {
if err := packages_model.DeleteBlobUploadByID(ctx, id); err != nil {
return err
}
- err := os.Remove(buildFilePath(id))
+ err := os.Remove(buildFilePath(uploadPathTempDir(), id))
if err != nil && !os.IsNotExist(err) {
return err
}
diff --git a/services/packages/container/cleanup.go b/services/packages/container/cleanup.go
index 3f5f43bbc0..263562a396 100644
--- a/services/packages/container/cleanup.go
+++ b/services/packages/container/cleanup.go
@@ -13,7 +13,7 @@ import (
container_module "code.gitea.io/gitea/modules/packages/container"
packages_service "code.gitea.io/gitea/services/packages"
- digest "github.com/opencontainers/go-digest"
+ "github.com/opencontainers/go-digest"
)
// Cleanup removes expired container data
@@ -57,7 +57,7 @@ func cleanupExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) e
Type: packages_model.TypeContainer,
Version: packages_model.SearchValue{
ExactMatch: true,
- Value: container_model.UploadVersion,
+ Value: container_module.UploadVersion,
},
IsInternal: optional.Some(true),
HasFiles: optional.Some(false),
diff --git a/services/packages/container/common.go b/services/packages/container/common.go
index 5a14ed5b7a..02cbff2286 100644
--- a/services/packages/container/common.go
+++ b/services/packages/container/common.go
@@ -5,11 +5,17 @@ package container
import (
"context"
+ "io"
"strings"
packages_model "code.gitea.io/gitea/models/packages"
+ container_service "code.gitea.io/gitea/models/packages/container"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/packages"
container_module "code.gitea.io/gitea/modules/packages/container"
+
+ "github.com/opencontainers/image-spec/specs-go/v1"
)
// UpdateRepositoryNames updates the repository name property for all packages of the specific owner
@@ -22,7 +28,7 @@ func UpdateRepositoryNames(ctx context.Context, owner *user_model.User, newOwner
newOwnerName = strings.ToLower(newOwnerName)
for _, p := range ps {
- if err := packages_model.DeletePropertyByName(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository); err != nil {
+ if err := packages_model.DeletePropertiesByName(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository); err != nil {
return err
}
@@ -33,3 +39,26 @@ func UpdateRepositoryNames(ctx context.Context, owner *user_model.User, newOwner
return nil
}
+
+func ParseManifestMetadata(ctx context.Context, rd io.Reader, ownerID int64, imageName string) (*v1.Manifest, *packages_model.PackageFileDescriptor, *container_module.Metadata, error) {
+ var manifest v1.Manifest
+ if err := json.NewDecoder(rd).Decode(&manifest); err != nil {
+ return nil, nil, nil, err
+ }
+ configDescriptor, err := container_service.GetContainerBlob(ctx, &container_service.BlobSearchOptions{
+ OwnerID: ownerID,
+ Image: imageName,
+ Digest: manifest.Config.Digest.String(),
+ })
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ configReader, err := packages.NewContentStore().OpenBlob(packages.BlobHash256Key(configDescriptor.Blob.HashSHA256))
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ defer configReader.Close()
+ metadata, err := container_module.ParseImageConfig(manifest.Config.MediaType, configReader)
+ return &manifest, configDescriptor, metadata, err
+}
diff --git a/services/packages/debian/repository.go b/services/packages/debian/repository.go
index 13e98a820e..34b52b45cf 100644
--- a/services/packages/debian/repository.go
+++ b/services/packages/debian/repository.go
@@ -23,10 +23,10 @@ import (
"code.gitea.io/gitea/modules/util"
packages_service "code.gitea.io/gitea/services/packages"
- "github.com/keybase/go-crypto/openpgp"
- "github.com/keybase/go-crypto/openpgp/armor"
- "github.com/keybase/go-crypto/openpgp/clearsign"
- "github.com/keybase/go-crypto/openpgp/packet"
+ "github.com/ProtonMail/go-crypto/openpgp"
+ "github.com/ProtonMail/go-crypto/openpgp/armor"
+ "github.com/ProtonMail/go-crypto/openpgp/clearsign"
+ "github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/ulikunitz/xz"
)
diff --git a/services/packages/package_update.go b/services/packages/package_update.go
new file mode 100644
index 0000000000..4a22ee7a62
--- /dev/null
+++ b/services/packages/package_update.go
@@ -0,0 +1,79 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package packages
+
+import (
+ "context"
+ "fmt"
+
+ org_model "code.gitea.io/gitea/models/organization"
+ packages_model "code.gitea.io/gitea/models/packages"
+ access_model "code.gitea.io/gitea/models/perm/access"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/util"
+)
+
+func LinkToRepository(ctx context.Context, pkg *packages_model.Package, repo *repo_model.Repository, doer *user_model.User) error {
+ if pkg.OwnerID != repo.OwnerID {
+ return util.ErrPermissionDenied
+ }
+ if pkg.RepoID > 0 {
+ return util.ErrInvalidArgument
+ }
+
+ perms, err := access_model.GetUserRepoPermission(ctx, repo, doer)
+ if err != nil {
+ return fmt.Errorf("error getting permissions for user %d on repository %d: %w", doer.ID, repo.ID, err)
+ }
+ if !perms.CanWrite(unit.TypePackages) {
+ return util.ErrPermissionDenied
+ }
+
+ if err := packages_model.SetRepositoryLink(ctx, pkg.ID, repo.ID); err != nil {
+ return fmt.Errorf("error while linking package '%v' to repo '%v' : %w", pkg.Name, repo.FullName(), err)
+ }
+ return nil
+}
+
+func UnlinkFromRepository(ctx context.Context, pkg *packages_model.Package, doer *user_model.User) error {
+ if pkg.RepoID == 0 {
+ return util.ErrInvalidArgument
+ }
+
+ repo, err := repo_model.GetRepositoryByID(ctx, pkg.RepoID)
+ if err != nil && !repo_model.IsErrRepoNotExist(err) {
+ return fmt.Errorf("error getting repository %d: %w", pkg.RepoID, err)
+ }
+ if err == nil {
+ perms, err := access_model.GetUserRepoPermission(ctx, repo, doer)
+ if err != nil {
+ return fmt.Errorf("error getting permissions for user %d on repository %d: %w", doer.ID, repo.ID, err)
+ }
+ if !perms.CanWrite(unit.TypePackages) {
+ return util.ErrPermissionDenied
+ }
+ }
+
+ user, err := user_model.GetUserByID(ctx, pkg.OwnerID)
+ if err != nil {
+ return err
+ }
+ if !doer.IsAdmin {
+ if !user.IsOrganization() {
+ if doer.ID != pkg.OwnerID {
+ return fmt.Errorf("no permission to unlink package '%v' from its repository, or packages are disabled", pkg.Name)
+ }
+ } else {
+ isOrgAdmin, err := org_model.OrgFromUser(user).IsOrgAdmin(ctx, doer.ID)
+ if err != nil {
+ return err
+ } else if !isOrgAdmin {
+ return fmt.Errorf("no permission to unlink package '%v' from its repository, or packages are disabled", pkg.Name)
+ }
+ }
+ }
+ return packages_model.UnlinkRepository(ctx, pkg.ID)
+}
diff --git a/services/packages/packages.go b/services/packages/packages.go
index 55351afce2..517334cbc7 100644
--- a/services/packages/packages.go
+++ b/services/packages/packages.go
@@ -132,12 +132,11 @@ func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, all
}
var err error
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
- if err == packages_model.ErrDuplicatePackage {
- packageCreated = false
- } else {
+ if !errors.Is(err, packages_model.ErrDuplicatePackage) {
log.Error("Error inserting package: %v", err)
return nil, false, err
}
+ packageCreated = false
}
if packageCreated {
@@ -163,11 +162,10 @@ func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, all
MetadataJSON: string(metadataJSON),
}
if pv, err = packages_model.GetOrInsertVersion(ctx, pv); err != nil {
- if err == packages_model.ErrDuplicatePackageVersion {
+ if errors.Is(err, packages_model.ErrDuplicatePackageVersion) && allowDuplicate {
versionCreated = false
- }
- if err != packages_model.ErrDuplicatePackageVersion || !allowDuplicate {
- log.Error("Error inserting package: %v", err)
+ } else {
+ log.Error("Error inserting package: %v", err) // other error, or disallowing duplicates
return nil, false, err
}
}
@@ -433,7 +431,7 @@ func GetOrCreateInternalPackageVersion(ctx context.Context, ownerID int64, packa
}
var err error
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
- if err != packages_model.ErrDuplicatePackage {
+ if !errors.Is(err, packages_model.ErrDuplicatePackage) {
log.Error("Error inserting package: %v", err)
return err
}
@@ -565,8 +563,8 @@ func DeletePackageFile(ctx context.Context, pf *packages_model.PackageFile) erro
return packages_model.DeleteFileByID(ctx, pf.ID)
}
-// GetFileStreamByPackageNameAndVersion returns the content of the specific package file
-func GetFileStreamByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
+// OpenFileForDownloadByPackageNameAndVersion returns the content of the specific package file and increases the download counter.
+func OpenFileForDownloadByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
log.Trace("Getting package file stream: %v, %v, %s, %s, %s, %s", pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version, pfi.Filename, pfi.CompositeKey)
pv, err := packages_model.GetVersionByNameAndVersion(ctx, pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version)
@@ -578,32 +576,38 @@ func GetFileStreamByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo,
return nil, nil, nil, err
}
- return GetFileStreamByPackageVersion(ctx, pv, pfi)
+ return OpenFileForDownloadByPackageVersion(ctx, pv, pfi)
}
-// GetFileStreamByPackageVersion returns the content of the specific package file
-func GetFileStreamByPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
+// OpenFileForDownloadByPackageVersion returns the content of the specific package file and increases the download counter.
+func OpenFileForDownloadByPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, pfi.Filename, pfi.CompositeKey)
if err != nil {
return nil, nil, nil, err
}
- return GetPackageFileStream(ctx, pf)
+ return OpenFileForDownload(ctx, pf)
}
-// GetPackageFileStream returns the content of the specific package file
-func GetPackageFileStream(ctx context.Context, pf *packages_model.PackageFile) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
+// OpenFileForDownload returns the content of the specific package file and increases the download counter.
+func OpenFileForDownload(ctx context.Context, pf *packages_model.PackageFile) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
pb, err := packages_model.GetBlobByID(ctx, pf.BlobID)
if err != nil {
return nil, nil, nil, err
}
- return GetPackageBlobStream(ctx, pf, pb, nil)
+ return OpenBlobForDownload(ctx, pf, pb, nil)
+}
+
+func OpenBlobStream(pb *packages_model.PackageBlob) (io.ReadSeekCloser, error) {
+ cs := packages_module.NewContentStore()
+ key := packages_module.BlobHash256Key(pb.HashSHA256)
+ return cs.OpenBlob(key)
}
-// GetPackageBlobStream returns the content of the specific package blob
+// OpenBlobForDownload returns the content of the specific package blob and increases the download counter.
// If the storage supports direct serving and it's enabled, only the direct serving url is returned.
-func GetPackageBlobStream(ctx context.Context, pf *packages_model.PackageFile, pb *packages_model.PackageBlob, serveDirectReqParams url.Values) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
+func OpenBlobForDownload(ctx context.Context, pf *packages_model.PackageFile, pb *packages_model.PackageBlob, serveDirectReqParams url.Values) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
key := packages_module.BlobHash256Key(pb.HashSHA256)
cs := packages_module.NewContentStore()
@@ -619,7 +623,7 @@ func GetPackageBlobStream(ctx context.Context, pf *packages_model.PackageFile, p
}
}
if u == nil {
- s, err = cs.Get(key)
+ s, err = cs.OpenBlob(key)
}
if err == nil {
diff --git a/services/packages/rpm/repository.go b/services/packages/rpm/repository.go
index a7d196c15c..fbbf8d7dad 100644
--- a/services/packages/rpm/repository.go
+++ b/services/packages/rpm/repository.go
@@ -408,7 +408,6 @@ func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []
files = append(files, f)
}
}
- packageVersion := fmt.Sprintf("%s-%s", pd.FileMetadata.Version, pd.FileMetadata.Release)
packages = append(packages, &Package{
Type: "rpm",
Name: pd.Package.Name,
@@ -437,7 +436,7 @@ func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []
Archive: pd.FileMetadata.ArchiveSize,
},
Location: Location{
- Href: fmt.Sprintf("package/%s/%s/%s/%s-%s.%s.rpm", pd.Package.Name, packageVersion, pd.FileMetadata.Architecture, pd.Package.Name, packageVersion, pd.FileMetadata.Architecture),
+ Href: fmt.Sprintf("package/%s/%s/%s/%s-%s.%s.rpm", pd.Package.Name, pd.Version.Version, pd.FileMetadata.Architecture, pd.Package.Name, pd.Version.Version, pd.FileMetadata.Architecture),
},
Format: Format{
License: pd.VersionMetadata.License,
@@ -471,7 +470,7 @@ func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []
}
// https://docs.pulpproject.org/en/2.19/plugins/pulp_rpm/tech-reference/rpm.html#filelists-xml
-func buildFilelists(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, group string) (*repoData, error) { //nolint:dupl
+func buildFilelists(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, group string) (*repoData, error) { //nolint:dupl // duplicates with buildOther
type Version struct {
Epoch string `xml:"epoch,attr"`
Version string `xml:"ver,attr"`
@@ -518,7 +517,7 @@ func buildFilelists(ctx context.Context, pv *packages_model.PackageVersion, pfs
}
// https://docs.pulpproject.org/en/2.19/plugins/pulp_rpm/tech-reference/rpm.html#other-xml
-func buildOther(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, group string) (*repoData, error) { //nolint:dupl
+func buildOther(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, group string) (*repoData, error) { //nolint:dupl // duplicates with buildFilelists
type Version struct {
Epoch string `xml:"epoch,attr"`
Version string `xml:"ver,attr"`
diff --git a/services/projects/issue.go b/services/projects/issue.go
index db1621a39f..590fe960d5 100644
--- a/services/projects/issue.go
+++ b/services/projects/issue.go
@@ -5,12 +5,13 @@ package project
import (
"context"
- "fmt"
+ "errors"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
project_model "code.gitea.io/gitea/models/project"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/optional"
)
// MoveIssuesOnProjectColumn moves or keeps issues in a column and sorts them inside that column
@@ -28,7 +29,7 @@ func MoveIssuesOnProjectColumn(ctx context.Context, doer *user_model.User, colum
return err
}
if int(count) != len(sortedIssueIDs) {
- return fmt.Errorf("all issues have to be added to a project first")
+ return errors.New("all issues have to be added to a project first")
}
issues, err := issues_model.GetIssuesByIDs(ctx, issueIDs)
@@ -55,25 +56,152 @@ func MoveIssuesOnProjectColumn(ctx context.Context, doer *user_model.User, colum
continue
}
- _, err = db.Exec(ctx, "UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?", column.ID, sorting, issueID)
+ projectColumnID, err := curIssue.ProjectColumnID(ctx)
if err != nil {
return err
}
- // add timeline to issue
- if _, err := issues_model.CreateComment(ctx, &issues_model.CreateCommentOptions{
- Type: issues_model.CommentTypeProjectColumn,
- Doer: doer,
- Repo: curIssue.Repo,
- Issue: curIssue,
- ProjectID: column.ProjectID,
- ProjectTitle: project.Title,
- ProjectColumnID: column.ID,
- ProjectColumnTitle: column.Title,
- }); err != nil {
+ if projectColumnID != column.ID {
+ // add timeline to issue
+ if _, err := issues_model.CreateComment(ctx, &issues_model.CreateCommentOptions{
+ Type: issues_model.CommentTypeProjectColumn,
+ Doer: doer,
+ Repo: curIssue.Repo,
+ Issue: curIssue,
+ ProjectID: column.ProjectID,
+ ProjectTitle: project.Title,
+ ProjectColumnID: column.ID,
+ ProjectColumnTitle: column.Title,
+ }); err != nil {
+ return err
+ }
+ }
+
+ _, err = db.Exec(ctx, "UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?", column.ID, sorting, issueID)
+ if err != nil {
return err
}
}
return nil
})
}
+
+// LoadIssuesFromProject load issues assigned to each project column inside the given project
+func LoadIssuesFromProject(ctx context.Context, project *project_model.Project, opts *issues_model.IssuesOptions) (map[int64]issues_model.IssueList, error) {
+ issueList, err := issues_model.Issues(ctx, opts.Copy(func(o *issues_model.IssuesOptions) {
+ o.ProjectID = project.ID
+ o.SortType = "project-column-sorting"
+ }))
+ if err != nil {
+ return nil, err
+ }
+
+ if err := issueList.LoadComments(ctx); err != nil {
+ return nil, err
+ }
+
+ defaultColumn, err := project.MustDefaultColumn(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ issueColumnMap, err := issues_model.LoadProjectIssueColumnMap(ctx, project.ID, defaultColumn.ID)
+ if err != nil {
+ return nil, err
+ }
+
+ results := make(map[int64]issues_model.IssueList)
+ for _, issue := range issueList {
+ projectColumnID, ok := issueColumnMap[issue.ID]
+ if !ok {
+ continue
+ }
+ if _, ok := results[projectColumnID]; !ok {
+ results[projectColumnID] = make(issues_model.IssueList, 0)
+ }
+ results[projectColumnID] = append(results[projectColumnID], issue)
+ }
+ return results, nil
+}
+
+// NumClosedIssues return counter of closed issues assigned to a project
+func loadNumClosedIssues(ctx context.Context, p *project_model.Project) error {
+ cnt, err := db.GetEngine(ctx).Table("project_issue").
+ Join("INNER", "issue", "project_issue.issue_id=issue.id").
+ Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, true).
+ Cols("issue_id").
+ Count()
+ if err != nil {
+ return err
+ }
+ p.NumClosedIssues = cnt
+ return nil
+}
+
+// NumOpenIssues return counter of open issues assigned to a project
+func loadNumOpenIssues(ctx context.Context, p *project_model.Project) error {
+ cnt, err := db.GetEngine(ctx).Table("project_issue").
+ Join("INNER", "issue", "project_issue.issue_id=issue.id").
+ Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, false).
+ Cols("issue_id").
+ Count()
+ if err != nil {
+ return err
+ }
+ p.NumOpenIssues = cnt
+ return nil
+}
+
+func LoadIssueNumbersForProjects(ctx context.Context, projects []*project_model.Project, doer *user_model.User) error {
+ for _, project := range projects {
+ if err := LoadIssueNumbersForProject(ctx, project, doer); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func LoadIssueNumbersForProject(ctx context.Context, project *project_model.Project, doer *user_model.User) error {
+ // for repository project, just get the numbers
+ if project.OwnerID == 0 {
+ if err := loadNumClosedIssues(ctx, project); err != nil {
+ return err
+ }
+ if err := loadNumOpenIssues(ctx, project); err != nil {
+ return err
+ }
+ project.NumIssues = project.NumClosedIssues + project.NumOpenIssues
+ return nil
+ }
+
+ if err := project.LoadOwner(ctx); err != nil {
+ return err
+ }
+
+ // for user or org projects, we need to check access permissions
+ opts := issues_model.IssuesOptions{
+ ProjectID: project.ID,
+ Doer: doer,
+ AllPublic: doer == nil,
+ Owner: project.Owner,
+ }
+
+ var err error
+ project.NumOpenIssues, err = issues_model.CountIssues(ctx, opts.Copy(func(o *issues_model.IssuesOptions) {
+ o.IsClosed = optional.Some(false)
+ }))
+ if err != nil {
+ return err
+ }
+
+ project.NumClosedIssues, err = issues_model.CountIssues(ctx, opts.Copy(func(o *issues_model.IssuesOptions) {
+ o.IsClosed = optional.Some(true)
+ }))
+ if err != nil {
+ return err
+ }
+
+ project.NumIssues = project.NumClosedIssues + project.NumOpenIssues
+
+ return nil
+}
diff --git a/services/projects/issue_test.go b/services/projects/issue_test.go
new file mode 100644
index 0000000000..e76d31e757
--- /dev/null
+++ b/services/projects/issue_test.go
@@ -0,0 +1,210 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package project
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ issues_model "code.gitea.io/gitea/models/issues"
+ org_model "code.gitea.io/gitea/models/organization"
+ project_model "code.gitea.io/gitea/models/project"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_Projects(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ userAdmin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ org3 := unittest.AssertExistsAndLoadBean(t, &org_model.Organization{ID: 3})
+ user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
+
+ t.Run("User projects", func(t *testing.T) {
+ pi1 := project_model.ProjectIssue{
+ ProjectID: 4,
+ IssueID: 1,
+ ProjectColumnID: 4,
+ }
+ err := db.Insert(db.DefaultContext, &pi1)
+ assert.NoError(t, err)
+ defer func() {
+ _, err = db.DeleteByID[project_model.ProjectIssue](db.DefaultContext, pi1.ID)
+ assert.NoError(t, err)
+ }()
+
+ pi2 := project_model.ProjectIssue{
+ ProjectID: 4,
+ IssueID: 4,
+ ProjectColumnID: 4,
+ }
+ err = db.Insert(db.DefaultContext, &pi2)
+ assert.NoError(t, err)
+ defer func() {
+ _, err = db.DeleteByID[project_model.ProjectIssue](db.DefaultContext, pi2.ID)
+ assert.NoError(t, err)
+ }()
+
+ projects, err := db.Find[project_model.Project](db.DefaultContext, project_model.SearchOptions{
+ OwnerID: user2.ID,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, projects, 3)
+ assert.EqualValues(t, 4, projects[0].ID)
+
+ t.Run("Authenticated user", func(t *testing.T) {
+ columnIssues, err := LoadIssuesFromProject(db.DefaultContext, projects[0], &issues_model.IssuesOptions{
+ Owner: user2,
+ Doer: user2,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, columnIssues, 1) // 4 has 2 issues, 6 will not contains here because 0 issues
+ assert.Len(t, columnIssues[4], 2) // user2 can visit both issues, one from public repository one from private repository
+ })
+
+ t.Run("Anonymous user", func(t *testing.T) {
+ columnIssues, err := LoadIssuesFromProject(db.DefaultContext, projects[0], &issues_model.IssuesOptions{
+ AllPublic: true,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, columnIssues, 1)
+ assert.Len(t, columnIssues[4], 1) // anonymous user can only visit public repo issues
+ })
+
+ t.Run("Authenticated user with no permission to the private repo", func(t *testing.T) {
+ columnIssues, err := LoadIssuesFromProject(db.DefaultContext, projects[0], &issues_model.IssuesOptions{
+ Owner: user2,
+ Doer: user4,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, columnIssues, 1)
+ assert.Len(t, columnIssues[4], 1) // user4 can only visit public repo issues
+ })
+ })
+
+ t.Run("Org projects", func(t *testing.T) {
+ project1 := project_model.Project{
+ Title: "project in an org",
+ OwnerID: org3.ID,
+ Type: project_model.TypeOrganization,
+ TemplateType: project_model.TemplateTypeBasicKanban,
+ }
+ err := project_model.NewProject(db.DefaultContext, &project1)
+ assert.NoError(t, err)
+ defer func() {
+ err := project_model.DeleteProjectByID(db.DefaultContext, project1.ID)
+ assert.NoError(t, err)
+ }()
+
+ column1 := project_model.Column{
+ Title: "column 1",
+ ProjectID: project1.ID,
+ }
+ err = project_model.NewColumn(db.DefaultContext, &column1)
+ assert.NoError(t, err)
+
+ column2 := project_model.Column{
+ Title: "column 2",
+ ProjectID: project1.ID,
+ }
+ err = project_model.NewColumn(db.DefaultContext, &column2)
+ assert.NoError(t, err)
+
+ // issue 6 belongs to private repo 3 under org 3
+ issue6 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 6})
+ err = issues_model.IssueAssignOrRemoveProject(db.DefaultContext, issue6, user2, project1.ID, column1.ID)
+ assert.NoError(t, err)
+
+ // issue 16 belongs to public repo 16 under org 3
+ issue16 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 16})
+ err = issues_model.IssueAssignOrRemoveProject(db.DefaultContext, issue16, user2, project1.ID, column1.ID)
+ assert.NoError(t, err)
+
+ projects, err := db.Find[project_model.Project](db.DefaultContext, project_model.SearchOptions{
+ OwnerID: org3.ID,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, projects, 1)
+ assert.Equal(t, project1.ID, projects[0].ID)
+
+ t.Run("Authenticated user", func(t *testing.T) {
+ columnIssues, err := LoadIssuesFromProject(db.DefaultContext, projects[0], &issues_model.IssuesOptions{
+ Owner: org3.AsUser(),
+ Doer: userAdmin,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, columnIssues, 1) // column1 has 2 issues, 6 will not contains here because 0 issues
+ assert.Len(t, columnIssues[column1.ID], 2) // user2 can visit both issues, one from public repository one from private repository
+ })
+
+ t.Run("Anonymous user", func(t *testing.T) {
+ columnIssues, err := LoadIssuesFromProject(db.DefaultContext, projects[0], &issues_model.IssuesOptions{
+ AllPublic: true,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, columnIssues, 1)
+ assert.Len(t, columnIssues[column1.ID], 1) // anonymous user can only visit public repo issues
+ })
+
+ t.Run("Authenticated user with no permission to the private repo", func(t *testing.T) {
+ columnIssues, err := LoadIssuesFromProject(db.DefaultContext, projects[0], &issues_model.IssuesOptions{
+ Owner: org3.AsUser(),
+ Doer: user2,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, columnIssues, 1)
+ assert.Len(t, columnIssues[column1.ID], 1) // user4 can only visit public repo issues
+ })
+ })
+
+ t.Run("Repository projects", func(t *testing.T) {
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+
+ projects, err := db.Find[project_model.Project](db.DefaultContext, project_model.SearchOptions{
+ RepoID: repo1.ID,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, projects, 1)
+ assert.EqualValues(t, 1, projects[0].ID)
+
+ t.Run("Authenticated user", func(t *testing.T) {
+ columnIssues, err := LoadIssuesFromProject(db.DefaultContext, projects[0], &issues_model.IssuesOptions{
+ RepoIDs: []int64{repo1.ID},
+ Doer: userAdmin,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, columnIssues, 3)
+ assert.Len(t, columnIssues[1], 2)
+ assert.Len(t, columnIssues[2], 1)
+ assert.Len(t, columnIssues[3], 1)
+ })
+
+ t.Run("Anonymous user", func(t *testing.T) {
+ columnIssues, err := LoadIssuesFromProject(db.DefaultContext, projects[0], &issues_model.IssuesOptions{
+ AllPublic: true,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, columnIssues, 3)
+ assert.Len(t, columnIssues[1], 2)
+ assert.Len(t, columnIssues[2], 1)
+ assert.Len(t, columnIssues[3], 1)
+ })
+
+ t.Run("Authenticated user with no permission to the private repo", func(t *testing.T) {
+ columnIssues, err := LoadIssuesFromProject(db.DefaultContext, projects[0], &issues_model.IssuesOptions{
+ RepoIDs: []int64{repo1.ID},
+ Doer: user2,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, columnIssues, 3)
+ assert.Len(t, columnIssues[1], 2)
+ assert.Len(t, columnIssues[2], 1)
+ assert.Len(t, columnIssues[3], 1)
+ })
+ })
+}
diff --git a/services/projects/main_test.go b/services/projects/main_test.go
new file mode 100644
index 0000000000..d39c82a140
--- /dev/null
+++ b/services/projects/main_test.go
@@ -0,0 +1,17 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package project
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+
+ _ "code.gitea.io/gitea/models/actions"
+ _ "code.gitea.io/gitea/models/activities"
+)
+
+func TestMain(m *testing.M) {
+ unittest.MainTest(m)
+}
diff --git a/services/pull/check.go b/services/pull/check.go
index f7aa8eb369..5d8990aa00 100644
--- a/services/pull/check.go
+++ b/services/pull/check.go
@@ -10,6 +10,7 @@ import (
"fmt"
"strconv"
"strings"
+ "time"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
@@ -25,6 +26,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/queue"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
asymkey_service "code.gitea.io/gitea/services/asymkey"
notify_service "code.gitea.io/gitea/services/notify"
@@ -34,27 +36,88 @@ import (
var prPatchCheckerQueue *queue.WorkerPoolQueue[string]
var (
- ErrIsClosed = errors.New("pull is closed")
- ErrUserNotAllowedToMerge = ErrDisallowedToMerge{}
- ErrHasMerged = errors.New("has already been merged")
- ErrIsWorkInProgress = errors.New("work in progress PRs cannot be merged")
- ErrIsChecking = errors.New("cannot merge while conflict checking is in progress")
- ErrNotMergeableState = errors.New("not in mergeable state")
- ErrDependenciesLeft = errors.New("is blocked by an open dependency")
+ ErrIsClosed = errors.New("pull is closed")
+ ErrNoPermissionToMerge = errors.New("no permission to merge")
+ ErrNotReadyToMerge = errors.New("not ready to merge")
+ ErrHasMerged = errors.New("has already been merged")
+ ErrIsWorkInProgress = errors.New("work in progress PRs cannot be merged")
+ ErrIsChecking = errors.New("cannot merge while conflict checking is in progress")
+ ErrNotMergeableState = errors.New("not in mergeable state")
+ ErrDependenciesLeft = errors.New("is blocked by an open dependency")
)
-// AddToTaskQueue adds itself to pull request test task queue.
-func AddToTaskQueue(ctx context.Context, pr *issues_model.PullRequest) {
+func markPullRequestStatusAsChecking(ctx context.Context, pr *issues_model.PullRequest) bool {
pr.Status = issues_model.PullRequestStatusChecking
err := pr.UpdateColsIfNotMerged(ctx, "status")
if err != nil {
- log.Error("AddToTaskQueue(%-v).UpdateCols.(add to queue): %v", pr, err)
+ log.Error("UpdateColsIfNotMerged failed, pr: %-v, err: %v", pr, err)
+ return false
+ }
+ pr, err = issues_model.GetPullRequestByID(ctx, pr.ID)
+ if err != nil {
+ log.Error("GetPullRequestByID failed, pr: %-v, err: %v", pr, err)
+ return false
+ }
+ return pr.Status == issues_model.PullRequestStatusChecking
+}
+
+var AddPullRequestToCheckQueue = realAddPullRequestToCheckQueue
+
+func realAddPullRequestToCheckQueue(prID int64) {
+ err := prPatchCheckerQueue.Push(strconv.FormatInt(prID, 10))
+ if err != nil && !errors.Is(err, queue.ErrAlreadyInQueue) {
+ log.Error("Error adding %v to the pull requests check queue: %v", prID, err)
+ }
+}
+
+func StartPullRequestCheckImmediately(ctx context.Context, pr *issues_model.PullRequest) {
+ if !markPullRequestStatusAsChecking(ctx, pr) {
return
}
- log.Trace("Adding %-v to the test pull requests queue", pr)
- err = prPatchCheckerQueue.Push(strconv.FormatInt(pr.ID, 10))
- if err != nil && err != queue.ErrAlreadyInQueue {
- log.Error("Error adding %-v to the test pull requests queue: %v", pr, err)
+ AddPullRequestToCheckQueue(pr.ID)
+}
+
+// StartPullRequestCheckDelayable will delay the check if the pull request was not updated recently.
+// When the "base" branch gets updated, all PRs targeting that "base" branch need to re-check whether
+// they are mergeable.
+// When there are too many stale PRs, each "base" branch update will consume a lot of system resources.
+// So we can delay the checks for PRs that were not updated recently, only mark their status as
+// "checking", and then next time when these PRs are updated or viewed, the real checks will run.
+func StartPullRequestCheckDelayable(ctx context.Context, pr *issues_model.PullRequest) {
+ if !markPullRequestStatusAsChecking(ctx, pr) {
+ return
+ }
+
+ if setting.Repository.PullRequest.DelayCheckForInactiveDays >= 0 {
+ if err := pr.LoadIssue(ctx); err != nil {
+ return
+ }
+ duration := 24 * time.Hour * time.Duration(setting.Repository.PullRequest.DelayCheckForInactiveDays)
+ if pr.Issue.UpdatedUnix.AddDuration(duration) <= timeutil.TimeStampNow() {
+ return
+ }
+ }
+
+ AddPullRequestToCheckQueue(pr.ID)
+}
+
+func StartPullRequestCheckOnView(ctx context.Context, pr *issues_model.PullRequest) {
+ // TODO: its correctness totally depends on the "unique queue" feature and the global lock.
+ // So duplicate "start" requests will be ignored if there is already a task in the queue or one is running.
+ // Ideally in the future we should decouple the "unique queue" feature from the "start" request.
+ if pr.Status == issues_model.PullRequestStatusChecking {
+ if setting.IsInTesting {
+ // In testing mode, there might be an "immediate" queue, which is not a real queue, everything is executed in the same goroutine
+ // So we can't use the global lock here, otherwise it will cause a deadlock.
+ AddPullRequestToCheckQueue(pr.ID)
+ } else {
+ // When a PR check starts, the task is popped from the queue and the task handler acquires the global lock
+ // So we need to acquire the global lock here to prevent from duplicate tasks
+ _, _ = globallock.TryLockAndDo(ctx, getPullWorkingLockKey(pr.ID), func(ctx context.Context) error {
+ AddPullRequestToCheckQueue(pr.ID) // the queue is a unique queue and won't add the same task again
+ return nil
+ })
+ }
}
}
@@ -84,7 +147,7 @@ func CheckPullMergeable(stdCtx context.Context, doer *user_model.User, perm *acc
log.Error("Error whilst checking if %-v is allowed to merge %-v: %v", doer, pr, err)
return err
} else if !allowedMerge {
- return ErrUserNotAllowedToMerge
+ return ErrNoPermissionToMerge
}
if mergeCheckType == MergeCheckTypeManually {
@@ -105,7 +168,7 @@ func CheckPullMergeable(stdCtx context.Context, doer *user_model.User, perm *acc
}
if err := CheckPullBranchProtections(ctx, pr, false); err != nil {
- if !IsErrDisallowedToMerge(err) {
+ if !errors.Is(err, ErrNotReadyToMerge) {
log.Error("Error whilst checking pull branch protection for %-v: %v", pr, err)
return err
}
@@ -172,10 +235,10 @@ func isSignedIfRequired(ctx context.Context, pr *issues_model.PullRequest, doer
return sign, err
}
-// checkAndUpdateStatus checks if pull request is possible to leaving checking status,
+// markPullRequestAsMergeable checks if pull request is possible to leaving checking status,
// and set to be either conflict or mergeable.
-func checkAndUpdateStatus(ctx context.Context, pr *issues_model.PullRequest) {
- // If status has not been changed to conflict by testPatch then we are mergeable
+func markPullRequestAsMergeable(ctx context.Context, pr *issues_model.PullRequest) {
+ // If status has not been changed to conflict by testPullRequestTmpRepoBranchMergeable then we are mergeable
if pr.Status == issues_model.PullRequestStatusChecking {
pr.Status = issues_model.PullRequestStatusMergeable
}
@@ -206,9 +269,9 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com
prHeadRef := pr.GetGitRefName()
// Check if the pull request is merged into BaseBranch
- if _, _, err := git.NewCommand(ctx, "merge-base", "--is-ancestor").
+ if _, _, err := git.NewCommand("merge-base", "--is-ancestor").
AddDynamicArguments(prHeadRef, pr.BaseBranch).
- RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath()}); err != nil {
+ RunStdString(ctx, &git.RunOpts{Dir: pr.BaseRepo.RepoPath()}); err != nil {
if strings.Contains(err.Error(), "exit status 1") {
// prHeadRef is not an ancestor of the base branch
return nil, nil
@@ -234,9 +297,9 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com
objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)
// Get the commit from BaseBranch where the pull request got merged
- mergeCommit, _, err := git.NewCommand(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse").
- AddDynamicArguments(prHeadCommitID + ".." + pr.BaseBranch).
- RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath()})
+ mergeCommit, _, err := git.NewCommand("rev-list", "--ancestry-path", "--merges", "--reverse").
+ AddDynamicArguments(prHeadCommitID+".."+pr.BaseBranch).
+ RunStdString(ctx, &git.RunOpts{Dir: pr.BaseRepo.RepoPath()})
if err != nil {
return nil, fmt.Errorf("git rev-list --ancestry-path --merges --reverse: %w", err)
} else if len(mergeCommit) < objectFormat.FullLength() {
@@ -282,9 +345,6 @@ func manuallyMerged(ctx context.Context, pr *issues_model.PullRequest) bool {
return false
}
- pr.MergedCommitID = commit.ID.String()
- pr.MergedUnix = timeutil.TimeStamp(commit.Author.When.Unix())
- pr.Status = issues_model.PullRequestStatusManuallyMerged
merger, _ := user_model.GetUserByEmail(ctx, commit.Author.Email)
// When the commit author is unknown set the BaseRepo owner as merger
@@ -297,10 +357,8 @@ func manuallyMerged(ctx context.Context, pr *issues_model.PullRequest) bool {
}
merger = pr.BaseRepo.Owner
}
- pr.Merger = merger
- pr.MergerID = merger.ID
- if merged, err := SetMerged(ctx, pr); err != nil {
+ if merged, err := SetMerged(ctx, pr, commit.ID.String(), timeutil.TimeStamp(commit.Author.When.Unix()), merger, issues_model.PullRequestStatusManuallyMerged); err != nil {
log.Error("%-v setMerged : %v", pr, err)
return false
} else if !merged {
@@ -315,6 +373,10 @@ func manuallyMerged(ctx context.Context, pr *issues_model.PullRequest) bool {
// InitializePullRequests checks and tests untested patches of pull requests.
func InitializePullRequests(ctx context.Context) {
+ // If we prefer to delay the checks, then no need to do any check during startup, there should be not much difference
+ if setting.Repository.PullRequest.DelayCheckForInactiveDays >= 0 {
+ return
+ }
prs, err := issues_model.GetPullRequestIDsByCheckStatus(ctx, issues_model.PullRequestStatusChecking)
if err != nil {
log.Error("Find Checking PRs: %v", err)
@@ -325,24 +387,12 @@ func InitializePullRequests(ctx context.Context) {
case <-ctx.Done():
return
default:
- log.Trace("Adding PR[%d] to the pull requests patch checking queue", prID)
- if err := prPatchCheckerQueue.Push(strconv.FormatInt(prID, 10)); err != nil {
- log.Error("Error adding PR[%d] to the pull requests patch checking queue %v", prID, err)
- }
+ AddPullRequestToCheckQueue(prID)
}
}
}
-// handle passed PR IDs and test the PRs
-func handler(items ...string) []string {
- for _, s := range items {
- id, _ := strconv.ParseInt(s, 10, 64)
- testPR(id)
- }
- return nil
-}
-
-func testPR(id int64) {
+func checkPullRequestMergeable(id int64) {
ctx := graceful.GetManager().HammerContext()
releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(id))
if err != nil {
@@ -356,7 +406,7 @@ func testPR(id int64) {
pr, err := issues_model.GetPullRequestByID(ctx, id)
if err != nil {
- log.Error("Unable to GetPullRequestByID[%d] for testPR: %v", id, err)
+ log.Error("Unable to GetPullRequestByID[%d] for checkPullRequestMergeable: %v", id, err)
return
}
@@ -375,15 +425,15 @@ func testPR(id int64) {
return
}
- if err := TestPatch(pr); err != nil {
- log.Error("testPatch[%-v]: %v", pr, err)
+ if err := testPullRequestBranchMergeable(pr); err != nil {
+ log.Error("testPullRequestTmpRepoBranchMergeable[%-v]: %v", pr, err)
pr.Status = issues_model.PullRequestStatusError
if err := pr.UpdateCols(ctx, "status"); err != nil {
log.Error("update pr [%-v] status to PullRequestStatusError failed: %v", pr, err)
}
return
}
- checkAndUpdateStatus(ctx, pr)
+ markPullRequestAsMergeable(ctx, pr)
}
// CheckPRsForBaseBranch check all pulls with baseBrannch
@@ -392,20 +442,24 @@ func CheckPRsForBaseBranch(ctx context.Context, baseRepo *repo_model.Repository,
if err != nil {
return err
}
-
for _, pr := range prs {
- AddToTaskQueue(ctx, pr)
+ StartPullRequestCheckImmediately(ctx, pr)
}
-
return nil
}
// Init runs the task queue to test all the checking status pull requests
func Init() error {
- prPatchCheckerQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "pr_patch_checker", handler)
+ prPatchCheckerQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "pr_patch_checker", func(items ...string) []string {
+ for _, s := range items {
+ id, _ := strconv.ParseInt(s, 10, 64)
+ checkPullRequestMergeable(id)
+ }
+ return nil
+ })
if prPatchCheckerQueue == nil {
- return fmt.Errorf("unable to create pr_patch_checker queue")
+ return errors.New("unable to create pr_patch_checker queue")
}
go graceful.GetManager().RunWithCancel(prPatchCheckerQueue)
diff --git a/services/pull/check_test.go b/services/pull/check_test.go
index dcf5f7b93a..fa3a676ef1 100644
--- a/services/pull/check_test.go
+++ b/services/pull/check_test.go
@@ -5,7 +5,6 @@
package pull
import (
- "context"
"strconv"
"testing"
"time"
@@ -33,11 +32,11 @@ func TestPullRequest_AddToTaskQueue(t *testing.T) {
cfg, err := setting.GetQueueSettings(setting.CfgProvider, "pr_patch_checker")
assert.NoError(t, err)
- prPatchCheckerQueue, err = queue.NewWorkerPoolQueueWithContext(context.Background(), "pr_patch_checker", cfg, testHandler, true)
+ prPatchCheckerQueue, err = queue.NewWorkerPoolQueueWithContext(t.Context(), "pr_patch_checker", cfg, testHandler, true)
assert.NoError(t, err)
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
- AddToTaskQueue(db.DefaultContext, pr)
+ StartPullRequestCheckImmediately(db.DefaultContext, pr)
assert.Eventually(t, func() bool {
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
@@ -52,7 +51,7 @@ func TestPullRequest_AddToTaskQueue(t *testing.T) {
select {
case id := <-idChan:
- assert.EqualValues(t, pr.ID, id)
+ assert.Equal(t, pr.ID, id)
case <-time.After(time.Second):
assert.FailNow(t, "Timeout: nothing was added to pullRequestQueue")
}
diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go
index aa1ad7cd66..7952ca6fe3 100644
--- a/services/pull/commit_status.go
+++ b/services/pull/commit_status.go
@@ -10,94 +10,59 @@ import (
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/commitstatus"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/structs"
"github.com/gobwas/glob"
"github.com/pkg/errors"
)
// MergeRequiredContextsCommitStatus returns a commit status state for given required contexts
-func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, requiredContexts []string) structs.CommitStatusState {
- // matchedCount is the number of `CommitStatus.Context` that match any context of `requiredContexts`
- matchedCount := 0
- returnedStatus := structs.CommitStatusSuccess
-
- if len(requiredContexts) > 0 {
- requiredContextsGlob := make(map[string]glob.Glob, len(requiredContexts))
- for _, ctx := range requiredContexts {
- if gp, err := glob.Compile(ctx); err != nil {
- log.Error("glob.Compile %s failed. Error: %v", ctx, err)
- } else {
- requiredContextsGlob[ctx] = gp
- }
- }
-
- for _, gp := range requiredContextsGlob {
- var targetStatus structs.CommitStatusState
- for _, commitStatus := range commitStatuses {
- if gp.Match(commitStatus.Context) {
- targetStatus = commitStatus.State
- matchedCount++
- break
- }
- }
-
- // If required rule not match any action, then it is pending
- if targetStatus == "" {
- if structs.CommitStatusPending.NoBetterThan(returnedStatus) {
- returnedStatus = structs.CommitStatusPending
- }
- break
- }
-
- if targetStatus.NoBetterThan(returnedStatus) {
- returnedStatus = targetStatus
- }
- }
+func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, requiredContexts []string) commitstatus.CommitStatusState {
+ if len(commitStatuses) == 0 {
+ return commitstatus.CommitStatusPending
}
- if matchedCount == 0 && returnedStatus == structs.CommitStatusSuccess {
- status := git_model.CalcCommitStatus(commitStatuses)
- if status != nil {
- return status.State
- }
- return structs.CommitStatusSuccess
+ if len(requiredContexts) == 0 {
+ return git_model.CalcCommitStatus(commitStatuses).State
}
- return returnedStatus
-}
-
-// IsCommitStatusContextSuccess returns true if all required status check contexts succeed.
-func IsCommitStatusContextSuccess(commitStatuses []*git_model.CommitStatus, requiredContexts []string) bool {
- // If no specific context is required, require that last commit status is a success
- if len(requiredContexts) == 0 {
- status := git_model.CalcCommitStatus(commitStatuses)
- if status == nil || status.State != structs.CommitStatusSuccess {
- return false
+ requiredContextsGlob := make(map[string]glob.Glob, len(requiredContexts))
+ for _, ctx := range requiredContexts {
+ if gp, err := glob.Compile(ctx); err != nil {
+ log.Error("glob.Compile %s failed. Error: %v", ctx, err)
+ } else {
+ requiredContextsGlob[ctx] = gp
}
- return true
}
- for _, ctx := range requiredContexts {
- var found bool
+ requiredCommitStatuses := make([]*git_model.CommitStatus, 0, len(commitStatuses))
+ allRequiredContextsMatched := true
+ for _, gp := range requiredContextsGlob {
+ requiredContextMatched := false
for _, commitStatus := range commitStatuses {
- if commitStatus.Context == ctx {
- if commitStatus.State != structs.CommitStatusSuccess {
- return false
- }
-
- found = true
- break
+ if gp.Match(commitStatus.Context) {
+ requiredCommitStatuses = append(requiredCommitStatuses, commitStatus)
+ requiredContextMatched = true
}
}
- if !found {
- return false
- }
+ allRequiredContextsMatched = allRequiredContextsMatched && requiredContextMatched
+ }
+ if len(requiredCommitStatuses) == 0 {
+ return commitstatus.CommitStatusPending
+ }
+
+ returnedStatus := git_model.CalcCommitStatus(requiredCommitStatuses).State
+ if allRequiredContextsMatched {
+ return returnedStatus
+ }
+
+ if returnedStatus == commitstatus.CommitStatusFailure {
+ return commitstatus.CommitStatusFailure
}
- return true
+ // even if part of success, return pending
+ return commitstatus.CommitStatusPending
}
// IsPullCommitStatusPass returns if all required status checks PASS
@@ -118,7 +83,7 @@ func IsPullCommitStatusPass(ctx context.Context, pr *issues_model.PullRequest) (
}
// GetPullRequestCommitStatusState returns pull request merged commit status state
-func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullRequest) (structs.CommitStatusState, error) {
+func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullRequest) (commitstatus.CommitStatusState, error) {
// Ensure HeadRepo is loaded
if err := pr.LoadHeadRepo(ctx); err != nil {
return "", errors.Wrap(err, "LoadHeadRepo")
@@ -131,10 +96,10 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR
}
defer closer.Close()
- if pr.Flow == issues_model.PullRequestFlowGithub && !headGitRepo.IsBranchExist(pr.HeadBranch) {
+ if pr.Flow == issues_model.PullRequestFlowGithub && !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
return "", errors.New("Head branch does not exist, can not merge")
}
- if pr.Flow == issues_model.PullRequestFlowAGit && !git.IsReferenceExist(ctx, headGitRepo.Path, pr.GetGitRefName()) {
+ if pr.Flow == issues_model.PullRequestFlowAGit && !gitrepo.IsReferenceExist(ctx, pr.HeadRepo, pr.GetGitRefName()) {
return "", errors.New("Head branch does not exist, can not merge")
}
@@ -152,7 +117,7 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR
return "", errors.Wrap(err, "LoadBaseRepo")
}
- commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptionsAll)
+ commitStatuses, err := git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptionsAll)
if err != nil {
return "", errors.Wrap(err, "GetLatestCommitStatus")
}
diff --git a/services/pull/commit_status_test.go b/services/pull/commit_status_test.go
index 592acdd55c..a58e788c04 100644
--- a/services/pull/commit_status_test.go
+++ b/services/pull/commit_status_test.go
@@ -8,58 +8,85 @@ import (
"testing"
git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/commitstatus"
"github.com/stretchr/testify/assert"
)
func TestMergeRequiredContextsCommitStatus(t *testing.T) {
- testCases := [][]*git_model.CommitStatus{
+ cases := []struct {
+ commitStatuses []*git_model.CommitStatus
+ requiredContexts []string
+ expected commitstatus.CommitStatusState
+ }{
{
- {Context: "Build 1", State: structs.CommitStatusSuccess},
- {Context: "Build 2", State: structs.CommitStatusSuccess},
- {Context: "Build 3", State: structs.CommitStatusSuccess},
+ commitStatuses: []*git_model.CommitStatus{},
+ requiredContexts: []string{},
+ expected: commitstatus.CommitStatusPending,
},
{
- {Context: "Build 1", State: structs.CommitStatusSuccess},
- {Context: "Build 2", State: structs.CommitStatusSuccess},
- {Context: "Build 2t", State: structs.CommitStatusPending},
+ commitStatuses: []*git_model.CommitStatus{
+ {Context: "Build xxx", State: commitstatus.CommitStatusSkipped},
+ },
+ requiredContexts: []string{"Build*"},
+ expected: commitstatus.CommitStatusSuccess,
},
{
- {Context: "Build 1", State: structs.CommitStatusSuccess},
- {Context: "Build 2", State: structs.CommitStatusSuccess},
- {Context: "Build 2t", State: structs.CommitStatusFailure},
+ commitStatuses: []*git_model.CommitStatus{
+ {Context: "Build 1", State: commitstatus.CommitStatusSkipped},
+ {Context: "Build 2", State: commitstatus.CommitStatusSuccess},
+ {Context: "Build 3", State: commitstatus.CommitStatusSuccess},
+ },
+ requiredContexts: []string{"Build*"},
+ expected: commitstatus.CommitStatusSuccess,
},
{
- {Context: "Build 1", State: structs.CommitStatusSuccess},
- {Context: "Build 2", State: structs.CommitStatusSuccess},
- {Context: "Build 2t", State: structs.CommitStatusSuccess},
+ commitStatuses: []*git_model.CommitStatus{
+ {Context: "Build 1", State: commitstatus.CommitStatusSuccess},
+ {Context: "Build 2", State: commitstatus.CommitStatusSuccess},
+ {Context: "Build 2t", State: commitstatus.CommitStatusPending},
+ },
+ requiredContexts: []string{"Build*", "Build 2t*"},
+ expected: commitstatus.CommitStatusPending,
},
{
- {Context: "Build 1", State: structs.CommitStatusSuccess},
- {Context: "Build 2", State: structs.CommitStatusSuccess},
- {Context: "Build 2t", State: structs.CommitStatusSuccess},
+ commitStatuses: []*git_model.CommitStatus{
+ {Context: "Build 1", State: commitstatus.CommitStatusSuccess},
+ {Context: "Build 2", State: commitstatus.CommitStatusSuccess},
+ {Context: "Build 2t", State: commitstatus.CommitStatusFailure},
+ },
+ requiredContexts: []string{"Build*", "Build 2t*"},
+ expected: commitstatus.CommitStatusFailure,
+ },
+ {
+ commitStatuses: []*git_model.CommitStatus{
+ {Context: "Build 1", State: commitstatus.CommitStatusSuccess},
+ {Context: "Build 2", State: commitstatus.CommitStatusSuccess},
+ {Context: "Build 2t", State: commitstatus.CommitStatusFailure},
+ },
+ requiredContexts: []string{"Build*"},
+ expected: commitstatus.CommitStatusFailure,
+ },
+ {
+ commitStatuses: []*git_model.CommitStatus{
+ {Context: "Build 1", State: commitstatus.CommitStatusSuccess},
+ {Context: "Build 2", State: commitstatus.CommitStatusSuccess},
+ {Context: "Build 2t", State: commitstatus.CommitStatusSuccess},
+ },
+ requiredContexts: []string{"Build*", "Build 2t*", "Build 3*"},
+ expected: commitstatus.CommitStatusPending,
+ },
+ {
+ commitStatuses: []*git_model.CommitStatus{
+ {Context: "Build 1", State: commitstatus.CommitStatusSuccess},
+ {Context: "Build 2", State: commitstatus.CommitStatusSuccess},
+ {Context: "Build 2t", State: commitstatus.CommitStatusSuccess},
+ },
+ requiredContexts: []string{"Build*", "Build *", "Build 2t*", "Build 1*"},
+ expected: commitstatus.CommitStatusSuccess,
},
}
- testCasesRequiredContexts := [][]string{
- {"Build*"},
- {"Build*", "Build 2t*"},
- {"Build*", "Build 2t*"},
- {"Build*", "Build 2t*", "Build 3*"},
- {"Build*", "Build *", "Build 2t*", "Build 1*"},
- }
-
- testCasesExpected := []structs.CommitStatusState{
- structs.CommitStatusSuccess,
- structs.CommitStatusPending,
- structs.CommitStatusFailure,
- structs.CommitStatusPending,
- structs.CommitStatusSuccess,
- }
-
- for i, commitStatuses := range testCases {
- if MergeRequiredContextsCommitStatus(commitStatuses, testCasesRequiredContexts[i]) != testCasesExpected[i] {
- assert.Fail(t, "Test case failed", "Test case %d failed", i+1)
- }
+ for i, c := range cases {
+ assert.Equal(t, c.expected, MergeRequiredContextsCommitStatus(c.commitStatuses, c.requiredContexts), "case %d", i)
}
}
diff --git a/services/pull/merge.go b/services/pull/merge.go
index 879fe5a540..cd9aeb2ad1 100644
--- a/services/pull/merge.go
+++ b/services/pull/merge.go
@@ -6,17 +6,21 @@ package pull
import (
"context"
+ "errors"
"fmt"
+ "maps"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
+ "unicode"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
access_model "code.gitea.io/gitea/models/perm/access"
+ pull_model "code.gitea.io/gitea/models/pull"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
@@ -58,7 +62,7 @@ func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issue
issueReference = "!"
}
- reviewedOn := fmt.Sprintf("Reviewed-on: %s", httplib.MakeAbsoluteURL(ctx, pr.Issue.Link()))
+ reviewedOn := "Reviewed-on: " + httplib.MakeAbsoluteURL(ctx, pr.Issue.Link())
reviewedBy := pr.GetApprovers(ctx)
if mergeStyle != "" {
@@ -92,9 +96,7 @@ func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issue
vars["HeadRepoOwnerName"] = pr.HeadRepo.OwnerName
vars["HeadRepoName"] = pr.HeadRepo.Name
}
- for extraKey, extraValue := range extraVars {
- vars[extraKey] = extraValue
- }
+ maps.Copy(vars, extraVars)
refs, err := pr.ResolveCrossReferences(ctx)
if err == nil {
closeIssueIndexes := make([]string, 0, len(refs))
@@ -159,6 +161,41 @@ func GetDefaultMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr
return getMergeMessage(ctx, baseGitRepo, pr, mergeStyle, nil)
}
+func AddCommitMessageTailer(message, tailerKey, tailerValue string) string {
+ tailerLine := tailerKey + ": " + tailerValue
+ message = strings.ReplaceAll(message, "\r\n", "\n")
+ message = strings.ReplaceAll(message, "\r", "\n")
+ if strings.Contains(message, "\n"+tailerLine+"\n") || strings.HasSuffix(message, "\n"+tailerLine) {
+ return message
+ }
+
+ if !strings.HasSuffix(message, "\n") {
+ message += "\n"
+ }
+ pos1 := strings.LastIndexByte(message[:len(message)-1], '\n')
+ pos2 := -1
+ if pos1 != -1 {
+ pos2 = strings.IndexByte(message[pos1:], ':')
+ if pos2 != -1 {
+ pos2 += pos1
+ }
+ }
+ var lastLineKey string
+ if pos1 != -1 && pos2 != -1 {
+ lastLineKey = message[pos1+1 : pos2]
+ }
+
+ isLikelyTailerLine := lastLineKey != "" && unicode.IsUpper(rune(lastLineKey[0])) && strings.Contains(message, "-")
+ for i := 0; isLikelyTailerLine && i < len(lastLineKey); i++ {
+ r := rune(lastLineKey[i])
+ isLikelyTailerLine = unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-'
+ }
+ if !strings.HasSuffix(message, "\n\n") && !isLikelyTailerLine {
+ message += "\n"
+ }
+ return message + tailerLine
+}
+
// ErrInvalidMergeStyle represents an error if merging with disabled merge strategy
type ErrInvalidMergeStyle struct {
ID int64
@@ -210,7 +247,15 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
}
defer releaser()
defer func() {
- go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "")
+ go AddTestPullRequestTask(TestPullRequestOptions{
+ RepoID: pr.BaseRepo.ID,
+ Doer: doer,
+ Branch: pr.BaseBranch,
+ IsSync: false,
+ IsForcePush: false,
+ OldCommitID: "",
+ NewCommitID: "",
+ })
}()
_, err = doMergeAndPush(ctx, pr, doer, mergeStyle, expectedHeadCommitID, message, repo_module.PushTriggerPRMergeToBase)
@@ -280,7 +325,7 @@ func handleCloseCrossReferences(ctx context.Context, pr *issues_model.PullReques
}
// doMergeAndPush performs the merge operation without changing any pull information in database and pushes it up to the base repository
-func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string, pushTrigger repo_module.PushTrigger) (string, error) { //nolint:unparam
+func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string, pushTrigger repo_module.PushTrigger) (string, error) { //nolint:unparam // non-error result is never used
// Clone base repo.
mergeCtx, cancel, err := createTemporaryRepoForMerge(ctx, pr, doer, expectedHeadCommitID)
if err != nil {
@@ -355,12 +400,12 @@ func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *use
)
mergeCtx.env = append(mergeCtx.env, repo_module.EnvPushTrigger+"="+string(pushTrigger))
- pushCmd := git.NewCommand(ctx, "push", "origin").AddDynamicArguments(baseBranch + ":" + git.BranchPrefix + pr.BaseBranch)
+ pushCmd := git.NewCommand("push", "origin").AddDynamicArguments(baseBranch + ":" + git.BranchPrefix + pr.BaseBranch)
// Push back to upstream.
// This cause an api call to "/api/internal/hook/post-receive/...",
// If it's merge, all db transaction and operations should be there but not here to prevent deadlock.
- if err := pushCmd.Run(mergeCtx.RunOpts()); err != nil {
+ if err := pushCmd.Run(ctx, mergeCtx.RunOpts()); err != nil {
if strings.Contains(mergeCtx.errbuf.String(), "non-fast-forward") {
return "", &git.ErrPushOutOfDate{
StdOut: mergeCtx.outbuf.String(),
@@ -385,13 +430,16 @@ func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *use
}
func commitAndSignNoAuthor(ctx *mergeContext, message string) error {
- cmdCommit := git.NewCommand(ctx, "commit").AddOptionFormat("--message=%s", message)
- if ctx.signKeyID == "" {
+ cmdCommit := git.NewCommand("commit").AddOptionFormat("--message=%s", message)
+ if ctx.signKey == nil {
cmdCommit.AddArguments("--no-gpg-sign")
} else {
- cmdCommit.AddOptionFormat("-S%s", ctx.signKeyID)
+ if ctx.signKey.Format != "" {
+ cmdCommit.AddConfig("gpg.format", ctx.signKey.Format)
+ }
+ cmdCommit.AddOptionFormat("-S%s", ctx.signKey.KeyID)
}
- if err := cmdCommit.Run(ctx.RunOpts()); err != nil {
+ if err := cmdCommit.Run(ctx, ctx.RunOpts()); err != nil {
log.Error("git commit %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("git commit %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
}
@@ -452,7 +500,7 @@ func (err ErrMergeDivergingFastForwardOnly) Error() string {
}
func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *git.Command) error {
- if err := cmd.Run(ctx.RunOpts()); err != nil {
+ if err := cmd.Run(ctx, ctx.RunOpts()); err != nil {
// Merge will leave a MERGE_HEAD file in the .git folder if there is a conflict
if _, statErr := os.Stat(filepath.Join(ctx.tmpBasePath, ".git", "MERGE_HEAD")); statErr == nil {
// We have a merge conflict error
@@ -508,25 +556,6 @@ func IsUserAllowedToMerge(ctx context.Context, pr *issues_model.PullRequest, p a
return false, nil
}
-// ErrDisallowedToMerge represents an error that a branch is protected and the current user is not allowed to modify it.
-type ErrDisallowedToMerge struct {
- Reason string
-}
-
-// IsErrDisallowedToMerge checks if an error is an ErrDisallowedToMerge.
-func IsErrDisallowedToMerge(err error) bool {
- _, ok := err.(ErrDisallowedToMerge)
- return ok
-}
-
-func (err ErrDisallowedToMerge) Error() string {
- return fmt.Sprintf("not allowed to merge [reason: %s]", err.Reason)
-}
-
-func (err ErrDisallowedToMerge) Unwrap() error {
- return util.ErrPermissionDenied
-}
-
// CheckPullBranchProtections checks whether the PR is ready to be merged (reviews and status checks)
func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullRequest, skipProtectedFilesCheck bool) (err error) {
if err = pr.LoadBaseRepo(ctx); err != nil {
@@ -546,31 +575,21 @@ func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullReques
return err
}
if !isPass {
- return ErrDisallowedToMerge{
- Reason: "Not all required status checks successful",
- }
+ return util.ErrorWrap(ErrNotReadyToMerge, "Not all required status checks successful")
}
if !issues_model.HasEnoughApprovals(ctx, pb, pr) {
- return ErrDisallowedToMerge{
- Reason: "Does not have enough approvals",
- }
+ return util.ErrorWrap(ErrNotReadyToMerge, "Does not have enough approvals")
}
if issues_model.MergeBlockedByRejectedReview(ctx, pb, pr) {
- return ErrDisallowedToMerge{
- Reason: "There are requested changes",
- }
+ return util.ErrorWrap(ErrNotReadyToMerge, "There are requested changes")
}
if issues_model.MergeBlockedByOfficialReviewRequests(ctx, pb, pr) {
- return ErrDisallowedToMerge{
- Reason: "There are official review requests",
- }
+ return util.ErrorWrap(ErrNotReadyToMerge, "There are official review requests")
}
if issues_model.MergeBlockedByOutdatedBranch(pb, pr) {
- return ErrDisallowedToMerge{
- Reason: "The head branch is behind the base branch",
- }
+ return util.ErrorWrap(ErrNotReadyToMerge, "The head branch is behind the base branch")
}
if skipProtectedFilesCheck {
@@ -578,9 +597,7 @@ func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullReques
}
if pb.MergeBlockedByProtectedFiles(pr.ChangedProtectedFiles) {
- return ErrDisallowedToMerge{
- Reason: "Changed protected files",
- }
+ return util.ErrorWrap(ErrNotReadyToMerge, "Changed protected files")
}
return nil
@@ -612,13 +629,13 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use
objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)
if len(commitID) != objectFormat.FullLength() {
- return fmt.Errorf("Wrong commit ID")
+ return errors.New("Wrong commit ID")
}
commit, err := baseGitRepo.GetCommit(commitID)
if err != nil {
if git.IsErrNotExist(err) {
- return fmt.Errorf("Wrong commit ID")
+ return errors.New("Wrong commit ID")
}
return err
}
@@ -629,20 +646,14 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use
return err
}
if !ok {
- return fmt.Errorf("Wrong commit ID")
+ return errors.New("Wrong commit ID")
}
- pr.MergedCommitID = commitID
- pr.MergedUnix = timeutil.TimeStamp(commit.Author.When.Unix())
- pr.Status = issues_model.PullRequestStatusManuallyMerged
- pr.Merger = doer
- pr.MergerID = doer.ID
-
var merged bool
- if merged, err = SetMerged(ctx, pr); err != nil {
+ if merged, err = SetMerged(ctx, pr, commitID, timeutil.TimeStamp(commit.Author.When.Unix()), doer, issues_model.PullRequestStatusManuallyMerged); err != nil {
return err
} else if !merged {
- return fmt.Errorf("SetMerged failed")
+ return errors.New("SetMerged failed")
}
return nil
})
@@ -658,41 +669,35 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use
}
// SetMerged sets a pull request to merged and closes the corresponding issue
-func SetMerged(ctx context.Context, pr *issues_model.PullRequest) (bool, error) {
+func SetMerged(ctx context.Context, pr *issues_model.PullRequest, mergedCommitID string, mergedTimeStamp timeutil.TimeStamp, merger *user_model.User, mergeStatus issues_model.PullRequestStatus) (bool, error) {
if pr.HasMerged {
return false, fmt.Errorf("PullRequest[%d] already merged", pr.Index)
}
- if pr.MergedCommitID == "" || pr.MergedUnix == 0 || pr.Merger == nil {
- return false, fmt.Errorf("unable to merge PullRequest[%d], some required fields are empty", pr.Index)
- }
pr.HasMerged = true
- sess := db.GetEngine(ctx)
+ pr.MergedCommitID = mergedCommitID
+ pr.MergedUnix = mergedTimeStamp
+ pr.Merger = merger
+ pr.MergerID = merger.ID
+ pr.Status = mergeStatus
+ // reset the conflicted files as there cannot be any if we're merged
+ pr.ConflictedFiles = []string{}
- if _, err := sess.Exec("UPDATE `issue` SET `repo_id` = `repo_id` WHERE `id` = ?", pr.IssueID); err != nil {
- return false, err
+ if pr.MergedCommitID == "" || pr.MergedUnix == 0 || pr.Merger == nil {
+ return false, fmt.Errorf("unable to merge PullRequest[%d], some required fields are empty", pr.Index)
}
- if _, err := sess.Exec("UPDATE `pull_request` SET `issue_id` = `issue_id` WHERE `id` = ?", pr.ID); err != nil {
+ ctx, committer, err := db.TxContext(ctx)
+ if err != nil {
return false, err
}
+ defer committer.Close()
pr.Issue = nil
if err := pr.LoadIssue(ctx); err != nil {
return false, err
}
- if tmpPr, err := issues_model.GetPullRequestByID(ctx, pr.ID); err != nil {
- return false, err
- } else if tmpPr.HasMerged {
- if pr.Issue.IsClosed {
- return false, nil
- }
- return false, fmt.Errorf("PullRequest[%d] already merged but it's associated issue [%d] is not closed", pr.Index, pr.IssueID)
- } else if pr.Issue.IsClosed {
- return false, fmt.Errorf("PullRequest[%d] already closed", pr.Index)
- }
-
if err := pr.Issue.LoadRepo(ctx); err != nil {
return false, err
}
@@ -701,16 +706,28 @@ func SetMerged(ctx context.Context, pr *issues_model.PullRequest) (bool, error)
return false, err
}
- if _, err := issues_model.ChangeIssueStatus(ctx, pr.Issue, pr.Merger, true, true); err != nil {
- return false, fmt.Errorf("ChangeIssueStatus: %w", err)
+ // Removing an auto merge pull and ignore if not exist
+ if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) {
+ return false, fmt.Errorf("DeleteScheduledAutoMerge[%d]: %v", pr.ID, err)
}
- // reset the conflicted files as there cannot be any if we're merged
- pr.ConflictedFiles = []string{}
+ // Set issue as closed
+ if _, err := issues_model.SetIssueAsClosed(ctx, pr.Issue, pr.Merger, true); err != nil {
+ return false, fmt.Errorf("ChangeIssueStatus: %w", err)
+ }
- // We need to save all of the data used to compute this merge as it may have already been changed by TestPatch. FIXME: need to set some state to prevent TestPatch from running whilst we are merging.
- if _, err := sess.Where("id = ?", pr.ID).Cols("has_merged, status, merge_base, merged_commit_id, merger_id, merged_unix, conflicted_files").Update(pr); err != nil {
+ // We need to save all of the data used to compute this merge as it may have already been changed by testPullRequestBranchMergeable. FIXME: need to set some state to prevent testPullRequestBranchMergeable from running whilst we are merging.
+ if cnt, err := db.GetEngine(ctx).Where("id = ?", pr.ID).
+ And("has_merged = ?", false).
+ Cols("has_merged, status, merge_base, merged_commit_id, merger_id, merged_unix, conflicted_files").
+ Update(pr); err != nil {
return false, fmt.Errorf("failed to update pr[%d]: %w", pr.ID, err)
+ } else if cnt != 1 {
+ return false, issues_model.ErrIssueAlreadyChanged
+ }
+
+ if err := committer.Commit(); err != nil {
+ return false, err
}
return true, nil
diff --git a/services/pull/merge_ff_only.go b/services/pull/merge_ff_only.go
index f57c732104..6c3a68b95b 100644
--- a/services/pull/merge_ff_only.go
+++ b/services/pull/merge_ff_only.go
@@ -11,7 +11,7 @@ import (
// doMergeStyleFastForwardOnly merges the tracking into the current HEAD - which is assumed to be staging branch (equal to the pr.BaseBranch)
func doMergeStyleFastForwardOnly(ctx *mergeContext) error {
- cmd := git.NewCommand(ctx, "merge", "--ff-only").AddDynamicArguments(trackingBranch)
+ cmd := git.NewCommand("merge", "--ff-only").AddDynamicArguments(trackingBranch)
if err := runMergeCommand(ctx, repo_model.MergeStyleFastForwardOnly, cmd); err != nil {
log.Error("%-v Unable to merge tracking into base: %v", ctx.pr, err)
return err
diff --git a/services/pull/merge_merge.go b/services/pull/merge_merge.go
index bf56c071db..118d21c7cd 100644
--- a/services/pull/merge_merge.go
+++ b/services/pull/merge_merge.go
@@ -11,7 +11,7 @@ import (
// doMergeStyleMerge merges the tracking branch into the current HEAD - which is assumed to be the staging branch (equal to the pr.BaseBranch)
func doMergeStyleMerge(ctx *mergeContext, message string) error {
- cmd := git.NewCommand(ctx, "merge", "--no-ff", "--no-commit").AddDynamicArguments(trackingBranch)
+ cmd := git.NewCommand("merge", "--no-ff", "--no-commit").AddDynamicArguments(trackingBranch)
if err := runMergeCommand(ctx, repo_model.MergeStyleMerge, cmd); err != nil {
log.Error("%-v Unable to merge tracking into base: %v", ctx.pr, err)
return err
diff --git a/services/pull/merge_prepare.go b/services/pull/merge_prepare.go
index 2e1cc8cf85..31a1e13734 100644
--- a/services/pull/merge_prepare.go
+++ b/services/pull/merge_prepare.go
@@ -23,11 +23,11 @@ import (
)
type mergeContext struct {
- *prContext
+ *prTmpRepoContext
doer *user_model.User
sig *git.Signature
committer *git.Signature
- signKeyID string // empty for no-sign, non-empty to sign
+ signKey *git.SigningKey
env []string
}
@@ -68,12 +68,12 @@ func createTemporaryRepoForMerge(ctx context.Context, pr *issues_model.PullReque
}
mergeCtx = &mergeContext{
- prContext: prCtx,
- doer: doer,
+ prTmpRepoContext: prCtx,
+ doer: doer,
}
if expectedHeadCommitID != "" {
- trackingCommitID, _, err := git.NewCommand(ctx, "show-ref", "--hash").AddDynamicArguments(git.BranchPrefix + trackingBranch).RunStdString(&git.RunOpts{Dir: mergeCtx.tmpBasePath})
+ trackingCommitID, _, err := git.NewCommand("show-ref", "--hash").AddDynamicArguments(git.BranchPrefix+trackingBranch).RunStdString(ctx, &git.RunOpts{Dir: mergeCtx.tmpBasePath})
if err != nil {
defer cancel()
log.Error("failed to get sha of head branch in %-v: show-ref[%s] --hash refs/heads/tracking: %v", mergeCtx.pr, mergeCtx.tmpBasePath, err)
@@ -99,9 +99,9 @@ func createTemporaryRepoForMerge(ctx context.Context, pr *issues_model.PullReque
mergeCtx.committer = mergeCtx.sig
// Determine if we should sign
- sign, keyID, signer, _ := asymkey_service.SignMerge(ctx, mergeCtx.pr, mergeCtx.doer, mergeCtx.tmpBasePath, "HEAD", trackingBranch)
+ sign, key, signer, _ := asymkey_service.SignMerge(ctx, mergeCtx.pr, mergeCtx.doer, mergeCtx.tmpBasePath, "HEAD", trackingBranch)
if sign {
- mergeCtx.signKeyID = keyID
+ mergeCtx.signKey = key
if pr.BaseRepo.GetTrustModel() == repo_model.CommitterTrustModel || pr.BaseRepo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
mergeCtx.committer = signer
}
@@ -151,8 +151,8 @@ func prepareTemporaryRepoForMerge(ctx *mergeContext) error {
}
setConfig := func(key, value string) error {
- if err := git.NewCommand(ctx, "config", "--local").AddDynamicArguments(key, value).
- Run(ctx.RunOpts()); err != nil {
+ if err := git.NewCommand("config", "--local").AddDynamicArguments(key, value).
+ Run(ctx, ctx.RunOpts()); err != nil {
log.Error("git config [%s -> %q]: %v\n%s\n%s", key, value, err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("git config [%s -> %q]: %w\n%s\n%s", key, value, err, ctx.outbuf.String(), ctx.errbuf.String())
}
@@ -184,8 +184,8 @@ func prepareTemporaryRepoForMerge(ctx *mergeContext) error {
}
// Read base branch index
- if err := git.NewCommand(ctx, "read-tree", "HEAD").
- Run(ctx.RunOpts()); err != nil {
+ if err := git.NewCommand("read-tree", "HEAD").
+ Run(ctx, ctx.RunOpts()); err != nil {
log.Error("git read-tree HEAD: %v\n%s\n%s", err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("Unable to read base branch in to the index: %w\n%s\n%s", err, ctx.outbuf.String(), ctx.errbuf.String())
}
@@ -221,8 +221,8 @@ func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string, o
return 0, nil, nil
}
- err = git.NewCommand(ctx, "diff-tree", "--no-commit-id", "--name-only", "-r", "-r", "-z", "--root").AddDynamicArguments(baseBranch, headBranch).
- Run(&git.RunOpts{
+ err = git.NewCommand("diff-tree", "--no-commit-id", "--name-only", "-r", "-r", "-z", "--root").AddDynamicArguments(baseBranch, headBranch).
+ Run(ctx, &git.RunOpts{
Dir: repoPath,
Stdout: diffOutWriter,
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
@@ -272,16 +272,16 @@ func (err ErrRebaseConflicts) Error() string {
// if there is a conflict it will return an ErrRebaseConflicts
func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) error {
// Checkout head branch
- if err := git.NewCommand(ctx, "checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch).
- Run(ctx.RunOpts()); err != nil {
+ if err := git.NewCommand("checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch).
+ Run(ctx, ctx.RunOpts()); err != nil {
return fmt.Errorf("unable to git checkout tracking as staging in temp repo for %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
}
ctx.outbuf.Reset()
ctx.errbuf.Reset()
// Rebase before merging
- if err := git.NewCommand(ctx, "rebase").AddDynamicArguments(baseBranch).
- Run(ctx.RunOpts()); err != nil {
+ if err := git.NewCommand("rebase").AddDynamicArguments(baseBranch).
+ Run(ctx, ctx.RunOpts()); err != nil {
// Rebase will leave a REBASE_HEAD file in .git if there is a conflict
if _, statErr := os.Stat(filepath.Join(ctx.tmpBasePath, ".git", "REBASE_HEAD")); statErr == nil {
var commitSha string
diff --git a/services/pull/merge_rebase.go b/services/pull/merge_rebase.go
index ecf376220e..dd7c8761f0 100644
--- a/services/pull/merge_rebase.go
+++ b/services/pull/merge_rebase.go
@@ -16,7 +16,7 @@ import (
// getRebaseAmendMessage composes the message to amend commits in rebase merge of a pull request.
func getRebaseAmendMessage(ctx *mergeContext, baseGitRepo *git.Repository) (message string, err error) {
// Get existing commit message.
- commitMessage, _, err := git.NewCommand(ctx, "show", "--format=%B", "-s").RunStdString(&git.RunOpts{Dir: ctx.tmpBasePath})
+ commitMessage, _, err := git.NewCommand("show", "--format=%B", "-s").RunStdString(ctx, &git.RunOpts{Dir: ctx.tmpBasePath})
if err != nil {
return "", err
}
@@ -42,7 +42,7 @@ func doMergeRebaseFastForward(ctx *mergeContext) error {
return fmt.Errorf("Failed to get full commit id for HEAD: %w", err)
}
- cmd := git.NewCommand(ctx, "merge", "--ff-only").AddDynamicArguments(stagingBranch)
+ cmd := git.NewCommand("merge", "--ff-only").AddDynamicArguments(stagingBranch)
if err := runMergeCommand(ctx, repo_model.MergeStyleRebase, cmd); err != nil {
log.Error("Unable to merge staging into base: %v", err)
return err
@@ -73,7 +73,7 @@ func doMergeRebaseFastForward(ctx *mergeContext) error {
}
if newMessage != "" {
- if err := git.NewCommand(ctx, "commit", "--amend").AddOptionFormat("--message=%s", newMessage).Run(&git.RunOpts{Dir: ctx.tmpBasePath}); err != nil {
+ if err := git.NewCommand("commit", "--amend").AddOptionFormat("--message=%s", newMessage).Run(ctx, &git.RunOpts{Dir: ctx.tmpBasePath}); err != nil {
log.Error("Unable to amend commit message: %v", err)
return err
}
@@ -84,7 +84,7 @@ func doMergeRebaseFastForward(ctx *mergeContext) error {
// Perform rebase merge with merge commit.
func doMergeRebaseMergeCommit(ctx *mergeContext, message string) error {
- cmd := git.NewCommand(ctx, "merge").AddArguments("--no-ff", "--no-commit").AddDynamicArguments(stagingBranch)
+ cmd := git.NewCommand("merge").AddArguments("--no-ff", "--no-commit").AddDynamicArguments(stagingBranch)
if err := runMergeCommand(ctx, repo_model.MergeStyleRebaseMerge, cmd); err != nil {
log.Error("Unable to merge staging into base: %v", err)
@@ -105,8 +105,8 @@ func doMergeStyleRebase(ctx *mergeContext, mergeStyle repo_model.MergeStyle, mes
}
// Checkout base branch again
- if err := git.NewCommand(ctx, "checkout").AddDynamicArguments(baseBranch).
- Run(ctx.RunOpts()); err != nil {
+ if err := git.NewCommand("checkout").AddDynamicArguments(baseBranch).
+ Run(ctx, ctx.RunOpts()); err != nil {
log.Error("git checkout base prior to merge post staging rebase %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("git checkout base prior to merge post staging rebase %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
}
diff --git a/services/pull/merge_squash.go b/services/pull/merge_squash.go
index 7258671888..0049c0b117 100644
--- a/services/pull/merge_squash.go
+++ b/services/pull/merge_squash.go
@@ -5,7 +5,6 @@ package pull
import (
"fmt"
- "strings"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
@@ -58,7 +57,7 @@ func doMergeStyleSquash(ctx *mergeContext, message string) error {
return fmt.Errorf("getAuthorSignatureSquash: %w", err)
}
- cmdMerge := git.NewCommand(ctx, "merge", "--squash").AddDynamicArguments(trackingBranch)
+ cmdMerge := git.NewCommand("merge", "--squash").AddDynamicArguments(trackingBranch)
if err := runMergeCommand(ctx, repo_model.MergeStyleSquash, cmdMerge); err != nil {
log.Error("%-v Unable to merge --squash tracking into base: %v", ctx.pr, err)
return err
@@ -66,20 +65,21 @@ func doMergeStyleSquash(ctx *mergeContext, message string) error {
if setting.Repository.PullRequest.AddCoCommitterTrailers && ctx.committer.String() != sig.String() {
// add trailer
- if !strings.Contains(message, fmt.Sprintf("Co-authored-by: %s", sig.String())) {
- message += fmt.Sprintf("\nCo-authored-by: %s", sig.String())
- }
- message += fmt.Sprintf("\nCo-committed-by: %s\n", sig.String())
+ message = AddCommitMessageTailer(message, "Co-authored-by", sig.String())
+ message = AddCommitMessageTailer(message, "Co-committed-by", sig.String()) // FIXME: this one should be removed, it is not really used or widely used
}
- cmdCommit := git.NewCommand(ctx, "commit").
+ cmdCommit := git.NewCommand("commit").
AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email).
AddOptionFormat("--message=%s", message)
- if ctx.signKeyID == "" {
+ if ctx.signKey == nil {
cmdCommit.AddArguments("--no-gpg-sign")
} else {
- cmdCommit.AddOptionFormat("-S%s", ctx.signKeyID)
+ if ctx.signKey.Format != "" {
+ cmdCommit.AddConfig("gpg.format", ctx.signKey.Format)
+ }
+ cmdCommit.AddOptionFormat("-S%s", ctx.signKey.KeyID)
}
- if err := cmdCommit.Run(ctx.RunOpts()); err != nil {
+ if err := cmdCommit.Run(ctx, ctx.RunOpts()); err != nil {
log.Error("git commit %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("git commit [%s:%s -> %s:%s]: %w\n%s\n%s", ctx.pr.HeadRepo.FullName(), ctx.pr.HeadBranch, ctx.pr.BaseRepo.FullName(), ctx.pr.BaseBranch, err, ctx.outbuf.String(), ctx.errbuf.String())
}
diff --git a/services/pull/merge_test.go b/services/pull/merge_test.go
index 6df6f55d46..91abeb9d9c 100644
--- a/services/pull/merge_test.go
+++ b/services/pull/merge_test.go
@@ -65,3 +65,28 @@ func Test_expandDefaultMergeMessage(t *testing.T) {
})
}
}
+
+func TestAddCommitMessageTailer(t *testing.T) {
+ // add tailer for empty message
+ assert.Equal(t, "\n\nTest-tailer: TestValue", AddCommitMessageTailer("", "Test-tailer", "TestValue"))
+
+ // add tailer for message without newlines
+ assert.Equal(t, "title\n\nTest-tailer: TestValue", AddCommitMessageTailer("title", "Test-tailer", "TestValue"))
+ assert.Equal(t, "title\n\nNot tailer: xxx\n\nTest-tailer: TestValue", AddCommitMessageTailer("title\n\nNot tailer: xxx", "Test-tailer", "TestValue"))
+ assert.Equal(t, "title\n\nNotTailer: xxx\n\nTest-tailer: TestValue", AddCommitMessageTailer("title\n\nNotTailer: xxx", "Test-tailer", "TestValue"))
+ assert.Equal(t, "title\n\nnot-tailer: xxx\n\nTest-tailer: TestValue", AddCommitMessageTailer("title\n\nnot-tailer: xxx", "Test-tailer", "TestValue"))
+
+ // add tailer for message with one EOL
+ assert.Equal(t, "title\n\nTest-tailer: TestValue", AddCommitMessageTailer("title\n", "Test-tailer", "TestValue"))
+
+ // add tailer for message with two EOLs
+ assert.Equal(t, "title\n\nTest-tailer: TestValue", AddCommitMessageTailer("title\n\n", "Test-tailer", "TestValue"))
+
+ // add tailer for message with existing tailer (won't duplicate)
+ assert.Equal(t, "title\n\nTest-tailer: TestValue", AddCommitMessageTailer("title\n\nTest-tailer: TestValue", "Test-tailer", "TestValue"))
+ assert.Equal(t, "title\n\nTest-tailer: TestValue\n", AddCommitMessageTailer("title\n\nTest-tailer: TestValue\n", "Test-tailer", "TestValue"))
+
+ // add tailer for message with existing tailer and different value (will append)
+ assert.Equal(t, "title\n\nTest-tailer: v1\nTest-tailer: v2", AddCommitMessageTailer("title\n\nTest-tailer: v1", "Test-tailer", "v2"))
+ assert.Equal(t, "title\n\nTest-tailer: v1\nTest-tailer: v2", AddCommitMessageTailer("title\n\nTest-tailer: v1\n", "Test-tailer", "v2"))
+}
diff --git a/services/pull/patch.go b/services/pull/patch.go
index 13623d73c6..153e0baf87 100644
--- a/services/pull/patch.go
+++ b/services/pull/patch.go
@@ -67,9 +67,8 @@ var patchErrorSuffices = []string{
": does not exist in index",
}
-// TestPatch will test whether a simple patch will apply
-func TestPatch(pr *issues_model.PullRequest) error {
- ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("TestPatch: %s", pr))
+func testPullRequestBranchMergeable(pr *issues_model.PullRequest) error {
+ ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("testPullRequestBranchMergeable: %s", pr))
defer finished()
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
@@ -81,10 +80,10 @@ func TestPatch(pr *issues_model.PullRequest) error {
}
defer cancel()
- return testPatch(ctx, prCtx, pr)
+ return testPullRequestTmpRepoBranchMergeable(ctx, prCtx, pr)
}
-func testPatch(ctx context.Context, prCtx *prContext, pr *issues_model.PullRequest) error {
+func testPullRequestTmpRepoBranchMergeable(ctx context.Context, prCtx *prTmpRepoContext, pr *issues_model.PullRequest) error {
gitRepo, err := git.OpenRepository(ctx, prCtx.tmpBasePath)
if err != nil {
return fmt.Errorf("OpenRepository: %w", err)
@@ -92,7 +91,7 @@ func testPatch(ctx context.Context, prCtx *prContext, pr *issues_model.PullReque
defer gitRepo.Close()
// 1. update merge base
- pr.MergeBase, _, err = git.NewCommand(ctx, "merge-base", "--", "base", "tracking").RunStdString(&git.RunOpts{Dir: prCtx.tmpBasePath})
+ pr.MergeBase, _, err = git.NewCommand("merge-base", "--", "base", "tracking").RunStdString(ctx, &git.RunOpts{Dir: prCtx.tmpBasePath})
if err != nil {
var err2 error
pr.MergeBase, err2 = gitRepo.GetRefCommitID(git.BranchPrefix + "base")
@@ -134,7 +133,7 @@ type errMergeConflict struct {
}
func (e *errMergeConflict) Error() string {
- return fmt.Sprintf("conflict detected at: %s", e.filename)
+ return "conflict detected at: " + e.filename
}
func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, filesToRemove *[]string, filesToAdd *[]git.IndexObjectInfo) error {
@@ -192,7 +191,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
}
// Need to get the objects from the object db to attempt to merge
- root, _, err := git.NewCommand(ctx, "unpack-file").AddDynamicArguments(file.stage1.sha).RunStdString(&git.RunOpts{Dir: tmpBasePath})
+ root, _, err := git.NewCommand("unpack-file").AddDynamicArguments(file.stage1.sha).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath})
if err != nil {
return fmt.Errorf("unable to get root object: %s at path: %s for merging. Error: %w", file.stage1.sha, file.stage1.path, err)
}
@@ -201,7 +200,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
_ = util.Remove(filepath.Join(tmpBasePath, root))
}()
- base, _, err := git.NewCommand(ctx, "unpack-file").AddDynamicArguments(file.stage2.sha).RunStdString(&git.RunOpts{Dir: tmpBasePath})
+ base, _, err := git.NewCommand("unpack-file").AddDynamicArguments(file.stage2.sha).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath})
if err != nil {
return fmt.Errorf("unable to get base object: %s at path: %s for merging. Error: %w", file.stage2.sha, file.stage2.path, err)
}
@@ -209,7 +208,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
defer func() {
_ = util.Remove(base)
}()
- head, _, err := git.NewCommand(ctx, "unpack-file").AddDynamicArguments(file.stage3.sha).RunStdString(&git.RunOpts{Dir: tmpBasePath})
+ head, _, err := git.NewCommand("unpack-file").AddDynamicArguments(file.stage3.sha).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath})
if err != nil {
return fmt.Errorf("unable to get head object:%s at path: %s for merging. Error: %w", file.stage3.sha, file.stage3.path, err)
}
@@ -219,13 +218,13 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
}()
// now git merge-file annoyingly takes a different order to the merge-tree ...
- _, _, conflictErr := git.NewCommand(ctx, "merge-file").AddDynamicArguments(base, root, head).RunStdString(&git.RunOpts{Dir: tmpBasePath})
+ _, _, conflictErr := git.NewCommand("merge-file").AddDynamicArguments(base, root, head).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath})
if conflictErr != nil {
return &errMergeConflict{file.stage2.path}
}
// base now contains the merged data
- hash, _, err := git.NewCommand(ctx, "hash-object", "-w", "--path").AddDynamicArguments(file.stage2.path, base).RunStdString(&git.RunOpts{Dir: tmpBasePath})
+ hash, _, err := git.NewCommand("hash-object", "-w", "--path").AddDynamicArguments(file.stage2.path, base).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath})
if err != nil {
return err
}
@@ -250,7 +249,7 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
defer cancel()
// First we use read-tree to do a simple three-way merge
- if _, _, err := git.NewCommand(ctx, "read-tree", "-m").AddDynamicArguments(base, ours, theirs).RunStdString(&git.RunOpts{Dir: gitPath}); err != nil {
+ if _, _, err := git.NewCommand("read-tree", "-m").AddDynamicArguments(base, ours, theirs).RunStdString(ctx, &git.RunOpts{Dir: gitPath}); err != nil {
log.Error("Unable to run read-tree -m! Error: %v", err)
return false, nil, fmt.Errorf("unable to run read-tree -m! Error: %w", err)
}
@@ -324,9 +323,9 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
// No conflicts detected so we need to check if the patch is empty...
// a. Write the newly merged tree and check the new tree-hash
var treeHash string
- treeHash, _, err = git.NewCommand(ctx, "write-tree").RunStdString(&git.RunOpts{Dir: tmpBasePath})
+ treeHash, _, err = git.NewCommand("write-tree").RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath})
if err != nil {
- lsfiles, _, _ := git.NewCommand(ctx, "ls-files", "-u").RunStdString(&git.RunOpts{Dir: tmpBasePath})
+ lsfiles, _, _ := git.NewCommand("ls-files", "-u").RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath})
return false, fmt.Errorf("unable to write unconflicted tree: %w\n`git ls-files -u`:\n%s", err, lsfiles)
}
treeHash = strings.TrimSpace(treeHash)
@@ -355,23 +354,19 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
}
// 3b. Create a plain patch from head to base
- tmpPatchFile, err := os.CreateTemp("", "patch")
+ tmpPatchFile, cleanup, err := setting.AppDataTempDir("git-repo-content").CreateTempFileRandom("patch")
if err != nil {
log.Error("Unable to create temporary patch file! Error: %v", err)
return false, fmt.Errorf("unable to create temporary patch file! Error: %w", err)
}
- defer func() {
- _ = util.Remove(tmpPatchFile.Name())
- }()
+ defer cleanup()
if err := gitRepo.GetDiffBinary(pr.MergeBase+"...tracking", tmpPatchFile); err != nil {
- tmpPatchFile.Close()
log.Error("Unable to get patch file from %s to %s in %s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.FullName(), err)
return false, fmt.Errorf("unable to get patch file from %s to %s in %s Error: %w", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.FullName(), err)
}
stat, err := tmpPatchFile.Stat()
if err != nil {
- tmpPatchFile.Close()
return false, fmt.Errorf("unable to stat patch file: %w", err)
}
patchPath := tmpPatchFile.Name()
@@ -384,10 +379,10 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
return false, nil
}
- log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
+ log.Trace("PullRequest[%d].testPullRequestTmpRepoBranchMergeable (patchPath): %s", pr.ID, patchPath)
// 4. Read the base branch in to the index of the temporary repository
- _, _, err = git.NewCommand(gitRepo.Ctx, "read-tree", "base").RunStdString(&git.RunOpts{Dir: tmpBasePath})
+ _, _, err = git.NewCommand("read-tree", "base").RunStdString(gitRepo.Ctx, &git.RunOpts{Dir: tmpBasePath})
if err != nil {
return false, fmt.Errorf("git read-tree %s: %w", pr.BaseBranch, err)
}
@@ -400,7 +395,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
prConfig := prUnit.PullRequestsConfig()
// 6. Prepare the arguments to apply the patch against the index
- cmdApply := git.NewCommand(gitRepo.Ctx, "apply", "--check", "--cached")
+ cmdApply := git.NewCommand("apply", "--check", "--cached")
if prConfig.IgnoreWhitespaceConflicts {
cmdApply.AddArguments("--ignore-whitespace")
}
@@ -431,7 +426,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
// 8. Run the check command
conflict = false
- err = cmdApply.Run(&git.RunOpts{
+ err = cmdApply.Run(gitRepo.Ctx, &git.RunOpts{
Dir: tmpBasePath,
Stderr: stderrWriter,
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
@@ -454,7 +449,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
scanner := bufio.NewScanner(stderrReader)
for scanner.Scan() {
line := scanner.Text()
- log.Trace("PullRequest[%d].testPatch: stderr: %s", pr.ID, line)
+ log.Trace("PullRequest[%d].testPullRequestTmpRepoBranchMergeable: stderr: %s", pr.ID, line)
if strings.HasPrefix(line, prefix) {
conflict = true
filepath := strings.TrimSpace(strings.Split(line[len(prefix):], ":")[0])
diff --git a/services/pull/patch_unmerged.go b/services/pull/patch_unmerged.go
index c60c48d923..200d2233e9 100644
--- a/services/pull/patch_unmerged.go
+++ b/services/pull/patch_unmerged.go
@@ -72,8 +72,8 @@ func readUnmergedLsFileLines(ctx context.Context, tmpBasePath string, outputChan
}()
stderr := &strings.Builder{}
- err = git.NewCommand(ctx, "ls-files", "-u", "-z").
- Run(&git.RunOpts{
+ err = git.NewCommand("ls-files", "-u", "-z").
+ Run(ctx, &git.RunOpts{
Dir: tmpBasePath,
Stdout: lsFilesWriter,
Stderr: stderr,
diff --git a/services/pull/protected_branch.go b/services/pull/protected_branch.go
new file mode 100644
index 0000000000..181bd32f44
--- /dev/null
+++ b/services/pull/protected_branch.go
@@ -0,0 +1,49 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package pull
+
+import (
+ "context"
+
+ git_model "code.gitea.io/gitea/models/git"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/gitrepo"
+)
+
+func CreateOrUpdateProtectedBranch(ctx context.Context, repo *repo_model.Repository,
+ protectBranch *git_model.ProtectedBranch, whitelistOptions git_model.WhitelistOptions,
+) error {
+ err := git_model.UpdateProtectBranch(ctx, repo, protectBranch, whitelistOptions)
+ if err != nil {
+ return err
+ }
+
+ isPlainRule := !git_model.IsRuleNameSpecial(protectBranch.RuleName)
+ var isBranchExist bool
+ if isPlainRule {
+ // TODO: read the database directly to check if the branch exists
+ isBranchExist = gitrepo.IsBranchExist(ctx, repo, protectBranch.RuleName)
+ }
+
+ if isBranchExist {
+ if err := CheckPRsForBaseBranch(ctx, repo, protectBranch.RuleName); err != nil {
+ return err
+ }
+ } else {
+ if !isPlainRule {
+ // FIXME: since we only need to recheck files protected rules, we could improve this
+ matchedBranches, err := git_model.FindAllMatchedBranches(ctx, repo.ID, protectBranch.RuleName)
+ if err != nil {
+ return err
+ }
+ for _, branchName := range matchedBranches {
+ if err = CheckPRsForBaseBranch(ctx, repo, branchName); err != nil {
+ return err
+ }
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/services/pull/pull.go b/services/pull/pull.go
index 85c36bb16a..701c4f4d32 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -6,6 +6,7 @@ package pull
import (
"bytes"
"context"
+ "errors"
"fmt"
"io"
"os"
@@ -95,7 +96,7 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error {
}
defer cancel()
- if err := testPatch(ctx, prCtx, pr); err != nil {
+ if err := testPullRequestTmpRepoBranchMergeable(ctx, prCtx, pr); err != nil {
return err
}
@@ -175,7 +176,7 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error {
}
if !pr.IsWorkInProgress(ctx) {
- reviewNotifiers, err = issue_service.PullRequestCodeOwnersReview(ctx, issue, pr)
+ reviewNotifiers, err = issue_service.PullRequestCodeOwnersReview(ctx, pr)
if err != nil {
return err
}
@@ -265,6 +266,7 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer
ID: pr.Issue.ID,
RepoID: pr.Issue.RepoID,
Index: pr.Issue.Index,
+ IsPull: true,
}
}
@@ -312,12 +314,12 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer
pr.BaseBranch = targetBranch
// Refresh patch
- if err := TestPatch(pr); err != nil {
+ if err := testPullRequestBranchMergeable(pr); err != nil {
return err
}
// Update target branch, PR diff and status
- // This is the same as checkAndUpdateStatus in check service, but also updates base_branch
+ // This is the same as markPullRequestAsMergeable in check service, but also updates base_branch
if pr.Status == issues_model.PullRequestStatusChecking {
pr.Status = issues_model.PullRequestStatusMergeable
}
@@ -370,19 +372,29 @@ func checkForInvalidation(ctx context.Context, requests issues_model.PullRequest
return nil
}
+type TestPullRequestOptions struct {
+ RepoID int64
+ Doer *user_model.User
+ Branch string
+ IsSync bool // True means it's a pull request synchronization, false means it's triggered for pull request merging or updating
+ IsForcePush bool
+ OldCommitID string
+ NewCommitID string
+}
+
// AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch,
// and generate new patch for testing as needed.
-func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, isSync bool, oldCommitID, newCommitID string) {
- log.Trace("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch)
+func AddTestPullRequestTask(opts TestPullRequestOptions) {
+ log.Trace("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests", opts.RepoID, opts.Branch)
graceful.GetManager().RunWithShutdownContext(func(ctx context.Context) {
// There is no sensible way to shut this down ":-("
// If you don't let it run all the way then you will lose data
// TODO: graceful: AddTestPullRequestTask needs to become a queue!
// GetUnmergedPullRequestsByHeadInfo() only return open and unmerged PR.
- prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(ctx, repoID, branch)
+ prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(ctx, opts.RepoID, opts.Branch)
if err != nil {
- log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err)
+ log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", opts.RepoID, opts.Branch, err)
return
}
@@ -397,26 +409,25 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
continue
}
- AddToTaskQueue(ctx, pr)
- comment, err := CreatePushPullComment(ctx, doer, pr, oldCommitID, newCommitID)
+ StartPullRequestCheckImmediately(ctx, pr)
+ comment, err := CreatePushPullComment(ctx, opts.Doer, pr, opts.OldCommitID, opts.NewCommitID)
if err == nil && comment != nil {
- notify_service.PullRequestPushCommits(ctx, doer, pr, comment)
+ notify_service.PullRequestPushCommits(ctx, opts.Doer, pr, comment)
}
}
- if isSync {
- requests := issues_model.PullRequestList(prs)
- if err = requests.LoadAttributes(ctx); err != nil {
+ if opts.IsSync {
+ if err = prs.LoadAttributes(ctx); err != nil {
log.Error("PullRequestList.LoadAttributes: %v", err)
}
- if invalidationErr := checkForInvalidation(ctx, requests, repoID, doer, branch); invalidationErr != nil {
+ if invalidationErr := checkForInvalidation(ctx, prs, opts.RepoID, opts.Doer, opts.Branch); invalidationErr != nil {
log.Error("checkForInvalidation: %v", invalidationErr)
}
if err == nil {
for _, pr := range prs {
objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)
- if newCommitID != "" && newCommitID != objectFormat.EmptyObjectID().String() {
- changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID)
+ if opts.NewCommitID != "" && opts.NewCommitID != objectFormat.EmptyObjectID().String() {
+ changed, err := checkIfPRContentChanged(ctx, pr, opts.OldCommitID, opts.NewCommitID)
if err != nil {
log.Error("checkIfPRContentChanged: %v", err)
}
@@ -432,12 +443,12 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
log.Error("GetFirstMatchProtectedBranchRule: %v", err)
}
if pb != nil && pb.DismissStaleApprovals {
- if err := DismissApprovalReviews(ctx, doer, pr); err != nil {
+ if err := DismissApprovalReviews(ctx, opts.Doer, pr); err != nil {
log.Error("DismissApprovalReviews: %v", err)
}
}
}
- if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, newCommitID); err != nil {
+ if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, opts.NewCommitID); err != nil {
log.Error("MarkReviewsAsNotStale: %v", err)
}
divergence, err := GetDiverging(ctx, pr)
@@ -451,21 +462,31 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
}
}
- notify_service.PullRequestSynchronized(ctx, doer, pr)
+ if !pr.IsWorkInProgress(ctx) {
+ reviewNotifiers, err := issue_service.PullRequestCodeOwnersReview(ctx, pr)
+ if err != nil {
+ log.Error("PullRequestCodeOwnersReview: %v", err)
+ }
+ if len(reviewNotifiers) > 0 {
+ issue_service.ReviewRequestNotify(ctx, pr.Issue, opts.Doer, reviewNotifiers)
+ }
+ }
+
+ notify_service.PullRequestSynchronized(ctx, opts.Doer, pr)
}
}
}
- log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch)
- prs, err = issues_model.GetUnmergedPullRequestsByBaseInfo(ctx, repoID, branch)
+ log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", opts.RepoID, opts.Branch)
+ prs, err = issues_model.GetUnmergedPullRequestsByBaseInfo(ctx, opts.RepoID, opts.Branch)
if err != nil {
- log.Error("Find pull requests [base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err)
+ log.Error("Find pull requests [base_repo_id: %d, base_branch: %s]: %v", opts.RepoID, opts.Branch, err)
return
}
for _, pr := range prs {
divergence, err := GetDiverging(ctx, pr)
if err != nil {
- if git_model.IsErrBranchNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
+ if git_model.IsErrBranchNotExist(err) && !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch)
} else {
log.Error("GetDiverging: %v", err)
@@ -476,7 +497,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
log.Error("UpdateCommitDivergence: %v", err)
}
}
- AddToTaskQueue(ctx, pr)
+ StartPullRequestCheckDelayable(ctx, pr)
}
})
}
@@ -503,14 +524,14 @@ func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest,
return false, fmt.Errorf("GetMergeBase: %w", err)
}
- cmd := git.NewCommand(ctx, "diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base)
+ cmd := git.NewCommand("diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base)
stdoutReader, stdoutWriter, err := os.Pipe()
if err != nil {
return false, fmt.Errorf("unable to open pipe for to run diff: %w", err)
}
stderr := new(bytes.Buffer)
- if err := cmd.Run(&git.RunOpts{
+ if err := cmd.Run(ctx, &git.RunOpts{
Dir: prCtx.tmpBasePath,
Stdout: stdoutWriter,
Stderr: stderr,
@@ -627,7 +648,7 @@ func UpdateRef(ctx context.Context, pr *issues_model.PullRequest) (err error) {
return err
}
- _, _, err = git.NewCommand(ctx, "update-ref").AddDynamicArguments(pr.GetGitRefName(), pr.HeadCommitID).RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath()})
+ _, _, err = git.NewCommand("update-ref").AddDynamicArguments(pr.GetGitRefName(), pr.HeadCommitID).RunStdString(ctx, &git.RunOpts{Dir: pr.BaseRepo.RepoPath()})
if err != nil {
log.Error("Unable to update ref in base repository for PR[%d] Error: %v", pr.ID, err)
}
@@ -635,43 +656,19 @@ func UpdateRef(ctx context.Context, pr *issues_model.PullRequest) (err error) {
return err
}
-type errlist []error
-
-func (errs errlist) Error() string {
- if len(errs) > 0 {
- var buf strings.Builder
- for i, err := range errs {
- if i > 0 {
- buf.WriteString(", ")
- }
- buf.WriteString(err.Error())
- }
- return buf.String()
- }
- return ""
-}
-
-// RetargetChildrenOnMerge retarget children pull requests on merge if possible
-func RetargetChildrenOnMerge(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) error {
- if setting.Repository.PullRequest.RetargetChildrenOnMerge && pr.BaseRepoID == pr.HeadRepoID {
- return RetargetBranchPulls(ctx, doer, pr.HeadRepoID, pr.HeadBranch, pr.BaseBranch)
- }
- return nil
-}
-
-// RetargetBranchPulls change target branch for all pull requests whose base branch is the branch
+// retargetBranchPulls change target branch for all pull requests whose base branch is the branch
// Both branch and targetBranch must be in the same repo (for security reasons)
-func RetargetBranchPulls(ctx context.Context, doer *user_model.User, repoID int64, branch, targetBranch string) error {
+func retargetBranchPulls(ctx context.Context, doer *user_model.User, repoID int64, branch, targetBranch string) error {
prs, err := issues_model.GetUnmergedPullRequestsByBaseInfo(ctx, repoID, branch)
if err != nil {
return err
}
- if err := issues_model.PullRequestList(prs).LoadAttributes(ctx); err != nil {
+ if err := prs.LoadAttributes(ctx); err != nil {
return err
}
- var errs errlist
+ var errs []error
for _, pr := range prs {
if err = pr.Issue.LoadRepo(ctx); err != nil {
errs = append(errs, err)
@@ -681,40 +678,75 @@ func RetargetBranchPulls(ctx context.Context, doer *user_model.User, repoID int6
errs = append(errs, err)
}
}
-
- if len(errs) > 0 {
- return errs
- }
- return nil
+ return errors.Join(errs...)
}
-// CloseBranchPulls close all the pull requests who's head branch is the branch
-func CloseBranchPulls(ctx context.Context, doer *user_model.User, repoID int64, branch string) error {
- prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(ctx, repoID, branch)
+// AdjustPullsCausedByBranchDeleted close all the pull requests who's head branch is the branch
+// Or Close all the plls who's base branch is the branch if setting.Repository.PullRequest.RetargetChildrenOnMerge is false.
+// If it's true, Retarget all these pulls to the default branch.
+func AdjustPullsCausedByBranchDeleted(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, branch string) error {
+ // branch as head branch
+ prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(ctx, repo.ID, branch)
if err != nil {
return err
}
- prs2, err := issues_model.GetUnmergedPullRequestsByBaseInfo(ctx, repoID, branch)
+ if err := prs.LoadAttributes(ctx); err != nil {
+ return err
+ }
+ prs.SetHeadRepo(repo)
+ if err := prs.LoadRepositories(ctx); err != nil {
+ return err
+ }
+
+ var errs []error
+ for _, pr := range prs {
+ if err = issue_service.CloseIssue(ctx, pr.Issue, doer, ""); err != nil && !issues_model.IsErrIssueIsClosed(err) && !issues_model.IsErrDependenciesLeft(err) {
+ errs = append(errs, err)
+ }
+ if err == nil {
+ if err := issues_model.AddDeletePRBranchComment(ctx, doer, pr.BaseRepo, pr.Issue.ID, pr.HeadBranch); err != nil {
+ log.Error("AddDeletePRBranchComment: %v", err)
+ errs = append(errs, err)
+ }
+ }
+ }
+
+ if setting.Repository.PullRequest.RetargetChildrenOnMerge {
+ if err := retargetBranchPulls(ctx, doer, repo.ID, branch, repo.DefaultBranch); err != nil {
+ log.Error("retargetBranchPulls failed: %v", err)
+ errs = append(errs, err)
+ }
+ return errors.Join(errs...)
+ }
+
+ // branch as base branch
+ prs, err = issues_model.GetUnmergedPullRequestsByBaseInfo(ctx, repo.ID, branch)
if err != nil {
return err
}
- prs = append(prs, prs2...)
- if err := issues_model.PullRequestList(prs).LoadAttributes(ctx); err != nil {
+ if err := prs.LoadAttributes(ctx); err != nil {
+ return err
+ }
+ prs.SetBaseRepo(repo)
+ if err := prs.LoadRepositories(ctx); err != nil {
return err
}
- var errs errlist
+ errs = nil
for _, pr := range prs {
- if err = issue_service.CloseIssue(ctx, pr.Issue, doer, ""); err != nil && !issues_model.IsErrPullWasClosed(err) && !issues_model.IsErrDependenciesLeft(err) {
+ if err = issues_model.AddDeletePRBranchComment(ctx, doer, pr.BaseRepo, pr.Issue.ID, pr.BaseBranch); err != nil {
+ log.Error("AddDeletePRBranchComment: %v", err)
errs = append(errs, err)
}
+ if err == nil {
+ if err = issue_service.CloseIssue(ctx, pr.Issue, doer, ""); err != nil && !issues_model.IsErrIssueIsClosed(err) && !issues_model.IsErrDependenciesLeft(err) {
+ errs = append(errs, err)
+ }
+ }
}
- if len(errs) > 0 {
- return errs
- }
- return nil
+ return errors.Join(errs...)
}
// CloseRepoBranchesPulls close all pull requests which head branches are in the given repository, but only whose base repo is not in the given repository
@@ -724,14 +756,14 @@ func CloseRepoBranchesPulls(ctx context.Context, doer *user_model.User, repo *re
return err
}
- var errs errlist
+ var errs []error
for _, branch := range branches {
- prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(ctx, repo.ID, branch.Name)
+ prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(ctx, repo.ID, branch)
if err != nil {
return err
}
- if err = issues_model.PullRequestList(prs).LoadAttributes(ctx); err != nil {
+ if err = prs.LoadAttributes(ctx); err != nil {
return err
}
@@ -741,16 +773,13 @@ func CloseRepoBranchesPulls(ctx context.Context, doer *user_model.User, repo *re
if pr.BaseRepoID == repo.ID {
continue
}
- if err = issue_service.CloseIssue(ctx, pr.Issue, doer, ""); err != nil && !issues_model.IsErrPullWasClosed(err) {
+ if err = issue_service.CloseIssue(ctx, pr.Issue, doer, ""); err != nil && !issues_model.IsErrIssueIsClosed(err) {
errs = append(errs, err)
}
}
}
- if len(errs) > 0 {
- return errs
- }
- return nil
+ return errors.Join(errs...)
}
var commitMessageTrailersPattern = regexp.MustCompile(`(?:^|\n\n)(?:[\w-]+[ \t]*:[^\n]+\n*(?:[ \t]+[^\n]+\n*)*)+$`)
@@ -916,12 +945,6 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ
return stringBuilder.String()
}
-// GetIssuesLastCommitStatus returns a map of issue ID to the most recent commit's latest status
-func GetIssuesLastCommitStatus(ctx context.Context, issues issues_model.IssueList) (map[int64]*git_model.CommitStatus, error) {
- _, lastStatus, err := GetIssuesAllCommitStatus(ctx, issues)
- return lastStatus, err
-}
-
// GetIssuesAllCommitStatus returns a map of issue ID to a list of all statuses for the most recent commit as well as a map of issue ID to only the commit's latest status
func GetIssuesAllCommitStatus(ctx context.Context, issues issues_model.IssueList) (map[int64][]*git_model.CommitStatus, map[int64]*git_model.CommitStatus, error) {
if err := issues.LoadPullRequests(ctx); err != nil {
@@ -975,7 +998,7 @@ func getAllCommitStatus(ctx context.Context, gitRepo *git.Repository, pr *issues
return nil, nil, shaErr
}
- statuses, _, err = git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptionsAll)
+ statuses, err = git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptionsAll)
lastStatus = git_model.CalcCommitStatus(statuses)
return statuses, lastStatus, err
}
diff --git a/services/pull/review.go b/services/pull/review.go
index 78723a58ae..5c80e7b338 100644
--- a/services/pull/review.go
+++ b/services/pull/review.go
@@ -395,7 +395,7 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string,
}
if review.Type != issues_model.ReviewTypeApprove && review.Type != issues_model.ReviewTypeReject {
- return nil, fmt.Errorf("not need to dismiss this review because it's type is not Approve or change request")
+ return nil, errors.New("not need to dismiss this review because it's type is not Approve or change request")
}
// load data for notify
@@ -405,7 +405,7 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string,
// Check if the review's repoID is the one we're currently expecting.
if review.Issue.RepoID != repoID {
- return nil, fmt.Errorf("reviews's repository is not the same as the one we expect")
+ return nil, errors.New("reviews's repository is not the same as the one we expect")
}
issue := review.Issue
diff --git a/services/pull/reviewer.go b/services/pull/reviewer.go
index bf0d8cb298..52f2f3401c 100644
--- a/services/pull/reviewer.go
+++ b/services/pull/reviewer.go
@@ -85,5 +85,5 @@ func GetReviewerTeams(ctx context.Context, repo *repo_model.Repository) ([]*orga
return nil, nil
}
- return organization.GetTeamsWithAccessToRepoUnit(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead, unit.TypePullRequests)
+ return organization.GetTeamsWithAccessToAnyRepoUnit(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead, unit.TypePullRequests)
}
diff --git a/services/pull/temp_repo.go b/services/pull/temp_repo.go
index e5753178b8..72406482e0 100644
--- a/services/pull/temp_repo.go
+++ b/services/pull/temp_repo.go
@@ -15,6 +15,7 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
)
@@ -27,7 +28,7 @@ const (
stagingBranch = "staging" // this is used for a working branch
)
-type prContext struct {
+type prTmpRepoContext struct {
context.Context
tmpBasePath string
pr *issues_model.PullRequest
@@ -35,7 +36,7 @@ type prContext struct {
errbuf *strings.Builder // any use should be preceded by a Reset and preferably after use
}
-func (ctx *prContext) RunOpts() *git.RunOpts {
+func (ctx *prTmpRepoContext) RunOpts() *git.RunOpts {
ctx.outbuf.Reset()
ctx.errbuf.Reset()
return &git.RunOpts{
@@ -47,7 +48,7 @@ func (ctx *prContext) RunOpts() *git.RunOpts {
// createTemporaryRepoForPR creates a temporary repo with "base" for pr.BaseBranch and "tracking" for pr.HeadBranch
// it also create a second base branch called "original_base"
-func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest) (prCtx *prContext, cancel context.CancelFunc, err error) {
+func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest) (prCtx *prTmpRepoContext, cancel context.CancelFunc, err error) {
if err := pr.LoadHeadRepo(ctx); err != nil {
log.Error("%-v LoadHeadRepo: %v", pr, err)
return nil, nil, fmt.Errorf("%v LoadHeadRepo: %w", pr, err)
@@ -73,23 +74,20 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
}
// Clone base repo.
- tmpBasePath, err := repo_module.CreateTemporaryPath("pull")
+ tmpBasePath, cleanup, err := repo_module.CreateTemporaryPath("pull")
if err != nil {
log.Error("CreateTemporaryPath[%-v]: %v", pr, err)
return nil, nil, err
}
- prCtx = &prContext{
+ cancel = cleanup
+
+ prCtx = &prTmpRepoContext{
Context: ctx,
tmpBasePath: tmpBasePath,
pr: pr,
outbuf: &strings.Builder{},
errbuf: &strings.Builder{},
}
- cancel = func() {
- if err := repo_module.RemoveTemporaryPath(tmpBasePath); err != nil {
- log.Error("Error whilst removing removing temporary repo for %-v: %v", pr, err)
- }
- }
baseRepoPath := pr.BaseRepo.RepoPath()
headRepoPath := pr.HeadRepo.RepoPath()
@@ -133,22 +131,22 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
return nil, nil, fmt.Errorf("Unable to add base repository to temporary repo [%s -> tmpBasePath]: %w", pr.BaseRepo.FullName(), err)
}
- if err := git.NewCommand(ctx, "remote", "add", "-t").AddDynamicArguments(pr.BaseBranch).AddArguments("-m").AddDynamicArguments(pr.BaseBranch).AddDynamicArguments("origin", baseRepoPath).
- Run(prCtx.RunOpts()); err != nil {
+ if err := git.NewCommand("remote", "add", "-t").AddDynamicArguments(pr.BaseBranch).AddArguments("-m").AddDynamicArguments(pr.BaseBranch).AddDynamicArguments("origin", baseRepoPath).
+ Run(ctx, prCtx.RunOpts()); err != nil {
log.Error("%-v Unable to add base repository as origin [%s -> %s]: %v\n%s\n%s", pr, pr.BaseRepo.FullName(), tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
cancel()
return nil, nil, fmt.Errorf("Unable to add base repository as origin [%s -> tmpBasePath]: %w\n%s\n%s", pr.BaseRepo.FullName(), err, prCtx.outbuf.String(), prCtx.errbuf.String())
}
- if err := git.NewCommand(ctx, "fetch", "origin").AddArguments(fetchArgs...).AddDashesAndList(pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).
- Run(prCtx.RunOpts()); err != nil {
+ if err := git.NewCommand("fetch", "origin").AddArguments(fetchArgs...).AddDashesAndList(pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).
+ Run(ctx, prCtx.RunOpts()); err != nil {
log.Error("%-v Unable to fetch origin base branch [%s:%s -> base, original_base in %s]: %v:\n%s\n%s", pr, pr.BaseRepo.FullName(), pr.BaseBranch, tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
cancel()
return nil, nil, fmt.Errorf("Unable to fetch origin base branch [%s:%s -> base, original_base in tmpBasePath]: %w\n%s\n%s", pr.BaseRepo.FullName(), pr.BaseBranch, err, prCtx.outbuf.String(), prCtx.errbuf.String())
}
- if err := git.NewCommand(ctx, "symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseBranch).
- Run(prCtx.RunOpts()); err != nil {
+ if err := git.NewCommand("symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseBranch).
+ Run(ctx, prCtx.RunOpts()); err != nil {
log.Error("%-v Unable to set HEAD as base branch in [%s]: %v\n%s\n%s", pr, tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
cancel()
return nil, nil, fmt.Errorf("Unable to set HEAD as base branch in tmpBasePath: %w\n%s\n%s", err, prCtx.outbuf.String(), prCtx.errbuf.String())
@@ -160,8 +158,8 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
return nil, nil, fmt.Errorf("Unable to add head base repository to temporary repo [%s -> tmpBasePath]: %w", pr.HeadRepo.FullName(), err)
}
- if err := git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteRepoName, headRepoPath).
- Run(prCtx.RunOpts()); err != nil {
+ if err := git.NewCommand("remote", "add").AddDynamicArguments(remoteRepoName, headRepoPath).
+ Run(ctx, prCtx.RunOpts()); err != nil {
log.Error("%-v Unable to add head repository as head_repo [%s -> %s]: %v\n%s\n%s", pr, pr.HeadRepo.FullName(), tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
cancel()
return nil, nil, fmt.Errorf("Unable to add head repository as head_repo [%s -> tmpBasePath]: %w\n%s\n%s", pr.HeadRepo.FullName(), err, prCtx.outbuf.String(), prCtx.errbuf.String())
@@ -178,10 +176,10 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
} else {
headBranch = pr.GetGitRefName()
}
- if err := git.NewCommand(ctx, "fetch").AddArguments(fetchArgs...).AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch).
- Run(prCtx.RunOpts()); err != nil {
+ if err := git.NewCommand("fetch").AddArguments(fetchArgs...).AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch).
+ Run(ctx, prCtx.RunOpts()); err != nil {
cancel()
- if !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
+ if !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
return nil, nil, git_model.ErrBranchNotExist{
BranchName: pr.HeadBranch,
}
diff --git a/services/pull/update.go b/services/pull/update.go
index abf7ad4509..b8f84e3d65 100644
--- a/services/pull/update.go
+++ b/services/pull/update.go
@@ -5,6 +5,7 @@ package pull
import (
"context"
+ "errors"
"fmt"
git_model "code.gitea.io/gitea/models/git"
@@ -23,7 +24,7 @@ import (
func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, message string, rebase bool) error {
if pr.Flow == issues_model.PullRequestFlowAGit {
// TODO: update of agit flow pull request's head branch is unsupported
- return fmt.Errorf("update of agit flow pull request's head branch is unsupported")
+ return errors.New("update of agit flow pull request's head branch is unsupported")
}
releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID))
@@ -40,14 +41,6 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
return fmt.Errorf("HeadBranch of PR %d is up to date", pr.Index)
}
- if rebase {
- defer func() {
- go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "")
- }()
-
- return updateHeadByRebaseOnToBase(ctx, pr, doer)
- }
-
if err := pr.LoadBaseRepo(ctx); err != nil {
log.Error("unable to load BaseRepo for %-v during update-by-merge: %v", pr, err)
return fmt.Errorf("unable to load BaseRepo for PR[%d] during update-by-merge: %w", pr.ID, err)
@@ -65,6 +58,22 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
return fmt.Errorf("unable to load HeadRepo for PR[%d] during update-by-merge: %w", pr.ID, err)
}
+ defer func() {
+ go AddTestPullRequestTask(TestPullRequestOptions{
+ RepoID: pr.BaseRepo.ID,
+ Doer: doer,
+ Branch: pr.BaseBranch,
+ IsSync: false,
+ IsForcePush: false,
+ OldCommitID: "",
+ NewCommitID: "",
+ })
+ }()
+
+ if rebase {
+ return updateHeadByRebaseOnToBase(ctx, pr, doer)
+ }
+
// TODO: FakePR: it is somewhat hacky, but it is the only way to "merge" at the moment
// ideally in the future the "merge" functions should be refactored to decouple from the PullRequest
// now use a fake reverse PR to switch head&base repos/branches
@@ -81,11 +90,6 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
}
_, err = doMergeAndPush(ctx, reversePR, doer, repo_model.MergeStyleMerge, "", message, repository.PushTriggerPRUpdateWithBase)
-
- defer func() {
- go AddTestPullRequestTask(doer, reversePR.HeadRepo.ID, reversePR.HeadBranch, false, "", "")
- }()
-
return err
}
diff --git a/services/pull/update_rebase.go b/services/pull/update_rebase.go
index 3e2a7be132..9ff062f99c 100644
--- a/services/pull/update_rebase.go
+++ b/services/pull/update_rebase.go
@@ -27,7 +27,7 @@ func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullReques
defer cancel()
// Determine the old merge-base before the rebase - we use this for LFS push later on
- oldMergeBase, _, _ := git.NewCommand(ctx, "merge-base").AddDashesAndList(baseBranch, trackingBranch).RunStdString(&git.RunOpts{Dir: mergeCtx.tmpBasePath})
+ oldMergeBase, _, _ := git.NewCommand("merge-base").AddDashesAndList(baseBranch, trackingBranch).RunStdString(ctx, &git.RunOpts{Dir: mergeCtx.tmpBasePath})
oldMergeBase = strings.TrimSpace(oldMergeBase)
// Rebase the tracking branch on to the base as the staging branch
@@ -62,7 +62,7 @@ func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullReques
headUser = pr.HeadRepo.Owner
}
- pushCmd := git.NewCommand(ctx, "push", "-f", "head_repo").
+ pushCmd := git.NewCommand("push", "-f", "head_repo").
AddDynamicArguments(stagingBranch + ":" + git.BranchPrefix + pr.HeadBranch)
// Push back to the head repository.
@@ -71,7 +71,7 @@ func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullReques
mergeCtx.outbuf.Reset()
mergeCtx.errbuf.Reset()
- if err := pushCmd.Run(&git.RunOpts{
+ if err := pushCmd.Run(ctx, &git.RunOpts{
Env: repo_module.FullPushingEnvironment(
headUser,
doer,
diff --git a/services/release/release.go b/services/release/release.go
index 835a5943b1..0b8a74252a 100644
--- a/services/release/release.go
+++ b/services/release/release.go
@@ -77,7 +77,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel
var created bool
// Only actual create when publish.
if !rel.IsDraft {
- if !gitRepo.IsTagExist(rel.TagName) {
+ if !gitrepo.IsTagExist(ctx, rel.Repo, rel.TagName) {
if err := rel.LoadAttributes(ctx); err != nil {
log.Error("LoadAttributes: %v", err)
return false, err
@@ -296,10 +296,7 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo
}
for _, attach := range attachments {
if attach.ReleaseID != rel.ID {
- return util.SilentWrap{
- Message: "delete attachment of release permission denied",
- Err: util.ErrPermissionDenied,
- }
+ return util.NewPermissionDeniedErrorf("delete attachment of release permission denied")
}
deletedUUIDs.Add(attach.UUID)
}
@@ -321,10 +318,7 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo
}
for _, attach := range attachments {
if attach.ReleaseID != rel.ID {
- return util.SilentWrap{
- Message: "update attachment of release permission denied",
- Err: util.ErrPermissionDenied,
- }
+ return util.NewPermissionDeniedErrorf("update attachment of release permission denied")
}
}
@@ -381,8 +375,8 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re
}
}
- if stdout, _, err := git.NewCommand(ctx, "tag", "-d").AddDashesAndList(rel.TagName).
- RunStdString(&git.RunOpts{Dir: repo.RepoPath()}); err != nil && !strings.Contains(err.Error(), "not found") {
+ if stdout, _, err := git.NewCommand("tag", "-d").AddDashesAndList(rel.TagName).
+ RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}); err != nil && !strings.Contains(err.Error(), "not found") {
log.Error("DeleteReleaseByID (git tag -d): %d in %v Failed:\nStdout: %s\nError: %v", rel.ID, repo, stdout, err)
return fmt.Errorf("git tag -d: %w", err)
}
diff --git a/services/release/release_test.go b/services/release/release_test.go
index 95a54832b9..36a9f667d6 100644
--- a/services/release/release_test.go
+++ b/services/release/release_test.go
@@ -250,9 +250,9 @@ func TestRelease_Update(t *testing.T) {
assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, []string{attach.UUID}, nil, nil))
assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release))
assert.Len(t, release.Attachments, 1)
- assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID)
- assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID)
- assert.EqualValues(t, attach.Name, release.Attachments[0].Name)
+ assert.Equal(t, attach.UUID, release.Attachments[0].UUID)
+ assert.Equal(t, release.ID, release.Attachments[0].ReleaseID)
+ assert.Equal(t, attach.Name, release.Attachments[0].Name)
// update the attachment name
assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, nil, nil, map[string]string{
@@ -261,9 +261,9 @@ func TestRelease_Update(t *testing.T) {
release.Attachments = nil
assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release))
assert.Len(t, release.Attachments, 1)
- assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID)
- assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID)
- assert.EqualValues(t, "test2.txt", release.Attachments[0].Name)
+ assert.Equal(t, attach.UUID, release.Attachments[0].UUID)
+ assert.Equal(t, release.ID, release.Attachments[0].ReleaseID)
+ assert.Equal(t, "test2.txt", release.Attachments[0].Name)
// delete the attachment
assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, nil, []string{attach.UUID}, nil))
diff --git a/services/repository/adopt.go b/services/repository/adopt.go
index e37909e7ab..2bd1c55de4 100644
--- a/services/repository/adopt.go
+++ b/services/repository/adopt.go
@@ -16,7 +16,6 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
@@ -28,18 +27,30 @@ import (
"github.com/gobwas/glob"
)
+func deleteFailedAdoptRepository(repoID int64) error {
+ return db.WithTx(db.DefaultContext, func(ctx context.Context) error {
+ if err := deleteDBRepository(ctx, repoID); err != nil {
+ return fmt.Errorf("deleteDBRepository: %w", err)
+ }
+ if err := git_model.DeleteRepoBranches(ctx, repoID); err != nil {
+ return fmt.Errorf("deleteRepoBranches: %w", err)
+ }
+ return repo_model.DeleteRepoReleases(ctx, repoID)
+ })
+}
+
// AdoptRepository adopts pre-existing repository files for the user/organization.
-func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
- if !doer.IsAdmin && !u.CanCreateRepo() {
+func AdoptRepository(ctx context.Context, doer, owner *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
+ if !doer.CanCreateRepoIn(owner) {
return nil, repo_model.ErrReachLimitOfRepo{
- Limit: u.MaxRepoCreation,
+ Limit: owner.MaxRepoCreation,
}
}
repo := &repo_model.Repository{
- OwnerID: u.ID,
- Owner: u,
- OwnerName: u.Name,
+ OwnerID: owner.ID,
+ Owner: owner,
+ OwnerName: owner.Name,
Name: opts.Name,
LowerName: strings.ToLower(opts.Name),
Description: opts.Description,
@@ -48,75 +59,67 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR
IsPrivate: opts.IsPrivate,
IsFsckEnabled: !opts.IsMirror,
CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch,
- Status: opts.Status,
+ Status: repo_model.RepositoryBeingMigrated,
IsEmpty: !opts.AutoInit,
}
- if err := db.WithTx(ctx, func(ctx context.Context) error {
- repoPath := repo_model.RepoPath(u.Name, repo.Name)
- isExist, err := util.IsExist(repoPath)
+ // 1 - create the repository database operations first
+ err := db.WithTx(ctx, func(ctx context.Context) error {
+ return createRepositoryInDB(ctx, doer, owner, repo, false)
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ // last - clean up if something goes wrong
+ // WARNING: Don't override all later err with local variables
+ defer func() {
if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
- return err
- }
- if !isExist {
- return repo_model.ErrRepoNotExist{
- OwnerName: u.Name,
- Name: repo.Name,
+ // we can not use the ctx because it maybe canceled or timeout
+ if errDel := deleteFailedAdoptRepository(repo.ID); errDel != nil {
+ log.Error("Failed to delete repository %s that could not be adopted: %v", repo.FullName(), errDel)
}
}
+ }()
- if err := CreateRepositoryByExample(ctx, doer, u, repo, true, false); err != nil {
- return err
- }
-
- // Re-fetch the repository from database before updating it (else it would
- // override changes that were done earlier with sql)
- if repo, err = repo_model.GetRepositoryByID(ctx, repo.ID); err != nil {
- return fmt.Errorf("getRepositoryByID: %w", err)
- }
-
- if err := adoptRepository(ctx, repoPath, repo, opts.DefaultBranch); err != nil {
- return fmt.Errorf("adoptRepository: %w", err)
- }
+ // Re-fetch the repository from database before updating it (else it would
+ // override changes that were done earlier with sql)
+ if repo, err = repo_model.GetRepositoryByID(ctx, repo.ID); err != nil {
+ return nil, fmt.Errorf("getRepositoryByID: %w", err)
+ }
- if err := repo_module.CheckDaemonExportOK(ctx, repo); err != nil {
- return fmt.Errorf("checkDaemonExportOK: %w", err)
- }
+ // 2 - adopt the repository from disk
+ if err = adoptRepository(ctx, repo, opts.DefaultBranch); err != nil {
+ return nil, fmt.Errorf("adoptRepository: %w", err)
+ }
- // Initialize Issue Labels if selected
- if len(opts.IssueLabels) > 0 {
- if err := repo_module.InitializeLabels(ctx, repo.ID, opts.IssueLabels, false); err != nil {
- return fmt.Errorf("InitializeLabels: %w", err)
- }
- }
+ // 3 - Update the git repository
+ if err = updateGitRepoAfterCreate(ctx, repo); err != nil {
+ return nil, fmt.Errorf("updateGitRepoAfterCreate: %w", err)
+ }
- if stdout, _, err := git.NewCommand(ctx, "update-server-info").
- RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
- log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
- return fmt.Errorf("CreateRepository(git update-server-info): %w", err)
- }
- return nil
- }); err != nil {
- return nil, err
+ // 4 - update repository status
+ repo.Status = repo_model.RepositoryReady
+ if err = repo_model.UpdateRepositoryColsWithAutoTime(ctx, repo, "status"); err != nil {
+ return nil, fmt.Errorf("UpdateRepositoryCols: %w", err)
}
- notify_service.AdoptRepository(ctx, doer, u, repo)
+ notify_service.AdoptRepository(ctx, doer, owner, repo)
return repo, nil
}
-func adoptRepository(ctx context.Context, repoPath string, repo *repo_model.Repository, defaultBranch string) (err error) {
- isExist, err := util.IsExist(repoPath)
+func adoptRepository(ctx context.Context, repo *repo_model.Repository, defaultBranch string) (err error) {
+ isExist, err := gitrepo.IsRepositoryExist(ctx, repo)
if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
+ log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err)
return err
}
if !isExist {
- return fmt.Errorf("adoptRepository: path does not already exist: %s", repoPath)
+ return fmt.Errorf("adoptRepository: path does not already exist: %s", repo.FullName())
}
- if err := repo_module.CreateDelegateHooks(repoPath); err != nil {
+ if err := gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
return fmt.Errorf("createDelegateHooks: %w", err)
}
@@ -193,8 +196,13 @@ func adoptRepository(ctx context.Context, repoPath string, repo *repo_model.Repo
return fmt.Errorf("setDefaultBranch: %w", err)
}
}
- if err = repo_module.UpdateRepository(ctx, repo, false); err != nil {
- return fmt.Errorf("updateRepository: %w", err)
+
+ if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_empty", "default_branch"); err != nil {
+ return fmt.Errorf("UpdateRepositoryCols: %w", err)
+ }
+
+ if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
+ log.Error("Failed to update size for repository: %v", err)
}
return nil
@@ -257,7 +265,7 @@ func checkUnadoptedRepositories(ctx context.Context, userName string, repoNamesT
}
return err
}
- repos, _, err := repo_model.GetUserRepositories(ctx, &repo_model.SearchRepoOptions{
+ repos, _, err := repo_model.GetUserRepositories(ctx, repo_model.SearchRepoOptions{
Actor: ctxUser,
Private: true,
ListOptions: db.ListOptions{
diff --git a/services/repository/adopt_test.go b/services/repository/adopt_test.go
index 123cedc1f2..86f586c748 100644
--- a/services/repository/adopt_test.go
+++ b/services/repository/adopt_test.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
)
@@ -28,7 +29,7 @@ func TestCheckUnadoptedRepositories_Add(t *testing.T) {
}
total := 30
- for i := 0; i < total; i++ {
+ for range total {
unadopted.add("something")
}
@@ -71,7 +72,7 @@ func TestListUnadoptedRepositories_ListOptions(t *testing.T) {
username := "user2"
unadoptedList := []string{path.Join(username, "unadopted1"), path.Join(username, "unadopted2")}
for _, unadopted := range unadoptedList {
- _ = os.Mkdir(path.Join(setting.RepoRootPath, unadopted+".git"), 0o755)
+ _ = os.Mkdir(filepath.Join(setting.RepoRootPath, unadopted+".git"), 0o755)
}
opts := db.ListOptions{Page: 1, PageSize: 1}
@@ -89,10 +90,36 @@ func TestListUnadoptedRepositories_ListOptions(t *testing.T) {
func TestAdoptRepository(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- assert.NoError(t, unittest.SyncDirs(filepath.Join(setting.RepoRootPath, "user2", "repo1.git"), filepath.Join(setting.RepoRootPath, "user2", "test-adopt.git")))
+
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- _, err := AdoptRepository(db.DefaultContext, user2, user2, CreateRepoOptions{Name: "test-adopt"})
+
+ // a successful adopt
+ destDir := filepath.Join(setting.RepoRootPath, user2.Name, "test-adopt.git")
+ assert.NoError(t, unittest.SyncDirs(filepath.Join(setting.RepoRootPath, user2.Name, "repo1.git"), destDir))
+
+ adoptedRepo, err := AdoptRepository(db.DefaultContext, user2, user2, CreateRepoOptions{Name: "test-adopt"})
assert.NoError(t, err)
repoTestAdopt := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: "test-adopt"})
assert.Equal(t, "sha1", repoTestAdopt.ObjectFormatName)
+
+ // just delete the adopted repo's db records
+ err = deleteFailedAdoptRepository(adoptedRepo.ID)
+ assert.NoError(t, err)
+
+ unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: "test-adopt"})
+
+ // a failed adopt because some mock data
+ // remove the hooks directory and create a file so that we cannot create the hooks successfully
+ _ = os.RemoveAll(filepath.Join(destDir, "hooks", "update.d"))
+ assert.NoError(t, os.WriteFile(filepath.Join(destDir, "hooks", "update.d"), []byte("tests"), os.ModePerm))
+
+ adoptedRepo, err = AdoptRepository(db.DefaultContext, user2, user2, CreateRepoOptions{Name: "test-adopt"})
+ assert.Error(t, err)
+ assert.Nil(t, adoptedRepo)
+
+ unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: "test-adopt"})
+
+ exist, err := util.IsExist(repo_model.RepoPath(user2.Name, "test-adopt"))
+ assert.NoError(t, err)
+ assert.True(t, exist) // the repository should be still in the disk
}
diff --git a/services/repository/archiver/archiver.go b/services/repository/archiver/archiver.go
index e1addbed33..a657e3884c 100644
--- a/services/repository/archiver/archiver.go
+++ b/services/repository/archiver/archiver.go
@@ -31,19 +31,20 @@ import (
// handle elsewhere.
type ArchiveRequest struct {
RepoID int64
- refName string
Type git.ArchiveType
CommitID string
+
+ archiveRefShortName string // the ref short name to download the archive, for example: "master", "v1.0.0", "commit id"
}
// ErrUnknownArchiveFormat request archive format is not supported
type ErrUnknownArchiveFormat struct {
- RequestFormat string
+ RequestNameType string
}
// Error implements error
func (err ErrUnknownArchiveFormat) Error() string {
- return fmt.Sprintf("unknown format: %s", err.RequestFormat)
+ return "unknown format: " + err.RequestNameType
}
// Is implements error
@@ -54,12 +55,12 @@ func (ErrUnknownArchiveFormat) Is(err error) bool {
// RepoRefNotFoundError is returned when a requested reference (commit, tag) was not found.
type RepoRefNotFoundError struct {
- RefName string
+ RefShortName string
}
// Error implements error.
func (e RepoRefNotFoundError) Error() string {
- return fmt.Sprintf("unrecognized repository reference: %s", e.RefName)
+ return "unrecognized repository reference: " + e.RefShortName
}
func (e RepoRefNotFoundError) Is(err error) bool {
@@ -67,43 +68,23 @@ func (e RepoRefNotFoundError) Is(err error) bool {
return ok
}
-func ParseFileName(uri string) (ext string, tp git.ArchiveType, err error) {
- switch {
- case strings.HasSuffix(uri, ".zip"):
- ext = ".zip"
- tp = git.ZIP
- case strings.HasSuffix(uri, ".tar.gz"):
- ext = ".tar.gz"
- tp = git.TARGZ
- case strings.HasSuffix(uri, ".bundle"):
- ext = ".bundle"
- tp = git.BUNDLE
- default:
- return "", 0, ErrUnknownArchiveFormat{RequestFormat: uri}
- }
- return ext, tp, nil
-}
-
// NewRequest creates an archival request, based on the URI. The
// resulting ArchiveRequest is suitable for being passed to Await()
// if it's determined that the request still needs to be satisfied.
-func NewRequest(repoID int64, repo *git.Repository, refName string, fileType git.ArchiveType) (*ArchiveRequest, error) {
- if fileType < git.ZIP || fileType > git.BUNDLE {
- return nil, ErrUnknownArchiveFormat{RequestFormat: fileType.String()}
- }
-
- r := &ArchiveRequest{
- RepoID: repoID,
- refName: refName,
- Type: fileType,
+func NewRequest(repoID int64, repo *git.Repository, archiveRefExt string) (*ArchiveRequest, error) {
+ // here the archiveRefShortName is not a clear ref, it could be a tag, branch or commit id
+ archiveRefShortName, archiveType := git.SplitArchiveNameType(archiveRefExt)
+ if archiveType == git.ArchiveUnknown {
+ return nil, ErrUnknownArchiveFormat{archiveRefExt}
}
// Get corresponding commit.
- commitID, err := repo.ConvertToGitID(r.refName)
+ commitID, err := repo.ConvertToGitID(archiveRefShortName)
if err != nil {
- return nil, RepoRefNotFoundError{RefName: r.refName}
+ return nil, RepoRefNotFoundError{RefShortName: archiveRefShortName}
}
+ r := &ArchiveRequest{RepoID: repoID, archiveRefShortName: archiveRefShortName, Type: archiveType}
r.CommitID = commitID.String()
return r, nil
}
@@ -111,11 +92,11 @@ func NewRequest(repoID int64, repo *git.Repository, refName string, fileType git
// GetArchiveName returns the name of the caller, based on the ref used by the
// caller to create this request.
func (aReq *ArchiveRequest) GetArchiveName() string {
- return strings.ReplaceAll(aReq.refName, "/", "-") + "." + aReq.Type.String()
+ return strings.ReplaceAll(aReq.archiveRefShortName, "/", "-") + "." + aReq.Type.String()
}
// Await awaits the completion of an ArchiveRequest. If the archive has
-// already been prepared the method returns immediately. Otherwise an archiver
+// already been prepared the method returns immediately. Otherwise, an archiver
// process will be started and its completion awaited. On success the returned
// RepoArchiver may be used to download the archive. Note that even if the
// context is cancelled/times out a started archiver will still continue to run
@@ -208,8 +189,8 @@ func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver
rd, w := io.Pipe()
defer func() {
- w.Close()
- rd.Close()
+ _ = w.Close()
+ _ = rd.Close()
}()
done := make(chan error, 1) // Ensure that there is some capacity which will ensure that the goroutine below can always finish
repo, err := repo_model.GetRepositoryByID(ctx, archiver.RepoID)
@@ -230,7 +211,7 @@ func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver
}
}()
- if archiver.Type == git.BUNDLE {
+ if archiver.Type == git.ArchiveBundle {
err = gitRepo.CreateBundle(
ctx,
archiver.CommitID,
diff --git a/services/repository/archiver/archiver_test.go b/services/repository/archiver/archiver_test.go
index 1d0c6e513d..87324ad38c 100644
--- a/services/repository/archiver/archiver_test.go
+++ b/services/repository/archiver/archiver_test.go
@@ -9,7 +9,6 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/services/contexttest"
_ "code.gitea.io/gitea/models/actions"
@@ -31,47 +30,47 @@ func TestArchive_Basic(t *testing.T) {
contexttest.LoadGitRepo(t, ctx)
defer ctx.Repo.GitRepo.Close()
- bogusReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP)
+ bogusReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
assert.NoError(t, err)
assert.NotNil(t, bogusReq)
- assert.EqualValues(t, firstCommit+".zip", bogusReq.GetArchiveName())
+ assert.Equal(t, firstCommit+".zip", bogusReq.GetArchiveName())
// Check a series of bogus requests.
// Step 1, valid commit with a bad extension.
- bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, 100)
+ bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".unknown")
assert.Error(t, err)
assert.Nil(t, bogusReq)
// Step 2, missing commit.
- bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "dbffff", git.ZIP)
+ bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "dbffff.zip")
assert.Error(t, err)
assert.Nil(t, bogusReq)
// Step 3, doesn't look like branch/tag/commit.
- bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "db", git.ZIP)
+ bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "db.zip")
assert.Error(t, err)
assert.Nil(t, bogusReq)
- bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "master", git.ZIP)
+ bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "master.zip")
assert.NoError(t, err)
assert.NotNil(t, bogusReq)
- assert.EqualValues(t, "master.zip", bogusReq.GetArchiveName())
+ assert.Equal(t, "master.zip", bogusReq.GetArchiveName())
- bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "test/archive", git.ZIP)
+ bogusReq, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, "test/archive.zip")
assert.NoError(t, err)
assert.NotNil(t, bogusReq)
- assert.EqualValues(t, "test-archive.zip", bogusReq.GetArchiveName())
+ assert.Equal(t, "test-archive.zip", bogusReq.GetArchiveName())
// Now two valid requests, firstCommit with valid extensions.
- zipReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP)
+ zipReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
assert.NoError(t, err)
assert.NotNil(t, zipReq)
- tgzReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.TARGZ)
+ tgzReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".tar.gz")
assert.NoError(t, err)
assert.NotNil(t, tgzReq)
- secondReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit, git.ZIP)
+ secondReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit+".bundle")
assert.NoError(t, err)
assert.NotNil(t, secondReq)
@@ -91,7 +90,7 @@ func TestArchive_Basic(t *testing.T) {
// Sleep two seconds to make sure the queue doesn't change.
time.Sleep(2 * time.Second)
- zipReq2, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP)
+ zipReq2, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
assert.NoError(t, err)
// This zipReq should match what's sitting in the queue, as we haven't
// let it release yet. From the consumer's point of view, this looks like
@@ -106,12 +105,12 @@ func TestArchive_Basic(t *testing.T) {
// Now we'll submit a request and TimedWaitForCompletion twice, before and
// after we release it. We should trigger both the timeout and non-timeout
// cases.
- timedReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit, git.TARGZ)
+ timedReq, err := NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, secondCommit+".tar.gz")
assert.NoError(t, err)
assert.NotNil(t, timedReq)
doArchive(db.DefaultContext, timedReq)
- zipReq2, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit, git.ZIP)
+ zipReq2, err = NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, firstCommit+".zip")
assert.NoError(t, err)
// Now, we're guaranteed to have released the original zipReq from the queue.
// Ensure that we don't get handed back the released entry somehow, but they
@@ -129,6 +128,6 @@ func TestArchive_Basic(t *testing.T) {
}
func TestErrUnknownArchiveFormat(t *testing.T) {
- err := ErrUnknownArchiveFormat{RequestFormat: "master"}
+ err := ErrUnknownArchiveFormat{RequestNameType: "xxx"}
assert.ErrorIs(t, err, ErrUnknownArchiveFormat{})
}
diff --git a/services/repository/avatar.go b/services/repository/avatar.go
index 15e51d4a25..26bf6da465 100644
--- a/services/repository/avatar.go
+++ b/services/repository/avatar.go
@@ -40,7 +40,7 @@ func UploadAvatar(ctx context.Context, repo *repo_model.Repository, data []byte)
// Users can upload the same image to other repo - prefix it with ID
// Then repo will be removed - only it avatar file will be removed
repo.Avatar = newAvatar
- if err := repo_model.UpdateRepositoryCols(ctx, repo, "avatar"); err != nil {
+ if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
return fmt.Errorf("UploadAvatar: Update repository avatar: %w", err)
}
@@ -77,7 +77,7 @@ func DeleteAvatar(ctx context.Context, repo *repo_model.Repository) error {
defer committer.Close()
repo.Avatar = ""
- if err := repo_model.UpdateRepositoryCols(ctx, repo, "avatar"); err != nil {
+ if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
return fmt.Errorf("DeleteAvatar: Update repository avatar: %w", err)
}
@@ -112,5 +112,5 @@ func generateAvatar(ctx context.Context, templateRepo, generateRepo *repo_model.
return err
}
- return repo_model.UpdateRepositoryCols(ctx, generateRepo, "avatar")
+ return repo_model.UpdateRepositoryColsNoAutoTime(ctx, generateRepo, "avatar")
}
diff --git a/services/repository/avatar_test.go b/services/repository/avatar_test.go
index bea820e85f..2dc5173eec 100644
--- a/services/repository/avatar_test.go
+++ b/services/repository/avatar_test.go
@@ -59,7 +59,7 @@ func TestDeleteAvatar(t *testing.T) {
err = DeleteAvatar(db.DefaultContext, repo)
assert.NoError(t, err)
- assert.Equal(t, "", repo.Avatar)
+ assert.Empty(t, repo.Avatar)
}
func TestGenerateAvatar(t *testing.T) {
diff --git a/services/repository/branch.go b/services/repository/branch.go
index fc476298ca..dd00ca7dcd 100644
--- a/services/repository/branch.go
+++ b/services/repository/branch.go
@@ -26,9 +26,11 @@ import (
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/queue"
repo_module "code.gitea.io/gitea/modules/repository"
+ "code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
webhook_module "code.gitea.io/gitea/modules/webhook"
+ actions_service "code.gitea.io/gitea/services/actions"
notify_service "code.gitea.io/gitea/services/notify"
release_service "code.gitea.io/gitea/services/release"
files_service "code.gitea.io/gitea/services/repository/files"
@@ -301,7 +303,7 @@ func SyncBranchesToDB(ctx context.Context, repoID, pusherID int64, branchNames,
// For other batches, it will hit optimization 4.
if len(branchNames) != len(commitIDs) {
- return fmt.Errorf("branchNames and commitIDs length not match")
+ return errors.New("branchNames and commitIDs length not match")
}
return db.WithTx(ctx, func(ctx context.Context) error {
@@ -408,14 +410,37 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, doer *user_m
return "target_exist", nil
}
- if gitRepo.IsBranchExist(to) {
+ if gitrepo.IsBranchExist(ctx, repo, to) {
return "target_exist", nil
}
- if !gitRepo.IsBranchExist(from) {
+ if !gitrepo.IsBranchExist(ctx, repo, from) {
return "from_not_exist", nil
}
+ perm, err := access_model.GetUserRepoPermission(ctx, repo, doer)
+ if err != nil {
+ return "", err
+ }
+
+ isDefault := from == repo.DefaultBranch
+ if isDefault && !perm.IsAdmin() {
+ return "", repo_model.ErrUserDoesNotHaveAccessToRepo{
+ UserID: doer.ID,
+ RepoName: repo.LowerName,
+ }
+ }
+
+ // If from == rule name, admins are allowed to modify them.
+ if protectedBranch, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, from); err != nil {
+ return "", err
+ } else if protectedBranch != nil && !perm.IsAdmin() {
+ return "", repo_model.ErrUserDoesNotHaveAccessToRepo{
+ UserID: doer.ID,
+ RepoName: repo.LowerName,
+ }
+ }
+
if err := git_model.RenameBranch(ctx, repo, from, to, func(ctx context.Context, isDefault bool) error {
err2 := gitRepo.RenameBranch(from, to)
if err2 != nil {
@@ -428,7 +453,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, doer *user_m
log.Error("DeleteCronTaskByRepo: %v", err)
}
// cancel running cron jobs of this repository and delete old schedules
- if err := actions_model.CancelPreviousJobs(
+ if err := actions_service.CancelPreviousJobs(
ctx,
repo.ID,
from,
@@ -489,7 +514,7 @@ func CanDeleteBranch(ctx context.Context, repo *repo_model.Repository, branchNam
}
// DeleteBranch delete branch
-func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, branchName string) error {
+func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, branchName string, pr *issues_model.PullRequest) error {
err := repo.MustNotBeArchived()
if err != nil {
return err
@@ -519,6 +544,12 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R
}
}
+ if pr != nil {
+ if err := issues_model.AddDeletePRBranchComment(ctx, doer, pr.BaseRepo, pr.Issue.ID, pr.HeadBranch); err != nil {
+ return fmt.Errorf("DeleteBranch: %v", err)
+ }
+ }
+
return gitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{
Force: true,
})
@@ -587,12 +618,12 @@ func AddAllRepoBranchesToSyncQueue(ctx context.Context) error {
return nil
}
-func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, newBranchName string) error {
+func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, newBranchName string) error {
if repo.DefaultBranch == newBranchName {
return nil
}
- if !gitRepo.IsBranchExist(newBranchName) {
+ if !gitrepo.IsBranchExist(ctx, repo, newBranchName) {
return git_model.ErrBranchNotExist{
BranchName: newBranchName,
}
@@ -609,7 +640,7 @@ func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, gitR
log.Error("DeleteCronTaskByRepo: %v", err)
}
// cancel running cron jobs of this repository and delete old schedules
- if err := actions_model.CancelPreviousJobs(
+ if err := actions_service.CancelPreviousJobs(
ctx,
repo.ID,
oldDefaultBranchName,
@@ -632,7 +663,81 @@ func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, gitR
}
}
+ // clear divergence cache
+ if err := DelRepoDivergenceFromCache(ctx, repo.ID); err != nil {
+ log.Error("DelRepoDivergenceFromCache: %v", err)
+ }
+
notify_service.ChangeDefaultBranch(ctx, repo)
return nil
}
+
+// BranchDivergingInfo contains the information about the divergence of a head branch to the base branch.
+type BranchDivergingInfo struct {
+ // whether the base branch contains new commits which are not in the head branch
+ BaseHasNewCommits bool
+
+ // behind/after are number of commits that the head branch is behind/after the base branch, it's 0 if it's unable to calculate.
+ // there could be a case that BaseHasNewCommits=true while the behind/after are both 0 (unable to calculate).
+ HeadCommitsBehind int
+ HeadCommitsAhead int
+}
+
+// GetBranchDivergingInfo returns the information about the divergence of a patch branch to the base branch.
+func GetBranchDivergingInfo(ctx reqctx.RequestContext, baseRepo *repo_model.Repository, baseBranch string, headRepo *repo_model.Repository, headBranch string) (*BranchDivergingInfo, error) {
+ headGitBranch, err := git_model.GetBranch(ctx, headRepo.ID, headBranch)
+ if err != nil {
+ return nil, err
+ }
+ if headGitBranch.IsDeleted {
+ return nil, git_model.ErrBranchNotExist{
+ BranchName: headBranch,
+ }
+ }
+ baseGitBranch, err := git_model.GetBranch(ctx, baseRepo.ID, baseBranch)
+ if err != nil {
+ return nil, err
+ }
+ if baseGitBranch.IsDeleted {
+ return nil, git_model.ErrBranchNotExist{
+ BranchName: baseBranch,
+ }
+ }
+
+ info := &BranchDivergingInfo{}
+ if headGitBranch.CommitID == baseGitBranch.CommitID {
+ return info, nil
+ }
+
+ // if the fork repo has new commits, this call will fail because they are not in the base repo
+ // exit status 128 - fatal: Invalid symmetric difference expression aaaaaaaaaaaa...bbbbbbbbbbbb
+ // so at the moment, we first check the update time, then check whether the fork branch has base's head
+ diff, err := git.GetDivergingCommits(ctx, baseRepo.RepoPath(), baseGitBranch.CommitID, headGitBranch.CommitID)
+ if err != nil {
+ info.BaseHasNewCommits = baseGitBranch.UpdatedUnix > headGitBranch.UpdatedUnix
+ if headRepo.IsFork && info.BaseHasNewCommits {
+ return info, nil
+ }
+ // if the base's update time is before the fork, check whether the base's head is in the fork
+ headGitRepo, err := gitrepo.RepositoryFromRequestContextOrOpen(ctx, headRepo)
+ if err != nil {
+ return nil, err
+ }
+ headCommit, err := headGitRepo.GetCommit(headGitBranch.CommitID)
+ if err != nil {
+ return nil, err
+ }
+ baseCommitID, err := git.NewIDFromString(baseGitBranch.CommitID)
+ if err != nil {
+ return nil, err
+ }
+ hasPreviousCommit, _ := headCommit.HasPreviousCommit(baseCommitID)
+ info.BaseHasNewCommits = !hasPreviousCommit
+ return info, nil
+ }
+
+ info.HeadCommitsBehind, info.HeadCommitsAhead = diff.Behind, diff.Ahead
+ info.BaseHasNewCommits = info.HeadCommitsBehind > 0
+ return info, nil
+}
diff --git a/services/repository/check.go b/services/repository/check.go
index acca15daf2..ffcd5ac749 100644
--- a/services/repository/check.go
+++ b/services/repository/check.go
@@ -86,10 +86,10 @@ func GitGcRepos(ctx context.Context, timeout time.Duration, args git.TrustedCmdA
// GitGcRepo calls 'git gc' to remove unnecessary files and optimize the local repository
func GitGcRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Duration, args git.TrustedCmdArgs) error {
log.Trace("Running git gc on %-v", repo)
- command := git.NewCommand(ctx, "gc").AddArguments(args...)
+ command := git.NewCommand("gc").AddArguments(args...)
var stdout string
var err error
- stdout, _, err = command.RunStdString(&git.RunOpts{Timeout: timeout, Dir: repo.RepoPath()})
+ stdout, _, err = command.RunStdString(ctx, &git.RunOpts{Timeout: timeout, Dir: repo.RepoPath()})
if err != nil {
log.Error("Repository garbage collection failed for %-v. Stdout: %s\nError: %v", repo, stdout, err)
desc := fmt.Sprintf("Repository garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err)
@@ -162,7 +162,7 @@ func DeleteMissingRepositories(ctx context.Context, doer *user_model.User) error
default:
}
log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID)
- if err := DeleteRepositoryDirectly(ctx, doer, repo.ID); err != nil {
+ if err := DeleteRepositoryDirectly(ctx, repo.ID); err != nil {
log.Error("Failed to DeleteRepository %-v: Error: %v", repo, err)
if err2 := system_model.CreateRepositoryNotice("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err); err2 != nil {
log.Error("CreateRepositoryNotice: %v", err)
diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go
index f369a303e6..fa7a89882a 100644
--- a/services/repository/commitstatus/commitstatus.go
+++ b/services/repository/commitstatus/commitstatus.go
@@ -14,17 +14,17 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/cache"
+ "code.gitea.io/gitea/modules/commitstatus"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
- api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/services/notify"
)
func getCacheKey(repoID int64, brancheName string) string {
- hashBytes := sha256.Sum256([]byte(fmt.Sprintf("%d:%s", repoID, brancheName)))
+ hashBytes := sha256.Sum256(fmt.Appendf(nil, "%d:%s", repoID, brancheName))
return fmt.Sprintf("commit_status:%x", hashBytes)
}
@@ -47,7 +47,7 @@ func getCommitStatusCache(repoID int64, branchName string) *commitStatusCacheVal
return nil
}
-func updateCommitStatusCache(repoID int64, branchName string, state api.CommitStatusState, targetURL string) error {
+func updateCommitStatusCache(repoID int64, branchName string, state commitstatus.CommitStatusState, targetURL string) error {
c := cache.GetCache()
bs, err := json.Marshal(commitStatusCacheValue{
State: state.String(),
@@ -127,7 +127,7 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep
for i, repo := range repos {
if cv := getCommitStatusCache(repo.ID, repo.DefaultBranch); cv != nil {
results[i] = &git_model.CommitStatus{
- State: api.CommitStatusState(cv.State),
+ State: commitstatus.CommitStatusState(cv.State),
TargetURL: cv.TargetURL,
}
} else {
diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go
index b0748f8ee3..a4ae505313 100644
--- a/services/repository/contributors_graph.go
+++ b/services/repository/contributors_graph.go
@@ -125,13 +125,13 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int
_ = stdoutWriter.Close()
}()
- gitCmd := git.NewCommand(repo.Ctx, "log", "--shortstat", "--no-merges", "--pretty=format:---%n%aN%n%aE%n%as", "--reverse")
+ gitCmd := git.NewCommand("log", "--shortstat", "--no-merges", "--pretty=format:---%n%aN%n%aE%n%as", "--reverse")
// AddOptionFormat("--max-count=%d", limit)
gitCmd.AddDynamicArguments(baseCommit.ID.String())
var extendedCommitStats []*ExtendedCommitStats
stderr := new(strings.Builder)
- err = gitCmd.Run(&git.RunOpts{
+ err = gitCmd.Run(repo.Ctx, &git.RunOpts{
Dir: repo.Path,
Stdout: stdoutWriter,
Stderr: stderr,
diff --git a/services/repository/contributors_graph_test.go b/services/repository/contributors_graph_test.go
index 6db93f6a64..7d32b1c931 100644
--- a/services/repository/contributors_graph_test.go
+++ b/services/repository/contributors_graph_test.go
@@ -38,14 +38,14 @@ func TestRepository_ContributorsGraph(t *testing.T) {
keys = append(keys, k)
}
slices.Sort(keys)
- assert.EqualValues(t, []string{
+ assert.Equal(t, []string{
"ethantkoenig@gmail.com",
"jimmy.praet@telenet.be",
"jon@allspice.io",
"total", // generated summary
}, keys)
- assert.EqualValues(t, &ContributorData{
+ assert.Equal(t, &ContributorData{
Name: "Ethan Koenig",
AvatarLink: "/assets/img/avatar_default.png",
TotalCommits: 1,
@@ -58,7 +58,7 @@ func TestRepository_ContributorsGraph(t *testing.T) {
},
},
}, data["ethantkoenig@gmail.com"])
- assert.EqualValues(t, &ContributorData{
+ assert.Equal(t, &ContributorData{
Name: "Total",
AvatarLink: "",
TotalCommits: 3,
diff --git a/services/repository/create.go b/services/repository/create.go
index a3199f2a40..bed02e5d7e 100644
--- a/services/repository/create.go
+++ b/services/repository/create.go
@@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
+ system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
@@ -28,7 +29,6 @@ import (
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates/vars"
- "code.gitea.io/gitea/modules/util"
)
// CreateRepoOptions contains the create repository options
@@ -52,7 +52,7 @@ type CreateRepoOptions struct {
ObjectFormatName string
}
-func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, repoPath string, opts CreateRepoOptions) error {
+func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir string, opts CreateRepoOptions) error {
commitTimeStr := time.Now().Format(time.RFC3339)
authorSig := repo.Owner.NewGitSig()
@@ -67,8 +67,8 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir,
)
// Clone to temporary path and do the init commit.
- if stdout, _, err := git.NewCommand(ctx, "clone").AddDynamicArguments(repoPath, tmpDir).
- RunStdString(&git.RunOpts{Dir: "", Env: env}); err != nil {
+ if stdout, _, err := git.NewCommand("clone").AddDynamicArguments(repo.RepoPath(), tmpDir).
+ RunStdString(ctx, &git.RunOpts{Dir: "", Env: env}); err != nil {
log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err)
return fmt.Errorf("git clone: %w", err)
}
@@ -79,7 +79,7 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir,
return fmt.Errorf("GetRepoInitFile[%s]: %w", opts.Readme, err)
}
- cloneLink := repo.CloneLink()
+ cloneLink := repo.CloneLink(ctx, nil /* no doer so do not generate user-related SSH link */)
match := map[string]string{
"Name": repo.Name,
"Description": repo.Description,
@@ -100,8 +100,8 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir,
// .gitignore
if len(opts.Gitignores) > 0 {
var buf bytes.Buffer
- names := strings.Split(opts.Gitignores, ",")
- for _, name := range names {
+ names := strings.SplitSeq(opts.Gitignores, ",")
+ for name := range names {
data, err = options.Gitignore(name)
if err != nil {
return fmt.Errorf("GetRepoInitFile[%s]: %w", name, err)
@@ -139,24 +139,23 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir,
}
// InitRepository initializes README and .gitignore if needed.
-func initRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) {
- if err = repo_module.CheckInitRepository(ctx, repo.OwnerName, repo.Name, opts.ObjectFormatName); err != nil {
- return err
+func initRepository(ctx context.Context, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) {
+ // Init git bare new repository.
+ if err = git.InitRepository(ctx, repo.RepoPath(), true, repo.ObjectFormatName); err != nil {
+ return fmt.Errorf("git.InitRepository: %w", err)
+ } else if err = gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
+ return fmt.Errorf("createDelegateHooks: %w", err)
}
// Initialize repository according to user's choice.
if opts.AutoInit {
- tmpDir, err := os.MkdirTemp(os.TempDir(), "gitea-"+repo.Name)
+ tmpDir, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("repos-" + repo.Name)
if err != nil {
- return fmt.Errorf("Failed to create temp dir for repository %s: %w", repo.RepoPath(), err)
+ return fmt.Errorf("failed to create temp dir for repository %s: %w", repo.FullName(), err)
}
- defer func() {
- if err := util.RemoveAll(tmpDir); err != nil {
- log.Warn("Unable to remove temporary directory: %s: Error: %v", tmpDir, err)
- }
- }()
+ defer cleanup()
- if err = prepareRepoCommit(ctx, repo, tmpDir, repoPath, opts); err != nil {
+ if err = prepareRepoCommit(ctx, repo, tmpDir, opts); err != nil {
return fmt.Errorf("prepareRepoCommit: %w", err)
}
@@ -192,18 +191,25 @@ func initRepository(ctx context.Context, repoPath string, u *user_model.User, re
}
}
- if err = UpdateRepository(ctx, repo, false); err != nil {
+ if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_empty", "default_branch", "default_wiki_branch"); err != nil {
return fmt.Errorf("updateRepository: %w", err)
}
+ if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
+ log.Error("Failed to update size for repository: %v", err)
+ }
+
return nil
}
// CreateRepositoryDirectly creates a repository for the user/organization.
-func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
- if !doer.IsAdmin && !u.CanCreateRepo() {
+// if needsUpdateToReady is true, it will update the repository status to ready when success
+func CreateRepositoryDirectly(ctx context.Context, doer, owner *user_model.User,
+ opts CreateRepoOptions, needsUpdateToReady bool,
+) (*repo_model.Repository, error) {
+ if !doer.CanCreateRepoIn(owner) {
return nil, repo_model.ErrReachLimitOfRepo{
- Limit: u.MaxRepoCreation,
+ Limit: owner.MaxRepoCreation,
}
}
@@ -223,9 +229,9 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
}
repo := &repo_model.Repository{
- OwnerID: u.ID,
- Owner: u,
- OwnerName: u.Name,
+ OwnerID: owner.ID,
+ Owner: owner,
+ OwnerName: owner.Name,
Name: opts.Name,
LowerName: strings.ToLower(opts.Name),
Description: opts.Description,
@@ -244,101 +250,91 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
ObjectFormatName: opts.ObjectFormatName,
}
- var rollbackRepo *repo_model.Repository
-
- if err := db.WithTx(ctx, func(ctx context.Context) error {
- if err := CreateRepositoryByExample(ctx, doer, u, repo, false, false); err != nil {
- return err
- }
-
- // No need for init mirror.
- if opts.IsMirror {
- return nil
- }
+ // 1 - create the repository database operations first
+ err := db.WithTx(ctx, func(ctx context.Context) error {
+ return createRepositoryInDB(ctx, doer, owner, repo, false)
+ })
+ if err != nil {
+ return nil, err
+ }
- repoPath := repo_model.RepoPath(u.Name, repo.Name)
- isExist, err := util.IsExist(repoPath)
+ // last - clean up if something goes wrong
+ // WARNING: Don't override all later err with local variables
+ defer func() {
if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
- return err
- }
- if isExist {
- // repo already exists - We have two or three options.
- // 1. We fail stating that the directory exists
- // 2. We create the db repository to go with this data and adopt the git repo
- // 3. We delete it and start afresh
- //
- // Previously Gitea would just delete and start afresh - this was naughty.
- // So we will now fail and delegate to other functionality to adopt or delete
- log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath)
- return repo_model.ErrRepoFilesAlreadyExist{
- Uname: u.Name,
- Name: repo.Name,
- }
+ // we can not use the ctx because it maybe canceled or timeout
+ cleanupRepository(repo.ID)
}
+ }()
- if err = initRepository(ctx, repoPath, doer, repo, opts); err != nil {
- if err2 := util.RemoveAll(repoPath); err2 != nil {
- log.Error("initRepository: %v", err)
- return fmt.Errorf(
- "delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2)
- }
- return fmt.Errorf("initRepository: %w", err)
- }
+ // No need for init mirror.
+ if opts.IsMirror {
+ return repo, nil
+ }
- // Initialize Issue Labels if selected
- if len(opts.IssueLabels) > 0 {
- if err = repo_module.InitializeLabels(ctx, repo.ID, opts.IssueLabels, false); err != nil {
- rollbackRepo = repo
- rollbackRepo.OwnerID = u.ID
- return fmt.Errorf("InitializeLabels: %w", err)
- }
+ // 2 - check whether the repository with the same storage exists
+ var isExist bool
+ isExist, err = gitrepo.IsRepositoryExist(ctx, repo)
+ if err != nil {
+ log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err)
+ return nil, err
+ }
+ if isExist {
+ log.Error("Files already exist in %s and we are not going to adopt or delete.", repo.FullName())
+ // Don't return directly, we need err in defer to cleanupRepository
+ err = repo_model.ErrRepoFilesAlreadyExist{
+ Uname: repo.OwnerName,
+ Name: repo.Name,
}
+ return nil, err
+ }
- if err := repo_module.CheckDaemonExportOK(ctx, repo); err != nil {
- return fmt.Errorf("checkDaemonExportOK: %w", err)
- }
+ // 3 - init git repository in storage
+ if err = initRepository(ctx, doer, repo, opts); err != nil {
+ return nil, fmt.Errorf("initRepository: %w", err)
+ }
- if stdout, _, err := git.NewCommand(ctx, "update-server-info").
- RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
- log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
- rollbackRepo = repo
- rollbackRepo.OwnerID = u.ID
- return fmt.Errorf("CreateRepository(git update-server-info): %w", err)
+ // 4 - Initialize Issue Labels if selected
+ if len(opts.IssueLabels) > 0 {
+ if err = repo_module.InitializeLabels(ctx, repo.ID, opts.IssueLabels, false); err != nil {
+ return nil, fmt.Errorf("InitializeLabels: %w", err)
}
+ }
- // update licenses
- var licenses []string
- if len(opts.License) > 0 {
- licenses = append(licenses, ConvertLicenseName(opts.License))
+ // 5 - Update the git repository
+ if err = updateGitRepoAfterCreate(ctx, repo); err != nil {
+ return nil, fmt.Errorf("updateGitRepoAfterCreate: %w", err)
+ }
- stdout, _, err := git.NewCommand(ctx, "rev-parse", "HEAD").RunStdString(&git.RunOpts{Dir: repoPath})
- if err != nil {
- log.Error("CreateRepository(git rev-parse HEAD) in %v: Stdout: %s\nError: %v", repo, stdout, err)
- rollbackRepo = repo
- rollbackRepo.OwnerID = u.ID
- return fmt.Errorf("CreateRepository(git rev-parse HEAD): %w", err)
- }
- if err := repo_model.UpdateRepoLicenses(ctx, repo, stdout, licenses); err != nil {
- return err
- }
+ // 6 - update licenses
+ var licenses []string
+ if len(opts.License) > 0 {
+ licenses = append(licenses, opts.License)
+
+ var stdout string
+ stdout, _, err = git.NewCommand("rev-parse", "HEAD").RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
+ if err != nil {
+ log.Error("CreateRepository(git rev-parse HEAD) in %v: Stdout: %s\nError: %v", repo, stdout, err)
+ return nil, fmt.Errorf("CreateRepository(git rev-parse HEAD): %w", err)
}
- return nil
- }); err != nil {
- if rollbackRepo != nil {
- if errDelete := DeleteRepositoryDirectly(ctx, doer, rollbackRepo.ID); errDelete != nil {
- log.Error("Rollback deleteRepository: %v", errDelete)
- }
+ if err = repo_model.UpdateRepoLicenses(ctx, repo, stdout, licenses); err != nil {
+ return nil, err
}
+ }
- return nil, err
+ // 7 - update repository status to be ready
+ if needsUpdateToReady {
+ repo.Status = repo_model.RepositoryReady
+ if err = repo_model.UpdateRepositoryColsWithAutoTime(ctx, repo, "status"); err != nil {
+ return nil, fmt.Errorf("UpdateRepositoryCols: %w", err)
+ }
}
return repo, nil
}
-// CreateRepositoryByExample creates a repository for the user/organization.
-func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, overwriteOrAdopt, isFork bool) (err error) {
+// createRepositoryInDB creates a repository for the user/organization.
+func createRepositoryInDB(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, isFork bool) (err error) {
if err = repo_model.IsUsableRepoName(repo.Name); err != nil {
return err
}
@@ -353,20 +349,6 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
}
}
- repoPath := repo_model.RepoPath(u.Name, repo.Name)
- isExist, err := util.IsExist(repoPath)
- if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
- return err
- }
- if !overwriteOrAdopt && isExist {
- log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath)
- return repo_model.ErrRepoFilesAlreadyExist{
- Uname: u.Name,
- Name: repo.Name,
- }
- }
-
if err = db.Insert(ctx, repo); err != nil {
return err
}
@@ -386,7 +368,8 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
}
units := make([]repo_model.RepoUnit, 0, len(defaultUnits))
for _, tp := range defaultUnits {
- if tp == unit.TypeIssues {
+ switch tp {
+ case unit.TypeIssues:
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: tp,
@@ -396,7 +379,7 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
EnableDependencies: setting.Service.DefaultEnableDependencies,
},
})
- } else if tp == unit.TypePullRequests {
+ case unit.TypePullRequests:
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: tp,
@@ -406,13 +389,13 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
AllowRebaseUpdate: true,
},
})
- } else if tp == unit.TypeProjects {
+ case unit.TypeProjects:
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &repo_model.ProjectsConfig{ProjectsMode: repo_model.ProjectsModeAll},
})
- } else {
+ default:
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: tp,
@@ -474,3 +457,26 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
return nil
}
+
+func cleanupRepository(repoID int64) {
+ if errDelete := DeleteRepositoryDirectly(db.DefaultContext, repoID); errDelete != nil {
+ log.Error("cleanupRepository failed: %v", errDelete)
+ // add system notice
+ if err := system_model.CreateRepositoryNotice("DeleteRepositoryDirectly failed when cleanup repository: %v", errDelete); err != nil {
+ log.Error("CreateRepositoryNotice: %v", err)
+ }
+ }
+}
+
+func updateGitRepoAfterCreate(ctx context.Context, repo *repo_model.Repository) error {
+ if err := checkDaemonExportOK(ctx, repo); err != nil {
+ return fmt.Errorf("checkDaemonExportOK: %w", err)
+ }
+
+ if stdout, _, err := git.NewCommand("update-server-info").
+ RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}); err != nil {
+ log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
+ return fmt.Errorf("CreateRepository(git update-server-info): %w", err)
+ }
+ return nil
+}
diff --git a/services/repository/create_test.go b/services/repository/create_test.go
new file mode 100644
index 0000000000..fe464c1441
--- /dev/null
+++ b/services/repository/create_test.go
@@ -0,0 +1,57 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repository
+
+import (
+ "os"
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/util"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestCreateRepositoryDirectly(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ // a successful creating repository
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+
+ createdRepo, err := CreateRepositoryDirectly(git.DefaultContext, user2, user2, CreateRepoOptions{
+ Name: "created-repo",
+ }, true)
+ assert.NoError(t, err)
+ assert.NotNil(t, createdRepo)
+
+ exist, err := util.IsExist(repo_model.RepoPath(user2.Name, createdRepo.Name))
+ assert.NoError(t, err)
+ assert.True(t, exist)
+
+ unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: createdRepo.Name})
+
+ err = DeleteRepositoryDirectly(db.DefaultContext, createdRepo.ID)
+ assert.NoError(t, err)
+
+ // a failed creating because some mock data
+ // create the repository directory so that the creation will fail after database record created.
+ assert.NoError(t, os.MkdirAll(repo_model.RepoPath(user2.Name, createdRepo.Name), os.ModePerm))
+
+ createdRepo2, err := CreateRepositoryDirectly(db.DefaultContext, user2, user2, CreateRepoOptions{
+ Name: "created-repo",
+ }, true)
+ assert.Nil(t, createdRepo2)
+ assert.Error(t, err)
+
+ // assert the cleanup is successful
+ unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: createdRepo.Name})
+
+ exist, err = util.IsExist(repo_model.RepoPath(user2.Name, createdRepo.Name))
+ assert.NoError(t, err)
+ assert.False(t, exist)
+}
diff --git a/services/repository/delete.go b/services/repository/delete.go
index 2166b4dd5c..c48d6e1d56 100644
--- a/services/repository/delete.go
+++ b/services/repository/delete.go
@@ -14,6 +14,7 @@ import (
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
+ packages_model "code.gitea.io/gitea/models/packages"
access_model "code.gitea.io/gitea/models/perm/access"
project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo"
@@ -22,17 +23,33 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
actions_module "code.gitea.io/gitea/modules/actions"
+ "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/storage"
+ actions_service "code.gitea.io/gitea/services/actions"
asymkey_service "code.gitea.io/gitea/services/asymkey"
+ issue_service "code.gitea.io/gitea/services/issue"
"xorm.io/builder"
)
+func deleteDBRepository(ctx context.Context, repoID int64) error {
+ if cnt, err := db.GetEngine(ctx).ID(repoID).Delete(&repo_model.Repository{}); err != nil {
+ return err
+ } else if cnt != 1 {
+ return repo_model.ErrRepoNotExist{
+ ID: repoID,
+ OwnerName: "",
+ Name: "",
+ }
+ }
+ return nil
+}
+
// DeleteRepository deletes a repository for a user or organization.
// make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock)
-func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID int64, ignoreOrgTeams ...bool) error {
+func DeleteRepositoryDirectly(ctx context.Context, repoID int64, ignoreOrgTeams ...bool) error {
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
@@ -80,14 +97,8 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
}
needRewriteKeysFile := deleted > 0
- if cnt, err := sess.ID(repoID).Delete(&repo_model.Repository{}); err != nil {
+ if err := deleteDBRepository(ctx, repoID); err != nil {
return err
- } else if cnt != 1 {
- return repo_model.ErrRepoNotExist{
- ID: repoID,
- OwnerName: "",
- Name: "",
- }
}
if org != nil && org.IsOrganization() {
@@ -124,6 +135,14 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
return err
}
+ // CleanupEphemeralRunnersByPickedTaskOfRepo deletes ephemeral global/org/user that have started any task of this repo
+ // The cannot pick a second task hardening for ephemeral runners expect that task objects remain available until runner deletion
+ // This method will delete affected ephemeral global/org/user runners
+ // &actions_model.ActionRunner{RepoID: repoID} does only handle ephemeral repository runners
+ if err := actions_service.CleanupEphemeralRunnersByPickedTaskOfRepo(ctx, repoID); err != nil {
+ return fmt.Errorf("cleanupEphemeralRunners: %w", err)
+ }
+
if err := db.DeleteBeans(ctx,
&access_model.Access{RepoID: repo.ID},
&activities_model.Action{RepoID: repo.ID},
@@ -158,6 +177,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
&actions_model.ActionSchedule{RepoID: repoID},
&actions_model.ActionArtifact{RepoID: repoID},
&actions_model.ActionRunnerToken{RepoID: repoID},
+ &issues_model.IssuePin{RepoID: repoID},
); err != nil {
return fmt.Errorf("deleteBeans: %w", err)
}
@@ -174,7 +194,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
// Delete Issues and related objects
var attachmentPaths []string
- if attachmentPaths, err = issues_model.DeleteIssuesByRepoID(ctx, repoID); err != nil {
+ if attachmentPaths, err = issue_service.DeleteIssuesByRepoID(ctx, repoID); err != nil {
return err
}
@@ -266,6 +286,11 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
return err
}
+ // unlink packages linked to this repository
+ if err = packages_model.UnlinkRepositoryFromAllPackages(ctx, repoID); err != nil {
+ return err
+ }
+
if err = committer.Commit(); err != nil {
return err
}
@@ -282,8 +307,13 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
// we delete the file but the database rollback, the repository will be broken.
// Remove repository files.
- repoPath := repo.RepoPath()
- system_model.RemoveAllWithNotice(ctx, "Delete repository files", repoPath)
+ if err := gitrepo.DeleteRepository(ctx, repo); err != nil {
+ desc := fmt.Sprintf("Delete repository files [%s]: %v", repo.FullName(), err)
+ // Note we use the db.DefaultContext here rather than passing in a context as the context may be cancelled
+ if err = system_model.CreateNotice(db.DefaultContext, system_model.NoticeRepository, desc); err != nil {
+ log.Error("CreateRepositoryNotice: %v", err)
+ }
+ }
// Remove wiki files
if repo.HasWiki() {
@@ -345,7 +375,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
// DeleteOwnerRepositoriesDirectly calls DeleteRepositoryDirectly for all repos of the given owner
func DeleteOwnerRepositoriesDirectly(ctx context.Context, owner *user_model.User) error {
for {
- repos, _, err := repo_model.GetUserRepositories(ctx, &repo_model.SearchRepoOptions{
+ repos, _, err := repo_model.GetUserRepositories(ctx, repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: repo_model.RepositoryListDefaultPageSize,
Page: 1,
@@ -361,7 +391,7 @@ func DeleteOwnerRepositoriesDirectly(ctx context.Context, owner *user_model.User
break
}
for _, repo := range repos {
- if err := DeleteRepositoryDirectly(ctx, owner, repo.ID); err != nil {
+ if err := DeleteRepositoryDirectly(ctx, repo.ID); err != nil {
return fmt.Errorf("unable to delete repository %s for %s[%d]. Error: %w", repo.Name, owner.Name, owner.ID, err)
}
}
diff --git a/services/repository/files/cherry_pick.go b/services/repository/files/cherry_pick.go
index 10545e9e03..6818bb343d 100644
--- a/services/repository/files/cherry_pick.go
+++ b/services/repository/files/cherry_pick.go
@@ -5,6 +5,7 @@ package files
import (
"context"
+ "errors"
"fmt"
"strings"
@@ -32,27 +33,25 @@ func (err ErrCommitIDDoesNotMatch) Error() string {
return fmt.Sprintf("file CommitID does not match [given: %s, expected: %s]", err.GivenCommitID, err.CurrentCommitID)
}
-// CherryPick cherrypicks or reverts a commit to the given repository
+// CherryPick cherry-picks or reverts a commit to the given repository
func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, revert bool, opts *ApplyDiffPatchOptions) (*structs.FileResponse, error) {
if err := opts.Validate(ctx, repo, doer); err != nil {
return nil, err
}
message := strings.TrimSpace(opts.Message)
- author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer)
-
- t, err := NewTemporaryUploadRepository(ctx, repo)
+ t, err := NewTemporaryUploadRepository(repo)
if err != nil {
log.Error("NewTemporaryUploadRepository failed: %v", err)
}
defer t.Close()
- if err := t.Clone(opts.OldBranch, false); err != nil {
+ if err := t.Clone(ctx, opts.OldBranch, false); err != nil {
return nil, err
}
- if err := t.SetDefaultIndex(); err != nil {
+ if err := t.SetDefaultIndex(ctx); err != nil {
return nil, err
}
- if err := t.RefreshIndex(); err != nil {
+ if err := t.RefreshIndex(ctx); err != nil {
return nil, err
}
@@ -102,28 +101,37 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod
}
if conflict {
- return nil, fmt.Errorf("failed to merge due to conflicts")
+ return nil, errors.New("failed to merge due to conflicts")
}
- treeHash, err := t.WriteTree()
+ treeHash, err := t.WriteTree(ctx)
if err != nil {
// likely non-sensical tree due to merge conflicts...
return nil, err
}
// Now commit the tree
- var commitHash string
+ commitOpts := &CommitTreeUserOptions{
+ ParentCommitID: "HEAD",
+ TreeHash: treeHash,
+ CommitMessage: message,
+ SignOff: opts.Signoff,
+ DoerUser: doer,
+ AuthorIdentity: opts.Author,
+ AuthorTime: nil,
+ CommitterIdentity: opts.Committer,
+ CommitterTime: nil,
+ }
if opts.Dates != nil {
- commitHash, err = t.CommitTreeWithDate("HEAD", author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer)
- } else {
- commitHash, err = t.CommitTree("HEAD", author, committer, treeHash, message, opts.Signoff)
+ commitOpts.AuthorTime, commitOpts.CommitterTime = &opts.Dates.Author, &opts.Dates.Committer
}
+ commitHash, err := t.CommitTree(ctx, commitOpts)
if err != nil {
return nil, err
}
// Then push this tree to NewBranch
- if err := t.Push(doer, commitHash, opts.NewBranch); err != nil {
+ if err := t.Push(ctx, doer, commitHash, opts.NewBranch); err != nil {
return nil, err
}
diff --git a/services/repository/files/commit.go b/services/repository/files/commit.go
index e0dad29273..3cc326d065 100644
--- a/services/repository/files/commit.go
+++ b/services/repository/files/commit.go
@@ -6,10 +6,10 @@ package files
import (
"context"
- asymkey_model "code.gitea.io/gitea/models/asymkey"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/structs"
+ asymkey_service "code.gitea.io/gitea/services/asymkey"
)
// CountDivergingCommits determines how many commits a branch is ahead or behind the repository's base branch
@@ -24,7 +24,7 @@ func CountDivergingCommits(ctx context.Context, repo *repo_model.Repository, bra
// GetPayloadCommitVerification returns the verification information of a commit
func GetPayloadCommitVerification(ctx context.Context, commit *git.Commit) *structs.PayloadCommitVerification {
verification := &structs.PayloadCommitVerification{}
- commitVerification := asymkey_model.ParseCommitWithSignature(ctx, commit)
+ commitVerification := asymkey_service.ParseCommitWithSignature(ctx, commit)
if commit.Signature != nil {
verification.Signature = commit.Signature.Signature
verification.Payload = commit.Signature.Payload
diff --git a/services/repository/files/content.go b/services/repository/files/content.go
index 0ab7422ce2..2c1e88bb59 100644
--- a/services/repository/files/content.go
+++ b/services/repository/files/content.go
@@ -5,17 +5,18 @@ package files
import (
"context"
- "fmt"
+ "io"
"net/url"
"path"
"strings"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/routers/api/v1/utils"
)
// ContentType repo content type
@@ -23,14 +24,10 @@ type ContentType string
// The string representations of different content types
const (
- // ContentTypeRegular regular content type (file)
- ContentTypeRegular ContentType = "file"
- // ContentTypeDir dir content type (dir)
- ContentTypeDir ContentType = "dir"
- // ContentLink link content type (symlink)
- ContentTypeLink ContentType = "symlink"
- // ContentTag submodule content type (submodule)
- ContentTypeSubmodule ContentType = "submodule"
+ ContentTypeRegular ContentType = "file" // regular content type (file)
+ ContentTypeDir ContentType = "dir" // dir content type (dir)
+ ContentTypeLink ContentType = "symlink" // link content type (symlink)
+ ContentTypeSubmodule ContentType = "submodule" // submodule content type (submodule)
)
// String gets the string of ContentType
@@ -38,67 +35,52 @@ func (ct *ContentType) String() string {
return string(*ct)
}
-// GetContentsOrList gets the meta data of a file's contents (*ContentsResponse) if treePath not a tree
-// directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag
-func GetContentsOrList(ctx context.Context, repo *repo_model.Repository, treePath, ref string) (any, error) {
- if repo.IsEmpty {
- return make([]any, 0), nil
- }
- if ref == "" {
- ref = repo.DefaultBranch
- }
- origRef := ref
-
- // Check that the path given in opts.treePath is valid (not a git path)
- cleanTreePath := CleanUploadFileName(treePath)
- if cleanTreePath == "" && treePath != "" {
- return nil, ErrFilenameInvalid{
- Path: treePath,
- }
- }
- treePath = cleanTreePath
-
- gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
- if err != nil {
- return nil, err
- }
- defer closer.Close()
+type GetContentsOrListOptions struct {
+ TreePath string
+ IncludeSingleFileContent bool // include the file's content when the tree path is a file
+ IncludeLfsMetadata bool
+ IncludeCommitMetadata bool
+ IncludeCommitMessage bool
+}
- // Get the commit object for the ref
- commit, err := gitRepo.GetCommit(ref)
- if err != nil {
- return nil, err
+// GetContentsOrList gets the metadata of a file's contents (*ContentsResponse) if treePath not a tree
+// directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag
+func GetContentsOrList(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, opts GetContentsOrListOptions) (ret api.ContentsExtResponse, _ error) {
+ entry, err := prepareGetContentsEntry(refCommit, &opts.TreePath)
+ if repo.IsEmpty && opts.TreePath == "" {
+ return api.ContentsExtResponse{DirContents: make([]*api.ContentsResponse, 0)}, nil
}
-
- entry, err := commit.GetTreeEntryByPath(treePath)
if err != nil {
- return nil, err
+ return ret, err
}
+ // get file contents
if entry.Type() != "tree" {
- return GetContents(ctx, repo, treePath, origRef, false)
+ ret.FileContents, err = getFileContentsByEntryInternal(ctx, repo, gitRepo, refCommit, entry, opts)
+ return ret, err
}
- // We are in a directory, so we return a list of FileContentResponse objects
- var fileList []*api.ContentsResponse
-
- gitTree, err := commit.SubTree(treePath)
+ // list directory contents
+ gitTree, err := refCommit.Commit.SubTree(opts.TreePath)
if err != nil {
- return nil, err
+ return ret, err
}
entries, err := gitTree.ListEntries()
if err != nil {
- return nil, err
+ return ret, err
}
+ ret.DirContents = make([]*api.ContentsResponse, 0, len(entries))
for _, e := range entries {
- subTreePath := path.Join(treePath, e.Name())
- fileContentResponse, err := GetContents(ctx, repo, subTreePath, origRef, true)
+ subOpts := opts
+ subOpts.TreePath = path.Join(opts.TreePath, e.Name())
+ subOpts.IncludeSingleFileContent = false // never include file content when listing a directory
+ fileContentResponse, err := GetFileContents(ctx, repo, gitRepo, refCommit, subOpts)
if err != nil {
- return nil, err
+ return ret, err
}
- fileList = append(fileList, fileContentResponse)
+ ret.DirContents = append(ret.DirContents, fileContentResponse)
}
- return fileList, nil
+ return ret, nil
}
// GetObjectTypeFromTreeEntry check what content is behind it
@@ -117,86 +99,96 @@ func GetObjectTypeFromTreeEntry(entry *git.TreeEntry) ContentType {
}
}
-// GetContents gets the meta data on a file's contents. Ref can be a branch, commit or tag
-func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref string, forList bool) (*api.ContentsResponse, error) {
- if ref == "" {
- ref = repo.DefaultBranch
- }
- origRef := ref
-
+func prepareGetContentsEntry(refCommit *utils.RefCommit, treePath *string) (*git.TreeEntry, error) {
// Check that the path given in opts.treePath is valid (not a git path)
- cleanTreePath := CleanUploadFileName(treePath)
- if cleanTreePath == "" && treePath != "" {
- return nil, ErrFilenameInvalid{
- Path: treePath,
- }
+ cleanTreePath := CleanGitTreePath(*treePath)
+ if cleanTreePath == "" && *treePath != "" {
+ return nil, ErrFilenameInvalid{Path: *treePath}
}
- treePath = cleanTreePath
+ *treePath = cleanTreePath
- gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
- if err != nil {
- return nil, err
+ // Only allow safe ref types
+ refType := refCommit.RefName.RefType()
+ if refType != git.RefTypeBranch && refType != git.RefTypeTag && refType != git.RefTypeCommit {
+ return nil, util.NewNotExistErrorf("no commit found for the ref [ref: %s]", refCommit.RefName)
}
- defer closer.Close()
- // Get the commit object for the ref
- commit, err := gitRepo.GetCommit(ref)
- if err != nil {
- return nil, err
- }
- commitID := commit.ID.String()
- if len(ref) >= 4 && strings.HasPrefix(commitID, ref) {
- ref = commit.ID.String()
- }
+ return refCommit.Commit.GetTreeEntryByPath(*treePath)
+}
- entry, err := commit.GetTreeEntryByPath(treePath)
+// GetFileContents gets the metadata on a file's contents. Ref can be a branch, commit or tag
+func GetFileContents(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, opts GetContentsOrListOptions) (*api.ContentsResponse, error) {
+ entry, err := prepareGetContentsEntry(refCommit, &opts.TreePath)
if err != nil {
return nil, err
}
+ return getFileContentsByEntryInternal(ctx, repo, gitRepo, refCommit, entry, opts)
+}
- refType := gitRepo.GetRefType(ref)
- if refType == "invalid" {
- return nil, fmt.Errorf("no commit found for the ref [ref: %s]", ref)
- }
-
- selfURL, err := url.Parse(repo.APIURL() + "/contents/" + util.PathEscapeSegments(treePath) + "?ref=" + url.QueryEscape(origRef))
+func getFileContentsByEntryInternal(_ context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, entry *git.TreeEntry, opts GetContentsOrListOptions) (*api.ContentsResponse, error) {
+ refType := refCommit.RefName.RefType()
+ commit := refCommit.Commit
+ selfURL, err := url.Parse(repo.APIURL() + "/contents/" + util.PathEscapeSegments(opts.TreePath) + "?ref=" + url.QueryEscape(refCommit.InputRef))
if err != nil {
return nil, err
}
selfURLString := selfURL.String()
- err = gitRepo.AddLastCommitCache(repo.GetCommitsCountCacheKey(ref, refType != git.ObjectCommit), repo.FullName(), commitID)
- if err != nil {
- return nil, err
- }
-
- lastCommit, err := commit.GetCommitByPath(treePath)
- if err != nil {
- return nil, err
- }
-
// All content types have these fields in populated
contentsResponse := &api.ContentsResponse{
- Name: entry.Name(),
- Path: treePath,
- SHA: entry.ID.String(),
- LastCommitSHA: lastCommit.ID.String(),
- Size: entry.Size(),
- URL: &selfURLString,
+ Name: entry.Name(),
+ Path: opts.TreePath,
+ SHA: entry.ID.String(),
+ Size: entry.Size(),
+ URL: &selfURLString,
Links: &api.FileLinksResponse{
Self: &selfURLString,
},
}
- // Now populate the rest of the ContentsResponse based on entry type
+ if opts.IncludeCommitMetadata || opts.IncludeCommitMessage {
+ err = gitRepo.AddLastCommitCache(repo.GetCommitsCountCacheKey(refCommit.InputRef, refType != git.RefTypeCommit), repo.FullName(), refCommit.CommitID)
+ if err != nil {
+ return nil, err
+ }
+
+ lastCommit, err := refCommit.Commit.GetCommitByPath(opts.TreePath)
+ if err != nil {
+ return nil, err
+ }
+
+ if opts.IncludeCommitMetadata {
+ contentsResponse.LastCommitSHA = util.ToPointer(lastCommit.ID.String())
+ // GitHub doesn't have these fields in the response, but we could follow other similar APIs to name them
+ // https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#list-commits
+ if lastCommit.Committer != nil {
+ contentsResponse.LastCommitterDate = util.ToPointer(lastCommit.Committer.When)
+ }
+ if lastCommit.Author != nil {
+ contentsResponse.LastAuthorDate = util.ToPointer(lastCommit.Author.When)
+ }
+ }
+ if opts.IncludeCommitMessage {
+ contentsResponse.LastCommitMessage = util.ToPointer(lastCommit.Message())
+ }
+ }
+
+ // Now populate the rest of the ContentsResponse based on the entry type
if entry.IsRegular() || entry.IsExecutable() {
contentsResponse.Type = string(ContentTypeRegular)
- if blobResponse, err := GetBlobBySHA(ctx, repo, gitRepo, entry.ID.String()); err != nil {
- return nil, err
- } else if !forList {
- // We don't show the content if we are getting a list of FileContentResponses
- contentsResponse.Encoding = &blobResponse.Encoding
- contentsResponse.Content = &blobResponse.Content
+ // if it is listing the repo root dir, don't waste system resources on reading content
+ if opts.IncludeSingleFileContent {
+ blobResponse, err := GetBlobBySHA(repo, gitRepo, entry.ID.String())
+ if err != nil {
+ return nil, err
+ }
+ contentsResponse.Encoding, contentsResponse.Content = blobResponse.Encoding, blobResponse.Content
+ contentsResponse.LfsOid, contentsResponse.LfsSize = blobResponse.LfsOid, blobResponse.LfsSize
+ } else if opts.IncludeLfsMetadata {
+ contentsResponse.LfsOid, contentsResponse.LfsSize, err = parsePossibleLfsPointerBlob(gitRepo, entry.ID.String())
+ if err != nil {
+ return nil, err
+ }
}
} else if entry.IsDir() {
contentsResponse.Type = string(ContentTypeDir)
@@ -210,7 +202,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref
contentsResponse.Target = &targetFromContent
} else if entry.IsSubModule() {
contentsResponse.Type = string(ContentTypeSubmodule)
- submodule, err := commit.GetSubModule(treePath)
+ submodule, err := commit.GetSubModule(opts.TreePath)
if err != nil {
return nil, err
}
@@ -220,7 +212,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref
}
// Handle links
if entry.IsRegular() || entry.IsLink() || entry.IsExecutable() {
- downloadURL, err := url.Parse(repo.HTMLURL() + "/raw/" + url.PathEscape(string(refType)) + "/" + util.PathEscapeSegments(ref) + "/" + util.PathEscapeSegments(treePath))
+ downloadURL, err := url.Parse(repo.HTMLURL() + "/raw/" + refCommit.RefName.RefWebLinkPath() + "/" + util.PathEscapeSegments(opts.TreePath))
if err != nil {
return nil, err
}
@@ -228,7 +220,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref
contentsResponse.DownloadURL = &downloadURLString
}
if !entry.IsSubModule() {
- htmlURL, err := url.Parse(repo.HTMLURL() + "/src/" + url.PathEscape(string(refType)) + "/" + util.PathEscapeSegments(ref) + "/" + util.PathEscapeSegments(treePath))
+ htmlURL, err := url.Parse(repo.HTMLURL() + "/src/" + refCommit.RefName.RefWebLinkPath() + "/" + util.PathEscapeSegments(opts.TreePath))
if err != nil {
return nil, err
}
@@ -248,49 +240,59 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref
return contentsResponse, nil
}
-// GetBlobBySHA get the GitBlobResponse of a repository using a sha hash.
-func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, sha string) (*api.GitBlobResponse, error) {
+func GetBlobBySHA(repo *repo_model.Repository, gitRepo *git.Repository, sha string) (*api.GitBlobResponse, error) {
gitBlob, err := gitRepo.GetBlob(sha)
if err != nil {
return nil, err
}
- content := ""
- if gitBlob.Size() <= setting.API.DefaultMaxBlobSize {
- content, err = gitBlob.GetBlobContentBase64()
- if err != nil {
- return nil, err
- }
+ ret := &api.GitBlobResponse{
+ SHA: gitBlob.ID.String(),
+ URL: repo.APIURL() + "/git/blobs/" + url.PathEscape(gitBlob.ID.String()),
+ Size: gitBlob.Size(),
}
- return &api.GitBlobResponse{
- SHA: gitBlob.ID.String(),
- URL: repo.APIURL() + "/git/blobs/" + url.PathEscape(gitBlob.ID.String()),
- Size: gitBlob.Size(),
- Encoding: "base64",
- Content: content,
- }, nil
-}
-// TryGetContentLanguage tries to get the (linguist) language of the file content
-func TryGetContentLanguage(gitRepo *git.Repository, commitID, treePath string) (string, error) {
- indexFilename, worktree, deleteTemporaryFile, err := gitRepo.ReadTreeToTemporaryIndex(commitID)
- if err != nil {
- return "", err
+ blobSize := gitBlob.Size()
+ if blobSize > setting.API.DefaultMaxBlobSize {
+ return ret, nil
}
- defer deleteTemporaryFile()
+ var originContent *strings.Builder
+ if 0 < blobSize && blobSize < lfs.MetaFileMaxSize {
+ originContent = &strings.Builder{}
+ }
- filename2attribute2info, err := gitRepo.CheckAttribute(git.CheckAttributeOpts{
- CachedOnly: true,
- Attributes: []string{git.AttributeLinguistLanguage, git.AttributeGitlabLanguage},
- Filenames: []string{treePath},
- IndexFile: indexFilename,
- WorkTree: worktree,
- })
+ content, err := gitBlob.GetBlobContentBase64(originContent)
if err != nil {
- return "", err
+ return nil, err
+ }
+
+ ret.Encoding, ret.Content = util.ToPointer("base64"), &content
+ if originContent != nil {
+ ret.LfsOid, ret.LfsSize = parsePossibleLfsPointerBuffer(strings.NewReader(originContent.String()))
}
+ return ret, nil
+}
- language := git.TryReadLanguageAttribute(filename2attribute2info[treePath])
+func parsePossibleLfsPointerBuffer(r io.Reader) (*string, *int64) {
+ p, _ := lfs.ReadPointer(r)
+ if p.IsValid() {
+ return &p.Oid, &p.Size
+ }
+ return nil, nil
+}
- return language.Value(), nil
+func parsePossibleLfsPointerBlob(gitRepo *git.Repository, sha string) (*string, *int64, error) {
+ gitBlob, err := gitRepo.GetBlob(sha)
+ if err != nil {
+ return nil, nil, err
+ }
+ if gitBlob.Size() > lfs.MetaFileMaxSize {
+ return nil, nil, nil // not a LFS pointer
+ }
+ buf, err := gitBlob.GetBlobContent(lfs.MetaFileMaxSize)
+ if err != nil {
+ return nil, nil, err
+ }
+ oid, size := parsePossibleLfsPointerBuffer(strings.NewReader(buf))
+ return oid, size, nil
}
diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go
index 7cb46c0bb6..d72f918074 100644
--- a/services/repository/files/content_test.go
+++ b/services/repository/files/content_test.go
@@ -7,8 +7,8 @@ import (
"testing"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/gitrepo"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/contexttest"
_ "code.gitea.io/gitea/models/actions"
@@ -20,36 +20,6 @@ func TestMain(m *testing.M) {
unittest.MainTest(m)
}
-func getExpectedReadmeContentsResponse() *api.ContentsResponse {
- treePath := "README.md"
- sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f"
- encoding := "base64"
- content := "IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x"
- selfURL := "https://try.gitea.io/api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master"
- htmlURL := "https://try.gitea.io/user2/repo1/src/branch/master/" + treePath
- gitURL := "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/" + sha
- downloadURL := "https://try.gitea.io/user2/repo1/raw/branch/master/" + treePath
- return &api.ContentsResponse{
- Name: treePath,
- Path: treePath,
- SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
- LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
- Type: "file",
- Size: 30,
- Encoding: &encoding,
- Content: &content,
- URL: &selfURL,
- HTMLURL: &htmlURL,
- GitURL: &gitURL,
- DownloadURL: &downloadURL,
- Links: &api.FileLinksResponse{
- Self: &selfURL,
- GitURL: &gitURL,
- HTMLURL: &htmlURL,
- },
- }
-}
-
func TestGetContents(t *testing.T) {
unittest.PrepareTestEnv(t)
ctx, _ := contexttest.MockContext(t, "user2/repo1")
@@ -58,195 +28,22 @@ func TestGetContents(t *testing.T) {
contexttest.LoadRepoCommit(t, ctx)
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadGitRepo(t, ctx)
- defer ctx.Repo.GitRepo.Close()
-
- treePath := "README.md"
- ref := ctx.Repo.Repository.DefaultBranch
-
- expectedContentsResponse := getExpectedReadmeContentsResponse()
-
- t.Run("Get README.md contents with GetContents(ctx, )", func(t *testing.T) {
- fileContentResponse, err := GetContents(ctx, ctx.Repo.Repository, treePath, ref, false)
- assert.EqualValues(t, expectedContentsResponse, fileContentResponse)
- assert.NoError(t, err)
- })
-
- t.Run("Get README.md contents with ref as empty string (should then use the repo's default branch) with GetContents(ctx, )", func(t *testing.T) {
- fileContentResponse, err := GetContents(ctx, ctx.Repo.Repository, treePath, "", false)
- assert.EqualValues(t, expectedContentsResponse, fileContentResponse)
- assert.NoError(t, err)
- })
-}
-
-func TestGetContentsOrListForDir(t *testing.T) {
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user2/repo1")
- ctx.SetPathParam("id", "1")
- contexttest.LoadRepo(t, ctx, 1)
- contexttest.LoadRepoCommit(t, ctx)
- contexttest.LoadUser(t, ctx, 2)
- contexttest.LoadGitRepo(t, ctx)
- defer ctx.Repo.GitRepo.Close()
-
- treePath := "" // root dir
- ref := ctx.Repo.Repository.DefaultBranch
-
- readmeContentsResponse := getExpectedReadmeContentsResponse()
- // because will be in a list, doesn't have encoding and content
- readmeContentsResponse.Encoding = nil
- readmeContentsResponse.Content = nil
-
- expectedContentsListResponse := []*api.ContentsResponse{
- readmeContentsResponse,
- }
-
- t.Run("Get root dir contents with GetContentsOrList(ctx, )", func(t *testing.T) {
- fileContentResponse, err := GetContentsOrList(ctx, ctx.Repo.Repository, treePath, ref)
- assert.EqualValues(t, expectedContentsListResponse, fileContentResponse)
- assert.NoError(t, err)
- })
-
- t.Run("Get root dir contents with ref as empty string (should then use the repo's default branch) with GetContentsOrList(ctx, )", func(t *testing.T) {
- fileContentResponse, err := GetContentsOrList(ctx, ctx.Repo.Repository, treePath, "")
- assert.EqualValues(t, expectedContentsListResponse, fileContentResponse)
- assert.NoError(t, err)
- })
-}
-
-func TestGetContentsOrListForFile(t *testing.T) {
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user2/repo1")
- ctx.SetPathParam("id", "1")
- contexttest.LoadRepo(t, ctx, 1)
- contexttest.LoadRepoCommit(t, ctx)
- contexttest.LoadUser(t, ctx, 2)
- contexttest.LoadGitRepo(t, ctx)
- defer ctx.Repo.GitRepo.Close()
-
- treePath := "README.md"
- ref := ctx.Repo.Repository.DefaultBranch
-
- expectedContentsResponse := getExpectedReadmeContentsResponse()
-
- t.Run("Get README.md contents with GetContentsOrList(ctx, )", func(t *testing.T) {
- fileContentResponse, err := GetContentsOrList(ctx, ctx.Repo.Repository, treePath, ref)
- assert.EqualValues(t, expectedContentsResponse, fileContentResponse)
- assert.NoError(t, err)
- })
-
- t.Run("Get README.md contents with ref as empty string (should then use the repo's default branch) with GetContentsOrList(ctx, )", func(t *testing.T) {
- fileContentResponse, err := GetContentsOrList(ctx, ctx.Repo.Repository, treePath, "")
- assert.EqualValues(t, expectedContentsResponse, fileContentResponse)
- assert.NoError(t, err)
- })
-}
-
-func TestGetContentsErrors(t *testing.T) {
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user2/repo1")
- ctx.SetPathParam("id", "1")
- contexttest.LoadRepo(t, ctx, 1)
- contexttest.LoadRepoCommit(t, ctx)
- contexttest.LoadUser(t, ctx, 2)
- contexttest.LoadGitRepo(t, ctx)
- defer ctx.Repo.GitRepo.Close()
-
- repo := ctx.Repo.Repository
- treePath := "README.md"
- ref := repo.DefaultBranch
-
- t.Run("bad treePath", func(t *testing.T) {
- badTreePath := "bad/tree.md"
- fileContentResponse, err := GetContents(ctx, repo, badTreePath, ref, false)
- assert.Error(t, err)
- assert.EqualError(t, err, "object does not exist [id: , rel_path: bad]")
- assert.Nil(t, fileContentResponse)
- })
-
- t.Run("bad ref", func(t *testing.T) {
- badRef := "bad_ref"
- fileContentResponse, err := GetContents(ctx, repo, treePath, badRef, false)
- assert.Error(t, err)
- assert.EqualError(t, err, "object does not exist [id: "+badRef+", rel_path: ]")
- assert.Nil(t, fileContentResponse)
- })
-}
-
-func TestGetContentsOrListErrors(t *testing.T) {
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user2/repo1")
- ctx.SetPathParam("id", "1")
- contexttest.LoadRepo(t, ctx, 1)
- contexttest.LoadRepoCommit(t, ctx)
- contexttest.LoadUser(t, ctx, 2)
- contexttest.LoadGitRepo(t, ctx)
- defer ctx.Repo.GitRepo.Close()
- repo := ctx.Repo.Repository
- treePath := "README.md"
- ref := repo.DefaultBranch
-
- t.Run("bad treePath", func(t *testing.T) {
- badTreePath := "bad/tree.md"
- fileContentResponse, err := GetContentsOrList(ctx, repo, badTreePath, ref)
- assert.Error(t, err)
- assert.EqualError(t, err, "object does not exist [id: , rel_path: bad]")
- assert.Nil(t, fileContentResponse)
- })
-
- t.Run("bad ref", func(t *testing.T) {
- badRef := "bad_ref"
- fileContentResponse, err := GetContentsOrList(ctx, repo, treePath, badRef)
- assert.Error(t, err)
- assert.EqualError(t, err, "object does not exist [id: "+badRef+", rel_path: ]")
- assert.Nil(t, fileContentResponse)
- })
-}
-
-func TestGetContentsOrListOfEmptyRepos(t *testing.T) {
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user30/empty")
- ctx.SetPathParam("id", "52")
- contexttest.LoadRepo(t, ctx, 52)
- contexttest.LoadUser(t, ctx, 30)
- contexttest.LoadGitRepo(t, ctx)
- defer ctx.Repo.GitRepo.Close()
-
- repo := ctx.Repo.Repository
-
- t.Run("empty repo", func(t *testing.T) {
- contents, err := GetContentsOrList(ctx, repo, "", "")
+ // GetContentsOrList's behavior is fully tested in integration tests, so we don't need to test it here.
+
+ t.Run("GetBlobBySHA", func(t *testing.T) {
+ sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
+ ctx.SetPathParam("id", "1")
+ ctx.SetPathParam("sha", sha)
+ gbr, err := GetBlobBySHA(ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.PathParam("sha"))
+ expectedGBR := &api.GitBlobResponse{
+ Content: util.ToPointer("dHJlZSAyYTJmMWQ0NjcwNzI4YTJlMTAwNDllMzQ1YmQ3YTI3NjQ2OGJlYWI2CmF1dGhvciB1c2VyMSA8YWRkcmVzczFAZXhhbXBsZS5jb20+IDE0ODk5NTY0NzkgLTA0MDAKY29tbWl0dGVyIEV0aGFuIEtvZW5pZyA8ZXRoYW50a29lbmlnQGdtYWlsLmNvbT4gMTQ4OTk1NjQ3OSAtMDQwMAoKSW5pdGlhbCBjb21taXQK"),
+ Encoding: util.ToPointer("base64"),
+ URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/65f1bf27bc3bf70f64657658635e66094edbcb4d",
+ SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
+ Size: 180,
+ }
assert.NoError(t, err)
- assert.Empty(t, contents)
+ assert.Equal(t, expectedGBR, gbr)
})
}
-
-func TestGetBlobBySHA(t *testing.T) {
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user2/repo1")
- contexttest.LoadRepo(t, ctx, 1)
- contexttest.LoadRepoCommit(t, ctx)
- contexttest.LoadUser(t, ctx, 2)
- contexttest.LoadGitRepo(t, ctx)
- defer ctx.Repo.GitRepo.Close()
-
- sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
- ctx.SetPathParam("id", "1")
- ctx.SetPathParam("sha", sha)
-
- gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
- if err != nil {
- t.Fail()
- }
-
- gbr, err := GetBlobBySHA(ctx, ctx.Repo.Repository, gitRepo, ctx.PathParam("sha"))
- expectedGBR := &api.GitBlobResponse{
- Content: "dHJlZSAyYTJmMWQ0NjcwNzI4YTJlMTAwNDllMzQ1YmQ3YTI3NjQ2OGJlYWI2CmF1dGhvciB1c2VyMSA8YWRkcmVzczFAZXhhbXBsZS5jb20+IDE0ODk5NTY0NzkgLTA0MDAKY29tbWl0dGVyIEV0aGFuIEtvZW5pZyA8ZXRoYW50a29lbmlnQGdtYWlsLmNvbT4gMTQ4OTk1NjQ3OSAtMDQwMAoKSW5pdGlhbCBjb21taXQK",
- Encoding: "base64",
- URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/65f1bf27bc3bf70f64657658635e66094edbcb4d",
- SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
- Size: 180,
- }
- assert.NoError(t, err)
- assert.Equal(t, expectedGBR, gbr)
-}
diff --git a/services/repository/files/diff.go b/services/repository/files/diff.go
index bf8b938e21..50d01f9d7c 100644
--- a/services/repository/files/diff.go
+++ b/services/repository/files/diff.go
@@ -16,27 +16,27 @@ func GetDiffPreview(ctx context.Context, repo *repo_model.Repository, branch, tr
if branch == "" {
branch = repo.DefaultBranch
}
- t, err := NewTemporaryUploadRepository(ctx, repo)
+ t, err := NewTemporaryUploadRepository(repo)
if err != nil {
return nil, err
}
defer t.Close()
- if err := t.Clone(branch, true); err != nil {
+ if err := t.Clone(ctx, branch, true); err != nil {
return nil, err
}
- if err := t.SetDefaultIndex(); err != nil {
+ if err := t.SetDefaultIndex(ctx); err != nil {
return nil, err
}
// Add the object to the database
- objectHash, err := t.HashObject(strings.NewReader(content))
+ objectHash, err := t.HashObjectAndWrite(ctx, strings.NewReader(content))
if err != nil {
return nil, err
}
// Add the object to the index
- if err := t.AddObjectToIndex("100644", objectHash, treePath); err != nil {
+ if err := t.AddObjectToIndex(ctx, "100644", objectHash, treePath); err != nil {
return nil, err
}
- return t.DiffIndex()
+ return t.DiffIndex(ctx)
}
diff --git a/services/repository/files/diff_test.go b/services/repository/files/diff_test.go
index b7bdcd8ecf..ae702e4189 100644
--- a/services/repository/files/diff_test.go
+++ b/services/repository/files/diff_test.go
@@ -30,14 +30,11 @@ func TestGetDiffPreview(t *testing.T) {
content := "# repo1\n\nDescription for repo1\nthis is a new line"
expectedDiff := &gitdiff.Diff{
- TotalAddition: 2,
- TotalDeletion: 1,
Files: []*gitdiff.DiffFile{
{
Name: "README.md",
OldName: "README.md",
NameHash: "8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d",
- Index: 1,
Addition: 2,
Deletion: 1,
Type: 2,
@@ -50,7 +47,6 @@ func TestGetDiffPreview(t *testing.T) {
Sections: []*gitdiff.DiffSection{
{
FileName: "README.md",
- Name: "",
Lines: []*gitdiff.DiffLine{
{
LeftIdx: 0,
@@ -114,7 +110,6 @@ func TestGetDiffPreview(t *testing.T) {
},
IsIncomplete: false,
}
- expectedDiff.NumFiles = len(expectedDiff.Files)
t.Run("with given branch", func(t *testing.T) {
diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, branch, treePath, content)
@@ -123,7 +118,7 @@ func TestGetDiffPreview(t *testing.T) {
assert.NoError(t, err)
bs, err := json.Marshal(diff)
assert.NoError(t, err)
- assert.EqualValues(t, string(expectedBs), string(bs))
+ assert.Equal(t, string(expectedBs), string(bs))
})
t.Run("empty branch, same results", func(t *testing.T) {
@@ -133,7 +128,7 @@ func TestGetDiffPreview(t *testing.T) {
assert.NoError(t, err)
bs, err := json.Marshal(diff)
assert.NoError(t, err)
- assert.EqualValues(t, expectedBs, bs)
+ assert.Equal(t, expectedBs, bs)
})
}
diff --git a/services/repository/files/file.go b/services/repository/files/file.go
index d7ca8e79e5..13d171d139 100644
--- a/services/repository/files/file.go
+++ b/services/repository/files/file.go
@@ -5,26 +5,48 @@ package files
import (
"context"
+ "errors"
"fmt"
"net/url"
"strings"
"time"
repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/routers/api/v1/utils"
)
-func GetFilesResponseFromCommit(ctx context.Context, repo *repo_model.Repository, commit *git.Commit, branch string, treeNames []string) (*api.FilesResponse, error) {
- files := []*api.ContentsResponse{}
- for _, file := range treeNames {
- fileContents, _ := GetContents(ctx, repo, file, branch, false) // ok if fails, then will be nil
+func GetContentsListFromTreePaths(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, treePaths []string) (files []*api.ContentsResponse) {
+ var size int64
+ for _, treePath := range treePaths {
+ // ok if fails, then will be nil
+ fileContents, _ := GetFileContents(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{
+ TreePath: treePath,
+ IncludeSingleFileContent: true,
+ IncludeCommitMetadata: true,
+ })
+ if fileContents != nil && fileContents.Content != nil && *fileContents.Content != "" {
+ // if content isn't empty (e.g., due to the single blob being too large), add file size to response size
+ size += int64(len(*fileContents.Content))
+ }
+ if size > setting.API.DefaultMaxResponseSize {
+ break // stop if max response size would be exceeded
+ }
files = append(files, fileContents)
+ if len(files) == setting.API.DefaultPagingNum {
+ break // stop if paging num reached
+ }
}
- fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil
- verification := GetPayloadCommitVerification(ctx, commit)
+ return files
+}
+
+func GetFilesResponseFromCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, refCommit *utils.RefCommit, treeNames []string) (*api.FilesResponse, error) {
+ files := GetContentsListFromTreePaths(ctx, repo, gitRepo, refCommit, treeNames)
+ fileCommitResponse, _ := GetFileCommitResponse(repo, refCommit.Commit) // ok if fails, then will be nil
+ verification := GetPayloadCommitVerification(ctx, refCommit.Commit)
filesResponse := &api.FilesResponse{
Files: files,
Commit: fileCommitResponse,
@@ -33,19 +55,6 @@ func GetFilesResponseFromCommit(ctx context.Context, repo *repo_model.Repository
return filesResponse, nil
}
-// GetFileResponseFromCommit Constructs a FileResponse from a Commit object
-func GetFileResponseFromCommit(ctx context.Context, repo *repo_model.Repository, commit *git.Commit, branch, treeName string) (*api.FileResponse, error) {
- fileContents, _ := GetContents(ctx, repo, treeName, branch, false) // ok if fails, then will be nil
- fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil
- verification := GetPayloadCommitVerification(ctx, commit)
- fileResponse := &api.FileResponse{
- Content: fileContents,
- Commit: fileCommitResponse,
- Verification: verification,
- }
- return fileResponse, nil
-}
-
// constructs a FileResponse with the file at the index from FilesResponse
func GetFileResponseFromFilesResponse(filesResponse *api.FilesResponse, index int) *api.FileResponse {
content := &api.ContentsResponse{}
@@ -63,10 +72,10 @@ func GetFileResponseFromFilesResponse(filesResponse *api.FilesResponse, index in
// GetFileCommitResponse Constructs a FileCommitResponse from a Commit object
func GetFileCommitResponse(repo *repo_model.Repository, commit *git.Commit) (*api.FileCommitResponse, error) {
if repo == nil {
- return nil, fmt.Errorf("repo cannot be nil")
+ return nil, errors.New("repo cannot be nil")
}
if commit == nil {
- return nil, fmt.Errorf("commit cannot be nil")
+ return nil, errors.New("commit cannot be nil")
}
commitURL, _ := url.Parse(repo.APIURL() + "/git/commits/" + url.PathEscape(commit.ID.String()))
commitTreeURL, _ := url.Parse(repo.APIURL() + "/git/trees/" + url.PathEscape(commit.Tree.ID.String()))
@@ -111,51 +120,6 @@ func GetFileCommitResponse(repo *repo_model.Repository, commit *git.Commit) (*ap
return fileCommit, nil
}
-// GetAuthorAndCommitterUsers Gets the author and committer user objects from the IdentityOptions
-func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *user_model.User) (authorUser, committerUser *user_model.User) {
- // Committer and author are optional. If they are not the doer (not same email address)
- // then we use bogus User objects for them to store their FullName and Email.
- // If only one of the two are provided, we set both of them to it.
- // If neither are provided, both are the doer.
- if committer != nil && committer.Email != "" {
- if doer != nil && strings.EqualFold(doer.Email, committer.Email) {
- committerUser = doer // the committer is the doer, so will use their user object
- if committer.Name != "" {
- committerUser.FullName = committer.Name
- }
- } else {
- committerUser = &user_model.User{
- FullName: committer.Name,
- Email: committer.Email,
- }
- }
- }
- if author != nil && author.Email != "" {
- if doer != nil && strings.EqualFold(doer.Email, author.Email) {
- authorUser = doer // the author is the doer, so will use their user object
- if authorUser.Name != "" {
- authorUser.FullName = author.Name
- }
- } else {
- authorUser = &user_model.User{
- FullName: author.Name,
- Email: author.Email,
- }
- }
- }
- if authorUser == nil {
- if committerUser != nil {
- authorUser = committerUser // No valid author was given so use the committer
- } else if doer != nil {
- authorUser = doer // No valid author was given and no valid committer so use the doer
- }
- }
- if committerUser == nil {
- committerUser = authorUser // No valid committer so use the author as the committer (was set to a valid user above)
- }
- return authorUser, committerUser
-}
-
// ErrFilenameInvalid represents a "FilenameInvalid" kind of error.
type ErrFilenameInvalid struct {
Path string
@@ -175,15 +139,17 @@ func (err ErrFilenameInvalid) Unwrap() error {
return util.ErrInvalidArgument
}
-// CleanUploadFileName Trims a filename and returns empty string if it is a .git directory
-func CleanUploadFileName(name string) string {
- // Rebase the filename
+// CleanGitTreePath cleans a tree path for git, it returns an empty string the path is invalid (e.g.: contains ".git" part)
+func CleanGitTreePath(name string) string {
name = util.PathJoinRel(name)
// Git disallows any filenames to have a .git directory in them.
- for _, part := range strings.Split(name, "/") {
+ for part := range strings.SplitSeq(name, "/") {
if strings.ToLower(part) == ".git" {
return ""
}
}
+ if name == "." {
+ name = ""
+ }
return name
}
diff --git a/services/repository/files/file_test.go b/services/repository/files/file_test.go
index 52c0574883..cdb6a266ff 100644
--- a/services/repository/files/file_test.go
+++ b/services/repository/files/file_test.go
@@ -6,115 +6,22 @@ package files
import (
"testing"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/services/contexttest"
-
"github.com/stretchr/testify/assert"
)
func TestCleanUploadFileName(t *testing.T) {
- t.Run("Clean regular file", func(t *testing.T) {
- name := "this/is/test"
- cleanName := CleanUploadFileName(name)
- expectedCleanName := name
- assert.EqualValues(t, expectedCleanName, cleanName)
- })
-
- t.Run("Clean a .git path", func(t *testing.T) {
- name := "this/is/test/.git"
- cleanName := CleanUploadFileName(name)
- expectedCleanName := ""
- assert.EqualValues(t, expectedCleanName, cleanName)
- })
-}
-
-func getExpectedFileResponse() *api.FileResponse {
- treePath := "README.md"
- sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f"
- encoding := "base64"
- content := "IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x"
- selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master"
- htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + treePath
- gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha
- downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath
- return &api.FileResponse{
- Content: &api.ContentsResponse{
- Name: treePath,
- Path: treePath,
- SHA: sha,
- LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
- Type: "file",
- Size: 30,
- Encoding: &encoding,
- Content: &content,
- URL: &selfURL,
- HTMLURL: &htmlURL,
- GitURL: &gitURL,
- DownloadURL: &downloadURL,
- Links: &api.FileLinksResponse{
- Self: &selfURL,
- GitURL: &gitURL,
- HTMLURL: &htmlURL,
- },
- },
- Commit: &api.FileCommitResponse{
- CommitMeta: api.CommitMeta{
- URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
- SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
- },
- HTMLURL: "https://try.gitea.io/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d",
- Author: &api.CommitUser{
- Identity: api.Identity{
- Name: "user1",
- Email: "address1@example.com",
- },
- Date: "2017-03-19T20:47:59Z",
- },
- Committer: &api.CommitUser{
- Identity: api.Identity{
- Name: "Ethan Koenig",
- Email: "ethantkoenig@gmail.com",
- },
- Date: "2017-03-19T20:47:59Z",
- },
- Parents: []*api.CommitMeta{},
- Message: "Initial commit\n",
- Tree: &api.CommitMeta{
- URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/trees/2a2f1d4670728a2e10049e345bd7a276468beab6",
- SHA: "2a2f1d4670728a2e10049e345bd7a276468beab6",
- },
- },
- Verification: &api.PayloadCommitVerification{
- Verified: false,
- Reason: "gpg.error.not_signed_commit",
- Signature: "",
- Payload: "",
- },
+ cases := []struct {
+ input, expected string
+ }{
+ {"", ""},
+ {".", ""},
+ {"a/./b", "a/b"},
+ {"a.git", "a.git"},
+ {".git/b", ""},
+ {"a/.git", ""},
+ {"/a/../../b", "b"},
+ }
+ for _, c := range cases {
+ assert.Equal(t, c.expected, CleanGitTreePath(c.input), "input: %q", c.input)
}
-}
-
-func TestGetFileResponseFromCommit(t *testing.T) {
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user2/repo1")
- ctx.SetPathParam("id", "1")
- contexttest.LoadRepo(t, ctx, 1)
- contexttest.LoadRepoCommit(t, ctx)
- contexttest.LoadUser(t, ctx, 2)
- contexttest.LoadGitRepo(t, ctx)
- defer ctx.Repo.GitRepo.Close()
-
- repo := ctx.Repo.Repository
- branch := repo.DefaultBranch
- treePath := "README.md"
- gitRepo, _ := gitrepo.OpenRepository(ctx, repo)
- defer gitRepo.Close()
- commit, _ := gitRepo.GetBranchCommit(branch)
- expectedFileResponse := getExpectedFileResponse()
-
- fileResponse, err := GetFileResponseFromCommit(ctx, repo, commit, branch, treePath)
- assert.NoError(t, err)
- assert.EqualValues(t, expectedFileResponse, fileResponse)
}
diff --git a/services/repository/files/patch.go b/services/repository/files/patch.go
index 38c17b4073..11a8744b7f 100644
--- a/services/repository/files/patch.go
+++ b/services/repository/files/patch.go
@@ -12,7 +12,6 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
@@ -45,7 +44,6 @@ type ApplyDiffPatchOptions struct {
NewBranch string
Message string
Content string
- SHA string
Author *IdentityOptions
Committer *IdentityOptions
Dates *CommitDateOptions
@@ -62,29 +60,26 @@ func (opts *ApplyDiffPatchOptions) Validate(ctx context.Context, repo *repo_mode
opts.NewBranch = opts.OldBranch
}
- gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
- if err != nil {
- return err
- }
- defer closer.Close()
-
// oldBranch must exist for this operation
- if _, err := gitRepo.GetBranch(opts.OldBranch); err != nil {
+ if exist, err := git_model.IsBranchExist(ctx, repo.ID, opts.OldBranch); err != nil {
return err
+ } else if !exist {
+ return git_model.ErrBranchNotExist{
+ BranchName: opts.OldBranch,
+ }
}
// A NewBranch can be specified for the patch to be applied to.
// Check to make sure the branch does not already exist, otherwise we can't proceed.
// If we aren't branching to a new branch, make sure user can commit to the given branch
if opts.NewBranch != opts.OldBranch {
- existingBranch, err := gitRepo.GetBranch(opts.NewBranch)
- if existingBranch != nil {
+ exist, err := git_model.IsBranchExist(ctx, repo.ID, opts.NewBranch)
+ if err != nil {
+ return err
+ } else if exist {
return git_model.ErrBranchAlreadyExists{
BranchName: opts.NewBranch,
}
}
- if err != nil && !git.IsErrBranchNotExist(err) {
- return err
- }
} else {
protectedBranch, err := git_model.GetFirstMatchProtectedBranchRule(ctx, repo.ID, opts.OldBranch)
if err != nil {
@@ -126,17 +121,15 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
message := strings.TrimSpace(opts.Message)
- author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer)
-
- t, err := NewTemporaryUploadRepository(ctx, repo)
+ t, err := NewTemporaryUploadRepository(repo)
if err != nil {
log.Error("NewTemporaryUploadRepository failed: %v", err)
}
defer t.Close()
- if err := t.Clone(opts.OldBranch, true); err != nil {
+ if err := t.Clone(ctx, opts.OldBranch, true); err != nil {
return nil, err
}
- if err := t.SetDefaultIndex(); err != nil {
+ if err := t.SetDefaultIndex(ctx); err != nil {
return nil, err
}
@@ -166,12 +159,12 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
stdout := &strings.Builder{}
stderr := &strings.Builder{}
- cmdApply := git.NewCommand(ctx, "apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary")
+ cmdApply := git.NewCommand("apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary")
if git.DefaultFeatures().CheckVersionAtLeast("2.32") {
cmdApply.AddArguments("-3")
}
- if err := cmdApply.Run(&git.RunOpts{
+ if err := cmdApply.Run(ctx, &git.RunOpts{
Dir: t.basePath,
Stdout: stdout,
Stderr: stderr,
@@ -181,24 +174,33 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
}
// Now write the tree
- treeHash, err := t.WriteTree()
+ treeHash, err := t.WriteTree(ctx)
if err != nil {
return nil, err
}
// Now commit the tree
- var commitHash string
+ commitOpts := &CommitTreeUserOptions{
+ ParentCommitID: "HEAD",
+ TreeHash: treeHash,
+ CommitMessage: message,
+ SignOff: opts.Signoff,
+ DoerUser: doer,
+ AuthorIdentity: opts.Author,
+ AuthorTime: nil,
+ CommitterIdentity: opts.Committer,
+ CommitterTime: nil,
+ }
if opts.Dates != nil {
- commitHash, err = t.CommitTreeWithDate("HEAD", author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer)
- } else {
- commitHash, err = t.CommitTree("HEAD", author, committer, treeHash, message, opts.Signoff)
+ commitOpts.AuthorTime, commitOpts.CommitterTime = &opts.Dates.Author, &opts.Dates.Committer
}
+ commitHash, err := t.CommitTree(ctx, commitOpts)
if err != nil {
return nil, err
}
// Then push this tree to NewBranch
- if err := t.Push(doer, commitHash, opts.NewBranch); err != nil {
+ if err := t.Push(ctx, doer, commitHash, opts.NewBranch); err != nil {
return nil, err
}
diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go
index 138af991f9..c2f61c8223 100644
--- a/services/repository/files/temp_repo.go
+++ b/services/repository/files/temp_repo.go
@@ -6,6 +6,7 @@ package files
import (
"bytes"
"context"
+ "errors"
"fmt"
"io"
"os"
@@ -19,44 +20,45 @@ import (
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/gitdiff"
)
// TemporaryUploadRepository is a type to wrap our upload repositories as a shallow clone
type TemporaryUploadRepository struct {
- ctx context.Context
repo *repo_model.Repository
gitRepo *git.Repository
basePath string
+ cleanup func()
}
// NewTemporaryUploadRepository creates a new temporary upload repository
-func NewTemporaryUploadRepository(ctx context.Context, repo *repo_model.Repository) (*TemporaryUploadRepository, error) {
- basePath, err := repo_module.CreateTemporaryPath("upload")
+func NewTemporaryUploadRepository(repo *repo_model.Repository) (*TemporaryUploadRepository, error) {
+ basePath, cleanup, err := repo_module.CreateTemporaryPath("upload")
if err != nil {
return nil, err
}
- t := &TemporaryUploadRepository{ctx: ctx, repo: repo, basePath: basePath}
+ t := &TemporaryUploadRepository{repo: repo, basePath: basePath, cleanup: cleanup}
return t, nil
}
// Close the repository cleaning up all files
func (t *TemporaryUploadRepository) Close() {
defer t.gitRepo.Close()
- if err := repo_module.RemoveTemporaryPath(t.basePath); err != nil {
- log.Error("Failed to remove temporary path %s: %v", t.basePath, err)
+ if t.cleanup != nil {
+ t.cleanup()
}
}
// Clone the base repository to our path and set branch as the HEAD
-func (t *TemporaryUploadRepository) Clone(branch string, bare bool) error {
- cmd := git.NewCommand(t.ctx, "clone", "-s", "-b").AddDynamicArguments(branch, t.repo.RepoPath(), t.basePath)
+func (t *TemporaryUploadRepository) Clone(ctx context.Context, branch string, bare bool) error {
+ cmd := git.NewCommand("clone", "-s", "-b").AddDynamicArguments(branch, t.repo.RepoPath(), t.basePath)
if bare {
cmd.AddArguments("--bare")
}
- if _, _, err := cmd.RunStdString(nil); err != nil {
+ if _, _, err := cmd.RunStdString(ctx, nil); err != nil {
stderr := err.Error()
if matched, _ := regexp.MatchString(".*Remote branch .* not found in upstream origin.*", stderr); matched {
return git.ErrBranchNotExist{
@@ -72,7 +74,7 @@ func (t *TemporaryUploadRepository) Clone(branch string, bare bool) error {
}
return fmt.Errorf("Clone: %w %s", err, stderr)
}
- gitRepo, err := git.OpenRepository(t.ctx, t.basePath)
+ gitRepo, err := git.OpenRepository(ctx, t.basePath)
if err != nil {
return err
}
@@ -81,11 +83,11 @@ func (t *TemporaryUploadRepository) Clone(branch string, bare bool) error {
}
// Init the repository
-func (t *TemporaryUploadRepository) Init(objectFormatName string) error {
- if err := git.InitRepository(t.ctx, t.basePath, false, objectFormatName); err != nil {
+func (t *TemporaryUploadRepository) Init(ctx context.Context, objectFormatName string) error {
+ if err := git.InitRepository(ctx, t.basePath, false, objectFormatName); err != nil {
return err
}
- gitRepo, err := git.OpenRepository(t.ctx, t.basePath)
+ gitRepo, err := git.OpenRepository(ctx, t.basePath)
if err != nil {
return err
}
@@ -94,28 +96,28 @@ func (t *TemporaryUploadRepository) Init(objectFormatName string) error {
}
// SetDefaultIndex sets the git index to our HEAD
-func (t *TemporaryUploadRepository) SetDefaultIndex() error {
- if _, _, err := git.NewCommand(t.ctx, "read-tree", "HEAD").RunStdString(&git.RunOpts{Dir: t.basePath}); err != nil {
+func (t *TemporaryUploadRepository) SetDefaultIndex(ctx context.Context) error {
+ if _, _, err := git.NewCommand("read-tree", "HEAD").RunStdString(ctx, &git.RunOpts{Dir: t.basePath}); err != nil {
return fmt.Errorf("SetDefaultIndex: %w", err)
}
return nil
}
// RefreshIndex looks at the current index and checks to see if merges or updates are needed by checking stat() information.
-func (t *TemporaryUploadRepository) RefreshIndex() error {
- if _, _, err := git.NewCommand(t.ctx, "update-index", "--refresh").RunStdString(&git.RunOpts{Dir: t.basePath}); err != nil {
+func (t *TemporaryUploadRepository) RefreshIndex(ctx context.Context) error {
+ if _, _, err := git.NewCommand("update-index", "--refresh").RunStdString(ctx, &git.RunOpts{Dir: t.basePath}); err != nil {
return fmt.Errorf("RefreshIndex: %w", err)
}
return nil
}
// LsFiles checks if the given filename arguments are in the index
-func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, error) {
+func (t *TemporaryUploadRepository) LsFiles(ctx context.Context, filenames ...string) ([]string, error) {
stdOut := new(bytes.Buffer)
stdErr := new(bytes.Buffer)
- if err := git.NewCommand(t.ctx, "ls-files", "-z").AddDashesAndList(filenames...).
- Run(&git.RunOpts{
+ if err := git.NewCommand("ls-files", "-z").AddDashesAndList(filenames...).
+ Run(ctx, &git.RunOpts{
Dir: t.basePath,
Stdout: stdOut,
Stderr: stdErr,
@@ -126,7 +128,7 @@ func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, erro
}
fileList := make([]string, 0, len(filenames))
- for _, line := range bytes.Split(stdOut.Bytes(), []byte{'\000'}) {
+ for line := range bytes.SplitSeq(stdOut.Bytes(), []byte{'\000'}) {
fileList = append(fileList, string(line))
}
@@ -134,7 +136,7 @@ func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, erro
}
// RemoveFilesFromIndex removes the given files from the index
-func (t *TemporaryUploadRepository) RemoveFilesFromIndex(filenames ...string) error {
+func (t *TemporaryUploadRepository) RemoveFilesFromIndex(ctx context.Context, filenames ...string) error {
objFmt, err := t.gitRepo.GetObjectFormat()
if err != nil {
return fmt.Errorf("unable to get object format for temporary repo: %q, error: %w", t.repo.FullName(), err)
@@ -150,8 +152,8 @@ func (t *TemporaryUploadRepository) RemoveFilesFromIndex(filenames ...string) er
}
}
- if err := git.NewCommand(t.ctx, "update-index", "--remove", "-z", "--index-info").
- Run(&git.RunOpts{
+ if err := git.NewCommand("update-index", "--remove", "-z", "--index-info").
+ Run(ctx, &git.RunOpts{
Dir: t.basePath,
Stdin: stdIn,
Stdout: stdOut,
@@ -162,13 +164,13 @@ func (t *TemporaryUploadRepository) RemoveFilesFromIndex(filenames ...string) er
return nil
}
-// HashObject writes the provided content to the object db and returns its hash
-func (t *TemporaryUploadRepository) HashObject(content io.Reader) (string, error) {
+// HashObjectAndWrite writes the provided content to the object db and returns its hash
+func (t *TemporaryUploadRepository) HashObjectAndWrite(ctx context.Context, content io.Reader) (string, error) {
stdOut := new(bytes.Buffer)
stdErr := new(bytes.Buffer)
- if err := git.NewCommand(t.ctx, "hash-object", "-w", "--stdin").
- Run(&git.RunOpts{
+ if err := git.NewCommand("hash-object", "-w", "--stdin").
+ Run(ctx, &git.RunOpts{
Dir: t.basePath,
Stdin: content,
Stdout: stdOut,
@@ -182,8 +184,8 @@ func (t *TemporaryUploadRepository) HashObject(content io.Reader) (string, error
}
// AddObjectToIndex adds the provided object hash to the index with the provided mode and path
-func (t *TemporaryUploadRepository) AddObjectToIndex(mode, objectHash, objectPath string) error {
- if _, _, err := git.NewCommand(t.ctx, "update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments(mode, objectHash, objectPath).RunStdString(&git.RunOpts{Dir: t.basePath}); err != nil {
+func (t *TemporaryUploadRepository) AddObjectToIndex(ctx context.Context, mode, objectHash, objectPath string) error {
+ if _, _, err := git.NewCommand("update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments(mode, objectHash, objectPath).RunStdString(ctx, &git.RunOpts{Dir: t.basePath}); err != nil {
stderr := err.Error()
if matched, _ := regexp.MatchString(".*Invalid path '.*", stderr); matched {
return ErrFilePathInvalid{
@@ -198,8 +200,8 @@ func (t *TemporaryUploadRepository) AddObjectToIndex(mode, objectHash, objectPat
}
// WriteTree writes the current index as a tree to the object db and returns its hash
-func (t *TemporaryUploadRepository) WriteTree() (string, error) {
- stdout, _, err := git.NewCommand(t.ctx, "write-tree").RunStdString(&git.RunOpts{Dir: t.basePath})
+func (t *TemporaryUploadRepository) WriteTree(ctx context.Context) (string, error) {
+ stdout, _, err := git.NewCommand("write-tree").RunStdString(ctx, &git.RunOpts{Dir: t.basePath})
if err != nil {
log.Error("Unable to write tree in temporary repo: %s(%s): Error: %v", t.repo.FullName(), t.basePath, err)
return "", fmt.Errorf("Unable to write-tree in temporary repo for: %s Error: %w", t.repo.FullName(), err)
@@ -208,16 +210,16 @@ func (t *TemporaryUploadRepository) WriteTree() (string, error) {
}
// GetLastCommit gets the last commit ID SHA of the repo
-func (t *TemporaryUploadRepository) GetLastCommit() (string, error) {
- return t.GetLastCommitByRef("HEAD")
+func (t *TemporaryUploadRepository) GetLastCommit(ctx context.Context) (string, error) {
+ return t.GetLastCommitByRef(ctx, "HEAD")
}
// GetLastCommitByRef gets the last commit ID SHA of the repo by ref
-func (t *TemporaryUploadRepository) GetLastCommitByRef(ref string) (string, error) {
+func (t *TemporaryUploadRepository) GetLastCommitByRef(ctx context.Context, ref string) (string, error) {
if ref == "" {
ref = "HEAD"
}
- stdout, _, err := git.NewCommand(t.ctx, "rev-parse").AddDynamicArguments(ref).RunStdString(&git.RunOpts{Dir: t.basePath})
+ stdout, _, err := git.NewCommand("rev-parse").AddDynamicArguments(ref).RunStdString(ctx, &git.RunOpts{Dir: t.basePath})
if err != nil {
log.Error("Unable to get last ref for %s in temporary repo: %s(%s): Error: %v", ref, t.repo.FullName(), t.basePath, err)
return "", fmt.Errorf("Unable to rev-parse %s in temporary repo for: %s Error: %w", ref, t.repo.FullName(), err)
@@ -225,15 +227,53 @@ func (t *TemporaryUploadRepository) GetLastCommitByRef(ref string) (string, erro
return strings.TrimSpace(stdout), nil
}
-// CommitTree creates a commit from a given tree for the user with provided message
-func (t *TemporaryUploadRepository) CommitTree(parent string, author, committer *user_model.User, treeHash, message string, signoff bool) (string, error) {
- return t.CommitTreeWithDate(parent, author, committer, treeHash, message, signoff, time.Now(), time.Now())
+type CommitTreeUserOptions struct {
+ ParentCommitID string
+ TreeHash string
+ CommitMessage string
+ SignOff bool
+
+ DoerUser *user_model.User
+
+ AuthorIdentity *IdentityOptions // if nil, use doer
+ AuthorTime *time.Time // if nil, use now
+ CommitterIdentity *IdentityOptions
+ CommitterTime *time.Time
+}
+
+func makeGitUserSignature(doer *user_model.User, identity, other *IdentityOptions) *git.Signature {
+ gitSig := &git.Signature{}
+ if identity != nil {
+ gitSig.Name, gitSig.Email = identity.GitUserName, identity.GitUserEmail
+ }
+ if other != nil {
+ gitSig.Name = util.IfZero(gitSig.Name, other.GitUserName)
+ gitSig.Email = util.IfZero(gitSig.Email, other.GitUserEmail)
+ }
+ if gitSig.Name == "" {
+ gitSig.Name = doer.GitName()
+ }
+ if gitSig.Email == "" {
+ gitSig.Email = doer.GetEmail()
+ }
+ return gitSig
}
-// CommitTreeWithDate creates a commit from a given tree for the user with provided message
-func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, committer *user_model.User, treeHash, message string, signoff bool, authorDate, committerDate time.Time) (string, error) {
- authorSig := author.NewGitSig()
- committerSig := committer.NewGitSig()
+// CommitTree creates a commit from a given tree for the user with provided message
+func (t *TemporaryUploadRepository) CommitTree(ctx context.Context, opts *CommitTreeUserOptions) (string, error) {
+ authorSig := makeGitUserSignature(opts.DoerUser, opts.AuthorIdentity, opts.CommitterIdentity)
+ committerSig := makeGitUserSignature(opts.DoerUser, opts.CommitterIdentity, opts.AuthorIdentity)
+
+ authorDate := opts.AuthorTime
+ committerDate := opts.CommitterTime
+ if authorDate == nil && committerDate == nil {
+ authorDate = util.ToPointer(time.Now())
+ committerDate = authorDate
+ } else if authorDate == nil {
+ authorDate = committerDate
+ } else if committerDate == nil {
+ committerDate = authorDate
+ }
// Because this may call hooks we should pass in the environment
env := append(os.Environ(),
@@ -244,24 +284,27 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, co
)
messageBytes := new(bytes.Buffer)
- _, _ = messageBytes.WriteString(message)
+ _, _ = messageBytes.WriteString(opts.CommitMessage)
_, _ = messageBytes.WriteString("\n")
- cmdCommitTree := git.NewCommand(t.ctx, "commit-tree").AddDynamicArguments(treeHash)
- if parent != "" {
- cmdCommitTree.AddOptionValues("-p", parent)
+ cmdCommitTree := git.NewCommand("commit-tree").AddDynamicArguments(opts.TreeHash)
+ if opts.ParentCommitID != "" {
+ cmdCommitTree.AddOptionValues("-p", opts.ParentCommitID)
}
var sign bool
- var keyID string
+ var key *git.SigningKey
var signer *git.Signature
- if parent != "" {
- sign, keyID, signer, _ = asymkey_service.SignCRUDAction(t.ctx, t.repo.RepoPath(), author, t.basePath, parent)
+ if opts.ParentCommitID != "" {
+ sign, key, signer, _ = asymkey_service.SignCRUDAction(ctx, t.repo.RepoPath(), opts.DoerUser, t.basePath, opts.ParentCommitID)
} else {
- sign, keyID, signer, _ = asymkey_service.SignInitialCommit(t.ctx, t.repo.RepoPath(), author)
+ sign, key, signer, _ = asymkey_service.SignInitialCommit(ctx, t.repo.RepoPath(), opts.DoerUser)
}
if sign {
- cmdCommitTree.AddOptionFormat("-S%s", keyID)
+ if key.Format != "" {
+ cmdCommitTree.AddConfig("gpg.format", key.Format)
+ }
+ cmdCommitTree.AddOptionFormat("-S%s", key.KeyID)
if t.repo.GetTrustModel() == repo_model.CommitterTrustModel || t.repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
if committerSig.Name != authorSig.Name || committerSig.Email != authorSig.Email {
// Add trailers
@@ -279,7 +322,7 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, co
cmdCommitTree.AddArguments("--no-gpg-sign")
}
- if signoff {
+ if opts.SignOff {
// Signed-off-by
_, _ = messageBytes.WriteString("\n")
_, _ = messageBytes.WriteString("Signed-off-by: ")
@@ -294,7 +337,7 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, co
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
if err := cmdCommitTree.
- Run(&git.RunOpts{
+ Run(ctx, &git.RunOpts{
Env: env,
Dir: t.basePath,
Stdin: messageBytes,
@@ -310,10 +353,10 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, co
}
// Push the provided commitHash to the repository branch by the provided user
-func (t *TemporaryUploadRepository) Push(doer *user_model.User, commitHash, branch string) error {
+func (t *TemporaryUploadRepository) Push(ctx context.Context, doer *user_model.User, commitHash, branch string) error {
// Because calls hooks we need to pass in the environment
env := repo_module.PushingEnvironment(doer, t.repo)
- if err := git.Push(t.ctx, t.basePath, git.PushOptions{
+ if err := git.Push(ctx, t.basePath, git.PushOptions{
Remote: t.repo.RepoPath(),
Branch: strings.TrimSpace(commitHash) + ":" + git.BranchPrefix + strings.TrimSpace(branch),
Env: env,
@@ -335,7 +378,7 @@ func (t *TemporaryUploadRepository) Push(doer *user_model.User, commitHash, bran
}
// DiffIndex returns a Diff of the current index to the head
-func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
+func (t *TemporaryUploadRepository) DiffIndex(ctx context.Context) (*gitdiff.Diff, error) {
stdoutReader, stdoutWriter, err := os.Pipe()
if err != nil {
return nil, fmt.Errorf("unable to open stdout pipe: %w", err)
@@ -346,8 +389,8 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
}()
stderr := new(bytes.Buffer)
var diff *gitdiff.Diff
- err = git.NewCommand(t.ctx, "diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD").
- Run(&git.RunOpts{
+ err = git.NewCommand("diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD").
+ Run(ctx, &git.RunOpts{
Timeout: 30 * time.Second,
Dir: t.basePath,
Stdout: stdoutWriter,
@@ -356,7 +399,7 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
_ = stdoutWriter.Close()
defer cancel()
var diffErr error
- diff, diffErr = gitdiff.ParsePatch(t.ctx, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader, "")
+ diff, diffErr = gitdiff.ParsePatch(ctx, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader, "")
_ = stdoutReader.Close()
if diffErr != nil {
// if the diffErr is not nil, it will be returned as the error of "Run()"
@@ -370,18 +413,13 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
return nil, fmt.Errorf("unable to run diff-index pipeline in temporary repo: %w", err)
}
- diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(t.ctx, t.basePath, git.TrustedCmdArgs{"--cached"}, "HEAD")
- if err != nil {
- return nil, err
- }
-
return diff, nil
}
// GetBranchCommit Gets the commit object of the given branch
func (t *TemporaryUploadRepository) GetBranchCommit(branch string) (*git.Commit, error) {
if t.gitRepo == nil {
- return nil, fmt.Errorf("repository has not been cloned")
+ return nil, errors.New("repository has not been cloned")
}
return t.gitRepo.GetBranchCommit(branch)
}
@@ -389,7 +427,7 @@ func (t *TemporaryUploadRepository) GetBranchCommit(branch string) (*git.Commit,
// GetCommit Gets the commit object of the given commit ID
func (t *TemporaryUploadRepository) GetCommit(commitID string) (*git.Commit, error) {
if t.gitRepo == nil {
- return nil, fmt.Errorf("repository has not been cloned")
+ return nil, errors.New("repository has not been cloned")
}
return t.gitRepo.GetCommit(commitID)
}
diff --git a/services/repository/files/tree.go b/services/repository/files/tree.go
index 6775186afd..f2cbacbf1c 100644
--- a/services/repository/files/tree.go
+++ b/services/repository/files/tree.go
@@ -6,10 +6,16 @@ package files
import (
"context"
"fmt"
+ "html/template"
"net/url"
+ "path"
+ "sort"
+ "strings"
repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
@@ -88,11 +94,7 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
if len(entries) > perPage {
tree.Truncated = true
}
- if rangeStart+perPage < len(entries) {
- rangeEnd = rangeStart + perPage
- } else {
- rangeEnd = len(entries)
- }
+ rangeEnd = min(rangeStart+perPage, len(entries))
tree.Entries = make([]api.GitEntry, rangeEnd-rangeStart)
for e := rangeStart; e < rangeEnd; e++ {
i := e - rangeStart
@@ -118,3 +120,110 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
}
return tree, nil
}
+
+func entryModeString(entryMode git.EntryMode) string {
+ switch entryMode {
+ case git.EntryModeBlob:
+ return "blob"
+ case git.EntryModeExec:
+ return "exec"
+ case git.EntryModeSymlink:
+ return "symlink"
+ case git.EntryModeCommit:
+ return "commit" // submodule
+ case git.EntryModeTree:
+ return "tree"
+ }
+ return "unknown"
+}
+
+type TreeViewNode struct {
+ EntryName string `json:"entryName"`
+ EntryMode string `json:"entryMode"`
+ EntryIcon template.HTML `json:"entryIcon"`
+ EntryIconOpen template.HTML `json:"entryIconOpen,omitempty"`
+
+ SymLinkedToMode string `json:"symLinkedToMode,omitempty"` // TODO: for the EntryMode="symlink"
+
+ FullPath string `json:"fullPath"`
+ SubmoduleURL string `json:"submoduleUrl,omitempty"`
+ Children []*TreeViewNode `json:"children,omitempty"`
+}
+
+func (node *TreeViewNode) sortLevel() int {
+ return util.Iif(node.EntryMode == "tree" || node.EntryMode == "commit", 0, 1)
+}
+
+func newTreeViewNodeFromEntry(ctx context.Context, renderedIconPool *fileicon.RenderedIconPool, commit *git.Commit, parentDir string, entry *git.TreeEntry) *TreeViewNode {
+ node := &TreeViewNode{
+ EntryName: entry.Name(),
+ EntryMode: entryModeString(entry.Mode()),
+ FullPath: path.Join(parentDir, entry.Name()),
+ }
+
+ entryInfo := fileicon.EntryInfoFromGitTreeEntry(commit, node.FullPath, entry)
+ node.EntryIcon = fileicon.RenderEntryIconHTML(renderedIconPool, entryInfo)
+ if entryInfo.EntryMode.IsDir() {
+ entryInfo.IsOpen = true
+ node.EntryIconOpen = fileicon.RenderEntryIconHTML(renderedIconPool, entryInfo)
+ }
+
+ if node.EntryMode == "commit" {
+ if subModule, err := commit.GetSubModule(node.FullPath); err != nil {
+ log.Error("GetSubModule: %v", err)
+ } else if subModule != nil {
+ submoduleFile := git.NewCommitSubmoduleFile(subModule.URL, entry.ID.String())
+ webLink := submoduleFile.SubmoduleWebLink(ctx)
+ node.SubmoduleURL = webLink.CommitWebLink
+ }
+ }
+
+ return node
+}
+
+// sortTreeViewNodes list directory first and with alpha sequence
+func sortTreeViewNodes(nodes []*TreeViewNode) {
+ sort.Slice(nodes, func(i, j int) bool {
+ a, b := nodes[i].sortLevel(), nodes[j].sortLevel()
+ if a != b {
+ return a < b
+ }
+ return nodes[i].EntryName < nodes[j].EntryName
+ })
+}
+
+func listTreeNodes(ctx context.Context, renderedIconPool *fileicon.RenderedIconPool, commit *git.Commit, tree *git.Tree, treePath, subPath string) ([]*TreeViewNode, error) {
+ entries, err := tree.ListEntries()
+ if err != nil {
+ return nil, err
+ }
+
+ subPathDirName, subPathRemaining, _ := strings.Cut(subPath, "/")
+ nodes := make([]*TreeViewNode, 0, len(entries))
+ for _, entry := range entries {
+ node := newTreeViewNodeFromEntry(ctx, renderedIconPool, commit, treePath, entry)
+ nodes = append(nodes, node)
+ if entry.IsDir() && subPathDirName == entry.Name() {
+ subTreePath := treePath + "/" + node.EntryName
+ if subTreePath[0] == '/' {
+ subTreePath = subTreePath[1:]
+ }
+ subNodes, err := listTreeNodes(ctx, renderedIconPool, commit, entry.Tree(), subTreePath, subPathRemaining)
+ if err != nil {
+ log.Error("listTreeNodes: %v", err)
+ } else {
+ node.Children = subNodes
+ }
+ }
+ }
+ sortTreeViewNodes(nodes)
+ return nodes, nil
+}
+
+func GetTreeViewNodes(ctx context.Context, renderedIconPool *fileicon.RenderedIconPool, commit *git.Commit, treePath, subPath string) ([]*TreeViewNode, error) {
+ entry, err := commit.GetTreeEntryByPath(treePath)
+ if err != nil {
+ return nil, err
+ }
+ return listTreeNodes(ctx, renderedIconPool, commit, entry.Tree(), treePath, subPath)
+}
diff --git a/services/repository/files/tree_test.go b/services/repository/files/tree_test.go
index 0c60fddf7b..a53f342d40 100644
--- a/services/repository/files/tree_test.go
+++ b/services/repository/files/tree_test.go
@@ -4,9 +4,12 @@
package files
import (
+ "html/template"
"testing"
"code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/fileicon"
+ "code.gitea.io/gitea/modules/git"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/services/contexttest"
@@ -48,5 +51,69 @@ func TestGetTreeBySHA(t *testing.T) {
TotalCount: 1,
}
- assert.EqualValues(t, expectedTree, tree)
+ assert.Equal(t, expectedTree, tree)
+}
+
+func TestGetTreeViewNodes(t *testing.T) {
+ unittest.PrepareTestEnv(t)
+ ctx, _ := contexttest.MockContext(t, "user2/repo1")
+ ctx.Repo.RefFullName = git.RefNameFromBranch("sub-home-md-img-check")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+ contexttest.LoadGitRepo(t, ctx)
+ defer ctx.Repo.GitRepo.Close()
+
+ renderedIconPool := fileicon.NewRenderedIconPool()
+ mockIconForFile := func(id string) template.HTML {
+ return template.HTML(`<svg class="svg git-entry-icon octicon-file" width="16" height="16" aria-hidden="true"><use xlink:href="#` + id + `"></use></svg>`)
+ }
+ mockIconForFolder := func(id string) template.HTML {
+ return template.HTML(`<svg class="svg git-entry-icon octicon-file-directory-fill" width="16" height="16" aria-hidden="true"><use xlink:href="#` + id + `"></use></svg>`)
+ }
+ mockOpenIconForFolder := func(id string) template.HTML {
+ return template.HTML(`<svg class="svg git-entry-icon octicon-file-directory-open-fill" width="16" height="16" aria-hidden="true"><use xlink:href="#` + id + `"></use></svg>`)
+ }
+ treeNodes, err := GetTreeViewNodes(ctx, renderedIconPool, ctx.Repo.Commit, "", "")
+ assert.NoError(t, err)
+ assert.Equal(t, []*TreeViewNode{
+ {
+ EntryName: "docs",
+ EntryMode: "tree",
+ FullPath: "docs",
+ EntryIcon: mockIconForFolder(`svg-mfi-folder-docs`),
+ EntryIconOpen: mockOpenIconForFolder(`svg-mfi-folder-docs`),
+ },
+ }, treeNodes)
+
+ treeNodes, err = GetTreeViewNodes(ctx, renderedIconPool, ctx.Repo.Commit, "", "docs/README.md")
+ assert.NoError(t, err)
+ assert.Equal(t, []*TreeViewNode{
+ {
+ EntryName: "docs",
+ EntryMode: "tree",
+ FullPath: "docs",
+ EntryIcon: mockIconForFolder(`svg-mfi-folder-docs`),
+ EntryIconOpen: mockOpenIconForFolder(`svg-mfi-folder-docs`),
+ Children: []*TreeViewNode{
+ {
+ EntryName: "README.md",
+ EntryMode: "blob",
+ FullPath: "docs/README.md",
+ EntryIcon: mockIconForFile(`svg-mfi-readme`),
+ },
+ },
+ },
+ }, treeNodes)
+
+ treeNodes, err = GetTreeViewNodes(ctx, renderedIconPool, ctx.Repo.Commit, "docs", "README.md")
+ assert.NoError(t, err)
+ assert.Equal(t, []*TreeViewNode{
+ {
+ EntryName: "README.md",
+ EntryMode: "blob",
+ FullPath: "docs/README.md",
+ EntryIcon: mockIconForFile(`svg-mfi-readme`),
+ },
+ }, treeNodes)
}
diff --git a/services/repository/files/update.go b/services/repository/files/update.go
index a2763105b0..e871f777e5 100644
--- a/services/repository/files/update.go
+++ b/services/repository/files/update.go
@@ -8,6 +8,7 @@ import (
"fmt"
"io"
"path"
+ "slices"
"strings"
"time"
@@ -15,20 +16,22 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/git/attribute"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/routers/api/v1/utils"
asymkey_service "code.gitea.io/gitea/services/asymkey"
pull_service "code.gitea.io/gitea/services/pull"
)
// IdentityOptions for a person's identity like an author or committer
type IdentityOptions struct {
- Name string
- Email string
+ GitUserName string // to match "git config user.name"
+ GitUserEmail string // to match "git config user.email"
}
// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE
@@ -85,14 +88,32 @@ func (err ErrRepoFileDoesNotExist) Unwrap() error {
return util.ErrNotExist
}
+type LazyReadSeeker interface {
+ io.ReadSeeker
+ io.Closer
+ OpenLazyReader() error
+}
+
// ChangeRepoFiles adds, updates or removes multiple files in the given repository
-func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, opts *ChangeRepoFilesOptions) (*structs.FilesResponse, error) {
+func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, opts *ChangeRepoFilesOptions) (_ *structs.FilesResponse, errRet error) {
+ var addedLfsPointers []lfs.Pointer
+ defer func() {
+ if errRet != nil {
+ for _, lfsPointer := range addedLfsPointers {
+ _, err := git_model.RemoveLFSMetaObjectByOid(ctx, repo.ID, lfsPointer.Oid)
+ if err != nil {
+ log.Error("ChangeRepoFiles: RemoveLFSMetaObjectByOid failed: %v", err)
+ }
+ }
+ }
+ }()
+
err := repo.MustNotBeArchived()
if err != nil {
return nil, err
}
- // If no branch name is set, assume default branch
+ // If no branch name is set, assume the default branch
if opts.OldBranch == "" {
opts.OldBranch = repo.DefaultBranch
}
@@ -107,8 +128,13 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
defer closer.Close()
// oldBranch must exist for this operation
- if _, err := gitRepo.GetBranch(opts.OldBranch); err != nil && !repo.IsEmpty {
+ if exist, err := git_model.IsBranchExist(ctx, repo.ID, opts.OldBranch); err != nil {
return nil, err
+ } else if !exist && !repo.IsEmpty {
+ return nil, git_model.ErrBranchNotExist{
+ RepoID: repo.ID,
+ BranchName: opts.OldBranch,
+ }
}
var treePaths []string
@@ -119,14 +145,14 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
}
// Check that the path given in opts.treePath is valid (not a git path)
- treePath := CleanUploadFileName(file.TreePath)
+ treePath := CleanGitTreePath(file.TreePath)
if treePath == "" {
return nil, ErrFilenameInvalid{
Path: file.TreePath,
}
}
// If there is a fromTreePath (we are copying it), also clean it up
- fromTreePath := CleanUploadFileName(file.FromTreePath)
+ fromTreePath := CleanGitTreePath(file.FromTreePath)
if fromTreePath == "" && file.FromTreePath != "" {
return nil, ErrFilenameInvalid{
Path: file.FromTreePath,
@@ -145,30 +171,28 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
// Check to make sure the branch does not already exist, otherwise we can't proceed.
// If we aren't branching to a new branch, make sure user can commit to the given branch
if opts.NewBranch != opts.OldBranch {
- existingBranch, err := gitRepo.GetBranch(opts.NewBranch)
- if existingBranch != nil {
+ exist, err := git_model.IsBranchExist(ctx, repo.ID, opts.NewBranch)
+ if err != nil {
+ return nil, err
+ }
+ if exist {
return nil, git_model.ErrBranchAlreadyExists{
BranchName: opts.NewBranch,
}
}
- if err != nil && !git.IsErrBranchNotExist(err) {
- return nil, err
- }
} else if err := VerifyBranchProtection(ctx, repo, doer, opts.OldBranch, treePaths); err != nil {
return nil, err
}
message := strings.TrimSpace(opts.Message)
- author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer)
-
- t, err := NewTemporaryUploadRepository(ctx, repo)
+ t, err := NewTemporaryUploadRepository(repo)
if err != nil {
log.Error("NewTemporaryUploadRepository failed: %v", err)
}
defer t.Close()
hasOldBranch := true
- if err := t.Clone(opts.OldBranch, true); err != nil {
+ if err := t.Clone(ctx, opts.OldBranch, true); err != nil {
for _, file := range opts.Files {
if file.Operation == "delete" {
return nil, err
@@ -177,14 +201,14 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
if !git.IsErrBranchNotExist(err) || !repo.IsEmpty {
return nil, err
}
- if err := t.Init(repo.ObjectFormatName); err != nil {
+ if err := t.Init(ctx, repo.ObjectFormatName); err != nil {
return nil, err
}
hasOldBranch = false
opts.LastCommitID = ""
}
if hasOldBranch {
- if err := t.SetDefaultIndex(); err != nil {
+ if err := t.SetDefaultIndex(ctx); err != nil {
return nil, err
}
}
@@ -192,19 +216,13 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
for _, file := range opts.Files {
if file.Operation == "delete" {
// Get the files in the index
- filesInIndex, err := t.LsFiles(file.TreePath)
+ filesInIndex, err := t.LsFiles(ctx, file.TreePath)
if err != nil {
return nil, fmt.Errorf("DeleteRepoFile: %w", err)
}
// Find the file we want to delete in the index
- inFilelist := false
- for _, indexFile := range filesInIndex {
- if indexFile == file.TreePath {
- inFilelist = true
- break
- }
- }
+ inFilelist := slices.Contains(filesInIndex, file.TreePath)
if !inFilelist {
return nil, ErrRepoFileDoesNotExist{
Path: file.TreePath,
@@ -220,7 +238,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
return nil, err // Couldn't get a commit for the branch
}
- // Assigned LastCommitID in opts if it hasn't been set
+ // Assigned LastCommitID in "opts" if it hasn't been set
if opts.LastCommitID == "" {
opts.LastCommitID = commit.ID.String()
} else {
@@ -232,22 +250,25 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
}
for _, file := range opts.Files {
- if err := handleCheckErrors(file, commit, opts); err != nil {
+ if err = handleCheckErrors(file, commit, opts); err != nil {
return nil, err
}
}
}
- contentStore := lfs.NewContentStore()
+ lfsContentStore := lfs.NewContentStore()
for _, file := range opts.Files {
switch file.Operation {
- case "create", "update":
- if err := CreateOrUpdateFile(ctx, t, file, contentStore, repo.ID, hasOldBranch); err != nil {
+ case "create", "update", "rename", "upload":
+ addedLfsPointer, err := modifyFile(ctx, t, file, lfsContentStore, repo.ID)
+ if err != nil {
return nil, err
}
+ if addedLfsPointer != nil {
+ addedLfsPointers = append(addedLfsPointers, *addedLfsPointer)
+ }
case "delete":
- // Remove the file from the index
- if err := t.RemoveFilesFromIndex(file.TreePath); err != nil {
+ if err = t.RemoveFilesFromIndex(ctx, file.TreePath); err != nil {
return nil, err
}
default:
@@ -256,24 +277,33 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
}
// Now write the tree
- treeHash, err := t.WriteTree()
+ treeHash, err := t.WriteTree(ctx)
if err != nil {
return nil, err
}
// Now commit the tree
- var commitHash string
+ commitOpts := &CommitTreeUserOptions{
+ ParentCommitID: opts.LastCommitID,
+ TreeHash: treeHash,
+ CommitMessage: message,
+ SignOff: opts.Signoff,
+ DoerUser: doer,
+ AuthorIdentity: opts.Author,
+ AuthorTime: nil,
+ CommitterIdentity: opts.Committer,
+ CommitterTime: nil,
+ }
if opts.Dates != nil {
- commitHash, err = t.CommitTreeWithDate(opts.LastCommitID, author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer)
- } else {
- commitHash, err = t.CommitTree(opts.LastCommitID, author, committer, treeHash, message, opts.Signoff)
+ commitOpts.AuthorTime, commitOpts.CommitterTime = &opts.Dates.Author, &opts.Dates.Committer
}
+ commitHash, err := t.CommitTree(ctx, commitOpts)
if err != nil {
return nil, err
}
// Then push this tree to NewBranch
- if err := t.Push(doer, commitHash, opts.NewBranch); err != nil {
+ if err := t.Push(ctx, doer, commitHash, opts.NewBranch); err != nil {
log.Error("%T %v", err, err)
return nil, err
}
@@ -283,14 +313,16 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
return nil, err
}
- filesResponse, err := GetFilesResponseFromCommit(ctx, repo, commit, opts.NewBranch, treePaths)
+ // FIXME: this call seems not right, why it needs to read the file content again
+ // FIXME: why it uses the NewBranch as "ref", it should use the commit ID because the response is only for this commit
+ filesResponse, err := GetFilesResponseFromCommit(ctx, repo, gitRepo, utils.NewRefCommit(git.RefNameFromBranch(opts.NewBranch), commit), treePaths)
if err != nil {
return nil, err
}
if repo.IsEmpty {
if isEmpty, err := gitRepo.IsEmpty(); err == nil && !isEmpty {
- _ = repo_model.UpdateRepositoryCols(ctx, &repo_model.Repository{ID: repo.ID, IsEmpty: false, DefaultBranch: opts.NewBranch}, "is_empty", "default_branch")
+ _ = repo_model.UpdateRepositoryColsWithAutoTime(ctx, &repo_model.Repository{ID: repo.ID, IsEmpty: false, DefaultBranch: opts.NewBranch}, "is_empty", "default_branch")
}
}
@@ -356,22 +388,33 @@ func (err ErrSHAOrCommitIDNotProvided) Error() string {
// handles the check for various issues for ChangeRepoFiles
func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRepoFilesOptions) error {
- if file.Operation == "update" || file.Operation == "delete" {
- fromEntry, err := commit.GetTreeEntryByPath(file.Options.fromTreePath)
- if err != nil {
- return err
+ // check old entry (fromTreePath/fromEntry)
+ if file.Operation == "update" || file.Operation == "upload" || file.Operation == "delete" || file.Operation == "rename" {
+ var fromEntryIDString string
+ {
+ fromEntry, err := commit.GetTreeEntryByPath(file.Options.fromTreePath)
+ if file.Operation == "upload" && git.IsErrNotExist(err) {
+ fromEntry = nil
+ } else if err != nil {
+ return err
+ }
+ if fromEntry != nil {
+ fromEntryIDString = fromEntry.ID.String()
+ file.Options.executable = fromEntry.IsExecutable() // FIXME: legacy hacky approach, it shouldn't prepare the "Options" in the "check" function
+ }
}
+
if file.SHA != "" {
- // If a SHA was given and the SHA given doesn't match the SHA of the fromTreePath, throw error
- if file.SHA != fromEntry.ID.String() {
+ // If the SHA given doesn't match the SHA of the fromTreePath, throw error
+ if file.SHA != fromEntryIDString {
return pull_service.ErrSHADoesNotMatch{
Path: file.Options.treePath,
GivenSHA: file.SHA,
- CurrentSHA: fromEntry.ID.String(),
+ CurrentSHA: fromEntryIDString,
}
}
} else if opts.LastCommitID != "" {
- // If a lastCommitID was given and it doesn't match the commitID of the head of the branch throw
+ // If a lastCommitID given doesn't match the branch head's commitID throw
// an error, but only if we aren't creating a new branch.
if commit.ID.String() != opts.LastCommitID && opts.OldBranch == opts.NewBranch {
if changed, err := commit.FileChangedSinceCommit(file.Options.treePath, opts.LastCommitID); err != nil {
@@ -389,13 +432,13 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
// haven't been made. We throw an error if one wasn't provided.
return ErrSHAOrCommitIDNotProvided{}
}
- file.Options.executable = fromEntry.IsExecutable()
}
- if file.Operation == "create" || file.Operation == "update" {
- // For the path where this file will be created/updated, we need to make
- // sure no parts of the path are existing files or links except for the last
- // item in the path which is the file name, and that shouldn't exist IF it is
- // a new file OR is being moved to a new path.
+
+ // check new entry (treePath/treeEntry)
+ if file.Operation == "create" || file.Operation == "update" || file.Operation == "upload" || file.Operation == "rename" {
+ // For operation's target path, we need to make sure no parts of the path are existing files or links
+ // except for the last item in the path (which is the file name).
+ // And that shouldn't exist IF it is a new file OR is being moved to a new path.
treePathParts := strings.Split(file.Options.treePath, "/")
subTreePath := ""
for index, part := range treePathParts {
@@ -432,7 +475,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
Type: git.EntryModeTree,
}
} else if file.Options.fromTreePath != file.Options.treePath || file.Operation == "create" {
- // The entry shouldn't exist if we are creating new file or moving to a new path
+ // The entry shouldn't exist if we are creating the new file or moving to a new path
return ErrRepoFileAlreadyExists{
Path: file.Options.treePath,
}
@@ -443,21 +486,23 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep
return nil
}
-// CreateOrUpdateFile handles creating or updating a file for ChangeRepoFiles
-func CreateOrUpdateFile(ctx context.Context, t *TemporaryUploadRepository, file *ChangeRepoFile, contentStore *lfs.ContentStore, repoID int64, hasOldBranch bool) error {
+func modifyFile(ctx context.Context, t *TemporaryUploadRepository, file *ChangeRepoFile, contentStore *lfs.ContentStore, repoID int64) (addedLfsPointer *lfs.Pointer, _ error) {
+ if rd, ok := file.ContentReader.(LazyReadSeeker); ok {
+ if err := rd.OpenLazyReader(); err != nil {
+ return nil, fmt.Errorf("OpenLazyReader: %w", err)
+ }
+ defer rd.Close()
+ }
+
// Get the two paths (might be the same if not moving) from the index if they exist
- filesInIndex, err := t.LsFiles(file.TreePath, file.FromTreePath)
+ filesInIndex, err := t.LsFiles(ctx, file.TreePath, file.FromTreePath)
if err != nil {
- return fmt.Errorf("UpdateRepoFile: %w", err)
+ return nil, fmt.Errorf("LsFiles: %w", err)
}
// If is a new file (not updating) then the given path shouldn't exist
if file.Operation == "create" {
- for _, indexFile := range filesInIndex {
- if indexFile == file.TreePath {
- return ErrRepoFileAlreadyExists{
- Path: file.TreePath,
- }
- }
+ if slices.Contains(filesInIndex, file.TreePath) {
+ return nil, ErrRepoFileAlreadyExists{Path: file.TreePath}
}
}
@@ -465,79 +510,178 @@ func CreateOrUpdateFile(ctx context.Context, t *TemporaryUploadRepository, file
if file.Options.fromTreePath != file.Options.treePath && len(filesInIndex) > 0 {
for _, indexFile := range filesInIndex {
if indexFile == file.Options.fromTreePath {
- if err := t.RemoveFilesFromIndex(file.FromTreePath); err != nil {
- return err
+ if err = t.RemoveFilesFromIndex(ctx, file.FromTreePath); err != nil {
+ return nil, err
}
}
}
}
- treeObjectContentReader := file.ContentReader
- var lfsMetaObject *git_model.LFSMetaObject
- if setting.LFS.StartServer && hasOldBranch {
- // Check there is no way this can return multiple infos
- filename2attribute2info, err := t.gitRepo.CheckAttribute(git.CheckAttributeOpts{
- Attributes: []string{"filter"},
- Filenames: []string{file.Options.treePath},
- CachedOnly: true,
- })
+ var writeObjectRet *writeRepoObjectRet
+ switch file.Operation {
+ case "create", "update", "upload":
+ writeObjectRet, err = writeRepoObjectForModify(ctx, t, file)
+ case "rename":
+ writeObjectRet, err = writeRepoObjectForRename(ctx, t, file)
+ default:
+ return nil, util.NewInvalidArgumentErrorf("unknown file modification operation: '%s'", file.Operation)
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ // Add the object to the index, the "file.Options.executable" is set in handleCheckErrors by the caller (legacy hacky approach)
+ if err = t.AddObjectToIndex(ctx, util.Iif(file.Options.executable, "100755", "100644"), writeObjectRet.ObjectHash, file.Options.treePath); err != nil {
+ return nil, err
+ }
+
+ if writeObjectRet.LfsContent == nil {
+ return nil, nil // No LFS pointer, so nothing to do
+ }
+ defer writeObjectRet.LfsContent.Close()
+
+ // Now we must store the content into an LFS object
+ lfsMetaObject, err := git_model.NewLFSMetaObject(ctx, repoID, writeObjectRet.LfsPointer)
+ if err != nil {
+ return nil, err
+ }
+ exist, err := contentStore.Exists(lfsMetaObject.Pointer)
+ if err != nil {
+ return nil, err
+ }
+ if !exist {
+ err = contentStore.Put(lfsMetaObject.Pointer, writeObjectRet.LfsContent)
if err != nil {
- return err
+ if _, errRemove := git_model.RemoveLFSMetaObjectByOid(ctx, repoID, lfsMetaObject.Oid); errRemove != nil {
+ return nil, fmt.Errorf("unable to remove failed inserted LFS object %s: %v (Prev Error: %w)", lfsMetaObject.Oid, errRemove, err)
+ }
+ return nil, err
}
+ }
+ return &lfsMetaObject.Pointer, nil
+}
- if filename2attribute2info[file.Options.treePath] != nil && filename2attribute2info[file.Options.treePath]["filter"] == "lfs" {
- // OK so we are supposed to LFS this data!
- pointer, err := lfs.GeneratePointer(treeObjectContentReader)
+func checkIsLfsFileInGitAttributes(ctx context.Context, t *TemporaryUploadRepository, paths []string) (ret []bool, err error) {
+ attributesMap, err := attribute.CheckAttributes(ctx, t.gitRepo, "" /* use temp repo's working dir */, attribute.CheckAttributeOpts{
+ Attributes: []string{attribute.Filter},
+ Filenames: paths,
+ })
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range paths {
+ isLFSFile := attributesMap[p] != nil && attributesMap[p].Get(attribute.Filter).ToString().Value() == "lfs"
+ ret = append(ret, isLFSFile)
+ }
+ return ret, nil
+}
+
+type writeRepoObjectRet struct {
+ ObjectHash string
+ LfsContent io.ReadCloser // if not nil, then the caller should store its content in LfsPointer, then close it
+ LfsPointer lfs.Pointer
+}
+
+// writeRepoObjectForModify hashes the git object for create or update operations
+func writeRepoObjectForModify(ctx context.Context, t *TemporaryUploadRepository, file *ChangeRepoFile) (ret *writeRepoObjectRet, err error) {
+ ret = &writeRepoObjectRet{}
+ treeObjectContentReader := file.ContentReader
+ if setting.LFS.StartServer {
+ checkIsLfsFiles, err := checkIsLfsFileInGitAttributes(ctx, t, []string{file.Options.treePath})
+ if err != nil {
+ return nil, err
+ }
+ if checkIsLfsFiles[0] {
+ // OK, so we are supposed to LFS this data!
+ ret.LfsPointer, err = lfs.GeneratePointer(file.ContentReader)
if err != nil {
- return err
+ return nil, err
}
- lfsMetaObject = &git_model.LFSMetaObject{Pointer: pointer, RepositoryID: repoID}
- treeObjectContentReader = strings.NewReader(pointer.StringContent())
+ if _, err = file.ContentReader.Seek(0, io.SeekStart); err != nil {
+ return nil, err
+ }
+ ret.LfsContent = io.NopCloser(file.ContentReader)
+ treeObjectContentReader = strings.NewReader(ret.LfsPointer.StringContent())
}
}
- // Add the object to the database
- objectHash, err := t.HashObject(treeObjectContentReader)
+ ret.ObjectHash, err = t.HashObjectAndWrite(ctx, treeObjectContentReader)
if err != nil {
- return err
+ return nil, err
}
+ return ret, nil
+}
- // Add the object to the index
- if file.Options.executable {
- if err := t.AddObjectToIndex("100755", objectHash, file.Options.treePath); err != nil {
- return err
- }
- } else {
- if err := t.AddObjectToIndex("100644", objectHash, file.Options.treePath); err != nil {
- return err
+// writeRepoObjectForRename the same as writeRepoObjectForModify buf for "rename"
+func writeRepoObjectForRename(ctx context.Context, t *TemporaryUploadRepository, file *ChangeRepoFile) (ret *writeRepoObjectRet, err error) {
+ lastCommitID, err := t.GetLastCommit(ctx)
+ if err != nil {
+ return nil, err
+ }
+ commit, err := t.GetCommit(lastCommitID)
+ if err != nil {
+ return nil, err
+ }
+ oldEntry, err := commit.GetTreeEntryByPath(file.Options.fromTreePath)
+ if err != nil {
+ return nil, err
+ }
+
+ ret = &writeRepoObjectRet{ObjectHash: oldEntry.ID.String()}
+ if !setting.LFS.StartServer {
+ return ret, nil
+ }
+
+ checkIsLfsFiles, err := checkIsLfsFileInGitAttributes(ctx, t, []string{file.Options.fromTreePath, file.Options.treePath})
+ if err != nil {
+ return nil, err
+ }
+ oldIsLfs, newIsLfs := checkIsLfsFiles[0], checkIsLfsFiles[1]
+
+ // If the old and new paths are both in lfs or both not in lfs, the object hash of the old file can be used directly
+ // as the object doesn't change
+ if oldIsLfs == newIsLfs {
+ return ret, nil
+ }
+
+ oldEntryBlobPointerBy := func(f func(r io.Reader) (lfs.Pointer, error)) (lfsPointer lfs.Pointer, err error) {
+ r, err := oldEntry.Blob().DataAsync()
+ if err != nil {
+ return lfsPointer, err
}
+ defer r.Close()
+ return f(r)
}
- if lfsMetaObject != nil {
- // We have an LFS object - create it
- lfsMetaObject, err = git_model.NewLFSMetaObject(ctx, lfsMetaObject.RepositoryID, lfsMetaObject.Pointer)
+ var treeObjectContentReader io.ReadCloser
+ if oldIsLfs {
+ // If the old is in lfs but the new isn't, read the content from lfs and add it as a normal git object
+ pointer, err := oldEntryBlobPointerBy(lfs.ReadPointer)
if err != nil {
- return err
+ return nil, err
}
- exist, err := contentStore.Exists(lfsMetaObject.Pointer)
+ treeObjectContentReader, err = lfs.ReadMetaObject(pointer)
if err != nil {
- return err
+ return nil, err
}
- if !exist {
- _, err := file.ContentReader.Seek(0, io.SeekStart)
- if err != nil {
- return err
- }
- if err := contentStore.Put(lfsMetaObject.Pointer, file.ContentReader); err != nil {
- if _, err2 := git_model.RemoveLFSMetaObjectByOid(ctx, repoID, lfsMetaObject.Oid); err2 != nil {
- return fmt.Errorf("unable to remove failed inserted LFS object %s: %v (Prev Error: %w)", lfsMetaObject.Oid, err2, err)
- }
- return err
- }
+ defer treeObjectContentReader.Close()
+ } else {
+ // If the new is in lfs but the old isn't, read the content from the git object and generate a lfs pointer of it
+ ret.LfsPointer, err = oldEntryBlobPointerBy(lfs.GeneratePointer)
+ if err != nil {
+ return nil, err
+ }
+ ret.LfsContent, err = oldEntry.Blob().DataAsync()
+ if err != nil {
+ return nil, err
}
+ treeObjectContentReader = io.NopCloser(strings.NewReader(ret.LfsPointer.StringContent()))
}
-
- return nil
+ ret.ObjectHash, err = t.HashObjectAndWrite(ctx, treeObjectContentReader)
+ if err != nil {
+ return nil, err
+ }
+ return ret, nil
}
// VerifyBranchProtection verify the branch protection for modifying the given treePath on the given branch
diff --git a/services/repository/files/upload.go b/services/repository/files/upload.go
index cbfaf49d13..b783cbd01d 100644
--- a/services/repository/files/upload.go
+++ b/services/repository/files/upload.go
@@ -8,14 +8,11 @@ import (
"fmt"
"os"
"path"
- "strings"
+ "sync"
- git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/lfs"
- "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/log"
)
// UploadRepoFileOptions contains the uploaded repository file options
@@ -27,199 +24,88 @@ type UploadRepoFileOptions struct {
Message string
Files []string // In UUID format.
Signoff bool
+ Author *IdentityOptions
+ Committer *IdentityOptions
}
-type uploadInfo struct {
- upload *repo_model.Upload
- lfsMetaObject *git_model.LFSMetaObject
+type lazyLocalFileReader struct {
+ *os.File
+ localFilename string
+ counter int
+ mu sync.Mutex
}
-func cleanUpAfterFailure(ctx context.Context, infos *[]uploadInfo, t *TemporaryUploadRepository, original error) error {
- for _, info := range *infos {
- if info.lfsMetaObject == nil {
- continue
- }
- if !info.lfsMetaObject.Existing {
- if _, err := git_model.RemoveLFSMetaObjectByOid(ctx, t.repo.ID, info.lfsMetaObject.Oid); err != nil {
- original = fmt.Errorf("%w, %v", original, err) // We wrap the original error - as this is the underlying error that required the fallback
- }
- }
- }
- return original
-}
-
-// UploadRepoFiles uploads files to the given repository
-func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, opts *UploadRepoFileOptions) error {
- if len(opts.Files) == 0 {
- return nil
- }
+var _ LazyReadSeeker = (*lazyLocalFileReader)(nil)
- uploads, err := repo_model.GetUploadsByUUIDs(ctx, opts.Files)
- if err != nil {
- return fmt.Errorf("GetUploadsByUUIDs [uuids: %v]: %w", opts.Files, err)
- }
+func (l *lazyLocalFileReader) Close() error {
+ l.mu.Lock()
+ defer l.mu.Unlock()
- names := make([]string, len(uploads))
- infos := make([]uploadInfo, len(uploads))
- for i, upload := range uploads {
- // Check file is not lfs locked, will return nil if lock setting not enabled
- filepath := path.Join(opts.TreePath, upload.Name)
- lfsLock, err := git_model.GetTreePathLock(ctx, repo.ID, filepath)
- if err != nil {
- return err
- }
- if lfsLock != nil && lfsLock.OwnerID != doer.ID {
- u, err := user_model.GetUserByID(ctx, lfsLock.OwnerID)
- if err != nil {
- return err
+ if l.counter > 0 {
+ l.counter--
+ if l.counter == 0 {
+ if err := l.File.Close(); err != nil {
+ return fmt.Errorf("close file %s: %w", l.localFilename, err)
}
- return git_model.ErrLFSFileLocked{RepoID: repo.ID, Path: filepath, UserName: u.Name}
- }
-
- names[i] = upload.Name
- infos[i] = uploadInfo{upload: upload}
- }
-
- t, err := NewTemporaryUploadRepository(ctx, repo)
- if err != nil {
- return err
- }
- defer t.Close()
-
- hasOldBranch := true
- if err = t.Clone(opts.OldBranch, true); err != nil {
- if !git.IsErrBranchNotExist(err) || !repo.IsEmpty {
- return err
- }
- if err = t.Init(repo.ObjectFormatName); err != nil {
- return err
- }
- hasOldBranch = false
- opts.LastCommitID = ""
- }
- if hasOldBranch {
- if err = t.SetDefaultIndex(); err != nil {
- return err
- }
- }
-
- var filename2attribute2info map[string]map[string]string
- if setting.LFS.StartServer {
- filename2attribute2info, err = t.gitRepo.CheckAttribute(git.CheckAttributeOpts{
- Attributes: []string{"filter"},
- Filenames: names,
- CachedOnly: true,
- })
- if err != nil {
- return err
+ l.File = nil
}
+ return nil
}
+ return fmt.Errorf("file %s already closed", l.localFilename)
+}
- // Copy uploaded files into repository.
- for i := range infos {
- if err := copyUploadedLFSFileIntoRepository(&infos[i], filename2attribute2info, t, opts.TreePath); err != nil {
- return err
- }
- }
+func (l *lazyLocalFileReader) OpenLazyReader() error {
+ l.mu.Lock()
+ defer l.mu.Unlock()
- // Now write the tree
- treeHash, err := t.WriteTree()
- if err != nil {
- return err
+ if l.File != nil {
+ l.counter++
+ return nil
}
- // make author and committer the doer
- author := doer
- committer := doer
-
- // Now commit the tree
- commitHash, err := t.CommitTree(opts.LastCommitID, author, committer, treeHash, opts.Message, opts.Signoff)
+ file, err := os.Open(l.localFilename)
if err != nil {
return err
}
+ l.File = file
+ l.counter = 1
+ return nil
+}
- // Now deal with LFS objects
- for i := range infos {
- if infos[i].lfsMetaObject == nil {
- continue
- }
- infos[i].lfsMetaObject, err = git_model.NewLFSMetaObject(ctx, infos[i].lfsMetaObject.RepositoryID, infos[i].lfsMetaObject.Pointer)
- if err != nil {
- // OK Now we need to cleanup
- return cleanUpAfterFailure(ctx, &infos, t, err)
- }
- // Don't move the files yet - we need to ensure that
- // everything can be inserted first
- }
-
- // OK now we can insert the data into the store - there's no way to clean up the store
- // once it's in there, it's in there.
- contentStore := lfs.NewContentStore()
- for _, info := range infos {
- if err := uploadToLFSContentStore(info, contentStore); err != nil {
- return cleanUpAfterFailure(ctx, &infos, t, err)
- }
- }
-
- // Then push this tree to NewBranch
- if err := t.Push(doer, commitHash, opts.NewBranch); err != nil {
- return err
+// UploadRepoFiles uploads files to the given repository
+func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, opts *UploadRepoFileOptions) error {
+ if len(opts.Files) == 0 {
+ return nil
}
- return repo_model.DeleteUploads(ctx, uploads...)
-}
-
-func copyUploadedLFSFileIntoRepository(info *uploadInfo, filename2attribute2info map[string]map[string]string, t *TemporaryUploadRepository, treePath string) error {
- file, err := os.Open(info.upload.LocalPath())
+ uploads, err := repo_model.GetUploadsByUUIDs(ctx, opts.Files)
if err != nil {
- return err
+ return fmt.Errorf("GetUploadsByUUIDs [uuids: %v]: %w", opts.Files, err)
}
- defer file.Close()
- var objectHash string
- if setting.LFS.StartServer && filename2attribute2info[info.upload.Name] != nil && filename2attribute2info[info.upload.Name]["filter"] == "lfs" {
- // Handle LFS
- // FIXME: Inefficient! this should probably happen in models.Upload
- pointer, err := lfs.GeneratePointer(file)
- if err != nil {
- return err
- }
-
- info.lfsMetaObject = &git_model.LFSMetaObject{Pointer: pointer, RepositoryID: t.repo.ID}
-
- if objectHash, err = t.HashObject(strings.NewReader(pointer.StringContent())); err != nil {
- return err
- }
- } else if objectHash, err = t.HashObject(file); err != nil {
- return err
+ changeOpts := &ChangeRepoFilesOptions{
+ LastCommitID: opts.LastCommitID,
+ OldBranch: opts.OldBranch,
+ NewBranch: opts.NewBranch,
+ Message: opts.Message,
+ Signoff: opts.Signoff,
+ Author: opts.Author,
+ Committer: opts.Committer,
+ }
+ for _, upload := range uploads {
+ changeOpts.Files = append(changeOpts.Files, &ChangeRepoFile{
+ Operation: "upload",
+ TreePath: path.Join(opts.TreePath, upload.Name),
+ ContentReader: &lazyLocalFileReader{localFilename: upload.LocalPath()},
+ })
}
- // Add the object to the index
- return t.AddObjectToIndex("100644", objectHash, path.Join(treePath, info.upload.Name))
-}
-
-func uploadToLFSContentStore(info uploadInfo, contentStore *lfs.ContentStore) error {
- if info.lfsMetaObject == nil {
- return nil
- }
- exist, err := contentStore.Exists(info.lfsMetaObject.Pointer)
+ _, err = ChangeRepoFiles(ctx, repo, doer, changeOpts)
if err != nil {
return err
}
- if !exist {
- file, err := os.Open(info.upload.LocalPath())
- if err != nil {
- return err
- }
-
- defer file.Close()
- // FIXME: Put regenerates the hash and copies the file over.
- // I guess this strictly ensures the soundness of the store but this is inefficient.
- if err := contentStore.Put(info.lfsMetaObject.Pointer, file); err != nil {
- // OK Now we need to cleanup
- // Can't clean up the store, once uploaded there they're there.
- return err
- }
+ if err := repo_model.DeleteUploads(ctx, uploads...); err != nil {
+ log.Error("DeleteUploads: %v", err)
}
return nil
}
diff --git a/services/repository/fork.go b/services/repository/fork.go
index cff0b1a403..8bd3498b17 100644
--- a/services/repository/fork.go
+++ b/services/repository/fork.go
@@ -65,7 +65,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
}
// Fork is prohibited, if user has reached maximum limit of repositories
- if !owner.CanForkRepo() {
+ if !doer.CanForkRepoIn(owner) {
return nil, repo_model.ErrReachLimitOfRepo{
Limit: owner.MaxRepoCreation,
}
@@ -100,117 +100,106 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
IsFork: true,
ForkID: opts.BaseRepo.ID,
ObjectFormatName: opts.BaseRepo.ObjectFormatName,
+ Status: repo_model.RepositoryBeingMigrated,
}
- oldRepoPath := opts.BaseRepo.RepoPath()
-
- needsRollback := false
- rollbackFn := func() {
- if !needsRollback {
- return
- }
-
- repoPath := repo_model.RepoPath(owner.Name, repo.Name)
-
- if exists, _ := util.IsExist(repoPath); !exists {
- return
- }
-
- // As the transaction will be failed and hence database changes will be destroyed we only need
- // to delete the related repository on the filesystem
- if errDelete := util.RemoveAll(repoPath); errDelete != nil {
- log.Error("Failed to remove fork repo")
- }
- }
-
- needsRollbackInPanic := true
- defer func() {
- panicErr := recover()
- if panicErr == nil {
- return
- }
-
- if needsRollbackInPanic {
- rollbackFn()
- }
- panic(panicErr)
- }()
-
- err = db.WithTx(ctx, func(txCtx context.Context) error {
- if err = CreateRepositoryByExample(txCtx, doer, owner, repo, false, true); err != nil {
+ // 1 - Create the repository in the database
+ err = db.WithTx(ctx, func(ctx context.Context) error {
+ if err = createRepositoryInDB(ctx, doer, owner, repo, true); err != nil {
return err
}
-
- if err = repo_model.IncrementRepoForkNum(txCtx, opts.BaseRepo.ID); err != nil {
+ if err = repo_model.IncrementRepoForkNum(ctx, opts.BaseRepo.ID); err != nil {
return err
}
// copy lfs files failure should not be ignored
- if err = git_model.CopyLFS(txCtx, repo, opts.BaseRepo); err != nil {
- return err
- }
-
- needsRollback = true
+ return git_model.CopyLFS(ctx, repo, opts.BaseRepo)
+ })
+ if err != nil {
+ return nil, err
+ }
- cloneCmd := git.NewCommand(txCtx, "clone", "--bare")
- if opts.SingleBranch != "" {
- cloneCmd.AddArguments("--single-branch", "--branch").AddDynamicArguments(opts.SingleBranch)
- }
- repoPath := repo_model.RepoPath(owner.Name, repo.Name)
- if stdout, _, err := cloneCmd.AddDynamicArguments(oldRepoPath, repoPath).
- RunStdBytes(&git.RunOpts{Timeout: 10 * time.Minute}); err != nil {
- log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, opts.BaseRepo, stdout, err)
- return fmt.Errorf("git clone: %w", err)
+ // last - clean up if something goes wrong
+ // WARNING: Don't override all later err with local variables
+ defer func() {
+ if err != nil {
+ // we can not use the ctx because it maybe canceled or timeout
+ cleanupRepository(repo.ID)
}
+ }()
- if err := repo_module.CheckDaemonExportOK(txCtx, repo); err != nil {
- return fmt.Errorf("checkDaemonExportOK: %w", err)
+ // 2 - check whether the repository with the same storage exists
+ var isExist bool
+ isExist, err = gitrepo.IsRepositoryExist(ctx, repo)
+ if err != nil {
+ log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err)
+ return nil, err
+ }
+ if isExist {
+ log.Error("Files already exist in %s and we are not going to adopt or delete.", repo.FullName())
+ // Don't return directly, we need err in defer to cleanupRepository
+ err = repo_model.ErrRepoFilesAlreadyExist{
+ Uname: repo.OwnerName,
+ Name: repo.Name,
}
+ return nil, err
+ }
- if stdout, _, err := git.NewCommand(txCtx, "update-server-info").
- RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
- log.Error("Fork Repository (git update-server-info) failed for %v:\nStdout: %s\nError: %v", repo, stdout, err)
- return fmt.Errorf("git update-server-info: %w", err)
- }
+ // 3 - Clone the repository
+ cloneCmd := git.NewCommand("clone", "--bare")
+ if opts.SingleBranch != "" {
+ cloneCmd.AddArguments("--single-branch", "--branch").AddDynamicArguments(opts.SingleBranch)
+ }
+ var stdout []byte
+ if stdout, _, err = cloneCmd.AddDynamicArguments(opts.BaseRepo.RepoPath(), repo.RepoPath()).
+ RunStdBytes(ctx, &git.RunOpts{Timeout: 10 * time.Minute}); err != nil {
+ log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, opts.BaseRepo, stdout, err)
+ return nil, fmt.Errorf("git clone: %w", err)
+ }
- if err = repo_module.CreateDelegateHooks(repoPath); err != nil {
- return fmt.Errorf("createDelegateHooks: %w", err)
- }
+ // 4 - Update the git repository
+ if err = updateGitRepoAfterCreate(ctx, repo); err != nil {
+ return nil, fmt.Errorf("updateGitRepoAfterCreate: %w", err)
+ }
- gitRepo, err := gitrepo.OpenRepository(txCtx, repo)
- if err != nil {
- return fmt.Errorf("OpenRepository: %w", err)
- }
- defer gitRepo.Close()
+ // 5 - Create hooks
+ if err = gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
+ return nil, fmt.Errorf("createDelegateHooks: %w", err)
+ }
- _, err = repo_module.SyncRepoBranchesWithRepo(txCtx, repo, gitRepo, doer.ID)
- return err
- })
- needsRollbackInPanic = false
+ // 6 - Sync the repository branches and tags
+ var gitRepo *git.Repository
+ gitRepo, err = gitrepo.OpenRepository(ctx, repo)
if err != nil {
- rollbackFn()
- return nil, err
+ return nil, fmt.Errorf("OpenRepository: %w", err)
}
+ defer gitRepo.Close()
+ if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, doer.ID); err != nil {
+ return nil, fmt.Errorf("SyncRepoBranchesWithRepo: %w", err)
+ }
+ if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil {
+ return nil, fmt.Errorf("Sync releases from git tags failed: %v", err)
+ }
+
+ // 7 - Update the repository
// even if below operations failed, it could be ignored. And they will be retried
- if err := repo_module.UpdateRepoSize(ctx, repo); err != nil {
+ if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
log.Error("Failed to update size for repository: %v", err)
+ err = nil
}
- if err := repo_model.CopyLanguageStat(ctx, opts.BaseRepo, repo); err != nil {
+ if err = repo_model.CopyLanguageStat(ctx, opts.BaseRepo, repo); err != nil {
log.Error("Copy language stat from oldRepo failed: %v", err)
+ err = nil
}
- if err := repo_model.CopyLicense(ctx, opts.BaseRepo, repo); err != nil {
+ if err = repo_model.CopyLicense(ctx, opts.BaseRepo, repo); err != nil {
return nil, err
}
- gitRepo, err := gitrepo.OpenRepository(ctx, repo)
- if err != nil {
- log.Error("Open created git repository failed: %v", err)
- } else {
- defer gitRepo.Close()
- if err := repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil {
- log.Error("Sync releases from git tags failed: %v", err)
- }
+ // 8 - update repository status to be ready
+ repo.Status = repo_model.RepositoryReady
+ if err = repo_model.UpdateRepositoryColsWithAutoTime(ctx, repo, "status"); err != nil {
+ return nil, fmt.Errorf("UpdateRepositoryCols: %w", err)
}
notify_service.ForkRepository(ctx, doer, opts.BaseRepo, repo)
@@ -220,7 +209,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
// ConvertForkToNormalRepository convert the provided repo from a forked repo to normal repo
func ConvertForkToNormalRepository(ctx context.Context, repo *repo_model.Repository) error {
- err := db.WithTx(ctx, func(ctx context.Context) error {
+ return db.WithTx(ctx, func(ctx context.Context) error {
repo, err := repo_model.GetRepositoryByID(ctx, repo.ID)
if err != nil {
return err
@@ -237,16 +226,8 @@ func ConvertForkToNormalRepository(ctx context.Context, repo *repo_model.Reposit
repo.IsFork = false
repo.ForkID = 0
-
- if err := repo_module.UpdateRepository(ctx, repo, false); err != nil {
- log.Error("Unable to update repository %-v whilst converting from fork. Error: %v", repo, err)
- return err
- }
-
- return nil
+ return repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_fork", "fork_id")
})
-
- return err
}
type findForksOptions struct {
@@ -256,9 +237,11 @@ type findForksOptions struct {
}
func (opts findForksOptions) ToConds() builder.Cond {
- return builder.Eq{"fork_id": opts.RepoID}.And(
- repo_model.AccessibleRepositoryCondition(opts.Doer, unit.TypeInvalid),
- )
+ cond := builder.Eq{"fork_id": opts.RepoID}
+ if opts.Doer != nil && opts.Doer.IsAdmin {
+ return cond
+ }
+ return cond.And(repo_model.AccessibleRepositoryCondition(opts.Doer, unit.TypeInvalid))
}
// FindForks returns all the forks of the repository
diff --git a/services/repository/fork_test.go b/services/repository/fork_test.go
index 452798b25b..5375f79028 100644
--- a/services/repository/fork_test.go
+++ b/services/repository/fork_test.go
@@ -4,13 +4,17 @@
package repository
import (
+ "os"
"testing"
+ "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
+ "code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
)
@@ -35,7 +39,7 @@ func TestForkRepository(t *testing.T) {
assert.False(t, repo_model.IsErrReachLimitOfRepo(err))
// change AllowForkWithoutMaximumLimit to false for the test
- setting.Repository.AllowForkWithoutMaximumLimit = false
+ defer test.MockVariableValue(&setting.Repository.AllowForkWithoutMaximumLimit, false)()
// user has reached maximum limit of repositories
user.MaxRepoCreation = 0
fork2, err := ForkRepository(git.DefaultContext, user, user, ForkRepoOptions{
@@ -46,3 +50,43 @@ func TestForkRepository(t *testing.T) {
assert.Nil(t, fork2)
assert.True(t, repo_model.IsErrReachLimitOfRepo(err))
}
+
+func TestForkRepositoryCleanup(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ // a successful fork
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo10 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
+
+ fork, err := ForkRepository(git.DefaultContext, user2, user2, ForkRepoOptions{
+ BaseRepo: repo10,
+ Name: "test",
+ })
+ assert.NoError(t, err)
+ assert.NotNil(t, fork)
+
+ exist, err := util.IsExist(repo_model.RepoPath(user2.Name, "test"))
+ assert.NoError(t, err)
+ assert.True(t, exist)
+
+ err = DeleteRepositoryDirectly(db.DefaultContext, fork.ID)
+ assert.NoError(t, err)
+
+ // a failed creating because some mock data
+ // create the repository directory so that the creation will fail after database record created.
+ assert.NoError(t, os.MkdirAll(repo_model.RepoPath(user2.Name, "test"), os.ModePerm))
+
+ fork2, err := ForkRepository(db.DefaultContext, user2, user2, ForkRepoOptions{
+ BaseRepo: repo10,
+ Name: "test",
+ })
+ assert.Nil(t, fork2)
+ assert.Error(t, err)
+
+ // assert the cleanup is successful
+ unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: "test"})
+
+ exist, err = util.IsExist(repo_model.RepoPath(user2.Name, "test"))
+ assert.NoError(t, err)
+ assert.False(t, exist)
+}
diff --git a/services/repository/generate.go b/services/repository/generate.go
index ef9a8dc940..867b5d7855 100644
--- a/services/repository/generate.go
+++ b/services/repository/generate.go
@@ -17,11 +17,11 @@ import (
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/gobwas/glob"
@@ -42,16 +42,14 @@ type expansion struct {
var defaultTransformers = []transformer{
{Name: "SNAKE", Transform: xstrings.ToSnakeCase},
{Name: "KEBAB", Transform: xstrings.ToKebabCase},
- {Name: "CAMEL", Transform: func(str string) string {
- return xstrings.FirstRuneToLower(xstrings.ToCamelCase(str))
- }},
- {Name: "PASCAL", Transform: xstrings.ToCamelCase},
+ {Name: "CAMEL", Transform: xstrings.ToCamelCase},
+ {Name: "PASCAL", Transform: xstrings.ToPascalCase},
{Name: "LOWER", Transform: strings.ToLower},
{Name: "UPPER", Transform: strings.ToUpper},
{Name: "TITLE", Transform: util.ToTitleCase},
}
-func generateExpansion(src string, templateRepo, generateRepo *repo_model.Repository, sanitizeFileName bool) string {
+func generateExpansion(ctx context.Context, src string, templateRepo, generateRepo *repo_model.Repository, sanitizeFileName bool) string {
year, month, day := time.Now().Date()
expansions := []expansion{
{Name: "YEAR", Value: strconv.Itoa(year), Transformers: nil},
@@ -66,10 +64,10 @@ func generateExpansion(src string, templateRepo, generateRepo *repo_model.Reposi
{Name: "TEMPLATE_OWNER", Value: templateRepo.OwnerName, Transformers: defaultTransformers},
{Name: "REPO_LINK", Value: generateRepo.Link(), Transformers: nil},
{Name: "TEMPLATE_LINK", Value: templateRepo.Link(), Transformers: nil},
- {Name: "REPO_HTTPS_URL", Value: generateRepo.CloneLink().HTTPS, Transformers: nil},
- {Name: "TEMPLATE_HTTPS_URL", Value: templateRepo.CloneLink().HTTPS, Transformers: nil},
- {Name: "REPO_SSH_URL", Value: generateRepo.CloneLink().SSH, Transformers: nil},
- {Name: "TEMPLATE_SSH_URL", Value: templateRepo.CloneLink().SSH, Transformers: nil},
+ {Name: "REPO_HTTPS_URL", Value: generateRepo.CloneLinkGeneral(ctx).HTTPS, Transformers: nil},
+ {Name: "TEMPLATE_HTTPS_URL", Value: templateRepo.CloneLinkGeneral(ctx).HTTPS, Transformers: nil},
+ {Name: "REPO_SSH_URL", Value: generateRepo.CloneLinkGeneral(ctx).SSH, Transformers: nil},
+ {Name: "TEMPLATE_SSH_URL", Value: templateRepo.CloneLinkGeneral(ctx).SSH, Transformers: nil},
}
expansionMap := make(map[string]string)
@@ -138,7 +136,7 @@ func readGiteaTemplateFile(tmpDir string) (*GiteaTemplate, error) {
return &GiteaTemplate{Path: gtPath, Content: content}, nil
}
-func processGiteaTemplateFile(tmpDir string, templateRepo, generateRepo *repo_model.Repository, giteaTemplateFile *GiteaTemplate) error {
+func processGiteaTemplateFile(ctx context.Context, tmpDir string, templateRepo, generateRepo *repo_model.Repository, giteaTemplateFile *GiteaTemplate) error {
if err := util.Remove(giteaTemplateFile.Path); err != nil {
return fmt.Errorf("remove .giteatemplate: %w", err)
}
@@ -163,12 +161,12 @@ func processGiteaTemplateFile(tmpDir string, templateRepo, generateRepo *repo_mo
return err
}
- generatedContent := []byte(generateExpansion(string(content), templateRepo, generateRepo, false))
+ generatedContent := []byte(generateExpansion(ctx, string(content), templateRepo, generateRepo, false))
if err := os.WriteFile(path, generatedContent, 0o644); err != nil {
return err
}
- substPath := filepath.FromSlash(filepath.Join(tmpDirSlash, generateExpansion(base, templateRepo, generateRepo, true)))
+ substPath := filepath.FromSlash(filepath.Join(tmpDirSlash, generateExpansion(ctx, base, templateRepo, generateRepo, true)))
// Create parent subdirectories if needed or continue silently if it exists
if err = os.MkdirAll(filepath.Dir(substPath), 0o755); err != nil {
@@ -226,7 +224,7 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r
}
if giteaTemplateFile != nil {
- err = processGiteaTemplateFile(tmpDir, templateRepo, generateRepo, giteaTemplateFile)
+ err = processGiteaTemplateFile(ctx, tmpDir, templateRepo, generateRepo, giteaTemplateFile)
if err != nil {
return err
}
@@ -236,8 +234,8 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r
return err
}
- if stdout, _, err := git.NewCommand(ctx, "remote", "add", "origin").AddDynamicArguments(repo.RepoPath()).
- RunStdString(&git.RunOpts{Dir: tmpDir, Env: env}); err != nil {
+ if stdout, _, err := git.NewCommand("remote", "add", "origin").AddDynamicArguments(repo.RepoPath()).
+ RunStdString(ctx, &git.RunOpts{Dir: tmpDir, Env: env}); err != nil {
log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err)
return fmt.Errorf("git remote add: %w", err)
}
@@ -255,48 +253,35 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r
return initRepoCommit(ctx, tmpDir, repo, repo.Owner, defaultBranch)
}
-func generateGitContent(ctx context.Context, repo, templateRepo, generateRepo *repo_model.Repository) (err error) {
- tmpDir, err := os.MkdirTemp(os.TempDir(), "gitea-"+repo.Name)
+// GenerateGitContent generates git content from a template repository
+func GenerateGitContent(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) (err error) {
+ tmpDir, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("gitea-" + generateRepo.Name)
if err != nil {
- return fmt.Errorf("Failed to create temp dir for repository %s: %w", repo.RepoPath(), err)
+ return fmt.Errorf("failed to create temp dir for repository %s: %w", generateRepo.FullName(), err)
}
+ defer cleanup()
- defer func() {
- if err := util.RemoveAll(tmpDir); err != nil {
- log.Error("RemoveAll: %v", err)
- }
- }()
-
- if err = generateRepoCommit(ctx, repo, templateRepo, generateRepo, tmpDir); err != nil {
+ if err = generateRepoCommit(ctx, generateRepo, templateRepo, generateRepo, tmpDir); err != nil {
return fmt.Errorf("generateRepoCommit: %w", err)
}
// re-fetch repo
- if repo, err = repo_model.GetRepositoryByID(ctx, repo.ID); err != nil {
+ if generateRepo, err = repo_model.GetRepositoryByID(ctx, generateRepo.ID); err != nil {
return fmt.Errorf("getRepositoryByID: %w", err)
}
// if there was no default branch supplied when generating the repo, use the default one from the template
- if strings.TrimSpace(repo.DefaultBranch) == "" {
- repo.DefaultBranch = templateRepo.DefaultBranch
+ if strings.TrimSpace(generateRepo.DefaultBranch) == "" {
+ generateRepo.DefaultBranch = templateRepo.DefaultBranch
}
- if err = gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil {
+ if err = gitrepo.SetDefaultBranch(ctx, generateRepo, generateRepo.DefaultBranch); err != nil {
return fmt.Errorf("setDefaultBranch: %w", err)
}
- if err = UpdateRepository(ctx, repo, false); err != nil {
+ if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, generateRepo, "default_branch"); err != nil {
return fmt.Errorf("updateRepository: %w", err)
}
- return nil
-}
-
-// GenerateGitContent generates git content from a template repository
-func GenerateGitContent(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
- if err := generateGitContent(ctx, generateRepo, templateRepo, generateRepo); err != nil {
- return err
- }
-
if err := repo_module.UpdateRepoSize(ctx, generateRepo); err != nil {
return fmt.Errorf("failed to update size for repository: %w", err)
}
@@ -328,58 +313,6 @@ func (gro GenerateRepoOptions) IsValid() bool {
gro.IssueLabels || gro.ProtectedBranch // or other items as they are added
}
-// generateRepository generates a repository from a template
-func generateRepository(ctx context.Context, doer, owner *user_model.User, templateRepo *repo_model.Repository, opts GenerateRepoOptions) (_ *repo_model.Repository, err error) {
- generateRepo := &repo_model.Repository{
- OwnerID: owner.ID,
- Owner: owner,
- OwnerName: owner.Name,
- Name: opts.Name,
- LowerName: strings.ToLower(opts.Name),
- Description: opts.Description,
- DefaultBranch: opts.DefaultBranch,
- IsPrivate: opts.Private,
- IsEmpty: !opts.GitContent || templateRepo.IsEmpty,
- IsFsckEnabled: templateRepo.IsFsckEnabled,
- TemplateID: templateRepo.ID,
- TrustModel: templateRepo.TrustModel,
- ObjectFormatName: templateRepo.ObjectFormatName,
- }
-
- if err = CreateRepositoryByExample(ctx, doer, owner, generateRepo, false, false); err != nil {
- return nil, err
- }
-
- repoPath := generateRepo.RepoPath()
- isExist, err := util.IsExist(repoPath)
- if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
- return nil, err
- }
- if isExist {
- return nil, repo_model.ErrRepoFilesAlreadyExist{
- Uname: generateRepo.OwnerName,
- Name: generateRepo.Name,
- }
- }
-
- if err = repo_module.CheckInitRepository(ctx, owner.Name, generateRepo.Name, generateRepo.ObjectFormatName); err != nil {
- return generateRepo, err
- }
-
- if err = repo_module.CheckDaemonExportOK(ctx, generateRepo); err != nil {
- return generateRepo, fmt.Errorf("checkDaemonExportOK: %w", err)
- }
-
- if stdout, _, err := git.NewCommand(ctx, "update-server-info").
- RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
- log.Error("GenerateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", generateRepo, stdout, err)
- return generateRepo, fmt.Errorf("error in GenerateRepository(git update-server-info): %w", err)
- }
-
- return generateRepo, nil
-}
-
var fileNameSanitizeRegexp = regexp.MustCompile(`(?i)\.\.|[<>:\"/\\|?*\x{0000}-\x{001F}]|^(con|prn|aux|nul|com\d|lpt\d)$`)
// Sanitize user input to valid OS filenames
diff --git a/services/repository/generate_test.go b/services/repository/generate_test.go
index b0f97d0ffb..1163c392c9 100644
--- a/services/repository/generate_test.go
+++ b/services/repository/generate_test.go
@@ -7,6 +7,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
var giteaTemplate = []byte(`
@@ -65,3 +66,26 @@ func TestFileNameSanitize(t *testing.T) {
assert.Equal(t, "_", fileNameSanitize("\u0000"))
assert.Equal(t, "目标", fileNameSanitize("目标"))
}
+
+func TestTransformers(t *testing.T) {
+ cases := []struct {
+ name string
+ expected string
+ }{
+ {"SNAKE", "abc_def_xyz"},
+ {"KEBAB", "abc-def-xyz"},
+ {"CAMEL", "abcDefXyz"},
+ {"PASCAL", "AbcDefXyz"},
+ {"LOWER", "abc_def-xyz"},
+ {"UPPER", "ABC_DEF-XYZ"},
+ {"TITLE", "Abc_def-Xyz"},
+ }
+
+ input := "Abc_Def-XYZ"
+ assert.Len(t, defaultTransformers, len(cases))
+ for i, c := range cases {
+ tf := defaultTransformers[i]
+ require.Equal(t, c.name, tf.Name)
+ assert.Equal(t, c.expected, tf.Transform(input), "case %s", c.name)
+ }
+}
diff --git a/modules/gitgraph/graph.go b/services/repository/gitgraph/graph.go
index 7e12be030f..d06d18c1b4 100644
--- a/modules/gitgraph/graph.go
+++ b/services/repository/gitgraph/graph.go
@@ -22,14 +22,14 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
page = 1
}
- graphCmd := git.NewCommand(r.Ctx, "log", "--graph", "--date-order", "--decorate=full")
+ graphCmd := git.NewCommand("log", "--graph", "--date-order", "--decorate=full")
if hidePRRefs {
graphCmd.AddArguments("--exclude=" + git.PullPrefix + "*")
}
if len(branches) == 0 {
- graphCmd.AddArguments("--all")
+ graphCmd.AddArguments("--tags", "--branches")
}
graphCmd.AddArguments("-C", "-M", "--date=iso-strict").
@@ -53,7 +53,7 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
scanner := bufio.NewScanner(stdoutReader)
- if err := graphCmd.Run(&git.RunOpts{
+ if err := graphCmd.Run(r.Ctx, &git.RunOpts{
Dir: r.Path,
Stdout: stdoutWriter,
Stderr: stderr,
diff --git a/modules/gitgraph/graph_models.go b/services/repository/gitgraph/graph_models.go
index 191b0b3afc..02b0268cd9 100644
--- a/modules/gitgraph/graph_models.go
+++ b/services/repository/gitgraph/graph_models.go
@@ -17,6 +17,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
+ asymkey_service "code.gitea.io/gitea/services/asymkey"
)
// NewGraph creates a basic graph
@@ -114,13 +115,13 @@ func (graph *Graph) LoadAndProcessCommits(ctx context.Context, repository *repo_
}
}
- c.Verification = asymkey_model.ParseCommitWithSignature(ctx, c.Commit)
+ c.Verification = asymkey_service.ParseCommitWithSignature(ctx, c.Commit)
_ = asymkey_model.CalculateTrustStatus(c.Verification, repository.GetTrustModel(), func(user *user_model.User) (bool, error) {
return repo_model.IsOwnerMemberCollaborator(ctx, repository, user.ID)
}, &keyMap)
- statuses, _, err := git_model.GetLatestCommitStatus(ctx, repository.ID, c.Commit.ID.String(), db.ListOptions{})
+ statuses, err := git_model.GetLatestCommitStatus(ctx, repository.ID, c.Commit.ID.String(), db.ListOptionsAll)
if err != nil {
log.Error("GetLatestCommitStatus: %v", err)
} else {
@@ -231,8 +232,8 @@ func newRefsFromRefNames(refNames []byte) []git.Reference {
continue
}
refName := string(refNameBytes)
- if strings.HasPrefix(refName, "tag: ") {
- refName = strings.TrimPrefix(refName, "tag: ")
+ if after, ok := strings.CutPrefix(refName, "tag: "); ok {
+ refName = after
} else {
refName = strings.TrimPrefix(refName, "HEAD -> ")
}
diff --git a/modules/gitgraph/graph_test.go b/services/repository/gitgraph/graph_test.go
index 18d427acd9..93fa1aec6a 100644
--- a/modules/gitgraph/graph_test.go
+++ b/services/repository/gitgraph/graph_test.go
@@ -6,10 +6,13 @@ package gitgraph
import (
"bytes"
"fmt"
+ "slices"
"strings"
"testing"
"code.gitea.io/gitea/modules/git"
+
+ "github.com/stretchr/testify/assert"
)
func BenchmarkGetCommitGraph(b *testing.B) {
@@ -19,7 +22,7 @@ func BenchmarkGetCommitGraph(b *testing.B) {
}
defer currentRepo.Close()
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
graph, err := GetCommitGraph(currentRepo, 1, 0, false, nil, nil)
if err != nil {
b.Error("Could get commit graph")
@@ -36,7 +39,7 @@ func BenchmarkParseCommitString(b *testing.B) {
parser := &Parser{}
parser.Reset()
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
parser.Reset()
graph := NewGraph()
if err := parser.AddLineToGraph(graph, 0, []byte(testString)); err != nil {
@@ -53,7 +56,7 @@ func BenchmarkParseGlyphs(b *testing.B) {
parser.Reset()
tgBytes := []byte(testglyphs)
var tg []byte
- for i := 0; i < b.N; i++ {
+ for b.Loop() {
parser.Reset()
tg = tgBytes
idx := bytes.Index(tg, []byte("\n"))
@@ -115,13 +118,7 @@ func TestReleaseUnusedColors(t *testing.T) {
if parser.firstAvailable == -1 {
// All in use
for _, color := range parser.availableColors {
- found := false
- for _, oldColor := range parser.oldColors {
- if oldColor == color {
- found = true
- break
- }
- }
+ found := slices.Contains(parser.oldColors, color)
if !found {
t.Errorf("In testcase:\n%d\t%d\t%d %d =>\n%d\t%d\t%d %d: %d should be available but is not",
testcase.availableColors,
@@ -139,13 +136,7 @@ func TestReleaseUnusedColors(t *testing.T) {
// Some in use
for i := parser.firstInUse; i != parser.firstAvailable; i = (i + 1) % len(parser.availableColors) {
color := parser.availableColors[i]
- found := false
- for _, oldColor := range parser.oldColors {
- if oldColor == color {
- found = true
- break
- }
- }
+ found := slices.Contains(parser.oldColors, color)
if !found {
t.Errorf("In testcase:\n%d\t%d\t%d %d =>\n%d\t%d\t%d %d: %d should be available but is not",
testcase.availableColors,
@@ -161,13 +152,7 @@ func TestReleaseUnusedColors(t *testing.T) {
}
for i := parser.firstAvailable; i != parser.firstInUse; i = (i + 1) % len(parser.availableColors) {
color := parser.availableColors[i]
- found := false
- for _, oldColor := range parser.oldColors {
- if oldColor == color {
- found = true
- break
- }
- }
+ found := slices.Contains(parser.oldColors, color)
if found {
t.Errorf("In testcase:\n%d\t%d\t%d %d =>\n%d\t%d\t%d %d: %d should not be available but is",
testcase.availableColors,
@@ -235,9 +220,7 @@ func TestParseGlyphs(t *testing.T) {
}
row++
}
- if len(parser.availableColors) != 9 {
- t.Errorf("Expected 9 colors but have %d", len(parser.availableColors))
- }
+ assert.Len(t, parser.availableColors, 9)
}
func TestCommitStringParsing(t *testing.T) {
@@ -262,453 +245,451 @@ func TestCommitStringParsing(t *testing.T) {
return
}
- if test.commitMessage != commit.Subject {
- t.Errorf("%s does not match %s", test.commitMessage, commit.Subject)
- }
+ assert.Equal(t, test.commitMessage, commit.Subject)
})
}
}
-var testglyphs = `*
-*
-*
-*
-*
-*
-*
-*
-|\
-* |
-* |
-* |
-* |
-* |
-| *
-* |
-| *
-| |\
-* | |
-| | *
-| | |\
-* | | \
-|\ \ \ \
-| * | | |
-| |\| | |
-* | | | |
-|/ / / /
-| | | *
-| * | |
-| * | |
-| * | |
-* | | |
-* | | |
-* | | |
-* | | |
-* | | |
-|\ \ \ \
-| | * | |
-| | |\| |
-| | | * |
-| | | | *
-* | | | |
-* | | | |
-* | | | |
-* | | | |
-* | | | |
-|\ \ \ \ \
-| * | | | |
-|/| | | | |
-| | |/ / /
-| |/| | |
-| | | | *
-| * | | |
-|/| | | |
-| * | | |
-|/| | | |
-| | |/ /
-| |/| |
-| * | |
-| * | |
-| |\ \ \
-| | * | |
-| |/| | |
-| | | |/
-| | |/|
-| * | |
-| * | |
-| * | |
-| | * |
-| | |\ \
-| | | * |
-| | |/| |
-| | | * |
-| | | |\ \
-| | | | * |
-| | | |/| |
-| | * | | |
-| | * | | |
-| | |\ \ \ \
-| | | * | | |
-| | |/| | | |
-| | | | | * |
-| | | | |/ /
-* | | | / /
-|/ / / / /
-* | | | |
-|\ \ \ \ \
-| * | | | |
-|/| | | | |
-| * | | | |
-| * | | | |
-| |\ \ \ \ \
-| | | * \ \ \
-| | | |\ \ \ \
-| | | | * | | |
-| | | |/| | | |
-| | | | | |/ /
-| | | | |/| |
-* | | | | | |
-* | | | | | |
-* | | | | | |
-| | | | * | |
-* | | | | | |
-| | * | | | |
-| |/| | | | |
-* | | | | | |
-| |/ / / / /
-|/| | | | |
-| | | | * |
-| | | |/ /
-| | |/| |
-| * | | |
-| | | | *
-| | * | |
-| | |\ \ \
-| | | * | |
-| | |/| | |
-| | | |/ /
-| | | * |
-| | * | |
-| | |\ \ \
-| | | * | |
-| | |/| | |
-| | | |/ /
-| | | * |
-* | | | |
-|\ \ \ \ \
-| * \ \ \ \
-| |\ \ \ \ \
-| | | |/ / /
-| | |/| | |
-| | | | * |
-| | | | * |
-* | | | | |
-* | | | | |
-|/ / / / /
-| | | * |
-* | | | |
-* | | | |
-* | | | |
-* | | | |
-|\ \ \ \ \
-| * | | | |
-|/| | | | |
-| | * | | |
-| | |\ \ \ \
-| | | * | | |
-| | |/| | | |
-| |/| | |/ /
-| | | |/| |
-| | | | | *
-| |_|_|_|/
-|/| | | |
-| | * | |
-| |/ / /
-* | | |
-* | | |
-| | * |
-* | | |
-* | | |
-| * | |
-| | * |
-| * | |
-* | | |
-|\ \ \ \
-| * | | |
-|/| | | |
-| |/ / /
-| * | |
-| |\ \ \
-| | * | |
-| |/| | |
-| | |/ /
-| | * |
-| | |\ \
-| | | * |
-| | |/| |
-* | | | |
-* | | | |
-|\ \ \ \ \
-| * | | | |
-|/| | | | |
-| | * | | |
-| | * | | |
-| | * | | |
-| |/ / / /
-| * | | |
-| |\ \ \ \
-| | * | | |
-| |/| | | |
-* | | | | |
-* | | | | |
-* | | | | |
-* | | | | |
-* | | | | |
-| | | | * |
-* | | | | |
-|\ \ \ \ \ \
-| * | | | | |
-|/| | | | | |
-| | | | | * |
-| | | | |/ /
-* | | | | |
-|\ \ \ \ \ \
-* | | | | | |
-* | | | | | |
-| | | | * | |
-* | | | | | |
-* | | | | | |
-|\ \ \ \ \ \ \
-| | |_|_|/ / /
-| |/| | | | |
-| | | | * | |
-| | | | * | |
-| | | | * | |
-| | | | * | |
-| | | | * | |
-| | | | * | |
-| | | |/ / /
-| | | * | |
-| | | * | |
-| | | * | |
-| | |/| | |
-| | | * | |
-| | |/| | |
-| | | |/ /
-| | * | |
-| |/| | |
-| | | * |
-| | |/ /
-| | * |
-| * | |
-| |\ \ \
-| * | | |
-| | * | |
-| |/| | |
-| | |/ /
-| | * |
-| | |\ \
-| | * | |
-* | | | |
-|\| | | |
-| * | | |
-| * | | |
-| * | | |
-| | * | |
-| * | | |
-| |\| | |
-| * | | |
-| | * | |
-| | * | |
-| * | | |
-| * | | |
-| * | | |
-| * | | |
-| * | | |
-| * | | |
-| * | | |
-| * | | |
-| | * | |
-| * | | |
-| * | | |
-| * | | |
-| * | | |
-| | * | |
-* | | | |
-|\| | | |
-| | * | |
-| * | | |
-| |\| | |
-| | * | |
-| | * | |
-| | * | |
-| | | * |
-* | | | |
-|\| | | |
-| | * | |
-| | |/ /
-| * | |
-| * | |
-| |\| |
-* | | |
-|\| | |
-| | * |
-| | * |
-| | * |
-| * | |
-| | * |
-| * | |
-| | * |
-| | * |
-| | * |
-| * | |
-| * | |
-| * | |
-| * | |
-| * | |
-| * | |
-| * | |
-* | | |
-|\| | |
-| * | |
-| |\| |
-| | * |
-| | |\ \
-* | | | |
-|\| | | |
-| * | | |
-| |\| | |
-| | * | |
-| | | * |
-| | |/ /
-* | | |
-* | | |
-|\| | |
-| * | |
-| |\| |
-| | * |
-| | * |
-| | * |
-| | | *
-* | | |
-|\| | |
-| * | |
-| * | |
-| | | *
-| | | |\
-* | | | |
-| |_|_|/
-|/| | |
-| * | |
-| |\| |
-| | * |
-| | * |
-| | * |
-| | * |
-| | * |
-| * | |
-* | | |
-|\| | |
-| * | |
-|/| | |
-| |/ /
-| * |
-| |\ \
-| * | |
-| * | |
-* | | |
-|\| | |
-| | * |
-| * | |
-| * | |
-| * | |
-* | | |
-|\| | |
-| * | |
-| * | |
-| | * |
-| | |\ \
-| | |/ /
-| |/| |
-| * | |
-* | | |
-|\| | |
-| * | |
-* | | |
-|\| | |
-| * | |
-| |\ \ \
-| * | | |
-| * | | |
-| | | * |
-| * | | |
-| * | | |
-| | |/ /
-| |/| |
-| | * |
-* | | |
-|\| | |
-| * | |
-| * | |
-| * | |
-| * | |
-| * | |
-| |\ \ \
-* | | | |
-|\| | | |
-| * | | |
-| * | | |
-* | | | |
-* | | | |
-|\| | | |
-| | | | *
-| | | | |\
-| |_|_|_|/
-|/| | | |
-| * | | |
-* | | | |
-* | | | |
-|\| | | |
-| * | | |
-| |\ \ \ \
-| | | |/ /
-| | |/| |
-| * | | |
-| * | | |
-| * | | |
-| * | | |
-| | * | |
-| | | * |
-| | |/ /
-| |/| |
-* | | |
-|\| | |
-| * | |
-| * | |
-| * | |
-| * | |
-| * | |
-* | | |
-|\| | |
-| * | |
-| * | |
-* | | |
-| * | |
-| * | |
-| * | |
-* | | |
-* | | |
-* | | |
-|\| | |
-| * | |
-* | | |
-* | | |
-* | | |
-* | | |
-| | | *
-* | | |
-|\| | |
-| * | |
-| * | |
-| * | |
+var testglyphs = `*
+*
+*
+*
+*
+*
+*
+*
+|\
+* |
+* |
+* |
+* |
+* |
+| *
+* |
+| *
+| |\
+* | |
+| | *
+| | |\
+* | | \
+|\ \ \ \
+| * | | |
+| |\| | |
+* | | | |
+|/ / / /
+| | | *
+| * | |
+| * | |
+| * | |
+* | | |
+* | | |
+* | | |
+* | | |
+* | | |
+|\ \ \ \
+| | * | |
+| | |\| |
+| | | * |
+| | | | *
+* | | | |
+* | | | |
+* | | | |
+* | | | |
+* | | | |
+|\ \ \ \ \
+| * | | | |
+|/| | | | |
+| | |/ / /
+| |/| | |
+| | | | *
+| * | | |
+|/| | | |
+| * | | |
+|/| | | |
+| | |/ /
+| |/| |
+| * | |
+| * | |
+| |\ \ \
+| | * | |
+| |/| | |
+| | | |/
+| | |/|
+| * | |
+| * | |
+| * | |
+| | * |
+| | |\ \
+| | | * |
+| | |/| |
+| | | * |
+| | | |\ \
+| | | | * |
+| | | |/| |
+| | * | | |
+| | * | | |
+| | |\ \ \ \
+| | | * | | |
+| | |/| | | |
+| | | | | * |
+| | | | |/ /
+* | | | / /
+|/ / / / /
+* | | | |
+|\ \ \ \ \
+| * | | | |
+|/| | | | |
+| * | | | |
+| * | | | |
+| |\ \ \ \ \
+| | | * \ \ \
+| | | |\ \ \ \
+| | | | * | | |
+| | | |/| | | |
+| | | | | |/ /
+| | | | |/| |
+* | | | | | |
+* | | | | | |
+* | | | | | |
+| | | | * | |
+* | | | | | |
+| | * | | | |
+| |/| | | | |
+* | | | | | |
+| |/ / / / /
+|/| | | | |
+| | | | * |
+| | | |/ /
+| | |/| |
+| * | | |
+| | | | *
+| | * | |
+| | |\ \ \
+| | | * | |
+| | |/| | |
+| | | |/ /
+| | | * |
+| | * | |
+| | |\ \ \
+| | | * | |
+| | |/| | |
+| | | |/ /
+| | | * |
+* | | | |
+|\ \ \ \ \
+| * \ \ \ \
+| |\ \ \ \ \
+| | | |/ / /
+| | |/| | |
+| | | | * |
+| | | | * |
+* | | | | |
+* | | | | |
+|/ / / / /
+| | | * |
+* | | | |
+* | | | |
+* | | | |
+* | | | |
+|\ \ \ \ \
+| * | | | |
+|/| | | | |
+| | * | | |
+| | |\ \ \ \
+| | | * | | |
+| | |/| | | |
+| |/| | |/ /
+| | | |/| |
+| | | | | *
+| |_|_|_|/
+|/| | | |
+| | * | |
+| |/ / /
+* | | |
+* | | |
+| | * |
+* | | |
+* | | |
+| * | |
+| | * |
+| * | |
+* | | |
+|\ \ \ \
+| * | | |
+|/| | | |
+| |/ / /
+| * | |
+| |\ \ \
+| | * | |
+| |/| | |
+| | |/ /
+| | * |
+| | |\ \
+| | | * |
+| | |/| |
+* | | | |
+* | | | |
+|\ \ \ \ \
+| * | | | |
+|/| | | | |
+| | * | | |
+| | * | | |
+| | * | | |
+| |/ / / /
+| * | | |
+| |\ \ \ \
+| | * | | |
+| |/| | | |
+* | | | | |
+* | | | | |
+* | | | | |
+* | | | | |
+* | | | | |
+| | | | * |
+* | | | | |
+|\ \ \ \ \ \
+| * | | | | |
+|/| | | | | |
+| | | | | * |
+| | | | |/ /
+* | | | | |
+|\ \ \ \ \ \
+* | | | | | |
+* | | | | | |
+| | | | * | |
+* | | | | | |
+* | | | | | |
+|\ \ \ \ \ \ \
+| | |_|_|/ / /
+| |/| | | | |
+| | | | * | |
+| | | | * | |
+| | | | * | |
+| | | | * | |
+| | | | * | |
+| | | | * | |
+| | | |/ / /
+| | | * | |
+| | | * | |
+| | | * | |
+| | |/| | |
+| | | * | |
+| | |/| | |
+| | | |/ /
+| | * | |
+| |/| | |
+| | | * |
+| | |/ /
+| | * |
+| * | |
+| |\ \ \
+| * | | |
+| | * | |
+| |/| | |
+| | |/ /
+| | * |
+| | |\ \
+| | * | |
+* | | | |
+|\| | | |
+| * | | |
+| * | | |
+| * | | |
+| | * | |
+| * | | |
+| |\| | |
+| * | | |
+| | * | |
+| | * | |
+| * | | |
+| * | | |
+| * | | |
+| * | | |
+| * | | |
+| * | | |
+| * | | |
+| * | | |
+| | * | |
+| * | | |
+| * | | |
+| * | | |
+| * | | |
+| | * | |
+* | | | |
+|\| | | |
+| | * | |
+| * | | |
+| |\| | |
+| | * | |
+| | * | |
+| | * | |
+| | | * |
+* | | | |
+|\| | | |
+| | * | |
+| | |/ /
+| * | |
+| * | |
+| |\| |
+* | | |
+|\| | |
+| | * |
+| | * |
+| | * |
+| * | |
+| | * |
+| * | |
+| | * |
+| | * |
+| | * |
+| * | |
+| * | |
+| * | |
+| * | |
+| * | |
+| * | |
+| * | |
+* | | |
+|\| | |
+| * | |
+| |\| |
+| | * |
+| | |\ \
+* | | | |
+|\| | | |
+| * | | |
+| |\| | |
+| | * | |
+| | | * |
+| | |/ /
+* | | |
+* | | |
+|\| | |
+| * | |
+| |\| |
+| | * |
+| | * |
+| | * |
+| | | *
+* | | |
+|\| | |
+| * | |
+| * | |
+| | | *
+| | | |\
+* | | | |
+| |_|_|/
+|/| | |
+| * | |
+| |\| |
+| | * |
+| | * |
+| | * |
+| | * |
+| | * |
+| * | |
+* | | |
+|\| | |
+| * | |
+|/| | |
+| |/ /
+| * |
+| |\ \
+| * | |
+| * | |
+* | | |
+|\| | |
+| | * |
+| * | |
+| * | |
+| * | |
+* | | |
+|\| | |
+| * | |
+| * | |
+| | * |
+| | |\ \
+| | |/ /
+| |/| |
+| * | |
+* | | |
+|\| | |
+| * | |
+* | | |
+|\| | |
+| * | |
+| |\ \ \
+| * | | |
+| * | | |
+| | | * |
+| * | | |
+| * | | |
+| | |/ /
+| |/| |
+| | * |
+* | | |
+|\| | |
+| * | |
+| * | |
+| * | |
+| * | |
+| * | |
+| |\ \ \
+* | | | |
+|\| | | |
+| * | | |
+| * | | |
+* | | | |
+* | | | |
+|\| | | |
+| | | | *
+| | | | |\
+| |_|_|_|/
+|/| | | |
+| * | | |
+* | | | |
+* | | | |
+|\| | | |
+| * | | |
+| |\ \ \ \
+| | | |/ /
+| | |/| |
+| * | | |
+| * | | |
+| * | | |
+| * | | |
+| | * | |
+| | | * |
+| | |/ /
+| |/| |
+* | | |
+|\| | |
+| * | |
+| * | |
+| * | |
+| * | |
+| * | |
+* | | |
+|\| | |
+| * | |
+| * | |
+* | | |
+| * | |
+| * | |
+| * | |
+* | | |
+* | | |
+* | | |
+|\| | |
+| * | |
+* | | |
+* | | |
+* | | |
+* | | |
+| | | *
+* | | |
+|\| | |
+| * | |
+| * | |
+| * | |
`
diff --git a/modules/gitgraph/parser.go b/services/repository/gitgraph/parser.go
index f6bf9b0b90..f6bf9b0b90 100644
--- a/modules/gitgraph/parser.go
+++ b/services/repository/gitgraph/parser.go
diff --git a/services/repository/hooks.go b/services/repository/hooks.go
index 97e9e290a3..c13b272550 100644
--- a/services/repository/hooks.go
+++ b/services/repository/hooks.go
@@ -12,7 +12,6 @@ import (
"code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
"xorm.io/builder"
)
@@ -32,11 +31,11 @@ func SyncRepositoryHooks(ctx context.Context) error {
default:
}
- if err := repo_module.CreateDelegateHooks(repo.RepoPath()); err != nil {
+ if err := gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
return fmt.Errorf("SyncRepositoryHook: %w", err)
}
if repo.HasWiki() {
- if err := repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil {
+ if err := gitrepo.CreateDelegateHooks(ctx, repo.WikiStorageRepo()); err != nil {
return fmt.Errorf("SyncRepositoryHook: %w", err)
}
}
diff --git a/services/repository/init.go b/services/repository/init.go
index c719e11786..1eeeb4aa4f 100644
--- a/services/repository/init.go
+++ b/services/repository/init.go
@@ -33,18 +33,21 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
committerName := sig.Name
committerEmail := sig.Email
- if stdout, _, err := git.NewCommand(ctx, "add", "--all").
- RunStdString(&git.RunOpts{Dir: tmpPath}); err != nil {
+ if stdout, _, err := git.NewCommand("add", "--all").
+ RunStdString(ctx, &git.RunOpts{Dir: tmpPath}); err != nil {
log.Error("git add --all failed: Stdout: %s\nError: %v", stdout, err)
return fmt.Errorf("git add --all: %w", err)
}
- cmd := git.NewCommand(ctx, "commit", "--message=Initial commit").
+ cmd := git.NewCommand("commit", "--message=Initial commit").
AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email)
- sign, keyID, signer, _ := asymkey_service.SignInitialCommit(ctx, tmpPath, u)
+ sign, key, signer, _ := asymkey_service.SignInitialCommit(ctx, tmpPath, u)
if sign {
- cmd.AddOptionFormat("-S%s", keyID)
+ if key.Format != "" {
+ cmd.AddConfig("gpg.format", key.Format)
+ }
+ cmd.AddOptionFormat("-S%s", key.KeyID)
if repo.GetTrustModel() == repo_model.CommitterTrustModel || repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
// need to set the committer to the KeyID owner
@@ -61,7 +64,7 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
)
if stdout, _, err := cmd.
- RunStdString(&git.RunOpts{Dir: tmpPath, Env: env}); err != nil {
+ RunStdString(ctx, &git.RunOpts{Dir: tmpPath, Env: env}); err != nil {
log.Error("Failed to commit: %v: Stdout: %s\nError: %v", cmd.LogString(), stdout, err)
return fmt.Errorf("git commit: %w", err)
}
@@ -70,8 +73,8 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
defaultBranch = setting.Repository.DefaultBranch
}
- if stdout, _, err := git.NewCommand(ctx, "push", "origin").AddDynamicArguments("HEAD:" + defaultBranch).
- RunStdString(&git.RunOpts{Dir: tmpPath, Env: repo_module.InternalPushingEnvironment(u, repo)}); err != nil {
+ if stdout, _, err := git.NewCommand("push", "origin").AddDynamicArguments("HEAD:"+defaultBranch).
+ RunStdString(ctx, &git.RunOpts{Dir: tmpPath, Env: repo_module.InternalPushingEnvironment(u, repo)}); err != nil {
log.Error("Failed to push back to HEAD: Stdout: %s\nError: %v", stdout, err)
return fmt.Errorf("git push: %w", err)
}
diff --git a/services/repository/lfs_test.go b/services/repository/lfs_test.go
index ee0b8f6b89..78ff8c853e 100644
--- a/services/repository/lfs_test.go
+++ b/services/repository/lfs_test.go
@@ -5,7 +5,6 @@ package repository_test
import (
"bytes"
- "context"
"testing"
"time"
@@ -36,7 +35,7 @@ func TestGarbageCollectLFSMetaObjects(t *testing.T) {
lfsOid := storeObjectInRepo(t, repo.ID, &lfsContent)
// gc
- err = repo_service.GarbageCollectLFSMetaObjects(context.Background(), repo_service.GarbageCollectLFSMetaObjectsOptions{
+ err = repo_service.GarbageCollectLFSMetaObjects(t.Context(), repo_service.GarbageCollectLFSMetaObjectsOptions{
AutoFix: true,
OlderThan: time.Now().Add(7 * 24 * time.Hour).Add(5 * 24 * time.Hour),
UpdatedLessRecentlyThan: time.Now().Add(7 * 24 * time.Hour).Add(3 * 24 * time.Hour),
diff --git a/services/repository/license.go b/services/repository/license.go
index 2453be3c87..8622911fa2 100644
--- a/services/repository/license.go
+++ b/services/repository/license.go
@@ -14,7 +14,6 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options"
"code.gitea.io/gitea/modules/queue"
@@ -25,7 +24,6 @@ import (
var (
classifier *licenseclassifier.Classifier
LicenseFileName = "LICENSE"
- licenseAliases map[string]string
// licenseUpdaterQueue represents a queue to handle update repo licenses
licenseUpdaterQueue *queue.WorkerPoolQueue[*LicenseUpdaterOptions]
@@ -38,34 +36,6 @@ func AddRepoToLicenseUpdaterQueue(opts *LicenseUpdaterOptions) error {
return licenseUpdaterQueue.Push(opts)
}
-func loadLicenseAliases() error {
- if licenseAliases != nil {
- return nil
- }
-
- data, err := options.AssetFS().ReadFile("license", "etc", "license-aliases.json")
- if err != nil {
- return err
- }
- err = json.Unmarshal(data, &licenseAliases)
- if err != nil {
- return err
- }
- return nil
-}
-
-func ConvertLicenseName(name string) string {
- if err := loadLicenseAliases(); err != nil {
- return name
- }
-
- v, ok := licenseAliases[name]
- if ok {
- return v
- }
- return name
-}
-
func InitLicenseClassifier() error {
// threshold should be 0.84~0.86 or the test will be failed
classifier = licenseclassifier.NewClassifier(.85)
@@ -74,20 +44,13 @@ func InitLicenseClassifier() error {
return err
}
- existLicense := make(container.Set[string])
- if len(licenseFiles) > 0 {
- for _, licenseFile := range licenseFiles {
- licenseName := ConvertLicenseName(licenseFile)
- if existLicense.Contains(licenseName) {
- continue
- }
- existLicense.Add(licenseName)
- data, err := options.License(licenseFile)
- if err != nil {
- return err
- }
- classifier.AddContent("License", licenseFile, licenseName, data)
+ for _, licenseFile := range licenseFiles {
+ licenseName := licenseFile
+ data, err := options.License(licenseFile)
+ if err != nil {
+ return err
}
+ classifier.AddContent("License", licenseName, licenseName, data)
}
return nil
}
diff --git a/services/repository/license_test.go b/services/repository/license_test.go
index 9d3e0f36e3..eb897f3c03 100644
--- a/services/repository/license_test.go
+++ b/services/repository/license_test.go
@@ -4,13 +4,13 @@
package repository
import (
- "fmt"
"strings"
"testing"
repo_module "code.gitea.io/gitea/modules/repository"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func Test_detectLicense(t *testing.T) {
@@ -33,9 +33,7 @@ func Test_detectLicense(t *testing.T) {
},
}
- repo_module.LoadRepoConfig()
- err := loadLicenseAliases()
- assert.NoError(t, err)
+ require.NoError(t, repo_module.LoadRepoConfig())
for _, licenseName := range repo_module.Licenses {
license, err := repo_module.GetLicense(licenseName, &repo_module.LicenseValues{
Owner: "Gitea",
@@ -46,14 +44,13 @@ func Test_detectLicense(t *testing.T) {
assert.NoError(t, err)
tests = append(tests, DetectLicenseTest{
- name: fmt.Sprintf("single license test: %s", licenseName),
+ name: "single license test: " + licenseName,
arg: string(license),
- want: []string{ConvertLicenseName(licenseName)},
+ want: []string{licenseName},
})
}
- err = InitLicenseClassifier()
- assert.NoError(t, err)
+ require.NoError(t, InitLicenseClassifier())
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
license, err := detectLicense(strings.NewReader(tt.arg))
diff --git a/services/repository/merge_upstream.go b/services/repository/merge_upstream.go
index 85ca8f7e31..8d6f11372c 100644
--- a/services/repository/merge_upstream.go
+++ b/services/repository/merge_upstream.go
@@ -4,35 +4,38 @@
package repository
import (
- "context"
+ "errors"
"fmt"
- git_model "code.gitea.io/gitea/models/git"
issue_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
repo_module "code.gitea.io/gitea/modules/repository"
+ "code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/pull"
)
-type UpstreamDivergingInfo struct {
- BaseIsNewer bool
- CommitsBehind int
- CommitsAhead int
-}
-
-func MergeUpstream(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, branch string) (mergeStyle string, err error) {
+// MergeUpstream merges the base repository's default branch into the fork repository's current branch.
+func MergeUpstream(ctx reqctx.RequestContext, doer *user_model.User, repo *repo_model.Repository, branch string, ffOnly bool) (mergeStyle string, err error) {
if err = repo.MustNotBeArchived(); err != nil {
return "", err
}
if err = repo.GetBaseRepo(ctx); err != nil {
return "", err
}
+ divergingInfo, err := GetUpstreamDivergingInfo(ctx, repo, branch)
+ if err != nil {
+ return "", err
+ }
+ if !divergingInfo.BaseBranchHasNewCommits {
+ return "up-to-date", nil
+ }
+
err = git.Push(ctx, repo.BaseRepo.RepoPath(), git.PushOptions{
Remote: repo.RepoPath(),
- Branch: fmt.Sprintf("%s:%s", branch, branch),
+ Branch: fmt.Sprintf("%s:%s", divergingInfo.BaseBranchName, branch),
Env: repo_module.PushingEnvironment(doer, repo),
})
if err == nil {
@@ -42,6 +45,11 @@ func MergeUpstream(ctx context.Context, doer *user_model.User, repo *repo_model.
return "", err
}
+ // If ff_only is requested and fast-forward failed, return error
+ if ffOnly {
+ return "", util.NewInvalidArgumentErrorf("fast-forward merge not possible: branch has diverged")
+ }
+
// TODO: FakePR: it is somewhat hacky, but it is the only way to "merge" at the moment
// ideally in the future the "merge" functions should be refactored to decouple from the PullRequest
fakeIssue := &issue_model.Issue{
@@ -64,7 +72,7 @@ func MergeUpstream(ctx context.Context, doer *user_model.User, repo *repo_model.
BaseRepoID: repo.BaseRepo.ID,
BaseRepo: repo.BaseRepo,
HeadBranch: branch, // maybe HeadCommitID is not needed
- BaseBranch: branch,
+ BaseBranch: divergingInfo.BaseBranchName,
}
fakeIssue.PullRequest = fakePR
err = pull.Update(ctx, fakePR, doer, "merge upstream", false)
@@ -74,42 +82,47 @@ func MergeUpstream(ctx context.Context, doer *user_model.User, repo *repo_model.
return "merge", nil
}
-func GetUpstreamDivergingInfo(ctx context.Context, repo *repo_model.Repository, branch string) (*UpstreamDivergingInfo, error) {
- if !repo.IsFork {
+// UpstreamDivergingInfo is also used in templates, so it needs to search for all references before changing it.
+type UpstreamDivergingInfo struct {
+ BaseBranchName string
+ BaseBranchHasNewCommits bool
+ HeadBranchCommitsBehind int
+}
+
+// GetUpstreamDivergingInfo returns the information about the divergence between the fork repository's branch and the base repository's default branch.
+func GetUpstreamDivergingInfo(ctx reqctx.RequestContext, forkRepo *repo_model.Repository, forkBranch string) (*UpstreamDivergingInfo, error) {
+ if !forkRepo.IsFork {
return nil, util.NewInvalidArgumentErrorf("repo is not a fork")
}
- if repo.IsArchived {
+ if forkRepo.IsArchived {
return nil, util.NewInvalidArgumentErrorf("repo is archived")
}
- if err := repo.GetBaseRepo(ctx); err != nil {
- return nil, err
- }
-
- forkBranch, err := git_model.GetBranch(ctx, repo.ID, branch)
- if err != nil {
- return nil, err
- }
-
- baseBranch, err := git_model.GetBranch(ctx, repo.BaseRepo.ID, branch)
- if err != nil {
+ if err := forkRepo.GetBaseRepo(ctx); err != nil {
return nil, err
}
- info := &UpstreamDivergingInfo{}
- if forkBranch.CommitID == baseBranch.CommitID {
- return info, nil
+ // Do the best to follow the GitHub's behavior, suppose there is a `branch-a` in fork repo:
+ // * if `branch-a` exists in base repo: try to sync `base:branch-a` to `fork:branch-a`
+ // * if `branch-a` doesn't exist in base repo: try to sync `base:main` to `fork:branch-a`
+ info, err := GetBranchDivergingInfo(ctx, forkRepo.BaseRepo, forkBranch, forkRepo, forkBranch)
+ if err == nil {
+ return &UpstreamDivergingInfo{
+ BaseBranchName: forkBranch,
+ BaseBranchHasNewCommits: info.BaseHasNewCommits,
+ HeadBranchCommitsBehind: info.HeadCommitsBehind,
+ }, nil
}
-
- // TODO: if the fork repo has new commits, this call will fail:
- // exit status 128 - fatal: Invalid symmetric difference expression aaaaaaaaaaaa...bbbbbbbbbbbb
- // so at the moment, we are not able to handle this case, should be improved in the future
- diff, err := git.GetDivergingCommits(ctx, repo.BaseRepo.RepoPath(), baseBranch.CommitID, forkBranch.CommitID)
- if err != nil {
- info.BaseIsNewer = baseBranch.UpdatedUnix > forkBranch.UpdatedUnix
- return info, nil
+ if errors.Is(err, util.ErrNotExist) {
+ info, err = GetBranchDivergingInfo(ctx, forkRepo.BaseRepo, forkRepo.BaseRepo.DefaultBranch, forkRepo, forkBranch)
+ if err == nil {
+ return &UpstreamDivergingInfo{
+ BaseBranchName: forkRepo.BaseRepo.DefaultBranch,
+ BaseBranchHasNewCommits: info.BaseHasNewCommits,
+ HeadBranchCommitsBehind: info.HeadCommitsBehind,
+ }, nil
+ }
}
- info.CommitsBehind, info.CommitsAhead = diff.Behind, diff.Ahead
- return info, nil
+ return nil, err
}
diff --git a/services/repository/migrate.go b/services/repository/migrate.go
index 7fe63eb5ca..0a3dc45339 100644
--- a/services/repository/migrate.go
+++ b/services/repository/migrate.go
@@ -8,14 +8,15 @@ import (
"errors"
"fmt"
"net/http"
- "strings"
"time"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
+ unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/migration"
@@ -117,14 +118,8 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
repo.Owner = u
}
- if err := repo_module.CheckDaemonExportOK(ctx, repo); err != nil {
- return repo, fmt.Errorf("checkDaemonExportOK: %w", err)
- }
-
- if stdout, _, err := git.NewCommand(ctx, "update-server-info").
- RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
- log.Error("MigrateRepositoryGitData(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
- return repo, fmt.Errorf("error in MigrateRepositoryGitData(git update-server-info): %w", err)
+ if err := updateGitRepoAfterCreate(ctx, repo); err != nil {
+ return nil, fmt.Errorf("updateGitRepoAfterCreate: %w", err)
}
gitRepo, err := git.OpenRepository(ctx, repoPath)
@@ -141,12 +136,12 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
if !repo.IsEmpty {
if len(repo.DefaultBranch) == 0 {
// Try to get HEAD branch and set it as default branch.
- headBranch, err := gitRepo.GetHEADBranch()
+ headBranchName, err := git.GetDefaultBranch(ctx, repoPath)
if err != nil {
return repo, fmt.Errorf("GetHEADBranch: %w", err)
}
- if headBranch != nil {
- repo.DefaultBranch = headBranch.Name
+ if headBranchName != "" {
+ repo.DefaultBranch = headBranchName
}
}
@@ -154,9 +149,9 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
return repo, fmt.Errorf("SyncRepoBranchesWithRepo: %v", err)
}
+ // if releases migration are not requested, we will sync all tags here
+ // otherwise, the releases sync will be done out of this function
if !opts.Releases {
- // note: this will greatly improve release (tag) sync
- // for pull-mirrors with many tags
repo.IsMirror = opts.Mirror
if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil {
log.Error("Failed to synchronize tags to releases for repository: %v", err)
@@ -225,15 +220,19 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
}
repo.IsMirror = true
- if err = UpdateRepository(ctx, repo, false); err != nil {
+ if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "num_watches", "is_empty", "default_branch", "default_wiki_branch", "is_mirror"); err != nil {
return nil, err
}
+ if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
+ log.Error("Failed to update size for repository: %v", err)
+ }
+
// this is necessary for sync local tags from remote
configName := fmt.Sprintf("remote.%s.fetch", mirrorModel.GetRemoteName())
- if stdout, _, err := git.NewCommand(ctx, "config").
+ if stdout, _, err := git.NewCommand("config").
AddOptionValues("--add", configName, `+refs/tags/*:refs/tags/*`).
- RunStdString(&git.RunOpts{Dir: repoPath}); err != nil {
+ RunStdString(ctx, &git.RunOpts{Dir: repoPath}); err != nil {
log.Error("MigrateRepositoryGitData(git config --add <remote> +refs/tags/*:refs/tags/*) in %v: Stdout: %s\nError: %v", repo, stdout, err)
return repo, fmt.Errorf("error in MigrateRepositoryGitData(git config --add <remote> +refs/tags/*:refs/tags/*): %w", err)
}
@@ -246,18 +245,31 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
}
}
+ var enableRepoUnits []repo_model.RepoUnit
+ if opts.Releases && !unit_model.TypeReleases.UnitGlobalDisabled() {
+ enableRepoUnits = append(enableRepoUnits, repo_model.RepoUnit{RepoID: repo.ID, Type: unit_model.TypeReleases})
+ }
+ if opts.Wiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
+ enableRepoUnits = append(enableRepoUnits, repo_model.RepoUnit{RepoID: repo.ID, Type: unit_model.TypeWiki})
+ }
+ if len(enableRepoUnits) > 0 {
+ err = UpdateRepositoryUnits(ctx, repo, enableRepoUnits, nil)
+ if err != nil {
+ return nil, err
+ }
+ }
return repo, committer.Commit()
}
// cleanUpMigrateGitConfig removes mirror info which prevents "push --all".
// This also removes possible user credentials.
func cleanUpMigrateGitConfig(ctx context.Context, repoPath string) error {
- cmd := git.NewCommand(ctx, "remote", "rm", "origin")
+ cmd := git.NewCommand("remote", "rm", "origin")
// if the origin does not exist
- _, stderr, err := cmd.RunStdString(&git.RunOpts{
+ _, _, err := cmd.RunStdString(ctx, &git.RunOpts{
Dir: repoPath,
})
- if err != nil && !strings.HasPrefix(stderr, "fatal: No such remote") {
+ if err != nil && !git.IsRemoteNotExistError(err) {
return err
}
return nil
@@ -265,18 +277,17 @@ func cleanUpMigrateGitConfig(ctx context.Context, repoPath string) error {
// CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors.
func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo_model.Repository, error) {
- repoPath := repo.RepoPath()
- if err := repo_module.CreateDelegateHooks(repoPath); err != nil {
+ if err := gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
return repo, fmt.Errorf("createDelegateHooks: %w", err)
}
if repo.HasWiki() {
- if err := repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil {
+ if err := gitrepo.CreateDelegateHooks(ctx, repo.WikiStorageRepo()); err != nil {
return repo, fmt.Errorf("createDelegateHooks.(wiki): %w", err)
}
}
- _, _, err := git.NewCommand(ctx, "remote", "rm", "origin").RunStdString(&git.RunOpts{Dir: repoPath})
- if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
+ _, _, err := git.NewCommand("remote", "rm", "origin").RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
+ if err != nil && !git.IsRemoteNotExistError(err) {
return repo, fmt.Errorf("CleanUpMigrateInfo: %w", err)
}
diff --git a/services/repository/push.go b/services/repository/push.go
index 06ad65e48f..af3c873d15 100644
--- a/services/repository/push.go
+++ b/services/repository/push.go
@@ -23,6 +23,7 @@ import (
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
issue_service "code.gitea.io/gitea/services/issue"
notify_service "code.gitea.io/gitea/services/notify"
pull_service "code.gitea.io/gitea/services/pull"
@@ -65,7 +66,7 @@ func PushUpdates(opts []*repo_module.PushUpdateOptions) error {
for _, opt := range opts {
if opt.IsNewRef() && opt.IsDelRef() {
- return fmt.Errorf("Old and new revisions are both NULL")
+ return errors.New("Old and new revisions are both NULL")
}
}
@@ -133,23 +134,26 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
} else { // is new tag
newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
if err != nil {
- return fmt.Errorf("gitRepo.GetCommit(%s) in %s/%s[%d]: %w", opts.NewCommitID, repo.OwnerName, repo.Name, repo.ID, err)
+ // in case there is dirty data, for example, the "github.com/git/git" repository has tags pointing to non-existing commits
+ if !errors.Is(err, util.ErrNotExist) {
+ log.Error("Unable to get tag commit: gitRepo.GetCommit(%s) in %s/%s[%d]: %v", opts.NewCommitID, repo.OwnerName, repo.Name, repo.ID, err)
+ }
+ } else {
+ commits := repo_module.NewPushCommits()
+ commits.HeadCommit = repo_module.CommitToPushCommit(newCommit)
+ commits.CompareURL = repo.ComposeCompareURL(objectFormat.EmptyObjectID().String(), opts.NewCommitID)
+
+ notify_service.PushCommits(
+ ctx, pusher, repo,
+ &repo_module.PushUpdateOptions{
+ RefFullName: opts.RefFullName,
+ OldCommitID: objectFormat.EmptyObjectID().String(),
+ NewCommitID: opts.NewCommitID,
+ }, commits)
+
+ addTags = append(addTags, tagName)
+ notify_service.CreateRef(ctx, pusher, repo, opts.RefFullName, opts.NewCommitID)
}
-
- commits := repo_module.NewPushCommits()
- commits.HeadCommit = repo_module.CommitToPushCommit(newCommit)
- commits.CompareURL = repo.ComposeCompareURL(objectFormat.EmptyObjectID().String(), opts.NewCommitID)
-
- notify_service.PushCommits(
- ctx, pusher, repo,
- &repo_module.PushUpdateOptions{
- RefFullName: opts.RefFullName,
- OldCommitID: objectFormat.EmptyObjectID().String(),
- NewCommitID: opts.NewCommitID,
- }, commits)
-
- addTags = append(addTags, tagName)
- notify_service.CreateRef(ctx, pusher, repo, opts.RefFullName, opts.NewCommitID)
}
} else if opts.RefFullName.IsBranch() {
if pusher == nil || pusher.ID != opts.PusherID {
@@ -163,59 +167,25 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
}
}
- branch := opts.RefFullName.BranchName()
if !opts.IsDelRef() {
+ branch := opts.RefFullName.BranchName()
+
log.Trace("TriggerTask '%s/%s' by %s", repo.Name, branch, pusher.Name)
- go pull_service.AddTestPullRequestTask(pusher, repo.ID, branch, true, opts.OldCommitID, opts.NewCommitID)
newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
if err != nil {
return fmt.Errorf("gitRepo.GetCommit(%s) in %s/%s[%d]: %w", opts.NewCommitID, repo.OwnerName, repo.Name, repo.ID, err)
}
- refName := opts.RefName()
-
// Push new branch.
var l []*git.Commit
if opts.IsNewRef() {
- if repo.IsEmpty { // Change default branch and empty status only if pushed ref is non-empty branch.
- repo.DefaultBranch = refName
- repo.IsEmpty = false
- if repo.DefaultBranch != setting.Repository.DefaultBranch {
- if err := gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil {
- return err
- }
- }
- // Update the is empty and default_branch columns
- if err := repo_model.UpdateRepositoryCols(ctx, repo, "default_branch", "is_empty"); err != nil {
- return fmt.Errorf("UpdateRepositoryCols: %w", err)
- }
- }
-
- l, err = newCommit.CommitsBeforeLimit(10)
- if err != nil {
- return fmt.Errorf("newCommit.CommitsBeforeLimit: %w", err)
- }
- notify_service.CreateRef(ctx, pusher, repo, opts.RefFullName, opts.NewCommitID)
+ l, err = pushNewBranch(ctx, repo, pusher, opts, newCommit)
} else {
- l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID)
- if err != nil {
- return fmt.Errorf("newCommit.CommitsBeforeUntil: %w", err)
- }
-
- isForcePush, err := newCommit.IsForcePush(opts.OldCommitID)
- if err != nil {
- log.Error("IsForcePush %s:%s failed: %v", repo.FullName(), branch, err)
- }
-
- if isForcePush {
- log.Trace("Push %s is a force push", opts.NewCommitID)
-
- cache.Remove(repo.GetCommitsCountCacheKey(opts.RefName(), true))
- } else {
- // TODO: increment update the commit count cache but not remove
- cache.Remove(repo.GetCommitsCountCacheKey(opts.RefName(), true))
- }
+ l, err = pushUpdateBranch(ctx, repo, pusher, opts, newCommit)
+ }
+ if err != nil {
+ return err
}
// delete cache for divergence
@@ -232,36 +202,11 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
commits := repo_module.GitToPushCommits(l)
commits.HeadCommit = repo_module.CommitToPushCommit(newCommit)
- if err := issue_service.UpdateIssuesCommit(ctx, pusher, repo, commits.Commits, refName); err != nil {
+ if err := issue_service.UpdateIssuesCommit(ctx, pusher, repo, commits.Commits, opts.RefName()); err != nil {
log.Error("updateIssuesCommit: %v", err)
}
- oldCommitID := opts.OldCommitID
- if oldCommitID == objectFormat.EmptyObjectID().String() && len(commits.Commits) > 0 {
- oldCommit, err := gitRepo.GetCommit(commits.Commits[len(commits.Commits)-1].Sha1)
- if err != nil && !git.IsErrNotExist(err) {
- log.Error("unable to GetCommit %s from %-v: %v", oldCommitID, repo, err)
- }
- if oldCommit != nil {
- for i := 0; i < oldCommit.ParentCount(); i++ {
- commitID, _ := oldCommit.ParentID(i)
- if !commitID.IsZero() {
- oldCommitID = commitID.String()
- break
- }
- }
- }
- }
-
- if oldCommitID == objectFormat.EmptyObjectID().String() && repo.DefaultBranch != branch {
- oldCommitID = repo.DefaultBranch
- }
-
- if oldCommitID != objectFormat.EmptyObjectID().String() {
- commits.CompareURL = repo.ComposeCompareURL(oldCommitID, opts.NewCommitID)
- } else {
- commits.CompareURL = ""
- }
+ commits.CompareURL = getCompareURL(repo, gitRepo, objectFormat, commits.Commits, opts)
if len(commits.Commits) > setting.UI.FeedMaxCommitNum {
commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum]
@@ -274,11 +219,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
log.Error("repo_module.CacheRef %s/%s failed: %v", repo.ID, branch, err)
}
} else {
- notify_service.DeleteRef(ctx, pusher, repo, opts.RefFullName)
- if err = pull_service.CloseBranchPulls(ctx, pusher, repo.ID, branch); err != nil {
- // close all related pulls
- log.Error("close related pull request failed: %v", err)
- }
+ pushDeleteBranch(ctx, repo, pusher, opts)
}
// Even if user delete a branch on a repository which he didn't watch, he will be watch that.
@@ -289,8 +230,11 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
log.Trace("Non-tag and non-branch commits pushed.")
}
}
- if err := PushUpdateAddDeleteTags(ctx, repo, gitRepo, addTags, delTags); err != nil {
- return fmt.Errorf("PushUpdateAddDeleteTags: %w", err)
+
+ if len(addTags)+len(delTags) > 0 {
+ if err := PushUpdateAddDeleteTags(ctx, repo, gitRepo, pusher, addTags, delTags); err != nil {
+ return fmt.Errorf("PushUpdateAddDeleteTags: %w", err)
+ }
}
// Change repository last updated time.
@@ -301,18 +245,114 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
return nil
}
+func getCompareURL(repo *repo_model.Repository, gitRepo *git.Repository, objectFormat git.ObjectFormat, commits []*repo_module.PushCommit, opts *repo_module.PushUpdateOptions) string {
+ oldCommitID := opts.OldCommitID
+ if oldCommitID == objectFormat.EmptyObjectID().String() && len(commits) > 0 {
+ oldCommit, err := gitRepo.GetCommit(commits[len(commits)-1].Sha1)
+ if err != nil && !git.IsErrNotExist(err) {
+ log.Error("unable to GetCommit %s from %-v: %v", oldCommitID, repo, err)
+ }
+ if oldCommit != nil {
+ for i := 0; i < oldCommit.ParentCount(); i++ {
+ commitID, _ := oldCommit.ParentID(i)
+ if !commitID.IsZero() {
+ oldCommitID = commitID.String()
+ break
+ }
+ }
+ }
+ }
+
+ if oldCommitID == objectFormat.EmptyObjectID().String() && repo.DefaultBranch != opts.RefFullName.BranchName() {
+ oldCommitID = repo.DefaultBranch
+ }
+
+ if oldCommitID != objectFormat.EmptyObjectID().String() {
+ return repo.ComposeCompareURL(oldCommitID, opts.NewCommitID)
+ }
+ return ""
+}
+
+func pushNewBranch(ctx context.Context, repo *repo_model.Repository, pusher *user_model.User, opts *repo_module.PushUpdateOptions, newCommit *git.Commit) ([]*git.Commit, error) {
+ if repo.IsEmpty { // Change default branch and empty status only if pushed ref is non-empty branch.
+ repo.DefaultBranch = opts.RefName()
+ repo.IsEmpty = false
+ if repo.DefaultBranch != setting.Repository.DefaultBranch {
+ if err := gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil {
+ return nil, err
+ }
+ }
+ // Update the is empty and default_branch columns
+ if err := repo_model.UpdateRepositoryColsWithAutoTime(ctx, repo, "default_branch", "is_empty"); err != nil {
+ return nil, fmt.Errorf("UpdateRepositoryCols: %w", err)
+ }
+ }
+
+ l, err := newCommit.CommitsBeforeLimit(10)
+ if err != nil {
+ return nil, fmt.Errorf("newCommit.CommitsBeforeLimit: %w", err)
+ }
+ notify_service.CreateRef(ctx, pusher, repo, opts.RefFullName, opts.NewCommitID)
+ return l, nil
+}
+
+func pushUpdateBranch(_ context.Context, repo *repo_model.Repository, pusher *user_model.User, opts *repo_module.PushUpdateOptions, newCommit *git.Commit) ([]*git.Commit, error) {
+ l, err := newCommit.CommitsBeforeUntil(opts.OldCommitID)
+ if err != nil {
+ return nil, fmt.Errorf("newCommit.CommitsBeforeUntil: %w", err)
+ }
+
+ branch := opts.RefFullName.BranchName()
+
+ isForcePush, err := newCommit.IsForcePush(opts.OldCommitID)
+ if err != nil {
+ log.Error("IsForcePush %s:%s failed: %v", repo.FullName(), branch, err)
+ }
+
+ // only update branch can trigger pull request task because the pull request hasn't been created yet when creating a branch
+ go pull_service.AddTestPullRequestTask(pull_service.TestPullRequestOptions{
+ RepoID: repo.ID,
+ Doer: pusher,
+ Branch: branch,
+ IsSync: true,
+ IsForcePush: isForcePush,
+ OldCommitID: opts.OldCommitID,
+ NewCommitID: opts.NewCommitID,
+ })
+
+ if isForcePush {
+ log.Trace("Push %s is a force push", opts.NewCommitID)
+
+ cache.Remove(repo.GetCommitsCountCacheKey(opts.RefName(), true))
+ } else {
+ // TODO: increment update the commit count cache but not remove
+ cache.Remove(repo.GetCommitsCountCacheKey(opts.RefName(), true))
+ }
+
+ return l, nil
+}
+
+func pushDeleteBranch(ctx context.Context, repo *repo_model.Repository, pusher *user_model.User, opts *repo_module.PushUpdateOptions) {
+ notify_service.DeleteRef(ctx, pusher, repo, opts.RefFullName)
+
+ if err := pull_service.AdjustPullsCausedByBranchDeleted(ctx, pusher, repo, opts.RefFullName.BranchName()); err != nil {
+ // close all related pulls
+ log.Error("close related pull request failed: %v", err)
+ }
+}
+
// PushUpdateAddDeleteTags updates a number of added and delete tags
-func PushUpdateAddDeleteTags(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, addTags, delTags []string) error {
+func PushUpdateAddDeleteTags(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, pusher *user_model.User, addTags, delTags []string) error {
return db.WithTx(ctx, func(ctx context.Context) error {
- if err := repo_model.PushUpdateDeleteTagsContext(ctx, repo, delTags); err != nil {
+ if err := repo_model.PushUpdateDeleteTags(ctx, repo, delTags); err != nil {
return err
}
- return pushUpdateAddTags(ctx, repo, gitRepo, addTags)
+ return pushUpdateAddTags(ctx, repo, gitRepo, pusher, addTags)
})
}
// pushUpdateAddTags updates a number of add tags
-func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, tags []string) error {
+func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, pusher *user_model.User, tags []string) error {
if len(tags) == 0 {
return nil
}
@@ -338,14 +378,12 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
newReleases := make([]*repo_model.Release, 0, len(lowerTags)-len(relMap))
- emailToUser := make(map[string]*user_model.User)
-
for i, lowerTag := range lowerTags {
tag, err := gitRepo.GetTag(tags[i])
if err != nil {
return fmt.Errorf("GetTag: %w", err)
}
- commit, err := tag.Commit(gitRepo)
+ commit, err := gitRepo.GetTagCommit(tag.Name)
if err != nil {
return fmt.Errorf("Commit: %w", err)
}
@@ -357,29 +395,12 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
if sig == nil {
sig = commit.Committer
}
- var author *user_model.User
- createdAt := time.Unix(1, 0)
+ createdAt := time.Unix(1, 0)
if sig != nil {
- var ok bool
- author, ok = emailToUser[sig.Email]
- if !ok {
- author, err = user_model.GetUserByEmail(ctx, sig.Email)
- if err != nil && !user_model.IsErrUserNotExist(err) {
- return fmt.Errorf("GetUserByEmail: %w", err)
- }
- if author != nil {
- emailToUser[sig.Email] = author
- }
- }
createdAt = sig.When
}
- commitsCount, err := commit.CommitsCount()
- if err != nil {
- return fmt.Errorf("CommitsCount: %w", err)
- }
-
rel, has := relMap[lowerTag]
parts := strings.SplitN(tag.Message, "\n", 2)
@@ -395,31 +416,26 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
LowerTagName: lowerTag,
Target: "",
Sha1: commit.ID.String(),
- NumCommits: commitsCount,
+ NumCommits: -1, // the commits count will be updated when the UI needs it
Note: note,
IsDraft: false,
IsPrerelease: false,
IsTag: true,
+ PublisherID: pusher.ID,
CreatedUnix: timeutil.TimeStamp(createdAt.Unix()),
}
- if author != nil {
- rel.PublisherID = author.ID
- }
newReleases = append(newReleases, rel)
} else {
rel.Sha1 = commit.ID.String()
rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix())
- rel.NumCommits = commitsCount
if rel.IsTag {
rel.Title = parts[0]
rel.Note = note
- if author != nil {
- rel.PublisherID = author.ID
- }
} else {
rel.IsDraft = false
}
+ rel.PublisherID = pusher.ID
if err = repo_model.UpdateRelease(ctx, rel); err != nil {
return fmt.Errorf("Update: %w", err)
}
diff --git a/services/repository/repository.go b/services/repository/repository.go
index 59b4491132..e574dc6c01 100644
--- a/services/repository/repository.go
+++ b/services/repository/repository.go
@@ -5,23 +5,29 @@ package repository
import (
"context"
+ "errors"
"fmt"
+ "os"
+ "path/filepath"
+ "strings"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
- packages_model "code.gitea.io/gitea/models/packages"
+ access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
- system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/graceful"
+ issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/queue"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
notify_service "code.gitea.io/gitea/services/notify"
pull_service "code.gitea.io/gitea/services/pull"
)
@@ -41,7 +47,7 @@ type WebSearchResults struct {
// CreateRepository creates a repository for the user/organization.
func CreateRepository(ctx context.Context, doer, owner *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
- repo, err := CreateRepositoryDirectly(ctx, doer, owner, opts)
+ repo, err := CreateRepositoryDirectly(ctx, doer, owner, opts, true)
if err != nil {
// No need to rollback here we should do this in CreateRepository...
return nil, err
@@ -63,11 +69,7 @@ func DeleteRepository(ctx context.Context, doer *user_model.User, repo *repo_mod
notify_service.DeleteRepository(ctx, doer, repo)
}
- if err := DeleteRepositoryDirectly(ctx, doer, repo.ID); err != nil {
- return err
- }
-
- return packages_model.UnlinkRepositoryFromAllPackages(ctx, repo.ID)
+ return DeleteRepositoryDirectly(ctx, repo.ID)
}
// PushCreateRepo creates a repository when a new repository is pushed to an appropriate namespace
@@ -77,10 +79,10 @@ func PushCreateRepo(ctx context.Context, authUser, owner *user_model.User, repoN
if ok, err := organization.CanCreateOrgRepo(ctx, owner.ID, authUser.ID); err != nil {
return nil, err
} else if !ok {
- return nil, fmt.Errorf("cannot push-create repository for org")
+ return nil, errors.New("cannot push-create repository for org")
}
} else if authUser.ID != owner.ID {
- return nil, fmt.Errorf("cannot push-create repository for another user")
+ return nil, errors.New("cannot push-create repository for another user")
}
}
@@ -99,15 +101,13 @@ func PushCreateRepo(ctx context.Context, authUser, owner *user_model.User, repoN
func Init(ctx context.Context) error {
licenseUpdaterQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "repo_license_updater", repoLicenseUpdater)
if licenseUpdaterQueue == nil {
- return fmt.Errorf("unable to create repo_license_updater queue")
+ return errors.New("unable to create repo_license_updater queue")
}
go graceful.GetManager().RunWithCancel(licenseUpdaterQueue)
if err := repo_module.LoadRepoConfig(); err != nil {
return err
}
- system_model.RemoveAllWithNotice(ctx, "Clean up temporary repository uploads", setting.Repository.Upload.TempPath)
- system_model.RemoveAllWithNotice(ctx, "Clean up temporary repositories", repo_module.LocalCopyPath())
if err := initPushQueue(); err != nil {
return err
}
@@ -116,42 +116,107 @@ func Init(ctx context.Context) error {
// UpdateRepository updates a repository
func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibilityChanged bool) (err error) {
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
-
- if err = repo_module.UpdateRepository(ctx, repo, visibilityChanged); err != nil {
- return fmt.Errorf("updateRepository: %w", err)
- }
-
- return committer.Commit()
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ if err = updateRepository(ctx, repo, visibilityChanged); err != nil {
+ return fmt.Errorf("updateRepository: %w", err)
+ }
+ return nil
+ })
}
-func UpdateRepositoryVisibility(ctx context.Context, repo *repo_model.Repository, isPrivate bool) (err error) {
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
+func MakeRepoPublic(ctx context.Context, repo *repo_model.Repository) (err error) {
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ repo.IsPrivate = false
+ if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_private"); err != nil {
+ return err
+ }
- defer committer.Close()
+ if err = repo.LoadOwner(ctx); err != nil {
+ return fmt.Errorf("LoadOwner: %w", err)
+ }
+ if repo.Owner.IsOrganization() {
+ // Organization repository need to recalculate access table when visibility is changed.
+ if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
+ return fmt.Errorf("recalculateTeamAccesses: %w", err)
+ }
+ }
- repo.IsPrivate = isPrivate
+ // Create/Remove git-daemon-export-ok for git-daemon...
+ if err := checkDaemonExportOK(ctx, repo); err != nil {
+ return err
+ }
- if err = repo_module.UpdateRepository(ctx, repo, true); err != nil {
- return fmt.Errorf("UpdateRepositoryVisibility: %w", err)
- }
+ forkRepos, err := repo_model.GetRepositoriesByForkID(ctx, repo.ID)
+ if err != nil {
+ return fmt.Errorf("getRepositoriesByForkID: %w", err)
+ }
- return committer.Commit()
-}
+ if repo.Owner.Visibility != structs.VisibleTypePrivate {
+ for i := range forkRepos {
+ if err = MakeRepoPublic(ctx, forkRepos[i]); err != nil {
+ return fmt.Errorf("MakeRepoPublic[%d]: %w", forkRepos[i].ID, err)
+ }
+ }
+ }
-func MakeRepoPublic(ctx context.Context, repo *repo_model.Repository) (err error) {
- return UpdateRepositoryVisibility(ctx, repo, false)
+ // If visibility is changed, we need to update the issue indexer.
+ // Since the data in the issue indexer have field to indicate if the repo is public or not.
+ issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
+
+ return nil
+ })
}
func MakeRepoPrivate(ctx context.Context, repo *repo_model.Repository) (err error) {
- return UpdateRepositoryVisibility(ctx, repo, true)
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ repo.IsPrivate = true
+ if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_private"); err != nil {
+ return err
+ }
+
+ if err = repo.LoadOwner(ctx); err != nil {
+ return fmt.Errorf("LoadOwner: %w", err)
+ }
+ if repo.Owner.IsOrganization() {
+ // Organization repository need to recalculate access table when visibility is changed.
+ if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
+ return fmt.Errorf("recalculateTeamAccesses: %w", err)
+ }
+ }
+
+ // If repo has become private, we need to set its actions to private.
+ _, err = db.GetEngine(ctx).Where("repo_id = ?", repo.ID).Cols("is_private").Update(&activities_model.Action{
+ IsPrivate: true,
+ })
+ if err != nil {
+ return err
+ }
+
+ if err = repo_model.ClearRepoStars(ctx, repo.ID); err != nil {
+ return err
+ }
+
+ // Create/Remove git-daemon-export-ok for git-daemon...
+ if err := checkDaemonExportOK(ctx, repo); err != nil {
+ return err
+ }
+
+ forkRepos, err := repo_model.GetRepositoriesByForkID(ctx, repo.ID)
+ if err != nil {
+ return fmt.Errorf("getRepositoriesByForkID: %w", err)
+ }
+ for i := range forkRepos {
+ if err = MakeRepoPrivate(ctx, forkRepos[i]); err != nil {
+ return fmt.Errorf("MakeRepoPrivate[%d]: %w", forkRepos[i].ID, err)
+ }
+ }
+
+ // If visibility is changed, we need to update the issue indexer.
+ // Since the data in the issue indexer have field to indicate if the repo is public or not.
+ issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
+
+ return nil
+ })
}
// LinkedRepository returns the linked repo if any
@@ -177,3 +242,97 @@ func LinkedRepository(ctx context.Context, a *repo_model.Attachment) (*repo_mode
}
return nil, -1, nil
}
+
+// checkDaemonExportOK creates/removes git-daemon-export-ok for git-daemon...
+func checkDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error {
+ if err := repo.LoadOwner(ctx); err != nil {
+ return err
+ }
+
+ // Create/Remove git-daemon-export-ok for git-daemon...
+ daemonExportFile := filepath.Join(repo.RepoPath(), `git-daemon-export-ok`)
+
+ isExist, err := util.IsExist(daemonExportFile)
+ if err != nil {
+ log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err)
+ return err
+ }
+
+ isPublic := !repo.IsPrivate && repo.Owner.Visibility == structs.VisibleTypePublic
+ if !isPublic && isExist {
+ if err = util.Remove(daemonExportFile); err != nil {
+ log.Error("Failed to remove %s: %v", daemonExportFile, err)
+ }
+ } else if isPublic && !isExist {
+ if f, err := os.Create(daemonExportFile); err != nil {
+ log.Error("Failed to create %s: %v", daemonExportFile, err)
+ } else {
+ f.Close()
+ }
+ }
+
+ return nil
+}
+
+// updateRepository updates a repository with db context
+func updateRepository(ctx context.Context, repo *repo_model.Repository, visibilityChanged bool) (err error) {
+ repo.LowerName = strings.ToLower(repo.Name)
+
+ e := db.GetEngine(ctx)
+
+ if _, err = e.ID(repo.ID).NoAutoTime().AllCols().Update(repo); err != nil {
+ return fmt.Errorf("update: %w", err)
+ }
+
+ if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
+ log.Error("Failed to update size for repository: %v", err)
+ }
+
+ if visibilityChanged {
+ if err = repo.LoadOwner(ctx); err != nil {
+ return fmt.Errorf("LoadOwner: %w", err)
+ }
+ if repo.Owner.IsOrganization() {
+ // Organization repository need to recalculate access table when visibility is changed.
+ if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
+ return fmt.Errorf("recalculateTeamAccesses: %w", err)
+ }
+ }
+
+ // If repo has become private, we need to set its actions to private.
+ if repo.IsPrivate {
+ _, err = e.Where("repo_id = ?", repo.ID).Cols("is_private").Update(&activities_model.Action{
+ IsPrivate: true,
+ })
+ if err != nil {
+ return err
+ }
+
+ if err = repo_model.ClearRepoStars(ctx, repo.ID); err != nil {
+ return err
+ }
+ }
+
+ // Create/Remove git-daemon-export-ok for git-daemon...
+ if err := checkDaemonExportOK(ctx, repo); err != nil {
+ return err
+ }
+
+ forkRepos, err := repo_model.GetRepositoriesByForkID(ctx, repo.ID)
+ if err != nil {
+ return fmt.Errorf("getRepositoriesByForkID: %w", err)
+ }
+ for i := range forkRepos {
+ forkRepos[i].IsPrivate = repo.IsPrivate || repo.Owner.Visibility == structs.VisibleTypePrivate
+ if err = updateRepository(ctx, forkRepos[i], true); err != nil {
+ return fmt.Errorf("updateRepository[%d]: %w", forkRepos[i].ID, err)
+ }
+ }
+
+ // If visibility is changed, we need to update the issue indexer.
+ // Since the data in the issue indexer have field to indicate if the repo is public or not.
+ issue_indexer.UpdateRepoIndexer(ctx, repo.ID)
+ }
+
+ return nil
+}
diff --git a/services/repository/repository_test.go b/services/repository/repository_test.go
index 892a11a23e..8f9fdf8fa1 100644
--- a/services/repository/repository_test.go
+++ b/services/repository/repository_test.go
@@ -6,6 +6,7 @@ package repository
import (
"testing"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
@@ -40,3 +41,23 @@ func TestLinkedRepository(t *testing.T) {
})
}
}
+
+func TestUpdateRepositoryVisibilityChanged(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ // Get sample repo and change visibility
+ repo, err := repo_model.GetRepositoryByID(db.DefaultContext, 9)
+ assert.NoError(t, err)
+ repo.IsPrivate = true
+
+ // Update it
+ err = updateRepository(db.DefaultContext, repo, true)
+ assert.NoError(t, err)
+
+ // Check visibility of action has become private
+ act := activities_model.Action{}
+ _, err = db.GetEngine(db.DefaultContext).ID(3).Get(&act)
+
+ assert.NoError(t, err)
+ assert.True(t, act.IsPrivate)
+}
diff --git a/services/repository/setting.go b/services/repository/setting.go
index b82f24271e..e0c787dd2d 100644
--- a/services/repository/setting.go
+++ b/services/repository/setting.go
@@ -7,7 +7,6 @@ import (
"context"
"slices"
- actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
@@ -29,7 +28,7 @@ func UpdateRepositoryUnits(ctx context.Context, repo *repo_model.Repository, uni
}
if slices.Contains(deleteUnitTypes, unit.TypeActions) {
- if err := actions_model.CleanRepoScheduleTasks(ctx, repo); err != nil {
+ if err := actions_service.CleanRepoScheduleTasks(ctx, repo); err != nil {
log.Error("CleanRepoScheduleTasks: %v", err)
}
}
diff --git a/services/repository/template.go b/services/repository/template.go
index 36a680c8e2..6906a60083 100644
--- a/services/repository/template.go
+++ b/services/repository/template.go
@@ -5,12 +5,17 @@ package repository
import (
"context"
+ "fmt"
+ "strings"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/log"
notify_service "code.gitea.io/gitea/services/notify"
)
@@ -63,70 +68,124 @@ func GenerateProtectedBranch(ctx context.Context, templateRepo, generateRepo *re
// GenerateRepository generates a repository from a template
func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templateRepo *repo_model.Repository, opts GenerateRepoOptions) (_ *repo_model.Repository, err error) {
- if !doer.IsAdmin && !owner.CanCreateRepo() {
+ if !doer.CanCreateRepoIn(owner) {
return nil, repo_model.ErrReachLimitOfRepo{
Limit: owner.MaxRepoCreation,
}
}
- var generateRepo *repo_model.Repository
- if err = db.WithTx(ctx, func(ctx context.Context) error {
- generateRepo, err = generateRepository(ctx, doer, owner, templateRepo, opts)
+ generateRepo := &repo_model.Repository{
+ OwnerID: owner.ID,
+ Owner: owner,
+ OwnerName: owner.Name,
+ Name: opts.Name,
+ LowerName: strings.ToLower(opts.Name),
+ Description: opts.Description,
+ DefaultBranch: opts.DefaultBranch,
+ IsPrivate: opts.Private,
+ IsEmpty: !opts.GitContent || templateRepo.IsEmpty,
+ IsFsckEnabled: templateRepo.IsFsckEnabled,
+ TemplateID: templateRepo.ID,
+ TrustModel: templateRepo.TrustModel,
+ ObjectFormatName: templateRepo.ObjectFormatName,
+ Status: repo_model.RepositoryBeingMigrated,
+ }
+
+ // 1 - Create the repository in the database
+ if err := db.WithTx(ctx, func(ctx context.Context) error {
+ return createRepositoryInDB(ctx, doer, owner, generateRepo, false)
+ }); err != nil {
+ return nil, err
+ }
+
+ // last - clean up the repository if something goes wrong
+ defer func() {
if err != nil {
- return err
+ // we can not use the ctx because it maybe canceled or timeout
+ cleanupRepository(generateRepo.ID)
}
+ }()
- // Git Content
- if opts.GitContent && !templateRepo.IsEmpty {
- if err = GenerateGitContent(ctx, templateRepo, generateRepo); err != nil {
- return err
- }
+ // 2 - check whether the repository with the same storage exists
+ isExist, err := gitrepo.IsRepositoryExist(ctx, generateRepo)
+ if err != nil {
+ log.Error("Unable to check if %s exists. Error: %v", generateRepo.FullName(), err)
+ return nil, err
+ }
+ if isExist {
+ // Don't return directly, we need err in defer to cleanupRepository
+ err = repo_model.ErrRepoFilesAlreadyExist{
+ Uname: generateRepo.OwnerName,
+ Name: generateRepo.Name,
}
+ return nil, err
+ }
- // Topics
- if opts.Topics {
- if err = repo_model.GenerateTopics(ctx, templateRepo, generateRepo); err != nil {
- return err
- }
+ // 3 -Init git bare new repository.
+ if err = git.InitRepository(ctx, generateRepo.RepoPath(), true, generateRepo.ObjectFormatName); err != nil {
+ return nil, fmt.Errorf("git.InitRepository: %w", err)
+ } else if err = gitrepo.CreateDelegateHooks(ctx, generateRepo); err != nil {
+ return nil, fmt.Errorf("createDelegateHooks: %w", err)
+ }
+
+ // 4 - Update the git repository
+ if err = updateGitRepoAfterCreate(ctx, generateRepo); err != nil {
+ return nil, fmt.Errorf("updateGitRepoAfterCreate: %w", err)
+ }
+
+ // 5 - generate the repository contents according to the template
+ // Git Content
+ if opts.GitContent && !templateRepo.IsEmpty {
+ if err = GenerateGitContent(ctx, templateRepo, generateRepo); err != nil {
+ return nil, err
}
+ }
- // Git Hooks
- if opts.GitHooks {
- if err = GenerateGitHooks(ctx, templateRepo, generateRepo); err != nil {
- return err
- }
+ // Topics
+ if opts.Topics {
+ if err = repo_model.GenerateTopics(ctx, templateRepo, generateRepo); err != nil {
+ return nil, err
}
+ }
- // Webhooks
- if opts.Webhooks {
- if err = GenerateWebhooks(ctx, templateRepo, generateRepo); err != nil {
- return err
- }
+ // Git Hooks
+ if opts.GitHooks {
+ if err = GenerateGitHooks(ctx, templateRepo, generateRepo); err != nil {
+ return nil, err
}
+ }
- // Avatar
- if opts.Avatar && len(templateRepo.Avatar) > 0 {
- if err = generateAvatar(ctx, templateRepo, generateRepo); err != nil {
- return err
- }
+ // Webhooks
+ if opts.Webhooks {
+ if err = GenerateWebhooks(ctx, templateRepo, generateRepo); err != nil {
+ return nil, err
}
+ }
- // Issue Labels
- if opts.IssueLabels {
- if err = GenerateIssueLabels(ctx, templateRepo, generateRepo); err != nil {
- return err
- }
+ // Avatar
+ if opts.Avatar && len(templateRepo.Avatar) > 0 {
+ if err = generateAvatar(ctx, templateRepo, generateRepo); err != nil {
+ return nil, err
}
+ }
- if opts.ProtectedBranch {
- if err = GenerateProtectedBranch(ctx, templateRepo, generateRepo); err != nil {
- return err
- }
+ // Issue Labels
+ if opts.IssueLabels {
+ if err = GenerateIssueLabels(ctx, templateRepo, generateRepo); err != nil {
+ return nil, err
}
+ }
- return nil
- }); err != nil {
- return nil, err
+ if opts.ProtectedBranch {
+ if err = GenerateProtectedBranch(ctx, templateRepo, generateRepo); err != nil {
+ return nil, err
+ }
+ }
+
+ // 6 - update repository status to be ready
+ generateRepo.Status = repo_model.RepositoryReady
+ if err = repo_model.UpdateRepositoryColsWithAutoTime(ctx, generateRepo, "status"); err != nil {
+ return nil, fmt.Errorf("UpdateRepositoryCols: %w", err)
}
notify_service.CreateRepository(ctx, doer, owner, generateRepo)
diff --git a/services/repository/transfer.go b/services/repository/transfer.go
index 9ef28ddeb9..5ad63cca67 100644
--- a/services/repository/transfer.go
+++ b/services/repository/transfer.go
@@ -17,29 +17,31 @@ import (
project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/globallock"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
notify_service "code.gitea.io/gitea/services/notify"
)
-func getRepoWorkingLockKey(repoID int64) string {
- return fmt.Sprintf("repo_working_%d", repoID)
+type LimitReachedError struct{ Limit int }
+
+func (LimitReachedError) Error() string {
+ return "Repository limit has been reached"
}
-// TransferOwnership transfers all corresponding setting from old user to new one.
-func TransferOwnership(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*organization.Team) error {
- if err := repo.LoadOwner(ctx); err != nil {
- return err
- }
- for _, team := range teams {
- if newOwner.ID != team.OrgID {
- return fmt.Errorf("team %d does not belong to organization", team.ID)
- }
- }
+func IsRepositoryLimitReached(err error) bool {
+ _, ok := err.(LimitReachedError)
+ return ok
+}
- oldOwner := repo.Owner
+func getRepoWorkingLockKey(repoID int64) string {
+ return fmt.Sprintf("repo_working_%d", repoID)
+}
+// AcceptTransferOwnership transfers all corresponding setting from old user to new one.
+func AcceptTransferOwnership(ctx context.Context, repo *repo_model.Repository, doer *user_model.User) error {
releaser, err := globallock.Lock(ctx, getRepoWorkingLockKey(repo.ID))
if err != nil {
log.Error("lock.Lock(): %v", err)
@@ -47,29 +49,49 @@ func TransferOwnership(ctx context.Context, doer, newOwner *user_model.User, rep
}
defer releaser()
- if err := transferOwnership(ctx, doer, newOwner.Name, repo); err != nil {
- return err
- }
- releaser()
-
- newRepo, err := repo_model.GetRepositoryByID(ctx, repo.ID)
+ repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, repo)
if err != nil {
return err
}
- for _, team := range teams {
- if err := addRepositoryToTeam(ctx, team, newRepo); err != nil {
+ oldOwnerName := repo.OwnerName
+
+ if err := db.WithTx(ctx, func(ctx context.Context) error {
+ if err := repoTransfer.LoadAttributes(ctx); err != nil {
return err
}
+
+ if !doer.CanCreateRepoIn(repoTransfer.Recipient) {
+ limit := util.Iif(repoTransfer.Recipient.MaxRepoCreation >= 0, repoTransfer.Recipient.MaxRepoCreation, setting.Repository.MaxCreationLimit)
+ return LimitReachedError{Limit: limit}
+ }
+
+ if !repoTransfer.CanUserAcceptOrRejectTransfer(ctx, doer) {
+ return util.ErrPermissionDenied
+ }
+
+ if err := repo.LoadOwner(ctx); err != nil {
+ return err
+ }
+ for _, team := range repoTransfer.Teams {
+ if repoTransfer.Recipient.ID != team.OrgID {
+ return fmt.Errorf("team %d does not belong to organization", team.ID)
+ }
+ }
+
+ return transferOwnership(ctx, repoTransfer.Doer, repoTransfer.Recipient.Name, repo, repoTransfer.Teams)
+ }); err != nil {
+ return err
}
+ releaser()
- notify_service.TransferRepository(ctx, doer, repo, oldOwner.Name)
+ notify_service.TransferRepository(ctx, doer, repo, oldOwnerName)
return nil
}
// transferOwnership transfers all corresponding repository items from old user to new one.
-func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName string, repo *repo_model.Repository) (err error) {
+func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName string, repo *repo_model.Repository, teams []*organization.Team) (err error) {
repoRenamed := false
wikiRenamed := false
oldOwnerName := doer.Name
@@ -138,7 +160,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
repo.OwnerName = newOwner.Name
// Update repository.
- if _, err := sess.ID(repo.ID).Update(repo); err != nil {
+ if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "owner_id", "owner_name"); err != nil {
return fmt.Errorf("update owner: %w", err)
}
@@ -174,15 +196,13 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
collaboration.UserID = 0
}
- // Remove old team-repository relations.
if oldOwner.IsOrganization() {
+ // Remove old team-repository relations.
if err := organization.RemoveOrgRepo(ctx, oldOwner.ID, repo.ID); err != nil {
return fmt.Errorf("removeOrgRepo: %w", err)
}
- }
- // Remove project's issues that belong to old organization's projects
- if oldOwner.IsOrganization() {
+ // Remove project's issues that belong to old organization's projects
projects, err := project_model.GetAllProjectsIDsByOwnerIDAndType(ctx, oldOwner.ID, project_model.TypeOrganization)
if err != nil {
return fmt.Errorf("Unable to find old org projects: %w", err)
@@ -225,15 +245,13 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
return fmt.Errorf("watchRepo: %w", err)
}
- // Remove watch for organization.
if oldOwner.IsOrganization() {
+ // Remove watch for organization.
if err := repo_model.WatchRepo(ctx, oldOwner, repo, false); err != nil {
return fmt.Errorf("watchRepo [false]: %w", err)
}
- }
- // Delete labels that belong to the old organization and comments that added these labels
- if oldOwner.IsOrganization() {
+ // Delete labels that belong to the old organization and comments that added these labels
if _, err := sess.Exec(`DELETE FROM issue_label WHERE issue_label.id IN (
SELECT il_too.id FROM (
SELECT il_too_too.id
@@ -261,7 +279,6 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
// Rename remote repository to new path and delete local copy.
dir := user_model.UserPath(newOwner.Name)
-
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return fmt.Errorf("Failed to create dir %s: %w", dir, err)
}
@@ -273,7 +290,6 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
// Rename remote wiki repository to new path and delete local copy.
wikiPath := repo_model.WikiPath(oldOwner.Name, repo.Name)
-
if isExist, err := util.IsExist(wikiPath); err != nil {
log.Error("Unable to check if %s exists. Error: %v", wikiPath, err)
return err
@@ -288,7 +304,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
return fmt.Errorf("deleteRepositoryTransfer: %w", err)
}
repo.Status = repo_model.RepositoryReady
- if err := repo_model.UpdateRepositoryCols(ctx, repo, "status"); err != nil {
+ if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "status"); err != nil {
return err
}
@@ -301,6 +317,17 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
return fmt.Errorf("repo_model.NewRedirect: %w", err)
}
+ newRepo, err := repo_model.GetRepositoryByID(ctx, repo.ID)
+ if err != nil {
+ return err
+ }
+
+ for _, team := range teams {
+ if err := addRepositoryToTeam(ctx, team, newRepo); err != nil {
+ return err
+ }
+ }
+
return committer.Commit()
}
@@ -321,13 +348,13 @@ func changeRepositoryName(ctx context.Context, repo *repo_model.Repository, newR
return fmt.Errorf("IsRepositoryExist: %w", err)
} else if has {
return repo_model.ErrRepoAlreadyExist{
- Uname: repo.Owner.Name,
+ Uname: repo.OwnerName,
Name: newRepoName,
}
}
- newRepoPath := repo_model.RepoPath(repo.Owner.Name, newRepoName)
- if err = util.Rename(repo.RepoPath(), newRepoPath); err != nil {
+ if err = gitrepo.RenameRepository(ctx, repo,
+ repo_model.StorageRepo(repo_model.RelativePath(repo.OwnerName, newRepoName))); err != nil {
return fmt.Errorf("rename repository directory: %w", err)
}
@@ -343,17 +370,9 @@ func changeRepositoryName(ctx context.Context, repo *repo_model.Repository, newR
}
}
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
-
- if err := repo_model.NewRedirect(ctx, repo.Owner.ID, repo.ID, oldRepoName, newRepoName); err != nil {
- return err
- }
-
- return committer.Commit()
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ return repo_model.NewRedirect(ctx, repo.Owner.ID, repo.ID, oldRepoName, newRepoName)
+ })
}
// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
@@ -387,70 +406,147 @@ func ChangeRepositoryName(ctx context.Context, doer *user_model.User, repo *repo
// StartRepositoryTransfer transfer a repo from one owner to a new one.
// it make repository into pending transfer state, if doer can not create repo for new owner.
func StartRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*organization.Team) error {
+ releaser, err := globallock.Lock(ctx, getRepoWorkingLockKey(repo.ID))
+ if err != nil {
+ return fmt.Errorf("lock.Lock: %w", err)
+ }
+ defer releaser()
+
if err := repo_model.TestRepositoryReadyForTransfer(repo.Status); err != nil {
return err
}
- // Admin is always allowed to transfer || user transfer repo back to his account
- if doer.IsAdmin || doer.ID == newOwner.ID {
- return TransferOwnership(ctx, doer, newOwner, repo, teams)
+ if !doer.CanForkRepoIn(newOwner) {
+ limit := util.Iif(newOwner.MaxRepoCreation >= 0, newOwner.MaxRepoCreation, setting.Repository.MaxCreationLimit)
+ return LimitReachedError{Limit: limit}
}
- if user_model.IsUserBlockedBy(ctx, doer, newOwner.ID) {
- return user_model.ErrBlockedUser
- }
+ var isDirectTransfer bool
+ oldOwnerName := repo.OwnerName
- // If new owner is an org and user can create repos he can transfer directly too
- if newOwner.IsOrganization() {
- allowed, err := organization.CanCreateOrgRepo(ctx, newOwner.ID, doer.ID)
- if err != nil {
- return err
+ if err := db.WithTx(ctx, func(ctx context.Context) error {
+ // Admin is always allowed to transfer || user transfer repo back to his account,
+ // then it will transfer directly without acceptance.
+ if doer.IsAdmin || doer.ID == newOwner.ID {
+ isDirectTransfer = true
+ return transferOwnership(ctx, doer, newOwner.Name, repo, teams)
}
- if allowed {
- return TransferOwnership(ctx, doer, newOwner, repo, teams)
+
+ if user_model.IsUserBlockedBy(ctx, doer, newOwner.ID) {
+ return user_model.ErrBlockedUser
}
- }
- // In case the new owner would not have sufficient access to the repo, give access rights for read
- hasAccess, err := access_model.HasAnyUnitAccess(ctx, newOwner.ID, repo)
- if err != nil {
- return err
- }
- if !hasAccess {
- if err := AddOrUpdateCollaborator(ctx, repo, newOwner, perm.AccessModeRead); err != nil {
+ // If new owner is an org and user can create repos he can transfer directly too
+ if newOwner.IsOrganization() {
+ allowed, err := organization.CanCreateOrgRepo(ctx, newOwner.ID, doer.ID)
+ if err != nil {
+ return err
+ }
+ if allowed {
+ isDirectTransfer = true
+ return transferOwnership(ctx, doer, newOwner.Name, repo, teams)
+ }
+ }
+
+ // In case the new owner would not have sufficient access to the repo, give access rights for read
+ hasAccess, err := access_model.HasAnyUnitAccess(ctx, newOwner.ID, repo)
+ if err != nil {
return err
}
- }
+ if !hasAccess {
+ if err := AddOrUpdateCollaborator(ctx, repo, newOwner, perm.AccessModeRead); err != nil {
+ return err
+ }
+ }
- // Make repo as pending for transfer
- repo.Status = repo_model.RepositoryPendingTransfer
- if err := repo_model.CreatePendingRepositoryTransfer(ctx, doer, newOwner, repo.ID, teams); err != nil {
+ // Make repo as pending for transfer
+ repo.Status = repo_model.RepositoryPendingTransfer
+ return repo_model.CreatePendingRepositoryTransfer(ctx, doer, newOwner, repo.ID, teams)
+ }); err != nil {
return err
}
- // notify users who are able to accept / reject transfer
- notify_service.RepoPendingTransfer(ctx, doer, newOwner, repo)
+ if isDirectTransfer {
+ notify_service.TransferRepository(ctx, doer, repo, oldOwnerName)
+ } else {
+ // notify users who are able to accept / reject transfer
+ notify_service.RepoPendingTransfer(ctx, doer, newOwner, repo)
+ }
return nil
}
-// CancelRepositoryTransfer marks the repository as ready and remove pending transfer entry,
+// RejectRepositoryTransfer marks the repository as ready and remove pending transfer entry,
// thus cancel the transfer process.
-func CancelRepositoryTransfer(ctx context.Context, repo *repo_model.Repository) error {
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
+// The accepter can reject the transfer.
+func RejectRepositoryTransfer(ctx context.Context, repo *repo_model.Repository, doer *user_model.User) error {
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, repo)
+ if err != nil {
+ return err
+ }
+
+ if err := repoTransfer.LoadAttributes(ctx); err != nil {
+ return err
+ }
+
+ if !repoTransfer.CanUserAcceptOrRejectTransfer(ctx, doer) {
+ return util.ErrPermissionDenied
+ }
+
+ repo.Status = repo_model.RepositoryReady
+ if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "status"); err != nil {
+ return err
+ }
+
+ return repo_model.DeleteRepositoryTransfer(ctx, repo.ID)
+ })
+}
+
+func canUserCancelTransfer(ctx context.Context, r *repo_model.RepoTransfer, u *user_model.User) bool {
+ if u.IsAdmin || u.ID == r.DoerID {
+ return true
}
- defer committer.Close()
- repo.Status = repo_model.RepositoryReady
- if err := repo_model.UpdateRepositoryCols(ctx, repo, "status"); err != nil {
- return err
+ if err := r.LoadAttributes(ctx); err != nil {
+ log.Error("LoadAttributes: %v", err)
+ return false
}
- if err := repo_model.DeleteRepositoryTransfer(ctx, repo.ID); err != nil {
- return err
+ if err := r.Repo.LoadOwner(ctx); err != nil {
+ log.Error("LoadOwner: %v", err)
+ return false
}
- return committer.Commit()
+ if !r.Repo.Owner.IsOrganization() {
+ return r.Repo.OwnerID == u.ID
+ }
+
+ perm, err := access_model.GetUserRepoPermission(ctx, r.Repo, u)
+ if err != nil {
+ log.Error("GetUserRepoPermission: %v", err)
+ return false
+ }
+ return perm.IsOwner()
+}
+
+// CancelRepositoryTransfer cancels the repository transfer process. The sender or
+// the users who have admin permission of the original repository can cancel the transfer
+func CancelRepositoryTransfer(ctx context.Context, repoTransfer *repo_model.RepoTransfer, doer *user_model.User) error {
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ if err := repoTransfer.LoadAttributes(ctx); err != nil {
+ return err
+ }
+
+ if !canUserCancelTransfer(ctx, repoTransfer, doer) {
+ return util.ErrPermissionDenied
+ }
+
+ repoTransfer.Repo.Status = repo_model.RepositoryReady
+ if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repoTransfer.Repo, "status"); err != nil {
+ return err
+ }
+
+ return repo_model.DeleteRepositoryTransfer(ctx, repoTransfer.RepoID)
+ })
}
diff --git a/services/repository/transfer_test.go b/services/repository/transfer_test.go
index 91722fb8ae..80a073e9f9 100644
--- a/services/repository/transfer_test.go
+++ b/services/repository/transfer_test.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Gitea Authors. All rights reserved.
+// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repository
@@ -14,11 +14,14 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/feed"
notify_service "code.gitea.io/gitea/services/notify"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
var notifySync sync.Once
@@ -34,23 +37,26 @@ func TestTransferOwnership(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
- repo.Owner = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
- assert.NoError(t, TransferOwnership(db.DefaultContext, doer, doer, repo, nil))
+ assert.NoError(t, repo.LoadOwner(db.DefaultContext))
+ repoTransfer := unittest.AssertExistsAndLoadBean(t, &repo_model.RepoTransfer{ID: 1})
+ assert.NoError(t, repoTransfer.LoadAttributes(db.DefaultContext))
+ assert.NoError(t, AcceptTransferOwnership(db.DefaultContext, repo, doer))
transferredRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
- assert.EqualValues(t, 2, transferredRepo.OwnerID)
+ assert.EqualValues(t, 1, transferredRepo.OwnerID) // repo_transfer.yml id=1
+ unittest.AssertNotExistsBean(t, &repo_model.RepoTransfer{ID: 1})
exist, err := util.IsExist(repo_model.RepoPath("org3", "repo3"))
assert.NoError(t, err)
assert.False(t, exist)
- exist, err = util.IsExist(repo_model.RepoPath("user2", "repo3"))
+ exist, err = util.IsExist(repo_model.RepoPath("user1", "repo3"))
assert.NoError(t, err)
assert.True(t, exist)
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
OpType: activities_model.ActionTransferRepo,
- ActUserID: 2,
+ ActUserID: 1,
RepoID: 3,
Content: "org3/repo3",
})
@@ -61,10 +67,10 @@ func TestTransferOwnership(t *testing.T) {
func TestStartRepositoryTransferSetPermission(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
recipient := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
- repo.Owner = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
+ assert.NoError(t, repo.LoadOwner(db.DefaultContext))
hasAccess, err := access_model.HasAnyUnitAccess(db.DefaultContext, recipient.ID, repo)
assert.NoError(t, err)
@@ -82,7 +88,7 @@ func TestStartRepositoryTransferSetPermission(t *testing.T) {
func TestRepositoryTransfer(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
transfer, err := repo_model.GetPendingRepositoryTransfer(db.DefaultContext, repo)
@@ -90,7 +96,7 @@ func TestRepositoryTransfer(t *testing.T) {
assert.NotNil(t, transfer)
// Cancel transfer
- assert.NoError(t, CancelRepositoryTransfer(db.DefaultContext, repo))
+ assert.NoError(t, CancelRepositoryTransfer(db.DefaultContext, transfer, doer))
transfer, err = repo_model.GetPendingRepositoryTransfer(db.DefaultContext, repo)
assert.Error(t, err)
@@ -113,10 +119,49 @@ func TestRepositoryTransfer(t *testing.T) {
assert.Error(t, err)
assert.True(t, repo_model.IsErrRepoTransferInProgress(err))
- // Unknown user
- err = repo_model.CreatePendingRepositoryTransfer(db.DefaultContext, doer, &user_model.User{ID: 1000, LowerName: "user1000"}, repo.ID, nil)
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
+ // Unknown user, transfer non-existent transfer repo id = 2
+ err = repo_model.CreatePendingRepositoryTransfer(db.DefaultContext, doer, &user_model.User{ID: 1000, LowerName: "user1000"}, repo2.ID, nil)
assert.Error(t, err)
- // Cancel transfer
- assert.NoError(t, CancelRepositoryTransfer(db.DefaultContext, repo))
+ // Reject transfer
+ err = RejectRepositoryTransfer(db.DefaultContext, repo2, doer)
+ assert.True(t, repo_model.IsErrNoPendingTransfer(err))
+}
+
+// Test transfer rejections
+func TestRepositoryTransferRejection(t *testing.T) {
+ require.NoError(t, unittest.PrepareTestDatabase())
+ // Set limit to 0 repositories so no repositories can be transferred
+ defer test.MockVariableValue(&setting.Repository.MaxCreationLimit, 0)()
+
+ // Admin case
+ doerAdmin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 5})
+
+ transfer, err := repo_model.GetPendingRepositoryTransfer(db.DefaultContext, repo)
+ require.NoError(t, err)
+ require.NotNil(t, transfer)
+ require.NoError(t, transfer.LoadRecipient(db.DefaultContext))
+
+ require.True(t, doerAdmin.CanCreateRepoIn(transfer.Recipient)) // admin is not subject to limits
+
+ // Administrator should not be affected by the limits so transfer should be successful
+ assert.NoError(t, AcceptTransferOwnership(db.DefaultContext, repo, doerAdmin))
+
+ // Non admin user case
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10})
+ repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 21})
+
+ transfer, err = repo_model.GetPendingRepositoryTransfer(db.DefaultContext, repo)
+ require.NoError(t, err)
+ require.NotNil(t, transfer)
+ require.NoError(t, transfer.LoadRecipient(db.DefaultContext))
+
+ require.False(t, doer.CanCreateRepoIn(transfer.Recipient)) // regular user is subject to limits
+
+ // Cannot accept because of the limit
+ err = AcceptTransferOwnership(db.DefaultContext, repo, doer)
+ assert.Error(t, err)
+ assert.True(t, IsRepositoryLimitReached(err))
}
diff --git a/services/secrets/secrets.go b/services/secrets/secrets.go
index 031c474dd7..ec6a3cb062 100644
--- a/services/secrets/secrets.go
+++ b/services/secrets/secrets.go
@@ -10,7 +10,7 @@ import (
secret_model "code.gitea.io/gitea/models/secret"
)
-func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data string) (*secret_model.Secret, bool, error) {
+func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data, description string) (*secret_model.Secret, bool, error) {
if err := ValidateName(name); err != nil {
return nil, false, err
}
@@ -25,14 +25,14 @@ func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data
}
if len(s) == 0 {
- s, err := secret_model.InsertEncryptedSecret(ctx, ownerID, repoID, name, data)
+ s, err := secret_model.InsertEncryptedSecret(ctx, ownerID, repoID, name, data, description)
if err != nil {
return nil, false, err
}
return s, true, nil
}
- if err := secret_model.UpdateSecret(ctx, s[0].ID, data); err != nil {
+ if err := secret_model.UpdateSecret(ctx, s[0].ID, data, description); err != nil {
return nil, false, err
}
diff --git a/services/task/task.go b/services/task/task.go
index c90ee91270..ee5fa1f348 100644
--- a/services/task/task.go
+++ b/services/task/task.go
@@ -5,6 +5,7 @@ package task
import (
"context"
+ "errors"
"fmt"
admin_model "code.gitea.io/gitea/models/admin"
@@ -41,7 +42,7 @@ func Run(ctx context.Context, t *admin_model.Task) error {
func Init() error {
taskQueue = queue.CreateSimpleQueue(graceful.GetManager().ShutdownContext(), "task", handler)
if taskQueue == nil {
- return fmt.Errorf("unable to create task queue")
+ return errors.New("unable to create task queue")
}
go graceful.GetManager().RunWithCancel(taskQueue)
return nil
@@ -110,7 +111,7 @@ func CreateMigrateTask(ctx context.Context, doer, u *user_model.User, opts base.
IsPrivate: opts.Private || setting.Repository.ForcePrivate,
IsMirror: opts.Mirror,
Status: repo_model.RepositoryBeingMigrated,
- })
+ }, false)
if err != nil {
task.EndTime = timeutil.TimeStampNow()
task.Status = structs.TaskStatusFailed
diff --git a/services/user/block.go b/services/user/block.go
index c24ce5273c..7727780dfc 100644
--- a/services/user/block.go
+++ b/services/user/block.go
@@ -117,10 +117,10 @@ func BlockUser(ctx context.Context, doer, blocker, blockee *user_model.User, not
}
// cancel each other repository transfers
- if err := cancelRepositoryTransfers(ctx, blocker, blockee); err != nil {
+ if err := cancelRepositoryTransfers(ctx, doer, blocker, blockee); err != nil {
return err
}
- if err := cancelRepositoryTransfers(ctx, blockee, blocker); err != nil {
+ if err := cancelRepositoryTransfers(ctx, doer, blockee, blocker); err != nil {
return err
}
@@ -192,7 +192,7 @@ func unwatchRepos(ctx context.Context, watcher, repoOwner *user_model.User) erro
}
}
-func cancelRepositoryTransfers(ctx context.Context, sender, recipient *user_model.User) error {
+func cancelRepositoryTransfers(ctx context.Context, doer, sender, recipient *user_model.User) error {
transfers, err := repo_model.GetPendingRepositoryTransfers(ctx, &repo_model.PendingRepositoryTransferOptions{
SenderID: sender.ID,
RecipientID: recipient.ID,
@@ -202,12 +202,7 @@ func cancelRepositoryTransfers(ctx context.Context, sender, recipient *user_mode
}
for _, transfer := range transfers {
- repo, err := repo_model.GetRepositoryByID(ctx, transfer.RepoID)
- if err != nil {
- return err
- }
-
- if err := repo_service.CancelRepositoryTransfer(ctx, repo); err != nil {
+ if err := repo_service.CancelRepositoryTransfer(ctx, transfer, doer); err != nil {
return err
}
}
diff --git a/services/user/update.go b/services/user/update.go
index 4a39f4f783..d7354542bf 100644
--- a/services/user/update.go
+++ b/services/user/update.go
@@ -15,6 +15,26 @@ import (
"code.gitea.io/gitea/modules/structs"
)
+type UpdateOptionField[T any] struct {
+ FieldValue T
+ FromSync bool
+}
+
+func UpdateOptionFieldFromValue[T any](value T) optional.Option[UpdateOptionField[T]] {
+ return optional.Some(UpdateOptionField[T]{FieldValue: value})
+}
+
+func UpdateOptionFieldFromSync[T any](value T) optional.Option[UpdateOptionField[T]] {
+ return optional.Some(UpdateOptionField[T]{FieldValue: value, FromSync: true})
+}
+
+func UpdateOptionFieldFromPtr[T any](value *T) optional.Option[UpdateOptionField[T]] {
+ if value == nil {
+ return optional.None[UpdateOptionField[T]]()
+ }
+ return UpdateOptionFieldFromValue(*value)
+}
+
type UpdateOptions struct {
KeepEmailPrivate optional.Option[bool]
FullName optional.Option[string]
@@ -32,7 +52,7 @@ type UpdateOptions struct {
DiffViewStyle optional.Option[string]
AllowCreateOrganization optional.Option[bool]
IsActive optional.Option[bool]
- IsAdmin optional.Option[bool]
+ IsAdmin optional.Option[UpdateOptionField[bool]]
EmailNotificationsPreference optional.Option[string]
SetLastLogin bool
RepoAdminChangeTeamAccess optional.Option[bool]
@@ -111,13 +131,18 @@ func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) er
cols = append(cols, "is_restricted")
}
if opts.IsAdmin.Has() {
- if !opts.IsAdmin.Value() && user_model.IsLastAdminUser(ctx, u) {
- return user_model.ErrDeleteLastAdminUser{UID: u.ID}
+ if opts.IsAdmin.Value().FieldValue /* true */ {
+ u.IsAdmin = opts.IsAdmin.Value().FieldValue // set IsAdmin=true
+ cols = append(cols, "is_admin")
+ } else if !user_model.IsLastAdminUser(ctx, u) /* not the last admin */ {
+ u.IsAdmin = opts.IsAdmin.Value().FieldValue // it's safe to change it from false to true (not the last admin)
+ cols = append(cols, "is_admin")
+ } else /* IsAdmin=false but this is the last admin user */ { //nolint:gocritic // make it easier to read
+ if !opts.IsAdmin.Value().FromSync {
+ return user_model.ErrDeleteLastAdminUser{UID: u.ID}
+ }
+ // else: syncing from external-source, this user is the last admin, so skip the "IsAdmin=false" change
}
-
- u.IsAdmin = opts.IsAdmin.Value()
-
- cols = append(cols, "is_admin")
}
if opts.Visibility.Has() {
diff --git a/services/user/update_test.go b/services/user/update_test.go
index fc24a6c212..27513e8040 100644
--- a/services/user/update_test.go
+++ b/services/user/update_test.go
@@ -22,7 +22,11 @@ func TestUpdateUser(t *testing.T) {
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.Error(t, UpdateUser(db.DefaultContext, admin, &UpdateOptions{
- IsAdmin: optional.Some(false),
+ IsAdmin: UpdateOptionFieldFromValue(false),
+ }))
+
+ assert.NoError(t, UpdateUser(db.DefaultContext, admin, &UpdateOptions{
+ IsAdmin: UpdateOptionFieldFromSync(false),
}))
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 28})
@@ -38,7 +42,7 @@ func TestUpdateUser(t *testing.T) {
MaxRepoCreation: optional.Some(10),
IsRestricted: optional.Some(true),
IsActive: optional.Some(false),
- IsAdmin: optional.Some(true),
+ IsAdmin: UpdateOptionFieldFromValue(true),
Visibility: optional.Some(structs.VisibleTypePrivate),
KeepActivityPrivate: optional.Some(true),
Language: optional.Some("lang"),
@@ -60,7 +64,7 @@ func TestUpdateUser(t *testing.T) {
assert.Equal(t, opts.MaxRepoCreation.Value(), user.MaxRepoCreation)
assert.Equal(t, opts.IsRestricted.Value(), user.IsRestricted)
assert.Equal(t, opts.IsActive.Value(), user.IsActive)
- assert.Equal(t, opts.IsAdmin.Value(), user.IsAdmin)
+ assert.Equal(t, opts.IsAdmin.Value().FieldValue, user.IsAdmin)
assert.Equal(t, opts.Visibility.Value(), user.Visibility)
assert.Equal(t, opts.KeepActivityPrivate.Value(), user.KeepActivityPrivate)
assert.Equal(t, opts.Language.Value(), user.Language)
@@ -80,7 +84,7 @@ func TestUpdateUser(t *testing.T) {
assert.Equal(t, opts.MaxRepoCreation.Value(), user.MaxRepoCreation)
assert.Equal(t, opts.IsRestricted.Value(), user.IsRestricted)
assert.Equal(t, opts.IsActive.Value(), user.IsActive)
- assert.Equal(t, opts.IsAdmin.Value(), user.IsAdmin)
+ assert.Equal(t, opts.IsAdmin.Value().FieldValue, user.IsAdmin)
assert.Equal(t, opts.Visibility.Value(), user.Visibility)
assert.Equal(t, opts.KeepActivityPrivate.Value(), user.KeepActivityPrivate)
assert.Equal(t, opts.Language.Value(), user.Language)
diff --git a/services/user/user.go b/services/user/user.go
index 1aeebff142..c7252430de 100644
--- a/services/user/user.go
+++ b/services/user/user.go
@@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
+ "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/agit"
asymkey_service "code.gitea.io/gitea/services/asymkey"
@@ -177,8 +178,8 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
PageSize: repo_model.RepositoryListDefaultPageSize,
Page: 1,
},
- UserID: u.ID,
- IncludePrivate: true,
+ UserID: u.ID,
+ IncludeVisibility: structs.VisibleTypePrivate,
})
if err != nil {
return fmt.Errorf("unable to find org list for %s[%d]. Error: %w", u.Name, u.ID, err)
diff --git a/services/user/user_test.go b/services/user/user_test.go
index 162a735cd4..28a0df8628 100644
--- a/services/user/user_test.go
+++ b/services/user/user_test.go
@@ -150,7 +150,7 @@ func TestRenameUser(t *testing.T) {
redirectUID, err := user_model.LookupUserRedirect(db.DefaultContext, oldUsername)
assert.NoError(t, err)
- assert.EqualValues(t, user.ID, redirectUID)
+ assert.Equal(t, user.ID, redirectUID)
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, OwnerName: user.Name})
})
diff --git a/services/versioned_migration/migration.go b/services/versioned_migration/migration.go
new file mode 100644
index 0000000000..b66d853531
--- /dev/null
+++ b/services/versioned_migration/migration.go
@@ -0,0 +1,24 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package versioned_migration
+
+import (
+ "context"
+
+ "code.gitea.io/gitea/models/migrations"
+ "code.gitea.io/gitea/modules/globallock"
+
+ "xorm.io/xorm"
+)
+
+func Migrate(ctx context.Context, x *xorm.Engine) error {
+ // only one instance can do the migration at the same time if there are multiple instances
+ release, err := globallock.Lock(ctx, "gitea_versioned_migration")
+ if err != nil {
+ return err
+ }
+ defer release()
+
+ return migrations.Migrate(x)
+}
diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go
index 4707602cdf..e8e6ed19c1 100644
--- a/services/webhook/deliver.go
+++ b/services/webhook/deliver.go
@@ -10,6 +10,7 @@ import (
"crypto/sha256"
"crypto/tls"
"encoding/hex"
+ "errors"
"fmt"
"io"
"net/http"
@@ -18,6 +19,7 @@ import (
"sync"
"time"
+ user_model "code.gitea.io/gitea/models/user"
webhook_model "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/hostmatcher"
@@ -40,7 +42,7 @@ func newDefaultRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook
case http.MethodPost:
switch w.ContentType {
case webhook_model.ContentTypeJSON:
- req, err = http.NewRequest("POST", w.URL, strings.NewReader(t.PayloadContent))
+ req, err = http.NewRequest(http.MethodPost, w.URL, strings.NewReader(t.PayloadContent))
if err != nil {
return nil, nil, err
}
@@ -51,7 +53,7 @@ func newDefaultRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook
"payload": []string{t.PayloadContent},
}
- req, err = http.NewRequest("POST", w.URL, strings.NewReader(forms.Encode()))
+ req, err = http.NewRequest(http.MethodPost, w.URL, strings.NewReader(forms.Encode()))
if err != nil {
return nil, nil, err
}
@@ -68,7 +70,7 @@ func newDefaultRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook
vals := u.Query()
vals["payload"] = []string{t.PayloadContent}
u.RawQuery = vals.Encode()
- req, err = http.NewRequest("GET", u.String(), nil)
+ req, err = http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return nil, nil, err
}
@@ -80,7 +82,7 @@ func newDefaultRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook
return nil, nil, err
}
url := fmt.Sprintf("%s/%s", w.URL, url.PathEscape(txnID))
- req, err = http.NewRequest("PUT", url, strings.NewReader(t.PayloadContent))
+ req, err = http.NewRequest(http.MethodPut, url, strings.NewReader(t.PayloadContent))
if err != nil {
return nil, nil, err
}
@@ -92,10 +94,10 @@ func newDefaultRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook
}
body = []byte(t.PayloadContent)
- return req, body, addDefaultHeaders(req, []byte(w.Secret), t, body)
+ return req, body, addDefaultHeaders(req, []byte(w.Secret), w, t, body)
}
-func addDefaultHeaders(req *http.Request, secret []byte, t *webhook_model.HookTask, payloadContent []byte) error {
+func addDefaultHeaders(req *http.Request, secret []byte, w *webhook_model.Webhook, t *webhook_model.HookTask, payloadContent []byte) error {
var signatureSHA1 string
var signatureSHA256 string
if len(secret) > 0 {
@@ -112,10 +114,27 @@ func addDefaultHeaders(req *http.Request, secret []byte, t *webhook_model.HookTa
event := t.EventType.Event()
eventType := string(t.EventType)
+ targetType := "default"
+ if w.IsSystemWebhook {
+ targetType = "system"
+ } else if w.RepoID != 0 {
+ targetType = "repository"
+ } else if w.OwnerID != 0 {
+ owner, err := user_model.GetUserByID(req.Context(), w.OwnerID)
+ if owner != nil && err == nil {
+ if owner.IsOrganization() {
+ targetType = "organization"
+ } else {
+ targetType = "user"
+ }
+ }
+ }
+
req.Header.Add("X-Gitea-Delivery", t.UUID)
req.Header.Add("X-Gitea-Event", event)
req.Header.Add("X-Gitea-Event-Type", eventType)
req.Header.Add("X-Gitea-Signature", signatureSHA256)
+ req.Header.Add("X-Gitea-Hook-Installation-Target-Type", targetType)
req.Header.Add("X-Gogs-Delivery", t.UUID)
req.Header.Add("X-Gogs-Event", event)
req.Header.Add("X-Gogs-Event-Type", eventType)
@@ -125,6 +144,7 @@ func addDefaultHeaders(req *http.Request, secret []byte, t *webhook_model.HookTa
req.Header["X-GitHub-Delivery"] = []string{t.UUID}
req.Header["X-GitHub-Event"] = []string{event}
req.Header["X-GitHub-Event-Type"] = []string{eventType}
+ req.Header["X-GitHub-Hook-Installation-Target-Type"] = []string{targetType}
return nil
}
@@ -309,7 +329,7 @@ func Init() error {
hookQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "webhook_sender", handler)
if hookQueue == nil {
- return fmt.Errorf("unable to create webhook_sender queue")
+ return errors.New("unable to create webhook_sender queue")
}
go graceful.GetManager().RunWithCancel(hookQueue)
diff --git a/services/webhook/deliver_test.go b/services/webhook/deliver_test.go
index d0cfc1598f..1d32d7b772 100644
--- a/services/webhook/deliver_test.go
+++ b/services/webhook/deliver_test.go
@@ -4,7 +4,6 @@
package webhook
import (
- "context"
"io"
"net/http"
"net/http/httptest"
@@ -65,7 +64,7 @@ func TestWebhookProxy(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.req, func(t *testing.T) {
- req, err := http.NewRequest("POST", tt.req, nil)
+ req, err := http.NewRequest(http.MethodPost, tt.req, nil)
require.NoError(t, err)
u, err := webhookProxy(allowedHostMatcher)(req)
@@ -92,7 +91,7 @@ func TestWebhookDeliverAuthorizationHeader(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/webhook", r.URL.Path)
assert.Equal(t, "Bearer s3cr3t-t0ken", r.Header.Get("Authorization"))
- w.WriteHeader(200)
+ w.WriteHeader(http.StatusOK)
done <- struct{}{}
}))
t.Cleanup(s.Close)
@@ -118,7 +117,7 @@ func TestWebhookDeliverAuthorizationHeader(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, hookTask)
- assert.NoError(t, Deliver(context.Background(), hookTask))
+ assert.NoError(t, Deliver(t.Context(), hookTask))
select {
case <-done:
case <-time.After(5 * time.Second):
@@ -139,7 +138,7 @@ func TestWebhookDeliverHookTask(t *testing.T) {
case "/webhook/66d222a5d6349e1311f551e50722d837e30fce98":
// Version 1
assert.Equal(t, "push", r.Header.Get("X-GitHub-Event"))
- assert.Equal(t, "", r.Header.Get("Content-Type"))
+ assert.Empty(t, r.Header.Get("Content-Type"))
body, err := io.ReadAll(r.Body)
assert.NoError(t, err)
assert.Equal(t, `{"data": 42}`, string(body))
@@ -153,11 +152,11 @@ func TestWebhookDeliverHookTask(t *testing.T) {
assert.Len(t, body, 2147)
default:
- w.WriteHeader(404)
+ w.WriteHeader(http.StatusNotFound)
t.Fatalf("unexpected url path %s", r.URL.Path)
return
}
- w.WriteHeader(200)
+ w.WriteHeader(http.StatusOK)
done <- struct{}{}
}))
t.Cleanup(s.Close)
@@ -185,7 +184,7 @@ func TestWebhookDeliverHookTask(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, hookTask)
- assert.NoError(t, Deliver(context.Background(), hookTask))
+ assert.NoError(t, Deliver(t.Context(), hookTask))
select {
case <-done:
case <-time.After(5 * time.Second):
@@ -211,7 +210,7 @@ func TestWebhookDeliverHookTask(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, hookTask)
- assert.NoError(t, Deliver(context.Background(), hookTask))
+ assert.NoError(t, Deliver(t.Context(), hookTask))
select {
case <-done:
case <-time.After(5 * time.Second):
@@ -280,7 +279,7 @@ func TestWebhookDeliverSpecificTypes(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, hookTask)
- assert.NoError(t, Deliver(context.Background(), hookTask))
+ assert.NoError(t, Deliver(t.Context(), hookTask))
select {
case gotBody := <-cases[typ].gotBody:
diff --git a/services/webhook/dingtalk.go b/services/webhook/dingtalk.go
index e382f5a9df..5bbc610fe5 100644
--- a/services/webhook/dingtalk.go
+++ b/services/webhook/dingtalk.go
@@ -30,7 +30,7 @@ func (dc dingtalkConvertor) Create(p *api.CreatePayload) (DingtalkPayload, error
refName := git.RefName(p.Ref).ShortName()
title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
- return createDingtalkPayload(title, title, fmt.Sprintf("view ref %s", refName), p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName)), nil
+ return createDingtalkPayload(title, title, "view ref "+refName, p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName)), nil
}
// Delete implements PayloadConvertor Delete method
@@ -39,14 +39,14 @@ func (dc dingtalkConvertor) Delete(p *api.DeletePayload) (DingtalkPayload, error
refName := git.RefName(p.Ref).ShortName()
title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
- return createDingtalkPayload(title, title, fmt.Sprintf("view ref %s", refName), p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName)), nil
+ return createDingtalkPayload(title, title, "view ref "+refName, p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName)), nil
}
// Fork implements PayloadConvertor Fork method
func (dc dingtalkConvertor) Fork(p *api.ForkPayload) (DingtalkPayload, error) {
title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName)
- return createDingtalkPayload(title, title, fmt.Sprintf("view forked repo %s", p.Repo.FullName), p.Repo.HTMLURL), nil
+ return createDingtalkPayload(title, title, "view forked repo "+p.Repo.FullName, p.Repo.HTMLURL), nil
}
// Push implements PayloadConvertor Push method
@@ -170,6 +170,24 @@ func (dc dingtalkConvertor) Package(p *api.PackagePayload) (DingtalkPayload, err
return createDingtalkPayload(text, text, "view package", p.Package.HTMLURL), nil
}
+func (dc dingtalkConvertor) Status(p *api.CommitStatusPayload) (DingtalkPayload, error) {
+ text, _ := getStatusPayloadInfo(p, noneLinkFormatter, true)
+
+ return createDingtalkPayload(text, text, "Status Changed", p.TargetURL), nil
+}
+
+func (dingtalkConvertor) WorkflowRun(p *api.WorkflowRunPayload) (DingtalkPayload, error) {
+ text, _ := getWorkflowRunPayloadInfo(p, noneLinkFormatter, true)
+
+ return createDingtalkPayload(text, text, "Workflow Run", p.WorkflowRun.HTMLURL), nil
+}
+
+func (dingtalkConvertor) WorkflowJob(p *api.WorkflowJobPayload) (DingtalkPayload, error) {
+ text, _ := getWorkflowJobPayloadInfo(p, noneLinkFormatter, true)
+
+ return createDingtalkPayload(text, text, "Workflow Job", p.WorkflowJob.HTMLURL), nil
+}
+
func createDingtalkPayload(title, text, singleTitle, singleURL string) DingtalkPayload {
return DingtalkPayload{
MsgType: "actionCard",
@@ -190,3 +208,7 @@ func newDingtalkRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_
var pc payloadConvertor[DingtalkPayload] = dingtalkConvertor{}
return newJSONRequest(pc, w, t, true)
}
+
+func init() {
+ RegisterWebhookRequester(webhook_module.DINGTALK, newDingtalkRequest)
+}
diff --git a/services/webhook/dingtalk_test.go b/services/webhook/dingtalk_test.go
index 25f47347d0..763d23048a 100644
--- a/services/webhook/dingtalk_test.go
+++ b/services/webhook/dingtalk_test.go
@@ -4,7 +4,6 @@
package webhook
import (
- "context"
"net/url"
"testing"
@@ -236,7 +235,7 @@ func TestDingTalkJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := newDingtalkRequest(context.Background(), hook, task)
+ req, reqBody, err := newDingtalkRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
diff --git a/services/webhook/discord.go b/services/webhook/discord.go
index c562d98168..0426964181 100644
--- a/services/webhook/discord.go
+++ b/services/webhook/discord.go
@@ -101,6 +101,13 @@ var (
redColor = color("ff3232")
)
+// https://discord.com/developers/docs/resources/message#embed-object-embed-limits
+// Discord has some limits in place for the embeds.
+// According to some tests, there is no consistent limit for different character sets.
+// For example: 4096 ASCII letters are allowed, but only 2490 emoji characters are allowed.
+// To keep it simple, we currently truncate at 2000.
+const discordDescriptionCharactersLimit = 2000
+
type discordConvertor struct {
Username string
AvatarURL string
@@ -265,6 +272,24 @@ func (d discordConvertor) Package(p *api.PackagePayload) (DiscordPayload, error)
return d.createPayload(p.Sender, text, "", p.Package.HTMLURL, color), nil
}
+func (d discordConvertor) Status(p *api.CommitStatusPayload) (DiscordPayload, error) {
+ text, color := getStatusPayloadInfo(p, noneLinkFormatter, false)
+
+ return d.createPayload(p.Sender, text, "", p.TargetURL, color), nil
+}
+
+func (d discordConvertor) WorkflowRun(p *api.WorkflowRunPayload) (DiscordPayload, error) {
+ text, color := getWorkflowRunPayloadInfo(p, noneLinkFormatter, false)
+
+ return d.createPayload(p.Sender, text, "", p.WorkflowRun.HTMLURL, color), nil
+}
+
+func (d discordConvertor) WorkflowJob(p *api.WorkflowJobPayload) (DiscordPayload, error) {
+ text, color := getWorkflowJobPayloadInfo(p, noneLinkFormatter, false)
+
+ return d.createPayload(p.Sender, text, "", p.WorkflowJob.HTMLURL, color), nil
+}
+
func newDiscordRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
meta := &DiscordMeta{}
if err := json.Unmarshal([]byte(w.Meta), meta); err != nil {
@@ -277,12 +302,16 @@ func newDiscordRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_m
return newJSONRequest(pc, w, t, true)
}
+func init() {
+ RegisterWebhookRequester(webhook_module.DISCORD, newDiscordRequest)
+}
+
func parseHookPullRequestEventType(event webhook_module.HookEventType) (string, error) {
switch event {
case webhook_module.HookEventPullRequestReviewApproved:
return "approved", nil
case webhook_module.HookEventPullRequestReviewRejected:
- return "rejected", nil
+ return "requested changes", nil
case webhook_module.HookEventPullRequestReviewComment:
return "comment", nil
default:
@@ -297,7 +326,7 @@ func (d discordConvertor) createPayload(s *api.User, title, text, url string, co
Embeds: []DiscordEmbed{
{
Title: title,
- Description: text,
+ Description: util.TruncateRunes(text, discordDescriptionCharactersLimit),
URL: url,
Color: color,
Author: DiscordEmbedAuthor{
diff --git a/services/webhook/discord_test.go b/services/webhook/discord_test.go
index 36b99d452e..7f503e3374 100644
--- a/services/webhook/discord_test.go
+++ b/services/webhook/discord_test.go
@@ -4,7 +4,6 @@
package webhook
import (
- "context"
"testing"
webhook_model "code.gitea.io/gitea/models/webhook"
@@ -303,7 +302,7 @@ func TestDiscordJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := newDiscordRequest(context.Background(), hook, task)
+ req, reqBody, err := newDiscordRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
diff --git a/services/webhook/feishu.go b/services/webhook/feishu.go
index 7ca7d1cf5f..b6ee80c44c 100644
--- a/services/webhook/feishu.go
+++ b/services/webhook/feishu.go
@@ -5,9 +5,13 @@ package webhook
import (
"context"
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/base64"
"fmt"
"net/http"
"strings"
+ "time"
webhook_model "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/git"
@@ -16,10 +20,12 @@ import (
)
type (
- // FeishuPayload represents
+ // FeishuPayload represents the payload for Feishu webhook
FeishuPayload struct {
- MsgType string `json:"msg_type"` // text / post / image / share_chat / interactive / file /audio / media
- Content struct {
+ Timestamp int64 `json:"timestamp,omitempty"` // Unix timestamp for signature verification
+ Sign string `json:"sign,omitempty"` // Signature for verification
+ MsgType string `json:"msg_type"` // text / post / image / share_chat / interactive / file /audio / media
+ Content struct {
Text string `json:"text"`
} `json:"content"`
}
@@ -166,7 +172,49 @@ func (fc feishuConvertor) Package(p *api.PackagePayload) (FeishuPayload, error)
return newFeishuTextPayload(text), nil
}
+func (fc feishuConvertor) Status(p *api.CommitStatusPayload) (FeishuPayload, error) {
+ text, _ := getStatusPayloadInfo(p, noneLinkFormatter, true)
+
+ return newFeishuTextPayload(text), nil
+}
+
+func (feishuConvertor) WorkflowRun(p *api.WorkflowRunPayload) (FeishuPayload, error) {
+ text, _ := getWorkflowRunPayloadInfo(p, noneLinkFormatter, true)
+
+ return newFeishuTextPayload(text), nil
+}
+
+func (feishuConvertor) WorkflowJob(p *api.WorkflowJobPayload) (FeishuPayload, error) {
+ text, _ := getWorkflowJobPayloadInfo(p, noneLinkFormatter, true)
+
+ return newFeishuTextPayload(text), nil
+}
+
+// feishuGenSign generates a signature for Feishu webhook
+// https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot
+func feishuGenSign(secret string, timestamp int64) string {
+ // key="{timestamp}\n{secret}", then hmac-sha256, then base64 encode
+ stringToSign := fmt.Sprintf("%d\n%s", timestamp, secret)
+ h := hmac.New(sha256.New, []byte(stringToSign))
+ return base64.StdEncoding.EncodeToString(h.Sum(nil))
+}
+
func newFeishuRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
- var pc payloadConvertor[FeishuPayload] = feishuConvertor{}
- return newJSONRequest(pc, w, t, true)
+ payload, err := newPayload(feishuConvertor{}, []byte(t.PayloadContent), t.EventType)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Add timestamp and signature if secret is provided
+ if w.Secret != "" {
+ timestamp := time.Now().Unix()
+ payload.Timestamp = timestamp
+ payload.Sign = feishuGenSign(w.Secret, timestamp)
+ }
+
+ return prepareJSONRequest(payload, w, t, false /* no default headers */)
+}
+
+func init() {
+ RegisterWebhookRequester(webhook_module.FEISHU, newFeishuRequest)
}
diff --git a/services/webhook/feishu_test.go b/services/webhook/feishu_test.go
index ef18333fd4..7e200ea132 100644
--- a/services/webhook/feishu_test.go
+++ b/services/webhook/feishu_test.go
@@ -4,7 +4,6 @@
package webhook
import (
- "context"
"testing"
webhook_model "code.gitea.io/gitea/models/webhook"
@@ -169,6 +168,7 @@ func TestFeishuJSONPayload(t *testing.T) {
URL: "https://feishu.example.com/",
Meta: `{}`,
HTTPMethod: "POST",
+ Secret: "secret",
}
task := &webhook_model.HookTask{
HookID: hook.ID,
@@ -177,17 +177,20 @@ func TestFeishuJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := newFeishuRequest(context.Background(), hook, task)
+ req, reqBody, err := newFeishuRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
assert.Equal(t, "POST", req.Method)
assert.Equal(t, "https://feishu.example.com/", req.URL.String())
- assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256"))
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
var body FeishuPayload
err = json.NewDecoder(req.Body).Decode(&body)
assert.NoError(t, err)
assert.Equal(t, "[test/repo:test] \r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", body.Content.Text)
+ assert.Equal(t, feishuGenSign(hook.Secret, body.Timestamp), body.Sign)
+
+ // a separate sign test, the result is generated by official python code, so the algo must be correct
+ assert.Equal(t, "rWZ84lcag1x9aBFhn1gtV4ZN+4gme3pilfQNMk86vKg=", feishuGenSign("a", 1))
}
diff --git a/services/webhook/general.go b/services/webhook/general.go
index dde43bb349..be457e46f5 100644
--- a/services/webhook/general.go
+++ b/services/webhook/general.go
@@ -9,7 +9,9 @@ import (
"net/url"
"strings"
+ user_model "code.gitea.io/gitea/models/user"
webhook_model "code.gitea.io/gitea/models/webhook"
+ "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
@@ -37,19 +39,20 @@ func getPullRequestInfo(p *api.PullRequestPayload) (title, link, by, operator, o
for i, user := range assignList {
assignStringList[i] = user.UserName
}
- if p.Action == api.HookIssueAssigned {
+ switch p.Action {
+ case api.HookIssueAssigned:
operateResult = fmt.Sprintf("%s assign this to %s", p.Sender.UserName, assignList[len(assignList)-1].UserName)
- } else if p.Action == api.HookIssueUnassigned {
- operateResult = fmt.Sprintf("%s unassigned this for someone", p.Sender.UserName)
- } else if p.Action == api.HookIssueMilestoned {
+ case api.HookIssueUnassigned:
+ operateResult = p.Sender.UserName + " unassigned this for someone"
+ case api.HookIssueMilestoned:
operateResult = fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID)
}
link = p.PullRequest.HTMLURL
- by = fmt.Sprintf("PullRequest by %s", p.PullRequest.Poster.UserName)
+ by = "PullRequest by " + p.PullRequest.Poster.UserName
if len(assignStringList) > 0 {
- assignees = fmt.Sprintf("Assignees: %s", strings.Join(assignStringList, ", "))
+ assignees = "Assignees: " + strings.Join(assignStringList, ", ")
}
- operator = fmt.Sprintf("Operator: %s", p.Sender.UserName)
+ operator = "Operator: " + p.Sender.UserName
return title, link, by, operator, operateResult, assignees
}
@@ -62,19 +65,20 @@ func getIssuesInfo(p *api.IssuePayload) (issueTitle, link, by, operator, operate
for i, user := range assignList {
assignStringList[i] = user.UserName
}
- if p.Action == api.HookIssueAssigned {
+ switch p.Action {
+ case api.HookIssueAssigned:
operateResult = fmt.Sprintf("%s assign this to %s", p.Sender.UserName, assignList[len(assignList)-1].UserName)
- } else if p.Action == api.HookIssueUnassigned {
- operateResult = fmt.Sprintf("%s unassigned this for someone", p.Sender.UserName)
- } else if p.Action == api.HookIssueMilestoned {
+ case api.HookIssueUnassigned:
+ operateResult = p.Sender.UserName + " unassigned this for someone"
+ case api.HookIssueMilestoned:
operateResult = fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID)
}
link = p.Issue.HTMLURL
- by = fmt.Sprintf("Issue by %s", p.Issue.Poster.UserName)
+ by = "Issue by " + p.Issue.Poster.UserName
if len(assignStringList) > 0 {
- assignees = fmt.Sprintf("Assignees: %s", strings.Join(assignStringList, ", "))
+ assignees = "Assignees: " + strings.Join(assignStringList, ", ")
}
- operator = fmt.Sprintf("Operator: %s", p.Sender.UserName)
+ operator = "Operator: " + p.Sender.UserName
return issueTitle, link, by, operator, operateResult, assignees
}
@@ -83,11 +87,11 @@ func getIssuesCommentInfo(p *api.IssueCommentPayload) (title, link, by, operator
title = fmt.Sprintf("[Comment-%s #%d]: %s\n%s", p.Repository.FullName, p.Issue.Index, p.Action, p.Issue.Title)
link = p.Issue.HTMLURL
if p.IsPull {
- by = fmt.Sprintf("PullRequest by %s", p.Issue.Poster.UserName)
+ by = "PullRequest by " + p.Issue.Poster.UserName
} else {
- by = fmt.Sprintf("Issue by %s", p.Issue.Poster.UserName)
+ by = "Issue by " + p.Issue.Poster.UserName
}
- operator = fmt.Sprintf("Operator: %s", p.Sender.UserName)
+ operator = "Operator: " + p.Sender.UserName
return title, link, by, operator
}
@@ -131,7 +135,7 @@ func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, with
text = fmt.Sprintf("[%s] Issue milestone cleared: %s", repoLink, titleLink)
}
if withSender {
- text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
+ text += " by " + linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)
}
if p.Action == api.HookIssueOpened || p.Action == api.HookIssueEdited {
@@ -196,7 +200,7 @@ func getPullRequestPayloadInfo(p *api.PullRequestPayload, linkFormatter linkForm
text = fmt.Sprintf("[%s] Pull request review request removed: %s", repoLink, titleLink)
}
if withSender {
- text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName))
+ text += " by " + linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
}
return text, issueTitle, extraMarkdown, color
@@ -218,7 +222,7 @@ func getReleasePayloadInfo(p *api.ReleasePayload, linkFormatter linkFormatter, w
color = redColor
}
if withSender {
- text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
+ text += " by " + linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)
}
return text, color
@@ -247,7 +251,7 @@ func getWikiPayloadInfo(p *api.WikiPayload, linkFormatter linkFormatter, withSen
}
if withSender {
- text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
+ text += " by " + linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)
}
return text, color, pageLink
@@ -283,7 +287,7 @@ func getIssueCommentPayloadInfo(p *api.IssueCommentPayload, linkFormatter linkFo
color = redColor
}
if withSender {
- text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
+ text += " by " + linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)
}
return text, issueTitle, color
@@ -294,14 +298,92 @@ func getPackagePayloadInfo(p *api.PackagePayload, linkFormatter linkFormatter, w
switch p.Action {
case api.HookPackageCreated:
- text = fmt.Sprintf("Package created: %s", refLink)
+ text = "Package created: " + refLink
color = greenColor
case api.HookPackageDeleted:
- text = fmt.Sprintf("Package deleted: %s", refLink)
+ text = "Package deleted: " + refLink
color = redColor
}
if withSender {
- text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
+ text += " by " + linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)
+ }
+
+ return text, color
+}
+
+func getStatusPayloadInfo(p *api.CommitStatusPayload, linkFormatter linkFormatter, withSender bool) (text string, color int) {
+ refLink := linkFormatter(p.TargetURL, fmt.Sprintf("%s [%s]", p.Context, base.ShortSha(p.SHA)))
+
+ text = fmt.Sprintf("Commit Status changed: %s - %s", refLink, p.Description)
+ color = greenColor
+ if withSender {
+ if user_model.IsGiteaActionsUserName(p.Sender.UserName) {
+ text += " by " + p.Sender.FullName
+ } else {
+ text += " by " + linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)
+ }
+ }
+
+ return text, color
+}
+
+func getWorkflowRunPayloadInfo(p *api.WorkflowRunPayload, linkFormatter linkFormatter, withSender bool) (text string, color int) {
+ description := p.WorkflowRun.Conclusion
+ if description == "" {
+ description = p.WorkflowRun.Status
+ }
+ refLink := linkFormatter(p.WorkflowRun.HTMLURL, fmt.Sprintf("%s(#%d)", p.WorkflowRun.DisplayTitle, p.WorkflowRun.ID)+"["+base.ShortSha(p.WorkflowRun.HeadSha)+"]:"+description)
+
+ text = fmt.Sprintf("Workflow Run %s: %s", p.Action, refLink)
+ switch description {
+ case "waiting":
+ color = orangeColor
+ case "queued":
+ color = orangeColorLight
+ case "success":
+ color = greenColor
+ case "failure":
+ color = redColor
+ case "cancelled":
+ color = yellowColor
+ case "skipped":
+ color = purpleColor
+ default:
+ color = greyColor
+ }
+ if withSender {
+ text += " by " + linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)
+ }
+
+ return text, color
+}
+
+func getWorkflowJobPayloadInfo(p *api.WorkflowJobPayload, linkFormatter linkFormatter, withSender bool) (text string, color int) {
+ description := p.WorkflowJob.Conclusion
+ if description == "" {
+ description = p.WorkflowJob.Status
+ }
+ refLink := linkFormatter(p.WorkflowJob.HTMLURL, fmt.Sprintf("%s(#%d)", p.WorkflowJob.Name, p.WorkflowJob.RunID)+"["+base.ShortSha(p.WorkflowJob.HeadSha)+"]:"+description)
+
+ text = fmt.Sprintf("Workflow Job %s: %s", p.Action, refLink)
+ switch description {
+ case "waiting":
+ color = orangeColor
+ case "queued":
+ color = orangeColorLight
+ case "success":
+ color = greenColor
+ case "failure":
+ color = redColor
+ case "cancelled":
+ color = yellowColor
+ case "skipped":
+ color = purpleColor
+ default:
+ color = greyColor
+ }
+ if withSender {
+ text += " by " + linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)
}
return text, color
diff --git a/services/webhook/general_test.go b/services/webhook/general_test.go
index ef1ec7f324..ec735d785a 100644
--- a/services/webhook/general_test.go
+++ b/services/webhook/general_test.go
@@ -319,8 +319,8 @@ func packageTestPayload() *api.PackagePayload {
AvatarURL: "http://localhost:3000/user1/avatar",
},
Repository: nil,
- Organization: &api.User{
- UserName: "org1",
+ Organization: &api.Organization{
+ Name: "org1",
AvatarURL: "http://localhost:3000/org1/avatar",
},
Package: &api.Package{
diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go
index 5e9f808d8b..3e9163f78c 100644
--- a/services/webhook/matrix.go
+++ b/services/webhook/matrix.go
@@ -15,6 +15,7 @@ import (
"strings"
webhook_model "code.gitea.io/gitea/models/webhook"
+ "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
@@ -24,6 +25,10 @@ import (
webhook_module "code.gitea.io/gitea/modules/webhook"
)
+func init() {
+ RegisterWebhookRequester(webhook_module.MATRIX, newMatrixRequest)
+}
+
func newMatrixRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
meta := &MatrixMeta{}
if err := json.Unmarshal([]byte(w.Meta), meta); err != nil {
@@ -52,7 +57,7 @@ func newMatrixRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_mo
}
req.Header.Set("Content-Type", "application/json")
- return req, body, addDefaultHeaders(req, []byte(w.Secret), t, body) // likely useless, but has always been sent historially
+ return req, body, addDefaultHeaders(req, []byte(w.Secret), w, t, body) // likely useless, but has always been sent historially
}
const matrixPayloadSizeLimit = 1024 * 64
@@ -240,6 +245,25 @@ func (m matrixConvertor) Package(p *api.PackagePayload) (MatrixPayload, error) {
return m.newPayload(text)
}
+func (m matrixConvertor) Status(p *api.CommitStatusPayload) (MatrixPayload, error) {
+ refLink := htmlLinkFormatter(p.TargetURL, fmt.Sprintf("%s [%s]", p.Context, base.ShortSha(p.SHA)))
+ text := fmt.Sprintf("Commit Status changed: %s - %s", refLink, p.Description)
+
+ return m.newPayload(text)
+}
+
+func (m matrixConvertor) WorkflowRun(p *api.WorkflowRunPayload) (MatrixPayload, error) {
+ text, _ := getWorkflowRunPayloadInfo(p, htmlLinkFormatter, true)
+
+ return m.newPayload(text)
+}
+
+func (m matrixConvertor) WorkflowJob(p *api.WorkflowJobPayload) (MatrixPayload, error) {
+ text, _ := getWorkflowJobPayloadInfo(p, htmlLinkFormatter, true)
+
+ return m.newPayload(text)
+}
+
var urlRegex = regexp.MustCompile(`<a [^>]*?href="([^">]*?)">(.*?)</a>`)
func getMessageBody(htmlText string) string {
diff --git a/services/webhook/matrix_test.go b/services/webhook/matrix_test.go
index 058f8e3c5f..d36d93c5a7 100644
--- a/services/webhook/matrix_test.go
+++ b/services/webhook/matrix_test.go
@@ -4,7 +4,6 @@
package webhook
import (
- "context"
"testing"
webhook_model "code.gitea.io/gitea/models/webhook"
@@ -211,7 +210,7 @@ func TestMatrixJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := newMatrixRequest(context.Background(), hook, task)
+ req, reqBody, err := newMatrixRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
diff --git a/services/webhook/msteams.go b/services/webhook/msteams.go
index 7ef96ffa27..450a544b42 100644
--- a/services/webhook/msteams.go
+++ b/services/webhook/msteams.go
@@ -8,6 +8,7 @@ import (
"fmt"
"net/http"
"net/url"
+ "strconv"
"strings"
webhook_model "code.gitea.io/gitea/models/webhook"
@@ -73,7 +74,7 @@ func (m msteamsConvertor) Create(p *api.CreatePayload) (MSTeamsPayload, error) {
"",
p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName),
greenColor,
- &MSTeamsFact{fmt.Sprintf("%s:", p.RefType), refName},
+ &MSTeamsFact{p.RefType + ":", refName},
), nil
}
@@ -90,7 +91,7 @@ func (m msteamsConvertor) Delete(p *api.DeletePayload) (MSTeamsPayload, error) {
"",
p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName),
yellowColor,
- &MSTeamsFact{fmt.Sprintf("%s:", p.RefType), refName},
+ &MSTeamsFact{p.RefType + ":", refName},
), nil
}
@@ -148,7 +149,7 @@ func (m msteamsConvertor) Push(p *api.PushPayload) (MSTeamsPayload, error) {
text,
titleLink,
greenColor,
- &MSTeamsFact{"Commit count:", fmt.Sprintf("%d", p.TotalCommits)},
+ &MSTeamsFact{"Commit count:", strconv.Itoa(p.TotalCommits)},
), nil
}
@@ -163,7 +164,7 @@ func (m msteamsConvertor) Issue(p *api.IssuePayload) (MSTeamsPayload, error) {
extraMarkdown,
p.Issue.HTMLURL,
color,
- &MSTeamsFact{"Issue #:", fmt.Sprintf("%d", p.Issue.ID)},
+ &MSTeamsFact{"Issue #:", strconv.FormatInt(p.Issue.ID, 10)},
), nil
}
@@ -178,7 +179,7 @@ func (m msteamsConvertor) IssueComment(p *api.IssueCommentPayload) (MSTeamsPaylo
p.Comment.Body,
p.Comment.HTMLURL,
color,
- &MSTeamsFact{"Issue #:", fmt.Sprintf("%d", p.Issue.ID)},
+ &MSTeamsFact{"Issue #:", strconv.FormatInt(p.Issue.ID, 10)},
), nil
}
@@ -193,7 +194,7 @@ func (m msteamsConvertor) PullRequest(p *api.PullRequestPayload) (MSTeamsPayload
extraMarkdown,
p.PullRequest.HTMLURL,
color,
- &MSTeamsFact{"Pull request #:", fmt.Sprintf("%d", p.PullRequest.ID)},
+ &MSTeamsFact{"Pull request #:", strconv.FormatInt(p.PullRequest.ID, 10)},
), nil
}
@@ -230,7 +231,7 @@ func (m msteamsConvertor) Review(p *api.PullRequestPayload, event webhook_module
text,
p.PullRequest.HTMLURL,
color,
- &MSTeamsFact{"Pull request #:", fmt.Sprintf("%d", p.PullRequest.ID)},
+ &MSTeamsFact{"Pull request #:", strconv.FormatInt(p.PullRequest.ID, 10)},
), nil
}
@@ -303,6 +304,48 @@ func (m msteamsConvertor) Package(p *api.PackagePayload) (MSTeamsPayload, error)
), nil
}
+func (m msteamsConvertor) Status(p *api.CommitStatusPayload) (MSTeamsPayload, error) {
+ title, color := getStatusPayloadInfo(p, noneLinkFormatter, false)
+
+ return createMSTeamsPayload(
+ p.Repo,
+ p.Sender,
+ title,
+ "",
+ p.TargetURL,
+ color,
+ &MSTeamsFact{"CommitStatus:", p.Context},
+ ), nil
+}
+
+func (msteamsConvertor) WorkflowRun(p *api.WorkflowRunPayload) (MSTeamsPayload, error) {
+ title, color := getWorkflowRunPayloadInfo(p, noneLinkFormatter, false)
+
+ return createMSTeamsPayload(
+ p.Repo,
+ p.Sender,
+ title,
+ "",
+ p.WorkflowRun.HTMLURL,
+ color,
+ &MSTeamsFact{"WorkflowRun:", p.WorkflowRun.DisplayTitle},
+ ), nil
+}
+
+func (msteamsConvertor) WorkflowJob(p *api.WorkflowJobPayload) (MSTeamsPayload, error) {
+ title, color := getWorkflowJobPayloadInfo(p, noneLinkFormatter, false)
+
+ return createMSTeamsPayload(
+ p.Repo,
+ p.Sender,
+ title,
+ "",
+ p.WorkflowJob.HTMLURL,
+ color,
+ &MSTeamsFact{"WorkflowJob:", p.WorkflowJob.Name},
+ ), nil
+}
+
func createMSTeamsPayload(r *api.Repository, s *api.User, title, text, actionTarget string, color int, fact *MSTeamsFact) MSTeamsPayload {
facts := make([]MSTeamsFact, 0, 2)
if r != nil {
@@ -349,3 +392,7 @@ func newMSTeamsRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_m
var pc payloadConvertor[MSTeamsPayload] = msteamsConvertor{}
return newJSONRequest(pc, w, t, true)
}
+
+func init() {
+ RegisterWebhookRequester(webhook_module.MSTEAMS, newMSTeamsRequest)
+}
diff --git a/services/webhook/msteams_test.go b/services/webhook/msteams_test.go
index 01e08b918e..0d98b94bad 100644
--- a/services/webhook/msteams_test.go
+++ b/services/webhook/msteams_test.go
@@ -4,7 +4,6 @@
package webhook
import (
- "context"
"testing"
webhook_model "code.gitea.io/gitea/models/webhook"
@@ -336,7 +335,7 @@ func TestMSTeamsPayload(t *testing.T) {
assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
- assert.Equal(t, "", pl.Sections[0].Text)
+ assert.Empty(t, pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.Sections[0].Facts {
if fact.Name == "Repository:" {
@@ -357,7 +356,7 @@ func TestMSTeamsPayload(t *testing.T) {
assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.Summary)
assert.Len(t, pl.Sections, 1)
assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle)
- assert.Equal(t, "", pl.Sections[0].Text)
+ assert.Empty(t, pl.Sections[0].Text)
assert.Len(t, pl.Sections[0].Facts, 2)
for _, fact := range pl.Sections[0].Facts {
if fact.Name == "Repository:" {
@@ -439,7 +438,7 @@ func TestMSTeamsJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := newMSTeamsRequest(context.Background(), hook, task)
+ req, reqBody, err := newMSTeamsRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go
index a3d5cb34b1..672abd5c95 100644
--- a/services/webhook/notifier.go
+++ b/services/webhook/notifier.go
@@ -6,14 +6,18 @@ package webhook
import (
"context"
+ actions_model "code.gitea.io/gitea/models/actions"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
+ "code.gitea.io/gitea/models/organization"
packages_model "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
@@ -290,6 +294,43 @@ func (m *webhookNotifier) NewIssue(ctx context.Context, issue *issues_model.Issu
}
}
+func (m *webhookNotifier) DeleteIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) {
+ permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
+ if issue.IsPull {
+ if err := issue.LoadPullRequest(ctx); err != nil {
+ log.Error("LoadPullRequest: %v", err)
+ return
+ }
+ if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventPullRequest, &api.PullRequestPayload{
+ Action: api.HookIssueDeleted,
+ Index: issue.Index,
+ PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, doer),
+ Repository: convert.ToRepo(ctx, issue.Repo, permission),
+ Sender: convert.ToUser(ctx, doer, nil),
+ }); err != nil {
+ log.Error("PrepareWebhooks: %v", err)
+ }
+ } else {
+ if err := issue.LoadRepo(ctx); err != nil {
+ log.Error("issue.LoadRepo: %v", err)
+ return
+ }
+ if err := issue.LoadPoster(ctx); err != nil {
+ log.Error("issue.LoadPoster: %v", err)
+ return
+ }
+ if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssues, &api.IssuePayload{
+ Action: api.HookIssueDeleted,
+ Index: issue.Index,
+ Issue: convert.ToAPIIssue(ctx, issue.Poster, issue),
+ Repository: convert.ToRepo(ctx, issue.Repo, permission),
+ Sender: convert.ToUser(ctx, doer, nil),
+ }); err != nil {
+ log.Error("PrepareWebhooks: %v", err)
+ }
+ }
+}
+
func (m *webhookNotifier) NewPullRequest(ctx context.Context, pull *issues_model.PullRequest, mentions []*user_model.User) {
if err := pull.LoadIssue(ctx); err != nil {
log.Error("pull.LoadIssue: %v", err)
@@ -601,7 +642,7 @@ func (m *webhookNotifier) IssueChangeMilestone(ctx context.Context, doer *user_m
func (m *webhookNotifier) PushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
apiPusher := convert.ToUser(ctx, pusher, nil)
- apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL())
+ apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo)
if err != nil {
log.Error("commits.ToAPIPayloadCommits failed: %v", err)
return
@@ -763,12 +804,10 @@ func (m *webhookNotifier) PullRequestReviewRequest(ctx context.Context, doer *us
func (m *webhookNotifier) CreateRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName, refID string) {
apiPusher := convert.ToUser(ctx, pusher, nil)
apiRepo := convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeNone})
- refName := refFullName.ShortName()
-
if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventCreate, &api.CreatePayload{
- Ref: refName, // FIXME: should it be a full ref name?
+ Ref: refFullName.ShortName(), // FIXME: should it be a full ref name? But it will break the existing webhooks?
Sha: refID,
- RefType: refFullName.RefType(),
+ RefType: string(refFullName.RefType()),
Repo: apiRepo,
Sender: apiPusher,
}); err != nil {
@@ -800,11 +839,9 @@ func (m *webhookNotifier) PullRequestSynchronized(ctx context.Context, doer *use
func (m *webhookNotifier) DeleteRef(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, refFullName git.RefName) {
apiPusher := convert.ToUser(ctx, pusher, nil)
apiRepo := convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeOwner})
- refName := refFullName.ShortName()
-
if err := PrepareWebhooks(ctx, EventSource{Repository: repo}, webhook_module.HookEventDelete, &api.DeletePayload{
- Ref: refName, // FIXME: should it be a full ref name?
- RefType: refFullName.RefType(),
+ Ref: refFullName.ShortName(), // FIXME: should it be a full ref name? But it will break the existing webhooks?
+ RefType: string(refFullName.RefType()),
PusherType: api.PusherTypeUser,
Repo: apiRepo,
Sender: apiPusher,
@@ -844,7 +881,7 @@ func (m *webhookNotifier) DeleteRelease(ctx context.Context, doer *user_model.Us
func (m *webhookNotifier) SyncPushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
apiPusher := convert.ToUser(ctx, pusher, nil)
- apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo.RepoPath(), repo.HTMLURL())
+ apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(ctx, repo)
if err != nil {
log.Error("commits.ToAPIPayloadCommits failed: %v", err)
return
@@ -868,12 +905,17 @@ func (m *webhookNotifier) SyncPushCommits(ctx context.Context, pusher *user_mode
func (m *webhookNotifier) CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, commit *repository.PushCommit, sender *user_model.User, status *git_model.CommitStatus) {
apiSender := convert.ToUser(ctx, sender, nil)
- apiCommit, err := repository.ToAPIPayloadCommit(ctx, map[string]*user_model.User{}, repo.RepoPath(), repo.HTMLURL(), commit)
+ apiCommit, err := repository.ToAPIPayloadCommit(ctx, map[string]*user_model.User{}, repo, commit)
if err != nil {
log.Error("commits.ToAPIPayloadCommits failed: %v", err)
return
}
+ // as a webhook url, target should be an absolute url. But for internal actions target url
+ // the target url is a url path with no host and port to make it easy to be visited
+ // from multiple hosts. So we need to convert it to an absolute url here.
+ target := httplib.MakeAbsoluteURL(ctx, status.TargetURL)
+
payload := api.CommitStatusPayload{
Context: status.Context,
CreatedAt: status.CreatedUnix.AsTime().UTC(),
@@ -881,7 +923,7 @@ func (m *webhookNotifier) CreateCommitStatus(ctx context.Context, repo *repo_mod
ID: status.ID,
SHA: commit.Sha1,
State: status.State.String(),
- TargetURL: status.TargetURL,
+ TargetURL: target,
Commit: apiCommit,
Repo: convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeOwner}),
@@ -924,10 +966,90 @@ func notifyPackage(ctx context.Context, sender *user_model.User, pd *packages_mo
return
}
+ var org *api.Organization
+ if pd.Owner.IsOrganization() {
+ org = convert.ToOrganization(ctx, organization.OrgFromUser(pd.Owner))
+ }
+
if err := PrepareWebhooks(ctx, source, webhook_module.HookEventPackage, &api.PackagePayload{
- Action: action,
- Package: apiPackage,
- Sender: convert.ToUser(ctx, sender, nil),
+ Action: action,
+ Package: apiPackage,
+ Organization: org,
+ Sender: convert.ToUser(ctx, sender, nil),
+ }); err != nil {
+ log.Error("PrepareWebhooks: %v", err)
+ }
+}
+
+func (*webhookNotifier) WorkflowJobStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, job *actions_model.ActionRunJob, task *actions_model.ActionTask) {
+ source := EventSource{
+ Repository: repo,
+ Owner: repo.Owner,
+ }
+
+ var org *api.Organization
+ if repo.Owner.IsOrganization() {
+ org = convert.ToOrganization(ctx, organization.OrgFromUser(repo.Owner))
+ }
+
+ status, _ := convert.ToActionsStatus(job.Status)
+
+ convertedJob, err := convert.ToActionWorkflowJob(ctx, repo, task, job)
+ if err != nil {
+ log.Error("ToActionWorkflowJob: %v", err)
+ return
+ }
+
+ if err := PrepareWebhooks(ctx, source, webhook_module.HookEventWorkflowJob, &api.WorkflowJobPayload{
+ Action: status,
+ WorkflowJob: convertedJob,
+ Organization: org,
+ Repo: convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeOwner}),
+ Sender: convert.ToUser(ctx, sender, nil),
+ }); err != nil {
+ log.Error("PrepareWebhooks: %v", err)
+ }
+}
+
+func (*webhookNotifier) WorkflowRunStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, run *actions_model.ActionRun) {
+ source := EventSource{
+ Repository: repo,
+ Owner: repo.Owner,
+ }
+
+ var org *api.Organization
+ if repo.Owner.IsOrganization() {
+ org = convert.ToOrganization(ctx, organization.OrgFromUser(repo.Owner))
+ }
+
+ status := convert.ToWorkflowRunAction(run.Status)
+
+ gitRepo, err := gitrepo.OpenRepository(ctx, repo)
+ if err != nil {
+ log.Error("OpenRepository: %v", err)
+ return
+ }
+ defer gitRepo.Close()
+
+ convertedWorkflow, err := convert.GetActionWorkflow(ctx, gitRepo, repo, run.WorkflowID)
+ if err != nil {
+ log.Error("GetActionWorkflow: %v", err)
+ return
+ }
+
+ convertedRun, err := convert.ToActionWorkflowRun(ctx, repo, run)
+ if err != nil {
+ log.Error("ToActionWorkflowRun: %v", err)
+ return
+ }
+
+ if err := PrepareWebhooks(ctx, source, webhook_module.HookEventWorkflowRun, &api.WorkflowRunPayload{
+ Action: status,
+ Workflow: convertedWorkflow,
+ WorkflowRun: convertedRun,
+ Organization: org,
+ Repo: convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeOwner}),
+ Sender: convert.ToUser(ctx, sender, nil),
}); err != nil {
log.Error("PrepareWebhooks: %v", err)
}
diff --git a/services/webhook/packagist.go b/services/webhook/packagist.go
index 4d809ab3a6..e6a00b0293 100644
--- a/services/webhook/packagist.go
+++ b/services/webhook/packagist.go
@@ -110,6 +110,18 @@ func (pc packagistConvertor) Package(_ *api.PackagePayload) (PackagistPayload, e
return PackagistPayload{}, nil
}
+func (pc packagistConvertor) Status(_ *api.CommitStatusPayload) (PackagistPayload, error) {
+ return PackagistPayload{}, nil
+}
+
+func (pc packagistConvertor) WorkflowRun(_ *api.WorkflowRunPayload) (PackagistPayload, error) {
+ return PackagistPayload{}, nil
+}
+
+func (pc packagistConvertor) WorkflowJob(_ *api.WorkflowJobPayload) (PackagistPayload, error) {
+ return PackagistPayload{}, nil
+}
+
func newPackagistRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
meta := &PackagistMeta{}
if err := json.Unmarshal([]byte(w.Meta), meta); err != nil {
@@ -120,3 +132,7 @@ func newPackagistRequest(_ context.Context, w *webhook_model.Webhook, t *webhook
}
return newJSONRequest(pc, w, t, true)
}
+
+func init() {
+ RegisterWebhookRequester(webhook_module.PACKAGIST, newPackagistRequest)
+}
diff --git a/services/webhook/packagist_test.go b/services/webhook/packagist_test.go
index f47807fa6e..4e77f29edc 100644
--- a/services/webhook/packagist_test.go
+++ b/services/webhook/packagist_test.go
@@ -4,7 +4,6 @@
package webhook
import (
- "context"
"testing"
webhook_model "code.gitea.io/gitea/models/webhook"
@@ -164,7 +163,7 @@ func TestPackagistJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := newPackagistRequest(context.Background(), hook, task)
+ req, reqBody, err := newPackagistRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
@@ -199,7 +198,7 @@ func TestPackagistEmptyPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := newPackagistRequest(context.Background(), hook, task)
+ req, reqBody, err := newPackagistRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
@@ -211,5 +210,5 @@ func TestPackagistEmptyPayload(t *testing.T) {
var body PackagistPayload
err = json.NewDecoder(req.Body).Decode(&body)
assert.NoError(t, err)
- assert.Equal(t, "", body.PackagistRepository.URL)
+ assert.Empty(t, body.PackagistRepository.URL)
}
diff --git a/services/webhook/payloader.go b/services/webhook/payloader.go
index ab280a25b6..b607bf3250 100644
--- a/services/webhook/payloader.go
+++ b/services/webhook/payloader.go
@@ -28,6 +28,9 @@ type payloadConvertor[T any] interface {
Release(*api.ReleasePayload) (T, error)
Wiki(*api.WikiPayload) (T, error)
Package(*api.PackagePayload) (T, error)
+ Status(*api.CommitStatusPayload) (T, error)
+ WorkflowRun(*api.WorkflowRunPayload) (T, error)
+ WorkflowJob(*api.WorkflowJobPayload) (T, error)
}
func convertUnmarshalledJSON[T, P any](convert func(P) (T, error), data []byte) (t T, err error) {
@@ -77,6 +80,12 @@ func newPayload[T any](rc payloadConvertor[T], data []byte, event webhook_module
return convertUnmarshalledJSON(rc.Wiki, data)
case webhook_module.HookEventPackage:
return convertUnmarshalledJSON(rc.Package, data)
+ case webhook_module.HookEventStatus:
+ return convertUnmarshalledJSON(rc.Status, data)
+ case webhook_module.HookEventWorkflowRun:
+ return convertUnmarshalledJSON(rc.WorkflowRun, data)
+ case webhook_module.HookEventWorkflowJob:
+ return convertUnmarshalledJSON(rc.WorkflowJob, data)
}
return t, fmt.Errorf("newPayload unsupported event: %s", event)
}
@@ -86,7 +95,10 @@ func newJSONRequest[T any](pc payloadConvertor[T], w *webhook_model.Webhook, t *
if err != nil {
return nil, nil, err
}
+ return prepareJSONRequest(payload, w, t, withDefaultHeaders)
+}
+func prepareJSONRequest[T any](payload T, w *webhook_model.Webhook, t *webhook_model.HookTask, withDefaultHeaders bool) (*http.Request, []byte, error) {
body, err := json.MarshalIndent(payload, "", " ")
if err != nil {
return nil, nil, err
@@ -104,7 +116,7 @@ func newJSONRequest[T any](pc payloadConvertor[T], w *webhook_model.Webhook, t *
req.Header.Set("Content-Type", "application/json")
if withDefaultHeaders {
- return req, body, addDefaultHeaders(req, []byte(w.Secret), t, body)
+ return req, body, addDefaultHeaders(req, []byte(w.Secret), w, t, body)
}
return req, body, nil
}
diff --git a/services/webhook/slack.go b/services/webhook/slack.go
index c905e7a89f..3d645a55d0 100644
--- a/services/webhook/slack.go
+++ b/services/webhook/slack.go
@@ -84,9 +84,9 @@ func SlackLinkFormatter(url, text string) string {
// SlackLinkToRef slack-formatter link to a repo ref
func SlackLinkToRef(repoURL, ref string) string {
// FIXME: SHA1 hardcoded here
- url := git.RefURL(repoURL, ref)
- refName := git.RefName(ref).ShortName()
- return SlackLinkFormatter(url, refName)
+ refName := git.RefName(ref)
+ url := repoURL + "/src/" + refName.RefWebLinkPath()
+ return SlackLinkFormatter(url, refName.ShortName())
}
// Create implements payloadConvertor Create method
@@ -167,6 +167,24 @@ func (s slackConvertor) Package(p *api.PackagePayload) (SlackPayload, error) {
return s.createPayload(text, nil), nil
}
+func (s slackConvertor) Status(p *api.CommitStatusPayload) (SlackPayload, error) {
+ text, _ := getStatusPayloadInfo(p, SlackLinkFormatter, true)
+
+ return s.createPayload(text, nil), nil
+}
+
+func (s slackConvertor) WorkflowRun(p *api.WorkflowRunPayload) (SlackPayload, error) {
+ text, _ := getWorkflowRunPayloadInfo(p, SlackLinkFormatter, true)
+
+ return s.createPayload(text, nil), nil
+}
+
+func (s slackConvertor) WorkflowJob(p *api.WorkflowJobPayload) (SlackPayload, error) {
+ text, _ := getWorkflowJobPayloadInfo(p, SlackLinkFormatter, true)
+
+ return s.createPayload(text, nil), nil
+}
+
// Push implements payloadConvertor Push method
func (s slackConvertor) Push(p *api.PushPayload) (SlackPayload, error) {
// n new commits
@@ -295,6 +313,10 @@ func newSlackRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_mod
return newJSONRequest(pc, w, t, true)
}
+func init() {
+ RegisterWebhookRequester(webhook_module.SLACK, newSlackRequest)
+}
+
var slackChannel = regexp.MustCompile(`^#?[a-z0-9_-]{1,80}$`)
// IsValidSlackChannel validates a channel name conforms to what slack expects:
diff --git a/services/webhook/slack_test.go b/services/webhook/slack_test.go
index 7ebf16aba2..839ed6f770 100644
--- a/services/webhook/slack_test.go
+++ b/services/webhook/slack_test.go
@@ -4,7 +4,6 @@
package webhook
import (
- "context"
"testing"
webhook_model "code.gitea.io/gitea/models/webhook"
@@ -178,7 +177,7 @@ func TestSlackJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := newSlackRequest(context.Background(), hook, task)
+ req, reqBody, err := newSlackRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
diff --git a/services/webhook/telegram.go b/services/webhook/telegram.go
index e54d6f2947..fdd428b45c 100644
--- a/services/webhook/telegram.go
+++ b/services/webhook/telegram.go
@@ -174,10 +174,28 @@ func (t telegramConvertor) Package(p *api.PackagePayload) (TelegramPayload, erro
return createTelegramPayloadHTML(text), nil
}
+func (t telegramConvertor) Status(p *api.CommitStatusPayload) (TelegramPayload, error) {
+ text, _ := getStatusPayloadInfo(p, htmlLinkFormatter, true)
+
+ return createTelegramPayloadHTML(text), nil
+}
+
+func (telegramConvertor) WorkflowRun(p *api.WorkflowRunPayload) (TelegramPayload, error) {
+ text, _ := getWorkflowRunPayloadInfo(p, htmlLinkFormatter, true)
+
+ return createTelegramPayloadHTML(text), nil
+}
+
+func (telegramConvertor) WorkflowJob(p *api.WorkflowJobPayload) (TelegramPayload, error) {
+ text, _ := getWorkflowJobPayloadInfo(p, htmlLinkFormatter, true)
+
+ return createTelegramPayloadHTML(text), nil
+}
+
func createTelegramPayloadHTML(msgHTML string) TelegramPayload {
// https://core.telegram.org/bots/api#formatting-options
return TelegramPayload{
- Message: strings.TrimSpace(markup.Sanitize(msgHTML)),
+ Message: strings.TrimSpace(string(markup.Sanitize(msgHTML))),
ParseMode: "HTML",
DisableWebPreview: true,
}
@@ -187,3 +205,7 @@ func newTelegramRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_
var pc payloadConvertor[TelegramPayload] = telegramConvertor{}
return newJSONRequest(pc, w, t, true)
}
+
+func init() {
+ RegisterWebhookRequester(webhook_module.TELEGRAM, newTelegramRequest)
+}
diff --git a/services/webhook/telegram_test.go b/services/webhook/telegram_test.go
index 7ba81f1564..3fa8e27836 100644
--- a/services/webhook/telegram_test.go
+++ b/services/webhook/telegram_test.go
@@ -4,7 +4,6 @@
package webhook
import (
- "context"
"testing"
webhook_model "code.gitea.io/gitea/models/webhook"
@@ -195,7 +194,7 @@ func TestTelegramJSONPayload(t *testing.T) {
PayloadVersion: 2,
}
- req, reqBody, err := newTelegramRequest(context.Background(), hook, task)
+ req, reqBody, err := newTelegramRequest(t.Context(), hook, task)
require.NotNil(t, req)
require.NotNil(t, reqBody)
require.NoError(t, err)
diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go
index e0e8fa2fc1..182078b39d 100644
--- a/services/webhook/webhook.go
+++ b/services/webhook/webhook.go
@@ -27,16 +27,12 @@ import (
"github.com/gobwas/glob"
)
-var webhookRequesters = map[webhook_module.HookType]func(context.Context, *webhook_model.Webhook, *webhook_model.HookTask) (req *http.Request, body []byte, err error){
- webhook_module.SLACK: newSlackRequest,
- webhook_module.DISCORD: newDiscordRequest,
- webhook_module.DINGTALK: newDingtalkRequest,
- webhook_module.TELEGRAM: newTelegramRequest,
- webhook_module.MSTEAMS: newMSTeamsRequest,
- webhook_module.FEISHU: newFeishuRequest,
- webhook_module.MATRIX: newMatrixRequest,
- webhook_module.WECHATWORK: newWechatworkRequest,
- webhook_module.PACKAGIST: newPackagistRequest,
+type Requester func(context.Context, *webhook_model.Webhook, *webhook_model.HookTask) (req *http.Request, body []byte, err error)
+
+var webhookRequesters = map[webhook_module.HookType]Requester{}
+
+func RegisterWebhookRequester(hookType webhook_module.HookType, requester Requester) {
+ webhookRequesters[hookType] = requester
}
// IsValidHookTaskType returns true if a webhook registered
@@ -137,14 +133,8 @@ func PrepareWebhook(ctx context.Context, w *webhook_model.Webhook, event webhook
return nil
}
- for _, e := range w.EventCheckers() {
- if event == e.Type {
- if !e.Has() {
- return nil
- }
-
- break
- }
+ if !w.HasEvent(event) {
+ return nil
}
// Avoid sending "0 new commits" to non-integration relevant webhooks (e.g. slack, discord, etc.).
diff --git a/services/webhook/webhook_test.go b/services/webhook/webhook_test.go
index 6bac02712b..5a805347e3 100644
--- a/services/webhook/webhook_test.go
+++ b/services/webhook/webhook_test.go
@@ -13,6 +13,7 @@ import (
webhook_model "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/test"
webhook_module "code.gitea.io/gitea/modules/webhook"
"code.gitea.io/gitea/services/convert"
@@ -84,7 +85,8 @@ func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) {
func TestWebhookUserMail(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
- setting.Service.NoReplyAddress = "no-reply.com"
+ defer test.MockVariableValue(&setting.Service.NoReplyAddress, "no-reply.com")()
+
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.Equal(t, user.GetPlaceholderEmail(), convert.ToUser(db.DefaultContext, user, nil).Email)
assert.Equal(t, user.Email, convert.ToUser(db.DefaultContext, user, user).Email)
diff --git a/services/webhook/wechatwork.go b/services/webhook/wechatwork.go
index 1d8c1d7dac..1875317406 100644
--- a/services/webhook/wechatwork.go
+++ b/services/webhook/wechatwork.go
@@ -175,7 +175,29 @@ func (wc wechatworkConvertor) Package(p *api.PackagePayload) (WechatworkPayload,
return newWechatworkMarkdownPayload(text), nil
}
+func (wc wechatworkConvertor) Status(p *api.CommitStatusPayload) (WechatworkPayload, error) {
+ text, _ := getStatusPayloadInfo(p, noneLinkFormatter, true)
+
+ return newWechatworkMarkdownPayload(text), nil
+}
+
+func (wc wechatworkConvertor) WorkflowRun(p *api.WorkflowRunPayload) (WechatworkPayload, error) {
+ text, _ := getWorkflowRunPayloadInfo(p, noneLinkFormatter, true)
+
+ return newWechatworkMarkdownPayload(text), nil
+}
+
+func (wc wechatworkConvertor) WorkflowJob(p *api.WorkflowJobPayload) (WechatworkPayload, error) {
+ text, _ := getWorkflowJobPayloadInfo(p, noneLinkFormatter, true)
+
+ return newWechatworkMarkdownPayload(text), nil
+}
+
func newWechatworkRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
var pc payloadConvertor[WechatworkPayload] = wechatworkConvertor{}
return newJSONRequest(pc, w, t, true)
}
+
+func init() {
+ RegisterWebhookRequester(webhook_module.WECHATWORK, newWechatworkRequest)
+}
diff --git a/services/webtheme/webtheme.go b/services/webtheme/webtheme.go
index dc801e1ff7..4e89d6dbac 100644
--- a/services/webtheme/webtheme.go
+++ b/services/webtheme/webtheme.go
@@ -4,6 +4,7 @@
package webtheme
import (
+ "regexp"
"sort"
"strings"
"sync"
@@ -12,63 +13,154 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
)
var (
- availableThemes []string
- availableThemesSet container.Set[string]
- themeOnce sync.Once
+ availableThemes []*ThemeMetaInfo
+ availableThemeInternalNames container.Set[string]
+ themeOnce sync.Once
)
+const (
+ fileNamePrefix = "theme-"
+ fileNameSuffix = ".css"
+)
+
+type ThemeMetaInfo struct {
+ FileName string
+ InternalName string
+ DisplayName string
+}
+
+func parseThemeMetaInfoToMap(cssContent string) map[string]string {
+ /*
+ The theme meta info is stored in the CSS file's variables of `gitea-theme-meta-info` element,
+ which is a privately defined and is only used by backend to extract the meta info.
+ Not using ":root" because it is difficult to parse various ":root" blocks when importing other files,
+ it is difficult to control the overriding, and it's difficult to avoid user's customized overridden styles.
+ */
+ metaInfoContent := cssContent
+ if pos := strings.LastIndex(metaInfoContent, "gitea-theme-meta-info"); pos >= 0 {
+ metaInfoContent = metaInfoContent[pos:]
+ }
+
+ reMetaInfoItem := `
+(
+\s*(--[-\w]+)
+\s*:
+\s*(
+("(\\"|[^"])*")
+|('(\\'|[^'])*')
+|([^'";]+)
+)
+\s*;
+\s*
+)
+`
+ reMetaInfoItem = strings.ReplaceAll(reMetaInfoItem, "\n", "")
+ reMetaInfoBlock := `\bgitea-theme-meta-info\s*\{(` + reMetaInfoItem + `+)\}`
+ re := regexp.MustCompile(reMetaInfoBlock)
+ matchedMetaInfoBlock := re.FindAllStringSubmatch(metaInfoContent, -1)
+ if len(matchedMetaInfoBlock) == 0 {
+ return nil
+ }
+ re = regexp.MustCompile(strings.ReplaceAll(reMetaInfoItem, "\n", ""))
+ matchedItems := re.FindAllStringSubmatch(matchedMetaInfoBlock[0][1], -1)
+ m := map[string]string{}
+ for _, item := range matchedItems {
+ v := item[3]
+ if after, ok := strings.CutPrefix(v, `"`); ok {
+ v = strings.TrimSuffix(after, `"`)
+ v = strings.ReplaceAll(v, `\"`, `"`)
+ } else if after, ok := strings.CutPrefix(v, `'`); ok {
+ v = strings.TrimSuffix(after, `'`)
+ v = strings.ReplaceAll(v, `\'`, `'`)
+ }
+ m[item[2]] = v
+ }
+ return m
+}
+
+func defaultThemeMetaInfoByFileName(fileName string) *ThemeMetaInfo {
+ themeInfo := &ThemeMetaInfo{
+ FileName: fileName,
+ InternalName: strings.TrimSuffix(strings.TrimPrefix(fileName, fileNamePrefix), fileNameSuffix),
+ }
+ themeInfo.DisplayName = themeInfo.InternalName
+ return themeInfo
+}
+
+func defaultThemeMetaInfoByInternalName(fileName string) *ThemeMetaInfo {
+ return defaultThemeMetaInfoByFileName(fileNamePrefix + fileName + fileNameSuffix)
+}
+
+func parseThemeMetaInfo(fileName, cssContent string) *ThemeMetaInfo {
+ themeInfo := defaultThemeMetaInfoByFileName(fileName)
+ m := parseThemeMetaInfoToMap(cssContent)
+ if m == nil {
+ return themeInfo
+ }
+ themeInfo.DisplayName = m["--theme-display-name"]
+ return themeInfo
+}
+
func initThemes() {
availableThemes = nil
defer func() {
- availableThemesSet = container.SetOf(availableThemes...)
- if !availableThemesSet.Contains(setting.UI.DefaultTheme) {
+ availableThemeInternalNames = container.Set[string]{}
+ for _, theme := range availableThemes {
+ availableThemeInternalNames.Add(theme.InternalName)
+ }
+ if !availableThemeInternalNames.Contains(setting.UI.DefaultTheme) {
setting.LogStartupProblem(1, log.ERROR, "Default theme %q is not available, please correct the '[ui].DEFAULT_THEME' setting in the config file", setting.UI.DefaultTheme)
}
}()
cssFiles, err := public.AssetFS().ListFiles("/assets/css")
if err != nil {
log.Error("Failed to list themes: %v", err)
- availableThemes = []string{setting.UI.DefaultTheme}
+ availableThemes = []*ThemeMetaInfo{defaultThemeMetaInfoByInternalName(setting.UI.DefaultTheme)}
return
}
- var foundThemes []string
- for _, name := range cssFiles {
- name, ok := strings.CutPrefix(name, "theme-")
- if !ok {
- continue
- }
- name, ok = strings.CutSuffix(name, ".css")
- if !ok {
- continue
+ var foundThemes []*ThemeMetaInfo
+ for _, fileName := range cssFiles {
+ if strings.HasPrefix(fileName, fileNamePrefix) && strings.HasSuffix(fileName, fileNameSuffix) {
+ content, err := public.AssetFS().ReadFile("/assets/css/" + fileName)
+ if err != nil {
+ log.Error("Failed to read theme file %q: %v", fileName, err)
+ continue
+ }
+ foundThemes = append(foundThemes, parseThemeMetaInfo(fileName, util.UnsafeBytesToString(content)))
}
- foundThemes = append(foundThemes, name)
}
if len(setting.UI.Themes) > 0 {
allowedThemes := container.SetOf(setting.UI.Themes...)
for _, theme := range foundThemes {
- if allowedThemes.Contains(theme) {
+ if allowedThemes.Contains(theme.InternalName) {
availableThemes = append(availableThemes, theme)
}
}
} else {
availableThemes = foundThemes
}
- sort.Strings(availableThemes)
+ sort.Slice(availableThemes, func(i, j int) bool {
+ if availableThemes[i].InternalName == setting.UI.DefaultTheme {
+ return true
+ }
+ return availableThemes[i].DisplayName < availableThemes[j].DisplayName
+ })
if len(availableThemes) == 0 {
setting.LogStartupProblem(1, log.ERROR, "No theme candidate in asset files, but Gitea requires there should be at least one usable theme")
- availableThemes = []string{setting.UI.DefaultTheme}
+ availableThemes = []*ThemeMetaInfo{defaultThemeMetaInfoByInternalName(setting.UI.DefaultTheme)}
}
}
-func GetAvailableThemes() []string {
+func GetAvailableThemes() []*ThemeMetaInfo {
themeOnce.Do(initThemes)
return availableThemes
}
-func IsThemeAvailable(name string) bool {
+func IsThemeAvailable(internalName string) bool {
themeOnce.Do(initThemes)
- return availableThemesSet.Contains(name)
+ return availableThemeInternalNames.Contains(internalName)
}
diff --git a/services/webtheme/webtheme_test.go b/services/webtheme/webtheme_test.go
new file mode 100644
index 0000000000..587953ab0c
--- /dev/null
+++ b/services/webtheme/webtheme_test.go
@@ -0,0 +1,37 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package webtheme
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestParseThemeMetaInfo(t *testing.T) {
+ m := parseThemeMetaInfoToMap(`gitea-theme-meta-info {
+ --k1: "v1";
+ --k2: "v\"2";
+ --k3: 'v3';
+ --k4: 'v\'4';
+ --k5: v5;
+}`)
+ assert.Equal(t, map[string]string{
+ "--k1": "v1",
+ "--k2": `v"2`,
+ "--k3": "v3",
+ "--k4": "v'4",
+ "--k5": "v5",
+ }, m)
+
+ // if an auto theme imports others, the meta info should be extracted from the last one
+ // the meta in imported themes should be ignored to avoid incorrect overriding
+ m = parseThemeMetaInfoToMap(`
+@media (prefers-color-scheme: dark) { gitea-theme-meta-info { --k1: foo; } }
+@media (prefers-color-scheme: light) { gitea-theme-meta-info { --k1: bar; } }
+gitea-theme-meta-info {
+ --k2: real;
+}`)
+ assert.Equal(t, map[string]string{"--k2": "real"}, m)
+}
diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go
index 7a0419aea7..0a955406e2 100644
--- a/services/wiki/wiki.go
+++ b/services/wiki/wiki.go
@@ -41,9 +41,9 @@ func InitWiki(ctx context.Context, repo *repo_model.Repository) error {
if err := git.InitRepository(ctx, repo.WikiPath(), true, repo.ObjectFormatName); err != nil {
return fmt.Errorf("InitRepository: %w", err)
- } else if err = repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil {
+ } else if err = gitrepo.CreateDelegateHooks(ctx, repo.WikiStorageRepo()); err != nil {
return fmt.Errorf("createDelegateHooks: %w", err)
- } else if _, _, err = git.NewCommand(ctx, "symbolic-ref", "HEAD").AddDynamicArguments(git.BranchPrefix + repo.DefaultWikiBranch).RunStdString(&git.RunOpts{Dir: repo.WikiPath()}); err != nil {
+ } else if _, _, err = git.NewCommand("symbolic-ref", "HEAD").AddDynamicArguments(git.BranchPrefix+repo.DefaultWikiBranch).RunStdString(ctx, &git.RunOpts{Dir: repo.WikiPath()}); err != nil {
return fmt.Errorf("unable to set default wiki branch to %q: %w", repo.DefaultWikiBranch, err)
}
return nil
@@ -100,17 +100,13 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
return fmt.Errorf("InitWiki: %w", err)
}
- hasDefaultBranch := git.IsBranchExist(ctx, repo.WikiPath(), repo.DefaultWikiBranch)
+ hasDefaultBranch := gitrepo.IsBranchExist(ctx, repo.WikiStorageRepo(), repo.DefaultWikiBranch)
- basePath, err := repo_module.CreateTemporaryPath("update-wiki")
+ basePath, cleanup, err := repo_module.CreateTemporaryPath("update-wiki")
if err != nil {
return err
}
- defer func() {
- if err := repo_module.RemoveTemporaryPath(basePath); err != nil {
- log.Error("Merge: RemoveTemporaryPath: %s", err)
- }
- }()
+ defer cleanup()
cloneOpts := git.CloneRepoOptions{
Bare: true,
@@ -198,7 +194,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
sign, signingKey, signer, _ := asymkey_service.SignWikiCommit(ctx, repo, doer)
if sign {
- commitTreeOpts.KeyID = signingKey
+ commitTreeOpts.Key = signingKey
if repo.GetTrustModel() == repo_model.CommitterTrustModel || repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
committer = signer
}
@@ -264,15 +260,11 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
return fmt.Errorf("InitWiki: %w", err)
}
- basePath, err := repo_module.CreateTemporaryPath("update-wiki")
+ basePath, cleanup, err := repo_module.CreateTemporaryPath("update-wiki")
if err != nil {
return err
}
- defer func() {
- if err := repo_module.RemoveTemporaryPath(basePath); err != nil {
- log.Error("Merge: RemoveTemporaryPath: %s", err)
- }
- }()
+ defer cleanup()
if err := git.Clone(ctx, repo.WikiPath(), basePath, git.CloneRepoOptions{
Bare: true,
@@ -324,7 +316,7 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
sign, signingKey, signer, _ := asymkey_service.SignWikiCommit(ctx, repo, doer)
if sign {
- commitTreeOpts.KeyID = signingKey
+ commitTreeOpts.Key = signingKey
if repo.GetTrustModel() == repo_model.CommitterTrustModel || repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
committer = signer
}
@@ -373,7 +365,7 @@ func ChangeDefaultWikiBranch(ctx context.Context, repo *repo_model.Repository, n
}
return db.WithTx(ctx, func(ctx context.Context) error {
repo.DefaultWikiBranch = newBranch
- if err := repo_model.UpdateRepositoryCols(ctx, repo, "default_wiki_branch"); err != nil {
+ if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "default_wiki_branch"); err != nil {
return fmt.Errorf("unable to update database: %w", err)
}
@@ -381,7 +373,7 @@ func ChangeDefaultWikiBranch(ctx context.Context, repo *repo_model.Repository, n
return nil
}
- oldDefBranch, err := gitrepo.GetWikiDefaultBranch(ctx, repo)
+ oldDefBranch, err := gitrepo.GetDefaultBranch(ctx, repo.WikiStorageRepo())
if err != nil {
return fmt.Errorf("unable to get default branch: %w", err)
}
@@ -389,7 +381,7 @@ func ChangeDefaultWikiBranch(ctx context.Context, repo *repo_model.Repository, n
return nil
}
- gitRepo, err := gitrepo.OpenWikiRepository(ctx, repo)
+ gitRepo, err := gitrepo.OpenRepository(ctx, repo.WikiStorageRepo())
if errors.Is(err, util.ErrNotExist) {
return nil // no git repo on storage, no need to do anything else
} else if err != nil {
diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go
index 0a18cffa25..6ea3ca9c1b 100644
--- a/services/wiki/wiki_test.go
+++ b/services/wiki/wiki_test.go
@@ -17,6 +17,7 @@ import (
_ "code.gitea.io/gitea/models/actions"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
@@ -25,7 +26,7 @@ func TestMain(m *testing.M) {
func TestWebPathSegments(t *testing.T) {
a := WebPathSegments("a%2Fa/b+c/d-e/f-g.-")
- assert.EqualValues(t, []string{"a/a", "b c", "d e", "f-g"}, a)
+ assert.Equal(t, []string{"a/a", "b c", "d e", "f-g"}, a)
}
func TestUserTitleToWebPath(t *testing.T) {
@@ -62,7 +63,7 @@ func TestWebPathToDisplayName(t *testing.T) {
{"a b", "a%20b.md"},
} {
_, displayName := WebPathToUserTitle(test.WebPath)
- assert.EqualValues(t, test.Expected, displayName)
+ assert.Equal(t, test.Expected, displayName)
}
}
@@ -79,7 +80,7 @@ func TestWebPathToGitPath(t *testing.T) {
{"2000-01-02-meeting.md", "2000-01-02+meeting"},
{"2000-01-02 meeting.-.md", "2000-01-02%20meeting.-"},
} {
- assert.EqualValues(t, test.Expected, WebPathToGitPath(test.WikiName))
+ assert.Equal(t, test.Expected, WebPathToGitPath(test.WikiName))
}
}
@@ -115,9 +116,9 @@ func TestGitPathToWebPath(t *testing.T) {
func TestUserWebGitPathConsistency(t *testing.T) {
maxLen := 20
b := make([]byte, maxLen)
- for i := 0; i < 1000; i++ {
+ for range 1000 {
l := rand.Intn(maxLen)
- for j := 0; j < l; j++ {
+ for j := range l {
r := rand.Intn(0x80-0x20) + 0x20
b[j] = byte(r)
}
@@ -133,9 +134,9 @@ func TestUserWebGitPathConsistency(t *testing.T) {
_, userTitle1 := WebPathToUserTitle(webPath1)
gitPath1 := WebPathToGitPath(webPath1)
- assert.EqualValues(t, userTitle, userTitle1, "UserTitle for userTitle: %q", userTitle)
- assert.EqualValues(t, webPath, webPath1, "WebPath for userTitle: %q", userTitle)
- assert.EqualValues(t, gitPath, gitPath1, "GitPath for userTitle: %q", userTitle)
+ assert.Equal(t, userTitle, userTitle1, "UserTitle for userTitle: %q", userTitle)
+ assert.Equal(t, webPath, webPath1, "WebPath for userTitle: %q", userTitle)
+ assert.Equal(t, gitPath, gitPath1, "GitPath for userTitle: %q", userTitle)
}
}
@@ -165,17 +166,16 @@ func TestRepository_AddWikiPage(t *testing.T) {
webPath := UserTitleToWebPath("", userTitle)
assert.NoError(t, AddWikiPage(git.DefaultContext, doer, repo, webPath, wikiContent, commitMsg))
// Now need to show that the page has been added:
- gitRepo, err := gitrepo.OpenWikiRepository(git.DefaultContext, repo)
- if !assert.NoError(t, err) {
- return
- }
+ gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo.WikiStorageRepo())
+ require.NoError(t, err)
+
defer gitRepo.Close()
masterTree, err := gitRepo.GetTree(repo.DefaultWikiBranch)
assert.NoError(t, err)
gitPath := WebPathToGitPath(webPath)
entry, err := masterTree.GetTreeEntryByPath(gitPath)
assert.NoError(t, err)
- assert.EqualValues(t, gitPath, entry.Name(), "%s not added correctly", userTitle)
+ assert.Equal(t, gitPath, entry.Name(), "%s not added correctly", userTitle)
})
}
@@ -213,14 +213,14 @@ func TestRepository_EditWikiPage(t *testing.T) {
assert.NoError(t, EditWikiPage(git.DefaultContext, doer, repo, "Home", webPath, newWikiContent, commitMsg))
// Now need to show that the page has been added:
- gitRepo, err := gitrepo.OpenWikiRepository(git.DefaultContext, repo)
+ gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo.WikiStorageRepo())
assert.NoError(t, err)
masterTree, err := gitRepo.GetTree(repo.DefaultWikiBranch)
assert.NoError(t, err)
gitPath := WebPathToGitPath(webPath)
entry, err := masterTree.GetTreeEntryByPath(gitPath)
assert.NoError(t, err)
- assert.EqualValues(t, gitPath, entry.Name(), "%s not edited correctly", newWikiName)
+ assert.Equal(t, gitPath, entry.Name(), "%s not edited correctly", newWikiName)
if newWikiName != "Home" {
_, err := masterTree.GetTreeEntryByPath("Home.md")
@@ -237,10 +237,9 @@ func TestRepository_DeleteWikiPage(t *testing.T) {
assert.NoError(t, DeleteWikiPage(git.DefaultContext, doer, repo, "Home"))
// Now need to show that the page has been added:
- gitRepo, err := gitrepo.OpenWikiRepository(git.DefaultContext, repo)
- if !assert.NoError(t, err) {
- return
- }
+ gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo.WikiStorageRepo())
+ require.NoError(t, err)
+
defer gitRepo.Close()
masterTree, err := gitRepo.GetTree(repo.DefaultWikiBranch)
assert.NoError(t, err)
@@ -252,10 +251,9 @@ func TestRepository_DeleteWikiPage(t *testing.T) {
func TestPrepareWikiFileName(t *testing.T) {
unittest.PrepareTestEnv(t)
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
- gitRepo, err := gitrepo.OpenWikiRepository(git.DefaultContext, repo)
- if !assert.NoError(t, err) {
- return
- }
+ gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo.WikiStorageRepo())
+ require.NoError(t, err)
+
defer gitRepo.Close()
tests := []struct {
@@ -292,7 +290,7 @@ func TestPrepareWikiFileName(t *testing.T) {
t.Errorf("expect to find an escaped file but we could not detect one")
}
}
- assert.EqualValues(t, tt.wikiPath, newWikiPath)
+ assert.Equal(t, tt.wikiPath, newWikiPath)
})
}
}
@@ -307,21 +305,20 @@ func TestPrepareWikiFileName_FirstPage(t *testing.T) {
assert.NoError(t, err)
gitRepo, err := git.OpenRepository(git.DefaultContext, tmpDir)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
+
defer gitRepo.Close()
existence, newWikiPath, err := prepareGitPath(gitRepo, "master", "Home")
assert.False(t, existence)
assert.NoError(t, err)
- assert.EqualValues(t, "Home.md", newWikiPath)
+ assert.Equal(t, "Home.md", newWikiPath)
}
func TestWebPathConversion(t *testing.T) {
assert.Equal(t, "path/wiki", WebPathToURLPath(WebPath("path/wiki")))
assert.Equal(t, "wiki", WebPathToURLPath(WebPath("wiki")))
- assert.Equal(t, "", WebPathToURLPath(WebPath("")))
+ assert.Empty(t, WebPathToURLPath(WebPath("")))
}
func TestWebPathFromRequest(t *testing.T) {
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index c4c31968e0..596f703131 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -44,7 +44,7 @@ parts:
source: .
stage-packages: [ git, sqlite3, openssh-client ]
build-packages: [ git, libpam0g-dev, libsqlite3-dev, build-essential]
- build-snaps: [ go/1.23/stable, node/22/stable ]
+ build-snaps: [ go/1.24/stable, node/22/stable ]
build-environment:
- LDFLAGS: ""
override-pull: |
diff --git a/stylelint.config.js b/stylelint.config.js
index 977c35d9d5..c57c1fd3a3 100644
--- a/stylelint.config.js
+++ b/stylelint.config.js
@@ -1,3 +1,5 @@
+// @ts-check
+import {defineConfig} from 'stylelint-define-config';
import {fileURLToPath} from 'node:url';
const cssVarFiles = [
@@ -6,8 +8,8 @@ const cssVarFiles = [
fileURLToPath(new URL('web_src/css/themes/theme-gitea-dark.css', import.meta.url)),
];
-/** @type {import('stylelint').Config} */
-export default {
+export default defineConfig({
+ extends: 'stylelint-config-recommended',
plugins: [
'stylelint-declaration-strict-value',
'stylelint-declaration-block-no-ignored-properties',
@@ -67,7 +69,7 @@ export default {
'@stylistic/function-comma-space-after': null,
'@stylistic/function-comma-space-before': null,
'@stylistic/function-max-empty-lines': 0,
- '@stylistic/function-parentheses-newline-inside': 'never-multi-line',
+ '@stylistic/function-parentheses-newline-inside': null,
'@stylistic/function-parentheses-space-inside': null,
'@stylistic/function-whitespace-after': null,
'@stylistic/indentation': 2,
@@ -114,134 +116,33 @@ export default {
'@stylistic/value-list-comma-space-after': null,
'@stylistic/value-list-comma-space-before': null,
'@stylistic/value-list-max-empty-lines': 0,
- 'alpha-value-notation': null,
- 'annotation-no-unknown': true,
- 'at-rule-allowed-list': null,
- 'at-rule-disallowed-list': null,
- 'at-rule-empty-line-before': null,
'at-rule-no-unknown': [true, {ignoreAtRules: ['tailwind']}],
'at-rule-no-vendor-prefix': true,
- 'at-rule-property-required-list': null,
- 'block-no-empty': true,
- 'color-function-notation': null,
- 'color-hex-alpha': null,
- 'color-hex-length': null,
- 'color-named': null,
- 'color-no-hex': null,
- 'color-no-invalid-hex': true,
- 'comment-empty-line-before': null,
- 'comment-no-empty': true,
- 'comment-pattern': null,
- 'comment-whitespace-inside': null,
- 'comment-word-disallowed-list': null,
'csstools/value-no-unknown-custom-properties': [true, {importFrom: cssVarFiles}],
- 'custom-media-pattern': null,
- 'custom-property-empty-line-before': null,
- 'custom-property-no-missing-var-function': true,
- 'custom-property-pattern': null,
- 'declaration-block-no-duplicate-custom-properties': true,
'declaration-block-no-duplicate-properties': [true, {ignore: ['consecutive-duplicates-with-different-values']}],
- 'declaration-block-no-redundant-longhand-properties': [true, {ignoreShorthands: ['flex-flow', 'overflow']}],
- 'declaration-block-no-shorthand-property-overrides': null,
- 'declaration-block-single-line-max-declarations': null,
- 'declaration-empty-line-before': null,
- 'declaration-no-important': null,
- 'declaration-property-max-values': null,
- 'declaration-property-unit-allowed-list': null,
+ 'declaration-block-no-redundant-longhand-properties': [true, {ignoreShorthands: ['flex-flow', 'overflow', 'grid-template']}],
+ // @ts-expect-error - https://github.com/stylelint-types/stylelint-define-config/issues/1
'declaration-property-unit-disallowed-list': {'line-height': ['em']},
- 'declaration-property-value-allowed-list': null,
'declaration-property-value-disallowed-list': {'word-break': ['break-word']},
- 'declaration-property-value-no-unknown': true,
'font-family-name-quotes': 'always-where-recommended',
- 'font-family-no-duplicate-names': true,
- 'font-family-no-missing-generic-family-keyword': true,
- 'font-weight-notation': null,
- 'function-allowed-list': null,
- 'function-calc-no-unspaced-operator': true,
- 'function-disallowed-list': null,
- 'function-linear-gradient-no-nonstandard-direction': true,
'function-name-case': 'lower',
- 'function-no-unknown': true,
- 'function-url-no-scheme-relative': null,
'function-url-quotes': 'always',
- 'function-url-scheme-allowed-list': null,
- 'function-url-scheme-disallowed-list': null,
- 'hue-degree-notation': null,
'import-notation': 'string',
- 'keyframe-block-no-duplicate-selectors': true,
- 'keyframe-declaration-no-important': true,
- 'keyframe-selector-notation': null,
- 'keyframes-name-pattern': null,
- 'length-zero-no-unit': [true, {ignore: ['custom-properties']}, {ignoreFunctions: ['var']}],
- 'max-nesting-depth': null,
- 'media-feature-name-allowed-list': null,
- 'media-feature-name-disallowed-list': null,
- 'media-feature-name-no-unknown': true,
+ 'length-zero-no-unit': [true, {ignore: ['custom-properties'], ignoreFunctions: ['var']}],
'media-feature-name-no-vendor-prefix': true,
- 'media-feature-name-unit-allowed-list': null,
- 'media-feature-name-value-allowed-list': null,
- 'media-feature-name-value-no-unknown': true,
- 'media-feature-range-notation': null,
- 'media-query-no-invalid': true,
- 'named-grid-areas-no-invalid': true,
'no-descending-specificity': null,
- 'no-duplicate-at-import-rules': true,
- 'no-duplicate-selectors': true,
- 'no-empty-source': true,
- 'no-invalid-double-slash-comments': true,
'no-invalid-position-at-import-rule': [true, {ignoreAtRules: ['tailwind']}],
- 'no-irregular-whitespace': true,
'no-unknown-animations': null, // disabled until stylelint supports multi-file linting
'no-unknown-custom-media': null, // disabled until stylelint supports multi-file linting
'no-unknown-custom-properties': null, // disabled until stylelint supports multi-file linting
- 'number-max-precision': null,
'plugin/declaration-block-no-ignored-properties': true,
- 'property-allowed-list': null,
- 'property-disallowed-list': null,
- 'property-no-unknown': true,
- 'property-no-vendor-prefix': null,
- 'rule-empty-line-before': null,
- 'rule-selector-property-disallowed-list': null,
- 'scale-unlimited/declaration-strict-value': [['/color$/', 'font-weight'], {ignoreValues: '/^(inherit|transparent|unset|initial|currentcolor|none)$/', ignoreFunctions: false, disableFix: true, expandShorthand: true}],
- 'selector-anb-no-unmatchable': true,
- 'selector-attribute-name-disallowed-list': null,
- 'selector-attribute-operator-allowed-list': null,
- 'selector-attribute-operator-disallowed-list': null,
+ 'scale-unlimited/declaration-strict-value': [['/color$/', 'font-weight'], {ignoreValues: '/^(inherit|transparent|unset|initial|currentcolor|none)$/', ignoreFunctions: true, disableFix: true, expandShorthand: true}],
'selector-attribute-quotes': 'always',
- 'selector-class-pattern': null,
- 'selector-combinator-allowed-list': null,
- 'selector-combinator-disallowed-list': null,
- 'selector-disallowed-list': null,
- 'selector-id-pattern': null,
- 'selector-max-attribute': null,
- 'selector-max-class': null,
- 'selector-max-combinators': null,
- 'selector-max-compound-selectors': null,
- 'selector-max-id': null,
- 'selector-max-pseudo-class': null,
- 'selector-max-specificity': null,
- 'selector-max-type': null,
- 'selector-max-universal': null,
- 'selector-nested-pattern': null,
- 'selector-no-qualifying-type': null,
'selector-no-vendor-prefix': true,
- 'selector-not-notation': null,
- 'selector-pseudo-class-allowed-list': null,
- 'selector-pseudo-class-disallowed-list': null,
- 'selector-pseudo-class-no-unknown': true,
- 'selector-pseudo-element-allowed-list': null,
'selector-pseudo-element-colon-notation': 'double',
- 'selector-pseudo-element-disallowed-list': null,
- 'selector-pseudo-element-no-unknown': true,
'selector-type-case': 'lower',
'selector-type-no-unknown': [true, {ignore: ['custom-elements']}],
'shorthand-property-no-redundant-values': true,
- 'string-no-newline': true,
- 'time-min-milliseconds': null,
- 'unit-allowed-list': null,
- 'unit-disallowed-list': null,
- 'unit-no-unknown': true,
- 'value-keyword-case': null,
'value-no-vendor-prefix': [true, {ignoreValues: ['box', 'inline-box']}],
},
-};
+});
diff --git a/tailwind.config.js b/tailwind.config.js
index fe285432f3..fa233a9814 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -29,11 +29,10 @@ export default {
important: true, // the frameworks are mixed together, so tailwind needs to override other framework's styles
content: [
isProduction && '!./templates/devtest/**/*',
- isProduction && '!./web_src/js/standalone/devtest.js',
+ isProduction && '!./web_src/js/standalone/devtest.ts',
'!./templates/swagger/v1_json.tmpl',
'!./templates/user/auth/oidc_wellknown.tmpl',
'!**/*_test.go',
- '!./modules/{public,options,templates}/bindata.go',
'./{build,models,modules,routers,services}/**/*.go',
'./templates/**/*.tmpl',
'./web_src/js/**/*.{ts,js,vue}',
diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl
index 660f0d0881..781f514af4 100644
--- a/templates/admin/auth/edit.tmpl
+++ b/templates/admin/auth/edit.tmpl
@@ -15,7 +15,14 @@
</div>
<div class="required inline field {{if .Err_Name}}error{{end}}">
<label for="auth_name">{{ctx.Locale.Tr "admin.auths.auth_name"}}</label>
- <input id="auth_name" name="name" value="{{.Source.Name}}" autofocus required>
+ <input id="auth_name" name="name" value="{{.Source.Name}}" required>
+ </div>
+ <div class="inline field">
+ <div class="ui checkbox">
+ <label ><strong>{{ctx.Locale.Tr "admin.auths.skip_local_two_fa"}}</strong></label>
+ <input name="two_factor_policy" type="checkbox" value="skip" {{if eq .Source.TwoFactorPolicy "skip"}}checked{{end}}>
+ <p class="help">{{ctx.Locale.Tr "admin.auths.skip_local_two_fa_helper"}}</p>
+ </div>
</div>
<!-- LDAP and DLDAP -->
@@ -159,13 +166,6 @@
</div>
</div>
{{end}}
- <div class="optional field">
- <div class="ui checkbox">
- <label for="skip_local_two_fa"><strong>{{ctx.Locale.Tr "admin.auths.skip_local_two_fa"}}</strong></label>
- <input id="skip_local_two_fa" name="skip_local_two_fa" type="checkbox" {{if $cfg.SkipLocalTwoFA}}checked{{end}}>
- <p class="help">{{ctx.Locale.Tr "admin.auths.skip_local_two_fa_helper"}}</p>
- </div>
- </div>
<div class="inline field">
<div class="ui checkbox">
<label for="allow_deactivate_all"><strong>{{ctx.Locale.Tr "admin.auths.allow_deactivate_all"}}</strong></label>
@@ -227,13 +227,6 @@
<input id="allowed_domains" name="allowed_domains" value="{{$cfg.AllowedDomains}}">
<p class="help">{{ctx.Locale.Tr "admin.auths.allowed_domains_helper"}}</p>
</div>
- <div class="optional field">
- <div class="ui checkbox">
- <label for="skip_local_two_fa"><strong>{{ctx.Locale.Tr "admin.auths.skip_local_two_fa"}}</strong></label>
- <input id="skip_local_two_fa" name="skip_local_two_fa" type="checkbox" {{if $cfg.SkipLocalTwoFA}}checked{{end}}>
- <p class="help">{{ctx.Locale.Tr "admin.auths.skip_local_two_fa_helper"}}</p>
- </div>
- </div>
{{end}}
<!-- PAM -->
@@ -247,13 +240,6 @@
<label for="pam_email_domain">{{ctx.Locale.Tr "admin.auths.pam_email_domain"}}</label>
<input id="pam_email_domain" name="pam_email_domain" value="{{$cfg.EmailDomain}}">
</div>
- <div class="optional field">
- <div class="ui checkbox">
- <label for="skip_local_two_fa"><strong>{{ctx.Locale.Tr "admin.auths.skip_local_two_fa"}}</strong></label>
- <input id="skip_local_two_fa" name="skip_local_two_fa" type="checkbox" {{if $cfg.SkipLocalTwoFA}}checked{{end}}>
- <p class="help">{{ctx.Locale.Tr "admin.auths.skip_local_two_fa_helper"}}</p>
- </div>
- </div>
{{end}}
<!-- OAuth2 -->
@@ -288,13 +274,6 @@
<label for="open_id_connect_auto_discovery_url">{{ctx.Locale.Tr "admin.auths.openIdConnectAutoDiscoveryURL"}}</label>
<input id="open_id_connect_auto_discovery_url" name="open_id_connect_auto_discovery_url" value="{{$cfg.OpenIDConnectAutoDiscoveryURL}}">
</div>
- <div class="optional field">
- <div class="ui checkbox">
- <label for="skip_local_two_fa"><strong>{{ctx.Locale.Tr "admin.auths.skip_local_two_fa"}}</strong></label>
- <input id="skip_local_two_fa" name="skip_local_two_fa" type="checkbox" {{if $cfg.SkipLocalTwoFA}}checked{{end}}>
- <p class="help">{{ctx.Locale.Tr "admin.auths.skip_local_two_fa_helper"}}</p>
- </div>
- </div>
<div class="oauth2_use_custom_url inline field">
<div class="ui checkbox">
<label><strong>{{ctx.Locale.Tr "admin.auths.oauth2_use_custom_url"}}</strong></label>
@@ -429,7 +408,10 @@
<div class="field">
<button class="ui primary button">{{ctx.Locale.Tr "admin.auths.update"}}</button>
- <button class="ui red button delete-button" data-url="{{$.Link}}/delete" data-id="{{.Source.ID}}">{{ctx.Locale.Tr "admin.auths.delete"}}</button>
+ <button class="ui red button link-action" data-url="{{$.Link}}/delete?id={{.Source.ID}}"
+ data-modal-confirm-header="{{ctx.Locale.Tr "admin.auths.delete_auth_title"}}"
+ data-modal-confirm-content="{{ctx.Locale.Tr "admin.auths.delete_auth_desc"}}"
+ >{{ctx.Locale.Tr "admin.auths.delete"}}</button>
</div>
</form>
</div>
@@ -445,16 +427,4 @@
<p class="oauth2">{{ctx.Locale.Tr "admin.auths.tips.oauth2.general.tip"}} <b id="oauth2-callback-url"></b></p>
</div>
</div>
-
-<div class="ui g-modal-confirm delete modal">
- <div class="header">
- {{svg "octicon-trash"}}
- {{ctx.Locale.Tr "admin.auths.delete_auth_title"}}
- </div>
- <div class="content">
- <p>{{ctx.Locale.Tr "admin.auths.delete_auth_desc"}}</p>
- </div>
- {{template "base/modal_actions_confirm" .}}
-</div>
-
{{template "admin/layout_footer" .}}
diff --git a/templates/admin/auth/list.tmpl b/templates/admin/auth/list.tmpl
index 7931014b1a..a1e72b742f 100644
--- a/templates/admin/auth/list.tmpl
+++ b/templates/admin/auth/list.tmpl
@@ -30,6 +30,8 @@
<td>{{DateUtils.AbsoluteShort .CreatedUnix}}</td>
<td><a href="{{AppSubUrl}}/-/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td>
</tr>
+ {{else}}
+ <tr><td class="tw-text-center" colspan="7">{{ctx.Locale.Tr "no_results_found"}}</td></tr>
{{end}}
</tbody>
</table>
diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl
index 29a5e1b473..806347c720 100644
--- a/templates/admin/config.tmpl
+++ b/templates/admin/config.tmpl
@@ -69,10 +69,6 @@
{{if not .SSH.StartBuiltinServer}}
<dt>{{ctx.Locale.Tr "admin.config.ssh_root_path"}}</dt>
<dd>{{.SSH.RootPath}}</dd>
- <dt>{{ctx.Locale.Tr "admin.config.ssh_key_test_path"}}</dt>
- <dd>{{.SSH.KeyTestPath}}</dd>
- <dt>{{ctx.Locale.Tr "admin.config.ssh_keygen_path"}}</dt>
- <dd>{{.SSH.KeygenPath}}</dd>
<dt>{{ctx.Locale.Tr "admin.config.ssh_minimum_key_size_check"}}</dt>
<dd>{{svg (Iif .SSH.MinimumKeySizeCheck "octicon-check" "octicon-x")}}</dd>
{{if .SSH.MinimumKeySizeCheck}}
@@ -148,7 +144,7 @@
<dt>{{ctx.Locale.Tr "admin.config.enable_openid_signin"}}</dt>
<dd>{{svg (Iif .Service.EnableOpenIDSignIn "octicon-check" "octicon-x")}}</dd>
<dt>{{ctx.Locale.Tr "admin.config.require_sign_in_view"}}</dt>
- <dd>{{svg (Iif .Service.RequireSignInView "octicon-check" "octicon-x")}}</dd>
+ <dd>{{svg (Iif .Service.RequireSignInViewStrict "octicon-check" "octicon-x")}}</dd>
<dt>{{ctx.Locale.Tr "admin.config.mail_notify"}}</dt>
<dd>{{svg (Iif .Service.EnableNotifyMail "octicon-check" "octicon-x")}}</dd>
<dt>{{ctx.Locale.Tr "admin.config.enable_captcha"}}</dt>
diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl
index af2349d288..2426a43b15 100644
--- a/templates/admin/dashboard.tmpl
+++ b/templates/admin/dashboard.tmpl
@@ -15,57 +15,57 @@
<tbody>
<tr>
<td>{{ctx.Locale.Tr "admin.dashboard.delete_inactive_accounts"}}</td>
- <td class="text right"><button type="submit" class="ui primary button" name="op" value="delete_inactive_accounts">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
+ <td class="tw-text-right"><button type="submit" class="ui primary button" name="op" value="delete_inactive_accounts">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr>
<tr>
<td>{{ctx.Locale.Tr "admin.dashboard.delete_repo_archives"}}</td>
- <td class="text right"><button type="submit" class="ui primary button" name="op" value="delete_repo_archives">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
+ <td class="tw-text-right"><button type="submit" class="ui primary button" name="op" value="delete_repo_archives">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr>
<tr>
<td>{{ctx.Locale.Tr "admin.dashboard.delete_missing_repos"}}</td>
- <td class="text right"><button type="submit" class="ui primary button" name="op" value="delete_missing_repos">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
+ <td class="tw-text-right"><button type="submit" class="ui primary button" name="op" value="delete_missing_repos">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr>
<tr>
<td>{{ctx.Locale.Tr "admin.dashboard.git_gc_repos"}}</td>
- <td class="text right"><button type="submit" class="ui primary button" name="op" value="git_gc_repos">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
+ <td class="tw-text-right"><button type="submit" class="ui primary button" name="op" value="git_gc_repos">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr>
{{if and (not .SSH.Disabled) (not .SSH.StartBuiltinServer)}}
<tr>
<td>{{ctx.Locale.Tr "admin.dashboard.resync_all_sshkeys"}}</td>
- <td class="text right"><button type="submit" class="ui primary button" name="op" value="resync_all_sshkeys">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
+ <td class="tw-text-right"><button type="submit" class="ui primary button" name="op" value="resync_all_sshkeys">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr>
<tr>
<td>{{ctx.Locale.Tr "admin.dashboard.resync_all_sshprincipals"}}</td>
- <td class="text right"><button type="submit" class="ui primary button" name="op" value="resync_all_sshprincipals">{{svg "octicon-play" 16}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
+ <td class="tw-text-right"><button type="submit" class="ui primary button" name="op" value="resync_all_sshprincipals">{{svg "octicon-play" 16}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr>
{{end}}
<tr>
<td>{{ctx.Locale.Tr "admin.dashboard.resync_all_hooks"}}</td>
- <td class="text right"><button type="submit" class="ui primary button" name="op" value="resync_all_hooks">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
+ <td class="tw-text-right"><button type="submit" class="ui primary button" name="op" value="resync_all_hooks">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr>
<tr>
<td>{{ctx.Locale.Tr "admin.dashboard.reinit_missing_repos"}}</td>
- <td class="text right"><button type="submit" class="ui primary button" name="op" value="reinit_missing_repos">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
+ <td class="tw-text-right"><button type="submit" class="ui primary button" name="op" value="reinit_missing_repos">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr>
<tr>
<td>{{ctx.Locale.Tr "admin.dashboard.sync_external_users"}}</td>
- <td class="text right"><button type="submit" class="ui primary button" name="op" value="sync_external_users">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
+ <td class="tw-text-right"><button type="submit" class="ui primary button" name="op" value="sync_external_users">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr>
<tr>
<td>{{ctx.Locale.Tr "admin.dashboard.repo_health_check"}}</td>
- <td class="text right"><button type="submit" class="ui primary button" name="op" value="repo_health_check">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
+ <td class="tw-text-right"><button type="submit" class="ui primary button" name="op" value="repo_health_check">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr>
<tr>
<td>{{ctx.Locale.Tr "admin.dashboard.delete_generated_repository_avatars"}}</td>
- <td class="text right"><button type="submit" class="ui primary button" name="op" value="delete_generated_repository_avatars">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
+ <td class="tw-text-right"><button type="submit" class="ui primary button" name="op" value="delete_generated_repository_avatars">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr>
<tr>
<td>{{ctx.Locale.Tr "admin.dashboard.sync_repo_branches"}}</td>
- <td class="text right"><button type="submit" class="ui primary button" name="op" value="sync_repo_branches">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
+ <td class="tw-text-right"><button type="submit" class="ui primary button" name="op" value="sync_repo_branches">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr>
<tr>
<td>{{ctx.Locale.Tr "admin.dashboard.sync_repo_tags"}}</td>
- <td class="text right"><button type="submit" class="ui primary button" name="op" value="sync_repo_tags">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
+ <td class="tw-text-right"><button type="submit" class="ui primary button" name="op" value="sync_repo_tags">{{svg "octicon-play"}} {{ctx.Locale.Tr "admin.dashboard.operation_run"}}</button></td>
</tr>
</tbody>
</table>
diff --git a/templates/admin/emails/list.tmpl b/templates/admin/emails/list.tmpl
index 0dc1fb9d03..b4335aeeec 100644
--- a/templates/admin/emails/list.tmpl
+++ b/templates/admin/emails/list.tmpl
@@ -67,6 +67,8 @@
>{{svg "octicon-trash"}}</a>
</td>
</tr>
+ {{else}}
+ <tr><td class="tw-text-center" colspan="6">{{ctx.Locale.Tr "no_results_found"}}</td></tr>
{{end}}
</tbody>
</table>
diff --git a/templates/admin/hooks.tmpl b/templates/admin/hooks.tmpl
index c77d27dbd0..d5fdef6850 100644
--- a/templates/admin/hooks.tmpl
+++ b/templates/admin/hooks.tmpl
@@ -1,9 +1,6 @@
{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin hooks")}}
<div class="admin-setting-content">
-
{{template "repo/settings/webhook/base_list" .SystemWebhooks}}
{{template "repo/settings/webhook/base_list" .DefaultWebhooks}}
-
- {{template "repo/settings/webhook/delete_modal" .}}
</div>
{{template "admin/layout_footer" .}}
diff --git a/templates/admin/navbar.tmpl b/templates/admin/navbar.tmpl
index 4116357d1d..72584ec799 100644
--- a/templates/admin/navbar.tmpl
+++ b/templates/admin/navbar.tmpl
@@ -95,7 +95,7 @@
<a class="{{if .PageIsAdminNotices}}active {{end}}item" href="{{AppSubUrl}}/-/admin/notices">
{{ctx.Locale.Tr "admin.notices"}}
</a>
- <details class="item toggleable-item" {{if or .PageIsAdminMonitorStats .PageIsAdminMonitorCron .PageIsAdminMonitorQueue .PageIsAdminMonitorStacktrace}}open{{end}}>
+ <details class="item toggleable-item" {{if or .PageIsAdminMonitorStats .PageIsAdminMonitorCron .PageIsAdminMonitorQueue .PageIsAdminMonitorTrace}}open{{end}}>
<summary>{{ctx.Locale.Tr "admin.monitor"}}</summary>
<div class="menu">
<a class="{{if .PageIsAdminMonitorStats}}active {{end}}item" href="{{AppSubUrl}}/-/admin/monitor/stats">
@@ -107,8 +107,8 @@
<a class="{{if .PageIsAdminMonitorQueue}}active {{end}}item" href="{{AppSubUrl}}/-/admin/monitor/queue">
{{ctx.Locale.Tr "admin.monitor.queues"}}
</a>
- <a class="{{if .PageIsAdminMonitorStacktrace}}active {{end}}item" href="{{AppSubUrl}}/-/admin/monitor/stacktrace">
- {{ctx.Locale.Tr "admin.monitor.stacktrace"}}
+ <a class="{{if .PageIsAdminMonitorTrace}}active {{end}}item" href="{{AppSubUrl}}/-/admin/monitor/stacktrace">
+ {{ctx.Locale.Tr "admin.monitor.trace"}}
</a>
</div>
</details>
diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl
index fd475d7157..a4c9dc53fb 100644
--- a/templates/admin/notice.tmpl
+++ b/templates/admin/notice.tmpl
@@ -24,6 +24,8 @@
<td nowrap>{{DateUtils.AbsoluteShort .CreatedUnix}}</td>
<td class="view-detail"><a href="#">{{svg "octicon-note" 16}}</a></td>
</tr>
+ {{else}}
+ <tr><td class="tw-text-center" colspan="6">{{ctx.Locale.Tr "no_results_found"}}</td></tr>
{{end}}
</tbody>
{{if .Notices}}
diff --git a/templates/admin/org/list.tmpl b/templates/admin/org/list.tmpl
index d5e09939c5..137c42b45d 100644
--- a/templates/admin/org/list.tmpl
+++ b/templates/admin/org/list.tmpl
@@ -66,6 +66,8 @@
<td>{{DateUtils.AbsoluteShort .CreatedUnix}}</td>
<td><a href="{{.OrganisationLink}}/settings" data-tooltip-content="{{ctx.Locale.Tr "edit"}}">{{svg "octicon-pencil"}}</a></td>
</tr>
+ {{else}}
+ <tr><td class="tw-text-center" colspan="7">{{ctx.Locale.Tr "no_results_found"}}</td></tr>
{{end}}
</tbody>
</table>
diff --git a/templates/admin/packages/list.tmpl b/templates/admin/packages/list.tmpl
index 08c11442bc..4817f2681b 100644
--- a/templates/admin/packages/list.tmpl
+++ b/templates/admin/packages/list.tmpl
@@ -72,8 +72,15 @@
</td>
<td>{{FileSize .CalculateBlobSize}}</td>
<td>{{DateUtils.AbsoluteShort .Version.CreatedUnix}}</td>
- <td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.Version.ID}}" data-name="{{.Package.Name}}" data-data-version="{{.Version.Version}}">{{svg "octicon-trash"}}</a></td>
+ <td>
+ <a class="text red show-modal" href data-modal="#admin-package-delete-modal"
+ data-modal-form.action="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}&id={{.Version.ID}}"
+ data-modal-package-name="{{.Package.Name}}" data-modal-package-version="{{.Version.Version}}"
+ >{{svg "octicon-trash"}}</a>
+ </td>
</tr>
+ {{else}}
+ <tr><td class="tw-text-center" colspan="10">{{ctx.Locale.Tr "no_results_found"}}</td></tr>
{{end}}
</tbody>
</table>
@@ -82,15 +89,13 @@
{{template "base/paginate" .}}
</div>
-<div class="ui g-modal-confirm delete modal">
- <div class="header">
- {{svg "octicon-trash"}}
- {{ctx.Locale.Tr "packages.settings.delete"}}
- </div>
+<form class="ui small modal form-fetch-action" method="post" id="admin-package-delete-modal">
+ {{.CsrfTokenHtml}}
+ <div class="header">{{svg "octicon-trash"}} {{ctx.Locale.Tr "packages.settings.delete"}}</div>
<div class="content">
- {{ctx.Locale.Tr "packages.settings.delete.notice" (`<span class="name"></span>`|SafeHTML) (`<span class="dataVersion"></span>`|SafeHTML)}}
+ {{ctx.Locale.Tr "packages.settings.delete.notice" (HTMLFormat `<span class="%s"></span>` "package-name") (HTMLFormat `<span class="%s"></span>` "package-version")}}
</div>
{{template "base/modal_actions_confirm" .}}
-</div>
+</form>
{{template "admin/layout_footer" .}}
diff --git a/templates/admin/perftrace.tmpl b/templates/admin/perftrace.tmpl
new file mode 100644
index 0000000000..2e09f14e46
--- /dev/null
+++ b/templates/admin/perftrace.tmpl
@@ -0,0 +1,13 @@
+{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin monitor")}}
+
+<div class="admin-setting-content">
+ {{template "admin/trace_tabs" .}}
+
+ {{range $record := .PerfTraceRecords}}
+ <div class="ui segment tw-w-full tw-overflow-auto">
+ <pre class="tw-whitespace-pre">{{$record.Content}}</pre>
+ </div>
+ {{end}}
+</div>
+
+{{template "admin/layout_footer" .}}
diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl
index 08fd893e76..2ad2de3a1d 100644
--- a/templates/admin/repo/list.tmpl
+++ b/templates/admin/repo/list.tmpl
@@ -7,7 +7,7 @@
</div>
</h4>
<div class="ui attached segment">
- {{template "shared/repo_search" .}}
+ {{template "shared/repo/search" .}}
</div>
<div class="ui attached table segment">
<table class="ui very basic striped table unstackable">
@@ -84,8 +84,15 @@
<td>{{FileSize .LFSSize}}</td>
<td>{{DateUtils.AbsoluteShort .UpdatedUnix}}</td>
<td>{{DateUtils.AbsoluteShort .CreatedUnix}}</td>
- <td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.ID}}" data-name="{{.Name}}">{{svg "octicon-trash"}}</a></td>
+ <td>
+ <a class="text red show-modal" href data-modal="#admin-repo-delete-modal"
+ data-modal-form.action="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}&id={{.ID}}"
+ data-modal-repo-name="{{.Name}}"
+ >{{svg "octicon-trash"}}</a>
+ </td>
</tr>
+ {{else}}
+ <tr><td class="tw-text-center" colspan="12">{{ctx.Locale.Tr "no_results_found"}}</td></tr>
{{end}}
</tbody>
</table>
@@ -94,17 +101,15 @@
{{template "base/paginate" .}}
</div>
-<div class="ui g-modal-confirm delete modal">
- <div class="header">
- {{svg "octicon-trash"}}
- {{ctx.Locale.Tr "repo.settings.delete"}}
- </div>
+<form class="ui small modal form-fetch-action" id="admin-repo-delete-modal" method="post">
+ {{.CsrfTokenHtml}}
+ <div class="header">{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.settings.delete"}}</div>
<div class="content">
<p>{{ctx.Locale.Tr "repo.settings.delete_desc"}}</p>
- {{ctx.Locale.Tr "repo.settings.delete_notices_2" (`<span class="name"></span>`|SafeHTML)}}<br>
+ {{ctx.Locale.Tr "repo.settings.delete_notices_2" (HTMLFormat `<span class="%s"></span>` "repo-name")}}<br>
{{ctx.Locale.Tr "repo.settings.delete_notices_fork_1"}}<br>
</div>
{{template "base/modal_actions_confirm" .}}
-</div>
+</form>
{{template "admin/layout_footer" .}}
diff --git a/templates/admin/stacktrace-row.tmpl b/templates/admin/stacktrace-row.tmpl
index 97c361ff90..db7ed81c79 100644
--- a/templates/admin/stacktrace-row.tmpl
+++ b/templates/admin/stacktrace-row.tmpl
@@ -17,7 +17,10 @@
</div>
<div>
{{if or (eq .Process.Type "request") (eq .Process.Type "normal")}}
- <a class="delete-button icon" href="" data-url="{{.root.Link}}/cancel/{{.Process.PID}}" data-id="{{.Process.PID}}" data-name="{{.Process.Description}}">{{svg "octicon-trash" 16 "text-red"}}</a>
+ <a class="link-action" data-url="{{.root.Link}}/cancel/{{.Process.PID}}"
+ data-modal-confirm-header="{{ctx.Locale.Tr "admin.monitor.process.cancel"}}"
+ data-modal-confirm-content="{{ctx.Locale.Tr "admin.monitor.process.cancel_desc"}}"
+ >{{svg "octicon-trash" 16 "text-red"}}</a>
{{end}}
</div>
</div>
diff --git a/templates/admin/stacktrace.tmpl b/templates/admin/stacktrace.tmpl
index ce03d80555..c5dde6b30c 100644
--- a/templates/admin/stacktrace.tmpl
+++ b/templates/admin/stacktrace.tmpl
@@ -1,22 +1,7 @@
{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin monitor")}}
<div class="admin-setting-content">
- <div class="tw-flex tw-items-center">
- <div class="tw-flex-1">
- <div class="ui compact small menu">
- <a class="{{if eq .ShowGoroutineList "process"}}active {{end}}item" href="?show=process">{{ctx.Locale.Tr "admin.monitor.process"}}</a>
- <a class="{{if eq .ShowGoroutineList "stacktrace"}}active {{end}}item" href="?show=stacktrace">{{ctx.Locale.Tr "admin.monitor.stacktrace"}}</a>
- </div>
- </div>
- <form target="_blank" action="{{AppSubUrl}}/-/admin/monitor/diagnosis" class="ui form">
- <div class="ui inline field">
- <button class="ui primary small button">{{ctx.Locale.Tr "admin.monitor.download_diagnosis_report"}}</button>
- <input name="seconds" size="3" maxlength="3" value="10"> {{ctx.Locale.Tr "tool.raw_seconds"}}
- </div>
- </form>
- </div>
-
- <div class="divider"></div>
+ {{template "admin/trace_tabs" .}}
<h4 class="ui top attached header">
{{printf "%d Goroutines" .GoroutineCount}}{{/* Goroutine is non-translatable*/}}
@@ -34,15 +19,4 @@
{{end}}
</div>
-<div class="ui g-modal-confirm delete modal">
- <div class="header">
- {{ctx.Locale.Tr "admin.monitor.process.cancel"}}
- </div>
- <div class="content">
- <p>{{ctx.Locale.Tr "admin.monitor.process.cancel_notices" (`<span class="name"></span>`|SafeHTML)}}</p>
- <p>{{ctx.Locale.Tr "admin.monitor.process.cancel_desc"}}</p>
- </div>
- {{template "base/modal_actions_confirm" .}}
-</div>
-
{{template "admin/layout_footer" .}}
diff --git a/templates/admin/trace_tabs.tmpl b/templates/admin/trace_tabs.tmpl
new file mode 100644
index 0000000000..5066c9c41b
--- /dev/null
+++ b/templates/admin/trace_tabs.tmpl
@@ -0,0 +1,19 @@
+<div class="flex-text-block">
+ <div class="tw-flex-1">
+ <div class="ui compact small menu">
+ {{if .ShowAdminPerformanceTraceTab}}
+ <a class="item {{Iif .PageIsAdminMonitorPerfTrace "active"}}" href="{{AppSubUrl}}/-/admin/monitor/perftrace">{{ctx.Locale.Tr "admin.monitor.performance_logs"}}</a>
+ {{end}}
+ <a class="item {{Iif (eq .ShowGoroutineList "process") "active"}}" href="{{AppSubUrl}}/-/admin/monitor/stacktrace?show=process">{{ctx.Locale.Tr "admin.monitor.process"}}</a>
+ <a class="item {{Iif (eq .ShowGoroutineList "stacktrace") "active"}}" href="{{AppSubUrl}}/-/admin/monitor/stacktrace?show=stacktrace">{{ctx.Locale.Tr "admin.monitor.stacktrace"}}</a>
+ </div>
+ </div>
+ <form target="_blank" action="{{AppSubUrl}}/-/admin/monitor/diagnosis" class="ui form">
+ <div class="ui inline field">
+ <button class="ui primary small button">{{ctx.Locale.Tr "admin.monitor.download_diagnosis_report"}}</button>
+ <input name="seconds" size="3" maxlength="3" value="10"> {{ctx.Locale.Tr "tool.raw_seconds"}}
+ </div>
+ </form>
+</div>
+
+<div class="divider"></div>
diff --git a/templates/admin/user/edit.tmpl b/templates/admin/user/edit.tmpl
index d591a645d8..879b5cb550 100644
--- a/templates/admin/user/edit.tmpl
+++ b/templates/admin/user/edit.tmpl
@@ -9,7 +9,7 @@
{{.CsrfTokenHtml}}
<div class="field {{if .Err_UserName}}error{{end}}">
<label for="user_name">{{ctx.Locale.Tr "username"}}</label>
- <input id="user_name" name="user_name" value="{{.User.Name}}" autofocus {{if not .User.IsLocal}}disabled{{end}} maxlength="40">
+ <input id="user_name" name="user_name" value="{{.User.Name}}" {{if not .User.IsLocal}}disabled{{end}} maxlength="40">
</div>
<!-- Types and name -->
<div class="inline required field {{if .Err_LoginType}}error{{end}}">
@@ -55,7 +55,7 @@
<div class="required non-local field {{if .Err_LoginName}}error{{end}} {{if eq .User.LoginSource 0}}tw-hidden{{end}}">
<label for="login_name">{{ctx.Locale.Tr "admin.users.auth_login_name"}}</label>
- <input id="login_name" name="login_name" value="{{.User.LoginName}}" autofocus>
+ <input id="login_name" name="login_name" value="{{.User.LoginName}}">
</div>
<div class="field {{if .Err_FullName}}error{{end}}">
<label for="full_name">{{ctx.Locale.Tr "settings.full_name"}}</label>
@@ -63,7 +63,7 @@
</div>
<div class="required field {{if .Err_Email}}error{{end}}">
<label for="email">{{ctx.Locale.Tr "email"}}</label>
- <input id="email" name="email" type="email" value="{{.User.Email}}" autofocus required>
+ <input id="email" name="email" type="email" value="{{.User.Email}}" required>
</div>
<div class="local field {{if .Err_Password}}error{{end}} {{if not (or (.User.IsLocal) (.User.IsOAuth2))}}tw-hidden{{end}}">
<label for="password">{{ctx.Locale.Tr "password"}}</label>
@@ -195,8 +195,7 @@
</div>
<div class="inline field tw-pl-4">
- <label for="avatar">{{ctx.Locale.Tr "settings.choose_new_avatar"}}</label>
- <input name="avatar" type="file" accept="image/png,image/jpeg,image/gif,image/webp">
+ {{template "shared/avatar_upload_crop" dict "LabelText" (ctx.Locale.Tr "settings.choose_new_avatar")}}
</div>
<div class="field">
diff --git a/templates/admin/user/list.tmpl b/templates/admin/user/list.tmpl
index 7e4c8854f5..eb3f6cd720 100644
--- a/templates/admin/user/list.tmpl
+++ b/templates/admin/user/list.tmpl
@@ -109,6 +109,8 @@
</div>
</td>
</tr>
+ {{else}}
+ <tr class="no-results-row"><td class="tw-text-center" colspan="9">{{ctx.Locale.Tr "no_results_found"}}</td></tr>
{{end}}
</tbody>
</table>
diff --git a/templates/admin/user/view.tmpl b/templates/admin/user/view.tmpl
index 21943a8382..67f9148e64 100644
--- a/templates/admin/user/view.tmpl
+++ b/templates/admin/user/view.tmpl
@@ -15,10 +15,7 @@
</div>
<div class="tw-flex-1">
<h4 class="ui top attached header">
- {{ctx.Locale.Tr "admin.emails"}}
- <div class="ui right">
- {{.EmailsTotal}}
- </div>
+ {{ctx.Locale.Tr "admin.emails"}} ({{ctx.Locale.Tr "admin.total" .EmailsTotal}})
</h4>
<div class="ui attached segment">
{{template "admin/user/view_emails" .}}
@@ -26,19 +23,13 @@
</div>
</div>
<h4 class="ui top attached header">
- {{ctx.Locale.Tr "admin.repositories"}}
- <div class="ui right">
- {{.ReposTotal}}
- </div>
+ {{ctx.Locale.Tr "admin.repositories"}} ({{ctx.Locale.Tr "admin.total" .ReposTotal}})
</h4>
<div class="ui attached segment">
- {{template "explore/repo_list" .}}
+ {{template "shared/repo/list" .}}
</div>
<h4 class="ui top attached header">
- {{ctx.Locale.Tr "settings.organization"}}
- <div class="ui right">
- {{.OrgsTotal}}
- </div>
+ {{ctx.Locale.Tr "settings.organization"}} ({{ctx.Locale.Tr "admin.total" .OrgsTotal}})
</h4>
<div class="ui attached segment">
{{template "explore/user_list" .}}
diff --git a/templates/admin/user/view_details.tmpl b/templates/admin/user/view_details.tmpl
index be2f32b5ec..db61bc9359 100644
--- a/templates/admin/user/view_details.tmpl
+++ b/templates/admin/user/view_details.tmpl
@@ -9,30 +9,25 @@
{{if .User.IsAdmin}}
<span class="ui basic label">{{ctx.Locale.Tr "admin.users.admin"}}</span>
{{end}}
+ {{if .User.IsTypeBot}}
+ <span class="ui basic label">{{ctx.Locale.Tr "admin.users.bot"}}</span>
+ {{end}}
</div>
<div class="flex-item-body">
<b>{{ctx.Locale.Tr "admin.users.auth_source"}}:</b>
- {{if eq .LoginSource.ID 0}}
- {{ctx.Locale.Tr "admin.users.local"}}
- {{else}}
- {{.LoginSource.Name}}
- {{end}}
+ {{Iif (eq .LoginSource.ID 0) (ctx.Locale.Tr "admin.users.local") .LoginSource.Name}}
</div>
<div class="flex-item-body">
<b>{{ctx.Locale.Tr "admin.users.activated"}}:</b>
- {{if .User.IsActive}}
- {{svg "octicon-check"}}
- {{else}}
- {{svg "octicon-x"}}
- {{end}}
+ {{svg (Iif .User.IsActive "octicon-check" "octicon-x")}}
+ </div>
+ <div class="flex-item-body">
+ <b>{{ctx.Locale.Tr "admin.users.prohibit_login"}}:</b>
+ {{svg (Iif .User.ProhibitLogin "octicon-check" "octicon-x")}}
</div>
<div class="flex-item-body">
<b>{{ctx.Locale.Tr "admin.users.restricted"}}:</b>
- {{if .User.IsRestricted}}
- {{svg "octicon-check"}}
- {{else}}
- {{svg "octicon-x"}}
- {{end}}
+ {{svg (Iif .User.IsRestricted "octicon-check" "octicon-x")}}
</div>
<div class="flex-item-body">
<b>{{ctx.Locale.Tr "settings.visibility"}}:</b>
@@ -42,11 +37,7 @@
</div>
<div class="flex-item-body">
<b>{{ctx.Locale.Tr "admin.users.2fa"}}:</b>
- {{if .TwoFactorEnabled}}
- <span class="text green">{{svg "octicon-check"}}</span>
- {{else}}
- {{svg "octicon-x"}}
- {{end}}
+ {{svg (Iif .TwoFactorEnabled "octicon-check" "octicon-x")}}
</div>
{{if .User.Language}}
<div class="flex-item-body">
diff --git a/templates/admin/user/view_emails.tmpl b/templates/admin/user/view_emails.tmpl
index 22ce305a88..7e77206f1c 100644
--- a/templates/admin/user/view_emails.tmpl
+++ b/templates/admin/user/view_emails.tmpl
@@ -3,7 +3,7 @@
<div class="flex-item">
<div class="flex-item-main">
<div class="flex-text-block">
- {{.Email}}
+ <a href="mailto:{{.Email}}">{{.Email}}</a>
{{if .IsPrimary}}
<div class="ui primary label">{{ctx.Locale.Tr "settings.primary"}}</div>
{{end}}
diff --git a/templates/base/alert.tmpl b/templates/base/alert.tmpl
index 760d3bfa2c..5ebe191771 100644
--- a/templates/base/alert.tmpl
+++ b/templates/base/alert.tmpl
@@ -1,20 +1,25 @@
-{{if .Flash.ErrorMsg}}
+{{- if .Flash.ErrorMsg -}}
<div class="ui negative message flash-message flash-error">
<p>{{.Flash.ErrorMsg | SanitizeHTML}}</p>
</div>
-{{end}}
-{{if .Flash.SuccessMsg}}
+{{- end -}}
+{{- if .Flash.SuccessMsg -}}
<div class="ui positive message flash-message flash-success">
<p>{{.Flash.SuccessMsg | SanitizeHTML}}</p>
</div>
-{{end}}
-{{if .Flash.InfoMsg}}
+{{- end -}}
+{{- if .Flash.InfoMsg -}}
<div class="ui info message flash-message flash-info">
<p>{{.Flash.InfoMsg | SanitizeHTML}}</p>
</div>
-{{end}}
-{{if .Flash.WarningMsg}}
+{{- end -}}
+{{- if .Flash.WarningMsg -}}
<div class="ui warning message flash-message flash-warning">
<p>{{.Flash.WarningMsg | SanitizeHTML}}</p>
</div>
-{{end}}
+{{- end -}}
+{{- if .ShowTwoFactorRequiredMessage -}}
+<div class="ui negative message flash-message flash-error">
+ <p><a href="{{AppSubUrl}}/user/settings/security/two_factor/enroll">{{ctx.Locale.Tr "auth.twofa_required"}}</a></p>
+</div>
+{{- end -}}
diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl
index fed426a469..3af66e7369 100644
--- a/templates/base/footer.tmpl
+++ b/templates/base/footer.tmpl
@@ -5,16 +5,10 @@
<div>
{{end}}
- {{template "custom/body_inner_post" .}}
-
+ {{template "custom/body_inner_post" .}}
</div>
-
{{template "custom/body_outer_post" .}}
-
{{template "base/footer_content" .}}
-
- <script src="{{AssetUrlPrefix}}/js/index.js?v={{AssetVersion}}" onerror="alert('Failed to load asset files from ' + this.src + '. Please make sure the asset files can be accessed.')"></script>
-
{{template "custom/footer" .}}
</body>
</html>
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl
index 174267fd2f..e9526818e3 100644
--- a/templates/base/head.tmpl
+++ b/templates/base/head.tmpl
@@ -19,12 +19,6 @@
<link rel="icon" href="{{AssetUrlPrefix}}/img/favicon.svg" type="image/svg+xml">
<link rel="alternate icon" href="{{AssetUrlPrefix}}/img/favicon.png" type="image/png">
{{template "base/head_script" .}}
- <noscript>
- <style>
- .dropdown:hover > .menu { display: block; }
- .ui.secondary.menu .dropdown.item > .menu { margin-top: 0; }
- </style>
- </noscript>
{{template "base/head_opengraph" .}}
{{template "base/head_style" .}}
{{template "custom/header" .}}
diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl
index bf0e7e632b..b721779c95 100644
--- a/templates/base/head_navbar.tmpl
+++ b/templates/base/head_navbar.tmpl
@@ -1,8 +1,3 @@
-{{$notificationUnreadCount := 0}}
-{{if and .IsSigned .NotificationUnreadCount}}
- {{$notificationUnreadCount = call .NotificationUnreadCount}}
-{{end}}
-
<nav id="navbar" aria-label="{{ctx.Locale.Tr "aria.navbar"}}">
<div class="navbar-left">
<!-- the logo -->
@@ -12,22 +7,7 @@
<!-- mobile right menu, it must be here because in mobile view, each item is a flex column, the first item is a full row column -->
<div class="ui secondary menu navbar-mobile-right only-mobile">
- {{if and .IsSigned EnableTimetracking .ActiveStopwatch}}
- <a id="mobile-stopwatch-icon" class="active-stopwatch item" href="{{.ActiveStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}" data-seconds="{{.ActiveStopwatch.Seconds}}">
- <div class="tw-relative">
- {{svg "octicon-stopwatch"}}
- <span class="header-stopwatch-dot"></span>
- </div>
- </a>
- {{end}}
- {{if .IsSigned}}
- <a id="mobile-notifications-icon" class="item" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}" aria-label="{{ctx.Locale.Tr "notifications"}}">
- <div class="tw-relative">
- {{svg "octicon-bell"}}
- <span class="notification_count{{if not $notificationUnreadCount}} tw-hidden{{end}}">{{$notificationUnreadCount}}</span>
- </div>
- </a>
- {{end}}
+ {{template "base/head_navbar_icons" dict "PageGlobalData" .PageGlobalData}}
<button class="item ui icon mini button tw-m-0" id="navbar-expand-toggle" aria-label="{{ctx.Locale.Tr "home.nav_menu"}}">{{svg "octicon-three-bars"}}</button>
</div>
@@ -70,7 +50,7 @@
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
</span>
<div class="menu user-menu">
- <div class="ui header">
+ <div class="header">
{{ctx.Locale.Tr "signed_in_as"}} <strong>{{.SignedUser.Name}}</strong>
</div>
@@ -82,22 +62,7 @@
</div><!-- end content avatar menu -->
</div><!-- end dropdown avatar menu -->
{{else if .IsSigned}}
- {{if and EnableTimetracking .ActiveStopwatch}}
- <a class="item not-mobile active-stopwatch" href="{{.ActiveStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}" data-seconds="{{.ActiveStopwatch.Seconds}}">
- <div class="tw-relative">
- {{svg "octicon-stopwatch"}}
- <span class="header-stopwatch-dot"></span>
- </div>
- </a>
- {{end}}
-
- <a class="item not-mobile" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}" aria-label="{{ctx.Locale.Tr "notifications"}}">
- <div class="tw-relative">
- {{svg "octicon-bell"}}
- <span class="notification_count{{if not $notificationUnreadCount}} tw-hidden{{end}}">{{$notificationUnreadCount}}</span>
- </div>
- </a>
-
+ {{template "base/head_navbar_icons" dict "ItemExtraClass" "not-mobile" "PageGlobalData" .PageGlobalData}}
<div class="ui dropdown jump item" data-tooltip-content="{{ctx.Locale.Tr "create_new"}}">
<span class="text">
{{svg "octicon-plus"}}
@@ -127,10 +92,8 @@
<span class="only-mobile">{{.SignedUser.Name}}</span>
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
</span>
- {{/* do not localize it, here it needs the fixed length (width) to make UI comfortable */}}
- {{if .IsAdmin}}<span class="navbar-profile-admin">admin</span>{{end}}
<div class="menu user-menu">
- <div class="ui header">
+ <div class="header">
{{ctx.Locale.Tr "signed_in_as"}} <strong>{{.SignedUser.Name}}</strong>
</div>
@@ -157,14 +120,6 @@
{{svg "octicon-question"}}
{{ctx.Locale.Tr "help"}}
</a>
- {{if .IsAdmin}}
- <div class="divider"></div>
- <a class="{{if .PageIsAdmin}}active {{end}}item" href="{{AppSubUrl}}/-/admin">
- {{svg "octicon-server"}}
- {{ctx.Locale.Tr "admin_panel"}}
- </a>
- {{end}}
-
<div class="divider"></div>
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout">
{{svg "octicon-sign-out"}}
@@ -175,24 +130,27 @@
{{else}}
{{if .ShowRegistrationButton}}
<a class="item{{if .PageIsSignUp}} active{{end}}" href="{{AppSubUrl}}/user/sign_up">
- {{svg "octicon-person"}} {{ctx.Locale.Tr "register"}}
+ {{svg "octicon-person"}}
+ <span class="tw-ml-1">{{ctx.Locale.Tr "register"}}</span>
</a>
{{end}}
<a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="{{AppSubUrl}}/user/login{{if not .PageIsSignIn}}?redirect_to={{.CurrentURL}}{{end}}">
- {{svg "octicon-sign-in"}} {{ctx.Locale.Tr "sign_in"}}
+ {{svg "octicon-sign-in"}}
+ <span class="tw-ml-1">{{ctx.Locale.Tr "sign_in"}}</span>
</a>
{{end}}
</div><!-- end full right menu -->
- {{if and .IsSigned EnableTimetracking .ActiveStopwatch}}
+ {{$activeStopwatch := and .PageGlobalData (call .PageGlobalData.GetActiveStopwatch)}}
+ {{if $activeStopwatch}}
<div class="active-stopwatch-popup tippy-target">
<div class="tw-flex tw-items-center tw-gap-2 tw-p-3">
- <a class="stopwatch-link tw-flex tw-items-center tw-gap-2 muted" href="{{.ActiveStopwatch.IssueLink}}">
+ <a class="stopwatch-link tw-flex tw-items-center tw-gap-2 muted" href="{{$activeStopwatch.IssueLink}}">
{{svg "octicon-issue-opened" 16}}
- <span class="stopwatch-issue">{{.ActiveStopwatch.RepoSlug}}#{{.ActiveStopwatch.IssueIndex}}</span>
+ <span class="stopwatch-issue">{{$activeStopwatch.RepoSlug}}#{{$activeStopwatch.IssueIndex}}</span>
</a>
<div class="tw-flex tw-gap-1">
- <form class="stopwatch-commit form-fetch-action" method="post" action="{{.ActiveStopwatch.IssueLink}}/times/stopwatch/toggle">
+ <form class="stopwatch-commit form-fetch-action" method="post" action="{{$activeStopwatch.IssueLink}}/times/stopwatch/stop">
{{.CsrfTokenHtml}}
<button
type="submit"
@@ -200,7 +158,7 @@
data-tooltip-content="{{ctx.Locale.Tr "repo.issues.stop_tracking"}}"
>{{svg "octicon-square-fill"}}</button>
</form>
- <form class="stopwatch-cancel form-fetch-action" method="post" action="{{.ActiveStopwatch.IssueLink}}/times/stopwatch/cancel">
+ <form class="stopwatch-cancel form-fetch-action" method="post" action="{{$activeStopwatch.IssueLink}}/times/stopwatch/cancel">
{{.CsrfTokenHtml}}
<button
type="submit"
diff --git a/templates/base/head_navbar_icons.tmpl b/templates/base/head_navbar_icons.tmpl
new file mode 100644
index 0000000000..e3bdd27992
--- /dev/null
+++ b/templates/base/head_navbar_icons.tmpl
@@ -0,0 +1,25 @@
+{{- $itemExtraClass := .ItemExtraClass -}}
+{{- $data := .PageGlobalData -}}
+{{if and $data $data.IsSigned}}{{/* data may not exist, for example: rendering 503 page before the PageGlobalData middleware */}}
+ {{- $activeStopwatch := call $data.GetActiveStopwatch -}}
+ {{- $notificationUnreadCount := call $data.GetNotificationUnreadCount -}}
+ {{if $activeStopwatch}}
+ <a class="item active-stopwatch {{$itemExtraClass}}" href="{{$activeStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}" data-seconds="{{$activeStopwatch.Seconds}}">
+ <div class="tw-relative">
+ {{svg "octicon-stopwatch"}}
+ <span class="header-stopwatch-dot"></span>
+ </div>
+ </a>
+ {{end}}
+ <a class="item {{$itemExtraClass}}" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}">
+ <div class="tw-relative">
+ {{svg "octicon-bell"}}
+ <span class="notification_count{{if not $notificationUnreadCount}} tw-hidden{{end}}">{{$notificationUnreadCount}}</span>
+ </div>
+ </a>
+ {{if $data.IsSiteAdmin}}
+ <a class="item {{$itemExtraClass}}" href="{{AppSubUrl}}/-/admin" data-tooltip-content="{{ctx.Locale.Tr "admin_panel"}}">
+ {{svg "octicon-server"}}
+ </a>
+ {{end}}
+{{end}}
diff --git a/templates/base/head_script.tmpl b/templates/base/head_script.tmpl
index 7c931e7404..f6648b59d8 100644
--- a/templates/base/head_script.tmpl
+++ b/templates/base/head_script.tmpl
@@ -46,4 +46,4 @@ If you introduce mistakes in it, Gitea JavaScript code wouldn't run correctly.
{{/* in case some pages don't render the pageData, we make sure it is an object to prevent null access */}}
window.config.pageData = window.config.pageData || {};
</script>
-<script src="{{AssetUrlPrefix}}/js/webcomponents.js?v={{AssetVersion}}"></script>
+<script src="{{AssetUrlPrefix}}/js/index.js?v={{AssetVersion}}" onerror="alert('Failed to load asset files from ' + this.src + '. Please make sure the asset files can be accessed.')"></script>
diff --git a/templates/base/paginate.tmpl b/templates/base/paginate.tmpl
index 9a7a6322f7..f6c1785ccf 100644
--- a/templates/base/paginate.tmpl
+++ b/templates/base/paginate.tmpl
@@ -2,32 +2,42 @@
{{$paginationLink := $.Link}}
{{if eq $paginationLink AppSubUrl}}{{$paginationLink = print $paginationLink "/"}}{{end}}
{{with .Page.Paginater}}
- {{if gt .TotalPages 1}}
+ {{if or (eq .TotalPages -1) (gt .TotalPages 1)}}
+ {{$showFirstLast := gt .TotalPages 1}}
<div class="center page buttons">
<div class="ui borderless pagination menu">
+ {{if $showFirstLast}}
<a class="{{if .IsFirst}}disabled{{end}} item navigation" {{if not .IsFirst}}href="{{$paginationLink}}{{if $paginationParams}}?{{$paginationParams}}{{end}}"{{end}}>
{{svg "gitea-double-chevron-left" 16 "tw-mr-1"}}
<span class="navigation_label">{{ctx.Locale.Tr "admin.first_page"}}</span>
</a>
+ {{end}}
+
<a class="{{if not .HasPrevious}}disabled{{end}} item navigation" {{if .HasPrevious}}href="{{$paginationLink}}?page={{.Previous}}{{if $paginationParams}}&{{$paginationParams}}{{end}}"{{end}}>
{{svg "octicon-chevron-left" 16 "tw-mr-1"}}
<span class="navigation_label">{{ctx.Locale.Tr "repo.issues.previous"}}</span>
</a>
- {{range .Pages}}
+ {{$pages := .Pages}}
+ {{$pagesLen := len $pages}}
+ {{range $pages}}
{{if eq .Num -1}}
<a class="disabled item">...</a>
{{else}}
- <a class="{{if .IsCurrent}}active {{end}}item tw-content-center" {{if not .IsCurrent}}href="{{$paginationLink}}?page={{.Num}}{{if $paginationParams}}&{{$paginationParams}}{{end}}"{{end}}>{{.Num}}</a>
+ {{/* do not highlight the current page if there is only one page */}}
+ <a class="{{if and .IsCurrent (gt $pagesLen 1)}}active {{end}}item" {{if not .IsCurrent}}href="{{$paginationLink}}?page={{.Num}}{{if $paginationParams}}&{{$paginationParams}}{{end}}"{{end}}>{{.Num}}</a>
{{end}}
{{end}}
<a class="{{if not .HasNext}}disabled{{end}} item navigation" {{if .HasNext}}href="{{$paginationLink}}?page={{.Next}}{{if $paginationParams}}&{{$paginationParams}}{{end}}"{{end}}>
<span class="navigation_label">{{ctx.Locale.Tr "repo.issues.next"}}</span>
{{svg "octicon-chevron-right" 16 "tw-ml-1"}}
</a>
+
+ {{if $showFirstLast}}
<a class="{{if .IsLast}}disabled{{end}} item navigation" {{if not .IsLast}}href="{{$paginationLink}}?page={{.TotalPages}}{{if $paginationParams}}&{{$paginationParams}}{{end}}"{{end}}>
<span class="navigation_label">{{ctx.Locale.Tr "admin.last_page"}}</span>
{{svg "gitea-double-chevron-right" 16 "tw-ml-1"}}
</a>
+ {{end}}
</div>
</div>
{{end}}
diff --git a/templates/devtest/badge-actions-svg.tmpl b/templates/devtest/badge-actions-svg.tmpl
new file mode 100644
index 0000000000..5be4fb3131
--- /dev/null
+++ b/templates/devtest/badge-actions-svg.tmpl
@@ -0,0 +1,25 @@
+{{template "devtest/devtest-header"}}
+<div class="page-content devtest ui container">
+ <div>
+ <h1>Actions SVG</h1>
+ <form class="tw-my-3">
+ <div class="tw-mb-2">
+ {{range $fontName := .BadgeFontFamilyNames}}
+ <label><input name="font" type="radio" value="{{$fontName}}" {{Iif (eq $.SelectedFontFamilyName $fontName) "checked"}}>{{$fontName}}</label>
+ {{end}}
+ </div>
+ <div class="tw-mb-2">
+ {{range $style := .BadgeStyles}}
+ <label><input name="style" type="radio" value="{{$style}}" {{Iif (eq $.SelectedStyle $style) "checked"}}>{{$style}}</label>
+ {{end}}
+ </div>
+ <button>submit</button>
+ </form>
+ <div class="flex-text-block tw-flex-wrap">
+ {{range $badgeSVG := .BadgeSVGs}}
+ <div>{{$badgeSVG}}</div>
+ {{end}}
+ </div>
+ </div>
+</div>
+{{template "devtest/devtest-footer"}}
diff --git a/templates/devtest/commit-sign-badge.tmpl b/templates/devtest/badge-commit-sign.tmpl
index a6677c4495..a6677c4495 100644
--- a/templates/devtest/commit-sign-badge.tmpl
+++ b/templates/devtest/badge-commit-sign.tmpl
diff --git a/templates/devtest/flex-list.tmpl b/templates/devtest/flex-list.tmpl
index 11d71d56a9..0db84b0c59 100644
--- a/templates/devtest/flex-list.tmpl
+++ b/templates/devtest/flex-list.tmpl
@@ -68,7 +68,7 @@
<a class="text primary" href="{{$.Link}}">
gitea-org / gitea
</a>
- <span data-tooltip-content="{{ctx.Locale.Tr "repo.fork"}}">{{svg "octicon-repo-forked"}}</span>
+ <span class="flex-text-inline" data-tooltip-content="{{ctx.Locale.Tr "repo.fork"}}">{{svg "octicon-repo-forked"}}</span>
</div>
<div class="flex-item-trailing">
<a class="muted" href="{{$.Link}}">
diff --git a/templates/devtest/fomantic-dropdown.tmpl b/templates/devtest/fomantic-dropdown.tmpl
index d41a161e86..a10dc890ce 100644
--- a/templates/devtest/fomantic-dropdown.tmpl
+++ b/templates/devtest/fomantic-dropdown.tmpl
@@ -75,12 +75,12 @@
<h2>Selection</h2>
<div>
{{/* the "selection" class is optional, it will be added by JS automatically */}}
- <select class="ui dropdown selection ellipsis-items-nowrap">
+ <select class="ui dropdown selection ellipsis-text-items">
<option>a</option>
<option>abcdefuvwxyz</option>
<option>loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong</option>
</select>
- <select class="ui dropdown ellipsis-items-nowrap tw-max-w-[8em]">
+ <select class="ui dropdown ellipsis-text-items tw-max-w-[8em]">
<option>loooooooooooooooooooooooooooooooooooooooooooooooooooooooooong</option>
<option>abcdefuvwxyz</option>
<option>a</option>
diff --git a/templates/devtest/fomantic-modal.tmpl b/templates/devtest/fomantic-modal.tmpl
index 2fbe2bd97d..838c6893a4 100644
--- a/templates/devtest/fomantic-modal.tmpl
+++ b/templates/devtest/fomantic-modal.tmpl
@@ -49,6 +49,16 @@
</form>
</div>
+ <div id="test-modal-form-5" class="ui mini modal">
+ <div class="header">Form dialog (layout 5)</div>
+ <div class="content">
+ <form method="post">
+ <div class="ui input tw-w-full"><input name="user_input"></div>
+ {{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
+ </form>
+ </div>
+ </div>
+
<div class="ui g-modal-confirm modal" id="test-modal-default">
<div class="header">{{svg "octicon-file"}} Default dialog <span>title</span></div>
<div class="content">
diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl
index 5b40268761..7a435cc433 100644
--- a/templates/devtest/gitea-ui.tmpl
+++ b/templates/devtest/gitea-ui.tmpl
@@ -9,16 +9,16 @@
<a class="silenced" href="#">silenced</a>
</div>
<h1>Button</h1>
- <div>
- Style:
- <label><input type="checkbox" name="button-style-compact" value="compact">compact</label>
- <label><input type="radio" name="button-style-size" value="">(normal)</label>
- <label><input type="radio" name="button-style-size" value="tiny">tiny</label>
- <label><input type="radio" name="button-style-size" value="mini">mini</label>
+ ".ui.button" styles:
+ <div class="flex-text-block tw-gap-4">
+ <label class="gt-checkbox"><input type="radio" name="button-style-size" value="">(normal)</label>
+ <label class="gt-checkbox"><input type="radio" name="button-style-size" value="small">small</label>
+ <label class="gt-checkbox"><input type="radio" name="button-style-size" value="tiny">tiny</label>
+ <label class="gt-checkbox"><input type="radio" name="button-style-size" value="mini">mini</label>
</div>
- <div>
- State:
- <label><input type="checkbox" name="button-state-disabled" value="disabled">disabled</label>
+ <div class="flex-text-block tw-gap-4">
+ <label class="gt-checkbox"><input type="checkbox" name="button-style-compact" value="compact">compact</label>
+ <label class="gt-checkbox"><input type="checkbox" name="button-state-disabled" value="disabled">disabled</label>
</div>
<div id="devtest-button-samples">
<ul class="button-sample-groups">
diff --git a/templates/devtest/markup-render.tmpl b/templates/devtest/markup-render.tmpl
new file mode 100644
index 0000000000..69d29d7829
--- /dev/null
+++ b/templates/devtest/markup-render.tmpl
@@ -0,0 +1,71 @@
+{{template "devtest/devtest-header"}}
+<div class="page-content devtest ui container">
+ {{$longCode := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}}
+ <div class="tw-flex">
+ <div class="tw-w-[50%] tw-p-4">
+ <div class="markup render-content">
+ Inline <code>code</code> content
+ </div>
+
+ <div class="divider"></div>
+
+ <div class="markup render-content">
+ <p>content before</p>
+ <pre><code>Very long line with no code block or container: {{$longCode}}</code></pre>
+ <p>content after</p>
+ </div>
+
+ <div class="divider"></div>
+
+ <div class="markup render-content">
+ <p>content before</p>
+ <div class="code-block-container code-overflow-wrap">
+ <pre class="code-block"><code>Very long line with wrap: {{$longCode}}</code></pre>
+ </div>
+ <p>content after</p>
+ </div>
+
+ <div class="divider"></div>
+
+ <div class="markup render-content">
+ <p>content before</p>
+ <div class="code-block-container code-overflow-scroll">
+ <pre class="code-block"><code>Short line in scroll container</code></pre>
+ </div>
+ <div class="code-block-container code-overflow-scroll">
+ <pre class="code-block"><code>Very long line with scroll: {{$longCode}}</code></pre>
+ </div>
+ <p>content after</p>
+ </div>
+ </div>
+
+ <div class="tw-w-[50%] tw-p-4">
+ <div class="markup render-content">
+ <p>content before</p>
+ <div class="code-block-container">
+ <pre class="code-block"><code class="language-math">
+ \lim\limits_{n\rightarrow\infty}{\left(1+\frac{1}{n}\right)^n}
+ </code></pre>
+ </div>
+ <p>content after</p>
+ </div>
+
+ <div class="divider"></div>
+
+ <div class="markup render-content">
+ <p>content before</p>
+ <div class="code-block-container">
+ <pre class="code-block"><code class="language-mermaid is-loading">
+ graph LR
+ A[Square Rect] -- Link text --> B((Circle))
+ A --> C(Round Rect)
+ B --> D{Rhombus}
+ C --> D
+ </code></pre>
+ </div>
+ <p>content after</p>
+ </div>
+ </div>
+ </div>
+</div>
+{{template "devtest/devtest-footer"}}
diff --git a/templates/devtest/repo-action-view.tmpl b/templates/devtest/repo-action-view.tmpl
index 9c6bdf13da..677eccc062 100644
--- a/templates/devtest/repo-action-view.tmpl
+++ b/templates/devtest/repo-action-view.tmpl
@@ -1,8 +1,13 @@
{{template "base/head" .}}
<div class="page-content">
+ <div class="tw-flex tw-justify-center tw-items-center tw-gap-5">
+ <a href="/devtest/repo-action-view/10/100">Run:CanCancel</a>
+ <a href="/devtest/repo-action-view/20/200">Run:CanApprove</a>
+ <a href="/devtest/repo-action-view/30/300">Run:CanRerun</a>
+ </div>
{{template "repo/actions/view_component" (dict
- "RunIndex" 1
- "JobIndex" 2
+ "RunIndex" (or .RunID 10)
+ "JobIndex" (or .JobID 100)
"ActionsURL" (print AppSubUrl "/devtest/actions-mock")
)}}
</div>
diff --git a/templates/explore/repos.tmpl b/templates/explore/repos.tmpl
index 53742bf0d9..68da398306 100644
--- a/templates/explore/repos.tmpl
+++ b/templates/explore/repos.tmpl
@@ -2,8 +2,8 @@
<div role="main" aria-label="{{.Title}}" class="page-content explore repositories">
{{template "explore/navbar" .}}
<div class="ui container">
- {{template "shared/repo_search" .}}
- {{template "explore/repo_list" .}}
+ {{template "shared/repo/search" .}}
+ {{template "shared/repo/list" .}}
{{template "base/paginate" .}}
</div>
</div>
diff --git a/templates/home.tmpl b/templates/home.tmpl
index 116dc487dc..cc9da82605 100644
--- a/templates/home.tmpl
+++ b/templates/home.tmpl
@@ -4,10 +4,10 @@
<div class="center">
<img class="logo" width="220" height="220" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}">
<div class="hero">
- <h1 class="ui icon header title">
+ <h1 class="ui icon header title tw-text-balance">
{{AppName}}
</h1>
- <h2>{{ctx.Locale.Tr "startpage.app_desc"}}</h2>
+ <h2 class="tw-text-balance">{{ctx.Locale.Tr "startpage.app_desc"}}</h2>
</div>
</div>
</div>
@@ -16,7 +16,7 @@
<h1 class="hero ui icon header">
{{svg "octicon-flame"}} {{ctx.Locale.Tr "startpage.install"}}
</h1>
- <p class="large">
+ <p class="large tw-text-balance">
{{ctx.Locale.Tr "startpage.install_desc" "https://docs.gitea.com/installation/install-from-binary" "https://github.com/go-gitea/gitea/tree/master/docker" "https://docs.gitea.com/installation/install-from-package"}}
</p>
</div>
@@ -24,7 +24,7 @@
<h1 class="hero ui icon header">
{{svg "octicon-device-desktop"}} {{ctx.Locale.Tr "startpage.platform"}}
</h1>
- <p class="large">
+ <p class="large tw-text-balance">
{{ctx.Locale.Tr "startpage.platform_desc" "https://go.dev/"}}
</p>
</div>
@@ -34,7 +34,7 @@
<h1 class="hero ui icon header">
{{svg "octicon-rocket"}} {{ctx.Locale.Tr "startpage.lightweight"}}
</h1>
- <p class="large">
+ <p class="large tw-text-balance">
{{ctx.Locale.Tr "startpage.lightweight_desc"}}
</p>
</div>
@@ -42,7 +42,7 @@
<h1 class="hero ui icon header">
{{svg "octicon-code"}} {{ctx.Locale.Tr "startpage.license"}}
</h1>
- <p class="large">
+ <p class="large tw-text-balance">
{{ctx.Locale.Tr "startpage.license_desc" "https://code.gitea.io/gitea" "code.gitea.io/gitea" "https://github.com/go-gitea/gitea"}}
</p>
</div>
diff --git a/templates/install.tmpl b/templates/install.tmpl
index 6c4cc7df01..0aec52f27b 100644
--- a/templates/install.tmpl
+++ b/templates/install.tmpl
@@ -1,7 +1,7 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content install">
<div class="ui grid install-config-container">
- <div class="sixteen wide center aligned centered column">
+ <div class="sixteen wide tw-text-center centered column">
<h3 class="ui top attached header">
{{ctx.Locale.Tr "install.title"}}
</h3>
@@ -351,5 +351,4 @@
</div>
</div>
</div>
-<img class="tw-hidden" src="{{AssetUrlPrefix}}/img/loading.png">
{{template "base/footer" .}}
diff --git a/templates/org/create.tmpl b/templates/org/create.tmpl
index 004cd9be80..2d6dca5440 100644
--- a/templates/org/create.tmpl
+++ b/templates/org/create.tmpl
@@ -1,55 +1,51 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content organization new org">
- <div class="ui middle very relaxed page grid">
- <div class="column">
- <form class="ui form" action="{{.Link}}" method="post">
+ <div class="ui container medium-width">
+ {{template "base/alert" .}}
+ <h3 class="ui top attached header">
+ {{ctx.Locale.Tr "new_org"}}
+ </h3>
+ <div class="ui attached segment">
+ <form class="ui form left-right-form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
- <h3 class="ui top attached header">
- {{ctx.Locale.Tr "new_org"}}
- </h3>
- <div class="ui attached segment">
- {{template "base/alert" .}}
- <div class="inline required field {{if .Err_OrgName}}error{{end}}">
- <label for="org_name">{{ctx.Locale.Tr "org.org_name_holder"}}</label>
- <input id="org_name" name="org_name" value="{{.org_name}}" autofocus required maxlength="40">
- <span class="help">{{ctx.Locale.Tr "org.org_name_helper"}}</span>
- </div>
+ <div class="inline required field {{if .Err_OrgName}}error{{end}}">
+ <label for="org_name">{{ctx.Locale.Tr "org.org_name_holder"}}</label>
+ <input id="org_name" name="org_name" value="{{.org_name}}" autofocus required maxlength="40">
+ <span class="help">{{ctx.Locale.Tr "org.org_name_helper"}}</span>
+ </div>
- <div class="inline field {{if .Err_OrgVisibility}}error{{end}}">
- <span class="inline required field"><label for="visibility">{{ctx.Locale.Tr "org.settings.visibility"}}</label></span>
- <div class="inline-grouped-list">
- <div class="ui radio checkbox">
- <input class="enable-system-radio" name="visibility" type="radio" value="0" {{if .DefaultOrgVisibilityMode.IsPublic}}checked{{end}}>
- <label>{{ctx.Locale.Tr "org.settings.visibility.public"}}</label>
- </div>
- <div class="ui radio checkbox">
- <input class="enable-system-radio" name="visibility" type="radio" value="1" {{if .DefaultOrgVisibilityMode.IsLimited}}checked{{end}}>
- <label>{{ctx.Locale.Tr "org.settings.visibility.limited"}}</label>
- </div>
- <div class="ui radio checkbox">
- <input class="enable-system-radio" name="visibility" type="radio" value="2" {{if .DefaultOrgVisibilityMode.IsPrivate}}checked{{end}}>
- <label>{{ctx.Locale.Tr "org.settings.visibility.private"}}</label>
- </div>
+ <div class="inline field required {{if .Err_OrgVisibility}}error{{end}}">
+ <label for="visibility">{{ctx.Locale.Tr "org.settings.visibility"}}</label>
+ <div class="inline-right">
+ <div class="ui radio checkbox">
+ <input class="enable-system-radio" name="visibility" type="radio" value="0" {{if .visibility.IsPublic}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "org.settings.visibility.public"}}</label>
</div>
- </div>
-
- <div class="inline field" id="permission_box">
- <label>{{ctx.Locale.Tr "org.settings.permission"}}</label>
- <div class="inline-grouped-list">
- <div class="ui checkbox">
- <input type="checkbox" name="repo_admin_change_team_access" checked>
- <label>{{ctx.Locale.Tr "org.settings.repoadminchangeteam"}}</label>
- </div>
+ <div class="ui radio checkbox">
+ <input class="enable-system-radio" name="visibility" type="radio" value="1" {{if .visibility.IsLimited}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "org.settings.visibility.limited"}}</label>
+ </div>
+ <div class="ui radio checkbox">
+ <input class="enable-system-radio" name="visibility" type="radio" value="2" {{if .visibility.IsPrivate}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "org.settings.visibility.private"}}</label>
</div>
</div>
+ </div>
- <div class="inline field">
- <label></label>
- <button class="ui primary button">
- {{ctx.Locale.Tr "org.create_org"}}
- </button>
+ <div class="inline field" id="permission_box">
+ <label>{{ctx.Locale.Tr "org.settings.permission"}}</label>
+ <div class="ui checkbox">
+ <input type="checkbox" name="repo_admin_change_team_access" {{if .repo_admin_change_team_access}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "org.settings.repoadminchangeteam"}}</label>
</div>
</div>
+
+ <div class="inline field">
+ <label></label>
+ <button class="ui primary button">
+ {{ctx.Locale.Tr "org.create_org"}}
+ </button>
+ </div>
</form>
</div>
</div>
diff --git a/templates/org/header.tmpl b/templates/org/header.tmpl
index 80519361fd..90798b5d7c 100644
--- a/templates/org/header.tmpl
+++ b/templates/org/header.tmpl
@@ -18,7 +18,7 @@
{{end}}
</span>
</div>
- {{if .RenderedDescription}}<div class="render-content markup tw-break-anywhere">{{.RenderedDescription}}</div>{{end}}
+ {{if .RenderedDescription}}<div class="render-content markup">{{.RenderedDescription}}</div>{{end}}
<div class="text light meta tw-mt-1">
{{if .Org.Location}}<div class="flex-text-block">{{svg "octicon-location"}} <span>{{.Org.Location}}</span></div>{{end}}
{{if .Org.Website}}<div class="flex-text-block">{{svg "octicon-link"}} <a class="muted" target="_blank" rel="noopener noreferrer me" href="{{.Org.Website}}">{{.Org.Website}}</a></div>{{end}}
diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl
index db750692bf..3cde3554c9 100644
--- a/templates/org/home.tmpl
+++ b/templates/org/home.tmpl
@@ -6,20 +6,20 @@
<div class="ui mobile reversed stackable grid">
<div class="ui {{if .ShowMemberAndTeamTab}}eleven wide{{end}} column">
{{if .ProfileReadmeContent}}
- <div id="readme_profile" class="markup" data-profile-view-as-member="{{.IsViewingOrgAsMember}}">{{.ProfileReadmeContent}}</div>
+ <div id="readme_profile" class="render-content markup" data-profile-view-as-member="{{.IsViewingOrgAsMember}}">{{.ProfileReadmeContent}}</div>
{{end}}
- {{template "shared/repo_search" .}}
- {{template "explore/repo_list" .}}
+ {{template "shared/repo/search" .}}
+ {{template "shared/repo/list" .}}
{{template "base/paginate" .}}
</div>
{{if .ShowMemberAndTeamTab}}
<div class="ui five wide column">
{{if .CanCreateOrgRepo}}
- <div class="center aligned tw-mb-4">
- <a class="ui primary button" href="{{AppSubUrl}}/repo/create?org={{.Org.ID}}">{{ctx.Locale.Tr "new_repo"}}</a>
+ <div class="tw-flex tw-flex-wrap tw-justify-center tw-gap-x-1 tw-gap-y-2 tw-mb-4">
+ <a class="ui primary button tw-grow" href="{{AppSubUrl}}/repo/create?org={{.Org.ID}}">{{ctx.Locale.Tr "new_repo"}}</a>
{{if not .DisableNewPullMirrors}}
- <a class="ui primary button" href="{{AppSubUrl}}/repo/migrate?org={{.Org.ID}}&mirror=1">{{ctx.Locale.Tr "new_migrate"}}</a>
+ <a class="ui primary button tw-grow" href="{{AppSubUrl}}/repo/migrate?org={{.Org.ID}}&mirror=1">{{ctx.Locale.Tr "new_migrate"}}</a>
{{end}}
</div>
<div class="divider"></div>
@@ -32,7 +32,6 @@
<span class="text">{{svg "octicon-eye"}} {{ctx.Locale.Tr "org.view_as_role" $viewAsRole}}</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
- {{/* TODO: does it really need to use CurrentURL with query parameters? Why not construct a new link with clear parameters */}}
<a href="?view_as=public" class="item {{if not .IsViewingOrgAsMember}}selected{{end}}">
{{svg "octicon-check" 14 (Iif (not .IsViewingOrgAsMember) "" "tw-invisible")}} {{ctx.Locale.Tr "settings.visibility.public"}}
</a>
diff --git a/templates/org/member/members.tmpl b/templates/org/member/members.tmpl
index 4388dc9520..2d0f4bc423 100644
--- a/templates/org/member/members.tmpl
+++ b/templates/org/member/members.tmpl
@@ -73,7 +73,7 @@
{{ctx.Locale.Tr "org.members.leave"}}
</div>
<div class="content">
- <p>{{ctx.Locale.Tr "org.members.leave.detail" (`<span class="dataOrganizationName"></span>`|SafeHTML)}}</p>
+ <p>{{ctx.Locale.Tr "org.members.leave.detail" (HTMLFormat `<span class="%s"></span>` "dataOrganizationName")}}</p>
</div>
{{template "base/modal_actions_confirm" .}}
</div>
@@ -82,7 +82,7 @@
{{ctx.Locale.Tr "org.members.remove"}}
</div>
<div class="content">
- <p>{{ctx.Locale.Tr "org.members.remove.detail" (`<span class="name"></span>`|SafeHTML) (`<span class="dataOrganizationName"></span>`|SafeHTML)}}</p>
+ <p>{{ctx.Locale.Tr "org.members.remove.detail" (HTMLFormat `<span class="%s"></span>` "name") (HTMLFormat `<span class="%s"></span>` "dataOrganizationName")}}</p>
</div>
{{template "base/modal_actions_confirm" .}}
</div>
diff --git a/templates/org/menu.tmpl b/templates/org/menu.tmpl
index 4a8aee68a7..d876dabb44 100644
--- a/templates/org/menu.tmpl
+++ b/templates/org/menu.tmpl
@@ -44,6 +44,11 @@
{{end}}
</a>
{{end}}
+ {{if and EnableTimetracking .IsOrganizationOwner}}
+ <a class="{{if $.PageIsOrgTimes}}active{{end}} item" href="{{$.OrgLink}}/worktime">
+ {{svg "octicon-clock"}} {{ctx.Locale.Tr "org.worktime"}}
+ </a>
+ {{end}}
{{if .IsOrganizationOwner}}
<span class="item-flex-space"></span>
<a class="{{if .PageIsOrgSettings}}active {{end}}item" href="{{.OrgLink}}/settings">
diff --git a/templates/org/projects/new.tmpl b/templates/org/projects/new.tmpl
index fc52130f68..c021c5a0fe 100644
--- a/templates/org/projects/new.tmpl
+++ b/templates/org/projects/new.tmpl
@@ -1,9 +1,24 @@
{{template "base/head" .}}
-<div role="main" aria-label="{{.Title}}" class="page-content organization projects edit-project new">
- {{template "shared/user/org_profile_avatar" .}}
+{{if .ContextUser.IsOrganization}}
+<div role="main" aria-label="{{.Title}}" class="page-content organization projects">
+ {{template "org/header" .}}
<div class="ui container">
- {{template "user/overview/header" .}}
- {{template "projects/new" .}}
+ {{template "projects/new" .}}
</div>
</div>
+{{else}}
+<div role="main" aria-label="{{.Title}}" class="page-content user profile">
+ <div class="ui container">
+ <div class="ui stackable grid">
+ <div class="ui four wide column">
+ {{template "shared/user/profile_big_avatar" .}}
+ </div>
+ <div class="ui twelve wide column tw-mb-4">
+ {{template "user/overview/header" .}}
+ {{template "projects/new" .}}
+ </div>
+ </div>
+ </div>
+</div>
+{{end}}
{{template "base/footer" .}}
diff --git a/templates/org/projects/view.tmpl b/templates/org/projects/view.tmpl
index bd74114fe2..1bfbc8d8b4 100644
--- a/templates/org/projects/view.tmpl
+++ b/templates/org/projects/view.tmpl
@@ -8,8 +8,6 @@
{{template "user/overview/header" .}}
</div>
{{end}}
- <div class="ui container fluid padded">
- {{template "projects/view" .}}
- </div>
+ {{template "projects/view" .}}
</div>
{{template "base/footer" .}}
diff --git a/templates/org/settings/delete.tmpl b/templates/org/settings/delete.tmpl
deleted file mode 100644
index e1ef471e34..0000000000
--- a/templates/org/settings/delete.tmpl
+++ /dev/null
@@ -1,35 +0,0 @@
-{{template "org/settings/layout_head" (dict "ctxData" . "pageClass" "organization settings delete")}}
-
- <div class="org-setting-content">
- <h4 class="ui top attached error header">
- {{ctx.Locale.Tr "org.settings.delete_account"}}
- </h4>
- <div class="ui attached error segment">
- <div class="ui red message">
- <p class="text left">{{svg "octicon-alert"}} {{ctx.Locale.Tr "org.settings.delete_prompt"}}</p>
- </div>
- <form class="ui form ignore-dirty" id="delete-form" action="{{.Link}}" method="post">
- {{.CsrfTokenHtml}}
- <div class="inline required field {{if .Err_OrgName}}error{{end}}">
- <label for="org_name">{{ctx.Locale.Tr "org.org_name_holder"}}</label>
- <input id="org_name" name="org_name" value="" autocomplete="off" autofocus required>
- </div>
- <button class="ui red button delete-button" data-type="form" data-form="#delete-form">
- {{ctx.Locale.Tr "org.settings.confirm_delete_account"}}
- </button>
- </form>
- </div>
- </div>
-
-<div class="ui g-modal-confirm delete modal">
- <div class="header">
- {{svg "octicon-trash"}}
- {{ctx.Locale.Tr "org.settings.delete_org_title"}}
- </div>
- <div class="content">
- <p>{{ctx.Locale.Tr "org.settings.delete_org_desc"}}</p>
- </div>
- {{template "base/modal_actions_confirm" .}}
-</div>
-
-{{template "org/settings/layout_footer" .}}
diff --git a/templates/org/settings/hooks.tmpl b/templates/org/settings/hooks.tmpl
index 9f307968f8..b05e22fe20 100644
--- a/templates/org/settings/hooks.tmpl
+++ b/templates/org/settings/hooks.tmpl
@@ -1,5 +1,5 @@
{{template "org/settings/layout_head" (dict "ctxData" . "pageClass" "organization settings webhooks")}}
<div class="org-setting-content">
- {{template "repo/settings/webhook/list" .}}
+ {{template "repo/settings/webhook/base_list" .}}
</div>
{{template "org/settings/layout_footer" .}}
diff --git a/templates/org/settings/navbar.tmpl b/templates/org/settings/navbar.tmpl
index ce792f667c..58475de7e7 100644
--- a/templates/org/settings/navbar.tmpl
+++ b/templates/org/settings/navbar.tmpl
@@ -41,8 +41,5 @@
</div>
</details>
{{end}}
- <a class="{{if .PageIsSettingsDelete}}active {{end}}item" href="{{.OrgLink}}/settings/delete">
- {{ctx.Locale.Tr "org.settings.delete"}}
- </a>
</div>
</div>
diff --git a/templates/org/settings/options.tmpl b/templates/org/settings/options.tmpl
index 3b817d068b..d94bb4c62b 100644
--- a/templates/org/settings/options.tmpl
+++ b/templates/org/settings/options.tmpl
@@ -1,103 +1,97 @@
{{template "org/settings/layout_head" (dict "ctxData" . "pageClass" "organization settings options")}}
- <div class="org-setting-content">
- <h4 class="ui top attached header">
- {{ctx.Locale.Tr "org.settings.options"}}
- </h4>
- <div class="ui attached segment">
- <form class="ui form" action="{{.Link}}" method="post">
- {{.CsrfTokenHtml}}
- <div class="required field {{if .Err_Name}}error{{end}}">
- <label for="org_name">{{ctx.Locale.Tr "org.org_name_holder"}}
- <span class="text red tw-hidden" id="org-name-change-prompt">
- <br>{{ctx.Locale.Tr "org.settings.change_orgname_prompt"}}<br>{{ctx.Locale.Tr "org.settings.change_orgname_redirect_prompt"}}
- </span>
- </label>
- <input id="org_name" name="name" value="{{.Org.Name}}" data-org-name="{{.Org.Name}}" autofocus required maxlength="40">
- </div>
- <div class="field {{if .Err_FullName}}error{{end}}">
- <label for="full_name">{{ctx.Locale.Tr "org.org_full_name_holder"}}</label>
- <input id="full_name" name="full_name" value="{{.Org.FullName}}" maxlength="100">
- </div>
- <div class="field {{if .Err_Email}}error{{end}}">
- <label for="email">{{ctx.Locale.Tr "org.settings.email"}}</label>
- <input id="email" name="email" type="email" value="{{.Org.Email}}" maxlength="255">
- </div>
- <div class="field {{if .Err_Description}}error{{end}}">
- {{/* it is rendered as markdown, but the length is limited, so at the moment we do not use the markdown editor here */}}
- <label for="description">{{ctx.Locale.Tr "org.org_desc"}}</label>
- <textarea id="description" name="description" rows="2" maxlength="255">{{.Org.Description}}</textarea>
- </div>
- <div class="field {{if .Err_Website}}error{{end}}">
- <label for="website">{{ctx.Locale.Tr "org.settings.website"}}</label>
- <input id="website" name="website" type="url" value="{{.Org.Website}}" maxlength="255">
- </div>
- <div class="field">
- <label for="location">{{ctx.Locale.Tr "org.settings.location"}}</label>
- <input id="location" name="location" value="{{.Org.Location}}" maxlength="50">
- </div>
- <div class="divider"></div>
- <div class="field" id="visibility_box">
- <label for="visibility">{{ctx.Locale.Tr "org.settings.visibility"}}</label>
- <div class="field">
- <div class="ui radio checkbox">
- <input class="enable-system-radio" name="visibility" type="radio" value="0" {{if eq .CurrentVisibility 0}}checked{{end}}>
- <label>{{ctx.Locale.Tr "org.settings.visibility.public"}}</label>
- </div>
- </div>
- <div class="field">
- <div class="ui radio checkbox">
- <input class="enable-system-radio" name="visibility" type="radio" value="1" {{if eq .CurrentVisibility 1}}checked{{end}}>
- <label>{{ctx.Locale.Tr "org.settings.visibility.limited"}}</label>
- </div>
- </div>
- <div class="field">
- <div class="ui radio checkbox">
- <input class="enable-system-radio" name="visibility" type="radio" value="2" {{if eq .CurrentVisibility 2}}checked{{end}}>
- <label>{{ctx.Locale.Tr "org.settings.visibility.private"}}</label>
- </div>
- </div>
- </div>
+<div class="ui segments org-setting-content">
+ <h4 class="ui top attached header">
+ {{ctx.Locale.Tr "org.settings.options"}}
+ </h4>
+ <div class="ui attached segment">
+ <form class="ui form" action="{{.Link}}" method="post">
+ {{.CsrfTokenHtml}}
+ <div class="field {{if .Err_FullName}}error{{end}}">
+ <label for="full_name">{{ctx.Locale.Tr "org.org_full_name_holder"}}</label>
+ <input id="full_name" name="full_name" value="{{.Org.FullName}}" maxlength="100">
+ </div>
+ <div class="field {{if .Err_Email}}error{{end}}">
+ <label for="email">{{ctx.Locale.Tr "org.settings.email"}}</label>
+ <input id="email" name="email" type="email" value="{{.Org.Email}}" maxlength="255">
+ </div>
+ <div class="field {{if .Err_Description}}error{{end}}">
+ {{/* it is rendered as markdown, but the length is limited, so at the moment we do not use the markdown editor here */}}
+ <label for="description">{{ctx.Locale.Tr "org.org_desc"}}</label>
+ <textarea id="description" name="description" rows="2" maxlength="255">{{.Org.Description}}</textarea>
+ </div>
+ <div class="field {{if .Err_Website}}error{{end}}">
+ <label for="website">{{ctx.Locale.Tr "org.settings.website"}}</label>
+ <input id="website" name="website" type="url" value="{{.Org.Website}}" maxlength="255">
+ </div>
+ <div class="field">
+ <label for="location">{{ctx.Locale.Tr "org.settings.location"}}</label>
+ <input id="location" name="location" value="{{.Org.Location}}" maxlength="50">
+ </div>
- <div class="field" id="permission_box">
- <label>{{ctx.Locale.Tr "org.settings.permission"}}</label>
- <div class="field">
- <div class="ui checkbox">
- <input type="checkbox" name="repo_admin_change_team_access" {{if .RepoAdminChangeTeamAccess}}checked{{end}}>
- <label>{{ctx.Locale.Tr "org.settings.repoadminchangeteam"}}</label>
- </div>
- </div>
- </div>
+ <div class="divider"></div>
+ <div class="field" id="visibility_box">
+ <label for="visibility">{{ctx.Locale.Tr "org.settings.visibility"}}</label>
+ <div class="field">
+ <div class="ui radio checkbox">
+ <input class="enable-system-radio" name="visibility" type="radio" value="0" {{if eq .CurrentVisibility 0}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "org.settings.visibility.public"}}</label>
+ </div>
+ </div>
+ <div class="field">
+ <div class="ui radio checkbox">
+ <input class="enable-system-radio" name="visibility" type="radio" value="1" {{if eq .CurrentVisibility 1}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "org.settings.visibility.limited"}}</label>
+ </div>
+ </div>
+ <div class="field">
+ <div class="ui radio checkbox">
+ <input class="enable-system-radio" name="visibility" type="radio" value="2" {{if eq .CurrentVisibility 2}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "org.settings.visibility.private"}}</label>
+ </div>
+ </div>
+ </div>
- {{if .SignedUser.IsAdmin}}
- <div class="divider"></div>
+ <div class="field" id="permission_box">
+ <label>{{ctx.Locale.Tr "org.settings.permission"}}</label>
+ <div class="field">
+ <div class="ui checkbox">
+ <input type="checkbox" name="repo_admin_change_team_access" {{if .RepoAdminChangeTeamAccess}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "org.settings.repoadminchangeteam"}}</label>
+ </div>
+ </div>
+ </div>
- <div class="inline field {{if .Err_MaxRepoCreation}}error{{end}}">
- <label for="max_repo_creation">{{ctx.Locale.Tr "admin.users.max_repo_creation"}}</label>
- <input id="max_repo_creation" name="max_repo_creation" type="number" min="-1" value="{{.Org.MaxRepoCreation}}">
- <p class="help">{{ctx.Locale.Tr "admin.users.max_repo_creation_desc"}}</p>
- </div>
- {{end}}
+ {{if .SignedUser.IsAdmin}}
+ <div class="divider"></div>
- <div class="field">
- <button class="ui primary button">{{ctx.Locale.Tr "org.settings.update_settings"}}</button>
- </div>
- </form>
+ <div class="inline field {{if .Err_MaxRepoCreation}}error{{end}}">
+ <label for="max_repo_creation">{{ctx.Locale.Tr "admin.users.max_repo_creation"}}</label>
+ <input id="max_repo_creation" name="max_repo_creation" type="number" min="-1" value="{{.Org.MaxRepoCreation}}">
+ <p class="help">{{ctx.Locale.Tr "admin.users.max_repo_creation_desc"}}</p>
+ </div>
+ {{end}}
- <div class="divider"></div>
+ <div class="field">
+ <button class="ui primary button">{{ctx.Locale.Tr "org.settings.update_settings"}}</button>
+ </div>
+ </form>
- <form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data">
- {{.CsrfTokenHtml}}
- <div class="inline field">
- <label for="avatar">{{ctx.Locale.Tr "settings.choose_new_avatar"}}</label>
- <input name="avatar" type="file" accept="image/png,image/jpeg,image/gif,image/webp">
- </div>
+ <div class="divider"></div>
- <div class="field">
- <button class="ui primary button">{{ctx.Locale.Tr "settings.update_avatar"}}</button>
- <button class="ui red button link-action" data-url="{{.Link}}/avatar/delete">{{ctx.Locale.Tr "settings.delete_current_avatar"}}</button>
- </div>
- </form>
- </div>
+ <form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data">
+ {{.CsrfTokenHtml}}
+ <div class="inline field">
+ {{template "shared/avatar_upload_crop" dict "LabelText" (ctx.Locale.Tr "settings.choose_new_avatar")}}
+ </div>
+ <div class="field">
+ <button class="ui primary button">{{ctx.Locale.Tr "settings.update_avatar"}}</button>
+ <button class="ui red button link-action" data-url="{{.Link}}/avatar/delete">{{ctx.Locale.Tr "settings.delete_current_avatar"}}</button>
</div>
+ </form>
+ </div>
+</div>
+
+{{template "org/settings/options_dangerzone" .}}
+
{{template "org/settings/layout_footer" .}}
diff --git a/templates/org/settings/options_dangerzone.tmpl b/templates/org/settings/options_dangerzone.tmpl
new file mode 100644
index 0000000000..01cf3fd405
--- /dev/null
+++ b/templates/org/settings/options_dangerzone.tmpl
@@ -0,0 +1,93 @@
+<h4 class="ui top attached error header">
+ {{ctx.Locale.Tr "repo.settings.danger_zone"}}
+</h4>
+<div class="ui attached error danger segment">
+ <div class="flex-list">
+ <div class="flex-item tw-items-center">
+ <div class="flex-item-main">
+ <div class="flex-item-title">{{ctx.Locale.Tr "org.settings.rename"}}</div>
+ <div class="flex-item-body">{{ctx.Locale.Tr "org.settings.rename_desc"}}</div>
+ </div>
+ <div class="flex-item-trailing">
+ <button class="ui basic red show-modal button" data-modal="#rename-org-modal">{{ctx.Locale.Tr "org.settings.rename"}}</button>
+ </div>
+ </div>
+
+ <div class="flex-item">
+ <div class="flex-item-main">
+ <div class="flex-item-title">{{ctx.Locale.Tr "org.settings.delete_account"}}</div>
+ <div class="flex-item-body">{{ctx.Locale.Tr "org.settings.delete_prompt"}}</div>
+ </div>
+ <div class="flex-item-trailing">
+ <button class="ui basic red show-modal button" data-modal="#delete-org-modal">{{ctx.Locale.Tr "org.settings.delete_account"}}</button>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="ui small modal" id="rename-org-modal">
+ <div class="header">
+ {{ctx.Locale.Tr "org.settings.rename"}}
+ </div>
+ <div class="content">
+ <ul class="ui warning message">
+ <li>{{ctx.Locale.Tr "org.settings.rename_notices_1"}}</li>
+ <li>{{ctx.Locale.Tr "org.settings.rename_notices_2"}}</li>
+ </ul>
+ <form class="ui form form-fetch-action" action="{{.Link}}/rename" method="post">
+ {{.CsrfTokenHtml}}
+ <div class="field">
+ <label>
+ {{ctx.Locale.Tr "org.settings.name_confirm"}}
+ <span class="text red">{{.Org.Name}}</span>
+ </label>
+ </div>
+ <div class="required field">
+ <label for="org_name_to_rename">{{ctx.Locale.Tr "org.org_name_holder"}}</label>
+ <input id="org_name_to_rename" name="org_name" required>
+ </div>
+
+ <div class="required field">
+ <label>{{ctx.Locale.Tr "org.settings.rename_new_org_name"}}</label>
+ <input name="new_org_name" required>
+ </div>
+
+ <div class="actions">
+ <button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
+ <button class="ui red button">{{ctx.Locale.Tr "org.settings.rename"}}</button>
+ </div>
+ </form>
+ </div>
+</div>
+
+<div class="ui small modal" id="delete-org-modal">
+ <div class="header">
+ {{ctx.Locale.Tr "org.settings.delete_account"}}
+ </div>
+ <div class="content">
+ <ul class="ui warning message">
+ <li>{{ctx.Locale.Tr "org.settings.delete_notices_1"}}</li>
+ <li>{{ctx.Locale.Tr "org.settings.delete_notices_2" .Org.Name}}</li>
+ <li>{{ctx.Locale.Tr "org.settings.delete_notices_3" .Org.Name}}</li>
+ <li>{{ctx.Locale.Tr "org.settings.delete_notices_4" .Org.Name}}</li>
+ </ul>
+ <form class="ui form form-fetch-action" action="{{.Link}}/delete" method="post">
+ {{.CsrfTokenHtml}}
+ <div class="field">
+ <label>
+ {{ctx.Locale.Tr "org.settings.name_confirm"}}
+ <span class="text red">{{.Org.Name}}</span>
+ </label>
+ </div>
+ <div class="required field">
+ <label>{{ctx.Locale.Tr "org.org_name_holder"}}</label>
+ <input name="org_name" required>
+ </div>
+
+ <div class="actions">
+ <button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
+ <button class="ui red button">{{ctx.Locale.Tr "org.settings.delete_account"}}</button>
+ </div>
+ </form>
+ </div>
+</div>
diff --git a/templates/org/team/members.tmpl b/templates/org/team/members.tmpl
index 5433f01530..4bc063f90c 100644
--- a/templates/org/team/members.tmpl
+++ b/templates/org/team/members.tmpl
@@ -81,7 +81,7 @@
{{ctx.Locale.Tr "org.members.remove"}}
</div>
<div class="content">
- <p>{{ctx.Locale.Tr "org.members.remove.detail" (`<span class="name"></span>`|SafeHTML) (`<span class="dataTeamName"></span>`|SafeHTML)}}</p>
+ <p>{{ctx.Locale.Tr "org.members.remove.detail" (HTMLFormat `<span class="%s"></span>` "name") (HTMLFormat `<span class="%s"></span>` "dataTeamName")}}</p>
</div>
{{template "base/modal_actions_confirm" .}}
</div>
diff --git a/templates/org/team/new.tmpl b/templates/org/team/new.tmpl
index 410a3c4b62..67529ddfba 100644
--- a/templates/org/team/new.tmpl
+++ b/templates/org/team/new.tmpl
@@ -56,7 +56,7 @@
<br>
<div class="field">
<div class="ui radio checkbox">
- <input type="radio" name="permission" value="read" {{if or .PageIsOrgTeamsNew (eq .Team.AccessMode 1) (eq .Team.AccessMode 2)}}checked{{end}}>
+ <input type="radio" name="permission" value="read" {{if or .PageIsOrgTeamsNew (eq .Team.AccessMode 0) (eq .Team.AccessMode 1) (eq .Team.AccessMode 2)}}checked{{end}}>
<label>{{ctx.Locale.Tr "org.teams.general_access"}}</label>
<span class="help">{{ctx.Locale.Tr "org.teams.general_access_helper"}}</span>
</div>
@@ -77,11 +77,11 @@
<thead>
<tr>
<th>{{ctx.Locale.Tr "units.unit"}}</th>
- <th class="center aligned">{{ctx.Locale.Tr "org.teams.none_access"}}
+ <th class="tw-text-center">{{ctx.Locale.Tr "org.teams.none_access"}}
<span class="tw-align-middle" data-tooltip-content="{{ctx.Locale.Tr "org.teams.none_access_helper"}}">{{svg "octicon-question" 16 "tw-ml-1"}}</span></th>
- <th class="center aligned">{{ctx.Locale.Tr "org.teams.read_access"}}
+ <th class="tw-text-center">{{ctx.Locale.Tr "org.teams.read_access"}}
<span class="tw-align-middle" data-tooltip-content="{{ctx.Locale.Tr "org.teams.read_access_helper"}}">{{svg "octicon-question" 16 "tw-ml-1"}}</span></th>
- <th class="center aligned">{{ctx.Locale.Tr "org.teams.write_access"}}
+ <th class="tw-text-center">{{ctx.Locale.Tr "org.teams.write_access"}}
<span class="tw-align-middle" data-tooltip-content="{{ctx.Locale.Tr "org.teams.write_access_helper"}}">{{svg "octicon-question" 16 "tw-ml-1"}}</span></th>
</tr>
</thead>
@@ -97,17 +97,17 @@
</div>
</div>
</td>
- <td class="center aligned">
+ <td class="tw-text-center">
<div class="ui radio checkbox">
<input type="radio" name="unit_{{$unit.Type.Value}}" value="0"{{if or ($unit.Type.UnitGlobalDisabled) (eq ($.Team.UnitAccessMode ctx $unit.Type) 0)}} checked{{end}} title="{{ctx.Locale.Tr "org.teams.none_access"}}">
</div>
</td>
- <td class="center aligned">
+ <td class="tw-text-center">
<div class="ui radio checkbox">
<input type="radio" name="unit_{{$unit.Type.Value}}" value="1"{{if or (eq $.Team.ID 0) (eq ($.Team.UnitAccessMode ctx $unit.Type) 1)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}} title="{{ctx.Locale.Tr "org.teams.read_access"}}">
</div>
</td>
- <td class="center aligned">
+ <td class="tw-text-center">
<div class="ui radio checkbox">
<input type="radio" name="unit_{{$unit.Type.Value}}" value="2"{{if (ge ($.Team.UnitAccessMode ctx $unit.Type) 2)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}} title="{{ctx.Locale.Tr "org.teams.write_access"}}">
</div>
diff --git a/templates/org/team/repositories.tmpl b/templates/org/team/repositories.tmpl
index 92c3d724ba..4f4667ca8b 100644
--- a/templates/org/team/repositories.tmpl
+++ b/templates/org/team/repositories.tmpl
@@ -9,7 +9,7 @@
{{template "org/team/navbar" .}}
{{$canAddRemove := and $.IsOrganizationOwner (not $.Team.IncludesAllRepositories)}}
{{if $canAddRemove}}
- <div class="ui attached segment tw-flex tw-flex-wrap tw-gap-2">
+ <div class="ui top attached segment tw-flex tw-flex-wrap tw-gap-2">
<form class="ui form ignore-dirty tw-flex-1 tw-flex" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/add" method="post">
{{.CsrfTokenHtml}}
<div id="search-repo-box" data-uid="{{.Org.ID}}" class="ui search">
diff --git a/templates/org/team/sidebar.tmpl b/templates/org/team/sidebar.tmpl
index c4acd8da24..6dd5cb3eeb 100644
--- a/templates/org/team/sidebar.tmpl
+++ b/templates/org/team/sidebar.tmpl
@@ -42,10 +42,12 @@
<li>{{ctx.Locale.Tr "org.teams.can_create_org_repo"}}</li>
{{end}}
</ul>
- {{if (eq .Team.AccessMode 2)}}
+ {{/* the AccessMode should be either none or admin/owner, the real permissions are provided by each team unit */}}
+ {{if false}}{{/*(eq .Team.AccessMode 2)*/}}
<h3>{{ctx.Locale.Tr "org.settings.permission"}}</h3>
{{ctx.Locale.Tr "org.teams.write_permission_desc"}}
{{else if (eq .Team.AccessMode 3)}}
+ {{/* FIXME: here might not right, see "FIXME: TEAM-UNIT-PERMISSION", new units might not have correct admin permission*/}}
<h3>{{ctx.Locale.Tr "org.settings.permission"}}</h3>
{{ctx.Locale.Tr "org.teams.admin_permission_desc"}}
{{else}}
@@ -88,7 +90,7 @@
{{ctx.Locale.Tr "org.teams.leave"}}
</div>
<div class="content">
- <p>{{ctx.Locale.Tr "org.teams.leave.detail" (`<span class="name"></span>`|SafeHTML)}}</p>
+ <p>{{ctx.Locale.Tr "org.teams.leave.detail" (HTMLFormat `<span class="%s"></span>` "name")}}</p>
</div>
{{template "base/modal_actions_confirm" .}}
</div>
diff --git a/templates/org/team/teams.tmpl b/templates/org/team/teams.tmpl
index 53c909ee9c..cdd2789128 100644
--- a/templates/org/team/teams.tmpl
+++ b/templates/org/team/teams.tmpl
@@ -4,7 +4,7 @@
<div class="ui container">
{{template "base/alert" .}}
{{if .IsOrganizationOwner}}
- <div class="text right">
+ <div class="flex-text-block tw-justify-end">
<a class="ui primary button" href="{{.OrgLink}}/teams/new">{{svg "octicon-plus"}} {{ctx.Locale.Tr "org.create_new_team"}}</a>
</div>
<div class="divider"></div>
@@ -49,7 +49,7 @@
{{ctx.Locale.Tr "org.teams.leave"}}
</div>
<div class="content">
- <p>{{ctx.Locale.Tr "org.teams.leave.detail" (`<span class="name"></span>`|SafeHTML)}}</p>
+ <p>{{ctx.Locale.Tr "org.teams.leave.detail" (HTMLFormat `<span class="%s"></span>` "name")}}</p>
</div>
{{template "base/modal_actions_confirm" .}}
</div>
diff --git a/templates/org/worktime.tmpl b/templates/org/worktime.tmpl
new file mode 100644
index 0000000000..5d99998129
--- /dev/null
+++ b/templates/org/worktime.tmpl
@@ -0,0 +1,40 @@
+{{template "base/head" .}}
+<div class="page-content organization times">
+ {{template "org/header" .}}
+ <div class="ui container">
+ <div class="ui grid">
+ <div class="three wide column">
+ <form class="ui form" method="get">
+ <input type="hidden" name="by" value="{{$.WorktimeBy}}">
+ <div class="field">
+ <label>{{ctx.Locale.Tr "org.worktime.date_range_start"}}</label>
+ <input type="date" name="from" value="{{.RangeFrom}}">
+ </div>
+ <div class="field">
+ <label>{{ctx.Locale.Tr "org.worktime.date_range_end"}}</label>
+ <input type="date" name="to" value="{{.RangeTo}}">
+ </div>
+ <button class="ui primary button">{{ctx.Locale.Tr "org.worktime.query"}}</button>
+ </form>
+ </div>
+ <div class="thirteen wide column">
+ <div class="ui column">
+ <div class="ui compact small menu">
+ {{$queryParams := QueryBuild "from" .RangeFrom "to" .RangeTo}}
+ <a class="{{Iif .WorktimeByRepos "active"}} item" href="{{$.Org.OrganisationLink}}/worktime?by=repos&{{$queryParams}}">{{svg "octicon-repo"}} {{ctx.Locale.Tr "org.worktime.by_repositories"}}</a>
+ <a class="{{Iif .WorktimeByMilestones "active"}} item" href="{{$.Org.OrganisationLink}}/worktime?by=milestones&{{$queryParams}}">{{svg "octicon-milestone"}} {{ctx.Locale.Tr "org.worktime.by_milestones"}}</a>
+ <a class="{{Iif .WorktimeByMembers "active"}} item" href="{{$.Org.OrganisationLink}}/worktime?by=members&{{$queryParams}}">{{svg "octicon-people"}} {{ctx.Locale.Tr "org.worktime.by_members"}}</a>
+ </div>
+ </div>
+ {{if .WorktimeByRepos}}
+ {{template "org/worktime/table_repos" dict "Org" .Org "WorktimeSumResult" .WorktimeSumResult}}
+ {{else if .WorktimeByMilestones}}
+ {{template "org/worktime/table_milestones" dict "Org" .Org "WorktimeSumResult" .WorktimeSumResult}}
+ {{else if .WorktimeByMembers}}
+ {{template "org/worktime/table_members" dict "Org" .Org "WorktimeSumResult" .WorktimeSumResult}}
+ {{end}}
+ </div>
+ </div>
+ </div>
+</div>
+{{template "base/footer" .}}
diff --git a/templates/org/worktime/table_members.tmpl b/templates/org/worktime/table_members.tmpl
new file mode 100644
index 0000000000..a59d1941d8
--- /dev/null
+++ b/templates/org/worktime/table_members.tmpl
@@ -0,0 +1,16 @@
+<table class="ui table">
+ <thead>
+ <tr>
+ <th>{{ctx.Locale.Tr "org.members.member"}}</th>
+ <th>{{ctx.Locale.Tr "org.worktime.time"}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{range $.WorktimeSumResult}}
+ <tr>
+ <td>{{svg "octicon-person"}} <a href="{{AppSubUrl}}/{{PathEscape .UserName}}">{{.UserName}}</a></td>
+ <td>{{svg "octicon-clock"}} {{.SumTime | Sec2Hour}}</td>
+ </tr>
+ {{end}}
+ </tbody>
+</table>
diff --git a/templates/org/worktime/table_milestones.tmpl b/templates/org/worktime/table_milestones.tmpl
new file mode 100644
index 0000000000..6ef9289e56
--- /dev/null
+++ b/templates/org/worktime/table_milestones.tmpl
@@ -0,0 +1,28 @@
+<table class="ui table">
+ <thead>
+ <tr>
+ <th>{{ctx.Locale.Tr "repository"}}</th>
+ <th>{{ctx.Locale.Tr "repo.milestone"}}</th>
+ <th>{{ctx.Locale.Tr "org.worktime.time"}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{range $.WorktimeSumResult}}
+ <tr>
+ <td>
+ {{if not .HideRepoName}}
+ {{svg "octicon-repo"}} <a href="{{$.Org.HomeLink}}/{{PathEscape .RepoName}}/issues">{{.RepoName}}</a>
+ {{end}}
+ </td>
+ <td>
+ {{if .MilestoneName}}
+ {{svg "octicon-milestone"}} <a href="{{$.Org.HomeLink}}/{{PathEscape .RepoName}}/milestone/{{.MilestoneID}}">{{.MilestoneName}}</a>
+ {{else}}
+ -
+ {{end}}
+ </td>
+ <td>{{svg "octicon-clock"}} {{.SumTime | Sec2Hour}}</td>
+ </tr>
+ {{end}}
+ </tbody>
+</table>
diff --git a/templates/org/worktime/table_repos.tmpl b/templates/org/worktime/table_repos.tmpl
new file mode 100644
index 0000000000..eaa085df0c
--- /dev/null
+++ b/templates/org/worktime/table_repos.tmpl
@@ -0,0 +1,16 @@
+<table class="ui table">
+ <thead>
+ <tr>
+ <th>{{ctx.Locale.Tr "repository"}}</th>
+ <th>{{ctx.Locale.Tr "org.worktime.time"}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{range $.WorktimeSumResult}}
+ <tr>
+ <td>{{svg "octicon-repo"}} <a href="{{$.Org.HomeLink}}/{{PathEscape .RepoName}}/issues">{{.RepoName}}</a></td>
+ <td>{{svg "octicon-clock"}} {{.SumTime | Sec2Hour}}</td>
+ </tr>
+ {{end}}
+ </tbody>
+</table>
diff --git a/templates/package/content/container.tmpl b/templates/package/content/container.tmpl
index 04732d276a..1a1335aaa6 100644
--- a/templates/package/content/container.tmpl
+++ b/templates/package/content/container.tmpl
@@ -16,7 +16,17 @@
</div>
<div class="field">
<label>{{svg "octicon-code"}} {{ctx.Locale.Tr "packages.container.digest"}}</label>
- <div class="markup"><pre class="code-block"><code>{{range .PackageDescriptor.Files}}{{if eq .File.LowerName "manifest.json"}}{{.Properties.GetByName "container.digest"}}{{end}}{{end}}</code></pre></div>
+ <div class="markup">
+ <div class="code-block-container code-overflow-scroll">
+ <pre class="code-block"><code>
+ {{- range .PackageDescriptor.Files -}}
+ {{- if eq .File.LowerName "manifest.json" -}}
+ {{- .Properties.GetByName "container.digest" -}}{{"\n"}}
+ {{- end -}}
+ {{- end -}}
+ </code></pre>
+ </div>
+ </div>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "packages.registry.documentation" "Container" "https://docs.gitea.com/usage/packages/container/"}}</label>
@@ -24,7 +34,7 @@
</div>
</div>
{{if .PackageDescriptor.Metadata.Manifests}}
- <h4 class="ui top attached header">{{ctx.Locale.Tr "packages.container.multi_arch"}}</h4>
+ <h4 class="ui top attached header">{{ctx.Locale.Tr "packages.container.images"}}</h4>
<div class="ui attached segment">
<table class="ui very basic compact table">
<thead>
@@ -39,7 +49,11 @@
{{/* "unknown/unknown" is attestation-manifest, so we should skip it */}}
{{if ne .Platform "unknown/unknown"}}
<tr>
- <td><a class="tw-font-mono" href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .Digest}}">{{StringUtils.TrimPrefix .Digest "sha256:" | ShortSha}}</a></td>
+ <td>
+ <a class="tw-font-mono" href="{{$.PackageDescriptor.PackageWebLink}}/{{$.PackageDescriptor.Version.LowerVersion}}/{{PathEscape .Digest}}">
+ {{StringUtils.TrimPrefix .Digest "sha256:" | ShortSha}}
+ </a>
+ </td>
<td>{{.Platform}}</td>
<td>{{FileSize .Size}}</td>
</tr>
@@ -55,12 +69,24 @@
{{.PackageDescriptor.Metadata.Description}}
</div>
{{end}}
- {{if .PackageDescriptor.Metadata.ImageLayers}}
- <h4 class="ui top attached header">{{ctx.Locale.Tr "packages.container.layers"}}</h4>
+
+ {{/* a container manifest may contain sub manifests, so here we try to display some information of the sub manifest,
+ not perfect, just better than before */}}
+ {{$imageMetadata := .ContainerImageMetadata}}
+ {{if $imageMetadata.ImageLayers}}
+ <h4 class="ui top attached header flex-text-block">
+ {{ctx.Locale.Tr "packages.container.layers"}}
+ {{/* only show the platform if the image metadata is not the package's, which means that it is a sub manifest */}}
+ {{if ne .ContainerImageMetadata .PackageDescriptor.Metadata}}
+ <span class="tw-text-sm flex-text-inline" title="{{ctx.Locale.Tr "packages.container.details.platform"}}">
+ ({{svg "octicon-cpu" 12}} {{.ContainerImageMetadata.Platform}})
+ </span>
+ {{end}}
+ </h4>
<div class="ui attached segment tw-break-anywhere">
- <table class="ui very basic compact table">
+ <table class="ui very basic compact table tw-font-mono">
<tbody>
- {{range .PackageDescriptor.Metadata.ImageLayers}}
+ {{range $imageMetadata.ImageLayers}}
<tr>
<td>{{.}}</td>
</tr>
@@ -69,10 +95,10 @@
</table>
</div>
{{end}}
- {{if .PackageDescriptor.Metadata.Labels}}
+ {{if $imageMetadata.Labels}}
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.container.labels"}}</h4>
<div class="ui attached segment">
- <table class="ui very basic compact table">
+ <table class="ui very basic compact table tw-font-mono">
<thead>
<tr>
<th>{{ctx.Locale.Tr "packages.container.labels.key"}}</th>
@@ -80,9 +106,9 @@
</tr>
</thead>
<tbody>
- {{range $key, $value := .PackageDescriptor.Metadata.Labels}}
+ {{range $key, $value := $imageMetadata.Labels}}
<tr>
- <td class="top aligned">{{$key}}</td>
+ <td class="tw-align-top">{{$key}}</td>
<td class="tw-break-anywhere">{{$value}}</td>
</tr>
{{end}}
diff --git a/templates/package/content/maven.tmpl b/templates/package/content/maven.tmpl
index f56595a830..e98fc53692 100644
--- a/templates/package/content/maven.tmpl
+++ b/templates/package/content/maven.tmpl
@@ -11,7 +11,7 @@
<div class="markup"><pre class="code-block"><code>&lt;repositories&gt;
&lt;repository&gt;
&lt;id&gt;gitea&lt;/id&gt;
- &lt;url&gt;<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></origin-url>&lt;/url&gt;
+ &lt;url&gt;<origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/maven"></origin-url>&lt;/url&gt;
&lt;/repository&gt;
&lt;/repositories&gt;
diff --git a/templates/package/content/pypi.tmpl b/templates/package/content/pypi.tmpl
index 2a22a6ed71..2625c160fe 100644
--- a/templates/package/content/pypi.tmpl
+++ b/templates/package/content/pypi.tmpl
@@ -4,7 +4,7 @@
<div class="ui form">
<div class="field">
<label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.pypi.install"}}</label>
- <div class="markup"><pre class="code-block"><code>pip install --index-url <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple/"></origin-url> {{.PackageDescriptor.Package.Name}}</code></pre></div>
+ <div class="markup"><pre class="code-block"><code>pip install --index-url <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple/"></origin-url> --extra-index-url https://pypi.org/ {{.PackageDescriptor.Package.Name}}</code></pre></div>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "packages.registry.documentation" "PyPI" "https://docs.gitea.com/usage/packages/pypi/"}}</label>
diff --git a/templates/package/shared/view.tmpl b/templates/package/shared/view.tmpl
new file mode 100644
index 0000000000..52673accf9
--- /dev/null
+++ b/templates/package/shared/view.tmpl
@@ -0,0 +1,107 @@
+<div class="issue-title-header">
+ {{$packageVersionLink := print $.PackageDescriptor.PackageWebLink "/" (PathEscape .PackageDescriptor.Version.LowerVersion)}}
+ <h1>{{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}})</h1>
+ <div>
+ {{$timeStr := DateUtils.TimeSince .PackageDescriptor.Version.CreatedUnix}}
+ {{if .HasRepositoryAccess}}
+ {{ctx.Locale.Tr "packages.published_by_in" $timeStr .PackageDescriptor.Creator.HomeLink .PackageDescriptor.Creator.GetDisplayName .PackageDescriptor.Repository.Link .PackageDescriptor.Repository.FullName}}
+ {{else}}
+ {{ctx.Locale.Tr "packages.published_by" $timeStr .PackageDescriptor.Creator.HomeLink .PackageDescriptor.Creator.GetDisplayName}}
+ {{end}}
+ </div>
+</div>
+<div class="packages-content">
+ <div class="packages-content-left">
+ {{template "package/content/alpine" .}}
+ {{template "package/content/arch" .}}
+ {{template "package/content/cargo" .}}
+ {{template "package/content/chef" .}}
+ {{template "package/content/composer" .}}
+ {{template "package/content/conan" .}}
+ {{template "package/content/conda" .}}
+ {{template "package/content/container" .}}
+ {{template "package/content/cran" .}}
+ {{template "package/content/debian" .}}
+ {{template "package/content/generic" .}}
+ {{template "package/content/go" .}}
+ {{template "package/content/helm" .}}
+ {{template "package/content/maven" .}}
+ {{template "package/content/npm" .}}
+ {{template "package/content/nuget" .}}
+ {{template "package/content/pub" .}}
+ {{template "package/content/pypi" .}}
+ {{template "package/content/rpm" .}}
+ {{template "package/content/rubygems" .}}
+ {{template "package/content/swift" .}}
+ {{template "package/content/vagrant" .}}
+ </div>
+ <div class="ui segment packages-content-right">
+ <strong>{{ctx.Locale.Tr "packages.details"}}</strong>
+ <div class="ui relaxed list flex-items-block">
+ <div class="item">{{svg .PackageDescriptor.Package.Type.SVGName}} {{.PackageDescriptor.Package.Type.Name}}</div>
+ {{if .HasRepositoryAccess}}
+ <div class="item">{{svg "octicon-repo"}} <a href="{{.PackageDescriptor.Repository.Link}}">{{.PackageDescriptor.Repository.FullName}}</a></div>
+ {{end}}
+ <div class="item">{{svg "octicon-calendar"}} {{DateUtils.TimeSince .PackageDescriptor.Version.CreatedUnix}}</div>
+ <div class="item">{{svg "octicon-download"}} {{.PackageDescriptor.Version.DownloadCount}}</div>
+ {{template "package/metadata/alpine" .}}
+ {{template "package/metadata/arch" .}}
+ {{template "package/metadata/cargo" .}}
+ {{template "package/metadata/chef" .}}
+ {{template "package/metadata/composer" .}}
+ {{template "package/metadata/conan" .}}
+ {{template "package/metadata/conda" .}}
+ {{template "package/metadata/container" .}}
+ {{template "package/metadata/cran" .}}
+ {{template "package/metadata/debian" .}}
+ {{template "package/metadata/generic" .}}
+ {{template "package/metadata/helm" .}}
+ {{template "package/metadata/maven" .}}
+ {{template "package/metadata/npm" .}}
+ {{template "package/metadata/nuget" .}}
+ {{template "package/metadata/pub" .}}
+ {{template "package/metadata/pypi" .}}
+ {{template "package/metadata/rpm" .}}
+ {{template "package/metadata/rubygems" .}}
+ {{template "package/metadata/swift" .}}
+ {{template "package/metadata/vagrant" .}}
+ {{if not (and (eq .PackageDescriptor.Package.Type "container") .PackageDescriptor.Metadata.Manifests)}}
+ <div class="item">{{svg "octicon-database"}} {{FileSize .PackageDescriptor.CalculateBlobSize}}</div>
+ {{end}}
+ </div>
+ {{if not (eq .PackageDescriptor.Package.Type "container")}}
+ <div class="divider"></div>
+ <strong>{{ctx.Locale.Tr "packages.assets"}} ({{len .PackageDescriptor.Files}})</strong>
+ <div class="ui relaxed list">
+ {{range .PackageDescriptor.Files}}
+ <div class="item">
+ <a href="{{$packageVersionLink}}/files/{{.File.ID}}">{{.File.Name}}</a>
+ <span class="text small tw-whitespace-nowrap">{{FileSize .Blob.Size}}</span>
+ </div>
+ {{end}}
+ </div>
+ {{end}}
+ <div class="divider"></div>
+ <strong>{{ctx.Locale.Tr "packages.versions"}} ({{.TotalVersionCount}})</strong>
+ <a class="tw-float-right" href="{{$.PackageDescriptor.PackageWebLink}}/versions">{{ctx.Locale.Tr "packages.versions.view_all"}}</a>
+ <div class="ui relaxed list">
+ {{range .LatestVersions}}
+ <div class="item tw-flex">
+ <a class="tw-flex-1 gt-ellipsis" title="{{.Version}}" href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .LowerVersion}}">{{.Version}}</a>
+ <span class="text small">{{DateUtils.AbsoluteShort .CreatedUnix}}</span>
+ </div>
+ {{end}}
+ </div>
+ {{if or .CanWritePackages .HasRepositoryAccess}}
+ <div class="divider"></div>
+ <div class="ui relaxed list flex-items-block">
+ {{if .HasRepositoryAccess}}
+ <div class="item">{{svg "octicon-issue-opened"}} <a href="{{.PackageDescriptor.Repository.Link}}/issues">{{ctx.Locale.Tr "repo.issues"}}</a></div>
+ {{end}}
+ {{if .CanWritePackages}}
+ <div class="item">{{svg "octicon-tools"}} <a href="{{$packageVersionLink}}/settings">{{ctx.Locale.Tr "repo.settings"}}</a></div>
+ {{end}}
+ </div>
+ {{end}}
+ </div>
+</div>
diff --git a/templates/package/view.tmpl b/templates/package/view.tmpl
index 9e92207466..9067f44296 100644
--- a/templates/package/view.tmpl
+++ b/templates/package/view.tmpl
@@ -1,114 +1,24 @@
{{template "base/head" .}}
-<div role="main" aria-label="{{.Title}}" class="page-content repository packages">
- {{template "shared/user/org_profile_avatar" .}}
+{{if .ContextUser.IsOrganization}}
+<div role="main" aria-label="{{.Title}}" class="page-content organization packages">
+ {{template "org/header" .}}
<div class="ui container">
- {{template "user/overview/header" .}}
- <div class="issue-title-header">
- <h1>{{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}})</h1>
- <div>
- {{$timeStr := DateUtils.TimeSince .PackageDescriptor.Version.CreatedUnix}}
- {{if .HasRepositoryAccess}}
- {{ctx.Locale.Tr "packages.published_by_in" $timeStr .PackageDescriptor.Creator.HomeLink .PackageDescriptor.Creator.GetDisplayName .PackageDescriptor.Repository.Link .PackageDescriptor.Repository.FullName}}
- {{else}}
- {{ctx.Locale.Tr "packages.published_by" $timeStr .PackageDescriptor.Creator.HomeLink .PackageDescriptor.Creator.GetDisplayName}}
- {{end}}
- </div>
- </div>
- <div class="issue-content">
- <div class="issue-content-left">
- {{template "package/content/alpine" .}}
- {{template "package/content/arch" .}}
- {{template "package/content/cargo" .}}
- {{template "package/content/chef" .}}
- {{template "package/content/composer" .}}
- {{template "package/content/conan" .}}
- {{template "package/content/conda" .}}
- {{template "package/content/container" .}}
- {{template "package/content/cran" .}}
- {{template "package/content/debian" .}}
- {{template "package/content/generic" .}}
- {{template "package/content/go" .}}
- {{template "package/content/helm" .}}
- {{template "package/content/maven" .}}
- {{template "package/content/npm" .}}
- {{template "package/content/nuget" .}}
- {{template "package/content/pub" .}}
- {{template "package/content/pypi" .}}
- {{template "package/content/rpm" .}}
- {{template "package/content/rubygems" .}}
- {{template "package/content/swift" .}}
- {{template "package/content/vagrant" .}}
+ {{template "package/shared/view" .}}
+ </div>
+</div>
+{{else}}
+<div role="main" aria-label="{{.Title}}" class="page-content user profile packages">
+ <div class="ui container">
+ <div class="ui stackable grid">
+ <div class="ui four wide column">
+ {{template "shared/user/profile_big_avatar" .}}
</div>
- <div class="issue-content-right ui segment">
- <strong>{{ctx.Locale.Tr "packages.details"}}</strong>
- <div class="ui relaxed list flex-items-block">
- <div class="item">{{svg .PackageDescriptor.Package.Type.SVGName}} {{.PackageDescriptor.Package.Type.Name}}</div>
- {{if .HasRepositoryAccess}}
- <div class="item">{{svg "octicon-repo"}} <a href="{{.PackageDescriptor.Repository.Link}}">{{.PackageDescriptor.Repository.FullName}}</a></div>
- {{end}}
- <div class="item">{{svg "octicon-calendar"}} {{DateUtils.TimeSince .PackageDescriptor.Version.CreatedUnix}}</div>
- <div class="item">{{svg "octicon-download"}} {{.PackageDescriptor.Version.DownloadCount}}</div>
- {{template "package/metadata/alpine" .}}
- {{template "package/metadata/arch" .}}
- {{template "package/metadata/cargo" .}}
- {{template "package/metadata/chef" .}}
- {{template "package/metadata/composer" .}}
- {{template "package/metadata/conan" .}}
- {{template "package/metadata/conda" .}}
- {{template "package/metadata/container" .}}
- {{template "package/metadata/cran" .}}
- {{template "package/metadata/debian" .}}
- {{template "package/metadata/generic" .}}
- {{template "package/metadata/helm" .}}
- {{template "package/metadata/maven" .}}
- {{template "package/metadata/npm" .}}
- {{template "package/metadata/nuget" .}}
- {{template "package/metadata/pub" .}}
- {{template "package/metadata/pypi" .}}
- {{template "package/metadata/rpm" .}}
- {{template "package/metadata/rubygems" .}}
- {{template "package/metadata/swift" .}}
- {{template "package/metadata/vagrant" .}}
- {{if not (and (eq .PackageDescriptor.Package.Type "container") .PackageDescriptor.Metadata.Manifests)}}
- <div class="item">{{svg "octicon-database"}} {{FileSize .PackageDescriptor.CalculateBlobSize}}</div>
- {{end}}
- </div>
- {{if not (eq .PackageDescriptor.Package.Type "container")}}
- <div class="divider"></div>
- <strong>{{ctx.Locale.Tr "packages.assets"}} ({{len .PackageDescriptor.Files}})</strong>
- <div class="ui relaxed list">
- {{range .PackageDescriptor.Files}}
- <div class="item">
- <a href="{{$.Link}}/files/{{.File.ID}}">{{.File.Name}}</a>
- <span class="text small file-size">{{FileSize .Blob.Size}}</span>
- </div>
- {{end}}
- </div>
- {{end}}
- <div class="divider"></div>
- <strong>{{ctx.Locale.Tr "packages.versions"}} ({{.TotalVersionCount}})</strong>
- <a class="tw-float-right" href="{{$.PackageDescriptor.PackageWebLink}}/versions">{{ctx.Locale.Tr "packages.versions.view_all"}}</a>
- <div class="ui relaxed list">
- {{range .LatestVersions}}
- <div class="item tw-flex">
- <a class="tw-flex-1 gt-ellipsis" title="{{.Version}}" href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .LowerVersion}}">{{.Version}}</a>
- <span class="text small">{{DateUtils.AbsoluteShort .CreatedUnix}}</span>
- </div>
- {{end}}
- </div>
- {{if or .CanWritePackages .HasRepositoryAccess}}
- <div class="divider"></div>
- <div class="ui relaxed list flex-items-block">
- {{if .HasRepositoryAccess}}
- <div class="item">{{svg "octicon-issue-opened"}} <a href="{{.PackageDescriptor.Repository.Link}}/issues">{{ctx.Locale.Tr "repo.issues"}}</a></div>
- {{end}}
- {{if .CanWritePackages}}
- <div class="item">{{svg "octicon-tools"}} <a href="{{.Link}}/settings">{{ctx.Locale.Tr "repo.settings"}}</a></div>
- {{end}}
- </div>
- {{end}}
+ <div class="ui twelve wide column tw-mb-4">
+ {{template "user/overview/header" .}}
+ {{template "package/shared/view" .}}
</div>
</div>
</div>
</div>
+{{end}}
{{template "base/footer" .}}
diff --git a/templates/post-install.tmpl b/templates/post-install.tmpl
index fa10827295..9baac4f84c 100644
--- a/templates/post-install.tmpl
+++ b/templates/post-install.tmpl
@@ -2,9 +2,9 @@
<div role="main" aria-label="{{.Title}}" class="page-content install post-install tw-h-full">
<div class="home tw-text-center tw-h-full tw-flex tw-flex-col tw-justify-center"><!-- the "home" class makes the links green -->
<!-- the "cup" has a handler, so move it a little leftward to make it visually in the center -->
- <div class="tw-ml-[-30px]"><img width="160" src="{{AssetUrlPrefix}}/img/loading.png" alt="" aria-hidden="true"></div>
+ <div class="tw-ml-[-30px]"><img width="160" src="{{AssetUrlPrefix}}/img/loading.png" alt aria-hidden="true"></div>
<div class="tw-my-[2em] tw-text-[18px]">
- <a id="goto-user-login" href="{{AppSubUrl}}/user/login">{{ctx.Locale.Tr "install.installing_desc"}}</a>
+ <a id="goto-after-install" href="{{AppSubUrl}}{{Iif .IsAccountCreated "/user/login" "/user/sign_up"}}">{{ctx.Locale.Tr "install.installing_desc"}}</a>
</div>
</div>
</div>
diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl
index f5a48f7241..e769543f6a 100644
--- a/templates/projects/list.tmpl
+++ b/templates/projects/list.tmpl
@@ -1,5 +1,5 @@
{{if and $.CanWriteProjects (not $.Repository.IsArchived)}}
- <div class="tw-flex tw-justify-between tw-mb-4">
+ <div class="flex-text-block tw-justify-between tw-mb-4">
<div class="small-menu-items ui compact tiny menu list-header-toggle">
<a class="item{{if not .IsShowClosed}} active{{end}}" href="?state=open&q={{$.Keyword}}">
{{svg "octicon-project-symlink" 16 "tw-mr-2"}}
@@ -10,9 +10,7 @@
{{ctx.Locale.PrettyNumber .ClosedCount}}&nbsp;{{ctx.Locale.Tr "repo.issues.closed_title"}}
</a>
</div>
- <div class="tw-text-right">
- <a class="ui small primary button" href="{{$.Link}}/new">{{ctx.Locale.Tr "repo.projects.new"}}</a>
- </div>
+ <a class="ui small primary button" href="{{$.Link}}/new">{{ctx.Locale.Tr "repo.projects.new"}}</a>
</div>
{{end}}
@@ -34,6 +32,8 @@
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="?q={{$.Keyword}}&sort=oldest&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
+ <a class="{{if eq .SortType "alphabetically"}}active {{end}}item" href="?q={{$.Keyword}}&sort=alphabetically&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
+ <a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="?q={{$.Keyword}}&sort=reversealphabetically&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="?q={{$.Keyword}}&sort=recentupdate&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="?q={{$.Keyword}}&sort=leastupdate&state={{$.State}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
</div>
@@ -52,11 +52,11 @@
<div class="group">
<div class="flex-text-block">
{{svg "octicon-issue-opened" 14}}
- {{ctx.Locale.PrettyNumber (.NumOpenIssues ctx)}}&nbsp;{{ctx.Locale.Tr "repo.issues.open_title"}}
+ {{ctx.Locale.PrettyNumber .NumOpenIssues}}&nbsp;{{ctx.Locale.Tr "repo.issues.open_title"}}
</div>
<div class="flex-text-block">
{{svg "octicon-check" 14}}
- {{ctx.Locale.PrettyNumber (.NumClosedIssues ctx)}}&nbsp;{{ctx.Locale.Tr "repo.issues.closed_title"}}
+ {{ctx.Locale.PrettyNumber .NumClosedIssues}}&nbsp;{{ctx.Locale.Tr "repo.issues.closed_title"}}
</div>
</div>
{{if and $.CanWriteProjects (not $.Repository.IsArchived)}}
@@ -67,14 +67,12 @@
{{else}}
<a class="link-action flex-text-inline" href data-url="{{.Link ctx}}/close">{{svg "octicon-skip" 14}}{{ctx.Locale.Tr "repo.projects.close"}}</a>
{{end}}
- <a class="delete-button flex-text-inline" href="#" data-url="{{.Link ctx}}/delete">{{svg "octicon-trash" 14}}{{ctx.Locale.Tr "repo.issues.label_delete"}}</a>
+ <a class="link-action flex-text-inline text red" href data-modal-confirm="#repo-project-delete-modal" data-url="{{.Link ctx}}/delete">{{svg "octicon-trash" 14}}{{ctx.Locale.Tr "repo.issues.label_delete"}}</a>
</div>
{{end}}
</div>
{{if .Description}}
- <div class="content">
- {{.RenderedContent}}
- </div>
+ <div class="render-content markup">{{.RenderedContent}}</div>
{{end}}
</li>
{{end}}
@@ -83,14 +81,9 @@
</div>
{{if and $.CanWriteProjects (not $.Repository.IsArchived)}}
-<div class="ui g-modal-confirm delete modal">
- <div class="header">
- {{svg "octicon-trash"}}
- {{ctx.Locale.Tr "repo.projects.deletion"}}
- </div>
- <div class="content">
- <p>{{ctx.Locale.Tr "repo.projects.deletion_desc"}}</p>
- </div>
+<div class="ui small modal" id="repo-project-delete-modal">
+ <div class="header">{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.projects.deletion"}}</div>
+ <div class="content"><p>{{ctx.Locale.Tr "repo.projects.deletion_desc"}}</p></div>
{{template "base/modal_actions_confirm" .}}
</div>
{{end}}
diff --git a/templates/projects/new.tmpl b/templates/projects/new.tmpl
index a936079c46..f2630be09b 100644
--- a/templates/projects/new.tmpl
+++ b/templates/projects/new.tmpl
@@ -64,7 +64,7 @@
</div>
</div>
<div class="divider"></div>
- <div class="tw-text-right">
+ <div class="flex-text-block tw-justify-end">
<a class="ui cancel button" href="{{$.CancelLink}}">
{{ctx.Locale.Tr "repo.milestones.cancel"}}
</a>
diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl
index 2550cae69c..6aa776da02 100644
--- a/templates/projects/view.tmpl
+++ b/templates/projects/view.tmpl
@@ -1,27 +1,32 @@
{{$canWriteProject := and .CanWriteProjects (or (not .Repository) (not .Repository.IsArchived))}}
-<div class="ui container tw-max-w-full">
- <div class="tw-flex tw-justify-between tw-items-center tw-mb-4 tw-gap-3">
- <h2 class="tw-mb-0 tw-flex-1 tw-break-anywhere">{{.Project.Title}}</h2>
- <div class="project-toolbar-right">
- <div class="ui secondary filter menu labels">
- {{$queryLink := QueryBuild "?" "labels" .SelectLabels "assignee" $.AssigneeID "archived_labels" (Iif $.ShowArchivedLabels "true")}}
-
- {{template "repo/issue/filter_item_label" dict "Labels" .Labels "QueryLink" $queryLink "SupportArchivedLabel" true}}
-
- {{template "repo/issue/filter_item_user_assign" dict
- "QueryParamKey" "assignee"
- "QueryLink" $queryLink
- "UserSearchList" $.Assignees
- "SelectedUserId" $.AssigneeID
- "TextFilterTitle" (ctx.Locale.Tr "repo.issues.filter_assignee")
- "TextZeroValue" (ctx.Locale.Tr "repo.issues.filter_assginee_no_select")
- "TextNegativeOne" (ctx.Locale.Tr "repo.issues.filter_assginee_no_assignee")
- }}
- </div>
- </div>
+<div class="ui container fluid padded projects-view">
+ <div class="ui container flex-text-block project-header">
+ <h2>{{.Project.Title}}</h2>
+ <div class="tw-flex-1"></div>
+ <div class="ui secondary menu tw-m-0">
+ {{$queryLink := QueryBuild "?" "labels" .SelectLabels "assignee" $.AssigneeID "archived_labels" (Iif $.ShowArchivedLabels "true")}}
+ {{template "repo/issue/filter_item_label" dict "Labels" .Labels "QueryLink" $queryLink "SupportArchivedLabel" true}}
+ {{template "repo/issue/filter_item_user_assign" dict
+ "QueryParamKey" "assignee"
+ "QueryLink" $queryLink
+ "UserSearchList" $.Assignees
+ "SelectedUserId" $.AssigneeID
+ "TextFilterTitle" (ctx.Locale.Tr "repo.issues.filter_assignee")
+ "TextFilterMatchNone" (ctx.Locale.Tr "repo.issues.filter_assignee_no_assignee")
+ "TextFilterMatchAny" (ctx.Locale.Tr "repo.issues.filter_assignee_any_assignee")
+ }}
+ </div>
{{if $canWriteProject}}
<div class="ui compact mini menu">
+ <a class="item screen-full">
+ {{svg "octicon-screen-full"}}
+ {{ctx.Locale.Tr "projects.enter_fullscreen"}}
+ </a>
+ <a class="item screen-normal tw-hidden">
+ {{svg "octicon-screen-normal"}}
+ {{ctx.Locale.Tr "projects.exit_fullscreen"}}
+ </a>
<a class="item" href="{{.Link}}/edit?redirect=project">
{{svg "octicon-pencil"}}
{{ctx.Locale.Tr "repo.issues.label_edit"}}
@@ -59,18 +64,17 @@
{{end}}
</div>
- <div class="content">{{$.Project.RenderedContent}}</div>
-
- <div class="divider"></div>
-</div>
+ <div class="ui container project-description">
+ {{$.Project.RenderedContent}}
+ <div class="divider"></div>
+ </div>
-<div id="project-board" data-project-borad-writable="{{$canWriteProject}}">
- <div class="board {{if $canWriteProject}}sortable{{end}}" {{if $canWriteProject}}data-url="{{$.Link}}/move"{{end}}>
+ <div id="project-board" class="board {{if $canWriteProject}}sortable{{end}}" data-project-borad-writable="{{$canWriteProject}}" {{if $canWriteProject}}data-url="{{$.Link}}/move"{{end}}>
{{range .Columns}}
<div class="project-column" {{if .Color}}style="background: {{.Color}} !important; color: {{ContrastColor .Color}} !important"{{end}} data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.Link}}/{{.ID}}">
<div class="project-column-header{{if $canWriteProject}} tw-cursor-grab{{end}}">
<div class="ui circular label project-column-issue-count">
- {{.NumIssues ctx}}
+ {{.NumIssues}}
</div>
<div class="project-column-title-text gt-ellipsis">{{.Title}}</div>
{{if $canWriteProject}}
@@ -126,16 +130,16 @@
<input class="project-column-id" type="hidden" name="id">
<div class="required field">
<label class="project-column-title-label" for="project-column-title-input">title</label>
- <input id="project-column-title-input" name="title" value="{{.Title}}" required>
+ <input id="project-column-title-input" name="title" required>
</div>
<div class="field">
<label class="project-column-color-label" for="project-column-color-input">color</label>
<div class="js-color-picker-input column">
- <input maxlength="7" placeholder="#c320f6" id="project-column-color-input" name="color" value="{{.Color}}">
+ <input maxlength="7" placeholder="#c320f6" id="project-column-color-input" name="color">
{{template "repo/issue/label_precolors"}}
</div>
</div>
- <div class="actions tw-text-right">
+ <div class="actions">
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
<button type="submit" class="ui primary button project-column-button-save">save</button>
</div>
diff --git a/templates/repo/actions/runs_list.tmpl b/templates/repo/actions/runs_list.tmpl
index fa1adb3e3b..23df61a43c 100644
--- a/templates/repo/actions/runs_list.tmpl
+++ b/templates/repo/actions/runs_list.tmpl
@@ -5,36 +5,55 @@
<h2>{{if $.IsFiltered}}{{ctx.Locale.Tr "actions.runs.no_results"}}{{else}}{{ctx.Locale.Tr "actions.runs.no_runs"}}{{end}}</h2>
</div>
{{end}}
- {{range .Runs}}
+ {{range $run := .Runs}}
<div class="flex-item tw-items-center">
<div class="flex-item-leading">
- {{template "repo/actions/status" (dict "status" .Status.String)}}
+ {{template "repo/actions/status" (dict "status" $run.Status.String)}}
</div>
<div class="flex-item-main">
- <a class="flex-item-title" title="{{.Title}}" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
- {{if .Title}}{{.Title}}{{else}}{{ctx.Locale.Tr "actions.runs.empty_commit_message"}}{{end}}
+ <a class="flex-item-title" title="{{$run.Title}}" href="{{$run.Link}}">
+ {{or $run.Title (ctx.Locale.Tr "actions.runs.empty_commit_message")}}
</a>
<div class="flex-item-body">
- <span><b>{{if not $.CurWorkflow}}{{.WorkflowID}} {{end}}#{{.Index}}</b>:</span>
- {{- if .ScheduleID -}}
+ <span><b>{{if not $.CurWorkflow}}{{$run.WorkflowID}} {{end}}#{{$run.Index}}</b>:</span>
+ {{- if $run.ScheduleID -}}
{{ctx.Locale.Tr "actions.runs.scheduled"}}
{{- else -}}
{{ctx.Locale.Tr "actions.runs.commit"}}
- <a href="{{$.RepoLink}}/commit/{{.CommitSHA}}">{{ShortSha .CommitSHA}}</a>
+ <a href="{{$.RepoLink}}/commit/{{$run.CommitSHA}}">{{ShortSha $run.CommitSHA}}</a>
{{ctx.Locale.Tr "actions.runs.pushed_by"}}
- <a href="{{.TriggerUser.HomeLink}}">{{.TriggerUser.GetDisplayName}}</a>
+ <a href="{{$run.TriggerUser.HomeLink}}">{{$run.TriggerUser.GetDisplayName}}</a>
{{- end -}}
</div>
</div>
<div class="flex-item-trailing">
- {{if .IsRefDeleted}}
- <span class="ui label run-list-ref gt-ellipsis tw-line-through" data-tooltip-content="{{.PrettyRef}}">{{.PrettyRef}}</span>
+ {{if $run.IsRefDeleted}}
+ <span class="ui label run-list-ref gt-ellipsis tw-line-through" data-tooltip-content="{{$run.PrettyRef}}">{{$run.PrettyRef}}</span>
{{else}}
- <a class="ui label run-list-ref gt-ellipsis" href="{{.RefLink}}" data-tooltip-content="{{.PrettyRef}}">{{.PrettyRef}}</a>
+ <a class="ui label run-list-ref gt-ellipsis" href="{{$run.RefLink}}" data-tooltip-content="{{$run.PrettyRef}}">{{$run.PrettyRef}}</a>
{{end}}
<div class="run-list-item-right">
- <div class="run-list-meta">{{svg "octicon-calendar" 16}}{{DateUtils.TimeSince .Updated}}</div>
- <div class="run-list-meta">{{svg "octicon-stopwatch" 16}}{{.Duration}}</div>
+ <div class="run-list-meta">{{svg "octicon-calendar" 16}}{{DateUtils.TimeSince $run.Updated}}</div>
+ <div class="run-list-meta">{{svg "octicon-stopwatch" 16}}{{$run.Duration}}</div>
+ </div>
+ <div class="ui dropdown jump tw-p-2">
+ {{svg "octicon-kebab-horizontal"}}
+ <div class="menu flex-items-menu">
+ <a class="item" href="{{$run.Link}}/workflow">{{svg "octicon-play"}}{{ctx.Locale.Tr "actions.runs.view_workflow_file"}}</a>
+ {{if and $.CanWriteRepoUnitActions (not $run.Status.IsDone)}}
+ <a class="item link-action" data-url="{{$run.Link}}/cancel">
+ {{svg "octicon-x"}}{{ctx.Locale.Tr "actions.runs.cancel"}}
+ </a>
+ {{end}}
+ {{if and $.CanWriteRepoUnitActions $run.Status.IsDone}}
+ <a class="item link-action"
+ data-url="{{$run.Link}}/delete"
+ data-modal-confirm="{{ctx.Locale.Tr "actions.runs.delete.description"}}"
+ >
+ {{svg "octicon-trash"}}{{ctx.Locale.Tr "actions.runs.delete"}}
+ </a>
+ {{end}}
+ </div>
</div>
</div>
</div>
diff --git a/templates/repo/actions/status.tmpl b/templates/repo/actions/status.tmpl
index 64c2543302..f2020bc160 100644
--- a/templates/repo/actions/status.tmpl
+++ b/templates/repo/actions/status.tmpl
@@ -16,7 +16,7 @@
{{else if eq .status "blocked"}}
{{svg "octicon-blocked" $size (printf "text yellow %s" $className)}}
{{else if eq .status "running"}}
- {{svg "octicon-meter" $size (printf "text yellow job-status-rotate %s" $className)}}
+ {{svg "octicon-meter" $size (printf "text yellow circular-spin %s" $className)}}
{{else}}{{/*failure, unknown*/}}
{{svg "octicon-x-circle-fill" $size (printf "text red %s" $className)}}
{{end}}
diff --git a/templates/repo/actions/view_component.tmpl b/templates/repo/actions/view_component.tmpl
index 8d1de41f70..4e338ffcfc 100644
--- a/templates/repo/actions/view_component.tmpl
+++ b/templates/repo/actions/view_component.tmpl
@@ -4,7 +4,7 @@
data-actions-url="{{.ActionsURL}}"
data-locale-approve="{{ctx.Locale.Tr "repo.diff.review.approve"}}"
- data-locale-cancel="{{ctx.Locale.Tr "cancel"}}"
+ data-locale-cancel="{{ctx.Locale.Tr "actions.runs.cancel"}}"
data-locale-rerun="{{ctx.Locale.Tr "rerun"}}"
data-locale-rerun-all="{{ctx.Locale.Tr "rerun_all"}}"
data-locale-runs-scheduled="{{ctx.Locale.Tr "actions.runs.scheduled"}}"
@@ -19,6 +19,7 @@
data-locale-status-skipped="{{ctx.Locale.Tr "actions.status.skipped"}}"
data-locale-status-blocked="{{ctx.Locale.Tr "actions.status.blocked"}}"
data-locale-artifacts-title="{{ctx.Locale.Tr "artifacts"}}"
+ data-locale-artifact-expired="{{ctx.Locale.Tr "expired"}}"
data-locale-confirm-delete-artifact="{{ctx.Locale.Tr "confirm_delete_artifact"}}"
data-locale-show-timestamps="{{ctx.Locale.Tr "show_timestamps"}}"
data-locale-show-log-seconds="{{ctx.Locale.Tr "show_log_seconds"}}"
diff --git a/templates/repo/actions/workflow_dispatch.tmpl b/templates/repo/actions/workflow_dispatch.tmpl
index 21f3ef2077..540bbe9162 100644
--- a/templates/repo/actions/workflow_dispatch.tmpl
+++ b/templates/repo/actions/workflow_dispatch.tmpl
@@ -10,8 +10,8 @@
<span class="ui inline required field">
<label>{{ctx.Locale.Tr "actions.workflow.from_ref"}}:</label>
</span>
- <div class="ui inline field dropdown button select-branch branch-selector-dropdown ellipsis-items-nowrap">
- <input type="hidden" name="ref" value="refs/heads/{{index .Branches 0}}">
+ <div class="ui inline field dropdown button select-branch branch-selector-dropdown ellipsis-text-items">
+ <input type="hidden" name="ref" hx-sync="this:replace" hx-target="#runWorkflowDispatchModalInputs" hx-swap="innerHTML" hx-get="{{$.Link}}/workflow-dispatch-inputs?workflow={{$.CurWorkflow}}" hx-trigger="change" value="refs/heads/{{index .Branches 0}}">
{{svg "octicon-git-branch" 14}}
<div class="default text">{{index .Branches 0}}</div>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
@@ -49,30 +49,9 @@
<div class="divider"></div>
- {{range $item := .WorkflowDispatchConfig.Inputs}}
- <div class="ui field {{if .Required}}required{{end}}">
- {{if eq .Type "choice"}}
- <label>{{.Description}}:</label>
- <select class="ui selection type dropdown" name="{{.Name}}">
- {{range .Options}}
- <option value="{{.}}" {{if eq $item.Default .}}selected{{end}} >{{.}}</option>
- {{end}}
- </select>
- {{else if eq .Type "boolean"}}
- <div class="ui inline checkbox">
- <label>{{.Description}}</label>
- <input type="checkbox" name="{{.Name}}" {{if eq .Default "true"}}checked{{end}}>
- </div>
- {{else if eq .Type "number"}}
- <label>{{.Description}}:</label>
- <input name="{{.Name}}" value="{{.Default}}" {{if .Required}}required{{end}}>
- {{else}}
- <label>{{.Description}}:</label>
- <input name="{{.Name}}" value="{{.Default}}" {{if .Required}}required{{end}}>
- {{end}}
+ <div id="runWorkflowDispatchModalInputs">
+ {{template "repo/actions/workflow_dispatch_inputs" .}}
</div>
- {{end}}
- <button class="ui tiny primary button" type="submit">Submit</button>
</form>
</div>
</div>
diff --git a/templates/repo/actions/workflow_dispatch_inputs.tmpl b/templates/repo/actions/workflow_dispatch_inputs.tmpl
new file mode 100644
index 0000000000..37538a318f
--- /dev/null
+++ b/templates/repo/actions/workflow_dispatch_inputs.tmpl
@@ -0,0 +1,46 @@
+{{if not .WorkflowDispatchConfig}}
+ <div class="ui error message tw-block">{{/* using "ui message" in "ui form" needs to force to display */}}
+ {{if not .CurWorkflowExists}}
+ {{ctx.Locale.Tr "actions.workflow.not_found" $.CurWorkflow}}
+ {{else}}
+ {{ctx.Locale.Tr "actions.workflow.has_no_workflow_dispatch" $.CurWorkflow}}
+ {{end}}
+ </div>
+{{else}}
+ {{range $item := .WorkflowDispatchConfig.Inputs}}
+ <div class="ui field {{if .Required}}required{{end}}">
+ {{if eq .Type "choice"}}
+ <label>{{or .Description .Name}}:</label>
+ {{/* htmx won't initialize the fomantic dropdown, so it is a standard "select" input */}}
+ <select class="ui selection dropdown" name="{{.Name}}">
+ {{range .Options}}
+ <option value="{{.}}" {{if eq $item.Default .}}selected{{end}}>{{.}}</option>
+ {{end}}
+ </select>
+ {{else if eq .Type "boolean"}}
+ {{/* htmx doesn't trigger our JS code to attach fomantic label to checkbox, so here we use standard checkbox */}}
+ <label class="tw-flex flex-text-inline">
+ <input type="checkbox" name="{{.Name}}" {{if eq .Default "true"}}checked{{end}}>
+ {{or .Description .Name}}
+ </label>
+ {{else if eq .Type "number"}}
+ <label>{{or .Description .Name}}:</label>
+ <input name="{{.Name}}" value="{{.Default}}" {{if .Required}}required{{end}}>
+ {{else}}
+ <label>{{or .Description .Name}}:</label>
+ <input name="{{.Name}}" value="{{.Default}}" {{if .Required}}required{{end}}>
+ {{end}}
+ </div>
+ {{end}}
+ <div class="ui field">
+ {{/* use autofocus here to prevent the "branch selection" dropdown from getting focus, otherwise it will auto popup */}}
+ <button class="ui tiny primary button" autofocus type="submit">{{ctx.Locale.Tr "actions.workflow.run"}}</button>
+ </div>
+{{end}}
+{{range .workflows}}
+ {{if and .ErrMsg (eq .Entry.Name $.CurWorkflow)}}
+ <div class="ui field">
+ <div>{{svg "octicon-alert" 16 "text red"}} {{.ErrMsg}}</div>
+ </div>
+ {{end}}
+{{end}}
diff --git a/templates/repo/blame.tmpl b/templates/repo/blame.tmpl
index 62d1bbf2ba..c4d9f0741f 100644
--- a/templates/repo/blame.tmpl
+++ b/templates/repo/blame.tmpl
@@ -1,5 +1,5 @@
{{if or .UsesIgnoreRevs .FaultyIgnoreRevsFile}}
- {{$revsFileLink := URLJoin .RepoLink "src" .BranchNameSubURL "/.git-blame-ignore-revs"}}
+ {{$revsFileLink := URLJoin .RepoLink "src" .RefTypeNameSubURL "/.git-blame-ignore-revs"}}
{{if .UsesIgnoreRevs}}
<div class="ui info message">
<p>{{ctx.Locale.Tr "repo.blame.ignore_revs" $revsFileLink "?bypass-blame-ignore=true"}}</p>
@@ -10,7 +10,7 @@
</div>
{{end}}
{{end}}
-<div class="{{TabSizeClass .Editorconfig .FileName}} non-diff-file-content">
+<div class="{{TabSizeClass .Editorconfig .FileTreePath}} non-diff-file-content">
<h4 class="file-header ui top attached header tw-flex tw-items-center tw-justify-between tw-flex-wrap">
<div class="file-header-left tw-flex tw-items-center tw-py-2 tw-pr-4">
{{template "repo/file_info" .}}
@@ -18,11 +18,11 @@
<div class="file-header-right file-actions tw-flex tw-items-center tw-flex-wrap">
<div class="ui buttons">
<a class="ui tiny button" href="{{$.RawFileLink}}">{{ctx.Locale.Tr "repo.file_raw"}}</a>
- {{if not .IsViewCommit}}
+ {{if or .RefFullName.IsBranch .RefFullName.IsTag}}
<a class="ui tiny button" href="{{.RepoLink}}/src/commit/{{.CommitID | PathEscape}}/{{.TreePath | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.file_permalink"}}</a>
{{end}}
- <a class="ui tiny button" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.normal_view"}}</a>
- <a class="ui tiny button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.file_history"}}</a>
+ <a class="ui tiny button" href="{{.RepoLink}}/src/{{.RefTypeNameSubURL}}/{{.TreePath | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.normal_view"}}</a>
+ <a class="ui tiny button" href="{{.RepoLink}}/commits/{{.RefTypeNameSubURL}}/{{.TreePath | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.file_history"}}</a>
<button class="ui tiny button unescape-button">{{ctx.Locale.Tr "repo.unescape_control_characters"}}</button>
<button class="ui tiny button escape-button tw-hidden">{{ctx.Locale.Tr "repo.escape_control_characters"}}</button>
</div>
@@ -82,6 +82,8 @@
</table>
{{end}}{{/* end if .IsFileTooLarge */}}
<div class="code-line-menu tippy-target">
+ {{/*FIXME: the "HasSourceRenderedToggle" is never set on blame page, it should mean "whether the file is renderable".
+ If the file is renderable, then it must has the "display=source" parameter to make sure the file view page shows the source code, then line number works. */}}
{{if $.Permission.CanRead ctx.Consts.RepoUnitTypeIssues}}
<a class="item ref-in-new-issue" role="menuitem" data-url-issue-new="{{.RepoLink}}/issues/new" data-url-param-body-link="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}" rel="nofollow noindex">{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}</a>
{{end}}
diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl
index cb504e2b75..9e86641c6f 100644
--- a/templates/repo/branch/list.tmpl
+++ b/templates/repo/branch/list.tmpl
@@ -20,17 +20,17 @@
<tr>
<td>
<div class="flex-text-block">
- <a class="gt-ellipsis" href="{{.RepoLink}}/src/branch/{{PathEscapeSegments .DefaultBranchBranch.DBBranch.Name}}">{{.DefaultBranchBranch.DBBranch.Name}}</a>
+ <a class="gt-ellipsis branch-name" href="{{.RepoLink}}/src/branch/{{PathEscapeSegments .DefaultBranchBranch.DBBranch.Name}}">{{.DefaultBranchBranch.DBBranch.Name}}</a>
{{if .DefaultBranchBranch.IsProtected}}
<span data-tooltip-content="{{ctx.Locale.Tr "repo.settings.protected_branch"}}">{{svg "octicon-shield-lock"}}</span>
{{end}}
<button class="btn interact-fg tw-px-1" data-clipboard-text="{{.DefaultBranchBranch.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_branch"}}">{{svg "octicon-copy" 14}}</button>
{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DefaultBranchBranch.DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DefaultBranchBranch.DBBranch.CommitID)}}
</div>
- <p class="info tw-flex tw-items-center tw-my-1">{{svg "octicon-git-commit" 16 "tw-mr-1"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{ctx.RenderUtils.RenderCommitMessage .DefaultBranchBranch.DBBranch.CommitMessage (.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{DateUtils.TimeSince .DefaultBranchBranch.DBBranch.CommitTime}}{{if .DefaultBranchBranch.DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
+ <p class="info tw-flex tw-items-center tw-my-1">{{svg "octicon-git-commit" 16 "tw-mr-1"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{ctx.RenderUtils.RenderCommitMessage .DefaultBranchBranch.DBBranch.CommitMessage .Repository}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{DateUtils.TimeSince .DefaultBranchBranch.DBBranch.CommitTime}}{{if .DefaultBranchBranch.DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
</td>
{{/* FIXME: here and below, the tw-overflow-visible is not quite right but it is still needed the moment: to show the important buttons when the width is narrow */}}
- <td class="right aligned middle aligned tw-overflow-visible">
+ <td class="tw-text-right tw-overflow-visible">
{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
<button class="btn interact-bg show-create-branch-modal tw-p-2"
data-modal="#create-branch-modal"
@@ -42,7 +42,7 @@
</button>
{{end}}
{{if .EnableFeed}}
- <a role="button" class="btn interact-bg tw-p-2" href="{{$.FeedURL}}/rss/branch/{{PathEscapeSegments .DefaultBranchBranch.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">{{svg "octicon-rss"}}</a>
+ <a role="button" class="btn interact-bg tw-p-2" href="{{$.RepoLink}}/rss/branch/{{PathEscapeSegments .DefaultBranchBranch.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">{{svg "octicon-rss"}}</a>
{{end}}
{{if not $.DisableDownloadSourceArchives}}
<div class="ui dropdown btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "repo.branch.download" ($.DefaultBranchBranch.DBBranch.Name)}}">
@@ -90,25 +90,31 @@
<td class="eight wide">
{{if .DBBranch.IsDeleted}}
<div class="flex-text-block">
- <span class="gt-ellipsis">{{.DBBranch.Name}}</span>
+ <span class="gt-ellipsis branch-name">{{.DBBranch.Name}}</span>
<button class="btn interact-fg tw-px-1" data-clipboard-text="{{.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_branch"}}">{{svg "octicon-copy" 14}}</button>
</div>
<p class="info">{{ctx.Locale.Tr "repo.branch.deleted_by" .DBBranch.DeletedBy.Name}} {{DateUtils.TimeSince .DBBranch.DeletedUnix}}</p>
{{else}}
<div class="flex-text-block">
- <a class="gt-ellipsis" href="{{$.RepoLink}}/src/branch/{{PathEscapeSegments .DBBranch.Name}}">{{.DBBranch.Name}}</a>
+ <a class="gt-ellipsis branch-name" href="{{$.RepoLink}}/src/branch/{{PathEscapeSegments .DBBranch.Name}}">{{.DBBranch.Name}}</a>
{{if .IsProtected}}
<span data-tooltip-content="{{ctx.Locale.Tr "repo.settings.protected_branch"}}">{{svg "octicon-shield-lock"}}</span>
{{end}}
<button class="btn interact-fg tw-px-1" data-clipboard-text="{{.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_branch"}}">{{svg "octicon-copy" 14}}</button>
{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DBBranch.CommitID)}}
</div>
- <p class="info tw-flex tw-items-center tw-my-1">{{svg "octicon-git-commit" 16 "tw-mr-1"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{ctx.RenderUtils.RenderCommitMessage .DBBranch.CommitMessage ($.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{DateUtils.TimeSince .DBBranch.CommitTime}}{{if .DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}} &nbsp;{{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p>
+ <p class="info tw-flex tw-items-center tw-my-1">{{svg "octicon-git-commit" 16 "tw-mr-1"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{ctx.RenderUtils.RenderCommitMessage .DBBranch.CommitMessage $.Repository}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{DateUtils.TimeSince .DBBranch.CommitTime}}{{if .DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}} &nbsp;{{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p>
{{end}}
</td>
<td class="two wide ui">
- {{if and (not .DBBranch.IsDeleted) $.DefaultBranchBranch}}
- <div class="commit-divergence">
+ {{if and (not .DBBranch.IsDeleted) $.DefaultBranchBranch}}
+ {{$tooltipDivergence := ""}}
+ {{if or .CommitsBehind .CommitsAhead}}
+ {{$tooltipDivergence = ctx.Locale.Tr "repo.branch.commits_divergence_from" .CommitsBehind .CommitsAhead $.DefaultBranchBranch.DBBranch.Name}}
+ {{else}}
+ {{$tooltipDivergence = ctx.Locale.Tr "repo.branch.commits_no_divergence" $.DefaultBranchBranch.DBBranch.Name}}
+ {{end}}
+ <div class="commit-divergence" data-tooltip-content="{{$tooltipDivergence}}">
<div class="bar-group">
<div class="count count-behind">{{.CommitsBehind}}</div>
{{/* old code bears 0/0.0 = NaN output, so it might output invalid "width: NaNpx", it just works and doesn't caues any problem. */}}
@@ -119,22 +125,22 @@
<div class="bar bar-ahead" style="width: {{Eval 100 "*" .CommitsAhead "/" "(" .CommitsBehind "+" .CommitsAhead "+" 0.0 ")"}}%"></div>
</div>
</div>
- {{end}}
+ {{end}}
</td>
- <td class="two wide right aligned">
+ <td class="two wide tw-text-right">
{{if not .LatestPullRequest}}
{{if .IsIncluded}}
<span class="ui orange large label" data-tooltip-content="{{ctx.Locale.Tr "repo.branch.included_desc"}}">
{{svg "octicon-git-pull-request"}} {{ctx.Locale.Tr "repo.branch.included"}}
</span>
{{else if and (not .DBBranch.IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}}
- <a href="{{$.RepoLink}}/compare/{{PathEscapeSegments $.DefaultBranchBranch.DBBranch.Name}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{PathEscape $.Owner.Name}}:{{end}}{{PathEscapeSegments .DBBranch.Name}}">
+ <a href="{{$.RepoLink}}/compare/{{PathEscapeSegments $.DefaultBranchBranch.DBBranch.Name}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{PathEscape $.Owner.Name}}:{{end}}{{PathEscapeSegments .DBBranch.Name}}?expand=1">
<button id="new-pull-request" class="ui compact basic button tw-mr-0">{{if $.CanPull}}{{ctx.Locale.Tr "repo.pulls.compare_changes"}}{{else}}{{ctx.Locale.Tr "action.compare_branch"}}{{end}}</button>
</a>
{{end}}
{{else if and .LatestPullRequest.HasMerged .MergeMovedOn}}
{{if and (not .DBBranch.IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}}
- <a href="{{$.RepoLink}}/compare/{{PathEscapeSegments $.DefaultBranchBranch.DBBranch.Name}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{PathEscape $.Owner.Name}}:{{end}}{{PathEscapeSegments .DBBranch.Name}}">
+ <a href="{{$.RepoLink}}/compare/{{PathEscapeSegments $.DefaultBranchBranch.DBBranch.Name}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{PathEscape $.Owner.Name}}:{{end}}{{PathEscapeSegments .DBBranch.Name}}?expand=1">
<button id="new-pull-request" class="ui compact basic button tw-mr-0">{{if $.CanPull}}{{ctx.Locale.Tr "repo.pulls.compare_changes"}}{{else}}{{ctx.Locale.Tr "action.compare_branch"}}{{end}}</button>
</a>
{{end}}
@@ -143,14 +149,14 @@
{{if .LatestPullRequest.HasMerged}}
<a href="{{.LatestPullRequest.Issue.Link}}" class="ui purple large label">{{svg "octicon-git-merge" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.pulls.merged"}}</a>
{{else if .LatestPullRequest.Issue.IsClosed}}
- <a href="{{.LatestPullRequest.Issue.Link}}" class="ui red large label">{{svg "octicon-git-pull-request" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.issues.closed_title"}}</a>
+ <a href="{{.LatestPullRequest.Issue.Link}}" class="ui red large label">{{svg "octicon-git-pull-request-closed" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.issues.closed_title"}}</a>
{{else}}
<a href="{{.LatestPullRequest.Issue.Link}}" class="ui green large label">{{svg "octicon-git-pull-request" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.issues.open_title"}}</a>
{{end}}
{{end}}
</td>
{{/* FIXME: here and above, the tw-overflow-visible is not quite right */}}
- <td class="three wide right aligned tw-overflow-visible">
+ <td class="three wide tw-text-right tw-overflow-visible">
{{if and $.IsWriter (not $.Repository.IsArchived) (not .DBBranch.IsDeleted)}}
<button class="btn interact-bg tw-p-2 show-modal show-create-branch-modal"
data-branch-from="{{.DBBranch.Name}}"
@@ -162,7 +168,7 @@
</button>
{{end}}
{{if $.EnableFeed}}
- <a role="button" class="btn interact-bg tw-p-2" href="{{$.FeedURL}}/rss/branch/{{PathEscapeSegments .DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">{{svg "octicon-rss"}}</a>
+ <a role="button" class="btn interact-bg tw-p-2" href="{{$.RepoLink}}/rss/branch/{{PathEscapeSegments .DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">{{svg "octicon-rss"}}</a>
{{end}}
{{if and (not .DBBranch.IsDeleted) (not $.DisableDownloadSourceArchives)}}
<div class="ui dropdown btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "repo.branch.download" (.DBBranch.Name)}}">
diff --git a/templates/repo/branch_dropdown.tmpl b/templates/repo/branch_dropdown.tmpl
index b68c34a02a..36dc047c23 100644
--- a/templates/repo/branch_dropdown.tmpl
+++ b/templates/repo/branch_dropdown.tmpl
@@ -1,8 +1,8 @@
{{/* Attributes:
* ContainerClasses
* Repository
-* CurrentRefType: eg. "branch", "tag"
-* CurrentRefShortName: eg. "master", "v1.0"
+* CurrentRefType: eg. "branch", "tag", "commit"
+* CurrentRefShortName: eg. "master", "v1.0", "abcdef0123"
* CurrentTreePath
* RefLinkTemplate: redirect to the link when a branch/tag is selected
* RefFormActionTemplate: change the parent form's action when a branch/tag is selected
@@ -14,7 +14,8 @@
Search "repo/branch_dropdown" in the template directory to find all occurrences.
*/}}
-<div class="js-branch-tag-selector {{if .ContainerClasses}}{{.ContainerClasses}}{{end}}"
+<div class="{{if .ContainerClasses}}{{.ContainerClasses}}{{end}}"
+ data-global-init="initRepoBranchTagSelector"
data-text-release-compare="{{ctx.Locale.Tr "repo.release.compare"}}"
data-text-branches="{{ctx.Locale.Tr "repo.branches"}}"
data-text-tags="{{ctx.Locale.Tr "repo.tags"}}"
@@ -45,17 +46,21 @@ Search "repo/branch_dropdown" in the template directory to find all occurrences.
data-enable-feed="{{ctx.RootData.EnableFeed}}"
>
{{/* show dummy elements before Vue componment is mounted, this code must match the code in BranchTagSelector.vue */}}
- <div class="ui dropdown custom branch-selector-dropdown ellipsis-items-nowrap">
- <div class="ui button branch-dropdown-button">
+ <div class="ui dropdown custom branch-selector-dropdown ellipsis-text-items">
+ <div class="ui compact button branch-dropdown-button">
<span class="flex-text-block gt-ellipsis">
- {{if not .DropdownFixedText}}
- {{if .ShowTabTags}}
+ {{if .DropdownFixedText}}
+ {{.DropdownFixedText}}
+ {{else}}
+ {{if eq .CurrentRefType "tag"}}
{{svg "octicon-tag"}}
- {{else if .ShowTabBranches}}
+ {{else if eq .CurrentRefType "branch"}}
{{svg "octicon-git-branch"}}
+ {{else}}
+ {{svg "octicon-git-commit"}}
{{end}}
+ <strong class="tw-inline-block gt-ellipsis">{{.CurrentRefShortName}}</strong>
{{end}}
- <strong class="tw-ml-2 tw-inline-block gt-ellipsis">{{Iif .DropdownFixedText .SelectedRefShortName}}</strong>
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</div>
diff --git a/templates/repo/clone_panel.tmpl b/templates/repo/clone_panel.tmpl
index d3496bdb73..e23bc8a19a 100644
--- a/templates/repo/clone_panel.tmpl
+++ b/templates/repo/clone_panel.tmpl
@@ -1,5 +1,6 @@
-<button class="ui primary button js-btn-clone-panel">
- <span>{{svg "octicon-code" 16}} Code</span>
+<button class="ui compact primary button js-btn-clone-panel">
+ {{svg "octicon-code" 16}}
+ <span>{{ctx.Locale.Tr "repo.code"}}</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</button>
<div class="clone-panel-popup tippy-target">
@@ -13,6 +14,7 @@
{{if $.CloneButtonShowSSH}}
<button class="item repo-clone-ssh" data-link="{{$.CloneButtonOriginLink.SSH}}">SSH</button>
{{end}}
+ <button class="item repo-clone-tea" data-link="{{$.CloneButtonOriginLink.Tea}}">Tea CLI</button>
</div>
<div class="divider"></div>
@@ -32,12 +34,14 @@
{{end}}
</div>
- {{if and (not $.DisableDownloadSourceArchives) $.RefName}}
+ {{if and (not $.DisableDownloadSourceArchives) $.RefFullName}}
<div class="divider"></div>
<div class="flex-items-block clone-panel-list">
- <a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.zip" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_zip"}}</a>
- <a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_tar"}}</a>
- <a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.bundle" rel="nofollow">{{svg "octicon-package"}} {{ctx.Locale.Tr "repo.download_bundle"}}</a>
+ {{/* FIXME: here it only uses the shortname in the ref to build the link, it can't distinguish the branch/tag/commit with the same name
+ in the future, it's better to use something like "/archive/branch/the-name.zip", "/archive/tag/the-name.zip" */}}
+ <a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefFullName.ShortName}}.zip" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_zip"}}</a>
+ <a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefFullName.ShortName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip"}} {{ctx.Locale.Tr "repo.download_tar"}}</a>
+ <a class="item muted archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefFullName.ShortName}}.bundle" rel="nofollow">{{svg "octicon-package"}} {{ctx.Locale.Tr "repo.download_bundle"}}</a>
</div>
{{end}}
{{end}}
diff --git a/templates/repo/code/recently_pushed_new_branches.tmpl b/templates/repo/code/recently_pushed_new_branches.tmpl
index f0edf6065b..4a864ba756 100644
--- a/templates/repo/code/recently_pushed_new_branches.tmpl
+++ b/templates/repo/code/recently_pushed_new_branches.tmpl
@@ -5,7 +5,7 @@
{{$branchLink := HTMLFormat `<a href="%s">%s</a>` .BranchLink .BranchDisplayName}}
{{ctx.Locale.Tr "repo.pulls.recently_pushed_new_branches" $branchLink $timeSince}}
</div>
- <a role="button" class="ui compact green button tw-m-0" href="{{.BranchCompareURL}}">
+ <a role="button" class="ui compact green button tw-m-0" href="{{QueryBuild .BranchCompareURL "expand" 1}}">
{{ctx.Locale.Tr "repo.pulls.compare_changes"}}
</a>
</div>
diff --git a/templates/repo/code/upstream_diverging_info.tmpl b/templates/repo/code/upstream_diverging_info.tmpl
index 51402598f9..b3d35c05e5 100644
--- a/templates/repo/code/upstream_diverging_info.tmpl
+++ b/templates/repo/code/upstream_diverging_info.tmpl
@@ -1,16 +1,21 @@
-{{if and .UpstreamDivergingInfo (or .UpstreamDivergingInfo.BaseIsNewer .UpstreamDivergingInfo.CommitsBehind)}}
+{{if and .UpstreamDivergingInfo .UpstreamDivergingInfo.BaseBranchHasNewCommits}}
<div class="ui message flex-text-block">
<div class="tw-flex-1">
- {{$upstreamLink := printf "%s/src/branch/%s" .Repository.BaseRepo.Link (.BranchName|PathEscapeSegments)}}
- {{$upstreamHtml := HTMLFormat `<a href="%s">%s:%s</a>` $upstreamLink .Repository.BaseRepo.FullName .BranchName}}
- {{if .UpstreamDivergingInfo.CommitsBehind}}
- {{ctx.Locale.TrN .UpstreamDivergingInfo.CommitsBehind "repo.pulls.upstream_diverging_prompt_behind_1" "repo.pulls.upstream_diverging_prompt_behind_n" .UpstreamDivergingInfo.CommitsBehind $upstreamHtml}}
+ {{$upstreamLink := printf "%s/src/branch/%s" .Repository.BaseRepo.Link (.UpstreamDivergingInfo.BaseBranchName|PathEscapeSegments)}}
+ {{$upstreamRepoBranchDisplay := HTMLFormat "%s:%s" .Repository.BaseRepo.FullName .UpstreamDivergingInfo.BaseBranchName}}
+ {{$thisRepoBranchDisplay := HTMLFormat "%s:%s" .Repository.FullName .BranchName}}
+ {{$upstreamHtml := HTMLFormat `<a href="%s">%s</a>` $upstreamLink $upstreamRepoBranchDisplay}}
+ {{if .UpstreamDivergingInfo.HeadBranchCommitsBehind}}
+ {{ctx.Locale.TrN .UpstreamDivergingInfo.HeadBranchCommitsBehind "repo.pulls.upstream_diverging_prompt_behind_1" "repo.pulls.upstream_diverging_prompt_behind_n" .UpstreamDivergingInfo.HeadBranchCommitsBehind $upstreamHtml}}
{{else}}
{{ctx.Locale.Tr "repo.pulls.upstream_diverging_prompt_base_newer" $upstreamHtml}}
{{end}}
</div>
{{if .CanWriteCode}}
- <button class="ui compact primary button tw-m-0 link-action" data-url="{{.Repository.Link}}/branches/merge-upstream?branch={{.BranchName}}">
+ <button class="ui compact primary button tw-m-0 link-action"
+ data-modal-confirm-header="{{ctx.Locale.Tr "repo.pulls.upstream_diverging_merge"}}"
+ data-modal-confirm-content="{{ctx.Locale.Tr "repo.pulls.upstream_diverging_merge_confirm" $upstreamRepoBranchDisplay $thisRepoBranchDisplay}}"
+ data-url="{{.Repository.Link}}/branches/merge-upstream?branch={{.BranchName}}">
{{ctx.Locale.Tr "repo.pulls.upstream_diverging_merge"}}
</button>
{{end}}
diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl
index 3d95e8a715..46f641824b 100644
--- a/templates/repo/commit_page.tmpl
+++ b/templates/repo/commit_page.tmpl
@@ -5,7 +5,7 @@
<div class="ui container fluid padded">
<div class="ui top attached header clearing segment tw-relative commit-header">
<div class="tw-flex tw-mb-4 tw-gap-1">
- <h3 class="tw-mb-0 tw-flex-1"><span class="commit-summary" title="{{.Commit.Summary}}">{{ctx.RenderUtils.RenderCommitMessage .Commit.Message ($.Repository.ComposeMetas ctx)}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}</h3>
+ <h3 class="tw-mb-0 tw-flex-1"><span class="commit-summary" title="{{.Commit.Summary}}">{{ctx.RenderUtils.RenderCommitMessage .Commit.Message $.Repository}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses "AdditionalClasses" "tw-inline"}}</h3>
{{if not $.PageIsWiki}}
<div class="commit-header-buttons">
<a class="ui primary tiny button" href="{{.SourcePath}}">
@@ -16,7 +16,7 @@
{{ctx.Locale.Tr "repo.commit.operations"}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
- <div class="ui header">{{ctx.Locale.Tr "repo.commit.operations"}}</div>
+ <div class="header">{{ctx.Locale.Tr "repo.commit.operations"}}</div>
<div class="divider"></div>
<div class="item show-create-branch-modal"
data-content="{{ctx.Locale.Tr "repo.branch.new_branch_from" (.CommitID)}}" {{/* used by the form */}}
@@ -54,13 +54,11 @@
<p id="cherry-pick-content" class="branch-dropdown"></p>
<form method="get">
- {{/*FIXME: CurrentRefShortName seems not making sense here (old code),
- because the "commit page" has no "$.BranchName" info, so only using DefaultBranch should be enough */}}
{{template "repo/branch_dropdown" dict
"Repository" .Repository
"ShowTabBranches" true
"CurrentRefType" "branch"
- "CurrentRefShortName" (or $.BranchName $.Repository.DefaultBranch)
+ "CurrentRefShortName" $.Repository.DefaultBranch
"RefFormActionTemplate" (print "{RepoLink}/_cherrypick/" .CommitID "/{RefShortName}")
}}
<input type="hidden" id="cherry-pick-type" name="cherry-pick-type"><br>
@@ -77,7 +75,7 @@
{{.CsrfTokenHtml}}
<div class="field">
<label>
- {{ctx.Locale.Tr "repo.branch.new_branch_from" (`<span class="text" id="modal-create-branch-from-span"></span>`|SafeHTML)}}
+ {{ctx.Locale.Tr "repo.branch.new_branch_from" (HTMLFormat `<span class="%s" id="%s"></span>` "text" "modal-create-branch-from-span")}}
</label>
</div>
<div class="required field">
@@ -85,7 +83,7 @@
<input id="new_branch_name" name="new_branch_name" required>
</div>
- <div class="text right actions">
+ <div class="actions">
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
<button class="ui primary button">{{ctx.Locale.Tr "repo.branch.confirm_create_branch"}}</button>
</div>
@@ -102,7 +100,7 @@
<input type="hidden" name="create_tag" value="true">
<div class="field">
<label>
- {{ctx.Locale.Tr "repo.tag.create_tag_from" (`<span class="text" id="modal-create-tag-from-span"></span>`|SafeHTML)}}
+ {{ctx.Locale.Tr "repo.tag.create_tag_from" (HTMLFormat `<span class="%s" id="%s"></span>` "text" "modal-create-tag-from-span")}}
</label>
</div>
<div class="required field">
@@ -110,7 +108,7 @@
<input id="new_branch_name" name="new_branch_name" required>
</div>
- <div class="text right actions">
+ <div class="actions">
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
<button class="ui primary button">{{ctx.Locale.Tr "repo.tag.confirm_create_tag"}}</button>
</div>
@@ -124,7 +122,7 @@
{{end}}
</div>
{{if IsMultilineCommitMessage .Commit.Message}}
- <pre class="commit-body">{{ctx.RenderUtils.RenderCommitBody .Commit.Message ($.Repository.ComposeMetas ctx)}}</pre>
+ <pre class="commit-body">{{ctx.RenderUtils.RenderCommitBody .Commit.Message $.Repository}}</pre>
{{end}}
{{template "repo/commit_load_branches_and_tags" .}}
</div>
diff --git a/templates/repo/commit_sign_badge.tmpl b/templates/repo/commit_sign_badge.tmpl
index aa68e9dd23..02089d7a4c 100644
--- a/templates/repo/commit_sign_badge.tmpl
+++ b/templates/repo/commit_sign_badge.tmpl
@@ -38,6 +38,8 @@ so this template should be kept as small as possbile, DO NOT put large component
{{- else -}}
{{- if $verification.Warning -}}
{{- $extraClass = print $extraClass " sign-warning" -}}
+ {{- else -}}
+ {{- $extraClass = "" -}}{{/* the commit is not signed */}}
{{- end -}}
{{- $msgReason = ctx.Locale.Tr $verification.Reason -}}{{- /* dirty part: it is the translation key ..... */ -}}
{{- end -}}
@@ -57,6 +59,7 @@ so this template should be kept as small as possbile, DO NOT put large component
<a {{if $commitBaseLink}}href="{{$commitBaseLink}}/{{$commit.ID}}"{{end}} class="ui label commit-id-short {{$extraClass}}" rel="nofollow">
{{- ShortSha $commit.ID.String -}}
{{- end -}}
+{{- if or (not $commit) $extraClass}}{{/* only show the lock icon if there is no commit info (icon only) or the commit is really signed */}}
<span class="ui label commit-sign-badge {{$extraClass}}">
{{- if $verified -}}
{{- if and $signingUser $signingUser.ID -}}
@@ -70,7 +73,7 @@ so this template should be kept as small as possbile, DO NOT put large component
<span data-tooltip-content="{{$msgReason}}">{{svg "gitea-unlock"}}</span>
{{- end -}}
</span>
-
+{{- end -}}
{{- if $commit -}}
</a>
{{- end -}}
diff --git a/templates/repo/commit_status.tmpl b/templates/repo/commit_status.tmpl
index eb700ab2bb..7184f5f8eb 100644
--- a/templates/repo/commit_status.tmpl
+++ b/templates/repo/commit_status.tmpl
@@ -14,3 +14,6 @@
{{if eq .State "warning"}}
{{svg "gitea-exclamation" 18 "commit-status icon text yellow"}}
{{end}}
+{{if eq .State "skipped"}}
+ {{svg "octicon-skip" 18 "commit-status icon text grey"}}
+{{end}}
diff --git a/templates/repo/commit_statuses.tmpl b/templates/repo/commit_statuses.tmpl
index a6f75584a3..1bbfb33105 100644
--- a/templates/repo/commit_statuses.tmpl
+++ b/templates/repo/commit_statuses.tmpl
@@ -1,10 +1,10 @@
{{if .Statuses}}
{{if and (eq (len .Statuses) 1) .Status.TargetURL}}
- <a class="flex-text-inline tw-no-underline {{.AdditionalClasses}}" data-tippy="commit-statuses" href="{{.Status.TargetURL}}">
+ <a class="flex-text-inline tw-no-underline {{.AdditionalClasses}}" data-global-init="initCommitStatuses" href="{{.Status.TargetURL}}">
{{template "repo/commit_status" .Status}}
</a>
{{else}}
- <span class="flex-text-inline {{.AdditionalClasses}}" data-tippy="commit-statuses" tabindex="0">
+ <span class="flex-text-inline {{.AdditionalClasses}}" data-global-init="initCommitStatuses" tabindex="0">
{{template "repo/commit_status" .Status}}
</span>
{{end}}
diff --git a/templates/repo/commits.tmpl b/templates/repo/commits.tmpl
index 7065bf33f4..07c6b5f618 100644
--- a/templates/repo/commits.tmpl
+++ b/templates/repo/commits.tmpl
@@ -5,24 +5,17 @@
{{template "repo/sub_menu" .}}
<div class="repo-button-row">
<div class="repo-button-row-left">
-
- {{$branchDropdownCurrentRefType := "branch"}}
- {{$branchDropdownCurrentRefShortName := .BranchName}}
- {{if .IsViewTag}}
- {{$branchDropdownCurrentRefType = "tag"}}
- {{$branchDropdownCurrentRefShortName = .TagName}}
- {{end}}
- {{template "repo/branch_dropdown" dict
+ {{- /* for /owner/repo/commits/{RefType}/{RefShortName} */ -}}
+ {{- template "repo/branch_dropdown" dict
"Repository" .Repository
"ShowTabBranches" true
"ShowTabTags" true
- "CurrentRefType" $branchDropdownCurrentRefType
- "CurrentRefShortName" $branchDropdownCurrentRefShortName
+ "CurrentRefType" .RefFullName.RefType
+ "CurrentRefShortName" .RefFullName.ShortName
"CurrentTreePath" .TreePath
"RefLinkTemplate" "{RepoLink}/commits/{RefType}/{RefShortName}/{TreePath}"
"AllowCreateNewRef" .CanCreateBranch
- }}
-
+ -}}
<a href="{{.RepoLink}}/graph" class="ui basic small compact button">
{{svg "octicon-git-branch"}}
{{ctx.Locale.Tr "repo.commit_graph"}}
diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl
index 329dc45149..959f2a9398 100644
--- a/templates/repo/commits_list.tmpl
+++ b/templates/repo/commits_list.tmpl
@@ -5,7 +5,7 @@
<th class="three wide">{{ctx.Locale.Tr "repo.commits.author"}}</th>
<th class="two wide sha">{{StringUtils.ToUpper $.Repository.ObjectFormatName}}</th>
<th class="eight wide message">{{ctx.Locale.Tr "repo.commits.message"}}</th>
- <th class="two wide right aligned">{{ctx.Locale.Tr "repo.commits.date"}}</th>
+ <th class="two wide tw-text-right">{{ctx.Locale.Tr "repo.commits.date"}}</th>
<th class="one wide"></th>
</tr>
</thead>
@@ -44,33 +44,42 @@
<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{.Summary | ctx.RenderUtils.RenderEmoji}}</span>
{{else}}
{{$commitLink:= printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String)}}
- <span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{ctx.RenderUtils.RenderCommitMessageLinkSubject .Message $commitLink ($.Repository.ComposeMetas ctx)}}</span>
+ <span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{ctx.RenderUtils.RenderCommitMessageLinkSubject .Message $commitLink $.Repository}}</span>
{{end}}
</span>
{{if IsMultilineCommitMessage .Message}}
- <button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
+ <button class="ui button ellipsis-button" aria-expanded="false" data-global-click="onRepoEllipsisButtonClick">...</button>
{{end}}
{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
{{if IsMultilineCommitMessage .Message}}
- <pre class="commit-body tw-hidden">{{ctx.RenderUtils.RenderCommitBody .Message ($.Repository.ComposeMetas ctx)}}</pre>
+ <pre class="commit-body tw-hidden">{{ctx.RenderUtils.RenderCommitBody .Message $.Repository}}</pre>
{{end}}
{{if $.CommitsTagsMap}}
{{range (index $.CommitsTagsMap .ID.String)}}
- {{- template "repo/tag/name" dict "RepoLink" $.Repository.Link "TagName" .TagName "IsRelease" (not .IsTag) -}}
+ {{- template "repo/tag/name" dict "AdditionalClasses" "tw-py-0" "RepoLink" $.Repository.Link "TagName" .TagName "IsRelease" (not .IsTag) -}}
{{end}}
{{end}}
</td>
{{if .Committer}}
- <td class="text right aligned">{{DateUtils.TimeSince .Committer.When}}</td>
+ <td class="tw-text-right">{{DateUtils.TimeSince .Committer.When}}</td>
{{else}}
- <td class="text right aligned">{{DateUtils.TimeSince .Author.When}}</td>
+ <td class="tw-text-right">{{DateUtils.TimeSince .Author.When}}</td>
{{end}}
- <td class="text right aligned tw-py-0">
- <button class="btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "copy_hash"}}" data-clipboard-text="{{.ID}}">{{svg "octicon-copy"}}</button>
- {{if not $.PageIsWiki}}{{/* at the moment, wiki doesn't support "view at history point*/}}
+ <td class="tw-text-right tw-py-0">
+ <button class="btn interact-bg tw-p-2 copy-commit-id" data-tooltip-content="{{ctx.Locale.Tr "copy_hash"}}" data-clipboard-text="{{.ID}}">{{svg "octicon-copy"}}</button>
+ {{/* at the moment, wiki doesn't support these "view" links like "view at history point" */}}
+ {{if not $.PageIsWiki}}
+ {{/* view single file diff */}}
+ {{if $.FileTreePath}}
+ <a class="btn interact-bg tw-p-2 view-single-diff" data-tooltip-content="{{ctx.Locale.Tr "repo.commits.view_file_diff"}}"
+ href="{{$commitRepoLink}}/commit/{{.ID.String}}?files={{$.FileTreePath}}"
+ >{{svg "octicon-file-diff"}}</a>
+ {{end}}
+
+ {{/* view at history point */}}
{{$viewCommitLink := printf "%s/src/commit/%s" $commitRepoLink (PathEscape .ID.String)}}
- {{if $.FileName}}{{$viewCommitLink = printf "%s/%s" $viewCommitLink (PathEscapeSegments $.FileName)}}{{end}}
- <a class="btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "repo.commits.view_path"}}" href="{{$viewCommitLink}}">{{svg "octicon-file-code"}}</a>
+ {{if $.FileTreePath}}{{$viewCommitLink = printf "%s/%s" $viewCommitLink (PathEscapeSegments $.FileTreePath)}}{{end}}
+ <a class="btn interact-bg tw-p-2 view-commit-path" data-tooltip-content="{{ctx.Locale.Tr "repo.commits.view_path"}}" href="{{$viewCommitLink}}">{{svg "octicon-file-code"}}</a>
{{end}}
</td>
</tr>
diff --git a/templates/repo/commits_list_small.tmpl b/templates/repo/commits_list_small.tmpl
index 2acf7c58b8..ee94ad7e58 100644
--- a/templates/repo/commits_list_small.tmpl
+++ b/templates/repo/commits_list_small.tmpl
@@ -15,7 +15,7 @@
{{$commitLink:= printf "%s/%s" $commitBaseLink (PathEscape .ID.String)}}
<span class="tw-flex-1 tw-font-mono gt-ellipsis" title="{{.Summary}}">
- {{- ctx.RenderUtils.RenderCommitMessageLinkSubject .Message $commitLink ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx) -}}
+ {{- ctx.RenderUtils.RenderCommitMessageLinkSubject .Message $commitLink $.comment.Issue.PullRequest.BaseRepo -}}
</span>
{{if IsMultilineCommitMessage .Message}}
@@ -29,7 +29,7 @@
</div>
{{if IsMultilineCommitMessage .Message}}
<pre class="commit-body tw-ml-[33px] tw-hidden" data-singular-commit-body-for="{{$tag}}">
- {{- ctx.RenderUtils.RenderCommitBody .Message ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx) -}}
+ {{- ctx.RenderUtils.RenderCommitBody .Message $.comment.Issue.PullRequest.BaseRepo -}}
</pre>
{{end}}
{{end}}
diff --git a/templates/repo/commits_table.tmpl b/templates/repo/commits_table.tmpl
index 91fc1c2fae..a0c5eacdd4 100644
--- a/templates/repo/commits_table.tmpl
+++ b/templates/repo/commits_table.tmpl
@@ -19,7 +19,7 @@
{{if .PageIsCommits}}
<div class="ui attached segment">
- <form class="ignore-dirty" action="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/search">
+ <form class="ignore-dirty" action="{{.RepoLink}}/commits/{{.RefTypeNameSubURL}}/search">
<div class="ui small fluid action input">
{{template "shared/search/input" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.commit_kind")}}
{{template "repo/commits_search_dropdown" .}}
diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl
index 78eb2f704a..ada7e0c092 100644
--- a/templates/repo/create.tmpl
+++ b/templates/repo/create.tmpl
@@ -1,222 +1,217 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository new-repo">
- <div class="ui middle very relaxed page one column grid">
- <div class="column">
- <form class="ui form new-repo-form" action="{{.Link}}" method="post">
+ <div class="ui container medium-width">
+ <h3 class="ui top attached header">
+ {{ctx.Locale.Tr "new_repo"}}
+ </h3>
+ <div class="ui attached segment">
+ {{template "base/alert" .}}
+ {{template "repo/create_helper" .}}
+ <form class="ui form left-right-form new-repo-form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
- <h3 class="ui top attached header">
- {{ctx.Locale.Tr "new_repo"}}
- </h3>
- <div class="ui attached segment">
- {{template "base/alert" .}}
- {{template "repo/create_helper" .}}
-
- {{if not .CanCreateRepo}}
- <div class="ui negative message">
- <p>{{ctx.Locale.TrN .MaxCreationLimit "repo.form.reach_limit_of_creation_1" "repo.form.reach_limit_of_creation_n" .MaxCreationLimit}}</p>
- </div>
- {{end}}
- <div class="inline required field {{if .Err_Owner}}error{{end}}">
- <label>{{ctx.Locale.Tr "repo.owner"}}</label>
- <div class="ui selection owner dropdown">
- <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
- <span class="text truncated-item-container" title="{{.ContextUser.Name}}">
- {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
- <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
- </span>
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- <div class="menu">
- <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}">
- {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
- <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
- </div>
- {{range .Orgs}}
- <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
- {{ctx.AvatarUtils.Avatar . 28 "mini"}}
- <span class="truncated-item-name">{{.ShortName 40}}</span>
- </div>
+ <div id="create-repo-error-message" class="ui negative message tw-text-center tw-hidden"></div>
+ <div class="inline required field {{if .Err_Owner}}error{{end}}">
+ <label>{{ctx.Locale.Tr "repo.owner"}}</label>
+ <div class="ui selection dropdown ellipsis-text-items" id="repo_owner_dropdown">
+ <input type="hidden" name="uid" value="{{.ContextUser.ID}}">
+ <span class="text"></span>
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ <div class="menu">
+ <div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}"
+ {{if not .CanCreateRepoInDoer}}
+ data-create-repo-disallowed-prompt="{{ctx.Locale.TrN .MaxCreationLimit "repo.form.reach_limit_of_creation_1" "repo.form.reach_limit_of_creation_n" .MaxCreationLimitOfDoer}}"
{{end}}
- </div>
+ >
+ {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
+ {{.SignedUser.ShortName 40}}
+ </div>
+ {{range .Orgs}}
+ <div class="item" data-value="{{.ID}}" title="{{.Name}}">
+ {{ctx.AvatarUtils.Avatar . 28 "mini"}}
+ {{.ShortName 40}}
+ </div>
+ {{end}}
</div>
- <span class="help">{{ctx.Locale.Tr "repo.owner_helper"}}</span>
</div>
+ <span class="help">{{ctx.Locale.Tr "repo.owner_helper"}}</span>
+ </div>
+
+ <div class="inline required field {{if .Err_RepoName}}error{{end}}">
+ <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
+ <input id="repo_name" name="repo_name" value="{{.repo_name}}" autofocus required maxlength="100">
+ <span class="help" data-help-for-repo-name>{{ctx.Locale.Tr "repo.repo_name_helper"}}</span>
+ <span class="help tw-hidden" data-help-for-repo-name=".profile">{{ctx.Locale.Tr "repo.repo_name_profile_public_hint"}}</span>
+ <span class="help tw-hidden" data-help-for-repo-name=".profile-private">{{ctx.Locale.Tr "repo.repo_name_profile_private_hint"}}</span>
+ </div>
- <div class="inline required field {{if .Err_RepoName}}error{{end}}">
- <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
- <input id="repo_name" name="repo_name" value="{{.repo_name}}" autofocus required maxlength="100">
- <span class="help" data-help-for-repo-name>{{ctx.Locale.Tr "repo.repo_name_helper"}}</span>
- <span class="help tw-hidden" data-help-for-repo-name=".profile">{{ctx.Locale.Tr "repo.repo_name_profile_public_hint"}}</span>
- <span class="help tw-hidden" data-help-for-repo-name=".profile-private">{{ctx.Locale.Tr "repo.repo_name_profile_private_hint"}}</span>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
+ <div class="ui checkbox">
+ {{if .IsForcedPrivate}}
+ <input name="private" type="checkbox" checked disabled>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
+ {{else}}
+ <input name="private" type="checkbox" {{if .private}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ {{end}}
</div>
+ <span class="help">{{ctx.Locale.Tr "repo.visibility_description"}}</span>
+ </div>
+ <div class="inline field {{if .Err_Description}}error{{end}}">
+ <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
+ <textarea id="description" rows="2" name="description" placeholder="{{ctx.Locale.Tr "repo.repo_desc_helper"}}" maxlength="2048">{{.description}}</textarea>
+ </div>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.template"}}</label>
+ <div id="repo_template_search" class="ui search selection dropdown">
+ <input type="hidden" id="repo_template" name="repo_template" value="{{or .repo_template ""}}">
+ <div class="default text">{{.repo_template_name}}</div>
+ <div class="menu">
+ </div>
+ </div>
+ </div>
+ <div id="template_units" class="tw-hidden">
<div class="inline field">
- <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
+ <label>{{ctx.Locale.Tr "repo.template.items"}}</label>
<div class="ui checkbox">
- {{if .IsForcedPrivate}}
- <input name="private" type="checkbox" checked disabled>
- <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
- {{else}}
- <input name="private" type="checkbox" {{if .private}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
- {{end}}
+ <input name="git_content" type="checkbox" {{if .git_content}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.template.git_content"}}</label>
+ </div>
+ <div class="ui checkbox" {{if not .SignedUser.CanEditGitHook}}data-tooltip-content="{{ctx.Locale.Tr "repo.template.git_hooks_tooltip"}}"{{end}}>
+ <input name="git_hooks" type="checkbox" {{if .git_hooks}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.template.git_hooks"}}</label>
</div>
- <span class="help">{{ctx.Locale.Tr "repo.visibility_description"}}</span>
- </div>
- <div class="inline field {{if .Err_Description}}error{{end}}">
- <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
- <textarea id="description" rows="2" name="description" placeholder="{{ctx.Locale.Tr "repo.repo_desc_helper"}}" maxlength="2048">{{.description}}</textarea>
</div>
<div class="inline field">
- <label>{{ctx.Locale.Tr "repo.template"}}</label>
- <div id="repo_template_search" class="ui search selection dropdown">
- <input type="hidden" id="repo_template" name="repo_template" value="{{.repo_template}}">
- <div class="default text">{{.repo_template_name}}</div>
- <div class="menu">
- </div>
+ <label></label>
+ <div class="ui checkbox">
+ <input name="webhooks" type="checkbox" {{if .webhooks}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.template.webhooks"}}</label>
</div>
- </div>
-
- <div id="template_units" class="tw-hidden">
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.template.items"}}</label>
- <div class="ui checkbox">
- <input name="git_content" type="checkbox" {{if .git_content}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.template.git_content"}}</label>
- </div>
- <div class="ui checkbox" {{if not .SignedUser.CanEditGitHook}}data-tooltip-content="{{ctx.Locale.Tr "repo.template.git_hooks_tooltip"}}"{{end}}>
- <input name="git_hooks" type="checkbox" {{if .git_hooks}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.template.git_hooks"}}</label>
- </div>
+ <div class="ui checkbox">
+ <input name="topics" type="checkbox" {{if .topics}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.template.topics"}}</label>
</div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="webhooks" type="checkbox" {{if .webhooks}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.template.webhooks"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="topics" type="checkbox" {{if .topics}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.template.topics"}}</label>
- </div>
+ </div>
+ <div class="inline field">
+ <label></label>
+ <div class="ui checkbox">
+ <input name="avatar" type="checkbox" {{if .avatar}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.template.avatar"}}</label>
</div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="avatar" type="checkbox" {{if .avatar}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.template.avatar"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.template.issue_labels"}}</label>
- </div>
+ <div class="ui checkbox">
+ <input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.template.issue_labels"}}</label>
</div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="protected_branch" type="checkbox" {{if .protected_branch}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.settings.protected_branch"}}</label>
- </div>
+ </div>
+ <div class="inline field">
+ <label></label>
+ <div class="ui checkbox">
+ <input name="protected_branch" type="checkbox" {{if .protected_branch}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.settings.protected_branch"}}</label>
</div>
</div>
+ </div>
- <div id="non_template">
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.issue_labels"}}</label>
- <div class="ui search selection dropdown">
- <input type="hidden" name="issue_labels" value="{{.issueLabels}}">
- <div class="default text">{{ctx.Locale.Tr "repo.issue_labels_helper"}}</div>
- <div class="menu">
- <div class="item" data-value="">{{ctx.Locale.Tr "repo.issue_labels_helper"}}</div>
- {{range .LabelTemplateFiles}}
- <div class="item" data-value="{{.DisplayName}}">{{.DisplayName}}<br><i>({{.Description}})</i></div>
- {{end}}
- </div>
+ <div id="non_template">
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.issue_labels"}}</label>
+ <div class="ui search selection dropdown">
+ <input type="hidden" name="issue_labels" value="{{.issueLabels}}">
+ <div class="default text">{{ctx.Locale.Tr "repo.issue_labels_helper"}}</div>
+ <div class="menu">
+ <div class="item" data-value="">{{ctx.Locale.Tr "repo.issue_labels_helper"}}</div>
+ {{range .LabelTemplateFiles}}
+ <div class="item" data-value="{{.DisplayName}}">{{.DisplayName}}<br><i>({{.Description}})</i></div>
+ {{end}}
</div>
</div>
+ </div>
- <div class="divider"></div>
+ <div class="divider"></div>
- <div class="inline field">
- <label>.gitignore</label>
- <div class="ui multiple search selection dropdown">
- <input type="hidden" name="gitignores" value="{{.gitignores}}">
- <div class="default text">{{ctx.Locale.Tr "repo.repo_gitignore_helper"}}</div>
- <div class="menu">
- {{range .Gitignores}}
- <div class="item" data-value="{{.}}">{{.}}</div>
- {{end}}
- </div>
- </div>
- <span class="help">{{ctx.Locale.Tr "repo.repo_gitignore_helper_desc"}}</span>
- </div>
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.license"}}</label>
- <div class="ui search selection dropdown">
- <input type="hidden" name="license" value="{{.license}}">
- <div class="default text">{{ctx.Locale.Tr "repo.license_helper"}}</div>
- <div class="menu">
- <div class="item" data-value="">{{ctx.Locale.Tr "repo.license_helper"}}</div>
- {{range .Licenses}}
- <div class="item" data-value="{{.}}">{{.}}</div>
- {{end}}
- </div>
+ <div class="inline field">
+ <label>.gitignore</label>
+ <div class="ui multiple search selection dropdown">
+ <input type="hidden" name="gitignores" value="{{.gitignores}}">
+ <div class="default text">{{ctx.Locale.Tr "repo.repo_gitignore_helper"}}</div>
+ <div class="menu">
+ {{range .Gitignores}}
+ <div class="item" data-value="{{.}}">{{.}}</div>
+ {{end}}
</div>
- <span class="help">{{ctx.Locale.Tr "repo.license_helper_desc" "https://choosealicense.com/"}}</span>
</div>
-
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.readme"}}</label>
- <div class="ui selection dropdown">
- <input type="hidden" name="readme" value="{{.readme}}">
- <div class="default text">{{ctx.Locale.Tr "repo.readme_helper"}}</div>
- <div class="menu">
- {{range .Readmes}}
- <div class="item" data-value="{{.}}">{{.}}</div>
- {{end}}
- </div>
+ <span class="help">{{ctx.Locale.Tr "repo.repo_gitignore_helper_desc"}}</span>
+ </div>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.license"}}</label>
+ <div class="ui search selection dropdown">
+ <input type="hidden" name="license" value="{{.license}}">
+ <div class="default text">{{ctx.Locale.Tr "repo.license_helper"}}</div>
+ <div class="menu">
+ <div class="item" data-value="">{{ctx.Locale.Tr "repo.license_helper"}}</div>
+ {{range .Licenses}}
+ <div class="item" data-value="{{.}}">{{.}}</div>
+ {{end}}
</div>
- <span class="help">{{ctx.Locale.Tr "repo.readme_helper_desc"}}</span>
</div>
- <div class="inline field">
- <div class="ui checkbox" id="auto-init">
- <input name="auto_init" type="checkbox" {{if .auto_init}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.auto_init"}}</label>
+ <span class="help">{{ctx.Locale.Tr "repo.license_helper_desc" "https://choosealicense.com/"}}</span>
+ </div>
+
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.readme"}}</label>
+ <div class="ui selection dropdown">
+ <input type="hidden" name="readme" value="{{.readme}}">
+ <div class="default text">{{ctx.Locale.Tr "repo.readme_helper"}}</div>
+ <div class="menu">
+ {{range .Readmes}}
+ <div class="item" data-value="{{.}}">{{.}}</div>
+ {{end}}
</div>
</div>
- <div class="inline field">
- <label for="default_branch">{{ctx.Locale.Tr "repo.default_branch"}}</label>
- <input id="default_branch" name="default_branch" value="{{.default_branch}}" placeholder="{{.default_branch}}">
- <span class="help">{{ctx.Locale.Tr "repo.default_branch_helper"}}</span>
- </div>
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.object_format"}}</label>
- <div class="ui selection owner dropdown">
- <input type="hidden" id="object_format_name" name="object_format_name" value="{{.DefaultObjectFormat.Name}}" required>
- <div class="default text">{{.DefaultObjectFormat.Name}}</div>
- <div class="menu">
- {{range .SupportedObjectFormats}}
- <div class="item" data-value="{{.Name}}">{{.Name}}</div>
- {{end}}
- </div>
- </div>
- <span class="help">{{ctx.Locale.Tr "repo.object_format_helper"}}</span>
+ <span class="help">{{ctx.Locale.Tr "repo.readme_helper_desc"}}</span>
+ </div>
+ <div class="inline field">
+ <label></label>
+ <div class="ui checkbox" id="auto-init">
+ <input name="auto_init" type="checkbox" {{if .auto_init}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.auto_init"}}</label>
</div>
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.template"}}</label>
- <div class="ui checkbox">
- <input name="template" type="checkbox">
- <label>{{ctx.Locale.Tr "repo.template_helper"}}</label>
+ </div>
+ <div class="inline field">
+ <label for="default_branch">{{ctx.Locale.Tr "repo.default_branch"}}</label>
+ <input id="default_branch" name="default_branch" value="{{.default_branch}}" placeholder="{{.default_branch}}">
+ <span class="help">{{ctx.Locale.Tr "repo.default_branch_helper"}}</span>
+ </div>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.object_format"}}</label>
+ <div class="ui selection owner dropdown">
+ <input type="hidden" id="object_format_name" name="object_format_name" value="{{or .object_format_name .DefaultObjectFormat.Name}}" required>
+ <div class="default text">{{.DefaultObjectFormat.Name}}</div>
+ <div class="menu">
+ {{range .SupportedObjectFormats}}
+ <div class="item" data-value="{{.Name}}">{{.Name}}</div>
+ {{end}}
</div>
</div>
+ <span class="help">{{ctx.Locale.Tr "repo.object_format_helper"}}</span>
</div>
- <br>
<div class="inline field">
- <label></label>
- <button class="ui primary button{{if not .CanCreateRepo}} disabled{{end}}">
- {{ctx.Locale.Tr "repo.create_repo"}}
- </button>
+ <label>{{ctx.Locale.Tr "repo.template"}}</label>
+ <div class="ui checkbox">
+ <input name="template" type="checkbox">
+ <label>{{ctx.Locale.Tr "repo.template_helper"}}</label>
+ </div>
</div>
</div>
+ <br>
+ <div class="inline field">
+ <label></label>
+ <button class="ui primary button">
+ {{ctx.Locale.Tr "repo.create_repo"}}
+ </button>
+ </div>
</form>
</div>
</div>
diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl
index 9733e5f980..22abf9a219 100644
--- a/templates/repo/diff/box.tmpl
+++ b/templates/repo/diff/box.tmpl
@@ -1,6 +1,6 @@
-{{$showFileTree := (and (not .DiffNotAvailable) (gt .Diff.NumFiles 1))}}
+{{$showFileTree := (and (not .DiffNotAvailable) (gt .DiffShortStat.NumFiles 1))}}
<div>
- <div class="diff-detail-box diff-box">
+ <div class="diff-detail-box">
<div class="tw-flex tw-items-center tw-flex-wrap tw-gap-2 tw-ml-0.5">
{{if $showFileTree}}
<button class="diff-toggle-file-tree-button not-mobile btn interact-fg" data-show-text="{{ctx.Locale.Tr "repo.diff.show_file_tree"}}" data-hide-text="{{ctx.Locale.Tr "repo.diff.hide_file_tree"}}">
@@ -19,17 +19,17 @@
{{end}}
{{if not .DiffNotAvailable}}
<div class="diff-detail-stats tw-flex tw-items-center tw-flex-wrap">
- {{svg "octicon-diff" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.diff.stats_desc" .Diff.NumFiles .Diff.TotalAddition .Diff.TotalDeletion}}
+ {{svg "octicon-diff" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.diff.stats_desc" .DiffShortStat.NumFiles .DiffShortStat.TotalAddition .DiffShortStat.TotalDeletion}}
</div>
{{end}}
</div>
<div class="diff-detail-actions">
- {{if and .PageIsPullFiles $.SignedUserID (not .IsArchived) (not .DiffNotAvailable)}}
+ {{if and .PageIsPullFiles $.SignedUserID (not .DiffNotAvailable)}}
<div class="not-mobile tw-flex tw-items-center tw-flex-col tw-whitespace-nowrap tw-mr-1">
<label for="viewed-files-summary" id="viewed-files-summary-label" data-text-changed-template="{{ctx.Locale.Tr "repo.pulls.viewed_files_label"}}">
- {{ctx.Locale.Tr "repo.pulls.viewed_files_label" .Diff.NumViewedFiles .Diff.NumFiles}}
+ {{ctx.Locale.Tr "repo.pulls.viewed_files_label" .Diff.NumViewedFiles .DiffShortStat.NumFiles}}
</label>
- <progress id="viewed-files-summary" value="{{.Diff.NumViewedFiles}}" max="{{.Diff.NumFiles}}"></progress>
+ <progress id="viewed-files-summary" value="{{.Diff.NumViewedFiles}}" max="{{.DiffShortStat.NumFiles}}"></progress>
</div>
{{end}}
{{template "repo/diff/whitespace_dropdown" .}}
@@ -37,12 +37,12 @@
{{if .PageIsPullFiles}}
<div id="diff-commit-select" data-issuelink="{{$.Issue.Link}}" data-queryparams="?style={{if $.IsSplitStyle}}split{{else}}unified{{end}}&whitespace={{$.WhitespaceBehavior}}&show-outdated={{$.ShowOutdatedComments}}" data-filter_changes_by_commit="{{ctx.Locale.Tr "repo.pulls.filter_changes_by_commit"}}">
{{/* the following will be replaced by vue component, but this avoids any loading artifacts till the vue component is initialized */}}
- <div class="ui jump dropdown basic button custom">
+ <div class="ui jump dropdown tiny basic button custom">
{{svg "octicon-git-commit"}}
</div>
</div>
{{end}}
- {{if and .PageIsPullFiles $.SignedUserID (not .IsArchived)}}
+ {{if and .PageIsPullFiles $.SignedUserID}}
{{template "repo/diff/new_review" .}}
{{end}}
</div>
@@ -57,35 +57,10 @@
<div>{{ctx.Locale.Tr "repo.pulls.showing_specified_commit_range" (ShortSha .BeforeCommitID) (ShortSha .AfterCommitID)}} - <a href="{{$.Issue.Link}}/files?style={{if $.IsSplitStyle}}split{{else}}unified{{end}}&whitespace={{$.WhitespaceBehavior}}&show-outdated={{$.ShowOutdatedComments}}">{{ctx.Locale.Tr "repo.pulls.show_all_commits"}}</a></div>
</div>
{{end}}
- <script id="diff-data-script" type="module">
- const diffDataFiles = [{{range $i, $file := .Diff.Files}}{Name:"{{$file.Name}}",NameHash:"{{$file.NameHash}}",Type:{{$file.Type}},IsBin:{{$file.IsBin}},Addition:{{$file.Addition}},Deletion:{{$file.Deletion}},IsViewed:{{$file.IsViewed}}},{{end}}];
- const diffData = {
- isIncomplete: {{.Diff.IsIncomplete}},
- tooManyFilesMessage: "{{ctx.Locale.Tr "repo.diff.too_many_files"}}",
- binaryFileMessage: "{{ctx.Locale.Tr "repo.diff.bin"}}",
- showMoreMessage: "{{ctx.Locale.Tr "repo.diff.show_more"}}",
- statisticsMessage: "{{ctx.Locale.Tr "repo.diff.stats_desc_file"}}",
- linkLoadMore: "?skip-to={{.Diff.End}}&file-only=true",
- };
-
- // for first time loading, the diffFileInfo is a plain object
- // after the Vue component is mounted, the diffFileInfo is a reactive object
- // keep in mind that this script block would be executed many times when loading more files, by "loadMoreFiles"
- let diffFileInfo = window.config.pageData.diffFileInfo || {
- files:[],
- fileTreeIsVisible: false,
- fileListIsVisible: false,
- isLoadingNewData: false,
- selectedItem: '',
- };
- diffFileInfo = Object.assign(diffFileInfo, diffData);
- diffFileInfo.files.push(...diffDataFiles);
- window.config.pageData.diffFileInfo = diffFileInfo;
- </script>
- <div id="diff-file-list"></div>
{{end}}
<div id="diff-container">
{{if $showFileTree}}
+ {{$.FileIconPoolHTML}}
<div id="diff-file-tree" class="tw-hidden not-mobile"></div>
<script>
if (diffTreeVisible) document.getElementById('diff-file-tree').classList.remove('tw-hidden');
@@ -105,8 +80,8 @@
{{$isCsv := (call $.IsCsvFile $file)}}
{{$showFileViewToggle := or $isImage (and (not $file.IsIncomplete) $isCsv)}}
{{$isExpandable := or (gt $file.Addition 0) (gt $file.Deletion 0) $file.IsBin}}
- {{$isReviewFile := and $.IsSigned $.PageIsPullFiles (not $.IsArchived) $.IsShowingAllCommits}}
- <div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}} tw-mt-0" id="diff-{{$file.NameHash}}" data-old-filename="{{$file.OldName}}" data-new-filename="{{$file.Name}}" {{if or ($file.ShouldBeHidden) (not $isExpandable)}}data-folded="true"{{end}}>
+ {{$isReviewFile := and $.IsSigned $.PageIsPullFiles (not $.Repository.IsArchived) $.IsShowingAllCommits}}
+ <div class="diff-file-box file-content {{TabSizeClass $.Editorconfig $file.Name}} tw-mt-0" id="diff-{{$file.NameHash}}" data-old-filename="{{$file.OldName}}" data-new-filename="{{$file.Name}}" {{if or ($file.ShouldBeHidden) (not $isExpandable)}}data-folded="true"{{end}}>
<h4 class="diff-file-header sticky-2nd-row ui top attached header">
<div class="diff-file-name tw-flex tw-flex-1 tw-items-center tw-gap-1 tw-flex-wrap">
<button class="fold-file btn interact-bg tw-p-1{{if not $isExpandable}} tw-invisible{{end}}">
@@ -126,8 +101,8 @@
{{end}}
</div>
<span class="file tw-flex tw-items-center tw-font-mono tw-flex-1"><a class="muted file-link" title="{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}" href="#diff-{{$file.NameHash}}">{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}</a>
- {{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}}
<button class="btn interact-fg tw-p-2" data-clipboard-text="{{$file.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_path"}}">{{svg "octicon-copy" 14}}</button>
+ {{if .IsLFSFile}}<span class="ui label">LFS</span>{{end}}
{{if $file.IsGenerated}}
<span class="ui label">{{ctx.Locale.Tr "repo.diff.generated"}}</span>
{{end}}
@@ -161,23 +136,25 @@
<input type="checkbox" name="{{$file.GetDiffFileName}}" autocomplete="off"{{if $file.IsViewed}} checked{{end}}> {{ctx.Locale.Tr "repo.pulls.has_viewed_file"}}
</label>
{{end}}
- <button class="btn diff-header-popup-btn tw-p-1">{{svg "octicon-kebab-horizontal" 18}}</button>
- <div class="tippy-target">
- {{if not (or $file.IsIncomplete $file.IsBin $file.IsSubmodule)}}
- <button class="unescape-button item" data-unicode-content-selector="#diff-{{$file.NameHash}}">{{ctx.Locale.Tr "repo.unescape_control_characters"}}</button>
- <button class="escape-button tw-hidden item" data-unicode-content-selector="#diff-{{$file.NameHash}}">{{ctx.Locale.Tr "repo.escape_control_characters"}}</button>
- {{end}}
- {{if and (not $file.IsSubmodule) (not $.PageIsWiki)}}
- {{if $file.IsDeleted}}
- <a class="item" rel="nofollow" href="{{$.BeforeSourcePath}}/{{PathEscapeSegments .Name}}">{{ctx.Locale.Tr "repo.diff.view_file"}}</a>
- {{else}}
- <a class="item" rel="nofollow" href="{{$.SourcePath}}/{{PathEscapeSegments .Name}}">{{ctx.Locale.Tr "repo.diff.view_file"}}</a>
- {{if and $.Repository.CanEnableEditor $.CanEditFile (not $file.IsLFSFile) (not $file.IsBin)}}
- <a class="item" rel="nofollow" href="{{$.HeadRepoLink}}/_edit/{{PathEscapeSegments $.HeadBranchName}}/{{PathEscapeSegments $file.Name}}?return_uri={{print $.BackToLink "#diff-" $file.NameHash | QueryEscape}}">{{ctx.Locale.Tr "repo.editor.edit_this_file"}}</a>
+ {{if not $file.IsSubmodule}}
+ <button class="btn diff-header-popup-btn tw-p-1">{{svg "octicon-kebab-horizontal" 18}}</button>
+ <div class="tippy-target">
+ {{if not (or $file.IsIncomplete $file.IsBin)}}
+ <button class="unescape-button item" data-unicode-content-selector="#diff-{{$file.NameHash}}">{{ctx.Locale.Tr "repo.unescape_control_characters"}}</button>
+ <button class="escape-button tw-hidden item" data-unicode-content-selector="#diff-{{$file.NameHash}}">{{ctx.Locale.Tr "repo.escape_control_characters"}}</button>
+ {{end}}
+ {{if not $.PageIsWiki}}
+ {{if $file.IsDeleted}}
+ <a class="item" rel="nofollow" href="{{$.BeforeSourcePath}}/{{PathEscapeSegments .Name}}">{{ctx.Locale.Tr "repo.diff.view_file"}}</a>
+ {{else}}
+ <a class="item" rel="nofollow" href="{{$.SourcePath}}/{{PathEscapeSegments .Name}}">{{ctx.Locale.Tr "repo.diff.view_file"}}</a>
+ {{if and $.Repository.CanEnableEditor $.CanEditFile}}
+ <a class="item" rel="nofollow" href="{{$.HeadRepoLink}}/_edit/{{PathEscapeSegments $.HeadBranchName}}/{{PathEscapeSegments $file.Name}}?return_uri={{print $.BackToLink "#diff-" $file.NameHash | QueryEscape}}">{{ctx.Locale.Tr "repo.editor.edit_this_file"}}</a>
+ {{end}}
{{end}}
{{end}}
- {{end}}
- </div>
+ </div>
+ {{end}}
</div>
</h4>
<div class="diff-file-body ui attached unstackable table segment" {{if and $file.IsViewed $.IsShowingAllCommits}}data-folded="true"{{end}}>
@@ -195,6 +172,17 @@
{{ctx.Locale.Tr "repo.diff.bin_not_shown"}}
{{end}}
</div>
+ {{else if $file.SubmoduleDiffInfo}}
+ <div class="tw-p-3">{{svg "octicon-file-submodule"}} {{$submoduleDiffInfo := $file.SubmoduleDiffInfo -}}
+ {{- $submoduleName := $submoduleDiffInfo.SubmoduleRepoLinkHTML ctx -}}
+ {{- if $file.IsDeleted -}}
+ {{- ctx.Locale.Tr "repo.diff.submodule_deleted" $submoduleName ($submoduleDiffInfo.CommitRefIDLinkHTML ctx $submoduleDiffInfo.PreviousRefID) -}}
+ {{- else if $file.IsCreated -}}
+ {{- ctx.Locale.Tr "repo.diff.submodule_added" $submoduleName ($submoduleDiffInfo.CommitRefIDLinkHTML ctx $submoduleDiffInfo.NewRefID) -}}
+ {{- else -}}
+ {{- ctx.Locale.Tr "repo.diff.submodule_updated" $submoduleName ($submoduleDiffInfo.CompareRefIDLinkHTML ctx) -}}
+ {{end}}
+ </div>
{{else}}
<table class="chroma" data-new-comment-url="{{$.Issue.Link}}/files/reviews/new_comment" data-path="{{$file.Name}}">
{{if $.IsSplitStyle}}
@@ -222,7 +210,7 @@
{{end}}
{{if .Diff.IsIncomplete}}
- <div class="diff-file-box diff-box file-content tw-mt-2" id="diff-incomplete">
+ <div class="diff-file-box file-content tw-mt-2" id="diff-incomplete">
<h4 class="ui top attached header tw-font-normal tw-flex tw-items-center tw-justify-between">
{{ctx.Locale.Tr "repo.diff.too_many_files"}}
<a class="ui basic tiny button" id="diff-show-more-files" data-href="?skip-to={{.Diff.End}}&file-only=true">{{ctx.Locale.Tr "repo.diff.show_more"}}</a>
@@ -236,6 +224,7 @@
{{if and (not $.Repository.IsArchived) (not .DiffNotAvailable)}}
<template id="issue-comment-editor-template">
<form class="ui form comment">
+ <div class="field">
{{template "shared/combomarkdowneditor" (dict
"CustomInit" true
"MarkdownPreviewInRepo" $.Repository
@@ -243,12 +232,13 @@
"TextareaName" "content"
"DropzoneParentContainer" ".ui.form"
)}}
+ </div>
{{if .IsAttachmentEnabled}}
<div class="field">
{{template "repo/upload" .}}
</div>
{{end}}
- <div class="text right edit buttons">
+ <div class="field flex-text-block tw-justify-end">
<button class="ui cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
<button class="ui primary button">{{ctx.Locale.Tr "repo.issues.save"}}</button>
</div>
diff --git a/templates/repo/diff/comment_form.tmpl b/templates/repo/diff/comment_form.tmpl
index 964dc2adc7..58b675467c 100644
--- a/templates/repo/diff/comment_form.tmpl
+++ b/templates/repo/diff/comment_form.tmpl
@@ -27,7 +27,7 @@
{{end}}
<div class="field footer">
- <div class="tw-text-right">
+ <div class="flex-text-block tw-justify-end">
{{if $.reply}}
<button class="ui submit primary tiny button btn-reply" type="submit">{{ctx.Locale.Tr "repo.diff.comment.reply"}}</button>
<input type="hidden" name="reply" value="{{$.reply}}">
diff --git a/templates/repo/diff/comments.tmpl b/templates/repo/diff/comments.tmpl
index 2d716688b9..2e8261e479 100644
--- a/templates/repo/diff/comments.tmpl
+++ b/templates/repo/diff/comments.tmpl
@@ -8,7 +8,7 @@
{{template "shared/user/avatarlink" dict "user" .Poster}}
{{end}}
<div class="content comment-container">
- <div class="ui top attached header comment-header tw-flex tw-items-center tw-justify-between">
+ <div class="comment-header">
<div class="comment-header-left tw-flex tw-items-center">
{{if .OriginalAuthor}}
<span class="text black tw-font-semibold tw-mr-1">
@@ -48,7 +48,9 @@
</div>
{{end}}
{{end}}
- {{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/comments/%d/reactions" $.root.RepoLink .ID)}}
+ {{if not $.root.Repository.IsArchived}}
+ {{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/comments/%d/reactions" $.root.RepoLink .ID)}}
+ {{end}}
{{template "repo/issue/view_content/context_menu" dict "item" . "delete" true "issue" false "diff" true "IsCommentPoster" (and $.root.IsSigned (eq $.root.SignedUserID .PosterID))}}
</div>
</div>
diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl
index 9a7a04a328..4e8ad1326c 100644
--- a/templates/repo/diff/compare.tmpl
+++ b/templates/repo/diff/compare.tmpl
@@ -32,7 +32,7 @@
<a class="tw-mr-2" href="{{$.HeadRepo.Link}}/compare/{{PathEscapeSegments $.HeadBranch}}{{$.CompareSeparator}}{{if not $.PullRequestCtx.SameRepo}}{{PathEscape $.BaseName}}/{{PathEscape $.Repository.Name}}:{{end}}{{PathEscapeSegments $.BaseBranch}}" title="{{ctx.Locale.Tr "repo.pulls.switch_head_and_base"}}">{{svg "octicon-git-compare"}}</a>
<div class="ui dropdown jump select-branch">
<div class="ui basic small button">
- <span class="text">{{if $.PageIsComparePull}}{{ctx.Locale.Tr "repo.pulls.compare_base"}}{{else}}{{ctx.Locale.Tr "repo.compare.compare_base"}}{{end}}: {{$BaseCompareName}}:{{$.BaseBranch}}</span>
+ <span class="text">{{if $.PageIsComparePull}}{{ctx.Locale.Tr "repo.pulls.compare_base"}}{{else}}{{ctx.Locale.Tr "repo.compare.compare_base"}}{{end}}: <strong>{{$BaseCompareName}}:{{$.BaseBranch}}</strong></span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</div>
<div class="menu">
@@ -103,7 +103,7 @@
<div class="ui dropdown jump select-branch">
<div class="ui basic small button">
- <span class="text">{{if $.PageIsComparePull}}{{ctx.Locale.Tr "repo.pulls.compare_compare"}}{{else}}{{ctx.Locale.Tr "repo.compare.compare_head"}}{{end}}: {{$HeadCompareName}}:{{$.HeadBranch}}</span>
+ <span class="text">{{if $.PageIsComparePull}}{{ctx.Locale.Tr "repo.pulls.compare_compare"}}{{else}}{{ctx.Locale.Tr "repo.compare.compare_head"}}{{end}}: <strong>{{$HeadCompareName}}:{{$.HeadBranch}}</strong></span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</div>
<div class="menu">
@@ -189,7 +189,7 @@
<div class="ui segment flex-text-block tw-gap-4">
{{template "shared/issueicon" .}}
<div class="issue-title tw-break-anywhere">
- {{ctx.RenderUtils.RenderIssueTitle .PullRequest.Issue.Title ($.Repository.ComposeMetas ctx)}}
+ {{ctx.RenderUtils.RenderIssueTitle .PullRequest.Issue.Title $.Repository}}
<span class="index">#{{.PullRequest.Issue.Index}}</span>
</div>
<a href="{{$.RepoLink}}/pulls/{{.PullRequest.Issue.Index}}" class="ui compact button primary">
@@ -205,10 +205,10 @@
{{end}}
</div>
{{else if $allowCreatePR}}
- <div class="ui info message pullrequest-form-toggle {{if .Flash}}tw-hidden{{end}}">
+ <div class="ui info message pullrequest-form-toggle {{if .ExpandNewPrForm}}tw-hidden{{end}}">
<button class="ui button primary show-panel toggle" data-panel=".pullrequest-form-toggle, .pullrequest-form">{{ctx.Locale.Tr "repo.pulls.new"}}</button>
</div>
- <div class="pullrequest-form {{if not .Flash}}tw-hidden{{end}}">
+ <div class="pullrequest-form {{if not .ExpandNewPrForm}}tw-hidden{{end}}">
{{template "repo/issue/new_form" .}}
</div>
{{end}}
diff --git a/templates/repo/diff/conversation.tmpl b/templates/repo/diff/conversation.tmpl
index 08f60644b3..eb2abfa7e9 100644
--- a/templates/repo/diff/conversation.tmpl
+++ b/templates/repo/diff/conversation.tmpl
@@ -8,9 +8,9 @@
{{$referenceUrl := printf "%s#%s" $.Issue.Link $comment.HashTag}}
<div class="conversation-holder" data-path="{{$comment.TreePath}}" data-side="{{if lt $comment.Line 0}}left{{else}}right{{end}}" data-idx="{{$comment.UnsignedLine}}">
{{if $resolved}}
- <div class="ui attached header resolved-placeholder tw-flex tw-items-center tw-justify-between">
- <div class="ui grey text tw-flex tw-items-center tw-flex-wrap tw-gap-1">
- {{svg "octicon-check" 16 "icon tw-mr-1"}}
+ <div class="resolved-placeholder">
+ <div class="flex-text-block tw-flex-wrap grey text">
+ {{svg "octicon-check"}}
<b>{{$resolveDoer.Name}}</b> {{ctx.Locale.Tr "repo.issues.review.resolved_by"}}
{{if $invalid}}
<!--
@@ -22,35 +22,33 @@
</a>
{{end}}
</div>
- <div class="tw-flex tw-items-center tw-gap-2">
- <button id="show-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="ui tiny labeled button show-outdated tw-flex tw-items-center">
- {{svg "octicon-unfold" 16 "tw-mr-2"}}
- {{ctx.Locale.Tr "repo.issues.review.show_resolved"}}
+ <div class="flex-text-block">
+ <button id="show-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="btn tiny show-outdated">
+ {{svg "octicon-unfold" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.issues.review.show_resolved"}}
</button>
- <button id="hide-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="ui tiny labeled button hide-outdated tw-flex tw-items-center tw-hidden">
- {{svg "octicon-fold" 16 "tw-mr-2"}}
- {{ctx.Locale.Tr "repo.issues.review.hide_resolved"}}
+ <button id="hide-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="btn tiny hide-outdated tw-hidden">
+ {{svg "octicon-fold" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.issues.review.hide_resolved"}}
</button>
</div>
</div>
{{end}}
<div id="code-comments-{{$comment.ID}}" class="field comment-code-cloud {{if $resolved}}tw-hidden{{end}}">
<div class="comment-list">
- <ui class="ui comments">
+ <div class="ui comments">
{{template "repo/diff/comments" dict "root" $ "comments" .comments}}
- </ui>
+ </div>
</div>
- <div class="tw-flex tw-justify-end tw-items-center tw-gap-2 tw-mt-2 tw-flex-wrap">
+ <div class="flex-text-block tw-mt-2 tw-flex-wrap tw-justify-end">
<div class="ui buttons">
<button class="ui icon tiny basic button previous-conversation">
- {{svg "octicon-arrow-up" 12 "icon"}} {{ctx.Locale.Tr "repo.issues.previous"}}
+ {{svg "octicon-arrow-up" 12}} {{ctx.Locale.Tr "repo.issues.previous"}}
</button>
<button class="ui icon tiny basic button next-conversation">
- {{svg "octicon-arrow-down" 12 "icon"}} {{ctx.Locale.Tr "repo.issues.next"}}
+ {{svg "octicon-arrow-down" 12}} {{ctx.Locale.Tr "repo.issues.next"}}
</button>
</div>
{{if and $.CanMarkConversation $hasReview (not $isReviewPending)}}
- <button class="ui icon tiny basic button resolve-conversation tw-mr-0" data-origin="diff" data-action="{{if not $resolved}}Resolve{{else}}UnResolve{{end}}" data-comment-id="{{$comment.ID}}" data-update-url="{{$.RepoLink}}/issues/resolve_conversation">
+ <button class="ui icon tiny basic button resolve-conversation" data-origin="diff" data-action="{{if not $resolved}}Resolve{{else}}UnResolve{{end}}" data-comment-id="{{$comment.ID}}" data-update-url="{{$.RepoLink}}/issues/resolve_conversation">
{{if $resolved}}
{{ctx.Locale.Tr "repo.issues.review.un_resolve_conversation"}}
{{else}}
@@ -59,8 +57,8 @@
</button>
{{end}}
{{if and $.SignedUserID (not $.Repository.IsArchived)}}
- <button class="comment-form-reply ui primary tiny labeled icon button tw-mr-0">
- {{svg "octicon-reply" 16 "reply icon tw-mr-1"}}{{ctx.Locale.Tr "repo.diff.comment.reply"}}
+ <button class="comment-form-reply ui primary icon tiny button">
+ {{svg "octicon-reply" 12}}{{ctx.Locale.Tr "repo.diff.comment.reply"}}
</button>
{{end}}
</div>
diff --git a/templates/repo/diff/image_diff.tmpl b/templates/repo/diff/image_diff.tmpl
index 608174e51b..7557129c64 100644
--- a/templates/repo/diff/image_diff.tmpl
+++ b/templates/repo/diff/image_diff.tmpl
@@ -9,20 +9,20 @@
>
<overflow-menu class="ui secondary pointing tabular menu custom">
<div class="overflow-menu-items tw-justify-center">
- <a class="item active" data-tab="diff-side-by-side-{{.file.Index}}">{{ctx.Locale.Tr "repo.diff.image.side_by_side"}}</a>
+ <a class="item active" data-tab="diff-side-by-side-{{.file.NameHash}}">{{ctx.Locale.Tr "repo.diff.image.side_by_side"}}</a>
{{if and .blobBase .blobHead}}
- <a class="item" data-tab="diff-swipe-{{.file.Index}}">{{ctx.Locale.Tr "repo.diff.image.swipe"}}</a>
- <a class="item" data-tab="diff-overlay-{{.file.Index}}">{{ctx.Locale.Tr "repo.diff.image.overlay"}}</a>
+ <a class="item" data-tab="diff-swipe-{{.file.NameHash}}">{{ctx.Locale.Tr "repo.diff.image.swipe"}}</a>
+ <a class="item" data-tab="diff-overlay-{{.file.NameHash}}">{{ctx.Locale.Tr "repo.diff.image.overlay"}}</a>
{{end}}
</div>
</overflow-menu>
<div class="image-diff-tabs is-loading">
- <div class="ui bottom attached tab image-diff-container active" data-tab="diff-side-by-side-{{.file.Index}}">
+ <div class="ui bottom attached tab image-diff-container active" data-tab="diff-side-by-side-{{.file.NameHash}}">
<div class="diff-side-by-side">
{{if .blobBase}}
<span class="side">
<p class="side-header">{{ctx.Locale.Tr "repo.diff.file_before"}}</p>
- <span class="before-container"><img class="image-before"></span>
+ <span class="before-container"><img alt class="image-before"></span>
<p>
<span class="bounds-info-before">
{{ctx.Locale.Tr "repo.diff.file_image_width"}}: <span class="text bounds-info-width"></span>
@@ -37,7 +37,7 @@
{{if .blobHead}}
<span class="side">
<p class="side-header">{{ctx.Locale.Tr "repo.diff.file_after"}}</p>
- <span class="after-container"><img class="image-after"></span>
+ <span class="after-container"><img alt class="image-after"></span>
<p>
<span class="bounds-info-after">
{{ctx.Locale.Tr "repo.diff.file_image_width"}}: <span class="text bounds-info-width"></span>
@@ -52,12 +52,12 @@
</div>
</div>
{{if and .blobBase .blobHead}}
- <div class="ui bottom attached tab image-diff-container" data-tab="diff-swipe-{{.file.Index}}">
+ <div class="ui bottom attached tab image-diff-container" data-tab="diff-swipe-{{.file.NameHash}}">
<div class="diff-swipe">
<div class="swipe-frame">
- <span class="before-container"><img class="image-before"></span>
+ <span class="before-container"><img alt class="image-before"></span>
<span class="swipe-container">
- <span class="after-container"><img class="image-after"></span>
+ <span class="after-container"><img alt class="image-after"></span>
</span>
<span class="swipe-bar">
<span class="handle top-handle"></span>
@@ -66,12 +66,12 @@
</div>
</div>
</div>
- <div class="ui bottom attached tab image-diff-container" data-tab="diff-overlay-{{.file.Index}}">
+ <div class="ui bottom attached tab image-diff-container" data-tab="diff-overlay-{{.file.NameHash}}">
<div class="diff-overlay">
<input type="range" min="0" max="100" value="50">
<div class="overlay-frame">
- <span class="before-container"><img class="image-before"></span>
- <span class="after-container"><img class="image-after"></span>
+ <span class="before-container"><img alt class="image-before"></span>
+ <span class="after-container"><img alt class="image-after"></span>
</div>
</div>
</div>
diff --git a/templates/repo/diff/new_review.tmpl b/templates/repo/diff/new_review.tmpl
index 2febc6303a..3bb01a139a 100644
--- a/templates/repo/diff/new_review.tmpl
+++ b/templates/repo/diff/new_review.tmpl
@@ -1,56 +1,59 @@
-<div id="review-box">
- <button class="ui tiny primary button tw-pr-1 tw-flex js-btn-review {{if not $.IsShowingAllCommits}}disabled{{end}}" {{if not $.IsShowingAllCommits}}data-tooltip-content="{{ctx.Locale.Tr "repo.pulls.review_only_possible_for_full_diff"}}"{{end}}>
+<div id="review-box" {{if $.Repository.IsArchived}}data-tooltip-content="{{ctx.Locale.Tr "repo.archive.pull.nocomment"}}"{{end}}>
+ <button class="ui tiny primary button tw-pr-1 js-btn-review {{if not $.IsShowingAllCommits}}disabled{{end}}"
+ {{if not $.IsShowingAllCommits}}data-tooltip-content="{{ctx.Locale.Tr "repo.pulls.review_only_possible_for_full_diff"}}"{{end}}
+ {{if $.Repository.IsArchived}}disabled{{end}}
+ >
{{ctx.Locale.Tr "repo.diff.review"}}
<span class="ui small label review-comments-counter" data-pending-comment-number="{{.PendingCodeCommentNumber}}">{{.PendingCodeCommentNumber}}</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</button>
- {{if $.IsShowingAllCommits}}
- <div class="review-box-panel tippy-target">
- <div class="ui segment">
- <form class="ui form form-fetch-action" action="{{.Link}}/reviews/submit" method="post">
- {{.CsrfTokenHtml}}
- <input type="hidden" name="commit_id" value="{{.AfterCommitID}}">
- <div class="field tw-flex tw-items-center">
- <div class="tw-flex-1">{{ctx.Locale.Tr "repo.diff.review.header"}}</div>
- <a class="muted close">{{svg "octicon-x" 16}}</a>
- </div>
+</div>
+{{if $.IsShowingAllCommits}}
+<div class="review-box-panel tippy-target">
+ <div class="ui segment">
+ <form class="ui form form-fetch-action" action="{{.Link}}/reviews/submit" method="post">
+ {{.CsrfTokenHtml}}
+ <input type="hidden" name="commit_id" value="{{.AfterCommitID}}">
+ <div class="field tw-flex tw-items-center">
+ <div class="tw-flex-1">{{ctx.Locale.Tr "repo.diff.review.header"}}</div>
+ <a class="muted close">{{svg "octicon-x" 16}}</a>
+ </div>
+ <div class="field">
+ {{template "shared/combomarkdowneditor" (dict
+ "MarkdownPreviewInRepo" $.Repository
+ "MarkdownPreviewMode" "comment"
+ "TextareaName" "content"
+ "TextareaPlaceholder" (ctx.Locale.Tr "repo.diff.review.placeholder")
+ "DropzoneParentContainer" "form"
+ )}}
+ </div>
+ {{if .IsAttachmentEnabled}}
<div class="field">
- {{template "shared/combomarkdowneditor" (dict
- "MarkdownPreviewInRepo" $.Repository
- "MarkdownPreviewMode" "comment"
- "TextareaName" "content"
- "TextareaPlaceholder" (ctx.Locale.Tr "repo.diff.review.placeholder")
- "DropzoneParentContainer" "form"
- )}}
+ {{template "repo/upload" .}}
</div>
- {{if .IsAttachmentEnabled}}
- <div class="field">
- {{template "repo/upload" .}}
- </div>
- {{end}}
- <div class="divider"></div>
- {{$showSelfTooltip := (and $.IsSigned ($.Issue.IsPoster $.SignedUser.ID))}}
- {{if not $.Issue.IsClosed}}
- {{if $showSelfTooltip}}
- <span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_approve"}}">
- <button type="submit" name="type" value="approve" disabled class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
- </span>
- {{else}}
- <button type="submit" name="type" value="approve" class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
- {{end}}
+ {{end}}
+ <div class="divider"></div>
+ {{$showSelfTooltip := (and $.IsSigned ($.Issue.IsPoster $.SignedUser.ID))}}
+ {{if not $.Issue.IsClosed}}
+ {{if $showSelfTooltip}}
+ <span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_approve"}}">
+ <button type="submit" name="type" value="approve" disabled class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
+ </span>
+ {{else}}
+ <button type="submit" name="type" value="approve" class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
{{end}}
- <button type="submit" name="type" value="comment" class="ui submit tiny basic button btn-submit">{{ctx.Locale.Tr "repo.diff.review.comment"}}</button>
- {{if not $.Issue.IsClosed}}
- {{if $showSelfTooltip}}
- <span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_reject"}}">
- <button type="submit" name="type" value="reject" disabled class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
- </span>
- {{else}}
- <button type="submit" name="type" value="reject" class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
- {{end}}
+ {{end}}
+ <button type="submit" name="type" value="comment" class="ui submit tiny basic button btn-submit">{{ctx.Locale.Tr "repo.diff.review.comment"}}</button>
+ {{if not $.Issue.IsClosed}}
+ {{if $showSelfTooltip}}
+ <span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_reject"}}">
+ <button type="submit" name="type" value="reject" disabled class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
+ </span>
+ {{else}}
+ <button type="submit" name="type" value="reject" class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
{{end}}
- </form>
- </div>
+ {{end}}
+ </form>
</div>
- {{end}}
</div>
+{{end}}
diff --git a/templates/repo/diff/options_dropdown.tmpl b/templates/repo/diff/options_dropdown.tmpl
index 09b7b80e41..8d08e7ad46 100644
--- a/templates/repo/diff/options_dropdown.tmpl
+++ b/templates/repo/diff/options_dropdown.tmpl
@@ -1,7 +1,6 @@
<div class="ui dropdown tiny basic button" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.options_button"}}">
{{svg "octicon-kebab-horizontal"}}
<div class="menu">
- <a class="item" id="show-file-list-btn">{{ctx.Locale.Tr "repo.diff.show_diff_stats"}}</a>
{{if .Issue.Index}}
<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.patch" download="{{.Issue.Index}}.patch">{{ctx.Locale.Tr "repo.diff.download_patch"}}</a>
<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.diff" download="{{.Issue.Index}}.diff">{{ctx.Locale.Tr "repo.diff.download_diff"}}</a>
diff --git a/templates/repo/editor/cherry_pick.tmpl b/templates/repo/editor/cherry_pick.tmpl
index f9c9eef5aa..7981fd0761 100644
--- a/templates/repo/editor/cherry_pick.tmpl
+++ b/templates/repo/editor/cherry_pick.tmpl
@@ -3,15 +3,14 @@
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
- <form class="ui edit form" method="post" action="{{.RepoLink}}/_cherrypick/{{.SHA}}/{{.BranchName | PathEscapeSegments}}">
+ <form class="ui edit form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}">
{{.CsrfTokenHtml}}
- <input type="hidden" name="last_commit" value="{{.last_commit}}">
- <input type="hidden" name="page_has_posted" value="true">
+ {{template "repo/editor/common_top" .}}
<input type="hidden" name="revert" value="{{if eq .CherryPickType "revert"}}true{{else}}false{{end}}">
<div class="repo-editor-header">
- <div class="ui breadcrumb field {{if .Err_TreePath}}error{{end}}">
- {{$shaurl := printf "%s/commit/%s" $.RepoLink (PathEscape .SHA)}}
- {{$shalink := HTMLFormat `<a class="ui primary sha label" href="%s">%s</a>` $shaurl (ShortSha .SHA)}}
+ <div class="breadcrumb">
+ {{$shaurl := printf "%s/commit/%s" $.RepoLink (PathEscape .FromCommitID)}}
+ {{$shalink := HTMLFormat `<a class="ui primary sha label" href="%s">%s</a>` $shaurl (ShortSha .FromCommitID)}}
{{if eq .CherryPickType "revert"}}
{{ctx.Locale.Tr "repo.editor.revert" $shalink}}
{{else}}
diff --git a/templates/repo/editor/commit_form.tmpl b/templates/repo/editor/commit_form.tmpl
index c050324e93..7067614444 100644
--- a/templates/repo/editor/commit_form.tmpl
+++ b/templates/repo/editor/commit_form.tmpl
@@ -1,11 +1,11 @@
<div class="commit-form-wrapper">
{{ctx.AvatarUtils.Avatar .SignedUser 40 "commit-avatar"}}
<div class="commit-form">
- <h3>{{- if .CanCommitToBranch.WillSign}}
- <span title="{{ctx.Locale.Tr "repo.signing.will_sign" .CanCommitToBranch.SigningKey}}">{{svg "octicon-lock" 24}}</span>
+ <h3>{{- if .CommitFormOptions.WillSign}}
+ <span title="{{ctx.Locale.Tr "repo.signing.will_sign" .CommitFormOptions.SigningKey}}">{{svg "octicon-lock" 24}}</span>
{{ctx.Locale.Tr "repo.editor.commit_signed_changes"}}
{{- else}}
- <span title="{{ctx.Locale.Tr (printf "repo.signing.wont_sign.%s" .CanCommitToBranch.WontSignReason)}}">{{svg "octicon-unlock" 24}}</span>
+ <span title="{{ctx.Locale.Tr (printf "repo.signing.wont_sign.%s" .CommitFormOptions.WontSignReason)}}">{{svg "octicon-unlock" 24}}</span>
{{ctx.Locale.Tr "repo.editor.commit_changes"}}
{{- end}}</h3>
<div class="field">
@@ -22,17 +22,17 @@
</div>
<div class="quick-pull-choice js-quick-pull-choice">
<div class="field">
- <div class="ui radio checkbox {{if not .CanCommitToBranch.CanCommitToBranch}}disabled{{end}}">
+ <div class="ui radio checkbox {{if not .CommitFormOptions.CanCommitToBranch}}disabled{{end}}">
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="direct" data-button-text="{{ctx.Locale.Tr "repo.editor.commit_changes"}}" {{if eq .commit_choice "direct"}}checked{{end}}>
<label>
{{svg "octicon-git-commit"}}
{{ctx.Locale.Tr "repo.editor.commit_directly_to_this_branch" .BranchName}}
- {{if not .CanCommitToBranch.CanCommitToBranch}}
+ {{if not .CommitFormOptions.CanCommitToBranch}}
<div class="ui visible small warning message">
{{ctx.Locale.Tr "repo.editor.no_commit_to_branch"}}
<ul>
- {{if not .CanCommitToBranch.UserCanPush}}<li>{{ctx.Locale.Tr "repo.editor.user_no_push_to_branch"}}</li>{{end}}
- {{if and .CanCommitToBranch.RequireSigned (not .CanCommitToBranch.WillSign)}}<li>{{ctx.Locale.Tr "repo.editor.require_signed_commit"}}</li>{{end}}
+ {{if not .CommitFormOptions.UserCanPush}}<li>{{ctx.Locale.Tr "repo.editor.user_no_push_to_branch"}}</li>{{end}}
+ {{if and .CommitFormOptions.RequireSigned (not .CommitFormOptions.WillSign)}}<li>{{ctx.Locale.Tr "repo.editor.require_signed_commit"}}</li>{{end}}
</ul>
</div>
{{end}}
@@ -42,14 +42,14 @@
{{if and (not .Repository.IsEmpty) (not .IsEditingFileOnly)}}
<div class="field">
<div class="ui radio checkbox">
- {{if .CanCreatePullRequest}}
+ {{if .CommitFormOptions.CanCreatePullRequest}}
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch" data-button-text="{{ctx.Locale.Tr "repo.editor.propose_file_change"}}" {{if eq .commit_choice "commit-to-new-branch"}}checked{{end}}>
{{else}}
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch" data-button-text="{{ctx.Locale.Tr "repo.editor.commit_changes"}}" {{if eq .commit_choice "commit-to-new-branch"}}checked{{end}}>
{{end}}
<label>
{{svg "octicon-git-pull-request"}}
- {{if .CanCreatePullRequest}}
+ {{if .CommitFormOptions.CanCreatePullRequest}}
{{ctx.Locale.Tr "repo.editor.create_new_branch"}}
{{else}}
{{ctx.Locale.Tr "repo.editor.create_new_branch_np"}}
@@ -58,7 +58,7 @@
</div>
</div>
<div class="quick-pull-branch-name {{if not (eq .commit_choice "commit-to-new-branch")}}tw-hidden{{end}}">
- <div class="new-branch-name-input field {{if .Err_NewBranchName}}error{{end}}">
+ <div class="new-branch-name-input field">
{{svg "octicon-git-branch"}}
<input type="text" name="new_branch_name" maxlength="100" value="{{.new_branch_name}}" class="input-contrast tw-mr-1 js-quick-pull-new-branch-name" placeholder="{{ctx.Locale.Tr "repo.editor.new_branch_name_desc"}}" {{if eq .commit_choice "commit-to-new-branch"}}required{{end}} title="{{ctx.Locale.Tr "repo.editor.new_branch_name"}}">
<span class="text-muted js-quick-pull-normalization-info"></span>
@@ -66,7 +66,18 @@
</div>
{{end}}
</div>
+ {{if and .CommitCandidateEmails (gt (len .CommitCandidateEmails) 1)}}
+ <div class="field">
+ <label>{{ctx.Locale.Tr "repo.editor.commit_email"}}</label>
+ <select class="ui selection dropdown" name="commit_email">
+ {{- range $email := .CommitCandidateEmails -}}
+ <option {{if eq $email $.CommitDefaultEmail}}selected{{end}} value="{{$email}}">{{$email}}</option>
+ {{- end -}}
+ </select>
+ </div>
+ {{end}}
</div>
+ <input type="hidden" name="last_commit" value="{{.last_commit}}">
<button id="commit-button" type="submit" class="ui primary button">
{{if eq .commit_choice "commit-to-new-branch"}}{{ctx.Locale.Tr "repo.editor.propose_file_change"}}{{else}}{{ctx.Locale.Tr "repo.editor.commit_changes"}}{{end}}
</button>
diff --git a/templates/repo/editor/common_breadcrumb.tmpl b/templates/repo/editor/common_breadcrumb.tmpl
new file mode 100644
index 0000000000..8cfbe09d3e
--- /dev/null
+++ b/templates/repo/editor/common_breadcrumb.tmpl
@@ -0,0 +1,16 @@
+<div class="breadcrumb">
+ <a class="section" href="{{$.BranchLink}}">{{.Repository.Name}}</a>
+ {{$n := len .TreeNames}}
+ {{$l := Eval $n "-" 1}}
+ {{range $i, $v := .TreeNames}}
+ <div class="breadcrumb-divider">/</div>
+ {{if eq $i $l}}
+ <input id="file-name" maxlength="255" value="{{$v}}" placeholder="{{ctx.Locale.Tr (Iif $.PageIsUpload "repo.editor.add_subdir" "repo.editor.name_your_file")}}" data-editorconfig="{{$.EditorconfigJson}}" {{Iif $.PageIsUpload "" "required"}} autofocus>
+ <span data-tooltip-content="{{ctx.Locale.Tr "repo.editor.filename_help"}}">{{svg "octicon-info"}}</span>
+ {{else}}
+ <span class="section"><a href="{{$.BranchLink}}/{{index $.TreePaths $i | PathEscapeSegments}}">{{$v}}</a></span>
+ {{end}}
+ {{end}}
+ <span>{{ctx.Locale.Tr "repo.editor.or"}} <a href="{{or .ReturnURI (print $.BranchLink "/" (PathEscapeSegments .TreePath))}}">{{ctx.Locale.Tr "repo.editor.cancel_lower"}}</a></span>
+ <input type="hidden" id="tree_path" name="tree_path" value="{{.TreePath}}">
+</div>
diff --git a/templates/repo/editor/common_top.tmpl b/templates/repo/editor/common_top.tmpl
new file mode 100644
index 0000000000..23280ed565
--- /dev/null
+++ b/templates/repo/editor/common_top.tmpl
@@ -0,0 +1,6 @@
+{{if .CommitFormOptions.WillSubmitToFork}}
+<div class="ui blue message">
+ {{$repoLinkHTML := HTMLFormat `<a href="%s">%s</a>` .CommitFormOptions.TargetRepo.Link .CommitFormOptions.TargetRepo.FullName}}
+ {{ctx.Locale.Tr "repo.editor.fork_edit_description" $repoLinkHTML}}
+</div>
+{{end}}
diff --git a/templates/repo/editor/delete.tmpl b/templates/repo/editor/delete.tmpl
index 2c0c2fc792..bf6143f1cb 100644
--- a/templates/repo/editor/delete.tmpl
+++ b/templates/repo/editor/delete.tmpl
@@ -3,9 +3,9 @@
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
- <form class="ui form" method="post">
+ <form class="ui form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}">
{{.CsrfTokenHtml}}
- <input type="hidden" name="last_commit" value="{{.last_commit}}">
+ {{template "repo/editor/common_top" .}}
{{template "repo/editor/commit_form" .}}
</form>
</div>
diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl
index 204a426970..0911d02e1f 100644
--- a/templates/repo/editor/edit.tmpl
+++ b/templates/repo/editor/edit.tmpl
@@ -3,56 +3,49 @@
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
- <form class="ui edit form" method="post"
+ <form class="ui edit form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}"
data-text-empty-confirm-header="{{ctx.Locale.Tr "repo.editor.commit_empty_file_header"}}"
data-text-empty-confirm-content="{{ctx.Locale.Tr "repo.editor.commit_empty_file_text"}}"
>
{{.CsrfTokenHtml}}
- <input type="hidden" name="last_commit" value="{{.last_commit}}">
- <input type="hidden" name="page_has_posted" value="{{.PageHasPosted}}">
+ {{template "repo/editor/common_top" .}}
<div class="repo-editor-header">
- <div class="ui breadcrumb field{{if .Err_TreePath}} error{{end}}">
- <a class="section" href="{{$.BranchLink}}">{{.Repository.Name}}</a>
- {{$n := len .TreeNames}}
- {{$l := Eval $n "-" 1}}
- {{range $i, $v := .TreeNames}}
- <div class="breadcrumb-divider">/</div>
- {{if eq $i $l}}
- <input id="file-name" maxlength="255" value="{{$v}}" placeholder="{{ctx.Locale.Tr "repo.editor.name_your_file"}}" data-editorconfig="{{$.EditorconfigJson}}" required autofocus>
- <span data-tooltip-content="{{ctx.Locale.Tr "repo.editor.filename_help"}}">{{svg "octicon-info"}}</span>
- {{else}}
- <span class="section"><a href="{{$.BranchLink}}/{{index $.TreePaths $i | PathEscapeSegments}}">{{$v}}</a></span>
- {{end}}
- {{end}}
- <span>{{ctx.Locale.Tr "repo.editor.or"}} <a href="{{if .ReturnURI}}{{.ReturnURI}}{{else}}{{$.BranchLink}}{{if not .IsNewFile}}/{{PathEscapeSegments .TreePath}}{{end}}{{end}}">{{ctx.Locale.Tr "repo.editor.cancel_lower"}}</a></span>
- <input type="hidden" id="tree_path" name="tree_path" value="{{.TreePath}}" required>
- </div>
+ {{template "repo/editor/common_breadcrumb" .}}
</div>
- <div class="field">
- <div class="ui top attached header">
- <div class="ui compact small menu small-menu-items repo-editor-menu">
- <a class="active item" data-tab="write">{{svg "octicon-code"}} {{if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.new_file"}}{{else}}{{ctx.Locale.Tr "repo.editor.edit_file"}}{{end}}</a>
- <a class="item" data-tab="preview" data-preview-url="{{.Repository.Link}}/markup" data-preview-context-ref="{{.RepoLink}}/src/{{.BranchNameSubURL}}">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a>
- {{if not .IsNewFile}}
- <a class="item" data-tab="diff" hx-params="context,content" hx-vals='{"context":"{{.BranchLink}}"}' hx-include="#edit_area" hx-swap="innerHTML" hx-target=".tab[data-tab='diff']" hx-indicator=".tab[data-tab='diff']" hx-post="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">{{svg "octicon-diff"}} {{ctx.Locale.Tr "repo.editor.preview_changes"}}</a>
- {{end}}
- </div>
- </div>
- <div class="ui bottom attached segment tw-p-0">
- <div class="ui active tab tw-rounded-b" data-tab="write">
- <textarea id="edit_area" name="content" class="tw-hidden" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
- data-previewable-extensions="{{.PreviewableExtensions}}"
- data-line-wrap-extensions="{{.LineWrapExtensions}}">{{.FileContent}}</textarea>
- <div class="editor-loading is-loading"></div>
+ {{if not .NotEditableReason}}
+ <div class="field">
+ <div class="ui top attached header">
+ <div class="ui compact small menu small-menu-items repo-editor-menu">
+ <a class="active item" data-tab="write">{{svg "octicon-code"}} {{if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.new_file"}}{{else}}{{ctx.Locale.Tr "repo.editor.edit_file"}}{{end}}</a>
+ <a class="item" data-tab="preview" data-preview-url="{{.Repository.Link}}/markup" data-preview-context-ref="{{.RepoLink}}/src/{{.RefTypeNameSubURL}}">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a>
+ {{if not .IsNewFile}}
+ <a class="item" data-tab="diff" hx-params="context,content" hx-vals='{"context":"{{.BranchLink}}"}' hx-include="#edit_area" hx-swap="innerHTML" hx-target=".tab[data-tab='diff']" hx-indicator=".tab[data-tab='diff']" hx-post="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">{{svg "octicon-diff"}} {{ctx.Locale.Tr "repo.editor.preview_changes"}}</a>
+ {{end}}
+ </div>
</div>
- <div class="ui tab markup tw-px-4 tw-py-3" data-tab="preview">
- {{ctx.Locale.Tr "loading"}}
+ <div class="ui bottom attached segment tw-p-0">
+ <div class="ui active tab tw-rounded-b" data-tab="write">
+ <textarea id="edit_area" name="content" class="tw-hidden" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
+ data-previewable-extensions="{{.PreviewableExtensions}}"
+ data-line-wrap-extensions="{{.LineWrapExtensions}}">{{.FileContent}}</textarea>
+ <div class="editor-loading is-loading"></div>
+ </div>
+ <div class="ui tab tw-px-4 tw-py-3" data-tab="preview">
+ {{ctx.Locale.Tr "loading"}}
+ </div>
+ <div class="ui tab" data-tab="diff">
+ <div class="tw-p-16"></div>
+ </div>
</div>
- <div class="ui tab diff edit-diff" data-tab="diff">
- <div class="tw-p-16"></div>
+ </div>
+ {{else}}
+ <div class="field">
+ <div class="ui segment tw-text-center">
+ <h4 class="tw-font-semibold tw-mb-2">{{.NotEditableReason}}</h4>
+ <p>{{ctx.Locale.Tr "repo.editor.file_not_editable_hint"}}</p>
</div>
</div>
- </div>
+ {{end}}
{{template "repo/editor/commit_form" .}}
</form>
</div>
diff --git a/templates/repo/editor/fork.tmpl b/templates/repo/editor/fork.tmpl
new file mode 100644
index 0000000000..e28b2ba7a2
--- /dev/null
+++ b/templates/repo/editor/fork.tmpl
@@ -0,0 +1,18 @@
+{{template "base/head" .}}
+<div role="main" aria-label="{{.Title}}" class="page-content repository">
+ {{template "repo/header" .}}
+ <div class="ui container">
+ {{template "base/alert" .}}
+ <form class="ui form form-fetch-action" method="post" action="{{.RepoLink}}/_fork/{{.BranchName | PathEscapeSegments}}">
+ {{.CsrfTokenHtml}}
+ <div class="tw-text-center">
+ <div class="tw-my-[40px]">
+ <h3>{{ctx.Locale.Tr "repo.editor.fork_create"}}</h3>
+ <p>{{ctx.Locale.Tr "repo.editor.fork_create_description"}}</p>
+ </div>
+ <button class="ui primary button">{{ctx.Locale.Tr "repo.fork_repo"}}</button>
+ </div>
+ </form>
+ </div>
+</div>
+{{template "base/footer" .}}
diff --git a/templates/repo/editor/patch.tmpl b/templates/repo/editor/patch.tmpl
index 33a7c2a89d..fa00edd92e 100644
--- a/templates/repo/editor/patch.tmpl
+++ b/templates/repo/editor/patch.tmpl
@@ -3,15 +3,14 @@
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
- <form class="ui edit form" method="post" action="{{.RepoLink}}/_diffpatch/{{.BranchName | PathEscapeSegments}}"
+ <form class="ui edit form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}"
data-text-empty-confirm-header="{{ctx.Locale.Tr "repo.editor.commit_empty_file_header"}}"
data-text-empty-confirm-content="{{ctx.Locale.Tr "repo.editor.commit_empty_file_text"}}"
>
{{.CsrfTokenHtml}}
- <input type="hidden" name="last_commit" value="{{.last_commit}}">
- <input type="hidden" name="page_has_posted" value="{{.PageHasPosted}}">
+ {{template "repo/editor/common_top" .}}
<div class="repo-editor-header">
- <div class="ui breadcrumb field {{if .Err_TreePath}}error{{end}}">
+ <div class="breadcrumb">
{{ctx.Locale.Tr "repo.editor.patching"}}
<a class="section" href="{{$.RepoLink}}">{{.Repository.FullName}}</a>
<div class="breadcrumb-divider">:</div>
diff --git a/templates/repo/editor/upload.tmpl b/templates/repo/editor/upload.tmpl
index 5725020406..3e36c77b3b 100644
--- a/templates/repo/editor/upload.tmpl
+++ b/templates/repo/editor/upload.tmpl
@@ -3,25 +3,11 @@
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
- <form class="ui comment form" method="post">
+ <form class="ui comment form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}">
{{.CsrfTokenHtml}}
+ {{template "repo/editor/common_top" .}}
<div class="repo-editor-header">
- <div class="ui breadcrumb field {{if .Err_TreePath}}error{{end}}">
- <a class="section" href="{{$.BranchLink}}">{{.Repository.Name}}</a>
- {{$n := len .TreeNames}}
- {{$l := Eval $n "-" 1}}
- {{range $i, $v := .TreeNames}}
- <div class="breadcrumb-divider">/</div>
- {{if eq $i $l}}
- <input type="text" id="file-name" maxlength="255" value="{{$v}}" placeholder="{{ctx.Locale.Tr "repo.editor.add_subdir"}}" autofocus>
- <span data-tooltip-content="{{ctx.Locale.Tr "repo.editor.filename_help"}}">{{svg "octicon-info"}}</span>
- {{else}}
- <span class="section"><a href="{{$.BranchLink}}/{{index $.TreePaths $i | PathEscapeSegments}}">{{$v}}</a></span>
- {{end}}
- {{end}}
- <span>{{ctx.Locale.Tr "repo.editor.or"}} <a href="{{$.BranchLink}}{{if not .IsNewFile}}/{{.TreePath | PathEscapeSegments}}{{end}}">{{ctx.Locale.Tr "repo.editor.cancel_lower"}}</a></span>
- <input type="hidden" id="tree_path" name="tree_path" value="{{.TreePath}}" required>
- </div>
+ {{template "repo/editor/common_breadcrumb" .}}
</div>
<div class="field">
{{template "repo/upload" .}}
diff --git a/templates/repo/empty.tmpl b/templates/repo/empty.tmpl
index 7170fe3602..32b5c8401b 100644
--- a/templates/repo/empty.tmpl
+++ b/templates/repo/empty.tmpl
@@ -14,14 +14,13 @@
{{end}}
</div>
{{end}}
+
{{if .Repository.IsBroken}}
- <div class="ui segment center">
- {{ctx.Locale.Tr "repo.broken_message"}}
- </div>
+ <div class="ui segment center">{{ctx.Locale.Tr "repo.broken_message"}}</div>
+ {{else if .RepoHasContentsWithoutBranch}}
+ <div class="ui segment center">{{ctx.Locale.Tr "repo.no_branch"}}</div>
{{else if .CanWriteCode}}
- <h4 class="ui top attached header">
- {{ctx.Locale.Tr "repo.quick_guide"}}
- </h4>
+ <h4 class="ui top attached header">{{ctx.Locale.Tr "repo.quick_guide"}}</h4>
<div class="ui attached guide table segment empty-repo-guide">
<div class="item">
<h3>{{ctx.Locale.Tr "repo.clone_this_repo"}} <small>{{ctx.Locale.Tr "repo.clone_helper" "http://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository"}}</small></h3>
@@ -48,7 +47,7 @@
<h3>{{ctx.Locale.Tr "repo.create_new_repo_command"}}</h3>
<div class="markup">
<pre><code>touch README.md
-git init
+git init{{if ne .Repository.ObjectFormatName "sha1"}} --object-format={{.Repository.ObjectFormatName}}{{end}}{{/* for sha256 repo, it needs to set "object-format" explicitly*/}}
{{if ne .Repository.DefaultBranch "master"}}git checkout -b {{.Repository.DefaultBranch}}{{end}}
git add README.md
git commit -m "first commit"
@@ -66,12 +65,10 @@ git push -u origin {{.Repository.DefaultBranch}}</code></pre>
</div>
</div>
{{end}}
- {{else}}
- <div class="ui segment center">
- {{ctx.Locale.Tr "repo.empty_message"}}
- </div>
- {{end}}
- </div>
+ </div>
+ {{else}}
+ <div class="ui segment center">{{ctx.Locale.Tr "repo.empty_message"}}</div>
+ {{end}}
</div>
</div>
</div>
diff --git a/templates/repo/file_info.tmpl b/templates/repo/file_info.tmpl
index b63af68973..38133bde2b 100644
--- a/templates/repo/file_info.tmpl
+++ b/templates/repo/file_info.tmpl
@@ -11,7 +11,7 @@
{{end}}
{{if ne .FileSize nil}}
<div class="file-info-entry">
- {{FileSize .FileSize}}{{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}}
+ {{FileSize .FileSize}}{{if .IsLFSFile}}<span class="ui label">LFS</span>{{end}}
</div>
{{end}}
{{if .LFSLock}}
diff --git a/templates/repo/forks.tmpl b/templates/repo/forks.tmpl
index 725b67c651..6e46457893 100644
--- a/templates/repo/forks.tmpl
+++ b/templates/repo/forks.tmpl
@@ -1,20 +1,12 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository forks">
{{template "repo/header" .}}
- <div class="ui container">
+ <div class="ui container fork-list">
<h2 class="ui dividing header">
{{ctx.Locale.Tr "repo.forks"}}
</h2>
- <div class="flex-list">
- {{range .Forks}}
- <div class="flex-item tw-border-0 repo-fork-item">
- <span>{{ctx.AvatarUtils.Avatar .Owner}}</span>
- <span><a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a> / <a href="{{.Link}}">{{.Name}}</a></span>
- </div>
- {{end}}
- </div>
+ {{template "shared/repo/list" .}}
+ {{template "base/paginate" .}}
</div>
-
- {{template "base/paginate" .}}
</div>
{{template "base/footer" .}}
diff --git a/templates/repo/graph.tmpl b/templates/repo/graph.tmpl
index 9eb4bd4ecb..cd115f95dc 100644
--- a/templates/repo/graph.tmpl
+++ b/templates/repo/graph.tmpl
@@ -2,7 +2,7 @@
<div role="main" aria-label="{{.Title}}" class="page-content repository commits">
{{template "repo/header" .}}
<div class="ui container">
- <div id="git-graph-container" class="ui segment{{if eq .Mode "monochrome"}} monochrome{{end}}">
+ <div id="git-graph-container" class="ui segment {{if eq .Mode "monochrome"}}monochrome{{end}}">
<h2 class="ui header dividing">
{{ctx.Locale.Tr "repo.commit_graph"}}
<div class="ui icon buttons tiny color-buttons">
@@ -11,52 +11,45 @@
<div class="default text">{{ctx.Locale.Tr "repo.commit_graph.select"}}</div>
<div class="menu">
<div class="item" data-value="...flow-hide-pr-refs">
- <span class="truncate">
- {{svg "octicon-eye-closed" 16 "tw-mr-1"}}<span title="{{ctx.Locale.Tr "repo.commit_graph.hide_pr_refs"}}">{{ctx.Locale.Tr "repo.commit_graph.hide_pr_refs"}}</span>
- </span>
+ {{svg "octicon-eye-closed"}}
+ <span class="gt-ellipsis" title="{{ctx.Locale.Tr "repo.commit_graph.hide_pr_refs"}}">{{ctx.Locale.Tr "repo.commit_graph.hide_pr_refs"}}</span>
</div>
{{range .AllRefs}}
{{$refGroup := .RefGroup}}
{{if eq $refGroup "pull"}}
<div class="item" data-value="{{.Name}}">
- <span class="truncate">
- {{svg "octicon-git-pull-request" 16 "tw-mr-1"}}<span title="{{.ShortName}}">#{{.ShortName}}</span>
- </span>
+ {{svg "octicon-git-pull-request"}}
+ <span class="gt-ellipsis" title="{{.ShortName}}">#{{.ShortName}}</span>
</div>
{{else if eq $refGroup "tags"}}
<div class="item" data-value="{{.Name}}">
- <span class="truncate">
- {{svg "octicon-tag" 16 "tw-mr-1"}}<span title="{{.ShortName}}">{{.ShortName}}</span>
- </span>
+ {{svg "octicon-tag"}}
+ <span class="gt-ellipsis" title="{{.ShortName}}">{{.ShortName}}</span>
</div>
{{else if eq $refGroup "remotes"}}
<div class="item" data-value="{{.Name}}">
- <span class="truncate">
- {{svg "octicon-cross-reference" 16 "tw-mr-1"}}<span title="{{.ShortName}}">{{.ShortName}}</span>
- </span>
+ {{svg "octicon-cross-reference"}}
+ <span class="gt-ellipsis" title="{{.ShortName}}">{{.ShortName}}</span>
</div>
{{else if eq $refGroup "heads"}}
<div class="item" data-value="{{.Name}}">
- <span class="truncate">
- {{svg "octicon-git-branch" 16 "tw-mr-1"}}<span title="{{.ShortName}}">{{.ShortName}}</span>
- </span>
+ {{svg "octicon-git-branch"}}
+ <span class="gt-ellipsis" title="{{.ShortName}}">{{.ShortName}}</span>
</div>
{{end}}
{{end}}
</div>
</div>
- <button id="flow-color-monochrome" class="ui labelled icon button{{if eq .Mode "monochrome"}} active{{end}}" title="{{ctx.Locale.Tr "repo.commit_graph.monochrome"}}">{{svg "material-invert-colors" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.commit_graph.monochrome"}}</button>
- <button id="flow-color-colored" class="ui labelled icon button{{if ne .Mode "monochrome"}} active{{end}}" title="{{ctx.Locale.Tr "repo.commit_graph.color"}}">{{svg "material-palette" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.commit_graph.color"}}</button>
+ <button id="flow-color-monochrome" class="ui icon button{{if eq .Mode "monochrome"}} active{{end}}" title="{{ctx.Locale.Tr "repo.commit_graph.monochrome"}}">{{svg "material-invert-colors" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.commit_graph.monochrome"}}</button>
+ <button id="flow-color-colored" class="ui icon button{{if ne .Mode "monochrome"}} active{{end}}" title="{{ctx.Locale.Tr "repo.commit_graph.color"}}">{{svg "material-palette" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.commit_graph.color"}}</button>
</div>
</h2>
- <div class="ui dividing"></div>
- <div class="is-loading tw-py-32 tw-hidden" id="loading-indicator"></div>
- {{template "repo/graph/svgcontainer" .}}
- {{template "repo/graph/commits" .}}
+ <div id="git-graph-body">
+ {{template "repo/graph/svgcontainer" .}}
+ {{template "repo/graph/commits" .}}
+ {{template "base/paginate" .}}
+ </div>
</div>
</div>
</div>
-<div id="pagination">
- {{template "base/paginate" .}}
-</div>
{{template "base/footer" .}}
diff --git a/templates/repo/graph/commits.tmpl b/templates/repo/graph/commits.tmpl
index 6af0ba1f0f..07ec076697 100644
--- a/templates/repo/graph/commits.tmpl
+++ b/templates/repo/graph/commits.tmpl
@@ -5,10 +5,13 @@
{{if $commit.OnlyRelation}}
<span></span>
{{else}}
- {{template "repo/commit_sign_badge" dict "Commit" $commit.Commit "CommitBaseLink" (print $.RepoLink "/commit") "CommitSignVerification" $commit.Verification}}
+ {{/* every field must be in a span to get correctly styled */}}
+ <span>
+ {{template "repo/commit_sign_badge" dict "Commit" $commit.Commit "CommitBaseLink" (print $.RepoLink "/commit") "CommitSignVerification" $commit.Verification}}
+ </span>
<span class="message tw-inline-block gt-ellipsis">
- <span>{{ctx.RenderUtils.RenderCommitMessage $commit.Subject ($.Repository.ComposeMetas ctx)}}</span>
+ {{ctx.RenderUtils.RenderCommitMessage $commit.Subject $.Repository}}
</span>
<span class="commit-refs flex-text-inline">
@@ -17,18 +20,18 @@
{{if eq $refGroup "pull"}}
{{if or (not $.HidePRRefs) (SliceUtils.Contains $.SelectedBranches .Name)}}
<!-- it's intended to use issues not pulls, if it's a pull you will get redirected -->
- <a class="ui labelled basic tiny button" href="{{$.RepoLink}}/{{if $.Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypePullRequests}}pulls{{else}}issues{{end}}/{{.ShortName|PathEscape}}">
+ <a class="ui basic tiny button" href="{{$.RepoLink}}/{{if $.Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypePullRequests}}pulls{{else}}issues{{end}}/{{.ShortName|PathEscape}}">
{{svg "octicon-git-pull-request"}} #{{.ShortName}}
</a>
{{end}}
{{else if eq $refGroup "tags"}}
- {{- template "repo/tag/name" dict "RepoLink" $.Repository.Link "TagName" .ShortName -}}
+ {{- template "repo/tag/name" dict "AdditionalClasses" "tag-label" "RepoLink" $.Repository.Link "TagName" .ShortName -}}
{{else if eq $refGroup "remotes"}}
- <a class="ui labelled basic tiny button" href="{{$.RepoLink}}/src/commit/{{$commit.Rev|PathEscape}}">
+ <a class="ui basic tiny button" href="{{$.RepoLink}}/src/commit/{{$commit.Rev|PathEscape}}">
{{svg "octicon-cross-reference"}} {{.ShortName}}
</a>
{{else if eq $refGroup "heads"}}
- <a class="ui labelled basic tiny button" href="{{$.RepoLink}}/src/branch/{{.ShortName|PathEscape}}">
+ <a class="ui basic tiny button" href="{{$.RepoLink}}/src/branch/{{.ShortName|PathEscape}}">
{{svg "octicon-git-branch"}} {{.ShortName}}
</a>
{{else}}
diff --git a/templates/repo/graph/div.tmpl b/templates/repo/graph/div.tmpl
index c0bd4e269a..12c2869cd8 100644
--- a/templates/repo/graph/div.tmpl
+++ b/templates/repo/graph/div.tmpl
@@ -1,7 +1,3 @@
-<div>
- {{template "repo/graph/svgcontainer" .}}
- {{template "repo/graph/commits" .}}
- <div id="pagination">
- {{template "base/paginate" .}}
- </div>
-</div>
+{{template "repo/graph/svgcontainer" .}}
+{{template "repo/graph/commits" .}}
+{{template "base/paginate" .}}
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index c3ae697f31..b61076ff46 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -25,6 +25,9 @@
<div class="repo-icon only-mobile" data-tooltip-content="{{ctx.Locale.Tr "repo.desc.internal"}}">{{svg "octicon-shield-lock" 18}}</div>
{{end}}
{{end}}
+ {{if $.Permission.HasAnyUnitPublicAccess}}
+ <span class="ui basic orange label">{{ctx.Locale.Tr "repo.desc.public_access"}}</span>
+ {{end}}
{{if .IsTemplate}}
<span class="ui basic label not-mobile">{{ctx.Locale.Tr "repo.desc.template"}}</span>
<div class="repo-icon only-mobile" data-tooltip-content="{{ctx.Locale.Tr "repo.desc.template"}}">{{svg "octicon-repo-template" 18}}</div>
@@ -35,20 +38,20 @@
</div>
</div>
{{if not (or .IsBeingCreated .IsBroken)}}
- <div class="repo-buttons">
+ <div class="flex-text-block tw-flex-wrap">
{{if $.RepoTransfer}}
<form method="post" action="{{$.RepoLink}}/action/accept_transfer?redirect_to={{$.RepoLink}}">
{{$.CsrfTokenHtml}}
- <div data-tooltip-content="{{if $.CanUserAcceptTransfer}}{{ctx.Locale.Tr "repo.transfer.accept_desc" $.RepoTransfer.Recipient.DisplayName}}{{else}}{{ctx.Locale.Tr "repo.transfer.no_permission_to_accept"}}{{end}}">
- <button type="submit" class="ui basic button {{if $.CanUserAcceptTransfer}}primary {{end}} ok small"{{if not $.CanUserAcceptTransfer}} disabled{{end}}>
+ <div class="flex-text-inline" data-tooltip-content="{{if $.CanUserAcceptOrRejectTransfer}}{{ctx.Locale.Tr "repo.transfer.accept_desc" $.RepoTransfer.Recipient.DisplayName}}{{else}}{{ctx.Locale.Tr "repo.transfer.no_permission_to_accept"}}{{end}}">
+ <button type="submit" class="ui compact small basic button {{if $.CanUserAcceptOrRejectTransfer}}primary {{end}} ok small"{{if not $.CanUserAcceptOrRejectTransfer}} disabled{{end}}>
{{ctx.Locale.Tr "repo.transfer.accept"}}
</button>
</div>
</form>
<form method="post" action="{{$.RepoLink}}/action/reject_transfer?redirect_to={{$.RepoLink}}">
{{$.CsrfTokenHtml}}
- <div data-tooltip-content="{{if $.CanUserAcceptTransfer}}{{ctx.Locale.Tr "repo.transfer.reject_desc" $.RepoTransfer.Recipient.DisplayName}}{{else}}{{ctx.Locale.Tr "repo.transfer.no_permission_to_reject"}}{{end}}">
- <button type="submit" class="ui basic button {{if $.CanUserAcceptTransfer}}red {{end}}ok small"{{if not $.CanUserAcceptTransfer}} disabled{{end}}>
+ <div class="flex-text-inline" data-tooltip-content="{{if $.CanUserAcceptOrRejectTransfer}}{{ctx.Locale.Tr "repo.transfer.reject_desc" $.RepoTransfer.Recipient.DisplayName}}{{else}}{{ctx.Locale.Tr "repo.transfer.no_permission_to_reject"}}{{end}}">
+ <button type="submit" class="ui compact small basic button {{if $.CanUserAcceptOrRejectTransfer}}red {{end}}ok small"{{if not $.CanUserAcceptOrRejectTransfer}} disabled{{end}}>
{{ctx.Locale.Tr "repo.transfer.reject"}}
</button>
</div>
@@ -133,7 +136,7 @@
{{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}}
<div class="overflow-menu-items">
{{if .Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
- <a class="{{if .PageIsViewCode}}active {{end}}item" href="{{.RepoLink}}{{if and (ne .BranchName .Repository.DefaultBranch) (not $.PageIsWiki)}}/src/{{.BranchNameSubURL}}{{end}}">
+ <a class="{{if .PageIsViewCode}}active {{end}}item" href="{{.RepoLink}}{{if and (ne .BranchName .Repository.DefaultBranch) (not $.PageIsWiki)}}/src/{{.RefTypeNameSubURL}}{{end}}">
{{svg "octicon-code"}} {{ctx.Locale.Tr "repo.code"}}
</a>
{{end}}
@@ -162,7 +165,7 @@
</a>
{{end}}
- {{if and .EnableActions (.Permission.CanRead ctx.Consts.RepoUnitTypeActions)}}
+ {{if and .EnableActions (.Permission.CanRead ctx.Consts.RepoUnitTypeActions) (not .IsEmptyRepo)}}
<a class="{{if .PageIsActions}}active {{end}}item" href="{{.RepoLink}}/actions">
{{svg "octicon-play"}} {{ctx.Locale.Tr "actions.actions"}}
{{if .Repository.NumOpenActionRuns}}
@@ -208,7 +211,7 @@
</a>
{{end}}
- {{if and (.Permission.CanReadAny ctx.Consts.RepoUnitTypePullRequests ctx.Consts.RepoUnitTypeIssues ctx.Consts.RepoUnitTypeReleases) (not .IsEmptyRepo)}}
+ {{if and (.Permission.CanReadAny ctx.Consts.RepoUnitTypePullRequests ctx.Consts.RepoUnitTypeIssues ctx.Consts.RepoUnitTypeReleases ctx.Consts.RepoUnitTypeCode) (not .IsEmptyRepo)}}
<a class="{{if .PageIsActivity}}active {{end}}item" href="{{.RepoLink}}/activity">
{{svg "octicon-pulse"}} {{ctx.Locale.Tr "repo.activity"}}
</a>
@@ -223,11 +226,19 @@
</a>
{{end}}
</div>
- {{else if .Permission.IsAdmin}}
+ {{else}}
<div class="overflow-menu-items">
+ {{if(and .Repository.IsBeingCreated (.Permission.CanRead ctx.Consts.RepoUnitTypeCode))}}
+ <a class="{{if not .PageIsRepoSettings}}active {{end}}item" href="{{.RepoLink}}">
+ {{svg "octicon-clock"}} {{ctx.Locale.Tr "repo.migration_status"}}
+ </a>
+ {{end}}
+
+ {{if .Permission.IsAdmin}}
<a class="{{if .PageIsRepoSettings}}active {{end}} item" href="{{.RepoLink}}/settings">
{{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}}
</a>
+ {{end}}
</div>
{{end}}
</overflow-menu>
diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index d73b7470bc..f86b90502d 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -1,7 +1,8 @@
{{template "base/head" .}}
+{{$showSidebar := and (not .TreeNames) (not .HideRepoInfo) (not .IsBlame)}}
<div role="main" aria-label="{{.Title}}" class="page-content repository file list {{if .IsBlame}}blame{{end}}">
{{template "repo/header" .}}
- <div class="ui container {{if .IsBlame}}fluid padded{{end}}">
+ <div class="ui container {{if or .TreeNames .IsBlame}}fluid padded{{end}}">
{{template "base/alert" .}}
{{if .Repository.IsArchived}}
@@ -16,117 +17,9 @@
{{template "repo/code/recently_pushed_new_branches" .}}
- {{$treeNamesLen := len .TreeNames}}
- {{$isTreePathRoot := eq $treeNamesLen 0}}
- {{$showSidebar := and $isTreePathRoot (not .HideRepoInfo) (not .IsBlame)}}
<div class="{{Iif $showSidebar "repo-grid-filelist-sidebar" "repo-grid-filelist-only"}}">
<div class="repo-home-filelist">
- {{template "repo/sub_menu" .}}
- <div class="repo-button-row">
- <div class="repo-button-row-left">
- {{$branchDropdownCurrentRefType := "branch"}}
- {{$branchDropdownCurrentRefShortName := .BranchName}}
- {{if .IsViewTag}}
- {{$branchDropdownCurrentRefType = "tag"}}
- {{$branchDropdownCurrentRefShortName = .TagName}}
- {{end}}
- {{template "repo/branch_dropdown" dict
- "Repository" .Repository
- "ShowTabBranches" true
- "ShowTabTags" true
- "CurrentRefType" $branchDropdownCurrentRefType
- "CurrentRefShortName" $branchDropdownCurrentRefShortName
- "CurrentTreePath" .TreePath
- "RefLinkTemplate" "{RepoLink}/src/{RefType}/{RefShortName}/{TreePath}"
- "AllowCreateNewRef" .CanCreateBranch
- "ShowViewAllRefsEntry" true
- }}
- {{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}}
- {{$cmpBranch := ""}}
- {{if ne .Repository.ID .BaseRepo.ID}}
- {{$cmpBranch = printf "%s/%s:" (.Repository.OwnerName|PathEscape) (.Repository.Name|PathEscape)}}
- {{end}}
- {{$cmpBranch = print $cmpBranch (.BranchName|PathEscapeSegments)}}
- {{$compareLink := printf "%s/compare/%s...%s" .BaseRepo.Link (.BaseRepo.DefaultBranch|PathEscapeSegments) $cmpBranch}}
- <a id="new-pull-request" role="button" class="ui compact basic button" href="{{$compareLink}}"
- data-tooltip-content="{{if .PullRequestCtx.Allowed}}{{ctx.Locale.Tr "repo.pulls.compare_changes"}}{{else}}{{ctx.Locale.Tr "action.compare_branch"}}{{end}}">
- {{svg "octicon-git-pull-request"}}
- </a>
- {{end}}
-
- <!-- Show go to file if on home page -->
- {{if $isTreePathRoot}}
- <a href="{{.Repository.Link}}/find/{{.BranchNameSubURL}}" class="ui compact basic button">{{ctx.Locale.Tr "repo.find_file.go_to_file"}}</a>
- {{end}}
-
- {{if and .CanWriteCode .IsViewBranch (not .Repository.IsMirror) (not .Repository.IsArchived) (not .IsViewFile)}}
- <button class="ui dropdown basic compact jump button"{{if not .Repository.CanEnableEditor}} disabled{{end}}>
- {{ctx.Locale.Tr "repo.editor.add_file"}}
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- <div class="menu">
- <a class="item" href="{{.RepoLink}}/_new/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">
- {{ctx.Locale.Tr "repo.editor.new_file"}}
- </a>
- {{if .RepositoryUploadEnabled}}
- <a class="item" href="{{.RepoLink}}/_upload/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">
- {{ctx.Locale.Tr "repo.editor.upload_file"}}
- </a>
- {{end}}
- <a class="item" href="{{.RepoLink}}/_diffpatch/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">
- {{ctx.Locale.Tr "repo.editor.patch"}}
- </a>
- </div>
- </button>
- {{end}}
-
- {{if and $isTreePathRoot .Repository.IsTemplate}}
- <a role="button" class="ui primary compact button" href="{{AppSubUrl}}/repo/create?template_id={{.Repository.ID}}">
- {{ctx.Locale.Tr "repo.use_template"}}
- </a>
- {{end}}
-
- {{if not $isTreePathRoot}}
- {{$treeNameIdxLast := Eval $treeNamesLen "-" 1}}
- <span class="breadcrumb repo-path tw-ml-1">
- <a class="section" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}" title="{{.Repository.Name}}">{{StringUtils.EllipsisString .Repository.Name 30}}</a>
- {{- range $i, $v := .TreeNames -}}
- <span class="breadcrumb-divider">/</span>
- {{- if eq $i $treeNameIdxLast -}}
- <span class="active section" title="{{$v}}">{{$v}}</span>
- <button class="btn interact-fg tw-mx-1" data-clipboard-text="{{$.TreePath}}" data-tooltip-content="{{ctx.Locale.Tr "copy_path"}}">{{svg "octicon-copy" 14}}</button>
- {{- else -}}
- {{$p := index $.Paths $i}}<span class="section"><a href="{{$.BranchLink}}/{{PathEscapeSegments $p}}" title="{{$v}}">{{$v}}</a></span>
- {{- end -}}
- {{- end -}}
- </span>
- {{end}}
- </div>
-
- <div class="repo-button-row-right">
- <!-- Only show clone panel in repository home page -->
- {{if $isTreePathRoot}}
- {{template "repo/clone_panel" .}}
- {{end}}
- {{if and (not $isTreePathRoot) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}}
- <a class="ui button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">
- {{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}}
- </a>
- {{end}}
- </div>
- </div>
- {{if .IsViewFile}}
- {{template "repo/view_file" .}}
- {{else if .IsBlame}}
- {{template "repo/blame" .}}
- {{else}}{{/* IsViewDirectory */}}
- {{if $isTreePathRoot}}
- {{template "repo/code/upstream_diverging_info" .}}
- {{end}}
- {{template "repo/view_list" .}}
- {{if and .ReadmeExist (or .IsMarkup .IsPlainText)}}
- {{template "repo/view_file" .}}
- {{end}}
- {{end}}
+ {{template "repo/view_content" .}}
</div>
{{if $showSidebar}}
diff --git a/templates/repo/home_sidebar_bottom.tmpl b/templates/repo/home_sidebar_bottom.tmpl
index f780dc122d..01e630ccbf 100644
--- a/templates/repo/home_sidebar_bottom.tmpl
+++ b/templates/repo/home_sidebar_bottom.tmpl
@@ -4,14 +4,14 @@
<div class="flex-item">
<div class="flex-item-main">
<div class="flex-item-title">
- <a class="item muted" href="{{.Link}}/releases">
+ <a class="item muted" href="{{.RepoLink}}/releases">
{{ctx.Locale.Tr "repo.releases"}}
<span class="ui small label">{{.NumReleases}}</span>
</a>
</div>
<div class="flex-item">
- <div class="flex-item-icon">
- {{svg "octicon-tag" 16}}
+ <div class="flex-item-leading">
+ <div class="tw-mt-1">{{svg "octicon-tag" 16}}</div>
</div>
<div class="flex-item-main">
<div class="flex-item-header">
diff --git a/templates/repo/home_sidebar_top.tmpl b/templates/repo/home_sidebar_top.tmpl
index 607dc62e2e..8c2089c839 100644
--- a/templates/repo/home_sidebar_top.tmpl
+++ b/templates/repo/home_sidebar_top.tmpl
@@ -1,5 +1,5 @@
<div class="repo-home-sidebar-top">
- <form class="ignore-dirty tw-flex tw-flex-1 tw-mt-1" action="{{.RepoLink}}/search" method="get">
+ <form class="ignore-dirty tw-flex tw-flex-1" action="{{.RepoLink}}/search" method="get">
<div class="ui small action input tw-flex-1">
<input name="q" size="10" placeholder="{{ctx.Locale.Tr "search.code_kind"}}"> {{template "shared/search/button"}}
</div>
@@ -45,7 +45,7 @@
{{end}}
{{if .ReadmeExist}}
- <a class="flex-text-block muted" href="{{.TreeLink}}/{{.FileName}}">
+ <a class="flex-text-block muted" href="{{.RepoLink}}/src/{{.RefTypeNameSubURL}}/{{PathEscapeSegments .FileTreePath}}">
{{svg "octicon-book"}} {{ctx.Locale.Tr "readme"}}
</a>
{{end}}
@@ -62,6 +62,11 @@
{{svg "octicon-cross-reference"}} {{ctx.Locale.Tr "repo.cite_this_repo"}}
</a>
{{end}}
+ <span class="flex-text-block muted" {{if not (eq .Repository.Size 0)}}data-tooltip-placement="top" data-tooltip-content="{{.Repository.SizeDetailsString}}"{{end}}>
+ {{$fileSizeFormatted := FileSize .Repository.Size}}{{/* the formatted string is always "{val} {unit}" */}}
+ {{$fileSizeFields := StringUtils.Split $fileSizeFormatted " "}}
+ {{svg "octicon-database"}} <b>{{ctx.Locale.PrettyNumber (index $fileSizeFields 0)}}</b> {{index $fileSizeFields 1}}
+ </span>
</div>
</div>
</div>
diff --git a/templates/repo/icon.tmpl b/templates/repo/icon.tmpl
index e5e0bd68e7..e4a904c46b 100644
--- a/templates/repo/icon.tmpl
+++ b/templates/repo/icon.tmpl
@@ -1,6 +1,6 @@
{{$avatarLink := (.RelAvatarLink ctx)}}
{{if $avatarLink}}
- <img class="ui avatar tw-align-middle" src="{{$avatarLink}}" width="24" height="24" alt="{{.FullName}}">
+ <img class="ui avatar tw-align-middle" src="{{$avatarLink}}" width="24" height="24" alt aria-hidden="true">
{{else if $.IsMirror}}
{{svg "octicon-mirror" 24}}
{{else if $.IsFork}}
diff --git a/templates/repo/issue/branch_selector_field.tmpl b/templates/repo/issue/branch_selector_field.tmpl
index 9183b7b46a..c8b67490ac 100644
--- a/templates/repo/issue/branch_selector_field.tmpl
+++ b/templates/repo/issue/branch_selector_field.tmpl
@@ -14,7 +14,7 @@ Still needs to figure out:
*/}}
{{if and (not .Issue.IsPull) (not .PageIsComparePull)}}
<input id="ref_selector" name="ref" type="hidden" value="{{.Reference}}">
-<div class="ui dropdown select-branch branch-selector-dropdown ellipsis-items-nowrap {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}}"
+<div class="ui dropdown select-branch branch-selector-dropdown ellipsis-text-items {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}}"
data-no-results="{{ctx.Locale.Tr "no_results_found"}}"
{{if and .Issue (or .IsIssueWriter .HasIssuesOrPullsWritePermission)}}data-url-update-issueref="{{$.RepoLink}}/issues/{{.Issue.Index}}/ref"{{end}}
>
diff --git a/templates/repo/issue/card.tmpl b/templates/repo/issue/card.tmpl
index 2e19e86d7a..6dae49c455 100644
--- a/templates/repo/issue/card.tmpl
+++ b/templates/repo/issue/card.tmpl
@@ -4,7 +4,7 @@
{{if $attachments}}
<div class="card-attachment-images">
{{range $attachments}}
- <img src="{{.DownloadURL}}" alt="{{.Name}}" />
+ <img loading="lazy" src="{{.DownloadURL}}" alt="{{.Name}}" />
{{end}}
</div>
{{end}}
@@ -16,7 +16,7 @@
</div>
<a class="issue-card-title muted issue-title tw-break-anywhere" href="{{.Link}}">{{.Title | ctx.RenderUtils.RenderIssueSimpleTitle}}</a>
{{if and $.isPinnedIssueCard $.Page.IsRepoAdmin}}
- <a role="button" class="issue-card-unpin muted tw-flex tw-items-center" data-tooltip-content={{ctx.Locale.Tr "repo.issues.unpin_issue"}} data-issue-id="{{.ID}}" data-unpin-url="{{$.Page.Link}}/unpin/{{.Index}}">
+ <a role="button" class="issue-card-unpin muted tw-flex tw-items-center" data-tooltip-content={{ctx.Locale.Tr "repo.issues.unpin"}} data-issue-id="{{.ID}}" data-unpin-url="{{$.Page.Link}}/unpin/{{.Index}}">
{{svg "octicon-x" 16}}
</a>
{{end}}
@@ -45,7 +45,7 @@
{{if $.Page.LinkedPRs}}
{{range index $.Page.LinkedPRs .ID}}
<div class="meta tw-my-1">
- <a href="{{$.Issue.Repo.Link}}/pulls/{{.Index}}">
+ <a href="{{.Repo.Link}}/pulls/{{.Index}}">
<span class="tw-m-0 text {{if .PullRequest.HasMerged}}purple{{else if .IsClosed}}red{{else}}green{{end}}">{{svg "octicon-git-merge" 16 "tw-mr-1 tw-align-middle"}}</span>
<span class="tw-align-middle">{{.Title}} <span class="text light grey">#{{.Index}}</span></span>
</a>
@@ -63,16 +63,21 @@
{{if or .Labels .Assignees}}
<div class="issue-card-bottom">
- <div class="labels-list">
- {{range .Labels}}
- <a target="_blank" href="{{$.Issue.Repo.Link}}/issues?labels={{.ID}}">{{ctx.RenderUtils.RenderLabel .}}</a>
+ {{/* the labels container must always be present, to help the assignees list right-aligned */}}
+ <div class="issue-card-bottom-part labels-list">
+ {{range $label := .Labels}}
+ {{$link := print $.Issue.Repo.Link "/issues"}}
+ {{$link = QueryBuild $link "labels" $label.ID}}
+ {{ctx.RenderUtils.RenderLabelWithLink $label $link}}
{{end}}
</div>
- <div class="issue-card-assignees">
+ {{if .Assignees}}
+ <div class="issue-card-bottom-part tw-justify-end">
{{range .Assignees}}
- <a target="_blank" href="{{.HomeLink}}" data-tooltip-content="{{ctx.Locale.Tr "repo.projects.column.assigned_to"}} {{.Name}}">{{ctx.AvatarUtils.Avatar . 28}}</a>
+ <a target="_blank" href="{{.HomeLink}}" data-tooltip-content="{{ctx.Locale.Tr "repo.projects.column.assigned_to"}} {{.Name}}">{{ctx.AvatarUtils.Avatar . 24}}</a>
{{end}}
</div>
+ {{end}}
</div>
{{end}}
{{end}}
diff --git a/templates/repo/issue/choose.tmpl b/templates/repo/issue/choose.tmpl
index 38cf9e485f..e3314f4260 100644
--- a/templates/repo/issue/choose.tmpl
+++ b/templates/repo/issue/choose.tmpl
@@ -10,11 +10,11 @@
{{range .IssueTemplates}}
<div class="ui attached segment">
<div class="ui two column grid">
- <div class="column left aligned">
+ <div class="column">
<strong>{{.Name}}</strong>
<br>{{.About}}
</div>
- <div class="column right aligned">
+ <div class="column tw-text-right">
<a href="{{$.RepoLink}}/issues/new?template={{.FileName}}{{if $.milestone}}&milestone={{$.milestone}}{{end}}{{if $.project}}&project={{$.project}}{{end}}" class="ui primary button">{{ctx.Locale.Tr "repo.issues.choose.get_started"}}</a>
</div>
</div>
@@ -23,11 +23,11 @@
{{range .IssueConfig.ContactLinks}}
<div class="ui attached segment">
<div class="ui two column grid">
- <div class="column left aligned">
+ <div class="column">
<strong>{{.Name}}</strong>
<br>{{.About}}
</div>
- <div class="column right aligned">
+ <div class="column tw-text-right">
<a href="{{.URL}}" class="ui primary button">{{svg "octicon-link-external"}} {{ctx.Locale.Tr "repo.issues.choose.open_external_link"}}</a>
</div>
</div>
@@ -36,11 +36,11 @@
{{if .IssueConfig.BlankIssuesEnabled}}
<div class="ui attached segment">
<div class="ui two column grid">
- <div class="column left aligned">
+ <div class="column">
<strong>{{ctx.Locale.Tr "repo.issues.choose.blank"}}</strong>
<br/>{{ctx.Locale.Tr "repo.issues.choose.blank_about"}}
</div>
- <div class="column right aligned">
+ <div class="column tw-text-right">
<a href="{{.RepoLink}}/issues/new?{{if .milestone}}&milestone={{.milestone}}{{end}}{{if $.project}}&project={{$.project}}{{end}}" class="ui primary button">{{ctx.Locale.Tr "repo.issues.choose.get_started"}}</a>
</div>
</div>
diff --git a/templates/repo/issue/fields/markdown.tmpl b/templates/repo/issue/fields/markdown.tmpl
index da8f5e6bdf..dbf4b71ba8 100644
--- a/templates/repo/issue/fields/markdown.tmpl
+++ b/templates/repo/issue/fields/markdown.tmpl
@@ -1,3 +1,3 @@
<div class="field {{if not .item.VisibleOnForm}}tw-hidden{{end}}">
- <div class="markup">{{ctx.RenderUtils.MarkdownToHtml .item.Attributes.value}}</div>
+ <div class="render-content markup">{{ctx.RenderUtils.MarkdownToHtml .item.Attributes.value}}</div>
</div>
diff --git a/templates/repo/issue/filter_item_label.tmpl b/templates/repo/issue/filter_item_label.tmpl
index 0883d93804..04c3605a6a 100644
--- a/templates/repo/issue/filter_item_label.tmpl
+++ b/templates/repo/issue/filter_item_label.tmpl
@@ -22,7 +22,7 @@
<span class="label-filter-exclude-info">{{ctx.Locale.Tr "repo.issues.filter_label_exclude"}}</span>
<div class="divider"></div>
<a class="item label-filter-query-default" href="{{QueryBuild $queryLink "labels" NIL}}">{{ctx.Locale.Tr "repo.issues.filter_label_no_select"}}</a>
- <a class="item label-filter-query-not-set" href="{{QueryBuild $queryLink "labels" 0}}">{{ctx.Locale.Tr "repo.issues.filter_label_select_no_label"}}</a>
+ <a class="item label-filter-query-not-set" href="{{QueryBuild $queryLink "labels" "0"}}">{{ctx.Locale.Tr "repo.issues.filter_label_select_no_label"}}</a>
{{/* The logic here is not the same as the label selector in the issue sidebar.
The one in the issue sidebar renders "repo labels | divider | org labels".
Maybe the logic should be updated to be consistent.*/}}
diff --git a/templates/repo/issue/filter_item_user_assign.tmpl b/templates/repo/issue/filter_item_user_assign.tmpl
index 4f1db71d57..42886edaa0 100644
--- a/templates/repo/issue/filter_item_user_assign.tmpl
+++ b/templates/repo/issue/filter_item_user_assign.tmpl
@@ -4,8 +4,8 @@
* UserSearchList
* SelectedUserId: 0 or empty means default, -1 means "no user is set"
* TextFilterTitle
-* TextZeroValue: the text for "all issues"
-* TextNegativeOne: the text for "issues with no assignee"
+* TextFilterMatchNone: the text for "issues with no assignee"
+* TextFilterMatchAny: the text for "issues with any assignee"
*/}}
{{$queryLink := .QueryLink}}
<div class="item ui dropdown jump {{if not .UserSearchList}}disabled{{end}}">
@@ -15,16 +15,24 @@
<i class="icon">{{svg "octicon-search" 16}}</i>
<input type="text" placeholder="{{ctx.Locale.Tr "repo.issues.filter_user_placeholder"}}">
</div>
- {{if $.TextZeroValue}}
- <a class="item {{if not .SelectedUserId}}selected{{end}}" href="{{QueryBuild $queryLink $.QueryParamKey NIL}}">{{$.TextZeroValue}}</a>
+ {{if $.TextFilterMatchNone}}
+ {{$isSelected := eq .SelectedUserId "(none)"}}
+ <a class="item" href="{{QueryBuild $queryLink $.QueryParamKey (Iif $isSelected NIL "(none)")}}">
+ {{svg "octicon-check" 14 (Iif $isSelected "" "tw-invisible")}} {{$.TextFilterMatchNone}}
+ </a>
{{end}}
- {{if $.TextNegativeOne}}
- <a class="item {{if eq .SelectedUserId -1}}selected{{end}}" href="{{QueryBuild $queryLink $.QueryParamKey -1}}">{{$.TextNegativeOne}}</a>
+ {{if $.TextFilterMatchAny}}
+ {{$isSelected := eq .SelectedUserId "(any)"}}
+ <a class="item" href="{{QueryBuild $queryLink $.QueryParamKey (Iif $isSelected NIL "(any)")}}">
+ {{svg "octicon-check" 14 (Iif $isSelected "" "tw-invisible")}} {{$.TextFilterMatchAny}}
+ </a>
{{end}}
<div class="divider"></div>
- {{range .UserSearchList}}
- <a class="item {{if eq $.SelectedUserId .ID}}selected{{end}}" href="{{QueryBuild $queryLink $.QueryParamKey .ID}}">
- {{ctx.AvatarUtils.Avatar . 20}}{{template "repo/search_name" .}}
+ {{range $user := .UserSearchList}}
+ {{$isSelected := eq $.SelectedUserId (print $user.ID)}}
+ <a class="item" href="{{QueryBuild $queryLink $.QueryParamKey (Iif $isSelected NIL $user.ID)}}">
+ {{svg "octicon-check" 14 (Iif $isSelected "" "tw-invisible")}}
+ {{ctx.AvatarUtils.Avatar $user 20}}{{template "repo/search_name" .}}
</a>
{{end}}
</div>
diff --git a/templates/repo/issue/filter_list.tmpl b/templates/repo/issue/filter_list.tmpl
index 7612d93b21..bfdf94513e 100644
--- a/templates/repo/issue/filter_list.tmpl
+++ b/templates/repo/issue/filter_list.tmpl
@@ -15,7 +15,7 @@
<input type="text" placeholder="{{ctx.Locale.Tr "repo.issues.filter_milestone"}}">
</div>
<div class="divider"></div>
- <a class="{{if not $.MilestoneID}}active selected {{end}}item" href="{{QueryBuild $queryLink "milestone" 0}}">{{ctx.Locale.Tr "repo.issues.filter_milestone_all"}}</a>
+ <a class="{{if not $.MilestoneID}}active selected {{end}}item" href="{{QueryBuild $queryLink "milestone" NIL}}">{{ctx.Locale.Tr "repo.issues.filter_milestone_all"}}</a>
<a class="{{if $.MilestoneID}}{{if eq $.MilestoneID -1}}active selected {{end}}{{end}}item" href="{{QueryBuild $queryLink "milestone" -1}}">{{ctx.Locale.Tr "repo.issues.filter_milestone_none"}}</a>
{{if .OpenMilestones}}
<div class="divider"></div>
@@ -94,8 +94,8 @@
"UserSearchList" $.Assignees
"SelectedUserId" $.AssigneeID
"TextFilterTitle" (ctx.Locale.Tr "repo.issues.filter_assignee")
- "TextZeroValue" (ctx.Locale.Tr "repo.issues.filter_assginee_no_select")
- "TextNegativeOne" (ctx.Locale.Tr "repo.issues.filter_assginee_no_assignee")
+ "TextFilterMatchNone" (ctx.Locale.Tr "repo.issues.filter_assignee_no_assignee")
+ "TextFilterMatchAny" (ctx.Locale.Tr "repo.issues.filter_assignee_any_assignee")
}}
{{if .IsSigned}}
@@ -106,7 +106,7 @@
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
- <a class="{{if eq .ViewType "all"}}active {{end}}item" href="{{QueryBuild $queryLink "type" "all"}}">{{ctx.Locale.Tr "repo.issues.filter_type.all_issues"}}</a>
+ <a class="{{if eq .ViewType "all"}}active {{end}}item" href="{{QueryBuild $queryLink "type" "all"}}">{{if .PageIsPullList}}{{ctx.Locale.Tr "repo.issues.filter_type.all_pull_requests"}}{{else}}{{ctx.Locale.Tr "repo.issues.filter_type.all_issues"}}{{end}}</a>
<a class="{{if eq .ViewType "assigned"}}active {{end}}item" href="{{QueryBuild $queryLink "type" "assigned"}}">{{ctx.Locale.Tr "repo.issues.filter_type.assigned_to_you"}}</a>
<a class="{{if eq .ViewType "created_by"}}active {{end}}item" href="{{QueryBuild $queryLink "type" "created_by"}}">{{ctx.Locale.Tr "repo.issues.filter_type.created_by_you"}}</a>
{{if .PageIsPullList}}
@@ -133,5 +133,11 @@
<a class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="{{QueryBuild $queryLink "sort" "leastcomment"}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastcomment"}}</a>
<a class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="{{QueryBuild $queryLink "sort" "nearduedate"}}">{{ctx.Locale.Tr "repo.issues.filter_sort.nearduedate"}}</a>
<a class="{{if eq .SortType "farduedate"}}active {{end}}item" href="{{QueryBuild $queryLink "sort" "farduedate"}}">{{ctx.Locale.Tr "repo.issues.filter_sort.farduedate"}}</a>
+ <div class="divider"></div>
+ <div class="header">{{ctx.Locale.Tr "repo.issues.filter_label"}}</div>
+ {{range $scope := .ExclusiveLabelScopes}}
+ {{$sortType := (printf "scope-%s" $scope)}}
+ <a class="{{if eq $.SortType $sortType}}active {{end}}item" href="{{QueryBuild $queryLink "sort" $sortType}}">{{$scope}}</a>
+ {{end}}
</div>
</div>
diff --git a/templates/repo/issue/filters.tmpl b/templates/repo/issue/filters.tmpl
index 06e7c1aa6c..409ec876e6 100644
--- a/templates/repo/issue/filters.tmpl
+++ b/templates/repo/issue/filters.tmpl
@@ -9,7 +9,7 @@
<div class="ui compact tiny secondary menu">
<span class="item" data-tooltip-content='{{ctx.Locale.Tr "tracked_time_summary"}}'>
{{svg "octicon-clock"}}
- {{.TotalTrackedTime | Sec2Time}}
+ {{.TotalTrackedTime | Sec2Hour}}
</span>
</div>
{{end}}
diff --git a/templates/repo/issue/labels/label_edit_modal.tmpl b/templates/repo/issue/labels/label_edit_modal.tmpl
index 527b7ff900..6837d66dce 100644
--- a/templates/repo/issue/labels/label_edit_modal.tmpl
+++ b/templates/repo/issue/labels/label_edit_modal.tmpl
@@ -5,7 +5,7 @@
>
<div class="header"></div>
<div class="content">
- <form class="ui form ignore-dirty" method="post">
+ <form class="ui form ignore-dirty form-fetch-action" method="post">
{{.CsrfTokenHtml}}
<input name="id" type="hidden">
<div class="required field">
@@ -24,7 +24,13 @@
<div class="desc tw-ml-1 tw-mt-2 tw-hidden label-exclusive-warning">
{{svg "octicon-alert"}} {{ctx.Locale.Tr "repo.issues.label_exclusive_warning"}}
</div>
- <br>
+ <div class="field label-exclusive-order-input-field tw-mt-2">
+ <label class="flex-text-block">
+ {{ctx.Locale.Tr "repo.issues.label_exclusive_order"}}
+ <span data-tooltip-content="{{ctx.Locale.Tr "repo.issues.label_exclusive_order_tooltip"}}">{{svg "octicon-info"}}</span>
+ </label>
+ <input class="label-exclusive-order-input" name="exclusive_order" type="number" maxlength="4">
+ </div>
</div>
<div class="field label-is-archived-input-field">
<div class="ui checkbox">
@@ -44,7 +50,8 @@
<div class="field">
<label for="color">{{ctx.Locale.Tr "repo.issues.label_color"}}</label>
<div class="column js-color-picker-input">
- <input name="color" value="#70c24a"placeholder="#c320f6" required maxlength="7">
+ <!-- the "#" is optional because backend NormalizeColor is able to handle it, API also accepts both formats, and it is easier for users to directly copy-paste a hex value -->
+ <input name="color" value="#70c24a" placeholder="#c320f6" required pattern="^#?([\dA-Fa-f]{3}|[\dA-Fa-f]{6})$" maxlength="7">
{{template "repo/issue/label_precolors"}}
</div>
</div>
diff --git a/templates/repo/issue/labels/label_list.tmpl b/templates/repo/issue/labels/label_list.tmpl
index 822567301e..c8a6b46cc4 100644
--- a/templates/repo/issue/labels/label_list.tmpl
+++ b/templates/repo/issue/labels/label_list.tmpl
@@ -26,7 +26,7 @@
<div class="divider"></div>
{{end}}
- <ul class="issue-label-list">
+ <ul class="issue-label-list muted-links">
{{$canEditLabel := and (not $.PageIsOrgSettingsLabels) (not $.Repository.IsArchived) (or $.CanWriteIssues $.CanWritePulls)}}
{{$canEditLabel = or $canEditLabel $.PageIsOrgSettingsLabels}}
{{range .Labels}}
@@ -42,34 +42,31 @@
<a class="open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{ctx.Locale.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
{{end}}
</div>
- <div class="label-operation tw-flex">
+ <div class="label-operation">
{{template "repo/issue/labels/label_archived" .}}
- <div class="tw-flex tw-ml-auto">
{{if $canEditLabel}}
<a class="edit-label-button" href="#"
data-label-id="{{.ID}}" data-label-name="{{.Name}}" data-label-color="{{.Color}}"
data-label-exclusive="{{.Exclusive}}" data-label-is-archived="{{gt .ArchivedUnix 0}}"
data-label-num-issues="{{.NumIssues}}" data-label-description="{{.Description}}"
+ data-label-exclusive-order="{{.ExclusiveOrder}}"
>{{svg "octicon-pencil"}} {{ctx.Locale.Tr "repo.issues.label_edit"}}</a>
<a class="link-action" href="#" data-url="{{$.Link}}/delete?id={{.ID}}"
data-modal-confirm-header="{{ctx.Locale.Tr "repo.issues.label_deletion"}}"
data-modal-confirm-content="{{ctx.Locale.Tr "repo.issues.label_deletion_desc"}}"
>{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.issues.label_delete"}}</a>
{{end}}
- </div>
</div>
</li>
{{end}}
{{if and (not .PageIsOrgSettingsLabels) (.OrgLabels)}}
<li class="item">
- <div class="ui grid middle aligned">
- <div class="ten wide column">
- {{ctx.Locale.Tr "repo.org_labels_desc"}}
- {{if .IsOrganizationOwner}}
- <a href="{{.OrganizationLink}}/settings/labels">({{ctx.Locale.Tr "repo.org_labels_desc_manage"}})</a>:
- {{end}}
- </div>
+ <div>{{/* parent is flex, so use block here to keep sentence spaces */}}
+ {{ctx.Locale.Tr "repo.org_labels_desc"}}
+ {{if .IsOrganizationOwner}}
+ <a href="{{.OrganizationLink}}/settings/labels">({{ctx.Locale.Tr "repo.org_labels_desc_manage"}})</a>:
+ {{end}}
</div>
</li>
diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl
index 01b610b39d..0ab761e038 100644
--- a/templates/repo/issue/list.tmpl
+++ b/templates/repo/issue/list.tmpl
@@ -14,9 +14,10 @@
</div>
{{end}}
- <div class="list-header">
- {{template "repo/issue/navbar" .}}
+ <div class="list-header flex-text-block">
{{template "repo/issue/search" .}}
+ <a class="ui small button" href="{{.RepoLink}}/labels">{{ctx.Locale.Tr "repo.labels"}}</a>
+ <a class="ui small button" href="{{.RepoLink}}/milestones">{{ctx.Locale.Tr "repo.milestones"}}</a>
{{if not .Repository.IsArchived}}
{{if .PageIsIssueList}}
<a class="ui small primary button issue-list-new" href="{{.RepoLink}}/issues/new{{if .NewIssueChooseTemplate}}/choose{{end}}">{{ctx.Locale.Tr "repo.issues.new"}}</a>
@@ -40,7 +41,7 @@
<div class="ui compact tiny secondary menu">
<span class="item" data-tooltip-content='{{ctx.Locale.Tr "tracked_time_summary"}}'>
{{svg "octicon-clock"}}
- {{.TotalTrackedTime | Sec2Time}}
+ {{.TotalTrackedTime | Sec2Hour}}
</span>
</div>
{{end}}
diff --git a/templates/repo/issue/milestone_issues.tmpl b/templates/repo/issue/milestone_issues.tmpl
index 4fc6057117..591820080f 100644
--- a/templates/repo/issue/milestone_issues.tmpl
+++ b/templates/repo/issue/milestone_issues.tmpl
@@ -3,10 +3,10 @@
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
- <div class="tw-flex">
- <h1 class="tw-mb-2">{{.Milestone.Name}}</h1>
+ <div class="flex-text-block tw-flex-wrap tw-mb-2">
+ <h1 class="tw-flex-1 tw-m-0">{{.Milestone.Name}}</h1>
{{if not .Repository.IsArchived}}
- <div class="text right tw-flex-1">
+ <div>
{{if or .CanWriteIssues .CanWritePulls}}
{{if .Milestone.IsClosed}}
<a class="ui primary basic button link-action" href data-url="{{$.RepoLink}}/milestones/{{.MilestoneID}}/open">{{ctx.Locale.Tr "repo.milestones.open"}}
@@ -22,14 +22,14 @@
{{end}}
</div>
{{if .Milestone.RenderedContent}}
- <div class="markup content tw-mb-4">
+ <div class="render-content markup tw-mb-4">
{{.Milestone.RenderedContent}}
</div>
{{end}}
<div class="tw-flex tw-flex-col tw-gap-2">
<progress class="milestone-progress-big" value="{{.Milestone.Completeness}}" max="100"></progress>
- <div class="tw-flex tw-gap-4">
- <div classs="tw-flex tw-items-center">
+ <div class="flex-text-block tw-gap-4">
+ <div class="flex-text-inline">
{{$closedDate:= DateUtils.TimeSince .Milestone.ClosedDateUnix}}
{{if .IsClosed}}
{{svg "octicon-clock"}} {{ctx.Locale.Tr "repo.milestones.closed" $closedDate}}
@@ -46,11 +46,11 @@
{{end}}
{{end}}
</div>
- <div class="tw-mr-2">{{ctx.Locale.Tr "repo.milestones.completeness" .Milestone.Completeness}}</div>
+ <div>{{ctx.Locale.Tr "repo.milestones.completeness" .Milestone.Completeness}}</div>
{{if .TotalTrackedTime}}
<div data-tooltip-content='{{ctx.Locale.Tr "tracked_time_summary"}}'>
{{svg "octicon-clock"}}
- {{.TotalTrackedTime | Sec2Time}}
+ {{.TotalTrackedTime | Sec2Hour}}
</div>
{{end}}
</div>
diff --git a/templates/repo/issue/milestone_new.tmpl b/templates/repo/issue/milestone_new.tmpl
index 4809149a21..9bcced6f54 100644
--- a/templates/repo/issue/milestone_new.tmpl
+++ b/templates/repo/issue/milestone_new.tmpl
@@ -44,7 +44,7 @@
"TextareaPlaceholder" (ctx.Locale.Tr "repo.milestones.desc")
)}}
</div>
- <div class="tw-text-right">
+ <div class="flex-text-block tw-justify-end">
{{if .PageIsEditMilestone}}
<a class="ui primary basic button" href="{{.RepoLink}}/milestones">
{{ctx.Locale.Tr "repo.milestones.cancel"}}
diff --git a/templates/repo/issue/milestones.tmpl b/templates/repo/issue/milestones.tmpl
index 9515acfb8e..8805c709a0 100644
--- a/templates/repo/issue/milestones.tmpl
+++ b/templates/repo/issue/milestones.tmpl
@@ -41,7 +41,7 @@
{{if .TotalTrackedTime}}
<div class="flex-text-block">
{{svg "octicon-clock"}}
- {{.TotalTrackedTime|Sec2Time}}
+ {{.TotalTrackedTime|Sec2Hour}}
</div>
{{end}}
{{if .UpdatedUnix}}
@@ -76,14 +76,12 @@
{{else}}
<a class="link-action flex-text-inline" href data-url="{{$.Link}}/{{.ID}}/close">{{svg "octicon-x" 14}}{{ctx.Locale.Tr "repo.milestones.close"}}</a>
{{end}}
- <a class="delete-button flex-text-inline" href="#" data-url="{{$.RepoLink}}/milestones/delete" data-id="{{.ID}}">{{svg "octicon-trash" 14}}{{ctx.Locale.Tr "repo.issues.label_delete"}}</a>
+ <a class="link-action flex-text-inline text red" href data-modal-confirm="#repo-milestone-delete-modal" data-url="{{$.RepoLink}}/milestones/delete?id={{.ID}}">{{svg "octicon-trash" 14}}{{ctx.Locale.Tr "repo.issues.label_delete"}}</a>
</div>
{{end}}
</div>
{{if .Content}}
- <div class="markup content">
- {{.RenderedContent}}
- </div>
+ <div class="render-content markup">{{.RenderedContent}}</div>
{{end}}
</li>
{{end}}
@@ -94,15 +92,11 @@
</div>
{{if or .CanWriteIssues .CanWritePulls}}
- <div class="ui g-modal-confirm delete modal">
- <div class="header">
- {{svg "octicon-trash"}}
- {{ctx.Locale.Tr "repo.milestones.deletion"}}
- </div>
- <div class="content">
- <p>{{ctx.Locale.Tr "repo.milestones.deletion_desc"}}</p>
- </div>
+ <div class="ui small modal" id="repo-milestone-delete-modal">
+ <div class="header">{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.milestones.deletion"}}</div>
+ <div class="content"><p>{{ctx.Locale.Tr "repo.milestones.deletion_desc"}}</p></div>
{{template "base/modal_actions_confirm" .}}
</div>
{{end}}
+
{{template "base/footer" .}}
diff --git a/templates/repo/issue/new_form.tmpl b/templates/repo/issue/new_form.tmpl
index dd4c7617ce..9413200870 100644
--- a/templates/repo/issue/new_form.tmpl
+++ b/templates/repo/issue/new_form.tmpl
@@ -1,6 +1,4 @@
-{{if .Flash}}
{{template "base/alert" .}}
-{{end}}
<form class="issue-content ui comment form form-fetch-action" id="new-issue" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<div class="issue-content-left">
@@ -9,7 +7,10 @@
{{ctx.AvatarUtils.Avatar .SignedUser 40}}
<div class="ui segment content tw-my-0">
<div class="field">
- <input name="title" class="js-autofocus-end" id="issue_title" placeholder="{{ctx.Locale.Tr "repo.milestones.title"}}" value="{{if .TitleQuery}}{{.TitleQuery}}{{else if .IssueTemplateTitle}}{{.IssueTemplateTitle}}{{else}}{{.title}}{{end}}" required maxlength="255" autocomplete="off">
+ <input name="title" data-global-init="initInputAutoFocusEnd" id="issue_title" required maxlength="255" autocomplete="off"
+ placeholder="{{ctx.Locale.Tr "repo.milestones.title"}}"
+ value="{{if .TitleQuery}}{{.TitleQuery}}{{else if .IssueTemplateTitle}}{{.IssueTemplateTitle}}{{else}}{{.title}}{{end}}"
+ >
{{if .PageIsComparePull}}
<div class="title_wip_desc" data-wip-prefixes="{{JsonUtils.EncodeToString .PullRequestWorkInProgressPrefixes}}">{{ctx.Locale.Tr "repo.pulls.title_wip_desc" (index .PullRequestWorkInProgressPrefixes 0)}}</div>
{{end}}
@@ -32,7 +33,7 @@
{{else}}
{{template "repo/issue/comment_tab" .}}
{{end}}
- <div class="text right">
+ <div class="flex-text-block tw-justify-end">
<button class="ui primary button">
{{if .PageIsComparePull}}
{{ctx.Locale.Tr "repo.pulls.create"}}
diff --git a/templates/repo/issue/openclose.tmpl b/templates/repo/issue/openclose.tmpl
index b9dd04a7db..00a31b5fad 100644
--- a/templates/repo/issue/openclose.tmpl
+++ b/templates/repo/issue/openclose.tmpl
@@ -17,7 +17,7 @@
{{ctx.Locale.PrettyNumber .OpenCount}} {{ctx.Locale.Tr "repo.issues.open_title"}}
</a>
<a class="{{if eq .State "closed"}}active {{end}}item flex-text-inline" href="{{if eq .State "closed"}}{{$allStatesLink}}{{else}}{{$closedLink}}{{end}}">
- {{svg "octicon-check"}}
+ {{svg "octicon-issue-closed"}}
{{ctx.Locale.PrettyNumber .ClosedCount}} {{ctx.Locale.Tr "repo.issues.closed_title"}}
</a>
</div>
diff --git a/templates/repo/issue/search.tmpl b/templates/repo/issue/search.tmpl
index 07732ab5e7..de6c194ee8 100644
--- a/templates/repo/issue/search.tmpl
+++ b/templates/repo/issue/search.tmpl
@@ -8,6 +8,7 @@
<input type="hidden" name="project" value="{{$.ProjectID}}">
<input type="hidden" name="assignee" value="{{$.AssigneeID}}">
<input type="hidden" name="poster" value="{{$.PosterUsername}}">
+ <input type="hidden" name="sort" value="{{$.SortType}}">
{{end}}
{{template "shared/search/input" dict "Value" .Keyword}}
{{if .PageIsIssueList}}
diff --git a/templates/repo/issue/sidebar/allow_maintainer_edit.tmpl b/templates/repo/issue/sidebar/allow_maintainer_edit.tmpl
index ad4ce96a47..f584f9076c 100644
--- a/templates/repo/issue/sidebar/allow_maintainer_edit.tmpl
+++ b/templates/repo/issue/sidebar/allow_maintainer_edit.tmpl
@@ -1,13 +1,18 @@
-{{if and .Issue.IsPull .IsIssuePoster (not .Issue.IsClosed) .Issue.PullRequest.HeadRepo}}
- {{if and (not (eq .Issue.PullRequest.HeadRepo.FullName .Issue.PullRequest.BaseRepo.FullName)) .CanWriteToHeadRepo}}
+{{- $isHeadForkedRepo := and .Issue.PullRequest .Issue.PullRequest.HeadRepo (ne .Issue.PullRequest.HeadRepo.FullName .Issue.PullRequest.BaseRepo.FullName) -}}
+{{if $isHeadForkedRepo}}
+ {{- $isPullPoster := and .Issue.IsPull .IsIssuePoster -}}
+ {{- $isPullEditable := and .Issue.PullRequest (not .Issue.IsClosed) (not .Repository.IsArchived) -}}
+ {{- $allowToChange := and $isPullPoster $isPullEditable -}}
<div class="divider"></div>
- <div class="ui checkbox loading-icon-2px" id="allow-edits-from-maintainers"
+ <div class="ui checkbox {{if not $allowToChange}}disabled{{end}} loading-icon-2px"
+ {{if $allowToChange}}
+ id="allow-edits-from-maintainers"
data-url="{{.Issue.Link}}"
data-tooltip-content="{{ctx.Locale.Tr "repo.pulls.allow_edits_from_maintainers_desc"}}"
data-prompt-error="{{ctx.Locale.Tr "repo.pulls.allow_edits_from_maintainers_err"}}"
+ {{end}}
>
<label><strong>{{ctx.Locale.Tr "repo.pulls.allow_edits_from_maintainers"}}</strong></label>
- <input type="checkbox" {{if .Issue.PullRequest.AllowMaintainerEdit}}checked{{end}}>
+ <input type="checkbox" {{if .Issue.PullRequest.AllowMaintainerEdit}}checked{{end}} {{if not $allowToChange}}disabled{{end}}>
</div>
- {{end}}
{{end}}
diff --git a/templates/repo/issue/sidebar/assignee_list.tmpl b/templates/repo/issue/sidebar/assignee_list.tmpl
index d8ccd73387..1d2279a45d 100644
--- a/templates/repo/issue/sidebar/assignee_list.tmpl
+++ b/templates/repo/issue/sidebar/assignee_list.tmpl
@@ -6,8 +6,8 @@
{{if $pageMeta.Issue}}data-update-url="{{$pageMeta.RepoLink}}/issues/assignee?issue_ids={{$pageMeta.Issue.ID}}"{{end}}
>
<input class="combo-value" name="assignee_ids" type="hidden" value="{{$data.SelectedAssigneeIDs}}">
- <div class="ui dropdown {{if not $pageMeta.CanModifyIssueOrPull}}disabled{{end}}">
- <a class="text muted">
+ <div class="ui dropdown full-width {{if not $pageMeta.CanModifyIssueOrPull}}disabled{{end}}">
+ <a class="fixed-text muted">
<strong>{{ctx.Locale.Tr "repo.issues.new.assignees"}}</strong> {{if $pageMeta.CanModifyIssueOrPull}}{{svg "octicon-gear"}}{{end}}
</a>
<div class="menu">
@@ -15,10 +15,11 @@
<i class="icon">{{svg "octicon-search" 16}}</i>
<input type="text" placeholder="{{ctx.Locale.Tr "repo.issues.filter_assignees"}}">
</div>
- <div class="item clear-selection">{{ctx.Locale.Tr "repo.issues.new.clear_assignees"}}</div>
- <div class="scrolling menu">
+ <div class="scrolling menu flex-items-block">
+ <div class="item clear-selection" data-text="">{{ctx.Locale.Tr "repo.issues.new.clear_assignees"}}</div>
+ <div class="divider"></div>
{{range $data.CandidateAssignees}}
- <a class="item muted" href="#" data-value="{{.ID}}">
+ <a class="item" href="#" data-value="{{.ID}}">
<span class="item-check-mark">{{svg "octicon-check"}}</span>
{{ctx.AvatarUtils.Avatar . 20}} {{template "repo/search_name" .}}
</a>
@@ -26,10 +27,10 @@
</div>
</div>
</div>
- <div class="ui list tw-flex tw-flex-row tw-gap-2">
+ <div class="ui relaxed list muted-links flex-items-block">
<span class="item empty-list {{if $issueAssignees}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_assignees"}}</span>
{{range $issueAssignees}}
- <a class="item muted" href="{{$pageMeta.RepoLink}}/{{if $pageMeta.IsPullRequest}}pulls{{else}}issues{{end}}?assignee={{.ID}}">
+ <a class="item" href="{{$pageMeta.RepoLink}}/{{if $pageMeta.IsPullRequest}}pulls{{else}}issues{{end}}?assignee={{.ID}}">
{{ctx.AvatarUtils.Avatar . 20}} {{.GetDisplayName}}
</a>
{{end}}
diff --git a/templates/repo/issue/sidebar/issue_dependencies.tmpl b/templates/repo/issue/sidebar/issue_dependencies.tmpl
index 837bcd5670..bbae011958 100644
--- a/templates/repo/issue/sidebar/issue_dependencies.tmpl
+++ b/templates/repo/issue/sidebar/issue_dependencies.tmpl
@@ -22,7 +22,7 @@
{{range .BlockingDependencies}}
<div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} tw-flex tw-items-center tw-justify-between">
<div class="item-left tw-flex tw-justify-center tw-flex-col tw-flex-1 gt-ellipsis">
- <a class="muted gt-ellipsis" href="{{.Issue.Link}}" data-tooltip-content="#{{.Issue.Index}} {{.Issue.Title | ctx.RenderUtils.RenderEmoji}}">
+ <a class="muted issue-dependency-title gt-ellipsis" href="{{.Issue.Link}}" data-tooltip-content="#{{.Issue.Index}} {{.Issue.Title | ctx.RenderUtils.RenderEmoji}}">
#{{.Issue.Index}} {{.Issue.Title | ctx.RenderUtils.RenderEmoji}}
</a>
<div class="text small gt-ellipsis" data-tooltip-content="{{.Repository.OwnerName}}/{{.Repository.Name}}">
@@ -31,7 +31,9 @@
</div>
<div class="item-right tw-flex tw-items-center tw-m-1">
{{if and $.CanCreateIssueDependencies (not $.Repository.IsArchived)}}
- <a class="delete-dependency-button ci muted" data-id="{{.Issue.ID}}" data-type="blocking" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.remove_info"}}">
+ <a class="muted show-modal" data-modal="#issue-remove-dependency-confirm"
+ data-modal-remove-dependency-id="{{.Issue.ID}}" data-modal-dependency-type="blocking"
+ data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.remove_info"}}">
{{svg "octicon-trash" 16}}
</a>
{{end}}
@@ -54,7 +56,7 @@
{{range .BlockedByDependencies}}
<div class="item dependency{{if .Issue.IsClosed}} is-closed{{end}} tw-flex tw-items-center tw-justify-between">
<div class="item-left tw-flex tw-justify-center tw-flex-col tw-flex-1 gt-ellipsis">
- <a class="muted gt-ellipsis" href="{{.Issue.Link}}" data-tooltip-content="#{{.Issue.Index}} {{.Issue.Title | ctx.RenderUtils.RenderEmoji}}">
+ <a class="muted issue-dependency-title gt-ellipsis" href="{{.Issue.Link}}" data-tooltip-content="#{{.Issue.Index}} {{.Issue.Title | ctx.RenderUtils.RenderEmoji}}">
#{{.Issue.Index}} {{.Issue.Title | ctx.RenderUtils.RenderEmoji}}
</a>
<div class="text small gt-ellipsis" data-tooltip-content="{{.Repository.OwnerName}}/{{.Repository.Name}}">
@@ -63,7 +65,9 @@
</div>
<div class="item-right tw-flex tw-items-center tw-m-1">
{{if and $.CanCreateIssueDependencies (not $.Repository.IsArchived)}}
- <a class="delete-dependency-button ci muted" data-id="{{.Issue.ID}}" data-type="blockedBy" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.remove_info"}}">
+ <a class="muted show-modal" data-modal="#issue-remove-dependency-confirm"
+ data-modal-remove-dependency-id="{{.Issue.ID}}" data-modal-dependency-type="blockedBy"
+ data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.remove_info"}}">
{{svg "octicon-trash" 16}}
</a>
{{end}}
@@ -76,7 +80,7 @@
<div class="item-left tw-flex tw-justify-center tw-flex-col tw-flex-1 gt-ellipsis">
<div class="gt-ellipsis">
<span data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.no_permission.can_remove"}}">{{svg "octicon-lock" 16}}</span>
- <span class="gt-ellipsis" data-tooltip-content="#{{.Issue.Index}} {{.Issue.Title | ctx.RenderUtils.RenderEmoji}}">
+ <span class="gt-ellipsis issue-dependency-title" data-tooltip-content="#{{.Issue.Index}} {{.Issue.Title | ctx.RenderUtils.RenderEmoji}}">
#{{.Issue.Index}} {{.Issue.Title | ctx.RenderUtils.RenderEmoji}}
</span>
</div>
@@ -86,7 +90,9 @@
</div>
<div class="item-right tw-flex tw-items-center tw-m-1">
{{if and $.CanCreateIssueDependencies (not $.Repository.IsArchived)}}
- <a class="delete-dependency-button ci muted" data-id="{{.Issue.ID}}" data-type="blocking" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.remove_info"}}">
+ <a class="muted show-modal" data-modal="#issue-remove-dependency-confirm"
+ data-modal-remove-dependency-id="{{.Issue.ID}}" data-modal-dependency-type="blocking"
+ data-tooltip-content="{{ctx.Locale.Tr "repo.issues.dependency.remove_info"}}">
{{svg "octicon-trash" 16}}
</a>
{{end}}
@@ -106,7 +112,7 @@
<form method="post" action="{{.Issue.Link}}/dependency/add" id="addDependencyForm">
{{$.CsrfTokenHtml}}
<div class="ui fluid action input">
- <div class="ui search selection dropdown" id="new-dependency-drop-list" data-issue-id="{{.Issue.ID}}">
+ <div class="ui search selection dropdown" id="new-dependency-drop-list" data-issue-id="{{.Issue.ID}}" data-issue-cross-repo-search="{{.AllowCrossRepositoryDependencies}}">
<input name="newDependency" type="hidden">
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<input type="text" class="search">
@@ -122,28 +128,19 @@
</div>
{{if and .CanCreateIssueDependencies (not .Repository.IsArchived)}}
- <input type="hidden" id="crossRepoSearch" value="{{.AllowCrossRepositoryDependencies}}">
-
- <div class="ui g-modal-confirm modal remove-dependency">
- <div class="header">
- {{svg "octicon-trash"}}
- {{ctx.Locale.Tr "repo.issues.dependency.remove_header"}}
- </div>
+ <form id="issue-remove-dependency-confirm" class="ui g-modal-confirm modal" method="post" action="{{.Issue.Link}}/dependency/delete">
+ <div class="header">{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.issues.dependency.remove_header"}}</div>
<div class="content">
- <form method="post" action="{{.Issue.Link}}/dependency/delete" id="removeDependencyForm">
- {{$.CsrfTokenHtml}}
- <input type="hidden" value="" name="removeDependencyID" id="removeDependencyID">
- <input type="hidden" value="" name="dependencyType" id="dependencyType">
- </form>
- <p>{{if .Issue.IsPull}}
- {{ctx.Locale.Tr "repo.issues.dependency.pr_remove_text"}}
- {{else}}
- {{ctx.Locale.Tr "repo.issues.dependency.issue_remove_text"}}
- {{end}}</p>
+ {{$.CsrfTokenHtml}}
+ <input type="hidden" value="" name="removeDependencyID" class="remove-dependency-id">
+ <input type="hidden" value="" name="dependencyType" class="dependency-type">
+ <p>
+ {{ctx.Locale.Tr (Iif .Issue.IsPull "repo.issues.dependency.pr_remove_text" "repo.issues.dependency.issue_remove_text")}}
+ </p>
+ {{$ModalButtonCancelText := ctx.Locale.Tr "repo.issues.dependency.cancel"}}
+ {{$ModalButtonOkText := ctx.Locale.Tr "repo.issues.dependency.remove"}}
+ {{template "base/modal_actions_confirm" (dict "." . "ModalButtonCancelText" $ModalButtonCancelText "ModalButtonOkText" $ModalButtonOkText)}}
</div>
- {{$ModalButtonCancelText := ctx.Locale.Tr "repo.issues.dependency.cancel"}}
- {{$ModalButtonOkText := ctx.Locale.Tr "repo.issues.dependency.remove"}}
- {{template "base/modal_actions_confirm" (dict "." . "ModalButtonCancelText" $ModalButtonCancelText "ModalButtonOkText" $ModalButtonOkText)}}
- </div>
+ </form>
{{end}}
{{end}}
diff --git a/templates/repo/issue/sidebar/issue_management.tmpl b/templates/repo/issue/sidebar/issue_management.tmpl
index 76edf16e03..8876a5054e 100644
--- a/templates/repo/issue/sidebar/issue_management.tmpl
+++ b/templates/repo/issue/sidebar/issue_management.tmpl
@@ -1,6 +1,7 @@
{{if and .IsRepoAdmin (not .Repository.IsArchived)}}
<div class="divider"></div>
+ {{/* Pin issue */}}
{{if or .PinEnabled .Issue.IsPinned}}
<form class="tw-mt-1 form-fetch-action single-button-form" method="post" {{if $.NewPinAllowed}}action="{{.Issue.Link}}/pin"{{else}}data-tooltip-content="{{ctx.Locale.Tr "repo.issues.max_pinned"}}"{{end}}>
{{$.CsrfTokenHtml}}
@@ -16,16 +17,15 @@
</form>
{{end}}
- <button class="tw-mt-1 fluid ui show-modal button{{if .Issue.IsLocked}} red{{end}}" data-modal="#lock">
+ {{/* Lock/unlock conversation */}}
+ <button class="tw-mt-1 fluid ui show-modal button{{if .Issue.IsLocked}} red{{end}}" data-modal="#lock-conversation">
{{if .Issue.IsLocked}}
- {{svg "octicon-key"}}
- {{ctx.Locale.Tr "repo.issues.unlock"}}
+ {{svg "octicon-key"}} {{ctx.Locale.Tr "repo.issues.unlock"}}
{{else}}
- {{svg "octicon-lock"}}
- {{ctx.Locale.Tr "repo.issues.lock"}}
+ {{svg "octicon-lock"}} {{ctx.Locale.Tr "repo.issues.lock"}}
{{end}}
</button>
- <div class="ui tiny modal" id="lock">
+ <div class="ui tiny modal" id="lock-conversation">
<div class="header">
{{if .Issue.IsLocked}}
{{ctx.Locale.Tr "repo.issues.unlock.title"}}
@@ -45,29 +45,20 @@
{{end}}
</div>
- <form class="ui form form-fetch-action" action="{{.Issue.Link}}{{if .Issue.IsLocked}}/unlock{{else}}/lock{{end}}"
- method="post">
+ <form class="ui form form-fetch-action" method="post" action="{{.Issue.Link}}{{if .Issue.IsLocked}}/unlock{{else}}/lock{{end}}">
{{.CsrfTokenHtml}}
{{if not .Issue.IsLocked}}
<div class="field">
- <strong> {{ctx.Locale.Tr "repo.issues.lock.reason"}} </strong>
+ <strong>{{ctx.Locale.Tr "repo.issues.lock.reason"}}</strong>
</div>
<div class="field">
<div class="ui fluid dropdown selection">
-
- <select name="reason">
- <option value=""> </option>
- {{range .LockReasons}}
- <option value="{{.}}">{{.}}</option>
- {{end}}
- </select>
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
-
- <div class="default text"> </div>
-
+ <input type="hidden" name="reason">
+ <div class="text"></div> {{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
+ <div class="item" data-value=""></div>
{{range .LockReasons}}
<div class="item" data-value="{{.}}">{{.}}</div>
{{end}}
@@ -76,9 +67,10 @@
</div>
{{end}}
- <div class="text right actions">
+ <div class="actions">
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
- <button class="ui red button">
+ {{/* explicitly focus the submit button, to avoid Fomantic modal focuses and popups the dropdown */}}
+ <button class="ui red button" autofocus>
{{if .Issue.IsLocked}}
{{ctx.Locale.Tr "repo.issues.unlock_confirm"}}
{{else}}
diff --git a/templates/repo/issue/sidebar/label_list.tmpl b/templates/repo/issue/sidebar/label_list.tmpl
index 9dd83ba188..15c8760d1a 100644
--- a/templates/repo/issue/sidebar/label_list.tmpl
+++ b/templates/repo/issue/sidebar/label_list.tmpl
@@ -4,8 +4,8 @@
{{if $pageMeta.Issue}}data-update-url="{{$pageMeta.RepoLink}}/issues/labels?issue_ids={{$pageMeta.Issue.ID}}"{{end}}
>
<input class="combo-value" name="label_ids" type="hidden" value="{{$data.SelectedLabelIDs}}">
- <div class="ui dropdown {{if not $pageMeta.CanModifyIssueOrPull}}disabled{{end}}">
- <a class="text muted">
+ <div class="ui dropdown full-width {{if not $pageMeta.CanModifyIssueOrPull}}disabled{{end}}">
+ <a class="fixed-text muted">
<strong>{{ctx.Locale.Tr "repo.issues.new.labels"}}</strong> {{if $pageMeta.CanModifyIssueOrPull}}{{svg "octicon-gear"}}{{end}}
</a>
<div class="menu">
@@ -16,8 +16,9 @@
<i class="icon">{{svg "octicon-search" 16}}</i>
<input type="text" placeholder="{{ctx.Locale.Tr "repo.issues.filter_labels"}}">
</div>
- <a class="item clear-selection" href="#">{{ctx.Locale.Tr "repo.issues.new.clear_labels"}}</a>
<div class="scrolling menu">
+ <a class="item clear-selection" data-text="" href="#">{{ctx.Locale.Tr "repo.issues.new.clear_labels"}}</a>
+ <div class="divider"></div>
{{$previousExclusiveScope := "_no_scope"}}
{{range $data.RepoLabels}}
{{$exclusiveScope := .ExclusiveScope}}
@@ -42,7 +43,7 @@
</div>
</div>
- <div class="ui list labels-list tw-my-2 tw-flex tw-gap-2">
+ <div class="ui list labels-list">
<span class="item empty-list {{if $data.SelectedLabelIDs}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_label"}}</span>
{{range $data.AllLabels}}
{{if .IsChecked}}
diff --git a/templates/repo/issue/sidebar/milestone_list.tmpl b/templates/repo/issue/sidebar/milestone_list.tmpl
index 8b3e5b0eee..5bc961ed3c 100644
--- a/templates/repo/issue/sidebar/milestone_list.tmpl
+++ b/templates/repo/issue/sidebar/milestone_list.tmpl
@@ -6,8 +6,8 @@
{{if $pageMeta.Issue}}data-update-url="{{$pageMeta.RepoLink}}/issues/milestone?issue_ids={{$pageMeta.Issue.ID}}"{{end}}
>
<input class="combo-value" name="milestone_id" type="hidden" value="{{$data.SelectedMilestoneID}}">
- <div class="ui dropdown {{if not $pageMeta.CanModifyIssueOrPull}}disabled{{end}}">
- <a class="text muted">
+ <div class="ui dropdown full-width {{if not $pageMeta.CanModifyIssueOrPull}}disabled{{end}}">
+ <a class="fixed-text muted">
<strong>{{ctx.Locale.Tr "repo.issues.new.milestone"}}</strong> {{if $pageMeta.CanModifyIssueOrPull}}{{svg "octicon-gear"}}{{end}}
</a>
<div class="menu">
@@ -18,13 +18,13 @@
<i class="icon">{{svg "octicon-search"}}</i>
<input type="text" placeholder="{{ctx.Locale.Tr "repo.issues.filter_milestones"}}">
</div>
- <div class="divider"></div>
- <div class="item clear-selection">{{ctx.Locale.Tr "repo.issues.new.clear_milestone"}}</div>
<div class="scrolling menu">
+ <div class="item clear-selection" data-text="">{{ctx.Locale.Tr "repo.issues.new.clear_milestone"}}</div>
+ <div class="divider"></div>
{{if $data.OpenMilestones}}
<div class="header">{{ctx.Locale.Tr "repo.issues.filter_milestone_open"}}</div>
{{range $data.OpenMilestones}}
- <a class="item muted" data-value="{{.ID}}" href="{{$pageMeta.RepoLink}}/issues?milestone={{.ID}}">
+ <a class="item muted" data-value="{{.ID}}" href="{{$pageMeta.RepoLink}}/milestone/{{.ID}}">
{{svg "octicon-milestone" 18}} {{.Name}}
</a>
{{end}}
@@ -33,7 +33,7 @@
{{if $data.ClosedMilestones}}
<div class="header">{{ctx.Locale.Tr "repo.issues.filter_milestone_closed"}}</div>
{{range $data.ClosedMilestones}}
- <a class="item muted" data-value="{{.ID}}" href="{{$pageMeta.RepoLink}}/issues?milestone={{.ID}}">
+ <a class="item muted" data-value="{{.ID}}" href="{{$pageMeta.RepoLink}}/milestone/{{.ID}}">
{{svg "octicon-milestone" 18}} {{.Name}}
</a>
{{end}}
@@ -43,10 +43,10 @@
</div>
</div>
- <div class="ui list">
+ <div class="ui list muted-links flex-items-block">
<span class="item empty-list {{if $issueMilestone}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_milestone"}}</span>
{{if $issueMilestone}}
- <a class="item muted" href="{{$pageMeta.RepoLink}}/milestone/{{$issueMilestone.ID}}">
+ <a class="item" href="{{$pageMeta.RepoLink}}/milestone/{{$issueMilestone.ID}}">
{{svg "octicon-milestone" 18}} {{$issueMilestone.Name}}
</a>
{{end}}
diff --git a/templates/repo/issue/sidebar/project_list.tmpl b/templates/repo/issue/sidebar/project_list.tmpl
index 0cbbdce760..a212261a22 100644
--- a/templates/repo/issue/sidebar/project_list.tmpl
+++ b/templates/repo/issue/sidebar/project_list.tmpl
@@ -6,8 +6,8 @@
{{if $pageMeta.Issue}}data-update-url="{{$pageMeta.RepoLink}}/issues/projects?issue_ids={{$pageMeta.Issue.ID}}"{{end}}
>
<input class="combo-value" name="project_id" type="hidden" value="{{$data.SelectedProjectID}}">
- <div class="ui dropdown {{if not $pageMeta.CanModifyIssueOrPull}}disabled{{end}}">
- <a class="text muted">
+ <div class="ui dropdown full-width {{if not $pageMeta.CanModifyIssueOrPull}}disabled{{end}}">
+ <a class="fixed-text muted">
<strong>{{ctx.Locale.Tr "repo.issues.new.projects"}}</strong> {{if $pageMeta.CanModifyIssueOrPull}}{{svg "octicon-gear"}}{{end}}
</a>
<div class="menu">
@@ -17,8 +17,9 @@
<input type="text" placeholder="{{ctx.Locale.Tr "repo.issues.filter_projects"}}">
</div>
{{end}}
- <div class="item clear-selection">{{ctx.Locale.Tr "repo.issues.new.clear_projects"}}</div>
<div class="scrolling menu">
+ <div class="item clear-selection" data-text="">{{ctx.Locale.Tr "repo.issues.new.clear_projects"}}</div>
+ <div class="divider"></div>
{{if $data.OpenProjects}}
<div class="header">{{ctx.Locale.Tr "repo.issues.new.open_projects"}}</div>
{{range $data.OpenProjects}}
@@ -39,10 +40,10 @@
</div>
</div>
</div>
- <div class="ui list">
+ <div class="ui list muted-links flex-items-block">
<span class="item empty-list {{if $issueProject}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_projects"}}</span>
{{if $issueProject}}
- <a class="item muted" href="{{$issueProject.Link ctx}}">
+ <a class="item" href="{{$issueProject.Link ctx}}">
{{svg $issueProject.IconName 18}} {{$issueProject.Title}}
</a>
{{end}}
diff --git a/templates/repo/issue/sidebar/reference_link.tmpl b/templates/repo/issue/sidebar/reference_link.tmpl
index 6b8f120c7b..ad7c0f6bc0 100644
--- a/templates/repo/issue/sidebar/reference_link.tmpl
+++ b/templates/repo/issue/sidebar/reference_link.tmpl
@@ -1,8 +1,6 @@
<div class="divider"></div>
-<div class="ui equal width compact grid">
- {{$issueReferenceLink := printf "%s#%d" .Issue.Repo.FullName .Issue.Index}}
- <div class="row tw-items-center" data-tooltip-content="{{$issueReferenceLink}}">
- <span class="text column truncate">{{ctx.Locale.Tr "repo.issues.reference_link" $issueReferenceLink}}</span>
- <button class="ui two wide button column tw-p-2" data-clipboard-text="{{$issueReferenceLink}}">{{svg "octicon-copy" 14}}</button>
- </div>
+{{$issueReferenceLink := printf "%s#%d" .Issue.Repo.FullName .Issue.Index}}
+<div class="flex-text-block" data-tooltip-content="{{$issueReferenceLink}}">
+ <span class="tw-flex-1 gt-ellipsis">{{ctx.Locale.Tr "repo.issues.reference_link" $issueReferenceLink}}</span>
+ <button class="ui compact tiny icon button" data-clipboard-text="{{$issueReferenceLink}}">{{svg "octicon-copy" 14}}</button>
</div>
diff --git a/templates/repo/issue/sidebar/reviewer_list.tmpl b/templates/repo/issue/sidebar/reviewer_list.tmpl
index b3d9590d58..2b5ca80fe7 100644
--- a/templates/repo/issue/sidebar/reviewer_list.tmpl
+++ b/templates/repo/issue/sidebar/reviewer_list.tmpl
@@ -6,8 +6,8 @@
{{if $pageMeta.Issue}}data-update-url="{{$pageMeta.RepoLink}}/issues/request_review?issue_ids={{$pageMeta.Issue.ID}}"{{end}}
>
<input type="hidden" class="combo-value" name="reviewer_ids">{{/* match CreateIssueForm */}}
- <div class="ui dropdown {{if or (not $hasCandidates) (not $data.CanChooseReviewer)}}disabled{{end}}">
- <a class="text muted">
+ <div class="ui dropdown full-width {{if or (not $hasCandidates) (not $data.CanChooseReviewer)}}disabled{{end}}">
+ <a class="fixed-text muted">
<strong>{{ctx.Locale.Tr "repo.issues.review.reviewers"}}</strong> {{if $data.CanChooseReviewer}}{{svg "octicon-gear"}}{{end}}
</a>
<div class="menu flex-items-menu">
@@ -43,7 +43,7 @@
</div>
</div>
- <div class="ui relaxed list flex-items-block tw-my-4">
+ <div class="ui relaxed list flex-items-block">
<span class="item empty-list {{if or $data.OriginalReviews $data.CurrentPullReviewers}}tw-hidden{{end}}">
{{ctx.Locale.Tr "repo.issues.new.no_reviewers"}}
</span>
@@ -51,9 +51,9 @@
<div class="item">
<div class="flex-text-inline tw-flex-1">
{{if .User}}
- <a class="muted flex-text-inline" href="{{.User.HomeLink}}">{{ctx.AvatarUtils.Avatar .User 20}} {{.User.GetDisplayName}}</a>
+ <a class="muted flex-text-inline tw-gap-2" href="{{.User.HomeLink}}">{{ctx.AvatarUtils.Avatar .User 20}} {{.User.GetDisplayName}}</a>
{{else if .Team}}
- {{svg "octicon-people" 20}} {{$repoOwnerName}}/{{.Team.Name}}
+ <span class="flex-text-inline tw-gap-2">{{svg "octicon-people" 20}} {{$repoOwnerName}}/{{.Team.Name}}</span>
{{end}}
</div>
<div class="flex-text-inline">
@@ -92,7 +92,7 @@
<div class="flex-text-inline tw-flex-1">
{{$originalURLHostname := $pageMeta.Repository.GetOriginalURLHostname}}
{{$originalURL := $pageMeta.Repository.OriginalURL}}
- <a class="muted flex-text-inline" href="{{$originalURL}}" data-tooltip-content="{{ctx.Locale.Tr "repo.migrated_from_fake" $originalURLHostname}}">
+ <a class="muted flex-text-inline tw-gap-2" href="{{$originalURL}}" data-tooltip-content="{{ctx.Locale.Tr "repo.migrated_from_fake" $originalURLHostname}}">
{{svg (MigrationIcon $originalURLHostname) 20}} {{.OriginalAuthor}}
</a>
</div>
@@ -121,7 +121,7 @@
<label for="issue-sidebar-dismiss-review-message">{{ctx.Locale.Tr "action.review_dismissed_reason"}}</label>
<input id="issue-sidebar-dismiss-review-message" name="message">
</div>
- <div class="text right actions">
+ <div class="actions">
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
<button class="ui red button" type="submit">{{ctx.Locale.Tr "ok"}}</button>
</div>
diff --git a/templates/repo/issue/sidebar/stopwatch_timetracker.tmpl b/templates/repo/issue/sidebar/stopwatch_timetracker.tmpl
index f107dc5ef5..ab8600b068 100644
--- a/templates/repo/issue/sidebar/stopwatch_timetracker.tmpl
+++ b/templates/repo/issue/sidebar/stopwatch_timetracker.tmpl
@@ -2,10 +2,13 @@
{{if and .CanUseTimetracker (not .Repository.IsArchived)}}
<div class="divider"></div>
<div>
- <div class="ui dropdown jump">
- <a class="text muted">
- <strong>{{ctx.Locale.Tr "repo.issues.tracker"}}</strong> {{svg "octicon-gear"}}
- {{if $.IsStopwatchRunning}}{{svg "octicon-stopwatch"}}{{end}}
+ <div class="ui dropdown full-width jump">
+ <a class="fixed-text muted">
+ <div>
+ <strong>{{ctx.Locale.Tr "repo.issues.tracker"}}</strong>
+ {{if $.IsStopwatchRunning}}{{svg "octicon-stopwatch"}}{{end}}
+ </div>
+ {{svg "octicon-gear"}}
</a>
<div class="menu">
<a class="item issue-set-time-estimate show-modal" data-modal="#issue-time-set-estimate-modal">
@@ -13,14 +16,14 @@
</a>
<div class="divider"></div>
{{if $.IsStopwatchRunning}}
- <a class="item issue-stop-time link-action" data-url="{{.Issue.Link}}/times/stopwatch/toggle">
+ <a class="item issue-stop-time link-action" data-url="{{.Issue.Link}}/times/stopwatch/stop">
{{svg "octicon-stopwatch"}} {{ctx.Locale.Tr "repo.issues.timetracker_timer_stop"}}
</a>
<a class="item issue-cancel-time link-action" data-url="{{.Issue.Link}}/times/stopwatch/cancel">
{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.issues.timetracker_timer_discard"}}
</a>
{{else}}
- <a class="item issue-start-time link-action" data-url="{{.Issue.Link}}/times/stopwatch/toggle">
+ <a class="item issue-start-time link-action" data-url="{{.Issue.Link}}/times/stopwatch/start">
{{svg "octicon-stopwatch"}} {{ctx.Locale.Tr "repo.issues.timetracker_timer_start"}}
</a>
<a class="item issue-add-time show-modal" data-modal="#issue-time-manually-add-modal">
@@ -72,7 +75,7 @@
{{end}}
{{if .WorkingUsers}}
<div class="ui comments tw-mt-2">
- {{ctx.Locale.Tr "repo.issues.time_spent_from_all_authors" ($.Issue.TotalTrackedTime | Sec2Time)}}
+ {{ctx.Locale.Tr "repo.issues.time_spent_from_all_authors" ($.Issue.TotalTrackedTime | Sec2Hour)}}
<div>
{{range $user, $trackedtime := .WorkingUsers}}
<div class="comment tw-mt-2">
@@ -82,7 +85,7 @@
<div class="content">
{{template "shared/user/authorlink" $user}}
<div class="text">
- {{$trackedtime|Sec2Time}}
+ {{$trackedtime|Sec2Hour}}
</div>
</div>
</div>
diff --git a/templates/repo/issue/sidebar/wip_switch.tmpl b/templates/repo/issue/sidebar/wip_switch.tmpl
index 06a3be0d8f..8c40908f62 100644
--- a/templates/repo/issue/sidebar/wip_switch.tmpl
+++ b/templates/repo/issue/sidebar/wip_switch.tmpl
@@ -1,7 +1,5 @@
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .HasMerged) (not .Issue.IsClosed) (not .IsPullWorkInProgress)}}
- <div class="toggle-wip tw-mt-2" data-title="{{.Issue.Title}}" data-wip-prefix="{{index .PullRequestWorkInProgressPrefixes 0}}" data-update-url="{{.Issue.Link}}/title">
- <a class="muted">
- {{ctx.Locale.Tr "repo.pulls.still_in_progress"}} {{ctx.Locale.Tr "repo.pulls.add_prefix" (index .PullRequestWorkInProgressPrefixes 0)}}
- </a>
- </div>
+ <a data-global-init="initPullRequestWipToggle" data-title="{{.Issue.Title}}" data-wip-prefix="{{index .PullRequestWorkInProgressPrefixes 0}}" data-update-url="{{.Issue.Link}}/title">
+ {{ctx.Locale.Tr "repo.pulls.still_in_progress"}} {{ctx.Locale.Tr "repo.pulls.add_prefix" (index .PullRequestWorkInProgressPrefixes 0)}}
+ </a>
{{end}}
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl
index 1a68781ecd..dae3c4ee6a 100644
--- a/templates/repo/issue/view_content.tmpl
+++ b/templates/repo/issue/view_content.tmpl
@@ -13,7 +13,7 @@
</a>
{{end}}
<div class="content comment-container">
- <div class="ui top attached header comment-header tw-flex tw-items-center tw-justify-between" role="heading" aria-level="3">
+ <div class="comment-header" role="heading" aria-level="3">
<div class="comment-header-left tw-flex tw-items-center">
{{if .Issue.OriginalAuthor}}
<span class="text black tw-font-semibold">
@@ -68,7 +68,7 @@
{{template "repo/issue/view_content/comments" .}}
{{if and .Issue.IsPull (not $.Repository.IsArchived)}}
- {{template "repo/issue/view_content/pull".}}
+ {{template "repo/issue/view_content/pull_merge_box".}}
{{end}}
{{if .IsSigned}}
@@ -83,7 +83,7 @@
{{template "repo/issue/comment_tab" .}}
{{.CsrfTokenHtml}}
<div class="field footer">
- <div class="text right">
+ <div class="flex-text-block tw-justify-end">
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .DisableStatusChange)}}
{{if .Issue.IsClosed}}
<button id="status-button" class="ui primary basic button" data-status="{{ctx.Locale.Tr "repo.issues.reopen_issue"}}" data-status-and-comment="{{ctx.Locale.Tr "repo.issues.reopen_comment_issue"}}" name="status" value="reopen">
@@ -157,7 +157,7 @@
{{end}}
<div class="field">
- <div class="text right edit">
+ <div class="flex-text-block tw-justify-end">
<button type="button" class="ui cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
<button type="submit" class="ui primary button">{{ctx.Locale.Tr "repo.issues.save"}}</button>
</div>
diff --git a/templates/repo/issue/view_content/add_reaction.tmpl b/templates/repo/issue/view_content/add_reaction.tmpl
index 6baded8fe9..2f5764d964 100644
--- a/templates/repo/issue/view_content/add_reaction.tmpl
+++ b/templates/repo/issue/view_content/add_reaction.tmpl
@@ -3,7 +3,7 @@
<a class="muted">{{svg "octicon-smiley"}}</a>
<div class="menu">
{{range $value := AllowedReactions}}
- <a class="item emoji comment-reaction-button" data-tooltip-content="{{$value}}" aria-label="{{$value}}" data-reaction-content="{{$value}}">{{ReactionToEmoji $value}}</a>
+ <a class="item emoji" data-tooltip-content="{{$value}}" aria-label="{{$value}}" data-reaction-content="{{$value}}" data-global-click="onCommentReactionButtonClick">{{ReactionToEmoji $value}}</a>
{{end}}
</div>
</div>
diff --git a/templates/repo/issue/view_content/attachments.tmpl b/templates/repo/issue/view_content/attachments.tmpl
index 2155f78656..e865050559 100644
--- a/templates/repo/issue/view_content/attachments.tmpl
+++ b/templates/repo/issue/view_content/attachments.tmpl
@@ -31,7 +31,7 @@
{{if FilenameIsImage .Name}}
{{if not (StringUtils.Contains (StringUtils.ToString $.RenderedContent) .UUID)}}
<a target="_blank" rel="noopener noreferrer" href="{{.DownloadURL}}">
- <img alt="{{.Name}}" src="{{.DownloadURL}}" title="{{ctx.Locale.Tr "repo.issues.attachment.open_tab" .Name}}">
+ <img loading="lazy" alt="{{.Name}}" src="{{.DownloadURL}}" title="{{ctx.Locale.Tr "repo.issues.attachment.open_tab" .Name}}">
</a>
{{end}}
{{end}}
diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl
index 2e1a67edcc..50b8f58fc3 100644
--- a/templates/repo/issue/view_content/comments.tmpl
+++ b/templates/repo/issue/view_content/comments.tmpl
@@ -26,7 +26,7 @@
</a>
{{end}}
<div class="content comment-container">
- <div class="ui top attached header comment-header tw-flex tw-items-center tw-justify-between" role="heading" aria-level="3">
+ <div class="comment-header" role="heading" aria-level="3">
<div class="comment-header-left tw-flex tw-items-center">
{{if .OriginalAuthor}}
<span class="text black tw-font-semibold tw-mr-1">
@@ -163,12 +163,13 @@
</span>
<div class="detail flex-text-block">
{{svg "octicon-git-commit"}}
+ {{/* the content is a link like <a href="{RepoLink}/commit/{CommitID}">message title</a> (from CreateRefComment) */}}
<span class="text grey muted-links">{{.Content | SanitizeHTML}}</span>
</div>
</div>
{{else if eq .Type 7}}
{{if or .AddedLabels .RemovedLabels}}
- <div class="timeline-item event" id="{{.HashTag}}">
+ <div class="timeline-item event with-labels-list-inline" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-tag"}}</span>
{{template "shared/user/avatarlink" dict "user" .Poster}}
<span class="text grey muted-links">
@@ -252,7 +253,7 @@
<span class="text grey muted-links">
{{template "shared/user/authorlink" .Poster}}
{{$timeStr := .RenderedContent}} {{/* compatibility with time comments made before v1.21 */}}
- {{if not $timeStr}}{{$timeStr = .Content|Sec2Time}}{{end}}
+ {{if not $timeStr}}{{$timeStr = .Content|Sec2Hour}}{{end}}
{{ctx.Locale.Tr "repo.issues.stop_tracking_history" $timeStr $createdStr}}
</span>
{{template "repo/issue/view_content/comments_delete_time" dict "ctxData" $ "comment" .}}
@@ -264,7 +265,7 @@
<span class="text grey muted-links">
{{template "shared/user/authorlink" .Poster}}
{{$timeStr := .RenderedContent}} {{/* compatibility with time comments made before v1.21 */}}
- {{if not $timeStr}}{{$timeStr = .Content|Sec2Time}}{{end}}
+ {{if not $timeStr}}{{$timeStr = .Content|Sec2Hour}}{{end}}
{{ctx.Locale.Tr "repo.issues.add_time_history" $timeStr $createdStr}}
</span>
{{template "repo/issue/view_content/comments_delete_time" dict "ctxData" $ "comment" .}}
@@ -393,7 +394,7 @@
{{if or .Content .Attachments}}
<div class="timeline-item comment">
<div class="content comment-container">
- <div class="ui top attached header comment-header tw-flex tw-items-center tw-justify-between">
+ <div class="comment-header">
<div class="comment-header-left tw-flex tw-items-center">
{{if gt .Poster.ID 0}}
<a class="inline-timeline-avatar" href="{{.Poster.HomeLink}}">
@@ -506,7 +507,7 @@
{{/* compatibility with time comments made before v1.21 */}}
<span class="text grey muted-links">{{.RenderedContent}}</span>
{{else}}
- <span class="text grey muted-links">- {{.Content|Sec2Time}}</span>
+ <span class="text grey muted-links">- {{.Content|Sec2Hour}}</span>
{{end}}
</div>
</div>
@@ -615,7 +616,7 @@
<div class="timeline-item-group">
<div class="timeline-item event" id="{{.HashTag}}">
<a class="timeline-avatar"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}>
- <img src="{{.Poster.AvatarLink ctx}}" width="40" height="40">
+ <img loading="lazy" alt src="{{.Poster.AvatarLink ctx}}" width="40" height="40">
</a>
<span class="badge grey">{{svg "octicon-x" 16}}</span>
<span class="text grey muted-links">
@@ -633,8 +634,8 @@
</div>
{{if .Content}}
<div class="timeline-item comment">
- <div class="content">
- <div class="ui top attached header comment-header-left tw-flex tw-items-center arrow-top">
+ <div class="content comment-container">
+ <div class="comment-header arrow-top">
{{if gt .Poster.ID 0}}
<a class="inline-timeline-avatar" href="{{.Poster.HomeLink}}">
{{ctx.AvatarUtils.Avatar .Poster 24}}
@@ -644,7 +645,7 @@
{{ctx.Locale.Tr "action.review_dismissed_reason"}}
</span>
</div>
- <div class="ui attached segment">
+ <div class="ui attached segment comment-body">
<div class="render-content markup">
{{if .RenderedContent}}
{{.RenderedContent}}
diff --git a/templates/repo/issue/view_content/conversation.tmpl b/templates/repo/issue/view_content/conversation.tmpl
index 14803298b8..aa94a2dbc4 100644
--- a/templates/repo/issue/view_content/conversation.tmpl
+++ b/templates/repo/issue/view_content/conversation.tmpl
@@ -17,7 +17,7 @@
</div>
<div>
{{if or $invalid $resolved}}
- <button id="show-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="{{if not $resolved}}tw-hidden {{end}}ui compact labeled button show-outdated tw-flex tw-items-center">
+ <button id="show-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="{{if not $resolved}}tw-hidden{{end}} btn tiny show-outdated">
{{svg "octicon-unfold" 16 "tw-mr-2"}}
{{if $resolved}}
{{ctx.Locale.Tr "repo.issues.review.show_resolved"}}
@@ -25,7 +25,7 @@
{{ctx.Locale.Tr "repo.issues.review.show_outdated"}}
{{end}}
</button>
- <button id="hide-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="{{if $resolved}}tw-hidden {{end}}ui compact labeled button hide-outdated tw-flex tw-items-center">
+ <button id="hide-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="{{if $resolved}}tw-hidden {{end}} btn tiny hide-outdated">
{{svg "octicon-fold" 16 "tw-mr-2"}}
{{if $resolved}}
{{ctx.Locale.Tr "repo.issues.review.hide_resolved"}}
@@ -40,7 +40,7 @@
{{if $diff}}
{{$file := (index $diff.Files 0)}}
<div id="code-preview-{{$comment.ID}}" class="ui table segment{{if $resolved}} tw-hidden{{end}}">
- <div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}}">
+ <div class="diff-file-box file-content {{TabSizeClass $.Editorconfig $file.Name}}">
<div class="file-body file-code code-view code-diff code-diff-unified unicode-escaped">
<table>
<tbody>
@@ -57,7 +57,7 @@
{{$createdSubStr:= DateUtils.TimeSince .CreatedUnix}}
<div class="comment code-comment" id="{{.HashTag}}">
<div class="content comment-container">
- <div class="header comment-header">
+ <div class="comment-header">
<div class="comment-header-left tw-flex tw-items-center">
{{if not .OriginalAuthor}}
<a class="avatar">
@@ -109,7 +109,7 @@
</div>
{{end}}
</div>
- <div class="code-comment-buttons tw-flex tw-items-center tw-flex-wrap tw-mt-2 tw-mb-1 tw-mx-2">
+ <div class="flex-text-block tw-flex-wrap tw-my-2">
<div class="tw-flex-1">
{{if $resolved}}
<div class="ui grey text">
@@ -118,7 +118,7 @@
</div>
{{end}}
</div>
- <div class="code-comment-buttons-buttons">
+ <div class="flex-text-block">
{{if and $.CanMarkConversation $hasReview (not $isReviewPending)}}
<button class="ui tiny basic button resolve-conversation" data-origin="timeline" data-action="{{if not $resolved}}Resolve{{else}}UnResolve{{end}}" data-comment-id="{{$comment.ID}}" data-update-url="{{$.RepoLink}}/issues/resolve_conversation">
{{if $resolved}}
@@ -129,8 +129,8 @@
</button>
{{end}}
{{if and $.SignedUserID (not $.Repository.IsArchived)}}
- <button class="comment-form-reply ui primary tiny labeled icon button tw-ml-1 tw-mr-0">
- {{svg "octicon-reply" 16 "reply icon tw-mr-1"}}{{ctx.Locale.Tr "repo.diff.comment.reply"}}
+ <button class="comment-form-reply ui primary icon tiny button">
+ {{svg "octicon-reply" 12}}{{ctx.Locale.Tr "repo.diff.comment.reply"}}
</button>
{{end}}
</div>
diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull_merge_box.tmpl
index 7c53c5edb5..46bcd3b8b3 100644
--- a/templates/repo/issue/view_content/pull.tmpl
+++ b/templates/repo/issue/view_content/pull_merge_box.tmpl
@@ -1,7 +1,13 @@
{{if and .Issue.PullRequest.HasMerged (not .IsPullBranchDeletable)}}
{{/* Then the merge box will not be displayed because this page already contains enough information */}}
{{else}}
-<div class="timeline-item comment merge box">
+<div class="timeline-item comment pull-merge-box"
+ data-global-init="initRepoPullMergeBox"
+ {{if .PullMergeBoxReloadingInterval}}
+ data-pull-merge-box-reloading-interval="{{.PullMergeBoxReloadingInterval}}"
+ data-pull-link="{{.Issue.Link}}"
+ {{end}}
+>
<div class="timeline-avatar text {{if .Issue.PullRequest.HasMerged}}purple
{{- else if .Issue.IsClosed}}grey
{{- else if .IsPullWorkInProgress}}grey
@@ -83,13 +89,13 @@
{{ctx.Locale.Tr "repo.pulls.data_broken"}}
</div>
{{else if .IsPullWorkInProgress}}
- <div class="item toggle-wip" data-title="{{.Issue.Title}}" data-wip-prefix="{{.WorkInProgressPrefix}}" data-update-url="{{.Issue.Link}}/title">
+ <div class="item">
<div class="item-section-left flex-text-inline tw-flex-1">
{{svg "octicon-x"}}
{{ctx.Locale.Tr "repo.pulls.cannot_merge_work_in_progress"}}
</div>
{{if or .HasIssuesOrPullsWritePermission .IsIssuePoster}}
- <button class="ui compact button">
+ <button class="ui compact button" data-global-init="initPullRequestWipToggle" data-title="{{.Issue.Title}}" data-wip-prefix="{{.WorkInProgressPrefix}}" data-update-url="{{.Issue.Link}}/title">
{{ctx.Locale.Tr "repo.pulls.remove_prefix" .WorkInProgressPrefix}}
</button>
{{end}}
@@ -97,7 +103,7 @@
{{template "repo/issue/view_content/update_branch_by_merge" $}}
{{else if .Issue.PullRequest.IsChecking}}
<div class="item">
- {{svg "octicon-sync"}}
+ {{svg "octicon-sync" 16 "circular-spin"}}
{{ctx.Locale.Tr "repo.pulls.is_checking"}}
</div>
{{else if .Issue.PullRequest.IsAncestor}}
@@ -191,10 +197,11 @@
</div>
{{end}}
{{end}}
+
{{template "repo/issue/view_content/update_branch_by_merge" $}}
+
{{if .Issue.PullRequest.IsEmpty}}
<div class="divider"></div>
-
<div class="item">
{{svg "octicon-alert"}}
{{ctx.Locale.Tr "repo.pulls.is_empty"}}
@@ -216,7 +223,7 @@
const defaultMergeMessage = {{.DefaultMergeBody}};
const defaultSquashMergeMessage = {{.DefaultSquashMergeBody}};
const mergeForm = {
- 'baseLink': {{.Link}},
+ 'baseLink': {{.Issue.Link}},
'textCancel': {{ctx.Locale.Tr "cancel"}},
'textDeleteBranch': {{ctx.Locale.Tr "repo.branch.delete" .HeadTarget}},
'textAutoMergeButtonWhenSucceed': {{ctx.Locale.Tr "repo.pulls.auto_merge_button_when_succeed"}},
@@ -318,7 +325,7 @@
{{if .IsBlockedByApprovals}}
<div class="item text red">
{{svg "octicon-x"}}
- {{ctx.Locale.Tr "repo.pulls.blocked_by_approvals" .GrantedApprovals .ProtectedBranch.RequiredApprovals}}
+ {{ctx.Locale.Tr "repo.pulls.blocked_by_approvals" .GrantedApprovals .ProtectedBranch.RequiredApprovals}}
</div>
{{else if .IsBlockedByRejection}}
<div class="item text red">
@@ -377,7 +384,7 @@
*/}}
{{if and $.StillCanManualMerge (not $showGeneralMergeForm)}}
<div class="divider"></div>
- <form class="ui form form-fetch-action" action="{{.Link}}/merge" method="post">{{/* another similar form is in PullRequestMergeForm.vue*/}}
+ <form class="ui form form-fetch-action" action="{{.Issue.Link}}/merge" method="post">{{/* another similar form is in PullRequestMergeForm.vue*/}}
{{.CsrfTokenHtml}}
<div class="field">
<input type="text" name="merge_commit_id" placeholder="{{ctx.Locale.Tr "repo.pulls.merge_commit_id"}}">
diff --git a/templates/repo/issue/view_content/pull_merge_instruction.tmpl b/templates/repo/issue/view_content/pull_merge_instruction.tmpl
index 9a3e2cb7d7..a1cff41a3a 100644
--- a/templates/repo/issue/view_content/pull_merge_instruction.tmpl
+++ b/templates/repo/issue/view_content/pull_merge_instruction.tmpl
@@ -1,55 +1,57 @@
<div class="divider"></div>
-<div class="instruct-toggle"> {{ctx.Locale.Tr "repo.pulls.cmd_instruction_hint"}} </div>
-<div class="instruct-content tw-mt-2 tw-hidden">
- <div><h3>{{ctx.Locale.Tr "repo.pulls.cmd_instruction_checkout_title"}}</h3>{{ctx.Locale.Tr "repo.pulls.cmd_instruction_checkout_desc"}}</div>
- {{$localBranch := .PullRequest.HeadBranch}}
- {{if ne .PullRequest.HeadRepo.ID .PullRequest.BaseRepo.ID}}
- {{$localBranch = print .PullRequest.HeadRepo.OwnerName "-" .PullRequest.HeadBranch}}
- {{end}}
- <div class="ui secondary segment">
- {{if eq .PullRequest.Flow 0}}
- <div>git fetch -u {{if ne .PullRequest.HeadRepo.ID .PullRequest.BaseRepo.ID}}<origin-url data-url="{{.PullRequest.HeadRepo.Link}}"></origin-url>{{else}}origin{{end}} {{.PullRequest.HeadBranch}}:{{$localBranch}}</div>
- {{else}}
- <div>git fetch -u origin {{.PullRequest.GetGitRefName}}:{{$localBranch}}</div>
+<details>
+ <summary>{{ctx.Locale.Tr "repo.pulls.cmd_instruction_hint"}}</summary>
+ <div class="tw-mt-2">
+ <div><h3>{{ctx.Locale.Tr "repo.pulls.cmd_instruction_checkout_title"}}</h3>{{ctx.Locale.Tr "repo.pulls.cmd_instruction_checkout_desc"}}</div>
+ {{$localBranch := .PullRequest.HeadBranch}}
+ {{if ne .PullRequest.HeadRepo.ID .PullRequest.BaseRepo.ID}}
+ {{$localBranch = print .PullRequest.HeadRepo.OwnerName "-" .PullRequest.HeadBranch}}
{{end}}
- <div>git checkout {{$localBranch}}</div>
- </div>
- {{if .ShowMergeInstructions}}
- <div>
- <h3>{{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_title"}}</h3>
- {{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_desc"}}
- {{if not .AutodetectManualMerge}}
- <div>{{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_warning"}}</div>
- {{end}}
- </div>
- <div class="ui secondary segment">
- <div data-pull-merge-style="merge">
- <div>git checkout {{.PullRequest.BaseBranch}}</div>
- <div>git merge --no-ff {{$localBranch}}</div>
- </div>
- <div class="tw-hidden" data-pull-merge-style="rebase">
- <div>git checkout {{.PullRequest.BaseBranch}}</div>
- <div>git merge --ff-only {{$localBranch}}</div>
- </div>
- <div class="tw-hidden" data-pull-merge-style="rebase-merge">
+ <div class="ui secondary segment">
+ {{if eq .PullRequest.Flow 0}}
+ <div>git fetch -u {{if ne .PullRequest.HeadRepo.ID .PullRequest.BaseRepo.ID}}<origin-url data-url="{{.PullRequest.HeadRepo.Link}}"></origin-url>{{else}}origin{{end}} {{.PullRequest.HeadBranch}}:{{$localBranch}}</div>
+ {{else}}
+ <div>git fetch -u origin {{.PullRequest.GetGitRefName}}:{{$localBranch}}</div>
+ {{end}}
<div>git checkout {{$localBranch}}</div>
- <div>git rebase {{.PullRequest.BaseBranch}}</div>
- <div>git checkout {{.PullRequest.BaseBranch}}</div>
- <div>git merge --no-ff {{$localBranch}}</div>
- </div>
- <div class="tw-hidden" data-pull-merge-style="squash">
- <div>git checkout {{.PullRequest.BaseBranch}}</div>
- <div>git merge --squash {{$localBranch}}</div>
</div>
- <div class="tw-hidden" data-pull-merge-style="fast-forward-only">
- <div>git checkout {{.PullRequest.BaseBranch}}</div>
- <div>git merge --ff-only {{$localBranch}}</div>
+ {{if .ShowMergeInstructions}}
+ <div>
+ <h3>{{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_title"}}</h3>
+ {{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_desc"}}
+ {{if not .AutodetectManualMerge}}
+ <div>{{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_warning"}}</div>
+ {{end}}
</div>
- <div class="tw-hidden" data-pull-merge-style="manually-merged">
- <div>git checkout {{.PullRequest.BaseBranch}}</div>
- <div>git merge {{$localBranch}}</div>
+ <div class="ui secondary segment">
+ <div data-pull-merge-style="merge">
+ <div>git checkout {{.PullRequest.BaseBranch}}</div>
+ <div>git merge --no-ff {{$localBranch}}</div>
+ </div>
+ <div class="tw-hidden" data-pull-merge-style="rebase">
+ <div>git checkout {{.PullRequest.BaseBranch}}</div>
+ <div>git merge --ff-only {{$localBranch}}</div>
+ </div>
+ <div class="tw-hidden" data-pull-merge-style="rebase-merge">
+ <div>git checkout {{$localBranch}}</div>
+ <div>git rebase {{.PullRequest.BaseBranch}}</div>
+ <div>git checkout {{.PullRequest.BaseBranch}}</div>
+ <div>git merge --no-ff {{$localBranch}}</div>
+ </div>
+ <div class="tw-hidden" data-pull-merge-style="squash">
+ <div>git checkout {{.PullRequest.BaseBranch}}</div>
+ <div>git merge --squash {{$localBranch}}</div>
+ </div>
+ <div class="tw-hidden" data-pull-merge-style="fast-forward-only">
+ <div>git checkout {{.PullRequest.BaseBranch}}</div>
+ <div>git merge --ff-only {{$localBranch}}</div>
+ </div>
+ <div class="tw-hidden" data-pull-merge-style="manually-merged">
+ <div>git checkout {{.PullRequest.BaseBranch}}</div>
+ <div>git merge {{$localBranch}}</div>
+ </div>
+ <div>git push origin {{.PullRequest.BaseBranch}}</div>
</div>
- <div>git push origin {{.PullRequest.BaseBranch}}</div>
+ {{end}}
</div>
- {{end}}
-</div>
+</details>
diff --git a/templates/repo/issue/view_content/reactions.tmpl b/templates/repo/issue/view_content/reactions.tmpl
index 0011efe8b2..286d93a9b1 100644
--- a/templates/repo/issue/view_content/reactions.tmpl
+++ b/templates/repo/issue/view_content/reactions.tmpl
@@ -1,9 +1,9 @@
<div class="bottom-reactions" data-action-url="{{$.ActionURL}}">
{{range $key, $value := .Reactions}}
{{$hasReacted := $value.HasUser ctx.RootData.SignedUserID}}
- <a role="button" class="ui label basic{{if $hasReacted}} primary{{end}}{{if not ctx.RootData.IsSigned}} disabled{{end}} comment-reaction-button"
- data-tooltip-content
- title="{{$value.GetFirstUsers}}{{if gt ($value.GetMoreUserCount) 0}} {{ctx.Locale.Tr "repo.reactions_more" $value.GetMoreUserCount}}{{end}}"
+ <a role="button" class="ui label basic{{if $hasReacted}} primary{{end}}{{if not ctx.RootData.IsSigned}} disabled{{end}}"
+ data-global-click="onCommentReactionButtonClick"
+ data-tooltip-content title="{{$value.GetFirstUsers}}{{if gt ($value.GetMoreUserCount) 0}} {{ctx.Locale.Tr "repo.reactions_more" $value.GetMoreUserCount}}{{end}}"
aria-label="{{$value.GetFirstUsers}}{{if gt ($value.GetMoreUserCount) 0}} {{ctx.Locale.Tr "repo.reactions_more" $value.GetMoreUserCount}}{{end}}"
data-tooltip-placement="bottom-start"
data-reaction-content="{{$key}}" data-has-reacted="{{$hasReacted}}">
diff --git a/templates/repo/issue/view_content/reference_issue_dialog.tmpl b/templates/repo/issue/view_content/reference_issue_dialog.tmpl
index d6c9081001..0b28bdc811 100644
--- a/templates/repo/issue/view_content/reference_issue_dialog.tmpl
+++ b/templates/repo/issue/view_content/reference_issue_dialog.tmpl
@@ -7,7 +7,7 @@
{{.CsrfTokenHtml}}
<div class="field">
<label><strong>{{ctx.Locale.Tr "repository"}}</strong></label>
- <div class="ui search selection dropdown issue_reference_repository_search ellipsis-items-nowrap">
+ <div class="ui search selection dropdown issue_reference_repository_search ellipsis-text-items">
<div class="default text gt-ellipsis">{{.Repository.FullName}}</div>
<div class="menu"></div>
</div>
@@ -20,7 +20,7 @@
<label><strong>{{ctx.Locale.Tr "repo.issues.reference_issue.body"}}</strong></label>
<textarea name="content"></textarea>
</div>
- <div class="text right">
+ <div class="flex-text-block tw-justify-end">
<button class="ui primary button">{{ctx.Locale.Tr "repo.issues.create"}}</button>
</div>
</form>
diff --git a/templates/repo/issue/view_content/update_branch_by_merge.tmpl b/templates/repo/issue/view_content/update_branch_by_merge.tmpl
index adce052dee..5d959bf0b3 100644
--- a/templates/repo/issue/view_content/update_branch_by_merge.tmpl
+++ b/templates/repo/issue/view_content/update_branch_by_merge.tmpl
@@ -8,8 +8,8 @@
<div class="item-section-right">
{{if and $.UpdateAllowed $.UpdateByRebaseAllowed}}
<div class="tw-inline-block">
- <div class="ui buttons update-button">
- <button class="ui button" data-do="{{$.Link}}/update" data-redirect="{{$.Link}}">
+ <div id="update-pr-branch-with-base" class="ui buttons">
+ <button class="ui button" data-do="{{$.Issue.Link}}/update" data-redirect="{{$.Issue.Link}}">
<span class="button-text">
{{ctx.Locale.Tr "repo.pulls.update_branch"}}
</span>
@@ -17,15 +17,15 @@
<div class="ui dropdown icon button">
{{svg "octicon-triangle-down"}}
<div class="menu">
- <a class="item active selected" data-do="{{$.Link}}/update">{{ctx.Locale.Tr "repo.pulls.update_branch"}}</a>
- <a class="item" data-do="{{$.Link}}/update?style=rebase">{{ctx.Locale.Tr "repo.pulls.update_branch_rebase"}}</a>
+ <a class="item active selected" data-do="{{$.Issue.Link}}/update">{{ctx.Locale.Tr "repo.pulls.update_branch"}}</a>
+ <a class="item" data-do="{{$.Issue.Link}}/update?style=rebase">{{ctx.Locale.Tr "repo.pulls.update_branch_rebase"}}</a>
</div>
</div>
</div>
</div>
{{end}}
{{if and $.UpdateAllowed (not $.UpdateByRebaseAllowed)}}
- <form action="{{$.Link}}/update" method="post" class="ui update-branch-form">
+ <form action="{{$.Issue.Link}}/update" method="post" class="ui update-branch-form">
{{$.CsrfTokenHtml}}
<button class="ui compact button">
<span class="ui text">{{ctx.Locale.Tr "repo.pulls.update_branch"}}</span>
diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl
index 0354f6ef22..b8f28dfd9b 100644
--- a/templates/repo/issue/view_title.tmpl
+++ b/templates/repo/issue/view_title.tmpl
@@ -13,7 +13,7 @@
{{$canEditIssueTitle := and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
<div class="issue-title" id="issue-title-display">
<h1 class="tw-break-anywhere">
- {{ctx.RenderUtils.RenderIssueTitle .Issue.Title ($.Repository.ComposeMetas ctx)}}
+ {{ctx.RenderUtils.RenderIssueTitle .Issue.Title $.Repository}}
<span class="index">#{{.Issue.Index}}</span>
</h1>
<div class="issue-title-buttons">
@@ -42,7 +42,7 @@
{{if .HasMerged}}
<div class="ui purple label issue-state-label">{{svg "octicon-git-merge" 16 "tw-mr-1"}} {{if eq .Issue.PullRequest.Status 3}}{{ctx.Locale.Tr "repo.pulls.manually_merged"}}{{else}}{{ctx.Locale.Tr "repo.pulls.merged"}}{{end}}</div>
{{else if .Issue.IsClosed}}
- <div class="ui red label issue-state-label">{{svg (Iif .Issue.IsPull "octicon-git-pull-request" "octicon-issue-closed")}} {{ctx.Locale.Tr "repo.issues.closed_title"}}</div>
+ <div class="ui red label issue-state-label">{{svg (Iif .Issue.IsPull "octicon-git-pull-request-closed" "octicon-issue-closed")}} {{ctx.Locale.Tr "repo.issues.closed_title"}}</div>
{{else if .Issue.IsPull}}
{{if .IsPullWorkInProgress}}
<div class="ui grey label issue-state-label">{{svg "octicon-git-pull-request-draft"}} {{ctx.Locale.Tr "repo.issues.draft_title"}}</div>
diff --git a/templates/repo/latest_commit.tmpl b/templates/repo/latest_commit.tmpl
index c62efc8e88..cff338949f 100644
--- a/templates/repo/latest_commit.tmpl
+++ b/templates/repo/latest_commit.tmpl
@@ -21,10 +21,10 @@
{{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses}}
{{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}}
- <span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{ctx.RenderUtils.RenderCommitMessageLinkSubject .LatestCommit.Message $commitLink ($.Repository.ComposeMetas ctx)}}</span>
+ <span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{ctx.RenderUtils.RenderCommitMessageLinkSubject .LatestCommit.Message $commitLink $.Repository}}</span>
{{if IsMultilineCommitMessage .LatestCommit.Message}}
- <button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
- <pre class="commit-body tw-hidden">{{ctx.RenderUtils.RenderCommitBody .LatestCommit.Message ($.Repository.ComposeMetas ctx)}}</pre>
+ <button class="ui button ellipsis-button" aria-expanded="false" data-global-click="onRepoEllipsisButtonClick">...</button>
+ <pre class="commit-body tw-hidden">{{ctx.RenderUtils.RenderCommitBody .LatestCommit.Message $.Repository}}</pre>
{{end}}
</span>
{{end}}
diff --git a/templates/repo/migrate/codebase.tmpl b/templates/repo/migrate/codebase.tmpl
index c8059b7c7b..f4de8ff236 100644
--- a/templates/repo/migrate/codebase.tmpl
+++ b/templates/repo/migrate/codebase.tmpl
@@ -1,114 +1,114 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository new migrate">
- <div class="ui middle very relaxed page grid">
- <div class="column">
- <form class="ui form" action="{{.Link}}" method="post">
+ <div class="ui container medium-width">
+ <h3 class="ui top attached header">
+ {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
+ </h3>
+ <div class="ui attached segment">
+ {{template "base/alert" .}}
+ <form class="ui form left-right-form" action="{{.Link}}" method="post">
{{template "base/disable_form_autofill"}}
{{.CsrfTokenHtml}}
- <h3 class="ui top attached header">
- {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
- <input id="service_type" type="hidden" name="service" value="{{.service}}">
- </h3>
- <div class="ui attached segment">
- {{template "base/alert" .}}
- <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
- <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
- <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
- <span class="help">
- {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
- </span>
- </div>
- <div class="inline field {{if .Err_Auth}}error{{end}}">
- <label for="auth_username">{{ctx.Locale.Tr "username"}}</label>
- <input id="auth_username" name="auth_username" value="{{.auth_username}}" {{if not .auth_username}}data-need-clear="true"{{end}}>
- </div>
- <div class="inline field {{if .Err_Auth}}error{{end}}">
- <label for="auth_password">{{ctx.Locale.Tr "password"}}</label>
- <input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}">
- </div>
+ <input id="service_type" type="hidden" name="service" value="{{.service}}">
- {{template "repo/migrate/options" .}}
+ <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
+ <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
+ <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
+ <span class="help">
+ {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
+ </span>
+ </div>
- <div id="migrate_items">
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
- <div class="ui checkbox">
- <input name="milestones" type="checkbox" {{if .milestones}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_milestones"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_labels"}}</label>
- </div>
- </div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="issues" type="checkbox" {{if .issues}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_issues"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_merge_requests"}}</label>
- </div>
- </div>
- </div>
+ <div class="inline field {{if .Err_Auth}}error{{end}}">
+ <label for="auth_username">{{ctx.Locale.Tr "username"}}</label>
+ <input id="auth_username" name="auth_username" value="{{.auth_username}}" {{if not .auth_username}}data-need-clear="true"{{end}}>
+ </div>
+ <div class="inline field {{if .Err_Auth}}error{{end}}">
+ <label for="auth_password">{{ctx.Locale.Tr "password"}}</label>
+ <input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}">
+ </div>
- <div class="divider"></div>
+ {{template "repo/migrate/options" .}}
- <div class="inline required field {{if .Err_Owner}}error{{end}}">
- <label>{{ctx.Locale.Tr "repo.owner"}}</label>
- <div class="ui selection owner dropdown">
- <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
- <span class="text truncated-item-container" title="{{.ContextUser.Name}}">
- {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
- <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
- </span>
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- <div class="menu" title="{{.SignedUser.Name}}">
- <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
- {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
- <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
- </div>
- {{range .Orgs}}
- <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
- {{ctx.AvatarUtils.Avatar . 28 "mini"}}
- <span class="truncated-item-name">{{.ShortName 40}}</span>
- </div>
- {{end}}
- </div>
+ <div id="migrate_items">
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
+ <div class="ui checkbox">
+ <input name="milestones" type="checkbox" {{if .milestones}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_milestones"}}</label>
+ </div>
+ <div class="ui checkbox">
+ <input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_labels"}}</label>
</div>
- </div>
-
- <div class="inline required field {{if .Err_RepoName}}error{{end}}">
- <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
- <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
</div>
<div class="inline field">
- <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
+ <label></label>
<div class="ui checkbox">
- {{if .IsForcedPrivate}}
- <input name="private" type="checkbox" checked disabled>
- <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
- {{else}}
- <input name="private" type="checkbox" {{if .private}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
- {{end}}
+ <input name="issues" type="checkbox" {{if .issues}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_issues"}}</label>
+ </div>
+ <div class="ui checkbox">
+ <input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_merge_requests"}}</label>
</div>
</div>
- <div class="inline field {{if .Err_Description}}error{{end}}">
- <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
- <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
+ </div>
+
+ <div class="divider"></div>
+
+ <div class="inline required field {{if .Err_Owner}}error{{end}}">
+ <label>{{ctx.Locale.Tr "repo.owner"}}</label>
+ <div class="ui selection owner dropdown ellipsis-text-items">
+ <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
+ <span class="text" title="{{.ContextUser.Name}}">
+ {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
+ {{.ContextUser.ShortName 40}}
+ </span>
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ <div class="menu" title="{{.SignedUser.Name}}">
+ <div class="item" data-value="{{.SignedUser.ID}}">
+ {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
+ {{.SignedUser.ShortName 40}}
+ </div>
+ {{range .Orgs}}
+ <div class="item" data-value="{{.ID}}" title="{{.Name}}">
+ {{ctx.AvatarUtils.Avatar . 28 "mini"}}
+ {{.ShortName 40}}
+ </div>
+ {{end}}
+ </div>
</div>
+ </div>
- <div class="inline field">
- <label></label>
- <button class="ui primary button">
- {{ctx.Locale.Tr "repo.migrate_repo"}}
- </button>
+ <div class="inline required field {{if .Err_RepoName}}error{{end}}">
+ <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
+ <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
+ </div>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
+ <div class="ui checkbox">
+ {{if .IsForcedPrivate}}
+ <input name="private" type="checkbox" checked disabled>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
+ {{else}}
+ <input name="private" type="checkbox" {{if .private}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ {{end}}
</div>
</div>
+ <div class="inline field {{if .Err_Description}}error{{end}}">
+ <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
+ <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
+ </div>
+
+ <div class="inline field">
+ <label></label>
+ <button class="ui primary button">
+ {{ctx.Locale.Tr "repo.migrate_repo"}}
+ </button>
+ </div>
</form>
</div>
</div>
diff --git a/templates/repo/migrate/codecommit.tmpl b/templates/repo/migrate/codecommit.tmpl
index d1cebd0e48..275a2aef09 100644
--- a/templates/repo/migrate/codecommit.tmpl
+++ b/templates/repo/migrate/codecommit.tmpl
@@ -1,115 +1,115 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository new migrate">
- <div class="ui middle very relaxed page grid">
- <div class="column">
- <form class="ui form" action="{{.Link}}" method="post">
+ <div class="ui container medium-width">
+ <h3 class="ui top attached header">
+ {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
+ </h3>
+ <div class="ui attached segment">
+ {{template "base/alert" .}}
+ <form class="ui form left-right-form" action="{{.Link}}" method="post">
{{template "base/disable_form_autofill"}}
{{.CsrfTokenHtml}}
- <h3 class="ui top attached header">
- {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
- <input id="service_type" type="hidden" name="service" value="{{.service}}">
- </h3>
- <div class="ui attached segment">
- {{template "base/alert" .}}
- <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
- <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
- <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
- <span class="help">
- {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
- </span>
- </div>
- <div class="inline required field {{if .Err_Auth}}error{{end}}">
- <label for="aws_access_key_id">{{ctx.Locale.Tr "repo.migrate.codecommit.aws_access_key_id"}}</label>
- <input id="aws_access_key_id" name="aws_access_key_id" value="{{.aws_access_key_id}}" required>
- </div>
- <div class="inline required field {{if .Err_Auth}}error{{end}}">
- <label for="aws_secret_access_key">{{ctx.Locale.Tr "repo.migrate.codecommit.aws_secret_access_key"}}</label>
- <input id="aws_secret_access_key" name="aws_secret_access_key" type="password" value="{{.aws_secret_access_key}}" required>
- </div>
- <div class="inline required field {{if .Err_Auth}}error{{end}}">
- <label for="auth_username">{{ctx.Locale.Tr "repo.migrate.codecommit.https_git_credentials_username"}}</label>
- <input id="auth_username" name="auth_username" value="{{.auth_username}}" required>
- </div>
- <div class="inline required field {{if .Err_Auth}}error{{end}}">
- <label for="auth_password">{{ctx.Locale.Tr "repo.migrate.codecommit.https_git_credentials_password"}}</label>
- <input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}" required>
+ <input id="service_type" type="hidden" name="service" value="{{.service}}">
+
+ <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
+ <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
+ <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
+ <span class="help">
+ {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
+ </span>
+ </div>
+
+ <div class="inline required field {{if .Err_Auth}}error{{end}}">
+ <label for="aws_access_key_id">{{ctx.Locale.Tr "repo.migrate.codecommit.aws_access_key_id"}}</label>
+ <input id="aws_access_key_id" name="aws_access_key_id" value="{{.aws_access_key_id}}" required>
+ </div>
+ <div class="inline required field {{if .Err_Auth}}error{{end}}">
+ <label for="aws_secret_access_key">{{ctx.Locale.Tr "repo.migrate.codecommit.aws_secret_access_key"}}</label>
+ <input id="aws_secret_access_key" name="aws_secret_access_key" type="password" value="{{.aws_secret_access_key}}" required>
+ </div>
+ <div class="inline required field {{if .Err_Auth}}error{{end}}">
+ <label for="auth_username">{{ctx.Locale.Tr "repo.migrate.codecommit.https_git_credentials_username"}}</label>
+ <input id="auth_username" name="auth_username" value="{{.auth_username}}" required>
+ </div>
+ <div class="inline required field {{if .Err_Auth}}error{{end}}">
+ <label for="auth_password">{{ctx.Locale.Tr "repo.migrate.codecommit.https_git_credentials_password"}}</label>
+ <input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}" required>
+ </div>
+
+ {{if not .DisableNewPullMirrors}}
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.migrate_options"}}</label>
+ <div class="ui checkbox">
+ <input id="mirror" name="mirror" type="checkbox" {{if .mirror}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_options_mirror_helper"}}</label>
</div>
+ </div>
+ {{end}}
- {{if not .DisableNewPullMirrors}}
+ <div id="migrate_items">
<div class="inline field">
- <label>{{ctx.Locale.Tr "repo.migrate_options"}}</label>
+ <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
<div class="ui checkbox">
- <input id="mirror" name="mirror" type="checkbox" {{if .mirror}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_options_mirror_helper"}}</label>
- </div>
- </div>
- {{end}}
-
- <div id="migrate_items">
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
- <div class="ui checkbox">
- <input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_pullrequests"}}</label>
- </div>
+ <input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_pullrequests"}}</label>
</div>
</div>
+ </div>
- <div class="divider"></div>
+ <div class="divider"></div>
- <div class="inline required field {{if .Err_Owner}}error{{end}}">
- <label>{{ctx.Locale.Tr "repo.owner"}}</label>
- <div class="ui selection owner dropdown">
- <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
- <span class="text truncated-item-container" title="{{.ContextUser.Name}}">
- {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
- <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
- </span>
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- <div class="menu" title="{{.SignedUser.Name}}">
- <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
- {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
- <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
- </div>
- {{range .Orgs}}
- <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
- {{ctx.AvatarUtils.Avatar . 28 "mini"}}
- <span class="truncated-item-name">{{.ShortName 40}}</span>
- </div>
- {{end}}
+ <div class="inline required field {{if .Err_Owner}}error{{end}}">
+ <label>{{ctx.Locale.Tr "repo.owner"}}</label>
+ <div class="ui selection owner dropdown ellipsis-text-items">
+ <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
+ <span class="text" title="{{.ContextUser.Name}}">
+ {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
+ {{.ContextUser.ShortName 40}}
+ </span>
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ <div class="menu" title="{{.SignedUser.Name}}">
+ <div class="item" data-value="{{.SignedUser.ID}}">
+ {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
+ {{.SignedUser.ShortName 40}}
</div>
- </div>
- </div>
-
- <div class="inline required field {{if .Err_RepoName}}error{{end}}">
- <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
- <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
- </div>
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
- <div class="ui checkbox">
- {{if .IsForcedPrivate}}
- <input name="private" type="checkbox" checked disabled>
- <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
- {{else}}
- <input name="private" type="checkbox" {{if .private}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ {{range .Orgs}}
+ <div class="item" data-value="{{.ID}}" title="{{.Name}}">
+ {{ctx.AvatarUtils.Avatar . 28 "mini"}}
+ {{.ShortName 40}}
+ </div>
{{end}}
</div>
</div>
- <div class="inline field {{if .Err_Description}}error{{end}}">
- <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
- <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
- </div>
+ </div>
- <div class="inline field">
- <label></label>
- <button class="ui primary button">
- {{ctx.Locale.Tr "repo.migrate_repo"}}
- </button>
+ <div class="inline required field {{if .Err_RepoName}}error{{end}}">
+ <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
+ <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
+ </div>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
+ <div class="ui checkbox">
+ {{if .IsForcedPrivate}}
+ <input name="private" type="checkbox" checked disabled>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
+ {{else}}
+ <input name="private" type="checkbox" {{if .private}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ {{end}}
</div>
</div>
+ <div class="inline field {{if .Err_Description}}error{{end}}">
+ <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
+ <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
+ </div>
+
+ <div class="inline field">
+ <label></label>
+ <button class="ui primary button">
+ {{ctx.Locale.Tr "repo.migrate_repo"}}
+ </button>
+ </div>
</form>
</div>
</div>
diff --git a/templates/repo/migrate/git.tmpl b/templates/repo/migrate/git.tmpl
index 9c5f0d7d6d..41139d4fd6 100644
--- a/templates/repo/migrate/git.tmpl
+++ b/templates/repo/migrate/git.tmpl
@@ -1,88 +1,88 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository new migrate">
- <div class="ui middle very relaxed page grid">
- <div class="column">
- <form class="ui form" action="{{.Link}}" method="post">
+ <div class="ui container medium-width">
+ <h3 class="ui top attached header">
+ {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
+ </h3>
+ <div class="ui attached segment">
+ {{template "base/alert" .}}
+ <form class="ui form left-right-form" action="{{.Link}}" method="post">
{{template "base/disable_form_autofill"}}
{{.CsrfTokenHtml}}
- <h3 class="ui top attached header">
- {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
- <input id="service_type" type="hidden" name="service" value="{{.service}}">
- </h3>
- <div class="ui attached segment">
- {{template "base/alert" .}}
- <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
- <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
- <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
- <span class="help">
- {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
- </span>
- </div>
- <div class="inline field {{if .Err_Auth}}error{{end}}">
- <label for="auth_username">{{ctx.Locale.Tr "username"}}</label>
- <input id="auth_username" name="auth_username" value="{{.auth_username}}" {{if not .auth_username}}data-need-clear="true"{{end}}>
- </div>
- <div class="inline field {{if .Err_Auth}}error{{end}}">
- <label for="auth_password">{{ctx.Locale.Tr "password"}}</label>
- <input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}">
- </div>
- {{template "repo/migrate/options" .}}
+ <input id="service_type" type="hidden" name="service" value="{{.service}}">
- <div class="divider"></div>
+ <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
+ <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
+ <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
+ <span class="help">
+ {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
+ </span>
+ </div>
+ <div class="inline field {{if .Err_Auth}}error{{end}}">
+ <label for="auth_username">{{ctx.Locale.Tr "username"}}</label>
+ <input id="auth_username" name="auth_username" value="{{.auth_username}}" {{if not .auth_username}}data-need-clear="true"{{end}}>
+ </div>
+ <div class="inline field {{if .Err_Auth}}error{{end}}">
+ <label for="auth_password">{{ctx.Locale.Tr "password"}}</label>
+ <input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}">
+ </div>
- <div class="inline required field {{if .Err_Owner}}error{{end}}">
- <label>{{ctx.Locale.Tr "repo.owner"}}</label>
- <div class="ui selection owner dropdown">
- <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
- <span class="text truncated-item-container" title="{{.ContextUser.Name}}">
- {{ctx.AvatarUtils.Avatar .ContextUser}}
- <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
- </span>
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- <div class="menu" title="{{.SignedUser.Name}}">
- <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
- {{ctx.AvatarUtils.Avatar .SignedUser}}
- <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
- </div>
- {{range .Orgs}}
- <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
- {{ctx.AvatarUtils.Avatar .}}
- <span class="truncated-item-name">{{.ShortName 40}}</span>
- </div>
- {{end}}
- </div>
- </div>
- </div>
+ {{template "repo/migrate/options" .}}
- <div class="inline required field {{if .Err_RepoName}}error{{end}}">
- <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
- <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
- </div>
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
- <div class="ui checkbox">
- {{if .IsForcedPrivate}}
- <input name="private" type="checkbox" checked disabled>
- <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
- {{else}}
- <input name="private" type="checkbox" {{if .private}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ <div class="divider"></div>
+
+ <div class="inline required field {{if .Err_Owner}}error{{end}}">
+ <label>{{ctx.Locale.Tr "repo.owner"}}</label>
+ <div class="ui selection owner dropdown ellipsis-text-items">
+ <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
+ <span class="text" title="{{.ContextUser.Name}}">
+ {{ctx.AvatarUtils.Avatar .ContextUser}}
+ {{.ContextUser.ShortName 40}}
+ </span>
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ <div class="menu" title="{{.SignedUser.Name}}">
+ <div class="item" data-value="{{.SignedUser.ID}}">
+ {{ctx.AvatarUtils.Avatar .SignedUser}}
+ {{.SignedUser.ShortName 40}}
+ </div>
+ {{range .Orgs}}
+ <div class="item" data-value="{{.ID}}" title="{{.Name}}">
+ {{ctx.AvatarUtils.Avatar .}}
+ {{.ShortName 40}}
+ </div>
{{end}}
</div>
</div>
- <div class="inline field {{if .Err_Description}}error{{end}}">
- <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
- <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
- </div>
+ </div>
- <div class="inline field">
- <label></label>
- <button class="ui primary button">
- {{ctx.Locale.Tr "repo.migrate_repo"}}
- </button>
+ <div class="inline required field {{if .Err_RepoName}}error{{end}}">
+ <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
+ <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
+ </div>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
+ <div class="ui checkbox">
+ {{if .IsForcedPrivate}}
+ <input name="private" type="checkbox" checked disabled>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
+ {{else}}
+ <input name="private" type="checkbox" {{if .private}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ {{end}}
</div>
</div>
+ <div class="inline field {{if .Err_Description}}error{{end}}">
+ <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
+ <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
+ </div>
+
+ <div class="inline field">
+ <label></label>
+ <button class="ui primary button">
+ {{ctx.Locale.Tr "repo.migrate_repo"}}
+ </button>
+ </div>
</form>
</div>
</div>
diff --git a/templates/repo/migrate/gitbucket.tmpl b/templates/repo/migrate/gitbucket.tmpl
index b667fa828a..c89aa6c744 100644
--- a/templates/repo/migrate/gitbucket.tmpl
+++ b/templates/repo/migrate/gitbucket.tmpl
@@ -1,130 +1,130 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository new migrate">
- <div class="ui middle very relaxed page grid">
- <div class="column">
- <form class="ui form" action="{{.Link}}" method="post">
+ <div class="ui container medium-width">
+ <h3 class="ui top attached header">
+ {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
+ </h3>
+ <div class="ui attached segment">
+ {{template "base/alert" .}}
+ <form class="ui form left-right-form" action="{{.Link}}" method="post">
{{template "base/disable_form_autofill"}}
{{.CsrfTokenHtml}}
- <h3 class="ui top attached header">
- {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
- <input id="service_type" type="hidden" name="service" value="{{.service}}">
- </h3>
- <div class="ui attached segment">
- {{template "base/alert" .}}
- <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
- <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
- <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
- <span class="help">
- {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
- </span>
- </div>
- <div class="inline field {{if .Err_Auth}}error{{end}}">
- <label for="auth_username">{{ctx.Locale.Tr "username"}}</label>
- <input id="auth_username" name="auth_username" value="{{.auth_username}}" {{if not .auth_username}}data-need-clear="true"{{end}}>
- </div>
- <div class="inline field {{if .Err_Auth}}error{{end}}">
- <label for="auth_password">{{ctx.Locale.Tr "password"}}</label>
- <input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}">
- </div>
+ <input id="service_type" type="hidden" name="service" value="{{.service}}">
+
+ <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
+ <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
+ <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
+ <span class="help">
+ {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
+ </span>
+ </div>
- {{template "repo/migrate/options" .}}
+ <div class="inline field {{if .Err_Auth}}error{{end}}">
+ <label for="auth_username">{{ctx.Locale.Tr "username"}}</label>
+ <input id="auth_username" name="auth_username" value="{{.auth_username}}" {{if not .auth_username}}data-need-clear="true"{{end}}>
+ </div>
+ <div class="inline field {{if .Err_Auth}}error{{end}}">
+ <label for="auth_password">{{ctx.Locale.Tr "password"}}</label>
+ <input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}">
+ </div>
+
+ {{template "repo/migrate/options" .}}
+
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
+ <div class="ui checkbox">
+ <input name="wiki" type="checkbox" {{if .wiki}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_wiki"}}</label>
+ </div>
+ </div>
+ <div id="migrate_items" class="inline field">
+ <span class="help">{{ctx.Locale.Tr "repo.migrate.migrate_items_options"}}</span>
<div class="inline field">
- <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
+ <label></label>
+ <div class="ui checkbox">
+ <input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_labels"}}</label>
+ </div>
<div class="ui checkbox">
- <input name="wiki" type="checkbox" {{if .wiki}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_wiki"}}</label>
+ <input name="issues" type="checkbox" {{if .issues}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_issues"}}</label>
</div>
</div>
-
- <div id="migrate_items">
- <span class="help">{{ctx.Locale.Tr "repo.migrate.migrate_items_options"}}</span>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_labels"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="issues" type="checkbox" {{if .issues}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_issues"}}</label>
- </div>
+ <div class="inline field">
+ <label></label>
+ <div class="ui checkbox">
+ <input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_pullrequests"}}</label>
</div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_pullrequests"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="releases" type="checkbox" {{if .releases}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_releases"}}</label>
- </div>
+ <div class="ui checkbox">
+ <input name="releases" type="checkbox" {{if .releases}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_releases"}}</label>
</div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="milestones" type="checkbox" {{if .milestones}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_milestones"}}</label>
- </div>
+ </div>
+ <div class="inline field">
+ <label></label>
+ <div class="ui checkbox">
+ <input name="milestones" type="checkbox" {{if .milestones}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_milestones"}}</label>
</div>
</div>
+ </div>
- <div class="divider"></div>
+ <div class="divider"></div>
- <div class="inline required field {{if .Err_Owner}}error{{end}}">
- <label>{{ctx.Locale.Tr "repo.owner"}}</label>
- <div class="ui selection owner dropdown">
- <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
- <span class="text truncated-item-container" title="{{.ContextUser.Name}}">
- {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
- <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
- </span>
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- <div class="menu" title="{{.SignedUser.Name}}">
- <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
- {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
- <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
- </div>
- {{range .Orgs}}
- <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
- {{ctx.AvatarUtils.Avatar . 28 "mini"}}
- <span class="truncated-item-name">{{.ShortName 40}}</span>
- </div>
- {{end}}
+ <div class="inline required field {{if .Err_Owner}}error{{end}}">
+ <label>{{ctx.Locale.Tr "repo.owner"}}</label>
+ <div class="ui selection owner dropdown ellipsis-text-items">
+ <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
+ <span class="text" title="{{.ContextUser.Name}}">
+ {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
+ {{.ContextUser.ShortName 40}}
+ </span>
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ <div class="menu" title="{{.SignedUser.Name}}">
+ <div class="item" data-value="{{.SignedUser.ID}}">
+ {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
+ {{.SignedUser.ShortName 40}}
</div>
- </div>
- </div>
-
- <div class="inline required field {{if .Err_RepoName}}error{{end}}">
- <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
- <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
- </div>
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
- <div class="ui checkbox">
- {{if .IsForcedPrivate}}
- <input name="private" type="checkbox" checked disabled>
- <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
- {{else}}
- <input name="private" type="checkbox" {{if .private}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ {{range .Orgs}}
+ <div class="item" data-value="{{.ID}}" title="{{.Name}}">
+ {{ctx.AvatarUtils.Avatar . 28 "mini"}}
+ {{.ShortName 40}}
+ </div>
{{end}}
</div>
</div>
- <div class="inline field {{if .Err_Description}}error{{end}}">
- <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
- <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
- </div>
+ </div>
- <div class="inline field">
- <label></label>
- <button class="ui primary button">
- {{ctx.Locale.Tr "repo.migrate_repo"}}
- </button>
+ <div class="inline required field {{if .Err_RepoName}}error{{end}}">
+ <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
+ <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
+ </div>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
+ <div class="ui checkbox">
+ {{if .IsForcedPrivate}}
+ <input name="private" type="checkbox" checked disabled>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
+ {{else}}
+ <input name="private" type="checkbox" {{if .private}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ {{end}}
</div>
</div>
+ <div class="inline field {{if .Err_Description}}error{{end}}">
+ <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
+ <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
+ </div>
+
+ <div class="inline field">
+ <label></label>
+ <button class="ui primary button">
+ {{ctx.Locale.Tr "repo.migrate_repo"}}
+ </button>
+ </div>
</form>
</div>
</div>
diff --git a/templates/repo/migrate/gitea.tmpl b/templates/repo/migrate/gitea.tmpl
index 3b8f377096..55cf61a77f 100644
--- a/templates/repo/migrate/gitea.tmpl
+++ b/templates/repo/migrate/gitea.tmpl
@@ -1,126 +1,126 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository new migrate">
- <div class="ui middle very relaxed page grid">
- <div class="column">
- <form class="ui form" action="{{.Link}}" method="post">
+ <div class="ui container medium-width">
+ <h3 class="ui top attached header">
+ {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
+ </h3>
+ <div class="ui attached segment">
+ {{template "base/alert" .}}
+ <form class="ui form left-right-form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
- <h3 class="ui top attached header">
- {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
- <input id="service_type" type="hidden" name="service" value="{{.service}}">
- </h3>
- <div class="ui attached segment">
- {{template "base/alert" .}}
- <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
- <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
- <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
- <span class="help">
- {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
- </span>
- </div>
- <div class="inline field {{if .Err_Auth}}error{{end}}">
- <label for="auth_token">{{ctx.Locale.Tr "access_token"}}</label>
- <input id="auth_token" name="auth_token" type="password" autocomplete="new-password" value="{{.auth_token}}" {{if not .auth_token}} data-need-clear="true" {{end}}>
- <a target="_blank" href="https://docs.gitea.com/development/api-usage">{{svg "octicon-question"}}</a>
- </div>
+ <input id="service_type" type="hidden" name="service" value="{{.service}}">
- {{template "repo/migrate/options" .}}
+ <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
+ <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
+ <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
+ <span class="help">
+ {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
+ </span>
+ </div>
+
+ <div class="inline field {{if .Err_Auth}}error{{end}}">
+ <label for="auth_token">{{ctx.Locale.Tr "access_token"}}</label>
+ <input id="auth_token" name="auth_token" type="password" autocomplete="new-password" value="{{.auth_token}}" {{if not .auth_token}} data-need-clear="true" {{end}}>
+ <a target="_blank" href="https://docs.gitea.com/development/api-usage">{{svg "octicon-question"}}</a>
+ </div>
+ {{template "repo/migrate/options" .}}
+
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
+ <div class="ui checkbox">
+ <input name="wiki" type="checkbox" {{if .wiki}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_wiki"}}</label>
+ </div>
+ </div>
+
+ <div id="migrate_items" class="inline field">
+ <span class="help">{{ctx.Locale.Tr "repo.migrate.migrate_items_options"}}</span>
<div class="inline field">
- <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
+ <label></label>
<div class="ui checkbox">
- <input name="wiki" type="checkbox" {{if .wiki}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_wiki"}}</label>
+ <input name="labels" type="checkbox" {{if .labels}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_labels"}}</label>
+ </div>
+ <div class="ui checkbox">
+ <input name="issues" type="checkbox" {{if .issues}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_issues"}}</label>
</div>
</div>
-
- <div id="migrate_items">
- <span class="help">{{ctx.Locale.Tr "repo.migrate.migrate_items_options"}}</span>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="labels" type="checkbox" {{if .labels}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_labels"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="issues" type="checkbox" {{if .issues}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_issues"}}</label>
- </div>
+ <div class="inline field">
+ <label></label>
+ <div class="ui checkbox">
+ <input name="pull_requests" type="checkbox" {{if .pull_requests}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_pullrequests"}}</label>
</div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="pull_requests" type="checkbox" {{if .pull_requests}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_pullrequests"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="releases" type="checkbox" {{if .releases}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_releases"}}</label>
- </div>
+ <div class="ui checkbox">
+ <input name="releases" type="checkbox" {{if .releases}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_releases"}}</label>
</div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="milestones" type="checkbox" {{if .milestones}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_milestones"}}</label>
- </div>
+ </div>
+ <div class="inline field">
+ <label></label>
+ <div class="ui checkbox">
+ <input name="milestones" type="checkbox" {{if .milestones}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_milestones"}}</label>
</div>
</div>
+ </div>
- <div class="divider"></div>
+ <div class="divider"></div>
- <div class="inline required field {{if .Err_Owner}}error{{end}}">
- <label>{{ctx.Locale.Tr "repo.owner"}}</label>
- <div class="ui selection owner dropdown">
- <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
- <span class="text truncated-item-container" title="{{.ContextUser.Name}}">
- {{ctx.AvatarUtils.Avatar .ContextUser}}
- <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
- </span>
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- <div class="menu" title="{{.SignedUser.Name}}">
- <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
- {{ctx.AvatarUtils.Avatar .SignedUser}}
- <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
- </div>
- {{range .Orgs}}
- <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
- {{ctx.AvatarUtils.Avatar .}}
- <span class="truncated-item-name">{{.ShortName 40}}</span>
- </div>
- {{end}}
+ <div class="inline required field {{if .Err_Owner}}error{{end}}">
+ <label>{{ctx.Locale.Tr "repo.owner"}}</label>
+ <div class="ui selection owner dropdown ellipsis-text-items">
+ <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
+ <span class="text" title="{{.ContextUser.Name}}">
+ {{ctx.AvatarUtils.Avatar .ContextUser}}
+ {{.ContextUser.ShortName 40}}
+ </span>
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ <div class="menu" title="{{.SignedUser.Name}}">
+ <div class="item" data-value="{{.SignedUser.ID}}">
+ {{ctx.AvatarUtils.Avatar .SignedUser}}
+ {{.SignedUser.ShortName 40}}
+ </div>
+ {{range .Orgs}}
+ <div class="item" data-value="{{.ID}}" title="{{.Name}}">
+ {{ctx.AvatarUtils.Avatar .}}
+ {{.ShortName 40}}
</div>
- </div>
- </div>
-
- <div class="inline required field {{if .Err_RepoName}}error{{end}}">
- <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
- <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
- </div>
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
- <div class="ui checkbox">
- {{if .IsForcedPrivate}}
- <input name="private" type="checkbox" checked disabled>
- <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
- {{else}}
- <input name="private" type="checkbox" {{if .private}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
{{end}}
</div>
</div>
- <div class="inline field {{if .Err_Description}}error{{end}}">
- <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
- <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
- </div>
+ </div>
- <div class="inline field">
- <label></label>
- <button class="ui primary button">
- {{ctx.Locale.Tr "repo.migrate_repo"}}
- </button>
+ <div class="inline required field {{if .Err_RepoName}}error{{end}}">
+ <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
+ <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
+ </div>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
+ <div class="ui checkbox">
+ {{if .IsForcedPrivate}}
+ <input name="private" type="checkbox" checked disabled>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
+ {{else}}
+ <input name="private" type="checkbox" {{if .private}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ {{end}}
</div>
</div>
+ <div class="inline field {{if .Err_Description}}error{{end}}">
+ <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
+ <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
+ </div>
+
+ <div class="inline field">
+ <label></label>
+ <button class="ui primary button">
+ {{ctx.Locale.Tr "repo.migrate_repo"}}
+ </button>
+ </div>
</form>
</div>
</div>
diff --git a/templates/repo/migrate/github.tmpl b/templates/repo/migrate/github.tmpl
index 3535eddfc2..86b585b861 100644
--- a/templates/repo/migrate/github.tmpl
+++ b/templates/repo/migrate/github.tmpl
@@ -1,128 +1,128 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository new migrate">
- <div class="ui middle very relaxed page grid">
- <div class="column">
- <form class="ui form" action="{{.Link}}" method="post">
+ <div class="ui container medium-width">
+ <h3 class="ui top attached header">
+ {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
+ </h3>
+ <div class="ui attached segment">
+ {{template "base/alert" .}}
+ <form class="ui form left-right-form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
- <h3 class="ui top attached header">
- {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
- <input id="service_type" type="hidden" name="service" value="{{.service}}">
- </h3>
- <div class="ui attached segment">
- {{template "base/alert" .}}
- <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
- <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
- <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
- <span class="help">
- {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}
- </span>
- </div>
- <div class="inline field {{if .Err_Auth}}error{{end}}">
- <label for="auth_token">{{ctx.Locale.Tr "access_token"}}</label>
- <input id="auth_token" name="auth_token" type="password" autocomplete="new-password" value="{{.auth_token}}" {{if not .auth_token}}data-need-clear="true"{{end}}>
- <a target="_blank" href="https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token">{{svg "octicon-question"}}</a>
- <span class="help">
- {{ctx.Locale.Tr "repo.migrate.github_token_desc"}}
- </span>
- </div>
+ <input id="service_type" type="hidden" name="service" value="{{.service}}">
- {{template "repo/migrate/options" .}}
+ <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
+ <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
+ <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
+ <span class="help">
+ {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}
+ </span>
+ </div>
+ <div class="inline field {{if .Err_Auth}}error{{end}}">
+ <label for="auth_token">{{ctx.Locale.Tr "access_token"}}</label>
+ <input id="auth_token" name="auth_token" type="password" autocomplete="new-password" value="{{.auth_token}}" {{if not .auth_token}}data-need-clear="true"{{end}}>
+ <a target="_blank" href="https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token">{{svg "octicon-question"}}</a>
+ <span class="help">
+ {{ctx.Locale.Tr "repo.migrate.github_token_desc"}}
+ </span>
+ </div>
+
+ {{template "repo/migrate/options" .}}
+
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
+ <div class="ui checkbox">
+ <input name="wiki" type="checkbox" {{if .wiki}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_wiki"}}</label>
+ </div>
+ </div>
+ <div id="migrate_items" class="inline field">
+ <span class="help">{{ctx.Locale.Tr "repo.migrate.migrate_items_options"}}</span>
<div class="inline field">
- <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
+ <label></label>
<div class="ui checkbox">
- <input name="wiki" type="checkbox" {{if .wiki}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_wiki"}}</label>
+ <input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_labels"}}</label>
+ </div>
+ <div class="ui checkbox">
+ <input name="issues" type="checkbox" {{if .issues}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_issues"}}</label>
</div>
</div>
- <div id="migrate_items">
- <span class="help">{{ctx.Locale.Tr "repo.migrate.migrate_items_options"}}</span>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_labels"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="issues" type="checkbox" {{if .issues}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_issues"}}</label>
- </div>
+ <div class="inline field">
+ <label></label>
+ <div class="ui checkbox">
+ <input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_pullrequests"}}</label>
</div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_pullrequests"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="releases" type="checkbox" {{if .releases}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_releases"}}</label>
- </div>
+ <div class="ui checkbox">
+ <input name="releases" type="checkbox" {{if .releases}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_releases"}}</label>
</div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="milestones" type="checkbox" {{if .milestones}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_milestones"}}</label>
- </div>
+ </div>
+ <div class="inline field">
+ <label></label>
+ <div class="ui checkbox">
+ <input name="milestones" type="checkbox" {{if .milestones}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_milestones"}}</label>
</div>
</div>
+ </div>
- <div class="divider"></div>
+ <div class="divider"></div>
- <div class="inline required field {{if .Err_Owner}}error{{end}}">
- <label>{{ctx.Locale.Tr "repo.owner"}}</label>
- <div class="ui selection owner dropdown">
- <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
- <span class="text truncated-item-container" title="{{.ContextUser.Name}}">
- {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
- <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
- </span>
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- <div class="menu" title="{{.SignedUser.Name}}">
- <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
- {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
- <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
- </div>
- {{range .Orgs}}
- <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
- {{ctx.AvatarUtils.Avatar . 28 "mini"}}
- <span class="truncated-item-name">{{.ShortName 40}}</span>
- </div>
- {{end}}
+ <div class="inline required field {{if .Err_Owner}}error{{end}}">
+ <label>{{ctx.Locale.Tr "repo.owner"}}</label>
+ <div class="ui selection owner dropdown ellipsis-text-items">
+ <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
+ <span class="text" title="{{.ContextUser.Name}}">
+ {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
+ {{.ContextUser.ShortName 40}}
+ </span>
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ <div class="menu" title="{{.SignedUser.Name}}">
+ <div class="item" data-value="{{.SignedUser.ID}}">
+ {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
+ {{.SignedUser.ShortName 40}}
</div>
- </div>
- </div>
-
- <div class="inline required field {{if .Err_RepoName}}error{{end}}">
- <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
- <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
- </div>
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
- <div class="ui checkbox">
- {{if .IsForcedPrivate}}
- <input name="private" type="checkbox" checked disabled>
- <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
- {{else}}
- <input name="private" type="checkbox" {{if .private}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ {{range .Orgs}}
+ <div class="item" data-value="{{.ID}}" title="{{.Name}}">
+ {{ctx.AvatarUtils.Avatar . 28 "mini"}}
+ {{.ShortName 40}}
+ </div>
{{end}}
</div>
</div>
- <div class="inline field {{if .Err_Description}}error{{end}}">
- <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
- <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
- </div>
+ </div>
- <div class="inline field">
- <label></label>
- <button class="ui primary button">
- {{ctx.Locale.Tr "repo.migrate_repo"}}
- </button>
+ <div class="inline required field {{if .Err_RepoName}}error{{end}}">
+ <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
+ <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
+ </div>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
+ <div class="ui checkbox">
+ {{if .IsForcedPrivate}}
+ <input name="private" type="checkbox" checked disabled>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
+ {{else}}
+ <input name="private" type="checkbox" {{if .private}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ {{end}}
</div>
</div>
+ <div class="inline field {{if .Err_Description}}error{{end}}">
+ <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
+ <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
+ </div>
+
+ <div class="inline field">
+ <label></label>
+ <button class="ui primary button">
+ {{ctx.Locale.Tr "repo.migrate_repo"}}
+ </button>
+ </div>
</form>
</div>
</div>
diff --git a/templates/repo/migrate/gitlab.tmpl b/templates/repo/migrate/gitlab.tmpl
index f705fb3090..edd69f0027 100644
--- a/templates/repo/migrate/gitlab.tmpl
+++ b/templates/repo/migrate/gitlab.tmpl
@@ -1,125 +1,125 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository new migrate">
- <div class="ui middle very relaxed page grid">
- <div class="column">
- <form class="ui form" action="{{.Link}}" method="post">
+ <div class="ui container medium-width">
+ <h3 class="ui top attached header">
+ {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
+ </h3>
+ <div class="ui attached segment">
+ {{template "base/alert" .}}
+ <form class="ui form left-right-form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
- <h3 class="ui top attached header">
- {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
- <input id="service_type" type="hidden" name="service" value="{{.service}}">
- </h3>
- <div class="ui attached segment">
- {{template "base/alert" .}}
- <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
- <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
- <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
- <span class="help">
- {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
- </span>
- </div>
- <div class="inline field {{if .Err_Auth}}error{{end}}">
- <label for="auth_token">{{ctx.Locale.Tr "access_token"}}</label>
- <input id="auth_token" name="auth_token" type="password" autocomplete="new-password" value="{{.auth_token}}" {{if not .auth_token}}data-need-clear="true"{{end}}>
- <a target="_blank" href="https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html">{{svg "octicon-question"}}</a>
- </div>
+ <input id="service_type" type="hidden" name="service" value="{{.service}}">
- {{template "repo/migrate/options" .}}
+ <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
+ <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
+ <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
+ <span class="help">
+ {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
+ </span>
+ </div>
+
+ <div class="inline field {{if .Err_Auth}}error{{end}}">
+ <label for="auth_token">{{ctx.Locale.Tr "access_token"}}</label>
+ <input id="auth_token" name="auth_token" type="password" autocomplete="new-password" value="{{.auth_token}}" {{if not .auth_token}}data-need-clear="true"{{end}}>
+ <a target="_blank" href="https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html">{{svg "octicon-question"}}</a>
+ </div>
+ {{template "repo/migrate/options" .}}
+
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
+ <div class="ui checkbox">
+ <input name="wiki" type="checkbox" {{if .wiki}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_wiki"}}</label>
+ </div>
+ </div>
+ <div id="migrate_items" class="inline field">
+ <span class="help">{{ctx.Locale.Tr "repo.migrate.migrate_items_options"}}</span>
<div class="inline field">
- <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
+ <label></label>
+ <div class="ui checkbox">
+ <input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_labels"}}</label>
+ </div>
<div class="ui checkbox">
- <input name="wiki" type="checkbox" {{if .wiki}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_wiki"}}</label>
+ <input name="issues" type="checkbox" {{if .issues}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_issues"}}</label>
</div>
</div>
- <div id="migrate_items">
- <span class="help">{{ctx.Locale.Tr "repo.migrate.migrate_items_options"}}</span>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_labels"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="issues" type="checkbox" {{if .issues}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_issues"}}</label>
- </div>
+ <div class="inline field">
+ <label></label>
+ <div class="ui checkbox">
+ <input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_merge_requests"}}</label>
</div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_merge_requests"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="releases" type="checkbox" {{if .releases}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_releases"}}</label>
- </div>
+ <div class="ui checkbox">
+ <input name="releases" type="checkbox" {{if .releases}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_releases"}}</label>
</div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="milestones" type="checkbox" {{if .milestones}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_milestones"}}</label>
- </div>
+ </div>
+ <div class="inline field">
+ <label></label>
+ <div class="ui checkbox">
+ <input name="milestones" type="checkbox" {{if .milestones}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_milestones"}}</label>
</div>
</div>
+ </div>
- <div class="divider"></div>
+ <div class="divider"></div>
- <div class="inline required field {{if .Err_Owner}}error{{end}}">
- <label>{{ctx.Locale.Tr "repo.owner"}}</label>
- <div class="ui selection owner dropdown">
- <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
- <span class="text truncated-item-container" title="{{.ContextUser.Name}}">
- {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
- <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
- </span>
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- <div class="menu" title="{{.SignedUser.Name}}">
- <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
- {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
- <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
- </div>
- {{range .Orgs}}
- <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
- {{ctx.AvatarUtils.Avatar . 28 "mini"}}
- <span class="truncated-item-name">{{.ShortName 40}}</span>
- </div>
- {{end}}
+ <div class="inline required field {{if .Err_Owner}}error{{end}}">
+ <label>{{ctx.Locale.Tr "repo.owner"}}</label>
+ <div class="ui selection owner dropdown ellipsis-text-items">
+ <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
+ <span class="text" title="{{.ContextUser.Name}}">
+ {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
+ {{.ContextUser.ShortName 40}}
+ </span>
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ <div class="menu" title="{{.SignedUser.Name}}">
+ <div class="item" data-value="{{.SignedUser.ID}}">
+ {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
+ {{.SignedUser.ShortName 40}}
</div>
- </div>
- </div>
-
- <div class="inline required field {{if .Err_RepoName}}error{{end}}">
- <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
- <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
- </div>
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
- <div class="ui checkbox">
- {{if .IsForcedPrivate}}
- <input name="private" type="checkbox" checked disabled>
- <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
- {{else}}
- <input name="private" type="checkbox" {{if .private}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ {{range .Orgs}}
+ <div class="item" data-value="{{.ID}}" title="{{.Name}}">
+ {{ctx.AvatarUtils.Avatar . 28 "mini"}}
+ {{.ShortName 40}}
+ </div>
{{end}}
</div>
</div>
- <div class="inline field {{if .Err_Description}}error{{end}}">
- <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
- <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
- </div>
+ </div>
- <div class="inline field">
- <label></label>
- <button class="ui primary button">
- {{ctx.Locale.Tr "repo.migrate_repo"}}
- </button>
+ <div class="inline required field {{if .Err_RepoName}}error{{end}}">
+ <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
+ <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
+ </div>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
+ <div class="ui checkbox">
+ {{if .IsForcedPrivate}}
+ <input name="private" type="checkbox" checked disabled>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
+ {{else}}
+ <input name="private" type="checkbox" {{if .private}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ {{end}}
</div>
</div>
+ <div class="inline field {{if .Err_Description}}error{{end}}">
+ <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
+ <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
+ </div>
+
+ <div class="inline field">
+ <label></label>
+ <button class="ui primary button">
+ {{ctx.Locale.Tr "repo.migrate_repo"}}
+ </button>
+ </div>
</form>
</div>
</div>
diff --git a/templates/repo/migrate/gogs.tmpl b/templates/repo/migrate/gogs.tmpl
index eca83b1636..9149c43239 100644
--- a/templates/repo/migrate/gogs.tmpl
+++ b/templates/repo/migrate/gogs.tmpl
@@ -1,128 +1,128 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository new migrate">
- <div class="ui middle very relaxed page grid">
- <div class="column">
- <form class="ui form" action="{{.Link}}" method="post">
+ <div class="ui container medium-width">
+ <h3 class="ui top attached header">
+ {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
+ </h3>
+ <div class="ui attached segment">
+ {{template "base/alert" .}}
+ <form class="ui form left-right-form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
- <h3 class="ui top attached header">
- {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
- <input id="service_type" type="hidden" name="service" value="{{.service}}">
- </h3>
- <div class="ui attached segment">
- {{template "base/alert" .}}
- <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
- <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
- <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
- <span class="help">
- {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
- </span>
- </div>
- <div class="inline field {{if .Err_Auth}}error{{end}}">
- <label for="auth_token">{{ctx.Locale.Tr "access_token"}}</label>
- <input id="auth_token" name="auth_token" type="password" autocomplete="new-password" value="{{.auth_token}}" {{if not .auth_token}} data-need-clear="true" {{end}}>
- <!-- <a target="_blank" href="https://docs.gitea.com/development/api-usage">{{svg "octicon-question"}}</a> -->
- </div>
+ <input id="service_type" type="hidden" name="service" value="{{.service}}">
- {{template "repo/migrate/options" .}}
+ <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
+ <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
+ <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
+ <span class="help">
+ {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
+ </span>
+ </div>
+
+ <div class="inline field {{if .Err_Auth}}error{{end}}">
+ <label for="auth_token">{{ctx.Locale.Tr "access_token"}}</label>
+ <input id="auth_token" name="auth_token" type="password" autocomplete="new-password" value="{{.auth_token}}" {{if not .auth_token}} data-need-clear="true" {{end}}>
+ <!-- <a target="_blank" href="https://docs.gitea.com/development/api-usage">{{svg "octicon-question"}}</a> -->
+ </div>
+ {{template "repo/migrate/options" .}}
+
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
+ <div class="ui checkbox">
+ <input name="wiki" type="checkbox" {{if .wiki}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_wiki"}}</label>
+ </div>
+ </div>
+
+ <div id="migrate_items" class="inline field">
+ <span class="help">{{ctx.Locale.Tr "repo.migrate.migrate_items_options"}}</span>
<div class="inline field">
- <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
+ <label></label>
<div class="ui checkbox">
- <input name="wiki" type="checkbox" {{if .wiki}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_wiki"}}</label>
+ <input name="labels" type="checkbox" {{if .labels}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_labels"}}</label>
+ </div>
+ <div class="ui checkbox">
+ <input name="issues" type="checkbox" {{if .issues}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_issues"}}</label>
</div>
</div>
-
- <div id="migrate_items">
- <span class="help">{{ctx.Locale.Tr "repo.migrate.migrate_items_options"}}</span>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="labels" type="checkbox" {{if .labels}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_labels"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="issues" type="checkbox" {{if .issues}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_issues"}}</label>
- </div>
+ <div class="inline field">
+ <label></label>
+ <div class="ui checkbox">
+ <input name="milestones" type="checkbox" {{if .milestones}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_milestones"}}</label>
</div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="milestones" type="checkbox" {{if .milestones}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_milestones"}}</label>
- </div>
+ </div>
+ <!-- Gogs do not support it
+ <div class="inline field">
+ <label></label>
+ <div class="ui checkbox">
+ <input name="pull_requests" type="checkbox" {{if .pull_requests}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_merge_requests"}}</label>
</div>
- <!-- Gogs do not support it
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="pull_requests" type="checkbox" {{if .pull_requests}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_merge_requests"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="releases" type="checkbox" {{if .releases}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_releases"}}</label>
- </div>
+ <div class="ui checkbox">
+ <input name="releases" type="checkbox" {{if .releases}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_releases"}}</label>
</div>
- -->
</div>
+ -->
+ </div>
- <div class="divider"></div>
+ <div class="divider"></div>
- <div class="inline required field {{if .Err_Owner}}error{{end}}">
- <label>{{ctx.Locale.Tr "repo.owner"}}</label>
- <div class="ui selection owner dropdown">
- <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
- <span class="text truncated-item-container" title="{{.ContextUser.Name}}">
- {{ctx.AvatarUtils.Avatar .ContextUser}}
- <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
- </span>
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- <div class="menu" title="{{.SignedUser.Name}}">
- <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
- {{ctx.AvatarUtils.Avatar .SignedUser}}
- <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
- </div>
- {{range .Orgs}}
- <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
- {{ctx.AvatarUtils.Avatar .}}
- <span class="truncated-item-name">{{.ShortName 40}}</span>
- </div>
- {{end}}
+ <div class="inline required field {{if .Err_Owner}}error{{end}}">
+ <label>{{ctx.Locale.Tr "repo.owner"}}</label>
+ <div class="ui selection owner dropdown ellipsis-text-items">
+ <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
+ <span class="text" title="{{.ContextUser.Name}}">
+ {{ctx.AvatarUtils.Avatar .ContextUser}}
+ {{.ContextUser.ShortName 40}}
+ </span>
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ <div class="menu" title="{{.SignedUser.Name}}">
+ <div class="item" data-value="{{.SignedUser.ID}}">
+ {{ctx.AvatarUtils.Avatar .SignedUser}}
+ {{.SignedUser.ShortName 40}}
+ </div>
+ {{range .Orgs}}
+ <div class="item" data-value="{{.ID}}" title="{{.Name}}">
+ {{ctx.AvatarUtils.Avatar .}}
+ {{.ShortName 40}}
</div>
- </div>
- </div>
-
- <div class="inline required field {{if .Err_RepoName}}error{{end}}">
- <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
- <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
- </div>
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
- <div class="ui checkbox">
- {{if .IsForcedPrivate}}
- <input name="private" type="checkbox" checked disabled>
- <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
- {{else}}
- <input name="private" type="checkbox" {{if .private}} checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
{{end}}
</div>
</div>
- <div class="inline field {{if .Err_Description}}error{{end}}">
- <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
- <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
- </div>
+ </div>
- <div class="inline field">
- <label></label>
- <button class="ui primary button">
- {{ctx.Locale.Tr "repo.migrate_repo"}}
- </button>
+ <div class="inline required field {{if .Err_RepoName}}error{{end}}">
+ <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
+ <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
+ </div>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
+ <div class="ui checkbox">
+ {{if .IsForcedPrivate}}
+ <input name="private" type="checkbox" checked disabled>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
+ {{else}}
+ <input name="private" type="checkbox" {{if .private}} checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ {{end}}
</div>
</div>
+ <div class="inline field {{if .Err_Description}}error{{end}}">
+ <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
+ <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
+ </div>
+
+ <div class="inline field">
+ <label></label>
+ <button class="ui primary button">
+ {{ctx.Locale.Tr "repo.migrate_repo"}}
+ </button>
+ </div>
</form>
</div>
</div>
diff --git a/templates/repo/migrate/migrate.tmpl b/templates/repo/migrate/migrate.tmpl
index c5c697edff..79ac5cc8a1 100644
--- a/templates/repo/migrate/migrate.tmpl
+++ b/templates/repo/migrate/migrate.tmpl
@@ -19,7 +19,7 @@
<div class="header tw-text-center">
{{.Title}}
</div>
- <div class="description tw-text-center">
+ <div class="description tw-text-center tw-text-balance">
{{ctx.Locale.Tr (printf "repo.migrate.%s.description" .Name)}}
</div>
</div>
diff --git a/templates/repo/migrate/migrating.tmpl b/templates/repo/migrate/migrating.tmpl
index bc07b488f2..e73e3a9790 100644
--- a/templates/repo/migrate/migrating.tmpl
+++ b/templates/repo/migrate/migrating.tmpl
@@ -7,19 +7,19 @@
{{template "base/alert" .}}
<div class="home">
<div class="ui stackable middle very relaxed page grid">
- <div id="repo_migrating" class="sixteen wide center aligned centered column" data-migrating-repo-link="{{.Link}}">
+ <div id="repo_migrating" class="sixteen wide tw-text-center centered column" data-migrating-repo-link="{{.Link}}">
<div>
- <img src="{{AssetUrlPrefix}}/img/loading.png">
+ <img alt src="{{AssetUrlPrefix}}/img/loading.png">
</div>
</div>
- <div id="repo_migrating_failed_image" class="sixteen wide center aligned centered column tw-hidden">
+ <div id="repo_migrating_failed_image" class="sixteen wide tw-text-center centered column tw-hidden">
<div>
- <img src="{{AssetUrlPrefix}}/img/failed.png">
+ <img alt src="{{AssetUrlPrefix}}/img/failed.png">
</div>
</div>
</div>
<div class="ui stackable middle very relaxed page grid">
- <div class="sixteen wide center aligned centered column">
+ <div class="sixteen wide tw-text-center centered column">
<div id="repo_migrating_progress">
<p>{{ctx.Locale.Tr "repo.migrate.migrating" .CloneAddr}}</p>
<p id="repo_migrating_progress_message"></p>
@@ -77,7 +77,7 @@
<input id="repo_name_to_delete" name="repo_name" required>
</div>
- <div class="text right actions">
+ <div class="actions">
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
<button class="ui red button">{{ctx.Locale.Tr "repo.settings.confirm_delete"}}</button>
</div>
diff --git a/templates/repo/migrate/onedev.tmpl b/templates/repo/migrate/onedev.tmpl
index e1aad96ba4..a25ccf9438 100644
--- a/templates/repo/migrate/onedev.tmpl
+++ b/templates/repo/migrate/onedev.tmpl
@@ -1,114 +1,115 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository new migrate">
- <div class="ui middle very relaxed page grid">
- <div class="column">
- <form class="ui form" action="{{.Link}}" method="post">
+ <div class="ui container medium-width">
+ <h3 class="ui top attached header">
+ {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
+ </h3>
+ <div class="ui attached segment">
+ {{template "base/alert" .}}
+ <form class="ui form left-right-form" action="{{.Link}}" method="post">
{{template "base/disable_form_autofill"}}
{{.CsrfTokenHtml}}
- <h3 class="ui top attached header">
- {{ctx.Locale.Tr "repo.migrate.migrate" .service.Title}}
- <input id="service_type" type="hidden" name="service" value="{{.service}}">
- </h3>
- <div class="ui attached segment">
- {{template "base/alert" .}}
- <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
- <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
- <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
- <span class="help">
- {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
- </span>
- </div>
- <div class="inline field {{if .Err_Auth}}error{{end}}">
- <label for="auth_username">{{ctx.Locale.Tr "username"}}</label>
- <input id="auth_username" name="auth_username" value="{{.auth_username}}" {{if not .auth_username}}data-need-clear="true"{{end}}>
- </div>
- <div class="inline field {{if .Err_Auth}}error{{end}}">
- <label for="auth_password">{{ctx.Locale.Tr "password"}}</label>
- <input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}">
- </div>
+ <input id="service_type" type="hidden" name="service" value="{{.service}}">
- {{template "repo/migrate/options" .}}
+ <div class="inline required field {{if .Err_CloneAddr}}error{{end}}">
+ <label for="clone_addr">{{ctx.Locale.Tr "repo.migrate.clone_address"}}</label>
+ <input id="clone_addr" name="clone_addr" value="{{.clone_addr}}" autofocus required>
+ <span class="help">
+ {{ctx.Locale.Tr "repo.migrate.clone_address_desc"}}{{if .ContextUser.CanImportLocal}} {{ctx.Locale.Tr "repo.migrate.clone_local_path"}}{{end}}
+ </span>
+ </div>
- <div id="migrate_items">
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
- <div class="ui checkbox">
- <input name="milestones" type="checkbox" {{if .milestones}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_milestones"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_labels"}}</label>
- </div>
- </div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <input name="issues" type="checkbox" {{if .issues}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_issues"}}</label>
- </div>
- <div class="ui checkbox">
- <input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.migrate_items_pullrequests"}}</label>
- </div>
- </div>
- </div>
+ <div class="inline field {{if .Err_Auth}}error{{end}}">
+ <label for="auth_username">{{ctx.Locale.Tr "username"}}</label>
+ <input id="auth_username" name="auth_username" value="{{.auth_username}}" {{if not .auth_username}}data-need-clear="true"{{end}}>
+ </div>
+ <div class="inline field {{if .Err_Auth}}error{{end}}">
+ <label for="auth_password">{{ctx.Locale.Tr "password"}}</label>
+ <input id="auth_password" name="auth_password" type="password" value="{{.auth_password}}">
+ </div>
- <div class="divider"></div>
+ {{template "repo/migrate/options" .}}
- <div class="inline required field {{if .Err_Owner}}error{{end}}">
- <label>{{ctx.Locale.Tr "repo.owner"}}</label>
- <div class="ui selection owner dropdown">
- <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
- <span class="text truncated-item-container" title="{{.ContextUser.Name}}">
- {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
- <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
- </span>
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- <div class="menu" title="{{.SignedUser.Name}}">
- <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}">
- {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
- <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
- </div>
- {{range .Orgs}}
- <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
- {{ctx.AvatarUtils.Avatar . 28 "mini"}}
- <span class="truncated-item-name">{{.ShortName 40}}</span>
- </div>
- {{end}}
- </div>
+ <div id="migrate_items" class="inline field">
+ <label></label>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.migrate_items"}}</label>
+ <div class="ui checkbox">
+ <input name="milestones" type="checkbox" {{if .milestones}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_milestones"}}</label>
+ </div>
+ <div class="ui checkbox">
+ <input name="labels" type="checkbox" {{if .labels}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_labels"}}</label>
</div>
- </div>
-
- <div class="inline required field {{if .Err_RepoName}}error{{end}}">
- <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
- <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
</div>
<div class="inline field">
- <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
+ <label></label>
<div class="ui checkbox">
- {{if .IsForcedPrivate}}
- <input name="private" type="checkbox" checked disabled>
- <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
- {{else}}
- <input name="private" type="checkbox" {{if .private}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
- {{end}}
+ <input name="issues" type="checkbox" {{if .issues}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_issues"}}</label>
+ </div>
+ <div class="ui checkbox">
+ <input name="pull_requests" type="checkbox" {{if .pull_requests}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.migrate_items_pullrequests"}}</label>
</div>
</div>
- <div class="inline field {{if .Err_Description}}error{{end}}">
- <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
- <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
+ </div>
+
+ <div class="divider"></div>
+
+ <div class="inline required field {{if .Err_Owner}}error{{end}}">
+ <label>{{ctx.Locale.Tr "repo.owner"}}</label>
+ <div class="ui selection owner dropdown ellipsis-text-items">
+ <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
+ <span class="text" title="{{.ContextUser.Name}}">
+ {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
+ {{.ContextUser.ShortName 40}}
+ </span>
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ <div class="menu" title="{{.SignedUser.Name}}">
+ <div class="item" data-value="{{.SignedUser.ID}}">
+ {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
+ {{.SignedUser.ShortName 40}}
+ </div>
+ {{range .Orgs}}
+ <div class="item" data-value="{{.ID}}" title="{{.Name}}">
+ {{ctx.AvatarUtils.Avatar . 28 "mini"}}
+ {{.ShortName 40}}
+ </div>
+ {{end}}
+ </div>
</div>
+ </div>
- <div class="inline field">
- <label></label>
- <button class="ui primary button">
- {{ctx.Locale.Tr "repo.migrate_repo"}}
- </button>
+ <div class="inline required field {{if .Err_RepoName}}error{{end}}">
+ <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
+ <input id="repo_name" name="repo_name" value="{{.repo_name}}" required maxlength="100">
+ </div>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
+ <div class="ui checkbox">
+ {{if .IsForcedPrivate}}
+ <input name="private" type="checkbox" checked disabled>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper_forced"}}</label>
+ {{else}}
+ <input name="private" type="checkbox" {{if .private}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ {{end}}
</div>
</div>
+ <div class="inline field {{if .Err_Description}}error{{end}}">
+ <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
+ <textarea id="description" name="description" maxlength="2048">{{.description}}</textarea>
+ </div>
+
+ <div class="inline field">
+ <label></label>
+ <button class="ui primary button">
+ {{ctx.Locale.Tr "repo.migrate_repo"}}
+ </button>
+ </div>
</form>
</div>
</div>
diff --git a/templates/repo/navbar.tmpl b/templates/repo/navbar.tmpl
index b2471dc17e..e004c5254b 100644
--- a/templates/repo/navbar.tmpl
+++ b/templates/repo/navbar.tmpl
@@ -1,14 +1,19 @@
+{{$canReadCode := $.Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
+
<div class="ui fluid vertical menu">
+ {{/* the default activity page "pulse" could work with any permission: code, issue, pr, release*/}}
<a class="{{if .PageIsPulse}}active {{end}}item" href="{{.RepoLink}}/activity">
{{ctx.Locale.Tr "repo.activity.navbar.pulse"}}
</a>
- <a class="{{if .PageIsContributors}}active {{end}}item" href="{{.RepoLink}}/activity/contributors">
- {{ctx.Locale.Tr "repo.activity.navbar.contributors"}}
- </a>
- <a class="{{if .PageIsCodeFrequency}}active{{end}} item" href="{{.RepoLink}}/activity/code-frequency">
- {{ctx.Locale.Tr "repo.activity.navbar.code_frequency"}}
- </a>
- <a class="{{if .PageIsRecentCommits}}active{{end}} item" href="{{.RepoLink}}/activity/recent-commits">
- {{ctx.Locale.Tr "repo.activity.navbar.recent_commits"}}
- </a>
+ {{if $canReadCode}}
+ <a class="{{if .PageIsContributors}}active {{end}}item" href="{{.RepoLink}}/activity/contributors">
+ {{ctx.Locale.Tr "repo.activity.navbar.contributors"}}
+ </a>
+ <a class="{{if .PageIsCodeFrequency}}active{{end}} item" href="{{.RepoLink}}/activity/code-frequency">
+ {{ctx.Locale.Tr "repo.activity.navbar.code_frequency"}}
+ </a>
+ <a class="{{if .PageIsRecentCommits}}active{{end}} item" href="{{.RepoLink}}/activity/recent-commits">
+ {{ctx.Locale.Tr "repo.activity.navbar.recent_commits"}}
+ </a>
+ {{end}}
</div>
diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl
index 05ad7264bf..1acaaf8b4b 100644
--- a/templates/repo/projects/view.tmpl
+++ b/templates/repo/projects/view.tmpl
@@ -2,14 +2,13 @@
<div role="main" aria-label="{{.Title}}" class="page-content repository projects view-project">
{{template "repo/header" .}}
<div class="ui container padded">
- <div class="tw-flex tw-justify-between tw-items-center tw-mb-4">
- {{template "repo/issue/navbar" .}}
+ <div class="flex-text-block tw-justify-end tw-mb-4">
+ <a class="ui small button" href="{{.RepoLink}}/labels">{{ctx.Locale.Tr "repo.labels"}}</a>
+ <a class="ui small button" href="{{.RepoLink}}/milestones">{{ctx.Locale.Tr "repo.milestones"}}</a>
<a class="ui small primary button" href="{{.RepoLink}}/issues/new/choose?project={{.Project.ID}}">{{ctx.Locale.Tr "repo.issues.new"}}</a>
</div>
</div>
- <div class="ui container fluid padded">
- {{template "projects/view" .}}
- </div>
+ {{template "projects/view" .}}
</div>
{{template "base/footer" .}}
diff --git a/templates/repo/pulls/fork.tmpl b/templates/repo/pulls/fork.tmpl
index 7af535f1d3..0d775ed6a0 100644
--- a/templates/repo/pulls/fork.tmpl
+++ b/templates/repo/pulls/fork.tmpl
@@ -1,85 +1,83 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content repository new fork">
- <div class="ui middle very relaxed page grid">
- <div class="column">
- <form class="ui form" action="{{.Link}}" method="post">
+ <div class="ui container medium-width">
+ <h3 class="ui top attached header">
+ {{ctx.Locale.Tr "new_fork"}}
+ </h3>
+ <div class="ui attached segment">
+ {{template "base/alert" .}}
+ <form class="ui form form-fetch-action left-right-form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
- <h3 class="ui top attached header">
- {{ctx.Locale.Tr "new_fork"}}
- </h3>
- <div class="ui attached segment">
- {{template "base/alert" .}}
- <div class="inline required field {{if .Err_Owner}}error{{end}}">
- <label>{{ctx.Locale.Tr "repo.owner"}}</label>
- <div class="ui selection owner dropdown">
- <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
- <span class="text truncated-item-container" title="{{.ContextUser.Name}}">
- {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
- <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
- </span>
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- <div class="menu">
- {{if .CanForkToUser}}
- <div class="item truncated-item-container" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}">
- {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
- <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
- </div>
- {{end}}
- {{range .Orgs}}
- <div class="item truncated-item-container" data-value="{{.ID}}" title="{{.Name}}">
- {{ctx.AvatarUtils.Avatar . 28 "mini"}}
- <span class="truncated-item-name">{{.ShortName 40}}</span>
- </div>
- {{end}}
- </div>
+ <div class="inline required field {{if .Err_Owner}}error{{end}}">
+ <label>{{ctx.Locale.Tr "repo.owner"}}</label>
+ <div class="ui selection owner dropdown ellipsis-text-items">
+ <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
+ <span class="text" title="{{.ContextUser.Name}}">
+ {{ctx.AvatarUtils.Avatar .ContextUser 28 "mini"}}
+ {{.ContextUser.ShortName 40}}
+ </span>
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ <div class="menu">
+ {{if .CanForkToUser}}
+ <div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}">
+ {{ctx.AvatarUtils.Avatar .SignedUser 28 "mini"}}
+ {{.SignedUser.ShortName 40}}
+ </div>
+ {{end}}
+ {{range .Orgs}}
+ <div class="item" data-value="{{.ID}}" title="{{.Name}}">
+ {{ctx.AvatarUtils.Avatar . 28 "mini"}}
+ {{.ShortName 40}}
+ </div>
+ {{end}}
</div>
</div>
+ </div>
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.fork_from"}}</label>
- <a href="{{.ForkRepo.Link}}" class="tw-inline-block">{{.ForkRepo.FullName}}</a>
- </div>
- <div class="inline required field {{if .Err_RepoName}}error{{end}}">
- <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
- <input id="repo_name" name="repo_name" value="{{.repo_name}}" required>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.fork_from"}}</label>
+ <a href="{{.ForkRepo.Link}}" class="tw-inline-block">{{.ForkRepo.FullName}}</a>
+ </div>
+ <div class="inline required field {{if .Err_RepoName}}error{{end}}">
+ <label for="repo_name">{{ctx.Locale.Tr "repo.repo_name"}}</label>
+ <input id="repo_name" name="repo_name" value="{{.repo_name}}" required>
+ </div>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
+ <div class="ui disabled checkbox">
+ <input type="checkbox" disabled {{if .IsPrivate}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
</div>
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.visibility"}}</label>
- <div class="ui disabled checkbox">
- <input type="checkbox" disabled {{if .IsPrivate}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.visibility_helper"}}</label>
+ <span class="help">{{ctx.Locale.Tr "repo.fork_visibility_helper"}}</span>
+ </div>
+ <div class="inline field">
+ <label>{{ctx.Locale.Tr "repo.fork_branch"}}</label>
+ <div class="ui selection dropdown ellipsis-text-items">
+ <input type="hidden" id="fork_single_branch" name="fork_single_branch" value="" required>
+ <div class="text" title="{{ctx.Locale.Tr "repo.all_branches"}}">
+ {{ctx.Locale.Tr "repo.all_branches"}}
</div>
- <span class="help">{{ctx.Locale.Tr "repo.fork_visibility_helper"}}</span>
- </div>
- <div class="inline field">
- <label>{{ctx.Locale.Tr "repo.fork_branch"}}</label>
- <div class="ui selection dropdown ellipsis-items-nowrap">
- <input type="hidden" id="fork_single_branch" name="fork_single_branch" value="" required>
- <div class="text" title="{{ctx.Locale.Tr "repo.all_branches"}}">
- <span class="truncated-item-name">{{ctx.Locale.Tr "repo.all_branches"}}</span>
- </div>
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- <div class="menu">
- <div class="item" data-value="" title="{{ctx.Locale.Tr "repo.all_branches"}}">
- {{ctx.Locale.Tr "repo.all_branches"}}
- </div>
- {{range .Branches}}
- <div class="item" data-value="{{.}}" title="{{.}}">{{.}}</div>
- {{end}}
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ <div class="menu">
+ <div class="item" data-value="" title="{{ctx.Locale.Tr "repo.all_branches"}}">
+ {{ctx.Locale.Tr "repo.all_branches"}}
</div>
+ {{range .Branches}}
+ <div class="item" data-value="{{.}}" title="{{.}}">{{.}}</div>
+ {{end}}
</div>
</div>
- <div class="inline field {{if .Err_Description}}error{{end}}">
- <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
- <textarea id="description" name="description">{{.description}}</textarea>
- </div>
+ </div>
+ <div class="inline field {{if .Err_Description}}error{{end}}">
+ <label for="description">{{ctx.Locale.Tr "repo.repo_desc"}}</label>
+ <textarea id="description" name="description">{{.description}}</textarea>
+ </div>
- <div class="inline field">
- <label></label>
- <button class="ui primary button{{if not .CanForkRepo}} disabled{{end}}">
- {{ctx.Locale.Tr "repo.fork_repo"}}
- </button>
- </div>
+ <div class="inline field">
+ <label></label>
+ <button class="ui primary button{{if not .CanForkRepoInNewOwner}} disabled{{end}}">
+ {{ctx.Locale.Tr "repo.fork_repo"}}
+ </button>
</div>
</form>
</div>
diff --git a/templates/repo/pulls/tab_menu.tmpl b/templates/repo/pulls/tab_menu.tmpl
index 8b192c44db..a0ecdf96cd 100644
--- a/templates/repo/pulls/tab_menu.tmpl
+++ b/templates/repo/pulls/tab_menu.tmpl
@@ -15,11 +15,11 @@
{{template "shared/misc/tabtitle" (ctx.Locale.Tr "repo.pulls.tab_files")}}
<span class="ui small label">{{if .NumFiles}}{{.NumFiles}}{{else}}-{{end}}</span>
</a>
- {{if or .Diff.TotalAddition .Diff.TotalDeletion}}
+ {{if or .DiffShortStat.TotalAddition .DiffShortStat.TotalDeletion}}
<span class="tw-ml-auto tw-pl-3 tw-whitespace-nowrap tw-pr-0 tw-font-bold tw-flex tw-items-center tw-gap-2">
- <span><span class="text green">{{if .Diff.TotalAddition}}+{{.Diff.TotalAddition}}{{end}}</span> <span class="text red">{{if .Diff.TotalDeletion}}-{{.Diff.TotalDeletion}}{{end}}</span></span>
+ <span><span class="text green">{{if .DiffShortStat.TotalAddition}}+{{.DiffShortStat.TotalAddition}}{{end}}</span> <span class="text red">{{if .DiffShortStat.TotalDeletion}}-{{.DiffShortStat.TotalDeletion}}{{end}}</span></span>
<span class="diff-stats-bar">
- <div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .Diff.TotalAddition "/" "(" .Diff.TotalAddition "+" .Diff.TotalDeletion "+" 0.0 ")"}}%"></div>
+ <div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .DiffShortStat.TotalAddition "/" "(" .DiffShortStat.TotalAddition "+" .DiffShortStat.TotalDeletion "+" 0.0 ")"}}%"></div>
</span>
</span>
{{end}}
diff --git a/templates/repo/pulse.tmpl b/templates/repo/pulse.tmpl
index 5222633507..cbafee9ba9 100644
--- a/templates/repo/pulse.tmpl
+++ b/templates/repo/pulse.tmpl
@@ -1,12 +1,12 @@
<h2 class="ui header activity-header">
<span>{{DateUtils.AbsoluteLong .DateFrom}} - {{DateUtils.AbsoluteLong .DateUntil}}</span>
<!-- Period -->
- <div class="ui floating dropdown jump filter">
+ <div class="ui floating dropdown jump">
<div class="ui basic compact button">
{{ctx.Locale.Tr "repo.activity.period.filter_label"}} <strong>{{.PeriodText}}</strong>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
</div>
- <div class="menu">
+ <div class="left menu">
<a class="{{if eq .Period "daily"}}active {{end}}item" href="{{$.RepoLink}}/activity/daily">{{ctx.Locale.Tr "repo.activity.period.daily"}}</a>
<a class="{{if eq .Period "halfweekly"}}active {{end}}item" href="{{$.RepoLink}}/activity/halfweekly">{{ctx.Locale.Tr "repo.activity.period.halfweekly"}}</a>
<a class="{{if eq .Period "weekly"}}active {{end}}item" href="{{$.RepoLink}}/activity/weekly">{{ctx.Locale.Tr "repo.activity.period.weekly"}}</a>
@@ -49,7 +49,7 @@
</div>
{{else}}
<div class="stats-table">
- <a class="table-cell tiny background light grey"></a>
+ <a class="table-cell tiny tw-bg-grey"></a>
</div>
{{end}}
{{ctx.Locale.TrN .Activity.ActiveIssueCount "repo.activity.active_issues_count_1" "repo.activity.active_issues_count_n" .Activity.ActiveIssueCount}}
@@ -82,7 +82,7 @@
{{if .Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
{{if eq .Activity.Code.CommitCountInAllBranches 0}}
- <div class="ui center aligned segment">
+ <div class="ui tw-text-center segment">
<h4 class="ui header">{{ctx.Locale.Tr "repo.activity.no_git_activity"}}</h4>
</div>
{{end}}
diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl
index 99934d2118..882ffe40b7 100644
--- a/templates/repo/release/list.tmpl
+++ b/templates/repo/release/list.tmpl
@@ -21,10 +21,11 @@
{{$compareTarget = $release.Sha1}}
{{end}}
{{template "repo/branch_dropdown" dict
+ "ContainerClasses" "release-branch-tag-selector"
"Repository" $.Repository
"ShowTabTags" true
"DropdownFixedText" (ctx.Locale.Tr "repo.release.compare")
- "RefLinkTemplate" (print "{RepoLink}/compare/{RefShortName}..." (PathEscapeSegments $compareTarget))
+ "RefLinkTemplate" (print "{RepoLink}/compare/{RefShortName}" "..." (PathEscapeSegments $compareTarget))
}}
{{end}}
</div>
@@ -49,7 +50,11 @@
{{svg (MigrationIcon $release.Repo.GetOriginalURLHostname) 20 "tw-mr-1"}}{{$release.OriginalAuthor}}
{{else if $release.Publisher}}
{{ctx.AvatarUtils.Avatar $release.Publisher 20 "tw-mr-1"}}
- <a href="{{$release.Publisher.HomeLink}}">{{$release.Publisher.GetDisplayName}}</a>
+ {{if gt $release.PublisherID 0}}
+ <a href="{{$release.Publisher.HomeLink}}">{{$release.Publisher.GetDisplayName}}</a>
+ {{else}}
+ {{$release.Publisher.GetDisplayName}}
+ {{end}}
{{else}}
Ghost
{{end}}
@@ -60,11 +65,11 @@
{{if $release.CreatedUnix}}
<span class="time">{{DateUtils.TimeSince $release.CreatedUnix}}</span>
{{end}}
- {{if and (not $release.IsDraft) ($.Permission.CanRead ctx.Consts.RepoUnitTypeCode)}}
+ {{if and (gt $release.NumCommits 0) (not $release.IsDraft) ($.Permission.CanRead ctx.Consts.RepoUnitTypeCode)}}
| <span class="ahead"><a href="{{$.RepoLink}}/compare/{{$release.TagName | PathEscapeSegments}}...{{$release.TargetBehind | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.release.ahead.commits" $release.NumCommitsBehind}}</a> {{ctx.Locale.Tr "repo.release.ahead.target" $release.TargetBehind}}</span>
{{end}}
</p>
- <div class="markup desc">
+ <div class="render-content markup">
{{$release.RenderedNote}}
</div>
<div class="divider"></div>
@@ -72,25 +77,31 @@
<summary>
{{ctx.Locale.Tr "repo.release.downloads"}}
</summary>
- <ul class="list">
+ <ul class="ui divided list attachment-list">
{{if and (not $.DisableDownloadSourceArchives) (not $release.IsDraft) ($.Permission.CanRead ctx.Consts.RepoUnitTypeCode)}}
- <li>
- <a class="archive-link" href="{{$.RepoLink}}/archive/{{$release.TagName | PathEscapeSegments}}.zip" rel="nofollow"><strong>{{svg "octicon-file-zip" 16 "download-icon"}}{{ctx.Locale.Tr "repo.release.source_code"}} (ZIP)</strong></a>
+ <li class="item">
+ <a class="archive-link" download href="{{$.RepoLink}}/archive/{{$release.TagName | PathEscapeSegments}}.zip" rel="nofollow">
+ <strong class="flex-text-inline">{{svg "octicon-file-zip" 16 "download-icon"}}{{ctx.Locale.Tr "repo.release.source_code"}} (ZIP)</strong>
+ </a>
</li>
- <li>
- <a class="archive-link" href="{{$.RepoLink}}/archive/{{$release.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow"><strong>{{svg "octicon-file-zip" 16 "download-icon"}}{{ctx.Locale.Tr "repo.release.source_code"}} (TAR.GZ)</strong></a>
+ <li class="item">
+ <a class="archive-link" download href="{{$.RepoLink}}/archive/{{$release.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow">
+ <strong class="flex-text-inline">{{svg "octicon-file-zip" 16 "download-icon"}}{{ctx.Locale.Tr "repo.release.source_code"}} (TAR.GZ)</strong>
+ </a>
</li>
{{end}}
- {{range $release.Attachments}}
- <li>
- <a target="_blank" rel="nofollow" href="{{.DownloadURL}}" download>
- <strong>{{svg "octicon-package" 16 "download-icon"}}{{.Name}}</strong>
+ {{range $att := $release.Attachments}}
+ <li class="item">
+ <a target="_blank" class="tw-flex-1 gt-ellipsis" rel="nofollow" download href="{{$att.DownloadURL}}">
+ <strong class="flex-text-inline">{{svg "octicon-package" 16 "download-icon"}}<span class="gt-ellipsis">{{$att.Name}}</span></strong>
</a>
- <div>
- <span class="text grey">{{.Size | FileSize}}</span>
- <span data-tooltip-content="{{ctx.Locale.Tr "repo.release.download_count" (ctx.Locale.PrettyNumber .DownloadCount)}}">
+ <div class="attachment-right-info flex-text-inline">
+ <span class="tw-pl-5">{{$att.Size | FileSize}}</span>
+ <span class="flex-text-inline" data-tooltip-content="{{ctx.Locale.Tr "repo.release.download_count" (ctx.Locale.PrettyNumber $att.DownloadCount)}}">
{{svg "octicon-info"}}
</span>
+ <div class="tw-flex-1"></div>
+ {{DateUtils.TimeSince $att.CreatedUnix}}
</div>
</li>
{{end}}
diff --git a/templates/repo/release/new.tmpl b/templates/repo/release/new.tmpl
index 574b0d0311..109a18fa0e 100644
--- a/templates/repo/release/new.tmpl
+++ b/templates/repo/release/new.tmpl
@@ -100,32 +100,26 @@
</div>
<span class="help">{{ctx.Locale.Tr "repo.release.prerelease_helper"}}</span>
<div class="divider tw-mt-0"></div>
- <div class="tw-flex tw-justify-end">
+ <div class="flex-text-block tw-justify-end">
{{if .PageIsEditRelease}}
<a class="ui small button" href="{{.RepoLink}}/releases">
{{ctx.Locale.Tr "repo.release.cancel"}}
</a>
- <a class="ui small red button delete-button" data-url="{{$.RepoLink}}/releases/delete" data-id="{{.ID}}">
+ <a class="ui small red button link-action" data-modal-confirm="#repo-release-delete-modal" data-url="{{$.RepoLink}}/releases/delete?id={{.ID}}">
{{ctx.Locale.Tr "repo.release.delete_release"}}
</a>
{{if .IsDraft}}
- <button class="ui small button" type="submit" name="draft" value="{{ctx.Locale.Tr "repo.release.save_draft"}}">{{ctx.Locale.Tr "repo.release.save_draft"}}</button>
- <button class="ui small primary button">
- {{ctx.Locale.Tr "repo.release.publish"}}
- </button>
+ <button class="ui small button" type="submit" name="draft" value="1">{{ctx.Locale.Tr "repo.release.save_draft"}}</button>
+ <button class="ui small primary button">{{ctx.Locale.Tr "repo.release.publish"}}</button>
{{else}}
- <button class="ui small primary button">
- {{ctx.Locale.Tr "repo.release.edit_release"}}
- </button>
+ <button class="ui small primary button">{{ctx.Locale.Tr "repo.release.edit_release"}}</button>
{{end}}
{{else}}
- {{if not .tag_name}}
+ {{if .ShowCreateTagOnlyButton}}
<button class="ui small button" name="tag_only" value="1">{{ctx.Locale.Tr "repo.release.add_tag"}}</button>
{{end}}
<button class="ui small button" name="draft" value="1">{{ctx.Locale.Tr "repo.release.save_draft"}}</button>
- <button class="ui small primary button">
- {{ctx.Locale.Tr "repo.release.publish"}}
- </button>
+ <button class="ui small primary button">{{ctx.Locale.Tr "repo.release.publish"}}</button>
{{end}}
</div>
</div>
@@ -135,15 +129,11 @@
</div>
{{if .PageIsEditRelease}}
- <div class="ui g-modal-confirm delete modal">
- <div class="header">
- {{svg "octicon-trash"}}
- {{ctx.Locale.Tr "repo.release.deletion"}}
- </div>
- <div class="content">
- <p>{{ctx.Locale.Tr "repo.release.deletion_desc"}}</p>
- </div>
+ <div class="ui small modal" id="repo-release-delete-modal">
+ <div class="header">{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.release.deletion"}}</div>
+ <div class="content"><p>{{ctx.Locale.Tr "repo.release.deletion_desc"}}</p></div>
{{template "base/modal_actions_confirm" .}}
</div>
{{end}}
+
{{template "base/footer" .}}
diff --git a/templates/repo/release_tag_header.tmpl b/templates/repo/release_tag_header.tmpl
index f96c76864f..12acf4bfeb 100644
--- a/templates/repo/release_tag_header.tmpl
+++ b/templates/repo/release_tag_header.tmpl
@@ -2,7 +2,7 @@
{{$canReadCode := $.Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
{{if $canReadReleases}}
- <div class="tw-flex">
+ <div class="flex-text-block">
<div class="tw-flex-1 tw-flex tw-items-center">
<h2 class="ui compact small menu small-menu-items">
<a class="{{if and .PageIsReleaseList (not .PageIsSingleTag)}}active {{end}}item" href="{{.RepoLink}}/releases">{{ctx.Locale.PrettyNumber .NumReleases}} {{ctx.Locale.TrN .NumReleases "repo.release" "repo.releases"}}</a>
@@ -17,7 +17,7 @@
</a>
{{end}}
{{if and (not .PageIsTagList) .CanCreateRelease}}
- <a class="ui small primary button" href="{{$.RepoLink}}/releases/new{{if .PageIsSingleTag}}?tag={{.TagName}}{{end}}">
+ <a class="ui small primary button" href="{{$.RepoLink}}/releases/new{{if .PageIsSingleTag}}?tag={{.SingleReleaseTagName}}{{end}}">
{{ctx.Locale.Tr "repo.release.new_release"}}
</a>
{{end}}
diff --git a/templates/repo/settings/branches.tmpl b/templates/repo/settings/branches.tmpl
index 57d9f2c5a8..e8e7a3f1c2 100644
--- a/templates/repo/settings/branches.tmpl
+++ b/templates/repo/settings/branches.tmpl
@@ -49,14 +49,14 @@
</div>
</div>
<div class="flex-item-trailing">
- <a class="rm ui tiny button" href="{{$.Repository.Link}}/settings/branches/edit?rule_name={{.RuleName}}">{{ctx.Locale.Tr "repo.settings.edit_protected_branch"}}</a>
- <button class="ui red tiny button delete-button" data-url="{{$.Repository.Link}}/settings/branches/{{.ID}}/delete" data-id="{{.ID}}">
+ <a class="ui tiny button" href="{{$.Repository.Link}}/settings/branches/edit?rule_name={{.RuleName}}">{{ctx.Locale.Tr "repo.settings.edit_protected_branch"}}</a>
+ <button class="ui red tiny button link-action" data-modal-confirm="#repo-branch-protection-delete-modal" data-url="{{$.Repository.Link}}/settings/branches/{{.ID}}/delete?id={{.ID}}">
{{ctx.Locale.Tr "repo.settings.protected_branch.delete_rule"}}
</button>
</div>
</div>
{{else}}
- <div class="flex-item center aligned">
+ <div class="flex-item tw-text-center">
{{ctx.Locale.Tr "repo.settings.no_protected_branch"}}
</div>
{{end}}
@@ -65,14 +65,9 @@
{{end}}
</div>
-<div class="ui g-modal-confirm delete modal">
- <div class="header">
- {{svg "octicon-trash"}}
- {{ctx.Locale.Tr "repo.settings.protected_branch_deletion"}}
- </div>
- <div class="content">
- <p>{{ctx.Locale.Tr "repo.settings.protected_branch_deletion_desc"}}</p>
- </div>
+<div class="ui small modal" id="repo-branch-protection-delete-modal">
+ <div class="header">{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.settings.protected_branch_deletion"}}</div>
+ <div class="content"><p>{{ctx.Locale.Tr "repo.settings.protected_branch_deletion_desc"}}</p></div>
{{template "base/modal_actions_confirm" .}}
</div>
diff --git a/templates/repo/settings/collaboration.tmpl b/templates/repo/settings/collaboration.tmpl
index 9f90f0a2b9..62903e1cfb 100644
--- a/templates/repo/settings/collaboration.tmpl
+++ b/templates/repo/settings/collaboration.tmpl
@@ -29,7 +29,7 @@
</div>
</div>
</div>
- <button class="ui red tiny button inline delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}">
+ <button class="ui red tiny button link-action" data-modal-confirm="#repo-collaborator-delete-modal" data-url="{{$.Link}}/delete?id={{.ID}}">
{{ctx.Locale.Tr "repo.settings.delete_collaborator"}}
</button>
</div>
@@ -48,69 +48,84 @@
</form>
</div>
- {{if .RepoOwnerIsOrganization}}
- <h4 class="ui top attached header">
- {{ctx.Locale.Tr "repo.settings.teams"}}
- </h4>
- {{$allowedToChangeTeams := (or (.Org.RepoAdminChangeTeamAccess) (.Permission.IsOwner))}}
- {{if .Teams}}
- <div class="ui attached segment">
- <div class="flex-list">
- {{range $t, $team := .Teams}}
- <div class="flex-item">
- <div class="flex-item-main">
- <a class="flex-item-title text primary" href="{{AppSubUrl}}/org/{{$.OrgName|PathEscape}}/teams/{{.LowerName|PathEscape}}">
- {{.Name}}
- </a>
- <div class="flex-item-body flex-text-block">
- {{svg "octicon-shield-lock"}}
- {{if eq .AccessMode 1}}{{ctx.Locale.Tr "repo.settings.collaboration.read"}}{{else if eq .AccessMode 2}}{{ctx.Locale.Tr "repo.settings.collaboration.write"}}{{else if eq .AccessMode 3}}{{ctx.Locale.Tr "repo.settings.collaboration.admin"}}{{else if eq .AccessMode 4}}{{ctx.Locale.Tr "repo.settings.collaboration.owner"}}{{else}}{{ctx.Locale.Tr "repo.settings.collaboration.undefined"}}{{end}}
+ {{if $.Repository.Owner.IsOrganization}}
+ <h4 class="ui top attached header">
+ {{ctx.Locale.Tr "repo.settings.teams"}}
+ </h4>
+ {{$allowedToChangeTeams := (or (.Org.RepoAdminChangeTeamAccess) (.Permission.IsOwner))}}
+ {{if .Teams}}
+ <div class="ui attached segment">
+ <div class="flex-list">
+ {{range $t, $team := .Teams}}
+ <div class="flex-item">
+ <div class="flex-item-main">
+ <a class="flex-item-title text primary" href="{{AppSubUrl}}/org/{{$.OrgName|PathEscape}}/teams/{{.LowerName|PathEscape}}">
+ {{.Name}}
+ </a>
+ <div class="flex-item-body flex-text-block">
+ {{/*FIXME: TEAM-UNIT-PERMISSION this display is not right, search the fixme keyword to see more details */}}
+ {{svg "octicon-shield-lock"}}
+ {{if eq .AccessMode 0}}
+ {{ctx.Locale.Tr "repo.settings.collaboration.per_unit"}}
+ {{else if eq .AccessMode 1}}
+ {{ctx.Locale.Tr "repo.settings.collaboration.read"}}
+ {{else if eq .AccessMode 2}}
+ {{ctx.Locale.Tr "repo.settings.collaboration.write"}}
+ {{else if eq .AccessMode 3}}
+ {{ctx.Locale.Tr "repo.settings.collaboration.admin"}}
+ {{else if eq .AccessMode 4}}
+ {{ctx.Locale.Tr "repo.settings.collaboration.owner"}}
+ {{else}}
+ {{ctx.Locale.Tr "repo.settings.collaboration.undefined"}}
+ {{end}}
+ </div>
+ {{if or (eq .AccessMode 0) (eq .AccessMode 1) (eq .AccessMode 2)}}
+ {{$first := true}}
+ <div class="flex-item-body" data-tooltip-content="{{ctx.Locale.Tr "repo.settings.change_team_permission_tip"}}">
+ Units:
+ {{range $u, $unit := $.Units}}
+ {{- if and ($.Repo.UnitEnabled ctx $unit.Type) ($team.UnitEnabled ctx $unit.Type) -}}
+ {{- Iif $first "" ", "}}{{ctx.Locale.Tr $unit.NameKey -}}
+ {{- $first = false -}}
+ {{- end -}}
+ {{end}}
+ {{if $first}}None{{end}}
+ </div>
+ {{end}}
</div>
- {{if or (eq .AccessMode 1) (eq .AccessMode 2)}}
- {{$first := true}}
- <div class="flex-item-body" data-tooltip-content="{{ctx.Locale.Tr "repo.settings.change_team_permission_tip"}}">
- Sections: {{range $u, $unit := $.Units}}{{if and ($.Repo.UnitEnabled ctx $unit.Type) ($team.UnitEnabled ctx $unit.Type)}}{{if $first}}{{$first = false}}{{else}}, {{end}}{{ctx.Locale.Tr $unit.NameKey}}{{end}}{{end}} {{if $first}}None{{end}}
+ {{if $allowedToChangeTeams}}
+ <div class="flex-item-trailing" {{if .IncludesAllRepositories}} data-tooltip-content="{{ctx.Locale.Tr "repo.settings.delete_team_tip"}}"{{end}}>
+ <button class="ui red tiny button link-action {{if .IncludesAllRepositories}}disabled{{end}}" data-modal-confirm="#repo-collaborator-delete-modal" data-url="{{$.Link}}/team/delete?id={{.ID}}">
+ {{ctx.Locale.Tr "repo.settings.delete_collaborator"}}
+ </button>
</div>
{{end}}
</div>
- {{if $allowedToChangeTeams}}
- <div class="flex-item-trailing" {{if .IncludesAllRepositories}} data-tooltip-content="{{ctx.Locale.Tr "repo.settings.delete_team_tip"}}"{{end}}>
- <button class="ui red tiny button inline delete-button {{if .IncludesAllRepositories}}disabled{{end}}" data-url="{{$.Link}}/team/delete" data-id="{{.ID}}">
- {{ctx.Locale.Tr "repo.settings.delete_collaborator"}}
- </button>
- </div>
- {{end}}
+ {{end}}
+ </div>
+ </div>
+ {{end}}
+ <div class="ui bottom attached segment">
+ {{if $allowedToChangeTeams}}
+ <form class="ui form" id="repo-collab-team-form" action="{{.Link}}/team" method="post">
+ {{.CsrfTokenHtml}}
+ <div id="search-team-box" class="ui search input tw-align-middle" data-org-name="{{.OrgName}}">
+ <input class="prompt" name="team" placeholder="{{ctx.Locale.Tr "search.team_kind"}}" autocomplete="off" required>
+ </div>
+ <button class="ui primary button">{{ctx.Locale.Tr "repo.settings.add_team"}}</button>
+ </form>
+ {{else}}
+ <div class="item">
+ {{ctx.Locale.Tr "repo.settings.change_team_access_not_allowed"}}
</div>
{{end}}
</div>
- </div>
- {{end}}
- <div class="ui bottom attached segment">
- {{if $allowedToChangeTeams}}
- <form class="ui form" id="repo-collab-team-form" action="{{.Link}}/team" method="post">
- {{.CsrfTokenHtml}}
- <div id="search-team-box" class="ui search input tw-align-middle" data-org-name="{{.OrgName}}">
- <input class="prompt" name="team" placeholder="{{ctx.Locale.Tr "search.team_kind"}}" autocomplete="off" autofocus required>
- </div>
- <button class="ui primary button">{{ctx.Locale.Tr "repo.settings.add_team"}}</button>
- </form>
- {{else}}
- <div class="item">
- {{ctx.Locale.Tr "repo.settings.change_team_access_not_allowed"}}
- </div>
- {{end}}
- </div>
{{end}}
</div>
-<div class="ui g-modal-confirm delete modal">
- <div class="header">
- {{svg "octicon-trash"}}
- {{ctx.Locale.Tr "repo.settings.collaborator_deletion"}}
- </div>
- <div class="content">
- <p>{{ctx.Locale.Tr "repo.settings.collaborator_deletion_desc"}}</p>
- </div>
+<div class="ui small modal" id="repo-collaborator-delete-modal">
+ <div class="header">{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.settings.collaborator_deletion"}}</div>
+ <div class="content"><p>{{ctx.Locale.Tr "repo.settings.collaborator_deletion_desc"}}</p></div>
{{template "base/modal_actions_confirm" .}}
</div>
diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl
index 5eb2a47e5a..b82d584b22 100644
--- a/templates/repo/settings/deploy_keys.tmpl
+++ b/templates/repo/settings/deploy_keys.tmpl
@@ -59,7 +59,7 @@
</div>
</div>
<div class="flex-item-trailing">
- <button class="ui red tiny button delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}">
+ <button class="ui red tiny button link-action" data-modal-confirm="#repo-deploy-key-delete-modal" data-url="{{$.Link}}/delete?id={{.ID}}">
{{ctx.Locale.Tr "settings.delete_key"}}
</button>
</div>
@@ -72,14 +72,9 @@
</div>
</div>
-<div class="ui g-modal-confirm delete modal">
- <div class="header">
- {{svg "octicon-trash"}}
- {{ctx.Locale.Tr "repo.settings.deploy_key_deletion"}}
- </div>
- <div class="content">
- <p>{{ctx.Locale.Tr "repo.settings.deploy_key_deletion_desc"}}</p>
- </div>
+<div class="ui small modal" id="repo-deploy-key-delete-modal">
+ <div class="header">{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.settings.deploy_key_deletion"}}</div>
+ <div class="content"><p>{{ctx.Locale.Tr "repo.settings.deploy_key_deletion_desc"}}</p></div>
{{template "base/modal_actions_confirm" .}}
</div>
diff --git a/templates/repo/settings/githooks.tmpl b/templates/repo/settings/githooks.tmpl
index 1a603f9fe8..9b17af1406 100644
--- a/templates/repo/settings/githooks.tmpl
+++ b/templates/repo/settings/githooks.tmpl
@@ -4,18 +4,14 @@
{{ctx.Locale.Tr "repo.settings.githooks"}}
</h4>
<div class="ui attached segment">
- <div class="ui list">
+ <div class="ui list flex-items-block">
+ <div class="item"><span>{{ctx.Locale.Tr "repo.settings.githooks_desc"}}</span></div>
+ {{range .Hooks}}
<div class="item">
- {{ctx.Locale.Tr "repo.settings.githooks_desc"}}
+ <span class="text {{if .IsActive}}green{{else}}grey{{end}}">{{svg "octicon-dot-fill" 22}}</span>
+ <span class="gt-ellipsis tw-flex-1">{{.Name}}</span>
+ <a class="muted tw-p-2" href="{{$.RepoLink}}/settings/hooks/git/{{.Name|PathEscape}}">{{svg "octicon-pencil"}}</a>
</div>
- {{range .Hooks}}
- <div class="item truncated-item-container">
- <span class="text {{if .IsActive}}green{{else}}grey{{end}} tw-mr-2">{{svg "octicon-dot-fill" 22}}</span>
- <span class="text truncate tw-flex-1 tw-mr-2">{{.Name}}</span>
- <a class="muted tw-float-right tw-p-2" href="{{$.RepoLink}}/settings/hooks/git/{{.Name|PathEscape}}">
- {{svg "octicon-pencil"}}
- </a>
- </div>
{{end}}
</div>
</div>
diff --git a/templates/repo/settings/lfs.tmpl b/templates/repo/settings/lfs.tmpl
index c1878d2853..4b46aeed95 100644
--- a/templates/repo/settings/lfs.tmpl
+++ b/templates/repo/settings/lfs.tmpl
@@ -12,13 +12,13 @@
{{range .LFSFiles}}
<tr>
<td>
- <a href="{{$.Link}}/show/{{.Oid}}" title="{{.Oid}}" class="ui brown button tw-font-mono">
+ <a href="{{$.Link}}/show/{{.Oid}}" title="{{.Oid}}" class="ui button tw-font-mono">
{{ShortSha .Oid}}
</a>
</td>
<td>{{FileSize .Size}}</td>
<td>{{DateUtils.TimeSince .CreatedUnix}}</td>
- <td class="right aligned">
+ <td class="tw-text-right">
<a class="ui primary button" href="{{$.Link}}/find?oid={{.Oid}}&size={{.Size}}">{{ctx.Locale.Tr "repo.settings.lfs_findcommits"}}</a>
<button class="ui basic show-modal icon button red" data-modal="#delete-{{.Oid}}">
<span class="btn-octicon btn-octicon-danger" data-tooltip-content="{{ctx.Locale.Tr "repo.editor.delete_this_file"}}">{{svg "octicon-trash"}}</span>
diff --git a/templates/repo/settings/lfs_file.tmpl b/templates/repo/settings/lfs_file.tmpl
index f6fac05b69..cd1b168401 100644
--- a/templates/repo/settings/lfs_file.tmpl
+++ b/templates/repo/settings/lfs_file.tmpl
@@ -13,7 +13,7 @@
</h4>
<div class="ui bottom attached table unstackable segment">
{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
- <div class="file-view{{if .IsMarkup}} markup {{.MarkupType}}{{else if .IsPlainText}} plain-text{{else if .IsTextFile}} code-view{{end}}">
+ <div class="file-view {{if .IsPlainText}}plain-text{{else if .IsTextFile}}code-view{{end}}">
{{if .IsFileTooLarge}}
{{template "shared/filetoolarge" dict "RawFileLink" .RawFileLink}}
{{else if not .FileSize}}
@@ -21,7 +21,7 @@
{{else if not .IsTextFile}}
<div class="view-raw">
{{if .IsImageFile}}
- <img src="{{$.RawFileLink}}">
+ <img loading="lazy" alt="{{$.RawFileLink}}" src="{{$.RawFileLink}}">
{{else if .IsVideoFile}}
<video controls src="{{$.RawFileLink}}">
<strong>{{ctx.Locale.Tr "repo.video_not_supported_in_browser"}}</strong>
@@ -30,8 +30,6 @@
<audio controls src="{{$.RawFileLink}}">
<strong>{{ctx.Locale.Tr "repo.audio_not_supported_in_browser"}}</strong>
</audio>
- {{else if .IsPDFFile}}
- <div class="pdf-content is-loading" data-src="{{$.RawFileLink}}" data-fallback-button-text="{{ctx.Locale.Tr "diff.view_file"}}"></div>
{{else}}
<a href="{{$.RawFileLink}}" rel="nofollow" class="tw-p-4">{{ctx.Locale.Tr "repo.file_view_raw"}}</a>
{{end}}
diff --git a/templates/repo/settings/lfs_locks.tmpl b/templates/repo/settings/lfs_locks.tmpl
index 64c6b3a550..dfbae0633e 100644
--- a/templates/repo/settings/lfs_locks.tmpl
+++ b/templates/repo/settings/lfs_locks.tmpl
@@ -36,7 +36,7 @@
</a>
</td>
<td>{{DateUtils.TimeSince .Created}}</td>
- <td class="right aligned">
+ <td class="tw-text-right">
<form action="{{$.LFSFilesLink}}/locks/{{$lock.ID}}/unlock" method="post">
{{$.CsrfTokenHtml}}
<button class="ui primary button"><span class="btn-octicon">{{svg "octicon-lock"}}</span>{{ctx.Locale.Tr "repo.settings.lfs_force_unlock"}}</button>
diff --git a/templates/repo/settings/navbar.tmpl b/templates/repo/settings/navbar.tmpl
index 3e127ccbb3..3dd86d1f6a 100644
--- a/templates/repo/settings/navbar.tmpl
+++ b/templates/repo/settings/navbar.tmpl
@@ -4,6 +4,11 @@
<a class="{{if .PageIsSettingsOptions}}active {{end}}item" href="{{.RepoLink}}/settings">
{{ctx.Locale.Tr "repo.settings.options"}}
</a>
+ {{if or .Repository.IsPrivate .Permission.HasAnyUnitPublicAccess}}
+ <a class="{{if .PageIsSettingsPublicAccess}}active {{end}}item" href="{{.RepoLink}}/settings/public_access">
+ {{ctx.Locale.Tr "repo.settings.public_access"}}
+ </a>
+ {{end}}
<a class="{{if .PageIsSettingsCollaboration}}active {{end}}item" href="{{.RepoLink}}/settings/collaboration">
{{ctx.Locale.Tr "repo.settings.collaboration"}}
</a>
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 21eaf6a651..fc42056e0a 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -10,7 +10,7 @@
<input type="hidden" name="action" value="update">
<div class="required field {{if .Err_RepoName}}error{{end}}">
<label>{{ctx.Locale.Tr "repo.repo_name"}}</label>
- <input name="repo_name" value="{{.Repository.Name}}" data-repo-name="{{.Repository.Name}}" autofocus required>
+ <input name="repo_name" value="{{.Repository.Name}}" data-repo-name="{{.Repository.Name}}" required>
</div>
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.repo_size"}}</label>
@@ -40,8 +40,7 @@
<form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data">
{{.CsrfTokenHtml}}
<div class="inline field">
- <label for="avatar">{{ctx.Locale.Tr "settings.choose_new_avatar"}}</label>
- <input name="avatar" type="file" accept="image/png,image/jpeg,image/gif,image/webp">
+ {{template "shared/avatar_upload_crop" dict "LabelText" (ctx.Locale.Tr "settings.choose_new_avatar")}}
</div>
<div class="field">
<button class="ui primary button">{{ctx.Locale.Tr "settings.update_avatar"}}</button>
@@ -118,7 +117,7 @@
<td>{{.PullMirror.RemoteAddress}}</td>
<td>{{ctx.Locale.Tr "repo.settings.mirror_settings.direction.pull"}}</td>
<td>{{DateUtils.FullTime .PullMirror.UpdatedUnix}}</td>
- <td class="right aligned">
+ <td class="tw-text-right">
<form method="post" class="tw-inline-block">
{{.CsrfTokenHtml}}
<input type="hidden" name="action" value="mirror-sync">
@@ -204,9 +203,18 @@
{{range .PushMirrors}}
<tr>
<td class="tw-break-anywhere">{{.RemoteAddress}}</td>
- <td>{{ctx.Locale.Tr "repo.settings.mirror_settings.direction.push"}}</td>
- <td>{{if .LastUpdateUnix}}{{DateUtils.FullTime .LastUpdateUnix}}{{else}}{{ctx.Locale.Tr "never"}}{{end}} {{if .LastError}}<div class="ui red label" data-tooltip-content="{{.LastError}}">{{ctx.Locale.Tr "error"}}</div>{{end}}</td>
- <td class="right aligned">
+ <td>{{ctx.Locale.Tr "repo.settings.mirror_settings.direction.push"}} ({{.Interval}})</td>
+ <td>
+ <span class="flex-text-block">
+ {{if .LastUpdateUnix}}
+ {{DateUtils.FullTime .LastUpdateUnix}}
+ {{else}}
+ {{ctx.Locale.Tr "never"}}
+ {{end}}
+ {{if .LastError}}<span class="ui red label" data-tooltip-content="{{.LastError}}">{{ctx.Locale.Tr "error"}}</span>{{end}}
+ </span>
+ </td>
+ <td class="tw-text-right">
<button
class="ui tiny button show-modal"
data-modal="#push-mirror-edit-modal"
@@ -329,16 +337,6 @@
<label>{{ctx.Locale.Tr "repo.settings.default_wiki_branch_name"}}</label>
<input name="default_wiki_branch" value="{{.Repository.DefaultWikiBranch}}">
</div>
- <div class="inline field">
- {{$unitInternalWiki := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeWiki}}
- <label>{{ctx.Locale.Tr "repo.settings.default_wiki_everyone_access"}}</label>
- <select name="default_wiki_everyone_access" class="ui selection dropdown">
- {{/* everyone access mode is different from others, none means it is unset and won't be applied */}}
- <option value="none" {{Iif (eq $unitInternalWiki.EveryoneAccessMode 0) "selected"}}>{{ctx.Locale.Tr "settings.permission_not_set"}}</option>
- <option value="read" {{Iif (eq $unitInternalWiki.EveryoneAccessMode 1) "selected"}}>{{ctx.Locale.Tr "settings.permission_read"}}</option>
- <option value="write" {{Iif (eq $unitInternalWiki.EveryoneAccessMode 2) "selected"}}>{{ctx.Locale.Tr "settings.permission_write"}}</option>
- </select>
- </div>
</div>
<div class="field">
<div class="ui radio checkbox{{if $isExternalWikiGlobalDisabled}} disabled{{end}}"{{if $isExternalWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
@@ -804,7 +802,7 @@
</div>
</div>
{{end}}
- {{if and .Repository.IsFork .Repository.Owner.CanCreateRepo}}
+ {{if .CanConvertFork}}
<div class="flex-item">
<div class="flex-item-main">
<div class="flex-item-title">{{ctx.Locale.Tr "repo.settings.convert_fork"}}</div>
@@ -910,7 +908,7 @@
<input name="repo_name" required maxlength="100">
</div>
- <div class="text right actions">
+ <div class="actions">
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
<button class="ui red button">{{ctx.Locale.Tr "repo.settings.convert_confirm"}}</button>
</div>
@@ -918,7 +916,7 @@
</div>
</div>
{{end}}
- {{if and .Repository.IsFork .Repository.Owner.CanCreateRepo}}
+ {{if .CanConvertFork}}
<div class="ui small modal" id="convert-fork-repo-modal">
<div class="header">
{{ctx.Locale.Tr "repo.settings.convert_fork"}}
@@ -941,7 +939,7 @@
<input name="repo_name" required>
</div>
- <div class="text right actions">
+ <div class="actions">
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
<button class="ui red button">{{ctx.Locale.Tr "repo.settings.convert_fork_confirm"}}</button>
</div>
@@ -978,7 +976,7 @@
<input id="new_owner_name" name="new_owner_name" required>
</div>
- <div class="text right actions">
+ <div class="actions">
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
<button class="ui red button">{{ctx.Locale.Tr "repo.settings.transfer_perform"}}</button>
</div>
@@ -1012,7 +1010,7 @@
<input id="repo_name_to_delete" name="repo_name" required>
</div>
- <div class="text right actions">
+ <div class="actions">
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
<button class="ui red button">{{ctx.Locale.Tr "repo.settings.confirm_delete"}}</button>
</div>
@@ -1072,7 +1070,7 @@
<input name="repo_name" required>
</div>
- <div class="text right actions">
+ <div class="actions">
<button class="ui cancel button">{{ctx.Locale.Tr "settings.cancel"}}</button>
<button class="ui red button">{{ctx.Locale.Tr "repo.settings.confirm_wiki_delete"}}</button>
</div>
diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl
index 61cc6077a1..3c311c18c3 100644
--- a/templates/repo/settings/protected_branch.tmpl
+++ b/templates/repo/settings/protected_branch.tmpl
@@ -2,13 +2,17 @@
<div class="repo-setting-content">
<form class="ui form" action="{{.Link}}" method="post">
<h4 class="ui top attached header">
- {{ctx.Locale.Tr "repo.settings.branch_protection" .Rule.RuleName}}
+ {{if .Rule.RuleName}}
+ {{ctx.Locale.Tr "repo.settings.branch_protection" .Rule.RuleName}}
+ {{else}}
+ {{ctx.Locale.Tr "repo.settings.branches.add_new_rule"}}
+ {{end}}
</h4>
<div class="ui attached segment branch-protection">
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.protect_patterns"}}</h5>
- <div class="field">
+ <div class="field required">
<label>{{ctx.Locale.Tr "repo.settings.protect_branch_name_pattern"}}</label>
- <input name="rule_name" type="text" value="{{.Rule.RuleName}}">
+ <input name="rule_name" type="text" value="{{.Rule.RuleName}}" required>
<input name="rule_id" type="hidden" value="{{.Rule.ID}}">
<p class="help tw-ml-0">{{ctx.Locale.Tr "repo.settings.protect_branch_name_pattern_desc" "https://github.com/gobwas/glob"}}</p>
</div>
diff --git a/templates/repo/settings/public_access.tmpl b/templates/repo/settings/public_access.tmpl
new file mode 100644
index 0000000000..c1c198bcce
--- /dev/null
+++ b/templates/repo/settings/public_access.tmpl
@@ -0,0 +1,54 @@
+{{template "repo/settings/layout_head" (dict "ctxData" . "pageClass" "repository settings")}}
+<div class="repo-setting-content">
+ <h4 class="ui top attached header">
+ {{ctx.Locale.Tr "repo.settings.public_access"}}
+ </h4>
+ <div class="ui attached segment">
+ <p>
+ {{ctx.Locale.Tr "repo.settings.public_access_desc"}}
+ </p>
+ {{$paNotSet := "not-set"}}
+ {{$paAnonymousRead := "anonymous-read"}}
+ {{$paEveryoneRead := "everyone-read"}}
+ {{$paEveryoneWrite := "everyone-write"}}
+ <form class="ui form" method="post">
+ {{.CsrfTokenHtml}}
+ <table class="ui table unstackable tw-my-2">
+ <thead>
+ <tr>
+ <th>{{ctx.Locale.Tr "units.unit"}}</th>
+ <th class="tw-text-center">{{ctx.Locale.Tr "settings.permission_not_set"}}</th>
+ <th class="tw-text-center">{{ctx.Locale.Tr "settings.permission_anonymous_read"}}</th>
+ <th class="tw-text-center">{{ctx.Locale.Tr "settings.permission_everyone_read"}}</th>
+ <th class="tw-text-center">{{ctx.Locale.Tr "settings.permission_everyone_write"}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{range $ua := .RepoUnitPublicAccesses}}
+ <tr>
+ <td>{{$ua.DisplayName}}</td>
+ <td class="tw-text-center"><label><input type="radio" name="{{$ua.FormKey}}" value="{{$paNotSet}}" {{Iif (eq $paNotSet $ua.UnitPublicAccess) "checked"}}></label></td>
+ <td class="tw-text-center"><label><input type="radio" name="{{$ua.FormKey}}" value="{{$paAnonymousRead}}" {{Iif (eq $paAnonymousRead $ua.UnitPublicAccess) "checked"}}></label></td>
+ <td class="tw-text-center"><label><input type="radio" name="{{$ua.FormKey}}" value="{{$paEveryoneRead}}" {{Iif (eq $paEveryoneRead $ua.UnitPublicAccess) "checked"}}></label></td>
+ <td class="tw-text-center">
+ {{if SliceUtils.Contains $ua.PublicAccessTypes $paEveryoneWrite}}
+ <label><input type="radio" name="{{$ua.FormKey}}" value="{{$paEveryoneWrite}}" {{Iif (eq $paEveryoneWrite $ua.UnitPublicAccess) "checked"}}></label>
+ {{else}}
+ -
+ {{end}}
+ </td>
+ </tr>
+ {{end}}
+ </tbody>
+ </table>
+ <ul class="tw-my-3 tw-pl-5 tw-flex tw-flex-col tw-gap-3">
+ <li>{{ctx.Locale.Tr "repo.settings.public_access.docs.not_set"}}</li>
+ <li>{{ctx.Locale.Tr "repo.settings.public_access.docs.anonymous_read"}}</li>
+ <li>{{ctx.Locale.Tr "repo.settings.public_access.docs.everyone_read"}}</li>
+ <li>{{ctx.Locale.Tr "repo.settings.public_access.docs.everyone_write"}}</li>
+ </ul>
+ <button class="ui primary button {{if .GlobalForcePrivate}}disabled{{end}}">{{ctx.Locale.Tr "repo.settings.update_settings"}}</button>
+ </form>
+ </div>
+</div>
+{{template "repo/settings/layout_footer" .}}
diff --git a/templates/repo/settings/push_mirror_sync_modal.tmpl b/templates/repo/settings/push_mirror_sync_modal.tmpl
index e8dad61a48..3bd624fab7 100644
--- a/templates/repo/settings/push_mirror_sync_modal.tmpl
+++ b/templates/repo/settings/push_mirror_sync_modal.tmpl
@@ -2,31 +2,27 @@
<div class="header">
{{ctx.Locale.Tr "repo.settings.mirror_settings.push_mirror.edit_sync_time"}}
</div>
- <div class="content">
- <form class="ui form ignore-dirty" method="post">
- {{.CsrfTokenHtml}}
- <input type="hidden" name="action" value="push-mirror-update">
- <input type="hidden" name="push_mirror_id" id="push-mirror-edit-id">
- <div class="field">
- <label for="name">{{ctx.Locale.Tr "repo.settings.mirror_settings.mirrored_repository"}}</label>
- <div class="ui small input">
- <input id="push-mirror-edit-address" readonly>
- </div>
- </div>
- <div class="inline field">
- <label for="push-mirror-edit-interval">{{ctx.Locale.Tr "repo.mirror_interval" .MinimumMirrorInterval}}</label>
- <input id="push-mirror-edit-interval" name="push_mirror_interval" autofocus>
- </div>
- <div class="actions">
- <button class="ui small basic cancel button">
- {{svg "octicon-x"}}
- {{ctx.Locale.Tr "cancel"}}
- </button>
- <button class="ui primary small approve button">
- {{svg "fontawesome-save"}}
- {{ctx.Locale.Tr "save"}}
- </button>
- </div>
- </form>
- </div>
+ <form class="content ui form ignore-dirty" method="post">
+ {{.CsrfTokenHtml}}
+ <input type="hidden" name="action" value="push-mirror-update">
+ <input type="hidden" name="push_mirror_id" id="push-mirror-edit-id">
+ <div class="field">
+ <label>{{ctx.Locale.Tr "repo.settings.mirror_settings.mirrored_repository"}}</label>
+ <span id="push-mirror-edit-address"></span>
+ </div>
+ <div class="field">
+ <label for="push-mirror-edit-interval">{{ctx.Locale.Tr "repo.mirror_interval" .MinimumMirrorInterval}}</label>
+ <input id="push-mirror-edit-interval" name="push_mirror_interval" class="tw-w-auto">
+ </div>
+ <div class="actions">
+ <button class="ui small basic cancel button">
+ {{svg "octicon-x"}}
+ {{ctx.Locale.Tr "cancel"}}
+ </button>
+ <button class="ui primary small approve button">
+ {{svg "fontawesome-save"}}
+ {{ctx.Locale.Tr "save"}}
+ </button>
+ </div>
+ </form>
</div>
diff --git a/templates/repo/settings/tags.tmpl b/templates/repo/settings/tags.tmpl
index 27b0f519a8..12ec9102b7 100644
--- a/templates/repo/settings/tags.tmpl
+++ b/templates/repo/settings/tags.tmpl
@@ -104,7 +104,7 @@
{{ctx.Locale.Tr "repo.settings.tags.protection.allowed.noone"}}
{{end}}
</td>
- <td class="right aligned">
+ <td class="tw-text-right">
<a class="ui tiny primary button" href="{{$.RepoLink}}/settings/tags/{{.ID}}">{{ctx.Locale.Tr "edit"}}</a>
<form class="tw-inline-block" action="{{$.RepoLink}}/settings/tags/delete" method="post">
{{$.CsrfTokenHtml}}
@@ -114,7 +114,7 @@
</td>
</tr>
{{else}}
- <tr class="center aligned"><td colspan="3">{{ctx.Locale.Tr "repo.settings.tags.protection.none"}}</td></tr>
+ <tr class="tw-text-center"><td colspan="3">{{ctx.Locale.Tr "repo.settings.tags.protection.none"}}</td></tr>
{{end}}
</tbody>
</table>
diff --git a/templates/repo/settings/webhook/base.tmpl b/templates/repo/settings/webhook/base.tmpl
index d524722454..39bb7a9fe0 100644
--- a/templates/repo/settings/webhook/base.tmpl
+++ b/templates/repo/settings/webhook/base.tmpl
@@ -1,5 +1,5 @@
{{template "repo/settings/layout_head" (dict "ctxData" . "pageClass" "repository settings webhooks")}}
<div class="repo-setting-content">
- {{template "repo/settings/webhook/list" .}}
+ {{template "repo/settings/webhook/base_list" .}}
</div>
{{template "repo/settings/layout_footer" .}}
diff --git a/templates/repo/settings/webhook/base_list.tmpl b/templates/repo/settings/webhook/base_list.tmpl
index 36e75a7eb5..a808d4122f 100644
--- a/templates/repo/settings/webhook/base_list.tmpl
+++ b/templates/repo/settings/webhook/base_list.tmpl
@@ -8,18 +8,19 @@
</div>
</h4>
<div class="ui attached segment">
- <div class="ui list">
- <div class="item">
- {{.Description}}
- </div>
+ <div class="ui list flex-items-block">
+ <div class="item"><span>{{.Description}}</span></div>
{{range .Webhooks}}
- <div class="item truncated-item-container">
- <span class="text {{if eq .LastStatus 1}}green{{else if eq .LastStatus 2}}red{{else}}grey{{end}} tw-mr-2">{{svg "octicon-dot-fill" 22}}</span>
- <div class="text truncate tw-flex-1 tw-mr-2">
+ <div class="item">
+ <span class="text {{if eq .LastStatus 1}}green{{else if eq .LastStatus 2}}red{{else}}grey{{end}}">{{svg "octicon-dot-fill" 22}}</span>
+ <div class="gt-ellipsis tw-flex-1">
<a title="{{.URL}}" href="{{$.BaseLink}}/{{.ID}}">{{.URL}}</a>
</div>
<a class="muted tw-p-2" href="{{$.BaseLink}}/{{.ID}}">{{svg "octicon-pencil"}}</a>
- <a class="delete-button tw-p-2" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}}</a>
+ <a class="text red tw-p-2 link-action"
+ data-url="{{$.Link}}/delete?id={{.ID}}"
+ data-modal-confirm="{{ctx.Locale.Tr "repo.settings.webhook_deletion_desc"}}"
+ >{{svg "octicon-trash"}}</a>
</div>
{{end}}
</div>
diff --git a/templates/repo/settings/webhook/delete_modal.tmpl b/templates/repo/settings/webhook/delete_modal.tmpl
deleted file mode 100644
index 9955ed3a2f..0000000000
--- a/templates/repo/settings/webhook/delete_modal.tmpl
+++ /dev/null
@@ -1,10 +0,0 @@
-<div class="ui g-modal-confirm delete modal">
- <div class="header">
- {{svg "octicon-trash"}}
- {{ctx.Locale.Tr "repo.settings.webhook_deletion"}}
- </div>
- <div class="content">
- <p>{{ctx.Locale.Tr "repo.settings.webhook_deletion_desc"}}</p>
- </div>
- {{template "base/modal_actions_confirm" .}}
-</div>
diff --git a/templates/repo/settings/webhook/dingtalk.tmpl b/templates/repo/settings/webhook/dingtalk.tmpl
index 0ba99e98ee..dd208cde17 100644
--- a/templates/repo/settings/webhook/dingtalk.tmpl
+++ b/templates/repo/settings/webhook/dingtalk.tmpl
@@ -6,6 +6,7 @@
<label for="payload_url">{{ctx.Locale.Tr "repo.settings.payload_url"}}</label>
<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required>
</div>
- {{template "repo/settings/webhook/settings" .}}
+ {{/* FIXME: support authorization header or not? */}}
+ {{template "repo/settings/webhook/settings" dict "BaseLink" .BaseLink "Webhook" .Webhook "UseAuthorizationHeader" "optional"}}
</form>
{{end}}
diff --git a/templates/repo/settings/webhook/discord.tmpl b/templates/repo/settings/webhook/discord.tmpl
index 104346e042..fa66249fa5 100644
--- a/templates/repo/settings/webhook/discord.tmpl
+++ b/templates/repo/settings/webhook/discord.tmpl
@@ -14,6 +14,7 @@
<label for="icon_url">{{ctx.Locale.Tr "repo.settings.discord_icon_url"}}</label>
<input id="icon_url" name="icon_url" value="{{.DiscordHook.IconURL}}" placeholder="https://example.com/assets/img/logo.svg">
</div>
- {{template "repo/settings/webhook/settings" .}}
+ {{/* FIXME: support authorization header or not? */}}
+ {{template "repo/settings/webhook/settings" dict "BaseLink" .BaseLink "Webhook" .Webhook "UseAuthorizationHeader" "optional"}}
</form>
{{end}}
diff --git a/templates/repo/settings/webhook/feishu.tmpl b/templates/repo/settings/webhook/feishu.tmpl
index d80deab26f..13bd0d92a1 100644
--- a/templates/repo/settings/webhook/feishu.tmpl
+++ b/templates/repo/settings/webhook/feishu.tmpl
@@ -1,12 +1,14 @@
{{if eq .HookType "feishu"}}
- <p>{{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://feishu.cn" (ctx.Locale.Tr "repo.settings.web_hook_name_feishu")}}</p>
- <p>{{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://larksuite.com" (ctx.Locale.Tr "repo.settings.web_hook_name_larksuite")}}</p>
+ <p>
+ {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://feishu.cn" (ctx.Locale.Tr "repo.settings.web_hook_name_feishu")}}
+ {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://larksuite.com" (ctx.Locale.Tr "repo.settings.web_hook_name_larksuite")}}
+ </p>
<form class="ui form" action="{{.BaseLink}}/feishu/{{or .Webhook.ID "new"}}" method="post">
{{.CsrfTokenHtml}}
<div class="required field {{if .Err_PayloadURL}}error{{end}}">
<label for="payload_url">{{ctx.Locale.Tr "repo.settings.payload_url"}}</label>
<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required>
</div>
- {{template "repo/settings/webhook/settings" .}}
+ {{template "repo/settings/webhook/settings" dict "BaseLink" .BaseLink "Webhook" .Webhook "UseRequestSecret" "optional"}}
</form>
{{end}}
diff --git a/templates/repo/settings/webhook/gitea.tmpl b/templates/repo/settings/webhook/gitea.tmpl
index e6eb61ea92..30f14d609b 100644
--- a/templates/repo/settings/webhook/gitea.tmpl
+++ b/templates/repo/settings/webhook/gitea.tmpl
@@ -31,10 +31,11 @@
</div>
</div>
</div>
- <div class="field {{if .Err_Secret}}error{{end}}">
- <label for="secret">{{ctx.Locale.Tr "repo.settings.secret"}}</label>
- <input id="secret" name="secret" type="password" value="{{.Webhook.Secret}}" autocomplete="off">
- </div>
- {{template "repo/settings/webhook/settings" .}}
+ {{template "repo/settings/webhook/settings" dict
+ "BaseLink" .BaseLink
+ "Webhook" .Webhook
+ "UseAuthorizationHeader" "optional"
+ "UseRequestSecret" "optional"
+ }}
</form>
{{end}}
diff --git a/templates/repo/settings/webhook/gogs.tmpl b/templates/repo/settings/webhook/gogs.tmpl
index e91a3279e4..c0e054602a 100644
--- a/templates/repo/settings/webhook/gogs.tmpl
+++ b/templates/repo/settings/webhook/gogs.tmpl
@@ -19,10 +19,11 @@
</div>
</div>
</div>
- <div class="field {{if .Err_Secret}}error{{end}}">
- <label for="secret">{{ctx.Locale.Tr "repo.settings.secret"}}</label>
- <input id="secret" name="secret" type="password" value="{{.Webhook.Secret}}" autocomplete="off">
- </div>
- {{template "repo/settings/webhook/settings" .}}
+ {{template "repo/settings/webhook/settings" dict
+ "BaseLink" .BaseLink
+ "Webhook" .Webhook
+ "UseAuthorizationHeader" "optional"
+ "UseRequestSecret" "optional"
+ }}
</form>
{{end}}
diff --git a/templates/repo/settings/webhook/history.tmpl b/templates/repo/settings/webhook/history.tmpl
index ea3c037813..953ba69670 100644
--- a/templates/repo/settings/webhook/history.tmpl
+++ b/templates/repo/settings/webhook/history.tmpl
@@ -26,7 +26,7 @@
{{else}}
<span class="text red">{{svg "octicon-alert"}}</span>
{{end}}
- <a class="ui primary sha label toggle button show-panel" data-panel="#info-{{.ID}}">{{.UUID}}</a>
+ <button class="btn interact-bg tw-p-2 toggle show-panel" data-panel="#info-{{.ID}}">{{.UUID}}</button>
</div>
<span class="text grey">
{{DateUtils.TimeSince .Delivered}}
diff --git a/templates/repo/settings/webhook/list.tmpl b/templates/repo/settings/webhook/list.tmpl
deleted file mode 100644
index b24159fccb..0000000000
--- a/templates/repo/settings/webhook/list.tmpl
+++ /dev/null
@@ -1,4 +0,0 @@
-
-{{template "repo/settings/webhook/base_list" .}}
-
-{{template "repo/settings/webhook/delete_modal" .}}
diff --git a/templates/repo/settings/webhook/matrix.tmpl b/templates/repo/settings/webhook/matrix.tmpl
index 7f1c9f08e6..e0aad2d807 100644
--- a/templates/repo/settings/webhook/matrix.tmpl
+++ b/templates/repo/settings/webhook/matrix.tmpl
@@ -22,6 +22,6 @@
</div>
</div>
</div>
- {{template "repo/settings/webhook/settings" .}}
+ {{template "repo/settings/webhook/settings" dict "BaseLink" .BaseLink "Webhook" .Webhook "UseAuthorizationHeader" "required"}}
</form>
{{end}}
diff --git a/templates/repo/settings/webhook/msteams.tmpl b/templates/repo/settings/webhook/msteams.tmpl
index 62ea24e763..17718a1064 100644
--- a/templates/repo/settings/webhook/msteams.tmpl
+++ b/templates/repo/settings/webhook/msteams.tmpl
@@ -6,6 +6,7 @@
<label for="payload_url">{{ctx.Locale.Tr "repo.settings.payload_url"}}</label>
<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required>
</div>
- {{template "repo/settings/webhook/settings" .}}
+ {{/* FIXME: support authorization header or not? */}}
+ {{template "repo/settings/webhook/settings" dict "BaseLink" .BaseLink "Webhook" .Webhook "UseAuthorizationHeader" "optional"}}
</form>
{{end}}
diff --git a/templates/repo/settings/webhook/packagist.tmpl b/templates/repo/settings/webhook/packagist.tmpl
index 25aba2a435..c813e7c2af 100644
--- a/templates/repo/settings/webhook/packagist.tmpl
+++ b/templates/repo/settings/webhook/packagist.tmpl
@@ -14,6 +14,7 @@
<label for="package_url">{{ctx.Locale.Tr "repo.settings.packagist_package_url"}}</label>
<input id="package_url" name="package_url" value="{{.PackagistHook.PackageURL}}" placeholder="https://packagist.org/packages/laravel/framework" required>
</div>
- {{template "repo/settings/webhook/settings" .}}
+ {{/* FIXME: support authorization header or not? */}}
+ {{template "repo/settings/webhook/settings" dict "BaseLink" .BaseLink "Webhook" .Webhook "UseAuthorizationHeader" "optional"}}
</form>
{{end}}
diff --git a/templates/repo/settings/webhook/settings.tmpl b/templates/repo/settings/webhook/settings.tmpl
index cf3b0eb053..a8ad1d6c9e 100644
--- a/templates/repo/settings/webhook/settings.tmpl
+++ b/templates/repo/settings/webhook/settings.tmpl
@@ -1,4 +1,52 @@
-{{$isNew:=or .PageIsSettingsHooksNew .PageIsAdminDefaultHooksNew .PageIsAdminSystemHooksNew}}
+{{/* Template attributes:
+- BaseLink: Base URL for the repository settings
+- WebHook: Webhook object containing details about the webhook
+- UseAuthorizationHeader: optional or required
+- UseRequestSecret: optional or required
+*/}}
+{{$isNew := not .Webhook.ID}}
+
+<div class="inline field">
+ <div class="ui checkbox">
+ <input name="active" type="checkbox" {{if or $isNew .Webhook.IsActive}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.settings.active"}}</label>
+ <span class="help">{{ctx.Locale.Tr "repo.settings.active_helper"}}</span>
+ </div>
+</div>
+
+<!-- Authorization Header -->
+{{if .UseAuthorizationHeader}}
+ {{$attributeValid := or (eq .UseAuthorizationHeader "optional") (eq .UseAuthorizationHeader "required")}}
+ {{if not $attributeValid}}<div class="ui error message">Invalid UseAuthorizationHeader: {{.UseAuthorizationHeader}}}</div>{{end}}
+ {{$required := eq .UseAuthorizationHeader "required"}}
+ <div class="field {{if $required}}required{{end}}">
+ <label>{{ctx.Locale.Tr "repo.settings.authorization_header"}}</label>
+ <input name="authorization_header" type="text" value="{{.Webhook.HeaderAuthorization}}" {{if $required}}required placeholder="Bearer $access_token"{{end}}>
+ {{if not $required}}
+ <span class="help">{{ctx.Locale.Tr "repo.settings.authorization_header_desc" (HTMLFormat "<code>%s</code>, <code>%s</code>" "Bearer token123456" "Basic YWxhZGRpbjpvcGVuc2VzYW1l")}}</span>
+ {{end}}
+ </div>
+{{end}}
+
+<!-- Secret -->
+{{if .UseRequestSecret}}
+ {{$attributeValid := or (eq .UseRequestSecret "optional") (eq .UseRequestSecret "required")}}
+ {{if not $attributeValid}}<div class="ui error message">Invalid UseRequestSecret: {{.UseRequestSecret}}}</div>{{end}}
+ {{$required := eq .UseRequestSecret "required"}}
+ <div class="field {{if $required}}required{{end}}">
+ <label>{{ctx.Locale.Tr "repo.settings.secret"}}</label>
+ <input name="secret" type="password" value="{{.Webhook.Secret}}" autocomplete="off" {{if $required}}required{{end}}>
+ <span class="help">{{ctx.Locale.Tr "repo.settings.webhook_secret_desc"}}</span>
+ </div>
+{{end}}
+
+<!-- Branch filter -->
+<div class="field">
+ <label>{{ctx.Locale.Tr "repo.settings.branch_filter"}}</label>
+ <input name="branch_filter" type="text" value="{{or .Webhook.BranchFilter "*"}}">
+ <span class="help">{{ctx.Locale.Tr "repo.settings.branch_filter_desc" "https://pkg.go.dev/github.com/gobwas/glob#Compile" "github.com/gobwas/glob"}}</span>
+</div>
+
<div class="field">
<h4>{{ctx.Locale.Tr "repo.settings.event_desc"}}</h4>
<div class="grouped event type fields">
@@ -31,7 +79,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="create" type="checkbox" {{if .Webhook.Create}}checked{{end}}>
+ <input name="create" type="checkbox" {{if .Webhook.HookEvents.Get "create"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_create"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_create_desc"}}</span>
</div>
@@ -41,7 +89,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="delete" type="checkbox" {{if .Webhook.Delete}}checked{{end}}>
+ <input name="delete" type="checkbox" {{if .Webhook.HookEvents.Get "delete"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_delete"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_delete_desc"}}</span>
</div>
@@ -51,7 +99,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="fork" type="checkbox" {{if .Webhook.Fork}}checked{{end}}>
+ <input name="fork" type="checkbox" {{if .Webhook.HookEvents.Get "fork"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_fork"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_fork_desc"}}</span>
</div>
@@ -61,7 +109,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="push" type="checkbox" {{if .Webhook.Push}}checked{{end}}>
+ <input name="push" type="checkbox" {{if .Webhook.HookEvents.Get "push"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_push"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_push_desc"}}</span>
</div>
@@ -71,7 +119,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="repository" type="checkbox" {{if .Webhook.Repository}}checked{{end}}>
+ <input name="repository" type="checkbox" {{if .Webhook.HookEvents.Get "repository"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_repository"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_repository_desc"}}</span>
</div>
@@ -81,7 +129,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="release" type="checkbox" {{if .Webhook.Release}}checked{{end}}>
+ <input name="release" type="checkbox" {{if .Webhook.HookEvents.Get "release"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_release"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_release_desc"}}</span>
</div>
@@ -91,7 +139,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="package" type="checkbox" {{if .Webhook.Package}}checked{{end}}>
+ <input name="package" type="checkbox" {{if .Webhook.HookEvents.Get "package"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_package"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_package_desc"}}</span>
</div>
@@ -102,13 +150,24 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="wiki" type="checkbox" {{if .Webhook.Wiki}}checked{{end}}>
+ <input name="wiki" type="checkbox" {{if .Webhook.HookEvents.Get "wiki"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_wiki"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_wiki_desc"}}</span>
</div>
</div>
</div>
+ <!-- Status -->
+ <div class="seven wide column">
+ <div class="field">
+ <div class="ui checkbox">
+ <input name="status" type="checkbox" {{if .Webhook.HookEvents.Get "status"}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.settings.event_statuses"}}</label>
+ <span class="help">{{ctx.Locale.Tr "repo.settings.event_statuses_desc"}}</span>
+ </div>
+ </div>
+ </div>
+
<!-- Issue Events -->
<div class="fourteen wide column">
<label>{{ctx.Locale.Tr "repo.settings.event_header_issue"}}</label>
@@ -117,7 +176,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="issues" type="checkbox" {{if .Webhook.Issues}}checked{{end}}>
+ <input name="issues" type="checkbox" {{if .Webhook.HookEvents.Get "issues"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_issues"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_issues_desc"}}</span>
</div>
@@ -127,7 +186,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="issue_assign" type="checkbox" {{if .Webhook.IssueAssign}}checked{{end}}>
+ <input name="issue_assign" type="checkbox" {{if .Webhook.HookEvents.Get "issue_assign"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_issue_assign"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_issue_assign_desc"}}</span>
</div>
@@ -137,7 +196,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="issue_label" type="checkbox" {{if .Webhook.IssueLabel}}checked{{end}}>
+ <input name="issue_label" type="checkbox" {{if .Webhook.HookEvents.Get "issue_label"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_issue_label"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_issue_label_desc"}}</span>
</div>
@@ -147,7 +206,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="issue_milestone" type="checkbox" {{if .Webhook.IssueMilestone}}checked{{end}}>
+ <input name="issue_milestone" type="checkbox" {{if .Webhook.HookEvents.Get "issue_milestone"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_issue_milestone"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_issue_milestone_desc"}}</span>
</div>
@@ -157,7 +216,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="issue_comment" type="checkbox" {{if .Webhook.IssueComment}}checked{{end}}>
+ <input name="issue_comment" type="checkbox" {{if .Webhook.HookEvents.Get "issue_comment"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_issue_comment"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_issue_comment_desc"}}</span>
</div>
@@ -172,7 +231,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="pull_request" type="checkbox" {{if .Webhook.PullRequest}}checked{{end}}>
+ <input name="pull_request" type="checkbox" {{if .Webhook.HookEvents.Get "pull_request"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_pull_request"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_pull_request_desc"}}</span>
</div>
@@ -182,7 +241,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="pull_request_assign" type="checkbox" {{if .Webhook.PullRequestAssign}}checked{{end}}>
+ <input name="pull_request_assign" type="checkbox" {{if .Webhook.HookEvents.Get "pull_request_assign"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_pull_request_assign"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_pull_request_assign_desc"}}</span>
</div>
@@ -192,7 +251,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="pull_request_label" type="checkbox" {{if .Webhook.PullRequestLabel}}checked{{end}}>
+ <input name="pull_request_label" type="checkbox" {{if .Webhook.HookEvents.Get "pull_request_label"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_pull_request_label"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_pull_request_label_desc"}}</span>
</div>
@@ -202,7 +261,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="pull_request_milestone" type="checkbox" {{if .Webhook.PullRequestMilestone}}checked{{end}}>
+ <input name="pull_request_milestone" type="checkbox" {{if .Webhook.HookEvents.Get "pull_request_milestone"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_pull_request_milestone"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_pull_request_milestone_desc"}}</span>
</div>
@@ -212,7 +271,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="pull_request_comment" type="checkbox" {{if .Webhook.PullRequestComment}}checked{{end}}>
+ <input name="pull_request_comment" type="checkbox" {{if .Webhook.HookEvents.Get "pull_request_comment"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_pull_request_comment"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_pull_request_comment_desc"}}</span>
</div>
@@ -222,7 +281,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="pull_request_review" type="checkbox" {{if .Webhook.PullRequestReview}}checked{{end}}>
+ <input name="pull_request_review" type="checkbox" {{if .Webhook.HookEvents.Get "pull_request_review"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_pull_request_review"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_pull_request_review_desc"}}</span>
</div>
@@ -232,7 +291,7 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="pull_request_sync" type="checkbox" {{if .Webhook.PullRequestSync}}checked{{end}}>
+ <input name="pull_request_sync" type="checkbox" {{if .Webhook.HookEvents.Get "pull_request_sync"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_pull_request_sync"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_pull_request_sync_desc"}}</span>
</div>
@@ -242,47 +301,47 @@
<div class="seven wide column">
<div class="field">
<div class="ui checkbox">
- <input name="pull_request_review_request" type="checkbox" {{if .Webhook.PullRequestReviewRequest}}checked{{end}}>
+ <input name="pull_request_review_request" type="checkbox" {{if .Webhook.HookEvents.Get "pull_request_review_request"}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.event_pull_request_review_request"}}</label>
<span class="help">{{ctx.Locale.Tr "repo.settings.event_pull_request_review_request_desc"}}</span>
</div>
</div>
</div>
+ <!-- Workflow Events -->
+ <div class="fourteen wide column">
+ <label>{{ctx.Locale.Tr "repo.settings.event_header_workflow"}}</label>
+ </div>
+ <!-- Workflow Run Event -->
+ <div class="seven wide column">
+ <div class="field">
+ <div class="ui checkbox">
+ <input name="workflow_run" type="checkbox" {{if .Webhook.HookEvents.Get "workflow_run"}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.settings.event_workflow_run"}}</label>
+ <span class="help">{{ctx.Locale.Tr "repo.settings.event_workflow_run_desc"}}</span>
+ </div>
+ </div>
+ </div>
+ <!-- Workflow Job Event -->
+ <div class="seven wide column">
+ <div class="field">
+ <div class="ui checkbox">
+ <input name="workflow_job" type="checkbox" {{if .Webhook.HookEvents.Get "workflow_job"}}checked{{end}}>
+ <label>{{ctx.Locale.Tr "repo.settings.event_workflow_job"}}</label>
+ <span class="help">{{ctx.Locale.Tr "repo.settings.event_workflow_job_desc"}}</span>
+ </div>
+ </div>
+ </div>
</div>
</div>
-<!-- Branch filter -->
-<div class="field">
- <label for="branch_filter">{{ctx.Locale.Tr "repo.settings.branch_filter"}}</label>
- <input id="branch_filter" name="branch_filter" type="text" value="{{or .Webhook.BranchFilter "*"}}">
- <span class="help">{{ctx.Locale.Tr "repo.settings.branch_filter_desc" "https://pkg.go.dev/github.com/gobwas/glob#Compile" "github.com/gobwas/glob"}}</span>
-</div>
-
-<!-- Authorization Header -->
-<div class="field{{if eq .HookType "matrix"}} required{{end}}">
- <label for="authorization_header">{{ctx.Locale.Tr "repo.settings.authorization_header"}}</label>
- <input id="authorization_header" name="authorization_header" type="text" value="{{.Webhook.HeaderAuthorization}}"{{if eq .HookType "matrix"}} placeholder="Bearer $access_token" required{{end}}>
- {{if ne .HookType "matrix"}}{{/* Matrix doesn't make the authorization optional but it is implied by the help string, should be changed.*/}}
- <span class="help">{{ctx.Locale.Tr "repo.settings.authorization_header_desc" ("<code>Bearer token123456</code>, <code>Basic YWxhZGRpbjpvcGVuc2VzYW1l</code>" | SafeHTML)}}</span>
- {{end}}
-</div>
-
-<div class="divider"></div>
-
-<div class="inline field">
- <div class="ui checkbox">
- <input name="active" type="checkbox" {{if or $isNew .Webhook.IsActive}}checked{{end}}>
- <label>{{ctx.Locale.Tr "repo.settings.active"}}</label>
- <span class="help">{{ctx.Locale.Tr "repo.settings.active_helper"}}</span>
- </div>
-</div>
<div class="field">
{{if $isNew}}
<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.add_webhook"}}</button>
{{else}}
<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.update_webhook"}}</button>
- <a class="ui red delete-button button" data-url="{{.BaseLink}}/delete" data-id="{{.Webhook.ID}}">{{ctx.Locale.Tr "repo.settings.delete_webhook"}}</a>
+ <a class="ui red button link-action"
+ data-url="{{.BaseLink}}/delete?id={{.Webhook.ID}}"
+ data-modal-confirm="{{ctx.Locale.Tr "repo.settings.webhook_deletion_desc"}}"
+ >{{ctx.Locale.Tr "repo.settings.delete_webhook"}}</a>
{{end}}
</div>
-
-{{template "repo/settings/webhook/delete_modal" .}}
diff --git a/templates/repo/settings/webhook/slack.tmpl b/templates/repo/settings/webhook/slack.tmpl
index e7cae92d4b..519d6afa1a 100644
--- a/templates/repo/settings/webhook/slack.tmpl
+++ b/templates/repo/settings/webhook/slack.tmpl
@@ -23,6 +23,7 @@
<label for="color">{{ctx.Locale.Tr "repo.settings.slack_color"}}</label>
<input id="color" name="color" value="{{.SlackHook.Color}}" placeholder="#dd4b39, good, warning, danger">
</div>
- {{template "repo/settings/webhook/settings" .}}
+ {{/* FIXME: support authorization header or not? */}}
+ {{template "repo/settings/webhook/settings" dict "BaseLink" .BaseLink "Webhook" .Webhook "UseAuthorizationHeader" "optional"}}
</form>
{{end}}
diff --git a/templates/repo/settings/webhook/telegram.tmpl b/templates/repo/settings/webhook/telegram.tmpl
index f92c2be0db..5ab89b72cc 100644
--- a/templates/repo/settings/webhook/telegram.tmpl
+++ b/templates/repo/settings/webhook/telegram.tmpl
@@ -14,6 +14,7 @@
<label for="thread_id">{{ctx.Locale.Tr "repo.settings.thread_id"}}</label>
<input id="thread_id" name="thread_id" type="text" value="{{.TelegramHook.ThreadID}}">
</div>
- {{template "repo/settings/webhook/settings" .}}
+ {{/* FIXME: support authorization header or not? */}}
+ {{template "repo/settings/webhook/settings" dict "BaseLink" .BaseLink "Webhook" .Webhook "UseAuthorizationHeader" "optional"}}
</form>
{{end}}
diff --git a/templates/repo/settings/webhook/wechatwork.tmpl b/templates/repo/settings/webhook/wechatwork.tmpl
index 78a1617123..cbc29b4610 100644
--- a/templates/repo/settings/webhook/wechatwork.tmpl
+++ b/templates/repo/settings/webhook/wechatwork.tmpl
@@ -6,6 +6,7 @@
<label for="payload_url">{{ctx.Locale.Tr "repo.settings.payload_url"}}</label>
<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required>
</div>
- {{template "repo/settings/webhook/settings" .}}
+ {{/* FIXME: support authorization header or not? */}}
+ {{template "repo/settings/webhook/settings" dict "BaseLink" .BaseLink "Webhook" .Webhook "UseAuthorizationHeader" "optional"}}
</form>
{{end}}
diff --git a/templates/repo/star_unstar.tmpl b/templates/repo/star_unstar.tmpl
index 9234a0d196..dea965ab30 100644
--- a/templates/repo/star_unstar.tmpl
+++ b/templates/repo/star_unstar.tmpl
@@ -1,10 +1,10 @@
-<form hx-boost="true" hx-target="this" method="post" action="{{$.RepoLink}}/action/{{if $.IsStaringRepo}}unstar{{else}}star{{end}}">
+<form class="flex-text-inline" hx-boost="true" hx-target="this" method="post" action="{{$.RepoLink}}/action/{{if $.IsStaringRepo}}unstar{{else}}star{{end}}">
<div class="ui labeled button" {{if not $.IsSigned}}data-tooltip-content="{{ctx.Locale.Tr "repo.star_guest_user"}}"{{end}}>
{{$buttonText := ctx.Locale.Tr "repo.star"}}
{{if $.IsStaringRepo}}{{$buttonText = ctx.Locale.Tr "repo.unstar"}}{{end}}
<button type="submit" class="ui compact small basic button"{{if not $.IsSigned}} disabled{{end}} aria-label="{{$buttonText}}">
{{svg (Iif $.IsStaringRepo "octicon-star-fill" "octicon-star")}}
- <span aria-hidden="true">{{$buttonText}}</span>
+ <span class="not-mobile" aria-hidden="true">{{$buttonText}}</span>
</button>
<a hx-boost="false" class="ui basic label" href="{{$.RepoLink}}/stars">
{{CountFmt .Repository.NumStars}}
diff --git a/templates/repo/sub_menu.tmpl b/templates/repo/sub_menu.tmpl
index ccb98b94ad..66ab86cb55 100644
--- a/templates/repo/sub_menu.tmpl
+++ b/templates/repo/sub_menu.tmpl
@@ -1,8 +1,8 @@
{{if and (not .HideRepoInfo) (not .IsBlame)}}
-<div class="ui segments repository-summary tw-mt-1 tw-mb-0">
+<div class="ui segments repository-summary tw-my-0">
<div class="ui segment sub-menu repository-menu">
{{if and (.Permission.CanRead ctx.Consts.RepoUnitTypeCode) (not .IsEmptyRepo)}}
- <a class="item muted {{if .PageIsCommits}}active{{end}}" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}">
+ <a class="item muted {{if .PageIsCommits}}active{{end}}" href="{{.RepoLink}}/commits/{{.RefFullName.RefWebLinkPath}}">
{{svg "octicon-history"}} <b>{{ctx.Locale.PrettyNumber .CommitsCount}}</b> {{ctx.Locale.TrN .CommitsCount "repo.commit" "repo.commits"}}
</a>
<a class="item muted {{if .PageIsBranches}}active{{end}}" href="{{.RepoLink}}/branches">
@@ -13,11 +13,6 @@
{{svg "octicon-tag"}} <b>{{ctx.Locale.PrettyNumber .NumTags}}</b> {{ctx.Locale.TrN .NumTags "repo.tag" "repo.tags"}}
</a>
{{end}}
- <span class="item not-mobile" {{if not (eq .Repository.Size 0)}}data-tooltip-placement="top" data-tooltip-content="{{.Repository.SizeDetailsString}}"{{end}}>
- {{$fileSizeFormatted := FileSize .Repository.Size}}{{/* the formatted string is always "{val} {unit}" */}}
- {{$fileSizeFields := StringUtils.Split $fileSizeFormatted " "}}
- {{svg "octicon-database"}} <b>{{ctx.Locale.PrettyNumber (index $fileSizeFields 0)}}</b> {{index $fileSizeFields 1}}
- </span>
{{end}}
</div>
</div>
diff --git a/templates/repo/tag/list.tmpl b/templates/repo/tag/list.tmpl
index 9789943b49..8b33b96f86 100644
--- a/templates/repo/tag/list.tmpl
+++ b/templates/repo/tag/list.tmpl
@@ -5,9 +5,7 @@
{{template "base/alert" .}}
{{template "repo/release_tag_header" .}}
<h4 class="ui top attached header">
- <div class="five wide column tw-flex tw-items-center">
- {{.TagCount}} {{ctx.Locale.Tr "repo.release.tags"}}
- </div>
+ {{.TagCount}} {{ctx.Locale.Tr "repo.release.tags"}}
</h4>
{{$canReadReleases := $.Permission.CanRead ctx.Consts.RepoUnitTypeReleases}}
<div class="ui attached segment">
@@ -15,53 +13,49 @@
{{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.tag_kind") "Tooltip" (ctx.Locale.Tr "search.tag_tooltip")}}
</form>
</div>
- <div class="ui attached table segment">
+ <div class="ui attached segment tw-p-0">
{{if .Releases}}
- <table class="ui very basic striped fixed table single line" id="tags-table">
- <tbody class="tag-list">
- {{range $idx, $release := .Releases}}
- <tr>
- <td class="tag-list-row">
- <h3 class="tag-list-row-title tw-mb-2">
- {{if $canReadReleases}}
- <a class="tag-list-row-link tw-flex tw-items-center" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
- {{else}}
- <a class="tag-list-row-link tw-flex tw-items-center" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
- {{end}}
- </h3>
- <div class="download tw-flex tw-items-center">
- {{if $.Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
- {{if .CreatedUnix}}
- <span class="tw-mr-2">{{svg "octicon-clock" 16 "tw-mr-1"}}{{DateUtils.TimeSince .CreatedUnix}}</span>
- {{end}}
+ <div class="ui divided list" id="tags-table">
+ {{range $idx, $release := .Releases}}
+ <div class="item tag-list-row tw-p-4">
+ <h3 class="tag-list-row-title tw-mb-2">
+ {{if $canReadReleases}}
+ <a class="tag-list-row-link" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
+ {{else}}
+ <a class="tag-list-row-link" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
+ {{end}}
+ </h3>
+ <div class="flex-text-block muted-links tw-gap-4 tw-flex-wrap">
+ {{if $.Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
+ {{if .CreatedUnix}}
+ <span class="flex-text-inline">{{svg "octicon-clock"}}{{DateUtils.TimeSince .CreatedUnix}}</span>
+ {{end}}
- <a class="tw-mr-2 tw-font-mono muted" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "tw-mr-1"}}{{ShortSha .Sha1}}</a>
+ <a class="flex-text-inline tw-font-mono" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit"}}{{ShortSha .Sha1}}</a>
- {{if not $.DisableDownloadSourceArchives}}
- <a class="archive-link tw-mr-2 muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "tw-mr-1"}}ZIP</a>
- <a class="archive-link tw-mr-2 muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "tw-mr-1"}}TAR.GZ</a>
- {{end}}
+ {{if not $.DisableDownloadSourceArchives}}
+ <a class="archive-link flex-text-inline" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.zip" rel="nofollow">{{svg "octicon-file-zip"}}ZIP</a>
+ <a class="archive-link flex-text-inline" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip"}}TAR.GZ</a>
+ {{end}}
- {{if (and $canReadReleases $.CanCreateRelease $release.IsTag)}}
- <a class="tw-mr-2 muted" href="{{$.RepoLink}}/releases/new?tag={{.TagName}}">{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.new_release"}}</a>
- {{end}}
+ {{if (and $canReadReleases $.CanCreateRelease $release.IsTag)}}
+ <a class="flex-text-inline" href="{{$.RepoLink}}/releases/new?tag={{.TagName}}">{{svg "octicon-tag"}}{{ctx.Locale.Tr "repo.release.new_release"}}</a>
+ {{end}}
- {{if (and ($.Permission.CanWrite ctx.Consts.RepoUnitTypeCode) $release.IsTag)}}
- <a class="ui delete-button tw-mr-2 muted" data-url="{{$.RepoLink}}/tags/delete" data-id="{{.ID}}">
- {{svg "octicon-trash" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.delete_tag"}}
- </a>
- {{end}}
+ {{if (and ($.Permission.CanWrite ctx.Consts.RepoUnitTypeCode) $release.IsTag)}}
+ <a class="flex-text-inline link-action" data-url="{{$.RepoLink}}/tags/delete?id={{.ID}}" data-modal-confirm="#confirm-delete-tag-modal">
+ {{svg "octicon-trash"}}{{ctx.Locale.Tr "repo.release.delete_tag"}}
+ </a>
+ {{end}}
- {{if and $canReadReleases (not $release.IsTag)}}
- <a class="tw-mr-2 muted" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}">{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.detail"}}</a>
- {{end}}
- {{end}}
- </div>
- </td>
- </tr>
- {{end}}
- </tbody>
- </table>
+ {{if and $canReadReleases (not $release.IsTag)}}
+ <a class="flex-text-inline" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}">{{svg "octicon-tag"}}{{ctx.Locale.Tr "repo.release.detail"}}</a>
+ {{end}}
+ {{end}}
+ </div>
+ </div>
+ {{end}}
+ </div>
{{else}}
{{if .NumTags}}
<p class="tw-p-4">{{ctx.Locale.Tr "no_results_found"}}</p>
@@ -73,9 +67,8 @@
</div>
{{if $.Permission.CanWrite ctx.Consts.RepoUnitTypeCode}}
-<div class="ui g-modal-confirm delete modal">
+<div id="confirm-delete-tag-modal" class="ui small modal">
<div class="header">
- {{svg "octicon-trash"}}
{{ctx.Locale.Tr "repo.release.delete_tag"}}
</div>
<div class="content">
diff --git a/templates/repo/tag/name.tmpl b/templates/repo/tag/name.tmpl
index c3042014d3..24e7de046d 100644
--- a/templates/repo/tag/name.tmpl
+++ b/templates/repo/tag/name.tmpl
@@ -1,3 +1,3 @@
-<a class="ui label basic tiny button{{if .IsRelease}} primary{{end}}" href="{{.RepoLink}}/src/tag/{{.TagName|PathEscape}}">
+<a class="ui basic label {{if .IsRelease}}primary{{end}} {{.AdditionalClasses}}" href="{{.RepoLink}}/src/tag/{{.TagName|PathEscape}}">
{{svg "octicon-tag"}} {{.TagName}}
</a>
diff --git a/templates/repo/unicode_escape_prompt.tmpl b/templates/repo/unicode_escape_prompt.tmpl
index 8bceafa8bb..f8226ec728 100644
--- a/templates/repo/unicode_escape_prompt.tmpl
+++ b/templates/repo/unicode_escape_prompt.tmpl
@@ -1,22 +1,22 @@
{{if .EscapeStatus}}
{{if .EscapeStatus.HasInvisible}}
- <div class="ui warning message unicode-escape-prompt tw-text-left">
+ <div class="ui warning message unicode-escape-prompt">
<button class="btn close icon hide-panel" data-panel-closest=".message">{{svg "octicon-x" 16 "close inside"}}</button>
<div class="header">
{{ctx.Locale.Tr "repo.invisible_runes_header"}}
</div>
- <p>{{ctx.Locale.Tr "repo.invisible_runes_description"}}</p>
+ <div>{{ctx.Locale.Tr "repo.invisible_runes_description"}}</div>
{{if .EscapeStatus.HasAmbiguous}}
- <p>{{ctx.Locale.Tr "repo.ambiguous_runes_description"}}</p>
+ <div>{{ctx.Locale.Tr "repo.ambiguous_runes_description"}}</div>
{{end}}
</div>
{{else if .EscapeStatus.HasAmbiguous}}
- <div class="ui warning message unicode-escape-prompt tw-text-left">
+ <div class="ui warning message unicode-escape-prompt">
<button class="btn close icon hide-panel" data-panel-closest=".message">{{svg "octicon-x" 16 "close inside"}}</button>
<div class="header">
{{ctx.Locale.Tr "repo.ambiguous_runes_header"}}
</div>
- <p>{{ctx.Locale.Tr "repo.ambiguous_runes_description"}}</p>
+ <div>{{ctx.Locale.Tr "repo.ambiguous_runes_description"}}</div>
</div>
{{end}}
{{end}}
diff --git a/templates/repo/view.tmpl b/templates/repo/view.tmpl
new file mode 100644
index 0000000000..85d09d03a1
--- /dev/null
+++ b/templates/repo/view.tmpl
@@ -0,0 +1,29 @@
+{{template "base/head" .}}
+<div role="main" aria-label="{{.Title}}" class="page-content repository file list {{if .IsBlame}}blame{{end}}">
+ {{template "repo/header" .}}
+ <div class="ui container {{if or .TreeNames .IsBlame}}fluid padded{{end}}">
+ {{template "base/alert" .}}
+
+ {{if .Repository.IsArchived}}
+ <div class="ui warning message tw-text-center">
+ {{if .Repository.ArchivedUnix.IsZero}}
+ {{ctx.Locale.Tr "repo.archive.title"}}
+ {{else}}
+ {{ctx.Locale.Tr "repo.archive.title_date" (DateUtils.AbsoluteLong .Repository.ArchivedUnix)}}
+ {{end}}
+ </div>
+ {{end}}
+
+ {{template "repo/code/recently_pushed_new_branches" .}}
+
+ <div class="repo-view-container">
+ <div class="tw-flex tw-flex-col repo-view-file-tree-container not-mobile {{if not .UserSettingCodeViewShowFileTree}}tw-hidden{{end}}" {{if .IsSigned}}data-user-is-signed-in{{end}}>
+ {{template "repo/view_file_tree" .}}
+ </div>
+ <div class="repo-view-content">
+ {{template "repo/view_content" .}}
+ </div>
+ </div>
+ </div>
+</div>
+{{template "base/footer" .}}
diff --git a/templates/repo/view_content.tmpl b/templates/repo/view_content.tmpl
new file mode 100644
index 0000000000..3ba04a9974
--- /dev/null
+++ b/templates/repo/view_content.tmpl
@@ -0,0 +1,111 @@
+{{$isTreePathRoot := not .TreeNames}}
+
+{{template "repo/sub_menu" .}}
+<div class="repo-button-row">
+ <div class="repo-button-row-left">
+ {{if not $isTreePathRoot}}
+ <button class="repo-view-file-tree-toggle-show ui compact basic button icon not-mobile {{if .UserSettingCodeViewShowFileTree}}tw-hidden{{end}}"
+ data-global-click="onRepoViewFileTreeToggle" data-toggle-action="show"
+ data-tooltip-content="{{ctx.Locale.Tr "repo.diff.show_file_tree"}}">
+ {{svg "octicon-sidebar-collapse"}}
+ </button>
+ {{end}}
+
+ {{template "repo/branch_dropdown" dict
+ "Repository" .Repository
+ "ShowTabBranches" true
+ "ShowTabTags" true
+ "CurrentRefType" .RefFullName.RefType
+ "CurrentRefShortName" .RefFullName.ShortName
+ "CurrentTreePath" .TreePath
+ "RefLinkTemplate" "{RepoLink}/src/{RefType}/{RefShortName}/{TreePath}"
+ "AllowCreateNewRef" .CanCreateBranch
+ "ShowViewAllRefsEntry" true
+ }}
+
+ {{if and .CanCompareOrPull .RefFullName.IsBranch (not .Repository.IsArchived)}}
+ {{$cmpBranch := ""}}
+ {{if ne .Repository.ID .BaseRepo.ID}}
+ {{$cmpBranch = printf "%s/%s:" (.Repository.OwnerName|PathEscape) (.Repository.Name|PathEscape)}}
+ {{end}}
+ {{$cmpBranch = print $cmpBranch (.BranchName|PathEscapeSegments)}}
+ {{$compareLink := printf "%s/compare/%s...%s" .BaseRepo.Link (.BaseRepo.DefaultBranch|PathEscapeSegments) $cmpBranch}}
+ <a id="new-pull-request" role="button" class="ui compact basic button" href="{{QueryBuild $compareLink "expand" 1}}"
+ data-tooltip-content="{{if .PullRequestCtx.Allowed}}{{ctx.Locale.Tr "repo.pulls.compare_changes"}}{{else}}{{ctx.Locale.Tr "action.compare_branch"}}{{end}}">
+ {{svg "octicon-git-pull-request"}}
+ </a>
+ {{end}}
+
+ <!-- Show go to file if on home page -->
+ {{if $isTreePathRoot}}
+ <a href="{{.Repository.Link}}/find/{{.RefTypeNameSubURL}}" class="ui compact basic button">{{ctx.Locale.Tr "repo.find_file.go_to_file"}}</a>
+ {{end}}
+
+ {{if and .RefFullName.IsBranch (not .IsViewFile)}}
+ <button class="ui dropdown basic compact jump button repo-add-file" {{if not .Repository.CanEnableEditor}}disabled{{end}}>
+ {{ctx.Locale.Tr "repo.editor.add_file"}}
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ <div class="menu">
+ <a class="item" href="{{.RepoLink}}/_new/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">
+ {{ctx.Locale.Tr "repo.editor.new_file"}}
+ </a>
+ {{if .RepositoryUploadEnabled}}
+ <a class="item" href="{{.RepoLink}}/_upload/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">
+ {{ctx.Locale.Tr "repo.editor.upload_file"}}
+ </a>
+ {{end}}
+ <a class="item" href="{{.RepoLink}}/_diffpatch/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">
+ {{ctx.Locale.Tr "repo.editor.patch"}}
+ </a>
+ </div>
+ </button>
+ {{end}}
+
+ {{if and $isTreePathRoot .Repository.IsTemplate}}
+ <a role="button" class="ui primary compact button" href="{{AppSubUrl}}/repo/create?template_id={{.Repository.ID}}">
+ {{ctx.Locale.Tr "repo.use_template"}}
+ </a>
+ {{end}}
+
+ {{if not $isTreePathRoot}}
+ {{$treeNameIdxLast := Eval (len .TreeNames) "-" 1}}
+ <span class="breadcrumb">
+ <a class="section" href="{{.RepoLink}}/src/{{.RefTypeNameSubURL}}" title="{{.Repository.Name}}">{{StringUtils.EllipsisString .Repository.Name 30}}</a>
+ {{- range $i, $v := .TreeNames -}}
+ <span class="breadcrumb-divider">/</span>
+ {{- if eq $i $treeNameIdxLast -}}
+ <span class="active section" title="{{$v}}">{{$v}}</span>
+ <button class="btn interact-fg tw-mx-1" data-clipboard-text="{{$.TreePath}}" data-tooltip-content="{{ctx.Locale.Tr "copy_path"}}">{{svg "octicon-copy" 14}}</button>
+ {{- else -}}
+ {{$p := index $.Paths $i}}<span class="section"><a href="{{$.BranchLink}}/{{PathEscapeSegments $p}}" title="{{$v}}">{{$v}}</a></span>
+ {{- end -}}
+ {{- end -}}
+ </span>
+ {{end}}
+ </div>
+
+ <div class="repo-button-row-right">
+ <!-- Only show clone panel in repository home page -->
+ {{if $isTreePathRoot}}
+ {{template "repo/clone_panel" .}}
+ {{end}}
+ {{if and (not $isTreePathRoot) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}}
+ <a class="ui button" href="{{.RepoLink}}/commits/{{.RefTypeNameSubURL}}/{{.TreePath | PathEscapeSegments}}">
+ {{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}}
+ </a>
+ {{end}}
+ </div>
+</div>
+{{if .IsViewFile}}
+ {{template "repo/view_file" .}}
+{{else if .IsBlame}}
+ {{template "repo/blame" .}}
+{{else}}{{/* IsViewDirectory */}}
+ {{if $isTreePathRoot}}
+ {{template "repo/code/upstream_diverging_info" .}}
+ {{end}}
+ {{template "repo/view_list" .}}
+ {{if and .ReadmeExist (or .IsMarkup .IsPlainText)}}
+ {{template "repo/view_file" .}}
+ {{end}}
+{{end}}
diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl
index 0a43e3db54..1486d7181d 100644
--- a/templates/repo/view_file.tmpl
+++ b/templates/repo/view_file.tmpl
@@ -1,4 +1,6 @@
-<div {{if .ReadmeInList}}id="readme" {{end}}class="{{TabSizeClass .Editorconfig .FileName}} non-diff-file-content">
+<div {{if .ReadmeInList}}id="readme"{{end}} class="{{TabSizeClass .Editorconfig .FileTreePath}} non-diff-file-content"
+ data-global-init="initRepoFileView" data-raw-file-link="{{.RawFileLink}}">
+
{{- if .FileError}}
<div class="ui error message">
<div class="text left tw-whitespace-pre">{{.FileError}}</div>
@@ -27,37 +29,41 @@
<div class="file-header-left tw-flex tw-items-center tw-py-2 tw-pr-4">
{{if .ReadmeInList}}
{{svg "octicon-book" 16 "tw-mr-2"}}
- <strong><a class="muted" href="#readme">{{.FileName}}</a></strong>
+ <strong><a class="muted" href="#readme">{{.ReadmeInList}}</a></strong>
{{else}}
{{template "repo/file_info" .}}
{{end}}
</div>
- <div class="file-header-right file-actions tw-flex tw-items-center tw-flex-wrap">
- {{if .HasSourceRenderedToggle}}
- <div class="ui compact icon buttons">
- <a href="?display=source" class="ui mini basic button {{if .IsDisplayingSource}}active{{end}}" data-tooltip-content="{{ctx.Locale.Tr "repo.file_view_source"}}">{{svg "octicon-code" 15}}</a>
- <a href="{{$.Link}}" class="ui mini basic button {{if .IsDisplayingRendered}}active{{end}}" data-tooltip-content="{{ctx.Locale.Tr "repo.file_view_rendered"}}">{{svg "octicon-file" 15}}</a>
- </div>
- {{end}}
+ <div class="file-header-right file-actions flex-text-block tw-flex-wrap">
+ {{/* this componment is also controlled by frontend plugin renders */}}
+ <div class="ui compact icon buttons file-view-toggle-buttons {{Iif .HasSourceRenderedToggle "" "tw-hidden"}}">
+ {{if .IsRepresentableAsText}}
+ <a href="?display=source" class="ui mini basic button file-view-toggle-source {{if .IsDisplayingSource}}active{{end}}" data-tooltip-content="{{ctx.Locale.Tr "repo.file_view_source"}}">{{svg "octicon-code" 15}}</a>
+ {{end}}
+ <a href="?display=rendered" class="ui mini basic button file-view-toggle-rendered {{if not .IsDisplayingSource}}active{{end}}" data-tooltip-content="{{ctx.Locale.Tr "repo.file_view_rendered"}}">{{svg "octicon-file" 15}}</a>
+ </div>
{{if not .ReadmeInList}}
<div class="ui buttons tw-mr-1">
<a class="ui mini basic button" href="{{$.RawFileLink}}">{{ctx.Locale.Tr "repo.file_raw"}}</a>
- {{if not .IsViewCommit}}
+ {{if or .RefFullName.IsBranch .RefFullName.IsTag}}
<a class="ui mini basic button" href="{{.RepoLink}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}">{{ctx.Locale.Tr "repo.file_permalink"}}</a>
{{end}}
{{if .IsRepresentableAsText}}
- <a class="ui mini basic button" href="{{.RepoLink}}/blame/{{.BranchNameSubURL}}/{{PathEscapeSegments .TreePath}}">{{ctx.Locale.Tr "repo.blame"}}</a>
+ <a class="ui mini basic button" href="{{.RepoLink}}/blame/{{.RefTypeNameSubURL}}/{{PathEscapeSegments .TreePath}}">{{ctx.Locale.Tr "repo.blame"}}</a>
{{end}}
- <a class="ui mini basic button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{PathEscapeSegments .TreePath}}">{{ctx.Locale.Tr "repo.file_history"}}</a>
+ <a class="ui mini basic button" href="{{.RepoLink}}/commits/{{.RefTypeNameSubURL}}/{{PathEscapeSegments .TreePath}}">{{ctx.Locale.Tr "repo.file_history"}}</a>
{{if .EscapeStatus.Escaped}}
<button class="ui mini basic button unescape-button tw-hidden">{{ctx.Locale.Tr "repo.unescape_control_characters"}}</button>
<button class="ui mini basic button escape-button">{{ctx.Locale.Tr "repo.escape_control_characters"}}</button>
{{end}}
</div>
<a download class="btn-octicon" data-tooltip-content="{{ctx.Locale.Tr "repo.download_file"}}" href="{{$.RawFileLink}}">{{svg "octicon-download"}}</a>
- <a id="copy-content" class="btn-octicon {{if not .CanCopyContent}} disabled{{end}}"{{if or .IsImageFile (and .HasSourceRenderedToggle (not .IsDisplayingSource))}} data-link="{{$.RawFileLink}}"{{end}} data-tooltip-content="{{if .CanCopyContent}}{{ctx.Locale.Tr "copy_content"}}{{else}}{{ctx.Locale.Tr "copy_type_unsupported"}}{{end}}">{{svg "octicon-copy"}}</a>
+ <a class="btn-octicon {{if not .CanCopyContent}}disabled{{end}}" data-global-click="onCopyContentButtonClick"
+ {{if not .IsDisplayingSource}}data-raw-file-link="{{$.RawFileLink}}"{{end}}
+ data-tooltip-content="{{if .CanCopyContent}}{{ctx.Locale.Tr "copy_content"}}{{else}}{{ctx.Locale.Tr "copy_type_unsupported"}}{{end}}"
+ >{{svg "octicon-copy"}}</a>
{{if .EnableFeed}}
- <a class="btn-octicon" href="{{$.FeedURL}}/rss/{{$.BranchNameSubURL}}/{{PathEscapeSegments .TreePath}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">
+ <a class="btn-octicon" href="{{$.RepoLink}}/rss/{{$.RefTypeNameSubURL}}/{{PathEscapeSegments .TreePath}}" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">
{{svg "octicon-rss"}}
</a>
{{end}}
@@ -78,45 +84,28 @@
<button class="ui mini basic button escape-button tw-mr-1">{{ctx.Locale.Tr "repo.escape_control_characters"}}</button>
{{end}}
{{if and .ReadmeInList .CanEditReadmeFile}}
- <a class="btn-octicon" data-tooltip-content="{{ctx.Locale.Tr "repo.editor.edit_this_file"}}" href="{{.RepoLink}}/_edit/{{PathEscapeSegments .BranchName}}/{{PathEscapeSegments .TreePath}}/{{PathEscapeSegments .FileName}}">{{svg "octicon-pencil"}}</a>
+ <a class="btn-octicon" data-tooltip-content="{{ctx.Locale.Tr "repo.editor.edit_this_file"}}" href="{{.RepoLink}}/_edit/{{PathEscapeSegments .BranchName}}/{{PathEscapeSegments .FileTreePath}}">{{svg "octicon-pencil"}}</a>
{{end}}
</div>
</h4>
+
<div class="ui bottom attached table unstackable segment">
- {{if not (or .IsMarkup .IsRenderedHTML)}}
- {{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
+ {{if not .IsMarkup}}
+ {{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus}}
{{end}}
- <div class="file-view{{if .IsMarkup}} markup {{.MarkupType}}{{else if .IsPlainText}} plain-text{{else if .IsTextSource}} code-view{{end}}">
+ <div class="file-view {{if .IsMarkup}}markup {{.MarkupType}}{{else if .IsPlainText}}plain-text{{else if .IsDisplayingSource}}code-view{{end}}">
{{if .IsFileTooLarge}}
{{template "shared/filetoolarge" dict "RawFileLink" .RawFileLink}}
{{else if not .FileSize}}
{{template "shared/fileisempty"}}
{{else if .IsMarkup}}
- {{if .FileContent}}{{.FileContent}}{{end}}
+ {{.FileContent}}
{{else if .IsPlainText}}
<pre>{{if .FileContent}}{{.FileContent}}{{end}}</pre>
- {{else if not .IsTextSource}}
- <div class="view-raw">
- {{if .IsImageFile}}
- <img src="{{$.RawFileLink}}">
- {{else if .IsVideoFile}}
- <video controls src="{{$.RawFileLink}}">
- <strong>{{ctx.Locale.Tr "repo.video_not_supported_in_browser"}}</strong>
- </video>
- {{else if .IsAudioFile}}
- <audio controls src="{{$.RawFileLink}}">
- <strong>{{ctx.Locale.Tr "repo.audio_not_supported_in_browser"}}</strong>
- </audio>
- {{else if .IsPDFFile}}
- <div class="pdf-content is-loading" data-src="{{$.RawFileLink}}" data-fallback-button-text="{{ctx.Locale.Tr "repo.diff.view_file"}}"></div>
- {{else}}
- <a href="{{$.RawFileLink}}" rel="nofollow" class="tw-p-4">{{ctx.Locale.Tr "repo.file_view_raw"}}</a>
- {{end}}
- </div>
- {{else if .FileSize}}
+ {{else if .FileContent}}
<table>
<tbody>
- {{range $idx, $code := .FileContent}}
+ {{range $idx, $code := .FileContent}}
{{$line := Eval $idx "+" 1}}
<tr>
<td id="L{{$line}}" class="lines-num"><span id="L{{$line}}" data-line-number="{{$line}}"></span></td>
@@ -125,17 +114,38 @@
{{end}}
<td rel="L{{$line}}" class="lines-code chroma"><code class="code-inner">{{$code}}</code></td>
</tr>
- {{end}}
+ {{end}}
</tbody>
</table>
- <div class="code-line-menu tippy-target">
- {{if $.Permission.CanRead ctx.Consts.RepoUnitTypeIssues}}
- <a class="item ref-in-new-issue" role="menuitem" data-url-issue-new="{{.RepoLink}}/issues/new" data-url-param-body-link="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}" rel="nofollow noindex">{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}</a>
+ {{else}}
+ <div class="view-raw">
+ {{if .IsImageFile}}
+ <img alt="{{$.RawFileLink}}" src="{{$.RawFileLink}}">
+ {{else if .IsVideoFile}}
+ <video controls src="{{$.RawFileLink}}">
+ <strong>{{ctx.Locale.Tr "repo.video_not_supported_in_browser"}}</strong>
+ </video>
+ {{else if .IsAudioFile}}
+ <audio controls src="{{$.RawFileLink}}">
+ <strong>{{ctx.Locale.Tr "repo.audio_not_supported_in_browser"}}</strong>
+ </audio>
+ {{else}}
+ <div class="file-view-render-container">
+ <div class="file-view-raw-prompt tw-p-4">
+ <a href="{{$.RawFileLink}}" rel="nofollow">{{ctx.Locale.Tr "repo.file_view_raw"}}</a>
+ </div>
+ </div>
{{end}}
- <a class="item view_git_blame" role="menuitem" href="{{.Repository.Link}}/blame/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}">{{ctx.Locale.Tr "repo.view_git_blame"}}</a>
- <a class="item copy-line-permalink" role="menuitem" data-url="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}">{{ctx.Locale.Tr "repo.file_copy_permalink"}}</a>
</div>
{{end}}
</div>
+
+ <div class="code-line-menu tippy-target">
+ {{if $.Permission.CanRead ctx.Consts.RepoUnitTypeIssues}}
+ <a class="item ref-in-new-issue" role="menuitem" data-url-issue-new="{{.RepoLink}}/issues/new" data-url-param-body-link="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}" rel="nofollow noindex">{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}</a>
+ {{end}}
+ <a class="item view_git_blame" role="menuitem" href="{{.Repository.Link}}/blame/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}">{{ctx.Locale.Tr "repo.view_git_blame"}}</a>
+ <a class="item copy-line-permalink" role="menuitem" data-url="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}">{{ctx.Locale.Tr "repo.file_copy_permalink"}}</a>
+ </div>
</div>
</div>
diff --git a/templates/repo/view_file_tree.tmpl b/templates/repo/view_file_tree.tmpl
new file mode 100644
index 0000000000..8aed05f346
--- /dev/null
+++ b/templates/repo/view_file_tree.tmpl
@@ -0,0 +1,15 @@
+<div class="flex-text-block repo-button-row">
+ <button class="ui compact basic icon button"
+ data-global-click="onRepoViewFileTreeToggle" data-toggle-action="hide"
+ data-tooltip-content="{{ctx.Locale.Tr "repo.diff.hide_file_tree"}}">
+ {{svg "octicon-sidebar-expand"}}
+ </button>
+ <b>{{ctx.Locale.Tr "files"}}</b>
+</div>
+
+{{/* TODO: Dynamically move components such as refSelector and createPR here */}}
+<div id="view-file-tree" class="tw-overflow-auto tw-h-full is-loading"
+ data-repo-link="{{.RepoLink}}"
+ data-tree-path="{{$.TreePath}}"
+ data-current-ref-name-sub-url="{{.RefTypeNameSubURL}}"
+></div>
diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl
index c8e97d2617..b655f735a3 100644
--- a/templates/repo/view_list.tmpl
+++ b/templates/repo/view_list.tmpl
@@ -4,30 +4,32 @@
{{template "repo/latest_commit" .}}
<div>{{if and .LatestCommit .LatestCommit.Committer}}{{DateUtils.TimeSince .LatestCommit.Committer.When}}{{end}}</div>
</div>
+ {{$.FileIconPoolHTML}}
{{if .HasParentPath}}
<a class="repo-file-line parent-link silenced" href="{{.BranchLink}}{{if .ParentPath}}{{PathEscapeSegments .ParentPath}}{{end}}">
- {{svg "octicon-file-directory-fill"}} ..
+ {{index $.FileIcons ".."}} ..
</a>
{{end}}
{{range $item := .Files}}
<div class="repo-file-item">
{{$entry := $item.Entry}}
{{$commit := $item.Commit}}
- {{$subModuleFile := $item.SubModuleFile}}
- <div class="repo-file-cell name {{if not $commit}}notready{{end}}">
+ {{$submoduleFile := $item.SubmoduleFile}}
+ <div class="repo-file-cell name muted-links {{if not $commit}}notready{{end}}">
+ {{index $.FileIcons $entry.Name}}
{{if $entry.IsSubModule}}
- {{svg "octicon-file-submodule"}}
- {{$refURL := $subModuleFile.RefURL AppUrl $.Repository.FullName $.SSHDomain}} {{/* FIXME: the usage of AppUrl seems incorrect, it would be fixed in the future, use AppSubUrl instead */}}
- {{if $refURL}}
- <a class="muted" href="{{$refURL}}">{{$entry.Name}}</a> <span class="at">@</span> <a href="{{$refURL}}/commit/{{PathEscape $subModuleFile.RefID}}">{{ShortSha $subModuleFile.RefID}}</a>
+ {{$submoduleLink := $submoduleFile.SubmoduleWebLink ctx}}
+ {{if $submoduleLink}}
+ <a class="entry-name" href="{{$submoduleLink.RepoWebLink}}" title="{{$entry.Name}}">{{$entry.Name}}</a>
+ @ <a class="text primary" href="{{$submoduleLink.CommitWebLink}}">{{ShortSha $submoduleFile.RefID}}</a>
{{else}}
- {{$entry.Name}} <span class="at">@</span> {{ShortSha $subModuleFile.RefID}}
+ <span class="entry-name" title="{{$entry.Name}}">{{$entry.Name}}</span>
+ @ {{ShortSha $submoduleFile.RefID}}
{{end}}
{{else}}
{{if $entry.IsDir}}
{{$subJumpablePathName := $entry.GetSubJumpablePathName}}
- {{svg "octicon-file-directory-fill"}}
- <a class="muted" href="{{$.TreeLink}}/{{PathEscapeSegments $subJumpablePathName}}" title="{{$subJumpablePathName}}">
+ <a class="entry-name" href="{{$.TreeLink}}/{{PathEscapeSegments $subJumpablePathName}}" title="{{$subJumpablePathName}}">
{{$subJumpablePathFields := StringUtils.Split $subJumpablePathName "/"}}
{{$subJumpablePathFieldLast := (Eval (len $subJumpablePathFields) "-" 1)}}
{{if eq $subJumpablePathFieldLast 0}}
@@ -38,15 +40,17 @@
{{end}}
</a>
{{else}}
- {{svg (printf "octicon-%s" (EntryIcon $entry))}}
- <a class="muted" href="{{$.TreeLink}}/{{PathEscapeSegments $entry.Name}}" title="{{$entry.Name}}">{{$entry.Name}}</a>
+ <a class="entry-name" href="{{$.TreeLink}}/{{PathEscapeSegments $entry.Name}}" title="{{$entry.Name}}">{{$entry.Name}}</a>
+ {{if $entry.IsLink}}
+ <a class="entry-symbol-link flex-text-inline" data-tooltip-content title="{{ctx.Locale.Tr "repo.find_file.follow_symlink"}}" href="{{$.TreeLink}}/{{PathEscapeSegments $entry.Name}}?follow_symlink=1">{{svg "octicon-link" 12}}</a>
+ {{end}}
{{end}}
{{end}}
</div>
<div class="repo-file-cell message loading-icon-2px">
{{if $commit}}
{{$commitLink := printf "%s/commit/%s" $.RepoLink (PathEscape $commit.ID.String)}}
- {{ctx.RenderUtils.RenderCommitMessageLinkSubject $commit.Message $commitLink ($.Repository.ComposeMetas ctx)}}
+ {{ctx.RenderUtils.RenderCommitMessageLinkSubject $commit.Message $commitLink $.Repository}}
{{else}}
… {{/* will be loaded again by LastCommitLoaderURL */}}
{{end}}
diff --git a/templates/repo/watch_unwatch.tmpl b/templates/repo/watch_unwatch.tmpl
index 465cd91c2b..6f2e5b7a19 100644
--- a/templates/repo/watch_unwatch.tmpl
+++ b/templates/repo/watch_unwatch.tmpl
@@ -1,10 +1,10 @@
-<form hx-boost="true" hx-target="this" method="post" action="{{$.RepoLink}}/action/{{if $.IsWatchingRepo}}unwatch{{else}}watch{{end}}">
+<form class="flex-text-inline" hx-boost="true" hx-target="this" method="post" action="{{$.RepoLink}}/action/{{if $.IsWatchingRepo}}unwatch{{else}}watch{{end}}">
<div class="ui labeled button" {{if not $.IsSigned}}data-tooltip-content="{{ctx.Locale.Tr "repo.watch_guest_user"}}"{{end}}>
{{$buttonText := ctx.Locale.Tr "repo.watch"}}
{{if $.IsWatchingRepo}}{{$buttonText = ctx.Locale.Tr "repo.unwatch"}}{{end}}
<button type="submit" class="ui compact small basic button"{{if not $.IsSigned}} disabled{{end}} aria-label="{{$buttonText}}">
{{svg "octicon-eye"}}
- <span aria-hidden="true">{{$buttonText}}</span>
+ <span class="not-mobile" aria-hidden="true">{{$buttonText}}</span>
</button>
<a hx-boost="false" class="ui basic label" href="{{.RepoLink}}/watchers">
{{CountFmt .Repository.NumWatches}}
diff --git a/templates/repo/wiki/new.tmpl b/templates/repo/wiki/new.tmpl
index ea2913c0fd..12f0983904 100644
--- a/templates/repo/wiki/new.tmpl
+++ b/templates/repo/wiki/new.tmpl
@@ -18,7 +18,7 @@
{{ctx.Locale.Tr "repo.wiki.page_name_desc"}}
</div>
- {{$content := .content}}
+ {{$content := .WikiEditContent}}
{{if not .PageIsWikiEdit}}
{{$content = ctx.Locale.Tr "repo.wiki.welcome"}}
{{end}}
@@ -35,7 +35,7 @@
<input name="message" aria-label="{{ctx.Locale.Tr "repo.wiki.default_commit_message"}}" placeholder="{{ctx.Locale.Tr "repo.wiki.default_commit_message"}}">
</div>
<div class="divider"></div>
- <div class="text right">
+ <div class="flex-text-block tw-justify-end">
<a class="ui basic cancel button" href="{{.Link}}">{{ctx.Locale.Tr "cancel"}}</a>
<button class="ui primary button">{{ctx.Locale.Tr "repo.wiki.save_page"}}</button>
</div>
diff --git a/templates/repo/wiki/pages.tmpl b/templates/repo/wiki/pages.tmpl
index 38d6d6f944..5ceb8a4d5b 100644
--- a/templates/repo/wiki/pages.tmpl
+++ b/templates/repo/wiki/pages.tmpl
@@ -10,7 +10,7 @@
{{end}}
</span>
</h2>
- {{if .IsRepositoryAdmin}}<div>{{ctx.Locale.Tr "repo.default_branch"}}: {{.Repository.DefaultWikiBranch}}</div>{{end}}
+ {{if $.Permission.IsAdmin}}<div>{{ctx.Locale.Tr "repo.default_branch"}}: {{.Repository.DefaultWikiBranch}}</div>{{end}}
<table class="ui table wiki-pages-list">
<tbody>
{{range .Pages}}
@@ -21,7 +21,7 @@
<a class="wiki-git-entry" href="{{$.RepoLink}}/wiki/{{.GitEntryName | PathEscape}}" data-tooltip-content="{{ctx.Locale.Tr "repo.wiki.original_git_entry_tooltip"}}">{{svg "octicon-chevron-right"}}</a>
</td>
{{$timeSince := DateUtils.TimeSince .UpdatedUnix}}
- <td class="text right">{{ctx.Locale.Tr "repo.wiki.last_updated" $timeSince}}</td>
+ <td class="tw-text-right">{{ctx.Locale.Tr "repo.wiki.last_updated" $timeSince}}</td>
</tr>
{{end}}
</tbody>
diff --git a/templates/repo/wiki/revision.tmpl b/templates/repo/wiki/revision.tmpl
index ca8954928d..108e378937 100644
--- a/templates/repo/wiki/revision.tmpl
+++ b/templates/repo/wiki/revision.tmpl
@@ -3,18 +3,18 @@
{{template "repo/header" .}}
{{$title := .title}}
<div class="ui container">
- <div class="ui stackable grid">
- <div class="ui eight wide column">
- <div class="ui header">
- <a class="file-revisions-btn ui basic button" title="{{ctx.Locale.Tr "repo.wiki.back_to_wiki"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}"><span>{{.revision}}</span> {{svg "octicon-home"}}</a>
+ <div class="ui dividing header flex-text-block tw-flex-wrap tw-justify-between">
+ <div class="flex-text-block">
+ <a class="ui basic button tw-px-3" title="{{ctx.Locale.Tr "repo.wiki.back_to_wiki"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}">{{svg "octicon-home"}}</a>
+ <div class="tw-flex-1 gt-ellipsis">
{{$title}}
- <div class="ui sub header tw-break-anywhere">
+ <div class="ui sub header gt-ellipsis">
{{$timeSince := DateUtils.TimeSince .Author.When}}
{{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince}}
</div>
</div>
</div>
- <div class="ui eight wide column text right">
+ <div class="flex-text-block">
{{template "repo/clone_panel" .}}
</div>
</div>
diff --git a/templates/repo/wiki/start.tmpl b/templates/repo/wiki/start.tmpl
index 1b3c3d538a..e622db5eb5 100644
--- a/templates/repo/wiki/start.tmpl
+++ b/templates/repo/wiki/start.tmpl
@@ -7,7 +7,7 @@
<h2>{{ctx.Locale.Tr "repo.wiki.welcome"}}</h2>
<p>{{ctx.Locale.Tr "repo.wiki.welcome_desc"}}</p>
{{if and .CanWriteWiki (not .Repository.IsMirror)}}
- <a class="ui primary button" href="{{.RepoLink}}/wiki?action=_new">{{ctx.Locale.Tr "repo.wiki.create_first_page"}}</a>
+ <a class="ui primary button tw-mr-0" href="{{.RepoLink}}/wiki?action=_new">{{ctx.Locale.Tr "repo.wiki.create_first_page"}}</a>
{{end}}
</div>
</div>
diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl
index 843a977e3e..4c7ef364d2 100644
--- a/templates/repo/wiki/view.tmpl
+++ b/templates/repo/wiki/view.tmpl
@@ -33,7 +33,7 @@
<div class="ui dividing header">
<div class="flex-text-block tw-flex-wrap tw-justify-end">
<div class="flex-text-block tw-flex-1 tw-min-w-[300px]">
- <a class="file-revisions-btn ui basic button" title="{{ctx.Locale.Tr "repo.wiki.file_revision"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}?action=_revision" ><span>{{.CommitCount}}</span> {{svg "octicon-history"}}</a>
+ <a class="ui basic button tw-px-3 tw-gap-3" title="{{ctx.Locale.Tr "repo.wiki.file_revision"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}?action=_revision" >{{if .CommitCount}}<span>{{.CommitCount}}</span> {{end}}{{svg "octicon-history"}}</a>
<div class="tw-flex-1 gt-ellipsis">
{{$title}}
<div class="ui sub header gt-ellipsis">
@@ -50,7 +50,7 @@
{{if and .CanWriteWiki (not .Repository.IsMirror)}}
<a class="ui small button" href="{{.RepoLink}}/wiki/{{.PageURL}}?action=_edit">{{ctx.Locale.Tr "repo.wiki.edit_page_button"}}</a>
<a class="ui small primary button" href="{{.RepoLink}}/wiki?action=_new">{{ctx.Locale.Tr "repo.wiki.new_page_button"}}</a>
- <a class="ui small red button delete-button" href="" data-url="{{.RepoLink}}/wiki/{{.PageURL}}?action=_delete" data-id="{{.PageURL}}">{{ctx.Locale.Tr "repo.wiki.delete_page_button"}}</a>
+ <a class="ui small red button link-action" href data-modal-confirm="#repo-wiki-delete-page-modal" data-url="{{.RepoLink}}/wiki/{{.PageURL}}?action=_delete">{{ctx.Locale.Tr "repo.wiki.delete_page_button"}}</a>
{{end}}
</div>
</div>
@@ -62,50 +62,43 @@
{{end}}
<div class="wiki-content-parts">
- {{if .sidebarTocContent}}
- <div class="markup wiki-content-sidebar wiki-content-toc">
- {{.sidebarTocContent | SafeHTML}}
+ {{if .WikiSidebarTocHTML}}
+ <div class="render-content markup wiki-content-sidebar wiki-content-toc">
+ {{.WikiSidebarTocHTML}}
</div>
{{end}}
- <div class="markup wiki-content-main {{if or .sidebarTocContent .sidebarPresent}}with-sidebar{{end}}">
- {{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
- {{.content | SafeHTML}}
+ <div class="render-content markup wiki-content-main {{if or .WikiSidebarTocHTML .WikiSidebarHTML}}with-sidebar{{end}}">
+ {{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus}}
+ {{.WikiContentHTML}}
</div>
- {{if .sidebarPresent}}
- <div class="markup wiki-content-sidebar">
+ {{if .WikiSidebarHTML}}
+ <div class="render-content markup wiki-content-sidebar">
{{if and .CanWriteWiki (not .Repository.IsMirror)}}
<a class="tw-float-right muted" href="{{.RepoLink}}/wiki/_Sidebar?action=_edit" aria-label="{{ctx.Locale.Tr "repo.wiki.edit_page_button"}}">{{svg "octicon-pencil"}}</a>
{{end}}
- {{template "repo/unicode_escape_prompt" dict "EscapeStatus" .sidebarEscapeStatus "root" $}}
- {{.sidebarContent | SafeHTML}}
+ {{.WikiSidebarHTML}}
</div>
{{end}}
<div class="tw-clear-both"></div>
- {{if .footerPresent}}
- <div class="markup wiki-content-footer">
+ {{if .WikiFooterHTML}}
+ <div class="render-content markup wiki-content-footer">
{{if and .CanWriteWiki (not .Repository.IsMirror)}}
<a class="tw-float-right muted" href="{{.RepoLink}}/wiki/_Footer?action=_edit" aria-label="{{ctx.Locale.Tr "repo.wiki.edit_page_button"}}">{{svg "octicon-pencil"}}</a>
{{end}}
- {{template "repo/unicode_escape_prompt" dict "footerEscapeStatus" .sidebarEscapeStatus "root" $}}
- {{.footerContent | SafeHTML}}
+ {{.WikiFooterHTML}}
</div>
{{end}}
</div>
</div>
</div>
-<div class="ui g-modal-confirm delete modal">
- <div class="header">
- {{svg "octicon-trash"}}
- {{ctx.Locale.Tr "repo.wiki.delete_page_button"}}
- </div>
- <div class="content">
- <p>{{ctx.Locale.Tr "repo.wiki.delete_page_notice_1" $title}}</p>
- </div>
+<div class="ui small modal" id="repo-wiki-delete-page-modal">
+ <div class="header">{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.wiki.delete_page_button"}}</div>
+ <div class="content"><p>{{ctx.Locale.Tr "repo.wiki.delete_page_notice_1" $title}}</p></div>
{{template "base/modal_actions_confirm" .}}
</div>
diff --git a/templates/shared/actions/runner_badge.tmpl b/templates/shared/actions/runner_badge.tmpl
deleted file mode 100644
index 816e87e177..0000000000
--- a/templates/shared/actions/runner_badge.tmpl
+++ /dev/null
@@ -1,25 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{.Badge.Width}}" height="18"
- role="img" aria-label="{{.Badge.Label.Text}}: {{.Badge.Message.Text}}">
- <title>{{.Badge.Label.Text}}: {{.Badge.Message.Text}}</title>
- <linearGradient id="s" x2="0" y2="100%">
- <stop offset="0" stop-color="#fff" stop-opacity=".7" />
- <stop offset=".1" stop-color="#aaa" stop-opacity=".1" />
- <stop offset=".9" stop-color="#000" stop-opacity=".3" />
- <stop offset="1" stop-color="#000" stop-opacity=".5" />
- </linearGradient>
- <clipPath id="r">
- <rect width="{{.Badge.Width}}" height="18" rx="4" fill="#fff" />
- </clipPath>
- <g clip-path="url(#r)">
- <rect width="{{.Badge.Label.Width}}" height="18" fill="#555" />
- <rect x="{{.Badge.Label.Width}}" width="{{.Badge.Message.Width}}" height="18" fill="{{.Badge.Color}}" />
- <rect width="{{.Badge.Width}}" height="18" fill="url(#s)" />
- </g>
- <g fill="#fff" text-anchor="middle" font-family="Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision"
- font-size="{{.Badge.FontSize}}"><text aria-hidden="true" x="{{.Badge.Label.X}}" y="140" fill="#010101" fill-opacity=".3"
- transform="scale(.1)" textLength="{{.Badge.Label.TextLength}}">{{.Badge.Label.Text}}</text><text x="{{.Badge.Label.X}}" y="130"
- transform="scale(.1)" fill="#fff" textLength="{{.Badge.Label.TextLength}}">{{.Badge.Label.Text}}</text><text aria-hidden="true"
- x="{{.Badge.Message.X}}" y="140" fill="#010101" fill-opacity=".3" transform="scale(.1)"
- textLength="{{.Badge.Message.TextLength}}">{{.Badge.Message.Text}}</text><text x="{{.Badge.Message.X}}" y="130" transform="scale(.1)"
- fill="#fff" textLength="{{.Badge.Message.TextLength}}">{{.Badge.Message.Text}}</text></g>
-</svg>
diff --git a/templates/shared/actions/runner_badge_flat-square.tmpl b/templates/shared/actions/runner_badge_flat-square.tmpl
new file mode 100644
index 0000000000..cc1dc1e8f3
--- /dev/null
+++ b/templates/shared/actions/runner_badge_flat-square.tmpl
@@ -0,0 +1,15 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{.Badge.Width}}" height="20"
+ role="img" aria-label="{{.Badge.Label.Text}}: {{.Badge.Message.Text}}">
+ <title>{{.Badge.Label.Text}}: {{.Badge.Message.Text}}</title>
+ <g shape-rendering="crispEdges">
+ <rect width="{{.Badge.Label.Width}}" height="20" fill="#555" />
+ <rect x="{{.Badge.Label.Width}}" width="{{.Badge.Message.Width}}" height="20" fill="{{.Badge.Color}}" />
+ </g>
+ <g fill="#fff" text-anchor="middle" font-family="{{.Badge.FontFamily}}"
+ text-rendering="geometricPrecision" font-size="{{.Badge.FontSize}}">
+ <text x="{{.Badge.Label.X}}" y="140"
+ transform="scale(.1)" fill="#fff" textLength="{{.Badge.Label.TextLength}}">{{.Badge.Label.Text}}</text>
+ <text x="{{.Badge.Message.X}}" y="140" transform="scale(.1)" fill="#fff"
+ textLength="{{.Badge.Message.TextLength}}">{{.Badge.Message.Text}}</text>
+ </g>
+</svg>
diff --git a/templates/shared/actions/runner_badge_flat.tmpl b/templates/shared/actions/runner_badge_flat.tmpl
new file mode 100644
index 0000000000..1ba9be09fb
--- /dev/null
+++ b/templates/shared/actions/runner_badge_flat.tmpl
@@ -0,0 +1,27 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{.Badge.Width}}" height="20"
+ role="img" aria-label="{{.Badge.Label.Text}}: {{.Badge.Message.Text}}">
+ <title>{{.Badge.Label.Text}}: {{.Badge.Message.Text}}</title>
+ <linearGradient id="{{.Badge.IDPrefix}}s" x2="0" y2="100%">
+ <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
+ <stop offset="1" stop-opacity=".1" />
+ </linearGradient>
+ <clipPath id="{{.Badge.IDPrefix}}r">
+ <rect width="{{.Badge.Width}}" height="20" rx="3" fill="#fff" />
+ </clipPath>
+ <g clip-path="url(#{{.Badge.IDPrefix}}r)">
+ <rect width="{{.Badge.Label.Width}}" height="20" fill="#555" />
+ <rect x="{{.Badge.Label.Width}}" width="{{.Badge.Message.Width}}" height="20" fill="{{.Badge.Color}}" />
+ <rect width="{{.Badge.Width}}" height="20" fill="url(#{{.Badge.IDPrefix}}s)" />
+ </g>
+ <g fill="#fff" text-anchor="middle" font-family="{{.Badge.FontFamily}}"
+ text-rendering="geometricPrecision" font-size="{{.Badge.FontSize}}">
+ <text aria-hidden="true" x="{{.Badge.Label.X}}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)"
+ textLength="{{.Badge.Label.TextLength}}">{{.Badge.Label.Text}}</text>
+ <text x="{{.Badge.Label.X}}" y="140"
+ transform="scale(.1)" fill="#fff" textLength="{{.Badge.Label.TextLength}}">{{.Badge.Label.Text}}</text>
+ <text aria-hidden="true" x="{{.Badge.Message.X}}" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)"
+ textLength="{{.Badge.Message.TextLength}}">{{.Badge.Message.Text}}</text>
+ <text x="{{.Badge.Message.X}}" y="140" transform="scale(.1)" fill="#fff"
+ textLength="{{.Badge.Message.TextLength}}">{{.Badge.Message.Text}}</text>
+ </g>
+</svg>
diff --git a/templates/shared/actions/runner_edit.tmpl b/templates/shared/actions/runner_edit.tmpl
index 54250f830b..d452d69f7a 100644
--- a/templates/shared/actions/runner_edit.tmpl
+++ b/templates/shared/actions/runner_edit.tmpl
@@ -40,7 +40,7 @@
<div class="field">
<button class="ui primary button" data-url="{{.Link}}">{{ctx.Locale.Tr "actions.runners.update_runner"}}</button>
- <button class="ui red button delete-button show-modal" data-url="{{.Link}}/delete" data-modal="#runner-delete-modal">
+ <button class="ui red button delete-button" data-url="{{.Link}}/delete" data-modal="#runner-delete-modal">
{{ctx.Locale.Tr "actions.runners.delete_runner"}}</button>
</div>
</form>
diff --git a/templates/shared/actions/runner_list.tmpl b/templates/shared/actions/runner_list.tmpl
index e5907da8e8..43321a8dc5 100644
--- a/templates/shared/actions/runner_list.tmpl
+++ b/templates/shared/actions/runner_list.tmpl
@@ -16,7 +16,7 @@
<div class="header">
Registration Token
</div>
- <div class="ui input">
+ <div class="ui action input">
<input type="text" value="{{.RegistrationToken}}" readonly>
<button class="ui basic label button" aria-label="{{ctx.Locale.Tr "copy"}}" data-clipboard-text="{{.RegistrationToken}}">
{{svg "octicon-copy" 14}}
@@ -64,30 +64,26 @@
</tr>
</thead>
<tbody>
- {{if .Runners}}
- {{range .Runners}}
+ {{range .Runners}}
<tr>
- <td>
- <span class="ui {{if .IsOnline}}green{{end}} label">{{.StatusLocaleName ctx.Locale}}</span>
- </td>
+ <td><span class="ui label {{if .IsOnline}}green{{end}}">{{.StatusLocaleName ctx.Locale}}</span></td>
<td>{{.ID}}</td>
<td><p data-tooltip-content="{{.Description}}">{{.Name}}</p></td>
<td>{{if .Version}}{{.Version}}{{else}}{{ctx.Locale.Tr "unknown"}}{{end}}</td>
<td><span data-tooltip-content="{{.BelongsToOwnerName}}">{{.BelongsToOwnerType.LocaleString ctx.Locale}}</span></td>
- <td class="tw-flex tw-flex-wrap tw-gap-2 runner-tags">
- {{range .AgentLabels}}<span class="ui label">{{.}}</span>{{end}}
+ <td>
+ <span class="flex-text-inline">{{range .AgentLabels}}<span class="ui label">{{.}}</span>{{end}}</span>
</td>
<td>{{if .LastOnline}}{{DateUtils.TimeSince .LastOnline}}{{else}}{{ctx.Locale.Tr "never"}}{{end}}</td>
- <td class="runner-ops">
- {{if .Editable $.RunnerOwnerID $.RunnerRepoID}}
- <a href="{{$.Link}}/{{.ID}}">{{svg "octicon-pencil"}}</a>
+ <td>
+ {{if .EditableInContext $.RunnerOwnerID $.RunnerRepoID}}
+ <a href="{{$.Link}}/{{.ID}}">{{svg "octicon-pencil"}}</a>
{{end}}
</td>
</tr>
- {{end}}
{{else}}
<tr>
- <td class="center aligned" colspan="8">{{ctx.Locale.Tr "actions.runners.none"}}</td>
+ <td class="tw-text-center" colspan="8">{{ctx.Locale.Tr "actions.runners.none"}}</td>
</tr>
{{end}}
</tbody>
diff --git a/templates/shared/avatar_upload_crop.tmpl b/templates/shared/avatar_upload_crop.tmpl
new file mode 100644
index 0000000000..3bc012dd99
--- /dev/null
+++ b/templates/shared/avatar_upload_crop.tmpl
@@ -0,0 +1,8 @@
+{{- /* we do not need to set for/id here, global aria init code will add them automatically */ -}}
+<label>{{.LabelText}}</label>
+<input class="avatar-file-with-cropper" name="avatar" type="file" accept="image/png,image/jpeg,image/gif,image/webp" data-global-init="initAvatarUploader">
+{{- /* the cropper-panel must be next sibling of the input "avatar" */ -}}
+<div class="cropper-panel tw-hidden">
+ <div class="tw-my-2">{{ctx.Locale.Tr "settings.cropper_prompt"}}</div>
+ <div class="cropper-wrapper"><img class="cropper-source" src alt></div>
+</div>
diff --git a/templates/shared/combomarkdowneditor.tmpl b/templates/shared/combomarkdowneditor.tmpl
index b1c3b29cf3..fa3e6c6ade 100644
--- a/templates/shared/combomarkdowneditor.tmpl
+++ b/templates/shared/combomarkdowneditor.tmpl
@@ -81,7 +81,7 @@
}
</script>
</div>
- <div class="ui tab markup" data-tab-panel="markdown-previewer">
+ <div class="ui tab" data-tab-panel="markdown-previewer">
{{ctx.Locale.Tr "loading"}}
</div>
<div class="markdown-add-table-panel tippy-target">
diff --git a/templates/shared/issueicon.tmpl b/templates/shared/issueicon.tmpl
index f828de5c66..bb6247c708 100644
--- a/templates/shared/issueicon.tmpl
+++ b/templates/shared/issueicon.tmpl
@@ -1,3 +1,4 @@
+{{/* the logic should be kept the same as getIssueIcon/getIssueColor in JS code */}}
{{- if .IsPull -}}
{{- if not .PullRequest -}}
No PullRequest
@@ -6,7 +7,7 @@
{{- if .PullRequest.HasMerged -}}
{{- svg "octicon-git-merge" 16 "text purple" -}}
{{- else -}}
- {{- svg "octicon-git-pull-request" 16 "text red" -}}
+ {{- svg "octicon-git-pull-request-closed" 16 "text red" -}}
{{- end -}}
{{- else -}}
{{- if .PullRequest.IsWorkInProgress ctx -}}
diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl
index e8015b40ea..98c26b32dc 100644
--- a/templates/shared/issuelist.tmpl
+++ b/templates/shared/issuelist.tmpl
@@ -3,11 +3,14 @@
{{range .Issues}}
<div class="flex-item">
- <div class="flex-item-icon">
- {{if $.CanWriteIssuesOrPulls}}
- <input type="checkbox" autocomplete="off" class="issue-checkbox tw-mr-4" data-issue-id={{.ID}} aria-label="{{ctx.Locale.Tr "repo.issues.action_check"}} &quot;{{.Title}}&quot;">
- {{end}}
- {{template "shared/issueicon" .}}
+ <div class="flex-item-leading">
+ {{/* using some tw helpers is the only way to align the checkbox */}}
+ <div class="flex-text-inline tw-mt-[2px]">
+ {{if $.CanWriteIssuesOrPulls}}
+ <input type="checkbox" autocomplete="off" class="issue-checkbox tw-mr-[14px]" data-issue-id={{.ID}} aria-label="{{ctx.Locale.Tr "repo.issues.action_check"}} &quot;{{.Title}}&quot;">
+ {{end}}
+ {{template "shared/issueicon" .}}
+ </div>
</div>
<div class="flex-item-main">
@@ -19,7 +22,7 @@
{{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID)}}
{{end}}
{{end}}
- <span class="labels-list tw-ml-1">
+ <span class="labels-list">
{{range .Labels}}
<a href="?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.RenderUtils.RenderLabel .}}</a>
{{end}}
@@ -28,7 +31,7 @@
{{if .TotalTrackedTime}}
<div class="text grey flex-text-block">
{{svg "octicon-clock" 16}}
- {{.TotalTrackedTime | Sec2Time}}
+ {{.TotalTrackedTime | Sec2Hour}}
</div>
{{end}}
</div>
@@ -153,6 +156,11 @@
</div>
{{end}}
</div>
+ {{else}}
+ <div class="tw-text-center tw-p-8">
+ <h3 class="tw-my-4">{{ctx.Locale.Tr "repo.issues.filter_no_results"}}</h3>
+ <p class="tw-text-placeholder-text">{{ctx.Locale.Tr "repo.issues.filter_no_results_placeholder"}}</p>
+ </div>
{{end}}
{{if .IssueIndexerUnavailable}}
<div class="ui error message">
diff --git a/templates/explore/repo_list.tmpl b/templates/shared/repo/list.tmpl
index 219b1255c0..2c8af14f9c 100644
--- a/templates/explore/repo_list.tmpl
+++ b/templates/shared/repo/list.tmpl
@@ -2,12 +2,16 @@
{{range .Repos}}
<div class="flex-item">
<div class="flex-item-leading">
- {{template "repo/icon" .}}
+ {{if $.ShowRepoOwnerAvatar}}
+ {{ctx.AvatarUtils.Avatar .Owner 24}}
+ {{else}}
+ {{template "repo/icon" .}}
+ {{end}}
</div>
<div class="flex-item-main">
<div class="flex-item-header">
<div class="flex-item-title">
- {{if and (or $.PageIsExplore $.PageIsProfileStarList) .Owner}}
+ {{if and $.ShowRepoOwnerOnList .Owner}}
<a class="text primary name" href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>/
{{end}}
<a class="text primary name" href="{{.Link}}">{{.Name}}</a>
@@ -39,12 +43,12 @@
{{end}}
{{if not $.DisableStars}}
<a class="flex-text-inline" href="{{.Link}}/stars">
- <span aria-label="{{ctx.Locale.Tr "repo.stars"}}">{{svg "octicon-star" 16}}</span>
+ <span class="tw-contents" aria-label="{{ctx.Locale.Tr "repo.stars"}}">{{svg "octicon-star" 16}}</span>
<span {{if ge .NumStars 1000}}data-tooltip-content="{{.NumStars}}"{{end}}>{{CountFmt .NumStars}}</span>
</a>
{{end}}
<a class="flex-text-inline" href="{{.Link}}/forks">
- <span aria-label="{{ctx.Locale.Tr "repo.forks"}}">{{svg "octicon-git-branch" 16}}</span>
+ <span class="tw-contents" aria-label="{{ctx.Locale.Tr "repo.forks"}}">{{svg "octicon-git-branch" 16}}</span>
<span {{if ge .NumForks 1000}}data-tooltip-content="{{.NumForks}}"{{end}}>{{CountFmt .NumForks}}</span>
</a>
</div>
diff --git a/templates/shared/repo_search.tmpl b/templates/shared/repo/search.tmpl
index 7fcb5d2361..a909061184 100644
--- a/templates/shared/repo_search.tmpl
+++ b/templates/shared/repo/search.tmpl
@@ -1,5 +1,5 @@
<div class="ui small secondary filter menu">
- <form id="repo-search-form" class="ui form ignore-dirty tw-flex-1 tw-flex tw-gap-x-2">
+ <form id="repo-search-form" class="ui form ignore-dirty tw-flex-1 tw-flex tw-items-center tw-gap-x-2">
{{if .Language}}<input type="hidden" name="language" value="{{.Language}}">{{end}}
{{if .PageIsExploreRepositories}}<input type="hidden" name="only_show_relevant" value="{{.OnlyShowRelevant}}">{{end}}
{{if .TabName}}<input type="hidden" name="tab" value="{{.TabName}}">{{end}}
diff --git a/templates/shared/search/code/results.tmpl b/templates/shared/search/code/results.tmpl
index a98a662654..8a08f5c25c 100644
--- a/templates/shared/search/code/results.tmpl
+++ b/templates/shared/search/code/results.tmpl
@@ -1,7 +1,7 @@
<div class="flex-text-block tw-flex-wrap">
{{range $term := .SearchResultLanguages}}
<a class="ui {{if eq $.Language $term.Language}}primary{{end}} basic label tw-m-0"
- href="?q={{$.Keyword}}{{if ne $.Language $term.Language}}&l={{$term.Language}}{{end}}&fuzzy={{$.IsFuzzy}}">
+ href="?q={{$.Keyword}}{{if ne $.Language $term.Language}}&l={{$term.Language}}{{end}}&search_mode={{$.SelectedSearchMode}}">
<i class="color-icon tw-mr-2" style="background-color: {{$term.Color}}"></i>
{{$term.Language}}
<div class="detail">{{$term.Count}}</div>
@@ -11,7 +11,7 @@
<div class="repository search">
{{range $result := .SearchResults}}
{{$repo := or $.Repo (index $.RepoMaps .RepoID)}}
- <div class="diff-file-box diff-box file-content non-diff-file-content repo-search-result">
+ <div class="diff-file-box file-content non-diff-file-content repo-search-result">
<h4 class="ui top attached header tw-font-normal tw-flex tw-flex-wrap">
{{if not $.Repo}}
<span class="file tw-flex-1">
diff --git a/templates/shared/search/code/search.tmpl b/templates/shared/search/code/search.tmpl
index dde45c0fbf..2041213e19 100644
--- a/templates/shared/search/code/search.tmpl
+++ b/templates/shared/search/code/search.tmpl
@@ -1,5 +1,11 @@
<form class="ui form ignore-dirty">
- {{template "shared/search/combo_fuzzy" dict "Value" .Keyword "Disabled" .CodeIndexerUnavailable "IsFuzzy" .IsFuzzy "Placeholder" (ctx.Locale.Tr "search.code_kind")}}
+ {{template "shared/search/combo" (dict
+ "Disabled" .CodeIndexerUnavailable
+ "Value" .Keyword
+ "Placeholder" (ctx.Locale.Tr "search.code_kind")
+ "SearchModes" .SearchModes
+ "SelectedSearchMode" .SelectedSearchMode
+ )}}
</form>
<div class="divider"></div>
<div class="ui list">
diff --git a/templates/shared/search/combo.tmpl b/templates/shared/search/combo.tmpl
index 788db95cc1..3aba9a7f04 100644
--- a/templates/shared/search/combo.tmpl
+++ b/templates/shared/search/combo.tmpl
@@ -1,8 +1,30 @@
-{{/* Value - value of the search field (for search results page) */}}
-{{/* Disabled (optional) - if search field/button has to be disabled */}}
-{{/* Placeholder (optional) - placeholder text to be used */}}
-{{/* Tooltip (optional) - a tooltip to be displayed on button hover */}}
+{{/* Attributes:
+* Value - value of the search field (for search results page)
+* Disabled (optional) - if search field/button has to be disabled
+* Placeholder (optional) - placeholder text to be used
+* Tooltip (optional) - a tooltip to be displayed on button hover
+* SearchModes - a list of search modes to be displayed in the dropdown
+* SelectedSearchMode - the currently selected search mode
+*/}}
<div class="ui small fluid action input">
{{template "shared/search/input" dict "Value" .Value "Disabled" .Disabled "Placeholder" .Placeholder}}
+ {{if .SearchModes}}
+ <div class="ui small dropdown selection {{if .Disabled}}disabled{{end}}" data-tooltip-content="{{ctx.Locale.Tr "search.type_tooltip"}}">
+ <div class="text"></div> {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ <input name="search_mode" type="hidden" value="
+ {{- if .SelectedSearchMode -}}
+ {{- .SelectedSearchMode -}}
+ {{- else -}}
+ {{- $defaultSearchMode := index .SearchModes 0 -}}
+ {{- $defaultSearchMode.ModeValue -}}
+ {{- end -}}
+ ">
+ <div class="menu">
+ {{range $mode := .SearchModes}}
+ <div class="item" data-value="{{$mode.ModeValue}}" data-tooltip-content="{{ctx.Locale.Tr $mode.TooltipTrKey}}">{{ctx.Locale.Tr $mode.TitleTrKey}}</div>
+ {{end}}
+ </div>
+ </div>
+ {{end}}
{{template "shared/search/button" dict "Disabled" .Disabled "Tooltip" .Tooltip}}
</div>
diff --git a/templates/shared/search/combo_fuzzy.tmpl b/templates/shared/search/combo_fuzzy.tmpl
deleted file mode 100644
index 3540a89ecb..0000000000
--- a/templates/shared/search/combo_fuzzy.tmpl
+++ /dev/null
@@ -1,10 +0,0 @@
-{{/* Value - value of the search field (for search results page) */}}
-{{/* Disabled (optional) - if search field/button has to be disabled */}}
-{{/* Placeholder (optional) - placeholder text to be used */}}
-{{/* IsFuzzy - state of the fuzzy search toggle */}}
-{{/* Tooltip (optional) - a tooltip to be displayed on button hover */}}
-<div class="ui small fluid action input">
- {{template "shared/search/input" dict "Value" .Value "Disabled" .Disabled "Placeholder" .Placeholder}}
- {{template "shared/search/fuzzy" dict "Disabled" .Disabled "IsFuzzy" .IsFuzzy}}
- {{template "shared/search/button" dict "Disabled" .Disabled "Tooltip" .Tooltip}}
-</div>
diff --git a/templates/shared/search/fuzzy.tmpl b/templates/shared/search/fuzzy.tmpl
deleted file mode 100644
index 5c09d3c150..0000000000
--- a/templates/shared/search/fuzzy.tmpl
+++ /dev/null
@@ -1,10 +0,0 @@
-{{/* Disabled (optional) - if dropdown has to be disabled */}}
-{{/* IsFuzzy - state of the fuzzy search toggle */}}
-<div class="ui small dropdown selection {{if .Disabled}} disabled{{end}}" data-tooltip-content="{{ctx.Locale.Tr "search.type_tooltip"}}">
- <input name="fuzzy" type="hidden"{{if .Disabled}} disabled{{end}} value="{{.IsFuzzy}}">{{svg "octicon-triangle-down" 14 "dropdown icon"}}
- <div class="text">{{if .IsFuzzy}}{{ctx.Locale.Tr "search.fuzzy"}}{{else}}{{ctx.Locale.Tr "search.exact"}}{{end}}</div>
- <div class="menu">
- <div class="item" data-value="true" data-tooltip-content="{{ctx.Locale.Tr "search.fuzzy_tooltip"}}">{{ctx.Locale.Tr "search.fuzzy"}}</div>
- <div class="item" data-value="false" data-tooltip-content="{{ctx.Locale.Tr "search.exact_tooltip"}}">{{ctx.Locale.Tr "search.exact"}}</div>
- </div>
-</div>
diff --git a/templates/shared/secrets/add_list.tmpl b/templates/shared/secrets/add_list.tmpl
index 59596d1013..44305e9502 100644
--- a/templates/shared/secrets/add_list.tmpl
+++ b/templates/shared/secrets/add_list.tmpl
@@ -4,9 +4,13 @@
<button class="ui primary tiny button show-modal"
data-modal="#add-secret-modal"
data-modal-form.action="{{.Link}}"
- data-modal-header="{{ctx.Locale.Tr "secrets.creation"}}"
+ data-modal-header="{{ctx.Locale.Tr "secrets.add_secret"}}"
+ data-modal-secret-name.value=""
+ data-modal-secret-name.read-only="false"
+ data-modal-secret-data=""
+ data-modal-secret-description=""
>
- {{ctx.Locale.Tr "secrets.creation"}}
+ {{ctx.Locale.Tr "secrets.add_secret"}}
</button>
</div>
</h4>
@@ -23,6 +27,9 @@
{{.Name}}
</div>
<div class="flex-item-body">
+ {{if .Description}}{{.Description}}{{else}}-{{end}}
+ </div>
+ <div class="flex-item-body">
******
</div>
</div>
@@ -30,7 +37,19 @@
<span class="color-text-light-2">
{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}}
</span>
- <button class="ui btn interact-bg link-action tw-p-2"
+ <button class="btn interact-bg show-modal tw-p-2"
+ data-modal="#add-secret-modal"
+ data-modal-form.action="{{$.Link}}"
+ data-modal-header="{{ctx.Locale.Tr "secrets.edit_secret"}}"
+ data-tooltip-content="{{ctx.Locale.Tr "secrets.edit_secret"}}"
+ data-modal-secret-name.value="{{.Name}}"
+ data-modal-secret-name.read-only="true"
+ data-modal-secret-data=""
+ data-modal-secret-description="{{if .Description}}{{.Description}}{{end}}"
+ >
+ {{svg "octicon-pencil"}}
+ </button>
+ <button class="btn interact-bg link-action tw-p-2"
data-url="{{$.Link}}/delete?id={{.ID}}"
data-modal-confirm="{{ctx.Locale.Tr "secrets.deletion.description"}}"
data-tooltip-content="{{ctx.Locale.Tr "secrets.deletion"}}"
@@ -48,9 +67,7 @@
{{/* Add secret dialog */}}
<div class="ui small modal" id="add-secret-modal">
- <div class="header">
- <span id="actions-modal-header"></span>
- </div>
+ <div class="header"></div>
<form class="ui form form-fetch-action" method="post">
<div class="content">
{{.CsrfTokenHtml}}
@@ -72,9 +89,20 @@
<textarea required
id="secret-data"
name="data"
+ maxlength="{{.DataMaxLength}}"
placeholder="{{ctx.Locale.Tr "secrets.creation.value_placeholder"}}"
></textarea>
</div>
+ <div class="field">
+ <label for="secret-description">{{ctx.Locale.Tr "secrets.creation.description"}}</label>
+ <textarea
+ id="secret-description"
+ name="description"
+ rows="2"
+ maxlength="{{.DescriptionMaxLength}}"
+ placeholder="{{ctx.Locale.Tr "secrets.creation.description_placeholder"}}"
+ ></textarea>
+ </div>
</div>
{{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
</form>
diff --git a/templates/shared/user/authorlink.tmpl b/templates/shared/user/authorlink.tmpl
index d57a635b4b..abfee6aae3 100644
--- a/templates/shared/user/authorlink.tmpl
+++ b/templates/shared/user/authorlink.tmpl
@@ -1 +1 @@
-<a class="author text black tw-font-semibold muted"{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{.GetDisplayName}}</a>{{if .IsBot}}<span class="ui basic label tw-p-1">bot</span>{{end}}
+<a class="author text black tw-font-semibold muted"{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{.GetDisplayName}}</a>{{if .IsTypeBot}}<span class="ui basic label tw-p-1 tw-align-baseline">bot</span>{{end}}
diff --git a/templates/shared/user/block_user_dialog.tmpl b/templates/shared/user/block_user_dialog.tmpl
index c6db4ca1e4..e1fa939945 100644
--- a/templates/shared/user/block_user_dialog.tmpl
+++ b/templates/shared/user/block_user_dialog.tmpl
@@ -14,7 +14,7 @@
<input id="block-note" name="note">
<p class="help">{{ctx.Locale.Tr "user.block.note.info"}}</p>
</div>
- <div class="text right actions">
+ <div class="actions">
<button class="ui cancel button">{{ctx.Locale.Tr "cancel"}}</button>
<button class="ui red button">{{ctx.Locale.Tr "user.block.block"}}</button>
</div>
diff --git a/templates/shared/user/blocked_users.tmpl b/templates/shared/user/blocked_users.tmpl
index e83a039ef5..a4c74c46ac 100644
--- a/templates/shared/user/blocked_users.tmpl
+++ b/templates/shared/user/blocked_users.tmpl
@@ -74,7 +74,7 @@
<input name="note" class="modal-note" />
<p class="help">{{ctx.Locale.Tr "user.block.note.info"}}</p>
</div>
- <div class="text right actions">
+ <div class="actions">
<button class="ui cancel button">{{ctx.Locale.Tr "cancel"}}</button>
<button class="ui primary button">{{ctx.Locale.Tr "save"}}</button>
</div>
diff --git a/templates/shared/user/profile_big_avatar.tmpl b/templates/shared/user/profile_big_avatar.tmpl
index 91f04e0b53..1190dc54ec 100644
--- a/templates/shared/user/profile_big_avatar.tmpl
+++ b/templates/shared/user/profile_big_avatar.tmpl
@@ -103,7 +103,7 @@
<ul class="user-badges">
{{range .Badges}}
<li>
- <img width="64" height="64" src="{{.ImageURL}}" alt="{{.Description}}" data-tooltip-content="{{.Description}}">
+ <img loading="lazy" width="64" height="64" src="{{.ImageURL}}" alt="{{.Description}}" data-tooltip-content="{{.Description}}">
</li>
{{end}}
</ul>
diff --git a/templates/shared/variables/variable_list.tmpl b/templates/shared/variables/variable_list.tmpl
index 7a0ab48cef..2edca431c1 100644
--- a/templates/shared/variables/variable_list.tmpl
+++ b/templates/shared/variables/variable_list.tmpl
@@ -7,6 +7,7 @@
data-modal-header="{{ctx.Locale.Tr "actions.variables.creation"}}"
data-modal-dialog-variable-name=""
data-modal-dialog-variable-data=""
+ data-modal-dialog-variable-description=""
>
{{ctx.Locale.Tr "actions.variables.creation"}}
</button>
@@ -25,6 +26,9 @@
{{.Name}}
</div>
<div class="flex-item-body">
+ {{if .Description}}{{.Description}}{{else}}-{{end}}
+ </div>
+ <div class="flex-item-body">
{{.Data}}
</div>
</div>
@@ -39,6 +43,7 @@
data-modal-header="{{ctx.Locale.Tr "actions.variables.edit"}}"
data-modal-dialog-variable-name="{{.Name}}"
data-modal-dialog-variable-data="{{.Data}}"
+ data-modal-dialog-variable-description="{{.Description}}"
>
{{svg "octicon-pencil"}}
</button>
@@ -82,9 +87,20 @@
<textarea required
name="data"
id="dialog-variable-data"
+ maxlength="{{.DataMaxLength}}"
placeholder="{{ctx.Locale.Tr "secrets.creation.value_placeholder"}}"
></textarea>
</div>
+ <div class="field">
+ <label for="dialog-variable-description">{{ctx.Locale.Tr "secrets.creation.description"}}</label>
+ <textarea
+ name="description"
+ id="dialog-variable-description"
+ rows="2"
+ maxlength="{{.DescriptionMaxLength}}"
+ placeholder="{{ctx.Locale.Tr "secrets.creation.description_placeholder"}}"
+ ></textarea>
+ </div>
</div>
{{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}}
</form>
diff --git a/templates/shared/webhook/icon.tmpl b/templates/shared/webhook/icon.tmpl
index 0f80787c57..105212eb56 100644
--- a/templates/shared/webhook/icon.tmpl
+++ b/templates/shared/webhook/icon.tmpl
@@ -5,23 +5,23 @@
{{if eq .HookType "gitea"}}
{{svg "gitea-gitea" $size "img"}}
{{else if eq .HookType "gogs"}}
- <img width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/gogs.ico">
+ <img alt width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/gogs.ico">
{{else if eq .HookType "slack"}}
- <img width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/slack.png">
+ <img alt width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/slack.png">
{{else if eq .HookType "discord"}}
- <img width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/discord.png">
+ <img alt width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/discord.png">
{{else if eq .HookType "dingtalk"}}
- <img width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/dingtalk.ico">
+ <img alt width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/dingtalk.ico">
{{else if eq .HookType "telegram"}}
- <img width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/telegram.png">
+ <img alt width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/telegram.png">
{{else if eq .HookType "msteams"}}
- <img width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/msteams.png">
+ <img alt width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/msteams.png">
{{else if eq .HookType "feishu"}}
- <img width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/feishu.png">
+ {{svg "gitea-feishu" $size "img"}}
{{else if eq .HookType "matrix"}}
{{svg "gitea-matrix" $size "img"}}
{{else if eq .HookType "wechatwork"}}
- <img width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/wechatwork.png">
+ <img alt width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/wechatwork.png">
{{else if eq .HookType "packagist"}}
- <img width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/packagist.png">
+ <img alt width="{{$size}}" height="{{$size}}" src="{{AssetUrlPrefix}}/img/packagist.png">
{{end}}
diff --git a/templates/status/404.tmpl b/templates/status/404.tmpl
index 6cfc88a0d7..e83dcf463b 100644
--- a/templates/status/404.tmpl
+++ b/templates/status/404.tmpl
@@ -2,6 +2,7 @@
<div role="main" aria-label="{{.Title}}" class="page-content {{if .IsRepo}}repository{{end}}">
{{if .IsRepo}}{{template "repo/header" .}}{{end}}
<div class="ui container">
+ {{template "base/alert" .}}
<div class="status-page-error">
<div class="status-page-error-title">404 Not Found</div>
<div class="tw-text-center">
diff --git a/templates/status/500.tmpl b/templates/status/500.tmpl
index 198f1ea898..6dfa2d8a8c 100644
--- a/templates/status/500.tmpl
+++ b/templates/status/500.tmpl
@@ -38,7 +38,7 @@
{{if .ErrorMsg}}
<div class="tw-mt-8">
<p>{{ctx.Locale.Tr "error.occurred"}}:</p>
- <pre class="tw-whitespace-pre-wrap tw-break-all">{{.ErrorMsg}}</pre>
+ <pre class="tw-whitespace-pre-wrap tw-wrap-anywhere">{{.ErrorMsg}}</pre>
</div>
{{end}}
<div class="tw-mt-8 tw-text-center">
diff --git a/templates/status/503.tmpl b/templates/status/503.tmpl
new file mode 100644
index 0000000000..5b1db9fc07
--- /dev/null
+++ b/templates/status/503.tmpl
@@ -0,0 +1,12 @@
+{{template "base/head" .}}
+<div role="main" aria-label="503 Service Unavailable" class="page-content">
+ <div class="ui container">
+ <div class="status-page-error">
+ <div class="status-page-error-title">503 Service Unavailable</div>
+ <div class="tw-text-center">
+ <div class="tw-my-4">{{ctx.Locale.Tr "error503"}}</div>
+ </div>
+ </div>
+ </div>
+</div>
+{{template "base/footer" .}}
diff --git a/templates/swagger/ui.tmpl b/templates/swagger/ui.tmpl
index 9935ab9c5a..c48ba82fe0 100644
--- a/templates/swagger/ui.tmpl
+++ b/templates/swagger/ui.tmpl
@@ -7,6 +7,7 @@
<body>
<a class="swagger-back-link" href="{{AppSubUrl}}/">{{svg "octicon-reply"}}{{ctx.Locale.Tr "return_to_gitea"}}</a>
<div id="swagger-ui" data-source="{{AppSubUrl}}/swagger.{{.APIJSONVersion}}.json"></div>
+ <footer class="page-footer"></footer>
<script src="{{AssetUrlPrefix}}/js/swagger.js?v={{AssetVersion}}"></script>
</body>
</html>
diff --git a/templates/swagger/v1_input.json b/templates/swagger/v1_input.json
new file mode 100644
index 0000000000..e74c8fc9c4
--- /dev/null
+++ b/templates/swagger/v1_input.json
@@ -0,0 +1,6 @@
+{
+ "info": {
+ "version": "{{.SwaggerAppVer}}"
+ },
+ "basePath": "{{.SwaggerAppSubUrl}}/api/v1"
+}
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 82a301da2f..1cc3735b2b 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -19,9 +19,9 @@
"name": "MIT",
"url": "http://opensource.org/licenses/MIT"
},
- "version": "{{AppVer | JSEscape}}"
+ "version": "{{.SwaggerAppVer}}"
},
- "basePath": "{{AppSubUrl | JSEscape}}/api/v1",
+ "basePath": "{{.SwaggerAppSubUrl}}/api/v1",
"paths": {
"/activitypub/user-id/{user-id}": {
"get": {
@@ -75,6 +75,218 @@
}
}
},
+ "/admin/actions/jobs": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "summary": "Lists all jobs",
+ "operationId": "listAdminWorkflowJobs",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
+ "name": "status",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page number of results to return (1-based)",
+ "name": "page",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page size of results",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/WorkflowJobsList"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/admin/actions/runners": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "summary": "Get all runners",
+ "operationId": "getAdminRunners",
+ "responses": {
+ "200": {
+ "$ref": "#/definitions/ActionRunnersResponse"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/admin/actions/runners/registration-token": {
+ "post": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "summary": "Get an global actions runner registration token",
+ "operationId": "adminCreateRunnerRegistrationToken",
+ "responses": {
+ "200": {
+ "$ref": "#/responses/RegistrationToken"
+ }
+ }
+ }
+ },
+ "/admin/actions/runners/{runner_id}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "summary": "Get an global runner",
+ "operationId": "getAdminRunner",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "id of the runner",
+ "name": "runner_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/definitions/ActionRunner"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ },
+ "delete": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "summary": "Delete an global runner",
+ "operationId": "deleteAdminRunner",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "id of the runner",
+ "name": "runner_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "runner has been deleted"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/admin/actions/runs": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "admin"
+ ],
+ "summary": "Lists all runs",
+ "operationId": "listAdminWorkflowRuns",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "workflow event name",
+ "name": "event",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "workflow branch",
+ "name": "branch",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
+ "name": "status",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "triggered by user",
+ "name": "actor",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "triggering sha of the workflow run",
+ "name": "head_sha",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page number of results to return (1-based)",
+ "name": "page",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page size of results",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/WorkflowRunsList"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
"/admin/cron": {
"get": {
"produces": [
@@ -234,6 +446,18 @@
"description": "page size of results",
"name": "limit",
"in": "query"
+ },
+ {
+ "enum": [
+ "system",
+ "default",
+ "all"
+ ],
+ "type": "string",
+ "default": "system",
+ "description": "system, default or both kinds of webhooks",
+ "name": "type",
+ "in": "query"
}
],
"responses": {
@@ -542,7 +766,7 @@
},
{
"type": "string",
- "description": "user's login name to search for",
+ "description": "identifier of the user, provided by the external authenticator",
"name": "login_name",
"in": "query"
},
@@ -618,7 +842,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user to delete",
+ "description": "username of the user to delete",
"name": "username",
"in": "path",
"required": true
@@ -660,7 +884,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user to edit",
+ "description": "username of the user whose data is to be edited",
"name": "username",
"in": "path",
"required": true
@@ -702,7 +926,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose badges are to be listed",
"name": "username",
"in": "path",
"required": true
@@ -732,7 +956,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user to whom a badge is to be added",
"name": "username",
"in": "path",
"required": true
@@ -766,7 +990,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose badge is to be deleted",
"name": "username",
"in": "path",
"required": true
@@ -808,7 +1032,7 @@
"parameters": [
{
"type": "string",
- "description": "username of the user",
+ "description": "username of the user who is to receive a public key",
"name": "username",
"in": "path",
"required": true
@@ -847,7 +1071,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose public key is to be deleted",
"name": "username",
"in": "path",
"required": true
@@ -890,7 +1114,7 @@
"parameters": [
{
"type": "string",
- "description": "username of the user that will own the created organization",
+ "description": "username of the user who will own the created organization",
"name": "username",
"in": "path",
"required": true
@@ -930,7 +1154,7 @@
"parameters": [
{
"type": "string",
- "description": "existing username of user",
+ "description": "current username of the user",
"name": "username",
"in": "path",
"required": true
@@ -973,7 +1197,7 @@
"parameters": [
{
"type": "string",
- "description": "username of the user. This user will own the created repository",
+ "description": "username of the user who will own the created repository",
"name": "username",
"in": "path",
"required": true
@@ -1685,6 +1909,88 @@
}
}
},
+ "/orgs/{org}/actions/jobs": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "organization"
+ ],
+ "summary": "Get org-level workflow jobs",
+ "operationId": "getOrgWorkflowJobs",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the organization",
+ "name": "org",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
+ "name": "status",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page number of results to return (1-based)",
+ "name": "page",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page size of results",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/WorkflowJobsList"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/orgs/{org}/actions/runners": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "organization"
+ ],
+ "summary": "Get org-level runners",
+ "operationId": "getOrgRunners",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the organization",
+ "name": "org",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/definitions/ActionRunnersResponse"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
"/orgs/{org}/actions/runners/registration-token": {
"get": {
"produces": [
@@ -1709,6 +2015,180 @@
"$ref": "#/responses/RegistrationToken"
}
}
+ },
+ "post": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "organization"
+ ],
+ "summary": "Get an organization's actions runner registration token",
+ "operationId": "orgCreateRunnerRegistrationToken",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the organization",
+ "name": "org",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/RegistrationToken"
+ }
+ }
+ }
+ },
+ "/orgs/{org}/actions/runners/{runner_id}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "organization"
+ ],
+ "summary": "Get an org-level runner",
+ "operationId": "getOrgRunner",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the organization",
+ "name": "org",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "id of the runner",
+ "name": "runner_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/definitions/ActionRunner"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ },
+ "delete": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "organization"
+ ],
+ "summary": "Delete an org-level runner",
+ "operationId": "deleteOrgRunner",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the organization",
+ "name": "org",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "id of the runner",
+ "name": "runner_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "runner has been deleted"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/orgs/{org}/actions/runs": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "organization"
+ ],
+ "summary": "Get org-level workflow runs",
+ "operationId": "getOrgWorkflowRuns",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the organization",
+ "name": "org",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "workflow event name",
+ "name": "event",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "workflow branch",
+ "name": "branch",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
+ "name": "status",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "triggered by user",
+ "name": "actor",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "triggering sha of the workflow run",
+ "name": "head_sha",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page number of results to return (1-based)",
+ "name": "page",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page size of results",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/WorkflowRunsList"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
}
},
"/orgs/{org}/actions/secrets": {
@@ -2013,16 +2493,16 @@
],
"responses": {
"201": {
- "description": "response when creating an org-level variable"
- },
- "204": {
- "description": "response when creating an org-level variable"
+ "description": "successfully created the org-level variable"
},
"400": {
"$ref": "#/responses/error"
},
- "404": {
- "$ref": "#/responses/notFound"
+ "409": {
+ "description": "variable name already exists."
+ },
+ "500": {
+ "$ref": "#/responses/error"
}
}
},
@@ -2236,7 +2716,7 @@
},
{
"type": "string",
- "description": "user to check",
+ "description": "username of the user to check",
"name": "username",
"in": "path",
"required": true
@@ -2267,7 +2747,7 @@
},
{
"type": "string",
- "description": "user to block",
+ "description": "username of the user to block",
"name": "username",
"in": "path",
"required": true
@@ -2307,7 +2787,7 @@
},
{
"type": "string",
- "description": "user to unblock",
+ "description": "username of the user to unblock",
"name": "username",
"in": "path",
"required": true
@@ -2778,7 +3258,7 @@
},
{
"type": "string",
- "description": "username of the user",
+ "description": "username of the user to check for an organization membership",
"name": "username",
"in": "path",
"required": true
@@ -2815,7 +3295,7 @@
},
{
"type": "string",
- "description": "username of the user",
+ "description": "username of the user to remove from the organization",
"name": "username",
"in": "path",
"required": true
@@ -2889,7 +3369,7 @@
},
{
"type": "string",
- "description": "username of the user",
+ "description": "username of the user to check for a public organization membership",
"name": "username",
"in": "path",
"required": true
@@ -2923,7 +3403,7 @@
},
{
"type": "string",
- "description": "username of the user",
+ "description": "username of the user whose membership is to be publicized",
"name": "username",
"in": "path",
"required": true
@@ -2960,7 +3440,7 @@
},
{
"type": "string",
- "description": "username of the user",
+ "description": "username of the user whose membership is to be concealed",
"name": "username",
"in": "path",
"required": true
@@ -2979,6 +3459,46 @@
}
}
},
+ "/orgs/{org}/rename": {
+ "post": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "organization"
+ ],
+ "summary": "Rename an organization",
+ "operationId": "renameOrg",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "existing org name",
+ "name": "org",
+ "in": "path",
+ "required": true
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/RenameOrgOption"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "$ref": "#/responses/empty"
+ },
+ "403": {
+ "$ref": "#/responses/forbidden"
+ },
+ "422": {
+ "$ref": "#/responses/validationError"
+ }
+ }
+ }
+ },
"/orgs/{org}/repos": {
"get": {
"produces": [
@@ -3287,6 +3807,191 @@
}
}
},
+ "/packages/{owner}/{type}/{name}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "package"
+ ],
+ "summary": "Gets all versions of a package",
+ "operationId": "listPackageVersions",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the package",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "type of the package",
+ "name": "type",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the package",
+ "name": "name",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "description": "page number of results to return (1-based)",
+ "name": "page",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page size of results",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/PackageList"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/packages/{owner}/{type}/{name}/-/latest": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "package"
+ ],
+ "summary": "Gets the latest version of a package",
+ "operationId": "getLatestPackageVersion",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the package",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "type of the package",
+ "name": "type",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the package",
+ "name": "name",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/Package"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/packages/{owner}/{type}/{name}/-/link/{repo_name}": {
+ "post": {
+ "tags": [
+ "package"
+ ],
+ "summary": "Link a package to a repository",
+ "operationId": "linkPackage",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the package",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "type of the package",
+ "name": "type",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the package",
+ "name": "name",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repository to link.",
+ "name": "repo_name",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "201": {
+ "$ref": "#/responses/empty"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/packages/{owner}/{type}/{name}/-/unlink": {
+ "post": {
+ "tags": [
+ "package"
+ ],
+ "summary": "Unlink a package from a repository",
+ "operationId": "unlinkPackage",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the package",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "type of the package",
+ "name": "type",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the package",
+ "name": "name",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "201": {
+ "$ref": "#/responses/empty"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
"/packages/{owner}/{type}/{name}/{version}": {
"get": {
"produces": [
@@ -3867,6 +4572,375 @@
}
}
},
+ "/repos/{owner}/{repo}/actions/artifacts": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Lists all artifacts for a repository",
+ "operationId": "getArtifacts",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the owner",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repository",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the artifact",
+ "name": "name",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/ArtifactsList"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/artifacts/{artifact_id}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Gets a specific artifact for a workflow run",
+ "operationId": "getArtifact",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the owner",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repository",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "id of the artifact",
+ "name": "artifact_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/Artifact"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ },
+ "delete": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Deletes a specific artifact for a workflow run",
+ "operationId": "deleteArtifact",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the owner",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repository",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "id of the artifact",
+ "name": "artifact_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/artifacts/{artifact_id}/zip": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Downloads a specific artifact for a workflow run redirects to blob url",
+ "operationId": "downloadArtifact",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the owner",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repository",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "id of the artifact",
+ "name": "artifact_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "302": {
+ "description": "redirect to the blob download"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/jobs": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Lists all jobs for a repository",
+ "operationId": "listWorkflowJobs",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the owner",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repository",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
+ "name": "status",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page number of results to return (1-based)",
+ "name": "page",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page size of results",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/WorkflowJobsList"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/jobs/{job_id}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Gets a specific workflow job for a workflow run",
+ "operationId": "getWorkflowJob",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the owner",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repository",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "id of the job",
+ "name": "job_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/WorkflowJob"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/jobs/{job_id}/logs": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Downloads the job logs for a workflow run",
+ "operationId": "downloadActionsRunJobLogs",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the owner",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repository",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "description": "id of the job",
+ "name": "job_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "output blob content"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/runners": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Get repo-level runners",
+ "operationId": "getRepoRunners",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/definitions/ActionRunnersResponse"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
"/repos/{owner}/{repo}/actions/runners/registration-token": {
"get": {
"produces": [
@@ -3898,6 +4972,414 @@
"$ref": "#/responses/RegistrationToken"
}
}
+ },
+ "post": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Get a repository's actions runner registration token",
+ "operationId": "repoCreateRunnerRegistrationToken",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/RegistrationToken"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/runners/{runner_id}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Get an repo-level runner",
+ "operationId": "getRepoRunner",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "id of the runner",
+ "name": "runner_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/definitions/ActionRunner"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ },
+ "delete": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Delete an repo-level runner",
+ "operationId": "deleteRepoRunner",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "id of the runner",
+ "name": "runner_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "runner has been deleted"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/runs": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Lists all runs for a repository run",
+ "operationId": "getWorkflowRuns",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the owner",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repository",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "workflow event name",
+ "name": "event",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "workflow branch",
+ "name": "branch",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
+ "name": "status",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "triggered by user",
+ "name": "actor",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "triggering sha of the workflow run",
+ "name": "head_sha",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page number of results to return (1-based)",
+ "name": "page",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page size of results",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/ArtifactsList"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/runs/{run}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Gets a specific workflow run",
+ "operationId": "GetWorkflowRun",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the owner",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repository",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "id of the run",
+ "name": "run",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/WorkflowRun"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ },
+ "delete": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Delete a workflow run",
+ "operationId": "deleteActionRun",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the owner",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repository",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "description": "runid of the workflow run",
+ "name": "run",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/runs/{run}/artifacts": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Lists all artifacts for a repository run",
+ "operationId": "getArtifactsOfRun",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the owner",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repository",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "description": "runid of the workflow run",
+ "name": "run",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the artifact",
+ "name": "name",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/ArtifactsList"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/runs/{run}/jobs": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Lists all jobs for a workflow run",
+ "operationId": "listWorkflowRunJobs",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "name of the owner",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repository",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "description": "runid of the workflow run",
+ "name": "run",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
+ "name": "status",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page number of results to return (1-based)",
+ "name": "page",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page size of results",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/WorkflowJobsList"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
}
},
"/repos/{owner}/{repo}/actions/secrets": {
@@ -4043,7 +5525,7 @@
],
"responses": {
"204": {
- "description": "delete one secret of the organization"
+ "description": "delete one secret of the repository"
},
"400": {
"$ref": "#/responses/error"
@@ -4307,14 +5789,14 @@
"201": {
"description": "response when creating a repo-level variable"
},
- "204": {
- "description": "response when creating a repo-level variable"
- },
"400": {
"$ref": "#/responses/error"
},
- "404": {
- "$ref": "#/responses/notFound"
+ "409": {
+ "description": "variable name already exists."
+ },
+ "500": {
+ "$ref": "#/responses/error"
}
}
},
@@ -4369,6 +5851,275 @@
}
}
},
+ "/repos/{owner}/{repo}/actions/workflows": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "List repository workflows",
+ "operationId": "ActionsListRepositoryWorkflows",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/ActionWorkflowList"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "403": {
+ "$ref": "#/responses/forbidden"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ },
+ "422": {
+ "$ref": "#/responses/validationError"
+ },
+ "500": {
+ "$ref": "#/responses/error"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/workflows/{workflow_id}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Get a workflow",
+ "operationId": "ActionsGetWorkflow",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "id of the workflow",
+ "name": "workflow_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/ActionWorkflow"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "403": {
+ "$ref": "#/responses/forbidden"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ },
+ "422": {
+ "$ref": "#/responses/validationError"
+ },
+ "500": {
+ "$ref": "#/responses/error"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable": {
+ "put": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Disable a workflow",
+ "operationId": "ActionsDisableWorkflow",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "id of the workflow",
+ "name": "workflow_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "403": {
+ "$ref": "#/responses/forbidden"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ },
+ "422": {
+ "$ref": "#/responses/validationError"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches": {
+ "post": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Create a workflow dispatch event",
+ "operationId": "ActionsDispatchWorkflow",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "id of the workflow",
+ "name": "workflow_id",
+ "in": "path",
+ "required": true
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/CreateActionWorkflowDispatch"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "403": {
+ "$ref": "#/responses/forbidden"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ },
+ "422": {
+ "$ref": "#/responses/validationError"
+ }
+ }
+ }
+ },
+ "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable": {
+ "put": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Enable a workflow",
+ "operationId": "ActionsEnableWorkflow",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "id of the workflow",
+ "name": "workflow_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "403": {
+ "$ref": "#/responses/forbidden"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ },
+ "409": {
+ "$ref": "#/responses/conflict"
+ },
+ "422": {
+ "$ref": "#/responses/validationError"
+ }
+ }
+ }
+ },
"/repos/{owner}/{repo}/activities/feeds": {
"get": {
"produces": [
@@ -5179,7 +6930,7 @@
},
{
"type": "string",
- "description": "username of the collaborator",
+ "description": "username of the user to check for being a collaborator",
"name": "collaborator",
"in": "path",
"required": true
@@ -5223,7 +6974,7 @@
},
{
"type": "string",
- "description": "username of the collaborator to add",
+ "description": "username of the user to add or update as a collaborator",
"name": "collaborator",
"in": "path",
"required": true
@@ -5323,7 +7074,7 @@
},
{
"type": "string",
- "description": "username of the collaborator",
+ "description": "username of the collaborator whose permissions are to be obtained",
"name": "collaborator",
"in": "path",
"required": true
@@ -5380,6 +7131,20 @@
"in": "query"
},
{
+ "type": "string",
+ "format": "date-time",
+ "description": "Only commits after this date will be returned (ISO 8601 format)",
+ "name": "since",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "format": "date-time",
+ "description": "Only commits before this date will be returned (ISO 8601 format)",
+ "name": "until",
+ "in": "query"
+ },
+ {
"type": "boolean",
"description": "include diff stats for every commit (disable for speedup, default 'true')",
"name": "stat",
@@ -5659,13 +7424,14 @@
},
"/repos/{owner}/{repo}/contents": {
"get": {
+ "description": "This API follows GitHub's design, and it is not easy to use. Recommend users to use our \"contents-ext\" API instead.",
"produces": [
"application/json"
],
"tags": [
"repository"
],
- "summary": "Gets the metadata of all the entries of the root dir",
+ "summary": "Gets the metadata of all the entries of the root dir.",
"operationId": "repoGetContentsList",
"parameters": [
{
@@ -5684,7 +7450,7 @@
},
{
"type": "string",
- "description": "The name of the commit/branch/tag. Default the repository’s default branch (usually master)",
+ "description": "The name of the commit/branch/tag. Default to the repository’s default branch.",
"name": "ref",
"in": "query"
}
@@ -5753,15 +7519,72 @@
}
}
},
+ "/repos/{owner}/{repo}/contents-ext/{filepath}": {
+ "get": {
+ "description": "It guarantees that only one of the response fields is set if the request succeeds. Users can pass \"includes=file_content\" or \"includes=lfs_metadata\" to retrieve more fields. \"includes=file_content\" only works for single file, if you need to retrieve file contents in batch, use \"file-contents\" API after listing the directory.",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "The extended \"contents\" API, to get file metadata and/or content, or list a directory.",
+ "operationId": "repoGetContentsExt",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "path of the dir, file, symlink or submodule in the repo. Swagger requires path parameter to be \"required\", you can leave it empty or pass a single dot (\".\") to get the root directory.",
+ "name": "filepath",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "the name of the commit/branch/tag, default to the repository’s default branch.",
+ "name": "ref",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "By default this API's response only contains file's metadata. Use comma-separated \"includes\" options to retrieve more fields. Option \"file_content\" will try to retrieve the file content, \"lfs_metadata\" will try to retrieve LFS metadata, \"commit_metadata\" will try to retrieve commit metadata, and \"commit_message\" will try to retrieve commit message.",
+ "name": "includes",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/ContentsExtResponse"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
"/repos/{owner}/{repo}/contents/{filepath}": {
"get": {
+ "description": "This API follows GitHub's design, and it is not easy to use. Recommend users to use the \"contents-ext\" API instead.",
"produces": [
"application/json"
],
"tags": [
"repository"
],
- "summary": "Gets the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir",
+ "summary": "Gets the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir.",
"operationId": "repoGetContents",
"parameters": [
{
@@ -5787,7 +7610,7 @@
},
{
"type": "string",
- "description": "The name of the commit/branch/tag. Default the repository’s default branch (usually master)",
+ "description": "The name of the commit/branch/tag. Default to the repository’s default branch.",
"name": "ref",
"in": "query"
}
@@ -5979,6 +7802,9 @@
"404": {
"$ref": "#/responses/error"
},
+ "422": {
+ "$ref": "#/responses/error"
+ },
"423": {
"$ref": "#/responses/repoArchivedError"
}
@@ -6069,7 +7895,7 @@
},
{
"type": "string",
- "description": "The name of the commit/branch/tag. Default the repository’s default branch (usually master)",
+ "description": "The name of the commit/branch/tag. Default to the repository’s default branch.",
"name": "ref",
"in": "query"
}
@@ -6084,6 +7910,105 @@
}
}
},
+ "/repos/{owner}/{repo}/file-contents": {
+ "get": {
+ "description": "See the POST method. This GET method supports using JSON encoded request body in query parameter.",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Get the metadata and contents of requested files",
+ "operationId": "repoGetFileContents",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "The name of the commit/branch/tag. Default to the repository’s default branch.",
+ "name": "ref",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "The JSON encoded body (see the POST request): {\"files\": [\"filename1\", \"filename2\"]}",
+ "name": "body",
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/ContentsListResponse"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ },
+ "post": {
+ "description": "Uses automatic pagination based on default page size and max response size and returns the maximum allowed number of files. Files which could not be retrieved are null. Files which are too large are being returned with `encoding == null`, `content == null` and `size \u003e 0`, they can be requested separately by using the `download_url`.",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Get the metadata and contents of requested files",
+ "operationId": "repoGetFileContentsPost",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "The name of the commit/branch/tag. Default to the repository’s default branch.",
+ "name": "ref",
+ "in": "query"
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/GetFilesOptions"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/ContentsListResponse"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
"/repos/{owner}/{repo}/forks": {
"get": {
"produces": [
@@ -9305,6 +11230,111 @@
}
}
},
+ "/repos/{owner}/{repo}/issues/{index}/lock": {
+ "put": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "issue"
+ ],
+ "summary": "Lock an issue",
+ "operationId": "issueLockIssue",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "index of the issue",
+ "name": "index",
+ "in": "path",
+ "required": true
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/LockIssueOption"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "$ref": "#/responses/empty"
+ },
+ "403": {
+ "$ref": "#/responses/forbidden"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ },
+ "delete": {
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "issue"
+ ],
+ "summary": "Unlock an issue",
+ "operationId": "issueUnlockIssue",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "index of the issue",
+ "name": "index",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "$ref": "#/responses/empty"
+ },
+ "403": {
+ "$ref": "#/responses/forbidden"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
"/repos/{owner}/{repo}/issues/{index}/pin": {
"post": {
"tags": [
@@ -9666,7 +11696,7 @@
"$ref": "#/responses/notFound"
},
"409": {
- "description": "Cannot cancel a non existent stopwatch"
+ "description": "Cannot cancel a non-existent stopwatch"
}
}
}
@@ -9772,7 +11802,7 @@
"$ref": "#/responses/notFound"
},
"409": {
- "description": "Cannot stop a non existent stopwatch"
+ "description": "Cannot stop a non-existent stopwatch"
}
}
}
@@ -9921,7 +11951,7 @@
},
{
"type": "string",
- "description": "user to subscribe",
+ "description": "username of the user to subscribe the issue to",
"name": "user",
"in": "path",
"required": true
@@ -9979,7 +12009,7 @@
},
{
"type": "string",
- "description": "user witch unsubscribe",
+ "description": "username of the user to unsubscribe from an issue",
"name": "user",
"in": "path",
"required": true
@@ -10849,7 +12879,7 @@
},
{
"type": "string",
- "description": "The name of the commit/branch/tag. Default the repository’s default branch",
+ "description": "The name of the commit/branch/tag. Default to the repository’s default branch",
"name": "ref",
"in": "query"
}
@@ -10867,6 +12897,52 @@
}
}
},
+ "/repos/{owner}/{repo}/merge-upstream": {
+ "post": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Merge a branch from upstream",
+ "operationId": "repoMergeUpstream",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/MergeUpstreamRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/MergeUpstreamResponse"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
"/repos/{owner}/{repo}/milestones": {
"get": {
"produces": [
@@ -11361,6 +13437,12 @@
"required": true
},
{
+ "type": "string",
+ "description": "Filter by target base branch of the pull request",
+ "name": "base_branch",
+ "in": "query"
+ },
+ {
"enum": [
"open",
"closed",
@@ -11376,6 +13458,7 @@
"enum": [
"oldest",
"recentupdate",
+ "recentclose",
"leastupdate",
"mostcomment",
"leastcomment",
@@ -12940,7 +15023,7 @@
},
{
"type": "string",
- "description": "The name of the commit/branch/tag. Default the repository’s default branch",
+ "description": "The name of the commit/branch/tag. Default to the repository’s default branch",
"name": "ref",
"in": "query"
}
@@ -13668,6 +15751,42 @@
}
}
},
+ "/repos/{owner}/{repo}/signing-key.pub": {
+ "get": {
+ "produces": [
+ "text/plain"
+ ],
+ "tags": [
+ "repository"
+ ],
+ "summary": "Get signing-key.pub for given repository",
+ "operationId": "repoSigningKeySSH",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "owner of the repo",
+ "name": "owner",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "name of the repo",
+ "name": "repo",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "ssh public key",
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
"/repos/{owner}/{repo}/stargazers": {
"get": {
"produces": [
@@ -13710,6 +15829,9 @@
"200": {
"$ref": "#/responses/UserList"
},
+ "403": {
+ "$ref": "#/responses/forbidden"
+ },
"404": {
"$ref": "#/responses/notFound"
}
@@ -14699,7 +16821,7 @@
},
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose tracked times are to be listed",
"name": "user",
"in": "path",
"required": true
@@ -15498,6 +17620,26 @@
}
}
},
+ "/signing-key.pub": {
+ "get": {
+ "produces": [
+ "text/plain"
+ ],
+ "tags": [
+ "miscellaneous"
+ ],
+ "summary": "Get default signing-key.pub",
+ "operationId": "getSigningKeySSH",
+ "responses": {
+ "200": {
+ "description": "ssh public key",
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
"/teams/{id}": {
"get": {
"produces": [
@@ -15702,7 +17844,7 @@
},
{
"type": "string",
- "description": "username of the member to list",
+ "description": "username of the user whose data is to be listed",
"name": "username",
"in": "path",
"required": true
@@ -15737,7 +17879,7 @@
},
{
"type": "string",
- "description": "username of the user to add",
+ "description": "username of the user to add to a team",
"name": "username",
"in": "path",
"required": true
@@ -15775,7 +17917,7 @@
},
{
"type": "string",
- "description": "username of the user to remove",
+ "description": "username of the user to remove from a team",
"name": "username",
"in": "path",
"required": true
@@ -16029,6 +18171,72 @@
}
}
},
+ "/user/actions/jobs": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "user"
+ ],
+ "summary": "Get workflow jobs",
+ "operationId": "getUserWorkflowJobs",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
+ "name": "status",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page number of results to return (1-based)",
+ "name": "page",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page size of results",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/WorkflowJobsList"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/user/actions/runners": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "user"
+ ],
+ "summary": "Get user-level runners",
+ "operationId": "getUserRunners",
+ "responses": {
+ "200": {
+ "$ref": "#/definitions/ActionRunnersResponse"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
"/user/actions/runners/registration-token": {
"get": {
"produces": [
@@ -16044,6 +18252,150 @@
"$ref": "#/responses/RegistrationToken"
}
}
+ },
+ "post": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "user"
+ ],
+ "summary": "Get an user's actions runner registration token",
+ "operationId": "userCreateRunnerRegistrationToken",
+ "responses": {
+ "200": {
+ "$ref": "#/responses/RegistrationToken"
+ }
+ }
+ }
+ },
+ "/user/actions/runners/{runner_id}": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "user"
+ ],
+ "summary": "Get an user-level runner",
+ "operationId": "getUserRunner",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "id of the runner",
+ "name": "runner_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/definitions/ActionRunner"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ },
+ "delete": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "user"
+ ],
+ "summary": "Delete an user-level runner",
+ "operationId": "deleteUserRunner",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "id of the runner",
+ "name": "runner_id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "runner has been deleted"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
+ }
+ },
+ "/user/actions/runs": {
+ "get": {
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "user"
+ ],
+ "summary": "Get workflow runs",
+ "operationId": "getUserWorkflowRuns",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "workflow event name",
+ "name": "event",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "workflow branch",
+ "name": "branch",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "workflow status (pending, queued, in_progress, failure, success, skipped)",
+ "name": "status",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "triggered by user",
+ "name": "actor",
+ "in": "query"
+ },
+ {
+ "type": "string",
+ "description": "triggering sha of the workflow run",
+ "name": "head_sha",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page number of results to return (1-based)",
+ "name": "page",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "description": "page size of results",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/WorkflowRunsList"
+ },
+ "400": {
+ "$ref": "#/responses/error"
+ },
+ "404": {
+ "$ref": "#/responses/notFound"
+ }
+ }
}
},
"/user/actions/secrets/{secretname}": {
@@ -16265,16 +18617,13 @@
],
"responses": {
"201": {
- "description": "response when creating a variable"
- },
- "204": {
- "description": "response when creating a variable"
+ "description": "successfully created the user-level variable"
},
"400": {
"$ref": "#/responses/error"
},
- "404": {
- "$ref": "#/responses/notFound"
+ "409": {
+ "description": "variable name already exists."
}
}
},
@@ -16547,7 +18896,7 @@
"parameters": [
{
"type": "string",
- "description": "user to check",
+ "description": "username of the user to check",
"name": "username",
"in": "path",
"required": true
@@ -16571,7 +18920,7 @@
"parameters": [
{
"type": "string",
- "description": "user to block",
+ "description": "username of the user to block",
"name": "username",
"in": "path",
"required": true
@@ -16604,7 +18953,7 @@
"parameters": [
{
"type": "string",
- "description": "user to unblock",
+ "description": "username of the user to unblock",
"name": "username",
"in": "path",
"required": true
@@ -16766,7 +19115,7 @@
"parameters": [
{
"type": "string",
- "description": "username of followed user",
+ "description": "username of the user to check for authenticated followers",
"name": "username",
"in": "path",
"required": true
@@ -16790,7 +19139,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user to follow",
+ "description": "username of the user to follow",
"name": "username",
"in": "path",
"required": true
@@ -16817,7 +19166,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user to unfollow",
+ "description": "username of the user to unfollow",
"name": "username",
"in": "path",
"required": true
@@ -17448,6 +19797,9 @@
"responses": {
"200": {
"$ref": "#/responses/RepositoryList"
+ },
+ "403": {
+ "$ref": "#/responses/forbidden"
}
}
}
@@ -17479,6 +19831,9 @@
"204": {
"$ref": "#/responses/empty"
},
+ "403": {
+ "$ref": "#/responses/forbidden"
+ },
"404": {
"$ref": "#/responses/notFound"
}
@@ -17544,6 +19899,9 @@
"204": {
"$ref": "#/responses/empty"
},
+ "403": {
+ "$ref": "#/responses/forbidden"
+ },
"404": {
"$ref": "#/responses/notFound"
}
@@ -17762,7 +20120,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user to get",
+ "description": "username of the user whose data is to be listed",
"name": "username",
"in": "path",
"required": true
@@ -17791,7 +20149,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose activity feeds are to be listed",
"name": "username",
"in": "path",
"required": true
@@ -17845,7 +20203,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose followers are to be listed",
"name": "username",
"in": "path",
"required": true
@@ -17886,7 +20244,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose followed users are to be listed",
"name": "username",
"in": "path",
"required": true
@@ -17924,14 +20282,14 @@
"parameters": [
{
"type": "string",
- "description": "username of following user",
+ "description": "username of the following user",
"name": "username",
"in": "path",
"required": true
},
{
"type": "string",
- "description": "username of followed user",
+ "description": "username of the followed user",
"name": "target",
"in": "path",
"required": true
@@ -17960,7 +20318,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose GPG key list is to be obtained",
"name": "username",
"in": "path",
"required": true
@@ -18001,7 +20359,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user to get",
+ "description": "username of the user whose heatmap is to be obtained",
"name": "username",
"in": "path",
"required": true
@@ -18030,7 +20388,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose public keys are to be listed",
"name": "username",
"in": "path",
"required": true
@@ -18077,7 +20435,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose organizations are to be listed",
"name": "username",
"in": "path",
"required": true
@@ -18118,7 +20476,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose permissions are to be obtained",
"name": "username",
"in": "path",
"required": true
@@ -18157,7 +20515,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose owned repos are to be listed",
"name": "username",
"in": "path",
"required": true
@@ -18198,7 +20556,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose starred repos are to be listed",
"name": "username",
"in": "path",
"required": true
@@ -18220,6 +20578,9 @@
"200": {
"$ref": "#/responses/RepositoryList"
},
+ "403": {
+ "$ref": "#/responses/forbidden"
+ },
"404": {
"$ref": "#/responses/notFound"
}
@@ -18239,7 +20600,7 @@
"parameters": [
{
"type": "string",
- "description": "username of the user",
+ "description": "username of the user whose watched repos are to be listed",
"name": "username",
"in": "path",
"required": true
@@ -18280,7 +20641,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of to user whose access tokens are to be listed",
"name": "username",
"in": "path",
"required": true
@@ -18322,7 +20683,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose token is to be created",
"name": "username",
"in": "path",
"required": true
@@ -18361,7 +20722,7 @@
"parameters": [
{
"type": "string",
- "description": "username of user",
+ "description": "username of the user whose token is to be deleted",
"name": "username",
"in": "path",
"required": true
@@ -18428,11 +20789,21 @@
"type": "object",
"title": "AccessToken represents an API access token.",
"properties": {
+ "created_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "Created"
+ },
"id": {
"type": "integer",
"format": "int64",
"x-go-name": "ID"
},
+ "last_used_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "Updated"
+ },
"name": {
"type": "string",
"x-go-name": "Name"
@@ -18455,6 +20826,150 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
+ "ActionArtifact": {
+ "description": "ActionArtifact represents a ActionArtifact",
+ "type": "object",
+ "properties": {
+ "archive_download_url": {
+ "type": "string",
+ "x-go-name": "ArchiveDownloadURL"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "CreatedAt"
+ },
+ "expired": {
+ "type": "boolean",
+ "x-go-name": "Expired"
+ },
+ "expires_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "ExpiresAt"
+ },
+ "id": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "ID"
+ },
+ "name": {
+ "type": "string",
+ "x-go-name": "Name"
+ },
+ "size_in_bytes": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "SizeInBytes"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "UpdatedAt"
+ },
+ "url": {
+ "type": "string",
+ "x-go-name": "URL"
+ },
+ "workflow_run": {
+ "$ref": "#/definitions/ActionWorkflowRun"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
+ "ActionArtifactsResponse": {
+ "description": "ActionArtifactsResponse returns ActionArtifacts",
+ "type": "object",
+ "properties": {
+ "artifacts": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ActionArtifact"
+ },
+ "x-go-name": "Entries"
+ },
+ "total_count": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "TotalCount"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
+ "ActionRunner": {
+ "description": "ActionRunner represents a Runner",
+ "type": "object",
+ "properties": {
+ "busy": {
+ "type": "boolean",
+ "x-go-name": "Busy"
+ },
+ "ephemeral": {
+ "type": "boolean",
+ "x-go-name": "Ephemeral"
+ },
+ "id": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "ID"
+ },
+ "labels": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ActionRunnerLabel"
+ },
+ "x-go-name": "Labels"
+ },
+ "name": {
+ "type": "string",
+ "x-go-name": "Name"
+ },
+ "status": {
+ "type": "string",
+ "x-go-name": "Status"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
+ "ActionRunnerLabel": {
+ "description": "ActionRunnerLabel represents a Runner Label",
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "ID"
+ },
+ "name": {
+ "type": "string",
+ "x-go-name": "Name"
+ },
+ "type": {
+ "type": "string",
+ "x-go-name": "Type"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
+ "ActionRunnersResponse": {
+ "description": "ActionRunnersResponse returns Runners",
+ "type": "object",
+ "properties": {
+ "runners": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ActionRunner"
+ },
+ "x-go-name": "Entries"
+ },
+ "total_count": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "TotalCount"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
"ActionTask": {
"description": "ActionTask represents a ActionTask",
"type": "object",
@@ -18547,6 +21062,11 @@
"type": "string",
"x-go-name": "Data"
},
+ "description": {
+ "description": "the description of the variable",
+ "type": "string",
+ "x-go-name": "Description"
+ },
"name": {
"description": "the name of the variable",
"type": "string",
@@ -18567,6 +21087,305 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
+ "ActionWorkflow": {
+ "description": "ActionWorkflow represents a ActionWorkflow",
+ "type": "object",
+ "properties": {
+ "badge_url": {
+ "type": "string",
+ "x-go-name": "BadgeURL"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "CreatedAt"
+ },
+ "deleted_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "DeletedAt"
+ },
+ "html_url": {
+ "type": "string",
+ "x-go-name": "HTMLURL"
+ },
+ "id": {
+ "type": "string",
+ "x-go-name": "ID"
+ },
+ "name": {
+ "type": "string",
+ "x-go-name": "Name"
+ },
+ "path": {
+ "type": "string",
+ "x-go-name": "Path"
+ },
+ "state": {
+ "type": "string",
+ "x-go-name": "State"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "UpdatedAt"
+ },
+ "url": {
+ "type": "string",
+ "x-go-name": "URL"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
+ "ActionWorkflowJob": {
+ "description": "ActionWorkflowJob represents a WorkflowJob",
+ "type": "object",
+ "properties": {
+ "completed_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "CompletedAt"
+ },
+ "conclusion": {
+ "type": "string",
+ "x-go-name": "Conclusion"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "CreatedAt"
+ },
+ "head_branch": {
+ "type": "string",
+ "x-go-name": "HeadBranch"
+ },
+ "head_sha": {
+ "type": "string",
+ "x-go-name": "HeadSha"
+ },
+ "html_url": {
+ "type": "string",
+ "x-go-name": "HTMLURL"
+ },
+ "id": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "ID"
+ },
+ "labels": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "x-go-name": "Labels"
+ },
+ "name": {
+ "type": "string",
+ "x-go-name": "Name"
+ },
+ "run_attempt": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "RunAttempt"
+ },
+ "run_id": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "RunID"
+ },
+ "run_url": {
+ "type": "string",
+ "x-go-name": "RunURL"
+ },
+ "runner_id": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "RunnerID"
+ },
+ "runner_name": {
+ "type": "string",
+ "x-go-name": "RunnerName"
+ },
+ "started_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "StartedAt"
+ },
+ "status": {
+ "type": "string",
+ "x-go-name": "Status"
+ },
+ "steps": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ActionWorkflowStep"
+ },
+ "x-go-name": "Steps"
+ },
+ "url": {
+ "type": "string",
+ "x-go-name": "URL"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
+ "ActionWorkflowJobsResponse": {
+ "description": "ActionWorkflowJobsResponse returns ActionWorkflowJobs",
+ "type": "object",
+ "properties": {
+ "jobs": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ActionWorkflowJob"
+ },
+ "x-go-name": "Entries"
+ },
+ "total_count": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "TotalCount"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
+ "ActionWorkflowRun": {
+ "description": "ActionWorkflowRun represents a WorkflowRun",
+ "type": "object",
+ "properties": {
+ "actor": {
+ "$ref": "#/definitions/User"
+ },
+ "completed_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "CompletedAt"
+ },
+ "conclusion": {
+ "type": "string",
+ "x-go-name": "Conclusion"
+ },
+ "display_title": {
+ "type": "string",
+ "x-go-name": "DisplayTitle"
+ },
+ "event": {
+ "type": "string",
+ "x-go-name": "Event"
+ },
+ "head_branch": {
+ "type": "string",
+ "x-go-name": "HeadBranch"
+ },
+ "head_repository": {
+ "$ref": "#/definitions/Repository"
+ },
+ "head_sha": {
+ "type": "string",
+ "x-go-name": "HeadSha"
+ },
+ "html_url": {
+ "type": "string",
+ "x-go-name": "HTMLURL"
+ },
+ "id": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "ID"
+ },
+ "path": {
+ "type": "string",
+ "x-go-name": "Path"
+ },
+ "repository": {
+ "$ref": "#/definitions/Repository"
+ },
+ "repository_id": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "RepositoryID"
+ },
+ "run_attempt": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "RunAttempt"
+ },
+ "run_number": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "RunNumber"
+ },
+ "started_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "StartedAt"
+ },
+ "status": {
+ "type": "string",
+ "x-go-name": "Status"
+ },
+ "trigger_actor": {
+ "$ref": "#/definitions/User"
+ },
+ "url": {
+ "type": "string",
+ "x-go-name": "URL"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
+ "ActionWorkflowRunsResponse": {
+ "description": "ActionWorkflowRunsResponse returns ActionWorkflowRuns",
+ "type": "object",
+ "properties": {
+ "total_count": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "TotalCount"
+ },
+ "workflow_runs": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ActionWorkflowRun"
+ },
+ "x-go-name": "Entries"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
+ "ActionWorkflowStep": {
+ "description": "ActionWorkflowStep represents a step of a WorkflowJob",
+ "type": "object",
+ "properties": {
+ "completed_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "CompletedAt"
+ },
+ "conclusion": {
+ "type": "string",
+ "x-go-name": "Conclusion"
+ },
+ "name": {
+ "type": "string",
+ "x-go-name": "Name"
+ },
+ "number": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "Number"
+ },
+ "started_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "StartedAt"
+ },
+ "status": {
+ "type": "string",
+ "x-go-name": "Status"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
"Activity": {
"type": "object",
"properties": {
@@ -18704,7 +21523,7 @@
"x-go-name": "Time"
},
"user_name": {
- "description": "User who spent the time (optional)",
+ "description": "username of the user who spent the time working on the issue (optional)",
"type": "string",
"x-go-name": "User"
}
@@ -19051,7 +21870,7 @@
],
"properties": {
"content": {
- "description": "new or updated file content, must be base64 encoded",
+ "description": "new or updated file content, it must be base64 encoded",
"type": "string",
"x-go-name": "ContentBase64"
},
@@ -19061,11 +21880,13 @@
"x-go-name": "FromPath"
},
"operation": {
- "description": "indicates what to do with the file",
+ "description": "indicates what to do with the file: \"create\" for creating a new file, \"update\" for updating an existing file,\n\"upload\" for creating or updating a file, \"rename\" for renaming a file, and \"delete\" for deleting an existing file.",
"type": "string",
"enum": [
"create",
"update",
+ "upload",
+ "rename",
"delete"
],
"x-go-name": "Operation"
@@ -19076,7 +21897,7 @@
"x-go-name": "Path"
},
"sha": {
- "description": "sha is the SHA for the file that already exists, required for update or delete",
+ "description": "the blob ID (SHA) for the file that already exists, required for changing existing files",
"type": "string",
"x-go-name": "SHA"
}
@@ -19192,7 +22013,17 @@
"x-go-name": "SHA"
},
"state": {
- "$ref": "#/definitions/CommitStatusState"
+ "type": "string",
+ "enum": [
+ "pending",
+ "success",
+ "error",
+ "failure",
+ "warning",
+ "skipped"
+ ],
+ "x-go-enum-desc": "pending CommitStatusPending CommitStatusPending is for when the CommitStatus is Pending\nsuccess CommitStatusSuccess CommitStatusSuccess is for when the CommitStatus is Success\nerror CommitStatusError CommitStatusError is for when the CommitStatus is Error\nfailure CommitStatusFailure CommitStatusFailure is for when the CommitStatus is Failure\nwarning CommitStatusWarning CommitStatusWarning is for when the CommitStatus is Warning\nskipped CommitStatusSkipped CommitStatusSkipped is for when CommitStatus is Skipped",
+ "x-go-name": "State"
},
"statuses": {
"type": "array",
@@ -19420,7 +22251,17 @@
"x-go-name": "ID"
},
"status": {
- "$ref": "#/definitions/CommitStatusState"
+ "type": "string",
+ "enum": [
+ "pending",
+ "success",
+ "error",
+ "failure",
+ "warning",
+ "skipped"
+ ],
+ "x-go-enum-desc": "pending CommitStatusPending CommitStatusPending is for when the CommitStatus is Pending\nsuccess CommitStatusSuccess CommitStatusSuccess is for when the CommitStatus is Success\nerror CommitStatusError CommitStatusError is for when the CommitStatus is Error\nfailure CommitStatusFailure CommitStatusFailure is for when the CommitStatus is Failure\nwarning CommitStatusWarning CommitStatusWarning is for when the CommitStatus is Warning\nskipped CommitStatusSkipped CommitStatusSkipped is for when CommitStatus is Skipped",
+ "x-go-name": "State"
},
"target_url": {
"type": "string",
@@ -19438,11 +22279,6 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
- "CommitStatusState": {
- "description": "CommitStatusState holds the state of a CommitStatus\nIt can be \"pending\", \"success\", \"error\" and \"failure\"",
- "type": "string",
- "x-go-package": "code.gitea.io/gitea/modules/structs"
- },
"CommitUser": {
"type": "object",
"title": "CommitUser contains information of a user in the context of a commit.",
@@ -19482,6 +22318,22 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
+ "ContentsExtResponse": {
+ "type": "object",
+ "properties": {
+ "dir_contents": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ContentsResponse"
+ },
+ "x-go-name": "DirContents"
+ },
+ "file_contents": {
+ "$ref": "#/definitions/ContentsResponse"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
"ContentsResponse": {
"description": "ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content",
"type": "object",
@@ -19511,10 +22363,33 @@
"type": "string",
"x-go-name": "HTMLURL"
},
+ "last_author_date": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "LastAuthorDate"
+ },
+ "last_commit_message": {
+ "type": "string",
+ "x-go-name": "LastCommitMessage"
+ },
"last_commit_sha": {
"type": "string",
"x-go-name": "LastCommitSHA"
},
+ "last_committer_date": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "LastCommitterDate"
+ },
+ "lfs_oid": {
+ "type": "string",
+ "x-go-name": "LfsOid"
+ },
+ "lfs_size": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "LfsSize"
+ },
"name": {
"type": "string",
"x-go-name": "Name"
@@ -19570,7 +22445,40 @@
"items": {
"type": "string"
},
- "x-go-name": "Scopes"
+ "x-go-name": "Scopes",
+ "example": [
+ "all",
+ "read:activitypub",
+ "read:issue",
+ "write:misc",
+ "read:notification",
+ "read:organization",
+ "read:package",
+ "read:repository",
+ "read:user"
+ ]
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
+ "CreateActionWorkflowDispatch": {
+ "description": "CreateActionWorkflowDispatch represents the payload for triggering a workflow dispatch event",
+ "type": "object",
+ "required": [
+ "ref"
+ ],
+ "properties": {
+ "inputs": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "x-go-name": "Inputs"
+ },
+ "ref": {
+ "type": "string",
+ "x-go-name": "Ref",
+ "example": "refs/heads/main"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
@@ -20117,6 +23025,11 @@
"description": "Data of the secret to update",
"type": "string",
"x-go-name": "Data"
+ },
+ "description": {
+ "description": "Description of the secret to update",
+ "type": "string",
+ "x-go-name": "Description"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
@@ -20149,6 +23062,7 @@
"x-go-name": "RepoAdminChangeTeamAccess"
},
"username": {
+ "description": "username of the organization",
"type": "string",
"x-go-name": "UserName"
},
@@ -20338,6 +23252,10 @@
"type": "boolean",
"x-go-name": "IsPrerelease"
},
+ "tag_message": {
+ "type": "string",
+ "x-go-name": "TagMessage"
+ },
"tag_name": {
"type": "string",
"x-go-name": "TagName"
@@ -20443,7 +23361,17 @@
"x-go-name": "Description"
},
"state": {
- "$ref": "#/definitions/CommitStatusState"
+ "type": "string",
+ "enum": [
+ "pending",
+ "success",
+ "error",
+ "failure",
+ "warning",
+ "skipped"
+ ],
+ "x-go-enum-desc": "pending CommitStatusPending CommitStatusPending is for when the CommitStatus is Pending\nsuccess CommitStatusSuccess CommitStatusSuccess is for when the CommitStatus is Success\nerror CommitStatusError CommitStatusError is for when the CommitStatus is Error\nfailure CommitStatusFailure CommitStatusFailure is for when the CommitStatus is Failure\nwarning CommitStatusWarning CommitStatusWarning is for when the CommitStatus is Warning\nskipped CommitStatusSkipped CommitStatusSkipped is for when CommitStatus is Skipped",
+ "x-go-name": "State"
},
"target_url": {
"type": "string",
@@ -20585,7 +23513,9 @@
"x-go-name": "FullName"
},
"login_name": {
+ "description": "identifier of the user, provided by the external authenticator (if configured)",
"type": "string",
+ "default": "empty",
"x-go-name": "LoginName"
},
"must_change_password": {
@@ -20610,6 +23540,7 @@
"x-go-name": "SourceID"
},
"username": {
+ "description": "username of the user",
"type": "string",
"x-go-name": "Username"
},
@@ -20627,6 +23558,11 @@
"value"
],
"properties": {
+ "description": {
+ "description": "Description of the variable to create",
+ "type": "string",
+ "x-go-name": "Description"
+ },
"value": {
"description": "Value of the variable to create",
"type": "string",
@@ -20734,7 +23670,7 @@
"x-go-name": "NewBranchName"
},
"sha": {
- "description": "sha is the SHA for the file that already exists",
+ "description": "the blob ID (SHA) for the file that already exists, it is required for changing existing files",
"type": "string",
"x-go-name": "SHA"
},
@@ -21590,7 +24526,9 @@
"x-go-name": "Location"
},
"login_name": {
+ "description": "identifier of the user, provided by the external authenticator (if configured)",
"type": "string",
+ "default": "empty",
"x-go-name": "LoginName"
},
"max_repo_creation": {
@@ -21649,6 +24587,7 @@
"x-go-name": "UserID"
},
"username": {
+ "description": "username of the user",
"type": "string",
"x-go-name": "UserName"
},
@@ -21910,6 +24849,11 @@
"format": "int64",
"x-go-name": "DefaultMaxBlobSize"
},
+ "default_max_response_size": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "DefaultMaxResponseSize"
+ },
"default_paging_num": {
"type": "integer",
"format": "int64",
@@ -22076,6 +25020,20 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
+ "GetFilesOptions": {
+ "description": "GetFilesOptions options for retrieving metadate and content of multiple files",
+ "type": "object",
+ "properties": {
+ "files": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "x-go-name": "Files"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
"GitBlobResponse": {
"description": "GitBlobResponse represents a git blob",
"type": "object",
@@ -22088,6 +25046,15 @@
"type": "string",
"x-go-name": "Encoding"
},
+ "lfs_oid": {
+ "type": "string",
+ "x-go-name": "LfsOid"
+ },
+ "lfs_size": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "LfsSize"
+ },
"sha": {
"type": "string",
"x-go-name": "SHA"
@@ -22730,6 +25697,17 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
+ "LockIssueOption": {
+ "description": "LockIssueOption options to lock an issue",
+ "type": "object",
+ "properties": {
+ "lock_reason": {
+ "type": "string",
+ "x-go-name": "Reason"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
"MarkdownOption": {
"description": "MarkdownOption markdown options",
"type": "object",
@@ -22827,6 +25805,30 @@
"x-go-name": "MergePullRequestForm",
"x-go-package": "code.gitea.io/gitea/services/forms"
},
+ "MergeUpstreamRequest": {
+ "type": "object",
+ "properties": {
+ "branch": {
+ "type": "string",
+ "x-go-name": "Branch"
+ },
+ "ff_only": {
+ "type": "boolean",
+ "x-go-name": "FfOnly"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
+ "MergeUpstreamResponse": {
+ "type": "object",
+ "properties": {
+ "merge_type": {
+ "type": "string",
+ "x-go-name": "MergeStyle"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
"MigrateRepoOptions": {
"description": "MigrateRepoOptions options for migrating repository's\nthis is used to interact with api v1",
"type": "object",
@@ -22922,7 +25924,8 @@
"gogs",
"onedev",
"gitbucket",
- "codebase"
+ "codebase",
+ "codecommit"
],
"x-go-name": "Service"
},
@@ -23309,7 +26312,7 @@
"x-go-name": "RepoAdminChangeTeamAccess"
},
"username": {
- "description": "deprecated",
+ "description": "username of the organization\ndeprecated",
"type": "string",
"x-go-name": "UserName"
},
@@ -23424,10 +26427,6 @@
"description": "PackageFile represents a package file",
"type": "object",
"properties": {
- "Size": {
- "type": "integer",
- "format": "int64"
- },
"id": {
"type": "integer",
"format": "int64",
@@ -23452,6 +26451,11 @@
"sha512": {
"type": "string",
"x-go-name": "HashSHA512"
+ },
+ "size": {
+ "type": "integer",
+ "format": "int64",
+ "x-go-name": "Size"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
@@ -23552,6 +26556,7 @@
"x-go-name": "Name"
},
"username": {
+ "description": "username of the user",
"type": "string",
"x-go-name": "UserName"
}
@@ -23603,6 +26608,11 @@
"type": "string",
"x-go-name": "KeyType"
},
+ "last_used_at": {
+ "type": "string",
+ "format": "date-time",
+ "x-go-name": "Updated"
+ },
"read_only": {
"type": "boolean",
"x-go-name": "ReadOnly"
@@ -24129,6 +27139,22 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
+ "RenameOrgOption": {
+ "description": "RenameOrgOption options when renaming an organization",
+ "type": "object",
+ "required": [
+ "new_name"
+ ],
+ "properties": {
+ "new_name": {
+ "description": "New username for this org. This name cannot be in use yet by any other user.",
+ "type": "string",
+ "uniqueItems": true,
+ "x-go-name": "NewName"
+ }
+ },
+ "x-go-package": "code.gitea.io/gitea/modules/structs"
+ },
"RenameUserOption": {
"description": "RenameUserOption options when renaming a user",
"type": "object",
@@ -24233,6 +27259,10 @@
"type": "boolean",
"x-go-name": "AllowFastForwardOnly"
},
+ "allow_manual_merge": {
+ "type": "boolean",
+ "x-go-name": "AllowManualMerge"
+ },
"allow_merge_commits": {
"type": "boolean",
"x-go-name": "AllowMerge"
@@ -24262,6 +27292,10 @@
"format": "date-time",
"x-go-name": "ArchivedAt"
},
+ "autodetect_manual_merge": {
+ "type": "boolean",
+ "x-go-name": "AutodetectManualMerge"
+ },
"avatar_url": {
"type": "string",
"x-go-name": "AvatarURL"
@@ -24552,6 +27586,11 @@
"format": "date-time",
"x-go-name": "Created"
},
+ "description": {
+ "description": "the secret's description",
+ "type": "string",
+ "x-go-name": "Description"
+ },
"name": {
"description": "the secret's name",
"type": "string",
@@ -24981,6 +28020,7 @@
"x-go-name": "UserID"
},
"user_name": {
+ "description": "username of the user",
"type": "string",
"x-go-name": "UserName"
}
@@ -25084,7 +28124,7 @@
"x-go-name": "NewBranchName"
},
"sha": {
- "description": "sha is the SHA for the file that already exists",
+ "description": "the blob ID (SHA) for the file that already exists, it is required for changing existing files",
"type": "string",
"x-go-name": "SHA"
},
@@ -25127,6 +28167,11 @@
"value"
],
"properties": {
+ "description": {
+ "description": "Description of the variable to update",
+ "type": "string",
+ "x-go-name": "Description"
+ },
"name": {
"description": "New name for the variable. If the field is empty, the variable name won't be updated.",
"type": "string",
@@ -25217,12 +28262,12 @@
"x-go-name": "Location"
},
"login": {
- "description": "the user's username",
+ "description": "login of the user, same as `username`",
"type": "string",
"x-go-name": "UserName"
},
"login_name": {
- "description": "the user's authentication sign-in name.",
+ "description": "identifier of the user, provided by the external authenticator (if configured)",
"type": "string",
"default": "empty",
"x-go-name": "LoginName"
@@ -25538,6 +28583,21 @@
"$ref": "#/definitions/ActionVariable"
}
},
+ "ActionWorkflow": {
+ "description": "ActionWorkflow",
+ "schema": {
+ "$ref": "#/definitions/ActionWorkflow"
+ }
+ },
+ "ActionWorkflowList": {
+ "description": "ActionWorkflowList",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ActionWorkflow"
+ }
+ }
+ },
"ActivityFeedsList": {
"description": "ActivityFeedsList",
"schema": {
@@ -25559,6 +28619,18 @@
"$ref": "#/definitions/AnnotatedTag"
}
},
+ "Artifact": {
+ "description": "Artifact",
+ "schema": {
+ "$ref": "#/definitions/ActionArtifact"
+ }
+ },
+ "ArtifactsList": {
+ "description": "ArtifactsList",
+ "schema": {
+ "$ref": "#/definitions/ActionArtifactsResponse"
+ }
+ },
"Attachment": {
"description": "Attachment",
"schema": {
@@ -25731,6 +28803,12 @@
"$ref": "#/definitions/Compare"
}
},
+ "ContentsExtResponse": {
+ "description": "",
+ "schema": {
+ "$ref": "#/definitions/ContentsExtResponse"
+ }
+ },
"ContentsListResponse": {
"description": "ContentsListResponse",
"schema": {
@@ -26008,6 +29086,18 @@
"type": "string"
}
},
+ "MergeUpstreamRequest": {
+ "description": "",
+ "schema": {
+ "$ref": "#/definitions/MergeUpstreamRequest"
+ }
+ },
+ "MergeUpstreamResponse": {
+ "description": "",
+ "schema": {
+ "$ref": "#/definitions/MergeUpstreamResponse"
+ }
+ },
"Milestone": {
"description": "Milestone",
"schema": {
@@ -26283,6 +29373,18 @@
}
}
},
+ "Runner": {
+ "description": "Runner",
+ "schema": {
+ "$ref": "#/definitions/ActionRunner"
+ }
+ },
+ "RunnerList": {
+ "description": "RunnerList",
+ "schema": {
+ "$ref": "#/definitions/ActionRunnersResponse"
+ }
+ },
"SearchResults": {
"description": "SearchResults",
"schema": {
@@ -26493,6 +29595,30 @@
}
}
},
+ "WorkflowJob": {
+ "description": "WorkflowJob",
+ "schema": {
+ "$ref": "#/definitions/ActionWorkflowJob"
+ }
+ },
+ "WorkflowJobsList": {
+ "description": "WorkflowJobsList",
+ "schema": {
+ "$ref": "#/definitions/ActionWorkflowJobsResponse"
+ }
+ },
+ "WorkflowRun": {
+ "description": "WorkflowRun",
+ "schema": {
+ "$ref": "#/definitions/ActionWorkflowRun"
+ }
+ },
+ "WorkflowRunsList": {
+ "description": "WorkflowRunsList",
+ "schema": {
+ "$ref": "#/definitions/ActionWorkflowRunsResponse"
+ }
+ },
"conflict": {
"description": "APIConflict is a conflict empty response"
},
@@ -26541,7 +29667,7 @@
"parameterBodies": {
"description": "parameterBodies",
"schema": {
- "$ref": "#/definitions/UpdateVariableOption"
+ "$ref": "#/definitions/LockIssueOption"
}
},
"redirect": {
@@ -26640,4 +29766,4 @@
"TOTPHeader": []
}
]
-}
+} \ No newline at end of file
diff --git a/templates/user/auth/captcha.tmpl b/templates/user/auth/captcha.tmpl
index 8dd4d1cc51..6779948da5 100644
--- a/templates/user/auth/captcha.tmpl
+++ b/templates/user/auth/captcha.tmpl
@@ -10,12 +10,12 @@
<div class="inline field tw-text-center required">
<div id="captcha" data-captcha-type="g-recaptcha" class="g-recaptcha-style" data-sitekey="{{.RecaptchaSitekey}}"></div>
</div>
- <script src='{{URLJoin .RecaptchaURL "api.js"}}'></script>
+ <script defer src='{{URLJoin .RecaptchaURL "api.js"}}'></script>
{{else if eq .CaptchaType "hcaptcha"}}
<div class="inline field tw-text-center required">
<div id="captcha" data-captcha-type="h-captcha" class="h-captcha-style" data-sitekey="{{.HcaptchaSitekey}}"></div>
</div>
- <script src='https://hcaptcha.com/1/api.js'></script>
+ <script defer src='https://hcaptcha.com/1/api.js'></script>
{{else if eq .CaptchaType "mcaptcha"}}
<div class="inline field tw-text-center">
<div class="m-captcha-style" id="mcaptcha__widget-container"></div>
@@ -25,5 +25,5 @@
<div class="inline field tw-text-center">
<div id="captcha" data-captcha-type="cf-turnstile" data-sitekey="{{.CfTurnstileSitekey}}"></div>
</div>
- <script src='https://challenges.cloudflare.com/turnstile/v0/api.js'></script>
+ <script defer src='https://challenges.cloudflare.com/turnstile/v0/api.js'></script>
{{end}}{{end}}
diff --git a/templates/user/auth/finalize_openid.tmpl b/templates/user/auth/finalize_openid.tmpl
deleted file mode 100644
index 1c1dcdb825..0000000000
--- a/templates/user/auth/finalize_openid.tmpl
+++ /dev/null
@@ -1,47 +0,0 @@
-{{template "base/head" .}}
-<div role="main" aria-label="{{.Title}}" class="page-content user signin">
- <div class="ui container">
- <div class="ui grid">
- {{template "user/auth/finalize_openid_navbar" .}}
- <div class="twelve wide column content">
- {{template "base/alert" .}}
- <h4 class="ui top attached header">
- {{ctx.Locale.Tr "auth.login_userpass"}}
- </h4>
- <div class="ui attached segment">
- <form class="ui form" action="{{.Link}}" method="post">
- {{.CsrfTokenHtml}}
- <div class="required inline field {{if .Err_UserName}}error{{end}}">
- <label for="user_name">{{ctx.Locale.Tr "home.uname_holder"}}</label>
- <input id="user_name" type="text" name="user_name" value="{{.user_name}}" autofocus required>
- </div>
- <div class="required inline field {{if .Err_Password}}error{{end}}">
- <label for="password">{{ctx.Locale.Tr "password"}}</label>
- <input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
- </div>
- <div class="inline field">
- <label></label>
- <div class="ui checkbox">
- <label>{{ctx.Locale.Tr "auth.remember_me"}}</label>
- <input name="remember" type="checkbox">
- </div>
- </div>
-
- <div class="inline field">
- <label></label>
- <button class="ui primary button">{{ctx.Locale.Tr "sign_in"}}</button>
- <a href="{{AppSubUrl}}/user/forget_password">{{ctx.Locale.Tr "auth.forget_password"}}</a>
- </div>
- {{if .ShowRegistrationButton}}
- <div class="inline field">
- <label></label>
- <a href="{{AppSubUrl}}/user/sign_up">{{ctx.Locale.Tr "auth.sign_up_now"}}</a>
- </div>
- {{end}}
- </form>
- </div>
- </div>
- </div>
- </div>
-</div>
-{{template "base/footer" .}}
diff --git a/templates/user/auth/grant.tmpl b/templates/user/auth/grant.tmpl
index 4031dd7a63..e56241b0f8 100644
--- a/templates/user/auth/grant.tmpl
+++ b/templates/user/auth/grant.tmpl
@@ -1,35 +1,33 @@
{{template "base/head" .}}
-<div role="main" aria-label="{{.Title}}" class="page-content ui one column stackable center aligned page grid oauth2-authorize-application-box">
- <div class="column seven wide">
- <div class="ui middle centered raised segments">
- <h3 class="ui top attached header">
- {{ctx.Locale.Tr "auth.authorize_title" .Application.Name}}
- </h3>
- <div class="ui attached segment">
- {{template "base/alert" .}}
- <p>
- {{if not .AdditionalScopes}}
- <b>{{ctx.Locale.Tr "auth.authorize_application_description"}}</b><br>
- {{end}}
- {{ctx.Locale.Tr "auth.authorize_application_created_by" .ApplicationCreatorLinkHTML}}<br>
- {{ctx.Locale.Tr "auth.authorize_application_with_scopes" (HTMLFormat "<b>%s</b>" .Scope)}}
- </p>
- </div>
- <div class="ui attached segment">
- <p>{{ctx.Locale.Tr "auth.authorize_redirect_notice" .ApplicationRedirectDomainHTML}}</p>
- </div>
- <div class="ui attached segment">
- <form method="post" action="{{AppSubUrl}}/login/oauth/grant">
- {{.CsrfTokenHtml}}
- <input type="hidden" name="client_id" value="{{.Application.ClientID}}">
- <input type="hidden" name="state" value="{{.State}}">
- <input type="hidden" name="scope" value="{{.Scope}}">
- <input type="hidden" name="nonce" value="{{.Nonce}}">
- <input type="hidden" name="redirect_uri" value="{{.RedirectURI}}">
- <button type="submit" id="authorize-app" name="granted" value="true" class="ui red inline button">{{ctx.Locale.Tr "auth.authorize_application"}}</button>
- <button type="submit" name="granted" value="false" class="ui basic primary inline button">{{ctx.Locale.Tr "cancel"}}</button>
- </form>
- </div>
+<div role="main" aria-label="{{.Title}}" class="page-content oauth2-authorize-application-box">
+ <div class="ui container tw-max-w-[500px]">
+ <h3 class="ui top attached header">
+ {{ctx.Locale.Tr "auth.authorize_title" .Application.Name}}
+ </h3>
+ <div class="ui attached segment">
+ {{template "base/alert" .}}
+ <p>
+ {{if not .AdditionalScopes}}
+ <b>{{ctx.Locale.Tr "auth.authorize_application_description"}}</b><br>
+ {{end}}
+ {{ctx.Locale.Tr "auth.authorize_application_created_by" .ApplicationCreatorLinkHTML}}<br>
+ {{ctx.Locale.Tr "auth.authorize_application_with_scopes" (HTMLFormat "<b>%s</b>" .Scope)}}
+ </p>
+ </div>
+ <div class="ui attached segment">
+ <p>{{ctx.Locale.Tr "auth.authorize_redirect_notice" .ApplicationRedirectDomainHTML}}</p>
+ </div>
+ <div class="ui attached segment tw-text-center">
+ <form method="post" action="{{AppSubUrl}}/login/oauth/grant">
+ {{.CsrfTokenHtml}}
+ <input type="hidden" name="client_id" value="{{.Application.ClientID}}">
+ <input type="hidden" name="state" value="{{.State}}">
+ <input type="hidden" name="scope" value="{{.Scope}}">
+ <input type="hidden" name="nonce" value="{{.Nonce}}">
+ <input type="hidden" name="redirect_uri" value="{{.RedirectURI}}">
+ <button type="submit" id="authorize-app" name="granted" value="true" class="ui red inline button">{{ctx.Locale.Tr "auth.authorize_application"}}</button>
+ <button type="submit" name="granted" value="false" class="ui basic primary inline button">{{ctx.Locale.Tr "cancel"}}</button>
+ </form>
</div>
</div>
</div>
diff --git a/templates/user/auth/grant_error.tmpl b/templates/user/auth/grant_error.tmpl
index b2e077960c..7a4521d331 100644
--- a/templates/user/auth/grant_error.tmpl
+++ b/templates/user/auth/grant_error.tmpl
@@ -1,15 +1,12 @@
{{template "base/head" .}}
-<div role="main" aria-label="{{.Title}}" class="page-content ui one column stackable center aligned page grid oauth2-authorize-application-box {{if .IsRepo}}repository{{end}}">
- {{if .IsRepo}}{{template "repo/header" .}}{{end}}
- <div class="column seven wide">
- <div class="ui middle centered raised segments">
- <h1 class="ui top attached header">
- {{ctx.Locale.Tr "auth.authorization_failed"}}
- </h1>
- <h3 class="ui attached segment">{{.Error.ErrorDescription}}</h3>
- <div class="ui attached segment">
- <p>{{ctx.Locale.Tr "auth.authorization_failed_desc"}}</p>
- </div>
+<div role="main" aria-label="{{.Title}}" class="page-content oauth2-authorize-application-box">
+ <div class="ui container tw-max-w-[500px]">
+ <h1 class="ui top attached header">
+ {{ctx.Locale.Tr "auth.authorization_failed"}}
+ </h1>
+ <h3 class="ui attached segment">{{.Error.ErrorDescription}}</h3>
+ <div class="ui attached segment">
+ <p>{{ctx.Locale.Tr "auth.authorization_failed_desc"}}</p>
</div>
</div>
</div>
diff --git a/templates/user/auth/link_account.tmpl b/templates/user/auth/link_account.tmpl
index a99e172d05..d244ce38c2 100644
--- a/templates/user/auth/link_account.tmpl
+++ b/templates/user/auth/link_account.tmpl
@@ -16,13 +16,18 @@
</div>
</overflow-menu>
<div class="ui middle very relaxed page grid">
- <div class="column">
+ <div class="column tw-my-5">
+ {{/* these styles are quite tricky but it needs to be the same as the signin page */}}
<div class="ui tab {{if not .user_exists}}active{{end}}" data-tab="auth-link-signup-tab">
+ <div class="tw-flex tw-flex-col tw-gap-4 tw-max-w-2xl tw-m-auto">
{{if .AutoRegistrationFailedPrompt}}<div class="ui message">{{.AutoRegistrationFailedPrompt}}</div>{{end}}
{{template "user/auth/signup_inner" .}}
+ </div>
</div>
<div class="ui tab {{if .user_exists}}active{{end}}" data-tab="auth-link-signin-tab">
+ <div class="tw-flex tw-flex-col tw-gap-4 tw-max-w-2xl tw-m-auto">
{{template "user/auth/signin_inner" .}}
+ </div>
</div>
</div>
</div>
diff --git a/templates/user/auth/oidc_wellknown.tmpl b/templates/user/auth/oidc_wellknown.tmpl
index 54bb4a763d..52c9a1b788 100644
--- a/templates/user/auth/oidc_wellknown.tmpl
+++ b/templates/user/auth/oidc_wellknown.tmpl
@@ -1,16 +1,16 @@
{
- "issuer": "{{AppUrl | JSEscape}}",
- "authorization_endpoint": "{{AppUrl | JSEscape}}login/oauth/authorize",
- "token_endpoint": "{{AppUrl | JSEscape}}login/oauth/access_token",
- "jwks_uri": "{{AppUrl | JSEscape}}login/oauth/keys",
- "userinfo_endpoint": "{{AppUrl | JSEscape}}login/oauth/userinfo",
- "introspection_endpoint": "{{AppUrl | JSEscape}}login/oauth/introspect",
+ "issuer": "{{.OidcIssuer}}",
+ "authorization_endpoint": "{{.OidcBaseUrl}}/login/oauth/authorize",
+ "token_endpoint": "{{.OidcBaseUrl}}/login/oauth/access_token",
+ "jwks_uri": "{{.OidcBaseUrl}}/login/oauth/keys",
+ "userinfo_endpoint": "{{.OidcBaseUrl}}/login/oauth/userinfo",
+ "introspection_endpoint": "{{.OidcBaseUrl}}/login/oauth/introspect",
"response_types_supported": [
"code",
"id_token"
],
"id_token_signing_alg_values_supported": [
- "{{.SigningKey.SigningMethod.Alg | JSEscape}}"
+ "{{.SigningKeyMethodAlg}}"
],
"subject_types_supported": [
"public"
diff --git a/templates/user/auth/reset_passwd.tmpl b/templates/user/auth/reset_passwd.tmpl
index f8303feef3..37a23b3e55 100644
--- a/templates/user/auth/reset_passwd.tmpl
+++ b/templates/user/auth/reset_passwd.tmpl
@@ -34,18 +34,18 @@
{{ctx.Locale.Tr "twofa"}}
</h4>
<div class="ui warning visible message">{{ctx.Locale.Tr "settings.twofa_is_enrolled"}}</div>
- {{if .scratch_code}}
- <div class="required inline field {{if .Err_Token}}error{{end}}">
- <label for="token">{{ctx.Locale.Tr "auth.scratch_code"}}</label>
- <input id="token" name="token" type="text" autocomplete="off" autofocus required>
- </div>
- <input type="hidden" name="scratch_code" value="true">
- {{else}}
- <div class="required field {{if .Err_Passcode}}error{{end}}">
- <label for="passcode">{{ctx.Locale.Tr "passcode"}}</label>
- <input id="passcode" name="passcode" type="number" autocomplete="off" autofocus required>
- </div>
- {{end}}
+ {{if .scratch_code}}
+ <div class="required inline field {{if .Err_Token}}error{{end}}">
+ <label for="token">{{ctx.Locale.Tr "auth.scratch_code"}}</label>
+ <input id="token" name="token" type="text" autocomplete="off" autofocus required>
+ </div>
+ <input type="hidden" name="scratch_code" value="true">
+ {{else}}
+ <div class="required field {{if .Err_Passcode}}error{{end}}">
+ <label for="passcode">{{ctx.Locale.Tr "passcode"}}</label>
+ <input id="passcode" name="passcode" type="number" autocomplete="off" autofocus required>
+ </div>
+ {{end}}
{{end}}
<div class="divider"></div>
<div class="inline field">
diff --git a/templates/user/auth/signin.tmpl b/templates/user/auth/signin.tmpl
index 54cc82d49d..75e1bb27f9 100644
--- a/templates/user/auth/signin.tmpl
+++ b/templates/user/auth/signin.tmpl
@@ -1,6 +1,7 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content user signin{{if .LinkAccountMode}} icon{{end}}">
<div class="ui middle very relaxed page grid">
+ {{/* these styles are quite tricky and should also apply to the signup and link_account pages */}}
<div class="column tw-flex tw-flex-col tw-gap-4 tw-max-w-2xl tw-m-auto">
{{template "user/auth/signin_inner" .}}
</div>
diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl
index dd608e5aa1..5552363a0a 100644
--- a/templates/user/auth/signin_inner.tmpl
+++ b/templates/user/auth/signin_inner.tmpl
@@ -18,9 +18,9 @@
<input id="user_name" type="text" name="user_name" value="{{.user_name}}" autofocus required tabindex="1">
</div>
{{if or (not .DisablePassword) .LinkAccountMode}}
- <div class="required field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}} form-field-content-aside-label">
- <label for="password">{{ctx.Locale.Tr "password"}}</label>
- <div>
+ <div class="required field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
+ <div class="tw-flex tw-mb-1">
+ <label for="password" class="tw-flex-1">{{ctx.Locale.Tr "password"}}</label>
<a href="{{AppSubUrl}}/user/forgot_password" tabindex="4">{{ctx.Locale.Tr "auth.forgot_password"}}</a>
</div>
<input id="password" name="password" type="password" value="{{.password}}" autocomplete="current-password" required tabindex="2">
@@ -59,11 +59,13 @@
</div>
</div>
+{{if or .EnablePasskeyAuth .ShowRegistrationButton}}
<div class="ui container fluid">
- {{template "user/auth/webauthn_error" .}}
-
<div class="ui attached segment header top tw-max-w-2xl tw-m-auto tw-flex tw-flex-col tw-items-center">
- <a class="signin-passkey">{{ctx.Locale.Tr "auth.signin_passkey"}}</a>
+ {{if .EnablePasskeyAuth}}
+ {{template "user/auth/webauthn_error" .}}
+ <a class="signin-passkey">{{ctx.Locale.Tr "auth.signin_passkey"}}</a>
+ {{end}}
{{if .ShowRegistrationButton}}
<div class="field">
@@ -73,3 +75,4 @@
{{end}}
</div>
</div>
+{{end}}
diff --git a/templates/user/auth/signup_inner.tmpl b/templates/user/auth/signup_inner.tmpl
index b3b2a4205e..a3f6e1471f 100644
--- a/templates/user/auth/signup_inner.tmpl
+++ b/templates/user/auth/signup_inner.tmpl
@@ -7,6 +7,9 @@
{{end}}
</h4>
<div class="ui attached segment">
+ {{if .IsFirstTimeRegistration}}
+ <p>{{ctx.Locale.Tr "auth.sign_up_tip"}}</p>
+ {{end}}
<form class="ui form" action="{{.SignUpLink}}" method="post">
{{.CsrfTokenHtml}}
{{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister)}}
@@ -59,12 +62,12 @@
</div>
<div class="ui container fluid">
+ {{if not .LinkAccountMode}}
<div class="ui attached segment header top tw-flex tw-flex-col tw-items-center">
- {{if not .LinkAccountMode}}
<div class="field">
<span>{{ctx.Locale.Tr "auth.already_have_account"}}</span>
<a href="{{AppSubUrl}}/user/login">{{ctx.Locale.Tr "auth.sign_in_now"}}</a>
</div>
- {{end}}
</div>
+ {{end}}
</div>
diff --git a/templates/user/auth/signup_openid_connect.tmpl b/templates/user/auth/signup_openid_connect.tmpl
index e4b7936374..67e99b2f89 100644
--- a/templates/user/auth/signup_openid_connect.tmpl
+++ b/templates/user/auth/signup_openid_connect.tmpl
@@ -1,36 +1,36 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content user signup">
{{template "user/auth/signup_openid_navbar" .}}
- <div class="ui container">
- {{template "base/alert" .}}
- <h4 class="ui top attached header">
- {{ctx.Locale.Tr "auth.openid_connect_title"}}
- </h4>
- <div class="ui attached segment">
- <p>
- {{ctx.Locale.Tr "auth.openid_connect_desc"}}
- </p>
- <form class="ui form" action="{{.Link}}" method="post">
- {{.CsrfTokenHtml}}
- <div class="required inline field {{if .Err_UserName}}error{{end}}">
- <label for="user_name">{{ctx.Locale.Tr "home.uname_holder"}}</label>
- <input id="user_name" type="text" name="user_name" value="{{.user_name}}" autofocus required>
- </div>
- <div class="required inline field {{if .Err_Password}}error{{end}}">
- <label for="password">{{ctx.Locale.Tr "password"}}</label>
- <input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
- </div>
- <div class="inline field">
- <label for="openid">OpenID URI</label>
- <input id="openid" value="{{.OpenID}}" readonly>
- </div>
- <div class="inline field">
- <label></label>
- <button class="ui primary button">{{ctx.Locale.Tr "auth.openid_connect_submit"}}</button>
- <a href="{{AppSubUrl}}/user/forgot_password">{{ctx.Locale.Tr "auth.forgot_password"}}</a>
- </div>
- </form>
+ <div class="ui container medium-width">
+ {{template "base/alert" .}}
+ <h4 class="ui top attached header">
+ {{ctx.Locale.Tr "auth.openid_connect_title"}}
+ </h4>
+ <div class="ui attached segment">
+ <form class="ui form left-right-form" action="{{.Link}}" method="post">
+ {{.CsrfTokenHtml}}
+ <div class="inline field">
+ <span class="help">{{ctx.Locale.Tr "auth.openid_connect_desc"}}</span>
</div>
+ <div class="required inline field {{if .Err_UserName}}error{{end}}">
+ <label for="user_name">{{ctx.Locale.Tr "home.uname_holder"}}</label>
+ <input id="user_name" type="text" name="user_name" value="{{.user_name}}" autofocus required>
+ </div>
+ <div class="required inline field {{if .Err_Password}}error{{end}}">
+ <label for="password">{{ctx.Locale.Tr "password"}}</label>
+ <input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
+ </div>
+ <div class="inline field">
+ <label for="openid">OpenID URI</label>
+ <input id="openid" value="{{.OpenID}}" readonly>
+ </div>
+ <div class="inline field">
+ <label></label>
+ <button class="ui primary button">{{ctx.Locale.Tr "auth.openid_connect_submit"}}</button>
+ <a href="{{AppSubUrl}}/user/forgot_password">{{ctx.Locale.Tr "auth.forgot_password"}}</a>
+ </div>
+ </form>
+ </div>
</div>
</div>
{{template "base/footer" .}}
diff --git a/templates/user/auth/signup_openid_register.tmpl b/templates/user/auth/signup_openid_register.tmpl
index c017a0e65b..df6268d151 100644
--- a/templates/user/auth/signup_openid_register.tmpl
+++ b/templates/user/auth/signup_openid_register.tmpl
@@ -7,7 +7,7 @@
{{ctx.Locale.Tr "auth.openid_register_title"}}
</h4>
<div class="ui attached segment">
- <p class="tw-max-w-2xl tw-mx-auto">
+ <p>
{{ctx.Locale.Tr "auth.openid_register_desc"}}
</p>
<form class="ui form" action="{{.Link}}" method="post">
diff --git a/templates/user/auth/webauthn.tmpl b/templates/user/auth/webauthn.tmpl
index 1b84765323..158ad0a546 100644
--- a/templates/user/auth/webauthn.tmpl
+++ b/templates/user/auth/webauthn.tmpl
@@ -1,7 +1,7 @@
{{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content user signin webauthn-prompt">
<div class="ui page grid">
- <div class="column center aligned">
+ <div class="column tw-text-center">
{{template "user/auth/webauthn_error" .}}
<h3 class="ui top attached header">{{ctx.Locale.Tr "twofa"}}</h3>
<div class="ui attached segment">
diff --git a/templates/user/dashboard/dashboard.tmpl b/templates/user/dashboard/dashboard.tmpl
index 5dc46dc0a5..666dd78073 100644
--- a/templates/user/dashboard/dashboard.tmpl
+++ b/templates/user/dashboard/dashboard.tmpl
@@ -5,7 +5,11 @@
<div class="flex-container-main">
{{template "base/alert" .}}
{{template "user/heatmap" .}}
- {{template "user/dashboard/feeds" .}}
+ {{if .Page.Paginater.TotalPages}}
+ {{template "user/dashboard/feeds" .}}
+ {{else}}
+ {{template "user/dashboard/guide" .}}
+ {{end}}
</div>
{{template "user/dashboard/repolist" .}}
</div>
diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl
index 1c1ba5566f..37dede7af6 100644
--- a/templates/user/dashboard/feeds.tmpl
+++ b/templates/user/dashboard/feeds.tmpl
@@ -73,10 +73,13 @@
{{else if .GetOpType.InActions "publish_release"}}
{{$linkText := .Content | ctx.RenderUtils.RenderEmoji}}
{{ctx.Locale.Tr "action.publish_release" (.GetRepoLink ctx) (printf "%s/releases/tag/%s" (.GetRepoLink ctx) .GetTag) (.ShortRepoPath ctx) $linkText}}
- {{else if .GetOpType.InActions "review_dismissed"}}
+ {{else if .GetOpType.InActions "pull_review_dismissed"}}
{{$index := index .GetIssueInfos 0}}
{{$reviewer := index .GetIssueInfos 1}}
{{ctx.Locale.Tr "action.review_dismissed" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx) $reviewer}}
+ {{else if .GetOpType.InActions "auto_merge_pull_request"}}
+ {{$index := index .GetIssueInfos 0}}
+ {{ctx.Locale.Tr "action.auto_merge_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}}
{{end}}
{{DateUtils.TimeSince .GetCreate}}
</div>
@@ -88,10 +91,10 @@
{{range $push.Commits}}
{{$commitLink := printf "%s/commit/%s" $repoLink .Sha1}}
<div class="flex-text-block">
- <img class="ui avatar" src="{{$push.AvatarLink ctx .AuthorEmail}}" title="{{.AuthorName}}" width="16" height="16">
+ <img loading="lazy" alt class="ui avatar" src="{{$push.AvatarLink ctx .AuthorEmail}}" title="{{.AuthorName}}" width="16" height="16">
<a class="ui sha label" href="{{$commitLink}}">{{ShortSha .Sha1}}</a>
<span class="text truncate">
- {{ctx.RenderUtils.RenderCommitMessage .Message ($repo.ComposeMetas ctx)}}
+ {{ctx.RenderUtils.RenderCommitMessage .Message $repo}}
</span>
</div>
{{end}}
@@ -107,7 +110,7 @@
<a href="{{.GetCommentLink ctx}}" class="text truncate issue title">{{(.GetIssueTitle ctx) | ctx.RenderUtils.RenderIssueSimpleTitle}}</a>
{{$comment := index .GetIssueInfos 1}}
{{if $comment}}
- <div class="markup tw-text-14">{{ctx.RenderUtils.MarkdownToHtml $comment}}</div>
+ <div class="render-content markup tw-text-14">{{ctx.RenderUtils.MarkdownToHtml $comment}}</div>
{{end}}
{{else if .GetOpType.InActions "merge_pull_request"}}
<div class="flex-item-body text black">{{index .GetIssueInfos 1}}</div>
diff --git a/templates/user/dashboard/guide.tmpl b/templates/user/dashboard/guide.tmpl
new file mode 100644
index 0000000000..bdbe81ece0
--- /dev/null
+++ b/templates/user/dashboard/guide.tmpl
@@ -0,0 +1,10 @@
+<div class="tw-text-center tw-p-8">
+ {{svg "octicon-package" 24 "tw-text-placeholder-text"}}
+ <h3 class="tw-my-4">{{ctx.Locale.Tr "home.guide_title"}}</h3>
+ <p class="tw-text-placeholder-text">{{ctx.Locale.Tr "home.guide_desc"}}</p>
+ <div>
+ <a href="{{AppSubUrl}}/explore/repos">{{ctx.Locale.Tr "home.explore_repos"}}</a>
+ <span>·</span>
+ <a href="{{AppSubUrl}}/explore/users">{{ctx.Locale.Tr "home.explore_users"}}</a>
+ </div>
+</div>
diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl
index 7924dd2305..54c96a3aa5 100644
--- a/templates/user/dashboard/issues.tmpl
+++ b/templates/user/dashboard/issues.tmpl
@@ -4,7 +4,7 @@
<div class="ui container">
{{template "base/alert" .}}
<div class="flex-container">
- {{$queryLink := QueryBuild "?" "type" $.ViewType "sort" $.SortType "state" $.State "q" $.Keyword "labels" .SelectLabels "fuzzy" $.IsFuzzy}}
+ {{$queryLink := QueryBuild "?" "type" $.ViewType "sort" $.SortType "state" $.State "q" $.Keyword "labels" .SelectLabels "search_mode" $.SelectedSearchMode}}
<div class="flex-container-nav">
<div class="ui secondary vertical filter menu tw-bg-transparent">
<a class="{{if eq .ViewType "your_repositories"}}active{{end}} item" href="{{QueryBuild $queryLink "type" "your_repositories"}}">
@@ -53,7 +53,13 @@
<input type="hidden" name="type" value="{{$.ViewType}}">
<input type="hidden" name="sort" value="{{$.SortType}}">
<input type="hidden" name="state" value="{{$.State}}">
- {{template "shared/search/combo_fuzzy" dict "Value" $.Keyword "IsFuzzy" $.IsFuzzy "Placeholder" (ctx.Locale.Tr (Iif .PageIsPulls "search.pull_kind" "search.issue_kind")) "Tooltip" (ctx.Locale.Tr "explore.go_to")}}
+ {{template "shared/search/combo" (dict
+ "Value" $.Keyword
+ "Placeholder" (ctx.Locale.Tr (Iif .PageIsPulls "search.pull_kind" "search.issue_kind"))
+ "Tooltip" (ctx.Locale.Tr "explore.go_to")
+ "SearchModes" .SearchModes
+ "SelectedSearchMode" .SelectedSearchMode
+ )}}
</form>
<div class="list-header-filters">
diff --git a/templates/user/dashboard/milestones.tmpl b/templates/user/dashboard/milestones.tmpl
index c0059d3cd4..15b1b9e30b 100644
--- a/templates/user/dashboard/milestones.tmpl
+++ b/templates/user/dashboard/milestones.tmpl
@@ -33,7 +33,7 @@
{{end}}
</div>
</div>
- <div class="flex-container-main content">
+ <div class="flex-container-main">
<div class="list-header">
<div class="small-menu-items ui compact tiny menu list-header-toggle">
<a class="item{{if not .IsShowClosed}} active{{end}}" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=open&q={{$.Keyword}}">
@@ -100,7 +100,7 @@
{{if .TotalTrackedTime}}
<div class="flex-text-block">
{{svg "octicon-clock"}}
- {{.TotalTrackedTime|Sec2Time}}
+ {{.TotalTrackedTime|Sec2Hour}}
</div>
{{end}}
{{if .UpdatedUnix}}
@@ -116,7 +116,7 @@
{{ctx.Locale.Tr "repo.milestones.closed" $closedDate}}
{{else}}
{{if .DeadlineString}}
- <span{{if .IsOverdue}} class="text red"{{end}}>
+ <span class="flex-text-inline {{if .IsOverdue}}text red{{end}}">
{{svg "octicon-calendar" 14}}
{{DateUtils.AbsoluteShort (.DeadlineString|DateUtils.ParseLegacy)}}
</span>
@@ -140,9 +140,7 @@
{{end}}
</div>
{{if .Content}}
- <div class="markup content">
- {{.RenderedContent}}
- </div>
+ <div class="render-content markup">{{.RenderedContent}}</div>
{{end}}
</li>
{{end}}
diff --git a/templates/user/dashboard/navbar.tmpl b/templates/user/dashboard/navbar.tmpl
index 7982cbd950..7eb845833f 100644
--- a/templates/user/dashboard/navbar.tmpl
+++ b/templates/user/dashboard/navbar.tmpl
@@ -2,32 +2,32 @@
<div class="ui secondary stackable menu">
<div class="item">
<div class="ui floating dropdown jump">
- <span class="text truncated-item-container">
+ <span class="text">
{{ctx.AvatarUtils.Avatar .ContextUser 24 "tw-mr-1"}}
- <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span>
+ <span class="gt-ellipsis">{{.ContextUser.ShortName 40}}</span>
<span class="org-visibility">
{{if .ContextUser.Visibility.IsLimited}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
{{if .ContextUser.Visibility.IsPrivate}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</div>{{end}}
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon tw-ml-1"}}
</span>
- <div class="context user overflow menu">
- <div class="ui header">
+ <div class="menu context-user-switch">
+ <div class="header">
{{ctx.Locale.Tr "home.switch_dashboard_context"}}
</div>
- <div class="scrolling menu items">
- <a class="{{if eq .ContextUser.ID .SignedUser.ID}}active selected{{end}} item truncated-item-container" href="{{AppSubUrl}}/{{if .PageIsIssues}}issues{{else if .PageIsPulls}}pulls{{else if .PageIsMilestonesDashboard}}milestones{{end}}">
+ <div class="scrolling menu">
+ <a class="{{if eq .ContextUser.ID .SignedUser.ID}}active selected{{end}} item" href="{{AppSubUrl}}/{{if .PageIsIssues}}issues{{else if .PageIsPulls}}pulls{{else if .PageIsMilestonesDashboard}}milestones{{end}}">
{{ctx.AvatarUtils.Avatar .SignedUser}}
- <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span>
+ <span class="gt-ellipsis">{{.SignedUser.ShortName 40}}</span>
<span class="org-visibility">
{{if .SignedUser.Visibility.IsLimited}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
{{if .SignedUser.Visibility.IsPrivate}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</div>{{end}}
</span>
</a>
{{range .Orgs}}
- <a class="{{if eq $.ContextUser.ID .ID}}active selected{{end}} item truncated-item-container" title="{{.Name}}" href="{{.OrganisationLink}}/{{if $.PageIsIssues}}issues{{else if $.PageIsPulls}}pulls{{else if $.PageIsMilestonesDashboard}}milestones{{else}}dashboard{{end}}">
+ <a class="{{if eq $.ContextUser.ID .ID}}active selected{{end}} item" title="{{.Name}}" href="{{.OrganisationLink}}/{{if $.PageIsIssues}}issues{{else if $.PageIsPulls}}pulls{{else if $.PageIsMilestonesDashboard}}milestones{{else}}dashboard{{end}}">
{{ctx.AvatarUtils.Avatar .}}
- <span class="truncated-item-name">{{.ShortName 40}}</span>
+ <span class="gt-ellipsis">{{.ShortName 40}}</span>
<span class="org-visibility">
{{if .Visibility.IsLimited}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
{{if .Visibility.IsPrivate}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</div>{{end}}
@@ -37,7 +37,7 @@
</div>
{{if .SignedUser.CanCreateOrganization}}
<a class="item" href="{{AppSubUrl}}/org/create">
- {{svg "octicon-plus"}}&nbsp;&nbsp;&nbsp;{{ctx.Locale.Tr "new_org"}}
+ {{svg "octicon-plus" 16 "tw-ml-1 tw-mr-5"}}{{ctx.Locale.Tr "new_org"}}
</a>
{{end}}
</div>
@@ -56,7 +56,7 @@
</span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="context user overflow menu">
- <div class="ui header">
+ <div class="header">
{{ctx.Locale.Tr "home.filter_by_team_repositories"}}
</div>
<div class="scrolling menu items">
@@ -77,7 +77,7 @@
{{end}}
{{if .ContextUser.IsOrganization}}
- <div class="right menu">
+ <div class="right menu tw-flex-wrap tw-justify-end">
<a class="{{if .PageIsNews}}active {{end}}item tw-ml-auto" href="{{.ContextUser.DashboardLink}}{{if .Team}}/{{PathEscape .Team.Name}}{{end}}">
{{svg "octicon-rss"}}&nbsp;{{ctx.Locale.Tr "activities"}}
</a>
@@ -98,7 +98,7 @@
{{end}}
<div class="item">
<a class="ui primary basic button" href="{{.ContextUser.HomeLink}}" title="{{ctx.Locale.Tr "home.view_home" .ContextUser.Name}}">
- {{ctx.Locale.Tr "home.view_home" (.ContextUser.ShortName 40)}}
+ {{ctx.Locale.Tr "home.view_home" (.ContextUser.ShortName 20)}}
</a>
</div>
</div>
diff --git a/templates/user/dashboard/repolist.tmpl b/templates/user/dashboard/repolist.tmpl
index a2764ba608..8b0fcbb401 100644
--- a/templates/user/dashboard/repolist.tmpl
+++ b/templates/user/dashboard/repolist.tmpl
@@ -5,6 +5,10 @@ const data = {
isMirrorsEnabled: {{.MirrorsEnabled}},
isStarsEnabled: {{not .IsDisableStars}},
+ canCreateMigrations: {{not .DisableMigrations}},
+
+ textNoOrg: {{ctx.Locale.Tr "home.empty_org"}},
+ textNoRepo: {{ctx.Locale.Tr "home.empty_repo"}},
textRepository: {{ctx.Locale.Tr "repository"}},
textOrganization: {{ctx.Locale.Tr "organization"}},
textMyRepos: {{ctx.Locale.Tr "home.my_repos"}},
diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl
index 0d2371a358..b8655a84a4 100644
--- a/templates/user/notification/notification_div.tmpl
+++ b/templates/user/notification/notification_div.tmpl
@@ -1,6 +1,6 @@
<div role="main" aria-label="{{.Title}}" class="page-content user notification" id="notification_div" data-sequence-number="{{.SequenceNumber}}">
<div class="ui container">
- {{$notificationUnreadCount := call .NotificationUnreadCount}}
+ {{$notificationUnreadCount := call .PageGlobalData.GetNotificationUnreadCount}}
<div class="tw-flex tw-items-center tw-justify-between tw-mb-[--page-spacing]">
<div class="small-menu-items ui compact tiny menu">
<a class="{{if eq .Status 1}}active {{end}}item" href="{{AppSubUrl}}/notifications?q=unread">
diff --git a/templates/user/notification/notification_subscriptions.tmpl b/templates/user/notification/notification_subscriptions.tmpl
index b92a32a957..e4dd27e63b 100644
--- a/templates/user/notification/notification_subscriptions.tmpl
+++ b/templates/user/notification/notification_subscriptions.tmpl
@@ -28,7 +28,7 @@
</div>
</div>
<div class="tw-flex tw-justify-between">
- <div class="ui right aligned secondary filter menu labels">
+ <div class="ui secondary filter menu labels">
<!-- Type -->
<div class="ui dropdown type jump item">
<span class="text">
@@ -69,8 +69,8 @@
{{template "shared/issuelist" dict "." . "listType" "dashboard"}}
{{end}}
{{else}}
- {{template "shared/repo_search" .}}
- {{template "explore/repo_list" .}}
+ {{template "shared/repo/search" .}}
+ {{template "shared/repo/list" .}}
{{template "base/paginate" .}}
{{end}}
</div>
diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl
index 2c83ce97cd..74a53b937d 100644
--- a/templates/user/profile.tmpl
+++ b/templates/user/profile.tmpl
@@ -17,8 +17,8 @@
{{template "user/dashboard/feeds" .}}
{{else if eq .TabName "stars"}}
<div class="stars">
- {{template "shared/repo_search" .}}
- {{template "explore/repo_list" .}}
+ {{template "shared/repo/search" .}}
+ {{template "shared/repo/list" .}}
{{template "base/paginate" .}}
</div>
{{else if eq .TabName "following"}}
@@ -26,12 +26,12 @@
{{else if eq .TabName "followers"}}
{{template "repo/user_cards" .}}
{{else if eq .TabName "overview"}}
- <div id="readme_profile" class="markup">{{.ProfileReadme}}</div>
+ <div id="readme_profile" class="render-content markup">{{.ProfileReadmeContent}}</div>
{{else if eq .TabName "organizations"}}
{{template "repo/user_cards" .}}
{{else}}
- {{template "shared/repo_search" .}}
- {{template "explore/repo_list" .}}
+ {{template "shared/repo/search" .}}
+ {{template "shared/repo/list" .}}
{{template "base/paginate" .}}
{{end}}
</div>
diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl
index 27b0ef10c7..2c01c88d47 100644
--- a/templates/user/settings/account.tmpl
+++ b/templates/user/settings/account.tmpl
@@ -43,11 +43,11 @@
<div class="ui list flex-items-block">
{{if $.EnableNotifyMail}}
<div class="item">
- <div class="tw-mb-2">{{ctx.Locale.Tr "settings.email_desc"}}</div>
- <form action="{{AppSubUrl}}/user/settings/account/email" class="ui form" method="post">
+ <form class="ui form tw-w-full" action="{{AppSubUrl}}/user/settings/account/email" method="post">
{{$.CsrfTokenHtml}}
<input name="_method" type="hidden" value="NOTIFICATION">
- <div class="tw-flex tw-flex-wrap tw-gap-2">
+ <div class="field">
+ <label>{{ctx.Locale.Tr "settings.email_desc"}}</label>
<div class="ui selection dropdown">
<input name="preference" type="hidden" value="{{.EmailNotificationsPreference}}">
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
@@ -59,6 +59,8 @@
<div data-value="disabled" class="{{if eq .EmailNotificationsPreference "disabled"}}active selected {{end}}item">{{ctx.Locale.Tr "settings.email_notifications.disable"}}</div>
</div>
</div>
+ </div>
+ <div class="field">
<button class="ui primary button">{{ctx.Locale.Tr "settings.email_notifications.submit"}}</button>
</div>
</form>
diff --git a/templates/user/settings/appearance.tmpl b/templates/user/settings/appearance.tmpl
index 4fa248910a..362f73bcb8 100644
--- a/templates/user/settings/appearance.tmpl
+++ b/templates/user/settings/appearance.tmpl
@@ -18,7 +18,7 @@
<label>{{ctx.Locale.Tr "settings.ui"}}</label>
<select name="theme" class="ui dropdown">
{{range $theme := .AllThemes}}
- <option value="{{$theme}}" {{Iif (eq $.SignedUser.Theme $theme) "selected"}}>{{$theme}}</option>
+ <option value="{{$theme.InternalName}}" {{Iif (eq $.SignedUser.Theme $theme.InternalName) "selected"}}>{{$theme.DisplayName}}</option>
{{end}}
</select>
</div>
diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl
index 31d1a2ac5b..8c24da7fc9 100644
--- a/templates/user/settings/applications.tmpl
+++ b/templates/user/settings/applications.tmpl
@@ -50,49 +50,41 @@
</div>
</div>
<div class="ui bottom attached segment">
- <h5 class="ui top header">
- {{ctx.Locale.Tr "settings.generate_new_token"}}
- </h5>
- <form id="scoped-access-form" class="ui form ignore-dirty" action="{{.Link}}" method="post">
- {{.CsrfTokenHtml}}
- <div class="field {{if .Err_Name}}error{{end}}">
- <label for="name">{{ctx.Locale.Tr "settings.token_name"}}</label>
- <input id="name" name="name" value="{{.name}}" autofocus required maxlength="255">
- </div>
- <div class="field">
- <label>{{ctx.Locale.Tr "settings.repo_and_org_access"}}</label>
- <label class="tw-cursor-pointer">
- <input class="enable-system tw-mt-1 tw-mr-1" type="radio" name="scope" value="{{$.AccessTokenScopePublicOnly}}">
- {{ctx.Locale.Tr "settings.permissions_public_only"}}
- </label>
- <label class="tw-cursor-pointer">
- <input class="enable-system tw-mt-1 tw-mr-1" type="radio" name="scope" value="" checked>
- {{ctx.Locale.Tr "settings.permissions_access_all"}}
- </label>
- </div>
- <details class="ui optional field">
- <summary class="tw-pb-4 tw-pl-1">
- {{ctx.Locale.Tr "settings.select_permissions"}}
- </summary>
- <p class="activity meta">
- <i>{{ctx.Locale.Tr "settings.access_token_desc" (HTMLFormat `href="%s/api/swagger" target="_blank"` AppSubUrl) (`href="https://docs.gitea.com/development/oauth2-provider#scopes" target="_blank"`|SafeHTML)}}</i>
- </p>
- <div id="scoped-access-token-selector"
- data-is-admin="{{if .IsAdmin}}true{{else}}false{{end}}"
- data-no-access-label="{{ctx.Locale.Tr "settings.permission_no_access"}}"
- data-read-label="{{ctx.Locale.Tr "settings.permission_read"}}"
- data-write-label="{{ctx.Locale.Tr "settings.permission_write"}}"
- data-locale-component-failed-to-load="{{ctx.Locale.Tr "graphs.component_failed_to_load"}}"
- >
+ <details {{if or .name (not .Tokens)}}open{{end}}>
+ <summary><h4 class="ui header tw-inline-block tw-my-2">{{ctx.Locale.Tr "settings.generate_new_token"}}</h4></summary>
+ <form class="ui form ignore-dirty" action="{{.Link}}" method="post">
+ {{.CsrfTokenHtml}}
+ <div class="field {{if .Err_Name}}error{{end}}">
+ <label for="name">{{ctx.Locale.Tr "settings.token_name"}}</label>
+ <input id="name" name="name" value="{{.name}}" required maxlength="255">
</div>
- </details>
- <button id="scoped-access-submit" class="ui primary button">
- {{ctx.Locale.Tr "settings.generate_token"}}
- </button>
- </form>{{/* Fomantic ".ui.form .warning.message" is hidden by default, so put the warning message out of the form*/}}
- <div id="scoped-access-warning" class="ui warning message center tw-hidden">
- {{ctx.Locale.Tr "settings.at_least_one_permission"}}
- </div>
+ <div class="field">
+ <div class="tw-my-2">{{ctx.Locale.Tr "settings.repo_and_org_access"}}</div>
+ <label class="gt-checkbox">
+ <input type="radio" name="scope-public-only" value="{{$.AccessTokenScopePublicOnly}}"> {{ctx.Locale.Tr "settings.permissions_public_only"}}
+ </label>
+ <label class="gt-checkbox">
+ <input type="radio" name="scope-public-only" value="" checked> {{ctx.Locale.Tr "settings.permissions_access_all"}}
+ </label>
+ </div>
+ <div>
+ <div class="tw-my-2">{{ctx.Locale.Tr "settings.access_token_desc" (HTMLFormat `href="%s/api/swagger" target="_blank"` AppSubUrl) (HTMLFormat `href="%s" target="_blank"` "https://docs.gitea.com/development/oauth2-provider#scopes")}}</div>
+ <table class="ui table unstackable tw-my-2">
+ {{range $category := .TokenCategories}}
+ <tr>
+ <td>{{$category}}</td>
+ <td><label class="gt-checkbox"><input type="radio" name="scope-{{$category}}" value="" checked> {{ctx.Locale.Tr "settings.permission_no_access"}}</label></td>
+ <td><label class="gt-checkbox"><input type="radio" name="scope-{{$category}}" value="read:{{$category}}"> {{ctx.Locale.Tr "settings.permission_read"}}</label></td>
+ <td><label class="gt-checkbox"><input type="radio" name="scope-{{$category}}" value="write:{{$category}}"> {{ctx.Locale.Tr "settings.permission_write"}}</label></td>
+ </tr>
+ {{end}}
+ </table>
+ </div>
+ <button class="ui primary button">
+ {{ctx.Locale.Tr "settings.generate_token"}}
+ </button>
+ </form>
+ </details>
</div>
{{if .EnableOAuth2}}
diff --git a/templates/user/settings/applications_oauth2_list.tmpl b/templates/user/settings/applications_oauth2_list.tmpl
index 61098e118b..418d8e9cfc 100644
--- a/templates/user/settings/applications_oauth2_list.tmpl
+++ b/templates/user/settings/applications_oauth2_list.tmpl
@@ -48,33 +48,33 @@
</div>
<div class="ui bottom attached segment">
- <h5 class="ui top header">
- {{ctx.Locale.Tr "settings.create_oauth2_application"}}
- </h5>
- <form class="ui form ignore-dirty" action="{{.Link}}/oauth2" method="post">
- {{.CsrfTokenHtml}}
- <div class="field {{if .Err_AppName}}error{{end}}">
- <label for="application-name">{{ctx.Locale.Tr "settings.oauth2_application_name"}}</label>
- <input id="application-name" name="application_name" value="{{.application_name}}" required maxlength="255">
- </div>
- <div class="field {{if .Err_RedirectURI}}error{{end}}">
- <label for="redirect-uris">{{ctx.Locale.Tr "settings.oauth2_redirect_uris"}}</label>
- <textarea name="redirect_uris" id="redirect-uris"></textarea>
- </div>
- <div class="field {{if .Err_ConfidentialClient}}error{{end}}">
- <div class="ui checkbox">
- <label>{{ctx.Locale.Tr "settings.oauth2_confidential_client"}}</label>
- <input class="disable-setting" type="checkbox" name="confidential_client" data-target="#skip-secondary-authorization" checked>
+ <details {{if .application_name}}open{{end}}>
+ <summary><h4 class="ui header tw-inline-block tw-my-2">{{ctx.Locale.Tr "settings.create_oauth2_application"}}</h4></summary>
+ <form class="ui form ignore-dirty" action="{{.Link}}/oauth2" method="post">
+ {{.CsrfTokenHtml}}
+ <div class="field {{if .Err_AppName}}error{{end}}">
+ <label for="application-name">{{ctx.Locale.Tr "settings.oauth2_application_name"}}</label>
+ <input id="application-name" name="application_name" value="{{.application_name}}" required maxlength="255">
</div>
- </div>
- <div class="field {{if .Err_SkipSecondaryAuthorization}}error{{end}} disabled" id="skip-secondary-authorization">
- <div class="ui checkbox">
- <label>{{ctx.Locale.Tr "settings.oauth2_skip_secondary_authorization"}}</label>
- <input type="checkbox" name="skip_secondary_authorization">
+ <div class="field {{if .Err_RedirectURI}}error{{end}}">
+ <label for="redirect-uris">{{ctx.Locale.Tr "settings.oauth2_redirect_uris"}}</label>
+ <textarea name="redirect_uris" id="redirect-uris"></textarea>
</div>
- </div>
- <button class="ui primary button">
- {{ctx.Locale.Tr "settings.create_oauth2_application_button"}}
- </button>
- </form>
+ <div class="field {{if .Err_ConfidentialClient}}error{{end}}">
+ <div class="ui checkbox">
+ <label>{{ctx.Locale.Tr "settings.oauth2_confidential_client"}}</label>
+ <input class="disable-setting" type="checkbox" name="confidential_client" data-target="#skip-secondary-authorization" checked>
+ </div>
+ </div>
+ <div class="field {{if .Err_SkipSecondaryAuthorization}}error{{end}} disabled" id="skip-secondary-authorization">
+ <div class="ui checkbox">
+ <label>{{ctx.Locale.Tr "settings.oauth2_skip_secondary_authorization"}}</label>
+ <input type="checkbox" name="skip_secondary_authorization">
+ </div>
+ </div>
+ <button class="ui primary button">
+ {{ctx.Locale.Tr "settings.create_oauth2_application_button"}}
+ </button>
+ </form>
+ </details>
</div>
diff --git a/templates/user/settings/hooks.tmpl b/templates/user/settings/hooks.tmpl
index 477c333220..e2d18001f2 100644
--- a/templates/user/settings/hooks.tmpl
+++ b/templates/user/settings/hooks.tmpl
@@ -1,5 +1,5 @@
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings webhooks")}}
<div class="user-setting-content">
- {{template "repo/settings/webhook/list" .}}
+ {{template "repo/settings/webhook/base_list" .}}
</div>
{{template "user/settings/layout_footer" .}}
diff --git a/templates/user/settings/keys_ssh.tmpl b/templates/user/settings/keys_ssh.tmpl
index b894ccdfbd..9d62d4ab08 100644
--- a/templates/user/settings/keys_ssh.tmpl
+++ b/templates/user/settings/keys_ssh.tmpl
@@ -78,7 +78,16 @@
<input readonly="" value="{{$.TokenToSign}}">
<div class="help">
<p>{{ctx.Locale.Tr "settings.ssh_token_help"}}</p>
- <p><code>{{printf "echo -n '%s' | ssh-keygen -Y sign -n gitea -f /path_to_PrivateKey_or_RelatedPublicKey" $.TokenToSign}}</code></p>
+ <p><code>echo -n '{{$.TokenToSign}}' | ssh-keygen -Y sign -n gitea -f /path_to_PrivateKey_or_RelatedPublicKey</code></p>
+ <details>
+ <summary>Windows PowerShell</summary>
+ <p><code>cmd /c "&lt;NUL set /p=`"{{$.TokenToSign}}`"| ssh-keygen -Y sign -n gitea -f /path_to_PrivateKey_or_RelatedPublicKey"</code></p>
+ </details>
+ <br>
+ <details>
+ <summary>Windows CMD</summary>
+ <p><code>set /p={{$.TokenToSign}}| ssh-keygen -Y sign -n gitea -f /path_to_PrivateKey_or_RelatedPublicKey</code></p>
+ </details>
</div>
<br>
</div>
diff --git a/templates/user/settings/organization.tmpl b/templates/user/settings/organization.tmpl
index 16c27b52cd..a48ca9ec9b 100644
--- a/templates/user/settings/organization.tmpl
+++ b/templates/user/settings/organization.tmpl
@@ -47,7 +47,7 @@
{{ctx.Locale.Tr "org.members.leave"}}
</div>
<div class="content">
- <p>{{ctx.Locale.Tr "org.members.leave.detail" (`<span class="dataOrganizationName"></span>`|SafeHTML)}}</p>
+ <p>{{ctx.Locale.Tr "org.members.leave.detail" (HTMLFormat `<span class="%s"></span>` "dataOrganizationName")}}</p>
</div>
{{template "base/modal_actions_confirm" .}}
</div>
diff --git a/templates/user/settings/profile.tmpl b/templates/user/settings/profile.tmpl
index 197763425c..d8e5e27b89 100644
--- a/templates/user/settings/profile.tmpl
+++ b/templates/user/settings/profile.tmpl
@@ -12,7 +12,7 @@
<span class="text red tw-hidden" id="name-change-prompt"> {{ctx.Locale.Tr "settings.change_username_prompt"}}</span>
<span class="text red tw-hidden" id="name-change-redirect-prompt"> {{ctx.Locale.Tr "settings.change_username_redirect_prompt"}}</span>
</label>
- <input id="username" name="name" value="{{.SignedUser.Name}}" data-name="{{.SignedUser.Name}}" autofocus required {{if or (not .SignedUser.IsLocal) ($.UserDisabledFeatures.Contains "change_username") .IsReverseProxy}}disabled{{end}} maxlength="40">
+ <input id="username" name="name" value="{{.SignedUser.Name}}" data-name="{{.SignedUser.Name}}" required {{if or (not .SignedUser.IsLocal) ($.UserDisabledFeatures.Contains "change_username") .IsReverseProxy}}disabled{{end}} maxlength="40">
{{if or (not .SignedUser.IsLocal) ($.UserDisabledFeatures.Contains "change_username") .IsReverseProxy}}
<p class="help text blue">{{ctx.Locale.Tr "settings.password_username_disabled"}}</p>
{{end}}
@@ -124,13 +124,7 @@
</div>
<div class="inline field tw-pl-4">
- <label for="new-avatar">{{ctx.Locale.Tr "settings.choose_new_avatar"}}</label>
- <input id="new-avatar" name="avatar" type="file" accept="image/png,image/jpeg,image/gif,image/webp">
- </div>
-
- <div class="field tw-pl-4 cropper-panel tw-hidden">
- <div>{{ctx.Locale.Tr "settings.cropper_prompt"}}</div>
- <div class="cropper-wrapper"><img class="cropper-source" src alt></div>
+ {{template "shared/avatar_upload_crop" dict "LabelText" (ctx.Locale.Tr "settings.choose_new_avatar")}}
</div>
<div class="field">
diff --git a/templates/user/settings/repos.tmpl b/templates/user/settings/repos.tmpl
index a50fb586c7..4aed8070de 100644
--- a/templates/user/settings/repos.tmpl
+++ b/templates/user/settings/repos.tmpl
@@ -81,7 +81,7 @@
{{end}}
{{else}}
{{if .Repos}}
- <div class="ui middle aligned divided list">
+ <div class="ui list">
{{range .Repos}}
<div class="item">
<div class="content flex-text-block">
diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go
index ece136be50..4a408dbd7b 100644
--- a/tests/e2e/e2e_test.go
+++ b/tests/e2e/e2e_test.go
@@ -4,7 +4,7 @@
// This is primarily coped from /tests/integration/integration_test.go
// TODO: Move common functions to shared file
-//nolint:forbidigo
+//nolint:forbidigo // use of print functions is allowed in tests
package e2e
import (
@@ -94,7 +94,7 @@ func TestE2e(t *testing.T) {
onGiteaRun(t, func(*testing.T, *url.URL) {
cmd := exec.Command(runArgs[0], runArgs...)
cmd.Env = os.Environ()
- cmd.Env = append(cmd.Env, fmt.Sprintf("GITEA_URL=%s", setting.AppURL))
+ cmd.Env = append(cmd.Env, "GITEA_URL="+setting.AppURL)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
diff --git a/tests/e2e/utils_e2e.ts b/tests/e2e/utils_e2e.ts
index 14ec836600..3e92e0d3c2 100644
--- a/tests/e2e/utils_e2e.ts
+++ b/tests/e2e/utils_e2e.ts
@@ -1,12 +1,13 @@
import {expect} from '@playwright/test';
import {env} from 'node:process';
+import type {Browser, Page, WorkerInfo} from '@playwright/test';
const ARTIFACTS_PATH = `tests/e2e/test-artifacts`;
const LOGIN_PASSWORD = 'password';
// log in user and store session info. This should generally be
// run in test.beforeAll(), then the session can be loaded in tests.
-export async function login_user(browser, workerInfo, user) {
+export async function login_user(browser: Browser, workerInfo: WorkerInfo, user: string) {
// Set up a new context
const context = await browser.newContext();
const page = await context.newPage();
@@ -17,8 +18,8 @@ export async function login_user(browser, workerInfo, user) {
expect(response?.status()).toBe(200); // Status OK
// Fill out form
- await page.type('input[name=user_name]', user);
- await page.type('input[name=password]', LOGIN_PASSWORD);
+ await page.locator('input[name=user_name]').fill(user);
+ await page.locator('input[name=password]').fill(LOGIN_PASSWORD);
await page.click('form button.ui.primary.button:visible');
await page.waitForLoadState('networkidle'); // eslint-disable-line playwright/no-networkidle
@@ -31,7 +32,7 @@ export async function login_user(browser, workerInfo, user) {
return context;
}
-export async function load_logged_in_context(browser, workerInfo, user) {
+export async function load_logged_in_context(browser: Browser, workerInfo: WorkerInfo, user: string) {
let context;
try {
context = await browser.newContext({storageState: `${ARTIFACTS_PATH}/state-${user}-${workerInfo.workerIndex}.json`});
@@ -43,7 +44,7 @@ export async function load_logged_in_context(browser, workerInfo, user) {
return context;
}
-export async function save_visual(page) {
+export async function save_visual(page: Page) {
// Optionally include visual testing
if (env.VISUAL_TEST) {
await page.waitForLoadState('networkidle'); // eslint-disable-line playwright/no-networkidle
diff --git a/tests/e2e/utils_e2e_test.go b/tests/e2e/utils_e2e_test.go
index efb4a6ba88..5ba05f3453 100644
--- a/tests/e2e/utils_e2e_test.go
+++ b/tests/e2e/utils_e2e_test.go
@@ -38,7 +38,7 @@ func onGiteaRunTB(t testing.TB, callback func(testing.TB, *url.URL), prepare ...
u.Host = listener.Addr().String()
defer func() {
- ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
+ ctx, cancel := context.WithTimeout(t.Context(), 2*time.Minute)
s.Shutdown(ctx)
cancel()
}()
diff --git a/tests/fuzz/fuzz_test.go b/tests/fuzz/fuzz_test.go
index 01d562d995..e7b832412a 100644
--- a/tests/fuzz/fuzz_test.go
+++ b/tests/fuzz/fuzz_test.go
@@ -19,6 +19,7 @@ func newFuzzRenderContext() *markup.RenderContext {
func FuzzMarkdownRenderRaw(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
+ setting.IsInTesting = true
setting.AppURL = "http://localhost:3000/"
markdown.RenderRaw(newFuzzRenderContext(), bytes.NewReader(data), io.Discard)
})
@@ -26,6 +27,7 @@ func FuzzMarkdownRenderRaw(f *testing.F) {
func FuzzMarkupPostProcess(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
+ setting.IsInTesting = true
setting.AppURL = "http://localhost:3000/"
markup.PostProcessDefault(newFuzzRenderContext(), bytes.NewReader(data), io.Discard)
})
diff --git a/tests/gitea-repositories-meta/user2/test_commit_revert.git/HEAD b/tests/gitea-repositories-meta/user2/test_commit_revert.git/HEAD
deleted file mode 100644
index b870d82622..0000000000
--- a/tests/gitea-repositories-meta/user2/test_commit_revert.git/HEAD
+++ /dev/null
@@ -1 +0,0 @@
-ref: refs/heads/main
diff --git a/tests/gitea-repositories-meta/user2/test_commit_revert.git/config b/tests/gitea-repositories-meta/user2/test_commit_revert.git/config
deleted file mode 100644
index 57bbcba5be..0000000000
--- a/tests/gitea-repositories-meta/user2/test_commit_revert.git/config
+++ /dev/null
@@ -1,8 +0,0 @@
-[core]
- repositoryformatversion = 0
- filemode = true
- bare = true
- ignorecase = true
- precomposeunicode = true
-[remote "origin"]
- url = https://try.gitea.io/me-heer/test_commit_revert.git
diff --git a/tests/gitea-repositories-meta/user2/test_commit_revert.git/objects/pack/pack-91200c8e6707636a6cc3e0d8101fba08b19dcb91.idx b/tests/gitea-repositories-meta/user2/test_commit_revert.git/objects/pack/pack-91200c8e6707636a6cc3e0d8101fba08b19dcb91.idx
deleted file mode 100644
index 77bcbe7fb4..0000000000
--- a/tests/gitea-repositories-meta/user2/test_commit_revert.git/objects/pack/pack-91200c8e6707636a6cc3e0d8101fba08b19dcb91.idx
+++ /dev/null
Binary files differ
diff --git a/tests/gitea-repositories-meta/user2/test_commit_revert.git/objects/pack/pack-91200c8e6707636a6cc3e0d8101fba08b19dcb91.pack b/tests/gitea-repositories-meta/user2/test_commit_revert.git/objects/pack/pack-91200c8e6707636a6cc3e0d8101fba08b19dcb91.pack
deleted file mode 100644
index 7271cdaeb8..0000000000
--- a/tests/gitea-repositories-meta/user2/test_commit_revert.git/objects/pack/pack-91200c8e6707636a6cc3e0d8101fba08b19dcb91.pack
+++ /dev/null
Binary files differ
diff --git a/tests/gitea-repositories-meta/user2/test_commit_revert.git/packed-refs b/tests/gitea-repositories-meta/user2/test_commit_revert.git/packed-refs
deleted file mode 100644
index 1f546d7fd5..0000000000
--- a/tests/gitea-repositories-meta/user2/test_commit_revert.git/packed-refs
+++ /dev/null
@@ -1,3 +0,0 @@
-# pack-refs with: peeled fully-peeled sorted
-46aa6ab2c881ae90e15d9ccfc947d1625c892ce5 refs/heads/develop
-deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7 refs/heads/main
diff --git a/tests/gitea-repositories-meta/user2/test_commit_revert.git/refs/heads/main b/tests/gitea-repositories-meta/user2/test_commit_revert.git/refs/heads/main
deleted file mode 100644
index ab80ca3ca6..0000000000
--- a/tests/gitea-repositories-meta/user2/test_commit_revert.git/refs/heads/main
+++ /dev/null
@@ -1 +0,0 @@
-deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7
diff --git a/tests/integration/actions_delete_run_test.go b/tests/integration/actions_delete_run_test.go
new file mode 100644
index 0000000000..22f9a1f740
--- /dev/null
+++ b/tests/integration/actions_delete_run_test.go
@@ -0,0 +1,181 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "fmt"
+ "net/http"
+ "net/url"
+ "testing"
+ "time"
+
+ actions_model "code.gitea.io/gitea/models/actions"
+ auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/routers/web/repo/actions"
+
+ runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
+ "github.com/stretchr/testify/assert"
+ "google.golang.org/protobuf/types/known/timestamppb"
+)
+
+func TestActionsDeleteRun(t *testing.T) {
+ now := time.Now()
+ testCase := struct {
+ treePath string
+ fileContent string
+ outcomes map[string]*mockTaskOutcome
+ expectedStatuses map[string]string
+ }{
+ treePath: ".gitea/workflows/test1.yml",
+ fileContent: `name: test1
+on:
+ push:
+ paths:
+ - .gitea/workflows/test1.yml
+jobs:
+ job1:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo job1
+ job2:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo job2
+ job3:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo job3
+`,
+ outcomes: map[string]*mockTaskOutcome{
+ "job1": {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ logRows: []*runnerv1.LogRow{
+ {
+ Time: timestamppb.New(now.Add(4 * time.Second)),
+ Content: " \U0001F433 docker create image",
+ },
+ {
+ Time: timestamppb.New(now.Add(5 * time.Second)),
+ Content: "job1",
+ },
+ {
+ Time: timestamppb.New(now.Add(6 * time.Second)),
+ Content: "\U0001F3C1 Job succeeded",
+ },
+ },
+ },
+ "job2": {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ logRows: []*runnerv1.LogRow{
+ {
+ Time: timestamppb.New(now.Add(4 * time.Second)),
+ Content: " \U0001F433 docker create image",
+ },
+ {
+ Time: timestamppb.New(now.Add(5 * time.Second)),
+ Content: "job2",
+ },
+ {
+ Time: timestamppb.New(now.Add(6 * time.Second)),
+ Content: "\U0001F3C1 Job succeeded",
+ },
+ },
+ },
+ "job3": {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ logRows: []*runnerv1.LogRow{
+ {
+ Time: timestamppb.New(now.Add(4 * time.Second)),
+ Content: " \U0001F433 docker create image",
+ },
+ {
+ Time: timestamppb.New(now.Add(5 * time.Second)),
+ Content: "job3",
+ },
+ {
+ Time: timestamppb.New(now.Add(6 * time.Second)),
+ Content: "\U0001F3C1 Job succeeded",
+ },
+ },
+ },
+ },
+ expectedStatuses: map[string]string{
+ "job1": actions_model.StatusSuccess.String(),
+ "job2": actions_model.StatusSuccess.String(),
+ "job3": actions_model.StatusSuccess.String(),
+ },
+ }
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ session := loginUser(t, user2.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+
+ apiRepo := createActionsTestRepo(t, token, "actions-delete-run-test", false)
+ runner := newMockRunner()
+ runner.registerAsRepoRunner(t, user2.Name, apiRepo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
+
+ opts := getWorkflowCreateFileOptions(user2, apiRepo.DefaultBranch, "create "+testCase.treePath, testCase.fileContent)
+ createWorkflowFile(t, token, user2.Name, apiRepo.Name, testCase.treePath, opts)
+
+ runIndex := ""
+ for i := 0; i < len(testCase.outcomes); i++ {
+ task := runner.fetchTask(t)
+ jobName := getTaskJobNameByTaskID(t, token, user2.Name, apiRepo.Name, task.Id)
+ outcome := testCase.outcomes[jobName]
+ assert.NotNil(t, outcome)
+ runner.execTask(t, task, outcome)
+ runIndex = task.Context.GetFields()["run_number"].GetStringValue()
+ assert.Equal(t, "1", runIndex)
+ }
+
+ for i := 0; i < len(testCase.outcomes); i++ {
+ req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/actions/runs/%s/jobs/%d", user2.Name, apiRepo.Name, runIndex, i), map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ })
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ var listResp actions.ViewResponse
+ err := json.Unmarshal(resp.Body.Bytes(), &listResp)
+ assert.NoError(t, err)
+ assert.Len(t, listResp.State.Run.Jobs, 3)
+
+ req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s/actions/runs/%s/jobs/%d/logs", user2.Name, apiRepo.Name, runIndex, i)).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusOK)
+ }
+
+ req := NewRequestWithValues(t, "GET", fmt.Sprintf("/%s/%s/actions/runs/%s", user2.Name, apiRepo.Name, runIndex), map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ })
+ session.MakeRequest(t, req, http.StatusOK)
+
+ req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/actions/runs/%s/delete", user2.Name, apiRepo.Name, runIndex), map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ })
+ session.MakeRequest(t, req, http.StatusOK)
+
+ req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/actions/runs/%s/delete", user2.Name, apiRepo.Name, runIndex), map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ })
+ session.MakeRequest(t, req, http.StatusNotFound)
+
+ req = NewRequestWithValues(t, "GET", fmt.Sprintf("/%s/%s/actions/runs/%s", user2.Name, apiRepo.Name, runIndex), map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ })
+ session.MakeRequest(t, req, http.StatusNotFound)
+
+ for i := 0; i < len(testCase.outcomes); i++ {
+ req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/actions/runs/%s/jobs/%d", user2.Name, apiRepo.Name, runIndex, i), map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ })
+ session.MakeRequest(t, req, http.StatusNotFound)
+
+ req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s/actions/runs/%s/jobs/%d/logs", user2.Name, apiRepo.Name, runIndex, i)).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNotFound)
+ }
+ })
+}
diff --git a/tests/integration/actions_job_test.go b/tests/integration/actions_job_test.go
index e13277678d..4f4456a4e5 100644
--- a/tests/integration/actions_job_test.go
+++ b/tests/integration/actions_job_test.go
@@ -8,16 +8,24 @@ import (
"fmt"
"net/http"
"net/url"
+ "reflect"
+ "strconv"
"testing"
"time"
actions_model "code.gitea.io/gitea/models/actions"
auth_model "code.gitea.io/gitea/models/auth"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ actions_service "code.gitea.io/gitea/services/actions"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
+ "connectrpc.com/connect"
"github.com/stretchr/testify/assert"
)
@@ -31,7 +39,7 @@ func TestJobWithNeeds(t *testing.T) {
{
treePath: ".gitea/workflows/job-with-needs.yml",
fileContent: `name: job-with-needs
-on:
+on:
push:
paths:
- '.gitea/workflows/job-with-needs.yml'
@@ -62,7 +70,7 @@ jobs:
{
treePath: ".gitea/workflows/job-with-needs-fail.yml",
fileContent: `name: job-with-needs-fail
-on:
+on:
push:
paths:
- '.gitea/workflows/job-with-needs-fail.yml'
@@ -90,7 +98,7 @@ jobs:
{
treePath: ".gitea/workflows/job-with-needs-fail-if.yml",
fileContent: `name: job-with-needs-fail-if
-on:
+on:
push:
paths:
- '.gitea/workflows/job-with-needs-fail-if.yml'
@@ -127,12 +135,12 @@ jobs:
apiRepo := createActionsTestRepo(t, token, "actions-jobs-with-needs", false)
runner := newMockRunner()
- runner.registerAsRepoRunner(t, user2.Name, apiRepo.Name, "mock-runner", []string{"ubuntu-latest"})
+ runner.registerAsRepoRunner(t, user2.Name, apiRepo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
for _, tc := range testCases {
- t.Run(fmt.Sprintf("test %s", tc.treePath), func(t *testing.T) {
+ t.Run("test "+tc.treePath, func(t *testing.T) {
// create the workflow file
- opts := getWorkflowCreateFileOptions(user2, apiRepo.DefaultBranch, fmt.Sprintf("create %s", tc.treePath), tc.fileContent)
+ opts := getWorkflowCreateFileOptions(user2, apiRepo.DefaultBranch, "create "+tc.treePath, tc.fileContent)
fileResp := createWorkflowFile(t, token, user2.Name, apiRepo.Name, tc.treePath, opts)
// fetch and execute task
@@ -159,9 +167,6 @@ jobs:
}
})
}
-
- httpContext := NewAPITestContext(t, user2.Name, apiRepo.Name, auth_model.AccessTokenScopeWriteRepository)
- doAPIDeleteRepository(httpContext)(t)
})
}
@@ -175,7 +180,7 @@ func TestJobNeedsMatrix(t *testing.T) {
{
treePath: ".gitea/workflows/jobs-outputs-with-matrix.yml",
fileContent: `name: jobs-outputs-with-matrix
-on:
+on:
push:
paths:
- '.gitea/workflows/jobs-outputs-with-matrix.yml'
@@ -194,7 +199,7 @@ jobs:
id: gen_output
run: |
version="${{ matrix.version }}"
- echo "output_${version}=${version}" >> "$GITHUB_OUTPUT"
+ echo "output_${version}=${version}" >> "$GITHUB_OUTPUT"
job2:
runs-on: ubuntu-latest
needs: [job1]
@@ -241,7 +246,7 @@ jobs:
{
treePath: ".gitea/workflows/jobs-outputs-with-matrix-failure.yml",
fileContent: `name: jobs-outputs-with-matrix-failure
-on:
+on:
push:
paths:
- '.gitea/workflows/jobs-outputs-with-matrix-failure.yml'
@@ -260,7 +265,7 @@ jobs:
id: gen_output
run: |
version="${{ matrix.version }}"
- echo "output_${version}=${version}" >> "$GITHUB_OUTPUT"
+ echo "output_${version}=${version}" >> "$GITHUB_OUTPUT"
job2:
runs-on: ubuntu-latest
if: ${{ always() }}
@@ -313,11 +318,11 @@ jobs:
apiRepo := createActionsTestRepo(t, token, "actions-jobs-outputs-with-matrix", false)
runner := newMockRunner()
- runner.registerAsRepoRunner(t, user2.Name, apiRepo.Name, "mock-runner", []string{"ubuntu-latest"})
+ runner.registerAsRepoRunner(t, user2.Name, apiRepo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
for _, tc := range testCases {
- t.Run(fmt.Sprintf("test %s", tc.treePath), func(t *testing.T) {
- opts := getWorkflowCreateFileOptions(user2, apiRepo.DefaultBranch, fmt.Sprintf("create %s", tc.treePath), tc.fileContent)
+ t.Run("test "+tc.treePath, func(t *testing.T) {
+ opts := getWorkflowCreateFileOptions(user2, apiRepo.DefaultBranch, "create "+tc.treePath, tc.fileContent)
createWorkflowFile(t, token, user2.Name, apiRepo.Name, tc.treePath, opts)
for i := 0; i < len(tc.outcomes); i++ {
@@ -341,9 +346,238 @@ jobs:
}
})
}
+ })
+}
+
+func TestActionsGiteaContext(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ user2Session := loginUser(t, user2.Name)
+ user2Token := getTokenForLoggedInUser(t, user2Session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+
+ apiBaseRepo := createActionsTestRepo(t, user2Token, "actions-gitea-context", false)
+ baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiBaseRepo.ID})
+ user2APICtx := NewAPITestContext(t, baseRepo.OwnerName, baseRepo.Name, auth_model.AccessTokenScopeWriteRepository)
+
+ runner := newMockRunner()
+ runner.registerAsRepoRunner(t, baseRepo.OwnerName, baseRepo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
+
+ // init the workflow
+ wfTreePath := ".gitea/workflows/pull.yml"
+ wfFileContent := `name: Pull Request
+on: pull_request
+jobs:
+ wf1-job:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo 'test the pull'
+`
+ opts := getWorkflowCreateFileOptions(user2, baseRepo.DefaultBranch, "create "+wfTreePath, wfFileContent)
+ createWorkflowFile(t, user2Token, baseRepo.OwnerName, baseRepo.Name, wfTreePath, opts)
+ // user2 creates a pull request
+ doAPICreateFile(user2APICtx, "user2-patch.txt", &api.CreateFileOptions{
+ FileOptions: api.FileOptions{
+ NewBranchName: "user2/patch-1",
+ Message: "create user2-patch.txt",
+ Author: api.Identity{
+ Name: user2.Name,
+ Email: user2.Email,
+ },
+ Committer: api.Identity{
+ Name: user2.Name,
+ Email: user2.Email,
+ },
+ Dates: api.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ },
+ ContentBase64: base64.StdEncoding.EncodeToString([]byte("user2-fix")),
+ })(t)
+ apiPull, err := doAPICreatePullRequest(user2APICtx, baseRepo.OwnerName, baseRepo.Name, baseRepo.DefaultBranch, "user2/patch-1")(t)
+ assert.NoError(t, err)
+ task := runner.fetchTask(t)
+ gtCtx := task.Context.GetFields()
+ actionTask := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: task.Id})
+ actionRunJob := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: actionTask.JobID})
+ actionRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: actionRunJob.RunID})
+ assert.NoError(t, actionRun.LoadAttributes(t.Context()))
+
+ assert.Equal(t, user2.Name, gtCtx["actor"].GetStringValue())
+ assert.Equal(t, setting.AppURL+"api/v1", gtCtx["api_url"].GetStringValue())
+ assert.Equal(t, apiPull.Base.Ref, gtCtx["base_ref"].GetStringValue())
+ runEvent := map[string]any{}
+ assert.NoError(t, json.Unmarshal([]byte(actionRun.EventPayload), &runEvent))
+ assert.True(t, reflect.DeepEqual(gtCtx["event"].GetStructValue().AsMap(), runEvent))
+ assert.Equal(t, actionRun.TriggerEvent, gtCtx["event_name"].GetStringValue())
+ assert.Equal(t, apiPull.Head.Ref, gtCtx["head_ref"].GetStringValue())
+ assert.Equal(t, actionRunJob.JobID, gtCtx["job"].GetStringValue())
+ assert.Equal(t, actionRun.Ref, gtCtx["ref"].GetStringValue())
+ assert.Equal(t, (git.RefName(actionRun.Ref)).ShortName(), gtCtx["ref_name"].GetStringValue())
+ assert.False(t, gtCtx["ref_protected"].GetBoolValue())
+ assert.Equal(t, string((git.RefName(actionRun.Ref)).RefType()), gtCtx["ref_type"].GetStringValue())
+ assert.Equal(t, actionRun.Repo.OwnerName+"/"+actionRun.Repo.Name, gtCtx["repository"].GetStringValue())
+ assert.Equal(t, actionRun.Repo.OwnerName, gtCtx["repository_owner"].GetStringValue())
+ assert.Equal(t, actionRun.Repo.HTMLURL(), gtCtx["repositoryUrl"].GetStringValue())
+ assert.Equal(t, strconv.FormatInt(actionRunJob.RunID, 10), gtCtx["run_id"].GetStringValue())
+ assert.Equal(t, strconv.FormatInt(actionRun.Index, 10), gtCtx["run_number"].GetStringValue())
+ assert.Equal(t, strconv.FormatInt(actionRunJob.Attempt, 10), gtCtx["run_attempt"].GetStringValue())
+ assert.Equal(t, "Actions", gtCtx["secret_source"].GetStringValue())
+ assert.Equal(t, setting.AppURL, gtCtx["server_url"].GetStringValue())
+ assert.Equal(t, actionRun.CommitSHA, gtCtx["sha"].GetStringValue())
+ assert.Equal(t, actionRun.WorkflowID, gtCtx["workflow"].GetStringValue())
+ assert.Equal(t, setting.Actions.DefaultActionsURL.URL(), gtCtx["gitea_default_actions_url"].GetStringValue())
+ token := gtCtx["token"].GetStringValue()
+ assert.Equal(t, actionTask.TokenLastEight, token[len(token)-8:])
+ })
+}
+
+// Ephemeral
+func TestActionsGiteaContextEphemeral(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ user2Session := loginUser(t, user2.Name)
+ user2Token := getTokenForLoggedInUser(t, user2Session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+
+ apiBaseRepo := createActionsTestRepo(t, user2Token, "actions-gitea-context", false)
+ baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiBaseRepo.ID})
+ user2APICtx := NewAPITestContext(t, baseRepo.OwnerName, baseRepo.Name, auth_model.AccessTokenScopeWriteRepository)
+
+ runner := newMockRunner()
+ runner.registerAsRepoRunner(t, baseRepo.OwnerName, baseRepo.Name, "mock-runner", []string{"ubuntu-latest"}, true)
+
+ // verify CleanupEphemeralRunners does not remove this runner
+ err := actions_service.CleanupEphemeralRunners(t.Context())
+ assert.NoError(t, err)
+
+ // init the workflow
+ wfTreePath := ".gitea/workflows/pull.yml"
+ wfFileContent := `name: Pull Request
+on: pull_request
+jobs:
+ wf1-job:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo 'test the pull'
+ wf2-job:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo 'test the pull'
+`
+ opts := getWorkflowCreateFileOptions(user2, baseRepo.DefaultBranch, "create "+wfTreePath, wfFileContent)
+ createWorkflowFile(t, user2Token, baseRepo.OwnerName, baseRepo.Name, wfTreePath, opts)
+ // user2 creates a pull request
+ doAPICreateFile(user2APICtx, "user2-patch.txt", &api.CreateFileOptions{
+ FileOptions: api.FileOptions{
+ NewBranchName: "user2/patch-1",
+ Message: "create user2-patch.txt",
+ Author: api.Identity{
+ Name: user2.Name,
+ Email: user2.Email,
+ },
+ Committer: api.Identity{
+ Name: user2.Name,
+ Email: user2.Email,
+ },
+ Dates: api.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ },
+ ContentBase64: base64.StdEncoding.EncodeToString([]byte("user2-fix")),
+ })(t)
+ apiPull, err := doAPICreatePullRequest(user2APICtx, baseRepo.OwnerName, baseRepo.Name, baseRepo.DefaultBranch, "user2/patch-1")(t)
+ assert.NoError(t, err)
+ task := runner.fetchTask(t)
+ gtCtx := task.Context.GetFields()
+ actionTask := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: task.Id})
+ actionRunJob := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: actionTask.JobID})
+ actionRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: actionRunJob.RunID})
+ assert.NoError(t, actionRun.LoadAttributes(t.Context()))
+
+ assert.Equal(t, user2.Name, gtCtx["actor"].GetStringValue())
+ assert.Equal(t, setting.AppURL+"api/v1", gtCtx["api_url"].GetStringValue())
+ assert.Equal(t, apiPull.Base.Ref, gtCtx["base_ref"].GetStringValue())
+ runEvent := map[string]any{}
+ assert.NoError(t, json.Unmarshal([]byte(actionRun.EventPayload), &runEvent))
+ assert.True(t, reflect.DeepEqual(gtCtx["event"].GetStructValue().AsMap(), runEvent))
+ assert.Equal(t, actionRun.TriggerEvent, gtCtx["event_name"].GetStringValue())
+ assert.Equal(t, apiPull.Head.Ref, gtCtx["head_ref"].GetStringValue())
+ assert.Equal(t, actionRunJob.JobID, gtCtx["job"].GetStringValue())
+ assert.Equal(t, actionRun.Ref, gtCtx["ref"].GetStringValue())
+ assert.Equal(t, (git.RefName(actionRun.Ref)).ShortName(), gtCtx["ref_name"].GetStringValue())
+ assert.False(t, gtCtx["ref_protected"].GetBoolValue())
+ assert.Equal(t, string((git.RefName(actionRun.Ref)).RefType()), gtCtx["ref_type"].GetStringValue())
+ assert.Equal(t, actionRun.Repo.OwnerName+"/"+actionRun.Repo.Name, gtCtx["repository"].GetStringValue())
+ assert.Equal(t, actionRun.Repo.OwnerName, gtCtx["repository_owner"].GetStringValue())
+ assert.Equal(t, actionRun.Repo.HTMLURL(), gtCtx["repositoryUrl"].GetStringValue())
+ assert.Equal(t, strconv.FormatInt(actionRunJob.RunID, 10), gtCtx["run_id"].GetStringValue())
+ assert.Equal(t, strconv.FormatInt(actionRun.Index, 10), gtCtx["run_number"].GetStringValue())
+ assert.Equal(t, strconv.FormatInt(actionRunJob.Attempt, 10), gtCtx["run_attempt"].GetStringValue())
+ assert.Equal(t, "Actions", gtCtx["secret_source"].GetStringValue())
+ assert.Equal(t, setting.AppURL, gtCtx["server_url"].GetStringValue())
+ assert.Equal(t, actionRun.CommitSHA, gtCtx["sha"].GetStringValue())
+ assert.Equal(t, actionRun.WorkflowID, gtCtx["workflow"].GetStringValue())
+ assert.Equal(t, setting.Actions.DefaultActionsURL.URL(), gtCtx["gitea_default_actions_url"].GetStringValue())
+ token := gtCtx["token"].GetStringValue()
+ assert.Equal(t, actionTask.TokenLastEight, token[len(token)-8:])
+
+ // verify CleanupEphemeralRunners does not remove this runner
+ err = actions_service.CleanupEphemeralRunners(t.Context())
+ assert.NoError(t, err)
+
+ resp, err := runner.client.runnerServiceClient.FetchTask(t.Context(), connect.NewRequest(&runnerv1.FetchTaskRequest{
+ TasksVersion: 0,
+ }))
+ assert.NoError(t, err)
+ assert.Nil(t, resp.Msg.Task)
+
+ // verify CleanupEphemeralRunners does not remove this runner
+ err = actions_service.CleanupEphemeralRunners(t.Context())
+ assert.NoError(t, err)
+
+ _, err = runner.client.runnerServiceClient.UpdateTask(t.Context(), connect.NewRequest(&runnerv1.UpdateTaskRequest{
+ State: &runnerv1.TaskState{
+ Id: actionTask.ID,
+ Result: runnerv1.Result_RESULT_SUCCESS,
+ },
+ }))
+ assert.NoError(t, err)
+
+ resp, err = runner.client.runnerServiceClient.FetchTask(t.Context(), connect.NewRequest(&runnerv1.FetchTaskRequest{
+ TasksVersion: 0,
+ }))
+ assert.Error(t, err)
+ assert.Nil(t, resp)
+
+ resp, err = runner.client.runnerServiceClient.FetchTask(t.Context(), connect.NewRequest(&runnerv1.FetchTaskRequest{
+ TasksVersion: 0,
+ }))
+ assert.Error(t, err)
+ assert.Nil(t, resp)
+
+ // create a runner that picks a job and get force cancelled
+ runnerToBeRemoved := newMockRunner()
+ runnerToBeRemoved.registerAsRepoRunner(t, baseRepo.OwnerName, baseRepo.Name, "mock-runner-to-be-removed", []string{"ubuntu-latest"}, true)
+
+ taskToStopAPIObj := runnerToBeRemoved.fetchTask(t)
+
+ taskToStop := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: taskToStopAPIObj.Id})
+
+ // verify CleanupEphemeralRunners does not remove the custom crafted runner
+ err = actions_service.CleanupEphemeralRunners(t.Context())
+ assert.NoError(t, err)
+
+ runnerToRemove := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{ID: taskToStop.RunnerID})
+
+ err = actions_model.StopTask(t.Context(), taskToStop.ID, actions_model.StatusFailure)
+ assert.NoError(t, err)
+
+ // verify CleanupEphemeralRunners does remove the custom crafted runner
+ err = actions_service.CleanupEphemeralRunners(t.Context())
+ assert.NoError(t, err)
- httpContext := NewAPITestContext(t, user2.Name, apiRepo.Name, auth_model.AccessTokenScopeWriteRepository)
- doAPIDeleteRepository(httpContext)(t)
+ unittest.AssertNotExistsBean(t, &actions_model.ActionRunner{ID: runnerToRemove.ID})
})
}
diff --git a/tests/integration/actions_log_test.go b/tests/integration/actions_log_test.go
index fd055fc4c4..503bda97c9 100644
--- a/tests/integration/actions_log_test.go
+++ b/tests/integration/actions_log_test.go
@@ -7,10 +7,12 @@ import (
"fmt"
"net/http"
"net/url"
+ "strconv"
"strings"
"testing"
"time"
+ actions_model "code.gitea.io/gitea/models/actions"
auth_model "code.gitea.io/gitea/models/auth"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
@@ -29,13 +31,13 @@ func TestDownloadTaskLogs(t *testing.T) {
testCases := []struct {
treePath string
fileContent string
- outcome *mockTaskOutcome
+ outcome []*mockTaskOutcome
zstdEnabled bool
}{
{
treePath: ".gitea/workflows/download-task-logs-zstd.yml",
fileContent: `name: download-task-logs-zstd
-on:
+on:
push:
paths:
- '.gitea/workflows/download-task-logs-zstd.yml'
@@ -44,21 +46,44 @@ jobs:
runs-on: ubuntu-latest
steps:
- run: echo job1 with zstd enabled
+ job2:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo job2 with zstd enabled
`,
- outcome: &mockTaskOutcome{
- result: runnerv1.Result_RESULT_SUCCESS,
- logRows: []*runnerv1.LogRow{
- {
- Time: timestamppb.New(now.Add(1 * time.Second)),
- Content: " \U0001F433 docker create image",
+ outcome: []*mockTaskOutcome{
+ {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ logRows: []*runnerv1.LogRow{
+ {
+ Time: timestamppb.New(now.Add(1 * time.Second)),
+ Content: " \U0001F433 docker create image",
+ },
+ {
+ Time: timestamppb.New(now.Add(2 * time.Second)),
+ Content: "job1 zstd enabled",
+ },
+ {
+ Time: timestamppb.New(now.Add(3 * time.Second)),
+ Content: "\U0001F3C1 Job succeeded",
+ },
},
- {
- Time: timestamppb.New(now.Add(2 * time.Second)),
- Content: "job1 zstd enabled",
- },
- {
- Time: timestamppb.New(now.Add(3 * time.Second)),
- Content: "\U0001F3C1 Job succeeded",
+ },
+ {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ logRows: []*runnerv1.LogRow{
+ {
+ Time: timestamppb.New(now.Add(1 * time.Second)),
+ Content: " \U0001F433 docker create image",
+ },
+ {
+ Time: timestamppb.New(now.Add(2 * time.Second)),
+ Content: "job2 zstd enabled",
+ },
+ {
+ Time: timestamppb.New(now.Add(3 * time.Second)),
+ Content: "\U0001F3C1 Job succeeded",
+ },
},
},
},
@@ -67,7 +92,7 @@ jobs:
{
treePath: ".gitea/workflows/download-task-logs-no-zstd.yml",
fileContent: `name: download-task-logs-no-zstd
-on:
+on:
push:
paths:
- '.gitea/workflows/download-task-logs-no-zstd.yml'
@@ -76,21 +101,44 @@ jobs:
runs-on: ubuntu-latest
steps:
- run: echo job1 with zstd disabled
+ job2:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo job2 with zstd disabled
`,
- outcome: &mockTaskOutcome{
- result: runnerv1.Result_RESULT_SUCCESS,
- logRows: []*runnerv1.LogRow{
- {
- Time: timestamppb.New(now.Add(4 * time.Second)),
- Content: " \U0001F433 docker create image",
- },
- {
- Time: timestamppb.New(now.Add(5 * time.Second)),
- Content: "job1 zstd disabled",
+ outcome: []*mockTaskOutcome{
+ {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ logRows: []*runnerv1.LogRow{
+ {
+ Time: timestamppb.New(now.Add(4 * time.Second)),
+ Content: " \U0001F433 docker create image",
+ },
+ {
+ Time: timestamppb.New(now.Add(5 * time.Second)),
+ Content: "job1 zstd disabled",
+ },
+ {
+ Time: timestamppb.New(now.Add(6 * time.Second)),
+ Content: "\U0001F3C1 Job succeeded",
+ },
},
- {
- Time: timestamppb.New(now.Add(6 * time.Second)),
- Content: "\U0001F3C1 Job succeeded",
+ },
+ {
+ result: runnerv1.Result_RESULT_SUCCESS,
+ logRows: []*runnerv1.LogRow{
+ {
+ Time: timestamppb.New(now.Add(4 * time.Second)),
+ Content: " \U0001F433 docker create image",
+ },
+ {
+ Time: timestamppb.New(now.Add(5 * time.Second)),
+ Content: "job2 zstd disabled",
+ },
+ {
+ Time: timestamppb.New(now.Add(6 * time.Second)),
+ Content: "\U0001F3C1 Job succeeded",
+ },
},
},
},
@@ -105,10 +153,10 @@ jobs:
apiRepo := createActionsTestRepo(t, token, "actions-download-task-logs", false)
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID})
runner := newMockRunner()
- runner.registerAsRepoRunner(t, user2.Name, repo.Name, "mock-runner", []string{"ubuntu-latest"})
+ runner.registerAsRepoRunner(t, user2.Name, repo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
for _, tc := range testCases {
- t.Run(fmt.Sprintf("test %s", tc.treePath), func(t *testing.T) {
+ t.Run("test "+tc.treePath, func(t *testing.T) {
var resetFunc func()
if tc.zstdEnabled {
resetFunc = test.MockVariableValue(&setting.Actions.LogCompression, "zstd")
@@ -119,41 +167,60 @@ jobs:
}
// create the workflow file
- opts := getWorkflowCreateFileOptions(user2, repo.DefaultBranch, fmt.Sprintf("create %s", tc.treePath), tc.fileContent)
+ opts := getWorkflowCreateFileOptions(user2, repo.DefaultBranch, "create "+tc.treePath, tc.fileContent)
createWorkflowFile(t, token, user2.Name, repo.Name, tc.treePath, opts)
- // fetch and execute task
- task := runner.fetchTask(t)
- runner.execTask(t, task, tc.outcome)
+ // fetch and execute tasks
+ for jobIndex, outcome := range tc.outcome {
+ task := runner.fetchTask(t)
+ runner.execTask(t, task, outcome)
- // check whether the log file exists
- logFileName := fmt.Sprintf("%s/%02x/%d.log", repo.FullName(), task.Id%256, task.Id)
- if setting.Actions.LogCompression.IsZstd() {
- logFileName += ".zst"
- }
- _, err := storage.Actions.Stat(logFileName)
- assert.NoError(t, err)
+ // check whether the log file exists
+ logFileName := fmt.Sprintf("%s/%02x/%d.log", repo.FullName(), task.Id%256, task.Id)
+ if setting.Actions.LogCompression.IsZstd() {
+ logFileName += ".zst"
+ }
+ _, err := storage.Actions.Stat(logFileName)
+ assert.NoError(t, err)
- // download task logs and check content
- runIndex := task.Context.GetFields()["run_number"].GetStringValue()
- req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/actions/runs/%s/jobs/0/logs", user2.Name, repo.Name, runIndex)).
- AddTokenAuth(token)
- resp := MakeRequest(t, req, http.StatusOK)
- logTextLines := strings.Split(strings.TrimSpace(resp.Body.String()), "\n")
- assert.Len(t, logTextLines, len(tc.outcome.logRows))
- for idx, lr := range tc.outcome.logRows {
- assert.Equal(
- t,
- fmt.Sprintf("%s %s", lr.Time.AsTime().Format("2006-01-02T15:04:05.0000000Z07:00"), lr.Content),
- logTextLines[idx],
- )
- }
+ // download task logs and check content
+ runIndex := task.Context.GetFields()["run_number"].GetStringValue()
+ req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/actions/runs/%s/jobs/%d/logs", user2.Name, repo.Name, runIndex, jobIndex)).
+ AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ logTextLines := strings.Split(strings.TrimSpace(resp.Body.String()), "\n")
+ assert.Len(t, logTextLines, len(outcome.logRows))
+ for idx, lr := range outcome.logRows {
+ assert.Equal(
+ t,
+ fmt.Sprintf("%s %s", lr.Time.AsTime().Format("2006-01-02T15:04:05.0000000Z07:00"), lr.Content),
+ logTextLines[idx],
+ )
+ }
+
+ runID, _ := strconv.ParseInt(task.Context.GetFields()["run_id"].GetStringValue(), 10, 64)
+ jobs, err := actions_model.GetRunJobsByRunID(t.Context(), runID)
+ assert.NoError(t, err)
+ assert.Len(t, jobs, len(tc.outcome))
+ jobID := jobs[jobIndex].ID
+
+ // download task logs from API and check content
+ req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/actions/jobs/%d/logs", user2.Name, repo.Name, jobID)).
+ AddTokenAuth(token)
+ resp = MakeRequest(t, req, http.StatusOK)
+ logTextLines = strings.Split(strings.TrimSpace(resp.Body.String()), "\n")
+ assert.Len(t, logTextLines, len(outcome.logRows))
+ for idx, lr := range outcome.logRows {
+ assert.Equal(
+ t,
+ fmt.Sprintf("%s %s", lr.Time.AsTime().Format("2006-01-02T15:04:05.0000000Z07:00"), lr.Content),
+ logTextLines[idx],
+ )
+ }
+ }
resetFunc()
})
}
-
- httpContext := NewAPITestContext(t, user2.Name, repo.Name, auth_model.AccessTokenScopeWriteRepository)
- doAPIDeleteRepository(httpContext)(t)
})
}
diff --git a/tests/integration/actions_runner_modify_test.go b/tests/integration/actions_runner_modify_test.go
new file mode 100644
index 0000000000..7d711bae49
--- /dev/null
+++ b/tests/integration/actions_runner_modify_test.go
@@ -0,0 +1,150 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+
+ actions_model "code.gitea.io/gitea/models/actions"
+ "code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/tests"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestActionsRunnerModify(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ ctx := t.Context()
+
+ require.NoError(t, db.DeleteAllRecords("action_runner"))
+
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ _ = actions_model.CreateRunner(ctx, &actions_model.ActionRunner{OwnerID: user2.ID, Name: "user2-runner", TokenHash: "a", UUID: "a"})
+ user2Runner := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{OwnerID: user2.ID, Name: "user2-runner"})
+ userWebURL := "/user/settings/actions/runners"
+
+ org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3, Type: user_model.UserTypeOrganization})
+ require.NoError(t, actions_model.CreateRunner(ctx, &actions_model.ActionRunner{OwnerID: org3.ID, Name: "org3-runner", TokenHash: "b", UUID: "b"}))
+ org3Runner := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{OwnerID: org3.ID, Name: "org3-runner"})
+ orgWebURL := "/org/org3/settings/actions/runners"
+
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ _ = actions_model.CreateRunner(ctx, &actions_model.ActionRunner{RepoID: repo1.ID, Name: "repo1-runner", TokenHash: "c", UUID: "c"})
+ repo1Runner := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{RepoID: repo1.ID, Name: "repo1-runner"})
+ repoWebURL := "/user2/repo1/settings/actions/runners"
+
+ _ = actions_model.CreateRunner(ctx, &actions_model.ActionRunner{Name: "global-runner", TokenHash: "d", UUID: "d"})
+ globalRunner := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{Name: "global-runner"})
+ adminWebURL := "/-/admin/actions/runners"
+
+ sessionAdmin := loginUser(t, "user1")
+ sessionUser2 := loginUser(t, user2.Name)
+
+ doUpdate := func(t *testing.T, sess *TestSession, baseURL string, id int64, description string, expectedStatus int) {
+ req := NewRequestWithValues(t, "POST", fmt.Sprintf("%s/%d", baseURL, id), map[string]string{
+ "_csrf": GetUserCSRFToken(t, sess),
+ "description": description,
+ })
+ sess.MakeRequest(t, req, expectedStatus)
+ }
+
+ doDelete := func(t *testing.T, sess *TestSession, baseURL string, id int64, expectedStatus int) {
+ req := NewRequestWithValues(t, "POST", fmt.Sprintf("%s/%d/delete", baseURL, id), map[string]string{
+ "_csrf": GetUserCSRFToken(t, sess),
+ })
+ sess.MakeRequest(t, req, expectedStatus)
+ }
+
+ assertDenied := func(t *testing.T, sess *TestSession, baseURL string, id int64) {
+ doUpdate(t, sess, baseURL, id, "ChangedDescription", http.StatusNotFound)
+ doDelete(t, sess, baseURL, id, http.StatusNotFound)
+ v := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{ID: id})
+ assert.Empty(t, v.Description)
+ }
+
+ assertSuccess := func(t *testing.T, sess *TestSession, baseURL string, id int64) {
+ doUpdate(t, sess, baseURL, id, "ChangedDescription", http.StatusSeeOther)
+ v := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{ID: id})
+ assert.Equal(t, "ChangedDescription", v.Description)
+ doDelete(t, sess, baseURL, id, http.StatusOK)
+ unittest.AssertNotExistsBean(t, &actions_model.ActionRunner{ID: id})
+ }
+
+ t.Run("UpdateUserRunner", func(t *testing.T) {
+ theRunner := user2Runner
+ t.Run("FromOrg", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, orgWebURL, theRunner.ID)
+ })
+ t.Run("FromRepo", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, repoWebURL, theRunner.ID)
+ })
+ t.Run("FromAdmin", func(t *testing.T) {
+ t.Skip("Admin can update any runner (not right but not too bad)")
+ assertDenied(t, sessionAdmin, adminWebURL, theRunner.ID)
+ })
+ })
+
+ t.Run("UpdateOrgRunner", func(t *testing.T) {
+ theRunner := org3Runner
+ t.Run("FromRepo", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, repoWebURL, theRunner.ID)
+ })
+ t.Run("FromUser", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, userWebURL, theRunner.ID)
+ })
+ t.Run("FromAdmin", func(t *testing.T) {
+ t.Skip("Admin can update any runner (not right but not too bad)")
+ assertDenied(t, sessionAdmin, adminWebURL, theRunner.ID)
+ })
+ })
+
+ t.Run("UpdateRepoRunner", func(t *testing.T) {
+ theRunner := repo1Runner
+ t.Run("FromOrg", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, orgWebURL, theRunner.ID)
+ })
+ t.Run("FromUser", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, userWebURL, theRunner.ID)
+ })
+ t.Run("FromAdmin", func(t *testing.T) {
+ t.Skip("Admin can update any runner (not right but not too bad)")
+ assertDenied(t, sessionAdmin, adminWebURL, theRunner.ID)
+ })
+ })
+
+ t.Run("UpdateGlobalRunner", func(t *testing.T) {
+ theRunner := globalRunner
+ t.Run("FromOrg", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, orgWebURL, theRunner.ID)
+ })
+ t.Run("FromUser", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, userWebURL, theRunner.ID)
+ })
+ t.Run("FromRepo", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, repoWebURL, theRunner.ID)
+ })
+ })
+
+ t.Run("UpdateSuccess", func(t *testing.T) {
+ t.Run("User", func(t *testing.T) {
+ assertSuccess(t, sessionUser2, userWebURL, user2Runner.ID)
+ })
+ t.Run("Org", func(t *testing.T) {
+ assertSuccess(t, sessionAdmin, orgWebURL, org3Runner.ID)
+ })
+ t.Run("Repo", func(t *testing.T) {
+ assertSuccess(t, sessionUser2, repoWebURL, repo1Runner.ID)
+ })
+ t.Run("Admin", func(t *testing.T) {
+ assertSuccess(t, sessionAdmin, adminWebURL, globalRunner.ID)
+ })
+ })
+}
diff --git a/tests/integration/actions_runner_test.go b/tests/integration/actions_runner_test.go
index 355ea1705e..6cc5a10e0f 100644
--- a/tests/integration/actions_runner_test.go
+++ b/tests/integration/actions_runner_test.go
@@ -37,7 +37,7 @@ func newMockRunner() *mockRunner {
}
func newMockRunnerClient(uuid, token string) *mockRunnerClient {
- baseURL := fmt.Sprintf("%sapi/actions", setting.AppURL)
+ baseURL := setting.AppURL + "api/actions"
opt := connect.WithInterceptors(connect.UnaryInterceptorFunc(func(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
@@ -60,26 +60,27 @@ func newMockRunnerClient(uuid, token string) *mockRunnerClient {
}
func (r *mockRunner) doPing(t *testing.T) {
- resp, err := r.client.pingServiceClient.Ping(context.Background(), connect.NewRequest(&pingv1.PingRequest{
+ resp, err := r.client.pingServiceClient.Ping(t.Context(), connect.NewRequest(&pingv1.PingRequest{
Data: "mock-runner",
}))
assert.NoError(t, err)
assert.Equal(t, "Hello, mock-runner!", resp.Msg.Data)
}
-func (r *mockRunner) doRegister(t *testing.T, name, token string, labels []string) {
+func (r *mockRunner) doRegister(t *testing.T, name, token string, labels []string, ephemeral bool) {
r.doPing(t)
- resp, err := r.client.runnerServiceClient.Register(context.Background(), connect.NewRequest(&runnerv1.RegisterRequest{
- Name: name,
- Token: token,
- Version: "mock-runner-version",
- Labels: labels,
+ resp, err := r.client.runnerServiceClient.Register(t.Context(), connect.NewRequest(&runnerv1.RegisterRequest{
+ Name: name,
+ Token: token,
+ Version: "mock-runner-version",
+ Labels: labels,
+ Ephemeral: ephemeral,
}))
assert.NoError(t, err)
r.client = newMockRunnerClient(resp.Msg.Runner.Uuid, resp.Msg.Runner.Token)
}
-func (r *mockRunner) registerAsRepoRunner(t *testing.T, ownerName, repoName, runnerName string, labels []string) {
+func (r *mockRunner) registerAsRepoRunner(t *testing.T, ownerName, repoName, runnerName string, labels []string, ephemeral bool) {
session := loginUser(t, ownerName)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/actions/runners/registration-token", ownerName, repoName)).AddTokenAuth(token)
@@ -88,7 +89,7 @@ func (r *mockRunner) registerAsRepoRunner(t *testing.T, ownerName, repoName, run
Token string `json:"token"`
}
DecodeJSON(t, resp, &registrationToken)
- r.doRegister(t, runnerName, registrationToken.Token, labels)
+ r.doRegister(t, runnerName, registrationToken.Token, labels, ephemeral)
}
func (r *mockRunner) fetchTask(t *testing.T, timeout ...time.Duration) *runnerv1.Task {
@@ -99,7 +100,7 @@ func (r *mockRunner) fetchTask(t *testing.T, timeout ...time.Duration) *runnerv1
ddl := time.Now().Add(fetchTimeout)
var task *runnerv1.Task
for time.Now().Before(ddl) {
- resp, err := r.client.runnerServiceClient.FetchTask(context.Background(), connect.NewRequest(&runnerv1.FetchTaskRequest{
+ resp, err := r.client.runnerServiceClient.FetchTask(t.Context(), connect.NewRequest(&runnerv1.FetchTaskRequest{
TasksVersion: 0,
}))
assert.NoError(t, err)
@@ -114,15 +115,14 @@ func (r *mockRunner) fetchTask(t *testing.T, timeout ...time.Duration) *runnerv1
}
type mockTaskOutcome struct {
- result runnerv1.Result
- outputs map[string]string
- logRows []*runnerv1.LogRow
- execTime time.Duration
+ result runnerv1.Result
+ outputs map[string]string
+ logRows []*runnerv1.LogRow
}
func (r *mockRunner) execTask(t *testing.T, task *runnerv1.Task, outcome *mockTaskOutcome) {
for idx, lr := range outcome.logRows {
- resp, err := r.client.runnerServiceClient.UpdateLog(context.Background(), connect.NewRequest(&runnerv1.UpdateLogRequest{
+ resp, err := r.client.runnerServiceClient.UpdateLog(t.Context(), connect.NewRequest(&runnerv1.UpdateLogRequest{
TaskId: task.Id,
Index: int64(idx),
Rows: []*runnerv1.LogRow{lr},
@@ -133,7 +133,7 @@ func (r *mockRunner) execTask(t *testing.T, task *runnerv1.Task, outcome *mockTa
}
sentOutputKeys := make([]string, 0, len(outcome.outputs))
for outputKey, outputValue := range outcome.outputs {
- resp, err := r.client.runnerServiceClient.UpdateTask(context.Background(), connect.NewRequest(&runnerv1.UpdateTaskRequest{
+ resp, err := r.client.runnerServiceClient.UpdateTask(t.Context(), connect.NewRequest(&runnerv1.UpdateTaskRequest{
State: &runnerv1.TaskState{
Id: task.Id,
Result: runnerv1.Result_RESULT_UNSPECIFIED,
@@ -144,8 +144,7 @@ func (r *mockRunner) execTask(t *testing.T, task *runnerv1.Task, outcome *mockTa
sentOutputKeys = append(sentOutputKeys, outputKey)
assert.ElementsMatch(t, sentOutputKeys, resp.Msg.SentOutputs)
}
- time.Sleep(outcome.execTime)
- resp, err := r.client.runnerServiceClient.UpdateTask(context.Background(), connect.NewRequest(&runnerv1.UpdateTaskRequest{
+ resp, err := r.client.runnerServiceClient.UpdateTask(t.Context(), connect.NewRequest(&runnerv1.UpdateTaskRequest{
State: &runnerv1.TaskState{
Id: task.Id,
Result: outcome.result,
diff --git a/tests/integration/actions_trigger_test.go b/tests/integration/actions_trigger_test.go
index 4718aa7e73..088491d570 100644
--- a/tests/integration/actions_trigger_test.go
+++ b/tests/integration/actions_trigger_test.go
@@ -4,7 +4,9 @@
package integration
import (
+ "encoding/base64"
"fmt"
+ "net/http"
"net/url"
"strings"
"testing"
@@ -17,17 +19,23 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
- unit_model "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
actions_module "code.gitea.io/gitea/modules/actions"
+ "code.gitea.io/gitea/modules/commitstatus"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
+ api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"
+ "code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
+ issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull"
release_service "code.gitea.io/gitea/services/release"
repo_service "code.gitea.io/gitea/services/repository"
+ commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus"
files_service "code.gitea.io/gitea/services/repository/files"
"github.com/stretchr/testify/assert"
@@ -52,13 +60,6 @@ func TestPullRequestTargetEvent(t *testing.T) {
assert.NoError(t, err)
assert.NotEmpty(t, baseRepo)
- // enable actions
- err = repo_service.UpdateRepositoryUnits(db.DefaultContext, baseRepo, []repo_model.RepoUnit{{
- RepoID: baseRepo.ID,
- Type: unit_model.TypeActions,
- }}, nil)
- assert.NoError(t, err)
-
// add user4 as the collaborator
ctx := NewAPITestContext(t, baseRepo.OwnerName, baseRepo.Name, auth_model.AccessTokenScopeWriteRepository)
t.Run("AddUser4AsCollaboratorWithReadAccess", doAPIAddCollaborator(ctx, "user4", perm.AccessModeRead))
@@ -76,21 +77,31 @@ func TestPullRequestTargetEvent(t *testing.T) {
addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, baseRepo, user2, &files_service.ChangeRepoFilesOptions{
Files: []*files_service.ChangeRepoFile{
{
- Operation: "create",
- TreePath: ".gitea/workflows/pr.yml",
- ContentReader: strings.NewReader("name: test\non:\n pull_request_target:\n paths:\n - 'file_*.txt'\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - run: echo helloworld\n"),
+ Operation: "create",
+ TreePath: ".gitea/workflows/pr.yml",
+ ContentReader: strings.NewReader(`name: test
+on:
+ pull_request_target:
+ paths:
+ - 'file_*.txt'
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo helloworld
+`),
},
},
Message: "add workflow",
OldBranch: "main",
NewBranch: "main",
Author: &files_service.IdentityOptions{
- Name: user2.Name,
- Email: user2.Email,
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
},
Committer: &files_service.IdentityOptions{
- Name: user2.Name,
- Email: user2.Email,
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
},
Dates: &files_service.CommitDateOptions{
Author: time.Now(),
@@ -113,12 +124,12 @@ func TestPullRequestTargetEvent(t *testing.T) {
OldBranch: "main",
NewBranch: "fork-branch-1",
Author: &files_service.IdentityOptions{
- Name: user4.Name,
- Email: user4.Email,
+ GitUserName: user4.Name,
+ GitUserEmail: user4.Email,
},
Committer: &files_service.IdentityOptions{
- Name: user4.Name,
- Email: user4.Email,
+ GitUserName: user4.Name,
+ GitUserEmail: user4.Email,
},
Dates: &files_service.CommitDateOptions{
Author: time.Now(),
@@ -168,12 +179,12 @@ func TestPullRequestTargetEvent(t *testing.T) {
OldBranch: "main",
NewBranch: "fork-branch-2",
Author: &files_service.IdentityOptions{
- Name: user4.Name,
- Email: user4.Email,
+ GitUserName: user4.Name,
+ GitUserEmail: user4.Email,
},
Committer: &files_service.IdentityOptions{
- Name: user4.Name,
- Email: user4.Email,
+ GitUserName: user4.Name,
+ GitUserEmail: user4.Email,
},
Dates: &files_service.CommitDateOptions{
Author: time.Now(),
@@ -228,32 +239,35 @@ func TestSkipCI(t *testing.T) {
assert.NoError(t, err)
assert.NotEmpty(t, repo)
- // enable actions
- err = repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, []repo_model.RepoUnit{{
- RepoID: repo.ID,
- Type: unit_model.TypeActions,
- }}, nil)
- assert.NoError(t, err)
-
// add workflow file to the repo
addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
Files: []*files_service.ChangeRepoFile{
{
- Operation: "create",
- TreePath: ".gitea/workflows/pr.yml",
- ContentReader: strings.NewReader("name: test\non:\n push:\n branches: [master]\n pull_request:\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - run: echo helloworld\n"),
+ Operation: "create",
+ TreePath: ".gitea/workflows/pr.yml",
+ ContentReader: strings.NewReader(`name: test
+on:
+ push:
+ branches: [master]
+ pull_request:
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo helloworld
+`),
},
},
Message: "add workflow",
OldBranch: "master",
NewBranch: "master",
Author: &files_service.IdentityOptions{
- Name: user2.Name,
- Email: user2.Email,
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
},
Committer: &files_service.IdentityOptions{
- Name: user2.Name,
- Email: user2.Email,
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
},
Dates: &files_service.CommitDateOptions{
Author: time.Now(),
@@ -275,16 +289,16 @@ func TestSkipCI(t *testing.T) {
ContentReader: strings.NewReader("bar"),
},
},
- Message: fmt.Sprintf("%s add bar", setting.Actions.SkipWorkflowStrings[0]),
+ Message: setting.Actions.SkipWorkflowStrings[0] + " add bar",
OldBranch: "master",
NewBranch: "master",
Author: &files_service.IdentityOptions{
- Name: user2.Name,
- Email: user2.Email,
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
},
Committer: &files_service.IdentityOptions{
- Name: user2.Name,
- Email: user2.Email,
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
},
Dates: &files_service.CommitDateOptions{
Author: time.Now(),
@@ -310,12 +324,12 @@ func TestSkipCI(t *testing.T) {
OldBranch: "master",
NewBranch: "test-skip-ci",
Author: &files_service.IdentityOptions{
- Name: user2.Name,
- Email: user2.Email,
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
},
Committer: &files_service.IdentityOptions{
- Name: user2.Name,
- Email: user2.Email,
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
},
Dates: &files_service.CommitDateOptions{
Author: time.Now(),
@@ -354,32 +368,33 @@ func TestCreateDeleteRefEvent(t *testing.T) {
assert.NoError(t, err)
assert.NotEmpty(t, repo)
- // enable actions
- err = repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, []repo_model.RepoUnit{{
- RepoID: repo.ID,
- Type: unit_model.TypeActions,
- }}, nil)
- assert.NoError(t, err)
-
// add workflow file to the repo
addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
Files: []*files_service.ChangeRepoFile{
{
- Operation: "create",
- TreePath: ".gitea/workflows/createdelete.yml",
- ContentReader: strings.NewReader("name: test\non:\n [create,delete]\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - run: echo helloworld\n"),
+ Operation: "create",
+ TreePath: ".gitea/workflows/createdelete.yml",
+ ContentReader: strings.NewReader(`name: test
+on:
+ [create,delete]
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo helloworld
+`),
},
},
Message: "add workflow",
OldBranch: "main",
NewBranch: "main",
Author: &files_service.IdentityOptions{
- Name: user2.Name,
- Email: user2.Email,
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
},
Committer: &files_service.IdentityOptions{
- Name: user2.Name,
- Email: user2.Email,
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
},
Dates: &files_service.CommitDateOptions{
Author: time.Now(),
@@ -423,7 +438,7 @@ func TestCreateDeleteRefEvent(t *testing.T) {
assert.NotNil(t, run)
// delete the branch
- err = repo_service.DeleteBranch(db.DefaultContext, user2, repo, gitRepo, "test-create-branch")
+ err = repo_service.DeleteBranch(db.DefaultContext, user2, repo, gitRepo, "test-create-branch", nil)
assert.NoError(t, err)
run = unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
Title: "add workflow",
@@ -451,3 +466,1141 @@ func TestCreateDeleteRefEvent(t *testing.T) {
assert.NotNil(t, run)
})
}
+
+func TestPullRequestCommitStatusEvent(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the repo
+ user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) // contributor of the repo
+
+ // create a repo
+ repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
+ Name: "repo-pull-request",
+ Description: "test pull-request event",
+ AutoInit: true,
+ Gitignores: "Go",
+ License: "MIT",
+ Readme: "Default",
+ DefaultBranch: "main",
+ IsPrivate: false,
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, repo)
+
+ // add user4 as the collaborator
+ ctx := NewAPITestContext(t, repo.OwnerName, repo.Name, auth_model.AccessTokenScopeWriteRepository)
+ t.Run("AddUser4AsCollaboratorWithReadAccess", doAPIAddCollaborator(ctx, "user4", perm.AccessModeRead))
+
+ // add the workflow file to the repo
+ addWorkflow, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "create",
+ TreePath: ".gitea/workflows/pr.yml",
+ ContentReader: strings.NewReader(`name: test
+on:
+ pull_request:
+ types: [assigned, unassigned, labeled, unlabeled, opened, edited, closed, reopened, synchronize, milestoned, demilestoned, review_requested, review_request_removed]
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo helloworld
+`),
+ },
+ },
+ Message: "add workflow",
+ OldBranch: "main",
+ NewBranch: "main",
+ Author: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Committer: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, addWorkflow)
+ sha := addWorkflow.Commit.SHA
+
+ // create a new branch
+ testBranch := "test-branch"
+ gitRepo, err := git.OpenRepository(git.DefaultContext, ".")
+ assert.NoError(t, err)
+ err = repo_service.CreateNewBranch(git.DefaultContext, user2, repo, gitRepo, "main", testBranch)
+ assert.NoError(t, err)
+
+ // create Pull
+ pullIssue := &issues_model.Issue{
+ RepoID: repo.ID,
+ Title: "A test PR",
+ PosterID: user2.ID,
+ Poster: user2,
+ IsPull: true,
+ }
+ pullRequest := &issues_model.PullRequest{
+ HeadRepoID: repo.ID,
+ BaseRepoID: repo.ID,
+ HeadBranch: testBranch,
+ BaseBranch: "main",
+ HeadRepo: repo,
+ BaseRepo: repo,
+ Type: issues_model.PullRequestGitea,
+ }
+ prOpts := &pull_service.NewPullRequestOptions{Repo: repo, Issue: pullIssue, PullRequest: pullRequest}
+ err = pull_service.NewPullRequest(db.DefaultContext, prOpts)
+ assert.NoError(t, err)
+
+ // opened
+ checkCommitStatusAndInsertFakeStatus(t, repo, sha)
+
+ // edited
+ err = issue_service.ChangeContent(db.DefaultContext, pullIssue, user2, "test", 0)
+ assert.NoError(t, err)
+ checkCommitStatusAndInsertFakeStatus(t, repo, sha)
+
+ // closed
+ err = issue_service.CloseIssue(db.DefaultContext, pullIssue, user2, "")
+ assert.NoError(t, err)
+ checkCommitStatusAndInsertFakeStatus(t, repo, sha)
+
+ // reopened
+ err = issue_service.ReopenIssue(db.DefaultContext, pullIssue, user2, "")
+ assert.NoError(t, err)
+ checkCommitStatusAndInsertFakeStatus(t, repo, sha)
+
+ // assign
+ removed, _, err := issue_service.ToggleAssigneeWithNotify(db.DefaultContext, pullIssue, user2, user4.ID)
+ assert.False(t, removed)
+ assert.NoError(t, err)
+ checkCommitStatusAndInsertFakeStatus(t, repo, sha)
+
+ // unassign
+ removed, _, err = issue_service.ToggleAssigneeWithNotify(db.DefaultContext, pullIssue, user2, user4.ID)
+ assert.True(t, removed)
+ assert.NoError(t, err)
+ checkCommitStatusAndInsertFakeStatus(t, repo, sha)
+
+ // labeled
+ label := &issues_model.Label{
+ RepoID: repo.ID,
+ Name: "test",
+ Exclusive: false,
+ Description: "test",
+ Color: "#e11d21",
+ }
+ err = issues_model.NewLabel(db.DefaultContext, label)
+ assert.NoError(t, err)
+ err = issue_service.AddLabel(db.DefaultContext, pullIssue, user2, label)
+ assert.NoError(t, err)
+ checkCommitStatusAndInsertFakeStatus(t, repo, sha)
+
+ // unlabeled
+ err = issue_service.RemoveLabel(db.DefaultContext, pullIssue, user2, label)
+ assert.NoError(t, err)
+ checkCommitStatusAndInsertFakeStatus(t, repo, sha)
+
+ // synchronize
+ addFileResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "create",
+ TreePath: "test.txt",
+ ContentReader: strings.NewReader("test"),
+ },
+ },
+ Message: "add file",
+ OldBranch: testBranch,
+ NewBranch: testBranch,
+ Author: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Committer: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, addFileResp)
+ sha = addFileResp.Commit.SHA
+ assert.Eventually(t, func() bool {
+ latestCommitStatuses, err := git_model.GetLatestCommitStatus(db.DefaultContext, repo.ID, sha, db.ListOptionsAll)
+ assert.NoError(t, err)
+ if len(latestCommitStatuses) == 0 {
+ return false
+ }
+ if latestCommitStatuses[0].State == commitstatus.CommitStatusPending {
+ insertFakeStatus(t, repo, sha, latestCommitStatuses[0].TargetURL, latestCommitStatuses[0].Context)
+ return true
+ }
+ return false
+ }, 1*time.Second, 100*time.Millisecond)
+
+ // milestoned
+ milestone := &issues_model.Milestone{
+ RepoID: repo.ID,
+ Name: "test",
+ Content: "test",
+ DeadlineUnix: timeutil.TimeStampNow(),
+ }
+ err = issues_model.NewMilestone(db.DefaultContext, milestone)
+ assert.NoError(t, err)
+ err = issue_service.ChangeMilestoneAssign(db.DefaultContext, pullIssue, user2, milestone.ID)
+ assert.NoError(t, err)
+ checkCommitStatusAndInsertFakeStatus(t, repo, sha)
+
+ // demilestoned
+ err = issue_service.ChangeMilestoneAssign(db.DefaultContext, pullIssue, user2, milestone.ID)
+ assert.NoError(t, err)
+ checkCommitStatusAndInsertFakeStatus(t, repo, sha)
+
+ // review_requested
+ _, err = issue_service.ReviewRequest(db.DefaultContext, pullIssue, user2, nil, user4, true)
+ assert.NoError(t, err)
+ checkCommitStatusAndInsertFakeStatus(t, repo, sha)
+
+ // review_request_removed
+ _, err = issue_service.ReviewRequest(db.DefaultContext, pullIssue, user2, nil, user4, false)
+ assert.NoError(t, err)
+ checkCommitStatusAndInsertFakeStatus(t, repo, sha)
+ })
+}
+
+func checkCommitStatusAndInsertFakeStatus(t *testing.T, repo *repo_model.Repository, sha string) {
+ latestCommitStatuses, err := git_model.GetLatestCommitStatus(db.DefaultContext, repo.ID, sha, db.ListOptionsAll)
+ assert.NoError(t, err)
+ assert.Len(t, latestCommitStatuses, 1)
+ assert.Equal(t, commitstatus.CommitStatusPending, latestCommitStatuses[0].State)
+
+ insertFakeStatus(t, repo, sha, latestCommitStatuses[0].TargetURL, latestCommitStatuses[0].Context)
+}
+
+func insertFakeStatus(t *testing.T, repo *repo_model.Repository, sha, targetURL, context string) {
+ err := commitstatus_service.CreateCommitStatus(db.DefaultContext, repo, user_model.NewActionsUser(), sha, &git_model.CommitStatus{
+ State: commitstatus.CommitStatusSuccess,
+ TargetURL: targetURL,
+ Context: context,
+ })
+ assert.NoError(t, err)
+}
+
+func TestWorkflowDispatchPublicApi(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ session := loginUser(t, user2.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // create the repo
+ repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
+ Name: "workflow-dispatch-event",
+ Description: "test workflow-dispatch ci event",
+ AutoInit: true,
+ Gitignores: "Go",
+ License: "MIT",
+ Readme: "Default",
+ DefaultBranch: "main",
+ IsPrivate: false,
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, repo)
+
+ // add workflow file to the repo
+ addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "create",
+ TreePath: ".gitea/workflows/dispatch.yml",
+ ContentReader: strings.NewReader(`
+on:
+ workflow_dispatch
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo helloworld
+`),
+ },
+ },
+ Message: "add workflow",
+ OldBranch: "main",
+ NewBranch: "main",
+ Author: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Committer: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, addWorkflowToBaseResp)
+
+ // Get the commit ID of the default branch
+ gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo)
+ assert.NoError(t, err)
+ defer gitRepo.Close()
+ branch, err := git_model.GetBranch(db.DefaultContext, repo.ID, repo.DefaultBranch)
+ assert.NoError(t, err)
+ values := url.Values{}
+ values.Set("ref", "main")
+ req := NewRequestWithURLValues(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/workflows/dispatch.yml/dispatches", repo.FullName()), values).
+ AddTokenAuth(token)
+ _ = MakeRequest(t, req, http.StatusNoContent)
+
+ run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
+ Title: "add workflow",
+ RepoID: repo.ID,
+ Event: "workflow_dispatch",
+ Ref: "refs/heads/main",
+ WorkflowID: "dispatch.yml",
+ CommitSHA: branch.CommitID,
+ })
+ assert.NotNil(t, run)
+ })
+}
+
+func TestWorkflowDispatchPublicApiWithInputs(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ session := loginUser(t, user2.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // create the repo
+ repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
+ Name: "workflow-dispatch-event",
+ Description: "test workflow-dispatch ci event",
+ AutoInit: true,
+ Gitignores: "Go",
+ License: "MIT",
+ Readme: "Default",
+ DefaultBranch: "main",
+ IsPrivate: false,
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, repo)
+
+ // add workflow file to the repo
+ addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "create",
+ TreePath: ".gitea/workflows/dispatch.yml",
+ ContentReader: strings.NewReader(`
+on:
+ workflow_dispatch: { inputs: { myinput: { default: def }, myinput2: { default: def2 }, myinput3: { type: boolean, default: false } } }
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo helloworld
+`),
+ },
+ },
+ Message: "add workflow",
+ OldBranch: "main",
+ NewBranch: "main",
+ Author: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Committer: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, addWorkflowToBaseResp)
+
+ // Get the commit ID of the default branch
+ gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo)
+ assert.NoError(t, err)
+ defer gitRepo.Close()
+ branch, err := git_model.GetBranch(db.DefaultContext, repo.ID, repo.DefaultBranch)
+ assert.NoError(t, err)
+ values := url.Values{}
+ values.Set("ref", "main")
+ values.Set("inputs[myinput]", "val0")
+ values.Set("inputs[myinput3]", "true")
+ req := NewRequestWithURLValues(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/workflows/dispatch.yml/dispatches", repo.FullName()), values).
+ AddTokenAuth(token)
+ _ = MakeRequest(t, req, http.StatusNoContent)
+
+ run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
+ Title: "add workflow",
+ RepoID: repo.ID,
+ Event: "workflow_dispatch",
+ Ref: "refs/heads/main",
+ WorkflowID: "dispatch.yml",
+ CommitSHA: branch.CommitID,
+ })
+ assert.NotNil(t, run)
+ dispatchPayload := &api.WorkflowDispatchPayload{}
+ err = json.Unmarshal([]byte(run.EventPayload), dispatchPayload)
+ assert.NoError(t, err)
+ assert.Contains(t, dispatchPayload.Inputs, "myinput")
+ assert.Contains(t, dispatchPayload.Inputs, "myinput2")
+ assert.Contains(t, dispatchPayload.Inputs, "myinput3")
+ assert.Equal(t, "val0", dispatchPayload.Inputs["myinput"])
+ assert.Equal(t, "def2", dispatchPayload.Inputs["myinput2"])
+ assert.Equal(t, "true", dispatchPayload.Inputs["myinput3"])
+ })
+}
+
+func TestWorkflowDispatchPublicApiJSON(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ session := loginUser(t, user2.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // create the repo
+ repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
+ Name: "workflow-dispatch-event",
+ Description: "test workflow-dispatch ci event",
+ AutoInit: true,
+ Gitignores: "Go",
+ License: "MIT",
+ Readme: "Default",
+ DefaultBranch: "main",
+ IsPrivate: false,
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, repo)
+
+ // add workflow file to the repo
+ addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "create",
+ TreePath: ".gitea/workflows/dispatch.yml",
+ ContentReader: strings.NewReader(`
+on:
+ workflow_dispatch: { inputs: { myinput: { default: def }, myinput2: { default: def2 }, myinput3: { type: boolean, default: false } } }
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo helloworld
+`),
+ },
+ },
+ Message: "add workflow",
+ OldBranch: "main",
+ NewBranch: "main",
+ Author: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Committer: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, addWorkflowToBaseResp)
+
+ // Get the commit ID of the default branch
+ gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo)
+ assert.NoError(t, err)
+ defer gitRepo.Close()
+ branch, err := git_model.GetBranch(db.DefaultContext, repo.ID, repo.DefaultBranch)
+ assert.NoError(t, err)
+ inputs := &api.CreateActionWorkflowDispatch{
+ Ref: "main",
+ Inputs: map[string]string{
+ "myinput": "val0",
+ "myinput3": "true",
+ },
+ }
+
+ req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/workflows/dispatch.yml/dispatches", repo.FullName()), inputs).
+ AddTokenAuth(token)
+ _ = MakeRequest(t, req, http.StatusNoContent)
+
+ run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
+ Title: "add workflow",
+ RepoID: repo.ID,
+ Event: "workflow_dispatch",
+ Ref: "refs/heads/main",
+ WorkflowID: "dispatch.yml",
+ CommitSHA: branch.CommitID,
+ })
+ assert.NotNil(t, run)
+ })
+}
+
+func TestWorkflowDispatchPublicApiWithInputsJSON(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ session := loginUser(t, user2.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // create the repo
+ repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
+ Name: "workflow-dispatch-event",
+ Description: "test workflow-dispatch ci event",
+ AutoInit: true,
+ Gitignores: "Go",
+ License: "MIT",
+ Readme: "Default",
+ DefaultBranch: "main",
+ IsPrivate: false,
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, repo)
+
+ // add workflow file to the repo
+ addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "create",
+ TreePath: ".gitea/workflows/dispatch.yml",
+ ContentReader: strings.NewReader(`
+on:
+ workflow_dispatch: { inputs: { myinput: { default: def }, myinput2: { default: def2 }, myinput3: { type: boolean, default: false } } }
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo helloworld
+`),
+ },
+ },
+ Message: "add workflow",
+ OldBranch: "main",
+ NewBranch: "main",
+ Author: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Committer: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, addWorkflowToBaseResp)
+
+ // Get the commit ID of the default branch
+ gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo)
+ assert.NoError(t, err)
+ defer gitRepo.Close()
+ branch, err := git_model.GetBranch(db.DefaultContext, repo.ID, repo.DefaultBranch)
+ assert.NoError(t, err)
+ inputs := &api.CreateActionWorkflowDispatch{
+ Ref: "main",
+ Inputs: map[string]string{
+ "myinput": "val0",
+ "myinput3": "true",
+ },
+ }
+ req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/workflows/dispatch.yml/dispatches", repo.FullName()), inputs).
+ AddTokenAuth(token)
+ _ = MakeRequest(t, req, http.StatusNoContent)
+
+ run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
+ Title: "add workflow",
+ RepoID: repo.ID,
+ Event: "workflow_dispatch",
+ Ref: "refs/heads/main",
+ WorkflowID: "dispatch.yml",
+ CommitSHA: branch.CommitID,
+ })
+ assert.NotNil(t, run)
+ dispatchPayload := &api.WorkflowDispatchPayload{}
+ err = json.Unmarshal([]byte(run.EventPayload), dispatchPayload)
+ assert.NoError(t, err)
+ assert.Contains(t, dispatchPayload.Inputs, "myinput")
+ assert.Contains(t, dispatchPayload.Inputs, "myinput2")
+ assert.Contains(t, dispatchPayload.Inputs, "myinput3")
+ assert.Equal(t, "val0", dispatchPayload.Inputs["myinput"])
+ assert.Equal(t, "def2", dispatchPayload.Inputs["myinput2"])
+ assert.Equal(t, "true", dispatchPayload.Inputs["myinput3"])
+ })
+}
+
+func TestWorkflowDispatchPublicApiWithInputsNonDefaultBranchJSON(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ session := loginUser(t, user2.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // create the repo
+ repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
+ Name: "workflow-dispatch-event",
+ Description: "test workflow-dispatch ci event",
+ AutoInit: true,
+ Gitignores: "Go",
+ License: "MIT",
+ Readme: "Default",
+ DefaultBranch: "main",
+ IsPrivate: false,
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, repo)
+
+ // add workflow file to the repo
+ addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "create",
+ TreePath: ".gitea/workflows/dispatch.yml",
+ ContentReader: strings.NewReader(`
+on:
+ workflow_dispatch
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo helloworld
+`),
+ },
+ },
+ Message: "add workflow",
+ OldBranch: "main",
+ NewBranch: "main",
+ Author: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Committer: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, addWorkflowToBaseResp)
+
+ // add workflow file to the repo
+ addWorkflowToBaseResp, err = files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "update",
+ TreePath: ".gitea/workflows/dispatch.yml",
+ ContentReader: strings.NewReader(`
+on:
+ workflow_dispatch: { inputs: { myinput: { default: def }, myinput2: { default: def2 }, myinput3: { type: boolean, default: false } } }
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo helloworld
+`),
+ },
+ },
+ Message: "add workflow",
+ OldBranch: "main",
+ NewBranch: "dispatch",
+ Author: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Committer: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, addWorkflowToBaseResp)
+
+ // Get the commit ID of the dispatch branch
+ gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo)
+ assert.NoError(t, err)
+ defer gitRepo.Close()
+ commit, err := gitRepo.GetBranchCommit("dispatch")
+ assert.NoError(t, err)
+ inputs := &api.CreateActionWorkflowDispatch{
+ Ref: "refs/heads/dispatch",
+ Inputs: map[string]string{
+ "myinput": "val0",
+ "myinput3": "true",
+ },
+ }
+ req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/workflows/dispatch.yml/dispatches", repo.FullName()), inputs).
+ AddTokenAuth(token)
+ _ = MakeRequest(t, req, http.StatusNoContent)
+
+ run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
+ Title: "add workflow",
+ RepoID: repo.ID,
+ Repo: repo,
+ Event: "workflow_dispatch",
+ Ref: "refs/heads/dispatch",
+ WorkflowID: "dispatch.yml",
+ CommitSHA: commit.ID.String(),
+ })
+ assert.NotNil(t, run)
+ dispatchPayload := &api.WorkflowDispatchPayload{}
+ err = json.Unmarshal([]byte(run.EventPayload), dispatchPayload)
+ assert.NoError(t, err)
+ assert.Contains(t, dispatchPayload.Inputs, "myinput")
+ assert.Contains(t, dispatchPayload.Inputs, "myinput2")
+ assert.Contains(t, dispatchPayload.Inputs, "myinput3")
+ assert.Equal(t, "val0", dispatchPayload.Inputs["myinput"])
+ assert.Equal(t, "def2", dispatchPayload.Inputs["myinput2"])
+ assert.Equal(t, "true", dispatchPayload.Inputs["myinput3"])
+ })
+}
+
+func TestWorkflowApi(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ session := loginUser(t, user2.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // create the repo
+ repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
+ Name: "workflow-api",
+ Description: "test workflow apis",
+ AutoInit: true,
+ Gitignores: "Go",
+ License: "MIT",
+ Readme: "Default",
+ DefaultBranch: "main",
+ IsPrivate: false,
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, repo)
+
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/workflows", repo.FullName())).
+ AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ workflows := &api.ActionWorkflowResponse{}
+ json.NewDecoder(resp.Body).Decode(workflows)
+ assert.Empty(t, workflows.Workflows)
+
+ // add workflow file to the repo
+ addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "create",
+ TreePath: ".gitea/workflows/dispatch.yml",
+ ContentReader: strings.NewReader(`
+on:
+ workflow_dispatch: { inputs: { myinput: { default: def }, myinput2: { default: def2 }, myinput3: { type: boolean, default: false } } }
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo helloworld
+`),
+ },
+ },
+ Message: "add workflow",
+ OldBranch: "main",
+ NewBranch: "main",
+ Author: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Committer: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, addWorkflowToBaseResp)
+
+ req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/workflows", repo.FullName())).
+ AddTokenAuth(token)
+ resp = MakeRequest(t, req, http.StatusOK)
+ json.NewDecoder(resp.Body).Decode(workflows)
+ assert.Len(t, workflows.Workflows, 1)
+ assert.Equal(t, "dispatch.yml", workflows.Workflows[0].Name)
+ assert.Equal(t, ".gitea/workflows/dispatch.yml", workflows.Workflows[0].Path)
+ assert.Equal(t, ".gitea/workflows/dispatch.yml", workflows.Workflows[0].Path)
+ assert.Equal(t, "active", workflows.Workflows[0].State)
+
+ // Use a hardcoded api path
+ req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/workflows/%s", repo.FullName(), workflows.Workflows[0].ID)).
+ AddTokenAuth(token)
+ resp = MakeRequest(t, req, http.StatusOK)
+ workflow := &api.ActionWorkflow{}
+ json.NewDecoder(resp.Body).Decode(workflow)
+ assert.Equal(t, workflows.Workflows[0].ID, workflow.ID)
+ assert.Equal(t, workflows.Workflows[0].Path, workflow.Path)
+ assert.Equal(t, workflows.Workflows[0].URL, workflow.URL)
+ assert.Equal(t, workflows.Workflows[0].HTMLURL, workflow.HTMLURL)
+ assert.Equal(t, workflows.Workflows[0].Name, workflow.Name)
+ assert.Equal(t, workflows.Workflows[0].State, workflow.State)
+
+ // Use the provided url instead of the hardcoded one
+ req = NewRequest(t, "GET", workflows.Workflows[0].URL).
+ AddTokenAuth(token)
+ resp = MakeRequest(t, req, http.StatusOK)
+ workflow = &api.ActionWorkflow{}
+ json.NewDecoder(resp.Body).Decode(workflow)
+ assert.Equal(t, workflows.Workflows[0].ID, workflow.ID)
+ assert.Equal(t, workflows.Workflows[0].Path, workflow.Path)
+ assert.Equal(t, workflows.Workflows[0].URL, workflow.URL)
+ assert.Equal(t, workflows.Workflows[0].HTMLURL, workflow.HTMLURL)
+ assert.Equal(t, workflows.Workflows[0].Name, workflow.Name)
+ assert.Equal(t, workflows.Workflows[0].State, workflow.State)
+
+ // Disable the workflow
+ req = NewRequest(t, "PUT", workflows.Workflows[0].URL+"/disable").
+ AddTokenAuth(token)
+ _ = MakeRequest(t, req, http.StatusNoContent)
+
+ // Use the provided url instead of the hardcoded one
+ req = NewRequest(t, "GET", workflows.Workflows[0].URL).
+ AddTokenAuth(token)
+ resp = MakeRequest(t, req, http.StatusOK)
+ workflow = &api.ActionWorkflow{}
+ json.NewDecoder(resp.Body).Decode(workflow)
+ assert.Equal(t, workflows.Workflows[0].ID, workflow.ID)
+ assert.Equal(t, workflows.Workflows[0].Path, workflow.Path)
+ assert.Equal(t, workflows.Workflows[0].URL, workflow.URL)
+ assert.Equal(t, workflows.Workflows[0].HTMLURL, workflow.HTMLURL)
+ assert.Equal(t, workflows.Workflows[0].Name, workflow.Name)
+ assert.Equal(t, "disabled_manually", workflow.State)
+
+ inputs := &api.CreateActionWorkflowDispatch{
+ Ref: "main",
+ Inputs: map[string]string{
+ "myinput": "val0",
+ "myinput3": "true",
+ },
+ }
+ // Since the workflow is disabled, so the response code is 403 forbidden
+ req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/workflows/dispatch.yml/dispatches", repo.FullName()), inputs).
+ AddTokenAuth(token)
+ _ = MakeRequest(t, req, http.StatusForbidden)
+
+ // Enable the workflow again
+ req = NewRequest(t, "PUT", workflows.Workflows[0].URL+"/enable").
+ AddTokenAuth(token)
+ _ = MakeRequest(t, req, http.StatusNoContent)
+
+ // Use the provided url instead of the hardcoded one
+ req = NewRequest(t, "GET", workflows.Workflows[0].URL).
+ AddTokenAuth(token)
+ resp = MakeRequest(t, req, http.StatusOK)
+ workflow = &api.ActionWorkflow{}
+ json.NewDecoder(resp.Body).Decode(workflow)
+ assert.Equal(t, workflows.Workflows[0].ID, workflow.ID)
+ assert.Equal(t, workflows.Workflows[0].Path, workflow.Path)
+ assert.Equal(t, workflows.Workflows[0].URL, workflow.URL)
+ assert.Equal(t, workflows.Workflows[0].HTMLURL, workflow.HTMLURL)
+ assert.Equal(t, workflows.Workflows[0].Name, workflow.Name)
+ assert.Equal(t, workflows.Workflows[0].State, workflow.State)
+
+ req = NewRequest(t, "GET", workflows.Workflows[0].URL).
+ AddTokenAuth(token)
+ resp = MakeRequest(t, req, http.StatusOK)
+ workflow = &api.ActionWorkflow{}
+ json.NewDecoder(resp.Body).Decode(workflow)
+ assert.Equal(t, workflows.Workflows[0].ID, workflow.ID)
+ assert.Equal(t, workflows.Workflows[0].Path, workflow.Path)
+ assert.Equal(t, workflows.Workflows[0].URL, workflow.URL)
+ assert.Equal(t, workflows.Workflows[0].HTMLURL, workflow.HTMLURL)
+ assert.Equal(t, workflows.Workflows[0].Name, workflow.Name)
+ assert.Equal(t, workflows.Workflows[0].State, workflow.State)
+
+ // Get the commit ID of the default branch
+ gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo)
+ assert.NoError(t, err)
+ defer gitRepo.Close()
+ branch, err := git_model.GetBranch(db.DefaultContext, repo.ID, repo.DefaultBranch)
+ assert.NoError(t, err)
+ inputs = &api.CreateActionWorkflowDispatch{
+ Ref: "main",
+ Inputs: map[string]string{
+ "myinput": "val0",
+ "myinput3": "true",
+ },
+ }
+ req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/actions/workflows/dispatch.yml/dispatches", repo.FullName()), inputs).
+ AddTokenAuth(token)
+ _ = MakeRequest(t, req, http.StatusNoContent)
+
+ run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
+ Title: "add workflow",
+ RepoID: repo.ID,
+ Event: "workflow_dispatch",
+ Ref: "refs/heads/main",
+ WorkflowID: "dispatch.yml",
+ CommitSHA: branch.CommitID,
+ })
+ assert.NotNil(t, run)
+ dispatchPayload := &api.WorkflowDispatchPayload{}
+ err = json.Unmarshal([]byte(run.EventPayload), dispatchPayload)
+ assert.NoError(t, err)
+ assert.Contains(t, dispatchPayload.Inputs, "myinput")
+ assert.Contains(t, dispatchPayload.Inputs, "myinput2")
+ assert.Contains(t, dispatchPayload.Inputs, "myinput3")
+ assert.Equal(t, "val0", dispatchPayload.Inputs["myinput"])
+ assert.Equal(t, "def2", dispatchPayload.Inputs["myinput2"])
+ assert.Equal(t, "true", dispatchPayload.Inputs["myinput3"])
+ })
+}
+
+func TestClosePullRequestWithPath(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ // user2 is the owner of the base repo
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ user2Token := getTokenForLoggedInUser(t, loginUser(t, user2.Name), auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+ // user4 is the owner of the fork repo
+ user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
+ user4Token := getTokenForLoggedInUser(t, loginUser(t, user4.Name), auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+
+ // create the base repo
+ apiBaseRepo := createActionsTestRepo(t, user2Token, "close-pull-request-with-path", false)
+ baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiBaseRepo.ID})
+ user2APICtx := NewAPITestContext(t, baseRepo.OwnerName, baseRepo.Name, auth_model.AccessTokenScopeWriteRepository)
+
+ // init the workflow
+ wfTreePath := ".gitea/workflows/pull.yml"
+ wfFileContent := `name: Pull Request
+on:
+ pull_request:
+ types:
+ - closed
+ paths:
+ - 'app/**'
+jobs:
+ echo:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo 'Hello World'
+`
+ opts1 := getWorkflowCreateFileOptions(user2, baseRepo.DefaultBranch, "create "+wfTreePath, wfFileContent)
+ createWorkflowFile(t, user2Token, baseRepo.OwnerName, baseRepo.Name, wfTreePath, opts1)
+
+ // user4 forks the repo
+ req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks", baseRepo.OwnerName, baseRepo.Name),
+ &api.CreateForkOption{
+ Name: util.ToPointer("close-pull-request-with-path-fork"),
+ }).AddTokenAuth(user4Token)
+ resp := MakeRequest(t, req, http.StatusAccepted)
+ var apiForkRepo api.Repository
+ DecodeJSON(t, resp, &apiForkRepo)
+ forkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiForkRepo.ID})
+ user4APICtx := NewAPITestContext(t, user4.Name, forkRepo.Name, auth_model.AccessTokenScopeWriteRepository)
+
+ // user4 creates a pull request to add file "app/main.go"
+ doAPICreateFile(user4APICtx, "app/main.go", &api.CreateFileOptions{
+ FileOptions: api.FileOptions{
+ NewBranchName: "user4/add-main",
+ Message: "create main.go",
+ Author: api.Identity{
+ Name: user4.Name,
+ Email: user4.Email,
+ },
+ Committer: api.Identity{
+ Name: user4.Name,
+ Email: user4.Email,
+ },
+ Dates: api.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ },
+ ContentBase64: base64.StdEncoding.EncodeToString([]byte("// main.go")),
+ })(t)
+ apiPull, err := doAPICreatePullRequest(user4APICtx, baseRepo.OwnerName, baseRepo.Name, baseRepo.DefaultBranch, user4.Name+":user4/add-main")(t)
+ assert.NoError(t, err)
+
+ doAPIMergePullRequest(user2APICtx, baseRepo.OwnerName, baseRepo.Name, apiPull.Index)(t)
+
+ pullRequest := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: apiPull.ID})
+
+ // load and compare ActionRun
+ assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: baseRepo.ID}))
+ actionRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{RepoID: baseRepo.ID})
+ assert.Equal(t, actions_module.GithubEventPullRequest, actionRun.TriggerEvent)
+ assert.Equal(t, pullRequest.MergedCommitID, actionRun.CommitSHA)
+ })
+}
+
+func TestActionRunNameWithContextVariables(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+
+ // create the repo
+ repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
+ Name: "action-run-name-with-variables",
+ Description: "test action run name",
+ AutoInit: true,
+ Gitignores: "Go",
+ License: "MIT",
+ Readme: "Default",
+ DefaultBranch: "main",
+ IsPrivate: false,
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, repo)
+
+ // add workflow file to the repo
+ addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "create",
+ TreePath: ".gitea/workflows/runname.yml",
+ ContentReader: strings.NewReader(`name: test
+on:
+ [create,delete]
+run-name: ${{ gitea.actor }} is running this workflow
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo helloworld
+`),
+ },
+ },
+ Message: "add workflow with run-name",
+ OldBranch: "main",
+ NewBranch: "main",
+ Author: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Committer: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, addWorkflowToBaseResp)
+
+ // Get the commit ID of the default branch
+ gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo)
+ assert.NoError(t, err)
+ defer gitRepo.Close()
+ branch, err := git_model.GetBranch(db.DefaultContext, repo.ID, repo.DefaultBranch)
+ assert.NoError(t, err)
+
+ // create a branch
+ err = repo_service.CreateNewBranchFromCommit(db.DefaultContext, user2, repo, gitRepo, branch.CommitID, "test-action-run-name-with-variables")
+ assert.NoError(t, err)
+ run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
+ Title: user2.LoginName + " is running this workflow",
+ RepoID: repo.ID,
+ Event: "create",
+ Ref: "refs/heads/test-action-run-name-with-variables",
+ WorkflowID: "runname.yml",
+ CommitSHA: branch.CommitID,
+ })
+ assert.NotNil(t, run)
+ })
+}
+
+func TestActionRunName(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+
+ // create the repo
+ repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
+ Name: "action-run-name",
+ Description: "test action run-name",
+ AutoInit: true,
+ Gitignores: "Go",
+ License: "MIT",
+ Readme: "Default",
+ DefaultBranch: "main",
+ IsPrivate: false,
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, repo)
+
+ // add workflow file to the repo
+ addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "create",
+ TreePath: ".gitea/workflows/runname.yml",
+ ContentReader: strings.NewReader(`name: test
+on:
+ [create,delete]
+run-name: run name without variables
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo helloworld
+`),
+ },
+ },
+ Message: "add workflow with run name",
+ OldBranch: "main",
+ NewBranch: "main",
+ Author: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Committer: &files_service.IdentityOptions{
+ GitUserName: user2.Name,
+ GitUserEmail: user2.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, addWorkflowToBaseResp)
+
+ // Get the commit ID of the default branch
+ gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo)
+ assert.NoError(t, err)
+ defer gitRepo.Close()
+ branch, err := git_model.GetBranch(db.DefaultContext, repo.ID, repo.DefaultBranch)
+ assert.NoError(t, err)
+
+ // create a branch
+ err = repo_service.CreateNewBranchFromCommit(db.DefaultContext, user2, repo, gitRepo, branch.CommitID, "test-action-run-name")
+ assert.NoError(t, err)
+ run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{
+ Title: "run name without variables",
+ RepoID: repo.ID,
+ Event: "create",
+ Ref: "refs/heads/test-action-run-name",
+ WorkflowID: "runname.yml",
+ CommitSHA: branch.CommitID,
+ })
+ assert.NotNil(t, run)
+ })
+}
diff --git a/tests/integration/actions_variables_test.go b/tests/integration/actions_variables_test.go
new file mode 100644
index 0000000000..c0d0bd371b
--- /dev/null
+++ b/tests/integration/actions_variables_test.go
@@ -0,0 +1,148 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+
+ actions_model "code.gitea.io/gitea/models/actions"
+ "code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/tests"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestActionsVariables(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ ctx := t.Context()
+
+ require.NoError(t, db.DeleteAllRecords("action_variable"))
+
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ _, _ = actions_model.InsertVariable(ctx, user2.ID, 0, "VAR", "user2-var", "user2-var-description")
+ user2Var := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionVariable{OwnerID: user2.ID, Name: "VAR"})
+ userWebURL := "/user/settings/actions/variables"
+
+ org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3, Type: user_model.UserTypeOrganization})
+ _, _ = actions_model.InsertVariable(ctx, org3.ID, 0, "VAR", "org3-var", "org3-var-description")
+ org3Var := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionVariable{OwnerID: org3.ID, Name: "VAR"})
+ orgWebURL := "/org/org3/settings/actions/variables"
+
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ _, _ = actions_model.InsertVariable(ctx, 0, repo1.ID, "VAR", "repo1-var", "repo1-var-description")
+ repo1Var := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionVariable{RepoID: repo1.ID, Name: "VAR"})
+ repoWebURL := "/user2/repo1/settings/actions/variables"
+
+ _, _ = actions_model.InsertVariable(ctx, 0, 0, "VAR", "global-var", "global-var-description")
+ globalVar := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionVariable{Name: "VAR", Data: "global-var"})
+ adminWebURL := "/-/admin/actions/variables"
+
+ sessionAdmin := loginUser(t, "user1")
+ sessionUser2 := loginUser(t, user2.Name)
+
+ doUpdate := func(t *testing.T, sess *TestSession, baseURL string, id int64, data string, expectedStatus int) {
+ req := NewRequestWithValues(t, "POST", fmt.Sprintf("%s/%d/edit", baseURL, id), map[string]string{
+ "_csrf": GetUserCSRFToken(t, sess),
+ "name": "VAR",
+ "data": data,
+ })
+ sess.MakeRequest(t, req, expectedStatus)
+ }
+
+ doDelete := func(t *testing.T, sess *TestSession, baseURL string, id int64, expectedStatus int) {
+ req := NewRequestWithValues(t, "POST", fmt.Sprintf("%s/%d/delete", baseURL, id), map[string]string{
+ "_csrf": GetUserCSRFToken(t, sess),
+ })
+ sess.MakeRequest(t, req, expectedStatus)
+ }
+
+ assertDenied := func(t *testing.T, sess *TestSession, baseURL string, id int64) {
+ doUpdate(t, sess, baseURL, id, "ChangedData", http.StatusNotFound)
+ doDelete(t, sess, baseURL, id, http.StatusNotFound)
+ v := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionVariable{ID: id})
+ assert.Contains(t, v.Data, "-var")
+ }
+
+ assertSuccess := func(t *testing.T, sess *TestSession, baseURL string, id int64) {
+ doUpdate(t, sess, baseURL, id, "ChangedData", http.StatusOK)
+ v := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionVariable{ID: id})
+ assert.Equal(t, "ChangedData", v.Data)
+ doDelete(t, sess, baseURL, id, http.StatusOK)
+ unittest.AssertNotExistsBean(t, &actions_model.ActionVariable{ID: id})
+ }
+
+ t.Run("UpdateUserVar", func(t *testing.T) {
+ theVar := user2Var
+ t.Run("FromOrg", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, orgWebURL, theVar.ID)
+ })
+ t.Run("FromRepo", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, repoWebURL, theVar.ID)
+ })
+ t.Run("FromAdmin", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, adminWebURL, theVar.ID)
+ })
+ })
+
+ t.Run("UpdateOrgVar", func(t *testing.T) {
+ theVar := org3Var
+ t.Run("FromRepo", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, repoWebURL, theVar.ID)
+ })
+ t.Run("FromUser", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, userWebURL, theVar.ID)
+ })
+ t.Run("FromAdmin", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, adminWebURL, theVar.ID)
+ })
+ })
+
+ t.Run("UpdateRepoVar", func(t *testing.T) {
+ theVar := repo1Var
+ t.Run("FromOrg", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, orgWebURL, theVar.ID)
+ })
+ t.Run("FromUser", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, userWebURL, theVar.ID)
+ })
+ t.Run("FromAdmin", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, adminWebURL, theVar.ID)
+ })
+ })
+
+ t.Run("UpdateGlobalVar", func(t *testing.T) {
+ theVar := globalVar
+ t.Run("FromOrg", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, orgWebURL, theVar.ID)
+ })
+ t.Run("FromUser", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, userWebURL, theVar.ID)
+ })
+ t.Run("FromRepo", func(t *testing.T) {
+ assertDenied(t, sessionAdmin, repoWebURL, theVar.ID)
+ })
+ })
+
+ t.Run("UpdateSuccess", func(t *testing.T) {
+ t.Run("User", func(t *testing.T) {
+ assertSuccess(t, sessionUser2, userWebURL, user2Var.ID)
+ })
+ t.Run("Org", func(t *testing.T) {
+ assertSuccess(t, sessionAdmin, orgWebURL, org3Var.ID)
+ })
+ t.Run("Repo", func(t *testing.T) {
+ assertSuccess(t, sessionUser2, repoWebURL, repo1Var.ID)
+ })
+ t.Run("Admin", func(t *testing.T) {
+ assertSuccess(t, sessionAdmin, adminWebURL, globalVar.ID)
+ })
+ })
+}
diff --git a/tests/integration/admin_user_test.go b/tests/integration/admin_user_test.go
index d5d7e70bc7..95e03ab750 100644
--- a/tests/integration/admin_user_test.go
+++ b/tests/integration/admin_user_test.go
@@ -4,6 +4,7 @@
package integration
import (
+ "fmt"
"net/http"
"strconv"
"testing"
@@ -72,12 +73,37 @@ func TestAdminDeleteUser(t *testing.T) {
session := loginUser(t, "user1")
- csrf := GetUserCSRFToken(t, session)
- req := NewRequestWithValues(t, "POST", "/-/admin/users/8/delete", map[string]string{
- "_csrf": csrf,
- })
- session.MakeRequest(t, req, http.StatusSeeOther)
-
- assertUserDeleted(t, 8)
- unittest.CheckConsistencyFor(t, &user_model.User{})
+ usersToDelete := []struct {
+ userID int64
+ purge bool
+ }{
+ {
+ userID: 2,
+ purge: true,
+ },
+ {
+ userID: 8,
+ },
+ }
+
+ for _, entry := range usersToDelete {
+ t.Run(fmt.Sprintf("DeleteUser%d", entry.userID), func(t *testing.T) {
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: entry.userID})
+ assert.NotNil(t, user)
+
+ var query string
+ if entry.purge {
+ query = "?purge=true"
+ }
+
+ csrf := GetUserCSRFToken(t, session)
+ req := NewRequestWithValues(t, "POST", fmt.Sprintf("/-/admin/users/%d/delete%s", entry.userID, query), map[string]string{
+ "_csrf": csrf,
+ })
+ session.MakeRequest(t, req, http.StatusSeeOther)
+
+ assertUserDeleted(t, entry.userID)
+ unittest.CheckConsistencyFor(t, &user_model.User{})
+ })
+ }
}
diff --git a/tests/integration/api_actions_artifact_v4_test.go b/tests/integration/api_actions_artifact_v4_test.go
index 8821472801..3db8bbb82e 100644
--- a/tests/integration/api_actions_artifact_v4_test.go
+++ b/tests/integration/api_actions_artifact_v4_test.go
@@ -8,13 +8,20 @@ import (
"crypto/sha256"
"encoding/hex"
"encoding/xml"
+ "fmt"
"io"
"net/http"
"strings"
"testing"
"time"
+ auth_model "code.gitea.io/gitea/models/auth"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/storage"
+ api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/actions"
actions_service "code.gitea.io/gitea/services/actions"
@@ -334,6 +341,206 @@ func TestActionsArtifactV4DownloadSingle(t *testing.T) {
assert.Equal(t, body, resp.Body.String())
}
+func TestActionsArtifactV4RunDownloadSinglePublicApi(t *testing.T) {
+ defer prepareTestEnvActionsArtifacts(t)()
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ session := loginUser(t, user.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // confirm artifact can be listed and found by name
+ req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/runs/792/artifacts?name=artifact-v4-download", repo.FullName()), nil).
+ AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ var listResp api.ActionArtifactsResponse
+ err := json.Unmarshal(resp.Body.Bytes(), &listResp)
+ assert.NoError(t, err)
+ assert.NotEmpty(t, listResp.Entries[0].ArchiveDownloadURL)
+ assert.Equal(t, "artifact-v4-download", listResp.Entries[0].Name)
+
+ // confirm artifact blob storage url can be retrieved
+ req = NewRequestWithBody(t, "GET", listResp.Entries[0].ArchiveDownloadURL, nil).
+ AddTokenAuth(token)
+
+ resp = MakeRequest(t, req, http.StatusFound)
+
+ // confirm artifact can be downloaded and has expected content
+ req = NewRequestWithBody(t, "GET", resp.Header().Get("Location"), nil).
+ AddTokenAuth(token)
+ resp = MakeRequest(t, req, http.StatusOK)
+
+ body := strings.Repeat("D", 1024)
+ assert.Equal(t, body, resp.Body.String())
+}
+
+func TestActionsArtifactV4DownloadSinglePublicApi(t *testing.T) {
+ defer prepareTestEnvActionsArtifacts(t)()
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ session := loginUser(t, user.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // confirm artifact can be listed and found by name
+ req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts?name=artifact-v4-download", repo.FullName()), nil).
+ AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ var listResp api.ActionArtifactsResponse
+ err := json.Unmarshal(resp.Body.Bytes(), &listResp)
+ assert.NoError(t, err)
+ assert.NotEmpty(t, listResp.Entries[0].ArchiveDownloadURL)
+ assert.Equal(t, "artifact-v4-download", listResp.Entries[0].Name)
+
+ // confirm artifact blob storage url can be retrieved
+ req = NewRequestWithBody(t, "GET", listResp.Entries[0].ArchiveDownloadURL, nil).
+ AddTokenAuth(token)
+
+ resp = MakeRequest(t, req, http.StatusFound)
+
+ blobLocation := resp.Header().Get("Location")
+
+ // confirm artifact can be downloaded without token and has expected content
+ req = NewRequestWithBody(t, "GET", blobLocation, nil)
+ resp = MakeRequest(t, req, http.StatusOK)
+ body := strings.Repeat("D", 1024)
+ assert.Equal(t, body, resp.Body.String())
+
+ // confirm artifact can not be downloaded without query
+ req = NewRequestWithBody(t, "GET", blobLocation, nil)
+ req.URL.RawQuery = ""
+ _ = MakeRequest(t, req, http.StatusUnauthorized)
+}
+
+func TestActionsArtifactV4DownloadSinglePublicApiPrivateRepo(t *testing.T) {
+ defer prepareTestEnvActionsArtifacts(t)()
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ session := loginUser(t, user.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // confirm artifact can be listed and found by name
+ req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts?name=artifact-v4-download", repo.FullName()), nil).
+ AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ var listResp api.ActionArtifactsResponse
+ err := json.Unmarshal(resp.Body.Bytes(), &listResp)
+ assert.NoError(t, err)
+ assert.Equal(t, int64(23), listResp.Entries[0].ID)
+ assert.NotEmpty(t, listResp.Entries[0].ArchiveDownloadURL)
+ assert.Equal(t, "artifact-v4-download", listResp.Entries[0].Name)
+
+ // confirm artifact blob storage url can be retrieved
+ req = NewRequestWithBody(t, "GET", listResp.Entries[0].ArchiveDownloadURL, nil).
+ AddTokenAuth(token)
+
+ resp = MakeRequest(t, req, http.StatusFound)
+
+ blobLocation := resp.Header().Get("Location")
+ // confirm artifact can be downloaded without token and has expected content
+ req = NewRequestWithBody(t, "GET", blobLocation, nil)
+ resp = MakeRequest(t, req, http.StatusOK)
+ body := strings.Repeat("D", 1024)
+ assert.Equal(t, body, resp.Body.String())
+
+ // confirm artifact can not be downloaded without query
+ req = NewRequestWithBody(t, "GET", blobLocation, nil)
+ req.URL.RawQuery = ""
+ _ = MakeRequest(t, req, http.StatusUnauthorized)
+}
+
+func TestActionsArtifactV4ListAndGetPublicApi(t *testing.T) {
+ defer prepareTestEnvActionsArtifacts(t)()
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ session := loginUser(t, user.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // confirm artifact can be listed
+ req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts", repo.FullName()), nil).
+ AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ var listResp api.ActionArtifactsResponse
+ err := json.Unmarshal(resp.Body.Bytes(), &listResp)
+ assert.NoError(t, err)
+
+ for _, artifact := range listResp.Entries {
+ assert.Contains(t, artifact.URL, fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), artifact.ID))
+ assert.Contains(t, artifact.ArchiveDownloadURL, fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d/zip", repo.FullName(), artifact.ID))
+ req = NewRequestWithBody(t, "GET", listResp.Entries[0].URL, nil).
+ AddTokenAuth(token)
+
+ resp = MakeRequest(t, req, http.StatusOK)
+ var artifactResp api.ActionArtifact
+ err := json.Unmarshal(resp.Body.Bytes(), &artifactResp)
+ assert.NoError(t, err)
+
+ assert.Equal(t, artifact.ID, artifactResp.ID)
+ assert.Equal(t, artifact.Name, artifactResp.Name)
+ assert.Equal(t, artifact.SizeInBytes, artifactResp.SizeInBytes)
+ assert.Equal(t, artifact.URL, artifactResp.URL)
+ assert.Equal(t, artifact.ArchiveDownloadURL, artifactResp.ArchiveDownloadURL)
+ }
+}
+
+func TestActionsArtifactV4GetArtifactMismatchedRepoNotFound(t *testing.T) {
+ defer prepareTestEnvActionsArtifacts(t)()
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ session := loginUser(t, user.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // confirm artifacts of wrong repo is not visible
+ req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), 22), nil).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNotFound)
+}
+
+func TestActionsArtifactV4DownloadArtifactMismatchedRepoNotFound(t *testing.T) {
+ defer prepareTestEnvActionsArtifacts(t)()
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ session := loginUser(t, user.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // confirm artifacts of wrong repo is not visible
+ req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d/zip", repo.FullName(), 22), nil).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNotFound)
+}
+
+func TestActionsArtifactV4DownloadArtifactCorrectRepoFound(t *testing.T) {
+ defer prepareTestEnvActionsArtifacts(t)()
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ session := loginUser(t, user.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // confirm artifacts of correct repo is visible
+ req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d/zip", repo.FullName(), 22), nil).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusFound)
+}
+
+func TestActionsArtifactV4DownloadRawArtifactCorrectRepoMissingSignatureUnauthorized(t *testing.T) {
+ defer prepareTestEnvActionsArtifacts(t)()
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ session := loginUser(t, user.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // confirm cannot use the raw artifact endpoint even with a correct access token
+ req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d/zip/raw", repo.FullName(), 22), nil).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusUnauthorized)
+}
+
func TestActionsArtifactV4Delete(t *testing.T) {
defer prepareTestEnvActionsArtifacts(t)()
@@ -350,4 +557,72 @@ func TestActionsArtifactV4Delete(t *testing.T) {
var deleteResp actions.DeleteArtifactResponse
protojson.Unmarshal(resp.Body.Bytes(), &deleteResp)
assert.True(t, deleteResp.Ok)
+
+ // confirm artifact is no longer accessible by GetSignedArtifactURL
+ req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/GetSignedArtifactURL", toProtoJSON(&actions.GetSignedArtifactURLRequest{
+ Name: "artifact-v4-download",
+ WorkflowRunBackendId: "792",
+ WorkflowJobRunBackendId: "193",
+ })).
+ AddTokenAuth(token)
+ _ = MakeRequest(t, req, http.StatusNotFound)
+
+ // confirm artifact is no longer enumerateable by ListArtifacts and returns length == 0 without error
+ req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/ListArtifacts", toProtoJSON(&actions.ListArtifactsRequest{
+ NameFilter: wrapperspb.String("artifact-v4-download"),
+ WorkflowRunBackendId: "792",
+ WorkflowJobRunBackendId: "193",
+ })).AddTokenAuth(token)
+ resp = MakeRequest(t, req, http.StatusOK)
+ var listResp actions.ListArtifactsResponse
+ protojson.Unmarshal(resp.Body.Bytes(), &listResp)
+ assert.Empty(t, listResp.Artifacts)
+}
+
+func TestActionsArtifactV4DeletePublicApi(t *testing.T) {
+ defer prepareTestEnvActionsArtifacts(t)()
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ session := loginUser(t, user.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // confirm artifacts exists
+ req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), 22), nil).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusOK)
+
+ // delete artifact by id
+ req = NewRequestWithBody(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), 22), nil).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+
+ // confirm artifacts has been deleted
+ req = NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), 22), nil).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNotFound)
+}
+
+func TestActionsArtifactV4DeletePublicApiNotAllowedReadScope(t *testing.T) {
+ defer prepareTestEnvActionsArtifacts(t)()
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ session := loginUser(t, user.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
+
+ // confirm artifacts exists
+ req := NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), 22), nil).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusOK)
+
+ // try delete artifact by id
+ req = NewRequestWithBody(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), 22), nil).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusForbidden)
+
+ // confirm artifacts has not been deleted
+ req = NewRequestWithBody(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/artifacts/%d", repo.FullName(), 22), nil).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusOK)
}
diff --git a/tests/integration/api_actions_delete_run_test.go b/tests/integration/api_actions_delete_run_test.go
new file mode 100644
index 0000000000..5b41702c57
--- /dev/null
+++ b/tests/integration/api_actions_delete_run_test.go
@@ -0,0 +1,98 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/json"
+ api "code.gitea.io/gitea/modules/structs"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestAPIActionsDeleteRunCheckPermission(t *testing.T) {
+ defer prepareTestEnvActionsArtifacts(t)()
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ session := loginUser(t, user.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+ testAPIActionsDeleteRun(t, repo, token, http.StatusNotFound)
+}
+
+func TestAPIActionsDeleteRun(t *testing.T) {
+ defer prepareTestEnvActionsArtifacts(t)()
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ session := loginUser(t, user.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ testAPIActionsDeleteRunListArtifacts(t, repo, token, 2)
+ testAPIActionsDeleteRunListTasks(t, repo, token, true)
+ testAPIActionsDeleteRun(t, repo, token, http.StatusNoContent)
+
+ testAPIActionsDeleteRunListArtifacts(t, repo, token, 0)
+ testAPIActionsDeleteRunListTasks(t, repo, token, false)
+ testAPIActionsDeleteRun(t, repo, token, http.StatusNotFound)
+}
+
+func TestAPIActionsDeleteRunRunning(t *testing.T) {
+ defer prepareTestEnvActionsArtifacts(t)()
+
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ session := loginUser(t, user.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/actions/runs/793", repo.FullName())).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusBadRequest)
+}
+
+func testAPIActionsDeleteRun(t *testing.T, repo *repo_model.Repository, token string, expected int) {
+ req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795", repo.FullName())).
+ AddTokenAuth(token)
+ MakeRequest(t, req, expected)
+}
+
+func testAPIActionsDeleteRunListArtifacts(t *testing.T, repo *repo_model.Repository, token string, artifacts int) {
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/runs/795/artifacts", repo.FullName())).
+ AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ var listResp api.ActionArtifactsResponse
+ err := json.Unmarshal(resp.Body.Bytes(), &listResp)
+ assert.NoError(t, err)
+ assert.Len(t, listResp.Entries, artifacts)
+}
+
+func testAPIActionsDeleteRunListTasks(t *testing.T, repo *repo_model.Repository, token string, expected bool) {
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/actions/tasks", repo.FullName())).
+ AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ var listResp api.ActionTaskResponse
+ err := json.Unmarshal(resp.Body.Bytes(), &listResp)
+ assert.NoError(t, err)
+ findTask1 := false
+ findTask2 := false
+ for _, entry := range listResp.Entries {
+ if entry.ID == 53 {
+ findTask1 = true
+ continue
+ }
+ if entry.ID == 54 {
+ findTask2 = true
+ continue
+ }
+ }
+ assert.Equal(t, expected, findTask1)
+ assert.Equal(t, expected, findTask2)
+}
diff --git a/tests/integration/api_actions_runner_test.go b/tests/integration/api_actions_runner_test.go
new file mode 100644
index 0000000000..fb9ba5b0c2
--- /dev/null
+++ b/tests/integration/api_actions_runner_test.go
@@ -0,0 +1,342 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "fmt"
+ "net/http"
+ "slices"
+ "testing"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/tests"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestAPIActionsRunner(t *testing.T) {
+ t.Run("AdminRunner", testActionsRunnerAdmin)
+ t.Run("UserRunner", testActionsRunnerUser)
+ t.Run("OwnerRunner", testActionsRunnerOwner)
+ t.Run("RepoRunner", testActionsRunnerRepo)
+}
+
+func testActionsRunnerAdmin(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ adminUsername := "user1"
+ token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
+ req := NewRequest(t, "POST", "/api/v1/admin/actions/runners/registration-token").AddTokenAuth(token)
+ tokenResp := MakeRequest(t, req, http.StatusOK)
+ var registrationToken struct {
+ Token string `json:"token"`
+ }
+ DecodeJSON(t, tokenResp, &registrationToken)
+ assert.NotEmpty(t, registrationToken.Token)
+
+ req = NewRequest(t, "GET", "/api/v1/admin/actions/runners").AddTokenAuth(token)
+ runnerListResp := MakeRequest(t, req, http.StatusOK)
+ runnerList := api.ActionRunnersResponse{}
+ DecodeJSON(t, runnerListResp, &runnerList)
+
+ idx := slices.IndexFunc(runnerList.Entries, func(e *api.ActionRunner) bool { return e.ID == 34349 })
+ require.NotEqual(t, -1, idx)
+ expectedRunner := runnerList.Entries[idx]
+ assert.Equal(t, "runner_to_be_deleted", expectedRunner.Name)
+ assert.False(t, expectedRunner.Ephemeral)
+ assert.Len(t, expectedRunner.Labels, 2)
+ assert.Equal(t, "runner_to_be_deleted", expectedRunner.Labels[0].Name)
+ assert.Equal(t, "linux", expectedRunner.Labels[1].Name)
+
+ // Verify all returned runners can be requested and deleted
+ for _, runnerEntry := range runnerList.Entries {
+ // Verify get the runner by id
+ req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/admin/actions/runners/%d", runnerEntry.ID)).AddTokenAuth(token)
+ runnerResp := MakeRequest(t, req, http.StatusOK)
+
+ runner := api.ActionRunner{}
+ DecodeJSON(t, runnerResp, &runner)
+
+ assert.Equal(t, runnerEntry.Name, runner.Name)
+ assert.Equal(t, runnerEntry.ID, runner.ID)
+ assert.Equal(t, runnerEntry.Ephemeral, runner.Ephemeral)
+ assert.ElementsMatch(t, runnerEntry.Labels, runner.Labels)
+
+ req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/admin/actions/runners/%d", runnerEntry.ID)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+
+ // Verify runner deletion
+ req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/admin/actions/runners/%d", runnerEntry.ID)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNotFound)
+ }
+}
+
+func testActionsRunnerUser(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ userUsername := "user1"
+ token := getUserToken(t, userUsername, auth_model.AccessTokenScopeWriteUser)
+ req := NewRequest(t, "POST", "/api/v1/user/actions/runners/registration-token").AddTokenAuth(token)
+ tokenResp := MakeRequest(t, req, http.StatusOK)
+ var registrationToken struct {
+ Token string `json:"token"`
+ }
+ DecodeJSON(t, tokenResp, &registrationToken)
+ assert.NotEmpty(t, registrationToken.Token)
+
+ req = NewRequest(t, "GET", "/api/v1/user/actions/runners").AddTokenAuth(token)
+ runnerListResp := MakeRequest(t, req, http.StatusOK)
+ runnerList := api.ActionRunnersResponse{}
+ DecodeJSON(t, runnerListResp, &runnerList)
+
+ assert.Len(t, runnerList.Entries, 1)
+ assert.Equal(t, "runner_to_be_deleted-user", runnerList.Entries[0].Name)
+ assert.Equal(t, int64(34346), runnerList.Entries[0].ID)
+ assert.False(t, runnerList.Entries[0].Ephemeral)
+ assert.Len(t, runnerList.Entries[0].Labels, 2)
+ assert.Equal(t, "runner_to_be_deleted", runnerList.Entries[0].Labels[0].Name)
+ assert.Equal(t, "linux", runnerList.Entries[0].Labels[1].Name)
+
+ // Verify get the runner by id
+ req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
+ runnerResp := MakeRequest(t, req, http.StatusOK)
+
+ runner := api.ActionRunner{}
+ DecodeJSON(t, runnerResp, &runner)
+
+ assert.Equal(t, "runner_to_be_deleted-user", runner.Name)
+ assert.Equal(t, int64(34346), runner.ID)
+ assert.False(t, runner.Ephemeral)
+ assert.Len(t, runner.Labels, 2)
+ assert.Equal(t, "runner_to_be_deleted", runner.Labels[0].Name)
+ assert.Equal(t, "linux", runner.Labels[1].Name)
+
+ // Verify delete the runner by id
+ req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+
+ // Verify runner deletion
+ req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNotFound)
+}
+
+func testActionsRunnerOwner(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ t.Run("GetRunner", func(t *testing.T) {
+ userUsername := "user2"
+ token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadOrganization)
+ // Verify get the runner by id with read scope
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", 34347)).AddTokenAuth(token)
+ runnerResp := MakeRequest(t, req, http.StatusOK)
+
+ runner := api.ActionRunner{}
+ DecodeJSON(t, runnerResp, &runner)
+
+ assert.Equal(t, "runner_to_be_deleted-org", runner.Name)
+ assert.Equal(t, int64(34347), runner.ID)
+ assert.False(t, runner.Ephemeral)
+ assert.Len(t, runner.Labels, 2)
+ assert.Equal(t, "runner_to_be_deleted", runner.Labels[0].Name)
+ assert.Equal(t, "linux", runner.Labels[1].Name)
+ })
+
+ t.Run("Access", func(t *testing.T) {
+ userUsername := "user2"
+ token := getUserToken(t, userUsername, auth_model.AccessTokenScopeWriteOrganization)
+ req := NewRequest(t, "POST", "/api/v1/orgs/org3/actions/runners/registration-token").AddTokenAuth(token)
+ tokenResp := MakeRequest(t, req, http.StatusOK)
+ var registrationToken struct {
+ Token string `json:"token"`
+ }
+ DecodeJSON(t, tokenResp, &registrationToken)
+ assert.NotEmpty(t, registrationToken.Token)
+
+ req = NewRequest(t, "GET", "/api/v1/orgs/org3/actions/runners").AddTokenAuth(token)
+ runnerListResp := MakeRequest(t, req, http.StatusOK)
+ runnerList := api.ActionRunnersResponse{}
+ DecodeJSON(t, runnerListResp, &runnerList)
+
+ idx := slices.IndexFunc(runnerList.Entries, func(e *api.ActionRunner) bool { return e.ID == 34347 })
+ require.NotEqual(t, -1, idx)
+ expectedRunner := runnerList.Entries[idx]
+
+ require.NotNil(t, expectedRunner)
+ assert.Equal(t, "runner_to_be_deleted-org", expectedRunner.Name)
+ assert.Equal(t, int64(34347), expectedRunner.ID)
+ assert.False(t, expectedRunner.Ephemeral)
+ assert.Len(t, expectedRunner.Labels, 2)
+ assert.Equal(t, "runner_to_be_deleted", expectedRunner.Labels[0].Name)
+ assert.Equal(t, "linux", expectedRunner.Labels[1].Name)
+
+ // Verify get the runner by id
+ req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", expectedRunner.ID)).AddTokenAuth(token)
+ runnerResp := MakeRequest(t, req, http.StatusOK)
+
+ runner := api.ActionRunner{}
+ DecodeJSON(t, runnerResp, &runner)
+
+ assert.Equal(t, "runner_to_be_deleted-org", runner.Name)
+ assert.Equal(t, int64(34347), runner.ID)
+ assert.False(t, runner.Ephemeral)
+ assert.Len(t, runner.Labels, 2)
+ assert.Equal(t, "runner_to_be_deleted", runner.Labels[0].Name)
+ assert.Equal(t, "linux", runner.Labels[1].Name)
+
+ // Verify delete the runner by id
+ req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", expectedRunner.ID)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+
+ // Verify runner deletion
+ req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", expectedRunner.ID)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNotFound)
+ })
+
+ t.Run("DeleteReadScopeForbidden", func(t *testing.T) {
+ userUsername := "user2"
+ token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadOrganization)
+
+ // Verify delete the runner by id is forbidden with read scope
+ req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", 34347)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusForbidden)
+ })
+
+ t.Run("GetRepoScopeForbidden", func(t *testing.T) {
+ userUsername := "user2"
+ token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadRepository)
+ // Verify get the runner by id with read scope
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", 34347)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusForbidden)
+ })
+
+ t.Run("GetAdminRunner", func(t *testing.T) {
+ userUsername := "user2"
+ token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadOrganization)
+ // Verify get a runner by id of different entity is not found
+ // runner.EditableInContext(ownerID, repoID) false
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", 34349)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNotFound)
+ })
+
+ t.Run("DeleteAdminRunner", func(t *testing.T) {
+ userUsername := "user2"
+ token := getUserToken(t, userUsername, auth_model.AccessTokenScopeWriteOrganization)
+ // Verify delete a runner by id of different entity is not found
+ // runner.EditableInContext(ownerID, repoID) false
+ req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/orgs/org3/actions/runners/%d", 34349)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNotFound)
+ })
+}
+
+func testActionsRunnerRepo(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ t.Run("GetRunner", func(t *testing.T) {
+ userUsername := "user2"
+ token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadRepository)
+ // Verify get the runner by id with read scope
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", 34348)).AddTokenAuth(token)
+ runnerResp := MakeRequest(t, req, http.StatusOK)
+
+ runner := api.ActionRunner{}
+ DecodeJSON(t, runnerResp, &runner)
+
+ assert.Equal(t, "runner_to_be_deleted-repo1", runner.Name)
+ assert.Equal(t, int64(34348), runner.ID)
+ assert.False(t, runner.Ephemeral)
+ assert.Len(t, runner.Labels, 2)
+ assert.Equal(t, "runner_to_be_deleted", runner.Labels[0].Name)
+ assert.Equal(t, "linux", runner.Labels[1].Name)
+ })
+
+ t.Run("Access", func(t *testing.T) {
+ userUsername := "user2"
+ token := getUserToken(t, userUsername, auth_model.AccessTokenScopeWriteRepository)
+ req := NewRequest(t, "POST", "/api/v1/repos/user2/repo1/actions/runners/registration-token").AddTokenAuth(token)
+ tokenResp := MakeRequest(t, req, http.StatusOK)
+ var registrationToken struct {
+ Token string `json:"token"`
+ }
+ DecodeJSON(t, tokenResp, &registrationToken)
+ assert.NotEmpty(t, registrationToken.Token)
+
+ req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/actions/runners").AddTokenAuth(token)
+ runnerListResp := MakeRequest(t, req, http.StatusOK)
+ runnerList := api.ActionRunnersResponse{}
+ DecodeJSON(t, runnerListResp, &runnerList)
+
+ assert.Len(t, runnerList.Entries, 1)
+ assert.Equal(t, "runner_to_be_deleted-repo1", runnerList.Entries[0].Name)
+ assert.Equal(t, int64(34348), runnerList.Entries[0].ID)
+ assert.False(t, runnerList.Entries[0].Ephemeral)
+ assert.Len(t, runnerList.Entries[0].Labels, 2)
+ assert.Equal(t, "runner_to_be_deleted", runnerList.Entries[0].Labels[0].Name)
+ assert.Equal(t, "linux", runnerList.Entries[0].Labels[1].Name)
+
+ // Verify get the runner by id
+ req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
+ runnerResp := MakeRequest(t, req, http.StatusOK)
+
+ runner := api.ActionRunner{}
+ DecodeJSON(t, runnerResp, &runner)
+
+ assert.Equal(t, "runner_to_be_deleted-repo1", runner.Name)
+ assert.Equal(t, int64(34348), runner.ID)
+ assert.False(t, runner.Ephemeral)
+ assert.Len(t, runner.Labels, 2)
+ assert.Equal(t, "runner_to_be_deleted", runner.Labels[0].Name)
+ assert.Equal(t, "linux", runner.Labels[1].Name)
+
+ // Verify delete the runner by id
+ req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+
+ // Verify runner deletion
+ req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", runnerList.Entries[0].ID)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNotFound)
+ })
+
+ t.Run("DeleteReadScopeForbidden", func(t *testing.T) {
+ userUsername := "user2"
+ token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadRepository)
+
+ // Verify delete the runner by id is forbidden with read scope
+ req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", 34348)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusForbidden)
+ })
+
+ t.Run("GetOrganizationScopeForbidden", func(t *testing.T) {
+ userUsername := "user2"
+ token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadOrganization)
+ // Verify get the runner by id with read scope
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", 34348)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusForbidden)
+ })
+
+ t.Run("GetAdminRunnerNotFound", func(t *testing.T) {
+ userUsername := "user2"
+ token := getUserToken(t, userUsername, auth_model.AccessTokenScopeReadRepository)
+ // Verify get a runner by id of different entity is not found
+ // runner.EditableInContext(ownerID, repoID) false
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", 34349)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNotFound)
+ })
+
+ t.Run("DeleteAdminRunnerNotFound", func(t *testing.T) {
+ userUsername := "user2"
+ token := getUserToken(t, userUsername, auth_model.AccessTokenScopeWriteRepository)
+ // Verify delete a runner by id of different entity is not found
+ // runner.EditableInContext(ownerID, repoID) false
+ req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", 34349)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNotFound)
+ })
+
+ t.Run("DeleteAdminRunnerNotFoundUnknownID", func(t *testing.T) {
+ userUsername := "user2"
+ token := getUserToken(t, userUsername, auth_model.AccessTokenScopeWriteRepository)
+ // Verify delete a runner by unknown id is not found
+ req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/user2/repo1/actions/runners/%d", 4384797347934)).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNotFound)
+ })
+}
diff --git a/tests/integration/api_activitypub_person_test.go b/tests/integration/api_activitypub_person_test.go
index 42a2a09072..9bb1f2736e 100644
--- a/tests/integration/api_activitypub_person_test.go
+++ b/tests/integration/api_activitypub_person_test.go
@@ -4,32 +4,31 @@
package integration
import (
- "context"
"fmt"
"net/http"
"net/http/httptest"
- "net/url"
"testing"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/activitypub"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/routers"
+ "code.gitea.io/gitea/tests"
ap "github.com/go-ap/activitypub"
"github.com/stretchr/testify/assert"
)
func TestActivityPubPerson(t *testing.T) {
- setting.Federation.Enabled = true
- testWebRoutes = routers.NormalRoutes()
- defer func() {
- setting.Federation.Enabled = false
- testWebRoutes = routers.NormalRoutes()
- }()
+ defer tests.PrepareTestEnv(t)()
+ defer test.MockVariableValue(&setting.Federation.Enabled, true)()
+ defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
+
+ t.Run("ExistingPerson", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
- onGiteaRun(t, func(*testing.T, *url.URL) {
userID := 2
username := "user2"
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/activitypub/user-id/%v", userID))
@@ -57,49 +56,26 @@ func TestActivityPubPerson(t *testing.T) {
assert.NotNil(t, pubKeyPem)
assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", pubKeyPem)
})
-}
-
-func TestActivityPubMissingPerson(t *testing.T) {
- setting.Federation.Enabled = true
- testWebRoutes = routers.NormalRoutes()
- defer func() {
- setting.Federation.Enabled = false
- testWebRoutes = routers.NormalRoutes()
- }()
-
- onGiteaRun(t, func(*testing.T, *url.URL) {
+ t.Run("MissingPerson", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", "/api/v1/activitypub/user-id/999999999")
resp := MakeRequest(t, req, http.StatusNotFound)
assert.Contains(t, resp.Body.String(), "user does not exist")
})
-}
-
-func TestActivityPubPersonInbox(t *testing.T) {
- setting.Federation.Enabled = true
- testWebRoutes = routers.NormalRoutes()
- defer func() {
- setting.Federation.Enabled = false
- testWebRoutes = routers.NormalRoutes()
- }()
-
- srv := httptest.NewServer(testWebRoutes)
- defer srv.Close()
+ t.Run("MissingPersonInbox", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ srv := httptest.NewServer(testWebRoutes)
+ defer srv.Close()
+ defer test.MockVariableValue(&setting.AppURL, srv.URL+"/")()
- onGiteaRun(t, func(*testing.T, *url.URL) {
- appURL := setting.AppURL
- setting.AppURL = srv.URL + "/"
- defer func() {
- setting.Database.LogSQL = false
- setting.AppURL = appURL
- }()
username1 := "user1"
- ctx := context.Background()
+ ctx := t.Context()
user1, err := user_model.GetUserByName(ctx, username1)
assert.NoError(t, err)
- user1url := fmt.Sprintf("%s/api/v1/activitypub/user-id/1#main-key", srv.URL)
+ user1url := srv.URL + "/api/v1/activitypub/user-id/1#main-key"
c, err := activitypub.NewClient(db.DefaultContext, user1, user1url)
assert.NoError(t, err)
- user2inboxurl := fmt.Sprintf("%s/api/v1/activitypub/user-id/2/inbox", srv.URL)
+ user2inboxurl := srv.URL + "/api/v1/activitypub/user-id/2/inbox"
// Signed request succeeds
resp, err := c.Post([]byte{}, user2inboxurl)
diff --git a/tests/integration/api_admin_org_test.go b/tests/integration/api_admin_org_test.go
index a29d0ba1d7..b2d77456c4 100644
--- a/tests/integration/api_admin_org_test.go
+++ b/tests/integration/api_admin_org_test.go
@@ -5,7 +5,6 @@ package integration
import (
"net/http"
- "net/url"
"strings"
"testing"
@@ -19,10 +18,12 @@ import (
)
func TestAPIAdminOrgCreate(t *testing.T) {
- onGiteaRun(t, func(*testing.T, *url.URL) {
- session := loginUser(t, "user1")
- token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteAdmin)
+ defer tests.PrepareTestEnv(t)()
+ session := loginUser(t, "user1")
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteAdmin)
+ t.Run("CreateOrg", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
org := api.CreateOrgOption{
UserName: "user2_org",
FullName: "User2's organization",
@@ -51,13 +52,8 @@ func TestAPIAdminOrgCreate(t *testing.T) {
FullName: org.FullName,
})
})
-}
-
-func TestAPIAdminOrgCreateBadVisibility(t *testing.T) {
- onGiteaRun(t, func(*testing.T, *url.URL) {
- session := loginUser(t, "user1")
- token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteAdmin)
-
+ t.Run("CreateBadVisibility", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
org := api.CreateOrgOption{
UserName: "user2_org",
FullName: "User2's organization",
@@ -70,22 +66,21 @@ func TestAPIAdminOrgCreateBadVisibility(t *testing.T) {
AddTokenAuth(token)
MakeRequest(t, req, http.StatusUnprocessableEntity)
})
-}
-
-func TestAPIAdminOrgCreateNotAdmin(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
- nonAdminUsername := "user2"
- session := loginUser(t, nonAdminUsername)
- token := getTokenForLoggedInUser(t, session)
- org := api.CreateOrgOption{
- UserName: "user2_org",
- FullName: "User2's organization",
- Description: "This organization created by admin for user2",
- Website: "https://try.gitea.io",
- Location: "Shanghai",
- Visibility: "public",
- }
- req := NewRequestWithJSON(t, "POST", "/api/v1/admin/users/user2/orgs", &org).
- AddTokenAuth(token)
- MakeRequest(t, req, http.StatusForbidden)
+ t.Run("CreateNotAdmin", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ nonAdminUsername := "user2"
+ session := loginUser(t, nonAdminUsername)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll)
+ org := api.CreateOrgOption{
+ UserName: "user2_org",
+ FullName: "User2's organization",
+ Description: "This organization created by admin for user2",
+ Website: "https://try.gitea.io",
+ Location: "Shanghai",
+ Visibility: "public",
+ }
+ req := NewRequestWithJSON(t, "POST", "/api/v1/admin/users/user2/orgs", &org).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusForbidden)
+ })
}
diff --git a/tests/integration/api_admin_test.go b/tests/integration/api_admin_test.go
index 66209ee4e0..d28a103e59 100644
--- a/tests/integration/api_admin_test.go
+++ b/tests/integration/api_admin_test.go
@@ -76,7 +76,7 @@ func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) {
var newPublicKey api.PublicKey
DecodeJSON(t, resp, &newPublicKey)
- token = getUserToken(t, normalUsername)
+ token = getUserToken(t, normalUsername, auth_model.AccessTokenScopeAll)
req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d", adminUsername, newPublicKey.ID).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusForbidden)
@@ -88,7 +88,7 @@ func TestAPISudoUser(t *testing.T) {
normalUsername := "user2"
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeReadUser)
- req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user?sudo=%s", normalUsername)).
+ req := NewRequest(t, "GET", "/api/v1/user?sudo="+normalUsername).
AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
var user api.User
@@ -103,7 +103,7 @@ func TestAPISudoUserForbidden(t *testing.T) {
normalUsername := "user2"
token := getUserToken(t, normalUsername, auth_model.AccessTokenScopeReadAdmin)
- req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user?sudo=%s", adminUsername)).
+ req := NewRequest(t, "GET", "/api/v1/user?sudo="+adminUsername).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusForbidden)
}
@@ -139,7 +139,7 @@ func TestAPIListUsersNotLoggedIn(t *testing.T) {
func TestAPIListUsersNonAdmin(t *testing.T) {
defer tests.PrepareTestEnv(t)()
nonAdminUsername := "user2"
- token := getUserToken(t, nonAdminUsername)
+ token := getUserToken(t, nonAdminUsername, auth_model.AccessTokenScopeAll)
req := NewRequest(t, "GET", "/api/v1/admin/users").
AddTokenAuth(token)
MakeRequest(t, req, http.StatusForbidden)
@@ -193,7 +193,7 @@ func TestAPIEditUser(t *testing.T) {
defer tests.PrepareTestEnv(t)()
adminUsername := "user1"
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
- urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2")
+ urlStr := "/api/v1/admin/users/" + "user2"
fullNameToChange := "Full Name User 2"
req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{
@@ -217,7 +217,7 @@ func TestAPIEditUser(t *testing.T) {
errMap := make(map[string]any)
json.Unmarshal(resp.Body.Bytes(), &errMap)
- assert.EqualValues(t, "e-mail invalid [email: ]", errMap["message"].(string))
+ assert.Equal(t, "e-mail invalid [email: ]", errMap["message"].(string))
user2 = unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"})
assert.False(t, user2.IsRestricted)
@@ -374,7 +374,7 @@ func TestAPIEditUser_NotAllowedEmailDomain(t *testing.T) {
adminUsername := "user1"
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
- urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2")
+ urlStr := "/api/v1/admin/users/" + "user2"
newEmail := "user2@example1.com"
req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
diff --git a/tests/integration/api_branch_test.go b/tests/integration/api_branch_test.go
index 8a0bd2e4ff..16e1f2812e 100644
--- a/tests/integration/api_branch_test.go
+++ b/tests/integration/api_branch_test.go
@@ -24,13 +24,13 @@ func testAPIGetBranch(t *testing.T, branchName string, exists bool) {
AddTokenAuth(token)
resp := MakeRequest(t, req, NoExpectedStatus)
if !exists {
- assert.EqualValues(t, http.StatusNotFound, resp.Code)
+ assert.Equal(t, http.StatusNotFound, resp.Code)
return
}
- assert.EqualValues(t, http.StatusOK, resp.Code)
+ assert.Equal(t, http.StatusOK, resp.Code)
var branch api.Branch
DecodeJSON(t, resp, &branch)
- assert.EqualValues(t, branchName, branch.Name)
+ assert.Equal(t, branchName, branch.Name)
assert.True(t, branch.UserCanPush)
assert.True(t, branch.UserCanMerge)
}
@@ -44,7 +44,7 @@ func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPSta
if resp.Code == http.StatusOK {
var branchProtection api.BranchProtection
DecodeJSON(t, resp, &branchProtection)
- assert.EqualValues(t, branchName, branchProtection.RuleName)
+ assert.Equal(t, branchName, branchProtection.RuleName)
return &branchProtection
}
return nil
@@ -60,7 +60,7 @@ func testAPICreateBranchProtection(t *testing.T, branchName string, expectedPrio
if resp.Code == http.StatusCreated {
var branchProtection api.BranchProtection
DecodeJSON(t, resp, &branchProtection)
- assert.EqualValues(t, branchName, branchProtection.RuleName)
+ assert.Equal(t, branchName, branchProtection.RuleName)
assert.EqualValues(t, expectedPriority, branchProtection.Priority)
}
}
@@ -74,7 +74,7 @@ func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.Bran
if resp.Code == http.StatusOK {
var branchProtection api.BranchProtection
DecodeJSON(t, resp, &branchProtection)
- assert.EqualValues(t, branchName, branchProtection.RuleName)
+ assert.Equal(t, branchName, branchProtection.RuleName)
}
}
@@ -181,7 +181,7 @@ func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBran
DecodeJSON(t, resp, &branch)
if resp.Result().StatusCode == http.StatusCreated {
- assert.EqualValues(t, newBranch, branch.Name)
+ assert.Equal(t, newBranch, branch.Name)
}
return resp.Result().StatusCode == status
@@ -190,28 +190,61 @@ func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBran
func TestAPIUpdateBranch(t *testing.T) {
onGiteaRun(t, func(t *testing.T, _ *url.URL) {
t.Run("UpdateBranchWithEmptyRepo", func(t *testing.T) {
- testAPIUpdateBranch(t, "user10", "repo6", "master", "test", http.StatusNotFound)
+ testAPIUpdateBranch(t, "user10", "user10", "repo6", "master", "test", http.StatusNotFound)
})
t.Run("UpdateBranchWithSameBranchNames", func(t *testing.T) {
- resp := testAPIUpdateBranch(t, "user2", "repo1", "master", "master", http.StatusUnprocessableEntity)
+ resp := testAPIUpdateBranch(t, "user2", "user2", "repo1", "master", "master", http.StatusUnprocessableEntity)
assert.Contains(t, resp.Body.String(), "Cannot rename a branch using the same name or rename to a branch that already exists.")
})
t.Run("UpdateBranchThatAlreadyExists", func(t *testing.T) {
- resp := testAPIUpdateBranch(t, "user2", "repo1", "master", "branch2", http.StatusUnprocessableEntity)
+ resp := testAPIUpdateBranch(t, "user2", "user2", "repo1", "master", "branch2", http.StatusUnprocessableEntity)
assert.Contains(t, resp.Body.String(), "Cannot rename a branch using the same name or rename to a branch that already exists.")
})
t.Run("UpdateBranchWithNonExistentBranch", func(t *testing.T) {
- resp := testAPIUpdateBranch(t, "user2", "repo1", "i-dont-exist", "new-branch-name", http.StatusNotFound)
+ resp := testAPIUpdateBranch(t, "user2", "user2", "repo1", "i-dont-exist", "new-branch-name", http.StatusNotFound)
assert.Contains(t, resp.Body.String(), "Branch doesn't exist.")
})
- t.Run("RenameBranchNormalScenario", func(t *testing.T) {
- testAPIUpdateBranch(t, "user2", "repo1", "branch2", "new-branch-name", http.StatusNoContent)
+ t.Run("UpdateBranchWithNonAdminDoer", func(t *testing.T) {
+ // don't allow default branch renaming
+ resp := testAPIUpdateBranch(t, "user40", "user2", "repo1", "master", "new-branch-name", http.StatusForbidden)
+ assert.Contains(t, resp.Body.String(), "User must be a repo or site admin to rename default or protected branches.")
+
+ // don't allow protected branch renaming
+ token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteRepository)
+ req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branches", &api.CreateBranchRepoOption{
+ BranchName: "protected-branch",
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusCreated)
+ testAPICreateBranchProtection(t, "protected-branch", 1, http.StatusCreated)
+ resp = testAPIUpdateBranch(t, "user40", "user2", "repo1", "protected-branch", "new-branch-name", http.StatusForbidden)
+ assert.Contains(t, resp.Body.String(), "User must be a repo or site admin to rename default or protected branches.")
+ })
+ t.Run("UpdateBranchWithGlobedBasedProtectionRulesAndAdminAccess", func(t *testing.T) {
+ // don't allow branch that falls under glob-based protection rules to be renamed
+ token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteRepository)
+ req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branch_protections", &api.BranchProtection{
+ RuleName: "protected/**",
+ EnablePush: true,
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusCreated)
+
+ from := "protected/1"
+ req = NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branches", &api.CreateBranchRepoOption{
+ BranchName: from,
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusCreated)
+
+ resp := testAPIUpdateBranch(t, "user2", "user2", "repo1", from, "new-branch-name", http.StatusForbidden)
+ assert.Contains(t, resp.Body.String(), "Branch is protected by glob-based protection rules.")
+ })
+ t.Run("UpdateBranchNormalScenario", func(t *testing.T) {
+ testAPIUpdateBranch(t, "user2", "user2", "repo1", "branch2", "new-branch-name", http.StatusNoContent)
})
})
}
-func testAPIUpdateBranch(t *testing.T, ownerName, repoName, from, to string, expectedHTTPStatus int) *httptest.ResponseRecorder {
- token := getUserToken(t, ownerName, auth_model.AccessTokenScopeWriteRepository)
+func testAPIUpdateBranch(t *testing.T, doerName, ownerName, repoName, from, to string, expectedHTTPStatus int) *httptest.ResponseRecorder {
+ token := getUserToken(t, doerName, auth_model.AccessTokenScopeWriteRepository)
req := NewRequestWithJSON(t, "PATCH", "api/v1/repos/"+ownerName+"/"+repoName+"/branches/"+from, &api.UpdateBranchRepoOption{
Name: to,
}).AddTokenAuth(token)
@@ -270,7 +303,7 @@ func TestAPICreateBranchWithSyncBranches(t *testing.T) {
RepoID: 1,
})
assert.NoError(t, err)
- assert.Len(t, branches, 4)
+ assert.Len(t, branches, 6)
// make a broke repository with no branch on database
_, err = db.DeleteByBean(db.DefaultContext, git_model.Branch{RepoID: 1})
@@ -287,7 +320,7 @@ func TestAPICreateBranchWithSyncBranches(t *testing.T) {
RepoID: 1,
})
assert.NoError(t, err)
- assert.Len(t, branches, 5)
+ assert.Len(t, branches, 7)
branches, err = db.Find[git_model.Branch](db.DefaultContext, git_model.FindBranchOptions{
RepoID: 1,
diff --git a/tests/integration/api_comment_test.go b/tests/integration/api_comment_test.go
index 255b8332b2..9842c358f6 100644
--- a/tests/integration/api_comment_test.go
+++ b/tests/integration/api_comment_test.go
@@ -106,7 +106,7 @@ func TestAPICreateComment(t *testing.T) {
var updatedComment api.Comment
DecodeJSON(t, resp, &updatedComment)
- assert.EqualValues(t, commentBody, updatedComment.Body)
+ assert.Equal(t, commentBody, updatedComment.Body)
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: updatedComment.ID, IssueID: issue.ID, Content: commentBody})
t.Run("BlockedByRepoOwner", func(t *testing.T) {
@@ -174,7 +174,7 @@ func TestAPIGetSystemUserComment(t *testing.T) {
user_model.NewGhostUser(),
user_model.NewActionsUser(),
} {
- body := fmt.Sprintf("Hello %s", systemUser.Name)
+ body := "Hello " + systemUser.Name
comment, err := issues_model.CreateComment(db.DefaultContext, &issues_model.CreateCommentOptions{
Type: issues_model.CommentTypeComment,
Doer: systemUser,
@@ -233,8 +233,8 @@ func TestAPIEditComment(t *testing.T) {
var updatedComment api.Comment
DecodeJSON(t, resp, &updatedComment)
- assert.EqualValues(t, comment.ID, updatedComment.ID)
- assert.EqualValues(t, newCommentBody, updatedComment.Body)
+ assert.Equal(t, comment.ID, updatedComment.ID)
+ assert.Equal(t, newCommentBody, updatedComment.Body)
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID, IssueID: issue.ID, Content: newCommentBody})
}
diff --git a/tests/integration/api_fork_test.go b/tests/integration/api_fork_test.go
index 580bb459e7..2837c3b93e 100644
--- a/tests/integration/api_fork_test.go
+++ b/tests/integration/api_fork_test.go
@@ -10,6 +10,7 @@ import (
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
org_model "code.gitea.io/gitea/models/organization"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
api "code.gitea.io/gitea/modules/structs"
@@ -33,7 +34,7 @@ func TestAPIForkListLimitedAndPrivateRepos(t *testing.T) {
// fork into a limited org
limitedOrg := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 22})
- assert.EqualValues(t, api.VisibleTypeLimited, limitedOrg.Visibility)
+ assert.Equal(t, api.VisibleTypeLimited, limitedOrg.Visibility)
ownerTeam1, err := org_model.OrgFromUser(limitedOrg).GetOwnerTeam(db.DefaultContext)
assert.NoError(t, err)
@@ -48,7 +49,7 @@ func TestAPIForkListLimitedAndPrivateRepos(t *testing.T) {
user4Sess := loginUser(t, "user4")
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user4"})
privateOrg := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 23})
- assert.EqualValues(t, api.VisibleTypePrivate, privateOrg.Visibility)
+ assert.Equal(t, api.VisibleTypePrivate, privateOrg.Visibility)
ownerTeam2, err := org_model.OrgFromUser(privateOrg).GetOwnerTeam(db.DefaultContext)
assert.NoError(t, err)
@@ -69,7 +70,7 @@ func TestAPIForkListLimitedAndPrivateRepos(t *testing.T) {
DecodeJSON(t, resp, &forks)
assert.Empty(t, forks)
- assert.EqualValues(t, "0", resp.Header().Get("X-Total-Count"))
+ assert.Equal(t, "0", resp.Header().Get("X-Total-Count"))
})
t.Run("Logged in", func(t *testing.T) {
@@ -81,8 +82,8 @@ func TestAPIForkListLimitedAndPrivateRepos(t *testing.T) {
var forks []*api.Repository
DecodeJSON(t, resp, &forks)
- assert.Len(t, forks, 1)
- assert.EqualValues(t, "1", resp.Header().Get("X-Total-Count"))
+ assert.Len(t, forks, 2)
+ assert.Equal(t, "2", resp.Header().Get("X-Total-Count"))
assert.NoError(t, org_service.AddTeamMember(db.DefaultContext, ownerTeam2, user1))
@@ -93,6 +94,34 @@ func TestAPIForkListLimitedAndPrivateRepos(t *testing.T) {
DecodeJSON(t, resp, &forks)
assert.Len(t, forks, 2)
- assert.EqualValues(t, "2", resp.Header().Get("X-Total-Count"))
+ assert.Equal(t, "2", resp.Header().Get("X-Total-Count"))
})
}
+
+func TestGetPrivateReposForks(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ user1Sess := loginUser(t, "user1")
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) // private repository
+ privateOrg := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 23})
+ user1Token := getTokenForLoggedInUser(t, user1Sess, auth_model.AccessTokenScopeWriteRepository)
+
+ forkedRepoName := "forked-repo"
+ // create fork from a private repository
+ req := NewRequestWithJSON(t, "POST", "/api/v1/repos/"+repo2.FullName()+"/forks", &api.CreateForkOption{
+ Organization: &privateOrg.Name,
+ Name: &forkedRepoName,
+ }).AddTokenAuth(user1Token)
+ MakeRequest(t, req, http.StatusAccepted)
+
+ // test get a private fork without clear permissions
+ req = NewRequest(t, "GET", "/api/v1/repos/"+repo2.FullName()+"/forks").AddTokenAuth(user1Token)
+ resp := MakeRequest(t, req, http.StatusOK)
+
+ forks := []*api.Repository{}
+ DecodeJSON(t, resp, &forks)
+ assert.Len(t, forks, 1)
+ assert.Equal(t, "1", resp.Header().Get("X-Total-Count"))
+ assert.Equal(t, "forked-repo", forks[0].Name)
+ assert.Equal(t, privateOrg.Name, forks[0].Owner.UserName)
+}
diff --git a/tests/integration/api_gitignore_templates_test.go b/tests/integration/api_gitignore_templates_test.go
index c58f5eebfe..1c56d51344 100644
--- a/tests/integration/api_gitignore_templates_test.go
+++ b/tests/integration/api_gitignore_templates_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "fmt"
"net/http"
"testing"
@@ -38,7 +37,7 @@ func TestAPIGetGitignoreTemplateInfo(t *testing.T) {
// Use the first template for the test
templateName := repo_module.Gitignores[0]
- urlStr := fmt.Sprintf("/api/v1/gitignore/templates/%s", templateName)
+ urlStr := "/api/v1/gitignore/templates/" + templateName
req := NewRequest(t, "GET", urlStr)
resp := MakeRequest(t, req, http.StatusOK)
diff --git a/tests/integration/api_gpg_keys_test.go b/tests/integration/api_gpg_keys_test.go
index ec0dafc2d6..edfb9e6eca 100644
--- a/tests/integration/api_gpg_keys_test.go
+++ b/tests/integration/api_gpg_keys_test.go
@@ -86,13 +86,13 @@ func TestGPGKeys(t *testing.T) {
assert.Len(t, keys, 1)
primaryKey1 := keys[0] // Primary key 1
- assert.EqualValues(t, "38EA3BCED732982C", primaryKey1.KeyID)
+ assert.Equal(t, "38EA3BCED732982C", primaryKey1.KeyID)
assert.Len(t, primaryKey1.Emails, 1)
- assert.EqualValues(t, "user2@example.com", primaryKey1.Emails[0].Email)
+ assert.Equal(t, "user2@example.com", primaryKey1.Emails[0].Email)
assert.True(t, primaryKey1.Emails[0].Verified)
subKey := primaryKey1.SubsKey[0] // Subkey of 38EA3BCED732982C
- assert.EqualValues(t, "70D7C694D17D03AD", subKey.KeyID)
+ assert.Equal(t, "70D7C694D17D03AD", subKey.KeyID)
assert.Empty(t, subKey.Emails)
var key api.GPGKey
@@ -100,16 +100,16 @@ func TestGPGKeys(t *testing.T) {
AddTokenAuth(tokenWithGPGKeyScope)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &key)
- assert.EqualValues(t, "38EA3BCED732982C", key.KeyID)
+ assert.Equal(t, "38EA3BCED732982C", key.KeyID)
assert.Len(t, key.Emails, 1)
- assert.EqualValues(t, "user2@example.com", key.Emails[0].Email)
+ assert.Equal(t, "user2@example.com", key.Emails[0].Email)
assert.True(t, key.Emails[0].Verified)
req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(subKey.ID, 10)). // Subkey of 38EA3BCED732982C
AddTokenAuth(tokenWithGPGKeyScope)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &key)
- assert.EqualValues(t, "70D7C694D17D03AD", key.KeyID)
+ assert.Equal(t, "70D7C694D17D03AD", key.KeyID)
assert.Empty(t, key.Emails)
})
diff --git a/tests/integration/api_helper_for_declarative_test.go b/tests/integration/api_helper_for_declarative_test.go
index 7755b9861a..b30cdfd0fc 100644
--- a/tests/integration/api_helper_for_declarative_test.go
+++ b/tests/integration/api_helper_for_declarative_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "context"
"fmt"
"net/http"
"net/http/httptest"
@@ -34,6 +33,10 @@ type APITestContext struct {
func NewAPITestContext(t *testing.T, username, reponame string, scope ...auth.AccessTokenScope) APITestContext {
session := loginUser(t, username)
+ if len(scope) == 0 {
+ // FIXME: legacy logic: no scope means all
+ scope = []auth.AccessTokenScope{auth.AccessTokenScopeAll}
+ }
token := getTokenForLoggedInUser(t, session, scope...)
return APITestContext{
Session: session,
@@ -260,7 +263,7 @@ func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64)
var req *RequestWrapper
var resp *httptest.ResponseRecorder
- for i := 0; i < 6; i++ {
+ for range 6 {
req = NewRequestWithJSON(t, http.MethodPost, urlStr, &forms.MergePullRequestForm{
MergeMessageField: "doAPIMergePullRequest Merge",
Do: string(repo_model.MergeStyleMerge),
@@ -273,8 +276,8 @@ func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64)
}
err := api.APIError{}
DecodeJSON(t, resp, &err)
- assert.EqualValues(t, "Please try again later", err.Message)
- queue.GetManager().FlushAll(context.Background(), 5*time.Second)
+ assert.Equal(t, "Please try again later", err.Message)
+ queue.GetManager().FlushAll(t.Context(), 5*time.Second)
<-time.After(1 * time.Second)
}
@@ -283,7 +286,7 @@ func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64)
expected = http.StatusOK
}
- if !assert.EqualValues(t, expected, resp.Code,
+ if !assert.Equal(t, expected, resp.Code,
"Request: %s %s", req.Method, req.URL.String()) {
logUnexpectedResponse(t, resp)
}
diff --git a/tests/integration/api_issue_label_test.go b/tests/integration/api_issue_label_test.go
index c9cdd46b9a..4324fd37d9 100644
--- a/tests/integration/api_issue_label_test.go
+++ b/tests/integration/api_issue_label_test.go
@@ -38,8 +38,8 @@ func TestAPIModifyLabels(t *testing.T) {
apiLabel := new(api.Label)
DecodeJSON(t, resp, &apiLabel)
dbLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: apiLabel.ID, RepoID: repo.ID})
- assert.EqualValues(t, dbLabel.Name, apiLabel.Name)
- assert.EqualValues(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color)
+ assert.Equal(t, dbLabel.Name, apiLabel.Name)
+ assert.Equal(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color)
req = NewRequestWithJSON(t, "POST", urlStr, &api.CreateLabelOption{
Name: "TestL 2",
@@ -67,7 +67,7 @@ func TestAPIModifyLabels(t *testing.T) {
AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiLabel)
- assert.EqualValues(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color)
+ assert.Equal(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color)
// EditLabel
newName := "LabelNewName"
@@ -79,7 +79,7 @@ func TestAPIModifyLabels(t *testing.T) {
}).AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiLabel)
- assert.EqualValues(t, newColor, apiLabel.Color)
+ assert.Equal(t, newColor, apiLabel.Color)
req = NewRequestWithJSON(t, "PATCH", singleURLStr, &api.EditLabelOption{
Color: &newColorWrong,
}).AddTokenAuth(token)
@@ -165,7 +165,7 @@ func TestAPIReplaceIssueLabels(t *testing.T) {
var apiLabels []*api.Label
DecodeJSON(t, resp, &apiLabels)
if assert.Len(t, apiLabels, 1) {
- assert.EqualValues(t, label.ID, apiLabels[0].ID)
+ assert.Equal(t, label.ID, apiLabels[0].ID)
}
unittest.AssertCount(t, &issues_model.IssueLabel{IssueID: issue.ID}, 1)
@@ -191,7 +191,7 @@ func TestAPIReplaceIssueLabelsWithLabelNames(t *testing.T) {
var apiLabels []*api.Label
DecodeJSON(t, resp, &apiLabels)
if assert.Len(t, apiLabels, 1) {
- assert.EqualValues(t, label.Name, apiLabels[0].Name)
+ assert.Equal(t, label.Name, apiLabels[0].Name)
}
}
@@ -215,8 +215,8 @@ func TestAPIModifyOrgLabels(t *testing.T) {
apiLabel := new(api.Label)
DecodeJSON(t, resp, &apiLabel)
dbLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: apiLabel.ID, OrgID: owner.ID})
- assert.EqualValues(t, dbLabel.Name, apiLabel.Name)
- assert.EqualValues(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color)
+ assert.Equal(t, dbLabel.Name, apiLabel.Name)
+ assert.Equal(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color)
req = NewRequestWithJSON(t, "POST", urlStr, &api.CreateLabelOption{
Name: "TestL 2",
@@ -244,7 +244,7 @@ func TestAPIModifyOrgLabels(t *testing.T) {
AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiLabel)
- assert.EqualValues(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color)
+ assert.Equal(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color)
// EditLabel
newName := "LabelNewName"
@@ -256,7 +256,7 @@ func TestAPIModifyOrgLabels(t *testing.T) {
}).AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiLabel)
- assert.EqualValues(t, newColor, apiLabel.Color)
+ assert.Equal(t, newColor, apiLabel.Color)
req = NewRequestWithJSON(t, "PATCH", singleURLStr, &api.EditLabelOption{
Color: &newColorWrong,
}).AddTokenAuth(token)
diff --git a/tests/integration/api_issue_lock_test.go b/tests/integration/api_issue_lock_test.go
new file mode 100644
index 0000000000..47b1f2cf0d
--- /dev/null
+++ b/tests/integration/api_issue_lock_test.go
@@ -0,0 +1,74 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ issues_model "code.gitea.io/gitea/models/issues"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/tests"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestAPILockIssue(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ t.Run("Lock", func(t *testing.T) {
+ issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
+ assert.False(t, issueBefore.IsLocked)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/lock", owner.Name, repo.Name, issueBefore.Index)
+
+ session := loginUser(t, owner.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
+
+ // check lock issue
+ req := NewRequestWithJSON(t, "PUT", urlStr, api.LockIssueOption{Reason: "Spam"}).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+ issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
+ assert.True(t, issueAfter.IsLocked)
+
+ // check with other user
+ user34 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 34})
+ session34 := loginUser(t, user34.Name)
+ token34 := getTokenForLoggedInUser(t, session34, auth_model.AccessTokenScopeAll)
+ req = NewRequestWithJSON(t, "PUT", urlStr, api.LockIssueOption{Reason: "Spam"}).AddTokenAuth(token34)
+ MakeRequest(t, req, http.StatusForbidden)
+ })
+
+ t.Run("Unlock", func(t *testing.T) {
+ issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/lock", owner.Name, repo.Name, issueBefore.Index)
+
+ session := loginUser(t, owner.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
+
+ lockReq := NewRequestWithJSON(t, "PUT", urlStr, api.LockIssueOption{Reason: "Spam"}).AddTokenAuth(token)
+ MakeRequest(t, lockReq, http.StatusNoContent)
+
+ // check unlock issue
+ req := NewRequest(t, "DELETE", urlStr).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+ issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
+ assert.False(t, issueAfter.IsLocked)
+
+ // check with other user
+ user34 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 34})
+ session34 := loginUser(t, user34.Name)
+ token34 := getTokenForLoggedInUser(t, session34, auth_model.AccessTokenScopeAll)
+ req = NewRequest(t, "DELETE", urlStr).AddTokenAuth(token34)
+ MakeRequest(t, req, http.StatusForbidden)
+ })
+}
diff --git a/tests/integration/api_issue_milestone_test.go b/tests/integration/api_issue_milestone_test.go
index 2d00752302..1196c8d358 100644
--- a/tests/integration/api_issue_milestone_test.go
+++ b/tests/integration/api_issue_milestone_test.go
@@ -73,7 +73,7 @@ func TestAPIIssuesMilestone(t *testing.T) {
AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiMilestone)
- assert.EqualValues(t, apiMilestones[2], apiMilestone)
+ assert.Equal(t, apiMilestones[2], apiMilestone)
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/milestones?state=%s&name=%s", owner.Name, repo.Name, "all", "milestone2")).
AddTokenAuth(token)
diff --git a/tests/integration/api_issue_stopwatch_test.go b/tests/integration/api_issue_stopwatch_test.go
index 4765787e6f..3606d9a228 100644
--- a/tests/integration/api_issue_stopwatch_test.go
+++ b/tests/integration/api_issue_stopwatch_test.go
@@ -35,11 +35,11 @@ func TestAPIListStopWatches(t *testing.T) {
stopwatch := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: owner.ID})
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: stopwatch.IssueID})
if assert.Len(t, apiWatches, 1) {
- assert.EqualValues(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix())
- assert.EqualValues(t, issue.Index, apiWatches[0].IssueIndex)
- assert.EqualValues(t, issue.Title, apiWatches[0].IssueTitle)
- assert.EqualValues(t, repo.Name, apiWatches[0].RepoName)
- assert.EqualValues(t, repo.OwnerName, apiWatches[0].RepoOwnerName)
+ assert.Equal(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix())
+ assert.Equal(t, issue.Index, apiWatches[0].IssueIndex)
+ assert.Equal(t, issue.Title, apiWatches[0].IssueTitle)
+ assert.Equal(t, repo.Name, apiWatches[0].RepoName)
+ assert.Equal(t, repo.OwnerName, apiWatches[0].RepoOwnerName)
assert.Positive(t, apiWatches[0].Seconds)
}
}
diff --git a/tests/integration/api_issue_subscription_test.go b/tests/integration/api_issue_subscription_test.go
index 7a716301c4..74ba171c01 100644
--- a/tests/integration/api_issue_subscription_test.go
+++ b/tests/integration/api_issue_subscription_test.go
@@ -43,11 +43,11 @@ func TestAPIIssueSubscriptions(t *testing.T) {
wi := new(api.WatchInfo)
DecodeJSON(t, resp, wi)
- assert.EqualValues(t, isWatching, wi.Subscribed)
- assert.EqualValues(t, !isWatching, wi.Ignored)
- assert.EqualValues(t, issue.APIURL(db.DefaultContext)+"/subscriptions", wi.URL)
+ assert.Equal(t, isWatching, wi.Subscribed)
+ assert.Equal(t, !isWatching, wi.Ignored)
+ assert.Equal(t, issue.APIURL(db.DefaultContext)+"/subscriptions", wi.URL)
assert.EqualValues(t, issue.CreatedUnix, wi.CreatedAt.Unix())
- assert.EqualValues(t, issueRepo.APIURL(), wi.RepositoryURL)
+ assert.Equal(t, issueRepo.APIURL(), wi.RepositoryURL)
}
testSubscription(issue1, true)
diff --git a/tests/integration/api_issue_test.go b/tests/integration/api_issue_test.go
index d8394a33d9..370c90a100 100644
--- a/tests/integration/api_issue_test.go
+++ b/tests/integration/api_issue_test.go
@@ -166,7 +166,7 @@ func TestAPICreateIssueParallel(t *testing.T) {
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner.Name, repoBefore.Name)
var wg sync.WaitGroup
- for i := 0; i < 10; i++ {
+ for i := range 10 {
wg.Add(1)
go func(parentT *testing.T, i int) {
parentT.Run(fmt.Sprintf("ParallelCreateIssue_%d", i), func(t *testing.T) {
@@ -267,10 +267,7 @@ func TestAPISearchIssues(t *testing.T) {
defer tests.PrepareTestEnv(t)()
// as this API was used in the frontend, it uses UI page size
- expectedIssueCount := 20 // from the fixtures
- if expectedIssueCount > setting.UI.IssuePagingNum {
- expectedIssueCount = setting.UI.IssuePagingNum
- }
+ expectedIssueCount := min(20, setting.UI.IssuePagingNum) // 20 is from the fixtures
link, _ := url.Parse("/api/v1/repos/issues/search")
token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue)
@@ -313,7 +310,7 @@ func TestAPISearchIssues(t *testing.T) {
req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiIssues)
- assert.EqualValues(t, "22", resp.Header().Get("X-Total-Count"))
+ assert.Equal(t, "22", resp.Header().Get("X-Total-Count"))
assert.Len(t, apiIssues, 20)
query.Add("limit", "10")
@@ -321,7 +318,7 @@ func TestAPISearchIssues(t *testing.T) {
req = NewRequest(t, "GET", link.String()).AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiIssues)
- assert.EqualValues(t, "22", resp.Header().Get("X-Total-Count"))
+ assert.Equal(t, "22", resp.Header().Get("X-Total-Count"))
assert.Len(t, apiIssues, 10)
query = url.Values{"assigned": {"true"}, "state": {"all"}}
@@ -371,10 +368,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) {
defer tests.PrepareTestEnv(t)()
// as this API was used in the frontend, it uses UI page size
- expectedIssueCount := 20 // from the fixtures
- if expectedIssueCount > setting.UI.IssuePagingNum {
- expectedIssueCount = setting.UI.IssuePagingNum
- }
+ expectedIssueCount := min(20, setting.UI.IssuePagingNum) // 20 is from the fixtures
link, _ := url.Parse("/api/v1/repos/issues/search")
token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue)
diff --git a/tests/integration/api_issue_tracked_time_test.go b/tests/integration/api_issue_tracked_time_test.go
index fd2c452b20..bd562b602e 100644
--- a/tests/integration/api_issue_tracked_time_test.go
+++ b/tests/integration/api_issue_tracked_time_test.go
@@ -41,8 +41,8 @@ func TestAPIGetTrackedTimes(t *testing.T) {
for i, time := range expect {
assert.Equal(t, time.ID, apiTimes[i].ID)
- assert.EqualValues(t, issue2.Title, apiTimes[i].Issue.Title)
- assert.EqualValues(t, issue2.ID, apiTimes[i].IssueID)
+ assert.Equal(t, issue2.Title, apiTimes[i].Issue.Title)
+ assert.Equal(t, issue2.ID, apiTimes[i].IssueID)
assert.Equal(t, time.Created.Unix(), apiTimes[i].Created.Unix())
assert.Equal(t, time.Time, apiTimes[i].Time)
user, err := user_model.GetUserByID(db.DefaultContext, time.UserID)
@@ -125,6 +125,6 @@ func TestAPIAddTrackedTimes(t *testing.T) {
DecodeJSON(t, resp, &apiNewTime)
assert.EqualValues(t, 33, apiNewTime.Time)
- assert.EqualValues(t, user2.ID, apiNewTime.UserID)
+ assert.Equal(t, user2.ID, apiNewTime.UserID)
assert.EqualValues(t, 947688818, apiNewTime.Created.Unix())
}
diff --git a/tests/integration/api_keys_test.go b/tests/integration/api_keys_test.go
index 2276b955cf..3162051acc 100644
--- a/tests/integration/api_keys_test.go
+++ b/tests/integration/api_keys_test.go
@@ -143,7 +143,7 @@ func TestCreateUserKey(t *testing.T) {
})
// Search by fingerprint
- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/keys?fingerprint=%s", newPublicKey.Fingerprint)).
+ req = NewRequest(t, "GET", "/api/v1/user/keys?fingerprint="+newPublicKey.Fingerprint).
AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusOK)
@@ -183,7 +183,7 @@ func TestCreateUserKey(t *testing.T) {
token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeWriteUser)
// Should find key even though not ours, but we shouldn't know whose it is
- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/keys?fingerprint=%s", newPublicKey.Fingerprint)).
+ req = NewRequest(t, "GET", "/api/v1/user/keys?fingerprint="+newPublicKey.Fingerprint).
AddTokenAuth(token2)
resp = MakeRequest(t, req, http.StatusOK)
diff --git a/tests/integration/api_label_templates_test.go b/tests/integration/api_label_templates_test.go
index 007e979011..3e637daba6 100644
--- a/tests/integration/api_label_templates_test.go
+++ b/tests/integration/api_label_templates_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "fmt"
"net/http"
"net/url"
"strings"
@@ -42,7 +41,7 @@ func TestAPIGetLabelTemplateInfo(t *testing.T) {
// Use the first template for the test
templateName := repo_module.LabelTemplateFiles[0].DisplayName
- urlStr := fmt.Sprintf("/api/v1/label/templates/%s", url.PathEscape(templateName))
+ urlStr := "/api/v1/label/templates/" + url.PathEscape(templateName)
req := NewRequest(t, "GET", urlStr)
resp := MakeRequest(t, req, http.StatusOK)
diff --git a/tests/integration/api_license_templates_test.go b/tests/integration/api_license_templates_test.go
index e12aab7c2c..52e240f9a7 100644
--- a/tests/integration/api_license_templates_test.go
+++ b/tests/integration/api_license_templates_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "fmt"
"net/http"
"net/url"
"testing"
@@ -39,7 +38,7 @@ func TestAPIGetLicenseTemplateInfo(t *testing.T) {
// Use the first template for the test
licenseName := repo_module.Licenses[0]
- urlStr := fmt.Sprintf("/api/v1/licenses/%s", url.PathEscape(licenseName))
+ urlStr := "/api/v1/licenses/" + url.PathEscape(licenseName)
req := NewRequest(t, "GET", urlStr)
resp := MakeRequest(t, req, http.StatusOK)
diff --git a/tests/integration/api_nodeinfo_test.go b/tests/integration/api_nodeinfo_test.go
index 75f8dbb4ba..916c2f1723 100644
--- a/tests/integration/api_nodeinfo_test.go
+++ b/tests/integration/api_nodeinfo_test.go
@@ -5,35 +5,31 @@ package integration
import (
"net/http"
- "net/url"
"testing"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/routers"
+ "code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
)
func TestNodeinfo(t *testing.T) {
- setting.Federation.Enabled = true
- testWebRoutes = routers.NormalRoutes()
- defer func() {
- setting.Federation.Enabled = false
- testWebRoutes = routers.NormalRoutes()
- }()
+ defer tests.PrepareTestEnv(t)()
+ defer test.MockVariableValue(&setting.Federation.Enabled, true)()
+ defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
- onGiteaRun(t, func(*testing.T, *url.URL) {
- req := NewRequest(t, "GET", "/api/v1/nodeinfo")
- resp := MakeRequest(t, req, http.StatusOK)
- VerifyJSONSchema(t, resp, "nodeinfo_2.1.json")
+ req := NewRequest(t, "GET", "/api/v1/nodeinfo")
+ resp := MakeRequest(t, req, http.StatusOK)
+ VerifyJSONSchema(t, resp, "nodeinfo_2.1.json")
- var nodeinfo api.NodeInfo
- DecodeJSON(t, resp, &nodeinfo)
- assert.True(t, nodeinfo.OpenRegistrations)
- assert.Equal(t, "gitea", nodeinfo.Software.Name)
- assert.Equal(t, 29, nodeinfo.Usage.Users.Total)
- assert.Equal(t, 22, nodeinfo.Usage.LocalPosts)
- assert.Equal(t, 3, nodeinfo.Usage.LocalComments)
- })
+ var nodeinfo api.NodeInfo
+ DecodeJSON(t, resp, &nodeinfo)
+ assert.True(t, nodeinfo.OpenRegistrations)
+ assert.Equal(t, "gitea", nodeinfo.Software.Name)
+ assert.Equal(t, 29, nodeinfo.Usage.Users.Total)
+ assert.Equal(t, 22, nodeinfo.Usage.LocalPosts)
+ assert.Equal(t, 3, nodeinfo.Usage.LocalComments)
}
diff --git a/tests/integration/api_notification_test.go b/tests/integration/api_notification_test.go
index dc4ba83ecc..e6bc142476 100644
--- a/tests/integration/api_notification_test.go
+++ b/tests/integration/api_notification_test.go
@@ -35,7 +35,7 @@ func TestAPINotification(t *testing.T) {
// -- GET /notifications --
// test filter
since := "2000-01-01T00%3A50%3A01%2B00%3A00" // 946687801
- req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?since=%s", since)).
+ req := NewRequest(t, "GET", "/api/v1/notifications?since="+since).
AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
var apiNL []api.NotificationThread
@@ -104,10 +104,10 @@ func TestAPINotification(t *testing.T) {
assert.EqualValues(t, 5, apiN.ID)
assert.False(t, apiN.Pinned)
assert.True(t, apiN.Unread)
- assert.EqualValues(t, "issue4", apiN.Subject.Title)
+ assert.Equal(t, "issue4", apiN.Subject.Title)
assert.EqualValues(t, "Issue", apiN.Subject.Type)
- assert.EqualValues(t, thread5.Issue.APIURL(db.DefaultContext), apiN.Subject.URL)
- assert.EqualValues(t, thread5.Repository.HTMLURL(), apiN.Repository.HTMLURL)
+ assert.Equal(t, thread5.Issue.APIURL(db.DefaultContext), apiN.Subject.URL)
+ assert.Equal(t, thread5.Repository.HTMLURL(), apiN.Repository.HTMLURL)
MakeRequest(t, NewRequest(t, "GET", "/api/v1/notifications/new"), http.StatusUnauthorized)
diff --git a/tests/integration/api_oauth2_apps_test.go b/tests/integration/api_oauth2_apps_test.go
index 7a17b4ca88..13f64fd69e 100644
--- a/tests/integration/api_oauth2_apps_test.go
+++ b/tests/integration/api_oauth2_apps_test.go
@@ -43,12 +43,12 @@ func testAPICreateOAuth2Application(t *testing.T) {
var createdApp *api.OAuth2Application
DecodeJSON(t, resp, &createdApp)
- assert.EqualValues(t, appBody.Name, createdApp.Name)
+ assert.Equal(t, appBody.Name, createdApp.Name)
assert.Len(t, createdApp.ClientSecret, 56)
assert.Len(t, createdApp.ClientID, 36)
assert.True(t, createdApp.ConfidentialClient)
assert.NotEmpty(t, createdApp.Created)
- assert.EqualValues(t, appBody.RedirectURIs[0], createdApp.RedirectURIs[0])
+ assert.Equal(t, appBody.RedirectURIs[0], createdApp.RedirectURIs[0])
unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{UID: user.ID, Name: createdApp.Name})
}
@@ -74,12 +74,12 @@ func testAPIListOAuth2Applications(t *testing.T) {
DecodeJSON(t, resp, &appList)
expectedApp := appList[0]
- assert.EqualValues(t, expectedApp.Name, existApp.Name)
- assert.EqualValues(t, expectedApp.ClientID, existApp.ClientID)
+ assert.Equal(t, expectedApp.Name, existApp.Name)
+ assert.Equal(t, expectedApp.ClientID, existApp.ClientID)
assert.Equal(t, expectedApp.ConfidentialClient, existApp.ConfidentialClient)
assert.Len(t, expectedApp.ClientID, 36)
assert.Empty(t, expectedApp.ClientSecret)
- assert.EqualValues(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0])
+ assert.Equal(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0])
unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name})
}
@@ -128,13 +128,13 @@ func testAPIGetOAuth2Application(t *testing.T) {
DecodeJSON(t, resp, &app)
expectedApp := app
- assert.EqualValues(t, expectedApp.Name, existApp.Name)
- assert.EqualValues(t, expectedApp.ClientID, existApp.ClientID)
+ assert.Equal(t, expectedApp.Name, existApp.Name)
+ assert.Equal(t, expectedApp.ClientID, existApp.ClientID)
assert.Equal(t, expectedApp.ConfidentialClient, existApp.ConfidentialClient)
assert.Len(t, expectedApp.ClientID, 36)
assert.Empty(t, expectedApp.ClientSecret)
assert.Len(t, expectedApp.RedirectURIs, 1)
- assert.EqualValues(t, expectedApp.RedirectURIs[0], existApp.RedirectURIs[0])
+ assert.Equal(t, expectedApp.RedirectURIs[0], existApp.RedirectURIs[0])
unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name})
}
@@ -168,8 +168,8 @@ func testAPIUpdateOAuth2Application(t *testing.T) {
expectedApp := app
assert.Len(t, expectedApp.RedirectURIs, 2)
- assert.EqualValues(t, expectedApp.RedirectURIs[0], appBody.RedirectURIs[0])
- assert.EqualValues(t, expectedApp.RedirectURIs[1], appBody.RedirectURIs[1])
+ assert.Equal(t, expectedApp.RedirectURIs[0], appBody.RedirectURIs[0])
+ assert.Equal(t, expectedApp.RedirectURIs[1], appBody.RedirectURIs[1])
assert.Equal(t, expectedApp.ConfidentialClient, appBody.ConfidentialClient)
unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name})
}
diff --git a/tests/integration/api_org_test.go b/tests/integration/api_org_test.go
index fff121490c..6577bd1684 100644
--- a/tests/integration/api_org_test.go
+++ b/tests/integration/api_org_test.go
@@ -6,7 +6,6 @@ package integration
import (
"fmt"
"net/http"
- "net/url"
"strings"
"testing"
@@ -19,46 +18,53 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
-func TestAPIOrgCreate(t *testing.T) {
- onGiteaRun(t, func(*testing.T, *url.URL) {
- token := getUserToken(t, "user1", auth_model.AccessTokenScopeWriteOrganization)
-
- org := api.CreateOrgOption{
- UserName: "user1_org",
- FullName: "User1's organization",
- Description: "This organization created by user1",
- Website: "https://try.gitea.io",
- Location: "Shanghai",
- Visibility: "limited",
- }
- req := NewRequestWithJSON(t, "POST", "/api/v1/orgs", &org).
- AddTokenAuth(token)
- resp := MakeRequest(t, req, http.StatusCreated)
-
- var apiOrg api.Organization
- DecodeJSON(t, resp, &apiOrg)
-
- assert.Equal(t, org.UserName, apiOrg.Name)
- assert.Equal(t, org.FullName, apiOrg.FullName)
- assert.Equal(t, org.Description, apiOrg.Description)
- assert.Equal(t, org.Website, apiOrg.Website)
- assert.Equal(t, org.Location, apiOrg.Location)
- assert.Equal(t, org.Visibility, apiOrg.Visibility)
+func TestAPIOrgCreateRename(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ token := getUserToken(t, "user1", auth_model.AccessTokenScopeWriteOrganization)
+
+ org := api.CreateOrgOption{
+ UserName: "user1_org",
+ FullName: "User1's organization",
+ Description: "This organization created by user1",
+ Website: "https://try.gitea.io",
+ Location: "Shanghai",
+ Visibility: "limited",
+ }
+ req := NewRequestWithJSON(t, "POST", "/api/v1/orgs", &org).AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusCreated)
+
+ var apiOrg api.Organization
+ DecodeJSON(t, resp, &apiOrg)
+
+ assert.Equal(t, org.UserName, apiOrg.Name)
+ assert.Equal(t, org.FullName, apiOrg.FullName)
+ assert.Equal(t, org.Description, apiOrg.Description)
+ assert.Equal(t, org.Website, apiOrg.Website)
+ assert.Equal(t, org.Location, apiOrg.Location)
+ assert.Equal(t, org.Visibility, apiOrg.Visibility)
+
+ unittest.AssertExistsAndLoadBean(t, &user_model.User{
+ Name: org.UserName,
+ LowerName: strings.ToLower(org.UserName),
+ FullName: org.FullName,
+ })
- unittest.AssertExistsAndLoadBean(t, &user_model.User{
- Name: org.UserName,
- LowerName: strings.ToLower(org.UserName),
- FullName: org.FullName,
- })
+ // check org name
+ req = NewRequestf(t, "GET", "/api/v1/orgs/%s", org.UserName).AddTokenAuth(token)
+ resp = MakeRequest(t, req, http.StatusOK)
+ DecodeJSON(t, resp, &apiOrg)
+ assert.Equal(t, org.UserName, apiOrg.Name)
+ t.Run("CheckPermission", func(t *testing.T) {
// Check owner team permission
ownerTeam, _ := org_model.GetOwnerTeam(db.DefaultContext, apiOrg.ID)
-
for _, ut := range unit_model.AllRepoUnitTypes {
up := perm.AccessModeOwner
if ut == unit_model.TypeExternalTracker || ut == unit_model.TypeExternalWiki {
@@ -71,40 +77,67 @@ func TestAPIOrgCreate(t *testing.T) {
AccessMode: up,
})
}
+ })
- req = NewRequestf(t, "GET", "/api/v1/orgs/%s", org.UserName).
- AddTokenAuth(token)
+ t.Run("CheckMembers", func(t *testing.T) {
+ req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members", org.UserName).AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusOK)
- DecodeJSON(t, resp, &apiOrg)
- assert.EqualValues(t, org.UserName, apiOrg.Name)
- req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", org.UserName).
- AddTokenAuth(token)
- resp = MakeRequest(t, req, http.StatusOK)
+ // user1 on this org is public
+ var users []*api.User
+ DecodeJSON(t, resp, &users)
+ assert.Len(t, users, 1)
+ assert.Equal(t, "user1", users[0].UserName)
+ })
+ t.Run("RenameOrg", func(t *testing.T) {
+ req = NewRequestWithJSON(t, "POST", "/api/v1/orgs/user1_org/rename", &api.RenameOrgOption{
+ NewName: "renamed_org",
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusNoContent)
+ unittest.AssertExistsAndLoadBean(t, &org_model.Organization{Name: "renamed_org"})
+ org.UserName = "renamed_org" // update the variable so the following tests could still use it
+ })
+
+ t.Run("ListRepos", func(t *testing.T) {
+ // FIXME: this test is wrong, there is no repository at all, so the for-loop is empty
+ req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", org.UserName).AddTokenAuth(token)
+ resp = MakeRequest(t, req, http.StatusOK)
var repos []*api.Repository
DecodeJSON(t, resp, &repos)
for _, repo := range repos {
assert.False(t, repo.Private)
}
+ })
+}
+
+func TestAPIOrgGeneral(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ user1Session := loginUser(t, "user1")
+ user1Token := getTokenForLoggedInUser(t, user1Session, auth_model.AccessTokenScopeWriteOrganization)
- req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members", org.UserName).
- AddTokenAuth(token)
+ t.Run("OrgGetAll", func(t *testing.T) {
+ // accessing with a token will return all orgs
+ req := NewRequest(t, "GET", "/api/v1/orgs").AddTokenAuth(user1Token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ var apiOrgList []*api.Organization
+
+ DecodeJSON(t, resp, &apiOrgList)
+ assert.Len(t, apiOrgList, 13)
+ assert.Equal(t, "Limited Org 36", apiOrgList[1].FullName)
+ assert.Equal(t, "limited", apiOrgList[1].Visibility)
+
+ // accessing without a token will return only public orgs
+ req = NewRequest(t, "GET", "/api/v1/orgs")
resp = MakeRequest(t, req, http.StatusOK)
- // user1 on this org is public
- var users []*api.User
- DecodeJSON(t, resp, &users)
- assert.Len(t, users, 1)
- assert.EqualValues(t, "user1", users[0].UserName)
+ DecodeJSON(t, resp, &apiOrgList)
+ assert.Len(t, apiOrgList, 9)
+ assert.Equal(t, "org 17", apiOrgList[0].FullName)
+ assert.Equal(t, "public", apiOrgList[0].Visibility)
})
-}
-func TestAPIOrgEdit(t *testing.T) {
- onGiteaRun(t, func(*testing.T, *url.URL) {
- session := loginUser(t, "user1")
-
- token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization)
+ t.Run("OrgEdit", func(t *testing.T) {
org := api.EditOrgOption{
FullName: "Org3 organization new full name",
Description: "A new description",
@@ -112,8 +145,7 @@ func TestAPIOrgEdit(t *testing.T) {
Location: "Beijing",
Visibility: "private",
}
- req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/org3", &org).
- AddTokenAuth(token)
+ req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/org3", &org).AddTokenAuth(user1Token)
resp := MakeRequest(t, req, http.StatusOK)
var apiOrg api.Organization
@@ -126,13 +158,8 @@ func TestAPIOrgEdit(t *testing.T) {
assert.Equal(t, org.Location, apiOrg.Location)
assert.Equal(t, org.Visibility, apiOrg.Visibility)
})
-}
-func TestAPIOrgEditBadVisibility(t *testing.T) {
- onGiteaRun(t, func(*testing.T, *url.URL) {
- session := loginUser(t, "user1")
-
- token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization)
+ t.Run("OrgEditBadVisibility", func(t *testing.T) {
org := api.EditOrgOption{
FullName: "Org3 organization new full name",
Description: "A new description",
@@ -140,18 +167,12 @@ func TestAPIOrgEditBadVisibility(t *testing.T) {
Location: "Beijing",
Visibility: "badvisibility",
}
- req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/org3", &org).
- AddTokenAuth(token)
+ req := NewRequestWithJSON(t, "PATCH", "/api/v1/orgs/org3", &org).AddTokenAuth(user1Token)
MakeRequest(t, req, http.StatusUnprocessableEntity)
})
-}
-func TestAPIOrgDeny(t *testing.T) {
- onGiteaRun(t, func(*testing.T, *url.URL) {
- setting.Service.RequireSignInView = true
- defer func() {
- setting.Service.RequireSignInView = false
- }()
+ t.Run("OrgDeny", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, true)()
orgName := "user1_org"
req := NewRequestf(t, "GET", "/api/v1/orgs/%s", orgName)
@@ -163,43 +184,13 @@ func TestAPIOrgDeny(t *testing.T) {
req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members", orgName)
MakeRequest(t, req, http.StatusNotFound)
})
-}
-
-func TestAPIGetAll(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
- token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadOrganization)
-
- // accessing with a token will return all orgs
- req := NewRequest(t, "GET", "/api/v1/orgs").
- AddTokenAuth(token)
- resp := MakeRequest(t, req, http.StatusOK)
- var apiOrgList []*api.Organization
-
- DecodeJSON(t, resp, &apiOrgList)
- assert.Len(t, apiOrgList, 13)
- assert.Equal(t, "Limited Org 36", apiOrgList[1].FullName)
- assert.Equal(t, "limited", apiOrgList[1].Visibility)
-
- // accessing without a token will return only public orgs
- req = NewRequest(t, "GET", "/api/v1/orgs")
- resp = MakeRequest(t, req, http.StatusOK)
-
- DecodeJSON(t, resp, &apiOrgList)
- assert.Len(t, apiOrgList, 9)
- assert.Equal(t, "org 17", apiOrgList[0].FullName)
- assert.Equal(t, "public", apiOrgList[0].Visibility)
-}
-
-func TestAPIOrgSearchEmptyTeam(t *testing.T) {
- onGiteaRun(t, func(*testing.T, *url.URL) {
- token := getUserToken(t, "user1", auth_model.AccessTokenScopeWriteOrganization)
+ t.Run("OrgSearchEmptyTeam", func(t *testing.T) {
orgName := "org_with_empty_team"
-
// create org
req := NewRequestWithJSON(t, "POST", "/api/v1/orgs", &api.CreateOrgOption{
UserName: orgName,
- }).AddTokenAuth(token)
+ }).AddTokenAuth(user1Token)
MakeRequest(t, req, http.StatusCreated)
// create team with no member
@@ -208,12 +199,12 @@ func TestAPIOrgSearchEmptyTeam(t *testing.T) {
IncludesAllRepositories: true,
Permission: "read",
Units: []string{"repo.code", "repo.issues", "repo.ext_issues", "repo.wiki", "repo.pulls"},
- }).AddTokenAuth(token)
+ }).AddTokenAuth(user1Token)
MakeRequest(t, req, http.StatusCreated)
// case-insensitive search for teams that have no members
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/orgs/%s/teams/search?q=%s", orgName, "empty")).
- AddTokenAuth(token)
+ AddTokenAuth(user1Token)
resp := MakeRequest(t, req, http.StatusOK)
data := struct {
Ok bool
@@ -222,7 +213,40 @@ func TestAPIOrgSearchEmptyTeam(t *testing.T) {
DecodeJSON(t, resp, &data)
assert.True(t, data.Ok)
if assert.Len(t, data.Data, 1) {
- assert.EqualValues(t, "Empty", data.Data[0].Name)
+ assert.Equal(t, "Empty", data.Data[0].Name)
}
})
+
+ t.Run("User2ChangeStatus", func(t *testing.T) {
+ user2Session := loginUser(t, "user2")
+ user2Token := getTokenForLoggedInUser(t, user2Session, auth_model.AccessTokenScopeWriteOrganization)
+
+ req := NewRequest(t, "PUT", "/api/v1/orgs/org3/public_members/user2").AddTokenAuth(user2Token)
+ MakeRequest(t, req, http.StatusNoContent)
+ req = NewRequest(t, "DELETE", "/api/v1/orgs/org3/public_members/user2").AddTokenAuth(user2Token)
+ MakeRequest(t, req, http.StatusNoContent)
+
+ // non admin but org owner could also change other member's status
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"})
+ require.False(t, user2.IsAdmin)
+ req = NewRequest(t, "PUT", "/api/v1/orgs/org3/public_members/user1").AddTokenAuth(user2Token)
+ MakeRequest(t, req, http.StatusNoContent)
+ req = NewRequest(t, "DELETE", "/api/v1/orgs/org3/public_members/user1").AddTokenAuth(user2Token)
+ MakeRequest(t, req, http.StatusNoContent)
+ })
+
+ t.Run("User4ChangeStatus", func(t *testing.T) {
+ user4Session := loginUser(t, "user4")
+ user4Token := getTokenForLoggedInUser(t, user4Session, auth_model.AccessTokenScopeWriteOrganization)
+
+ // user4 is a normal team member, they could change their own status
+ req := NewRequest(t, "PUT", "/api/v1/orgs/org3/public_members/user4").AddTokenAuth(user4Token)
+ MakeRequest(t, req, http.StatusNoContent)
+ req = NewRequest(t, "DELETE", "/api/v1/orgs/org3/public_members/user4").AddTokenAuth(user4Token)
+ MakeRequest(t, req, http.StatusNoContent)
+ req = NewRequest(t, "PUT", "/api/v1/orgs/org3/public_members/user1").AddTokenAuth(user4Token)
+ MakeRequest(t, req, http.StatusForbidden)
+ req = NewRequest(t, "DELETE", "/api/v1/orgs/org3/public_members/user1").AddTokenAuth(user4Token)
+ MakeRequest(t, req, http.StatusForbidden)
+ })
}
diff --git a/tests/integration/api_packages_arch_test.go b/tests/integration/api_packages_arch_test.go
index 9c7a9dd19d..e5778b4203 100644
--- a/tests/integration/api_packages_arch_test.go
+++ b/tests/integration/api_packages_arch_test.go
@@ -79,6 +79,34 @@ license = MIT`)
return buf.Bytes()
}
+ readIndexContent := func(r io.Reader) (map[string]string, error) {
+ gzr, err := gzip.NewReader(r)
+ if err != nil {
+ return nil, err
+ }
+
+ content := make(map[string]string)
+
+ tr := tar.NewReader(gzr)
+ for {
+ hd, err := tr.Next()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ buf, err := io.ReadAll(tr)
+ if err != nil {
+ return nil, err
+ }
+
+ content[hd.Name] = string(buf)
+ }
+
+ return content, nil
+ }
compressions := []string{"gz", "xz", "zst"}
repositories := []string{"main", "testing", "with/slash", ""}
@@ -135,7 +163,7 @@ license = MIT`)
assert.Condition(t, func() bool {
seen := false
expectedFilename := fmt.Sprintf("%s-%s-aarch64.pkg.tar.%s", packageName, packageVersion, compression)
- expectedCompositeKey := fmt.Sprintf("%s|aarch64", repository)
+ expectedCompositeKey := repository + "|aarch64"
for _, pf := range pfs {
if pf.Name == expectedFilename && pf.CompositeKey == expectedCompositeKey {
if seen {
@@ -171,35 +199,6 @@ license = MIT`)
MakeRequest(t, req, http.StatusConflict)
})
- readIndexContent := func(r io.Reader) (map[string]string, error) {
- gzr, err := gzip.NewReader(r)
- if err != nil {
- return nil, err
- }
-
- content := make(map[string]string)
-
- tr := tar.NewReader(gzr)
- for {
- hd, err := tr.Next()
- if err == io.EOF {
- break
- }
- if err != nil {
- return nil, err
- }
-
- buf, err := io.ReadAll(tr)
- if err != nil {
- return nil, err
- }
-
- content[hd.Name] = string(buf)
- }
-
- return content, nil
- }
-
t.Run("Index", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
@@ -299,4 +298,39 @@ license = MIT`)
})
}
}
+ t.Run("KeepLastVersion", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ pkgVer1 := createPackage("gz", "gitea-test", "1.0.0", "aarch64")
+ pkgVer2 := createPackage("gz", "gitea-test", "1.0.1", "aarch64")
+ req := NewRequestWithBody(t, "PUT", rootURL, bytes.NewReader(pkgVer1)).
+ AddBasicAuth(user.Name)
+ MakeRequest(t, req, http.StatusCreated)
+ req = NewRequestWithBody(t, "PUT", rootURL, bytes.NewReader(pkgVer2)).
+ AddBasicAuth(user.Name)
+ MakeRequest(t, req, http.StatusCreated)
+
+ req = NewRequest(t, "GET", fmt.Sprintf("%s/aarch64/%s", rootURL, arch_service.IndexArchiveFilename))
+ resp := MakeRequest(t, req, http.StatusOK)
+
+ content, err := readIndexContent(resp.Body)
+ assert.NoError(t, err)
+ assert.Len(t, content, 2)
+
+ _, has := content["gitea-test-1.0.0/desc"]
+ assert.False(t, has)
+ _, has = content["gitea-test-1.0.1/desc"]
+ assert.True(t, has)
+
+ req = NewRequest(t, "DELETE", rootURL+"/gitea-test/1.0.1/aarch64").
+ AddBasicAuth(user.Name)
+ MakeRequest(t, req, http.StatusNoContent)
+
+ req = NewRequest(t, "GET", fmt.Sprintf("%s/aarch64/%s", rootURL, arch_service.IndexArchiveFilename))
+ resp = MakeRequest(t, req, http.StatusOK)
+ content, err = readIndexContent(resp.Body)
+ assert.NoError(t, err)
+ assert.Len(t, content, 2)
+ _, has = content["gitea-test-1.0.0/desc"]
+ assert.True(t, has)
+ })
}
diff --git a/tests/integration/api_packages_cargo_test.go b/tests/integration/api_packages_cargo_test.go
index 3fb9687653..8b5caa7ea7 100644
--- a/tests/integration/api_packages_cargo_test.go
+++ b/tests/integration/api_packages_cargo_test.go
@@ -94,7 +94,7 @@ func testPackageCargo(t *testing.T, _ *neturl.URL) {
}
root := fmt.Sprintf("%sapi/packages/%s/cargo", setting.AppURL, user.Name)
- url := fmt.Sprintf("%s/api/v1/crates", root)
+ url := root + "/api/v1/crates"
t.Run("Index", func(t *testing.T) {
t.Run("Git/Config", func(t *testing.T) {
diff --git a/tests/integration/api_packages_chef_test.go b/tests/integration/api_packages_chef_test.go
index 6efb2708af..8f2c2592e7 100644
--- a/tests/integration/api_packages_chef_test.go
+++ b/tests/integration/api_packages_chef_test.go
@@ -181,7 +181,7 @@ nwIDAQAB
var data []byte
if version == "1.3" {
- data = []byte(fmt.Sprintf(
+ data = fmt.Appendf(nil,
"Method:%s\nPath:%s\nX-Ops-Content-Hash:%s\nX-Ops-Sign:version=%s\nX-Ops-Timestamp:%s\nX-Ops-UserId:%s\nX-Ops-Server-API-Version:%s",
req.Method,
path.Clean(req.URL.Path),
@@ -190,17 +190,17 @@ nwIDAQAB
req.Header.Get("X-Ops-Timestamp"),
username,
req.Header.Get("X-Ops-Server-Api-Version"),
- ))
+ )
} else {
sum := sha1.Sum([]byte(path.Clean(req.URL.Path)))
- data = []byte(fmt.Sprintf(
+ data = fmt.Appendf(nil,
"Method:%s\nHashed Path:%s\nX-Ops-Content-Hash:%s\nX-Ops-Timestamp:%s\nX-Ops-UserId:%s",
req.Method,
base64.StdEncoding.EncodeToString(sum[:]),
req.Header.Get("X-Ops-Content-Hash"),
req.Header.Get("X-Ops-Timestamp"),
username,
- ))
+ )
}
for k := range req.Header {
@@ -274,7 +274,7 @@ nwIDAQAB
uploadPackage := func(t *testing.T, version string, expectedStatus int) {
var body bytes.Buffer
mpw := multipart.NewWriter(&body)
- part, _ := mpw.CreateFormFile("tarball", fmt.Sprintf("%s.tar.gz", version))
+ part, _ := mpw.CreateFormFile("tarball", version+".tar.gz")
zw := gzip.NewWriter(part)
tw := tar.NewWriter(zw)
@@ -320,7 +320,7 @@ nwIDAQAB
pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
assert.NoError(t, err)
assert.Len(t, pfs, 1)
- assert.Equal(t, fmt.Sprintf("%s.tar.gz", packageVersion), pfs[0].Name)
+ assert.Equal(t, packageVersion+".tar.gz", pfs[0].Name)
assert.True(t, pfs[0].IsLead)
uploadPackage(t, packageVersion, http.StatusConflict)
diff --git a/tests/integration/api_packages_composer_test.go b/tests/integration/api_packages_composer_test.go
index 51b047ab41..54f61d91d9 100644
--- a/tests/integration/api_packages_composer_test.go
+++ b/tests/integration/api_packages_composer_test.go
@@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
composer_module "code.gitea.io/gitea/modules/packages/composer"
@@ -63,7 +64,7 @@ func TestPackageComposer(t *testing.T) {
t.Run("ServiceIndex", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%s/packages.json", url)).
+ req := NewRequest(t, "GET", url+"/packages.json").
AddBasicAuth(user.Name)
resp := MakeRequest(t, req, http.StatusOK)
@@ -217,5 +218,39 @@ func TestPackageComposer(t *testing.T) {
assert.Equal(t, "4f5fa464c3cb808a1df191dbf6cb75363f8b7072", pkgs[0].Dist.Checksum)
assert.Len(t, pkgs[0].Bin, 1)
assert.Equal(t, packageBin, pkgs[0].Bin[0])
+
+ // Test package linked to repository
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ userPkgs, err := packages.GetPackagesByType(db.DefaultContext, user.ID, packages.TypeComposer)
+ assert.NoError(t, err)
+ assert.Len(t, userPkgs, 1)
+ assert.EqualValues(t, 0, userPkgs[0].RepoID)
+
+ err = packages.SetRepositoryLink(db.DefaultContext, userPkgs[0].ID, repo1.ID)
+ assert.NoError(t, err)
+
+ req = NewRequest(t, "GET", fmt.Sprintf("%s/p2/%s/%s.json", url, vendorName, projectName)).
+ AddBasicAuth(user.Name)
+ resp = MakeRequest(t, req, http.StatusOK)
+
+ result = composer.PackageMetadataResponse{}
+ DecodeJSON(t, resp, &result)
+
+ assert.Contains(t, result.Packages, packageName)
+ pkgs = result.Packages[packageName]
+ assert.Len(t, pkgs, 1)
+ assert.Equal(t, packageName, pkgs[0].Name)
+ assert.Equal(t, packageVersion, pkgs[0].Version)
+ assert.Equal(t, packageType, pkgs[0].Type)
+ assert.Equal(t, packageDescription, pkgs[0].Description)
+ assert.Len(t, pkgs[0].Authors, 1)
+ assert.Equal(t, packageAuthor, pkgs[0].Authors[0].Name)
+ assert.Equal(t, "zip", pkgs[0].Dist.Type)
+ assert.Equal(t, "4f5fa464c3cb808a1df191dbf6cb75363f8b7072", pkgs[0].Dist.Checksum)
+ assert.Len(t, pkgs[0].Bin, 1)
+ assert.Equal(t, packageBin, pkgs[0].Bin[0])
+ assert.Equal(t, repo1.HTMLURL(), pkgs[0].Source.URL)
+ assert.Equal(t, "git", pkgs[0].Source.Type)
+ assert.Equal(t, packageVersion, pkgs[0].Source.Reference)
})
}
diff --git a/tests/integration/api_packages_conan_test.go b/tests/integration/api_packages_conan_test.go
index 3055e57a2e..4e83c998b8 100644
--- a/tests/integration/api_packages_conan_test.go
+++ b/tests/integration/api_packages_conan_test.go
@@ -91,18 +91,18 @@ func uploadConanPackageV1(t *testing.T, baseURL, token, name, version, user, cha
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNotFound)
- req = NewRequest(t, "GET", fmt.Sprintf("%s/digest", recipeURL)).
+ req = NewRequest(t, "GET", recipeURL+"/digest").
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNotFound)
- req = NewRequest(t, "GET", fmt.Sprintf("%s/download_urls", recipeURL)).
+ req = NewRequest(t, "GET", recipeURL+"/download_urls").
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNotFound)
- req = NewRequest(t, "POST", fmt.Sprintf("%s/upload_urls", recipeURL))
+ req = NewRequest(t, "POST", recipeURL+"/upload_urls")
MakeRequest(t, req, http.StatusUnauthorized)
- req = NewRequestWithJSON(t, "POST", fmt.Sprintf("%s/upload_urls", recipeURL), map[string]int64{
+ req = NewRequestWithJSON(t, "POST", recipeURL+"/upload_urls", map[string]int64{
conanfileName: int64(len(contentConanfile)),
"removed.txt": 0,
}).AddTokenAuth(token)
@@ -127,18 +127,18 @@ func uploadConanPackageV1(t *testing.T, baseURL, token, name, version, user, cha
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNotFound)
- req = NewRequest(t, "GET", fmt.Sprintf("%s/digest", packageURL)).
+ req = NewRequest(t, "GET", packageURL+"/digest").
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNotFound)
- req = NewRequest(t, "GET", fmt.Sprintf("%s/download_urls", packageURL)).
+ req = NewRequest(t, "GET", packageURL+"/download_urls").
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNotFound)
- req = NewRequest(t, "POST", fmt.Sprintf("%s/upload_urls", packageURL))
+ req = NewRequest(t, "POST", packageURL+"/upload_urls")
MakeRequest(t, req, http.StatusUnauthorized)
- req = NewRequestWithJSON(t, "POST", fmt.Sprintf("%s/upload_urls", packageURL), map[string]int64{
+ req = NewRequestWithJSON(t, "POST", packageURL+"/upload_urls", map[string]int64{
conaninfoName: int64(len(contentConaninfo)),
"removed.txt": 0,
}).AddTokenAuth(token)
@@ -167,7 +167,7 @@ func uploadConanPackageV2(t *testing.T, baseURL, token, name, version, user, cha
AddTokenAuth(token)
MakeRequest(t, req, http.StatusCreated)
- req = NewRequest(t, "GET", fmt.Sprintf("%s/files", recipeURL)).
+ req = NewRequest(t, "GET", recipeURL+"/files").
AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
@@ -180,7 +180,7 @@ func uploadConanPackageV2(t *testing.T, baseURL, token, name, version, user, cha
packageURL := fmt.Sprintf("%s/packages/%s/revisions/%s", recipeURL, conanPackageReference, packageRevision)
- req = NewRequest(t, "GET", fmt.Sprintf("%s/files", packageURL)).
+ req = NewRequest(t, "GET", packageURL+"/files").
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNotFound)
@@ -188,7 +188,7 @@ func uploadConanPackageV2(t *testing.T, baseURL, token, name, version, user, cha
AddTokenAuth(token)
MakeRequest(t, req, http.StatusCreated)
- req = NewRequest(t, "GET", fmt.Sprintf("%s/files", packageURL)).
+ req = NewRequest(t, "GET", packageURL+"/files").
AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusOK)
@@ -219,7 +219,7 @@ func TestPackageConan(t *testing.T) {
t.Run("Ping", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%s/v1/ping", url))
+ req := NewRequest(t, "GET", url+"/v1/ping")
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "revisions", resp.Header().Get("X-Conan-Server-Capabilities"))
@@ -230,7 +230,7 @@ func TestPackageConan(t *testing.T) {
t.Run("UserName/Password Authenticate", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%s/v1/users/authenticate", url)).
+ req := NewRequest(t, "GET", url+"/v1/users/authenticate").
AddBasicAuth(user.Name)
resp := MakeRequest(t, req, http.StatusOK)
@@ -256,7 +256,7 @@ func TestPackageConan(t *testing.T) {
token := getTokenForLoggedInUser(t, session, scope)
- req := NewRequest(t, "GET", fmt.Sprintf("%s/v1/users/authenticate", url)).
+ req := NewRequest(t, "GET", url+"/v1/users/authenticate").
AddTokenAuth(token)
resp := MakeRequest(t, req, expectedAuthStatusCode)
if expectedAuthStatusCode != http.StatusOK {
@@ -273,7 +273,7 @@ func TestPackageConan(t *testing.T) {
recipeURL := fmt.Sprintf("%s/v1/conans/%s/%s/%s/%s", url, "TestScope", version1, "testing", channel1)
- req = NewRequestWithJSON(t, "POST", fmt.Sprintf("%s/upload_urls", recipeURL), map[string]int64{
+ req = NewRequestWithJSON(t, "POST", recipeURL+"/upload_urls", map[string]int64{
conanfileName: 64,
"removed.txt": 0,
}).AddTokenAuth(token)
@@ -308,7 +308,7 @@ func TestPackageConan(t *testing.T) {
t.Run("CheckCredentials", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%s/v1/users/check_credentials", url)).
+ req := NewRequest(t, "GET", url+"/v1/users/check_credentials").
AddTokenAuth(token)
MakeRequest(t, req, http.StatusOK)
})
@@ -356,7 +356,7 @@ func TestPackageConan(t *testing.T) {
assert.Equal(t, int64(len(contentConaninfo)), pb.Size)
} else {
- assert.FailNow(t, "unknown file: %s", pf.Name)
+ assert.FailNow(t, "unknown file", "unknown file: %s", pf.Name)
}
}
})
@@ -376,14 +376,14 @@ func TestPackageConan(t *testing.T) {
assert.Contains(t, fileHashes, conanfileName)
assert.Equal(t, "7abc52241c22090782c54731371847a8", fileHashes[conanfileName])
- req = NewRequest(t, "GET", fmt.Sprintf("%s/digest", recipeURL))
+ req = NewRequest(t, "GET", recipeURL+"/digest")
resp = MakeRequest(t, req, http.StatusOK)
downloadURLs := make(map[string]string)
DecodeJSON(t, resp, &downloadURLs)
assert.Contains(t, downloadURLs, conanfileName)
- req = NewRequest(t, "GET", fmt.Sprintf("%s/download_urls", recipeURL))
+ req = NewRequest(t, "GET", recipeURL+"/download_urls")
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &downloadURLs)
@@ -404,14 +404,14 @@ func TestPackageConan(t *testing.T) {
assert.Contains(t, fileHashes, conaninfoName)
assert.Equal(t, "7628bfcc5b17f1470c468621a78df394", fileHashes[conaninfoName])
- req = NewRequest(t, "GET", fmt.Sprintf("%s/digest", packageURL))
+ req = NewRequest(t, "GET", packageURL+"/digest")
resp = MakeRequest(t, req, http.StatusOK)
downloadURLs = make(map[string]string)
DecodeJSON(t, resp, &downloadURLs)
assert.Contains(t, downloadURLs, conaninfoName)
- req = NewRequest(t, "GET", fmt.Sprintf("%s/download_urls", packageURL))
+ req = NewRequest(t, "GET", packageURL+"/download_urls")
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &downloadURLs)
@@ -550,7 +550,7 @@ func TestPackageConan(t *testing.T) {
t.Run("Ping", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%s/v2/ping", url))
+ req := NewRequest(t, "GET", url+"/v2/ping")
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "revisions", resp.Header().Get("X-Conan-Server-Capabilities"))
@@ -561,7 +561,7 @@ func TestPackageConan(t *testing.T) {
t.Run("UserName/Password Authenticate", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%s/v2/users/authenticate", url)).
+ req := NewRequest(t, "GET", url+"/v2/users/authenticate").
AddBasicAuth(user.Name)
resp := MakeRequest(t, req, http.StatusOK)
@@ -573,7 +573,7 @@ func TestPackageConan(t *testing.T) {
assert.Equal(t, user.ID, pkgMeta.UserID)
assert.Equal(t, auth_model.AccessTokenScopeAll, pkgMeta.Scope)
- token = fmt.Sprintf("Bearer %s", body)
+ token = "Bearer " + body
})
badToken := ""
@@ -590,7 +590,7 @@ func TestPackageConan(t *testing.T) {
token := getTokenForLoggedInUser(t, session, scope)
- req := NewRequest(t, "GET", fmt.Sprintf("%s/v2/users/authenticate", url)).
+ req := NewRequest(t, "GET", url+"/v2/users/authenticate").
AddTokenAuth(token)
resp := MakeRequest(t, req, expectedAuthStatusCode)
if expectedAuthStatusCode != http.StatusOK {
@@ -640,7 +640,7 @@ func TestPackageConan(t *testing.T) {
t.Run("CheckCredentials", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%s/v2/users/check_credentials", url)).
+ req := NewRequest(t, "GET", url+"/v2/users/check_credentials").
AddTokenAuth(token)
MakeRequest(t, req, http.StatusOK)
})
@@ -664,7 +664,7 @@ func TestPackageConan(t *testing.T) {
recipeURL := fmt.Sprintf("%s/v2/conans/%s/%s/%s/%s", url, name, version1, user1, channel1)
- req := NewRequest(t, "GET", fmt.Sprintf("%s/latest", recipeURL))
+ req := NewRequest(t, "GET", recipeURL+"/latest")
resp := MakeRequest(t, req, http.StatusOK)
obj := make(map[string]string)
diff --git a/tests/integration/api_packages_conda_test.go b/tests/integration/api_packages_conda_test.go
index 272a660d45..32f55e5435 100644
--- a/tests/integration/api_packages_conda_test.go
+++ b/tests/integration/api_packages_conda_test.go
@@ -193,19 +193,19 @@ func TestPackageConda(t *testing.T) {
Removed map[string]*PackageInfo `json:"removed"`
}
- req := NewRequest(t, "GET", fmt.Sprintf("%s/noarch/repodata.json", root))
+ req := NewRequest(t, "GET", root+"/noarch/repodata.json")
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "application/json", resp.Header().Get("Content-Type"))
- req = NewRequest(t, "GET", fmt.Sprintf("%s/noarch/repodata.json.bz2", root))
+ req = NewRequest(t, "GET", root+"/noarch/repodata.json.bz2")
resp = MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "application/x-bzip2", resp.Header().Get("Content-Type"))
- req = NewRequest(t, "GET", fmt.Sprintf("%s/noarch/current_repodata.json", root))
+ req = NewRequest(t, "GET", root+"/noarch/current_repodata.json")
resp = MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "application/json", resp.Header().Get("Content-Type"))
- req = NewRequest(t, "GET", fmt.Sprintf("%s/noarch/current_repodata.json.bz2", root))
+ req = NewRequest(t, "GET", root+"/noarch/current_repodata.json.bz2")
resp = MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "application/x-bzip2", resp.Header().Get("Content-Type"))
@@ -218,7 +218,7 @@ func TestPackageConda(t *testing.T) {
pd, err := packages.GetPackageDescriptor(db.DefaultContext, pv)
assert.NoError(t, err)
- req := NewRequest(t, "GET", fmt.Sprintf("%s/noarch/repodata.json", root))
+ req := NewRequest(t, "GET", root+"/noarch/repodata.json")
resp := MakeRequest(t, req, http.StatusOK)
var result RepoData
diff --git a/tests/integration/api_packages_container_test.go b/tests/integration/api_packages_container_test.go
index 3905ad1b70..204f099bbe 100644
--- a/tests/integration/api_packages_container_test.go
+++ b/tests/integration/api_packages_container_test.go
@@ -7,8 +7,10 @@ import (
"bytes"
"crypto/sha256"
"encoding/base64"
+ "encoding/hex"
"fmt"
"net/http"
+ "strconv"
"strings"
"sync"
"testing"
@@ -16,7 +18,6 @@ import (
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
- container_model "code.gitea.io/gitea/models/packages/container"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
container_module "code.gitea.io/gitea/modules/packages/container"
@@ -56,7 +57,7 @@ func TestPackageContainer(t *testing.T) {
return values
}
- images := []string{"test", "te/st"}
+ images := []string{"test", "sub/name"}
tags := []string{"latest", "main"}
multiTag := "multi"
@@ -69,7 +70,8 @@ func TestPackageContainer(t *testing.T) {
configContent := `{"architecture":"amd64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/true"],"ArgsEscaped":true,"Image":"sha256:9bd8b88dc68b80cffe126cc820e4b52c6e558eb3b37680bfee8e5f3ed7b8c257"},"container":"b89fe92a887d55c0961f02bdfbfd8ac3ddf66167db374770d2d9e9fab3311510","container_config":{"Hostname":"b89fe92a887d","Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"/true\"]"],"ArgsEscaped":true,"Image":"sha256:9bd8b88dc68b80cffe126cc820e4b52c6e558eb3b37680bfee8e5f3ed7b8c257"},"created":"2022-01-01T00:00:00.000000000Z","docker_version":"20.10.12","history":[{"created":"2022-01-01T00:00:00.000000000Z","created_by":"/bin/sh -c #(nop) COPY file:0e7589b0c800daaf6fa460d2677101e4676dd9491980210cb345480e513f3602 in /true "},{"created":"2022-01-01T00:00:00.000000001Z","created_by":"/bin/sh -c #(nop) CMD [\"/true\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:0ff3b91bdf21ecdf2f2f3d4372c2098a14dbe06cd678e8f0a85fd4902d00e2e2"]}}`
manifestDigest := "sha256:4f10484d1c1bb13e3956b4de1cd42db8e0f14a75be1617b60f2de3cd59c803c6"
- manifestContent := `{"schemaVersion":2,"mediaType":"application/vnd.docker.distribution.manifest.v2+json","config":{"mediaType":"application/vnd.docker.container.image.v1+json","digest":"sha256:4607e093bec406eaadb6f3a340f63400c9d3a7038680744c406903766b938f0d","size":1069},"layers":[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4","size":32}]}`
+ manifestContent := `{"schemaVersion":2,"mediaType":"` + container_module.ContentTypeDockerDistributionManifestV2 + `","config":{"mediaType":"application/vnd.docker.container.image.v1+json","digest":"sha256:4607e093bec406eaadb6f3a340f63400c9d3a7038680744c406903766b938f0d","size":1069},"layers":[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4","size":32}]}`
+ manifestContentType := container_module.ContentTypeDockerDistributionManifestV2
untaggedManifestDigest := "sha256:4305f5f5572b9a426b88909b036e52ee3cf3d7b9c1b01fac840e90747f56623d"
untaggedManifestContent := `{"schemaVersion":2,"mediaType":"` + oci.MediaTypeImageManifest + `","config":{"mediaType":"application/vnd.docker.container.image.v1+json","digest":"sha256:4607e093bec406eaadb6f3a340f63400c9d3a7038680744c406903766b938f0d","size":1069},"layers":[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4","size":32}]}`
@@ -92,12 +94,12 @@ func TestPackageContainer(t *testing.T) {
t.Run("Anonymous", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL))
+ req := NewRequest(t, "GET", setting.AppURL+"v2")
resp := MakeRequest(t, req, http.StatusUnauthorized)
assert.ElementsMatch(t, defaultAuthenticateValues, resp.Header().Values("WWW-Authenticate"))
- req = NewRequest(t, "GET", fmt.Sprintf("%sv2/token", setting.AppURL))
+ req = NewRequest(t, "GET", setting.AppURL+"v2/token")
resp = MakeRequest(t, req, http.StatusOK)
tokenResponse := &TokenResponse{}
@@ -105,18 +107,18 @@ func TestPackageContainer(t *testing.T) {
assert.NotEmpty(t, tokenResponse.Token)
- anonymousToken = fmt.Sprintf("Bearer %s", tokenResponse.Token)
+ anonymousToken = "Bearer " + tokenResponse.Token
- req = NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL)).
+ req = NewRequest(t, "GET", setting.AppURL+"v2").
AddTokenAuth(anonymousToken)
MakeRequest(t, req, http.StatusOK)
- defer test.MockVariableValue(&setting.Service.RequireSignInView, true)()
+ defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, true)()
- req = NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL))
+ req = NewRequest(t, "GET", setting.AppURL+"v2")
MakeRequest(t, req, http.StatusUnauthorized)
- req = NewRequest(t, "GET", fmt.Sprintf("%sv2/token", setting.AppURL))
+ req = NewRequest(t, "GET", setting.AppURL+"v2/token")
MakeRequest(t, req, http.StatusUnauthorized)
defer test.MockVariableValue(&setting.AppURL, "https://domain:8443/sub-path/")()
@@ -129,12 +131,12 @@ func TestPackageContainer(t *testing.T) {
t.Run("UserName/Password", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL))
+ req := NewRequest(t, "GET", setting.AppURL+"v2")
resp := MakeRequest(t, req, http.StatusUnauthorized)
assert.ElementsMatch(t, defaultAuthenticateValues, resp.Header().Values("WWW-Authenticate"))
- req = NewRequest(t, "GET", fmt.Sprintf("%sv2/token", setting.AppURL)).
+ req = NewRequest(t, "GET", setting.AppURL+"v2/token").
AddBasicAuth(user.Name)
resp = MakeRequest(t, req, http.StatusOK)
@@ -147,9 +149,9 @@ func TestPackageContainer(t *testing.T) {
assert.Equal(t, user.ID, pkgMeta.UserID)
assert.Equal(t, auth_model.AccessTokenScopeAll, pkgMeta.Scope)
- userToken = fmt.Sprintf("Bearer %s", tokenResponse.Token)
+ userToken = "Bearer " + tokenResponse.Token
- req = NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL)).
+ req = NewRequest(t, "GET", setting.AppURL+"v2").
AddTokenAuth(userToken)
MakeRequest(t, req, http.StatusOK)
})
@@ -161,23 +163,23 @@ func TestPackageContainer(t *testing.T) {
session := loginUser(t, user.Name)
readToken = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage)
- req := NewRequest(t, "GET", fmt.Sprintf("%sv2/token", setting.AppURL))
+ req := NewRequest(t, "GET", setting.AppURL+"v2/token")
req.Request.SetBasicAuth(user.Name, readToken)
resp := MakeRequest(t, req, http.StatusOK)
tokenResponse := &TokenResponse{}
DecodeJSON(t, resp, &tokenResponse)
- readToken = fmt.Sprintf("Bearer %s", tokenResponse.Token)
+ readToken = "Bearer " + tokenResponse.Token
badToken = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadNotification)
- req = NewRequest(t, "GET", fmt.Sprintf("%sv2/token", setting.AppURL))
+ req = NewRequest(t, "GET", setting.AppURL+"v2/token")
req.Request.SetBasicAuth(user.Name, badToken)
MakeRequest(t, req, http.StatusUnauthorized)
testCase := func(scope auth_model.AccessTokenScope, expectedAuthStatus, expectedStatus int) {
token := getTokenForLoggedInUser(t, session, scope)
- req := NewRequest(t, "GET", fmt.Sprintf("%sv2/token", setting.AppURL))
+ req := NewRequest(t, "GET", setting.AppURL+"v2/token")
req.SetBasicAuth(user.Name, token)
resp := MakeRequest(t, req, expectedAuthStatus)
@@ -190,8 +192,8 @@ func TestPackageContainer(t *testing.T) {
assert.NotEmpty(t, tokenResponse.Token)
- req = NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL)).
- AddTokenAuth(fmt.Sprintf("Bearer %s", tokenResponse.Token))
+ req = NewRequest(t, "GET", setting.AppURL+"v2").
+ AddTokenAuth("Bearer " + tokenResponse.Token)
MakeRequest(t, req, expectedStatus)
}
testCase(auth_model.AccessTokenScopeReadPackage, http.StatusOK, http.StatusOK)
@@ -204,17 +206,17 @@ func TestPackageContainer(t *testing.T) {
t.Run("DetermineSupport", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL)).
+ req := NewRequest(t, "GET", setting.AppURL+"v2").
AddTokenAuth(userToken)
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "registry/2.0", resp.Header().Get("Docker-Distribution-Api-Version"))
- req = NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL)).
+ req = NewRequest(t, "GET", setting.AppURL+"v2").
AddTokenAuth(readToken)
resp = MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "registry/2.0", resp.Header().Get("Docker-Distribution-Api-Version"))
- req = NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL)).
+ req = NewRequest(t, "GET", setting.AppURL+"v2").
AddTokenAuth(badToken)
MakeRequest(t, req, http.StatusUnauthorized)
})
@@ -226,15 +228,15 @@ func TestPackageContainer(t *testing.T) {
t.Run("UploadBlob/Monolithic", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "POST", fmt.Sprintf("%s/blobs/uploads", url)).
+ req := NewRequest(t, "POST", url+"/blobs/uploads").
AddTokenAuth(anonymousToken)
MakeRequest(t, req, http.StatusUnauthorized)
- req = NewRequest(t, "POST", fmt.Sprintf("%s/blobs/uploads", url)).
+ req = NewRequest(t, "POST", url+"/blobs/uploads").
AddTokenAuth(readToken)
MakeRequest(t, req, http.StatusUnauthorized)
- req = NewRequest(t, "POST", fmt.Sprintf("%s/blobs/uploads", url)).
+ req = NewRequest(t, "POST", url+"/blobs/uploads").
AddTokenAuth(badToken)
MakeRequest(t, req, http.StatusUnauthorized)
@@ -249,7 +251,7 @@ func TestPackageContainer(t *testing.T) {
assert.Equal(t, fmt.Sprintf("/v2/%s/%s/blobs/%s", user.Name, image, blobDigest), resp.Header().Get("Location"))
assert.Equal(t, blobDigest, resp.Header().Get("Docker-Content-Digest"))
- pv, err := packages_model.GetInternalVersionByNameAndVersion(db.DefaultContext, user.ID, packages_model.TypeContainer, image, container_model.UploadVersion)
+ pv, err := packages_model.GetInternalVersionByNameAndVersion(db.DefaultContext, user.ID, packages_model.TypeContainer, image, container_module.UploadVersion)
assert.NoError(t, err)
pfs, err := packages_model.GetFilesByVersionID(db.DefaultContext, pv.ID)
@@ -264,15 +266,15 @@ func TestPackageContainer(t *testing.T) {
t.Run("UploadBlob/Chunked", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "POST", fmt.Sprintf("%s/blobs/uploads", url)).
+ req := NewRequest(t, "POST", url+"/blobs/uploads").
AddTokenAuth(readToken)
MakeRequest(t, req, http.StatusUnauthorized)
- req = NewRequest(t, "POST", fmt.Sprintf("%s/blobs/uploads", url)).
+ req = NewRequest(t, "POST", url+"/blobs/uploads").
AddTokenAuth(badToken)
MakeRequest(t, req, http.StatusUnauthorized)
- req = NewRequest(t, "POST", fmt.Sprintf("%s/blobs/uploads", url)).
+ req = NewRequest(t, "POST", url+"/blobs/uploads").
AddTokenAuth(userToken)
resp := MakeRequest(t, req, http.StatusAccepted)
@@ -295,11 +297,22 @@ func TestPackageContainer(t *testing.T) {
SetHeader("Content-Range", "1-10")
MakeRequest(t, req, http.StatusRequestedRangeNotSatisfiable)
- contentRange := fmt.Sprintf("0-%d", len(blobContent)-1)
- req.SetHeader("Content-Range", contentRange)
+ // first patch without Content-Range
+ req = NewRequestWithBody(t, "PATCH", setting.AppURL+uploadURL[1:], bytes.NewReader(blobContent[:1])).
+ AddTokenAuth(userToken)
+ resp = MakeRequest(t, req, http.StatusAccepted)
+ assert.NotEmpty(t, resp.Header().Get("Location"))
+ assert.Equal(t, "0-0", resp.Header().Get("Range"))
+
+ // then send remaining content with Content-Range
+ req = NewRequestWithBody(t, "PATCH", setting.AppURL+uploadURL[1:], bytes.NewReader(blobContent[1:])).
+ SetHeader("Content-Range", fmt.Sprintf("1-%d", len(blobContent)-1)).
+ AddTokenAuth(userToken)
resp = MakeRequest(t, req, http.StatusAccepted)
+ contentRange := fmt.Sprintf("0-%d", len(blobContent)-1)
assert.Equal(t, uuid, resp.Header().Get("Docker-Upload-Uuid"))
+ assert.NotEmpty(t, resp.Header().Get("Location"))
assert.Equal(t, contentRange, resp.Header().Get("Range"))
uploadURL = resp.Header().Get("Location")
@@ -309,7 +322,8 @@ func TestPackageContainer(t *testing.T) {
resp = MakeRequest(t, req, http.StatusNoContent)
assert.Equal(t, uuid, resp.Header().Get("Docker-Upload-Uuid"))
- assert.Equal(t, fmt.Sprintf("0-%d", len(blobContent)), resp.Header().Get("Range"))
+ assert.Equal(t, uploadURL, resp.Header().Get("Location"))
+ assert.Equal(t, contentRange, resp.Header().Get("Range"))
pbu, err = packages_model.GetBlobUploadByID(db.DefaultContext, uuid)
assert.NoError(t, err)
@@ -325,7 +339,7 @@ func TestPackageContainer(t *testing.T) {
t.Run("Cancel", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "POST", fmt.Sprintf("%s/blobs/uploads", url)).
+ req := NewRequest(t, "POST", url+"/blobs/uploads").
AddTokenAuth(userToken)
resp := MakeRequest(t, req, http.StatusAccepted)
@@ -340,7 +354,8 @@ func TestPackageContainer(t *testing.T) {
resp = MakeRequest(t, req, http.StatusNoContent)
assert.Equal(t, uuid, resp.Header().Get("Docker-Upload-Uuid"))
- assert.Equal(t, "0-0", resp.Header().Get("Range"))
+ // FIXME: undefined behavior when the uploaded content is empty: https://github.com/opencontainers/distribution-spec/issues/578
+ assert.Nil(t, resp.Header().Values("Range"))
req = NewRequest(t, "DELETE", setting.AppURL+uploadURL[1:]).
AddTokenAuth(userToken)
@@ -428,7 +443,7 @@ func TestPackageContainer(t *testing.T) {
assert.Len(t, pd.Files, 3)
for _, pfd := range pd.Files {
switch pfd.File.Name {
- case container_model.ManifestFilename:
+ case container_module.ManifestFilename:
assert.True(t, pfd.File.IsLead)
assert.Equal(t, "application/vnd.docker.distribution.manifest.v2+json", pfd.Properties.GetByName(container_module.PropertyMediaType))
assert.Equal(t, manifestDigest, pfd.Properties.GetByName(container_module.PropertyDigest))
@@ -441,7 +456,7 @@ func TestPackageContainer(t *testing.T) {
assert.Equal(t, "application/vnd.docker.image.rootfs.diff.tar.gzip", pfd.Properties.GetByName(container_module.PropertyMediaType))
assert.Equal(t, blobDigest, pfd.Properties.GetByName(container_module.PropertyDigest))
default:
- assert.FailNow(t, "unknown file: %s", pfd.File.Name)
+ assert.FailNow(t, "unknown file", "unknown file: %s", pfd.File.Name)
}
}
@@ -467,7 +482,7 @@ func TestPackageContainer(t *testing.T) {
t.Run("HeadManifest", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "HEAD", fmt.Sprintf("%s/manifests/unknown-tag", url)).
+ req := NewRequest(t, "HEAD", url+"/manifests/unknown-tag").
AddTokenAuth(userToken)
MakeRequest(t, req, http.StatusNotFound)
@@ -475,14 +490,14 @@ func TestPackageContainer(t *testing.T) {
AddTokenAuth(userToken)
resp := MakeRequest(t, req, http.StatusOK)
- assert.Equal(t, fmt.Sprintf("%d", len(manifestContent)), resp.Header().Get("Content-Length"))
+ assert.Equal(t, strconv.Itoa(len(manifestContent)), resp.Header().Get("Content-Length"))
assert.Equal(t, manifestDigest, resp.Header().Get("Docker-Content-Digest"))
})
t.Run("GetManifest", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%s/manifests/unknown-tag", url)).
+ req := NewRequest(t, "GET", url+"/manifests/unknown-tag").
AddTokenAuth(userToken)
MakeRequest(t, req, http.StatusNotFound)
@@ -490,8 +505,8 @@ func TestPackageContainer(t *testing.T) {
AddTokenAuth(userToken)
resp := MakeRequest(t, req, http.StatusOK)
- assert.Equal(t, fmt.Sprintf("%d", len(manifestContent)), resp.Header().Get("Content-Length"))
- assert.Equal(t, oci.MediaTypeImageManifest, resp.Header().Get("Content-Type"))
+ assert.Equal(t, strconv.Itoa(len(manifestContent)), resp.Header().Get("Content-Length"))
+ assert.Equal(t, manifestContentType, resp.Header().Get("Content-Type"))
assert.Equal(t, manifestDigest, resp.Header().Get("Docker-Content-Digest"))
assert.Equal(t, manifestContent, resp.Body.String())
})
@@ -512,7 +527,7 @@ func TestPackageContainer(t *testing.T) {
AddTokenAuth(userToken)
resp = MakeRequest(t, req, http.StatusOK)
- assert.Equal(t, fmt.Sprintf("%d", len(untaggedManifestContent)), resp.Header().Get("Content-Length"))
+ assert.Equal(t, strconv.Itoa(len(untaggedManifestContent)), resp.Header().Get("Content-Length"))
assert.Equal(t, untaggedManifestDigest, resp.Header().Get("Docker-Content-Digest"))
pv, err := packages_model.GetVersionByNameAndVersion(db.DefaultContext, user.ID, packages_model.TypeContainer, image, untaggedManifestDigest)
@@ -530,7 +545,7 @@ func TestPackageContainer(t *testing.T) {
assert.Len(t, pd.Files, 3)
for _, pfd := range pd.Files {
- if pfd.File.Name == container_model.ManifestFilename {
+ if pfd.File.Name == container_module.ManifestFilename {
assert.True(t, pfd.File.IsLead)
assert.Equal(t, oci.MediaTypeImageManifest, pfd.Properties.GetByName(container_module.PropertyMediaType))
assert.Equal(t, untaggedManifestDigest, pfd.Properties.GetByName(container_module.PropertyDigest))
@@ -598,7 +613,7 @@ func TestPackageContainer(t *testing.T) {
AddTokenAuth(userToken)
resp := MakeRequest(t, req, http.StatusOK)
- assert.Equal(t, fmt.Sprintf("%d", len(blobContent)), resp.Header().Get("Content-Length"))
+ assert.Equal(t, strconv.Itoa(len(blobContent)), resp.Header().Get("Content-Length"))
assert.Equal(t, blobDigest, resp.Header().Get("Docker-Content-Digest"))
req = NewRequest(t, "HEAD", fmt.Sprintf("%s/blobs/%s", url, blobDigest)).
@@ -617,11 +632,27 @@ func TestPackageContainer(t *testing.T) {
AddTokenAuth(userToken)
resp := MakeRequest(t, req, http.StatusOK)
- assert.Equal(t, fmt.Sprintf("%d", len(blobContent)), resp.Header().Get("Content-Length"))
+ assert.Equal(t, strconv.Itoa(len(blobContent)), resp.Header().Get("Content-Length"))
assert.Equal(t, blobDigest, resp.Header().Get("Docker-Content-Digest"))
assert.Equal(t, blobContent, resp.Body.Bytes())
})
+ t.Run("GetBlob/Empty", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ emptyDigestBuf := sha256.Sum256(nil)
+ emptyDigest := "sha256:" + hex.EncodeToString(emptyDigestBuf[:])
+ req := NewRequestWithBody(t, "POST", fmt.Sprintf("%s/blobs/uploads?digest=%s", url, emptyDigest), strings.NewReader("")).AddTokenAuth(userToken)
+ MakeRequest(t, req, http.StatusCreated)
+
+ req = NewRequest(t, "HEAD", fmt.Sprintf("%s/blobs/%s", url, emptyDigest)).AddTokenAuth(userToken)
+ resp := MakeRequest(t, req, http.StatusOK)
+ assert.Equal(t, "0", resp.Header().Get("Content-Length"))
+
+ req = NewRequest(t, "GET", fmt.Sprintf("%s/blobs/%s", url, emptyDigest)).AddTokenAuth(userToken)
+ resp = MakeRequest(t, req, http.StatusOK)
+ assert.Equal(t, "0", resp.Header().Get("Content-Length"))
+ })
+
t.Run("GetTagList", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
@@ -631,27 +662,27 @@ func TestPackageContainer(t *testing.T) {
ExpectedLink string
}{
{
- URL: fmt.Sprintf("%s/tags/list", url),
+ URL: url + "/tags/list",
ExpectedTags: []string{"latest", "main", "multi"},
ExpectedLink: fmt.Sprintf(`</v2/%s/%s/tags/list?last=multi>; rel="next"`, user.Name, image),
},
{
- URL: fmt.Sprintf("%s/tags/list?n=0", url),
+ URL: url + "/tags/list?n=0",
ExpectedTags: []string{},
ExpectedLink: "",
},
{
- URL: fmt.Sprintf("%s/tags/list?n=2", url),
+ URL: url + "/tags/list?n=2",
ExpectedTags: []string{"latest", "main"},
ExpectedLink: fmt.Sprintf(`</v2/%s/%s/tags/list?last=main&n=2>; rel="next"`, user.Name, image),
},
{
- URL: fmt.Sprintf("%s/tags/list?last=main", url),
+ URL: url + "/tags/list?last=main",
ExpectedTags: []string{"multi"},
ExpectedLink: fmt.Sprintf(`</v2/%s/%s/tags/list?last=multi>; rel="next"`, user.Name, image),
},
{
- URL: fmt.Sprintf("%s/tags/list?n=1&last=latest", url),
+ URL: url + "/tags/list?n=1&last=latest",
ExpectedTags: []string{"main"},
ExpectedLink: fmt.Sprintf(`</v2/%s/%s/tags/list?last=main&n=1>; rel="next"`, user.Name, image),
},
@@ -731,7 +762,7 @@ func TestPackageContainer(t *testing.T) {
url := fmt.Sprintf("%sv2/%s/parallel", setting.AppURL, user.Name)
var wg sync.WaitGroup
- for i := 0; i < 10; i++ {
+ for i := range 10 {
wg.Add(1)
content := []byte{byte(i)}
@@ -757,7 +788,7 @@ func TestPackageContainer(t *testing.T) {
return func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%sv2/_catalog", setting.AppURL)).
+ req := NewRequest(t, "GET", setting.AppURL+"v2/_catalog").
AddTokenAuth(userToken)
resp := MakeRequest(t, req, http.StatusOK)
diff --git a/tests/integration/api_packages_cran_test.go b/tests/integration/api_packages_cran_test.go
index 667ba0908c..bd4a99f331 100644
--- a/tests/integration/api_packages_cran_test.go
+++ b/tests/integration/api_packages_cran_test.go
@@ -133,8 +133,8 @@ func TestPackageCran(t *testing.T) {
assert.Contains(t, resp.Header().Get("Content-Type"), "text/plain")
body := resp.Body.String()
- assert.Contains(t, body, fmt.Sprintf("Package: %s", packageName))
- assert.Contains(t, body, fmt.Sprintf("Version: %s", packageVersion))
+ assert.Contains(t, body, "Package: "+packageName)
+ assert.Contains(t, body, "Version: "+packageVersion)
req = NewRequest(t, "GET", url+"/src/contrib/PACKAGES.gz").
AddBasicAuth(user.Name)
@@ -230,8 +230,8 @@ func TestPackageCran(t *testing.T) {
assert.Contains(t, resp.Header().Get("Content-Type"), "text/plain")
body := resp.Body.String()
- assert.Contains(t, body, fmt.Sprintf("Package: %s", packageName))
- assert.Contains(t, body, fmt.Sprintf("Version: %s", packageVersion))
+ assert.Contains(t, body, "Package: "+packageName)
+ assert.Contains(t, body, "Version: "+packageVersion)
req = NewRequest(t, "GET", url+"/bin/windows/contrib/4.2/PACKAGES.gz").
AddBasicAuth(user.Name)
diff --git a/tests/integration/api_packages_debian_test.go b/tests/integration/api_packages_debian_test.go
index 98027d774c..3ae60d2aa2 100644
--- a/tests/integration/api_packages_debian_test.go
+++ b/tests/integration/api_packages_debian_test.go
@@ -284,7 +284,7 @@ func TestPackageDebian(t *testing.T) {
// because "Iterate" keeps a dangling SQL session but the callback function still uses the same session to execute statements.
// The "Iterate" problem has been checked by TestContextSafety now, so here we only need to check the cleanup logic with a small number
packagesCount := 2
- for i := 0; i < packagesCount; i++ {
+ for i := range packagesCount {
uploadURL := fmt.Sprintf("%s/pool/%s/%s/upload", rootURL, "test", "main")
req := NewRequestWithBody(t, "PUT", uploadURL, createArchive(packageName, "1.0."+strconv.Itoa(i), "all")).AddBasicAuth(user.Name)
MakeRequest(t, req, http.StatusCreated)
diff --git a/tests/integration/api_packages_generic_test.go b/tests/integration/api_packages_generic_test.go
index baa8dd66c8..5f410fc470 100644
--- a/tests/integration/api_packages_generic_test.go
+++ b/tests/integration/api_packages_generic_test.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@@ -131,11 +132,7 @@ func TestPackageGeneric(t *testing.T) {
t.Run("RequireSignInView", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
-
- setting.Service.RequireSignInView = true
- defer func() {
- setting.Service.RequireSignInView = false
- }()
+ defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, true)()
req = NewRequest(t, "GET", url+"/dummy.bin")
MakeRequest(t, req, http.StatusUnauthorized)
diff --git a/tests/integration/api_packages_goproxy_test.go b/tests/integration/api_packages_goproxy_test.go
index dab9fefc5e..fa0ee5b901 100644
--- a/tests/integration/api_packages_goproxy_test.go
+++ b/tests/integration/api_packages_goproxy_test.go
@@ -87,7 +87,7 @@ func TestPackageGo(t *testing.T) {
AddBasicAuth(user.Name)
MakeRequest(t, req, http.StatusConflict)
- time.Sleep(time.Second)
+ time.Sleep(time.Second) // Ensure the timestamp is different, then the "list" below can have stable order
content = createArchive(map[string][]byte{
packageName + "@" + packageVersion2 + "/go.mod": []byte(goModContent),
diff --git a/tests/integration/api_packages_helm_test.go b/tests/integration/api_packages_helm_test.go
index 76285add11..8f5c6ac571 100644
--- a/tests/integration/api_packages_helm_test.go
+++ b/tests/integration/api_packages_helm_test.go
@@ -52,7 +52,7 @@ dependencies:
zw := gzip.NewWriter(&buf)
archive := tar.NewWriter(zw)
archive.WriteHeader(&tar.Header{
- Name: fmt.Sprintf("%s/Chart.yaml", packageName),
+ Name: packageName + "/Chart.yaml",
Mode: 0o600,
Size: int64(len(chartContent)),
})
@@ -122,7 +122,7 @@ dependencies:
t.Run("Index", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%s/index.yaml", url)).
+ req := NewRequest(t, "GET", url+"/index.yaml").
AddBasicAuth(user.Name)
resp := MakeRequest(t, req, http.StatusOK)
diff --git a/tests/integration/api_packages_maven_test.go b/tests/integration/api_packages_maven_test.go
index 486a5af93e..30ef1884cd 100644
--- a/tests/integration/api_packages_maven_test.go
+++ b/tests/integration/api_packages_maven_test.go
@@ -80,6 +80,7 @@ func TestPackageMaven(t *testing.T) {
t.Run("UploadLegacy", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
+ // try to upload a package with legacy package name (will be saved as "GroupID-ArtifactID")
legacyRootLink := "/api/packages/user2/maven/com/gitea/legacy-project"
req := NewRequestWithBody(t, "PUT", legacyRootLink+"/1.0.2/any-file-name?use_legacy_package_name=1", strings.NewReader("test-content")).AddBasicAuth(user.Name)
MakeRequest(t, req, http.StatusCreated)
@@ -97,6 +98,13 @@ func TestPackageMaven(t *testing.T) {
req = NewRequest(t, "GET", "/user2/-/packages/maven/com.gitea%3Alegacy-project/1.0.2")
MakeRequest(t, req, http.StatusNotFound)
+ // legacy package names should also be able to be listed
+ req = NewRequest(t, "GET", legacyRootLink+"/maven-metadata.xml").AddBasicAuth(user.Name)
+ resp := MakeRequest(t, req, http.StatusOK)
+ respBody := resp.Body.String()
+ assert.Contains(t, respBody, "<version>1.0.2</version>")
+
+ // then upload a package with correct package name (will be saved as "GroupID:ArtifactID")
req = NewRequestWithBody(t, "PUT", legacyRootLink+"/1.0.3/any-file-name", strings.NewReader("test-content")).AddBasicAuth(user.Name)
MakeRequest(t, req, http.StatusCreated)
_, err = packages.GetPackageByName(db.DefaultContext, user.ID, packages.TypeMaven, "com.gitea-legacy-project")
@@ -114,6 +122,12 @@ func TestPackageMaven(t *testing.T) {
req = NewRequest(t, "GET", "/user2/-/packages/maven/com.gitea%3Alegacy-project/1.0.2")
MakeRequest(t, req, http.StatusOK)
+ // now 2 packages should be listed
+ req = NewRequest(t, "GET", legacyRootLink+"/maven-metadata.xml").AddBasicAuth(user.Name)
+ resp = MakeRequest(t, req, http.StatusOK)
+ respBody = resp.Body.String()
+ assert.Contains(t, respBody, "<version>1.0.2</version>")
+ assert.Contains(t, respBody, "<version>1.0.3</version>")
require.NoError(t, packages.DeletePackageByID(db.DefaultContext, p.ID))
})
@@ -307,7 +321,7 @@ func TestPackageMavenConcurrent(t *testing.T) {
defer tests.PrintCurrentTest(t)()
var wg sync.WaitGroup
- for i := 0; i < 10; i++ {
+ for i := range 10 {
wg.Add(1)
go func(i int) {
putFile(t, fmt.Sprintf("/%s/%s.jar", packageVersion, strconv.Itoa(i)), "test", http.StatusCreated)
diff --git a/tests/integration/api_packages_npm_test.go b/tests/integration/api_packages_npm_test.go
index b9660aeeb9..a190ed679d 100644
--- a/tests/integration/api_packages_npm_test.go
+++ b/tests/integration/api_packages_npm_test.go
@@ -28,7 +28,7 @@ func TestPackageNpm(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- token := fmt.Sprintf("Bearer %s", getTokenForLoggedInUser(t, loginUser(t, user.Name), auth_model.AccessTokenScopeWritePackage))
+ token := "Bearer " + getTokenForLoggedInUser(t, loginUser(t, user.Name), auth_model.AccessTokenScopeWritePackage)
packageName := "@scope/test-package"
packageVersion := "1.0.1-pre"
@@ -52,24 +52,33 @@ func TestPackageNpm(t *testing.T) {
"` + packageTag + `": "` + version + `"
},
"versions": {
- "` + version + `": {
- "name": "` + packageName + `",
- "version": "` + version + `",
- "description": "` + packageDescription + `",
- "author": {
- "name": "` + packageAuthor + `"
- },
- "bin": {
- "` + packageBinName + `": "` + packageBinPath + `"
- },
- "dist": {
- "integrity": "sha512-yA4FJsVhetynGfOC1jFf79BuS+jrHbm0fhh+aHzCQkOaOBXKf9oBnC4a6DnLLnEsHQDRLYd00cwj8sCXpC+wIg==",
- "shasum": "aaa7eaf852a948b0aa05afeda35b1badca155d90"
- },
- "repository": {
- "type": "` + repoType + `",
- "url": "` + repoURL + `"
- }
+ "` + version + `": {
+ "name": "` + packageName + `",
+ "version": "` + version + `",
+ "description": "` + packageDescription + `",
+ "author": {
+ "name": "` + packageAuthor + `"
+ },
+ "bin": {
+ "` + packageBinName + `": "` + packageBinPath + `"
+ },
+ "dist": {
+ "integrity": "sha512-yA4FJsVhetynGfOC1jFf79BuS+jrHbm0fhh+aHzCQkOaOBXKf9oBnC4a6DnLLnEsHQDRLYd00cwj8sCXpC+wIg==",
+ "shasum": "aaa7eaf852a948b0aa05afeda35b1badca155d90"
+ },
+ "repository": {
+ "type": "` + repoType + `",
+ "url": "` + repoURL + `"
+ },
+ "peerDependencies": {
+ "tea": "2.x",
+ "soy-milk": "1.2"
+ },
+ "peerDependenciesMeta": {
+ "soy-milk": {
+ "optional": true
+ }
+ }
}
},
"_attachments": {
@@ -178,6 +187,8 @@ func TestPackageNpm(t *testing.T) {
assert.Equal(t, fmt.Sprintf("%s%s/-/%s/%s", setting.AppURL, root[1:], packageVersion, filename), pmv.Dist.Tarball)
assert.Equal(t, repoType, result.Repository.Type)
assert.Equal(t, repoURL, result.Repository.URL)
+ assert.Equal(t, map[string]string{"tea": "2.x", "soy-milk": "1.2"}, pmv.PeerDependencies)
+ assert.Equal(t, map[string]any{"soy-milk": map[string]any{"optional": true}}, pmv.PeerDependenciesMeta)
})
t.Run("AddTag", func(t *testing.T) {
diff --git a/tests/integration/api_packages_nuget_test.go b/tests/integration/api_packages_nuget_test.go
index 622c2c4394..65b1b9845a 100644
--- a/tests/integration/api_packages_nuget_test.go
+++ b/tests/integration/api_packages_nuget_test.go
@@ -46,21 +46,30 @@ func TestPackageNuGet(t *testing.T) {
defer tests.PrepareTestEnv(t)()
type FeedEntryProperties struct {
- Version string `xml:"Version"`
- NormalizedVersion string `xml:"NormalizedVersion"`
Authors string `xml:"Authors"`
+ Copyright string `xml:"Copyright,omitempty"`
+ Created nuget.TypedValue[time.Time] `xml:"Created"`
Dependencies string `xml:"Dependencies"`
Description string `xml:"Description"`
- VersionDownloadCount nuget.TypedValue[int64] `xml:"VersionDownloadCount"`
+ DevelopmentDependency nuget.TypedValue[bool] `xml:"DevelopmentDependency"`
DownloadCount nuget.TypedValue[int64] `xml:"DownloadCount"`
- PackageSize nuget.TypedValue[int64] `xml:"PackageSize"`
- Created nuget.TypedValue[time.Time] `xml:"Created"`
+ ID string `xml:"Id"`
+ IconURL string `xml:"IconUrl,omitempty"`
+ Language string `xml:"Language,omitempty"`
LastUpdated nuget.TypedValue[time.Time] `xml:"LastUpdated"`
- Published nuget.TypedValue[time.Time] `xml:"Published"`
+ LicenseURL string `xml:"LicenseUrl,omitempty"`
+ MinClientVersion string `xml:"MinClientVersion,omitempty"`
+ NormalizedVersion string `xml:"NormalizedVersion"`
+ Owners string `xml:"Owners,omitempty"`
+ PackageSize nuget.TypedValue[int64] `xml:"PackageSize"`
ProjectURL string `xml:"ProjectUrl,omitempty"`
+ Published nuget.TypedValue[time.Time] `xml:"Published"`
ReleaseNotes string `xml:"ReleaseNotes,omitempty"`
RequireLicenseAcceptance nuget.TypedValue[bool] `xml:"RequireLicenseAcceptance"`
+ Tags string `xml:"Tags,omitempty"`
Title string `xml:"Title"`
+ Version string `xml:"Version"`
+ VersionDownloadCount nuget.TypedValue[int64] `xml:"VersionDownloadCount"`
}
type FeedEntry struct {
@@ -86,28 +95,54 @@ func TestPackageNuGet(t *testing.T) {
readToken := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadPackage)
badToken := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadNotification)
- packageName := "test.package"
+ packageName := "test.package" // id
+ packageID := packageName
packageVersion := "1.0.3"
packageAuthors := "KN4CK3R"
packageDescription := "Gitea Test Package"
+
symbolFilename := "test.pdb"
symbolID := "d910bb6948bd4c6cb40155bcf52c3c94"
+ packageCopyright := "Package Copyright"
+ packageIconURL := "https://gitea.io/favicon.png"
+ packageLanguage := "Package Language"
+ packageLicenseURL := "https://gitea.io/license"
+ packageMinClientVersion := "1.0.0.0"
+ packageOwners := "Package Owners"
+ packageProjectURL := "https://gitea.io"
+ packageReleaseNotes := "Package Release Notes"
+ packageTags := "tag_1 tag_2 tag_3"
+ packageTitle := "Package Title"
+ packageDevelopmentDependency := true
+ packageRequireLicenseAcceptance := true
+
createNuspec := func(id, version string) string {
return `<?xml version="1.0" encoding="utf-8"?>
-<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
- <metadata>
- <id>` + id + `</id>
- <version>` + version + `</version>
- <authors>` + packageAuthors + `</authors>
- <description>` + packageDescription + `</description>
- <dependencies>
- <group targetFramework=".NETStandard2.0">
- <dependency id="Microsoft.CSharp" version="4.5.0" />
- </group>
- </dependencies>
- </metadata>
-</package>`
+ <package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
+ <metadata minClientVersion="` + packageMinClientVersion + `">
+ <authors>` + packageAuthors + `</authors>
+ <copyright>` + packageCopyright + `</copyright>
+ <description>` + packageDescription + `</description>
+ <developmentDependency>true</developmentDependency>
+ <iconUrl>` + packageIconURL + `</iconUrl>
+ <id>` + id + `</id>
+ <language>` + packageLanguage + `</language>
+ <licenseUrl>` + packageLicenseURL + `</licenseUrl>
+ <owners>` + packageOwners + `</owners>
+ <projectUrl>` + packageProjectURL + `</projectUrl>
+ <releaseNotes>` + packageReleaseNotes + `</releaseNotes>
+ <requireLicenseAcceptance>true</requireLicenseAcceptance>
+ <tags>` + packageTags + `</tags>
+ <title>` + packageTitle + `</title>
+ <version>` + version + `</version>
+ <dependencies>
+ <group targetFramework=".NETStandard2.0">
+ <dependency id="Microsoft.CSharp" version="4.5.0" />
+ </group>
+ </dependencies>
+ </metadata>
+ </package>`
}
createPackage := func(id, version string) *bytes.Buffer {
@@ -198,7 +233,7 @@ func TestPackageNuGet(t *testing.T) {
t.Run(c.Owner, func(t *testing.T) {
url := fmt.Sprintf("/api/packages/%s/nuget", c.Owner)
- req := NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url))
+ req := NewRequest(t, "GET", url+"/index.json")
if c.UseBasicAuth {
req.AddBasicAuth(user.Name)
} else if c.token != "" {
@@ -273,10 +308,10 @@ func TestPackageNuGet(t *testing.T) {
pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID)
assert.NoError(t, err)
assert.Equal(t, int64(len(content)), pb.Size)
- case fmt.Sprintf("%s.nuspec", packageName):
+ case packageName + ".nuspec":
assert.False(t, pf.IsLead)
default:
- assert.Fail(t, "unexpected filename: %v", pf.Name)
+ assert.Fail(t, "unexpected filename", "unexpected filename: %v", pf.Name)
}
}
@@ -319,10 +354,10 @@ func TestPackageNuGet(t *testing.T) {
pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID)
assert.NoError(t, err)
assert.Equal(t, int64(len(content)), pb.Size)
- case fmt.Sprintf("%s.nuspec", packageName):
+ case packageName + ".nuspec":
assert.False(t, pf.IsLead)
default:
- assert.Fail(t, "unexpected filename: %v", pf.Name)
+ assert.Fail(t, "unexpected filename", "unexpected filename: %v", pf.Name)
}
}
@@ -360,15 +395,15 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
return &buf
}
- req := NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage("unknown-package", "SymbolsPackage")).
+ req := NewRequestWithBody(t, "PUT", url+"/symbolpackage", createSymbolPackage("unknown-package", "SymbolsPackage")).
AddBasicAuth(user.Name)
MakeRequest(t, req, http.StatusNotFound)
- req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage(packageName, "DummyPackage")).
+ req = NewRequestWithBody(t, "PUT", url+"/symbolpackage", createSymbolPackage(packageName, "DummyPackage")).
AddBasicAuth(user.Name)
MakeRequest(t, req, http.StatusBadRequest)
- req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage(packageName, "SymbolsPackage")).
+ req = NewRequestWithBody(t, "PUT", url+"/symbolpackage", createSymbolPackage(packageName, "SymbolsPackage")).
AddBasicAuth(user.Name)
MakeRequest(t, req, http.StatusCreated)
@@ -393,19 +428,19 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID)
assert.NoError(t, err)
- assert.Equal(t, int64(412), pb.Size)
+ assert.Equal(t, int64(610), pb.Size)
case fmt.Sprintf("%s.%s.snupkg", packageName, packageVersion):
assert.False(t, pf.IsLead)
pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID)
assert.NoError(t, err)
assert.Equal(t, int64(616), pb.Size)
- case fmt.Sprintf("%s.nuspec", packageName):
+ case packageName + ".nuspec":
assert.False(t, pf.IsLead)
pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID)
assert.NoError(t, err)
- assert.Equal(t, int64(427), pb.Size)
+ assert.Equal(t, int64(996), pb.Size)
case symbolFilename:
assert.False(t, pf.IsLead)
@@ -419,11 +454,11 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
assert.Equal(t, nuget_module.PropertySymbolID, pps[0].Name)
assert.Equal(t, symbolID, pps[0].Value)
default:
- assert.FailNow(t, "unexpected file: %v", pf.Name)
+ assert.FailNow(t, "unexpected file", "unexpected file: %v", pf.Name)
}
}
- req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage(packageName, "SymbolsPackage")).
+ req = NewRequestWithBody(t, "PUT", url+"/symbolpackage", createSymbolPackage(packageName, "SymbolsPackage")).
AddBasicAuth(user.Name)
MakeRequest(t, req, http.StatusConflict)
})
@@ -631,7 +666,7 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
})
t.Run("Next", func(t *testing.T) {
- req := NewRequest(t, "GET", fmt.Sprintf("%s/Search()?searchTerm='test'&$skip=0&$top=1", url)).
+ req := NewRequest(t, "GET", url+"/Search()?searchTerm='test'&$skip=0&$top=1").
AddBasicAuth(user.Name)
resp := MakeRequest(t, req, http.StatusOK)
@@ -736,10 +771,24 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
var result FeedEntry
decodeXML(t, resp, &result)
- assert.Equal(t, packageName, result.Properties.Title)
- assert.Equal(t, packageVersion, result.Properties.Version)
assert.Equal(t, packageAuthors, result.Properties.Authors)
assert.Equal(t, packageDescription, result.Properties.Description)
+ assert.Equal(t, packageID, result.Properties.ID)
+ assert.Equal(t, packageVersion, result.Properties.Version)
+
+ assert.Equal(t, packageCopyright, result.Properties.Copyright)
+ assert.Equal(t, packageDevelopmentDependency, result.Properties.DevelopmentDependency.Value)
+ assert.Equal(t, packageIconURL, result.Properties.IconURL)
+ assert.Equal(t, packageLanguage, result.Properties.Language)
+ assert.Equal(t, packageLicenseURL, result.Properties.LicenseURL)
+ assert.Equal(t, packageMinClientVersion, result.Properties.MinClientVersion)
+ assert.Equal(t, packageOwners, result.Properties.Owners)
+ assert.Equal(t, packageProjectURL, result.Properties.ProjectURL)
+ assert.Equal(t, packageReleaseNotes, result.Properties.ReleaseNotes)
+ assert.Equal(t, packageRequireLicenseAcceptance, result.Properties.RequireLicenseAcceptance.Value)
+ assert.Equal(t, packageTags, result.Properties.Tags)
+ assert.Equal(t, packageTitle, result.Properties.Title)
+
assert.Equal(t, "Microsoft.CSharp:4.5.0:.NETStandard2.0", result.Properties.Dependencies)
})
diff --git a/tests/integration/api_packages_pub_test.go b/tests/integration/api_packages_pub_test.go
index 11da894ddf..3c1bca908e 100644
--- a/tests/integration/api_packages_pub_test.go
+++ b/tests/integration/api_packages_pub_test.go
@@ -37,7 +37,7 @@ func TestPackagePub(t *testing.T) {
packageVersion := "1.0.1"
packageDescription := "Test Description"
- filename := fmt.Sprintf("%s.tar.gz", packageVersion)
+ filename := packageVersion + ".tar.gz"
pubspecContent := `name: ` + packageName + `
version: ` + packageVersion + `
diff --git a/tests/integration/api_packages_pypi_test.go b/tests/integration/api_packages_pypi_test.go
index e973f6a52a..54db45f1ac 100644
--- a/tests/integration/api_packages_pypi_test.go
+++ b/tests/integration/api_packages_pypi_test.go
@@ -32,15 +32,16 @@ func TestPackagePyPI(t *testing.T) {
packageVersion := "1!1.0.1+r1234"
packageAuthor := "KN4CK3R"
packageDescription := "Test Description"
+ projectURL := "https://example.com"
content := "test"
hashSHA256 := "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
root := fmt.Sprintf("/api/packages/%s/pypi", user.Name)
- uploadFile := func(t *testing.T, filename, content string, expectedStatus int) {
- body := &bytes.Buffer{}
- writer := multipart.NewWriter(body)
+ createBasicMultipartFile := func(filename, packageName, content string) (body *bytes.Buffer, writer *multipart.Writer, closer func() error) {
+ body = &bytes.Buffer{}
+ writer = multipart.NewWriter(body)
part, _ := writer.CreateFormFile("content", filename)
_, _ = io.Copy(part, strings.NewReader(content))
@@ -52,14 +53,27 @@ func TestPackagePyPI(t *testing.T) {
writer.WriteField("sha256_digest", hashSHA256)
writer.WriteField("requires_python", "3.6")
- _ = writer.Close()
+ return body, writer, writer.Close
+ }
+ uploadHelper := func(t *testing.T, body *bytes.Buffer, contentType string, expectedStatus int) {
req := NewRequestWithBody(t, "POST", root, body).
- SetHeader("Content-Type", writer.FormDataContentType()).
+ SetHeader("Content-Type", contentType).
AddBasicAuth(user.Name)
MakeRequest(t, req, expectedStatus)
}
+ uploadFile := func(t *testing.T, filename, content string, expectedStatus int) {
+ body, writer, closeFunc := createBasicMultipartFile(filename, packageName, content)
+
+ writer.WriteField("project_urls", "DOCUMENTATION , https://readthedocs.org")
+ writer.WriteField("project_urls", "Home-page, "+projectURL)
+
+ _ = closeFunc()
+
+ uploadHelper(t, body, writer.FormDataContentType(), expectedStatus)
+ }
+
t.Run("Upload", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
@@ -74,6 +88,7 @@ func TestPackagePyPI(t *testing.T) {
assert.NoError(t, err)
assert.Nil(t, pd.SemVer)
assert.IsType(t, &pypi.Metadata{}, pd.Metadata)
+ assert.Equal(t, projectURL, pd.Metadata.(*pypi.Metadata).ProjectURL)
assert.Equal(t, packageName, pd.Package.Name)
assert.Equal(t, packageVersion, pd.Version.Version)
@@ -133,6 +148,48 @@ func TestPackagePyPI(t *testing.T) {
uploadFile(t, "test.tar.gz", content, http.StatusConflict)
})
+ t.Run("UploadUsingDeprecatedHomepageMetadata", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ pkgName := "homepage-package"
+ body, writer, closeFunc := createBasicMultipartFile("test.whl", pkgName, content)
+
+ writer.WriteField("home_page", projectURL)
+
+ _ = closeFunc()
+
+ uploadHelper(t, body, writer.FormDataContentType(), http.StatusCreated)
+
+ pvs, err := packages.GetVersionsByPackageName(db.DefaultContext, user.ID, packages.TypePyPI, pkgName)
+ assert.NoError(t, err)
+ assert.Len(t, pvs, 1)
+
+ pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
+ assert.NoError(t, err)
+ assert.IsType(t, &pypi.Metadata{}, pd.Metadata)
+ assert.Equal(t, projectURL, pd.Metadata.(*pypi.Metadata).ProjectURL)
+ })
+
+ t.Run("UploadWithoutAnyHomepageURLMetadata", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ pkgName := "no-project-url-or-homepage-package"
+ body, writer, closeFunc := createBasicMultipartFile("test.whl", pkgName, content)
+
+ _ = closeFunc()
+
+ uploadHelper(t, body, writer.FormDataContentType(), http.StatusCreated)
+
+ pvs, err := packages.GetVersionsByPackageName(db.DefaultContext, user.ID, packages.TypePyPI, pkgName)
+ assert.NoError(t, err)
+ assert.Len(t, pvs, 1)
+
+ pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
+ assert.NoError(t, err)
+ assert.IsType(t, &pypi.Metadata{}, pd.Metadata)
+ assert.Empty(t, pd.Metadata.(*pypi.Metadata).ProjectURL)
+ })
+
t.Run("Download", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
@@ -147,7 +204,7 @@ func TestPackagePyPI(t *testing.T) {
downloadFile("test.whl")
downloadFile("test.tar.gz")
- pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypePyPI)
+ pvs, err := packages.GetVersionsByPackageName(db.DefaultContext, user.ID, packages.TypePyPI, packageName)
assert.NoError(t, err)
assert.Len(t, pvs, 1)
assert.Equal(t, int64(2), pvs[0].DownloadCount)
diff --git a/tests/integration/api_packages_rpm_test.go b/tests/integration/api_packages_rpm_test.go
index 6feceaeb78..bd1959f64e 100644
--- a/tests/integration/api_packages_rpm_test.go
+++ b/tests/integration/api_packages_rpm_test.go
@@ -157,9 +157,14 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`,
t.Run("Download", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
+ // download the package without the file name
req := NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s", groupURL, packageName, packageVersion, packageArchitecture))
resp := MakeRequest(t, req, http.StatusOK)
+ assert.Equal(t, content, resp.Body.Bytes())
+ // download the package with a file name (it can be anything)
+ req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s/any-file-name", groupURL, packageName, packageVersion, packageArchitecture))
+ resp = MakeRequest(t, req, http.StatusOK)
assert.Equal(t, content, resp.Body.Bytes())
})
@@ -317,7 +322,7 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`,
var result Metadata
decodeGzipXML(t, resp, &result)
- assert.EqualValues(t, 1, result.PackageCount)
+ assert.Equal(t, 1, result.PackageCount)
assert.Len(t, result.Packages, 1)
p := result.Packages[0]
assert.Equal(t, "rpm", p.Type)
@@ -366,7 +371,7 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`,
var result Filelists
decodeGzipXML(t, resp, &result)
- assert.EqualValues(t, 1, result.PackageCount)
+ assert.Equal(t, 1, result.PackageCount)
assert.Len(t, result.Packages, 1)
p := result.Packages[0]
assert.NotEmpty(t, p.Pkgid)
@@ -403,7 +408,7 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`,
var result Other
decodeGzipXML(t, resp, &result)
- assert.EqualValues(t, 1, result.PackageCount)
+ assert.Equal(t, 1, result.PackageCount)
assert.Len(t, result.Packages, 1)
p := result.Packages[0]
assert.NotEmpty(t, p.Pkgid)
@@ -447,7 +452,8 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`,
pub, err := openpgp.ReadArmoredKeyRing(gpgResp.Body)
require.NoError(t, err)
- req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s", groupURL, packageName, packageVersion, packageArchitecture))
+ rpmFileName := fmt.Sprintf("%s-%s.%s.rpm", packageName, packageVersion, packageArchitecture)
+ req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s/%s", groupURL, packageName, packageVersion, packageArchitecture, rpmFileName))
resp := MakeRequest(t, req, http.StatusOK)
_, sigs, err := rpmutils.Verify(resp.Body, pub)
diff --git a/tests/integration/api_packages_rubygems_test.go b/tests/integration/api_packages_rubygems_test.go
index fe9283df4d..ab76c52440 100644
--- a/tests/integration/api_packages_rubygems_test.go
+++ b/tests/integration/api_packages_rubygems_test.go
@@ -185,7 +185,7 @@ func TestPackageRubyGems(t *testing.T) {
root := fmt.Sprintf("/api/packages/%s/rubygems", user.Name)
uploadFile := func(t *testing.T, content []byte, expectedStatus int) {
- req := NewRequestWithBody(t, "POST", fmt.Sprintf("%s/api/v1/gems", root), bytes.NewReader(content)).
+ req := NewRequestWithBody(t, "POST", root+"/api/v1/gems", bytes.NewReader(content)).
AddBasicAuth(user.Name)
MakeRequest(t, req, expectedStatus)
}
@@ -293,7 +293,7 @@ gAAAAP//MS06Gw==`)
t.Run("Versions", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%s/versions", root)).AddBasicAuth(user.Name)
+ req := NewRequest(t, "GET", root+"/versions").AddBasicAuth(user.Name)
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, `---
gitea 1.0.5 08843c2dd0ea19910e6b056b98e38f1c
@@ -307,7 +307,7 @@ gitea-another 0.99 8b639e4048d282941485368ec42609be
_ = writer.WriteField("gem_name", packageName)
_ = writer.WriteField("version", packageVersion)
_ = writer.Close()
- req := NewRequestWithBody(t, "DELETE", fmt.Sprintf("%s/api/v1/gems/yank", root), &body).
+ req := NewRequestWithBody(t, "DELETE", root+"/api/v1/gems/yank", &body).
SetHeader("Content-Type", writer.FormDataContentType()).
AddBasicAuth(user.Name)
MakeRequest(t, req, http.StatusOK)
@@ -330,7 +330,7 @@ gitea-another 0.99 8b639e4048d282941485368ec42609be
t.Run("VersionsAfterDelete", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("%s/versions", root)).AddBasicAuth(user.Name)
+ req := NewRequest(t, "GET", root+"/versions").AddBasicAuth(user.Name)
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "---\n", resp.Body.String())
})
diff --git a/tests/integration/api_packages_swift_test.go b/tests/integration/api_packages_swift_test.go
index c0e0dccfab..b29e8459ff 100644
--- a/tests/integration/api_packages_swift_test.go
+++ b/tests/integration/api_packages_swift_test.go
@@ -23,6 +23,7 @@ import (
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestPackageSwift(t *testing.T) {
@@ -34,6 +35,7 @@ func TestPackageSwift(t *testing.T) {
packageName := "test_package"
packageID := packageScope + "." + packageName
packageVersion := "1.0.3"
+ packageVersion2 := "1.0.4"
packageAuthor := "KN4CK3R"
packageDescription := "Gitea Test Package"
packageRepositoryURL := "https://gitea.io/gitea/gitea"
@@ -183,6 +185,94 @@ func TestPackageSwift(t *testing.T) {
)
})
+ t.Run("UploadMultipart", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ uploadPackage := func(t *testing.T, url string, expectedStatus int, sr io.Reader, metadata string) {
+ var body bytes.Buffer
+ mpw := multipart.NewWriter(&body)
+
+ // Read the source archive content
+ sourceContent, err := io.ReadAll(sr)
+ assert.NoError(t, err)
+ mpw.WriteField("source-archive", string(sourceContent))
+
+ if metadata != "" {
+ mpw.WriteField("metadata", metadata)
+ }
+
+ mpw.Close()
+
+ req := NewRequestWithBody(t, "PUT", url, &body).
+ SetHeader("Content-Type", mpw.FormDataContentType()).
+ SetHeader("Accept", swift_router.AcceptJSON).
+ AddBasicAuth(user.Name)
+ MakeRequest(t, req, expectedStatus)
+ }
+
+ createArchive := func(files map[string]string) *bytes.Buffer {
+ var buf bytes.Buffer
+ zw := zip.NewWriter(&buf)
+ for filename, content := range files {
+ w, _ := zw.Create(filename)
+ w.Write([]byte(content))
+ }
+ zw.Close()
+ return &buf
+ }
+
+ uploadURL := fmt.Sprintf("%s/%s/%s/%s", url, packageScope, packageName, packageVersion2)
+
+ req := NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader([]byte{}))
+ MakeRequest(t, req, http.StatusUnauthorized)
+
+ // Test with metadata as form field
+ uploadPackage(
+ t,
+ uploadURL,
+ http.StatusCreated,
+ createArchive(map[string]string{
+ "Package.swift": contentManifest1,
+ "Package@swift-5.6.swift": contentManifest2,
+ }),
+ `{"name":"`+packageName+`","version":"`+packageVersion2+`","description":"`+packageDescription+`","codeRepository":"`+packageRepositoryURL+`","author":{"givenName":"`+packageAuthor+`"},"repositoryURLs":["`+packageRepositoryURL+`"]}`,
+ )
+
+ pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeSwift)
+ assert.NoError(t, err)
+ require.Len(t, pvs, 2) // ATTENTION: many subtests are unable to run separately, they depend on the results of previous tests
+ thisPackageVersion := pvs[0]
+ pd, err := packages.GetPackageDescriptor(db.DefaultContext, thisPackageVersion)
+ assert.NoError(t, err)
+ assert.NotNil(t, pd.SemVer)
+ assert.Equal(t, packageID, pd.Package.Name)
+ assert.Equal(t, packageVersion2, pd.Version.Version)
+ assert.IsType(t, &swift_module.Metadata{}, pd.Metadata)
+ metadata := pd.Metadata.(*swift_module.Metadata)
+ assert.Equal(t, packageDescription, metadata.Description)
+ assert.Len(t, metadata.Manifests, 2)
+ assert.Equal(t, contentManifest1, metadata.Manifests[""].Content)
+ assert.Equal(t, contentManifest2, metadata.Manifests["5.6"].Content)
+ assert.Len(t, pd.VersionProperties, 1)
+ assert.Equal(t, packageRepositoryURL, pd.VersionProperties.GetByName(swift_module.PropertyRepositoryURL))
+
+ pfs, err := packages.GetFilesByVersionID(db.DefaultContext, thisPackageVersion.ID)
+ assert.NoError(t, err)
+ assert.Len(t, pfs, 1)
+ assert.Equal(t, fmt.Sprintf("%s-%s.zip", packageName, packageVersion2), pfs[0].Name)
+ assert.True(t, pfs[0].IsLead)
+
+ uploadPackage(
+ t,
+ uploadURL,
+ http.StatusConflict,
+ createArchive(map[string]string{
+ "Package.swift": contentManifest1,
+ }),
+ "",
+ )
+ })
+
t.Run("Download", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
@@ -211,7 +301,7 @@ func TestPackageSwift(t *testing.T) {
SetHeader("Accept", swift_router.AcceptJSON)
resp := MakeRequest(t, req, http.StatusOK)
- versionURL := setting.AppURL + url[1:] + fmt.Sprintf("/%s/%s/%s", packageScope, packageName, packageVersion)
+ versionURL := setting.AppURL + url[1:] + fmt.Sprintf("/%s/%s/%s", packageScope, packageName, packageVersion2)
assert.Equal(t, "1", resp.Header().Get("Content-Version"))
assert.Equal(t, fmt.Sprintf(`<%s>; rel="latest-version"`, versionURL), resp.Header().Get("Link"))
@@ -221,9 +311,9 @@ func TestPackageSwift(t *testing.T) {
var result *swift_router.EnumeratePackageVersionsResponse
DecodeJSON(t, resp, &result)
- assert.Len(t, result.Releases, 1)
- assert.Contains(t, result.Releases, packageVersion)
- assert.Equal(t, versionURL, result.Releases[packageVersion].URL)
+ assert.Len(t, result.Releases, 2)
+ assert.Contains(t, result.Releases, packageVersion2)
+ assert.Equal(t, versionURL, result.Releases[packageVersion2].URL)
req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s.json", url, packageScope, packageName)).
AddBasicAuth(user.Name)
diff --git a/tests/integration/api_packages_test.go b/tests/integration/api_packages_test.go
index daf32e82f9..f10b098885 100644
--- a/tests/integration/api_packages_test.go
+++ b/tests/integration/api_packages_test.go
@@ -15,14 +15,15 @@ import (
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
- container_model "code.gitea.io/gitea/models/packages/container"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
+ container_module "code.gitea.io/gitea/modules/packages/container"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
packages_service "code.gitea.io/gitea/services/packages"
packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup"
+ repo_service "code.gitea.io/gitea/services/repository"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@@ -34,7 +35,7 @@ func TestPackageAPI(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
session := loginUser(t, user.Name)
tokenReadPackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage)
- tokenDeletePackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWritePackage)
+ tokenWritePackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWritePackage)
packageName := "test-package"
packageVersion := "1.0.3"
@@ -48,7 +49,7 @@ func TestPackageAPI(t *testing.T) {
t.Run("ListPackages", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s", user.Name)).
+ req := NewRequest(t, "GET", "/api/v1/packages/"+user.Name).
AddTokenAuth(tokenReadPackage)
resp := MakeRequest(t, req, http.StatusOK)
@@ -82,47 +83,101 @@ func TestPackageAPI(t *testing.T) {
assert.Equal(t, packageVersion, p.Version)
assert.NotNil(t, p.Creator)
assert.Equal(t, user.Name, p.Creator.UserName)
+ })
- t.Run("RepositoryLink", func(t *testing.T) {
- defer tests.PrintCurrentTest(t)()
+ t.Run("ListPackageVersions", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
- p, err := packages_model.GetPackageByName(db.DefaultContext, user.ID, packages_model.TypeGeneric, packageName)
- assert.NoError(t, err)
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s", user.Name, packageName)).
+ AddTokenAuth(tokenReadPackage)
+ resp := MakeRequest(t, req, http.StatusOK)
- // no repository link
- req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
- AddTokenAuth(tokenReadPackage)
- resp := MakeRequest(t, req, http.StatusOK)
+ var apiPackages []*api.Package
+ DecodeJSON(t, resp, &apiPackages)
+
+ assert.Len(t, apiPackages, 1)
+ assert.Equal(t, string(packages_model.TypeGeneric), apiPackages[0].Type)
+ assert.Equal(t, packageName, apiPackages[0].Name)
+ assert.Equal(t, packageVersion, apiPackages[0].Version)
+ })
+
+ t.Run("LatestPackageVersion", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
- var ap1 *api.Package
- DecodeJSON(t, resp, &ap1)
- assert.Nil(t, ap1.Repository)
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/latest", user.Name, packageName)).
+ AddTokenAuth(tokenReadPackage)
+ resp := MakeRequest(t, req, http.StatusOK)
- // link to public repository
- assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 1))
+ var apiPackage *api.Package
+ DecodeJSON(t, resp, &apiPackage)
- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
- AddTokenAuth(tokenReadPackage)
- resp = MakeRequest(t, req, http.StatusOK)
+ assert.Equal(t, string(packages_model.TypeGeneric), apiPackage.Type)
+ assert.Equal(t, packageName, apiPackage.Name)
+ assert.Equal(t, packageVersion, apiPackage.Version)
+ })
- var ap2 *api.Package
- DecodeJSON(t, resp, &ap2)
- assert.NotNil(t, ap2.Repository)
- assert.EqualValues(t, 1, ap2.Repository.ID)
+ t.Run("RepositoryLink", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
- // link to private repository
- assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 2))
+ _, err := packages_model.GetPackageByName(db.DefaultContext, user.ID, packages_model.TypeGeneric, packageName)
+ assert.NoError(t, err)
- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
- AddTokenAuth(tokenReadPackage)
- resp = MakeRequest(t, req, http.StatusOK)
+ // no repository link
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
+ AddTokenAuth(tokenReadPackage)
+ resp := MakeRequest(t, req, http.StatusOK)
- var ap3 *api.Package
- DecodeJSON(t, resp, &ap3)
- assert.Nil(t, ap3.Repository)
+ var ap1 *api.Package
+ DecodeJSON(t, resp, &ap1)
+ assert.Nil(t, ap1.Repository)
- assert.NoError(t, packages_model.UnlinkRepositoryFromAllPackages(db.DefaultContext, 2))
+ // create a repository
+ newRepo, err := repo_service.CreateRepository(db.DefaultContext, user, user, repo_service.CreateRepoOptions{
+ Name: "repo4",
})
+ assert.NoError(t, err)
+
+ // link to public repository
+ req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/link/%s", user.Name, packageName, newRepo.Name)).AddTokenAuth(tokenWritePackage)
+ MakeRequest(t, req, http.StatusCreated)
+
+ req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
+ AddTokenAuth(tokenReadPackage)
+ resp = MakeRequest(t, req, http.StatusOK)
+
+ var ap2 *api.Package
+ DecodeJSON(t, resp, &ap2)
+ assert.NotNil(t, ap2.Repository)
+ assert.Equal(t, newRepo.ID, ap2.Repository.ID)
+
+ // link to repository without write access, should fail
+ req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/link/%s", user.Name, packageName, "repo3")).AddTokenAuth(tokenWritePackage)
+ MakeRequest(t, req, http.StatusNotFound)
+
+ // remove link
+ req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/unlink", user.Name, packageName)).AddTokenAuth(tokenWritePackage)
+ MakeRequest(t, req, http.StatusNoContent)
+
+ req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
+ AddTokenAuth(tokenReadPackage)
+ resp = MakeRequest(t, req, http.StatusOK)
+
+ var ap3 *api.Package
+ DecodeJSON(t, resp, &ap3)
+ assert.Nil(t, ap3.Repository)
+
+ // force link to a repository the currently logged-in user doesn't have access to
+ privateRepoID := int64(6)
+ assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, ap1.ID, privateRepoID))
+
+ req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).AddTokenAuth(tokenReadPackage)
+ resp = MakeRequest(t, req, http.StatusOK)
+
+ var ap4 *api.Package
+ DecodeJSON(t, resp, &ap4)
+ assert.Nil(t, ap4.Repository)
+
+ assert.NoError(t, packages_model.UnlinkRepositoryFromAllPackages(db.DefaultContext, privateRepoID))
})
t.Run("ListPackageFiles", func(t *testing.T) {
@@ -152,11 +207,11 @@ func TestPackageAPI(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s", user.Name, packageName, packageVersion)).
- AddTokenAuth(tokenDeletePackage)
+ AddTokenAuth(tokenWritePackage)
MakeRequest(t, req, http.StatusNotFound)
req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
- AddTokenAuth(tokenDeletePackage)
+ AddTokenAuth(tokenWritePackage)
MakeRequest(t, req, http.StatusNoContent)
})
}
@@ -384,7 +439,7 @@ func TestPackageAccess(t *testing.T) {
{limitedOrgNoMember, http.StatusOK},
{publicOrgNoMember, http.StatusOK},
} {
- req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s", target.Owner.Name)).
+ req := NewRequest(t, "GET", "/api/v1/packages/"+target.Owner.Name).
AddTokenAuth(tokenReadPackage)
MakeRequest(t, req, target.ExpectedStatus)
}
@@ -483,7 +538,7 @@ func TestPackageCleanup(t *testing.T) {
assert.NoError(t, err)
assert.NotEmpty(t, pbs)
- _, err = packages_model.GetInternalVersionByNameAndVersion(db.DefaultContext, user.ID, packages_model.TypeContainer, "cleanup-test", container_model.UploadVersion)
+ _, err = packages_model.GetInternalVersionByNameAndVersion(db.DefaultContext, user.ID, packages_model.TypeContainer, "cleanup-test", container_module.UploadVersion)
assert.NoError(t, err)
err = packages_cleanup_service.CleanupTask(db.DefaultContext, duration)
@@ -493,7 +548,7 @@ func TestPackageCleanup(t *testing.T) {
assert.NoError(t, err)
assert.Empty(t, pbs)
- _, err = packages_model.GetInternalVersionByNameAndVersion(db.DefaultContext, user.ID, packages_model.TypeContainer, "cleanup-test", container_model.UploadVersion)
+ _, err = packages_model.GetInternalVersionByNameAndVersion(db.DefaultContext, user.ID, packages_model.TypeContainer, "cleanup-test", container_module.UploadVersion)
assert.ErrorIs(t, err, packages_model.ErrPackageNotExist)
})
@@ -581,12 +636,16 @@ func TestPackageCleanup(t *testing.T) {
},
{
Name: "Mixed",
- Versions: []version{
- {Version: "keep", ShouldExist: true, Created: time.Now().Add(time.Duration(10000)).Unix()},
- {Version: "dummy", ShouldExist: true, Created: 1},
- {Version: "test-3", ShouldExist: true},
- {Version: "test-4", ShouldExist: false, Created: 1},
- },
+ Versions: func(limit, removeDays int) []version {
+ aa := []version{
+ {Version: "keep", ShouldExist: true, Created: time.Now().Add(time.Duration(10000)).Unix()},
+ {Version: "dummy", ShouldExist: true, Created: 1},
+ }
+ for i := range limit {
+ aa = append(aa, version{Version: fmt.Sprintf("test-%v", i+3), ShouldExist: util.Iif(i < removeDays, true, false), Created: time.Now().AddDate(0, 0, -i).Unix()})
+ }
+ return aa
+ }(220, 7),
Rule: &packages_model.PackageCleanupRule{
Enabled: true,
KeepCount: 1,
@@ -631,7 +690,7 @@ func TestPackageCleanup(t *testing.T) {
err = packages_service.DeletePackageVersionAndReferences(db.DefaultContext, pv)
assert.NoError(t, err)
} else {
- assert.ErrorIs(t, err, packages_model.ErrPackageNotExist)
+ assert.ErrorIs(t, err, packages_model.ErrPackageNotExist, v.Version)
}
}
diff --git a/tests/integration/api_packages_vagrant_test.go b/tests/integration/api_packages_vagrant_test.go
index a5e954f3a2..1743e37222 100644
--- a/tests/integration/api_packages_vagrant_test.go
+++ b/tests/integration/api_packages_vagrant_test.go
@@ -35,7 +35,7 @@ func TestPackageVagrant(t *testing.T) {
packageDescription := "Test Description"
packageProvider := "virtualbox"
- filename := fmt.Sprintf("%s.box", packageProvider)
+ filename := packageProvider + ".box"
infoContent, _ := json.Marshal(map[string]string{
"description": packageDescription,
@@ -59,7 +59,7 @@ func TestPackageVagrant(t *testing.T) {
t.Run("Authenticate", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- authenticateURL := fmt.Sprintf("%s/authenticate", root)
+ authenticateURL := root + "/authenticate"
req := NewRequest(t, "GET", authenticateURL)
MakeRequest(t, req, http.StatusUnauthorized)
diff --git a/tests/integration/api_private_serv_test.go b/tests/integration/api_private_serv_test.go
index 2001bb1fad..b0dd0cf049 100644
--- a/tests/integration/api_private_serv_test.go
+++ b/tests/integration/api_private_serv_test.go
@@ -17,7 +17,7 @@ import (
func TestAPIPrivateNoServ(t *testing.T) {
onGiteaRun(t, func(*testing.T, *url.URL) {
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
key, user, err := private.ServNoCommand(ctx, 1)
assert.NoError(t, err)
@@ -39,7 +39,7 @@ func TestAPIPrivateNoServ(t *testing.T) {
func TestAPIPrivateServ(t *testing.T) {
onGiteaRun(t, func(*testing.T, *url.URL) {
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
// Can push to a repo we own
diff --git a/tests/integration/api_pull_commits_test.go b/tests/integration/api_pull_commits_test.go
index 5ffc8158f3..f43ad7d3be 100644
--- a/tests/integration/api_pull_commits_test.go
+++ b/tests/integration/api_pull_commits_test.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestAPIPullCommits(t *testing.T) {
@@ -29,9 +30,7 @@ func TestAPIPullCommits(t *testing.T) {
var commits []*api.Commit
DecodeJSON(t, resp, &commits)
- if !assert.Len(t, commits, 2) {
- return
- }
+ require.Len(t, commits, 2)
assert.Equal(t, "985f0301dba5e7b34be866819cd15ad3d8f508ee", commits[0].SHA)
assert.Equal(t, "5c050d3b6d2db231ab1f64e324f1b6b9a0b181c2", commits[1].SHA)
diff --git a/tests/integration/api_pull_review_test.go b/tests/integration/api_pull_review_test.go
index ba6b62d0d7..1fc65ddea8 100644
--- a/tests/integration/api_pull_review_test.go
+++ b/tests/integration/api_pull_review_test.go
@@ -21,6 +21,7 @@ import (
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
"xorm.io/builder"
)
@@ -39,21 +40,20 @@ func TestAPIPullReview(t *testing.T) {
var reviews []*api.PullReview
DecodeJSON(t, resp, &reviews)
- if !assert.Len(t, reviews, 8) {
- return
- }
+ require.Len(t, reviews, 8)
+
for _, r := range reviews {
- assert.EqualValues(t, pullIssue.HTMLURL(), r.HTMLPullURL)
+ assert.Equal(t, pullIssue.HTMLURL(), r.HTMLPullURL)
}
assert.EqualValues(t, 8, reviews[3].ID)
assert.EqualValues(t, "APPROVED", reviews[3].State)
- assert.EqualValues(t, 0, reviews[3].CodeCommentsCount)
+ assert.Equal(t, 0, reviews[3].CodeCommentsCount)
assert.True(t, reviews[3].Stale)
assert.False(t, reviews[3].Official)
assert.EqualValues(t, 10, reviews[5].ID)
assert.EqualValues(t, "REQUEST_CHANGES", reviews[5].State)
- assert.EqualValues(t, 1, reviews[5].CodeCommentsCount)
+ assert.Equal(t, 1, reviews[5].CodeCommentsCount)
assert.EqualValues(t, -1, reviews[5].Reviewer.ID) // ghost user
assert.False(t, reviews[5].Stale)
assert.True(t, reviews[5].Official)
@@ -64,13 +64,13 @@ func TestAPIPullReview(t *testing.T) {
resp = MakeRequest(t, req, http.StatusOK)
var review api.PullReview
DecodeJSON(t, resp, &review)
- assert.EqualValues(t, *reviews[3], review)
+ assert.Equal(t, *reviews[3], review)
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls/%d/reviews/%d", repo.OwnerName, repo.Name, pullIssue.Index, reviews[5].ID).
AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &review)
- assert.EqualValues(t, *reviews[5], review)
+ assert.Equal(t, *reviews[5], review)
// test GetPullReviewComments
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 7})
@@ -80,11 +80,11 @@ func TestAPIPullReview(t *testing.T) {
var reviewComments []*api.PullReviewComment
DecodeJSON(t, resp, &reviewComments)
assert.Len(t, reviewComments, 1)
- assert.EqualValues(t, "Ghost", reviewComments[0].Poster.UserName)
- assert.EqualValues(t, "a review from a deleted user", reviewComments[0].Body)
- assert.EqualValues(t, comment.ID, reviewComments[0].ID)
+ assert.Equal(t, "Ghost", reviewComments[0].Poster.UserName)
+ assert.Equal(t, "a review from a deleted user", reviewComments[0].Body)
+ assert.Equal(t, comment.ID, reviewComments[0].ID)
assert.EqualValues(t, comment.UpdatedUnix, reviewComments[0].Updated.Unix())
- assert.EqualValues(t, comment.HTMLURL(db.DefaultContext), reviewComments[0].HTMLURL)
+ assert.Equal(t, comment.HTMLURL(db.DefaultContext), reviewComments[0].HTMLURL)
// test CreatePullReview
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{
@@ -113,7 +113,7 @@ func TestAPIPullReview(t *testing.T) {
DecodeJSON(t, resp, &review)
assert.EqualValues(t, 6, review.ID)
assert.EqualValues(t, "PENDING", review.State)
- assert.EqualValues(t, 3, review.CodeCommentsCount)
+ assert.Equal(t, 3, review.CodeCommentsCount)
// test SubmitPullReview
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d", repo.OwnerName, repo.Name, pullIssue.Index, review.ID), &api.SubmitPullReviewOptions{
@@ -124,7 +124,7 @@ func TestAPIPullReview(t *testing.T) {
DecodeJSON(t, resp, &review)
assert.EqualValues(t, 6, review.ID)
assert.EqualValues(t, "APPROVED", review.State)
- assert.EqualValues(t, 3, review.CodeCommentsCount)
+ assert.Equal(t, 3, review.CodeCommentsCount)
// test dismiss review
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews/%d/dismissals", repo.OwnerName, repo.Name, pullIssue.Index, review.ID), &api.DismissPullReviewOptions{
@@ -151,7 +151,7 @@ func TestAPIPullReview(t *testing.T) {
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &review)
assert.EqualValues(t, "COMMENT", review.State)
- assert.EqualValues(t, 0, review.CodeCommentsCount)
+ assert.Equal(t, 0, review.CodeCommentsCount)
req = NewRequestf(t, http.MethodDelete, "/api/v1/repos/%s/%s/pulls/%d/reviews/%d", repo.OwnerName, repo.Name, pullIssue.Index, review.ID).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)
@@ -179,7 +179,7 @@ func TestAPIPullReview(t *testing.T) {
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &commentReview)
assert.EqualValues(t, "COMMENT", commentReview.State)
- assert.EqualValues(t, 2, commentReview.CodeCommentsCount)
+ assert.Equal(t, 2, commentReview.CodeCommentsCount)
assert.Empty(t, commentReview.Body)
assert.False(t, commentReview.Dismissed)
@@ -194,8 +194,8 @@ func TestAPIPullReview(t *testing.T) {
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &commentReview)
assert.EqualValues(t, "COMMENT", commentReview.State)
- assert.EqualValues(t, 0, commentReview.CodeCommentsCount)
- assert.EqualValues(t, commentBody, commentReview.Body)
+ assert.Equal(t, 0, commentReview.CodeCommentsCount)
+ assert.Equal(t, commentBody, commentReview.Body)
assert.False(t, commentReview.Dismissed)
// test CreatePullReview Comment without body and no comments
@@ -207,7 +207,7 @@ func TestAPIPullReview(t *testing.T) {
resp = MakeRequest(t, req, http.StatusUnprocessableEntity)
errMap := make(map[string]any)
json.Unmarshal(resp.Body.Bytes(), &errMap)
- assert.EqualValues(t, "review event COMMENT requires a body or a comment", errMap["message"].(string))
+ assert.Equal(t, "review event COMMENT requires a body or a comment", errMap["message"].(string))
// test get review requests
// to make it simple, use same api with get review
@@ -221,14 +221,14 @@ func TestAPIPullReview(t *testing.T) {
DecodeJSON(t, resp, &reviews)
assert.EqualValues(t, 11, reviews[0].ID)
assert.EqualValues(t, "REQUEST_REVIEW", reviews[0].State)
- assert.EqualValues(t, 0, reviews[0].CodeCommentsCount)
+ assert.Equal(t, 0, reviews[0].CodeCommentsCount)
assert.False(t, reviews[0].Stale)
assert.True(t, reviews[0].Official)
- assert.EqualValues(t, "test_team", reviews[0].ReviewerTeam.Name)
+ assert.Equal(t, "test_team", reviews[0].ReviewerTeam.Name)
assert.EqualValues(t, 12, reviews[1].ID)
assert.EqualValues(t, "REQUEST_REVIEW", reviews[1].State)
- assert.EqualValues(t, 0, reviews[0].CodeCommentsCount)
+ assert.Equal(t, 0, reviews[0].CodeCommentsCount)
assert.False(t, reviews[1].Stale)
assert.True(t, reviews[1].Official)
assert.EqualValues(t, 1, reviews[1].Reviewer.ID)
diff --git a/tests/integration/api_pull_test.go b/tests/integration/api_pull_test.go
index 969e110895..f2df6021e1 100644
--- a/tests/integration/api_pull_test.go
+++ b/tests/integration/api_pull_test.go
@@ -5,11 +5,13 @@ package integration
import (
"bytes"
- "context"
"fmt"
"io"
"net/http"
+ "net/url"
+ "strings"
"testing"
+ "time"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
@@ -18,11 +20,15 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/gitdiff"
issue_service "code.gitea.io/gitea/services/issue"
+ pull_service "code.gitea.io/gitea/services/pull"
+ files_service "code.gitea.io/gitea/services/repository/files"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@@ -51,30 +57,30 @@ func TestAPIViewPulls(t *testing.T) {
assert.Empty(t, pull.RequestedReviewersTeams)
assert.EqualValues(t, 5, pull.RequestedReviewers[0].ID)
assert.EqualValues(t, 6, pull.RequestedReviewers[1].ID)
- assert.EqualValues(t, 1, pull.ChangedFiles)
if assert.EqualValues(t, 5, pull.ID) {
resp = ctx.Session.MakeRequest(t, NewRequest(t, "GET", pull.DiffURL), http.StatusOK)
bs, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
- patch, err := gitdiff.ParsePatch(context.Background(), 1000, 5000, 10, bytes.NewReader(bs), "")
+ patch, err := gitdiff.ParsePatch(t.Context(), 1000, 5000, 10, bytes.NewReader(bs), "")
assert.NoError(t, err)
- if assert.Len(t, patch.Files, pull.ChangedFiles) {
+ if assert.Len(t, patch.Files, 1) {
assert.Equal(t, "File-WoW", patch.Files[0].Name)
// FIXME: The old name should be empty if it's a file add type
assert.Equal(t, "File-WoW", patch.Files[0].OldName)
- assert.EqualValues(t, pull.Additions, patch.Files[0].Addition)
- assert.EqualValues(t, pull.Deletions, patch.Files[0].Deletion)
+ assert.Equal(t, 1, patch.Files[0].Addition)
+ assert.Equal(t, 0, patch.Files[0].Deletion)
assert.Equal(t, gitdiff.DiffFileAdd, patch.Files[0].Type)
}
t.Run(fmt.Sprintf("APIGetPullFiles_%d", pull.ID),
doAPIGetPullFiles(ctx, pull, func(t *testing.T, files []*api.ChangedFile) {
- if assert.Len(t, files, pull.ChangedFiles) {
+ if assert.Len(t, files, 1) {
assert.Equal(t, "File-WoW", files[0].Filename)
assert.Empty(t, files[0].PreviousFilename)
- assert.EqualValues(t, pull.Additions, files[0].Additions)
- assert.EqualValues(t, pull.Deletions, files[0].Deletions)
+ assert.Equal(t, 1, files[0].Additions)
+ assert.Equal(t, 1, files[0].Changes)
+ assert.Equal(t, 0, files[0].Deletions)
assert.Equal(t, "added", files[0].Status)
}
}))
@@ -88,53 +94,51 @@ func TestAPIViewPulls(t *testing.T) {
assert.EqualValues(t, 4, pull.RequestedReviewers[1].ID)
assert.EqualValues(t, 2, pull.RequestedReviewers[2].ID)
assert.EqualValues(t, 5, pull.RequestedReviewers[3].ID)
- assert.EqualValues(t, 1, pull.ChangedFiles)
if assert.EqualValues(t, 2, pull.ID) {
resp = ctx.Session.MakeRequest(t, NewRequest(t, "GET", pull.DiffURL), http.StatusOK)
bs, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
- patch, err := gitdiff.ParsePatch(context.Background(), 1000, 5000, 10, bytes.NewReader(bs), "")
+ patch, err := gitdiff.ParsePatch(t.Context(), 1000, 5000, 10, bytes.NewReader(bs), "")
assert.NoError(t, err)
- if assert.Len(t, patch.Files, pull.ChangedFiles) {
+ if assert.Len(t, patch.Files, 1) {
assert.Equal(t, "README.md", patch.Files[0].Name)
assert.Equal(t, "README.md", patch.Files[0].OldName)
- assert.EqualValues(t, pull.Additions, patch.Files[0].Addition)
- assert.EqualValues(t, pull.Deletions, patch.Files[0].Deletion)
+ assert.Equal(t, 4, patch.Files[0].Addition)
+ assert.Equal(t, 1, patch.Files[0].Deletion)
assert.Equal(t, gitdiff.DiffFileChange, patch.Files[0].Type)
}
t.Run(fmt.Sprintf("APIGetPullFiles_%d", pull.ID),
doAPIGetPullFiles(ctx, pull, func(t *testing.T, files []*api.ChangedFile) {
- if assert.Len(t, files, pull.ChangedFiles) {
+ if assert.Len(t, files, 1) {
assert.Equal(t, "README.md", files[0].Filename)
// FIXME: The PreviousFilename name should be the same as Filename if it's a file change
- assert.Equal(t, "", files[0].PreviousFilename)
- assert.EqualValues(t, pull.Additions, files[0].Additions)
- assert.EqualValues(t, pull.Deletions, files[0].Deletions)
+ assert.Empty(t, files[0].PreviousFilename)
+ assert.Equal(t, 4, files[0].Additions)
+ assert.Equal(t, 1, files[0].Deletions)
assert.Equal(t, "changed", files[0].Status)
}
}))
}
- pull = pulls[2]
+ pull = pulls[0]
assert.EqualValues(t, 1, pull.Poster.ID)
- assert.Len(t, pull.RequestedReviewers, 1)
+ assert.Len(t, pull.RequestedReviewers, 2)
assert.Empty(t, pull.RequestedReviewersTeams)
- assert.EqualValues(t, 1, pull.RequestedReviewers[0].ID)
- assert.EqualValues(t, 0, pull.ChangedFiles)
+ assert.EqualValues(t, 5, pull.RequestedReviewers[0].ID)
- if assert.EqualValues(t, 1, pull.ID) {
+ if assert.EqualValues(t, 5, pull.ID) {
resp = ctx.Session.MakeRequest(t, NewRequest(t, "GET", pull.DiffURL), http.StatusOK)
bs, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
- patch, err := gitdiff.ParsePatch(context.Background(), 1000, 5000, 10, bytes.NewReader(bs), "")
+ patch, err := gitdiff.ParsePatch(t.Context(), 1000, 5000, 10, bytes.NewReader(bs), "")
assert.NoError(t, err)
- assert.EqualValues(t, pull.ChangedFiles, patch.NumFiles)
+ assert.Len(t, patch.Files, 1)
t.Run(fmt.Sprintf("APIGetPullFiles_%d", pull.ID),
doAPIGetPullFiles(ctx, pull, func(t *testing.T, files []*api.ChangedFile) {
- assert.Len(t, files, pull.ChangedFiles)
+ assert.Len(t, files, 1)
}))
}
}
@@ -196,7 +200,7 @@ func TestAPICreatePullSuccess(t *testing.T) {
session := loginUser(t, owner11.Name)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &api.CreatePullRequestOption{
- Head: fmt.Sprintf("%s:master", owner11.Name),
+ Head: owner11.Name + ":master",
Base: "master",
Title: "create a failure pr",
}).AddTokenAuth(token)
@@ -216,7 +220,7 @@ func TestAPICreatePullBasePermission(t *testing.T) {
session := loginUser(t, user4.Name)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
opts := &api.CreatePullRequestOption{
- Head: fmt.Sprintf("%s:master", repo11.OwnerName),
+ Head: repo11.OwnerName + ":master",
Base: "master",
Title: "create a failure pr",
}
@@ -244,7 +248,7 @@ func TestAPICreatePullHeadPermission(t *testing.T) {
session := loginUser(t, user4.Name)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
opts := &api.CreatePullRequestOption{
- Head: fmt.Sprintf("%s:master", repo11.OwnerName),
+ Head: repo11.OwnerName + ":master",
Base: "master",
Title: "create a failure pr",
}
@@ -272,7 +276,7 @@ func TestAPICreatePullSameRepoSuccess(t *testing.T) {
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner.Name, repo.Name), &api.CreatePullRequestOption{
- Head: fmt.Sprintf("%s:pr-to-update", owner.Name),
+ Head: owner.Name + ":pr-to-update",
Base: "master",
Title: "successfully create a PR between branches of the same repository",
}).AddTokenAuth(token)
@@ -293,7 +297,7 @@ func TestAPICreatePullWithFieldsSuccess(t *testing.T) {
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
opts := &api.CreatePullRequestOption{
- Head: fmt.Sprintf("%s:master", owner11.Name),
+ Head: owner11.Name + ":master",
Base: "master",
Title: "create a failure pr",
Body: "foobaaar",
@@ -310,12 +314,12 @@ func TestAPICreatePullWithFieldsSuccess(t *testing.T) {
DecodeJSON(t, res, pull)
assert.NotNil(t, pull.Milestone)
- assert.EqualValues(t, opts.Milestone, pull.Milestone.ID)
+ assert.Equal(t, opts.Milestone, pull.Milestone.ID)
if assert.Len(t, pull.Assignees, 1) {
- assert.EqualValues(t, opts.Assignees[0], owner10.Name)
+ assert.Equal(t, opts.Assignees[0], owner10.Name)
}
assert.NotNil(t, pull.Labels)
- assert.EqualValues(t, opts.Labels[0], pull.Labels[0].ID)
+ assert.Equal(t, opts.Labels[0], pull.Labels[0].ID)
}
func TestAPICreatePullWithFieldsFailure(t *testing.T) {
@@ -331,7 +335,7 @@ func TestAPICreatePullWithFieldsFailure(t *testing.T) {
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
opts := &api.CreatePullRequestOption{
- Head: fmt.Sprintf("%s:master", owner11.Name),
+ Head: owner11.Name + ":master",
Base: "master",
}
@@ -369,7 +373,7 @@ func TestAPIEditPull(t *testing.T) {
apiPull := new(api.PullRequest)
resp := MakeRequest(t, req, http.StatusCreated)
DecodeJSON(t, resp, apiPull)
- assert.EqualValues(t, "master", apiPull.Base.Name)
+ assert.Equal(t, "master", apiPull.Base.Name)
newTitle := "edit a this pr"
newBody := "edited body"
@@ -380,7 +384,7 @@ func TestAPIEditPull(t *testing.T) {
}).AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusCreated)
DecodeJSON(t, resp, apiPull)
- assert.EqualValues(t, "feature/1", apiPull.Base.Name)
+ assert.Equal(t, "feature/1", apiPull.Base.Name)
// check comment history
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: apiPull.ID})
err := pull.LoadIssue(db.DefaultContext)
@@ -427,3 +431,94 @@ func TestAPICommitPullRequest(t *testing.T) {
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/commits/%s/pull", owner.Name, repo.Name, invalidCommitSHA).AddTokenAuth(ctx.Token)
ctx.Session.MakeRequest(t, req, http.StatusNotFound)
}
+
+func TestAPIViewPullFilesWithHeadRepoDeleted(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+
+ ctx := NewAPITestContext(t, "user1", baseRepo.Name, auth_model.AccessTokenScopeAll)
+
+ doAPIForkRepository(ctx, "user2")(t)
+
+ forkedRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ForkID: baseRepo.ID, OwnerName: "user1"})
+
+ // add a new file to the forked repo
+ addFileToForkedResp, err := files_service.ChangeRepoFiles(git.DefaultContext, forkedRepo, user1, &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "create",
+ TreePath: "file_1.txt",
+ ContentReader: strings.NewReader("file1"),
+ },
+ },
+ Message: "add file1",
+ OldBranch: "master",
+ NewBranch: "fork-branch-1",
+ Author: &files_service.IdentityOptions{
+ GitUserName: user1.Name,
+ GitUserEmail: user1.Email,
+ },
+ Committer: &files_service.IdentityOptions{
+ GitUserName: user1.Name,
+ GitUserEmail: user1.Email,
+ },
+ Dates: &files_service.CommitDateOptions{
+ Author: time.Now(),
+ Committer: time.Now(),
+ },
+ })
+ assert.NoError(t, err)
+ assert.NotEmpty(t, addFileToForkedResp)
+
+ // create Pull
+ pullIssue := &issues_model.Issue{
+ RepoID: baseRepo.ID,
+ Title: "Test pull-request-target-event",
+ PosterID: user1.ID,
+ Poster: user1,
+ IsPull: true,
+ }
+ pullRequest := &issues_model.PullRequest{
+ HeadRepoID: forkedRepo.ID,
+ BaseRepoID: baseRepo.ID,
+ HeadBranch: "fork-branch-1",
+ BaseBranch: "master",
+ HeadRepo: forkedRepo,
+ BaseRepo: baseRepo,
+ Type: issues_model.PullRequestGitea,
+ }
+
+ prOpts := &pull_service.NewPullRequestOptions{Repo: baseRepo, Issue: pullIssue, PullRequest: pullRequest}
+ err = pull_service.NewPullRequest(git.DefaultContext, prOpts)
+ assert.NoError(t, err)
+ pr := convert.ToAPIPullRequest(t.Context(), pullRequest, user1)
+
+ ctx = NewAPITestContext(t, "user2", baseRepo.Name, auth_model.AccessTokenScopeAll)
+ doAPIGetPullFiles(ctx, pr, func(t *testing.T, files []*api.ChangedFile) {
+ if assert.Len(t, files, 1) {
+ assert.Equal(t, "file_1.txt", files[0].Filename)
+ assert.Empty(t, files[0].PreviousFilename)
+ assert.Equal(t, 1, files[0].Additions)
+ assert.Equal(t, 1, files[0].Changes)
+ assert.Equal(t, 0, files[0].Deletions)
+ assert.Equal(t, "added", files[0].Status)
+ }
+ })(t)
+
+ // delete the head repository of the pull request
+ forkCtx := NewAPITestContext(t, "user1", forkedRepo.Name, auth_model.AccessTokenScopeAll)
+ doAPIDeleteRepository(forkCtx)(t)
+
+ doAPIGetPullFiles(ctx, pr, func(t *testing.T, files []*api.ChangedFile) {
+ if assert.Len(t, files, 1) {
+ assert.Equal(t, "file_1.txt", files[0].Filename)
+ assert.Empty(t, files[0].PreviousFilename)
+ assert.Equal(t, 1, files[0].Additions)
+ assert.Equal(t, 1, files[0].Changes)
+ assert.Equal(t, 0, files[0].Deletions)
+ assert.Equal(t, "added", files[0].Status)
+ }
+ })(t)
+ })
+}
diff --git a/tests/integration/api_releases_test.go b/tests/integration/api_releases_test.go
index b3d4928b7b..a3dbc0363b 100644
--- a/tests/integration/api_releases_test.go
+++ b/tests/integration/api_releases_test.go
@@ -97,7 +97,7 @@ func createNewReleaseUsingAPI(t *testing.T, token string, owner *user_model.User
Title: newRelease.Title,
}
unittest.AssertExistsAndLoadBean(t, rel)
- assert.EqualValues(t, newRelease.Note, rel.Note)
+ assert.Equal(t, newRelease.Note, rel.Note)
return &newRelease
}
@@ -151,7 +151,7 @@ func TestAPICreateAndUpdateRelease(t *testing.T) {
Title: newRelease.Title,
}
unittest.AssertExistsAndLoadBean(t, rel)
- assert.EqualValues(t, rel.Note, newRelease.Note)
+ assert.Equal(t, rel.Note, newRelease.Note)
}
func TestAPICreateProtectedTagRelease(t *testing.T) {
@@ -329,7 +329,7 @@ func TestAPIUploadAssetRelease(t *testing.T) {
var attachment *api.Attachment
DecodeJSON(t, resp, &attachment)
- assert.EqualValues(t, filename, attachment.Name)
+ assert.Equal(t, filename, attachment.Name)
assert.EqualValues(t, 104, attachment.Size)
req = NewRequestWithBody(t, http.MethodPost, assetURL+"?name=test-asset", bytes.NewReader(body.Bytes())).
@@ -340,7 +340,7 @@ func TestAPIUploadAssetRelease(t *testing.T) {
var attachment2 *api.Attachment
DecodeJSON(t, resp, &attachment2)
- assert.EqualValues(t, "test-asset", attachment2.Name)
+ assert.Equal(t, "test-asset", attachment2.Name)
assert.EqualValues(t, 104, attachment2.Size)
})
@@ -358,7 +358,7 @@ func TestAPIUploadAssetRelease(t *testing.T) {
var attachment *api.Attachment
DecodeJSON(t, resp, &attachment)
- assert.EqualValues(t, "stream.bin", attachment.Name)
+ assert.Equal(t, "stream.bin", attachment.Name)
assert.EqualValues(t, 104, attachment.Size)
})
}
diff --git a/tests/integration/api_repo_archive_test.go b/tests/integration/api_repo_archive_test.go
index 8589199da3..97c2c0d54b 100644
--- a/tests/integration/api_repo_archive_test.go
+++ b/tests/integration/api_repo_archive_test.go
@@ -12,7 +12,9 @@ import (
"testing"
auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/tests"
@@ -48,7 +50,7 @@ func TestAPIDownloadArchive(t *testing.T) {
bs2, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
// The locked URL should give the same bytes as the non-locked one
- assert.EqualValues(t, bs, bs2)
+ assert.Equal(t, bs, bs2)
link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master.bundle", user2.Name, repo.Name))
resp = MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusOK)
@@ -58,9 +60,12 @@ func TestAPIDownloadArchive(t *testing.T) {
link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master", user2.Name, repo.Name))
MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusBadRequest)
+
+ t.Run("GitHubStyle", testAPIDownloadArchiveGitHubStyle)
+ t.Run("PrivateRepo", testAPIDownloadArchivePrivateRepo)
}
-func TestAPIDownloadArchive2(t *testing.T) {
+func testAPIDownloadArchiveGitHubStyle(t *testing.T) {
defer tests.PrepareTestEnv(t)()
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
@@ -88,14 +93,20 @@ func TestAPIDownloadArchive2(t *testing.T) {
bs2, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
// The locked URL should give the same bytes as the non-locked one
- assert.EqualValues(t, bs, bs2)
+ assert.Equal(t, bs, bs2)
link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/bundle/master", user2.Name, repo.Name))
resp = MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusOK)
bs, err = io.ReadAll(resp.Body)
assert.NoError(t, err)
assert.Len(t, bs, 382)
+}
- link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master", user2.Name, repo.Name))
- MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusBadRequest)
+func testAPIDownloadArchivePrivateRepo(t *testing.T) {
+ _ = repo_model.UpdateRepositoryColsNoAutoTime(t.Context(), &repo_model.Repository{ID: 1, IsPrivate: true}, "is_private")
+ MakeRequest(t, NewRequest(t, "HEAD", "/api/v1/repos/user2/repo1/archive/master.zip"), http.StatusNotFound)
+ MakeRequest(t, NewRequest(t, "HEAD", "/api/v1/repos/user2/repo1/zipball/master"), http.StatusNotFound)
+ _ = repo_model.UpdateRepoUnitPublicAccess(t.Context(), &repo_model.RepoUnit{RepoID: 1, Type: unit.TypeCode, AnonymousAccessMode: perm.AccessModeRead})
+ MakeRequest(t, NewRequest(t, "HEAD", "/api/v1/repos/user2/repo1/archive/master.zip"), http.StatusOK)
+ MakeRequest(t, NewRequest(t, "HEAD", "/api/v1/repos/user2/repo1/zipball/master"), http.StatusOK)
}
diff --git a/tests/integration/api_repo_branch_test.go b/tests/integration/api_repo_branch_test.go
index 63080b308c..066eb366b1 100644
--- a/tests/integration/api_repo_branch_test.go
+++ b/tests/integration/api_repo_branch_test.go
@@ -4,11 +4,11 @@
package integration
import (
- "bytes"
"fmt"
"io"
"net/http"
"net/url"
+ "strings"
"testing"
auth_model "code.gitea.io/gitea/models/auth"
@@ -42,8 +42,8 @@ func TestAPIRepoBranchesPlain(t *testing.T) {
var branches []*api.Branch
assert.NoError(t, json.Unmarshal(bs, &branches))
assert.Len(t, branches, 2)
- assert.EqualValues(t, "test_branch", branches[0].Name)
- assert.EqualValues(t, "master", branches[1].Name)
+ assert.Equal(t, "test_branch", branches[0].Name)
+ assert.Equal(t, "master", branches[1].Name)
link2, _ := url.Parse(fmt.Sprintf("/api/v1/repos/org3/%s/branches/test_branch", repo3.Name))
MakeRequest(t, NewRequest(t, "GET", link2.String()).AddTokenAuth(publicOnlyToken), http.StatusForbidden)
@@ -53,20 +53,20 @@ func TestAPIRepoBranchesPlain(t *testing.T) {
assert.NoError(t, err)
var branch api.Branch
assert.NoError(t, json.Unmarshal(bs, &branch))
- assert.EqualValues(t, "test_branch", branch.Name)
+ assert.Equal(t, "test_branch", branch.Name)
MakeRequest(t, NewRequest(t, "POST", link.String()).AddTokenAuth(publicOnlyToken), http.StatusForbidden)
req := NewRequest(t, "POST", link.String()).AddTokenAuth(token)
req.Header.Add("Content-Type", "application/json")
- req.Body = io.NopCloser(bytes.NewBufferString(`{"new_branch_name":"test_branch2", "old_branch_name": "test_branch", "old_ref_name":"refs/heads/test_branch"}`))
+ req.Body = io.NopCloser(strings.NewReader(`{"new_branch_name":"test_branch2", "old_branch_name": "test_branch", "old_ref_name":"refs/heads/test_branch"}`))
resp = MakeRequest(t, req, http.StatusCreated)
bs, err = io.ReadAll(resp.Body)
assert.NoError(t, err)
var branch2 api.Branch
assert.NoError(t, json.Unmarshal(bs, &branch2))
- assert.EqualValues(t, "test_branch2", branch2.Name)
- assert.EqualValues(t, branch.Commit.ID, branch2.Commit.ID)
+ assert.Equal(t, "test_branch2", branch2.Name)
+ assert.Equal(t, branch.Commit.ID, branch2.Commit.ID)
resp = MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusOK)
bs, err = io.ReadAll(resp.Body)
@@ -75,9 +75,9 @@ func TestAPIRepoBranchesPlain(t *testing.T) {
branches = []*api.Branch{}
assert.NoError(t, json.Unmarshal(bs, &branches))
assert.Len(t, branches, 3)
- assert.EqualValues(t, "test_branch", branches[0].Name)
- assert.EqualValues(t, "test_branch2", branches[1].Name)
- assert.EqualValues(t, "master", branches[2].Name)
+ assert.Equal(t, "test_branch", branches[0].Name)
+ assert.Equal(t, "test_branch2", branches[1].Name)
+ assert.Equal(t, "master", branches[2].Name)
link3, _ := url.Parse(fmt.Sprintf("/api/v1/repos/org3/%s/branches/test_branch2", repo3.Name))
MakeRequest(t, NewRequest(t, "DELETE", link3.String()), http.StatusNotFound)
@@ -104,8 +104,8 @@ func TestAPIRepoBranchesMirror(t *testing.T) {
var branches []*api.Branch
assert.NoError(t, json.Unmarshal(bs, &branches))
assert.Len(t, branches, 2)
- assert.EqualValues(t, "test_branch", branches[0].Name)
- assert.EqualValues(t, "master", branches[1].Name)
+ assert.Equal(t, "test_branch", branches[0].Name)
+ assert.Equal(t, "master", branches[1].Name)
link2, _ := url.Parse(fmt.Sprintf("/api/v1/repos/org3/%s/branches/test_branch", repo5.Name))
resp = MakeRequest(t, NewRequest(t, "GET", link2.String()).AddTokenAuth(token), http.StatusOK)
@@ -113,18 +113,18 @@ func TestAPIRepoBranchesMirror(t *testing.T) {
assert.NoError(t, err)
var branch api.Branch
assert.NoError(t, json.Unmarshal(bs, &branch))
- assert.EqualValues(t, "test_branch", branch.Name)
+ assert.Equal(t, "test_branch", branch.Name)
req := NewRequest(t, "POST", link.String()).AddTokenAuth(token)
req.Header.Add("Content-Type", "application/json")
- req.Body = io.NopCloser(bytes.NewBufferString(`{"new_branch_name":"test_branch2", "old_branch_name": "test_branch", "old_ref_name":"refs/heads/test_branch"}`))
+ req.Body = io.NopCloser(strings.NewReader(`{"new_branch_name":"test_branch2", "old_branch_name": "test_branch", "old_ref_name":"refs/heads/test_branch"}`))
resp = MakeRequest(t, req, http.StatusForbidden)
bs, err = io.ReadAll(resp.Body)
assert.NoError(t, err)
- assert.EqualValues(t, "{\"message\":\"Git Repository is a mirror.\",\"url\":\""+setting.AppURL+"api/swagger\"}\n", string(bs))
+ assert.Equal(t, "{\"message\":\"Git Repository is a mirror.\",\"url\":\""+setting.AppURL+"api/swagger\"}\n", string(bs))
resp = MakeRequest(t, NewRequest(t, "DELETE", link2.String()).AddTokenAuth(token), http.StatusForbidden)
bs, err = io.ReadAll(resp.Body)
assert.NoError(t, err)
- assert.EqualValues(t, "{\"message\":\"Git Repository is a mirror.\",\"url\":\""+setting.AppURL+"api/swagger\"}\n", string(bs))
+ assert.Equal(t, "{\"message\":\"Git Repository is a mirror.\",\"url\":\""+setting.AppURL+"api/swagger\"}\n", string(bs))
}
diff --git a/tests/integration/api_repo_collaborator_test.go b/tests/integration/api_repo_collaborator_test.go
index 463db1dfb1..11e2924e84 100644
--- a/tests/integration/api_repo_collaborator_test.go
+++ b/tests/integration/api_repo_collaborator_test.go
@@ -5,7 +5,6 @@ package integration
import (
"net/http"
- "net/url"
"testing"
auth_model "code.gitea.io/gitea/models/auth"
@@ -14,132 +13,145 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
)
func TestAPIRepoCollaboratorPermission(t *testing.T) {
- onGiteaRun(t, func(t *testing.T, u *url.URL) {
- repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
- repo2Owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo2.OwnerID})
+ defer tests.PrepareTestEnv(t)()
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
+ repo2Owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo2.OwnerID})
- user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
- user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
- user10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10})
- user11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 11})
- user34 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 34})
+ user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
+ user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
+ user10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10})
+ user11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 11})
+ user34 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 34})
- testCtx := NewAPITestContext(t, repo2Owner.Name, repo2.Name, auth_model.AccessTokenScopeWriteRepository)
+ testCtx := NewAPITestContext(t, repo2Owner.Name, repo2.Name, auth_model.AccessTokenScopeWriteRepository)
- t.Run("RepoOwnerShouldBeOwner", func(t *testing.T) {
- req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, repo2Owner.Name).
- AddTokenAuth(testCtx.Token)
- resp := MakeRequest(t, req, http.StatusOK)
+ t.Run("RepoOwnerShouldBeOwner", func(t *testing.T) {
+ req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, repo2Owner.Name).
+ AddTokenAuth(testCtx.Token)
+ resp := MakeRequest(t, req, http.StatusOK)
- var repoPermission api.RepoCollaboratorPermission
- DecodeJSON(t, resp, &repoPermission)
+ var repoPermission api.RepoCollaboratorPermission
+ DecodeJSON(t, resp, &repoPermission)
- assert.Equal(t, "owner", repoPermission.Permission)
- })
+ assert.Equal(t, "owner", repoPermission.Permission)
+ })
- t.Run("CollaboratorWithReadAccess", func(t *testing.T) {
- t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user4.Name, perm.AccessModeRead))
+ t.Run("CollaboratorWithReadAccess", func(t *testing.T) {
+ t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user4.Name, perm.AccessModeRead))
- req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user4.Name).
- AddTokenAuth(testCtx.Token)
- resp := MakeRequest(t, req, http.StatusOK)
+ req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user4.Name).
+ AddTokenAuth(testCtx.Token)
+ resp := MakeRequest(t, req, http.StatusOK)
- var repoPermission api.RepoCollaboratorPermission
- DecodeJSON(t, resp, &repoPermission)
+ var repoPermission api.RepoCollaboratorPermission
+ DecodeJSON(t, resp, &repoPermission)
- assert.Equal(t, "read", repoPermission.Permission)
- })
+ assert.Equal(t, "read", repoPermission.Permission)
+ })
- t.Run("CollaboratorWithWriteAccess", func(t *testing.T) {
- t.Run("AddUserAsCollaboratorWithWriteAccess", doAPIAddCollaborator(testCtx, user4.Name, perm.AccessModeWrite))
+ t.Run("CollaboratorWithWriteAccess", func(t *testing.T) {
+ t.Run("AddUserAsCollaboratorWithWriteAccess", doAPIAddCollaborator(testCtx, user4.Name, perm.AccessModeWrite))
- req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user4.Name).
- AddTokenAuth(testCtx.Token)
- resp := MakeRequest(t, req, http.StatusOK)
+ req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user4.Name).
+ AddTokenAuth(testCtx.Token)
+ resp := MakeRequest(t, req, http.StatusOK)
- var repoPermission api.RepoCollaboratorPermission
- DecodeJSON(t, resp, &repoPermission)
+ var repoPermission api.RepoCollaboratorPermission
+ DecodeJSON(t, resp, &repoPermission)
- assert.Equal(t, "write", repoPermission.Permission)
- })
+ assert.Equal(t, "write", repoPermission.Permission)
+ })
- t.Run("CollaboratorWithAdminAccess", func(t *testing.T) {
- t.Run("AddUserAsCollaboratorWithAdminAccess", doAPIAddCollaborator(testCtx, user4.Name, perm.AccessModeAdmin))
+ t.Run("CollaboratorWithAdminAccess", func(t *testing.T) {
+ t.Run("AddUserAsCollaboratorWithAdminAccess", doAPIAddCollaborator(testCtx, user4.Name, perm.AccessModeAdmin))
- req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user4.Name).
- AddTokenAuth(testCtx.Token)
- resp := MakeRequest(t, req, http.StatusOK)
+ req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user4.Name).
+ AddTokenAuth(testCtx.Token)
+ resp := MakeRequest(t, req, http.StatusOK)
- var repoPermission api.RepoCollaboratorPermission
- DecodeJSON(t, resp, &repoPermission)
+ var repoPermission api.RepoCollaboratorPermission
+ DecodeJSON(t, resp, &repoPermission)
- assert.Equal(t, "admin", repoPermission.Permission)
- })
+ assert.Equal(t, "admin", repoPermission.Permission)
+ })
- t.Run("CollaboratorNotFound", func(t *testing.T) {
- req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, "non-existent-user").
- AddTokenAuth(testCtx.Token)
- MakeRequest(t, req, http.StatusNotFound)
- })
+ t.Run("CollaboratorNotFound", func(t *testing.T) {
+ req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, "non-existent-user").
+ AddTokenAuth(testCtx.Token)
+ MakeRequest(t, req, http.StatusNotFound)
+ })
- t.Run("CollaboratorBlocked", func(t *testing.T) {
- ctx := NewAPITestContext(t, repo2Owner.Name, repo2.Name, auth_model.AccessTokenScopeWriteRepository)
- ctx.ExpectedCode = http.StatusForbidden
- doAPIAddCollaborator(ctx, user34.Name, perm.AccessModeAdmin)(t)
- })
+ t.Run("CollaboratorBlocked", func(t *testing.T) {
+ ctx := NewAPITestContext(t, repo2Owner.Name, repo2.Name, auth_model.AccessTokenScopeWriteRepository)
+ ctx.ExpectedCode = http.StatusForbidden
+ doAPIAddCollaborator(ctx, user34.Name, perm.AccessModeAdmin)(t)
+ })
+
+ t.Run("CollaboratorCanQueryItsPermissions", func(t *testing.T) {
+ t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead))
+
+ _session := loginUser(t, user5.Name)
+ _testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository)
+
+ req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user5.Name).
+ AddTokenAuth(_testCtx.Token)
+ resp := _session.MakeRequest(t, req, http.StatusOK)
- t.Run("CollaboratorCanQueryItsPermissions", func(t *testing.T) {
- t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead))
+ var repoPermission api.RepoCollaboratorPermission
+ DecodeJSON(t, resp, &repoPermission)
- _session := loginUser(t, user5.Name)
- _testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository)
+ assert.Equal(t, "read", repoPermission.Permission)
- req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user5.Name).
- AddTokenAuth(_testCtx.Token)
- resp := _session.MakeRequest(t, req, http.StatusOK)
+ t.Run("CollaboratorCanReadOwnPermission", func(t *testing.T) {
+ session := loginUser(t, user5.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
- var repoPermission api.RepoCollaboratorPermission
- DecodeJSON(t, resp, &repoPermission)
+ req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user5.Name).AddTokenAuth(token)
+ resp = MakeRequest(t, req, http.StatusOK)
- assert.Equal(t, "read", repoPermission.Permission)
+ repoCollPerm := api.RepoCollaboratorPermission{}
+ DecodeJSON(t, resp, &repoCollPerm)
+
+ assert.Equal(t, "read", repoCollPerm.Permission)
})
+ })
- t.Run("CollaboratorCanQueryItsPermissions", func(t *testing.T) {
- t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead))
+ t.Run("CollaboratorCanQueryItsPermissions", func(t *testing.T) {
+ t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead))
- _session := loginUser(t, user5.Name)
- _testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository)
+ _session := loginUser(t, user5.Name)
+ _testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository)
- req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user5.Name).
- AddTokenAuth(_testCtx.Token)
- resp := _session.MakeRequest(t, req, http.StatusOK)
+ req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user5.Name).
+ AddTokenAuth(_testCtx.Token)
+ resp := _session.MakeRequest(t, req, http.StatusOK)
- var repoPermission api.RepoCollaboratorPermission
- DecodeJSON(t, resp, &repoPermission)
+ var repoPermission api.RepoCollaboratorPermission
+ DecodeJSON(t, resp, &repoPermission)
- assert.Equal(t, "read", repoPermission.Permission)
- })
+ assert.Equal(t, "read", repoPermission.Permission)
+ })
- t.Run("RepoAdminCanQueryACollaboratorsPermissions", func(t *testing.T) {
- t.Run("AddUserAsCollaboratorWithAdminAccess", doAPIAddCollaborator(testCtx, user10.Name, perm.AccessModeAdmin))
- t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user11.Name, perm.AccessModeRead))
+ t.Run("RepoAdminCanQueryACollaboratorsPermissions", func(t *testing.T) {
+ t.Run("AddUserAsCollaboratorWithAdminAccess", doAPIAddCollaborator(testCtx, user10.Name, perm.AccessModeAdmin))
+ t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user11.Name, perm.AccessModeRead))
- _session := loginUser(t, user10.Name)
- _testCtx := NewAPITestContext(t, user10.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository)
+ _session := loginUser(t, user10.Name)
+ _testCtx := NewAPITestContext(t, user10.Name, repo2.Name, auth_model.AccessTokenScopeReadRepository)
- req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user11.Name).
- AddTokenAuth(_testCtx.Token)
- resp := _session.MakeRequest(t, req, http.StatusOK)
+ req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission", repo2Owner.Name, repo2.Name, user11.Name).
+ AddTokenAuth(_testCtx.Token)
+ resp := _session.MakeRequest(t, req, http.StatusOK)
- var repoPermission api.RepoCollaboratorPermission
- DecodeJSON(t, resp, &repoPermission)
+ var repoPermission api.RepoCollaboratorPermission
+ DecodeJSON(t, resp, &repoPermission)
- assert.Equal(t, "read", repoPermission.Permission)
- })
+ assert.Equal(t, "read", repoPermission.Permission)
})
}
diff --git a/tests/integration/api_repo_file_create_test.go b/tests/integration/api_repo_file_create_test.go
index 41ad7211ff..af3bc54680 100644
--- a/tests/integration/api_repo_file_create_test.go
+++ b/tests/integration/api_repo_file_create_test.go
@@ -4,12 +4,11 @@
package integration
import (
- stdCtx "context"
"encoding/base64"
"fmt"
"net/http"
"net/url"
- "path/filepath"
+ "path"
"testing"
"time"
@@ -20,6 +19,7 @@ import (
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context"
"github.com/stretchr/testify/assert"
@@ -50,28 +50,42 @@ func getCreateFileOptions() api.CreateFileOptions {
}
}
-func getExpectedFileResponseForCreate(repoFullName, commitID, treePath, latestCommitSHA string) *api.FileResponse {
+func normalizeFileContentResponseCommitTime(c *api.ContentsResponse) {
+ // decoded JSON response may contain different timezone from the one parsed by git commit
+ // so we need to normalize the time to UTC to make "assert.Equal" pass
+ c.LastCommitterDate = util.ToPointer(c.LastCommitterDate.UTC())
+ c.LastAuthorDate = util.ToPointer(c.LastAuthorDate.UTC())
+}
+
+type apiFileResponseInfo struct {
+ repoFullName, commitID, treePath, lastCommitSHA string
+ lastCommitterWhen, lastAuthorWhen time.Time
+}
+
+func getExpectedFileResponseForCreate(info apiFileResponseInfo) *api.FileResponse {
sha := "a635aa942442ddfdba07468cf9661c08fbdf0ebf"
encoding := "base64"
content := "VGhpcyBpcyBuZXcgdGV4dA=="
- selfURL := setting.AppURL + "api/v1/repos/" + repoFullName + "/contents/" + treePath + "?ref=master"
- htmlURL := setting.AppURL + repoFullName + "/src/branch/master/" + treePath
- gitURL := setting.AppURL + "api/v1/repos/" + repoFullName + "/git/blobs/" + sha
- downloadURL := setting.AppURL + repoFullName + "/raw/branch/master/" + treePath
- return &api.FileResponse{
+ selfURL := setting.AppURL + "api/v1/repos/" + info.repoFullName + "/contents/" + info.treePath + "?ref=master"
+ htmlURL := setting.AppURL + info.repoFullName + "/src/branch/master/" + info.treePath
+ gitURL := setting.AppURL + "api/v1/repos/" + info.repoFullName + "/git/blobs/" + sha
+ downloadURL := setting.AppURL + info.repoFullName + "/raw/branch/master/" + info.treePath
+ ret := &api.FileResponse{
Content: &api.ContentsResponse{
- Name: filepath.Base(treePath),
- Path: treePath,
- SHA: sha,
- LastCommitSHA: latestCommitSHA,
- Size: 16,
- Type: "file",
- Encoding: &encoding,
- Content: &content,
- URL: &selfURL,
- HTMLURL: &htmlURL,
- GitURL: &gitURL,
- DownloadURL: &downloadURL,
+ Name: path.Base(info.treePath),
+ Path: info.treePath,
+ SHA: sha,
+ LastCommitSHA: util.ToPointer(info.lastCommitSHA),
+ LastCommitterDate: util.ToPointer(info.lastCommitterWhen),
+ LastAuthorDate: util.ToPointer(info.lastAuthorWhen),
+ Size: 16,
+ Type: "file",
+ Encoding: &encoding,
+ Content: &content,
+ URL: &selfURL,
+ HTMLURL: &htmlURL,
+ GitURL: &gitURL,
+ DownloadURL: &downloadURL,
Links: &api.FileLinksResponse{
Self: &selfURL,
GitURL: &gitURL,
@@ -80,10 +94,10 @@ func getExpectedFileResponseForCreate(repoFullName, commitID, treePath, latestCo
},
Commit: &api.FileCommitResponse{
CommitMeta: api.CommitMeta{
- URL: setting.AppURL + "api/v1/repos/" + repoFullName + "/git/commits/" + commitID,
- SHA: commitID,
+ URL: setting.AppURL + "api/v1/repos/" + info.repoFullName + "/git/commits/" + info.commitID,
+ SHA: info.commitID,
},
- HTMLURL: setting.AppURL + repoFullName + "/commit/" + commitID,
+ HTMLURL: setting.AppURL + info.repoFullName + "/commit/" + info.commitID,
Author: &api.CommitUser{
Identity: api.Identity{
Name: "Anne Doe",
@@ -107,6 +121,8 @@ func getExpectedFileResponseForCreate(repoFullName, commitID, treePath, latestCo
Payload: "",
},
}
+ normalizeFileContentResponseCommitTime(ret.Content)
+ return ret
}
func BenchmarkAPICreateFileSmall(b *testing.B) {
@@ -115,7 +131,7 @@ func BenchmarkAPICreateFileSmall(b *testing.B) {
repo1 := unittest.AssertExistsAndLoadBean(b, &repo_model.Repository{ID: 1}) // public repo
b.ResetTimer()
- for n := 0; n < b.N; n++ {
+ for n := 0; b.Loop(); n++ {
treePath := fmt.Sprintf("update/file%d.txt", n)
_, _ = createFileInBranch(user2, repo1, treePath, repo1.DefaultBranch, treePath)
}
@@ -130,7 +146,7 @@ func BenchmarkAPICreateFileMedium(b *testing.B) {
repo1 := unittest.AssertExistsAndLoadBean(b, &repo_model.Repository{ID: 1}) // public repo
b.ResetTimer()
- for n := 0; n < b.N; n++ {
+ for n := 0; b.Loop(); n++ {
treePath := fmt.Sprintf("update/file%d.txt", n)
copy(data, treePath)
_, _ = createFileInBranch(user2, repo1, treePath, repo1.DefaultBranch, treePath)
@@ -167,22 +183,30 @@ func TestAPICreateFile(t *testing.T) {
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath), &createFileOptions).
AddTokenAuth(token2)
resp := MakeRequest(t, req, http.StatusCreated)
- gitRepo, _ := gitrepo.OpenRepository(stdCtx.Background(), repo1)
+ gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo1)
+ defer gitRepo.Close()
commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName)
- latestCommit, _ := gitRepo.GetCommitByPath(treePath)
- expectedFileResponse := getExpectedFileResponseForCreate("user2/repo1", commitID, treePath, latestCommit.ID.String())
+ lastCommit, _ := gitRepo.GetCommitByPath(treePath)
+ expectedFileResponse := getExpectedFileResponseForCreate(apiFileResponseInfo{
+ repoFullName: "user2/repo1",
+ commitID: commitID,
+ treePath: treePath,
+ lastCommitSHA: lastCommit.ID.String(),
+ lastCommitterWhen: lastCommit.Committer.When,
+ lastAuthorWhen: lastCommit.Author.When,
+ })
var fileResponse api.FileResponse
DecodeJSON(t, resp, &fileResponse)
- assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
- assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
- assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
- assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
- assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
- assert.EqualValues(t, expectedFileResponse.Commit.Author.Date, fileResponse.Commit.Author.Date)
- assert.EqualValues(t, expectedFileResponse.Commit.Committer.Email, fileResponse.Commit.Committer.Email)
- assert.EqualValues(t, expectedFileResponse.Commit.Committer.Name, fileResponse.Commit.Committer.Name)
- assert.EqualValues(t, expectedFileResponse.Commit.Committer.Date, fileResponse.Commit.Committer.Date)
- gitRepo.Close()
+ normalizeFileContentResponseCommitTime(fileResponse.Content)
+ assert.Equal(t, expectedFileResponse.Content, fileResponse.Content)
+ assert.Equal(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
+ assert.Equal(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
+ assert.Equal(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
+ assert.Equal(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
+ assert.Equal(t, expectedFileResponse.Commit.Author.Date, fileResponse.Commit.Author.Date)
+ assert.Equal(t, expectedFileResponse.Commit.Committer.Email, fileResponse.Commit.Committer.Email)
+ assert.Equal(t, expectedFileResponse.Commit.Committer.Name, fileResponse.Commit.Committer.Name)
+ assert.Equal(t, expectedFileResponse.Commit.Committer.Date, fileResponse.Commit.Committer.Date)
}
// Test creating a file in a new branch
@@ -199,10 +223,10 @@ func TestAPICreateFile(t *testing.T) {
expectedSHA := "a635aa942442ddfdba07468cf9661c08fbdf0ebf"
expectedHTMLURL := fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/new_branch/new/file%d.txt", fileID)
expectedDownloadURL := fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/new_branch/new/file%d.txt", fileID)
- assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA)
- assert.EqualValues(t, expectedHTMLURL, *fileResponse.Content.HTMLURL)
- assert.EqualValues(t, expectedDownloadURL, *fileResponse.Content.DownloadURL)
- assert.EqualValues(t, createFileOptions.Message+"\n", fileResponse.Commit.Message)
+ assert.Equal(t, expectedSHA, fileResponse.Content.SHA)
+ assert.Equal(t, expectedHTMLURL, *fileResponse.Content.HTMLURL)
+ assert.Equal(t, expectedDownloadURL, *fileResponse.Content.DownloadURL)
+ assert.Equal(t, createFileOptions.Message+"\n", fileResponse.Commit.Message)
// Test creating a file without a message
createFileOptions = getCreateFileOptions()
@@ -214,7 +238,7 @@ func TestAPICreateFile(t *testing.T) {
resp = MakeRequest(t, req, http.StatusCreated)
DecodeJSON(t, resp, &fileResponse)
expectedMessage := "Add " + treePath + "\n"
- assert.EqualValues(t, expectedMessage, fileResponse.Commit.Message)
+ assert.Equal(t, expectedMessage, fileResponse.Commit.Message)
// Test trying to create a file that already exists, should fail
createFileOptions = getCreateFileOptions()
@@ -285,20 +309,28 @@ func TestAPICreateFile(t *testing.T) {
AddTokenAuth(token2)
resp = MakeRequest(t, req, http.StatusCreated)
emptyRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "empty-repo"}) // public repo
- gitRepo, _ := gitrepo.OpenRepository(stdCtx.Background(), emptyRepo)
+ gitRepo, _ := gitrepo.OpenRepository(t.Context(), emptyRepo)
+ defer gitRepo.Close()
commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName)
latestCommit, _ := gitRepo.GetCommitByPath(treePath)
- expectedFileResponse := getExpectedFileResponseForCreate("user2/empty-repo", commitID, treePath, latestCommit.ID.String())
+ expectedFileResponse := getExpectedFileResponseForCreate(apiFileResponseInfo{
+ repoFullName: "user2/empty-repo",
+ commitID: commitID,
+ treePath: treePath,
+ lastCommitSHA: latestCommit.ID.String(),
+ lastCommitterWhen: latestCommit.Committer.When,
+ lastAuthorWhen: latestCommit.Author.When,
+ })
DecodeJSON(t, resp, &fileResponse)
- assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
- assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
- assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
- assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
- assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
- assert.EqualValues(t, expectedFileResponse.Commit.Author.Date, fileResponse.Commit.Author.Date)
- assert.EqualValues(t, expectedFileResponse.Commit.Committer.Email, fileResponse.Commit.Committer.Email)
- assert.EqualValues(t, expectedFileResponse.Commit.Committer.Name, fileResponse.Commit.Committer.Name)
- assert.EqualValues(t, expectedFileResponse.Commit.Committer.Date, fileResponse.Commit.Committer.Date)
- gitRepo.Close()
+ normalizeFileContentResponseCommitTime(fileResponse.Content)
+ assert.Equal(t, expectedFileResponse.Content, fileResponse.Content)
+ assert.Equal(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
+ assert.Equal(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
+ assert.Equal(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
+ assert.Equal(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
+ assert.Equal(t, expectedFileResponse.Commit.Author.Date, fileResponse.Commit.Author.Date)
+ assert.Equal(t, expectedFileResponse.Commit.Committer.Email, fileResponse.Commit.Committer.Email)
+ assert.Equal(t, expectedFileResponse.Commit.Committer.Name, fileResponse.Commit.Committer.Name)
+ assert.Equal(t, expectedFileResponse.Commit.Committer.Date, fileResponse.Commit.Committer.Date)
})
}
diff --git a/tests/integration/api_repo_file_delete_test.go b/tests/integration/api_repo_file_delete_test.go
index 7c93307e19..9dd47f93e6 100644
--- a/tests/integration/api_repo_file_delete_test.go
+++ b/tests/integration/api_repo_file_delete_test.go
@@ -20,20 +20,22 @@ import (
func getDeleteFileOptions() *api.DeleteFileOptions {
return &api.DeleteFileOptions{
- FileOptions: api.FileOptions{
- BranchName: "master",
- NewBranchName: "master",
- Message: "Removing the file new/file.txt",
- Author: api.Identity{
- Name: "John Doe",
- Email: "johndoe@example.com",
- },
- Committer: api.Identity{
- Name: "Jane Doe",
- Email: "janedoe@example.com",
+ FileOptionsWithSHA: api.FileOptionsWithSHA{
+ FileOptions: api.FileOptions{
+ BranchName: "master",
+ NewBranchName: "master",
+ Message: "Removing the file new/file.txt",
+ Author: api.Identity{
+ Name: "John Doe",
+ Email: "johndoe@example.com",
+ },
+ Committer: api.Identity{
+ Name: "Jane Doe",
+ Email: "janedoe@example.com",
+ },
},
+ SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
},
- SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
}
}
@@ -87,7 +89,7 @@ func TestAPIDeleteFile(t *testing.T) {
DecodeJSON(t, resp, &fileResponse)
assert.NotNil(t, fileResponse)
assert.Nil(t, fileResponse.Content)
- assert.EqualValues(t, deleteFileOptions.Message+"\n", fileResponse.Commit.Message)
+ assert.Equal(t, deleteFileOptions.Message+"\n", fileResponse.Commit.Message)
// Test deleting file without a message
fileID++
@@ -100,7 +102,7 @@ func TestAPIDeleteFile(t *testing.T) {
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &fileResponse)
expectedMessage := "Delete " + treePath + "\n"
- assert.EqualValues(t, expectedMessage, fileResponse.Commit.Message)
+ assert.Equal(t, expectedMessage, fileResponse.Commit.Message)
// Test deleting a file with the wrong SHA
fileID++
@@ -110,7 +112,7 @@ func TestAPIDeleteFile(t *testing.T) {
deleteFileOptions.SHA = "badsha"
req = NewRequestWithJSON(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath), &deleteFileOptions).
AddTokenAuth(token2)
- MakeRequest(t, req, http.StatusBadRequest)
+ MakeRequest(t, req, http.StatusUnprocessableEntity)
// Test creating a file in repo16 by user4 who does not have write access
fileID++
diff --git a/tests/integration/api_repo_file_get_test.go b/tests/integration/api_repo_file_get_test.go
index 2f897093ee..379851b689 100644
--- a/tests/integration/api_repo_file_get_test.go
+++ b/tests/integration/api_repo_file_get_test.go
@@ -44,8 +44,6 @@ func TestAPIGetRawFileOrLFS(t *testing.T) {
reqLFS := NewRequest(t, "GET", "/api/v1/repos/user2/repo1/media/"+lfs)
respLFS := MakeRequestNilResponseRecorder(t, reqLFS, http.StatusOK)
assert.Equal(t, testFileSizeSmall, respLFS.Length)
-
- doAPIDeleteRepository(httpContext)
})
})
}
diff --git a/tests/integration/api_repo_file_update_test.go b/tests/integration/api_repo_file_update_test.go
index ac28e0c0a2..9a56711da6 100644
--- a/tests/integration/api_repo_file_update_test.go
+++ b/tests/integration/api_repo_file_update_test.go
@@ -4,12 +4,11 @@
package integration
import (
- stdCtx "context"
"encoding/base64"
"fmt"
"net/http"
"net/url"
- "path/filepath"
+ "path"
"testing"
auth_model "code.gitea.io/gitea/models/auth"
@@ -19,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context"
"github.com/stretchr/testify/assert"
@@ -28,7 +28,7 @@ func getUpdateFileOptions() *api.UpdateFileOptions {
content := "This is updated text"
contentEncoded := base64.StdEncoding.EncodeToString([]byte(content))
return &api.UpdateFileOptions{
- DeleteFileOptions: api.DeleteFileOptions{
+ FileOptionsWithSHA: api.FileOptionsWithSHA{
FileOptions: api.FileOptions{
BranchName: "master",
NewBranchName: "master",
@@ -48,28 +48,30 @@ func getUpdateFileOptions() *api.UpdateFileOptions {
}
}
-func getExpectedFileResponseForUpdate(commitID, treePath, lastCommitSHA string) *api.FileResponse {
+func getExpectedFileResponseForUpdate(info apiFileResponseInfo) *api.FileResponse {
sha := "08bd14b2e2852529157324de9c226b3364e76136"
encoding := "base64"
content := "VGhpcyBpcyB1cGRhdGVkIHRleHQ="
- selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master"
- htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + treePath
+ selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + info.treePath + "?ref=master"
+ htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + info.treePath
gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha
- downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath
- return &api.FileResponse{
+ downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + info.treePath
+ ret := &api.FileResponse{
Content: &api.ContentsResponse{
- Name: filepath.Base(treePath),
- Path: treePath,
- SHA: sha,
- LastCommitSHA: lastCommitSHA,
- Type: "file",
- Size: 20,
- Encoding: &encoding,
- Content: &content,
- URL: &selfURL,
- HTMLURL: &htmlURL,
- GitURL: &gitURL,
- DownloadURL: &downloadURL,
+ Name: path.Base(info.treePath),
+ Path: info.treePath,
+ SHA: sha,
+ LastCommitSHA: util.ToPointer(info.lastCommitSHA),
+ LastCommitterDate: util.ToPointer(info.lastCommitterWhen),
+ LastAuthorDate: util.ToPointer(info.lastAuthorWhen),
+ Type: "file",
+ Size: 20,
+ Encoding: &encoding,
+ Content: &content,
+ URL: &selfURL,
+ HTMLURL: &htmlURL,
+ GitURL: &gitURL,
+ DownloadURL: &downloadURL,
Links: &api.FileLinksResponse{
Self: &selfURL,
GitURL: &gitURL,
@@ -78,10 +80,10 @@ func getExpectedFileResponseForUpdate(commitID, treePath, lastCommitSHA string)
},
Commit: &api.FileCommitResponse{
CommitMeta: api.CommitMeta{
- URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + commitID,
- SHA: commitID,
+ URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + info.commitID,
+ SHA: info.commitID,
},
- HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
+ HTMLURL: setting.AppURL + "user2/repo1/commit/" + info.commitID,
Author: &api.CommitUser{
Identity: api.Identity{
Name: "John Doe",
@@ -103,6 +105,8 @@ func getExpectedFileResponseForUpdate(commitID, treePath, lastCommitSHA string)
Payload: "",
},
}
+ normalizeFileContentResponseCommitTime(ret.Content)
+ return ret
}
func TestAPIUpdateFile(t *testing.T) {
@@ -135,18 +139,25 @@ func TestAPIUpdateFile(t *testing.T) {
req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath), &updateFileOptions).
AddTokenAuth(token2)
resp := MakeRequest(t, req, http.StatusOK)
- gitRepo, _ := gitrepo.OpenRepository(stdCtx.Background(), repo1)
+ gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo1)
+ defer gitRepo.Close()
commitID, _ := gitRepo.GetBranchCommitID(updateFileOptions.NewBranchName)
lasCommit, _ := gitRepo.GetCommitByPath(treePath)
- expectedFileResponse := getExpectedFileResponseForUpdate(commitID, treePath, lasCommit.ID.String())
+ expectedFileResponse := getExpectedFileResponseForUpdate(apiFileResponseInfo{
+ commitID: commitID,
+ treePath: treePath,
+ lastCommitSHA: lasCommit.ID.String(),
+ lastCommitterWhen: lasCommit.Committer.When,
+ lastAuthorWhen: lasCommit.Author.When,
+ })
var fileResponse api.FileResponse
DecodeJSON(t, resp, &fileResponse)
- assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
- assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
- assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
- assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
- assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
- gitRepo.Close()
+ normalizeFileContentResponseCommitTime(fileResponse.Content)
+ assert.Equal(t, expectedFileResponse.Content, fileResponse.Content)
+ assert.Equal(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
+ assert.Equal(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
+ assert.Equal(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
+ assert.Equal(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
}
// Test updating a file in a new branch
@@ -164,10 +175,10 @@ func TestAPIUpdateFile(t *testing.T) {
expectedSHA := "08bd14b2e2852529157324de9c226b3364e76136"
expectedHTMLURL := fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/new_branch/update/file%d.txt", fileID)
expectedDownloadURL := fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/new_branch/update/file%d.txt", fileID)
- assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA)
- assert.EqualValues(t, expectedHTMLURL, *fileResponse.Content.HTMLURL)
- assert.EqualValues(t, expectedDownloadURL, *fileResponse.Content.DownloadURL)
- assert.EqualValues(t, updateFileOptions.Message+"\n", fileResponse.Commit.Message)
+ assert.Equal(t, expectedSHA, fileResponse.Content.SHA)
+ assert.Equal(t, expectedHTMLURL, *fileResponse.Content.HTMLURL)
+ assert.Equal(t, expectedDownloadURL, *fileResponse.Content.DownloadURL)
+ assert.Equal(t, updateFileOptions.Message+"\n", fileResponse.Commit.Message)
// Test updating a file and renaming it
updateFileOptions = getUpdateFileOptions()
@@ -184,9 +195,9 @@ func TestAPIUpdateFile(t *testing.T) {
expectedSHA = "08bd14b2e2852529157324de9c226b3364e76136"
expectedHTMLURL = fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/master/rename/update/file%d.txt", fileID)
expectedDownloadURL = fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/master/rename/update/file%d.txt", fileID)
- assert.EqualValues(t, expectedSHA, fileResponse.Content.SHA)
- assert.EqualValues(t, expectedHTMLURL, *fileResponse.Content.HTMLURL)
- assert.EqualValues(t, expectedDownloadURL, *fileResponse.Content.DownloadURL)
+ assert.Equal(t, expectedSHA, fileResponse.Content.SHA)
+ assert.Equal(t, expectedHTMLURL, *fileResponse.Content.HTMLURL)
+ assert.Equal(t, expectedDownloadURL, *fileResponse.Content.DownloadURL)
// Test updating a file without a message
updateFileOptions = getUpdateFileOptions()
@@ -200,7 +211,7 @@ func TestAPIUpdateFile(t *testing.T) {
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &fileResponse)
expectedMessage := "Update " + treePath + "\n"
- assert.EqualValues(t, expectedMessage, fileResponse.Commit.Message)
+ assert.Equal(t, expectedMessage, fileResponse.Commit.Message)
// Test updating a file with the wrong SHA
fileID++
diff --git a/tests/integration/api_repo_files_change_test.go b/tests/integration/api_repo_files_change_test.go
index fb3ae5e4dd..999bcdc680 100644
--- a/tests/integration/api_repo_files_change_test.go
+++ b/tests/integration/api_repo_files_change_test.go
@@ -4,7 +4,6 @@
package integration
import (
- stdCtx "context"
"encoding/base64"
"fmt"
"net/http"
@@ -78,51 +77,56 @@ func TestAPIChangeFiles(t *testing.T) {
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
// Test changing files in repo1 which user2 owns, try both with branch and empty branch
- for _, branch := range [...]string{
- "master", // Branch
- "", // Empty branch
- } {
- fileID++
- createTreePath := fmt.Sprintf("new/file%d.txt", fileID)
- updateTreePath := fmt.Sprintf("update/file%d.txt", fileID)
- deleteTreePath := fmt.Sprintf("delete/file%d.txt", fileID)
- createFile(user2, repo1, updateTreePath)
- createFile(user2, repo1, deleteTreePath)
- changeFilesOptions := getChangeFilesOptions()
- changeFilesOptions.BranchName = branch
- changeFilesOptions.Files[0].Path = createTreePath
- changeFilesOptions.Files[1].Path = updateTreePath
- changeFilesOptions.Files[2].Path = deleteTreePath
- req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/contents", user2.Name, repo1.Name), &changeFilesOptions).
- AddTokenAuth(token2)
- resp := MakeRequest(t, req, http.StatusCreated)
- gitRepo, _ := gitrepo.OpenRepository(stdCtx.Background(), repo1)
- commitID, _ := gitRepo.GetBranchCommitID(changeFilesOptions.NewBranchName)
- createLasCommit, _ := gitRepo.GetCommitByPath(createTreePath)
- updateLastCommit, _ := gitRepo.GetCommitByPath(updateTreePath)
- expectedCreateFileResponse := getExpectedFileResponseForCreate(fmt.Sprintf("%v/%v", user2.Name, repo1.Name), commitID, createTreePath, createLasCommit.ID.String())
- expectedUpdateFileResponse := getExpectedFileResponseForUpdate(commitID, updateTreePath, updateLastCommit.ID.String())
- var filesResponse api.FilesResponse
- DecodeJSON(t, resp, &filesResponse)
-
- // check create file
- assert.EqualValues(t, expectedCreateFileResponse.Content, filesResponse.Files[0])
-
- // check update file
- assert.EqualValues(t, expectedUpdateFileResponse.Content, filesResponse.Files[1])
-
- // test commit info
- assert.EqualValues(t, expectedCreateFileResponse.Commit.SHA, filesResponse.Commit.SHA)
- assert.EqualValues(t, expectedCreateFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
- assert.EqualValues(t, expectedCreateFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email)
- assert.EqualValues(t, expectedCreateFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name)
- assert.EqualValues(t, expectedCreateFileResponse.Commit.Committer.Email, filesResponse.Commit.Committer.Email)
- assert.EqualValues(t, expectedCreateFileResponse.Commit.Committer.Name, filesResponse.Commit.Committer.Name)
-
- // test delete file
- assert.Nil(t, filesResponse.Files[2])
-
- gitRepo.Close()
+ for _, branch := range []string{"master", ""} {
+ t.Run("Branch-"+branch, func(t *testing.T) {
+ fileID++
+ createTreePath := fmt.Sprintf("new/file%d.txt", fileID)
+ updateTreePath := fmt.Sprintf("update/file%d.txt", fileID)
+ deleteTreePath := fmt.Sprintf("delete/file%d.txt", fileID)
+ _, _ = createFile(user2, repo1, updateTreePath)
+ _, _ = createFile(user2, repo1, deleteTreePath)
+ changeFilesOptions := getChangeFilesOptions()
+ changeFilesOptions.BranchName = branch
+ changeFilesOptions.Files[0].Path = createTreePath
+ changeFilesOptions.Files[1].Path = updateTreePath
+ changeFilesOptions.Files[2].Path = deleteTreePath
+ req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/contents", user2.Name, repo1.Name), &changeFilesOptions).
+ AddTokenAuth(token2)
+ resp := MakeRequest(t, req, http.StatusCreated)
+ gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo1)
+ defer gitRepo.Close()
+ commitID, _ := gitRepo.GetBranchCommitID(changeFilesOptions.NewBranchName)
+ createLasCommit, _ := gitRepo.GetCommitByPath(createTreePath)
+ updateLastCommit, _ := gitRepo.GetCommitByPath(updateTreePath)
+ expectedCreateFileResponse := getExpectedFileResponseForCreate(apiFileResponseInfo{
+ repoFullName: fmt.Sprintf("%s/%s", user2.Name, repo1.Name),
+ commitID: commitID,
+ treePath: createTreePath,
+ lastCommitSHA: createLasCommit.ID.String(),
+ lastCommitterWhen: createLasCommit.Committer.When,
+ lastAuthorWhen: createLasCommit.Author.When,
+ })
+ expectedUpdateFileResponse := getExpectedFileResponseForUpdate(apiFileResponseInfo{
+ commitID: commitID,
+ treePath: updateTreePath,
+ lastCommitSHA: updateLastCommit.ID.String(),
+ lastCommitterWhen: updateLastCommit.Committer.When,
+ lastAuthorWhen: updateLastCommit.Author.When,
+ })
+ var filesResponse api.FilesResponse
+ DecodeJSON(t, resp, &filesResponse)
+ normalizeFileContentResponseCommitTime(filesResponse.Files[0])
+ normalizeFileContentResponseCommitTime(filesResponse.Files[1])
+ assert.Equal(t, expectedCreateFileResponse.Content, filesResponse.Files[0]) // check create file
+ assert.Equal(t, expectedUpdateFileResponse.Content, filesResponse.Files[1]) // check update file
+ assert.Equal(t, expectedCreateFileResponse.Commit.SHA, filesResponse.Commit.SHA)
+ assert.Equal(t, expectedCreateFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
+ assert.Equal(t, expectedCreateFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email)
+ assert.Equal(t, expectedCreateFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name)
+ assert.Equal(t, expectedCreateFileResponse.Commit.Committer.Email, filesResponse.Commit.Committer.Email)
+ assert.Equal(t, expectedCreateFileResponse.Commit.Committer.Name, filesResponse.Commit.Committer.Name)
+ assert.Nil(t, filesResponse.Files[2]) // test delete file
+ })
}
// Test changing files in a new branch
@@ -150,15 +154,15 @@ func TestAPIChangeFiles(t *testing.T) {
expectedUpdateSHA := "08bd14b2e2852529157324de9c226b3364e76136"
expectedUpdateHTMLURL := fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/new_branch/update/file%d.txt", fileID)
expectedUpdateDownloadURL := fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/new_branch/update/file%d.txt", fileID)
- assert.EqualValues(t, expectedCreateSHA, filesResponse.Files[0].SHA)
- assert.EqualValues(t, expectedCreateHTMLURL, *filesResponse.Files[0].HTMLURL)
- assert.EqualValues(t, expectedCreateDownloadURL, *filesResponse.Files[0].DownloadURL)
- assert.EqualValues(t, expectedUpdateSHA, filesResponse.Files[1].SHA)
- assert.EqualValues(t, expectedUpdateHTMLURL, *filesResponse.Files[1].HTMLURL)
- assert.EqualValues(t, expectedUpdateDownloadURL, *filesResponse.Files[1].DownloadURL)
+ assert.Equal(t, expectedCreateSHA, filesResponse.Files[0].SHA)
+ assert.Equal(t, expectedCreateHTMLURL, *filesResponse.Files[0].HTMLURL)
+ assert.Equal(t, expectedCreateDownloadURL, *filesResponse.Files[0].DownloadURL)
+ assert.Equal(t, expectedUpdateSHA, filesResponse.Files[1].SHA)
+ assert.Equal(t, expectedUpdateHTMLURL, *filesResponse.Files[1].HTMLURL)
+ assert.Equal(t, expectedUpdateDownloadURL, *filesResponse.Files[1].DownloadURL)
assert.Nil(t, filesResponse.Files[2])
- assert.EqualValues(t, changeFilesOptions.Message+"\n", filesResponse.Commit.Message)
+ assert.Equal(t, changeFilesOptions.Message+"\n", filesResponse.Commit.Message)
// Test updating a file and renaming it
changeFilesOptions = getChangeFilesOptions()
@@ -176,9 +180,9 @@ func TestAPIChangeFiles(t *testing.T) {
expectedUpdateSHA = "08bd14b2e2852529157324de9c226b3364e76136"
expectedUpdateHTMLURL = fmt.Sprintf(setting.AppURL+"user2/repo1/src/branch/master/rename/update/file%d.txt", fileID)
expectedUpdateDownloadURL = fmt.Sprintf(setting.AppURL+"user2/repo1/raw/branch/master/rename/update/file%d.txt", fileID)
- assert.EqualValues(t, expectedUpdateSHA, filesResponse.Files[0].SHA)
- assert.EqualValues(t, expectedUpdateHTMLURL, *filesResponse.Files[0].HTMLURL)
- assert.EqualValues(t, expectedUpdateDownloadURL, *filesResponse.Files[0].DownloadURL)
+ assert.Equal(t, expectedUpdateSHA, filesResponse.Files[0].SHA)
+ assert.Equal(t, expectedUpdateHTMLURL, *filesResponse.Files[0].HTMLURL)
+ assert.Equal(t, expectedUpdateDownloadURL, *filesResponse.Files[0].DownloadURL)
// Test updating a file without a message
changeFilesOptions = getChangeFilesOptions()
@@ -198,7 +202,7 @@ func TestAPIChangeFiles(t *testing.T) {
resp = MakeRequest(t, req, http.StatusCreated)
DecodeJSON(t, resp, &filesResponse)
expectedMessage := fmt.Sprintf("Add %v\nUpdate %v\nDelete %v\n", createTreePath, updateTreePath, deleteTreePath)
- assert.EqualValues(t, expectedMessage, filesResponse.Commit.Message)
+ assert.Equal(t, expectedMessage, filesResponse.Commit.Message)
// Test updating a file with the wrong SHA
fileID++
diff --git a/tests/integration/api_repo_files_get_test.go b/tests/integration/api_repo_files_get_test.go
new file mode 100644
index 0000000000..a4ded7da3f
--- /dev/null
+++ b/tests/integration/api_repo_files_get_test.go
@@ -0,0 +1,157 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "fmt"
+ "net/http"
+ "net/url"
+ "testing"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/setting"
+ api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/test"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/tests"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestAPIGetRequestedFiles(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the repo1 & repo16
+ org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) // owner of the repo3, is an org
+ user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) // owner of neither repos
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) // public repo
+ repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // public repo
+ repo16 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) // private repo
+
+ // Get user2's token
+ session := loginUser(t, user2.Name)
+ token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+ // Get user4's token
+ session = loginUser(t, user4.Name)
+ token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo1)
+ assert.NoError(t, err)
+ defer gitRepo.Close()
+ lastCommit, _ := gitRepo.GetCommitByPath("README.md")
+
+ requestFiles := func(t *testing.T, url string, files []string, expectedStatusCode ...int) (ret []*api.ContentsResponse) {
+ req := NewRequestWithJSON(t, "POST", url, &api.GetFilesOptions{Files: files})
+ resp := MakeRequest(t, req, util.OptionalArg(expectedStatusCode, http.StatusOK))
+ if resp.Code != http.StatusOK {
+ return nil
+ }
+ DecodeJSON(t, resp, &ret)
+ return ret
+ }
+
+ t.Run("User2Get", func(t *testing.T) {
+ reqBodyOpt := &api.GetFilesOptions{Files: []string{"README.md"}}
+ reqBodyParam, _ := json.Marshal(reqBodyOpt)
+ req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1/file-contents?body="+url.QueryEscape(string(reqBodyParam)))
+ resp := MakeRequest(t, req, http.StatusOK)
+ var ret []*api.ContentsResponse
+ DecodeJSON(t, resp, &ret)
+ expected := []*api.ContentsResponse{getExpectedContentsResponseForContents(repo1.DefaultBranch, "branch", lastCommit.ID.String())}
+ assert.Equal(t, expected, ret)
+ })
+ t.Run("User2NoRef", func(t *testing.T) {
+ ret := requestFiles(t, "/api/v1/repos/user2/repo1/file-contents", []string{"README.md"})
+ expected := []*api.ContentsResponse{getExpectedContentsResponseForContents(repo1.DefaultBranch, "branch", lastCommit.ID.String())}
+ assert.Equal(t, expected, ret)
+ })
+ t.Run("User2RefBranch", func(t *testing.T) {
+ ret := requestFiles(t, "/api/v1/repos/user2/repo1/file-contents?ref=master", []string{"README.md"})
+ expected := []*api.ContentsResponse{getExpectedContentsResponseForContents(repo1.DefaultBranch, "branch", lastCommit.ID.String())}
+ assert.Equal(t, expected, ret)
+ })
+ t.Run("User2RefTag", func(t *testing.T) {
+ ret := requestFiles(t, "/api/v1/repos/user2/repo1/file-contents?ref=v1.1", []string{"README.md"})
+ expected := []*api.ContentsResponse{getExpectedContentsResponseForContents("v1.1", "tag", lastCommit.ID.String())}
+ assert.Equal(t, expected, ret)
+ })
+ t.Run("User2RefCommit", func(t *testing.T) {
+ ret := requestFiles(t, "/api/v1/repos/user2/repo1/file-contents?ref=65f1bf27bc3bf70f64657658635e66094edbcb4d", []string{"README.md"})
+ expected := []*api.ContentsResponse{getExpectedContentsResponseForContents("65f1bf27bc3bf70f64657658635e66094edbcb4d", "commit", lastCommit.ID.String())}
+ assert.Equal(t, expected, ret)
+ })
+ t.Run("User2RefNotExist", func(t *testing.T) {
+ ret := requestFiles(t, "/api/v1/repos/user2/repo1/file-contents?ref=not-exist", []string{"README.md"}, http.StatusNotFound)
+ assert.Empty(t, ret)
+ })
+
+ t.Run("PermissionCheck", func(t *testing.T) {
+ filesOptions := &api.GetFilesOptions{Files: []string{"README.md"}}
+ // Test accessing private ref with user token that does not have access - should fail
+ req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/file-contents", user2.Name, repo16.Name), &filesOptions).AddTokenAuth(token4)
+ MakeRequest(t, req, http.StatusNotFound)
+ // Test access private ref of owner of token
+ req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/file-contents", user2.Name, repo16.Name), &filesOptions).AddTokenAuth(token2)
+ MakeRequest(t, req, http.StatusOK)
+ // Test access of org org3 private repo file by owner user2
+ req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/file-contents", org3.Name, repo3.Name), &filesOptions).AddTokenAuth(token2)
+ MakeRequest(t, req, http.StatusOK)
+ })
+
+ t.Run("ResponseList", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.API.DefaultPagingNum)()
+ defer test.MockVariableValue(&setting.API.DefaultMaxBlobSize)()
+ defer test.MockVariableValue(&setting.API.DefaultMaxResponseSize)()
+
+ type expected struct {
+ Name string
+ HasContent bool
+ }
+ assertResponse := func(t *testing.T, expected []*expected, ret []*api.ContentsResponse) {
+ require.Len(t, ret, len(expected))
+ for i, e := range expected {
+ if e == nil {
+ assert.Nil(t, ret[i], "item %d", i)
+ continue
+ }
+ assert.Equal(t, e.Name, ret[i].Name, "item %d name", i)
+ if e.HasContent {
+ require.NotNil(t, ret[i].Content, "item %d content", i)
+ assert.NotEmpty(t, *ret[i].Content, "item %d content", i)
+ } else {
+ assert.Nil(t, ret[i].Content, "item %d content", i)
+ }
+ }
+ }
+
+ // repo1 "DefaultBranch" has 2 files: LICENSE (1064 bytes), README.md (30 bytes)
+ ret := requestFiles(t, "/api/v1/repos/user2/repo1/file-contents?ref=DefaultBranch", []string{"no-such.txt", "LICENSE", "README.md"})
+ assertResponse(t, []*expected{nil, {"LICENSE", true}, {"README.md", true}}, ret)
+
+ // the returned file list is limited by the DefaultPagingNum
+ setting.API.DefaultPagingNum = 2
+ ret = requestFiles(t, "/api/v1/repos/user2/repo1/file-contents?ref=DefaultBranch", []string{"no-such.txt", "LICENSE", "README.md"})
+ assertResponse(t, []*expected{nil, {"LICENSE", true}}, ret)
+ setting.API.DefaultPagingNum = 100
+
+ // if a file exceeds the DefaultMaxBlobSize, the content is not returned
+ setting.API.DefaultMaxBlobSize = 200
+ ret = requestFiles(t, "/api/v1/repos/user2/repo1/file-contents?ref=DefaultBranch", []string{"no-such.txt", "LICENSE", "README.md"})
+ assertResponse(t, []*expected{nil, {"LICENSE", false}, {"README.md", true}}, ret)
+ setting.API.DefaultMaxBlobSize = 20000
+
+ // if the total response size would exceed the DefaultMaxResponseSize, then the list stops
+ setting.API.DefaultMaxResponseSize = ret[1].Size*4/3 + 10
+ ret = requestFiles(t, "/api/v1/repos/user2/repo1/file-contents?ref=DefaultBranch", []string{"no-such.txt", "LICENSE", "README.md"})
+ assertResponse(t, []*expected{nil, {"LICENSE", true}}, ret)
+ setting.API.DefaultMaxBlobSize = 20000
+ })
+}
diff --git a/tests/integration/api_repo_get_contents_list_test.go b/tests/integration/api_repo_get_contents_list_test.go
index 1ba74490a3..563d6fcc10 100644
--- a/tests/integration/api_repo_get_contents_list_test.go
+++ b/tests/integration/api_repo_get_contents_list_test.go
@@ -6,8 +6,9 @@ package integration
import (
"net/http"
"net/url"
- "path/filepath"
+ "path"
"testing"
+ "time"
auth_model "code.gitea.io/gitea/models/auth"
repo_model "code.gitea.io/gitea/models/repo"
@@ -17,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
repo_service "code.gitea.io/gitea/services/repository"
"github.com/stretchr/testify/assert"
@@ -31,16 +33,18 @@ func getExpectedContentsListResponseForContents(ref, refType, lastCommitSHA stri
downloadURL := setting.AppURL + "user2/repo1/raw/" + refType + "/" + ref + "/" + treePath
return []*api.ContentsResponse{
{
- Name: filepath.Base(treePath),
- Path: treePath,
- SHA: sha,
- LastCommitSHA: lastCommitSHA,
- Type: "file",
- Size: 30,
- URL: &selfURL,
- HTMLURL: &htmlURL,
- GitURL: &gitURL,
- DownloadURL: &downloadURL,
+ Name: path.Base(treePath),
+ Path: treePath,
+ SHA: sha,
+ LastCommitSHA: util.ToPointer(lastCommitSHA),
+ LastCommitterDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))),
+ LastAuthorDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))),
+ Type: "file",
+ Size: 30,
+ URL: &selfURL,
+ HTMLURL: &htmlURL,
+ GitURL: &gitURL,
+ DownloadURL: &downloadURL,
Links: &api.FileLinksResponse{
Self: &selfURL,
GitURL: &gitURL,
@@ -62,7 +66,6 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) // public repo
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // public repo
repo16 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) // private repo
- treePath := "" // root dir
// Get user2's token
session := loginUser(t, user2.Name)
@@ -91,7 +94,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
// ref is default ref
ref := repo1.DefaultBranch
refType := "branch"
- req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
+ req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents?ref=%s", user2.Name, repo1.Name, ref)
resp := MakeRequest(t, req, http.StatusOK)
var contentsListResponse []*api.ContentsResponse
DecodeJSON(t, resp, &contentsListResponse)
@@ -99,22 +102,22 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
lastCommit, err := gitRepo.GetCommitByPath("README.md")
assert.NoError(t, err)
expectedContentsListResponse := getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String())
- assert.EqualValues(t, expectedContentsListResponse, contentsListResponse)
+ assert.Equal(t, expectedContentsListResponse, contentsListResponse)
// No ref
refType = "branch"
- req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath)
+ req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/", user2.Name, repo1.Name)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &contentsListResponse)
assert.NotNil(t, contentsListResponse)
expectedContentsListResponse = getExpectedContentsListResponseForContents(repo1.DefaultBranch, refType, lastCommit.ID.String())
- assert.EqualValues(t, expectedContentsListResponse, contentsListResponse)
+ assert.Equal(t, expectedContentsListResponse, contentsListResponse)
// ref is the branch we created above in setup
ref = newBranch
refType = "branch"
- req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
+ req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents?ref=%s", user2.Name, repo1.Name, ref)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &contentsListResponse)
assert.NotNil(t, contentsListResponse)
@@ -123,12 +126,12 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
lastCommit, err = branchCommit.GetCommitByPath("README.md")
assert.NoError(t, err)
expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String())
- assert.EqualValues(t, expectedContentsListResponse, contentsListResponse)
+ assert.Equal(t, expectedContentsListResponse, contentsListResponse)
// ref is the new tag we created above in setup
ref = newTag
refType = "tag"
- req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
+ req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/?ref=%s", user2.Name, repo1.Name, ref)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &contentsListResponse)
assert.NotNil(t, contentsListResponse)
@@ -137,35 +140,35 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
lastCommit, err = tagCommit.GetCommitByPath("README.md")
assert.NoError(t, err)
expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String())
- assert.EqualValues(t, expectedContentsListResponse, contentsListResponse)
+ assert.Equal(t, expectedContentsListResponse, contentsListResponse)
// ref is a commit
ref = commitID
refType = "commit"
- req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
+ req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/?ref=%s", user2.Name, repo1.Name, ref)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &contentsListResponse)
assert.NotNil(t, contentsListResponse)
expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType, commitID)
- assert.EqualValues(t, expectedContentsListResponse, contentsListResponse)
+ assert.Equal(t, expectedContentsListResponse, contentsListResponse)
// Test file contents a file with a bad ref
ref = "badref"
- req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
+ req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/?ref=%s", user2.Name, repo1.Name, ref)
MakeRequest(t, req, http.StatusNotFound)
// Test accessing private ref with user token that does not have access - should fail
- req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s", user2.Name, repo16.Name, treePath).
+ req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/", user2.Name, repo16.Name).
AddTokenAuth(token4)
MakeRequest(t, req, http.StatusNotFound)
// Test access private ref of owner of token
- req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/readme.md", user2.Name, repo16.Name).
+ req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/", user2.Name, repo16.Name).
AddTokenAuth(token2)
MakeRequest(t, req, http.StatusOK)
// Test access of org org3 private repo file by owner user2
- req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s", org3.Name, repo3.Name, treePath).
+ req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/", org3.Name, repo3.Name).
AddTokenAuth(token2)
MakeRequest(t, req, http.StatusOK)
}
diff --git a/tests/integration/api_repo_get_contents_test.go b/tests/integration/api_repo_get_contents_test.go
index 68a8608117..33df74f6ee 100644
--- a/tests/integration/api_repo_get_contents_test.go
+++ b/tests/integration/api_repo_get_contents_test.go
@@ -7,7 +7,9 @@ import (
"io"
"net/http"
"net/url"
+ "slices"
"testing"
+ "time"
auth_model "code.gitea.io/gitea/models/auth"
repo_model "code.gitea.io/gitea/models/repo"
@@ -17,33 +19,33 @@ import (
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
repo_service "code.gitea.io/gitea/services/repository"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func getExpectedContentsResponseForContents(ref, refType, lastCommitSHA string) *api.ContentsResponse {
treePath := "README.md"
- sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f"
- encoding := "base64"
- content := "IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x"
selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=" + ref
htmlURL := setting.AppURL + "user2/repo1/src/" + refType + "/" + ref + "/" + treePath
- gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha
- downloadURL := setting.AppURL + "user2/repo1/raw/" + refType + "/" + ref + "/" + treePath
+ gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/4b4851ad51df6a7d9f25c979345979eaeb5b349f"
return &api.ContentsResponse{
- Name: treePath,
- Path: treePath,
- SHA: sha,
- LastCommitSHA: lastCommitSHA,
- Type: "file",
- Size: 30,
- Encoding: &encoding,
- Content: &content,
- URL: &selfURL,
- HTMLURL: &htmlURL,
- GitURL: &gitURL,
- DownloadURL: &downloadURL,
+ Name: treePath,
+ Path: treePath,
+ SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
+ LastCommitSHA: util.ToPointer(lastCommitSHA),
+ LastCommitterDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))),
+ LastAuthorDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))),
+ Type: "file",
+ Size: 30,
+ Encoding: util.ToPointer("base64"),
+ Content: util.ToPointer("IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x"),
+ URL: &selfURL,
+ HTMLURL: &htmlURL,
+ GitURL: &gitURL,
+ DownloadURL: util.ToPointer(setting.AppURL + "user2/repo1/raw/" + refType + "/" + ref + "/" + treePath),
Links: &api.FileLinksResponse{
Self: &selfURL,
GitURL: &gitURL,
@@ -53,7 +55,11 @@ func getExpectedContentsResponseForContents(ref, refType, lastCommitSHA string)
}
func TestAPIGetContents(t *testing.T) {
- onGiteaRun(t, testAPIGetContents)
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ testAPIGetContentsRefFormats(t)
+ testAPIGetContents(t, u)
+ testAPIGetContentsExt(t)
+ })
}
func testAPIGetContents(t *testing.T, u *url.URL) {
@@ -75,54 +81,56 @@ func testAPIGetContents(t *testing.T, u *url.URL) {
// Get the commit ID of the default branch
gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo1)
- assert.NoError(t, err)
+ require.NoError(t, err)
defer gitRepo.Close()
// Make a new branch in repo1
newBranch := "test_branch"
err = repo_service.CreateNewBranch(git.DefaultContext, user2, repo1, gitRepo, repo1.DefaultBranch, newBranch)
- assert.NoError(t, err)
+ require.NoError(t, err)
commitID, err := gitRepo.GetBranchCommitID(repo1.DefaultBranch)
- assert.NoError(t, err)
+ require.NoError(t, err)
// Make a new tag in repo1
newTag := "test_tag"
err = gitRepo.CreateTag(newTag, commitID)
- assert.NoError(t, err)
+ require.NoError(t, err)
/*** END SETUP ***/
+ // not found
+ req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/no-such/file.md", user2.Name, repo1.Name)
+ resp := MakeRequest(t, req, http.StatusNotFound)
+ assert.Contains(t, resp.Body.String(), "object does not exist [id: , rel_path: no-such]")
+
// ref is default ref
ref := repo1.DefaultBranch
refType := "branch"
- req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
- resp := MakeRequest(t, req, http.StatusOK)
+ req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
+ resp = MakeRequest(t, req, http.StatusOK)
var contentsResponse api.ContentsResponse
DecodeJSON(t, resp, &contentsResponse)
- assert.NotNil(t, contentsResponse)
lastCommit, _ := gitRepo.GetCommitByPath("README.md")
expectedContentsResponse := getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String())
- assert.EqualValues(t, *expectedContentsResponse, contentsResponse)
+ assert.Equal(t, *expectedContentsResponse, contentsResponse)
// No ref
refType = "branch"
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &contentsResponse)
- assert.NotNil(t, contentsResponse)
expectedContentsResponse = getExpectedContentsResponseForContents(repo1.DefaultBranch, refType, lastCommit.ID.String())
- assert.EqualValues(t, *expectedContentsResponse, contentsResponse)
+ assert.Equal(t, *expectedContentsResponse, contentsResponse)
- // ref is the branch we created above in setup
+ // ref is the branch we created above in setup
ref = newBranch
refType = "branch"
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &contentsResponse)
- assert.NotNil(t, contentsResponse)
branchCommit, _ := gitRepo.GetBranchCommit(ref)
lastCommit, _ = branchCommit.GetCommitByPath("README.md")
expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String())
- assert.EqualValues(t, *expectedContentsResponse, contentsResponse)
+ assert.Equal(t, *expectedContentsResponse, contentsResponse)
// ref is the new tag we created above in setup
ref = newTag
@@ -130,11 +138,10 @@ func testAPIGetContents(t *testing.T, u *url.URL) {
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &contentsResponse)
- assert.NotNil(t, contentsResponse)
tagCommit, _ := gitRepo.GetTagCommit(ref)
lastCommit, _ = tagCommit.GetCommitByPath("README.md")
expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String())
- assert.EqualValues(t, *expectedContentsResponse, contentsResponse)
+ assert.Equal(t, *expectedContentsResponse, contentsResponse)
// ref is a commit
ref = commitID
@@ -142,9 +149,8 @@ func testAPIGetContents(t *testing.T, u *url.URL) {
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &contentsResponse)
- assert.NotNil(t, contentsResponse)
expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType, commitID)
- assert.EqualValues(t, *expectedContentsResponse, contentsResponse)
+ assert.Equal(t, *expectedContentsResponse, contentsResponse)
// Test file contents a file with a bad ref
ref = "badref"
@@ -167,29 +173,131 @@ func testAPIGetContents(t *testing.T, u *url.URL) {
MakeRequest(t, req, http.StatusOK)
}
-func TestAPIGetContentsRefFormats(t *testing.T) {
- onGiteaRun(t, func(t *testing.T, u *url.URL) {
- file := "README.md"
- sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
- content := "# repo1\n\nDescription for repo1"
-
- noRef := setting.AppURL + "api/v1/repos/user2/repo1/raw/" + file
- refInPath := setting.AppURL + "api/v1/repos/user2/repo1/raw/" + sha + "/" + file
- refInQuery := setting.AppURL + "api/v1/repos/user2/repo1/raw/" + file + "?ref=" + sha
-
- resp := MakeRequest(t, NewRequest(t, http.MethodGet, noRef), http.StatusOK)
- raw, err := io.ReadAll(resp.Body)
- assert.NoError(t, err)
- assert.EqualValues(t, content, string(raw))
-
- resp = MakeRequest(t, NewRequest(t, http.MethodGet, refInPath), http.StatusOK)
- raw, err = io.ReadAll(resp.Body)
- assert.NoError(t, err)
- assert.EqualValues(t, content, string(raw))
-
- resp = MakeRequest(t, NewRequest(t, http.MethodGet, refInQuery), http.StatusOK)
- raw, err = io.ReadAll(resp.Body)
- assert.NoError(t, err)
- assert.EqualValues(t, content, string(raw))
+func testAPIGetContentsRefFormats(t *testing.T) {
+ file := "README.md"
+ sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
+ content := "# repo1\n\nDescription for repo1"
+
+ resp := MakeRequest(t, NewRequest(t, http.MethodGet, "/api/v1/repos/user2/repo1/raw/"+file), http.StatusOK)
+ raw, err := io.ReadAll(resp.Body)
+ assert.NoError(t, err)
+ assert.Equal(t, content, string(raw))
+
+ resp = MakeRequest(t, NewRequest(t, http.MethodGet, "/api/v1/repos/user2/repo1/raw/"+sha+"/"+file), http.StatusOK)
+ raw, err = io.ReadAll(resp.Body)
+ assert.NoError(t, err)
+ assert.Equal(t, content, string(raw))
+
+ resp = MakeRequest(t, NewRequest(t, http.MethodGet, "/api/v1/repos/user2/repo1/raw/"+file+"?ref="+sha), http.StatusOK)
+ raw, err = io.ReadAll(resp.Body)
+ assert.NoError(t, err)
+ assert.Equal(t, content, string(raw))
+
+ resp = MakeRequest(t, NewRequest(t, http.MethodGet, "/api/v1/repos/user2/repo1/raw/"+file+"?ref=master"), http.StatusOK)
+ raw, err = io.ReadAll(resp.Body)
+ assert.NoError(t, err)
+ assert.Equal(t, content, string(raw))
+
+ _ = MakeRequest(t, NewRequest(t, http.MethodGet, "/api/v1/repos/user2/repo1/raw/docs/README.md?ref=main"), http.StatusNotFound)
+ _ = MakeRequest(t, NewRequest(t, http.MethodGet, "/api/v1/repos/user2/repo1/raw/README.md?ref=main"), http.StatusOK)
+ _ = MakeRequest(t, NewRequest(t, http.MethodGet, "/api/v1/repos/user2/repo1/raw/docs/README.md?ref=sub-home-md-img-check"), http.StatusOK)
+ _ = MakeRequest(t, NewRequest(t, http.MethodGet, "/api/v1/repos/user2/repo1/raw/README.md?ref=sub-home-md-img-check"), http.StatusNotFound)
+
+ // FIXME: this is an incorrect behavior, non-existing branch falls back to default branch
+ _ = MakeRequest(t, NewRequest(t, http.MethodGet, "/api/v1/repos/user2/repo1/raw/README.md?ref=no-such"), http.StatusOK)
+}
+
+func testAPIGetContentsExt(t *testing.T) {
+ session := loginUser(t, "user2")
+ token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+ t.Run("DirContents", func(t *testing.T) {
+ req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext?ref=sub-home-md-img-check")
+ resp := MakeRequest(t, req, http.StatusOK)
+ var contentsResponse api.ContentsExtResponse
+ DecodeJSON(t, resp, &contentsResponse)
+ assert.Nil(t, contentsResponse.FileContents)
+ assert.NotNil(t, contentsResponse.DirContents)
+
+ req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/.?ref=sub-home-md-img-check")
+ resp = MakeRequest(t, req, http.StatusOK)
+ contentsResponse = api.ContentsExtResponse{}
+ DecodeJSON(t, resp, &contentsResponse)
+ assert.Nil(t, contentsResponse.FileContents)
+ assert.NotNil(t, contentsResponse.DirContents)
+
+ req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs?ref=sub-home-md-img-check")
+ resp = MakeRequest(t, req, http.StatusOK)
+ contentsResponse = api.ContentsExtResponse{}
+ DecodeJSON(t, resp, &contentsResponse)
+ assert.Nil(t, contentsResponse.FileContents)
+ assert.Equal(t, "README.md", contentsResponse.DirContents[0].Name)
+ assert.Nil(t, contentsResponse.DirContents[0].Encoding)
+ assert.Nil(t, contentsResponse.DirContents[0].Content)
+ assert.Nil(t, contentsResponse.DirContents[0].LastCommitSHA)
+ assert.Nil(t, contentsResponse.DirContents[0].LastCommitMessage)
+
+ // "includes=file_content" shouldn't affect directory listing
+ req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs?ref=sub-home-md-img-check&includes=file_content")
+ resp = MakeRequest(t, req, http.StatusOK)
+ contentsResponse = api.ContentsExtResponse{}
+ DecodeJSON(t, resp, &contentsResponse)
+ assert.Nil(t, contentsResponse.FileContents)
+ assert.Equal(t, "README.md", contentsResponse.DirContents[0].Name)
+ assert.Nil(t, contentsResponse.DirContents[0].Encoding)
+ assert.Nil(t, contentsResponse.DirContents[0].Content)
+
+ req = NewRequestf(t, "GET", "/api/v1/repos/user2/lfs/contents-ext?includes=file_content,lfs_metadata").AddTokenAuth(token2)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ contentsResponse = api.ContentsExtResponse{}
+ DecodeJSON(t, resp, &contentsResponse)
+ assert.Nil(t, contentsResponse.FileContents)
+ respFileIdx := slices.IndexFunc(contentsResponse.DirContents, func(response *api.ContentsResponse) bool { return response.Name == "jpeg.jpg" })
+ require.NotEqual(t, -1, respFileIdx)
+ respFile := contentsResponse.DirContents[respFileIdx]
+ assert.Equal(t, "jpeg.jpg", respFile.Name)
+ assert.Nil(t, respFile.Encoding)
+ assert.Nil(t, respFile.Content)
+ assert.Equal(t, util.ToPointer(int64(107)), respFile.LfsSize)
+ assert.Equal(t, util.ToPointer("0b8d8b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351"), respFile.LfsOid)
+ })
+ t.Run("FileContents", func(t *testing.T) {
+ // by default, no file content or commit info is returned
+ req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs/README.md?ref=sub-home-md-img-check")
+ resp := MakeRequest(t, req, http.StatusOK)
+ var contentsResponse api.ContentsExtResponse
+ DecodeJSON(t, resp, &contentsResponse)
+ assert.Nil(t, contentsResponse.DirContents)
+ assert.Equal(t, "README.md", contentsResponse.FileContents.Name)
+ assert.Nil(t, contentsResponse.FileContents.Encoding)
+ assert.Nil(t, contentsResponse.FileContents.Content)
+ assert.Nil(t, contentsResponse.FileContents.LastCommitSHA)
+ assert.Nil(t, contentsResponse.FileContents.LastCommitMessage)
+
+ // file content is only returned when `includes=file_content`
+ req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/contents-ext/docs/README.md?ref=sub-home-md-img-check&includes=file_content,commit_metadata,commit_message")
+ resp = MakeRequest(t, req, http.StatusOK)
+ contentsResponse = api.ContentsExtResponse{}
+ DecodeJSON(t, resp, &contentsResponse)
+ assert.Nil(t, contentsResponse.DirContents)
+ assert.Equal(t, "README.md", contentsResponse.FileContents.Name)
+ assert.NotNil(t, contentsResponse.FileContents.Encoding)
+ assert.NotNil(t, contentsResponse.FileContents.Content)
+ assert.Equal(t, "4649299398e4d39a5c09eb4f534df6f1e1eb87cc", *contentsResponse.FileContents.LastCommitSHA)
+ assert.Equal(t, "Test how READMEs render images when found in a subfolder\n", *contentsResponse.FileContents.LastCommitMessage)
+
+ req = NewRequestf(t, "GET", "/api/v1/repos/user2/lfs/contents-ext/jpeg.jpg?includes=file_content").AddTokenAuth(token2)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ contentsResponse = api.ContentsExtResponse{}
+ DecodeJSON(t, resp, &contentsResponse)
+ assert.Nil(t, contentsResponse.DirContents)
+ assert.NotNil(t, contentsResponse.FileContents)
+ respFile := contentsResponse.FileContents
+ assert.Equal(t, "jpeg.jpg", respFile.Name)
+ assert.NotNil(t, respFile.Encoding)
+ assert.NotNil(t, respFile.Content)
+ assert.Nil(t, contentsResponse.FileContents.LastCommitSHA)
+ assert.Nil(t, contentsResponse.FileContents.LastCommitMessage)
+ assert.Equal(t, util.ToPointer(int64(107)), respFile.LfsSize)
+ assert.Equal(t, util.ToPointer("0b8d8b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351"), respFile.LfsOid)
})
}
diff --git a/tests/integration/api_repo_git_blobs_test.go b/tests/integration/api_repo_git_blobs_test.go
index 184362e7e3..d4274bdb40 100644
--- a/tests/integration/api_repo_git_blobs_test.go
+++ b/tests/integration/api_repo_git_blobs_test.go
@@ -41,7 +41,7 @@ func TestAPIReposGitBlobs(t *testing.T) {
DecodeJSON(t, resp, &gitBlobResponse)
assert.NotNil(t, gitBlobResponse)
expectedContent := "dHJlZSAyYTJmMWQ0NjcwNzI4YTJlMTAwNDllMzQ1YmQ3YTI3NjQ2OGJlYWI2CmF1dGhvciB1c2VyMSA8YWRkcmVzczFAZXhhbXBsZS5jb20+IDE0ODk5NTY0NzkgLTA0MDAKY29tbWl0dGVyIEV0aGFuIEtvZW5pZyA8ZXRoYW50a29lbmlnQGdtYWlsLmNvbT4gMTQ4OTk1NjQ3OSAtMDQwMAoKSW5pdGlhbCBjb21taXQK"
- assert.Equal(t, expectedContent, gitBlobResponse.Content)
+ assert.Equal(t, expectedContent, *gitBlobResponse.Content)
// Tests a private repo with no token so will fail
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/blobs/%s", user2.Name, repo16.Name, repo16ReadmeSHA)
@@ -72,7 +72,7 @@ func TestAPIReposGitBlobs(t *testing.T) {
// Login as User4.
session = loginUser(t, user4.Name)
- token4 := getTokenForLoggedInUser(t, session)
+ token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll)
// Test using org repo "org3/repo3" where user4 is a NOT collaborator
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/blobs/d56a3073c1dbb7b15963110a049d50cdb5db99fc?access=%s", org3.Name, repo3.Name, token4)
diff --git a/tests/integration/api_repo_git_commits_test.go b/tests/integration/api_repo_git_commits_test.go
index c4c626eb49..a1584d4629 100644
--- a/tests/integration/api_repo_git_commits_test.go
+++ b/tests/integration/api_repo_git_commits_test.go
@@ -72,12 +72,12 @@ func TestAPIReposGitCommitList(t *testing.T) {
DecodeJSON(t, resp, &apiData)
assert.Len(t, apiData, 2)
- assert.EqualValues(t, "cfe3b3c1fd36fba04f9183287b106497e1afe986", apiData[0].CommitMeta.SHA)
+ assert.Equal(t, "cfe3b3c1fd36fba04f9183287b106497e1afe986", apiData[0].CommitMeta.SHA)
compareCommitFiles(t, []string{"link_hi", "test.csv"}, apiData[0].Files)
- assert.EqualValues(t, "c8e31bc7688741a5287fcde4fbb8fc129ca07027", apiData[1].CommitMeta.SHA)
+ assert.Equal(t, "c8e31bc7688741a5287fcde4fbb8fc129ca07027", apiData[1].CommitMeta.SHA)
compareCommitFiles(t, []string{"test.csv"}, apiData[1].Files)
- assert.EqualValues(t, "2", resp.Header().Get("X-Total"))
+ assert.Equal(t, "2", resp.Header().Get("X-Total"))
}
func TestAPIReposGitCommitListNotMaster(t *testing.T) {
@@ -96,14 +96,14 @@ func TestAPIReposGitCommitListNotMaster(t *testing.T) {
DecodeJSON(t, resp, &apiData)
assert.Len(t, apiData, 3)
- assert.EqualValues(t, "69554a64c1e6030f051e5c3f94bfbd773cd6a324", apiData[0].CommitMeta.SHA)
+ assert.Equal(t, "69554a64c1e6030f051e5c3f94bfbd773cd6a324", apiData[0].CommitMeta.SHA)
compareCommitFiles(t, []string{"readme.md"}, apiData[0].Files)
- assert.EqualValues(t, "27566bd5738fc8b4e3fef3c5e72cce608537bd95", apiData[1].CommitMeta.SHA)
+ assert.Equal(t, "27566bd5738fc8b4e3fef3c5e72cce608537bd95", apiData[1].CommitMeta.SHA)
compareCommitFiles(t, []string{"readme.md"}, apiData[1].Files)
- assert.EqualValues(t, "5099b81332712fe655e34e8dd63574f503f61811", apiData[2].CommitMeta.SHA)
+ assert.Equal(t, "5099b81332712fe655e34e8dd63574f503f61811", apiData[2].CommitMeta.SHA)
compareCommitFiles(t, []string{"readme.md"}, apiData[2].Files)
- assert.EqualValues(t, "3", resp.Header().Get("X-Total"))
+ assert.Equal(t, "3", resp.Header().Get("X-Total"))
}
func TestAPIReposGitCommitListPage2Empty(t *testing.T) {
@@ -177,7 +177,7 @@ func TestDownloadCommitDiffOrPatch(t *testing.T) {
reqDiff := NewRequestf(t, "GET", "/api/v1/repos/%s/repo16/git/commits/f27c2b2b03dcab38beaf89b0ab4ff61f6de63441.diff", user.Name).
AddTokenAuth(token)
resp := MakeRequest(t, reqDiff, http.StatusOK)
- assert.EqualValues(t,
+ assert.Equal(t,
"commit f27c2b2b03dcab38beaf89b0ab4ff61f6de63441\nAuthor: User2 <user2@example.com>\nDate: Sun Aug 6 19:55:01 2017 +0200\n\n good signed commit\n\ndiff --git a/readme.md b/readme.md\nnew file mode 100644\nindex 0000000..458121c\n--- /dev/null\n+++ b/readme.md\n@@ -0,0 +1 @@\n+good sign\n",
resp.Body.String())
@@ -185,7 +185,7 @@ func TestDownloadCommitDiffOrPatch(t *testing.T) {
reqPatch := NewRequestf(t, "GET", "/api/v1/repos/%s/repo16/git/commits/f27c2b2b03dcab38beaf89b0ab4ff61f6de63441.patch", user.Name).
AddTokenAuth(token)
resp = MakeRequest(t, reqPatch, http.StatusOK)
- assert.EqualValues(t,
+ assert.Equal(t,
"From f27c2b2b03dcab38beaf89b0ab4ff61f6de63441 Mon Sep 17 00:00:00 2001\nFrom: User2 <user2@example.com>\nDate: Sun, 6 Aug 2017 19:55:01 +0200\nSubject: [PATCH] good signed commit\n\n---\n readme.md | 1 +\n 1 file changed, 1 insertion(+)\n create mode 100644 readme.md\n\ndiff --git a/readme.md b/readme.md\nnew file mode 100644\nindex 0000000..458121c\n--- /dev/null\n+++ b/readme.md\n@@ -0,0 +1 @@\n+good sign\n",
resp.Body.String())
}
@@ -208,7 +208,7 @@ func TestGetFileHistory(t *testing.T) {
assert.Equal(t, "f27c2b2b03dcab38beaf89b0ab4ff61f6de63441", apiData[0].CommitMeta.SHA)
compareCommitFiles(t, []string{"readme.md"}, apiData[0].Files)
- assert.EqualValues(t, "1", resp.Header().Get("X-Total"))
+ assert.Equal(t, "1", resp.Header().Get("X-Total"))
}
func TestGetFileHistoryNotOnMaster(t *testing.T) {
@@ -229,5 +229,5 @@ func TestGetFileHistoryNotOnMaster(t *testing.T) {
assert.Equal(t, "c8e31bc7688741a5287fcde4fbb8fc129ca07027", apiData[0].CommitMeta.SHA)
compareCommitFiles(t, []string{"test.csv"}, apiData[0].Files)
- assert.EqualValues(t, "1", resp.Header().Get("X-Total"))
+ assert.Equal(t, "1", resp.Header().Get("X-Total"))
}
diff --git a/tests/integration/api_repo_git_tags_test.go b/tests/integration/api_repo_git_tags_test.go
index c5883a8058..5a66337589 100644
--- a/tests/integration/api_repo_git_tags_test.go
+++ b/tests/integration/api_repo_git_tags_test.go
@@ -30,8 +30,8 @@ func TestAPIGitTags(t *testing.T) {
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
// Set up git config for the tagger
- _ = git.NewCommand(git.DefaultContext, "config", "user.name").AddDynamicArguments(user.Name).Run(&git.RunOpts{Dir: repo.RepoPath()})
- _ = git.NewCommand(git.DefaultContext, "config", "user.email").AddDynamicArguments(user.Email).Run(&git.RunOpts{Dir: repo.RepoPath()})
+ _ = git.NewCommand("config", "user.name").AddDynamicArguments(user.Name).Run(git.DefaultContext, &git.RunOpts{Dir: repo.RepoPath()})
+ _ = git.NewCommand("config", "user.email").AddDynamicArguments(user.Email).Run(git.DefaultContext, &git.RunOpts{Dir: repo.RepoPath()})
gitRepo, _ := gitrepo.OpenRepository(git.DefaultContext, repo)
defer gitRepo.Close()
diff --git a/tests/integration/api_repo_git_trees_test.go b/tests/integration/api_repo_git_trees_test.go
index 8eec6d8d22..47063d9091 100644
--- a/tests/integration/api_repo_git_trees_test.go
+++ b/tests/integration/api_repo_git_trees_test.go
@@ -69,7 +69,7 @@ func TestAPIReposGitTrees(t *testing.T) {
// Login as User4.
session = loginUser(t, user4.Name)
- token4 := getTokenForLoggedInUser(t, session)
+ token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll)
// Test using org repo "org3/repo3" where user4 is a NOT collaborator
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/trees/d56a3073c1dbb7b15963110a049d50cdb5db99fc?access=%s", org3.Name, repo3.Name, token4)
diff --git a/tests/integration/api_repo_languages_test.go b/tests/integration/api_repo_languages_test.go
index 1045aef57d..6347a43b4e 100644
--- a/tests/integration/api_repo_languages_test.go
+++ b/tests/integration/api_repo_languages_test.go
@@ -9,6 +9,8 @@ import (
"testing"
"time"
+ "code.gitea.io/gitea/modules/test"
+
"github.com/stretchr/testify/assert"
)
@@ -32,7 +34,8 @@ func TestRepoLanguages(t *testing.T) {
"content": "package main",
"commit_choice": "direct",
})
- session.MakeRequest(t, req, http.StatusSeeOther)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ assert.NotEmpty(t, test.RedirectURL(resp))
// let gitea calculate language stats
time.Sleep(time.Second)
diff --git a/tests/integration/api_repo_lfs_locks_test.go b/tests/integration/api_repo_lfs_locks_test.go
index 4ba01e6d9b..161fa45dc6 100644
--- a/tests/integration/api_repo_lfs_locks_test.go
+++ b/tests/integration/api_repo_lfs_locks_test.go
@@ -112,7 +112,7 @@ func TestAPILFSLocksLogged(t *testing.T) {
var lfsLock api.LFSLockResponse
DecodeJSON(t, resp, &lfsLock)
assert.Equal(t, test.user.Name, lfsLock.Lock.Owner.Name)
- assert.EqualValues(t, lfsLock.Lock.LockedAt.Format(time.RFC3339), lfsLock.Lock.LockedAt.Format(time.RFC3339Nano)) // locked at should be rounded to second
+ assert.Equal(t, lfsLock.Lock.LockedAt.Format(time.RFC3339), lfsLock.Lock.LockedAt.Format(time.RFC3339Nano)) // locked at should be rounded to second
for _, id := range test.addTime {
resultsTests[id].locksTimes = append(resultsTests[id].locksTimes, time.Now())
}
@@ -129,9 +129,9 @@ func TestAPILFSLocksLogged(t *testing.T) {
DecodeJSON(t, resp, &lfsLocks)
assert.Len(t, lfsLocks.Locks, test.totalCount)
for i, lock := range lfsLocks.Locks {
- assert.EqualValues(t, test.locksOwners[i].Name, lock.Owner.Name)
+ assert.Equal(t, test.locksOwners[i].Name, lock.Owner.Name)
assert.WithinDuration(t, test.locksTimes[i], lock.LockedAt, 10*time.Second)
- assert.EqualValues(t, lock.LockedAt.Format(time.RFC3339), lock.LockedAt.Format(time.RFC3339Nano)) // locked at should be rounded to second
+ assert.Equal(t, lock.LockedAt.Format(time.RFC3339), lock.LockedAt.Format(time.RFC3339Nano)) // locked at should be rounded to second
}
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks/verify", test.repo.FullName()), map[string]string{})
@@ -143,7 +143,7 @@ func TestAPILFSLocksLogged(t *testing.T) {
assert.Len(t, lfsLocksVerify.Ours, test.oursCount)
assert.Len(t, lfsLocksVerify.Theirs, test.theirsCount)
for _, lock := range lfsLocksVerify.Ours {
- assert.EqualValues(t, test.user.Name, lock.Owner.Name)
+ assert.Equal(t, test.user.Name, lock.Owner.Name)
deleteTests = append(deleteTests, struct {
user *user_model.User
repo *repo_model.Repository
diff --git a/tests/integration/api_repo_lfs_migrate_test.go b/tests/integration/api_repo_lfs_migrate_test.go
index 8b4d79db02..6ca6f9afab 100644
--- a/tests/integration/api_repo_lfs_migrate_test.go
+++ b/tests/integration/api_repo_lfs_migrate_test.go
@@ -40,7 +40,7 @@ func TestAPIRepoLFSMigrateLocal(t *testing.T) {
LFS: true,
}).AddTokenAuth(token)
resp := MakeRequest(t, req, NoExpectedStatus)
- assert.EqualValues(t, http.StatusCreated, resp.Code)
+ assert.Equal(t, http.StatusCreated, resp.Code)
store := lfs.NewContentStore()
ok, _ := store.Verify(lfs.Pointer{Oid: "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab041", Size: 6})
diff --git a/tests/integration/api_repo_lfs_test.go b/tests/integration/api_repo_lfs_test.go
index 6b42b83bc5..ec6a3a3b57 100644
--- a/tests/integration/api_repo_lfs_test.go
+++ b/tests/integration/api_repo_lfs_test.go
@@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@@ -226,9 +227,7 @@ func TestAPILFSBatch(t *testing.T) {
t.Run("FileTooBig", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
-
- oldMaxFileSize := setting.LFS.MaxFileSize
- setting.LFS.MaxFileSize = 2
+ defer test.MockVariableValue(&setting.LFS.MaxFileSize, 2)()
req := newRequest(t, &lfs.BatchRequest{
Operation: "upload",
@@ -243,8 +242,6 @@ func TestAPILFSBatch(t *testing.T) {
assert.NotNil(t, br.Objects[0].Error)
assert.Equal(t, http.StatusUnprocessableEntity, br.Objects[0].Error.Code)
assert.Equal(t, "Size must be less than or equal to 2", br.Objects[0].Error.Message)
-
- setting.LFS.MaxFileSize = oldMaxFileSize
})
t.Run("AddMeta", func(t *testing.T) {
diff --git a/tests/integration/api_repo_license_test.go b/tests/integration/api_repo_license_test.go
index 52d3085694..fb4450a2bd 100644
--- a/tests/integration/api_repo_license_test.go
+++ b/tests/integration/api_repo_license_test.go
@@ -12,12 +12,13 @@ import (
auth_model "code.gitea.io/gitea/models/auth"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
var testLicenseContent = `
-Copyright (c) 2024 Gitea
+Copyright (c) 2024 Gitea
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
@@ -48,7 +49,8 @@ func TestAPIRepoLicense(t *testing.T) {
"content": testLicenseContent,
"commit_choice": "direct",
})
- session.MakeRequest(t, req, http.StatusSeeOther)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ assert.NotEmpty(t, test.RedirectURL(resp))
// let gitea update repo license
time.Sleep(time.Second)
diff --git a/tests/integration/api_repo_raw_test.go b/tests/integration/api_repo_raw_test.go
index e5f83d1c80..e9d741925f 100644
--- a/tests/integration/api_repo_raw_test.go
+++ b/tests/integration/api_repo_raw_test.go
@@ -30,11 +30,11 @@ func TestAPIReposRaw(t *testing.T) {
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/raw/%s/README.md", user.Name, ref).
AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
- assert.EqualValues(t, "file", resp.Header().Get("x-gitea-object-type"))
+ assert.Equal(t, "file", resp.Header().Get("x-gitea-object-type"))
}
// Test default branch
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/raw/README.md", user.Name).
AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
- assert.EqualValues(t, "file", resp.Header().Get("x-gitea-object-type"))
+ assert.Equal(t, "file", resp.Header().Get("x-gitea-object-type"))
}
diff --git a/tests/integration/api_repo_secrets_test.go b/tests/integration/api_repo_secrets_test.go
index c3074d9ece..2059aff484 100644
--- a/tests/integration/api_repo_secrets_test.go
+++ b/tests/integration/api_repo_secrets_test.go
@@ -73,6 +73,33 @@ func TestAPIRepoSecrets(t *testing.T) {
}
})
+ t.Run("CreateWithDescription", func(t *testing.T) {
+ cases := []struct {
+ Name string
+ Description string
+ ExpectedStatus int
+ }{
+ {
+ Name: "no_description",
+ Description: "",
+ ExpectedStatus: http.StatusCreated,
+ },
+ {
+ Name: "description",
+ Description: "some description",
+ ExpectedStatus: http.StatusCreated,
+ },
+ }
+
+ for _, c := range cases {
+ req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/actions/secrets/%s", repo.FullName(), c.Name), api.CreateOrUpdateSecretOption{
+ Data: "data",
+ Description: c.Description,
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, c.ExpectedStatus)
+ }
+ })
+
t.Run("Update", func(t *testing.T) {
name := "update_secret"
url := fmt.Sprintf("/api/v1/repos/%s/actions/secrets/%s", repo.FullName(), name)
diff --git a/tests/integration/api_repo_tags_test.go b/tests/integration/api_repo_tags_test.go
index a7f021ca4f..3932a8ba2b 100644
--- a/tests/integration/api_repo_tags_test.go
+++ b/tests/integration/api_repo_tags_test.go
@@ -48,10 +48,10 @@ func TestAPIRepoTags(t *testing.T) {
assert.Len(t, tags, 2)
for _, tag := range tags {
if tag.Name != "v1.1" {
- assert.EqualValues(t, newTag.Name, tag.Name)
- assert.EqualValues(t, newTag.Message, tag.Message)
- assert.EqualValues(t, "nice!\nand some text", tag.Message)
- assert.EqualValues(t, newTag.Commit.SHA, tag.Commit.SHA)
+ assert.Equal(t, newTag.Name, tag.Name)
+ assert.Equal(t, newTag.Message, tag.Message)
+ assert.Equal(t, "nice!\nand some text", tag.Message)
+ assert.Equal(t, newTag.Commit.SHA, tag.Commit.SHA)
}
}
@@ -61,7 +61,7 @@ func TestAPIRepoTags(t *testing.T) {
resp = MakeRequest(t, req, http.StatusOK)
var tag *api.Tag
DecodeJSON(t, resp, &tag)
- assert.EqualValues(t, newTag, tag)
+ assert.Equal(t, newTag, tag)
// delete tag
delReq := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/tags/%s", user.Name, repoName, newTag.Name).
diff --git a/tests/integration/api_repo_teams_test.go b/tests/integration/api_repo_teams_test.go
index 07d065b02b..143e3dd29f 100644
--- a/tests/integration/api_repo_teams_test.go
+++ b/tests/integration/api_repo_teams_test.go
@@ -37,15 +37,15 @@ func TestAPIRepoTeams(t *testing.T) {
var teams []*api.Team
DecodeJSON(t, res, &teams)
if assert.Len(t, teams, 2) {
- assert.EqualValues(t, "Owners", teams[0].Name)
+ assert.Equal(t, "Owners", teams[0].Name)
assert.True(t, teams[0].CanCreateOrgRepo)
assert.True(t, util.SliceSortedEqual(unit.AllUnitKeyNames(), teams[0].Units), "%v == %v", unit.AllUnitKeyNames(), teams[0].Units)
- assert.EqualValues(t, "owner", teams[0].Permission)
+ assert.Equal(t, "owner", teams[0].Permission)
- assert.EqualValues(t, "test_team", teams[1].Name)
+ assert.Equal(t, "test_team", teams[1].Name)
assert.False(t, teams[1].CanCreateOrgRepo)
- assert.EqualValues(t, []string{"repo.issues"}, teams[1].Units)
- assert.EqualValues(t, "write", teams[1].Permission)
+ assert.Equal(t, []string{"repo.issues"}, teams[1].Units)
+ assert.Equal(t, "write", teams[1].Permission)
}
// IsTeam
@@ -54,7 +54,7 @@ func TestAPIRepoTeams(t *testing.T) {
res = MakeRequest(t, req, http.StatusOK)
var team *api.Team
DecodeJSON(t, res, &team)
- assert.EqualValues(t, teams[1], team)
+ assert.Equal(t, teams[1], team)
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/teams/%s", publicOrgRepo.FullName(), "NonExistingTeam")).
AddTokenAuth(token)
diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go
index 122afbfa08..a2c3a467c6 100644
--- a/tests/integration/api_repo_test.go
+++ b/tests/integration/api_repo_test.go
@@ -37,7 +37,7 @@ func TestAPIUserReposNotLogin(t *testing.T) {
unittest.Cond("is_private = ?", false))
assert.Len(t, apiRepos, expectedLen)
for _, repo := range apiRepos {
- assert.EqualValues(t, user.ID, repo.Owner.ID)
+ assert.Equal(t, user.ID, repo.Owner.ID)
assert.False(t, repo.Private)
}
}
@@ -45,7 +45,7 @@ func TestAPIUserReposNotLogin(t *testing.T) {
func TestAPIUserReposWithWrongToken(t *testing.T) {
defer tests.PrepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- wrongToken := fmt.Sprintf("Bearer %s", "wrong_token")
+ wrongToken := "Bearer " + "wrong_token"
req := NewRequestf(t, "GET", "/api/v1/users/%s/repos", user.Name).
AddTokenAuth(wrongToken)
resp := MakeRequest(t, req, http.StatusUnauthorized)
@@ -266,25 +266,25 @@ func TestAPIViewRepo(t *testing.T) {
resp := MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &repo)
assert.EqualValues(t, 1, repo.ID)
- assert.EqualValues(t, "repo1", repo.Name)
- assert.EqualValues(t, 2, repo.Releases)
- assert.EqualValues(t, 1, repo.OpenIssues)
- assert.EqualValues(t, 3, repo.OpenPulls)
+ assert.Equal(t, "repo1", repo.Name)
+ assert.Equal(t, 2, repo.Releases)
+ assert.Equal(t, 1, repo.OpenIssues)
+ assert.Equal(t, 3, repo.OpenPulls)
req = NewRequest(t, "GET", "/api/v1/repos/user12/repo10")
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &repo)
assert.EqualValues(t, 10, repo.ID)
- assert.EqualValues(t, "repo10", repo.Name)
- assert.EqualValues(t, 1, repo.OpenPulls)
- assert.EqualValues(t, 1, repo.Forks)
+ assert.Equal(t, "repo10", repo.Name)
+ assert.Equal(t, 1, repo.OpenPulls)
+ assert.Equal(t, 1, repo.Forks)
req = NewRequest(t, "GET", "/api/v1/repos/user5/repo4")
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &repo)
assert.EqualValues(t, 4, repo.ID)
- assert.EqualValues(t, "repo4", repo.Name)
- assert.EqualValues(t, 1, repo.Stars)
+ assert.Equal(t, "repo4", repo.Name)
+ assert.Equal(t, 1, repo.Stars)
}
func TestAPIOrgRepos(t *testing.T) {
@@ -337,9 +337,9 @@ func TestAPIOrgReposWithCodeUnitDisabled(t *testing.T) {
var units []unit_model.Type
units = append(units, unit_model.TypeCode)
- if err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo21, nil, units); err != nil {
- assert.Fail(t, "should have been able to delete code repository unit; failed to %v", err)
- }
+ err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo21, nil, units)
+ assert.NoError(t, err, "should have been able to delete code repository unit")
+
assert.False(t, repo21.UnitEnabled(db.DefaultContext, unit_model.TypeCode))
session := loginUser(t, "user2")
@@ -403,12 +403,12 @@ func TestAPIRepoMigrate(t *testing.T) {
case "Remote visit addressed rate limitation.":
t.Log("test hit github rate limitation")
case "You can not import from disallowed hosts.":
- assert.EqualValues(t, "private-ip", testCase.repoName)
+ assert.Equal(t, "private-ip", testCase.repoName)
default:
- assert.FailNow(t, "unexpected error '%v' on url '%s'", respJSON["message"], testCase.cloneURL)
+ assert.FailNow(t, "unexpected error", "unexpected error '%v' on url '%s'", respJSON["message"], testCase.cloneURL)
}
} else {
- assert.EqualValues(t, testCase.expectedStatus, resp.Code)
+ assert.Equal(t, testCase.expectedStatus, resp.Code)
}
}
}
@@ -471,6 +471,15 @@ func TestAPIMirrorSyncNonMirrorRepo(t *testing.T) {
assert.Equal(t, "Repository is not a mirror", errRespJSON["message"])
}
+func testAPIOrgCreateRepo(t *testing.T, session *TestSession, orgName, repoName string, status int) {
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization, auth_model.AccessTokenScopeWriteRepository)
+
+ req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/org/%s/repos", orgName), &api.CreateRepoOption{
+ Name: repoName,
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, status)
+}
+
func TestAPIOrgRepoCreate(t *testing.T) {
testCases := []struct {
ctxUserID int64
@@ -488,11 +497,7 @@ func TestAPIOrgRepoCreate(t *testing.T) {
for _, testCase := range testCases {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID})
session := loginUser(t, user.Name)
- token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization, auth_model.AccessTokenScopeWriteRepository)
- req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/org/%s/repos", testCase.orgName), &api.CreateRepoOption{
- Name: testCase.repoName,
- }).AddTokenAuth(token)
- MakeRequest(t, req, testCase.expectedStatus)
+ testAPIOrgCreateRepo(t, session, testCase.orgName, testCase.repoName, testCase.expectedStatus)
}
}
@@ -581,7 +586,7 @@ func TestAPIRepoTransfer(t *testing.T) {
// cleanup
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID})
- _ = repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, repo.ID)
+ _ = repo_service.DeleteRepositoryDirectly(db.DefaultContext, repo.ID)
}
func transfer(t *testing.T) *repo_model.Repository {
@@ -735,5 +740,5 @@ func TestAPIRepoGetAssignees(t *testing.T) {
resp := MakeRequest(t, req, http.StatusOK)
var assignees []*api.User
DecodeJSON(t, resp, &assignees)
- assert.Len(t, assignees, 1)
+ assert.Len(t, assignees, 2)
}
diff --git a/tests/integration/api_repo_topic_test.go b/tests/integration/api_repo_topic_test.go
index a10e159b78..82d0c54ca8 100644
--- a/tests/integration/api_repo_topic_test.go
+++ b/tests/integration/api_repo_topic_test.go
@@ -30,7 +30,7 @@ func TestAPITopicSearch(t *testing.T) {
res := MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 6)
- assert.EqualValues(t, "6", res.Header().Get("x-total-count"))
+ assert.Equal(t, "6", res.Header().Get("x-total-count"))
// pagination search topics first page
topics.TopicNames = nil
@@ -40,7 +40,7 @@ func TestAPITopicSearch(t *testing.T) {
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 4)
- assert.EqualValues(t, "6", res.Header().Get("x-total-count"))
+ assert.Equal(t, "6", res.Header().Get("x-total-count"))
// pagination search topics second page
topics.TopicNames = nil
@@ -50,7 +50,7 @@ func TestAPITopicSearch(t *testing.T) {
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 2)
- assert.EqualValues(t, "6", res.Header().Get("x-total-count"))
+ assert.Equal(t, "6", res.Header().Get("x-total-count"))
// add keyword search
query = url.Values{"page": []string{"1"}, "limit": []string{"4"}}
@@ -66,8 +66,8 @@ func TestAPITopicSearch(t *testing.T) {
DecodeJSON(t, res, &topics)
if assert.Len(t, topics.TopicNames, 1) {
assert.EqualValues(t, 2, topics.TopicNames[0].ID)
- assert.EqualValues(t, "database", topics.TopicNames[0].Name)
- assert.EqualValues(t, 1, topics.TopicNames[0].RepoCount)
+ assert.Equal(t, "database", topics.TopicNames[0].Name)
+ assert.Equal(t, 1, topics.TopicNames[0].RepoCount)
}
}
diff --git a/tests/integration/api_repo_variables_test.go b/tests/integration/api_repo_variables_test.go
index 7847962b07..b5c88af279 100644
--- a/tests/integration/api_repo_variables_test.go
+++ b/tests/integration/api_repo_variables_test.go
@@ -35,11 +35,11 @@ func TestAPIRepoVariables(t *testing.T) {
},
{
Name: "_",
- ExpectedStatus: http.StatusNoContent,
+ ExpectedStatus: http.StatusCreated,
},
{
Name: "TEST_VAR",
- ExpectedStatus: http.StatusNoContent,
+ ExpectedStatus: http.StatusCreated,
},
{
Name: "test_var",
@@ -81,7 +81,7 @@ func TestAPIRepoVariables(t *testing.T) {
req := NewRequestWithJSON(t, "POST", url, api.CreateVariableOption{
Value: "initial_val",
}).AddTokenAuth(token)
- MakeRequest(t, req, http.StatusNoContent)
+ MakeRequest(t, req, http.StatusCreated)
cases := []struct {
Name string
@@ -138,7 +138,7 @@ func TestAPIRepoVariables(t *testing.T) {
req := NewRequestWithJSON(t, "POST", url, api.CreateVariableOption{
Value: "initial_val",
}).AddTokenAuth(token)
- MakeRequest(t, req, http.StatusNoContent)
+ MakeRequest(t, req, http.StatusCreated)
req = NewRequest(t, "DELETE", url).AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)
diff --git a/tests/integration/api_settings_test.go b/tests/integration/api_settings_test.go
index 9881578fba..743dbb0481 100644
--- a/tests/integration/api_settings_test.go
+++ b/tests/integration/api_settings_test.go
@@ -30,11 +30,12 @@ func TestAPIExposedSettings(t *testing.T) {
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiSettings)
- assert.EqualValues(t, &api.GeneralAPISettings{
+ assert.Equal(t, &api.GeneralAPISettings{
MaxResponseItems: setting.API.MaxResponseItems,
DefaultPagingNum: setting.API.DefaultPagingNum,
DefaultGitTreesPerPage: setting.API.DefaultGitTreesPerPage,
DefaultMaxBlobSize: setting.API.DefaultMaxBlobSize,
+ DefaultMaxResponseSize: setting.API.DefaultMaxResponseSize,
}, apiSettings)
repo := new(api.GeneralRepoSettings)
@@ -42,7 +43,7 @@ func TestAPIExposedSettings(t *testing.T) {
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &repo)
- assert.EqualValues(t, &api.GeneralRepoSettings{
+ assert.Equal(t, &api.GeneralRepoSettings{
MirrorsDisabled: !setting.Mirror.Enabled,
HTTPGitDisabled: setting.Repository.DisableHTTPGit,
MigrationsDisabled: setting.Repository.DisableMigrations,
@@ -55,7 +56,7 @@ func TestAPIExposedSettings(t *testing.T) {
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &attachment)
- assert.EqualValues(t, &api.GeneralAttachmentSettings{
+ assert.Equal(t, &api.GeneralAttachmentSettings{
Enabled: setting.Attachment.Enabled,
AllowedTypes: setting.Attachment.AllowedTypes,
MaxFiles: setting.Attachment.MaxFiles,
diff --git a/tests/integration/api_team_test.go b/tests/integration/api_team_test.go
index d14c66ff2c..e54203ed50 100644
--- a/tests/integration/api_team_test.go
+++ b/tests/integration/api_team_test.go
@@ -40,9 +40,9 @@ func TestAPITeam(t *testing.T) {
var apiTeam api.Team
DecodeJSON(t, resp, &apiTeam)
- assert.EqualValues(t, team.ID, apiTeam.ID)
+ assert.Equal(t, team.ID, apiTeam.ID)
assert.Equal(t, team.Name, apiTeam.Name)
- assert.EqualValues(t, convert.ToOrganization(db.DefaultContext, org), apiTeam.Organization)
+ assert.Equal(t, convert.ToOrganization(db.DefaultContext, org), apiTeam.Organization)
// non team member user will not access the teams details
teamUser2 := unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{ID: 3})
@@ -78,9 +78,9 @@ func TestAPITeam(t *testing.T) {
apiTeam = api.Team{}
DecodeJSON(t, resp, &apiTeam)
checkTeamResponse(t, "CreateTeam1", &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
- teamToCreate.Permission, teamToCreate.Units, nil)
+ "none", teamToCreate.Units, nil)
checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
- teamToCreate.Permission, teamToCreate.Units, nil)
+ "none", teamToCreate.Units, nil)
teamID := apiTeam.ID
// Edit team.
@@ -149,9 +149,9 @@ func TestAPITeam(t *testing.T) {
apiTeam = api.Team{}
DecodeJSON(t, resp, &apiTeam)
checkTeamResponse(t, "CreateTeam2", &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
- "read", nil, teamToCreate.UnitsMap)
+ "none", nil, teamToCreate.UnitsMap)
checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
- "read", nil, teamToCreate.UnitsMap)
+ "none", nil, teamToCreate.UnitsMap)
teamID = apiTeam.ID
// Edit team.
@@ -171,9 +171,9 @@ func TestAPITeam(t *testing.T) {
apiTeam = api.Team{}
DecodeJSON(t, resp, &apiTeam)
checkTeamResponse(t, "EditTeam2", &apiTeam, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories,
- "read", nil, teamToEdit.UnitsMap)
+ "none", nil, teamToEdit.UnitsMap)
checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories,
- "read", nil, teamToEdit.UnitsMap)
+ "none", nil, teamToEdit.UnitsMap)
// Edit team Description only
editDescription = "second team"
@@ -184,9 +184,9 @@ func TestAPITeam(t *testing.T) {
apiTeam = api.Team{}
DecodeJSON(t, resp, &apiTeam)
checkTeamResponse(t, "EditTeam2_DescOnly", &apiTeam, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories,
- "read", nil, teamToEdit.UnitsMap)
+ "none", nil, teamToEdit.UnitsMap)
checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories,
- "read", nil, teamToEdit.UnitsMap)
+ "none", nil, teamToEdit.UnitsMap)
// Read team.
teamRead = unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
@@ -247,10 +247,10 @@ func checkTeamResponse(t *testing.T, testName string, apiTeam *api.Team, name, d
if units != nil {
sort.StringSlice(units).Sort()
sort.StringSlice(apiTeam.Units).Sort()
- assert.EqualValues(t, units, apiTeam.Units, "units")
+ assert.Equal(t, units, apiTeam.Units, "units")
}
if unitsMap != nil {
- assert.EqualValues(t, unitsMap, apiTeam.UnitsMap, "unitsMap")
+ assert.Equal(t, unitsMap, apiTeam.UnitsMap, "unitsMap")
}
})
}
diff --git a/tests/integration/api_team_user_test.go b/tests/integration/api_team_user_test.go
index 6c80bc9f80..cbbbe00a9e 100644
--- a/tests/integration/api_team_user_test.go
+++ b/tests/integration/api_team_user_test.go
@@ -21,29 +21,31 @@ import (
func TestAPITeamUser(t *testing.T) {
defer tests.PrepareTestEnv(t)()
-
- normalUsername := "user2"
- session := loginUser(t, normalUsername)
- token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrganization)
- req := NewRequest(t, "GET", "/api/v1/teams/1/members/user1").
- AddTokenAuth(token)
- MakeRequest(t, req, http.StatusNotFound)
-
- req = NewRequest(t, "GET", "/api/v1/teams/1/members/user2").
- AddTokenAuth(token)
- resp := MakeRequest(t, req, http.StatusOK)
- var user2 *api.User
- DecodeJSON(t, resp, &user2)
- user2.Created = user2.Created.In(time.Local)
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"})
-
- expectedUser := convert.ToUser(db.DefaultContext, user, user)
-
- // test time via unix timestamp
- assert.EqualValues(t, expectedUser.LastLogin.Unix(), user2.LastLogin.Unix())
- assert.EqualValues(t, expectedUser.Created.Unix(), user2.Created.Unix())
- expectedUser.LastLogin = user2.LastLogin
- expectedUser.Created = user2.Created
-
- assert.Equal(t, expectedUser, user2)
+ user2Session := loginUser(t, "user2")
+ user2Token := getTokenForLoggedInUser(t, user2Session, auth_model.AccessTokenScopeWriteOrganization)
+
+ t.Run("User2ReadUser1", func(t *testing.T) {
+ req := NewRequest(t, "GET", "/api/v1/teams/1/members/user1").AddTokenAuth(user2Token)
+ MakeRequest(t, req, http.StatusNotFound)
+ })
+
+ t.Run("User2ReadSelf", func(t *testing.T) {
+ // read self user
+ req := NewRequest(t, "GET", "/api/v1/teams/1/members/user2").AddTokenAuth(user2Token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ var user2 *api.User
+ DecodeJSON(t, resp, &user2)
+ user2.Created = user2.Created.In(time.Local)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"})
+
+ expectedUser := convert.ToUser(db.DefaultContext, user, user)
+
+ // test time via unix timestamp
+ assert.Equal(t, expectedUser.LastLogin.Unix(), user2.LastLogin.Unix())
+ assert.Equal(t, expectedUser.Created.Unix(), user2.Created.Unix())
+ expectedUser.LastLogin = user2.LastLogin
+ expectedUser.Created = user2.Created
+
+ assert.Equal(t, expectedUser, user2)
+ })
}
diff --git a/tests/integration/api_token_test.go b/tests/integration/api_token_test.go
index 01d18ef6f1..1770358d21 100644
--- a/tests/integration/api_token_test.go
+++ b/tests/integration/api_token_test.go
@@ -507,7 +507,7 @@ func runTestCase(t *testing.T, testCase *requiredScopeTestCase, user *user_model
} else if minRequiredLevel == auth_model.Write {
unauthorizedLevel = auth_model.Read
} else {
- assert.FailNow(t, "Invalid test case: Unknown access token scope level: %v", minRequiredLevel)
+ assert.FailNow(t, "Invalid test case", "Unknown access token scope level: %v", minRequiredLevel)
}
}
diff --git a/tests/integration/api_twofa_test.go b/tests/integration/api_twofa_test.go
index 18e6fa91b7..938feba61a 100644
--- a/tests/integration/api_twofa_test.go
+++ b/tests/integration/api_twofa_test.go
@@ -94,7 +94,7 @@ func TestBasicAuthWithWebAuthn(t *testing.T) {
}
var userParsed userResponse
DecodeJSON(t, resp, &userParsed)
- assert.EqualValues(t, "Basic authorization is not allowed while webAuthn enrolled", userParsed.Message)
+ assert.Equal(t, "basic authorization is not allowed while WebAuthn enrolled", userParsed.Message)
// user32 has webauthn enrolled, he can't request git protocol with basic auth
req = NewRequest(t, "GET", "/user2/repo1/info/refs")
diff --git a/tests/integration/api_user_block_test.go b/tests/integration/api_user_block_test.go
index ae6b9eb849..6f73b089df 100644
--- a/tests/integration/api_user_block_test.go
+++ b/tests/integration/api_user_block_test.go
@@ -76,7 +76,7 @@ func TestBlockUser(t *testing.T) {
blockeeName := "user10"
t.Run("Block", func(t *testing.T) {
- req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/blocks/%s", blockeeName))
+ req := NewRequest(t, "PUT", "/api/v1/user/blocks/"+blockeeName)
MakeRequest(t, req, http.StatusUnauthorized)
assert.EqualValues(t, 1, countStars(t, blockerID, blockeeID))
@@ -84,7 +84,7 @@ func TestBlockUser(t *testing.T) {
assert.EqualValues(t, 1, countRepositoryTransfers(t, blockerID, blockeeID))
assert.EqualValues(t, 1, countCollaborations(t, blockerID, blockeeID))
- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/blocks/%s", blockeeName)).
+ req = NewRequest(t, "GET", "/api/v1/user/blocks/"+blockeeName).
AddTokenAuth(blockerToken)
MakeRequest(t, req, http.StatusNotFound)
@@ -97,15 +97,15 @@ func TestBlockUser(t *testing.T) {
assert.EqualValues(t, 0, countRepositoryTransfers(t, blockerID, blockeeID))
assert.EqualValues(t, 0, countCollaborations(t, blockerID, blockeeID))
- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/blocks/%s", blockeeName)).
+ req = NewRequest(t, "GET", "/api/v1/user/blocks/"+blockeeName).
AddTokenAuth(blockerToken)
MakeRequest(t, req, http.StatusNoContent)
- req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/blocks/%s", blockeeName)).
+ req = NewRequest(t, "PUT", "/api/v1/user/blocks/"+blockeeName).
AddTokenAuth(blockerToken)
MakeRequest(t, req, http.StatusBadRequest) // can't block blocked user
- req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/blocks/%s", "org3")).
+ req = NewRequest(t, "PUT", "/api/v1/user/blocks/"+"org3").
AddTokenAuth(blockerToken)
MakeRequest(t, req, http.StatusBadRequest) // can't block organization
@@ -124,18 +124,18 @@ func TestBlockUser(t *testing.T) {
})
t.Run("Unblock", func(t *testing.T) {
- req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/blocks/%s", blockeeName))
+ req := NewRequest(t, "DELETE", "/api/v1/user/blocks/"+blockeeName)
MakeRequest(t, req, http.StatusUnauthorized)
- req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/blocks/%s", blockeeName)).
+ req = NewRequest(t, "DELETE", "/api/v1/user/blocks/"+blockeeName).
AddTokenAuth(blockerToken)
MakeRequest(t, req, http.StatusNoContent)
- req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/blocks/%s", blockeeName)).
+ req = NewRequest(t, "DELETE", "/api/v1/user/blocks/"+blockeeName).
AddTokenAuth(blockerToken)
MakeRequest(t, req, http.StatusBadRequest)
- req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/blocks/%s", "org3")).
+ req = NewRequest(t, "DELETE", "/api/v1/user/blocks/"+"org3").
AddTokenAuth(blockerToken)
MakeRequest(t, req, http.StatusBadRequest)
diff --git a/tests/integration/api_user_email_test.go b/tests/integration/api_user_email_test.go
index 6441e2ed8e..5b6f0708ea 100644
--- a/tests/integration/api_user_email_test.go
+++ b/tests/integration/api_user_email_test.go
@@ -28,7 +28,7 @@ func TestAPIListEmails(t *testing.T) {
var emails []*api.Email
DecodeJSON(t, resp, &emails)
- assert.EqualValues(t, []*api.Email{
+ assert.Equal(t, []*api.Email{
{
Email: "user2@example.com",
Verified: true,
@@ -66,7 +66,7 @@ func TestAPIAddEmail(t *testing.T) {
var emails []*api.Email
DecodeJSON(t, resp, &emails)
- assert.EqualValues(t, []*api.Email{
+ assert.Equal(t, []*api.Email{
{
Email: "user2@example.com",
Verified: true,
@@ -119,7 +119,7 @@ func TestAPIDeleteEmail(t *testing.T) {
var emails []*api.Email
DecodeJSON(t, resp, &emails)
- assert.EqualValues(t, []*api.Email{
+ assert.Equal(t, []*api.Email{
{
Email: "user2@example.com",
Verified: true,
diff --git a/tests/integration/api_user_follow_test.go b/tests/integration/api_user_follow_test.go
index fe20af6769..6cb31a6802 100644
--- a/tests/integration/api_user_follow_test.go
+++ b/tests/integration/api_user_follow_test.go
@@ -32,7 +32,7 @@ func TestAPIFollow(t *testing.T) {
t.Run("Follow", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/following/%s", user1)).
+ req := NewRequest(t, "PUT", "/api/v1/user/following/"+user1).
AddTokenAuth(token2)
MakeRequest(t, req, http.StatusNoContent)
@@ -110,11 +110,11 @@ func TestAPIFollow(t *testing.T) {
t.Run("CheckMyFollowing", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/following/%s", user1)).
+ req := NewRequest(t, "GET", "/api/v1/user/following/"+user1).
AddTokenAuth(token2)
MakeRequest(t, req, http.StatusNoContent)
- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/following/%s", user2)).
+ req = NewRequest(t, "GET", "/api/v1/user/following/"+user2).
AddTokenAuth(token1)
MakeRequest(t, req, http.StatusNotFound)
})
@@ -122,7 +122,7 @@ func TestAPIFollow(t *testing.T) {
t.Run("Unfollow", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/following/%s", user1)).
+ req := NewRequest(t, "DELETE", "/api/v1/user/following/"+user1).
AddTokenAuth(token2)
MakeRequest(t, req, http.StatusNoContent)
})
diff --git a/tests/integration/api_user_info_test.go b/tests/integration/api_user_info_test.go
index 89f7266859..06353eabe0 100644
--- a/tests/integration/api_user_info_test.go
+++ b/tests/integration/api_user_info_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "fmt"
"net/http"
"testing"
@@ -31,7 +30,7 @@ func TestAPIUserInfo(t *testing.T) {
t.Run("GetInfo", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/%s", user2)).
+ req := NewRequest(t, "GET", "/api/v1/users/"+user2).
AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
@@ -39,17 +38,17 @@ func TestAPIUserInfo(t *testing.T) {
DecodeJSON(t, resp, &u)
assert.Equal(t, user2, u.UserName)
- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/%s", user2))
+ req = NewRequest(t, "GET", "/api/v1/users/"+user2)
MakeRequest(t, req, http.StatusNotFound)
// test if the placaholder Mail is returned if a User is not logged in
- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/%s", org3.Name))
+ req = NewRequest(t, "GET", "/api/v1/users/"+org3.Name)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &u)
assert.Equal(t, org3.GetPlaceholderEmail(), u.Email)
// Test if the correct Mail is returned if a User is logged in
- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/%s", org3.Name)).
+ req = NewRequest(t, "GET", "/api/v1/users/"+org3.Name).
AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &u)
diff --git a/tests/integration/api_user_search_test.go b/tests/integration/api_user_search_test.go
index 5604a14259..97264969eb 100644
--- a/tests/integration/api_user_search_test.go
+++ b/tests/integration/api_user_search_test.go
@@ -66,7 +66,7 @@ func TestAPIUserSearchNotLoggedIn(t *testing.T) {
for _, user := range results.Data {
assert.Contains(t, user.UserName, query)
modelUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: user.ID})
- assert.EqualValues(t, modelUser.GetPlaceholderEmail(), user.Email)
+ assert.Equal(t, modelUser.GetPlaceholderEmail(), user.Email)
}
}
@@ -85,8 +85,8 @@ func TestAPIUserSearchSystemUsers(t *testing.T) {
assert.NotEmpty(t, results.Data)
if assert.Len(t, results.Data, 1) {
user := results.Data[0]
- assert.EqualValues(t, user.UserName, systemUser.Name)
- assert.EqualValues(t, user.ID, systemUser.ID)
+ assert.Equal(t, user.UserName, systemUser.Name)
+ assert.Equal(t, user.ID, systemUser.ID)
}
})
}
@@ -108,7 +108,7 @@ func TestAPIUserSearchAdminLoggedInUserHidden(t *testing.T) {
for _, user := range results.Data {
assert.Contains(t, user.UserName, query)
assert.NotEmpty(t, user.Email)
- assert.EqualValues(t, "private", user.Visibility)
+ assert.Equal(t, "private", user.Visibility)
}
}
diff --git a/tests/integration/api_user_secrets_test.go b/tests/integration/api_user_secrets_test.go
index 56bf30e804..10024ac090 100644
--- a/tests/integration/api_user_secrets_test.go
+++ b/tests/integration/api_user_secrets_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "fmt"
"net/http"
"testing"
@@ -55,7 +54,7 @@ func TestAPIUserSecrets(t *testing.T) {
}
for _, c := range cases {
- req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/user/actions/secrets/%s", c.Name), api.CreateOrUpdateSecretOption{
+ req := NewRequestWithJSON(t, "PUT", "/api/v1/user/actions/secrets/"+c.Name, api.CreateOrUpdateSecretOption{
Data: "data",
}).AddTokenAuth(token)
MakeRequest(t, req, c.ExpectedStatus)
@@ -64,7 +63,7 @@ func TestAPIUserSecrets(t *testing.T) {
t.Run("Update", func(t *testing.T) {
name := "update_secret"
- url := fmt.Sprintf("/api/v1/user/actions/secrets/%s", name)
+ url := "/api/v1/user/actions/secrets/" + name
req := NewRequestWithJSON(t, "PUT", url, api.CreateOrUpdateSecretOption{
Data: "initial",
@@ -79,7 +78,7 @@ func TestAPIUserSecrets(t *testing.T) {
t.Run("Delete", func(t *testing.T) {
name := "delete_secret"
- url := fmt.Sprintf("/api/v1/user/actions/secrets/%s", name)
+ url := "/api/v1/user/actions/secrets/" + name
req := NewRequestWithJSON(t, "PUT", url, api.CreateOrUpdateSecretOption{
Data: "initial",
diff --git a/tests/integration/api_user_star_test.go b/tests/integration/api_user_star_test.go
index 0062889a92..989e7ab1d1 100644
--- a/tests/integration/api_user_star_test.go
+++ b/tests/integration/api_user_star_test.go
@@ -11,7 +11,9 @@ import (
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@@ -30,13 +32,13 @@ func TestAPIStar(t *testing.T) {
t.Run("Star", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/starred/%s", repo)).
+ req := NewRequest(t, "PUT", "/api/v1/user/starred/"+repo).
AddTokenAuth(tokenWithUserScope)
MakeRequest(t, req, http.StatusNoContent)
// blocked user can't star a repo
user34 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 34})
- req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/starred/%s", repo)).
+ req = NewRequest(t, "PUT", "/api/v1/user/starred/"+repo).
AddTokenAuth(getUserToken(t, user34.Name, auth_model.AccessTokenScopeWriteRepository))
MakeRequest(t, req, http.StatusForbidden)
})
@@ -74,11 +76,11 @@ func TestAPIStar(t *testing.T) {
t.Run("IsStarring", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s", repo)).
+ req := NewRequest(t, "GET", "/api/v1/user/starred/"+repo).
AddTokenAuth(tokenWithUserScope)
MakeRequest(t, req, http.StatusNoContent)
- req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s", repo+"notexisting")).
+ req = NewRequest(t, "GET", "/api/v1/user/starred/"+repo+"notexisting").
AddTokenAuth(tokenWithUserScope)
MakeRequest(t, req, http.StatusNotFound)
})
@@ -86,8 +88,70 @@ func TestAPIStar(t *testing.T) {
t.Run("Unstar", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
- req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/starred/%s", repo)).
+ req := NewRequest(t, "DELETE", "/api/v1/user/starred/"+repo).
AddTokenAuth(tokenWithUserScope)
MakeRequest(t, req, http.StatusNoContent)
})
}
+
+func TestAPIStarDisabled(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ user := "user1"
+ repo := "user2/repo1"
+
+ session := loginUser(t, user)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser)
+ tokenWithUserScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteRepository)
+
+ defer test.MockVariableValue(&setting.Repository.DisableStars, true)()
+
+ t.Run("Star", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequest(t, "PUT", "/api/v1/user/starred/"+repo).
+ AddTokenAuth(tokenWithUserScope)
+ MakeRequest(t, req, http.StatusForbidden)
+
+ user34 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 34})
+ req = NewRequest(t, "PUT", "/api/v1/user/starred/"+repo).
+ AddTokenAuth(getUserToken(t, user34.Name, auth_model.AccessTokenScopeWriteRepository))
+ MakeRequest(t, req, http.StatusForbidden)
+ })
+
+ t.Run("GetStarredRepos", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/%s/starred", user)).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusForbidden)
+ })
+
+ t.Run("GetMyStarredRepos", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequest(t, "GET", "/api/v1/user/starred").
+ AddTokenAuth(tokenWithUserScope)
+ MakeRequest(t, req, http.StatusForbidden)
+ })
+
+ t.Run("IsStarring", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequest(t, "GET", "/api/v1/user/starred/"+repo).
+ AddTokenAuth(tokenWithUserScope)
+ MakeRequest(t, req, http.StatusForbidden)
+
+ req = NewRequest(t, "GET", "/api/v1/user/starred/"+repo+"notexisting").
+ AddTokenAuth(tokenWithUserScope)
+ MakeRequest(t, req, http.StatusForbidden)
+ })
+
+ t.Run("Unstar", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequest(t, "DELETE", "/api/v1/user/starred/"+repo).
+ AddTokenAuth(tokenWithUserScope)
+ MakeRequest(t, req, http.StatusForbidden)
+ })
+}
diff --git a/tests/integration/api_user_variables_test.go b/tests/integration/api_user_variables_test.go
index 9fd84ddf81..d430c9e21d 100644
--- a/tests/integration/api_user_variables_test.go
+++ b/tests/integration/api_user_variables_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "fmt"
"net/http"
"testing"
@@ -30,11 +29,11 @@ func TestAPIUserVariables(t *testing.T) {
},
{
Name: "_",
- ExpectedStatus: http.StatusNoContent,
+ ExpectedStatus: http.StatusCreated,
},
{
Name: "TEST_VAR",
- ExpectedStatus: http.StatusNoContent,
+ ExpectedStatus: http.StatusCreated,
},
{
Name: "test_var",
@@ -63,7 +62,7 @@ func TestAPIUserVariables(t *testing.T) {
}
for _, c := range cases {
- req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/actions/variables/%s", c.Name), api.CreateVariableOption{
+ req := NewRequestWithJSON(t, "POST", "/api/v1/user/actions/variables/"+c.Name, api.CreateVariableOption{
Value: "value",
}).AddTokenAuth(token)
MakeRequest(t, req, c.ExpectedStatus)
@@ -72,11 +71,11 @@ func TestAPIUserVariables(t *testing.T) {
t.Run("UpdateUserVariable", func(t *testing.T) {
variableName := "test_update_var"
- url := fmt.Sprintf("/api/v1/user/actions/variables/%s", variableName)
+ url := "/api/v1/user/actions/variables/" + variableName
req := NewRequestWithJSON(t, "POST", url, api.CreateVariableOption{
Value: "initial_val",
}).AddTokenAuth(token)
- MakeRequest(t, req, http.StatusNoContent)
+ MakeRequest(t, req, http.StatusCreated)
cases := []struct {
Name string
@@ -118,7 +117,7 @@ func TestAPIUserVariables(t *testing.T) {
}
for _, c := range cases {
- req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/user/actions/variables/%s", c.Name), api.UpdateVariableOption{
+ req := NewRequestWithJSON(t, "PUT", "/api/v1/user/actions/variables/"+c.Name, api.UpdateVariableOption{
Name: c.UpdateName,
Value: "updated_val",
}).AddTokenAuth(token)
@@ -128,12 +127,12 @@ func TestAPIUserVariables(t *testing.T) {
t.Run("DeleteRepoVariable", func(t *testing.T) {
variableName := "test_delete_var"
- url := fmt.Sprintf("/api/v1/user/actions/variables/%s", variableName)
+ url := "/api/v1/user/actions/variables/" + variableName
req := NewRequestWithJSON(t, "POST", url, api.CreateVariableOption{
Value: "initial_val",
}).AddTokenAuth(token)
- MakeRequest(t, req, http.StatusNoContent)
+ MakeRequest(t, req, http.StatusCreated)
req = NewRequest(t, "DELETE", url).AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)
diff --git a/tests/integration/api_wiki_test.go b/tests/integration/api_wiki_test.go
index 05d90fc4e3..8e5f67e282 100644
--- a/tests/integration/api_wiki_test.go
+++ b/tests/integration/api_wiki_test.go
@@ -172,6 +172,19 @@ func TestAPIListWikiPages(t *testing.T) {
assert.Equal(t, dummymeta, meta)
}
+func testAPICreateWikiPage(t *testing.T, session *TestSession, userName, repoName, title string, status int) {
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/new", userName, repoName)
+
+ req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateWikiPageOptions{
+ Title: title,
+ ContentBase64: base64.StdEncoding.EncodeToString([]byte("Wiki page content for API unit tests")),
+ Message: "",
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, status)
+}
+
func TestAPINewWikiPage(t *testing.T) {
for _, title := range []string{
"New page",
@@ -180,16 +193,7 @@ func TestAPINewWikiPage(t *testing.T) {
defer tests.PrepareTestEnv(t)()
username := "user2"
session := loginUser(t, username)
- token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
-
- urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/new", username, "repo1")
-
- req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateWikiPageOptions{
- Title: title,
- ContentBase64: base64.StdEncoding.EncodeToString([]byte("Wiki page content for API unit tests")),
- Message: "",
- }).AddTokenAuth(token)
- MakeRequest(t, req, http.StatusCreated)
+ testAPICreateWikiPage(t, session, username, "repo1", title, http.StatusCreated)
}
}
diff --git a/tests/integration/auth_ldap_test.go b/tests/integration/auth_ldap_test.go
index 5c50fd0288..24f0c03bed 100644
--- a/tests/integration/auth_ldap_test.go
+++ b/tests/integration/auth_ldap_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "context"
"net/http"
"os"
"strings"
@@ -16,6 +15,7 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/optional"
+ "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/util"
@@ -238,7 +238,7 @@ func TestLDAPUserSync(t *testing.T) {
defer tests.PrepareTestEnv(t)()
te.addAuthSource(t)
- err := auth.SyncExternalUsers(context.Background(), true)
+ err := auth.SyncExternalUsers(t.Context(), true)
assert.NoError(t, err)
// Check if users exists
@@ -279,7 +279,7 @@ func TestLDAPUserSyncWithEmptyUsernameAttribute(t *testing.T) {
htmlDoc := NewHTMLParser(t, resp.Body)
- tr := htmlDoc.doc.Find("table.table tbody tr")
+ tr := htmlDoc.doc.Find("table.table tbody tr:not(.no-results-row)")
assert.Equal(t, 0, tr.Length())
}
@@ -292,7 +292,7 @@ func TestLDAPUserSyncWithEmptyUsernameAttribute(t *testing.T) {
MakeRequest(t, req, http.StatusSeeOther)
}
- require.NoError(t, auth.SyncExternalUsers(context.Background(), true))
+ require.NoError(t, auth.SyncExternalUsers(t.Context(), true))
authSource := unittest.AssertExistsAndLoadBean(t, &auth_model.Source{
Name: payload["name"],
@@ -328,7 +328,7 @@ func TestLDAPUserSyncWithGroupFilter(t *testing.T) {
u := te.otherLDAPUsers[0]
testLoginFailed(t, u.UserName, u.Password, translation.NewLocale("en-US").TrString("form.username_password_incorrect"))
- require.NoError(t, auth.SyncExternalUsers(context.Background(), true))
+ require.NoError(t, auth.SyncExternalUsers(t.Context(), true))
// Assert members of LDAP group "cn=git" are added
for _, gitLDAPUser := range te.gitLDAPUsers {
@@ -351,7 +351,7 @@ func TestLDAPUserSyncWithGroupFilter(t *testing.T) {
ldapConfig.GroupFilter = "(cn=ship_crew)"
require.NoError(t, auth_model.UpdateSource(db.DefaultContext, ldapSource))
- require.NoError(t, auth.SyncExternalUsers(context.Background(), true))
+ require.NoError(t, auth.SyncExternalUsers(t.Context(), true))
for _, gitLDAPUser := range te.gitLDAPUsers {
if gitLDAPUser.UserName == "fry" || gitLDAPUser.UserName == "leela" || gitLDAPUser.UserName == "bender" {
@@ -392,7 +392,7 @@ func TestLDAPUserSSHKeySync(t *testing.T) {
defer tests.PrepareTestEnv(t)()
te.addAuthSource(t, ldapAuthOptions{attributeSSHPublicKey: "sshPublicKey"})
- require.NoError(t, auth.SyncExternalUsers(context.Background(), true))
+ require.NoError(t, auth.SyncExternalUsers(t.Context(), true))
// Check if users has SSH keys synced
for _, u := range te.gitLDAPUsers {
@@ -432,14 +432,14 @@ func TestLDAPGroupTeamSyncAddMember(t *testing.T) {
assert.NoError(t, err)
team, err := organization.GetTeam(db.DefaultContext, org.ID, "team11")
assert.NoError(t, err)
- require.NoError(t, auth.SyncExternalUsers(context.Background(), true))
+ require.NoError(t, auth.SyncExternalUsers(t.Context(), true))
for _, gitLDAPUser := range te.gitLDAPUsers {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{
Name: gitLDAPUser.UserName,
})
usersOrgs, err := db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
- UserID: user.ID,
- IncludePrivate: true,
+ UserID: user.ID,
+ IncludeVisibility: structs.VisibleTypePrivate,
})
assert.NoError(t, err)
allOrgTeams, err := organization.GetUserOrgTeams(db.DefaultContext, org.ID, user.ID)
diff --git a/tests/integration/benchmarks_test.go b/tests/integration/benchmarks_test.go
deleted file mode 100644
index 62da761d2d..0000000000
--- a/tests/integration/benchmarks_test.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package integration
-
-import (
- "math/rand/v2"
- "net/http"
- "net/url"
- "testing"
-
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- api "code.gitea.io/gitea/modules/structs"
-)
-
-// StringWithCharset random string (from https://www.calhoun.io/creating-random-strings-in-go/)
-func StringWithCharset(length int, charset string) string {
- b := make([]byte, length)
- for i := range b {
- b[i] = charset[rand.IntN(len(charset))]
- }
- return string(b)
-}
-
-func BenchmarkRepoBranchCommit(b *testing.B) {
- onGiteaRun(b, func(b *testing.B, u *url.URL) {
- samples := []int64{1, 2, 3}
- b.ResetTimer()
-
- for _, repoID := range samples {
- b.StopTimer()
- repo := unittest.AssertExistsAndLoadBean(b, &repo_model.Repository{ID: repoID})
- b.StartTimer()
- b.Run(repo.Name, func(b *testing.B) {
- session := loginUser(b, "user2")
- b.ResetTimer()
- b.Run("CreateBranch", func(b *testing.B) {
- b.StopTimer()
- branchName := StringWithCharset(5+rand.IntN(10), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- b.Run("new_"+branchName, func(b *testing.B) {
- b.Skip("benchmark broken") // TODO fix
- testAPICreateBranch(b, session, repo.OwnerName, repo.Name, repo.DefaultBranch, "new_"+branchName, http.StatusCreated)
- })
- }
- })
- b.Run("GetBranches", func(b *testing.B) {
- req := NewRequestf(b, "GET", "/api/v1/repos/%s/branches", repo.FullName())
- session.MakeRequest(b, req, http.StatusOK)
- })
- b.Run("AccessCommits", func(b *testing.B) {
- var branches []*api.Branch
- req := NewRequestf(b, "GET", "/api/v1/repos/%s/branches", repo.FullName())
- resp := session.MakeRequest(b, req, http.StatusOK)
- DecodeJSON(b, resp, &branches)
- b.ResetTimer() // We measure from here
- if len(branches) != 0 {
- for i := 0; i < b.N; i++ {
- req := NewRequestf(b, "GET", "/api/v1/repos/%s/commits?sha=%s", repo.FullName(), branches[i%len(branches)].Name)
- session.MakeRequest(b, req, http.StatusOK)
- }
- }
- })
- })
- }
- })
-}
diff --git a/tests/integration/change_default_branch_test.go b/tests/integration/change_default_branch_test.go
index 729eb1e4ce..9b61cff9fd 100644
--- a/tests/integration/change_default_branch_test.go
+++ b/tests/integration/change_default_branch_test.go
@@ -6,12 +6,16 @@ package integration
import (
"fmt"
"net/http"
+ "strconv"
"testing"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/tests"
+
+ "github.com/stretchr/testify/assert"
)
func TestChangeDefaultBranch(t *testing.T) {
@@ -38,3 +42,96 @@ func TestChangeDefaultBranch(t *testing.T) {
})
session.MakeRequest(t, req, http.StatusNotFound)
}
+
+func checkDivergence(t *testing.T, session *TestSession, branchesURL, expectedDefaultBranch string, expectedBranchToDivergence map[string]git.DivergeObject) {
+ req := NewRequest(t, "GET", branchesURL)
+ resp := session.MakeRequest(t, req, http.StatusOK)
+
+ htmlDoc := NewHTMLParser(t, resp.Body)
+
+ branchNodes := htmlDoc.doc.Find(".branch-name").Nodes
+ branchNames := []string{}
+ for _, node := range branchNodes {
+ branchNames = append(branchNames, node.FirstChild.Data)
+ }
+
+ expectBranchCount := len(expectedBranchToDivergence)
+
+ assert.Len(t, branchNames, expectBranchCount+1)
+ assert.Equal(t, expectedDefaultBranch, branchNames[0])
+
+ allCountBehindNodes := htmlDoc.doc.Find(".count-behind").Nodes
+ allCountAheadNodes := htmlDoc.doc.Find(".count-ahead").Nodes
+
+ assert.Len(t, allCountAheadNodes, expectBranchCount)
+ assert.Len(t, allCountBehindNodes, expectBranchCount)
+
+ for i := range expectBranchCount {
+ branchName := branchNames[i+1]
+ assert.Contains(t, expectedBranchToDivergence, branchName)
+
+ expectedCountAhead := expectedBranchToDivergence[branchName].Ahead
+ expectedCountBehind := expectedBranchToDivergence[branchName].Behind
+ countAhead, err := strconv.Atoi(allCountAheadNodes[i].FirstChild.Data)
+ assert.NoError(t, err)
+ countBehind, err := strconv.Atoi(allCountBehindNodes[i].FirstChild.Data)
+ assert.NoError(t, err)
+
+ assert.Equal(t, expectedCountAhead, countAhead)
+ assert.Equal(t, expectedCountBehind, countBehind)
+ }
+}
+
+func TestChangeDefaultBranchDivergence(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16})
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+
+ session := loginUser(t, owner.Name)
+ branchesURL := fmt.Sprintf("/%s/%s/branches", owner.Name, repo.Name)
+ settingsBranchesURL := fmt.Sprintf("/%s/%s/settings/branches", owner.Name, repo.Name)
+
+ // check branch divergence before switching default branch
+ expectedBranchToDivergenceBefore := map[string]git.DivergeObject{
+ "not-signed": {
+ Ahead: 0,
+ Behind: 0,
+ },
+ "good-sign-not-yet-validated": {
+ Ahead: 0,
+ Behind: 1,
+ },
+ "good-sign": {
+ Ahead: 1,
+ Behind: 3,
+ },
+ }
+ checkDivergence(t, session, branchesURL, "master", expectedBranchToDivergenceBefore)
+
+ // switch default branch
+ newDefaultBranch := "good-sign-not-yet-validated"
+ csrf := GetUserCSRFToken(t, session)
+ req := NewRequestWithValues(t, "POST", settingsBranchesURL, map[string]string{
+ "_csrf": csrf,
+ "action": "default_branch",
+ "branch": newDefaultBranch,
+ })
+ session.MakeRequest(t, req, http.StatusSeeOther)
+
+ // check branch divergence after switching default branch
+ expectedBranchToDivergenceAfter := map[string]git.DivergeObject{
+ "master": {
+ Ahead: 1,
+ Behind: 0,
+ },
+ "not-signed": {
+ Ahead: 1,
+ Behind: 0,
+ },
+ "good-sign": {
+ Ahead: 1,
+ Behind: 2,
+ },
+ }
+ checkDivergence(t, session, branchesURL, newDefaultBranch, expectedBranchToDivergenceAfter)
+}
diff --git a/tests/integration/cmd_keys_test.go b/tests/integration/cmd_keys_test.go
index 61f11c58b0..3878302ef0 100644
--- a/tests/integration/cmd_keys_test.go
+++ b/tests/integration/cmd_keys_test.go
@@ -13,7 +13,7 @@ import (
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
- "github.com/urfave/cli/v2"
+ "github.com/urfave/cli/v3"
)
func Test_CmdKeys(t *testing.T) {
@@ -36,18 +36,21 @@ func Test_CmdKeys(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- out := new(bytes.Buffer)
- app := cli.NewApp()
- app.Writer = out
- app.Commands = []*cli.Command{cmd.CmdKeys}
+ var stdout, stderr bytes.Buffer
+ app := &cli.Command{
+ Writer: &stdout,
+ ErrWriter: &stderr,
+ Commands: []*cli.Command{cmd.CmdKeys},
+ }
cmd.CmdKeys.HideHelp = true
- err := app.Run(append([]string{"prog"}, tt.args...))
+ err := app.Run(t.Context(), append([]string{"prog"}, tt.args...))
if tt.wantErr {
assert.Error(t, err)
+ assert.Equal(t, tt.expectedOutput, stderr.String())
} else {
assert.NoError(t, err)
+ assert.Equal(t, tt.expectedOutput, stdout.String())
}
- assert.Equal(t, tt.expectedOutput, out.String())
})
}
})
diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go
index cbf927813e..0648777fed 100644
--- a/tests/integration/compare_test.go
+++ b/tests/integration/compare_test.go
@@ -133,7 +133,7 @@ func TestCompareCodeExpand(t *testing.T) {
Readme: "Default",
AutoInit: true,
DefaultBranch: "main",
- })
+ }, true)
assert.NoError(t, err)
session := loginUser(t, user1.Name)
diff --git a/tests/integration/db_collation_test.go b/tests/integration/db_collation_test.go
index acec4aa5d1..339bfce71c 100644
--- a/tests/integration/db_collation_test.go
+++ b/tests/integration/db_collation_test.go
@@ -75,7 +75,7 @@ func TestDatabaseCollation(t *testing.T) {
defer test.MockVariableValue(&setting.Database.CharsetCollation, "utf8mb4_bin")()
r, err := db.CheckCollations(x)
assert.NoError(t, err)
- assert.EqualValues(t, "utf8mb4_bin", r.ExpectedCollation)
+ assert.Equal(t, "utf8mb4_bin", r.ExpectedCollation)
assert.NoError(t, db.ConvertDatabaseTable())
r, err = db.CheckCollations(x)
assert.NoError(t, err)
diff --git a/tests/integration/delete_user_test.go b/tests/integration/delete_user_test.go
index ad3c882882..4b02c4725a 100644
--- a/tests/integration/delete_user_test.go
+++ b/tests/integration/delete_user_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "fmt"
"net/http"
"testing"
@@ -34,7 +33,7 @@ func TestUserDeleteAccount(t *testing.T) {
session := loginUser(t, "user8")
csrf := GetUserCSRFToken(t, session)
- urlStr := fmt.Sprintf("/user/settings/account/delete?password=%s", userPassword)
+ urlStr := "/user/settings/account/delete?password=" + userPassword
req := NewRequestWithValues(t, "POST", urlStr, map[string]string{
"_csrf": csrf,
})
@@ -49,7 +48,7 @@ func TestUserDeleteAccountStillOwnRepos(t *testing.T) {
session := loginUser(t, "user2")
csrf := GetUserCSRFToken(t, session)
- urlStr := fmt.Sprintf("/user/settings/account/delete?password=%s", userPassword)
+ urlStr := "/user/settings/account/delete?password=" + userPassword
req := NewRequestWithValues(t, "POST", urlStr, map[string]string{
"_csrf": csrf,
})
diff --git a/tests/integration/dump_restore_test.go b/tests/integration/dump_restore_test.go
index 47bb6f76e9..d2d43075c3 100644
--- a/tests/integration/dump_restore_test.go
+++ b/tests/integration/dump_restore_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "context"
"errors"
"fmt"
"net/url"
@@ -21,7 +20,6 @@ import (
base "code.gitea.io/gitea/modules/migration"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/migrations"
"github.com/stretchr/testify/assert"
@@ -44,10 +42,7 @@ func TestDumpRestore(t *testing.T) {
reponame := "repo1"
- basePath, err := os.MkdirTemp("", reponame)
- assert.NoError(t, err)
- defer util.RemoveAll(basePath)
-
+ basePath := t.TempDir()
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame})
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
session := loginUser(t, repoOwner.Name)
@@ -57,7 +52,7 @@ func TestDumpRestore(t *testing.T) {
// Phase 1: dump repo1 from the Gitea instance to the filesystem
//
- ctx := context.Background()
+ ctx := t.Context()
opts := migrations.MigrateOptions{
GitServiceType: structs.GiteaService,
Issues: true,
@@ -66,10 +61,10 @@ func TestDumpRestore(t *testing.T) {
Milestones: true,
Comments: true,
AuthToken: token,
- CloneAddr: repo.CloneLink().HTTPS,
+ CloneAddr: repo.CloneLinkGeneral(t.Context()).HTTPS,
RepoName: reponame,
}
- err = migrations.DumpRepository(ctx, basePath, repoOwner.Name, opts)
+ err := migrations.DumpRepository(ctx, basePath, repoOwner.Name, opts)
assert.NoError(t, err)
//
@@ -96,7 +91,7 @@ func TestDumpRestore(t *testing.T) {
// Phase 3: dump restored from the Gitea instance to the filesystem
//
opts.RepoName = newreponame
- opts.CloneAddr = newrepo.CloneLink().HTTPS
+ opts.CloneAddr = newrepo.CloneLinkGeneral(t.Context()).HTTPS
err = migrations.DumpRepository(ctx, basePath, repoOwner.Name, opts)
assert.NoError(t, err)
@@ -183,7 +178,7 @@ func (c *compareDump) assertEquals(repoBefore, repoAfter *repo_model.Repository)
}).([]*base.Comment)
assert.True(c.t, ok)
for _, comment := range comments {
- assert.EqualValues(c.t, issue.Number, comment.IssueIndex)
+ assert.Equal(c.t, issue.Number, comment.IssueIndex)
}
}
@@ -210,7 +205,7 @@ func (c *compareDump) assertEquals(repoBefore, repoAfter *repo_model.Repository)
comments, ok := c.assertEqual(filename, []base.Comment{}, compareFields{}).([]*base.Comment)
assert.True(c.t, ok)
for _, comment := range comments {
- assert.EqualValues(c.t, pr.Number, comment.IssueIndex)
+ assert.Equal(c.t, pr.Number, comment.IssueIndex)
}
}
}
@@ -218,7 +213,7 @@ func (c *compareDump) assertEquals(repoBefore, repoAfter *repo_model.Repository)
func (c *compareDump) assertLoadYAMLFiles(beforeFilename, afterFilename string, before, after any) {
_, beforeErr := os.Stat(beforeFilename)
_, afterErr := os.Stat(afterFilename)
- assert.EqualValues(c.t, errors.Is(beforeErr, os.ErrNotExist), errors.Is(afterErr, os.ErrNotExist))
+ assert.Equal(c.t, errors.Is(beforeErr, os.ErrNotExist), errors.Is(afterErr, os.ErrNotExist))
if errors.Is(beforeErr, os.ErrNotExist) {
return
}
@@ -270,7 +265,7 @@ func (c *compareDump) assertEqual(filename string, kind any, fields compareField
}
func (c *compareDump) assertEqualSlices(before, after reflect.Value, fields compareFields) any {
- assert.EqualValues(c.t, before.Len(), after.Len())
+ assert.Equal(c.t, before.Len(), after.Len())
if before.Len() == after.Len() {
for i := 0; i < before.Len(); i++ {
_ = c.assertEqualValues(
@@ -303,15 +298,15 @@ func (c *compareDump) assertEqualValues(before, after reflect.Value, fields comp
assert.True(c.t, ok)
as, ok := ai.(string)
assert.True(c.t, ok)
- assert.EqualValues(c.t, compare.transform(bs), compare.transform(as))
+ assert.Equal(c.t, compare.transform(bs), compare.transform(as))
continue
}
if compare.before != nil && compare.after != nil {
//
// The fields are expected to have different values
//
- assert.EqualValues(c.t, compare.before, bi)
- assert.EqualValues(c.t, compare.after, ai)
+ assert.Equal(c.t, compare.before, bi)
+ assert.Equal(c.t, compare.after, ai)
continue
}
if compare.nested != nil {
@@ -322,7 +317,7 @@ func (c *compareDump) assertEqualValues(before, after reflect.Value, fields comp
continue
}
}
- assert.EqualValues(c.t, bi, ai)
+ assert.Equal(c.t, bi, ai)
}
return after.Interface()
}
diff --git a/tests/integration/editor_test.go b/tests/integration/editor_test.go
index f0f71b80d1..ac47ed0094 100644
--- a/tests/integration/editor_test.go
+++ b/tests/integration/editor_test.go
@@ -4,175 +4,452 @@
package integration
import (
+ "bytes"
"fmt"
+ "io"
+ "maps"
+ "mime/multipart"
"net/http"
"net/http/httptest"
"net/url"
"path"
+ "strings"
"testing"
- "code.gitea.io/gitea/modules/json"
- gitea_context "code.gitea.io/gitea/services/context"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/test"
+ "code.gitea.io/gitea/modules/translation"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
-func TestCreateFile(t *testing.T) {
+func TestEditor(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
- session := loginUser(t, "user2")
- testCreateFile(t, session, "user2", "repo1", "master", "test.txt", "Content")
+ sessionUser2 := loginUser(t, "user2")
+ t.Run("EditFileNotAllowed", testEditFileNotAllowed)
+ t.Run("DiffPreview", testEditorDiffPreview)
+ t.Run("CreateFile", testEditorCreateFile)
+ t.Run("EditFile", func(t *testing.T) {
+ testEditFile(t, sessionUser2, "user2", "repo1", "master", "README.md", "Hello, World (direct)\n")
+ testEditFileToNewBranch(t, sessionUser2, "user2", "repo1", "master", "feature/test", "README.md", "Hello, World (commit-to-new-branch)\n")
+ })
+ t.Run("PatchFile", testEditorPatchFile)
+ t.Run("DeleteFile", func(t *testing.T) {
+ viewLink := "/user2/repo1/src/branch/branch2/README.md"
+ sessionUser2.MakeRequest(t, NewRequest(t, "GET", viewLink), http.StatusOK)
+ testEditorActionPostRequest(t, sessionUser2, "/user2/repo1/_delete/branch2/README.md", map[string]string{"commit_choice": "direct"})
+ sessionUser2.MakeRequest(t, NewRequest(t, "GET", viewLink), http.StatusNotFound)
+ })
+ t.Run("ForkToEditFile", func(t *testing.T) {
+ testForkToEditFile(t, loginUser(t, "user4"), "user4", "user2", "repo1", "master", "README.md")
+ })
+ t.Run("WebGitCommitEmail", testEditorWebGitCommitEmail)
+ t.Run("ProtectedBranch", testEditorProtectedBranch)
})
}
-func testCreateFile(t *testing.T, session *TestSession, user, repo, branch, filePath, content string) *httptest.ResponseRecorder {
- // Request editor page
- newURL := fmt.Sprintf("/%s/%s/_new/%s/", user, repo, branch)
- req := NewRequest(t, "GET", newURL)
- resp := session.MakeRequest(t, req, http.StatusOK)
-
- doc := NewHTMLParser(t, resp.Body)
- lastCommit := doc.GetInputValueByName("last_commit")
- assert.NotEmpty(t, lastCommit)
+func testEditorCreateFile(t *testing.T) {
+ session := loginUser(t, "user2")
+ testCreateFile(t, session, "user2", "repo1", "master", "test.txt", "Content")
+ testEditorActionPostRequestError(t, session, "/user2/repo1/_new/master/", map[string]string{
+ "tree_path": "test.txt",
+ "commit_choice": "direct",
+ "new_branch_name": "master",
+ }, `A file named "test.txt" already exists in this repository.`)
+ testEditorActionPostRequestError(t, session, "/user2/repo1/_new/master/", map[string]string{
+ "tree_path": "test.txt",
+ "commit_choice": "commit-to-new-branch",
+ "new_branch_name": "master",
+ }, `Branch "master" already exists in this repository.`)
+}
- // Save new file to master branch
- req = NewRequestWithValues(t, "POST", newURL, map[string]string{
- "_csrf": doc.GetCSRF(),
- "last_commit": lastCommit,
+func testCreateFile(t *testing.T, session *TestSession, user, repo, branch, filePath, content string) {
+ testEditorActionEdit(t, session, user, repo, "_new", branch, "", map[string]string{
"tree_path": filePath,
"content": content,
"commit_choice": "direct",
})
- return session.MakeRequest(t, req, http.StatusSeeOther)
}
-func TestCreateFileOnProtectedBranch(t *testing.T) {
- onGiteaRun(t, func(t *testing.T, u *url.URL) {
- session := loginUser(t, "user2")
-
- csrf := GetUserCSRFToken(t, session)
- // Change master branch to protected
- req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/edit", map[string]string{
- "_csrf": csrf,
- "rule_name": "master",
- "enable_push": "true",
- })
- session.MakeRequest(t, req, http.StatusSeeOther)
- // Check if master branch has been locked successfully
- flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
- assert.NotNil(t, flashCookie)
- assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2522master%2522%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
+func testEditorProtectedBranch(t *testing.T) {
+ session := loginUser(t, "user2")
+ // Change the "master" branch to "protected"
+ req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/edit", map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ "rule_name": "master",
+ "enable_push": "true",
+ })
+ session.MakeRequest(t, req, http.StatusSeeOther)
+ flashMsg := session.GetCookieFlashMessage()
+ assert.Equal(t, `Branch protection for rule "master" has been updated.`, flashMsg.SuccessMsg)
- // Request editor page
- req = NewRequest(t, "GET", "/user2/repo1/_new/master/")
- resp := session.MakeRequest(t, req, http.StatusOK)
+ // Try to commit a file to the "master" branch and it should fail
+ resp := testEditorActionPostRequest(t, session, "/user2/repo1/_new/master/", map[string]string{"tree_path": "test-protected-branch.txt", "commit_choice": "direct"})
+ assert.Equal(t, http.StatusBadRequest, resp.Code)
+ assert.Equal(t, `Cannot commit to protected branch "master".`, test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
+}
- doc := NewHTMLParser(t, resp.Body)
- lastCommit := doc.GetInputValueByName("last_commit")
- assert.NotEmpty(t, lastCommit)
+func testEditorActionPostRequest(t *testing.T, session *TestSession, requestPath string, params map[string]string) *httptest.ResponseRecorder {
+ req := NewRequest(t, "GET", requestPath)
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ form := map[string]string{
+ "_csrf": htmlDoc.GetCSRF(),
+ "last_commit": htmlDoc.GetInputValueByName("last_commit"),
+ }
+ maps.Copy(form, params)
+ req = NewRequestWithValues(t, "POST", requestPath, form)
+ return session.MakeRequest(t, req, NoExpectedStatus)
+}
- // Save new file to master branch
- req = NewRequestWithValues(t, "POST", "/user2/repo1/_new/master/", map[string]string{
- "_csrf": doc.GetCSRF(),
- "last_commit": lastCommit,
- "tree_path": "test.txt",
- "content": "Content",
- "commit_choice": "direct",
- })
+func testEditorActionPostRequestError(t *testing.T, session *TestSession, requestPath string, params map[string]string, errorMessage string) {
+ resp := testEditorActionPostRequest(t, session, requestPath, params)
+ assert.Equal(t, http.StatusBadRequest, resp.Code)
+ assert.Equal(t, errorMessage, test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
+}
- resp = session.MakeRequest(t, req, http.StatusOK)
- // Check body for error message
- assert.Contains(t, resp.Body.String(), "Cannot commit to protected branch &#34;master&#34;.")
+func testEditorActionEdit(t *testing.T, session *TestSession, user, repo, editorAction, branch, filePath string, params map[string]string) *httptest.ResponseRecorder {
+ params["tree_path"] = util.IfZero(params["tree_path"], filePath)
+ newBranchName := util.Iif(params["commit_choice"] == "direct", branch, params["new_branch_name"])
+ resp := testEditorActionPostRequest(t, session, fmt.Sprintf("/%s/%s/%s/%s/%s", user, repo, editorAction, branch, filePath), params)
+ assert.Equal(t, http.StatusOK, resp.Code)
+ assert.NotEmpty(t, test.RedirectURL(resp))
+ req := NewRequest(t, "GET", path.Join(user, repo, "raw/branch", newBranchName, params["tree_path"]))
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ assert.Equal(t, params["content"], resp.Body.String())
+ return resp
+}
- // remove the protected branch
- csrf = GetUserCSRFToken(t, session)
+func testEditFile(t *testing.T, session *TestSession, user, repo, branch, filePath, newContent string) {
+ testEditorActionEdit(t, session, user, repo, "_edit", branch, filePath, map[string]string{
+ "content": newContent,
+ "commit_choice": "direct",
+ })
+}
- // Change master branch to protected
- req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/1/delete", map[string]string{
- "_csrf": csrf,
- })
+func testEditFileToNewBranch(t *testing.T, session *TestSession, user, repo, branch, targetBranch, filePath, newContent string) {
+ testEditorActionEdit(t, session, user, repo, "_edit", branch, filePath, map[string]string{
+ "content": newContent,
+ "commit_choice": "commit-to-new-branch",
+ "new_branch_name": targetBranch,
+ })
+}
- resp = session.MakeRequest(t, req, http.StatusOK)
+func testEditorDiffPreview(t *testing.T) {
+ session := loginUser(t, "user2")
+ req := NewRequestWithValues(t, "POST", "/user2/repo1/_preview/master/README.md", map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ "content": "Hello, World (Edited)\n",
+ })
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ assert.Contains(t, resp.Body.String(), `<span class="added-code">Hello, World (Edited)</span>`)
+}
- res := make(map[string]string)
- assert.NoError(t, json.NewDecoder(resp.Body).Decode(&res))
- assert.EqualValues(t, "/user2/repo1/settings/branches", res["redirect"])
+func testEditorPatchFile(t *testing.T) {
+ session := loginUser(t, "user2")
+ pathContentCommon := `diff --git a/patch-file-1.txt b/patch-file-1.txt
+new file mode 100644
+index 0000000000..aaaaaaaaaa
+--- /dev/null
++++ b/patch-file-1.txt
+@@ -0,0 +1 @@
++`
+ testEditorActionPostRequest(t, session, "/user2/repo1/_diffpatch/master/", map[string]string{
+ "content": pathContentCommon + "patched content\n",
+ "commit_choice": "commit-to-new-branch",
+ "new_branch_name": "patched-branch",
+ })
+ resp := MakeRequest(t, NewRequest(t, "GET", "/user2/repo1/raw/branch/patched-branch/patch-file-1.txt"), http.StatusOK)
+ assert.Equal(t, "patched content\n", resp.Body.String())
- // Check if master branch has been locked successfully
- flashCookie = session.GetCookie(gitea_context.CookieNameFlash)
- assert.NotNil(t, flashCookie)
- assert.EqualValues(t, "error%3DRemoving%2Bbranch%2Bprotection%2Brule%2B%25221%2522%2Bfailed.", flashCookie.Value)
+ // patch again, it should fail
+ resp = testEditorActionPostRequest(t, session, "/user2/repo1/_diffpatch/patched-branch/", map[string]string{
+ "content": pathContentCommon + "another patched content\n",
+ "commit_choice": "commit-to-new-branch",
+ "new_branch_name": "patched-branch-1",
})
+ assert.Equal(t, "Unable to apply patch", test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
}
-func testEditFile(t *testing.T, session *TestSession, user, repo, branch, filePath, newContent string) *httptest.ResponseRecorder {
- // Get to the 'edit this file' page
- req := NewRequest(t, "GET", path.Join(user, repo, "_edit", branch, filePath))
- resp := session.MakeRequest(t, req, http.StatusOK)
+func testEditorWebGitCommitEmail(t *testing.T) {
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ require.True(t, user.KeepEmailPrivate)
- htmlDoc := NewHTMLParser(t, resp.Body)
- lastCommit := htmlDoc.GetInputValueByName("last_commit")
- assert.NotEmpty(t, lastCommit)
-
- // Submit the edits
- req = NewRequestWithValues(t, "POST", path.Join(user, repo, "_edit", branch, filePath),
- map[string]string{
- "_csrf": htmlDoc.GetCSRF(),
- "last_commit": lastCommit,
- "tree_path": filePath,
- "content": newContent,
- "commit_choice": "direct",
- },
- )
- session.MakeRequest(t, req, http.StatusSeeOther)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ gitRepo, _ := git.OpenRepository(git.DefaultContext, repo1.RepoPath())
+ defer gitRepo.Close()
+ getLastCommit := func(t *testing.T) *git.Commit {
+ c, err := gitRepo.GetBranchCommit("master")
+ require.NoError(t, err)
+ return c
+ }
- // Verify the change
- req = NewRequest(t, "GET", path.Join(user, repo, "raw/branch", branch, filePath))
- resp = session.MakeRequest(t, req, http.StatusOK)
- assert.EqualValues(t, newContent, resp.Body.String())
+ session := loginUser(t, user.Name)
- return resp
-}
+ makeReq := func(t *testing.T, link string, params map[string]string, expectedUserName, expectedEmail string) *httptest.ResponseRecorder {
+ lastCommit := getLastCommit(t)
+ params["_csrf"] = GetUserCSRFToken(t, session)
+ params["last_commit"] = lastCommit.ID.String()
+ params["commit_choice"] = "direct"
+ req := NewRequestWithValues(t, "POST", link, params)
+ resp := session.MakeRequest(t, req, NoExpectedStatus)
+ newCommit := getLastCommit(t)
+ if expectedUserName == "" {
+ require.Equal(t, lastCommit.ID.String(), newCommit.ID.String())
+ respErr := test.ParseJSONError(resp.Body.Bytes())
+ assert.Equal(t, translation.NewLocale("en-US").TrString("repo.editor.invalid_commit_email"), respErr.ErrorMessage)
+ } else {
+ require.NotEqual(t, lastCommit.ID.String(), newCommit.ID.String())
+ assert.Equal(t, expectedUserName, newCommit.Author.Name)
+ assert.Equal(t, expectedEmail, newCommit.Author.Email)
+ assert.Equal(t, expectedUserName, newCommit.Committer.Name)
+ assert.Equal(t, expectedEmail, newCommit.Committer.Email)
+ }
+ return resp
+ }
-func testEditFileToNewBranch(t *testing.T, session *TestSession, user, repo, branch, targetBranch, filePath, newContent string) *httptest.ResponseRecorder {
- // Get to the 'edit this file' page
- req := NewRequest(t, "GET", path.Join(user, repo, "_edit", branch, filePath))
- resp := session.MakeRequest(t, req, http.StatusOK)
+ uploadFile := func(t *testing.T, name, content string) string {
+ body := &bytes.Buffer{}
+ uploadForm := multipart.NewWriter(body)
+ file, _ := uploadForm.CreateFormFile("file", name)
+ _, _ = io.Copy(file, strings.NewReader(content))
+ _ = uploadForm.WriteField("_csrf", GetUserCSRFToken(t, session))
+ _ = uploadForm.Close()
- htmlDoc := NewHTMLParser(t, resp.Body)
- lastCommit := htmlDoc.GetInputValueByName("last_commit")
- assert.NotEmpty(t, lastCommit)
-
- // Submit the edits
- req = NewRequestWithValues(t, "POST", path.Join(user, repo, "_edit", branch, filePath),
- map[string]string{
- "_csrf": htmlDoc.GetCSRF(),
- "last_commit": lastCommit,
- "tree_path": filePath,
- "content": newContent,
- "commit_choice": "commit-to-new-branch",
- "new_branch_name": targetBranch,
- },
- )
- session.MakeRequest(t, req, http.StatusSeeOther)
+ req := NewRequestWithBody(t, "POST", "/user2/repo1/upload-file", body)
+ req.Header.Add("Content-Type", uploadForm.FormDataContentType())
+ resp := session.MakeRequest(t, req, http.StatusOK)
- // Verify the change
- req = NewRequest(t, "GET", path.Join(user, repo, "raw/branch", targetBranch, filePath))
- resp = session.MakeRequest(t, req, http.StatusOK)
- assert.EqualValues(t, newContent, resp.Body.String())
+ respMap := map[string]string{}
+ DecodeJSON(t, resp, &respMap)
+ return respMap["uuid"]
+ }
- return resp
-}
+ t.Run("EmailInactive", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ email := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 35, UID: user.ID})
+ require.False(t, email.IsActivated)
+ makeReq(t, "/user2/repo1/_edit/master/README.md", map[string]string{
+ "tree_path": "README.md",
+ "content": "test content",
+ "commit_email": email.Email,
+ }, "", "")
+ })
-func TestEditFile(t *testing.T) {
- onGiteaRun(t, func(t *testing.T, u *url.URL) {
- session := loginUser(t, "user2")
- testEditFile(t, session, "user2", "repo1", "master", "README.md", "Hello, World (Edited)\n")
+ t.Run("EmailInvalid", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ email := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 1, IsActivated: true})
+ require.NotEqual(t, email.UID, user.ID)
+ makeReq(t, "/user2/repo1/_edit/master/README.md", map[string]string{
+ "tree_path": "README.md",
+ "content": "test content",
+ "commit_email": email.Email,
+ }, "", "")
+ })
+
+ testWebGit := func(t *testing.T, linkForKeepPrivate string, paramsForKeepPrivate map[string]string, linkForChosenEmail string, paramsForChosenEmail map[string]string) (resp1, resp2 *httptest.ResponseRecorder) {
+ t.Run("DefaultEmailKeepPrivate", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ paramsForKeepPrivate["commit_email"] = ""
+ resp1 = makeReq(t, linkForKeepPrivate, paramsForKeepPrivate, "User Two", "user2@noreply.example.org")
+ })
+ t.Run("ChooseEmail", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ paramsForChosenEmail["commit_email"] = "user2@example.com"
+ resp2 = makeReq(t, linkForChosenEmail, paramsForChosenEmail, "User Two", "user2@example.com")
+ })
+ return resp1, resp2
+ }
+
+ t.Run("Edit", func(t *testing.T) {
+ testWebGit(t,
+ "/user2/repo1/_edit/master/README.md", map[string]string{"tree_path": "README.md", "content": "for keep private"},
+ "/user2/repo1/_edit/master/README.md", map[string]string{"tree_path": "README.md", "content": "for chosen email"},
+ )
+ })
+
+ t.Run("UploadDelete", func(t *testing.T) {
+ file1UUID := uploadFile(t, "file1", "File 1")
+ file2UUID := uploadFile(t, "file2", "File 2")
+ testWebGit(t,
+ "/user2/repo1/_upload/master", map[string]string{"files": file1UUID},
+ "/user2/repo1/_upload/master", map[string]string{"files": file2UUID},
+ )
+ testWebGit(t,
+ "/user2/repo1/_delete/master/file1", map[string]string{},
+ "/user2/repo1/_delete/master/file2", map[string]string{},
+ )
+ })
+
+ t.Run("ApplyPatchCherryPick", func(t *testing.T) {
+ testWebGit(t,
+ "/user2/repo1/_diffpatch/master", map[string]string{
+ "tree_path": "__dummy__",
+ "content": `diff --git a/patch-file-1.txt b/patch-file-1.txt
+new file mode 100644
+index 0000000000..aaaaaaaaaa
+--- /dev/null
++++ b/patch-file-1.txt
+@@ -0,0 +1 @@
++File 1
+`,
+ },
+ "/user2/repo1/_diffpatch/master", map[string]string{
+ "tree_path": "__dummy__",
+ "content": `diff --git a/patch-file-2.txt b/patch-file-2.txt
+new file mode 100644
+index 0000000000..bbbbbbbbbb
+--- /dev/null
++++ b/patch-file-2.txt
+@@ -0,0 +1 @@
++File 2
+`,
+ },
+ )
+
+ commit1, err := gitRepo.GetCommitByPath("patch-file-1.txt")
+ require.NoError(t, err)
+ commit2, err := gitRepo.GetCommitByPath("patch-file-2.txt")
+ require.NoError(t, err)
+ resp1, _ := testWebGit(t,
+ "/user2/repo1/_cherrypick/"+commit1.ID.String()+"/master", map[string]string{"revert": "true"},
+ "/user2/repo1/_cherrypick/"+commit2.ID.String()+"/master", map[string]string{"revert": "true"},
+ )
+
+ // By the way, test the "cherrypick" page: a successful revert redirects to the main branch
+ assert.Equal(t, "/user2/repo1/src/branch/master", test.RedirectURL(resp1))
})
}
-func TestEditFileToNewBranch(t *testing.T) {
- onGiteaRun(t, func(t *testing.T, u *url.URL) {
- session := loginUser(t, "user2")
- testEditFileToNewBranch(t, session, "user2", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
+func testForkToEditFile(t *testing.T, session *TestSession, user, owner, repo, branch, filePath string) {
+ forkToEdit := func(t *testing.T, session *TestSession, owner, repo, operation, branch, filePath string) {
+ // visit the base repo, see the "Add File" button
+ req := NewRequest(t, "GET", path.Join(owner, repo))
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ AssertHTMLElement(t, htmlDoc, ".repo-add-file", 1)
+
+ // attempt to edit a file, see the guideline page
+ req = NewRequest(t, "GET", path.Join(owner, repo, operation, branch, filePath))
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ assert.Contains(t, resp.Body.String(), "Fork Repository to Propose Changes")
+
+ // fork the repository
+ req = NewRequestWithValues(t, "POST", path.Join(owner, repo, "_fork", branch), map[string]string{"_csrf": GetUserCSRFToken(t, session)})
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ assert.JSONEq(t, `{"redirect":""}`, resp.Body.String())
+ }
+
+ t.Run("ForkButArchived", func(t *testing.T) {
+ // Fork repository because we can't edit it
+ forkToEdit(t, session, owner, repo, "_edit", branch, filePath)
+
+ // Archive the repository
+ req := NewRequestWithValues(t, "POST", path.Join(user, repo, "settings"),
+ map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ "repo_name": repo,
+ "action": "archive",
+ },
+ )
+ session.MakeRequest(t, req, http.StatusSeeOther)
+
+ // Check editing archived repository is disabled
+ req = NewRequest(t, "GET", path.Join(owner, repo, "_edit", branch, filePath)).SetHeader("Accept", "text/html")
+ resp := session.MakeRequest(t, req, http.StatusNotFound)
+ assert.Contains(t, resp.Body.String(), "You have forked this repository but your fork is not editable.")
+
+ // Unfork the repository
+ req = NewRequestWithValues(t, "POST", path.Join(user, repo, "settings"),
+ map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ "repo_name": repo,
+ "action": "convert_fork",
+ },
+ )
+ session.MakeRequest(t, req, http.StatusSeeOther)
+ })
+
+ // Fork repository again, and check the existence of the forked repo with unique name
+ forkToEdit(t, session, owner, repo, "_edit", branch, filePath)
+ session.MakeRequest(t, NewRequestf(t, "GET", "/%s/%s-1", user, repo), http.StatusOK)
+
+ t.Run("CheckBaseRepoForm", func(t *testing.T) {
+ // the base repo's edit form should have the correct action and upload links (pointing to the forked repo)
+ req := NewRequest(t, "GET", path.Join(owner, repo, "_upload", branch, filePath)+"?foo=bar")
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+
+ uploadForm := htmlDoc.doc.Find(".form-fetch-action")
+ formAction := uploadForm.AttrOr("action", "")
+ assert.Equal(t, fmt.Sprintf("/%s/%s-1/_upload/%s/%s?from_base_branch=%s&foo=bar", user, repo, branch, filePath, branch), formAction)
+ uploadLink := uploadForm.Find(".dropzone").AttrOr("data-link-url", "")
+ assert.Equal(t, fmt.Sprintf("/%s/%s-1/upload-file", user, repo), uploadLink)
+ newBranchName := uploadForm.Find("input[name=new_branch_name]").AttrOr("value", "")
+ assert.Equal(t, user+"-patch-1", newBranchName)
+ commitChoice := uploadForm.Find("input[name=commit_choice][checked]").AttrOr("value", "")
+ assert.Equal(t, "commit-to-new-branch", commitChoice)
+ lastCommit := uploadForm.Find("input[name=last_commit]").AttrOr("value", "")
+ assert.NotEmpty(t, lastCommit)
})
+
+ t.Run("ViewBaseEditFormAndCommitToFork", func(t *testing.T) {
+ req := NewRequest(t, "GET", path.Join(owner, repo, "_edit", branch, filePath))
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ editRequestForm := map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ "last_commit": htmlDoc.GetInputValueByName("last_commit"),
+ "tree_path": filePath,
+ "content": "new content in fork",
+ "commit_choice": "commit-to-new-branch",
+ }
+ // change a file in the forked repo with existing branch name (should fail)
+ editRequestForm["new_branch_name"] = "master"
+ req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s-1/_edit/%s/%s?from_base_branch=%s", user, repo, branch, filePath, branch), editRequestForm)
+ resp = session.MakeRequest(t, req, http.StatusBadRequest)
+ respJSON := test.ParseJSONError(resp.Body.Bytes())
+ assert.Equal(t, `Branch "master" already exists in your fork, please choose a new branch name.`, respJSON.ErrorMessage)
+
+ // change a file in the forked repo (should succeed)
+ newBranchName := htmlDoc.GetInputValueByName("new_branch_name")
+ editRequestForm["new_branch_name"] = newBranchName
+ req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s-1/_edit/%s/%s?from_base_branch=%s", user, repo, branch, filePath, branch), editRequestForm)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ assert.Equal(t, fmt.Sprintf("/%s/%s/compare/%s...%s/%s-1:%s", owner, repo, branch, user, repo, newBranchName), test.RedirectURL(resp))
+
+ // check the file in the fork's branch is changed
+ req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s-1/src/branch/%s/%s", user, repo, newBranchName, filePath))
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ assert.Contains(t, resp.Body.String(), "new content in fork")
+ })
+}
+
+func testEditFileNotAllowed(t *testing.T) {
+ sessionUser1 := loginUser(t, "user1") // admin, all access
+ sessionUser4 := loginUser(t, "user4")
+ // "_cherrypick" has a different route pattern, so skip its test
+ operations := []string{"_new", "_edit", "_delete", "_upload", "_diffpatch"}
+ for _, operation := range operations {
+ t.Run(operation, func(t *testing.T) {
+ // Branch does not exist
+ targetLink := path.Join("user2", "repo1", operation, "missing", "README.md")
+ sessionUser1.MakeRequest(t, NewRequest(t, "GET", targetLink), http.StatusNotFound)
+
+ // Private repository
+ targetLink = path.Join("user2", "repo2", operation, "master", "Home.md")
+ sessionUser1.MakeRequest(t, NewRequest(t, "GET", targetLink), http.StatusOK)
+ sessionUser4.MakeRequest(t, NewRequest(t, "GET", targetLink), http.StatusNotFound)
+
+ // Empty repository
+ targetLink = path.Join("org41", "repo61", operation, "master", "README.md")
+ sessionUser1.MakeRequest(t, NewRequest(t, "GET", targetLink), http.StatusNotFound)
+ })
+ }
}
diff --git a/tests/integration/empty_repo_test.go b/tests/integration/empty_repo_test.go
index 630a3c03af..6a8c70f12f 100644
--- a/tests/integration/empty_repo_test.go
+++ b/tests/integration/empty_repo_test.go
@@ -10,10 +10,11 @@ import (
"io"
"mime/multipart"
"net/http"
- "net/http/httptest"
+ "strings"
"testing"
auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@@ -21,12 +22,14 @@ import (
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
-func testAPINewFile(t *testing.T, session *TestSession, user, repo, branch, treePath, content string) *httptest.ResponseRecorder {
+func testAPINewFile(t *testing.T, session *TestSession, user, repo, branch, treePath, content string) {
url := fmt.Sprintf("/%s/%s/_new/%s", user, repo, branch)
req := NewRequestWithValues(t, "POST", url, map[string]string{
"_csrf": GetUserCSRFToken(t, session),
@@ -34,7 +37,8 @@ func testAPINewFile(t *testing.T, session *TestSession, user, repo, branch, tree
"tree_path": treePath,
"content": content,
})
- return session.MakeRequest(t, req, http.StatusSeeOther)
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ assert.NotEmpty(t, test.RedirectURL(resp))
}
func TestEmptyRepo(t *testing.T) {
@@ -58,8 +62,22 @@ func TestEmptyRepoAddFile(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user30")
- req := NewRequest(t, "GET", "/user30/empty/_new/"+setting.Repository.DefaultBranch)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
+
+ // test web page
+ req := NewRequest(t, "GET", "/user30/empty")
resp := session.MakeRequest(t, req, http.StatusOK)
+ bodyString := resp.Body.String()
+ assert.Contains(t, bodyString, "empty-repo-guide")
+ assert.True(t, test.IsNormalPageCompleted(bodyString))
+
+ // test api
+ req = NewRequest(t, "GET", "/api/v1/repos/user30/empty/raw/main/README.md").AddTokenAuth(token)
+ session.MakeRequest(t, req, http.StatusNotFound)
+
+ // create a new file
+ req = NewRequest(t, "GET", "/user30/empty/_new/"+setting.Repository.DefaultBranch)
+ resp = session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body).Find(`input[name="commit_choice"]`)
assert.Empty(t, doc.AttrOr("checked", "_no_"))
req = NewRequestWithValues(t, "POST", "/user30/empty/_new/"+setting.Repository.DefaultBranch, map[string]string{
@@ -69,13 +87,43 @@ func TestEmptyRepoAddFile(t *testing.T) {
"content": "newly-added-test-file",
})
- resp = session.MakeRequest(t, req, http.StatusSeeOther)
+ resp = session.MakeRequest(t, req, http.StatusOK)
redirect := test.RedirectURL(resp)
assert.Equal(t, "/user30/empty/src/branch/"+setting.Repository.DefaultBranch+"/test-file.md", redirect)
req = NewRequest(t, "GET", redirect)
resp = session.MakeRequest(t, req, http.StatusOK)
assert.Contains(t, resp.Body.String(), "newly-added-test-file")
+
+ // the repo is not empty anymore
+ req = NewRequest(t, "GET", "/user30/empty")
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ assert.Contains(t, resp.Body.String(), "test-file.md")
+
+ // if the repo is in incorrect state, it should be able to self-heal (recover to correct state)
+ testEmptyOrBrokenRecover := func(t *testing.T, isEmpty, isBroken bool) {
+ user30EmptyRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: 30, Name: "empty"})
+ user30EmptyRepo.IsEmpty = isEmpty
+ user30EmptyRepo.Status = util.Iif(isBroken, repo_model.RepositoryBroken, repo_model.RepositoryReady)
+ user30EmptyRepo.DefaultBranch = "no-such"
+ _, err := db.GetEngine(db.DefaultContext).ID(user30EmptyRepo.ID).Cols("is_empty", "status", "default_branch").Update(user30EmptyRepo)
+ require.NoError(t, err)
+ user30EmptyRepo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: 30, Name: "empty"})
+ assert.Equal(t, isEmpty, user30EmptyRepo.IsEmpty)
+ assert.Equal(t, isBroken, user30EmptyRepo.Status == repo_model.RepositoryBroken)
+
+ req = NewRequest(t, "GET", "/user30/empty")
+ resp = session.MakeRequest(t, req, http.StatusSeeOther)
+ redirect = test.RedirectURL(resp)
+ assert.Equal(t, "/user30/empty", redirect)
+
+ req = NewRequest(t, "GET", "/user30/empty")
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ assert.Contains(t, resp.Body.String(), "test-file.md")
+ }
+ testEmptyOrBrokenRecover(t, true, false)
+ testEmptyOrBrokenRecover(t, false, true)
+ testEmptyOrBrokenRecover(t, true, true)
}
func TestEmptyRepoUploadFile(t *testing.T) {
@@ -91,7 +139,7 @@ func TestEmptyRepoUploadFile(t *testing.T) {
mpForm := multipart.NewWriter(body)
_ = mpForm.WriteField("_csrf", GetUserCSRFToken(t, session))
file, _ := mpForm.CreateFormFile("file", "uploaded-file.txt")
- _, _ = io.Copy(file, bytes.NewBufferString("newly-uploaded-test-file"))
+ _, _ = io.Copy(file, strings.NewReader("newly-uploaded-test-file"))
_ = mpForm.Close()
req = NewRequestWithBody(t, "POST", "/user30/empty/upload-file", body)
@@ -106,9 +154,9 @@ func TestEmptyRepoUploadFile(t *testing.T) {
"files": respMap["uuid"],
"tree_path": "",
})
- resp = session.MakeRequest(t, req, http.StatusSeeOther)
+ resp = session.MakeRequest(t, req, http.StatusOK)
redirect := test.RedirectURL(resp)
- assert.Equal(t, "/user30/empty/src/branch/"+setting.Repository.DefaultBranch+"/", redirect)
+ assert.Equal(t, "/user30/empty/src/branch/"+setting.Repository.DefaultBranch, redirect)
req = NewRequest(t, "GET", redirect)
resp = session.MakeRequest(t, req, http.StatusOK)
@@ -133,7 +181,7 @@ func TestEmptyRepoAddFileByAPI(t *testing.T) {
var fileResponse api.FileResponse
DecodeJSON(t, resp, &fileResponse)
expectedHTMLURL := setting.AppURL + "user30/empty/src/branch/new_branch/new-file.txt"
- assert.EqualValues(t, expectedHTMLURL, *fileResponse.Content.HTMLURL)
+ assert.Equal(t, expectedHTMLURL, *fileResponse.Content.HTMLURL)
req = NewRequest(t, "GET", "/user30/empty/src/branch/new_branch/new-file.txt")
resp = session.MakeRequest(t, req, http.StatusOK)
diff --git a/tests/integration/ephemeral_actions_runner_deletion_test.go b/tests/integration/ephemeral_actions_runner_deletion_test.go
new file mode 100644
index 0000000000..40f8c643a8
--- /dev/null
+++ b/tests/integration/ephemeral_actions_runner_deletion_test.go
@@ -0,0 +1,77 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "testing"
+
+ actions_model "code.gitea.io/gitea/models/actions"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/util"
+ repo_service "code.gitea.io/gitea/services/repository"
+ user_service "code.gitea.io/gitea/services/user"
+ "code.gitea.io/gitea/tests"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestEphemeralActionsRunnerDeletion(t *testing.T) {
+ t.Run("ByTaskCompletion", testEphemeralActionsRunnerDeletionByTaskCompletion)
+ t.Run("ByRepository", testEphemeralActionsRunnerDeletionByRepository)
+ t.Run("ByUser", testEphemeralActionsRunnerDeletionByUser)
+}
+
+// Test that the ephemeral runner is deleted when the task is finished
+func testEphemeralActionsRunnerDeletionByTaskCompletion(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ _, err := actions_model.GetRunnerByID(t.Context(), 34350)
+ assert.NoError(t, err)
+
+ task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 52})
+ assert.Equal(t, actions_model.StatusRunning, task.Status)
+
+ task.Status = actions_model.StatusSuccess
+ err = actions_model.UpdateTask(t.Context(), task, "status")
+ assert.NoError(t, err)
+
+ _, err = actions_model.GetRunnerByID(t.Context(), 34350)
+ assert.ErrorIs(t, err, util.ErrNotExist)
+}
+
+func testEphemeralActionsRunnerDeletionByRepository(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ _, err := actions_model.GetRunnerByID(t.Context(), 34350)
+ assert.NoError(t, err)
+
+ task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 52})
+ assert.Equal(t, actions_model.StatusRunning, task.Status)
+
+ err = repo_service.DeleteRepositoryDirectly(t.Context(), task.RepoID, true)
+ assert.NoError(t, err)
+
+ _, err = actions_model.GetRunnerByID(t.Context(), 34350)
+ assert.ErrorIs(t, err, util.ErrNotExist)
+}
+
+// Test that the ephemeral runner is deleted when a user is deleted
+func testEphemeralActionsRunnerDeletionByUser(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ _, err := actions_model.GetRunnerByID(t.Context(), 34350)
+ assert.NoError(t, err)
+
+ task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 52})
+ assert.Equal(t, actions_model.StatusRunning, task.Status)
+
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+
+ err = user_service.DeleteUser(t.Context(), user, true)
+ assert.NoError(t, err)
+
+ _, err = actions_model.GetRunnerByID(t.Context(), 34350)
+ assert.ErrorIs(t, err, util.ErrNotExist)
+}
diff --git a/tests/integration/feed_repo_test.go b/tests/integration/feed_repo_test.go
new file mode 100644
index 0000000000..2915b9b3f4
--- /dev/null
+++ b/tests/integration/feed_repo_test.go
@@ -0,0 +1,35 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "encoding/xml"
+ "net/http"
+ "testing"
+
+ "code.gitea.io/gitea/tests"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestFeedRepo(t *testing.T) {
+ t.Run("RSS", func(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ req := NewRequest(t, "GET", "/user2/repo1.rss")
+ resp := MakeRequest(t, req, http.StatusOK)
+
+ data := resp.Body.String()
+ assert.Contains(t, data, `<rss version="2.0"`)
+
+ var rss RSS
+ err := xml.Unmarshal(resp.Body.Bytes(), &rss)
+ assert.NoError(t, err)
+ assert.Contains(t, rss.Channel.Link, "/user2/repo1")
+ assert.NotEmpty(t, rss.Channel.PubDate)
+ assert.Len(t, rss.Channel.Items, 1)
+ assert.Equal(t, "issue5", rss.Channel.Items[0].Description)
+ assert.NotEmpty(t, rss.Channel.Items[0].PubDate)
+ })
+}
diff --git a/tests/integration/api_feed_user_test.go b/tests/integration/feed_user_test.go
index c44f9a1951..4315c67f48 100644
--- a/tests/integration/api_feed_user_test.go
+++ b/tests/integration/feed_user_test.go
@@ -4,6 +4,7 @@
package integration
import (
+ "encoding/xml"
"net/http"
"testing"
@@ -12,7 +13,23 @@ import (
"github.com/stretchr/testify/assert"
)
-func TestFeed(t *testing.T) {
+// RSS is a struct to unmarshal RSS feeds test only
+type RSS struct {
+ Channel struct {
+ Title string `xml:"title"`
+ Link string `xml:"link"`
+ Description string `xml:"description"`
+ PubDate string `xml:"pubDate"`
+ Items []struct {
+ Title string `xml:"title"`
+ Link string `xml:"link"`
+ Description string `xml:"description"`
+ PubDate string `xml:"pubDate"`
+ } `xml:"item"`
+ } `xml:"channel"`
+}
+
+func TestFeedUser(t *testing.T) {
t.Run("User", func(t *testing.T) {
t.Run("Atom", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
@@ -32,6 +49,12 @@ func TestFeed(t *testing.T) {
data := resp.Body.String()
assert.Contains(t, data, `<rss version="2.0"`)
+
+ var rss RSS
+ err := xml.Unmarshal(resp.Body.Bytes(), &rss)
+ assert.NoError(t, err)
+ assert.Contains(t, rss.Channel.Link, "/user2")
+ assert.NotEmpty(t, rss.Channel.PubDate)
})
})
}
diff --git a/tests/integration/git_general_test.go b/tests/integration/git_general_test.go
index a47cb75196..3b0f9589d2 100644
--- a/tests/integration/git_general_test.go
+++ b/tests/integration/git_general_test.go
@@ -11,8 +11,10 @@ import (
"net/http"
"net/url"
"os"
+ "os/exec"
"path"
"path/filepath"
+ "slices"
"strconv"
"testing"
"time"
@@ -24,14 +26,16 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/commitstatus"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
- gitea_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/tests"
+ "github.com/kballard/go-shellquote"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
const (
@@ -80,6 +84,7 @@ func testGitGeneral(t *testing.T, u *url.URL) {
mediaTest(t, &httpContext, pushedFilesStandard[0], pushedFilesStandard[1], pushedFilesLFS[0], pushedFilesLFS[1])
t.Run("CreateAgitFlowPull", doCreateAgitFlowPull(dstPath, &httpContext, "test/head"))
+ t.Run("CreateProtectedBranch", doCreateProtectedBranch(&httpContext, dstPath))
t.Run("BranchProtectMerge", doBranchProtectPRMerge(&httpContext, dstPath))
t.Run("AutoMerge", doAutoPRMerge(&httpContext, dstPath))
t.Run("CreatePRAndSetManuallyMerged", doCreatePRAndSetManuallyMerged(httpContext, httpContext, dstPath, "master", "test-manually-merge"))
@@ -104,7 +109,12 @@ func testGitGeneral(t *testing.T, u *url.URL) {
// Setup key the user ssh key
withKeyFile(t, keyname, func(keyFile string) {
- t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile))
+ var keyID int64
+ t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile, func(t *testing.T, key api.PublicKey) {
+ keyID = key.ID
+ }))
+ assert.NotZero(t, keyID)
+ t.Run("LFSAccessTest", doSSHLFSAccessTest(sshContext, keyID))
// Setup remote link
// TODO: get url from api
@@ -121,6 +131,7 @@ func testGitGeneral(t *testing.T, u *url.URL) {
mediaTest(t, &sshContext, pushedFilesStandard[0], pushedFilesStandard[1], pushedFilesLFS[0], pushedFilesLFS[1])
t.Run("CreateAgitFlowPull", doCreateAgitFlowPull(dstPath, &sshContext, "test/head2"))
+ t.Run("CreateProtectedBranch", doCreateProtectedBranch(&sshContext, dstPath))
t.Run("BranchProtectMerge", doBranchProtectPRMerge(&sshContext, dstPath))
t.Run("MergeFork", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
@@ -134,6 +145,36 @@ func testGitGeneral(t *testing.T, u *url.URL) {
})
}
+func doSSHLFSAccessTest(_ APITestContext, keyID int64) func(*testing.T) {
+ return func(t *testing.T) {
+ sshCommand := os.Getenv("GIT_SSH_COMMAND") // it is set in withKeyFile
+ sshCmdParts, err := shellquote.Split(sshCommand) // and parse the ssh command to construct some mocked arguments
+ require.NoError(t, err)
+
+ t.Run("User2AccessOwned", func(t *testing.T) {
+ sshCmdUser2Self := append(slices.Clone(sshCmdParts),
+ "-p", strconv.Itoa(setting.SSH.ListenPort), "git@"+setting.SSH.ListenHost,
+ "git-lfs-authenticate", "user2/repo1.git", "upload", // accessible to own repo
+ )
+ cmd := exec.CommandContext(t.Context(), sshCmdUser2Self[0], sshCmdUser2Self[1:]...)
+ _, err := cmd.Output()
+ assert.NoError(t, err) // accessible, no error
+ })
+
+ t.Run("User2AccessOther", func(t *testing.T) {
+ sshCmdUser2Other := append(slices.Clone(sshCmdParts),
+ "-p", strconv.Itoa(setting.SSH.ListenPort), "git@"+setting.SSH.ListenHost,
+ "git-lfs-authenticate", "user5/repo4.git", "upload", // inaccessible to other's (user5/repo4)
+ )
+ cmd := exec.CommandContext(t.Context(), sshCmdUser2Other[0], sshCmdUser2Other[1:]...)
+ _, err := cmd.Output()
+ var errExit *exec.ExitError
+ require.ErrorAs(t, err, &errExit) // inaccessible, error
+ assert.Contains(t, string(errExit.Stderr), fmt.Sprintf("User: 2:user2 with Key: %d:test-key is not authorized to write to user5/repo4.", keyID))
+ })
+ }
+}
+
func ensureAnonymousClone(t *testing.T, u *url.URL) {
dstLocalPath := t.TempDir()
t.Run("CloneAnonymous", doGitClone(dstLocalPath, u))
@@ -151,9 +192,9 @@ func lfsCommitAndPushTest(t *testing.T, dstPath string, sizes ...int) (pushedFil
t.Run("CommitAndPushLFS", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
prefix := "lfs-data-file-"
- err := git.NewCommand(git.DefaultContext, "lfs").AddArguments("install").Run(&git.RunOpts{Dir: dstPath})
+ err := git.NewCommand("lfs").AddArguments("install").Run(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
- _, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("track").AddDynamicArguments(prefix + "*").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err = git.NewCommand("lfs").AddArguments("track").AddDynamicArguments(prefix+"*").RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
err = git.AddChanges(dstPath, false, ".gitattributes")
assert.NoError(t, err)
@@ -269,20 +310,20 @@ func lockTest(t *testing.T, repoPath string) {
}
func lockFileTest(t *testing.T, filename, repoPath string) {
- _, _, err := git.NewCommand(git.DefaultContext, "lfs").AddArguments("locks").RunStdString(&git.RunOpts{Dir: repoPath})
+ _, _, err := git.NewCommand("lfs").AddArguments("locks").RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
assert.NoError(t, err)
- _, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("lock").AddDynamicArguments(filename).RunStdString(&git.RunOpts{Dir: repoPath})
+ _, _, err = git.NewCommand("lfs").AddArguments("lock").AddDynamicArguments(filename).RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
assert.NoError(t, err)
- _, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("locks").RunStdString(&git.RunOpts{Dir: repoPath})
+ _, _, err = git.NewCommand("lfs").AddArguments("locks").RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
assert.NoError(t, err)
- _, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("unlock").AddDynamicArguments(filename).RunStdString(&git.RunOpts{Dir: repoPath})
+ _, _, err = git.NewCommand("lfs").AddArguments("unlock").AddDynamicArguments(filename).RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath})
assert.NoError(t, err)
}
func doCommitAndPush(t *testing.T, size int, repoPath, prefix string) string {
name, err := generateCommitWithNewData(size, repoPath, "user2@example.com", "User Two", prefix)
assert.NoError(t, err)
- _, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "master").RunStdString(&git.RunOpts{Dir: repoPath}) // Push
+ _, _, err = git.NewCommand("push", "origin", "master").RunStdString(git.DefaultContext, &git.RunOpts{Dir: repoPath}) // Push
assert.NoError(t, err)
return name
}
@@ -325,6 +366,34 @@ func generateCommitWithNewData(size int, repoPath, email, fullName, prefix strin
return filepath.Base(tmpFile.Name()), err
}
+func doCreateProtectedBranch(baseCtx *APITestContext, dstPath string) func(t *testing.T) {
+ return func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame, auth_model.AccessTokenScopeWriteRepository)
+
+ t.Run("ProtectBranchWithFilePatterns", doProtectBranch(ctx, "release-*", baseCtx.Username, "", "", "config*"))
+
+ // push a new branch without any new commits
+ t.Run("CreateProtectedBranch-NoChanges", doGitCreateBranch(dstPath, "release-v1.0"))
+ t.Run("PushProtectedBranch-NoChanges", doGitPushTestRepository(dstPath, "origin", "release-v1.0"))
+ t.Run("CheckoutMaster-NoChanges", doGitCheckoutBranch(dstPath, "master"))
+
+ // push a new branch with a new unprotected file
+ t.Run("CreateProtectedBranch-UnprotectedFile", doGitCreateBranch(dstPath, "release-v2.0"))
+ _, err := generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "abc.txt")
+ assert.NoError(t, err)
+ t.Run("PushProtectedBranch-UnprotectedFile", doGitPushTestRepository(dstPath, "origin", "release-v2.0"))
+ t.Run("CheckoutMaster-UnprotectedFile", doGitCheckoutBranch(dstPath, "master"))
+
+ // push a new branch with a new protected file
+ t.Run("CreateProtectedBranch-ProtectedFile", doGitCreateBranch(dstPath, "release-v3.0"))
+ _, err = generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "config")
+ assert.NoError(t, err)
+ t.Run("PushProtectedBranch-ProtectedFile", doGitPushTestRepositoryFail(dstPath, "origin", "release-v3.0"))
+ t.Run("CheckoutMaster-ProtectedFile", doGitCheckoutBranch(dstPath, "master"))
+ }
+}
+
func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) {
return func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
@@ -334,60 +403,48 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes
ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame, auth_model.AccessTokenScopeWriteRepository)
// Protect branch without any whitelisting
- t.Run("ProtectBranchNoWhitelist", func(t *testing.T) {
- doProtectBranch(ctx, "protected", "", "", "")
- })
+ t.Run("ProtectBranchNoWhitelist", doProtectBranch(ctx, "protected", "", "", "", ""))
// Try to push without permissions, which should fail
t.Run("TryPushWithoutPermissions", func(t *testing.T) {
_, err := generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "branch-data-file-")
assert.NoError(t, err)
- doGitPushTestRepositoryFail(dstPath, "origin", "protected")
+ doGitPushTestRepositoryFail(dstPath, "origin", "protected")(t)
})
// Set up permissions for normal push but not force push
- t.Run("SetupNormalPushPermissions", func(t *testing.T) {
- doProtectBranch(ctx, "protected", baseCtx.Username, "", "")
- })
+ t.Run("SetupNormalPushPermissions", doProtectBranch(ctx, "protected", baseCtx.Username, "", "", ""))
// Normal push should work
t.Run("NormalPushWithPermissions", func(t *testing.T) {
_, err := generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "branch-data-file-")
assert.NoError(t, err)
- doGitPushTestRepository(dstPath, "origin", "protected")
+ doGitPushTestRepository(dstPath, "origin", "protected")(t)
})
// Try to force push without force push permissions, which should fail
t.Run("ForcePushWithoutForcePermissions", func(t *testing.T) {
t.Run("CreateDivergentHistory", func(t *testing.T) {
- git.NewCommand(git.DefaultContext, "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath})
+ git.NewCommand("reset", "--hard", "HEAD~1").Run(git.DefaultContext, &git.RunOpts{Dir: dstPath})
_, err := generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "branch-data-file-new")
assert.NoError(t, err)
})
- doGitPushTestRepositoryFail(dstPath, "-f", "origin", "protected")
+ doGitPushTestRepositoryFail(dstPath, "-f", "origin", "protected")(t)
})
// Set up permissions for force push but not normal push
- t.Run("SetupForcePushPermissions", func(t *testing.T) {
- doProtectBranch(ctx, "protected", "", baseCtx.Username, "")
- })
+ t.Run("SetupForcePushPermissions", doProtectBranch(ctx, "protected", "", baseCtx.Username, "", ""))
// Try to force push without normal push permissions, which should fail
- t.Run("ForcePushWithoutNormalPermissions", func(t *testing.T) {
- doGitPushTestRepositoryFail(dstPath, "-f", "origin", "protected")
- })
+ t.Run("ForcePushWithoutNormalPermissions", doGitPushTestRepositoryFail(dstPath, "-f", "origin", "protected"))
// Set up permissions for normal and force push (both are required to force push)
- t.Run("SetupNormalAndForcePushPermissions", func(t *testing.T) {
- doProtectBranch(ctx, "protected", baseCtx.Username, baseCtx.Username, "")
- })
+ t.Run("SetupNormalAndForcePushPermissions", doProtectBranch(ctx, "protected", baseCtx.Username, baseCtx.Username, "", ""))
// Force push should now work
- t.Run("ForcePushWithPermissions", func(t *testing.T) {
- doGitPushTestRepository(dstPath, "-f", "origin", "protected")
- })
+ t.Run("ForcePushWithPermissions", doGitPushTestRepository(dstPath, "-f", "origin", "protected"))
- t.Run("ProtectProtectedBranchNoWhitelist", doProtectBranch(ctx, "protected", "", "", ""))
+ t.Run("ProtectProtectedBranchNoWhitelist", doProtectBranch(ctx, "protected", "", "", "", ""))
t.Run("PushToUnprotectedBranch", doGitPushTestRepository(dstPath, "origin", "protected:unprotected"))
var pr api.PullRequest
var err error
@@ -409,14 +466,14 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes
t.Run("MergePR", doAPIMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index))
t.Run("PullProtected", doGitPull(dstPath, "origin", "protected"))
- t.Run("ProtectProtectedBranchUnprotectedFilePaths", doProtectBranch(ctx, "protected", "", "", "unprotected-file-*"))
+ t.Run("ProtectProtectedBranchUnprotectedFilePaths", doProtectBranch(ctx, "protected", "", "", "unprotected-file-*", ""))
t.Run("GenerateCommit", func(t *testing.T) {
_, err := generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "unprotected-file-")
assert.NoError(t, err)
})
t.Run("PushUnprotectedFilesToProtectedBranch", doGitPushTestRepository(dstPath, "origin", "protected"))
- t.Run("ProtectProtectedBranchWhitelist", doProtectBranch(ctx, "protected", baseCtx.Username, "", ""))
+ t.Run("ProtectProtectedBranchWhitelist", doProtectBranch(ctx, "protected", baseCtx.Username, "", "", ""))
t.Run("CheckoutMaster", doGitCheckoutBranch(dstPath, "master"))
t.Run("CreateBranchForced", doGitCreateBranch(dstPath, "toforce"))
@@ -431,7 +488,7 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes
}
}
-func doProtectBranch(ctx APITestContext, branch, userToWhitelistPush, userToWhitelistForcePush, unprotectedFilePatterns string) func(t *testing.T) {
+func doProtectBranch(ctx APITestContext, branch, userToWhitelistPush, userToWhitelistForcePush, unprotectedFilePatterns, protectedFilePatterns string) func(t *testing.T) {
// We are going to just use the owner to set the protection.
return func(t *testing.T) {
csrf := GetUserCSRFToken(t, ctx.Session)
@@ -440,6 +497,7 @@ func doProtectBranch(ctx APITestContext, branch, userToWhitelistPush, userToWhit
"_csrf": csrf,
"rule_name": branch,
"unprotected_file_patterns": unprotectedFilePatterns,
+ "protected_file_patterns": protectedFilePatterns,
}
if userToWhitelistPush != "" {
@@ -463,9 +521,8 @@ func doProtectBranch(ctx APITestContext, branch, userToWhitelistPush, userToWhit
ctx.Session.MakeRequest(t, req, http.StatusSeeOther)
// Check if master branch has been locked successfully
- flashCookie := ctx.Session.GetCookie(gitea_context.CookieNameFlash)
- assert.NotNil(t, flashCookie)
- assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2522"+url.QueryEscape(branch)+"%2522%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
+ flashMsg := ctx.Session.GetCookieFlashMessage()
+ assert.Equal(t, `Branch protection for rule "`+branch+`" has been updated.`, flashMsg.SuccessMsg)
}
}
@@ -574,7 +631,7 @@ func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
// create a context for a currently non-existent repository
- ctx.Reponame = fmt.Sprintf("repo-tmp-push-create-%s", u.Scheme)
+ ctx.Reponame = "repo-tmp-push-create-" + u.Scheme
u.Path = ctx.GitPath()
// Create a temporary directory
@@ -605,7 +662,7 @@ func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) {
// Now add a remote that is invalid to "Push To Create"
invalidCtx := ctx
- invalidCtx.Reponame = fmt.Sprintf("invalid/repo-tmp-push-create-%s", u.Scheme)
+ invalidCtx.Reponame = "invalid/repo-tmp-push-create-" + u.Scheme
u.Path = invalidCtx.GitPath()
t.Run("AddInvalidRemote", doGitAddRemote(tmpDir, "invalid", u))
@@ -657,7 +714,7 @@ func doAutoPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) {
commitID := path.Base(commitURL)
- addCommitStatus := func(status api.CommitStatusState) func(*testing.T) {
+ addCommitStatus := func(status commitstatus.CommitStatusState) func(*testing.T) {
return doAPICreateCommitStatus(ctx, commitID, api.CreateStatusOption{
State: status,
TargetURL: "http://test.ci/",
@@ -667,7 +724,7 @@ func doAutoPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) {
}
// Call API to add Pending status for commit
- t.Run("CreateStatus", addCommitStatus(api.CommitStatusPending))
+ t.Run("CreateStatus", addCommitStatus(commitstatus.CommitStatusPending))
// Cancel not existing auto merge
ctx.ExpectedCode = http.StatusNotFound
@@ -696,7 +753,7 @@ func doAutoPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) {
assert.False(t, pr.HasMerged)
// Call API to add Failure status for commit
- t.Run("CreateStatus", addCommitStatus(api.CommitStatusFailure))
+ t.Run("CreateStatus", addCommitStatus(commitstatus.CommitStatusFailure))
// Check pr status
pr, err = doAPIGetPullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index)(t)
@@ -704,7 +761,7 @@ func doAutoPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) {
assert.False(t, pr.HasMerged)
// Call API to add Success status for commit
- t.Run("CreateStatus", addCommitStatus(api.CommitStatusSuccess))
+ t.Run("CreateStatus", addCommitStatus(commitstatus.CommitStatusSuccess))
// wait to let gitea merge stuff
time.Sleep(time.Second)
@@ -726,9 +783,8 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string
}
gitRepo, err := git.OpenRepository(git.DefaultContext, dstPath)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
+
defer gitRepo.Close()
var (
@@ -736,9 +792,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string
commit string
)
repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, ctx.Username, ctx.Reponame)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
pullNum := unittest.GetCount(t, &issues_model.PullRequest{})
@@ -746,9 +800,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string
t.Run("AddCommit", func(t *testing.T) {
err := os.WriteFile(path.Join(dstPath, "test_file"), []byte("## test content"), 0o666)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
err = git.AddChanges(dstPath, true)
assert.NoError(t, err)
@@ -772,44 +824,38 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string
})
t.Run("Push", func(t *testing.T) {
- err := git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master", "-o").AddDynamicArguments("topic=" + headBranch).Run(&git.RunOpts{Dir: dstPath})
- if !assert.NoError(t, err) {
- return
- }
+ err := git.NewCommand("push", "origin", "HEAD:refs/for/master", "-o").AddDynamicArguments("topic="+headBranch).Run(git.DefaultContext, &git.RunOpts{Dir: dstPath})
+ require.NoError(t, err)
+
unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+1)
pr1 = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
HeadRepoID: repo.ID,
Flow: issues_model.PullRequestFlowAGit,
})
- if !assert.NotEmpty(t, pr1) {
- return
- }
+ require.NotEmpty(t, pr1)
+
prMsg, err := doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)(t)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
+
assert.Equal(t, "user2/"+headBranch, pr1.HeadBranch)
assert.False(t, prMsg.HasMerged)
assert.Contains(t, "Testing commit 1", prMsg.Body)
assert.Equal(t, commit, prMsg.Head.Sha)
- _, _, err = git.NewCommand(git.DefaultContext, "push", "origin").AddDynamicArguments("HEAD:refs/for/master/test/" + headBranch).RunStdString(&git.RunOpts{Dir: dstPath})
- if !assert.NoError(t, err) {
- return
- }
+ _, _, err = git.NewCommand("push", "origin").AddDynamicArguments("HEAD:refs/for/master/test/"+headBranch).RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
+ require.NoError(t, err)
+
unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+2)
pr2 = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
HeadRepoID: repo.ID,
Index: pr1.Index + 1,
Flow: issues_model.PullRequestFlowAGit,
})
- if !assert.NotEmpty(t, pr2) {
- return
- }
+ require.NotEmpty(t, pr2)
+
prMsg, err = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr2.Index)(t)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
+
assert.Equal(t, "user2/test/"+headBranch, pr2.HeadBranch)
assert.False(t, prMsg.HasMerged)
})
@@ -820,9 +866,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string
t.Run("AddCommit2", func(t *testing.T) {
err := os.WriteFile(path.Join(dstPath, "test_file"), []byte("## test content \n ## test content 2"), 0o666)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
err = git.AddChanges(dstPath, true)
assert.NoError(t, err)
@@ -846,27 +890,23 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string
})
t.Run("Push2", func(t *testing.T) {
- err := git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master", "-o").AddDynamicArguments("topic=" + headBranch).Run(&git.RunOpts{Dir: dstPath})
- if !assert.NoError(t, err) {
- return
- }
+ err := git.NewCommand("push", "origin", "HEAD:refs/for/master", "-o").AddDynamicArguments("topic="+headBranch).Run(git.DefaultContext, &git.RunOpts{Dir: dstPath})
+ require.NoError(t, err)
+
unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+2)
prMsg, err := doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)(t)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
+
assert.False(t, prMsg.HasMerged)
assert.Equal(t, commit, prMsg.Head.Sha)
- _, _, err = git.NewCommand(git.DefaultContext, "push", "origin").AddDynamicArguments("HEAD:refs/for/master/test/" + headBranch).RunStdString(&git.RunOpts{Dir: dstPath})
- if !assert.NoError(t, err) {
- return
- }
+ _, _, err = git.NewCommand("push", "origin").AddDynamicArguments("HEAD:refs/for/master/test/"+headBranch).RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
+ require.NoError(t, err)
+
unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+2)
prMsg, err = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr2.Index)(t)
- if !assert.NoError(t, err) {
- return
- }
+ require.NoError(t, err)
+
assert.False(t, prMsg.HasMerged)
assert.Equal(t, commit, prMsg.Head.Sha)
})
diff --git a/tests/integration/git_helper_for_declarative_test.go b/tests/integration/git_helper_for_declarative_test.go
index 43b151e0b6..7d42508bfe 100644
--- a/tests/integration/git_helper_for_declarative_test.go
+++ b/tests/integration/git_helper_for_declarative_test.go
@@ -10,7 +10,6 @@ import (
"net/http"
"net/url"
"os"
- "path"
"path/filepath"
"strconv"
"testing"
@@ -35,12 +34,12 @@ func withKeyFile(t *testing.T, keyname string, callback func(string)) {
err = ssh.GenKeyPair(keyFile)
assert.NoError(t, err)
- err = os.WriteFile(path.Join(tmpDir, "ssh"), []byte("#!/bin/bash\n"+
+ err = os.WriteFile(filepath.Join(tmpDir, "ssh"), []byte("#!/bin/bash\n"+
"ssh -o \"UserKnownHostsFile=/dev/null\" -o \"StrictHostKeyChecking=no\" -o \"IdentitiesOnly=yes\" -i \""+keyFile+"\" \"$@\""), 0o700)
assert.NoError(t, err)
// Setup ssh wrapper
- t.Setenv("GIT_SSH", path.Join(tmpDir, "ssh"))
+ t.Setenv("GIT_SSH", filepath.Join(tmpDir, "ssh"))
t.Setenv("GIT_SSH_COMMAND",
"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i \""+keyFile+"\"")
t.Setenv("GIT_SSH_VARIANT", "ssh")
@@ -76,7 +75,7 @@ func onGiteaRun[T testing.TB](t T, callback func(T, *url.URL)) {
u.Host = listener.Addr().String()
defer func() {
- ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
+ ctx, cancel := context.WithTimeout(t.Context(), 2*time.Minute)
s.Shutdown(ctx)
cancel()
}()
@@ -89,7 +88,7 @@ func onGiteaRun[T testing.TB](t T, callback func(T, *url.URL)) {
func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) {
return func(t *testing.T) {
- assert.NoError(t, git.CloneWithArgs(context.Background(), git.AllowLFSFiltersArgs(), u.String(), dstLocalPath, git.CloneRepoOptions{}))
+ assert.NoError(t, git.CloneWithArgs(t.Context(), git.AllowLFSFiltersArgs(), u.String(), dstLocalPath, git.CloneRepoOptions{}))
exist, err := util.IsExist(filepath.Join(dstLocalPath, "README.md"))
assert.NoError(t, err)
assert.True(t, exist)
@@ -98,7 +97,7 @@ func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) {
func doPartialGitClone(dstLocalPath string, u *url.URL) func(*testing.T) {
return func(t *testing.T) {
- assert.NoError(t, git.CloneWithArgs(context.Background(), git.AllowLFSFiltersArgs(), u.String(), dstLocalPath, git.CloneRepoOptions{
+ assert.NoError(t, git.CloneWithArgs(t.Context(), git.AllowLFSFiltersArgs(), u.String(), dstLocalPath, git.CloneRepoOptions{
Filter: "blob:none",
}))
exist, err := util.IsExist(filepath.Join(dstLocalPath, "README.md"))
@@ -122,9 +121,9 @@ func doGitInitTestRepository(dstPath string) func(*testing.T) {
// Init repository in dstPath
assert.NoError(t, git.InitRepository(git.DefaultContext, dstPath, false, git.Sha1ObjectFormat.Name()))
// forcibly set default branch to master
- _, _, err := git.NewCommand(git.DefaultContext, "symbolic-ref", "HEAD", git.BranchPrefix+"master").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err := git.NewCommand("symbolic-ref", "HEAD", git.BranchPrefix+"master").RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
- assert.NoError(t, os.WriteFile(filepath.Join(dstPath, "README.md"), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s", dstPath)), 0o644))
+ assert.NoError(t, os.WriteFile(filepath.Join(dstPath, "README.md"), []byte("# Testing Repository\n\nOriginally created in: "+dstPath), 0o644))
assert.NoError(t, git.AddChanges(dstPath, true))
signature := git.Signature{
Email: "test@example.com",
@@ -141,21 +140,21 @@ func doGitInitTestRepository(dstPath string) func(*testing.T) {
func doGitAddRemote(dstPath, remoteName string, u *url.URL) func(*testing.T) {
return func(t *testing.T) {
- _, _, err := git.NewCommand(git.DefaultContext, "remote", "add").AddDynamicArguments(remoteName, u.String()).RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err := git.NewCommand("remote", "add").AddDynamicArguments(remoteName, u.String()).RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
}
}
func doGitPushTestRepository(dstPath string, args ...string) func(*testing.T) {
return func(t *testing.T) {
- _, _, err := git.NewCommand(git.DefaultContext, "push", "-u").AddArguments(git.ToTrustedCmdArgs(args)...).RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err := git.NewCommand("push", "-u").AddArguments(git.ToTrustedCmdArgs(args)...).RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
}
}
func doGitPushTestRepositoryFail(dstPath string, args ...string) func(*testing.T) {
return func(t *testing.T) {
- _, _, err := git.NewCommand(git.DefaultContext, "push").AddArguments(git.ToTrustedCmdArgs(args)...).RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err := git.NewCommand("push").AddArguments(git.ToTrustedCmdArgs(args)...).RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.Error(t, err)
}
}
@@ -164,7 +163,7 @@ func doGitAddSomeCommits(dstPath, branch string) func(*testing.T) {
return func(t *testing.T) {
doGitCheckoutBranch(dstPath, branch)(t)
- assert.NoError(t, os.WriteFile(filepath.Join(dstPath, fmt.Sprintf("file-%s.txt", branch)), []byte(fmt.Sprintf("file %s", branch)), 0o644))
+ assert.NoError(t, os.WriteFile(filepath.Join(dstPath, fmt.Sprintf("file-%s.txt", branch)), []byte("file "+branch), 0o644))
assert.NoError(t, git.AddChanges(dstPath, true))
signature := git.Signature{
Email: "test@test.test",
@@ -173,35 +172,35 @@ func doGitAddSomeCommits(dstPath, branch string) func(*testing.T) {
assert.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{
Committer: &signature,
Author: &signature,
- Message: fmt.Sprintf("update %s", branch),
+ Message: "update " + branch,
}))
}
}
func doGitCreateBranch(dstPath, branch string) func(*testing.T) {
return func(t *testing.T) {
- _, _, err := git.NewCommand(git.DefaultContext, "checkout", "-b").AddDynamicArguments(branch).RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err := git.NewCommand("checkout", "-b").AddDynamicArguments(branch).RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
}
}
func doGitCheckoutBranch(dstPath string, args ...string) func(*testing.T) {
return func(t *testing.T) {
- _, _, err := git.NewCommandContextNoGlobals(git.DefaultContext, git.AllowLFSFiltersArgs()...).AddArguments("checkout").AddArguments(git.ToTrustedCmdArgs(args)...).RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err := git.NewCommandNoGlobals(git.AllowLFSFiltersArgs()...).AddArguments("checkout").AddArguments(git.ToTrustedCmdArgs(args)...).RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
}
}
func doGitMerge(dstPath string, args ...string) func(*testing.T) {
return func(t *testing.T) {
- _, _, err := git.NewCommand(git.DefaultContext, "merge").AddArguments(git.ToTrustedCmdArgs(args)...).RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err := git.NewCommand("merge").AddArguments(git.ToTrustedCmdArgs(args)...).RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
}
}
func doGitPull(dstPath string, args ...string) func(*testing.T) {
return func(t *testing.T) {
- _, _, err := git.NewCommandContextNoGlobals(git.DefaultContext, git.AllowLFSFiltersArgs()...).AddArguments("pull").AddArguments(git.ToTrustedCmdArgs(args)...).RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err := git.NewCommandNoGlobals(git.AllowLFSFiltersArgs()...).AddArguments("pull").AddArguments(git.ToTrustedCmdArgs(args)...).RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
}
}
diff --git a/tests/integration/git_lfs_ssh_test.go b/tests/integration/git_lfs_ssh_test.go
index 9cb7fd089b..64a403f513 100644
--- a/tests/integration/git_lfs_ssh_test.go
+++ b/tests/integration/git_lfs_ssh_test.go
@@ -4,7 +4,6 @@
package integration
import (
- gocontext "context"
"net/url"
"slices"
"strings"
@@ -46,7 +45,7 @@ func TestGitLFSSSH(t *testing.T) {
setting.LFS.AllowPureSSH = true
require.NoError(t, cfg.Save())
- _, _, cmdErr := git.NewCommand(gocontext.Background(), "config", "lfs.sshtransfer", "always").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, cmdErr := git.NewCommand("config", "lfs.sshtransfer", "always").RunStdString(t.Context(), &git.RunOpts{Dir: dstPath})
assert.NoError(t, cmdErr)
lfsCommitAndPushTest(t, dstPath, 10)
})
@@ -55,9 +54,14 @@ func TestGitLFSSSH(t *testing.T) {
return strings.Contains(s, "POST /api/internal/repo/user2/repo1.git/info/lfs/objects/batch")
})
countUpload := slices.ContainsFunc(routerCalls, func(s string) bool {
- return strings.Contains(s, "PUT /user2/repo1.git/info/lfs/objects/")
+ return strings.Contains(s, "PUT /api/internal/repo/user2/repo1.git/info/lfs/objects/")
+ })
+ nonAPIRequests := slices.ContainsFunc(routerCalls, func(s string) bool {
+ fields := strings.Fields(s)
+ return !strings.HasPrefix(fields[1], "/api/")
})
assert.NotZero(t, countBatch)
assert.NotZero(t, countUpload)
+ assert.Zero(t, nonAPIRequests)
})
}
diff --git a/tests/integration/git_misc_test.go b/tests/integration/git_misc_test.go
index 00ed2a766f..a5c53fd6e9 100644
--- a/tests/integration/git_misc_test.go
+++ b/tests/integration/git_misc_test.go
@@ -5,7 +5,6 @@ package integration
import (
"bytes"
- "context"
"io"
"net/url"
"sync"
@@ -91,7 +90,7 @@ func TestAgitPullPush(t *testing.T) {
dstPath := t.TempDir()
doGitClone(dstPath, u)(t)
- gitRepo, err := git.OpenRepository(context.Background(), dstPath)
+ gitRepo, err := git.OpenRepository(t.Context(), dstPath)
assert.NoError(t, err)
defer gitRepo.Close()
@@ -102,10 +101,10 @@ func TestAgitPullPush(t *testing.T) {
assert.NoError(t, err)
// push to create an agit pull request
- err = git.NewCommand(git.DefaultContext, "push", "origin",
+ err = git.NewCommand("push", "origin",
"-o", "title=test-title", "-o", "description=test-description",
"HEAD:refs/for/master/test-agit-push",
- ).Run(&git.RunOpts{Dir: dstPath})
+ ).Run(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
// check pull request exist
@@ -119,20 +118,122 @@ func TestAgitPullPush(t *testing.T) {
assert.NoError(t, err)
// push 2
- err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master/test-agit-push").Run(&git.RunOpts{Dir: dstPath})
+ err = git.NewCommand("push", "origin", "HEAD:refs/for/master/test-agit-push").Run(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
// reset to first commit
- err = git.NewCommand(git.DefaultContext, "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath})
+ err = git.NewCommand("reset", "--hard", "HEAD~1").Run(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
// test force push without confirm
- _, stderr, err := git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master/test-agit-push").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, stderr, err := git.NewCommand("push", "origin", "HEAD:refs/for/master/test-agit-push").RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.Error(t, err)
assert.Contains(t, stderr, "[remote rejected] HEAD -> refs/for/master/test-agit-push (request `force-push` push option)")
// test force push with confirm
- err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master/test-agit-push", "-o", "force-push").Run(&git.RunOpts{Dir: dstPath})
+ err = git.NewCommand("push", "origin", "HEAD:refs/for/master/test-agit-push", "-o", "force-push").Run(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
})
}
+
+func TestAgitReviewStaleness(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ baseAPITestContext := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+
+ u.Path = baseAPITestContext.GitPath()
+ u.User = url.UserPassword("user2", userPassword)
+
+ dstPath := t.TempDir()
+ doGitClone(dstPath, u)(t)
+
+ gitRepo, err := git.OpenRepository(t.Context(), dstPath)
+ assert.NoError(t, err)
+ defer gitRepo.Close()
+
+ doGitCreateBranch(dstPath, "test-agit-review")
+
+ // Create initial commit
+ _, err = generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "initial-")
+ assert.NoError(t, err)
+
+ // create PR via agit
+ err = git.NewCommand("push", "origin",
+ "-o", "title=Test agit Review Staleness", "-o", "description=Testing review staleness",
+ "HEAD:refs/for/master/test-agit-review",
+ ).Run(git.DefaultContext, &git.RunOpts{Dir: dstPath})
+ assert.NoError(t, err)
+
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
+ BaseRepoID: 1,
+ Flow: issues_model.PullRequestFlowAGit,
+ HeadBranch: "user2/test-agit-review",
+ })
+ assert.NoError(t, pr.LoadIssue(db.DefaultContext))
+
+ // Get initial commit ID for the review
+ initialCommitID := pr.HeadCommitID
+ t.Logf("Initial commit ID: %s", initialCommitID)
+
+ // Create a review on the PR (as user1 reviewing user2's PR)
+ reviewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+ review, err := issues_model.CreateReview(db.DefaultContext, issues_model.CreateReviewOptions{
+ Type: issues_model.ReviewTypeApprove,
+ Reviewer: reviewer,
+ Issue: pr.Issue,
+ CommitID: initialCommitID,
+ Content: "LGTM! Looks good to merge.",
+ Official: false,
+ })
+ assert.NoError(t, err)
+ assert.False(t, review.Stale, "New review should not be stale")
+
+ // Verify review exists and is not stale
+ reviews, err := issues_model.FindReviews(db.DefaultContext, issues_model.FindReviewOptions{
+ IssueID: pr.IssueID,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, reviews, 1)
+ assert.Equal(t, initialCommitID, reviews[0].CommitID)
+ assert.False(t, reviews[0].Stale, "Review should not be stale initially")
+
+ // Create a new commit and update the agit PR
+ _, err = generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "updated-")
+ assert.NoError(t, err)
+
+ err = git.NewCommand("push", "origin", "HEAD:refs/for/master/test-agit-review").Run(git.DefaultContext, &git.RunOpts{Dir: dstPath})
+ assert.NoError(t, err)
+
+ // Reload PR to get updated commit ID
+ pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{
+ BaseRepoID: 1,
+ Flow: issues_model.PullRequestFlowAGit,
+ HeadBranch: "user2/test-agit-review",
+ })
+ assert.NoError(t, pr.LoadIssue(db.DefaultContext))
+
+ // For AGit PRs, HeadCommitID must be loaded from git references
+ baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ baseGitRepo, err := gitrepo.OpenRepository(db.DefaultContext, baseRepo)
+ assert.NoError(t, err)
+ defer baseGitRepo.Close()
+
+ updatedCommitID, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
+ assert.NoError(t, err)
+ t.Logf("Updated commit ID: %s", updatedCommitID)
+
+ // Verify the PR was updated with new commit
+ assert.NotEqual(t, initialCommitID, updatedCommitID, "PR should have new commit ID after update")
+
+ // Check that the review is now marked as stale
+ reviews, err = issues_model.FindReviews(db.DefaultContext, issues_model.FindReviewOptions{
+ IssueID: pr.IssueID,
+ })
+ assert.NoError(t, err)
+ assert.Len(t, reviews, 1)
+
+ assert.True(t, reviews[0].Stale, "Review should be marked as stale after AGit PR update")
+
+ // The review commit ID should remain the same (pointing to the original commit)
+ assert.Equal(t, initialCommitID, reviews[0].CommitID, "Review commit ID should remain unchanged and point to original commit")
+ })
+}
diff --git a/tests/integration/git_push_test.go b/tests/integration/git_push_test.go
index e68f8bfce2..d716847b54 100644
--- a/tests/integration/git_push_test.go
+++ b/tests/integration/git_push_test.go
@@ -27,7 +27,7 @@ func TestGitPush(t *testing.T) {
func testGitPush(t *testing.T, u *url.URL) {
t.Run("Push branches at once", func(t *testing.T) {
runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
- for i := 0; i < 100; i++ {
+ for i := range 100 {
branchName := fmt.Sprintf("branch-%d", i)
pushed = append(pushed, branchName)
doGitCreateBranch(gitPath, branchName)(t)
@@ -40,7 +40,7 @@ func testGitPush(t *testing.T, u *url.URL) {
t.Run("Push branches exists", func(t *testing.T) {
runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
- for i := 0; i < 10; i++ {
+ for i := range 10 {
branchName := fmt.Sprintf("branch-%d", i)
if i < 5 {
pushed = append(pushed, branchName)
@@ -54,7 +54,7 @@ func testGitPush(t *testing.T, u *url.URL) {
pushed = pushed[:0]
// do some changes for the first 5 branches created above
- for i := 0; i < 5; i++ {
+ for i := range 5 {
branchName := fmt.Sprintf("branch-%d", i)
pushed = append(pushed, branchName)
@@ -75,7 +75,7 @@ func testGitPush(t *testing.T, u *url.URL) {
t.Run("Push branches one by one", func(t *testing.T) {
runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
- for i := 0; i < 100; i++ {
+ for i := range 100 {
branchName := fmt.Sprintf("branch-%d", i)
doGitCreateBranch(gitPath, branchName)(t)
doGitPushTestRepository(gitPath, "origin", branchName)(t)
@@ -101,14 +101,14 @@ func testGitPush(t *testing.T, u *url.URL) {
doGitPushTestRepository(gitPath, "origin", "master")(t) // make sure master is the default branch instead of a branch we are going to delete
pushed = append(pushed, "master")
- for i := 0; i < 100; i++ {
+ for i := range 100 {
branchName := fmt.Sprintf("branch-%d", i)
pushed = append(pushed, branchName)
doGitCreateBranch(gitPath, branchName)(t)
}
doGitPushTestRepository(gitPath, "origin", "--all")(t)
- for i := 0; i < 10; i++ {
+ for i := range 10 {
branchName := fmt.Sprintf("branch-%d", i)
doGitPushTestRepository(gitPath, "origin", "--delete", branchName)(t)
deleted = append(deleted, branchName)
@@ -170,7 +170,7 @@ func runTestGitPush(t *testing.T, u *url.URL, gitOperation func(t *testing.T, gi
dbBranches := make([]*git_model.Branch, 0)
require.NoError(t, db.GetEngine(db.DefaultContext).Where("repo_id=?", repo.ID).Find(&dbBranches))
- assert.Equalf(t, len(pushedBranches), len(dbBranches), "mismatched number of branches in db")
+ assert.Lenf(t, dbBranches, len(pushedBranches), "mismatched number of branches in db")
dbBranchesMap := make(map[string]*git_model.Branch, len(dbBranches))
for _, branch := range dbBranches {
dbBranchesMap[branch.Name] = branch
@@ -191,7 +191,7 @@ func runTestGitPush(t *testing.T, u *url.URL, gitOperation func(t *testing.T, gi
assert.Equal(t, commitID, branch.CommitID)
}
- require.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, repo.ID))
+ require.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, repo.ID))
}
func TestPushPullRefs(t *testing.T) {
@@ -204,8 +204,8 @@ func TestPushPullRefs(t *testing.T) {
dstPath := t.TempDir()
doGitClone(dstPath, u)(t)
- cmd := git.NewCommand(git.DefaultContext, "push", "--delete", "origin", "refs/pull/2/head")
- stdout, stderr, err := cmd.RunStdString(&git.RunOpts{
+ cmd := git.NewCommand("push", "--delete", "origin", "refs/pull/2/head")
+ stdout, stderr, err := cmd.RunStdString(git.DefaultContext, &git.RunOpts{
Dir: dstPath,
})
assert.Error(t, err)
diff --git a/tests/integration/git_smart_http_test.go b/tests/integration/git_smart_http_test.go
index 15336b9b81..e984fd3aad 100644
--- a/tests/integration/git_smart_http_test.go
+++ b/tests/integration/git_smart_http_test.go
@@ -9,60 +9,90 @@ import (
"net/url"
"testing"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
+ "code.gitea.io/gitea/modules/util"
+
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestGitSmartHTTP(t *testing.T) {
- onGiteaRun(t, testGitSmartHTTP)
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ testGitSmartHTTP(t, u)
+ testRenamedRepoRedirect(t)
+ })
}
func testGitSmartHTTP(t *testing.T, u *url.URL) {
kases := []struct {
- p string
- code int
+ method, path string
+ code int
}{
{
- p: "user2/repo1/info/refs",
+ path: "user2/repo1/info/refs",
code: http.StatusOK,
},
{
- p: "user2/repo1/HEAD",
+ method: "HEAD",
+ path: "user2/repo1/info/refs",
+ code: http.StatusOK,
+ },
+ {
+ path: "user2/repo1/HEAD",
code: http.StatusOK,
},
{
- p: "user2/repo1/objects/info/alternates",
+ path: "user2/repo1/objects/info/alternates",
code: http.StatusNotFound,
},
{
- p: "user2/repo1/objects/info/http-alternates",
+ path: "user2/repo1/objects/info/http-alternates",
code: http.StatusNotFound,
},
{
- p: "user2/repo1/../../custom/conf/app.ini",
+ path: "user2/repo1/../../custom/conf/app.ini",
code: http.StatusNotFound,
},
{
- p: "user2/repo1/objects/info/../../../../custom/conf/app.ini",
+ path: "user2/repo1/objects/info/../../../../custom/conf/app.ini",
code: http.StatusNotFound,
},
{
- p: `user2/repo1/objects/info/..\..\..\..\custom\conf\app.ini`,
+ path: `user2/repo1/objects/info/..\..\..\..\custom\conf\app.ini`,
code: http.StatusBadRequest,
},
}
for _, kase := range kases {
- t.Run(kase.p, func(t *testing.T) {
- p := u.String() + kase.p
- req, err := http.NewRequest("GET", p, nil)
- assert.NoError(t, err)
+ t.Run(kase.path, func(t *testing.T) {
+ req, err := http.NewRequest(util.IfZero(kase.method, "GET"), u.String()+kase.path, nil)
+ require.NoError(t, err)
req.SetBasicAuth("user2", userPassword)
resp, err := http.DefaultClient.Do(req)
- assert.NoError(t, err)
+ require.NoError(t, err)
defer resp.Body.Close()
- assert.EqualValues(t, kase.code, resp.StatusCode)
+ assert.Equal(t, kase.code, resp.StatusCode)
_, err = io.ReadAll(resp.Body)
- assert.NoError(t, err)
+ require.NoError(t, err)
})
}
}
+
+func testRenamedRepoRedirect(t *testing.T) {
+ defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, true)()
+
+ // git client requires to get a 301 redirect response before 401 unauthorized response
+ req := NewRequest(t, "GET", "/user2/oldrepo1/info/refs")
+ resp := MakeRequest(t, req, http.StatusMovedPermanently)
+ redirect := resp.Header().Get("Location")
+ assert.Equal(t, "/user2/repo1/info/refs", redirect)
+
+ req = NewRequest(t, "GET", redirect)
+ resp = MakeRequest(t, req, http.StatusUnauthorized)
+ assert.Equal(t, "Unauthorized\n", resp.Body.String())
+
+ req = NewRequest(t, "GET", redirect).AddBasicAuth("user2")
+ resp = MakeRequest(t, req, http.StatusOK)
+ assert.Contains(t, resp.Body.String(), "65f1bf27bc3bf70f64657658635e66094edbcb4d\trefs/tags/v1.1")
+}
diff --git a/tests/integration/gpg_git_test.go b/tests/integration/gpg_ssh_git_test.go
index acfe70026e..56f9f87783 100644
--- a/tests/integration/gpg_git_test.go
+++ b/tests/integration/gpg_ssh_git_test.go
@@ -4,7 +4,10 @@
package integration
import (
+ "crypto/ed25519"
+ "crypto/rand"
"encoding/base64"
+ "encoding/pem"
"fmt"
"net/url"
"os"
@@ -22,6 +25,8 @@ import (
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/armor"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "golang.org/x/crypto/ssh"
)
func TestGPGGit(t *testing.T) {
@@ -33,9 +38,7 @@ func TestGPGGit(t *testing.T) {
// Need to create a root key
rootKeyPair, err := importTestingKey()
- if !assert.NoError(t, err, "importTestingKey") {
- return
- }
+ require.NoError(t, err, "importTestingKey")
defer test.MockVariableValue(&setting.Repository.Signing.SigningKey, rootKeyPair.PrimaryKey.KeyIdShortString())()
defer test.MockVariableValue(&setting.Repository.Signing.SigningName, "gitea")()
@@ -43,6 +46,37 @@ func TestGPGGit(t *testing.T) {
defer test.MockVariableValue(&setting.Repository.Signing.InitialCommit, []string{"never"})()
defer test.MockVariableValue(&setting.Repository.Signing.CRUDActions, []string{"never"})()
+ testGitSigning(t)
+}
+
+func TestSSHGit(t *testing.T) {
+ tmpDir := t.TempDir() // use a temp dir to store the SSH keys
+ err := os.Chmod(tmpDir, 0o700)
+ assert.NoError(t, err)
+
+ pub, priv, err := ed25519.GenerateKey(rand.Reader)
+ require.NoError(t, err, "ed25519.GenerateKey")
+ sshPubKey, err := ssh.NewPublicKey(pub)
+ require.NoError(t, err, "ssh.NewPublicKey")
+
+ err = os.WriteFile(tmpDir+"/id_ed25519.pub", ssh.MarshalAuthorizedKey(sshPubKey), 0o600)
+ require.NoError(t, err, "os.WriteFile id_ed25519.pub")
+ block, err := ssh.MarshalPrivateKey(priv, "")
+ require.NoError(t, err, "ssh.MarshalPrivateKey")
+ err = os.WriteFile(tmpDir+"/id_ed25519", pem.EncodeToMemory(block), 0o600)
+ require.NoError(t, err, "os.WriteFile id_ed25519")
+
+ defer test.MockVariableValue(&setting.Repository.Signing.SigningKey, tmpDir+"/id_ed25519.pub")()
+ defer test.MockVariableValue(&setting.Repository.Signing.SigningName, "gitea")()
+ defer test.MockVariableValue(&setting.Repository.Signing.SigningEmail, "gitea@fake.local")()
+ defer test.MockVariableValue(&setting.Repository.Signing.SigningFormat, "ssh")()
+ defer test.MockVariableValue(&setting.Repository.Signing.InitialCommit, []string{"never"})()
+ defer test.MockVariableValue(&setting.Repository.Signing.CRUDActions, []string{"never"})()
+
+ testGitSigning(t)
+}
+
+func testGitSigning(t *testing.T) {
username := "user2"
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: username})
baseAPITestContext := NewAPITestContext(t, username, "repo1")
@@ -100,26 +134,14 @@ func TestGPGGit(t *testing.T) {
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
t.Run("CreateCRUDFile-Always", crudActionCreateFile(
t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
- assert.NotNil(t, response.Verification)
- if response.Verification == nil {
- assert.FailNow(t, "no verification provided with response! %v", response)
- }
- assert.True(t, response.Verification.Verified)
- if !response.Verification.Verified {
- t.FailNow()
- }
+ require.NotNil(t, response.Verification, "no verification provided with response! %v", response)
+ require.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
}))
t.Run("CreateCRUDFile-ParentSigned-always", crudActionCreateFile(
t, testCtx, user, "parentsigned", "parentsigned-always", "signed-parent2.txt", func(t *testing.T, response api.FileResponse) {
- assert.NotNil(t, response.Verification)
- if response.Verification == nil {
- assert.FailNow(t, "no verification provided with response! %v", response)
- }
- assert.True(t, response.Verification.Verified)
- if !response.Verification.Verified {
- t.FailNow()
- }
+ require.NotNil(t, response.Verification, "no verification provided with response! %v", response)
+ require.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
}))
})
@@ -130,14 +152,8 @@ func TestGPGGit(t *testing.T) {
testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
t.Run("CreateCRUDFile-Always-ParentSigned", crudActionCreateFile(
t, testCtx, user, "always", "always-parentsigned", "signed-always-parentsigned.txt", func(t *testing.T, response api.FileResponse) {
- assert.NotNil(t, response.Verification)
- if response.Verification == nil {
- assert.FailNow(t, "no verification provided with response! %v", response)
- }
- assert.True(t, response.Verification.Verified)
- if !response.Verification.Verified {
- t.FailNow()
- }
+ require.NotNil(t, response.Verification, "no verification provided with response! %v", response)
+ require.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
}))
})
@@ -148,18 +164,9 @@ func TestGPGGit(t *testing.T) {
testCtx := NewAPITestContext(t, username, "initial-always", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
- assert.NotNil(t, branch.Commit)
- if branch.Commit == nil {
- assert.FailNow(t, "no commit provided with branch! %v", branch)
- }
- assert.NotNil(t, branch.Commit.Verification)
- if branch.Commit.Verification == nil {
- assert.FailNow(t, "no verification provided with branch commit! %v", branch.Commit)
- }
- assert.True(t, branch.Commit.Verification.Verified)
- if !branch.Commit.Verification.Verified {
- t.FailNow()
- }
+ require.NotNil(t, branch.Commit, "no commit provided with branch! %v", branch)
+ require.NotNil(t, branch.Commit.Verification, "no verification provided with branch commit! %v", branch.Commit)
+ require.True(t, branch.Commit.Verification.Verified)
assert.Equal(t, "gitea@fake.local", branch.Commit.Verification.Signer.Email)
}))
})
@@ -182,11 +189,7 @@ func TestGPGGit(t *testing.T) {
t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) {
- assert.True(t, response.Verification.Verified)
- if !response.Verification.Verified {
- t.FailNow()
- return
- }
+ require.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
}))
})
@@ -198,11 +201,7 @@ func TestGPGGit(t *testing.T) {
t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
t.Run("CreateCRUDFile-Always", crudActionCreateFile(
t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
- assert.True(t, response.Verification.Verified)
- if !response.Verification.Verified {
- t.FailNow()
- return
- }
+ require.True(t, response.Verification.Verified)
assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
}))
})
@@ -274,7 +273,7 @@ func crudActionCreateFile(_ *testing.T, ctx APITestContext, user *user_model.Use
Email: user.Email,
},
},
- ContentBase64: base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("This is new text for %s", path))),
+ ContentBase64: base64.StdEncoding.EncodeToString([]byte("This is new text for " + path)),
}, callback...)
}
diff --git a/tests/integration/html_helper.go b/tests/integration/html_helper.go
index 2217ddec2e..4d589b32e7 100644
--- a/tests/integration/html_helper.go
+++ b/tests/integration/html_helper.go
@@ -42,12 +42,13 @@ func (doc *HTMLDoc) GetCSRF() string {
return doc.GetInputValueByName("_csrf")
}
-// AssertElement check if element by selector exists or does not exist depending on checkExists
-func (doc *HTMLDoc) AssertElement(t testing.TB, selector string, checkExists bool) {
+// AssertHTMLElement check if the element by selector exists or does not exist depending on checkExists
+func AssertHTMLElement[T int | bool](t testing.TB, doc *HTMLDoc, selector string, checkExists T) {
sel := doc.doc.Find(selector)
- if checkExists {
- assert.Equal(t, 1, sel.Length())
- } else {
- assert.Equal(t, 0, sel.Length())
+ switch v := any(checkExists).(type) {
+ case bool:
+ assert.Equal(t, v, sel.Length() > 0)
+ case int:
+ assert.Equal(t, v, sel.Length())
}
}
diff --git a/tests/integration/incoming_email_test.go b/tests/integration/incoming_email_test.go
index e968a2956e..6ccf82a9eb 100644
--- a/tests/integration/incoming_email_test.go
+++ b/tests/integration/incoming_email_test.go
@@ -50,12 +50,12 @@ func TestIncomingEmail(t *testing.T) {
ref, err := incoming_payload.GetReferenceFromPayload(db.DefaultContext, issuePayload)
assert.NoError(t, err)
assert.IsType(t, ref, new(issues_model.Issue))
- assert.EqualValues(t, issue.ID, ref.(*issues_model.Issue).ID)
+ assert.Equal(t, issue.ID, ref.(*issues_model.Issue).ID)
ref, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, commentPayload)
assert.NoError(t, err)
assert.IsType(t, ref, new(issues_model.Comment))
- assert.EqualValues(t, comment.ID, ref.(*issues_model.Comment).ID)
+ assert.Equal(t, comment.ID, ref.(*issues_model.Comment).ID)
})
t.Run("Token", func(t *testing.T) {
diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go
index 46b93b0a10..5896a97ef1 100644
--- a/tests/integration/integration_test.go
+++ b/tests/integration/integration_test.go
@@ -1,7 +1,7 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-//nolint:forbidigo
+//nolint:forbidigo // use of print functions is allowed in tests
package integration
import (
@@ -29,6 +29,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/routers"
gitea_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/tests"
@@ -118,12 +119,11 @@ type TestSession struct {
jar http.CookieJar
}
-func (s *TestSession) GetCookie(name string) *http.Cookie {
+func (s *TestSession) GetRawCookie(name string) *http.Cookie {
baseURL, err := url.Parse(setting.AppURL)
if err != nil {
return nil
}
-
for _, c := range s.jar.Cookies(baseURL) {
if c.Name == name {
return c
@@ -132,8 +132,25 @@ func (s *TestSession) GetCookie(name string) *http.Cookie {
return nil
}
+func (s *TestSession) GetSiteCookie(name string) string {
+ c := s.GetRawCookie(name)
+ if c != nil {
+ v, _ := url.QueryUnescape(c.Value)
+ return v
+ }
+ return ""
+}
+
+func (s *TestSession) GetCookieFlashMessage() *middleware.Flash {
+ cookie := s.GetSiteCookie(gitea_context.CookieNameFlash)
+ return middleware.ParseCookieFlashMessage(cookie)
+}
+
func (s *TestSession) MakeRequest(t testing.TB, rw *RequestWrapper, expectedStatus int) *httptest.ResponseRecorder {
t.Helper()
+ if s == nil {
+ return MakeRequest(t, rw, expectedStatus)
+ }
req := rw.Request
baseURL, err := url.Parse(setting.AppURL)
assert.NoError(t, err)
@@ -235,55 +252,19 @@ func loginUserWithPassword(t testing.TB, userName, password string) *TestSession
// token has to be unique this counter take care of
var tokenCounter int64
-// getTokenForLoggedInUser returns a token for a logged in user.
-// The scope is an optional list of snake_case strings like the frontend form fields,
-// but without the "scope_" prefix.
+// getTokenForLoggedInUser returns a token for a logged-in user.
func getTokenForLoggedInUser(t testing.TB, session *TestSession, scopes ...auth.AccessTokenScope) string {
t.Helper()
- var token string
- req := NewRequest(t, "GET", "/user/settings/applications")
- resp := session.MakeRequest(t, req, http.StatusOK)
- var csrf string
- for _, cookie := range resp.Result().Cookies() {
- if cookie.Name != "_csrf" {
- continue
- }
- csrf = cookie.Value
- break
- }
- if csrf == "" {
- doc := NewHTMLParser(t, resp.Body)
- csrf = doc.GetCSRF()
- }
- assert.NotEmpty(t, csrf)
urlValues := url.Values{}
- urlValues.Add("_csrf", csrf)
+ urlValues.Add("_csrf", GetUserCSRFToken(t, session))
urlValues.Add("name", fmt.Sprintf("api-testing-token-%d", atomic.AddInt64(&tokenCounter, 1)))
for _, scope := range scopes {
- urlValues.Add("scope", string(scope))
+ urlValues.Add("scope-dummy", string(scope)) // it only needs to start with "scope-" to be accepted
}
- req = NewRequestWithURLValues(t, "POST", "/user/settings/applications", urlValues)
- resp = session.MakeRequest(t, req, http.StatusSeeOther)
-
- // Log the flash values on failure
- if !assert.Equal(t, []string{"/user/settings/applications"}, resp.Result().Header["Location"]) {
- for _, cookie := range resp.Result().Cookies() {
- if cookie.Name != gitea_context.CookieNameFlash {
- continue
- }
- flash, _ := url.ParseQuery(cookie.Value)
- for key, value := range flash {
- t.Logf("Flash %q: %q", key, value)
- }
- }
- }
-
- req = NewRequest(t, "GET", "/user/settings/applications")
- resp = session.MakeRequest(t, req, http.StatusOK)
- htmlDoc := NewHTMLParser(t, resp.Body)
- token = htmlDoc.doc.Find(".ui.info p").Text()
- assert.NotEmpty(t, token)
- return token
+ req := NewRequestWithURLValues(t, "POST", "/user/settings/applications", urlValues)
+ session.MakeRequest(t, req, http.StatusSeeOther)
+ flashes := session.GetCookieFlashMessage()
+ return flashes.InfoMsg
}
type RequestWrapper struct {
@@ -332,7 +313,7 @@ func NewRequestWithValues(t testing.TB, method, urlStr string, values map[string
func NewRequestWithURLValues(t testing.TB, method, urlStr string, urlValues url.Values) *RequestWrapper {
t.Helper()
- return NewRequestWithBody(t, method, urlStr, bytes.NewBufferString(urlValues.Encode())).
+ return NewRequestWithBody(t, method, urlStr, strings.NewReader(urlValues.Encode())).
SetHeader("Content-Type", "application/x-www-form-urlencoded")
}
@@ -382,7 +363,7 @@ func MakeRequestNilResponseRecorder(t testing.TB, rw *RequestWrapper, expectedSt
recorder := NewNilResponseRecorder()
testWebRoutes.ServeHTTP(recorder, req)
if expectedStatus != NoExpectedStatus {
- if !assert.EqualValues(t, expectedStatus, recorder.Code,
+ if !assert.Equal(t, expectedStatus, recorder.Code,
"Request: %s %s", req.Method, req.URL.String()) {
logUnexpectedResponse(t, &recorder.ResponseRecorder)
}
@@ -396,7 +377,7 @@ func MakeRequestNilResponseHashSumRecorder(t testing.TB, rw *RequestWrapper, exp
recorder := NewNilResponseHashSumRecorder()
testWebRoutes.ServeHTTP(recorder, req)
if expectedStatus != NoExpectedStatus {
- if !assert.EqualValues(t, expectedStatus, recorder.Code,
+ if !assert.Equal(t, expectedStatus, recorder.Code,
"Request: %s %s", req.Method, req.URL.String()) {
logUnexpectedResponse(t, &recorder.ResponseRecorder)
}
@@ -458,9 +439,9 @@ func VerifyJSONSchema(t testing.TB, resp *httptest.ResponseRecorder, schemaFile
// GetUserCSRFToken returns CSRF token for current user
func GetUserCSRFToken(t testing.TB, session *TestSession) string {
t.Helper()
- cookie := session.GetCookie("_csrf")
+ cookie := session.GetSiteCookie("_csrf")
require.NotEmpty(t, cookie)
- return cookie.Value
+ return cookie
}
// GetUserCSRFToken returns CSRF token for anonymous user (not logged in)
diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go
index 4617c5f89a..b5dca58357 100644
--- a/tests/integration/issue_test.go
+++ b/tests/integration/issue_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "context"
"fmt"
"html/template"
"net/http"
@@ -33,7 +32,7 @@ import (
func getIssuesSelection(t testing.TB, htmlDoc *HTMLDoc) *goquery.Selection {
issueList := htmlDoc.doc.Find("#issue-list")
- assert.EqualValues(t, 1, issueList.Length())
+ assert.Equal(t, 1, issueList.Length())
return issueList.Find(".flex-item").Find(".issue-title")
}
@@ -77,19 +76,16 @@ func TestViewIssuesSortByType(t *testing.T) {
htmlDoc := NewHTMLParser(t, resp.Body)
issuesSelection := getIssuesSelection(t, htmlDoc)
- expectedNumIssues := unittest.GetCount(t,
+ expectedNumIssues := min(unittest.GetCount(t,
&issues_model.Issue{RepoID: repo.ID, PosterID: user.ID},
unittest.Cond("is_closed=?", false),
unittest.Cond("is_pull=?", false),
- )
- if expectedNumIssues > setting.UI.IssuePagingNum {
- expectedNumIssues = setting.UI.IssuePagingNum
- }
- assert.EqualValues(t, expectedNumIssues, issuesSelection.Length())
+ ), setting.UI.IssuePagingNum)
+ assert.Equal(t, expectedNumIssues, issuesSelection.Length())
issuesSelection.Each(func(_ int, selection *goquery.Selection) {
issue := getIssue(t, repo.ID, selection)
- assert.EqualValues(t, user.ID, issue.PosterID)
+ assert.Equal(t, user.ID, issue.PosterID)
})
}
@@ -101,7 +97,7 @@ func TestViewIssuesKeyword(t *testing.T) {
RepoID: repo.ID,
Index: 1,
})
- issues.UpdateIssueIndexer(context.Background(), issue.ID)
+ issues.UpdateIssueIndexer(t.Context(), issue.ID)
time.Sleep(time.Second * 1)
const keyword = "first"
req := NewRequestf(t, "GET", "%s/issues?q=%s", repo.Link(), keyword)
@@ -109,7 +105,7 @@ func TestViewIssuesKeyword(t *testing.T) {
htmlDoc := NewHTMLParser(t, resp.Body)
issuesSelection := getIssuesSelection(t, htmlDoc)
- assert.EqualValues(t, 1, issuesSelection.Length())
+ assert.Equal(t, 1, issuesSelection.Length())
issuesSelection.Each(func(_ int, selection *goquery.Selection) {
issue := getIssue(t, repo.ID, selection)
assert.False(t, issue.IsClosed)
@@ -152,6 +148,22 @@ func testNewIssue(t *testing.T, session *TestSession, user, repo, title, content
return issueURL
}
+func testIssueDelete(t *testing.T, session *TestSession, issueURL string) {
+ req := NewRequestWithValues(t, "POST", path.Join(issueURL, "delete"), map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ })
+ session.MakeRequest(t, req, http.StatusSeeOther)
+}
+
+func testIssueAssign(t *testing.T, session *TestSession, repoLink string, issueID, assigneeID int64) {
+ req := NewRequestWithValues(t, "POST", fmt.Sprintf(repoLink+"/issues/assignee?issue_ids=%d", issueID), map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ "id": strconv.FormatInt(assigneeID, 10),
+ "action": "", // empty action means assign
+ })
+ session.MakeRequest(t, req, http.StatusOK)
+}
+
func testIssueAddComment(t *testing.T, session *TestSession, issueURL, content, status string) int64 {
req := NewRequest(t, "GET", issueURL)
resp := session.MakeRequest(t, req, http.StatusOK)
@@ -174,7 +186,7 @@ func testIssueAddComment(t *testing.T, session *TestSession, issueURL, content,
htmlDoc = NewHTMLParser(t, resp.Body)
- val := htmlDoc.doc.Find(".comment-list .comment .render-content p").Eq(commentCount).Text()
+ val := strings.TrimSpace(htmlDoc.doc.Find(".comment-list .comment .render-content").Eq(commentCount).Text())
assert.Equal(t, content, val)
idAttr, has := htmlDoc.doc.Find(".comment-list .comment").Eq(commentCount).Attr("id")
@@ -185,6 +197,15 @@ func testIssueAddComment(t *testing.T, session *TestSession, issueURL, content,
return int64(id)
}
+func testIssueChangeMilestone(t *testing.T, session *TestSession, repoLink string, issueID, milestoneID int64) {
+ req := NewRequestWithValues(t, "POST", fmt.Sprintf(repoLink+"/issues/milestone?issue_ids=%d", issueID), map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ "id": strconv.FormatInt(milestoneID, 10),
+ })
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ assert.Equal(t, `{"ok":true}`, strings.TrimSpace(resp.Body.String()))
+}
+
func TestNewIssue(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user2")
@@ -196,21 +217,21 @@ func TestEditIssue(t *testing.T) {
session := loginUser(t, "user2")
issueURL := testNewIssue(t, session, "user2", "repo1", "Title", "Description")
- req := NewRequestWithValues(t, "POST", fmt.Sprintf("%s/content", issueURL), map[string]string{
+ req := NewRequestWithValues(t, "POST", issueURL+"/content", map[string]string{
"_csrf": GetUserCSRFToken(t, session),
"content": "modified content",
"context": fmt.Sprintf("/%s/%s", "user2", "repo1"),
})
session.MakeRequest(t, req, http.StatusOK)
- req = NewRequestWithValues(t, "POST", fmt.Sprintf("%s/content", issueURL), map[string]string{
+ req = NewRequestWithValues(t, "POST", issueURL+"/content", map[string]string{
"_csrf": GetUserCSRFToken(t, session),
"content": "modified content",
"context": fmt.Sprintf("/%s/%s", "user2", "repo1"),
})
session.MakeRequest(t, req, http.StatusBadRequest)
- req = NewRequestWithValues(t, "POST", fmt.Sprintf("%s/content", issueURL), map[string]string{
+ req = NewRequestWithValues(t, "POST", issueURL+"/content", map[string]string{
"_csrf": GetUserCSRFToken(t, session),
"content": "modified content",
"content_version": "1",
@@ -474,10 +495,7 @@ func TestSearchIssues(t *testing.T) {
session := loginUser(t, "user2")
- expectedIssueCount := 20 // from the fixtures
- if expectedIssueCount > setting.UI.IssuePagingNum {
- expectedIssueCount = setting.UI.IssuePagingNum
- }
+ expectedIssueCount := min(20, setting.UI.IssuePagingNum) // 20 is from the fixtures
link, _ := url.Parse("/issues/search")
req := NewRequest(t, "GET", link.String())
@@ -511,7 +529,7 @@ func TestSearchIssues(t *testing.T) {
req = NewRequest(t, "GET", link.String())
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiIssues)
- assert.EqualValues(t, "22", resp.Header().Get("X-Total-Count"))
+ assert.Equal(t, "22", resp.Header().Get("X-Total-Count"))
assert.Len(t, apiIssues, 20)
query.Add("limit", "5")
@@ -519,7 +537,7 @@ func TestSearchIssues(t *testing.T) {
req = NewRequest(t, "GET", link.String())
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiIssues)
- assert.EqualValues(t, "22", resp.Header().Get("X-Total-Count"))
+ assert.Equal(t, "22", resp.Header().Get("X-Total-Count"))
assert.Len(t, apiIssues, 5)
query = url.Values{"assigned": {"true"}, "state": {"all"}}
@@ -568,10 +586,7 @@ func TestSearchIssues(t *testing.T) {
func TestSearchIssuesWithLabels(t *testing.T) {
defer tests.PrepareTestEnv(t)()
- expectedIssueCount := 20 // from the fixtures
- if expectedIssueCount > setting.UI.IssuePagingNum {
- expectedIssueCount = setting.UI.IssuePagingNum
- }
+ expectedIssueCount := min(20, setting.UI.IssuePagingNum) // 20 is from the fixtures
session := loginUser(t, "user1")
link, _ := url.Parse("/issues/search")
@@ -646,7 +661,7 @@ func TestGetIssueInfo(t *testing.T) {
}
DecodeJSON(t, resp, &respStruct)
- assert.EqualValues(t, issue.ID, respStruct.ConvertedIssue.ID)
+ assert.Equal(t, issue.ID, respStruct.ConvertedIssue.ID)
assert.Contains(t, string(respStruct.RenderedLabels), `"labels-list"`)
}
@@ -666,7 +681,7 @@ func TestUpdateIssueDeadline(t *testing.T) {
req := NewRequestWithValues(t, "POST", urlStr, map[string]string{"deadline": "2022-04-06"})
session.MakeRequest(t, req, http.StatusOK)
issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10})
- assert.EqualValues(t, "2022-04-06", issueAfter.DeadlineUnix.FormatDate())
+ assert.Equal(t, "2022-04-06", issueAfter.DeadlineUnix.FormatDate())
req = NewRequestWithValues(t, "POST", urlStr, map[string]string{"deadline": ""})
session.MakeRequest(t, req, http.StatusOK)
@@ -687,8 +702,8 @@ func TestIssueReferenceURL(t *testing.T) {
// the "reference" uses relative URLs, then JS code will convert them to absolute URLs for current origin, in case users are using multiple domains
ref, _ := htmlDoc.Find(`.timeline-item.comment.first .reference-issue`).Attr("data-reference")
- assert.EqualValues(t, "/user2/repo1/issues/1#issue-1", ref)
+ assert.Equal(t, "/user2/repo1/issues/1#issue-1", ref)
ref, _ = htmlDoc.Find(`.timeline-item.comment:not(.first) .reference-issue`).Attr("data-reference")
- assert.EqualValues(t, "/user2/repo1/issues/1#issuecomment-2", ref)
+ assert.Equal(t, "/user2/repo1/issues/1#issuecomment-2", ref)
}
diff --git a/tests/integration/lfs_local_endpoint_test.go b/tests/integration/lfs_local_endpoint_test.go
index d42888bbe1..e67f0712a3 100644
--- a/tests/integration/lfs_local_endpoint_test.go
+++ b/tests/integration/lfs_local_endpoint_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "fmt"
"net/url"
"os"
"path/filepath"
@@ -41,55 +40,55 @@ func TestDetermineLocalEndpoint(t *testing.T) {
{
cloneurl: root,
lfsurl: "",
- expected: str2url(fmt.Sprintf("file://%s", root)),
+ expected: str2url("file://" + root),
},
// case 1
{
cloneurl: root,
lfsurl: lfsroot,
- expected: str2url(fmt.Sprintf("file://%s", lfsroot)),
+ expected: str2url("file://" + lfsroot),
},
// case 2
{
cloneurl: "https://git.com/repo.git",
lfsurl: lfsroot,
- expected: str2url(fmt.Sprintf("file://%s", lfsroot)),
+ expected: str2url("file://" + lfsroot),
},
// case 3
{
cloneurl: rootdotgit,
lfsurl: "",
- expected: str2url(fmt.Sprintf("file://%s", filepath.Join(rootdotgit, ".git"))),
+ expected: str2url("file://" + filepath.Join(rootdotgit, ".git")),
},
// case 4
{
cloneurl: "",
lfsurl: rootdotgit,
- expected: str2url(fmt.Sprintf("file://%s", filepath.Join(rootdotgit, ".git"))),
+ expected: str2url("file://" + filepath.Join(rootdotgit, ".git")),
},
// case 5
{
cloneurl: rootdotgit,
lfsurl: rootdotgit,
- expected: str2url(fmt.Sprintf("file://%s", filepath.Join(rootdotgit, ".git"))),
+ expected: str2url("file://" + filepath.Join(rootdotgit, ".git")),
},
// case 6
{
- cloneurl: fmt.Sprintf("file://%s", root),
+ cloneurl: "file://" + root,
lfsurl: "",
- expected: str2url(fmt.Sprintf("file://%s", root)),
+ expected: str2url("file://" + root),
},
// case 7
{
- cloneurl: fmt.Sprintf("file://%s", root),
- lfsurl: fmt.Sprintf("file://%s", lfsroot),
- expected: str2url(fmt.Sprintf("file://%s", lfsroot)),
+ cloneurl: "file://" + root,
+ lfsurl: "file://" + lfsroot,
+ expected: str2url("file://" + lfsroot),
},
// case 8
{
cloneurl: root,
- lfsurl: fmt.Sprintf("file://%s", lfsroot),
- expected: str2url(fmt.Sprintf("file://%s", lfsroot)),
+ lfsurl: "file://" + lfsroot,
+ expected: str2url("file://" + lfsroot),
},
// case 9
{
diff --git a/tests/integration/lfs_view_test.go b/tests/integration/lfs_view_test.go
index a0e004ed58..c26ece22be 100644
--- a/tests/integration/lfs_view_test.go
+++ b/tests/integration/lfs_view_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "context"
"fmt"
"net/http"
"strings"
@@ -39,7 +38,7 @@ func TestLFSRender(t *testing.T) {
doc := NewHTMLParser(t, resp.Body).doc
fileInfo := doc.Find("div.file-info-entry").First().Text()
- assert.Contains(t, fileInfo, "Stored with Git LFS")
+ assert.Contains(t, fileInfo, "LFS")
content := doc.Find("div.file-view").Text()
assert.Contains(t, content, "Testing documents in LFS")
@@ -55,7 +54,7 @@ func TestLFSRender(t *testing.T) {
doc := NewHTMLParser(t, resp.Body).doc
fileInfo := doc.Find("div.file-info-entry").First().Text()
- assert.Contains(t, fileInfo, "Stored with Git LFS")
+ assert.Contains(t, fileInfo, "LFS")
src, exists := doc.Find(".file-view img").Attr("src")
assert.True(t, exists, "The image should be in an <img> tag")
@@ -69,14 +68,15 @@ func TestLFSRender(t *testing.T) {
req := NewRequest(t, "GET", "/user2/lfs/src/branch/master/crypt.bin")
resp := session.MakeRequest(t, req, http.StatusOK)
- doc := NewHTMLParser(t, resp.Body).doc
+ doc := NewHTMLParser(t, resp.Body)
fileInfo := doc.Find("div.file-info-entry").First().Text()
- assert.Contains(t, fileInfo, "Stored with Git LFS")
+ assert.Contains(t, fileInfo, "LFS")
- rawLink, exists := doc.Find("div.file-view > div.view-raw > a").Attr("href")
- assert.True(t, exists, "Download link should render instead of content because this is a binary file")
- assert.Equal(t, "/user2/lfs/media/branch/master/crypt.bin", rawLink, "The download link should use the proper /media link because it's in LFS")
+ // find new file view container
+ fileViewContainer := doc.Find("[data-global-init=initRepoFileView]")
+ assert.Equal(t, "/user2/lfs/media/branch/master/crypt.bin", fileViewContainer.AttrOr("data-raw-file-link", ""))
+ AssertHTMLElement(t, doc, ".view-raw > .file-view-render-container > .file-view-raw-prompt", 1)
})
// check that a directory with a README file shows its text
@@ -143,7 +143,7 @@ func TestLFSLockView(t *testing.T) {
defer tests.PrintCurrentTest(t)()
// make sure the display names are different, or the test is meaningless
- require.NoError(t, repo3.LoadOwner(context.Background()))
+ require.NoError(t, repo3.LoadOwner(t.Context()))
require.NotEqual(t, user2.DisplayName(), repo3.Owner.DisplayName())
req := NewRequest(t, "GET", fmt.Sprintf("/%s/settings/lfs/locks", repo3.FullName()))
diff --git a/tests/integration/linguist_test.go b/tests/integration/linguist_test.go
index 2d50dc599a..d156dd6f3d 100644
--- a/tests/integration/linguist_test.go
+++ b/tests/integration/linguist_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "context"
"net/url"
"strconv"
"strings"
@@ -246,7 +245,7 @@ func TestLinguist(t *testing.T) {
assert.NoError(t, err)
assert.NoError(t, stats.UpdateRepoIndexer(repo))
- assert.NoError(t, queue.GetManager().FlushAll(context.Background(), 10*time.Second))
+ assert.NoError(t, queue.GetManager().FlushAll(t.Context(), 10*time.Second))
stats, err := repo_model.GetTopLanguageStats(db.DefaultContext, repo, len(c.FilesToAdd))
assert.NoError(t, err)
diff --git a/tests/integration/links_test.go b/tests/integration/links_test.go
index b54f670c23..f80cc6f3f9 100644
--- a/tests/integration/links_test.go
+++ b/tests/integration/links_test.go
@@ -17,56 +17,68 @@ import (
"github.com/stretchr/testify/assert"
)
-func TestLinksNoLogin(t *testing.T) {
+func assertLinkPageComplete(t *testing.T, session *TestSession, link string) {
+ req := NewRequest(t, "GET", link)
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ assert.True(t, test.IsNormalPageCompleted(resp.Body.String()), "Page did not complete: "+link)
+}
+
+func TestLinks(t *testing.T) {
defer tests.PrepareTestEnv(t)()
+ t.Run("NoLogin", testLinksNoLogin)
+ t.Run("RedirectsNoLogin", testLinksRedirectsNoLogin)
+ t.Run("NoLoginNotExist", testLinksNoLoginNotExist)
+ t.Run("AsUser", testLinksAsUser)
+ t.Run("RepoCommon", testLinksRepoCommon)
+}
+
+func testLinksNoLogin(t *testing.T) {
links := []string{
+ "/",
"/explore/repos",
"/explore/repos?q=test",
"/explore/users",
"/explore/users?q=test",
"/explore/organizations",
"/explore/organizations?q=test",
- "/",
"/user/sign_up",
"/user/login",
"/user/forgot_password",
- "/api/swagger",
"/user2/repo1",
"/user2/repo1/",
"/user2/repo1/projects",
"/user2/repo1/projects/1",
"/user2/repo1/releases/tag/delete-tag", // It's the only one existing record on release.yml which has is_tag: true
- "/.well-known/security.txt",
+ "/api/swagger",
}
-
for _, link := range links {
- req := NewRequest(t, "GET", link)
- MakeRequest(t, req, http.StatusOK)
+ assertLinkPageComplete(t, nil, link)
}
+ MakeRequest(t, NewRequest(t, "GET", "/.well-known/security.txt"), http.StatusOK)
}
-func TestRedirectsNoLogin(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
-
+func testLinksRedirectsNoLogin(t *testing.T) {
redirects := []struct{ from, to string }{
{"/user2/repo1/commits/master", "/user2/repo1/commits/branch/master"},
{"/user2/repo1/src/master", "/user2/repo1/src/branch/master"},
- {"/user2/repo1/src/master/file.txt", "/user2/repo1/src/branch/master/file.txt"},
- {"/user2/repo1/src/master/directory/file.txt", "/user2/repo1/src/branch/master/directory/file.txt"},
- {"/user/avatar/Ghost/-1", "/assets/img/avatar_default.png"},
+ {"/user2/repo1/src/master/a%2fb.txt", "/user2/repo1/src/branch/master/a%2fb.txt"},
+ {"/user2/repo1/src/master/directory/file.txt?a=1", "/user2/repo1/src/branch/master/directory/file.txt?a=1"},
+ {"/user2/repo1/src/branch/master/directory/file.txt?raw=1&other=2", "/user2/repo1/raw/branch/master/directory/file.txt"},
+ {"/user2/repo1/tree/a%2fb?a=1", "/user2/repo1/src/a%2fb?a=1"},
+ {"/user2/repo1/blob/123456/%20?a=1", "/user2/repo1/src/commit/123456/%20?a=1"},
+ {"/user/avatar/GhosT/-1", "/assets/img/avatar_default.png"},
+ {"/user/avatar/Gitea-ActionS/0", "/assets/img/avatar_default.png"},
{"/api/v1/swagger", "/api/swagger"},
}
for _, c := range redirects {
req := NewRequest(t, "GET", c.from)
resp := MakeRequest(t, req, http.StatusSeeOther)
- assert.EqualValues(t, path.Join(setting.AppSubURL, c.to), test.RedirectURL(resp))
+ assert.Equal(t, path.Join(setting.AppSubURL, c.to), test.RedirectURL(resp))
}
}
-func TestNoLoginNotExist(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
-
+func testLinksNoLoginNotExist(t *testing.T) {
links := []string{
"/user5/repo4/projects",
"/user5/repo4/projects/3",
@@ -78,7 +90,8 @@ func TestNoLoginNotExist(t *testing.T) {
}
}
-func testLinksAsUser(userName string, t *testing.T) {
+func testLinksAsUser(t *testing.T) {
+ session := loginUser(t, "user2")
links := []string{
"/explore/repos",
"/explore/repos?q=test",
@@ -126,18 +139,14 @@ func testLinksAsUser(userName string, t *testing.T) {
"/user/settings/repos",
}
- session := loginUser(t, userName)
for _, link := range links {
- req := NewRequest(t, "GET", link)
- session.MakeRequest(t, req, http.StatusOK)
+ assertLinkPageComplete(t, session, link)
}
- reqAPI := NewRequestf(t, "GET", "/api/v1/users/%s/repos", userName)
+ reqAPI := NewRequestf(t, "GET", "/api/v1/users/user2/repos")
respAPI := MakeRequest(t, reqAPI, http.StatusOK)
-
var apiRepos []*api.Repository
DecodeJSON(t, respAPI, &apiRepos)
-
repoLinks := []string{
"",
"/issues",
@@ -160,24 +169,15 @@ func testLinksAsUser(userName string, t *testing.T) {
"/wiki/?action=_new",
"/activity",
}
-
for _, repo := range apiRepos {
for _, link := range repoLinks {
- req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s%s", userName, repo.Name, link))
- session.MakeRequest(t, req, http.StatusOK)
+ link = fmt.Sprintf("/user2/%s%s", repo.Name, link)
+ assertLinkPageComplete(t, session, link)
}
}
}
-func TestLinksLogin(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
-
- testLinksAsUser("user2", t)
-}
-
-func TestRepoLinks(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
-
+func testLinksRepoCommon(t *testing.T) {
// repo1 has enabled almost features, so we can test most links
repoLink := "/user2/repo1"
links := []string{
@@ -188,21 +188,18 @@ func TestRepoLinks(t *testing.T) {
// anonymous user
for _, link := range links {
- req := NewRequest(t, "GET", repoLink+link)
- MakeRequest(t, req, http.StatusOK)
+ assertLinkPageComplete(t, nil, repoLink+link)
}
// admin/owner user
session := loginUser(t, "user1")
for _, link := range links {
- req := NewRequest(t, "GET", repoLink+link)
- session.MakeRequest(t, req, http.StatusOK)
+ assertLinkPageComplete(t, session, repoLink+link)
}
// non-admin non-owner user
session = loginUser(t, "user2")
for _, link := range links {
- req := NewRequest(t, "GET", repoLink+link)
- session.MakeRequest(t, req, http.StatusOK)
+ assertLinkPageComplete(t, session, repoLink+link)
}
}
diff --git a/tests/integration/markup_external_test.go b/tests/integration/markup_external_test.go
index 2d713b0eb9..47d143a29e 100644
--- a/tests/integration/markup_external_test.go
+++ b/tests/integration/markup_external_test.go
@@ -27,7 +27,7 @@ func TestExternalMarkupRenderer(t *testing.T) {
req := NewRequest(t, "GET", "/user30/renderer/src/branch/master/README.html")
resp := MakeRequest(t, req, http.StatusOK)
- assert.EqualValues(t, "text/html; charset=utf-8", resp.Header().Get("Content-Type"))
+ assert.Equal(t, "text/html; charset=utf-8", resp.Header().Get("Content-Type"))
bs, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
@@ -36,25 +36,25 @@ func TestExternalMarkupRenderer(t *testing.T) {
div := doc.Find("div.file-view")
data, err := div.Html()
assert.NoError(t, err)
- assert.EqualValues(t, "<div>\n\ttest external renderer\n</div>", strings.TrimSpace(data))
+ assert.Equal(t, "<div>\n\ttest external renderer\n</div>", strings.TrimSpace(data))
r := markup.GetRendererByFileName("a.html").(*external.Renderer)
r.RenderContentMode = setting.RenderContentModeIframe
req = NewRequest(t, "GET", "/user30/renderer/src/branch/master/README.html")
resp = MakeRequest(t, req, http.StatusOK)
- assert.EqualValues(t, "text/html; charset=utf-8", resp.Header().Get("Content-Type"))
+ assert.Equal(t, "text/html; charset=utf-8", resp.Header().Get("Content-Type"))
bs, err = io.ReadAll(resp.Body)
assert.NoError(t, err)
doc = NewHTMLParser(t, bytes.NewBuffer(bs))
iframe := doc.Find("iframe")
- assert.EqualValues(t, "/user30/renderer/render/branch/master/README.html", iframe.AttrOr("src", ""))
+ assert.Equal(t, "/user30/renderer/render/branch/master/README.html", iframe.AttrOr("src", ""))
req = NewRequest(t, "GET", "/user30/renderer/render/branch/master/README.html")
resp = MakeRequest(t, req, http.StatusOK)
- assert.EqualValues(t, "text/html; charset=utf-8", resp.Header().Get("Content-Type"))
+ assert.Equal(t, "text/html; charset=utf-8", resp.Header().Get("Content-Type"))
bs, err = io.ReadAll(resp.Body)
assert.NoError(t, err)
- assert.EqualValues(t, "frame-src 'self'; sandbox allow-scripts", resp.Header().Get("Content-Security-Policy"))
- assert.EqualValues(t, "<div>\n\ttest external renderer\n</div>\n", string(bs))
+ assert.Equal(t, "frame-src 'self'; sandbox allow-scripts", resp.Header().Get("Content-Security-Policy"))
+ assert.Equal(t, "<div>\n\ttest external renderer\n</div>\n", string(bs))
}
diff --git a/tests/integration/migrate_test.go b/tests/integration/migrate_test.go
index 4c784dd22b..a89dc8b85c 100644
--- a/tests/integration/migrate_test.go
+++ b/tests/integration/migrate_test.go
@@ -9,6 +9,7 @@ import (
"net/url"
"os"
"path/filepath"
+ "strconv"
"testing"
auth_model "code.gitea.io/gitea/models/auth"
@@ -79,8 +80,12 @@ func TestMigrateGiteaForm(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK)
// Step 2: load the form
htmlDoc := NewHTMLParser(t, resp.Body)
- link, exists := htmlDoc.doc.Find(`form.ui.form[action^="/repo/migrate"]`).Attr("action")
+ form := htmlDoc.doc.Find(`form.ui.form[action^="/repo/migrate"]`)
+ link, exists := form.Attr("action")
assert.True(t, exists, "The template has changed")
+ serviceInput, exists := form.Find(`input[name="service"]`).Attr("value")
+ assert.True(t, exists)
+ assert.Equal(t, fmt.Sprintf("%d", structs.GiteaService), serviceInput)
// Step 4: submit the migration to only migrate issues
migratedRepoName := "otherrepo"
req = NewRequestWithValues(t, "POST", link, map[string]string{
@@ -91,12 +96,12 @@ func TestMigrateGiteaForm(t *testing.T) {
"issues": "on",
"repo_name": migratedRepoName,
"description": "",
- "uid": fmt.Sprintf("%d", repoOwner.ID),
+ "uid": strconv.FormatInt(repoOwner.ID, 10),
})
resp = session.MakeRequest(t, req, http.StatusSeeOther)
// Step 5: a redirection displays the migrated repository
loc := resp.Header().Get("Location")
- assert.EqualValues(t, fmt.Sprintf("/%s/%s", ownerName, migratedRepoName), loc)
+ assert.Equal(t, fmt.Sprintf("/%s/%s", ownerName, migratedRepoName), loc)
// Step 6: check the repo was created
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: migratedRepoName})
})
diff --git a/tests/integration/migration-test/migration_test.go b/tests/integration/migration-test/migration_test.go
index 616ccf291a..0fc8a6e24d 100644
--- a/tests/integration/migration-test/migration_test.go
+++ b/tests/integration/migration-test/migration_test.go
@@ -38,7 +38,7 @@ var currentEngine *xorm.Engine
func initMigrationTest(t *testing.T) func() {
testlogger.Init()
giteaRoot := test.SetupGiteaRoot()
- setting.AppPath = path.Join(giteaRoot, "gitea")
+ setting.AppPath = filepath.Join(giteaRoot, "gitea")
if _, err := os.Stat(setting.AppPath); err != nil {
testlogger.Fatalf(fmt.Sprintf("Could not find gitea binary at %s\n", setting.AppPath))
}
@@ -47,16 +47,16 @@ func initMigrationTest(t *testing.T) func() {
if giteaConf == "" {
testlogger.Fatalf("Environment variable $GITEA_CONF not set\n")
} else if !path.IsAbs(giteaConf) {
- setting.CustomConf = path.Join(giteaRoot, giteaConf)
+ setting.CustomConf = filepath.Join(giteaRoot, giteaConf)
} else {
setting.CustomConf = giteaConf
}
- unittest.InitSettings()
+ unittest.InitSettingsForTesting()
assert.NotEmpty(t, setting.RepoRootPath)
- assert.NoError(t, unittest.SyncDirs(path.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath))
- assert.NoError(t, git.InitFull(context.Background()))
+ assert.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath))
+ assert.NoError(t, git.InitFull(t.Context()))
setting.LoadDBSetting()
setting.InitLoggersForTest()
@@ -140,10 +140,10 @@ func restoreOldDB(t *testing.T, version string) {
assert.NoError(t, err)
defer db.Close()
- _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", setting.Database.Name))
+ _, err = db.Exec("DROP DATABASE IF EXISTS " + setting.Database.Name)
assert.NoError(t, err)
- _, err = db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", setting.Database.Name))
+ _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + setting.Database.Name)
assert.NoError(t, err)
db.Close()
@@ -170,10 +170,10 @@ func restoreOldDB(t *testing.T, version string) {
}
defer db.Close()
- _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", setting.Database.Name))
+ _, err = db.Exec("DROP DATABASE IF EXISTS " + setting.Database.Name)
assert.NoError(t, err)
- _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", setting.Database.Name))
+ _, err = db.Exec("CREATE DATABASE " + setting.Database.Name)
assert.NoError(t, err)
db.Close()
@@ -195,7 +195,7 @@ func restoreOldDB(t *testing.T, version string) {
if !schrows.Next() {
// Create and setup a DB schema
- _, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s", setting.Database.Schema))
+ _, err = db.Exec("CREATE SCHEMA " + setting.Database.Schema)
assert.NoError(t, err)
}
schrows.Close()
@@ -248,7 +248,7 @@ func restoreOldDB(t *testing.T, version string) {
}
}
-func wrappedMigrate(x *xorm.Engine) error {
+func wrappedMigrate(ctx context.Context, x *xorm.Engine) error {
currentEngine = x
return migrations.Migrate(x)
}
@@ -259,13 +259,13 @@ func doMigrationTest(t *testing.T, version string) {
setting.InitSQLLoggersForCli(log.INFO)
- err := db.InitEngineWithMigration(context.Background(), wrappedMigrate)
+ err := db.InitEngineWithMigration(t.Context(), wrappedMigrate)
assert.NoError(t, err)
currentEngine.Close()
beans, _ := db.NamesToBean()
- err = db.InitEngineWithMigration(context.Background(), func(x *xorm.Engine) error {
+ err = db.InitEngineWithMigration(t.Context(), func(ctx context.Context, x *xorm.Engine) error {
currentEngine = x
return migrate_base.RecreateTables(beans...)(x)
})
@@ -273,7 +273,7 @@ func doMigrationTest(t *testing.T, version string) {
currentEngine.Close()
// We do this a second time to ensure that there is not a problem with retained indices
- err = db.InitEngineWithMigration(context.Background(), func(x *xorm.Engine) error {
+ err = db.InitEngineWithMigration(t.Context(), func(ctx context.Context, x *xorm.Engine) error {
currentEngine = x
return migrate_base.RecreateTables(beans...)(x)
})
diff --git a/tests/integration/mirror_pull_test.go b/tests/integration/mirror_pull_test.go
index 77050c4bbc..c33b2eb04d 100644
--- a/tests/integration/mirror_pull_test.go
+++ b/tests/integration/mirror_pull_test.go
@@ -4,11 +4,12 @@
package integration
import (
- "context"
+ "slices"
"testing"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
@@ -20,11 +21,13 @@ import (
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestMirrorPull(t *testing.T) {
defer tests.PrepareTestEnv(t)()
+ ctx := t.Context()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
repoPath := repo_model.RepoPath(user.Name, repo.Name)
@@ -36,24 +39,28 @@ func TestMirrorPull(t *testing.T) {
Mirror: true,
CloneAddr: repoPath,
Wiki: true,
- Releases: false,
+ Releases: true,
}
- mirrorRepo, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, user, repo_service.CreateRepoOptions{
+ mirrorRepo, err := repo_service.CreateRepositoryDirectly(ctx, user, user, repo_service.CreateRepoOptions{
Name: opts.RepoName,
Description: opts.Description,
IsPrivate: opts.Private,
IsMirror: opts.Mirror,
Status: repo_model.RepositoryBeingMigrated,
- })
+ }, false)
assert.NoError(t, err)
assert.True(t, mirrorRepo.IsMirror, "expected pull-mirror repo to be marked as a mirror immediately after its creation")
- ctx := context.Background()
-
- mirror, err := repo_service.MigrateRepositoryGitData(ctx, user, mirrorRepo, opts, nil)
+ mirrorRepo, err = repo_service.MigrateRepositoryGitData(ctx, user, mirrorRepo, opts, nil)
assert.NoError(t, err)
+ // these units should have been enabled
+ mirrorRepo.Units = nil
+ require.NoError(t, mirrorRepo.LoadUnits(ctx))
+ assert.True(t, slices.ContainsFunc(mirrorRepo.Units, func(u *repo_model.RepoUnit) bool { return u.Type == unit.TypeReleases }))
+ assert.True(t, slices.ContainsFunc(mirrorRepo.Units, func(u *repo_model.RepoUnit) bool { return u.Type == unit.TypeWiki }))
+
gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo)
assert.NoError(t, err)
defer gitRepo.Close()
@@ -61,10 +68,11 @@ func TestMirrorPull(t *testing.T) {
findOptions := repo_model.FindReleasesOptions{
IncludeDrafts: true,
IncludeTags: true,
- RepoID: mirror.ID,
+ RepoID: mirrorRepo.ID,
}
initCount, err := db.Count[repo_model.Release](db.DefaultContext, findOptions)
assert.NoError(t, err)
+ assert.Zero(t, initCount) // no sync yet, so even though there is a tag in source repo, the mirror's release table is still empty
assert.NoError(t, release_service.CreateRelease(gitRepo, &repo_model.Release{
RepoID: repo.ID,
@@ -80,24 +88,27 @@ func TestMirrorPull(t *testing.T) {
IsTag: true,
}, nil, ""))
- _, err = repo_model.GetMirrorByRepoID(ctx, mirror.ID)
+ _, err = repo_model.GetMirrorByRepoID(ctx, mirrorRepo.ID)
assert.NoError(t, err)
- ok := mirror_service.SyncPullMirror(ctx, mirror.ID)
+ ok := mirror_service.SyncPullMirror(ctx, mirrorRepo.ID)
assert.True(t, ok)
+ // actually there is a tag in the source repo, so after "sync", that tag will also come into the mirror
+ initCount++
+
count, err := db.Count[repo_model.Release](db.DefaultContext, findOptions)
assert.NoError(t, err)
- assert.EqualValues(t, initCount+1, count)
+ assert.Equal(t, initCount+1, count)
release, err := repo_model.GetRelease(db.DefaultContext, repo.ID, "v0.2")
assert.NoError(t, err)
assert.NoError(t, release_service.DeleteReleaseByID(ctx, repo, release, user, true))
- ok = mirror_service.SyncPullMirror(ctx, mirror.ID)
+ ok = mirror_service.SyncPullMirror(ctx, mirrorRepo.ID)
assert.True(t, ok)
count, err = db.Count[repo_model.Release](db.DefaultContext, findOptions)
assert.NoError(t, err)
- assert.EqualValues(t, initCount, count)
+ assert.Equal(t, initCount, count)
}
diff --git a/tests/integration/mirror_push_test.go b/tests/integration/mirror_push_test.go
index 0dd8919bff..9b6d4c4017 100644
--- a/tests/integration/mirror_push_test.go
+++ b/tests/integration/mirror_push_test.go
@@ -4,12 +4,10 @@
package integration
import (
- "context"
"fmt"
"net/http"
"net/url"
"strconv"
- "strings"
"testing"
"time"
@@ -20,7 +18,6 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
- gitea_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/migrations"
mirror_service "code.gitea.io/gitea/services/mirror"
repo_service "code.gitea.io/gitea/services/repository"
@@ -43,7 +40,7 @@ func testMirrorPush(t *testing.T, u *url.URL) {
mirrorRepo, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, user, repo_service.CreateRepoOptions{
Name: "test-push-mirror",
- })
+ }, true)
assert.NoError(t, err)
session := loginUser(t, user.Name)
@@ -55,7 +52,7 @@ func testMirrorPush(t *testing.T, u *url.URL) {
assert.NoError(t, err)
assert.Len(t, mirrors, 1)
- ok := mirror_service.SyncPushMirror(context.Background(), mirrors[0].ID)
+ ok := mirror_service.SyncPushMirror(t.Context(), mirrors[0].ID)
assert.True(t, ok)
srcGitRepo, err := gitrepo.OpenRepository(git.DefaultContext, srcRepo)
@@ -92,9 +89,8 @@ func testCreatePushMirror(t *testing.T, session *TestSession, owner, repo, addre
})
session.MakeRequest(t, req, http.StatusSeeOther)
- flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
- assert.NotNil(t, flashCookie)
- assert.Contains(t, flashCookie.Value, "success")
+ flashMsg := session.GetCookieFlashMessage()
+ assert.NotEmpty(t, flashMsg.SuccessMsg)
}
func doRemovePushMirror(t *testing.T, session *TestSession, owner, repo string, pushMirrorID int64) bool {
@@ -104,8 +100,8 @@ func doRemovePushMirror(t *testing.T, session *TestSession, owner, repo string,
"push_mirror_id": strconv.FormatInt(pushMirrorID, 10),
})
resp := session.MakeRequest(t, req, NoExpectedStatus)
- flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
- return resp.Code == http.StatusSeeOther && flashCookie != nil && strings.Contains(flashCookie.Value, "success")
+ flashMsg := session.GetCookieFlashMessage()
+ return resp.Code == http.StatusSeeOther && assert.NotEmpty(t, flashMsg.SuccessMsg)
}
func doUpdatePushMirror(t *testing.T, session *TestSession, owner, repo string, pushMirrorID int64, interval string) bool {
@@ -132,18 +128,18 @@ func TestRepoSettingPushMirrorUpdate(t *testing.T) {
pushMirrors, cnt, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, repo2.ID, db.ListOptions{})
assert.NoError(t, err)
assert.EqualValues(t, 1, cnt)
- assert.EqualValues(t, 24*time.Hour, pushMirrors[0].Interval)
+ assert.Equal(t, 24*time.Hour, pushMirrors[0].Interval)
repo2PushMirrorID := pushMirrors[0].ID
// update repo2 push mirror
assert.True(t, doUpdatePushMirror(t, session, "user2", "repo2", repo2PushMirrorID, "10m0s"))
pushMirror := unittest.AssertExistsAndLoadBean(t, &repo_model.PushMirror{ID: repo2PushMirrorID})
- assert.EqualValues(t, 10*time.Minute, pushMirror.Interval)
+ assert.Equal(t, 10*time.Minute, pushMirror.Interval)
// avoid updating repo2 push mirror from repo1
assert.False(t, doUpdatePushMirror(t, session, "user2", "repo1", repo2PushMirrorID, "20m0s"))
pushMirror = unittest.AssertExistsAndLoadBean(t, &repo_model.PushMirror{ID: repo2PushMirrorID})
- assert.EqualValues(t, 10*time.Minute, pushMirror.Interval) // not changed
+ assert.Equal(t, 10*time.Minute, pushMirror.Interval) // not changed
// avoid deleting repo2 push mirror from repo1
assert.False(t, doRemovePushMirror(t, session, "user2", "repo1", repo2PushMirrorID))
diff --git a/tests/integration/nonascii_branches_test.go b/tests/integration/nonascii_branches_test.go
index e5934a148d..cc71acf002 100644
--- a/tests/integration/nonascii_branches_test.go
+++ b/tests/integration/nonascii_branches_test.go
@@ -12,7 +12,7 @@ import (
"code.gitea.io/gitea/tests"
- "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func setDefaultBranch(t *testing.T, session *TestSession, user, repo, branch string) {
@@ -46,21 +46,21 @@ func TestNonAsciiBranches(t *testing.T) {
{
from: "master/badfile",
to: "branch/master/badfile",
- status: http.StatusNotFound, // it does not exists
+ status: http.StatusNotFound, // it does not exist
},
{
from: "ГлавнаÑВетка",
- to: "branch/%D0%93%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F%D0%92%D0%B5%D1%82%D0%BA%D0%B0",
+ to: "branch/%d0%93%d0%bb%d0%b0%d0%b2%d0%bd%d0%b0%d1%8f%d0%92%d0%b5%d1%82%d0%ba%d0%b0",
status: http.StatusOK,
},
{
from: "а/б/в",
- to: "branch/%D0%B0/%D0%B1/%D0%B2",
+ to: "branch/%d0%b0/%d0%b1/%d0%b2",
status: http.StatusOK,
},
{
from: "Grüßen/README.md",
- to: "branch/Gr%C3%BC%C3%9Fen/README.md",
+ to: "branch/Gr%c3%bc%c3%9fen/README.md",
status: http.StatusOK,
},
{
@@ -70,7 +70,7 @@ func TestNonAsciiBranches(t *testing.T) {
},
{
from: "Plus+Is+Not+Space/Файл.md",
- to: "branch/Plus+Is+Not+Space/%D0%A4%D0%B0%D0%B9%D0%BB.md",
+ to: "branch/Plus+Is+Not+Space/%d0%a4%d0%b0%d0%b9%d0%bb.md",
status: http.StatusOK,
},
{
@@ -80,29 +80,29 @@ func TestNonAsciiBranches(t *testing.T) {
},
{
from: "ブランãƒ",
- to: "branch/%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81",
+ to: "branch/%e3%83%96%e3%83%a9%e3%83%b3%e3%83%81",
status: http.StatusOK,
},
// Tags
{
from: "ТÑг",
- to: "tag/%D0%A2%D1%8D%D0%B3",
+ to: "tag/%d0%a2%d1%8d%d0%b3",
status: http.StatusOK,
},
{
from: "Ð/人",
- to: "tag/%D0%81/%E4%BA%BA",
+ to: "tag/%d0%81/%e4%ba%ba",
status: http.StatusOK,
},
{
from: "ã‚¿ã‚°",
- to: "tag/%E3%82%BF%E3%82%B0",
+ to: "tag/%e3%82%bf%e3%82%b0",
status: http.StatusOK,
},
{
from: "タグ/ファイル.md",
- to: "tag/%E3%82%BF%E3%82%B0/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.md",
+ to: "tag/%e3%82%bf%e3%82%b0/%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab.md",
status: http.StatusOK,
},
@@ -114,12 +114,12 @@ func TestNonAsciiBranches(t *testing.T) {
},
{
from: "Файл.md",
- to: "branch/Plus+Is+Not+Space/%D0%A4%D0%B0%D0%B9%D0%BB.md",
+ to: "branch/Plus+Is+Not+Space/%d0%a4%d0%b0%d0%b9%d0%bb.md",
status: http.StatusOK,
},
{
from: "ファイル.md",
- to: "branch/Plus+Is+Not+Space/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.md",
+ to: "branch/Plus+Is+Not+Space/%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab.md",
status: http.StatusNotFound, // it's not on default branch
},
@@ -131,7 +131,7 @@ func TestNonAsciiBranches(t *testing.T) {
},
{
from: "%E3%82%BF%E3%82%b0",
- to: "tag/%E3%82%BF%E3%82%B0",
+ to: "tag/%E3%82%BF%E3%82%b0",
status: http.StatusOK,
},
{
@@ -141,12 +141,12 @@ func TestNonAsciiBranches(t *testing.T) {
},
{
from: "%D0%81%2F%E4%BA%BA",
- to: "tag/%D0%81/%E4%BA%BA",
+ to: "tag/%D0%81%2F%E4%BA%BA",
status: http.StatusOK,
},
{
from: "Ð%2F%E4%BA%BA",
- to: "tag/%D0%81/%E4%BA%BA",
+ to: "tag/%d0%81%2F%E4%BA%BA",
status: http.StatusOK,
},
{
@@ -199,14 +199,10 @@ func TestNonAsciiBranches(t *testing.T) {
t.Run(test.from, func(t *testing.T) {
req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/src/%s", user, repo, test.from))
resp := session.MakeRequest(t, req, http.StatusSeeOther)
- if resp.Code != http.StatusSeeOther {
- return
- }
+ require.Equal(t, http.StatusSeeOther, resp.Code)
redirectLocation := resp.Header().Get("Location")
- if !assert.Equal(t, fmt.Sprintf("/%s/%s/src/%s", user, repo, test.to), redirectLocation) {
- return
- }
+ require.Equal(t, fmt.Sprintf("/%s/%s/src/%s", user, repo, test.to), redirectLocation)
req = NewRequest(t, "GET", redirectLocation)
session.MakeRequest(t, req, test.status)
diff --git a/tests/integration/oauth_test.go b/tests/integration/oauth_test.go
index d6f1ba33ec..f8bc33c32a 100644
--- a/tests/integration/oauth_test.go
+++ b/tests/integration/oauth_test.go
@@ -19,31 +19,41 @@ import (
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
- oauth2_provider "code.gitea.io/gitea/services/oauth2_provider"
+ "code.gitea.io/gitea/modules/test"
+ "code.gitea.io/gitea/services/oauth2_provider"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
-func TestAuthorizeNoClientID(t *testing.T) {
+func TestOAuth2Provider(t *testing.T) {
defer tests.PrepareTestEnv(t)()
+
+ t.Run("AuthorizeNoClientID", testAuthorizeNoClientID)
+ t.Run("AuthorizeUnregisteredRedirect", testAuthorizeUnregisteredRedirect)
+ t.Run("AuthorizeUnsupportedResponseType", testAuthorizeUnsupportedResponseType)
+ t.Run("AuthorizeUnsupportedCodeChallengeMethod", testAuthorizeUnsupportedCodeChallengeMethod)
+ t.Run("AuthorizeLoginRedirect", testAuthorizeLoginRedirect)
+
+ t.Run("OAuth2WellKnown", testOAuth2WellKnown)
+}
+
+func testAuthorizeNoClientID(t *testing.T) {
req := NewRequest(t, "GET", "/login/oauth/authorize")
ctx := loginUser(t, "user2")
resp := ctx.MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), "Client ID not registered")
}
-func TestAuthorizeUnregisteredRedirect(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testAuthorizeUnregisteredRedirect(t *testing.T) {
req := NewRequest(t, "GET", "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=UNREGISTERED&response_type=code&state=thestate")
ctx := loginUser(t, "user1")
resp := ctx.MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), "Unregistered Redirect URI")
}
-func TestAuthorizeUnsupportedResponseType(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testAuthorizeUnsupportedResponseType(t *testing.T) {
req := NewRequest(t, "GET", "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=a&response_type=UNEXPECTED&state=thestate")
ctx := loginUser(t, "user1")
resp := ctx.MakeRequest(t, req, http.StatusSeeOther)
@@ -53,8 +63,7 @@ func TestAuthorizeUnsupportedResponseType(t *testing.T) {
assert.Equal(t, "Only code response type is supported.", u.Query().Get("error_description"))
}
-func TestAuthorizeUnsupportedCodeChallengeMethod(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testAuthorizeUnsupportedCodeChallengeMethod(t *testing.T) {
req := NewRequest(t, "GET", "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=a&response_type=code&state=thestate&code_challenge_method=UNEXPECTED")
ctx := loginUser(t, "user1")
resp := ctx.MakeRequest(t, req, http.StatusSeeOther)
@@ -64,8 +73,7 @@ func TestAuthorizeUnsupportedCodeChallengeMethod(t *testing.T) {
assert.Equal(t, "unsupported code challenge method", u.Query().Get("error_description"))
}
-func TestAuthorizeLoginRedirect(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testAuthorizeLoginRedirect(t *testing.T) {
req := NewRequest(t, "GET", "/login/oauth/authorize")
assert.Contains(t, MakeRequest(t, req, http.StatusSeeOther).Body.String(), "/user/login")
}
@@ -77,7 +85,7 @@ func TestAuthorizeShow(t *testing.T) {
resp := ctx.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
- htmlDoc.AssertElement(t, "#authorize-app", true)
+ AssertHTMLElement(t, htmlDoc, "#authorize-app", true)
htmlDoc.GetCSRF()
}
@@ -691,10 +699,6 @@ func TestOAuth_GrantScopesReadRepositoryFailOrganization(t *testing.T) {
FullRepoName: "user2/commitsonpr",
Private: false,
},
- {
- FullRepoName: "user2/test_commit_revert",
- Private: true,
- },
}
assert.Equal(t, reposExpected, reposCaptured)
@@ -907,3 +911,23 @@ func TestOAuth_GrantScopesClaimAllGroups(t *testing.T) {
assert.Contains(t, userinfoParsed.Groups, group)
}
}
+
+func testOAuth2WellKnown(t *testing.T) {
+ urlOpenidConfiguration := "/.well-known/openid-configuration"
+
+ defer test.MockVariableValue(&setting.AppURL, "https://try.gitea.io/")()
+ req := NewRequest(t, "GET", urlOpenidConfiguration)
+ resp := MakeRequest(t, req, http.StatusOK)
+ var respMap map[string]any
+ DecodeJSON(t, resp, &respMap)
+ assert.Equal(t, "https://try.gitea.io", respMap["issuer"])
+ assert.Equal(t, "https://try.gitea.io/login/oauth/authorize", respMap["authorization_endpoint"])
+ assert.Equal(t, "https://try.gitea.io/login/oauth/access_token", respMap["token_endpoint"])
+ assert.Equal(t, "https://try.gitea.io/login/oauth/keys", respMap["jwks_uri"])
+ assert.Equal(t, "https://try.gitea.io/login/oauth/userinfo", respMap["userinfo_endpoint"])
+ assert.Equal(t, "https://try.gitea.io/login/oauth/introspect", respMap["introspection_endpoint"])
+ assert.Equal(t, []any{"RS256"}, respMap["id_token_signing_alg_values_supported"])
+
+ defer test.MockVariableValue(&setting.OAuth2.Enabled, false)()
+ MakeRequest(t, NewRequest(t, "GET", urlOpenidConfiguration), http.StatusNotFound)
+}
diff --git a/tests/integration/org_count_test.go b/tests/integration/org_count_test.go
index 8a33c218be..c48008e627 100644
--- a/tests/integration/org_count_test.go
+++ b/tests/integration/org_count_test.go
@@ -4,7 +4,6 @@
package integration
import (
- "net/url"
"strings"
"testing"
@@ -14,15 +13,17 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
)
func TestOrgCounts(t *testing.T) {
- onGiteaRun(t, testOrgCounts)
+ defer tests.PrepareTestEnv(t)()
+ testOrgCounts(t)
}
-func testOrgCounts(t *testing.T, u *url.URL) {
+func testOrgCounts(t *testing.T) {
orgOwner := "user2"
orgName := "testOrg"
orgCollaborator := "user4"
@@ -119,8 +120,8 @@ func doCheckOrgCounts(username string, orgCounts map[string]int, strict bool, ca
})
orgs, err := db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
- UserID: user.ID,
- IncludePrivate: true,
+ UserID: user.ID,
+ IncludeVisibility: api.VisibleTypePrivate,
})
assert.NoError(t, err)
diff --git a/tests/integration/org_team_invite_test.go b/tests/integration/org_team_invite_test.go
index 4c1053702e..7444980ea8 100644
--- a/tests/integration/org_team_invite_test.go
+++ b/tests/integration/org_team_invite_test.go
@@ -58,7 +58,7 @@ func TestOrgTeamEmailInvite(t *testing.T) {
session = loginUser(t, user.Name)
// join the team
- inviteURL := fmt.Sprintf("/org/invite/%s", invites[0].Token)
+ inviteURL := "/org/invite/" + invites[0].Token
csrf = GetUserCSRFToken(t, session)
req = NewRequestWithValues(t, "POST", inviteURL, map[string]string{
"_csrf": csrf,
@@ -108,8 +108,8 @@ func TestOrgTeamEmailInviteRedirectsExistingUser(t *testing.T) {
assert.Len(t, invites, 1)
// accept the invite
- inviteURL := fmt.Sprintf("/org/invite/%s", invites[0].Token)
- req = NewRequest(t, "GET", fmt.Sprintf("/user/login?redirect_to=%s", url.QueryEscape(inviteURL)))
+ inviteURL := "/org/invite/" + invites[0].Token
+ req = NewRequest(t, "GET", "/user/login?redirect_to="+url.QueryEscape(inviteURL))
resp = MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
@@ -179,8 +179,8 @@ func TestOrgTeamEmailInviteRedirectsNewUser(t *testing.T) {
assert.Len(t, invites, 1)
// accept the invite
- inviteURL := fmt.Sprintf("/org/invite/%s", invites[0].Token)
- req = NewRequest(t, "GET", fmt.Sprintf("/user/sign_up?redirect_to=%s", url.QueryEscape(inviteURL)))
+ inviteURL := "/org/invite/" + invites[0].Token
+ req = NewRequest(t, "GET", "/user/sign_up?redirect_to="+url.QueryEscape(inviteURL))
resp = MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
@@ -260,8 +260,8 @@ func TestOrgTeamEmailInviteRedirectsNewUserWithActivation(t *testing.T) {
// new user: accept the invite
session = emptyTestSession(t)
- inviteURL := fmt.Sprintf("/org/invite/%s", invites[0].Token)
- req = NewRequest(t, "GET", fmt.Sprintf("/user/sign_up?redirect_to=%s", url.QueryEscape(inviteURL)))
+ inviteURL := "/org/invite/" + invites[0].Token
+ req = NewRequest(t, "GET", "/user/sign_up?redirect_to="+url.QueryEscape(inviteURL))
session.MakeRequest(t, req, http.StatusOK)
req = NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{
"user_name": "doesnotexist",
@@ -275,7 +275,7 @@ func TestOrgTeamEmailInviteRedirectsNewUserWithActivation(t *testing.T) {
assert.NoError(t, err)
activationCode := user_model.GenerateUserTimeLimitCode(&user_model.TimeLimitCodeOptions{Purpose: user_model.TimeLimitCodeActivateAccount}, user)
- activateURL := fmt.Sprintf("/user/activate?code=%s", activationCode)
+ activateURL := "/user/activate?code=" + activationCode
req = NewRequestWithValues(t, "POST", activateURL, map[string]string{
"password": "examplePassword!1",
})
@@ -337,8 +337,8 @@ func TestOrgTeamEmailInviteRedirectsExistingUserWithLogin(t *testing.T) {
session = loginUser(t, "user5")
// accept the invite (note: this uses the sign_up url)
- inviteURL := fmt.Sprintf("/org/invite/%s", invites[0].Token)
- req = NewRequest(t, "GET", fmt.Sprintf("/user/sign_up?redirect_to=%s", url.QueryEscape(inviteURL)))
+ inviteURL := "/org/invite/" + invites[0].Token
+ req = NewRequest(t, "GET", "/user/sign_up?redirect_to="+url.QueryEscape(inviteURL))
resp = session.MakeRequest(t, req, http.StatusSeeOther)
assert.Equal(t, inviteURL, test.RedirectURL(resp))
diff --git a/tests/integration/org_test.go b/tests/integration/org_test.go
index ef4ef2bb9b..3ed7baa5ba 100644
--- a/tests/integration/org_test.go
+++ b/tests/integration/org_test.go
@@ -10,12 +10,17 @@ import (
"testing"
auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/organization"
+ "code.gitea.io/gitea/models/perm"
+ "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestOrgRepos(t *testing.T) {
@@ -40,8 +45,8 @@ func TestOrgRepos(t *testing.T) {
sel := htmlDoc.doc.Find("a.name")
assert.Len(t, repos, len(sel.Nodes))
- for i := 0; i < len(repos); i++ {
- assert.EqualValues(t, repos[i], strings.TrimSpace(sel.Eq(i).Text()))
+ for i := range repos {
+ assert.Equal(t, repos[i], strings.TrimSpace(sel.Eq(i).Text()))
}
}
})
@@ -151,7 +156,7 @@ func TestOrgRestrictedUser(t *testing.T) {
// assert restrictedUser cannot see the org or the public repo
restrictedSession := loginUser(t, restrictedUser)
- req := NewRequest(t, "GET", fmt.Sprintf("/%s", orgName))
+ req := NewRequest(t, "GET", "/"+orgName)
restrictedSession.MakeRequest(t, req, http.StatusNotFound)
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s", orgName, repoName))
@@ -177,9 +182,9 @@ func TestOrgRestrictedUser(t *testing.T) {
resp := adminSession.MakeRequest(t, req, http.StatusCreated)
DecodeJSON(t, resp, &apiTeam)
checkTeamResponse(t, "CreateTeam_codereader", &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
- teamToCreate.Permission, teamToCreate.Units, nil)
+ "none", teamToCreate.Units, nil)
checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
- teamToCreate.Permission, teamToCreate.Units, nil)
+ "none", teamToCreate.Units, nil)
// teamID := apiTeam.ID
// Now we need to add the restricted user to the team
@@ -188,7 +193,7 @@ func TestOrgRestrictedUser(t *testing.T) {
_ = adminSession.MakeRequest(t, req, http.StatusNoContent)
// Now we need to check if the restrictedUser can access the repo
- req = NewRequest(t, "GET", fmt.Sprintf("/%s", orgName))
+ req = NewRequest(t, "GET", "/"+orgName)
restrictedSession.MakeRequest(t, req, http.StatusOK)
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s", orgName, repoName))
@@ -217,4 +222,32 @@ func TestTeamSearch(t *testing.T) {
session = loginUser(t, user5.Name)
req = NewRequestf(t, "GET", "/org/%s/teams/-/search?q=%s", org.Name, "team")
session.MakeRequest(t, req, http.StatusNotFound)
+
+ t.Run("SearchWithPermission", func(t *testing.T) {
+ ctx := t.Context()
+ const testOrgID int64 = 500
+ const testRepoID int64 = 2000
+ testTeam := &organization.Team{OrgID: testOrgID, LowerName: "test_team", AccessMode: perm.AccessModeNone}
+ require.NoError(t, db.Insert(ctx, testTeam))
+ require.NoError(t, db.Insert(ctx, &organization.TeamRepo{OrgID: testOrgID, TeamID: testTeam.ID, RepoID: testRepoID}))
+ require.NoError(t, db.Insert(ctx, &organization.TeamUnit{OrgID: testOrgID, TeamID: testTeam.ID, Type: unit.TypeCode, AccessMode: perm.AccessModeRead}))
+ require.NoError(t, db.Insert(ctx, &organization.TeamUnit{OrgID: testOrgID, TeamID: testTeam.ID, Type: unit.TypeIssues, AccessMode: perm.AccessModeWrite}))
+
+ teams, err := organization.GetTeamsWithAccessToAnyRepoUnit(ctx, testOrgID, testRepoID, perm.AccessModeRead, unit.TypeCode, unit.TypeIssues)
+ require.NoError(t, err)
+ assert.Len(t, teams, 1) // can read "code" or "issues"
+
+ teams, err = organization.GetTeamsWithAccessToAnyRepoUnit(ctx, testOrgID, testRepoID, perm.AccessModeWrite, unit.TypeCode)
+ require.NoError(t, err)
+ assert.Empty(t, teams) // cannot write "code"
+
+ teams, err = organization.GetTeamsWithAccessToAnyRepoUnit(ctx, testOrgID, testRepoID, perm.AccessModeWrite, unit.TypeIssues)
+ require.NoError(t, err)
+ assert.Len(t, teams, 1) // can write "issues"
+
+ _, _ = db.GetEngine(ctx).ID(testTeam.ID).Update(&organization.Team{AccessMode: perm.AccessModeWrite})
+ teams, err = organization.GetTeamsWithAccessToAnyRepoUnit(ctx, testOrgID, testRepoID, perm.AccessModeWrite, unit.TypeCode)
+ require.NoError(t, err)
+ assert.Len(t, teams, 1) // team permission is "write", so can write "code"
+ })
}
diff --git a/tests/integration/org_worktime_test.go b/tests/integration/org_worktime_test.go
new file mode 100644
index 0000000000..fb5216be8d
--- /dev/null
+++ b/tests/integration/org_worktime_test.go
@@ -0,0 +1,293 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration_test
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/organization"
+ "code.gitea.io/gitea/models/unittest"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+// TestTimesByRepos tests TimesByRepos functionality
+func testTimesByRepos(t *testing.T) {
+ kases := []struct {
+ name string
+ unixfrom int64
+ unixto int64
+ orgname int64
+ expected []organization.WorktimeSumByRepos
+ }{
+ {
+ name: "Full sum for org 1",
+ unixfrom: 0,
+ unixto: 9223372036854775807,
+ orgname: 1,
+ expected: []organization.WorktimeSumByRepos(nil),
+ },
+ {
+ name: "Full sum for org 2",
+ unixfrom: 0,
+ unixto: 9223372036854775807,
+ orgname: 2,
+ expected: []organization.WorktimeSumByRepos{
+ {
+ RepoName: "repo1",
+ SumTime: 4083,
+ },
+ {
+ RepoName: "repo2",
+ SumTime: 75,
+ },
+ },
+ },
+ {
+ name: "Simple time bound",
+ unixfrom: 946684801,
+ unixto: 946684802,
+ orgname: 2,
+ expected: []organization.WorktimeSumByRepos{
+ {
+ RepoName: "repo1",
+ SumTime: 3662,
+ },
+ },
+ },
+ {
+ name: "Both times inclusive",
+ unixfrom: 946684801,
+ unixto: 946684801,
+ orgname: 2,
+ expected: []organization.WorktimeSumByRepos{
+ {
+ RepoName: "repo1",
+ SumTime: 3661,
+ },
+ },
+ },
+ {
+ name: "Should ignore deleted",
+ unixfrom: 947688814,
+ unixto: 947688815,
+ orgname: 2,
+ expected: []organization.WorktimeSumByRepos{
+ {
+ RepoName: "repo2",
+ SumTime: 71,
+ },
+ },
+ },
+ }
+
+ // Run test kases
+ for _, kase := range kases {
+ t.Run(kase.name, func(t *testing.T) {
+ org, err := organization.GetOrgByID(db.DefaultContext, kase.orgname)
+ assert.NoError(t, err)
+ results, err := organization.GetWorktimeByRepos(org, kase.unixfrom, kase.unixto)
+ assert.NoError(t, err)
+ assert.Equal(t, kase.expected, results)
+ })
+ }
+}
+
+// TestTimesByMilestones tests TimesByMilestones functionality
+func testTimesByMilestones(t *testing.T) {
+ kases := []struct {
+ name string
+ unixfrom int64
+ unixto int64
+ orgname int64
+ expected []organization.WorktimeSumByMilestones
+ }{
+ {
+ name: "Full sum for org 1",
+ unixfrom: 0,
+ unixto: 9223372036854775807,
+ orgname: 1,
+ expected: []organization.WorktimeSumByMilestones(nil),
+ },
+ {
+ name: "Full sum for org 2",
+ unixfrom: 0,
+ unixto: 9223372036854775807,
+ orgname: 2,
+ expected: []organization.WorktimeSumByMilestones{
+ {
+ RepoName: "repo1",
+ MilestoneName: "",
+ MilestoneID: 0,
+ SumTime: 401,
+ HideRepoName: false,
+ },
+ {
+ RepoName: "repo1",
+ MilestoneName: "milestone1",
+ MilestoneID: 1,
+ SumTime: 3682,
+ HideRepoName: true,
+ },
+ {
+ RepoName: "repo2",
+ MilestoneName: "",
+ MilestoneID: 0,
+ SumTime: 75,
+ HideRepoName: false,
+ },
+ },
+ },
+ {
+ name: "Simple time bound",
+ unixfrom: 946684801,
+ unixto: 946684802,
+ orgname: 2,
+ expected: []organization.WorktimeSumByMilestones{
+ {
+ RepoName: "repo1",
+ MilestoneName: "milestone1",
+ MilestoneID: 1,
+ SumTime: 3662,
+ HideRepoName: false,
+ },
+ },
+ },
+ {
+ name: "Both times inclusive",
+ unixfrom: 946684801,
+ unixto: 946684801,
+ orgname: 2,
+ expected: []organization.WorktimeSumByMilestones{
+ {
+ RepoName: "repo1",
+ MilestoneName: "milestone1",
+ MilestoneID: 1,
+ SumTime: 3661,
+ HideRepoName: false,
+ },
+ },
+ },
+ {
+ name: "Should ignore deleted",
+ unixfrom: 947688814,
+ unixto: 947688815,
+ orgname: 2,
+ expected: []organization.WorktimeSumByMilestones{
+ {
+ RepoName: "repo2",
+ MilestoneName: "",
+ MilestoneID: 0,
+ SumTime: 71,
+ HideRepoName: false,
+ },
+ },
+ },
+ }
+
+ // Run test kases
+ for _, kase := range kases {
+ t.Run(kase.name, func(t *testing.T) {
+ org, err := organization.GetOrgByID(db.DefaultContext, kase.orgname)
+ require.NoError(t, err)
+ results, err := organization.GetWorktimeByMilestones(org, kase.unixfrom, kase.unixto)
+ if assert.NoError(t, err) {
+ assert.Equal(t, kase.expected, results)
+ }
+ })
+ }
+}
+
+// TestTimesByMembers tests TimesByMembers functionality
+func testTimesByMembers(t *testing.T) {
+ kases := []struct {
+ name string
+ unixfrom int64
+ unixto int64
+ orgname int64
+ expected []organization.WorktimeSumByMembers
+ }{
+ {
+ name: "Full sum for org 1",
+ unixfrom: 0,
+ unixto: 9223372036854775807,
+ orgname: 1,
+ expected: []organization.WorktimeSumByMembers(nil),
+ },
+ {
+ // Test case: Sum of times forever in org no. 2
+ name: "Full sum for org 2",
+ unixfrom: 0,
+ unixto: 9223372036854775807,
+ orgname: 2,
+ expected: []organization.WorktimeSumByMembers{
+ {
+ UserName: "user2",
+ SumTime: 3666,
+ },
+ {
+ UserName: "user1",
+ SumTime: 491,
+ },
+ },
+ },
+ {
+ name: "Simple time bound",
+ unixfrom: 946684801,
+ unixto: 946684802,
+ orgname: 2,
+ expected: []organization.WorktimeSumByMembers{
+ {
+ UserName: "user2",
+ SumTime: 3662,
+ },
+ },
+ },
+ {
+ name: "Both times inclusive",
+ unixfrom: 946684801,
+ unixto: 946684801,
+ orgname: 2,
+ expected: []organization.WorktimeSumByMembers{
+ {
+ UserName: "user2",
+ SumTime: 3661,
+ },
+ },
+ },
+ {
+ name: "Should ignore deleted",
+ unixfrom: 947688814,
+ unixto: 947688815,
+ orgname: 2,
+ expected: []organization.WorktimeSumByMembers{
+ {
+ UserName: "user1",
+ SumTime: 71,
+ },
+ },
+ },
+ }
+
+ // Run test kases
+ for _, kase := range kases {
+ t.Run(kase.name, func(t *testing.T) {
+ org, err := organization.GetOrgByID(db.DefaultContext, kase.orgname)
+ assert.NoError(t, err)
+ results, err := organization.GetWorktimeByMembers(org, kase.unixfrom, kase.unixto)
+ assert.NoError(t, err)
+ assert.Equal(t, kase.expected, results)
+ })
+ }
+}
+
+func TestOrgWorktime(t *testing.T) {
+ // we need to run these tests in integration test because there are complex SQL queries
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ t.Run("ByRepos", testTimesByRepos)
+ t.Run("ByMilestones", testTimesByMilestones)
+ t.Run("ByMembers", testTimesByMembers)
+}
diff --git a/tests/integration/project_test.go b/tests/integration/project_test.go
index cdff9aa2fd..43a489d4c4 100644
--- a/tests/integration/project_test.go
+++ b/tests/integration/project_test.go
@@ -47,7 +47,7 @@ func TestMoveRepoProjectColumns(t *testing.T) {
err := project_model.NewProject(db.DefaultContext, &project1)
assert.NoError(t, err)
- for i := 0; i < 3; i++ {
+ for i := range 3 {
err = project_model.NewColumn(db.DefaultContext, &project_model.Column{
Title: fmt.Sprintf("column %d", i+1),
ProjectID: project1.ID,
@@ -78,10 +78,10 @@ func TestMoveRepoProjectColumns(t *testing.T) {
columnsAfter, err := project1.GetColumns(db.DefaultContext)
assert.NoError(t, err)
- assert.Len(t, columns, 3)
- assert.EqualValues(t, columns[1].ID, columnsAfter[0].ID)
- assert.EqualValues(t, columns[2].ID, columnsAfter[1].ID)
- assert.EqualValues(t, columns[0].ID, columnsAfter[2].ID)
+ assert.Len(t, columnsAfter, 3)
+ assert.Equal(t, columns[1].ID, columnsAfter[0].ID)
+ assert.Equal(t, columns[2].ID, columnsAfter[1].ID)
+ assert.Equal(t, columns[0].ID, columnsAfter[2].ID)
assert.NoError(t, project_model.DeleteProjectByID(db.DefaultContext, project1.ID))
}
diff --git a/tests/integration/pull_commit_test.go b/tests/integration/pull_commit_test.go
index 8d98349fd3..9f3b1a9ef5 100644
--- a/tests/integration/pull_commit_test.go
+++ b/tests/integration/pull_commit_test.go
@@ -5,30 +5,37 @@ package integration
import (
"net/http"
- "net/url"
"testing"
pull_service "code.gitea.io/gitea/services/pull"
+ "code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestListPullCommits(t *testing.T) {
- onGiteaRun(t, func(t *testing.T, u *url.URL) {
- session := loginUser(t, "user5")
- req := NewRequest(t, "GET", "/user2/repo1/pulls/3/commits/list")
- resp := session.MakeRequest(t, req, http.StatusOK)
-
- var pullCommitList struct {
- Commits []pull_service.CommitInfo `json:"commits"`
- LastReviewCommitSha string `json:"last_review_commit_sha"`
- }
- DecodeJSON(t, resp, &pullCommitList)
-
- if assert.Len(t, pullCommitList.Commits, 2) {
- assert.Equal(t, "985f0301dba5e7b34be866819cd15ad3d8f508ee", pullCommitList.Commits[0].ID)
- assert.Equal(t, "5c050d3b6d2db231ab1f64e324f1b6b9a0b181c2", pullCommitList.Commits[1].ID)
- }
- assert.Equal(t, "4a357436d925b5c974181ff12a994538ddc5a269", pullCommitList.LastReviewCommitSha)
+ defer tests.PrepareTestEnv(t)()
+
+ session := loginUser(t, "user5")
+ req := NewRequest(t, "GET", "/user2/repo1/pulls/3/commits/list")
+ resp := session.MakeRequest(t, req, http.StatusOK)
+
+ var pullCommitList struct {
+ Commits []pull_service.CommitInfo `json:"commits"`
+ LastReviewCommitSha string `json:"last_review_commit_sha"`
+ }
+ DecodeJSON(t, resp, &pullCommitList)
+
+ require.Len(t, pullCommitList.Commits, 2)
+ assert.Equal(t, "985f0301dba5e7b34be866819cd15ad3d8f508ee", pullCommitList.Commits[0].ID)
+ assert.Equal(t, "5c050d3b6d2db231ab1f64e324f1b6b9a0b181c2", pullCommitList.Commits[1].ID)
+ assert.Equal(t, "4a357436d925b5c974181ff12a994538ddc5a269", pullCommitList.LastReviewCommitSha)
+
+ t.Run("CommitBlobExcerpt", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ req = NewRequest(t, "GET", "/user2/repo1/blob_excerpt/985f0301dba5e7b34be866819cd15ad3d8f508ee?last_left=0&last_right=0&left=2&right=2&left_hunk_size=2&right_hunk_size=2&path=README.md&style=split&direction=up")
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ assert.Contains(t, resp.Body.String(), `<td class="lines-code lines-code-new"><code class="code-inner"># repo1</code>`)
})
}
diff --git a/tests/integration/pull_compare_test.go b/tests/integration/pull_compare_test.go
index 106774aa54..f95a2f1690 100644
--- a/tests/integration/pull_compare_test.go
+++ b/tests/integration/pull_compare_test.go
@@ -13,7 +13,6 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/test"
repo_service "code.gitea.io/gitea/services/repository"
"code.gitea.io/gitea/tests"
@@ -24,23 +23,38 @@ import (
func TestPullCompare(t *testing.T) {
defer tests.PrepareTestEnv(t)()
- session := loginUser(t, "user2")
- req := NewRequest(t, "GET", "/user2/repo1/pulls")
- resp := session.MakeRequest(t, req, http.StatusOK)
- htmlDoc := NewHTMLParser(t, resp.Body)
- link, exists := htmlDoc.doc.Find(".new-pr-button").Attr("href")
- assert.True(t, exists, "The template has changed")
+ t.Run("PullsNewRedirect", func(t *testing.T) {
+ req := NewRequest(t, "GET", "/user2/repo1/pulls/new/foo")
+ resp := MakeRequest(t, req, http.StatusSeeOther)
+ redirect := test.RedirectURL(resp)
+ assert.Equal(t, "/user2/repo1/compare/master...foo?expand=1", redirect)
- req = NewRequest(t, "GET", link)
- resp = session.MakeRequest(t, req, http.StatusOK)
- assert.EqualValues(t, http.StatusOK, resp.Code)
+ req = NewRequest(t, "GET", "/user13/repo11/pulls/new/foo")
+ resp = MakeRequest(t, req, http.StatusSeeOther)
+ redirect = test.RedirectURL(resp)
+ assert.Equal(t, "/user12/repo10/compare/master...user13:foo?expand=1", redirect)
+ })
+
+ t.Run("ButtonsExist", func(t *testing.T) {
+ session := loginUser(t, "user2")
+
+ // test the "New PR" button
+ req := NewRequest(t, "GET", "/user2/repo1/pulls")
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ link, exists := htmlDoc.doc.Find(".new-pr-button").Attr("href")
+ assert.True(t, exists, "The template has changed")
+ req = NewRequest(t, "GET", link)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ assert.Equal(t, http.StatusOK, resp.Code)
- // test the edit button in the PR diff view
- req = NewRequest(t, "GET", "/user2/repo1/pulls/3/files")
- resp = session.MakeRequest(t, req, http.StatusOK)
- doc := NewHTMLParser(t, resp.Body)
- editButtonCount := doc.doc.Find(".diff-file-header-actions a[href*='/_edit/']").Length()
- assert.Positive(t, editButtonCount, "Expected to find a button to edit a file in the PR diff view but there were none")
+ // test the edit button in the PR diff view
+ req = NewRequest(t, "GET", "/user2/repo1/pulls/3/files")
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ doc := NewHTMLParser(t, resp.Body)
+ editButtonCount := doc.doc.Find(".diff-file-header-actions a[href*='/_edit/']").Length()
+ assert.Positive(t, editButtonCount, "Expected to find a button to edit a file in the PR diff view but there were none")
+ })
onGiteaRun(t, func(t *testing.T, u *url.URL) {
defer tests.PrepareTestEnv(t)()
@@ -54,24 +68,23 @@ func TestPullCompare(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
issueIndex := unittest.AssertExistsAndLoadBean(t, &issues_model.IssueIndex{GroupID: repo1.ID}, unittest.OrderBy("group_id ASC"))
prFilesURL := fmt.Sprintf("/user2/repo1/pulls/%d/files", issueIndex.MaxIndex)
- req = NewRequest(t, "GET", prFilesURL)
- resp = session.MakeRequest(t, req, http.StatusOK)
+ req := NewRequest(t, "GET", prFilesURL)
+ resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
editButtonCount := doc.doc.Find(".diff-file-header-actions a[href*='/_edit/']").Length()
assert.Positive(t, editButtonCount, "Expected to find a button to edit a file in the PR diff view but there were none")
repoForked := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: "repo1"})
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
// delete the head repository and revisit the PR diff view
- err := repo_service.DeleteRepositoryDirectly(db.DefaultContext, user2, repoForked.ID)
+ err := repo_service.DeleteRepositoryDirectly(db.DefaultContext, repoForked.ID)
assert.NoError(t, err)
req = NewRequest(t, "GET", prFilesURL)
resp = session.MakeRequest(t, req, http.StatusOK)
doc = NewHTMLParser(t, resp.Body)
editButtonCount = doc.doc.Find(".diff-file-header-actions a[href*='/_edit/']").Length()
- assert.EqualValues(t, 0, editButtonCount, "Expected not to find a button to edit a file in the PR diff view because head repository has been deleted")
+ assert.Equal(t, 0, editButtonCount, "Expected not to find a button to edit a file in the PR diff view because head repository has been deleted")
})
}
@@ -95,7 +108,7 @@ func TestPullCompare_EnableAllowEditsFromMaintainer(t *testing.T) {
// user2 (admin of repo3) goes to the PR files page
user2Session := loginUser(t, "user2")
- resp = user2Session.MakeRequest(t, NewRequest(t, "GET", fmt.Sprintf("%s/files", prURL)), http.StatusOK)
+ resp = user2Session.MakeRequest(t, NewRequest(t, "GET", prURL+"/files"), http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
nodes := htmlDoc.doc.Find(".diff-file-box[data-new-filename=\"README.md\"] .diff-file-header-actions .tippy-target a")
if assert.Equal(t, 1, nodes.Length()) {
@@ -112,14 +125,14 @@ func TestPullCompare_EnableAllowEditsFromMaintainer(t *testing.T) {
htmlDoc = NewHTMLParser(t, resp.Body)
dataURL, exists := htmlDoc.doc.Find("#allow-edits-from-maintainers").Attr("data-url")
assert.True(t, exists)
- req := NewRequestWithValues(t, "POST", fmt.Sprintf("%s/set_allow_maintainer_edit", dataURL), map[string]string{
+ req := NewRequestWithValues(t, "POST", dataURL+"/set_allow_maintainer_edit", map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"allow_maintainer_edit": "true",
})
user4Session.MakeRequest(t, req, http.StatusOK)
// user2 (admin of repo3) goes to the PR files page again
- resp = user2Session.MakeRequest(t, NewRequest(t, "GET", fmt.Sprintf("%s/files", prURL)), http.StatusOK)
+ resp = user2Session.MakeRequest(t, NewRequest(t, "GET", prURL+"/files"), http.StatusOK)
htmlDoc = NewHTMLParser(t, resp.Body)
nodes = htmlDoc.doc.Find(".diff-file-box[data-new-filename=\"README.md\"] .diff-file-header-actions .tippy-target a")
if assert.Equal(t, 2, nodes.Length()) {
@@ -146,7 +159,8 @@ func TestPullCompare_EnableAllowEditsFromMaintainer(t *testing.T) {
"commit_summary": "user2 updated the file",
"commit_choice": "direct",
})
- user2Session.MakeRequest(t, req, http.StatusSeeOther)
+ resp = user2Session.MakeRequest(t, req, http.StatusOK)
+ assert.NotEmpty(t, test.RedirectURL(resp))
}
}
})
diff --git a/tests/integration/pull_create_test.go b/tests/integration/pull_create_test.go
index 162ea532c8..179c84e673 100644
--- a/tests/integration/pull_create_test.go
+++ b/tests/integration/pull_create_test.go
@@ -265,7 +265,7 @@ func TestCreateAgitPullWithReadPermission(t *testing.T) {
t.Run("add commit", doGitAddSomeCommits(dstPath, "master"))
t.Run("do agit pull create", func(t *testing.T) {
- err := git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master", "-o").AddDynamicArguments("topic=" + "test-topic").Run(&git.RunOpts{Dir: dstPath})
+ err := git.NewCommand("push", "origin", "HEAD:refs/for/master", "-o").AddDynamicArguments("topic="+"test-topic").Run(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
})
})
@@ -293,10 +293,10 @@ func TestCreatePullWhenBlocked(t *testing.T) {
// sessionBase := loginUser(t, "user2")
token := getUserToken(t, RepoOwner, auth_model.AccessTokenScopeWriteUser)
- req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/blocks/%s", ForkOwner)).
+ req := NewRequest(t, "GET", "/api/v1/user/blocks/"+ForkOwner).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNotFound)
- req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/blocks/%s", ForkOwner)).
+ req = NewRequest(t, "PUT", "/api/v1/user/blocks/"+ForkOwner).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)
@@ -308,7 +308,7 @@ func TestCreatePullWhenBlocked(t *testing.T) {
// Teardown
// Unblock user
- req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/blocks/%s", ForkOwner)).
+ req = NewRequest(t, "DELETE", "/api/v1/user/blocks/"+ForkOwner).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)
})
diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go
index 2edc95d4c8..73b4c22070 100644
--- a/tests/integration/pull_merge_test.go
+++ b/tests/integration/pull_merge_test.go
@@ -5,7 +5,6 @@ package integration
import (
"bytes"
- "context"
"fmt"
"net/http"
"net/http/httptest"
@@ -27,6 +26,7 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
+ "code.gitea.io/gitea/modules/commitstatus"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/queue"
@@ -67,7 +67,7 @@ func testPullMerge(t *testing.T, session *TestSession, user, repo, pullnum strin
}{}
DecodeJSON(t, resp, &respJSON)
- assert.EqualValues(t, fmt.Sprintf("/%s/%s/pulls/%s", user, repo, pullnum), respJSON.Redirect)
+ assert.Equal(t, fmt.Sprintf("/%s/%s/pulls/%s", user, repo, pullnum), respJSON.Redirect)
return resp
}
@@ -101,7 +101,7 @@ func TestPullMerge(t *testing.T) {
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
- assert.EqualValues(t, "pulls", elem[3])
+ assert.Equal(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false)
hookTasks, err = webhook.HookTasks(db.DefaultContext, 1, 1)
@@ -123,7 +123,7 @@ func TestPullRebase(t *testing.T) {
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
- assert.EqualValues(t, "pulls", elem[3])
+ assert.Equal(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleRebase, false)
hookTasks, err = webhook.HookTasks(db.DefaultContext, 1, 1)
@@ -145,7 +145,7 @@ func TestPullRebaseMerge(t *testing.T) {
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
- assert.EqualValues(t, "pulls", elem[3])
+ assert.Equal(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleRebaseMerge, false)
hookTasks, err = webhook.HookTasks(db.DefaultContext, 1, 1)
@@ -168,7 +168,7 @@ func TestPullSquash(t *testing.T) {
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
- assert.EqualValues(t, "pulls", elem[3])
+ assert.Equal(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleSquash, false)
hookTasks, err = webhook.HookTasks(db.DefaultContext, 1, 1)
@@ -186,7 +186,7 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "feature/test", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
- assert.EqualValues(t, "pulls", elem[3])
+ assert.Equal(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false)
// Check PR branch deletion
@@ -199,7 +199,7 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
assert.NotEmpty(t, respJSON.Redirect, "Redirected URL is not found")
elem = strings.Split(respJSON.Redirect, "/")
- assert.EqualValues(t, "pulls", elem[3])
+ assert.Equal(t, "pulls", elem[3])
// Check branch deletion result
req := NewRequest(t, "GET", respJSON.Redirect)
@@ -208,7 +208,7 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
htmlDoc := NewHTMLParser(t, resp.Body)
resultMsg := htmlDoc.doc.Find(".ui.message>p").Text()
- assert.EqualValues(t, "Branch \"user1/repo1:feature/test\" has been deleted.", resultMsg)
+ assert.Equal(t, "Branch \"user1/repo1:feature/test\" has been deleted.", resultMsg)
})
}
@@ -266,11 +266,11 @@ func TestCantMergeConflict(t *testing.T) {
gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo1)
assert.NoError(t, err)
- err = pull_service.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "CONFLICT", false)
+ err = pull_service.Merge(t.Context(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "CONFLICT", false)
assert.Error(t, err, "Merge should return an error due to conflict")
assert.True(t, pull_service.IsErrMergeConflicts(err), "Merge error is not a conflict error")
- err = pull_service.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleRebase, "", "CONFLICT", false)
+ err = pull_service.Merge(t.Context(), pr, user1, gitRepo, repo_model.MergeStyleRebase, "", "CONFLICT", false)
assert.Error(t, err, "Merge should return an error due to conflict")
assert.True(t, pull_service.IsErrRebaseConflicts(err), "Merge error is not a conflict error")
gitRepo.Close()
@@ -294,12 +294,12 @@ func TestCantMergeUnrelated(t *testing.T) {
})
path := repo_model.RepoPath(user1.Name, repo1.Name)
- err := git.NewCommand(git.DefaultContext, "read-tree", "--empty").Run(&git.RunOpts{Dir: path})
+ err := git.NewCommand("read-tree", "--empty").Run(git.DefaultContext, &git.RunOpts{Dir: path})
assert.NoError(t, err)
- stdin := bytes.NewBufferString("Unrelated File")
+ stdin := strings.NewReader("Unrelated File")
var stdout strings.Builder
- err = git.NewCommand(git.DefaultContext, "hash-object", "-w", "--stdin").Run(&git.RunOpts{
+ err = git.NewCommand("hash-object", "-w", "--stdin").Run(git.DefaultContext, &git.RunOpts{
Dir: path,
Stdin: stdin,
Stdout: &stdout,
@@ -308,10 +308,10 @@ func TestCantMergeUnrelated(t *testing.T) {
assert.NoError(t, err)
sha := strings.TrimSpace(stdout.String())
- _, _, err = git.NewCommand(git.DefaultContext, "update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments("100644", sha, "somewher-over-the-rainbow").RunStdString(&git.RunOpts{Dir: path})
+ _, _, err = git.NewCommand("update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments("100644", sha, "somewher-over-the-rainbow").RunStdString(git.DefaultContext, &git.RunOpts{Dir: path})
assert.NoError(t, err)
- treeSha, _, err := git.NewCommand(git.DefaultContext, "write-tree").RunStdString(&git.RunOpts{Dir: path})
+ treeSha, _, err := git.NewCommand("write-tree").RunStdString(git.DefaultContext, &git.RunOpts{Dir: path})
assert.NoError(t, err)
treeSha = strings.TrimSpace(treeSha)
@@ -331,8 +331,8 @@ func TestCantMergeUnrelated(t *testing.T) {
_, _ = messageBytes.WriteString("\n")
stdout.Reset()
- err = git.NewCommand(git.DefaultContext, "commit-tree").AddDynamicArguments(treeSha).
- Run(&git.RunOpts{
+ err = git.NewCommand("commit-tree").AddDynamicArguments(treeSha).
+ Run(git.DefaultContext, &git.RunOpts{
Env: env,
Dir: path,
Stdin: messageBytes,
@@ -341,7 +341,7 @@ func TestCantMergeUnrelated(t *testing.T) {
assert.NoError(t, err)
commitSha := strings.TrimSpace(stdout.String())
- _, _, err = git.NewCommand(git.DefaultContext, "branch", "unrelated").AddDynamicArguments(commitSha).RunStdString(&git.RunOpts{Dir: path})
+ _, _, err = git.NewCommand("branch", "unrelated").AddDynamicArguments(commitSha).RunStdString(git.DefaultContext, &git.RunOpts{Dir: path})
assert.NoError(t, err)
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n")
@@ -365,7 +365,7 @@ func TestCantMergeUnrelated(t *testing.T) {
BaseBranch: "base",
})
- err = pull_service.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "UNRELATED", false)
+ err = pull_service.Merge(t.Context(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "UNRELATED", false)
assert.Error(t, err, "Merge should return an error due to unrelated")
assert.True(t, pull_service.IsErrMergeUnrelatedHistories(err), "Merge error is not a unrelated histories error")
gitRepo.Close()
@@ -405,7 +405,7 @@ func TestFastForwardOnlyMerge(t *testing.T) {
gitRepo, err := git.OpenRepository(git.DefaultContext, repo_model.RepoPath(user1.Name, repo1.Name))
assert.NoError(t, err)
- err = pull_service.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "FAST-FORWARD-ONLY", false)
+ err = pull_service.Merge(t.Context(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "FAST-FORWARD-ONLY", false)
assert.NoError(t, err)
@@ -447,7 +447,7 @@ func TestCantFastForwardOnlyMergeDiverging(t *testing.T) {
gitRepo, err := git.OpenRepository(git.DefaultContext, repo_model.RepoPath(user1.Name, repo1.Name))
assert.NoError(t, err)
- err = pull_service.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "DIVERGING", false)
+ err = pull_service.Merge(t.Context(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "DIVERGING", false)
assert.Error(t, err, "Merge should return an error due to being for a diverging branch")
assert.True(t, pull_service.IsErrMergeDivergingFastForwardOnly(err), "Merge error is not a diverging fast-forward-only error")
@@ -545,11 +545,11 @@ func TestPullRetargetChildOnBranchDelete(t *testing.T) {
respBasePR := testPullCreate(t, session, "user2", "repo1", true, "master", "base-pr", "Base Pull Request")
elemBasePR := strings.Split(test.RedirectURL(respBasePR), "/")
- assert.EqualValues(t, "pulls", elemBasePR[3])
+ assert.Equal(t, "pulls", elemBasePR[3])
respChildPR := testPullCreate(t, session, "user1", "repo1", false, "base-pr", "child-pr", "Child Pull Request")
elemChildPR := strings.Split(test.RedirectURL(respChildPR), "/")
- assert.EqualValues(t, "pulls", elemChildPR[3])
+ assert.Equal(t, "pulls", elemChildPR[3])
testPullMerge(t, session, elemBasePR[1], elemBasePR[2], elemBasePR[4], repo_model.MergeStyleMerge, true)
@@ -565,8 +565,8 @@ func TestPullRetargetChildOnBranchDelete(t *testing.T) {
targetBranch := htmlDoc.doc.Find("#branch_target>a").Text()
prStatus := strings.TrimSpace(htmlDoc.doc.Find(".issue-title-meta>.issue-state-label").Text())
- assert.EqualValues(t, "master", targetBranch)
- assert.EqualValues(t, "Open", prStatus)
+ assert.Equal(t, "master", targetBranch)
+ assert.Equal(t, "Open", prStatus)
})
}
@@ -579,11 +579,13 @@ func TestPullDontRetargetChildOnWrongRepo(t *testing.T) {
respBasePR := testPullCreate(t, session, "user1", "repo1", false, "master", "base-pr", "Base Pull Request")
elemBasePR := strings.Split(test.RedirectURL(respBasePR), "/")
- assert.EqualValues(t, "pulls", elemBasePR[3])
+ assert.Equal(t, "pulls", elemBasePR[3])
respChildPR := testPullCreate(t, session, "user1", "repo1", true, "base-pr", "child-pr", "Child Pull Request")
elemChildPR := strings.Split(test.RedirectURL(respChildPR), "/")
- assert.EqualValues(t, "pulls", elemChildPR[3])
+ assert.Equal(t, "pulls", elemChildPR[3])
+
+ defer test.MockVariableValue(&setting.Repository.PullRequest.RetargetChildrenOnMerge, false)()
testPullMerge(t, session, elemBasePR[1], elemBasePR[2], elemBasePR[4], repo_model.MergeStyleMerge, true)
@@ -600,8 +602,8 @@ func TestPullDontRetargetChildOnWrongRepo(t *testing.T) {
targetBranch := htmlDoc.doc.Find("#branch_target>span").Text()
prStatus := strings.TrimSpace(htmlDoc.doc.Find(".issue-title-meta>.issue-state-label").Text())
- assert.EqualValues(t, "base-pr", targetBranch)
- assert.EqualValues(t, "Closed", prStatus)
+ assert.Equal(t, "base-pr", targetBranch)
+ assert.Equal(t, "Closed", prStatus)
})
}
@@ -613,7 +615,7 @@ func TestPullRequestMergedWithNoPermissionDeleteBranch(t *testing.T) {
respBasePR := testPullCreate(t, session, "user4", "repo1", false, "master", "base-pr", "Base Pull Request")
elemBasePR := strings.Split(test.RedirectURL(respBasePR), "/")
- assert.EqualValues(t, "pulls", elemBasePR[3])
+ assert.Equal(t, "pulls", elemBasePR[3])
// user2 has no permission to delete branch of repo user1/repo1
session2 := loginUser(t, "user2")
@@ -634,7 +636,7 @@ func TestPullMergeIndexerNotifier(t *testing.T) {
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
createPullResp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "Indexer notifier test pull")
- assert.NoError(t, queue.GetManager().FlushAll(context.Background(), 0))
+ assert.NoError(t, queue.GetManager().FlushAll(t.Context(), 0))
time.Sleep(time.Second)
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
@@ -664,7 +666,7 @@ func TestPullMergeIndexerNotifier(t *testing.T) {
// merge the pull request
elem := strings.Split(test.RedirectURL(createPullResp), "/")
- assert.EqualValues(t, "pulls", elem[3])
+ assert.Equal(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false)
// check if the issue is closed
@@ -673,7 +675,7 @@ func TestPullMergeIndexerNotifier(t *testing.T) {
})
assert.True(t, issue.IsClosed)
- assert.NoError(t, queue.GetManager().FlushAll(context.Background(), 0))
+ assert.NoError(t, queue.GetManager().FlushAll(t.Context(), 0))
time.Sleep(time.Second)
// search issues again
@@ -693,12 +695,12 @@ func testResetRepo(t *testing.T, repoPath, branch, commitID string) {
assert.NoError(t, err)
f.Close()
- repo, err := git.OpenRepository(context.Background(), repoPath)
+ repo, err := git.OpenRepository(t.Context(), repoPath)
assert.NoError(t, err)
defer repo.Close()
id, err := repo.GetBranchCommitID(branch)
assert.NoError(t, err)
- assert.EqualValues(t, commitID, id)
+ assert.Equal(t, commitID, id)
}
func TestPullAutoMergeAfterCommitStatusSucceed(t *testing.T) {
@@ -736,12 +738,12 @@ func TestPullAutoMergeAfterCommitStatusSucceed(t *testing.T) {
session.MakeRequest(t, req, http.StatusSeeOther)
// first time insert automerge record, return true
- scheduled, err := automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test")
+ scheduled, err := automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test", false)
assert.NoError(t, err)
assert.True(t, scheduled)
// second time insert automerge record, return false because it does exist
- scheduled, err = automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test")
+ scheduled, err = automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test", false)
assert.Error(t, err)
assert.False(t, scheduled)
@@ -767,7 +769,7 @@ func TestPullAutoMergeAfterCommitStatusSucceed(t *testing.T) {
}()
err = commitstatus_service.CreateCommitStatus(db.DefaultContext, baseRepo, user1, sha, &git_model.CommitStatus{
- State: api.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
TargetURL: "https://gitea.com",
Context: "gitea/actions",
})
@@ -820,12 +822,12 @@ func TestPullAutoMergeAfterCommitStatusSucceedAndApproval(t *testing.T) {
session.MakeRequest(t, req, http.StatusSeeOther)
// first time insert automerge record, return true
- scheduled, err := automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test")
+ scheduled, err := automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test", false)
assert.NoError(t, err)
assert.True(t, scheduled)
// second time insert automerge record, return false because it does exist
- scheduled, err = automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test")
+ scheduled, err = automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test", false)
assert.Error(t, err)
assert.False(t, scheduled)
@@ -847,7 +849,7 @@ func TestPullAutoMergeAfterCommitStatusSucceedAndApproval(t *testing.T) {
}()
err = commitstatus_service.CreateCommitStatus(db.DefaultContext, baseRepo, user1, sha, &git_model.CommitStatus{
- State: api.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
TargetURL: "https://gitea.com",
Context: "gitea/actions",
})
@@ -913,13 +915,13 @@ func TestPullAutoMergeAfterCommitStatusSucceedAndApprovalForAgitFlow(t *testing.
stderrBuf := &bytes.Buffer{}
- err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master", "-o").
+ err = git.NewCommand("push", "origin", "HEAD:refs/for/master", "-o").
AddDynamicArguments(`topic=test/head2`).
AddArguments("-o").
AddDynamicArguments(`title="create a test pull request with agit"`).
AddArguments("-o").
AddDynamicArguments(`description="This PR is a test pull request which created with agit"`).
- Run(&git.RunOpts{Dir: dstPath, Stderr: stderrBuf})
+ Run(git.DefaultContext, &git.RunOpts{Dir: dstPath, Stderr: stderrBuf})
assert.NoError(t, err)
assert.Contains(t, stderrBuf.String(), setting.AppURL+"user2/repo1/pulls/6")
@@ -949,12 +951,12 @@ func TestPullAutoMergeAfterCommitStatusSucceedAndApprovalForAgitFlow(t *testing.
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
// first time insert automerge record, return true
- scheduled, err := automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test")
+ scheduled, err := automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test", false)
assert.NoError(t, err)
assert.True(t, scheduled)
// second time insert automerge record, return false because it does exist
- scheduled, err = automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test")
+ scheduled, err = automerge.ScheduleAutoMerge(db.DefaultContext, user1, pr, repo_model.MergeStyleMerge, "auto merge test", false)
assert.Error(t, err)
assert.False(t, scheduled)
@@ -976,14 +978,12 @@ func TestPullAutoMergeAfterCommitStatusSucceedAndApprovalForAgitFlow(t *testing.
}()
err = commitstatus_service.CreateCommitStatus(db.DefaultContext, baseRepo, user1, sha, &git_model.CommitStatus{
- State: api.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
TargetURL: "https://gitea.com",
Context: "gitea/actions",
})
assert.NoError(t, err)
- time.Sleep(2 * time.Second)
-
// reload pr again
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
assert.False(t, pr.HasMerged)
@@ -996,8 +996,6 @@ func TestPullAutoMergeAfterCommitStatusSucceedAndApprovalForAgitFlow(t *testing.
htmlDoc := NewHTMLParser(t, resp.Body)
testSubmitReview(t, approveSession, htmlDoc.GetCSRF(), "user2", "repo1", strconv.Itoa(int(pr.Index)), sha, "approve", http.StatusOK)
- time.Sleep(2 * time.Second)
-
// realod pr again
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
assert.True(t, pr.HasMerged)
diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go
index 68de421413..1121751c88 100644
--- a/tests/integration/pull_review_test.go
+++ b/tests/integration/pull_review_test.go
@@ -57,7 +57,7 @@ func TestPullView_CodeOwner(t *testing.T) {
AutoInit: true,
ObjectFormatName: git.Sha1ObjectFormat.Name(),
DefaultBranch: "master",
- })
+ }, true)
assert.NoError(t, err)
// add CODEOWNERS to default branch
@@ -67,7 +67,7 @@ func TestPullView_CodeOwner(t *testing.T) {
{
Operation: "create",
TreePath: "CODEOWNERS",
- ContentReader: strings.NewReader("README.md @user5\n"),
+ ContentReader: strings.NewReader("README.md @user5\nuser8-file.md @user8\n"),
},
},
})
@@ -75,7 +75,7 @@ func TestPullView_CodeOwner(t *testing.T) {
t.Run("First Pull Request", func(t *testing.T) {
// create a new branch to prepare for pull request
- _, err = files_service.ChangeRepoFiles(db.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+ _, err := files_service.ChangeRepoFiles(db.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
NewBranch: "codeowner-basebranch",
Files: []*files_service.ChangeRepoFile{
{
@@ -95,17 +95,35 @@ func TestPullView_CodeOwner(t *testing.T) {
unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5})
assert.NoError(t, pr.LoadIssue(db.DefaultContext))
- err := issue_service.ChangeTitle(db.DefaultContext, pr.Issue, user2, "[WIP] Test Pull Request")
+ // update the file on the pr branch
+ _, err = files_service.ChangeRepoFiles(db.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
+ OldBranch: "codeowner-basebranch",
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "create",
+ TreePath: "user8-file.md",
+ ContentReader: strings.NewReader("# This is a new project2\n"),
+ },
+ },
+ })
+ assert.NoError(t, err)
+
+ reviewNotifiers, err := issue_service.PullRequestCodeOwnersReview(db.DefaultContext, pr)
+ assert.NoError(t, err)
+ assert.Len(t, reviewNotifiers, 1)
+ assert.EqualValues(t, 8, reviewNotifiers[0].Reviewer.ID)
+
+ err = issue_service.ChangeTitle(db.DefaultContext, pr.Issue, user2, "[WIP] Test Pull Request")
assert.NoError(t, err)
prUpdated1 := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
assert.NoError(t, prUpdated1.LoadIssue(db.DefaultContext))
- assert.EqualValues(t, "[WIP] Test Pull Request", prUpdated1.Issue.Title)
+ assert.Equal(t, "[WIP] Test Pull Request", prUpdated1.Issue.Title)
err = issue_service.ChangeTitle(db.DefaultContext, prUpdated1.Issue, user2, "Test Pull Request2")
assert.NoError(t, err)
prUpdated2 := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
assert.NoError(t, prUpdated2.LoadIssue(db.DefaultContext))
- assert.EqualValues(t, "Test Pull Request2", prUpdated2.Issue.Title)
+ assert.Equal(t, "Test Pull Request2", prUpdated2.Issue.Title)
})
// change the default branch CODEOWNERS file to change README.md's codeowner
@@ -193,7 +211,7 @@ func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) {
testEditFile(t, user1Session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "master", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
- assert.EqualValues(t, "pulls", elem[3])
+ assert.Equal(t, "pulls", elem[3])
testPullMerge(t, user1Session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false)
// Grab the CSRF token.
@@ -213,7 +231,7 @@ func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) {
testEditFileToNewBranch(t, user1Session, "user1", "repo1", "master", "a-test-branch", "README.md", "Hello, World (Editied...again)\n")
resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "a-test-branch", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
- assert.EqualValues(t, "pulls", elem[3])
+ assert.Equal(t, "pulls", elem[3])
testIssueClose(t, user1Session, elem[1], elem[2], elem[4])
// Grab the CSRF token.
diff --git a/tests/integration/pull_status_test.go b/tests/integration/pull_status_test.go
index ac9036ca96..49326a594a 100644
--- a/tests/integration/pull_status_test.go
+++ b/tests/integration/pull_status_test.go
@@ -13,9 +13,14 @@ import (
auth_model "code.gitea.io/gitea/models/auth"
git_model "code.gitea.io/gitea/models/git"
+ "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/commitstatus"
+ "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/test"
+ "code.gitea.io/gitea/services/pull"
"github.com/stretchr/testify/assert"
)
@@ -51,20 +56,20 @@ func TestPullCreate_CommitStatus(t *testing.T) {
commitID := path.Base(commitURL)
- statusList := []api.CommitStatusState{
- api.CommitStatusPending,
- api.CommitStatusError,
- api.CommitStatusFailure,
- api.CommitStatusSuccess,
- api.CommitStatusWarning,
+ statusList := []commitstatus.CommitStatusState{
+ commitstatus.CommitStatusPending,
+ commitstatus.CommitStatusError,
+ commitstatus.CommitStatusFailure,
+ commitstatus.CommitStatusSuccess,
+ commitstatus.CommitStatusWarning,
}
- statesIcons := map[api.CommitStatusState]string{
- api.CommitStatusPending: "octicon-dot-fill",
- api.CommitStatusSuccess: "octicon-check",
- api.CommitStatusError: "gitea-exclamation",
- api.CommitStatusFailure: "octicon-x",
- api.CommitStatusWarning: "gitea-exclamation",
+ statesIcons := map[commitstatus.CommitStatusState]string{
+ commitstatus.CommitStatusPending: "octicon-dot-fill",
+ commitstatus.CommitStatusSuccess: "octicon-check",
+ commitstatus.CommitStatusError: "gitea-exclamation",
+ commitstatus.CommitStatusFailure: "octicon-x",
+ commitstatus.CommitStatusWarning: "gitea-exclamation",
}
testCtx := NewAPITestContext(t, "user1", "repo1", auth_model.AccessTokenScopeWriteRepository)
@@ -86,7 +91,7 @@ func TestPullCreate_CommitStatus(t *testing.T) {
commitURL, exists = doc.doc.Find("#commits-table tbody tr td.sha a").Last().Attr("href")
assert.True(t, exists)
assert.NotEmpty(t, commitURL)
- assert.EqualValues(t, commitID, path.Base(commitURL))
+ assert.Equal(t, commitID, path.Base(commitURL))
cls, ok := doc.doc.Find("#commits-table tbody tr td.message .commit-status").Last().Attr("class")
assert.True(t, ok)
@@ -95,7 +100,7 @@ func TestPullCreate_CommitStatus(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: "repo1"})
css := unittest.AssertExistsAndLoadBean(t, &git_model.CommitStatusSummary{RepoID: repo1.ID, SHA: commitID})
- assert.EqualValues(t, api.CommitStatusWarning, css.State)
+ assert.Equal(t, commitstatus.CommitStatusSuccess, css.State)
})
}
@@ -124,7 +129,7 @@ func TestPullCreate_EmptyChangesWithDifferentCommits(t *testing.T) {
session := loginUser(t, "user1")
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "status1", "README.md", "status1")
- testEditFileToNewBranch(t, session, "user1", "repo1", "status1", "status1", "README.md", "# repo1\n\nDescription for repo1")
+ testEditFile(t, session, "user1", "repo1", "status1", "README.md", "# repo1\n\nDescription for repo1")
url := path.Join("user1", "repo1", "compare", "master...status1")
req := NewRequestWithValues(t, "POST", url,
@@ -165,3 +170,74 @@ func TestPullCreate_EmptyChangesWithSameCommits(t *testing.T) {
assert.Contains(t, text, "This branch is already included in the target branch. There is nothing to merge.")
})
}
+
+func TestPullStatusDelayCheck(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ defer test.MockVariableValue(&setting.Repository.PullRequest.DelayCheckForInactiveDays, 1)()
+ defer test.MockVariableValue(&pull.AddPullRequestToCheckQueue)()
+
+ session := loginUser(t, "user2")
+
+ run := func(t *testing.T, fn func(*testing.T)) (issue3 *issues.Issue, checkedPrID int64) {
+ pull.AddPullRequestToCheckQueue = func(prID int64) {
+ checkedPrID = prID
+ }
+ fn(t)
+ issue3 = unittest.AssertExistsAndLoadBean(t, &issues.Issue{RepoID: 1, Index: 3})
+ _ = issue3.LoadPullRequest(t.Context())
+ return issue3, checkedPrID
+ }
+
+ assertReloadingInterval := func(t *testing.T, interval string) {
+ req := NewRequest(t, "GET", "/user2/repo1/pulls/3")
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ attr := "data-pull-merge-box-reloading-interval"
+ if interval == "" {
+ assert.NotContains(t, resp.Body.String(), attr)
+ } else {
+ assert.Contains(t, resp.Body.String(), fmt.Sprintf(`%s="%v"`, attr, interval))
+ }
+ }
+
+ // PR issue3 is merageable at the beginning
+ issue3, checkedPrID := run(t, func(t *testing.T) {})
+ assert.Equal(t, issues.PullRequestStatusMergeable, issue3.PullRequest.Status)
+ assert.Zero(t, checkedPrID)
+ assertReloadingInterval(t, "") // the PR is mergeable, so no need to reload the merge box
+
+ // setting.IsProd = false // it would cause data-race because the queue handlers might be running and reading its value
+ // assertReloadingInterval(t, "1") // make sure dev mode always do merge box reloading, to make sure the UI logic won't break
+ // setting.IsProd = true
+
+ // when base branch changes, PR status should be updated, but it is inactive for long time, so no real check
+ issue3, checkedPrID = run(t, func(t *testing.T) {
+ testEditFile(t, session, "user2", "repo1", "master", "README.md", "new content 1")
+ })
+ assert.Equal(t, issues.PullRequestStatusChecking, issue3.PullRequest.Status)
+ assert.Zero(t, checkedPrID)
+ assertReloadingInterval(t, "2000") // the PR status is "checking", so try to reload the merge box
+
+ // view a PR with status=checking, it starts the real check
+ issue3, checkedPrID = run(t, func(t *testing.T) {
+ req := NewRequest(t, "GET", "/user2/repo1/pulls/3")
+ session.MakeRequest(t, req, http.StatusOK)
+ })
+ assert.Equal(t, issues.PullRequestStatusChecking, issue3.PullRequest.Status)
+ assert.Equal(t, issue3.PullRequest.ID, checkedPrID)
+
+ // when base branch changes, still so no real check
+ issue3, checkedPrID = run(t, func(t *testing.T) {
+ testEditFile(t, session, "user2", "repo1", "master", "README.md", "new content 2")
+ })
+ assert.Equal(t, issues.PullRequestStatusChecking, issue3.PullRequest.Status)
+ assert.Zero(t, checkedPrID)
+
+ // then allow to check PRs without delay, when base branch changes, the PRs will be checked
+ setting.Repository.PullRequest.DelayCheckForInactiveDays = -1
+ issue3, checkedPrID = run(t, func(t *testing.T) {
+ testEditFile(t, session, "user2", "repo1", "master", "README.md", "new content 3")
+ })
+ assert.Equal(t, issues.PullRequestStatusChecking, issue3.PullRequest.Status)
+ assert.Equal(t, issue3.PullRequest.ID, checkedPrID)
+ })
+}
diff --git a/tests/integration/pull_update_test.go b/tests/integration/pull_update_test.go
index dfe47c1053..d28537112d 100644
--- a/tests/integration/pull_update_test.go
+++ b/tests/integration/pull_update_test.go
@@ -33,8 +33,8 @@ func TestAPIPullUpdate(t *testing.T) {
// Test GetDiverging
diffCount, err := pull_service.GetDiverging(git.DefaultContext, pr)
assert.NoError(t, err)
- assert.EqualValues(t, 1, diffCount.Behind)
- assert.EqualValues(t, 1, diffCount.Ahead)
+ assert.Equal(t, 1, diffCount.Behind)
+ assert.Equal(t, 1, diffCount.Ahead)
assert.NoError(t, pr.LoadBaseRepo(db.DefaultContext))
assert.NoError(t, pr.LoadIssue(db.DefaultContext))
@@ -47,8 +47,8 @@ func TestAPIPullUpdate(t *testing.T) {
// Test GetDiverging after update
diffCount, err = pull_service.GetDiverging(git.DefaultContext, pr)
assert.NoError(t, err)
- assert.EqualValues(t, 0, diffCount.Behind)
- assert.EqualValues(t, 2, diffCount.Ahead)
+ assert.Equal(t, 0, diffCount.Behind)
+ assert.Equal(t, 2, diffCount.Ahead)
})
}
@@ -62,8 +62,8 @@ func TestAPIPullUpdateByRebase(t *testing.T) {
// Test GetDiverging
diffCount, err := pull_service.GetDiverging(git.DefaultContext, pr)
assert.NoError(t, err)
- assert.EqualValues(t, 1, diffCount.Behind)
- assert.EqualValues(t, 1, diffCount.Ahead)
+ assert.Equal(t, 1, diffCount.Behind)
+ assert.Equal(t, 1, diffCount.Ahead)
assert.NoError(t, pr.LoadBaseRepo(db.DefaultContext))
assert.NoError(t, pr.LoadIssue(db.DefaultContext))
@@ -76,8 +76,8 @@ func TestAPIPullUpdateByRebase(t *testing.T) {
// Test GetDiverging after update
diffCount, err = pull_service.GetDiverging(git.DefaultContext, pr)
assert.NoError(t, err)
- assert.EqualValues(t, 0, diffCount.Behind)
- assert.EqualValues(t, 1, diffCount.Ahead)
+ assert.Equal(t, 0, diffCount.Behind)
+ assert.Equal(t, 1, diffCount.Ahead)
})
}
@@ -115,12 +115,12 @@ func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *issues_mod
OldBranch: "master",
NewBranch: "master",
Author: &files_service.IdentityOptions{
- Name: actor.Name,
- Email: actor.Email,
+ GitUserName: actor.Name,
+ GitUserEmail: actor.Email,
},
Committer: &files_service.IdentityOptions{
- Name: actor.Name,
- Email: actor.Email,
+ GitUserName: actor.Name,
+ GitUserEmail: actor.Email,
},
Dates: &files_service.CommitDateOptions{
Author: time.Now(),
@@ -142,12 +142,12 @@ func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *issues_mod
OldBranch: "master",
NewBranch: "newBranch",
Author: &files_service.IdentityOptions{
- Name: actor.Name,
- Email: actor.Email,
+ GitUserName: actor.Name,
+ GitUserEmail: actor.Email,
},
Committer: &files_service.IdentityOptions{
- Name: actor.Name,
- Email: actor.Email,
+ GitUserName: actor.Name,
+ GitUserEmail: actor.Email,
},
Dates: &files_service.CommitDateOptions{
Author: time.Now(),
diff --git a/tests/integration/release_test.go b/tests/integration/release_test.go
index 40bd798d16..88a58787af 100644
--- a/tests/integration/release_test.go
+++ b/tests/integration/release_test.go
@@ -7,7 +7,6 @@ import (
"fmt"
"net/http"
"testing"
- "time"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
@@ -39,7 +38,7 @@ func createNewRelease(t *testing.T, session *TestSession, repoURL, tag, title st
postData["prerelease"] = "on"
}
if draft {
- postData["draft"] = "Save Draft"
+ postData["draft"] = "1"
}
req = NewRequestWithValues(t, "POST", link, postData)
@@ -54,12 +53,12 @@ func checkLatestReleaseAndCount(t *testing.T, session *TestSession, repoURL, ver
htmlDoc := NewHTMLParser(t, resp.Body)
labelText := htmlDoc.doc.Find("#release-list > li .detail .label").First().Text()
- assert.EqualValues(t, label, labelText)
+ assert.Equal(t, label, labelText)
titleText := htmlDoc.doc.Find("#release-list > li .detail h4 a").First().Text()
- assert.EqualValues(t, version, titleText)
+ assert.Equal(t, version, titleText)
releaseList := htmlDoc.doc.Find("#release-list > li")
- assert.EqualValues(t, count, releaseList.Length())
+ assert.Equal(t, count, releaseList.Length())
}
func TestViewReleases(t *testing.T) {
@@ -68,9 +67,6 @@ func TestViewReleases(t *testing.T) {
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user2/repo1/releases")
session.MakeRequest(t, req, http.StatusOK)
-
- // if CI is to slow this test fail, so lets wait a bit
- time.Sleep(time.Millisecond * 100)
}
func TestViewReleasesNoLogin(t *testing.T) {
@@ -118,7 +114,7 @@ func TestCreateReleasePaging(t *testing.T) {
session := loginUser(t, "user2")
// Create enough releases to have paging
- for i := 0; i < 12; i++ {
+ for i := range 12 {
version := fmt.Sprintf("v0.0.%d", i)
createNewRelease(t, session, "/user2/repo1", version, version, false, false)
}
@@ -157,14 +153,14 @@ func TestViewReleaseListNoLogin(t *testing.T) {
commitsToMain = append(commitsToMain, s.Find(".ahead > a").Text())
})
- assert.EqualValues(t, []string{
+ assert.Equal(t, []string{
"/user2/repo-release/releases/tag/empty-target-branch",
"/user2/repo-release/releases/tag/non-existing-target-branch",
"/user2/repo-release/releases/tag/v2.0",
"/user2/repo-release/releases/tag/v1.1",
"/user2/repo-release/releases/tag/v1.0",
}, links)
- assert.EqualValues(t, []string{
+ assert.Equal(t, []string{
"1 commits", // like v1.1
"1 commits", // like v1.1
"0 commits",
@@ -173,17 +169,25 @@ func TestViewReleaseListNoLogin(t *testing.T) {
}, commitsToMain)
}
-func TestViewSingleReleaseNoLogin(t *testing.T) {
+func TestViewSingleRelease(t *testing.T) {
defer tests.PrepareTestEnv(t)()
- req := NewRequest(t, "GET", "/user2/repo-release/releases/tag/v1.0")
- resp := MakeRequest(t, req, http.StatusOK)
-
- htmlDoc := NewHTMLParser(t, resp.Body)
- // check the "number of commits to main since this release"
- releaseList := htmlDoc.doc.Find("#release-list .ahead > a")
- assert.EqualValues(t, 1, releaseList.Length())
- assert.EqualValues(t, "3 commits", releaseList.First().Text())
+ t.Run("NoLogin", func(t *testing.T) {
+ req := NewRequest(t, "GET", "/user2/repo-release/releases/tag/v1.0")
+ resp := MakeRequest(t, req, http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ // check the "number of commits to main since this release"
+ releaseList := htmlDoc.doc.Find("#release-list .ahead > a")
+ assert.Equal(t, 1, releaseList.Length())
+ assert.Equal(t, "3 commits", releaseList.First().Text())
+ })
+ t.Run("Login", func(t *testing.T) {
+ session := loginUser(t, "user1")
+ req := NewRequest(t, "GET", "/user2/repo1/releases/tag/delete-tag") // "delete-tag" is the only one with is_tag=true (although strange name)
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ // the New Release button should contain the tag name
+ assert.Contains(t, resp.Body.String(), `<a class="ui small primary button" href="/user2/repo1/releases/new?tag=delete-tag">`)
+ })
}
func TestViewReleaseListLogin(t *testing.T) {
@@ -210,7 +214,7 @@ func TestViewReleaseListLogin(t *testing.T) {
links = append(links, link)
})
- assert.EqualValues(t, []string{
+ assert.Equal(t, []string{
"/user2/repo1/releases/tag/draft-release",
"/user2/repo1/releases/tag/v1.0",
"/user2/repo1/releases/tag/v1.1",
@@ -237,7 +241,7 @@ func TestViewTagsList(t *testing.T) {
tagNames = append(tagNames, s.Text())
})
- assert.EqualValues(t, []string{"v1.0", "delete-tag", "v1.1"}, tagNames)
+ assert.Equal(t, []string{"v1.0", "delete-tag", "v1.1"}, tagNames)
}
func TestDownloadReleaseAttachment(t *testing.T) {
diff --git a/tests/integration/rename_branch_test.go b/tests/integration/rename_branch_test.go
index 576264ba95..492fdf781b 100644
--- a/tests/integration/rename_branch_test.go
+++ b/tests/integration/rename_branch_test.go
@@ -11,7 +11,6 @@ import (
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
- gitea_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@@ -82,9 +81,8 @@ func testRenameBranch(t *testing.T, u *url.URL) {
"to": "branch1",
})
session.MakeRequest(t, req, http.StatusSeeOther)
- flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
- assert.NotNil(t, flashCookie)
- assert.Contains(t, flashCookie.Value, "error")
+ flashMsg := session.GetCookieFlashMessage()
+ assert.NotEmpty(t, flashMsg.ErrorMsg)
branch2 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
assert.Equal(t, "branch2", branch2.Name)
@@ -110,9 +108,8 @@ func testRenameBranch(t *testing.T, u *url.URL) {
})
session.MakeRequest(t, req, http.StatusSeeOther)
- flashCookie = session.GetCookie(gitea_context.CookieNameFlash)
- assert.NotNil(t, flashCookie)
- assert.Contains(t, flashCookie.Value, "success")
+ flashMsg = session.GetCookieFlashMessage()
+ assert.NotEmpty(t, flashMsg.SuccessMsg)
unittest.AssertNotExistsBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
diff --git a/tests/integration/repo_activity_test.go b/tests/integration/repo_activity_test.go
index b04560379d..d5025decba 100644
--- a/tests/integration/repo_activity_test.go
+++ b/tests/integration/repo_activity_test.go
@@ -9,7 +9,9 @@ import (
"strings"
"testing"
+ "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
@@ -24,7 +26,7 @@ func TestRepoActivity(t *testing.T) {
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
- assert.EqualValues(t, "pulls", elem[3])
+ assert.Equal(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false)
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/better_readme", "README.md", "Hello, World (Edited Again)\n")
@@ -61,5 +63,14 @@ func TestRepoActivity(t *testing.T) {
// Should be 3 new issues
list = htmlDoc.doc.Find("#new-issues").Next().Find("p.desc")
assert.Len(t, list.Nodes, 3)
+
+ // Non-existing default branch
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: "repo1"})
+ repo1.DefaultBranch = "no-such-branch"
+ _, _ = db.GetEngine(t.Context()).Cols("default_branch").Update(repo1)
+ req = NewRequest(t, "GET", "/user2/repo1/activity")
+ req.Header.Add("Accept", "text/html")
+ resp = session.MakeRequest(t, req, http.StatusNotFound)
+ assert.Contains(t, resp.Body.String(), `Default branch "no-such-branch" does not exist.`)
})
}
diff --git a/tests/integration/repo_branch_test.go b/tests/integration/repo_branch_test.go
index 2b4c417334..50ceb65330 100644
--- a/tests/integration/repo_branch_test.go
+++ b/tests/integration/repo_branch_test.go
@@ -11,13 +11,8 @@ import (
"strings"
"testing"
- auth_model "code.gitea.io/gitea/models/auth"
- org_model "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
- api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/tests"
@@ -142,19 +137,51 @@ func TestCreateBranchInvalidCSRF(t *testing.T) {
assert.Contains(t, resp.Body.String(), "Invalid CSRF token")
}
-func prepareBranch(t *testing.T, session *TestSession, repo *repo_model.Repository) {
- baseRefSubURL := fmt.Sprintf("branch/%s", repo.DefaultBranch)
-
+func prepareRecentlyPushedBranchTest(t *testing.T, headSession *TestSession, baseRepo, headRepo *repo_model.Repository) {
+ refSubURL := "branch/" + headRepo.DefaultBranch
+ baseRepoPath := baseRepo.OwnerName + "/" + baseRepo.Name
+ headRepoPath := headRepo.OwnerName + "/" + headRepo.Name
+ // Case 1: Normal branch changeset to display pushed message
// create branch with no new commit
- testCreateBranch(t, session, repo.OwnerName, repo.Name, baseRefSubURL, "no-commit", http.StatusSeeOther)
+ testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, refSubURL, "no-commit", http.StatusSeeOther)
// create branch with commit
- testCreateBranch(t, session, repo.OwnerName, repo.Name, baseRefSubURL, "new-commit", http.StatusSeeOther)
- testAPINewFile(t, session, repo.OwnerName, repo.Name, "new-commit", "new-commit.txt", "new-commit")
+ testAPINewFile(t, headSession, headRepo.OwnerName, headRepo.Name, "new-commit", fmt.Sprintf("new-file-%s.txt", headRepo.Name), "new-commit")
+
+ // create a branch then delete it
+ testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "deleted-branch", http.StatusSeeOther)
+ testUIDeleteBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "deleted-branch")
+
+ // only `new-commit` branch has commits ahead the base branch
+ checkRecentlyPushedNewBranches(t, headSession, headRepoPath, []string{"new-commit"})
+ if baseRepo.RepoPath() != headRepo.RepoPath() {
+ checkRecentlyPushedNewBranches(t, headSession, baseRepoPath, []string{fmt.Sprintf("%v:new-commit", headRepo.FullName())})
+ }
+
+ // Case 2: Create PR so that `new-commit` branch will not show
+ testCreatePullToDefaultBranch(t, headSession, baseRepo, headRepo, "new-commit", "merge new-commit to default branch")
+ // No push message show because of active PR
+ checkRecentlyPushedNewBranches(t, headSession, headRepoPath, []string{})
+ if baseRepo.RepoPath() != headRepo.RepoPath() {
+ checkRecentlyPushedNewBranches(t, headSession, baseRepoPath, []string{})
+ }
+}
+
+func prepareRecentlyPushedBranchSpecialTest(t *testing.T, session *TestSession, baseRepo, headRepo *repo_model.Repository) {
+ refSubURL := "branch/" + headRepo.DefaultBranch
+ baseRepoPath := baseRepo.OwnerName + "/" + baseRepo.Name
+ headRepoPath := headRepo.OwnerName + "/" + headRepo.Name
+ // create branch with no new commit
+ testCreateBranch(t, session, headRepo.OwnerName, headRepo.Name, refSubURL, "no-commit-special", http.StatusSeeOther)
+
+ // update base (default) branch before head branch is updated
+ testAPINewFile(t, session, baseRepo.OwnerName, baseRepo.Name, baseRepo.DefaultBranch, fmt.Sprintf("new-file-special-%s.txt", headRepo.Name), "new-commit")
- // create deleted branch
- testCreateBranch(t, session, repo.OwnerName, repo.Name, "branch/new-commit", "deleted-branch", http.StatusSeeOther)
- testUIDeleteBranch(t, session, repo.OwnerName, repo.Name, "deleted-branch")
+ // Though we have new `no-commit` branch, but the headBranch is not newer or commits ahead baseBranch. No message show.
+ checkRecentlyPushedNewBranches(t, session, headRepoPath, []string{})
+ if baseRepo.RepoPath() != headRepo.RepoPath() {
+ checkRecentlyPushedNewBranches(t, session, baseRepoPath, []string{})
+ }
}
func testCreatePullToDefaultBranch(t *testing.T, session *TestSession, baseRepo, headRepo *repo_model.Repository, headBranch, title string) string {
@@ -169,6 +196,9 @@ func testCreatePullToDefaultBranch(t *testing.T, session *TestSession, baseRepo,
}
func prepareRepoPR(t *testing.T, baseSession, headSession *TestSession, baseRepo, headRepo *repo_model.Repository) {
+ refSubURL := "branch/" + headRepo.DefaultBranch
+ testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, refSubURL, "new-commit", http.StatusSeeOther)
+
// create opening PR
testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "opening-pr", http.StatusSeeOther)
testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "opening-pr", "opening pr")
@@ -210,65 +240,19 @@ func checkRecentlyPushedNewBranches(t *testing.T, session *TestSession, repoPath
func TestRecentlyPushedNewBranches(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
- user1Session := loginUser(t, "user1")
- user2Session := loginUser(t, "user2")
user12Session := loginUser(t, "user12")
- user13Session := loginUser(t, "user13")
- // prepare branch and PRs in original repo
+ // Same reposioty check
repo10 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
- prepareBranch(t, user12Session, repo10)
prepareRepoPR(t, user12Session, user12Session, repo10, repo10)
-
- // outdated new branch should not be displayed
- checkRecentlyPushedNewBranches(t, user12Session, "user12/repo10", []string{"new-commit"})
+ prepareRecentlyPushedBranchTest(t, user12Session, repo10, repo10)
+ prepareRecentlyPushedBranchSpecialTest(t, user12Session, repo10, repo10)
// create a fork repo in public org
- testRepoFork(t, user12Session, repo10.OwnerName, repo10.Name, "org25", "org25_fork_repo10", "new-commit")
+ testRepoFork(t, user12Session, repo10.OwnerName, repo10.Name, "org25", "org25_fork_repo10", repo10.DefaultBranch)
orgPublicForkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: 25, Name: "org25_fork_repo10"})
prepareRepoPR(t, user12Session, user12Session, repo10, orgPublicForkRepo)
-
- // user12 is the owner of the repo10 and the organization org25
- // in repo10, user12 has opening/closed/merged pr and closed/merged pr with deleted branch
- checkRecentlyPushedNewBranches(t, user12Session, "user12/repo10", []string{"org25/org25_fork_repo10:new-commit", "new-commit"})
-
- userForkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 11})
- testCtx := NewAPITestContext(t, repo10.OwnerName, repo10.Name, auth_model.AccessTokenScopeWriteRepository)
- t.Run("AddUser13AsCollaborator", doAPIAddCollaborator(testCtx, "user13", perm.AccessModeWrite))
- prepareBranch(t, user13Session, userForkRepo)
- prepareRepoPR(t, user13Session, user13Session, repo10, userForkRepo)
-
- // create branch with same name in different repo by user13
- testCreateBranch(t, user13Session, repo10.OwnerName, repo10.Name, "branch/new-commit", "same-name-branch", http.StatusSeeOther)
- testCreateBranch(t, user13Session, userForkRepo.OwnerName, userForkRepo.Name, "branch/new-commit", "same-name-branch", http.StatusSeeOther)
- testCreatePullToDefaultBranch(t, user13Session, repo10, userForkRepo, "same-name-branch", "same name branch pr")
-
- // user13 pushed 2 branches with the same name in repo10 and repo11
- // and repo11's branch has a pr, but repo10's branch doesn't
- // in this case, we should get repo10's branch but not repo11's branch
- checkRecentlyPushedNewBranches(t, user13Session, "user12/repo10", []string{"same-name-branch", "user13/repo11:new-commit"})
-
- // create a fork repo in private org
- testRepoFork(t, user1Session, repo10.OwnerName, repo10.Name, "private_org35", "org35_fork_repo10", "new-commit")
- orgPrivateForkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: 35, Name: "org35_fork_repo10"})
- prepareRepoPR(t, user1Session, user1Session, repo10, orgPrivateForkRepo)
-
- // user1 is the owner of private_org35 and no write permission to repo10
- // so user1 can only see the branch in org35_fork_repo10
- checkRecentlyPushedNewBranches(t, user1Session, "user12/repo10", []string{"private_org35/org35_fork_repo10:new-commit"})
-
- // user2 push a branch in private_org35
- testCreateBranch(t, user2Session, orgPrivateForkRepo.OwnerName, orgPrivateForkRepo.Name, "branch/new-commit", "user-read-permission", http.StatusSeeOther)
- // convert write permission to read permission for code unit
- token := getTokenForLoggedInUser(t, user1Session, auth_model.AccessTokenScopeWriteOrganization)
- req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/teams/%d", 24), &api.EditTeamOption{
- Name: "team24",
- UnitsMap: map[string]string{"repo.code": "read"},
- }).AddTokenAuth(token)
- MakeRequest(t, req, http.StatusOK)
- teamUnit := unittest.AssertExistsAndLoadBean(t, &org_model.TeamUnit{TeamID: 24, Type: unit.TypeCode})
- assert.Equal(t, perm.AccessModeRead, teamUnit.AccessMode)
- // user2 can see the branch as it is created by user2
- checkRecentlyPushedNewBranches(t, user2Session, "user12/repo10", []string{"private_org35/org35_fork_repo10:user-read-permission"})
+ prepareRecentlyPushedBranchTest(t, user12Session, repo10, orgPublicForkRepo)
+ prepareRecentlyPushedBranchSpecialTest(t, user12Session, repo10, orgPublicForkRepo)
})
}
diff --git a/tests/integration/repo_commits_search_test.go b/tests/integration/repo_commits_search_test.go
index 74ac25c0f5..9b05e36399 100644
--- a/tests/integration/repo_commits_search_test.go
+++ b/tests/integration/repo_commits_search_test.go
@@ -23,7 +23,7 @@ func testRepoCommitsSearch(t *testing.T, query, commit string) {
doc := NewHTMLParser(t, resp.Body)
sel := doc.doc.Find("#commits-table tbody tr td.sha a")
- assert.EqualValues(t, commit, strings.TrimSpace(sel.Text()))
+ assert.Equal(t, commit, strings.TrimSpace(sel.Text()))
}
func TestRepoCommitsSearch(t *testing.T) {
diff --git a/tests/integration/repo_commits_test.go b/tests/integration/repo_commits_test.go
index fc066e06d3..0097a7f62e 100644
--- a/tests/integration/repo_commits_test.go
+++ b/tests/integration/repo_commits_test.go
@@ -12,11 +12,13 @@ import (
"testing"
auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/modules/commitstatus"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/tests"
+ "github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
)
@@ -35,6 +37,28 @@ func TestRepoCommits(t *testing.T) {
assert.NotEmpty(t, commitURL)
}
+func Test_ReposGitCommitListNotMaster(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ session := loginUser(t, "user2")
+ req := NewRequest(t, "GET", "/user2/repo16/commits/branch/master")
+ resp := session.MakeRequest(t, req, http.StatusOK)
+
+ doc := NewHTMLParser(t, resp.Body)
+ var commits []string
+ doc.doc.Find("#commits-table .commit-id-short").Each(func(i int, s *goquery.Selection) {
+ commitURL, _ := s.Attr("href")
+ commits = append(commits, path.Base(commitURL))
+ })
+ assert.Equal(t, []string{"69554a64c1e6030f051e5c3f94bfbd773cd6a324", "27566bd5738fc8b4e3fef3c5e72cce608537bd95", "5099b81332712fe655e34e8dd63574f503f61811"}, commits)
+
+ var userHrefs []string
+ doc.doc.Find("#commits-table .author-wrapper").Each(func(i int, s *goquery.Selection) {
+ userHref, _ := s.Attr("href")
+ userHrefs = append(userHrefs, userHref)
+ })
+ assert.Equal(t, []string{"/user2", "/user21", "/user2"}, userHrefs)
+}
+
func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
defer tests.PrepareTestEnv(t)()
@@ -53,7 +77,7 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
// Call API to add status for commit
ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository)
t.Run("CreateStatus", doAPICreateCommitStatus(ctx, path.Base(commitURL), api.CreateStatusOption{
- State: api.CommitStatusState(state),
+ State: commitstatus.CommitStatusState(state),
TargetURL: "http://test.ci/",
Description: "",
Context: "testci",
@@ -97,10 +121,10 @@ func testRepoCommitsWithStatus(t *testing.T, resp, respOne *httptest.ResponseRec
assert.NotNil(t, status)
if assert.Len(t, statuses, 1) {
- assert.Equal(t, api.CommitStatusState(state), statuses[0].State)
+ assert.Equal(t, commitstatus.CommitStatusState(state), statuses[0].State)
assert.Equal(t, setting.AppURL+"api/v1/repos/user2/repo1/statuses/65f1bf27bc3bf70f64657658635e66094edbcb4d", statuses[0].URL)
assert.Equal(t, "http://test.ci/", statuses[0].TargetURL)
- assert.Equal(t, "", statuses[0].Description)
+ assert.Empty(t, statuses[0].Description)
assert.Equal(t, "testci", statuses[0].Context)
assert.Len(t, status.Statuses, 1)
@@ -145,13 +169,13 @@ func TestRepoCommitsStatusParallel(t *testing.T) {
assert.NotEmpty(t, commitURL)
var wg sync.WaitGroup
- for i := 0; i < 10; i++ {
+ for i := range 10 {
wg.Add(1)
go func(parentT *testing.T, i int) {
parentT.Run(fmt.Sprintf("ParallelCreateStatus_%d", i), func(t *testing.T) {
ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository)
runBody := doAPICreateCommitStatus(ctx, path.Base(commitURL), api.CreateStatusOption{
- State: api.CommitStatusPending,
+ State: commitstatus.CommitStatusPending,
TargetURL: "http://test.ci/",
Description: "",
Context: "testci",
@@ -182,14 +206,14 @@ func TestRepoCommitsStatusMultiple(t *testing.T) {
// Call API to add status for commit
ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository)
t.Run("CreateStatus", doAPICreateCommitStatus(ctx, path.Base(commitURL), api.CreateStatusOption{
- State: api.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
TargetURL: "http://test.ci/",
Description: "",
Context: "testci",
}))
t.Run("CreateStatus", doAPICreateCommitStatus(ctx, path.Base(commitURL), api.CreateStatusOption{
- State: api.CommitStatusSuccess,
+ State: commitstatus.CommitStatusSuccess,
TargetURL: "http://test.ci/",
Description: "",
Context: "other_context",
@@ -199,7 +223,7 @@ func TestRepoCommitsStatusMultiple(t *testing.T) {
resp = session.MakeRequest(t, req, http.StatusOK)
doc = NewHTMLParser(t, resp.Body)
- // Check that the data-tippy="commit-statuses" (for trigger) and commit-status (svg) are present
- sel := doc.doc.Find("#commits-table .message [data-tippy=\"commit-statuses\"] .commit-status")
+ // Check that the data-global-init="initCommitStatuses" (for trigger) and commit-status (svg) are present
+ sel := doc.doc.Find(`#commits-table .message [data-global-init="initCommitStatuses"] .commit-status`)
assert.Equal(t, 1, sel.Length())
}
diff --git a/tests/integration/repo_fork_test.go b/tests/integration/repo_fork_test.go
index 267fd0d56e..95325eefeb 100644
--- a/tests/integration/repo_fork_test.go
+++ b/tests/integration/repo_fork_test.go
@@ -7,6 +7,7 @@ import (
"fmt"
"net/http"
"net/http/httptest"
+ "strconv"
"testing"
"code.gitea.io/gitea/models/db"
@@ -14,6 +15,7 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/test"
org_service "code.gitea.io/gitea/services/org"
"code.gitea.io/gitea/tests"
@@ -46,11 +48,12 @@ func testRepoFork(t *testing.T, session *TestSession, ownerName, repoName, forkO
assert.True(t, exists, "Fork owner '%s' is not present in select box", forkOwnerName)
req = NewRequestWithValues(t, "POST", link, map[string]string{
"_csrf": htmlDoc.GetCSRF(),
- "uid": fmt.Sprintf("%d", forkOwner.ID),
+ "uid": strconv.FormatInt(forkOwner.ID, 10),
"repo_name": forkRepoName,
"fork_single_branch": forkBranch,
})
- session.MakeRequest(t, req, http.StatusSeeOther)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ assert.Equal(t, fmt.Sprintf("/%s/%s", forkOwnerName, forkRepoName), test.RedirectURL(resp))
// Step4: check the existence of the forked repo
req = NewRequestf(t, "GET", "/%s/%s", forkOwnerName, forkRepoName)
@@ -81,14 +84,14 @@ func TestRepoForkToOrg(t *testing.T) {
func TestForkListLimitedAndPrivateRepos(t *testing.T) {
defer tests.PrepareTestEnv(t)()
- forkItemSelector := ".repo-fork-item"
+ forkItemSelector := ".fork-list .flex-item"
user1Sess := loginUser(t, "user1")
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user1"})
// fork to a limited org
limitedOrg := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 22})
- assert.EqualValues(t, structs.VisibleTypeLimited, limitedOrg.Visibility)
+ assert.Equal(t, structs.VisibleTypeLimited, limitedOrg.Visibility)
ownerTeam1, err := org_model.OrgFromUser(limitedOrg).GetOwnerTeam(db.DefaultContext)
assert.NoError(t, err)
assert.NoError(t, org_service.AddTeamMember(db.DefaultContext, ownerTeam1, user1))
@@ -98,7 +101,7 @@ func TestForkListLimitedAndPrivateRepos(t *testing.T) {
user4Sess := loginUser(t, "user4")
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user4"})
privateOrg := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 23})
- assert.EqualValues(t, structs.VisibleTypePrivate, privateOrg.Visibility)
+ assert.Equal(t, structs.VisibleTypePrivate, privateOrg.Visibility)
ownerTeam2, err := org_model.OrgFromUser(privateOrg).GetOwnerTeam(db.DefaultContext)
assert.NoError(t, err)
assert.NoError(t, org_service.AddTeamMember(db.DefaultContext, ownerTeam2, user4))
@@ -109,7 +112,7 @@ func TestForkListLimitedAndPrivateRepos(t *testing.T) {
req := NewRequest(t, "GET", "/user2/repo1/forks")
resp := MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
- assert.EqualValues(t, 0, htmlDoc.Find(forkItemSelector).Length())
+ assert.Equal(t, 0, htmlDoc.Find(forkItemSelector).Length())
})
t.Run("Logged in", func(t *testing.T) {
@@ -118,11 +121,12 @@ func TestForkListLimitedAndPrivateRepos(t *testing.T) {
req := NewRequest(t, "GET", "/user2/repo1/forks")
resp := user1Sess.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
- assert.EqualValues(t, 1, htmlDoc.Find(forkItemSelector).Length())
+ // since user1 is an admin, he can get both of the forked repositories
+ assert.Equal(t, 2, htmlDoc.Find(forkItemSelector).Length())
assert.NoError(t, org_service.AddTeamMember(db.DefaultContext, ownerTeam2, user1))
resp = user1Sess.MakeRequest(t, req, http.StatusOK)
htmlDoc = NewHTMLParser(t, resp.Body)
- assert.EqualValues(t, 2, htmlDoc.Find(forkItemSelector).Length())
+ assert.Equal(t, 2, htmlDoc.Find(forkItemSelector).Length())
})
}
diff --git a/tests/integration/repo_generate_test.go b/tests/integration/repo_generate_test.go
index ff2aa220d3..fca4e92982 100644
--- a/tests/integration/repo_generate_test.go
+++ b/tests/integration/repo_generate_test.go
@@ -7,6 +7,7 @@ import (
"fmt"
"net/http"
"net/http/httptest"
+ "strconv"
"strings"
"testing"
@@ -31,20 +32,20 @@ func testRepoGenerate(t *testing.T, session *TestSession, templateID, templateOw
// Step2: click the "Use this template" button
htmlDoc := NewHTMLParser(t, resp.Body)
- link, exists := htmlDoc.doc.Find("a.ui.button[href^=\"/repo/create\"]").Attr("href")
+ link, exists := htmlDoc.doc.Find(`a.ui.button[href^="/repo/create"]`).Attr("href")
assert.True(t, exists, "The template has changed")
req = NewRequest(t, "GET", link)
resp = session.MakeRequest(t, req, http.StatusOK)
- // Step3: fill the form of the create
+ // Step3: fill the form on the "create" page
htmlDoc = NewHTMLParser(t, resp.Body)
- link, exists = htmlDoc.doc.Find("form.ui.form[action^=\"/repo/create\"]").Attr("action")
+ link, exists = htmlDoc.doc.Find(`form.ui.form[action^="/repo/create"]`).Attr("action")
assert.True(t, exists, "The template has changed")
- _, exists = htmlDoc.doc.Find(fmt.Sprintf(".owner.dropdown .item[data-value=\"%d\"]", generateOwner.ID)).Attr("data-value")
+ _, exists = htmlDoc.doc.Find(fmt.Sprintf(`#repo_owner_dropdown .item[data-value="%d"]`, generateOwner.ID)).Attr("data-value")
assert.True(t, exists, "Generate owner '%s' is not present in select box", generateOwnerName)
req = NewRequestWithValues(t, "POST", link, map[string]string{
"_csrf": htmlDoc.GetCSRF(),
- "uid": fmt.Sprintf("%d", generateOwner.ID),
+ "uid": strconv.FormatInt(generateOwner.ID, 10),
"repo_name": generateRepoName,
"repo_template": templateID,
"git_content": "true",
diff --git a/tests/integration/repo_merge_upstream_test.go b/tests/integration/repo_merge_upstream_test.go
new file mode 100644
index 0000000000..d33d31c646
--- /dev/null
+++ b/tests/integration/repo_merge_upstream_test.go
@@ -0,0 +1,183 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "fmt"
+ "net/http"
+ "net/url"
+ "strings"
+ "testing"
+ "time"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/models/db"
+ git_model "code.gitea.io/gitea/models/git"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestRepoMergeUpstream(t *testing.T) {
+ onGiteaRun(t, func(*testing.T, *url.URL) {
+ forkUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
+
+ baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ baseUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: baseRepo.OwnerID})
+
+ checkFileContent := func(branch, exp string) {
+ req := NewRequest(t, "GET", fmt.Sprintf("/%s/test-repo-fork/raw/branch/%s/new-file.txt", forkUser.Name, branch))
+ resp := MakeRequest(t, req, http.StatusOK)
+ require.Equal(t, exp, resp.Body.String())
+ }
+
+ session := loginUser(t, forkUser.Name)
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
+
+ // create a fork
+ req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks", baseUser.Name, baseRepo.Name), &api.CreateForkOption{
+ Name: util.ToPointer("test-repo-fork"),
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusAccepted)
+ forkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: forkUser.ID, Name: "test-repo-fork"})
+
+ // create fork-branch
+ req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/test-repo-fork/branches/_new/branch/master", forkUser.Name), map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ "new_branch_name": "fork-branch",
+ })
+ session.MakeRequest(t, req, http.StatusSeeOther)
+
+ queryMergeUpstreamButtonLink := func(htmlDoc *HTMLDoc) string {
+ return htmlDoc.Find(`button[data-url*="merge-upstream"]`).AttrOr("data-url", "")
+ }
+
+ t.Run("HeadBeforeBase", func(t *testing.T) {
+ // add a file in base repo
+ sessionBaseUser := loginUser(t, baseUser.Name)
+ require.NoError(t, createOrReplaceFileInBranch(baseUser, baseRepo, "new-file.txt", "master", "test-content-1"))
+
+ var mergeUpstreamLink string
+ t.Run("DetectDefaultBranch", func(t *testing.T) {
+ // the repo shows a prompt to "sync fork" (defaults to the default branch)
+ require.Eventually(t, func() bool {
+ resp := session.MakeRequest(t, NewRequestf(t, "GET", "/%s/test-repo-fork/src/branch/fork-branch", forkUser.Name), http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ mergeUpstreamLink = queryMergeUpstreamButtonLink(htmlDoc)
+ if mergeUpstreamLink == "" {
+ return false
+ }
+ respMsg, _ := htmlDoc.Find(".ui.message:not(.positive)").Html()
+ return strings.Contains(respMsg, `This branch is 1 commit behind <a href="/user2/repo1/src/branch/master">user2/repo1:master</a>`)
+ }, 5*time.Second, 100*time.Millisecond)
+ })
+
+ t.Run("DetectSameBranch", func(t *testing.T) {
+ // if the fork-branch name also exists in the base repo, then use that branch instead
+ req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/_new/branch/master", map[string]string{
+ "_csrf": GetUserCSRFToken(t, sessionBaseUser),
+ "new_branch_name": "fork-branch",
+ })
+ sessionBaseUser.MakeRequest(t, req, http.StatusSeeOther)
+
+ require.Eventually(t, func() bool {
+ resp := session.MakeRequest(t, NewRequestf(t, "GET", "/%s/test-repo-fork/src/branch/fork-branch", forkUser.Name), http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ mergeUpstreamLink = queryMergeUpstreamButtonLink(htmlDoc)
+ if mergeUpstreamLink == "" {
+ return false
+ }
+ respMsg, _ := htmlDoc.Find(".ui.message:not(.positive)").Html()
+ return strings.Contains(respMsg, `This branch is 1 commit behind <a href="/user2/repo1/src/branch/fork-branch">user2/repo1:fork-branch</a>`)
+ }, 5*time.Second, 100*time.Millisecond)
+ })
+
+ // click the "sync fork" button
+ req = NewRequestWithValues(t, "POST", mergeUpstreamLink, map[string]string{"_csrf": GetUserCSRFToken(t, session)})
+ session.MakeRequest(t, req, http.StatusOK)
+ checkFileContent("fork-branch", "test-content-1")
+
+ // delete the "fork-branch" from the base repo
+ req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/delete?name=fork-branch", map[string]string{
+ "_csrf": GetUserCSRFToken(t, sessionBaseUser),
+ })
+ sessionBaseUser.MakeRequest(t, req, http.StatusOK)
+ })
+
+ t.Run("BaseChangeAfterHeadChange", func(t *testing.T) {
+ // update the files: base first, head later, and check the prompt
+ require.NoError(t, createOrReplaceFileInBranch(baseUser, baseRepo, "new-file.txt", "master", "test-content-2"))
+ require.NoError(t, createOrReplaceFileInBranch(forkUser, forkRepo, "new-file-other.txt", "fork-branch", "test-content-other"))
+
+ // make sure the base branch's update time is before the fork, to make it test the complete logic
+ baseBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: baseRepo.ID, Name: "master"})
+ forkBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: forkRepo.ID, Name: "fork-branch"})
+ _, err := db.GetEngine(db.DefaultContext).ID(forkBranch.ID).Update(&git_model.Branch{UpdatedUnix: baseBranch.UpdatedUnix + 1})
+ require.NoError(t, err)
+
+ // the repo shows a prompt to "sync fork"
+ require.Eventually(t, func() bool {
+ resp := session.MakeRequest(t, NewRequestf(t, "GET", "/%s/test-repo-fork/src/branch/fork-branch", forkUser.Name), http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ respMsg, _ := htmlDoc.Find(".ui.message:not(.positive)").Html()
+ return strings.Contains(respMsg, `The base branch <a href="/user2/repo1/src/branch/master">user2/repo1:master</a> has new changes`)
+ }, 5*time.Second, 100*time.Millisecond)
+
+ // and do the merge-upstream by API
+ req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/test-repo-fork/merge-upstream", forkUser.Name), &api.MergeUpstreamRequest{
+ Branch: "fork-branch",
+ }).AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+ checkFileContent("fork-branch", "test-content-2")
+
+ var mergeResp api.MergeUpstreamResponse
+ DecodeJSON(t, resp, &mergeResp)
+ assert.Equal(t, "merge", mergeResp.MergeStyle)
+
+ // after merge, there should be no "sync fork" button anymore
+ require.Eventually(t, func() bool {
+ resp := session.MakeRequest(t, NewRequestf(t, "GET", "/%s/test-repo-fork/src/branch/fork-branch", forkUser.Name), http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ return queryMergeUpstreamButtonLink(htmlDoc) == ""
+ }, 5*time.Second, 100*time.Millisecond)
+ })
+
+ t.Run("FastForwardOnly", func(t *testing.T) {
+ // Create a clean branch for fast-forward testing
+ req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/test-repo-fork/branches/_new/branch/master", forkUser.Name), map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ "new_branch_name": "ff-test-branch",
+ })
+ session.MakeRequest(t, req, http.StatusSeeOther)
+
+ // Add content to base repository that can be fast-forwarded
+ require.NoError(t, createOrReplaceFileInBranch(baseUser, baseRepo, "ff-test.txt", "master", "ff-content-1"))
+
+ // ff_only=true with fast-forward possible (should succeed)
+ req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/test-repo-fork/merge-upstream", forkUser.Name), &api.MergeUpstreamRequest{
+ Branch: "ff-test-branch",
+ FfOnly: true,
+ }).AddTokenAuth(token)
+ resp := MakeRequest(t, req, http.StatusOK)
+
+ var mergeResp api.MergeUpstreamResponse
+ DecodeJSON(t, resp, &mergeResp)
+ assert.Equal(t, "fast-forward", mergeResp.MergeStyle)
+
+ // ff_only=true when fast-forward is not possible (should fail)
+ require.NoError(t, createOrReplaceFileInBranch(baseUser, baseRepo, "another-file.txt", "master", "more-content"))
+
+ req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/test-repo-fork/merge-upstream", forkUser.Name), &api.MergeUpstreamRequest{
+ Branch: "fork-branch",
+ FfOnly: true,
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusBadRequest)
+ })
+ })
+}
diff --git a/tests/integration/repo_mergecommit_revert_test.go b/tests/integration/repo_mergecommit_revert_test.go
deleted file mode 100644
index 103fb47e2b..0000000000
--- a/tests/integration/repo_mergecommit_revert_test.go
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2024 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package integration
-
-import (
- "net/http"
- "testing"
-
- "code.gitea.io/gitea/tests"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestRepoMergeCommitRevert(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
- session := loginUser(t, "user2")
-
- req := NewRequest(t, "GET", "/user2/test_commit_revert/_cherrypick/deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7/main?ref=main&refType=branch&cherry-pick-type=revert")
- resp := session.MakeRequest(t, req, http.StatusOK)
-
- htmlDoc := NewHTMLParser(t, resp.Body)
- req = NewRequestWithValues(t, "POST", "/user2/test_commit_revert/_cherrypick/deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7/main", map[string]string{
- "_csrf": htmlDoc.GetCSRF(),
- "last_commit": "deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7",
- "page_has_posted": "true",
- "revert": "true",
- "commit_summary": "reverting test commit",
- "commit_message": "test message",
- "commit_choice": "direct",
- "new_branch_name": "test-revert-branch-1",
- })
- resp = session.MakeRequest(t, req, http.StatusSeeOther)
-
- // A successful revert redirects to the main branch
- assert.EqualValues(t, "/user2/test_commit_revert/src/branch/main", resp.Header().Get("Location"))
-}
diff --git a/tests/integration/repo_search_test.go b/tests/integration/repo_search_test.go
index 29d1517f4e..36a2e81f3b 100644
--- a/tests/integration/repo_search_test.go
+++ b/tests/integration/repo_search_test.go
@@ -57,5 +57,5 @@ func testSearch(t *testing.T, url string, expected []string) {
resp := MakeRequest(t, req, http.StatusOK)
filenames := resultFilenames(NewHTMLParser(t, resp.Body))
- assert.EqualValues(t, expected, filenames)
+ assert.Equal(t, expected, filenames)
}
diff --git a/tests/integration/repo_tag_test.go b/tests/integration/repo_tag_test.go
index 5638826ea0..8ea7508559 100644
--- a/tests/integration/repo_tag_test.go
+++ b/tests/integration/repo_tag_test.go
@@ -55,10 +55,10 @@ func TestCreateNewTagProtected(t *testing.T) {
doGitClone(dstPath, u)(t)
- _, _, err := git.NewCommand(git.DefaultContext, "tag", "v-2").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err := git.NewCommand("tag", "v-2").RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
- _, _, err = git.NewCommand(git.DefaultContext, "push", "--tags").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err = git.NewCommand("push", "--tags").RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.Error(t, err)
assert.Contains(t, err.Error(), "Tag v-2 is protected")
})
@@ -75,20 +75,20 @@ func TestCreateNewTagProtected(t *testing.T) {
doGitClone(dstPath, u)(t)
- _, _, err := git.NewCommand(git.DefaultContext, "tag", "v-1.1", "-m", "force update", "--force").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err := git.NewCommand("tag", "v-1.1", "-m", "force update", "--force").RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
require.NoError(t, err)
- _, _, err = git.NewCommand(git.DefaultContext, "push", "--tags").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err = git.NewCommand("push", "--tags").RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
require.NoError(t, err)
- _, _, err = git.NewCommand(git.DefaultContext, "tag", "v-1.1", "-m", "force update v2", "--force").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err = git.NewCommand("tag", "v-1.1", "-m", "force update v2", "--force").RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
require.NoError(t, err)
- _, _, err = git.NewCommand(git.DefaultContext, "push", "--tags").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err = git.NewCommand("push", "--tags").RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
require.Error(t, err)
assert.Contains(t, err.Error(), "the tag already exists in the remote")
- _, _, err = git.NewCommand(git.DefaultContext, "push", "--tags", "--force").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err = git.NewCommand("push", "--tags", "--force").RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
require.NoError(t, err)
req := NewRequestf(t, "GET", "/%s/releases/tag/v-1.1", repo.FullName())
resp := MakeRequest(t, req, http.StatusOK)
@@ -137,15 +137,15 @@ func TestRepushTag(t *testing.T) {
doGitClone(dstPath, u)(t)
// create and push a tag
- _, _, err := git.NewCommand(git.DefaultContext, "tag", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err := git.NewCommand("tag", "v2.0").RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
- _, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "--tags", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err = git.NewCommand("push", "origin", "--tags", "v2.0").RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
// create a release for the tag
createdRelease := createNewReleaseUsingAPI(t, token, owner, repo, "v2.0", "", "Release of v2.0", "desc")
assert.False(t, createdRelease.IsDraft)
// delete the tag
- _, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "--delete", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err = git.NewCommand("push", "origin", "--delete", "v2.0").RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
// query the release by API and it should be a draft
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner.Name, repo.Name, "v2.0"))
@@ -154,7 +154,7 @@ func TestRepushTag(t *testing.T) {
DecodeJSON(t, resp, &respRelease)
assert.True(t, respRelease.IsDraft)
// re-push the tag
- _, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "--tags", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath})
+ _, _, err = git.NewCommand("push", "origin", "--tags", "v2.0").RunStdString(git.DefaultContext, &git.RunOpts{Dir: dstPath})
assert.NoError(t, err)
// query the release by API and it should not be a draft
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner.Name, repo.Name, "v2.0"))
diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go
index 8c568a1272..adfe07519f 100644
--- a/tests/integration/repo_test.go
+++ b/tests/integration/repo_test.go
@@ -6,21 +6,51 @@ package integration
import (
"fmt"
"net/http"
+ "os"
"path"
+ "strconv"
"strings"
"testing"
"time"
+ "code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
+ "code.gitea.io/gitea/modules/util"
+ repo_service "code.gitea.io/gitea/services/repository"
"code.gitea.io/gitea/tests"
"github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
-func TestViewRepo(t *testing.T) {
+func TestRepoView(t *testing.T) {
defer tests.PrepareTestEnv(t)()
+ t.Run("ViewRepoPublic", testViewRepoPublic)
+ t.Run("ViewRepoWithCache", testViewRepoWithCache)
+ t.Run("ViewRepoPrivate", testViewRepoPrivate)
+ t.Run("ViewRepo1CloneLinkAnonymous", testViewRepo1CloneLinkAnonymous)
+ t.Run("ViewRepo1CloneLinkAuthorized", testViewRepo1CloneLinkAuthorized)
+ t.Run("ViewRepoWithSymlinks", testViewRepoWithSymlinks)
+ t.Run("ViewFileInRepo", testViewFileInRepo)
+ t.Run("BlameFileInRepo", testBlameFileInRepo)
+ t.Run("ViewRepoDirectory", testViewRepoDirectory)
+ t.Run("ViewRepoDirectoryReadme", testViewRepoDirectoryReadme)
+ t.Run("ViewRepoSymlink", testViewRepoSymlink)
+ t.Run("MarkDownReadmeImage", testMarkDownReadmeImage)
+ t.Run("MarkDownReadmeImageSubfolder", testMarkDownReadmeImageSubfolder)
+ t.Run("GeneratedSourceLink", testGeneratedSourceLink)
+ t.Run("ViewCommit", testViewCommit)
+}
+
+func testViewRepoPublic(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
session := loginUser(t, "user2")
@@ -41,87 +71,118 @@ func TestViewRepo(t *testing.T) {
session.MakeRequest(t, req, http.StatusNotFound)
}
-func testViewRepo(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
-
- req := NewRequest(t, "GET", "/org3/repo3")
- session := loginUser(t, "user2")
- resp := session.MakeRequest(t, req, http.StatusOK)
-
- htmlDoc := NewHTMLParser(t, resp.Body)
- files := htmlDoc.doc.Find("#repo-files-table .repo-file-item")
-
- type file struct {
- fileName string
- commitID string
- commitMsg string
- commitTime string
- }
+func testViewRepoWithCache(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ testView := func(t *testing.T) {
+ req := NewRequest(t, "GET", "/org3/repo3")
+ session := loginUser(t, "user2")
+ resp := session.MakeRequest(t, req, http.StatusOK)
- var items []file
-
- files.Each(func(i int, s *goquery.Selection) {
- tds := s.Find(".repo-file-cell")
- var f file
- tds.Each(func(i int, s *goquery.Selection) {
- if i == 0 {
- f.fileName = strings.TrimSpace(s.Text())
- } else if i == 1 {
- a := s.Find("a")
- f.commitMsg = strings.TrimSpace(a.Text())
- l, _ := a.Attr("href")
- f.commitID = path.Base(l)
- }
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ files := htmlDoc.doc.Find("#repo-files-table .repo-file-item")
+
+ type file struct {
+ fileName string
+ commitID string
+ commitMsg string
+ commitTime string
+ }
+
+ var items []file
+
+ files.Each(func(i int, s *goquery.Selection) {
+ tds := s.Find(".repo-file-cell")
+ var f file
+ tds.Each(func(i int, s *goquery.Selection) {
+ if i == 0 {
+ f.fileName = strings.TrimSpace(s.Text())
+ } else if i == 1 {
+ a := s.Find("a")
+ f.commitMsg = strings.TrimSpace(a.Text())
+ l, _ := a.Attr("href")
+ f.commitID = path.Base(l)
+ }
+ })
+
+ // convert "2017-06-14 21:54:21 +0800" to "Wed, 14 Jun 2017 13:54:21 UTC"
+ htmlTimeString, _ := s.Find("relative-time").Attr("datetime")
+ htmlTime, _ := time.Parse(time.RFC3339, htmlTimeString)
+ f.commitTime = htmlTime.In(time.Local).Format(time.RFC1123)
+ items = append(items, f)
})
- // convert "2017-06-14 21:54:21 +0800" to "Wed, 14 Jun 2017 13:54:21 UTC"
- htmlTimeString, _ := s.Find("relative-time").Attr("datetime")
- htmlTime, _ := time.Parse(time.RFC3339, htmlTimeString)
- f.commitTime = htmlTime.In(time.Local).Format(time.RFC1123)
- items = append(items, f)
- })
-
- commitT := time.Date(2017, time.June, 14, 13, 54, 21, 0, time.UTC).In(time.Local).Format(time.RFC1123)
- assert.EqualValues(t, []file{
- {
- fileName: "doc",
- commitID: "2a47ca4b614a9f5a43abbd5ad851a54a616ffee6",
- commitMsg: "init project",
- commitTime: commitT,
- },
- {
- fileName: "README.md",
- commitID: "2a47ca4b614a9f5a43abbd5ad851a54a616ffee6",
- commitMsg: "init project",
- commitTime: commitT,
- },
- }, items)
-}
+ commitT := time.Date(2017, time.June, 14, 13, 54, 21, 0, time.UTC).In(time.Local).Format(time.RFC1123)
+ assert.Equal(t, []file{
+ {
+ fileName: "doc",
+ commitID: "2a47ca4b614a9f5a43abbd5ad851a54a616ffee6",
+ commitMsg: "init project",
+ commitTime: commitT,
+ },
+ {
+ fileName: "README.md",
+ commitID: "2a47ca4b614a9f5a43abbd5ad851a54a616ffee6",
+ commitMsg: "init project",
+ commitTime: commitT,
+ },
+ }, items)
+ }
-func TestViewRepo2(t *testing.T) {
+ // FIXME: these test don't seem quite right, no enough assert
// no last commit cache
- testViewRepo(t)
-
+ testView(t)
// enable last commit cache for all repositories
oldCommitsCount := setting.CacheService.LastCommit.CommitsCount
setting.CacheService.LastCommit.CommitsCount = 0
// first view will not hit the cache
- testViewRepo(t)
+ testView(t)
// second view will hit the cache
- testViewRepo(t)
+ testView(t)
setting.CacheService.LastCommit.CommitsCount = oldCommitsCount
}
-func TestViewRepo3(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testViewRepoPrivate(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", "/org3/repo3")
- session := loginUser(t, "user4")
- session.MakeRequest(t, req, http.StatusOK)
+ MakeRequest(t, req, http.StatusNotFound)
+
+ t.Run("OrgMemberAccess", func(t *testing.T) {
+ req = NewRequest(t, "GET", "/org3/repo3")
+ session := loginUser(t, "user4")
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ assert.Contains(t, resp.Body.String(), `<div id="repo-files-table"`)
+ })
+
+ t.Run("PublicAccess-AnonymousAccess", func(t *testing.T) {
+ session := loginUser(t, "user1")
+
+ // set unit code to "anonymous read"
+ req = NewRequestWithValues(t, "POST", "/org3/repo3/settings/public_access", map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ "repo-unit-access-" + strconv.Itoa(int(unit.TypeCode)): "anonymous-read",
+ })
+ session.MakeRequest(t, req, http.StatusSeeOther)
+
+ // try to "anonymous read" (ok)
+ req = NewRequest(t, "GET", "/org3/repo3")
+ resp := MakeRequest(t, req, http.StatusOK)
+ assert.Contains(t, resp.Body.String(), `<span class="ui basic orange label">Public Access</span>`)
+
+ // remove "anonymous read"
+ req = NewRequestWithValues(t, "POST", "/org3/repo3/settings/public_access", map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ })
+ session.MakeRequest(t, req, http.StatusSeeOther)
+
+ // try to "anonymous read" (not found)
+ req = NewRequest(t, "GET", "/org3/repo3")
+ MakeRequest(t, req, http.StatusNotFound)
+ })
}
-func TestViewRepo1CloneLinkAnonymous(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testViewRepo1CloneLinkAnonymous(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", "/user2/repo1")
resp := MakeRequest(t, req, http.StatusOK)
@@ -130,12 +191,17 @@ func TestViewRepo1CloneLinkAnonymous(t *testing.T) {
link, exists := htmlDoc.doc.Find(".repo-clone-https").Attr("data-link")
assert.True(t, exists, "The template has changed")
assert.Equal(t, setting.AppURL+"user2/repo1.git", link)
+
_, exists = htmlDoc.doc.Find(".repo-clone-ssh").Attr("data-link")
assert.False(t, exists)
+
+ link, exists = htmlDoc.doc.Find(".repo-clone-tea").Attr("data-link")
+ assert.True(t, exists, "The template has changed")
+ assert.Equal(t, "tea clone user2/repo1", link)
}
-func TestViewRepo1CloneLinkAuthorized(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testViewRepo1CloneLinkAuthorized(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
session := loginUser(t, "user2")
@@ -146,15 +212,20 @@ func TestViewRepo1CloneLinkAuthorized(t *testing.T) {
link, exists := htmlDoc.doc.Find(".repo-clone-https").Attr("data-link")
assert.True(t, exists, "The template has changed")
assert.Equal(t, setting.AppURL+"user2/repo1.git", link)
+
link, exists = htmlDoc.doc.Find(".repo-clone-ssh").Attr("data-link")
assert.True(t, exists, "The template has changed")
sshURL := fmt.Sprintf("ssh://%s@%s:%d/user2/repo1.git", setting.SSH.User, setting.SSH.Domain, setting.SSH.Port)
assert.Equal(t, sshURL, link)
-}
-func TestViewRepoWithSymlinks(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+ link, exists = htmlDoc.doc.Find(".repo-clone-tea").Attr("data-link")
+ assert.True(t, exists, "The template has changed")
+ assert.Equal(t, "tea clone user2/repo1", link)
+}
+func testViewRepoWithSymlinks(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ defer test.MockVariableValue(&setting.UI.FileIconTheme, "basic")()
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user2/repo20.git")
@@ -176,8 +247,8 @@ func TestViewRepoWithSymlinks(t *testing.T) {
}
// TestViewFileInRepo repo description, topics and summary should not be displayed when viewing a file
-func TestViewFileInRepo(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testViewFileInRepo(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
session := loginUser(t, "user2")
@@ -189,14 +260,14 @@ func TestViewFileInRepo(t *testing.T) {
repoTopics := htmlDoc.doc.Find("#repo-topics")
repoSummary := htmlDoc.doc.Find(".repository-summary")
- assert.EqualValues(t, 0, description.Length())
- assert.EqualValues(t, 0, repoTopics.Length())
- assert.EqualValues(t, 0, repoSummary.Length())
+ assert.Equal(t, 0, description.Length())
+ assert.Equal(t, 0, repoTopics.Length())
+ assert.Equal(t, 0, repoSummary.Length())
}
// TestBlameFileInRepo repo description, topics and summary should not be displayed when running blame on a file
-func TestBlameFileInRepo(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testBlameFileInRepo(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
session := loginUser(t, "user2")
@@ -208,14 +279,14 @@ func TestBlameFileInRepo(t *testing.T) {
repoTopics := htmlDoc.doc.Find("#repo-topics")
repoSummary := htmlDoc.doc.Find(".repository-summary")
- assert.EqualValues(t, 0, description.Length())
- assert.EqualValues(t, 0, repoTopics.Length())
- assert.EqualValues(t, 0, repoSummary.Length())
+ assert.Equal(t, 0, description.Length())
+ assert.Equal(t, 0, repoTopics.Length())
+ assert.Equal(t, 0, repoSummary.Length())
}
// TestViewRepoDirectory repo description, topics and summary should not be displayed when within a directory
-func TestViewRepoDirectory(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testViewRepoDirectory(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
session := loginUser(t, "user2")
@@ -236,8 +307,8 @@ func TestViewRepoDirectory(t *testing.T) {
}
// ensure that the all the different ways to find and render a README work
-func TestViewRepoDirectoryReadme(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testViewRepoDirectoryReadme(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
// there are many combinations:
// - READMEs can be .md, .txt, or have no extension
@@ -343,8 +414,23 @@ func TestViewRepoDirectoryReadme(t *testing.T) {
missing("symlink-loop", "/user2/readme-test/src/branch/symlink-loop/")
}
-func TestMarkDownReadmeImage(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testViewRepoSymlink(t *testing.T) {
+ session := loginUser(t, "user2")
+ req := NewRequest(t, "GET", "/user2/readme-test/src/branch/symlink")
+ resp := session.MakeRequest(t, req, http.StatusOK)
+
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ AssertHTMLElement(t, htmlDoc, ".entry-symbol-link", true)
+ followSymbolLinkHref := htmlDoc.Find(".entry-symbol-link").AttrOr("href", "")
+ require.Equal(t, "/user2/readme-test/src/branch/symlink/README.md?follow_symlink=1", followSymbolLinkHref)
+
+ req = NewRequest(t, "GET", followSymbolLinkHref)
+ resp = session.MakeRequest(t, req, http.StatusSeeOther)
+ assert.Equal(t, "/user2/readme-test/src/branch/symlink/some/other/path/awefulcake.txt?follow_symlink=1", resp.Header().Get("Location"))
+}
+
+func testMarkDownReadmeImage(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
session := loginUser(t, "user2")
@@ -365,8 +451,8 @@ func TestMarkDownReadmeImage(t *testing.T) {
assert.Equal(t, "/user2/repo1/media/branch/home-md-img-check/test-fake-img.jpg", src)
}
-func TestMarkDownReadmeImageSubfolder(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testMarkDownReadmeImageSubfolder(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
session := loginUser(t, "user2")
@@ -388,8 +474,8 @@ func TestMarkDownReadmeImageSubfolder(t *testing.T) {
assert.Equal(t, "/user2/repo1/media/branch/sub-home-md-img-check/docs/test-fake-img.jpg", src)
}
-func TestGeneratedSourceLink(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testGeneratedSourceLink(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
t.Run("Rendered file", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
@@ -424,11 +510,54 @@ func TestGeneratedSourceLink(t *testing.T) {
})
}
-func TestViewCommit(t *testing.T) {
- defer tests.PrepareTestEnv(t)()
+func testViewCommit(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", "/user2/repo1/commit/0123456789012345678901234567890123456789")
req.Header.Add("Accept", "text/html")
resp := MakeRequest(t, req, http.StatusNotFound)
assert.True(t, test.IsNormalPageCompleted(resp.Body.String()), "non-existing commit should render 404 page")
}
+
+// TestGenerateRepository the test cannot succeed when moved as a unit test
+func TestGenerateRepository(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ // a successful generate from template
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo44 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 44})
+
+ generatedRepo, err := repo_service.GenerateRepository(git.DefaultContext, user2, user2, repo44, repo_service.GenerateRepoOptions{
+ Name: "generated-from-template-44",
+ GitContent: true,
+ })
+ assert.NoError(t, err)
+ assert.NotNil(t, generatedRepo)
+
+ exist, err := util.IsExist(repo_model.RepoPath(user2.Name, generatedRepo.Name))
+ assert.NoError(t, err)
+ assert.True(t, exist)
+
+ unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: generatedRepo.Name})
+
+ err = repo_service.DeleteRepositoryDirectly(db.DefaultContext, generatedRepo.ID)
+ assert.NoError(t, err)
+
+ // a failed creating because some mock data
+ // create the repository directory so that the creation will fail after database record created.
+ assert.NoError(t, os.MkdirAll(repo_model.RepoPath(user2.Name, "generated-from-template-44"), os.ModePerm))
+
+ generatedRepo2, err := repo_service.GenerateRepository(db.DefaultContext, user2, user2, repo44, repo_service.GenerateRepoOptions{
+ Name: "generated-from-template-44",
+ GitContent: true,
+ })
+ assert.Nil(t, generatedRepo2)
+ assert.Error(t, err)
+
+ // assert the cleanup is successful
+ unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: generatedRepo.Name})
+
+ exist, err = util.IsExist(repo_model.RepoPath(user2.Name, generatedRepo.Name))
+ assert.NoError(t, err)
+ assert.False(t, exist)
+}
diff --git a/tests/integration/repo_topic_test.go b/tests/integration/repo_topic_test.go
index f198397007..7f9594b9fd 100644
--- a/tests/integration/repo_topic_test.go
+++ b/tests/integration/repo_topic_test.go
@@ -25,7 +25,7 @@ func TestTopicSearch(t *testing.T) {
res := MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 6)
- assert.EqualValues(t, "6", res.Header().Get("x-total-count"))
+ assert.Equal(t, "6", res.Header().Get("x-total-count"))
// pagination search topics
topics.TopicNames = nil
@@ -35,7 +35,7 @@ func TestTopicSearch(t *testing.T) {
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 4)
- assert.EqualValues(t, "6", res.Header().Get("x-total-count"))
+ assert.Equal(t, "6", res.Header().Get("x-total-count"))
// second page
topics.TopicNames = nil
@@ -45,7 +45,7 @@ func TestTopicSearch(t *testing.T) {
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 2)
- assert.EqualValues(t, "6", res.Header().Get("x-total-count"))
+ assert.Equal(t, "6", res.Header().Get("x-total-count"))
// add keyword search
topics.TopicNames = nil
@@ -63,7 +63,7 @@ func TestTopicSearch(t *testing.T) {
DecodeJSON(t, res, &topics)
if assert.Len(t, topics.TopicNames, 1) {
assert.EqualValues(t, 2, topics.TopicNames[0].ID)
- assert.EqualValues(t, "database", topics.TopicNames[0].Name)
- assert.EqualValues(t, 1, topics.TopicNames[0].RepoCount)
+ assert.Equal(t, "database", topics.TopicNames[0].Name)
+ assert.Equal(t, 1, topics.TopicNames[0].RepoCount)
}
}
diff --git a/tests/integration/repo_webhook_test.go b/tests/integration/repo_webhook_test.go
index ef44a9e2d0..1da7bc9d3c 100644
--- a/tests/integration/repo_webhook_test.go
+++ b/tests/integration/repo_webhook_test.go
@@ -4,14 +4,31 @@
package integration
import (
+ "fmt"
+ "io"
"net/http"
+ "net/http/httptest"
+ "net/url"
+ "path"
"strings"
"testing"
+ auth_model "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/models/webhook"
+ "code.gitea.io/gitea/modules/commitstatus"
+ "code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/json"
+ api "code.gitea.io/gitea/modules/structs"
+ webhook_module "code.gitea.io/gitea/modules/webhook"
"code.gitea.io/gitea/tests"
+ runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
"github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestNewWebHookLink(t *testing.T) {
@@ -39,3 +56,1169 @@ func TestNewWebHookLink(t *testing.T) {
})
}
}
+
+func testAPICreateWebhookForRepo(t *testing.T, session *TestSession, userName, repoName, url, event string, branchFilter ...string) {
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll)
+ var branchFilterString string
+ if len(branchFilter) > 0 {
+ branchFilterString = branchFilter[0]
+ }
+ req := NewRequestWithJSON(t, "POST", "/api/v1/repos/"+userName+"/"+repoName+"/hooks", api.CreateHookOption{
+ Type: "gitea",
+ Config: api.CreateHookOptionConfig{
+ "content_type": "json",
+ "url": url,
+ },
+ Events: []string{event},
+ Active: true,
+ BranchFilter: branchFilterString,
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusCreated)
+}
+
+func testCreateWebhookForRepo(t *testing.T, session *TestSession, webhookType, userName, repoName, url, eventKind string) {
+ csrf := GetUserCSRFToken(t, session)
+ req := NewRequestWithValues(t, "POST", "/"+userName+"/"+repoName+"/settings/hooks/"+webhookType+"/new", map[string]string{
+ "_csrf": csrf,
+ "payload_url": url,
+ "events": eventKind,
+ "active": "true",
+ "content_type": fmt.Sprintf("%d", webhook.ContentTypeJSON),
+ "http_method": "POST",
+ })
+ session.MakeRequest(t, req, http.StatusSeeOther)
+}
+
+func testAPICreateWebhookForOrg(t *testing.T, session *TestSession, userName, url, event string) {
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll)
+ req := NewRequestWithJSON(t, "POST", "/api/v1/orgs/"+userName+"/hooks", api.CreateHookOption{
+ Type: "gitea",
+ Config: api.CreateHookOptionConfig{
+ "content_type": "json",
+ "url": url,
+ },
+ Events: []string{event},
+ Active: true,
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusCreated)
+}
+
+type mockWebhookProvider struct {
+ server *httptest.Server
+}
+
+func newMockWebhookProvider(callback func(r *http.Request), status int) *mockWebhookProvider {
+ m := &mockWebhookProvider{}
+ m.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ callback(r)
+ w.WriteHeader(status)
+ }))
+ return m
+}
+
+func (m *mockWebhookProvider) URL() string {
+ if m.server == nil {
+ return ""
+ }
+ return m.server.URL
+}
+
+// Close closes the mock webhook http server
+func (m *mockWebhookProvider) Close() {
+ if m.server != nil {
+ m.server.Close()
+ m.server = nil
+ }
+}
+
+func Test_WebhookCreate(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.CreatePayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.CreatePayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = string(webhook_module.HookEventCreate)
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "create")
+
+ // 2. trigger the webhook
+ testAPICreateBranch(t, session, "user2", "repo1", "master", "master2", http.StatusCreated)
+
+ // 3. validate the webhook is triggered
+ assert.Len(t, payloads, 1)
+ assert.Equal(t, string(webhook_module.HookEventCreate), triggeredEvent)
+ assert.Equal(t, "repo1", payloads[0].Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].Repo.FullName)
+ assert.Equal(t, "master2", payloads[0].Ref)
+ assert.Equal(t, "branch", payloads[0].RefType)
+ })
+}
+
+func Test_WebhookDelete(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.DeletePayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.DeletePayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "delete"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "delete")
+
+ // 2. trigger the webhook
+ testAPICreateBranch(t, session, "user2", "repo1", "master", "master2", http.StatusCreated)
+ testAPIDeleteBranch(t, "master2", http.StatusNoContent)
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "delete", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.Equal(t, "repo1", payloads[0].Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].Repo.FullName)
+ assert.Equal(t, "master2", payloads[0].Ref)
+ assert.Equal(t, "branch", payloads[0].RefType)
+ })
+}
+
+func Test_WebhookFork(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.ForkPayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.ForkPayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "fork"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user1")
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "fork")
+
+ // 2. trigger the webhook
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1-fork", "master")
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "fork", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.Equal(t, "repo1-fork", payloads[0].Repo.Name)
+ assert.Equal(t, "user1/repo1-fork", payloads[0].Repo.FullName)
+ assert.Equal(t, "repo1", payloads[0].Forkee.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].Forkee.FullName)
+ })
+}
+
+func Test_WebhookIssueComment(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.IssueCommentPayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.IssueCommentPayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "issue_comment"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "issue_comment")
+
+ t.Run("create comment", func(t *testing.T) {
+ // 2. trigger the webhook
+ issueURL := testNewIssue(t, session, "user2", "repo1", "Title2", "Description2")
+ testIssueAddComment(t, session, issueURL, "issue title2 comment1", "")
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "issue_comment", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.EqualValues(t, "created", payloads[0].Action)
+ assert.Equal(t, "repo1", payloads[0].Issue.Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
+ assert.Equal(t, "Title2", payloads[0].Issue.Title)
+ assert.Equal(t, "Description2", payloads[0].Issue.Body)
+ assert.Equal(t, "issue title2 comment1", payloads[0].Comment.Body)
+ })
+
+ t.Run("update comment", func(t *testing.T) {
+ payloads = make([]api.IssueCommentPayload, 0, 2)
+ triggeredEvent = ""
+
+ // 2. trigger the webhook
+ issueURL := testNewIssue(t, session, "user2", "repo1", "Title3", "Description3")
+ commentID := testIssueAddComment(t, session, issueURL, "issue title3 comment1", "")
+ modifiedContent := "issue title2 comment1 - modified"
+ req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d", "user2", "repo1", commentID), map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ "content": modifiedContent,
+ })
+ session.MakeRequest(t, req, http.StatusOK)
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "issue_comment", triggeredEvent)
+ assert.Len(t, payloads, 2)
+ assert.EqualValues(t, "edited", payloads[1].Action)
+ assert.Equal(t, "repo1", payloads[1].Issue.Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[1].Issue.Repo.FullName)
+ assert.Equal(t, "Title3", payloads[1].Issue.Title)
+ assert.Equal(t, "Description3", payloads[1].Issue.Body)
+ assert.Equal(t, modifiedContent, payloads[1].Comment.Body)
+ })
+
+ t.Run("Update comment with no content change", func(t *testing.T) {
+ payloads = make([]api.IssueCommentPayload, 0, 2)
+ triggeredEvent = ""
+ commentContent := "issue title3 comment1"
+
+ // 2. trigger the webhook
+ issueURL := testNewIssue(t, session, "user2", "repo1", "Title3", "Description3")
+ commentID := testIssueAddComment(t, session, issueURL, commentContent, "")
+
+ payloads = make([]api.IssueCommentPayload, 0, 2)
+ triggeredEvent = ""
+ req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d", "user2", "repo1", commentID), map[string]string{
+ "_csrf": GetUserCSRFToken(t, session),
+ "content": commentContent,
+ })
+ session.MakeRequest(t, req, http.StatusOK)
+
+ // 3. validate the webhook is not triggered because no content change
+ assert.Empty(t, triggeredEvent)
+ assert.Empty(t, payloads)
+ })
+ })
+}
+
+func Test_WebhookRelease(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.ReleasePayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.ReleasePayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "release"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "release")
+
+ // 2. trigger the webhook
+ createNewRelease(t, session, "/user2/repo1", "v0.0.99", "v0.0.99", false, false)
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "release", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.Equal(t, "repo1", payloads[0].Repository.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].Repository.FullName)
+ assert.Equal(t, "v0.0.99", payloads[0].Release.TagName)
+ assert.False(t, payloads[0].Release.IsDraft)
+ assert.False(t, payloads[0].Release.IsPrerelease)
+ })
+}
+
+func Test_WebhookPush(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.PushPayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.PushPayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "push"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "push")
+
+ // 2. trigger the webhook
+ testCreateFile(t, session, "user2", "repo1", "master", "test_webhook_push.md", "# a test file for webhook push")
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "push", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.Equal(t, "repo1", payloads[0].Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].Repo.FullName)
+ assert.Len(t, payloads[0].Commits, 1)
+ assert.Equal(t, []string{"test_webhook_push.md"}, payloads[0].Commits[0].Added)
+ })
+}
+
+func Test_WebhookPushDevBranch(t *testing.T) {
+ var payloads []api.PushPayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.PushPayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "push"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+
+ // only for dev branch
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "push", "develop")
+
+ // 2. this should not trigger the webhook
+ testCreateFile(t, session, "user2", "repo1", "master", "test_webhook_push.md", "# a test file for webhook push")
+ assert.Empty(t, triggeredEvent)
+ assert.Empty(t, payloads)
+
+ // 3. trigger the webhook
+ testCreateFile(t, session, "user2", "repo1", "develop", "test_webhook_push.md", "# a test file for webhook push")
+
+ // 4. validate the webhook is triggered
+ assert.Equal(t, "push", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.Equal(t, "repo1", payloads[0].Repo.Name)
+ assert.Equal(t, "develop", payloads[0].Branch())
+ assert.Equal(t, "user2/repo1", payloads[0].Repo.FullName)
+ assert.Len(t, payloads[0].Commits, 1)
+ assert.Equal(t, []string{"test_webhook_push.md"}, payloads[0].Commits[0].Added)
+ })
+}
+
+func Test_WebhookIssue(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.IssuePayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.IssuePayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "issues"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "issues")
+
+ // 2. trigger the webhook
+ testNewIssue(t, session, "user2", "repo1", "Title1", "Description1")
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "issues", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.EqualValues(t, "opened", payloads[0].Action)
+ assert.Equal(t, "repo1", payloads[0].Issue.Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
+ assert.Equal(t, "Title1", payloads[0].Issue.Title)
+ assert.Equal(t, "Description1", payloads[0].Issue.Body)
+ assert.Positive(t, payloads[0].Issue.Created.Unix())
+ assert.Positive(t, payloads[0].Issue.Updated.Unix())
+ })
+}
+
+func Test_WebhookIssueDelete(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.IssuePayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.IssuePayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "issue"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "issues")
+ issueURL := testNewIssue(t, session, "user2", "repo1", "Title1", "Description1")
+
+ // 2. trigger the webhook
+ testIssueDelete(t, session, issueURL)
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "issue", triggeredEvent)
+ require.Len(t, payloads, 2)
+ assert.EqualValues(t, "deleted", payloads[1].Action)
+ assert.Equal(t, "repo1", payloads[1].Issue.Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[1].Issue.Repo.FullName)
+ assert.Equal(t, "Title1", payloads[1].Issue.Title)
+ assert.Equal(t, "Description1", payloads[1].Issue.Body)
+ })
+}
+
+func Test_WebhookIssueAssign(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.PullRequestPayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.PullRequestPayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "pull_request_assign"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "pull_request_assign")
+
+ // 2. trigger the webhook, issue 2 is a pull request
+ testIssueAssign(t, session, repo1.Link(), 2, user2.ID)
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "pull_request_assign", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.EqualValues(t, "assigned", payloads[0].Action)
+ assert.Equal(t, "repo1", payloads[0].PullRequest.Base.Repository.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].PullRequest.Base.Repository.FullName)
+ assert.Equal(t, "issue2", payloads[0].PullRequest.Title)
+ assert.Equal(t, "content for the second issue", payloads[0].PullRequest.Body)
+ assert.Equal(t, user2.ID, payloads[0].PullRequest.Assignee.ID)
+ })
+}
+
+func Test_WebhookIssueMilestone(t *testing.T) {
+ var payloads []api.IssuePayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.IssuePayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "issues"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ // create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "issue_milestone")
+
+ t.Run("assign a milestone", func(t *testing.T) {
+ // trigger the webhook
+ testIssueChangeMilestone(t, session, repo1.Link(), 1, 1)
+
+ // validate the webhook is triggered
+ assert.Equal(t, "issues", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.Equal(t, "milestoned", string(payloads[0].Action))
+ assert.Equal(t, "repo1", payloads[0].Issue.Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
+ assert.Equal(t, "issue1", payloads[0].Issue.Title)
+ assert.Equal(t, "content for the first issue", payloads[0].Issue.Body)
+ assert.EqualValues(t, 1, payloads[0].Issue.Milestone.ID)
+ })
+
+ t.Run("change a milestong", func(t *testing.T) {
+ // trigger the webhook again
+ triggeredEvent = ""
+ payloads = make([]api.IssuePayload, 0, 1)
+ // change milestone to 2
+ testIssueChangeMilestone(t, session, repo1.Link(), 1, 2)
+
+ // validate the webhook is triggered
+ assert.Equal(t, "issues", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.Equal(t, "milestoned", string(payloads[0].Action))
+ assert.Equal(t, "repo1", payloads[0].Issue.Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
+ assert.Equal(t, "issue1", payloads[0].Issue.Title)
+ assert.Equal(t, "content for the first issue", payloads[0].Issue.Body)
+ assert.EqualValues(t, 2, payloads[0].Issue.Milestone.ID)
+ })
+
+ t.Run("remove a milestone", func(t *testing.T) {
+ // trigger the webhook again
+ triggeredEvent = ""
+ payloads = make([]api.IssuePayload, 0, 1)
+ // change milestone to 0
+ testIssueChangeMilestone(t, session, repo1.Link(), 1, 0)
+
+ // validate the webhook is triggered
+ assert.Equal(t, "issues", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.Equal(t, "demilestoned", string(payloads[0].Action))
+ assert.Equal(t, "repo1", payloads[0].Issue.Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
+ assert.Equal(t, "issue1", payloads[0].Issue.Title)
+ assert.Equal(t, "content for the first issue", payloads[0].Issue.Body)
+ assert.Nil(t, payloads[0].Issue.Milestone)
+ })
+ })
+}
+
+func Test_WebhookPullRequest(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.PullRequestPayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.PullRequestPayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "pull_request"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "pull_request")
+
+ testAPICreateBranch(t, session, "user2", "repo1", "master", "master2", http.StatusCreated)
+ // 2. trigger the webhook
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
+ testCreatePullToDefaultBranch(t, session, repo1, repo1, "master2", "first pull request")
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "pull_request", triggeredEvent)
+ require.Len(t, payloads, 1)
+ assert.Equal(t, "repo1", payloads[0].PullRequest.Base.Repository.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].PullRequest.Base.Repository.FullName)
+ assert.Equal(t, "repo1", payloads[0].PullRequest.Head.Repository.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].PullRequest.Head.Repository.FullName)
+ assert.Equal(t, 0, *payloads[0].PullRequest.Additions)
+ assert.Equal(t, 0, *payloads[0].PullRequest.ChangedFiles)
+ assert.Equal(t, 0, *payloads[0].PullRequest.Deletions)
+ })
+}
+
+func Test_WebhookPullRequestDelete(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.PullRequestPayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.PullRequestPayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "pull_request"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "pull_request")
+
+ testAPICreateBranch(t, session, "user2", "repo1", "master", "master2", http.StatusCreated)
+
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
+ issueURL := testCreatePullToDefaultBranch(t, session, repo1, repo1, "master2", "first pull request")
+
+ // 2. trigger the webhook
+ testIssueDelete(t, session, path.Join(repo1.Link(), "pulls", issueURL))
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "pull_request", triggeredEvent)
+ require.Len(t, payloads, 2)
+ assert.EqualValues(t, "deleted", payloads[1].Action)
+ assert.Equal(t, "repo1", payloads[1].PullRequest.Base.Repository.Name)
+ assert.Equal(t, "user2/repo1", payloads[1].PullRequest.Base.Repository.FullName)
+ assert.Equal(t, 0, *payloads[1].PullRequest.Additions)
+ assert.Equal(t, 0, *payloads[1].PullRequest.ChangedFiles)
+ assert.Equal(t, 0, *payloads[1].PullRequest.Deletions)
+ })
+}
+
+func Test_WebhookPullRequestComment(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.IssueCommentPayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.IssueCommentPayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "pull_request_comment"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "pull_request_comment")
+
+ // 2. trigger the webhook
+ testAPICreateBranch(t, session, "user2", "repo1", "master", "master2", http.StatusCreated)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
+ prID := testCreatePullToDefaultBranch(t, session, repo1, repo1, "master2", "first pull request")
+
+ testIssueAddComment(t, session, "/user2/repo1/pulls/"+prID, "pull title2 comment1", "")
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "pull_request_comment", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.EqualValues(t, "created", payloads[0].Action)
+ assert.Equal(t, "repo1", payloads[0].Issue.Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
+ assert.Equal(t, "first pull request", payloads[0].Issue.Title)
+ assert.Empty(t, payloads[0].Issue.Body)
+ assert.Equal(t, "pull title2 comment1", payloads[0].Comment.Body)
+ })
+}
+
+func Test_WebhookWiki(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.WikiPayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.WikiPayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "wiki"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "wiki")
+
+ // 2. trigger the webhook
+ testAPICreateWikiPage(t, session, "user2", "repo1", "Test Wiki Page", http.StatusCreated)
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "wiki", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.EqualValues(t, "created", payloads[0].Action)
+ assert.Equal(t, "repo1", payloads[0].Repository.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].Repository.FullName)
+ assert.Equal(t, "Test-Wiki-Page", payloads[0].Page)
+ })
+}
+
+func Test_WebhookRepository(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.RepositoryPayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.RepositoryPayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "repository"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user1")
+
+ testAPICreateWebhookForOrg(t, session, "org3", provider.URL(), "repository")
+
+ // 2. trigger the webhook
+ testAPIOrgCreateRepo(t, session, "org3", "repo_new", http.StatusCreated)
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "repository", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.EqualValues(t, "created", payloads[0].Action)
+ assert.Equal(t, "org3", payloads[0].Organization.UserName)
+ assert.Equal(t, "repo_new", payloads[0].Repository.Name)
+ assert.Equal(t, "org3/repo_new", payloads[0].Repository.FullName)
+ })
+}
+
+func Test_WebhookPackage(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.PackagePayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ content, _ := io.ReadAll(r.Body)
+ var payload api.PackagePayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "package"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user1")
+
+ testAPICreateWebhookForOrg(t, session, "org3", provider.URL(), "package")
+
+ // 2. trigger the webhook
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll)
+ url := fmt.Sprintf("/api/packages/%s/generic/%s/%s", "org3", "gitea", "v1.24.0")
+ req := NewRequestWithBody(t, "PUT", url+"/gitea", strings.NewReader("This is a dummy file")).
+ AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusCreated)
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "package", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.EqualValues(t, "created", payloads[0].Action)
+ assert.Equal(t, "gitea", payloads[0].Package.Name)
+ assert.Equal(t, "generic", payloads[0].Package.Type)
+ assert.Equal(t, "org3", payloads[0].Organization.UserName)
+ assert.Equal(t, "v1.24.0", payloads[0].Package.Version)
+ })
+}
+
+func Test_WebhookStatus(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.CommitStatusPayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ assert.Contains(t, r.Header["X-Github-Event-Type"], "status", "X-GitHub-Event-Type should contain status")
+ assert.Contains(t, r.Header["X-Github-Hook-Installation-Target-Type"], "repository", "X-GitHub-Hook-Installation-Target-Type should contain repository")
+ assert.Contains(t, r.Header["X-Gitea-Event-Type"], "status", "X-Gitea-Event-Type should contain status")
+ assert.Contains(t, r.Header["X-Gitea-Hook-Installation-Target-Type"], "repository", "X-Gitea-Hook-Installation-Target-Type should contain repository")
+ assert.Contains(t, r.Header["X-Gogs-Event-Type"], "status", "X-Gogs-Event-Type should contain status")
+ content, _ := io.ReadAll(r.Body)
+ var payload api.CommitStatusPayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "status"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "status")
+
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
+
+ gitRepo1, err := gitrepo.OpenRepository(t.Context(), repo1)
+ assert.NoError(t, err)
+ commitID, err := gitRepo1.GetBranchCommitID(repo1.DefaultBranch)
+ assert.NoError(t, err)
+
+ // 2. trigger the webhook
+ testCtx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeAll)
+
+ // update a status for a commit via API
+ doAPICreateCommitStatus(testCtx, commitID, api.CreateStatusOption{
+ State: commitstatus.CommitStatusSuccess,
+ TargetURL: "http://test.ci/",
+ Description: "",
+ Context: "testci",
+ })(t)
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "status", triggeredEvent)
+ assert.Len(t, payloads, 1)
+ assert.Equal(t, commitID, payloads[0].Commit.ID)
+ assert.Equal(t, "repo1", payloads[0].Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].Repo.FullName)
+ assert.Equal(t, "testci", payloads[0].Context)
+ assert.Equal(t, commitID, payloads[0].SHA)
+ })
+}
+
+func Test_WebhookStatus_NoWrongTrigger(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var trigger string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ assert.NotContains(t, r.Header["X-Github-Event-Type"], "status", "X-GitHub-Event-Type should not contain status")
+ assert.NotContains(t, r.Header["X-Gitea-Event-Type"], "status", "X-Gitea-Event-Type should not contain status")
+ assert.NotContains(t, r.Header["X-Gogs-Event-Type"], "status", "X-Gogs-Event-Type should not contain status")
+ trigger = "push"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ session := loginUser(t, "user2")
+
+ // create a push_only webhook from web UI
+ testCreateWebhookForRepo(t, session, "gitea", "user2", "repo1", provider.URL(), "push_only")
+
+ // 2. trigger the webhook with a push action
+ testCreateFile(t, session, "user2", "repo1", "master", "test_webhook_push.md", "# a test file for webhook push")
+
+ // 3. validate the webhook is triggered with right event
+ assert.Equal(t, "push", trigger)
+ })
+}
+
+func Test_WebhookWorkflowJob(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ var payloads []api.WorkflowJobPayload
+ var triggeredEvent string
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ assert.Contains(t, r.Header["X-Github-Event-Type"], "workflow_job", "X-GitHub-Event-Type should contain workflow_job")
+ assert.Contains(t, r.Header["X-Gitea-Event-Type"], "workflow_job", "X-Gitea-Event-Type should contain workflow_job")
+ assert.Contains(t, r.Header["X-Gogs-Event-Type"], "workflow_job", "X-Gogs-Event-Type should contain workflow_job")
+ content, _ := io.ReadAll(r.Body)
+ var payload api.WorkflowJobPayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ payloads = append(payloads, payload)
+ triggeredEvent = "workflow_job"
+ }, http.StatusOK)
+ defer provider.Close()
+
+ // 1. create a new webhook with special webhook for repo1
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ session := loginUser(t, "user2")
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "workflow_job")
+
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
+
+ gitRepo1, err := gitrepo.OpenRepository(t.Context(), repo1)
+ assert.NoError(t, err)
+
+ runner := newMockRunner()
+ runner.registerAsRepoRunner(t, "user2", "repo1", "mock-runner", []string{"ubuntu-latest"}, false)
+
+ // 2. trigger the webhooks
+
+ // add workflow file to the repo
+ // init the workflow
+ wfTreePath := ".gitea/workflows/push.yml"
+ wfFileContent := `name: Push
+on: push
+jobs:
+ wf1-job:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo 'test the webhook'
+ wf2-job:
+ runs-on: ubuntu-latest
+ needs: wf1-job
+ steps:
+ - run: echo 'cmd 1'
+ - run: echo 'cmd 2'
+`
+ opts := getWorkflowCreateFileOptions(user2, repo1.DefaultBranch, "create "+wfTreePath, wfFileContent)
+ createWorkflowFile(t, token, "user2", "repo1", wfTreePath, opts)
+
+ commitID, err := gitRepo1.GetBranchCommitID(repo1.DefaultBranch)
+ assert.NoError(t, err)
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "workflow_job", triggeredEvent)
+ assert.Len(t, payloads, 2)
+ assert.Equal(t, "queued", payloads[0].Action)
+ assert.Equal(t, "queued", payloads[0].WorkflowJob.Status)
+ assert.Equal(t, []string{"ubuntu-latest"}, payloads[0].WorkflowJob.Labels)
+ assert.Equal(t, commitID, payloads[0].WorkflowJob.HeadSha)
+ assert.Equal(t, "repo1", payloads[0].Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[0].Repo.FullName)
+
+ assert.Equal(t, "waiting", payloads[1].Action)
+ assert.Equal(t, "waiting", payloads[1].WorkflowJob.Status)
+ assert.Equal(t, commitID, payloads[1].WorkflowJob.HeadSha)
+ assert.Equal(t, "repo1", payloads[1].Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[1].Repo.FullName)
+
+ // 4. Execute a single Job
+ task := runner.fetchTask(t)
+ outcome := &mockTaskOutcome{
+ result: runnerv1.Result_RESULT_SUCCESS,
+ }
+ runner.execTask(t, task, outcome)
+
+ // 5. validate the webhook is triggered
+ assert.Equal(t, "workflow_job", triggeredEvent)
+ assert.Len(t, payloads, 5)
+ assert.Equal(t, "in_progress", payloads[2].Action)
+ assert.Equal(t, "in_progress", payloads[2].WorkflowJob.Status)
+ assert.Equal(t, "mock-runner", payloads[2].WorkflowJob.RunnerName)
+ assert.Equal(t, commitID, payloads[2].WorkflowJob.HeadSha)
+ assert.Equal(t, "repo1", payloads[2].Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[2].Repo.FullName)
+
+ assert.Equal(t, "completed", payloads[3].Action)
+ assert.Equal(t, "completed", payloads[3].WorkflowJob.Status)
+ assert.Equal(t, "mock-runner", payloads[3].WorkflowJob.RunnerName)
+ assert.Equal(t, "success", payloads[3].WorkflowJob.Conclusion)
+ assert.Equal(t, commitID, payloads[3].WorkflowJob.HeadSha)
+ assert.Equal(t, "repo1", payloads[3].Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[3].Repo.FullName)
+ assert.Contains(t, payloads[3].WorkflowJob.URL, fmt.Sprintf("/actions/jobs/%d", payloads[3].WorkflowJob.ID))
+ assert.Contains(t, payloads[3].WorkflowJob.HTMLURL, fmt.Sprintf("/jobs/%d", 0))
+ assert.Len(t, payloads[3].WorkflowJob.Steps, 1)
+
+ assert.Equal(t, "queued", payloads[4].Action)
+ assert.Equal(t, "queued", payloads[4].WorkflowJob.Status)
+ assert.Equal(t, []string{"ubuntu-latest"}, payloads[4].WorkflowJob.Labels)
+ assert.Equal(t, commitID, payloads[4].WorkflowJob.HeadSha)
+ assert.Equal(t, "repo1", payloads[4].Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[4].Repo.FullName)
+
+ // 6. Execute a single Job
+ task = runner.fetchTask(t)
+ outcome = &mockTaskOutcome{
+ result: runnerv1.Result_RESULT_FAILURE,
+ }
+ runner.execTask(t, task, outcome)
+
+ // 7. validate the webhook is triggered
+ assert.Equal(t, "workflow_job", triggeredEvent)
+ assert.Len(t, payloads, 7)
+ assert.Equal(t, "in_progress", payloads[5].Action)
+ assert.Equal(t, "in_progress", payloads[5].WorkflowJob.Status)
+ assert.Equal(t, "mock-runner", payloads[5].WorkflowJob.RunnerName)
+
+ assert.Equal(t, commitID, payloads[5].WorkflowJob.HeadSha)
+ assert.Equal(t, "repo1", payloads[5].Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[5].Repo.FullName)
+
+ assert.Equal(t, "completed", payloads[6].Action)
+ assert.Equal(t, "completed", payloads[6].WorkflowJob.Status)
+ assert.Equal(t, "failure", payloads[6].WorkflowJob.Conclusion)
+ assert.Equal(t, "mock-runner", payloads[6].WorkflowJob.RunnerName)
+ assert.Equal(t, commitID, payloads[6].WorkflowJob.HeadSha)
+ assert.Equal(t, "repo1", payloads[6].Repo.Name)
+ assert.Equal(t, "user2/repo1", payloads[6].Repo.FullName)
+ assert.Contains(t, payloads[6].WorkflowJob.URL, fmt.Sprintf("/actions/jobs/%d", payloads[6].WorkflowJob.ID))
+ assert.Contains(t, payloads[6].WorkflowJob.HTMLURL, fmt.Sprintf("/jobs/%d", 1))
+ assert.Len(t, payloads[6].WorkflowJob.Steps, 2)
+ })
+}
+
+type workflowRunWebhook struct {
+ URL string
+ payloads []api.WorkflowRunPayload
+ triggeredEvent string
+}
+
+func Test_WebhookWorkflowRun(t *testing.T) {
+ webhookData := &workflowRunWebhook{}
+ provider := newMockWebhookProvider(func(r *http.Request) {
+ assert.Contains(t, r.Header["X-Github-Event-Type"], "workflow_run", "X-GitHub-Event-Type should contain workflow_run")
+ assert.Contains(t, r.Header["X-Gitea-Event-Type"], "workflow_run", "X-Gitea-Event-Type should contain workflow_run")
+ assert.Contains(t, r.Header["X-Gogs-Event-Type"], "workflow_run", "X-Gogs-Event-Type should contain workflow_run")
+ content, _ := io.ReadAll(r.Body)
+ var payload api.WorkflowRunPayload
+ err := json.Unmarshal(content, &payload)
+ assert.NoError(t, err)
+ webhookData.payloads = append(webhookData.payloads, payload)
+ webhookData.triggeredEvent = "workflow_run"
+ }, http.StatusOK)
+ defer provider.Close()
+ webhookData.URL = provider.URL()
+
+ tests := []struct {
+ name string
+ callback func(t *testing.T, webhookData *workflowRunWebhook)
+ }{
+ {
+ name: "WorkflowRun",
+ callback: testWebhookWorkflowRun,
+ },
+ {
+ name: "WorkflowRunDepthLimit",
+ callback: testWebhookWorkflowRunDepthLimit,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ webhookData.payloads = nil
+ webhookData.triggeredEvent = ""
+ onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
+ test.callback(t, webhookData)
+ })
+ })
+ }
+}
+
+func testWebhookWorkflowRun(t *testing.T, webhookData *workflowRunWebhook) {
+ // 1. create a new webhook with special webhook for repo1
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ session := loginUser(t, "user2")
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", webhookData.URL, "workflow_run")
+
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
+
+ gitRepo1, err := gitrepo.OpenRepository(t.Context(), repo1)
+ assert.NoError(t, err)
+
+ runner := newMockRunner()
+ runner.registerAsRepoRunner(t, "user2", "repo1", "mock-runner", []string{"ubuntu-latest"}, false)
+
+ // 2.1 add workflow_run workflow file to the repo
+
+ opts := getWorkflowCreateFileOptions(user2, repo1.DefaultBranch, "create "+"dispatch.yml", `
+on:
+ workflow_run:
+ workflows: ["Push"]
+ types:
+ - completed
+jobs:
+ dispatch:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo 'test the webhook'
+`)
+ createWorkflowFile(t, token, "user2", "repo1", ".gitea/workflows/dispatch.yml", opts)
+
+ // 2.2 trigger the webhooks
+
+ // add workflow file to the repo
+ // init the workflow
+ wfTreePath := ".gitea/workflows/push.yml"
+ wfFileContent := `name: Push
+on: push
+jobs:
+ wf1-job:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo 'test the webhook'
+ wf2-job:
+ runs-on: ubuntu-latest
+ needs: wf1-job
+ steps:
+ - run: echo 'cmd 1'
+ - run: echo 'cmd 2'
+`
+ opts = getWorkflowCreateFileOptions(user2, repo1.DefaultBranch, "create "+wfTreePath, wfFileContent)
+ createWorkflowFile(t, token, "user2", "repo1", wfTreePath, opts)
+
+ commitID, err := gitRepo1.GetBranchCommitID(repo1.DefaultBranch)
+ assert.NoError(t, err)
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
+ assert.Len(t, webhookData.payloads, 1)
+ assert.Equal(t, "requested", webhookData.payloads[0].Action)
+ assert.Equal(t, "queued", webhookData.payloads[0].WorkflowRun.Status)
+ assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[0].WorkflowRun.HeadBranch)
+ assert.Equal(t, commitID, webhookData.payloads[0].WorkflowRun.HeadSha)
+ assert.Equal(t, "repo1", webhookData.payloads[0].Repo.Name)
+ assert.Equal(t, "user2/repo1", webhookData.payloads[0].Repo.FullName)
+
+ // 4. Execute two Jobs
+ task := runner.fetchTask(t)
+ outcome := &mockTaskOutcome{
+ result: runnerv1.Result_RESULT_SUCCESS,
+ }
+ runner.execTask(t, task, outcome)
+
+ task = runner.fetchTask(t)
+ outcome = &mockTaskOutcome{
+ result: runnerv1.Result_RESULT_FAILURE,
+ }
+ runner.execTask(t, task, outcome)
+
+ // 7. validate the webhook is triggered
+ assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
+ assert.Len(t, webhookData.payloads, 3)
+ assert.Equal(t, "completed", webhookData.payloads[1].Action)
+ assert.Equal(t, "push", webhookData.payloads[1].WorkflowRun.Event)
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
+ assert.Len(t, webhookData.payloads, 3)
+ assert.Equal(t, "requested", webhookData.payloads[2].Action)
+ assert.Equal(t, "queued", webhookData.payloads[2].WorkflowRun.Status)
+ assert.Equal(t, "workflow_run", webhookData.payloads[2].WorkflowRun.Event)
+ assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[2].WorkflowRun.HeadBranch)
+ assert.Equal(t, commitID, webhookData.payloads[2].WorkflowRun.HeadSha)
+ assert.Equal(t, "repo1", webhookData.payloads[2].Repo.Name)
+ assert.Equal(t, "user2/repo1", webhookData.payloads[2].Repo.FullName)
+}
+
+func testWebhookWorkflowRunDepthLimit(t *testing.T, webhookData *workflowRunWebhook) {
+ // 1. create a new webhook with special webhook for repo1
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ session := loginUser(t, "user2")
+ token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+
+ testAPICreateWebhookForRepo(t, session, "user2", "repo1", webhookData.URL, "workflow_run")
+
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
+
+ gitRepo1, err := gitrepo.OpenRepository(t.Context(), repo1)
+ assert.NoError(t, err)
+
+ // 2. trigger the webhooks
+
+ // add workflow file to the repo
+ // init the workflow
+ wfTreePath := ".gitea/workflows/push.yml"
+ wfFileContent := `name: Endless Loop
+on:
+ push:
+ workflow_run:
+ types:
+ - requested
+jobs:
+ dispatch:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo 'test the webhook'
+`
+ opts := getWorkflowCreateFileOptions(user2, repo1.DefaultBranch, "create "+wfTreePath, wfFileContent)
+ createWorkflowFile(t, token, "user2", "repo1", wfTreePath, opts)
+
+ commitID, err := gitRepo1.GetBranchCommitID(repo1.DefaultBranch)
+ assert.NoError(t, err)
+
+ // 3. validate the webhook is triggered
+ assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
+ // 1x push + 5x workflow_run requested chain
+ assert.Len(t, webhookData.payloads, 6)
+ for i := range 6 {
+ assert.Equal(t, "requested", webhookData.payloads[i].Action)
+ assert.Equal(t, "queued", webhookData.payloads[i].WorkflowRun.Status)
+ assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[i].WorkflowRun.HeadBranch)
+ assert.Equal(t, commitID, webhookData.payloads[i].WorkflowRun.HeadSha)
+ if i == 0 {
+ assert.Equal(t, "push", webhookData.payloads[i].WorkflowRun.Event)
+ } else {
+ assert.Equal(t, "workflow_run", webhookData.payloads[i].WorkflowRun.Event)
+ }
+ assert.Equal(t, "repo1", webhookData.payloads[i].Repo.Name)
+ assert.Equal(t, "user2/repo1", webhookData.payloads[i].Repo.FullName)
+ }
+}
diff --git a/tests/integration/repofiles_change_test.go b/tests/integration/repofiles_change_test.go
index d86dcc01fe..dc389f5680 100644
--- a/tests/integration/repofiles_change_test.go
+++ b/tests/integration/repofiles_change_test.go
@@ -4,8 +4,9 @@
package integration
import (
+ "fmt"
"net/url"
- "path/filepath"
+ "path"
"strings"
"testing"
"time"
@@ -16,6 +17,7 @@ import (
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/contexttest"
files_service "code.gitea.io/gitea/services/repository/files"
@@ -57,6 +59,40 @@ func getUpdateRepoFilesOptions(repo *repo_model.Repository) *files_service.Chang
}
}
+func getUpdateRepoFilesRenameOptions(repo *repo_model.Repository) *files_service.ChangeRepoFilesOptions {
+ return &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ // move normally
+ {
+ Operation: "rename",
+ FromTreePath: "README.md",
+ TreePath: "README.txt",
+ },
+ // move from in lfs
+ {
+ Operation: "rename",
+ FromTreePath: "crypt.bin",
+ TreePath: "crypt1.bin",
+ },
+ // move from lfs to normal
+ {
+ Operation: "rename",
+ FromTreePath: "jpeg.jpg",
+ TreePath: "jpeg.jpeg",
+ },
+ // move from normal to lfs
+ {
+ Operation: "rename",
+ FromTreePath: "CONTRIBUTING.md",
+ TreePath: "CONTRIBUTING.md.bin",
+ },
+ },
+ OldBranch: repo.DefaultBranch,
+ NewBranch: repo.DefaultBranch,
+ Message: "Rename files",
+ }
+}
+
func getDeleteRepoFilesOptions(repo *repo_model.Repository) *files_service.ChangeRepoFilesOptions {
return &files_service.ChangeRepoFilesOptions{
Files: []*files_service.ChangeRepoFile{
@@ -71,14 +107,14 @@ func getDeleteRepoFilesOptions(repo *repo_model.Repository) *files_service.Chang
NewBranch: repo.DefaultBranch,
Message: "Deletes README.md",
Author: &files_service.IdentityOptions{
- Name: "Bob Smith",
- Email: "bob@smith.com",
+ GitUserName: "Bob Smith",
+ GitUserEmail: "bob@smith.com",
},
Committer: nil,
}
}
-func getExpectedFileResponseForRepofilesDelete() *api.FileResponse {
+func getExpectedFileResponseForRepoFilesDelete() *api.FileResponse {
// Just returns fields that don't change, i.e. fields with commit SHAs and dates can't be determined
return &api.FileResponse{
Content: nil,
@@ -106,7 +142,7 @@ func getExpectedFileResponseForRepofilesDelete() *api.FileResponse {
}
}
-func getExpectedFileResponseForRepofilesCreate(commitID, lastCommitSHA string) *api.FileResponse {
+func getExpectedFileResponseForRepoFilesCreate(commitID string, lastCommit *git.Commit) *api.FileResponse {
treePath := "new/file.txt"
encoding := "base64"
content := "VGhpcyBpcyBhIE5FVyBmaWxl"
@@ -116,18 +152,20 @@ func getExpectedFileResponseForRepofilesCreate(commitID, lastCommitSHA string) *
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath
return &api.FileResponse{
Content: &api.ContentsResponse{
- Name: filepath.Base(treePath),
- Path: treePath,
- SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
- LastCommitSHA: lastCommitSHA,
- Type: "file",
- Size: 18,
- Encoding: &encoding,
- Content: &content,
- URL: &selfURL,
- HTMLURL: &htmlURL,
- GitURL: &gitURL,
- DownloadURL: &downloadURL,
+ Name: path.Base(treePath),
+ Path: treePath,
+ SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
+ LastCommitSHA: util.ToPointer(lastCommit.ID.String()),
+ LastCommitterDate: util.ToPointer(lastCommit.Committer.When),
+ LastAuthorDate: util.ToPointer(lastCommit.Author.When),
+ Type: "file",
+ Size: 18,
+ Encoding: &encoding,
+ Content: &content,
+ URL: &selfURL,
+ HTMLURL: &htmlURL,
+ GitURL: &gitURL,
+ DownloadURL: &downloadURL,
Links: &api.FileLinksResponse{
Self: &selfURL,
GitURL: &gitURL,
@@ -160,7 +198,7 @@ func getExpectedFileResponseForRepofilesCreate(commitID, lastCommitSHA string) *
SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
},
},
- Message: "Updates README.md\n",
+ Message: "Creates new/file.txt\n",
Tree: &api.CommitMeta{
URL: setting.AppURL + "api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
SHA: "f93e3a1a1525fb5b91020git dda86e44810c87a2d7bc",
@@ -175,7 +213,7 @@ func getExpectedFileResponseForRepofilesCreate(commitID, lastCommitSHA string) *
}
}
-func getExpectedFileResponseForRepofilesUpdate(commitID, filename, lastCommitSHA string) *api.FileResponse {
+func getExpectedFileResponseForRepoFilesUpdate(commitID, filename, lastCommitSHA string, lastCommitterWhen, lastAuthorWhen time.Time) *api.FileResponse {
encoding := "base64"
content := "VGhpcyBpcyBVUERBVEVEIGNvbnRlbnQgZm9yIHRoZSBSRUFETUUgZmlsZQ=="
selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + filename + "?ref=master"
@@ -184,18 +222,20 @@ func getExpectedFileResponseForRepofilesUpdate(commitID, filename, lastCommitSHA
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + filename
return &api.FileResponse{
Content: &api.ContentsResponse{
- Name: filename,
- Path: filename,
- SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647",
- LastCommitSHA: lastCommitSHA,
- Type: "file",
- Size: 43,
- Encoding: &encoding,
- Content: &content,
- URL: &selfURL,
- HTMLURL: &htmlURL,
- GitURL: &gitURL,
- DownloadURL: &downloadURL,
+ Name: filename,
+ Path: filename,
+ SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647",
+ LastCommitSHA: util.ToPointer(lastCommitSHA),
+ LastCommitterDate: util.ToPointer(lastCommitterWhen),
+ LastAuthorDate: util.ToPointer(lastAuthorWhen),
+ Type: "file",
+ Size: 43,
+ Encoding: &encoding,
+ Content: &content,
+ URL: &selfURL,
+ HTMLURL: &htmlURL,
+ GitURL: &gitURL,
+ DownloadURL: &downloadURL,
Links: &api.FileLinksResponse{
Self: &selfURL,
GitURL: &gitURL,
@@ -243,6 +283,114 @@ func getExpectedFileResponseForRepofilesUpdate(commitID, filename, lastCommitSHA
}
}
+func getExpectedFileResponseForRepoFilesUpdateRename(commitID, lastCommitSHA string) *api.FilesResponse {
+ details := []struct {
+ filename, sha, content string
+ size int64
+ lfsOid *string
+ lfsSize *int64
+ }{
+ {
+ filename: "README.txt",
+ sha: "8276d2a29779af982c0afa976bdb793b52d442a8",
+ size: 22,
+ content: "IyBBbiBMRlMtZW5hYmxlZCByZXBvCg==",
+ },
+ {
+ filename: "crypt1.bin",
+ sha: "d4a41a0d4db4949e129bd22f871171ea988103ef",
+ size: 129,
+ content: "dmVyc2lvbiBodHRwczovL2dpdC1sZnMuZ2l0aHViLmNvbS9zcGVjL3YxCm9pZCBzaGEyNTY6MmVjY2RiNDM4MjVkMmE0OWQ5OWQ1NDJkYWEyMDA3NWNmZjFkOTdkOWQyMzQ5YTg5NzdlZmU5YzAzNjYxNzM3YwpzaXplIDIwNDgK",
+ lfsOid: util.ToPointer("2eccdb43825d2a49d99d542daa20075cff1d97d9d2349a8977efe9c03661737c"),
+ lfsSize: util.ToPointer(int64(2048)),
+ },
+ {
+ filename: "jpeg.jpeg",
+ sha: "71911bf48766c7181518c1070911019fbb00b1fc",
+ size: 107,
+ content: "/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
+ },
+ {
+ filename: "CONTRIBUTING.md.bin",
+ sha: "2b6c6c4eaefa24b22f2092c3d54b263ff26feb58",
+ size: 127,
+ content: "dmVyc2lvbiBodHRwczovL2dpdC1sZnMuZ2l0aHViLmNvbS9zcGVjL3YxCm9pZCBzaGEyNTY6N2I2YjJjODhkYmE5Zjc2MGExYTU4NDY5YjY3ZmVlMmI2OThlZjdlOTM5OWM0Y2E0ZjM0YTE0Y2NiZTM5ZjYyMwpzaXplIDI3Cg==",
+ lfsOid: util.ToPointer("7b6b2c88dba9f760a1a58469b67fee2b698ef7e9399c4ca4f34a14ccbe39f623"),
+ lfsSize: util.ToPointer(int64(27)),
+ },
+ }
+
+ var responses []*api.ContentsResponse
+ for _, detail := range details {
+ selfURL := setting.AppURL + "api/v1/repos/user2/lfs/contents/" + detail.filename + "?ref=master"
+ htmlURL := setting.AppURL + "user2/lfs/src/branch/master/" + detail.filename
+ gitURL := setting.AppURL + "api/v1/repos/user2/lfs/git/blobs/" + detail.sha
+ downloadURL := setting.AppURL + "user2/lfs/raw/branch/master/" + detail.filename
+ // don't set time related fields because there might be different time in one operation
+ responses = append(responses, &api.ContentsResponse{
+ Name: detail.filename,
+ Path: detail.filename,
+ SHA: detail.sha,
+ LastCommitSHA: util.ToPointer(lastCommitSHA),
+ Type: "file",
+ Size: detail.size,
+ Encoding: util.ToPointer("base64"),
+ Content: &detail.content,
+ URL: &selfURL,
+ HTMLURL: &htmlURL,
+ GitURL: &gitURL,
+ DownloadURL: &downloadURL,
+ Links: &api.FileLinksResponse{
+ Self: &selfURL,
+ GitURL: &gitURL,
+ HTMLURL: &htmlURL,
+ },
+ LfsOid: detail.lfsOid,
+ LfsSize: detail.lfsSize,
+ })
+ }
+
+ return &api.FilesResponse{
+ Files: responses,
+ Commit: &api.FileCommitResponse{
+ CommitMeta: api.CommitMeta{
+ URL: setting.AppURL + "api/v1/repos/user2/lfs/git/commits/" + commitID,
+ SHA: commitID,
+ },
+ HTMLURL: setting.AppURL + "user2/lfs/commit/" + commitID,
+ Author: &api.CommitUser{
+ Identity: api.Identity{
+ Name: "User Two",
+ Email: "user2@noreply.example.org",
+ },
+ },
+ Committer: &api.CommitUser{
+ Identity: api.Identity{
+ Name: "User Two",
+ Email: "user2@noreply.example.org",
+ },
+ },
+ Parents: []*api.CommitMeta{
+ {
+ URL: setting.AppURL + "api/v1/repos/user2/lfs/git/commits/73cf03db6ece34e12bf91e8853dc58f678f2f82d",
+ SHA: "73cf03db6ece34e12bf91e8853dc58f678f2f82d",
+ },
+ },
+ Message: "Rename files\n",
+ Tree: &api.CommitMeta{
+ URL: setting.AppURL + "api/v1/repos/user2/lfs/git/trees/5307376dc3a5557dc1c403c29a8984668ca9ecb5",
+ SHA: "5307376dc3a5557dc1c403c29a8984668ca9ecb5",
+ },
+ },
+ Verification: &api.PayloadCommitVerification{
+ Verified: false,
+ Reason: "gpg.error.not_signed_commit",
+ Signature: "",
+ Payload: "",
+ },
+ }
+}
+
func TestChangeRepoFilesForCreate(t *testing.T) {
// setup
onGiteaRun(t, func(t *testing.T, u *url.URL) {
@@ -268,14 +416,14 @@ func TestChangeRepoFilesForCreate(t *testing.T) {
commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch)
lastCommit, _ := gitRepo.GetCommitByPath("new/file.txt")
- expectedFileResponse := getExpectedFileResponseForRepofilesCreate(commitID, lastCommit.ID.String())
+ expectedFileResponse := getExpectedFileResponseForRepoFilesCreate(commitID, lastCommit)
assert.NotNil(t, expectedFileResponse)
if expectedFileResponse != nil {
- assert.EqualValues(t, expectedFileResponse.Content, filesResponse.Files[0])
- assert.EqualValues(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA)
- assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
- assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email)
- assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name)
+ assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0])
+ assert.Equal(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA)
+ assert.Equal(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
+ assert.Equal(t, expectedFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email)
+ assert.Equal(t, expectedFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name)
}
})
}
@@ -305,12 +453,12 @@ func TestChangeRepoFilesForUpdate(t *testing.T) {
commit, _ := gitRepo.GetBranchCommit(opts.NewBranch)
lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath)
- expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String())
- assert.EqualValues(t, expectedFileResponse.Content, filesResponse.Files[0])
- assert.EqualValues(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA)
- assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
- assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email)
- assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name)
+ expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When)
+ assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0])
+ assert.Equal(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA)
+ assert.Equal(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
+ assert.Equal(t, expectedFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email)
+ assert.Equal(t, expectedFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name)
})
}
@@ -341,7 +489,7 @@ func TestChangeRepoFilesForUpdateWithFileMove(t *testing.T) {
commit, _ := gitRepo.GetBranchCommit(opts.NewBranch)
lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath)
- expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String())
+ expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When)
// assert that the old file no longer exists in the last commit of the branch
fromEntry, err := commit.GetTreeEntryByPath(opts.Files[0].FromTreePath)
switch err.(type) {
@@ -355,12 +503,44 @@ func TestChangeRepoFilesForUpdateWithFileMove(t *testing.T) {
assert.Nil(t, fromEntry) // Should no longer exist here
assert.NotNil(t, toEntry) // Should exist here
// assert SHA has remained the same but paths use the new file name
- assert.EqualValues(t, expectedFileResponse.Content.SHA, filesResponse.Files[0].SHA)
- assert.EqualValues(t, expectedFileResponse.Content.Name, filesResponse.Files[0].Name)
- assert.EqualValues(t, expectedFileResponse.Content.Path, filesResponse.Files[0].Path)
- assert.EqualValues(t, expectedFileResponse.Content.URL, filesResponse.Files[0].URL)
- assert.EqualValues(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA)
- assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
+ assert.Equal(t, expectedFileResponse.Content.SHA, filesResponse.Files[0].SHA)
+ assert.Equal(t, expectedFileResponse.Content.Name, filesResponse.Files[0].Name)
+ assert.Equal(t, expectedFileResponse.Content.Path, filesResponse.Files[0].Path)
+ assert.Equal(t, expectedFileResponse.Content.URL, filesResponse.Files[0].URL)
+ assert.Equal(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA)
+ assert.Equal(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
+ })
+}
+
+func TestChangeRepoFilesForUpdateWithFileRename(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ ctx, _ := contexttest.MockContext(t, "user2/lfs")
+ ctx.SetPathParam("id", "54")
+ contexttest.LoadRepo(t, ctx, 54)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+ contexttest.LoadGitRepo(t, ctx)
+ defer ctx.Repo.GitRepo.Close()
+
+ repo := ctx.Repo.Repository
+ opts := getUpdateRepoFilesRenameOptions(repo)
+
+ // test
+ filesResponse, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, ctx.Doer, opts)
+
+ // asserts
+ assert.NoError(t, err)
+ gitRepo, _ := gitrepo.OpenRepository(git.DefaultContext, repo)
+ defer gitRepo.Close()
+
+ commit, _ := gitRepo.GetBranchCommit(repo.DefaultBranch)
+ lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath)
+ expectedFileResponse := getExpectedFileResponseForRepoFilesUpdateRename(commit.ID.String(), lastCommit.ID.String())
+ for _, file := range filesResponse.Files {
+ file.LastCommitterDate, file.LastAuthorDate = nil, nil // there might be different time in one operation, so we ignore them
+ }
+ assert.Len(t, filesResponse.Files, 4)
+ assert.Equal(t, expectedFileResponse.Files, filesResponse.Files)
})
}
@@ -392,8 +572,8 @@ func TestChangeRepoFilesWithoutBranchNames(t *testing.T) {
commit, _ := gitRepo.GetBranchCommit(repo.DefaultBranch)
lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath)
- expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String())
- assert.EqualValues(t, expectedFileResponse.Content, filesResponse.Files[0])
+ expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When)
+ assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0])
})
}
@@ -418,13 +598,13 @@ func testDeleteRepoFiles(t *testing.T, u *url.URL) {
t.Run("Delete README.md file", func(t *testing.T) {
filesResponse, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, doer, opts)
assert.NoError(t, err)
- expectedFileResponse := getExpectedFileResponseForRepofilesDelete()
+ expectedFileResponse := getExpectedFileResponseForRepoFilesDelete()
assert.NotNil(t, filesResponse)
assert.Nil(t, filesResponse.Files[0])
- assert.EqualValues(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message)
- assert.EqualValues(t, expectedFileResponse.Commit.Author.Identity, filesResponse.Commit.Author.Identity)
- assert.EqualValues(t, expectedFileResponse.Commit.Committer.Identity, filesResponse.Commit.Committer.Identity)
- assert.EqualValues(t, expectedFileResponse.Verification, filesResponse.Verification)
+ assert.Equal(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message)
+ assert.Equal(t, expectedFileResponse.Commit.Author.Identity, filesResponse.Commit.Author.Identity)
+ assert.Equal(t, expectedFileResponse.Commit.Committer.Identity, filesResponse.Commit.Committer.Identity)
+ assert.Equal(t, expectedFileResponse.Verification, filesResponse.Verification)
})
t.Run("Verify README.md has been deleted", func(t *testing.T) {
@@ -460,13 +640,13 @@ func testDeleteRepoFilesWithoutBranchNames(t *testing.T, u *url.URL) {
t.Run("Delete README.md without Branch Name", func(t *testing.T) {
filesResponse, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, doer, opts)
assert.NoError(t, err)
- expectedFileResponse := getExpectedFileResponseForRepofilesDelete()
+ expectedFileResponse := getExpectedFileResponseForRepoFilesDelete()
assert.NotNil(t, filesResponse)
assert.Nil(t, filesResponse.Files[0])
- assert.EqualValues(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message)
- assert.EqualValues(t, expectedFileResponse.Commit.Author.Identity, filesResponse.Commit.Author.Identity)
- assert.EqualValues(t, expectedFileResponse.Commit.Committer.Identity, filesResponse.Commit.Committer.Identity)
- assert.EqualValues(t, expectedFileResponse.Verification, filesResponse.Verification)
+ assert.Equal(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message)
+ assert.Equal(t, expectedFileResponse.Commit.Author.Identity, filesResponse.Commit.Author.Identity)
+ assert.Equal(t, expectedFileResponse.Commit.Committer.Identity, filesResponse.Commit.Committer.Identity)
+ assert.Equal(t, expectedFileResponse.Verification, filesResponse.Verification)
})
}
@@ -490,7 +670,7 @@ func TestChangeRepoFilesErrors(t *testing.T) {
filesResponse, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, doer, opts)
assert.Error(t, err)
assert.Nil(t, filesResponse)
- expectedError := "branch does not exist [name: " + opts.OldBranch + "]"
+ expectedError := fmt.Sprintf("branch does not exist [repo_id: %d name: %s]", repo.ID, opts.OldBranch)
assert.EqualError(t, err, expectedError)
})
diff --git a/tests/integration/session_test.go b/tests/integration/session_test.go
index b18a25827d..f72e2e24b7 100644
--- a/tests/integration/session_test.go
+++ b/tests/integration/session_test.go
@@ -27,11 +27,11 @@ func Test_RegenerateSession(t *testing.T) {
sess, err := auth.RegenerateSession(db.DefaultContext, "", key)
assert.NoError(t, err)
- assert.EqualValues(t, key, sess.Key)
+ assert.Equal(t, key, sess.Key)
assert.Empty(t, sess.Data)
sess, err = auth.ReadSession(db.DefaultContext, key2)
assert.NoError(t, err)
- assert.EqualValues(t, key2, sess.Key)
+ assert.Equal(t, key2, sess.Key)
assert.Empty(t, sess.Data)
}
diff --git a/tests/integration/setting_test.go b/tests/integration/setting_test.go
index 9dad9ca716..64fd28c5e9 100644
--- a/tests/integration/setting_test.go
+++ b/tests/integration/setting_test.go
@@ -8,6 +8,7 @@ import (
"testing"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@@ -92,8 +93,7 @@ func TestSettingShowUserEmailProfile(t *testing.T) {
func TestSettingLandingPage(t *testing.T) {
defer tests.PrepareTestEnv(t)()
-
- landingPage := setting.LandingPageURL
+ defer test.MockVariableValue(&setting.LandingPageURL)()
setting.LandingPageURL = setting.LandingPageHome
req := NewRequest(t, "GET", "/")
@@ -113,6 +113,4 @@ func TestSettingLandingPage(t *testing.T) {
req = NewRequest(t, "GET", "/")
resp = MakeRequest(t, req, http.StatusSeeOther)
assert.Equal(t, "/user/login", resp.Header().Get("Location"))
-
- setting.LandingPageURL = landingPage
}
diff --git a/tests/integration/signin_test.go b/tests/integration/signin_test.go
index d7c0b1bcd3..67af5b5877 100644
--- a/tests/integration/signin_test.go
+++ b/tests/integration/signin_test.go
@@ -15,8 +15,12 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/translation"
+ "code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/routers"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/tests"
+ "github.com/markbates/goth"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -32,7 +36,7 @@ func testLoginFailed(t *testing.T, username, password, message string) {
htmlDoc := NewHTMLParser(t, resp.Body)
resultMsg := htmlDoc.doc.Find(".ui.message>p").Text()
- assert.EqualValues(t, message, resultMsg)
+ assert.Equal(t, message, resultMsg)
}
func TestSignin(t *testing.T) {
@@ -76,7 +80,7 @@ func TestSigninWithRememberMe(t *testing.T) {
})
session.MakeRequest(t, req, http.StatusSeeOther)
- c := session.GetCookie(setting.CookieRememberName)
+ c := session.GetRawCookie(setting.CookieRememberName)
assert.NotNil(t, c)
session = emptyTestSession(t)
@@ -95,19 +99,32 @@ func TestSigninWithRememberMe(t *testing.T) {
session.MakeRequest(t, req, http.StatusOK)
}
-func TestEnablePasswordSignInForm(t *testing.T) {
+func TestEnablePasswordSignInFormAndEnablePasskeyAuth(t *testing.T) {
defer tests.PrepareTestEnv(t)()
+ mockLinkAccount := func(ctx *context.Context) {
+ gothUser := goth.User{Email: "invalid-email", Name: "."}
+ _ = ctx.Session.Set("linkAccountGothUser", gothUser)
+ }
+
t.Run("EnablePasswordSignInForm=false", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
defer test.MockVariableValue(&setting.Service.EnablePasswordSignInForm, false)()
req := NewRequest(t, "GET", "/user/login")
resp := MakeRequest(t, req, http.StatusOK)
- NewHTMLParser(t, resp.Body).AssertElement(t, "form[action='/user/login']", false)
+ doc := NewHTMLParser(t, resp.Body)
+ AssertHTMLElement(t, doc, "form[action='/user/login']", false)
req = NewRequest(t, "POST", "/user/login")
MakeRequest(t, req, http.StatusForbidden)
+
+ req = NewRequest(t, "GET", "/user/link_account")
+ defer web.RouteMockReset()
+ web.RouteMock(web.MockAfterMiddlewares, mockLinkAccount)
+ resp = MakeRequest(t, req, http.StatusOK)
+ doc = NewHTMLParser(t, resp.Body)
+ AssertHTMLElement(t, doc, "form[action='/user/link_account_signin']", false)
})
t.Run("EnablePasswordSignInForm=true", func(t *testing.T) {
@@ -116,9 +133,66 @@ func TestEnablePasswordSignInForm(t *testing.T) {
req := NewRequest(t, "GET", "/user/login")
resp := MakeRequest(t, req, http.StatusOK)
- NewHTMLParser(t, resp.Body).AssertElement(t, "form[action='/user/login']", true)
+ doc := NewHTMLParser(t, resp.Body)
+ AssertHTMLElement(t, doc, "form[action='/user/login']", true)
req = NewRequest(t, "POST", "/user/login")
MakeRequest(t, req, http.StatusOK)
+
+ req = NewRequest(t, "GET", "/user/link_account")
+ defer web.RouteMockReset()
+ web.RouteMock(web.MockAfterMiddlewares, mockLinkAccount)
+ resp = MakeRequest(t, req, http.StatusOK)
+ doc = NewHTMLParser(t, resp.Body)
+ AssertHTMLElement(t, doc, "form[action='/user/link_account_signin']", true)
+ })
+
+ t.Run("EnablePasskeyAuth=false", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ defer test.MockVariableValue(&setting.Service.EnablePasskeyAuth, false)()
+
+ req := NewRequest(t, "GET", "/user/login")
+ resp := MakeRequest(t, req, http.StatusOK)
+ doc := NewHTMLParser(t, resp.Body)
+ AssertHTMLElement(t, doc, ".signin-passkey", false)
+ })
+
+ t.Run("EnablePasskeyAuth=true", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ defer test.MockVariableValue(&setting.Service.EnablePasskeyAuth, true)()
+
+ req := NewRequest(t, "GET", "/user/login")
+ resp := MakeRequest(t, req, http.StatusOK)
+ doc := NewHTMLParser(t, resp.Body)
+ AssertHTMLElement(t, doc, ".signin-passkey", true)
+ })
+}
+
+func TestRequireSignInView(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ t.Run("NoRequireSignInView", func(t *testing.T) {
+ require.False(t, setting.Service.RequireSignInViewStrict)
+ require.False(t, setting.Service.BlockAnonymousAccessExpensive)
+ req := NewRequest(t, "GET", "/user2/repo1/src/branch/master")
+ MakeRequest(t, req, http.StatusOK)
+ })
+ t.Run("RequireSignInView", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, true)()
+ defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
+ req := NewRequest(t, "GET", "/user2/repo1/src/branch/master")
+ resp := MakeRequest(t, req, http.StatusSeeOther)
+ assert.Equal(t, "/user/login", resp.Header().Get("Location"))
+ })
+ t.Run("BlockAnonymousAccessExpensive", func(t *testing.T) {
+ defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, false)()
+ defer test.MockVariableValue(&setting.Service.BlockAnonymousAccessExpensive, true)()
+ defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
+
+ req := NewRequest(t, "GET", "/user2/repo1")
+ MakeRequest(t, req, http.StatusOK)
+
+ req = NewRequest(t, "GET", "/user2/repo1/src/branch/master")
+ resp := MakeRequest(t, req, http.StatusSeeOther)
+ assert.Equal(t, "/user/login", resp.Header().Get("Location"))
})
}
diff --git a/tests/integration/signup_test.go b/tests/integration/signup_test.go
index e86851352e..c08f57d33e 100644
--- a/tests/integration/signup_test.go
+++ b/tests/integration/signup_test.go
@@ -22,8 +22,7 @@ import (
func TestSignup(t *testing.T) {
defer tests.PrepareTestEnv(t)()
-
- setting.Service.EnableCaptcha = false
+ defer test.MockVariableValue(&setting.Service.EnableCaptcha, false)()
req := NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{
"user_name": "exampleUser",
@@ -40,9 +39,8 @@ func TestSignup(t *testing.T) {
func TestSignupAsRestricted(t *testing.T) {
defer tests.PrepareTestEnv(t)()
-
- setting.Service.EnableCaptcha = false
- setting.Service.DefaultUserIsRestricted = true
+ defer test.MockVariableValue(&setting.Service.EnableCaptcha, false)()
+ defer test.MockVariableValue(&setting.Service.DefaultUserIsRestricted, true)()
req := NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{
"user_name": "restrictedUser",
@@ -62,8 +60,7 @@ func TestSignupAsRestricted(t *testing.T) {
func TestSignupEmailValidation(t *testing.T) {
defer tests.PrepareTestEnv(t)()
-
- setting.Service.EnableCaptcha = false
+ defer test.MockVariableValue(&setting.Service.EnableCaptcha, false)()
tests := []struct {
email string
diff --git a/tests/integration/ssh_key_test.go b/tests/integration/ssh_key_test.go
index eb3a3e926a..b34a986be3 100644
--- a/tests/integration/ssh_key_test.go
+++ b/tests/integration/ssh_key_test.go
@@ -27,7 +27,7 @@ func doCheckRepositoryEmptyStatus(ctx APITestContext, isEmpty bool) func(*testin
func doAddChangesToCheckout(dstPath, filename string) func(*testing.T) {
return func(t *testing.T) {
- assert.NoError(t, os.WriteFile(filepath.Join(dstPath, filename), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s at time: %v", dstPath, time.Now())), 0o644))
+ assert.NoError(t, os.WriteFile(filepath.Join(dstPath, filename), fmt.Appendf(nil, "# Testing Repository\n\nOriginally created in: %s at time: %v", dstPath, time.Now()), 0o644))
assert.NoError(t, git.AddChanges(dstPath, true))
signature := git.Signature{
Email: "test@example.com",
@@ -51,7 +51,7 @@ func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) {
ctx := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
ctxWithDeleteRepo := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
- keyname := fmt.Sprintf("%s-push", ctx.Reponame)
+ keyname := ctx.Reponame + "-push"
u.Path = ctx.GitPath()
t.Run("CreateEmptyRepository", doAPICreateRepository(ctx, true))
@@ -89,7 +89,7 @@ func testKeyOnlyOneType(t *testing.T, u *url.URL) {
reponame := "ssh-key-test-repo"
username := "user2"
u.Path = fmt.Sprintf("%s/%s.git", username, reponame)
- keyname := fmt.Sprintf("%s-push", reponame)
+ keyname := reponame + "-push"
// OK login
ctx := NewAPITestContext(t, username, reponame, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
diff --git a/tests/integration/timetracking_test.go b/tests/integration/timetracking_test.go
index d459de5df6..4e8109be96 100644
--- a/tests/integration/timetracking_test.go
+++ b/tests/integration/timetracking_test.go
@@ -42,15 +42,15 @@ func testViewTimetrackingControls(t *testing.T, session *TestSession, user, repo
htmlDoc := NewHTMLParser(t, resp.Body)
- htmlDoc.AssertElement(t, ".issue-start-time", canTrackTime)
- htmlDoc.AssertElement(t, ".issue-add-time", canTrackTime)
+ AssertHTMLElement(t, htmlDoc, ".issue-start-time", canTrackTime)
+ AssertHTMLElement(t, htmlDoc, ".issue-add-time", canTrackTime)
issueLink := path.Join(user, repo, "issues", issue)
- req = NewRequestWithValues(t, "POST", path.Join(issueLink, "times", "stopwatch", "toggle"), map[string]string{
+ reqStart := NewRequestWithValues(t, "POST", path.Join(issueLink, "times", "stopwatch", "start"), map[string]string{
"_csrf": htmlDoc.GetCSRF(),
})
if canTrackTime {
- session.MakeRequest(t, req, http.StatusOK)
+ session.MakeRequest(t, reqStart, http.StatusOK)
req = NewRequest(t, "GET", issueLink)
resp = session.MakeRequest(t, req, http.StatusOK)
@@ -59,16 +59,16 @@ func testViewTimetrackingControls(t *testing.T, session *TestSession, user, repo
events := htmlDoc.doc.Find(".event > span.text")
assert.Contains(t, events.Last().Text(), "started working")
- htmlDoc.AssertElement(t, ".issue-stop-time", true)
- htmlDoc.AssertElement(t, ".issue-cancel-time", true)
+ AssertHTMLElement(t, htmlDoc, ".issue-stop-time", true)
+ AssertHTMLElement(t, htmlDoc, ".issue-cancel-time", true)
// Sleep for 1 second to not get wrong order for stopping timer
time.Sleep(time.Second)
- req = NewRequestWithValues(t, "POST", path.Join(issueLink, "times", "stopwatch", "toggle"), map[string]string{
+ reqStop := NewRequestWithValues(t, "POST", path.Join(issueLink, "times", "stopwatch", "stop"), map[string]string{
"_csrf": htmlDoc.GetCSRF(),
})
- session.MakeRequest(t, req, http.StatusOK)
+ session.MakeRequest(t, reqStop, http.StatusOK)
req = NewRequest(t, "GET", issueLink)
resp = session.MakeRequest(t, req, http.StatusOK)
@@ -77,6 +77,6 @@ func testViewTimetrackingControls(t *testing.T, session *TestSession, user, repo
events = htmlDoc.doc.Find(".event > span.text")
assert.Contains(t, events.Last().Text(), "worked for ")
} else {
- session.MakeRequest(t, req, http.StatusNotFound)
+ session.MakeRequest(t, reqStart, http.StatusNotFound)
}
}
diff --git a/tests/integration/user_avatar_test.go b/tests/integration/user_avatar_test.go
index caca9a3e56..14ea012ac8 100644
--- a/tests/integration/user_avatar_test.go
+++ b/tests/integration/user_avatar_test.go
@@ -5,89 +5,87 @@ package integration
import (
"bytes"
- "fmt"
"image/png"
"io"
"mime/multipart"
"net/http"
- "net/url"
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/avatar"
+ "code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
)
func TestUserAvatar(t *testing.T) {
- onGiteaRun(t, func(t *testing.T, u *url.URL) {
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the repo3, is an org
+ defer tests.PrepareTestEnv(t)()
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the repo3, is an org
- seed := user2.Email
- if len(seed) == 0 {
- seed = user2.Name
- }
+ seed := user2.Email
+ if len(seed) == 0 {
+ seed = user2.Name
+ }
- img, err := avatar.RandomImage([]byte(seed))
- if err != nil {
- assert.NoError(t, err)
- return
- }
+ img, err := avatar.RandomImage([]byte(seed))
+ if err != nil {
+ assert.NoError(t, err)
+ return
+ }
- session := loginUser(t, "user2")
- csrf := GetUserCSRFToken(t, session)
+ session := loginUser(t, "user2")
+ csrf := GetUserCSRFToken(t, session)
- imgData := &bytes.Buffer{}
+ imgData := &bytes.Buffer{}
- body := &bytes.Buffer{}
+ body := &bytes.Buffer{}
- // Setup multi-part
- writer := multipart.NewWriter(body)
- writer.WriteField("source", "local")
- part, err := writer.CreateFormFile("avatar", "avatar-for-testuseravatar.png")
- if err != nil {
- assert.NoError(t, err)
- return
- }
+ // Setup multi-part
+ writer := multipart.NewWriter(body)
+ writer.WriteField("source", "local")
+ part, err := writer.CreateFormFile("avatar", "avatar-for-testuseravatar.png")
+ if err != nil {
+ assert.NoError(t, err)
+ return
+ }
- if err := png.Encode(imgData, img); err != nil {
- assert.NoError(t, err)
- return
- }
+ if err := png.Encode(imgData, img); err != nil {
+ assert.NoError(t, err)
+ return
+ }
- if _, err := io.Copy(part, imgData); err != nil {
- assert.NoError(t, err)
- return
- }
+ if _, err := io.Copy(part, imgData); err != nil {
+ assert.NoError(t, err)
+ return
+ }
- if err := writer.Close(); err != nil {
- assert.NoError(t, err)
- return
- }
+ if err := writer.Close(); err != nil {
+ assert.NoError(t, err)
+ return
+ }
- req := NewRequestWithBody(t, "POST", "/user/settings/avatar", body)
- req.Header.Add("X-Csrf-Token", csrf)
- req.Header.Add("Content-Type", writer.FormDataContentType())
+ req := NewRequestWithBody(t, "POST", "/user/settings/avatar", body)
+ req.Header.Add("X-Csrf-Token", csrf)
+ req.Header.Add("Content-Type", writer.FormDataContentType())
- session.MakeRequest(t, req, http.StatusSeeOther)
+ session.MakeRequest(t, req, http.StatusSeeOther)
- user2 = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the repo3, is an org
+ user2 = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the repo3, is an org
- req = NewRequest(t, "GET", user2.AvatarLinkWithSize(db.DefaultContext, 0))
- _ = session.MakeRequest(t, req, http.StatusOK)
+ req = NewRequest(t, "GET", user2.AvatarLinkWithSize(db.DefaultContext, 0))
+ _ = session.MakeRequest(t, req, http.StatusOK)
- testGetAvatarRedirect(t, user2)
+ testGetAvatarRedirect(t, user2)
- // Can't test if the response matches because the image is re-generated on upload but checking that this at least doesn't give a 404 should be enough.
- })
+ // Can't test if the response matches because the image is re-generated on upload but checking that this at least doesn't give a 404 should be enough.
}
func testGetAvatarRedirect(t *testing.T, user *user_model.User) {
- t.Run(fmt.Sprintf("getAvatarRedirect_%s", user.Name), func(t *testing.T) {
+ t.Run("getAvatarRedirect_"+user.Name, func(t *testing.T) {
req := NewRequestf(t, "GET", "/%s.png", user.Name)
resp := MakeRequest(t, req, http.StatusSeeOther)
- assert.EqualValues(t, fmt.Sprintf("/avatars/%s", user.Avatar), resp.Header().Get("location"))
+ assert.Equal(t, "/avatars/"+user.Avatar, resp.Header().Get("location"))
})
}
diff --git a/tests/integration/user_settings_test.go b/tests/integration/user_settings_test.go
index d8402eb25f..eab1a72ed5 100644
--- a/tests/integration/user_settings_test.go
+++ b/tests/integration/user_settings_test.go
@@ -19,21 +19,21 @@ import (
func assertNavbar(t *testing.T, doc *HTMLDoc) {
// Only show the account page if users can change their email notifications, delete themselves, or manage credentials
if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureDeletion, setting.UserFeatureManageCredentials) && !setting.Service.EnableNotifyMail {
- doc.AssertElement(t, ".menu a[href='/user/settings/account']", false)
+ AssertHTMLElement(t, doc, ".menu a[href='/user/settings/account']", false)
} else {
- doc.AssertElement(t, ".menu a[href='/user/settings/account']", true)
+ AssertHTMLElement(t, doc, ".menu a[href='/user/settings/account']", true)
}
if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageMFA, setting.UserFeatureManageCredentials) {
- doc.AssertElement(t, ".menu a[href='/user/settings/security']", false)
+ AssertHTMLElement(t, doc, ".menu a[href='/user/settings/security']", false)
} else {
- doc.AssertElement(t, ".menu a[href='/user/settings/security']", true)
+ AssertHTMLElement(t, doc, ".menu a[href='/user/settings/security']", true)
}
if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageSSHKeys, setting.UserFeatureManageGPGKeys) {
- doc.AssertElement(t, ".menu a[href='/user/settings/keys']", false)
+ AssertHTMLElement(t, doc, ".menu a[href='/user/settings/keys']", false)
} else {
- doc.AssertElement(t, ".menu a[href='/user/settings/keys']", true)
+ AssertHTMLElement(t, doc, ".menu a[href='/user/settings/keys']", true)
}
}
@@ -64,11 +64,11 @@ func TestUserSettingsAccount(t *testing.T) {
doc := NewHTMLParser(t, resp.Body)
// account navbar should display
- doc.AssertElement(t, ".menu a[href='/user/settings/account']", true)
+ AssertHTMLElement(t, doc, ".menu a[href='/user/settings/account']", true)
- doc.AssertElement(t, "#password", true)
- doc.AssertElement(t, "#email", true)
- doc.AssertElement(t, "#delete-form", true)
+ AssertHTMLElement(t, doc, "#password", true)
+ AssertHTMLElement(t, doc, "#email", true)
+ AssertHTMLElement(t, doc, "#delete-form", true)
})
t.Run("credentials disabled", func(t *testing.T) {
@@ -83,9 +83,9 @@ func TestUserSettingsAccount(t *testing.T) {
assertNavbar(t, doc)
- doc.AssertElement(t, "#password", false)
- doc.AssertElement(t, "#email", false)
- doc.AssertElement(t, "#delete-form", true)
+ AssertHTMLElement(t, doc, "#password", false)
+ AssertHTMLElement(t, doc, "#email", false)
+ AssertHTMLElement(t, doc, "#delete-form", true)
})
t.Run("deletion disabled", func(t *testing.T) {
@@ -100,9 +100,9 @@ func TestUserSettingsAccount(t *testing.T) {
assertNavbar(t, doc)
- doc.AssertElement(t, "#password", true)
- doc.AssertElement(t, "#email", true)
- doc.AssertElement(t, "#delete-form", false)
+ AssertHTMLElement(t, doc, "#password", true)
+ AssertHTMLElement(t, doc, "#email", true)
+ AssertHTMLElement(t, doc, "#delete-form", false)
})
t.Run("deletion, credentials and email notifications are disabled", func(t *testing.T) {
@@ -249,7 +249,7 @@ func TestUserSettingsSecurity(t *testing.T) {
assertNavbar(t, doc)
- doc.AssertElement(t, "#register-webauthn", true)
+ AssertHTMLElement(t, doc, "#register-webauthn", true)
})
t.Run("mfa disabled", func(t *testing.T) {
@@ -263,7 +263,7 @@ func TestUserSettingsSecurity(t *testing.T) {
assertNavbar(t, doc)
- doc.AssertElement(t, "#register-webauthn", false)
+ AssertHTMLElement(t, doc, "#register-webauthn", false)
})
t.Run("credentials and mfa disabled", func(t *testing.T) {
@@ -356,8 +356,8 @@ func TestUserSettingsKeys(t *testing.T) {
assertNavbar(t, doc)
- doc.AssertElement(t, "#add-ssh-button", true)
- doc.AssertElement(t, "#add-gpg-key-panel", true)
+ AssertHTMLElement(t, doc, "#add-ssh-button", true)
+ AssertHTMLElement(t, doc, "#add-gpg-key-panel", true)
})
t.Run("ssh keys disabled", func(t *testing.T) {
@@ -372,8 +372,8 @@ func TestUserSettingsKeys(t *testing.T) {
assertNavbar(t, doc)
- doc.AssertElement(t, "#add-ssh-button", false)
- doc.AssertElement(t, "#add-gpg-key-panel", true)
+ AssertHTMLElement(t, doc, "#add-ssh-button", false)
+ AssertHTMLElement(t, doc, "#add-gpg-key-panel", true)
})
t.Run("gpg keys disabled", func(t *testing.T) {
@@ -388,8 +388,8 @@ func TestUserSettingsKeys(t *testing.T) {
assertNavbar(t, doc)
- doc.AssertElement(t, "#add-ssh-button", true)
- doc.AssertElement(t, "#add-gpg-key-panel", false)
+ AssertHTMLElement(t, doc, "#add-ssh-button", true)
+ AssertHTMLElement(t, doc, "#add-gpg-key-panel", false)
})
t.Run("ssh & gpg keys disabled", func(t *testing.T) {
diff --git a/tests/integration/user_test.go b/tests/integration/user_test.go
index 5b6f28d1ff..34692d9cab 100644
--- a/tests/integration/user_test.go
+++ b/tests/integration/user_test.go
@@ -134,8 +134,7 @@ Note: This user hasn't uploaded any GPG keys.
=twTO
------END PGP PUBLIC KEY BLOCK-----
-`)
+-----END PGP PUBLIC KEY BLOCK-----`)
// Import key
// User1 <user1@example.com>
session := loginUser(t, "user1")
@@ -169,8 +168,7 @@ C0TLXKur6NVYQMn01iyL+FZzRpEWNuYF3f9QeeLJ/+l2DafESNhNTy17+RPmacK6
7XhJ1v6JYuh8kaYaEz8OpZDeh7f6Ho6PzJrsy/TKTKhGgZNINj1iaPFyOkQgKR5M
GrE0MHOxUbc9tbtyk0F1SuzREUBH
=DDXw
------END PGP PUBLIC KEY BLOCK-----
-`)
+-----END PGP PUBLIC KEY BLOCK-----`)
// Export new key
testExportUserGPGKeys(t, "user1", `-----BEGIN PGP PUBLIC KEY BLOCK-----
@@ -201,8 +199,7 @@ C0TLXKur6NVYQMn01iyL+FZzRpEWNuYF3f9QeeLJ/+l2DafESNhNTy17+RPmacK6
7XhJ1v6JYuh8kaYaEz8OpZDeh7f6Ho6PzJrsy/TKTKhGgZNINj1iaPFyOkQgKR5M
GrE0MHOxUbc9tbtyk0F1SuzREUBH
=WFf5
------END PGP PUBLIC KEY BLOCK-----
-`)
+-----END PGP PUBLIC KEY BLOCK-----`)
}
func testExportUserGPGKeys(t *testing.T, user, expected string) {
@@ -220,12 +217,12 @@ func TestGetUserRss(t *testing.T) {
user34 := "the_34-user.with.all.allowedChars"
req := NewRequestf(t, "GET", "/%s.rss", user34)
resp := MakeRequest(t, req, http.StatusOK)
- if assert.EqualValues(t, "application/rss+xml;charset=utf-8", resp.Header().Get("Content-Type")) {
+ if assert.Equal(t, "application/rss+xml;charset=utf-8", resp.Header().Get("Content-Type")) {
rssDoc := NewHTMLParser(t, resp.Body).Find("channel")
title, _ := rssDoc.ChildrenFiltered("title").Html()
- assert.EqualValues(t, "Feed of &#34;the_1-user.with.all.allowedChars&#34;", title)
+ assert.Equal(t, "Feed of &#34;the_1-user.with.all.allowedChars&#34;", title)
description, _ := rssDoc.ChildrenFiltered("description").Html()
- assert.EqualValues(t, "&lt;p dir=&#34;auto&#34;&gt;some &lt;a href=&#34;https://commonmark.org/&#34; rel=&#34;nofollow&#34;&gt;commonmark&lt;/a&gt;!&lt;/p&gt;\n", description)
+ assert.Equal(t, "&lt;p dir=&#34;auto&#34;&gt;some &lt;a href=&#34;https://commonmark.org/&#34; rel=&#34;nofollow&#34;&gt;commonmark&lt;/a&gt;!&lt;/p&gt;\n", description)
}
req = NewRequestf(t, "GET", "/non-existent-user.rss")
@@ -250,18 +247,18 @@ func TestListStopWatches(t *testing.T) {
stopwatch := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: owner.ID})
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: stopwatch.IssueID})
if assert.Len(t, apiWatches, 1) {
- assert.EqualValues(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix())
- assert.EqualValues(t, issue.Index, apiWatches[0].IssueIndex)
- assert.EqualValues(t, issue.Title, apiWatches[0].IssueTitle)
- assert.EqualValues(t, repo.Name, apiWatches[0].RepoName)
- assert.EqualValues(t, repo.OwnerName, apiWatches[0].RepoOwnerName)
+ assert.Equal(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix())
+ assert.Equal(t, issue.Index, apiWatches[0].IssueIndex)
+ assert.Equal(t, issue.Title, apiWatches[0].IssueTitle)
+ assert.Equal(t, repo.Name, apiWatches[0].RepoName)
+ assert.Equal(t, repo.OwnerName, apiWatches[0].RepoOwnerName)
assert.Positive(t, apiWatches[0].Seconds)
}
}
func TestUserLocationMapLink(t *testing.T) {
- setting.Service.UserLocationMapURL = "https://example/foo/"
defer tests.PrepareTestEnv(t)()
+ defer test.MockVariableValue(&setting.Service.UserLocationMapURL, "https://example/foo/")()
session := loginUser(t, "user2")
req := NewRequestWithValues(t, "POST", "/user/settings", map[string]string{
@@ -276,5 +273,5 @@ func TestUserLocationMapLink(t *testing.T) {
req = NewRequest(t, "GET", "/user2/")
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
- htmlDoc.AssertElement(t, `a[href="https://example/foo/A%2Fb"]`, true)
+ AssertHTMLElement(t, htmlDoc, `a[href="https://example/foo/A%2Fb"]`, true)
}
diff --git a/tests/integration/view_test.go b/tests/integration/view_test.go
index f434446801..9ed3e30857 100644
--- a/tests/integration/view_test.go
+++ b/tests/integration/view_test.go
@@ -25,3 +25,43 @@ func TestRenderFileSVGIsInImgTag(t *testing.T) {
assert.True(t, exists, "The SVG image should be in an <img> tag so that scripts in the SVG are not run")
assert.Equal(t, "/user2/repo2/raw/branch/master/line.svg", src)
}
+
+func TestCommitListActions(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+ session := loginUser(t, "user2")
+
+ t.Run("WikiRevisionList", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequest(t, "GET", "/user2/repo1/wiki/Home?action=_revision")
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ AssertHTMLElement(t, htmlDoc, ".commit-list .copy-commit-id", true)
+ AssertHTMLElement(t, htmlDoc, `.commit-list .view-single-diff`, false)
+ AssertHTMLElement(t, htmlDoc, `.commit-list .view-commit-path`, false)
+ })
+
+ t.Run("RepoCommitList", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequest(t, "GET", "/user2/repo1/commits/branch/master")
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+
+ AssertHTMLElement(t, htmlDoc, `.commit-list .copy-commit-id`, true)
+ AssertHTMLElement(t, htmlDoc, `.commit-list .view-single-diff`, false)
+ AssertHTMLElement(t, htmlDoc, `.commit-list .view-commit-path`, true)
+ })
+
+ t.Run("RepoFileHistory", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+
+ req := NewRequest(t, "GET", "/user2/repo1/commits/branch/master/README.md")
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+
+ AssertHTMLElement(t, htmlDoc, `.commit-list .copy-commit-id`, true)
+ AssertHTMLElement(t, htmlDoc, `.commit-list .view-single-diff`, true)
+ AssertHTMLElement(t, htmlDoc, `.commit-list .view-commit-path`, true)
+ })
+}
diff --git a/tests/integration/webfinger_test.go b/tests/integration/webfinger_test.go
index a1abc8d32b..757d442cd2 100644
--- a/tests/integration/webfinger_test.go
+++ b/tests/integration/webfinger_test.go
@@ -7,11 +7,13 @@ import (
"fmt"
"net/http"
"net/url"
+ "strconv"
"testing"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@@ -19,11 +21,7 @@ import (
func TestWebfinger(t *testing.T) {
defer tests.PrepareTestEnv(t)()
-
- setting.Federation.Enabled = true
- defer func() {
- setting.Federation.Enabled = false
- }()
+ defer test.MockVariableValue(&setting.Federation.Enabled, true)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
@@ -52,7 +50,7 @@ func TestWebfinger(t *testing.T) {
var jrd webfingerJRD
DecodeJSON(t, resp, &jrd)
assert.Equal(t, "acct:user2@"+appURL.Host, jrd.Subject)
- assert.ElementsMatch(t, []string{user.HTMLURL(), appURL.String() + "api/v1/activitypub/user-id/" + fmt.Sprint(user.ID)}, jrd.Aliases)
+ assert.ElementsMatch(t, []string{user.HTMLURL(), appURL.String() + "api/v1/activitypub/user-id/" + strconv.FormatInt(user.ID, 10)}, jrd.Aliases)
req = NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=acct:%s@%s", user.LowerName, "unknown.host"))
MakeRequest(t, req, http.StatusBadRequest)
@@ -63,6 +61,6 @@ func TestWebfinger(t *testing.T) {
req = NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=acct:%s@%s", "user31", appURL.Host))
session.MakeRequest(t, req, http.StatusOK)
- req = NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=mailto:%s", user.Email))
+ req = NewRequest(t, "GET", "/.well-known/webfinger?resource=mailto:"+user.Email)
MakeRequest(t, req, http.StatusNotFound)
}
diff --git a/tests/integration/wiki_test.go b/tests/integration/wiki_test.go
index bb73f71a45..ac458af378 100644
--- a/tests/integration/wiki_test.go
+++ b/tests/integration/wiki_test.go
@@ -4,8 +4,6 @@
package integration
import (
- "context"
- "fmt"
"net/http"
"net/url"
"os"
@@ -30,7 +28,7 @@ func assertFileExist(t *testing.T, p string) {
func assertFileEqual(t *testing.T, p string, content []byte) {
bs, err := os.ReadFile(p)
assert.NoError(t, err)
- assert.EqualValues(t, content, bs)
+ assert.Equal(t, content, bs)
}
func TestRepoCloneWiki(t *testing.T) {
@@ -39,11 +37,11 @@ func TestRepoCloneWiki(t *testing.T) {
dstPath := t.TempDir()
- r := fmt.Sprintf("%suser2/repo1.wiki.git", u.String())
+ r := u.String() + "user2/repo1.wiki.git"
u, _ = url.Parse(r)
u.User = url.UserPassword("user2", userPassword)
t.Run("Clone", func(t *testing.T) {
- assert.NoError(t, git.CloneWithArgs(context.Background(), git.AllowLFSFiltersArgs(), u.String(), dstPath, git.CloneRepoOptions{}))
+ assert.NoError(t, git.CloneWithArgs(t.Context(), git.AllowLFSFiltersArgs(), u.String(), dstPath, git.CloneRepoOptions{}))
assertFileEqual(t, filepath.Join(dstPath, "Home.md"), []byte("# Home page\n\nThis is the home page!\n"))
assertFileExist(t, filepath.Join(dstPath, "Page-With-Image.md"))
assertFileExist(t, filepath.Join(dstPath, "Page-With-Spaced-Name.md"))
@@ -70,6 +68,6 @@ func Test_RepoWikiPages(t *testing.T) {
href, _ := firstAnchor.Attr("href")
pagePath := strings.TrimPrefix(href, "/user2/repo1/wiki/")
- assert.EqualValues(t, expectedPagePaths[i], pagePath)
+ assert.Equal(t, expectedPagePaths[i], pagePath)
})
}
diff --git a/tests/integration/workflow_run_api_check_test.go b/tests/integration/workflow_run_api_check_test.go
new file mode 100644
index 0000000000..6a80bb5118
--- /dev/null
+++ b/tests/integration/workflow_run_api_check_test.go
@@ -0,0 +1,167 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package integration
+
+import (
+ "fmt"
+ "net/http"
+ "net/url"
+ "testing"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/tests"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestAPIWorkflowRun(t *testing.T) {
+ t.Run("AdminRuns", func(t *testing.T) {
+ testAPIWorkflowRunBasic(t, "/api/v1/admin/actions", "User1", 802, auth_model.AccessTokenScopeReadAdmin, auth_model.AccessTokenScopeReadRepository)
+ })
+ t.Run("UserRuns", func(t *testing.T) {
+ testAPIWorkflowRunBasic(t, "/api/v1/user/actions", "User2", 803, auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopeReadRepository)
+ })
+ t.Run("OrgRuns", func(t *testing.T) {
+ testAPIWorkflowRunBasic(t, "/api/v1/orgs/org3/actions", "User1", 802, auth_model.AccessTokenScopeReadOrganization, auth_model.AccessTokenScopeReadRepository)
+ })
+ t.Run("RepoRuns", func(t *testing.T) {
+ testAPIWorkflowRunBasic(t, "/api/v1/repos/org3/repo5/actions", "User2", 802, auth_model.AccessTokenScopeReadRepository)
+ })
+}
+
+func testAPIWorkflowRunBasic(t *testing.T, apiRootURL, userUsername string, runID int64, scope ...auth_model.AccessTokenScope) {
+ defer tests.PrepareTestEnv(t)()
+ token := getUserToken(t, userUsername, scope...)
+
+ apiRunsURL := fmt.Sprintf("%s/%s", apiRootURL, "runs")
+ req := NewRequest(t, "GET", apiRunsURL).AddTokenAuth(token)
+ runnerListResp := MakeRequest(t, req, http.StatusOK)
+ runnerList := api.ActionWorkflowRunsResponse{}
+ DecodeJSON(t, runnerListResp, &runnerList)
+
+ foundRun := false
+
+ for _, run := range runnerList.Entries {
+ // Verify filtering works
+ verifyWorkflowRunCanbeFoundWithStatusFilter(t, apiRunsURL, token, run.ID, "", run.Status, "", "", "", "")
+ verifyWorkflowRunCanbeFoundWithStatusFilter(t, apiRunsURL, token, run.ID, run.Conclusion, "", "", "", "", "")
+ verifyWorkflowRunCanbeFoundWithStatusFilter(t, apiRunsURL, token, run.ID, "", "", "", run.HeadBranch, "", "")
+ verifyWorkflowRunCanbeFoundWithStatusFilter(t, apiRunsURL, token, run.ID, "", "", run.Event, "", "", "")
+ verifyWorkflowRunCanbeFoundWithStatusFilter(t, apiRunsURL, token, run.ID, "", "", "", "", run.TriggerActor.UserName, "")
+ verifyWorkflowRunCanbeFoundWithStatusFilter(t, apiRunsURL, token, run.ID, "", "", "", "", run.TriggerActor.UserName, run.HeadSha)
+
+ // Verify run url works
+ req := NewRequest(t, "GET", run.URL).AddTokenAuth(token)
+ runResp := MakeRequest(t, req, http.StatusOK)
+ apiRun := api.ActionWorkflowRun{}
+ DecodeJSON(t, runResp, &apiRun)
+ assert.Equal(t, run.ID, apiRun.ID)
+ assert.Equal(t, run.Status, apiRun.Status)
+ assert.Equal(t, run.Conclusion, apiRun.Conclusion)
+ assert.Equal(t, run.Event, apiRun.Event)
+
+ // Verify jobs list works
+ req = NewRequest(t, "GET", fmt.Sprintf("%s/%s", run.URL, "jobs")).AddTokenAuth(token)
+ jobsResp := MakeRequest(t, req, http.StatusOK)
+ jobList := api.ActionWorkflowJobsResponse{}
+ DecodeJSON(t, jobsResp, &jobList)
+
+ if run.ID == runID {
+ foundRun = true
+ assert.Len(t, jobList.Entries, 1)
+ for _, job := range jobList.Entries {
+ // Check the jobs list of the run
+ verifyWorkflowJobCanbeFoundWithStatusFilter(t, fmt.Sprintf("%s/%s", run.URL, "jobs"), token, job.ID, "", job.Status)
+ verifyWorkflowJobCanbeFoundWithStatusFilter(t, fmt.Sprintf("%s/%s", run.URL, "jobs"), token, job.ID, job.Conclusion, "")
+ // Check the run independent job list
+ verifyWorkflowJobCanbeFoundWithStatusFilter(t, fmt.Sprintf("%s/%s", apiRootURL, "jobs"), token, job.ID, "", job.Status)
+ verifyWorkflowJobCanbeFoundWithStatusFilter(t, fmt.Sprintf("%s/%s", apiRootURL, "jobs"), token, job.ID, job.Conclusion, "")
+
+ // Verify job url works
+ req := NewRequest(t, "GET", job.URL).AddTokenAuth(token)
+ jobsResp := MakeRequest(t, req, http.StatusOK)
+ apiJob := api.ActionWorkflowJob{}
+ DecodeJSON(t, jobsResp, &apiJob)
+ assert.Equal(t, job.ID, apiJob.ID)
+ assert.Equal(t, job.RunID, apiJob.RunID)
+ assert.Equal(t, job.Status, apiJob.Status)
+ assert.Equal(t, job.Conclusion, apiJob.Conclusion)
+ }
+ }
+ }
+ assert.True(t, foundRun, "Expected to find run with ID %d", runID)
+}
+
+func verifyWorkflowRunCanbeFoundWithStatusFilter(t *testing.T, runAPIURL, token string, id int64, conclusion, status, event, branch, actor, headSHA string) {
+ filter := url.Values{}
+ if conclusion != "" {
+ filter.Add("status", conclusion)
+ }
+ if status != "" {
+ filter.Add("status", status)
+ }
+ if event != "" {
+ filter.Set("event", event)
+ }
+ if branch != "" {
+ filter.Set("branch", branch)
+ }
+ if actor != "" {
+ filter.Set("actor", actor)
+ }
+ if headSHA != "" {
+ filter.Set("head_sha", headSHA)
+ }
+ req := NewRequest(t, "GET", runAPIURL+"?"+filter.Encode()).AddTokenAuth(token)
+ runResp := MakeRequest(t, req, http.StatusOK)
+ runList := api.ActionWorkflowRunsResponse{}
+ DecodeJSON(t, runResp, &runList)
+
+ found := false
+ for _, run := range runList.Entries {
+ if conclusion != "" {
+ assert.Equal(t, conclusion, run.Conclusion)
+ }
+ if status != "" {
+ assert.Equal(t, status, run.Status)
+ }
+ if event != "" {
+ assert.Equal(t, event, run.Event)
+ }
+ if branch != "" {
+ assert.Equal(t, branch, run.HeadBranch)
+ }
+ if actor != "" {
+ assert.Equal(t, actor, run.Actor.UserName)
+ }
+ found = found || run.ID == id
+ }
+ assert.True(t, found, "Expected to find run with ID %d", id)
+}
+
+func verifyWorkflowJobCanbeFoundWithStatusFilter(t *testing.T, runAPIURL, token string, id int64, conclusion, status string) {
+ filter := conclusion
+ if filter == "" {
+ filter = status
+ }
+ if filter == "" {
+ return
+ }
+ req := NewRequest(t, "GET", runAPIURL+"?status="+filter).AddTokenAuth(token)
+ jobListResp := MakeRequest(t, req, http.StatusOK)
+ jobList := api.ActionWorkflowJobsResponse{}
+ DecodeJSON(t, jobListResp, &jobList)
+
+ found := false
+ for _, job := range jobList.Entries {
+ if conclusion != "" {
+ assert.Equal(t, conclusion, job.Conclusion)
+ } else {
+ assert.Equal(t, status, job.Status)
+ }
+ found = found || job.ID == id
+ }
+ assert.True(t, found, "Expected to find job with ID %d", id)
+}
diff --git a/tests/integration/xss_test.go b/tests/integration/xss_test.go
index a8eaa5fc62..9058fc210a 100644
--- a/tests/integration/xss_test.go
+++ b/tests/integration/xss_test.go
@@ -32,8 +32,8 @@ func TestXSSUserFullName(t *testing.T) {
req = NewRequestf(t, "GET", "/%s", user.Name)
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
- assert.EqualValues(t, 0, htmlDoc.doc.Find("script.evil").Length())
- assert.EqualValues(t, fullName,
+ assert.Equal(t, 0, htmlDoc.doc.Find("script.evil").Length())
+ assert.Equal(t, fullName,
htmlDoc.doc.Find("div.content").Find(".header.text.center").Text(),
)
}
diff --git a/tests/mssql.ini.tmpl b/tests/mssql.ini.tmpl
index b50816b2cd..42bf683a07 100644
--- a/tests/mssql.ini.tmpl
+++ b/tests/mssql.ini.tmpl
@@ -26,6 +26,9 @@ TYPE = immediate
[queue.push_update]
TYPE = immediate
+[queue.webhook_sender]
+TYPE = immediate
+
[repository]
ROOT = {{REPO_TEST_DIR}}tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mssql/gitea-repositories
@@ -42,6 +45,7 @@ SIGNING_KEY = none
SSH_DOMAIN = localhost
HTTP_PORT = 3003
ROOT_URL = http://localhost:3003/
+LOCAL_ROOT_URL = http://127.0.0.1:3003/
DISABLE_SSH = false
SSH_LISTEN_HOST = localhost
SSH_PORT = 2201
@@ -111,3 +115,6 @@ ENABLED = true
[actions]
ENABLED = true
+
+[webhook]
+ALLOWED_HOST_LIST = 127.0.0.1
diff --git a/tests/mysql.ini.tmpl b/tests/mysql.ini.tmpl
index ec8307acc3..7cef540d1d 100644
--- a/tests/mysql.ini.tmpl
+++ b/tests/mysql.ini.tmpl
@@ -28,6 +28,9 @@ TYPE = immediate
[queue.push_update]
TYPE = immediate
+[queue.webhook_sender]
+TYPE = immediate
+
[repository]
ROOT = {{REPO_TEST_DIR}}tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mysql/gitea-repositories
@@ -44,6 +47,7 @@ SIGNING_KEY = none
SSH_DOMAIN = localhost
HTTP_PORT = 3001
ROOT_URL = http://localhost:3001/
+LOCAL_ROOT_URL = http://127.0.0.1:3001/
DISABLE_SSH = false
SSH_LISTEN_HOST = localhost
SSH_PORT = 2201
@@ -118,3 +122,6 @@ REPLY_TO_ADDRESS = incoming+%{token}@localhost
[actions]
ENABLED = true
+
+[webhook]
+ALLOWED_HOST_LIST = 127.0.0.1
diff --git a/tests/pgsql.ini.tmpl b/tests/pgsql.ini.tmpl
index 139ea9c2b7..13a5932608 100644
--- a/tests/pgsql.ini.tmpl
+++ b/tests/pgsql.ini.tmpl
@@ -27,6 +27,9 @@ TYPE = immediate
[queue.push_update]
TYPE = immediate
+[queue.webhook_sender]
+TYPE = immediate
+
[repository]
ROOT = {{REPO_TEST_DIR}}tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-pgsql/gitea-repositories
@@ -43,6 +46,7 @@ SIGNING_KEY = none
SSH_DOMAIN = localhost
HTTP_PORT = 3002
ROOT_URL = http://localhost:3002/
+LOCAL_ROOT_URL = http://127.0.0.1:3002/
DISABLE_SSH = false
SSH_LISTEN_HOST = localhost
SSH_PORT = 2202
@@ -127,3 +131,6 @@ ENABLED = true
[actions]
ENABLED = true
+
+[webhook]
+ALLOWED_HOST_LIST = 127.0.0.1
diff --git a/tests/sqlite.ini.tmpl b/tests/sqlite.ini.tmpl
index 2f7a3e8182..938f203633 100644
--- a/tests/sqlite.ini.tmpl
+++ b/tests/sqlite.ini.tmpl
@@ -22,6 +22,9 @@ TYPE = immediate
[queue.push_update]
TYPE = immediate
+[queue.webhook_sender]
+TYPE = immediate
+
[repository]
ROOT = {{REPO_TEST_DIR}}tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-sqlite/gitea-repositories
@@ -38,6 +41,7 @@ SIGNING_KEY = none
SSH_DOMAIN = localhost
HTTP_PORT = 3003
ROOT_URL = http://localhost:3003/
+LOCAL_ROOT_URL = http://127.0.0.1:3003/
DISABLE_SSH = false
SSH_LISTEN_HOST = localhost
SSH_PORT = 2203
@@ -116,3 +120,6 @@ RENDER_CONTENT_MODE=sanitized
[actions]
ENABLED = true
+
+[webhook]
+ALLOWED_HOST_LIST = 127.0.0.1
diff --git a/tests/test_utils.go b/tests/test_utils.go
index 96eb5731b4..ad692058a6 100644
--- a/tests/test_utils.go
+++ b/tests/test_utils.go
@@ -1,7 +1,6 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-//nolint:forbidigo
package tests
import (
@@ -18,7 +17,6 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
- repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/test"
@@ -56,7 +54,7 @@ func InitTest(requireGitea bool) {
// Notice: when doing "ssh push", Gitea executes sub processes, debugger won't work for the sub processes.
giteaConf = "tests/sqlite.ini"
_ = os.Setenv("GITEA_CONF", giteaConf)
- fmt.Printf("Environment variable $GITEA_CONF not set, use default: %s\n", giteaConf)
+ _, _ = fmt.Fprintf(os.Stderr, "Environment variable $GITEA_CONF not set - defaulting to %s\n", giteaConf)
if !setting.EnableSQLite3 {
testlogger.Fatalf(`sqlite3 requires: -tags sqlite,sqlite_unlock_notify` + "\n")
}
@@ -67,9 +65,8 @@ func InitTest(requireGitea bool) {
setting.CustomConf = giteaConf
}
- unittest.InitSettings()
+ unittest.InitSettingsForTesting()
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
- _ = util.RemoveAll(repo_module.LocalCopyPath())
if err := git.InitFull(context.Background()); err != nil {
log.Fatal("git.InitOnceWithSync: %v", err)
@@ -93,7 +90,7 @@ func InitTest(requireGitea bool) {
if err != nil {
log.Fatal("sql.Open: %v", err)
}
- if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", setting.Database.Name)); err != nil {
+ if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + setting.Database.Name); err != nil {
log.Fatal("db.Exec: %v", err)
}
case setting.Database.Type.IsPostgreSQL():
@@ -118,7 +115,7 @@ func InitTest(requireGitea bool) {
defer dbrows.Close()
if !dbrows.Next() {
- if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", setting.Database.Name)); err != nil {
+ if _, err = db.Exec("CREATE DATABASE " + setting.Database.Name); err != nil {
log.Fatal("db.Exec: CREATE DATABASE: %v", err)
}
}
@@ -148,7 +145,7 @@ func InitTest(requireGitea bool) {
if !schrows.Next() {
// Create and setup a DB schema
- if _, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s", setting.Database.Schema)); err != nil {
+ if _, err = db.Exec("CREATE SCHEMA " + setting.Database.Schema); err != nil {
log.Fatal("db.Exec: CREATE SCHEMA: %v", err)
}
}
diff --git a/tools/generate-svg-vscode-extensions.json b/tools/generate-svg-vscode-extensions.json
new file mode 100644
index 0000000000..b258d3f1a5
--- /dev/null
+++ b/tools/generate-svg-vscode-extensions.json
@@ -0,0 +1,570 @@
+{
+ "pkg:bat": {
+ "bat": [
+ ".bat",
+ ".cmd"
+ ]
+ },
+ "pkg:clojure": {
+ "clojure": [
+ ".clj",
+ ".cljs",
+ ".cljc",
+ ".cljx",
+ ".clojure",
+ ".edn"
+ ]
+ },
+ "pkg:coffeescript": {
+ "coffeescript": [
+ ".coffee",
+ ".cson",
+ ".iced"
+ ]
+ },
+ "pkg:configuration-editing": {
+ "jsonc": [
+ ".code-workspace",
+ "language-configuration.json",
+ "icon-theme.json",
+ "color-theme.json"
+ ],
+ "json": [
+ ".code-profile"
+ ]
+ },
+ "pkg:cpp": {
+ "c": [
+ ".c",
+ ".i"
+ ],
+ "cpp": [
+ ".cpp",
+ ".cppm",
+ ".cc",
+ ".ccm",
+ ".cxx",
+ ".cxxm",
+ ".c++",
+ ".c++m",
+ ".hpp",
+ ".hh",
+ ".hxx",
+ ".h++",
+ ".h",
+ ".ii",
+ ".ino",
+ ".inl",
+ ".ipp",
+ ".ixx",
+ ".tpp",
+ ".txx",
+ ".hpp.in",
+ ".h.in"
+ ],
+ "cuda-cpp": [
+ ".cu",
+ ".cuh"
+ ]
+ },
+ "pkg:csharp": {
+ "csharp": [
+ ".cs",
+ ".csx",
+ ".cake"
+ ]
+ },
+ "pkg:css": {
+ "css": [
+ ".css"
+ ]
+ },
+ "pkg:dart": {
+ "dart": [
+ ".dart"
+ ]
+ },
+ "pkg:diff": {
+ "diff": [
+ ".diff",
+ ".patch",
+ ".rej"
+ ]
+ },
+ "pkg:docker": {
+ "dockerfile": [
+ ".dockerfile",
+ ".containerfile"
+ ]
+ },
+ "pkg:fsharp": {
+ "fsharp": [
+ ".fs",
+ ".fsi",
+ ".fsx",
+ ".fsscript"
+ ]
+ },
+ "pkg:git-base": {
+ "ignore": [
+ ".gitignore_global",
+ ".gitignore",
+ ".git-blame-ignore-revs"
+ ]
+ },
+ "pkg:go": {
+ "go": [
+ ".go"
+ ]
+ },
+ "pkg:groovy": {
+ "groovy": [
+ ".groovy",
+ ".gvy",
+ ".gradle",
+ ".jenkinsfile",
+ ".nf"
+ ]
+ },
+ "pkg:handlebars": {
+ "handlebars": [
+ ".handlebars",
+ ".hbs",
+ ".hjs"
+ ]
+ },
+ "pkg:hlsl": {
+ "hlsl": [
+ ".hlsl",
+ ".hlsli",
+ ".fx",
+ ".fxh",
+ ".vsh",
+ ".psh",
+ ".cginc",
+ ".compute"
+ ]
+ },
+ "pkg:html": {
+ "html": [
+ ".html",
+ ".htm",
+ ".shtml",
+ ".xhtml",
+ ".xht",
+ ".mdoc",
+ ".jsp",
+ ".asp",
+ ".aspx",
+ ".jshtm",
+ ".volt",
+ ".ejs",
+ ".rhtml"
+ ]
+ },
+ "pkg:ini": {
+ "ini": [
+ ".ini"
+ ],
+ "properties": [
+ ".conf",
+ ".properties",
+ ".cfg",
+ ".directory",
+ ".gitattributes",
+ ".gitconfig",
+ ".gitmodules",
+ ".editorconfig",
+ ".repo"
+ ]
+ },
+ "pkg:java": {
+ "java": [
+ ".java",
+ ".jav"
+ ]
+ },
+ "pkg:javascript": {
+ "javascriptreact": [
+ ".jsx"
+ ],
+ "javascript": [
+ ".js",
+ ".es6",
+ ".mjs",
+ ".cjs",
+ ".pac"
+ ]
+ },
+ "pkg:json": {
+ "json": [
+ ".json",
+ ".bowerrc",
+ ".jscsrc",
+ ".webmanifest",
+ ".js.map",
+ ".css.map",
+ ".ts.map",
+ ".har",
+ ".jslintrc",
+ ".jsonld",
+ ".geojson",
+ ".ipynb",
+ ".vuerc"
+ ],
+ "jsonc": [
+ ".jsonc",
+ ".eslintrc",
+ ".eslintrc.json",
+ ".jsfmtrc",
+ ".jshintrc",
+ ".swcrc",
+ ".hintrc",
+ ".babelrc"
+ ],
+ "jsonl": [
+ ".jsonl",
+ ".ndjson"
+ ],
+ "snippets": [
+ ".code-snippets"
+ ]
+ },
+ "pkg:julia": {
+ "julia": [
+ ".jl"
+ ],
+ "juliamarkdown": [
+ ".jmd"
+ ]
+ },
+ "pkg:latex": {
+ "tex": [
+ ".sty",
+ ".cls",
+ ".bbx",
+ ".cbx"
+ ],
+ "latex": [
+ ".tex",
+ ".ltx",
+ ".ctx"
+ ],
+ "bibtex": [
+ ".bib"
+ ]
+ },
+ "pkg:less": {
+ "less": [
+ ".less"
+ ]
+ },
+ "pkg:log": {
+ "log": [
+ ".log",
+ "*.log.?"
+ ]
+ },
+ "pkg:lua": {
+ "lua": [
+ ".lua"
+ ]
+ },
+ "pkg:make": {
+ "makefile": [
+ ".mak",
+ ".mk"
+ ]
+ },
+ "pkg:markdown-basics": {
+ "markdown": [
+ ".md",
+ ".mkd",
+ ".mdwn",
+ ".mdown",
+ ".markdown",
+ ".markdn",
+ ".mdtxt",
+ ".mdtext",
+ ".workbook"
+ ]
+ },
+ "pkg:ms-vscode.js-debug": {
+ "wat": [
+ ".wat",
+ ".wasm"
+ ]
+ },
+ "pkg:npm": {
+ "ignore": [
+ ".npmignore"
+ ],
+ "properties": [
+ ".npmrc"
+ ]
+ },
+ "pkg:objective-c": {
+ "objective-c": [
+ ".m"
+ ],
+ "objective-cpp": [
+ ".mm"
+ ]
+ },
+ "pkg:perl": {
+ "perl": [
+ ".pl",
+ ".pm",
+ ".pod",
+ ".t",
+ ".PL",
+ ".psgi"
+ ],
+ "raku": [
+ ".raku",
+ ".rakumod",
+ ".rakutest",
+ ".rakudoc",
+ ".nqp",
+ ".p6",
+ ".pl6",
+ ".pm6"
+ ]
+ },
+ "pkg:php": {
+ "php": [
+ ".php",
+ ".php4",
+ ".php5",
+ ".phtml",
+ ".ctp"
+ ]
+ },
+ "pkg:powershell": {
+ "powershell": [
+ ".ps1",
+ ".psm1",
+ ".psd1",
+ ".pssc",
+ ".psrc"
+ ]
+ },
+ "pkg:pug": {
+ "jade": [
+ ".pug",
+ ".jade"
+ ]
+ },
+ "pkg:python": {
+ "python": [
+ ".py",
+ ".rpy",
+ ".pyw",
+ ".cpy",
+ ".gyp",
+ ".gypi",
+ ".pyi",
+ ".ipy",
+ ".pyt"
+ ]
+ },
+ "pkg:r": {
+ "r": [
+ ".r",
+ ".rhistory",
+ ".rprofile",
+ ".rt"
+ ]
+ },
+ "pkg:razor": {
+ "razor": [
+ ".cshtml",
+ ".razor"
+ ]
+ },
+ "pkg:restructuredtext": {
+ "restructuredtext": [
+ ".rst"
+ ]
+ },
+ "pkg:ruby": {
+ "ruby": [
+ ".rb",
+ ".rbx",
+ ".rjs",
+ ".gemspec",
+ ".rake",
+ ".ru",
+ ".erb",
+ ".podspec",
+ ".rbi"
+ ]
+ },
+ "pkg:rust": {
+ "rust": [
+ ".rs"
+ ]
+ },
+ "pkg:scss": {
+ "scss": [
+ ".scss"
+ ]
+ },
+ "pkg:search-result": {
+ "search-result": [
+ ".code-search"
+ ]
+ },
+ "pkg:shaderlab": {
+ "shaderlab": [
+ ".shader"
+ ]
+ },
+ "pkg:shellscript": {
+ "shellscript": [
+ ".sh",
+ ".bash",
+ ".bashrc",
+ ".bash_aliases",
+ ".bash_profile",
+ ".bash_login",
+ ".ebuild",
+ ".eclass",
+ ".profile",
+ ".bash_logout",
+ ".xprofile",
+ ".xsession",
+ ".xsessionrc",
+ ".Xsession",
+ ".zsh",
+ ".zshrc",
+ ".zprofile",
+ ".zlogin",
+ ".zlogout",
+ ".zshenv",
+ ".zsh-theme",
+ ".fish",
+ ".ksh",
+ ".csh",
+ ".cshrc",
+ ".tcshrc",
+ ".yashrc",
+ ".yash_profile"
+ ]
+ },
+ "pkg:sql": {
+ "sql": [
+ ".sql",
+ ".dsql"
+ ]
+ },
+ "pkg:swift": {
+ "swift": [
+ ".swift"
+ ]
+ },
+ "pkg:typescript-basics": {
+ "typescript": [
+ ".ts",
+ ".cts",
+ ".mts"
+ ],
+ "typescriptreact": [
+ ".tsx"
+ ],
+ "json": [
+ ".tsbuildinfo"
+ ]
+ },
+ "pkg:vb": {
+ "vb": [
+ ".vb",
+ ".brs",
+ ".vbs",
+ ".bas",
+ ".vba"
+ ]
+ },
+ "pkg:xml": {
+ "xml": [
+ ".xml",
+ ".xsd",
+ ".ascx",
+ ".atom",
+ ".axml",
+ ".axaml",
+ ".bpmn",
+ ".cpt",
+ ".csl",
+ ".csproj",
+ ".csproj.user",
+ ".dita",
+ ".ditamap",
+ ".dtd",
+ ".ent",
+ ".mod",
+ ".dtml",
+ ".fsproj",
+ ".fxml",
+ ".iml",
+ ".isml",
+ ".jmx",
+ ".launch",
+ ".menu",
+ ".mxml",
+ ".nuspec",
+ ".opml",
+ ".owl",
+ ".proj",
+ ".props",
+ ".pt",
+ ".publishsettings",
+ ".pubxml",
+ ".pubxml.user",
+ ".rbxlx",
+ ".rbxmx",
+ ".rdf",
+ ".rng",
+ ".rss",
+ ".shproj",
+ ".storyboard",
+ ".svg",
+ ".targets",
+ ".tld",
+ ".tmx",
+ ".vbproj",
+ ".vbproj.user",
+ ".vcxproj",
+ ".vcxproj.filters",
+ ".wsdl",
+ ".wxi",
+ ".wxl",
+ ".wxs",
+ ".xaml",
+ ".xbl",
+ ".xib",
+ ".xlf",
+ ".xliff",
+ ".xpdl",
+ ".xul",
+ ".xoml"
+ ],
+ "xsl": [
+ ".xsl",
+ ".xslt"
+ ]
+ },
+ "pkg:yaml": {
+ "yaml": [
+ ".yaml",
+ ".yml",
+ ".eyaml",
+ ".eyml",
+ ".cff",
+ ".yaml-tmlanguage",
+ ".yaml-tmpreferences",
+ ".yaml-tmtheme",
+ ".winget"
+ ]
+ }
+}
diff --git a/tools/generate-svg.js b/tools/generate-svg.js
index f744162099..12f3db039d 100755
--- a/tools/generate-svg.js
+++ b/tools/generate-svg.js
@@ -5,33 +5,27 @@ import {parse} from 'node:path';
import {readFile, writeFile, mkdir} from 'node:fs/promises';
import {fileURLToPath} from 'node:url';
import {exit} from 'node:process';
+import * as fs from 'node:fs';
const glob = (pattern) => fastGlob.sync(pattern, {
cwd: fileURLToPath(new URL('..', import.meta.url)),
absolute: true,
});
-function doExit(err) {
- if (err) console.error(err);
- exit(err ? 1 : 0);
-}
-
-async function processFile(file, {prefix, fullName} = {}) {
- let name;
- if (fullName) {
- name = fullName;
- } else {
+async function processAssetsSvgFile(file, {prefix, fullName} = {}) {
+ let name = fullName;
+ if (!name) {
name = parse(file).name;
if (prefix) name = `${prefix}-${name}`;
if (prefix === 'octicon') name = name.replace(/-[0-9]+$/, ''); // chop of '-16' on octicons
}
-
// Set the `xmlns` attribute so that the files are displayable in standalone documents
// The svg backend module will strip the attribute during startup for inline display
const {data} = optimize(await readFile(file, 'utf8'), {
plugins: [
{name: 'preset-default'},
{name: 'removeDimensions'},
+ {name: 'removeTitle'},
{name: 'prefixIds', params: {prefix: () => name}},
{name: 'addClassesToSVGElement', params: {classNames: ['svg', name]}},
{
@@ -44,28 +38,75 @@ async function processFile(file, {prefix, fullName} = {}) {
},
],
});
-
await writeFile(fileURLToPath(new URL(`../public/assets/img/svg/${name}.svg`, import.meta.url)), data);
}
-function processFiles(pattern, opts) {
- return glob(pattern).map((file) => processFile(file, opts));
+function processAssetsSvgFiles(pattern, opts) {
+ return glob(pattern).map((file) => processAssetsSvgFile(file, opts));
}
-async function main() {
- try {
- await mkdir(fileURLToPath(new URL('../public/assets/img/svg', import.meta.url)), {recursive: true});
- } catch {}
+async function processMaterialFileIcons() {
+ const files = glob('node_modules/material-icon-theme/icons/*.svg');
+ const svgSymbols = {};
+ for (const file of files) {
+ // remove all unnecessary attributes, only keep "viewBox"
+ const {data} = optimize(await readFile(file, 'utf8'), {
+ plugins: [
+ {name: 'preset-default'},
+ {name: 'removeDimensions'},
+ {name: 'removeXMLNS'},
+ {name: 'removeAttrs', params: {attrs: 'xml:space', elemSeparator: ','}},
+ ],
+ });
+ const svgName = parse(file).name;
+ // intentionally use single quote here to avoid escaping
+ svgSymbols[svgName] = data.replace(/"/g, `'`);
+ }
+ fs.writeFileSync(fileURLToPath(new URL(`../options/fileicon/material-icon-svgs.json`, import.meta.url)), JSON.stringify(svgSymbols, null, 2));
+ const vscodeExtensionsJson = await readFile(fileURLToPath(new URL(`generate-svg-vscode-extensions.json`, import.meta.url)));
+ const vscodeExtensions = JSON.parse(vscodeExtensionsJson);
+ const iconRulesJson = await readFile(fileURLToPath(new URL(`../node_modules/material-icon-theme/dist/material-icons.json`, import.meta.url)));
+ const iconRules = JSON.parse(iconRulesJson);
+ // The rules are from VSCode material-icon-theme, we need to adjust them to our needs
+ // 1. We only use lowercase filenames to match (it should be good enough for most cases and more efficient)
+ // 2. We do not have a "Language ID" system:
+ // * https://code.visualstudio.com/docs/languages/identifiers#_known-language-identifiers
+ // * https://github.com/microsoft/vscode/tree/1.98.0/extensions
+ delete iconRules.iconDefinitions;
+ for (const [k, v] of Object.entries(iconRules.fileNames)) iconRules.fileNames[k.toLowerCase()] = v;
+ for (const [k, v] of Object.entries(iconRules.folderNames)) iconRules.folderNames[k.toLowerCase()] = v;
+ for (const [k, v] of Object.entries(iconRules.fileExtensions)) iconRules.fileExtensions[k.toLowerCase()] = v;
+ // Use VSCode's "Language ID" mapping from its extensions
+ for (const [_, langIdExtMap] of Object.entries(vscodeExtensions)) {
+ for (const [langId, names] of Object.entries(langIdExtMap)) {
+ for (const name of names) {
+ const nameLower = name.toLowerCase();
+ if (nameLower[0] === '.') {
+ iconRules.fileExtensions[nameLower.substring(1)] ??= langId;
+ } else {
+ iconRules.fileNames[nameLower] ??= langId;
+ }
+ }
+ }
+ }
+ const iconRulesPretty = JSON.stringify(iconRules, null, 2);
+ fs.writeFileSync(fileURLToPath(new URL(`../options/fileicon/material-icon-rules.json`, import.meta.url)), iconRulesPretty);
+}
+
+async function main() {
+ await mkdir(fileURLToPath(new URL('../public/assets/img/svg', import.meta.url)), {recursive: true});
await Promise.all([
- ...processFiles('node_modules/@primer/octicons/build/svg/*-16.svg', {prefix: 'octicon'}),
- ...processFiles('web_src/svg/*.svg'),
- ...processFiles('public/assets/img/gitea.svg', {fullName: 'gitea-gitea'}),
+ ...processAssetsSvgFiles('node_modules/@primer/octicons/build/svg/*-16.svg', {prefix: 'octicon'}),
+ ...processAssetsSvgFiles('web_src/svg/*.svg'),
+ ...processAssetsSvgFiles('public/assets/img/gitea.svg', {fullName: 'gitea-gitea'}),
+ processMaterialFileIcons(),
]);
}
try {
- doExit(await main());
+ await main();
} catch (err) {
- doExit(err);
+ console.error(err);
+ exit(1);
}
diff --git a/tools/lint-go-gopls.sh b/tools/lint-go-gopls.sh
index 4bb69f4c16..2cd26ca6fe 100755
--- a/tools/lint-go-gopls.sh
+++ b/tools/lint-go-gopls.sh
@@ -8,10 +8,10 @@ IGNORE_PATTERNS=(
)
# lint all go files with 'gopls check' and look for lines starting with the
-# current absolute path, indicating a error was found. This is neccessary
+# current absolute path, indicating a error was found. This is necessary
# because the tool does not set non-zero exit code when errors are found.
# ref: https://github.com/golang/go/issues/67078
-ERROR_LINES=$("$GO" run "$GOPLS_PACKAGE" check "$@" 2>/dev/null | grep -E "^$PWD" | grep -vFf <(printf '%s\n' "${IGNORE_PATTERNS[@]}"));
+ERROR_LINES=$("$GO" run "$GOPLS_PACKAGE" check -severity=warning "$@" 2>/dev/null | grep -E "^$PWD" | grep -vFf <(printf '%s\n' "${IGNORE_PATTERNS[@]}"));
NUM_ERRORS=$(echo -n "$ERROR_LINES" | wc -l)
if [ "$NUM_ERRORS" -eq "0" ]; then
diff --git a/tsconfig.json b/tsconfig.json
index d32cca0aaa..4dcee6666f 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,9 +1,13 @@
{
"include": [
- "*",
- "tests/e2e/**/*",
- "tools/**/*",
- "web_src/js/**/*",
+ "${configDir}/.*",
+ "${configDir}/*",
+ "${configDir}/tests/e2e/**/*",
+ "${configDir}/tests/e2e/**/.*",
+ "${configDir}/tools/**/*",
+ "${configDir}/tools/**/.*",
+ "${configDir}/web_src/js/**/*",
+ "${configDir}/web_src/js/**/.*",
],
"compilerOptions": {
"target": "es2020",
@@ -14,15 +18,21 @@
"allowJs": true,
"allowSyntheticDefaultImports": true,
"alwaysStrict": true,
+ "erasableSyntaxOnly": true,
"esModuleInterop": true,
"isolatedModules": true,
+ "libReplacement": false,
"noEmit": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"verbatimModuleSyntax": true,
"stripInternal": true,
"strict": false,
+ "strictBindCallApply": true,
+ "strictBuiltinIteratorReturn": true,
"strictFunctionTypes": true,
+ "noImplicitAny": true,
+ "noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noPropertyAccessFromIndexSignature": false,
diff --git a/updates.config.js b/updates.config.js
index a4a2fa5228..4373ab9b95 100644
--- a/updates.config.js
+++ b/updates.config.js
@@ -1,9 +1,14 @@
export default {
exclude: [
'@mcaptcha/vanilla-glue', // breaking changes in rc versions need to be handled
+ '@stylistic/eslint-plugin-js', // need to migrate to eslint 9
+ 'cropperjs', // need to migrate to v2 but v2 is not compatible with v1
'eslint', // need to migrate to eslint flat config first
'eslint-plugin-array-func', // need to migrate to eslint flat config first
+ 'eslint-plugin-github', // need to migrate to eslint 9 - https://github.com/github/eslint-plugin-github/issues/585
'eslint-plugin-no-use-extend-native', // need to migrate to eslint flat config first
+ 'eslint-plugin-unicorn', // need to migrate to eslint 9
'eslint-plugin-vitest', // need to migrate to eslint flat config first
+ 'tailwindcss', // need to migrate
],
};
diff --git a/web_src/css/actions.css b/web_src/css/actions.css
index 0ab09f537a..c43ebe21a0 100644
--- a/web_src/css/actions.css
+++ b/web_src/css/actions.css
@@ -6,14 +6,6 @@
overflow-x: auto;
}
-.runner-container .runner-ops > a {
- margin-left: 0.5em;
-}
-
-.runner-container .runner-ops-delete {
- color: var(--color-red-light);
-}
-
.runner-container .runner-new-text {
color: var(--color-white);
}
@@ -67,6 +59,7 @@
.run-list-ref {
display: inline-block !important;
+ max-width: 105px;
}
@media (max-width: 767.98px) {
diff --git a/web_src/css/base.css b/web_src/css/base.css
index d6ab5e1009..529ddd5386 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -2,7 +2,10 @@
/* fonts */
--fonts-proportional: -apple-system, "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial;
--fonts-monospace: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace, var(--fonts-emoji);
- --fonts-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla";
+ /* GitHub explicitly sets font names like: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla";
+ Actually "Twemoji Mozilla" emoji font is widely used by browsers like Firefox, Pale Moon, and it is more likely up-to-dated than the system emoji font.
+ So not setting emoji font seems to be the best choice, here we just use a non-existing dummy font name and let browsers choose. */
+ --fonts-emoji: -emoji-fallback;
/* font weights - use between 400 and 600 for general purposes. Avoid 700 as it is perceived too bold */
--font-weight-light: 300;
--font-weight-normal: 400;
@@ -26,6 +29,16 @@
--checkbox-size: 15px; /* height and width of checkbox and radio inputs */
--page-spacing: 16px; /* space between page elements */
--page-margin-x: 32px; /* minimum space on left and right side of page */
+ --page-space-bottom: 64px; /* space between last page element and footer */
+
+ /* z-index */
+ --z-index-modal: 1001; /* modal dialog, hard-coded from Fomantic modal.css */
+ --z-index-toast: 1002; /* should be larger than modal */
+
+ --font-size-label: 12px; /* font size of individual labels */
+
+ --gap-inline: 0.25rem; /* gap for inline texts and elements, for example: the spaces for sentence with labels, button text, etc */
+ --gap-block: 0.25rem; /* gap for element blocks, for example: spaces between buttons, menu image & title, header icon & title etc */
}
@media (min-width: 768px) and (max-width: 1200px) {
@@ -224,6 +237,7 @@ progress::-moz-progress-bar {
}
.unselectable,
+.btn,
.button,
.lines-num,
.lines-commit,
@@ -313,6 +327,16 @@ a.label,
background: var(--color-hover);
}
+.empty-placeholder {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding-top: 40px;
+ padding-bottom: 40px;
+ text-align: center;
+ text-wrap: balance;
+}
+
.inline-code-block {
padding: 2px 4px;
border-radius: .24em;
@@ -336,12 +360,18 @@ a.label,
border-color: var(--color-secondary);
}
+.ui.dropdown .menu > .header {
+ text-transform: none; /* reset fomantic's "uppercase" */
+}
+
.ui.dropdown .menu > .header:not(.ui) {
color: var(--color-text);
+ font-size: 0.95em; /* reset fomantic's small font-size */
}
.ui.dropdown .menu > .item {
color: var(--color-text);
+ line-height: var(--line-height-default);
}
.ui.dropdown .menu > .item:hover {
@@ -400,11 +430,6 @@ a.label,
color: var(--color-text-light-2);
}
-.ui.form textarea:not([rows]) {
- height: var(--min-height-textarea); /* override fomantic default 12em */
- min-height: var(--min-height-textarea); /* override fomantic default 8em */
-}
-
/* styles from removed fomantic transition module */
.hidden.transition {
visibility: hidden;
@@ -474,7 +499,7 @@ img.ui.avatar,
.full.height {
flex-grow: 1;
- padding-bottom: 80px;
+ padding-bottom: var(--page-space-bottom);
}
.status-page-error {
@@ -506,97 +531,6 @@ img.ui.avatar,
margin-top: calc(var(--page-spacing) - 1rem);
}
-.ui.form .fields.error .field textarea,
-.ui.form .fields.error .field select,
-.ui.form .fields.error .field input:not([type]),
-.ui.form .fields.error .field input[type="date"],
-.ui.form .fields.error .field input[type="datetime-local"],
-.ui.form .fields.error .field input[type="email"],
-.ui.form .fields.error .field input[type="number"],
-.ui.form .fields.error .field input[type="password"],
-.ui.form .fields.error .field input[type="search"],
-.ui.form .fields.error .field input[type="tel"],
-.ui.form .fields.error .field input[type="time"],
-.ui.form .fields.error .field input[type="text"],
-.ui.form .fields.error .field input[type="file"],
-.ui.form .fields.error .field input[type="url"],
-.ui.form .fields.error .field .ui.dropdown,
-.ui.form .fields.error .field .ui.dropdown .item,
-.ui.form .field.error .ui.dropdown,
-.ui.form .field.error .ui.dropdown .text,
-.ui.form .field.error .ui.dropdown .item,
-.ui.form .field.error textarea,
-.ui.form .field.error select,
-.ui.form .field.error input:not([type]),
-.ui.form .field.error input[type="date"],
-.ui.form .field.error input[type="datetime-local"],
-.ui.form .field.error input[type="email"],
-.ui.form .field.error input[type="number"],
-.ui.form .field.error input[type="password"],
-.ui.form .field.error input[type="search"],
-.ui.form .field.error input[type="tel"],
-.ui.form .field.error input[type="time"],
-.ui.form .field.error input[type="text"],
-.ui.form .field.error input[type="file"],
-.ui.form .field.error input[type="url"],
-.ui.form .field.error select:focus,
-.ui.form .field.error input:not([type]):focus,
-.ui.form .field.error input[type="date"]:focus,
-.ui.form .field.error input[type="datetime-local"]:focus,
-.ui.form .field.error input[type="email"]:focus,
-.ui.form .field.error input[type="number"]:focus,
-.ui.form .field.error input[type="password"]:focus,
-.ui.form .field.error input[type="search"]:focus,
-.ui.form .field.error input[type="tel"]:focus,
-.ui.form .field.error input[type="time"]:focus,
-.ui.form .field.error input[type="text"]:focus,
-.ui.form .field.error input[type="file"]:focus,
-.ui.form .field.error input[type="url"]:focus {
- background-color: var(--color-error-bg);
- border-color: var(--color-error-border);
- color: var(--color-error-text);
-}
-
-.ui.form .fields.error .field .ui.dropdown,
-.ui.form .field.error .ui.dropdown,
-.ui.form .fields.error .field .ui.dropdown:hover,
-.ui.form .field.error .ui.dropdown:hover {
- border-color: var(--color-error-border) !important;
-}
-
-.ui.form .fields.error .field .ui.dropdown .menu .item:hover,
-.ui.form .field.error .ui.dropdown .menu .item:hover {
- background-color: var(--color-error-bg-hover);
-}
-
-.ui.form .fields.error .field .ui.dropdown .menu .active.item,
-.ui.form .field.error .ui.dropdown .menu .active.item {
- background-color: var(--color-error-bg-active) !important;
-}
-
-.ui.form .fields.error .dropdown .menu,
-.ui.form .field.error .dropdown .menu {
- border-color: var(--color-error-border) !important;
-}
-
-input:-webkit-autofill,
-input:-webkit-autofill:focus,
-input:-webkit-autofill:hover,
-input:-webkit-autofill:active,
-.ui.form .field.field input:-webkit-autofill,
-.ui.form .field.field input:-webkit-autofill:focus,
-.ui.form .field.field input:-webkit-autofill:hover,
-.ui.form .field.field input:-webkit-autofill:active {
- -webkit-background-clip: text;
- -webkit-text-fill-color: var(--color-text);
- box-shadow: 0 0 0 100px var(--color-primary-light-6) inset !important;
- border-color: var(--color-primary-light-4) !important;
-}
-
-.ui.form .field.muted {
- opacity: var(--opacity-disabled);
-}
-
.text.primary {
color: var(--color-primary) !important;
}
@@ -613,38 +547,18 @@ input:-webkit-autofill:active,
color: var(--color-yellow) !important;
}
-.text.olive {
- color: var(--color-olive) !important;
-}
-
.text.green {
color: var(--color-green) !important;
}
-.text.teal {
- color: var(--color-teal) !important;
-}
-
.text.blue {
color: var(--color-blue) !important;
}
-.text.violet {
- color: var(--color-violet) !important;
-}
-
.text.purple {
color: var(--color-purple) !important;
}
-.text.pink {
- color: var(--color-pink) !important;
-}
-
-.text.brown {
- color: var(--color-brown) !important;
-}
-
.text.black {
color: var(--color-text) !important;
}
@@ -691,18 +605,6 @@ input:-webkit-autofill:active,
box-shadow: 0 6px 18px var(--color-shadow) !important;
}
-.ui.dropdown .menu > .header {
- font-size: 0.8em;
-}
-
-.ui .text.left {
- text-align: left !important;
-}
-
-.ui .text.right {
- text-align: right !important;
-}
-
.ui .text.truncate {
overflow-x: hidden;
text-overflow: ellipsis;
@@ -726,18 +628,6 @@ input:-webkit-autofill:active,
vertical-align: middle;
}
-.ui .form .autofill-dummy {
- position: absolute;
- width: 1px;
- height: 1px;
- overflow: hidden;
- z-index: -10000;
-}
-
-.ui .form .sub.field {
- margin-left: 25px;
-}
-
.ui .sha.label {
font-family: var(--fonts-monospace);
font-size: 13px;
@@ -765,46 +655,6 @@ input:-webkit-autofill:active,
font-weight: var(--font-weight-normal);
}
-.ui .background.red {
- background-color: var(--color-red) !important;
-}
-
-.ui .background.blue {
- background-color: var(--color-blue) !important;
-}
-
-.ui .background.black {
- background-color: var(--color-black) !important;
-}
-
-.ui .background.grey {
- background-color: var(--color-grey) !important;
-}
-
-.ui .background.light.grey {
- background-color: var(--color-grey) !important;
-}
-
-.ui .background.green {
- background-color: var(--color-green) !important;
-}
-
-.ui .background.purple {
- background-color: var(--color-purple) !important;
-}
-
-.ui .background.yellow {
- background-color: var(--color-yellow) !important;
-}
-
-.ui .background.orange {
- background-color: var(--color-orange) !important;
-}
-
-.ui .background.gold {
- background-color: var(--color-gold) !important;
-}
-
.ui .migrate {
color: var(--color-text-light-2) !important;
}
@@ -821,50 +671,11 @@ input:-webkit-autofill:active,
border: 1px solid;
}
-.ui .border.red {
- border-color: var(--color-red) !important;
-}
-
-.ui .border.blue {
- border-color: var(--color-blue) !important;
-}
-
-.ui .border.black {
- border-color: var(--color-black) !important;
-}
-
-.ui .border.grey {
- border-color: var(--color-grey) !important;
-}
-
-.ui .border.light.grey {
- border-color: var(--color-grey) !important;
-}
-
-.ui .border.green {
- border-color: var(--color-green) !important;
-}
-
-.ui .border.purple {
- border-color: var(--color-purple) !important;
-}
-
-.ui .border.yellow {
- border-color: var(--color-yellow) !important;
-}
-
-.ui .border.orange {
- border-color: var(--color-orange) !important;
-}
-
-.ui .border.gold {
- border-color: var(--color-gold) !important;
-}
-
-.ui.floating.dropdown .overflow.menu .scrolling.menu.items {
+.ui.dropdown .menu.context-user-switch .scrolling.menu {
border-radius: 0 !important;
box-shadow: none !important;
border-bottom: 1px solid var(--color-secondary);
+ max-width: 80vw;
}
.user-menu > .item {
@@ -960,6 +771,14 @@ overflow-menu .overflow-menu-button {
padding: 0;
}
+/* match the styles of ".ui.secondary.pointing.menu .active.item" */
+overflow-menu.ui.secondary.pointing.menu .overflow-menu-button.active {
+ padding: 2px 0 0;
+ border-bottom: 2px solid currentcolor;
+ background-color: transparent;
+ font-weight: var(--font-weight-medium);
+}
+
overflow-menu .overflow-menu-button:hover {
color: var(--color-text-dark);
}
@@ -985,22 +804,6 @@ overflow-menu .ui.label {
margin-top: 3em !important;
}
-/* multiple radio or checkboxes as inline element */
-.inline-grouped-list {
- display: inline-block;
- vertical-align: top;
-}
-
-.inline-grouped-list > .ui {
- display: block;
- margin-top: 5px;
- margin-bottom: 10px;
-}
-
-.inline-grouped-list > .ui:first-child {
- margin-top: 1px;
-}
-
.lines-blame-btn {
padding: 0 0 0 5px;
display: flex;
@@ -1031,10 +834,6 @@ overflow-menu .ui.label {
display: block;
}
-.code-view .lines-num span::after {
- cursor: pointer;
-}
-
.lines-type-marker {
vertical-align: top;
white-space: nowrap;
@@ -1071,43 +870,16 @@ overflow-menu .ui.label {
.lines-escape {
width: 0;
white-space: nowrap;
+ padding: 0;
}
.lines-code {
padding-left: 5px;
}
-.file-view tr.active {
- color: inherit !important;
- background: inherit !important;
-}
-
-.file-view tr.active .lines-num,
-.file-view tr.active .lines-code {
- background: var(--color-highlight-bg) !important;
-}
-
-.file-view tr.active:last-of-type .lines-code {
- border-bottom-right-radius: var(--border-radius);
-}
-
-.file-view tr.active .lines-num {
- position: relative;
-}
-
-.file-view tr.active .lines-num::before {
- content: "";
- position: absolute;
- left: 0;
- width: 2px;
- height: 100%;
- background: var(--color-highlight-fg);
-}
-
.code-inner {
font: 12px var(--fonts-monospace);
white-space: pre-wrap;
- word-break: break-all;
overflow-wrap: anywhere;
line-height: inherit; /* needed for inline code preview in markup */
}
@@ -1155,12 +927,12 @@ overflow-menu .ui.label {
margin-right: 4px;
}
-.top-line-blame {
+tr.top-line-blame {
border-top: 1px solid var(--color-secondary);
}
-.code-view tr.top-line-blame:first-of-type {
- border-top: none;
+tr.top-line-blame:first-of-type {
+ border-top: none; /* merge code lines belonging to the same commit into one block */
}
.lines-code .bottom-line,
@@ -1168,15 +940,6 @@ overflow-menu .ui.label {
border-bottom: 1px solid var(--color-secondary);
}
-.code-view {
- background: var(--color-code-bg);
- border-radius: var(--border-radius);
-}
-
-.code-view table {
- width: 100%;
-}
-
.migrate .svg.gitea-git {
color: var(--color-git);
}
@@ -1220,14 +983,7 @@ table th[data-sortt-desc] .svg {
box-shadow: 0 0 0 1px var(--color-secondary) inset;
}
-.emoji {
- font-size: 1.25em;
- line-height: var(--line-height-default);
- font-style: normal !important;
- font-weight: var(--font-weight-normal) !important;
- vertical-align: -0.075em;
-}
-
+/* for "image" emojis like ":git:" ":gitea:" and ":github:" (see CUSTOM_EMOJIS config option) */
.emoji img {
border-width: 0 !important;
margin: 0 !important;
@@ -1266,26 +1022,13 @@ table th[data-sortt-desc] .svg {
text-align: left;
}
-.truncated-item-container {
- display: flex !important;
- align-items: center;
-}
-
-.ellipsis-button {
- padding: 0 5px 8px !important;
- display: inline-block !important;
- font-weight: var(--font-weight-semibold) !important;
- line-height: 6px !important;
- vertical-align: middle !important;
-}
-
-.truncated-item-name {
- line-height: 2;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- margin-top: -0.5em;
- margin-bottom: -0.5em;
+.ui.button.ellipsis-button {
+ padding: 0 5px 8px;
+ display: inline-block;
+ font-weight: var(--font-weight-semibold);
+ line-height: 8px;
+ vertical-align: middle;
+ min-height: 0;
}
.precolors {
@@ -1355,7 +1098,7 @@ table th[data-sortt-desc] .svg {
.flex-text-inline {
display: inline-flex;
align-items: center;
- gap: .25rem;
+ gap: var(--gap-inline);
vertical-align: middle;
min-width: 0; /* make ellipsis work */
}
@@ -1378,20 +1121,27 @@ table th[data-sortt-desc] .svg {
}
.ui.list.flex-items-block > .item,
+.ui.form .field > label.flex-text-block, /* override fomantic "block" style */
.flex-items-block > .item,
.flex-text-block {
display: flex;
align-items: center;
- gap: .5rem;
+ gap: var(--gap-block);
min-width: 0;
}
+.ui.dropdown > .ui.button,
+.flex-text-block > .ui.button,
+.flex-text-inline > .ui.button {
+ margin: 0; /* fomantic buttons have default margin, when we use them in a flex container with gap, we do not need these margins */
+}
+
/* to override Fomantic's default display: block for ".menu .item", and use a slightly larger gap for menu item content
the "!important" is necessary to override Fomantic UI menu item styles, meanwhile we should keep the "hidden" items still hidden */
.ui.dropdown .menu.flex-items-menu > .item:not(.hidden, .filtered, .tw-hidden) {
display: flex !important;
align-items: center;
- gap: .5rem;
+ gap: var(--gap-block);
min-width: 0;
}
.ui.dropdown .menu.flex-items-menu > .item img,
@@ -1399,15 +1149,33 @@ the "!important" is necessary to override Fomantic UI menu item styles, meanwhil
margin: 0; /* use gap, but not margin */
}
-.ui.dropdown.ellipsis-items-nowrap > .text {
+.ui.dropdown.ellipsis-text-items {
+ /* reset y padding and use the line-height below instead, to avoid the "overflow: hidden" clips the larger image in the "text" element */
+ padding-top: 0;
+ padding-bottom: 0;
+}
+
+.ui.dropdown.ellipsis-text-items > .text {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
+ line-height: 2.71; /* matches fomantic dropdown's default min-height */
}
-.ellipsis-items-nowrap > .item,
-.ui.dropdown.ellipsis-items-nowrap .menu > .item {
+.ui.dropdown.ellipsis-text-items .menu > .item {
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
}
+
+.svg.octicon-file-directory-fill,
+.svg.octicon-file-directory-open-fill,
+.svg.octicon-file-submodule {
+ color: var(--color-primary);
+}
+
+.svg.octicon-file,
+.svg.octicon-file-symlink-file,
+.svg.octicon-file-directory-symlink {
+ color: var(--color-secondary-dark-7);
+}
diff --git a/web_src/css/editor/combomarkdowneditor.css b/web_src/css/editor/combomarkdowneditor.css
index 835286b795..046010c6c8 100644
--- a/web_src/css/editor/combomarkdowneditor.css
+++ b/web_src/css/editor/combomarkdowneditor.css
@@ -100,67 +100,3 @@
border-bottom: 1px solid var(--color-secondary);
padding-bottom: 1rem;
}
-
-text-expander {
- display: block;
- position: relative;
-}
-
-text-expander .suggestions {
- position: absolute;
- min-width: 180px;
- padding: 0;
- margin-top: 24px;
- list-style: none;
- background: var(--color-box-body);
- border-radius: var(--border-radius);
- border: 1px solid var(--color-secondary);
- box-shadow: 0 .5rem 1rem var(--color-shadow);
- z-index: 100; /* needs to be > 20 to be on top of dropzone's .dz-details */
-}
-
-text-expander .suggestions li {
- display: flex;
- align-items: center;
- cursor: pointer;
- padding: 4px 8px;
- font-weight: var(--font-weight-medium);
-}
-
-text-expander .suggestions li + li {
- border-top: 1px solid var(--color-secondary-alpha-40);
-}
-
-text-expander .suggestions li:first-child {
- border-radius: var(--border-radius) var(--border-radius) 0 0;
-}
-
-text-expander .suggestions li:last-child {
- border-radius: 0 0 var(--border-radius) var(--border-radius);
-}
-
-text-expander .suggestions li:only-child {
- border-radius: var(--border-radius);
-}
-
-text-expander .suggestions li:hover {
- background: var(--color-hover);
-}
-
-text-expander .suggestions .fullname {
- font-weight: var(--font-weight-normal);
- margin-left: 4px;
- color: var(--color-text-light-1);
-}
-
-text-expander .suggestions li[aria-selected="true"],
-text-expander .suggestions li[aria-selected="true"] span {
- background: var(--color-primary);
- color: var(--color-primary-contrast);
-}
-
-text-expander .suggestions img {
- width: 24px;
- height: 24px;
- margin-right: 8px;
-}
diff --git a/web_src/css/editor/fileeditor.css b/web_src/css/editor/fileeditor.css
index 444ee8c7e7..698efffc99 100644
--- a/web_src/css/editor/fileeditor.css
+++ b/web_src/css/editor/fileeditor.css
@@ -74,12 +74,3 @@
padding: 1rem;
text-align: center;
}
-
-.edit-diff {
- padding: 0 !important;
-}
-
-.edit-diff > div > .ui.table {
- border-top: none !important;
- border-bottom: none !important;
-}
diff --git a/web_src/css/features/cropper.css b/web_src/css/features/cropper.css
index ed7171e770..f7f8168006 100644
--- a/web_src/css/features/cropper.css
+++ b/web_src/css/features/cropper.css
@@ -1,6 +1,6 @@
@import "cropperjs/dist/cropper.css";
-.page-content.user.profile .cropper-panel .cropper-wrapper {
+.avatar-file-with-cropper + .cropper-panel .cropper-wrapper {
max-width: 400px;
max-height: 400px;
}
diff --git a/web_src/css/features/expander.css b/web_src/css/features/expander.css
new file mode 100644
index 0000000000..f560b2a9fd
--- /dev/null
+++ b/web_src/css/features/expander.css
@@ -0,0 +1,96 @@
+text-expander .suggestions,
+.tribute-container {
+ position: absolute;
+ max-height: min(300px, 95vh);
+ max-width: min(500px, 95vw);
+ overflow-x: hidden;
+ overflow-y: auto;
+ white-space: nowrap;
+ background: var(--color-menu);
+ box-shadow: 0 6px 18px var(--color-shadow);
+ border-radius: var(--border-radius);
+ border: 1px solid var(--color-secondary);
+ z-index: 100; /* needs to be > 20 to be on top of dropzone's .dz-details */
+}
+
+text-expander {
+ display: block;
+ position: relative;
+}
+
+text-expander .suggestions {
+ padding: 0;
+ margin-top: 24px;
+ list-style: none;
+}
+
+text-expander .suggestions li,
+.tribute-item {
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ gap: 6px;
+ font-weight: var(--font-weight-medium);
+}
+
+text-expander .suggestions li,
+.tribute-container li {
+ padding: 3px 6px;
+}
+
+text-expander .suggestions li + li,
+.tribute-container li + li {
+ border-top: 1px solid var(--color-secondary);
+}
+
+text-expander .suggestions li:first-child {
+ border-radius: var(--border-radius) var(--border-radius) 0 0;
+}
+
+text-expander .suggestions li:last-child {
+ border-radius: 0 0 var(--border-radius) var(--border-radius);
+}
+
+text-expander .suggestions li:only-child {
+ border-radius: var(--border-radius);
+}
+
+text-expander .suggestions .fullname,
+.tribute-container li .fullname {
+ font-weight: var(--font-weight-normal);
+ color: var(--color-text-light-1);
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+text-expander .suggestions li:hover,
+text-expander .suggestions li:hover *,
+text-expander .suggestions li[aria-selected="true"],
+text-expander .suggestions li[aria-selected="true"] *,
+.tribute-container li.highlight,
+.tribute-container li.highlight * {
+ background: var(--color-primary);
+ color: var(--color-primary-contrast);
+}
+
+text-expander .suggestions img,
+.tribute-item img {
+ width: 21px;
+ height: 21px;
+ object-fit: contain;
+ aspect-ratio: 1;
+}
+
+.tribute-container {
+ display: block;
+}
+
+.tribute-container ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.tribute-container li.no-match {
+ cursor: default;
+}
diff --git a/web_src/css/features/gitgraph.css b/web_src/css/features/gitgraph.css
index 1ed541a695..8bdafc3c99 100644
--- a/web_src/css/features/gitgraph.css
+++ b/web_src/css/features/gitgraph.css
@@ -10,14 +10,6 @@
align-items: center;
}
-#git-graph-container .color-buttons {
- margin-right: 0;
-}
-
-#git-graph-container .ui.header.dividing {
- padding-bottom: 10px;
-}
-
#git-graph-container #flow-select-refs-dropdown {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
@@ -31,25 +23,6 @@
align-items: center;
}
-#git-graph-container #flow-select-refs-dropdown .ui.label .truncate {
- display: inline-block;
- max-width: 140px;
- overflow: hidden;
- text-overflow: ellipsis;
- vertical-align: top;
- white-space: nowrap;
-}
-
-#git-graph-container #flow-select-refs-dropdown .default.text {
- padding-top: 4px;
- padding-bottom: 4px;
-}
-
-#git-graph-container #flow-select-refs-dropdown input.search {
- position: relative;
- top: 1px;
-}
-
#git-graph-container li {
list-style-type: none;
height: 24px;
@@ -57,16 +30,20 @@
white-space: nowrap;
display: flex;
align-items: center;
- gap: 0.25em;
+ gap: 0.5em;
}
-#git-graph-container li .ui.label.commit-id-short {
- padding-top: 2px;
- padding-bottom: 2px;
+#git-graph-container li > span {
+ flex-shrink: 0;
}
-#git-graph-container li .node-relation {
- font-family: var(--fonts-monospace);
+#git-graph-container li > span.message {
+ flex-shrink: 1;
+}
+
+#git-graph-container li .ui.label.commit-id-short {
+ padding: 2px 4px;
+ height: 20px;
}
#git-graph-container li .author {
@@ -78,17 +55,6 @@
font-size: 80%;
}
-#git-graph-container li a:not(.ui):hover {
- text-decoration: underline;
-}
-
-#git-graph-container li a em {
- color: var(--color-red);
- border-bottom: 1px dotted var(--color-secondary);
- text-decoration: none;
- font-style: normal;
-}
-
#git-graph-container #rel-container {
max-width: 30%;
overflow-x: auto;
@@ -105,21 +71,16 @@
width: 100%;
}
-#git-graph-container #rev-list li.highlight.hover {
- background-color: var(--color-secondary-alpha-30);
-}
-
-#git-graph-container #rev-list .commit-refs .button {
+#git-graph-container li .commit-refs .ui.button,
+#git-graph-container li .commit-refs .ui.label.tag-label {
padding: 2px 4px;
margin-right: 0.25em;
display: inline-block;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
-}
-
-#git-graph-container #graph-raw-list {
- margin: 0;
+ line-height: var(--line-height-default);
+ min-height: 0;
}
#git-graph-container.monochrome #rel-container .flow-group {
@@ -127,11 +88,6 @@
fill: var(--color-secondary-dark-5);
}
-#git-graph-container.monochrome #rel-container .flow-group.highlight {
- stroke: var(--color-secondary-dark-12);
- fill: var(--color-secondary-dark-12);
-}
-
#git-graph-container:not(.monochrome) #rel-container .flow-group.flow-color-16-1 {
stroke: #499a37;
fill: #499a37;
diff --git a/web_src/css/features/projects.css b/web_src/css/features/projects.css
index 8763d3684e..7fd5150970 100644
--- a/web_src/css/features/projects.css
+++ b/web_src/css/features/projects.css
@@ -1,43 +1,35 @@
-.board {
+#project-board {
display: flex;
+ align-items: stretch;
flex-direction: row;
flex-wrap: nowrap;
- overflow-x: auto;
- overflow-y: clip;
- align-items: stretch;
+ overflow: auto;
margin: 0 0.5em;
+ min-height: max(calc(100vh - 400px), 300px);
+ max-height: calc(100vh - 120px);
}
-.project-toolbar-right .filter.menu {
- flex-direction: row;
+.project-header {
+ padding: 0.5em 0;
flex-wrap: wrap;
}
-@media (max-width: 767.98px) {
- .project-toolbar-right .dropdown .menu {
- left: auto !important;
- right: auto !important;
- }
+.project-header h2 {
+ white-space: nowrap;
+ margin: 0;
}
.project-column {
- background-color: var(--color-project-column-bg) !important;
- border: 1px solid var(--color-secondary) !important;
- border-radius: var(--border-radius);
- margin: 0 0.5rem !important;
- padding: 0.5rem !important;
- width: 320px;
- height: initial;
- min-height: max(calc(100vh - 400px), 300px);
flex: 0 0 auto;
- overflow: visible;
display: flex;
flex-direction: column;
- cursor: default;
-}
-
-.project-column .issue-card {
- color: var(--color-text);
+ background-color: var(--color-project-column-bg);
+ border: 1px solid var(--color-secondary);
+ border-radius: var(--border-radius);
+ margin: 0 0.5rem;
+ padding: 0.5rem;
+ width: 320px;
+ overflow: visible;
}
.project-column-header {
@@ -51,16 +43,15 @@
color: inherit;
}
-.project-column > .cards {
+.project-column > .ui.cards {
flex: 1;
display: flex;
- align-content: baseline;
- margin: 0 !important;
- padding: 0 !important;
- flex-wrap: nowrap !important;
+ flex-wrap: nowrap;
flex-direction: column;
- overflow-x: clip;
+ overflow: clip auto;
gap: .25rem;
+ margin: 0;
+ padding: 0;
}
.project-column > .divider {
@@ -110,3 +101,21 @@
.card-ghost * {
opacity: 0;
}
+
+.fullscreen.projects-view {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ flex-direction: column;
+}
+
+/* Hide project-description in full-screen due to its variable height. No need to show it for better space use. */
+.fullscreen.projects-view .project-description {
+ display: none;
+}
+
+.fullscreen.projects-view #project-board {
+ flex: 1;
+ max-height: unset;
+ padding-bottom: 0.5em;
+}
diff --git a/web_src/css/features/tribute.css b/web_src/css/features/tribute.css
deleted file mode 100644
index 99a026b9bc..0000000000
--- a/web_src/css/features/tribute.css
+++ /dev/null
@@ -1,32 +0,0 @@
-@import "tributejs/dist/tribute.css";
-
-.tribute-container {
- box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.25);
- border-radius: var(--border-radius);
-}
-
-.tribute-container ul {
- margin-top: 0 !important;
- background: var(--color-body) !important;
-}
-
-.tribute-container li {
- padding: 3px 0.5rem !important;
-}
-
-.tribute-container li span.fullname {
- font-weight: var(--font-weight-normal);
- font-size: 0.8rem;
-}
-
-.tribute-container li.highlight,
-.tribute-container li:hover {
- background: var(--color-primary) !important;
- color: var(--color-primary-contrast) !important;
-}
-
-.tribute-item {
- display: flex;
- align-items: center;
- gap: 6px;
-}
diff --git a/web_src/css/form.css b/web_src/css/form.css
index a92ba354b4..c51eba1bc9 100644
--- a/web_src/css/form.css
+++ b/web_src/css/form.css
@@ -1,3 +1,111 @@
+.ui .form .autofill-dummy {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ overflow: hidden;
+ z-index: -10000;
+}
+
+.ui .form .sub.field {
+ margin-left: 25px;
+}
+
+.ui.form .fields.error .field textarea,
+.ui.form .fields.error .field select,
+.ui.form .fields.error .field input:not([type]),
+.ui.form .fields.error .field input[type="date"],
+.ui.form .fields.error .field input[type="datetime-local"],
+.ui.form .fields.error .field input[type="email"],
+.ui.form .fields.error .field input[type="number"],
+.ui.form .fields.error .field input[type="password"],
+.ui.form .fields.error .field input[type="search"],
+.ui.form .fields.error .field input[type="tel"],
+.ui.form .fields.error .field input[type="time"],
+.ui.form .fields.error .field input[type="text"],
+.ui.form .fields.error .field input[type="file"],
+.ui.form .fields.error .field input[type="url"],
+.ui.form .fields.error .field .ui.dropdown,
+.ui.form .fields.error .field .ui.dropdown .item,
+.ui.form .field.error .ui.dropdown,
+.ui.form .field.error .ui.dropdown .text,
+.ui.form .field.error .ui.dropdown .item,
+.ui.form .field.error textarea,
+.ui.form .field.error select,
+.ui.form .field.error input:not([type]),
+.ui.form .field.error input[type="date"],
+.ui.form .field.error input[type="datetime-local"],
+.ui.form .field.error input[type="email"],
+.ui.form .field.error input[type="number"],
+.ui.form .field.error input[type="password"],
+.ui.form .field.error input[type="search"],
+.ui.form .field.error input[type="tel"],
+.ui.form .field.error input[type="time"],
+.ui.form .field.error input[type="text"],
+.ui.form .field.error input[type="file"],
+.ui.form .field.error input[type="url"],
+.ui.form .field.error select:focus,
+.ui.form .field.error input:not([type]):focus,
+.ui.form .field.error input[type="date"]:focus,
+.ui.form .field.error input[type="datetime-local"]:focus,
+.ui.form .field.error input[type="email"]:focus,
+.ui.form .field.error input[type="number"]:focus,
+.ui.form .field.error input[type="password"]:focus,
+.ui.form .field.error input[type="search"]:focus,
+.ui.form .field.error input[type="tel"]:focus,
+.ui.form .field.error input[type="time"]:focus,
+.ui.form .field.error input[type="text"]:focus,
+.ui.form .field.error input[type="file"]:focus,
+.ui.form .field.error input[type="url"]:focus {
+ background-color: var(--color-error-bg);
+ border-color: var(--color-error-border);
+ color: var(--color-error-text);
+}
+
+.ui.form .fields.error .field .ui.dropdown,
+.ui.form .field.error .ui.dropdown,
+.ui.form .fields.error .field .ui.dropdown:hover,
+.ui.form .field.error .ui.dropdown:hover {
+ border-color: var(--color-error-border) !important;
+}
+
+.ui.form .fields.error .field .ui.dropdown .menu .item:hover,
+.ui.form .field.error .ui.dropdown .menu .item:hover {
+ background-color: var(--color-error-bg-hover);
+}
+
+.ui.form .fields.error .field .ui.dropdown .menu .active.item,
+.ui.form .field.error .ui.dropdown .menu .active.item {
+ background-color: var(--color-error-bg-active) !important;
+}
+
+.ui.form .fields.error .dropdown .menu,
+.ui.form .field.error .dropdown .menu {
+ border-color: var(--color-error-border) !important;
+}
+
+input:-webkit-autofill,
+input:-webkit-autofill:focus,
+input:-webkit-autofill:hover,
+input:-webkit-autofill:active,
+.ui.form .field.field input:-webkit-autofill,
+.ui.form .field.field input:-webkit-autofill:focus,
+.ui.form .field.field input:-webkit-autofill:hover,
+.ui.form .field.field input:-webkit-autofill:active {
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: var(--color-text);
+ box-shadow: 0 0 0 100px var(--color-primary-light-6) inset !important;
+ border-color: var(--color-primary-light-4) !important;
+}
+
+.ui.form .field.muted {
+ opacity: var(--opacity-disabled);
+}
+
+.ui.form textarea:not([rows]) {
+ height: var(--min-height-textarea); /* override fomantic default 12em */
+ min-height: var(--min-height-textarea); /* override fomantic default 8em */
+}
+
.ui.input textarea,
.ui.form textarea,
.ui.form input:not([type]),
@@ -38,11 +146,6 @@ textarea,
color: var(--color-input-text);
}
-/* fix fomantic small dropdown having inconsistent padding with input */
-.ui.small.selection.dropdown {
- padding: .67857143em 1.6em .67857143em 1em;
-}
-
input:hover,
textarea:hover,
.ui.input input:hover,
@@ -109,56 +212,15 @@ textarea:focus,
color: var(--color-input-text);
}
-/* match <select> padding to <input> */
-.ui.form select {
- padding: 0.67857143em 1em;
+.ui.form .field > .selection.dropdown {
+ min-width: 14em; /* matches the default min width */
}
.form .help {
color: var(--color-secondary-dark-5);
padding-bottom: 0.6em;
display: inline-block;
-}
-
-#create-page-form form {
- margin: auto;
-}
-
-#create-page-form form .ui.message {
- text-align: center;
-}
-
-@media (min-width: 768px) {
- #create-page-form form {
- width: 800px !important;
- }
- #create-page-form form .header {
- padding-left: 280px !important;
- }
- #create-page-form form .inline.field > label {
- text-align: right;
- width: 250px !important;
- word-wrap: break-word;
- }
- #create-page-form form .help {
- margin-left: 265px !important;
- }
- #create-page-form form .optional .title {
- margin-left: 250px !important;
- }
- #create-page-form form .inline.field > input,
- #create-page-form form .inline.field > textarea {
- width: 50%;
- }
-}
-
-@media (max-width: 767.98px) {
- #create-page-form form .optional .title {
- margin-left: 15px;
- }
- #create-page-form form .inline.field > label {
- display: block;
- }
+ text-wrap: balance;
}
.m-captcha-style {
@@ -182,12 +244,12 @@ textarea:focus,
height: 76px !important;
}
.m-captcha-style {
- width: 50%;
+ max-width: 450px;
}
}
@media (max-height: 575px) {
- #rc-imageselect,
+ #rc-imageselect, /* google recaptcha */
.g-recaptcha-style,
.h-captcha-style {
transform: scale(0.77);
@@ -195,295 +257,45 @@ textarea:focus,
}
}
-.user.forgot.password form,
-.user.reset.password form,
-.user.signup form {
- margin: auto;
- width: 700px !important;
-}
-
-.user.activate form .ui.message,
-.user.forgot.password form .ui.message,
-.user.reset.password form .ui.message,
-.user.link-account form .ui.message,
-.user.signin form .ui.message,
-.user.signup form .ui.message {
- text-align: center;
-}
-
-@media (min-width: 768px) {
- .user.activate form,
- .user.forgot.password form,
- .user.reset.password form,
- .user.link-account form,
- .user.signin form,
- .user.signup form {
- width: 800px !important;
- }
- .user.activate form .header,
- .user.forgot.password form .header,
- .user.reset.password form .header,
- .user.link-account form .header,
- .user.signin form .header,
- .user.signup form .header {
- padding-left: 280px !important;
- }
- .user.activate form .inline.field > label {
- text-align: right;
- width: 250px !important;
- word-wrap: break-word;
- }
- .user.activate form .help,
- .user.forgot.password form .help,
- .user.reset.password form .help,
- .user.link-account form .help,
- .user.signin form .help,
- .user.signup form .help {
- margin-left: 265px !important;
- }
- .user.activate form .optional .title,
- .user.forgot.password form .optional .title,
- .user.reset.password form .optional .title,
- .user.link-account form .optional .title,
- .user.signin form .optional .title,
- .user.signup form .optional .title {
- margin-left: 250px !important;
- }
-}
-
-@media (max-width: 767.98px) {
- .user.activate form .optional .title,
- .user.forgot.password form .optional .title,
- .user.reset.password form .optional .title,
- .user.link-account form .optional .title,
- .user.signin form .optional .title,
- .user.signup form .optional .title {
- margin-left: 15px;
- }
- .user.activate form .inline.field > label,
- .user.forgot.password form .inline.field > label,
- .user.reset.password form .inline.field > label,
- .user.link-account form .inline.field > label,
- .user.signin form .inline.field > label,
- .user.signup form .inline.field > label {
- display: block;
- }
-}
-
-.user.activate form .header,
-.user.forgot.password form .header,
-.user.reset.password form .header,
-.user.link-account form .header,
-.user.signin form .header,
-.user.signup form .header {
- padding-left: 0 !important;
- text-align: center;
-}
-
-.user.activate form .inline.field > label,
-.user.forgot.password form .inline.field > label,
-.user.reset.password form .inline.field > label,
-.user.link-account form .inline.field > label,
-.user.signin form .inline.field > label,
-.user.signup form .inline.field > label {
- width: 200px;
-}
-
-@media (max-width: 767.98px) {
- .user.activate form .inline.field > label,
- .user.forgot.password form .inline.field > label,
- .user.reset.password form .inline.field > label,
- .user.link-account form .inline.field > label,
- .user.signin form .inline.field > label,
- .user.signup form .inline.field > label {
- width: 100% !important;
- }
-}
-
-.user.activate form input[type="number"],
-.user.forgot.password form input[type="number"],
-.user.reset.password form input[type="number"],
-.user.link-account form input[type="number"],
-.user.signin form input[type="number"],
-.user.signup form input[type="number"] {
- -moz-appearance: textfield;
-}
-
-.user.activate form input::-webkit-outer-spin-button,
-.user.forgot.password form input::-webkit-outer-spin-button,
-.user.reset.password form input::-webkit-outer-spin-button,
-.user.link-account form input::-webkit-outer-spin-button,
-.user.signin form input::-webkit-outer-spin-button,
-.user.signup form input::-webkit-outer-spin-button,
-.user.activate form input::-webkit-inner-spin-button,
-.user.forgot.password form input::-webkit-inner-spin-button,
-.user.reset.password form input::-webkit-inner-spin-button,
-.user.link-account form input::-webkit-inner-spin-button,
-.user.signin form input::-webkit-inner-spin-button,
-.user.signup form input::-webkit-inner-spin-button {
- -webkit-appearance: none;
- margin: 0;
-}
-
-.repository.new-repo form,
-.repository.new.migrate form,
-.repository.new.fork form {
- margin: auto;
-}
-
-.repository.new-repo form .ui.message,
-.repository.new.migrate form .ui.message,
-.repository.new.fork form .ui.message {
- text-align: center;
-}
-
-@media (min-width: 768px) {
- .repository.new-repo form,
- .repository.new.migrate form,
- .repository.new.fork form {
- width: 800px !important;
- }
- .repository.new-repo form .header,
- .repository.new.migrate form .header,
- .repository.new.fork form .header {
- padding-left: 280px !important;
- }
- .repository.new-repo form .inline.field > label,
- .repository.new.migrate form .inline.field > label,
- .repository.new.fork form .inline.field > label {
- text-align: right;
- width: 250px !important;
- word-wrap: break-word;
- }
- .repository.new-repo form .help,
- .repository.new.migrate form .help,
- .repository.new.fork form .help {
- margin-left: 265px !important;
- }
- .repository.new-repo form .optional .title,
- .repository.new.migrate form .optional .title,
- .repository.new.fork form .optional .title {
- margin-left: 250px !important;
- }
- .repository.new-repo form .inline.field > input,
- .repository.new.migrate form .inline.field > input,
- .repository.new.fork form .inline.field > input,
- .repository.new-repo form .inline.field > textarea,
- .repository.new.migrate form .inline.field > textarea,
- .repository.new.fork form .inline.field > textarea {
- width: 50%;
- }
+.ui.form.left-right-form .inline.field > label {
+ text-align: right;
+ width: 250px;
+ margin-right: 10px;
}
-@media (max-width: 767.98px) {
- .repository.new-repo form .optional .title,
- .repository.new.migrate form .optional .title,
- .repository.new.fork form .optional .title {
- margin-left: 15px;
- }
- .repository.new-repo form .inline.field > label,
- .repository.new.migrate form .inline.field > label,
- .repository.new.fork form .inline.field > label {
- display: block;
- }
+.ui.form.left-right-form .inline.field > .help {
+ display: block;
+ margin-left: calc(250px + 15px);
}
-.repository.new-repo form .dropdown .text,
-.repository.new.migrate form .dropdown .text,
-.repository.new.fork form .dropdown .text {
- margin-right: 0 !important;
+.ui.form.left-right-form .inline.field input:not([type="checkbox"], [type="radio"]),
+.ui.form.left-right-form .inline.field .ui.dropdown,
+.ui.form.left-right-form .inline.field textarea {
+ width: 50%;
}
-.repository.new-repo form .header,
-.repository.new.migrate form .header,
-.repository.new.fork form .header {
- padding-left: 0 !important;
- text-align: center;
+.ui.form.left-right-form .inline.field .ui.dropdown input.search {
+ width: 100%;
}
-.repository.new-repo form .selection.dropdown,
-.repository.new.migrate form .selection.dropdown,
-.repository.new.fork form .selection.dropdown,
-.repository.new.fork form .field a {
- vertical-align: middle;
- width: 50% !important;
+.ui.form.left-right-form .inline.field .inline-right {
+ display: inline-flex;
+ flex-direction: column;
+ gap: 0.5em;
}
@media (max-width: 767.98px) {
- .repository.new-repo form label,
- .repository.new.migrate form label,
- .repository.new.fork form label,
- .repository.new-repo form .inline.field > input,
- .repository.new.migrate form .inline.field > input,
- .repository.new.fork form .inline.field > input,
- .repository.new.fork form .field a,
- .repository.new-repo form .selection.dropdown,
- .repository.new.migrate form .selection.dropdown,
- .repository.new.fork form .selection.dropdown {
- width: 100% !important;
- }
- .repository.new-repo form .field button,
- .repository.new.migrate form .field button,
- .repository.new.fork form .field button,
- .repository.new-repo form .field a,
- .repository.new.migrate form .field a {
- margin-bottom: 1em;
+ .ui.form.left-right-form .inline.field > label {
width: 100%;
+ margin: 0;
+ text-align: left;
}
-}
-
-@media (min-width: 768px) {
- .repository.new-repo .ui.form #auto-init {
- margin-left: 265px !important;
- }
-}
-
-.repository.new-repo .ui.form .selection.dropdown:not(.owner) {
- width: 50% !important;
-}
-
-@media (max-width: 767.98px) {
- .repository.new-repo .ui.form .selection.dropdown:not(.owner) {
- width: 100% !important;
+ .ui.form.left-right-form .inline.field > .help {
+ margin: 0;
}
-}
-
-/* form fields with additional content besides their label, used on login form
- * use like <div class="field"><label/><a/><input/></div> */
-.form-field-content-aside-label {
- display: grid;
- grid-template-columns: 1fr 1fr;
-}
-.form-field-content-aside-label > *:nth-child(2) {
- text-align: right;
-}
-.form-field-content-aside-label input {
- grid-column: span 2;
-}
-
-.ui.form .field > .selection.dropdown {
- min-width: 14em; /* matches the default min width */
-}
-
-.new.webhook form .help {
- margin-left: 25px;
-}
-
-.new.webhook .events.fields .column {
- padding-left: 40px;
-}
-
-.githook textarea {
- font-family: var(--fonts-monospace);
-}
-
-@media (max-width: 767.98px) {
- .new.org .ui.form .field button,
- .new.org .ui.form .field a {
- margin-bottom: 1em;
+ .ui.form.left-right-form .inline.field input:not([type="checkbox"], [type="radio"]),
+ .ui.form.left-right-form .inline.field .ui.dropdown,
+ .ui.form.left-right-form .inline.field textarea {
width: 100%;
}
- .new.org .ui.form .field input {
- width: 100% !important;
- }
}
diff --git a/web_src/css/home.css b/web_src/css/home.css
index 77d2ecf92b..195d1f5d96 100644
--- a/web_src/css/home.css
+++ b/web_src/css/home.css
@@ -21,7 +21,7 @@
}
.home .hero .svg {
- color: var(--color-green);
+ color: var(--color-logo);
height: 40px;
width: 50px;
vertical-align: bottom;
@@ -40,7 +40,7 @@
}
.home a {
- color: var(--color-green);
+ color: var(--color-logo);
}
.page-footer {
diff --git a/web_src/css/index.css b/web_src/css/index.css
index ce1a23b245..291cd04b2b 100644
--- a/web_src/css/index.css
+++ b/web_src/css/index.css
@@ -18,8 +18,8 @@
@import "./modules/checkbox.css";
@import "./modules/dimmer.css";
@import "./modules/modal.css";
+@import "./modules/tab.css";
-@import "./modules/select.css";
@import "./modules/tippy.css";
@import "./modules/breadcrumb.css";
@import "./modules/comment.css";
@@ -39,7 +39,7 @@
@import "./features/imagediff.css";
@import "./features/codeeditor.css";
@import "./features/projects.css";
-@import "./features/tribute.css";
+@import "./features/expander.css";
@import "./features/cropper.css";
@import "./features/console.css";
@@ -62,7 +62,7 @@
@import "./repo/issue-label.css";
@import "./repo/issue-list.css";
@import "./repo/list-header.css";
-@import "./repo/linebutton.css";
+@import "./repo/file-view.css";
@import "./repo/wiki.css";
@import "./repo/header.css";
@import "./repo/home.css";
@@ -70,6 +70,7 @@
@import "./repo/reactions.css";
@import "./repo/clone.css";
@import "./repo/commit-sign.css";
+@import "./repo/packages.css";
@import "./editor/fileeditor.css";
@import "./editor/combomarkdowneditor.css";
diff --git a/web_src/css/markup/codecopy.css b/web_src/css/markup/codecopy.css
index e3017ae962..5a7b9955e7 100644
--- a/web_src/css/markup/codecopy.css
+++ b/web_src/css/markup/codecopy.css
@@ -1,8 +1,3 @@
-.markup .code-block,
-.markup .mermaid-block {
- position: relative;
-}
-
.markup .code-copy {
position: absolute;
top: 8px;
@@ -28,8 +23,8 @@
background: var(--color-secondary-dark-1) !important;
}
-.markup .code-block:hover .code-copy,
-.markup .mermaid-block:hover .code-copy {
+.markup .code-block-container:hover .code-copy,
+.markup .code-block:hover .code-copy {
visibility: visible;
animation: fadein 0.2s both;
}
diff --git a/web_src/css/markup/content.css b/web_src/css/markup/content.css
index d2dcf2ec6e..c6a89edf25 100644
--- a/web_src/css/markup/content.css
+++ b/web_src/css/markup/content.css
@@ -5,10 +5,6 @@
overflow-wrap: break-word;
}
-.conversation-holder .markup {
- overflow-wrap: anywhere; /* prevent overflow in code comments. TODO: properly restrict .conversation-holder width and remove this */
-}
-
.markup > *:first-child {
margin-top: 0 !important;
}
@@ -138,6 +134,13 @@
margin-bottom: 16px;
}
+/* override p:last-child from base.css.
+Fomantic assumes that <p>/<hX> elements only have margins between elements, but not for the first's top or last's bottom.
+In markup content, we always use bottom margin for all elements */
+.markup p:last-child {
+ margin-bottom: 16px;
+}
+
.markup hr {
height: 4px;
padding: 0;
@@ -285,7 +288,6 @@
.markup table {
display: block;
width: 100%;
- width: max-content;
max-width: 100%;
overflow: auto;
}
@@ -314,10 +316,18 @@
box-sizing: initial;
}
+.file-view.markup {
+ padding: 1em 2em;
+}
+
+.file-view.markup:has(.file-not-rendered-prompt) {
+ padding: 0; /* let the file-not-rendered-prompt layout itself */
+}
+
/* this background ensures images can break <hr>. We can only do this on
cases where the background is known and not transparent. */
-.markup.file-view img,
-.markup.file-view video,
+.file-view.markup img,
+.file-view.markup video,
.comment-body .markup img, /* regular comment */
.comment-body .markup video,
.comment-content .markup img, /* code comment */
@@ -337,11 +347,6 @@
padding-right: 28px;
}
-.markup .emoji {
- max-width: none;
- vertical-align: text-top;
-}
-
.markup span.frame {
display: block;
overflow: hidden;
@@ -453,14 +458,25 @@
}
.markup pre > code {
- padding: 0;
- margin: 0;
font-size: 100%;
+}
+
+.markup .code-block,
+.markup .code-block-container {
+ position: relative;
+}
+
+.markup .code-block-container.code-overflow-wrap pre > code {
white-space: pre-wrap;
- word-break: break-all;
- overflow-wrap: break-word;
- background: transparent;
- border: 0;
+}
+
+.markup .code-block-container.code-overflow-scroll pre {
+ overflow-x: auto;
+}
+
+.markup .code-block-container.code-overflow-scroll pre > code {
+ white-space: pre;
+ overflow-wrap: normal;
}
.markup .highlight {
@@ -481,16 +497,11 @@
word-break: normal;
}
-.markup pre {
- word-wrap: normal;
-}
-
.markup pre code,
.markup pre tt {
display: inline;
padding: 0;
line-height: inherit;
- word-wrap: normal;
background-color: transparent;
border: 0;
}
@@ -521,21 +532,19 @@
padding-left: 2em;
}
-.file-revisions-btn {
- display: block;
- float: left;
- margin-bottom: 2px !important;
- padding: 11px !important;
- margin-right: 10px !important;
+.markup details.frontmatter-content summary {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ margin-bottom: 0.25em;
}
-.file-revisions-btn i {
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- user-select: none;
+.markup details.frontmatter-content svg {
+ vertical-align: middle;
+ margin: 0 0.25em;
}
-.markup-render {
+.markup-content-iframe {
display: block;
border: none;
width: 100%;
diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css
index 481e997d4f..deaaf83680 100644
--- a/web_src/css/modules/animations.css
+++ b/web_src/css/modules/animations.css
@@ -52,8 +52,7 @@ form.single-button-form.is-loading .button {
}
.markup pre.is-loading,
-.editor-loading.is-loading,
-.pdf-content.is-loading {
+.editor-loading.is-loading {
height: var(--height-loading);
}
@@ -116,3 +115,15 @@ code.language-math.is-loading::after {
animation-duration: 100ms;
animation-timing-function: ease-in-out;
}
+
+/* FIXME: `octicon-sync` is counterclockwise, so this animation is also counterclockwise, it looks somewhat strange.
+Ideally in the future we should use a better image for clockwise animation. */
+.circular-spin {
+ animation: circular-spin-keyframes 1s linear infinite;
+}
+
+@keyframes circular-spin-keyframes {
+ 100% {
+ transform: rotate(-360deg);
+ }
+}
diff --git a/web_src/css/modules/breadcrumb.css b/web_src/css/modules/breadcrumb.css
index ca488c2150..77e31ef627 100644
--- a/web_src/css/modules/breadcrumb.css
+++ b/web_src/css/modules/breadcrumb.css
@@ -1,14 +1,10 @@
.breadcrumb {
display: flex;
- flex-wrap: wrap;
align-items: center;
gap: 3px;
+ overflow-wrap: anywhere;
}
.breadcrumb .breadcrumb-divider {
color: var(--color-text-light-2);
}
-
-.breadcrumb > * {
- display: inline;
-}
diff --git a/web_src/css/modules/button.css b/web_src/css/modules/button.css
index c4addd05f0..b105bb5de2 100644
--- a/web_src/css/modules/button.css
+++ b/web_src/css/modules/button.css
@@ -1,20 +1,15 @@
-/* based on Fomantic UI checkbox module, with just the parts extracted that we use. If you find any
- unused rules here after refactoring, please remove them. */
-
.ui.button {
cursor: pointer;
- display: inline-block;
- min-height: 1em;
+ display: inline-flex;
outline: none;
- vertical-align: baseline;
font-family: var(--fonts-regular);
margin: 0 0.25em 0 0;
- padding: 0.78571429em 1.5em;
font-weight: var(--font-weight-normal);
+ font-size: 1rem;
text-align: center;
text-decoration: none;
line-height: 1;
- border-radius: 0.28571429rem;
+ border-radius: var(--border-radius);
user-select: none;
-webkit-tap-highlight-color: transparent;
justify-content: center;
@@ -58,12 +53,13 @@
pointer-events: none !important;
}
+/* there is no "ui labeled icon button" support" because it is not used */
.ui.labeled.button:not(.icon) {
- display: inline-flex;
flex-direction: row;
background: none;
- padding: 0 !important;
+ padding: 0;
border: none;
+ min-height: unset;
}
.ui.labeled.button > .button {
margin: 0;
@@ -102,47 +98,60 @@
margin: 0 -0.21428571em 0 0.42857143em;
}
+/* reference sizes (not exactly at the moment): normal: padding-x=21, height=38 ; compact: padding-x=15, height=32 */
+.ui.button { /* stylelint-disable-line no-duplicate-selectors */
+ min-height: 38px;
+ padding: 0.57em /* around 8px */ 1.43em /* around 20px */;
+}
.ui.compact.buttons .button,
.ui.compact.button {
- padding: 0.58928571em 1.125em;
+ padding: 0.42em /* around 8px */ 1.07em /* around 15px */;
+ min-height: 32px;
}
.ui.compact.icon.buttons .button,
.ui.compact.icon.button {
- padding: 0.58928571em;
-}
-.ui.compact.labeled.icon.button {
- padding: 0.58928571em 3.69642857em;
-}
-.ui.compact.labeled.icon.button > .icon {
- padding: 0.58928571em 0;
+ padding: 0.57em /* around 8px */;
}
-.ui.buttons .button,
-.ui.button {
- font-size: 1rem;
-}
+/* reference size: mini: padding-x=16, height=30 ; compact: padding-x=12, height=26 */
.ui.mini.buttons .dropdown,
.ui.mini.buttons .dropdown .menu > .item,
.ui.mini.buttons .button,
.ui.ui.ui.ui.mini.button {
- font-size: 0.78571429rem;
+ font-size: 11px;
+ min-height: 30px;
+}
+.ui.ui.ui.ui.mini.button.compact {
+ min-height: 26px;
}
+
+/* reference size: tiny: padding-x=18, height=32 ; compact: padding-x=13, height=28 */
.ui.tiny.buttons .dropdown,
.ui.tiny.buttons .dropdown .menu > .item,
.ui.tiny.buttons .button,
.ui.ui.ui.ui.tiny.button {
- font-size: 0.85714286rem;
+ font-size: 12px;
+ min-height: 32px;
+}
+.ui.ui.ui.ui.tiny.button.compact {
+ min-height: 28px;
}
+
+/* reference size: small: padding-x=19, height=34 ; compact: padding-x=14, height=30 */
.ui.small.buttons .dropdown,
.ui.small.buttons .dropdown .menu > .item,
.ui.small.buttons .button,
.ui.ui.ui.ui.small.button {
- font-size: 0.92857143rem;
+ font-size: 13px;
+ min-height: 34px;
+}
+.ui.ui.ui.ui.small.button.compact {
+ min-height: 30px;
}
.ui.icon.buttons .button,
.ui.icon.button:not(.compact) {
- padding: 0.78571429em;
+ padding: 0.57em;
}
.ui.icon.buttons .button > .icon,
.ui.icon.button > .icon {
@@ -152,12 +161,12 @@
.ui.basic.buttons .button,
.ui.basic.button {
- border-radius: 0.28571429rem;
+ border-radius: var(--border-radius);
background: none;
}
.ui.basic.buttons {
border: 1px solid var(--color-secondary);
- border-radius: 0.28571429rem;
+ border-radius: var(--border-radius);
}
.ui.basic.buttons .button {
border-radius: 0;
@@ -188,29 +197,6 @@
background: var(--color-active);
}
-.ui.labeled.icon.button {
- position: relative;
- padding-left: 4.07142857em !important;
- padding-right: 1.5em !important;
-}
-
-.ui.labeled.icon.button > .icon {
- position: absolute;
- top: 0;
- left: 0;
- height: 100%;
- line-height: 1;
- border-radius: 0;
- border-top-left-radius: inherit;
- border-bottom-left-radius: inherit;
- text-align: center;
- animation: none;
- padding: 0.78571429em 0;
- margin: 0;
- width: 2.57142857em;
- background: var(--color-hover);
-}
-
.ui.button.toggle.active {
background-color: var(--color-green);
color: var(--color-white);
@@ -366,6 +352,14 @@ a.btn:hover {
color: inherit;
}
+.btn.tiny {
+ font-size: 12px;
+}
+
+.btn.small {
+ font-size: 13px;
+}
+
/* By default, Fomantic UI doesn't support "bordered" buttons group, but Gitea would like to use it.
And the default buttons always have borders now (not the same as Fomantic UI's default buttons, see above).
It needs some tricks to tweak the left/right borders with active state */
@@ -379,12 +373,12 @@ It needs some tricks to tweak the left/right borders with active state */
.ui.buttons .button:first-child {
border-left: none;
margin-left: 0;
- border-top-left-radius: 0.28571429rem;
- border-bottom-left-radius: 0.28571429rem;
+ border-top-left-radius: var(--border-radius);
+ border-bottom-left-radius: var(--border-radius);
}
.ui.buttons .button:last-child {
- border-top-right-radius: 0.28571429rem;
- border-bottom-right-radius: 0.28571429rem;
+ border-top-right-radius: var(--border-radius);
+ border-bottom-right-radius: var(--border-radius);
}
.ui.buttons .button:hover {
@@ -414,10 +408,3 @@ It needs some tricks to tweak the left/right borders with active state */
.ui.buttons .button.active + .button {
border-left: none;
}
-
-/* apply the vertical padding of .compact to non-compact buttons when they contain a svg as they
- would otherwise appear too large. Seen on "RSS Feed" button on repo releases tab. */
-.ui.small.button:not(.compact):has(.svg) {
- padding-top: 0.58928571em;
- padding-bottom: 0.58928571em;
-}
diff --git a/web_src/css/modules/checkbox.css b/web_src/css/modules/checkbox.css
index 0a3a71acaa..f7e61ba360 100644
--- a/web_src/css/modules/checkbox.css
+++ b/web_src/css/modules/checkbox.css
@@ -119,3 +119,13 @@ input[type="radio"] {
.ui.toggle.checkbox input:focus:checked ~ label::before {
background: var(--color-primary) !important;
}
+
+label.gt-checkbox {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25em;
+}
+
+.ui.form .field > label.gt-checkbox {
+ display: flex;
+}
diff --git a/web_src/css/modules/container.css b/web_src/css/modules/container.css
index 4a442c35b1..236cb986fd 100644
--- a/web_src/css/modules/container.css
+++ b/web_src/css/modules/container.css
@@ -11,7 +11,6 @@
.ui.fluid.container {
width: 100%;
}
-
-.ui[class*="center aligned"].container {
- text-align: center;
+.ui.container.medium-width {
+ width: 800px;
}
diff --git a/web_src/css/modules/dimmer.css b/web_src/css/modules/dimmer.css
index 8924821370..7d1ca6171a 100644
--- a/web_src/css/modules/dimmer.css
+++ b/web_src/css/modules/dimmer.css
@@ -20,7 +20,7 @@
opacity: 1;
}
-.ui.dimmer > * {
+.ui.dimmer > .ui.modal {
position: static;
margin-top: auto !important;
margin-bottom: auto !important;
diff --git a/web_src/css/modules/grid.css b/web_src/css/modules/grid.css
index a2c558047d..b4f4e16105 100644
--- a/web_src/css/modules/grid.css
+++ b/web_src/css/modules/grid.css
@@ -393,58 +393,6 @@
margin-right: 2.5rem;
}
-.ui[class*="middle aligned"].grid > .column:not(.row),
-.ui[class*="middle aligned"].grid > .row > .column,
-.ui.grid > [class*="middle aligned"].row > .column,
-.ui.grid > [class*="middle aligned"].column:not(.row),
-.ui.grid > .row > [class*="middle aligned"].column {
- flex-direction: column;
- vertical-align: middle;
- align-self: center !important;
-}
-
-.ui[class*="left aligned"].grid > .column,
-.ui[class*="left aligned"].grid > .row > .column,
-.ui.grid > [class*="left aligned"].row > .column,
-.ui.grid > [class*="left aligned"].column.column,
-.ui.grid > .row > [class*="left aligned"].column.column {
- text-align: left;
- align-self: inherit;
-}
-
-.ui[class*="center aligned"].grid > .column,
-.ui[class*="center aligned"].grid > .row > .column,
-.ui.grid > [class*="center aligned"].row > .column,
-.ui.grid > [class*="center aligned"].column.column,
-.ui.grid > .row > [class*="center aligned"].column.column {
- text-align: center;
- align-self: inherit;
-}
-.ui[class*="center aligned"].grid {
- justify-content: center;
-}
-
-.ui[class*="right aligned"].grid > .column,
-.ui[class*="right aligned"].grid > .row > .column,
-.ui.grid > [class*="right aligned"].row > .column,
-.ui.grid > [class*="right aligned"].column.column,
-.ui.grid > .row > [class*="right aligned"].column.column {
- text-align: right;
- align-self: inherit;
-}
-
-.ui[class*="equal width"].grid > .column:not(.row),
-.ui[class*="equal width"].grid > .row > .column,
-.ui.grid > [class*="equal width"].row > .column {
- display: inline-block;
- flex-grow: 1;
-}
-.ui[class*="equal width"].grid > .wide.column,
-.ui[class*="equal width"].grid > .row > .wide.column,
-.ui.grid > [class*="equal width"].row > .wide.column {
- flex-grow: 0;
-}
-
@media only screen and (max-width: 767.98px) {
.ui[class*="mobile reversed"].grid,
.ui[class*="mobile reversed"].grid > .row,
diff --git a/web_src/css/modules/label.css b/web_src/css/modules/label.css
index 1e42668aa1..f5d0decdf6 100644
--- a/web_src/css/modules/label.css
+++ b/web_src/css/modules/label.css
@@ -4,25 +4,20 @@
.ui.label {
display: inline-flex;
align-items: center;
- gap: .25rem;
- min-width: 0;
vertical-align: middle;
- line-height: 1;
+ gap: var(--gap-inline);
+ min-width: 0;
+ max-width: 100%;
background: var(--color-label-bg);
color: var(--color-label-text);
- padding: 0.3em 0.5em;
- font-size: 0.85714286rem;
+ padding: 2px 6px;
+ font-size: var(--font-size-label);
font-weight: var(--font-weight-medium);
border: 0 solid transparent;
- border-radius: 0.28571429rem;
+ border-radius: var(--border-radius);
white-space: nowrap;
-}
-
-.ui.label:first-child {
- margin-left: 0;
-}
-.ui.label:last-child {
- margin-right: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
a.ui.label {
@@ -94,10 +89,6 @@ a.ui.label:hover {
color: var(--color-label-text);
}
-.ui.label.visible:not(.dropdown) {
- display: inline-block !important;
-}
-
.ui.basic.label {
background: var(--color-button);
border: 1px solid var(--color-light-border);
@@ -292,3 +283,58 @@ a.ui.ui.ui.basic.grey.label:hover {
.ui.large.label {
font-size: 1rem;
}
+
+/* To let labels break up and wrap across multiple lines (issue title, comment event), use "display: contents here" to apply parent layout.
+If the labels-list itself needs some layouts, use extra classes or "tw" helpers. */
+.labels-list {
+ display: contents;
+ font-size: var(--font-size-label); /* it must match the label font size, otherwise the height mismatches */
+}
+
+.labels-list a {
+ max-width: 100%; /* for ellipsis */
+}
+
+.labels-list .ui.label {
+ min-height: 20px;
+ padding-top: 0;
+ padding-bottom: 0;
+}
+
+.with-labels-list-inline .labels-list .ui.label + .ui.label {
+ margin-left: var(--gap-inline);
+}
+
+.with-labels-list-inline .labels-list .ui.label {
+ line-height: var(--line-height-default);
+}
+
+/* Scoped labels with different colors on left and right */
+.ui.label.scope-parent {
+ background: none !important;
+ padding: 0 !important;
+ gap: 0 !important;
+}
+
+.ui.label.scope-parent > .ui.label {
+ margin: 0 !important; /* scoped label's margin is handled by the parent */
+}
+
+.ui.label.scope-left {
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.ui.label.scope-middle {
+ border-radius: 0;
+}
+
+.ui.label.scope-right {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+}
+
+.ui.label.archived-label {
+ filter: grayscale(0.5);
+ opacity: 0.5;
+}
diff --git a/web_src/css/modules/list.css b/web_src/css/modules/list.css
index 73760390de..46422cb97d 100644
--- a/web_src/css/modules/list.css
+++ b/web_src/css/modules/list.css
@@ -5,7 +5,6 @@
list-style-type: none;
margin: 1em 0;
padding: 0;
- font-size: 1em;
}
.ui.list:first-child {
diff --git a/web_src/css/modules/menu.css b/web_src/css/modules/menu.css
index a5efd23053..5072dcbd0e 100644
--- a/web_src/css/modules/menu.css
+++ b/web_src/css/modules/menu.css
@@ -1,5 +1,6 @@
.ui.menu {
display: flex;
+ flex-shrink: 0;
margin: 1rem 0;
font-family: var(--fonts-regular);
font-weight: var(--font-weight-normal);
@@ -643,6 +644,7 @@
display: inline-flex;
margin: 0;
vertical-align: middle;
+ flex-shrink: 0;
}
.ui.compact.vertical.menu {
display: inline-block;
diff --git a/web_src/css/modules/modal.css b/web_src/css/modules/modal.css
index 427d2529c8..fd6dacc30c 100644
--- a/web_src/css/modules/modal.css
+++ b/web_src/css/modules/modal.css
@@ -67,6 +67,7 @@ These inconsistent layouts should be refactored to simple ones.
border-radius: 0 0 var(--border-radius) var(--border-radius);
}
+.ui.modal .content > form > .actions,
.ui.modal .content > .actions {
padding-top: 1em; /* if the "actions" is in the "content", some paddings are already added by the "content" */
text-align: right;
diff --git a/web_src/css/modules/navbar.css b/web_src/css/modules/navbar.css
index b60d25977d..149766a586 100644
--- a/web_src/css/modules/navbar.css
+++ b/web_src/css/modules/navbar.css
@@ -48,7 +48,8 @@
align-items: stretch;
}
/* hide all items */
- #navbar .item {
+ #navbar .navbar-left > .item,
+ #navbar .navbar-right > .item {
display: none;
}
#navbar #navbar-logo {
@@ -100,19 +101,6 @@
}
}
-#navbar .ui.dropdown .navbar-profile-admin {
- display: block;
- position: absolute;
- font-size: 9px;
- font-weight: var(--font-weight-bold);
- color: var(--color-nav-bg);
- background: var(--color-primary);
- padding: 2px 3px;
- border-radius: 10px;
- top: -1px;
- left: 18px;
-}
-
#navbar a.item:hover .notification_count,
#navbar a.item:hover .header-stopwatch-dot {
border-color: var(--color-nav-hover-bg);
@@ -128,8 +116,8 @@
background: var(--color-primary);
border: 2px solid var(--color-nav-bg);
position: absolute;
- left: 6px;
- top: -9px;
+ left: calc(100% - 9px);
+ bottom: calc(100% - 9px);
min-width: 17px;
height: 17px;
border-radius: 11px; /* (height + 2 * borderThickness) / 2 */
diff --git a/web_src/css/modules/segment.css b/web_src/css/modules/segment.css
index 0f555cea93..adb514be59 100644
--- a/web_src/css/modules/segment.css
+++ b/web_src/css/modules/segment.css
@@ -123,13 +123,6 @@
clear: both;
}
-.ui[class*="left aligned"].segment {
- text-align: left;
-}
-.ui[class*="center aligned"].segment {
- text-align: center;
-}
-
.ui.secondary.segment {
background: var(--color-secondary-bg);
color: var(--color-text-light);
diff --git a/web_src/css/modules/select.css b/web_src/css/modules/select.css
deleted file mode 100644
index 1d7d749d4a..0000000000
--- a/web_src/css/modules/select.css
+++ /dev/null
@@ -1,25 +0,0 @@
-.gitea-select {
- position: relative;
-}
-
-.gitea-select select {
- appearance: none; /* hide default triangle */
-}
-
-/* ::before and ::after pseudo elements don't work on select elements,
- so we need to put it on the parent. */
-.gitea-select::after {
- position: absolute;
- top: 12px;
- right: 8px;
- pointer-events: none;
- content: "";
- width: 14px;
- height: 14px;
- mask-size: cover;
- -webkit-mask-size: cover;
- mask-image: var(--octicon-chevron-right);
- -webkit-mask-image: var(--octicon-chevron-right);
- transform: rotate(90deg); /* point the chevron down */
- background: currentcolor;
-}
diff --git a/web_src/css/modules/svg.css b/web_src/css/modules/svg.css
index b3060bddd6..738ec22cd3 100644
--- a/web_src/css/modules/svg.css
+++ b/web_src/css/modules/svg.css
@@ -4,6 +4,10 @@
fill: currentcolor;
}
+.svg.git-entry-icon {
+ fill: transparent; /* some material icons have dark background fill, so need to reset */
+}
+
.middle .svg {
vertical-align: middle;
}
diff --git a/web_src/css/modules/tab.css b/web_src/css/modules/tab.css
new file mode 100644
index 0000000000..63c83179b2
--- /dev/null
+++ b/web_src/css/modules/tab.css
@@ -0,0 +1,7 @@
+.ui.tab {
+ display: none;
+}
+
+.ui.tab.active {
+ display: block;
+}
diff --git a/web_src/css/modules/table.css b/web_src/css/modules/table.css
index 4fb9d4214e..eabca31a17 100644
--- a/web_src/css/modules/table.css
+++ b/web_src/css/modules/table.css
@@ -152,31 +152,6 @@
}
}
-.ui.table[class*="left aligned"],
-.ui.table [class*="left aligned"] {
- text-align: left;
-}
-
-.ui.table[class*="center aligned"],
-.ui.table [class*="center aligned"] {
- text-align: center;
-}
-
-.ui.table[class*="right aligned"],
-.ui.table [class*="right aligned"] {
- text-align: right;
-}
-
-.ui.table[class*="top aligned"],
-.ui.table [class*="top aligned"] {
- vertical-align: top;
-}
-
-.ui.table[class*="middle aligned"],
-.ui.table [class*="middle aligned"] {
- vertical-align: middle;
-}
-
.ui.table th.collapsing,
.ui.table td.collapsing {
width: 1px;
diff --git a/web_src/css/modules/tippy.css b/web_src/css/modules/tippy.css
index 55b9751cc6..3c0d63f2fb 100644
--- a/web_src/css/modules/tippy.css
+++ b/web_src/css/modules/tippy.css
@@ -28,6 +28,10 @@
z-index: 1;
}
+.tippy-box[data-theme="default"] {
+ box-shadow: 0 6px 18px var(--color-shadow);
+}
+
/* bare theme, no styling at all, except box-shadow */
.tippy-box[data-theme="bare"] {
border: none;
@@ -88,6 +92,10 @@
}
.tippy-box[data-theme="menu"] .item:focus {
+ background: var(--color-hover);
+}
+
+.tippy-box[data-theme="menu"] .item.active {
background: var(--color-active);
}
diff --git a/web_src/css/modules/toast.css b/web_src/css/modules/toast.css
index 1145f3b1b5..330d3b176e 100644
--- a/web_src/css/modules/toast.css
+++ b/web_src/css/modules/toast.css
@@ -3,7 +3,7 @@
position: fixed;
opacity: 0;
transition: all .2s ease;
- z-index: 500;
+ z-index: var(--z-index-toast);
border-radius: var(--border-radius);
box-shadow: 0 8px 24px var(--color-shadow);
display: flex;
diff --git a/web_src/css/org.css b/web_src/css/org.css
index 1082625041..48b41de297 100644
--- a/web_src/css/org.css
+++ b/web_src/css/org.css
@@ -1,94 +1,7 @@
-#create-page-form form {
- margin: auto;
-}
-
-#create-page-form form .ui.message {
- text-align: center;
-}
-
-@media (min-width: 768px) {
- #create-page-form form {
- width: 800px !important;
- }
- #create-page-form form .header {
- padding-left: 280px !important;
- }
- #create-page-form form .inline.field > label {
- text-align: right;
- width: 250px !important;
- word-wrap: break-word;
- }
- #create-page-form form .help {
- margin-left: 265px !important;
- }
- #create-page-form form .optional .title {
- margin-left: 250px !important;
- }
- #create-page-form form .inline.field > input,
- #create-page-form form .inline.field > textarea {
- width: 50%;
- }
-}
-
-@media (max-width: 767.98px) {
- #create-page-form form .optional .title {
- margin-left: 15px;
- }
- #create-page-form form .inline.field > label {
- display: block;
- }
-}
-
.organization .head .ui.header .ui.right {
margin-top: 5px;
}
-.organization.new.org form {
- margin: auto;
-}
-
-.organization.new.org form .ui.message {
- text-align: center;
-}
-
-@media (min-width: 768px) {
- .organization.new.org form {
- width: 800px !important;
- }
- .organization.new.org form .header {
- padding-left: 280px !important;
- }
- .organization.new.org form .inline.field > label {
- text-align: right;
- width: 250px !important;
- word-wrap: break-word;
- }
- .organization.new.org form .help {
- margin-left: 265px !important;
- }
- .organization.new.org form .optional .title {
- margin-left: 250px !important;
- }
- .organization.new.org form .inline.field > input,
- .organization.new.org form .inline.field > textarea {
- width: 50%;
- }
-}
-
-@media (max-width: 767.98px) {
- .organization.new.org form .optional .title {
- margin-left: 15px;
- }
- .organization.new.org form .inline.field > label {
- display: block;
- }
-}
-
-.organization.new.org form .header {
- padding-left: 0 !important;
- text-align: center;
-}
-
.page-content.organization .org-avatar {
margin-right: 15px;
}
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 22bbe3cc23..a72709c382 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -50,23 +50,44 @@
width: 300px;
}
-.issue-sidebar-combo .ui.dropdown .item:not(.checked) .item-check-mark {
- visibility: hidden;
+.issue-content-right .ui.dropdown.full-width {
+ width: 100%;
}
-.issue-content-right .dropdown > .menu {
+.issue-content-right .ui.dropdown.full-width > .fixed-text {
+ display: flex;
+ flex-grow: 1;
+ justify-content: space-between;
+}
+
+.issue-content-right .ui.dropdown > .menu {
max-width: 270px;
min-width: 0;
max-height: 500px;
overflow-x: auto;
}
-.issue-content-right .dropdown > .menu .item-secondary-info small {
+.issue-content-right .ui.dropdown > .menu .item-secondary-info small {
display: block;
text-overflow: ellipsis;
overflow: hidden;
}
+.issue-content-right .ui.list {
+ margin: 0.5em 0;
+ max-width: 100%;
+}
+
+.issue-sidebar-combo > .ui.dropdown .item:not(.checked) .item-check-mark {
+ visibility: hidden;
+}
+
+.issue-content-right .ui.list.labels-list {
+ display: flex;
+ gap: var(--gap-inline);
+ flex-wrap: wrap;
+}
+
@media (max-width: 767.98px) {
.issue-content-left,
.issue-content-right {
@@ -129,11 +150,6 @@ td .commit-summary {
}
}
-.repo-path {
- display: flex;
- overflow-wrap: anywhere;
-}
-
.repository.file.list .non-diff-file-content .header .icon {
font-size: 1em;
}
@@ -167,42 +183,6 @@ td .commit-summary {
cursor: default;
}
-.view-raw {
- display: flex;
- justify-content: center;
- align-items: center;
-}
-
-.view-raw > * {
- max-width: 100%;
-}
-
-.view-raw audio,
-.view-raw video,
-.view-raw img {
- margin: 1rem 0;
- border-radius: 0;
- object-fit: contain;
-}
-
-.view-raw img[src$=".svg" i] {
- max-height: 600px !important;
- max-width: 600px !important;
-}
-
-.pdf-content {
- width: 100%;
- height: 600px;
- border: none !important;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.pdf-content .pdf-fallback-button {
- margin: 50px auto;
-}
-
.repository.file.list .non-diff-file-content .plain-text {
padding: 1em 2em;
}
@@ -225,10 +205,6 @@ td .commit-summary {
padding: 0 !important;
}
-.non-diff-file-content .pdfobject {
- border-radius: 0 0 var(--border-radius) var(--border-radius);
-}
-
.repo-editor-header {
width: 100%;
}
@@ -476,14 +452,6 @@ td .commit-summary {
margin-right: 5px;
}
-.repository.view.issue .merge.box .branch-update.grid .row {
- padding-bottom: 1rem;
-}
-
-.repository.view.issue .merge.box .branch-update.grid .row .icon {
- margin-top: 1.1rem;
-}
-
.repository.view.issue .comment-list:not(.prevent-before-timeline)::before {
display: block;
content: "";
@@ -521,7 +489,7 @@ td .commit-summary {
.repository.view.issue .comment-list .timeline-item,
.repository.view.issue .comment-list .timeline-item-group {
- padding: 16px 0;
+ padding: 8px 0;
}
.repository.view.issue .comment-list .timeline-item-group .timeline-item {
@@ -575,6 +543,11 @@ td .commit-summary {
justify-content: center;
}
+.repository.view.issue .comment-list .timeline-item.commits-list .badge {
+ margin-right: 0;
+ height: 28px;
+}
+
.repository.view.issue .comment-list .timeline-item .badge .svg {
width: 22px;
height: 22px;
@@ -599,10 +572,6 @@ td .commit-summary {
padding-top: 0;
}
-.repository.view.issue .comment-list .timeline-item.commits-list .ui.avatar {
- margin-right: 0.25em;
-}
-
.repository.view.issue .comment-list .timeline-item.event > .commit-status-link {
float: right;
margin-right: 8px;
@@ -784,7 +753,7 @@ td .commit-summary {
box-shadow: none;
}
-.repository.view.issue .ui.depending .item.is-closed .title {
+.repository.view.issue .ui.depending .item.is-closed .issue-dependency-title {
text-decoration: line-through;
}
@@ -1085,10 +1054,6 @@ td .commit-summary {
height: 30px;
}
-.repository .diff-box .resolved-placeholder .button {
- padding: 8px 12px;
-}
-
.repository .diff-file-box .header {
background-color: var(--color-box-header);
}
@@ -1228,33 +1193,6 @@ td .commit-summary {
font-weight: var(--font-weight-normal);
}
-.empty-placeholder {
- display: flex;
- flex-direction: column;
- align-items: center;
- padding-top: 40px;
- padding-bottom: 40px;
-}
-
-.repository.packages .file-size {
- white-space: nowrap;
-}
-
-.file-view.markup {
- padding: 1em 2em;
-}
-
-.file-view.markup:has(.file-not-rendered-prompt) {
- padding: 0; /* let the file-not-rendered-prompt layout itself */
-}
-
-.file-not-rendered-prompt {
- padding: 1rem;
- text-align: center;
- font-size: 1rem !important; /* use consistent styles for various containers (code, markup, etc) */
- line-height: var(--line-height-default) !important; /* same as above */
-}
-
.repository .activity-header {
display: flex;
justify-content: space-between;
@@ -1480,16 +1418,12 @@ td .commit-summary {
}
.comment-header {
- border: none !important;
background: var(--color-box-header);
- border-bottom: 1px solid var(--color-secondary) !important;
- font-weight: var(--font-weight-normal) !important;
- padding: 0.5rem 1rem;
- margin: 0 !important;
+ border-bottom: 1px solid var(--color-secondary);
+ padding: 0 1rem;
position: relative;
color: var(--color-text);
min-height: 41px;
- background-color: var(--color-box-header);
display: flex;
justify-content: space-between;
align-items: center;
@@ -1581,43 +1515,6 @@ td .commit-summary {
border-bottom-right-radius: 4px;
}
-.labels-list {
- display: inline-flex;
- flex-wrap: wrap;
- gap: 2.5px;
- align-items: center;
-}
-
-.labels-list .label, .scope-parent > .label {
- padding: 0 6px;
- min-height: 20px;
- line-height: 1.3; /* there is a `font-size: 1.25em` for inside emoji, so here the line-height needs to be larger slightly */
-}
-
-/* Scoped labels with different colors on left and right */
-.ui.label.scope-parent {
- background: none !important;
- padding: 0 !important;
- gap: 0 !important;
-}
-
-.archived-label {
- filter: grayscale(0.5);
- opacity: 0.5;
-}
-
-.ui.label.scope-left {
- border-bottom-right-radius: 0;
- border-top-right-radius: 0;
- margin-right: 0;
-}
-
-.ui.label.scope-right {
- border-bottom-left-radius: 0;
- border-top-left-radius: 0;
- margin-left: 0;
-}
-
.repo-button-row {
margin: 8px 0;
display: flex;
@@ -1631,21 +1528,17 @@ td .commit-summary {
display: flex;
align-items: center;
gap: 0.5rem;
+ flex-wrap: wrap;
}
.repo-button-row-left {
- flex: 1;
+ flex-grow: 1;
}
-.repo-button-row .button {
- padding: 6px 10px !important;
- height: 30px;
+.repo-button-row .ui.button {
flex-shrink: 0;
margin: 0;
-}
-
-.repo-button-row .button.dropdown:not(.icon) {
- padding-right: 22px !important; /* normal buttons have !important paddings, so we need to override it for dropdown (Add File) icons */
+ min-height: 30px;
}
tbody.commit-list {
@@ -1732,8 +1625,7 @@ tbody.commit-list {
line-height: 18px;
margin: 1em;
white-space: pre-wrap;
- word-break: break-all;
- overflow-wrap: break-word;
+ overflow-wrap: anywhere;
}
.content-history-detail-dialog .header .avatar {
@@ -1791,12 +1683,12 @@ tbody.commit-list {
.resolved-placeholder {
display: flex;
align-items: center;
- font-size: 14px !important;
- padding: 8px !important;
- font-weight: var(--font-weight-normal) !important;
- border: 1px solid var(--color-secondary) !important;
- border-radius: var(--border-radius) !important;
- margin: 4px !important;
+ justify-content: space-between;
+ margin: 4px;
+ padding: 8px;
+ border: 1px solid var(--color-secondary);
+ border-radius: var(--border-radius);
+ background: var(--color-box-header);
}
.resolved-placeholder + .comment-code-cloud {
@@ -1870,6 +1762,7 @@ tbody.commit-list {
border-radius: 0;
display: flex;
flex-direction: column;
+ gap: 0.5em;
}
/* fomantic's last-child selector does not work with hidden last child */
@@ -2059,10 +1952,6 @@ tbody.commit-list {
box-shadow: 0 0.5rem 1rem var(--color-shadow) !important;
}
-.migrate-entry .description {
- text-wrap: balance;
-}
-
.commits-table .commits-table-right form {
display: flex;
align-items: center;
@@ -2224,10 +2113,11 @@ tbody.commit-list {
max-width: min(400px, 90vw);
}
-.branch-selector-dropdown .branch-dropdown-button {
+.branch-selector-dropdown .ui.button.branch-dropdown-button {
margin: 0;
max-width: 340px;
line-height: var(--line-height-default);
+ padding: 0 0.5em 0 0.75em;
}
/* FIXME: These media selectors are not ideal (just keep them from old code).
diff --git a/web_src/css/repo/clone.css b/web_src/css/repo/clone.css
index 3f6a1323fe..53eb8b7b87 100644
--- a/web_src/css/repo/clone.css
+++ b/web_src/css/repo/clone.css
@@ -1,14 +1,16 @@
/* only used by "repo/empty.tmpl" */
.clone-buttons-combo {
display: flex;
- align-items: center;
+ align-items: stretch;
flex: 1;
}
-.clone-buttons-combo input {
- border-left: none !important;
- border-radius: 0 !important;
- height: 30px;
+.clone-buttons-combo > .ui.button:not(:last-child) {
+ border-right: none;
+}
+
+.ui.action.input.clone-buttons-combo input {
+ border-radius: 0; /* override fomantic border-radius for ".ui.input > input" */
}
/* used by the clone-panel popup */
@@ -20,10 +22,12 @@
.clone-panel-tab .item {
padding: 5px 10px;
background: none;
+ color: var(--color-text-light-2);
}
.clone-panel-tab .item.active {
- border-bottom: 3px solid var(--color-secondary);
+ color: var(--color-text-dark);
+ border-bottom: 3px solid currentcolor;
}
.clone-panel-tab + .divider {
diff --git a/web_src/css/repo/commit-sign.css b/web_src/css/repo/commit-sign.css
index 834fdd95d1..56eee62ffc 100644
--- a/web_src/css/repo/commit-sign.css
+++ b/web_src/css/repo/commit-sign.css
@@ -9,6 +9,7 @@
.ui.label.commit-id-short {
font-family: var(--fonts-monospace);
+ height: 24px;
}
.ui.label.commit-id-short > .commit-sign-badge {
@@ -16,7 +17,7 @@
padding: 0;
border: 0 !important;
border-radius: 0;
- background: transparent;
+ background: transparent !important;
}
.ui.label.commit-id-short > .commit-sign-badge:hover {
diff --git a/web_src/css/repo/file-view.css b/web_src/css/repo/file-view.css
new file mode 100644
index 0000000000..907f136afe
--- /dev/null
+++ b/web_src/css/repo/file-view.css
@@ -0,0 +1,92 @@
+.file-view tr.active .lines-num,
+.file-view tr.active .lines-escape,
+.file-view tr.active .lines-code {
+ background: var(--color-highlight-bg);
+}
+
+/* set correct border radius on the last active lines, to avoid border overflow */
+.file-view tr.active:last-of-type .lines-code {
+ border-bottom-right-radius: var(--border-radius);
+}
+
+.file-view tr.active .lines-num {
+ position: relative;
+}
+
+/* add a darker "handler" at the beginning of the active line */
+.file-view tr.active .lines-num::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ width: 2px;
+ height: 100%;
+ background: var(--color-highlight-fg);
+}
+
+.file-view .file-not-rendered-prompt {
+ padding: 1rem;
+ text-align: center;
+ font-size: 1rem !important; /* use consistent styles for various containers (code, markup, etc) */
+ line-height: var(--line-height-default) !important; /* same as above */
+}
+
+/* ".code-view" is always used with ".file-view", to show the code of a file */
+.file-view.code-view {
+ background: var(--color-code-bg);
+ border-radius: var(--border-radius);
+}
+
+.file-view.code-view table {
+ width: 100%;
+}
+
+.file-view.code-view .lines-num span::after {
+ cursor: pointer;
+}
+
+.file-view.code-view .lines-num:hover {
+ color: var(--color-text-dark);
+}
+
+.file-view.code-view .ui.button.code-line-button {
+ border: 1px solid var(--color-secondary);
+ padding: 1px 4px;
+ margin: 0;
+ min-height: 0;
+ position: absolute;
+ left: 6px;
+}
+
+.file-view.code-view .ui.button.code-line-button:hover {
+ background: var(--color-secondary);
+}
+
+.view-raw {
+ display: flex;
+ justify-content: center;
+}
+
+.view-raw > * {
+ max-width: 100%;
+}
+
+.view-raw audio,
+.view-raw video,
+.view-raw img {
+ margin: 1rem;
+ border-radius: 0;
+ object-fit: contain;
+}
+
+.view-raw img[src$=".svg" i] {
+ max-height: 600px !important;
+ max-width: 600px !important;
+}
+
+.file-view-render-container {
+ width: 100%;
+}
+
+.file-view-render-container :last-child {
+ border-radius: 0 0 var(--border-radius) var(--border-radius); /* to match the "ui segment" bottom radius */
+}
diff --git a/web_src/css/repo/header.css b/web_src/css/repo/header.css
index b70691435f..910648ea32 100644
--- a/web_src/css/repo/header.css
+++ b/web_src/css/repo/header.css
@@ -27,47 +27,3 @@
.repo-header .flex-item-trailing {
flex-wrap: nowrap;
}
-
-.repo-buttons {
- align-items: center;
- display: flex;
- flex-flow: row wrap;
- word-break: keep-all;
- gap: 0.25em;
-}
-
-.repo-buttons button[disabled] ~ .label {
- opacity: var(--opacity-disabled);
- color: var(--color-text-dark);
- background: var(--color-light-mimic-enabled) !important;
-}
-
-.repo-buttons button[disabled] ~ .label:hover {
- color: var(--color-primary-dark-1);
-}
-
-.repo-buttons .ui.labeled.button.disabled {
- pointer-events: inherit !important;
-}
-
-.repo-buttons .ui.labeled.button.disabled > .label {
- color: var(--color-text-dark);
- background: var(--color-light-mimic-enabled) !important;
-}
-
-.repo-buttons .ui.labeled.button.disabled > .label:hover {
- color: var(--color-primary-dark-1);
-}
-
-.repo-buttons .ui.labeled.button.disabled > .button {
- pointer-events: none !important;
-}
-
-@media (max-width: 767.98px) {
- .repo-buttons .ui.button,
- .repo-buttons .ui.label {
- padding-left: 8px;
- padding-right: 8px;
- margin: 0;
- }
-}
diff --git a/web_src/css/repo/home-file-list.css b/web_src/css/repo/home-file-list.css
index 19ba1f2bcb..f2ab052a54 100644
--- a/web_src/css/repo/home-file-list.css
+++ b/web_src/css/repo/home-file-list.css
@@ -14,21 +14,6 @@
}
}
-#repo-files-table .repo-file-cell.name .svg {
- margin-right: 2px;
-}
-
-#repo-files-table .svg.octicon-file-directory-fill,
-#repo-files-table .svg.octicon-file-submodule {
- color: var(--color-primary);
-}
-
-#repo-files-table .svg.octicon-file,
-#repo-files-table .svg.octicon-file-symlink-file,
-#repo-files-table .svg.octicon-file-directory-symlink {
- color: var(--color-secondary-dark-7);
-}
-
#repo-files-table .repo-file-item {
display: contents;
}
@@ -65,15 +50,30 @@
}
#repo-files-table .repo-file-last-commit {
+ min-width: 0; /* otherwise the flex axis is not limited and the text might overflow in Pale Moon */
background: var(--color-box-header);
}
#repo-files-table .repo-file-cell.name {
+ display: flex;
+ align-items: center;
+ gap: 0.5em;
+ overflow: hidden;
+}
+
+#repo-files-table .repo-file-cell.name > a,
+#repo-files-table .repo-file-cell.name > span {
+ flex-shrink: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
+#repo-files-table .repo-file-cell.name .entry-name {
+ flex-shrink: 1;
+ min-width: 3em;
+}
+
@media (max-width: 767.98px) {
#repo-files-table .repo-file-cell.name {
max-width: 35vw;
diff --git a/web_src/css/repo/home.css b/web_src/css/repo/home.css
index 65005e2263..ee371f1b1c 100644
--- a/web_src/css/repo/home.css
+++ b/web_src/css/repo/home.css
@@ -1,7 +1,8 @@
.repo-grid-filelist-sidebar {
display: grid;
- grid-template-columns: auto 300px;
+ grid-template-columns: auto 280px;
grid-template-rows: auto auto 1fr;
+ gap: var(--page-spacing);
}
.repo-home-filelist {
@@ -13,13 +14,11 @@
.repo-home-sidebar-top {
grid-column: 2;
grid-row: 1;
- padding-left: 1em;
}
.repo-home-sidebar-bottom {
grid-column: 2;
grid-row: 2;
- padding-left: 1em;
}
.repo-home-sidebar-bottom .flex-list > :first-child {
@@ -50,6 +49,27 @@
}
}
+.repo-view-container {
+ display: flex;
+ gap: var(--page-spacing);
+}
+
+.repo-view-container .repo-view-file-tree-container {
+ flex: 0 0 15%;
+ min-width: 0;
+ max-height: 100vh;
+ position: sticky;
+ top: 0;
+ bottom: 0;
+ height: 100%;
+ overflow-y: hidden;
+}
+
+.repo-view-content {
+ flex: 1;
+ min-width: 0;
+}
+
.language-stats {
display: flex;
gap: 2px;
diff --git a/web_src/css/repo/issue-card.css b/web_src/css/repo/issue-card.css
index fb832bd05a..327919b1fe 100644
--- a/web_src/css/repo/issue-card.css
+++ b/web_src/css/repo/issue-card.css
@@ -7,6 +7,7 @@
padding: 8px 10px;
border: 1px solid var(--color-secondary);
background: var(--color-card);
+ color: var(--color-text); /* it can't inherit from parent because the card already has its own background */
}
.issue-card-icon,
@@ -28,13 +29,16 @@
display: flex;
width: 100%;
justify-content: space-between;
- gap: 0.25em;
+ gap: 1em;
}
-.issue-card-assignees {
+.issue-card-bottom-part {
display: flex;
+ flex: 1;
align-items: center;
gap: 0.25em;
- justify-content: end;
flex-wrap: wrap;
+ overflow: hidden;
+ max-width: fit-content;
+ max-height: fit-content;
}
diff --git a/web_src/css/repo/issue-label.css b/web_src/css/repo/issue-label.css
index 0a25d31da9..f75c73b50f 100644
--- a/web_src/css/repo/issue-label.css
+++ b/web_src/css/repo/issue-label.css
@@ -4,41 +4,46 @@
margin: 0;
}
-.issue-label-list .item {
+.issue-label-list > .item {
border-bottom: 1px solid var(--color-secondary);
display: flex;
padding: 1em 0;
margin: 0;
}
-.issue-label-list .item:first-child {
+.issue-label-list > .item:first-child {
padding-top: 0;
}
-.issue-label-list .item:last-child {
+.issue-label-list > .item:last-child {
border-bottom: none;
padding-bottom: 0;
}
-.issue-label-list .item .label-title {
+.issue-label-list > .item .label-title {
width: 33%;
+ padding-right: 1em;
}
-.issue-label-list .item .label-issues {
+.issue-label-list > .item .label-issues {
width: 33%;
+ padding-right: 1em;
}
-.issue-label-list .item .label-operation {
+.issue-label-list > .item .label-operation {
width: 33%;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5em;
+ justify-content: end;
+ align-items: center;
}
-.issue-label-list .item a {
+.issue-label-list > .item .label-operation a {
font-size: 12px;
- padding-right: 10px;
- color: var(--color-text-light);
}
-.issue-label-list .item.org-label {
+.issue-label-list > .item.org-label {
opacity: 0.7;
}
diff --git a/web_src/css/repo/linebutton.css b/web_src/css/repo/linebutton.css
deleted file mode 100644
index e99d0399d1..0000000000
--- a/web_src/css/repo/linebutton.css
+++ /dev/null
@@ -1,18 +0,0 @@
-.code-view .lines-num:hover {
- color: var(--color-text-dark) !important;
-}
-
-.code-line-button {
- border: 1px solid var(--color-secondary);
- border-radius: var(--border-radius);
- padding: 1px 4px !important;
- position: absolute;
- font-family: var(--fonts-regular);
- left: 0;
- transform: translateX(calc(-50% + 6px));
- cursor: pointer;
-}
-
-.code-line-button:hover {
- background: var(--color-secondary) !important;
-}
diff --git a/web_src/css/repo/list-header.css b/web_src/css/repo/list-header.css
index e666e046d3..9d0b13933a 100644
--- a/web_src/css/repo/list-header.css
+++ b/web_src/css/repo/list-header.css
@@ -1,6 +1,6 @@
.list-header {
display: flex;
- align-items: center;
+ align-items: stretch;
flex-wrap: wrap;
gap: .5rem;
}
@@ -8,9 +8,8 @@
.list-header-search {
display: flex;
flex: 1;
- align-items: center;
+ align-items: stretch;
flex-wrap: wrap;
- justify-content: center;
min-width: 200px; /* to enable flexbox wrapping on mobile */
}
diff --git a/web_src/css/repo/packages.css b/web_src/css/repo/packages.css
new file mode 100644
index 0000000000..75675f5243
--- /dev/null
+++ b/web_src/css/repo/packages.css
@@ -0,0 +1,25 @@
+.packages-content {
+ display: flex;
+ align-items: flex-start;
+ gap: 16px;
+}
+
+.packages-content-left {
+ margin: 0 !important;
+ width: calc(100% - 250px - 16px);
+}
+
+.packages-content-right {
+ margin: 0 !important;
+ width: 250px;
+}
+
+@media (max-width: 767.98px) {
+ .packages-content {
+ flex-direction: column;
+ }
+ .packages-content-left,
+ .packages-content-right {
+ width: 100%;
+ }
+}
diff --git a/web_src/css/repo/release-tag.css b/web_src/css/repo/release-tag.css
index 32027dd886..4b42c992ef 100644
--- a/web_src/css/repo/release-tag.css
+++ b/web_src/css/repo/release-tag.css
@@ -31,6 +31,7 @@
#release-list .release-entry .detail {
flex: 1;
margin: 0;
+ min-width: 0;
}
@media (max-width: 767.98px) {
@@ -45,7 +46,7 @@
display: flex;
align-items: center;
}
- #release-list .js-branch-tag-selector {
+ #release-list .release-branch-tag-selector {
margin-left: auto;
}
#release-list .branch-selector-dropdown .menu { /* open menu to left */
@@ -58,17 +59,24 @@
margin-bottom: 2px; /* the legacy trick to align the avatar vertically, no better solution at the moment */
}
-#release-list .release-entry .detail .download .list {
- padding-left: 0;
+#release-list .release-entry .attachment-list {
border: 1px solid var(--color-secondary);
border-radius: var(--border-radius);
}
-#release-list .release-entry .detail .download .list li {
+#release-list .release-entry .attachment-list > .item {
display: flex;
- justify-content: space-between;
padding: 8px;
- border-bottom: 1px solid var(--color-secondary);
+ flex-wrap: wrap;
+}
+
+#release-list .release-entry .attachment-list > .item a {
+ min-width: 300px;
+}
+
+#release-list .release-entry .attachment-list .attachment-right-info {
+ flex-shrink: 0;
+ min-width: 300px;
}
#release-list .release-entry .detail .download[open] summary {
@@ -76,7 +84,6 @@
}
#release-list .download-icon {
- margin-right: .25rem;
color: var(--color-text-light-1);
}
@@ -84,10 +91,6 @@
border-bottom: none;
}
-#tags-table .tag-list-row {
- padding: 8px 12px;
-}
-
#tags-table .tag-list-row-title {
font-size: 18px;
font-weight: var(--font-weight-normal);
diff --git a/web_src/css/repo/wiki.css b/web_src/css/repo/wiki.css
index ca59dadb9c..144cb1206c 100644
--- a/web_src/css/repo/wiki.css
+++ b/web_src/css/repo/wiki.css
@@ -39,10 +39,6 @@
min-width: 150px;
}
-.repository.wiki .wiki-content-sidebar .ui.message.unicode-escape-prompt p {
- display: none;
-}
-
.repository.wiki .wiki-content-footer {
margin-top: 1em;
}
diff --git a/web_src/css/review.css b/web_src/css/review.css
index 036ad017f8..23383c051c 100644
--- a/web_src/css/review.css
+++ b/web_src/css/review.css
@@ -1,15 +1,8 @@
-.show-outdated,
-.hide-outdated {
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- user-select: none;
- margin-right: 0 !important;
-}
-
.ui.button.add-code-comment {
padding: 2px;
position: absolute;
margin-left: -22px;
+ min-height: 0;
z-index: 5;
opacity: 0;
transition: transform 0.1s ease-in-out;
@@ -58,11 +51,6 @@
margin-bottom: 0.5em;
}
-.show-outdated:hover,
-.hide-outdated:hover {
- text-decoration: underline;
-}
-
.comment-code-cloud {
padding: 0.5rem 1rem !important;
position: relative;
diff --git a/web_src/css/shared/flex-list.css b/web_src/css/shared/flex-list.css
index 0f54779252..e94e9e9cc2 100644
--- a/web_src/css/shared/flex-list.css
+++ b/web_src/css/shared/flex-list.css
@@ -17,6 +17,7 @@
.flex-item .flex-item-main {
display: flex;
flex-direction: column;
+ gap: 0.25em;
flex-grow: 1;
flex-basis: 60%; /* avoid wrapping the "flex-item-trailing" too aggressively */
min-width: 0; /* make the "text truncate" work, otherwise the flex axis is not limited and the text just overflows */
@@ -33,14 +34,6 @@
color: var(--color-primary) !important;
}
-.flex-item .flex-item-icon {
- align-self: baseline; /* mainly used by the issue list, to align the leading icon with the title */
-}
-
-.flex-item .flex-item-icon + .flex-item-main {
- align-self: baseline;
-}
-
.flex-item .flex-item-trailing {
display: flex;
gap: 0.5rem;
@@ -54,7 +47,9 @@
display: inline-flex;
flex-wrap: wrap;
align-items: center;
- gap: .25rem;
+ /* labels are under effect of this gap here because they are display:contents. Ideally we should make wrapping
+ of labels work without display: contents and set this to a static value again. */
+ gap: var(--gap-inline);
max-width: 100%;
color: var(--color-text);
font-size: 16px;
diff --git a/web_src/css/shared/milestone.css b/web_src/css/shared/milestone.css
index 91e6b5e387..47e822f8d3 100644
--- a/web_src/css/shared/milestone.css
+++ b/web_src/css/shared/milestone.css
@@ -12,7 +12,7 @@
border-top: 1px solid var(--color-secondary);
}
-.milestone-card .content {
+.milestone-card .render-content {
padding-top: 10px;
}
diff --git a/web_src/css/themes/theme-gitea-auto-protanopia-deuteranopia.css b/web_src/css/themes/theme-gitea-auto-protanopia-deuteranopia.css
index bcbf67d13d..418d7daeab 100644
--- a/web_src/css/themes/theme-gitea-auto-protanopia-deuteranopia.css
+++ b/web_src/css/themes/theme-gitea-auto-protanopia-deuteranopia.css
@@ -1,2 +1,6 @@
@import "./theme-gitea-light-protanopia-deuteranopia.css" (prefers-color-scheme: light);
@import "./theme-gitea-dark-protanopia-deuteranopia.css" (prefers-color-scheme: dark);
+
+gitea-theme-meta-info {
+ --theme-display-name: "Auto (Red/Green Colorblind-friendly)";
+}
diff --git a/web_src/css/themes/theme-gitea-auto.css b/web_src/css/themes/theme-gitea-auto.css
index 509889e802..cca49be99e 100644
--- a/web_src/css/themes/theme-gitea-auto.css
+++ b/web_src/css/themes/theme-gitea-auto.css
@@ -1,2 +1,6 @@
@import "./theme-gitea-light.css" (prefers-color-scheme: light);
@import "./theme-gitea-dark.css" (prefers-color-scheme: dark);
+
+gitea-theme-meta-info {
+ --theme-display-name: "Auto";
+}
diff --git a/web_src/css/themes/theme-gitea-dark-protanopia-deuteranopia.css b/web_src/css/themes/theme-gitea-dark-protanopia-deuteranopia.css
index c1a6edaf35..928cb8ba19 100644
--- a/web_src/css/themes/theme-gitea-dark-protanopia-deuteranopia.css
+++ b/web_src/css/themes/theme-gitea-dark-protanopia-deuteranopia.css
@@ -1,5 +1,9 @@
@import "./theme-gitea-dark.css";
+gitea-theme-meta-info {
+ --theme-display-name: "Dark (Red/Green Colorblind-friendly)";
+}
+
/* red/green colorblind-friendly colors */
/* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */
:root {
diff --git a/web_src/css/themes/theme-gitea-dark.css b/web_src/css/themes/theme-gitea-dark.css
index 9bc7747697..48fbd14dfb 100644
--- a/web_src/css/themes/theme-gitea-dark.css
+++ b/web_src/css/themes/theme-gitea-dark.css
@@ -1,6 +1,10 @@
@import "../chroma/dark.css";
@import "../codemirror/dark.css";
+gitea-theme-meta-info {
+ --theme-display-name: "Dark";
+}
+
:root {
--is-dark-theme: true;
--color-primary: #4183c4;
@@ -181,6 +185,7 @@
--color-orange-badge-bg: #f2711c1a;
--color-orange-badge-hover-bg: #f2711c4d;
--color-git: #f05133;
+ --color-logo: #609926;
/* target-based colors */
--color-body: #1b1f23;
--color-box-header: #1a1d1f;
diff --git a/web_src/css/themes/theme-gitea-light-protanopia-deuteranopia.css b/web_src/css/themes/theme-gitea-light-protanopia-deuteranopia.css
index f42fa1db2c..32d920582c 100644
--- a/web_src/css/themes/theme-gitea-light-protanopia-deuteranopia.css
+++ b/web_src/css/themes/theme-gitea-light-protanopia-deuteranopia.css
@@ -1,5 +1,9 @@
@import "./theme-gitea-light.css";
+gitea-theme-meta-info {
+ --theme-display-name: "Light (Red/Green Colorblind-friendly)";
+}
+
/* red/green colorblind-friendly colors */
/* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */
:root {
diff --git a/web_src/css/themes/theme-gitea-light.css b/web_src/css/themes/theme-gitea-light.css
index d7f9debf90..eaff717417 100644
--- a/web_src/css/themes/theme-gitea-light.css
+++ b/web_src/css/themes/theme-gitea-light.css
@@ -1,6 +1,10 @@
@import "../chroma/light.css";
@import "../codemirror/light.css";
+gitea-theme-meta-info {
+ --theme-display-name: "Light";
+}
+
:root {
--is-dark-theme: false;
--color-primary: #4183c4;
@@ -181,6 +185,7 @@
--color-orange-badge-bg: #f2711c1a;
--color-orange-badge-hover-bg: #f2711c4d;
--color-git: #f05133;
+ --color-logo: #609926;
/* target-based colors */
--color-body: #ffffff;
--color-box-header: #f1f3f5;
diff --git a/web_src/fomantic/.npmrc b/web_src/fomantic/.npmrc
deleted file mode 100644
index fbacc988dc..0000000000
--- a/web_src/fomantic/.npmrc
+++ /dev/null
@@ -1,7 +0,0 @@
-audit=false
-fund=false
-update-notifier=false
-package-lock=true
-save-exact=true
-lockfile-version=3
-optional=false
diff --git a/web_src/fomantic/build/components/api.js b/web_src/fomantic/build/components/api.js
new file mode 100644
index 0000000000..a104cfbc1b
--- /dev/null
+++ b/web_src/fomantic/build/components/api.js
@@ -0,0 +1,1169 @@
+/*!
+ * # Fomantic-UI - API
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+;(function ($, window, document, undefined) {
+
+'use strict';
+
+$.isWindow = $.isWindow || function(obj) {
+ return obj != null && obj === obj.window;
+};
+
+ window = (typeof window != 'undefined' && window.Math == Math)
+ ? window
+ : (typeof self != 'undefined' && self.Math == Math)
+ ? self
+ : Function('return this')()
+;
+
+$.api = $.fn.api = function(parameters) {
+
+ var
+ // use window context if none specified
+ $allModules = $.isFunction(this)
+ ? $(window)
+ : $(this),
+ moduleSelector = $allModules.selector || '',
+ time = new Date().getTime(),
+ performance = [],
+
+ query = arguments[0],
+ methodInvoked = (typeof query == 'string'),
+ queryArguments = [].slice.call(arguments, 1),
+
+ returnedValue
+ ;
+
+ $allModules
+ .each(function() {
+ var
+ settings = ( $.isPlainObject(parameters) )
+ ? $.extend(true, {}, $.fn.api.settings, parameters)
+ : $.extend({}, $.fn.api.settings),
+
+ // internal aliases
+ namespace = settings.namespace,
+ metadata = settings.metadata,
+ selector = settings.selector,
+ error = settings.error,
+ className = settings.className,
+
+ // define namespaces for modules
+ eventNamespace = '.' + namespace,
+ moduleNamespace = 'module-' + namespace,
+
+ // element that creates request
+ $module = $(this),
+ $form = $module.closest(selector.form),
+
+ // context used for state
+ $context = (settings.stateContext)
+ ? $(settings.stateContext)
+ : $module,
+
+ // request details
+ ajaxSettings,
+ requestSettings,
+ url,
+ data,
+ requestStartTime,
+
+ // standard module
+ element = this,
+ context = $context[0],
+ instance = $module.data(moduleNamespace),
+ module
+ ;
+
+ module = {
+
+ initialize: function() {
+ if(!methodInvoked) {
+ module.bind.events();
+ }
+ module.instantiate();
+ },
+
+ instantiate: function() {
+ module.verbose('Storing instance of module', module);
+ instance = module;
+ $module
+ .data(moduleNamespace, instance)
+ ;
+ },
+
+ destroy: function() {
+ module.verbose('Destroying previous module for', element);
+ $module
+ .removeData(moduleNamespace)
+ .off(eventNamespace)
+ ;
+ },
+
+ bind: {
+ events: function() {
+ var
+ triggerEvent = module.get.event()
+ ;
+ if( triggerEvent ) {
+ module.verbose('Attaching API events to element', triggerEvent);
+ $module
+ .on(triggerEvent + eventNamespace, module.event.trigger)
+ ;
+ }
+ else if(settings.on == 'now') {
+ module.debug('Querying API endpoint immediately');
+ module.query();
+ }
+ }
+ },
+
+ decode: {
+ json: function(response) {
+ if(response !== undefined && typeof response == 'string') {
+ try {
+ response = JSON.parse(response);
+ }
+ catch(e) {
+ // isnt json string
+ }
+ }
+ return response;
+ }
+ },
+
+ read: {
+ cachedResponse: function(url) {
+ var
+ response
+ ;
+ if(window.Storage === undefined) {
+ module.error(error.noStorage);
+ return;
+ }
+ response = sessionStorage.getItem(url);
+ module.debug('Using cached response', url, response);
+ response = module.decode.json(response);
+ return response;
+ }
+ },
+ write: {
+ cachedResponse: function(url, response) {
+ if(response && response === '') {
+ module.debug('Response empty, not caching', response);
+ return;
+ }
+ if(window.Storage === undefined) {
+ module.error(error.noStorage);
+ return;
+ }
+ if( $.isPlainObject(response) ) {
+ response = JSON.stringify(response);
+ }
+ sessionStorage.setItem(url, response);
+ module.verbose('Storing cached response for url', url, response);
+ }
+ },
+
+ query: function() {
+
+ if(module.is.disabled()) {
+ module.debug('Element is disabled API request aborted');
+ return;
+ }
+
+ if(module.is.loading()) {
+ if(settings.interruptRequests) {
+ module.debug('Interrupting previous request');
+ module.abort();
+ }
+ else {
+ module.debug('Cancelling request, previous request is still pending');
+ return;
+ }
+ }
+
+ // pass element metadata to url (value, text)
+ if(settings.defaultData) {
+ $.extend(true, settings.urlData, module.get.defaultData());
+ }
+
+ // Add form content
+ if(settings.serializeForm) {
+ settings.data = module.add.formData(settings.data);
+ }
+
+ // call beforesend and get any settings changes
+ requestSettings = module.get.settings();
+
+ // check if before send cancelled request
+ if(requestSettings === false) {
+ module.cancelled = true;
+ module.error(error.beforeSend);
+ return;
+ }
+ else {
+ module.cancelled = false;
+ }
+
+ // get url
+ url = module.get.templatedURL();
+
+ if(!url && !module.is.mocked()) {
+ module.error(error.missingURL);
+ return;
+ }
+
+ // replace variables
+ url = module.add.urlData( url );
+ // missing url parameters
+ if( !url && !module.is.mocked()) {
+ return;
+ }
+
+ requestSettings.url = settings.base + url;
+
+ // look for jQuery ajax parameters in settings
+ ajaxSettings = $.extend(true, {}, settings, {
+ type : settings.method || settings.type,
+ data : data,
+ url : settings.base + url,
+ beforeSend : settings.beforeXHR,
+ success : function() {},
+ failure : function() {},
+ complete : function() {}
+ });
+
+ module.debug('Querying URL', ajaxSettings.url);
+ module.verbose('Using AJAX settings', ajaxSettings);
+ if(settings.cache === 'local' && module.read.cachedResponse(url)) {
+ module.debug('Response returned from local cache');
+ module.request = module.create.request();
+ module.request.resolveWith(context, [ module.read.cachedResponse(url) ]);
+ return;
+ }
+
+ if( !settings.throttle ) {
+ module.debug('Sending request', data, ajaxSettings.method);
+ module.send.request();
+ }
+ else {
+ if(!settings.throttleFirstRequest && !module.timer) {
+ module.debug('Sending request', data, ajaxSettings.method);
+ module.send.request();
+ module.timer = setTimeout(function(){}, settings.throttle);
+ }
+ else {
+ module.debug('Throttling request', settings.throttle);
+ clearTimeout(module.timer);
+ module.timer = setTimeout(function() {
+ if(module.timer) {
+ delete module.timer;
+ }
+ module.debug('Sending throttled request', data, ajaxSettings.method);
+ module.send.request();
+ }, settings.throttle);
+ }
+ }
+
+ },
+
+ should: {
+ removeError: function() {
+ return ( settings.hideError === true || (settings.hideError === 'auto' && !module.is.form()) );
+ }
+ },
+
+ is: {
+ disabled: function() {
+ return ($module.filter(selector.disabled).length > 0);
+ },
+ expectingJSON: function() {
+ return settings.dataType === 'json' || settings.dataType === 'jsonp';
+ },
+ form: function() {
+ return $module.is('form') || $context.is('form');
+ },
+ mocked: function() {
+ return (settings.mockResponse || settings.mockResponseAsync || settings.response || settings.responseAsync);
+ },
+ input: function() {
+ return $module.is('input');
+ },
+ loading: function() {
+ return (module.request)
+ ? (module.request.state() == 'pending')
+ : false
+ ;
+ },
+ abortedRequest: function(xhr) {
+ if(xhr && xhr.readyState !== undefined && xhr.readyState === 0) {
+ module.verbose('XHR request determined to be aborted');
+ return true;
+ }
+ else {
+ module.verbose('XHR request was not aborted');
+ return false;
+ }
+ },
+ validResponse: function(response) {
+ if( (!module.is.expectingJSON()) || !$.isFunction(settings.successTest) ) {
+ module.verbose('Response is not JSON, skipping validation', settings.successTest, response);
+ return true;
+ }
+ module.debug('Checking JSON returned success', settings.successTest, response);
+ if( settings.successTest(response) ) {
+ module.debug('Response passed success test', response);
+ return true;
+ }
+ else {
+ module.debug('Response failed success test', response);
+ return false;
+ }
+ }
+ },
+
+ was: {
+ cancelled: function() {
+ return (module.cancelled || false);
+ },
+ succesful: function() {
+ module.verbose('This behavior will be deleted due to typo. Use "was successful" instead.');
+ return module.was.successful();
+ },
+ successful: function() {
+ return (module.request && module.request.state() == 'resolved');
+ },
+ failure: function() {
+ return (module.request && module.request.state() == 'rejected');
+ },
+ complete: function() {
+ return (module.request && (module.request.state() == 'resolved' || module.request.state() == 'rejected') );
+ }
+ },
+
+ add: {
+ urlData: function(url, urlData) {
+ var
+ requiredVariables,
+ optionalVariables
+ ;
+ if(url) {
+ requiredVariables = url.match(settings.regExp.required);
+ optionalVariables = url.match(settings.regExp.optional);
+ urlData = urlData || settings.urlData;
+ if(requiredVariables) {
+ module.debug('Looking for required URL variables', requiredVariables);
+ $.each(requiredVariables, function(index, templatedString) {
+ var
+ // allow legacy {$var} style
+ variable = (templatedString.indexOf('$') !== -1)
+ ? templatedString.substr(2, templatedString.length - 3)
+ : templatedString.substr(1, templatedString.length - 2),
+ value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
+ ? urlData[variable]
+ : ($module.data(variable) !== undefined)
+ ? $module.data(variable)
+ : ($context.data(variable) !== undefined)
+ ? $context.data(variable)
+ : urlData[variable]
+ ;
+ // remove value
+ if(value === undefined) {
+ module.error(error.requiredParameter, variable, url);
+ url = false;
+ return false;
+ }
+ else {
+ module.verbose('Found required variable', variable, value);
+ value = (settings.encodeParameters)
+ ? module.get.urlEncodedValue(value)
+ : value
+ ;
+ url = url.replace(templatedString, value);
+ }
+ });
+ }
+ if(optionalVariables) {
+ module.debug('Looking for optional URL variables', requiredVariables);
+ $.each(optionalVariables, function(index, templatedString) {
+ var
+ // allow legacy {/$var} style
+ variable = (templatedString.indexOf('$') !== -1)
+ ? templatedString.substr(3, templatedString.length - 4)
+ : templatedString.substr(2, templatedString.length - 3),
+ value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
+ ? urlData[variable]
+ : ($module.data(variable) !== undefined)
+ ? $module.data(variable)
+ : ($context.data(variable) !== undefined)
+ ? $context.data(variable)
+ : urlData[variable]
+ ;
+ // optional replacement
+ if(value !== undefined) {
+ module.verbose('Optional variable Found', variable, value);
+ url = url.replace(templatedString, value);
+ }
+ else {
+ module.verbose('Optional variable not found', variable);
+ // remove preceding slash if set
+ if(url.indexOf('/' + templatedString) !== -1) {
+ url = url.replace('/' + templatedString, '');
+ }
+ else {
+ url = url.replace(templatedString, '');
+ }
+ }
+ });
+ }
+ }
+ return url;
+ },
+ formData: function(data) {
+ var
+ canSerialize = ($.fn.serializeObject !== undefined),
+ formData = (canSerialize)
+ ? $form.serializeObject()
+ : $form.serialize(),
+ hasOtherData
+ ;
+ data = data || settings.data;
+ hasOtherData = $.isPlainObject(data);
+
+ if(hasOtherData) {
+ if(canSerialize) {
+ module.debug('Extending existing data with form data', data, formData);
+ data = $.extend(true, {}, data, formData);
+ }
+ else {
+ module.error(error.missingSerialize);
+ module.debug('Cant extend data. Replacing data with form data', data, formData);
+ data = formData;
+ }
+ }
+ else {
+ module.debug('Adding form data', formData);
+ data = formData;
+ }
+ return data;
+ }
+ },
+
+ send: {
+ request: function() {
+ module.set.loading();
+ module.request = module.create.request();
+ if( module.is.mocked() ) {
+ module.mockedXHR = module.create.mockedXHR();
+ }
+ else {
+ module.xhr = module.create.xhr();
+ }
+ settings.onRequest.call(context, module.request, module.xhr);
+ }
+ },
+
+ event: {
+ trigger: function(event) {
+ module.query();
+ if(event.type == 'submit' || event.type == 'click') {
+ event.preventDefault();
+ }
+ },
+ xhr: {
+ always: function() {
+ // nothing special
+ },
+ done: function(response, textStatus, xhr) {
+ var
+ context = this,
+ elapsedTime = (new Date().getTime() - requestStartTime),
+ timeLeft = (settings.loadingDuration - elapsedTime),
+ translatedResponse = ( $.isFunction(settings.onResponse) )
+ ? module.is.expectingJSON() && !settings.rawResponse
+ ? settings.onResponse.call(context, $.extend(true, {}, response))
+ : settings.onResponse.call(context, response)
+ : false
+ ;
+ timeLeft = (timeLeft > 0)
+ ? timeLeft
+ : 0
+ ;
+ if(translatedResponse) {
+ module.debug('Modified API response in onResponse callback', settings.onResponse, translatedResponse, response);
+ response = translatedResponse;
+ }
+ if(timeLeft > 0) {
+ module.debug('Response completed early delaying state change by', timeLeft);
+ }
+ setTimeout(function() {
+ if( module.is.validResponse(response) ) {
+ module.request.resolveWith(context, [response, xhr]);
+ }
+ else {
+ module.request.rejectWith(context, [xhr, 'invalid']);
+ }
+ }, timeLeft);
+ },
+ fail: function(xhr, status, httpMessage) {
+ var
+ context = this,
+ elapsedTime = (new Date().getTime() - requestStartTime),
+ timeLeft = (settings.loadingDuration - elapsedTime)
+ ;
+ timeLeft = (timeLeft > 0)
+ ? timeLeft
+ : 0
+ ;
+ if(timeLeft > 0) {
+ module.debug('Response completed early delaying state change by', timeLeft);
+ }
+ setTimeout(function() {
+ if( module.is.abortedRequest(xhr) ) {
+ module.request.rejectWith(context, [xhr, 'aborted', httpMessage]);
+ }
+ else {
+ module.request.rejectWith(context, [xhr, 'error', status, httpMessage]);
+ }
+ }, timeLeft);
+ }
+ },
+ request: {
+ done: function(response, xhr) {
+ module.debug('Successful API Response', response);
+ if(settings.cache === 'local' && url) {
+ module.write.cachedResponse(url, response);
+ module.debug('Saving server response locally', module.cache);
+ }
+ settings.onSuccess.call(context, response, $module, xhr);
+ },
+ complete: function(firstParameter, secondParameter) {
+ var
+ xhr,
+ response
+ ;
+ // have to guess callback parameters based on request success
+ if( module.was.successful() ) {
+ response = firstParameter;
+ xhr = secondParameter;
+ }
+ else {
+ xhr = firstParameter;
+ response = module.get.responseFromXHR(xhr);
+ }
+ module.remove.loading();
+ settings.onComplete.call(context, response, $module, xhr);
+ },
+ fail: function(xhr, status, httpMessage) {
+ var
+ // pull response from xhr if available
+ response = module.get.responseFromXHR(xhr),
+ errorMessage = module.get.errorFromRequest(response, status, httpMessage)
+ ;
+ if(status == 'aborted') {
+ module.debug('XHR Aborted (Most likely caused by page navigation or CORS Policy)', status, httpMessage);
+ settings.onAbort.call(context, status, $module, xhr);
+ return true;
+ }
+ else if(status == 'invalid') {
+ module.debug('JSON did not pass success test. A server-side error has most likely occurred', response);
+ }
+ else if(status == 'error') {
+ if(xhr !== undefined) {
+ module.debug('XHR produced a server error', status, httpMessage);
+ // make sure we have an error to display to console
+ if( (xhr.status < 200 || xhr.status >= 300) && httpMessage !== undefined && httpMessage !== '') {
+ module.error(error.statusMessage + httpMessage, ajaxSettings.url);
+ }
+ settings.onError.call(context, errorMessage, $module, xhr);
+ }
+ }
+
+ if(settings.errorDuration && status !== 'aborted') {
+ module.debug('Adding error state');
+ module.set.error();
+ if( module.should.removeError() ) {
+ setTimeout(module.remove.error, settings.errorDuration);
+ }
+ }
+ module.debug('API Request failed', errorMessage, xhr);
+ settings.onFailure.call(context, response, $module, xhr);
+ }
+ }
+ },
+
+ create: {
+
+ request: function() {
+ // api request promise
+ return $.Deferred()
+ .always(module.event.request.complete)
+ .done(module.event.request.done)
+ .fail(module.event.request.fail)
+ ;
+ },
+
+ mockedXHR: function () {
+ var
+ // xhr does not simulate these properties of xhr but must return them
+ textStatus = false,
+ status = false,
+ httpMessage = false,
+ responder = settings.mockResponse || settings.response,
+ asyncResponder = settings.mockResponseAsync || settings.responseAsync,
+ asyncCallback,
+ response,
+ mockedXHR
+ ;
+
+ mockedXHR = $.Deferred()
+ .always(module.event.xhr.complete)
+ .done(module.event.xhr.done)
+ .fail(module.event.xhr.fail)
+ ;
+
+ if(responder) {
+ if( $.isFunction(responder) ) {
+ module.debug('Using specified synchronous callback', responder);
+ response = responder.call(context, requestSettings);
+ }
+ else {
+ module.debug('Using settings specified response', responder);
+ response = responder;
+ }
+ // simulating response
+ mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
+ }
+ else if( $.isFunction(asyncResponder) ) {
+ asyncCallback = function(response) {
+ module.debug('Async callback returned response', response);
+
+ if(response) {
+ mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
+ }
+ else {
+ mockedXHR.rejectWith(context, [{ responseText: response }, status, httpMessage]);
+ }
+ };
+ module.debug('Using specified async response callback', asyncResponder);
+ asyncResponder.call(context, requestSettings, asyncCallback);
+ }
+ return mockedXHR;
+ },
+
+ xhr: function() {
+ var
+ xhr
+ ;
+ // ajax request promise
+ xhr = $.ajax(ajaxSettings)
+ .always(module.event.xhr.always)
+ .done(module.event.xhr.done)
+ .fail(module.event.xhr.fail)
+ ;
+ module.verbose('Created server request', xhr, ajaxSettings);
+ return xhr;
+ }
+ },
+
+ set: {
+ error: function() {
+ module.verbose('Adding error state to element', $context);
+ $context.addClass(className.error);
+ },
+ loading: function() {
+ module.verbose('Adding loading state to element', $context);
+ $context.addClass(className.loading);
+ requestStartTime = new Date().getTime();
+ }
+ },
+
+ remove: {
+ error: function() {
+ module.verbose('Removing error state from element', $context);
+ $context.removeClass(className.error);
+ },
+ loading: function() {
+ module.verbose('Removing loading state from element', $context);
+ $context.removeClass(className.loading);
+ }
+ },
+
+ get: {
+ responseFromXHR: function(xhr) {
+ return $.isPlainObject(xhr)
+ ? (module.is.expectingJSON())
+ ? module.decode.json(xhr.responseText)
+ : xhr.responseText
+ : false
+ ;
+ },
+ errorFromRequest: function(response, status, httpMessage) {
+ return ($.isPlainObject(response) && response.error !== undefined)
+ ? response.error // use json error message
+ : (settings.error[status] !== undefined) // use server error message
+ ? settings.error[status]
+ : httpMessage
+ ;
+ },
+ request: function() {
+ return module.request || false;
+ },
+ xhr: function() {
+ return module.xhr || false;
+ },
+ settings: function() {
+ var
+ runSettings
+ ;
+ runSettings = settings.beforeSend.call($module, settings);
+ if(runSettings) {
+ if(runSettings.success !== undefined) {
+ module.debug('Legacy success callback detected', runSettings);
+ module.error(error.legacyParameters, runSettings.success);
+ runSettings.onSuccess = runSettings.success;
+ }
+ if(runSettings.failure !== undefined) {
+ module.debug('Legacy failure callback detected', runSettings);
+ module.error(error.legacyParameters, runSettings.failure);
+ runSettings.onFailure = runSettings.failure;
+ }
+ if(runSettings.complete !== undefined) {
+ module.debug('Legacy complete callback detected', runSettings);
+ module.error(error.legacyParameters, runSettings.complete);
+ runSettings.onComplete = runSettings.complete;
+ }
+ }
+ if(runSettings === undefined) {
+ module.error(error.noReturnedValue);
+ }
+ if(runSettings === false) {
+ return runSettings;
+ }
+ return (runSettings !== undefined)
+ ? $.extend(true, {}, runSettings)
+ : $.extend(true, {}, settings)
+ ;
+ },
+ urlEncodedValue: function(value) {
+ // GITEA-PATCH: always encode the value.
+ // Old code does "decodeURIComponent" first to guess whether the value is encoded, it is not right.
+ return window.encodeURIComponent(value);
+ },
+ defaultData: function() {
+ var
+ data = {}
+ ;
+ if( !$.isWindow(element) ) {
+ if( module.is.input() ) {
+ data.value = $module.val();
+ }
+ else if( module.is.form() ) {
+
+ }
+ else {
+ data.text = $module.text();
+ }
+ }
+ return data;
+ },
+ event: function() {
+ if( $.isWindow(element) || settings.on == 'now' ) {
+ module.debug('API called without element, no events attached');
+ return false;
+ }
+ else if(settings.on == 'auto') {
+ if( $module.is('input') ) {
+ return (element.oninput !== undefined)
+ ? 'input'
+ : (element.onpropertychange !== undefined)
+ ? 'propertychange'
+ : 'keyup'
+ ;
+ }
+ else if( $module.is('form') ) {
+ return 'submit';
+ }
+ else {
+ return 'click';
+ }
+ }
+ else {
+ return settings.on;
+ }
+ },
+ templatedURL: function(action) {
+ action = action || $module.data(metadata.action) || settings.action || false;
+ url = $module.data(metadata.url) || settings.url || false;
+ if(url) {
+ module.debug('Using specified url', url);
+ return url;
+ }
+ if(action) {
+ module.debug('Looking up url for action', action, settings.api);
+ if(settings.api[action] === undefined && !module.is.mocked()) {
+ module.error(error.missingAction, settings.action, settings.api);
+ return;
+ }
+ url = settings.api[action];
+ }
+ else if( module.is.form() ) {
+ url = $module.attr('action') || $context.attr('action') || false;
+ module.debug('No url or action specified, defaulting to form action', url);
+ }
+ return url;
+ }
+ },
+
+ abort: function() {
+ var
+ xhr = module.get.xhr()
+ ;
+ if( xhr && xhr.state() !== 'resolved') {
+ module.debug('Cancelling API request');
+ xhr.abort();
+ }
+ },
+
+ // reset state
+ reset: function() {
+ module.remove.error();
+ module.remove.loading();
+ },
+
+ setting: function(name, value) {
+ module.debug('Changing setting', name, value);
+ if( $.isPlainObject(name) ) {
+ $.extend(true, settings, name);
+ }
+ else if(value !== undefined) {
+ if($.isPlainObject(settings[name])) {
+ $.extend(true, settings[name], value);
+ }
+ else {
+ settings[name] = value;
+ }
+ }
+ else {
+ return settings[name];
+ }
+ },
+ internal: function(name, value) {
+ if( $.isPlainObject(name) ) {
+ $.extend(true, module, name);
+ }
+ else if(value !== undefined) {
+ module[name] = value;
+ }
+ else {
+ return module[name];
+ }
+ },
+ debug: function() {
+ if(!settings.silent && settings.debug) {
+ if(settings.performance) {
+ module.performance.log(arguments);
+ }
+ else {
+ module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
+ module.debug.apply(console, arguments);
+ }
+ }
+ },
+ verbose: function() {
+ if(!settings.silent && settings.verbose && settings.debug) {
+ if(settings.performance) {
+ module.performance.log(arguments);
+ }
+ else {
+ module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
+ module.verbose.apply(console, arguments);
+ }
+ }
+ },
+ error: function() {
+ if(!settings.silent) {
+ module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
+ module.error.apply(console, arguments);
+ }
+ },
+ performance: {
+ log: function(message) {
+ var
+ currentTime,
+ executionTime,
+ previousTime
+ ;
+ if(settings.performance) {
+ currentTime = new Date().getTime();
+ previousTime = time || currentTime;
+ executionTime = currentTime - previousTime;
+ time = currentTime;
+ performance.push({
+ 'Name' : message[0],
+ 'Arguments' : [].slice.call(message, 1) || '',
+ //'Element' : element,
+ 'Execution Time' : executionTime
+ });
+ }
+ clearTimeout(module.performance.timer);
+ module.performance.timer = setTimeout(module.performance.display, 500);
+ },
+ display: function() {
+ var
+ title = settings.name + ':',
+ totalTime = 0
+ ;
+ time = false;
+ clearTimeout(module.performance.timer);
+ $.each(performance, function(index, data) {
+ totalTime += data['Execution Time'];
+ });
+ title += ' ' + totalTime + 'ms';
+ if(moduleSelector) {
+ title += ' \'' + moduleSelector + '\'';
+ }
+ if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
+ console.groupCollapsed(title);
+ if(console.table) {
+ console.table(performance);
+ }
+ else {
+ $.each(performance, function(index, data) {
+ console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
+ });
+ }
+ console.groupEnd();
+ }
+ performance = [];
+ }
+ },
+ invoke: function(query, passedArguments, context) {
+ var
+ object = instance,
+ maxDepth,
+ found,
+ response
+ ;
+ passedArguments = passedArguments || queryArguments;
+ context = element || context;
+ if(typeof query == 'string' && object !== undefined) {
+ query = query.split(/[\. ]/);
+ maxDepth = query.length - 1;
+ $.each(query, function(depth, value) {
+ var camelCaseValue = (depth != maxDepth)
+ ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
+ : query
+ ;
+ if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
+ object = object[camelCaseValue];
+ }
+ else if( object[camelCaseValue] !== undefined ) {
+ found = object[camelCaseValue];
+ return false;
+ }
+ else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
+ object = object[value];
+ }
+ else if( object[value] !== undefined ) {
+ found = object[value];
+ return false;
+ }
+ else {
+ module.error(error.method, query);
+ return false;
+ }
+ });
+ }
+ if ( $.isFunction( found ) ) {
+ response = found.apply(context, passedArguments);
+ }
+ else if(found !== undefined) {
+ response = found;
+ }
+ if(Array.isArray(returnedValue)) {
+ returnedValue.push(response);
+ }
+ else if(returnedValue !== undefined) {
+ returnedValue = [returnedValue, response];
+ }
+ else if(response !== undefined) {
+ returnedValue = response;
+ }
+ return found;
+ }
+ };
+
+ if(methodInvoked) {
+ if(instance === undefined) {
+ module.initialize();
+ }
+ module.invoke(query);
+ }
+ else {
+ if(instance !== undefined) {
+ instance.invoke('destroy');
+ }
+ module.initialize();
+ }
+ })
+ ;
+
+ return (returnedValue !== undefined)
+ ? returnedValue
+ : this
+ ;
+};
+
+$.api.settings = {
+
+ name : 'API',
+ namespace : 'api',
+
+ debug : false,
+ verbose : false,
+ performance : true,
+
+ // object containing all templates endpoints
+ api : {},
+
+ // whether to cache responses
+ cache : true,
+
+ // whether new requests should abort previous requests
+ interruptRequests : true,
+
+ // event binding
+ on : 'auto',
+
+ // context for applying state classes
+ stateContext : false,
+
+ // duration for loading state
+ loadingDuration : 0,
+
+ // whether to hide errors after a period of time
+ hideError : 'auto',
+
+ // duration for error state
+ errorDuration : 2000,
+
+ // whether parameters should be encoded with encodeURIComponent
+ encodeParameters : true,
+
+ // API action to use
+ action : false,
+
+ // templated URL to use
+ url : false,
+
+ // base URL to apply to all endpoints
+ base : '',
+
+ // data that will
+ urlData : {},
+
+ // whether to add default data to url data
+ defaultData : true,
+
+ // whether to serialize closest form
+ serializeForm : false,
+
+ // how long to wait before request should occur
+ throttle : 0,
+
+ // whether to throttle first request or only repeated
+ throttleFirstRequest : true,
+
+ // standard ajax settings
+ method : 'get',
+ data : {},
+ dataType : 'json',
+
+ // mock response
+ mockResponse : false,
+ mockResponseAsync : false,
+
+ // aliases for mock
+ response : false,
+ responseAsync : false,
+
+// whether onResponse should work with response value without force converting into an object
+ rawResponse : false,
+
+ // callbacks before request
+ beforeSend : function(settings) { return settings; },
+ beforeXHR : function(xhr) {},
+ onRequest : function(promise, xhr) {},
+
+ // after request
+ onResponse : false, // function(response) { },
+
+ // response was successful, if JSON passed validation
+ onSuccess : function(response, $module) {},
+
+ // request finished without aborting
+ onComplete : function(response, $module) {},
+
+ // failed JSON success test
+ onFailure : function(response, $module) {},
+
+ // server error
+ onError : function(errorMessage, $module) {},
+
+ // request aborted
+ onAbort : function(errorMessage, $module) {},
+
+ successTest : false,
+
+ // errors
+ error : {
+ beforeSend : 'The before send function has aborted the request',
+ error : 'There was an error with your request',
+ exitConditions : 'API Request Aborted. Exit conditions met',
+ JSONParse : 'JSON could not be parsed during error handling',
+ legacyParameters : 'You are using legacy API success callback names',
+ method : 'The method you called is not defined',
+ missingAction : 'API action used but no url was defined',
+ missingSerialize : 'jquery-serialize-object is required to add form data to an existing data object',
+ missingURL : 'No URL specified for api event',
+ noReturnedValue : 'The beforeSend callback must return a settings object, beforeSend ignored.',
+ noStorage : 'Caching responses locally requires session storage',
+ parseError : 'There was an error parsing your request',
+ requiredParameter : 'Missing a required URL parameter: ',
+ statusMessage : 'Server gave an error: ',
+ timeout : 'Your request timed out'
+ },
+
+ regExp : {
+ required : /\{\$*[A-z0-9]+\}/g,
+ optional : /\{\/\$*[A-z0-9]+\}/g,
+ },
+
+ className: {
+ loading : 'loading',
+ error : 'error'
+ },
+
+ selector: {
+ disabled : '.disabled',
+ form : 'form'
+ },
+
+ metadata: {
+ action : 'action',
+ url : 'url'
+ }
+};
+
+
+
+})( jQuery, window, document );
diff --git a/web_src/fomantic/build/components/dropdown.css b/web_src/fomantic/build/components/dropdown.css
new file mode 100644
index 0000000000..b7b35a2f05
--- /dev/null
+++ b/web_src/fomantic/build/components/dropdown.css
@@ -0,0 +1,1755 @@
+/*!
+ * # Fomantic-UI - Dropdown
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+ Dropdown
+*******************************/
+
+.ui.dropdown {
+ cursor: pointer;
+ position: relative;
+ display: inline-block;
+ outline: none;
+ text-align: left;
+ transition: box-shadow 0.1s ease, width 0.1s ease;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+
+/*******************************
+ Content
+*******************************/
+
+
+/*--------------
+ Menu
+---------------*/
+
+.ui.dropdown .menu {
+ cursor: auto;
+ position: absolute;
+ display: none;
+ outline: none;
+ top: 100%;
+ min-width: -moz-max-content;
+ min-width: max-content;
+ margin: 0;
+ padding: 0 0;
+ background: #FFFFFF;
+ font-size: 1em;
+ text-shadow: none;
+ text-align: left;
+ box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
+ border: 1px solid rgba(34, 36, 38, 0.15);
+ border-radius: 0.28571429rem;
+ transition: opacity 0.1s ease;
+ z-index: 11;
+ will-change: transform, opacity;
+}
+.ui.dropdown .menu > * {
+ white-space: nowrap;
+}
+
+/*--------------
+ Hidden Input
+---------------*/
+
+.ui.dropdown > input:not(.search):first-child,
+.ui.dropdown > select {
+ display: none !important;
+}
+
+/*--------------
+ Dropdown Icon
+---------------*/
+
+.ui.dropdown:not(.labeled) > .dropdown.icon {
+ position: relative;
+ width: auto;
+ font-size: 0.85714286em;
+ margin: 0 0 0 1em;
+}
+.ui.dropdown .menu > .item .dropdown.icon {
+ width: auto;
+ float: right;
+ margin: 0em 0 0 1em;
+}
+.ui.dropdown .menu > .item .dropdown.icon + .text {
+ margin-right: 1em;
+}
+
+/*--------------
+ Text
+---------------*/
+
+.ui.dropdown > .text {
+ display: inline-block;
+ transition: none;
+}
+
+/*--------------
+ Menu Item
+---------------*/
+
+.ui.dropdown .menu > .item {
+ position: relative;
+ cursor: pointer;
+ display: block;
+ border: none;
+ height: auto;
+ min-height: 2.57142857rem;
+ text-align: left;
+ border-top: none;
+ line-height: 1em;
+ font-size: 1rem;
+ color: rgba(0, 0, 0, 0.87);
+ padding: 0.78571429rem 1.14285714rem !important;
+ text-transform: none;
+ font-weight: normal;
+ box-shadow: none;
+ -webkit-touch-callout: none;
+}
+.ui.dropdown .menu > .item:first-child {
+ border-top-width: 0;
+}
+.ui.dropdown .menu > .item.vertical {
+ display: flex;
+ flex-direction: column-reverse;
+}
+
+/*--------------
+ Floated Content
+---------------*/
+
+.ui.dropdown > .text > [class*="right floated"],
+.ui.dropdown .menu .item > [class*="right floated"] {
+ float: right !important;
+ margin-right: 0 !important;
+ margin-left: 1em !important;
+}
+.ui.dropdown > .text > [class*="left floated"],
+.ui.dropdown .menu .item > [class*="left floated"] {
+ float: left !important;
+ margin-left: 0 !important;
+ margin-right: 1em !important;
+}
+.ui.dropdown .menu .item > i.icon.floated,
+.ui.dropdown .menu .item > .flag.floated,
+.ui.dropdown .menu .item > .image.floated,
+.ui.dropdown .menu .item > img.floated {
+ margin-top: 0em;
+}
+
+/*--------------
+ Menu Divider
+---------------*/
+
+.ui.dropdown .menu > .header {
+ margin: 1rem 0 0.75rem;
+ padding: 0 1.14285714rem;
+ font-weight: 500;
+ text-transform: uppercase;
+}
+.ui.dropdown .menu > .header:not(.ui) {
+ color: rgba(0, 0, 0, 0.85);
+ font-size: 0.78571429em;
+}
+.ui.dropdown .menu > .divider {
+ border-top: 1px solid rgba(34, 36, 38, 0.1);
+ height: 0;
+ margin: 0.5em 0;
+}
+.ui.dropdown .menu > .horizontal.divider {
+ border-top: none;
+}
+.ui.dropdown.dropdown .menu > .input {
+ width: auto;
+ display: flex;
+ margin: 1.14285714rem 0.78571429rem;
+ min-width: 10rem;
+}
+.ui.dropdown .menu > .header + .input {
+ margin-top: 0;
+}
+.ui.dropdown .menu > .input:not(.transparent) input {
+ padding: 0.5em 1em;
+}
+.ui.dropdown .menu > .input:not(.transparent) .button,
+.ui.dropdown .menu > .input:not(.transparent) i.icon,
+.ui.dropdown .menu > .input:not(.transparent) .label {
+ padding-top: 0.5em;
+ padding-bottom: 0.5em;
+}
+
+/*-----------------
+ Item Description
+-------------------*/
+
+.ui.dropdown > .text > .description,
+.ui.dropdown .menu > .item > .description {
+ float: right;
+ margin: 0 0 0 1em;
+ color: rgba(0, 0, 0, 0.4);
+}
+.ui.dropdown .menu > .item.vertical > .description {
+ margin: 0;
+}
+
+/*-----------------
+ Item Text
+-------------------*/
+
+.ui.dropdown .menu > .item.vertical > .text {
+ margin-bottom: 0.25em;
+}
+
+/*-----------------
+ Message
+-------------------*/
+
+.ui.dropdown .menu > .message {
+ padding: 0.78571429rem 1.14285714rem;
+ font-weight: normal;
+}
+.ui.dropdown .menu > .message:not(.ui) {
+ color: rgba(0, 0, 0, 0.4);
+}
+
+/*--------------
+ Sub Menu
+---------------*/
+
+.ui.dropdown .menu .menu {
+ top: 0;
+ left: 100%;
+ right: auto;
+ margin: 0 -0.5em !important;
+ border-radius: 0.28571429rem !important;
+ z-index: 21 !important;
+}
+
+/* Hide Arrow */
+.ui.dropdown .menu .menu:after {
+ display: none;
+}
+
+/*--------------
+ Sub Elements
+---------------*/
+
+
+/* Icons / Flags / Labels / Image */
+.ui.dropdown > .text > i.icon,
+.ui.dropdown > .text > .label,
+.ui.dropdown > .text > .flag,
+.ui.dropdown > .text > img,
+.ui.dropdown > .text > .image {
+ margin-top: 0em;
+}
+.ui.dropdown .menu > .item > i.icon,
+.ui.dropdown .menu > .item > .label,
+.ui.dropdown .menu > .item > .flag,
+.ui.dropdown .menu > .item > .image,
+.ui.dropdown .menu > .item > img {
+ margin-top: 0em;
+}
+.ui.dropdown > .text > i.icon,
+.ui.dropdown > .text > .label,
+.ui.dropdown > .text > .flag,
+.ui.dropdown > .text > img,
+.ui.dropdown > .text > .image,
+.ui.dropdown .menu > .item > i.icon,
+.ui.dropdown .menu > .item > .label,
+.ui.dropdown .menu > .item > .flag,
+.ui.dropdown .menu > .item > .image,
+.ui.dropdown .menu > .item > img {
+ margin-left: 0;
+ float: none;
+ margin-right: 0.78571429rem;
+}
+
+/*--------------
+ Image
+---------------*/
+
+.ui.dropdown > .text > img,
+.ui.dropdown > .text > .image:not(.icon),
+.ui.dropdown .menu > .item > .image:not(.icon),
+.ui.dropdown .menu > .item > img {
+ display: inline-block;
+ vertical-align: top;
+ width: auto;
+ margin-top: -0.5em;
+ margin-bottom: -0.5em;
+ max-height: 2em;
+}
+
+
+/*******************************
+ Coupling
+*******************************/
+
+
+/*--------------
+ Menu
+---------------*/
+
+
+/* Remove Menu Item Divider */
+.ui.dropdown .ui.menu > .item:before,
+.ui.menu .ui.dropdown .menu > .item:before {
+ display: none;
+}
+
+/* Prevent Menu Item Border */
+.ui.menu .ui.dropdown .menu .active.item {
+ border-left: none;
+}
+
+/* Automatically float dropdown menu right on last menu item */
+.ui.menu .right.menu .dropdown:last-child > .menu:not(.left),
+.ui.menu .right.dropdown.item > .menu:not(.left),
+.ui.buttons > .ui.dropdown:last-child > .menu:not(.left) {
+ left: auto;
+ right: 0;
+}
+
+/*--------------
+ Label
+ ---------------*/
+
+
+/* Dropdown Menu */
+.ui.label.dropdown .menu {
+ min-width: 100%;
+}
+
+/*--------------
+ Button
+ ---------------*/
+
+
+/* No Margin On Icon Button */
+.ui.dropdown.icon.button > .dropdown.icon {
+ margin: 0;
+}
+.ui.button.dropdown .menu {
+ min-width: 100%;
+}
+
+
+/*******************************
+ Types
+*******************************/
+
+select.ui.dropdown {
+ height: 38px;
+ padding: 0.5em;
+ border: 1px solid rgba(34, 36, 38, 0.15);
+ visibility: visible;
+}
+
+/*--------------
+ Selection
+ ---------------*/
+
+
+/* Displays like a select box */
+.ui.selection.dropdown {
+ cursor: pointer;
+ word-wrap: break-word;
+ line-height: 1em;
+ white-space: normal;
+ outline: 0;
+ transform: rotateZ(0deg);
+ min-width: 14em;
+ min-height: 2.71428571em;
+ background: #FFFFFF;
+ display: inline-block;
+ padding: 0.78571429em 3.2em 0.78571429em 1em;
+ color: rgba(0, 0, 0, 0.87);
+ box-shadow: none;
+ border: 1px solid rgba(34, 36, 38, 0.15);
+ border-radius: 0.28571429rem;
+ transition: box-shadow 0.1s ease, width 0.1s ease;
+}
+.ui.selection.dropdown.visible,
+.ui.selection.dropdown.active {
+ z-index: 10;
+}
+.ui.selection.dropdown > .search.icon,
+.ui.selection.dropdown > .delete.icon,
+.ui.selection.dropdown > .dropdown.icon {
+ cursor: pointer;
+ position: absolute;
+ width: auto;
+ height: auto;
+ line-height: 1.21428571em;
+ top: 0.78571429em;
+ right: 1em;
+ z-index: 3;
+ margin: -0.78571429em;
+ padding: 0.91666667em;
+ opacity: 0.8;
+ transition: opacity 0.1s ease;
+}
+
+/* Compact */
+.ui.compact.selection.dropdown {
+ min-width: 0;
+}
+
+/* Selection Menu */
+.ui.selection.dropdown .menu {
+ overflow-x: hidden;
+ overflow-y: auto;
+ backface-visibility: hidden;
+ -webkit-overflow-scrolling: touch;
+ border-top-width: 0 !important;
+ width: auto;
+ outline: none;
+ margin: 0 -1px;
+ min-width: calc(100% + 2px);
+ width: calc(100% + 2px);
+ border-radius: 0 0 0.28571429rem 0.28571429rem;
+ box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
+ transition: opacity 0.1s ease;
+}
+.ui.selection.dropdown .menu:after,
+.ui.selection.dropdown .menu:before {
+ display: none;
+}
+
+/*--------------
+ Message
+ ---------------*/
+
+.ui.selection.dropdown .menu > .message {
+ padding: 0.78571429rem 1.14285714rem;
+}
+@media only screen and (max-width: 767.98px) {
+ .ui.selection.dropdown.short .menu {
+ max-height: 6.01071429rem;
+ }
+ .ui.selection.dropdown[class*="very short"] .menu {
+ max-height: 4.00714286rem;
+ }
+ .ui.selection.dropdown .menu {
+ max-height: 8.01428571rem;
+ }
+ .ui.selection.dropdown.long .menu {
+ max-height: 16.02857143rem;
+ }
+ .ui.selection.dropdown[class*="very long"] .menu {
+ max-height: 24.04285714rem;
+ }
+}
+@media only screen and (min-width: 768px) {
+ .ui.selection.dropdown.short .menu {
+ max-height: 8.01428571rem;
+ }
+ .ui.selection.dropdown[class*="very short"] .menu {
+ max-height: 5.34285714rem;
+ }
+ .ui.selection.dropdown .menu {
+ max-height: 10.68571429rem;
+ }
+ .ui.selection.dropdown.long .menu {
+ max-height: 21.37142857rem;
+ }
+ .ui.selection.dropdown[class*="very long"] .menu {
+ max-height: 32.05714286rem;
+ }
+}
+@media only screen and (min-width: 992px) {
+ .ui.selection.dropdown.short .menu {
+ max-height: 12.02142857rem;
+ }
+ .ui.selection.dropdown[class*="very short"] .menu {
+ max-height: 8.01428571rem;
+ }
+ .ui.selection.dropdown .menu {
+ max-height: 16.02857143rem;
+ }
+ .ui.selection.dropdown.long .menu {
+ max-height: 32.05714286rem;
+ }
+ .ui.selection.dropdown[class*="very long"] .menu {
+ max-height: 48.08571429rem;
+ }
+}
+@media only screen and (min-width: 1920px) {
+ .ui.selection.dropdown.short .menu {
+ max-height: 16.02857143rem;
+ }
+ .ui.selection.dropdown[class*="very short"] .menu {
+ max-height: 10.68571429rem;
+ }
+ .ui.selection.dropdown .menu {
+ max-height: 21.37142857rem;
+ }
+ .ui.selection.dropdown.long .menu {
+ max-height: 42.74285714rem;
+ }
+ .ui.selection.dropdown[class*="very long"] .menu {
+ max-height: 64.11428571rem;
+ }
+}
+
+/* Menu Item */
+.ui.selection.dropdown .menu > .item {
+ border-top: 1px solid #FAFAFA;
+ padding: 0.78571429rem 1.14285714rem !important;
+ white-space: normal;
+ word-wrap: normal;
+}
+
+/* User Item */
+.ui.selection.dropdown .menu > .hidden.addition.item {
+ display: none;
+}
+
+/* Hover */
+.ui.selection.dropdown:hover {
+ border-color: rgba(34, 36, 38, 0.35);
+ box-shadow: none;
+}
+
+/* Active */
+.ui.selection.active.dropdown {
+ border-color: #96C8DA;
+ box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
+}
+.ui.selection.active.dropdown .menu {
+ border-color: #96C8DA;
+ box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
+}
+
+/* Focus */
+.ui.selection.dropdown:focus {
+ border-color: #96C8DA;
+ box-shadow: none;
+}
+.ui.selection.dropdown:focus .menu {
+ border-color: #96C8DA;
+ box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
+}
+
+/* Visible */
+.ui.selection.visible.dropdown > .text:not(.default) {
+ font-weight: normal;
+ color: rgba(0, 0, 0, 0.8);
+}
+
+/* Visible Hover */
+.ui.selection.active.dropdown:hover {
+ border-color: #96C8DA;
+ box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
+}
+.ui.selection.active.dropdown:hover .menu {
+ border-color: #96C8DA;
+ box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
+}
+
+/* Dropdown Icon */
+.ui.active.selection.dropdown > .dropdown.icon,
+.ui.visible.selection.dropdown > .dropdown.icon {
+ opacity: '';
+ z-index: 3;
+}
+
+/* Connecting Border */
+.ui.active.selection.dropdown {
+ border-bottom-left-radius: 0 !important;
+ border-bottom-right-radius: 0 !important;
+}
+
+/* Empty Connecting Border */
+.ui.active.empty.selection.dropdown {
+ border-radius: 0.28571429rem !important;
+ box-shadow: none !important;
+}
+.ui.active.empty.selection.dropdown .menu {
+ border: none !important;
+ box-shadow: none !important;
+}
+
+/* CSS specific to iOS devices or firefox mobile only */
+@supports (-webkit-touch-callout: none) or (-webkit-overflow-scrolling: touch) or (-moz-appearance:none) {
+ @media (-moz-touch-enabled), (pointer: coarse) {
+ .ui.dropdown .scrollhint.menu:not(.hidden):before {
+ animation: scrollhint 2s ease 2;
+ content: '';
+ z-index: 15;
+ display: block;
+ position: absolute;
+ opacity: 0;
+ right: 0.25em;
+ top: 0;
+ height: 100%;
+ border-right: 0.25em solid;
+ border-left: 0;
+ -o-border-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0)) 1 100%;
+ border-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0)) 1 100%;
+ }
+ .ui.inverted.dropdown .scrollhint.menu:not(.hidden):before {
+ -o-border-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.75), rgba(255, 255, 255, 0)) 1 100%;
+ border-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.75), rgba(255, 255, 255, 0)) 1 100%;
+ }
+ @keyframes scrollhint {
+ 0% {
+ opacity: 1;
+ top: 100%;
+ }
+ 100% {
+ opacity: 0;
+ top: 0;
+ }
+ }
+ }
+}
+
+/*--------------
+ Searchable
+ ---------------*/
+
+
+/* Search Selection */
+.ui.search.dropdown {
+ min-width: '';
+}
+
+/* Search Dropdown */
+.ui.search.dropdown > input.search {
+ background: none transparent !important;
+ border: none !important;
+ box-shadow: none !important;
+ cursor: text;
+ top: 0;
+ left: 1px;
+ width: 100%;
+ outline: none;
+ -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
+ padding: inherit;
+}
+
+/* Text Layering */
+.ui.search.dropdown > input.search {
+ position: absolute;
+ z-index: 2;
+}
+.ui.search.dropdown > .text {
+ cursor: text;
+ position: relative;
+ left: 1px;
+ z-index: auto;
+}
+
+/* Search Selection */
+.ui.search.selection.dropdown > input.search {
+ line-height: 1.21428571em;
+ padding: 0.67857143em 3.2em 0.67857143em 1em;
+}
+
+/* Used to size multi select input to character width */
+.ui.search.selection.dropdown > span.sizer {
+ line-height: 1.21428571em;
+ padding: 0.67857143em 3.2em 0.67857143em 1em;
+ display: none;
+ white-space: pre;
+}
+
+/* Active/Visible Search */
+.ui.search.dropdown.active > input.search,
+.ui.search.dropdown.visible > input.search {
+ cursor: auto;
+}
+.ui.search.dropdown.active > .text,
+.ui.search.dropdown.visible > .text {
+ pointer-events: none;
+}
+
+/* Filtered Text */
+.ui.active.search.dropdown input.search:focus + .text i.icon,
+.ui.active.search.dropdown input.search:focus + .text .flag {
+ opacity: var(--opacity-disabled);
+}
+.ui.active.search.dropdown input.search:focus + .text {
+ color: rgba(115, 115, 115, 0.87) !important;
+}
+.ui.search.dropdown.button > span.sizer {
+ display: none;
+}
+
+/* Search Menu */
+.ui.search.dropdown .menu {
+ overflow-x: hidden;
+ overflow-y: auto;
+ backface-visibility: hidden;
+ -webkit-overflow-scrolling: touch;
+}
+@media only screen and (max-width: 767.98px) {
+ .ui.search.dropdown .menu {
+ max-height: 8.01428571rem;
+ }
+}
+@media only screen and (min-width: 768px) {
+ .ui.search.dropdown .menu {
+ max-height: 10.68571429rem;
+ }
+}
+@media only screen and (min-width: 992px) {
+ .ui.search.dropdown .menu {
+ max-height: 16.02857143rem;
+ }
+}
+@media only screen and (min-width: 1920px) {
+ .ui.search.dropdown .menu {
+ max-height: 21.37142857rem;
+ }
+}
+
+/* Clearable Selection */
+.ui.dropdown > .remove.icon {
+ cursor: pointer;
+ font-size: 0.85714286em;
+ margin: -0.78571429em;
+ padding: 0.91666667em;
+ right: 3em;
+ top: 0.78571429em;
+ position: absolute;
+ opacity: 0.6;
+ z-index: 3;
+}
+.ui.clearable.dropdown .text,
+.ui.clearable.dropdown a:last-of-type {
+ margin-right: 1.5em;
+}
+.ui.dropdown select.noselection ~ .remove.icon,
+.ui.dropdown input[value=''] ~ .remove.icon,
+.ui.dropdown input:not([value]) ~ .remove.icon,
+.ui.dropdown.loading > .remove.icon {
+ display: none;
+}
+
+/*--------------
+ Multiple
+ ---------------*/
+
+
+/* Multiple Selection */
+.ui.ui.multiple.dropdown {
+ padding: 0.22619048em 3.2em 0.22619048em 0.35714286em;
+}
+.ui.multiple.dropdown .menu {
+ cursor: auto;
+}
+
+/* Selection Label */
+.ui.multiple.dropdown > .label {
+ display: inline-block;
+ white-space: normal;
+ font-size: 1em;
+ padding: 0.35714286em 0.78571429em;
+ margin: 0.14285714rem 0.28571429rem 0.14285714rem 0;
+ box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
+}
+
+/* Dropdown Icon */
+.ui.multiple.dropdown .dropdown.icon {
+ margin: '';
+ padding: '';
+}
+
+/* Text */
+.ui.multiple.dropdown > .text {
+ position: static;
+ padding: 0;
+ max-width: 100%;
+ margin: 0.45238095em 0 0.45238095em 0.64285714em;
+ line-height: 1.21428571em;
+}
+.ui.multiple.dropdown > .text.default {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.ui.multiple.dropdown > .label ~ input.search {
+ margin-left: 0.14285714em !important;
+}
+.ui.multiple.dropdown > .label ~ .text {
+ display: none;
+}
+.ui.multiple.dropdown > .label:not(.image) > img:not(.centered) {
+ margin-right: 0.78571429rem;
+}
+.ui.multiple.dropdown > .label:not(.image) > img.ui:not(.avatar) {
+ margin-bottom: 0.39285714rem;
+}
+.ui.multiple.dropdown > .image.label img {
+ margin: -0.35714286em 0.78571429em -0.35714286em -0.78571429em;
+ height: 1.71428571em;
+}
+
+/*-----------------
+ Multiple Search
+ -----------------*/
+
+
+/* Multiple Search Selection */
+.ui.multiple.search.dropdown,
+.ui.multiple.search.dropdown > input.search {
+ cursor: text;
+}
+
+/* Prompt Text */
+.ui.multiple.search.dropdown > .text {
+ display: inline-block;
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding: inherit;
+ margin: 0.45238095em 0 0.45238095em 0.64285714em;
+ line-height: 1.21428571em;
+}
+.ui.multiple.search.dropdown > .label ~ .text {
+ display: none;
+}
+
+/* Search */
+.ui.multiple.search.dropdown > input.search {
+ position: static;
+ padding: 0;
+ max-width: 100%;
+ margin: 0.45238095em 0 0.45238095em 0.64285714em;
+ width: 2.2em;
+ line-height: 1.21428571em;
+}
+.ui.multiple.search.dropdown.button {
+ min-width: 14em;
+}
+
+/*--------------
+ Inline
+ ---------------*/
+
+.ui.inline.dropdown {
+ cursor: pointer;
+ display: inline-block;
+ color: inherit;
+}
+.ui.inline.dropdown .dropdown.icon {
+ margin: 0 0.21428571em 0 0.21428571em;
+ vertical-align: baseline;
+}
+.ui.inline.dropdown > .text {
+ font-weight: 500;
+}
+.ui.inline.dropdown .menu {
+ cursor: auto;
+ margin-top: 0.21428571em;
+ border-radius: 0.28571429rem;
+}
+
+
+/*******************************
+ States
+*******************************/
+
+
+/*--------------------
+ Active
+----------------------*/
+
+
+/* Menu Item Active */
+.ui.dropdown .menu .active.item {
+ background: transparent;
+ font-weight: 500;
+ color: rgba(0, 0, 0, 0.95);
+ box-shadow: none;
+ z-index: 12;
+}
+
+/*--------------------
+ Hover
+----------------------*/
+
+
+/* Menu Item Hover */
+.ui.dropdown .menu > .item:hover {
+ background: rgba(0, 0, 0, 0.05);
+ color: rgba(0, 0, 0, 0.95);
+ z-index: 13;
+}
+
+/*--------------------
+ Default Text
+----------------------*/
+
+.ui.dropdown:not(.button) > .default.text,
+.ui.default.dropdown:not(.button) > .text {
+ color: rgba(191, 191, 191, 0.87);
+}
+.ui.dropdown:not(.button) > input:focus ~ .default.text,
+.ui.default.dropdown:not(.button) > input:focus ~ .text {
+ color: rgba(115, 115, 115, 0.87);
+}
+
+/*--------------------
+ Loading
+ ---------------------*/
+
+.ui.loading.dropdown > i.icon {
+ height: 1em !important;
+}
+.ui.loading.selection.dropdown > i.icon {
+ padding: 1.5em 1.28571429em !important;
+}
+.ui.loading.dropdown > i.icon:before {
+ position: absolute;
+ content: '';
+ top: 50%;
+ left: 50%;
+ margin: -0.64285714em 0 0 -0.64285714em;
+ width: 1.28571429em;
+ height: 1.28571429em;
+ border-radius: 500rem;
+ border: 0.2em solid rgba(0, 0, 0, 0.1);
+}
+.ui.loading.dropdown > i.icon:after {
+ position: absolute;
+ content: '';
+ top: 50%;
+ left: 50%;
+ box-shadow: 0 0 0 1px transparent;
+ margin: -0.64285714em 0 0 -0.64285714em;
+ width: 1.28571429em;
+ height: 1.28571429em;
+ animation: loader 0.6s infinite linear;
+ border: 0.2em solid #767676;
+ border-radius: 500rem;
+}
+
+/* Coupling */
+.ui.loading.dropdown.button > i.icon:before,
+.ui.loading.dropdown.button > i.icon:after {
+ display: none;
+}
+.ui.loading.dropdown > .text {
+ transition: none;
+}
+
+/* Used To Check Position */
+.ui.dropdown .loading.menu {
+ display: block;
+ visibility: hidden;
+ z-index: -1;
+}
+.ui.dropdown > .loading.menu {
+ left: 0 !important;
+ right: auto !important;
+}
+.ui.dropdown > .menu .loading.menu {
+ left: 100% !important;
+ right: auto !important;
+}
+
+/*--------------------
+ Keyboard Select
+----------------------*/
+
+
+/* Selected Item */
+.ui.dropdown.selected,
+.ui.dropdown .menu .selected.item {
+ background: rgba(0, 0, 0, 0.03);
+ color: rgba(0, 0, 0, 0.95);
+}
+
+/*--------------------
+ Search Filtered
+----------------------*/
+
+
+/* Filtered Item */
+.ui.dropdown > .filtered.text {
+ visibility: hidden;
+}
+.ui.dropdown .filtered.item {
+ display: none !important;
+}
+
+/*--------------------
+ States
+ ----------------------*/
+
+.ui.dropdown.error,
+.ui.dropdown.error > .text,
+.ui.dropdown.error > .default.text {
+ color: #9F3A38;
+}
+.ui.selection.dropdown.error {
+ background: #FFF6F6;
+ border-color: #E0B4B4;
+}
+.ui.selection.dropdown.error:hover {
+ border-color: #E0B4B4;
+}
+.ui.multiple.selection.error.dropdown > .label {
+ border-color: #E0B4B4;
+}
+.ui.dropdown.error > .menu,
+.ui.dropdown.error > .menu .menu {
+ border-color: #E0B4B4;
+}
+.ui.dropdown.error > .menu > .item {
+ color: #9F3A38;
+}
+
+/* Item Hover */
+.ui.dropdown.error > .menu > .item:hover {
+ background-color: #FBE7E7;
+}
+
+/* Item Active */
+.ui.dropdown.error > .menu .active.item {
+ background-color: #FDCFCF;
+}
+.ui.dropdown.info,
+.ui.dropdown.info > .text,
+.ui.dropdown.info > .default.text {
+ color: #276F86;
+}
+.ui.selection.dropdown.info {
+ background: #F8FFFF;
+ border-color: #A9D5DE;
+}
+.ui.selection.dropdown.info:hover {
+ border-color: #A9D5DE;
+}
+.ui.multiple.selection.info.dropdown > .label {
+ border-color: #A9D5DE;
+}
+.ui.dropdown.info > .menu,
+.ui.dropdown.info > .menu .menu {
+ border-color: #A9D5DE;
+}
+.ui.dropdown.info > .menu > .item {
+ color: #276F86;
+}
+
+/* Item Hover */
+.ui.dropdown.info > .menu > .item:hover {
+ background-color: #e9f2fb;
+}
+
+/* Item Active */
+.ui.dropdown.info > .menu .active.item {
+ background-color: #cef1fd;
+}
+.ui.dropdown.success,
+.ui.dropdown.success > .text,
+.ui.dropdown.success > .default.text {
+ color: #2C662D;
+}
+.ui.selection.dropdown.success {
+ background: #FCFFF5;
+ border-color: #A3C293;
+}
+.ui.selection.dropdown.success:hover {
+ border-color: #A3C293;
+}
+.ui.multiple.selection.success.dropdown > .label {
+ border-color: #A3C293;
+}
+.ui.dropdown.success > .menu,
+.ui.dropdown.success > .menu .menu {
+ border-color: #A3C293;
+}
+.ui.dropdown.success > .menu > .item {
+ color: #2C662D;
+}
+
+/* Item Hover */
+.ui.dropdown.success > .menu > .item:hover {
+ background-color: #e9fbe9;
+}
+
+/* Item Active */
+.ui.dropdown.success > .menu .active.item {
+ background-color: #dafdce;
+}
+.ui.dropdown.warning,
+.ui.dropdown.warning > .text,
+.ui.dropdown.warning > .default.text {
+ color: #573A08;
+}
+.ui.selection.dropdown.warning {
+ background: #FFFAF3;
+ border-color: #C9BA9B;
+}
+.ui.selection.dropdown.warning:hover {
+ border-color: #C9BA9B;
+}
+.ui.multiple.selection.warning.dropdown > .label {
+ border-color: #C9BA9B;
+}
+.ui.dropdown.warning > .menu,
+.ui.dropdown.warning > .menu .menu {
+ border-color: #C9BA9B;
+}
+.ui.dropdown.warning > .menu > .item {
+ color: #573A08;
+}
+
+/* Item Hover */
+.ui.dropdown.warning > .menu > .item:hover {
+ background-color: #fbfbe9;
+}
+
+/* Item Active */
+.ui.dropdown.warning > .menu .active.item {
+ background-color: #fdfdce;
+}
+
+/*--------------------
+ Clear
+----------------------*/
+
+.ui.dropdown > .clear.dropdown.icon {
+ opacity: 0.8;
+ transition: opacity 0.1s ease;
+}
+.ui.dropdown > .clear.dropdown.icon:hover {
+ opacity: 1;
+}
+
+/*--------------------
+ Disabled
+ ----------------------*/
+
+
+/* Disabled */
+.ui.disabled.dropdown,
+.ui.dropdown .menu > .disabled.item {
+ cursor: default;
+ pointer-events: none;
+ opacity: var(--opacity-disabled);
+}
+
+
+/*******************************
+ Variations
+*******************************/
+
+
+/*--------------
+ Direction
+---------------*/
+
+
+/* Flyout Direction */
+.ui.dropdown .menu {
+ left: 0;
+}
+
+/* Default Side (Right) */
+.ui.dropdown .right.menu > .menu,
+.ui.dropdown .menu .right.menu {
+ left: 100% !important;
+ right: auto !important;
+ border-radius: 0.28571429rem !important;
+}
+
+/* Leftward Opening Menu */
+.ui.dropdown > .left.menu {
+ left: auto !important;
+ right: 0 !important;
+}
+.ui.dropdown > .left.menu .menu,
+.ui.dropdown .menu .left.menu {
+ left: auto;
+ right: 100%;
+ margin: 0 -0.5em 0 0 !important;
+ border-radius: 0.28571429rem !important;
+}
+.ui.dropdown .item .left.dropdown.icon,
+.ui.dropdown .left.menu .item .dropdown.icon {
+ width: auto;
+ float: left;
+ margin: 0em 0 0 0;
+}
+.ui.dropdown .item .left.dropdown.icon,
+.ui.dropdown .left.menu .item .dropdown.icon {
+ width: auto;
+ float: left;
+ margin: 0em 0 0 0;
+}
+.ui.dropdown .item .left.dropdown.icon + .text,
+.ui.dropdown .left.menu .item .dropdown.icon + .text {
+ margin-left: 1em;
+ margin-right: 0;
+}
+
+/*--------------
+ Upward
+ ---------------*/
+
+
+/* Upward Main Menu */
+.ui.upward.dropdown > .menu {
+ top: auto;
+ bottom: 100%;
+ box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.08);
+ border-radius: 0.28571429rem 0.28571429rem 0 0;
+}
+
+/* Upward Sub Menu */
+.ui.dropdown .upward.menu {
+ top: auto !important;
+ bottom: 0 !important;
+}
+
+/* Active Upward */
+.ui.simple.upward.active.dropdown,
+.ui.simple.upward.dropdown:hover {
+ border-radius: 0.28571429rem 0.28571429rem 0 0 !important;
+}
+.ui.upward.dropdown.button:not(.pointing):not(.floating).active {
+ border-radius: 0.28571429rem 0.28571429rem 0 0;
+}
+
+/* Selection */
+.ui.upward.selection.dropdown .menu {
+ border-top-width: 1px !important;
+ border-bottom-width: 0 !important;
+ box-shadow: 0 -2px 3px 0 rgba(0, 0, 0, 0.08);
+}
+.ui.upward.selection.dropdown:hover {
+ box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.05);
+}
+
+/* Active Upward */
+.ui.active.upward.selection.dropdown {
+ border-radius: 0 0 0.28571429rem 0.28571429rem !important;
+}
+
+/* Visible Upward */
+.ui.upward.selection.dropdown.visible {
+ box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.08);
+ border-radius: 0 0 0.28571429rem 0.28571429rem !important;
+}
+
+/* Visible Hover Upward */
+.ui.upward.active.selection.dropdown:hover {
+ box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.05);
+}
+.ui.upward.active.selection.dropdown:hover .menu {
+ box-shadow: 0 -2px 3px 0 rgba(0, 0, 0, 0.08);
+}
+
+/*--------------
+ Scrolling
+ ---------------*/
+
+
+/* Selection Menu */
+.ui.scrolling.dropdown .menu,
+.ui.dropdown .scrolling.menu {
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+.ui.scrolling.dropdown .menu {
+ overflow-x: hidden;
+ overflow-y: auto;
+ backface-visibility: hidden;
+ -webkit-overflow-scrolling: touch;
+ min-width: 100% !important;
+ width: auto !important;
+}
+.ui.dropdown .scrolling.menu {
+ position: static;
+ overflow-y: auto;
+ border: none;
+ box-shadow: none !important;
+ border-radius: 0 !important;
+ margin: 0 !important;
+ min-width: 100% !important;
+ width: auto !important;
+ border-top: 1px solid rgba(34, 36, 38, 0.15);
+}
+.ui.scrolling.dropdown .menu .item.item.item,
+.ui.dropdown .scrolling.menu > .item.item.item {
+ border-top: none;
+}
+.ui.scrolling.dropdown .menu .item:first-child,
+.ui.dropdown .scrolling.menu .item:first-child {
+ border-top: none;
+}
+.ui.dropdown > .animating.menu .scrolling.menu,
+.ui.dropdown > .visible.menu .scrolling.menu {
+ display: block;
+}
+
+/* Scrollbar in IE */
+@media all and (-ms-high-contrast: none) {
+ .ui.scrolling.dropdown .menu,
+ .ui.dropdown .scrolling.menu {
+ min-width: calc(100% - 17px);
+ }
+}
+@media only screen and (max-width: 767.98px) {
+ .ui.scrolling.dropdown .menu,
+ .ui.dropdown .scrolling.menu {
+ max-height: 10.28571429rem;
+ }
+}
+@media only screen and (min-width: 768px) {
+ .ui.scrolling.dropdown .menu,
+ .ui.dropdown .scrolling.menu {
+ max-height: 15.42857143rem;
+ }
+}
+@media only screen and (min-width: 992px) {
+ .ui.scrolling.dropdown .menu,
+ .ui.dropdown .scrolling.menu {
+ max-height: 20.57142857rem;
+ }
+}
+@media only screen and (min-width: 1920px) {
+ .ui.scrolling.dropdown .menu,
+ .ui.dropdown .scrolling.menu {
+ max-height: 20.57142857rem;
+ }
+}
+
+/*--------------
+ Columnar
+---------------*/
+
+.ui.column.dropdown > .menu {
+ flex-wrap: wrap;
+}
+.ui.dropdown[class*="two column"] > .menu > .item {
+ width: 50%;
+}
+.ui.dropdown[class*="three column"] > .menu > .item {
+ width: 33%;
+}
+.ui.dropdown[class*="four column"] > .menu > .item {
+ width: 25%;
+}
+.ui.dropdown[class*="five column"] > .menu > .item {
+ width: 20%;
+}
+
+/*--------------
+ Simple
+ ---------------*/
+
+
+/* Displays without javascript */
+.ui.simple.dropdown .menu:before,
+.ui.simple.dropdown .menu:after {
+ display: none;
+}
+.ui.simple.dropdown .menu {
+ position: absolute;
+
+/* IE hack to make dropdown icons appear inline */
+ display: -ms-inline-flexbox !important;
+ display: block;
+ overflow: hidden;
+ top: -9999px;
+ opacity: 0;
+ width: 0;
+ height: 0;
+ transition: opacity 0.1s ease;
+ margin-top: 0 !important;
+}
+.ui.simple.active.dropdown,
+.ui.simple.dropdown:hover {
+ border-bottom-left-radius: 0 !important;
+ border-bottom-right-radius: 0 !important;
+}
+.ui.simple.active.dropdown > .menu,
+.ui.simple.dropdown:hover > .menu {
+ overflow: visible;
+ width: auto;
+ height: auto;
+ top: 100%;
+ opacity: 1;
+}
+.ui.simple.dropdown > .menu > .item:active > .menu,
+.ui.simple.dropdown .menu .item:hover > .menu {
+ overflow: visible;
+ width: auto;
+ height: auto;
+ top: 0 !important;
+ left: 100%;
+ opacity: 1;
+}
+.ui.simple.dropdown > .menu > .item:active > .left.menu,
+.ui.simple.dropdown .menu .item:hover > .left.menu,
+.right.menu .ui.simple.dropdown > .menu > .item:active > .menu:not(.right),
+.right.menu .ui.simple.dropdown > .menu .item:hover > .menu:not(.right) {
+ left: auto;
+ right: 100%;
+}
+.ui.simple.disabled.dropdown:hover .menu {
+ display: none;
+ height: 0;
+ width: 0;
+ overflow: hidden;
+}
+
+/* Visible */
+.ui.simple.visible.dropdown > .menu {
+ display: block;
+}
+
+/* Scrolling */
+.ui.simple.scrolling.active.dropdown > .menu,
+.ui.simple.scrolling.dropdown:hover > .menu {
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+/*--------------
+ Fluid
+ ---------------*/
+
+.ui.fluid.dropdown {
+ display: block;
+ width: 100% !important;
+ min-width: 0;
+}
+.ui.fluid.dropdown > .dropdown.icon {
+ float: right;
+}
+
+/*--------------
+ Floating
+ ---------------*/
+
+.ui.floating.dropdown .menu {
+ left: 0;
+ right: auto;
+ box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15) !important;
+ border-radius: 0.28571429rem !important;
+}
+.ui.floating.dropdown > .menu {
+ border-radius: 0.28571429rem !important;
+}
+.ui:not(.upward).floating.dropdown > .menu {
+ margin-top: 0.5em;
+}
+.ui.upward.floating.dropdown > .menu {
+ margin-bottom: 0.5em;
+}
+
+/*--------------
+ Pointing
+ ---------------*/
+
+.ui.pointing.dropdown > .menu {
+ top: 100%;
+ margin-top: 0.78571429rem;
+ border-radius: 0.28571429rem;
+}
+.ui.pointing.dropdown > .menu:not(.hidden):after {
+ display: block;
+ position: absolute;
+ pointer-events: none;
+ content: '';
+ visibility: visible;
+ transform: rotate(45deg);
+ width: 0.5em;
+ height: 0.5em;
+ box-shadow: -1px -1px 0 0 rgba(34, 36, 38, 0.15);
+ background: #FFFFFF;
+ z-index: 2;
+}
+.ui.pointing.dropdown > .menu:not(.hidden):after {
+ top: -0.25em;
+ left: 50%;
+ margin: 0 0 0 -0.25em;
+}
+
+/* Top Left Pointing */
+.ui.top.left.pointing.dropdown > .menu {
+ top: 100%;
+ bottom: auto;
+ left: 0;
+ right: auto;
+ margin: 1em 0 0;
+}
+.ui.top.left.pointing.dropdown > .menu {
+ top: 100%;
+ bottom: auto;
+ left: 0;
+ right: auto;
+ margin: 1em 0 0;
+}
+.ui.top.left.pointing.dropdown > .menu:after {
+ top: -0.25em;
+ left: 1em;
+ right: auto;
+ margin: 0;
+ transform: rotate(45deg);
+}
+
+/* Top Right Pointing */
+.ui.top.right.pointing.dropdown > .menu {
+ top: 100%;
+ bottom: auto;
+ right: 0;
+ left: auto;
+ margin: 1em 0 0;
+}
+.ui.top.pointing.dropdown > .left.menu:after,
+.ui.top.right.pointing.dropdown > .menu:after {
+ top: -0.25em;
+ left: auto !important;
+ right: 1em !important;
+ margin: 0;
+ transform: rotate(45deg);
+}
+
+/* Left Pointing */
+.ui.left.pointing.dropdown > .menu {
+ top: 0;
+ left: 100%;
+ right: auto;
+ margin: 0 0 0 1em;
+}
+.ui.left.pointing.dropdown > .menu:after {
+ top: 1em;
+ left: -0.25em;
+ margin: 0 0 0 0;
+ transform: rotate(-45deg);
+}
+.ui.left:not(.top):not(.bottom).pointing.dropdown > .left.menu {
+ left: auto !important;
+ right: 100% !important;
+ margin: 0 1em 0 0;
+}
+.ui.left:not(.top):not(.bottom).pointing.dropdown > .left.menu:after {
+ top: 1em;
+ left: auto;
+ right: -0.25em;
+ margin: 0 0 0 0;
+ transform: rotate(135deg);
+}
+
+/* Right Pointing */
+.ui.right.pointing.dropdown > .menu {
+ top: 0;
+ left: auto;
+ right: 100%;
+ margin: 0 1em 0 0;
+}
+.ui.right.pointing.dropdown > .menu:after {
+ top: 1em;
+ left: auto;
+ right: -0.25em;
+ margin: 0 0 0 0;
+ transform: rotate(135deg);
+}
+
+/* Bottom Pointing */
+.ui.bottom.pointing.dropdown > .menu {
+ top: auto;
+ bottom: 100%;
+ left: 0;
+ right: auto;
+ margin: 0 0 1em;
+}
+.ui.bottom.pointing.dropdown > .menu:after {
+ top: auto;
+ bottom: -0.25em;
+ right: auto;
+ margin: 0;
+ transform: rotate(-135deg);
+}
+
+/* Reverse Sub-Menu Direction */
+.ui.bottom.pointing.dropdown > .menu .menu {
+ top: auto !important;
+ bottom: 0 !important;
+}
+
+/* Bottom Left */
+.ui.bottom.left.pointing.dropdown > .menu {
+ left: 0;
+ right: auto;
+}
+.ui.bottom.left.pointing.dropdown > .menu:after {
+ left: 1em;
+ right: auto;
+}
+
+/* Bottom Right */
+.ui.bottom.right.pointing.dropdown > .menu {
+ right: 0;
+ left: auto;
+}
+.ui.bottom.right.pointing.dropdown > .menu:after {
+ left: auto;
+ right: 1em;
+}
+
+/* Upward pointing */
+.ui.pointing.upward.dropdown .menu,
+.ui.top.pointing.upward.dropdown .menu {
+ top: auto !important;
+ bottom: 100% !important;
+ margin: 0 0 0.78571429rem;
+ border-radius: 0.28571429rem;
+}
+.ui.pointing.upward.dropdown .menu:after,
+.ui.top.pointing.upward.dropdown .menu:after {
+ top: 100% !important;
+ bottom: auto !important;
+ box-shadow: 1px 1px 0 0 rgba(34, 36, 38, 0.15);
+ margin: -0.25em 0 0;
+}
+
+/* Right Pointing Upward */
+.ui.right.pointing.upward.dropdown:not(.top):not(.bottom) .menu {
+ top: auto !important;
+ bottom: 0 !important;
+ margin: 0 1em 0 0;
+}
+.ui.right.pointing.upward.dropdown:not(.top):not(.bottom) .menu:after {
+ top: auto !important;
+ bottom: 0 !important;
+ margin: 0 0 1em 0;
+ box-shadow: -1px -1px 0 0 rgba(34, 36, 38, 0.15);
+}
+
+/* Left Pointing Upward */
+.ui.left.pointing.upward.dropdown:not(.top):not(.bottom) .menu {
+ top: auto !important;
+ bottom: 0 !important;
+ margin: 0 0 0 1em;
+}
+.ui.left.pointing.upward.dropdown:not(.top):not(.bottom) .menu:after {
+ top: auto !important;
+ bottom: 0 !important;
+ margin: 0 0 1em 0;
+ box-shadow: -1px -1px 0 0 rgba(34, 36, 38, 0.15);
+}
+
+/*--------------------
+ Sizes
+---------------------*/
+
+.ui.dropdown,
+.ui.dropdown .menu > .item {
+ font-size: 1rem;
+}
+.ui.mini.dropdown,
+.ui.mini.dropdown .menu > .item {
+ font-size: 0.78571429rem;
+}
+.ui.tiny.dropdown,
+.ui.tiny.dropdown .menu > .item {
+ font-size: 0.85714286rem;
+}
+.ui.small.dropdown,
+.ui.small.dropdown .menu > .item {
+ font-size: 0.92857143rem;
+}
+.ui.large.dropdown,
+.ui.large.dropdown .menu > .item {
+ font-size: 1.14285714rem;
+}
+.ui.big.dropdown,
+.ui.big.dropdown .menu > .item {
+ font-size: 1.28571429rem;
+}
+.ui.huge.dropdown,
+.ui.huge.dropdown .menu > .item {
+ font-size: 1.42857143rem;
+}
+.ui.massive.dropdown,
+.ui.massive.dropdown .menu > .item {
+ font-size: 1.71428571rem;
+}
+
+
+/*******************************
+ Theme Overrides
+*******************************/
+
+
+/* Dropdown Carets */
+@font-face {
+ font-family: 'Dropdown';
+ src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMggjB5AAAAC8AAAAYGNtYXAPfuIIAAABHAAAAExnYXNwAAAAEAAAAWgAAAAIZ2x5Zjo82LgAAAFwAAABVGhlYWQAQ88bAAACxAAAADZoaGVhAwcB6QAAAvwAAAAkaG10eAS4ABIAAAMgAAAAIGxvY2EBNgDeAAADQAAAABJtYXhwAAoAFgAAA1QAAAAgbmFtZVcZpu4AAAN0AAABRXBvc3QAAwAAAAAEvAAAACAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADw2gHg/+D/4AHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADgAAAAKAAgAAgACAAEAIPDa//3//wAAAAAAIPDX//3//wAB/+MPLQADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAIABJQElABMAABM0NzY3BTYXFhUUDwEGJwYvASY1AAUGBwEACAUGBoAFCAcGgAUBEgcGBQEBAQcECQYHfwYBAQZ/BwYAAQAAAG4BJQESABMAADc0PwE2MzIfARYVFAcGIyEiJyY1AAWABgcIBYAGBgUI/wAHBgWABwaABQWABgcHBgUFBgcAAAABABIASQC3AW4AEwAANzQ/ATYXNhcWHQEUBwYnBi8BJjUSBoAFCAcFBgYFBwgFgAbbBwZ/BwEBBwQJ/wgEBwEBB38GBgAAAAABAAAASQClAW4AEwAANxE0NzYzMh8BFhUUDwEGIyInJjUABQYHCAWABgaABQgHBgVbAQAIBQYGgAUIBwWABgYFBwAAAAEAAAABAADZuaKOXw889QALAgAAAAAA0ABHWAAAAADQAEdYAAAAAAElAW4AAAAIAAIAAAAAAAAAAQAAAeD/4AAAAgAAAAAAASUAAQAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAABAAAAASUAAAElAAAAtwASALcAAAAAAAAACgAUAB4AQgBkAIgAqgAAAAEAAAAIABQAAQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAOAK4AAQAAAAAAAQAOAAAAAQAAAAAAAgAOAEcAAQAAAAAAAwAOACQAAQAAAAAABAAOAFUAAQAAAAAABQAWAA4AAQAAAAAABgAHADIAAQAAAAAACgA0AGMAAwABBAkAAQAOAAAAAwABBAkAAgAOAEcAAwABBAkAAwAOACQAAwABBAkABAAOAFUAAwABBAkABQAWAA4AAwABBAkABgAOADkAAwABBAkACgA0AGMAaQBjAG8AbQBvAG8AbgBWAGUAcgBzAGkAbwBuACAAMQAuADAAaQBjAG8AbQBvAG8Abmljb21vb24AaQBjAG8AbQBvAG8AbgBSAGUAZwB1AGwAYQByAGkAYwBvAG0AbwBvAG4ARgBvAG4AdAAgAGcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAuAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=) format('truetype'), url(data:application/font-woff;charset=utf-8;base64,d09GRk9UVE8AAAVwAAoAAAAABSgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAAdkAAAHZLDXE/09TLzIAAALQAAAAYAAAAGAIIweQY21hcAAAAzAAAABMAAAATA9+4ghnYXNwAAADfAAAAAgAAAAIAAAAEGhlYWQAAAOEAAAANgAAADYAQ88baGhlYQAAA7wAAAAkAAAAJAMHAelobXR4AAAD4AAAACAAAAAgBLgAEm1heHAAAAQAAAAABgAAAAYACFAAbmFtZQAABAgAAAFFAAABRVcZpu5wb3N0AAAFUAAAACAAAAAgAAMAAAEABAQAAQEBCGljb21vb24AAQIAAQA6+BwC+BsD+BgEHgoAGVP/i4seCgAZU/+LiwwHi2v4lPh0BR0AAACIDx0AAACNER0AAAAJHQAAAdASAAkBAQgPERMWGyAlKmljb21vb25pY29tb29udTB1MXUyMHVGMEQ3dUYwRDh1RjBEOXVGMERBAAACAYkABgAIAgABAAQABwAKAA0AVgCfAOgBL/yUDvyUDvyUDvuUDvtvi/emFYuQjZCOjo+Pj42Qiwj3lIsFkIuQiY6Hj4iNhouGi4aJh4eHCPsU+xQFiIiGiYaLhouHjYeOCPsU9xQFiI+Jj4uQCA77b4v3FBWLkI2Pjo8I9xT3FAWPjo+NkIuQi5CJjogI9xT7FAWPh42Hi4aLhomHh4eIiIaJhosI+5SLBYaLh42HjoiPiY+LkAgO+92d928Vi5CNkI+OCPcU9xQFjo+QjZCLkIuPiY6Hj4iNhouGCIv7lAWLhomHh4iIh4eJhouGi4aNiI8I+xT3FAWHjomPi5AIDvvdi+YVi/eUBYuQjZCOjo+Pj42Qi5CLkImOhwj3FPsUBY+IjYaLhouGiYeHiAj7FPsUBYiHhomGi4aLh42Hj4iOiY+LkAgO+JQU+JQViwwKAAAAAAMCAAGQAAUAAAFMAWYAAABHAUwBZgAAAPUAGQCEAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA8NoB4P/g/+AB4AAgAAAAAQAAAAAAAAAAAAAAIAAAAAAAAgAAAAMAAAAUAAMAAQAAABQABAA4AAAACgAIAAIAAgABACDw2v/9//8AAAAAACDw1//9//8AAf/jDy0AAwABAAAAAAAAAAAAAAABAAH//wAPAAEAAAABAAA5emozXw889QALAgAAAAAA0ABHWAAAAADQAEdYAAAAAAElAW4AAAAIAAIAAAAAAAAAAQAAAeD/4AAAAgAAAAAAASUAAQAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAABAAAAASUAAAElAAAAtwASALcAAAAAUAAACAAAAAAADgCuAAEAAAAAAAEADgAAAAEAAAAAAAIADgBHAAEAAAAAAAMADgAkAAEAAAAAAAQADgBVAAEAAAAAAAUAFgAOAAEAAAAAAAYABwAyAAEAAAAAAAoANABjAAMAAQQJAAEADgAAAAMAAQQJAAIADgBHAAMAAQQJAAMADgAkAAMAAQQJAAQADgBVAAMAAQQJAAUAFgAOAAMAAQQJAAYADgA5AAMAAQQJAAoANABjAGkAYwBvAG0AbwBvAG4AVgBlAHIAcwBpAG8AbgAgADEALgAwAGkAYwBvAG0AbwBvAG5pY29tb29uAGkAYwBvAG0AbwBvAG4AUgBlAGcAdQBsAGEAcgBpAGMAbwBtAG8AbwBuAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('woff');
+ font-weight: normal;
+ font-style: normal;
+}
+.ui.dropdown > .dropdown.icon {
+ font-family: 'Dropdown';
+ line-height: 1;
+ height: 1em;
+ width: 1.23em;
+ backface-visibility: hidden;
+ font-weight: normal;
+ font-style: normal;
+ text-align: center;
+}
+.ui.dropdown > .dropdown.icon {
+ width: auto;
+}
+.ui.dropdown > .dropdown.icon:before {
+ content: '\f0d7';
+}
+
+/* Sub Menu */
+.ui.dropdown .menu .item .dropdown.icon:before {
+ content: '\f0da' /*rtl:'\f0d9'*/;
+}
+.ui.dropdown .item .left.dropdown.icon:before,
+.ui.dropdown .left.menu .item .dropdown.icon:before {
+ content: "\f0d9" /*rtl:"\f0da"*/;
+}
+
+/* Vertical Menu Dropdown */
+.ui.vertical.menu .dropdown.item > .dropdown.icon:before {
+ content: "\f0da" /*rtl:"\f0d9"*/;
+}
+/* Icons for Reference
+.dropdown.down.icon {
+ content: "\f0d7";
+}
+.dropdown.up.icon {
+ content: "\f0d8";
+}
+.dropdown.left.icon {
+ content: "\f0d9";
+}
+.dropdown.icon.icon {
+ content: "\f0da";
+}
+*/
+
+
+/*******************************
+ User Overrides
+*******************************/
+
diff --git a/web_src/fomantic/build/components/dropdown.js b/web_src/fomantic/build/components/dropdown.js
new file mode 100644
index 0000000000..3ad0984865
--- /dev/null
+++ b/web_src/fomantic/build/components/dropdown.js
@@ -0,0 +1,4245 @@
+/*!
+ * # Fomantic-UI - Dropdown
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+;(function ($, window, document, undefined) {
+
+'use strict';
+
+$.isFunction = $.isFunction || function(obj) {
+ return typeof obj === "function" && typeof obj.nodeType !== "number";
+};
+
+window = (typeof window != 'undefined' && window.Math == Math)
+ ? window
+ : (typeof self != 'undefined' && self.Math == Math)
+ ? self
+ : Function('return this')()
+;
+
+$.fn.dropdown = function(parameters) {
+ var
+ $allModules = $(this),
+ $document = $(document),
+
+ moduleSelector = $allModules.selector || '',
+
+ hasTouch = ('ontouchstart' in document.documentElement),
+ // GITEA-PATCH: always "click" as clickEvent, old code used "touchstart" as clickEvent, it is wrong,
+ // because "touchstart" caused problems when users try to scroll and the touch point is in the dropdown.
+ clickEvent = 'click',
+
+ time = new Date().getTime(),
+ performance = [],
+
+ query = arguments[0],
+ methodInvoked = (typeof query == 'string'),
+ queryArguments = [].slice.call(arguments, 1),
+ returnedValue
+ ;
+
+ $allModules
+ .each(function(elementIndex) {
+ var
+ settings = ( $.isPlainObject(parameters) )
+ ? $.extend(true, {}, $.fn.dropdown.settings, parameters)
+ : $.extend({}, $.fn.dropdown.settings),
+
+ className = settings.className,
+ message = settings.message,
+ fields = settings.fields,
+ keys = settings.keys,
+ metadata = settings.metadata,
+ namespace = settings.namespace,
+ regExp = settings.regExp,
+ selector = settings.selector,
+ error = settings.error,
+ templates = settings.templates,
+
+ eventNamespace = '.' + namespace,
+ moduleNamespace = 'module-' + namespace,
+
+ $module = $(this),
+ $context = $(settings.context),
+ $text = $module.find(selector.text),
+ $search = $module.find(selector.search),
+ $sizer = $module.find(selector.sizer),
+ $input = $module.find(selector.input),
+ $icon = $module.find(selector.icon),
+ $clear = $module.find(selector.clearIcon),
+
+ $combo = ($module.prev().find(selector.text).length > 0)
+ ? $module.prev().find(selector.text)
+ : $module.prev(),
+
+ $menu = $module.children(selector.menu),
+ $item = $menu.find(selector.item),
+ $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $(),
+
+ activated = false,
+ itemActivated = false,
+ internalChange = false,
+ iconClicked = false,
+ element = this,
+ instance = $module.data(moduleNamespace),
+
+ selectActionActive,
+ initialLoad,
+ pageLostFocus,
+ willRefocus,
+ elementNamespace,
+ id,
+ selectObserver,
+ menuObserver,
+ classObserver,
+ module
+ ;
+
+ module = {
+
+ initialize: function() {
+ module.debug('Initializing dropdown', settings);
+
+ if( module.is.alreadySetup() ) {
+ module.setup.reference();
+ }
+ else {
+ if (settings.ignoreDiacritics && !String.prototype.normalize) {
+ settings.ignoreDiacritics = false;
+ module.error(error.noNormalize, element);
+ }
+
+ module.setup.layout();
+
+ if(settings.values) {
+ module.set.initialLoad();
+ module.change.values(settings.values);
+ module.remove.initialLoad();
+ }
+
+ module.refreshData();
+
+ module.save.defaults();
+ module.restore.selected();
+
+ module.create.id();
+ module.bind.events();
+
+ module.observeChanges();
+ module.instantiate();
+ }
+
+ },
+
+ instantiate: function() {
+ module.verbose('Storing instance of dropdown', module);
+ instance = module;
+ $module
+ .data(moduleNamespace, module)
+ ;
+ },
+
+ destroy: function() {
+ module.verbose('Destroying previous dropdown', $module);
+ module.remove.tabbable();
+ module.remove.active();
+ $menu.transition('stop all');
+ $menu.removeClass(className.visible).addClass(className.hidden);
+ $module
+ .off(eventNamespace)
+ .removeData(moduleNamespace)
+ ;
+ $menu
+ .off(eventNamespace)
+ ;
+ $document
+ .off(elementNamespace)
+ ;
+ module.disconnect.menuObserver();
+ module.disconnect.selectObserver();
+ module.disconnect.classObserver();
+ },
+
+ observeChanges: function() {
+ if('MutationObserver' in window) {
+ selectObserver = new MutationObserver(module.event.select.mutation);
+ menuObserver = new MutationObserver(module.event.menu.mutation);
+ classObserver = new MutationObserver(module.event.class.mutation);
+ module.debug('Setting up mutation observer', selectObserver, menuObserver, classObserver);
+ module.observe.select();
+ module.observe.menu();
+ module.observe.class();
+ }
+ },
+
+ disconnect: {
+ menuObserver: function() {
+ if(menuObserver) {
+ menuObserver.disconnect();
+ }
+ },
+ selectObserver: function() {
+ if(selectObserver) {
+ selectObserver.disconnect();
+ }
+ },
+ classObserver: function() {
+ if(classObserver) {
+ classObserver.disconnect();
+ }
+ }
+ },
+ observe: {
+ select: function() {
+ if(module.has.input() && selectObserver) {
+ selectObserver.observe($module[0], {
+ childList : true,
+ subtree : true
+ });
+ }
+ },
+ menu: function() {
+ if(module.has.menu() && menuObserver) {
+ menuObserver.observe($menu[0], {
+ childList : true,
+ subtree : true
+ });
+ }
+ },
+ class: function() {
+ if(module.has.search() && classObserver) {
+ classObserver.observe($module[0], {
+ attributes : true
+ });
+ }
+ }
+ },
+
+ create: {
+ id: function() {
+ id = (Math.random().toString(16) + '000000000').substr(2, 8);
+ elementNamespace = '.' + id;
+ module.verbose('Creating unique id for element', id);
+ },
+ userChoice: function(values) {
+ var
+ $userChoices,
+ $userChoice,
+ isUserValue,
+ html
+ ;
+ values = values || module.get.userValues();
+ if(!values) {
+ return false;
+ }
+ values = Array.isArray(values)
+ ? values
+ : [values]
+ ;
+ $.each(values, function(index, value) {
+ if(module.get.item(value) === false) {
+ html = settings.templates.addition( module.add.variables(message.addResult, value) );
+ $userChoice = $('<div />')
+ .html(html)
+ .attr('data-' + metadata.value, value)
+ .attr('data-' + metadata.text, value)
+ .addClass(className.addition)
+ .addClass(className.item)
+ ;
+ if(settings.hideAdditions) {
+ $userChoice.addClass(className.hidden);
+ }
+ $userChoices = ($userChoices === undefined)
+ ? $userChoice
+ : $userChoices.add($userChoice)
+ ;
+ module.verbose('Creating user choices for value', value, $userChoice);
+ }
+ });
+ return $userChoices;
+ },
+ userLabels: function(value) {
+ var
+ userValues = module.get.userValues()
+ ;
+ if(userValues) {
+ module.debug('Adding user labels', userValues);
+ $.each(userValues, function(index, value) {
+ module.verbose('Adding custom user value');
+ module.add.label(value, value);
+ });
+ }
+ },
+ menu: function() {
+ $menu = $('<div />')
+ .addClass(className.menu)
+ .appendTo($module)
+ ;
+ },
+ sizer: function() {
+ $sizer = $('<span />')
+ .addClass(className.sizer)
+ .insertAfter($search)
+ ;
+ }
+ },
+
+ search: function(query) {
+ query = (query !== undefined)
+ ? query
+ : module.get.query()
+ ;
+ module.verbose('Searching for query', query);
+ if(module.has.minCharacters(query)) {
+ module.filter(query);
+ }
+ else {
+ module.hide(null,true);
+ }
+ },
+
+ select: {
+ firstUnfiltered: function() {
+ module.verbose('Selecting first non-filtered element');
+ module.remove.selectedItem();
+ $item
+ .not(selector.unselectable)
+ .not(selector.addition + selector.hidden)
+ .eq(0)
+ .addClass(className.selected)
+ ;
+ },
+ nextAvailable: function($selected) {
+ $selected = $selected.eq(0);
+ var
+ $nextAvailable = $selected.nextAll(selector.item).not(selector.unselectable).eq(0),
+ $prevAvailable = $selected.prevAll(selector.item).not(selector.unselectable).eq(0),
+ hasNext = ($nextAvailable.length > 0)
+ ;
+ if(hasNext) {
+ module.verbose('Moving selection to', $nextAvailable);
+ $nextAvailable.addClass(className.selected);
+ }
+ else {
+ module.verbose('Moving selection to', $prevAvailable);
+ $prevAvailable.addClass(className.selected);
+ }
+ }
+ },
+
+ setup: {
+ api: function() {
+ var
+ apiSettings = {
+ debug : settings.debug,
+ urlData : {
+ value : module.get.value(),
+ query : module.get.query()
+ },
+ on : false
+ }
+ ;
+ module.verbose('First request, initializing API');
+ $module
+ .api(apiSettings)
+ ;
+ },
+ layout: function() {
+ if( $module.is('select') ) {
+ module.setup.select();
+ module.setup.returnedObject();
+ }
+ if( !module.has.menu() ) {
+ module.create.menu();
+ }
+ if ( module.is.selection() && module.is.clearable() && !module.has.clearItem() ) {
+ module.verbose('Adding clear icon');
+ $clear = $('<i />')
+ .addClass('remove icon')
+ .insertBefore($text)
+ ;
+ }
+ if( module.is.search() && !module.has.search() ) {
+ module.verbose('Adding search input');
+ $search = $('<input />')
+ .addClass(className.search)
+ .prop('autocomplete', 'off')
+ .insertBefore($text)
+ ;
+ }
+ if( module.is.multiple() && module.is.searchSelection() && !module.has.sizer()) {
+ module.create.sizer();
+ }
+ if(settings.allowTab) {
+ module.set.tabbable();
+ }
+ },
+ select: function() {
+ var
+ selectValues = module.get.selectValues()
+ ;
+ module.debug('Dropdown initialized on a select', selectValues);
+ if( $module.is('select') ) {
+ $input = $module;
+ }
+ // see if select is placed correctly already
+ if($input.parent(selector.dropdown).length > 0) {
+ module.debug('UI dropdown already exists. Creating dropdown menu only');
+ $module = $input.closest(selector.dropdown);
+ if( !module.has.menu() ) {
+ module.create.menu();
+ }
+ $menu = $module.children(selector.menu);
+ module.setup.menu(selectValues);
+ }
+ else {
+ module.debug('Creating entire dropdown from select');
+ $module = $('<div />')
+ .attr('class', $input.attr('class') )
+ .addClass(className.selection)
+ .addClass(className.dropdown)
+ .html( templates.dropdown(selectValues, fields, settings.preserveHTML, settings.className) )
+ .insertBefore($input)
+ ;
+ if($input.hasClass(className.multiple) && $input.prop('multiple') === false) {
+ module.error(error.missingMultiple);
+ $input.prop('multiple', true);
+ }
+ if($input.is('[multiple]')) {
+ module.set.multiple();
+ }
+ if ($input.prop('disabled')) {
+ module.debug('Disabling dropdown');
+ $module.addClass(className.disabled);
+ }
+ $input
+ .removeAttr('required')
+ .removeAttr('class')
+ .detach()
+ .prependTo($module)
+ ;
+ }
+ module.refresh();
+ },
+ menu: function(values) {
+ $menu.html( templates.menu(values, fields,settings.preserveHTML,settings.className));
+ $item = $menu.find(selector.item);
+ $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
+ },
+ reference: function() {
+ module.debug('Dropdown behavior was called on select, replacing with closest dropdown');
+ // replace module reference
+ $module = $module.parent(selector.dropdown);
+ instance = $module.data(moduleNamespace);
+ element = $module.get(0);
+ module.refresh();
+ module.setup.returnedObject();
+ },
+ returnedObject: function() {
+ var
+ $firstModules = $allModules.slice(0, elementIndex),
+ $lastModules = $allModules.slice(elementIndex + 1)
+ ;
+ // adjust all modules to use correct reference
+ $allModules = $firstModules.add($module).add($lastModules);
+ }
+ },
+
+ refresh: function() {
+ module.refreshSelectors();
+ module.refreshData();
+ },
+
+ refreshItems: function() {
+ $item = $menu.find(selector.item);
+ $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
+ },
+
+ refreshSelectors: function() {
+ module.verbose('Refreshing selector cache');
+ $text = $module.find(selector.text);
+ $search = $module.find(selector.search);
+ $input = $module.find(selector.input);
+ $icon = $module.find(selector.icon);
+ $combo = ($module.prev().find(selector.text).length > 0)
+ ? $module.prev().find(selector.text)
+ : $module.prev()
+ ;
+ $menu = $module.children(selector.menu);
+ $item = $menu.find(selector.item);
+ $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
+ },
+
+ refreshData: function() {
+ module.verbose('Refreshing cached metadata');
+ $item
+ .removeData(metadata.text)
+ .removeData(metadata.value)
+ ;
+ },
+
+ clearData: function() {
+ module.verbose('Clearing metadata');
+ $item
+ .removeData(metadata.text)
+ .removeData(metadata.value)
+ ;
+ $module
+ .removeData(metadata.defaultText)
+ .removeData(metadata.defaultValue)
+ .removeData(metadata.placeholderText)
+ ;
+ },
+
+ toggle: function() {
+ module.verbose('Toggling menu visibility');
+ if( !module.is.active() ) {
+ module.show();
+ }
+ else {
+ module.hide();
+ }
+ },
+
+ show: function(callback, preventFocus) {
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ if(!module.can.show() && module.is.remote()) {
+ module.debug('No API results retrieved, searching before show');
+ module.queryRemote(module.get.query(), module.show);
+ }
+ if( module.can.show() && !module.is.active() ) {
+ module.debug('Showing dropdown');
+ if(module.has.message() && !(module.has.maxSelections() || module.has.allResultsFiltered()) ) {
+ module.remove.message();
+ }
+ if(module.is.allFiltered()) {
+ return true;
+ }
+ if(settings.onShow.call(element) !== false) {
+ $module.fomanticExt.onDropdownAfterFiltered.call(element); // GITEA-PATCH: callback to correctly handle the filtered items
+ module.animate.show(function() {
+ if( module.can.click() ) {
+ module.bind.intent();
+ }
+ if(module.has.search() && !preventFocus) {
+ module.focusSearch();
+ }
+ module.set.visible();
+ callback.call(element);
+ });
+ }
+ }
+ },
+
+ hide: function(callback, preventBlur) {
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ if( module.is.active() && !module.is.animatingOutward() ) {
+ module.debug('Hiding dropdown');
+ if(settings.onHide.call(element) !== false) {
+ module.animate.hide(function() {
+ module.remove.visible();
+ // hidding search focus
+ if ( module.is.focusedOnSearch() && preventBlur !== true ) {
+ $search.blur();
+ }
+ callback.call(element);
+ });
+ }
+ } else if( module.can.click() ) {
+ module.unbind.intent();
+ }
+ iconClicked = false;
+ },
+
+ hideOthers: function() {
+ module.verbose('Finding other dropdowns to hide');
+ $allModules
+ .not($module)
+ .has(selector.menu + '.' + className.visible)
+ .dropdown('hide')
+ ;
+ },
+
+ hideMenu: function() {
+ module.verbose('Hiding menu instantaneously');
+ module.remove.active();
+ module.remove.visible();
+ $menu.transition('hide');
+ },
+
+ hideSubMenus: function() {
+ var
+ $subMenus = $menu.children(selector.item).find(selector.menu)
+ ;
+ module.verbose('Hiding sub menus', $subMenus);
+ $subMenus.transition('hide');
+ },
+
+ bind: {
+ events: function() {
+ module.bind.keyboardEvents();
+ module.bind.inputEvents();
+ module.bind.mouseEvents();
+ },
+ keyboardEvents: function() {
+ module.verbose('Binding keyboard events');
+ $module
+ .on('keydown' + eventNamespace, module.event.keydown)
+ ;
+ if( module.has.search() ) {
+ $module
+ .on(module.get.inputEvent() + eventNamespace, selector.search, module.event.input)
+ ;
+ }
+ if( module.is.multiple() ) {
+ $document
+ .on('keydown' + elementNamespace, module.event.document.keydown)
+ ;
+ }
+ },
+ inputEvents: function() {
+ module.verbose('Binding input change events');
+ $module
+ .on('change' + eventNamespace, selector.input, module.event.change)
+ ;
+ },
+ mouseEvents: function() {
+ module.verbose('Binding mouse events');
+ if(module.is.multiple()) {
+ $module
+ .on(clickEvent + eventNamespace, selector.label, module.event.label.click)
+ .on(clickEvent + eventNamespace, selector.remove, module.event.remove.click)
+ ;
+ }
+ if( module.is.searchSelection() ) {
+ $module
+ .on('mousedown' + eventNamespace, module.event.mousedown)
+ .on('mouseup' + eventNamespace, module.event.mouseup)
+ .on('mousedown' + eventNamespace, selector.menu, module.event.menu.mousedown)
+ .on('mouseup' + eventNamespace, selector.menu, module.event.menu.mouseup)
+ .on(clickEvent + eventNamespace, selector.icon, module.event.icon.click)
+ .on(clickEvent + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
+ .on('focus' + eventNamespace, selector.search, module.event.search.focus)
+ .on(clickEvent + eventNamespace, selector.search, module.event.search.focus)
+ .on('blur' + eventNamespace, selector.search, module.event.search.blur)
+ .on(clickEvent + eventNamespace, selector.text, module.event.text.focus)
+ ;
+ if(module.is.multiple()) {
+ $module
+ .on(clickEvent + eventNamespace, module.event.click)
+ ;
+ }
+ }
+ else {
+ if(settings.on == 'click') {
+ $module
+ .on(clickEvent + eventNamespace, selector.icon, module.event.icon.click)
+ .on(clickEvent + eventNamespace, module.event.test.toggle)
+ ;
+ }
+ else if(settings.on == 'hover') {
+ $module
+ .on('mouseenter' + eventNamespace, module.delay.show)
+ .on('mouseleave' + eventNamespace, module.delay.hide)
+ ;
+ }
+ else {
+ $module
+ .on(settings.on + eventNamespace, module.toggle)
+ ;
+ }
+ $module
+ .on('mousedown' + eventNamespace, module.event.mousedown)
+ .on('mouseup' + eventNamespace, module.event.mouseup)
+ .on('focus' + eventNamespace, module.event.focus)
+ .on(clickEvent + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
+ ;
+ if(module.has.menuSearch() ) {
+ $module
+ .on('blur' + eventNamespace, selector.search, module.event.search.blur)
+ ;
+ }
+ else {
+ $module
+ .on('blur' + eventNamespace, module.event.blur)
+ ;
+ }
+ }
+ $menu
+ .on((hasTouch ? 'touchstart' : 'mouseenter') + eventNamespace, selector.item, module.event.item.mouseenter)
+ .on('mouseleave' + eventNamespace, selector.item, module.event.item.mouseleave)
+ .on('click' + eventNamespace, selector.item, module.event.item.click)
+ ;
+ },
+ intent: function() {
+ module.verbose('Binding hide intent event to document');
+ if(hasTouch) {
+ $document
+ .on('touchstart' + elementNamespace, module.event.test.touch)
+ .on('touchmove' + elementNamespace, module.event.test.touch)
+ ;
+ }
+ $document
+ .on(clickEvent + elementNamespace, module.event.test.hide)
+ ;
+ }
+ },
+
+ unbind: {
+ intent: function() {
+ module.verbose('Removing hide intent event from document');
+ if(hasTouch) {
+ $document
+ .off('touchstart' + elementNamespace)
+ .off('touchmove' + elementNamespace)
+ ;
+ }
+ $document
+ .off(clickEvent + elementNamespace)
+ ;
+ }
+ },
+
+ filter: function(query) {
+ var
+ searchTerm = (query !== undefined)
+ ? query
+ : module.get.query(),
+ afterFiltered = function() {
+ if(module.is.multiple()) {
+ module.filterActive();
+ }
+ if(query || (!query && module.get.activeItem().length == 0)) {
+ module.select.firstUnfiltered();
+ }
+ if( module.has.allResultsFiltered() ) {
+ if( settings.onNoResults.call(element, searchTerm) ) {
+ if(settings.allowAdditions) {
+ if(settings.hideAdditions) {
+ module.verbose('User addition with no menu, setting empty style');
+ module.set.empty();
+ module.hideMenu();
+ }
+ }
+ else {
+ module.verbose('All items filtered, showing message', searchTerm);
+ module.add.message(message.noResults);
+ }
+ }
+ else {
+ module.verbose('All items filtered, hiding dropdown', searchTerm);
+ module.hideMenu();
+ }
+ }
+ else {
+ module.remove.empty();
+ module.remove.message();
+ }
+ if(settings.allowAdditions) {
+ module.add.userSuggestion(module.escape.htmlEntities(query));
+ }
+ if(module.is.searchSelection() && module.can.show() && module.is.focusedOnSearch() ) {
+ module.show();
+ }
+ $module.fomanticExt.onDropdownAfterFiltered.call(element); // GITEA-PATCH: callback to correctly handle the filtered items
+ }
+ ;
+ if(settings.useLabels && module.has.maxSelections()) {
+ return;
+ }
+ if(settings.apiSettings) {
+ if( module.can.useAPI() ) {
+ module.queryRemote(searchTerm, function() {
+ if(settings.filterRemoteData) {
+ module.filterItems(searchTerm);
+ }
+ var preSelected = $input.val();
+ if(!Array.isArray(preSelected)) {
+ preSelected = preSelected && preSelected!=="" ? preSelected.split(settings.delimiter) : [];
+ }
+ $.each(preSelected,function(index,value){
+ $item.filter('[data-value="'+CSS.escape(value)+'"]') // GITEA-PATCH: use "CSS.escape" for query selector
+ .addClass(className.filtered)
+ ;
+ });
+ afterFiltered();
+ });
+ }
+ else {
+ module.error(error.noAPI);
+ }
+ }
+ else {
+ module.filterItems(searchTerm);
+ afterFiltered();
+ }
+ },
+
+ queryRemote: function(query, callback) {
+ var
+ apiSettings = {
+ errorDuration : false,
+ cache : 'local',
+ throttle : settings.throttle,
+ urlData : {
+ query: query
+ },
+ onError: function() {
+ module.add.message(message.serverError);
+ callback();
+ },
+ onFailure: function() {
+ module.add.message(message.serverError);
+ callback();
+ },
+ onSuccess : function(response) {
+ var
+ values = response[fields.remoteValues]
+ ;
+ if (!Array.isArray(values)){
+ values = [];
+ }
+ module.remove.message();
+ var menuConfig = {};
+ menuConfig[fields.values] = values;
+ module.setup.menu(menuConfig);
+
+ if(values.length===0 && !settings.allowAdditions) {
+ module.add.message(message.noResults);
+ }
+ callback();
+ }
+ }
+ ;
+ if( !$module.api('get request') ) {
+ module.setup.api();
+ }
+ apiSettings = $.extend(true, {}, apiSettings, settings.apiSettings);
+ $module
+ .api('setting', apiSettings)
+ .api('query')
+ ;
+ },
+
+ filterItems: function(query) {
+ var
+ searchTerm = module.remove.diacritics(query !== undefined
+ ? query
+ : module.get.query()
+ ),
+ results = null,
+ escapedTerm = module.escape.string(searchTerm),
+ regExpFlags = (settings.ignoreSearchCase ? 'i' : '') + 'gm',
+ beginsWithRegExp = new RegExp('^' + escapedTerm, regExpFlags)
+ ;
+ // avoid loop if we're matching nothing
+ if( module.has.query() ) {
+ results = [];
+
+ module.verbose('Searching for matching values', searchTerm);
+ $item
+ .each(function(){
+ var
+ $choice = $(this),
+ text,
+ value
+ ;
+ if($choice.hasClass(className.unfilterable)) {
+ results.push(this);
+ return true;
+ }
+ if(settings.match === 'both' || settings.match === 'text') {
+ text = module.remove.diacritics(String(module.get.choiceText($choice, false)));
+ if(text.search(beginsWithRegExp) !== -1) {
+ results.push(this);
+ return true;
+ }
+ else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text)) {
+ results.push(this);
+ return true;
+ }
+ else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, text)) {
+ results.push(this);
+ return true;
+ }
+ }
+ if(settings.match === 'both' || settings.match === 'value') {
+ value = module.remove.diacritics(String(module.get.choiceValue($choice, text)));
+ if(value.search(beginsWithRegExp) !== -1) {
+ results.push(this);
+ return true;
+ }
+ else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, value)) {
+ results.push(this);
+ return true;
+ }
+ else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, value)) {
+ results.push(this);
+ return true;
+ }
+ }
+ })
+ ;
+ }
+ module.debug('Showing only matched items', searchTerm);
+ module.remove.filteredItem();
+ if(results) {
+ $item
+ .not(results)
+ .addClass(className.filtered)
+ ;
+ }
+
+ if(!module.has.query()) {
+ $divider
+ .removeClass(className.hidden);
+ } else if(settings.hideDividers === true) {
+ $divider
+ .addClass(className.hidden);
+ } else if(settings.hideDividers === 'empty') {
+ $divider
+ .removeClass(className.hidden)
+ .filter(function() {
+ // First find the last divider in this divider group
+ // Dividers which are direct siblings are considered a group
+ var lastDivider = $(this).nextUntil(selector.item);
+
+ return (lastDivider.length ? lastDivider : $(this))
+ // Count all non-filtered items until the next divider (or end of the dropdown)
+ .nextUntil(selector.divider)
+ .filter(selector.item + ":not(." + className.filtered + ")")
+ // Hide divider if no items are found
+ .length === 0;
+ })
+ .addClass(className.hidden);
+ }
+ },
+
+ fuzzySearch: function(query, term) {
+ var
+ termLength = term.length,
+ queryLength = query.length
+ ;
+ query = (settings.ignoreSearchCase ? query.toLowerCase() : query);
+ term = (settings.ignoreSearchCase ? term.toLowerCase() : term);
+ if(queryLength > termLength) {
+ return false;
+ }
+ if(queryLength === termLength) {
+ return (query === term);
+ }
+ search: for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
+ var
+ queryCharacter = query.charCodeAt(characterIndex)
+ ;
+ while(nextCharacterIndex < termLength) {
+ if(term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
+ continue search;
+ }
+ }
+ return false;
+ }
+ return true;
+ },
+ exactSearch: function (query, term) {
+ query = (settings.ignoreSearchCase ? query.toLowerCase() : query);
+ term = (settings.ignoreSearchCase ? term.toLowerCase() : term);
+ return term.indexOf(query) > -1;
+
+ },
+ filterActive: function() {
+ if(settings.useLabels) {
+ $item.filter('.' + className.active)
+ .addClass(className.filtered)
+ ;
+ }
+ },
+
+ focusSearch: function(skipHandler) {
+ if( module.has.search() && !module.is.focusedOnSearch() ) {
+ if(skipHandler) {
+ $module.off('focus' + eventNamespace, selector.search);
+ $search.focus();
+ $module.on('focus' + eventNamespace, selector.search, module.event.search.focus);
+ }
+ else {
+ $search.focus();
+ }
+ }
+ },
+
+ blurSearch: function() {
+ if( module.has.search() ) {
+ $search.blur();
+ }
+ },
+
+ forceSelection: function() {
+ var
+ $currentlySelected = $item.not(className.filtered).filter('.' + className.selected).eq(0),
+ $activeItem = $item.not(className.filtered).filter('.' + className.active).eq(0),
+ $selectedItem = ($currentlySelected.length > 0)
+ ? $currentlySelected
+ : $activeItem,
+ hasSelected = ($selectedItem.length > 0)
+ ;
+ if(settings.allowAdditions || (hasSelected && !module.is.multiple())) {
+ module.debug('Forcing partial selection to selected item', $selectedItem);
+ module.event.item.click.call($selectedItem, {}, true);
+ }
+ else {
+ module.remove.searchTerm();
+ }
+ },
+
+ change: {
+ values: function(values) {
+ if(!settings.allowAdditions) {
+ module.clear();
+ }
+ module.debug('Creating dropdown with specified values', values);
+ var menuConfig = {};
+ menuConfig[fields.values] = values;
+ module.setup.menu(menuConfig);
+ $.each(values, function(index, item) {
+ if(item.selected == true) {
+ module.debug('Setting initial selection to', item[fields.value]);
+ module.set.selected(item[fields.value]);
+ if(!module.is.multiple()) {
+ return false;
+ }
+ }
+ });
+
+ if(module.has.selectInput()) {
+ module.disconnect.selectObserver();
+ $input.html('');
+ $input.append('<option disabled selected value></option>');
+ $.each(values, function(index, item) {
+ var
+ value = settings.templates.escape(item[fields.value]), // GITEA-PATCH: use "escape" for attribute value
+ name = settings.templates.escape(
+ item[fields.name] || '',
+ settings.preserveHTML
+ )
+ ;
+ $input.append('<option value="' + value + '">' + name + '</option>');
+ });
+ module.observe.select();
+ }
+ }
+ },
+
+ event: {
+ change: function() {
+ if(!internalChange) {
+ module.debug('Input changed, updating selection');
+ module.set.selected();
+ }
+ },
+ focus: function() {
+ if(settings.showOnFocus && !activated && module.is.hidden() && !pageLostFocus) {
+ module.show();
+ }
+ },
+ blur: function(event) {
+ pageLostFocus = (document.activeElement === this);
+ if(!activated && !pageLostFocus) {
+ module.remove.activeLabel();
+ module.hide();
+ }
+ },
+ mousedown: function() {
+ if(module.is.searchSelection()) {
+ // prevent menu hiding on immediate re-focus
+ willRefocus = true;
+ }
+ else {
+ // prevents focus callback from occurring on mousedown
+ activated = true;
+ }
+ },
+ mouseup: function() {
+ if(module.is.searchSelection()) {
+ // prevent menu hiding on immediate re-focus
+ willRefocus = false;
+ }
+ else {
+ activated = false;
+ }
+ },
+ click: function(event) {
+ var
+ $target = $(event.target)
+ ;
+ // focus search
+ if($target.is($module)) {
+ if(!module.is.focusedOnSearch()) {
+ module.focusSearch();
+ }
+ else {
+ module.show();
+ }
+ }
+ },
+ search: {
+ focus: function(event) {
+ activated = true;
+ if(module.is.multiple()) {
+ module.remove.activeLabel();
+ }
+ if(settings.showOnFocus || (event.type !== 'focus' && event.type !== 'focusin')) {
+ module.search();
+ }
+ },
+ blur: function(event) {
+ pageLostFocus = (document.activeElement === this);
+ if(module.is.searchSelection() && !willRefocus) {
+ if(!itemActivated && !pageLostFocus) {
+ if(settings.forceSelection) {
+ module.forceSelection();
+ } else if(!settings.allowAdditions){
+ module.remove.searchTerm();
+ }
+ module.hide();
+ }
+ }
+ willRefocus = false;
+ }
+ },
+ clearIcon: {
+ click: function(event) {
+ module.clear();
+ if(module.is.searchSelection()) {
+ module.remove.searchTerm();
+ }
+ module.hide();
+ event.stopPropagation();
+ }
+ },
+ icon: {
+ click: function(event) {
+ iconClicked=true;
+ // GITEA-PATCH: official dropdown doesn't support the search input in menu
+ // so we need to make the menu could be shown when the search input is in menu and user clicks the icon
+ const searchInputInMenu = Boolean($menu.find('.search > input').length);
+ if(module.has.search() && !searchInputInMenu) {
+ // the search input is in the dropdown element (but not in the popup menu), try to focus it
+ if(!module.is.active()) {
+ if(settings.showOnFocus){
+ module.focusSearch();
+ } else {
+ module.toggle();
+ }
+ } else {
+ module.blurSearch();
+ }
+ } else {
+ module.toggle();
+ }
+ }
+ },
+ text: {
+ focus: function(event) {
+ activated = true;
+ module.focusSearch();
+ }
+ },
+ input: function(event) {
+ if(module.is.multiple() || module.is.searchSelection()) {
+ module.set.filtered();
+ }
+ clearTimeout(module.timer);
+ module.timer = setTimeout(module.search, settings.delay.search);
+ },
+ label: {
+ click: function(event) {
+ var
+ $label = $(this),
+ $labels = $module.find(selector.label),
+ $activeLabels = $labels.filter('.' + className.active),
+ $nextActive = $label.nextAll('.' + className.active),
+ $prevActive = $label.prevAll('.' + className.active),
+ $range = ($nextActive.length > 0)
+ ? $label.nextUntil($nextActive).add($activeLabels).add($label)
+ : $label.prevUntil($prevActive).add($activeLabels).add($label)
+ ;
+ if(event.shiftKey) {
+ $activeLabels.removeClass(className.active);
+ $range.addClass(className.active);
+ }
+ else if(event.ctrlKey) {
+ $label.toggleClass(className.active);
+ }
+ else {
+ $activeLabels.removeClass(className.active);
+ $label.addClass(className.active);
+ }
+ settings.onLabelSelect.apply(this, $labels.filter('.' + className.active));
+ }
+ },
+ remove: {
+ click: function() {
+ var
+ $label = $(this).parent()
+ ;
+ if( $label.hasClass(className.active) ) {
+ // remove all selected labels
+ module.remove.activeLabels();
+ }
+ else {
+ // remove this label only
+ module.remove.activeLabels( $label );
+ }
+ }
+ },
+ test: {
+ toggle: function(event) {
+ var
+ toggleBehavior = (module.is.multiple())
+ ? module.show
+ : module.toggle
+ ;
+ if(module.is.bubbledLabelClick(event) || module.is.bubbledIconClick(event)) {
+ return;
+ }
+ if( module.determine.eventOnElement(event, toggleBehavior) ) {
+ event.preventDefault();
+ }
+ },
+ touch: function(event) {
+ module.determine.eventOnElement(event, function() {
+ if(event.type == 'touchstart') {
+ module.timer = setTimeout(function() {
+ module.hide();
+ }, settings.delay.touch);
+ }
+ else if(event.type == 'touchmove') {
+ clearTimeout(module.timer);
+ }
+ });
+ event.stopPropagation();
+ },
+ hide: function(event) {
+ if(module.determine.eventInModule(event, module.hide)){
+ if(element.id && $(event.target).attr('for') === element.id){
+ event.preventDefault();
+ }
+ }
+ }
+ },
+ class: {
+ mutation: function(mutations) {
+ mutations.forEach(function(mutation) {
+ if(mutation.attributeName === "class") {
+ module.check.disabled();
+ }
+ });
+ }
+ },
+ select: {
+ mutation: function(mutations) {
+ module.debug('<select> modified, recreating menu');
+ if(module.is.selectMutation(mutations)) {
+ module.disconnect.selectObserver();
+ module.refresh();
+ module.setup.select();
+ module.set.selected();
+ module.observe.select();
+ }
+ }
+ },
+ menu: {
+ mutation: function(mutations) {
+ var
+ mutation = mutations[0],
+ $addedNode = mutation.addedNodes
+ ? $(mutation.addedNodes[0])
+ : $(false),
+ $removedNode = mutation.removedNodes
+ ? $(mutation.removedNodes[0])
+ : $(false),
+ $changedNodes = $addedNode.add($removedNode),
+ isUserAddition = $changedNodes.is(selector.addition) || $changedNodes.closest(selector.addition).length > 0,
+ isMessage = $changedNodes.is(selector.message) || $changedNodes.closest(selector.message).length > 0
+ ;
+ if(isUserAddition || isMessage) {
+ module.debug('Updating item selector cache');
+ module.refreshItems();
+ }
+ else {
+ module.debug('Menu modified, updating selector cache');
+ module.refresh();
+ }
+ },
+ mousedown: function() {
+ itemActivated = true;
+ },
+ mouseup: function() {
+ itemActivated = false;
+ }
+ },
+ item: {
+ mouseenter: function(event) {
+ var
+ $target = $(event.target),
+ $item = $(this),
+ $subMenu = $item.children(selector.menu),
+ $otherMenus = $item.siblings(selector.item).children(selector.menu),
+ hasSubMenu = ($subMenu.length > 0),
+ isBubbledEvent = ($subMenu.find($target).length > 0)
+ ;
+ if( !isBubbledEvent && hasSubMenu ) {
+ clearTimeout(module.itemTimer);
+ module.itemTimer = setTimeout(function() {
+ module.verbose('Showing sub-menu', $subMenu);
+ $.each($otherMenus, function() {
+ module.animate.hide(false, $(this));
+ });
+ module.animate.show(false, $subMenu);
+ }, settings.delay.show);
+ event.preventDefault();
+ }
+ },
+ mouseleave: function(event) {
+ var
+ $subMenu = $(this).children(selector.menu)
+ ;
+ if($subMenu.length > 0) {
+ clearTimeout(module.itemTimer);
+ module.itemTimer = setTimeout(function() {
+ module.verbose('Hiding sub-menu', $subMenu);
+ module.animate.hide(false, $subMenu);
+ }, settings.delay.hide);
+ }
+ },
+ click: function (event, skipRefocus) {
+ var
+ $choice = $(this),
+ $target = (event)
+ ? $(event.target)
+ : $(''),
+ $subMenu = $choice.find(selector.menu),
+ text = module.get.choiceText($choice),
+ value = module.get.choiceValue($choice, text),
+ hasSubMenu = ($subMenu.length > 0),
+ isBubbledEvent = ($subMenu.find($target).length > 0)
+ ;
+ // prevents IE11 bug where menu receives focus even though `tabindex=-1`
+ if (document.activeElement.tagName.toLowerCase() !== 'input') {
+ $(document.activeElement).blur();
+ }
+ if(!isBubbledEvent && (!hasSubMenu || settings.allowCategorySelection)) {
+ if(module.is.searchSelection()) {
+ if(settings.allowAdditions) {
+ module.remove.userAddition();
+ }
+ module.remove.searchTerm();
+ if(!module.is.focusedOnSearch() && !(skipRefocus == true)) {
+ module.focusSearch(true);
+ }
+ }
+ if(!settings.useLabels) {
+ module.remove.filteredItem();
+ module.set.scrollPosition($choice);
+ }
+ module.determine.selectAction.call(this, text, value);
+ }
+ }
+ },
+
+ document: {
+ // label selection should occur even when element has no focus
+ keydown: function(event) {
+ var
+ pressedKey = event.which,
+ isShortcutKey = module.is.inObject(pressedKey, keys)
+ ;
+ if(isShortcutKey) {
+ var
+ $label = $module.find(selector.label),
+ $activeLabel = $label.filter('.' + className.active),
+ activeValue = $activeLabel.data(metadata.value),
+ labelIndex = $label.index($activeLabel),
+ labelCount = $label.length,
+ hasActiveLabel = ($activeLabel.length > 0),
+ hasMultipleActive = ($activeLabel.length > 1),
+ isFirstLabel = (labelIndex === 0),
+ isLastLabel = (labelIndex + 1 == labelCount),
+ isSearch = module.is.searchSelection(),
+ isFocusedOnSearch = module.is.focusedOnSearch(),
+ isFocused = module.is.focused(),
+ caretAtStart = (isFocusedOnSearch && module.get.caretPosition(false) === 0),
+ isSelectedSearch = (caretAtStart && module.get.caretPosition(true) !== 0),
+ $nextLabel
+ ;
+ if(isSearch && !hasActiveLabel && !isFocusedOnSearch) {
+ return;
+ }
+
+ if(pressedKey == keys.leftArrow) {
+ // activate previous label
+ if((isFocused || caretAtStart) && !hasActiveLabel) {
+ module.verbose('Selecting previous label');
+ $label.last().addClass(className.active);
+ }
+ else if(hasActiveLabel) {
+ if(!event.shiftKey) {
+ module.verbose('Selecting previous label');
+ $label.removeClass(className.active);
+ }
+ else {
+ module.verbose('Adding previous label to selection');
+ }
+ if(isFirstLabel && !hasMultipleActive) {
+ $activeLabel.addClass(className.active);
+ }
+ else {
+ $activeLabel.prev(selector.siblingLabel)
+ .addClass(className.active)
+ .end()
+ ;
+ }
+ event.preventDefault();
+ }
+ }
+ else if(pressedKey == keys.rightArrow) {
+ // activate first label
+ if(isFocused && !hasActiveLabel) {
+ $label.first().addClass(className.active);
+ }
+ // activate next label
+ if(hasActiveLabel) {
+ if(!event.shiftKey) {
+ module.verbose('Selecting next label');
+ $label.removeClass(className.active);
+ }
+ else {
+ module.verbose('Adding next label to selection');
+ }
+ if(isLastLabel) {
+ if(isSearch) {
+ if(!isFocusedOnSearch) {
+ module.focusSearch();
+ }
+ else {
+ $label.removeClass(className.active);
+ }
+ }
+ else if(hasMultipleActive) {
+ $activeLabel.next(selector.siblingLabel).addClass(className.active);
+ }
+ else {
+ $activeLabel.addClass(className.active);
+ }
+ }
+ else {
+ $activeLabel.next(selector.siblingLabel).addClass(className.active);
+ }
+ event.preventDefault();
+ }
+ }
+ else if(pressedKey == keys.deleteKey || pressedKey == keys.backspace) {
+ if(hasActiveLabel) {
+ module.verbose('Removing active labels');
+ if(isLastLabel) {
+ if(isSearch && !isFocusedOnSearch) {
+ module.focusSearch();
+ }
+ }
+ $activeLabel.last().next(selector.siblingLabel).addClass(className.active);
+ module.remove.activeLabels($activeLabel);
+ event.preventDefault();
+ }
+ else if(caretAtStart && !isSelectedSearch && !hasActiveLabel && pressedKey == keys.backspace) {
+ module.verbose('Removing last label on input backspace');
+ $activeLabel = $label.last().addClass(className.active);
+ module.remove.activeLabels($activeLabel);
+ }
+ }
+ else {
+ $activeLabel.removeClass(className.active);
+ }
+ }
+ }
+ },
+
+ keydown: function(event) {
+ var
+ pressedKey = event.which,
+ isShortcutKey = module.is.inObject(pressedKey, keys)
+ ;
+ if(isShortcutKey) {
+ var
+ $currentlySelected = $item.not(selector.unselectable).filter('.' + className.selected).eq(0),
+ $activeItem = $menu.children('.' + className.active).eq(0),
+ $selectedItem = ($currentlySelected.length > 0)
+ ? $currentlySelected
+ : $activeItem,
+ $visibleItems = ($selectedItem.length > 0)
+ ? $selectedItem.siblings(':not(.' + className.filtered +')').addBack()
+ : $menu.children(':not(.' + className.filtered +')'),
+ $subMenu = $selectedItem.children(selector.menu),
+ $parentMenu = $selectedItem.closest(selector.menu),
+ inVisibleMenu = ($parentMenu.hasClass(className.visible) || $parentMenu.hasClass(className.animating) || $parentMenu.parent(selector.menu).length > 0),
+ hasSubMenu = ($subMenu.length> 0),
+ hasSelectedItem = ($selectedItem.length > 0),
+ selectedIsSelectable = ($selectedItem.not(selector.unselectable).length > 0),
+ delimiterPressed = (pressedKey == keys.delimiter && settings.allowAdditions && module.is.multiple()),
+ isAdditionWithoutMenu = (settings.allowAdditions && settings.hideAdditions && (pressedKey == keys.enter || delimiterPressed) && selectedIsSelectable),
+ $nextItem,
+ isSubMenuItem,
+ newIndex
+ ;
+ // allow selection with menu closed
+ if(isAdditionWithoutMenu) {
+ module.verbose('Selecting item from keyboard shortcut', $selectedItem);
+ module.event.item.click.call($selectedItem, event);
+ if(module.is.searchSelection()) {
+ module.remove.searchTerm();
+ }
+ if(module.is.multiple()){
+ event.preventDefault();
+ }
+ }
+
+ // visible menu keyboard shortcuts
+ if( module.is.visible() ) {
+
+ // enter (select or open sub-menu)
+ if(pressedKey == keys.enter || delimiterPressed) {
+ if(pressedKey == keys.enter && hasSelectedItem && hasSubMenu && !settings.allowCategorySelection) {
+ module.verbose('Pressed enter on unselectable category, opening sub menu');
+ pressedKey = keys.rightArrow;
+ }
+ else if(selectedIsSelectable) {
+ module.verbose('Selecting item from keyboard shortcut', $selectedItem);
+ module.event.item.click.call($selectedItem, event);
+ if(module.is.searchSelection()) {
+ module.remove.searchTerm();
+ if(module.is.multiple()) {
+ $search.focus();
+ }
+ }
+ }
+ event.preventDefault();
+ }
+
+ // sub-menu actions
+ if(hasSelectedItem) {
+
+ if(pressedKey == keys.leftArrow) {
+
+ isSubMenuItem = ($parentMenu[0] !== $menu[0]);
+
+ if(isSubMenuItem) {
+ module.verbose('Left key pressed, closing sub-menu');
+ module.animate.hide(false, $parentMenu);
+ $selectedItem
+ .removeClass(className.selected)
+ ;
+ $parentMenu
+ .closest(selector.item)
+ .addClass(className.selected)
+ ;
+ event.preventDefault();
+ }
+ }
+
+ // right arrow (show sub-menu)
+ if(pressedKey == keys.rightArrow) {
+ if(hasSubMenu) {
+ module.verbose('Right key pressed, opening sub-menu');
+ module.animate.show(false, $subMenu);
+ $selectedItem
+ .removeClass(className.selected)
+ ;
+ $subMenu
+ .find(selector.item).eq(0)
+ .addClass(className.selected)
+ ;
+ event.preventDefault();
+ }
+ }
+ }
+
+ // up arrow (traverse menu up)
+ if(pressedKey == keys.upArrow) {
+ $nextItem = (hasSelectedItem && inVisibleMenu)
+ ? $selectedItem.prevAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
+ : $item.eq(0)
+ ;
+ if($visibleItems.index( $nextItem ) < 0) {
+ module.verbose('Up key pressed but reached top of current menu');
+ event.preventDefault();
+ return;
+ }
+ else {
+ module.verbose('Up key pressed, changing active item');
+ $selectedItem
+ .removeClass(className.selected)
+ ;
+ $nextItem
+ .addClass(className.selected)
+ ;
+ module.set.scrollPosition($nextItem);
+ if(settings.selectOnKeydown && module.is.single()) {
+ module.set.selectedItem($nextItem);
+ }
+ }
+ event.preventDefault();
+ }
+
+ // down arrow (traverse menu down)
+ if(pressedKey == keys.downArrow) {
+ $nextItem = (hasSelectedItem && inVisibleMenu)
+ ? $nextItem = $selectedItem.nextAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
+ : $item.eq(0)
+ ;
+ if($nextItem.length === 0) {
+ module.verbose('Down key pressed but reached bottom of current menu');
+ event.preventDefault();
+ return;
+ }
+ else {
+ module.verbose('Down key pressed, changing active item');
+ $item
+ .removeClass(className.selected)
+ ;
+ $nextItem
+ .addClass(className.selected)
+ ;
+ module.set.scrollPosition($nextItem);
+ if(settings.selectOnKeydown && module.is.single()) {
+ module.set.selectedItem($nextItem);
+ }
+ }
+ event.preventDefault();
+ }
+
+ // page down (show next page)
+ if(pressedKey == keys.pageUp) {
+ module.scrollPage('up');
+ event.preventDefault();
+ }
+ if(pressedKey == keys.pageDown) {
+ module.scrollPage('down');
+ event.preventDefault();
+ }
+
+ // escape (close menu)
+ if(pressedKey == keys.escape) {
+ module.verbose('Escape key pressed, closing dropdown');
+ module.hide();
+ }
+
+ }
+ else {
+ // delimiter key
+ if(delimiterPressed) {
+ event.preventDefault();
+ }
+ // down arrow (open menu)
+ if(pressedKey == keys.downArrow && !module.is.visible()) {
+ module.verbose('Down key pressed, showing dropdown');
+ module.show();
+ event.preventDefault();
+ }
+ }
+ }
+ else {
+ if( !module.has.search() ) {
+ module.set.selectedLetter( String.fromCharCode(pressedKey) );
+ }
+ }
+ }
+ },
+
+ trigger: {
+ change: function() {
+ var
+ inputElement = $input[0]
+ ;
+ if(inputElement) {
+ var events = document.createEvent('HTMLEvents');
+ module.verbose('Triggering native change event');
+ events.initEvent('change', true, false);
+ inputElement.dispatchEvent(events);
+ }
+ }
+ },
+
+ determine: {
+ selectAction: function(text, value) {
+ selectActionActive = true;
+ module.verbose('Determining action', settings.action);
+ if( $.isFunction( module.action[settings.action] ) ) {
+ module.verbose('Triggering preset action', settings.action, text, value);
+ module.action[ settings.action ].call(element, text, value, this);
+ }
+ else if( $.isFunction(settings.action) ) {
+ module.verbose('Triggering user action', settings.action, text, value);
+ settings.action.call(element, text, value, this);
+ }
+ else {
+ module.error(error.action, settings.action);
+ }
+ selectActionActive = false;
+ },
+ eventInModule: function(event, callback) {
+ var
+ $target = $(event.target),
+ inDocument = ($target.closest(document.documentElement).length > 0),
+ inModule = ($target.closest($module).length > 0)
+ ;
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ if(inDocument && !inModule) {
+ module.verbose('Triggering event', callback);
+ callback();
+ return true;
+ }
+ else {
+ module.verbose('Event occurred in dropdown, canceling callback');
+ return false;
+ }
+ },
+ eventOnElement: function(event, callback) {
+ var
+ $target = $(event.target),
+ $label = $target.closest(selector.siblingLabel),
+ inVisibleDOM = document.body.contains(event.target),
+ notOnLabel = ($module.find($label).length === 0 || !(module.is.multiple() && settings.useLabels)),
+ notInMenu = ($target.closest($menu).length === 0)
+ ;
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ if(inVisibleDOM && notOnLabel && notInMenu) {
+ module.verbose('Triggering event', callback);
+ callback();
+ return true;
+ }
+ else {
+ module.verbose('Event occurred in dropdown menu, canceling callback');
+ return false;
+ }
+ }
+ },
+
+ action: {
+
+ nothing: function() {},
+
+ activate: function(text, value, element) {
+ value = (value !== undefined)
+ ? value
+ : text
+ ;
+ if( module.can.activate( $(element) ) ) {
+ module.set.selected(value, $(element));
+ if(!module.is.multiple()) {
+ module.hideAndClear();
+ }
+ }
+ },
+
+ select: function(text, value, element) {
+ value = (value !== undefined)
+ ? value
+ : text
+ ;
+ if( module.can.activate( $(element) ) ) {
+ module.set.value(value, text, $(element));
+ if(!module.is.multiple()) {
+ module.hideAndClear();
+ }
+ }
+ },
+
+ combo: function(text, value, element) {
+ value = (value !== undefined)
+ ? value
+ : text
+ ;
+ module.set.selected(value, $(element));
+ module.hideAndClear();
+ },
+
+ hide: function(text, value, element) {
+ module.set.value(value, text, $(element));
+ module.hideAndClear();
+ }
+
+ },
+
+ get: {
+ id: function() {
+ return id;
+ },
+ defaultText: function() {
+ return $module.data(metadata.defaultText);
+ },
+ defaultValue: function() {
+ return $module.data(metadata.defaultValue);
+ },
+ placeholderText: function() {
+ if(settings.placeholder != 'auto' && typeof settings.placeholder == 'string') {
+ return settings.placeholder;
+ }
+ return $module.data(metadata.placeholderText) || '';
+ },
+ text: function() {
+ return settings.preserveHTML ? $text.html() : $text.text();
+ },
+ query: function() {
+ return String($search.val()).trim();
+ },
+ searchWidth: function(value) {
+ value = (value !== undefined)
+ ? value
+ : $search.val()
+ ;
+ $sizer.text(value);
+ // prevent rounding issues
+ return Math.ceil( $sizer.width() + 1);
+ },
+ selectionCount: function() {
+ var
+ values = module.get.values(),
+ count
+ ;
+ count = ( module.is.multiple() )
+ ? Array.isArray(values)
+ ? values.length
+ : 0
+ : (module.get.value() !== '')
+ ? 1
+ : 0
+ ;
+ return count;
+ },
+ transition: function($subMenu) {
+ return (settings.transition == 'auto')
+ ? module.is.upward($subMenu)
+ ? 'slide up'
+ : 'slide down'
+ : settings.transition
+ ;
+ },
+ userValues: function() {
+ var
+ values = module.get.values()
+ ;
+ if(!values) {
+ return false;
+ }
+ values = Array.isArray(values)
+ ? values
+ : [values]
+ ;
+ return $.grep(values, function(value) {
+ return (module.get.item(value) === false);
+ });
+ },
+ uniqueArray: function(array) {
+ return $.grep(array, function (value, index) {
+ return $.inArray(value, array) === index;
+ });
+ },
+ caretPosition: function(returnEndPos) {
+ var
+ input = $search.get(0),
+ range,
+ rangeLength
+ ;
+ if(returnEndPos && 'selectionEnd' in input){
+ return input.selectionEnd;
+ }
+ else if(!returnEndPos && 'selectionStart' in input) {
+ return input.selectionStart;
+ }
+ if (document.selection) {
+ input.focus();
+ range = document.selection.createRange();
+ rangeLength = range.text.length;
+ if(returnEndPos) {
+ return rangeLength;
+ }
+ range.moveStart('character', -input.value.length);
+ return range.text.length - rangeLength;
+ }
+ },
+ value: function() {
+ var
+ value = ($input.length > 0)
+ ? $input.val()
+ : $module.data(metadata.value),
+ isEmptyMultiselect = (Array.isArray(value) && value.length === 1 && value[0] === '')
+ ;
+ // prevents placeholder element from being selected when multiple
+ return (value === undefined || isEmptyMultiselect)
+ ? ''
+ : value
+ ;
+ },
+ values: function() {
+ var
+ value = module.get.value()
+ ;
+ if(value === '') {
+ return '';
+ }
+ return ( !module.has.selectInput() && module.is.multiple() )
+ ? (typeof value == 'string') // delimited string
+ ? module.escape.htmlEntities(value).split(settings.delimiter)
+ : ''
+ : value
+ ;
+ },
+ remoteValues: function() {
+ var
+ values = module.get.values(),
+ remoteValues = false
+ ;
+ if(values) {
+ if(typeof values == 'string') {
+ values = [values];
+ }
+ $.each(values, function(index, value) {
+ var
+ name = module.read.remoteData(value)
+ ;
+ module.verbose('Restoring value from session data', name, value);
+ if(name) {
+ if(!remoteValues) {
+ remoteValues = {};
+ }
+ remoteValues[value] = name;
+ }
+ });
+ }
+ return remoteValues;
+ },
+ choiceText: function($choice, preserveHTML) {
+ preserveHTML = (preserveHTML !== undefined)
+ ? preserveHTML
+ : settings.preserveHTML
+ ;
+ if($choice) {
+ if($choice.find(selector.menu).length > 0) {
+ module.verbose('Retrieving text of element with sub-menu');
+ $choice = $choice.clone();
+ $choice.find(selector.menu).remove();
+ $choice.find(selector.menuIcon).remove();
+ }
+ return ($choice.data(metadata.text) !== undefined)
+ ? $choice.data(metadata.text)
+ : (preserveHTML)
+ ? $choice.html().trim()
+ : $choice.text().trim()
+ ;
+ }
+ },
+ choiceValue: function($choice, choiceText) {
+ choiceText = choiceText || module.get.choiceText($choice);
+ if(!$choice) {
+ return false;
+ }
+ return ($choice.data(metadata.value) !== undefined)
+ ? String( $choice.data(metadata.value) )
+ : (typeof choiceText === 'string')
+ ? String(
+ settings.ignoreSearchCase
+ ? choiceText.toLowerCase()
+ : choiceText
+ ).trim()
+ : String(choiceText)
+ ;
+ },
+ inputEvent: function() {
+ var
+ input = $search[0]
+ ;
+ if(input) {
+ return (input.oninput !== undefined)
+ ? 'input'
+ : (input.onpropertychange !== undefined)
+ ? 'propertychange'
+ : 'keyup'
+ ;
+ }
+ return false;
+ },
+ selectValues: function() {
+ var
+ select = {},
+ oldGroup = [],
+ values = []
+ ;
+ $module
+ .find('option')
+ .each(function() {
+ var
+ $option = $(this),
+ name = $option.html(),
+ disabled = $option.attr('disabled'),
+ value = ( $option.attr('value') !== undefined )
+ ? $option.attr('value')
+ : name,
+ text = ( $option.data(metadata.text) !== undefined )
+ ? $option.data(metadata.text)
+ : name,
+ group = $option.parent('optgroup')
+ ;
+ if(settings.placeholder === 'auto' && value === '') {
+ select.placeholder = name;
+ }
+ else {
+ if(group.length !== oldGroup.length || group[0] !== oldGroup[0]) {
+ values.push({
+ type: 'header',
+ divider: settings.headerDivider,
+ name: group.attr('label') || ''
+ });
+ oldGroup = group;
+ }
+ values.push({
+ name : name,
+ value : value,
+ text : text,
+ disabled : disabled
+ });
+ }
+ })
+ ;
+ if(settings.placeholder && settings.placeholder !== 'auto') {
+ module.debug('Setting placeholder value to', settings.placeholder);
+ select.placeholder = settings.placeholder;
+ }
+ if(settings.sortSelect) {
+ if(settings.sortSelect === true) {
+ values.sort(function(a, b) {
+ return a.name.localeCompare(b.name);
+ });
+ } else if(settings.sortSelect === 'natural') {
+ values.sort(function(a, b) {
+ return (a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
+ });
+ } else if($.isFunction(settings.sortSelect)) {
+ values.sort(settings.sortSelect);
+ }
+ select[fields.values] = values;
+ module.debug('Retrieved and sorted values from select', select);
+ }
+ else {
+ select[fields.values] = values;
+ module.debug('Retrieved values from select', select);
+ }
+ return select;
+ },
+ activeItem: function() {
+ return $item.filter('.' + className.active);
+ },
+ selectedItem: function() {
+ var
+ $selectedItem = $item.not(selector.unselectable).filter('.' + className.selected)
+ ;
+ return ($selectedItem.length > 0)
+ ? $selectedItem
+ : $item.eq(0)
+ ;
+ },
+ itemWithAdditions: function(value) {
+ var
+ $items = module.get.item(value),
+ $userItems = module.create.userChoice(value),
+ hasUserItems = ($userItems && $userItems.length > 0)
+ ;
+ if(hasUserItems) {
+ $items = ($items.length > 0)
+ ? $items.add($userItems)
+ : $userItems
+ ;
+ }
+ return $items;
+ },
+ item: function(value, strict) {
+ var
+ $selectedItem = false,
+ shouldSearch,
+ isMultiple
+ ;
+ value = (value !== undefined)
+ ? value
+ : ( module.get.values() !== undefined)
+ ? module.get.values()
+ : module.get.text()
+ ;
+ isMultiple = (module.is.multiple() && Array.isArray(value));
+ shouldSearch = (isMultiple)
+ ? (value.length > 0)
+ : (value !== undefined && value !== null)
+ ;
+ strict = (value === '' || value === false || value === true)
+ ? true
+ : strict || false
+ ;
+ if(shouldSearch) {
+ $item
+ .each(function() {
+ var
+ $choice = $(this),
+ optionText = module.get.choiceText($choice),
+ optionValue = module.get.choiceValue($choice, optionText)
+ ;
+ // safe early exit
+ if(optionValue === null || optionValue === undefined) {
+ return;
+ }
+ if(isMultiple) {
+ if($.inArray(module.escape.htmlEntities(String(optionValue)), value.map(function(v){return String(v);})) !== -1) {
+ $selectedItem = ($selectedItem)
+ ? $selectedItem.add($choice)
+ : $choice
+ ;
+ }
+ }
+ else if(strict) {
+ module.verbose('Ambiguous dropdown value using strict type check', $choice, value);
+ if( optionValue === value) {
+ $selectedItem = $choice;
+ return true;
+ }
+ }
+ else {
+ if(settings.ignoreCase) {
+ optionValue = optionValue.toLowerCase();
+ value = value.toLowerCase();
+ }
+ if(module.escape.htmlEntities(String(optionValue)) === module.escape.htmlEntities(String(value))) {
+ module.verbose('Found select item by value', optionValue, value);
+ $selectedItem = $choice;
+ return true;
+ }
+ }
+ })
+ ;
+ }
+ return $selectedItem;
+ }
+ },
+
+ check: {
+ maxSelections: function(selectionCount) {
+ if(settings.maxSelections) {
+ selectionCount = (selectionCount !== undefined)
+ ? selectionCount
+ : module.get.selectionCount()
+ ;
+ if(selectionCount >= settings.maxSelections) {
+ module.debug('Maximum selection count reached');
+ if(settings.useLabels) {
+ $item.addClass(className.filtered);
+ module.add.message(message.maxSelections);
+ }
+ return true;
+ }
+ else {
+ module.verbose('No longer at maximum selection count');
+ module.remove.message();
+ module.remove.filteredItem();
+ if(module.is.searchSelection()) {
+ module.filterItems();
+ }
+ return false;
+ }
+ }
+ return true;
+ },
+ disabled: function(){
+ $search.attr('tabindex',module.is.disabled() ? -1 : 0);
+ }
+ },
+
+ restore: {
+ defaults: function(preventChangeTrigger) {
+ module.clear(preventChangeTrigger);
+ module.restore.defaultText();
+ module.restore.defaultValue();
+ },
+ defaultText: function() {
+ var
+ defaultText = module.get.defaultText(),
+ placeholderText = module.get.placeholderText
+ ;
+ if(defaultText === placeholderText) {
+ module.debug('Restoring default placeholder text', defaultText);
+ module.set.placeholderText(defaultText);
+ }
+ else {
+ module.debug('Restoring default text', defaultText);
+ module.set.text(defaultText);
+ }
+ },
+ placeholderText: function() {
+ module.set.placeholderText();
+ },
+ defaultValue: function() {
+ var
+ defaultValue = module.get.defaultValue()
+ ;
+ if(defaultValue !== undefined) {
+ module.debug('Restoring default value', defaultValue);
+ if(defaultValue !== '') {
+ module.set.value(defaultValue);
+ module.set.selected();
+ }
+ else {
+ module.remove.activeItem();
+ module.remove.selectedItem();
+ }
+ }
+ },
+ labels: function() {
+ if(settings.allowAdditions) {
+ if(!settings.useLabels) {
+ module.error(error.labels);
+ settings.useLabels = true;
+ }
+ module.debug('Restoring selected values');
+ module.create.userLabels();
+ }
+ module.check.maxSelections();
+ },
+ selected: function() {
+ module.restore.values();
+ if(module.is.multiple()) {
+ module.debug('Restoring previously selected values and labels');
+ module.restore.labels();
+ }
+ else {
+ module.debug('Restoring previously selected values');
+ }
+ },
+ values: function() {
+ // prevents callbacks from occurring on initial load
+ module.set.initialLoad();
+ if(settings.apiSettings && settings.saveRemoteData && module.get.remoteValues()) {
+ module.restore.remoteValues();
+ }
+ else {
+ module.set.selected();
+ }
+ var value = module.get.value();
+ if(value && value !== '' && !(Array.isArray(value) && value.length === 0)) {
+ $input.removeClass(className.noselection);
+ } else {
+ $input.addClass(className.noselection);
+ }
+ module.remove.initialLoad();
+ },
+ remoteValues: function() {
+ var
+ values = module.get.remoteValues()
+ ;
+ module.debug('Recreating selected from session data', values);
+ if(values) {
+ if( module.is.single() ) {
+ $.each(values, function(value, name) {
+ module.set.text(name);
+ });
+ }
+ else {
+ $.each(values, function(value, name) {
+ module.add.label(value, name);
+ });
+ }
+ }
+ }
+ },
+
+ read: {
+ remoteData: function(value) {
+ var
+ name
+ ;
+ if(window.Storage === undefined) {
+ module.error(error.noStorage);
+ return;
+ }
+ name = sessionStorage.getItem(value);
+ return (name !== undefined)
+ ? name
+ : false
+ ;
+ }
+ },
+
+ save: {
+ defaults: function() {
+ module.save.defaultText();
+ module.save.placeholderText();
+ module.save.defaultValue();
+ },
+ defaultValue: function() {
+ var
+ value = module.get.value()
+ ;
+ module.verbose('Saving default value as', value);
+ $module.data(metadata.defaultValue, value);
+ },
+ defaultText: function() {
+ var
+ text = module.get.text()
+ ;
+ module.verbose('Saving default text as', text);
+ $module.data(metadata.defaultText, text);
+ },
+ placeholderText: function() {
+ var
+ text
+ ;
+ if(settings.placeholder !== false && $text.hasClass(className.placeholder)) {
+ text = module.get.text();
+ module.verbose('Saving placeholder text as', text);
+ $module.data(metadata.placeholderText, text);
+ }
+ },
+ remoteData: function(name, value) {
+ if(window.Storage === undefined) {
+ module.error(error.noStorage);
+ return;
+ }
+ module.verbose('Saving remote data to session storage', value, name);
+ sessionStorage.setItem(value, name);
+ }
+ },
+
+ clear: function(preventChangeTrigger) {
+ if(module.is.multiple() && settings.useLabels) {
+ module.remove.labels();
+ }
+ else {
+ module.remove.activeItem();
+ module.remove.selectedItem();
+ module.remove.filteredItem();
+ }
+ module.set.placeholderText();
+ module.clearValue(preventChangeTrigger);
+ },
+
+ clearValue: function(preventChangeTrigger) {
+ module.set.value('', null, null, preventChangeTrigger);
+ },
+
+ scrollPage: function(direction, $selectedItem) {
+ var
+ $currentItem = $selectedItem || module.get.selectedItem(),
+ $menu = $currentItem.closest(selector.menu),
+ menuHeight = $menu.outerHeight(),
+ currentScroll = $menu.scrollTop(),
+ itemHeight = $item.eq(0).outerHeight(),
+ itemsPerPage = Math.floor(menuHeight / itemHeight),
+ maxScroll = $menu.prop('scrollHeight'),
+ newScroll = (direction == 'up')
+ ? currentScroll - (itemHeight * itemsPerPage)
+ : currentScroll + (itemHeight * itemsPerPage),
+ $selectableItem = $item.not(selector.unselectable),
+ isWithinRange,
+ $nextSelectedItem,
+ elementIndex
+ ;
+ elementIndex = (direction == 'up')
+ ? $selectableItem.index($currentItem) - itemsPerPage
+ : $selectableItem.index($currentItem) + itemsPerPage
+ ;
+ isWithinRange = (direction == 'up')
+ ? (elementIndex >= 0)
+ : (elementIndex < $selectableItem.length)
+ ;
+ $nextSelectedItem = (isWithinRange)
+ ? $selectableItem.eq(elementIndex)
+ : (direction == 'up')
+ ? $selectableItem.first()
+ : $selectableItem.last()
+ ;
+ if($nextSelectedItem.length > 0) {
+ module.debug('Scrolling page', direction, $nextSelectedItem);
+ $currentItem
+ .removeClass(className.selected)
+ ;
+ $nextSelectedItem
+ .addClass(className.selected)
+ ;
+ if(settings.selectOnKeydown && module.is.single()) {
+ module.set.selectedItem($nextSelectedItem);
+ }
+ $menu
+ .scrollTop(newScroll)
+ ;
+ }
+ },
+
+ set: {
+ filtered: function() {
+ var
+ isMultiple = module.is.multiple(),
+ isSearch = module.is.searchSelection(),
+ isSearchMultiple = (isMultiple && isSearch),
+ searchValue = (isSearch)
+ ? module.get.query()
+ : '',
+ hasSearchValue = (typeof searchValue === 'string' && searchValue.length > 0),
+ searchWidth = module.get.searchWidth(),
+ valueIsSet = searchValue !== ''
+ ;
+ if(isMultiple && hasSearchValue) {
+ module.verbose('Adjusting input width', searchWidth, settings.glyphWidth);
+ $search.css('width', searchWidth);
+ }
+ if(hasSearchValue || (isSearchMultiple && valueIsSet)) {
+ module.verbose('Hiding placeholder text');
+ $text.addClass(className.filtered);
+ }
+ else if(!isMultiple || (isSearchMultiple && !valueIsSet)) {
+ module.verbose('Showing placeholder text');
+ $text.removeClass(className.filtered);
+ }
+ },
+ empty: function() {
+ $module.addClass(className.empty);
+ },
+ loading: function() {
+ $module.addClass(className.loading);
+ },
+ placeholderText: function(text) {
+ text = text || module.get.placeholderText();
+ module.debug('Setting placeholder text', text);
+ module.set.text(text);
+ $text.addClass(className.placeholder);
+ },
+ tabbable: function() {
+ if( module.is.searchSelection() ) {
+ module.debug('Added tabindex to searchable dropdown');
+ $search
+ .val('')
+ ;
+ module.check.disabled();
+ $menu
+ .attr('tabindex', -1)
+ ;
+ }
+ else {
+ module.debug('Added tabindex to dropdown');
+ if( $module.attr('tabindex') === undefined) {
+ $module
+ .attr('tabindex', 0)
+ ;
+ $menu
+ .attr('tabindex', -1)
+ ;
+ }
+ }
+ },
+ initialLoad: function() {
+ module.verbose('Setting initial load');
+ initialLoad = true;
+ },
+ activeItem: function($item) {
+ if( settings.allowAdditions && $item.filter(selector.addition).length > 0 ) {
+ $item.addClass(className.filtered);
+ }
+ else {
+ $item.addClass(className.active);
+ }
+ },
+ partialSearch: function(text) {
+ var
+ length = module.get.query().length
+ ;
+ $search.val( text.substr(0, length));
+ },
+ scrollPosition: function($item, forceScroll) {
+ var
+ edgeTolerance = 5,
+ $menu,
+ hasActive,
+ offset,
+ itemHeight,
+ itemOffset,
+ menuOffset,
+ menuScroll,
+ menuHeight,
+ abovePage,
+ belowPage
+ ;
+
+ $item = $item || module.get.selectedItem();
+ $menu = $item.closest(selector.menu);
+ hasActive = ($item && $item.length > 0);
+ forceScroll = (forceScroll !== undefined)
+ ? forceScroll
+ : false
+ ;
+ if(module.get.activeItem().length === 0){
+ forceScroll = false;
+ }
+ if($item && $menu.length > 0 && hasActive) {
+ itemOffset = $item.position().top;
+
+ $menu.addClass(className.loading);
+ menuScroll = $menu.scrollTop();
+ menuOffset = $menu.offset().top;
+ itemOffset = $item.offset().top;
+ offset = menuScroll - menuOffset + itemOffset;
+ if(!forceScroll) {
+ menuHeight = $menu.height();
+ belowPage = menuScroll + menuHeight < (offset + edgeTolerance);
+ abovePage = ((offset - edgeTolerance) < menuScroll);
+ }
+ module.debug('Scrolling to active item', offset);
+ if(forceScroll || abovePage || belowPage) {
+ $menu.scrollTop(offset);
+ }
+ $menu.removeClass(className.loading);
+ }
+ },
+ text: function(text) {
+ if(settings.action === 'combo') {
+ module.debug('Changing combo button text', text, $combo);
+ if(settings.preserveHTML) {
+ $combo.html(text);
+ }
+ else {
+ $combo.text(text);
+ }
+ }
+ else if(settings.action === 'activate') {
+ if(text !== module.get.placeholderText()) {
+ $text.removeClass(className.placeholder);
+ }
+ module.debug('Changing text', text, $text);
+ $text
+ .removeClass(className.filtered)
+ ;
+ if(settings.preserveHTML) {
+ $text.html(text);
+ }
+ else {
+ $text.text(text);
+ }
+ }
+ },
+ selectedItem: function($item) {
+ var
+ value = module.get.choiceValue($item),
+ searchText = module.get.choiceText($item, false),
+ text = module.get.choiceText($item, true)
+ ;
+ module.debug('Setting user selection to item', $item);
+ module.remove.activeItem();
+ module.set.partialSearch(searchText);
+ module.set.activeItem($item);
+ module.set.selected(value, $item);
+ module.set.text(text);
+ },
+ selectedLetter: function(letter) {
+ var
+ $selectedItem = $item.filter('.' + className.selected),
+ alreadySelectedLetter = $selectedItem.length > 0 && module.has.firstLetter($selectedItem, letter),
+ $nextValue = false,
+ $nextItem
+ ;
+ // check next of same letter
+ if(alreadySelectedLetter) {
+ $nextItem = $selectedItem.nextAll($item).eq(0);
+ if( module.has.firstLetter($nextItem, letter) ) {
+ $nextValue = $nextItem;
+ }
+ }
+ // check all values
+ if(!$nextValue) {
+ $item
+ .each(function(){
+ if(module.has.firstLetter($(this), letter)) {
+ $nextValue = $(this);
+ return false;
+ }
+ })
+ ;
+ }
+ // set next value
+ if($nextValue) {
+ module.verbose('Scrolling to next value with letter', letter);
+ module.set.scrollPosition($nextValue);
+ $selectedItem.removeClass(className.selected);
+ $nextValue.addClass(className.selected);
+ if(settings.selectOnKeydown && module.is.single()) {
+ module.set.selectedItem($nextValue);
+ }
+ }
+ },
+ direction: function($menu) {
+ if(settings.direction == 'auto') {
+ // reset position, remove upward if it's base menu
+ if (!$menu) {
+ module.remove.upward();
+ } else if (module.is.upward($menu)) {
+ //we need make sure when make assertion openDownward for $menu, $menu does not have upward class
+ module.remove.upward($menu);
+ }
+
+ if(module.can.openDownward($menu)) {
+ module.remove.upward($menu);
+ }
+ else {
+ module.set.upward($menu);
+ }
+ if(!module.is.leftward($menu) && !module.can.openRightward($menu)) {
+ module.set.leftward($menu);
+ }
+ }
+ else if(settings.direction == 'upward') {
+ module.set.upward($menu);
+ }
+ },
+ upward: function($currentMenu) {
+ var $element = $currentMenu || $module;
+ $element.addClass(className.upward);
+ },
+ leftward: function($currentMenu) {
+ var $element = $currentMenu || $menu;
+ $element.addClass(className.leftward);
+ },
+ value: function(value, text, $selected, preventChangeTrigger) {
+ if(value !== undefined && value !== '' && !(Array.isArray(value) && value.length === 0)) {
+ $input.removeClass(className.noselection);
+ } else {
+ $input.addClass(className.noselection);
+ }
+ var
+ escapedValue = module.escape.value(value),
+ hasInput = ($input.length > 0),
+ currentValue = module.get.values(),
+ stringValue = (value !== undefined)
+ ? String(value)
+ : value,
+ newValue
+ ;
+ if(hasInput) {
+ if(!settings.allowReselection && stringValue == currentValue) {
+ module.verbose('Skipping value update already same value', value, currentValue);
+ if(!module.is.initialLoad()) {
+ return;
+ }
+ }
+
+ if( module.is.single() && module.has.selectInput() && module.can.extendSelect() ) {
+ module.debug('Adding user option', value);
+ module.add.optionValue(value);
+ }
+ module.debug('Updating input value', escapedValue, currentValue);
+ internalChange = true;
+ $input
+ .val(escapedValue)
+ ;
+ if(settings.fireOnInit === false && module.is.initialLoad()) {
+ module.debug('Input native change event ignored on initial load');
+ }
+ else if(preventChangeTrigger !== true) {
+ module.trigger.change();
+ }
+ internalChange = false;
+ }
+ else {
+ module.verbose('Storing value in metadata', escapedValue, $input);
+ if(escapedValue !== currentValue) {
+ $module.data(metadata.value, stringValue);
+ }
+ }
+ if(settings.fireOnInit === false && module.is.initialLoad()) {
+ module.verbose('No callback on initial load', settings.onChange);
+ }
+ else if(preventChangeTrigger !== true) {
+ settings.onChange.call(element, value, text, $selected);
+ }
+ },
+ active: function() {
+ $module
+ .addClass(className.active)
+ ;
+ },
+ multiple: function() {
+ $module.addClass(className.multiple);
+ },
+ visible: function() {
+ $module.addClass(className.visible);
+ },
+ exactly: function(value, $selectedItem) {
+ module.debug('Setting selected to exact values');
+ module.clear();
+ module.set.selected(value, $selectedItem);
+ },
+ selected: function(value, $selectedItem) {
+ var
+ isMultiple = module.is.multiple()
+ ;
+ $selectedItem = (settings.allowAdditions)
+ ? $selectedItem || module.get.itemWithAdditions(value)
+ : $selectedItem || module.get.item(value)
+ ;
+ if(!$selectedItem) {
+ return;
+ }
+ module.debug('Setting selected menu item to', $selectedItem);
+ if(module.is.multiple()) {
+ module.remove.searchWidth();
+ }
+ if(module.is.single()) {
+ module.remove.activeItem();
+ module.remove.selectedItem();
+ }
+ else if(settings.useLabels) {
+ module.remove.selectedItem();
+ }
+ // select each item
+ $selectedItem
+ .each(function() {
+ var
+ $selected = $(this),
+ selectedText = module.get.choiceText($selected),
+ selectedValue = module.get.choiceValue($selected, selectedText),
+
+ isFiltered = $selected.hasClass(className.filtered),
+ isActive = $selected.hasClass(className.active),
+ isUserValue = $selected.hasClass(className.addition),
+ shouldAnimate = (isMultiple && $selectedItem.length == 1)
+ ;
+ if(isMultiple) {
+ if(!isActive || isUserValue) {
+ if(settings.apiSettings && settings.saveRemoteData) {
+ module.save.remoteData(selectedText, selectedValue);
+ }
+ if(settings.useLabels) {
+ module.add.label(selectedValue, selectedText, shouldAnimate);
+ module.add.value(selectedValue, selectedText, $selected);
+ module.set.activeItem($selected);
+ module.filterActive();
+ module.select.nextAvailable($selectedItem);
+ }
+ else {
+ module.add.value(selectedValue, selectedText, $selected);
+ module.set.text(module.add.variables(message.count));
+ module.set.activeItem($selected);
+ }
+ }
+ else if(!isFiltered && (settings.useLabels || selectActionActive)) {
+ module.debug('Selected active value, removing label');
+ module.remove.selected(selectedValue);
+ }
+ }
+ else {
+ if(settings.apiSettings && settings.saveRemoteData) {
+ module.save.remoteData(selectedText, selectedValue);
+ }
+ module.set.text(selectedText);
+ module.set.value(selectedValue, selectedText, $selected);
+ $selected
+ .addClass(className.active)
+ .addClass(className.selected)
+ ;
+ }
+ })
+ ;
+ module.remove.searchTerm();
+ }
+ },
+
+ add: {
+ label: function(value, text, shouldAnimate) {
+ var
+ $next = module.is.searchSelection()
+ ? $search
+ : $text,
+ escapedValue = module.escape.value(value),
+ $label
+ ;
+ if(settings.ignoreCase) {
+ escapedValue = escapedValue.toLowerCase();
+ }
+ $label = $('<a />')
+ .addClass(className.label)
+ .attr('data-' + metadata.value, escapedValue)
+ .html(templates.label(escapedValue, text, settings.preserveHTML, settings.className))
+ ;
+ $label = settings.onLabelCreate.call($label, escapedValue, text);
+
+ if(module.has.label(value)) {
+ module.debug('User selection already exists, skipping', escapedValue);
+ return;
+ }
+ if(settings.label.variation) {
+ $label.addClass(settings.label.variation);
+ }
+ if(shouldAnimate === true) {
+ module.debug('Animating in label', $label);
+ $label
+ .addClass(className.hidden)
+ .insertBefore($next)
+ .transition({
+ animation : settings.label.transition,
+ debug : settings.debug,
+ verbose : settings.verbose,
+ duration : settings.label.duration
+ })
+ ;
+ }
+ else {
+ module.debug('Adding selection label', $label);
+ $label
+ .insertBefore($next)
+ ;
+ }
+ },
+ message: function(message) {
+ var
+ $message = $menu.children(selector.message),
+ html = settings.templates.message(module.add.variables(message))
+ ;
+ if($message.length > 0) {
+ $message
+ .html(html)
+ ;
+ }
+ else {
+ $message = $('<div/>')
+ .html(html)
+ .addClass(className.message)
+ .appendTo($menu)
+ ;
+ }
+ },
+ optionValue: function(value) {
+ var
+ escapedValue = module.escape.value(value),
+ $option = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
+ hasOption = ($option.length > 0)
+ ;
+ if(hasOption) {
+ return;
+ }
+ // temporarily disconnect observer
+ module.disconnect.selectObserver();
+ if( module.is.single() ) {
+ module.verbose('Removing previous user addition');
+ $input.find('option.' + className.addition).remove();
+ }
+ $('<option/>')
+ .prop('value', escapedValue)
+ .addClass(className.addition)
+ .html(value)
+ .appendTo($input)
+ ;
+ module.verbose('Adding user addition as an <option>', value);
+ module.observe.select();
+ },
+ userSuggestion: function(value) {
+ var
+ $addition = $menu.children(selector.addition),
+ $existingItem = module.get.item(value),
+ alreadyHasValue = $existingItem && $existingItem.not(selector.addition).length,
+ hasUserSuggestion = $addition.length > 0,
+ html
+ ;
+ if(settings.useLabels && module.has.maxSelections()) {
+ return;
+ }
+ if(value === '' || alreadyHasValue) {
+ $addition.remove();
+ return;
+ }
+ if(hasUserSuggestion) {
+ $addition
+ .data(metadata.value, value)
+ .data(metadata.text, value)
+ .attr('data-' + metadata.value, value)
+ .attr('data-' + metadata.text, value)
+ .removeClass(className.filtered)
+ ;
+ if(!settings.hideAdditions) {
+ html = settings.templates.addition( module.add.variables(message.addResult, value) );
+ $addition
+ .html(html)
+ ;
+ }
+ module.verbose('Replacing user suggestion with new value', $addition);
+ }
+ else {
+ $addition = module.create.userChoice(value);
+ $addition
+ .prependTo($menu)
+ ;
+ module.verbose('Adding item choice to menu corresponding with user choice addition', $addition);
+ }
+ if(!settings.hideAdditions || module.is.allFiltered()) {
+ $addition
+ .addClass(className.selected)
+ .siblings()
+ .removeClass(className.selected)
+ ;
+ }
+ module.refreshItems();
+ },
+ variables: function(message, term) {
+ var
+ hasCount = (message.search('{count}') !== -1),
+ hasMaxCount = (message.search('{maxCount}') !== -1),
+ hasTerm = (message.search('{term}') !== -1),
+ count,
+ query
+ ;
+ module.verbose('Adding templated variables to message', message);
+ if(hasCount) {
+ count = module.get.selectionCount();
+ message = message.replace('{count}', count);
+ }
+ if(hasMaxCount) {
+ count = module.get.selectionCount();
+ message = message.replace('{maxCount}', settings.maxSelections);
+ }
+ if(hasTerm) {
+ query = term || module.get.query();
+ message = message.replace('{term}', query);
+ }
+ return message;
+ },
+ value: function(addedValue, addedText, $selectedItem) {
+ var
+ currentValue = module.get.values(),
+ newValue
+ ;
+ if(module.has.value(addedValue)) {
+ module.debug('Value already selected');
+ return;
+ }
+ if(addedValue === '') {
+ module.debug('Cannot select blank values from multiselect');
+ return;
+ }
+ // extend current array
+ if(Array.isArray(currentValue)) {
+ newValue = currentValue.concat([addedValue]);
+ newValue = module.get.uniqueArray(newValue);
+ }
+ else {
+ newValue = [addedValue];
+ }
+ // add values
+ if( module.has.selectInput() ) {
+ if(module.can.extendSelect()) {
+ module.debug('Adding value to select', addedValue, newValue, $input);
+ module.add.optionValue(addedValue);
+ }
+ }
+ else {
+ newValue = newValue.join(settings.delimiter);
+ module.debug('Setting hidden input to delimited value', newValue, $input);
+ }
+
+ if(settings.fireOnInit === false && module.is.initialLoad()) {
+ module.verbose('Skipping onadd callback on initial load', settings.onAdd);
+ }
+ else {
+ settings.onAdd.call(element, addedValue, addedText, $selectedItem);
+ }
+ module.set.value(newValue, addedText, $selectedItem);
+ module.check.maxSelections();
+ },
+ },
+
+ remove: {
+ active: function() {
+ $module.removeClass(className.active);
+ },
+ activeLabel: function() {
+ $module.find(selector.label).removeClass(className.active);
+ },
+ empty: function() {
+ $module.removeClass(className.empty);
+ },
+ loading: function() {
+ $module.removeClass(className.loading);
+ },
+ initialLoad: function() {
+ initialLoad = false;
+ },
+ upward: function($currentMenu) {
+ var $element = $currentMenu || $module;
+ $element.removeClass(className.upward);
+ },
+ leftward: function($currentMenu) {
+ var $element = $currentMenu || $menu;
+ $element.removeClass(className.leftward);
+ },
+ visible: function() {
+ $module.removeClass(className.visible);
+ },
+ activeItem: function() {
+ $item.removeClass(className.active);
+ },
+ filteredItem: function() {
+ if(settings.useLabels && module.has.maxSelections() ) {
+ return;
+ }
+ if(settings.useLabels && module.is.multiple()) {
+ $item.not('.' + className.active).removeClass(className.filtered);
+ }
+ else {
+ $item.removeClass(className.filtered);
+ }
+ if(settings.hideDividers) {
+ $divider.removeClass(className.hidden);
+ }
+ module.remove.empty();
+ },
+ optionValue: function(value) {
+ var
+ escapedValue = module.escape.value(value),
+ $option = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
+ hasOption = ($option.length > 0)
+ ;
+ if(!hasOption || !$option.hasClass(className.addition)) {
+ return;
+ }
+ // temporarily disconnect observer
+ if(selectObserver) {
+ selectObserver.disconnect();
+ module.verbose('Temporarily disconnecting mutation observer');
+ }
+ $option.remove();
+ module.verbose('Removing user addition as an <option>', escapedValue);
+ if(selectObserver) {
+ selectObserver.observe($input[0], {
+ childList : true,
+ subtree : true
+ });
+ }
+ },
+ message: function() {
+ $menu.children(selector.message).remove();
+ },
+ searchWidth: function() {
+ $search.css('width', '');
+ },
+ searchTerm: function() {
+ module.verbose('Cleared search term');
+ $search.val('');
+ module.set.filtered();
+ },
+ userAddition: function() {
+ $item.filter(selector.addition).remove();
+ },
+ selected: function(value, $selectedItem) {
+ $selectedItem = (settings.allowAdditions)
+ ? $selectedItem || module.get.itemWithAdditions(value)
+ : $selectedItem || module.get.item(value)
+ ;
+
+ if(!$selectedItem) {
+ return false;
+ }
+
+ $selectedItem
+ .each(function() {
+ var
+ $selected = $(this),
+ selectedText = module.get.choiceText($selected),
+ selectedValue = module.get.choiceValue($selected, selectedText)
+ ;
+ if(module.is.multiple()) {
+ if(settings.useLabels) {
+ module.remove.value(selectedValue, selectedText, $selected);
+ module.remove.label(selectedValue);
+ }
+ else {
+ module.remove.value(selectedValue, selectedText, $selected);
+ if(module.get.selectionCount() === 0) {
+ module.set.placeholderText();
+ }
+ else {
+ module.set.text(module.add.variables(message.count));
+ }
+ }
+ }
+ else {
+ module.remove.value(selectedValue, selectedText, $selected);
+ }
+ $selected
+ .removeClass(className.filtered)
+ .removeClass(className.active)
+ ;
+ if(settings.useLabels) {
+ $selected.removeClass(className.selected);
+ }
+ })
+ ;
+ },
+ selectedItem: function() {
+ $item.removeClass(className.selected);
+ },
+ value: function(removedValue, removedText, $removedItem) {
+ var
+ values = module.get.values(),
+ newValue
+ ;
+ removedValue = module.escape.htmlEntities(removedValue);
+ if( module.has.selectInput() ) {
+ module.verbose('Input is <select> removing selected option', removedValue);
+ newValue = module.remove.arrayValue(removedValue, values);
+ module.remove.optionValue(removedValue);
+ }
+ else {
+ module.verbose('Removing from delimited values', removedValue);
+ newValue = module.remove.arrayValue(removedValue, values);
+ newValue = newValue.join(settings.delimiter);
+ }
+ if(settings.fireOnInit === false && module.is.initialLoad()) {
+ module.verbose('No callback on initial load', settings.onRemove);
+ }
+ else {
+ settings.onRemove.call(element, removedValue, removedText, $removedItem);
+ }
+ module.set.value(newValue, removedText, $removedItem);
+ module.check.maxSelections();
+ },
+ arrayValue: function(removedValue, values) {
+ if( !Array.isArray(values) ) {
+ values = [values];
+ }
+ values = $.grep(values, function(value){
+ return (removedValue != value);
+ });
+ module.verbose('Removed value from delimited string', removedValue, values);
+ return values;
+ },
+ label: function(value, shouldAnimate) {
+ var
+ $labels = $module.find(selector.label),
+ $removedLabel = $labels.filter('[data-' + metadata.value + '="' + module.escape.string(settings.ignoreCase ? value.toLowerCase() : value) +'"]')
+ ;
+ module.verbose('Removing label', $removedLabel);
+ $removedLabel.remove();
+ },
+ activeLabels: function($activeLabels) {
+ $activeLabels = $activeLabels || $module.find(selector.label).filter('.' + className.active);
+ module.verbose('Removing active label selections', $activeLabels);
+ module.remove.labels($activeLabels);
+ },
+ labels: function($labels) {
+ $labels = $labels || $module.find(selector.label);
+ module.verbose('Removing labels', $labels);
+ $labels
+ .each(function(){
+ var
+ $label = $(this),
+ value = $label.data(metadata.value),
+ stringValue = (value !== undefined)
+ ? String(value)
+ : value,
+ isUserValue = module.is.userValue(stringValue)
+ ;
+ if(settings.onLabelRemove.call($label, value) === false) {
+ module.debug('Label remove callback cancelled removal');
+ return;
+ }
+ module.remove.message();
+ if(isUserValue) {
+ module.remove.value(stringValue);
+ module.remove.label(stringValue);
+ }
+ else {
+ // selected will also remove label
+ module.remove.selected(stringValue);
+ }
+ })
+ ;
+ },
+ tabbable: function() {
+ if( module.is.searchSelection() ) {
+ module.debug('Searchable dropdown initialized');
+ $search
+ .removeAttr('tabindex')
+ ;
+ $menu
+ .removeAttr('tabindex')
+ ;
+ }
+ else {
+ module.debug('Simple selection dropdown initialized');
+ $module
+ .removeAttr('tabindex')
+ ;
+ $menu
+ .removeAttr('tabindex')
+ ;
+ }
+ },
+ diacritics: function(text) {
+ return settings.ignoreDiacritics ? text.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : text;
+ }
+ },
+
+ has: {
+ menuSearch: function() {
+ return (module.has.search() && $search.closest($menu).length > 0);
+ },
+ clearItem: function() {
+ return ($clear.length > 0);
+ },
+ search: function() {
+ return ($search.length > 0);
+ },
+ sizer: function() {
+ return ($sizer.length > 0);
+ },
+ selectInput: function() {
+ return ( $input.is('select') );
+ },
+ minCharacters: function(searchTerm) {
+ if(settings.minCharacters && !iconClicked) {
+ searchTerm = (searchTerm !== undefined)
+ ? String(searchTerm)
+ : String(module.get.query())
+ ;
+ return (searchTerm.length >= settings.minCharacters);
+ }
+ iconClicked=false;
+ return true;
+ },
+ firstLetter: function($item, letter) {
+ var
+ text,
+ firstLetter
+ ;
+ if(!$item || $item.length === 0 || typeof letter !== 'string') {
+ return false;
+ }
+ text = module.get.choiceText($item, false);
+ letter = letter.toLowerCase();
+ firstLetter = String(text).charAt(0).toLowerCase();
+ return (letter == firstLetter);
+ },
+ input: function() {
+ return ($input.length > 0);
+ },
+ items: function() {
+ return ($item.length > 0);
+ },
+ menu: function() {
+ return ($menu.length > 0);
+ },
+ message: function() {
+ return ($menu.children(selector.message).length !== 0);
+ },
+ label: function(value) {
+ var
+ escapedValue = module.escape.value(value),
+ $labels = $module.find(selector.label)
+ ;
+ if(settings.ignoreCase) {
+ escapedValue = escapedValue.toLowerCase();
+ }
+ return ($labels.filter('[data-' + metadata.value + '="' + module.escape.string(escapedValue) +'"]').length > 0);
+ },
+ maxSelections: function() {
+ return (settings.maxSelections && module.get.selectionCount() >= settings.maxSelections);
+ },
+ allResultsFiltered: function() {
+ var
+ $normalResults = $item.not(selector.addition)
+ ;
+ return ($normalResults.filter(selector.unselectable).length === $normalResults.length);
+ },
+ userSuggestion: function() {
+ return ($menu.children(selector.addition).length > 0);
+ },
+ query: function() {
+ return (module.get.query() !== '');
+ },
+ value: function(value) {
+ return (settings.ignoreCase)
+ ? module.has.valueIgnoringCase(value)
+ : module.has.valueMatchingCase(value)
+ ;
+ },
+ valueMatchingCase: function(value) {
+ var
+ values = module.get.values(),
+ hasValue = Array.isArray(values)
+ ? values && ($.inArray(value, values) !== -1)
+ : (values == value)
+ ;
+ return (hasValue)
+ ? true
+ : false
+ ;
+ },
+ valueIgnoringCase: function(value) {
+ var
+ values = module.get.values(),
+ hasValue = false
+ ;
+ if(!Array.isArray(values)) {
+ values = [values];
+ }
+ $.each(values, function(index, existingValue) {
+ if(String(value).toLowerCase() == String(existingValue).toLowerCase()) {
+ hasValue = true;
+ return false;
+ }
+ });
+ return hasValue;
+ }
+ },
+
+ is: {
+ active: function() {
+ return $module.hasClass(className.active);
+ },
+ animatingInward: function() {
+ return $menu.transition('is inward');
+ },
+ animatingOutward: function() {
+ return $menu.transition('is outward');
+ },
+ bubbledLabelClick: function(event) {
+ return $(event.target).is('select, input') && $module.closest('label').length > 0;
+ },
+ bubbledIconClick: function(event) {
+ return $(event.target).closest($icon).length > 0;
+ },
+ alreadySetup: function() {
+ return ($module.is('select') && $module.parent(selector.dropdown).data(moduleNamespace) !== undefined && $module.prev().length === 0);
+ },
+ animating: function($subMenu) {
+ return ($subMenu)
+ ? $subMenu.transition && $subMenu.transition('is animating')
+ : $menu.transition && $menu.transition('is animating')
+ ;
+ },
+ leftward: function($subMenu) {
+ var $selectedMenu = $subMenu || $menu;
+ return $selectedMenu.hasClass(className.leftward);
+ },
+ clearable: function() {
+ return ($module.hasClass(className.clearable) || settings.clearable);
+ },
+ disabled: function() {
+ return $module.hasClass(className.disabled);
+ },
+ focused: function() {
+ return (document.activeElement === $module[0]);
+ },
+ focusedOnSearch: function() {
+ return (document.activeElement === $search[0]);
+ },
+ allFiltered: function() {
+ return( (module.is.multiple() || module.has.search()) && !(settings.hideAdditions == false && module.has.userSuggestion()) && !module.has.message() && module.has.allResultsFiltered() );
+ },
+ hidden: function($subMenu) {
+ return !module.is.visible($subMenu);
+ },
+ initialLoad: function() {
+ return initialLoad;
+ },
+ inObject: function(needle, object) {
+ var
+ found = false
+ ;
+ $.each(object, function(index, property) {
+ if(property == needle) {
+ found = true;
+ return true;
+ }
+ });
+ return found;
+ },
+ multiple: function() {
+ return $module.hasClass(className.multiple);
+ },
+ remote: function() {
+ return settings.apiSettings && module.can.useAPI();
+ },
+ single: function() {
+ return !module.is.multiple();
+ },
+ selectMutation: function(mutations) {
+ var
+ selectChanged = false
+ ;
+ $.each(mutations, function(index, mutation) {
+ if($(mutation.target).is('select') || $(mutation.addedNodes).is('select')) {
+ selectChanged = true;
+ return false;
+ }
+ });
+ return selectChanged;
+ },
+ search: function() {
+ return $module.hasClass(className.search);
+ },
+ searchSelection: function() {
+ return ( module.has.search() && $search.parent(selector.dropdown).length === 1 );
+ },
+ selection: function() {
+ return $module.hasClass(className.selection);
+ },
+ userValue: function(value) {
+ return ($.inArray(value, module.get.userValues()) !== -1);
+ },
+ upward: function($menu) {
+ var $element = $menu || $module;
+ return $element.hasClass(className.upward);
+ },
+ visible: function($subMenu) {
+ return ($subMenu)
+ ? $subMenu.hasClass(className.visible)
+ : $menu.hasClass(className.visible)
+ ;
+ },
+ verticallyScrollableContext: function() {
+ var
+ overflowY = ($context.get(0) !== window)
+ ? $context.css('overflow-y')
+ : false
+ ;
+ return (overflowY == 'auto' || overflowY == 'scroll');
+ },
+ horizontallyScrollableContext: function() {
+ var
+ overflowX = ($context.get(0) !== window)
+ ? $context.css('overflow-X')
+ : false
+ ;
+ return (overflowX == 'auto' || overflowX == 'scroll');
+ }
+ },
+
+ can: {
+ activate: function($item) {
+ if(settings.useLabels) {
+ return true;
+ }
+ if(!module.has.maxSelections()) {
+ return true;
+ }
+ if(module.has.maxSelections() && $item.hasClass(className.active)) {
+ return true;
+ }
+ return false;
+ },
+ openDownward: function($subMenu) {
+ var
+ $currentMenu = $subMenu || $menu,
+ canOpenDownward = true,
+ onScreen = {},
+ calculations
+ ;
+ $currentMenu
+ .addClass(className.loading)
+ ;
+ calculations = {
+ context: {
+ offset : ($context.get(0) === window)
+ ? { top: 0, left: 0}
+ : $context.offset(),
+ scrollTop : $context.scrollTop(),
+ height : $context.outerHeight()
+ },
+ menu : {
+ offset: $currentMenu.offset(),
+ height: $currentMenu.outerHeight()
+ }
+ };
+ if(module.is.verticallyScrollableContext()) {
+ calculations.menu.offset.top += calculations.context.scrollTop;
+ }
+ onScreen = {
+ above : (calculations.context.scrollTop) <= calculations.menu.offset.top - calculations.context.offset.top - calculations.menu.height,
+ below : (calculations.context.scrollTop + calculations.context.height) >= calculations.menu.offset.top - calculations.context.offset.top + calculations.menu.height
+ };
+ if(onScreen.below) {
+ module.verbose('Dropdown can fit in context downward', onScreen);
+ canOpenDownward = true;
+ }
+ else if(!onScreen.below && !onScreen.above) {
+ module.verbose('Dropdown cannot fit in either direction, favoring downward', onScreen);
+ canOpenDownward = true;
+ }
+ else {
+ module.verbose('Dropdown cannot fit below, opening upward', onScreen);
+ canOpenDownward = false;
+ }
+ $currentMenu.removeClass(className.loading);
+ return canOpenDownward;
+ },
+ openRightward: function($subMenu) {
+ var
+ $currentMenu = $subMenu || $menu,
+ canOpenRightward = true,
+ isOffscreenRight = false,
+ calculations
+ ;
+ $currentMenu
+ .addClass(className.loading)
+ ;
+ calculations = {
+ context: {
+ offset : ($context.get(0) === window)
+ ? { top: 0, left: 0}
+ : $context.offset(),
+ scrollLeft : $context.scrollLeft(),
+ width : $context.outerWidth()
+ },
+ menu: {
+ offset : $currentMenu.offset(),
+ width : $currentMenu.outerWidth()
+ }
+ };
+ if(module.is.horizontallyScrollableContext()) {
+ calculations.menu.offset.left += calculations.context.scrollLeft;
+ }
+ isOffscreenRight = (calculations.menu.offset.left - calculations.context.offset.left + calculations.menu.width >= calculations.context.scrollLeft + calculations.context.width);
+ if(isOffscreenRight) {
+ module.verbose('Dropdown cannot fit in context rightward', isOffscreenRight);
+ canOpenRightward = false;
+ }
+ $currentMenu.removeClass(className.loading);
+ return canOpenRightward;
+ },
+ click: function() {
+ return (hasTouch || settings.on == 'click');
+ },
+ extendSelect: function() {
+ return settings.allowAdditions || settings.apiSettings;
+ },
+ show: function() {
+ return !module.is.disabled() && (module.has.items() || module.has.message());
+ },
+ useAPI: function() {
+ return $.fn.api !== undefined;
+ }
+ },
+
+ animate: {
+ show: function(callback, $subMenu) {
+ var
+ $currentMenu = $subMenu || $menu,
+ start = ($subMenu)
+ ? function() {}
+ : function() {
+ module.hideSubMenus();
+ module.hideOthers();
+ module.set.active();
+ },
+ transition
+ ;
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ module.verbose('Doing menu show animation', $currentMenu);
+ module.set.direction($subMenu);
+ transition = module.get.transition($subMenu);
+ if( module.is.selection() ) {
+ module.set.scrollPosition(module.get.selectedItem(), true);
+ }
+ if( module.is.hidden($currentMenu) || module.is.animating($currentMenu) ) {
+ var displayType = $module.hasClass('column') ? 'flex' : false;
+ if(transition == 'none') {
+ start();
+ $currentMenu.transition({
+ displayType: displayType
+ }).transition('show');
+ callback.call(element);
+ }
+ else if($.fn.transition !== undefined && $module.transition('is supported')) {
+ $currentMenu
+ .transition({
+ animation : transition + ' in',
+ debug : settings.debug,
+ verbose : settings.verbose,
+ duration : settings.duration,
+ queue : true,
+ onStart : start,
+ displayType: displayType,
+ onComplete : function() {
+ callback.call(element);
+ }
+ })
+ ;
+ }
+ else {
+ module.error(error.noTransition, transition);
+ }
+ }
+ },
+ hide: function(callback, $subMenu) {
+ var
+ $currentMenu = $subMenu || $menu,
+ start = ($subMenu)
+ ? function() {}
+ : function() {
+ if( module.can.click() ) {
+ module.unbind.intent();
+ }
+ module.remove.active();
+ },
+ transition = module.get.transition($subMenu)
+ ;
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ if( module.is.visible($currentMenu) || module.is.animating($currentMenu) ) {
+ module.verbose('Doing menu hide animation', $currentMenu);
+
+ if(transition == 'none') {
+ start();
+ $currentMenu.transition('hide');
+ callback.call(element);
+ }
+ else if($.fn.transition !== undefined && $module.transition('is supported')) {
+ $currentMenu
+ .transition({
+ animation : transition + ' out',
+ duration : settings.duration,
+ debug : settings.debug,
+ verbose : settings.verbose,
+ queue : false,
+ onStart : start,
+ onComplete : function() {
+ callback.call(element);
+ }
+ })
+ ;
+ }
+ else {
+ module.error(error.transition);
+ }
+ }
+ }
+ },
+
+ hideAndClear: function() {
+ module.remove.searchTerm();
+ if( module.has.maxSelections() ) {
+ return;
+ }
+ if(module.has.search()) {
+ module.hide(function() {
+ module.remove.filteredItem();
+ });
+ }
+ else {
+ module.hide();
+ }
+ },
+
+ delay: {
+ show: function() {
+ module.verbose('Delaying show event to ensure user intent');
+ clearTimeout(module.timer);
+ module.timer = setTimeout(module.show, settings.delay.show);
+ },
+ hide: function() {
+ module.verbose('Delaying hide event to ensure user intent');
+ clearTimeout(module.timer);
+ module.timer = setTimeout(module.hide, settings.delay.hide);
+ }
+ },
+
+ escape: {
+ value: function(value) {
+ var
+ multipleValues = Array.isArray(value),
+ stringValue = (typeof value === 'string'),
+ isUnparsable = (!stringValue && !multipleValues),
+ hasQuotes = (stringValue && value.search(regExp.quote) !== -1),
+ values = []
+ ;
+ if(isUnparsable || !hasQuotes) {
+ return value;
+ }
+ module.debug('Encoding quote values for use in select', value);
+ if(multipleValues) {
+ $.each(value, function(index, value){
+ values.push(value.replace(regExp.quote, '&quot;'));
+ });
+ return values;
+ }
+ return value.replace(regExp.quote, '&quot;');
+ },
+ string: function(text) {
+ text = String(text);
+ return text.replace(regExp.escape, '\\$&');
+ },
+ htmlEntities: function(string) {
+ var
+ badChars = /[<>"'`]/g,
+ shouldEscape = /[&<>"'`]/,
+ escape = {
+ "<": "&lt;",
+ ">": "&gt;",
+ '"': "&quot;",
+ "'": "&#x27;",
+ "`": "&#x60;"
+ },
+ escapedChar = function(chr) {
+ return escape[chr];
+ }
+ ;
+ if(shouldEscape.test(string)) {
+ string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
+ return string.replace(badChars, escapedChar);
+ }
+ return string;
+ }
+ },
+
+ setting: function(name, value) {
+ module.debug('Changing setting', name, value);
+ if( $.isPlainObject(name) ) {
+ $.extend(true, settings, name);
+ }
+ else if(value !== undefined) {
+ if($.isPlainObject(settings[name])) {
+ $.extend(true, settings[name], value);
+ }
+ else {
+ settings[name] = value;
+ }
+ }
+ else {
+ return settings[name];
+ }
+ },
+ internal: function(name, value) {
+ if( $.isPlainObject(name) ) {
+ $.extend(true, module, name);
+ }
+ else if(value !== undefined) {
+ module[name] = value;
+ }
+ else {
+ return module[name];
+ }
+ },
+ debug: function() {
+ if(!settings.silent && settings.debug) {
+ if(settings.performance) {
+ module.performance.log(arguments);
+ }
+ else {
+ module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
+ module.debug.apply(console, arguments);
+ }
+ }
+ },
+ verbose: function() {
+ if(!settings.silent && settings.verbose && settings.debug) {
+ if(settings.performance) {
+ module.performance.log(arguments);
+ }
+ else {
+ module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
+ module.verbose.apply(console, arguments);
+ }
+ }
+ },
+ error: function() {
+ if(!settings.silent) {
+ module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
+ module.error.apply(console, arguments);
+ }
+ },
+ performance: {
+ log: function(message) {
+ var
+ currentTime,
+ executionTime,
+ previousTime
+ ;
+ if(settings.performance) {
+ currentTime = new Date().getTime();
+ previousTime = time || currentTime;
+ executionTime = currentTime - previousTime;
+ time = currentTime;
+ performance.push({
+ 'Name' : message[0],
+ 'Arguments' : [].slice.call(message, 1) || '',
+ 'Element' : element,
+ 'Execution Time' : executionTime
+ });
+ }
+ clearTimeout(module.performance.timer);
+ module.performance.timer = setTimeout(module.performance.display, 500);
+ },
+ display: function() {
+ var
+ title = settings.name + ':',
+ totalTime = 0
+ ;
+ time = false;
+ clearTimeout(module.performance.timer);
+ $.each(performance, function(index, data) {
+ totalTime += data['Execution Time'];
+ });
+ title += ' ' + totalTime + 'ms';
+ if(moduleSelector) {
+ title += ' \'' + moduleSelector + '\'';
+ }
+ if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
+ console.groupCollapsed(title);
+ if(console.table) {
+ console.table(performance);
+ }
+ else {
+ $.each(performance, function(index, data) {
+ console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
+ });
+ }
+ console.groupEnd();
+ }
+ performance = [];
+ }
+ },
+ invoke: function(query, passedArguments, context) {
+ var
+ object = instance,
+ maxDepth,
+ found,
+ response
+ ;
+ passedArguments = passedArguments || queryArguments;
+ context = element || context;
+ if(typeof query == 'string' && object !== undefined) {
+ query = query.split(/[\. ]/);
+ maxDepth = query.length - 1;
+ $.each(query, function(depth, value) {
+ var camelCaseValue = (depth != maxDepth)
+ ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
+ : query
+ ;
+ if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
+ object = object[camelCaseValue];
+ }
+ else if( object[camelCaseValue] !== undefined ) {
+ found = object[camelCaseValue];
+ return false;
+ }
+ else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
+ object = object[value];
+ }
+ else if( object[value] !== undefined ) {
+ found = object[value];
+ return false;
+ }
+ else {
+ module.error(error.method, query);
+ return false;
+ }
+ });
+ }
+ if ( $.isFunction( found ) ) {
+ response = found.apply(context, passedArguments);
+ }
+ else if(found !== undefined) {
+ response = found;
+ }
+ if(Array.isArray(returnedValue)) {
+ returnedValue.push(response);
+ }
+ else if(returnedValue !== undefined) {
+ returnedValue = [returnedValue, response];
+ }
+ else if(response !== undefined) {
+ returnedValue = response;
+ }
+ return found;
+ }
+ };
+
+ if(methodInvoked) {
+ if(instance === undefined) {
+ module.initialize();
+ }
+ module.invoke(query);
+ }
+ else {
+ if(instance !== undefined) {
+ instance.invoke('destroy');
+ }
+ module.initialize();
+ }
+ })
+ ;
+ return (returnedValue !== undefined)
+ ? returnedValue
+ : $allModules
+ ;
+};
+
+$.fn.dropdown.settings = {
+
+ silent : false,
+ debug : false,
+ verbose : false,
+ performance : true,
+
+ on : 'click', // what event should show menu action on item selection
+ action : 'activate', // action on item selection (nothing, activate, select, combo, hide, function(){})
+
+ values : false, // specify values to use for dropdown
+
+ clearable : false, // whether the value of the dropdown can be cleared
+
+ apiSettings : false,
+ selectOnKeydown : true, // Whether selection should occur automatically when keyboard shortcuts used
+ minCharacters : 0, // Minimum characters required to trigger API call
+
+ filterRemoteData : false, // Whether API results should be filtered after being returned for query term
+ saveRemoteData : true, // Whether remote name/value pairs should be stored in sessionStorage to allow remote data to be restored on page refresh
+
+ throttle : 200, // How long to wait after last user input to search remotely
+
+ context : window, // Context to use when determining if on screen
+ direction : 'auto', // Whether dropdown should always open in one direction
+ keepOnScreen : true, // Whether dropdown should check whether it is on screen before showing
+
+ match : 'both', // what to match against with search selection (both, text, or label)
+ fullTextSearch : false, // search anywhere in value (set to 'exact' to require exact matches)
+ ignoreDiacritics : false, // match results also if they contain diacritics of the same base character (for example searching for "a" will also match "á" or "â" or "à", etc...)
+ hideDividers : false, // Whether to hide any divider elements (specified in selector.divider) that are sibling to any items when searched (set to true will hide all dividers, set to 'empty' will hide them when they are not followed by a visible item)
+
+ placeholder : 'auto', // whether to convert blank <select> values to placeholder text
+ preserveHTML : true, // preserve html when selecting value
+ sortSelect : false, // sort selection on init
+
+ forceSelection : true, // force a choice on blur with search selection
+
+ allowAdditions : false, // whether multiple select should allow user added values
+ ignoreCase : false, // whether to consider case sensitivity when creating labels
+ ignoreSearchCase : true, // whether to consider case sensitivity when filtering items
+ hideAdditions : true, // whether or not to hide special message prompting a user they can enter a value
+
+ maxSelections : false, // When set to a number limits the number of selections to this count
+ useLabels : true, // whether multiple select should filter currently active selections from choices
+ delimiter : ',', // when multiselect uses normal <input> the values will be delimited with this character
+
+ showOnFocus : true, // show menu on focus
+ allowReselection : false, // whether current value should trigger callbacks when reselected
+ allowTab : true, // add tabindex to element
+ allowCategorySelection : false, // allow elements with sub-menus to be selected
+
+ fireOnInit : false, // Whether callbacks should fire when initializing dropdown values
+
+ transition : 'auto', // auto transition will slide down or up based on direction
+ duration : 200, // duration of transition
+
+ glyphWidth : 1.037, // widest glyph width in em (W is 1.037 em) used to calculate multiselect input width
+
+ headerDivider : true, // whether option headers should have an additional divider line underneath when converted from <select> <optgroup>
+
+ // label settings on multi-select
+ label: {
+ transition : 'scale',
+ duration : 200,
+ variation : false
+ },
+
+ // delay before event
+ delay : {
+ hide : 300,
+ show : 200,
+ search : 20,
+ touch : 50
+ },
+
+ /* Callbacks */
+ onChange : function(value, text, $selected){},
+ onAdd : function(value, text, $selected){},
+ onRemove : function(value, text, $selected){},
+
+ onLabelSelect : function($selectedLabels){},
+ onLabelCreate : function(value, text) { return $(this); },
+ onLabelRemove : function(value) { return true; },
+ onNoResults : function(searchTerm) { return true; },
+ onShow : function(){},
+ onHide : function(){},
+
+ /* Component */
+ name : 'Dropdown',
+ namespace : 'dropdown',
+
+ message: {
+ addResult : 'Add <b>{term}</b>',
+ count : '{count} selected',
+ maxSelections : 'Max {maxCount} selections',
+ noResults : 'No results found.',
+ serverError : 'There was an error contacting the server'
+ },
+
+ error : {
+ action : 'You called a dropdown action that was not defined',
+ alreadySetup : 'Once a select has been initialized behaviors must be called on the created ui dropdown',
+ labels : 'Allowing user additions currently requires the use of labels.',
+ missingMultiple : '<select> requires multiple property to be set to correctly preserve multiple values',
+ method : 'The method you called is not defined.',
+ noAPI : 'The API module is required to load resources remotely',
+ noStorage : 'Saving remote data requires session storage',
+ noTransition : 'This module requires ui transitions <https://github.com/Semantic-Org/UI-Transition>',
+ noNormalize : '"ignoreDiacritics" setting will be ignored. Browser does not support String().normalize(). You may consider including <https://cdn.jsdelivr.net/npm/unorm@1.4.1/lib/unorm.min.js> as a polyfill.'
+ },
+
+ regExp : {
+ escape : /[-[\]{}()*+?.,\\^$|#\s:=@]/g,
+ quote : /"/g
+ },
+
+ metadata : {
+ defaultText : 'defaultText',
+ defaultValue : 'defaultValue',
+ placeholderText : 'placeholder',
+ text : 'text',
+ value : 'value'
+ },
+
+ // property names for remote query
+ fields: {
+ remoteValues : 'results', // grouping for api results
+ values : 'values', // grouping for all dropdown values
+ disabled : 'disabled', // whether value should be disabled
+ name : 'name', // displayed dropdown text
+ value : 'value', // actual dropdown value
+ text : 'text', // displayed text when selected
+ type : 'type', // type of dropdown element
+ image : 'image', // optional image path
+ imageClass : 'imageClass', // optional individual class for image
+ icon : 'icon', // optional icon name
+ iconClass : 'iconClass', // optional individual class for icon (for example to use flag instead)
+ class : 'class', // optional individual class for item/header
+ divider : 'divider' // optional divider append for group headers
+ },
+
+ keys : {
+ backspace : 8,
+ delimiter : 188, // comma
+ deleteKey : 46,
+ enter : 13,
+ escape : 27,
+ pageUp : 33,
+ pageDown : 34,
+ leftArrow : 37,
+ upArrow : 38,
+ rightArrow : 39,
+ downArrow : 40
+ },
+
+ selector : {
+ addition : '.addition',
+ divider : '.divider, .header',
+ dropdown : '.ui.dropdown',
+ hidden : '.hidden',
+ icon : '> .dropdown.icon',
+ input : '> input[type="hidden"], > select',
+ item : '.item',
+ label : '> .label',
+ remove : '> .label > .delete.icon',
+ siblingLabel : '.label',
+ menu : '.menu',
+ message : '.message',
+ menuIcon : '.dropdown.icon',
+ search : 'input.search, .menu > .search > input, .menu input.search',
+ sizer : '> span.sizer',
+ text : '> .text:not(.icon)',
+ unselectable : '.disabled, .filtered, .tw-hidden', // GITEA-PATCH: tw-hidden hides the item so it is also unselectable
+ clearIcon : '> .remove.icon'
+ },
+
+ className : {
+ active : 'active',
+ addition : 'addition',
+ animating : 'animating',
+ disabled : 'disabled',
+ empty : 'empty',
+ dropdown : 'ui dropdown',
+ filtered : 'filtered',
+ hidden : 'hidden transition',
+ icon : 'icon',
+ image : 'image',
+ item : 'item',
+ label : 'ui label',
+ loading : 'loading',
+ menu : 'menu',
+ message : 'message',
+ multiple : 'multiple',
+ placeholder : 'default',
+ sizer : 'sizer',
+ search : 'search',
+ selected : 'selected',
+ selection : 'selection',
+ upward : 'upward',
+ leftward : 'left',
+ visible : 'visible',
+ clearable : 'clearable',
+ noselection : 'noselection',
+ delete : 'delete',
+ header : 'header',
+ divider : 'divider',
+ groupIcon : '',
+ unfilterable : 'unfilterable'
+ }
+
+};
+
+/* Templates */
+$.fn.dropdown.settings.templates = {
+ deQuote: function(string) {
+ return String(string).replace(/"/g,"");
+ },
+ escape: function(string, preserveHTML) {
+ if (preserveHTML){
+ return string;
+ }
+ var
+ badChars = /[<>"'`]/g,
+ shouldEscape = /[&<>"'`]/,
+ escape = {
+ "<": "&lt;",
+ ">": "&gt;",
+ '"': "&quot;",
+ "'": "&#x27;",
+ "`": "&#x60;"
+ },
+ escapedChar = function(chr) {
+ return escape[chr];
+ }
+ ;
+ if(shouldEscape.test(string)) {
+ string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
+ return string.replace(badChars, escapedChar);
+ }
+ return string;
+ },
+ // generates dropdown from select values
+ dropdown: function(select, fields, preserveHTML, className) {
+ var
+ placeholder = select.placeholder || false,
+ html = '',
+ escape = $.fn.dropdown.settings.templates.escape
+ ;
+ html += '<i class="dropdown icon"></i>';
+ if(placeholder) {
+ html += '<div class="default text">' + escape(placeholder,preserveHTML) + '</div>';
+ }
+ else {
+ html += '<div class="text"></div>';
+ }
+ html += '<div class="'+className.menu+'">';
+ html += $.fn.dropdown.settings.templates.menu(select, fields, preserveHTML,className);
+ html += '</div>';
+ return html;
+ },
+
+ // generates just menu from select
+ menu: function(response, fields, preserveHTML, className) {
+ var
+ values = response[fields.values] || [],
+ html = '',
+ escape = $.fn.dropdown.settings.templates.escape,
+ deQuote = $.fn.dropdown.settings.templates.deQuote
+ ;
+ $.each(values, function(index, option) {
+ var
+ itemType = (option[fields.type])
+ ? option[fields.type]
+ : 'item'
+ ;
+
+ if( itemType === 'item' ) {
+ var
+ maybeText = (option[fields.text])
+ ? ' data-text="' + escape(option[fields.text]) + '"' // GITEA-PATCH: use "escape" for attribute value
+ : '',
+ maybeDisabled = (option[fields.disabled])
+ ? className.disabled+' '
+ : ''
+ ;
+ // GITEA-PATCH: use "escape" for attribute value
+ html += '<div class="'+ maybeDisabled + (option[fields.class] ? deQuote(option[fields.class]) : className.item)+'" data-value="' + escape(option[fields.value]) + '"' + maybeText + '>';
+ if(option[fields.image]) {
+ html += '<img class="'+(option[fields.imageClass] ? deQuote(option[fields.imageClass]) : className.image)+'" src="' + deQuote(option[fields.image]) + '">';
+ }
+ if(option[fields.icon]) {
+ html += '<i class="'+deQuote(option[fields.icon])+' '+(option[fields.iconClass] ? deQuote(option[fields.iconClass]) : className.icon)+'"></i>';
+ }
+ html += escape(option[fields.name] || '', preserveHTML);
+ html += '</div>';
+ } else if (itemType === 'header') {
+ var groupName = escape(option[fields.name] || '', preserveHTML),
+ groupIcon = option[fields.icon] ? deQuote(option[fields.icon]) : className.groupIcon
+ ;
+ if(groupName !== '' || groupIcon !== '') {
+ html += '<div class="' + (option[fields.class] ? deQuote(option[fields.class]) : className.header) + '">';
+ if (groupIcon !== '') {
+ html += '<i class="' + groupIcon + ' ' + (option[fields.iconClass] ? deQuote(option[fields.iconClass]) : className.icon) + '"></i>';
+ }
+ html += groupName;
+ html += '</div>';
+ }
+ if(option[fields.divider]){
+ html += '<div class="'+className.divider+'"></div>';
+ }
+ }
+ });
+ return html;
+ },
+
+ // generates label for multiselect
+ label: function(value, text, preserveHTML, className) {
+ var
+ escape = $.fn.dropdown.settings.templates.escape;
+ return escape(text,preserveHTML) + '<i class="'+className.delete+' icon"></i>';
+ },
+
+
+ // generates messages like "No results"
+ message: function(message) {
+ return message;
+ },
+
+ // generates user addition to selection menu
+ addition: function(choice) {
+ return choice;
+ }
+
+};
+
+})( jQuery, window, document );
diff --git a/web_src/fomantic/build/components/form.css b/web_src/fomantic/build/components/form.css
new file mode 100644
index 0000000000..0124e2d6b5
--- /dev/null
+++ b/web_src/fomantic/build/components/form.css
@@ -0,0 +1,1633 @@
+/*!
+ * # Fomantic-UI - Form
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+ Elements
+*******************************/
+
+
+/*--------------------
+ Form
+---------------------*/
+
+.ui.form {
+ position: relative;
+ max-width: 100%;
+}
+
+/*--------------------
+ Content
+---------------------*/
+
+.ui.form > p {
+ margin: 1em 0;
+}
+
+/*--------------------
+ Field
+---------------------*/
+
+.ui.form .field {
+ clear: both;
+ margin: 0 0 1em;
+}
+.ui.form .fields .fields,
+.ui.form .field:last-child,
+.ui.form .fields:last-child .field {
+ margin-bottom: 0;
+}
+.ui.form .fields .field {
+ clear: both;
+ margin: 0;
+}
+
+/*--------------------
+ Labels
+---------------------*/
+
+.ui.form .field > label {
+ display: block;
+ margin: 0 0 0.28571429rem 0;
+ color: rgba(0, 0, 0, 0.87);
+ font-size: 0.92857143em;
+ font-weight: 500;
+ text-transform: none;
+}
+
+/*--------------------
+ Standard Inputs
+---------------------*/
+
+.ui.form textarea,
+.ui.form input:not([type]),
+.ui.form input[type="date"],
+.ui.form input[type="datetime-local"],
+.ui.form input[type="email"],
+.ui.form input[type="number"],
+.ui.form input[type="password"],
+.ui.form input[type="search"],
+.ui.form input[type="tel"],
+.ui.form input[type="time"],
+.ui.form input[type="text"],
+.ui.form input[type="file"],
+.ui.form input[type="url"] {
+ width: 100%;
+ vertical-align: top;
+}
+
+/* Set max height on unusual input */
+.ui.form ::-webkit-datetime-edit,
+.ui.form ::-webkit-inner-spin-button {
+ height: 1.21428571em;
+}
+.ui.form input:not([type]),
+.ui.form input[type="date"],
+.ui.form input[type="datetime-local"],
+.ui.form input[type="email"],
+.ui.form input[type="number"],
+.ui.form input[type="password"],
+.ui.form input[type="search"],
+.ui.form input[type="tel"],
+.ui.form input[type="time"],
+.ui.form input[type="text"],
+.ui.form input[type="file"],
+.ui.form input[type="url"] {
+ font-family: var(--fonts-regular);
+ margin: 0;
+ outline: none;
+ -webkit-appearance: none;
+ -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
+ line-height: 1.21428571em;
+ padding: 0.67857143em 1em;
+ font-size: 1em;
+ background: #FFFFFF;
+ border: 1px solid rgba(34, 36, 38, 0.15);
+ color: rgba(0, 0, 0, 0.87);
+ border-radius: 0.28571429rem;
+ box-shadow: 0 0 0 0 transparent inset;
+ transition: color 0.1s ease, border-color 0.1s ease;
+}
+
+/* Text Area */
+.ui.input textarea,
+.ui.form textarea {
+ margin: 0;
+ -webkit-appearance: none;
+ -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
+ padding: 0.78571429em 1em;
+ background: #FFFFFF;
+ border: 1px solid rgba(34, 36, 38, 0.15);
+ outline: none;
+ color: rgba(0, 0, 0, 0.87);
+ border-radius: 0.28571429rem;
+ box-shadow: 0 0 0 0 transparent inset;
+ transition: color 0.1s ease, border-color 0.1s ease;
+ font-size: 1em;
+ font-family: var(--fonts-regular);
+ line-height: 1.2857;
+ resize: vertical;
+}
+.ui.form textarea:not([rows]) {
+ height: 12em;
+ min-height: 8em;
+ max-height: 24em;
+}
+.ui.form textarea,
+.ui.form input[type="checkbox"] {
+ vertical-align: top;
+}
+
+/*--------------------
+ Checkbox margin
+---------------------*/
+
+.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) label + .ui.ui.checkbox {
+ margin-top: 0.7em;
+}
+.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.checkbox {
+ margin-top: 2.41428571em;
+}
+.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.toggle.checkbox {
+ margin-top: 2.21428571em;
+}
+.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.slider.checkbox {
+ margin-top: 2.61428571em;
+}
+.ui.ui.form .field .fields .field:not(:only-child) .ui.checkbox {
+ margin-top: 0.6em;
+}
+.ui.ui.form .field .fields .field:not(:only-child) .ui.toggle.checkbox {
+ margin-top: 0.5em;
+}
+.ui.ui.form .field .fields .field:not(:only-child) .ui.slider.checkbox {
+ margin-top: 0.7em;
+}
+
+/*--------------------------
+ Input w/ attached Button
+---------------------------*/
+
+.ui.form input.attached {
+ width: auto;
+}
+
+/*--------------------
+ Basic Select
+---------------------*/
+
+.ui.form select {
+ display: block;
+ height: auto;
+ width: 100%;
+ background: #FFFFFF;
+ border: 1px solid rgba(34, 36, 38, 0.15);
+ border-radius: 0.28571429rem;
+ box-shadow: 0 0 0 0 transparent inset;
+ padding: 0.62em 1em;
+ color: rgba(0, 0, 0, 0.87);
+ transition: color 0.1s ease, border-color 0.1s ease;
+}
+
+/*--------------------
+ Dropdown
+---------------------*/
+
+
+/* Block */
+.ui.form .field > .selection.dropdown {
+ min-width: auto;
+ width: 100%;
+}
+.ui.form .field > .selection.dropdown > .dropdown.icon {
+ float: right;
+}
+
+/* Inline */
+.ui.form .inline.fields .field > .selection.dropdown,
+.ui.form .inline.field > .selection.dropdown {
+ width: auto;
+}
+.ui.form .inline.fields .field > .selection.dropdown > .dropdown.icon,
+.ui.form .inline.field > .selection.dropdown > .dropdown.icon {
+ float: none;
+}
+
+/*--------------------
+ UI Input
+---------------------*/
+
+
+/* Block */
+.ui.form .field .ui.input,
+.ui.form .fields .field .ui.input,
+.ui.form .wide.field .ui.input {
+ width: 100%;
+}
+
+/* Inline */
+.ui.form .inline.fields .field:not(.wide) .ui.input,
+.ui.form .inline.field:not(.wide) .ui.input {
+ width: auto;
+ vertical-align: middle;
+}
+
+/* Auto Input */
+.ui.form .fields .field .ui.input input,
+.ui.form .field .ui.input input {
+ width: auto;
+}
+
+/* Full Width Input */
+.ui.form .ten.fields .ui.input input,
+.ui.form .nine.fields .ui.input input,
+.ui.form .eight.fields .ui.input input,
+.ui.form .seven.fields .ui.input input,
+.ui.form .six.fields .ui.input input,
+.ui.form .five.fields .ui.input input,
+.ui.form .four.fields .ui.input input,
+.ui.form .three.fields .ui.input input,
+.ui.form .two.fields .ui.input input,
+.ui.form .wide.field .ui.input input {
+ flex: 1 0 auto;
+ width: 0;
+}
+
+/*--------------------
+ Types of Messages
+---------------------*/
+
+.ui.form .error.message,
+.ui.form .error.message:empty {
+ display: none;
+}
+.ui.form .info.message,
+.ui.form .info.message:empty {
+ display: none;
+}
+.ui.form .success.message,
+.ui.form .success.message:empty {
+ display: none;
+}
+.ui.form .warning.message,
+.ui.form .warning.message:empty {
+ display: none;
+}
+
+/* Assumptions */
+.ui.form .message:first-child {
+ margin-top: 0;
+}
+
+/*--------------------
+ Validation Prompt
+---------------------*/
+
+.ui.form .field .prompt.label {
+ white-space: normal;
+ background: #FFFFFF !important;
+ border: 1px solid #E0B4B4 !important;
+ color: #9F3A38 !important;
+}
+.ui.form .inline.fields .field .prompt,
+.ui.form .inline.field .prompt {
+ vertical-align: top;
+ margin: -0.25em 0 -0.5em 0.5em;
+}
+.ui.form .inline.fields .field .prompt:before,
+.ui.form .inline.field .prompt:before {
+ border-width: 0 0 1px 1px;
+ bottom: auto;
+ right: auto;
+ top: 50%;
+ left: 0;
+}
+
+
+/*******************************
+ States
+*******************************/
+
+
+/*--------------------
+ Autofilled
+---------------------*/
+
+.ui.form .field.field input:-webkit-autofill {
+ box-shadow: 0 0 0 100px #FFFFF0 inset !important;
+ border-color: #E5DFA1 !important;
+}
+
+/* Focus */
+.ui.form .field.field input:-webkit-autofill:focus {
+ box-shadow: 0 0 0 100px #FFFFF0 inset !important;
+ border-color: #D5C315 !important;
+}
+
+/*--------------------
+ Placeholder
+---------------------*/
+
+
+/* browsers require these rules separate */
+.ui.form ::-webkit-input-placeholder {
+ color: rgba(191, 191, 191, 0.87);
+}
+.ui.form :-ms-input-placeholder {
+ color: rgba(191, 191, 191, 0.87) !important;
+}
+.ui.form ::-moz-placeholder {
+ color: rgba(191, 191, 191, 0.87);
+}
+.ui.form :focus::-webkit-input-placeholder {
+ color: rgba(115, 115, 115, 0.87);
+}
+.ui.form :focus:-ms-input-placeholder {
+ color: rgba(115, 115, 115, 0.87) !important;
+}
+.ui.form :focus::-moz-placeholder {
+ color: rgba(115, 115, 115, 0.87);
+}
+
+/*--------------------
+ Focus
+---------------------*/
+
+.ui.form input:not([type]):focus,
+.ui.form input[type="date"]:focus,
+.ui.form input[type="datetime-local"]:focus,
+.ui.form input[type="email"]:focus,
+.ui.form input[type="number"]:focus,
+.ui.form input[type="password"]:focus,
+.ui.form input[type="search"]:focus,
+.ui.form input[type="tel"]:focus,
+.ui.form input[type="time"]:focus,
+.ui.form input[type="text"]:focus,
+.ui.form input[type="file"]:focus,
+.ui.form input[type="url"]:focus {
+ color: rgba(0, 0, 0, 0.95);
+ border-color: #85B7D9;
+ border-radius: 0.28571429rem;
+ background: #FFFFFF;
+ box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.35) inset;
+}
+.ui.form .ui.action.input:not([class*="left action"]) input:not([type]):focus,
+.ui.form .ui.action.input:not([class*="left action"]) input[type="date"]:focus,
+.ui.form .ui.action.input:not([class*="left action"]) input[type="datetime-local"]:focus,
+.ui.form .ui.action.input:not([class*="left action"]) input[type="email"]:focus,
+.ui.form .ui.action.input:not([class*="left action"]) input[type="number"]:focus,
+.ui.form .ui.action.input:not([class*="left action"]) input[type="password"]:focus,
+.ui.form .ui.action.input:not([class*="left action"]) input[type="search"]:focus,
+.ui.form .ui.action.input:not([class*="left action"]) input[type="tel"]:focus,
+.ui.form .ui.action.input:not([class*="left action"]) input[type="time"]:focus,
+.ui.form .ui.action.input:not([class*="left action"]) input[type="text"]:focus,
+.ui.form .ui.action.input:not([class*="left action"]) input[type="file"]:focus,
+.ui.form .ui.action.input:not([class*="left action"]) input[type="url"]:focus {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+.ui.form .ui[class*="left action"].input input:not([type]),
+.ui.form .ui[class*="left action"].input input[type="date"],
+.ui.form .ui[class*="left action"].input input[type="datetime-local"],
+.ui.form .ui[class*="left action"].input input[type="email"],
+.ui.form .ui[class*="left action"].input input[type="number"],
+.ui.form .ui[class*="left action"].input input[type="password"],
+.ui.form .ui[class*="left action"].input input[type="search"],
+.ui.form .ui[class*="left action"].input input[type="tel"],
+.ui.form .ui[class*="left action"].input input[type="time"],
+.ui.form .ui[class*="left action"].input input[type="text"],
+.ui.form .ui[class*="left action"].input input[type="file"],
+.ui.form .ui[class*="left action"].input input[type="url"] {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+}
+.ui.form textarea:focus {
+ color: rgba(0, 0, 0, 0.95);
+ border-color: #85B7D9;
+ border-radius: 0.28571429rem;
+ background: #FFFFFF;
+ box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.35) inset;
+ -webkit-appearance: none;
+}
+
+/*--------------------
+ States
+ ---------------------*/
+
+
+/* On Form */
+.ui.form.error .error.message:not(:empty) {
+ display: block;
+}
+.ui.form.error .compact.error.message:not(:empty) {
+ display: inline-block;
+}
+.ui.form.error .icon.error.message:not(:empty) {
+ display: flex;
+}
+
+/* On Field(s) */
+.ui.form .fields.error .error.message:not(:empty),
+.ui.form .field.error .error.message:not(:empty) {
+ display: block;
+}
+.ui.form .fields.error .compact.error.message:not(:empty),
+.ui.form .field.error .compact.error.message:not(:empty) {
+ display: inline-block;
+}
+.ui.form .fields.error .icon.error.message:not(:empty),
+.ui.form .field.error .icon.error.message:not(:empty) {
+ display: flex;
+}
+.ui.ui.form .fields.error .field label,
+.ui.ui.form .field.error label,
+.ui.ui.form .fields.error .field .input,
+.ui.ui.form .field.error .input {
+ color: #9F3A38;
+}
+.ui.form .fields.error .field .corner.label,
+.ui.form .field.error .corner.label {
+ border-color: #9F3A38;
+ color: #FFFFFF;
+}
+.ui.form .fields.error .field textarea,
+.ui.form .fields.error .field select,
+.ui.form .fields.error .field input:not([type]),
+.ui.form .fields.error .field input[type="date"],
+.ui.form .fields.error .field input[type="datetime-local"],
+.ui.form .fields.error .field input[type="email"],
+.ui.form .fields.error .field input[type="number"],
+.ui.form .fields.error .field input[type="password"],
+.ui.form .fields.error .field input[type="search"],
+.ui.form .fields.error .field input[type="tel"],
+.ui.form .fields.error .field input[type="time"],
+.ui.form .fields.error .field input[type="text"],
+.ui.form .fields.error .field input[type="file"],
+.ui.form .fields.error .field input[type="url"],
+.ui.form .field.error textarea,
+.ui.form .field.error select,
+.ui.form .field.error input:not([type]),
+.ui.form .field.error input[type="date"],
+.ui.form .field.error input[type="datetime-local"],
+.ui.form .field.error input[type="email"],
+.ui.form .field.error input[type="number"],
+.ui.form .field.error input[type="password"],
+.ui.form .field.error input[type="search"],
+.ui.form .field.error input[type="tel"],
+.ui.form .field.error input[type="time"],
+.ui.form .field.error input[type="text"],
+.ui.form .field.error input[type="file"],
+.ui.form .field.error input[type="url"] {
+ color: #9F3A38;
+ background: #FFF6F6;
+ border-color: #E0B4B4;
+ border-radius: '';
+ box-shadow: none;
+}
+.ui.form .field.error textarea:focus,
+.ui.form .field.error select:focus,
+.ui.form .field.error input:not([type]):focus,
+.ui.form .field.error input[type="date"]:focus,
+.ui.form .field.error input[type="datetime-local"]:focus,
+.ui.form .field.error input[type="email"]:focus,
+.ui.form .field.error input[type="number"]:focus,
+.ui.form .field.error input[type="password"]:focus,
+.ui.form .field.error input[type="search"]:focus,
+.ui.form .field.error input[type="tel"]:focus,
+.ui.form .field.error input[type="time"]:focus,
+.ui.form .field.error input[type="text"]:focus,
+.ui.form .field.error input[type="file"]:focus,
+.ui.form .field.error input[type="url"]:focus {
+ background: #FFF6F6;
+ border-color: #E0B4B4;
+ color: #9F3A38;
+ box-shadow: none;
+}
+
+/* Preserve Native Select Stylings */
+.ui.form .field.error select {
+ -webkit-appearance: menulist-button;
+}
+
+/*------------------
+ Input State
+ --------------------*/
+
+
+/* Transparent */
+.ui.form .field.error .transparent.input input,
+.ui.form .field.error .transparent.input textarea,
+.ui.form .field.error input.transparent,
+.ui.form .field.error textarea.transparent {
+ background-color: #FFF6F6 !important;
+ color: #9F3A38 !important;
+}
+
+/* Autofilled */
+.ui.form .error.error input:-webkit-autofill {
+ box-shadow: 0 0 0 100px #FFFAF0 inset !important;
+ border-color: #E0B4B4 !important;
+}
+
+/* Placeholder */
+.ui.form .error ::-webkit-input-placeholder {
+ color: #e7bdbc;
+}
+.ui.form .error :-ms-input-placeholder {
+ color: #e7bdbc !important;
+}
+.ui.form .error ::-moz-placeholder {
+ color: #e7bdbc;
+}
+.ui.form .error :focus::-webkit-input-placeholder {
+ color: #da9796;
+}
+.ui.form .error :focus:-ms-input-placeholder {
+ color: #da9796 !important;
+}
+.ui.form .error :focus::-moz-placeholder {
+ color: #da9796;
+}
+
+/*------------------
+ Dropdown State
+ --------------------*/
+
+.ui.form .fields.error .field .ui.dropdown,
+.ui.form .fields.error .field .ui.dropdown .item,
+.ui.form .field.error .ui.dropdown,
+.ui.form .field.error .ui.dropdown .text,
+.ui.form .field.error .ui.dropdown .item {
+ background: #FFF6F6;
+ color: #9F3A38;
+}
+.ui.form .fields.error .field .ui.dropdown,
+.ui.form .field.error .ui.dropdown {
+ border-color: #E0B4B4 !important;
+}
+.ui.form .fields.error .field .ui.dropdown:hover,
+.ui.form .field.error .ui.dropdown:hover {
+ border-color: #E0B4B4 !important;
+}
+.ui.form .fields.error .field .ui.dropdown:hover .menu,
+.ui.form .field.error .ui.dropdown:hover .menu {
+ border-color: #E0B4B4;
+}
+.ui.form .fields.error .field .ui.multiple.selection.dropdown > .label,
+.ui.form .field.error .ui.multiple.selection.dropdown > .label {
+ background-color: #EACBCB;
+ color: #9F3A38;
+}
+
+/* Hover */
+.ui.form .fields.error .field .ui.dropdown .menu .item:hover,
+.ui.form .field.error .ui.dropdown .menu .item:hover {
+ background-color: #FBE7E7;
+}
+
+/* Selected */
+.ui.form .fields.error .field .ui.dropdown .menu .selected.item,
+.ui.form .field.error .ui.dropdown .menu .selected.item {
+ background-color: #FBE7E7;
+}
+
+/* Active */
+.ui.form .fields.error .field .ui.dropdown .menu .active.item,
+.ui.form .field.error .ui.dropdown .menu .active.item {
+ background-color: #FDCFCF !important;
+}
+
+/*--------------------
+ Checkbox State
+ ---------------------*/
+
+.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) label,
+.ui.form .field.error .checkbox:not(.toggle):not(.slider) label,
+.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) .box,
+.ui.form .field.error .checkbox:not(.toggle):not(.slider) .box {
+ color: #9F3A38;
+}
+.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) label:before,
+.ui.form .field.error .checkbox:not(.toggle):not(.slider) label:before,
+.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) .box:before,
+.ui.form .field.error .checkbox:not(.toggle):not(.slider) .box:before {
+ background: #FFF6F6;
+ border-color: #E0B4B4;
+}
+.ui.form .fields.error .field .checkbox label:after,
+.ui.form .field.error .checkbox label:after,
+.ui.form .fields.error .field .checkbox .box:after,
+.ui.form .field.error .checkbox .box:after {
+ color: #9F3A38;
+}
+
+/* On Form */
+.ui.form.info .info.message:not(:empty) {
+ display: block;
+}
+.ui.form.info .compact.info.message:not(:empty) {
+ display: inline-block;
+}
+.ui.form.info .icon.info.message:not(:empty) {
+ display: flex;
+}
+
+/* On Field(s) */
+.ui.form .fields.info .info.message:not(:empty),
+.ui.form .field.info .info.message:not(:empty) {
+ display: block;
+}
+.ui.form .fields.info .compact.info.message:not(:empty),
+.ui.form .field.info .compact.info.message:not(:empty) {
+ display: inline-block;
+}
+.ui.form .fields.info .icon.info.message:not(:empty),
+.ui.form .field.info .icon.info.message:not(:empty) {
+ display: flex;
+}
+.ui.ui.form .fields.info .field label,
+.ui.ui.form .field.info label,
+.ui.ui.form .fields.info .field .input,
+.ui.ui.form .field.info .input {
+ color: #276F86;
+}
+.ui.form .fields.info .field .corner.label,
+.ui.form .field.info .corner.label {
+ border-color: #276F86;
+ color: #FFFFFF;
+}
+.ui.form .fields.info .field textarea,
+.ui.form .fields.info .field select,
+.ui.form .fields.info .field input:not([type]),
+.ui.form .fields.info .field input[type="date"],
+.ui.form .fields.info .field input[type="datetime-local"],
+.ui.form .fields.info .field input[type="email"],
+.ui.form .fields.info .field input[type="number"],
+.ui.form .fields.info .field input[type="password"],
+.ui.form .fields.info .field input[type="search"],
+.ui.form .fields.info .field input[type="tel"],
+.ui.form .fields.info .field input[type="time"],
+.ui.form .fields.info .field input[type="text"],
+.ui.form .fields.info .field input[type="file"],
+.ui.form .fields.info .field input[type="url"],
+.ui.form .field.info textarea,
+.ui.form .field.info select,
+.ui.form .field.info input:not([type]),
+.ui.form .field.info input[type="date"],
+.ui.form .field.info input[type="datetime-local"],
+.ui.form .field.info input[type="email"],
+.ui.form .field.info input[type="number"],
+.ui.form .field.info input[type="password"],
+.ui.form .field.info input[type="search"],
+.ui.form .field.info input[type="tel"],
+.ui.form .field.info input[type="time"],
+.ui.form .field.info input[type="text"],
+.ui.form .field.info input[type="file"],
+.ui.form .field.info input[type="url"] {
+ color: #276F86;
+ background: #F8FFFF;
+ border-color: #A9D5DE;
+ border-radius: '';
+ box-shadow: none;
+}
+.ui.form .field.info textarea:focus,
+.ui.form .field.info select:focus,
+.ui.form .field.info input:not([type]):focus,
+.ui.form .field.info input[type="date"]:focus,
+.ui.form .field.info input[type="datetime-local"]:focus,
+.ui.form .field.info input[type="email"]:focus,
+.ui.form .field.info input[type="number"]:focus,
+.ui.form .field.info input[type="password"]:focus,
+.ui.form .field.info input[type="search"]:focus,
+.ui.form .field.info input[type="tel"]:focus,
+.ui.form .field.info input[type="time"]:focus,
+.ui.form .field.info input[type="text"]:focus,
+.ui.form .field.info input[type="file"]:focus,
+.ui.form .field.info input[type="url"]:focus {
+ background: #F8FFFF;
+ border-color: #A9D5DE;
+ color: #276F86;
+ box-shadow: none;
+}
+
+/* Preserve Native Select Stylings */
+.ui.form .field.info select {
+ -webkit-appearance: menulist-button;
+}
+
+/*------------------
+ Input State
+ --------------------*/
+
+
+/* Transparent */
+.ui.form .field.info .transparent.input input,
+.ui.form .field.info .transparent.input textarea,
+.ui.form .field.info input.transparent,
+.ui.form .field.info textarea.transparent {
+ background-color: #F8FFFF !important;
+ color: #276F86 !important;
+}
+
+/* Autofilled */
+.ui.form .info.info input:-webkit-autofill {
+ box-shadow: 0 0 0 100px #F0FAFF inset !important;
+ border-color: #b3e0e0 !important;
+}
+
+/* Placeholder */
+.ui.form .info ::-webkit-input-placeholder {
+ color: #98cfe1;
+}
+.ui.form .info :-ms-input-placeholder {
+ color: #98cfe1 !important;
+}
+.ui.form .info ::-moz-placeholder {
+ color: #98cfe1;
+}
+.ui.form .info :focus::-webkit-input-placeholder {
+ color: #70bdd6;
+}
+.ui.form .info :focus:-ms-input-placeholder {
+ color: #70bdd6 !important;
+}
+.ui.form .info :focus::-moz-placeholder {
+ color: #70bdd6;
+}
+
+/*------------------
+ Dropdown State
+ --------------------*/
+
+.ui.form .fields.info .field .ui.dropdown,
+.ui.form .fields.info .field .ui.dropdown .item,
+.ui.form .field.info .ui.dropdown,
+.ui.form .field.info .ui.dropdown .text,
+.ui.form .field.info .ui.dropdown .item {
+ background: #F8FFFF;
+ color: #276F86;
+}
+.ui.form .fields.info .field .ui.dropdown,
+.ui.form .field.info .ui.dropdown {
+ border-color: #A9D5DE !important;
+}
+.ui.form .fields.info .field .ui.dropdown:hover,
+.ui.form .field.info .ui.dropdown:hover {
+ border-color: #A9D5DE !important;
+}
+.ui.form .fields.info .field .ui.dropdown:hover .menu,
+.ui.form .field.info .ui.dropdown:hover .menu {
+ border-color: #A9D5DE;
+}
+.ui.form .fields.info .field .ui.multiple.selection.dropdown > .label,
+.ui.form .field.info .ui.multiple.selection.dropdown > .label {
+ background-color: #cce3ea;
+ color: #276F86;
+}
+
+/* Hover */
+.ui.form .fields.info .field .ui.dropdown .menu .item:hover,
+.ui.form .field.info .ui.dropdown .menu .item:hover {
+ background-color: #e9f2fb;
+}
+
+/* Selected */
+.ui.form .fields.info .field .ui.dropdown .menu .selected.item,
+.ui.form .field.info .ui.dropdown .menu .selected.item {
+ background-color: #e9f2fb;
+}
+
+/* Active */
+.ui.form .fields.info .field .ui.dropdown .menu .active.item,
+.ui.form .field.info .ui.dropdown .menu .active.item {
+ background-color: #cef1fd !important;
+}
+
+/*--------------------
+ Checkbox State
+ ---------------------*/
+
+.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) label,
+.ui.form .field.info .checkbox:not(.toggle):not(.slider) label,
+.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) .box,
+.ui.form .field.info .checkbox:not(.toggle):not(.slider) .box {
+ color: #276F86;
+}
+.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) label:before,
+.ui.form .field.info .checkbox:not(.toggle):not(.slider) label:before,
+.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) .box:before,
+.ui.form .field.info .checkbox:not(.toggle):not(.slider) .box:before {
+ background: #F8FFFF;
+ border-color: #A9D5DE;
+}
+.ui.form .fields.info .field .checkbox label:after,
+.ui.form .field.info .checkbox label:after,
+.ui.form .fields.info .field .checkbox .box:after,
+.ui.form .field.info .checkbox .box:after {
+ color: #276F86;
+}
+
+/* On Form */
+.ui.form.success .success.message:not(:empty) {
+ display: block;
+}
+.ui.form.success .compact.success.message:not(:empty) {
+ display: inline-block;
+}
+.ui.form.success .icon.success.message:not(:empty) {
+ display: flex;
+}
+
+/* On Field(s) */
+.ui.form .fields.success .success.message:not(:empty),
+.ui.form .field.success .success.message:not(:empty) {
+ display: block;
+}
+.ui.form .fields.success .compact.success.message:not(:empty),
+.ui.form .field.success .compact.success.message:not(:empty) {
+ display: inline-block;
+}
+.ui.form .fields.success .icon.success.message:not(:empty),
+.ui.form .field.success .icon.success.message:not(:empty) {
+ display: flex;
+}
+.ui.ui.form .fields.success .field label,
+.ui.ui.form .field.success label,
+.ui.ui.form .fields.success .field .input,
+.ui.ui.form .field.success .input {
+ color: #2C662D;
+}
+.ui.form .fields.success .field .corner.label,
+.ui.form .field.success .corner.label {
+ border-color: #2C662D;
+ color: #FFFFFF;
+}
+.ui.form .fields.success .field textarea,
+.ui.form .fields.success .field select,
+.ui.form .fields.success .field input:not([type]),
+.ui.form .fields.success .field input[type="date"],
+.ui.form .fields.success .field input[type="datetime-local"],
+.ui.form .fields.success .field input[type="email"],
+.ui.form .fields.success .field input[type="number"],
+.ui.form .fields.success .field input[type="password"],
+.ui.form .fields.success .field input[type="search"],
+.ui.form .fields.success .field input[type="tel"],
+.ui.form .fields.success .field input[type="time"],
+.ui.form .fields.success .field input[type="text"],
+.ui.form .fields.success .field input[type="file"],
+.ui.form .fields.success .field input[type="url"],
+.ui.form .field.success textarea,
+.ui.form .field.success select,
+.ui.form .field.success input:not([type]),
+.ui.form .field.success input[type="date"],
+.ui.form .field.success input[type="datetime-local"],
+.ui.form .field.success input[type="email"],
+.ui.form .field.success input[type="number"],
+.ui.form .field.success input[type="password"],
+.ui.form .field.success input[type="search"],
+.ui.form .field.success input[type="tel"],
+.ui.form .field.success input[type="time"],
+.ui.form .field.success input[type="text"],
+.ui.form .field.success input[type="file"],
+.ui.form .field.success input[type="url"] {
+ color: #2C662D;
+ background: #FCFFF5;
+ border-color: #A3C293;
+ border-radius: '';
+ box-shadow: none;
+}
+.ui.form .field.success textarea:focus,
+.ui.form .field.success select:focus,
+.ui.form .field.success input:not([type]):focus,
+.ui.form .field.success input[type="date"]:focus,
+.ui.form .field.success input[type="datetime-local"]:focus,
+.ui.form .field.success input[type="email"]:focus,
+.ui.form .field.success input[type="number"]:focus,
+.ui.form .field.success input[type="password"]:focus,
+.ui.form .field.success input[type="search"]:focus,
+.ui.form .field.success input[type="tel"]:focus,
+.ui.form .field.success input[type="time"]:focus,
+.ui.form .field.success input[type="text"]:focus,
+.ui.form .field.success input[type="file"]:focus,
+.ui.form .field.success input[type="url"]:focus {
+ background: #FCFFF5;
+ border-color: #A3C293;
+ color: #2C662D;
+ box-shadow: none;
+}
+
+/* Preserve Native Select Stylings */
+.ui.form .field.success select {
+ -webkit-appearance: menulist-button;
+}
+
+/*------------------
+ Input State
+ --------------------*/
+
+
+/* Transparent */
+.ui.form .field.success .transparent.input input,
+.ui.form .field.success .transparent.input textarea,
+.ui.form .field.success input.transparent,
+.ui.form .field.success textarea.transparent {
+ background-color: #FCFFF5 !important;
+ color: #2C662D !important;
+}
+
+/* Autofilled */
+.ui.form .success.success input:-webkit-autofill {
+ box-shadow: 0 0 0 100px #F0FFF0 inset !important;
+ border-color: #bee0b3 !important;
+}
+
+/* Placeholder */
+.ui.form .success ::-webkit-input-placeholder {
+ color: #8fcf90;
+}
+.ui.form .success :-ms-input-placeholder {
+ color: #8fcf90 !important;
+}
+.ui.form .success ::-moz-placeholder {
+ color: #8fcf90;
+}
+.ui.form .success :focus::-webkit-input-placeholder {
+ color: #6cbf6d;
+}
+.ui.form .success :focus:-ms-input-placeholder {
+ color: #6cbf6d !important;
+}
+.ui.form .success :focus::-moz-placeholder {
+ color: #6cbf6d;
+}
+
+/*------------------
+ Dropdown State
+ --------------------*/
+
+.ui.form .fields.success .field .ui.dropdown,
+.ui.form .fields.success .field .ui.dropdown .item,
+.ui.form .field.success .ui.dropdown,
+.ui.form .field.success .ui.dropdown .text,
+.ui.form .field.success .ui.dropdown .item {
+ background: #FCFFF5;
+ color: #2C662D;
+}
+.ui.form .fields.success .field .ui.dropdown,
+.ui.form .field.success .ui.dropdown {
+ border-color: #A3C293 !important;
+}
+.ui.form .fields.success .field .ui.dropdown:hover,
+.ui.form .field.success .ui.dropdown:hover {
+ border-color: #A3C293 !important;
+}
+.ui.form .fields.success .field .ui.dropdown:hover .menu,
+.ui.form .field.success .ui.dropdown:hover .menu {
+ border-color: #A3C293;
+}
+.ui.form .fields.success .field .ui.multiple.selection.dropdown > .label,
+.ui.form .field.success .ui.multiple.selection.dropdown > .label {
+ background-color: #cceacc;
+ color: #2C662D;
+}
+
+/* Hover */
+.ui.form .fields.success .field .ui.dropdown .menu .item:hover,
+.ui.form .field.success .ui.dropdown .menu .item:hover {
+ background-color: #e9fbe9;
+}
+
+/* Selected */
+.ui.form .fields.success .field .ui.dropdown .menu .selected.item,
+.ui.form .field.success .ui.dropdown .menu .selected.item {
+ background-color: #e9fbe9;
+}
+
+/* Active */
+.ui.form .fields.success .field .ui.dropdown .menu .active.item,
+.ui.form .field.success .ui.dropdown .menu .active.item {
+ background-color: #dafdce !important;
+}
+
+/*--------------------
+ Checkbox State
+ ---------------------*/
+
+.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) label,
+.ui.form .field.success .checkbox:not(.toggle):not(.slider) label,
+.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) .box,
+.ui.form .field.success .checkbox:not(.toggle):not(.slider) .box {
+ color: #2C662D;
+}
+.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) label:before,
+.ui.form .field.success .checkbox:not(.toggle):not(.slider) label:before,
+.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) .box:before,
+.ui.form .field.success .checkbox:not(.toggle):not(.slider) .box:before {
+ background: #FCFFF5;
+ border-color: #A3C293;
+}
+.ui.form .fields.success .field .checkbox label:after,
+.ui.form .field.success .checkbox label:after,
+.ui.form .fields.success .field .checkbox .box:after,
+.ui.form .field.success .checkbox .box:after {
+ color: #2C662D;
+}
+
+/* On Form */
+.ui.form.warning .warning.message:not(:empty) {
+ display: block;
+}
+.ui.form.warning .compact.warning.message:not(:empty) {
+ display: inline-block;
+}
+.ui.form.warning .icon.warning.message:not(:empty) {
+ display: flex;
+}
+
+/* On Field(s) */
+.ui.form .fields.warning .warning.message:not(:empty),
+.ui.form .field.warning .warning.message:not(:empty) {
+ display: block;
+}
+.ui.form .fields.warning .compact.warning.message:not(:empty),
+.ui.form .field.warning .compact.warning.message:not(:empty) {
+ display: inline-block;
+}
+.ui.form .fields.warning .icon.warning.message:not(:empty),
+.ui.form .field.warning .icon.warning.message:not(:empty) {
+ display: flex;
+}
+.ui.ui.form .fields.warning .field label,
+.ui.ui.form .field.warning label,
+.ui.ui.form .fields.warning .field .input,
+.ui.ui.form .field.warning .input {
+ color: #573A08;
+}
+.ui.form .fields.warning .field .corner.label,
+.ui.form .field.warning .corner.label {
+ border-color: #573A08;
+ color: #FFFFFF;
+}
+.ui.form .fields.warning .field textarea,
+.ui.form .fields.warning .field select,
+.ui.form .fields.warning .field input:not([type]),
+.ui.form .fields.warning .field input[type="date"],
+.ui.form .fields.warning .field input[type="datetime-local"],
+.ui.form .fields.warning .field input[type="email"],
+.ui.form .fields.warning .field input[type="number"],
+.ui.form .fields.warning .field input[type="password"],
+.ui.form .fields.warning .field input[type="search"],
+.ui.form .fields.warning .field input[type="tel"],
+.ui.form .fields.warning .field input[type="time"],
+.ui.form .fields.warning .field input[type="text"],
+.ui.form .fields.warning .field input[type="file"],
+.ui.form .fields.warning .field input[type="url"],
+.ui.form .field.warning textarea,
+.ui.form .field.warning select,
+.ui.form .field.warning input:not([type]),
+.ui.form .field.warning input[type="date"],
+.ui.form .field.warning input[type="datetime-local"],
+.ui.form .field.warning input[type="email"],
+.ui.form .field.warning input[type="number"],
+.ui.form .field.warning input[type="password"],
+.ui.form .field.warning input[type="search"],
+.ui.form .field.warning input[type="tel"],
+.ui.form .field.warning input[type="time"],
+.ui.form .field.warning input[type="text"],
+.ui.form .field.warning input[type="file"],
+.ui.form .field.warning input[type="url"] {
+ color: #573A08;
+ background: #FFFAF3;
+ border-color: #C9BA9B;
+ border-radius: '';
+ box-shadow: none;
+}
+.ui.form .field.warning textarea:focus,
+.ui.form .field.warning select:focus,
+.ui.form .field.warning input:not([type]):focus,
+.ui.form .field.warning input[type="date"]:focus,
+.ui.form .field.warning input[type="datetime-local"]:focus,
+.ui.form .field.warning input[type="email"]:focus,
+.ui.form .field.warning input[type="number"]:focus,
+.ui.form .field.warning input[type="password"]:focus,
+.ui.form .field.warning input[type="search"]:focus,
+.ui.form .field.warning input[type="tel"]:focus,
+.ui.form .field.warning input[type="time"]:focus,
+.ui.form .field.warning input[type="text"]:focus,
+.ui.form .field.warning input[type="file"]:focus,
+.ui.form .field.warning input[type="url"]:focus {
+ background: #FFFAF3;
+ border-color: #C9BA9B;
+ color: #573A08;
+ box-shadow: none;
+}
+
+/* Preserve Native Select Stylings */
+.ui.form .field.warning select {
+ -webkit-appearance: menulist-button;
+}
+
+/*------------------
+ Input State
+ --------------------*/
+
+
+/* Transparent */
+.ui.form .field.warning .transparent.input input,
+.ui.form .field.warning .transparent.input textarea,
+.ui.form .field.warning input.transparent,
+.ui.form .field.warning textarea.transparent {
+ background-color: #FFFAF3 !important;
+ color: #573A08 !important;
+}
+
+/* Autofilled */
+.ui.form .warning.warning input:-webkit-autofill {
+ box-shadow: 0 0 0 100px #FFFFe0 inset !important;
+ border-color: #e0e0b3 !important;
+}
+
+/* Placeholder */
+.ui.form .warning ::-webkit-input-placeholder {
+ color: #edad3e;
+}
+.ui.form .warning :-ms-input-placeholder {
+ color: #edad3e !important;
+}
+.ui.form .warning ::-moz-placeholder {
+ color: #edad3e;
+}
+.ui.form .warning :focus::-webkit-input-placeholder {
+ color: #e39715;
+}
+.ui.form .warning :focus:-ms-input-placeholder {
+ color: #e39715 !important;
+}
+.ui.form .warning :focus::-moz-placeholder {
+ color: #e39715;
+}
+
+/*------------------
+ Dropdown State
+ --------------------*/
+
+.ui.form .fields.warning .field .ui.dropdown,
+.ui.form .fields.warning .field .ui.dropdown .item,
+.ui.form .field.warning .ui.dropdown,
+.ui.form .field.warning .ui.dropdown .text,
+.ui.form .field.warning .ui.dropdown .item {
+ background: #FFFAF3;
+ color: #573A08;
+}
+.ui.form .fields.warning .field .ui.dropdown,
+.ui.form .field.warning .ui.dropdown {
+ border-color: #C9BA9B !important;
+}
+.ui.form .fields.warning .field .ui.dropdown:hover,
+.ui.form .field.warning .ui.dropdown:hover {
+ border-color: #C9BA9B !important;
+}
+.ui.form .fields.warning .field .ui.dropdown:hover .menu,
+.ui.form .field.warning .ui.dropdown:hover .menu {
+ border-color: #C9BA9B;
+}
+.ui.form .fields.warning .field .ui.multiple.selection.dropdown > .label,
+.ui.form .field.warning .ui.multiple.selection.dropdown > .label {
+ background-color: #eaeacc;
+ color: #573A08;
+}
+
+/* Hover */
+.ui.form .fields.warning .field .ui.dropdown .menu .item:hover,
+.ui.form .field.warning .ui.dropdown .menu .item:hover {
+ background-color: #fbfbe9;
+}
+
+/* Selected */
+.ui.form .fields.warning .field .ui.dropdown .menu .selected.item,
+.ui.form .field.warning .ui.dropdown .menu .selected.item {
+ background-color: #fbfbe9;
+}
+
+/* Active */
+.ui.form .fields.warning .field .ui.dropdown .menu .active.item,
+.ui.form .field.warning .ui.dropdown .menu .active.item {
+ background-color: #fdfdce !important;
+}
+
+/*--------------------
+ Checkbox State
+ ---------------------*/
+
+.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) label,
+.ui.form .field.warning .checkbox:not(.toggle):not(.slider) label,
+.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) .box,
+.ui.form .field.warning .checkbox:not(.toggle):not(.slider) .box {
+ color: #573A08;
+}
+.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) label:before,
+.ui.form .field.warning .checkbox:not(.toggle):not(.slider) label:before,
+.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) .box:before,
+.ui.form .field.warning .checkbox:not(.toggle):not(.slider) .box:before {
+ background: #FFFAF3;
+ border-color: #C9BA9B;
+}
+.ui.form .fields.warning .field .checkbox label:after,
+.ui.form .field.warning .checkbox label:after,
+.ui.form .fields.warning .field .checkbox .box:after,
+.ui.form .field.warning .checkbox .box:after {
+ color: #573A08;
+}
+
+/*--------------------
+ Disabled
+ ---------------------*/
+
+.ui.form .disabled.fields .field,
+.ui.form .disabled.field,
+.ui.form .field :disabled {
+ pointer-events: none;
+ opacity: var(--opacity-disabled);
+}
+.ui.form .field.disabled > label,
+.ui.form .fields.disabled > label {
+ opacity: var(--opacity-disabled);
+}
+.ui.form .field.disabled :disabled {
+ opacity: 1;
+}
+
+/*--------------
+ Loading
+ ---------------*/
+
+.ui.loading.form {
+ position: relative;
+ cursor: default;
+ pointer-events: none;
+}
+.ui.loading.form:before {
+ position: absolute;
+ content: '';
+ top: 0;
+ left: 0;
+ background: rgba(255, 255, 255, 0.8);
+ width: 100%;
+ height: 100%;
+ z-index: 100;
+}
+.ui.loading.form.segments:before {
+ border-radius: 0.28571429rem;
+}
+.ui.loading.form:after {
+ position: absolute;
+ content: '';
+ top: 50%;
+ left: 50%;
+ margin: -1.5em 0 0 -1.5em;
+ width: 3em;
+ height: 3em;
+ animation: loader 0.6s infinite linear;
+ border: 0.2em solid #767676;
+ border-radius: 500rem;
+ box-shadow: 0 0 0 1px transparent;
+ visibility: visible;
+ z-index: 101;
+}
+
+
+/*******************************
+ Element Types
+*******************************/
+
+
+/*--------------------
+ Required Field
+ ---------------------*/
+
+.ui.form .required.fields:not(.grouped) > .field > label:after,
+.ui.form .required.fields.grouped > label:after,
+.ui.form .required.field > label:after,
+.ui.form .required.fields:not(.grouped) > .field > .checkbox:after,
+.ui.form .required.field > .checkbox:after,
+.ui.form label.required:after {
+ margin: -0.2em 0 0 0.2em;
+ content: '*';
+ color: #DB2828;
+}
+.ui.form .required.fields:not(.grouped) > .field > label:after,
+.ui.form .required.fields.grouped > label:after,
+.ui.form .required.field > label:after,
+.ui.form label.required:after {
+ display: inline-block;
+ vertical-align: top;
+}
+.ui.form .required.fields:not(.grouped) > .field > .checkbox:after,
+.ui.form .required.field > .checkbox:after {
+ position: absolute;
+ top: 0;
+ left: 100%;
+}
+
+
+/*******************************
+ Variations
+*******************************/
+
+
+/*--------------------
+ Field Groups
+ ---------------------*/
+
+
+/* Grouped Vertically */
+.ui.form .grouped.fields {
+ display: block;
+ margin: 0 0 1em;
+}
+.ui.form .grouped.fields:last-child {
+ margin-bottom: 0;
+}
+.ui.form .grouped.fields > label {
+ margin: 0 0 0.28571429rem 0;
+ color: rgba(0, 0, 0, 0.87);
+ font-size: 0.92857143em;
+ font-weight: 500;
+ text-transform: none;
+}
+.ui.form .grouped.fields .field,
+.ui.form .grouped.inline.fields .field {
+ display: block;
+ margin: 0.5em 0;
+ padding: 0;
+}
+.ui.form .grouped.inline.fields .ui.checkbox {
+ margin-bottom: 0.4em;
+}
+
+/*--------------------
+ Fields
+---------------------*/
+
+
+/* Split fields */
+.ui.form .fields {
+ display: flex;
+ flex-direction: row;
+ margin: 0 -0.5em 1em;
+}
+.ui.form .fields > .field {
+ flex: 0 1 auto;
+ padding-left: 0.5em;
+ padding-right: 0.5em;
+}
+.ui.form .fields > .field:first-child {
+ border-left: none;
+ box-shadow: none;
+}
+
+/* Other Combinations */
+.ui.form .two.fields > .fields,
+.ui.form .two.fields > .field {
+ width: 50%;
+}
+.ui.form .three.fields > .fields,
+.ui.form .three.fields > .field {
+ width: 33.33333333%;
+}
+.ui.form .four.fields > .fields,
+.ui.form .four.fields > .field {
+ width: 25%;
+}
+.ui.form .five.fields > .fields,
+.ui.form .five.fields > .field {
+ width: 20%;
+}
+.ui.form .six.fields > .fields,
+.ui.form .six.fields > .field {
+ width: 16.66666667%;
+}
+.ui.form .seven.fields > .fields,
+.ui.form .seven.fields > .field {
+ width: 14.28571429%;
+}
+.ui.form .eight.fields > .fields,
+.ui.form .eight.fields > .field {
+ width: 12.5%;
+}
+.ui.form .nine.fields > .fields,
+.ui.form .nine.fields > .field {
+ width: 11.11111111%;
+}
+.ui.form .ten.fields > .fields,
+.ui.form .ten.fields > .field {
+ width: 10%;
+}
+
+/* Swap to full width on mobile */
+@media only screen and (max-width: 767.98px) {
+ .ui.form .fields {
+ flex-wrap: wrap;
+ margin-bottom: 0;
+ }
+ .ui.form:not(.unstackable) .fields:not(.unstackable) > .fields,
+ .ui.form:not(.unstackable) .fields:not(.unstackable) > .field {
+ width: 100%;
+ margin: 0 0 1em;
+ }
+}
+
+/* Sizing Combinations */
+.ui.form .fields .wide.field {
+ width: 6.25%;
+ padding-left: 0.5em;
+ padding-right: 0.5em;
+}
+.ui.form .one.wide.field {
+ width: 6.25%;
+}
+.ui.form .two.wide.field {
+ width: 12.5%;
+}
+.ui.form .three.wide.field {
+ width: 18.75%;
+}
+.ui.form .four.wide.field {
+ width: 25%;
+}
+.ui.form .five.wide.field {
+ width: 31.25%;
+}
+.ui.form .six.wide.field {
+ width: 37.5%;
+}
+.ui.form .seven.wide.field {
+ width: 43.75%;
+}
+.ui.form .eight.wide.field {
+ width: 50%;
+}
+.ui.form .nine.wide.field {
+ width: 56.25%;
+}
+.ui.form .ten.wide.field {
+ width: 62.5%;
+}
+.ui.form .eleven.wide.field {
+ width: 68.75%;
+}
+.ui.form .twelve.wide.field {
+ width: 75%;
+}
+.ui.form .thirteen.wide.field {
+ width: 81.25%;
+}
+.ui.form .fourteen.wide.field {
+ width: 87.5%;
+}
+.ui.form .fifteen.wide.field {
+ width: 93.75%;
+}
+.ui.form .sixteen.wide.field {
+ width: 100%;
+}
+
+/*--------------------
+ Inline Fields
+ ---------------------*/
+
+.ui.form .inline.fields {
+ margin: 0 0 1em;
+ align-items: center;
+}
+.ui.form .inline.fields .field {
+ margin: 0;
+ padding: 0 1em 0 0;
+}
+
+/* Inline Label */
+.ui.form .inline.fields > label,
+.ui.form .inline.fields .field > label,
+.ui.form .inline.fields .field > p,
+.ui.form .inline.field > label,
+.ui.form .inline.field > p {
+ display: inline-block;
+ width: auto;
+ margin-top: 0;
+ margin-bottom: 0;
+ vertical-align: baseline;
+ font-size: 0.92857143em;
+ font-weight: 500;
+ color: rgba(0, 0, 0, 0.87);
+ text-transform: none;
+}
+
+/* Grouped Inline Label */
+.ui.form .inline.fields > label {
+ margin: 0.035714em 1em 0 0;
+}
+
+/* Inline Input */
+.ui.form .inline.fields .field > input,
+.ui.form .inline.fields .field > select,
+.ui.form .inline.field > input,
+.ui.form .inline.field > select {
+ display: inline-block;
+ width: auto;
+ margin-top: 0;
+ margin-bottom: 0;
+ vertical-align: middle;
+ font-size: 1em;
+}
+.ui.form .inline.fields .field .calendar:not(.popup),
+.ui.form .inline.field .calendar:not(.popup) {
+ display: inline-block;
+}
+.ui.form .inline.fields .field .calendar:not(.popup) > .input > input,
+.ui.form .inline.field .calendar:not(.popup) > .input > input {
+ width: 13.11em;
+}
+
+/* Label */
+.ui.form .inline.fields .field > :first-child,
+.ui.form .inline.field > :first-child {
+ margin: 0 0.85714286em 0 0;
+}
+.ui.form .inline.fields .field > :only-child,
+.ui.form .inline.field > :only-child {
+ margin: 0;
+}
+
+/* Wide */
+.ui.form .inline.fields .wide.field {
+ display: flex;
+ align-items: center;
+}
+.ui.form .inline.fields .wide.field > input,
+.ui.form .inline.fields .wide.field > select {
+ width: 100%;
+}
+
+/*--------------------
+ Sizes
+---------------------*/
+
+.ui.form,
+.ui.form .field .dropdown,
+.ui.form .field .dropdown .menu > .item {
+ font-size: 1rem;
+}
+.ui.mini.form,
+.ui.mini.form .field .dropdown,
+.ui.mini.form .field .dropdown .menu > .item {
+ font-size: 0.78571429rem;
+}
+.ui.tiny.form,
+.ui.tiny.form .field .dropdown,
+.ui.tiny.form .field .dropdown .menu > .item {
+ font-size: 0.85714286rem;
+}
+.ui.small.form,
+.ui.small.form .field .dropdown,
+.ui.small.form .field .dropdown .menu > .item {
+ font-size: 0.92857143rem;
+}
+.ui.large.form,
+.ui.large.form .field .dropdown,
+.ui.large.form .field .dropdown .menu > .item {
+ font-size: 1.14285714rem;
+}
+.ui.big.form,
+.ui.big.form .field .dropdown,
+.ui.big.form .field .dropdown .menu > .item {
+ font-size: 1.28571429rem;
+}
+.ui.huge.form,
+.ui.huge.form .field .dropdown,
+.ui.huge.form .field .dropdown .menu > .item {
+ font-size: 1.42857143rem;
+}
+.ui.massive.form,
+.ui.massive.form .field .dropdown,
+.ui.massive.form .field .dropdown .menu > .item {
+ font-size: 1.71428571rem;
+}
+
+
+/*******************************
+ Theme Overrides
+*******************************/
+
+
+
+/*******************************
+ Site Overrides
+*******************************/
+
diff --git a/web_src/fomantic/build/components/modal.css b/web_src/fomantic/build/components/modal.css
new file mode 100644
index 0000000000..7da015cfd0
--- /dev/null
+++ b/web_src/fomantic/build/components/modal.css
@@ -0,0 +1,698 @@
+/*!
+ * # Fomantic-UI - Modal
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+ Modal
+*******************************/
+
+.ui.modal {
+ position: absolute;
+ display: none;
+ z-index: 1001;
+ text-align: left;
+ background: #FFFFFF;
+ border: none;
+ box-shadow: 1px 3px 3px 0 rgba(0, 0, 0, 0.2), 1px 3px 15px 2px rgba(0, 0, 0, 0.2);
+ transform-origin: 50% 25%;
+ flex: 0 0 auto;
+ border-radius: 0.28571429rem;
+ -webkit-user-select: text;
+ -moz-user-select: text;
+ user-select: text;
+ will-change: top, left, margin, transform, opacity;
+}
+.ui.modal > :first-child:not(.icon):not(.dimmer),
+.ui.modal > i.icon:first-child + *,
+.ui.modal > .dimmer:first-child + *:not(.icon),
+.ui.modal > .dimmer:first-child + i.icon + * {
+ border-top-left-radius: 0.28571429rem;
+ border-top-right-radius: 0.28571429rem;
+}
+.ui.modal > :last-child {
+ border-bottom-left-radius: 0.28571429rem;
+ border-bottom-right-radius: 0.28571429rem;
+}
+.ui.modal > .ui.dimmer {
+ border-radius: inherit;
+}
+
+
+/*******************************
+ Content
+*******************************/
+
+
+/*--------------
+ Close
+---------------*/
+
+.ui.modal > .close {
+ cursor: pointer;
+ position: absolute;
+ top: -2.5rem;
+ right: -2.5rem;
+ z-index: 1;
+ opacity: 0.8;
+ font-size: 1.25em;
+ color: #FFFFFF;
+ width: 2.25rem;
+ height: 2.25rem;
+ padding: 0.625rem 0 0 0;
+}
+.ui.modal > .close:hover {
+ opacity: 1;
+}
+
+/*--------------
+ Header
+---------------*/
+
+.ui.modal > .header {
+ display: block;
+ font-family: var(--fonts-regular);
+ background: #FFFFFF;
+ margin: 0;
+ padding: 1.25rem 1.5rem;
+ box-shadow: none;
+ color: rgba(0, 0, 0, 0.85);
+ border-bottom: 1px solid rgba(34, 36, 38, 0.15);
+}
+.ui.modal > .header:not(.ui) {
+ font-size: 1.42857143rem;
+ line-height: 1.28571429em;
+ font-weight: 500;
+}
+
+/*--------------
+ Content
+---------------*/
+
+.ui.modal > .content {
+ display: block;
+ width: 100%;
+ font-size: 1em;
+ line-height: 1.4;
+ padding: 1.5rem;
+ background: #FFFFFF;
+}
+.ui.modal > .image.content {
+ display: flex;
+ flex-direction: row;
+}
+
+/* Image */
+.ui.modal > .content > .image {
+ display: block;
+ flex: 0 1 auto;
+ width: '';
+ align-self: start;
+ max-width: 100%;
+}
+
+.ui.modal > [class*="stretched"] {
+ align-self: stretch;
+}
+
+/* Description */
+.ui.modal > .content > .description {
+ display: block;
+ flex: 1 0 auto;
+ min-width: 0;
+ align-self: start;
+}
+.ui.modal > .content > i.icon + .description,
+.ui.modal > .content > .image + .description {
+ flex: 0 1 auto;
+ min-width: '';
+ width: auto;
+ padding-left: 2em;
+}
+/*rtl:ignore*/
+.ui.modal > .content > .image > i.icon {
+ margin: 0;
+ opacity: 1;
+ width: auto;
+ line-height: 1;
+ font-size: 8rem;
+}
+
+/*--------------
+ Actions
+---------------*/
+
+.ui.modal > .actions {
+ background: #F9FAFB;
+ padding: 1rem 1rem;
+ border-top: 1px solid rgba(34, 36, 38, 0.15);
+ text-align: right;
+}
+.ui.modal .actions > .button:not(.fluid) {
+ margin-left: 0.75em;
+}
+.ui.basic.modal > .actions {
+ border-top: none;
+}
+
+/*-------------------
+ Responsive
+--------------------*/
+
+
+/* Modal Width */
+@media only screen and (max-width: 767.98px) {
+ .ui.modal:not(.fullscreen) {
+ width: 95%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 768px) {
+ .ui.modal:not(.fullscreen) {
+ width: 88%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 992px) {
+ .ui.modal:not(.fullscreen) {
+ width: 850px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1200px) {
+ .ui.modal:not(.fullscreen) {
+ width: 900px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1920px) {
+ .ui.modal:not(.fullscreen) {
+ width: 950px;
+ margin: 0 0 0 0;
+ }
+}
+
+/* Tablet and Mobile */
+@media only screen and (max-width: 991.98px) {
+ .ui.modal > .header {
+ padding-right: 2.25rem;
+ }
+ .ui.modal > .close {
+ top: 1.0535rem;
+ right: 1rem;
+ color: rgba(0, 0, 0, 0.87);
+ }
+}
+
+/* Mobile */
+@media only screen and (max-width: 767.98px) {
+ .ui.modal > .header {
+ padding: 0.75rem 1rem !important;
+ padding-right: 2.25rem !important;
+ }
+ .ui.overlay.fullscreen.modal > .content.content.content {
+ min-height: calc(100vh - 8.1rem);
+ }
+ .ui.overlay.fullscreen.modal > .scrolling.content.content.content {
+ max-height: calc(100vh - 8.1rem);
+ }
+ .ui.modal > .content {
+ display: block;
+ padding: 1rem !important;
+ }
+ .ui.modal > .close {
+ top: 0.5rem !important;
+ right: 0.5rem !important;
+ }
+ /*rtl:ignore*/
+ .ui.modal .image.content {
+ flex-direction: column;
+ }
+ .ui.modal > .content > .image {
+ display: block;
+ max-width: 100%;
+ margin: 0 auto !important;
+ text-align: center;
+ padding: 0 0 1rem !important;
+ }
+ .ui.modal > .content > .image > i.icon {
+ font-size: 5rem;
+ text-align: center;
+ }
+ /*rtl:ignore*/
+ .ui.modal > .content > .description {
+ display: block;
+ width: 100% !important;
+ margin: 0 !important;
+ padding: 1rem 0 !important;
+ box-shadow: none;
+ }
+
+/* Let Buttons Stack */
+ .ui.modal > .actions {
+ padding: 1rem 1rem 0rem !important;
+ }
+ .ui.modal .actions > .buttons,
+ .ui.modal .actions > .button {
+ margin-bottom: 1rem;
+ }
+}
+
+/*--------------
+ Coupling
+---------------*/
+
+.ui.inverted.dimmer > .ui.modal {
+ box-shadow: 1px 3px 10px 2px rgba(0, 0, 0, 0.2);
+}
+
+
+/*******************************
+ Types
+*******************************/
+
+.ui.basic.modal {
+ background-color: transparent;
+ border: none;
+ border-radius: 0;
+ box-shadow: none !important;
+ color: #FFFFFF;
+}
+.ui.basic.modal > .header,
+.ui.basic.modal > .content,
+.ui.basic.modal > .actions {
+ background-color: transparent;
+}
+.ui.basic.modal > .header {
+ color: #FFFFFF;
+ border-bottom: none;
+}
+.ui.basic.modal > .close {
+ top: 1rem;
+ right: 1.5rem;
+ color: #FFFFFF;
+}
+.ui.inverted.dimmer > .basic.modal {
+ color: rgba(0, 0, 0, 0.87);
+}
+.ui.inverted.dimmer > .ui.basic.modal > .header {
+ color: rgba(0, 0, 0, 0.85);
+}
+
+/* Resort to margin positioning if legacy */
+.ui.legacy.legacy.modal,
+.ui.legacy.legacy.page.dimmer > .ui.modal {
+ left: 50% !important;
+}
+.ui.legacy.legacy.modal:not(.aligned),
+.ui.legacy.legacy.page.dimmer > .ui.modal:not(.aligned) {
+ top: 50%;
+}
+.ui.legacy.legacy.page.dimmer > .ui.scrolling.modal:not(.aligned),
+.ui.page.dimmer > .ui.scrolling.legacy.legacy.modal:not(.aligned),
+.ui.top.aligned.legacy.legacy.page.dimmer > .ui.modal:not(.aligned),
+.ui.top.aligned.dimmer > .ui.legacy.legacy.modal:not(.aligned) {
+ top: auto;
+}
+.ui.legacy.overlay.fullscreen.modal {
+ margin-top: -2rem !important;
+}
+
+
+/*******************************
+ States
+*******************************/
+
+.ui.loading.modal {
+ display: block;
+ visibility: hidden;
+ z-index: -1;
+}
+.ui.active.modal {
+ display: block;
+}
+
+
+/*******************************
+ Variations
+*******************************/
+
+
+/*--------------
+ Aligned
+ ---------------*/
+
+.modals.dimmer .ui.top.aligned.modal {
+ top: 5vh;
+}
+.modals.dimmer .ui.bottom.aligned.modal {
+ bottom: 5vh;
+}
+@media only screen and (max-width: 767.98px) {
+ .modals.dimmer .ui.top.aligned.modal {
+ top: 1rem;
+ }
+ .modals.dimmer .ui.bottom.aligned.modal {
+ bottom: 1rem;
+ }
+}
+
+/*--------------
+ Scrolling
+ ---------------*/
+
+
+/* Scrolling Dimmer */
+.scrolling.dimmable.dimmed {
+ overflow: hidden;
+}
+.scrolling.dimmable > .dimmer {
+ justify-content: flex-start;
+ position: fixed;
+}
+.scrolling.dimmable.dimmed > .dimmer {
+ overflow: auto;
+ -webkit-overflow-scrolling: touch;
+}
+.modals.dimmer .ui.scrolling.modal:not(.fullscreen) {
+ margin: 2rem auto;
+}
+
+/* Fix for Firefox, Edge, IE11 */
+.modals.dimmer .ui.scrolling.modal:not([class*="overlay fullscreen"])::after {
+ content: '\00A0';
+ position: absolute;
+ height: 2rem;
+}
+
+/* Undetached Scrolling */
+.scrolling.undetached.dimmable.dimmed {
+ overflow: auto;
+ -webkit-overflow-scrolling: touch;
+}
+.scrolling.undetached.dimmable.dimmed > .dimmer {
+ overflow: hidden;
+}
+.scrolling.undetached.dimmable .ui.scrolling.modal:not(.fullscreen) {
+ position: absolute;
+ left: 50%;
+}
+
+/* Scrolling Content */
+.ui.modal > .scrolling.content {
+ max-height: calc(80vh - 10rem);
+ overflow: auto;
+}
+.ui.overlay.fullscreen.modal > .content {
+ min-height: calc(100vh - 9.1rem);
+}
+.ui.overlay.fullscreen.modal > .scrolling.content {
+ max-height: calc(100vh - 9.1rem);
+}
+
+/*--------------
+ Full Screen
+ ---------------*/
+
+.ui.fullscreen.modal {
+ width: 95%;
+ left: 2.5%;
+ margin: 1em auto;
+}
+.ui.overlay.fullscreen.modal {
+ width: 100%;
+ left: 0;
+ margin: 0 auto;
+ top: 0;
+ border-radius: 0;
+}
+.ui.modal > .close.inside + .header,
+.ui.fullscreen.modal > .header {
+ padding-right: 2.25rem;
+}
+.ui.modal > .close.inside,
+.ui.fullscreen.modal > .close {
+ top: 1.0535rem;
+ right: 1rem;
+ color: rgba(0, 0, 0, 0.87);
+}
+.ui.basic.fullscreen.modal > .close {
+ color: #FFFFFF;
+}
+
+/*--------------
+ Size
+---------------*/
+
+.ui.modal {
+ font-size: 1rem;
+}
+.ui.mini.modal > .header:not(.ui) {
+ font-size: 1.3em;
+}
+@media only screen and (max-width: 767.98px) {
+ .ui.mini.modal {
+ width: 95%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 768px) {
+ .ui.mini.modal {
+ width: 35.2%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 992px) {
+ .ui.mini.modal {
+ width: 340px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1200px) {
+ .ui.mini.modal {
+ width: 360px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1920px) {
+ .ui.mini.modal {
+ width: 380px;
+ margin: 0 0 0 0;
+ }
+}
+.ui.tiny.modal > .header:not(.ui) {
+ font-size: 1.3em;
+}
+@media only screen and (max-width: 767.98px) {
+ .ui.tiny.modal {
+ width: 95%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 768px) {
+ .ui.tiny.modal {
+ width: 52.8%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 992px) {
+ .ui.tiny.modal {
+ width: 510px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1200px) {
+ .ui.tiny.modal {
+ width: 540px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1920px) {
+ .ui.tiny.modal {
+ width: 570px;
+ margin: 0 0 0 0;
+ }
+}
+.ui.small.modal > .header:not(.ui) {
+ font-size: 1.3em;
+}
+@media only screen and (max-width: 767.98px) {
+ .ui.small.modal {
+ width: 95%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 768px) {
+ .ui.small.modal {
+ width: 70.4%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 992px) {
+ .ui.small.modal {
+ width: 680px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1200px) {
+ .ui.small.modal {
+ width: 720px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1920px) {
+ .ui.small.modal {
+ width: 760px;
+ margin: 0 0 0 0;
+ }
+}
+.ui.large.modal > .header:not(.ui) {
+ font-size: 1.6em;
+}
+@media only screen and (max-width: 767.98px) {
+ .ui.large.modal {
+ width: 95%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 768px) {
+ .ui.large.modal {
+ width: 88%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 992px) {
+ .ui.large.modal {
+ width: 1020px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1200px) {
+ .ui.large.modal {
+ width: 1080px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1920px) {
+ .ui.large.modal {
+ width: 1140px;
+ margin: 0 0 0 0;
+ }
+}
+.ui.big.modal > .header:not(.ui) {
+ font-size: 1.6em;
+}
+@media only screen and (max-width: 767.98px) {
+ .ui.big.modal {
+ width: 95%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 768px) {
+ .ui.big.modal {
+ width: 88%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 992px) {
+ .ui.big.modal {
+ width: 1190px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1200px) {
+ .ui.big.modal {
+ width: 1260px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1920px) {
+ .ui.big.modal {
+ width: 1330px;
+ margin: 0 0 0 0;
+ }
+}
+.ui.huge.modal > .header:not(.ui) {
+ font-size: 1.6em;
+}
+@media only screen and (max-width: 767.98px) {
+ .ui.huge.modal {
+ width: 95%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 768px) {
+ .ui.huge.modal {
+ width: 88%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 992px) {
+ .ui.huge.modal {
+ width: 1360px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1200px) {
+ .ui.huge.modal {
+ width: 1440px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1920px) {
+ .ui.huge.modal {
+ width: 1520px;
+ margin: 0 0 0 0;
+ }
+}
+.ui.massive.modal > .header:not(.ui) {
+ font-size: 1.8em;
+}
+@media only screen and (max-width: 767.98px) {
+ .ui.massive.modal {
+ width: 95%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 768px) {
+ .ui.massive.modal {
+ width: 88%;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 992px) {
+ .ui.massive.modal {
+ width: 1530px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1200px) {
+ .ui.massive.modal {
+ width: 1620px;
+ margin: 0 0 0 0;
+ }
+}
+@media only screen and (min-width: 1920px) {
+ .ui.massive.modal {
+ width: 1710px;
+ margin: 0 0 0 0;
+ }
+}
+
+
+/*******************************
+ Theme Overrides
+*******************************/
+
+
+
+/*******************************
+ Site Overrides
+*******************************/
+
diff --git a/web_src/fomantic/build/components/modal.js b/web_src/fomantic/build/components/modal.js
new file mode 100644
index 0000000000..3f578ccfcc
--- /dev/null
+++ b/web_src/fomantic/build/components/modal.js
@@ -0,0 +1,1209 @@
+/*!
+ * # Fomantic-UI - Modal
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+;(function ($, window, document, undefined) {
+
+'use strict';
+
+$.isFunction = $.isFunction || function(obj) {
+ return typeof obj === "function" && typeof obj.nodeType !== "number";
+};
+
+window = (typeof window != 'undefined' && window.Math == Math)
+ ? window
+ : (typeof self != 'undefined' && self.Math == Math)
+ ? self
+ : Function('return this')()
+;
+
+$.fn.modal = function(parameters) {
+ var
+ $allModules = $(this),
+ $window = $(window),
+ $document = $(document),
+ $body = $('body'),
+
+ moduleSelector = $allModules.selector || '',
+
+ time = new Date().getTime(),
+ performance = [],
+
+ query = arguments[0],
+ methodInvoked = (typeof query == 'string'),
+ queryArguments = [].slice.call(arguments, 1),
+
+ requestAnimationFrame = window.requestAnimationFrame
+ || window.mozRequestAnimationFrame
+ || window.webkitRequestAnimationFrame
+ || window.msRequestAnimationFrame
+ || function(callback) { setTimeout(callback, 0); },
+
+ returnedValue
+ ;
+
+ $allModules
+ .each(function() {
+ var
+ settings = ( $.isPlainObject(parameters) )
+ ? $.extend(true, {}, $.fn.modal.settings, parameters)
+ : $.extend({}, $.fn.modal.settings),
+
+ selector = settings.selector,
+ className = settings.className,
+ namespace = settings.namespace,
+ error = settings.error,
+
+ eventNamespace = '.' + namespace,
+ moduleNamespace = 'module-' + namespace,
+
+ $module = $(this),
+ $context = $(settings.context),
+ $close = $module.find(selector.close),
+
+ $allModals,
+ $otherModals,
+ $focusedElement,
+ $dimmable,
+ $dimmer,
+
+ element = this,
+ instance = $module.data(moduleNamespace),
+
+ ignoreRepeatedEvents = false,
+
+ initialMouseDownInModal,
+ initialMouseDownInScrollbar,
+ initialBodyMargin = '',
+ tempBodyMargin = '',
+
+ elementEventNamespace,
+ id,
+ observer,
+ module
+ ;
+ module = {
+
+ initialize: function() {
+ module.cache = {};
+ module.verbose('Initializing dimmer', $context);
+
+ module.create.id();
+ module.create.dimmer();
+
+ if ( settings.allowMultiple ) {
+ module.create.innerDimmer();
+ }
+ if (!settings.centered){
+ $module.addClass('top aligned');
+ }
+ module.refreshModals();
+
+ module.bind.events();
+ if(settings.observeChanges) {
+ module.observeChanges();
+ }
+ module.instantiate();
+ },
+
+ instantiate: function() {
+ module.verbose('Storing instance of modal');
+ instance = module;
+ $module
+ .data(moduleNamespace, instance)
+ ;
+ },
+
+ create: {
+ dimmer: function() {
+ var
+ defaultSettings = {
+ debug : settings.debug,
+ dimmerName : 'modals'
+ },
+ dimmerSettings = $.extend(true, defaultSettings, settings.dimmerSettings)
+ ;
+ if($.fn.dimmer === undefined) {
+ module.error(error.dimmer);
+ return;
+ }
+ module.debug('Creating dimmer');
+ $dimmable = $context.dimmer(dimmerSettings);
+ if(settings.detachable) {
+ module.verbose('Modal is detachable, moving content into dimmer');
+ $dimmable.dimmer('add content', $module);
+ }
+ else {
+ module.set.undetached();
+ }
+ $dimmer = $dimmable.dimmer('get dimmer');
+ },
+ id: function() {
+ id = (Math.random().toString(16) + '000000000').substr(2, 8);
+ elementEventNamespace = '.' + id;
+ module.verbose('Creating unique id for element', id);
+ },
+ innerDimmer: function() {
+ if ( $module.find(selector.dimmer).length == 0 ) {
+ $module.prepend('<div class="ui inverted dimmer"></div>');
+ }
+ }
+ },
+
+ destroy: function() {
+ if (observer) {
+ observer.disconnect();
+ }
+ module.verbose('Destroying previous modal');
+ $module
+ .removeData(moduleNamespace)
+ .off(eventNamespace)
+ ;
+ $window.off(elementEventNamespace);
+ $dimmer.off(elementEventNamespace);
+ $close.off(eventNamespace);
+ $context.dimmer('destroy');
+ },
+
+ observeChanges: function() {
+ if('MutationObserver' in window) {
+ observer = new MutationObserver(function(mutations) {
+ module.debug('DOM tree modified, refreshing');
+ module.refresh();
+ });
+ observer.observe(element, {
+ childList : true,
+ subtree : true
+ });
+ module.debug('Setting up mutation observer', observer);
+ }
+ },
+
+ refresh: function() {
+ module.remove.scrolling();
+ module.cacheSizes();
+ if(!module.can.useFlex()) {
+ module.set.modalOffset();
+ }
+ module.set.screenHeight();
+ module.set.type();
+ },
+
+ refreshModals: function() {
+ $otherModals = $module.siblings(selector.modal);
+ $allModals = $otherModals.add($module);
+ },
+
+ attachEvents: function(selector, event) {
+ var
+ $toggle = $(selector)
+ ;
+ event = $.isFunction(module[event])
+ ? module[event]
+ : module.toggle
+ ;
+ if($toggle.length > 0) {
+ module.debug('Attaching modal events to element', selector, event);
+ $toggle
+ .off(eventNamespace)
+ .on('click' + eventNamespace, event)
+ ;
+ }
+ else {
+ module.error(error.notFound, selector);
+ }
+ },
+
+ bind: {
+ events: function() {
+ module.verbose('Attaching events');
+ $module
+ .on('click' + eventNamespace, selector.close, module.event.close)
+ .on('click' + eventNamespace, selector.approve, module.event.approve)
+ .on('click' + eventNamespace, selector.deny, module.event.deny)
+ ;
+ $window
+ .on('resize' + elementEventNamespace, module.event.resize)
+ ;
+ },
+ scrollLock: function() {
+ // touch events default to passive, due to changes in chrome to optimize mobile perf
+ $dimmable.get(0).addEventListener('touchmove', module.event.preventScroll, { passive: false });
+ }
+ },
+
+ unbind: {
+ scrollLock: function() {
+ $dimmable.get(0).removeEventListener('touchmove', module.event.preventScroll, { passive: false });
+ }
+ },
+
+ get: {
+ id: function() {
+ return (Math.random().toString(16) + '000000000').substr(2, 8);
+ }
+ },
+
+ event: {
+ approve: function() {
+ if(ignoreRepeatedEvents || settings.onApprove.call(element, $(this)) === false) {
+ module.verbose('Approve callback returned false cancelling hide');
+ return;
+ }
+ ignoreRepeatedEvents = true;
+ module.hide(function() {
+ ignoreRepeatedEvents = false;
+ });
+ },
+ preventScroll: function(event) {
+ if(event.target.className.indexOf('dimmer') !== -1) {
+ event.preventDefault();
+ }
+ },
+ deny: function() {
+ if(ignoreRepeatedEvents || settings.onDeny.call(element, $(this)) === false) {
+ module.verbose('Deny callback returned false cancelling hide');
+ return;
+ }
+ ignoreRepeatedEvents = true;
+ module.hide(function() {
+ ignoreRepeatedEvents = false;
+ });
+ },
+ close: function() {
+ module.hide();
+ },
+ mousedown: function(event) {
+ var
+ $target = $(event.target),
+ isRtl = module.is.rtl();
+ ;
+ initialMouseDownInModal = ($target.closest(selector.modal).length > 0);
+ if(initialMouseDownInModal) {
+ module.verbose('Mouse down event registered inside the modal');
+ }
+ initialMouseDownInScrollbar = module.is.scrolling() && ((!isRtl && $(window).outerWidth() - settings.scrollbarWidth <= event.clientX) || (isRtl && settings.scrollbarWidth >= event.clientX));
+ if(initialMouseDownInScrollbar) {
+ module.verbose('Mouse down event registered inside the scrollbar');
+ }
+ },
+ mouseup: function(event) {
+ if(!settings.closable) {
+ module.verbose('Dimmer clicked but closable setting is disabled');
+ return;
+ }
+ if(initialMouseDownInModal) {
+ module.debug('Dimmer clicked but mouse down was initially registered inside the modal');
+ return;
+ }
+ if(initialMouseDownInScrollbar){
+ module.debug('Dimmer clicked but mouse down was initially registered inside the scrollbar');
+ return;
+ }
+ var
+ $target = $(event.target),
+ isInModal = ($target.closest(selector.modal).length > 0),
+ isInDOM = $.contains(document.documentElement, event.target)
+ ;
+ if(!isInModal && isInDOM && module.is.active() && $module.hasClass(className.front) ) {
+ module.debug('Dimmer clicked, hiding all modals');
+ if(settings.allowMultiple) {
+ if(!module.hideAll()) {
+ return;
+ }
+ }
+ else if(!module.hide()){
+ return;
+ }
+ module.remove.clickaway();
+ }
+ },
+ debounce: function(method, delay) {
+ clearTimeout(module.timer);
+ module.timer = setTimeout(method, delay);
+ },
+ keyboard: function(event) {
+ var
+ keyCode = event.which,
+ escapeKey = 27
+ ;
+ if(keyCode == escapeKey) {
+ if(settings.closable) {
+ module.debug('Escape key pressed hiding modal');
+ if ( $module.hasClass(className.front) ) {
+ module.hide();
+ }
+ }
+ else {
+ module.debug('Escape key pressed, but closable is set to false');
+ }
+ event.preventDefault();
+ }
+ },
+ resize: function() {
+ if( $dimmable.dimmer('is active') && ( module.is.animating() || module.is.active() ) ) {
+ requestAnimationFrame(module.refresh);
+ }
+ }
+ },
+
+ toggle: function() {
+ if( module.is.active() || module.is.animating() ) {
+ module.hide();
+ }
+ else {
+ module.show();
+ }
+ },
+
+ show: function(callback) {
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ module.refreshModals();
+ module.set.dimmerSettings();
+ module.set.dimmerStyles();
+
+ module.showModal(callback);
+ },
+
+ hide: function(callback) {
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ module.refreshModals();
+ return module.hideModal(callback);
+ },
+
+ showModal: function(callback) {
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ if( module.is.animating() || !module.is.active() ) {
+ module.showDimmer();
+ module.cacheSizes();
+ module.set.bodyMargin();
+ if(module.can.useFlex()) {
+ module.remove.legacy();
+ }
+ else {
+ module.set.legacy();
+ module.set.modalOffset();
+ module.debug('Using non-flex legacy modal positioning.');
+ }
+ module.set.screenHeight();
+ module.set.type();
+ module.set.clickaway();
+
+ if( !settings.allowMultiple && module.others.active() ) {
+ module.hideOthers(module.showModal);
+ }
+ else {
+ ignoreRepeatedEvents = false;
+ if( settings.allowMultiple ) {
+ if ( module.others.active() ) {
+ $otherModals.filter('.' + className.active).find(selector.dimmer).addClass('active');
+ }
+
+ if ( settings.detachable ) {
+ $module.detach().appendTo($dimmer);
+ }
+ }
+ settings.onShow.call(element);
+ if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
+ module.debug('Showing modal with css animations');
+ $module
+ .transition({
+ debug : settings.debug,
+ animation : settings.transition + ' in',
+ queue : settings.queue,
+ duration : settings.duration,
+ useFailSafe : true,
+ onComplete : function() {
+ settings.onVisible.apply(element);
+ if(settings.keyboardShortcuts) {
+ module.add.keyboardShortcuts();
+ }
+ module.save.focus();
+ module.set.active();
+ if(settings.autofocus) {
+ module.set.autofocus();
+ }
+ callback();
+ }
+ })
+ ;
+ }
+ else {
+ module.error(error.noTransition);
+ }
+ }
+ }
+ else {
+ module.debug('Modal is already visible');
+ }
+ },
+
+ hideModal: function(callback, keepDimmed, hideOthersToo) {
+ var
+ $previousModal = $otherModals.filter('.' + className.active).last()
+ ;
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ module.debug('Hiding modal');
+ if(settings.onHide.call(element, $(this)) === false) {
+ module.verbose('Hide callback returned false cancelling hide');
+ ignoreRepeatedEvents = false;
+ return false;
+ }
+ $module.fomanticExt.onModalBeforeHidden.call(element); // GITEA-PATCH: handle more UI updates before hidden
+ if( module.is.animating() || module.is.active() ) {
+ if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
+ module.remove.active();
+ $module
+ .transition({
+ debug : settings.debug,
+ animation : settings.transition + ' out',
+ queue : settings.queue,
+ duration : settings.duration,
+ useFailSafe : true,
+ onStart : function() {
+ if(!module.others.active() && !module.others.animating() && !keepDimmed) {
+ module.hideDimmer();
+ }
+ if( settings.keyboardShortcuts && !module.others.active() ) {
+ module.remove.keyboardShortcuts();
+ }
+ },
+ onComplete : function() {
+ module.unbind.scrollLock();
+ if ( settings.allowMultiple ) {
+ $previousModal.addClass(className.front);
+ $module.removeClass(className.front);
+
+ if ( hideOthersToo ) {
+ $allModals.find(selector.dimmer).removeClass('active');
+ }
+ else {
+ $previousModal.find(selector.dimmer).removeClass('active');
+ }
+ }
+ settings.onHidden.call(element);
+ module.remove.dimmerStyles();
+ module.restore.focus();
+ callback();
+ }
+ })
+ ;
+ }
+ else {
+ module.error(error.noTransition);
+ }
+ }
+ },
+
+ showDimmer: function() {
+ if($dimmable.dimmer('is animating') || !$dimmable.dimmer('is active') ) {
+ module.save.bodyMargin();
+ module.debug('Showing dimmer');
+ $dimmable.dimmer('show');
+ }
+ else {
+ module.debug('Dimmer already visible');
+ }
+ },
+
+ hideDimmer: function() {
+ if( $dimmable.dimmer('is animating') || ($dimmable.dimmer('is active')) ) {
+ module.unbind.scrollLock();
+ $dimmable.dimmer('hide', function() {
+ module.restore.bodyMargin();
+ module.remove.clickaway();
+ module.remove.screenHeight();
+ });
+ }
+ else {
+ module.debug('Dimmer is not visible cannot hide');
+ return;
+ }
+ },
+
+ hideAll: function(callback) {
+ var
+ $visibleModals = $allModals.filter('.' + className.active + ', .' + className.animating)
+ ;
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ if( $visibleModals.length > 0 ) {
+ module.debug('Hiding all visible modals');
+ var hideOk = true;
+//check in reverse order trying to hide most top displayed modal first
+ $($visibleModals.get().reverse()).each(function(index,element){
+ if(hideOk){
+ hideOk = $(element).modal('hide modal', callback, false, true);
+ }
+ });
+ if(hideOk) {
+ module.hideDimmer();
+ }
+ return hideOk;
+ }
+ },
+
+ hideOthers: function(callback) {
+ var
+ $visibleModals = $otherModals.filter('.' + className.active + ', .' + className.animating)
+ ;
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ if( $visibleModals.length > 0 ) {
+ module.debug('Hiding other modals', $otherModals);
+ $visibleModals
+ .modal('hide modal', callback, true)
+ ;
+ }
+ },
+
+ others: {
+ active: function() {
+ return ($otherModals.filter('.' + className.active).length > 0);
+ },
+ animating: function() {
+ return ($otherModals.filter('.' + className.animating).length > 0);
+ }
+ },
+
+
+ add: {
+ keyboardShortcuts: function() {
+ module.verbose('Adding keyboard shortcuts');
+ $document
+ .on('keyup' + eventNamespace, module.event.keyboard)
+ ;
+ }
+ },
+
+ save: {
+ focus: function() {
+ var
+ $activeElement = $(document.activeElement),
+ inCurrentModal = $activeElement.closest($module).length > 0
+ ;
+ if(!inCurrentModal) {
+ $focusedElement = $(document.activeElement).blur();
+ }
+ },
+ bodyMargin: function() {
+ initialBodyMargin = $body.css('margin-'+(module.can.leftBodyScrollbar() ? 'left':'right'));
+ var bodyMarginRightPixel = parseInt(initialBodyMargin.replace(/[^\d.]/g, '')),
+ bodyScrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
+ tempBodyMargin = bodyMarginRightPixel + bodyScrollbarWidth;
+ }
+ },
+
+ restore: {
+ focus: function() {
+ if($focusedElement && $focusedElement.length > 0 && settings.restoreFocus) {
+ $focusedElement.focus();
+ }
+ },
+ bodyMargin: function() {
+ var position = module.can.leftBodyScrollbar() ? 'left':'right';
+ $body.css('margin-'+position, initialBodyMargin);
+ $body.find(selector.bodyFixed.replace('right',position)).css('padding-'+position, initialBodyMargin);
+ }
+ },
+
+ remove: {
+ active: function() {
+ $module.removeClass(className.active);
+ },
+ legacy: function() {
+ $module.removeClass(className.legacy);
+ },
+ clickaway: function() {
+ if (!settings.detachable) {
+ $module
+ .off('mousedown' + elementEventNamespace)
+ ;
+ }
+ $dimmer
+ .off('mousedown' + elementEventNamespace)
+ ;
+ $dimmer
+ .off('mouseup' + elementEventNamespace)
+ ;
+ },
+ dimmerStyles: function() {
+ $dimmer.removeClass(className.inverted);
+ $dimmable.removeClass(className.blurring);
+ },
+ bodyStyle: function() {
+ if($body.attr('style') === '') {
+ module.verbose('Removing style attribute');
+ $body.removeAttr('style');
+ }
+ },
+ screenHeight: function() {
+ module.debug('Removing page height');
+ $body
+ .css('height', '')
+ ;
+ },
+ keyboardShortcuts: function() {
+ module.verbose('Removing keyboard shortcuts');
+ $document
+ .off('keyup' + eventNamespace)
+ ;
+ },
+ scrolling: function() {
+ $dimmable.removeClass(className.scrolling);
+ $module.removeClass(className.scrolling);
+ }
+ },
+
+ cacheSizes: function() {
+ $module.addClass(className.loading);
+ var
+ scrollHeight = $module.prop('scrollHeight'),
+ modalWidth = $module.outerWidth(),
+ modalHeight = $module.outerHeight()
+ ;
+ if(module.cache.pageHeight === undefined || modalHeight !== 0) {
+ $.extend(module.cache, {
+ pageHeight : $(document).outerHeight(),
+ width : modalWidth,
+ height : modalHeight + settings.offset,
+ scrollHeight : scrollHeight + settings.offset,
+ contextHeight : (settings.context == 'body')
+ ? $(window).height()
+ : $dimmable.height(),
+ });
+ module.cache.topOffset = -(module.cache.height / 2);
+ }
+ $module.removeClass(className.loading);
+ module.debug('Caching modal and container sizes', module.cache);
+ },
+
+ can: {
+ leftBodyScrollbar: function(){
+ if(module.cache.leftBodyScrollbar === undefined) {
+ module.cache.leftBodyScrollbar = module.is.rtl() && ((module.is.iframe && !module.is.firefox()) || module.is.safari() || module.is.edge() || module.is.ie());
+ }
+ return module.cache.leftBodyScrollbar;
+ },
+ useFlex: function() {
+ if (settings.useFlex === 'auto') {
+ return settings.detachable && !module.is.ie();
+ }
+ if(settings.useFlex && module.is.ie()) {
+ module.debug('useFlex true is not supported in IE');
+ } else if(settings.useFlex && !settings.detachable) {
+ module.debug('useFlex true in combination with detachable false is not supported');
+ }
+ return settings.useFlex;
+ },
+ fit: function() {
+ var
+ contextHeight = module.cache.contextHeight,
+ verticalCenter = module.cache.contextHeight / 2,
+ topOffset = module.cache.topOffset,
+ scrollHeight = module.cache.scrollHeight,
+ height = module.cache.height,
+ paddingHeight = settings.padding,
+ startPosition = (verticalCenter + topOffset)
+ ;
+ return (scrollHeight > height)
+ ? (startPosition + scrollHeight + paddingHeight < contextHeight)
+ : (height + (paddingHeight * 2) < contextHeight)
+ ;
+ }
+ },
+
+ is: {
+ active: function() {
+ return $module.hasClass(className.active);
+ },
+ ie: function() {
+ if(module.cache.isIE === undefined) {
+ var
+ isIE11 = (!(window.ActiveXObject) && 'ActiveXObject' in window),
+ isIE = ('ActiveXObject' in window)
+ ;
+ module.cache.isIE = (isIE11 || isIE);
+ }
+ return module.cache.isIE;
+ },
+ animating: function() {
+ return $module.transition('is supported')
+ ? $module.transition('is animating')
+ : $module.is(':visible')
+ ;
+ },
+ scrolling: function() {
+ return $dimmable.hasClass(className.scrolling);
+ },
+ modernBrowser: function() {
+ // appName for IE11 reports 'Netscape' can no longer use
+ return !(window.ActiveXObject || 'ActiveXObject' in window);
+ },
+ rtl: function() {
+ if(module.cache.isRTL === undefined) {
+ module.cache.isRTL = $body.attr('dir') === 'rtl' || $body.css('direction') === 'rtl';
+ }
+ return module.cache.isRTL;
+ },
+ safari: function() {
+ if(module.cache.isSafari === undefined) {
+ module.cache.isSafari = /constructor/i.test(window.HTMLElement) || !!window.ApplePaySession;
+ }
+ return module.cache.isSafari;
+ },
+ edge: function(){
+ if(module.cache.isEdge === undefined) {
+ module.cache.isEdge = !!window.setImmediate && !module.is.ie();
+ }
+ return module.cache.isEdge;
+ },
+ firefox: function(){
+ if(module.cache.isFirefox === undefined) {
+ module.cache.isFirefox = !!window.InstallTrigger;
+ }
+ return module.cache.isFirefox;
+ },
+ iframe: function() {
+ return !(self === top);
+ }
+ },
+
+ set: {
+ autofocus: function() {
+ var
+ $inputs = $module.find('[tabindex], :input').filter(':visible').filter(function() {
+ return $(this).closest('.disabled').length === 0;
+ }),
+ $autofocus = $inputs.filter('[autofocus]'),
+ $input = ($autofocus.length > 0)
+ ? $autofocus.first()
+ : $inputs.first()
+ ;
+ if($input.length > 0) {
+ $input.focus();
+ }
+ },
+ bodyMargin: function() {
+ var position = module.can.leftBodyScrollbar() ? 'left':'right';
+ if(settings.detachable || module.can.fit()) {
+ $body.css('margin-'+position, tempBodyMargin + 'px');
+ }
+ $body.find(selector.bodyFixed.replace('right',position)).css('padding-'+position, tempBodyMargin + 'px');
+ },
+ clickaway: function() {
+ if (!settings.detachable) {
+ $module
+ .on('mousedown' + elementEventNamespace, module.event.mousedown)
+ ;
+ }
+ $dimmer
+ .on('mousedown' + elementEventNamespace, module.event.mousedown)
+ ;
+ $dimmer
+ .on('mouseup' + elementEventNamespace, module.event.mouseup)
+ ;
+ },
+ dimmerSettings: function() {
+ if($.fn.dimmer === undefined) {
+ module.error(error.dimmer);
+ return;
+ }
+ var
+ defaultSettings = {
+ debug : settings.debug,
+ dimmerName : 'modals',
+ closable : 'auto',
+ useFlex : module.can.useFlex(),
+ duration : {
+ show : settings.duration,
+ hide : settings.duration
+ }
+ },
+ dimmerSettings = $.extend(true, defaultSettings, settings.dimmerSettings)
+ ;
+ if(settings.inverted) {
+ dimmerSettings.variation = (dimmerSettings.variation !== undefined)
+ ? dimmerSettings.variation + ' inverted'
+ : 'inverted'
+ ;
+ }
+ $context.dimmer('setting', dimmerSettings);
+ },
+ dimmerStyles: function() {
+ if(settings.inverted) {
+ $dimmer.addClass(className.inverted);
+ }
+ else {
+ $dimmer.removeClass(className.inverted);
+ }
+ if(settings.blurring) {
+ $dimmable.addClass(className.blurring);
+ }
+ else {
+ $dimmable.removeClass(className.blurring);
+ }
+ },
+ modalOffset: function() {
+ if (!settings.detachable) {
+ var canFit = module.can.fit();
+ $module
+ .css({
+ top: (!$module.hasClass('aligned') && canFit)
+ ? $(document).scrollTop() + (module.cache.contextHeight - module.cache.height) / 2
+ : !canFit || $module.hasClass('top')
+ ? $(document).scrollTop() + settings.padding
+ : $(document).scrollTop() + (module.cache.contextHeight - module.cache.height - settings.padding),
+ marginLeft: -(module.cache.width / 2)
+ })
+ ;
+ } else {
+ $module
+ .css({
+ marginTop: (!$module.hasClass('aligned') && module.can.fit())
+ ? -(module.cache.height / 2)
+ : settings.padding / 2,
+ marginLeft: -(module.cache.width / 2)
+ })
+ ;
+ }
+ module.verbose('Setting modal offset for legacy mode');
+ },
+ screenHeight: function() {
+ if( module.can.fit() ) {
+ $body.css('height', '');
+ }
+ else if(!$module.hasClass('bottom')) {
+ module.debug('Modal is taller than page content, resizing page height');
+ $body
+ .css('height', module.cache.height + (settings.padding * 2) )
+ ;
+ }
+ },
+ active: function() {
+ $module.addClass(className.active + ' ' + className.front);
+ $otherModals.filter('.' + className.active).removeClass(className.front);
+ },
+ scrolling: function() {
+ $dimmable.addClass(className.scrolling);
+ $module.addClass(className.scrolling);
+ module.unbind.scrollLock();
+ },
+ legacy: function() {
+ $module.addClass(className.legacy);
+ },
+ type: function() {
+ if(module.can.fit()) {
+ module.verbose('Modal fits on screen');
+ if(!module.others.active() && !module.others.animating()) {
+ module.remove.scrolling();
+ module.bind.scrollLock();
+ }
+ }
+ else if (!$module.hasClass('bottom')){
+ module.verbose('Modal cannot fit on screen setting to scrolling');
+ module.set.scrolling();
+ } else {
+ module.verbose('Bottom aligned modal not fitting on screen is unsupported for scrolling');
+ }
+ },
+ undetached: function() {
+ $dimmable.addClass(className.undetached);
+ }
+ },
+
+ setting: function(name, value) {
+ module.debug('Changing setting', name, value);
+ if( $.isPlainObject(name) ) {
+ $.extend(true, settings, name);
+ }
+ else if(value !== undefined) {
+ if($.isPlainObject(settings[name])) {
+ $.extend(true, settings[name], value);
+ }
+ else {
+ settings[name] = value;
+ }
+ }
+ else {
+ return settings[name];
+ }
+ },
+ internal: function(name, value) {
+ if( $.isPlainObject(name) ) {
+ $.extend(true, module, name);
+ }
+ else if(value !== undefined) {
+ module[name] = value;
+ }
+ else {
+ return module[name];
+ }
+ },
+ debug: function() {
+ if(!settings.silent && settings.debug) {
+ if(settings.performance) {
+ module.performance.log(arguments);
+ }
+ else {
+ module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
+ module.debug.apply(console, arguments);
+ }
+ }
+ },
+ verbose: function() {
+ if(!settings.silent && settings.verbose && settings.debug) {
+ if(settings.performance) {
+ module.performance.log(arguments);
+ }
+ else {
+ module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
+ module.verbose.apply(console, arguments);
+ }
+ }
+ },
+ error: function() {
+ if(!settings.silent) {
+ module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
+ module.error.apply(console, arguments);
+ }
+ },
+ performance: {
+ log: function(message) {
+ var
+ currentTime,
+ executionTime,
+ previousTime
+ ;
+ if(settings.performance) {
+ currentTime = new Date().getTime();
+ previousTime = time || currentTime;
+ executionTime = currentTime - previousTime;
+ time = currentTime;
+ performance.push({
+ 'Name' : message[0],
+ 'Arguments' : [].slice.call(message, 1) || '',
+ 'Element' : element,
+ 'Execution Time' : executionTime
+ });
+ }
+ clearTimeout(module.performance.timer);
+ module.performance.timer = setTimeout(module.performance.display, 500);
+ },
+ display: function() {
+ var
+ title = settings.name + ':',
+ totalTime = 0
+ ;
+ time = false;
+ clearTimeout(module.performance.timer);
+ $.each(performance, function(index, data) {
+ totalTime += data['Execution Time'];
+ });
+ title += ' ' + totalTime + 'ms';
+ if(moduleSelector) {
+ title += ' \'' + moduleSelector + '\'';
+ }
+ if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
+ console.groupCollapsed(title);
+ if(console.table) {
+ console.table(performance);
+ }
+ else {
+ $.each(performance, function(index, data) {
+ console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
+ });
+ }
+ console.groupEnd();
+ }
+ performance = [];
+ }
+ },
+ invoke: function(query, passedArguments, context) {
+ var
+ object = instance,
+ maxDepth,
+ found,
+ response
+ ;
+ passedArguments = passedArguments || queryArguments;
+ context = element || context;
+ if(typeof query == 'string' && object !== undefined) {
+ query = query.split(/[\. ]/);
+ maxDepth = query.length - 1;
+ $.each(query, function(depth, value) {
+ var camelCaseValue = (depth != maxDepth)
+ ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
+ : query
+ ;
+ if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
+ object = object[camelCaseValue];
+ }
+ else if( object[camelCaseValue] !== undefined ) {
+ found = object[camelCaseValue];
+ return false;
+ }
+ else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
+ object = object[value];
+ }
+ else if( object[value] !== undefined ) {
+ found = object[value];
+ return false;
+ }
+ else {
+ return false;
+ }
+ });
+ }
+ if ( $.isFunction( found ) ) {
+ response = found.apply(context, passedArguments);
+ }
+ else if(found !== undefined) {
+ response = found;
+ }
+ if(Array.isArray(returnedValue)) {
+ returnedValue.push(response);
+ }
+ else if(returnedValue !== undefined) {
+ returnedValue = [returnedValue, response];
+ }
+ else if(response !== undefined) {
+ returnedValue = response;
+ }
+ return found;
+ }
+ };
+
+ if(methodInvoked) {
+ if(instance === undefined) {
+ module.initialize();
+ }
+ module.invoke(query);
+ }
+ else {
+ if(instance !== undefined) {
+ instance.invoke('destroy');
+ }
+ module.initialize();
+ }
+ })
+ ;
+
+ return (returnedValue !== undefined)
+ ? returnedValue
+ : this
+ ;
+};
+
+$.fn.modal.settings = {
+
+ name : 'Modal',
+ namespace : 'modal',
+
+ useFlex : 'auto',
+ offset : 0,
+
+ silent : false,
+ debug : false,
+ verbose : false,
+ performance : true,
+
+ observeChanges : false,
+
+ allowMultiple : false,
+ detachable : true,
+ closable : true,
+ autofocus : true,
+ restoreFocus : true,
+
+ inverted : false,
+ blurring : false,
+
+ centered : true,
+
+ dimmerSettings : {
+ closable : false,
+ useCSS : true
+ },
+
+ // whether to use keyboard shortcuts
+ keyboardShortcuts: true,
+
+ context : 'body',
+
+ queue : false,
+ duration : 500,
+ transition : 'scale',
+
+ // padding with edge of page
+ padding : 50,
+ scrollbarWidth: 10,
+
+ // called before show animation
+ onShow : function(){},
+
+ // called after show animation
+ onVisible : function(){},
+
+ // called before hide animation
+ onHide : function(){ return true; },
+
+ // called after hide animation
+ onHidden : function(){},
+
+ // called after approve selector match
+ onApprove : function(){ return true; },
+
+ // called after deny selector match
+ onDeny : function(){ return true; },
+
+ selector : {
+ close : '> .close',
+ approve : '.actions .positive, .actions .approve, .actions .ok',
+ deny : '.actions .negative, .actions .deny, .actions .cancel',
+ modal : '.ui.modal',
+ dimmer : '> .ui.dimmer',
+ bodyFixed: '> .ui.fixed.menu, > .ui.right.toast-container, > .ui.right.sidebar'
+ },
+ error : {
+ dimmer : 'UI Dimmer, a required component is not included in this page',
+ method : 'The method you called is not defined.',
+ notFound : 'The element you specified could not be found'
+ },
+ className : {
+ active : 'active',
+ animating : 'animating',
+ blurring : 'blurring',
+ inverted : 'inverted',
+ legacy : 'legacy',
+ loading : 'loading',
+ scrolling : 'scrolling',
+ undetached : 'undetached',
+ front : 'front'
+ }
+};
+
+
+})( jQuery, window, document );
diff --git a/web_src/fomantic/build/components/search.css b/web_src/fomantic/build/components/search.css
new file mode 100644
index 0000000000..b0a7b8db7e
--- /dev/null
+++ b/web_src/fomantic/build/components/search.css
@@ -0,0 +1,520 @@
+/*!
+ * # Fomantic-UI - Search
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+ Search
+*******************************/
+
+.ui.search {
+ position: relative;
+}
+.ui.search > .prompt {
+ margin: 0;
+ outline: none;
+ -webkit-appearance: none;
+ -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
+ text-shadow: none;
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1.21428571em;
+ padding: 0.67857143em 1em;
+ font-size: 1em;
+ background: #FFFFFF;
+ border: 1px solid rgba(34, 36, 38, 0.15);
+ color: rgba(0, 0, 0, 0.87);
+ box-shadow: 0 0 0 0 transparent inset;
+ transition: background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease, border-color 0.1s ease;
+}
+.ui.search .prompt {
+ border-radius: 500rem;
+}
+
+/*--------------
+ Icon
+---------------*/
+
+.ui.search .prompt ~ .search.icon {
+ cursor: pointer;
+}
+
+/*--------------
+ Results
+---------------*/
+
+.ui.search > .results {
+ display: none;
+ position: absolute;
+ top: 100%;
+ left: 0;
+ transform-origin: center top;
+ white-space: normal;
+ text-align: left;
+ text-transform: none;
+ background: #FFFFFF;
+ margin-top: 0.5em;
+ width: 18em;
+ border-radius: 0.28571429rem;
+ box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15);
+ border: 1px solid #D4D4D5;
+ z-index: 998;
+}
+.ui.search > .results > :first-child {
+ border-radius: 0.28571429rem 0.28571429rem 0 0;
+}
+.ui.search > .results > :last-child {
+ border-radius: 0 0 0.28571429rem 0.28571429rem;
+}
+
+/*--------------
+ Result
+---------------*/
+
+.ui.search > .results .result {
+ cursor: pointer;
+ display: block;
+ overflow: hidden;
+ font-size: 1em;
+ padding: 0.85714286em 1.14285714em;
+ color: rgba(0, 0, 0, 0.87);
+ line-height: 1.33;
+ border-bottom: 1px solid rgba(34, 36, 38, 0.1);
+}
+.ui.search > .results .result:last-child {
+ border-bottom: none !important;
+}
+
+/* Image */
+.ui.search > .results .result .image {
+ float: right;
+ overflow: hidden;
+ background: none;
+ width: 5em;
+ height: 3em;
+ border-radius: 0.25em;
+}
+.ui.search > .results .result .image img {
+ display: block;
+ width: auto;
+ height: 100%;
+}
+
+/*--------------
+ Info
+---------------*/
+
+.ui.search > .results .result .image + .content {
+ margin: 0 6em 0 0;
+}
+.ui.search > .results .result .title {
+ margin: -0.14285714em 0 0;
+ font-family: var(--fonts-regular);
+ font-weight: 500;
+ font-size: 1em;
+ color: rgba(0, 0, 0, 0.85);
+}
+.ui.search > .results .result .description {
+ margin-top: 0;
+ font-size: 0.92857143em;
+ color: rgba(0, 0, 0, 0.4);
+}
+.ui.search > .results .result .price {
+ float: right;
+ color: #21BA45;
+}
+
+/*--------------
+ Message
+---------------*/
+
+.ui.search > .results > .message {
+ padding: 1em 1em;
+}
+.ui.search > .results > .message .header {
+ font-family: var(--fonts-regular);
+ font-size: 1rem;
+ font-weight: 500;
+ color: rgba(0, 0, 0, 0.87);
+}
+.ui.search > .results > .message .description {
+ margin-top: 0.25rem;
+ font-size: 1em;
+ color: rgba(0, 0, 0, 0.87);
+}
+
+/* View All Results */
+.ui.search > .results > .action {
+ display: block;
+ border-top: none;
+ background: #F3F4F5;
+ padding: 0.92857143em 1em;
+ color: rgba(0, 0, 0, 0.87);
+ font-weight: 500;
+ text-align: center;
+}
+
+
+/*******************************
+ States
+*******************************/
+
+
+/*--------------------
+ Focus
+---------------------*/
+
+.ui.search > .prompt:focus {
+ border-color: rgba(34, 36, 38, 0.35);
+ background: #FFFFFF;
+ color: rgba(0, 0, 0, 0.95);
+}
+
+/*--------------------
+ Loading
+ ---------------------*/
+
+.ui.loading.search .input > i.icon:before {
+ position: absolute;
+ content: '';
+ top: 50%;
+ left: 50%;
+ margin: -0.64285714em 0 0 -0.64285714em;
+ width: 1.28571429em;
+ height: 1.28571429em;
+ border-radius: 500rem;
+ border: 0.2em solid rgba(0, 0, 0, 0.1);
+}
+.ui.loading.search .input > i.icon:after {
+ position: absolute;
+ content: '';
+ top: 50%;
+ left: 50%;
+ margin: -0.64285714em 0 0 -0.64285714em;
+ width: 1.28571429em;
+ height: 1.28571429em;
+ animation: loader 0.6s infinite linear;
+ border: 0.2em solid #767676;
+ border-radius: 500rem;
+ box-shadow: 0 0 0 1px transparent;
+}
+
+/*--------------
+ Hover
+---------------*/
+
+.ui.search > .results .result:hover,
+.ui.category.search > .results .category .result:hover {
+ background: #F9FAFB;
+}
+.ui.search .action:hover:not(div) {
+ background: #E0E0E0;
+}
+
+/*--------------
+ Active
+---------------*/
+
+.ui.category.search > .results .category.active {
+ background: #F3F4F5;
+}
+.ui.category.search > .results .category.active > .name {
+ color: rgba(0, 0, 0, 0.87);
+}
+.ui.search > .results .result.active,
+.ui.category.search > .results .category .result.active {
+ position: relative;
+ border-left-color: rgba(34, 36, 38, 0.1);
+ background: #F3F4F5;
+ box-shadow: none;
+}
+.ui.search > .results .result.active .title {
+ color: rgba(0, 0, 0, 0.85);
+}
+.ui.search > .results .result.active .description {
+ color: rgba(0, 0, 0, 0.85);
+}
+
+/*--------------------
+ Disabled
+ ----------------------*/
+
+
+/* Disabled */
+.ui.disabled.search {
+ cursor: default;
+ pointer-events: none;
+ opacity: var(--opacity-disabled);
+}
+
+
+/*******************************
+ Types
+*******************************/
+
+
+/*--------------
+ Selection
+ ---------------*/
+
+.ui.search.selection .prompt {
+ border-radius: 0.28571429rem;
+}
+
+/* Remove input */
+.ui.search.selection > .icon.input > .remove.icon {
+ pointer-events: none;
+ position: absolute;
+ left: auto;
+ opacity: 0;
+ color: '';
+ top: 0;
+ right: 0;
+ transition: color 0.1s ease, opacity 0.1s ease;
+}
+.ui.search.selection > .icon.input > .active.remove.icon {
+ cursor: pointer;
+ opacity: 0.8;
+ pointer-events: auto;
+}
+.ui.search.selection > .icon.input:not([class*="left icon"]) > .icon ~ .remove.icon {
+ right: 1.85714em;
+}
+.ui.search.selection > .icon.input > .remove.icon:hover {
+ opacity: 1;
+ color: #DB2828;
+}
+
+/*--------------
+ Category
+ ---------------*/
+
+.ui.category.search .results {
+ width: 28em;
+}
+.ui.category.search .results.animating,
+.ui.category.search .results.visible {
+ display: table;
+}
+
+/* Category */
+.ui.category.search > .results .category {
+ display: table-row;
+ background: #F3F4F5;
+ box-shadow: none;
+ transition: background 0.1s ease, border-color 0.1s ease;
+}
+
+/* Last Category */
+.ui.category.search > .results .category:last-child {
+ border-bottom: none;
+}
+
+/* First / Last */
+.ui.category.search > .results .category:first-child .name + .result {
+ border-radius: 0 0.28571429rem 0 0;
+}
+.ui.category.search > .results .category:last-child .result:last-child {
+ border-radius: 0 0 0.28571429rem 0;
+}
+
+/* Category Result Name */
+.ui.category.search > .results .category > .name {
+ display: table-cell;
+ text-overflow: ellipsis;
+ width: 100px;
+ white-space: nowrap;
+ background: transparent;
+ font-family: var(--fonts-regular);
+ font-size: 1em;
+ padding: 0.4em 1em;
+ font-weight: 500;
+ color: rgba(0, 0, 0, 0.4);
+ border-bottom: 1px solid rgba(34, 36, 38, 0.1);
+}
+
+/* Category Result */
+.ui.category.search > .results .category .results {
+ display: table-cell;
+ background: #FFFFFF;
+ border-left: 1px solid rgba(34, 36, 38, 0.15);
+ border-bottom: 1px solid rgba(34, 36, 38, 0.1);
+}
+.ui.category.search > .results .category .result {
+ border-bottom: 1px solid rgba(34, 36, 38, 0.1);
+ transition: background 0.1s ease, border-color 0.1s ease;
+ padding: 0.85714286em 1.14285714em;
+}
+
+
+/*******************************
+ Variations
+*******************************/
+
+
+/*-------------------
+ Scrolling
+ --------------------*/
+
+.ui.scrolling.search > .results,
+.ui.search.long > .results,
+.ui.search.short > .results {
+ overflow-x: hidden;
+ overflow-y: auto;
+ backface-visibility: hidden;
+ -webkit-overflow-scrolling: touch;
+}
+@media only screen and (max-width: 767.98px) {
+ .ui.scrolling.search > .results {
+ max-height: 12.17714286em;
+ }
+}
+@media only screen and (min-width: 768px) {
+ .ui.scrolling.search > .results {
+ max-height: 18.26571429em;
+ }
+}
+@media only screen and (min-width: 992px) {
+ .ui.scrolling.search > .results {
+ max-height: 24.35428571em;
+ }
+}
+@media only screen and (min-width: 1920px) {
+ .ui.scrolling.search > .results {
+ max-height: 36.53142857em;
+ }
+}
+@media only screen and (max-width: 767.98px) {
+ .ui.search.short > .results {
+ max-height: 12.17714286em;
+ }
+ .ui.search[class*="very short"] > .results {
+ max-height: 9.13285714em;
+ }
+ .ui.search.long > .results {
+ max-height: 24.35428571em;
+ }
+ .ui.search[class*="very long"] > .results {
+ max-height: 36.53142857em;
+ }
+}
+@media only screen and (min-width: 768px) {
+ .ui.search.short > .results {
+ max-height: 18.26571429em;
+ }
+ .ui.search[class*="very short"] > .results {
+ max-height: 13.69928571em;
+ }
+ .ui.search.long > .results {
+ max-height: 36.53142857em;
+ }
+ .ui.search[class*="very long"] > .results {
+ max-height: 54.79714286em;
+ }
+}
+@media only screen and (min-width: 992px) {
+ .ui.search.short > .results {
+ max-height: 24.35428571em;
+ }
+ .ui.search[class*="very short"] > .results {
+ max-height: 18.26571429em;
+ }
+ .ui.search.long > .results {
+ max-height: 48.70857143em;
+ }
+ .ui.search[class*="very long"] > .results {
+ max-height: 73.06285714em;
+ }
+}
+@media only screen and (min-width: 1920px) {
+ .ui.search.short > .results {
+ max-height: 36.53142857em;
+ }
+ .ui.search[class*="very short"] > .results {
+ max-height: 27.39857143em;
+ }
+ .ui.search.long > .results {
+ max-height: 73.06285714em;
+ }
+ .ui.search[class*="very long"] > .results {
+ max-height: 109.59428571em;
+ }
+}
+
+/*-------------------
+ Left / Right
+ --------------------*/
+
+.ui[class*="left aligned"].search > .results {
+ right: auto;
+ left: 0;
+}
+.ui[class*="right aligned"].search > .results {
+ right: 0;
+ left: auto;
+}
+
+/*--------------
+ Fluid
+---------------*/
+
+.ui.fluid.search .results {
+ width: 100%;
+}
+
+/*--------------
+ Sizes
+---------------*/
+
+.ui.search {
+ font-size: 1em;
+}
+.ui.mini.search {
+ font-size: 0.78571429em;
+}
+.ui.tiny.search {
+ font-size: 0.85714286em;
+}
+.ui.small.search {
+ font-size: 0.92857143em;
+}
+.ui.large.search {
+ font-size: 1.14285714em;
+}
+.ui.big.search {
+ font-size: 1.28571429em;
+}
+.ui.huge.search {
+ font-size: 1.42857143em;
+}
+.ui.massive.search {
+ font-size: 1.71428571em;
+}
+
+/*--------------
+ Mobile
+---------------*/
+
+@media only screen and (max-width: 767.98px) {
+ .ui.search .results {
+ max-width: calc(100vw - 2rem);
+ }
+}
+
+
+/*******************************
+ Theme Overrides
+*******************************/
+
+
+
+/*******************************
+ Site Overrides
+*******************************/
+
diff --git a/web_src/fomantic/build/components/search.js b/web_src/fomantic/build/components/search.js
new file mode 100644
index 0000000000..6f2c7ee699
--- /dev/null
+++ b/web_src/fomantic/build/components/search.js
@@ -0,0 +1,1565 @@
+/*!
+ * # Fomantic-UI - Search
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+;(function ($, window, document, undefined) {
+
+'use strict';
+
+$.isFunction = $.isFunction || function(obj) {
+ return typeof obj === "function" && typeof obj.nodeType !== "number";
+};
+
+window = (typeof window != 'undefined' && window.Math == Math)
+ ? window
+ : (typeof self != 'undefined' && self.Math == Math)
+ ? self
+ : Function('return this')()
+;
+
+$.fn.search = function(parameters) {
+ var
+ $allModules = $(this),
+ moduleSelector = $allModules.selector || '',
+
+ time = new Date().getTime(),
+ performance = [],
+
+ query = arguments[0],
+ methodInvoked = (typeof query == 'string'),
+ queryArguments = [].slice.call(arguments, 1),
+ returnedValue
+ ;
+ $(this)
+ .each(function() {
+ var
+ settings = ( $.isPlainObject(parameters) )
+ ? $.extend(true, {}, $.fn.search.settings, parameters)
+ : $.extend({}, $.fn.search.settings),
+
+ className = settings.className,
+ metadata = settings.metadata,
+ regExp = settings.regExp,
+ fields = settings.fields,
+ selector = settings.selector,
+ error = settings.error,
+ namespace = settings.namespace,
+
+ eventNamespace = '.' + namespace,
+ moduleNamespace = namespace + '-module',
+
+ $module = $(this),
+ $prompt = $module.find(selector.prompt),
+ $searchButton = $module.find(selector.searchButton),
+ $results = $module.find(selector.results),
+ $result = $module.find(selector.result),
+ $category = $module.find(selector.category),
+
+ element = this,
+ instance = $module.data(moduleNamespace),
+
+ disabledBubbled = false,
+ resultsDismissed = false,
+
+ module
+ ;
+
+ module = {
+
+ initialize: function() {
+ module.verbose('Initializing module');
+ module.get.settings();
+ module.determine.searchFields();
+ module.bind.events();
+ module.set.type();
+ module.create.results();
+ module.instantiate();
+ },
+ instantiate: function() {
+ module.verbose('Storing instance of module', module);
+ instance = module;
+ $module
+ .data(moduleNamespace, module)
+ ;
+ },
+ destroy: function() {
+ module.verbose('Destroying instance');
+ $module
+ .off(eventNamespace)
+ .removeData(moduleNamespace)
+ ;
+ },
+
+ refresh: function() {
+ module.debug('Refreshing selector cache');
+ $prompt = $module.find(selector.prompt);
+ $searchButton = $module.find(selector.searchButton);
+ $category = $module.find(selector.category);
+ $results = $module.find(selector.results);
+ $result = $module.find(selector.result);
+ },
+
+ refreshResults: function() {
+ $results = $module.find(selector.results);
+ $result = $module.find(selector.result);
+ },
+
+ bind: {
+ events: function() {
+ module.verbose('Binding events to search');
+ if(settings.automatic) {
+ $module
+ .on(module.get.inputEvent() + eventNamespace, selector.prompt, module.event.input)
+ ;
+ $prompt
+ .attr('autocomplete', 'off')
+ ;
+ }
+ $module
+ // prompt
+ .on('focus' + eventNamespace, selector.prompt, module.event.focus)
+ .on('blur' + eventNamespace, selector.prompt, module.event.blur)
+ .on('keydown' + eventNamespace, selector.prompt, module.handleKeyboard)
+ // search button
+ .on('click' + eventNamespace, selector.searchButton, module.query)
+ // results
+ .on('mousedown' + eventNamespace, selector.results, module.event.result.mousedown)
+ .on('mouseup' + eventNamespace, selector.results, module.event.result.mouseup)
+ .on('click' + eventNamespace, selector.result, module.event.result.click)
+ ;
+ }
+ },
+
+ determine: {
+ searchFields: function() {
+ // this makes sure $.extend does not add specified search fields to default fields
+ // this is the only setting which should not extend defaults
+ if(parameters && parameters.searchFields !== undefined) {
+ settings.searchFields = parameters.searchFields;
+ }
+ }
+ },
+
+ event: {
+ input: function() {
+ if(settings.searchDelay) {
+ clearTimeout(module.timer);
+ module.timer = setTimeout(function() {
+ if(module.is.focused()) {
+ module.query();
+ }
+ }, settings.searchDelay);
+ }
+ else {
+ module.query();
+ }
+ },
+ focus: function() {
+ module.set.focus();
+ if(settings.searchOnFocus && module.has.minimumCharacters() ) {
+ module.query(function() {
+ if(module.can.show() ) {
+ module.showResults();
+ }
+ });
+ }
+ },
+ blur: function(event) {
+ var
+ pageLostFocus = (document.activeElement === this),
+ callback = function() {
+ module.cancel.query();
+ module.remove.focus();
+ module.timer = setTimeout(module.hideResults, settings.hideDelay);
+ }
+ ;
+ if(pageLostFocus) {
+ return;
+ }
+ resultsDismissed = false;
+ if(module.resultsClicked) {
+ module.debug('Determining if user action caused search to close');
+ $module
+ .one('click.close' + eventNamespace, selector.results, function(event) {
+ if(module.is.inMessage(event) || disabledBubbled) {
+ $prompt.focus();
+ return;
+ }
+ disabledBubbled = false;
+ if( !module.is.animating() && !module.is.hidden()) {
+ callback();
+ }
+ })
+ ;
+ }
+ else {
+ module.debug('Input blurred without user action, closing results');
+ callback();
+ }
+ },
+ result: {
+ mousedown: function() {
+ module.resultsClicked = true;
+ },
+ mouseup: function() {
+ module.resultsClicked = false;
+ },
+ click: function(event) {
+ module.debug('Search result selected');
+ var
+ $result = $(this),
+ $title = $result.find(selector.title).eq(0),
+ $link = $result.is('a[href]')
+ ? $result
+ : $result.find('a[href]').eq(0),
+ href = $link.attr('href') || false,
+ target = $link.attr('target') || false,
+ // title is used for result lookup
+ value = ($title.length > 0)
+ ? $title.text()
+ : false,
+ results = module.get.results(),
+ result = $result.data(metadata.result) || module.get.result(value, results)
+ ;
+ if(value) {
+ module.set.value(value);
+ }
+ if( $.isFunction(settings.onSelect) ) {
+ if(settings.onSelect.call(element, result, results) === false) {
+ module.debug('Custom onSelect callback cancelled default select action');
+ disabledBubbled = true;
+ return;
+ }
+ }
+ module.hideResults();
+ if(href) {
+ event.preventDefault();
+ module.verbose('Opening search link found in result', $link);
+ if(target == '_blank' || event.ctrlKey) {
+ window.open(href);
+ }
+ else {
+ window.location.href = (href);
+ }
+ }
+ }
+ }
+ },
+ ensureVisible: function ensureVisible($el) {
+ var elTop, elBottom, resultsScrollTop, resultsHeight;
+
+ elTop = $el.position().top;
+ elBottom = elTop + $el.outerHeight(true);
+
+ resultsScrollTop = $results.scrollTop();
+ resultsHeight = $results.height()
+ parseInt($results.css('paddingTop'), 0) +
+ parseInt($results.css('paddingBottom'), 0);
+
+ if (elTop < 0) {
+ $results.scrollTop(resultsScrollTop + elTop);
+ }
+
+ else if (resultsHeight < elBottom) {
+ $results.scrollTop(resultsScrollTop + (elBottom - resultsHeight));
+ }
+ },
+ handleKeyboard: function(event) {
+ var
+ // force selector refresh
+ $result = $module.find(selector.result),
+ $category = $module.find(selector.category),
+ $activeResult = $result.filter('.' + className.active),
+ currentIndex = $result.index( $activeResult ),
+ resultSize = $result.length,
+ hasActiveResult = $activeResult.length > 0,
+
+ keyCode = event.which,
+ keys = {
+ backspace : 8,
+ enter : 13,
+ escape : 27,
+ upArrow : 38,
+ downArrow : 40
+ },
+ newIndex
+ ;
+ // search shortcuts
+ if(keyCode == keys.escape) {
+ module.verbose('Escape key pressed, blurring search field');
+ module.hideResults();
+ resultsDismissed = true;
+ }
+ if( module.is.visible() ) {
+ if(keyCode == keys.enter) {
+ module.verbose('Enter key pressed, selecting active result');
+ if( $result.filter('.' + className.active).length > 0 ) {
+ module.event.result.click.call($result.filter('.' + className.active), event);
+ event.preventDefault();
+ return false;
+ }
+ }
+ else if(keyCode == keys.upArrow && hasActiveResult) {
+ module.verbose('Up key pressed, changing active result');
+ newIndex = (currentIndex - 1 < 0)
+ ? currentIndex
+ : currentIndex - 1
+ ;
+ $category
+ .removeClass(className.active)
+ ;
+ $result
+ .removeClass(className.active)
+ .eq(newIndex)
+ .addClass(className.active)
+ .closest($category)
+ .addClass(className.active)
+ ;
+ module.ensureVisible($result.eq(newIndex));
+ event.preventDefault();
+ }
+ else if(keyCode == keys.downArrow) {
+ module.verbose('Down key pressed, changing active result');
+ newIndex = (currentIndex + 1 >= resultSize)
+ ? currentIndex
+ : currentIndex + 1
+ ;
+ $category
+ .removeClass(className.active)
+ ;
+ $result
+ .removeClass(className.active)
+ .eq(newIndex)
+ .addClass(className.active)
+ .closest($category)
+ .addClass(className.active)
+ ;
+ module.ensureVisible($result.eq(newIndex));
+ event.preventDefault();
+ }
+ }
+ else {
+ // query shortcuts
+ if(keyCode == keys.enter) {
+ module.verbose('Enter key pressed, executing query');
+ module.query();
+ module.set.buttonPressed();
+ $prompt.one('keyup', module.remove.buttonFocus);
+ }
+ }
+ },
+
+ setup: {
+ api: function(searchTerm, callback) {
+ var
+ apiSettings = {
+ debug : settings.debug,
+ on : false,
+ cache : settings.cache,
+ action : 'search',
+ urlData : {
+ query : searchTerm
+ },
+ onSuccess : function(response) {
+ module.parse.response.call(element, response, searchTerm);
+ callback();
+ },
+ onFailure : function() {
+ module.displayMessage(error.serverError);
+ callback();
+ },
+ onAbort : function(response) {
+ },
+ onError : module.error
+ }
+ ;
+ $.extend(true, apiSettings, settings.apiSettings);
+ module.verbose('Setting up API request', apiSettings);
+ $module.api(apiSettings);
+ }
+ },
+
+ can: {
+ useAPI: function() {
+ return $.fn.api !== undefined;
+ },
+ show: function() {
+ return module.is.focused() && !module.is.visible() && !module.is.empty();
+ },
+ transition: function() {
+ return settings.transition && $.fn.transition !== undefined && $module.transition('is supported');
+ }
+ },
+
+ is: {
+ animating: function() {
+ return $results.hasClass(className.animating);
+ },
+ hidden: function() {
+ return $results.hasClass(className.hidden);
+ },
+ inMessage: function(event) {
+ if(!event.target) {
+ return;
+ }
+ var
+ $target = $(event.target),
+ isInDOM = $.contains(document.documentElement, event.target)
+ ;
+ return (isInDOM && $target.closest(selector.message).length > 0);
+ },
+ empty: function() {
+ return ($results.html() === '');
+ },
+ visible: function() {
+ return ($results.filter(':visible').length > 0);
+ },
+ focused: function() {
+ return ($prompt.filter(':focus').length > 0);
+ }
+ },
+
+ get: {
+ settings: function() {
+ if($.isPlainObject(parameters) && parameters.searchFullText) {
+ settings.fullTextSearch = parameters.searchFullText;
+ module.error(settings.error.oldSearchSyntax, element);
+ }
+ if (settings.ignoreDiacritics && !String.prototype.normalize) {
+ settings.ignoreDiacritics = false;
+ module.error(error.noNormalize, element);
+ }
+ },
+ inputEvent: function() {
+ var
+ prompt = $prompt[0],
+ inputEvent = (prompt !== undefined && prompt.oninput !== undefined)
+ ? 'input'
+ : (prompt !== undefined && prompt.onpropertychange !== undefined)
+ ? 'propertychange'
+ : 'keyup'
+ ;
+ return inputEvent;
+ },
+ value: function() {
+ return $prompt.val();
+ },
+ results: function() {
+ var
+ results = $module.data(metadata.results)
+ ;
+ return results;
+ },
+ result: function(value, results) {
+ var
+ result = false
+ ;
+ value = (value !== undefined)
+ ? value
+ : module.get.value()
+ ;
+ results = (results !== undefined)
+ ? results
+ : module.get.results()
+ ;
+ if(settings.type === 'category') {
+ module.debug('Finding result that matches', value);
+ $.each(results, function(index, category) {
+ if(Array.isArray(category.results)) {
+ result = module.search.object(value, category.results)[0];
+ // don't continue searching if a result is found
+ if(result) {
+ return false;
+ }
+ }
+ });
+ }
+ else {
+ module.debug('Finding result in results object', value);
+ result = module.search.object(value, results)[0];
+ }
+ return result || false;
+ },
+ },
+
+ select: {
+ firstResult: function() {
+ module.verbose('Selecting first result');
+ $result.first().addClass(className.active);
+ }
+ },
+
+ set: {
+ focus: function() {
+ $module.addClass(className.focus);
+ },
+ loading: function() {
+ $module.addClass(className.loading);
+ },
+ value: function(value) {
+ module.verbose('Setting search input value', value);
+ $prompt
+ .val(value)
+ ;
+ },
+ type: function(type) {
+ type = type || settings.type;
+ if(settings.type == 'category') {
+ $module.addClass(settings.type);
+ }
+ },
+ buttonPressed: function() {
+ $searchButton.addClass(className.pressed);
+ }
+ },
+
+ remove: {
+ loading: function() {
+ $module.removeClass(className.loading);
+ },
+ focus: function() {
+ $module.removeClass(className.focus);
+ },
+ buttonPressed: function() {
+ $searchButton.removeClass(className.pressed);
+ },
+ diacritics: function(text) {
+ return settings.ignoreDiacritics ? text.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : text;
+ }
+ },
+
+ query: function(callback) {
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ var
+ searchTerm = module.get.value(),
+ cache = module.read.cache(searchTerm)
+ ;
+ callback = callback || function() {};
+ if( module.has.minimumCharacters() ) {
+ if(cache) {
+ module.debug('Reading result from cache', searchTerm);
+ module.save.results(cache.results);
+ module.addResults(cache.html);
+ module.inject.id(cache.results);
+ callback();
+ }
+ else {
+ module.debug('Querying for', searchTerm);
+ if($.isPlainObject(settings.source) || Array.isArray(settings.source)) {
+ module.search.local(searchTerm);
+ callback();
+ }
+ else if( module.can.useAPI() ) {
+ module.search.remote(searchTerm, callback);
+ }
+ else {
+ module.error(error.source);
+ callback();
+ }
+ }
+ settings.onSearchQuery.call(element, searchTerm);
+ }
+ else {
+ module.hideResults();
+ }
+ },
+
+ search: {
+ local: function(searchTerm) {
+ var
+ results = module.search.object(searchTerm, settings.source),
+ searchHTML
+ ;
+ module.set.loading();
+ module.save.results(results);
+ module.debug('Returned full local search results', results);
+ if(settings.maxResults > 0) {
+ module.debug('Using specified max results', results);
+ results = results.slice(0, settings.maxResults);
+ }
+ if(settings.type == 'category') {
+ results = module.create.categoryResults(results);
+ }
+ searchHTML = module.generateResults({
+ results: results
+ });
+ module.remove.loading();
+ module.addResults(searchHTML);
+ module.inject.id(results);
+ module.write.cache(searchTerm, {
+ html : searchHTML,
+ results : results
+ });
+ },
+ remote: function(searchTerm, callback) {
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ if($module.api('is loading')) {
+ $module.api('abort');
+ }
+ module.setup.api(searchTerm, callback);
+ $module
+ .api('query')
+ ;
+ },
+ object: function(searchTerm, source, searchFields) {
+ searchTerm = module.remove.diacritics(String(searchTerm));
+ var
+ results = [],
+ exactResults = [],
+ fuzzyResults = [],
+ searchExp = searchTerm.replace(regExp.escape, '\\$&'),
+ matchRegExp = new RegExp(regExp.beginsWith + searchExp, 'i'),
+
+ // avoid duplicates when pushing results
+ addResult = function(array, result) {
+ var
+ notResult = ($.inArray(result, results) == -1),
+ notFuzzyResult = ($.inArray(result, fuzzyResults) == -1),
+ notExactResults = ($.inArray(result, exactResults) == -1)
+ ;
+ if(notResult && notFuzzyResult && notExactResults) {
+ array.push(result);
+ }
+ }
+ ;
+ source = source || settings.source;
+ searchFields = (searchFields !== undefined)
+ ? searchFields
+ : settings.searchFields
+ ;
+
+ // search fields should be array to loop correctly
+ if(!Array.isArray(searchFields)) {
+ searchFields = [searchFields];
+ }
+
+ // exit conditions if no source
+ if(source === undefined || source === false) {
+ module.error(error.source);
+ return [];
+ }
+ // iterate through search fields looking for matches
+ $.each(searchFields, function(index, field) {
+ $.each(source, function(label, content) {
+ var
+ fieldExists = (typeof content[field] == 'string') || (typeof content[field] == 'number')
+ ;
+ if(fieldExists) {
+ var text;
+ if (typeof content[field] === 'string'){
+ text = module.remove.diacritics(content[field]);
+ } else {
+ text = content[field].toString();
+ }
+ if( text.search(matchRegExp) !== -1) {
+ // content starts with value (first in results)
+ addResult(results, content);
+ }
+ else if(settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text) ) {
+ // content fuzzy matches (last in results)
+ addResult(exactResults, content);
+ }
+ else if(settings.fullTextSearch == true && module.fuzzySearch(searchTerm, text) ) {
+ // content fuzzy matches (last in results)
+ addResult(fuzzyResults, content);
+ }
+ }
+ });
+ });
+ $.merge(exactResults, fuzzyResults);
+ $.merge(results, exactResults);
+ return results;
+ }
+ },
+ exactSearch: function (query, term) {
+ query = query.toLowerCase();
+ term = term.toLowerCase();
+ return term.indexOf(query) > -1;
+ },
+ fuzzySearch: function(query, term) {
+ var
+ termLength = term.length,
+ queryLength = query.length
+ ;
+ if(typeof query !== 'string') {
+ return false;
+ }
+ query = query.toLowerCase();
+ term = term.toLowerCase();
+ if(queryLength > termLength) {
+ return false;
+ }
+ if(queryLength === termLength) {
+ return (query === term);
+ }
+ search: for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
+ var
+ queryCharacter = query.charCodeAt(characterIndex)
+ ;
+ while(nextCharacterIndex < termLength) {
+ if(term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
+ continue search;
+ }
+ }
+ return false;
+ }
+ return true;
+ },
+
+ parse: {
+ response: function(response, searchTerm) {
+ if(Array.isArray(response)){
+ var o={};
+ o[fields.results]=response;
+ response = o;
+ }
+ var
+ searchHTML = module.generateResults(response)
+ ;
+ module.verbose('Parsing server response', response);
+ if(response !== undefined) {
+ if(searchTerm !== undefined && response[fields.results] !== undefined) {
+ module.addResults(searchHTML);
+ module.inject.id(response[fields.results]);
+ module.write.cache(searchTerm, {
+ html : searchHTML,
+ results : response[fields.results]
+ });
+ module.save.results(response[fields.results]);
+ }
+ }
+ }
+ },
+
+ cancel: {
+ query: function() {
+ if( module.can.useAPI() ) {
+ $module.api('abort');
+ }
+ }
+ },
+
+ has: {
+ minimumCharacters: function() {
+ var
+ searchTerm = module.get.value(),
+ numCharacters = searchTerm.length
+ ;
+ return (numCharacters >= settings.minCharacters);
+ },
+ results: function() {
+ if($results.length === 0) {
+ return false;
+ }
+ var
+ html = $results.html()
+ ;
+ return html != '';
+ }
+ },
+
+ clear: {
+ cache: function(value) {
+ var
+ cache = $module.data(metadata.cache)
+ ;
+ if(!value) {
+ module.debug('Clearing cache', value);
+ $module.removeData(metadata.cache);
+ }
+ else if(value && cache && cache[value]) {
+ module.debug('Removing value from cache', value);
+ delete cache[value];
+ $module.data(metadata.cache, cache);
+ }
+ }
+ },
+
+ read: {
+ cache: function(name) {
+ var
+ cache = $module.data(metadata.cache)
+ ;
+ if(settings.cache) {
+ module.verbose('Checking cache for generated html for query', name);
+ return (typeof cache == 'object') && (cache[name] !== undefined)
+ ? cache[name]
+ : false
+ ;
+ }
+ return false;
+ }
+ },
+
+ create: {
+ categoryResults: function(results) {
+ var
+ categoryResults = {}
+ ;
+ $.each(results, function(index, result) {
+ if(!result.category) {
+ return;
+ }
+ if(categoryResults[result.category] === undefined) {
+ module.verbose('Creating new category of results', result.category);
+ categoryResults[result.category] = {
+ name : result.category,
+ results : [result]
+ };
+ }
+ else {
+ categoryResults[result.category].results.push(result);
+ }
+ });
+ return categoryResults;
+ },
+ id: function(resultIndex, categoryIndex) {
+ var
+ resultID = (resultIndex + 1), // not zero indexed
+ letterID,
+ id
+ ;
+ if(categoryIndex !== undefined) {
+ // start char code for "A"
+ letterID = String.fromCharCode(97 + categoryIndex);
+ id = letterID + resultID;
+ module.verbose('Creating category result id', id);
+ }
+ else {
+ id = resultID;
+ module.verbose('Creating result id', id);
+ }
+ return id;
+ },
+ results: function() {
+ if($results.length === 0) {
+ $results = $('<div />')
+ .addClass(className.results)
+ .appendTo($module)
+ ;
+ }
+ }
+ },
+
+ inject: {
+ result: function(result, resultIndex, categoryIndex) {
+ module.verbose('Injecting result into results');
+ var
+ $selectedResult = (categoryIndex !== undefined)
+ ? $results
+ .children().eq(categoryIndex)
+ .children(selector.results)
+ .first()
+ .children(selector.result)
+ .eq(resultIndex)
+ : $results
+ .children(selector.result).eq(resultIndex)
+ ;
+ module.verbose('Injecting results metadata', $selectedResult);
+ $selectedResult
+ .data(metadata.result, result)
+ ;
+ },
+ id: function(results) {
+ module.debug('Injecting unique ids into results');
+ var
+ // since results may be object, we must use counters
+ categoryIndex = 0,
+ resultIndex = 0
+ ;
+ if(settings.type === 'category') {
+ // iterate through each category result
+ $.each(results, function(index, category) {
+ if(category.results.length > 0){
+ resultIndex = 0;
+ $.each(category.results, function(index, result) {
+ if(result.id === undefined) {
+ result.id = module.create.id(resultIndex, categoryIndex);
+ }
+ module.inject.result(result, resultIndex, categoryIndex);
+ resultIndex++;
+ });
+ categoryIndex++;
+ }
+ });
+ }
+ else {
+ // top level
+ $.each(results, function(index, result) {
+ if(result.id === undefined) {
+ result.id = module.create.id(resultIndex);
+ }
+ module.inject.result(result, resultIndex);
+ resultIndex++;
+ });
+ }
+ return results;
+ }
+ },
+
+ save: {
+ results: function(results) {
+ module.verbose('Saving current search results to metadata', results);
+ $module.data(metadata.results, results);
+ }
+ },
+
+ write: {
+ cache: function(name, value) {
+ var
+ cache = ($module.data(metadata.cache) !== undefined)
+ ? $module.data(metadata.cache)
+ : {}
+ ;
+ if(settings.cache) {
+ module.verbose('Writing generated html to cache', name, value);
+ cache[name] = value;
+ $module
+ .data(metadata.cache, cache)
+ ;
+ }
+ }
+ },
+
+ addResults: function(html) {
+ if( $.isFunction(settings.onResultsAdd) ) {
+ if( settings.onResultsAdd.call($results, html) === false ) {
+ module.debug('onResultsAdd callback cancelled default action');
+ return false;
+ }
+ }
+ if(html) {
+ $results
+ .html(html)
+ ;
+ module.refreshResults();
+ if(settings.selectFirstResult) {
+ module.select.firstResult();
+ }
+ module.showResults();
+ }
+ else {
+ module.hideResults(function() {
+ $results.empty();
+ });
+ }
+ },
+
+ showResults: function(callback) {
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ if(resultsDismissed) {
+ return;
+ }
+ if(!module.is.visible() && module.has.results()) {
+ if( module.can.transition() ) {
+ module.debug('Showing results with css animations');
+ $results
+ .transition({
+ animation : settings.transition + ' in',
+ debug : settings.debug,
+ verbose : settings.verbose,
+ duration : settings.duration,
+ onShow : function() {
+ var $firstResult = $module.find(selector.result).eq(0);
+ if($firstResult.length > 0) {
+ module.ensureVisible($firstResult);
+ }
+ },
+ onComplete : function() {
+ callback();
+ },
+ queue : true
+ })
+ ;
+ }
+ else {
+ module.debug('Showing results with javascript');
+ $results
+ .stop()
+ .fadeIn(settings.duration, settings.easing)
+ ;
+ }
+ settings.onResultsOpen.call($results);
+ }
+ },
+ hideResults: function(callback) {
+ callback = $.isFunction(callback)
+ ? callback
+ : function(){}
+ ;
+ if( module.is.visible() ) {
+ if( module.can.transition() ) {
+ module.debug('Hiding results with css animations');
+ $results
+ .transition({
+ animation : settings.transition + ' out',
+ debug : settings.debug,
+ verbose : settings.verbose,
+ duration : settings.duration,
+ onComplete : function() {
+ callback();
+ },
+ queue : true
+ })
+ ;
+ }
+ else {
+ module.debug('Hiding results with javascript');
+ $results
+ .stop()
+ .fadeOut(settings.duration, settings.easing)
+ ;
+ }
+ settings.onResultsClose.call($results);
+ }
+ },
+
+ generateResults: function(response) {
+ module.debug('Generating html from response', response);
+ var
+ template = settings.templates[settings.type],
+ isProperObject = ($.isPlainObject(response[fields.results]) && !$.isEmptyObject(response[fields.results])),
+ isProperArray = (Array.isArray(response[fields.results]) && response[fields.results].length > 0),
+ html = ''
+ ;
+ if(isProperObject || isProperArray ) {
+ if(settings.maxResults > 0) {
+ if(isProperObject) {
+ if(settings.type == 'standard') {
+ module.error(error.maxResults);
+ }
+ }
+ else {
+ response[fields.results] = response[fields.results].slice(0, settings.maxResults);
+ }
+ }
+ if($.isFunction(template)) {
+ html = template(response, fields, settings.preserveHTML);
+ }
+ else {
+ module.error(error.noTemplate, false);
+ }
+ }
+ else if(settings.showNoResults) {
+ html = module.displayMessage(error.noResults, 'empty', error.noResultsHeader);
+ }
+ settings.onResults.call(element, response);
+ return html;
+ },
+
+ displayMessage: function(text, type, header) {
+ type = type || 'standard';
+ module.debug('Displaying message', text, type, header);
+ module.addResults( settings.templates.message(text, type, header) );
+ return settings.templates.message(text, type, header);
+ },
+
+ setting: function(name, value) {
+ if( $.isPlainObject(name) ) {
+ $.extend(true, settings, name);
+ }
+ else if(value !== undefined) {
+ settings[name] = value;
+ }
+ else {
+ return settings[name];
+ }
+ },
+ internal: function(name, value) {
+ if( $.isPlainObject(name) ) {
+ $.extend(true, module, name);
+ }
+ else if(value !== undefined) {
+ module[name] = value;
+ }
+ else {
+ return module[name];
+ }
+ },
+ debug: function() {
+ if(!settings.silent && settings.debug) {
+ if(settings.performance) {
+ module.performance.log(arguments);
+ }
+ else {
+ module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
+ module.debug.apply(console, arguments);
+ }
+ }
+ },
+ verbose: function() {
+ if(!settings.silent && settings.verbose && settings.debug) {
+ if(settings.performance) {
+ module.performance.log(arguments);
+ }
+ else {
+ module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
+ module.verbose.apply(console, arguments);
+ }
+ }
+ },
+ error: function() {
+ if(!settings.silent) {
+ module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
+ module.error.apply(console, arguments);
+ }
+ },
+ performance: {
+ log: function(message) {
+ var
+ currentTime,
+ executionTime,
+ previousTime
+ ;
+ if(settings.performance) {
+ currentTime = new Date().getTime();
+ previousTime = time || currentTime;
+ executionTime = currentTime - previousTime;
+ time = currentTime;
+ performance.push({
+ 'Name' : message[0],
+ 'Arguments' : [].slice.call(message, 1) || '',
+ 'Element' : element,
+ 'Execution Time' : executionTime
+ });
+ }
+ clearTimeout(module.performance.timer);
+ module.performance.timer = setTimeout(module.performance.display, 500);
+ },
+ display: function() {
+ var
+ title = settings.name + ':',
+ totalTime = 0
+ ;
+ time = false;
+ clearTimeout(module.performance.timer);
+ $.each(performance, function(index, data) {
+ totalTime += data['Execution Time'];
+ });
+ title += ' ' + totalTime + 'ms';
+ if(moduleSelector) {
+ title += ' \'' + moduleSelector + '\'';
+ }
+ if($allModules.length > 1) {
+ title += ' ' + '(' + $allModules.length + ')';
+ }
+ if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
+ console.groupCollapsed(title);
+ if(console.table) {
+ console.table(performance);
+ }
+ else {
+ $.each(performance, function(index, data) {
+ console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
+ });
+ }
+ console.groupEnd();
+ }
+ performance = [];
+ }
+ },
+ invoke: function(query, passedArguments, context) {
+ var
+ object = instance,
+ maxDepth,
+ found,
+ response
+ ;
+ passedArguments = passedArguments || queryArguments;
+ context = element || context;
+ if(typeof query == 'string' && object !== undefined) {
+ query = query.split(/[\. ]/);
+ maxDepth = query.length - 1;
+ $.each(query, function(depth, value) {
+ var camelCaseValue = (depth != maxDepth)
+ ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
+ : query
+ ;
+ if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
+ object = object[camelCaseValue];
+ }
+ else if( object[camelCaseValue] !== undefined ) {
+ found = object[camelCaseValue];
+ return false;
+ }
+ else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
+ object = object[value];
+ }
+ else if( object[value] !== undefined ) {
+ found = object[value];
+ return false;
+ }
+ else {
+ return false;
+ }
+ });
+ }
+ if( $.isFunction( found ) ) {
+ response = found.apply(context, passedArguments);
+ }
+ else if(found !== undefined) {
+ response = found;
+ }
+ if(Array.isArray(returnedValue)) {
+ returnedValue.push(response);
+ }
+ else if(returnedValue !== undefined) {
+ returnedValue = [returnedValue, response];
+ }
+ else if(response !== undefined) {
+ returnedValue = response;
+ }
+ return found;
+ }
+ };
+ if(methodInvoked) {
+ if(instance === undefined) {
+ module.initialize();
+ }
+ module.invoke(query);
+ }
+ else {
+ if(instance !== undefined) {
+ instance.invoke('destroy');
+ }
+ module.initialize();
+ }
+
+ })
+ ;
+
+ return (returnedValue !== undefined)
+ ? returnedValue
+ : this
+ ;
+};
+
+$.fn.search.settings = {
+
+ name : 'Search',
+ namespace : 'search',
+
+ silent : false,
+ debug : false,
+ verbose : false,
+ performance : true,
+
+ // template to use (specified in settings.templates)
+ type : 'standard',
+
+ // minimum characters required to search
+ minCharacters : 1,
+
+ // whether to select first result after searching automatically
+ selectFirstResult : false,
+
+ // API config
+ apiSettings : false,
+
+ // object to search
+ source : false,
+
+ // Whether search should query current term on focus
+ searchOnFocus : true,
+
+ // fields to search
+ searchFields : [
+ 'id',
+ 'title',
+ 'description'
+ ],
+
+ // field to display in standard results template
+ displayField : '',
+
+ // search anywhere in value (set to 'exact' to require exact matches
+ fullTextSearch : 'exact',
+
+ // match results also if they contain diacritics of the same base character (for example searching for "a" will also match "á" or "â" or "à", etc...)
+ ignoreDiacritics : false,
+
+ // whether to add events to prompt automatically
+ automatic : true,
+
+ // delay before hiding menu after blur
+ hideDelay : 0,
+
+ // delay before searching
+ searchDelay : 200,
+
+ // maximum results returned from search
+ maxResults : 7,
+
+ // whether to store lookups in local cache
+ cache : true,
+
+ // whether no results errors should be shown
+ showNoResults : true,
+
+ // preserve possible html of resultset values
+ preserveHTML : true,
+
+ // transition settings
+ transition : 'scale',
+ duration : 200,
+ easing : 'easeOutExpo',
+
+ // callbacks
+ onSelect : false,
+ onResultsAdd : false,
+
+ onSearchQuery : function(query){},
+ onResults : function(response){},
+
+ onResultsOpen : function(){},
+ onResultsClose : function(){},
+
+ className: {
+ animating : 'animating',
+ active : 'active',
+ empty : 'empty',
+ focus : 'focus',
+ hidden : 'hidden',
+ loading : 'loading',
+ results : 'results',
+ pressed : 'down'
+ },
+
+ error : {
+ source : 'Cannot search. No source used, and Semantic API module was not included',
+ noResultsHeader : 'No Results',
+ noResults : 'Your search returned no results',
+ logging : 'Error in debug logging, exiting.',
+ noEndpoint : 'No search endpoint was specified',
+ noTemplate : 'A valid template name was not specified.',
+ oldSearchSyntax : 'searchFullText setting has been renamed fullTextSearch for consistency, please adjust your settings.',
+ serverError : 'There was an issue querying the server.',
+ maxResults : 'Results must be an array to use maxResults setting',
+ method : 'The method you called is not defined.',
+ noNormalize : '"ignoreDiacritics" setting will be ignored. Browser does not support String().normalize(). You may consider including <https://cdn.jsdelivr.net/npm/unorm@1.4.1/lib/unorm.min.js> as a polyfill.'
+ },
+
+ metadata: {
+ cache : 'cache',
+ results : 'results',
+ result : 'result'
+ },
+
+ regExp: {
+ escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
+ beginsWith : '(?:\s|^)'
+ },
+
+ // maps api response attributes to internal representation
+ fields: {
+ categories : 'results', // array of categories (category view)
+ categoryName : 'name', // name of category (category view)
+ categoryResults : 'results', // array of results (category view)
+ description : 'description', // result description
+ image : 'image', // result image
+ price : 'price', // result price
+ results : 'results', // array of results (standard)
+ title : 'title', // result title
+ url : 'url', // result url
+ action : 'action', // "view more" object name
+ actionText : 'text', // "view more" text
+ actionURL : 'url' // "view more" url
+ },
+
+ selector : {
+ prompt : '.prompt',
+ searchButton : '.search.button',
+ results : '.results',
+ message : '.results > .message',
+ category : '.category',
+ result : '.result',
+ title : '.title, .name'
+ },
+
+ templates: {
+ escape: function(string, preserveHTML) {
+ if (preserveHTML){
+ return string;
+ }
+ var
+ badChars = /[<>"'`]/g,
+ shouldEscape = /[&<>"'`]/,
+ escape = {
+ "<": "&lt;",
+ ">": "&gt;",
+ '"': "&quot;",
+ "'": "&#x27;",
+ "`": "&#x60;"
+ },
+ escapedChar = function(chr) {
+ return escape[chr];
+ }
+ ;
+ if(shouldEscape.test(string)) {
+ string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
+ return string.replace(badChars, escapedChar);
+ }
+ return string;
+ },
+ message: function(message, type, header) {
+ var
+ html = ''
+ ;
+ if(message !== undefined && type !== undefined) {
+ html += ''
+ + '<div class="message ' + type + '">'
+ ;
+ if(header) {
+ html += ''
+ + '<div class="header">' + header + '</div>'
+ ;
+ }
+ html += ' <div class="description">' + message + '</div>';
+ html += '</div>';
+ }
+ return html;
+ },
+ category: function(response, fields, preserveHTML) {
+ var
+ html = '',
+ escape = $.fn.search.settings.templates.escape
+ ;
+ if(response[fields.categoryResults] !== undefined) {
+
+ // each category
+ $.each(response[fields.categoryResults], function(index, category) {
+ if(category[fields.results] !== undefined && category.results.length > 0) {
+
+ html += '<div class="category">';
+
+ if(category[fields.categoryName] !== undefined) {
+ html += '<div class="name">' + escape(category[fields.categoryName], preserveHTML) + '</div>';
+ }
+
+ // each item inside category
+ html += '<div class="results">';
+ $.each(category.results, function(index, result) {
+ if(result[fields.url]) {
+ html += '<a class="result" href="' + result[fields.url].replace(/"/g,"") + '">';
+ }
+ else {
+ html += '<a class="result">';
+ }
+ if(result[fields.image] !== undefined) {
+ html += ''
+ + '<div class="image">'
+ + ' <img src="' + result[fields.image].replace(/"/g,"") + '">'
+ + '</div>'
+ ;
+ }
+ html += '<div class="content">';
+ if(result[fields.price] !== undefined) {
+ html += '<div class="price">' + escape(result[fields.price], preserveHTML) + '</div>';
+ }
+ if(result[fields.title] !== undefined) {
+ html += '<div class="title">' + escape(result[fields.title], preserveHTML) + '</div>';
+ }
+ if(result[fields.description] !== undefined) {
+ html += '<div class="description">' + escape(result[fields.description], preserveHTML) + '</div>';
+ }
+ html += ''
+ + '</div>'
+ ;
+ html += '</a>';
+ });
+ html += '</div>';
+ html += ''
+ + '</div>'
+ ;
+ }
+ });
+ if(response[fields.action]) {
+ if(fields.actionURL === false) {
+ html += ''
+ + '<div class="action">'
+ + escape(response[fields.action][fields.actionText], preserveHTML)
+ + '</div>';
+ } else {
+ html += ''
+ + '<a href="' + response[fields.action][fields.actionURL].replace(/"/g,"") + '" class="action">'
+ + escape(response[fields.action][fields.actionText], preserveHTML)
+ + '</a>';
+ }
+ }
+ return html;
+ }
+ return false;
+ },
+ standard: function(response, fields, preserveHTML) {
+ var
+ html = '',
+ escape = $.fn.search.settings.templates.escape
+ ;
+ if(response[fields.results] !== undefined) {
+
+ // each result
+ $.each(response[fields.results], function(index, result) {
+ if(result[fields.url]) {
+ html += '<a class="result" href="' + result[fields.url].replace(/"/g,"") + '">';
+ }
+ else {
+ html += '<a class="result">';
+ }
+ if(result[fields.image] !== undefined) {
+ html += ''
+ + '<div class="image">'
+ + ' <img src="' + result[fields.image].replace(/"/g,"") + '">'
+ + '</div>'
+ ;
+ }
+ html += '<div class="content">';
+ if(result[fields.price] !== undefined) {
+ html += '<div class="price">' + escape(result[fields.price], preserveHTML) + '</div>';
+ }
+ if(result[fields.title] !== undefined) {
+ html += '<div class="title">' + escape(result[fields.title], preserveHTML) + '</div>';
+ }
+ if(result[fields.description] !== undefined) {
+ html += '<div class="description">' + escape(result[fields.description], preserveHTML) + '</div>';
+ }
+ html += ''
+ + '</div>'
+ ;
+ html += '</a>';
+ });
+ if(response[fields.action]) {
+ if(fields.actionURL === false) {
+ html += ''
+ + '<div class="action">'
+ + escape(response[fields.action][fields.actionText], preserveHTML)
+ + '</div>';
+ } else {
+ html += ''
+ + '<a href="' + response[fields.action][fields.actionURL].replace(/"/g,"") + '" class="action">'
+ + escape(response[fields.action][fields.actionText], preserveHTML)
+ + '</a>';
+ }
+ }
+ return html;
+ }
+ return false;
+ }
+ }
+};
+
+})( jQuery, window, document );
diff --git a/web_src/fomantic/build/fomantic.css b/web_src/fomantic/build/fomantic.css
new file mode 100644
index 0000000000..a8fac9c9ba
--- /dev/null
+++ b/web_src/fomantic/build/fomantic.css
@@ -0,0 +1,4 @@
+@import "./components/dropdown.css";
+@import "./components/form.css";
+@import "./components/modal.css";
+@import "./components/search.css";
diff --git a/web_src/fomantic/build/fomantic.js b/web_src/fomantic/build/fomantic.js
new file mode 100644
index 0000000000..c903fd1633
--- /dev/null
+++ b/web_src/fomantic/build/fomantic.js
@@ -0,0 +1,6 @@
+import './components/api.js';
+import './components/dropdown.js';
+import './components/modal.js';
+import './components/search.js';
+
+// Hard-forked from Fomantic UI 2.8.7, patches are commented with "GITEA-PATCH"
diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css
deleted file mode 100644
index aef7a6bdbe..0000000000
--- a/web_src/fomantic/build/semantic.css
+++ /dev/null
@@ -1,5250 +0,0 @@
- /*
- * # Fomantic UI - 2.8.7
- * https://github.com/fomantic/Fomantic-UI
- * http://fomantic-ui.com/
- *
- * Copyright 2014 Contributors
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-/*!
- * # Fomantic-UI - Dropdown
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-/*******************************
- Dropdown
-*******************************/
-
-.ui.dropdown {
- cursor: pointer;
- position: relative;
- display: inline-block;
- outline: none;
- text-align: left;
- transition: box-shadow 0.1s ease, width 0.1s ease;
- -webkit-user-select: none;
- -moz-user-select: none;
- user-select: none;
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-}
-
-/*******************************
- Content
-*******************************/
-
-/*--------------
- Menu
----------------*/
-
-.ui.dropdown .menu {
- cursor: auto;
- position: absolute;
- display: none;
- outline: none;
- top: 100%;
- min-width: -moz-max-content;
- min-width: max-content;
- margin: 0;
- padding: 0 0;
- background: #FFFFFF;
- font-size: 1em;
- text-shadow: none;
- text-align: left;
- box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
- border: 1px solid rgba(34, 36, 38, 0.15);
- border-radius: 0.28571429rem;
- transition: opacity 0.1s ease;
- z-index: 11;
- will-change: transform, opacity;
-}
-
-.ui.dropdown .menu > * {
- white-space: nowrap;
-}
-
-/*--------------
- Hidden Input
----------------*/
-
-.ui.dropdown > input:not(.search):first-child,
-.ui.dropdown > select {
- display: none !important;
-}
-
-/*--------------
- Dropdown Icon
----------------*/
-
-.ui.dropdown:not(.labeled) > .dropdown.icon {
- position: relative;
- width: auto;
- font-size: 0.85714286em;
- margin: 0 0 0 1em;
-}
-
-.ui.dropdown .menu > .item .dropdown.icon {
- width: auto;
- float: right;
- margin: 0em 0 0 1em;
-}
-
-.ui.dropdown .menu > .item .dropdown.icon + .text {
- margin-right: 1em;
-}
-
-/*--------------
- Text
----------------*/
-
-.ui.dropdown > .text {
- display: inline-block;
- transition: none;
-}
-
-/*--------------
- Menu Item
----------------*/
-
-.ui.dropdown .menu > .item {
- position: relative;
- cursor: pointer;
- display: block;
- border: none;
- height: auto;
- min-height: 2.57142857rem;
- text-align: left;
- border-top: none;
- line-height: 1em;
- font-size: 1rem;
- color: rgba(0, 0, 0, 0.87);
- padding: 0.78571429rem 1.14285714rem !important;
- text-transform: none;
- font-weight: normal;
- box-shadow: none;
- -webkit-touch-callout: none;
-}
-
-.ui.dropdown .menu > .item:first-child {
- border-top-width: 0;
-}
-
-.ui.dropdown .menu > .item.vertical {
- display: flex;
- flex-direction: column-reverse;
-}
-
-/*--------------
- Floated Content
----------------*/
-
-.ui.dropdown > .text > [class*="right floated"],
-.ui.dropdown .menu .item > [class*="right floated"] {
- float: right !important;
- margin-right: 0 !important;
- margin-left: 1em !important;
-}
-
-.ui.dropdown > .text > [class*="left floated"],
-.ui.dropdown .menu .item > [class*="left floated"] {
- float: left !important;
- margin-left: 0 !important;
- margin-right: 1em !important;
-}
-
-.ui.dropdown .menu .item > i.icon.floated,
-.ui.dropdown .menu .item > .flag.floated,
-.ui.dropdown .menu .item > .image.floated,
-.ui.dropdown .menu .item > img.floated {
- margin-top: 0em;
-}
-
-/*--------------
- Menu Divider
----------------*/
-
-.ui.dropdown .menu > .header {
- margin: 1rem 0 0.75rem;
- padding: 0 1.14285714rem;
- font-weight: 500;
- text-transform: uppercase;
-}
-
-.ui.dropdown .menu > .header:not(.ui) {
- color: rgba(0, 0, 0, 0.85);
- font-size: 0.78571429em;
-}
-
-.ui.dropdown .menu > .divider {
- border-top: 1px solid rgba(34, 36, 38, 0.1);
- height: 0;
- margin: 0.5em 0;
-}
-
-.ui.dropdown .menu > .horizontal.divider {
- border-top: none;
-}
-
-.ui.dropdown.dropdown .menu > .input {
- width: auto;
- display: flex;
- margin: 1.14285714rem 0.78571429rem;
- min-width: 10rem;
-}
-
-.ui.dropdown .menu > .header + .input {
- margin-top: 0;
-}
-
-.ui.dropdown .menu > .input:not(.transparent) input {
- padding: 0.5em 1em;
-}
-
-.ui.dropdown .menu > .input:not(.transparent) .button,
-.ui.dropdown .menu > .input:not(.transparent) i.icon,
-.ui.dropdown .menu > .input:not(.transparent) .label {
- padding-top: 0.5em;
- padding-bottom: 0.5em;
-}
-
-/*-----------------
- Item Description
--------------------*/
-
-.ui.dropdown > .text > .description,
-.ui.dropdown .menu > .item > .description {
- float: right;
- margin: 0 0 0 1em;
- color: rgba(0, 0, 0, 0.4);
-}
-
-.ui.dropdown .menu > .item.vertical > .description {
- margin: 0;
-}
-
-/*-----------------
- Item Text
--------------------*/
-
-.ui.dropdown .menu > .item.vertical > .text {
- margin-bottom: 0.25em;
-}
-
-/*-----------------
- Message
--------------------*/
-
-.ui.dropdown .menu > .message {
- padding: 0.78571429rem 1.14285714rem;
- font-weight: normal;
-}
-
-.ui.dropdown .menu > .message:not(.ui) {
- color: rgba(0, 0, 0, 0.4);
-}
-
-/*--------------
- Sub Menu
----------------*/
-
-.ui.dropdown .menu .menu {
- top: 0;
- left: 100%;
- right: auto;
- margin: 0 -0.5em !important;
- border-radius: 0.28571429rem !important;
- z-index: 21 !important;
-}
-
-/* Hide Arrow */
-
-.ui.dropdown .menu .menu:after {
- display: none;
-}
-
-/*--------------
- Sub Elements
----------------*/
-
-/* Icons / Flags / Labels / Image */
-
-.ui.dropdown > .text > i.icon,
-.ui.dropdown > .text > .label,
-.ui.dropdown > .text > .flag,
-.ui.dropdown > .text > img,
-.ui.dropdown > .text > .image {
- margin-top: 0em;
-}
-
-.ui.dropdown .menu > .item > i.icon,
-.ui.dropdown .menu > .item > .label,
-.ui.dropdown .menu > .item > .flag,
-.ui.dropdown .menu > .item > .image,
-.ui.dropdown .menu > .item > img {
- margin-top: 0em;
-}
-
-.ui.dropdown > .text > i.icon,
-.ui.dropdown > .text > .label,
-.ui.dropdown > .text > .flag,
-.ui.dropdown > .text > img,
-.ui.dropdown > .text > .image,
-.ui.dropdown .menu > .item > i.icon,
-.ui.dropdown .menu > .item > .label,
-.ui.dropdown .menu > .item > .flag,
-.ui.dropdown .menu > .item > .image,
-.ui.dropdown .menu > .item > img {
- margin-left: 0;
- float: none;
- margin-right: 0.78571429rem;
-}
-
-/*--------------
- Image
----------------*/
-
-.ui.dropdown > .text > img,
-.ui.dropdown > .text > .image:not(.icon),
-.ui.dropdown .menu > .item > .image:not(.icon),
-.ui.dropdown .menu > .item > img {
- display: inline-block;
- vertical-align: top;
- width: auto;
- margin-top: -0.5em;
- margin-bottom: -0.5em;
- max-height: 2em;
-}
-
-/*******************************
- Coupling
-*******************************/
-
-/*--------------
- Menu
----------------*/
-
-/* Remove Menu Item Divider */
-
-.ui.dropdown .ui.menu > .item:before,
-.ui.menu .ui.dropdown .menu > .item:before {
- display: none;
-}
-
-/* Prevent Menu Item Border */
-
-.ui.menu .ui.dropdown .menu .active.item {
- border-left: none;
-}
-
-/* Automatically float dropdown menu right on last menu item */
-
-.ui.menu .right.menu .dropdown:last-child > .menu:not(.left),
-.ui.menu .right.dropdown.item > .menu:not(.left),
-.ui.buttons > .ui.dropdown:last-child > .menu:not(.left) {
- left: auto;
- right: 0;
-}
-
-/*--------------
- Label
- ---------------*/
-
-/* Dropdown Menu */
-
-.ui.label.dropdown .menu {
- min-width: 100%;
-}
-
-/*--------------
- Button
- ---------------*/
-
-/* No Margin On Icon Button */
-
-.ui.dropdown.icon.button > .dropdown.icon {
- margin: 0;
-}
-
-.ui.button.dropdown .menu {
- min-width: 100%;
-}
-
-/*******************************
- Types
-*******************************/
-
-select.ui.dropdown {
- height: 38px;
- padding: 0.5em;
- border: 1px solid rgba(34, 36, 38, 0.15);
- visibility: visible;
-}
-
-/*--------------
- Selection
- ---------------*/
-
-/* Displays like a select box */
-
-.ui.selection.dropdown {
- cursor: pointer;
- word-wrap: break-word;
- line-height: 1em;
- white-space: normal;
- outline: 0;
- transform: rotateZ(0deg);
- min-width: 14em;
- min-height: 2.71428571em;
- background: #FFFFFF;
- display: inline-block;
- padding: 0.78571429em 3.2em 0.78571429em 1em;
- color: rgba(0, 0, 0, 0.87);
- box-shadow: none;
- border: 1px solid rgba(34, 36, 38, 0.15);
- border-radius: 0.28571429rem;
- transition: box-shadow 0.1s ease, width 0.1s ease;
-}
-
-.ui.selection.dropdown.visible,
-.ui.selection.dropdown.active {
- z-index: 10;
-}
-
-.ui.selection.dropdown > .search.icon,
-.ui.selection.dropdown > .delete.icon,
-.ui.selection.dropdown > .dropdown.icon {
- cursor: pointer;
- position: absolute;
- width: auto;
- height: auto;
- line-height: 1.21428571em;
- top: 0.78571429em;
- right: 1em;
- z-index: 3;
- margin: -0.78571429em;
- padding: 0.91666667em;
- opacity: 0.8;
- transition: opacity 0.1s ease;
-}
-
-/* Compact */
-
-.ui.compact.selection.dropdown {
- min-width: 0;
-}
-
-/* Selection Menu */
-
-.ui.selection.dropdown .menu {
- overflow-x: hidden;
- overflow-y: auto;
- backface-visibility: hidden;
- -webkit-overflow-scrolling: touch;
- border-top-width: 0 !important;
- width: auto;
- outline: none;
- margin: 0 -1px;
- min-width: calc(100% + 2px);
- width: calc(100% + 2px);
- border-radius: 0 0 0.28571429rem 0.28571429rem;
- box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
- transition: opacity 0.1s ease;
-}
-
-.ui.selection.dropdown .menu:after,
-.ui.selection.dropdown .menu:before {
- display: none;
-}
-
-/*--------------
- Message
- ---------------*/
-
-.ui.selection.dropdown .menu > .message {
- padding: 0.78571429rem 1.14285714rem;
-}
-
-@media only screen and (max-width: 767.98px) {
- .ui.selection.dropdown.short .menu {
- max-height: 6.01071429rem;
- }
-
- .ui.selection.dropdown[class*="very short"] .menu {
- max-height: 4.00714286rem;
- }
-
- .ui.selection.dropdown .menu {
- max-height: 8.01428571rem;
- }
-
- .ui.selection.dropdown.long .menu {
- max-height: 16.02857143rem;
- }
-
- .ui.selection.dropdown[class*="very long"] .menu {
- max-height: 24.04285714rem;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.selection.dropdown.short .menu {
- max-height: 8.01428571rem;
- }
-
- .ui.selection.dropdown[class*="very short"] .menu {
- max-height: 5.34285714rem;
- }
-
- .ui.selection.dropdown .menu {
- max-height: 10.68571429rem;
- }
-
- .ui.selection.dropdown.long .menu {
- max-height: 21.37142857rem;
- }
-
- .ui.selection.dropdown[class*="very long"] .menu {
- max-height: 32.05714286rem;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.selection.dropdown.short .menu {
- max-height: 12.02142857rem;
- }
-
- .ui.selection.dropdown[class*="very short"] .menu {
- max-height: 8.01428571rem;
- }
-
- .ui.selection.dropdown .menu {
- max-height: 16.02857143rem;
- }
-
- .ui.selection.dropdown.long .menu {
- max-height: 32.05714286rem;
- }
-
- .ui.selection.dropdown[class*="very long"] .menu {
- max-height: 48.08571429rem;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.selection.dropdown.short .menu {
- max-height: 16.02857143rem;
- }
-
- .ui.selection.dropdown[class*="very short"] .menu {
- max-height: 10.68571429rem;
- }
-
- .ui.selection.dropdown .menu {
- max-height: 21.37142857rem;
- }
-
- .ui.selection.dropdown.long .menu {
- max-height: 42.74285714rem;
- }
-
- .ui.selection.dropdown[class*="very long"] .menu {
- max-height: 64.11428571rem;
- }
-}
-
-/* Menu Item */
-
-.ui.selection.dropdown .menu > .item {
- border-top: 1px solid #FAFAFA;
- padding: 0.78571429rem 1.14285714rem !important;
- white-space: normal;
- word-wrap: normal;
-}
-
-/* User Item */
-
-.ui.selection.dropdown .menu > .hidden.addition.item {
- display: none;
-}
-
-/* Hover */
-
-.ui.selection.dropdown:hover {
- border-color: rgba(34, 36, 38, 0.35);
- box-shadow: none;
-}
-
-/* Active */
-
-.ui.selection.active.dropdown {
- border-color: #96C8DA;
- box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
-}
-
-.ui.selection.active.dropdown .menu {
- border-color: #96C8DA;
- box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
-}
-
-/* Focus */
-
-.ui.selection.dropdown:focus {
- border-color: #96C8DA;
- box-shadow: none;
-}
-
-.ui.selection.dropdown:focus .menu {
- border-color: #96C8DA;
- box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
-}
-
-/* Visible */
-
-.ui.selection.visible.dropdown > .text:not(.default) {
- font-weight: normal;
- color: rgba(0, 0, 0, 0.8);
-}
-
-/* Visible Hover */
-
-.ui.selection.active.dropdown:hover {
- border-color: #96C8DA;
- box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
-}
-
-.ui.selection.active.dropdown:hover .menu {
- border-color: #96C8DA;
- box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
-}
-
-/* Dropdown Icon */
-
-.ui.active.selection.dropdown > .dropdown.icon,
-.ui.visible.selection.dropdown > .dropdown.icon {
- opacity: '';
- z-index: 3;
-}
-
-/* Connecting Border */
-
-.ui.active.selection.dropdown {
- border-bottom-left-radius: 0 !important;
- border-bottom-right-radius: 0 !important;
-}
-
-/* Empty Connecting Border */
-
-.ui.active.empty.selection.dropdown {
- border-radius: 0.28571429rem !important;
- box-shadow: none !important;
-}
-
-.ui.active.empty.selection.dropdown .menu {
- border: none !important;
- box-shadow: none !important;
-}
-
-/* CSS specific to iOS devices or firefox mobile only */
-
-@supports (-webkit-touch-callout: none) or (-webkit-overflow-scrolling: touch) or (-moz-appearance:none) {
-@media (-moz-touch-enabled), (pointer: coarse) {
- .ui.dropdown .scrollhint.menu:not(.hidden):before {
- animation: scrollhint 2s ease 2;
- content: '';
- z-index: 15;
- display: block;
- position: absolute;
- opacity: 0;
- right: 0.25em;
- top: 0;
- height: 100%;
- border-right: 0.25em solid;
- border-left: 0;
- -o-border-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0)) 1 100%;
- border-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0)) 1 100%;
- }
-
- .ui.inverted.dropdown .scrollhint.menu:not(.hidden):before {
- -o-border-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.75), rgba(255, 255, 255, 0)) 1 100%;
- border-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.75), rgba(255, 255, 255, 0)) 1 100%;
- }
-
-@keyframes scrollhint {
- 0% {
- opacity: 1;
- top: 100%;
- }
-
- 100% {
- opacity: 0;
- top: 0;
- }
-}
-}
-}
-
-/*--------------
- Searchable
- ---------------*/
-
-/* Search Selection */
-
-.ui.search.dropdown {
- min-width: '';
-}
-
-/* Search Dropdown */
-
-.ui.search.dropdown > input.search {
- background: none transparent !important;
- border: none !important;
- box-shadow: none !important;
- cursor: text;
- top: 0;
- left: 1px;
- width: 100%;
- outline: none;
- -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
- padding: inherit;
-}
-
-/* Text Layering */
-
-.ui.search.dropdown > input.search {
- position: absolute;
- z-index: 2;
-}
-
-.ui.search.dropdown > .text {
- cursor: text;
- position: relative;
- left: 1px;
- z-index: auto;
-}
-
-/* Search Selection */
-
-.ui.search.selection.dropdown > input.search {
- line-height: 1.21428571em;
- padding: 0.67857143em 3.2em 0.67857143em 1em;
-}
-
-/* Used to size multi select input to character width */
-
-.ui.search.selection.dropdown > span.sizer {
- line-height: 1.21428571em;
- padding: 0.67857143em 3.2em 0.67857143em 1em;
- display: none;
- white-space: pre;
-}
-
-/* Active/Visible Search */
-
-.ui.search.dropdown.active > input.search,
-.ui.search.dropdown.visible > input.search {
- cursor: auto;
-}
-
-.ui.search.dropdown.active > .text,
-.ui.search.dropdown.visible > .text {
- pointer-events: none;
-}
-
-/* Filtered Text */
-
-.ui.active.search.dropdown input.search:focus + .text i.icon,
-.ui.active.search.dropdown input.search:focus + .text .flag {
- opacity: var(--opacity-disabled);
-}
-
-.ui.active.search.dropdown input.search:focus + .text {
- color: rgba(115, 115, 115, 0.87) !important;
-}
-
-.ui.search.dropdown.button > span.sizer {
- display: none;
-}
-
-/* Search Menu */
-
-.ui.search.dropdown .menu {
- overflow-x: hidden;
- overflow-y: auto;
- backface-visibility: hidden;
- -webkit-overflow-scrolling: touch;
-}
-
-@media only screen and (max-width: 767.98px) {
- .ui.search.dropdown .menu {
- max-height: 8.01428571rem;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.search.dropdown .menu {
- max-height: 10.68571429rem;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.search.dropdown .menu {
- max-height: 16.02857143rem;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.search.dropdown .menu {
- max-height: 21.37142857rem;
- }
-}
-
-/* Clearable Selection */
-
-.ui.dropdown > .remove.icon {
- cursor: pointer;
- font-size: 0.85714286em;
- margin: -0.78571429em;
- padding: 0.91666667em;
- right: 3em;
- top: 0.78571429em;
- position: absolute;
- opacity: 0.6;
- z-index: 3;
-}
-
-.ui.clearable.dropdown .text,
-.ui.clearable.dropdown a:last-of-type {
- margin-right: 1.5em;
-}
-
-.ui.dropdown select.noselection ~ .remove.icon,
-.ui.dropdown input[value=''] ~ .remove.icon,
-.ui.dropdown input:not([value]) ~ .remove.icon,
-.ui.dropdown.loading > .remove.icon {
- display: none;
-}
-
-/*--------------
- Multiple
- ---------------*/
-
-/* Multiple Selection */
-
-.ui.ui.multiple.dropdown {
- padding: 0.22619048em 3.2em 0.22619048em 0.35714286em;
-}
-
-.ui.multiple.dropdown .menu {
- cursor: auto;
-}
-
-/* Selection Label */
-
-.ui.multiple.dropdown > .label {
- display: inline-block;
- white-space: normal;
- font-size: 1em;
- padding: 0.35714286em 0.78571429em;
- margin: 0.14285714rem 0.28571429rem 0.14285714rem 0;
- box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
-}
-
-/* Dropdown Icon */
-
-.ui.multiple.dropdown .dropdown.icon {
- margin: '';
- padding: '';
-}
-
-/* Text */
-
-.ui.multiple.dropdown > .text {
- position: static;
- padding: 0;
- max-width: 100%;
- margin: 0.45238095em 0 0.45238095em 0.64285714em;
- line-height: 1.21428571em;
-}
-
-.ui.multiple.dropdown > .text.default {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.ui.multiple.dropdown > .label ~ input.search {
- margin-left: 0.14285714em !important;
-}
-
-.ui.multiple.dropdown > .label ~ .text {
- display: none;
-}
-
-.ui.multiple.dropdown > .label:not(.image) > img:not(.centered) {
- margin-right: 0.78571429rem;
-}
-
-.ui.multiple.dropdown > .label:not(.image) > img.ui:not(.avatar) {
- margin-bottom: 0.39285714rem;
-}
-
-.ui.multiple.dropdown > .image.label img {
- margin: -0.35714286em 0.78571429em -0.35714286em -0.78571429em;
- height: 1.71428571em;
-}
-
-/*-----------------
- Multiple Search
- -----------------*/
-
-/* Multiple Search Selection */
-
-.ui.multiple.search.dropdown,
-.ui.multiple.search.dropdown > input.search {
- cursor: text;
-}
-
-/* Prompt Text */
-
-.ui.multiple.search.dropdown > .text {
- display: inline-block;
- position: absolute;
- top: 0;
- left: 0;
- padding: inherit;
- margin: 0.45238095em 0 0.45238095em 0.64285714em;
- line-height: 1.21428571em;
-}
-
-.ui.multiple.search.dropdown > .label ~ .text {
- display: none;
-}
-
-/* Search */
-
-.ui.multiple.search.dropdown > input.search {
- position: static;
- padding: 0;
- max-width: 100%;
- margin: 0.45238095em 0 0.45238095em 0.64285714em;
- width: 2.2em;
- line-height: 1.21428571em;
-}
-
-.ui.multiple.search.dropdown.button {
- min-width: 14em;
-}
-
-/*--------------
- Inline
- ---------------*/
-
-.ui.inline.dropdown {
- cursor: pointer;
- display: inline-block;
- color: inherit;
-}
-
-.ui.inline.dropdown .dropdown.icon {
- margin: 0 0.21428571em 0 0.21428571em;
- vertical-align: baseline;
-}
-
-.ui.inline.dropdown > .text {
- font-weight: 500;
-}
-
-.ui.inline.dropdown .menu {
- cursor: auto;
- margin-top: 0.21428571em;
- border-radius: 0.28571429rem;
-}
-
-/*******************************
- States
-*******************************/
-
-/*--------------------
- Active
-----------------------*/
-
-/* Menu Item Active */
-
-.ui.dropdown .menu .active.item {
- background: transparent;
- font-weight: 500;
- color: rgba(0, 0, 0, 0.95);
- box-shadow: none;
- z-index: 12;
-}
-
-/*--------------------
- Hover
-----------------------*/
-
-/* Menu Item Hover */
-
-.ui.dropdown .menu > .item:hover {
- background: rgba(0, 0, 0, 0.05);
- color: rgba(0, 0, 0, 0.95);
- z-index: 13;
-}
-
-/*--------------------
- Default Text
-----------------------*/
-
-.ui.dropdown:not(.button) > .default.text,
-.ui.default.dropdown:not(.button) > .text {
- color: rgba(191, 191, 191, 0.87);
-}
-
-.ui.dropdown:not(.button) > input:focus ~ .default.text,
-.ui.default.dropdown:not(.button) > input:focus ~ .text {
- color: rgba(115, 115, 115, 0.87);
-}
-
-/*--------------------
- Loading
- ---------------------*/
-
-.ui.loading.dropdown > i.icon {
- height: 1em !important;
-}
-
-.ui.loading.selection.dropdown > i.icon {
- padding: 1.5em 1.28571429em !important;
-}
-
-.ui.loading.dropdown > i.icon:before {
- position: absolute;
- content: '';
- top: 50%;
- left: 50%;
- margin: -0.64285714em 0 0 -0.64285714em;
- width: 1.28571429em;
- height: 1.28571429em;
- border-radius: 500rem;
- border: 0.2em solid rgba(0, 0, 0, 0.1);
-}
-
-.ui.loading.dropdown > i.icon:after {
- position: absolute;
- content: '';
- top: 50%;
- left: 50%;
- box-shadow: 0 0 0 1px transparent;
- margin: -0.64285714em 0 0 -0.64285714em;
- width: 1.28571429em;
- height: 1.28571429em;
- animation: loader 0.6s infinite linear;
- border: 0.2em solid #767676;
- border-radius: 500rem;
-}
-
-/* Coupling */
-
-.ui.loading.dropdown.button > i.icon:before,
-.ui.loading.dropdown.button > i.icon:after {
- display: none;
-}
-
-.ui.loading.dropdown > .text {
- transition: none;
-}
-
-/* Used To Check Position */
-
-.ui.dropdown .loading.menu {
- display: block;
- visibility: hidden;
- z-index: -1;
-}
-
-.ui.dropdown > .loading.menu {
- left: 0 !important;
- right: auto !important;
-}
-
-.ui.dropdown > .menu .loading.menu {
- left: 100% !important;
- right: auto !important;
-}
-
-/*--------------------
- Keyboard Select
-----------------------*/
-
-/* Selected Item */
-
-.ui.dropdown.selected,
-.ui.dropdown .menu .selected.item {
- background: rgba(0, 0, 0, 0.03);
- color: rgba(0, 0, 0, 0.95);
-}
-
-/*--------------------
- Search Filtered
-----------------------*/
-
-/* Filtered Item */
-
-.ui.dropdown > .filtered.text {
- visibility: hidden;
-}
-
-.ui.dropdown .filtered.item {
- display: none !important;
-}
-
-/*--------------------
- States
- ----------------------*/
-
-.ui.dropdown.error,
-.ui.dropdown.error > .text,
-.ui.dropdown.error > .default.text {
- color: #9F3A38;
-}
-
-.ui.selection.dropdown.error {
- background: #FFF6F6;
- border-color: #E0B4B4;
-}
-
-.ui.selection.dropdown.error:hover {
- border-color: #E0B4B4;
-}
-
-.ui.multiple.selection.error.dropdown > .label {
- border-color: #E0B4B4;
-}
-
-.ui.dropdown.error > .menu,
-.ui.dropdown.error > .menu .menu {
- border-color: #E0B4B4;
-}
-
-.ui.dropdown.error > .menu > .item {
- color: #9F3A38;
-}
-
-/* Item Hover */
-
-.ui.dropdown.error > .menu > .item:hover {
- background-color: #FBE7E7;
-}
-
-/* Item Active */
-
-.ui.dropdown.error > .menu .active.item {
- background-color: #FDCFCF;
-}
-
-.ui.dropdown.info,
-.ui.dropdown.info > .text,
-.ui.dropdown.info > .default.text {
- color: #276F86;
-}
-
-.ui.selection.dropdown.info {
- background: #F8FFFF;
- border-color: #A9D5DE;
-}
-
-.ui.selection.dropdown.info:hover {
- border-color: #A9D5DE;
-}
-
-.ui.multiple.selection.info.dropdown > .label {
- border-color: #A9D5DE;
-}
-
-.ui.dropdown.info > .menu,
-.ui.dropdown.info > .menu .menu {
- border-color: #A9D5DE;
-}
-
-.ui.dropdown.info > .menu > .item {
- color: #276F86;
-}
-
-/* Item Hover */
-
-.ui.dropdown.info > .menu > .item:hover {
- background-color: #e9f2fb;
-}
-
-/* Item Active */
-
-.ui.dropdown.info > .menu .active.item {
- background-color: #cef1fd;
-}
-
-.ui.dropdown.success,
-.ui.dropdown.success > .text,
-.ui.dropdown.success > .default.text {
- color: #2C662D;
-}
-
-.ui.selection.dropdown.success {
- background: #FCFFF5;
- border-color: #A3C293;
-}
-
-.ui.selection.dropdown.success:hover {
- border-color: #A3C293;
-}
-
-.ui.multiple.selection.success.dropdown > .label {
- border-color: #A3C293;
-}
-
-.ui.dropdown.success > .menu,
-.ui.dropdown.success > .menu .menu {
- border-color: #A3C293;
-}
-
-.ui.dropdown.success > .menu > .item {
- color: #2C662D;
-}
-
-/* Item Hover */
-
-.ui.dropdown.success > .menu > .item:hover {
- background-color: #e9fbe9;
-}
-
-/* Item Active */
-
-.ui.dropdown.success > .menu .active.item {
- background-color: #dafdce;
-}
-
-.ui.dropdown.warning,
-.ui.dropdown.warning > .text,
-.ui.dropdown.warning > .default.text {
- color: #573A08;
-}
-
-.ui.selection.dropdown.warning {
- background: #FFFAF3;
- border-color: #C9BA9B;
-}
-
-.ui.selection.dropdown.warning:hover {
- border-color: #C9BA9B;
-}
-
-.ui.multiple.selection.warning.dropdown > .label {
- border-color: #C9BA9B;
-}
-
-.ui.dropdown.warning > .menu,
-.ui.dropdown.warning > .menu .menu {
- border-color: #C9BA9B;
-}
-
-.ui.dropdown.warning > .menu > .item {
- color: #573A08;
-}
-
-/* Item Hover */
-
-.ui.dropdown.warning > .menu > .item:hover {
- background-color: #fbfbe9;
-}
-
-/* Item Active */
-
-.ui.dropdown.warning > .menu .active.item {
- background-color: #fdfdce;
-}
-
-/*--------------------
- Clear
-----------------------*/
-
-.ui.dropdown > .clear.dropdown.icon {
- opacity: 0.8;
- transition: opacity 0.1s ease;
-}
-
-.ui.dropdown > .clear.dropdown.icon:hover {
- opacity: 1;
-}
-
-/*--------------------
- Disabled
- ----------------------*/
-
-/* Disabled */
-
-.ui.disabled.dropdown,
-.ui.dropdown .menu > .disabled.item {
- cursor: default;
- pointer-events: none;
- opacity: var(--opacity-disabled);
-}
-
-/*******************************
- Variations
-*******************************/
-
-/*--------------
- Direction
----------------*/
-
-/* Flyout Direction */
-
-.ui.dropdown .menu {
- left: 0;
-}
-
-/* Default Side (Right) */
-
-.ui.dropdown .right.menu > .menu,
-.ui.dropdown .menu .right.menu {
- left: 100% !important;
- right: auto !important;
- border-radius: 0.28571429rem !important;
-}
-
-/* Leftward Opening Menu */
-
-.ui.dropdown > .left.menu {
- left: auto !important;
- right: 0 !important;
-}
-
-.ui.dropdown > .left.menu .menu,
-.ui.dropdown .menu .left.menu {
- left: auto;
- right: 100%;
- margin: 0 -0.5em 0 0 !important;
- border-radius: 0.28571429rem !important;
-}
-
-.ui.dropdown .item .left.dropdown.icon,
-.ui.dropdown .left.menu .item .dropdown.icon {
- width: auto;
- float: left;
- margin: 0em 0 0 0;
-}
-
-.ui.dropdown .item .left.dropdown.icon,
-.ui.dropdown .left.menu .item .dropdown.icon {
- width: auto;
- float: left;
- margin: 0em 0 0 0;
-}
-
-.ui.dropdown .item .left.dropdown.icon + .text,
-.ui.dropdown .left.menu .item .dropdown.icon + .text {
- margin-left: 1em;
- margin-right: 0;
-}
-
-/*--------------
- Upward
- ---------------*/
-
-/* Upward Main Menu */
-
-.ui.upward.dropdown > .menu {
- top: auto;
- bottom: 100%;
- box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.08);
- border-radius: 0.28571429rem 0.28571429rem 0 0;
-}
-
-/* Upward Sub Menu */
-
-.ui.dropdown .upward.menu {
- top: auto !important;
- bottom: 0 !important;
-}
-
-/* Active Upward */
-
-.ui.simple.upward.active.dropdown,
-.ui.simple.upward.dropdown:hover {
- border-radius: 0.28571429rem 0.28571429rem 0 0 !important;
-}
-
-.ui.upward.dropdown.button:not(.pointing):not(.floating).active {
- border-radius: 0.28571429rem 0.28571429rem 0 0;
-}
-
-/* Selection */
-
-.ui.upward.selection.dropdown .menu {
- border-top-width: 1px !important;
- border-bottom-width: 0 !important;
- box-shadow: 0 -2px 3px 0 rgba(0, 0, 0, 0.08);
-}
-
-.ui.upward.selection.dropdown:hover {
- box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.05);
-}
-
-/* Active Upward */
-
-.ui.active.upward.selection.dropdown {
- border-radius: 0 0 0.28571429rem 0.28571429rem !important;
-}
-
-/* Visible Upward */
-
-.ui.upward.selection.dropdown.visible {
- box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.08);
- border-radius: 0 0 0.28571429rem 0.28571429rem !important;
-}
-
-/* Visible Hover Upward */
-
-.ui.upward.active.selection.dropdown:hover {
- box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.05);
-}
-
-.ui.upward.active.selection.dropdown:hover .menu {
- box-shadow: 0 -2px 3px 0 rgba(0, 0, 0, 0.08);
-}
-
-/*--------------
- Scrolling
- ---------------*/
-
-/* Selection Menu */
-
-.ui.scrolling.dropdown .menu,
-.ui.dropdown .scrolling.menu {
- overflow-x: hidden;
- overflow-y: auto;
-}
-
-.ui.scrolling.dropdown .menu {
- overflow-x: hidden;
- overflow-y: auto;
- backface-visibility: hidden;
- -webkit-overflow-scrolling: touch;
- min-width: 100% !important;
- width: auto !important;
-}
-
-.ui.dropdown .scrolling.menu {
- position: static;
- overflow-y: auto;
- border: none;
- box-shadow: none !important;
- border-radius: 0 !important;
- margin: 0 !important;
- min-width: 100% !important;
- width: auto !important;
- border-top: 1px solid rgba(34, 36, 38, 0.15);
-}
-
-.ui.scrolling.dropdown .menu .item.item.item,
-.ui.dropdown .scrolling.menu > .item.item.item {
- border-top: none;
-}
-
-.ui.scrolling.dropdown .menu .item:first-child,
-.ui.dropdown .scrolling.menu .item:first-child {
- border-top: none;
-}
-
-.ui.dropdown > .animating.menu .scrolling.menu,
-.ui.dropdown > .visible.menu .scrolling.menu {
- display: block;
-}
-
-/* Scrollbar in IE */
-
-@media all and (-ms-high-contrast: none) {
- .ui.scrolling.dropdown .menu,
- .ui.dropdown .scrolling.menu {
- min-width: calc(100% - 17px);
- }
-}
-
-@media only screen and (max-width: 767.98px) {
- .ui.scrolling.dropdown .menu,
- .ui.dropdown .scrolling.menu {
- max-height: 10.28571429rem;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.scrolling.dropdown .menu,
- .ui.dropdown .scrolling.menu {
- max-height: 15.42857143rem;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.scrolling.dropdown .menu,
- .ui.dropdown .scrolling.menu {
- max-height: 20.57142857rem;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.scrolling.dropdown .menu,
- .ui.dropdown .scrolling.menu {
- max-height: 20.57142857rem;
- }
-}
-
-/*--------------
- Columnar
----------------*/
-
-.ui.column.dropdown > .menu {
- flex-wrap: wrap;
-}
-
-.ui.dropdown[class*="two column"] > .menu > .item {
- width: 50%;
-}
-
-.ui.dropdown[class*="three column"] > .menu > .item {
- width: 33%;
-}
-
-.ui.dropdown[class*="four column"] > .menu > .item {
- width: 25%;
-}
-
-.ui.dropdown[class*="five column"] > .menu > .item {
- width: 20%;
-}
-
-/*--------------
- Simple
- ---------------*/
-
-/* Displays without javascript */
-
-.ui.simple.dropdown .menu:before,
-.ui.simple.dropdown .menu:after {
- display: none;
-}
-
-.ui.simple.dropdown .menu {
- position: absolute;
- /* IE hack to make dropdown icons appear inline */
- display: -ms-inline-flexbox !important;
- display: block;
- overflow: hidden;
- top: -9999px;
- opacity: 0;
- width: 0;
- height: 0;
- transition: opacity 0.1s ease;
- margin-top: 0 !important;
-}
-
-.ui.simple.active.dropdown,
-.ui.simple.dropdown:hover {
- border-bottom-left-radius: 0 !important;
- border-bottom-right-radius: 0 !important;
-}
-
-.ui.simple.active.dropdown > .menu,
-.ui.simple.dropdown:hover > .menu {
- overflow: visible;
- width: auto;
- height: auto;
- top: 100%;
- opacity: 1;
-}
-
-.ui.simple.dropdown > .menu > .item:active > .menu,
-.ui.simple.dropdown .menu .item:hover > .menu {
- overflow: visible;
- width: auto;
- height: auto;
- top: 0 !important;
- left: 100%;
- opacity: 1;
-}
-
-.ui.simple.dropdown > .menu > .item:active > .left.menu,
-.ui.simple.dropdown .menu .item:hover > .left.menu,
-.right.menu .ui.simple.dropdown > .menu > .item:active > .menu:not(.right),
-.right.menu .ui.simple.dropdown > .menu .item:hover > .menu:not(.right) {
- left: auto;
- right: 100%;
-}
-
-.ui.simple.disabled.dropdown:hover .menu {
- display: none;
- height: 0;
- width: 0;
- overflow: hidden;
-}
-
-/* Visible */
-
-.ui.simple.visible.dropdown > .menu {
- display: block;
-}
-
-/* Scrolling */
-
-.ui.simple.scrolling.active.dropdown > .menu,
-.ui.simple.scrolling.dropdown:hover > .menu {
- overflow-x: hidden;
- overflow-y: auto;
-}
-
-/*--------------
- Fluid
- ---------------*/
-
-.ui.fluid.dropdown {
- display: block;
- width: 100% !important;
- min-width: 0;
-}
-
-.ui.fluid.dropdown > .dropdown.icon {
- float: right;
-}
-
-/*--------------
- Floating
- ---------------*/
-
-.ui.floating.dropdown .menu {
- left: 0;
- right: auto;
- box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15) !important;
- border-radius: 0.28571429rem !important;
-}
-
-.ui.floating.dropdown > .menu {
- border-radius: 0.28571429rem !important;
-}
-
-.ui:not(.upward).floating.dropdown > .menu {
- margin-top: 0.5em;
-}
-
-.ui.upward.floating.dropdown > .menu {
- margin-bottom: 0.5em;
-}
-
-/*--------------
- Pointing
- ---------------*/
-
-.ui.pointing.dropdown > .menu {
- top: 100%;
- margin-top: 0.78571429rem;
- border-radius: 0.28571429rem;
-}
-
-.ui.pointing.dropdown > .menu:not(.hidden):after {
- display: block;
- position: absolute;
- pointer-events: none;
- content: '';
- visibility: visible;
- transform: rotate(45deg);
- width: 0.5em;
- height: 0.5em;
- box-shadow: -1px -1px 0 0 rgba(34, 36, 38, 0.15);
- background: #FFFFFF;
- z-index: 2;
-}
-
-.ui.pointing.dropdown > .menu:not(.hidden):after {
- top: -0.25em;
- left: 50%;
- margin: 0 0 0 -0.25em;
-}
-
-/* Top Left Pointing */
-
-.ui.top.left.pointing.dropdown > .menu {
- top: 100%;
- bottom: auto;
- left: 0;
- right: auto;
- margin: 1em 0 0;
-}
-
-.ui.top.left.pointing.dropdown > .menu {
- top: 100%;
- bottom: auto;
- left: 0;
- right: auto;
- margin: 1em 0 0;
-}
-
-.ui.top.left.pointing.dropdown > .menu:after {
- top: -0.25em;
- left: 1em;
- right: auto;
- margin: 0;
- transform: rotate(45deg);
-}
-
-/* Top Right Pointing */
-
-.ui.top.right.pointing.dropdown > .menu {
- top: 100%;
- bottom: auto;
- right: 0;
- left: auto;
- margin: 1em 0 0;
-}
-
-.ui.top.pointing.dropdown > .left.menu:after,
-.ui.top.right.pointing.dropdown > .menu:after {
- top: -0.25em;
- left: auto !important;
- right: 1em !important;
- margin: 0;
- transform: rotate(45deg);
-}
-
-/* Left Pointing */
-
-.ui.left.pointing.dropdown > .menu {
- top: 0;
- left: 100%;
- right: auto;
- margin: 0 0 0 1em;
-}
-
-.ui.left.pointing.dropdown > .menu:after {
- top: 1em;
- left: -0.25em;
- margin: 0 0 0 0;
- transform: rotate(-45deg);
-}
-
-.ui.left:not(.top):not(.bottom).pointing.dropdown > .left.menu {
- left: auto !important;
- right: 100% !important;
- margin: 0 1em 0 0;
-}
-
-.ui.left:not(.top):not(.bottom).pointing.dropdown > .left.menu:after {
- top: 1em;
- left: auto;
- right: -0.25em;
- margin: 0 0 0 0;
- transform: rotate(135deg);
-}
-
-/* Right Pointing */
-
-.ui.right.pointing.dropdown > .menu {
- top: 0;
- left: auto;
- right: 100%;
- margin: 0 1em 0 0;
-}
-
-.ui.right.pointing.dropdown > .menu:after {
- top: 1em;
- left: auto;
- right: -0.25em;
- margin: 0 0 0 0;
- transform: rotate(135deg);
-}
-
-/* Bottom Pointing */
-
-.ui.bottom.pointing.dropdown > .menu {
- top: auto;
- bottom: 100%;
- left: 0;
- right: auto;
- margin: 0 0 1em;
-}
-
-.ui.bottom.pointing.dropdown > .menu:after {
- top: auto;
- bottom: -0.25em;
- right: auto;
- margin: 0;
- transform: rotate(-135deg);
-}
-
-/* Reverse Sub-Menu Direction */
-
-.ui.bottom.pointing.dropdown > .menu .menu {
- top: auto !important;
- bottom: 0 !important;
-}
-
-/* Bottom Left */
-
-.ui.bottom.left.pointing.dropdown > .menu {
- left: 0;
- right: auto;
-}
-
-.ui.bottom.left.pointing.dropdown > .menu:after {
- left: 1em;
- right: auto;
-}
-
-/* Bottom Right */
-
-.ui.bottom.right.pointing.dropdown > .menu {
- right: 0;
- left: auto;
-}
-
-.ui.bottom.right.pointing.dropdown > .menu:after {
- left: auto;
- right: 1em;
-}
-
-/* Upward pointing */
-
-.ui.pointing.upward.dropdown .menu,
-.ui.top.pointing.upward.dropdown .menu {
- top: auto !important;
- bottom: 100% !important;
- margin: 0 0 0.78571429rem;
- border-radius: 0.28571429rem;
-}
-
-.ui.pointing.upward.dropdown .menu:after,
-.ui.top.pointing.upward.dropdown .menu:after {
- top: 100% !important;
- bottom: auto !important;
- box-shadow: 1px 1px 0 0 rgba(34, 36, 38, 0.15);
- margin: -0.25em 0 0;
-}
-
-/* Right Pointing Upward */
-
-.ui.right.pointing.upward.dropdown:not(.top):not(.bottom) .menu {
- top: auto !important;
- bottom: 0 !important;
- margin: 0 1em 0 0;
-}
-
-.ui.right.pointing.upward.dropdown:not(.top):not(.bottom) .menu:after {
- top: auto !important;
- bottom: 0 !important;
- margin: 0 0 1em 0;
- box-shadow: -1px -1px 0 0 rgba(34, 36, 38, 0.15);
-}
-
-/* Left Pointing Upward */
-
-.ui.left.pointing.upward.dropdown:not(.top):not(.bottom) .menu {
- top: auto !important;
- bottom: 0 !important;
- margin: 0 0 0 1em;
-}
-
-.ui.left.pointing.upward.dropdown:not(.top):not(.bottom) .menu:after {
- top: auto !important;
- bottom: 0 !important;
- margin: 0 0 1em 0;
- box-shadow: -1px -1px 0 0 rgba(34, 36, 38, 0.15);
-}
-
-/*--------------------
- Sizes
----------------------*/
-
-.ui.dropdown,
-.ui.dropdown .menu > .item {
- font-size: 1rem;
-}
-
-.ui.mini.dropdown,
-.ui.mini.dropdown .menu > .item {
- font-size: 0.78571429rem;
-}
-
-.ui.tiny.dropdown,
-.ui.tiny.dropdown .menu > .item {
- font-size: 0.85714286rem;
-}
-
-.ui.small.dropdown,
-.ui.small.dropdown .menu > .item {
- font-size: 0.92857143rem;
-}
-
-.ui.large.dropdown,
-.ui.large.dropdown .menu > .item {
- font-size: 1.14285714rem;
-}
-
-.ui.big.dropdown,
-.ui.big.dropdown .menu > .item {
- font-size: 1.28571429rem;
-}
-
-.ui.huge.dropdown,
-.ui.huge.dropdown .menu > .item {
- font-size: 1.42857143rem;
-}
-
-.ui.massive.dropdown,
-.ui.massive.dropdown .menu > .item {
- font-size: 1.71428571rem;
-}
-
-/*******************************
- Theme Overrides
-*******************************/
-
-/* Dropdown Carets */
-
-@font-face {
- font-family: 'Dropdown';
- src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMggjB5AAAAC8AAAAYGNtYXAPfuIIAAABHAAAAExnYXNwAAAAEAAAAWgAAAAIZ2x5Zjo82LgAAAFwAAABVGhlYWQAQ88bAAACxAAAADZoaGVhAwcB6QAAAvwAAAAkaG10eAS4ABIAAAMgAAAAIGxvY2EBNgDeAAADQAAAABJtYXhwAAoAFgAAA1QAAAAgbmFtZVcZpu4AAAN0AAABRXBvc3QAAwAAAAAEvAAAACAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADw2gHg/+D/4AHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADgAAAAKAAgAAgACAAEAIPDa//3//wAAAAAAIPDX//3//wAB/+MPLQADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAIABJQElABMAABM0NzY3BTYXFhUUDwEGJwYvASY1AAUGBwEACAUGBoAFCAcGgAUBEgcGBQEBAQcECQYHfwYBAQZ/BwYAAQAAAG4BJQESABMAADc0PwE2MzIfARYVFAcGIyEiJyY1AAWABgcIBYAGBgUI/wAHBgWABwaABQWABgcHBgUFBgcAAAABABIASQC3AW4AEwAANzQ/ATYXNhcWHQEUBwYnBi8BJjUSBoAFCAcFBgYFBwgFgAbbBwZ/BwEBBwQJ/wgEBwEBB38GBgAAAAABAAAASQClAW4AEwAANxE0NzYzMh8BFhUUDwEGIyInJjUABQYHCAWABgaABQgHBgVbAQAIBQYGgAUIBwWABgYFBwAAAAEAAAABAADZuaKOXw889QALAgAAAAAA0ABHWAAAAADQAEdYAAAAAAElAW4AAAAIAAIAAAAAAAAAAQAAAeD/4AAAAgAAAAAAASUAAQAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAABAAAAASUAAAElAAAAtwASALcAAAAAAAAACgAUAB4AQgBkAIgAqgAAAAEAAAAIABQAAQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAOAK4AAQAAAAAAAQAOAAAAAQAAAAAAAgAOAEcAAQAAAAAAAwAOACQAAQAAAAAABAAOAFUAAQAAAAAABQAWAA4AAQAAAAAABgAHADIAAQAAAAAACgA0AGMAAwABBAkAAQAOAAAAAwABBAkAAgAOAEcAAwABBAkAAwAOACQAAwABBAkABAAOAFUAAwABBAkABQAWAA4AAwABBAkABgAOADkAAwABBAkACgA0AGMAaQBjAG8AbQBvAG8AbgBWAGUAcgBzAGkAbwBuACAAMQAuADAAaQBjAG8AbQBvAG8Abmljb21vb24AaQBjAG8AbQBvAG8AbgBSAGUAZwB1AGwAYQByAGkAYwBvAG0AbwBvAG4ARgBvAG4AdAAgAGcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAuAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=) format('truetype'), url(data:application/font-woff;charset=utf-8;base64,d09GRk9UVE8AAAVwAAoAAAAABSgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAAdkAAAHZLDXE/09TLzIAAALQAAAAYAAAAGAIIweQY21hcAAAAzAAAABMAAAATA9+4ghnYXNwAAADfAAAAAgAAAAIAAAAEGhlYWQAAAOEAAAANgAAADYAQ88baGhlYQAAA7wAAAAkAAAAJAMHAelobXR4AAAD4AAAACAAAAAgBLgAEm1heHAAAAQAAAAABgAAAAYACFAAbmFtZQAABAgAAAFFAAABRVcZpu5wb3N0AAAFUAAAACAAAAAgAAMAAAEABAQAAQEBCGljb21vb24AAQIAAQA6+BwC+BsD+BgEHgoAGVP/i4seCgAZU/+LiwwHi2v4lPh0BR0AAACIDx0AAACNER0AAAAJHQAAAdASAAkBAQgPERMWGyAlKmljb21vb25pY29tb29udTB1MXUyMHVGMEQ3dUYwRDh1RjBEOXVGMERBAAACAYkABgAIAgABAAQABwAKAA0AVgCfAOgBL/yUDvyUDvyUDvuUDvtvi/emFYuQjZCOjo+Pj42Qiwj3lIsFkIuQiY6Hj4iNhouGi4aJh4eHCPsU+xQFiIiGiYaLhouHjYeOCPsU9xQFiI+Jj4uQCA77b4v3FBWLkI2Pjo8I9xT3FAWPjo+NkIuQi5CJjogI9xT7FAWPh42Hi4aLhomHh4eIiIaJhosI+5SLBYaLh42HjoiPiY+LkAgO+92d928Vi5CNkI+OCPcU9xQFjo+QjZCLkIuPiY6Hj4iNhouGCIv7lAWLhomHh4iIh4eJhouGi4aNiI8I+xT3FAWHjomPi5AIDvvdi+YVi/eUBYuQjZCOjo+Pj42Qi5CLkImOhwj3FPsUBY+IjYaLhouGiYeHiAj7FPsUBYiHhomGi4aLh42Hj4iOiY+LkAgO+JQU+JQViwwKAAAAAAMCAAGQAAUAAAFMAWYAAABHAUwBZgAAAPUAGQCEAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA8NoB4P/g/+AB4AAgAAAAAQAAAAAAAAAAAAAAIAAAAAAAAgAAAAMAAAAUAAMAAQAAABQABAA4AAAACgAIAAIAAgABACDw2v/9//8AAAAAACDw1//9//8AAf/jDy0AAwABAAAAAAAAAAAAAAABAAH//wAPAAEAAAABAAA5emozXw889QALAgAAAAAA0ABHWAAAAADQAEdYAAAAAAElAW4AAAAIAAIAAAAAAAAAAQAAAeD/4AAAAgAAAAAAASUAAQAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAABAAAAASUAAAElAAAAtwASALcAAAAAUAAACAAAAAAADgCuAAEAAAAAAAEADgAAAAEAAAAAAAIADgBHAAEAAAAAAAMADgAkAAEAAAAAAAQADgBVAAEAAAAAAAUAFgAOAAEAAAAAAAYABwAyAAEAAAAAAAoANABjAAMAAQQJAAEADgAAAAMAAQQJAAIADgBHAAMAAQQJAAMADgAkAAMAAQQJAAQADgBVAAMAAQQJAAUAFgAOAAMAAQQJAAYADgA5AAMAAQQJAAoANABjAGkAYwBvAG0AbwBvAG4AVgBlAHIAcwBpAG8AbgAgADEALgAwAGkAYwBvAG0AbwBvAG5pY29tb29uAGkAYwBvAG0AbwBvAG4AUgBlAGcAdQBsAGEAcgBpAGMAbwBtAG8AbwBuAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('woff');
- font-weight: normal;
- font-style: normal;
-}
-
-.ui.dropdown > .dropdown.icon {
- font-family: 'Dropdown';
- line-height: 1;
- height: 1em;
- width: 1.23em;
- backface-visibility: hidden;
- font-weight: normal;
- font-style: normal;
- text-align: center;
-}
-
-.ui.dropdown > .dropdown.icon {
- width: auto;
-}
-
-.ui.dropdown > .dropdown.icon:before {
- content: '\f0d7';
-}
-
-/* Sub Menu */
-
-.ui.dropdown .menu .item .dropdown.icon:before {
- content: '\f0da' ;
-}
-
-.ui.dropdown .item .left.dropdown.icon:before,
-.ui.dropdown .left.menu .item .dropdown.icon:before {
- content: "\f0d9" ;
-}
-
-/* Vertical Menu Dropdown */
-
-.ui.vertical.menu .dropdown.item > .dropdown.icon:before {
- content: "\f0da" ;
-}
-
-/* Icons for Reference
-.dropdown.down.icon {
- content: "\f0d7";
-}
-.dropdown.up.icon {
- content: "\f0d8";
-}
-.dropdown.left.icon {
- content: "\f0d9";
-}
-.dropdown.icon.icon {
- content: "\f0da";
-}
-*/
-
-/*******************************
- User Overrides
-*******************************/
-/*!
- * # Fomantic-UI - Form
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-/*******************************
- Elements
-*******************************/
-
-/*--------------------
- Form
----------------------*/
-
-.ui.form {
- position: relative;
- max-width: 100%;
-}
-
-/*--------------------
- Content
----------------------*/
-
-.ui.form > p {
- margin: 1em 0;
-}
-
-/*--------------------
- Field
----------------------*/
-
-.ui.form .field {
- clear: both;
- margin: 0 0 1em;
-}
-
-.ui.form .fields .fields,
-.ui.form .field:last-child,
-.ui.form .fields:last-child .field {
- margin-bottom: 0;
-}
-
-.ui.form .fields .field {
- clear: both;
- margin: 0;
-}
-
-/*--------------------
- Labels
----------------------*/
-
-.ui.form .field > label {
- display: block;
- margin: 0 0 0.28571429rem 0;
- color: rgba(0, 0, 0, 0.87);
- font-size: 0.92857143em;
- font-weight: 500;
- text-transform: none;
-}
-
-/*--------------------
- Standard Inputs
----------------------*/
-
-.ui.form textarea,
-.ui.form input:not([type]),
-.ui.form input[type="date"],
-.ui.form input[type="datetime-local"],
-.ui.form input[type="email"],
-.ui.form input[type="number"],
-.ui.form input[type="password"],
-.ui.form input[type="search"],
-.ui.form input[type="tel"],
-.ui.form input[type="time"],
-.ui.form input[type="text"],
-.ui.form input[type="file"],
-.ui.form input[type="url"] {
- width: 100%;
- vertical-align: top;
-}
-
-/* Set max height on unusual input */
-
-.ui.form ::-webkit-datetime-edit,
-.ui.form ::-webkit-inner-spin-button {
- height: 1.21428571em;
-}
-
-.ui.form input:not([type]),
-.ui.form input[type="date"],
-.ui.form input[type="datetime-local"],
-.ui.form input[type="email"],
-.ui.form input[type="number"],
-.ui.form input[type="password"],
-.ui.form input[type="search"],
-.ui.form input[type="tel"],
-.ui.form input[type="time"],
-.ui.form input[type="text"],
-.ui.form input[type="file"],
-.ui.form input[type="url"] {
- font-family: var(--fonts-regular);
- margin: 0;
- outline: none;
- -webkit-appearance: none;
- -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
- line-height: 1.21428571em;
- padding: 0.67857143em 1em;
- font-size: 1em;
- background: #FFFFFF;
- border: 1px solid rgba(34, 36, 38, 0.15);
- color: rgba(0, 0, 0, 0.87);
- border-radius: 0.28571429rem;
- box-shadow: 0 0 0 0 transparent inset;
- transition: color 0.1s ease, border-color 0.1s ease;
-}
-
-/* Text Area */
-
-.ui.input textarea,
-.ui.form textarea {
- margin: 0;
- -webkit-appearance: none;
- -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
- padding: 0.78571429em 1em;
- background: #FFFFFF;
- border: 1px solid rgba(34, 36, 38, 0.15);
- outline: none;
- color: rgba(0, 0, 0, 0.87);
- border-radius: 0.28571429rem;
- box-shadow: 0 0 0 0 transparent inset;
- transition: color 0.1s ease, border-color 0.1s ease;
- font-size: 1em;
- font-family: var(--fonts-regular);
- line-height: 1.2857;
- resize: vertical;
-}
-
-.ui.form textarea:not([rows]) {
- height: 12em;
- min-height: 8em;
- max-height: 24em;
-}
-
-.ui.form textarea,
-.ui.form input[type="checkbox"] {
- vertical-align: top;
-}
-
-/*--------------------
- Checkbox margin
----------------------*/
-
-.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) label + .ui.ui.checkbox {
- margin-top: 0.7em;
-}
-
-.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.checkbox {
- margin-top: 2.41428571em;
-}
-
-.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.toggle.checkbox {
- margin-top: 2.21428571em;
-}
-
-.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.slider.checkbox {
- margin-top: 2.61428571em;
-}
-
-.ui.ui.form .field .fields .field:not(:only-child) .ui.checkbox {
- margin-top: 0.6em;
-}
-
-.ui.ui.form .field .fields .field:not(:only-child) .ui.toggle.checkbox {
- margin-top: 0.5em;
-}
-
-.ui.ui.form .field .fields .field:not(:only-child) .ui.slider.checkbox {
- margin-top: 0.7em;
-}
-
-/*--------------------------
- Input w/ attached Button
----------------------------*/
-
-.ui.form input.attached {
- width: auto;
-}
-
-/*--------------------
- Basic Select
----------------------*/
-
-.ui.form select {
- display: block;
- height: auto;
- width: 100%;
- background: #FFFFFF;
- border: 1px solid rgba(34, 36, 38, 0.15);
- border-radius: 0.28571429rem;
- box-shadow: 0 0 0 0 transparent inset;
- padding: 0.62em 1em;
- color: rgba(0, 0, 0, 0.87);
- transition: color 0.1s ease, border-color 0.1s ease;
-}
-
-/*--------------------
- Dropdown
----------------------*/
-
-/* Block */
-
-.ui.form .field > .selection.dropdown {
- min-width: auto;
- width: 100%;
-}
-
-.ui.form .field > .selection.dropdown > .dropdown.icon {
- float: right;
-}
-
-/* Inline */
-
-.ui.form .inline.fields .field > .selection.dropdown,
-.ui.form .inline.field > .selection.dropdown {
- width: auto;
-}
-
-.ui.form .inline.fields .field > .selection.dropdown > .dropdown.icon,
-.ui.form .inline.field > .selection.dropdown > .dropdown.icon {
- float: none;
-}
-
-/*--------------------
- UI Input
----------------------*/
-
-/* Block */
-
-.ui.form .field .ui.input,
-.ui.form .fields .field .ui.input,
-.ui.form .wide.field .ui.input {
- width: 100%;
-}
-
-/* Inline */
-
-.ui.form .inline.fields .field:not(.wide) .ui.input,
-.ui.form .inline.field:not(.wide) .ui.input {
- width: auto;
- vertical-align: middle;
-}
-
-/* Auto Input */
-
-.ui.form .fields .field .ui.input input,
-.ui.form .field .ui.input input {
- width: auto;
-}
-
-/* Full Width Input */
-
-.ui.form .ten.fields .ui.input input,
-.ui.form .nine.fields .ui.input input,
-.ui.form .eight.fields .ui.input input,
-.ui.form .seven.fields .ui.input input,
-.ui.form .six.fields .ui.input input,
-.ui.form .five.fields .ui.input input,
-.ui.form .four.fields .ui.input input,
-.ui.form .three.fields .ui.input input,
-.ui.form .two.fields .ui.input input,
-.ui.form .wide.field .ui.input input {
- flex: 1 0 auto;
- width: 0;
-}
-
-/*--------------------
- Types of Messages
----------------------*/
-
-.ui.form .error.message,
-.ui.form .error.message:empty {
- display: none;
-}
-
-.ui.form .info.message,
-.ui.form .info.message:empty {
- display: none;
-}
-
-.ui.form .success.message,
-.ui.form .success.message:empty {
- display: none;
-}
-
-.ui.form .warning.message,
-.ui.form .warning.message:empty {
- display: none;
-}
-
-/* Assumptions */
-
-.ui.form .message:first-child {
- margin-top: 0;
-}
-
-/*--------------------
- Validation Prompt
----------------------*/
-
-.ui.form .field .prompt.label {
- white-space: normal;
- background: #FFFFFF !important;
- border: 1px solid #E0B4B4 !important;
- color: #9F3A38 !important;
-}
-
-.ui.form .inline.fields .field .prompt,
-.ui.form .inline.field .prompt {
- vertical-align: top;
- margin: -0.25em 0 -0.5em 0.5em;
-}
-
-.ui.form .inline.fields .field .prompt:before,
-.ui.form .inline.field .prompt:before {
- border-width: 0 0 1px 1px;
- bottom: auto;
- right: auto;
- top: 50%;
- left: 0;
-}
-
-/*******************************
- States
-*******************************/
-
-/*--------------------
- Autofilled
----------------------*/
-
-.ui.form .field.field input:-webkit-autofill {
- box-shadow: 0 0 0 100px #FFFFF0 inset !important;
- border-color: #E5DFA1 !important;
-}
-
-/* Focus */
-
-.ui.form .field.field input:-webkit-autofill:focus {
- box-shadow: 0 0 0 100px #FFFFF0 inset !important;
- border-color: #D5C315 !important;
-}
-
-/*--------------------
- Placeholder
----------------------*/
-
-/* browsers require these rules separate */
-
-.ui.form ::-webkit-input-placeholder {
- color: rgba(191, 191, 191, 0.87);
-}
-
-.ui.form :-ms-input-placeholder {
- color: rgba(191, 191, 191, 0.87) !important;
-}
-
-.ui.form ::-moz-placeholder {
- color: rgba(191, 191, 191, 0.87);
-}
-
-.ui.form :focus::-webkit-input-placeholder {
- color: rgba(115, 115, 115, 0.87);
-}
-
-.ui.form :focus:-ms-input-placeholder {
- color: rgba(115, 115, 115, 0.87) !important;
-}
-
-.ui.form :focus::-moz-placeholder {
- color: rgba(115, 115, 115, 0.87);
-}
-
-/*--------------------
- Focus
----------------------*/
-
-.ui.form input:not([type]):focus,
-.ui.form input[type="date"]:focus,
-.ui.form input[type="datetime-local"]:focus,
-.ui.form input[type="email"]:focus,
-.ui.form input[type="number"]:focus,
-.ui.form input[type="password"]:focus,
-.ui.form input[type="search"]:focus,
-.ui.form input[type="tel"]:focus,
-.ui.form input[type="time"]:focus,
-.ui.form input[type="text"]:focus,
-.ui.form input[type="file"]:focus,
-.ui.form input[type="url"]:focus {
- color: rgba(0, 0, 0, 0.95);
- border-color: #85B7D9;
- border-radius: 0.28571429rem;
- background: #FFFFFF;
- box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.35) inset;
-}
-
-.ui.form .ui.action.input:not([class*="left action"]) input:not([type]):focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="date"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="datetime-local"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="email"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="number"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="password"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="search"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="tel"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="time"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="text"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="file"]:focus,
-.ui.form .ui.action.input:not([class*="left action"]) input[type="url"]:focus {
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
-}
-
-.ui.form .ui[class*="left action"].input input:not([type]),
-.ui.form .ui[class*="left action"].input input[type="date"],
-.ui.form .ui[class*="left action"].input input[type="datetime-local"],
-.ui.form .ui[class*="left action"].input input[type="email"],
-.ui.form .ui[class*="left action"].input input[type="number"],
-.ui.form .ui[class*="left action"].input input[type="password"],
-.ui.form .ui[class*="left action"].input input[type="search"],
-.ui.form .ui[class*="left action"].input input[type="tel"],
-.ui.form .ui[class*="left action"].input input[type="time"],
-.ui.form .ui[class*="left action"].input input[type="text"],
-.ui.form .ui[class*="left action"].input input[type="file"],
-.ui.form .ui[class*="left action"].input input[type="url"] {
- border-bottom-left-radius: 0;
- border-top-left-radius: 0;
-}
-
-.ui.form textarea:focus {
- color: rgba(0, 0, 0, 0.95);
- border-color: #85B7D9;
- border-radius: 0.28571429rem;
- background: #FFFFFF;
- box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.35) inset;
- -webkit-appearance: none;
-}
-
-/*--------------------
- States
- ---------------------*/
-
-/* On Form */
-
-.ui.form.error .error.message:not(:empty) {
- display: block;
-}
-
-.ui.form.error .compact.error.message:not(:empty) {
- display: inline-block;
-}
-
-.ui.form.error .icon.error.message:not(:empty) {
- display: flex;
-}
-
-/* On Field(s) */
-
-.ui.form .fields.error .error.message:not(:empty),
-.ui.form .field.error .error.message:not(:empty) {
- display: block;
-}
-
-.ui.form .fields.error .compact.error.message:not(:empty),
-.ui.form .field.error .compact.error.message:not(:empty) {
- display: inline-block;
-}
-
-.ui.form .fields.error .icon.error.message:not(:empty),
-.ui.form .field.error .icon.error.message:not(:empty) {
- display: flex;
-}
-
-.ui.ui.form .fields.error .field label,
-.ui.ui.form .field.error label,
-.ui.ui.form .fields.error .field .input,
-.ui.ui.form .field.error .input {
- color: #9F3A38;
-}
-
-.ui.form .fields.error .field .corner.label,
-.ui.form .field.error .corner.label {
- border-color: #9F3A38;
- color: #FFFFFF;
-}
-
-.ui.form .fields.error .field textarea,
-.ui.form .fields.error .field select,
-.ui.form .fields.error .field input:not([type]),
-.ui.form .fields.error .field input[type="date"],
-.ui.form .fields.error .field input[type="datetime-local"],
-.ui.form .fields.error .field input[type="email"],
-.ui.form .fields.error .field input[type="number"],
-.ui.form .fields.error .field input[type="password"],
-.ui.form .fields.error .field input[type="search"],
-.ui.form .fields.error .field input[type="tel"],
-.ui.form .fields.error .field input[type="time"],
-.ui.form .fields.error .field input[type="text"],
-.ui.form .fields.error .field input[type="file"],
-.ui.form .fields.error .field input[type="url"],
-.ui.form .field.error textarea,
-.ui.form .field.error select,
-.ui.form .field.error input:not([type]),
-.ui.form .field.error input[type="date"],
-.ui.form .field.error input[type="datetime-local"],
-.ui.form .field.error input[type="email"],
-.ui.form .field.error input[type="number"],
-.ui.form .field.error input[type="password"],
-.ui.form .field.error input[type="search"],
-.ui.form .field.error input[type="tel"],
-.ui.form .field.error input[type="time"],
-.ui.form .field.error input[type="text"],
-.ui.form .field.error input[type="file"],
-.ui.form .field.error input[type="url"] {
- color: #9F3A38;
- background: #FFF6F6;
- border-color: #E0B4B4;
- border-radius: '';
- box-shadow: none;
-}
-
-.ui.form .field.error textarea:focus,
-.ui.form .field.error select:focus,
-.ui.form .field.error input:not([type]):focus,
-.ui.form .field.error input[type="date"]:focus,
-.ui.form .field.error input[type="datetime-local"]:focus,
-.ui.form .field.error input[type="email"]:focus,
-.ui.form .field.error input[type="number"]:focus,
-.ui.form .field.error input[type="password"]:focus,
-.ui.form .field.error input[type="search"]:focus,
-.ui.form .field.error input[type="tel"]:focus,
-.ui.form .field.error input[type="time"]:focus,
-.ui.form .field.error input[type="text"]:focus,
-.ui.form .field.error input[type="file"]:focus,
-.ui.form .field.error input[type="url"]:focus {
- background: #FFF6F6;
- border-color: #E0B4B4;
- color: #9F3A38;
- box-shadow: none;
-}
-
-/* Preserve Native Select Stylings */
-
-.ui.form .field.error select {
- -webkit-appearance: menulist-button;
-}
-
-/*------------------
- Input State
- --------------------*/
-
-/* Transparent */
-
-.ui.form .field.error .transparent.input input,
-.ui.form .field.error .transparent.input textarea,
-.ui.form .field.error input.transparent,
-.ui.form .field.error textarea.transparent {
- background-color: #FFF6F6 !important;
- color: #9F3A38 !important;
-}
-
-/* Autofilled */
-
-.ui.form .error.error input:-webkit-autofill {
- box-shadow: 0 0 0 100px #FFFAF0 inset !important;
- border-color: #E0B4B4 !important;
-}
-
-/* Placeholder */
-
-.ui.form .error ::-webkit-input-placeholder {
- color: #e7bdbc;
-}
-
-.ui.form .error :-ms-input-placeholder {
- color: #e7bdbc !important;
-}
-
-.ui.form .error ::-moz-placeholder {
- color: #e7bdbc;
-}
-
-.ui.form .error :focus::-webkit-input-placeholder {
- color: #da9796;
-}
-
-.ui.form .error :focus:-ms-input-placeholder {
- color: #da9796 !important;
-}
-
-.ui.form .error :focus::-moz-placeholder {
- color: #da9796;
-}
-
-/*------------------
- Dropdown State
- --------------------*/
-
-.ui.form .fields.error .field .ui.dropdown,
-.ui.form .fields.error .field .ui.dropdown .item,
-.ui.form .field.error .ui.dropdown,
-.ui.form .field.error .ui.dropdown .text,
-.ui.form .field.error .ui.dropdown .item {
- background: #FFF6F6;
- color: #9F3A38;
-}
-
-.ui.form .fields.error .field .ui.dropdown,
-.ui.form .field.error .ui.dropdown {
- border-color: #E0B4B4 !important;
-}
-
-.ui.form .fields.error .field .ui.dropdown:hover,
-.ui.form .field.error .ui.dropdown:hover {
- border-color: #E0B4B4 !important;
-}
-
-.ui.form .fields.error .field .ui.dropdown:hover .menu,
-.ui.form .field.error .ui.dropdown:hover .menu {
- border-color: #E0B4B4;
-}
-
-.ui.form .fields.error .field .ui.multiple.selection.dropdown > .label,
-.ui.form .field.error .ui.multiple.selection.dropdown > .label {
- background-color: #EACBCB;
- color: #9F3A38;
-}
-
-/* Hover */
-
-.ui.form .fields.error .field .ui.dropdown .menu .item:hover,
-.ui.form .field.error .ui.dropdown .menu .item:hover {
- background-color: #FBE7E7;
-}
-
-/* Selected */
-
-.ui.form .fields.error .field .ui.dropdown .menu .selected.item,
-.ui.form .field.error .ui.dropdown .menu .selected.item {
- background-color: #FBE7E7;
-}
-
-/* Active */
-
-.ui.form .fields.error .field .ui.dropdown .menu .active.item,
-.ui.form .field.error .ui.dropdown .menu .active.item {
- background-color: #FDCFCF !important;
-}
-
-/*--------------------
- Checkbox State
- ---------------------*/
-
-.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) label,
-.ui.form .field.error .checkbox:not(.toggle):not(.slider) label,
-.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) .box,
-.ui.form .field.error .checkbox:not(.toggle):not(.slider) .box {
- color: #9F3A38;
-}
-
-.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) label:before,
-.ui.form .field.error .checkbox:not(.toggle):not(.slider) label:before,
-.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) .box:before,
-.ui.form .field.error .checkbox:not(.toggle):not(.slider) .box:before {
- background: #FFF6F6;
- border-color: #E0B4B4;
-}
-
-.ui.form .fields.error .field .checkbox label:after,
-.ui.form .field.error .checkbox label:after,
-.ui.form .fields.error .field .checkbox .box:after,
-.ui.form .field.error .checkbox .box:after {
- color: #9F3A38;
-}
-
-/* On Form */
-
-.ui.form.info .info.message:not(:empty) {
- display: block;
-}
-
-.ui.form.info .compact.info.message:not(:empty) {
- display: inline-block;
-}
-
-.ui.form.info .icon.info.message:not(:empty) {
- display: flex;
-}
-
-/* On Field(s) */
-
-.ui.form .fields.info .info.message:not(:empty),
-.ui.form .field.info .info.message:not(:empty) {
- display: block;
-}
-
-.ui.form .fields.info .compact.info.message:not(:empty),
-.ui.form .field.info .compact.info.message:not(:empty) {
- display: inline-block;
-}
-
-.ui.form .fields.info .icon.info.message:not(:empty),
-.ui.form .field.info .icon.info.message:not(:empty) {
- display: flex;
-}
-
-.ui.ui.form .fields.info .field label,
-.ui.ui.form .field.info label,
-.ui.ui.form .fields.info .field .input,
-.ui.ui.form .field.info .input {
- color: #276F86;
-}
-
-.ui.form .fields.info .field .corner.label,
-.ui.form .field.info .corner.label {
- border-color: #276F86;
- color: #FFFFFF;
-}
-
-.ui.form .fields.info .field textarea,
-.ui.form .fields.info .field select,
-.ui.form .fields.info .field input:not([type]),
-.ui.form .fields.info .field input[type="date"],
-.ui.form .fields.info .field input[type="datetime-local"],
-.ui.form .fields.info .field input[type="email"],
-.ui.form .fields.info .field input[type="number"],
-.ui.form .fields.info .field input[type="password"],
-.ui.form .fields.info .field input[type="search"],
-.ui.form .fields.info .field input[type="tel"],
-.ui.form .fields.info .field input[type="time"],
-.ui.form .fields.info .field input[type="text"],
-.ui.form .fields.info .field input[type="file"],
-.ui.form .fields.info .field input[type="url"],
-.ui.form .field.info textarea,
-.ui.form .field.info select,
-.ui.form .field.info input:not([type]),
-.ui.form .field.info input[type="date"],
-.ui.form .field.info input[type="datetime-local"],
-.ui.form .field.info input[type="email"],
-.ui.form .field.info input[type="number"],
-.ui.form .field.info input[type="password"],
-.ui.form .field.info input[type="search"],
-.ui.form .field.info input[type="tel"],
-.ui.form .field.info input[type="time"],
-.ui.form .field.info input[type="text"],
-.ui.form .field.info input[type="file"],
-.ui.form .field.info input[type="url"] {
- color: #276F86;
- background: #F8FFFF;
- border-color: #A9D5DE;
- border-radius: '';
- box-shadow: none;
-}
-
-.ui.form .field.info textarea:focus,
-.ui.form .field.info select:focus,
-.ui.form .field.info input:not([type]):focus,
-.ui.form .field.info input[type="date"]:focus,
-.ui.form .field.info input[type="datetime-local"]:focus,
-.ui.form .field.info input[type="email"]:focus,
-.ui.form .field.info input[type="number"]:focus,
-.ui.form .field.info input[type="password"]:focus,
-.ui.form .field.info input[type="search"]:focus,
-.ui.form .field.info input[type="tel"]:focus,
-.ui.form .field.info input[type="time"]:focus,
-.ui.form .field.info input[type="text"]:focus,
-.ui.form .field.info input[type="file"]:focus,
-.ui.form .field.info input[type="url"]:focus {
- background: #F8FFFF;
- border-color: #A9D5DE;
- color: #276F86;
- box-shadow: none;
-}
-
-/* Preserve Native Select Stylings */
-
-.ui.form .field.info select {
- -webkit-appearance: menulist-button;
-}
-
-/*------------------
- Input State
- --------------------*/
-
-/* Transparent */
-
-.ui.form .field.info .transparent.input input,
-.ui.form .field.info .transparent.input textarea,
-.ui.form .field.info input.transparent,
-.ui.form .field.info textarea.transparent {
- background-color: #F8FFFF !important;
- color: #276F86 !important;
-}
-
-/* Autofilled */
-
-.ui.form .info.info input:-webkit-autofill {
- box-shadow: 0 0 0 100px #F0FAFF inset !important;
- border-color: #b3e0e0 !important;
-}
-
-/* Placeholder */
-
-.ui.form .info ::-webkit-input-placeholder {
- color: #98cfe1;
-}
-
-.ui.form .info :-ms-input-placeholder {
- color: #98cfe1 !important;
-}
-
-.ui.form .info ::-moz-placeholder {
- color: #98cfe1;
-}
-
-.ui.form .info :focus::-webkit-input-placeholder {
- color: #70bdd6;
-}
-
-.ui.form .info :focus:-ms-input-placeholder {
- color: #70bdd6 !important;
-}
-
-.ui.form .info :focus::-moz-placeholder {
- color: #70bdd6;
-}
-
-/*------------------
- Dropdown State
- --------------------*/
-
-.ui.form .fields.info .field .ui.dropdown,
-.ui.form .fields.info .field .ui.dropdown .item,
-.ui.form .field.info .ui.dropdown,
-.ui.form .field.info .ui.dropdown .text,
-.ui.form .field.info .ui.dropdown .item {
- background: #F8FFFF;
- color: #276F86;
-}
-
-.ui.form .fields.info .field .ui.dropdown,
-.ui.form .field.info .ui.dropdown {
- border-color: #A9D5DE !important;
-}
-
-.ui.form .fields.info .field .ui.dropdown:hover,
-.ui.form .field.info .ui.dropdown:hover {
- border-color: #A9D5DE !important;
-}
-
-.ui.form .fields.info .field .ui.dropdown:hover .menu,
-.ui.form .field.info .ui.dropdown:hover .menu {
- border-color: #A9D5DE;
-}
-
-.ui.form .fields.info .field .ui.multiple.selection.dropdown > .label,
-.ui.form .field.info .ui.multiple.selection.dropdown > .label {
- background-color: #cce3ea;
- color: #276F86;
-}
-
-/* Hover */
-
-.ui.form .fields.info .field .ui.dropdown .menu .item:hover,
-.ui.form .field.info .ui.dropdown .menu .item:hover {
- background-color: #e9f2fb;
-}
-
-/* Selected */
-
-.ui.form .fields.info .field .ui.dropdown .menu .selected.item,
-.ui.form .field.info .ui.dropdown .menu .selected.item {
- background-color: #e9f2fb;
-}
-
-/* Active */
-
-.ui.form .fields.info .field .ui.dropdown .menu .active.item,
-.ui.form .field.info .ui.dropdown .menu .active.item {
- background-color: #cef1fd !important;
-}
-
-/*--------------------
- Checkbox State
- ---------------------*/
-
-.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) label,
-.ui.form .field.info .checkbox:not(.toggle):not(.slider) label,
-.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) .box,
-.ui.form .field.info .checkbox:not(.toggle):not(.slider) .box {
- color: #276F86;
-}
-
-.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) label:before,
-.ui.form .field.info .checkbox:not(.toggle):not(.slider) label:before,
-.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) .box:before,
-.ui.form .field.info .checkbox:not(.toggle):not(.slider) .box:before {
- background: #F8FFFF;
- border-color: #A9D5DE;
-}
-
-.ui.form .fields.info .field .checkbox label:after,
-.ui.form .field.info .checkbox label:after,
-.ui.form .fields.info .field .checkbox .box:after,
-.ui.form .field.info .checkbox .box:after {
- color: #276F86;
-}
-
-/* On Form */
-
-.ui.form.success .success.message:not(:empty) {
- display: block;
-}
-
-.ui.form.success .compact.success.message:not(:empty) {
- display: inline-block;
-}
-
-.ui.form.success .icon.success.message:not(:empty) {
- display: flex;
-}
-
-/* On Field(s) */
-
-.ui.form .fields.success .success.message:not(:empty),
-.ui.form .field.success .success.message:not(:empty) {
- display: block;
-}
-
-.ui.form .fields.success .compact.success.message:not(:empty),
-.ui.form .field.success .compact.success.message:not(:empty) {
- display: inline-block;
-}
-
-.ui.form .fields.success .icon.success.message:not(:empty),
-.ui.form .field.success .icon.success.message:not(:empty) {
- display: flex;
-}
-
-.ui.ui.form .fields.success .field label,
-.ui.ui.form .field.success label,
-.ui.ui.form .fields.success .field .input,
-.ui.ui.form .field.success .input {
- color: #2C662D;
-}
-
-.ui.form .fields.success .field .corner.label,
-.ui.form .field.success .corner.label {
- border-color: #2C662D;
- color: #FFFFFF;
-}
-
-.ui.form .fields.success .field textarea,
-.ui.form .fields.success .field select,
-.ui.form .fields.success .field input:not([type]),
-.ui.form .fields.success .field input[type="date"],
-.ui.form .fields.success .field input[type="datetime-local"],
-.ui.form .fields.success .field input[type="email"],
-.ui.form .fields.success .field input[type="number"],
-.ui.form .fields.success .field input[type="password"],
-.ui.form .fields.success .field input[type="search"],
-.ui.form .fields.success .field input[type="tel"],
-.ui.form .fields.success .field input[type="time"],
-.ui.form .fields.success .field input[type="text"],
-.ui.form .fields.success .field input[type="file"],
-.ui.form .fields.success .field input[type="url"],
-.ui.form .field.success textarea,
-.ui.form .field.success select,
-.ui.form .field.success input:not([type]),
-.ui.form .field.success input[type="date"],
-.ui.form .field.success input[type="datetime-local"],
-.ui.form .field.success input[type="email"],
-.ui.form .field.success input[type="number"],
-.ui.form .field.success input[type="password"],
-.ui.form .field.success input[type="search"],
-.ui.form .field.success input[type="tel"],
-.ui.form .field.success input[type="time"],
-.ui.form .field.success input[type="text"],
-.ui.form .field.success input[type="file"],
-.ui.form .field.success input[type="url"] {
- color: #2C662D;
- background: #FCFFF5;
- border-color: #A3C293;
- border-radius: '';
- box-shadow: none;
-}
-
-.ui.form .field.success textarea:focus,
-.ui.form .field.success select:focus,
-.ui.form .field.success input:not([type]):focus,
-.ui.form .field.success input[type="date"]:focus,
-.ui.form .field.success input[type="datetime-local"]:focus,
-.ui.form .field.success input[type="email"]:focus,
-.ui.form .field.success input[type="number"]:focus,
-.ui.form .field.success input[type="password"]:focus,
-.ui.form .field.success input[type="search"]:focus,
-.ui.form .field.success input[type="tel"]:focus,
-.ui.form .field.success input[type="time"]:focus,
-.ui.form .field.success input[type="text"]:focus,
-.ui.form .field.success input[type="file"]:focus,
-.ui.form .field.success input[type="url"]:focus {
- background: #FCFFF5;
- border-color: #A3C293;
- color: #2C662D;
- box-shadow: none;
-}
-
-/* Preserve Native Select Stylings */
-
-.ui.form .field.success select {
- -webkit-appearance: menulist-button;
-}
-
-/*------------------
- Input State
- --------------------*/
-
-/* Transparent */
-
-.ui.form .field.success .transparent.input input,
-.ui.form .field.success .transparent.input textarea,
-.ui.form .field.success input.transparent,
-.ui.form .field.success textarea.transparent {
- background-color: #FCFFF5 !important;
- color: #2C662D !important;
-}
-
-/* Autofilled */
-
-.ui.form .success.success input:-webkit-autofill {
- box-shadow: 0 0 0 100px #F0FFF0 inset !important;
- border-color: #bee0b3 !important;
-}
-
-/* Placeholder */
-
-.ui.form .success ::-webkit-input-placeholder {
- color: #8fcf90;
-}
-
-.ui.form .success :-ms-input-placeholder {
- color: #8fcf90 !important;
-}
-
-.ui.form .success ::-moz-placeholder {
- color: #8fcf90;
-}
-
-.ui.form .success :focus::-webkit-input-placeholder {
- color: #6cbf6d;
-}
-
-.ui.form .success :focus:-ms-input-placeholder {
- color: #6cbf6d !important;
-}
-
-.ui.form .success :focus::-moz-placeholder {
- color: #6cbf6d;
-}
-
-/*------------------
- Dropdown State
- --------------------*/
-
-.ui.form .fields.success .field .ui.dropdown,
-.ui.form .fields.success .field .ui.dropdown .item,
-.ui.form .field.success .ui.dropdown,
-.ui.form .field.success .ui.dropdown .text,
-.ui.form .field.success .ui.dropdown .item {
- background: #FCFFF5;
- color: #2C662D;
-}
-
-.ui.form .fields.success .field .ui.dropdown,
-.ui.form .field.success .ui.dropdown {
- border-color: #A3C293 !important;
-}
-
-.ui.form .fields.success .field .ui.dropdown:hover,
-.ui.form .field.success .ui.dropdown:hover {
- border-color: #A3C293 !important;
-}
-
-.ui.form .fields.success .field .ui.dropdown:hover .menu,
-.ui.form .field.success .ui.dropdown:hover .menu {
- border-color: #A3C293;
-}
-
-.ui.form .fields.success .field .ui.multiple.selection.dropdown > .label,
-.ui.form .field.success .ui.multiple.selection.dropdown > .label {
- background-color: #cceacc;
- color: #2C662D;
-}
-
-/* Hover */
-
-.ui.form .fields.success .field .ui.dropdown .menu .item:hover,
-.ui.form .field.success .ui.dropdown .menu .item:hover {
- background-color: #e9fbe9;
-}
-
-/* Selected */
-
-.ui.form .fields.success .field .ui.dropdown .menu .selected.item,
-.ui.form .field.success .ui.dropdown .menu .selected.item {
- background-color: #e9fbe9;
-}
-
-/* Active */
-
-.ui.form .fields.success .field .ui.dropdown .menu .active.item,
-.ui.form .field.success .ui.dropdown .menu .active.item {
- background-color: #dafdce !important;
-}
-
-/*--------------------
- Checkbox State
- ---------------------*/
-
-.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) label,
-.ui.form .field.success .checkbox:not(.toggle):not(.slider) label,
-.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) .box,
-.ui.form .field.success .checkbox:not(.toggle):not(.slider) .box {
- color: #2C662D;
-}
-
-.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) label:before,
-.ui.form .field.success .checkbox:not(.toggle):not(.slider) label:before,
-.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) .box:before,
-.ui.form .field.success .checkbox:not(.toggle):not(.slider) .box:before {
- background: #FCFFF5;
- border-color: #A3C293;
-}
-
-.ui.form .fields.success .field .checkbox label:after,
-.ui.form .field.success .checkbox label:after,
-.ui.form .fields.success .field .checkbox .box:after,
-.ui.form .field.success .checkbox .box:after {
- color: #2C662D;
-}
-
-/* On Form */
-
-.ui.form.warning .warning.message:not(:empty) {
- display: block;
-}
-
-.ui.form.warning .compact.warning.message:not(:empty) {
- display: inline-block;
-}
-
-.ui.form.warning .icon.warning.message:not(:empty) {
- display: flex;
-}
-
-/* On Field(s) */
-
-.ui.form .fields.warning .warning.message:not(:empty),
-.ui.form .field.warning .warning.message:not(:empty) {
- display: block;
-}
-
-.ui.form .fields.warning .compact.warning.message:not(:empty),
-.ui.form .field.warning .compact.warning.message:not(:empty) {
- display: inline-block;
-}
-
-.ui.form .fields.warning .icon.warning.message:not(:empty),
-.ui.form .field.warning .icon.warning.message:not(:empty) {
- display: flex;
-}
-
-.ui.ui.form .fields.warning .field label,
-.ui.ui.form .field.warning label,
-.ui.ui.form .fields.warning .field .input,
-.ui.ui.form .field.warning .input {
- color: #573A08;
-}
-
-.ui.form .fields.warning .field .corner.label,
-.ui.form .field.warning .corner.label {
- border-color: #573A08;
- color: #FFFFFF;
-}
-
-.ui.form .fields.warning .field textarea,
-.ui.form .fields.warning .field select,
-.ui.form .fields.warning .field input:not([type]),
-.ui.form .fields.warning .field input[type="date"],
-.ui.form .fields.warning .field input[type="datetime-local"],
-.ui.form .fields.warning .field input[type="email"],
-.ui.form .fields.warning .field input[type="number"],
-.ui.form .fields.warning .field input[type="password"],
-.ui.form .fields.warning .field input[type="search"],
-.ui.form .fields.warning .field input[type="tel"],
-.ui.form .fields.warning .field input[type="time"],
-.ui.form .fields.warning .field input[type="text"],
-.ui.form .fields.warning .field input[type="file"],
-.ui.form .fields.warning .field input[type="url"],
-.ui.form .field.warning textarea,
-.ui.form .field.warning select,
-.ui.form .field.warning input:not([type]),
-.ui.form .field.warning input[type="date"],
-.ui.form .field.warning input[type="datetime-local"],
-.ui.form .field.warning input[type="email"],
-.ui.form .field.warning input[type="number"],
-.ui.form .field.warning input[type="password"],
-.ui.form .field.warning input[type="search"],
-.ui.form .field.warning input[type="tel"],
-.ui.form .field.warning input[type="time"],
-.ui.form .field.warning input[type="text"],
-.ui.form .field.warning input[type="file"],
-.ui.form .field.warning input[type="url"] {
- color: #573A08;
- background: #FFFAF3;
- border-color: #C9BA9B;
- border-radius: '';
- box-shadow: none;
-}
-
-.ui.form .field.warning textarea:focus,
-.ui.form .field.warning select:focus,
-.ui.form .field.warning input:not([type]):focus,
-.ui.form .field.warning input[type="date"]:focus,
-.ui.form .field.warning input[type="datetime-local"]:focus,
-.ui.form .field.warning input[type="email"]:focus,
-.ui.form .field.warning input[type="number"]:focus,
-.ui.form .field.warning input[type="password"]:focus,
-.ui.form .field.warning input[type="search"]:focus,
-.ui.form .field.warning input[type="tel"]:focus,
-.ui.form .field.warning input[type="time"]:focus,
-.ui.form .field.warning input[type="text"]:focus,
-.ui.form .field.warning input[type="file"]:focus,
-.ui.form .field.warning input[type="url"]:focus {
- background: #FFFAF3;
- border-color: #C9BA9B;
- color: #573A08;
- box-shadow: none;
-}
-
-/* Preserve Native Select Stylings */
-
-.ui.form .field.warning select {
- -webkit-appearance: menulist-button;
-}
-
-/*------------------
- Input State
- --------------------*/
-
-/* Transparent */
-
-.ui.form .field.warning .transparent.input input,
-.ui.form .field.warning .transparent.input textarea,
-.ui.form .field.warning input.transparent,
-.ui.form .field.warning textarea.transparent {
- background-color: #FFFAF3 !important;
- color: #573A08 !important;
-}
-
-/* Autofilled */
-
-.ui.form .warning.warning input:-webkit-autofill {
- box-shadow: 0 0 0 100px #FFFFe0 inset !important;
- border-color: #e0e0b3 !important;
-}
-
-/* Placeholder */
-
-.ui.form .warning ::-webkit-input-placeholder {
- color: #edad3e;
-}
-
-.ui.form .warning :-ms-input-placeholder {
- color: #edad3e !important;
-}
-
-.ui.form .warning ::-moz-placeholder {
- color: #edad3e;
-}
-
-.ui.form .warning :focus::-webkit-input-placeholder {
- color: #e39715;
-}
-
-.ui.form .warning :focus:-ms-input-placeholder {
- color: #e39715 !important;
-}
-
-.ui.form .warning :focus::-moz-placeholder {
- color: #e39715;
-}
-
-/*------------------
- Dropdown State
- --------------------*/
-
-.ui.form .fields.warning .field .ui.dropdown,
-.ui.form .fields.warning .field .ui.dropdown .item,
-.ui.form .field.warning .ui.dropdown,
-.ui.form .field.warning .ui.dropdown .text,
-.ui.form .field.warning .ui.dropdown .item {
- background: #FFFAF3;
- color: #573A08;
-}
-
-.ui.form .fields.warning .field .ui.dropdown,
-.ui.form .field.warning .ui.dropdown {
- border-color: #C9BA9B !important;
-}
-
-.ui.form .fields.warning .field .ui.dropdown:hover,
-.ui.form .field.warning .ui.dropdown:hover {
- border-color: #C9BA9B !important;
-}
-
-.ui.form .fields.warning .field .ui.dropdown:hover .menu,
-.ui.form .field.warning .ui.dropdown:hover .menu {
- border-color: #C9BA9B;
-}
-
-.ui.form .fields.warning .field .ui.multiple.selection.dropdown > .label,
-.ui.form .field.warning .ui.multiple.selection.dropdown > .label {
- background-color: #eaeacc;
- color: #573A08;
-}
-
-/* Hover */
-
-.ui.form .fields.warning .field .ui.dropdown .menu .item:hover,
-.ui.form .field.warning .ui.dropdown .menu .item:hover {
- background-color: #fbfbe9;
-}
-
-/* Selected */
-
-.ui.form .fields.warning .field .ui.dropdown .menu .selected.item,
-.ui.form .field.warning .ui.dropdown .menu .selected.item {
- background-color: #fbfbe9;
-}
-
-/* Active */
-
-.ui.form .fields.warning .field .ui.dropdown .menu .active.item,
-.ui.form .field.warning .ui.dropdown .menu .active.item {
- background-color: #fdfdce !important;
-}
-
-/*--------------------
- Checkbox State
- ---------------------*/
-
-.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) label,
-.ui.form .field.warning .checkbox:not(.toggle):not(.slider) label,
-.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) .box,
-.ui.form .field.warning .checkbox:not(.toggle):not(.slider) .box {
- color: #573A08;
-}
-
-.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) label:before,
-.ui.form .field.warning .checkbox:not(.toggle):not(.slider) label:before,
-.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) .box:before,
-.ui.form .field.warning .checkbox:not(.toggle):not(.slider) .box:before {
- background: #FFFAF3;
- border-color: #C9BA9B;
-}
-
-.ui.form .fields.warning .field .checkbox label:after,
-.ui.form .field.warning .checkbox label:after,
-.ui.form .fields.warning .field .checkbox .box:after,
-.ui.form .field.warning .checkbox .box:after {
- color: #573A08;
-}
-
-/*--------------------
- Disabled
- ---------------------*/
-
-.ui.form .disabled.fields .field,
-.ui.form .disabled.field,
-.ui.form .field :disabled {
- pointer-events: none;
- opacity: var(--opacity-disabled);
-}
-
-.ui.form .field.disabled > label,
-.ui.form .fields.disabled > label {
- opacity: var(--opacity-disabled);
-}
-
-.ui.form .field.disabled :disabled {
- opacity: 1;
-}
-
-/*--------------
- Loading
- ---------------*/
-
-.ui.loading.form {
- position: relative;
- cursor: default;
- pointer-events: none;
-}
-
-.ui.loading.form:before {
- position: absolute;
- content: '';
- top: 0;
- left: 0;
- background: rgba(255, 255, 255, 0.8);
- width: 100%;
- height: 100%;
- z-index: 100;
-}
-
-.ui.loading.form.segments:before {
- border-radius: 0.28571429rem;
-}
-
-.ui.loading.form:after {
- position: absolute;
- content: '';
- top: 50%;
- left: 50%;
- margin: -1.5em 0 0 -1.5em;
- width: 3em;
- height: 3em;
- animation: loader 0.6s infinite linear;
- border: 0.2em solid #767676;
- border-radius: 500rem;
- box-shadow: 0 0 0 1px transparent;
- visibility: visible;
- z-index: 101;
-}
-
-/*******************************
- Element Types
-*******************************/
-
-/*--------------------
- Required Field
- ---------------------*/
-
-.ui.form .required.fields:not(.grouped) > .field > label:after,
-.ui.form .required.fields.grouped > label:after,
-.ui.form .required.field > label:after,
-.ui.form .required.fields:not(.grouped) > .field > .checkbox:after,
-.ui.form .required.field > .checkbox:after,
-.ui.form label.required:after {
- margin: -0.2em 0 0 0.2em;
- content: '*';
- color: #DB2828;
-}
-
-.ui.form .required.fields:not(.grouped) > .field > label:after,
-.ui.form .required.fields.grouped > label:after,
-.ui.form .required.field > label:after,
-.ui.form label.required:after {
- display: inline-block;
- vertical-align: top;
-}
-
-.ui.form .required.fields:not(.grouped) > .field > .checkbox:after,
-.ui.form .required.field > .checkbox:after {
- position: absolute;
- top: 0;
- left: 100%;
-}
-
-/*******************************
- Variations
-*******************************/
-
-/*--------------------
- Field Groups
- ---------------------*/
-
-/* Grouped Vertically */
-
-.ui.form .grouped.fields {
- display: block;
- margin: 0 0 1em;
-}
-
-.ui.form .grouped.fields:last-child {
- margin-bottom: 0;
-}
-
-.ui.form .grouped.fields > label {
- margin: 0 0 0.28571429rem 0;
- color: rgba(0, 0, 0, 0.87);
- font-size: 0.92857143em;
- font-weight: 500;
- text-transform: none;
-}
-
-.ui.form .grouped.fields .field,
-.ui.form .grouped.inline.fields .field {
- display: block;
- margin: 0.5em 0;
- padding: 0;
-}
-
-.ui.form .grouped.inline.fields .ui.checkbox {
- margin-bottom: 0.4em;
-}
-
-/*--------------------
- Fields
----------------------*/
-
-/* Split fields */
-
-.ui.form .fields {
- display: flex;
- flex-direction: row;
- margin: 0 -0.5em 1em;
-}
-
-.ui.form .fields > .field {
- flex: 0 1 auto;
- padding-left: 0.5em;
- padding-right: 0.5em;
-}
-
-.ui.form .fields > .field:first-child {
- border-left: none;
- box-shadow: none;
-}
-
-/* Other Combinations */
-
-.ui.form .two.fields > .fields,
-.ui.form .two.fields > .field {
- width: 50%;
-}
-
-.ui.form .three.fields > .fields,
-.ui.form .three.fields > .field {
- width: 33.33333333%;
-}
-
-.ui.form .four.fields > .fields,
-.ui.form .four.fields > .field {
- width: 25%;
-}
-
-.ui.form .five.fields > .fields,
-.ui.form .five.fields > .field {
- width: 20%;
-}
-
-.ui.form .six.fields > .fields,
-.ui.form .six.fields > .field {
- width: 16.66666667%;
-}
-
-.ui.form .seven.fields > .fields,
-.ui.form .seven.fields > .field {
- width: 14.28571429%;
-}
-
-.ui.form .eight.fields > .fields,
-.ui.form .eight.fields > .field {
- width: 12.5%;
-}
-
-.ui.form .nine.fields > .fields,
-.ui.form .nine.fields > .field {
- width: 11.11111111%;
-}
-
-.ui.form .ten.fields > .fields,
-.ui.form .ten.fields > .field {
- width: 10%;
-}
-
-/* Swap to full width on mobile */
-
-@media only screen and (max-width: 767.98px) {
- .ui.form .fields {
- flex-wrap: wrap;
- margin-bottom: 0;
- }
-
- .ui.form:not(.unstackable) .fields:not(.unstackable) > .fields,
- .ui.form:not(.unstackable) .fields:not(.unstackable) > .field {
- width: 100%;
- margin: 0 0 1em;
- }
-}
-
-/* Sizing Combinations */
-
-.ui.form .fields .wide.field {
- width: 6.25%;
- padding-left: 0.5em;
- padding-right: 0.5em;
-}
-
-.ui.form .one.wide.field {
- width: 6.25%;
-}
-
-.ui.form .two.wide.field {
- width: 12.5%;
-}
-
-.ui.form .three.wide.field {
- width: 18.75%;
-}
-
-.ui.form .four.wide.field {
- width: 25%;
-}
-
-.ui.form .five.wide.field {
- width: 31.25%;
-}
-
-.ui.form .six.wide.field {
- width: 37.5%;
-}
-
-.ui.form .seven.wide.field {
- width: 43.75%;
-}
-
-.ui.form .eight.wide.field {
- width: 50%;
-}
-
-.ui.form .nine.wide.field {
- width: 56.25%;
-}
-
-.ui.form .ten.wide.field {
- width: 62.5%;
-}
-
-.ui.form .eleven.wide.field {
- width: 68.75%;
-}
-
-.ui.form .twelve.wide.field {
- width: 75%;
-}
-
-.ui.form .thirteen.wide.field {
- width: 81.25%;
-}
-
-.ui.form .fourteen.wide.field {
- width: 87.5%;
-}
-
-.ui.form .fifteen.wide.field {
- width: 93.75%;
-}
-
-.ui.form .sixteen.wide.field {
- width: 100%;
-}
-
-/*--------------------
- Equal Width
----------------------*/
-
-.ui[class*="equal width"].form .fields > .field,
-.ui.form [class*="equal width"].fields > .field {
- width: 100%;
- flex: 1 1 auto;
-}
-
-/*--------------------
- Inline Fields
- ---------------------*/
-
-.ui.form .inline.fields {
- margin: 0 0 1em;
- align-items: center;
-}
-
-.ui.form .inline.fields .field {
- margin: 0;
- padding: 0 1em 0 0;
-}
-
-/* Inline Label */
-
-.ui.form .inline.fields > label,
-.ui.form .inline.fields .field > label,
-.ui.form .inline.fields .field > p,
-.ui.form .inline.field > label,
-.ui.form .inline.field > p {
- display: inline-block;
- width: auto;
- margin-top: 0;
- margin-bottom: 0;
- vertical-align: baseline;
- font-size: 0.92857143em;
- font-weight: 500;
- color: rgba(0, 0, 0, 0.87);
- text-transform: none;
-}
-
-/* Grouped Inline Label */
-
-.ui.form .inline.fields > label {
- margin: 0.035714em 1em 0 0;
-}
-
-/* Inline Input */
-
-.ui.form .inline.fields .field > input,
-.ui.form .inline.fields .field > select,
-.ui.form .inline.field > input,
-.ui.form .inline.field > select {
- display: inline-block;
- width: auto;
- margin-top: 0;
- margin-bottom: 0;
- vertical-align: middle;
- font-size: 1em;
-}
-
-.ui.form .inline.fields .field .calendar:not(.popup),
-.ui.form .inline.field .calendar:not(.popup) {
- display: inline-block;
-}
-
-.ui.form .inline.fields .field .calendar:not(.popup) > .input > input,
-.ui.form .inline.field .calendar:not(.popup) > .input > input {
- width: 13.11em;
-}
-
-/* Label */
-
-.ui.form .inline.fields .field > :first-child,
-.ui.form .inline.field > :first-child {
- margin: 0 0.85714286em 0 0;
-}
-
-.ui.form .inline.fields .field > :only-child,
-.ui.form .inline.field > :only-child {
- margin: 0;
-}
-
-/* Wide */
-
-.ui.form .inline.fields .wide.field {
- display: flex;
- align-items: center;
-}
-
-.ui.form .inline.fields .wide.field > input,
-.ui.form .inline.fields .wide.field > select {
- width: 100%;
-}
-
-/*--------------------
- Sizes
----------------------*/
-
-.ui.form,
-.ui.form .field .dropdown,
-.ui.form .field .dropdown .menu > .item {
- font-size: 1rem;
-}
-
-.ui.mini.form,
-.ui.mini.form .field .dropdown,
-.ui.mini.form .field .dropdown .menu > .item {
- font-size: 0.78571429rem;
-}
-
-.ui.tiny.form,
-.ui.tiny.form .field .dropdown,
-.ui.tiny.form .field .dropdown .menu > .item {
- font-size: 0.85714286rem;
-}
-
-.ui.small.form,
-.ui.small.form .field .dropdown,
-.ui.small.form .field .dropdown .menu > .item {
- font-size: 0.92857143rem;
-}
-
-.ui.large.form,
-.ui.large.form .field .dropdown,
-.ui.large.form .field .dropdown .menu > .item {
- font-size: 1.14285714rem;
-}
-
-.ui.big.form,
-.ui.big.form .field .dropdown,
-.ui.big.form .field .dropdown .menu > .item {
- font-size: 1.28571429rem;
-}
-
-.ui.huge.form,
-.ui.huge.form .field .dropdown,
-.ui.huge.form .field .dropdown .menu > .item {
- font-size: 1.42857143rem;
-}
-
-.ui.massive.form,
-.ui.massive.form .field .dropdown,
-.ui.massive.form .field .dropdown .menu > .item {
- font-size: 1.71428571rem;
-}
-
-/*******************************
- Theme Overrides
-*******************************/
-
-/*******************************
- Site Overrides
-*******************************/
-/*!
- * # Fomantic-UI - Modal
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-/*******************************
- Modal
-*******************************/
-
-.ui.modal {
- position: absolute;
- display: none;
- z-index: 1001;
- text-align: left;
- background: #FFFFFF;
- border: none;
- box-shadow: 1px 3px 3px 0 rgba(0, 0, 0, 0.2), 1px 3px 15px 2px rgba(0, 0, 0, 0.2);
- transform-origin: 50% 25%;
- flex: 0 0 auto;
- border-radius: 0.28571429rem;
- -webkit-user-select: text;
- -moz-user-select: text;
- user-select: text;
- will-change: top, left, margin, transform, opacity;
-}
-
-.ui.modal > :first-child:not(.icon):not(.dimmer),
-.ui.modal > i.icon:first-child + *,
-.ui.modal > .dimmer:first-child + *:not(.icon),
-.ui.modal > .dimmer:first-child + i.icon + * {
- border-top-left-radius: 0.28571429rem;
- border-top-right-radius: 0.28571429rem;
-}
-
-.ui.modal > :last-child {
- border-bottom-left-radius: 0.28571429rem;
- border-bottom-right-radius: 0.28571429rem;
-}
-
-.ui.modal > .ui.dimmer {
- border-radius: inherit;
-}
-
-/*******************************
- Content
-*******************************/
-
-/*--------------
- Close
----------------*/
-
-.ui.modal > .close {
- cursor: pointer;
- position: absolute;
- top: -2.5rem;
- right: -2.5rem;
- z-index: 1;
- opacity: 0.8;
- font-size: 1.25em;
- color: #FFFFFF;
- width: 2.25rem;
- height: 2.25rem;
- padding: 0.625rem 0 0 0;
-}
-
-.ui.modal > .close:hover {
- opacity: 1;
-}
-
-/*--------------
- Header
----------------*/
-
-.ui.modal > .header {
- display: block;
- font-family: var(--fonts-regular);
- background: #FFFFFF;
- margin: 0;
- padding: 1.25rem 1.5rem;
- box-shadow: none;
- color: rgba(0, 0, 0, 0.85);
- border-bottom: 1px solid rgba(34, 36, 38, 0.15);
-}
-
-.ui.modal > .header:not(.ui) {
- font-size: 1.42857143rem;
- line-height: 1.28571429em;
- font-weight: 500;
-}
-
-/*--------------
- Content
----------------*/
-
-.ui.modal > .content {
- display: block;
- width: 100%;
- font-size: 1em;
- line-height: 1.4;
- padding: 1.5rem;
- background: #FFFFFF;
-}
-
-.ui.modal > .image.content {
- display: flex;
- flex-direction: row;
-}
-
-/* Image */
-
-.ui.modal > .content > .image {
- display: block;
- flex: 0 1 auto;
- width: '';
- align-self: start;
- max-width: 100%;
-}
-
-.ui.modal > [class*="top aligned"] {
- align-self: start;
-}
-
-.ui.modal > [class*="middle aligned"] {
- align-self: center;
-}
-
-.ui.modal > [class*="stretched"] {
- align-self: stretch;
-}
-
-/* Description */
-
-.ui.modal > .content > .description {
- display: block;
- flex: 1 0 auto;
- min-width: 0;
- align-self: start;
-}
-
-.ui.modal > .content > i.icon + .description,
-.ui.modal > .content > .image + .description {
- flex: 0 1 auto;
- min-width: '';
- width: auto;
- padding-left: 2em;
-}
-
-/*rtl:ignore*/
-
-.ui.modal > .content > .image > i.icon {
- margin: 0;
- opacity: 1;
- width: auto;
- line-height: 1;
- font-size: 8rem;
-}
-
-/*--------------
- Actions
----------------*/
-
-.ui.modal > .actions {
- background: #F9FAFB;
- padding: 1rem 1rem;
- border-top: 1px solid rgba(34, 36, 38, 0.15);
- text-align: right;
-}
-
-.ui.modal .actions > .button:not(.fluid) {
- margin-left: 0.75em;
-}
-
-.ui.basic.modal > .actions {
- border-top: none;
-}
-
-/*-------------------
- Responsive
---------------------*/
-
-/* Modal Width */
-
-@media only screen and (max-width: 767.98px) {
- .ui.modal:not(.fullscreen) {
- width: 95%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.modal:not(.fullscreen) {
- width: 88%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.modal:not(.fullscreen) {
- width: 850px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1200px) {
- .ui.modal:not(.fullscreen) {
- width: 900px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.modal:not(.fullscreen) {
- width: 950px;
- margin: 0 0 0 0;
- }
-}
-
-/* Tablet and Mobile */
-
-@media only screen and (max-width: 991.98px) {
- .ui.modal > .header {
- padding-right: 2.25rem;
- }
-
- .ui.modal > .close {
- top: 1.0535rem;
- right: 1rem;
- color: rgba(0, 0, 0, 0.87);
- }
-}
-
-/* Mobile */
-
-@media only screen and (max-width: 767.98px) {
- .ui.modal > .header {
- padding: 0.75rem 1rem !important;
- padding-right: 2.25rem !important;
- }
-
- .ui.overlay.fullscreen.modal > .content.content.content {
- min-height: calc(100vh - 8.1rem);
- }
-
- .ui.overlay.fullscreen.modal > .scrolling.content.content.content {
- max-height: calc(100vh - 8.1rem);
- }
-
- .ui.modal > .content {
- display: block;
- padding: 1rem !important;
- }
-
- .ui.modal > .close {
- top: 0.5rem !important;
- right: 0.5rem !important;
- }
-
- /*rtl:ignore*/
-
- .ui.modal .image.content {
- flex-direction: column;
- }
-
- .ui.modal > .content > .image {
- display: block;
- max-width: 100%;
- margin: 0 auto !important;
- text-align: center;
- padding: 0 0 1rem !important;
- }
-
- .ui.modal > .content > .image > i.icon {
- font-size: 5rem;
- text-align: center;
- }
-
- /*rtl:ignore*/
-
- .ui.modal > .content > .description {
- display: block;
- width: 100% !important;
- margin: 0 !important;
- padding: 1rem 0 !important;
- box-shadow: none;
- }
-
- /* Let Buttons Stack */
-
- .ui.modal > .actions {
- padding: 1rem 1rem 0rem !important;
- }
-
- .ui.modal .actions > .buttons,
- .ui.modal .actions > .button {
- margin-bottom: 1rem;
- }
-}
-
-/*--------------
- Coupling
----------------*/
-
-.ui.inverted.dimmer > .ui.modal {
- box-shadow: 1px 3px 10px 2px rgba(0, 0, 0, 0.2);
-}
-
-/*******************************
- Types
-*******************************/
-
-.ui.basic.modal {
- background-color: transparent;
- border: none;
- border-radius: 0;
- box-shadow: none !important;
- color: #FFFFFF;
-}
-
-.ui.basic.modal > .header,
-.ui.basic.modal > .content,
-.ui.basic.modal > .actions {
- background-color: transparent;
-}
-
-.ui.basic.modal > .header {
- color: #FFFFFF;
- border-bottom: none;
-}
-
-.ui.basic.modal > .close {
- top: 1rem;
- right: 1.5rem;
- color: #FFFFFF;
-}
-
-.ui.inverted.dimmer > .basic.modal {
- color: rgba(0, 0, 0, 0.87);
-}
-
-.ui.inverted.dimmer > .ui.basic.modal > .header {
- color: rgba(0, 0, 0, 0.85);
-}
-
-/* Resort to margin positioning if legacy */
-
-.ui.legacy.legacy.modal,
-.ui.legacy.legacy.page.dimmer > .ui.modal {
- left: 50% !important;
-}
-
-.ui.legacy.legacy.modal:not(.aligned),
-.ui.legacy.legacy.page.dimmer > .ui.modal:not(.aligned) {
- top: 50%;
-}
-
-.ui.legacy.legacy.page.dimmer > .ui.scrolling.modal:not(.aligned),
-.ui.page.dimmer > .ui.scrolling.legacy.legacy.modal:not(.aligned),
-.ui.top.aligned.legacy.legacy.page.dimmer > .ui.modal:not(.aligned),
-.ui.top.aligned.dimmer > .ui.legacy.legacy.modal:not(.aligned) {
- top: auto;
-}
-
-.ui.legacy.overlay.fullscreen.modal {
- margin-top: -2rem !important;
-}
-
-/*******************************
- States
-*******************************/
-
-.ui.loading.modal {
- display: block;
- visibility: hidden;
- z-index: -1;
-}
-
-.ui.active.modal {
- display: block;
-}
-
-/*******************************
- Variations
-*******************************/
-
-/*--------------
- Aligned
- ---------------*/
-
-.modals.dimmer .ui.top.aligned.modal {
- top: 5vh;
-}
-
-.modals.dimmer .ui.bottom.aligned.modal {
- bottom: 5vh;
-}
-
-@media only screen and (max-width: 767.98px) {
- .modals.dimmer .ui.top.aligned.modal {
- top: 1rem;
- }
-
- .modals.dimmer .ui.bottom.aligned.modal {
- bottom: 1rem;
- }
-}
-
-/*--------------
- Scrolling
- ---------------*/
-
-/* Scrolling Dimmer */
-
-.scrolling.dimmable.dimmed {
- overflow: hidden;
-}
-
-.scrolling.dimmable > .dimmer {
- justify-content: flex-start;
- position: fixed;
-}
-
-.scrolling.dimmable.dimmed > .dimmer {
- overflow: auto;
- -webkit-overflow-scrolling: touch;
-}
-
-.modals.dimmer .ui.scrolling.modal:not(.fullscreen) {
- margin: 2rem auto;
-}
-
-/* Fix for Firefox, Edge, IE11 */
-
-.modals.dimmer .ui.scrolling.modal:not([class*="overlay fullscreen"])::after {
- content: '\00A0';
- position: absolute;
- height: 2rem;
-}
-
-/* Undetached Scrolling */
-
-.scrolling.undetached.dimmable.dimmed {
- overflow: auto;
- -webkit-overflow-scrolling: touch;
-}
-
-.scrolling.undetached.dimmable.dimmed > .dimmer {
- overflow: hidden;
-}
-
-.scrolling.undetached.dimmable .ui.scrolling.modal:not(.fullscreen) {
- position: absolute;
- left: 50%;
-}
-
-/* Scrolling Content */
-
-.ui.modal > .scrolling.content {
- max-height: calc(80vh - 10rem);
- overflow: auto;
-}
-
-.ui.overlay.fullscreen.modal > .content {
- min-height: calc(100vh - 9.1rem);
-}
-
-.ui.overlay.fullscreen.modal > .scrolling.content {
- max-height: calc(100vh - 9.1rem);
-}
-
-/*--------------
- Full Screen
- ---------------*/
-
-.ui.fullscreen.modal {
- width: 95%;
- left: 2.5%;
- margin: 1em auto;
-}
-
-.ui.overlay.fullscreen.modal {
- width: 100%;
- left: 0;
- margin: 0 auto;
- top: 0;
- border-radius: 0;
-}
-
-.ui.modal > .close.inside + .header,
-.ui.fullscreen.modal > .header {
- padding-right: 2.25rem;
-}
-
-.ui.modal > .close.inside,
-.ui.fullscreen.modal > .close {
- top: 1.0535rem;
- right: 1rem;
- color: rgba(0, 0, 0, 0.87);
-}
-
-.ui.basic.fullscreen.modal > .close {
- color: #FFFFFF;
-}
-
-/*--------------
- Size
----------------*/
-
-.ui.modal {
- font-size: 1rem;
-}
-
-.ui.mini.modal > .header:not(.ui) {
- font-size: 1.3em;
-}
-
-@media only screen and (max-width: 767.98px) {
- .ui.mini.modal {
- width: 95%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.mini.modal {
- width: 35.2%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.mini.modal {
- width: 340px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1200px) {
- .ui.mini.modal {
- width: 360px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.mini.modal {
- width: 380px;
- margin: 0 0 0 0;
- }
-}
-
-.ui.tiny.modal > .header:not(.ui) {
- font-size: 1.3em;
-}
-
-@media only screen and (max-width: 767.98px) {
- .ui.tiny.modal {
- width: 95%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.tiny.modal {
- width: 52.8%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.tiny.modal {
- width: 510px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1200px) {
- .ui.tiny.modal {
- width: 540px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.tiny.modal {
- width: 570px;
- margin: 0 0 0 0;
- }
-}
-
-.ui.small.modal > .header:not(.ui) {
- font-size: 1.3em;
-}
-
-@media only screen and (max-width: 767.98px) {
- .ui.small.modal {
- width: 95%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.small.modal {
- width: 70.4%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.small.modal {
- width: 680px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1200px) {
- .ui.small.modal {
- width: 720px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.small.modal {
- width: 760px;
- margin: 0 0 0 0;
- }
-}
-
-.ui.large.modal > .header:not(.ui) {
- font-size: 1.6em;
-}
-
-@media only screen and (max-width: 767.98px) {
- .ui.large.modal {
- width: 95%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.large.modal {
- width: 88%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.large.modal {
- width: 1020px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1200px) {
- .ui.large.modal {
- width: 1080px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.large.modal {
- width: 1140px;
- margin: 0 0 0 0;
- }
-}
-
-.ui.big.modal > .header:not(.ui) {
- font-size: 1.6em;
-}
-
-@media only screen and (max-width: 767.98px) {
- .ui.big.modal {
- width: 95%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.big.modal {
- width: 88%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.big.modal {
- width: 1190px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1200px) {
- .ui.big.modal {
- width: 1260px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.big.modal {
- width: 1330px;
- margin: 0 0 0 0;
- }
-}
-
-.ui.huge.modal > .header:not(.ui) {
- font-size: 1.6em;
-}
-
-@media only screen and (max-width: 767.98px) {
- .ui.huge.modal {
- width: 95%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.huge.modal {
- width: 88%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.huge.modal {
- width: 1360px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1200px) {
- .ui.huge.modal {
- width: 1440px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.huge.modal {
- width: 1520px;
- margin: 0 0 0 0;
- }
-}
-
-.ui.massive.modal > .header:not(.ui) {
- font-size: 1.8em;
-}
-
-@media only screen and (max-width: 767.98px) {
- .ui.massive.modal {
- width: 95%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.massive.modal {
- width: 88%;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.massive.modal {
- width: 1530px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1200px) {
- .ui.massive.modal {
- width: 1620px;
- margin: 0 0 0 0;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.massive.modal {
- width: 1710px;
- margin: 0 0 0 0;
- }
-}
-
-/*******************************
- Theme Overrides
-*******************************/
-
-/*******************************
- Site Overrides
-*******************************/
-/*!
- * # Fomantic-UI - Search
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-/*******************************
- Search
-*******************************/
-
-.ui.search {
- position: relative;
-}
-
-.ui.search > .prompt {
- margin: 0;
- outline: none;
- -webkit-appearance: none;
- -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
- text-shadow: none;
- font-style: normal;
- font-weight: normal;
- line-height: 1.21428571em;
- padding: 0.67857143em 1em;
- font-size: 1em;
- background: #FFFFFF;
- border: 1px solid rgba(34, 36, 38, 0.15);
- color: rgba(0, 0, 0, 0.87);
- box-shadow: 0 0 0 0 transparent inset;
- transition: background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease, border-color 0.1s ease;
-}
-
-.ui.search .prompt {
- border-radius: 500rem;
-}
-
-/*--------------
- Icon
----------------*/
-
-.ui.search .prompt ~ .search.icon {
- cursor: pointer;
-}
-
-/*--------------
- Results
----------------*/
-
-.ui.search > .results {
- display: none;
- position: absolute;
- top: 100%;
- left: 0;
- transform-origin: center top;
- white-space: normal;
- text-align: left;
- text-transform: none;
- background: #FFFFFF;
- margin-top: 0.5em;
- width: 18em;
- border-radius: 0.28571429rem;
- box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15);
- border: 1px solid #D4D4D5;
- z-index: 998;
-}
-
-.ui.search > .results > :first-child {
- border-radius: 0.28571429rem 0.28571429rem 0 0;
-}
-
-.ui.search > .results > :last-child {
- border-radius: 0 0 0.28571429rem 0.28571429rem;
-}
-
-/*--------------
- Result
----------------*/
-
-.ui.search > .results .result {
- cursor: pointer;
- display: block;
- overflow: hidden;
- font-size: 1em;
- padding: 0.85714286em 1.14285714em;
- color: rgba(0, 0, 0, 0.87);
- line-height: 1.33;
- border-bottom: 1px solid rgba(34, 36, 38, 0.1);
-}
-
-.ui.search > .results .result:last-child {
- border-bottom: none !important;
-}
-
-/* Image */
-
-.ui.search > .results .result .image {
- float: right;
- overflow: hidden;
- background: none;
- width: 5em;
- height: 3em;
- border-radius: 0.25em;
-}
-
-.ui.search > .results .result .image img {
- display: block;
- width: auto;
- height: 100%;
-}
-
-/*--------------
- Info
----------------*/
-
-.ui.search > .results .result .image + .content {
- margin: 0 6em 0 0;
-}
-
-.ui.search > .results .result .title {
- margin: -0.14285714em 0 0;
- font-family: var(--fonts-regular);
- font-weight: 500;
- font-size: 1em;
- color: rgba(0, 0, 0, 0.85);
-}
-
-.ui.search > .results .result .description {
- margin-top: 0;
- font-size: 0.92857143em;
- color: rgba(0, 0, 0, 0.4);
-}
-
-.ui.search > .results .result .price {
- float: right;
- color: #21BA45;
-}
-
-/*--------------
- Message
----------------*/
-
-.ui.search > .results > .message {
- padding: 1em 1em;
-}
-
-.ui.search > .results > .message .header {
- font-family: var(--fonts-regular);
- font-size: 1rem;
- font-weight: 500;
- color: rgba(0, 0, 0, 0.87);
-}
-
-.ui.search > .results > .message .description {
- margin-top: 0.25rem;
- font-size: 1em;
- color: rgba(0, 0, 0, 0.87);
-}
-
-/* View All Results */
-
-.ui.search > .results > .action {
- display: block;
- border-top: none;
- background: #F3F4F5;
- padding: 0.92857143em 1em;
- color: rgba(0, 0, 0, 0.87);
- font-weight: 500;
- text-align: center;
-}
-
-/*******************************
- States
-*******************************/
-
-/*--------------------
- Focus
----------------------*/
-
-.ui.search > .prompt:focus {
- border-color: rgba(34, 36, 38, 0.35);
- background: #FFFFFF;
- color: rgba(0, 0, 0, 0.95);
-}
-
-/*--------------------
- Loading
- ---------------------*/
-
-.ui.loading.search .input > i.icon:before {
- position: absolute;
- content: '';
- top: 50%;
- left: 50%;
- margin: -0.64285714em 0 0 -0.64285714em;
- width: 1.28571429em;
- height: 1.28571429em;
- border-radius: 500rem;
- border: 0.2em solid rgba(0, 0, 0, 0.1);
-}
-
-.ui.loading.search .input > i.icon:after {
- position: absolute;
- content: '';
- top: 50%;
- left: 50%;
- margin: -0.64285714em 0 0 -0.64285714em;
- width: 1.28571429em;
- height: 1.28571429em;
- animation: loader 0.6s infinite linear;
- border: 0.2em solid #767676;
- border-radius: 500rem;
- box-shadow: 0 0 0 1px transparent;
-}
-
-/*--------------
- Hover
----------------*/
-
-.ui.search > .results .result:hover,
-.ui.category.search > .results .category .result:hover {
- background: #F9FAFB;
-}
-
-.ui.search .action:hover:not(div) {
- background: #E0E0E0;
-}
-
-/*--------------
- Active
----------------*/
-
-.ui.category.search > .results .category.active {
- background: #F3F4F5;
-}
-
-.ui.category.search > .results .category.active > .name {
- color: rgba(0, 0, 0, 0.87);
-}
-
-.ui.search > .results .result.active,
-.ui.category.search > .results .category .result.active {
- position: relative;
- border-left-color: rgba(34, 36, 38, 0.1);
- background: #F3F4F5;
- box-shadow: none;
-}
-
-.ui.search > .results .result.active .title {
- color: rgba(0, 0, 0, 0.85);
-}
-
-.ui.search > .results .result.active .description {
- color: rgba(0, 0, 0, 0.85);
-}
-
-/*--------------------
- Disabled
- ----------------------*/
-
-/* Disabled */
-
-.ui.disabled.search {
- cursor: default;
- pointer-events: none;
- opacity: var(--opacity-disabled);
-}
-
-/*******************************
- Types
-*******************************/
-
-/*--------------
- Selection
- ---------------*/
-
-.ui.search.selection .prompt {
- border-radius: 0.28571429rem;
-}
-
-/* Remove input */
-
-.ui.search.selection > .icon.input > .remove.icon {
- pointer-events: none;
- position: absolute;
- left: auto;
- opacity: 0;
- color: '';
- top: 0;
- right: 0;
- transition: color 0.1s ease, opacity 0.1s ease;
-}
-
-.ui.search.selection > .icon.input > .active.remove.icon {
- cursor: pointer;
- opacity: 0.8;
- pointer-events: auto;
-}
-
-.ui.search.selection > .icon.input:not([class*="left icon"]) > .icon ~ .remove.icon {
- right: 1.85714em;
-}
-
-.ui.search.selection > .icon.input > .remove.icon:hover {
- opacity: 1;
- color: #DB2828;
-}
-
-/*--------------
- Category
- ---------------*/
-
-.ui.category.search .results {
- width: 28em;
-}
-
-.ui.category.search .results.animating,
-.ui.category.search .results.visible {
- display: table;
-}
-
-/* Category */
-
-.ui.category.search > .results .category {
- display: table-row;
- background: #F3F4F5;
- box-shadow: none;
- transition: background 0.1s ease, border-color 0.1s ease;
-}
-
-/* Last Category */
-
-.ui.category.search > .results .category:last-child {
- border-bottom: none;
-}
-
-/* First / Last */
-
-.ui.category.search > .results .category:first-child .name + .result {
- border-radius: 0 0.28571429rem 0 0;
-}
-
-.ui.category.search > .results .category:last-child .result:last-child {
- border-radius: 0 0 0.28571429rem 0;
-}
-
-/* Category Result Name */
-
-.ui.category.search > .results .category > .name {
- display: table-cell;
- text-overflow: ellipsis;
- width: 100px;
- white-space: nowrap;
- background: transparent;
- font-family: var(--fonts-regular);
- font-size: 1em;
- padding: 0.4em 1em;
- font-weight: 500;
- color: rgba(0, 0, 0, 0.4);
- border-bottom: 1px solid rgba(34, 36, 38, 0.1);
-}
-
-/* Category Result */
-
-.ui.category.search > .results .category .results {
- display: table-cell;
- background: #FFFFFF;
- border-left: 1px solid rgba(34, 36, 38, 0.15);
- border-bottom: 1px solid rgba(34, 36, 38, 0.1);
-}
-
-.ui.category.search > .results .category .result {
- border-bottom: 1px solid rgba(34, 36, 38, 0.1);
- transition: background 0.1s ease, border-color 0.1s ease;
- padding: 0.85714286em 1.14285714em;
-}
-
-/*******************************
- Variations
-*******************************/
-
-/*-------------------
- Scrolling
- --------------------*/
-
-.ui.scrolling.search > .results,
-.ui.search.long > .results,
-.ui.search.short > .results {
- overflow-x: hidden;
- overflow-y: auto;
- backface-visibility: hidden;
- -webkit-overflow-scrolling: touch;
-}
-
-@media only screen and (max-width: 767.98px) {
- .ui.scrolling.search > .results {
- max-height: 12.17714286em;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.scrolling.search > .results {
- max-height: 18.26571429em;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.scrolling.search > .results {
- max-height: 24.35428571em;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.scrolling.search > .results {
- max-height: 36.53142857em;
- }
-}
-
-@media only screen and (max-width: 767.98px) {
- .ui.search.short > .results {
- max-height: 12.17714286em;
- }
-
- .ui.search[class*="very short"] > .results {
- max-height: 9.13285714em;
- }
-
- .ui.search.long > .results {
- max-height: 24.35428571em;
- }
-
- .ui.search[class*="very long"] > .results {
- max-height: 36.53142857em;
- }
-}
-
-@media only screen and (min-width: 768px) {
- .ui.search.short > .results {
- max-height: 18.26571429em;
- }
-
- .ui.search[class*="very short"] > .results {
- max-height: 13.69928571em;
- }
-
- .ui.search.long > .results {
- max-height: 36.53142857em;
- }
-
- .ui.search[class*="very long"] > .results {
- max-height: 54.79714286em;
- }
-}
-
-@media only screen and (min-width: 992px) {
- .ui.search.short > .results {
- max-height: 24.35428571em;
- }
-
- .ui.search[class*="very short"] > .results {
- max-height: 18.26571429em;
- }
-
- .ui.search.long > .results {
- max-height: 48.70857143em;
- }
-
- .ui.search[class*="very long"] > .results {
- max-height: 73.06285714em;
- }
-}
-
-@media only screen and (min-width: 1920px) {
- .ui.search.short > .results {
- max-height: 36.53142857em;
- }
-
- .ui.search[class*="very short"] > .results {
- max-height: 27.39857143em;
- }
-
- .ui.search.long > .results {
- max-height: 73.06285714em;
- }
-
- .ui.search[class*="very long"] > .results {
- max-height: 109.59428571em;
- }
-}
-
-/*-------------------
- Left / Right
- --------------------*/
-
-.ui[class*="left aligned"].search > .results {
- right: auto;
- left: 0;
-}
-
-.ui[class*="right aligned"].search > .results {
- right: 0;
- left: auto;
-}
-
-/*--------------
- Fluid
----------------*/
-
-.ui.fluid.search .results {
- width: 100%;
-}
-
-/*--------------
- Sizes
----------------*/
-
-.ui.search {
- font-size: 1em;
-}
-
-.ui.mini.search {
- font-size: 0.78571429em;
-}
-
-.ui.tiny.search {
- font-size: 0.85714286em;
-}
-
-.ui.small.search {
- font-size: 0.92857143em;
-}
-
-.ui.large.search {
- font-size: 1.14285714em;
-}
-
-.ui.big.search {
- font-size: 1.28571429em;
-}
-
-.ui.huge.search {
- font-size: 1.42857143em;
-}
-
-.ui.massive.search {
- font-size: 1.71428571em;
-}
-
-/*--------------
- Mobile
----------------*/
-
-@media only screen and (max-width: 767.98px) {
- .ui.search .results {
- max-width: calc(100vw - 2rem);
- }
-}
-
-/*******************************
- Theme Overrides
-*******************************/
-
-/*******************************
- Site Overrides
-*******************************/
-/*!
- * # Fomantic-UI - Tab
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-/*******************************
- UI Tabs
-*******************************/
-
-.ui.tab {
- display: none;
-}
-
-/*******************************
- States
-*******************************/
-
-/*--------------------
- Active
----------------------*/
-
-.ui.tab.active,
-.ui.tab.open {
- display: block;
-}
-
-/*--------------------
- Loading
- ---------------------*/
-
-.ui.tab.loading {
- position: relative;
- overflow: hidden;
- display: block;
- min-height: 250px;
-}
-
-.ui.tab.loading * {
- position: relative !important;
- left: -10000px !important;
-}
-
-.ui.tab.loading:before,
-.ui.tab.loading.segment:before {
- position: absolute;
- content: '';
- top: 50%;
- left: 50%;
- margin: -1.25em 0 0 -1.25em;
- width: 2.5em;
- height: 2.5em;
- border-radius: 500rem;
- border: 0.2em solid rgba(0, 0, 0, 0.1);
-}
-
-.ui.tab.loading:after,
-.ui.tab.loading.segment:after {
- position: absolute;
- content: '';
- top: 50%;
- left: 50%;
- margin: -1.25em 0 0 -1.25em;
- width: 2.5em;
- height: 2.5em;
- animation: loader 0.6s infinite linear;
- border: 0.2em solid #767676;
- border-radius: 500rem;
- box-shadow: 0 0 0 1px transparent;
-}
-
-/*******************************
- Tab Overrides
-*******************************/
-
-/*******************************
- User Overrides
-*******************************/ \ No newline at end of file
diff --git a/web_src/fomantic/build/semantic.js b/web_src/fomantic/build/semantic.js
deleted file mode 100644
index 1297216a31..0000000000
--- a/web_src/fomantic/build/semantic.js
+++ /dev/null
@@ -1,11238 +0,0 @@
- /*
- * # Fomantic UI - 2.8.7
- * https://github.com/fomantic/Fomantic-UI
- * http://fomantic-ui.com/
- *
- * Copyright 2014 Contributors
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-/*!
- * # Fomantic-UI - API
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-;(function ($, window, document, undefined) {
-
-'use strict';
-
-$.isWindow = $.isWindow || function(obj) {
- return obj != null && obj === obj.window;
-};
-
- window = (typeof window != 'undefined' && window.Math == Math)
- ? window
- : (typeof self != 'undefined' && self.Math == Math)
- ? self
- : Function('return this')()
-;
-
-$.api = $.fn.api = function(parameters) {
-
- var
- // use window context if none specified
- $allModules = $.isFunction(this)
- ? $(window)
- : $(this),
- moduleSelector = $allModules.selector || '',
- time = new Date().getTime(),
- performance = [],
-
- query = arguments[0],
- methodInvoked = (typeof query == 'string'),
- queryArguments = [].slice.call(arguments, 1),
-
- returnedValue
- ;
-
- $allModules
- .each(function() {
- var
- settings = ( $.isPlainObject(parameters) )
- ? $.extend(true, {}, $.fn.api.settings, parameters)
- : $.extend({}, $.fn.api.settings),
-
- // internal aliases
- namespace = settings.namespace,
- metadata = settings.metadata,
- selector = settings.selector,
- error = settings.error,
- className = settings.className,
-
- // define namespaces for modules
- eventNamespace = '.' + namespace,
- moduleNamespace = 'module-' + namespace,
-
- // element that creates request
- $module = $(this),
- $form = $module.closest(selector.form),
-
- // context used for state
- $context = (settings.stateContext)
- ? $(settings.stateContext)
- : $module,
-
- // request details
- ajaxSettings,
- requestSettings,
- url,
- data,
- requestStartTime,
-
- // standard module
- element = this,
- context = $context[0],
- instance = $module.data(moduleNamespace),
- module
- ;
-
- module = {
-
- initialize: function() {
- if(!methodInvoked) {
- module.bind.events();
- }
- module.instantiate();
- },
-
- instantiate: function() {
- module.verbose('Storing instance of module', module);
- instance = module;
- $module
- .data(moduleNamespace, instance)
- ;
- },
-
- destroy: function() {
- module.verbose('Destroying previous module for', element);
- $module
- .removeData(moduleNamespace)
- .off(eventNamespace)
- ;
- },
-
- bind: {
- events: function() {
- var
- triggerEvent = module.get.event()
- ;
- if( triggerEvent ) {
- module.verbose('Attaching API events to element', triggerEvent);
- $module
- .on(triggerEvent + eventNamespace, module.event.trigger)
- ;
- }
- else if(settings.on == 'now') {
- module.debug('Querying API endpoint immediately');
- module.query();
- }
- }
- },
-
- decode: {
- json: function(response) {
- if(response !== undefined && typeof response == 'string') {
- try {
- response = JSON.parse(response);
- }
- catch(e) {
- // isnt json string
- }
- }
- return response;
- }
- },
-
- read: {
- cachedResponse: function(url) {
- var
- response
- ;
- if(window.Storage === undefined) {
- module.error(error.noStorage);
- return;
- }
- response = sessionStorage.getItem(url);
- module.debug('Using cached response', url, response);
- response = module.decode.json(response);
- return response;
- }
- },
- write: {
- cachedResponse: function(url, response) {
- if(response && response === '') {
- module.debug('Response empty, not caching', response);
- return;
- }
- if(window.Storage === undefined) {
- module.error(error.noStorage);
- return;
- }
- if( $.isPlainObject(response) ) {
- response = JSON.stringify(response);
- }
- sessionStorage.setItem(url, response);
- module.verbose('Storing cached response for url', url, response);
- }
- },
-
- query: function() {
-
- if(module.is.disabled()) {
- module.debug('Element is disabled API request aborted');
- return;
- }
-
- if(module.is.loading()) {
- if(settings.interruptRequests) {
- module.debug('Interrupting previous request');
- module.abort();
- }
- else {
- module.debug('Cancelling request, previous request is still pending');
- return;
- }
- }
-
- // pass element metadata to url (value, text)
- if(settings.defaultData) {
- $.extend(true, settings.urlData, module.get.defaultData());
- }
-
- // Add form content
- if(settings.serializeForm) {
- settings.data = module.add.formData(settings.data);
- }
-
- // call beforesend and get any settings changes
- requestSettings = module.get.settings();
-
- // check if before send cancelled request
- if(requestSettings === false) {
- module.cancelled = true;
- module.error(error.beforeSend);
- return;
- }
- else {
- module.cancelled = false;
- }
-
- // get url
- url = module.get.templatedURL();
-
- if(!url && !module.is.mocked()) {
- module.error(error.missingURL);
- return;
- }
-
- // replace variables
- url = module.add.urlData( url );
- // missing url parameters
- if( !url && !module.is.mocked()) {
- return;
- }
-
- requestSettings.url = settings.base + url;
-
- // look for jQuery ajax parameters in settings
- ajaxSettings = $.extend(true, {}, settings, {
- type : settings.method || settings.type,
- data : data,
- url : settings.base + url,
- beforeSend : settings.beforeXHR,
- success : function() {},
- failure : function() {},
- complete : function() {}
- });
-
- module.debug('Querying URL', ajaxSettings.url);
- module.verbose('Using AJAX settings', ajaxSettings);
- if(settings.cache === 'local' && module.read.cachedResponse(url)) {
- module.debug('Response returned from local cache');
- module.request = module.create.request();
- module.request.resolveWith(context, [ module.read.cachedResponse(url) ]);
- return;
- }
-
- if( !settings.throttle ) {
- module.debug('Sending request', data, ajaxSettings.method);
- module.send.request();
- }
- else {
- if(!settings.throttleFirstRequest && !module.timer) {
- module.debug('Sending request', data, ajaxSettings.method);
- module.send.request();
- module.timer = setTimeout(function(){}, settings.throttle);
- }
- else {
- module.debug('Throttling request', settings.throttle);
- clearTimeout(module.timer);
- module.timer = setTimeout(function() {
- if(module.timer) {
- delete module.timer;
- }
- module.debug('Sending throttled request', data, ajaxSettings.method);
- module.send.request();
- }, settings.throttle);
- }
- }
-
- },
-
- should: {
- removeError: function() {
- return ( settings.hideError === true || (settings.hideError === 'auto' && !module.is.form()) );
- }
- },
-
- is: {
- disabled: function() {
- return ($module.filter(selector.disabled).length > 0);
- },
- expectingJSON: function() {
- return settings.dataType === 'json' || settings.dataType === 'jsonp';
- },
- form: function() {
- return $module.is('form') || $context.is('form');
- },
- mocked: function() {
- return (settings.mockResponse || settings.mockResponseAsync || settings.response || settings.responseAsync);
- },
- input: function() {
- return $module.is('input');
- },
- loading: function() {
- return (module.request)
- ? (module.request.state() == 'pending')
- : false
- ;
- },
- abortedRequest: function(xhr) {
- if(xhr && xhr.readyState !== undefined && xhr.readyState === 0) {
- module.verbose('XHR request determined to be aborted');
- return true;
- }
- else {
- module.verbose('XHR request was not aborted');
- return false;
- }
- },
- validResponse: function(response) {
- if( (!module.is.expectingJSON()) || !$.isFunction(settings.successTest) ) {
- module.verbose('Response is not JSON, skipping validation', settings.successTest, response);
- return true;
- }
- module.debug('Checking JSON returned success', settings.successTest, response);
- if( settings.successTest(response) ) {
- module.debug('Response passed success test', response);
- return true;
- }
- else {
- module.debug('Response failed success test', response);
- return false;
- }
- }
- },
-
- was: {
- cancelled: function() {
- return (module.cancelled || false);
- },
- succesful: function() {
- module.verbose('This behavior will be deleted due to typo. Use "was successful" instead.');
- return module.was.successful();
- },
- successful: function() {
- return (module.request && module.request.state() == 'resolved');
- },
- failure: function() {
- return (module.request && module.request.state() == 'rejected');
- },
- complete: function() {
- return (module.request && (module.request.state() == 'resolved' || module.request.state() == 'rejected') );
- }
- },
-
- add: {
- urlData: function(url, urlData) {
- var
- requiredVariables,
- optionalVariables
- ;
- if(url) {
- requiredVariables = url.match(settings.regExp.required);
- optionalVariables = url.match(settings.regExp.optional);
- urlData = urlData || settings.urlData;
- if(requiredVariables) {
- module.debug('Looking for required URL variables', requiredVariables);
- $.each(requiredVariables, function(index, templatedString) {
- var
- // allow legacy {$var} style
- variable = (templatedString.indexOf('$') !== -1)
- ? templatedString.substr(2, templatedString.length - 3)
- : templatedString.substr(1, templatedString.length - 2),
- value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
- ? urlData[variable]
- : ($module.data(variable) !== undefined)
- ? $module.data(variable)
- : ($context.data(variable) !== undefined)
- ? $context.data(variable)
- : urlData[variable]
- ;
- // remove value
- if(value === undefined) {
- module.error(error.requiredParameter, variable, url);
- url = false;
- return false;
- }
- else {
- module.verbose('Found required variable', variable, value);
- value = (settings.encodeParameters)
- ? module.get.urlEncodedValue(value)
- : value
- ;
- url = url.replace(templatedString, value);
- }
- });
- }
- if(optionalVariables) {
- module.debug('Looking for optional URL variables', requiredVariables);
- $.each(optionalVariables, function(index, templatedString) {
- var
- // allow legacy {/$var} style
- variable = (templatedString.indexOf('$') !== -1)
- ? templatedString.substr(3, templatedString.length - 4)
- : templatedString.substr(2, templatedString.length - 3),
- value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
- ? urlData[variable]
- : ($module.data(variable) !== undefined)
- ? $module.data(variable)
- : ($context.data(variable) !== undefined)
- ? $context.data(variable)
- : urlData[variable]
- ;
- // optional replacement
- if(value !== undefined) {
- module.verbose('Optional variable Found', variable, value);
- url = url.replace(templatedString, value);
- }
- else {
- module.verbose('Optional variable not found', variable);
- // remove preceding slash if set
- if(url.indexOf('/' + templatedString) !== -1) {
- url = url.replace('/' + templatedString, '');
- }
- else {
- url = url.replace(templatedString, '');
- }
- }
- });
- }
- }
- return url;
- },
- formData: function(data) {
- var
- canSerialize = ($.fn.serializeObject !== undefined),
- formData = (canSerialize)
- ? $form.serializeObject()
- : $form.serialize(),
- hasOtherData
- ;
- data = data || settings.data;
- hasOtherData = $.isPlainObject(data);
-
- if(hasOtherData) {
- if(canSerialize) {
- module.debug('Extending existing data with form data', data, formData);
- data = $.extend(true, {}, data, formData);
- }
- else {
- module.error(error.missingSerialize);
- module.debug('Cant extend data. Replacing data with form data', data, formData);
- data = formData;
- }
- }
- else {
- module.debug('Adding form data', formData);
- data = formData;
- }
- return data;
- }
- },
-
- send: {
- request: function() {
- module.set.loading();
- module.request = module.create.request();
- if( module.is.mocked() ) {
- module.mockedXHR = module.create.mockedXHR();
- }
- else {
- module.xhr = module.create.xhr();
- }
- settings.onRequest.call(context, module.request, module.xhr);
- }
- },
-
- event: {
- trigger: function(event) {
- module.query();
- if(event.type == 'submit' || event.type == 'click') {
- event.preventDefault();
- }
- },
- xhr: {
- always: function() {
- // nothing special
- },
- done: function(response, textStatus, xhr) {
- var
- context = this,
- elapsedTime = (new Date().getTime() - requestStartTime),
- timeLeft = (settings.loadingDuration - elapsedTime),
- translatedResponse = ( $.isFunction(settings.onResponse) )
- ? module.is.expectingJSON() && !settings.rawResponse
- ? settings.onResponse.call(context, $.extend(true, {}, response))
- : settings.onResponse.call(context, response)
- : false
- ;
- timeLeft = (timeLeft > 0)
- ? timeLeft
- : 0
- ;
- if(translatedResponse) {
- module.debug('Modified API response in onResponse callback', settings.onResponse, translatedResponse, response);
- response = translatedResponse;
- }
- if(timeLeft > 0) {
- module.debug('Response completed early delaying state change by', timeLeft);
- }
- setTimeout(function() {
- if( module.is.validResponse(response) ) {
- module.request.resolveWith(context, [response, xhr]);
- }
- else {
- module.request.rejectWith(context, [xhr, 'invalid']);
- }
- }, timeLeft);
- },
- fail: function(xhr, status, httpMessage) {
- var
- context = this,
- elapsedTime = (new Date().getTime() - requestStartTime),
- timeLeft = (settings.loadingDuration - elapsedTime)
- ;
- timeLeft = (timeLeft > 0)
- ? timeLeft
- : 0
- ;
- if(timeLeft > 0) {
- module.debug('Response completed early delaying state change by', timeLeft);
- }
- setTimeout(function() {
- if( module.is.abortedRequest(xhr) ) {
- module.request.rejectWith(context, [xhr, 'aborted', httpMessage]);
- }
- else {
- module.request.rejectWith(context, [xhr, 'error', status, httpMessage]);
- }
- }, timeLeft);
- }
- },
- request: {
- done: function(response, xhr) {
- module.debug('Successful API Response', response);
- if(settings.cache === 'local' && url) {
- module.write.cachedResponse(url, response);
- module.debug('Saving server response locally', module.cache);
- }
- settings.onSuccess.call(context, response, $module, xhr);
- },
- complete: function(firstParameter, secondParameter) {
- var
- xhr,
- response
- ;
- // have to guess callback parameters based on request success
- if( module.was.successful() ) {
- response = firstParameter;
- xhr = secondParameter;
- }
- else {
- xhr = firstParameter;
- response = module.get.responseFromXHR(xhr);
- }
- module.remove.loading();
- settings.onComplete.call(context, response, $module, xhr);
- },
- fail: function(xhr, status, httpMessage) {
- var
- // pull response from xhr if available
- response = module.get.responseFromXHR(xhr),
- errorMessage = module.get.errorFromRequest(response, status, httpMessage)
- ;
- if(status == 'aborted') {
- module.debug('XHR Aborted (Most likely caused by page navigation or CORS Policy)', status, httpMessage);
- settings.onAbort.call(context, status, $module, xhr);
- return true;
- }
- else if(status == 'invalid') {
- module.debug('JSON did not pass success test. A server-side error has most likely occurred', response);
- }
- else if(status == 'error') {
- if(xhr !== undefined) {
- module.debug('XHR produced a server error', status, httpMessage);
- // make sure we have an error to display to console
- if( (xhr.status < 200 || xhr.status >= 300) && httpMessage !== undefined && httpMessage !== '') {
- module.error(error.statusMessage + httpMessage, ajaxSettings.url);
- }
- settings.onError.call(context, errorMessage, $module, xhr);
- }
- }
-
- if(settings.errorDuration && status !== 'aborted') {
- module.debug('Adding error state');
- module.set.error();
- if( module.should.removeError() ) {
- setTimeout(module.remove.error, settings.errorDuration);
- }
- }
- module.debug('API Request failed', errorMessage, xhr);
- settings.onFailure.call(context, response, $module, xhr);
- }
- }
- },
-
- create: {
-
- request: function() {
- // api request promise
- return $.Deferred()
- .always(module.event.request.complete)
- .done(module.event.request.done)
- .fail(module.event.request.fail)
- ;
- },
-
- mockedXHR: function () {
- var
- // xhr does not simulate these properties of xhr but must return them
- textStatus = false,
- status = false,
- httpMessage = false,
- responder = settings.mockResponse || settings.response,
- asyncResponder = settings.mockResponseAsync || settings.responseAsync,
- asyncCallback,
- response,
- mockedXHR
- ;
-
- mockedXHR = $.Deferred()
- .always(module.event.xhr.complete)
- .done(module.event.xhr.done)
- .fail(module.event.xhr.fail)
- ;
-
- if(responder) {
- if( $.isFunction(responder) ) {
- module.debug('Using specified synchronous callback', responder);
- response = responder.call(context, requestSettings);
- }
- else {
- module.debug('Using settings specified response', responder);
- response = responder;
- }
- // simulating response
- mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
- }
- else if( $.isFunction(asyncResponder) ) {
- asyncCallback = function(response) {
- module.debug('Async callback returned response', response);
-
- if(response) {
- mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
- }
- else {
- mockedXHR.rejectWith(context, [{ responseText: response }, status, httpMessage]);
- }
- };
- module.debug('Using specified async response callback', asyncResponder);
- asyncResponder.call(context, requestSettings, asyncCallback);
- }
- return mockedXHR;
- },
-
- xhr: function() {
- var
- xhr
- ;
- // ajax request promise
- xhr = $.ajax(ajaxSettings)
- .always(module.event.xhr.always)
- .done(module.event.xhr.done)
- .fail(module.event.xhr.fail)
- ;
- module.verbose('Created server request', xhr, ajaxSettings);
- return xhr;
- }
- },
-
- set: {
- error: function() {
- module.verbose('Adding error state to element', $context);
- $context.addClass(className.error);
- },
- loading: function() {
- module.verbose('Adding loading state to element', $context);
- $context.addClass(className.loading);
- requestStartTime = new Date().getTime();
- }
- },
-
- remove: {
- error: function() {
- module.verbose('Removing error state from element', $context);
- $context.removeClass(className.error);
- },
- loading: function() {
- module.verbose('Removing loading state from element', $context);
- $context.removeClass(className.loading);
- }
- },
-
- get: {
- responseFromXHR: function(xhr) {
- return $.isPlainObject(xhr)
- ? (module.is.expectingJSON())
- ? module.decode.json(xhr.responseText)
- : xhr.responseText
- : false
- ;
- },
- errorFromRequest: function(response, status, httpMessage) {
- return ($.isPlainObject(response) && response.error !== undefined)
- ? response.error // use json error message
- : (settings.error[status] !== undefined) // use server error message
- ? settings.error[status]
- : httpMessage
- ;
- },
- request: function() {
- return module.request || false;
- },
- xhr: function() {
- return module.xhr || false;
- },
- settings: function() {
- var
- runSettings
- ;
- runSettings = settings.beforeSend.call($module, settings);
- if(runSettings) {
- if(runSettings.success !== undefined) {
- module.debug('Legacy success callback detected', runSettings);
- module.error(error.legacyParameters, runSettings.success);
- runSettings.onSuccess = runSettings.success;
- }
- if(runSettings.failure !== undefined) {
- module.debug('Legacy failure callback detected', runSettings);
- module.error(error.legacyParameters, runSettings.failure);
- runSettings.onFailure = runSettings.failure;
- }
- if(runSettings.complete !== undefined) {
- module.debug('Legacy complete callback detected', runSettings);
- module.error(error.legacyParameters, runSettings.complete);
- runSettings.onComplete = runSettings.complete;
- }
- }
- if(runSettings === undefined) {
- module.error(error.noReturnedValue);
- }
- if(runSettings === false) {
- return runSettings;
- }
- return (runSettings !== undefined)
- ? $.extend(true, {}, runSettings)
- : $.extend(true, {}, settings)
- ;
- },
- urlEncodedValue: function(value) {
- var
- decodedValue = window.decodeURIComponent(value),
- encodedValue = window.encodeURIComponent(value),
- alreadyEncoded = (decodedValue !== value)
- ;
- if(alreadyEncoded) {
- module.debug('URL value is already encoded, avoiding double encoding', value);
- return value;
- }
- module.verbose('Encoding value using encodeURIComponent', value, encodedValue);
- return encodedValue;
- },
- defaultData: function() {
- var
- data = {}
- ;
- if( !$.isWindow(element) ) {
- if( module.is.input() ) {
- data.value = $module.val();
- }
- else if( module.is.form() ) {
-
- }
- else {
- data.text = $module.text();
- }
- }
- return data;
- },
- event: function() {
- if( $.isWindow(element) || settings.on == 'now' ) {
- module.debug('API called without element, no events attached');
- return false;
- }
- else if(settings.on == 'auto') {
- if( $module.is('input') ) {
- return (element.oninput !== undefined)
- ? 'input'
- : (element.onpropertychange !== undefined)
- ? 'propertychange'
- : 'keyup'
- ;
- }
- else if( $module.is('form') ) {
- return 'submit';
- }
- else {
- return 'click';
- }
- }
- else {
- return settings.on;
- }
- },
- templatedURL: function(action) {
- action = action || $module.data(metadata.action) || settings.action || false;
- url = $module.data(metadata.url) || settings.url || false;
- if(url) {
- module.debug('Using specified url', url);
- return url;
- }
- if(action) {
- module.debug('Looking up url for action', action, settings.api);
- if(settings.api[action] === undefined && !module.is.mocked()) {
- module.error(error.missingAction, settings.action, settings.api);
- return;
- }
- url = settings.api[action];
- }
- else if( module.is.form() ) {
- url = $module.attr('action') || $context.attr('action') || false;
- module.debug('No url or action specified, defaulting to form action', url);
- }
- return url;
- }
- },
-
- abort: function() {
- var
- xhr = module.get.xhr()
- ;
- if( xhr && xhr.state() !== 'resolved') {
- module.debug('Cancelling API request');
- xhr.abort();
- }
- },
-
- // reset state
- reset: function() {
- module.remove.error();
- module.remove.loading();
- },
-
- setting: function(name, value) {
- module.debug('Changing setting', name, value);
- if( $.isPlainObject(name) ) {
- $.extend(true, settings, name);
- }
- else if(value !== undefined) {
- if($.isPlainObject(settings[name])) {
- $.extend(true, settings[name], value);
- }
- else {
- settings[name] = value;
- }
- }
- else {
- return settings[name];
- }
- },
- internal: function(name, value) {
- if( $.isPlainObject(name) ) {
- $.extend(true, module, name);
- }
- else if(value !== undefined) {
- module[name] = value;
- }
- else {
- return module[name];
- }
- },
- debug: function() {
- if(!settings.silent && settings.debug) {
- if(settings.performance) {
- module.performance.log(arguments);
- }
- else {
- module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
- module.debug.apply(console, arguments);
- }
- }
- },
- verbose: function() {
- if(!settings.silent && settings.verbose && settings.debug) {
- if(settings.performance) {
- module.performance.log(arguments);
- }
- else {
- module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
- module.verbose.apply(console, arguments);
- }
- }
- },
- error: function() {
- if(!settings.silent) {
- module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
- module.error.apply(console, arguments);
- }
- },
- performance: {
- log: function(message) {
- var
- currentTime,
- executionTime,
- previousTime
- ;
- if(settings.performance) {
- currentTime = new Date().getTime();
- previousTime = time || currentTime;
- executionTime = currentTime - previousTime;
- time = currentTime;
- performance.push({
- 'Name' : message[0],
- 'Arguments' : [].slice.call(message, 1) || '',
- //'Element' : element,
- 'Execution Time' : executionTime
- });
- }
- clearTimeout(module.performance.timer);
- module.performance.timer = setTimeout(module.performance.display, 500);
- },
- display: function() {
- var
- title = settings.name + ':',
- totalTime = 0
- ;
- time = false;
- clearTimeout(module.performance.timer);
- $.each(performance, function(index, data) {
- totalTime += data['Execution Time'];
- });
- title += ' ' + totalTime + 'ms';
- if(moduleSelector) {
- title += ' \'' + moduleSelector + '\'';
- }
- if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
- console.groupCollapsed(title);
- if(console.table) {
- console.table(performance);
- }
- else {
- $.each(performance, function(index, data) {
- console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
- });
- }
- console.groupEnd();
- }
- performance = [];
- }
- },
- invoke: function(query, passedArguments, context) {
- var
- object = instance,
- maxDepth,
- found,
- response
- ;
- passedArguments = passedArguments || queryArguments;
- context = element || context;
- if(typeof query == 'string' && object !== undefined) {
- query = query.split(/[\. ]/);
- maxDepth = query.length - 1;
- $.each(query, function(depth, value) {
- var camelCaseValue = (depth != maxDepth)
- ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
- : query
- ;
- if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
- object = object[camelCaseValue];
- }
- else if( object[camelCaseValue] !== undefined ) {
- found = object[camelCaseValue];
- return false;
- }
- else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
- object = object[value];
- }
- else if( object[value] !== undefined ) {
- found = object[value];
- return false;
- }
- else {
- module.error(error.method, query);
- return false;
- }
- });
- }
- if ( $.isFunction( found ) ) {
- response = found.apply(context, passedArguments);
- }
- else if(found !== undefined) {
- response = found;
- }
- if(Array.isArray(returnedValue)) {
- returnedValue.push(response);
- }
- else if(returnedValue !== undefined) {
- returnedValue = [returnedValue, response];
- }
- else if(response !== undefined) {
- returnedValue = response;
- }
- return found;
- }
- };
-
- if(methodInvoked) {
- if(instance === undefined) {
- module.initialize();
- }
- module.invoke(query);
- }
- else {
- if(instance !== undefined) {
- instance.invoke('destroy');
- }
- module.initialize();
- }
- })
- ;
-
- return (returnedValue !== undefined)
- ? returnedValue
- : this
- ;
-};
-
-$.api.settings = {
-
- name : 'API',
- namespace : 'api',
-
- debug : false,
- verbose : false,
- performance : true,
-
- // object containing all templates endpoints
- api : {},
-
- // whether to cache responses
- cache : true,
-
- // whether new requests should abort previous requests
- interruptRequests : true,
-
- // event binding
- on : 'auto',
-
- // context for applying state classes
- stateContext : false,
-
- // duration for loading state
- loadingDuration : 0,
-
- // whether to hide errors after a period of time
- hideError : 'auto',
-
- // duration for error state
- errorDuration : 2000,
-
- // whether parameters should be encoded with encodeURIComponent
- encodeParameters : true,
-
- // API action to use
- action : false,
-
- // templated URL to use
- url : false,
-
- // base URL to apply to all endpoints
- base : '',
-
- // data that will
- urlData : {},
-
- // whether to add default data to url data
- defaultData : true,
-
- // whether to serialize closest form
- serializeForm : false,
-
- // how long to wait before request should occur
- throttle : 0,
-
- // whether to throttle first request or only repeated
- throttleFirstRequest : true,
-
- // standard ajax settings
- method : 'get',
- data : {},
- dataType : 'json',
-
- // mock response
- mockResponse : false,
- mockResponseAsync : false,
-
- // aliases for mock
- response : false,
- responseAsync : false,
-
-// whether onResponse should work with response value without force converting into an object
- rawResponse : false,
-
- // callbacks before request
- beforeSend : function(settings) { return settings; },
- beforeXHR : function(xhr) {},
- onRequest : function(promise, xhr) {},
-
- // after request
- onResponse : false, // function(response) { },
-
- // response was successful, if JSON passed validation
- onSuccess : function(response, $module) {},
-
- // request finished without aborting
- onComplete : function(response, $module) {},
-
- // failed JSON success test
- onFailure : function(response, $module) {},
-
- // server error
- onError : function(errorMessage, $module) {},
-
- // request aborted
- onAbort : function(errorMessage, $module) {},
-
- successTest : false,
-
- // errors
- error : {
- beforeSend : 'The before send function has aborted the request',
- error : 'There was an error with your request',
- exitConditions : 'API Request Aborted. Exit conditions met',
- JSONParse : 'JSON could not be parsed during error handling',
- legacyParameters : 'You are using legacy API success callback names',
- method : 'The method you called is not defined',
- missingAction : 'API action used but no url was defined',
- missingSerialize : 'jquery-serialize-object is required to add form data to an existing data object',
- missingURL : 'No URL specified for api event',
- noReturnedValue : 'The beforeSend callback must return a settings object, beforeSend ignored.',
- noStorage : 'Caching responses locally requires session storage',
- parseError : 'There was an error parsing your request',
- requiredParameter : 'Missing a required URL parameter: ',
- statusMessage : 'Server gave an error: ',
- timeout : 'Your request timed out'
- },
-
- regExp : {
- required : /\{\$*[A-z0-9]+\}/g,
- optional : /\{\/\$*[A-z0-9]+\}/g,
- },
-
- className: {
- loading : 'loading',
- error : 'error'
- },
-
- selector: {
- disabled : '.disabled',
- form : 'form'
- },
-
- metadata: {
- action : 'action',
- url : 'url'
- }
-};
-
-
-
-})( jQuery, window, document );
-
-/*!
- * # Fomantic-UI - Dropdown
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-;(function ($, window, document, undefined) {
-
-'use strict';
-
-$.isFunction = $.isFunction || function(obj) {
- return typeof obj === "function" && typeof obj.nodeType !== "number";
-};
-
-window = (typeof window != 'undefined' && window.Math == Math)
- ? window
- : (typeof self != 'undefined' && self.Math == Math)
- ? self
- : Function('return this')()
-;
-
-$.fn.dropdown = function(parameters) {
- var
- $allModules = $(this),
- $document = $(document),
-
- moduleSelector = $allModules.selector || '',
-
- hasTouch = ('ontouchstart' in document.documentElement),
- clickEvent = "click", unstableClickEvent = hasTouch
- ? 'touchstart'
- : 'click',
-
- time = new Date().getTime(),
- performance = [],
-
- query = arguments[0],
- methodInvoked = (typeof query == 'string'),
- queryArguments = [].slice.call(arguments, 1),
- returnedValue
- ;
-
- $allModules
- .each(function(elementIndex) {
- var
- settings = ( $.isPlainObject(parameters) )
- ? $.extend(true, {}, $.fn.dropdown.settings, parameters)
- : $.extend({}, $.fn.dropdown.settings),
-
- className = settings.className,
- message = settings.message,
- fields = settings.fields,
- keys = settings.keys,
- metadata = settings.metadata,
- namespace = settings.namespace,
- regExp = settings.regExp,
- selector = settings.selector,
- error = settings.error,
- templates = settings.templates,
-
- eventNamespace = '.' + namespace,
- moduleNamespace = 'module-' + namespace,
-
- $module = $(this),
- $context = $(settings.context),
- $text = $module.find(selector.text),
- $search = $module.find(selector.search),
- $sizer = $module.find(selector.sizer),
- $input = $module.find(selector.input),
- $icon = $module.find(selector.icon),
- $clear = $module.find(selector.clearIcon),
-
- $combo = ($module.prev().find(selector.text).length > 0)
- ? $module.prev().find(selector.text)
- : $module.prev(),
-
- $menu = $module.children(selector.menu),
- $item = $menu.find(selector.item),
- $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $(),
-
- activated = false,
- itemActivated = false,
- internalChange = false,
- iconClicked = false,
- element = this,
- instance = $module.data(moduleNamespace),
-
- selectActionActive,
- initialLoad,
- pageLostFocus,
- willRefocus,
- elementNamespace,
- id,
- selectObserver,
- menuObserver,
- classObserver,
- module
- ;
-
- module = {
-
- initialize: function() {
- module.debug('Initializing dropdown', settings);
-
- if( module.is.alreadySetup() ) {
- module.setup.reference();
- }
- else {
- if (settings.ignoreDiacritics && !String.prototype.normalize) {
- settings.ignoreDiacritics = false;
- module.error(error.noNormalize, element);
- }
-
- module.setup.layout();
-
- if(settings.values) {
- module.set.initialLoad();
- module.change.values(settings.values);
- module.remove.initialLoad();
- }
-
- module.refreshData();
-
- module.save.defaults();
- module.restore.selected();
-
- module.create.id();
- module.bind.events();
-
- module.observeChanges();
- module.instantiate();
- }
-
- },
-
- instantiate: function() {
- module.verbose('Storing instance of dropdown', module);
- instance = module;
- $module
- .data(moduleNamespace, module)
- ;
- },
-
- destroy: function() {
- module.verbose('Destroying previous dropdown', $module);
- module.remove.tabbable();
- module.remove.active();
- $menu.transition('stop all');
- $menu.removeClass(className.visible).addClass(className.hidden);
- $module
- .off(eventNamespace)
- .removeData(moduleNamespace)
- ;
- $menu
- .off(eventNamespace)
- ;
- $document
- .off(elementNamespace)
- ;
- module.disconnect.menuObserver();
- module.disconnect.selectObserver();
- module.disconnect.classObserver();
- },
-
- observeChanges: function() {
- if('MutationObserver' in window) {
- selectObserver = new MutationObserver(module.event.select.mutation);
- menuObserver = new MutationObserver(module.event.menu.mutation);
- classObserver = new MutationObserver(module.event.class.mutation);
- module.debug('Setting up mutation observer', selectObserver, menuObserver, classObserver);
- module.observe.select();
- module.observe.menu();
- module.observe.class();
- }
- },
-
- disconnect: {
- menuObserver: function() {
- if(menuObserver) {
- menuObserver.disconnect();
- }
- },
- selectObserver: function() {
- if(selectObserver) {
- selectObserver.disconnect();
- }
- },
- classObserver: function() {
- if(classObserver) {
- classObserver.disconnect();
- }
- }
- },
- observe: {
- select: function() {
- if(module.has.input() && selectObserver) {
- selectObserver.observe($module[0], {
- childList : true,
- subtree : true
- });
- }
- },
- menu: function() {
- if(module.has.menu() && menuObserver) {
- menuObserver.observe($menu[0], {
- childList : true,
- subtree : true
- });
- }
- },
- class: function() {
- if(module.has.search() && classObserver) {
- classObserver.observe($module[0], {
- attributes : true
- });
- }
- }
- },
-
- create: {
- id: function() {
- id = (Math.random().toString(16) + '000000000').substr(2, 8);
- elementNamespace = '.' + id;
- module.verbose('Creating unique id for element', id);
- },
- userChoice: function(values) {
- var
- $userChoices,
- $userChoice,
- isUserValue,
- html
- ;
- values = values || module.get.userValues();
- if(!values) {
- return false;
- }
- values = Array.isArray(values)
- ? values
- : [values]
- ;
- $.each(values, function(index, value) {
- if(module.get.item(value) === false) {
- html = settings.templates.addition( module.add.variables(message.addResult, value) );
- $userChoice = $('<div />')
- .html(html)
- .attr('data-' + metadata.value, value)
- .attr('data-' + metadata.text, value)
- .addClass(className.addition)
- .addClass(className.item)
- ;
- if(settings.hideAdditions) {
- $userChoice.addClass(className.hidden);
- }
- $userChoices = ($userChoices === undefined)
- ? $userChoice
- : $userChoices.add($userChoice)
- ;
- module.verbose('Creating user choices for value', value, $userChoice);
- }
- });
- return $userChoices;
- },
- userLabels: function(value) {
- var
- userValues = module.get.userValues()
- ;
- if(userValues) {
- module.debug('Adding user labels', userValues);
- $.each(userValues, function(index, value) {
- module.verbose('Adding custom user value');
- module.add.label(value, value);
- });
- }
- },
- menu: function() {
- $menu = $('<div />')
- .addClass(className.menu)
- .appendTo($module)
- ;
- },
- sizer: function() {
- $sizer = $('<span />')
- .addClass(className.sizer)
- .insertAfter($search)
- ;
- }
- },
-
- search: function(query) {
- query = (query !== undefined)
- ? query
- : module.get.query()
- ;
- module.verbose('Searching for query', query);
- if(module.has.minCharacters(query)) {
- module.filter(query);
- }
- else {
- module.hide(null,true);
- }
- },
-
- select: {
- firstUnfiltered: function() {
- module.verbose('Selecting first non-filtered element');
- module.remove.selectedItem();
- $item
- .not(selector.unselectable)
- .not(selector.addition + selector.hidden)
- .eq(0)
- .addClass(className.selected)
- ;
- },
- nextAvailable: function($selected) {
- $selected = $selected.eq(0);
- var
- $nextAvailable = $selected.nextAll(selector.item).not(selector.unselectable).eq(0),
- $prevAvailable = $selected.prevAll(selector.item).not(selector.unselectable).eq(0),
- hasNext = ($nextAvailable.length > 0)
- ;
- if(hasNext) {
- module.verbose('Moving selection to', $nextAvailable);
- $nextAvailable.addClass(className.selected);
- }
- else {
- module.verbose('Moving selection to', $prevAvailable);
- $prevAvailable.addClass(className.selected);
- }
- }
- },
-
- setup: {
- api: function() {
- var
- apiSettings = {
- debug : settings.debug,
- urlData : {
- value : module.get.value(),
- query : module.get.query()
- },
- on : false
- }
- ;
- module.verbose('First request, initializing API');
- $module
- .api(apiSettings)
- ;
- },
- layout: function() {
- if( $module.is('select') ) {
- module.setup.select();
- module.setup.returnedObject();
- }
- if( !module.has.menu() ) {
- module.create.menu();
- }
- if ( module.is.selection() && module.is.clearable() && !module.has.clearItem() ) {
- module.verbose('Adding clear icon');
- $clear = $('<i />')
- .addClass('remove icon')
- .insertBefore($text)
- ;
- }
- if( module.is.search() && !module.has.search() ) {
- module.verbose('Adding search input');
- $search = $('<input />')
- .addClass(className.search)
- .prop('autocomplete', 'off')
- .insertBefore($text)
- ;
- }
- if( module.is.multiple() && module.is.searchSelection() && !module.has.sizer()) {
- module.create.sizer();
- }
- if(settings.allowTab) {
- module.set.tabbable();
- }
- },
- select: function() {
- var
- selectValues = module.get.selectValues()
- ;
- module.debug('Dropdown initialized on a select', selectValues);
- if( $module.is('select') ) {
- $input = $module;
- }
- // see if select is placed correctly already
- if($input.parent(selector.dropdown).length > 0) {
- module.debug('UI dropdown already exists. Creating dropdown menu only');
- $module = $input.closest(selector.dropdown);
- if( !module.has.menu() ) {
- module.create.menu();
- }
- $menu = $module.children(selector.menu);
- module.setup.menu(selectValues);
- }
- else {
- module.debug('Creating entire dropdown from select');
- $module = $('<div />')
- .attr('class', $input.attr('class') )
- .addClass(className.selection)
- .addClass(className.dropdown)
- .html( templates.dropdown(selectValues, fields, settings.preserveHTML, settings.className) )
- .insertBefore($input)
- ;
- if($input.hasClass(className.multiple) && $input.prop('multiple') === false) {
- module.error(error.missingMultiple);
- $input.prop('multiple', true);
- }
- if($input.is('[multiple]')) {
- module.set.multiple();
- }
- if ($input.prop('disabled')) {
- module.debug('Disabling dropdown');
- $module.addClass(className.disabled);
- }
- $input
- .removeAttr('required')
- .removeAttr('class')
- .detach()
- .prependTo($module)
- ;
- }
- module.refresh();
- },
- menu: function(values) {
- $menu.html( templates.menu(values, fields,settings.preserveHTML,settings.className));
- $item = $menu.find(selector.item);
- $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
- },
- reference: function() {
- module.debug('Dropdown behavior was called on select, replacing with closest dropdown');
- // replace module reference
- $module = $module.parent(selector.dropdown);
- instance = $module.data(moduleNamespace);
- element = $module.get(0);
- module.refresh();
- module.setup.returnedObject();
- },
- returnedObject: function() {
- var
- $firstModules = $allModules.slice(0, elementIndex),
- $lastModules = $allModules.slice(elementIndex + 1)
- ;
- // adjust all modules to use correct reference
- $allModules = $firstModules.add($module).add($lastModules);
- }
- },
-
- refresh: function() {
- module.refreshSelectors();
- module.refreshData();
- },
-
- refreshItems: function() {
- $item = $menu.find(selector.item);
- $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
- },
-
- refreshSelectors: function() {
- module.verbose('Refreshing selector cache');
- $text = $module.find(selector.text);
- $search = $module.find(selector.search);
- $input = $module.find(selector.input);
- $icon = $module.find(selector.icon);
- $combo = ($module.prev().find(selector.text).length > 0)
- ? $module.prev().find(selector.text)
- : $module.prev()
- ;
- $menu = $module.children(selector.menu);
- $item = $menu.find(selector.item);
- $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
- },
-
- refreshData: function() {
- module.verbose('Refreshing cached metadata');
- $item
- .removeData(metadata.text)
- .removeData(metadata.value)
- ;
- },
-
- clearData: function() {
- module.verbose('Clearing metadata');
- $item
- .removeData(metadata.text)
- .removeData(metadata.value)
- ;
- $module
- .removeData(metadata.defaultText)
- .removeData(metadata.defaultValue)
- .removeData(metadata.placeholderText)
- ;
- },
-
- toggle: function() {
- module.verbose('Toggling menu visibility');
- if( !module.is.active() ) {
- module.show();
- }
- else {
- module.hide();
- }
- },
-
- show: function(callback, preventFocus) {
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- if(!module.can.show() && module.is.remote()) {
- module.debug('No API results retrieved, searching before show');
- module.queryRemote(module.get.query(), module.show);
- }
- if( module.can.show() && !module.is.active() ) {
- module.debug('Showing dropdown');
- if(module.has.message() && !(module.has.maxSelections() || module.has.allResultsFiltered()) ) {
- module.remove.message();
- }
- if(module.is.allFiltered()) {
- return true;
- }
- if(settings.onShow.call(element) !== false) {
- module.animate.show(function() {
- if( module.can.click() ) {
- module.bind.intent();
- }
- if(module.has.search() && !preventFocus) {
- module.focusSearch();
- }
- module.set.visible();
- callback.call(element);
- });
- }
- }
- },
-
- hide: function(callback, preventBlur) {
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- if( module.is.active() && !module.is.animatingOutward() ) {
- module.debug('Hiding dropdown');
- if(settings.onHide.call(element) !== false) {
- module.animate.hide(function() {
- module.remove.visible();
- // hidding search focus
- if ( module.is.focusedOnSearch() && preventBlur !== true ) {
- $search.blur();
- }
- callback.call(element);
- });
- }
- } else if( module.can.click() ) {
- module.unbind.intent();
- }
- iconClicked = false;
- },
-
- hideOthers: function() {
- module.verbose('Finding other dropdowns to hide');
- $allModules
- .not($module)
- .has(selector.menu + '.' + className.visible)
- .dropdown('hide')
- ;
- },
-
- hideMenu: function() {
- module.verbose('Hiding menu instantaneously');
- module.remove.active();
- module.remove.visible();
- $menu.transition('hide');
- },
-
- hideSubMenus: function() {
- var
- $subMenus = $menu.children(selector.item).find(selector.menu)
- ;
- module.verbose('Hiding sub menus', $subMenus);
- $subMenus.transition('hide');
- },
-
- bind: {
- events: function() {
- module.bind.keyboardEvents();
- module.bind.inputEvents();
- module.bind.mouseEvents();
- },
- keyboardEvents: function() {
- module.verbose('Binding keyboard events');
- $module
- .on('keydown' + eventNamespace, module.event.keydown)
- ;
- if( module.has.search() ) {
- $module
- .on(module.get.inputEvent() + eventNamespace, selector.search, module.event.input)
- ;
- }
- if( module.is.multiple() ) {
- $document
- .on('keydown' + elementNamespace, module.event.document.keydown)
- ;
- }
- },
- inputEvents: function() {
- module.verbose('Binding input change events');
- $module
- .on('change' + eventNamespace, selector.input, module.event.change)
- ;
- },
- mouseEvents: function() {
- module.verbose('Binding mouse events');
- if(module.is.multiple()) {
- $module
- .on(clickEvent + eventNamespace, selector.label, module.event.label.click)
- .on(clickEvent + eventNamespace, selector.remove, module.event.remove.click)
- ;
- }
- if( module.is.searchSelection() ) {
- $module
- .on('mousedown' + eventNamespace, module.event.mousedown)
- .on('mouseup' + eventNamespace, module.event.mouseup)
- .on('mousedown' + eventNamespace, selector.menu, module.event.menu.mousedown)
- .on('mouseup' + eventNamespace, selector.menu, module.event.menu.mouseup)
- .on(clickEvent + eventNamespace, selector.icon, module.event.icon.click)
- .on(clickEvent + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
- .on('focus' + eventNamespace, selector.search, module.event.search.focus)
- .on(clickEvent + eventNamespace, selector.search, module.event.search.focus)
- .on('blur' + eventNamespace, selector.search, module.event.search.blur)
- .on(clickEvent + eventNamespace, selector.text, module.event.text.focus)
- ;
- if(module.is.multiple()) {
- $module
- .on(clickEvent + eventNamespace, module.event.click)
- ;
- }
- }
- else {
- if(settings.on == 'click') {
- $module
- .on(clickEvent + eventNamespace, selector.icon, module.event.icon.click)
- .on(clickEvent + eventNamespace, module.event.test.toggle)
- ;
- }
- else if(settings.on == 'hover') {
- $module
- .on('mouseenter' + eventNamespace, module.delay.show)
- .on('mouseleave' + eventNamespace, module.delay.hide)
- ;
- }
- else {
- $module
- .on(settings.on + eventNamespace, module.toggle)
- ;
- }
- $module
- .on('mousedown' + eventNamespace, module.event.mousedown)
- .on('mouseup' + eventNamespace, module.event.mouseup)
- .on('focus' + eventNamespace, module.event.focus)
- .on(clickEvent + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
- ;
- if(module.has.menuSearch() ) {
- $module
- .on('blur' + eventNamespace, selector.search, module.event.search.blur)
- ;
- }
- else {
- $module
- .on('blur' + eventNamespace, module.event.blur)
- ;
- }
- }
- $menu
- .on((hasTouch ? 'touchstart' : 'mouseenter') + eventNamespace, selector.item, module.event.item.mouseenter)
- .on('mouseleave' + eventNamespace, selector.item, module.event.item.mouseleave)
- .on('click' + eventNamespace, selector.item, module.event.item.click)
- ;
- },
- intent: function() {
- module.verbose('Binding hide intent event to document');
- if(hasTouch) {
- $document
- .on('touchstart' + elementNamespace, module.event.test.touch)
- .on('touchmove' + elementNamespace, module.event.test.touch)
- ;
- }
- $document
- .on(clickEvent + elementNamespace, module.event.test.hide)
- ;
- }
- },
-
- unbind: {
- intent: function() {
- module.verbose('Removing hide intent event from document');
- if(hasTouch) {
- $document
- .off('touchstart' + elementNamespace)
- .off('touchmove' + elementNamespace)
- ;
- }
- $document
- .off(clickEvent + elementNamespace)
- ;
- }
- },
-
- filter: function(query) {
- var
- searchTerm = (query !== undefined)
- ? query
- : module.get.query(),
- afterFiltered = function() {
- if(module.is.multiple()) {
- module.filterActive();
- }
- if(query || (!query && module.get.activeItem().length == 0)) {
- module.select.firstUnfiltered();
- }
- if( module.has.allResultsFiltered() ) {
- if( settings.onNoResults.call(element, searchTerm) ) {
- if(settings.allowAdditions) {
- if(settings.hideAdditions) {
- module.verbose('User addition with no menu, setting empty style');
- module.set.empty();
- module.hideMenu();
- }
- }
- else {
- module.verbose('All items filtered, showing message', searchTerm);
- module.add.message(message.noResults);
- }
- }
- else {
- module.verbose('All items filtered, hiding dropdown', searchTerm);
- module.hideMenu();
- }
- }
- else {
- module.remove.empty();
- module.remove.message();
- }
- if(settings.allowAdditions) {
- module.add.userSuggestion(module.escape.htmlEntities(query));
- }
- if(module.is.searchSelection() && module.can.show() && module.is.focusedOnSearch() ) {
- module.show();
- }
- }
- ;
- if(settings.useLabels && module.has.maxSelections()) {
- return;
- }
- if(settings.apiSettings) {
- if( module.can.useAPI() ) {
- module.queryRemote(searchTerm, function() {
- if(settings.filterRemoteData) {
- module.filterItems(searchTerm);
- }
- var preSelected = $input.val();
- if(!Array.isArray(preSelected)) {
- preSelected = preSelected && preSelected!=="" ? preSelected.split(settings.delimiter) : [];
- }
- $.each(preSelected,function(index,value){
- $item.filter('[data-value="'+value+'"]')
- .addClass(className.filtered)
- ;
- });
- afterFiltered();
- });
- }
- else {
- module.error(error.noAPI);
- }
- }
- else {
- module.filterItems(searchTerm);
- afterFiltered();
- }
- },
-
- queryRemote: function(query, callback) {
- var
- apiSettings = {
- errorDuration : false,
- cache : 'local',
- throttle : settings.throttle,
- urlData : {
- query: query
- },
- onError: function() {
- module.add.message(message.serverError);
- callback();
- },
- onFailure: function() {
- module.add.message(message.serverError);
- callback();
- },
- onSuccess : function(response) {
- var
- values = response[fields.remoteValues]
- ;
- if (!Array.isArray(values)){
- values = [];
- }
- module.remove.message();
- var menuConfig = {};
- menuConfig[fields.values] = values;
- module.setup.menu(menuConfig);
-
- if(values.length===0 && !settings.allowAdditions) {
- module.add.message(message.noResults);
- }
- callback();
- }
- }
- ;
- if( !$module.api('get request') ) {
- module.setup.api();
- }
- apiSettings = $.extend(true, {}, apiSettings, settings.apiSettings);
- $module
- .api('setting', apiSettings)
- .api('query')
- ;
- },
-
- filterItems: function(query) {
- var
- searchTerm = module.remove.diacritics(query !== undefined
- ? query
- : module.get.query()
- ),
- results = null,
- escapedTerm = module.escape.string(searchTerm),
- regExpFlags = (settings.ignoreSearchCase ? 'i' : '') + 'gm',
- beginsWithRegExp = new RegExp('^' + escapedTerm, regExpFlags)
- ;
- // avoid loop if we're matching nothing
- if( module.has.query() ) {
- results = [];
-
- module.verbose('Searching for matching values', searchTerm);
- $item
- .each(function(){
- var
- $choice = $(this),
- text,
- value
- ;
- if($choice.hasClass(className.unfilterable)) {
- results.push(this);
- return true;
- }
- if(settings.match === 'both' || settings.match === 'text') {
- text = module.remove.diacritics(String(module.get.choiceText($choice, false)));
- if(text.search(beginsWithRegExp) !== -1) {
- results.push(this);
- return true;
- }
- else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text)) {
- results.push(this);
- return true;
- }
- else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, text)) {
- results.push(this);
- return true;
- }
- }
- if(settings.match === 'both' || settings.match === 'value') {
- value = module.remove.diacritics(String(module.get.choiceValue($choice, text)));
- if(value.search(beginsWithRegExp) !== -1) {
- results.push(this);
- return true;
- }
- else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, value)) {
- results.push(this);
- return true;
- }
- else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, value)) {
- results.push(this);
- return true;
- }
- }
- })
- ;
- }
- module.debug('Showing only matched items', searchTerm);
- module.remove.filteredItem();
- if(results) {
- $item
- .not(results)
- .addClass(className.filtered)
- ;
- }
-
- if(!module.has.query()) {
- $divider
- .removeClass(className.hidden);
- } else if(settings.hideDividers === true) {
- $divider
- .addClass(className.hidden);
- } else if(settings.hideDividers === 'empty') {
- $divider
- .removeClass(className.hidden)
- .filter(function() {
- // First find the last divider in this divider group
- // Dividers which are direct siblings are considered a group
- var lastDivider = $(this).nextUntil(selector.item);
-
- return (lastDivider.length ? lastDivider : $(this))
- // Count all non-filtered items until the next divider (or end of the dropdown)
- .nextUntil(selector.divider)
- .filter(selector.item + ":not(." + className.filtered + ")")
- // Hide divider if no items are found
- .length === 0;
- })
- .addClass(className.hidden);
- }
- },
-
- fuzzySearch: function(query, term) {
- var
- termLength = term.length,
- queryLength = query.length
- ;
- query = (settings.ignoreSearchCase ? query.toLowerCase() : query);
- term = (settings.ignoreSearchCase ? term.toLowerCase() : term);
- if(queryLength > termLength) {
- return false;
- }
- if(queryLength === termLength) {
- return (query === term);
- }
- search: for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
- var
- queryCharacter = query.charCodeAt(characterIndex)
- ;
- while(nextCharacterIndex < termLength) {
- if(term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
- continue search;
- }
- }
- return false;
- }
- return true;
- },
- exactSearch: function (query, term) {
- query = (settings.ignoreSearchCase ? query.toLowerCase() : query);
- term = (settings.ignoreSearchCase ? term.toLowerCase() : term);
- return term.indexOf(query) > -1;
-
- },
- filterActive: function() {
- if(settings.useLabels) {
- $item.filter('.' + className.active)
- .addClass(className.filtered)
- ;
- }
- },
-
- focusSearch: function(skipHandler) {
- if( module.has.search() && !module.is.focusedOnSearch() ) {
- if(skipHandler) {
- $module.off('focus' + eventNamespace, selector.search);
- $search.focus();
- $module.on('focus' + eventNamespace, selector.search, module.event.search.focus);
- }
- else {
- $search.focus();
- }
- }
- },
-
- blurSearch: function() {
- if( module.has.search() ) {
- $search.blur();
- }
- },
-
- forceSelection: function() {
- var
- $currentlySelected = $item.not(className.filtered).filter('.' + className.selected).eq(0),
- $activeItem = $item.not(className.filtered).filter('.' + className.active).eq(0),
- $selectedItem = ($currentlySelected.length > 0)
- ? $currentlySelected
- : $activeItem,
- hasSelected = ($selectedItem.length > 0)
- ;
- if(settings.allowAdditions || (hasSelected && !module.is.multiple())) {
- module.debug('Forcing partial selection to selected item', $selectedItem);
- module.event.item.click.call($selectedItem, {}, true);
- }
- else {
- module.remove.searchTerm();
- }
- },
-
- change: {
- values: function(values) {
- if(!settings.allowAdditions) {
- module.clear();
- }
- module.debug('Creating dropdown with specified values', values);
- var menuConfig = {};
- menuConfig[fields.values] = values;
- module.setup.menu(menuConfig);
- $.each(values, function(index, item) {
- if(item.selected == true) {
- module.debug('Setting initial selection to', item[fields.value]);
- module.set.selected(item[fields.value]);
- if(!module.is.multiple()) {
- return false;
- }
- }
- });
-
- if(module.has.selectInput()) {
- module.disconnect.selectObserver();
- $input.html('');
- $input.append('<option disabled selected value></option>');
- $.each(values, function(index, item) {
- var
- value = settings.templates.deQuote(item[fields.value]),
- name = settings.templates.escape(
- item[fields.name] || '',
- settings.preserveHTML
- )
- ;
- $input.append('<option value="' + value + '">' + name + '</option>');
- });
- module.observe.select();
- }
- }
- },
-
- event: {
- change: function() {
- if(!internalChange) {
- module.debug('Input changed, updating selection');
- module.set.selected();
- }
- },
- focus: function() {
- if(settings.showOnFocus && !activated && module.is.hidden() && !pageLostFocus) {
- module.show();
- }
- },
- blur: function(event) {
- pageLostFocus = (document.activeElement === this);
- if(!activated && !pageLostFocus) {
- module.remove.activeLabel();
- module.hide();
- }
- },
- mousedown: function() {
- if(module.is.searchSelection()) {
- // prevent menu hiding on immediate re-focus
- willRefocus = true;
- }
- else {
- // prevents focus callback from occurring on mousedown
- activated = true;
- }
- },
- mouseup: function() {
- if(module.is.searchSelection()) {
- // prevent menu hiding on immediate re-focus
- willRefocus = false;
- }
- else {
- activated = false;
- }
- },
- click: function(event) {
- var
- $target = $(event.target)
- ;
- // focus search
- if($target.is($module)) {
- if(!module.is.focusedOnSearch()) {
- module.focusSearch();
- }
- else {
- module.show();
- }
- }
- },
- search: {
- focus: function(event) {
- activated = true;
- if(module.is.multiple()) {
- module.remove.activeLabel();
- }
- if(settings.showOnFocus || (event.type !== 'focus' && event.type !== 'focusin')) {
- module.search();
- }
- },
- blur: function(event) {
- pageLostFocus = (document.activeElement === this);
- if(module.is.searchSelection() && !willRefocus) {
- if(!itemActivated && !pageLostFocus) {
- if(settings.forceSelection) {
- module.forceSelection();
- } else if(!settings.allowAdditions){
- module.remove.searchTerm();
- }
- module.hide();
- }
- }
- willRefocus = false;
- }
- },
- clearIcon: {
- click: function(event) {
- module.clear();
- if(module.is.searchSelection()) {
- module.remove.searchTerm();
- }
- module.hide();
- event.stopPropagation();
- }
- },
- icon: {
- click: function(event) {
- iconClicked=true;
- if(module.has.search()) {
- if(!module.is.active()) {
- if(settings.showOnFocus){
- module.focusSearch();
- } else {
- module.toggle();
- }
- } else {
- module.blurSearch();
- }
- } else {
- module.toggle();
- }
- }
- },
- text: {
- focus: function(event) {
- activated = true;
- module.focusSearch();
- }
- },
- input: function(event) {
- if(module.is.multiple() || module.is.searchSelection()) {
- module.set.filtered();
- }
- clearTimeout(module.timer);
- module.timer = setTimeout(module.search, settings.delay.search);
- },
- label: {
- click: function(event) {
- var
- $label = $(this),
- $labels = $module.find(selector.label),
- $activeLabels = $labels.filter('.' + className.active),
- $nextActive = $label.nextAll('.' + className.active),
- $prevActive = $label.prevAll('.' + className.active),
- $range = ($nextActive.length > 0)
- ? $label.nextUntil($nextActive).add($activeLabels).add($label)
- : $label.prevUntil($prevActive).add($activeLabels).add($label)
- ;
- if(event.shiftKey) {
- $activeLabels.removeClass(className.active);
- $range.addClass(className.active);
- }
- else if(event.ctrlKey) {
- $label.toggleClass(className.active);
- }
- else {
- $activeLabels.removeClass(className.active);
- $label.addClass(className.active);
- }
- settings.onLabelSelect.apply(this, $labels.filter('.' + className.active));
- }
- },
- remove: {
- click: function() {
- var
- $label = $(this).parent()
- ;
- if( $label.hasClass(className.active) ) {
- // remove all selected labels
- module.remove.activeLabels();
- }
- else {
- // remove this label only
- module.remove.activeLabels( $label );
- }
- }
- },
- test: {
- toggle: function(event) {
- var
- toggleBehavior = (module.is.multiple())
- ? module.show
- : module.toggle
- ;
- if(module.is.bubbledLabelClick(event) || module.is.bubbledIconClick(event)) {
- return;
- }
- if( module.determine.eventOnElement(event, toggleBehavior) ) {
- event.preventDefault();
- }
- },
- touch: function(event) {
- module.determine.eventOnElement(event, function() {
- if(event.type == 'touchstart') {
- module.timer = setTimeout(function() {
- module.hide();
- }, settings.delay.touch);
- }
- else if(event.type == 'touchmove') {
- clearTimeout(module.timer);
- }
- });
- event.stopPropagation();
- },
- hide: function(event) {
- if(module.determine.eventInModule(event, module.hide)){
- if(element.id && $(event.target).attr('for') === element.id){
- event.preventDefault();
- }
- }
- }
- },
- class: {
- mutation: function(mutations) {
- mutations.forEach(function(mutation) {
- if(mutation.attributeName === "class") {
- module.check.disabled();
- }
- });
- }
- },
- select: {
- mutation: function(mutations) {
- module.debug('<select> modified, recreating menu');
- if(module.is.selectMutation(mutations)) {
- module.disconnect.selectObserver();
- module.refresh();
- module.setup.select();
- module.set.selected();
- module.observe.select();
- }
- }
- },
- menu: {
- mutation: function(mutations) {
- var
- mutation = mutations[0],
- $addedNode = mutation.addedNodes
- ? $(mutation.addedNodes[0])
- : $(false),
- $removedNode = mutation.removedNodes
- ? $(mutation.removedNodes[0])
- : $(false),
- $changedNodes = $addedNode.add($removedNode),
- isUserAddition = $changedNodes.is(selector.addition) || $changedNodes.closest(selector.addition).length > 0,
- isMessage = $changedNodes.is(selector.message) || $changedNodes.closest(selector.message).length > 0
- ;
- if(isUserAddition || isMessage) {
- module.debug('Updating item selector cache');
- module.refreshItems();
- }
- else {
- module.debug('Menu modified, updating selector cache');
- module.refresh();
- }
- },
- mousedown: function() {
- itemActivated = true;
- },
- mouseup: function() {
- itemActivated = false;
- }
- },
- item: {
- mouseenter: function(event) {
- var
- $target = $(event.target),
- $item = $(this),
- $subMenu = $item.children(selector.menu),
- $otherMenus = $item.siblings(selector.item).children(selector.menu),
- hasSubMenu = ($subMenu.length > 0),
- isBubbledEvent = ($subMenu.find($target).length > 0)
- ;
- if( !isBubbledEvent && hasSubMenu ) {
- clearTimeout(module.itemTimer);
- module.itemTimer = setTimeout(function() {
- module.verbose('Showing sub-menu', $subMenu);
- $.each($otherMenus, function() {
- module.animate.hide(false, $(this));
- });
- module.animate.show(false, $subMenu);
- }, settings.delay.show);
- event.preventDefault();
- }
- },
- mouseleave: function(event) {
- var
- $subMenu = $(this).children(selector.menu)
- ;
- if($subMenu.length > 0) {
- clearTimeout(module.itemTimer);
- module.itemTimer = setTimeout(function() {
- module.verbose('Hiding sub-menu', $subMenu);
- module.animate.hide(false, $subMenu);
- }, settings.delay.hide);
- }
- },
- click: function (event, skipRefocus) {
- var
- $choice = $(this),
- $target = (event)
- ? $(event.target)
- : $(''),
- $subMenu = $choice.find(selector.menu),
- text = module.get.choiceText($choice),
- value = module.get.choiceValue($choice, text),
- hasSubMenu = ($subMenu.length > 0),
- isBubbledEvent = ($subMenu.find($target).length > 0)
- ;
- // prevents IE11 bug where menu receives focus even though `tabindex=-1`
- if (document.activeElement.tagName.toLowerCase() !== 'input') {
- $(document.activeElement).blur();
- }
- if(!isBubbledEvent && (!hasSubMenu || settings.allowCategorySelection)) {
- if(module.is.searchSelection()) {
- if(settings.allowAdditions) {
- module.remove.userAddition();
- }
- module.remove.searchTerm();
- if(!module.is.focusedOnSearch() && !(skipRefocus == true)) {
- module.focusSearch(true);
- }
- }
- if(!settings.useLabels) {
- module.remove.filteredItem();
- module.set.scrollPosition($choice);
- }
- module.determine.selectAction.call(this, text, value);
- }
- }
- },
-
- document: {
- // label selection should occur even when element has no focus
- keydown: function(event) {
- var
- pressedKey = event.which,
- isShortcutKey = module.is.inObject(pressedKey, keys)
- ;
- if(isShortcutKey) {
- var
- $label = $module.find(selector.label),
- $activeLabel = $label.filter('.' + className.active),
- activeValue = $activeLabel.data(metadata.value),
- labelIndex = $label.index($activeLabel),
- labelCount = $label.length,
- hasActiveLabel = ($activeLabel.length > 0),
- hasMultipleActive = ($activeLabel.length > 1),
- isFirstLabel = (labelIndex === 0),
- isLastLabel = (labelIndex + 1 == labelCount),
- isSearch = module.is.searchSelection(),
- isFocusedOnSearch = module.is.focusedOnSearch(),
- isFocused = module.is.focused(),
- caretAtStart = (isFocusedOnSearch && module.get.caretPosition(false) === 0),
- isSelectedSearch = (caretAtStart && module.get.caretPosition(true) !== 0),
- $nextLabel
- ;
- if(isSearch && !hasActiveLabel && !isFocusedOnSearch) {
- return;
- }
-
- if(pressedKey == keys.leftArrow) {
- // activate previous label
- if((isFocused || caretAtStart) && !hasActiveLabel) {
- module.verbose('Selecting previous label');
- $label.last().addClass(className.active);
- }
- else if(hasActiveLabel) {
- if(!event.shiftKey) {
- module.verbose('Selecting previous label');
- $label.removeClass(className.active);
- }
- else {
- module.verbose('Adding previous label to selection');
- }
- if(isFirstLabel && !hasMultipleActive) {
- $activeLabel.addClass(className.active);
- }
- else {
- $activeLabel.prev(selector.siblingLabel)
- .addClass(className.active)
- .end()
- ;
- }
- event.preventDefault();
- }
- }
- else if(pressedKey == keys.rightArrow) {
- // activate first label
- if(isFocused && !hasActiveLabel) {
- $label.first().addClass(className.active);
- }
- // activate next label
- if(hasActiveLabel) {
- if(!event.shiftKey) {
- module.verbose('Selecting next label');
- $label.removeClass(className.active);
- }
- else {
- module.verbose('Adding next label to selection');
- }
- if(isLastLabel) {
- if(isSearch) {
- if(!isFocusedOnSearch) {
- module.focusSearch();
- }
- else {
- $label.removeClass(className.active);
- }
- }
- else if(hasMultipleActive) {
- $activeLabel.next(selector.siblingLabel).addClass(className.active);
- }
- else {
- $activeLabel.addClass(className.active);
- }
- }
- else {
- $activeLabel.next(selector.siblingLabel).addClass(className.active);
- }
- event.preventDefault();
- }
- }
- else if(pressedKey == keys.deleteKey || pressedKey == keys.backspace) {
- if(hasActiveLabel) {
- module.verbose('Removing active labels');
- if(isLastLabel) {
- if(isSearch && !isFocusedOnSearch) {
- module.focusSearch();
- }
- }
- $activeLabel.last().next(selector.siblingLabel).addClass(className.active);
- module.remove.activeLabels($activeLabel);
- event.preventDefault();
- }
- else if(caretAtStart && !isSelectedSearch && !hasActiveLabel && pressedKey == keys.backspace) {
- module.verbose('Removing last label on input backspace');
- $activeLabel = $label.last().addClass(className.active);
- module.remove.activeLabels($activeLabel);
- }
- }
- else {
- $activeLabel.removeClass(className.active);
- }
- }
- }
- },
-
- keydown: function(event) {
- var
- pressedKey = event.which,
- isShortcutKey = module.is.inObject(pressedKey, keys)
- ;
- if(isShortcutKey) {
- var
- $currentlySelected = $item.not(selector.unselectable).filter('.' + className.selected).eq(0),
- $activeItem = $menu.children('.' + className.active).eq(0),
- $selectedItem = ($currentlySelected.length > 0)
- ? $currentlySelected
- : $activeItem,
- $visibleItems = ($selectedItem.length > 0)
- ? $selectedItem.siblings(':not(.' + className.filtered +')').addBack()
- : $menu.children(':not(.' + className.filtered +')'),
- $subMenu = $selectedItem.children(selector.menu),
- $parentMenu = $selectedItem.closest(selector.menu),
- inVisibleMenu = ($parentMenu.hasClass(className.visible) || $parentMenu.hasClass(className.animating) || $parentMenu.parent(selector.menu).length > 0),
- hasSubMenu = ($subMenu.length> 0),
- hasSelectedItem = ($selectedItem.length > 0),
- selectedIsSelectable = ($selectedItem.not(selector.unselectable).length > 0),
- delimiterPressed = (pressedKey == keys.delimiter && settings.allowAdditions && module.is.multiple()),
- isAdditionWithoutMenu = (settings.allowAdditions && settings.hideAdditions && (pressedKey == keys.enter || delimiterPressed) && selectedIsSelectable),
- $nextItem,
- isSubMenuItem,
- newIndex
- ;
- // allow selection with menu closed
- if(isAdditionWithoutMenu) {
- module.verbose('Selecting item from keyboard shortcut', $selectedItem);
- module.event.item.click.call($selectedItem, event);
- if(module.is.searchSelection()) {
- module.remove.searchTerm();
- }
- if(module.is.multiple()){
- event.preventDefault();
- }
- }
-
- // visible menu keyboard shortcuts
- if( module.is.visible() ) {
-
- // enter (select or open sub-menu)
- if(pressedKey == keys.enter || delimiterPressed) {
- if(pressedKey == keys.enter && hasSelectedItem && hasSubMenu && !settings.allowCategorySelection) {
- module.verbose('Pressed enter on unselectable category, opening sub menu');
- pressedKey = keys.rightArrow;
- }
- else if(selectedIsSelectable) {
- module.verbose('Selecting item from keyboard shortcut', $selectedItem);
- module.event.item.click.call($selectedItem, event);
- if(module.is.searchSelection()) {
- module.remove.searchTerm();
- if(module.is.multiple()) {
- $search.focus();
- }
- }
- }
- event.preventDefault();
- }
-
- // sub-menu actions
- if(hasSelectedItem) {
-
- if(pressedKey == keys.leftArrow) {
-
- isSubMenuItem = ($parentMenu[0] !== $menu[0]);
-
- if(isSubMenuItem) {
- module.verbose('Left key pressed, closing sub-menu');
- module.animate.hide(false, $parentMenu);
- $selectedItem
- .removeClass(className.selected)
- ;
- $parentMenu
- .closest(selector.item)
- .addClass(className.selected)
- ;
- event.preventDefault();
- }
- }
-
- // right arrow (show sub-menu)
- if(pressedKey == keys.rightArrow) {
- if(hasSubMenu) {
- module.verbose('Right key pressed, opening sub-menu');
- module.animate.show(false, $subMenu);
- $selectedItem
- .removeClass(className.selected)
- ;
- $subMenu
- .find(selector.item).eq(0)
- .addClass(className.selected)
- ;
- event.preventDefault();
- }
- }
- }
-
- // up arrow (traverse menu up)
- if(pressedKey == keys.upArrow) {
- $nextItem = (hasSelectedItem && inVisibleMenu)
- ? $selectedItem.prevAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
- : $item.eq(0)
- ;
- if($visibleItems.index( $nextItem ) < 0) {
- module.verbose('Up key pressed but reached top of current menu');
- event.preventDefault();
- return;
- }
- else {
- module.verbose('Up key pressed, changing active item');
- $selectedItem
- .removeClass(className.selected)
- ;
- $nextItem
- .addClass(className.selected)
- ;
- module.set.scrollPosition($nextItem);
- if(settings.selectOnKeydown && module.is.single()) {
- module.set.selectedItem($nextItem);
- }
- }
- event.preventDefault();
- }
-
- // down arrow (traverse menu down)
- if(pressedKey == keys.downArrow) {
- $nextItem = (hasSelectedItem && inVisibleMenu)
- ? $nextItem = $selectedItem.nextAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
- : $item.eq(0)
- ;
- if($nextItem.length === 0) {
- module.verbose('Down key pressed but reached bottom of current menu');
- event.preventDefault();
- return;
- }
- else {
- module.verbose('Down key pressed, changing active item');
- $item
- .removeClass(className.selected)
- ;
- $nextItem
- .addClass(className.selected)
- ;
- module.set.scrollPosition($nextItem);
- if(settings.selectOnKeydown && module.is.single()) {
- module.set.selectedItem($nextItem);
- }
- }
- event.preventDefault();
- }
-
- // page down (show next page)
- if(pressedKey == keys.pageUp) {
- module.scrollPage('up');
- event.preventDefault();
- }
- if(pressedKey == keys.pageDown) {
- module.scrollPage('down');
- event.preventDefault();
- }
-
- // escape (close menu)
- if(pressedKey == keys.escape) {
- module.verbose('Escape key pressed, closing dropdown');
- module.hide();
- }
-
- }
- else {
- // delimiter key
- if(delimiterPressed) {
- event.preventDefault();
- }
- // down arrow (open menu)
- if(pressedKey == keys.downArrow && !module.is.visible()) {
- module.verbose('Down key pressed, showing dropdown');
- module.show();
- event.preventDefault();
- }
- }
- }
- else {
- if( !module.has.search() ) {
- module.set.selectedLetter( String.fromCharCode(pressedKey) );
- }
- }
- }
- },
-
- trigger: {
- change: function() {
- var
- inputElement = $input[0]
- ;
- if(inputElement) {
- var events = document.createEvent('HTMLEvents');
- module.verbose('Triggering native change event');
- events.initEvent('change', true, false);
- inputElement.dispatchEvent(events);
- }
- }
- },
-
- determine: {
- selectAction: function(text, value) {
- selectActionActive = true;
- module.verbose('Determining action', settings.action);
- if( $.isFunction( module.action[settings.action] ) ) {
- module.verbose('Triggering preset action', settings.action, text, value);
- module.action[ settings.action ].call(element, text, value, this);
- }
- else if( $.isFunction(settings.action) ) {
- module.verbose('Triggering user action', settings.action, text, value);
- settings.action.call(element, text, value, this);
- }
- else {
- module.error(error.action, settings.action);
- }
- selectActionActive = false;
- },
- eventInModule: function(event, callback) {
- var
- $target = $(event.target),
- inDocument = ($target.closest(document.documentElement).length > 0),
- inModule = ($target.closest($module).length > 0)
- ;
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- if(inDocument && !inModule) {
- module.verbose('Triggering event', callback);
- callback();
- return true;
- }
- else {
- module.verbose('Event occurred in dropdown, canceling callback');
- return false;
- }
- },
- eventOnElement: function(event, callback) {
- var
- $target = $(event.target),
- $label = $target.closest(selector.siblingLabel),
- inVisibleDOM = document.body.contains(event.target),
- notOnLabel = ($module.find($label).length === 0 || !(module.is.multiple() && settings.useLabels)),
- notInMenu = ($target.closest($menu).length === 0)
- ;
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- if(inVisibleDOM && notOnLabel && notInMenu) {
- module.verbose('Triggering event', callback);
- callback();
- return true;
- }
- else {
- module.verbose('Event occurred in dropdown menu, canceling callback');
- return false;
- }
- }
- },
-
- action: {
-
- nothing: function() {},
-
- activate: function(text, value, element) {
- value = (value !== undefined)
- ? value
- : text
- ;
- if( module.can.activate( $(element) ) ) {
- module.set.selected(value, $(element));
- if(!module.is.multiple()) {
- module.hideAndClear();
- }
- }
- },
-
- select: function(text, value, element) {
- value = (value !== undefined)
- ? value
- : text
- ;
- if( module.can.activate( $(element) ) ) {
- module.set.value(value, text, $(element));
- if(!module.is.multiple()) {
- module.hideAndClear();
- }
- }
- },
-
- combo: function(text, value, element) {
- value = (value !== undefined)
- ? value
- : text
- ;
- module.set.selected(value, $(element));
- module.hideAndClear();
- },
-
- hide: function(text, value, element) {
- module.set.value(value, text, $(element));
- module.hideAndClear();
- }
-
- },
-
- get: {
- id: function() {
- return id;
- },
- defaultText: function() {
- return $module.data(metadata.defaultText);
- },
- defaultValue: function() {
- return $module.data(metadata.defaultValue);
- },
- placeholderText: function() {
- if(settings.placeholder != 'auto' && typeof settings.placeholder == 'string') {
- return settings.placeholder;
- }
- return $module.data(metadata.placeholderText) || '';
- },
- text: function() {
- return settings.preserveHTML ? $text.html() : $text.text();
- },
- query: function() {
- return String($search.val()).trim();
- },
- searchWidth: function(value) {
- value = (value !== undefined)
- ? value
- : $search.val()
- ;
- $sizer.text(value);
- // prevent rounding issues
- return Math.ceil( $sizer.width() + 1);
- },
- selectionCount: function() {
- var
- values = module.get.values(),
- count
- ;
- count = ( module.is.multiple() )
- ? Array.isArray(values)
- ? values.length
- : 0
- : (module.get.value() !== '')
- ? 1
- : 0
- ;
- return count;
- },
- transition: function($subMenu) {
- return (settings.transition == 'auto')
- ? module.is.upward($subMenu)
- ? 'slide up'
- : 'slide down'
- : settings.transition
- ;
- },
- userValues: function() {
- var
- values = module.get.values()
- ;
- if(!values) {
- return false;
- }
- values = Array.isArray(values)
- ? values
- : [values]
- ;
- return $.grep(values, function(value) {
- return (module.get.item(value) === false);
- });
- },
- uniqueArray: function(array) {
- return $.grep(array, function (value, index) {
- return $.inArray(value, array) === index;
- });
- },
- caretPosition: function(returnEndPos) {
- var
- input = $search.get(0),
- range,
- rangeLength
- ;
- if(returnEndPos && 'selectionEnd' in input){
- return input.selectionEnd;
- }
- else if(!returnEndPos && 'selectionStart' in input) {
- return input.selectionStart;
- }
- if (document.selection) {
- input.focus();
- range = document.selection.createRange();
- rangeLength = range.text.length;
- if(returnEndPos) {
- return rangeLength;
- }
- range.moveStart('character', -input.value.length);
- return range.text.length - rangeLength;
- }
- },
- value: function() {
- var
- value = ($input.length > 0)
- ? $input.val()
- : $module.data(metadata.value),
- isEmptyMultiselect = (Array.isArray(value) && value.length === 1 && value[0] === '')
- ;
- // prevents placeholder element from being selected when multiple
- return (value === undefined || isEmptyMultiselect)
- ? ''
- : value
- ;
- },
- values: function() {
- var
- value = module.get.value()
- ;
- if(value === '') {
- return '';
- }
- return ( !module.has.selectInput() && module.is.multiple() )
- ? (typeof value == 'string') // delimited string
- ? module.escape.htmlEntities(value).split(settings.delimiter)
- : ''
- : value
- ;
- },
- remoteValues: function() {
- var
- values = module.get.values(),
- remoteValues = false
- ;
- if(values) {
- if(typeof values == 'string') {
- values = [values];
- }
- $.each(values, function(index, value) {
- var
- name = module.read.remoteData(value)
- ;
- module.verbose('Restoring value from session data', name, value);
- if(name) {
- if(!remoteValues) {
- remoteValues = {};
- }
- remoteValues[value] = name;
- }
- });
- }
- return remoteValues;
- },
- choiceText: function($choice, preserveHTML) {
- preserveHTML = (preserveHTML !== undefined)
- ? preserveHTML
- : settings.preserveHTML
- ;
- if($choice) {
- if($choice.find(selector.menu).length > 0) {
- module.verbose('Retrieving text of element with sub-menu');
- $choice = $choice.clone();
- $choice.find(selector.menu).remove();
- $choice.find(selector.menuIcon).remove();
- }
- return ($choice.data(metadata.text) !== undefined)
- ? $choice.data(metadata.text)
- : (preserveHTML)
- ? $choice.html().trim()
- : $choice.text().trim()
- ;
- }
- },
- choiceValue: function($choice, choiceText) {
- choiceText = choiceText || module.get.choiceText($choice);
- if(!$choice) {
- return false;
- }
- return ($choice.data(metadata.value) !== undefined)
- ? String( $choice.data(metadata.value) )
- : (typeof choiceText === 'string')
- ? String(
- settings.ignoreSearchCase
- ? choiceText.toLowerCase()
- : choiceText
- ).trim()
- : String(choiceText)
- ;
- },
- inputEvent: function() {
- var
- input = $search[0]
- ;
- if(input) {
- return (input.oninput !== undefined)
- ? 'input'
- : (input.onpropertychange !== undefined)
- ? 'propertychange'
- : 'keyup'
- ;
- }
- return false;
- },
- selectValues: function() {
- var
- select = {},
- oldGroup = [],
- values = []
- ;
- $module
- .find('option')
- .each(function() {
- var
- $option = $(this),
- name = $option.html(),
- disabled = $option.attr('disabled'),
- value = ( $option.attr('value') !== undefined )
- ? $option.attr('value')
- : name,
- text = ( $option.data(metadata.text) !== undefined )
- ? $option.data(metadata.text)
- : name,
- group = $option.parent('optgroup')
- ;
- if(settings.placeholder === 'auto' && value === '') {
- select.placeholder = name;
- }
- else {
- if(group.length !== oldGroup.length || group[0] !== oldGroup[0]) {
- values.push({
- type: 'header',
- divider: settings.headerDivider,
- name: group.attr('label') || ''
- });
- oldGroup = group;
- }
- values.push({
- name : name,
- value : value,
- text : text,
- disabled : disabled
- });
- }
- })
- ;
- if(settings.placeholder && settings.placeholder !== 'auto') {
- module.debug('Setting placeholder value to', settings.placeholder);
- select.placeholder = settings.placeholder;
- }
- if(settings.sortSelect) {
- if(settings.sortSelect === true) {
- values.sort(function(a, b) {
- return a.name.localeCompare(b.name);
- });
- } else if(settings.sortSelect === 'natural') {
- values.sort(function(a, b) {
- return (a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
- });
- } else if($.isFunction(settings.sortSelect)) {
- values.sort(settings.sortSelect);
- }
- select[fields.values] = values;
- module.debug('Retrieved and sorted values from select', select);
- }
- else {
- select[fields.values] = values;
- module.debug('Retrieved values from select', select);
- }
- return select;
- },
- activeItem: function() {
- return $item.filter('.' + className.active);
- },
- selectedItem: function() {
- var
- $selectedItem = $item.not(selector.unselectable).filter('.' + className.selected)
- ;
- return ($selectedItem.length > 0)
- ? $selectedItem
- : $item.eq(0)
- ;
- },
- itemWithAdditions: function(value) {
- var
- $items = module.get.item(value),
- $userItems = module.create.userChoice(value),
- hasUserItems = ($userItems && $userItems.length > 0)
- ;
- if(hasUserItems) {
- $items = ($items.length > 0)
- ? $items.add($userItems)
- : $userItems
- ;
- }
- return $items;
- },
- item: function(value, strict) {
- var
- $selectedItem = false,
- shouldSearch,
- isMultiple
- ;
- value = (value !== undefined)
- ? value
- : ( module.get.values() !== undefined)
- ? module.get.values()
- : module.get.text()
- ;
- isMultiple = (module.is.multiple() && Array.isArray(value));
- shouldSearch = (isMultiple)
- ? (value.length > 0)
- : (value !== undefined && value !== null)
- ;
- strict = (value === '' || value === false || value === true)
- ? true
- : strict || false
- ;
- if(shouldSearch) {
- $item
- .each(function() {
- var
- $choice = $(this),
- optionText = module.get.choiceText($choice),
- optionValue = module.get.choiceValue($choice, optionText)
- ;
- // safe early exit
- if(optionValue === null || optionValue === undefined) {
- return;
- }
- if(isMultiple) {
- if($.inArray(module.escape.htmlEntities(String(optionValue)), value.map(function(v){return String(v);})) !== -1) {
- $selectedItem = ($selectedItem)
- ? $selectedItem.add($choice)
- : $choice
- ;
- }
- }
- else if(strict) {
- module.verbose('Ambiguous dropdown value using strict type check', $choice, value);
- if( optionValue === value) {
- $selectedItem = $choice;
- return true;
- }
- }
- else {
- if(settings.ignoreCase) {
- optionValue = optionValue.toLowerCase();
- value = value.toLowerCase();
- }
- if(module.escape.htmlEntities(String(optionValue)) === module.escape.htmlEntities(String(value))) {
- module.verbose('Found select item by value', optionValue, value);
- $selectedItem = $choice;
- return true;
- }
- }
- })
- ;
- }
- return $selectedItem;
- }
- },
-
- check: {
- maxSelections: function(selectionCount) {
- if(settings.maxSelections) {
- selectionCount = (selectionCount !== undefined)
- ? selectionCount
- : module.get.selectionCount()
- ;
- if(selectionCount >= settings.maxSelections) {
- module.debug('Maximum selection count reached');
- if(settings.useLabels) {
- $item.addClass(className.filtered);
- module.add.message(message.maxSelections);
- }
- return true;
- }
- else {
- module.verbose('No longer at maximum selection count');
- module.remove.message();
- module.remove.filteredItem();
- if(module.is.searchSelection()) {
- module.filterItems();
- }
- return false;
- }
- }
- return true;
- },
- disabled: function(){
- $search.attr('tabindex',module.is.disabled() ? -1 : 0);
- }
- },
-
- restore: {
- defaults: function(preventChangeTrigger) {
- module.clear(preventChangeTrigger);
- module.restore.defaultText();
- module.restore.defaultValue();
- },
- defaultText: function() {
- var
- defaultText = module.get.defaultText(),
- placeholderText = module.get.placeholderText
- ;
- if(defaultText === placeholderText) {
- module.debug('Restoring default placeholder text', defaultText);
- module.set.placeholderText(defaultText);
- }
- else {
- module.debug('Restoring default text', defaultText);
- module.set.text(defaultText);
- }
- },
- placeholderText: function() {
- module.set.placeholderText();
- },
- defaultValue: function() {
- var
- defaultValue = module.get.defaultValue()
- ;
- if(defaultValue !== undefined) {
- module.debug('Restoring default value', defaultValue);
- if(defaultValue !== '') {
- module.set.value(defaultValue);
- module.set.selected();
- }
- else {
- module.remove.activeItem();
- module.remove.selectedItem();
- }
- }
- },
- labels: function() {
- if(settings.allowAdditions) {
- if(!settings.useLabels) {
- module.error(error.labels);
- settings.useLabels = true;
- }
- module.debug('Restoring selected values');
- module.create.userLabels();
- }
- module.check.maxSelections();
- },
- selected: function() {
- module.restore.values();
- if(module.is.multiple()) {
- module.debug('Restoring previously selected values and labels');
- module.restore.labels();
- }
- else {
- module.debug('Restoring previously selected values');
- }
- },
- values: function() {
- // prevents callbacks from occurring on initial load
- module.set.initialLoad();
- if(settings.apiSettings && settings.saveRemoteData && module.get.remoteValues()) {
- module.restore.remoteValues();
- }
- else {
- module.set.selected();
- }
- var value = module.get.value();
- if(value && value !== '' && !(Array.isArray(value) && value.length === 0)) {
- $input.removeClass(className.noselection);
- } else {
- $input.addClass(className.noselection);
- }
- module.remove.initialLoad();
- },
- remoteValues: function() {
- var
- values = module.get.remoteValues()
- ;
- module.debug('Recreating selected from session data', values);
- if(values) {
- if( module.is.single() ) {
- $.each(values, function(value, name) {
- module.set.text(name);
- });
- }
- else {
- $.each(values, function(value, name) {
- module.add.label(value, name);
- });
- }
- }
- }
- },
-
- read: {
- remoteData: function(value) {
- var
- name
- ;
- if(window.Storage === undefined) {
- module.error(error.noStorage);
- return;
- }
- name = sessionStorage.getItem(value);
- return (name !== undefined)
- ? name
- : false
- ;
- }
- },
-
- save: {
- defaults: function() {
- module.save.defaultText();
- module.save.placeholderText();
- module.save.defaultValue();
- },
- defaultValue: function() {
- var
- value = module.get.value()
- ;
- module.verbose('Saving default value as', value);
- $module.data(metadata.defaultValue, value);
- },
- defaultText: function() {
- var
- text = module.get.text()
- ;
- module.verbose('Saving default text as', text);
- $module.data(metadata.defaultText, text);
- },
- placeholderText: function() {
- var
- text
- ;
- if(settings.placeholder !== false && $text.hasClass(className.placeholder)) {
- text = module.get.text();
- module.verbose('Saving placeholder text as', text);
- $module.data(metadata.placeholderText, text);
- }
- },
- remoteData: function(name, value) {
- if(window.Storage === undefined) {
- module.error(error.noStorage);
- return;
- }
- module.verbose('Saving remote data to session storage', value, name);
- sessionStorage.setItem(value, name);
- }
- },
-
- clear: function(preventChangeTrigger) {
- if(module.is.multiple() && settings.useLabels) {
- module.remove.labels();
- }
- else {
- module.remove.activeItem();
- module.remove.selectedItem();
- module.remove.filteredItem();
- }
- module.set.placeholderText();
- module.clearValue(preventChangeTrigger);
- },
-
- clearValue: function(preventChangeTrigger) {
- module.set.value('', null, null, preventChangeTrigger);
- },
-
- scrollPage: function(direction, $selectedItem) {
- var
- $currentItem = $selectedItem || module.get.selectedItem(),
- $menu = $currentItem.closest(selector.menu),
- menuHeight = $menu.outerHeight(),
- currentScroll = $menu.scrollTop(),
- itemHeight = $item.eq(0).outerHeight(),
- itemsPerPage = Math.floor(menuHeight / itemHeight),
- maxScroll = $menu.prop('scrollHeight'),
- newScroll = (direction == 'up')
- ? currentScroll - (itemHeight * itemsPerPage)
- : currentScroll + (itemHeight * itemsPerPage),
- $selectableItem = $item.not(selector.unselectable),
- isWithinRange,
- $nextSelectedItem,
- elementIndex
- ;
- elementIndex = (direction == 'up')
- ? $selectableItem.index($currentItem) - itemsPerPage
- : $selectableItem.index($currentItem) + itemsPerPage
- ;
- isWithinRange = (direction == 'up')
- ? (elementIndex >= 0)
- : (elementIndex < $selectableItem.length)
- ;
- $nextSelectedItem = (isWithinRange)
- ? $selectableItem.eq(elementIndex)
- : (direction == 'up')
- ? $selectableItem.first()
- : $selectableItem.last()
- ;
- if($nextSelectedItem.length > 0) {
- module.debug('Scrolling page', direction, $nextSelectedItem);
- $currentItem
- .removeClass(className.selected)
- ;
- $nextSelectedItem
- .addClass(className.selected)
- ;
- if(settings.selectOnKeydown && module.is.single()) {
- module.set.selectedItem($nextSelectedItem);
- }
- $menu
- .scrollTop(newScroll)
- ;
- }
- },
-
- set: {
- filtered: function() {
- var
- isMultiple = module.is.multiple(),
- isSearch = module.is.searchSelection(),
- isSearchMultiple = (isMultiple && isSearch),
- searchValue = (isSearch)
- ? module.get.query()
- : '',
- hasSearchValue = (typeof searchValue === 'string' && searchValue.length > 0),
- searchWidth = module.get.searchWidth(),
- valueIsSet = searchValue !== ''
- ;
- if(isMultiple && hasSearchValue) {
- module.verbose('Adjusting input width', searchWidth, settings.glyphWidth);
- $search.css('width', searchWidth);
- }
- if(hasSearchValue || (isSearchMultiple && valueIsSet)) {
- module.verbose('Hiding placeholder text');
- $text.addClass(className.filtered);
- }
- else if(!isMultiple || (isSearchMultiple && !valueIsSet)) {
- module.verbose('Showing placeholder text');
- $text.removeClass(className.filtered);
- }
- },
- empty: function() {
- $module.addClass(className.empty);
- },
- loading: function() {
- $module.addClass(className.loading);
- },
- placeholderText: function(text) {
- text = text || module.get.placeholderText();
- module.debug('Setting placeholder text', text);
- module.set.text(text);
- $text.addClass(className.placeholder);
- },
- tabbable: function() {
- if( module.is.searchSelection() ) {
- module.debug('Added tabindex to searchable dropdown');
- $search
- .val('')
- ;
- module.check.disabled();
- $menu
- .attr('tabindex', -1)
- ;
- }
- else {
- module.debug('Added tabindex to dropdown');
- if( $module.attr('tabindex') === undefined) {
- $module
- .attr('tabindex', 0)
- ;
- $menu
- .attr('tabindex', -1)
- ;
- }
- }
- },
- initialLoad: function() {
- module.verbose('Setting initial load');
- initialLoad = true;
- },
- activeItem: function($item) {
- if( settings.allowAdditions && $item.filter(selector.addition).length > 0 ) {
- $item.addClass(className.filtered);
- }
- else {
- $item.addClass(className.active);
- }
- },
- partialSearch: function(text) {
- var
- length = module.get.query().length
- ;
- $search.val( text.substr(0, length));
- },
- scrollPosition: function($item, forceScroll) {
- var
- edgeTolerance = 5,
- $menu,
- hasActive,
- offset,
- itemHeight,
- itemOffset,
- menuOffset,
- menuScroll,
- menuHeight,
- abovePage,
- belowPage
- ;
-
- $item = $item || module.get.selectedItem();
- $menu = $item.closest(selector.menu);
- hasActive = ($item && $item.length > 0);
- forceScroll = (forceScroll !== undefined)
- ? forceScroll
- : false
- ;
- if(module.get.activeItem().length === 0){
- forceScroll = false;
- }
- if($item && $menu.length > 0 && hasActive) {
- itemOffset = $item.position().top;
-
- $menu.addClass(className.loading);
- menuScroll = $menu.scrollTop();
- menuOffset = $menu.offset().top;
- itemOffset = $item.offset().top;
- offset = menuScroll - menuOffset + itemOffset;
- if(!forceScroll) {
- menuHeight = $menu.height();
- belowPage = menuScroll + menuHeight < (offset + edgeTolerance);
- abovePage = ((offset - edgeTolerance) < menuScroll);
- }
- module.debug('Scrolling to active item', offset);
- if(forceScroll || abovePage || belowPage) {
- $menu.scrollTop(offset);
- }
- $menu.removeClass(className.loading);
- }
- },
- text: function(text) {
- if(settings.action === 'combo') {
- module.debug('Changing combo button text', text, $combo);
- if(settings.preserveHTML) {
- $combo.html(text);
- }
- else {
- $combo.text(text);
- }
- }
- else if(settings.action === 'activate') {
- if(text !== module.get.placeholderText()) {
- $text.removeClass(className.placeholder);
- }
- module.debug('Changing text', text, $text);
- $text
- .removeClass(className.filtered)
- ;
- if(settings.preserveHTML) {
- $text.html(text);
- }
- else {
- $text.text(text);
- }
- }
- },
- selectedItem: function($item) {
- var
- value = module.get.choiceValue($item),
- searchText = module.get.choiceText($item, false),
- text = module.get.choiceText($item, true)
- ;
- module.debug('Setting user selection to item', $item);
- module.remove.activeItem();
- module.set.partialSearch(searchText);
- module.set.activeItem($item);
- module.set.selected(value, $item);
- module.set.text(text);
- },
- selectedLetter: function(letter) {
- var
- $selectedItem = $item.filter('.' + className.selected),
- alreadySelectedLetter = $selectedItem.length > 0 && module.has.firstLetter($selectedItem, letter),
- $nextValue = false,
- $nextItem
- ;
- // check next of same letter
- if(alreadySelectedLetter) {
- $nextItem = $selectedItem.nextAll($item).eq(0);
- if( module.has.firstLetter($nextItem, letter) ) {
- $nextValue = $nextItem;
- }
- }
- // check all values
- if(!$nextValue) {
- $item
- .each(function(){
- if(module.has.firstLetter($(this), letter)) {
- $nextValue = $(this);
- return false;
- }
- })
- ;
- }
- // set next value
- if($nextValue) {
- module.verbose('Scrolling to next value with letter', letter);
- module.set.scrollPosition($nextValue);
- $selectedItem.removeClass(className.selected);
- $nextValue.addClass(className.selected);
- if(settings.selectOnKeydown && module.is.single()) {
- module.set.selectedItem($nextValue);
- }
- }
- },
- direction: function($menu) {
- if(settings.direction == 'auto') {
- // reset position, remove upward if it's base menu
- if (!$menu) {
- module.remove.upward();
- } else if (module.is.upward($menu)) {
- //we need make sure when make assertion openDownward for $menu, $menu does not have upward class
- module.remove.upward($menu);
- }
-
- if(module.can.openDownward($menu)) {
- module.remove.upward($menu);
- }
- else {
- module.set.upward($menu);
- }
- if(!module.is.leftward($menu) && !module.can.openRightward($menu)) {
- module.set.leftward($menu);
- }
- }
- else if(settings.direction == 'upward') {
- module.set.upward($menu);
- }
- },
- upward: function($currentMenu) {
- var $element = $currentMenu || $module;
- $element.addClass(className.upward);
- },
- leftward: function($currentMenu) {
- var $element = $currentMenu || $menu;
- $element.addClass(className.leftward);
- },
- value: function(value, text, $selected, preventChangeTrigger) {
- if(value !== undefined && value !== '' && !(Array.isArray(value) && value.length === 0)) {
- $input.removeClass(className.noselection);
- } else {
- $input.addClass(className.noselection);
- }
- var
- escapedValue = module.escape.value(value),
- hasInput = ($input.length > 0),
- currentValue = module.get.values(),
- stringValue = (value !== undefined)
- ? String(value)
- : value,
- newValue
- ;
- if(hasInput) {
- if(!settings.allowReselection && stringValue == currentValue) {
- module.verbose('Skipping value update already same value', value, currentValue);
- if(!module.is.initialLoad()) {
- return;
- }
- }
-
- if( module.is.single() && module.has.selectInput() && module.can.extendSelect() ) {
- module.debug('Adding user option', value);
- module.add.optionValue(value);
- }
- module.debug('Updating input value', escapedValue, currentValue);
- internalChange = true;
- $input
- .val(escapedValue)
- ;
- if(settings.fireOnInit === false && module.is.initialLoad()) {
- module.debug('Input native change event ignored on initial load');
- }
- else if(preventChangeTrigger !== true) {
- module.trigger.change();
- }
- internalChange = false;
- }
- else {
- module.verbose('Storing value in metadata', escapedValue, $input);
- if(escapedValue !== currentValue) {
- $module.data(metadata.value, stringValue);
- }
- }
- if(settings.fireOnInit === false && module.is.initialLoad()) {
- module.verbose('No callback on initial load', settings.onChange);
- }
- else if(preventChangeTrigger !== true) {
- settings.onChange.call(element, value, text, $selected);
- }
- },
- active: function() {
- $module
- .addClass(className.active)
- ;
- },
- multiple: function() {
- $module.addClass(className.multiple);
- },
- visible: function() {
- $module.addClass(className.visible);
- },
- exactly: function(value, $selectedItem) {
- module.debug('Setting selected to exact values');
- module.clear();
- module.set.selected(value, $selectedItem);
- },
- selected: function(value, $selectedItem) {
- var
- isMultiple = module.is.multiple()
- ;
- $selectedItem = (settings.allowAdditions)
- ? $selectedItem || module.get.itemWithAdditions(value)
- : $selectedItem || module.get.item(value)
- ;
- if(!$selectedItem) {
- return;
- }
- module.debug('Setting selected menu item to', $selectedItem);
- if(module.is.multiple()) {
- module.remove.searchWidth();
- }
- if(module.is.single()) {
- module.remove.activeItem();
- module.remove.selectedItem();
- }
- else if(settings.useLabels) {
- module.remove.selectedItem();
- }
- // select each item
- $selectedItem
- .each(function() {
- var
- $selected = $(this),
- selectedText = module.get.choiceText($selected),
- selectedValue = module.get.choiceValue($selected, selectedText),
-
- isFiltered = $selected.hasClass(className.filtered),
- isActive = $selected.hasClass(className.active),
- isUserValue = $selected.hasClass(className.addition),
- shouldAnimate = (isMultiple && $selectedItem.length == 1)
- ;
- if(isMultiple) {
- if(!isActive || isUserValue) {
- if(settings.apiSettings && settings.saveRemoteData) {
- module.save.remoteData(selectedText, selectedValue);
- }
- if(settings.useLabels) {
- module.add.label(selectedValue, selectedText, shouldAnimate);
- module.add.value(selectedValue, selectedText, $selected);
- module.set.activeItem($selected);
- module.filterActive();
- module.select.nextAvailable($selectedItem);
- }
- else {
- module.add.value(selectedValue, selectedText, $selected);
- module.set.text(module.add.variables(message.count));
- module.set.activeItem($selected);
- }
- }
- else if(!isFiltered && (settings.useLabels || selectActionActive)) {
- module.debug('Selected active value, removing label');
- module.remove.selected(selectedValue);
- }
- }
- else {
- if(settings.apiSettings && settings.saveRemoteData) {
- module.save.remoteData(selectedText, selectedValue);
- }
- module.set.text(selectedText);
- module.set.value(selectedValue, selectedText, $selected);
- $selected
- .addClass(className.active)
- .addClass(className.selected)
- ;
- }
- })
- ;
- module.remove.searchTerm();
- }
- },
-
- add: {
- label: function(value, text, shouldAnimate) {
- var
- $next = module.is.searchSelection()
- ? $search
- : $text,
- escapedValue = module.escape.value(value),
- $label
- ;
- if(settings.ignoreCase) {
- escapedValue = escapedValue.toLowerCase();
- }
- $label = $('<a />')
- .addClass(className.label)
- .attr('data-' + metadata.value, escapedValue)
- .html(templates.label(escapedValue, text, settings.preserveHTML, settings.className))
- ;
- $label = settings.onLabelCreate.call($label, escapedValue, text);
-
- if(module.has.label(value)) {
- module.debug('User selection already exists, skipping', escapedValue);
- return;
- }
- if(settings.label.variation) {
- $label.addClass(settings.label.variation);
- }
- if(shouldAnimate === true) {
- module.debug('Animating in label', $label);
- $label
- .addClass(className.hidden)
- .insertBefore($next)
- .transition({
- animation : settings.label.transition,
- debug : settings.debug,
- verbose : settings.verbose,
- duration : settings.label.duration
- })
- ;
- }
- else {
- module.debug('Adding selection label', $label);
- $label
- .insertBefore($next)
- ;
- }
- },
- message: function(message) {
- var
- $message = $menu.children(selector.message),
- html = settings.templates.message(module.add.variables(message))
- ;
- if($message.length > 0) {
- $message
- .html(html)
- ;
- }
- else {
- $message = $('<div/>')
- .html(html)
- .addClass(className.message)
- .appendTo($menu)
- ;
- }
- },
- optionValue: function(value) {
- var
- escapedValue = module.escape.value(value),
- $option = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
- hasOption = ($option.length > 0)
- ;
- if(hasOption) {
- return;
- }
- // temporarily disconnect observer
- module.disconnect.selectObserver();
- if( module.is.single() ) {
- module.verbose('Removing previous user addition');
- $input.find('option.' + className.addition).remove();
- }
- $('<option/>')
- .prop('value', escapedValue)
- .addClass(className.addition)
- .html(value)
- .appendTo($input)
- ;
- module.verbose('Adding user addition as an <option>', value);
- module.observe.select();
- },
- userSuggestion: function(value) {
- var
- $addition = $menu.children(selector.addition),
- $existingItem = module.get.item(value),
- alreadyHasValue = $existingItem && $existingItem.not(selector.addition).length,
- hasUserSuggestion = $addition.length > 0,
- html
- ;
- if(settings.useLabels && module.has.maxSelections()) {
- return;
- }
- if(value === '' || alreadyHasValue) {
- $addition.remove();
- return;
- }
- if(hasUserSuggestion) {
- $addition
- .data(metadata.value, value)
- .data(metadata.text, value)
- .attr('data-' + metadata.value, value)
- .attr('data-' + metadata.text, value)
- .removeClass(className.filtered)
- ;
- if(!settings.hideAdditions) {
- html = settings.templates.addition( module.add.variables(message.addResult, value) );
- $addition
- .html(html)
- ;
- }
- module.verbose('Replacing user suggestion with new value', $addition);
- }
- else {
- $addition = module.create.userChoice(value);
- $addition
- .prependTo($menu)
- ;
- module.verbose('Adding item choice to menu corresponding with user choice addition', $addition);
- }
- if(!settings.hideAdditions || module.is.allFiltered()) {
- $addition
- .addClass(className.selected)
- .siblings()
- .removeClass(className.selected)
- ;
- }
- module.refreshItems();
- },
- variables: function(message, term) {
- var
- hasCount = (message.search('{count}') !== -1),
- hasMaxCount = (message.search('{maxCount}') !== -1),
- hasTerm = (message.search('{term}') !== -1),
- count,
- query
- ;
- module.verbose('Adding templated variables to message', message);
- if(hasCount) {
- count = module.get.selectionCount();
- message = message.replace('{count}', count);
- }
- if(hasMaxCount) {
- count = module.get.selectionCount();
- message = message.replace('{maxCount}', settings.maxSelections);
- }
- if(hasTerm) {
- query = term || module.get.query();
- message = message.replace('{term}', query);
- }
- return message;
- },
- value: function(addedValue, addedText, $selectedItem) {
- var
- currentValue = module.get.values(),
- newValue
- ;
- if(module.has.value(addedValue)) {
- module.debug('Value already selected');
- return;
- }
- if(addedValue === '') {
- module.debug('Cannot select blank values from multiselect');
- return;
- }
- // extend current array
- if(Array.isArray(currentValue)) {
- newValue = currentValue.concat([addedValue]);
- newValue = module.get.uniqueArray(newValue);
- }
- else {
- newValue = [addedValue];
- }
- // add values
- if( module.has.selectInput() ) {
- if(module.can.extendSelect()) {
- module.debug('Adding value to select', addedValue, newValue, $input);
- module.add.optionValue(addedValue);
- }
- }
- else {
- newValue = newValue.join(settings.delimiter);
- module.debug('Setting hidden input to delimited value', newValue, $input);
- }
-
- if(settings.fireOnInit === false && module.is.initialLoad()) {
- module.verbose('Skipping onadd callback on initial load', settings.onAdd);
- }
- else {
- settings.onAdd.call(element, addedValue, addedText, $selectedItem);
- }
- module.set.value(newValue, addedText, $selectedItem);
- module.check.maxSelections();
- },
- },
-
- remove: {
- active: function() {
- $module.removeClass(className.active);
- },
- activeLabel: function() {
- $module.find(selector.label).removeClass(className.active);
- },
- empty: function() {
- $module.removeClass(className.empty);
- },
- loading: function() {
- $module.removeClass(className.loading);
- },
- initialLoad: function() {
- initialLoad = false;
- },
- upward: function($currentMenu) {
- var $element = $currentMenu || $module;
- $element.removeClass(className.upward);
- },
- leftward: function($currentMenu) {
- var $element = $currentMenu || $menu;
- $element.removeClass(className.leftward);
- },
- visible: function() {
- $module.removeClass(className.visible);
- },
- activeItem: function() {
- $item.removeClass(className.active);
- },
- filteredItem: function() {
- if(settings.useLabels && module.has.maxSelections() ) {
- return;
- }
- if(settings.useLabels && module.is.multiple()) {
- $item.not('.' + className.active).removeClass(className.filtered);
- }
- else {
- $item.removeClass(className.filtered);
- }
- if(settings.hideDividers) {
- $divider.removeClass(className.hidden);
- }
- module.remove.empty();
- },
- optionValue: function(value) {
- var
- escapedValue = module.escape.value(value),
- $option = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
- hasOption = ($option.length > 0)
- ;
- if(!hasOption || !$option.hasClass(className.addition)) {
- return;
- }
- // temporarily disconnect observer
- if(selectObserver) {
- selectObserver.disconnect();
- module.verbose('Temporarily disconnecting mutation observer');
- }
- $option.remove();
- module.verbose('Removing user addition as an <option>', escapedValue);
- if(selectObserver) {
- selectObserver.observe($input[0], {
- childList : true,
- subtree : true
- });
- }
- },
- message: function() {
- $menu.children(selector.message).remove();
- },
- searchWidth: function() {
- $search.css('width', '');
- },
- searchTerm: function() {
- module.verbose('Cleared search term');
- $search.val('');
- module.set.filtered();
- },
- userAddition: function() {
- $item.filter(selector.addition).remove();
- },
- selected: function(value, $selectedItem) {
- $selectedItem = (settings.allowAdditions)
- ? $selectedItem || module.get.itemWithAdditions(value)
- : $selectedItem || module.get.item(value)
- ;
-
- if(!$selectedItem) {
- return false;
- }
-
- $selectedItem
- .each(function() {
- var
- $selected = $(this),
- selectedText = module.get.choiceText($selected),
- selectedValue = module.get.choiceValue($selected, selectedText)
- ;
- if(module.is.multiple()) {
- if(settings.useLabels) {
- module.remove.value(selectedValue, selectedText, $selected);
- module.remove.label(selectedValue);
- }
- else {
- module.remove.value(selectedValue, selectedText, $selected);
- if(module.get.selectionCount() === 0) {
- module.set.placeholderText();
- }
- else {
- module.set.text(module.add.variables(message.count));
- }
- }
- }
- else {
- module.remove.value(selectedValue, selectedText, $selected);
- }
- $selected
- .removeClass(className.filtered)
- .removeClass(className.active)
- ;
- if(settings.useLabels) {
- $selected.removeClass(className.selected);
- }
- })
- ;
- },
- selectedItem: function() {
- $item.removeClass(className.selected);
- },
- value: function(removedValue, removedText, $removedItem) {
- var
- values = module.get.values(),
- newValue
- ;
- removedValue = module.escape.htmlEntities(removedValue);
- if( module.has.selectInput() ) {
- module.verbose('Input is <select> removing selected option', removedValue);
- newValue = module.remove.arrayValue(removedValue, values);
- module.remove.optionValue(removedValue);
- }
- else {
- module.verbose('Removing from delimited values', removedValue);
- newValue = module.remove.arrayValue(removedValue, values);
- newValue = newValue.join(settings.delimiter);
- }
- if(settings.fireOnInit === false && module.is.initialLoad()) {
- module.verbose('No callback on initial load', settings.onRemove);
- }
- else {
- settings.onRemove.call(element, removedValue, removedText, $removedItem);
- }
- module.set.value(newValue, removedText, $removedItem);
- module.check.maxSelections();
- },
- arrayValue: function(removedValue, values) {
- if( !Array.isArray(values) ) {
- values = [values];
- }
- values = $.grep(values, function(value){
- return (removedValue != value);
- });
- module.verbose('Removed value from delimited string', removedValue, values);
- return values;
- },
- label: function(value, shouldAnimate) {
- var
- $labels = $module.find(selector.label),
- $removedLabel = $labels.filter('[data-' + metadata.value + '="' + module.escape.string(settings.ignoreCase ? value.toLowerCase() : value) +'"]')
- ;
- module.verbose('Removing label', $removedLabel);
- $removedLabel.remove();
- },
- activeLabels: function($activeLabels) {
- $activeLabels = $activeLabels || $module.find(selector.label).filter('.' + className.active);
- module.verbose('Removing active label selections', $activeLabels);
- module.remove.labels($activeLabels);
- },
- labels: function($labels) {
- $labels = $labels || $module.find(selector.label);
- module.verbose('Removing labels', $labels);
- $labels
- .each(function(){
- var
- $label = $(this),
- value = $label.data(metadata.value),
- stringValue = (value !== undefined)
- ? String(value)
- : value,
- isUserValue = module.is.userValue(stringValue)
- ;
- if(settings.onLabelRemove.call($label, value) === false) {
- module.debug('Label remove callback cancelled removal');
- return;
- }
- module.remove.message();
- if(isUserValue) {
- module.remove.value(stringValue);
- module.remove.label(stringValue);
- }
- else {
- // selected will also remove label
- module.remove.selected(stringValue);
- }
- })
- ;
- },
- tabbable: function() {
- if( module.is.searchSelection() ) {
- module.debug('Searchable dropdown initialized');
- $search
- .removeAttr('tabindex')
- ;
- $menu
- .removeAttr('tabindex')
- ;
- }
- else {
- module.debug('Simple selection dropdown initialized');
- $module
- .removeAttr('tabindex')
- ;
- $menu
- .removeAttr('tabindex')
- ;
- }
- },
- diacritics: function(text) {
- return settings.ignoreDiacritics ? text.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : text;
- }
- },
-
- has: {
- menuSearch: function() {
- return (module.has.search() && $search.closest($menu).length > 0);
- },
- clearItem: function() {
- return ($clear.length > 0);
- },
- search: function() {
- return ($search.length > 0);
- },
- sizer: function() {
- return ($sizer.length > 0);
- },
- selectInput: function() {
- return ( $input.is('select') );
- },
- minCharacters: function(searchTerm) {
- if(settings.minCharacters && !iconClicked) {
- searchTerm = (searchTerm !== undefined)
- ? String(searchTerm)
- : String(module.get.query())
- ;
- return (searchTerm.length >= settings.minCharacters);
- }
- iconClicked=false;
- return true;
- },
- firstLetter: function($item, letter) {
- var
- text,
- firstLetter
- ;
- if(!$item || $item.length === 0 || typeof letter !== 'string') {
- return false;
- }
- text = module.get.choiceText($item, false);
- letter = letter.toLowerCase();
- firstLetter = String(text).charAt(0).toLowerCase();
- return (letter == firstLetter);
- },
- input: function() {
- return ($input.length > 0);
- },
- items: function() {
- return ($item.length > 0);
- },
- menu: function() {
- return ($menu.length > 0);
- },
- message: function() {
- return ($menu.children(selector.message).length !== 0);
- },
- label: function(value) {
- var
- escapedValue = module.escape.value(value),
- $labels = $module.find(selector.label)
- ;
- if(settings.ignoreCase) {
- escapedValue = escapedValue.toLowerCase();
- }
- return ($labels.filter('[data-' + metadata.value + '="' + module.escape.string(escapedValue) +'"]').length > 0);
- },
- maxSelections: function() {
- return (settings.maxSelections && module.get.selectionCount() >= settings.maxSelections);
- },
- allResultsFiltered: function() {
- var
- $normalResults = $item.not(selector.addition)
- ;
- return ($normalResults.filter(selector.unselectable).length === $normalResults.length);
- },
- userSuggestion: function() {
- return ($menu.children(selector.addition).length > 0);
- },
- query: function() {
- return (module.get.query() !== '');
- },
- value: function(value) {
- return (settings.ignoreCase)
- ? module.has.valueIgnoringCase(value)
- : module.has.valueMatchingCase(value)
- ;
- },
- valueMatchingCase: function(value) {
- var
- values = module.get.values(),
- hasValue = Array.isArray(values)
- ? values && ($.inArray(value, values) !== -1)
- : (values == value)
- ;
- return (hasValue)
- ? true
- : false
- ;
- },
- valueIgnoringCase: function(value) {
- var
- values = module.get.values(),
- hasValue = false
- ;
- if(!Array.isArray(values)) {
- values = [values];
- }
- $.each(values, function(index, existingValue) {
- if(String(value).toLowerCase() == String(existingValue).toLowerCase()) {
- hasValue = true;
- return false;
- }
- });
- return hasValue;
- }
- },
-
- is: {
- active: function() {
- return $module.hasClass(className.active);
- },
- animatingInward: function() {
- return $menu.transition('is inward');
- },
- animatingOutward: function() {
- return $menu.transition('is outward');
- },
- bubbledLabelClick: function(event) {
- return $(event.target).is('select, input') && $module.closest('label').length > 0;
- },
- bubbledIconClick: function(event) {
- return $(event.target).closest($icon).length > 0;
- },
- alreadySetup: function() {
- return ($module.is('select') && $module.parent(selector.dropdown).data(moduleNamespace) !== undefined && $module.prev().length === 0);
- },
- animating: function($subMenu) {
- return ($subMenu)
- ? $subMenu.transition && $subMenu.transition('is animating')
- : $menu.transition && $menu.transition('is animating')
- ;
- },
- leftward: function($subMenu) {
- var $selectedMenu = $subMenu || $menu;
- return $selectedMenu.hasClass(className.leftward);
- },
- clearable: function() {
- return ($module.hasClass(className.clearable) || settings.clearable);
- },
- disabled: function() {
- return $module.hasClass(className.disabled);
- },
- focused: function() {
- return (document.activeElement === $module[0]);
- },
- focusedOnSearch: function() {
- return (document.activeElement === $search[0]);
- },
- allFiltered: function() {
- return( (module.is.multiple() || module.has.search()) && !(settings.hideAdditions == false && module.has.userSuggestion()) && !module.has.message() && module.has.allResultsFiltered() );
- },
- hidden: function($subMenu) {
- return !module.is.visible($subMenu);
- },
- initialLoad: function() {
- return initialLoad;
- },
- inObject: function(needle, object) {
- var
- found = false
- ;
- $.each(object, function(index, property) {
- if(property == needle) {
- found = true;
- return true;
- }
- });
- return found;
- },
- multiple: function() {
- return $module.hasClass(className.multiple);
- },
- remote: function() {
- return settings.apiSettings && module.can.useAPI();
- },
- single: function() {
- return !module.is.multiple();
- },
- selectMutation: function(mutations) {
- var
- selectChanged = false
- ;
- $.each(mutations, function(index, mutation) {
- if($(mutation.target).is('select') || $(mutation.addedNodes).is('select')) {
- selectChanged = true;
- return false;
- }
- });
- return selectChanged;
- },
- search: function() {
- return $module.hasClass(className.search);
- },
- searchSelection: function() {
- return ( module.has.search() && $search.parent(selector.dropdown).length === 1 );
- },
- selection: function() {
- return $module.hasClass(className.selection);
- },
- userValue: function(value) {
- return ($.inArray(value, module.get.userValues()) !== -1);
- },
- upward: function($menu) {
- var $element = $menu || $module;
- return $element.hasClass(className.upward);
- },
- visible: function($subMenu) {
- return ($subMenu)
- ? $subMenu.hasClass(className.visible)
- : $menu.hasClass(className.visible)
- ;
- },
- verticallyScrollableContext: function() {
- var
- overflowY = ($context.get(0) !== window)
- ? $context.css('overflow-y')
- : false
- ;
- return (overflowY == 'auto' || overflowY == 'scroll');
- },
- horizontallyScrollableContext: function() {
- var
- overflowX = ($context.get(0) !== window)
- ? $context.css('overflow-X')
- : false
- ;
- return (overflowX == 'auto' || overflowX == 'scroll');
- }
- },
-
- can: {
- activate: function($item) {
- if(settings.useLabels) {
- return true;
- }
- if(!module.has.maxSelections()) {
- return true;
- }
- if(module.has.maxSelections() && $item.hasClass(className.active)) {
- return true;
- }
- return false;
- },
- openDownward: function($subMenu) {
- var
- $currentMenu = $subMenu || $menu,
- canOpenDownward = true,
- onScreen = {},
- calculations
- ;
- $currentMenu
- .addClass(className.loading)
- ;
- calculations = {
- context: {
- offset : ($context.get(0) === window)
- ? { top: 0, left: 0}
- : $context.offset(),
- scrollTop : $context.scrollTop(),
- height : $context.outerHeight()
- },
- menu : {
- offset: $currentMenu.offset(),
- height: $currentMenu.outerHeight()
- }
- };
- if(module.is.verticallyScrollableContext()) {
- calculations.menu.offset.top += calculations.context.scrollTop;
- }
- onScreen = {
- above : (calculations.context.scrollTop) <= calculations.menu.offset.top - calculations.context.offset.top - calculations.menu.height,
- below : (calculations.context.scrollTop + calculations.context.height) >= calculations.menu.offset.top - calculations.context.offset.top + calculations.menu.height
- };
- if(onScreen.below) {
- module.verbose('Dropdown can fit in context downward', onScreen);
- canOpenDownward = true;
- }
- else if(!onScreen.below && !onScreen.above) {
- module.verbose('Dropdown cannot fit in either direction, favoring downward', onScreen);
- canOpenDownward = true;
- }
- else {
- module.verbose('Dropdown cannot fit below, opening upward', onScreen);
- canOpenDownward = false;
- }
- $currentMenu.removeClass(className.loading);
- return canOpenDownward;
- },
- openRightward: function($subMenu) {
- var
- $currentMenu = $subMenu || $menu,
- canOpenRightward = true,
- isOffscreenRight = false,
- calculations
- ;
- $currentMenu
- .addClass(className.loading)
- ;
- calculations = {
- context: {
- offset : ($context.get(0) === window)
- ? { top: 0, left: 0}
- : $context.offset(),
- scrollLeft : $context.scrollLeft(),
- width : $context.outerWidth()
- },
- menu: {
- offset : $currentMenu.offset(),
- width : $currentMenu.outerWidth()
- }
- };
- if(module.is.horizontallyScrollableContext()) {
- calculations.menu.offset.left += calculations.context.scrollLeft;
- }
- isOffscreenRight = (calculations.menu.offset.left - calculations.context.offset.left + calculations.menu.width >= calculations.context.scrollLeft + calculations.context.width);
- if(isOffscreenRight) {
- module.verbose('Dropdown cannot fit in context rightward', isOffscreenRight);
- canOpenRightward = false;
- }
- $currentMenu.removeClass(className.loading);
- return canOpenRightward;
- },
- click: function() {
- return (hasTouch || settings.on == 'click');
- },
- extendSelect: function() {
- return settings.allowAdditions || settings.apiSettings;
- },
- show: function() {
- return !module.is.disabled() && (module.has.items() || module.has.message());
- },
- useAPI: function() {
- return $.fn.api !== undefined;
- }
- },
-
- animate: {
- show: function(callback, $subMenu) {
- var
- $currentMenu = $subMenu || $menu,
- start = ($subMenu)
- ? function() {}
- : function() {
- module.hideSubMenus();
- module.hideOthers();
- module.set.active();
- },
- transition
- ;
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- module.verbose('Doing menu show animation', $currentMenu);
- module.set.direction($subMenu);
- transition = module.get.transition($subMenu);
- if( module.is.selection() ) {
- module.set.scrollPosition(module.get.selectedItem(), true);
- }
- if( module.is.hidden($currentMenu) || module.is.animating($currentMenu) ) {
- var displayType = $module.hasClass('column') ? 'flex' : false;
- if(transition == 'none') {
- start();
- $currentMenu.transition({
- displayType: displayType
- }).transition('show');
- callback.call(element);
- }
- else if($.fn.transition !== undefined && $module.transition('is supported')) {
- $currentMenu
- .transition({
- animation : transition + ' in',
- debug : settings.debug,
- verbose : settings.verbose,
- duration : settings.duration,
- queue : true,
- onStart : start,
- displayType: displayType,
- onComplete : function() {
- callback.call(element);
- }
- })
- ;
- }
- else {
- module.error(error.noTransition, transition);
- }
- }
- },
- hide: function(callback, $subMenu) {
- var
- $currentMenu = $subMenu || $menu,
- start = ($subMenu)
- ? function() {}
- : function() {
- if( module.can.click() ) {
- module.unbind.intent();
- }
- module.remove.active();
- },
- transition = module.get.transition($subMenu)
- ;
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- if( module.is.visible($currentMenu) || module.is.animating($currentMenu) ) {
- module.verbose('Doing menu hide animation', $currentMenu);
-
- if(transition == 'none') {
- start();
- $currentMenu.transition('hide');
- callback.call(element);
- }
- else if($.fn.transition !== undefined && $module.transition('is supported')) {
- $currentMenu
- .transition({
- animation : transition + ' out',
- duration : settings.duration,
- debug : settings.debug,
- verbose : settings.verbose,
- queue : false,
- onStart : start,
- onComplete : function() {
- callback.call(element);
- }
- })
- ;
- }
- else {
- module.error(error.transition);
- }
- }
- }
- },
-
- hideAndClear: function() {
- module.remove.searchTerm();
- if( module.has.maxSelections() ) {
- return;
- }
- if(module.has.search()) {
- module.hide(function() {
- module.remove.filteredItem();
- });
- }
- else {
- module.hide();
- }
- },
-
- delay: {
- show: function() {
- module.verbose('Delaying show event to ensure user intent');
- clearTimeout(module.timer);
- module.timer = setTimeout(module.show, settings.delay.show);
- },
- hide: function() {
- module.verbose('Delaying hide event to ensure user intent');
- clearTimeout(module.timer);
- module.timer = setTimeout(module.hide, settings.delay.hide);
- }
- },
-
- escape: {
- value: function(value) {
- var
- multipleValues = Array.isArray(value),
- stringValue = (typeof value === 'string'),
- isUnparsable = (!stringValue && !multipleValues),
- hasQuotes = (stringValue && value.search(regExp.quote) !== -1),
- values = []
- ;
- if(isUnparsable || !hasQuotes) {
- return value;
- }
- module.debug('Encoding quote values for use in select', value);
- if(multipleValues) {
- $.each(value, function(index, value){
- values.push(value.replace(regExp.quote, '&quot;'));
- });
- return values;
- }
- return value.replace(regExp.quote, '&quot;');
- },
- string: function(text) {
- text = String(text);
- return text.replace(regExp.escape, '\\$&');
- },
- htmlEntities: function(string) {
- var
- badChars = /[<>"'`]/g,
- shouldEscape = /[&<>"'`]/,
- escape = {
- "<": "&lt;",
- ">": "&gt;",
- '"': "&quot;",
- "'": "&#x27;",
- "`": "&#x60;"
- },
- escapedChar = function(chr) {
- return escape[chr];
- }
- ;
- if(shouldEscape.test(string)) {
- string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
- return string.replace(badChars, escapedChar);
- }
- return string;
- }
- },
-
- setting: function(name, value) {
- module.debug('Changing setting', name, value);
- if( $.isPlainObject(name) ) {
- $.extend(true, settings, name);
- }
- else if(value !== undefined) {
- if($.isPlainObject(settings[name])) {
- $.extend(true, settings[name], value);
- }
- else {
- settings[name] = value;
- }
- }
- else {
- return settings[name];
- }
- },
- internal: function(name, value) {
- if( $.isPlainObject(name) ) {
- $.extend(true, module, name);
- }
- else if(value !== undefined) {
- module[name] = value;
- }
- else {
- return module[name];
- }
- },
- debug: function() {
- if(!settings.silent && settings.debug) {
- if(settings.performance) {
- module.performance.log(arguments);
- }
- else {
- module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
- module.debug.apply(console, arguments);
- }
- }
- },
- verbose: function() {
- if(!settings.silent && settings.verbose && settings.debug) {
- if(settings.performance) {
- module.performance.log(arguments);
- }
- else {
- module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
- module.verbose.apply(console, arguments);
- }
- }
- },
- error: function() {
- if(!settings.silent) {
- module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
- module.error.apply(console, arguments);
- }
- },
- performance: {
- log: function(message) {
- var
- currentTime,
- executionTime,
- previousTime
- ;
- if(settings.performance) {
- currentTime = new Date().getTime();
- previousTime = time || currentTime;
- executionTime = currentTime - previousTime;
- time = currentTime;
- performance.push({
- 'Name' : message[0],
- 'Arguments' : [].slice.call(message, 1) || '',
- 'Element' : element,
- 'Execution Time' : executionTime
- });
- }
- clearTimeout(module.performance.timer);
- module.performance.timer = setTimeout(module.performance.display, 500);
- },
- display: function() {
- var
- title = settings.name + ':',
- totalTime = 0
- ;
- time = false;
- clearTimeout(module.performance.timer);
- $.each(performance, function(index, data) {
- totalTime += data['Execution Time'];
- });
- title += ' ' + totalTime + 'ms';
- if(moduleSelector) {
- title += ' \'' + moduleSelector + '\'';
- }
- if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
- console.groupCollapsed(title);
- if(console.table) {
- console.table(performance);
- }
- else {
- $.each(performance, function(index, data) {
- console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
- });
- }
- console.groupEnd();
- }
- performance = [];
- }
- },
- invoke: function(query, passedArguments, context) {
- var
- object = instance,
- maxDepth,
- found,
- response
- ;
- passedArguments = passedArguments || queryArguments;
- context = element || context;
- if(typeof query == 'string' && object !== undefined) {
- query = query.split(/[\. ]/);
- maxDepth = query.length - 1;
- $.each(query, function(depth, value) {
- var camelCaseValue = (depth != maxDepth)
- ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
- : query
- ;
- if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
- object = object[camelCaseValue];
- }
- else if( object[camelCaseValue] !== undefined ) {
- found = object[camelCaseValue];
- return false;
- }
- else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
- object = object[value];
- }
- else if( object[value] !== undefined ) {
- found = object[value];
- return false;
- }
- else {
- module.error(error.method, query);
- return false;
- }
- });
- }
- if ( $.isFunction( found ) ) {
- response = found.apply(context, passedArguments);
- }
- else if(found !== undefined) {
- response = found;
- }
- if(Array.isArray(returnedValue)) {
- returnedValue.push(response);
- }
- else if(returnedValue !== undefined) {
- returnedValue = [returnedValue, response];
- }
- else if(response !== undefined) {
- returnedValue = response;
- }
- return found;
- }
- };
-
- if(methodInvoked) {
- if(instance === undefined) {
- module.initialize();
- }
- module.invoke(query);
- }
- else {
- if(instance !== undefined) {
- instance.invoke('destroy');
- }
- module.initialize();
- }
- })
- ;
- return (returnedValue !== undefined)
- ? returnedValue
- : $allModules
- ;
-};
-
-$.fn.dropdown.settings = {
-
- silent : false,
- debug : false,
- verbose : false,
- performance : true,
-
- on : 'click', // what event should show menu action on item selection
- action : 'activate', // action on item selection (nothing, activate, select, combo, hide, function(){})
-
- values : false, // specify values to use for dropdown
-
- clearable : false, // whether the value of the dropdown can be cleared
-
- apiSettings : false,
- selectOnKeydown : true, // Whether selection should occur automatically when keyboard shortcuts used
- minCharacters : 0, // Minimum characters required to trigger API call
-
- filterRemoteData : false, // Whether API results should be filtered after being returned for query term
- saveRemoteData : true, // Whether remote name/value pairs should be stored in sessionStorage to allow remote data to be restored on page refresh
-
- throttle : 200, // How long to wait after last user input to search remotely
-
- context : window, // Context to use when determining if on screen
- direction : 'auto', // Whether dropdown should always open in one direction
- keepOnScreen : true, // Whether dropdown should check whether it is on screen before showing
-
- match : 'both', // what to match against with search selection (both, text, or label)
- fullTextSearch : false, // search anywhere in value (set to 'exact' to require exact matches)
- ignoreDiacritics : false, // match results also if they contain diacritics of the same base character (for example searching for "a" will also match "á" or "â" or "à", etc...)
- hideDividers : false, // Whether to hide any divider elements (specified in selector.divider) that are sibling to any items when searched (set to true will hide all dividers, set to 'empty' will hide them when they are not followed by a visible item)
-
- placeholder : 'auto', // whether to convert blank <select> values to placeholder text
- preserveHTML : true, // preserve html when selecting value
- sortSelect : false, // sort selection on init
-
- forceSelection : true, // force a choice on blur with search selection
-
- allowAdditions : false, // whether multiple select should allow user added values
- ignoreCase : false, // whether to consider case sensitivity when creating labels
- ignoreSearchCase : true, // whether to consider case sensitivity when filtering items
- hideAdditions : true, // whether or not to hide special message prompting a user they can enter a value
-
- maxSelections : false, // When set to a number limits the number of selections to this count
- useLabels : true, // whether multiple select should filter currently active selections from choices
- delimiter : ',', // when multiselect uses normal <input> the values will be delimited with this character
-
- showOnFocus : true, // show menu on focus
- allowReselection : false, // whether current value should trigger callbacks when reselected
- allowTab : true, // add tabindex to element
- allowCategorySelection : false, // allow elements with sub-menus to be selected
-
- fireOnInit : false, // Whether callbacks should fire when initializing dropdown values
-
- transition : 'auto', // auto transition will slide down or up based on direction
- duration : 200, // duration of transition
-
- glyphWidth : 1.037, // widest glyph width in em (W is 1.037 em) used to calculate multiselect input width
-
- headerDivider : true, // whether option headers should have an additional divider line underneath when converted from <select> <optgroup>
-
- // label settings on multi-select
- label: {
- transition : 'scale',
- duration : 200,
- variation : false
- },
-
- // delay before event
- delay : {
- hide : 300,
- show : 200,
- search : 20,
- touch : 50
- },
-
- /* Callbacks */
- onChange : function(value, text, $selected){},
- onAdd : function(value, text, $selected){},
- onRemove : function(value, text, $selected){},
-
- onLabelSelect : function($selectedLabels){},
- onLabelCreate : function(value, text) { return $(this); },
- onLabelRemove : function(value) { return true; },
- onNoResults : function(searchTerm) { return true; },
- onShow : function(){},
- onHide : function(){},
-
- /* Component */
- name : 'Dropdown',
- namespace : 'dropdown',
-
- message: {
- addResult : 'Add <b>{term}</b>',
- count : '{count} selected',
- maxSelections : 'Max {maxCount} selections',
- noResults : 'No results found.',
- serverError : 'There was an error contacting the server'
- },
-
- error : {
- action : 'You called a dropdown action that was not defined',
- alreadySetup : 'Once a select has been initialized behaviors must be called on the created ui dropdown',
- labels : 'Allowing user additions currently requires the use of labels.',
- missingMultiple : '<select> requires multiple property to be set to correctly preserve multiple values',
- method : 'The method you called is not defined.',
- noAPI : 'The API module is required to load resources remotely',
- noStorage : 'Saving remote data requires session storage',
- noTransition : 'This module requires ui transitions <https://github.com/Semantic-Org/UI-Transition>',
- noNormalize : '"ignoreDiacritics" setting will be ignored. Browser does not support String().normalize(). You may consider including <https://cdn.jsdelivr.net/npm/unorm@1.4.1/lib/unorm.min.js> as a polyfill.'
- },
-
- regExp : {
- escape : /[-[\]{}()*+?.,\\^$|#\s:=@]/g,
- quote : /"/g
- },
-
- metadata : {
- defaultText : 'defaultText',
- defaultValue : 'defaultValue',
- placeholderText : 'placeholder',
- text : 'text',
- value : 'value'
- },
-
- // property names for remote query
- fields: {
- remoteValues : 'results', // grouping for api results
- values : 'values', // grouping for all dropdown values
- disabled : 'disabled', // whether value should be disabled
- name : 'name', // displayed dropdown text
- value : 'value', // actual dropdown value
- text : 'text', // displayed text when selected
- type : 'type', // type of dropdown element
- image : 'image', // optional image path
- imageClass : 'imageClass', // optional individual class for image
- icon : 'icon', // optional icon name
- iconClass : 'iconClass', // optional individual class for icon (for example to use flag instead)
- class : 'class', // optional individual class for item/header
- divider : 'divider' // optional divider append for group headers
- },
-
- keys : {
- backspace : 8,
- delimiter : 188, // comma
- deleteKey : 46,
- enter : 13,
- escape : 27,
- pageUp : 33,
- pageDown : 34,
- leftArrow : 37,
- upArrow : 38,
- rightArrow : 39,
- downArrow : 40
- },
-
- selector : {
- addition : '.addition',
- divider : '.divider, .header',
- dropdown : '.ui.dropdown',
- hidden : '.hidden',
- icon : '> .dropdown.icon',
- input : '> input[type="hidden"], > select',
- item : '.item',
- label : '> .label',
- remove : '> .label > .delete.icon',
- siblingLabel : '.label',
- menu : '.menu',
- message : '.message',
- menuIcon : '.dropdown.icon',
- search : 'input.search, .menu > .search > input, .menu input.search',
- sizer : '> span.sizer',
- text : '> .text:not(.icon)',
- unselectable : '.disabled, .filtered',
- clearIcon : '> .remove.icon'
- },
-
- className : {
- active : 'active',
- addition : 'addition',
- animating : 'animating',
- disabled : 'disabled',
- empty : 'empty',
- dropdown : 'ui dropdown',
- filtered : 'filtered',
- hidden : 'hidden transition',
- icon : 'icon',
- image : 'image',
- item : 'item',
- label : 'ui label',
- loading : 'loading',
- menu : 'menu',
- message : 'message',
- multiple : 'multiple',
- placeholder : 'default',
- sizer : 'sizer',
- search : 'search',
- selected : 'selected',
- selection : 'selection',
- upward : 'upward',
- leftward : 'left',
- visible : 'visible',
- clearable : 'clearable',
- noselection : 'noselection',
- delete : 'delete',
- header : 'header',
- divider : 'divider',
- groupIcon : '',
- unfilterable : 'unfilterable'
- }
-
-};
-
-/* Templates */
-$.fn.dropdown.settings.templates = {
- deQuote: function(string) {
- return String(string).replace(/"/g,"");
- },
- escape: function(string, preserveHTML) {
- if (preserveHTML){
- return string;
- }
- var
- badChars = /[<>"'`]/g,
- shouldEscape = /[&<>"'`]/,
- escape = {
- "<": "&lt;",
- ">": "&gt;",
- '"': "&quot;",
- "'": "&#x27;",
- "`": "&#x60;"
- },
- escapedChar = function(chr) {
- return escape[chr];
- }
- ;
- if(shouldEscape.test(string)) {
- string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
- return string.replace(badChars, escapedChar);
- }
- return string;
- },
- // generates dropdown from select values
- dropdown: function(select, fields, preserveHTML, className) {
- var
- placeholder = select.placeholder || false,
- html = '',
- escape = $.fn.dropdown.settings.templates.escape
- ;
- html += '<i class="dropdown icon"></i>';
- if(placeholder) {
- html += '<div class="default text">' + escape(placeholder,preserveHTML) + '</div>';
- }
- else {
- html += '<div class="text"></div>';
- }
- html += '<div class="'+className.menu+'">';
- html += $.fn.dropdown.settings.templates.menu(select, fields, preserveHTML,className);
- html += '</div>';
- return html;
- },
-
- // generates just menu from select
- menu: function(response, fields, preserveHTML, className) {
- var
- values = response[fields.values] || [],
- html = '',
- escape = $.fn.dropdown.settings.templates.escape,
- deQuote = $.fn.dropdown.settings.templates.deQuote
- ;
- $.each(values, function(index, option) {
- var
- itemType = (option[fields.type])
- ? option[fields.type]
- : 'item'
- ;
-
- if( itemType === 'item' ) {
- var
- maybeText = (option[fields.text])
- ? ' data-text="' + deQuote(option[fields.text]) + '"'
- : '',
- maybeDisabled = (option[fields.disabled])
- ? className.disabled+' '
- : ''
- ;
- html += '<div class="'+ maybeDisabled + (option[fields.class] ? deQuote(option[fields.class]) : className.item)+'" data-value="' + deQuote(option[fields.value]) + '"' + maybeText + '>';
- if(option[fields.image]) {
- html += '<img class="'+(option[fields.imageClass] ? deQuote(option[fields.imageClass]) : className.image)+'" src="' + deQuote(option[fields.image]) + '">';
- }
- if(option[fields.icon]) {
- html += '<i class="'+deQuote(option[fields.icon])+' '+(option[fields.iconClass] ? deQuote(option[fields.iconClass]) : className.icon)+'"></i>';
- }
- html += escape(option[fields.name] || '', preserveHTML);
- html += '</div>';
- } else if (itemType === 'header') {
- var groupName = escape(option[fields.name] || '', preserveHTML),
- groupIcon = option[fields.icon] ? deQuote(option[fields.icon]) : className.groupIcon
- ;
- if(groupName !== '' || groupIcon !== '') {
- html += '<div class="' + (option[fields.class] ? deQuote(option[fields.class]) : className.header) + '">';
- if (groupIcon !== '') {
- html += '<i class="' + groupIcon + ' ' + (option[fields.iconClass] ? deQuote(option[fields.iconClass]) : className.icon) + '"></i>';
- }
- html += groupName;
- html += '</div>';
- }
- if(option[fields.divider]){
- html += '<div class="'+className.divider+'"></div>';
- }
- }
- });
- return html;
- },
-
- // generates label for multiselect
- label: function(value, text, preserveHTML, className) {
- var
- escape = $.fn.dropdown.settings.templates.escape;
- return escape(text,preserveHTML) + '<i class="'+className.delete+' icon"></i>';
- },
-
-
- // generates messages like "No results"
- message: function(message) {
- return message;
- },
-
- // generates user addition to selection menu
- addition: function(choice) {
- return choice;
- }
-
-};
-
-})( jQuery, window, document );
-
-/*!
- * # Fomantic-UI - Form Validation
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-;(function ($, window, document, undefined) {
-
-'use strict';
-
-$.isFunction = $.isFunction || function(obj) {
- return typeof obj === "function" && typeof obj.nodeType !== "number";
-};
-
-window = (typeof window != 'undefined' && window.Math == Math)
- ? window
- : (typeof self != 'undefined' && self.Math == Math)
- ? self
- : Function('return this')()
-;
-
-$.fn.form = function(parameters) {
- var
- $allModules = $(this),
- moduleSelector = $allModules.selector || '',
-
- time = new Date().getTime(),
- performance = [],
-
- query = arguments[0],
- legacyParameters = arguments[1],
- methodInvoked = (typeof query == 'string'),
- queryArguments = [].slice.call(arguments, 1),
- returnedValue
- ;
- $allModules
- .each(function() {
- var
- $module = $(this),
- element = this,
-
- formErrors = [],
- keyHeldDown = false,
-
- // set at run-time
- $field,
- $group,
- $message,
- $prompt,
- $submit,
- $clear,
- $reset,
-
- settings,
- validation,
-
- metadata,
- selector,
- className,
- regExp,
- error,
-
- namespace,
- moduleNamespace,
- eventNamespace,
-
- submitting = false,
- dirty = false,
- history = ['clean', 'clean'],
-
- instance,
- module
- ;
-
- module = {
-
- initialize: function() {
-
- // settings grabbed at run time
- module.get.settings();
- if(methodInvoked) {
- if(instance === undefined) {
- module.instantiate();
- }
- module.invoke(query);
- }
- else {
- if(instance !== undefined) {
- instance.invoke('destroy');
- }
- module.verbose('Initializing form validation', $module, settings);
- module.bindEvents();
- module.set.defaults();
- if (settings.autoCheckRequired) {
- module.set.autoCheck();
- }
- module.instantiate();
- }
- },
-
- instantiate: function() {
- module.verbose('Storing instance of module', module);
- instance = module;
- $module
- .data(moduleNamespace, module)
- ;
- },
-
- destroy: function() {
- module.verbose('Destroying previous module', instance);
- module.removeEvents();
- $module
- .removeData(moduleNamespace)
- ;
- },
-
- refresh: function() {
- module.verbose('Refreshing selector cache');
- $field = $module.find(selector.field);
- $group = $module.find(selector.group);
- $message = $module.find(selector.message);
- $prompt = $module.find(selector.prompt);
-
- $submit = $module.find(selector.submit);
- $clear = $module.find(selector.clear);
- $reset = $module.find(selector.reset);
- },
-
- submit: function() {
- module.verbose('Submitting form', $module);
- submitting = true;
- $module.submit();
- },
-
- attachEvents: function(selector, action) {
- action = action || 'submit';
- $(selector).on('click' + eventNamespace, function(event) {
- module[action]();
- event.preventDefault();
- });
- },
-
- bindEvents: function() {
- module.verbose('Attaching form events');
- $module
- .on('submit' + eventNamespace, module.validate.form)
- .on('blur' + eventNamespace, selector.field, module.event.field.blur)
- .on('click' + eventNamespace, selector.submit, module.submit)
- .on('click' + eventNamespace, selector.reset, module.reset)
- .on('click' + eventNamespace, selector.clear, module.clear)
- ;
- if(settings.keyboardShortcuts) {
- $module.on('keydown' + eventNamespace, selector.field, module.event.field.keydown);
- }
- $field.each(function(index, el) {
- var
- $input = $(el),
- type = $input.prop('type'),
- inputEvent = module.get.changeEvent(type, $input)
- ;
- $input.on(inputEvent + eventNamespace, module.event.field.change);
- });
-
- // Dirty events
- if (settings.preventLeaving) {
- $(window).on('beforeunload' + eventNamespace, module.event.beforeUnload);
- }
-
- $field.on('change click keyup keydown blur', function(e) {
- $(this).triggerHandler(e.type + ".dirty");
- });
-
- $field.on('change.dirty click.dirty keyup.dirty keydown.dirty blur.dirty', module.determine.isDirty);
-
- $module.on('dirty' + eventNamespace, function(e) {
- settings.onDirty.call();
- });
-
- $module.on('clean' + eventNamespace, function(e) {
- settings.onClean.call();
- })
- },
-
- clear: function() {
- $field.each(function (index, el) {
- var
- $field = $(el),
- $element = $field.parent(),
- $fieldGroup = $field.closest($group),
- $prompt = $fieldGroup.find(selector.prompt),
- $calendar = $field.closest(selector.uiCalendar),
- defaultValue = $field.data(metadata.defaultValue) || '',
- isCheckbox = $element.is(selector.uiCheckbox),
- isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
- isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
- isErrored = $fieldGroup.hasClass(className.error)
- ;
- if(isErrored) {
- module.verbose('Resetting error on field', $fieldGroup);
- $fieldGroup.removeClass(className.error);
- $prompt.remove();
- }
- if(isDropdown) {
- module.verbose('Resetting dropdown value', $element, defaultValue);
- $element.dropdown('clear', true);
- }
- else if(isCheckbox) {
- $field.prop('checked', false);
- }
- else if (isCalendar) {
- $calendar.calendar('clear');
- }
- else {
- module.verbose('Resetting field value', $field, defaultValue);
- $field.val('');
- }
- });
- module.remove.states();
- },
-
- reset: function() {
- $field.each(function (index, el) {
- var
- $field = $(el),
- $element = $field.parent(),
- $fieldGroup = $field.closest($group),
- $calendar = $field.closest(selector.uiCalendar),
- $prompt = $fieldGroup.find(selector.prompt),
- defaultValue = $field.data(metadata.defaultValue),
- isCheckbox = $element.is(selector.uiCheckbox),
- isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
- isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
- isErrored = $fieldGroup.hasClass(className.error)
- ;
- if(defaultValue === undefined) {
- return;
- }
- if(isErrored) {
- module.verbose('Resetting error on field', $fieldGroup);
- $fieldGroup.removeClass(className.error);
- $prompt.remove();
- }
- if(isDropdown) {
- module.verbose('Resetting dropdown value', $element, defaultValue);
- $element.dropdown('restore defaults', true);
- }
- else if(isCheckbox) {
- module.verbose('Resetting checkbox value', $element, defaultValue);
- $field.prop('checked', defaultValue);
- }
- else if (isCalendar) {
- $calendar.calendar('set date', defaultValue);
- }
- else {
- module.verbose('Resetting field value', $field, defaultValue);
- $field.val(defaultValue);
- }
- });
- module.remove.states();
- },
-
- determine: {
- isValid: function() {
- var
- allValid = true
- ;
- $.each(validation, function(fieldName, field) {
- if( !( module.validate.field(field, fieldName, true) ) ) {
- allValid = false;
- }
- });
- return allValid;
- },
- isDirty: function(e) {
- var formIsDirty = false;
-
- $field.each(function(index, el) {
- var
- $el = $(el),
- isCheckbox = ($el.filter(selector.checkbox).length > 0),
- isDirty
- ;
-
- if (isCheckbox) {
- isDirty = module.is.checkboxDirty($el);
- } else {
- isDirty = module.is.fieldDirty($el);
- }
-
- $el.data(settings.metadata.isDirty, isDirty);
-
- formIsDirty |= isDirty;
- });
-
- if (formIsDirty) {
- module.set.dirty();
- } else {
- module.set.clean();
- }
-
- if (e && e.namespace === 'dirty') {
- e.stopImmediatePropagation();
- e.preventDefault();
- }
- }
- },
-
- is: {
- bracketedRule: function(rule) {
- return (rule.type && rule.type.match(settings.regExp.bracket));
- },
- shorthandFields: function(fields) {
- var
- fieldKeys = Object.keys(fields),
- firstRule = fields[fieldKeys[0]]
- ;
- return module.is.shorthandRules(firstRule);
- },
- // duck type rule test
- shorthandRules: function(rules) {
- return (typeof rules == 'string' || Array.isArray(rules));
- },
- empty: function($field) {
- if(!$field || $field.length === 0) {
- return true;
- }
- else if($field.is(selector.checkbox)) {
- return !$field.is(':checked');
- }
- else {
- return module.is.blank($field);
- }
- },
- blank: function($field) {
- return String($field.val()).trim() === '';
- },
- valid: function(field, showErrors) {
- var
- allValid = true
- ;
- if(field) {
- module.verbose('Checking if field is valid', field);
- return module.validate.field(validation[field], field, !!showErrors);
- }
- else {
- module.verbose('Checking if form is valid');
- $.each(validation, function(fieldName, field) {
- if( !module.is.valid(fieldName, showErrors) ) {
- allValid = false;
- }
- });
- return allValid;
- }
- },
- dirty: function() {
- return dirty;
- },
- clean: function() {
- return !dirty;
- },
- fieldDirty: function($el) {
- var initialValue = $el.data(metadata.defaultValue);
- // Explicitly check for null/undefined here as value may be `false`, so ($el.data(dataInitialValue) || '') would not work
- if (initialValue == null) { initialValue = ''; }
- else if(Array.isArray(initialValue)) {
- initialValue = initialValue.toString();
- }
- var currentValue = $el.val();
- if (currentValue == null) { currentValue = ''; }
- // multiple select values are returned as arrays which are never equal, so do string conversion first
- else if(Array.isArray(currentValue)) {
- currentValue = currentValue.toString();
- }
- // Boolean values can be encoded as "true/false" or "True/False" depending on underlying frameworks so we need a case insensitive comparison
- var boolRegex = /^(true|false)$/i;
- var isBoolValue = boolRegex.test(initialValue) && boolRegex.test(currentValue);
- if (isBoolValue) {
- var regex = new RegExp("^" + initialValue + "$", "i");
- return !regex.test(currentValue);
- }
-
- return currentValue !== initialValue;
- },
- checkboxDirty: function($el) {
- var initialValue = $el.data(metadata.defaultValue);
- var currentValue = $el.is(":checked");
-
- return initialValue !== currentValue;
- },
- justDirty: function() {
- return (history[0] === 'dirty');
- },
- justClean: function() {
- return (history[0] === 'clean');
- }
- },
-
- removeEvents: function() {
- $module.off(eventNamespace);
- $field.off(eventNamespace);
- $submit.off(eventNamespace);
- $field.off(eventNamespace);
- },
-
- event: {
- field: {
- keydown: function(event) {
- var
- $field = $(this),
- key = event.which,
- isInput = $field.is(selector.input),
- isCheckbox = $field.is(selector.checkbox),
- isInDropdown = ($field.closest(selector.uiDropdown).length > 0),
- keyCode = {
- enter : 13,
- escape : 27
- }
- ;
- if( key == keyCode.escape) {
- module.verbose('Escape key pressed blurring field');
- $field
- .blur()
- ;
- }
- if(!event.ctrlKey && key == keyCode.enter && isInput && !isInDropdown && !isCheckbox) {
- if(!keyHeldDown) {
- $field.one('keyup' + eventNamespace, module.event.field.keyup);
- module.submit();
- module.debug('Enter pressed on input submitting form');
- }
- keyHeldDown = true;
- }
- },
- keyup: function() {
- keyHeldDown = false;
- },
- blur: function(event) {
- var
- $field = $(this),
- $fieldGroup = $field.closest($group),
- validationRules = module.get.validation($field)
- ;
- if( $fieldGroup.hasClass(className.error) ) {
- module.debug('Revalidating field', $field, validationRules);
- if(validationRules) {
- module.validate.field( validationRules );
- }
- }
- else if(settings.on == 'blur') {
- if(validationRules) {
- module.validate.field( validationRules );
- }
- }
- },
- change: function(event) {
- var
- $field = $(this),
- $fieldGroup = $field.closest($group),
- validationRules = module.get.validation($field)
- ;
- if(validationRules && (settings.on == 'change' || ( $fieldGroup.hasClass(className.error) && settings.revalidate) )) {
- clearTimeout(module.timer);
- module.timer = setTimeout(function() {
- module.debug('Revalidating field', $field, module.get.validation($field));
- module.validate.field( validationRules );
- if(!settings.inline) {
- module.validate.form(false,true);
- }
- }, settings.delay);
- }
- }
- },
- beforeUnload: function(event) {
- if (module.is.dirty() && !submitting) {
- var event = event || window.event;
-
- // For modern browsers
- if (event) {
- event.returnValue = settings.text.leavingMessage;
- }
-
- // For olders...
- return settings.text.leavingMessage;
- }
- }
-
- },
-
- get: {
- ancillaryValue: function(rule) {
- if(!rule.type || (!rule.value && !module.is.bracketedRule(rule))) {
- return false;
- }
- return (rule.value !== undefined)
- ? rule.value
- : rule.type.match(settings.regExp.bracket)[1] + ''
- ;
- },
- ruleName: function(rule) {
- if( module.is.bracketedRule(rule) ) {
- return rule.type.replace(rule.type.match(settings.regExp.bracket)[0], '');
- }
- return rule.type;
- },
- changeEvent: function(type, $input) {
- if(type == 'checkbox' || type == 'radio' || type == 'hidden' || $input.is('select')) {
- return 'change';
- }
- else {
- return module.get.inputEvent();
- }
- },
- inputEvent: function() {
- return (document.createElement('input').oninput !== undefined)
- ? 'input'
- : (document.createElement('input').onpropertychange !== undefined)
- ? 'propertychange'
- : 'keyup'
- ;
- },
- fieldsFromShorthand: function(fields) {
- var
- fullFields = {}
- ;
- $.each(fields, function(name, rules) {
- if(typeof rules == 'string') {
- rules = [rules];
- }
- fullFields[name] = {
- rules: []
- };
- $.each(rules, function(index, rule) {
- fullFields[name].rules.push({ type: rule });
- });
- });
- return fullFields;
- },
- prompt: function(rule, field) {
- var
- ruleName = module.get.ruleName(rule),
- ancillary = module.get.ancillaryValue(rule),
- $field = module.get.field(field.identifier),
- value = $field.val(),
- prompt = $.isFunction(rule.prompt)
- ? rule.prompt(value)
- : rule.prompt || settings.prompt[ruleName] || settings.text.unspecifiedRule,
- requiresValue = (prompt.search('{value}') !== -1),
- requiresName = (prompt.search('{name}') !== -1),
- $label,
- name
- ;
- if(requiresValue) {
- prompt = prompt.replace(/\{value\}/g, $field.val());
- }
- if(requiresName) {
- $label = $field.closest(selector.group).find('label').eq(0);
- name = ($label.length == 1)
- ? $label.text()
- : $field.prop('placeholder') || settings.text.unspecifiedField
- ;
- prompt = prompt.replace(/\{name\}/g, name);
- }
- prompt = prompt.replace(/\{identifier\}/g, field.identifier);
- prompt = prompt.replace(/\{ruleValue\}/g, ancillary);
- if(!rule.prompt) {
- module.verbose('Using default validation prompt for type', prompt, ruleName);
- }
- return prompt;
- },
- settings: function() {
- if($.isPlainObject(parameters)) {
- var
- keys = Object.keys(parameters),
- isLegacySettings = (keys.length > 0)
- ? (parameters[keys[0]].identifier !== undefined && parameters[keys[0]].rules !== undefined)
- : false
- ;
- if(isLegacySettings) {
- // 1.x (ducktyped)
- settings = $.extend(true, {}, $.fn.form.settings, legacyParameters);
- validation = $.extend({}, $.fn.form.settings.defaults, parameters);
- module.error(settings.error.oldSyntax, element);
- module.verbose('Extending settings from legacy parameters', validation, settings);
- }
- else {
- // 2.x
- if(parameters.fields && module.is.shorthandFields(parameters.fields)) {
- parameters.fields = module.get.fieldsFromShorthand(parameters.fields);
- }
- settings = $.extend(true, {}, $.fn.form.settings, parameters);
- validation = $.extend({}, $.fn.form.settings.defaults, settings.fields);
- module.verbose('Extending settings', validation, settings);
- }
- }
- else {
- settings = $.fn.form.settings;
- validation = $.fn.form.settings.defaults;
- module.verbose('Using default form validation', validation, settings);
- }
-
- // shorthand
- namespace = settings.namespace;
- metadata = settings.metadata;
- selector = settings.selector;
- className = settings.className;
- regExp = settings.regExp;
- error = settings.error;
- moduleNamespace = 'module-' + namespace;
- eventNamespace = '.' + namespace;
-
- // grab instance
- instance = $module.data(moduleNamespace);
-
- // refresh selector cache
- module.refresh();
- },
- field: function(identifier) {
- module.verbose('Finding field with identifier', identifier);
- identifier = module.escape.string(identifier);
- var t;
- if((t=$field.filter('#' + identifier)).length > 0 ) {
- return t;
- }
- if((t=$field.filter('[name="' + identifier +'"]')).length > 0 ) {
- return t;
- }
- if((t=$field.filter('[name="' + identifier +'[]"]')).length > 0 ) {
- return t;
- }
- if((t=$field.filter('[data-' + metadata.validate + '="'+ identifier +'"]')).length > 0 ) {
- return t;
- }
- return $('<input/>');
- },
- fields: function(fields) {
- var
- $fields = $()
- ;
- $.each(fields, function(index, name) {
- $fields = $fields.add( module.get.field(name) );
- });
- return $fields;
- },
- validation: function($field) {
- var
- fieldValidation,
- identifier
- ;
- if(!validation) {
- return false;
- }
- $.each(validation, function(fieldName, field) {
- identifier = field.identifier || fieldName;
- $.each(module.get.field(identifier), function(index, groupField) {
- if(groupField == $field[0]) {
- field.identifier = identifier;
- fieldValidation = field;
- return false;
- }
- });
- });
- return fieldValidation || false;
- },
- value: function (field) {
- var
- fields = [],
- results
- ;
- fields.push(field);
- results = module.get.values.call(element, fields);
- return results[field];
- },
- values: function (fields) {
- var
- $fields = Array.isArray(fields)
- ? module.get.fields(fields)
- : $field,
- values = {}
- ;
- $fields.each(function(index, field) {
- var
- $field = $(field),
- $calendar = $field.closest(selector.uiCalendar),
- name = $field.prop('name'),
- value = $field.val(),
- isCheckbox = $field.is(selector.checkbox),
- isRadio = $field.is(selector.radio),
- isMultiple = (name.indexOf('[]') !== -1),
- isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
- isChecked = (isCheckbox)
- ? $field.is(':checked')
- : false
- ;
- if(name) {
- if(isMultiple) {
- name = name.replace('[]', '');
- if(!values[name]) {
- values[name] = [];
- }
- if(isCheckbox) {
- if(isChecked) {
- values[name].push(value || true);
- }
- else {
- values[name].push(false);
- }
- }
- else {
- values[name].push(value);
- }
- }
- else {
- if(isRadio) {
- if(values[name] === undefined || values[name] === false) {
- values[name] = (isChecked)
- ? value || true
- : false
- ;
- }
- }
- else if(isCheckbox) {
- if(isChecked) {
- values[name] = value || true;
- }
- else {
- values[name] = false;
- }
- }
- else if(isCalendar) {
- var date = $calendar.calendar('get date');
-
- if (date !== null) {
- if (settings.dateHandling == 'date') {
- values[name] = date;
- } else if(settings.dateHandling == 'input') {
- values[name] = $calendar.calendar('get input date')
- } else if (settings.dateHandling == 'formatter') {
- var type = $calendar.calendar('setting', 'type');
-
- switch(type) {
- case 'date':
- values[name] = settings.formatter.date(date);
- break;
-
- case 'datetime':
- values[name] = settings.formatter.datetime(date);
- break;
-
- case 'time':
- values[name] = settings.formatter.time(date);
- break;
-
- case 'month':
- values[name] = settings.formatter.month(date);
- break;
-
- case 'year':
- values[name] = settings.formatter.year(date);
- break;
-
- default:
- module.debug('Wrong calendar mode', $calendar, type);
- values[name] = '';
- }
- }
- } else {
- values[name] = '';
- }
- } else {
- values[name] = value;
- }
- }
- }
- });
- return values;
- },
- dirtyFields: function() {
- return $field.filter(function(index, e) {
- return $(e).data(metadata.isDirty);
- });
- }
- },
-
- has: {
-
- field: function(identifier) {
- module.verbose('Checking for existence of a field with identifier', identifier);
- identifier = module.escape.string(identifier);
- if(typeof identifier !== 'string') {
- module.error(error.identifier, identifier);
- }
- if($field.filter('#' + identifier).length > 0 ) {
- return true;
- }
- else if( $field.filter('[name="' + identifier +'"]').length > 0 ) {
- return true;
- }
- else if( $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]').length > 0 ) {
- return true;
- }
- return false;
- }
-
- },
-
- can: {
- useElement: function(element){
- if ($.fn[element] !== undefined) {
- return true;
- }
- module.error(error.noElement.replace('{element}',element));
- return false;
- }
- },
-
- escape: {
- string: function(text) {
- text = String(text);
- return text.replace(regExp.escape, '\\$&');
- }
- },
-
- add: {
- // alias
- rule: function(name, rules) {
- module.add.field(name, rules);
- },
- field: function(name, rules) {
- // Validation should have at least a standard format
- if(validation[name] === undefined || validation[name].rules === undefined) {
- validation[name] = {
- rules: []
- };
- }
- var
- newValidation = {
- rules: []
- }
- ;
- if(module.is.shorthandRules(rules)) {
- rules = Array.isArray(rules)
- ? rules
- : [rules]
- ;
- $.each(rules, function(_index, rule) {
- newValidation.rules.push({ type: rule });
- });
- }
- else {
- newValidation.rules = rules.rules;
- }
- // For each new rule, check if there's not already one with the same type
- $.each(newValidation.rules, function (_index, rule) {
- if ($.grep(validation[name].rules, function(item){ return item.type == rule.type; }).length == 0) {
- validation[name].rules.push(rule);
- }
- });
- module.debug('Adding rules', newValidation.rules, validation);
- },
- fields: function(fields) {
- var
- newValidation
- ;
- if(fields && module.is.shorthandFields(fields)) {
- newValidation = module.get.fieldsFromShorthand(fields);
- }
- else {
- newValidation = fields;
- }
- validation = $.extend({}, validation, newValidation);
- },
- prompt: function(identifier, errors, internal) {
- var
- $field = module.get.field(identifier),
- $fieldGroup = $field.closest($group),
- $prompt = $fieldGroup.children(selector.prompt),
- promptExists = ($prompt.length !== 0)
- ;
- errors = (typeof errors == 'string')
- ? [errors]
- : errors
- ;
- module.verbose('Adding field error state', identifier);
- if(!internal) {
- $fieldGroup
- .addClass(className.error)
- ;
- }
- if(settings.inline) {
- if(!promptExists) {
- $prompt = settings.templates.prompt(errors, className.label);
- $prompt
- .appendTo($fieldGroup)
- ;
- }
- $prompt
- .html(errors[0])
- ;
- if(!promptExists) {
- if(settings.transition && module.can.useElement('transition') && $module.transition('is supported')) {
- module.verbose('Displaying error with css transition', settings.transition);
- $prompt.transition(settings.transition + ' in', settings.duration);
- }
- else {
- module.verbose('Displaying error with fallback javascript animation');
- $prompt
- .fadeIn(settings.duration)
- ;
- }
- }
- else {
- module.verbose('Inline errors are disabled, no inline error added', identifier);
- }
- }
- },
- errors: function(errors) {
- module.debug('Adding form error messages', errors);
- module.set.error();
- $message
- .html( settings.templates.error(errors) )
- ;
- }
- },
-
- remove: {
- errors: function() {
- module.debug('Removing form error messages');
- $message.empty();
- },
- states: function() {
- $module.removeClass(className.error).removeClass(className.success);
- if(!settings.inline) {
- module.remove.errors();
- }
- module.determine.isDirty();
- },
- rule: function(field, rule) {
- var
- rules = Array.isArray(rule)
- ? rule
- : [rule]
- ;
- if(validation[field] === undefined || !Array.isArray(validation[field].rules)) {
- return;
- }
- if(rule === undefined) {
- module.debug('Removed all rules');
- validation[field].rules = [];
- return;
- }
- $.each(validation[field].rules, function(index, rule) {
- if(rule && rules.indexOf(rule.type) !== -1) {
- module.debug('Removed rule', rule.type);
- validation[field].rules.splice(index, 1);
- }
- });
- },
- field: function(field) {
- var
- fields = Array.isArray(field)
- ? field
- : [field]
- ;
- $.each(fields, function(index, field) {
- module.remove.rule(field);
- });
- },
- // alias
- rules: function(field, rules) {
- if(Array.isArray(field)) {
- $.each(field, function(index, field) {
- module.remove.rule(field, rules);
- });
- }
- else {
- module.remove.rule(field, rules);
- }
- },
- fields: function(fields) {
- module.remove.field(fields);
- },
- prompt: function(identifier) {
- var
- $field = module.get.field(identifier),
- $fieldGroup = $field.closest($group),
- $prompt = $fieldGroup.children(selector.prompt)
- ;
- $fieldGroup
- .removeClass(className.error)
- ;
- if(settings.inline && $prompt.is(':visible')) {
- module.verbose('Removing prompt for field', identifier);
- if(settings.transition && module.can.useElement('transition') && $module.transition('is supported')) {
- $prompt.transition(settings.transition + ' out', settings.duration, function() {
- $prompt.remove();
- });
- }
- else {
- $prompt
- .fadeOut(settings.duration, function(){
- $prompt.remove();
- })
- ;
- }
- }
- }
- },
-
- set: {
- success: function() {
- $module
- .removeClass(className.error)
- .addClass(className.success)
- ;
- },
- defaults: function () {
- $field.each(function (index, el) {
- var
- $el = $(el),
- $parent = $el.parent(),
- isCheckbox = ($el.filter(selector.checkbox).length > 0),
- isDropdown = $parent.is(selector.uiDropdown) && module.can.useElement('dropdown'),
- $calendar = $el.closest(selector.uiCalendar),
- isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
- value = (isCheckbox)
- ? $el.is(':checked')
- : $el.val()
- ;
- if (isDropdown) {
- $parent.dropdown('save defaults');
- }
- else if (isCalendar) {
- $calendar.calendar('refresh');
- }
- $el.data(metadata.defaultValue, value);
- $el.data(metadata.isDirty, false);
- });
- },
- error: function() {
- $module
- .removeClass(className.success)
- .addClass(className.error)
- ;
- },
- value: function (field, value) {
- var
- fields = {}
- ;
- fields[field] = value;
- return module.set.values.call(element, fields);
- },
- values: function (fields) {
- if($.isEmptyObject(fields)) {
- return;
- }
- $.each(fields, function(key, value) {
- var
- $field = module.get.field(key),
- $element = $field.parent(),
- $calendar = $field.closest(selector.uiCalendar),
- isMultiple = Array.isArray(value),
- isCheckbox = $element.is(selector.uiCheckbox) && module.can.useElement('checkbox'),
- isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
- isRadio = ($field.is(selector.radio) && isCheckbox),
- isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
- fieldExists = ($field.length > 0),
- $multipleField
- ;
- if(fieldExists) {
- if(isMultiple && isCheckbox) {
- module.verbose('Selecting multiple', value, $field);
- $element.checkbox('uncheck');
- $.each(value, function(index, value) {
- $multipleField = $field.filter('[value="' + value + '"]');
- $element = $multipleField.parent();
- if($multipleField.length > 0) {
- $element.checkbox('check');
- }
- });
- }
- else if(isRadio) {
- module.verbose('Selecting radio value', value, $field);
- $field.filter('[value="' + value + '"]')
- .parent(selector.uiCheckbox)
- .checkbox('check')
- ;
- }
- else if(isCheckbox) {
- module.verbose('Setting checkbox value', value, $element);
- if(value === true || value === 1) {
- $element.checkbox('check');
- }
- else {
- $element.checkbox('uncheck');
- }
- }
- else if(isDropdown) {
- module.verbose('Setting dropdown value', value, $element);
- $element.dropdown('set selected', value);
- }
- else if (isCalendar) {
- $calendar.calendar('set date',value);
- }
- else {
- module.verbose('Setting field value', value, $field);
- $field.val(value);
- }
- }
- });
- },
- dirty: function() {
- module.verbose('Setting state dirty');
- dirty = true;
- history[0] = history[1];
- history[1] = 'dirty';
-
- if (module.is.justClean()) {
- $module.trigger('dirty');
- }
- },
- clean: function() {
- module.verbose('Setting state clean');
- dirty = false;
- history[0] = history[1];
- history[1] = 'clean';
-
- if (module.is.justDirty()) {
- $module.trigger('clean');
- }
- },
- asClean: function() {
- module.set.defaults();
- module.set.clean();
- },
- asDirty: function() {
- module.set.defaults();
- module.set.dirty();
- },
- autoCheck: function() {
- module.debug('Enabling auto check on required fields');
- $field.each(function (_index, el) {
- var
- $el = $(el),
- $elGroup = $(el).closest($group),
- isCheckbox = ($el.filter(selector.checkbox).length > 0),
- isRequired = $el.prop('required') || $elGroup.hasClass(className.required) || $elGroup.parent().hasClass(className.required),
- isDisabled = $el.is(':disabled') || $elGroup.hasClass(className.disabled) || $elGroup.parent().hasClass(className.disabled),
- validation = module.get.validation($el),
- hasEmptyRule = validation
- ? $.grep(validation.rules, function(rule) { return rule.type == "empty" }) !== 0
- : false,
- identifier = validation.identifier || $el.attr('id') || $el.attr('name') || $el.data(metadata.validate)
- ;
- if (isRequired && !isDisabled && !hasEmptyRule && identifier !== undefined) {
- if (isCheckbox) {
- module.verbose("Adding 'checked' rule on field", identifier);
- module.add.rule(identifier, "checked");
- } else {
- module.verbose("Adding 'empty' rule on field", identifier);
- module.add.rule(identifier, "empty");
- }
- }
- });
- }
- },
-
- validate: {
-
- form: function(event, ignoreCallbacks) {
- var values = module.get.values();
-
- // input keydown event will fire submit repeatedly by browser default
- if(keyHeldDown) {
- return false;
- }
-
- // reset errors
- formErrors = [];
- if( module.determine.isValid() ) {
- module.debug('Form has no validation errors, submitting');
- module.set.success();
- if(!settings.inline) {
- module.remove.errors();
- }
- if(ignoreCallbacks !== true) {
- return settings.onSuccess.call(element, event, values);
- }
- }
- else {
- module.debug('Form has errors');
- submitting = false;
- module.set.error();
- if(!settings.inline) {
- module.add.errors(formErrors);
- }
- // prevent ajax submit
- if(event && $module.data('moduleApi') !== undefined) {
- event.stopImmediatePropagation();
- }
- if(ignoreCallbacks !== true) {
- return settings.onFailure.call(element, formErrors, values);
- }
- }
- },
-
- // takes a validation object and returns whether field passes validation
- field: function(field, fieldName, showErrors) {
- showErrors = (showErrors !== undefined)
- ? showErrors
- : true
- ;
- if(typeof field == 'string') {
- module.verbose('Validating field', field);
- fieldName = field;
- field = validation[field];
- }
- var
- identifier = field.identifier || fieldName,
- $field = module.get.field(identifier),
- $dependsField = (field.depends)
- ? module.get.field(field.depends)
- : false,
- fieldValid = true,
- fieldErrors = []
- ;
- if(!field.identifier) {
- module.debug('Using field name as identifier', identifier);
- field.identifier = identifier;
- }
- var isDisabled = !$field.filter(':not(:disabled)').length;
- if(isDisabled) {
- module.debug('Field is disabled. Skipping', identifier);
- }
- else if(field.optional && module.is.blank($field)){
- module.debug('Field is optional and blank. Skipping', identifier);
- }
- else if(field.depends && module.is.empty($dependsField)) {
- module.debug('Field depends on another value that is not present or empty. Skipping', $dependsField);
- }
- else if(field.rules !== undefined) {
- if(showErrors) {
- $field.closest($group).removeClass(className.error);
- }
- $.each(field.rules, function(index, rule) {
- if( module.has.field(identifier)) {
- var invalidFields = module.validate.rule(field, rule,true) || [];
- if (invalidFields.length>0){
- module.debug('Field is invalid', identifier, rule.type);
- fieldErrors.push(module.get.prompt(rule, field));
- fieldValid = false;
- if(showErrors){
- $(invalidFields).closest($group).addClass(className.error);
- }
- }
- }
- });
- }
- if(fieldValid) {
- if(showErrors) {
- module.remove.prompt(identifier, fieldErrors);
- settings.onValid.call($field);
- }
- }
- else {
- if(showErrors) {
- formErrors = formErrors.concat(fieldErrors);
- module.add.prompt(identifier, fieldErrors, true);
- settings.onInvalid.call($field, fieldErrors);
- }
- return false;
- }
- return true;
- },
-
- // takes validation rule and returns whether field passes rule
- rule: function(field, rule, internal) {
- var
- $field = module.get.field(field.identifier),
- ancillary = module.get.ancillaryValue(rule),
- ruleName = module.get.ruleName(rule),
- ruleFunction = settings.rules[ruleName],
- invalidFields = [],
- isCheckbox = $field.is(selector.checkbox),
- isValid = function(field){
- var value = (isCheckbox ? $(field).filter(':checked').val() : $(field).val());
- // cast to string avoiding encoding special values
- value = (value === undefined || value === '' || value === null)
- ? ''
- : (settings.shouldTrim) ? String(value + '').trim() : String(value + '')
- ;
- return ruleFunction.call(field, value, ancillary, $module);
- }
- ;
- if( !$.isFunction(ruleFunction) ) {
- module.error(error.noRule, ruleName);
- return;
- }
- if(isCheckbox) {
- if (!isValid($field)) {
- invalidFields = $field;
- }
- } else {
- $.each($field, function (index, field) {
- if (!isValid(field)) {
- invalidFields.push(field);
- }
- });
- }
- return internal ? invalidFields : !(invalidFields.length>0);
- }
- },
-
- setting: function(name, value) {
- if( $.isPlainObject(name) ) {
- $.extend(true, settings, name);
- }
- else if(value !== undefined) {
- settings[name] = value;
- }
- else {
- return settings[name];
- }
- },
- internal: function(name, value) {
- if( $.isPlainObject(name) ) {
- $.extend(true, module, name);
- }
- else if(value !== undefined) {
- module[name] = value;
- }
- else {
- return module[name];
- }
- },
- debug: function() {
- if(!settings.silent && settings.debug) {
- if(settings.performance) {
- module.performance.log(arguments);
- }
- else {
- module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
- module.debug.apply(console, arguments);
- }
- }
- },
- verbose: function() {
- if(!settings.silent && settings.verbose && settings.debug) {
- if(settings.performance) {
- module.performance.log(arguments);
- }
- else {
- module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
- module.verbose.apply(console, arguments);
- }
- }
- },
- error: function() {
- if(!settings.silent) {
- module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
- module.error.apply(console, arguments);
- }
- },
- performance: {
- log: function(message) {
- var
- currentTime,
- executionTime,
- previousTime
- ;
- if(settings.performance) {
- currentTime = new Date().getTime();
- previousTime = time || currentTime;
- executionTime = currentTime - previousTime;
- time = currentTime;
- performance.push({
- 'Name' : message[0],
- 'Arguments' : [].slice.call(message, 1) || '',
- 'Element' : element,
- 'Execution Time' : executionTime
- });
- }
- clearTimeout(module.performance.timer);
- module.performance.timer = setTimeout(module.performance.display, 500);
- },
- display: function() {
- var
- title = settings.name + ':',
- totalTime = 0
- ;
- time = false;
- clearTimeout(module.performance.timer);
- $.each(performance, function(index, data) {
- totalTime += data['Execution Time'];
- });
- title += ' ' + totalTime + 'ms';
- if(moduleSelector) {
- title += ' \'' + moduleSelector + '\'';
- }
- if($allModules.length > 1) {
- title += ' ' + '(' + $allModules.length + ')';
- }
- if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
- console.groupCollapsed(title);
- if(console.table) {
- console.table(performance);
- }
- else {
- $.each(performance, function(index, data) {
- console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
- });
- }
- console.groupEnd();
- }
- performance = [];
- }
- },
- invoke: function(query, passedArguments, context) {
- var
- object = instance,
- maxDepth,
- found,
- response
- ;
- passedArguments = passedArguments || queryArguments;
- context = element || context;
- if(typeof query == 'string' && object !== undefined) {
- query = query.split(/[\. ]/);
- maxDepth = query.length - 1;
- $.each(query, function(depth, value) {
- var camelCaseValue = (depth != maxDepth)
- ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
- : query
- ;
- if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
- object = object[camelCaseValue];
- }
- else if( object[camelCaseValue] !== undefined ) {
- found = object[camelCaseValue];
- return false;
- }
- else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
- object = object[value];
- }
- else if( object[value] !== undefined ) {
- found = object[value];
- return false;
- }
- else {
- return false;
- }
- });
- }
- if( $.isFunction( found ) ) {
- response = found.apply(context, passedArguments);
- }
- else if(found !== undefined) {
- response = found;
- }
- if(Array.isArray(returnedValue)) {
- returnedValue.push(response);
- }
- else if(returnedValue !== undefined) {
- returnedValue = [returnedValue, response];
- }
- else if(response !== undefined) {
- returnedValue = response;
- }
- return found;
- }
- };
- module.initialize();
- })
- ;
-
- return (returnedValue !== undefined)
- ? returnedValue
- : this
- ;
-};
-
-$.fn.form.settings = {
-
- name : 'Form',
- namespace : 'form',
-
- debug : false,
- verbose : false,
- performance : true,
-
- fields : false,
-
- keyboardShortcuts : true,
- on : 'submit',
- inline : false,
-
- delay : 200,
- revalidate : true,
- shouldTrim : true,
-
- transition : 'scale',
- duration : 200,
-
- autoCheckRequired : false,
- preventLeaving : false,
- dateHandling : 'date', // 'date', 'input', 'formatter'
-
- onValid : function() {},
- onInvalid : function() {},
- onSuccess : function() { return true; },
- onFailure : function() { return false; },
- onDirty : function() {},
- onClean : function() {},
-
- metadata : {
- defaultValue : 'default',
- validate : 'validate',
- isDirty : 'isDirty'
- },
-
- regExp: {
- htmlID : /^[a-zA-Z][\w:.-]*$/g,
- bracket : /\[(.*)\]/i,
- decimal : /^\d+\.?\d*$/,
- email : /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,
- escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|:,=@]/g,
- flags : /^\/(.*)\/(.*)?/,
- integer : /^\-?\d+$/,
- number : /^\-?\d*(\.\d+)?$/,
- url : /(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/i
- },
-
- text: {
- unspecifiedRule : 'Please enter a valid value',
- unspecifiedField : 'This field',
- leavingMessage : 'There are unsaved changes on this page which will be discarded if you continue.'
- },
-
- prompt: {
- empty : '{name} must have a value',
- checked : '{name} must be checked',
- email : '{name} must be a valid e-mail',
- url : '{name} must be a valid url',
- regExp : '{name} is not formatted correctly',
- integer : '{name} must be an integer',
- decimal : '{name} must be a decimal number',
- number : '{name} must be set to a number',
- is : '{name} must be "{ruleValue}"',
- isExactly : '{name} must be exactly "{ruleValue}"',
- not : '{name} cannot be set to "{ruleValue}"',
- notExactly : '{name} cannot be set to exactly "{ruleValue}"',
- contain : '{name} must contain "{ruleValue}"',
- containExactly : '{name} must contain exactly "{ruleValue}"',
- doesntContain : '{name} cannot contain "{ruleValue}"',
- doesntContainExactly : '{name} cannot contain exactly "{ruleValue}"',
- minLength : '{name} must be at least {ruleValue} characters',
- length : '{name} must be at least {ruleValue} characters',
- exactLength : '{name} must be exactly {ruleValue} characters',
- maxLength : '{name} cannot be longer than {ruleValue} characters',
- match : '{name} must match {ruleValue} field',
- different : '{name} must have a different value than {ruleValue} field',
- creditCard : '{name} must be a valid credit card number',
- minCount : '{name} must have at least {ruleValue} choices',
- exactCount : '{name} must have exactly {ruleValue} choices',
- maxCount : '{name} must have {ruleValue} or less choices'
- },
-
- selector : {
- checkbox : 'input[type="checkbox"], input[type="radio"]',
- clear : '.clear',
- field : 'input:not(.search), textarea, select',
- group : '.field',
- input : 'input',
- message : '.error.message',
- prompt : '.prompt.label',
- radio : 'input[type="radio"]',
- reset : '.reset:not([type="reset"])',
- submit : '.submit:not([type="submit"])',
- uiCheckbox : '.ui.checkbox',
- uiDropdown : '.ui.dropdown',
- uiCalendar : '.ui.calendar'
- },
-
- className : {
- error : 'error',
- label : 'ui basic red pointing prompt label',
- pressed : 'down',
- success : 'success',
- required : 'required',
- disabled : 'disabled'
- },
-
- error: {
- identifier : 'You must specify a string identifier for each field',
- method : 'The method you called is not defined.',
- noRule : 'There is no rule matching the one you specified',
- oldSyntax : 'Starting in 2.0 forms now only take a single settings object. Validation settings converted to new syntax automatically.',
- noElement : 'This module requires ui {element}'
- },
-
- templates: {
-
- // template that produces error message
- error: function(errors) {
- var
- html = '<ul class="list">'
- ;
- $.each(errors, function(index, value) {
- html += '<li>' + value + '</li>';
- });
- html += '</ul>';
- return $(html);
- },
-
- // template that produces label
- prompt: function(errors, labelClasses) {
- return $('<div/>')
- .addClass(labelClasses)
- .html(errors[0])
- ;
- }
- },
-
- formatter: {
- date: function(date) {
- return Intl.DateTimeFormat('en-GB').format(date);
- },
- datetime: function(date) {
- return Intl.DateTimeFormat('en-GB', {
- year: "numeric",
- month: "2-digit",
- day: "2-digit",
- hour: '2-digit',
- minute: '2-digit',
- second: '2-digit'
- }).format(date);
- },
- time: function(date) {
- return Intl.DateTimeFormat('en-GB', {
- hour: '2-digit',
- minute: '2-digit',
- second: '2-digit'
- }).format(date);
- },
- month: function(date) {
- return Intl.DateTimeFormat('en-GB', {
- month: '2-digit',
- year: 'numeric'
- }).format(date);
- },
- year: function(date) {
- return Intl.DateTimeFormat('en-GB', {
- year: 'numeric'
- }).format(date);
- }
- },
-
- rules: {
-
- // is not empty or blank string
- empty: function(value) {
- return !(value === undefined || '' === value || Array.isArray(value) && value.length === 0);
- },
-
- // checkbox checked
- checked: function() {
- return ($(this).filter(':checked').length > 0);
- },
-
- // is most likely an email
- email: function(value){
- return $.fn.form.settings.regExp.email.test(value);
- },
-
- // value is most likely url
- url: function(value) {
- return $.fn.form.settings.regExp.url.test(value);
- },
-
- // matches specified regExp
- regExp: function(value, regExp) {
- if(regExp instanceof RegExp) {
- return value.match(regExp);
- }
- var
- regExpParts = regExp.match($.fn.form.settings.regExp.flags),
- flags
- ;
- // regular expression specified as /baz/gi (flags)
- if(regExpParts) {
- regExp = (regExpParts.length >= 2)
- ? regExpParts[1]
- : regExp
- ;
- flags = (regExpParts.length >= 3)
- ? regExpParts[2]
- : ''
- ;
- }
- return value.match( new RegExp(regExp, flags) );
- },
-
- // is valid integer or matches range
- integer: function(value, range) {
- var
- intRegExp = $.fn.form.settings.regExp.integer,
- min,
- max,
- parts
- ;
- if( !range || ['', '..'].indexOf(range) !== -1) {
- // do nothing
- }
- else if(range.indexOf('..') == -1) {
- if(intRegExp.test(range)) {
- min = max = range - 0;
- }
- }
- else {
- parts = range.split('..', 2);
- if(intRegExp.test(parts[0])) {
- min = parts[0] - 0;
- }
- if(intRegExp.test(parts[1])) {
- max = parts[1] - 0;
- }
- }
- return (
- intRegExp.test(value) &&
- (min === undefined || value >= min) &&
- (max === undefined || value <= max)
- );
- },
-
- // is valid number (with decimal)
- decimal: function(value) {
- return $.fn.form.settings.regExp.decimal.test(value);
- },
-
- // is valid number
- number: function(value) {
- return $.fn.form.settings.regExp.number.test(value);
- },
-
- // is value (case insensitive)
- is: function(value, text) {
- text = (typeof text == 'string')
- ? text.toLowerCase()
- : text
- ;
- value = (typeof value == 'string')
- ? value.toLowerCase()
- : value
- ;
- return (value == text);
- },
-
- // is value
- isExactly: function(value, text) {
- return (value == text);
- },
-
- // value is not another value (case insensitive)
- not: function(value, notValue) {
- value = (typeof value == 'string')
- ? value.toLowerCase()
- : value
- ;
- notValue = (typeof notValue == 'string')
- ? notValue.toLowerCase()
- : notValue
- ;
- return (value != notValue);
- },
-
- // value is not another value (case sensitive)
- notExactly: function(value, notValue) {
- return (value != notValue);
- },
-
- // value contains text (insensitive)
- contains: function(value, text) {
- // escape regex characters
- text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
- return (value.search( new RegExp(text, 'i') ) !== -1);
- },
-
- // value contains text (case sensitive)
- containsExactly: function(value, text) {
- // escape regex characters
- text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
- return (value.search( new RegExp(text) ) !== -1);
- },
-
- // value contains text (insensitive)
- doesntContain: function(value, text) {
- // escape regex characters
- text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
- return (value.search( new RegExp(text, 'i') ) === -1);
- },
-
- // value contains text (case sensitive)
- doesntContainExactly: function(value, text) {
- // escape regex characters
- text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
- return (value.search( new RegExp(text) ) === -1);
- },
-
- // is at least string length
- minLength: function(value, requiredLength) {
- return (value !== undefined)
- ? (value.length >= requiredLength)
- : false
- ;
- },
-
- // see rls notes for 2.0.6 (this is a duplicate of minLength)
- length: function(value, requiredLength) {
- return (value !== undefined)
- ? (value.length >= requiredLength)
- : false
- ;
- },
-
- // is exactly length
- exactLength: function(value, requiredLength) {
- return (value !== undefined)
- ? (value.length == requiredLength)
- : false
- ;
- },
-
- // is less than length
- maxLength: function(value, maxLength) {
- return (value !== undefined)
- ? (value.length <= maxLength)
- : false
- ;
- },
-
- // matches another field
- match: function(value, identifier, $module) {
- var
- matchingValue,
- matchingElement
- ;
- if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
- matchingValue = matchingElement.val();
- }
- else if((matchingElement = $module.find('#' + identifier)).length > 0) {
- matchingValue = matchingElement.val();
- }
- else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
- matchingValue = matchingElement.val();
- }
- else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
- matchingValue = matchingElement;
- }
- return (matchingValue !== undefined)
- ? ( value.toString() == matchingValue.toString() )
- : false
- ;
- },
-
- // different than another field
- different: function(value, identifier, $module) {
- // use either id or name of field
- var
- matchingValue,
- matchingElement
- ;
- if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
- matchingValue = matchingElement.val();
- }
- else if((matchingElement = $module.find('#' + identifier)).length > 0) {
- matchingValue = matchingElement.val();
- }
- else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
- matchingValue = matchingElement.val();
- }
- else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
- matchingValue = matchingElement;
- }
- return (matchingValue !== undefined)
- ? ( value.toString() !== matchingValue.toString() )
- : false
- ;
- },
-
- creditCard: function(cardNumber, cardTypes) {
- var
- cards = {
- visa: {
- pattern : /^4/,
- length : [16]
- },
- amex: {
- pattern : /^3[47]/,
- length : [15]
- },
- mastercard: {
- pattern : /^5[1-5]/,
- length : [16]
- },
- discover: {
- pattern : /^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)/,
- length : [16]
- },
- unionPay: {
- pattern : /^(62|88)/,
- length : [16, 17, 18, 19]
- },
- jcb: {
- pattern : /^35(2[89]|[3-8][0-9])/,
- length : [16]
- },
- maestro: {
- pattern : /^(5018|5020|5038|6304|6759|676[1-3])/,
- length : [12, 13, 14, 15, 16, 17, 18, 19]
- },
- dinersClub: {
- pattern : /^(30[0-5]|^36)/,
- length : [14]
- },
- laser: {
- pattern : /^(6304|670[69]|6771)/,
- length : [16, 17, 18, 19]
- },
- visaElectron: {
- pattern : /^(4026|417500|4508|4844|491(3|7))/,
- length : [16]
- }
- },
- valid = {},
- validCard = false,
- requiredTypes = (typeof cardTypes == 'string')
- ? cardTypes.split(',')
- : false,
- unionPay,
- validation
- ;
-
- if(typeof cardNumber !== 'string' || cardNumber.length === 0) {
- return;
- }
-
- // allow dashes in card
- cardNumber = cardNumber.replace(/[\-]/g, '');
-
- // verify card types
- if(requiredTypes) {
- $.each(requiredTypes, function(index, type){
- // verify each card type
- validation = cards[type];
- if(validation) {
- valid = {
- length : ($.inArray(cardNumber.length, validation.length) !== -1),
- pattern : (cardNumber.search(validation.pattern) !== -1)
- };
- if(valid.length && valid.pattern) {
- validCard = true;
- }
- }
- });
-
- if(!validCard) {
- return false;
- }
- }
-
- // skip luhn for UnionPay
- unionPay = {
- number : ($.inArray(cardNumber.length, cards.unionPay.length) !== -1),
- pattern : (cardNumber.search(cards.unionPay.pattern) !== -1)
- };
- if(unionPay.number && unionPay.pattern) {
- return true;
- }
-
- // verify luhn, adapted from <https://gist.github.com/2134376>
- var
- length = cardNumber.length,
- multiple = 0,
- producedValue = [
- [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
- [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
- ],
- sum = 0
- ;
- while (length--) {
- sum += producedValue[multiple][parseInt(cardNumber.charAt(length), 10)];
- multiple ^= 1;
- }
- return (sum % 10 === 0 && sum > 0);
- },
-
- minCount: function(value, minCount) {
- if(minCount == 0) {
- return true;
- }
- if(minCount == 1) {
- return (value !== '');
- }
- return (value.split(',').length >= minCount);
- },
-
- exactCount: function(value, exactCount) {
- if(exactCount == 0) {
- return (value === '');
- }
- if(exactCount == 1) {
- return (value !== '' && value.search(',') === -1);
- }
- return (value.split(',').length == exactCount);
- },
-
- maxCount: function(value, maxCount) {
- if(maxCount == 0) {
- return false;
- }
- if(maxCount == 1) {
- return (value.search(',') === -1);
- }
- return (value.split(',').length <= maxCount);
- }
- }
-
-};
-
-})( jQuery, window, document );
-
-/*!
- * # Fomantic-UI - Modal
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-;(function ($, window, document, undefined) {
-
-'use strict';
-
-$.isFunction = $.isFunction || function(obj) {
- return typeof obj === "function" && typeof obj.nodeType !== "number";
-};
-
-window = (typeof window != 'undefined' && window.Math == Math)
- ? window
- : (typeof self != 'undefined' && self.Math == Math)
- ? self
- : Function('return this')()
-;
-
-$.fn.modal = function(parameters) {
- var
- $allModules = $(this),
- $window = $(window),
- $document = $(document),
- $body = $('body'),
-
- moduleSelector = $allModules.selector || '',
-
- time = new Date().getTime(),
- performance = [],
-
- query = arguments[0],
- methodInvoked = (typeof query == 'string'),
- queryArguments = [].slice.call(arguments, 1),
-
- requestAnimationFrame = window.requestAnimationFrame
- || window.mozRequestAnimationFrame
- || window.webkitRequestAnimationFrame
- || window.msRequestAnimationFrame
- || function(callback) { setTimeout(callback, 0); },
-
- returnedValue
- ;
-
- $allModules
- .each(function() {
- var
- settings = ( $.isPlainObject(parameters) )
- ? $.extend(true, {}, $.fn.modal.settings, parameters)
- : $.extend({}, $.fn.modal.settings),
-
- selector = settings.selector,
- className = settings.className,
- namespace = settings.namespace,
- error = settings.error,
-
- eventNamespace = '.' + namespace,
- moduleNamespace = 'module-' + namespace,
-
- $module = $(this),
- $context = $(settings.context),
- $close = $module.find(selector.close),
-
- $allModals,
- $otherModals,
- $focusedElement,
- $dimmable,
- $dimmer,
-
- element = this,
- instance = $module.data(moduleNamespace),
-
- ignoreRepeatedEvents = false,
-
- initialMouseDownInModal,
- initialMouseDownInScrollbar,
- initialBodyMargin = '',
- tempBodyMargin = '',
-
- elementEventNamespace,
- id,
- observer,
- module
- ;
- module = {
-
- initialize: function() {
- module.cache = {};
- module.verbose('Initializing dimmer', $context);
-
- module.create.id();
- module.create.dimmer();
-
- if ( settings.allowMultiple ) {
- module.create.innerDimmer();
- }
- if (!settings.centered){
- $module.addClass('top aligned');
- }
- module.refreshModals();
-
- module.bind.events();
- if(settings.observeChanges) {
- module.observeChanges();
- }
- module.instantiate();
- },
-
- instantiate: function() {
- module.verbose('Storing instance of modal');
- instance = module;
- $module
- .data(moduleNamespace, instance)
- ;
- },
-
- create: {
- dimmer: function() {
- var
- defaultSettings = {
- debug : settings.debug,
- dimmerName : 'modals'
- },
- dimmerSettings = $.extend(true, defaultSettings, settings.dimmerSettings)
- ;
- if($.fn.dimmer === undefined) {
- module.error(error.dimmer);
- return;
- }
- module.debug('Creating dimmer');
- $dimmable = $context.dimmer(dimmerSettings);
- if(settings.detachable) {
- module.verbose('Modal is detachable, moving content into dimmer');
- $dimmable.dimmer('add content', $module);
- }
- else {
- module.set.undetached();
- }
- $dimmer = $dimmable.dimmer('get dimmer');
- },
- id: function() {
- id = (Math.random().toString(16) + '000000000').substr(2, 8);
- elementEventNamespace = '.' + id;
- module.verbose('Creating unique id for element', id);
- },
- innerDimmer: function() {
- if ( $module.find(selector.dimmer).length == 0 ) {
- $module.prepend('<div class="ui inverted dimmer"></div>');
- }
- }
- },
-
- destroy: function() {
- if (observer) {
- observer.disconnect();
- }
- module.verbose('Destroying previous modal');
- $module
- .removeData(moduleNamespace)
- .off(eventNamespace)
- ;
- $window.off(elementEventNamespace);
- $dimmer.off(elementEventNamespace);
- $close.off(eventNamespace);
- $context.dimmer('destroy');
- },
-
- observeChanges: function() {
- if('MutationObserver' in window) {
- observer = new MutationObserver(function(mutations) {
- module.debug('DOM tree modified, refreshing');
- module.refresh();
- });
- observer.observe(element, {
- childList : true,
- subtree : true
- });
- module.debug('Setting up mutation observer', observer);
- }
- },
-
- refresh: function() {
- module.remove.scrolling();
- module.cacheSizes();
- if(!module.can.useFlex()) {
- module.set.modalOffset();
- }
- module.set.screenHeight();
- module.set.type();
- },
-
- refreshModals: function() {
- $otherModals = $module.siblings(selector.modal);
- $allModals = $otherModals.add($module);
- },
-
- attachEvents: function(selector, event) {
- var
- $toggle = $(selector)
- ;
- event = $.isFunction(module[event])
- ? module[event]
- : module.toggle
- ;
- if($toggle.length > 0) {
- module.debug('Attaching modal events to element', selector, event);
- $toggle
- .off(eventNamespace)
- .on('click' + eventNamespace, event)
- ;
- }
- else {
- module.error(error.notFound, selector);
- }
- },
-
- bind: {
- events: function() {
- module.verbose('Attaching events');
- $module
- .on('click' + eventNamespace, selector.close, module.event.close)
- .on('click' + eventNamespace, selector.approve, module.event.approve)
- .on('click' + eventNamespace, selector.deny, module.event.deny)
- ;
- $window
- .on('resize' + elementEventNamespace, module.event.resize)
- ;
- },
- scrollLock: function() {
- // touch events default to passive, due to changes in chrome to optimize mobile perf
- $dimmable.get(0).addEventListener('touchmove', module.event.preventScroll, { passive: false });
- }
- },
-
- unbind: {
- scrollLock: function() {
- $dimmable.get(0).removeEventListener('touchmove', module.event.preventScroll, { passive: false });
- }
- },
-
- get: {
- id: function() {
- return (Math.random().toString(16) + '000000000').substr(2, 8);
- }
- },
-
- event: {
- approve: function() {
- if(ignoreRepeatedEvents || settings.onApprove.call(element, $(this)) === false) {
- module.verbose('Approve callback returned false cancelling hide');
- return;
- }
- ignoreRepeatedEvents = true;
- module.hide(function() {
- ignoreRepeatedEvents = false;
- });
- },
- preventScroll: function(event) {
- if(event.target.className.indexOf('dimmer') !== -1) {
- event.preventDefault();
- }
- },
- deny: function() {
- if(ignoreRepeatedEvents || settings.onDeny.call(element, $(this)) === false) {
- module.verbose('Deny callback returned false cancelling hide');
- return;
- }
- ignoreRepeatedEvents = true;
- module.hide(function() {
- ignoreRepeatedEvents = false;
- });
- },
- close: function() {
- module.hide();
- },
- mousedown: function(event) {
- var
- $target = $(event.target),
- isRtl = module.is.rtl();
- ;
- initialMouseDownInModal = ($target.closest(selector.modal).length > 0);
- if(initialMouseDownInModal) {
- module.verbose('Mouse down event registered inside the modal');
- }
- initialMouseDownInScrollbar = module.is.scrolling() && ((!isRtl && $(window).outerWidth() - settings.scrollbarWidth <= event.clientX) || (isRtl && settings.scrollbarWidth >= event.clientX));
- if(initialMouseDownInScrollbar) {
- module.verbose('Mouse down event registered inside the scrollbar');
- }
- },
- mouseup: function(event) {
- if(!settings.closable) {
- module.verbose('Dimmer clicked but closable setting is disabled');
- return;
- }
- if(initialMouseDownInModal) {
- module.debug('Dimmer clicked but mouse down was initially registered inside the modal');
- return;
- }
- if(initialMouseDownInScrollbar){
- module.debug('Dimmer clicked but mouse down was initially registered inside the scrollbar');
- return;
- }
- var
- $target = $(event.target),
- isInModal = ($target.closest(selector.modal).length > 0),
- isInDOM = $.contains(document.documentElement, event.target)
- ;
- if(!isInModal && isInDOM && module.is.active() && $module.hasClass(className.front) ) {
- module.debug('Dimmer clicked, hiding all modals');
- if(settings.allowMultiple) {
- if(!module.hideAll()) {
- return;
- }
- }
- else if(!module.hide()){
- return;
- }
- module.remove.clickaway();
- }
- },
- debounce: function(method, delay) {
- clearTimeout(module.timer);
- module.timer = setTimeout(method, delay);
- },
- keyboard: function(event) {
- var
- keyCode = event.which,
- escapeKey = 27
- ;
- if(keyCode == escapeKey) {
- if(settings.closable) {
- module.debug('Escape key pressed hiding modal');
- if ( $module.hasClass(className.front) ) {
- module.hide();
- }
- }
- else {
- module.debug('Escape key pressed, but closable is set to false');
- }
- event.preventDefault();
- }
- },
- resize: function() {
- if( $dimmable.dimmer('is active') && ( module.is.animating() || module.is.active() ) ) {
- requestAnimationFrame(module.refresh);
- }
- }
- },
-
- toggle: function() {
- if( module.is.active() || module.is.animating() ) {
- module.hide();
- }
- else {
- module.show();
- }
- },
-
- show: function(callback) {
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- module.refreshModals();
- module.set.dimmerSettings();
- module.set.dimmerStyles();
-
- module.showModal(callback);
- },
-
- hide: function(callback) {
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- module.refreshModals();
- return module.hideModal(callback);
- },
-
- showModal: function(callback) {
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- if( module.is.animating() || !module.is.active() ) {
- module.showDimmer();
- module.cacheSizes();
- module.set.bodyMargin();
- if(module.can.useFlex()) {
- module.remove.legacy();
- }
- else {
- module.set.legacy();
- module.set.modalOffset();
- module.debug('Using non-flex legacy modal positioning.');
- }
- module.set.screenHeight();
- module.set.type();
- module.set.clickaway();
-
- if( !settings.allowMultiple && module.others.active() ) {
- module.hideOthers(module.showModal);
- }
- else {
- ignoreRepeatedEvents = false;
- if( settings.allowMultiple ) {
- if ( module.others.active() ) {
- $otherModals.filter('.' + className.active).find(selector.dimmer).addClass('active');
- }
-
- if ( settings.detachable ) {
- $module.detach().appendTo($dimmer);
- }
- }
- settings.onShow.call(element);
- if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
- module.debug('Showing modal with css animations');
- $module
- .transition({
- debug : settings.debug,
- animation : settings.transition + ' in',
- queue : settings.queue,
- duration : settings.duration,
- useFailSafe : true,
- onComplete : function() {
- settings.onVisible.apply(element);
- if(settings.keyboardShortcuts) {
- module.add.keyboardShortcuts();
- }
- module.save.focus();
- module.set.active();
- if(settings.autofocus) {
- module.set.autofocus();
- }
- callback();
- }
- })
- ;
- }
- else {
- module.error(error.noTransition);
- }
- }
- }
- else {
- module.debug('Modal is already visible');
- }
- },
-
- hideModal: function(callback, keepDimmed, hideOthersToo) {
- var
- $previousModal = $otherModals.filter('.' + className.active).last()
- ;
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- module.debug('Hiding modal');
- if(settings.onHide.call(element, $(this)) === false) {
- module.verbose('Hide callback returned false cancelling hide');
- ignoreRepeatedEvents = false;
- return false;
- }
-
- if( module.is.animating() || module.is.active() ) {
- if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
- module.remove.active();
- $module
- .transition({
- debug : settings.debug,
- animation : settings.transition + ' out',
- queue : settings.queue,
- duration : settings.duration,
- useFailSafe : true,
- onStart : function() {
- if(!module.others.active() && !module.others.animating() && !keepDimmed) {
- module.hideDimmer();
- }
- if( settings.keyboardShortcuts && !module.others.active() ) {
- module.remove.keyboardShortcuts();
- }
- },
- onComplete : function() {
- module.unbind.scrollLock();
- if ( settings.allowMultiple ) {
- $previousModal.addClass(className.front);
- $module.removeClass(className.front);
-
- if ( hideOthersToo ) {
- $allModals.find(selector.dimmer).removeClass('active');
- }
- else {
- $previousModal.find(selector.dimmer).removeClass('active');
- }
- }
- settings.onHidden.call(element);
- module.remove.dimmerStyles();
- module.restore.focus();
- callback();
- }
- })
- ;
- }
- else {
- module.error(error.noTransition);
- }
- }
- },
-
- showDimmer: function() {
- if($dimmable.dimmer('is animating') || !$dimmable.dimmer('is active') ) {
- module.save.bodyMargin();
- module.debug('Showing dimmer');
- $dimmable.dimmer('show');
- }
- else {
- module.debug('Dimmer already visible');
- }
- },
-
- hideDimmer: function() {
- if( $dimmable.dimmer('is animating') || ($dimmable.dimmer('is active')) ) {
- module.unbind.scrollLock();
- $dimmable.dimmer('hide', function() {
- module.restore.bodyMargin();
- module.remove.clickaway();
- module.remove.screenHeight();
- });
- }
- else {
- module.debug('Dimmer is not visible cannot hide');
- return;
- }
- },
-
- hideAll: function(callback) {
- var
- $visibleModals = $allModals.filter('.' + className.active + ', .' + className.animating)
- ;
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- if( $visibleModals.length > 0 ) {
- module.debug('Hiding all visible modals');
- var hideOk = true;
-//check in reverse order trying to hide most top displayed modal first
- $($visibleModals.get().reverse()).each(function(index,element){
- if(hideOk){
- hideOk = $(element).modal('hide modal', callback, false, true);
- }
- });
- if(hideOk) {
- module.hideDimmer();
- }
- return hideOk;
- }
- },
-
- hideOthers: function(callback) {
- var
- $visibleModals = $otherModals.filter('.' + className.active + ', .' + className.animating)
- ;
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- if( $visibleModals.length > 0 ) {
- module.debug('Hiding other modals', $otherModals);
- $visibleModals
- .modal('hide modal', callback, true)
- ;
- }
- },
-
- others: {
- active: function() {
- return ($otherModals.filter('.' + className.active).length > 0);
- },
- animating: function() {
- return ($otherModals.filter('.' + className.animating).length > 0);
- }
- },
-
-
- add: {
- keyboardShortcuts: function() {
- module.verbose('Adding keyboard shortcuts');
- $document
- .on('keyup' + eventNamespace, module.event.keyboard)
- ;
- }
- },
-
- save: {
- focus: function() {
- var
- $activeElement = $(document.activeElement),
- inCurrentModal = $activeElement.closest($module).length > 0
- ;
- if(!inCurrentModal) {
- $focusedElement = $(document.activeElement).blur();
- }
- },
- bodyMargin: function() {
- initialBodyMargin = $body.css('margin-'+(module.can.leftBodyScrollbar() ? 'left':'right'));
- var bodyMarginRightPixel = parseInt(initialBodyMargin.replace(/[^\d.]/g, '')),
- bodyScrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
- tempBodyMargin = bodyMarginRightPixel + bodyScrollbarWidth;
- }
- },
-
- restore: {
- focus: function() {
- if($focusedElement && $focusedElement.length > 0 && settings.restoreFocus) {
- $focusedElement.focus();
- }
- },
- bodyMargin: function() {
- var position = module.can.leftBodyScrollbar() ? 'left':'right';
- $body.css('margin-'+position, initialBodyMargin);
- $body.find(selector.bodyFixed.replace('right',position)).css('padding-'+position, initialBodyMargin);
- }
- },
-
- remove: {
- active: function() {
- $module.removeClass(className.active);
- },
- legacy: function() {
- $module.removeClass(className.legacy);
- },
- clickaway: function() {
- if (!settings.detachable) {
- $module
- .off('mousedown' + elementEventNamespace)
- ;
- }
- $dimmer
- .off('mousedown' + elementEventNamespace)
- ;
- $dimmer
- .off('mouseup' + elementEventNamespace)
- ;
- },
- dimmerStyles: function() {
- $dimmer.removeClass(className.inverted);
- $dimmable.removeClass(className.blurring);
- },
- bodyStyle: function() {
- if($body.attr('style') === '') {
- module.verbose('Removing style attribute');
- $body.removeAttr('style');
- }
- },
- screenHeight: function() {
- module.debug('Removing page height');
- $body
- .css('height', '')
- ;
- },
- keyboardShortcuts: function() {
- module.verbose('Removing keyboard shortcuts');
- $document
- .off('keyup' + eventNamespace)
- ;
- },
- scrolling: function() {
- $dimmable.removeClass(className.scrolling);
- $module.removeClass(className.scrolling);
- }
- },
-
- cacheSizes: function() {
- $module.addClass(className.loading);
- var
- scrollHeight = $module.prop('scrollHeight'),
- modalWidth = $module.outerWidth(),
- modalHeight = $module.outerHeight()
- ;
- if(module.cache.pageHeight === undefined || modalHeight !== 0) {
- $.extend(module.cache, {
- pageHeight : $(document).outerHeight(),
- width : modalWidth,
- height : modalHeight + settings.offset,
- scrollHeight : scrollHeight + settings.offset,
- contextHeight : (settings.context == 'body')
- ? $(window).height()
- : $dimmable.height(),
- });
- module.cache.topOffset = -(module.cache.height / 2);
- }
- $module.removeClass(className.loading);
- module.debug('Caching modal and container sizes', module.cache);
- },
-
- can: {
- leftBodyScrollbar: function(){
- if(module.cache.leftBodyScrollbar === undefined) {
- module.cache.leftBodyScrollbar = module.is.rtl() && ((module.is.iframe && !module.is.firefox()) || module.is.safari() || module.is.edge() || module.is.ie());
- }
- return module.cache.leftBodyScrollbar;
- },
- useFlex: function() {
- if (settings.useFlex === 'auto') {
- return settings.detachable && !module.is.ie();
- }
- if(settings.useFlex && module.is.ie()) {
- module.debug('useFlex true is not supported in IE');
- } else if(settings.useFlex && !settings.detachable) {
- module.debug('useFlex true in combination with detachable false is not supported');
- }
- return settings.useFlex;
- },
- fit: function() {
- var
- contextHeight = module.cache.contextHeight,
- verticalCenter = module.cache.contextHeight / 2,
- topOffset = module.cache.topOffset,
- scrollHeight = module.cache.scrollHeight,
- height = module.cache.height,
- paddingHeight = settings.padding,
- startPosition = (verticalCenter + topOffset)
- ;
- return (scrollHeight > height)
- ? (startPosition + scrollHeight + paddingHeight < contextHeight)
- : (height + (paddingHeight * 2) < contextHeight)
- ;
- }
- },
-
- is: {
- active: function() {
- return $module.hasClass(className.active);
- },
- ie: function() {
- if(module.cache.isIE === undefined) {
- var
- isIE11 = (!(window.ActiveXObject) && 'ActiveXObject' in window),
- isIE = ('ActiveXObject' in window)
- ;
- module.cache.isIE = (isIE11 || isIE);
- }
- return module.cache.isIE;
- },
- animating: function() {
- return $module.transition('is supported')
- ? $module.transition('is animating')
- : $module.is(':visible')
- ;
- },
- scrolling: function() {
- return $dimmable.hasClass(className.scrolling);
- },
- modernBrowser: function() {
- // appName for IE11 reports 'Netscape' can no longer use
- return !(window.ActiveXObject || 'ActiveXObject' in window);
- },
- rtl: function() {
- if(module.cache.isRTL === undefined) {
- module.cache.isRTL = $body.attr('dir') === 'rtl' || $body.css('direction') === 'rtl';
- }
- return module.cache.isRTL;
- },
- safari: function() {
- if(module.cache.isSafari === undefined) {
- module.cache.isSafari = /constructor/i.test(window.HTMLElement) || !!window.ApplePaySession;
- }
- return module.cache.isSafari;
- },
- edge: function(){
- if(module.cache.isEdge === undefined) {
- module.cache.isEdge = !!window.setImmediate && !module.is.ie();
- }
- return module.cache.isEdge;
- },
- firefox: function(){
- if(module.cache.isFirefox === undefined) {
- module.cache.isFirefox = !!window.InstallTrigger;
- }
- return module.cache.isFirefox;
- },
- iframe: function() {
- return !(self === top);
- }
- },
-
- set: {
- autofocus: function() {
- var
- $inputs = $module.find('[tabindex], :input').filter(':visible').filter(function() {
- return $(this).closest('.disabled').length === 0;
- }),
- $autofocus = $inputs.filter('[autofocus]'),
- $input = ($autofocus.length > 0)
- ? $autofocus.first()
- : $inputs.first()
- ;
- if($input.length > 0) {
- $input.focus();
- }
- },
- bodyMargin: function() {
- var position = module.can.leftBodyScrollbar() ? 'left':'right';
- if(settings.detachable || module.can.fit()) {
- $body.css('margin-'+position, tempBodyMargin + 'px');
- }
- $body.find(selector.bodyFixed.replace('right',position)).css('padding-'+position, tempBodyMargin + 'px');
- },
- clickaway: function() {
- if (!settings.detachable) {
- $module
- .on('mousedown' + elementEventNamespace, module.event.mousedown)
- ;
- }
- $dimmer
- .on('mousedown' + elementEventNamespace, module.event.mousedown)
- ;
- $dimmer
- .on('mouseup' + elementEventNamespace, module.event.mouseup)
- ;
- },
- dimmerSettings: function() {
- if($.fn.dimmer === undefined) {
- module.error(error.dimmer);
- return;
- }
- var
- defaultSettings = {
- debug : settings.debug,
- dimmerName : 'modals',
- closable : 'auto',
- useFlex : module.can.useFlex(),
- duration : {
- show : settings.duration,
- hide : settings.duration
- }
- },
- dimmerSettings = $.extend(true, defaultSettings, settings.dimmerSettings)
- ;
- if(settings.inverted) {
- dimmerSettings.variation = (dimmerSettings.variation !== undefined)
- ? dimmerSettings.variation + ' inverted'
- : 'inverted'
- ;
- }
- $context.dimmer('setting', dimmerSettings);
- },
- dimmerStyles: function() {
- if(settings.inverted) {
- $dimmer.addClass(className.inverted);
- }
- else {
- $dimmer.removeClass(className.inverted);
- }
- if(settings.blurring) {
- $dimmable.addClass(className.blurring);
- }
- else {
- $dimmable.removeClass(className.blurring);
- }
- },
- modalOffset: function() {
- if (!settings.detachable) {
- var canFit = module.can.fit();
- $module
- .css({
- top: (!$module.hasClass('aligned') && canFit)
- ? $(document).scrollTop() + (module.cache.contextHeight - module.cache.height) / 2
- : !canFit || $module.hasClass('top')
- ? $(document).scrollTop() + settings.padding
- : $(document).scrollTop() + (module.cache.contextHeight - module.cache.height - settings.padding),
- marginLeft: -(module.cache.width / 2)
- })
- ;
- } else {
- $module
- .css({
- marginTop: (!$module.hasClass('aligned') && module.can.fit())
- ? -(module.cache.height / 2)
- : settings.padding / 2,
- marginLeft: -(module.cache.width / 2)
- })
- ;
- }
- module.verbose('Setting modal offset for legacy mode');
- },
- screenHeight: function() {
- if( module.can.fit() ) {
- $body.css('height', '');
- }
- else if(!$module.hasClass('bottom')) {
- module.debug('Modal is taller than page content, resizing page height');
- $body
- .css('height', module.cache.height + (settings.padding * 2) )
- ;
- }
- },
- active: function() {
- $module.addClass(className.active + ' ' + className.front);
- $otherModals.filter('.' + className.active).removeClass(className.front);
- },
- scrolling: function() {
- $dimmable.addClass(className.scrolling);
- $module.addClass(className.scrolling);
- module.unbind.scrollLock();
- },
- legacy: function() {
- $module.addClass(className.legacy);
- },
- type: function() {
- if(module.can.fit()) {
- module.verbose('Modal fits on screen');
- if(!module.others.active() && !module.others.animating()) {
- module.remove.scrolling();
- module.bind.scrollLock();
- }
- }
- else if (!$module.hasClass('bottom')){
- module.verbose('Modal cannot fit on screen setting to scrolling');
- module.set.scrolling();
- } else {
- module.verbose('Bottom aligned modal not fitting on screen is unsupported for scrolling');
- }
- },
- undetached: function() {
- $dimmable.addClass(className.undetached);
- }
- },
-
- setting: function(name, value) {
- module.debug('Changing setting', name, value);
- if( $.isPlainObject(name) ) {
- $.extend(true, settings, name);
- }
- else if(value !== undefined) {
- if($.isPlainObject(settings[name])) {
- $.extend(true, settings[name], value);
- }
- else {
- settings[name] = value;
- }
- }
- else {
- return settings[name];
- }
- },
- internal: function(name, value) {
- if( $.isPlainObject(name) ) {
- $.extend(true, module, name);
- }
- else if(value !== undefined) {
- module[name] = value;
- }
- else {
- return module[name];
- }
- },
- debug: function() {
- if(!settings.silent && settings.debug) {
- if(settings.performance) {
- module.performance.log(arguments);
- }
- else {
- module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
- module.debug.apply(console, arguments);
- }
- }
- },
- verbose: function() {
- if(!settings.silent && settings.verbose && settings.debug) {
- if(settings.performance) {
- module.performance.log(arguments);
- }
- else {
- module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
- module.verbose.apply(console, arguments);
- }
- }
- },
- error: function() {
- if(!settings.silent) {
- module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
- module.error.apply(console, arguments);
- }
- },
- performance: {
- log: function(message) {
- var
- currentTime,
- executionTime,
- previousTime
- ;
- if(settings.performance) {
- currentTime = new Date().getTime();
- previousTime = time || currentTime;
- executionTime = currentTime - previousTime;
- time = currentTime;
- performance.push({
- 'Name' : message[0],
- 'Arguments' : [].slice.call(message, 1) || '',
- 'Element' : element,
- 'Execution Time' : executionTime
- });
- }
- clearTimeout(module.performance.timer);
- module.performance.timer = setTimeout(module.performance.display, 500);
- },
- display: function() {
- var
- title = settings.name + ':',
- totalTime = 0
- ;
- time = false;
- clearTimeout(module.performance.timer);
- $.each(performance, function(index, data) {
- totalTime += data['Execution Time'];
- });
- title += ' ' + totalTime + 'ms';
- if(moduleSelector) {
- title += ' \'' + moduleSelector + '\'';
- }
- if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
- console.groupCollapsed(title);
- if(console.table) {
- console.table(performance);
- }
- else {
- $.each(performance, function(index, data) {
- console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
- });
- }
- console.groupEnd();
- }
- performance = [];
- }
- },
- invoke: function(query, passedArguments, context) {
- var
- object = instance,
- maxDepth,
- found,
- response
- ;
- passedArguments = passedArguments || queryArguments;
- context = element || context;
- if(typeof query == 'string' && object !== undefined) {
- query = query.split(/[\. ]/);
- maxDepth = query.length - 1;
- $.each(query, function(depth, value) {
- var camelCaseValue = (depth != maxDepth)
- ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
- : query
- ;
- if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
- object = object[camelCaseValue];
- }
- else if( object[camelCaseValue] !== undefined ) {
- found = object[camelCaseValue];
- return false;
- }
- else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
- object = object[value];
- }
- else if( object[value] !== undefined ) {
- found = object[value];
- return false;
- }
- else {
- return false;
- }
- });
- }
- if ( $.isFunction( found ) ) {
- response = found.apply(context, passedArguments);
- }
- else if(found !== undefined) {
- response = found;
- }
- if(Array.isArray(returnedValue)) {
- returnedValue.push(response);
- }
- else if(returnedValue !== undefined) {
- returnedValue = [returnedValue, response];
- }
- else if(response !== undefined) {
- returnedValue = response;
- }
- return found;
- }
- };
-
- if(methodInvoked) {
- if(instance === undefined) {
- module.initialize();
- }
- module.invoke(query);
- }
- else {
- if(instance !== undefined) {
- instance.invoke('destroy');
- }
- module.initialize();
- }
- })
- ;
-
- return (returnedValue !== undefined)
- ? returnedValue
- : this
- ;
-};
-
-$.fn.modal.settings = {
-
- name : 'Modal',
- namespace : 'modal',
-
- useFlex : 'auto',
- offset : 0,
-
- silent : false,
- debug : false,
- verbose : false,
- performance : true,
-
- observeChanges : false,
-
- allowMultiple : false,
- detachable : true,
- closable : true,
- autofocus : true,
- restoreFocus : true,
-
- inverted : false,
- blurring : false,
-
- centered : true,
-
- dimmerSettings : {
- closable : false,
- useCSS : true
- },
-
- // whether to use keyboard shortcuts
- keyboardShortcuts: true,
-
- context : 'body',
-
- queue : false,
- duration : 500,
- transition : 'scale',
-
- // padding with edge of page
- padding : 50,
- scrollbarWidth: 10,
-
- // called before show animation
- onShow : function(){},
-
- // called after show animation
- onVisible : function(){},
-
- // called before hide animation
- onHide : function(){ return true; },
-
- // called after hide animation
- onHidden : function(){},
-
- // called after approve selector match
- onApprove : function(){ return true; },
-
- // called after deny selector match
- onDeny : function(){ return true; },
-
- selector : {
- close : '> .close',
- approve : '.actions .positive, .actions .approve, .actions .ok',
- deny : '.actions .negative, .actions .deny, .actions .cancel',
- modal : '.ui.modal',
- dimmer : '> .ui.dimmer',
- bodyFixed: '> .ui.fixed.menu, > .ui.right.toast-container, > .ui.right.sidebar'
- },
- error : {
- dimmer : 'UI Dimmer, a required component is not included in this page',
- method : 'The method you called is not defined.',
- notFound : 'The element you specified could not be found'
- },
- className : {
- active : 'active',
- animating : 'animating',
- blurring : 'blurring',
- inverted : 'inverted',
- legacy : 'legacy',
- loading : 'loading',
- scrolling : 'scrolling',
- undetached : 'undetached',
- front : 'front'
- }
-};
-
-
-})( jQuery, window, document );
-
-/*!
- * # Fomantic-UI - Search
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-;(function ($, window, document, undefined) {
-
-'use strict';
-
-$.isFunction = $.isFunction || function(obj) {
- return typeof obj === "function" && typeof obj.nodeType !== "number";
-};
-
-window = (typeof window != 'undefined' && window.Math == Math)
- ? window
- : (typeof self != 'undefined' && self.Math == Math)
- ? self
- : Function('return this')()
-;
-
-$.fn.search = function(parameters) {
- var
- $allModules = $(this),
- moduleSelector = $allModules.selector || '',
-
- time = new Date().getTime(),
- performance = [],
-
- query = arguments[0],
- methodInvoked = (typeof query == 'string'),
- queryArguments = [].slice.call(arguments, 1),
- returnedValue
- ;
- $(this)
- .each(function() {
- var
- settings = ( $.isPlainObject(parameters) )
- ? $.extend(true, {}, $.fn.search.settings, parameters)
- : $.extend({}, $.fn.search.settings),
-
- className = settings.className,
- metadata = settings.metadata,
- regExp = settings.regExp,
- fields = settings.fields,
- selector = settings.selector,
- error = settings.error,
- namespace = settings.namespace,
-
- eventNamespace = '.' + namespace,
- moduleNamespace = namespace + '-module',
-
- $module = $(this),
- $prompt = $module.find(selector.prompt),
- $searchButton = $module.find(selector.searchButton),
- $results = $module.find(selector.results),
- $result = $module.find(selector.result),
- $category = $module.find(selector.category),
-
- element = this,
- instance = $module.data(moduleNamespace),
-
- disabledBubbled = false,
- resultsDismissed = false,
-
- module
- ;
-
- module = {
-
- initialize: function() {
- module.verbose('Initializing module');
- module.get.settings();
- module.determine.searchFields();
- module.bind.events();
- module.set.type();
- module.create.results();
- module.instantiate();
- },
- instantiate: function() {
- module.verbose('Storing instance of module', module);
- instance = module;
- $module
- .data(moduleNamespace, module)
- ;
- },
- destroy: function() {
- module.verbose('Destroying instance');
- $module
- .off(eventNamespace)
- .removeData(moduleNamespace)
- ;
- },
-
- refresh: function() {
- module.debug('Refreshing selector cache');
- $prompt = $module.find(selector.prompt);
- $searchButton = $module.find(selector.searchButton);
- $category = $module.find(selector.category);
- $results = $module.find(selector.results);
- $result = $module.find(selector.result);
- },
-
- refreshResults: function() {
- $results = $module.find(selector.results);
- $result = $module.find(selector.result);
- },
-
- bind: {
- events: function() {
- module.verbose('Binding events to search');
- if(settings.automatic) {
- $module
- .on(module.get.inputEvent() + eventNamespace, selector.prompt, module.event.input)
- ;
- $prompt
- .attr('autocomplete', 'off')
- ;
- }
- $module
- // prompt
- .on('focus' + eventNamespace, selector.prompt, module.event.focus)
- .on('blur' + eventNamespace, selector.prompt, module.event.blur)
- .on('keydown' + eventNamespace, selector.prompt, module.handleKeyboard)
- // search button
- .on('click' + eventNamespace, selector.searchButton, module.query)
- // results
- .on('mousedown' + eventNamespace, selector.results, module.event.result.mousedown)
- .on('mouseup' + eventNamespace, selector.results, module.event.result.mouseup)
- .on('click' + eventNamespace, selector.result, module.event.result.click)
- ;
- }
- },
-
- determine: {
- searchFields: function() {
- // this makes sure $.extend does not add specified search fields to default fields
- // this is the only setting which should not extend defaults
- if(parameters && parameters.searchFields !== undefined) {
- settings.searchFields = parameters.searchFields;
- }
- }
- },
-
- event: {
- input: function() {
- if(settings.searchDelay) {
- clearTimeout(module.timer);
- module.timer = setTimeout(function() {
- if(module.is.focused()) {
- module.query();
- }
- }, settings.searchDelay);
- }
- else {
- module.query();
- }
- },
- focus: function() {
- module.set.focus();
- if(settings.searchOnFocus && module.has.minimumCharacters() ) {
- module.query(function() {
- if(module.can.show() ) {
- module.showResults();
- }
- });
- }
- },
- blur: function(event) {
- var
- pageLostFocus = (document.activeElement === this),
- callback = function() {
- module.cancel.query();
- module.remove.focus();
- module.timer = setTimeout(module.hideResults, settings.hideDelay);
- }
- ;
- if(pageLostFocus) {
- return;
- }
- resultsDismissed = false;
- if(module.resultsClicked) {
- module.debug('Determining if user action caused search to close');
- $module
- .one('click.close' + eventNamespace, selector.results, function(event) {
- if(module.is.inMessage(event) || disabledBubbled) {
- $prompt.focus();
- return;
- }
- disabledBubbled = false;
- if( !module.is.animating() && !module.is.hidden()) {
- callback();
- }
- })
- ;
- }
- else {
- module.debug('Input blurred without user action, closing results');
- callback();
- }
- },
- result: {
- mousedown: function() {
- module.resultsClicked = true;
- },
- mouseup: function() {
- module.resultsClicked = false;
- },
- click: function(event) {
- module.debug('Search result selected');
- var
- $result = $(this),
- $title = $result.find(selector.title).eq(0),
- $link = $result.is('a[href]')
- ? $result
- : $result.find('a[href]').eq(0),
- href = $link.attr('href') || false,
- target = $link.attr('target') || false,
- // title is used for result lookup
- value = ($title.length > 0)
- ? $title.text()
- : false,
- results = module.get.results(),
- result = $result.data(metadata.result) || module.get.result(value, results)
- ;
- if(value) {
- module.set.value(value);
- }
- if( $.isFunction(settings.onSelect) ) {
- if(settings.onSelect.call(element, result, results) === false) {
- module.debug('Custom onSelect callback cancelled default select action');
- disabledBubbled = true;
- return;
- }
- }
- module.hideResults();
- if(href) {
- event.preventDefault();
- module.verbose('Opening search link found in result', $link);
- if(target == '_blank' || event.ctrlKey) {
- window.open(href);
- }
- else {
- window.location.href = (href);
- }
- }
- }
- }
- },
- ensureVisible: function ensureVisible($el) {
- var elTop, elBottom, resultsScrollTop, resultsHeight;
-
- elTop = $el.position().top;
- elBottom = elTop + $el.outerHeight(true);
-
- resultsScrollTop = $results.scrollTop();
- resultsHeight = $results.height()
- parseInt($results.css('paddingTop'), 0) +
- parseInt($results.css('paddingBottom'), 0);
-
- if (elTop < 0) {
- $results.scrollTop(resultsScrollTop + elTop);
- }
-
- else if (resultsHeight < elBottom) {
- $results.scrollTop(resultsScrollTop + (elBottom - resultsHeight));
- }
- },
- handleKeyboard: function(event) {
- var
- // force selector refresh
- $result = $module.find(selector.result),
- $category = $module.find(selector.category),
- $activeResult = $result.filter('.' + className.active),
- currentIndex = $result.index( $activeResult ),
- resultSize = $result.length,
- hasActiveResult = $activeResult.length > 0,
-
- keyCode = event.which,
- keys = {
- backspace : 8,
- enter : 13,
- escape : 27,
- upArrow : 38,
- downArrow : 40
- },
- newIndex
- ;
- // search shortcuts
- if(keyCode == keys.escape) {
- module.verbose('Escape key pressed, blurring search field');
- module.hideResults();
- resultsDismissed = true;
- }
- if( module.is.visible() ) {
- if(keyCode == keys.enter) {
- module.verbose('Enter key pressed, selecting active result');
- if( $result.filter('.' + className.active).length > 0 ) {
- module.event.result.click.call($result.filter('.' + className.active), event);
- event.preventDefault();
- return false;
- }
- }
- else if(keyCode == keys.upArrow && hasActiveResult) {
- module.verbose('Up key pressed, changing active result');
- newIndex = (currentIndex - 1 < 0)
- ? currentIndex
- : currentIndex - 1
- ;
- $category
- .removeClass(className.active)
- ;
- $result
- .removeClass(className.active)
- .eq(newIndex)
- .addClass(className.active)
- .closest($category)
- .addClass(className.active)
- ;
- module.ensureVisible($result.eq(newIndex));
- event.preventDefault();
- }
- else if(keyCode == keys.downArrow) {
- module.verbose('Down key pressed, changing active result');
- newIndex = (currentIndex + 1 >= resultSize)
- ? currentIndex
- : currentIndex + 1
- ;
- $category
- .removeClass(className.active)
- ;
- $result
- .removeClass(className.active)
- .eq(newIndex)
- .addClass(className.active)
- .closest($category)
- .addClass(className.active)
- ;
- module.ensureVisible($result.eq(newIndex));
- event.preventDefault();
- }
- }
- else {
- // query shortcuts
- if(keyCode == keys.enter) {
- module.verbose('Enter key pressed, executing query');
- module.query();
- module.set.buttonPressed();
- $prompt.one('keyup', module.remove.buttonFocus);
- }
- }
- },
-
- setup: {
- api: function(searchTerm, callback) {
- var
- apiSettings = {
- debug : settings.debug,
- on : false,
- cache : settings.cache,
- action : 'search',
- urlData : {
- query : searchTerm
- },
- onSuccess : function(response) {
- module.parse.response.call(element, response, searchTerm);
- callback();
- },
- onFailure : function() {
- module.displayMessage(error.serverError);
- callback();
- },
- onAbort : function(response) {
- },
- onError : module.error
- }
- ;
- $.extend(true, apiSettings, settings.apiSettings);
- module.verbose('Setting up API request', apiSettings);
- $module.api(apiSettings);
- }
- },
-
- can: {
- useAPI: function() {
- return $.fn.api !== undefined;
- },
- show: function() {
- return module.is.focused() && !module.is.visible() && !module.is.empty();
- },
- transition: function() {
- return settings.transition && $.fn.transition !== undefined && $module.transition('is supported');
- }
- },
-
- is: {
- animating: function() {
- return $results.hasClass(className.animating);
- },
- hidden: function() {
- return $results.hasClass(className.hidden);
- },
- inMessage: function(event) {
- if(!event.target) {
- return;
- }
- var
- $target = $(event.target),
- isInDOM = $.contains(document.documentElement, event.target)
- ;
- return (isInDOM && $target.closest(selector.message).length > 0);
- },
- empty: function() {
- return ($results.html() === '');
- },
- visible: function() {
- return ($results.filter(':visible').length > 0);
- },
- focused: function() {
- return ($prompt.filter(':focus').length > 0);
- }
- },
-
- get: {
- settings: function() {
- if($.isPlainObject(parameters) && parameters.searchFullText) {
- settings.fullTextSearch = parameters.searchFullText;
- module.error(settings.error.oldSearchSyntax, element);
- }
- if (settings.ignoreDiacritics && !String.prototype.normalize) {
- settings.ignoreDiacritics = false;
- module.error(error.noNormalize, element);
- }
- },
- inputEvent: function() {
- var
- prompt = $prompt[0],
- inputEvent = (prompt !== undefined && prompt.oninput !== undefined)
- ? 'input'
- : (prompt !== undefined && prompt.onpropertychange !== undefined)
- ? 'propertychange'
- : 'keyup'
- ;
- return inputEvent;
- },
- value: function() {
- return $prompt.val();
- },
- results: function() {
- var
- results = $module.data(metadata.results)
- ;
- return results;
- },
- result: function(value, results) {
- var
- result = false
- ;
- value = (value !== undefined)
- ? value
- : module.get.value()
- ;
- results = (results !== undefined)
- ? results
- : module.get.results()
- ;
- if(settings.type === 'category') {
- module.debug('Finding result that matches', value);
- $.each(results, function(index, category) {
- if(Array.isArray(category.results)) {
- result = module.search.object(value, category.results)[0];
- // don't continue searching if a result is found
- if(result) {
- return false;
- }
- }
- });
- }
- else {
- module.debug('Finding result in results object', value);
- result = module.search.object(value, results)[0];
- }
- return result || false;
- },
- },
-
- select: {
- firstResult: function() {
- module.verbose('Selecting first result');
- $result.first().addClass(className.active);
- }
- },
-
- set: {
- focus: function() {
- $module.addClass(className.focus);
- },
- loading: function() {
- $module.addClass(className.loading);
- },
- value: function(value) {
- module.verbose('Setting search input value', value);
- $prompt
- .val(value)
- ;
- },
- type: function(type) {
- type = type || settings.type;
- if(settings.type == 'category') {
- $module.addClass(settings.type);
- }
- },
- buttonPressed: function() {
- $searchButton.addClass(className.pressed);
- }
- },
-
- remove: {
- loading: function() {
- $module.removeClass(className.loading);
- },
- focus: function() {
- $module.removeClass(className.focus);
- },
- buttonPressed: function() {
- $searchButton.removeClass(className.pressed);
- },
- diacritics: function(text) {
- return settings.ignoreDiacritics ? text.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : text;
- }
- },
-
- query: function(callback) {
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- var
- searchTerm = module.get.value(),
- cache = module.read.cache(searchTerm)
- ;
- callback = callback || function() {};
- if( module.has.minimumCharacters() ) {
- if(cache) {
- module.debug('Reading result from cache', searchTerm);
- module.save.results(cache.results);
- module.addResults(cache.html);
- module.inject.id(cache.results);
- callback();
- }
- else {
- module.debug('Querying for', searchTerm);
- if($.isPlainObject(settings.source) || Array.isArray(settings.source)) {
- module.search.local(searchTerm);
- callback();
- }
- else if( module.can.useAPI() ) {
- module.search.remote(searchTerm, callback);
- }
- else {
- module.error(error.source);
- callback();
- }
- }
- settings.onSearchQuery.call(element, searchTerm);
- }
- else {
- module.hideResults();
- }
- },
-
- search: {
- local: function(searchTerm) {
- var
- results = module.search.object(searchTerm, settings.source),
- searchHTML
- ;
- module.set.loading();
- module.save.results(results);
- module.debug('Returned full local search results', results);
- if(settings.maxResults > 0) {
- module.debug('Using specified max results', results);
- results = results.slice(0, settings.maxResults);
- }
- if(settings.type == 'category') {
- results = module.create.categoryResults(results);
- }
- searchHTML = module.generateResults({
- results: results
- });
- module.remove.loading();
- module.addResults(searchHTML);
- module.inject.id(results);
- module.write.cache(searchTerm, {
- html : searchHTML,
- results : results
- });
- },
- remote: function(searchTerm, callback) {
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- if($module.api('is loading')) {
- $module.api('abort');
- }
- module.setup.api(searchTerm, callback);
- $module
- .api('query')
- ;
- },
- object: function(searchTerm, source, searchFields) {
- searchTerm = module.remove.diacritics(String(searchTerm));
- var
- results = [],
- exactResults = [],
- fuzzyResults = [],
- searchExp = searchTerm.replace(regExp.escape, '\\$&'),
- matchRegExp = new RegExp(regExp.beginsWith + searchExp, 'i'),
-
- // avoid duplicates when pushing results
- addResult = function(array, result) {
- var
- notResult = ($.inArray(result, results) == -1),
- notFuzzyResult = ($.inArray(result, fuzzyResults) == -1),
- notExactResults = ($.inArray(result, exactResults) == -1)
- ;
- if(notResult && notFuzzyResult && notExactResults) {
- array.push(result);
- }
- }
- ;
- source = source || settings.source;
- searchFields = (searchFields !== undefined)
- ? searchFields
- : settings.searchFields
- ;
-
- // search fields should be array to loop correctly
- if(!Array.isArray(searchFields)) {
- searchFields = [searchFields];
- }
-
- // exit conditions if no source
- if(source === undefined || source === false) {
- module.error(error.source);
- return [];
- }
- // iterate through search fields looking for matches
- $.each(searchFields, function(index, field) {
- $.each(source, function(label, content) {
- var
- fieldExists = (typeof content[field] == 'string') || (typeof content[field] == 'number')
- ;
- if(fieldExists) {
- var text;
- if (typeof content[field] === 'string'){
- text = module.remove.diacritics(content[field]);
- } else {
- text = content[field].toString();
- }
- if( text.search(matchRegExp) !== -1) {
- // content starts with value (first in results)
- addResult(results, content);
- }
- else if(settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text) ) {
- // content fuzzy matches (last in results)
- addResult(exactResults, content);
- }
- else if(settings.fullTextSearch == true && module.fuzzySearch(searchTerm, text) ) {
- // content fuzzy matches (last in results)
- addResult(fuzzyResults, content);
- }
- }
- });
- });
- $.merge(exactResults, fuzzyResults);
- $.merge(results, exactResults);
- return results;
- }
- },
- exactSearch: function (query, term) {
- query = query.toLowerCase();
- term = term.toLowerCase();
- return term.indexOf(query) > -1;
- },
- fuzzySearch: function(query, term) {
- var
- termLength = term.length,
- queryLength = query.length
- ;
- if(typeof query !== 'string') {
- return false;
- }
- query = query.toLowerCase();
- term = term.toLowerCase();
- if(queryLength > termLength) {
- return false;
- }
- if(queryLength === termLength) {
- return (query === term);
- }
- search: for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
- var
- queryCharacter = query.charCodeAt(characterIndex)
- ;
- while(nextCharacterIndex < termLength) {
- if(term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
- continue search;
- }
- }
- return false;
- }
- return true;
- },
-
- parse: {
- response: function(response, searchTerm) {
- if(Array.isArray(response)){
- var o={};
- o[fields.results]=response;
- response = o;
- }
- var
- searchHTML = module.generateResults(response)
- ;
- module.verbose('Parsing server response', response);
- if(response !== undefined) {
- if(searchTerm !== undefined && response[fields.results] !== undefined) {
- module.addResults(searchHTML);
- module.inject.id(response[fields.results]);
- module.write.cache(searchTerm, {
- html : searchHTML,
- results : response[fields.results]
- });
- module.save.results(response[fields.results]);
- }
- }
- }
- },
-
- cancel: {
- query: function() {
- if( module.can.useAPI() ) {
- $module.api('abort');
- }
- }
- },
-
- has: {
- minimumCharacters: function() {
- var
- searchTerm = module.get.value(),
- numCharacters = searchTerm.length
- ;
- return (numCharacters >= settings.minCharacters);
- },
- results: function() {
- if($results.length === 0) {
- return false;
- }
- var
- html = $results.html()
- ;
- return html != '';
- }
- },
-
- clear: {
- cache: function(value) {
- var
- cache = $module.data(metadata.cache)
- ;
- if(!value) {
- module.debug('Clearing cache', value);
- $module.removeData(metadata.cache);
- }
- else if(value && cache && cache[value]) {
- module.debug('Removing value from cache', value);
- delete cache[value];
- $module.data(metadata.cache, cache);
- }
- }
- },
-
- read: {
- cache: function(name) {
- var
- cache = $module.data(metadata.cache)
- ;
- if(settings.cache) {
- module.verbose('Checking cache for generated html for query', name);
- return (typeof cache == 'object') && (cache[name] !== undefined)
- ? cache[name]
- : false
- ;
- }
- return false;
- }
- },
-
- create: {
- categoryResults: function(results) {
- var
- categoryResults = {}
- ;
- $.each(results, function(index, result) {
- if(!result.category) {
- return;
- }
- if(categoryResults[result.category] === undefined) {
- module.verbose('Creating new category of results', result.category);
- categoryResults[result.category] = {
- name : result.category,
- results : [result]
- };
- }
- else {
- categoryResults[result.category].results.push(result);
- }
- });
- return categoryResults;
- },
- id: function(resultIndex, categoryIndex) {
- var
- resultID = (resultIndex + 1), // not zero indexed
- letterID,
- id
- ;
- if(categoryIndex !== undefined) {
- // start char code for "A"
- letterID = String.fromCharCode(97 + categoryIndex);
- id = letterID + resultID;
- module.verbose('Creating category result id', id);
- }
- else {
- id = resultID;
- module.verbose('Creating result id', id);
- }
- return id;
- },
- results: function() {
- if($results.length === 0) {
- $results = $('<div />')
- .addClass(className.results)
- .appendTo($module)
- ;
- }
- }
- },
-
- inject: {
- result: function(result, resultIndex, categoryIndex) {
- module.verbose('Injecting result into results');
- var
- $selectedResult = (categoryIndex !== undefined)
- ? $results
- .children().eq(categoryIndex)
- .children(selector.results)
- .first()
- .children(selector.result)
- .eq(resultIndex)
- : $results
- .children(selector.result).eq(resultIndex)
- ;
- module.verbose('Injecting results metadata', $selectedResult);
- $selectedResult
- .data(metadata.result, result)
- ;
- },
- id: function(results) {
- module.debug('Injecting unique ids into results');
- var
- // since results may be object, we must use counters
- categoryIndex = 0,
- resultIndex = 0
- ;
- if(settings.type === 'category') {
- // iterate through each category result
- $.each(results, function(index, category) {
- if(category.results.length > 0){
- resultIndex = 0;
- $.each(category.results, function(index, result) {
- if(result.id === undefined) {
- result.id = module.create.id(resultIndex, categoryIndex);
- }
- module.inject.result(result, resultIndex, categoryIndex);
- resultIndex++;
- });
- categoryIndex++;
- }
- });
- }
- else {
- // top level
- $.each(results, function(index, result) {
- if(result.id === undefined) {
- result.id = module.create.id(resultIndex);
- }
- module.inject.result(result, resultIndex);
- resultIndex++;
- });
- }
- return results;
- }
- },
-
- save: {
- results: function(results) {
- module.verbose('Saving current search results to metadata', results);
- $module.data(metadata.results, results);
- }
- },
-
- write: {
- cache: function(name, value) {
- var
- cache = ($module.data(metadata.cache) !== undefined)
- ? $module.data(metadata.cache)
- : {}
- ;
- if(settings.cache) {
- module.verbose('Writing generated html to cache', name, value);
- cache[name] = value;
- $module
- .data(metadata.cache, cache)
- ;
- }
- }
- },
-
- addResults: function(html) {
- if( $.isFunction(settings.onResultsAdd) ) {
- if( settings.onResultsAdd.call($results, html) === false ) {
- module.debug('onResultsAdd callback cancelled default action');
- return false;
- }
- }
- if(html) {
- $results
- .html(html)
- ;
- module.refreshResults();
- if(settings.selectFirstResult) {
- module.select.firstResult();
- }
- module.showResults();
- }
- else {
- module.hideResults(function() {
- $results.empty();
- });
- }
- },
-
- showResults: function(callback) {
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- if(resultsDismissed) {
- return;
- }
- if(!module.is.visible() && module.has.results()) {
- if( module.can.transition() ) {
- module.debug('Showing results with css animations');
- $results
- .transition({
- animation : settings.transition + ' in',
- debug : settings.debug,
- verbose : settings.verbose,
- duration : settings.duration,
- onShow : function() {
- var $firstResult = $module.find(selector.result).eq(0);
- if($firstResult.length > 0) {
- module.ensureVisible($firstResult);
- }
- },
- onComplete : function() {
- callback();
- },
- queue : true
- })
- ;
- }
- else {
- module.debug('Showing results with javascript');
- $results
- .stop()
- .fadeIn(settings.duration, settings.easing)
- ;
- }
- settings.onResultsOpen.call($results);
- }
- },
- hideResults: function(callback) {
- callback = $.isFunction(callback)
- ? callback
- : function(){}
- ;
- if( module.is.visible() ) {
- if( module.can.transition() ) {
- module.debug('Hiding results with css animations');
- $results
- .transition({
- animation : settings.transition + ' out',
- debug : settings.debug,
- verbose : settings.verbose,
- duration : settings.duration,
- onComplete : function() {
- callback();
- },
- queue : true
- })
- ;
- }
- else {
- module.debug('Hiding results with javascript');
- $results
- .stop()
- .fadeOut(settings.duration, settings.easing)
- ;
- }
- settings.onResultsClose.call($results);
- }
- },
-
- generateResults: function(response) {
- module.debug('Generating html from response', response);
- var
- template = settings.templates[settings.type],
- isProperObject = ($.isPlainObject(response[fields.results]) && !$.isEmptyObject(response[fields.results])),
- isProperArray = (Array.isArray(response[fields.results]) && response[fields.results].length > 0),
- html = ''
- ;
- if(isProperObject || isProperArray ) {
- if(settings.maxResults > 0) {
- if(isProperObject) {
- if(settings.type == 'standard') {
- module.error(error.maxResults);
- }
- }
- else {
- response[fields.results] = response[fields.results].slice(0, settings.maxResults);
- }
- }
- if($.isFunction(template)) {
- html = template(response, fields, settings.preserveHTML);
- }
- else {
- module.error(error.noTemplate, false);
- }
- }
- else if(settings.showNoResults) {
- html = module.displayMessage(error.noResults, 'empty', error.noResultsHeader);
- }
- settings.onResults.call(element, response);
- return html;
- },
-
- displayMessage: function(text, type, header) {
- type = type || 'standard';
- module.debug('Displaying message', text, type, header);
- module.addResults( settings.templates.message(text, type, header) );
- return settings.templates.message(text, type, header);
- },
-
- setting: function(name, value) {
- if( $.isPlainObject(name) ) {
- $.extend(true, settings, name);
- }
- else if(value !== undefined) {
- settings[name] = value;
- }
- else {
- return settings[name];
- }
- },
- internal: function(name, value) {
- if( $.isPlainObject(name) ) {
- $.extend(true, module, name);
- }
- else if(value !== undefined) {
- module[name] = value;
- }
- else {
- return module[name];
- }
- },
- debug: function() {
- if(!settings.silent && settings.debug) {
- if(settings.performance) {
- module.performance.log(arguments);
- }
- else {
- module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
- module.debug.apply(console, arguments);
- }
- }
- },
- verbose: function() {
- if(!settings.silent && settings.verbose && settings.debug) {
- if(settings.performance) {
- module.performance.log(arguments);
- }
- else {
- module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
- module.verbose.apply(console, arguments);
- }
- }
- },
- error: function() {
- if(!settings.silent) {
- module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
- module.error.apply(console, arguments);
- }
- },
- performance: {
- log: function(message) {
- var
- currentTime,
- executionTime,
- previousTime
- ;
- if(settings.performance) {
- currentTime = new Date().getTime();
- previousTime = time || currentTime;
- executionTime = currentTime - previousTime;
- time = currentTime;
- performance.push({
- 'Name' : message[0],
- 'Arguments' : [].slice.call(message, 1) || '',
- 'Element' : element,
- 'Execution Time' : executionTime
- });
- }
- clearTimeout(module.performance.timer);
- module.performance.timer = setTimeout(module.performance.display, 500);
- },
- display: function() {
- var
- title = settings.name + ':',
- totalTime = 0
- ;
- time = false;
- clearTimeout(module.performance.timer);
- $.each(performance, function(index, data) {
- totalTime += data['Execution Time'];
- });
- title += ' ' + totalTime + 'ms';
- if(moduleSelector) {
- title += ' \'' + moduleSelector + '\'';
- }
- if($allModules.length > 1) {
- title += ' ' + '(' + $allModules.length + ')';
- }
- if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
- console.groupCollapsed(title);
- if(console.table) {
- console.table(performance);
- }
- else {
- $.each(performance, function(index, data) {
- console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
- });
- }
- console.groupEnd();
- }
- performance = [];
- }
- },
- invoke: function(query, passedArguments, context) {
- var
- object = instance,
- maxDepth,
- found,
- response
- ;
- passedArguments = passedArguments || queryArguments;
- context = element || context;
- if(typeof query == 'string' && object !== undefined) {
- query = query.split(/[\. ]/);
- maxDepth = query.length - 1;
- $.each(query, function(depth, value) {
- var camelCaseValue = (depth != maxDepth)
- ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
- : query
- ;
- if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
- object = object[camelCaseValue];
- }
- else if( object[camelCaseValue] !== undefined ) {
- found = object[camelCaseValue];
- return false;
- }
- else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
- object = object[value];
- }
- else if( object[value] !== undefined ) {
- found = object[value];
- return false;
- }
- else {
- return false;
- }
- });
- }
- if( $.isFunction( found ) ) {
- response = found.apply(context, passedArguments);
- }
- else if(found !== undefined) {
- response = found;
- }
- if(Array.isArray(returnedValue)) {
- returnedValue.push(response);
- }
- else if(returnedValue !== undefined) {
- returnedValue = [returnedValue, response];
- }
- else if(response !== undefined) {
- returnedValue = response;
- }
- return found;
- }
- };
- if(methodInvoked) {
- if(instance === undefined) {
- module.initialize();
- }
- module.invoke(query);
- }
- else {
- if(instance !== undefined) {
- instance.invoke('destroy');
- }
- module.initialize();
- }
-
- })
- ;
-
- return (returnedValue !== undefined)
- ? returnedValue
- : this
- ;
-};
-
-$.fn.search.settings = {
-
- name : 'Search',
- namespace : 'search',
-
- silent : false,
- debug : false,
- verbose : false,
- performance : true,
-
- // template to use (specified in settings.templates)
- type : 'standard',
-
- // minimum characters required to search
- minCharacters : 1,
-
- // whether to select first result after searching automatically
- selectFirstResult : false,
-
- // API config
- apiSettings : false,
-
- // object to search
- source : false,
-
- // Whether search should query current term on focus
- searchOnFocus : true,
-
- // fields to search
- searchFields : [
- 'id',
- 'title',
- 'description'
- ],
-
- // field to display in standard results template
- displayField : '',
-
- // search anywhere in value (set to 'exact' to require exact matches
- fullTextSearch : 'exact',
-
- // match results also if they contain diacritics of the same base character (for example searching for "a" will also match "á" or "â" or "à", etc...)
- ignoreDiacritics : false,
-
- // whether to add events to prompt automatically
- automatic : true,
-
- // delay before hiding menu after blur
- hideDelay : 0,
-
- // delay before searching
- searchDelay : 200,
-
- // maximum results returned from search
- maxResults : 7,
-
- // whether to store lookups in local cache
- cache : true,
-
- // whether no results errors should be shown
- showNoResults : true,
-
- // preserve possible html of resultset values
- preserveHTML : true,
-
- // transition settings
- transition : 'scale',
- duration : 200,
- easing : 'easeOutExpo',
-
- // callbacks
- onSelect : false,
- onResultsAdd : false,
-
- onSearchQuery : function(query){},
- onResults : function(response){},
-
- onResultsOpen : function(){},
- onResultsClose : function(){},
-
- className: {
- animating : 'animating',
- active : 'active',
- empty : 'empty',
- focus : 'focus',
- hidden : 'hidden',
- loading : 'loading',
- results : 'results',
- pressed : 'down'
- },
-
- error : {
- source : 'Cannot search. No source used, and Semantic API module was not included',
- noResultsHeader : 'No Results',
- noResults : 'Your search returned no results',
- logging : 'Error in debug logging, exiting.',
- noEndpoint : 'No search endpoint was specified',
- noTemplate : 'A valid template name was not specified.',
- oldSearchSyntax : 'searchFullText setting has been renamed fullTextSearch for consistency, please adjust your settings.',
- serverError : 'There was an issue querying the server.',
- maxResults : 'Results must be an array to use maxResults setting',
- method : 'The method you called is not defined.',
- noNormalize : '"ignoreDiacritics" setting will be ignored. Browser does not support String().normalize(). You may consider including <https://cdn.jsdelivr.net/npm/unorm@1.4.1/lib/unorm.min.js> as a polyfill.'
- },
-
- metadata: {
- cache : 'cache',
- results : 'results',
- result : 'result'
- },
-
- regExp: {
- escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
- beginsWith : '(?:\s|^)'
- },
-
- // maps api response attributes to internal representation
- fields: {
- categories : 'results', // array of categories (category view)
- categoryName : 'name', // name of category (category view)
- categoryResults : 'results', // array of results (category view)
- description : 'description', // result description
- image : 'image', // result image
- price : 'price', // result price
- results : 'results', // array of results (standard)
- title : 'title', // result title
- url : 'url', // result url
- action : 'action', // "view more" object name
- actionText : 'text', // "view more" text
- actionURL : 'url' // "view more" url
- },
-
- selector : {
- prompt : '.prompt',
- searchButton : '.search.button',
- results : '.results',
- message : '.results > .message',
- category : '.category',
- result : '.result',
- title : '.title, .name'
- },
-
- templates: {
- escape: function(string, preserveHTML) {
- if (preserveHTML){
- return string;
- }
- var
- badChars = /[<>"'`]/g,
- shouldEscape = /[&<>"'`]/,
- escape = {
- "<": "&lt;",
- ">": "&gt;",
- '"': "&quot;",
- "'": "&#x27;",
- "`": "&#x60;"
- },
- escapedChar = function(chr) {
- return escape[chr];
- }
- ;
- if(shouldEscape.test(string)) {
- string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
- return string.replace(badChars, escapedChar);
- }
- return string;
- },
- message: function(message, type, header) {
- var
- html = ''
- ;
- if(message !== undefined && type !== undefined) {
- html += ''
- + '<div class="message ' + type + '">'
- ;
- if(header) {
- html += ''
- + '<div class="header">' + header + '</div>'
- ;
- }
- html += ' <div class="description">' + message + '</div>';
- html += '</div>';
- }
- return html;
- },
- category: function(response, fields, preserveHTML) {
- var
- html = '',
- escape = $.fn.search.settings.templates.escape
- ;
- if(response[fields.categoryResults] !== undefined) {
-
- // each category
- $.each(response[fields.categoryResults], function(index, category) {
- if(category[fields.results] !== undefined && category.results.length > 0) {
-
- html += '<div class="category">';
-
- if(category[fields.categoryName] !== undefined) {
- html += '<div class="name">' + escape(category[fields.categoryName], preserveHTML) + '</div>';
- }
-
- // each item inside category
- html += '<div class="results">';
- $.each(category.results, function(index, result) {
- if(result[fields.url]) {
- html += '<a class="result" href="' + result[fields.url].replace(/"/g,"") + '">';
- }
- else {
- html += '<a class="result">';
- }
- if(result[fields.image] !== undefined) {
- html += ''
- + '<div class="image">'
- + ' <img src="' + result[fields.image].replace(/"/g,"") + '">'
- + '</div>'
- ;
- }
- html += '<div class="content">';
- if(result[fields.price] !== undefined) {
- html += '<div class="price">' + escape(result[fields.price], preserveHTML) + '</div>';
- }
- if(result[fields.title] !== undefined) {
- html += '<div class="title">' + escape(result[fields.title], preserveHTML) + '</div>';
- }
- if(result[fields.description] !== undefined) {
- html += '<div class="description">' + escape(result[fields.description], preserveHTML) + '</div>';
- }
- html += ''
- + '</div>'
- ;
- html += '</a>';
- });
- html += '</div>';
- html += ''
- + '</div>'
- ;
- }
- });
- if(response[fields.action]) {
- if(fields.actionURL === false) {
- html += ''
- + '<div class="action">'
- + escape(response[fields.action][fields.actionText], preserveHTML)
- + '</div>';
- } else {
- html += ''
- + '<a href="' + response[fields.action][fields.actionURL].replace(/"/g,"") + '" class="action">'
- + escape(response[fields.action][fields.actionText], preserveHTML)
- + '</a>';
- }
- }
- return html;
- }
- return false;
- },
- standard: function(response, fields, preserveHTML) {
- var
- html = '',
- escape = $.fn.search.settings.templates.escape
- ;
- if(response[fields.results] !== undefined) {
-
- // each result
- $.each(response[fields.results], function(index, result) {
- if(result[fields.url]) {
- html += '<a class="result" href="' + result[fields.url].replace(/"/g,"") + '">';
- }
- else {
- html += '<a class="result">';
- }
- if(result[fields.image] !== undefined) {
- html += ''
- + '<div class="image">'
- + ' <img src="' + result[fields.image].replace(/"/g,"") + '">'
- + '</div>'
- ;
- }
- html += '<div class="content">';
- if(result[fields.price] !== undefined) {
- html += '<div class="price">' + escape(result[fields.price], preserveHTML) + '</div>';
- }
- if(result[fields.title] !== undefined) {
- html += '<div class="title">' + escape(result[fields.title], preserveHTML) + '</div>';
- }
- if(result[fields.description] !== undefined) {
- html += '<div class="description">' + escape(result[fields.description], preserveHTML) + '</div>';
- }
- html += ''
- + '</div>'
- ;
- html += '</a>';
- });
- if(response[fields.action]) {
- if(fields.actionURL === false) {
- html += ''
- + '<div class="action">'
- + escape(response[fields.action][fields.actionText], preserveHTML)
- + '</div>';
- } else {
- html += ''
- + '<a href="' + response[fields.action][fields.actionURL].replace(/"/g,"") + '" class="action">'
- + escape(response[fields.action][fields.actionText], preserveHTML)
- + '</a>';
- }
- }
- return html;
- }
- return false;
- }
- }
-};
-
-})( jQuery, window, document );
-
-/*!
- * # Fomantic-UI - Tab
- * http://github.com/fomantic/Fomantic-UI/
- *
- *
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- */
-
-;(function ($, window, document, undefined) {
-
-'use strict';
-
-$.isWindow = $.isWindow || function(obj) {
- return obj != null && obj === obj.window;
-};
-$.isFunction = $.isFunction || function(obj) {
- return typeof obj === "function" && typeof obj.nodeType !== "number";
-};
-
-window = (typeof window != 'undefined' && window.Math == Math)
- ? window
- : (typeof self != 'undefined' && self.Math == Math)
- ? self
- : Function('return this')()
-;
-
-$.fn.tab = function(parameters) {
-
- var
- // use window context if none specified
- $allModules = $.isFunction(this)
- ? $(window)
- : $(this),
-
- moduleSelector = $allModules.selector || '',
- time = new Date().getTime(),
- performance = [],
-
- query = arguments[0],
- methodInvoked = (typeof query == 'string'),
- queryArguments = [].slice.call(arguments, 1),
-
- initializedHistory = false,
- returnedValue
- ;
-
- $allModules
- .each(function() {
- var
-
- settings = ( $.isPlainObject(parameters) )
- ? $.extend(true, {}, $.fn.tab.settings, parameters)
- : $.extend({}, $.fn.tab.settings),
-
- className = settings.className,
- metadata = settings.metadata,
- selector = settings.selector,
- error = settings.error,
- regExp = settings.regExp,
-
- eventNamespace = '.' + settings.namespace,
- moduleNamespace = 'module-' + settings.namespace,
-
- $module = $(this),
- $context,
- $tabs,
-
- cache = {},
- firstLoad = true,
- recursionDepth = 0,
- element = this,
- instance = $module.data(moduleNamespace),
-
- activeTabPath,
- parameterArray,
- module,
-
- historyEvent
-
- ;
-
- module = {
-
- initialize: function() {
- module.debug('Initializing tab menu item', $module);
- module.fix.callbacks();
- module.determineTabs();
-
- module.debug('Determining tabs', settings.context, $tabs);
- // set up automatic routing
- if(settings.auto) {
- module.set.auto();
- }
- module.bind.events();
-
- if(settings.history && !initializedHistory) {
- module.initializeHistory();
- initializedHistory = true;
- }
-
- if(settings.autoTabActivation && instance === undefined && module.determine.activeTab() == null) {
- module.debug('No active tab detected, setting first tab active', module.get.initialPath());
- module.changeTab(settings.autoTabActivation === true ? module.get.initialPath() : settings.autoTabActivation);
- };
-
- module.instantiate();
- },
-
- instantiate: function () {
- module.verbose('Storing instance of module', module);
- instance = module;
- $module
- .data(moduleNamespace, module)
- ;
- },
-
- destroy: function() {
- module.debug('Destroying tabs', $module);
- $module
- .removeData(moduleNamespace)
- .off(eventNamespace)
- ;
- },
-
- bind: {
- events: function() {
- // if using $.tab don't add events
- if( !$.isWindow( element ) ) {
- module.debug('Attaching tab activation events to element', $module);
- $module
- .on('click' + eventNamespace, module.event.click)
- ;
- }
- }
- },
-
- determineTabs: function() {
- var
- $reference
- ;
-
- // determine tab context
- if(settings.context === 'parent') {
- if($module.closest(selector.ui).length > 0) {
- $reference = $module.closest(selector.ui);
- module.verbose('Using closest UI element as parent', $reference);
- }
- else {
- $reference = $module;
- }
- $context = $reference.parent();
- module.verbose('Determined parent element for creating context', $context);
- }
- else if(settings.context) {
- $context = $(settings.context);
- module.verbose('Using selector for tab context', settings.context, $context);
- }
- else {
- $context = $('body');
- }
- // find tabs
- if(settings.childrenOnly) {
- $tabs = $context.children(selector.tabs);
- module.debug('Searching tab context children for tabs', $context, $tabs);
- }
- else {
- $tabs = $context.find(selector.tabs);
- module.debug('Searching tab context for tabs', $context, $tabs);
- }
- },
-
- fix: {
- callbacks: function() {
- if( $.isPlainObject(parameters) && (parameters.onTabLoad || parameters.onTabInit) ) {
- if(parameters.onTabLoad) {
- parameters.onLoad = parameters.onTabLoad;
- delete parameters.onTabLoad;
- module.error(error.legacyLoad, parameters.onLoad);
- }
- if(parameters.onTabInit) {
- parameters.onFirstLoad = parameters.onTabInit;
- delete parameters.onTabInit;
- module.error(error.legacyInit, parameters.onFirstLoad);
- }
- settings = $.extend(true, {}, $.fn.tab.settings, parameters);
- }
- }
- },
-
- initializeHistory: function() {
- module.debug('Initializing page state');
- if( $.address === undefined ) {
- module.error(error.state);
- return false;
- }
- else {
- if(settings.historyType == 'state') {
- module.debug('Using HTML5 to manage state');
- if(settings.path !== false) {
- $.address
- .history(true)
- .state(settings.path)
- ;
- }
- else {
- module.error(error.path);
- return false;
- }
- }
- $.address
- .bind('change', module.event.history.change)
- ;
- }
- },
-
- event: {
- click: function(event) {
- var
- tabPath = $(this).data(metadata.tab)
- ;
- if(tabPath !== undefined) {
- if(settings.history) {
- module.verbose('Updating page state', event);
- $.address.value(tabPath);
- }
- else {
- module.verbose('Changing tab', event);
- module.changeTab(tabPath);
- }
- event.preventDefault();
- }
- else {
- module.debug('No tab specified');
- }
- },
- history: {
- change: function(event) {
- var
- tabPath = event.pathNames.join('/') || module.get.initialPath(),
- pageTitle = settings.templates.determineTitle(tabPath) || false
- ;
- module.performance.display();
- module.debug('History change event', tabPath, event);
- historyEvent = event;
- if(tabPath !== undefined) {
- module.changeTab(tabPath);
- }
- if(pageTitle) {
- $.address.title(pageTitle);
- }
- }
- }
- },
-
- refresh: function() {
- if(activeTabPath) {
- module.debug('Refreshing tab', activeTabPath);
- module.changeTab(activeTabPath);
- }
- },
-
- cache: {
-
- read: function(cacheKey) {
- return (cacheKey !== undefined)
- ? cache[cacheKey]
- : false
- ;
- },
- add: function(cacheKey, content) {
- cacheKey = cacheKey || activeTabPath;
- module.debug('Adding cached content for', cacheKey);
- cache[cacheKey] = content;
- },
- remove: function(cacheKey) {
- cacheKey = cacheKey || activeTabPath;
- module.debug('Removing cached content for', cacheKey);
- delete cache[cacheKey];
- }
- },
-
- escape: {
- string: function(text) {
- text = String(text);
- return text.replace(regExp.escape, '\\$&');
- }
- },
-
- set: {
- auto: function() {
- var
- url = (typeof settings.path == 'string')
- ? settings.path.replace(/\/$/, '') + '/{$tab}'
- : '/{$tab}'
- ;
- module.verbose('Setting up automatic tab retrieval from server', url);
- if($.isPlainObject(settings.apiSettings)) {
- settings.apiSettings.url = url;
- }
- else {
- settings.apiSettings = {
- url: url
- };
- }
- },
- loading: function(tabPath) {
- var
- $tab = module.get.tabElement(tabPath),
- isLoading = $tab.hasClass(className.loading)
- ;
- if(!isLoading) {
- module.verbose('Setting loading state for', $tab);
- $tab
- .addClass(className.loading)
- .siblings($tabs)
- .removeClass(className.active + ' ' + className.loading)
- ;
- if($tab.length > 0) {
- settings.onRequest.call($tab[0], tabPath);
- }
- }
- },
- state: function(state) {
- $.address.value(state);
- }
- },
-
- changeTab: function(tabPath) {
- var
- pushStateAvailable = (window.history && window.history.pushState),
- shouldIgnoreLoad = (pushStateAvailable && settings.ignoreFirstLoad && firstLoad),
- remoteContent = (settings.auto || $.isPlainObject(settings.apiSettings) ),
- // only add default path if not remote content
- pathArray = (remoteContent && !shouldIgnoreLoad)
- ? module.utilities.pathToArray(tabPath)
- : module.get.defaultPathArray(tabPath)
- ;
- tabPath = module.utilities.arrayToPath(pathArray);
- $.each(pathArray, function(index, tab) {
- var
- currentPathArray = pathArray.slice(0, index + 1),
- currentPath = module.utilities.arrayToPath(currentPathArray),
-
- isTab = module.is.tab(currentPath),
- isLastIndex = (index + 1 == pathArray.length),
-
- $tab = module.get.tabElement(currentPath),
- $anchor,
- nextPathArray,
- nextPath,
- isLastTab
- ;
- module.verbose('Looking for tab', tab);
- if(isTab) {
- module.verbose('Tab was found', tab);
- // scope up
- activeTabPath = currentPath;
- parameterArray = module.utilities.filterArray(pathArray, currentPathArray);
-
- if(isLastIndex) {
- isLastTab = true;
- }
- else {
- nextPathArray = pathArray.slice(0, index + 2);
- nextPath = module.utilities.arrayToPath(nextPathArray);
- isLastTab = ( !module.is.tab(nextPath) );
- if(isLastTab) {
- module.verbose('Tab parameters found', nextPathArray);
- }
- }
- if(isLastTab && remoteContent) {
- if(!shouldIgnoreLoad) {
- module.activate.navigation(currentPath);
- module.fetch.content(currentPath, tabPath);
- }
- else {
- module.debug('Ignoring remote content on first tab load', currentPath);
- firstLoad = false;
- module.cache.add(tabPath, $tab.html());
- module.activate.all(currentPath);
- settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
- settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
- }
- return false;
- }
- else {
- module.debug('Opened local tab', currentPath);
- module.activate.all(currentPath);
- if( !module.cache.read(currentPath) ) {
- module.cache.add(currentPath, true);
- module.debug('First time tab loaded calling tab init');
- settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
- }
- settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
- }
-
- }
- else if(tabPath.search('/') == -1 && tabPath !== '') {
- // look for in page anchor
- tabPath = module.escape.string(tabPath);
- $anchor = $('#' + tabPath + ', a[name="' + tabPath + '"]');
- currentPath = $anchor.closest('[data-tab]').data(metadata.tab);
- $tab = module.get.tabElement(currentPath);
- // if anchor exists use parent tab
- if($anchor && $anchor.length > 0 && currentPath) {
- module.debug('Anchor link used, opening parent tab', $tab, $anchor);
- if( !$tab.hasClass(className.active) ) {
- setTimeout(function() {
- module.scrollTo($anchor);
- }, 0);
- }
- module.activate.all(currentPath);
- if( !module.cache.read(currentPath) ) {
- module.cache.add(currentPath, true);
- module.debug('First time tab loaded calling tab init');
- settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
- }
- settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
- return false;
- }
- }
- else {
- module.error(error.missingTab, $module, $context, currentPath);
- return false;
- }
- });
- },
-
- scrollTo: function($element) {
- var
- scrollOffset = ($element && $element.length > 0)
- ? $element.offset().top
- : false
- ;
- if(scrollOffset !== false) {
- module.debug('Forcing scroll to an in-page link in a hidden tab', scrollOffset, $element);
- $(document).scrollTop(scrollOffset);
- }
- },
-
- update: {
- content: function(tabPath, html, evaluateScripts) {
- var
- $tab = module.get.tabElement(tabPath),
- tab = $tab[0]
- ;
- evaluateScripts = (evaluateScripts !== undefined)
- ? evaluateScripts
- : settings.evaluateScripts
- ;
- if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && typeof html !== 'string') {
- $tab
- .empty()
- .append($(html).clone(true))
- ;
- }
- else {
- if(evaluateScripts) {
- module.debug('Updating HTML and evaluating inline scripts', tabPath, html);
- $tab.html(html);
- }
- else {
- module.debug('Updating HTML', tabPath, html);
- tab.innerHTML = html;
- }
- }
- }
- },
-
- fetch: {
-
- content: function(tabPath, fullTabPath) {
- var
- $tab = module.get.tabElement(tabPath),
- apiSettings = {
- dataType : 'html',
- encodeParameters : false,
- on : 'now',
- cache : settings.alwaysRefresh,
- headers : {
- 'X-Remote': true
- },
- onSuccess : function(response) {
- if(settings.cacheType == 'response') {
- module.cache.add(fullTabPath, response);
- }
- module.update.content(tabPath, response);
- if(tabPath == activeTabPath) {
- module.debug('Content loaded', tabPath);
- module.activate.tab(tabPath);
- }
- else {
- module.debug('Content loaded in background', tabPath);
- }
- settings.onFirstLoad.call($tab[0], tabPath, parameterArray, historyEvent);
- settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
-
- if(settings.loadOnce) {
- module.cache.add(fullTabPath, true);
- }
- else if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && $tab.children().length > 0) {
- setTimeout(function() {
- var
- $clone = $tab.children().clone(true)
- ;
- $clone = $clone.not('script');
- module.cache.add(fullTabPath, $clone);
- }, 0);
- }
- else {
- module.cache.add(fullTabPath, $tab.html());
- }
- },
- urlData: {
- tab: fullTabPath
- }
- },
- request = $tab.api('get request') || false,
- existingRequest = ( request && request.state() === 'pending' ),
- requestSettings,
- cachedContent
- ;
-
- fullTabPath = fullTabPath || tabPath;
- cachedContent = module.cache.read(fullTabPath);
-
-
- if(settings.cache && cachedContent) {
- module.activate.tab(tabPath);
- module.debug('Adding cached content', fullTabPath);
- if(!settings.loadOnce) {
- if(settings.evaluateScripts == 'once') {
- module.update.content(tabPath, cachedContent, false);
- }
- else {
- module.update.content(tabPath, cachedContent);
- }
- }
- settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
- }
- else if(existingRequest) {
- module.set.loading(tabPath);
- module.debug('Content is already loading', fullTabPath);
- }
- else if($.api !== undefined) {
- requestSettings = $.extend(true, {}, settings.apiSettings, apiSettings);
- module.debug('Retrieving remote content', fullTabPath, requestSettings);
- module.set.loading(tabPath);
- $tab.api(requestSettings);
- }
- else {
- module.error(error.api);
- }
- }
- },
-
- activate: {
- all: function(tabPath) {
- module.activate.tab(tabPath);
- module.activate.navigation(tabPath);
- },
- tab: function(tabPath) {
- var
- $tab = module.get.tabElement(tabPath),
- $deactiveTabs = (settings.deactivate == 'siblings')
- ? $tab.siblings($tabs)
- : $tabs.not($tab),
- isActive = $tab.hasClass(className.active)
- ;
- module.verbose('Showing tab content for', $tab);
- if(!isActive) {
- $tab
- .addClass(className.active)
- ;
- $deactiveTabs
- .removeClass(className.active + ' ' + className.loading)
- ;
- if($tab.length > 0) {
- settings.onVisible.call($tab[0], tabPath);
- }
- }
- },
- navigation: function(tabPath) {
- var
- $navigation = module.get.navElement(tabPath),
- $deactiveNavigation = (settings.deactivate == 'siblings')
- ? $navigation.siblings($allModules)
- : $allModules.not($navigation),
- isActive = $navigation.hasClass(className.active)
- ;
- module.verbose('Activating tab navigation for', $navigation, tabPath);
- if(!isActive) {
- $navigation
- .addClass(className.active)
- ;
- $deactiveNavigation
- .removeClass(className.active + ' ' + className.loading)
- ;
- }
- }
- },
-
- deactivate: {
- all: function() {
- module.deactivate.navigation();
- module.deactivate.tabs();
- },
- navigation: function() {
- $allModules
- .removeClass(className.active)
- ;
- },
- tabs: function() {
- $tabs
- .removeClass(className.active + ' ' + className.loading)
- ;
- }
- },
-
- is: {
- tab: function(tabName) {
- return (tabName !== undefined)
- ? ( module.get.tabElement(tabName).length > 0 )
- : false
- ;
- }
- },
-
- get: {
- initialPath: function() {
- return $allModules.eq(0).data(metadata.tab) || $tabs.eq(0).data(metadata.tab);
- },
- path: function() {
- return $.address.value();
- },
- // adds default tabs to tab path
- defaultPathArray: function(tabPath) {
- return module.utilities.pathToArray( module.get.defaultPath(tabPath) );
- },
- defaultPath: function(tabPath) {
- var
- $defaultNav = $allModules.filter('[data-' + metadata.tab + '^="' + module.escape.string(tabPath) + '/"]').eq(0),
- defaultTab = $defaultNav.data(metadata.tab) || false
- ;
- if( defaultTab ) {
- module.debug('Found default tab', defaultTab);
- if(recursionDepth < settings.maxDepth) {
- recursionDepth++;
- return module.get.defaultPath(defaultTab);
- }
- module.error(error.recursion);
- }
- else {
- module.debug('No default tabs found for', tabPath, $tabs);
- }
- recursionDepth = 0;
- return tabPath;
- },
- navElement: function(tabPath) {
- tabPath = tabPath || activeTabPath;
- return $allModules.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]');
- },
- tabElement: function(tabPath) {
- var
- $fullPathTab,
- $simplePathTab,
- tabPathArray,
- lastTab
- ;
- tabPath = tabPath || activeTabPath;
- tabPathArray = module.utilities.pathToArray(tabPath);
- lastTab = module.utilities.last(tabPathArray);
- $fullPathTab = $tabs.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]');
- $simplePathTab = $tabs.filter('[data-' + metadata.tab + '="' + module.escape.string(lastTab) + '"]');
- return ($fullPathTab.length > 0)
- ? $fullPathTab
- : $simplePathTab
- ;
- },
- tab: function() {
- return activeTabPath;
- }
- },
-
- determine: {
- activeTab: function() {
- var activeTab = null;
-
- $tabs.each(function(_index, tab) {
- var $tab = $(tab);
-
- if( $tab.hasClass(className.active) ) {
- var
- tabPath = $(this).data(metadata.tab),
- $anchor = $allModules.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]')
- ;
-
- if( $anchor.hasClass(className.active) ) {
- activeTab = tabPath;
- }
- }
- });
-
- return activeTab;
- }
- },
-
- utilities: {
- filterArray: function(keepArray, removeArray) {
- return $.grep(keepArray, function(keepValue) {
- return ( $.inArray(keepValue, removeArray) == -1);
- });
- },
- last: function(array) {
- return Array.isArray(array)
- ? array[ array.length - 1]
- : false
- ;
- },
- pathToArray: function(pathName) {
- if(pathName === undefined) {
- pathName = activeTabPath;
- }
- return typeof pathName == 'string'
- ? pathName.split('/')
- : [pathName]
- ;
- },
- arrayToPath: function(pathArray) {
- return Array.isArray(pathArray)
- ? pathArray.join('/')
- : false
- ;
- }
- },
-
- setting: function(name, value) {
- module.debug('Changing setting', name, value);
- if( $.isPlainObject(name) ) {
- $.extend(true, settings, name);
- }
- else if(value !== undefined) {
- if($.isPlainObject(settings[name])) {
- $.extend(true, settings[name], value);
- }
- else {
- settings[name] = value;
- }
- }
- else {
- return settings[name];
- }
- },
- internal: function(name, value) {
- if( $.isPlainObject(name) ) {
- $.extend(true, module, name);
- }
- else if(value !== undefined) {
- module[name] = value;
- }
- else {
- return module[name];
- }
- },
- debug: function() {
- if(!settings.silent && settings.debug) {
- if(settings.performance) {
- module.performance.log(arguments);
- }
- else {
- module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
- module.debug.apply(console, arguments);
- }
- }
- },
- verbose: function() {
- if(!settings.silent && settings.verbose && settings.debug) {
- if(settings.performance) {
- module.performance.log(arguments);
- }
- else {
- module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
- module.verbose.apply(console, arguments);
- }
- }
- },
- error: function() {
- if(!settings.silent) {
- module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
- module.error.apply(console, arguments);
- }
- },
- performance: {
- log: function(message) {
- var
- currentTime,
- executionTime,
- previousTime
- ;
- if(settings.performance) {
- currentTime = new Date().getTime();
- previousTime = time || currentTime;
- executionTime = currentTime - previousTime;
- time = currentTime;
- performance.push({
- 'Name' : message[0],
- 'Arguments' : [].slice.call(message, 1) || '',
- 'Element' : element,
- 'Execution Time' : executionTime
- });
- }
- clearTimeout(module.performance.timer);
- module.performance.timer = setTimeout(module.performance.display, 500);
- },
- display: function() {
- var
- title = settings.name + ':',
- totalTime = 0
- ;
- time = false;
- clearTimeout(module.performance.timer);
- $.each(performance, function(index, data) {
- totalTime += data['Execution Time'];
- });
- title += ' ' + totalTime + 'ms';
- if(moduleSelector) {
- title += ' \'' + moduleSelector + '\'';
- }
- if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
- console.groupCollapsed(title);
- if(console.table) {
- console.table(performance);
- }
- else {
- $.each(performance, function(index, data) {
- console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
- });
- }
- console.groupEnd();
- }
- performance = [];
- }
- },
- invoke: function(query, passedArguments, context) {
- var
- object = instance,
- maxDepth,
- found,
- response
- ;
- passedArguments = passedArguments || queryArguments;
- context = element || context;
- if(typeof query == 'string' && object !== undefined) {
- query = query.split(/[\. ]/);
- maxDepth = query.length - 1;
- $.each(query, function(depth, value) {
- var camelCaseValue = (depth != maxDepth)
- ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
- : query
- ;
- if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
- object = object[camelCaseValue];
- }
- else if( object[camelCaseValue] !== undefined ) {
- found = object[camelCaseValue];
- return false;
- }
- else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
- object = object[value];
- }
- else if( object[value] !== undefined ) {
- found = object[value];
- return false;
- }
- else {
- module.error(error.method, query);
- return false;
- }
- });
- }
- if ( $.isFunction( found ) ) {
- response = found.apply(context, passedArguments);
- }
- else if(found !== undefined) {
- response = found;
- }
- if(Array.isArray(returnedValue)) {
- returnedValue.push(response);
- }
- else if(returnedValue !== undefined) {
- returnedValue = [returnedValue, response];
- }
- else if(response !== undefined) {
- returnedValue = response;
- }
- return found;
- }
- };
- if(methodInvoked) {
- if(instance === undefined) {
- module.initialize();
- }
- module.invoke(query);
- }
- else {
- if(instance !== undefined) {
- instance.invoke('destroy');
- }
- module.initialize();
- }
- })
- ;
- return (returnedValue !== undefined)
- ? returnedValue
- : this
- ;
-
-};
-
-// shortcut for tabbed content with no defined navigation
-$.tab = function() {
- $(window).tab.apply(this, arguments);
-};
-
-$.fn.tab.settings = {
-
- name : 'Tab',
- namespace : 'tab',
-
- silent : false,
- debug : false,
- verbose : false,
- performance : true,
-
- auto : false, // uses pjax style endpoints fetching content from same url with remote-content headers
- history : false, // use browser history
- historyType : 'hash', // #/ or html5 state
- path : false, // base path of url
-
- context : false, // specify a context that tabs must appear inside
- childrenOnly : false, // use only tabs that are children of context
- maxDepth : 25, // max depth a tab can be nested
-
- deactivate : 'siblings', // whether tabs should deactivate sibling menu elements or all elements initialized together
-
- alwaysRefresh : false, // load tab content new every tab click
- cache : true, // cache the content requests to pull locally
- loadOnce : false, // Whether tab data should only be loaded once when using remote content
- cacheType : 'response', // Whether to cache exact response, or to html cache contents after scripts execute
- ignoreFirstLoad : false, // don't load remote content on first load
-
- apiSettings : false, // settings for api call
- evaluateScripts : 'once', // whether inline scripts should be parsed (true/false/once). Once will not re-evaluate on cached content
- autoTabActivation: true, // whether a non existing active tab will auto activate the first available tab
-
- onFirstLoad : function(tabPath, parameterArray, historyEvent) {}, // called first time loaded
- onLoad : function(tabPath, parameterArray, historyEvent) {}, // called on every load
- onVisible : function(tabPath, parameterArray, historyEvent) {}, // called every time tab visible
- onRequest : function(tabPath, parameterArray, historyEvent) {}, // called ever time a tab beings loading remote content
-
- templates : {
- determineTitle: function(tabArray) {} // returns page title for path
- },
-
- error: {
- api : 'You attempted to load content without API module',
- method : 'The method you called is not defined',
- missingTab : 'Activated tab cannot be found. Tabs are case-sensitive.',
- noContent : 'The tab you specified is missing a content url.',
- path : 'History enabled, but no path was specified',
- recursion : 'Max recursive depth reached',
- legacyInit : 'onTabInit has been renamed to onFirstLoad in 2.0, please adjust your code.',
- legacyLoad : 'onTabLoad has been renamed to onLoad in 2.0. Please adjust your code',
- state : 'History requires Asual\'s Address library <https://github.com/asual/jquery-address>'
- },
-
- regExp : {
- escape : /[-[\]{}()*+?.,\\^$|#\s:=@]/g
- },
-
- metadata : {
- tab : 'tab',
- loaded : 'loaded',
- promise: 'promise'
- },
-
- className : {
- loading : 'loading',
- active : 'active'
- },
-
- selector : {
- tabs : '.ui.tab',
- ui : '.ui'
- }
-
-};
-
-})( jQuery, window, document );
diff --git a/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2 b/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
deleted file mode 100644
index 978a681a10..0000000000
--- a/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
+++ /dev/null
Binary files differ
diff --git a/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2 b/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
deleted file mode 100644
index 7e0118e526..0000000000
--- a/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
+++ /dev/null
Binary files differ
diff --git a/web_src/fomantic/package-lock.json b/web_src/fomantic/package-lock.json
deleted file mode 100644
index 0c02f59806..0000000000
--- a/web_src/fomantic/package-lock.json
+++ /dev/null
@@ -1,8777 +0,0 @@
-{
- "name": "fomantic",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "dependencies": {
- "fomantic-ui": "2.8.7"
- }
- },
- "node_modules/@choojs/findup": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/@choojs/findup/-/findup-0.2.1.tgz",
- "integrity": "sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==",
- "license": "MIT",
- "dependencies": {
- "commander": "^2.15.1"
- },
- "bin": {
- "findup": "bin/findup.js"
- }
- },
- "node_modules/@choojs/findup/node_modules/commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "license": "MIT"
- },
- "node_modules/@isaacs/cliui": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
- "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
- "license": "ISC",
- "dependencies": {
- "string-width": "^5.1.2",
- "string-width-cjs": "npm:string-width@^4.2.0",
- "strip-ansi": "^7.0.1",
- "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
- "wrap-ansi": "^8.1.0",
- "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
- "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
- }
- },
- "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
- "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
- "version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
- "license": "MIT"
- },
- "node_modules/@isaacs/cliui/node_modules/string-width": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
- "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
- "license": "MIT",
- "dependencies": {
- "eastasianwidth": "^0.2.0",
- "emoji-regex": "^9.2.2",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^6.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
- }
- },
- "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
- "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.1.0",
- "string-width": "^5.0.1",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/@octokit/auth-token": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
- "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
- "license": "MIT",
- "dependencies": {
- "@octokit/types": "^6.0.3"
- }
- },
- "node_modules/@octokit/core": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.2.tgz",
- "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@octokit/auth-token": "^5.0.0",
- "@octokit/graphql": "^8.0.0",
- "@octokit/request": "^9.0.0",
- "@octokit/request-error": "^6.0.1",
- "@octokit/types": "^13.0.0",
- "before-after-hook": "^3.0.2",
- "universal-user-agent": "^7.0.0"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/@octokit/core/node_modules/@octokit/auth-token": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz",
- "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==",
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/@octokit/core/node_modules/@octokit/endpoint": {
- "version": "10.1.1",
- "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz",
- "integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@octokit/types": "^13.0.0",
- "universal-user-agent": "^7.0.2"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/@octokit/core/node_modules/@octokit/openapi-types": {
- "version": "22.2.0",
- "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
- "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==",
- "license": "MIT",
- "peer": true
- },
- "node_modules/@octokit/core/node_modules/@octokit/request": {
- "version": "9.1.3",
- "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz",
- "integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@octokit/endpoint": "^10.0.0",
- "@octokit/request-error": "^6.0.1",
- "@octokit/types": "^13.1.0",
- "universal-user-agent": "^7.0.2"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/@octokit/core/node_modules/@octokit/request-error": {
- "version": "6.1.5",
- "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.5.tgz",
- "integrity": "sha512-IlBTfGX8Yn/oFPMwSfvugfncK2EwRLjzbrpifNaMY8o/HTEAFqCA1FZxjD9cWvSKBHgrIhc4CSBIzMxiLsbzFQ==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@octokit/types": "^13.0.0"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/@octokit/core/node_modules/@octokit/types": {
- "version": "13.6.2",
- "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.2.tgz",
- "integrity": "sha512-WpbZfZUcZU77DrSW4wbsSgTPfKcp286q3ItaIgvSbBpZJlu6mnYXAkjZz6LVZPXkEvLIM8McanyZejKTYUHipA==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@octokit/openapi-types": "^22.2.0"
- }
- },
- "node_modules/@octokit/core/node_modules/before-after-hook": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz",
- "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==",
- "license": "Apache-2.0",
- "peer": true
- },
- "node_modules/@octokit/core/node_modules/universal-user-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
- "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==",
- "license": "ISC",
- "peer": true
- },
- "node_modules/@octokit/endpoint": {
- "version": "6.0.12",
- "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
- "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
- "license": "MIT",
- "dependencies": {
- "@octokit/types": "^6.0.3",
- "is-plain-object": "^5.0.0",
- "universal-user-agent": "^6.0.0"
- }
- },
- "node_modules/@octokit/endpoint/node_modules/universal-user-agent": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
- "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
- "license": "ISC"
- },
- "node_modules/@octokit/graphql": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.1.tgz",
- "integrity": "sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@octokit/request": "^9.0.0",
- "@octokit/types": "^13.0.0",
- "universal-user-agent": "^7.0.0"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/@octokit/graphql/node_modules/@octokit/endpoint": {
- "version": "10.1.1",
- "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz",
- "integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@octokit/types": "^13.0.0",
- "universal-user-agent": "^7.0.2"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": {
- "version": "22.2.0",
- "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
- "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==",
- "license": "MIT",
- "peer": true
- },
- "node_modules/@octokit/graphql/node_modules/@octokit/request": {
- "version": "9.1.3",
- "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz",
- "integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@octokit/endpoint": "^10.0.0",
- "@octokit/request-error": "^6.0.1",
- "@octokit/types": "^13.1.0",
- "universal-user-agent": "^7.0.2"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/@octokit/graphql/node_modules/@octokit/request-error": {
- "version": "6.1.5",
- "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.5.tgz",
- "integrity": "sha512-IlBTfGX8Yn/oFPMwSfvugfncK2EwRLjzbrpifNaMY8o/HTEAFqCA1FZxjD9cWvSKBHgrIhc4CSBIzMxiLsbzFQ==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@octokit/types": "^13.0.0"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/@octokit/graphql/node_modules/@octokit/types": {
- "version": "13.6.2",
- "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.2.tgz",
- "integrity": "sha512-WpbZfZUcZU77DrSW4wbsSgTPfKcp286q3ItaIgvSbBpZJlu6mnYXAkjZz6LVZPXkEvLIM8McanyZejKTYUHipA==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@octokit/openapi-types": "^22.2.0"
- }
- },
- "node_modules/@octokit/graphql/node_modules/universal-user-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
- "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==",
- "license": "ISC",
- "peer": true
- },
- "node_modules/@octokit/openapi-types": {
- "version": "12.11.0",
- "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz",
- "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==",
- "license": "MIT"
- },
- "node_modules/@octokit/plugin-paginate-rest": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz",
- "integrity": "sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q==",
- "license": "MIT",
- "dependencies": {
- "@octokit/types": "^2.0.1"
- }
- },
- "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": {
- "version": "2.16.2",
- "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz",
- "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==",
- "license": "MIT",
- "dependencies": {
- "@types/node": ">= 8"
- }
- },
- "node_modules/@octokit/plugin-request-log": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz",
- "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==",
- "license": "MIT",
- "peerDependencies": {
- "@octokit/core": ">=3"
- }
- },
- "node_modules/@octokit/plugin-rest-endpoint-methods": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz",
- "integrity": "sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ==",
- "license": "MIT",
- "dependencies": {
- "@octokit/types": "^2.0.1",
- "deprecation": "^2.3.1"
- }
- },
- "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": {
- "version": "2.16.2",
- "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz",
- "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==",
- "license": "MIT",
- "dependencies": {
- "@types/node": ">= 8"
- }
- },
- "node_modules/@octokit/request": {
- "version": "5.6.3",
- "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
- "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
- "license": "MIT",
- "dependencies": {
- "@octokit/endpoint": "^6.0.1",
- "@octokit/request-error": "^2.1.0",
- "@octokit/types": "^6.16.1",
- "is-plain-object": "^5.0.0",
- "node-fetch": "^2.6.7",
- "universal-user-agent": "^6.0.0"
- }
- },
- "node_modules/@octokit/request-error": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.1.tgz",
- "integrity": "sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA==",
- "license": "MIT",
- "dependencies": {
- "@octokit/types": "^2.0.0",
- "deprecation": "^2.0.0",
- "once": "^1.4.0"
- }
- },
- "node_modules/@octokit/request-error/node_modules/@octokit/types": {
- "version": "2.16.2",
- "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz",
- "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==",
- "license": "MIT",
- "dependencies": {
- "@types/node": ">= 8"
- }
- },
- "node_modules/@octokit/request/node_modules/@octokit/request-error": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
- "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
- "license": "MIT",
- "dependencies": {
- "@octokit/types": "^6.0.3",
- "deprecation": "^2.0.0",
- "once": "^1.4.0"
- }
- },
- "node_modules/@octokit/request/node_modules/universal-user-agent": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
- "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
- "license": "ISC"
- },
- "node_modules/@octokit/rest": {
- "version": "16.43.2",
- "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.2.tgz",
- "integrity": "sha512-ngDBevLbBTFfrHZeiS7SAMAZ6ssuVmXuya+F/7RaVvlysgGa1JKJkKWY+jV6TCJYcW0OALfJ7nTIGXcBXzycfQ==",
- "license": "MIT",
- "dependencies": {
- "@octokit/auth-token": "^2.4.0",
- "@octokit/plugin-paginate-rest": "^1.1.1",
- "@octokit/plugin-request-log": "^1.0.0",
- "@octokit/plugin-rest-endpoint-methods": "2.4.0",
- "@octokit/request": "^5.2.0",
- "@octokit/request-error": "^1.0.2",
- "atob-lite": "^2.0.0",
- "before-after-hook": "^2.0.0",
- "btoa-lite": "^1.0.0",
- "deprecation": "^2.0.0",
- "lodash.get": "^4.4.2",
- "lodash.set": "^4.3.2",
- "lodash.uniq": "^4.5.0",
- "octokit-pagination-methods": "^1.1.0",
- "once": "^1.4.0",
- "universal-user-agent": "^4.0.0"
- }
- },
- "node_modules/@octokit/types": {
- "version": "6.41.0",
- "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz",
- "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==",
- "license": "MIT",
- "dependencies": {
- "@octokit/openapi-types": "^12.11.0"
- }
- },
- "node_modules/@one-ini/wasm": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz",
- "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
- "license": "MIT"
- },
- "node_modules/@pkgjs/parseargs": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
- "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/@types/expect": {
- "version": "1.20.4",
- "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz",
- "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==",
- "license": "MIT"
- },
- "node_modules/@types/node": {
- "version": "22.10.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz",
- "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==",
- "license": "MIT",
- "dependencies": {
- "undici-types": "~6.20.0"
- }
- },
- "node_modules/@types/vinyl": {
- "version": "2.0.12",
- "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.12.tgz",
- "integrity": "sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==",
- "license": "MIT",
- "dependencies": {
- "@types/expect": "^1.20.4",
- "@types/node": "*"
- }
- },
- "node_modules/abbrev": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz",
- "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==",
- "license": "ISC",
- "engines": {
- "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
- }
- },
- "node_modules/accord": {
- "version": "0.29.0",
- "resolved": "https://registry.npmjs.org/accord/-/accord-0.29.0.tgz",
- "integrity": "sha512-3OOR92FTc2p5/EcOzPcXp+Cbo+3C15nV9RXHlOUBCBpHhcB+0frbSNR9ehED/o7sTcyGVtqGJpguToEdlXhD0w==",
- "license": "MIT",
- "dependencies": {
- "convert-source-map": "^1.5.0",
- "glob": "^7.0.5",
- "indx": "^0.2.3",
- "lodash.clone": "^4.3.2",
- "lodash.defaults": "^4.0.1",
- "lodash.flatten": "^4.2.0",
- "lodash.merge": "^4.4.0",
- "lodash.partialright": "^4.1.4",
- "lodash.pick": "^4.2.1",
- "lodash.uniq": "^4.3.0",
- "resolve": "^1.5.0",
- "semver": "^5.3.0",
- "uglify-js": "^2.8.22",
- "when": "^3.7.8"
- }
- },
- "node_modules/accord/node_modules/lodash.defaults": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
- "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
- "license": "MIT"
- },
- "node_modules/align-text": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
- "integrity": "sha512-GrTZLRpmp6wIC2ztrWW9MjjTgSKccffgFagbNDOX95/dcjEcYZibYTeaOntySQLcdw1ztBoFkviiUvTMbb9MYg==",
- "license": "MIT",
- "dependencies": {
- "kind-of": "^3.0.2",
- "longest": "^1.0.1",
- "repeat-string": "^1.5.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/align-text/node_modules/kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "license": "MIT",
- "dependencies": {
- "is-buffer": "^1.1.5"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ansi-colors": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
- "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
- "license": "MIT",
- "dependencies": {
- "ansi-wrap": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ansi-cyan": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz",
- "integrity": "sha512-eCjan3AVo/SxZ0/MyIYRtkpxIu/H3xZN7URr1vXVrISxeyz8fUFz0FJziamK4sS8I+t35y4rHg1b2PklyBe/7A==",
- "license": "MIT",
- "dependencies": {
- "ansi-wrap": "0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ansi-escapes": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
- "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/ansi-gray": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
- "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==",
- "license": "MIT",
- "dependencies": {
- "ansi-wrap": "0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ansi-red": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz",
- "integrity": "sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow==",
- "license": "MIT",
- "dependencies": {
- "ansi-wrap": "0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ansi-styles": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
- "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ansi-wrap": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
- "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/any-shell-escape": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/any-shell-escape/-/any-shell-escape-0.1.1.tgz",
- "integrity": "sha512-36j4l5HVkboyRhIWgtMh1I9i8LTdFqVwDEHy1cp+QioJyKgAUG40X0W8s7jakWRta/Sjvm8mUG1fU6Tj8mWagQ==",
- "license": "MIT"
- },
- "node_modules/anymatch": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
- "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
- "license": "ISC",
- "dependencies": {
- "micromatch": "^3.1.4",
- "normalize-path": "^2.1.1"
- }
- },
- "node_modules/anymatch/node_modules/normalize-path": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
- "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
- "license": "MIT",
- "dependencies": {
- "remove-trailing-separator": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/append-buffer": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz",
- "integrity": "sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==",
- "license": "MIT",
- "dependencies": {
- "buffer-equal": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/archy": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
- "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==",
- "license": "MIT"
- },
- "node_modules/argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "license": "MIT",
- "dependencies": {
- "sprintf-js": "~1.0.2"
- }
- },
- "node_modules/arr-diff": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
- "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/arr-filter": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz",
- "integrity": "sha512-A2BETWCqhsecSvCkWAeVBFLH6sXEUGASuzkpjL3GR1SlL/PWL6M3J8EAAld2Uubmh39tvkJTqC9LeLHCUKmFXA==",
- "license": "MIT",
- "dependencies": {
- "make-iterator": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/arr-flatten": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
- "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/arr-map": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz",
- "integrity": "sha512-tVqVTHt+Q5Xb09qRkbu+DidW1yYzz5izWS2Xm2yFm7qJnmUfz4HPzNxbHkdRJbz2lrqI7S+z17xNYdFcBBO8Hw==",
- "license": "MIT",
- "dependencies": {
- "make-iterator": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/arr-union": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
- "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/array-differ": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz",
- "integrity": "sha512-LeZY+DZDRnvP7eMuQ6LHfCzUGxAAIViUBliK24P3hWXL6y4SortgR6Nim6xrkfSLlmH0+k+9NYNwVC2s53ZrYQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/array-each": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
- "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/array-initial": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz",
- "integrity": "sha512-BC4Yl89vneCYfpLrs5JU2aAu9/a+xWbeKhvISg9PT7eWFB9UlRvI+rKEtk6mgxWr3dSkk9gQ8hCrdqt06NXPdw==",
- "license": "MIT",
- "dependencies": {
- "array-slice": "^1.0.0",
- "is-number": "^4.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/array-initial/node_modules/is-number": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
- "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/array-last": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz",
- "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==",
- "license": "MIT",
- "dependencies": {
- "is-number": "^4.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/array-last/node_modules/is-number": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
- "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/array-slice": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
- "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/array-sort": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz",
- "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==",
- "license": "MIT",
- "dependencies": {
- "default-compare": "^1.0.0",
- "get-value": "^2.0.6",
- "kind-of": "^5.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/array-union": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
- "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
- "license": "MIT",
- "dependencies": {
- "array-uniq": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/array-uniq": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
- "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/array-unique": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
- "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/assign-symbols": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
- "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/async": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
- "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==",
- "license": "MIT"
- },
- "node_modules/async-done": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz",
- "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==",
- "license": "MIT",
- "dependencies": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.2",
- "process-nextick-args": "^2.0.0",
- "stream-exhaust": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/async-each": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz",
- "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==",
- "funding": [
- {
- "type": "individual",
- "url": "https://paulmillr.com/funding/"
- }
- ],
- "license": "MIT"
- },
- "node_modules/async-settle": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz",
- "integrity": "sha512-VPXfB4Vk49z1LHHodrEQ6Xf7W4gg1w0dAPROHngx7qgDjqmIQ+fXmwgGXTW/ITLai0YLSvWepJOP9EVpMnEAcw==",
- "license": "MIT",
- "dependencies": {
- "async-done": "^1.2.2"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/atob": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
- "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
- "license": "(MIT OR Apache-2.0)",
- "bin": {
- "atob": "bin/atob.js"
- },
- "engines": {
- "node": ">= 4.5.0"
- }
- },
- "node_modules/atob-lite": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz",
- "integrity": "sha512-LEeSAWeh2Gfa2FtlQE1shxQ8zi5F9GHarrGKz08TMdODD5T4eH6BMsvtnhbWZ+XQn+Gb6om/917ucvRu7l7ukw==",
- "license": "MIT"
- },
- "node_modules/autoprefixer": {
- "version": "9.8.8",
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz",
- "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==",
- "license": "MIT",
- "dependencies": {
- "browserslist": "^4.12.0",
- "caniuse-lite": "^1.0.30001109",
- "normalize-range": "^0.1.2",
- "num2fraction": "^1.2.2",
- "picocolors": "^0.2.1",
- "postcss": "^7.0.32",
- "postcss-value-parser": "^4.1.0"
- },
- "bin": {
- "autoprefixer": "bin/autoprefixer"
- },
- "funding": {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/autoprefixer"
- }
- },
- "node_modules/bach": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz",
- "integrity": "sha512-bZOOfCb3gXBXbTFXq3OZtGR88LwGeJvzu6szttaIzymOTS4ZttBNOWSv7aLZja2EMycKtRYV0Oa8SNKH/zkxvg==",
- "license": "MIT",
- "dependencies": {
- "arr-filter": "^1.1.1",
- "arr-flatten": "^1.0.1",
- "arr-map": "^2.0.0",
- "array-each": "^1.0.0",
- "array-initial": "^1.0.0",
- "array-last": "^1.1.1",
- "async-done": "^1.2.2",
- "async-settle": "^1.0.0",
- "now-and-later": "^2.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "license": "MIT"
- },
- "node_modules/base": {
- "version": "0.11.2",
- "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
- "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
- "license": "MIT",
- "dependencies": {
- "cache-base": "^1.0.1",
- "class-utils": "^0.3.5",
- "component-emitter": "^1.2.1",
- "define-property": "^1.0.0",
- "isobject": "^3.0.1",
- "mixin-deep": "^1.2.0",
- "pascalcase": "^0.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/base/node_modules/define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/beeper": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz",
- "integrity": "sha512-3vqtKL1N45I5dV0RdssXZG7X6pCqQrWPNOlBPZPrd+QkE2HEhR57Z04m0KtpbsZH73j+a3F8UD1TQnn+ExTvIA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/before-after-hook": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
- "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
- "license": "Apache-2.0"
- },
- "node_modules/better-console": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/better-console/-/better-console-1.0.1.tgz",
- "integrity": "sha512-M/azU25cj3ZHbMSoXEroDfzcolfUvM03PZw5EEBk9T3tqdIYfMXrIkEKb9q8OZMC8Hic8Q9l8jk6TZq9cyRrcw==",
- "license": "BSD",
- "dependencies": {
- "chalk": "^1.1.3",
- "cli-table": "~0.3.1"
- }
- },
- "node_modules/binary-extensions": {
- "version": "1.13.1",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
- "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/binaryextensions": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.3.0.tgz",
- "integrity": "sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.8"
- },
- "funding": {
- "url": "https://bevry.me/fund"
- }
- },
- "node_modules/bindings": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
- "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "file-uri-to-path": "1.0.0"
- }
- },
- "node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/braces": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
- "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
- "license": "MIT",
- "dependencies": {
- "arr-flatten": "^1.1.0",
- "array-unique": "^0.3.2",
- "extend-shallow": "^2.0.1",
- "fill-range": "^4.0.0",
- "isobject": "^3.0.1",
- "repeat-element": "^1.1.2",
- "snapdragon": "^0.8.1",
- "snapdragon-node": "^2.0.1",
- "split-string": "^3.0.2",
- "to-regex": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/browserslist": {
- "version": "4.24.2",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz",
- "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "caniuse-lite": "^1.0.30001669",
- "electron-to-chromium": "^1.5.41",
- "node-releases": "^2.0.18",
- "update-browserslist-db": "^1.1.1"
- },
- "bin": {
- "browserslist": "cli.js"
- },
- "engines": {
- "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
- }
- },
- "node_modules/btoa-lite": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz",
- "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==",
- "license": "MIT"
- },
- "node_modules/buffer-equal": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz",
- "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "license": "MIT"
- },
- "node_modules/cache-base": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
- "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
- "license": "MIT",
- "dependencies": {
- "collection-visit": "^1.0.0",
- "component-emitter": "^1.2.1",
- "get-value": "^2.0.6",
- "has-value": "^1.0.0",
- "isobject": "^3.0.1",
- "set-value": "^2.0.0",
- "to-object-path": "^0.3.0",
- "union-value": "^1.0.0",
- "unset-value": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/call-bind": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
- "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
- "license": "MIT",
- "dependencies": {
- "es-define-property": "^1.0.0",
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.4",
- "set-function-length": "^1.2.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/camelcase": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
- "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/caniuse-lite": {
- "version": "1.0.30001684",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz",
- "integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "CC-BY-4.0"
- },
- "node_modules/center-align": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
- "integrity": "sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ==",
- "license": "MIT",
- "dependencies": {
- "align-text": "^0.1.3",
- "lazy-cache": "^1.0.3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^2.2.1",
- "escape-string-regexp": "^1.0.2",
- "has-ansi": "^2.0.0",
- "strip-ansi": "^3.0.0",
- "supports-color": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/chardet": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
- "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
- "license": "MIT"
- },
- "node_modules/chokidar": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
- "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
- "license": "MIT",
- "dependencies": {
- "anymatch": "^2.0.0",
- "async-each": "^1.0.1",
- "braces": "^2.3.2",
- "glob-parent": "^3.1.0",
- "inherits": "^2.0.3",
- "is-binary-path": "^1.0.0",
- "is-glob": "^4.0.0",
- "normalize-path": "^3.0.0",
- "path-is-absolute": "^1.0.0",
- "readdirp": "^2.2.1",
- "upath": "^1.1.1"
- },
- "optionalDependencies": {
- "fsevents": "^1.2.7"
- }
- },
- "node_modules/class-utils": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
- "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
- "license": "MIT",
- "dependencies": {
- "arr-union": "^3.1.0",
- "define-property": "^0.2.5",
- "isobject": "^3.0.0",
- "static-extend": "^0.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/class-utils/node_modules/define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/class-utils/node_modules/is-descriptor": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz",
- "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==",
- "license": "MIT",
- "dependencies": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/clean-css": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
- "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
- "license": "MIT",
- "dependencies": {
- "source-map": "~0.6.0"
- },
- "engines": {
- "node": ">= 4.0"
- }
- },
- "node_modules/cli-cursor": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
- "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==",
- "license": "MIT",
- "dependencies": {
- "restore-cursor": "^2.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/cli-table": {
- "version": "0.3.11",
- "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz",
- "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==",
- "dependencies": {
- "colors": "1.0.3"
- },
- "engines": {
- "node": ">= 0.2.0"
- }
- },
- "node_modules/cli-width": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
- "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==",
- "license": "ISC"
- },
- "node_modules/cliui": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
- "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==",
- "license": "ISC",
- "dependencies": {
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wrap-ansi": "^2.0.0"
- }
- },
- "node_modules/cliui/node_modules/is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
- "license": "MIT",
- "dependencies": {
- "number-is-nan": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/cliui/node_modules/string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
- "license": "MIT",
- "dependencies": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/clone": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
- "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
- "license": "MIT",
- "engines": {
- "node": ">=0.8"
- }
- },
- "node_modules/clone-buffer": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz",
- "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/clone-stats": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz",
- "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==",
- "license": "MIT"
- },
- "node_modules/cloneable-readable": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz",
- "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==",
- "license": "MIT",
- "dependencies": {
- "inherits": "^2.0.1",
- "process-nextick-args": "^2.0.0",
- "readable-stream": "^2.3.5"
- }
- },
- "node_modules/code-point-at": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
- "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/collection-map": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz",
- "integrity": "sha512-5D2XXSpkOnleOI21TG7p3T0bGAsZ/XknZpKBmGYyluO8pw4zA3K8ZlrBIbC4FXg3m6z/RNFiUFfT2sQK01+UHA==",
- "license": "MIT",
- "dependencies": {
- "arr-map": "^2.0.2",
- "for-own": "^1.0.0",
- "make-iterator": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/collection-visit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
- "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
- "license": "MIT",
- "dependencies": {
- "map-visit": "^1.0.0",
- "object-visit": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "license": "MIT",
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "license": "MIT"
- },
- "node_modules/color-support": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
- "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
- "license": "ISC",
- "bin": {
- "color-support": "bin.js"
- }
- },
- "node_modules/colors": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
- "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==",
- "license": "MIT",
- "engines": {
- "node": ">=0.1.90"
- }
- },
- "node_modules/commander": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
- "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
- "license": "MIT",
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/component-emitter": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
- "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "license": "MIT"
- },
- "node_modules/concat-stream": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
- "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
- "engines": [
- "node >= 0.8"
- ],
- "license": "MIT",
- "dependencies": {
- "buffer-from": "^1.0.0",
- "inherits": "^2.0.3",
- "readable-stream": "^2.2.2",
- "typedarray": "^0.0.6"
- }
- },
- "node_modules/concat-with-sourcemaps": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz",
- "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==",
- "license": "ISC",
- "dependencies": {
- "source-map": "^0.6.1"
- }
- },
- "node_modules/config-chain": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
- "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
- "license": "MIT",
- "dependencies": {
- "ini": "^1.3.4",
- "proto-list": "~1.2.1"
- }
- },
- "node_modules/convert-source-map": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
- "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
- "license": "MIT"
- },
- "node_modules/copy-anything": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
- "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
- "license": "MIT",
- "dependencies": {
- "is-what": "^3.14.1"
- },
- "funding": {
- "url": "https://github.com/sponsors/mesqueeb"
- }
- },
- "node_modules/copy-descriptor": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
- "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/copy-props": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.5.tgz",
- "integrity": "sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw==",
- "license": "MIT",
- "dependencies": {
- "each-props": "^1.3.2",
- "is-plain-object": "^5.0.0"
- }
- },
- "node_modules/core-util-is": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
- "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
- "license": "MIT"
- },
- "node_modules/cross-spawn": {
- "version": "6.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz",
- "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==",
- "license": "MIT",
- "dependencies": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- },
- "engines": {
- "node": ">=4.8"
- }
- },
- "node_modules/css": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
- "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==",
- "license": "MIT",
- "dependencies": {
- "inherits": "^2.0.3",
- "source-map": "^0.6.1",
- "source-map-resolve": "^0.5.2",
- "urix": "^0.1.0"
- }
- },
- "node_modules/d": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz",
- "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==",
- "license": "ISC",
- "dependencies": {
- "es5-ext": "^0.10.64",
- "type": "^2.7.2"
- },
- "engines": {
- "node": ">=0.12"
- }
- },
- "node_modules/dateformat": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz",
- "integrity": "sha512-GODcnWq3YGoTnygPfi02ygEiRxqUxpJwuRHjdhJYuxpcZmDq4rjBiXYmbCCzStxo176ixfLT6i4NPwQooRySnw==",
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "license": "MIT",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/decode-uri-component": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
- "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/deep-assign": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/deep-assign/-/deep-assign-1.0.0.tgz",
- "integrity": "sha512-iAL1PDjxqhANx86VhUjK0HSb4bozMfJUK64rxdrlWPCgMv7rBvP6AFySY69e+k8JAtPHNWoTsQT5OJvE+Jgpjg==",
- "license": "MIT",
- "dependencies": {
- "is-obj": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/deepmerge": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
- "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/default-compare": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz",
- "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==",
- "license": "MIT",
- "dependencies": {
- "kind-of": "^5.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/default-resolution": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz",
- "integrity": "sha512-2xaP6GiwVwOEbXCGoJ4ufgC76m8cj805jrghScewJC2ZDsb9U0b4BIrba+xt/Uytyd0HvQ6+WymSRTfnYj59GQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/define-data-property": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
- "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
- "license": "MIT",
- "dependencies": {
- "es-define-property": "^1.0.0",
- "es-errors": "^1.3.0",
- "gopd": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/define-properties": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
- "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
- "license": "MIT",
- "dependencies": {
- "define-data-property": "^1.0.1",
- "has-property-descriptors": "^1.0.0",
- "object-keys": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/define-property": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
- "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^1.0.2",
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/del": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz",
- "integrity": "sha512-7yjqSoVSlJzA4t/VUwazuEagGeANEKB3f/aNI//06pfKgwoCb7f6Q1gETN1sZzYaj6chTQ0AhIwDiPdfOjko4A==",
- "license": "MIT",
- "dependencies": {
- "globby": "^6.1.0",
- "is-path-cwd": "^1.0.0",
- "is-path-in-cwd": "^1.0.0",
- "p-map": "^1.1.1",
- "pify": "^3.0.0",
- "rimraf": "^2.2.8"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/deprecation": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
- "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==",
- "license": "ISC"
- },
- "node_modules/detect-file": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
- "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/detect-indent": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
- "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/diff": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.8.tgz",
- "integrity": "sha512-1zEb73vemXFpUmfh3fsta4YHz3lwebxXvaWmPbFv9apujQBWDnkrPDLXLQs1gZo4RCWMDsT89r0Pf/z8/02TGA==",
- "engines": {
- "node": ">=0.3.1"
- }
- },
- "node_modules/duplexer2": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz",
- "integrity": "sha512-+AWBwjGadtksxjOQSFDhPNQbed7icNXApT4+2BNpsXzcCBiInq2H9XW0O8sfHFaPmnQRs7cg/P0fAr2IWQSW0g==",
- "license": "BSD",
- "dependencies": {
- "readable-stream": "~1.1.9"
- }
- },
- "node_modules/duplexer2/node_modules/isarray": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
- "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
- "license": "MIT"
- },
- "node_modules/duplexer2/node_modules/readable-stream": {
- "version": "1.1.14",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
- "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
- "license": "MIT",
- "dependencies": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.1",
- "isarray": "0.0.1",
- "string_decoder": "~0.10.x"
- }
- },
- "node_modules/duplexer2/node_modules/string_decoder": {
- "version": "0.10.31",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
- "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
- "license": "MIT"
- },
- "node_modules/duplexify": {
- "version": "3.7.1",
- "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
- "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
- "license": "MIT",
- "dependencies": {
- "end-of-stream": "^1.0.0",
- "inherits": "^2.0.1",
- "readable-stream": "^2.0.0",
- "stream-shift": "^1.0.0"
- }
- },
- "node_modules/each-props": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz",
- "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==",
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.1",
- "object.defaults": "^1.1.0"
- }
- },
- "node_modules/each-props/node_modules/is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/eastasianwidth": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
- "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
- "license": "MIT"
- },
- "node_modules/editorconfig": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz",
- "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==",
- "license": "MIT",
- "dependencies": {
- "@one-ini/wasm": "0.1.1",
- "commander": "^10.0.0",
- "minimatch": "9.0.1",
- "semver": "^7.5.3"
- },
- "bin": {
- "editorconfig": "bin/editorconfig"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/editorconfig/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/editorconfig/node_modules/minimatch": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz",
- "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==",
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/editorconfig/node_modules/semver": {
- "version": "7.6.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
- "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/electron-to-chromium": {
- "version": "1.5.65",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.65.tgz",
- "integrity": "sha512-PWVzBjghx7/wop6n22vS2MLU8tKGd4Q91aCEGhG/TYmW6PP5OcSXcdnxTe1NNt0T66N8D6jxh4kC8UsdzOGaIw==",
- "license": "ISC"
- },
- "node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT"
- },
- "node_modules/end-of-stream": {
- "version": "1.4.4",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
- "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
- "license": "MIT",
- "dependencies": {
- "once": "^1.4.0"
- }
- },
- "node_modules/errno": {
- "version": "0.1.8",
- "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
- "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "prr": "~1.0.1"
- },
- "bin": {
- "errno": "cli.js"
- }
- },
- "node_modules/error-ex": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
- "license": "MIT",
- "dependencies": {
- "is-arrayish": "^0.2.1"
- }
- },
- "node_modules/es-define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
- "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
- "license": "MIT",
- "dependencies": {
- "get-intrinsic": "^1.2.4"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-errors": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es5-ext": {
- "version": "0.10.64",
- "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
- "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
- "hasInstallScript": true,
- "license": "ISC",
- "dependencies": {
- "es6-iterator": "^2.0.3",
- "es6-symbol": "^3.1.3",
- "esniff": "^2.0.1",
- "next-tick": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/es6-iterator": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
- "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
- "license": "MIT",
- "dependencies": {
- "d": "1",
- "es5-ext": "^0.10.35",
- "es6-symbol": "^3.1.1"
- }
- },
- "node_modules/es6-symbol": {
- "version": "3.1.4",
- "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz",
- "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==",
- "license": "ISC",
- "dependencies": {
- "d": "^1.0.2",
- "ext": "^1.7.0"
- },
- "engines": {
- "node": ">=0.12"
- }
- },
- "node_modules/es6-weak-map": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz",
- "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==",
- "license": "ISC",
- "dependencies": {
- "d": "1",
- "es5-ext": "^0.10.46",
- "es6-iterator": "^2.0.3",
- "es6-symbol": "^3.1.1"
- }
- },
- "node_modules/escalade": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
- "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/esniff": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
- "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
- "license": "ISC",
- "dependencies": {
- "d": "^1.0.1",
- "es5-ext": "^0.10.62",
- "event-emitter": "^0.3.5",
- "type": "^2.7.2"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/event-emitter": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
- "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
- "license": "MIT",
- "dependencies": {
- "d": "1",
- "es5-ext": "~0.10.14"
- }
- },
- "node_modules/execa": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
- "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
- "license": "MIT",
- "dependencies": {
- "cross-spawn": "^6.0.0",
- "get-stream": "^4.0.0",
- "is-stream": "^1.1.0",
- "npm-run-path": "^2.0.0",
- "p-finally": "^1.0.0",
- "signal-exit": "^3.0.0",
- "strip-eof": "^1.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/expand-brackets": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
- "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
- "license": "MIT",
- "dependencies": {
- "debug": "^2.3.3",
- "define-property": "^0.2.5",
- "extend-shallow": "^2.0.1",
- "posix-character-classes": "^0.1.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/expand-brackets/node_modules/define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/expand-brackets/node_modules/is-descriptor": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz",
- "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==",
- "license": "MIT",
- "dependencies": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/expand-tilde": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
- "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==",
- "license": "MIT",
- "dependencies": {
- "homedir-polyfill": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ext": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
- "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
- "license": "ISC",
- "dependencies": {
- "type": "^2.7.2"
- }
- },
- "node_modules/extend": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
- "license": "MIT"
- },
- "node_modules/extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "license": "MIT",
- "dependencies": {
- "is-extendable": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/external-editor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
- "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
- "license": "MIT",
- "dependencies": {
- "chardet": "^0.7.0",
- "iconv-lite": "^0.4.24",
- "tmp": "^0.0.33"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/extglob": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
- "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
- "license": "MIT",
- "dependencies": {
- "array-unique": "^0.3.2",
- "define-property": "^1.0.0",
- "expand-brackets": "^2.1.4",
- "extend-shallow": "^2.0.1",
- "fragment-cache": "^0.2.1",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/extglob/node_modules/define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/fancy-log": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz",
- "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==",
- "license": "MIT",
- "dependencies": {
- "ansi-gray": "^0.1.1",
- "color-support": "^1.1.3",
- "parse-node-version": "^1.0.0",
- "time-stamp": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/fast-levenshtein": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz",
- "integrity": "sha512-Ia0sQNrMPXXkqVFt6w6M1n1oKo3NfKs+mvaV811Jwir7vAk9a6PVV9VPYf6X3BU97QiLEmuW3uXH9u87zDFfdw==",
- "license": "MIT"
- },
- "node_modules/figures": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
- "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==",
- "license": "MIT",
- "dependencies": {
- "escape-string-regexp": "^1.0.5"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/file-uri-to-path": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
- "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/fill-range": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
- "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
- "license": "MIT",
- "dependencies": {
- "extend-shallow": "^2.0.1",
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1",
- "to-regex-range": "^2.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/find-up": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
- "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==",
- "license": "MIT",
- "dependencies": {
- "path-exists": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/findup-sync": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz",
- "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==",
- "license": "MIT",
- "dependencies": {
- "detect-file": "^1.0.0",
- "is-glob": "^4.0.0",
- "micromatch": "^3.0.4",
- "resolve-dir": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/fined": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz",
- "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==",
- "license": "MIT",
- "dependencies": {
- "expand-tilde": "^2.0.2",
- "is-plain-object": "^2.0.3",
- "object.defaults": "^1.1.0",
- "object.pick": "^1.2.0",
- "parse-filepath": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/fined/node_modules/is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/first-chunk-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz",
- "integrity": "sha512-X8Z+b/0L4lToKYq+lwnKqi9X/Zek0NibLpsJgVsSxpoYq7JtiCtRb5HqKVEjEw/qAb/4AKKRLOwwKHlWNpm2Eg==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "^2.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/flagged-respawn": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz",
- "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/flush-write-stream": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
- "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
- "license": "MIT",
- "dependencies": {
- "inherits": "^2.0.3",
- "readable-stream": "^2.3.6"
- }
- },
- "node_modules/fomantic-ui": {
- "version": "2.8.7",
- "resolved": "https://registry.npmjs.org/fomantic-ui/-/fomantic-ui-2.8.7.tgz",
- "integrity": "sha512-u22d28Z+U8mduTIM50MYzBGRz7CXYjGs2fUY6KO8N3enE8OAatDOXV4Mb/Xvj/ck5aNE6er6XJNK1fFWXt/u/w==",
- "hasInstallScript": true,
- "license": "MIT",
- "dependencies": {
- "@octokit/rest": "^16.16.0",
- "better-console": "1.0.1",
- "del": "^3.0.0",
- "extend": "^3.0.2",
- "gulp": "^4.0.0",
- "gulp-autoprefixer": "^6.0.0",
- "gulp-chmod": "^2.0.0",
- "gulp-clean-css": "^3.10.0",
- "gulp-clone": "^2.0.1",
- "gulp-concat": "^2.6.1",
- "gulp-concat-css": "^3.1.0",
- "gulp-concat-filenames": "^1.2.0",
- "gulp-copy": "^4.0.0",
- "gulp-debug": "^4.0.0",
- "gulp-dedupe": "0.0.2",
- "gulp-flatten": "^0.4.0",
- "gulp-git": "^2.9.0",
- "gulp-header": "^2.0.5",
- "gulp-if": "^2.0.2",
- "gulp-json-editor": "^2.4.3",
- "gulp-less": "^4.0.1",
- "gulp-notify": "^3.0.0",
- "gulp-plumber": "^1.1.0",
- "gulp-print": "^5.0.0",
- "gulp-rename": "^1.4.0",
- "gulp-replace": "^1.0.0",
- "gulp-rtlcss": "^1.3.0",
- "gulp-tap": "^1.0.1",
- "gulp-uglify": "^3.0.1",
- "inquirer": "^6.2.1",
- "jquery": "^3.4.0",
- "less": "^3.7.0",
- "map-stream": "^0.1.0",
- "merge-stream": "^2.0.0",
- "mkdirp": "^0.5.1",
- "normalize-path": "^3.0.0",
- "replace-ext": "^1.0.0",
- "require-dot-file": "^0.4.0",
- "wrench-sui": "^0.0.3",
- "yamljs": "^0.3.0"
- },
- "engines": {
- "node": ">=10.15.3",
- "npm": ">=6.4.1"
- }
- },
- "node_modules/for-in": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
- "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/for-own": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
- "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==",
- "license": "MIT",
- "dependencies": {
- "for-in": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/foreground-child": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
- "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
- "license": "ISC",
- "dependencies": {
- "cross-spawn": "^7.0.0",
- "signal-exit": "^4.0.1"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/foreground-child/node_modules/cross-spawn": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
- "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "license": "MIT",
- "dependencies": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/foreground-child/node_modules/path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/foreground-child/node_modules/shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "license": "MIT",
- "dependencies": {
- "shebang-regex": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/foreground-child/node_modules/shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/foreground-child/node_modules/signal-exit": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
- "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
- "license": "ISC",
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/foreground-child/node_modules/which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "license": "ISC",
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "node-which": "bin/node-which"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/fork-stream": {
- "version": "0.0.4",
- "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz",
- "integrity": "sha512-Pqq5NnT78ehvUnAk/We/Jr22vSvanRlFTpAmQ88xBY/M1TlHe+P0ILuEyXS595ysdGfaj22634LBkGMA2GTcpA==",
- "license": "BSD"
- },
- "node_modules/fragment-cache": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
- "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
- "license": "MIT",
- "dependencies": {
- "map-cache": "^0.2.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/fs-mkdirp-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz",
- "integrity": "sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==",
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.1.11",
- "through2": "^2.0.3"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/fs-mkdirp-stream/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "license": "ISC"
- },
- "node_modules/fsevents": {
- "version": "1.2.13",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
- "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
- "deprecated": "Upgrade to fsevents v2 to mitigate potential security issues",
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "dependencies": {
- "bindings": "^1.5.0",
- "nan": "^2.12.1"
- },
- "engines": {
- "node": ">= 4.0"
- }
- },
- "node_modules/function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/get-caller-file": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
- "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
- "license": "ISC"
- },
- "node_modules/get-imports": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/get-imports/-/get-imports-1.0.0.tgz",
- "integrity": "sha512-9FjKG2Os+o/EuOIh3B/LNMbU2FWPGHVy/gs9TJpytK95IPl7lLqiu+VAU7JX6VZimqdmpLemgsGMdQWdKvqYGQ==",
- "license": "MIT",
- "dependencies": {
- "array-uniq": "^1.0.1",
- "import-regex": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/get-intrinsic": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
- "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2",
- "has-proto": "^1.0.1",
- "has-symbols": "^1.0.3",
- "hasown": "^2.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/get-own-enumerable-property-symbols": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
- "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==",
- "license": "ISC"
- },
- "node_modules/get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "license": "MIT",
- "dependencies": {
- "pump": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/get-stream/node_modules/pump": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
- "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
- "license": "MIT",
- "dependencies": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
- "node_modules/get-value": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
- "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
- "license": "ISC",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/glob-parent": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
- "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
- "license": "ISC",
- "dependencies": {
- "is-glob": "^3.1.0",
- "path-dirname": "^1.0.0"
- }
- },
- "node_modules/glob-parent/node_modules/is-glob": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
- "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
- "license": "MIT",
- "dependencies": {
- "is-extglob": "^2.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/glob-stream": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz",
- "integrity": "sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==",
- "license": "MIT",
- "dependencies": {
- "extend": "^3.0.0",
- "glob": "^7.1.1",
- "glob-parent": "^3.1.0",
- "is-negated-glob": "^1.0.0",
- "ordered-read-streams": "^1.0.0",
- "pumpify": "^1.3.5",
- "readable-stream": "^2.1.5",
- "remove-trailing-separator": "^1.0.1",
- "to-absolute-glob": "^2.0.0",
- "unique-stream": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/glob-watcher": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz",
- "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==",
- "license": "MIT",
- "dependencies": {
- "anymatch": "^2.0.0",
- "async-done": "^1.2.0",
- "chokidar": "^2.0.0",
- "is-negated-glob": "^1.0.0",
- "just-debounce": "^1.0.0",
- "normalize-path": "^3.0.0",
- "object.defaults": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/global-modules": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
- "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
- "license": "MIT",
- "dependencies": {
- "global-prefix": "^1.0.1",
- "is-windows": "^1.0.1",
- "resolve-dir": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/global-prefix": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
- "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==",
- "license": "MIT",
- "dependencies": {
- "expand-tilde": "^2.0.2",
- "homedir-polyfill": "^1.0.1",
- "ini": "^1.3.4",
- "is-windows": "^1.0.1",
- "which": "^1.2.14"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/globby": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
- "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==",
- "license": "MIT",
- "dependencies": {
- "array-union": "^1.0.1",
- "glob": "^7.0.3",
- "object-assign": "^4.0.1",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/globby/node_modules/pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/glogg": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz",
- "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==",
- "license": "MIT",
- "dependencies": {
- "sparkles": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/gopd": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
- "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
- "license": "MIT",
- "dependencies": {
- "get-intrinsic": "^1.1.3"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
- "license": "ISC"
- },
- "node_modules/growly": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
- "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==",
- "license": "MIT"
- },
- "node_modules/gulp": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz",
- "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==",
- "license": "MIT",
- "dependencies": {
- "glob-watcher": "^5.0.3",
- "gulp-cli": "^2.2.0",
- "undertaker": "^1.2.1",
- "vinyl-fs": "^3.0.0"
- },
- "bin": {
- "gulp": "bin/gulp.js"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/gulp-autoprefixer": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/gulp-autoprefixer/-/gulp-autoprefixer-6.1.0.tgz",
- "integrity": "sha512-Ti/BUFe+ekhbDJfspZIMiOsOvw51KhI9EncsDfK7NaxjqRm+v4xS9v99kPxEoiDavpWqQWvG8Y6xT1mMlB3aXA==",
- "license": "MIT",
- "dependencies": {
- "autoprefixer": "^9.5.1",
- "fancy-log": "^1.3.2",
- "plugin-error": "^1.0.1",
- "postcss": "^7.0.2",
- "through2": "^3.0.1",
- "vinyl-sourcemaps-apply": "^0.2.1"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/gulp-chmod": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/gulp-chmod/-/gulp-chmod-2.0.0.tgz",
- "integrity": "sha512-ttOK11mugzcy6D5CQD8rXqS7M4Ecoo64bDNhRXT9Yok9ztAcOeIK8hsv7LlV1eFS4iSQKZETvEZC5Kt/sH74sw==",
- "license": "MIT",
- "dependencies": {
- "deep-assign": "^1.0.0",
- "stat-mode": "^0.2.0",
- "through2": "^2.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/gulp-chmod/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-clean-css": {
- "version": "3.10.0",
- "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-3.10.0.tgz",
- "integrity": "sha512-7Isf9Y690o/Q5MVjEylH1H7L8WeZ89woW7DnhD5unTintOdZb67KdOayRgp9trUFo+f9UyJtuatV42e/+kghPg==",
- "license": "MIT",
- "dependencies": {
- "clean-css": "4.2.1",
- "plugin-error": "1.0.1",
- "through2": "2.0.3",
- "vinyl-sourcemaps-apply": "0.2.1"
- }
- },
- "node_modules/gulp-clean-css/node_modules/through2": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
- "integrity": "sha512-tmNYYHFqXmaKSSlOU4ZbQ82cxmFQa5LRWKFtWCNkGIiZ3/VHmOffCeWfBRZZRyXAhNP9itVMR+cuvomBOPlm8g==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "^2.1.5",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-cli": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz",
- "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==",
- "license": "MIT",
- "dependencies": {
- "ansi-colors": "^1.0.1",
- "archy": "^1.0.0",
- "array-sort": "^1.0.0",
- "color-support": "^1.1.3",
- "concat-stream": "^1.6.0",
- "copy-props": "^2.0.1",
- "fancy-log": "^1.3.2",
- "gulplog": "^1.0.0",
- "interpret": "^1.4.0",
- "isobject": "^3.0.1",
- "liftoff": "^3.1.0",
- "matchdep": "^2.0.0",
- "mute-stdout": "^1.0.0",
- "pretty-hrtime": "^1.0.0",
- "replace-homedir": "^1.0.0",
- "semver-greatest-satisfied-range": "^1.1.0",
- "v8flags": "^3.2.0",
- "yargs": "^7.1.0"
- },
- "bin": {
- "gulp": "bin/gulp.js"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/gulp-clone": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/gulp-clone/-/gulp-clone-2.0.1.tgz",
- "integrity": "sha512-SLg/KsHBbinR/pCX3PF5l1YlR28hLp0X+bcpf77PtMJ6zvAQ5kRjtCPV5Wt1wHXsXWZN0eTUZ15R8ZYpi/CdCA==",
- "dependencies": {
- "plugin-error": "^0.1.2",
- "through2": "^2.0.3"
- }
- },
- "node_modules/gulp-clone/node_modules/arr-diff": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz",
- "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==",
- "license": "MIT",
- "dependencies": {
- "arr-flatten": "^1.0.1",
- "array-slice": "^0.2.3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-clone/node_modules/arr-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz",
- "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-clone/node_modules/array-slice": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
- "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-clone/node_modules/extend-shallow": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz",
- "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==",
- "license": "MIT",
- "dependencies": {
- "kind-of": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-clone/node_modules/kind-of": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
- "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-clone/node_modules/plugin-error": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz",
- "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==",
- "license": "MIT",
- "dependencies": {
- "ansi-cyan": "^0.1.1",
- "ansi-red": "^0.1.1",
- "arr-diff": "^1.0.1",
- "arr-union": "^2.0.1",
- "extend-shallow": "^1.1.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-clone/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-concat": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz",
- "integrity": "sha512-a2scActrQrDBpBbR3WUZGyGS1JEPLg5PZJdIa7/Bi3GuKAmPYDK6SFhy/NZq5R8KsKKFvtfR0fakbUCcKGCCjg==",
- "license": "MIT",
- "dependencies": {
- "concat-with-sourcemaps": "^1.0.0",
- "through2": "^2.0.0",
- "vinyl": "^2.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/gulp-concat-css": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/gulp-concat-css/-/gulp-concat-css-3.1.0.tgz",
- "integrity": "sha512-iLTBPS+cutlgLyK3bp9DMts+WuS8n2mQpjzQ7p/ZVQc8FO5fvpN+ntg9U6jsuNvPeuii82aKm8XeOzF0nUK+TA==",
- "dependencies": {
- "lodash.defaults": "^3.0.0",
- "parse-import": "^2.0.0",
- "plugin-error": "^0.1.2",
- "rework": "~1.0.0",
- "rework-import": "^2.0.0",
- "rework-plugin-url": "^1.0.1",
- "through2": "~1.1.1",
- "vinyl": "^2.1.0"
- }
- },
- "node_modules/gulp-concat-css/node_modules/arr-diff": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz",
- "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==",
- "license": "MIT",
- "dependencies": {
- "arr-flatten": "^1.0.1",
- "array-slice": "^0.2.3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-concat-css/node_modules/arr-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz",
- "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-concat-css/node_modules/array-slice": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
- "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-concat-css/node_modules/extend-shallow": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz",
- "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==",
- "license": "MIT",
- "dependencies": {
- "kind-of": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-concat-css/node_modules/isarray": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
- "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
- "license": "MIT"
- },
- "node_modules/gulp-concat-css/node_modules/kind-of": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
- "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-concat-css/node_modules/plugin-error": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz",
- "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==",
- "license": "MIT",
- "dependencies": {
- "ansi-cyan": "^0.1.1",
- "ansi-red": "^0.1.1",
- "arr-diff": "^1.0.1",
- "arr-union": "^2.0.1",
- "extend-shallow": "^1.1.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-concat-css/node_modules/readable-stream": {
- "version": "1.1.14",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
- "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
- "license": "MIT",
- "dependencies": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.1",
- "isarray": "0.0.1",
- "string_decoder": "~0.10.x"
- }
- },
- "node_modules/gulp-concat-css/node_modules/string_decoder": {
- "version": "0.10.31",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
- "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
- "license": "MIT"
- },
- "node_modules/gulp-concat-css/node_modules/through2": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/through2/-/through2-1.1.1.tgz",
- "integrity": "sha512-zEbpaeSMHxczpTzO1KkMHjBC1enTA68ojeaZGG4toqdASpb9t4xUZaYFBq2/9OHo5nTGFVSYd4c910OR+6wxbQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": ">=1.1.13-1 <1.2.0-0",
- "xtend": ">=4.0.0 <4.1.0-0"
- }
- },
- "node_modules/gulp-concat-filenames": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/gulp-concat-filenames/-/gulp-concat-filenames-1.2.0.tgz",
- "integrity": "sha512-2wHcntxftYa2kiv5QOaniSNQuRf1axHGqkyXhRoCBXAVvwzrUp++qW9GNSAdvb3h+7m8yC8Fu25guuaDU+1WaA==",
- "dependencies": {
- "gulp-util": "3.x.x",
- "through": "2.x.x"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/gulp-concat/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-copy": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/gulp-copy/-/gulp-copy-4.0.1.tgz",
- "integrity": "sha512-UbdAwmEiVNNv55KAiUYWOP6Za7h8JPHNNyekNx8Gyc5XRlpUzTrlEclps939nOeiDPsd6jUtT2LmfavJirbZQg==",
- "license": "MIT",
- "dependencies": {
- "gulp": "^4.0.0",
- "plugin-error": "^0.1.2",
- "through2": "^2.0.3"
- }
- },
- "node_modules/gulp-copy/node_modules/arr-diff": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz",
- "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==",
- "license": "MIT",
- "dependencies": {
- "arr-flatten": "^1.0.1",
- "array-slice": "^0.2.3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-copy/node_modules/arr-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz",
- "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-copy/node_modules/array-slice": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
- "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-copy/node_modules/extend-shallow": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz",
- "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==",
- "license": "MIT",
- "dependencies": {
- "kind-of": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-copy/node_modules/kind-of": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
- "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-copy/node_modules/plugin-error": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz",
- "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==",
- "license": "MIT",
- "dependencies": {
- "ansi-cyan": "^0.1.1",
- "ansi-red": "^0.1.1",
- "arr-diff": "^1.0.1",
- "arr-union": "^2.0.1",
- "extend-shallow": "^1.1.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-copy/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-debug": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/gulp-debug/-/gulp-debug-4.0.0.tgz",
- "integrity": "sha512-cn/GhMD2nVZCVxAl5vWao4/dcoZ8wUJ8w3oqTvQaGDmC1vT7swNOEbhQTWJp+/otKePT64aENcqAQXDcdj5H1g==",
- "license": "MIT",
- "dependencies": {
- "chalk": "^2.3.0",
- "fancy-log": "^1.3.2",
- "plur": "^3.0.0",
- "stringify-object": "^3.0.0",
- "through2": "^2.0.0",
- "tildify": "^1.1.2"
- },
- "engines": {
- "node": ">=6"
- },
- "peerDependencies": {
- "gulp": ">=4"
- }
- },
- "node_modules/gulp-debug/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "license": "MIT",
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/gulp-debug/node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/gulp-debug/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "license": "MIT",
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/gulp-debug/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-dedupe": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/gulp-dedupe/-/gulp-dedupe-0.0.2.tgz",
- "integrity": "sha512-Y+FZmAVHUYDgJiGneLXY2sCErvcY89sskjGQILhh5YvNGZq5M+pKsY54K0MyquZGxj2g10ZDVM5vQnEP7yUrVA==",
- "license": "MIT",
- "dependencies": {
- "colors": "~1.0.2",
- "diff": "~1.0.8",
- "gulp-util": "~3.0.1",
- "lodash.defaults": "~2.4.1",
- "through": "~2.3.6"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/gulp-dedupe/node_modules/lodash.defaults": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz",
- "integrity": "sha512-5wTIPWwGGr07JFysAZB8+7JB2NjJKXDIwogSaRX5zED85zyUAQwtOqUk8AsJkkigUcL3akbHYXd5+BPtTGQPZw==",
- "license": "MIT",
- "dependencies": {
- "lodash._objecttypes": "~2.4.1",
- "lodash.keys": "~2.4.1"
- }
- },
- "node_modules/gulp-dedupe/node_modules/lodash.keys": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz",
- "integrity": "sha512-ZpJhwvUXHSNL5wYd1RM6CUa2ZuqorG9ngoJ9Ix5Cce+uX7I5O/E06FCJdhSZ33b5dVyeQDnIlWH7B2s5uByZ7g==",
- "license": "MIT",
- "dependencies": {
- "lodash._isnative": "~2.4.1",
- "lodash._shimkeys": "~2.4.1",
- "lodash.isobject": "~2.4.1"
- }
- },
- "node_modules/gulp-flatten": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/gulp-flatten/-/gulp-flatten-0.4.0.tgz",
- "integrity": "sha512-eg4spVTAiv1xXmugyaCxWne1oPtNG0UHEtABx5W8ScLiqAYceyYm6GYA36x0Qh8KOIXmAZV97L2aYGnKREG3Sg==",
- "license": "MIT",
- "dependencies": {
- "plugin-error": "^0.1.2",
- "through2": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/gulp-flatten/node_modules/arr-diff": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz",
- "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==",
- "license": "MIT",
- "dependencies": {
- "arr-flatten": "^1.0.1",
- "array-slice": "^0.2.3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-flatten/node_modules/arr-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz",
- "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-flatten/node_modules/array-slice": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
- "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-flatten/node_modules/extend-shallow": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz",
- "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==",
- "license": "MIT",
- "dependencies": {
- "kind-of": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-flatten/node_modules/kind-of": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
- "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-flatten/node_modules/plugin-error": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz",
- "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==",
- "license": "MIT",
- "dependencies": {
- "ansi-cyan": "^0.1.1",
- "ansi-red": "^0.1.1",
- "arr-diff": "^1.0.1",
- "arr-union": "^2.0.1",
- "extend-shallow": "^1.1.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-flatten/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-git": {
- "version": "2.11.0",
- "resolved": "https://registry.npmjs.org/gulp-git/-/gulp-git-2.11.0.tgz",
- "integrity": "sha512-7YOcwin7sr68weYhBNOtZia3LZOGZWXgGcxxcxCi2hjljTgysOhH9mLTH2hdG5YLcuAFNg7mMbb2xIRfYsaQZw==",
- "license": "MIT",
- "dependencies": {
- "any-shell-escape": "^0.1.1",
- "fancy-log": "^1.3.2",
- "lodash": "^4.17.21",
- "plugin-error": "^1.0.1",
- "require-dir": "^1.0.0",
- "strip-bom-stream": "^3.0.0",
- "vinyl": "^2.0.1"
- },
- "engines": {
- "node": ">= 0.9.0"
- }
- },
- "node_modules/gulp-header": {
- "version": "2.0.9",
- "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-2.0.9.tgz",
- "integrity": "sha512-LMGiBx+qH8giwrOuuZXSGvswcIUh0OiioNkUpLhNyvaC6/Ga8X6cfAeme2L5PqsbXMhL8o8b/OmVqIQdxprhcQ==",
- "license": "MIT",
- "dependencies": {
- "concat-with-sourcemaps": "^1.1.0",
- "lodash.template": "^4.5.0",
- "map-stream": "0.0.7",
- "through2": "^2.0.0"
- }
- },
- "node_modules/gulp-header/node_modules/map-stream": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz",
- "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==",
- "license": "MIT"
- },
- "node_modules/gulp-header/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-if": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-2.0.2.tgz",
- "integrity": "sha512-tV0UfXkZodpFq6CYxEqH8tqLQgN6yR9qOhpEEN3O6N5Hfqk3fFLcbAavSex5EqnmoQjyaZ/zvgwclvlTI1KGfw==",
- "license": "MIT",
- "dependencies": {
- "gulp-match": "^1.0.3",
- "ternary-stream": "^2.0.1",
- "through2": "^2.0.1"
- },
- "engines": {
- "node": ">= 0.10.0"
- }
- },
- "node_modules/gulp-if/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-json-editor": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/gulp-json-editor/-/gulp-json-editor-2.6.0.tgz",
- "integrity": "sha512-Ni0ZUpNrhesHiTlHQth/Nv1rXCn0LUicEvzA5XuGy186C4PVeNoRjfuAIQrbmt3scKv8dgGbCs0hd77ScTw7hA==",
- "license": "MIT",
- "dependencies": {
- "deepmerge": "^4.3.1",
- "detect-indent": "^6.1.0",
- "js-beautify": "^1.14.11",
- "plugin-error": "^2.0.1",
- "through2": "^4.0.2"
- }
- },
- "node_modules/gulp-json-editor/node_modules/plugin-error": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-2.0.1.tgz",
- "integrity": "sha512-zMakqvIDyY40xHOvzXka0kUvf40nYIuwRE8dWhti2WtjQZ31xAgBZBhxsK7vK3QbRXS1Xms/LO7B5cuAsfB2Gg==",
- "license": "MIT",
- "dependencies": {
- "ansi-colors": "^1.0.1"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "node_modules/gulp-json-editor/node_modules/readable-stream": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
- "license": "MIT",
- "dependencies": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/gulp-json-editor/node_modules/through2": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
- "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "3"
- }
- },
- "node_modules/gulp-less": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/gulp-less/-/gulp-less-4.0.1.tgz",
- "integrity": "sha512-hmM2k0FfQp7Ptm3ZaqO2CkMX3hqpiIOn4OHtuSsCeFym63F7oWlEua5v6u1cIjVUKYsVIs9zPg9vbqTEb/udpA==",
- "license": "MIT",
- "dependencies": {
- "accord": "^0.29.0",
- "less": "2.6.x || ^3.7.1",
- "object-assign": "^4.0.1",
- "plugin-error": "^0.1.2",
- "replace-ext": "^1.0.0",
- "through2": "^2.0.0",
- "vinyl-sourcemaps-apply": "^0.2.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-less/node_modules/arr-diff": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz",
- "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==",
- "license": "MIT",
- "dependencies": {
- "arr-flatten": "^1.0.1",
- "array-slice": "^0.2.3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-less/node_modules/arr-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz",
- "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-less/node_modules/array-slice": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
- "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-less/node_modules/extend-shallow": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz",
- "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==",
- "license": "MIT",
- "dependencies": {
- "kind-of": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-less/node_modules/kind-of": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
- "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-less/node_modules/plugin-error": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz",
- "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==",
- "license": "MIT",
- "dependencies": {
- "ansi-cyan": "^0.1.1",
- "ansi-red": "^0.1.1",
- "arr-diff": "^1.0.1",
- "arr-union": "^2.0.1",
- "extend-shallow": "^1.1.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-less/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-match": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.1.0.tgz",
- "integrity": "sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ==",
- "license": "MIT",
- "dependencies": {
- "minimatch": "^3.0.3"
- }
- },
- "node_modules/gulp-notify": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/gulp-notify/-/gulp-notify-3.2.0.tgz",
- "integrity": "sha512-qEocs1UVoDKKUjfsxJNMNwkRla0PbsyJwsqNNXpzYWsLQ29LhxRMY3wnTGZcc4hMHtalnvah/Dwlwb4NijH/0A==",
- "license": "MIT",
- "dependencies": {
- "ansi-colors": "^1.0.1",
- "fancy-log": "^1.3.2",
- "lodash.template": "^4.4.0",
- "node-notifier": "^5.2.1",
- "node.extend": "^2.0.0",
- "plugin-error": "^0.1.2",
- "through2": "^2.0.3"
- },
- "engines": {
- "node": ">=0.8.0",
- "npm": ">=1.2.10"
- }
- },
- "node_modules/gulp-notify/node_modules/arr-diff": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz",
- "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==",
- "license": "MIT",
- "dependencies": {
- "arr-flatten": "^1.0.1",
- "array-slice": "^0.2.3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-notify/node_modules/arr-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz",
- "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-notify/node_modules/array-slice": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
- "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-notify/node_modules/extend-shallow": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz",
- "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==",
- "license": "MIT",
- "dependencies": {
- "kind-of": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-notify/node_modules/kind-of": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
- "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-notify/node_modules/plugin-error": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz",
- "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==",
- "license": "MIT",
- "dependencies": {
- "ansi-cyan": "^0.1.1",
- "ansi-red": "^0.1.1",
- "arr-diff": "^1.0.1",
- "arr-union": "^2.0.1",
- "extend-shallow": "^1.1.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-notify/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-plumber": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/gulp-plumber/-/gulp-plumber-1.2.1.tgz",
- "integrity": "sha512-mctAi9msEAG7XzW5ytDVZ9PxWMzzi1pS2rBH7lA095DhMa6KEXjm+St0GOCc567pJKJ/oCvosVAZEpAey0q2eQ==",
- "license": "MIT",
- "dependencies": {
- "chalk": "^1.1.3",
- "fancy-log": "^1.3.2",
- "plugin-error": "^0.1.2",
- "through2": "^2.0.3"
- },
- "engines": {
- "node": ">=0.10",
- "npm": ">=1.2.10"
- }
- },
- "node_modules/gulp-plumber/node_modules/arr-diff": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz",
- "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==",
- "license": "MIT",
- "dependencies": {
- "arr-flatten": "^1.0.1",
- "array-slice": "^0.2.3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-plumber/node_modules/arr-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz",
- "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-plumber/node_modules/array-slice": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
- "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-plumber/node_modules/extend-shallow": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz",
- "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==",
- "license": "MIT",
- "dependencies": {
- "kind-of": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-plumber/node_modules/kind-of": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
- "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-plumber/node_modules/plugin-error": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz",
- "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==",
- "license": "MIT",
- "dependencies": {
- "ansi-cyan": "^0.1.1",
- "ansi-red": "^0.1.1",
- "arr-diff": "^1.0.1",
- "arr-union": "^2.0.1",
- "extend-shallow": "^1.1.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-plumber/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-print": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/gulp-print/-/gulp-print-5.0.2.tgz",
- "integrity": "sha512-iIpHMzC/b3gFvVXOfP9Jk94SWGIsDLVNUrxULRleQev+08ug07mh84b1AOlW6QDQdmInQiqDFqJN1UvhU2nXdg==",
- "license": "MIT",
- "dependencies": {
- "ansi-colors": "^3.2.4",
- "fancy-log": "^1.3.3",
- "map-stream": "0.0.7",
- "vinyl": "^2.2.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/gulp-print/node_modules/ansi-colors": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
- "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/gulp-print/node_modules/map-stream": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz",
- "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==",
- "license": "MIT"
- },
- "node_modules/gulp-rename": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.4.0.tgz",
- "integrity": "sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/gulp-replace": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.1.4.tgz",
- "integrity": "sha512-SVSF7ikuWKhpAW4l4wapAqPPSToJoiNKsbDoUnRrSgwZHH7lH8pbPeQj1aOVYQrbZKhfSVBxVW+Py7vtulRktw==",
- "license": "MIT",
- "dependencies": {
- "@types/node": "*",
- "@types/vinyl": "^2.0.4",
- "istextorbinary": "^3.0.0",
- "replacestream": "^4.0.3",
- "yargs-parser": ">=5.0.0-security.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/gulp-rtlcss": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/gulp-rtlcss/-/gulp-rtlcss-1.4.2.tgz",
- "integrity": "sha512-wd807z/xq4XKtSwgrEetbx/aPoI5gV0yWV2rNqEBRwe2cJvNKLDsYR9A968c3gZtaKRMGAue5g3pHn40R+GWSA==",
- "license": "MIT",
- "dependencies": {
- "plugin-error": "^1.0.1",
- "rtlcss": "^2.4.0",
- "through2": "^2.0.5",
- "vinyl-sourcemaps-apply": "^0.2.1"
- }
- },
- "node_modules/gulp-rtlcss/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-tap": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/gulp-tap/-/gulp-tap-1.0.1.tgz",
- "integrity": "sha512-VpCARRSyr+WP16JGnoIg98/AcmyQjOwCpQgYoE35CWTdEMSbpgtAIK2fndqv2yY7aXstW27v3ZNBs0Ltb0Zkbg==",
- "license": "MIT",
- "dependencies": {
- "through2": "^2.0.3"
- }
- },
- "node_modules/gulp-tap/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-uglify": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/gulp-uglify/-/gulp-uglify-3.0.2.tgz",
- "integrity": "sha512-gk1dhB74AkV2kzqPMQBLA3jPoIAPd/nlNzP2XMDSG8XZrqnlCiDGAqC+rZOumzFvB5zOphlFh6yr3lgcAb/OOg==",
- "license": "MIT",
- "dependencies": {
- "array-each": "^1.0.1",
- "extend-shallow": "^3.0.2",
- "gulplog": "^1.0.0",
- "has-gulplog": "^0.1.0",
- "isobject": "^3.0.1",
- "make-error-cause": "^1.1.1",
- "safe-buffer": "^5.1.2",
- "through2": "^2.0.0",
- "uglify-js": "^3.0.5",
- "vinyl-sourcemaps-apply": "^0.2.0"
- }
- },
- "node_modules/gulp-uglify/node_modules/extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
- "license": "MIT",
- "dependencies": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-uglify/node_modules/is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.4"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-uglify/node_modules/is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-uglify/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-uglify/node_modules/uglify-js": {
- "version": "3.19.3",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
- "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==",
- "license": "BSD-2-Clause",
- "bin": {
- "uglifyjs": "bin/uglifyjs"
- },
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/gulp-util": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz",
- "integrity": "sha512-q5oWPc12lwSFS9h/4VIjG+1NuNDlJ48ywV2JKItY4Ycc/n1fXJeYPVQsfu5ZrhQi7FGSDBalwUCLar/GyHXKGw==",
- "deprecated": "gulp-util is deprecated - replace it, following the guidelines at https://medium.com/gulpjs/gulp-util-ca3b1f9f9ac5",
- "license": "MIT",
- "dependencies": {
- "array-differ": "^1.0.0",
- "array-uniq": "^1.0.2",
- "beeper": "^1.0.0",
- "chalk": "^1.0.0",
- "dateformat": "^2.0.0",
- "fancy-log": "^1.1.0",
- "gulplog": "^1.0.0",
- "has-gulplog": "^0.1.0",
- "lodash._reescape": "^3.0.0",
- "lodash._reevaluate": "^3.0.0",
- "lodash._reinterpolate": "^3.0.0",
- "lodash.template": "^3.0.0",
- "minimist": "^1.1.0",
- "multipipe": "^0.1.2",
- "object-assign": "^3.0.0",
- "replace-ext": "0.0.1",
- "through2": "^2.0.0",
- "vinyl": "^0.5.0"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/gulp-util/node_modules/clone": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
- "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.8"
- }
- },
- "node_modules/gulp-util/node_modules/clone-stats": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz",
- "integrity": "sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==",
- "license": "MIT"
- },
- "node_modules/gulp-util/node_modules/lodash.template": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz",
- "integrity": "sha512-0B4Y53I0OgHUJkt+7RmlDFWKjVAI/YUpWNiL9GQz5ORDr4ttgfQGo+phBWKFLJbBdtOwgMuUkdOHOnPg45jKmQ==",
- "license": "MIT",
- "dependencies": {
- "lodash._basecopy": "^3.0.0",
- "lodash._basetostring": "^3.0.0",
- "lodash._basevalues": "^3.0.0",
- "lodash._isiterateecall": "^3.0.0",
- "lodash._reinterpolate": "^3.0.0",
- "lodash.escape": "^3.0.0",
- "lodash.keys": "^3.0.0",
- "lodash.restparam": "^3.0.0",
- "lodash.templatesettings": "^3.0.0"
- }
- },
- "node_modules/gulp-util/node_modules/lodash.templatesettings": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz",
- "integrity": "sha512-TcrlEr31tDYnWkHFWDCV3dHYroKEXpJZ2YJYvJdhN+y4AkWMDZ5I4I8XDtUKqSAyG81N7w+I1mFEJtcED+tGqQ==",
- "license": "MIT",
- "dependencies": {
- "lodash._reinterpolate": "^3.0.0",
- "lodash.escape": "^3.0.0"
- }
- },
- "node_modules/gulp-util/node_modules/object-assign": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
- "integrity": "sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/gulp-util/node_modules/replace-ext": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz",
- "integrity": "sha512-AFBWBy9EVRTa/LhEcG8QDP3FvpwZqmvN2QFDuJswFeaVhWnZMp8q3E6Zd90SR04PlIwfGdyVjNyLPyen/ek5CQ==",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/gulp-util/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/gulp-util/node_modules/vinyl": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz",
- "integrity": "sha512-P5zdf3WB9uzr7IFoVQ2wZTmUwHL8cMZWJGzLBNCHNZ3NB6HTMsYABtt7z8tAGIINLXyAob9B9a1yzVGMFOYKEA==",
- "license": "MIT",
- "dependencies": {
- "clone": "^1.0.0",
- "clone-stats": "^0.0.1",
- "replace-ext": "0.0.1"
- },
- "engines": {
- "node": ">= 0.9"
- }
- },
- "node_modules/gulplog": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz",
- "integrity": "sha512-hm6N8nrm3Y08jXie48jsC55eCZz9mnb4OirAStEk2deqeyhXU3C1otDVh+ccttMuc1sBi6RX6ZJ720hs9RCvgw==",
- "license": "MIT",
- "dependencies": {
- "glogg": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/has-ansi": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
- "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/has-gulplog": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz",
- "integrity": "sha512-+F4GzLjwHNNDEAJW2DC1xXfEoPkRDmUdJ7CBYw4MpqtDwOnqdImJl7GWlpqx+Wko6//J8uKTnIe4wZSv7yCqmw==",
- "license": "MIT",
- "dependencies": {
- "sparkles": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/has-property-descriptors": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
- "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
- "license": "MIT",
- "dependencies": {
- "es-define-property": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/has-proto": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
- "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/has-symbols": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
- "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/has-value": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
- "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
- "license": "MIT",
- "dependencies": {
- "get-value": "^2.0.6",
- "has-values": "^1.0.0",
- "isobject": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/has-values": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
- "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
- "license": "MIT",
- "dependencies": {
- "is-number": "^3.0.0",
- "kind-of": "^4.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/has-values/node_modules/kind-of": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
- "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
- "license": "MIT",
- "dependencies": {
- "is-buffer": "^1.1.5"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/hasown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "license": "MIT",
- "dependencies": {
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/homedir-polyfill": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
- "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
- "license": "MIT",
- "dependencies": {
- "parse-passwd": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/hosted-git-info": {
- "version": "2.8.9",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
- "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
- "license": "ISC"
- },
- "node_modules/iconv-lite": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
- "license": "MIT",
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/image-size": {
- "version": "0.5.5",
- "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
- "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
- "license": "MIT",
- "optional": true,
- "bin": {
- "image-size": "bin/image-size.js"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/import-regex": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/import-regex/-/import-regex-1.1.0.tgz",
- "integrity": "sha512-EblpleIyIdATUKj8ovFojUHyToxgjeKXQgTHZBGZ4cEkbtV21BlO1PSrzZQ6Fei2fgk7uhDeEx656yvPhlRGeA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/indx": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/indx/-/indx-0.2.3.tgz",
- "integrity": "sha512-SEM+Px+Ghr3fZ+i9BNvUIZJ4UhojFuf+sT7x3cl2/ElL7NXne1A/m29VYzWTTypdOgDnWfoKNewIuPA6y+NMyQ==",
- "license": "MIT"
- },
- "node_modules/inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
- "license": "ISC",
- "dependencies": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "license": "ISC"
- },
- "node_modules/ini": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
- "license": "ISC"
- },
- "node_modules/inquirer": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz",
- "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==",
- "license": "MIT",
- "dependencies": {
- "ansi-escapes": "^3.2.0",
- "chalk": "^2.4.2",
- "cli-cursor": "^2.1.0",
- "cli-width": "^2.0.0",
- "external-editor": "^3.0.3",
- "figures": "^2.0.0",
- "lodash": "^4.17.12",
- "mute-stream": "0.0.7",
- "run-async": "^2.2.0",
- "rxjs": "^6.4.0",
- "string-width": "^2.1.0",
- "strip-ansi": "^5.1.0",
- "through": "^2.3.6"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/inquirer/node_modules/ansi-regex": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
- "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/inquirer/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "license": "MIT",
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/inquirer/node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/inquirer/node_modules/strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^4.1.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/inquirer/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "license": "MIT",
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/interpret": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
- "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/invert-kv": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
- "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ip-regex": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-1.0.3.tgz",
- "integrity": "sha512-HjpCHTuxbR/6jWJroc/VN+npo5j0T4Vv2TAI5qdEHQx7hsL767MeccGFSsLtF694EiZKTSEqgoeU6DtGFCcuqQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/irregular-plurals": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-2.0.0.tgz",
- "integrity": "sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/is": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz",
- "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==",
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/is-absolute": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
- "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
- "license": "MIT",
- "dependencies": {
- "is-relative": "^1.0.0",
- "is-windows": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-accessor-descriptor": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz",
- "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==",
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "license": "MIT"
- },
- "node_modules/is-binary-path": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
- "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==",
- "license": "MIT",
- "dependencies": {
- "binary-extensions": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
- "license": "MIT"
- },
- "node_modules/is-core-module": {
- "version": "2.15.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
- "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-data-descriptor": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz",
- "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==",
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/is-descriptor": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
- "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
- "license": "MIT",
- "dependencies": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "license": "MIT",
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-negated-glob": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
- "integrity": "sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-number": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
- "license": "MIT",
- "dependencies": {
- "kind-of": "^3.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-number/node_modules/kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "license": "MIT",
- "dependencies": {
- "is-buffer": "^1.1.5"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-obj": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
- "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-path-cwd": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
- "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-path-in-cwd": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
- "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
- "license": "MIT",
- "dependencies": {
- "is-path-inside": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-path-inside": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
- "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==",
- "license": "MIT",
- "dependencies": {
- "path-is-inside": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-plain-object": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
- "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-regexp": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
- "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-relative": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
- "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
- "license": "MIT",
- "dependencies": {
- "is-unc-path": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-stream": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-unc-path": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
- "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
- "license": "MIT",
- "dependencies": {
- "unc-path-regex": "^0.1.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-utf8": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
- "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==",
- "license": "MIT"
- },
- "node_modules/is-valid-glob": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz",
- "integrity": "sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-what": {
- "version": "3.14.1",
- "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
- "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
- "license": "MIT"
- },
- "node_modules/is-windows": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
- "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-wsl": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
- "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
- "license": "MIT"
- },
- "node_modules/isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "license": "ISC"
- },
- "node_modules/isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/istextorbinary": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-3.3.0.tgz",
- "integrity": "sha512-Tvq1W6NAcZeJ8op+Hq7tdZ434rqnMx4CCZ7H0ff83uEloDvVbqAwaMTZcafKGJT0VHkYzuXUiCY4hlXQg6WfoQ==",
- "license": "MIT",
- "dependencies": {
- "binaryextensions": "^2.2.0",
- "textextensions": "^3.2.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://bevry.me/fund"
- }
- },
- "node_modules/jackspeak": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
- "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "@isaacs/cliui": "^8.0.2"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- },
- "optionalDependencies": {
- "@pkgjs/parseargs": "^0.11.0"
- }
- },
- "node_modules/jquery": {
- "version": "3.7.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
- "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
- "license": "MIT"
- },
- "node_modules/js-beautify": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz",
- "integrity": "sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==",
- "license": "MIT",
- "dependencies": {
- "config-chain": "^1.1.13",
- "editorconfig": "^1.0.4",
- "glob": "^10.3.3",
- "js-cookie": "^3.0.5",
- "nopt": "^7.2.0"
- },
- "bin": {
- "css-beautify": "js/bin/css-beautify.js",
- "html-beautify": "js/bin/html-beautify.js",
- "js-beautify": "js/bin/js-beautify.js"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/js-beautify/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/js-beautify/node_modules/glob": {
- "version": "10.4.5",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
- "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
- "license": "ISC",
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^3.1.2",
- "minimatch": "^9.0.4",
- "minipass": "^7.1.2",
- "package-json-from-dist": "^1.0.0",
- "path-scurry": "^1.11.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/js-beautify/node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/js-cookie": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
- "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
- "license": "MIT",
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/json-stable-stringify-without-jsonify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
- "license": "MIT"
- },
- "node_modules/just-debounce": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz",
- "integrity": "sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==",
- "license": "MIT"
- },
- "node_modules/kind-of": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
- "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/last-run": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz",
- "integrity": "sha512-U/VxvpX4N/rFvPzr3qG5EtLKEnNI0emvIQB3/ecEwv+8GHaUKbIB8vxv1Oai5FAF0d0r7LXHhLLe5K/yChm5GQ==",
- "license": "MIT",
- "dependencies": {
- "default-resolution": "^2.0.0",
- "es6-weak-map": "^2.0.1"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/lazy-cache": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
- "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/lazystream": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
- "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "^2.0.5"
- },
- "engines": {
- "node": ">= 0.6.3"
- }
- },
- "node_modules/lcid": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
- "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==",
- "license": "MIT",
- "dependencies": {
- "invert-kv": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/lead": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz",
- "integrity": "sha512-IpSVCk9AYvLHo5ctcIXxOBpMWUe+4TKN3VPWAKUbJikkmsGp0VrSM8IttVc32D6J4WUsiPE6aEFRNmIoF/gdow==",
- "license": "MIT",
- "dependencies": {
- "flush-write-stream": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/less": {
- "version": "3.13.1",
- "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz",
- "integrity": "sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==",
- "license": "Apache-2.0",
- "dependencies": {
- "copy-anything": "^2.0.1",
- "tslib": "^1.10.0"
- },
- "bin": {
- "lessc": "bin/lessc"
- },
- "engines": {
- "node": ">=6"
- },
- "optionalDependencies": {
- "errno": "^0.1.1",
- "graceful-fs": "^4.1.2",
- "image-size": "~0.5.0",
- "make-dir": "^2.1.0",
- "mime": "^1.4.1",
- "native-request": "^1.0.5",
- "source-map": "~0.6.0"
- }
- },
- "node_modules/liftoff": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz",
- "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==",
- "license": "MIT",
- "dependencies": {
- "extend": "^3.0.0",
- "findup-sync": "^3.0.0",
- "fined": "^1.0.1",
- "flagged-respawn": "^1.0.0",
- "is-plain-object": "^2.0.4",
- "object.map": "^1.0.0",
- "rechoir": "^0.6.2",
- "resolve": "^1.1.7"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/liftoff/node_modules/is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/load-json-file": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
- "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==",
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.1.2",
- "parse-json": "^2.2.0",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0",
- "strip-bom": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/load-json-file/node_modules/pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/lodash": {
- "version": "4.17.21",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
- "license": "MIT"
- },
- "node_modules/lodash._baseassign": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz",
- "integrity": "sha512-t3N26QR2IdSN+gqSy9Ds9pBu/J1EAFEshKlUHpJG3rvyJOYgcELIxcIeKKfZk7sjOz11cFfzJRsyFry/JyabJQ==",
- "license": "MIT",
- "dependencies": {
- "lodash._basecopy": "^3.0.0",
- "lodash.keys": "^3.0.0"
- }
- },
- "node_modules/lodash._basecopy": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz",
- "integrity": "sha512-rFR6Vpm4HeCK1WPGvjZSJ+7yik8d8PVUdCJx5rT2pogG4Ve/2ZS7kfmO5l5T2o5V2mqlNIfSF5MZlr1+xOoYQQ==",
- "license": "MIT"
- },
- "node_modules/lodash._basetostring": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz",
- "integrity": "sha512-mTzAr1aNAv/i7W43vOR/uD/aJ4ngbtsRaCubp2BfZhlGU/eORUjg/7F6X0orNMdv33JOrdgGybtvMN/po3EWrA==",
- "license": "MIT"
- },
- "node_modules/lodash._basevalues": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz",
- "integrity": "sha512-H94wl5P13uEqlCg7OcNNhMQ8KvWSIyqXzOPusRgHC9DK3o54P6P3xtbXlVbRABG4q5gSmp7EDdJ0MSuW9HX6Mg==",
- "license": "MIT"
- },
- "node_modules/lodash._bindcallback": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz",
- "integrity": "sha512-2wlI0JRAGX8WEf4Gm1p/mv/SZ+jLijpj0jyaE/AXeuQphzCgD8ZQW4oSpoN8JAopujOFGU3KMuq7qfHBWlGpjQ==",
- "license": "MIT"
- },
- "node_modules/lodash._createassigner": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz",
- "integrity": "sha512-LziVL7IDnJjQeeV95Wvhw6G28Z8Q6da87LWKOPWmzBLv4u6FAT/x5v00pyGW0u38UoogNF2JnD3bGgZZDaNEBw==",
- "license": "MIT",
- "dependencies": {
- "lodash._bindcallback": "^3.0.0",
- "lodash._isiterateecall": "^3.0.0",
- "lodash.restparam": "^3.0.0"
- }
- },
- "node_modules/lodash._getnative": {
- "version": "3.9.1",
- "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
- "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA==",
- "license": "MIT"
- },
- "node_modules/lodash._isiterateecall": {
- "version": "3.0.9",
- "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz",
- "integrity": "sha512-De+ZbrMu6eThFti/CSzhRvTKMgQToLxbij58LMfM8JnYDNSOjkjTCIaa8ixglOeGh2nyPlakbt5bJWJ7gvpYlQ==",
- "license": "MIT"
- },
- "node_modules/lodash._isnative": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz",
- "integrity": "sha512-BOlKGKNHhCHswGOWtmVb5zBygyxN7EmTuzVOSQI6QSoGhG+kvv71gICFS1TBpnqvT1n53txK8CDK3u5D2/GZxQ==",
- "license": "MIT"
- },
- "node_modules/lodash._objecttypes": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz",
- "integrity": "sha512-XpqGh1e7hhkOzftBfWE7zt+Yn9mVHFkDhicVttvKLsoCMLVVL+xTQjfjB4X4vtznauxv0QZ5ZAeqjvat0dh62Q==",
- "license": "MIT"
- },
- "node_modules/lodash._reescape": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz",
- "integrity": "sha512-Sjlavm5y+FUVIF3vF3B75GyXrzsfYV8Dlv3L4mEpuB9leg8N6yf/7rU06iLPx9fY0Mv3khVp9p7Dx0mGV6V5OQ==",
- "license": "MIT"
- },
- "node_modules/lodash._reevaluate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz",
- "integrity": "sha512-OrPwdDc65iJiBeUe5n/LIjd7Viy99bKwDdk7Z5ljfZg0uFRFlfQaCy9tZ4YMAag9WAZmlVpe1iZrkIMMSMHD3w==",
- "license": "MIT"
- },
- "node_modules/lodash._reinterpolate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
- "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==",
- "license": "MIT"
- },
- "node_modules/lodash._root": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz",
- "integrity": "sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==",
- "license": "MIT"
- },
- "node_modules/lodash._shimkeys": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz",
- "integrity": "sha512-lBrglYxLD/6KAJ8IEa5Lg+YHgNAL7FyKqXg4XOUI+Du/vtniLs1ZqS+yHNKPkK54waAgkdUnDOYaWf+rv4B+AA==",
- "license": "MIT",
- "dependencies": {
- "lodash._objecttypes": "~2.4.1"
- }
- },
- "node_modules/lodash.assign": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-3.2.0.tgz",
- "integrity": "sha512-/VVxzgGBmbphasTg51FrztxQJ/VgAUpol6zmJuSVSGcNg4g7FA4z7rQV8Ovr9V3vFBNWZhvKWHfpAytjTVUfFA==",
- "license": "MIT",
- "dependencies": {
- "lodash._baseassign": "^3.0.0",
- "lodash._createassigner": "^3.0.0",
- "lodash.keys": "^3.0.0"
- }
- },
- "node_modules/lodash.clone": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz",
- "integrity": "sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg==",
- "license": "MIT"
- },
- "node_modules/lodash.defaults": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-3.1.2.tgz",
- "integrity": "sha512-X7135IXFQt5JDFnYxOVAzVz+kFvwDn3N8DJYf+nrz/mMWEuSu7+OL6rWqsk3+VR1T4TejFCSu5isBJOLSID2bg==",
- "license": "MIT",
- "dependencies": {
- "lodash.assign": "^3.0.0",
- "lodash.restparam": "^3.0.0"
- }
- },
- "node_modules/lodash.escape": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz",
- "integrity": "sha512-n1PZMXgaaDWZDSvuNZ/8XOcYO2hOKDqZel5adtR30VKQAtoWs/5AOeFA0vPV8moiPzlqe7F4cP2tzpFewQyelQ==",
- "license": "MIT",
- "dependencies": {
- "lodash._root": "^3.0.0"
- }
- },
- "node_modules/lodash.flatten": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
- "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
- "license": "MIT"
- },
- "node_modules/lodash.get": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
- "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
- "license": "MIT"
- },
- "node_modules/lodash.isarguments": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
- "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==",
- "license": "MIT"
- },
- "node_modules/lodash.isarray": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
- "integrity": "sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ==",
- "license": "MIT"
- },
- "node_modules/lodash.isobject": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz",
- "integrity": "sha512-sTebg2a1PoicYEZXD5PBdQcTlIJ6hUslrlWr7iV0O7n+i4596s2NQ9I5CaZ5FbXSfya/9WQsrYLANUJv9paYVA==",
- "license": "MIT",
- "dependencies": {
- "lodash._objecttypes": "~2.4.1"
- }
- },
- "node_modules/lodash.keys": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
- "integrity": "sha512-CuBsapFjcubOGMn3VD+24HOAPxM79tH+V6ivJL3CHYjtrawauDJHUk//Yew9Hvc6e9rbCrURGk8z6PC+8WJBfQ==",
- "license": "MIT",
- "dependencies": {
- "lodash._getnative": "^3.0.0",
- "lodash.isarguments": "^3.0.0",
- "lodash.isarray": "^3.0.0"
- }
- },
- "node_modules/lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "license": "MIT"
- },
- "node_modules/lodash.partialright": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/lodash.partialright/-/lodash.partialright-4.2.1.tgz",
- "integrity": "sha512-yebmPMQZH7i4El6SdJTW9rn8irWl8VTcsmiWqm/I4sY8/ZjbSo0Z512HL6soeAu3mh5rhx5uIIo6kYJOQXbCxw==",
- "license": "MIT"
- },
- "node_modules/lodash.pick": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
- "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==",
- "license": "MIT"
- },
- "node_modules/lodash.restparam": {
- "version": "3.6.1",
- "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz",
- "integrity": "sha512-L4/arjjuq4noiUJpt3yS6KIKDtJwNe2fIYgMqyYYKoeIfV1iEqvPwhCx23o+R9dzouGihDAPN1dTIRWa7zk8tw==",
- "license": "MIT"
- },
- "node_modules/lodash.set": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
- "integrity": "sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==",
- "license": "MIT"
- },
- "node_modules/lodash.template": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
- "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
- "license": "MIT",
- "dependencies": {
- "lodash._reinterpolate": "^3.0.0",
- "lodash.templatesettings": "^4.0.0"
- }
- },
- "node_modules/lodash.templatesettings": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz",
- "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==",
- "license": "MIT",
- "dependencies": {
- "lodash._reinterpolate": "^3.0.0"
- }
- },
- "node_modules/lodash.uniq": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
- "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
- "license": "MIT"
- },
- "node_modules/longest": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
- "integrity": "sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/lru-cache": {
- "version": "10.4.3",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
- "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
- "license": "ISC"
- },
- "node_modules/macos-release": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.1.tgz",
- "integrity": "sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/make-dir": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
- "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "pify": "^4.0.1",
- "semver": "^5.6.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/make-dir/node_modules/pify": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
- "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/make-error": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
- "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
- "license": "ISC"
- },
- "node_modules/make-error-cause": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/make-error-cause/-/make-error-cause-1.2.2.tgz",
- "integrity": "sha512-4TO2Y3HkBnis4c0dxhAgD/jprySYLACf7nwN6V0HAHDx59g12WlRpUmFy1bRHamjGUEEBrEvCq6SUpsEE2lhUg==",
- "license": "Apache-2.0",
- "dependencies": {
- "make-error": "^1.2.0"
- }
- },
- "node_modules/make-iterator": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
- "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
- "license": "MIT",
- "dependencies": {
- "kind-of": "^6.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/make-iterator/node_modules/kind-of": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
- "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/map-cache": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
- "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/map-stream": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
- "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g=="
- },
- "node_modules/map-visit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
- "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
- "license": "MIT",
- "dependencies": {
- "object-visit": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/matchdep": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz",
- "integrity": "sha512-LFgVbaHIHMqCRuCZyfCtUOq9/Lnzhi7Z0KFUE2fhD54+JN2jLh3hC02RLkqauJ3U4soU6H1J3tfj/Byk7GoEjA==",
- "license": "MIT",
- "dependencies": {
- "findup-sync": "^2.0.0",
- "micromatch": "^3.0.4",
- "resolve": "^1.4.0",
- "stack-trace": "0.0.10"
- },
- "engines": {
- "node": ">= 0.10.0"
- }
- },
- "node_modules/matchdep/node_modules/findup-sync": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz",
- "integrity": "sha512-vs+3unmJT45eczmcAZ6zMJtxN3l/QXeccaXQx5cu/MeJMhewVfoWZqibRkOxPnmoR59+Zy5hjabfQc6JLSah4g==",
- "license": "MIT",
- "dependencies": {
- "detect-file": "^1.0.0",
- "is-glob": "^3.1.0",
- "micromatch": "^3.0.4",
- "resolve-dir": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/matchdep/node_modules/is-glob": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
- "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
- "license": "MIT",
- "dependencies": {
- "is-extglob": "^2.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/merge-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
- "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
- "license": "MIT"
- },
- "node_modules/micromatch": {
- "version": "3.1.10",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
- "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
- "license": "MIT",
- "dependencies": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "braces": "^2.3.1",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "extglob": "^2.0.4",
- "fragment-cache": "^0.2.1",
- "kind-of": "^6.0.2",
- "nanomatch": "^1.2.9",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/micromatch/node_modules/extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
- "license": "MIT",
- "dependencies": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/micromatch/node_modules/is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.4"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/micromatch/node_modules/is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/micromatch/node_modules/kind-of": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
- "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/mime": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
- "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
- "license": "MIT",
- "optional": true,
- "bin": {
- "mime": "cli.js"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/mimic-fn": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
- "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "license": "ISC",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/mixin-deep": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
- "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
- "license": "MIT",
- "dependencies": {
- "for-in": "^1.0.2",
- "is-extendable": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/mixin-deep/node_modules/is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.4"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/mixin-deep/node_modules/is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
- "license": "MIT",
- "dependencies": {
- "minimist": "^1.2.6"
- },
- "bin": {
- "mkdirp": "bin/cmd.js"
- }
- },
- "node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "license": "MIT"
- },
- "node_modules/multipipe": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz",
- "integrity": "sha512-7ZxrUybYv9NonoXgwoOqtStIu18D1c3eFZj27hqgf5kBrBF8Q+tE8V0MW8dKM5QLkQPh1JhhbKgHLY9kifov4Q==",
- "license": "MIT",
- "dependencies": {
- "duplexer2": "0.0.2"
- }
- },
- "node_modules/mute-stdout": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz",
- "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/mute-stream": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
- "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==",
- "license": "ISC"
- },
- "node_modules/nan": {
- "version": "2.22.0",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz",
- "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/nanomatch": {
- "version": "1.2.13",
- "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
- "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
- "license": "MIT",
- "dependencies": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "fragment-cache": "^0.2.1",
- "is-windows": "^1.0.2",
- "kind-of": "^6.0.2",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/nanomatch/node_modules/extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
- "license": "MIT",
- "dependencies": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/nanomatch/node_modules/is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.4"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/nanomatch/node_modules/is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/nanomatch/node_modules/kind-of": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
- "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/native-request": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.1.2.tgz",
- "integrity": "sha512-/etjwrK0J4Ebbcnt35VMWnfiUX/B04uwGJxyJInagxDqf2z5drSt/lsOvEMWGYunz1kaLZAFrV4NDAbOoDKvAQ==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/next-tick": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
- "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==",
- "license": "ISC"
- },
- "node_modules/nice-try": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
- "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
- "license": "MIT"
- },
- "node_modules/node-fetch": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
- "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
- "license": "MIT",
- "dependencies": {
- "whatwg-url": "^5.0.0"
- },
- "engines": {
- "node": "4.x || >=6.0.0"
- },
- "peerDependencies": {
- "encoding": "^0.1.0"
- },
- "peerDependenciesMeta": {
- "encoding": {
- "optional": true
- }
- }
- },
- "node_modules/node-notifier": {
- "version": "5.4.5",
- "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.5.tgz",
- "integrity": "sha512-tVbHs7DyTLtzOiN78izLA85zRqB9NvEXkAf014Vx3jtSvn/xBl6bR8ZYifj+dFcFrKI21huSQgJZ6ZtL3B4HfQ==",
- "license": "MIT",
- "dependencies": {
- "growly": "^1.3.0",
- "is-wsl": "^1.1.0",
- "semver": "^5.5.0",
- "shellwords": "^0.1.1",
- "which": "^1.3.0"
- }
- },
- "node_modules/node-releases": {
- "version": "2.0.18",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
- "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
- "license": "MIT"
- },
- "node_modules/node.extend": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.3.tgz",
- "integrity": "sha512-xwADg/okH48PvBmRZyoX8i8GJaKuJ1CqlqotlZOhUio8egD1P5trJupHKBzcPjSF9ifK2gPcEICRBnkfPqQXZw==",
- "license": "(MIT OR GPL-2.0)",
- "dependencies": {
- "hasown": "^2.0.0",
- "is": "^3.3.0"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/nopt": {
- "version": "7.2.1",
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz",
- "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==",
- "license": "ISC",
- "dependencies": {
- "abbrev": "^2.0.0"
- },
- "bin": {
- "nopt": "bin/nopt.js"
- },
- "engines": {
- "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
- }
- },
- "node_modules/normalize-package-data": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
- "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "hosted-git-info": "^2.1.4",
- "resolve": "^1.10.0",
- "semver": "2 || 3 || 4 || 5",
- "validate-npm-package-license": "^3.0.1"
- }
- },
- "node_modules/normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/normalize-range": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
- "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/now-and-later": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz",
- "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==",
- "license": "MIT",
- "dependencies": {
- "once": "^1.3.2"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/npm-run-path": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
- "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==",
- "license": "MIT",
- "dependencies": {
- "path-key": "^2.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/num2fraction": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
- "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==",
- "license": "MIT"
- },
- "node_modules/number-is-nan": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
- "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-copy": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
- "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
- "license": "MIT",
- "dependencies": {
- "copy-descriptor": "^0.1.0",
- "define-property": "^0.2.5",
- "kind-of": "^3.0.3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-copy/node_modules/define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-copy/node_modules/is-descriptor": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz",
- "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==",
- "license": "MIT",
- "dependencies": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/object-copy/node_modules/kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "license": "MIT",
- "dependencies": {
- "is-buffer": "^1.1.5"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/object-visit": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
- "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object.assign": {
- "version": "4.1.5",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz",
- "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==",
- "license": "MIT",
- "dependencies": {
- "call-bind": "^1.0.5",
- "define-properties": "^1.2.1",
- "has-symbols": "^1.0.3",
- "object-keys": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/object.defaults": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
- "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==",
- "license": "MIT",
- "dependencies": {
- "array-each": "^1.0.1",
- "array-slice": "^1.0.0",
- "for-own": "^1.0.0",
- "isobject": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object.map": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
- "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==",
- "license": "MIT",
- "dependencies": {
- "for-own": "^1.0.0",
- "make-iterator": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object.pick": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
- "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object.reduce": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz",
- "integrity": "sha512-naLhxxpUESbNkRqc35oQ2scZSJueHGQNUfMW/0U37IgN6tE2dgDWg3whf+NEliy3F/QysrO48XKUz/nGPe+AQw==",
- "license": "MIT",
- "dependencies": {
- "for-own": "^1.0.0",
- "make-iterator": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/octokit-pagination-methods": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz",
- "integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==",
- "license": "MIT"
- },
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "license": "ISC",
- "dependencies": {
- "wrappy": "1"
- }
- },
- "node_modules/onetime": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
- "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==",
- "license": "MIT",
- "dependencies": {
- "mimic-fn": "^1.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/ordered-read-streams": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
- "integrity": "sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "^2.0.1"
- }
- },
- "node_modules/os-homedir": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
- "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/os-locale": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
- "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==",
- "license": "MIT",
- "dependencies": {
- "lcid": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/os-name": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz",
- "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==",
- "license": "MIT",
- "dependencies": {
- "macos-release": "^2.2.0",
- "windows-release": "^3.1.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/os-tmpdir": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
- "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/p-finally": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
- "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/p-map": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz",
- "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/package-json-from-dist": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
- "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
- "license": "BlueOak-1.0.0"
- },
- "node_modules/parse-filepath": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
- "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==",
- "license": "MIT",
- "dependencies": {
- "is-absolute": "^1.0.0",
- "map-cache": "^0.2.0",
- "path-root": "^0.1.1"
- },
- "engines": {
- "node": ">=0.8"
- }
- },
- "node_modules/parse-import": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/parse-import/-/parse-import-2.0.0.tgz",
- "integrity": "sha512-c59vdx1LiQT+majNKMyfFLrNMAVS9U1bychTv3CEuxbKspgnVTrzLRtgtfCWyAmTuFAxQVSJFasVv8svJLksIg==",
- "license": "MIT",
- "dependencies": {
- "get-imports": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/parse-json": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
- "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==",
- "license": "MIT",
- "dependencies": {
- "error-ex": "^1.2.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/parse-node-version": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
- "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/parse-passwd": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
- "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/pascalcase": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
- "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/path-dirname": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
- "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==",
- "license": "MIT"
- },
- "node_modules/path-exists": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
- "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==",
- "license": "MIT",
- "dependencies": {
- "pinkie-promise": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/path-is-inside": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
- "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==",
- "license": "(WTFPL OR MIT)"
- },
- "node_modules/path-key": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/path-parse": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "license": "MIT"
- },
- "node_modules/path-root": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
- "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==",
- "license": "MIT",
- "dependencies": {
- "path-root-regex": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/path-root-regex": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz",
- "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/path-scurry": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
- "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "lru-cache": "^10.2.0",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
- },
- "engines": {
- "node": ">=16 || 14 >=14.18"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/path-type": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
- "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==",
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.1.2",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/path-type/node_modules/pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/picocolors": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
- "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
- "license": "ISC"
- },
- "node_modules/pify": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
- "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/pinkie": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
- "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/pinkie-promise": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
- "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
- "license": "MIT",
- "dependencies": {
- "pinkie": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/plugin-error": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz",
- "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==",
- "license": "MIT",
- "dependencies": {
- "ansi-colors": "^1.0.1",
- "arr-diff": "^4.0.0",
- "arr-union": "^3.1.0",
- "extend-shallow": "^3.0.2"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/plugin-error/node_modules/extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
- "license": "MIT",
- "dependencies": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/plugin-error/node_modules/is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.4"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/plugin-error/node_modules/is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/plur": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/plur/-/plur-3.1.1.tgz",
- "integrity": "sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w==",
- "license": "MIT",
- "dependencies": {
- "irregular-plurals": "^2.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/posix-character-classes": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
- "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/postcss": {
- "version": "7.0.39",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
- "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
- "license": "MIT",
- "dependencies": {
- "picocolors": "^0.2.1",
- "source-map": "^0.6.1"
- },
- "engines": {
- "node": ">=6.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- }
- },
- "node_modules/postcss-value-parser": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
- "license": "MIT"
- },
- "node_modules/pretty-hrtime": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
- "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/process-nextick-args": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
- "license": "MIT"
- },
- "node_modules/proto-list": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
- "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
- "license": "ISC"
- },
- "node_modules/prr": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
- "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/pump": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
- "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
- "license": "MIT",
- "dependencies": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
- "node_modules/pumpify": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
- "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
- "license": "MIT",
- "dependencies": {
- "duplexify": "^3.6.0",
- "inherits": "^2.0.3",
- "pump": "^2.0.0"
- }
- },
- "node_modules/read-pkg": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
- "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==",
- "license": "MIT",
- "dependencies": {
- "load-json-file": "^1.0.0",
- "normalize-package-data": "^2.3.2",
- "path-type": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/read-pkg-up": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
- "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==",
- "license": "MIT",
- "dependencies": {
- "find-up": "^1.0.0",
- "read-pkg": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/readable-stream": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
- "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
- "license": "MIT",
- "dependencies": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "node_modules/readable-stream/node_modules/safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "license": "MIT"
- },
- "node_modules/readdirp": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
- "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.1.11",
- "micromatch": "^3.1.10",
- "readable-stream": "^2.0.2"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/rechoir": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
- "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
- "dependencies": {
- "resolve": "^1.1.6"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/regex-not": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
- "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
- "license": "MIT",
- "dependencies": {
- "extend-shallow": "^3.0.2",
- "safe-regex": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/regex-not/node_modules/extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
- "license": "MIT",
- "dependencies": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/regex-not/node_modules/is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.4"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/regex-not/node_modules/is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/remove-bom-buffer": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz",
- "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==",
- "license": "MIT",
- "dependencies": {
- "is-buffer": "^1.1.5",
- "is-utf8": "^0.2.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/remove-bom-stream": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz",
- "integrity": "sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==",
- "license": "MIT",
- "dependencies": {
- "remove-bom-buffer": "^3.0.0",
- "safe-buffer": "^5.1.0",
- "through2": "^2.0.3"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/remove-bom-stream/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/remove-trailing-separator": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
- "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
- "license": "ISC"
- },
- "node_modules/repeat-element": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
- "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/repeat-string": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
- "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/replace-ext": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz",
- "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/replace-homedir": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz",
- "integrity": "sha512-CHPV/GAglbIB1tnQgaiysb8H2yCy8WQ7lcEwQ/eT+kLj0QHV8LnJW0zpqpE7RSkrMSRoa+EBoag86clf7WAgSg==",
- "license": "MIT",
- "dependencies": {
- "homedir-polyfill": "^1.0.1",
- "is-absolute": "^1.0.0",
- "remove-trailing-separator": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/replacestream": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz",
- "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "escape-string-regexp": "^1.0.3",
- "object-assign": "^4.0.1",
- "readable-stream": "^2.0.2"
- }
- },
- "node_modules/require-dir": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/require-dir/-/require-dir-1.2.0.tgz",
- "integrity": "sha512-LY85DTSu+heYgDqq/mK+7zFHWkttVNRXC9NKcKGyuGLdlsfbjEPrIEYdCVrx6hqnJb+xSu3Lzaoo8VnmOhhjNA==",
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/require-dot-file": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/require-dot-file/-/require-dot-file-0.4.0.tgz",
- "integrity": "sha512-pMe/T7+uFi2NMYsxuQtTh9n/UKD13HAHeDOk7KuP2pr7aKi5aMhvkbGD4IeoJKjy+3vdIUy8ggXYWzlZTL5FWA==",
- "license": "MIT"
- },
- "node_modules/require-main-filename": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
- "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==",
- "license": "ISC"
- },
- "node_modules/resolve": {
- "version": "1.22.8",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
- "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
- "license": "MIT",
- "dependencies": {
- "is-core-module": "^2.13.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/resolve-dir": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
- "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==",
- "license": "MIT",
- "dependencies": {
- "expand-tilde": "^2.0.0",
- "global-modules": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/resolve-options": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz",
- "integrity": "sha512-NYDgziiroVeDC29xq7bp/CacZERYsA9bXYd1ZmcJlF3BcrZv5pTb4NG7SjdyKDnXZ84aC4vo2u6sNKIA1LCu/A==",
- "license": "MIT",
- "dependencies": {
- "value-or-function": "^3.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/resolve-url": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
- "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==",
- "deprecated": "https://github.com/lydell/resolve-url#deprecated",
- "license": "MIT"
- },
- "node_modules/restore-cursor": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
- "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==",
- "license": "MIT",
- "dependencies": {
- "onetime": "^2.0.0",
- "signal-exit": "^3.0.2"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/ret": {
- "version": "0.1.15",
- "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
- "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.12"
- }
- },
- "node_modules/rework": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz",
- "integrity": "sha512-eEjL8FdkdsxApd0yWVZgBGzfCQiT8yqSc2H1p4jpZpQdtz7ohETiDMoje5PlM8I9WgkqkreVxFUKYOiJdVWDXw==",
- "dependencies": {
- "convert-source-map": "^0.3.3",
- "css": "^2.0.0"
- }
- },
- "node_modules/rework-import": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/rework-import/-/rework-import-2.1.0.tgz",
- "integrity": "sha512-ufvoQX6cDhrqYc8ZXvJ+6FqimwyI4qn8cH1ypAiS9Mn41iVPN/9RGwRvscBtUEkHA09w8voTIakRJKslgWcTEQ==",
- "license": "MIT",
- "dependencies": {
- "css": "^2.0.0",
- "globby": "^2.0.0",
- "parse-import": "^2.0.0",
- "url-regex": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/rework-import/node_modules/glob": {
- "version": "5.0.15",
- "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
- "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
- "license": "ISC",
- "dependencies": {
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "2 || 3",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/rework-import/node_modules/globby": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-2.1.0.tgz",
- "integrity": "sha512-CqRID2dMaN4Zi9PANiQHhmKaGu7ZASehBLnaDogjR9L3L1EqAGFhflafT0IrSN/zm9xFk+KMTXZCN8pUYOiO/Q==",
- "license": "MIT",
- "dependencies": {
- "array-union": "^1.0.1",
- "async": "^1.2.1",
- "glob": "^5.0.3",
- "object-assign": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/rework-import/node_modules/object-assign": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
- "integrity": "sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/rework-plugin-function": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/rework-plugin-function/-/rework-plugin-function-1.0.2.tgz",
- "integrity": "sha512-kyIphbC2Kuc3iFz1CSAQ5zmt4o/IHquhO+uG0kK0FQTjs4Z5eAxrqmrv3rZMR1KXa77SesaW9KwKyfbYoLMEqw==",
- "license": "MIT",
- "dependencies": {
- "rework-visit": "^1.0.0"
- }
- },
- "node_modules/rework-plugin-url": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/rework-plugin-url/-/rework-plugin-url-1.1.0.tgz",
- "integrity": "sha512-qlAhbJKfEK59jAPQppIn8bNXffW1INlaJZaXdX/ZLs/CzZSnn38Y0wESQ3tjOwRsDbPEUHN2XJ3ZgueDaaCC0A==",
- "license": "MIT",
- "dependencies": {
- "rework-plugin-function": "^1.0.0"
- }
- },
- "node_modules/rework-visit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz",
- "integrity": "sha512-W6V2fix7nCLUYX1v6eGPrBOZlc03/faqzP4sUxMAJMBMOPYhfV/RyLegTufn5gJKaOITyi+gvf0LXDZ9NzkHnQ==",
- "license": "MIT"
- },
- "node_modules/rework/node_modules/convert-source-map": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz",
- "integrity": "sha512-+4nRk0k3oEpwUB7/CalD7xE2z4VmtEnnq0GO2IPTkrooTrAhEsWvuLF5iWP1dXrwluki/azwXV1ve7gtYuPldg==",
- "license": "MIT"
- },
- "node_modules/right-align": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
- "integrity": "sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==",
- "license": "MIT",
- "dependencies": {
- "align-text": "^0.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
- "deprecated": "Rimraf versions prior to v4 are no longer supported",
- "license": "ISC",
- "dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
- }
- },
- "node_modules/rtlcss": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-2.6.2.tgz",
- "integrity": "sha512-06LFAr+GAPo+BvaynsXRfoYTJvSaWRyOhURCQ7aeI1MKph9meM222F+Zkt3bDamyHHJuGi3VPtiRkpyswmQbGA==",
- "license": "MIT",
- "dependencies": {
- "@choojs/findup": "^0.2.1",
- "chalk": "^2.4.2",
- "mkdirp": "^0.5.1",
- "postcss": "^6.0.23",
- "strip-json-comments": "^2.0.0"
- },
- "bin": {
- "rtlcss": "bin/rtlcss.js"
- }
- },
- "node_modules/rtlcss/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "license": "MIT",
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/rtlcss/node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/rtlcss/node_modules/postcss": {
- "version": "6.0.23",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
- "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
- "license": "MIT",
- "dependencies": {
- "chalk": "^2.4.1",
- "source-map": "^0.6.1",
- "supports-color": "^5.4.0"
- },
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/rtlcss/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "license": "MIT",
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/run-async": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
- "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/rxjs": {
- "version": "6.6.7",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
- "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^1.9.0"
- },
- "engines": {
- "npm": ">=2.0.0"
- }
- },
- "node_modules/safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/safe-regex": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
- "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
- "license": "MIT",
- "dependencies": {
- "ret": "~0.1.10"
- }
- },
- "node_modules/safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "license": "MIT"
- },
- "node_modules/semver": {
- "version": "5.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
- "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver"
- }
- },
- "node_modules/semver-greatest-satisfied-range": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz",
- "integrity": "sha512-Ny/iyOzSSa8M5ML46IAx3iXc6tfOsYU2R4AXi2UpHk60Zrgyq6eqPj/xiOfS0rRl/iiQ/rdJkVjw/5cdUyCntQ==",
- "license": "MIT",
- "dependencies": {
- "sver-compat": "^1.5.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/set-blocking": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
- "license": "ISC"
- },
- "node_modules/set-function-length": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
- "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
- "license": "MIT",
- "dependencies": {
- "define-data-property": "^1.1.4",
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.4",
- "gopd": "^1.0.1",
- "has-property-descriptors": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/set-value": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
- "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
- "license": "MIT",
- "dependencies": {
- "extend-shallow": "^2.0.1",
- "is-extendable": "^0.1.1",
- "is-plain-object": "^2.0.3",
- "split-string": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/set-value/node_modules/is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/shebang-command": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
- "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
- "license": "MIT",
- "dependencies": {
- "shebang-regex": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/shebang-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
- "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/shellwords": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
- "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==",
- "license": "MIT"
- },
- "node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "license": "ISC"
- },
- "node_modules/snapdragon": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
- "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
- "license": "MIT",
- "dependencies": {
- "base": "^0.11.1",
- "debug": "^2.2.0",
- "define-property": "^0.2.5",
- "extend-shallow": "^2.0.1",
- "map-cache": "^0.2.2",
- "source-map": "^0.5.6",
- "source-map-resolve": "^0.5.0",
- "use": "^3.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/snapdragon-node": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
- "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
- "license": "MIT",
- "dependencies": {
- "define-property": "^1.0.0",
- "isobject": "^3.0.0",
- "snapdragon-util": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/snapdragon-node/node_modules/define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/snapdragon-util": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
- "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
- "license": "MIT",
- "dependencies": {
- "kind-of": "^3.2.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/snapdragon-util/node_modules/kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "license": "MIT",
- "dependencies": {
- "is-buffer": "^1.1.5"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/snapdragon/node_modules/define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/snapdragon/node_modules/is-descriptor": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz",
- "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==",
- "license": "MIT",
- "dependencies": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/snapdragon/node_modules/source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/source-map-resolve": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
- "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
- "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated",
- "license": "MIT",
- "dependencies": {
- "atob": "^2.1.2",
- "decode-uri-component": "^0.2.0",
- "resolve-url": "^0.2.1",
- "source-map-url": "^0.4.0",
- "urix": "^0.1.0"
- }
- },
- "node_modules/source-map-url": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
- "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
- "deprecated": "See https://github.com/lydell/source-map-url#deprecated",
- "license": "MIT"
- },
- "node_modules/sparkles": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz",
- "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/spdx-correct": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
- "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
- "license": "Apache-2.0",
- "dependencies": {
- "spdx-expression-parse": "^3.0.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-exceptions": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
- "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
- "license": "CC-BY-3.0"
- },
- "node_modules/spdx-expression-parse": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
- "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
- "license": "MIT",
- "dependencies": {
- "spdx-exceptions": "^2.1.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-license-ids": {
- "version": "3.0.20",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz",
- "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==",
- "license": "CC0-1.0"
- },
- "node_modules/split-string": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
- "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
- "license": "MIT",
- "dependencies": {
- "extend-shallow": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/split-string/node_modules/extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
- "license": "MIT",
- "dependencies": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/split-string/node_modules/is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.4"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/split-string/node_modules/is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
- "license": "BSD-3-Clause"
- },
- "node_modules/stack-trace": {
- "version": "0.0.10",
- "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
- "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/stat-mode": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.2.2.tgz",
- "integrity": "sha512-o+7DC0OM5Jt3+gratXXqfXf62V/CBoqQbT7Kp7jCxTYW2PLOB2/ZSGIfm9T5/QZe1Vw1MCbu6DoB6JnhVtxcJw==",
- "license": "MIT"
- },
- "node_modules/static-extend": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
- "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==",
- "license": "MIT",
- "dependencies": {
- "define-property": "^0.2.5",
- "object-copy": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/static-extend/node_modules/define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/static-extend/node_modules/is-descriptor": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz",
- "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==",
- "license": "MIT",
- "dependencies": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/stream-exhaust": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz",
- "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==",
- "license": "MIT"
- },
- "node_modules/stream-shift": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
- "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
- "license": "MIT"
- },
- "node_modules/string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "~5.1.0"
- }
- },
- "node_modules/string_decoder/node_modules/safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "license": "MIT"
- },
- "node_modules/string-width": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
- "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
- "license": "MIT",
- "dependencies": {
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^4.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/string-width-cjs": {
- "name": "string-width",
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/string-width-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/string-width-cjs/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/string-width/node_modules/ansi-regex": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
- "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/string-width/node_modules/strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/stringify-object": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
- "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "get-own-enumerable-property-symbols": "^3.0.0",
- "is-obj": "^1.0.1",
- "is-regexp": "^1.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/strip-ansi-cjs": {
- "name": "strip-ansi",
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-bom": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
- "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==",
- "license": "MIT",
- "dependencies": {
- "is-utf8": "^0.2.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/strip-bom-buf": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz",
- "integrity": "sha512-1sUIL1jck0T1mhOLP2c696BIznzT525Lkub+n4jjMHjhjhoAQA6Ye659DxdlZBr0aLDMQoTxKIpnlqxgtwjsuQ==",
- "license": "MIT",
- "dependencies": {
- "is-utf8": "^0.2.1"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/strip-bom-stream": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-3.0.0.tgz",
- "integrity": "sha512-2di6sulSHfspbuEJHwwF6vzwijA4uaKsKYtviRQsJsOdxxb6yexiDcZFQ5oY10J50YxmCdHn/1sQmxDKbrGOVw==",
- "license": "MIT",
- "dependencies": {
- "first-chunk-stream": "^2.0.0",
- "strip-bom-buf": "^1.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/strip-eof": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
- "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/strip-json-comments": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
- "license": "MIT",
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/supports-preserve-symlinks-flag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/sver-compat": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz",
- "integrity": "sha512-aFTHfmjwizMNlNE6dsGmoAM4lHjL0CyiobWaFiXWSlD7cIxshW422Nb8KbXCmR6z+0ZEPY+daXJrDyh/vuwTyg==",
- "license": "MIT",
- "dependencies": {
- "es6-iterator": "^2.0.1",
- "es6-symbol": "^3.1.1"
- }
- },
- "node_modules/ternary-stream": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-2.1.1.tgz",
- "integrity": "sha512-j6ei9hxSoyGlqTmoMjOm+QNvUKDOIY6bNl4Uh1lhBvl6yjPW2iLqxDUYyfDPZknQ4KdRziFl+ec99iT4l7g0cw==",
- "license": "MIT",
- "dependencies": {
- "duplexify": "^3.5.0",
- "fork-stream": "^0.0.4",
- "merge-stream": "^1.0.0",
- "through2": "^2.0.1"
- },
- "engines": {
- "node": ">= 0.10.0"
- }
- },
- "node_modules/ternary-stream/node_modules/merge-stream": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz",
- "integrity": "sha512-e6RM36aegd4f+r8BZCcYXlO2P3H6xbUM6ktL2Xmf45GAOit9bI4z6/3VU7JwllVO1L7u0UDSg/EhzQ5lmMLolA==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "^2.0.1"
- }
- },
- "node_modules/ternary-stream/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/textextensions": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-3.3.0.tgz",
- "integrity": "sha512-mk82dS8eRABNbeVJrEiN5/UMSCliINAuz8mkUwH4SwslkNP//gbEzlWNS5au0z5Dpx40SQxzqZevZkn+WYJ9Dw==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://bevry.me/fund"
- }
- },
- "node_modules/through": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
- "license": "MIT"
- },
- "node_modules/through2": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
- "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
- "license": "MIT",
- "dependencies": {
- "inherits": "^2.0.4",
- "readable-stream": "2 || 3"
- }
- },
- "node_modules/through2-filter": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz",
- "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==",
- "license": "MIT",
- "dependencies": {
- "through2": "~2.0.0",
- "xtend": "~4.0.0"
- }
- },
- "node_modules/through2-filter/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/tildify": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz",
- "integrity": "sha512-Y9q1GaV/BO65Z9Yf4NOGMuwt3SGdptkZBnaaKfTQakrDyCLiuO1Kc5wxW4xLdsjzunRtqtOdhekiUFmZbklwYQ==",
- "license": "MIT",
- "dependencies": {
- "os-homedir": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/time-stamp": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz",
- "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/tmp": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
- "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
- "license": "MIT",
- "dependencies": {
- "os-tmpdir": "~1.0.2"
- },
- "engines": {
- "node": ">=0.6.0"
- }
- },
- "node_modules/to-absolute-glob": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
- "integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==",
- "license": "MIT",
- "dependencies": {
- "is-absolute": "^1.0.0",
- "is-negated-glob": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/to-object-path": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
- "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
- "license": "MIT",
- "dependencies": {
- "kind-of": "^3.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/to-object-path/node_modules/kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "license": "MIT",
- "dependencies": {
- "is-buffer": "^1.1.5"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/to-regex": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
- "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
- "license": "MIT",
- "dependencies": {
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "regex-not": "^1.0.2",
- "safe-regex": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/to-regex-range": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
- "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
- "license": "MIT",
- "dependencies": {
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/to-regex/node_modules/extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
- "license": "MIT",
- "dependencies": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/to-regex/node_modules/is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.4"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/to-regex/node_modules/is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/to-through": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz",
- "integrity": "sha512-+QIz37Ly7acM4EMdw2PRN389OneM5+d844tirkGp4dPKzI5OE72V9OsbFp+CIYJDahZ41ZV05hNtcPAQUAm9/Q==",
- "license": "MIT",
- "dependencies": {
- "through2": "^2.0.3"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/to-through/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
- "license": "MIT"
- },
- "node_modules/tslib": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
- "license": "0BSD"
- },
- "node_modules/type": {
- "version": "2.7.3",
- "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz",
- "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==",
- "license": "ISC"
- },
- "node_modules/typedarray": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
- "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
- "license": "MIT"
- },
- "node_modules/uglify-js": {
- "version": "2.8.29",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
- "integrity": "sha512-qLq/4y2pjcU3vhlhseXGGJ7VbFO4pBANu0kwl8VCa9KEI0V8VfZIx2Fy3w01iSTA/pGwKZSmu/+I4etLNDdt5w==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "source-map": "~0.5.1",
- "yargs": "~3.10.0"
- },
- "bin": {
- "uglifyjs": "bin/uglifyjs"
- },
- "engines": {
- "node": ">=0.8.0"
- },
- "optionalDependencies": {
- "uglify-to-browserify": "~1.0.0"
- }
- },
- "node_modules/uglify-js/node_modules/camelcase": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
- "integrity": "sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/uglify-js/node_modules/cliui": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
- "integrity": "sha512-GIOYRizG+TGoc7Wgc1LiOTLare95R3mzKgoln+Q/lE4ceiYH19gUpl0l0Ffq4lJDEf3FxujMe6IBfOCs7pfqNA==",
- "license": "ISC",
- "dependencies": {
- "center-align": "^0.1.1",
- "right-align": "^0.1.1",
- "wordwrap": "0.0.2"
- }
- },
- "node_modules/uglify-js/node_modules/source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/uglify-js/node_modules/yargs": {
- "version": "3.10.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
- "integrity": "sha512-QFzUah88GAGy9lyDKGBqZdkYApt63rCXYBGYnEP4xDJPXNqXXnBDACnbrXnViV6jRSqAePwrATi2i8mfYm4L1A==",
- "license": "MIT",
- "dependencies": {
- "camelcase": "^1.0.2",
- "cliui": "^2.1.0",
- "decamelize": "^1.0.0",
- "window-size": "0.1.0"
- }
- },
- "node_modules/uglify-to-browserify": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
- "integrity": "sha512-vb2s1lYx2xBtUgy+ta+b2J/GLVUR+wmpINwHePmPRhOsIVCG2wDzKJ0n14GslH1BifsqVzSOwQhRaCAsZ/nI4Q==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/unc-path-regex": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
- "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/undertaker": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz",
- "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==",
- "license": "MIT",
- "dependencies": {
- "arr-flatten": "^1.0.1",
- "arr-map": "^2.0.0",
- "bach": "^1.0.0",
- "collection-map": "^1.0.0",
- "es6-weak-map": "^2.0.1",
- "fast-levenshtein": "^1.0.0",
- "last-run": "^1.1.0",
- "object.defaults": "^1.0.0",
- "object.reduce": "^1.0.0",
- "undertaker-registry": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/undertaker-registry": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz",
- "integrity": "sha512-UR1khWeAjugW3548EfQmL9Z7pGMlBgXteQpr1IZeZBtnkCJQJIJ1Scj0mb9wQaPvUZ9Q17XqW6TIaPchJkyfqw==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/undici-types": {
- "version": "6.20.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
- "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
- "license": "MIT"
- },
- "node_modules/union-value": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
- "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
- "license": "MIT",
- "dependencies": {
- "arr-union": "^3.1.0",
- "get-value": "^2.0.6",
- "is-extendable": "^0.1.1",
- "set-value": "^2.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/unique-stream": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz",
- "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==",
- "license": "MIT",
- "dependencies": {
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "through2-filter": "^3.0.0"
- }
- },
- "node_modules/universal-user-agent": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.1.tgz",
- "integrity": "sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg==",
- "license": "ISC",
- "dependencies": {
- "os-name": "^3.1.0"
- }
- },
- "node_modules/unset-value": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
- "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==",
- "license": "MIT",
- "dependencies": {
- "has-value": "^0.3.1",
- "isobject": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/unset-value/node_modules/has-value": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
- "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
- "license": "MIT",
- "dependencies": {
- "get-value": "^2.0.3",
- "has-values": "^0.1.4",
- "isobject": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/unset-value/node_modules/has-value/node_modules/isobject": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
- "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
- "license": "MIT",
- "dependencies": {
- "isarray": "1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/unset-value/node_modules/has-values": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
- "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/upath": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
- "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
- "license": "MIT",
- "engines": {
- "node": ">=4",
- "yarn": "*"
- }
- },
- "node_modules/update-browserslist-db": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
- "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "escalade": "^3.2.0",
- "picocolors": "^1.1.0"
- },
- "bin": {
- "update-browserslist-db": "cli.js"
- },
- "peerDependencies": {
- "browserslist": ">= 4.21.0"
- }
- },
- "node_modules/update-browserslist-db/node_modules/picocolors": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "license": "ISC"
- },
- "node_modules/urix": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
- "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==",
- "deprecated": "Please see https://github.com/lydell/urix#deprecated",
- "license": "MIT"
- },
- "node_modules/url-regex": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/url-regex/-/url-regex-3.2.0.tgz",
- "integrity": "sha512-dQ9cJzMou5OKr6ZzfvwJkCq3rC72PNXhqz0v3EIhF4a3Np+ujr100AhUx2cKx5ei3iymoJpJrPB3sVSEMdqAeg==",
- "license": "MIT",
- "dependencies": {
- "ip-regex": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/use": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
- "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "license": "MIT"
- },
- "node_modules/v8flags": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz",
- "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==",
- "license": "MIT",
- "dependencies": {
- "homedir-polyfill": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/validate-npm-package-license": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
- "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
- "license": "Apache-2.0",
- "dependencies": {
- "spdx-correct": "^3.0.0",
- "spdx-expression-parse": "^3.0.0"
- }
- },
- "node_modules/value-or-function": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz",
- "integrity": "sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/vinyl": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz",
- "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==",
- "license": "MIT",
- "dependencies": {
- "clone": "^2.1.1",
- "clone-buffer": "^1.0.0",
- "clone-stats": "^1.0.0",
- "cloneable-readable": "^1.0.0",
- "remove-trailing-separator": "^1.0.1",
- "replace-ext": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/vinyl-fs": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz",
- "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==",
- "license": "MIT",
- "dependencies": {
- "fs-mkdirp-stream": "^1.0.0",
- "glob-stream": "^6.1.0",
- "graceful-fs": "^4.0.0",
- "is-valid-glob": "^1.0.0",
- "lazystream": "^1.0.0",
- "lead": "^1.0.0",
- "object.assign": "^4.0.4",
- "pumpify": "^1.3.5",
- "readable-stream": "^2.3.3",
- "remove-bom-buffer": "^3.0.0",
- "remove-bom-stream": "^1.2.0",
- "resolve-options": "^1.1.0",
- "through2": "^2.0.0",
- "to-through": "^2.0.0",
- "value-or-function": "^3.0.0",
- "vinyl": "^2.0.0",
- "vinyl-sourcemap": "^1.1.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/vinyl-fs/node_modules/through2": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
- "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
- "license": "MIT",
- "dependencies": {
- "readable-stream": "~2.3.6",
- "xtend": "~4.0.1"
- }
- },
- "node_modules/vinyl-sourcemap": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz",
- "integrity": "sha512-NiibMgt6VJGJmyw7vtzhctDcfKch4e4n9TBeoWlirb7FMg9/1Ov9k+A5ZRAtywBpRPiyECvQRQllYM8dECegVA==",
- "license": "MIT",
- "dependencies": {
- "append-buffer": "^1.0.2",
- "convert-source-map": "^1.5.0",
- "graceful-fs": "^4.1.6",
- "normalize-path": "^2.1.1",
- "now-and-later": "^2.0.0",
- "remove-bom-buffer": "^3.0.0",
- "vinyl": "^2.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/vinyl-sourcemap/node_modules/normalize-path": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
- "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
- "license": "MIT",
- "dependencies": {
- "remove-trailing-separator": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/vinyl-sourcemaps-apply": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz",
- "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==",
- "license": "ISC",
- "dependencies": {
- "source-map": "^0.5.1"
- }
- },
- "node_modules/vinyl-sourcemaps-apply/node_modules/source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/webidl-conversions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
- "license": "BSD-2-Clause"
- },
- "node_modules/whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "license": "MIT",
- "dependencies": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
- }
- },
- "node_modules/when": {
- "version": "3.7.8",
- "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz",
- "integrity": "sha512-5cZ7mecD3eYcMiCH4wtRPA5iFJZ50BJYDfckI5RRpQiktMiYTcn0ccLTZOvcbBume+1304fQztxeNzNS9Gvrnw==",
- "license": "MIT"
- },
- "node_modules/which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "license": "ISC",
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "which": "bin/which"
- }
- },
- "node_modules/which-module": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
- "integrity": "sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==",
- "license": "ISC"
- },
- "node_modules/window-size": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
- "integrity": "sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg==",
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/windows-release": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.3.tgz",
- "integrity": "sha512-OSOGH1QYiW5yVor9TtmXKQvt2vjQqbYS+DqmsZw+r7xDwLXEeT3JGW0ZppFmHx4diyXmxt238KFR3N9jzevBRg==",
- "license": "MIT",
- "dependencies": {
- "execa": "^1.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/wordwrap": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
- "integrity": "sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q==",
- "license": "MIT/X11",
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/wrap-ansi": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
- "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==",
- "license": "MIT",
- "dependencies": {
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/wrap-ansi-cjs": {
- "name": "wrap-ansi",
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "license": "MIT",
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/wrap-ansi-cjs/node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "license": "MIT",
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/wrap-ansi-cjs/node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "license": "MIT"
- },
- "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wrap-ansi-cjs/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
- "license": "MIT",
- "dependencies": {
- "number-is-nan": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/wrap-ansi/node_modules/string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
- "license": "MIT",
- "dependencies": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "license": "ISC"
- },
- "node_modules/wrench-sui": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/wrench-sui/-/wrench-sui-0.0.3.tgz",
- "integrity": "sha512-Y6qzMpcMG9akKnIdUsKzEF/Ht0KQJBP8ETkZj3FcGe93NC71e940WZUP1y+j+hc8Ecx9TyX0GvAWC4yymA88yA==",
- "engines": {
- "node": ">=0.1.97"
- }
- },
- "node_modules/xtend": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
- "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
- "license": "MIT",
- "engines": {
- "node": ">=0.4"
- }
- },
- "node_modules/y18n": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz",
- "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==",
- "license": "ISC"
- },
- "node_modules/yamljs": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz",
- "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==",
- "license": "MIT",
- "dependencies": {
- "argparse": "^1.0.7",
- "glob": "^7.0.5"
- },
- "bin": {
- "json2yaml": "bin/json2yaml",
- "yaml2json": "bin/yaml2json"
- }
- },
- "node_modules/yargs": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz",
- "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==",
- "license": "MIT",
- "dependencies": {
- "camelcase": "^3.0.0",
- "cliui": "^3.2.0",
- "decamelize": "^1.1.1",
- "get-caller-file": "^1.0.1",
- "os-locale": "^1.4.0",
- "read-pkg-up": "^1.0.1",
- "require-directory": "^2.1.1",
- "require-main-filename": "^1.0.1",
- "set-blocking": "^2.0.0",
- "string-width": "^1.0.2",
- "which-module": "^1.0.0",
- "y18n": "^3.2.1",
- "yargs-parser": "^5.0.1"
- }
- },
- "node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "license": "ISC",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/yargs/node_modules/is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
- "license": "MIT",
- "dependencies": {
- "number-is-nan": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/yargs/node_modules/string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
- "license": "MIT",
- "dependencies": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/yargs/node_modules/yargs-parser": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz",
- "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==",
- "license": "ISC",
- "dependencies": {
- "camelcase": "^3.0.0",
- "object.assign": "^4.1.0"
- }
- }
- }
-}
diff --git a/web_src/fomantic/package.json b/web_src/fomantic/package.json
deleted file mode 100644
index c031c070c5..0000000000
--- a/web_src/fomantic/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "dependencies": {
- "fomantic-ui": "2.8.7"
- }
-}
diff --git a/web_src/fomantic/theme.config.less b/web_src/fomantic/theme.config.less
index b92399409d..632ddce07f 100644
--- a/web_src/fomantic/theme.config.less
+++ b/web_src/fomantic/theme.config.less
@@ -1,21 +1,4 @@
-/*
-
-████████╗██╗ ██╗███████╗███╗ ███╗███████╗███████╗
-╚â•â•██╔â•â•â•██║ ██║██╔â•â•â•â•â•████╗ ████║██╔â•â•â•â•â•██╔â•â•â•â•â•
- ██║ ███████║█████╗ ██╔████╔██║█████╗ ███████╗
- ██║ ██╔â•â•██║██╔â•â•╠██║╚██╔â•██║██╔â•â•╠╚â•â•â•â•██║
- ██║ ██║ ██║███████╗██║ ╚â•╠██║███████╗███████║
- ╚â•╠╚â•╠╚â•â•╚â•â•â•â•â•â•â•╚â•╠╚â•â•╚â•â•â•â•â•â•â•╚â•â•â•â•â•â•â•
-
-*/
-
-/*******************************
- Theme Selection
-*******************************/
-
-/* To override a theme for an individual element
- specify theme name below
-*/
+/* To override a theme for an individual element, specify theme name below */
/* Global */
@site : 'default';
diff --git a/web_src/js/bootstrap.ts b/web_src/js/bootstrap.ts
index 9e41673b86..4d3f39f5bf 100644
--- a/web_src/js/bootstrap.ts
+++ b/web_src/js/bootstrap.ts
@@ -2,6 +2,7 @@
// to make sure the error handler always works, we should never import `window.config`, because
// some user's custom template breaks it.
import type {Intent} from './types.ts';
+import {html} from './utils/html.ts';
// This sets up the URL prefix used in webpack's chunk loading.
// This file must be imported before any lazy-loading is being attempted.
@@ -19,11 +20,15 @@ function shouldIgnoreError(err: Error) {
export function showGlobalErrorMessage(msg: string, msgType: Intent = 'error') {
const msgContainer = document.querySelector('.page-content') ?? document.body;
+ if (!msgContainer) {
+ alert(`${msgType}: ${msg}`);
+ return;
+ }
const msgCompact = msg.replace(/\W/g, '').trim(); // compact the message to a data attribute to avoid too many duplicated messages
let msgDiv = msgContainer.querySelector<HTMLDivElement>(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`);
if (!msgDiv) {
const el = document.createElement('div');
- el.innerHTML = `<div class="ui container js-global-error tw-my-[--page-spacing]"><div class="ui ${msgType} message tw-text-center tw-whitespace-pre-line"></div></div>`;
+ el.innerHTML = html`<div class="ui container js-global-error tw-my-[--page-spacing]"><div class="ui ${msgType} message tw-text-center tw-whitespace-pre-line"></div></div>`;
msgDiv = el.childNodes[0] as HTMLDivElement;
}
// merge duplicated messages into "the message (count)" format
diff --git a/web_src/js/components/ActionRunStatus.vue b/web_src/js/components/ActionRunStatus.vue
index 96c6c441be..bc3b99ab89 100644
--- a/web_src/js/components/ActionRunStatus.vue
+++ b/web_src/js/components/ActionRunStatus.vue
@@ -19,12 +19,12 @@ withDefaults(defineProps<{
<template>
<span :data-tooltip-content="localeStatus ?? status" v-if="status">
- <SvgIcon name="octicon-check-circle-fill" class="text green" :size="size" :class-name="className" v-if="status === 'success'"/>
- <SvgIcon name="octicon-skip" class="text grey" :size="size" :class-name="className" v-else-if="status === 'skipped'"/>
- <SvgIcon name="octicon-stop" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'cancelled'"/>
- <SvgIcon name="octicon-clock" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'waiting'"/>
- <SvgIcon name="octicon-blocked" class="text yellow" :size="size" :class-name="className" v-else-if="status === 'blocked'"/>
- <SvgIcon name="octicon-meter" class="text yellow" :size="size" :class-name="'job-status-rotate ' + className" v-else-if="status === 'running'"/>
+ <SvgIcon name="octicon-check-circle-fill" class="text green" :size="size" :class="className" v-if="status === 'success'"/>
+ <SvgIcon name="octicon-skip" class="text grey" :size="size" :class="className" v-else-if="status === 'skipped'"/>
+ <SvgIcon name="octicon-stop" class="text yellow" :size="size" :class="className" v-else-if="status === 'cancelled'"/>
+ <SvgIcon name="octicon-clock" class="text yellow" :size="size" :class="className" v-else-if="status === 'waiting'"/>
+ <SvgIcon name="octicon-blocked" class="text yellow" :size="size" :class="className" v-else-if="status === 'blocked'"/>
+ <SvgIcon name="octicon-meter" class="text yellow" :size="size" :class="'circular-spin ' + className" v-else-if="status === 'running'"/>
<SvgIcon name="octicon-x-circle-fill" class="text red" :size="size" v-else/><!-- failure, unknown -->
</span>
</template>
diff --git a/web_src/js/components/ActivityHeatmap.vue b/web_src/js/components/ActivityHeatmap.vue
index eaa9b0ffb1..296cb61cff 100644
--- a/web_src/js/components/ActivityHeatmap.vue
+++ b/web_src/js/components/ActivityHeatmap.vue
@@ -1,7 +1,7 @@
<script lang="ts" setup>
// TODO: Switch to upstream after https://github.com/razorness/vue3-calendar-heatmap/pull/34 is merged
import {CalendarHeatmap} from '@silverwind/vue3-calendar-heatmap';
-import {onMounted, ref} from 'vue';
+import {onMounted, shallowRef} from 'vue';
import type {Value as HeatmapValue, Locale as HeatmapLocale} from '@silverwind/vue3-calendar-heatmap';
defineProps<{
@@ -24,7 +24,7 @@ const colorRange = [
'var(--color-primary-dark-4)',
];
-const endDate = ref(new Date());
+const endDate = shallowRef(new Date());
onMounted(() => {
// work around issue with first legend color being rendered twice and legend cut off
diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue
index 0aae202d42..5ec4499e48 100644
--- a/web_src/js/components/ContextPopup.vue
+++ b/web_src/js/components/ContextPopup.vue
@@ -2,16 +2,16 @@
import {SvgIcon} from '../svg.ts';
import {GET} from '../modules/fetch.ts';
import {getIssueColor, getIssueIcon} from '../features/issue.ts';
-import {computed, onMounted, ref} from 'vue';
+import {computed, onMounted, shallowRef, useTemplateRef} from 'vue';
import type {IssuePathInfo} from '../types.ts';
const {appSubUrl, i18n} = window.config;
-const loading = ref(false);
-const issue = ref(null);
-const renderedLabels = ref('');
+const loading = shallowRef(false);
+const issue = shallowRef(null);
+const renderedLabels = shallowRef('');
const i18nErrorOccurred = i18n.error_occurred;
-const i18nErrorMessage = ref(null);
+const i18nErrorMessage = shallowRef(null);
const createdAt = computed(() => new Date(issue.value.created_at).toLocaleDateString(undefined, {year: 'numeric', month: 'short', day: 'numeric'}));
const body = computed(() => {
@@ -22,7 +22,7 @@ const body = computed(() => {
return body;
});
-const root = ref<HTMLElement | null>(null);
+const root = useTemplateRef('root');
onMounted(() => {
root.value.addEventListener('ce-load-context-popup', (e: CustomEventInit<IssuePathInfo>) => {
diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue
index 41793d60ed..e938814ec6 100644
--- a/web_src/js/components/DashboardRepoList.vue
+++ b/web_src/js/components/DashboardRepoList.vue
@@ -1,12 +1,12 @@
<script lang="ts">
-import {createApp, nextTick} from 'vue';
+import {nextTick, defineComponent} from 'vue';
import {SvgIcon} from '../svg.ts';
import {GET} from '../modules/fetch.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
const {appSubUrl, assetUrlPrefix, pageData} = window.config;
-type CommitStatus = 'pending' | 'success' | 'error' | 'failure' | 'warning';
+type CommitStatus = 'pending' | 'success' | 'error' | 'failure' | 'warning' | 'skipped';
type CommitStatusMap = {
[status in CommitStatus]: {
@@ -22,9 +22,10 @@ const commitStatus: CommitStatusMap = {
error: {name: 'gitea-exclamation', color: 'red'},
failure: {name: 'octicon-x', color: 'red'},
warning: {name: 'gitea-exclamation', color: 'yellow'},
+ skipped: {name: 'octicon-skip', color: 'grey'},
};
-const sfc = {
+export default defineComponent({
components: {SvgIcon},
data() {
const params = new URLSearchParams(window.location.search);
@@ -38,7 +39,7 @@ const sfc = {
return {
tab,
repos: [],
- reposTotalCount: 0,
+ reposTotalCount: null,
reposFilter,
archivedFilter,
privateFilter,
@@ -112,9 +113,6 @@ const sfc = {
const el = document.querySelector('#dashboard-repo-list');
this.changeReposFilter(this.reposFilter);
fomanticQuery(el.querySelector('.ui.dropdown')).dropdown();
- nextTick(() => {
- this.$refs.search.focus();
- });
this.textArchivedFilterTitles = {
'archived': this.textShowOnlyArchived,
@@ -130,12 +128,12 @@ const sfc = {
},
methods: {
- changeTab(t) {
- this.tab = t;
+ changeTab(tab: string) {
+ this.tab = tab;
this.updateHistory();
},
- changeReposFilter(filter) {
+ changeReposFilter(filter: string) {
this.reposFilter = filter;
this.repos = [];
this.page = 1;
@@ -218,7 +216,9 @@ const sfc = {
this.searchRepos();
},
- changePage(page) {
+ async changePage(page: number) {
+ if (this.isLoading) return;
+
this.page = page;
if (this.page > this.finalPage) {
this.page = this.finalPage;
@@ -228,7 +228,7 @@ const sfc = {
}
this.repos = [];
this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
- this.searchRepos();
+ await this.searchRepos();
},
async searchRepos() {
@@ -240,12 +240,20 @@ const sfc = {
let response, json;
try {
+ const firstLoad = this.reposTotalCount === null;
if (!this.reposTotalCount) {
const totalCountSearchURL = `${this.subUrl}/repo/search?count_only=1&uid=${this.uid}&team_id=${this.teamId}&q=&page=1&mode=`;
response = await GET(totalCountSearchURL);
- this.reposTotalCount = response.headers.get('X-Total-Count') ?? '?';
+ this.reposTotalCount = parseInt(response.headers.get('X-Total-Count') ?? '0');
+ }
+ if (firstLoad && this.reposTotalCount) {
+ nextTick(() => {
+ // MDN: If there's no focused element, this is the Document.body or Document.documentElement.
+ if ((document.activeElement === document.body || document.activeElement === document.documentElement)) {
+ this.$refs.search.focus({preventScroll: true});
+ }
+ });
}
-
response = await GET(searchedURL);
json = await response.json();
} catch {
@@ -256,7 +264,7 @@ const sfc = {
}
if (searchedURL === this.searchURL) {
- this.repos = json.data.map((webSearchRepo) => {
+ this.repos = json.data.map((webSearchRepo: any) => {
return {
...webSearchRepo.repository,
latest_commit_status_state: webSearchRepo.latest_commit_status?.State, // if latest_commit_status is null, it means there is no commit status
@@ -264,7 +272,7 @@ const sfc = {
locale_latest_commit_status_state: webSearchRepo.locale_latest_commit_status,
};
});
- const count = response.headers.get('X-Total-Count');
+ const count = Number(response.headers.get('X-Total-Count'));
if (searchedQuery === '' && searchedMode === '' && this.archivedFilter === 'both') {
this.reposTotalCount = count;
}
@@ -275,7 +283,7 @@ const sfc = {
}
},
- repoIcon(repo) {
+ repoIcon(repo: any) {
if (repo.fork) {
return 'octicon-repo-forked';
} else if (repo.mirror) {
@@ -298,7 +306,7 @@ const sfc = {
return commitStatus[status].color;
},
- reposFilterKeyControl(e) {
+ async reposFilterKeyControl(e: KeyboardEvent) {
switch (e.key) {
case 'Enter':
document.querySelector<HTMLAnchorElement>('.repo-owner-name-list li.active a')?.click();
@@ -307,7 +315,7 @@ const sfc = {
if (this.activeIndex > 0) {
this.activeIndex--;
} else if (this.page > 1) {
- this.changePage(this.page - 1);
+ await this.changePage(this.page - 1);
this.activeIndex = this.searchLimit - 1;
}
break;
@@ -316,17 +324,17 @@ const sfc = {
this.activeIndex++;
} else if (this.page < this.finalPage) {
this.activeIndex = 0;
- this.changePage(this.page + 1);
+ await this.changePage(this.page + 1);
}
break;
case 'ArrowRight':
if (this.page < this.finalPage) {
- this.changePage(this.page + 1);
+ await this.changePage(this.page + 1);
}
break;
case 'ArrowLeft':
if (this.page > 1) {
- this.changePage(this.page - 1);
+ await this.changePage(this.page - 1);
}
break;
}
@@ -335,16 +343,7 @@ const sfc = {
}
},
},
-};
-
-export function initDashboardRepoList() {
- const el = document.querySelector('#dashboard-repo-list');
- if (el) {
- createApp(sfc).mount(el);
- }
-}
-
-export default sfc; // activate the IDE's Vue plugin
+});
</script>
<template>
<div>
@@ -356,13 +355,21 @@ export default sfc; // activate the IDE's Vue plugin
<h4 class="ui top attached header tw-flex tw-items-center">
<div class="tw-flex-1 tw-flex tw-items-center">
{{ textMyRepos }}
- <span class="ui grey label tw-ml-2">{{ reposTotalCount }}</span>
+ <span v-if="reposTotalCount" class="ui grey label tw-ml-2">{{ reposTotalCount }}</span>
</div>
<a class="tw-flex tw-items-center muted" :href="subUrl + '/repo/create' + (isOrganization ? '?org=' + organizationId : '')" :data-tooltip-content="textNewRepo">
<svg-icon name="octicon-plus"/>
</a>
</h4>
- <div class="ui attached segment repos-search">
+ <div v-if="!reposTotalCount" class="ui attached segment">
+ <div v-if="!isLoading" class="empty-repo-or-org">
+ <svg-icon name="octicon-git-branch" :size="24"/>
+ <p>{{ textNoRepo }}</p>
+ </div>
+ <!-- using the loading indicator here will cause more (unnecessary) page flickers, so at the moment, not use the loading indicator -->
+ <!-- <div v-else class="is-loading loading-icon-2px tw-min-h-16"/> -->
+ </div>
+ <div v-else class="ui attached segment repos-search">
<div class="ui small fluid action left icon input">
<input type="search" spellcheck="false" maxlength="255" @input="changeReposFilter(reposFilter)" v-model="searchQuery" ref="search" @keydown="reposFilterKeyControl" :placeholder="textSearchRepos">
<i class="icon loading-icon-3px" :class="{'is-loading': isLoading}"><svg-icon name="octicon-search" :size="16"/></i>
@@ -375,7 +382,7 @@ export default sfc; // activate the IDE's Vue plugin
otherwise if the "input" handles click event for intermediate status, it breaks the internal state-->
<input type="checkbox" class="tw-pointer-events-none" v-bind.prop="checkboxArchivedFilterProps">
<label>
- <svg-icon name="octicon-archive" :size="16" class-name="tw-mr-1"/>
+ <svg-icon name="octicon-archive" :size="16" class="tw-mr-1"/>
{{ textShowArchived }}
</label>
</div>
@@ -384,7 +391,7 @@ export default sfc; // activate the IDE's Vue plugin
<div class="ui checkbox" ref="checkboxPrivateFilter" :title="checkboxPrivateFilterTitle">
<input type="checkbox" class="tw-pointer-events-none" v-bind.prop="checkboxPrivateFilterProps">
<label>
- <svg-icon name="octicon-lock" :size="16" class-name="tw-mr-1"/>
+ <svg-icon name="octicon-lock" :size="16" class="tw-mr-1"/>
{{ textShowPrivate }}
</label>
</div>
@@ -419,17 +426,17 @@ export default sfc; // activate the IDE's Vue plugin
</div>
<div v-if="repos.length" class="ui attached table segment tw-rounded-b">
<ul class="repo-owner-name-list">
- <li class="tw-flex tw-items-center tw-py-2" v-for="repo, index in repos" :class="{'active': index === activeIndex}" :key="repo.id">
+ <li class="tw-flex tw-items-center tw-py-2" v-for="(repo, index) in repos" :class="{'active': index === activeIndex}" :key="repo.id">
<a class="repo-list-link muted" :href="repo.link">
- <svg-icon :name="repoIcon(repo)" :size="16" class-name="repo-list-icon"/>
+ <svg-icon :name="repoIcon(repo)" :size="16" class="repo-list-icon"/>
<div class="text truncate">{{ repo.full_name }}</div>
<div v-if="repo.archived">
<svg-icon name="octicon-archive" :size="16"/>
</div>
</a>
- <a class="tw-flex tw-items-center" v-if="repo.latest_commit_status_state" :href="repo.latest_commit_status_state_link" :data-tooltip-content="repo.locale_latest_commit_status_state">
+ <a class="tw-flex tw-items-center" v-if="repo.latest_commit_status_state" :href="repo.latest_commit_status_state_link || null" :data-tooltip-content="repo.locale_latest_commit_status_state">
<!-- the commit status icon logic is taken from templates/repo/commit_status.tmpl -->
- <svg-icon :name="statusIcon(repo.latest_commit_status_state)" :class-name="'tw-ml-2 commit-status icon text ' + statusColor(repo.latest_commit_status_state)" :size="16"/>
+ <svg-icon :name="statusIcon(repo.latest_commit_status_state)" :class="'tw-ml-2 commit-status icon text ' + statusColor(repo.latest_commit_status_state)" :size="16"/>
</a>
</li>
</ul>
@@ -440,26 +447,26 @@ export default sfc; // activate the IDE's Vue plugin
class="item navigation tw-py-1" :class="{'disabled': page === 1}"
@click="changePage(1)" :title="textFirstPage"
>
- <svg-icon name="gitea-double-chevron-left" :size="16" class-name="tw-mr-1"/>
+ <svg-icon name="gitea-double-chevron-left" :size="16" class="tw-mr-1"/>
</a>
<a
class="item navigation tw-py-1" :class="{'disabled': page === 1}"
@click="changePage(page - 1)" :title="textPreviousPage"
>
- <svg-icon name="octicon-chevron-left" :size="16" clsas-name="tw-mr-1"/>
+ <svg-icon name="octicon-chevron-left" :size="16" class="tw-mr-1"/>
</a>
<a class="active item tw-py-1">{{ page }}</a>
<a
class="item navigation" :class="{'disabled': page === finalPage}"
@click="changePage(page + 1)" :title="textNextPage"
>
- <svg-icon name="octicon-chevron-right" :size="16" class-name="tw-ml-1"/>
+ <svg-icon name="octicon-chevron-right" :size="16" class="tw-ml-1"/>
</a>
<a
class="item navigation tw-py-1" :class="{'disabled': page === finalPage}"
@click="changePage(finalPage)" :title="textLastPage"
>
- <svg-icon name="gitea-double-chevron-right" :size="16" class-name="tw-ml-1"/>
+ <svg-icon name="gitea-double-chevron-right" :size="16" class="tw-ml-1"/>
</a>
</div>
</div>
@@ -475,11 +482,17 @@ export default sfc; // activate the IDE's Vue plugin
<svg-icon name="octicon-plus"/>
</a>
</h4>
- <div v-if="organizations.length" class="ui attached table segment tw-rounded-b">
+ <div v-if="!organizations.length" class="ui attached segment">
+ <div class="empty-repo-or-org">
+ <svg-icon name="octicon-organization" :size="24"/>
+ <p>{{ textNoOrg }}</p>
+ </div>
+ </div>
+ <div v-else class="ui attached table segment tw-rounded-b">
<ul class="repo-owner-name-list">
<li class="tw-flex tw-items-center tw-py-2" v-for="org in organizations" :key="org.name">
<a class="repo-list-link muted" :href="subUrl + '/' + encodeURIComponent(org.name)">
- <svg-icon name="octicon-organization" :size="16" class-name="repo-list-icon"/>
+ <svg-icon name="octicon-organization" :size="16" class="repo-list-icon"/>
<div class="text truncate">{{ org.full_name ? `${org.full_name} (${org.name})` : org.name }}</div>
<div><!-- div to prevent underline of label on hover -->
<span class="ui tiny basic label" v-if="org.org_visibility !== 'public'">
@@ -489,7 +502,7 @@ export default sfc; // activate the IDE's Vue plugin
</a>
<div class="text light grey tw-flex tw-items-center tw-ml-2">
{{ org.num_repos }}
- <svg-icon name="octicon-repo" :size="16" class-name="tw-ml-1 tw-mt-0.5"/>
+ <svg-icon name="octicon-repo" :size="16" class="tw-ml-1 tw-mt-0.5"/>
</div>
</li>
</ul>
@@ -554,4 +567,14 @@ ul li:not(:last-child) {
.repo-owner-name-list li.active {
background: var(--color-hover);
}
+
+.empty-repo-or-org {
+ margin-top: 1em;
+ text-align: center;
+ color: var(--color-placeholder-text);
+}
+
+.empty-repo-or-org p {
+ margin: 1em auto;
+}
</style>
diff --git a/web_src/js/components/DiffCommitSelector.vue b/web_src/js/components/DiffCommitSelector.vue
index 3a394955ca..4b18694bd2 100644
--- a/web_src/js/components/DiffCommitSelector.vue
+++ b/web_src/js/components/DiffCommitSelector.vue
@@ -1,9 +1,26 @@
<script lang="ts">
+import {defineComponent} from 'vue';
import {SvgIcon} from '../svg.ts';
import {GET} from '../modules/fetch.ts';
-import {generateAriaId} from '../modules/fomantic/base.ts';
+import {generateElemId} from '../utils/dom.ts';
-export default {
+type Commit = {
+ id: string,
+ hovered: boolean,
+ selected: boolean,
+ summary: string,
+ committer_or_author_name: string,
+ time: string,
+ short_sha: string,
+}
+
+type CommitListResult = {
+ commits: Array<Commit>,
+ last_review_commit_sha: string,
+ locale: Record<string, string>,
+}
+
+export default defineComponent({
components: {SvgIcon},
data: () => {
const el = document.querySelector('#diff-commit-select');
@@ -15,11 +32,11 @@ export default {
locale: {
filter_changes_by_commit: el.getAttribute('data-filter_changes_by_commit'),
} as Record<string, string>,
- commits: [],
+ commits: [] as Array<Commit>,
hoverActivated: false,
- lastReviewCommitSha: null,
- uniqueIdMenu: generateAriaId(),
- uniqueIdShowAll: generateAriaId(),
+ lastReviewCommitSha: '',
+ uniqueIdMenu: generateElemId('diff-commit-selector-menu-'),
+ uniqueIdShowAll: generateElemId('diff-commit-selector-show-all-'),
};
},
computed: {
@@ -55,11 +72,11 @@ export default {
switch (event.key) {
case 'ArrowDown': // select next element
event.preventDefault();
- this.focusElem(item.nextElementSibling, item);
+ this.focusElem(item.nextElementSibling as HTMLElement, item);
break;
case 'ArrowUp': // select previous element
event.preventDefault();
- this.focusElem(item.previousElementSibling, item);
+ this.focusElem(item.previousElementSibling as HTMLElement, item);
break;
case 'Escape': // close menu
event.preventDefault();
@@ -70,7 +87,7 @@ export default {
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
const item = document.activeElement; // try to highlight the selected commits
const commitIdx = item?.matches('.item') ? item.getAttribute('data-commit-idx') : null;
- if (commitIdx) this.highlight(this.commits[commitIdx]);
+ if (commitIdx) this.highlight(this.commits[Number(commitIdx)]);
}
},
onKeyUp(event: KeyboardEvent) {
@@ -86,7 +103,7 @@ export default {
}
}
},
- highlight(commit) {
+ highlight(commit: Commit) {
if (!this.hoverActivated) return;
const indexSelected = this.commits.findIndex((x) => x.selected);
const indexCurrentElem = this.commits.findIndex((x) => x.id === commit.id);
@@ -118,16 +135,17 @@ export default {
// set correct tabindex to allow easier navigation
this.$nextTick(() => {
if (this.menuVisible) {
- this.focusElem(this.$refs.showAllChanges, this.$refs.expandBtn);
+ this.focusElem(this.$refs.showAllChanges as HTMLElement, this.$refs.expandBtn as HTMLElement);
} else {
- this.focusElem(this.$refs.expandBtn, this.$refs.showAllChanges);
+ this.focusElem(this.$refs.expandBtn as HTMLElement, this.$refs.showAllChanges as HTMLElement);
}
});
},
+
/** Load the commits to show in this dropdown */
async fetchCommits() {
const resp = await GET(`${this.issueLink}/commits/list`);
- const results = await resp.json();
+ const results = await resp.json() as CommitListResult;
this.commits.push(...results.commits.map((x) => {
x.hovered = false;
return x;
@@ -165,7 +183,7 @@ export default {
* the diff from beginning of PR up to the second clicked commit is
* opened
*/
- commitClickedShift(commit) {
+ commitClickedShift(commit: Commit) {
this.hoverActivated = !this.hoverActivated;
commit.selected = true;
// Second click -> determine our range and open links accordingly
@@ -188,13 +206,13 @@ export default {
}
},
},
-};
+});
</script>
<template>
<div class="ui scrolling dropdown custom diff-commit-selector">
<button
ref="expandBtn"
- class="ui basic button"
+ class="ui tiny basic button"
@click.stop="toggleMenu()"
:data-tooltip-content="locale.filter_changes_by_commit"
aria-haspopup="true"
diff --git a/web_src/js/components/DiffFileList.vue b/web_src/js/components/DiffFileList.vue
deleted file mode 100644
index 2888c53d2e..0000000000
--- a/web_src/js/components/DiffFileList.vue
+++ /dev/null
@@ -1,60 +0,0 @@
-<script lang="ts" setup>
-import {onMounted, onUnmounted} from 'vue';
-import {loadMoreFiles} from '../features/repo-diff.ts';
-import {diffTreeStore} from '../modules/stores.ts';
-
-const store = diffTreeStore();
-
-onMounted(() => {
- document.querySelector('#show-file-list-btn').addEventListener('click', toggleFileList);
-});
-
-onUnmounted(() => {
- document.querySelector('#show-file-list-btn').removeEventListener('click', toggleFileList);
-});
-
-function toggleFileList() {
- store.fileListIsVisible = !store.fileListIsVisible;
-}
-
-function diffTypeToString(pType) {
- const diffTypes = {
- 1: 'add',
- 2: 'modify',
- 3: 'del',
- 4: 'rename',
- 5: 'copy',
- };
- return diffTypes[pType];
-}
-
-function diffStatsWidth(adds, dels) {
- return `${adds / (adds + dels) * 100}%`;
-}
-
-function loadMoreData() {
- loadMoreFiles(store.linkLoadMore);
-}
-</script>
-
-<template>
- <ol class="diff-stats tw-m-0" ref="root" v-if="store.fileListIsVisible">
- <li v-for="file in store.files" :key="file.NameHash">
- <div class="tw-font-semibold tw-flex tw-items-center pull-right">
- <span v-if="file.IsBin" class="tw-ml-0.5 tw-mr-2">{{ store.binaryFileMessage }}</span>
- {{ file.IsBin ? '' : file.Addition + file.Deletion }}
- <span v-if="!file.IsBin" class="diff-stats-bar tw-mx-2" :data-tooltip-content="store.statisticsMessage.replace('%d', (file.Addition + file.Deletion)).replace('%d', file.Addition).replace('%d', file.Deletion)">
- <div class="diff-stats-add-bar" :style="{ 'width': diffStatsWidth(file.Addition, file.Deletion) }"/>
- </span>
- </div>
- <!-- todo finish all file status, now modify, add, delete and rename -->
- <span :class="['status', diffTypeToString(file.Type)]" :data-tooltip-content="diffTypeToString(file.Type)">&nbsp;</span>
- <a class="file tw-font-mono" :href="'#diff-' + file.NameHash">{{ file.Name }}</a>
- </li>
- <li v-if="store.isIncomplete" class="tw-pt-1">
- <span class="file tw-flex tw-items-center tw-justify-between">{{ store.tooManyFilesMessage }}
- <a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a>
- </span>
- </li>
- </ol>
-</template>
diff --git a/web_src/js/components/DiffFileTree.vue b/web_src/js/components/DiffFileTree.vue
index 9eabc65ae9..981d10c1c1 100644
--- a/web_src/js/components/DiffFileTree.vue
+++ b/web_src/js/components/DiffFileTree.vue
@@ -1,83 +1,14 @@
<script lang="ts" setup>
import DiffFileTreeItem from './DiffFileTreeItem.vue';
-import {loadMoreFiles} from '../features/repo-diff.ts';
import {toggleElem} from '../utils/dom.ts';
-import {diffTreeStore} from '../modules/stores.ts';
+import {diffTreeStore} from '../modules/diff-file.ts';
import {setFileFolding} from '../features/file-fold.ts';
-import {computed, onMounted, onUnmounted} from 'vue';
+import {onMounted, onUnmounted} from 'vue';
const LOCAL_STORAGE_KEY = 'diff_file_tree_visible';
const store = diffTreeStore();
-const fileTree = computed(() => {
- const result = [];
- for (const file of store.files) {
- // Split file into directories
- const splits = file.Name.split('/');
- let index = 0;
- let parent = null;
- let isFile = false;
- for (const split of splits) {
- index += 1;
- // reached the end
- if (index === splits.length) {
- isFile = true;
- }
- let newParent = {
- name: split,
- children: [],
- isFile,
- } as {
- name: string,
- children: any[],
- isFile: boolean,
- file?: any,
- };
-
- if (isFile === true) {
- newParent.file = file;
- }
-
- if (parent) {
- // check if the folder already exists
- const existingFolder = parent.children.find(
- (x) => x.name === split,
- );
- if (existingFolder) {
- newParent = existingFolder;
- } else {
- parent.children.push(newParent);
- }
- } else {
- const existingFolder = result.find((x) => x.name === split);
- if (existingFolder) {
- newParent = existingFolder;
- } else {
- result.push(newParent);
- }
- }
- parent = newParent;
- }
- }
- const mergeChildIfOnlyOneDir = (entries) => {
- for (const entry of entries) {
- if (entry.children) {
- mergeChildIfOnlyOneDir(entry.children);
- }
- if (entry.children.length === 1 && entry.children[0].isFile === false) {
- // Merge it to the parent
- entry.name = `${entry.name}/${entry.children[0].name}`;
- entry.children = entry.children[0].children;
- }
- }
- };
- // Merge folders with just a folder as children in order to
- // reduce the depth of our tree.
- mergeChildIfOnlyOneDir(result);
- return result;
-});
-
onMounted(() => {
// Default to true if unset
store.fileTreeIsVisible = localStorage.getItem(LOCAL_STORAGE_KEY) !== 'false';
@@ -110,13 +41,13 @@ function toggleVisibility() {
updateVisibility(!store.fileTreeIsVisible);
}
-function updateVisibility(visible) {
+function updateVisibility(visible: boolean) {
store.fileTreeIsVisible = visible;
- localStorage.setItem(LOCAL_STORAGE_KEY, store.fileTreeIsVisible);
+ localStorage.setItem(LOCAL_STORAGE_KEY, store.fileTreeIsVisible.toString());
updateState(store.fileTreeIsVisible);
}
-function updateState(visible) {
+function updateState(visible: boolean) {
const btn = document.querySelector('.diff-toggle-file-tree-button');
const [toShow, toHide] = btn.querySelectorAll('.icon');
const tree = document.querySelector('#diff-file-tree');
@@ -126,19 +57,12 @@ function updateState(visible) {
toggleElem(toShow, !visible);
toggleElem(toHide, visible);
}
-
-function loadMoreData() {
- loadMoreFiles(store.linkLoadMore);
-}
</script>
<template>
+ <!-- only render the tree if we're visible. in many cases this is something that doesn't change very often -->
<div v-if="store.fileTreeIsVisible" class="diff-file-tree-items">
- <!-- only render the tree if we're visible. in many cases this is something that doesn't change very often -->
- <DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item"/>
- <div v-if="store.isIncomplete" class="tw-pt-1">
- <a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a>
- </div>
+ <DiffFileTreeItem v-for="item in store.diffFileTree.TreeRoot.Children" :key="item.FullName" :item="item"/>
</div>
</template>
diff --git a/web_src/js/components/DiffFileTreeItem.vue b/web_src/js/components/DiffFileTreeItem.vue
index 12cafd8f1b..f15f093ff8 100644
--- a/web_src/js/components/DiffFileTreeItem.vue
+++ b/web_src/js/components/DiffFileTreeItem.vue
@@ -1,66 +1,62 @@
<script lang="ts" setup>
-import {SvgIcon} from '../svg.ts';
-import {diffTreeStore} from '../modules/stores.ts';
-import {ref} from 'vue';
+import {SvgIcon, type SvgName} from '../svg.ts';
+import {shallowRef} from 'vue';
+import {type DiffStatus, type DiffTreeEntry, diffTreeStore} from '../modules/diff-file.ts';
-type File = {
- Name: string;
- NameHash: string;
- Type: number;
- IsViewed: boolean;
-}
-
-type Item = {
- name: string;
- isFile: boolean;
- file?: File;
- children?: Item[];
-};
-
-defineProps<{
- item: Item,
+const props = defineProps<{
+ item: DiffTreeEntry,
}>();
const store = diffTreeStore();
-const collapsed = ref(false);
+const collapsed = shallowRef(props.item.IsViewed);
-function getIconForDiffType(pType) {
- const diffTypes = {
- 1: {name: 'octicon-diff-added', classes: ['text', 'green']},
- 2: {name: 'octicon-diff-modified', classes: ['text', 'yellow']},
- 3: {name: 'octicon-diff-removed', classes: ['text', 'red']},
- 4: {name: 'octicon-diff-renamed', classes: ['text', 'teal']},
- 5: {name: 'octicon-diff-renamed', classes: ['text', 'green']}, // there is no octicon for copied, so renamed should be ok
+function getIconForDiffStatus(pType: DiffStatus) {
+ const diffTypes: Record<DiffStatus, { name: SvgName, classes: Array<string> }> = {
+ '': {name: 'octicon-blocked', classes: ['text', 'red']}, // unknown case
+ 'added': {name: 'octicon-diff-added', classes: ['text', 'green']},
+ 'modified': {name: 'octicon-diff-modified', classes: ['text', 'yellow']},
+ 'deleted': {name: 'octicon-diff-removed', classes: ['text', 'red']},
+ 'renamed': {name: 'octicon-diff-renamed', classes: ['text', 'teal']},
+ 'copied': {name: 'octicon-diff-renamed', classes: ['text', 'green']},
+ 'typechange': {name: 'octicon-diff-modified', classes: ['text', 'green']}, // there is no octicon for copied, so renamed should be ok
};
- return diffTypes[pType];
+ return diffTypes[pType] ?? diffTypes[''];
}
</script>
<template>
- <!--title instead of tooltip above as the tooltip needs too much work with the current methods, i.e. not being loaded or staying open for "too long"-->
+ <template v-if="item.EntryMode === 'tree'">
+ <div class="item-directory" :class="{ 'viewed': item.IsViewed }" :title="item.DisplayName" @click.stop="collapsed = !collapsed">
+ <!-- directory -->
+ <SvgIcon :name="collapsed ? 'octicon-chevron-right' : 'octicon-chevron-down'"/>
+ <!-- eslint-disable-next-line vue/no-v-html -->
+ <span class="tw-contents" v-html="collapsed ? store.folderIcon : store.folderOpenIcon"/>
+ <span class="gt-ellipsis">{{ item.DisplayName }}</span>
+ </div>
+
+ <div v-show="!collapsed" class="sub-items">
+ <DiffFileTreeItem v-for="childItem in item.Children" :key="childItem.DisplayName" :item="childItem"/>
+ </div>
+ </template>
<a
- v-if="item.isFile" class="item-file"
- :class="{'selected': store.selectedItem === '#diff-' + item.file.NameHash, 'viewed': item.file.IsViewed}"
- :title="item.name" :href="'#diff-' + item.file.NameHash"
+ v-else
+ class="item-file" :class="{ 'selected': store.selectedItem === '#diff-' + item.NameHash, 'viewed': item.IsViewed }"
+ :title="item.DisplayName" :href="'#diff-' + item.NameHash"
>
<!-- file -->
- <SvgIcon name="octicon-file"/>
- <span class="gt-ellipsis tw-flex-1">{{ item.name }}</span>
- <SvgIcon :name="getIconForDiffType(item.file.Type).name" :class="getIconForDiffType(item.file.Type).classes"/>
+ <!-- eslint-disable-next-line vue/no-v-html -->
+ <span class="tw-contents" v-html="item.FileIcon"/>
+ <span class="gt-ellipsis tw-flex-1">{{ item.DisplayName }}</span>
+ <SvgIcon
+ :name="getIconForDiffStatus(item.DiffStatus).name"
+ :class="getIconForDiffStatus(item.DiffStatus).classes"
+ />
</a>
- <div v-else class="item-directory" :title="item.name" @click.stop="collapsed = !collapsed">
- <!-- directory -->
- <SvgIcon :name="collapsed ? 'octicon-chevron-right' : 'octicon-chevron-down'"/>
- <SvgIcon class="text primary" :name="collapsed ? 'octicon-file-directory-fill' : 'octicon-file-directory-open-fill'"/>
- <span class="gt-ellipsis">{{ item.name }}</span>
- </div>
-
- <div v-if="item.children?.length" v-show="!collapsed" class="sub-items">
- <DiffFileTreeItem v-for="childItem in item.children" :key="childItem.name" :item="childItem"/>
- </div>
</template>
+
<style scoped>
-a, a:hover {
+a,
+a:hover {
text-decoration: none;
color: var(--color-text);
}
@@ -83,7 +79,8 @@ a, a:hover {
border-radius: 4px;
}
-.item-file.viewed {
+.item-file.viewed,
+.item-directory.viewed {
color: var(--color-text-light-3);
}
diff --git a/web_src/js/components/PullRequestMergeForm.vue b/web_src/js/components/PullRequestMergeForm.vue
index bafeec6c97..b2c28414c0 100644
--- a/web_src/js/components/PullRequestMergeForm.vue
+++ b/web_src/js/components/PullRequestMergeForm.vue
@@ -1,19 +1,19 @@
<script lang="ts" setup>
-import {computed, onMounted, onUnmounted, ref, watch} from 'vue';
+import {computed, onMounted, onUnmounted, shallowRef, watch} from 'vue';
import {SvgIcon} from '../svg.ts';
import {toggleElem} from '../utils/dom.ts';
const {csrfToken, pageData} = window.config;
-const mergeForm = ref(pageData.pullRequestMergeForm);
+const mergeForm = pageData.pullRequestMergeForm;
-const mergeTitleFieldValue = ref('');
-const mergeMessageFieldValue = ref('');
-const deleteBranchAfterMerge = ref(false);
-const autoMergeWhenSucceed = ref(false);
+const mergeTitleFieldValue = shallowRef('');
+const mergeMessageFieldValue = shallowRef('');
+const deleteBranchAfterMerge = shallowRef(false);
+const autoMergeWhenSucceed = shallowRef(false);
-const mergeStyle = ref('');
-const mergeStyleDetail = ref({
+const mergeStyle = shallowRef('');
+const mergeStyleDetail = shallowRef({
hideMergeMessageTexts: false,
textDoMerge: '',
mergeTitleFieldText: '',
@@ -21,33 +21,33 @@ const mergeStyleDetail = ref({
hideAutoMerge: false,
});
-const mergeStyleAllowedCount = ref(0);
+const mergeStyleAllowedCount = shallowRef(0);
-const showMergeStyleMenu = ref(false);
-const showActionForm = ref(false);
+const showMergeStyleMenu = shallowRef(false);
+const showActionForm = shallowRef(false);
const mergeButtonStyleClass = computed(() => {
- if (mergeForm.value.allOverridableChecksOk) return 'primary';
+ if (mergeForm.allOverridableChecksOk) return 'primary';
return autoMergeWhenSucceed.value ? 'primary' : 'red';
});
const forceMerge = computed(() => {
- return mergeForm.value.canMergeNow && !mergeForm.value.allOverridableChecksOk;
+ return mergeForm.canMergeNow && !mergeForm.allOverridableChecksOk;
});
watch(mergeStyle, (val) => {
- mergeStyleDetail.value = mergeForm.value.mergeStyles.find((e) => e.name === val);
+ mergeStyleDetail.value = mergeForm.mergeStyles.find((e: any) => e.name === val);
for (const elem of document.querySelectorAll('[data-pull-merge-style]')) {
toggleElem(elem, elem.getAttribute('data-pull-merge-style') === val);
}
});
onMounted(() => {
- mergeStyleAllowedCount.value = mergeForm.value.mergeStyles.reduce((v, msd) => v + (msd.allowed ? 1 : 0), 0);
+ mergeStyleAllowedCount.value = mergeForm.mergeStyles.reduce((v: any, msd: any) => v + (msd.allowed ? 1 : 0), 0);
- let mergeStyle = mergeForm.value.mergeStyles.find((e) => e.allowed && e.name === mergeForm.value.defaultMergeStyle)?.name;
- if (!mergeStyle) mergeStyle = mergeForm.value.mergeStyles.find((e) => e.allowed)?.name;
- switchMergeStyle(mergeStyle, !mergeForm.value.canMergeNow);
+ let mergeStyle = mergeForm.mergeStyles.find((e: any) => e.allowed && e.name === mergeForm.defaultMergeStyle)?.name;
+ if (!mergeStyle) mergeStyle = mergeForm.mergeStyles.find((e: any) => e.allowed)?.name;
+ switchMergeStyle(mergeStyle, !mergeForm.canMergeNow);
document.addEventListener('mouseup', hideMergeStyleMenu);
});
@@ -63,18 +63,18 @@ function hideMergeStyleMenu() {
function toggleActionForm(show: boolean) {
showActionForm.value = show;
if (!show) return;
- deleteBranchAfterMerge.value = mergeForm.value.defaultDeleteBranchAfterMerge;
+ deleteBranchAfterMerge.value = mergeForm.defaultDeleteBranchAfterMerge;
mergeTitleFieldValue.value = mergeStyleDetail.value.mergeTitleFieldText;
mergeMessageFieldValue.value = mergeStyleDetail.value.mergeMessageFieldText;
}
-function switchMergeStyle(name, autoMerge = false) {
+function switchMergeStyle(name: string, autoMerge = false) {
mergeStyle.value = name;
autoMergeWhenSucceed.value = autoMerge;
}
function clearMergeMessage() {
- mergeMessageFieldValue.value = mergeForm.value.defaultMergeMessage;
+ mergeMessageFieldValue.value = mergeForm.defaultMergeMessage;
}
</script>
@@ -129,7 +129,7 @@ function clearMergeMessage() {
{{ mergeForm.textCancel }}
</button>
- <div class="ui checkbox tw-ml-1" v-if="mergeForm.isPullBranchDeletable && !autoMergeWhenSucceed">
+ <div class="ui checkbox tw-ml-1" v-if="mergeForm.isPullBranchDeletable">
<input name="delete_branch_after_merge" type="checkbox" v-model="deleteBranchAfterMerge" id="delete-branch-after-merge">
<label for="delete-branch-after-merge">{{ mergeForm.textDeleteBranch }}</label>
</div>
diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue
index 914c9e76de..2eb2211269 100644
--- a/web_src/js/components/RepoActionView.vue
+++ b/web_src/js/components/RepoActionView.vue
@@ -1,11 +1,13 @@
<script lang="ts">
import {SvgIcon} from '../svg.ts';
import ActionRunStatus from './ActionRunStatus.vue';
-import {createApp} from 'vue';
+import {defineComponent, type PropType} from 'vue';
import {createElementFromAttrs, toggleElem} from '../utils/dom.ts';
import {formatDatetime} from '../utils/time.ts';
import {renderAnsi} from '../render/ansi.ts';
import {POST, DELETE} from '../modules/fetch.ts';
+import type {IntervalId} from '../types.ts';
+import {toggleFullScreen} from '../utils.ts';
// see "models/actions/status.go", if it needs to be used somewhere else, move it to a shared file like "types/actions.ts"
type RunStatus = 'unknown' | 'waiting' | 'running' | 'success' | 'failure' | 'cancelled' | 'skipped' | 'blocked';
@@ -24,6 +26,20 @@ type LogLineCommand = {
prefix: string,
}
+type Job = {
+ id: number;
+ name: string;
+ status: RunStatus;
+ canRerun: boolean;
+ duration: string;
+}
+
+type Step = {
+ summary: string,
+ duration: string,
+ status: RunStatus,
+}
+
function parseLineCommand(line: LogLine): LogLineCommand | null {
for (const prefix of LogLinePrefixesGroup) {
if (line.message.startsWith(prefix)) {
@@ -38,7 +54,7 @@ function parseLineCommand(line: LogLine): LogLineCommand | null {
return null;
}
-function isLogElementInViewport(el: HTMLElement): boolean {
+function isLogElementInViewport(el: Element): boolean {
const rect = el.getBoundingClientRect();
return rect.top >= 0 && rect.bottom <= window.innerHeight; // only check height but not width
}
@@ -57,25 +73,28 @@ function getLocaleStorageOptions(): LocaleStorageOptions {
return {autoScroll: true, expandRunning: false};
}
-const sfc = {
+export default defineComponent({
name: 'RepoActionView',
components: {
SvgIcon,
ActionRunStatus,
},
props: {
- runIndex: String,
- jobIndex: String,
- actionsURL: String,
- locale: Object,
- },
-
- watch: {
- optionAlwaysAutoScroll() {
- this.saveLocaleStorageOptions();
+ runIndex: {
+ type: String,
+ default: '',
},
- optionAlwaysExpandRunning() {
- this.saveLocaleStorageOptions();
+ jobIndex: {
+ type: String,
+ default: '',
+ },
+ actionsURL: {
+ type: String,
+ default: '',
+ },
+ locale: {
+ type: Object as PropType<Record<string, any>>,
+ default: null,
},
},
@@ -83,11 +102,10 @@ const sfc = {
const {autoScroll, expandRunning} = getLocaleStorageOptions();
return {
// internal state
- loadingAbortController: null,
- intervalID: null,
- currentJobStepsStates: [],
- artifacts: [],
- onHoverRerunIndex: -1,
+ loadingAbortController: null as AbortController | null,
+ intervalID: null as IntervalId | null,
+ currentJobStepsStates: [] as Array<Record<string, any>>,
+ artifacts: [] as Array<Record<string, any>>,
menuVisible: false,
isFullScreen: false,
timeVisible: {
@@ -102,10 +120,11 @@ const sfc = {
link: '',
title: '',
titleHTML: '',
- status: '',
+ status: '' as RunStatus, // do not show the status before initialized, otherwise it would show an incorrect "error" icon
canCancel: false,
canApprove: false,
canRerun: false,
+ canDeleteArtifact: false,
done: false,
workflowID: '',
workflowLink: '',
@@ -118,7 +137,7 @@ const sfc = {
// canRerun: false,
// duration: '',
// },
- ],
+ ] as Array<Job>,
commit: {
localeCommit: '',
localePushedBy: '',
@@ -131,6 +150,7 @@ const sfc = {
branch: {
name: '',
link: '',
+ isDeleted: false,
},
},
},
@@ -143,11 +163,20 @@ const sfc = {
// duration: '',
// status: '',
// }
- ],
+ ] as Array<Step>,
},
};
},
+ watch: {
+ optionAlwaysAutoScroll() {
+ this.saveLocaleStorageOptions();
+ },
+ optionAlwaysExpandRunning() {
+ this.saveLocaleStorageOptions();
+ },
+ },
+
async mounted() {
// load job data and then auto-reload periodically
// need to await first loadJob so this.currentJobStepsStates is initialized and can be used in hashChangeListener
@@ -180,17 +209,18 @@ const sfc = {
// get the job step logs container ('.job-step-logs')
getJobStepLogsContainer(stepIndex: number): HTMLElement {
- return this.$refs.logs[stepIndex];
+ return (this.$refs.logs as any)[stepIndex];
},
// get the active logs container element, either the `job-step-logs` or the `job-log-list` in the `job-log-group`
getActiveLogsContainer(stepIndex: number): HTMLElement {
const el = this.getJobStepLogsContainer(stepIndex);
+ // @ts-expect-error - _stepLogsActiveContainer is a custom property
return el._stepLogsActiveContainer ?? el;
},
// begin a log group
beginLogGroup(stepIndex: number, startTime: number, line: LogLine, cmd: LogLineCommand) {
- const el = this.$refs.logs[stepIndex];
+ const el = (this.$refs.logs as any)[stepIndex];
const elJobLogGroupSummary = createElementFromAttrs('summary', {class: 'job-log-group-summary'},
this.createLogLine(stepIndex, startTime, {
index: line.index,
@@ -208,7 +238,7 @@ const sfc = {
},
// end a log group
endLogGroup(stepIndex: number, startTime: number, line: LogLine, cmd: LogLineCommand) {
- const el = this.$refs.logs[stepIndex];
+ const el = (this.$refs.logs as any)[stepIndex];
el._stepLogsActiveContainer = null;
el.append(this.createLogLine(stepIndex, startTime, {
index: line.index,
@@ -263,7 +293,7 @@ const sfc = {
const el = this.getJobStepLogsContainer(stepIndex);
// if the logs container is empty, then auto-scroll if the step is expanded
if (!el.lastChild) return this.currentJobStepsStates[stepIndex].expanded;
- return isLogElementInViewport(el.lastChild);
+ return isLogElementInViewport(el.lastChild as Element);
},
appendLogs(stepIndex: number, startTime: number, logLines: LogLine[]) {
@@ -378,98 +408,39 @@ const sfc = {
if (this.menuVisible) this.menuVisible = false;
},
- toggleTimeDisplay(type: string) {
+ toggleTimeDisplay(type: 'seconds' | 'stamp') {
this.timeVisible[`log-time-${type}`] = !this.timeVisible[`log-time-${type}`];
- for (const el of this.$refs.steps.querySelectorAll(`.log-time-${type}`)) {
+ for (const el of (this.$refs.steps as HTMLElement).querySelectorAll(`.log-time-${type}`)) {
toggleElem(el, this.timeVisible[`log-time-${type}`]);
}
},
toggleFullScreen() {
this.isFullScreen = !this.isFullScreen;
- const fullScreenEl = document.querySelector('.action-view-right');
- const outerEl = document.querySelector('.full.height');
- const actionBodyEl = document.querySelector('.action-view-body');
- const headerEl = document.querySelector('#navbar');
- const contentEl = document.querySelector('.page-content');
- const footerEl = document.querySelector('.page-footer');
- toggleElem(headerEl, !this.isFullScreen);
- toggleElem(contentEl, !this.isFullScreen);
- toggleElem(footerEl, !this.isFullScreen);
- // move .action-view-right to new parent
- if (this.isFullScreen) {
- outerEl.append(fullScreenEl);
- } else {
- actionBodyEl.append(fullScreenEl);
- }
+ toggleFullScreen('.action-view-right', this.isFullScreen, '.action-view-body');
},
async hashChangeListener() {
const selectedLogStep = window.location.hash;
if (!selectedLogStep) return;
const [_, step, _line] = selectedLogStep.split('-');
- if (!this.currentJobStepsStates[step]) return;
- if (!this.currentJobStepsStates[step].expanded && this.currentJobStepsStates[step].cursor === null) {
- this.currentJobStepsStates[step].expanded = true;
+ const stepNum = Number(step);
+ if (!this.currentJobStepsStates[stepNum]) return;
+ if (!this.currentJobStepsStates[stepNum].expanded && this.currentJobStepsStates[stepNum].cursor === null) {
+ this.currentJobStepsStates[stepNum].expanded = true;
// need to await for load job if the step log is loaded for the first time
// so logline can be selected by querySelector
await this.loadJob();
}
- const logLine = this.$refs.steps.querySelector(selectedLogStep);
+ const logLine = (this.$refs.steps as HTMLElement).querySelector(selectedLogStep);
if (!logLine) return;
- logLine.querySelector('.line-num').click();
+ logLine.querySelector<HTMLAnchorElement>('.line-num').click();
},
},
-};
-
-export default sfc;
-
-export function initRepositoryActionView() {
- const el = document.querySelector('#repo-action-view');
- if (!el) return;
-
- // TODO: the parent element's full height doesn't work well now,
- // but we can not pollute the global style at the moment, only fix the height problem for pages with this component
- const parentFullHeight = document.querySelector<HTMLElement>('body > div.full.height');
- if (parentFullHeight) parentFullHeight.style.paddingBottom = '0';
-
- const view = createApp(sfc, {
- runIndex: el.getAttribute('data-run-index'),
- jobIndex: el.getAttribute('data-job-index'),
- actionsURL: el.getAttribute('data-actions-url'),
- locale: {
- approve: el.getAttribute('data-locale-approve'),
- cancel: el.getAttribute('data-locale-cancel'),
- rerun: el.getAttribute('data-locale-rerun'),
- rerun_all: el.getAttribute('data-locale-rerun-all'),
- scheduled: el.getAttribute('data-locale-runs-scheduled'),
- commit: el.getAttribute('data-locale-runs-commit'),
- pushedBy: el.getAttribute('data-locale-runs-pushed-by'),
- artifactsTitle: el.getAttribute('data-locale-artifacts-title'),
- areYouSure: el.getAttribute('data-locale-are-you-sure'),
- confirmDeleteArtifact: el.getAttribute('data-locale-confirm-delete-artifact'),
- showTimeStamps: el.getAttribute('data-locale-show-timestamps'),
- showLogSeconds: el.getAttribute('data-locale-show-log-seconds'),
- showFullScreen: el.getAttribute('data-locale-show-full-screen'),
- downloadLogs: el.getAttribute('data-locale-download-logs'),
- status: {
- unknown: el.getAttribute('data-locale-status-unknown'),
- waiting: el.getAttribute('data-locale-status-waiting'),
- running: el.getAttribute('data-locale-status-running'),
- success: el.getAttribute('data-locale-status-success'),
- failure: el.getAttribute('data-locale-status-failure'),
- cancelled: el.getAttribute('data-locale-status-cancelled'),
- skipped: el.getAttribute('data-locale-status-skipped'),
- blocked: el.getAttribute('data-locale-status-blocked'),
- },
- logsAlwaysAutoScroll: el.getAttribute('data-locale-logs-always-auto-scroll'),
- logsAlwaysExpandRunning: el.getAttribute('data-locale-logs-always-expand-running'),
- },
- });
- view.mount(el);
-}
+});
</script>
<template>
- <div class="ui container action-view-container">
+ <!-- make the view container full width to make users easier to read logs -->
+ <div class="ui fluid container">
<div class="action-view-header">
<div class="action-info-summary">
<div class="action-info-summary-title">
@@ -508,13 +479,13 @@ export function initRepositoryActionView() {
<div class="action-view-left">
<div class="job-group-section">
<div class="job-brief-list">
- <a class="job-brief-item" :href="run.link+'/jobs/'+index" :class="parseInt(jobIndex) === index ? 'selected' : ''" v-for="(job, index) in run.jobs" :key="job.id" @mouseenter="onHoverRerunIndex = job.id" @mouseleave="onHoverRerunIndex = -1">
+ <a class="job-brief-item" :href="run.link+'/jobs/'+index" :class="parseInt(jobIndex) === index ? 'selected' : ''" v-for="(job, index) in run.jobs" :key="job.id">
<div class="job-brief-item-left">
<ActionRunStatus :locale-status="locale.status[job.status]" :status="job.status"/>
<span class="job-brief-name tw-mx-2 gt-ellipsis">{{ job.name }}</span>
</div>
<span class="job-brief-item-right">
- <SvgIcon name="octicon-sync" role="button" :data-tooltip-content="locale.rerun" class="job-brief-rerun tw-mx-2 link-action" :data-url="`${run.link}/jobs/${index}/rerun`" v-if="job.canRerun && onHoverRerunIndex === job.id"/>
+ <SvgIcon name="octicon-sync" role="button" :data-tooltip-content="locale.rerun" class="job-brief-rerun tw-mx-2 link-action" :data-url="`${run.link}/jobs/${index}/rerun`" v-if="job.canRerun"/>
<span class="step-summary-duration">{{ job.duration }}</span>
</span>
</a>
@@ -525,14 +496,24 @@ export function initRepositoryActionView() {
{{ locale.artifactsTitle }}
</div>
<ul class="job-artifacts-list">
- <li class="job-artifacts-item" v-for="artifact in artifacts" :key="artifact.name">
- <a class="job-artifacts-link" target="_blank" :href="run.link+'/artifacts/'+artifact.name">
- <SvgIcon name="octicon-file" class="ui text black job-artifacts-icon"/>{{ artifact.name }}
- </a>
- <a v-if="run.canDeleteArtifact" @click="deleteArtifact(artifact.name)" class="job-artifacts-delete">
- <SvgIcon name="octicon-trash" class="ui text black job-artifacts-icon"/>
- </a>
- </li>
+ <template v-for="artifact in artifacts" :key="artifact.name">
+ <li class="job-artifacts-item">
+ <template v-if="artifact.status !== 'expired'">
+ <a class="flex-text-inline" target="_blank" :href="run.link+'/artifacts/'+artifact.name">
+ <SvgIcon name="octicon-file" class="text black"/>
+ <span class="gt-ellipsis">{{ artifact.name }}</span>
+ </a>
+ <a v-if="run.canDeleteArtifact" @click="deleteArtifact(artifact.name)">
+ <SvgIcon name="octicon-trash" class="text black"/>
+ </a>
+ </template>
+ <span v-else class="flex-text-inline text light grey">
+ <SvgIcon name="octicon-file"/>
+ <span class="gt-ellipsis">{{ artifact.name }}</span>
+ <span class="ui label text light grey tw-flex-shrink-0">{{ locale.artifactExpired }}</span>
+ </span>
+ </li>
+ </template>
</ul>
</div>
</div>
@@ -591,7 +572,7 @@ export function initRepositoryActionView() {
<!-- If the job is done and the job step log is loaded for the first time, show the loading icon
currentJobStepsStates[i].cursor === null means the log is loaded for the first time
-->
- <SvgIcon v-if="isDone(run.status) && currentJobStepsStates[i].expanded && currentJobStepsStates[i].cursor === null" name="octicon-sync" class="tw-mr-2 job-status-rotate"/>
+ <SvgIcon v-if="isDone(run.status) && currentJobStepsStates[i].expanded && currentJobStepsStates[i].cursor === null" name="octicon-sync" class="tw-mr-2 circular-spin"/>
<SvgIcon v-else :name="currentJobStepsStates[i].expanded ? 'octicon-chevron-down': 'octicon-chevron-right'" :class="['tw-mr-2', !isExpandable(jobStep.status) && 'tw-invisible']"/>
<ActionRunStatus :status="jobStep.status" class="tw-mr-2"/>
@@ -694,6 +675,7 @@ export function initRepositoryActionView() {
padding: 6px;
display: flex;
justify-content: space-between;
+ align-items: center;
}
.job-artifacts-list {
@@ -701,10 +683,6 @@ export function initRepositoryActionView() {
list-style: none;
}
-.job-artifacts-icon {
- padding-right: 3px;
-}
-
.job-brief-list {
display: flex;
flex-direction: column;
@@ -737,11 +715,6 @@ export function initRepositoryActionView() {
.job-brief-item .job-brief-rerun {
cursor: pointer;
- transition: transform 0.2s;
-}
-
-.job-brief-item .job-brief-rerun:hover {
- transform: scale(130%);
}
.job-brief-item .job-brief-item-left {
@@ -918,16 +891,6 @@ export function initRepositoryActionView() {
<style> /* eslint-disable-line vue-scoped-css/enforce-style-type */
/* some elements are not managed by vue, so we need to use global style */
-.job-status-rotate {
- animation: job-status-rotate-keyframes 1s linear infinite;
-}
-
-@keyframes job-status-rotate-keyframes {
- 100% {
- transform: rotate(-360deg);
- }
-}
-
.job-step-section {
margin: 10px;
}
@@ -977,7 +940,6 @@ export function initRepositoryActionView() {
.job-step-logs .job-log-line .log-msg {
flex: 1;
- word-break: break-all;
white-space: break-spaces;
margin-left: 10px;
overflow-wrap: anywhere;
diff --git a/web_src/js/components/RepoActivityTopAuthors.vue b/web_src/js/components/RepoActivityTopAuthors.vue
index 1f5e9825ba..bbdfda41d0 100644
--- a/web_src/js/components/RepoActivityTopAuthors.vue
+++ b/web_src/js/components/RepoActivityTopAuthors.vue
@@ -1,20 +1,23 @@
<script lang="ts" setup>
+// @ts-expect-error - module exports no types
import {VueBarGraph} from 'vue-bar-graph';
-import {computed, onMounted, ref} from 'vue';
+import {computed, onMounted, shallowRef, useTemplateRef} from 'vue';
-const colors = ref({
+const colors = shallowRef({
barColor: 'green',
textColor: 'black',
textAltColor: 'white',
});
-// possible keys:
-// * avatar_link: (...)
-// * commits: (...)
-// * home_link: (...)
-// * login: (...)
-// * name: (...)
-const activityTopAuthors = window.config.pageData.repoActivityTopAuthors || [];
+type ActivityAuthorData = {
+ avatar_link: string;
+ commits: number;
+ home_link: string;
+ login: string;
+ name: string;
+}
+
+const activityTopAuthors: Array<ActivityAuthorData> = window.config.pageData.repoActivityTopAuthors || [];
const graphPoints = computed(() => {
return activityTopAuthors.map((item) => {
@@ -26,7 +29,7 @@ const graphPoints = computed(() => {
});
const graphAuthors = computed(() => {
- return activityTopAuthors.map((item, idx) => {
+ return activityTopAuthors.map((item, idx: number) => {
return {
position: idx + 1,
...item,
@@ -38,8 +41,8 @@ const graphWidth = computed(() => {
return activityTopAuthors.length * 40;
});
-const styleElement = ref<HTMLElement | null>(null);
-const altStyleElement = ref<HTMLElement | null>(null);
+const styleElement = useTemplateRef('styleElement');
+const altStyleElement = useTemplateRef('altStyleElement');
onMounted(() => {
const refStyle = window.getComputedStyle(styleElement.value);
diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue
index a5ed8b6dad..8e3a29a0e0 100644
--- a/web_src/js/components/RepoBranchTagSelector.vue
+++ b/web_src/js/components/RepoBranchTagSelector.vue
@@ -1,5 +1,5 @@
<script lang="ts">
-import {nextTick} from 'vue';
+import {defineComponent, nextTick} from 'vue';
import {SvgIcon} from '../svg.ts';
import {showErrorToast} from '../modules/toast.ts';
import {GET} from '../modules/fetch.ts';
@@ -17,51 +17,11 @@ type SelectedTab = 'branches' | 'tags';
type TabLoadingStates = Record<SelectedTab, '' | 'loading' | 'done'>
-const sfc = {
+export default defineComponent({
components: {SvgIcon},
props: {
elRoot: HTMLElement,
},
- computed: {
- searchFieldPlaceholder() {
- return this.selectedTab === 'branches' ? this.textFilterBranch : this.textFilterTag;
- },
- filteredItems(): ListItem[] {
- const searchTermLower = this.searchTerm.toLowerCase();
- const items = this.allItems.filter((item: ListItem) => {
- const typeMatched = (this.selectedTab === 'branches' && item.refType === 'branch') || (this.selectedTab === 'tags' && item.refType === 'tag');
- if (!typeMatched) return false;
- if (!this.searchTerm) return true; // match all
- return item.refShortName.toLowerCase().includes(searchTermLower);
- });
-
- // TODO: fix this anti-pattern: side-effects-in-computed-properties
- this.activeItemIndex = !items.length && this.showCreateNewRef ? 0 : -1;
- return items;
- },
- showNoResults() {
- if (this.tabLoadingStates[this.selectedTab] !== 'done') return false;
- return !this.filteredItems.length && !this.showCreateNewRef;
- },
- showCreateNewRef() {
- if (!this.allowCreateNewRef || !this.searchTerm) {
- return false;
- }
- return !this.allItems.filter((item: ListItem) => {
- return item.refShortName === this.searchTerm; // FIXME: not quite right here, it mixes "branch" and "tag" names
- }).length;
- },
- createNewRefFormActionUrl() {
- return `${this.currentRepoLink}/branches/_new/${this.currentRefType}/${pathEscapeSegments(this.currentRefShortName)}`;
- },
- },
- watch: {
- menuVisible(visible: boolean) {
- if (!visible) return;
- this.focusSearchField();
- this.loadTabItems();
- },
- },
data() {
const shouldShowTabBranches = this.elRoot.getAttribute('data-show-tab-branches') === 'true';
return {
@@ -89,7 +49,7 @@ const sfc = {
currentRepoDefaultBranch: this.elRoot.getAttribute('data-current-repo-default-branch'),
currentRepoLink: this.elRoot.getAttribute('data-current-repo-link'),
currentTreePath: this.elRoot.getAttribute('data-current-tree-path'),
- currentRefType: this.elRoot.getAttribute('data-current-ref-type'),
+ currentRefType: this.elRoot.getAttribute('data-current-ref-type') as GitRefType,
currentRefShortName: this.elRoot.getAttribute('data-current-ref-short-name'),
refLinkTemplate: this.elRoot.getAttribute('data-ref-link-template'),
@@ -102,6 +62,46 @@ const sfc = {
enableFeed: this.elRoot.getAttribute('data-enable-feed') === 'true',
};
},
+ computed: {
+ searchFieldPlaceholder() {
+ return this.selectedTab === 'branches' ? this.textFilterBranch : this.textFilterTag;
+ },
+ filteredItems(): ListItem[] {
+ const searchTermLower = this.searchTerm.toLowerCase();
+ const items = this.allItems.filter((item: ListItem) => {
+ const typeMatched = (this.selectedTab === 'branches' && item.refType === 'branch') || (this.selectedTab === 'tags' && item.refType === 'tag');
+ if (!typeMatched) return false;
+ if (!this.searchTerm) return true; // match all
+ return item.refShortName.toLowerCase().includes(searchTermLower);
+ });
+
+ // TODO: fix this anti-pattern: side-effects-in-computed-properties
+ this.activeItemIndex = !items.length && this.showCreateNewRef ? 0 : -1; // eslint-disable-line vue/no-side-effects-in-computed-properties
+ return items;
+ },
+ showNoResults() {
+ if (this.tabLoadingStates[this.selectedTab] !== 'done') return false;
+ return !this.filteredItems.length && !this.showCreateNewRef;
+ },
+ showCreateNewRef() {
+ if (!this.allowCreateNewRef || !this.searchTerm) {
+ return false;
+ }
+ return !this.allItems.filter((item: ListItem) => {
+ return item.refShortName === this.searchTerm; // FIXME: not quite right here, it mixes "branch" and "tag" names
+ }).length;
+ },
+ createNewRefFormActionUrl() {
+ return `${this.currentRepoLink}/branches/_new/${this.currentRefType}/${pathEscapeSegments(this.currentRefShortName)}`;
+ },
+ },
+ watch: {
+ menuVisible(visible: boolean) {
+ if (!visible) return;
+ this.focusSearchField();
+ this.loadTabItems();
+ },
+ },
beforeMount() {
document.body.addEventListener('click', (e) => {
if (this.$el.contains(e.target)) return;
@@ -139,11 +139,11 @@ const sfc = {
}
},
createNewRef() {
- this.$refs.createNewRefForm?.submit();
+ (this.$refs.createNewRefForm as HTMLFormElement)?.submit();
},
focusSearchField() {
nextTick(() => {
- this.$refs.searchField.focus();
+ (this.$refs.searchField as HTMLInputElement).focus();
});
},
getSelectedIndexInFiltered() {
@@ -154,9 +154,10 @@ const sfc = {
},
getActiveItem() {
const el = this.$refs[`listItem${this.activeItemIndex}`]; // eslint-disable-line no-jquery/variable-pattern
+ // @ts-expect-error - el is unknown type
return (el && el.length) ? el[0] : null;
},
- keydown(e) {
+ keydown(e: KeyboardEvent) {
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
e.preventDefault();
@@ -180,7 +181,7 @@ const sfc = {
this.menuVisible = false;
}
},
- handleTabSwitch(selectedTab) {
+ handleTabSwitch(selectedTab: SelectedTab) {
this.selectedTab = selectedTab;
this.focusSearchField();
this.loadTabItems();
@@ -212,22 +213,21 @@ const sfc = {
}
},
},
-};
-
-export default sfc; // activate IDE's Vue plugin
+});
</script>
<template>
- <div class="ui dropdown custom branch-selector-dropdown ellipsis-items-nowrap">
- <div tabindex="0" class="ui button branch-dropdown-button" @click="menuVisible = !menuVisible">
+ <div class="ui dropdown custom branch-selector-dropdown ellipsis-text-items">
+ <div tabindex="0" class="ui compact button branch-dropdown-button" @click="menuVisible = !menuVisible">
<span class="flex-text-block gt-ellipsis">
<template v-if="dropdownFixedText">{{ dropdownFixedText }}</template>
<template v-else>
<svg-icon v-if="currentRefType === 'tag'" name="octicon-tag"/>
- <svg-icon v-else name="octicon-git-branch"/>
- <strong ref="dropdownRefName" class="tw-ml-2 tw-inline-block gt-ellipsis">{{ currentRefShortName }}</strong>
+ <svg-icon v-else-if="currentRefType === 'branch'" name="octicon-git-branch"/>
+ <svg-icon v-else name="octicon-git-commit"/>
+ <strong ref="dropdownRefName" class="tw-inline-block gt-ellipsis">{{ currentRefShortName }}</strong>
</template>
</span>
- <svg-icon name="octicon-triangle-down" :size="14" class-name="dropdown icon"/>
+ <svg-icon name="octicon-triangle-down" :size="14" class="dropdown icon"/>
</div>
<div class="menu transition" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak>
<div class="ui icon search input">
@@ -236,10 +236,10 @@ export default sfc; // activate IDE's Vue plugin
</div>
<div v-if="showTabBranches" class="branch-tag-tab">
<a class="branch-tag-item muted" :class="{active: selectedTab === 'branches'}" href="#" @click="handleTabSwitch('branches')">
- <svg-icon name="octicon-git-branch" :size="16" class-name="tw-mr-1"/>{{ textBranches }}
+ <svg-icon name="octicon-git-branch" :size="16" class="tw-mr-1"/>{{ textBranches }}
</a>
<a v-if="showTabTags" class="branch-tag-item muted" :class="{active: selectedTab === 'tags'}" href="#" @click="handleTabSwitch('tags')">
- <svg-icon name="octicon-tag" :size="16" class-name="tw-mr-1"/>{{ textTags }}
+ <svg-icon name="octicon-tag" :size="16" class="tw-mr-1"/>{{ textTags }}
</a>
</div>
<div class="branch-tag-divider"/>
diff --git a/web_src/js/components/RepoCodeFrequency.vue b/web_src/js/components/RepoCodeFrequency.vue
index 7696996cf6..f331a26fe9 100644
--- a/web_src/js/components/RepoCodeFrequency.vue
+++ b/web_src/js/components/RepoCodeFrequency.vue
@@ -23,7 +23,7 @@ import {
import {chartJsColors} from '../utils/color.ts';
import {sleep} from '../utils.ts';
import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm';
-import {onMounted, ref} from 'vue';
+import {onMounted, shallowRef} from 'vue';
const {pageData} = window.config;
@@ -47,10 +47,10 @@ defineProps<{
};
}>();
-const isLoading = ref(false);
-const errorText = ref('');
-const repoLink = ref(pageData.repoLink || []);
-const data = ref<DayData[]>([]);
+const isLoading = shallowRef(false);
+const errorText = shallowRef('');
+const repoLink = pageData.repoLink;
+const data = shallowRef<DayData[]>([]);
onMounted(() => {
fetchGraphData();
@@ -61,7 +61,7 @@ async function fetchGraphData() {
try {
let response: Response;
do {
- response = await GET(`${repoLink.value}/activity/code-frequency/data`);
+ response = await GET(`${repoLink}/activity/code-frequency/data`);
if (response.status === 202) {
await sleep(1000); // wait for 1 second before retrying
}
@@ -150,7 +150,7 @@ const options: ChartOptions<'line'> = {
<div class="tw-flex ui segment main-graph">
<div v-if="isLoading || errorText !== ''" class="gt-tc tw-m-auto">
<div v-if="isLoading">
- <SvgIcon name="octicon-sync" class="tw-mr-2 job-status-rotate"/>
+ <SvgIcon name="octicon-sync" class="tw-mr-2 circular-spin"/>
{{ locale.loadingInfo }}
</div>
<div v-else class="text red">
diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue
index e79fc80d8e..754acb997d 100644
--- a/web_src/js/components/RepoContributors.vue
+++ b/web_src/js/components/RepoContributors.vue
@@ -1,4 +1,5 @@
<script lang="ts">
+import {defineComponent, type PropType} from 'vue';
import {SvgIcon} from '../svg.ts';
import dayjs from 'dayjs';
import {
@@ -56,11 +57,11 @@ Chart.register(
customEventListener,
);
-export default {
+export default defineComponent({
components: {ChartLine, SvgIcon},
props: {
locale: {
- type: Object,
+ type: Object as PropType<Record<string, any>>,
required: true,
},
repoLink: {
@@ -79,16 +80,16 @@ export default {
sortedContributors: {} as Record<string, any>,
type: 'commits',
contributorsStats: {} as Record<string, any>,
- xAxisStart: null,
- xAxisEnd: null,
- xAxisMin: null,
- xAxisMax: null,
+ xAxisStart: null as number | null,
+ xAxisEnd: null as number | null,
+ xAxisMin: null as number | null,
+ xAxisMax: null as number | null,
}),
mounted() {
this.fetchGraphData();
fomanticQuery('#repo-contributors').dropdown({
- onChange: (val) => {
+ onChange: (val: string) => {
this.xAxisMin = this.xAxisStart;
this.xAxisMax = this.xAxisEnd;
this.type = val;
@@ -98,7 +99,7 @@ export default {
},
methods: {
sortContributors() {
- const contributors = this.filterContributorWeeksByDateRange();
+ const contributors: Record<string, any> = this.filterContributorWeeksByDateRange();
const criteria = `total_${this.type}`;
this.sortedContributors = Object.values(contributors)
.filter((contributor) => contributor[criteria] !== 0)
@@ -157,7 +158,7 @@ export default {
},
filterContributorWeeksByDateRange() {
- const filteredData = {};
+ const filteredData: Record<string, any> = {};
const data = this.contributorsStats;
for (const key of Object.keys(data)) {
const user = data[key];
@@ -195,7 +196,7 @@ export default {
// Normally, chartjs handles this automatically, but it will resize the graph when you
// zoom, pan etc. I think resizing the graph makes it harder to compare things visually.
const maxValue = Math.max(
- ...this.totalStats.weeks.map((o) => o[this.type]),
+ ...this.totalStats.weeks.map((o: Record<string, any>) => o[this.type]),
);
const [coefficient, exp] = maxValue.toExponential().split('e').map(Number);
if (coefficient % 1 === 0) return maxValue;
@@ -207,7 +208,7 @@ export default {
// for contributors' graph. If I let chartjs do this for me, it will choose different
// maxY value for each contributors' graph which again makes it harder to compare.
const maxValue = Math.max(
- ...this.sortedContributors.map((c) => c.max_contribution_type),
+ ...this.sortedContributors.map((c: Record<string, any>) => c.max_contribution_type),
);
const [coefficient, exp] = maxValue.toExponential().split('e').map(Number);
if (coefficient % 1 === 0) return maxValue;
@@ -231,8 +232,8 @@ export default {
},
updateOtherCharts({chart}: {chart: Chart}, reset: boolean = false) {
- const minVal = chart.options.scales.x.min;
- const maxVal = chart.options.scales.x.max;
+ const minVal = Number(chart.options.scales.x.min);
+ const maxVal = Number(chart.options.scales.x.max);
if (reset) {
this.xAxisMin = this.xAxisStart;
this.xAxisMax = this.xAxisEnd;
@@ -320,7 +321,7 @@ export default {
};
},
},
-};
+});
</script>
<template>
<div>
@@ -352,12 +353,12 @@ export default {
</div>
<div>
<!-- Contribution type -->
- <div class="ui dropdown jump" id="repo-contributors">
+ <div class="ui floating dropdown jump" id="repo-contributors">
<div class="ui basic compact button">
<span class="not-mobile">{{ locale.filterLabel }}</span> <strong>{{ locale.contributionType[type] }}</strong>
<svg-icon name="octicon-triangle-down" :size="14"/>
</div>
- <div class="menu">
+ <div class="left menu">
<div :class="['item', {'selected': type === 'commits'}]" data-value="commits">
{{ locale.contributionType.commits }}
</div>
@@ -374,7 +375,7 @@ export default {
<div class="tw-flex ui segment main-graph">
<div v-if="isLoading || errorText !== ''" class="gt-tc tw-m-auto">
<div v-if="isLoading">
- <SvgIcon name="octicon-sync" class="tw-mr-2 job-status-rotate"/>
+ <SvgIcon name="octicon-sync" class="tw-mr-2 circular-spin"/>
{{ locale.loadingInfo }}
</div>
<div v-else class="text red">
@@ -396,7 +397,7 @@ export default {
<div class="ui top attached header tw-flex tw-flex-1">
<b class="ui right">#{{ index + 1 }}</b>
<a :href="contributor.home_link">
- <img class="ui avatar tw-align-middle" height="40" width="40" :src="contributor.avatar_link" alt="">
+ <img loading="lazy" class="ui avatar tw-align-middle" height="40" width="40" :src="contributor.avatar_link" alt="">
</a>
<div class="tw-ml-2">
<a v-if="contributor.home_link !== ''" :href="contributor.home_link"><h4>{{ contributor.name }}</h4></a>
diff --git a/web_src/js/components/RepoRecentCommits.vue b/web_src/js/components/RepoRecentCommits.vue
index 10e1fdd70c..27aa27dfc3 100644
--- a/web_src/js/components/RepoRecentCommits.vue
+++ b/web_src/js/components/RepoRecentCommits.vue
@@ -21,7 +21,7 @@ import {
import {chartJsColors} from '../utils/color.ts';
import {sleep} from '../utils.ts';
import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm';
-import {onMounted, ref} from 'vue';
+import {onMounted, ref, shallowRef} from 'vue';
const {pageData} = window.config;
@@ -43,9 +43,9 @@ defineProps<{
};
}>();
-const isLoading = ref(false);
-const errorText = ref('');
-const repoLink = ref(pageData.repoLink || []);
+const isLoading = shallowRef(false);
+const errorText = shallowRef('');
+const repoLink = pageData.repoLink;
const data = ref<DayData[]>([]);
onMounted(() => {
@@ -57,7 +57,7 @@ async function fetchGraphData() {
try {
let response: Response;
do {
- response = await GET(`${repoLink.value}/activity/recent-commits/data`);
+ response = await GET(`${repoLink}/activity/recent-commits/data`);
if (response.status === 202) {
await sleep(1000); // wait for 1 second before retrying
}
@@ -128,7 +128,7 @@ const options: ChartOptions<'bar'> = {
<div class="tw-flex ui segment main-graph">
<div v-if="isLoading || errorText !== ''" class="gt-tc tw-m-auto">
<div v-if="isLoading">
- <SvgIcon name="octicon-sync" class="tw-mr-2 job-status-rotate"/>
+ <SvgIcon name="octicon-sync" class="tw-mr-2 circular-spin"/>
{{ locale.loadingInfo }}
</div>
<div v-else class="text red">
diff --git a/web_src/js/components/ScopedAccessTokenSelector.vue b/web_src/js/components/ScopedAccessTokenSelector.vue
deleted file mode 100644
index 63214d0bf5..0000000000
--- a/web_src/js/components/ScopedAccessTokenSelector.vue
+++ /dev/null
@@ -1,81 +0,0 @@
-<script lang="ts" setup>
-import {computed, onMounted, onUnmounted} from 'vue';
-import {hideElem, showElem} from '../utils/dom.ts';
-
-const props = defineProps<{
- isAdmin: boolean;
- noAccessLabel: string;
- readLabel: string;
- writeLabel: string;
-}>();
-
-const categories = computed(() => {
- const categories = [
- 'activitypub',
- ];
- if (props.isAdmin) {
- categories.push('admin');
- }
- categories.push(
- 'issue',
- 'misc',
- 'notification',
- 'organization',
- 'package',
- 'repository',
- 'user');
- return categories;
-});
-
-onMounted(() => {
- document.querySelector('#scoped-access-submit').addEventListener('click', onClickSubmit);
-});
-
-onUnmounted(() => {
- document.querySelector('#scoped-access-submit').removeEventListener('click', onClickSubmit);
-});
-
-function onClickSubmit(e) {
- e.preventDefault();
-
- const warningEl = document.querySelector('#scoped-access-warning');
- // check that at least one scope has been selected
- for (const el of document.querySelectorAll<HTMLInputElement>('.access-token-select')) {
- if (el.value) {
- // Hide the error if it was visible from previous attempt.
- hideElem(warningEl);
- // Submit the form.
- document.querySelector<HTMLFormElement>('#scoped-access-form').submit();
- // Don't show the warning.
- return;
- }
- }
- // no scopes selected, show validation error
- showElem(warningEl);
-}
-</script>
-
-<template>
- <div v-for="category in categories" :key="category" class="field tw-pl-1 tw-pb-1 access-token-category">
- <label class="category-label" :for="'access-token-scope-' + category">
- {{ category }}
- </label>
- <div class="gitea-select">
- <select
- class="ui selection access-token-select"
- name="scope"
- :id="'access-token-scope-' + category"
- >
- <option value="">
- {{ noAccessLabel }}
- </option>
- <option :value="'read:' + category">
- {{ readLabel }}
- </option>
- <option :value="'write:' + category">
- {{ writeLabel }}
- </option>
- </select>
- </div>
- </div>
-</template>
diff --git a/web_src/js/components/ViewFileTree.vue b/web_src/js/components/ViewFileTree.vue
new file mode 100644
index 0000000000..1f90f92586
--- /dev/null
+++ b/web_src/js/components/ViewFileTree.vue
@@ -0,0 +1,38 @@
+<script lang="ts" setup>
+import ViewFileTreeItem from './ViewFileTreeItem.vue';
+import {onMounted, useTemplateRef} from 'vue';
+import {createViewFileTreeStore} from './ViewFileTreeStore.ts';
+
+const elRoot = useTemplateRef('elRoot');
+
+const props = defineProps({
+ repoLink: {type: String, required: true},
+ treePath: {type: String, required: true},
+ currentRefNameSubURL: {type: String, required: true},
+});
+
+const store = createViewFileTreeStore(props);
+onMounted(async () => {
+ store.rootFiles = await store.loadChildren('', props.treePath);
+ elRoot.value.closest('.is-loading')?.classList?.remove('is-loading');
+ window.addEventListener('popstate', (e) => {
+ store.selectedItem = e.state?.treePath || '';
+ if (e.state?.url) store.loadViewContent(e.state.url);
+ });
+});
+</script>
+
+<template>
+ <div class="view-file-tree-items" ref="elRoot">
+ <ViewFileTreeItem v-for="item in store.rootFiles" :key="item.name" :item="item" :store="store"/>
+ </div>
+</template>
+
+<style scoped>
+.view-file-tree-items {
+ display: flex;
+ flex-direction: column;
+ gap: 1px;
+ margin-right: .5rem;
+}
+</style>
diff --git a/web_src/js/components/ViewFileTreeItem.vue b/web_src/js/components/ViewFileTreeItem.vue
new file mode 100644
index 0000000000..5173c7eb46
--- /dev/null
+++ b/web_src/js/components/ViewFileTreeItem.vue
@@ -0,0 +1,128 @@
+<script lang="ts" setup>
+import {SvgIcon} from '../svg.ts';
+import {isPlainClick} from '../utils/dom.ts';
+import {shallowRef} from 'vue';
+import {type createViewFileTreeStore} from './ViewFileTreeStore.ts';
+
+type Item = {
+ entryName: string;
+ entryMode: 'blob' | 'exec' | 'tree' | 'commit' | 'symlink' | 'unknown';
+ entryIcon: string;
+ entryIconOpen: string;
+ fullPath: string;
+ submoduleUrl?: string;
+ children?: Item[];
+};
+
+const props = defineProps<{
+ item: Item,
+ store: ReturnType<typeof createViewFileTreeStore>
+}>();
+
+const store = props.store;
+const isLoading = shallowRef(false);
+const children = shallowRef(props.item.children);
+const collapsed = shallowRef(!props.item.children);
+
+const doLoadChildren = async () => {
+ collapsed.value = !collapsed.value;
+ if (!collapsed.value) {
+ isLoading.value = true;
+ try {
+ children.value = await store.loadChildren(props.item.fullPath);
+ } finally {
+ isLoading.value = false;
+ }
+ }
+};
+
+const onItemClick = (e: MouseEvent) => {
+ // only handle the click event with page partial reloading if the user didn't press any special key
+ // let browsers handle special keys like "Ctrl+Click"
+ if (!isPlainClick(e)) return;
+ e.preventDefault();
+ if (props.item.entryMode === 'tree') doLoadChildren();
+ store.navigateTreeView(props.item.fullPath);
+};
+
+</script>
+
+<template>
+ <a
+ class="tree-item silenced"
+ :class="{
+ 'selected': store.selectedItem === item.fullPath,
+ 'type-submodule': item.entryMode === 'commit',
+ 'type-directory': item.entryMode === 'tree',
+ 'type-symlink': item.entryMode === 'symlink',
+ 'type-file': item.entryMode === 'blob' || item.entryMode === 'exec',
+ }"
+ :title="item.entryName"
+ :href="store.buildTreePathWebUrl(item.fullPath)"
+ @click.stop="onItemClick"
+ >
+ <div v-if="item.entryMode === 'tree'" class="item-toggle">
+ <SvgIcon v-if="isLoading" name="octicon-sync" class="circular-spin"/>
+ <SvgIcon v-else :name="collapsed ? 'octicon-chevron-right' : 'octicon-chevron-down'" @click.stop.prevent="doLoadChildren"/>
+ </div>
+ <div class="item-content">
+ <!-- eslint-disable-next-line vue/no-v-html -->
+ <span class="tw-contents" v-html="(!collapsed && item.entryIconOpen) ? item.entryIconOpen : item.entryIcon"/>
+ <span class="gt-ellipsis">{{ item.entryName }}</span>
+ </div>
+ </a>
+
+ <div v-if="children?.length" v-show="!collapsed" class="sub-items">
+ <ViewFileTreeItem v-for="childItem in children" :key="childItem.entryName" :item="childItem" :store="store"/>
+ </div>
+</template>
+
+<style scoped>
+.sub-items {
+ display: flex;
+ flex-direction: column;
+ gap: 1px;
+ margin-left: 14px;
+ border-left: 1px solid var(--color-secondary);
+}
+
+.tree-item.selected {
+ color: var(--color-text);
+ background: var(--color-active);
+ border-radius: 4px;
+}
+
+.tree-item.type-directory {
+ user-select: none;
+}
+
+.tree-item {
+ display: grid;
+ grid-template-columns: 16px 1fr;
+ grid-template-areas: "toggle content";
+ gap: 0.25em;
+ padding: 6px;
+}
+
+.tree-item:hover {
+ color: var(--color-text);
+ background: var(--color-hover);
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.item-toggle {
+ grid-area: toggle;
+ display: flex;
+ align-items: center;
+}
+
+.item-content {
+ grid-area: content;
+ display: flex;
+ align-items: center;
+ gap: 0.5em;
+ text-overflow: ellipsis;
+ min-width: 0;
+}
+</style>
diff --git a/web_src/js/components/ViewFileTreeStore.ts b/web_src/js/components/ViewFileTreeStore.ts
new file mode 100644
index 0000000000..e2155bd58a
--- /dev/null
+++ b/web_src/js/components/ViewFileTreeStore.ts
@@ -0,0 +1,45 @@
+import {reactive} from 'vue';
+import {GET} from '../modules/fetch.ts';
+import {pathEscapeSegments} from '../utils/url.ts';
+import {createElementFromHTML} from '../utils/dom.ts';
+import {html} from '../utils/html.ts';
+
+export function createViewFileTreeStore(props: { repoLink: string, treePath: string, currentRefNameSubURL: string}) {
+ const store = reactive({
+ rootFiles: [],
+ selectedItem: props.treePath,
+
+ async loadChildren(treePath: string, subPath: string = '') {
+ const response = await GET(`${props.repoLink}/tree-view/${props.currentRefNameSubURL}/${pathEscapeSegments(treePath)}?sub_path=${encodeURIComponent(subPath)}`);
+ const json = await response.json();
+ const poolSvgs = [];
+ for (const [svgId, svgContent] of Object.entries(json.renderedIconPool ?? {})) {
+ if (!document.querySelector(`.global-svg-icon-pool #${svgId}`)) poolSvgs.push(svgContent);
+ }
+ if (poolSvgs.length) {
+ const svgContainer = createElementFromHTML(html`<div class="global-svg-icon-pool tw-hidden"></div>`);
+ svgContainer.innerHTML = poolSvgs.join('');
+ document.body.append(svgContainer);
+ }
+ return json.fileTreeNodes ?? null;
+ },
+
+ async loadViewContent(url: string) {
+ url = url.includes('?') ? url.replace('?', '?only_content=true') : `${url}?only_content=true`;
+ const response = await GET(url);
+ document.querySelector('.repo-view-content').innerHTML = await response.text();
+ },
+
+ async navigateTreeView(treePath: string) {
+ const url = store.buildTreePathWebUrl(treePath);
+ window.history.pushState({treePath, url}, null, url);
+ store.selectedItem = treePath;
+ await store.loadViewContent(url);
+ },
+
+ buildTreePathWebUrl(treePath: string) {
+ return `${props.repoLink}/src/${props.currentRefNameSubURL}/${pathEscapeSegments(treePath)}`;
+ },
+ });
+ return store;
+}
diff --git a/web_src/js/features/admin/common.ts b/web_src/js/features/admin/common.ts
index 6c725a3efe..4ed5d62eee 100644
--- a/web_src/js/features/admin/common.ts
+++ b/web_src/js/features/admin/common.ts
@@ -1,7 +1,7 @@
-import $ from 'jquery';
import {checkAppUrl} from '../common-page.ts';
-import {hideElem, showElem, toggleElem} from '../../utils/dom.ts';
+import {hideElem, queryElems, showElem, toggleElem} from '../../utils/dom.ts';
import {POST} from '../../modules/fetch.ts';
+import {fomanticQuery} from '../../modules/fomantic/base.ts';
const {appSubUrl} = window.config;
@@ -19,32 +19,47 @@ export function initAdminCommon(): void {
// check whether appUrl(ROOT_URL) is correct, if not, show an error message
checkAppUrl();
- // New user
- if ($('.admin.new.user').length > 0 || $('.admin.edit.user').length > 0) {
- document.querySelector<HTMLInputElement>('#login_type')?.addEventListener('change', function () {
- if (this.value?.startsWith('0')) {
- document.querySelector<HTMLInputElement>('#user_name')?.removeAttribute('disabled');
- document.querySelector<HTMLInputElement>('#login_name')?.removeAttribute('required');
- hideElem('.non-local');
- showElem('.local');
- document.querySelector<HTMLInputElement>('#user_name')?.focus();
+ initAdminUser();
+ initAdminAuthentication();
+ initAdminNotice();
+}
- if (this.getAttribute('data-password') === 'required') {
- document.querySelector('#password')?.setAttribute('required', 'required');
- }
- } else {
- if (document.querySelector<HTMLDivElement>('.admin.edit.user')) {
- document.querySelector<HTMLInputElement>('#user_name')?.setAttribute('disabled', 'disabled');
- }
- document.querySelector<HTMLInputElement>('#login_name')?.setAttribute('required', 'required');
- showElem('.non-local');
- hideElem('.local');
- document.querySelector<HTMLInputElement>('#login_name')?.focus();
+function initAdminUser() {
+ const pageContent = document.querySelector('.page-content.admin.edit.user, .page-content.admin.new.user');
+ if (!pageContent) return;
- document.querySelector<HTMLInputElement>('#password')?.removeAttribute('required');
+ document.querySelector<HTMLInputElement>('#login_type')?.addEventListener('change', function () {
+ if (this.value?.startsWith('0')) {
+ document.querySelector<HTMLInputElement>('#user_name')?.removeAttribute('disabled');
+ document.querySelector<HTMLInputElement>('#login_name')?.removeAttribute('required');
+ hideElem('.non-local');
+ showElem('.local');
+ document.querySelector<HTMLInputElement>('#user_name')?.focus();
+
+ if (this.getAttribute('data-password') === 'required') {
+ document.querySelector('#password')?.setAttribute('required', 'required');
}
- });
- }
+ } else {
+ if (document.querySelector<HTMLDivElement>('.admin.edit.user')) {
+ document.querySelector<HTMLInputElement>('#user_name')?.setAttribute('disabled', 'disabled');
+ }
+ document.querySelector<HTMLInputElement>('#login_name')?.setAttribute('required', 'required');
+ showElem('.non-local');
+ hideElem('.local');
+ document.querySelector<HTMLInputElement>('#login_name')?.focus();
+
+ document.querySelector<HTMLInputElement>('#password')?.removeAttribute('required');
+ }
+ });
+}
+
+function initAdminAuthentication() {
+ const pageContent = document.querySelector('.page-content.admin.authentication');
+ if (!pageContent) return;
+
+ const isNewPage = pageContent.classList.contains('new');
+ const isEditPage = pageContent.classList.contains('edit');
+ if (!isNewPage && !isEditPage) return;
function onUsePagedSearchChange() {
const searchPageSizeElements = document.querySelectorAll<HTMLDivElement>('.search-page-size');
@@ -90,7 +105,7 @@ export function initAdminCommon(): void {
onOAuth2UseCustomURLChange(applyDefaultValues);
}
- function onOAuth2UseCustomURLChange(applyDefaultValues) {
+ function onOAuth2UseCustomURLChange(applyDefaultValues: boolean) {
const provider = document.querySelector<HTMLInputElement>('#oauth2_provider').value;
hideElem('.oauth2_use_custom_url_field');
for (const input of document.querySelectorAll<HTMLInputElement>('.oauth2_use_custom_url_field input[required]')) {
@@ -119,9 +134,11 @@ export function initAdminCommon(): void {
toggleElem(document.querySelector('#ldap-group-options'), checked);
}
+ const elAuthType = document.querySelector<HTMLInputElement>('#auth_type');
+
// New authentication
- if (document.querySelector<HTMLDivElement>('.admin.new.authentication')) {
- document.querySelector<HTMLInputElement>('#auth_type')?.addEventListener('change', function () {
+ if (isNewPage) {
+ const onAuthTypeChange = function () {
hideElem('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size, .sspi');
for (const input of document.querySelectorAll<HTMLInputElement>('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required], .sspi input[required]')) {
@@ -130,7 +147,7 @@ export function initAdminCommon(): void {
document.querySelector<HTMLDivElement>('.binddnrequired')?.classList.remove('required');
- const authType = this.value;
+ const authType = elAuthType.value;
switch (authType) {
case '2': // LDAP
showElem('.ldap');
@@ -179,20 +196,23 @@ export function initAdminCommon(): void {
if (authType === '2') {
onUsePagedSearchChange();
}
- });
- $('#auth_type').trigger('change');
+ };
+ elAuthType.addEventListener('change', onAuthTypeChange);
+ onAuthTypeChange();
+
document.querySelector<HTMLInputElement>('#security_protocol')?.addEventListener('change', onSecurityProtocolChange);
document.querySelector<HTMLInputElement>('#use_paged_search')?.addEventListener('change', onUsePagedSearchChange);
document.querySelector<HTMLInputElement>('#oauth2_provider')?.addEventListener('change', () => onOAuth2Change(true));
document.querySelector<HTMLInputElement>('#oauth2_use_custom_url')?.addEventListener('change', () => onOAuth2UseCustomURLChange(true));
- $('.js-ldap-group-toggle').on('change', onEnableLdapGroupsChange);
+
+ document.querySelector('.js-ldap-group-toggle').addEventListener('change', onEnableLdapGroupsChange);
}
// Edit authentication
- if (document.querySelector<HTMLDivElement>('.admin.edit.authentication')) {
- const authType = document.querySelector<HTMLInputElement>('#auth_type')?.value;
+ if (isEditPage) {
+ const authType = elAuthType.value;
if (authType === '2' || authType === '5') {
document.querySelector<HTMLInputElement>('#security_protocol')?.addEventListener('change', onSecurityProtocolChange);
- $('.js-ldap-group-toggle').on('change', onEnableLdapGroupsChange);
+ document.querySelector('.js-ldap-group-toggle').addEventListener('change', onEnableLdapGroupsChange);
onEnableLdapGroupsChange();
if (authType === '2') {
document.querySelector<HTMLInputElement>('#use_paged_search')?.addEventListener('change', onUsePagedSearchChange);
@@ -204,58 +224,63 @@ export function initAdminCommon(): void {
}
}
- if (document.querySelector<HTMLDivElement>('.admin.authentication')) {
- $('#auth_name').on('input', function () {
- // appSubUrl is either empty or is a path that starts with `/` and doesn't have a trailing slash.
- document.querySelector('#oauth2-callback-url').textContent = `${window.location.origin}${appSubUrl}/user/oauth2/${encodeURIComponent((this as HTMLInputElement).value)}/callback`;
- }).trigger('input');
- }
+ const elAuthName = document.querySelector<HTMLInputElement>('#auth_name');
+ const onAuthNameChange = function () {
+ // appSubUrl is either empty or is a path that starts with `/` and doesn't have a trailing slash.
+ document.querySelector('#oauth2-callback-url').textContent = `${window.location.origin}${appSubUrl}/user/oauth2/${encodeURIComponent(elAuthName.value)}/callback`;
+ };
+ elAuthName.addEventListener('input', onAuthNameChange);
+ onAuthNameChange();
+}
- // Notice
- if (document.querySelector<HTMLDivElement>('.admin.notice')) {
- const detailModal = document.querySelector<HTMLDivElement>('#detail-modal');
+function initAdminNotice() {
+ const pageContent = document.querySelector('.page-content.admin.notice');
+ if (!pageContent) return;
- // Attach view detail modals
- $('.view-detail').on('click', function () {
- const description = this.closest('tr').querySelector('.notice-description').textContent;
- detailModal.querySelector('.content pre').textContent = description;
- $(detailModal).modal('show');
- return false;
- });
+ const detailModal = document.querySelector<HTMLDivElement>('#detail-modal');
- // Select actions
- const checkboxes = document.querySelectorAll<HTMLInputElement>('.select.table .ui.checkbox input');
+ // Attach view detail modals
+ queryElems(pageContent, '.view-detail', (el) => el.addEventListener('click', (e) => {
+ e.preventDefault();
+ const elNoticeDesc = el.closest('tr').querySelector('.notice-description');
+ const elModalDesc = detailModal.querySelector('.content pre');
+ elModalDesc.textContent = elNoticeDesc.textContent;
+ fomanticQuery(detailModal).modal('show');
+ }));
- $('.select.action').on('click', function () {
- switch ($(this).data('action')) {
- case 'select-all':
- for (const checkbox of checkboxes) {
- checkbox.checked = true;
- }
- break;
- case 'deselect-all':
- for (const checkbox of checkboxes) {
- checkbox.checked = false;
- }
- break;
- case 'inverse':
- for (const checkbox of checkboxes) {
- checkbox.checked = !checkbox.checked;
- }
- break;
- }
- });
- document.querySelector<HTMLButtonElement>('#delete-selection')?.addEventListener('click', async function (e) {
- e.preventDefault();
- this.classList.add('is-loading', 'disabled');
- const data = new FormData();
- for (const checkbox of checkboxes) {
- if (checkbox.checked) {
- data.append('ids[]', checkbox.closest('.ui.checkbox').getAttribute('data-id'));
+ // Select actions
+ const checkboxes = document.querySelectorAll<HTMLInputElement>('.select.table .ui.checkbox input');
+
+ queryElems(pageContent, '.select.action', (el) => el.addEventListener('click', () => {
+ switch (el.getAttribute('data-action')) {
+ case 'select-all':
+ for (const checkbox of checkboxes) {
+ checkbox.checked = true;
}
+ break;
+ case 'deselect-all':
+ for (const checkbox of checkboxes) {
+ checkbox.checked = false;
+ }
+ break;
+ case 'inverse':
+ for (const checkbox of checkboxes) {
+ checkbox.checked = !checkbox.checked;
+ }
+ break;
+ }
+ }));
+
+ document.querySelector<HTMLButtonElement>('#delete-selection')?.addEventListener('click', async function (e) {
+ e.preventDefault();
+ this.classList.add('is-loading', 'disabled');
+ const data = new FormData();
+ for (const checkbox of checkboxes) {
+ if (checkbox.checked) {
+ data.append('ids[]', checkbox.closest('.ui.checkbox').getAttribute('data-id'));
}
- await POST(this.getAttribute('data-link'), {data});
- window.location.href = this.getAttribute('data-redirect');
- });
- }
+ }
+ await POST(this.getAttribute('data-link'), {data});
+ window.location.href = this.getAttribute('data-redirect');
+ });
}
diff --git a/web_src/js/features/autofocus-end.ts b/web_src/js/features/autofocus-end.ts
deleted file mode 100644
index 53e475b543..0000000000
--- a/web_src/js/features/autofocus-end.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export function initAutoFocusEnd() {
- for (const el of document.querySelectorAll<HTMLInputElement>('.js-autofocus-end')) {
- el.focus(); // expects only one such element on one page. If there are many, then the last one gets the focus.
- el.setSelectionRange(el.value.length, el.value.length);
- }
-}
diff --git a/web_src/js/features/captcha.ts b/web_src/js/features/captcha.ts
index 69b4aa6852..df234d0e5c 100644
--- a/web_src/js/features/captcha.ts
+++ b/web_src/js/features/captcha.ts
@@ -34,13 +34,18 @@ export async function initCaptcha() {
break;
}
case 'm-captcha': {
- const {default: mCaptcha} = await import(/* webpackChunkName: "mcaptcha-vanilla-glue" */'@mcaptcha/vanilla-glue');
- // @ts-expect-error
+ const mCaptcha = await import(/* webpackChunkName: "mcaptcha-vanilla-glue" */'@mcaptcha/vanilla-glue');
+
+ // FIXME: the mCaptcha code is not right, it's a miracle that the wrong code could run
+ // * the "vanilla-glue" has some problems with es6 module.
+ // * the INPUT_NAME is a "const", it should not be changed.
+ // * the "mCaptcha.default" is actually the "Widget".
+
+ // @ts-expect-error TS2540: Cannot assign to 'INPUT_NAME' because it is a read-only property.
mCaptcha.INPUT_NAME = 'm-captcha-response';
const instanceURL = captchaEl.getAttribute('data-instance-url');
- // @ts-expect-error
- mCaptcha.default({
+ new mCaptcha.default({
siteKey: {
instanceUrl: new URL(instanceURL),
key: siteKey,
diff --git a/web_src/js/features/citation.ts b/web_src/js/features/citation.ts
index fc5bb38f0a..3c9fe0afc8 100644
--- a/web_src/js/features/citation.ts
+++ b/web_src/js/features/citation.ts
@@ -5,9 +5,13 @@ const {pageData} = window.config;
async function initInputCitationValue(citationCopyApa: HTMLButtonElement, citationCopyBibtex: HTMLButtonElement) {
const [{Cite, plugins}] = await Promise.all([
+ // @ts-expect-error: module exports no types
import(/* webpackChunkName: "citation-js-core" */'@citation-js/core'),
+ // @ts-expect-error: module exports no types
import(/* webpackChunkName: "citation-js-formats" */'@citation-js/plugin-software-formats'),
+ // @ts-expect-error: module exports no types
import(/* webpackChunkName: "citation-js-bibtex" */'@citation-js/plugin-bibtex'),
+ // @ts-expect-error: module exports no types
import(/* webpackChunkName: "citation-js-csl" */'@citation-js/plugin-csl'),
]);
const {citationFileContent} = pageData;
diff --git a/web_src/js/features/common-button.test.ts b/web_src/js/features/common-button.test.ts
new file mode 100644
index 0000000000..f41bafbc79
--- /dev/null
+++ b/web_src/js/features/common-button.test.ts
@@ -0,0 +1,14 @@
+import {assignElementProperty} from './common-button.ts';
+
+test('assignElementProperty', () => {
+ const elForm = document.createElement('form');
+ assignElementProperty(elForm, 'action', '/test-link');
+ expect(elForm.action).contains('/test-link'); // the DOM always returns absolute URL
+ assignElementProperty(elForm, 'text-content', 'dummy');
+ expect(elForm.textContent).toBe('dummy');
+
+ const elInput = document.createElement('input');
+ expect(elInput.readOnly).toBe(false);
+ assignElementProperty(elInput, 'read-only', 'true');
+ expect(elInput.readOnly).toBe(true);
+});
diff --git a/web_src/js/features/common-button.ts b/web_src/js/features/common-button.ts
index acce992b90..3b112b116b 100644
--- a/web_src/js/features/common-button.ts
+++ b/web_src/js/features/common-button.ts
@@ -1,5 +1,5 @@
import {POST} from '../modules/fetch.ts';
-import {addDelegatedEventListener, hideElem, queryElems, showElem, toggleElem} from '../utils/dom.ts';
+import {addDelegatedEventListener, hideElem, isElemVisible, showElem, toggleElem} from '../utils/dom.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
import {camelize} from 'vue';
@@ -17,7 +17,8 @@ export function initGlobalDeleteButton(): void {
// Some model/form elements will be filled by `data-id` / `data-name` / `data-data-xxx` attributes.
// If there is a form defined by `data-form`, then the form will be submitted as-is (without any modification).
// If there is no form, then the data will be posted to `data-url`.
- // TODO: it's not encouraged to use this method. `show-modal` does far better than this.
+ // TODO: do not use this method in new code. `show-modal` / `link-action(data-modal-confirm)` does far better than this.
+ // FIXME: all legacy `delete-button` should be refactored to use `show-modal` or `link-action`
for (const btn of document.querySelectorAll<HTMLElement>('.delete-button')) {
btn.addEventListener('click', (e) => {
e.preventDefault();
@@ -42,13 +43,16 @@ export function initGlobalDeleteButton(): void {
fomanticQuery(modal).modal({
closable: false,
- onApprove: async () => {
+ onApprove: () => {
// if `data-type="form"` exists, then submit the form by the selector provided by `data-form="..."`
if (btn.getAttribute('data-type') === 'form') {
const formSelector = btn.getAttribute('data-form');
const form = document.querySelector<HTMLFormElement>(formSelector);
if (!form) throw new Error(`no form named ${formSelector} found`);
+ modal.classList.add('is-loading'); // the form is not in the modal, so also add loading indicator to the modal
+ form.classList.add('is-loading');
form.submit();
+ return false; // prevent modal from closing automatically
}
// prepare an AJAX form by data attributes
@@ -61,34 +65,36 @@ export function initGlobalDeleteButton(): void {
postData.append('id', value);
}
}
-
- const response = await POST(btn.getAttribute('data-url'), {data: postData});
- if (response.ok) {
- const data = await response.json();
- window.location.href = data.redirect;
- }
+ (async () => {
+ const response = await POST(btn.getAttribute('data-url'), {data: postData});
+ if (response.ok) {
+ const data = await response.json();
+ window.location.href = data.redirect;
+ }
+ })();
+ modal.classList.add('is-loading'); // the request is in progress, so also add loading indicator to the modal
+ return false; // prevent modal from closing automatically
},
}).modal('show');
});
}
}
-function onShowPanelClick(e) {
+function onShowPanelClick(el: HTMLElement, e: MouseEvent) {
// a '.show-panel' element can show a panel, by `data-panel="selector"`
// if it has "toggle" class, it toggles the panel
- const el = e.currentTarget;
e.preventDefault();
const sel = el.getAttribute('data-panel');
- if (el.classList.contains('toggle')) {
- toggleElem(sel);
- } else {
- showElem(sel);
+ const elems = el.classList.contains('toggle') ? toggleElem(sel) : showElem(sel);
+ for (const elem of elems) {
+ if (isElemVisible(elem as HTMLElement)) {
+ elem.querySelector<HTMLElement>('[autofocus]')?.focus();
+ }
}
}
-function onHidePanelClick(e) {
+function onHidePanelClick(el: HTMLElement, e: MouseEvent) {
// a `.hide-panel` element can hide a panel, by `data-panel="selector"` or `data-panel-closest="selector"`
- const el = e.currentTarget;
e.preventDefault();
let sel = el.getAttribute('data-panel');
if (sel) {
@@ -97,21 +103,35 @@ function onHidePanelClick(e) {
}
sel = el.getAttribute('data-panel-closest');
if (sel) {
- hideElem(el.parentNode.closest(sel));
+ hideElem((el.parentNode as HTMLElement).closest(sel));
return;
}
throw new Error('no panel to hide'); // should never happen, otherwise there is a bug in code
}
-function onShowModalClick(e) {
+export function assignElementProperty(el: any, name: string, val: string) {
+ name = camelize(name);
+ const old = el[name];
+ if (typeof old === 'boolean') {
+ el[name] = val === 'true';
+ } else if (typeof old === 'number') {
+ el[name] = parseFloat(val);
+ } else if (typeof old === 'string') {
+ el[name] = val;
+ } else {
+ // in the future, we could introduce a better typing system like `data-modal-form.action:string="..."`
+ throw new Error(`cannot assign element property ${name} by value ${val}`);
+ }
+}
+
+function onShowModalClick(el: HTMLElement, e: MouseEvent) {
// A ".show-modal" button will show a modal dialog defined by its "data-modal" attribute.
// Each "data-modal-{target}" attribute will be filled to target element's value or text-content.
// * First, try to query '#target'
// * Then, try to query '[name=target]'
// * Then, try to query '.target'
// * Then, try to query 'target' as HTML tag
- // If there is a ".{attr}" part like "data-modal-form.action", then the form's "action" attribute will be set.
- const el = e.currentTarget;
+ // If there is a ".{prop-name}" part like "data-modal-form.action", the "form" element's "action" property will be set, the "prop-name" will be camel-cased to "propName".
e.preventDefault();
const modalSelector = el.getAttribute('data-modal');
const elModal = document.querySelector(modalSelector);
@@ -124,33 +144,28 @@ function onShowModalClick(e) {
}
const attrTargetCombo = attrib.name.substring(modalAttrPrefix.length);
- const [attrTargetName, attrTargetAttr] = attrTargetCombo.split('.');
- // try to find target by: "#target" -> "[name=target]" -> ".target" -> "<target> tag"
+ const [attrTargetName, attrTargetProp] = attrTargetCombo.split('.');
+ // try to find target by: "#target" -> "[name=target]" -> ".target" -> "<target> tag", and then try the modal itself
const attrTarget = elModal.querySelector(`#${attrTargetName}`) ||
elModal.querySelector(`[name=${attrTargetName}]`) ||
elModal.querySelector(`.${attrTargetName}`) ||
- elModal.querySelector(`${attrTargetName}`);
+ elModal.querySelector(`${attrTargetName}`) ||
+ (elModal.matches(`${attrTargetName}`) || elModal.matches(`#${attrTargetName}`) || elModal.matches(`.${attrTargetName}`) ? elModal : null);
if (!attrTarget) {
if (!window.config.runModeIsProd) throw new Error(`attr target "${attrTargetCombo}" not found for modal`);
continue;
}
- if (attrTargetAttr) {
- attrTarget[camelize(attrTargetAttr)] = attrib.value;
+ if (attrTargetProp) {
+ assignElementProperty(attrTarget, attrTargetProp, attrib.value);
} else if (attrTarget.matches('input, textarea')) {
- attrTarget.value = attrib.value; // FIXME: add more supports like checkbox
+ (attrTarget as HTMLInputElement | HTMLTextAreaElement).value = attrib.value; // FIXME: add more supports like checkbox
} else {
attrTarget.textContent = attrib.value; // FIXME: it should be more strict here, only handle div/span/p
}
}
- fomanticQuery(elModal).modal('setting', {
- onApprove: () => {
- // "form-fetch-action" can handle network errors gracefully,
- // so keep the modal dialog to make users can re-submit the form if anything wrong happens.
- if (elModal.querySelector('.form-fetch-action')) return false;
- },
- }).modal('show');
+ fomanticQuery(elModal).modal('show');
}
export function initGlobalButtons(): void {
@@ -159,7 +174,15 @@ export function initGlobalButtons(): void {
// There are a few cancel buttons in non-modal forms, and there are some dynamically created forms (eg: the "Edit Issue Content")
addDelegatedEventListener(document, 'click', 'form button.ui.cancel.button', (_ /* el */, e) => e.preventDefault());
- queryElems(document, '.show-panel', (el) => el.addEventListener('click', onShowPanelClick));
- queryElems(document, '.hide-panel', (el) => el.addEventListener('click', onHidePanelClick));
- queryElems(document, '.show-modal', (el) => el.addEventListener('click', onShowModalClick));
+ // Ideally these "button" events should be handled by registerGlobalEventFunc
+ // Refactoring would involve too many changes, so at the moment, just use the global event listener.
+ addDelegatedEventListener(document, 'click', '.show-panel, .hide-panel, .show-modal', (el, e: MouseEvent) => {
+ if (el.classList.contains('show-panel')) {
+ onShowPanelClick(el, e);
+ } else if (el.classList.contains('hide-panel')) {
+ onHidePanelClick(el, e);
+ } else if (el.classList.contains('show-modal')) {
+ onShowModalClick(el, e);
+ }
+ });
}
diff --git a/web_src/js/features/common-fetch-action.ts b/web_src/js/features/common-fetch-action.ts
index bc72f4089a..3ca361b6e2 100644
--- a/web_src/js/features/common-fetch-action.ts
+++ b/web_src/js/features/common-fetch-action.ts
@@ -1,11 +1,11 @@
import {request} from '../modules/fetch.ts';
-import {showErrorToast} from '../modules/toast.ts';
-import {addDelegatedEventListener, submitEventSubmitter} from '../utils/dom.ts';
-import {confirmModal} from './comp/ConfirmModal.ts';
+import {hideToastsAll, showErrorToast} from '../modules/toast.ts';
+import {addDelegatedEventListener, createElementFromHTML, submitEventSubmitter} from '../utils/dom.ts';
+import {confirmModal, createConfirmModal} from './comp/ConfirmModal.ts';
import type {RequestOpts} from '../types.ts';
import {ignoreAreYouSure} from '../vendor/jquery.are-you-sure.ts';
-const {appSubUrl, i18n} = window.config;
+const {appSubUrl} = window.config;
// fetchActionDoRedirect does real redirection to bypass the browser's limitations of "location"
// more details are in the backend's fetch-redirect handler
@@ -23,10 +23,20 @@ function fetchActionDoRedirect(redirect: string) {
}
async function fetchActionDoRequest(actionElem: HTMLElement, url: string, opt: RequestOpts) {
+ const showErrorForResponse = (code: number, message: string) => {
+ showErrorToast(`Error ${code || 'request'}: ${message}`);
+ };
+
+ let respStatus = 0;
+ let respText = '';
try {
+ hideToastsAll();
const resp = await request(url, opt);
- if (resp.status === 200) {
- let {redirect} = await resp.json();
+ respStatus = resp.status;
+ respText = await resp.text();
+ const respJson = JSON.parse(respText);
+ if (respStatus === 200) {
+ let {redirect} = respJson;
redirect = redirect || actionElem.getAttribute('data-redirect');
ignoreAreYouSure(actionElem); // ignore the areYouSure check before reloading
if (redirect) {
@@ -35,29 +45,32 @@ async function fetchActionDoRequest(actionElem: HTMLElement, url: string, opt: R
window.location.reload();
}
return;
- } else if (resp.status >= 400 && resp.status < 500) {
- const data = await resp.json();
+ }
+
+ if (respStatus >= 400 && respStatus < 500 && respJson?.errorMessage) {
// the code was quite messy, sometimes the backend uses "err", sometimes it uses "error", and even "user_error"
// but at the moment, as a new approach, we only use "errorMessage" here, backend can use JSONError() to respond.
- if (data.errorMessage) {
- showErrorToast(data.errorMessage, {useHtmlBody: data.renderFormat === 'html'});
- } else {
- showErrorToast(`server error: ${resp.status}`);
- }
+ showErrorToast(respJson.errorMessage, {useHtmlBody: respJson.renderFormat === 'html'});
} else {
- showErrorToast(`server error: ${resp.status}`);
+ showErrorForResponse(respStatus, respText);
}
} catch (e) {
- if (e.name !== 'AbortError') {
- console.error('error when doRequest', e);
- showErrorToast(`${i18n.network_error} ${e}`);
+ if (e.name === 'SyntaxError') {
+ showErrorForResponse(respStatus, (respText || '').substring(0, 100));
+ } else if (e.name !== 'AbortError') {
+ console.error('fetchActionDoRequest error', e);
+ showErrorForResponse(respStatus, `${e}`);
}
}
actionElem.classList.remove('is-loading', 'loading-icon-2px');
}
-async function formFetchAction(formEl: HTMLFormElement, e: SubmitEvent) {
+async function onFormFetchActionSubmit(formEl: HTMLFormElement, e: SubmitEvent) {
e.preventDefault();
+ await submitFormFetchAction(formEl, submitEventSubmitter(e));
+}
+
+export async function submitFormFetchAction(formEl: HTMLFormElement, formSubmitter?: HTMLElement) {
if (formEl.classList.contains('is-loading')) return;
formEl.classList.add('is-loading');
@@ -66,16 +79,18 @@ async function formFetchAction(formEl: HTMLFormElement, e: SubmitEvent) {
}
const formMethod = formEl.getAttribute('method') || 'get';
- const formActionUrl = formEl.getAttribute('action');
+ const formActionUrl = formEl.getAttribute('action') || window.location.href;
const formData = new FormData(formEl);
- const formSubmitter = submitEventSubmitter(e);
const [submitterName, submitterValue] = [formSubmitter?.getAttribute('name'), formSubmitter?.getAttribute('value')];
if (submitterName) {
formData.append(submitterName, submitterValue || '');
}
let reqUrl = formActionUrl;
- const reqOpt = {method: formMethod.toUpperCase(), body: null};
+ const reqOpt = {
+ method: formMethod.toUpperCase(),
+ body: null as FormData | null,
+ };
if (formMethod.toLowerCase() === 'get') {
const params = new URLSearchParams();
for (const [key, value] of formData) {
@@ -93,36 +108,52 @@ async function formFetchAction(formEl: HTMLFormElement, e: SubmitEvent) {
await fetchActionDoRequest(formEl, reqUrl, reqOpt);
}
-async function linkAction(el: HTMLElement, e: Event) {
+async function onLinkActionClick(el: HTMLElement, e: Event) {
// A "link-action" can post AJAX request to its "data-url"
// Then the browser is redirected to: the "redirect" in response, or "data-redirect" attribute, or current URL by reloading.
- // If the "link-action" has "data-modal-confirm" attribute, a confirm modal dialog will be shown before taking action.
+ // If the "link-action" has "data-modal-confirm" attribute, a "confirm modal dialog" will be shown before taking action.
+ // Attribute "data-modal-confirm" can be a modal element by "#the-modal-id", or a string content for the modal dialog.
e.preventDefault();
const url = el.getAttribute('data-url');
const doRequest = async () => {
- if ('disabled' in el) el.disabled = true; // el could be A or BUTTON, but A doesn't have disabled attribute
+ if ('disabled' in el) el.disabled = true; // el could be A or BUTTON, but "A" doesn't have the "disabled" attribute
await fetchActionDoRequest(el, url, {method: el.getAttribute('data-link-action-method') || 'POST'});
if ('disabled' in el) el.disabled = false;
};
- const modalConfirmContent = el.getAttribute('data-modal-confirm') ||
- el.getAttribute('data-modal-confirm-content') || '';
- if (!modalConfirmContent) {
+ let elModal: HTMLElement | null = null;
+ const dataModalConfirm = el.getAttribute('data-modal-confirm') || '';
+ if (dataModalConfirm.startsWith('#')) {
+ // eslint-disable-next-line unicorn/prefer-query-selector
+ elModal = document.getElementById(dataModalConfirm.substring(1));
+ if (elModal) {
+ elModal = createElementFromHTML(elModal.outerHTML);
+ elModal.removeAttribute('id');
+ }
+ }
+ if (!elModal) {
+ const modalConfirmContent = dataModalConfirm || el.getAttribute('data-modal-confirm-content') || '';
+ if (modalConfirmContent) {
+ const isRisky = el.classList.contains('red') || el.classList.contains('negative');
+ elModal = createConfirmModal({
+ header: el.getAttribute('data-modal-confirm-header') || '',
+ content: modalConfirmContent,
+ confirmButtonColor: isRisky ? 'red' : 'primary',
+ });
+ }
+ }
+
+ if (!elModal) {
await doRequest();
return;
}
- const isRisky = el.classList.contains('red') || el.classList.contains('negative');
- if (await confirmModal({
- header: el.getAttribute('data-modal-confirm-header') || '',
- content: modalConfirmContent,
- confirmButtonColor: isRisky ? 'red' : 'primary',
- })) {
+ if (await confirmModal(elModal)) {
await doRequest();
}
}
export function initGlobalFetchAction() {
- addDelegatedEventListener(document, 'submit', '.form-fetch-action', formFetchAction);
- addDelegatedEventListener(document, 'click', '.link-action', linkAction);
+ addDelegatedEventListener(document, 'submit', '.form-fetch-action', onFormFetchActionSubmit);
+ addDelegatedEventListener(document, 'click', '.link-action', onLinkActionClick);
}
diff --git a/web_src/js/features/common-form.ts b/web_src/js/features/common-form.ts
index 8532d397cd..7321d80c44 100644
--- a/web_src/js/features/common-form.ts
+++ b/web_src/js/features/common-form.ts
@@ -17,13 +17,13 @@ export function initGlobalEnterQuickSubmit() {
if (e.key !== 'Enter') return;
const hasCtrlOrMeta = ((e.ctrlKey || e.metaKey) && !e.altKey);
if (hasCtrlOrMeta && e.target.matches('textarea')) {
- if (handleGlobalEnterQuickSubmit(e.target)) {
+ if (handleGlobalEnterQuickSubmit(e.target as HTMLElement)) {
e.preventDefault();
}
} else if (e.target.matches('input') && !e.target.closest('form')) {
// input in a normal form could handle Enter key by default, so we only handle the input outside a form
// eslint-disable-next-line unicorn/no-lonely-if
- if (handleGlobalEnterQuickSubmit(e.target)) {
+ if (handleGlobalEnterQuickSubmit(e.target as HTMLElement)) {
e.preventDefault();
}
}
diff --git a/web_src/js/features/common-issue-list.ts b/web_src/js/features/common-issue-list.ts
index e207364794..037529bd10 100644
--- a/web_src/js/features/common-issue-list.ts
+++ b/web_src/js/features/common-issue-list.ts
@@ -1,4 +1,4 @@
-import {isElemHidden, onInputDebounce, submitEventSubmitter, toggleElem} from '../utils/dom.ts';
+import {isElemVisible, onInputDebounce, submitEventSubmitter, toggleElem} from '../utils/dom.ts';
import {GET} from '../modules/fetch.ts';
const {appSubUrl} = window.config;
@@ -28,7 +28,7 @@ export function parseIssueListQuickGotoLink(repoLink: string, searchText: string
}
export function initCommonIssueListQuickGoto() {
- const goto = document.querySelector('#issue-list-quick-goto');
+ const goto = document.querySelector<HTMLElement>('#issue-list-quick-goto');
if (!goto) return;
const form = goto.closest('form');
@@ -37,7 +37,7 @@ export function initCommonIssueListQuickGoto() {
form.addEventListener('submit', (e) => {
// if there is no goto button, or the form is submitted by non-quick-goto elements, submit the form directly
- let doQuickGoto = !isElemHidden(goto);
+ let doQuickGoto = isElemVisible(goto);
const submitter = submitEventSubmitter(e);
if (submitter !== form && submitter !== input && submitter !== goto) doQuickGoto = false;
if (!doQuickGoto) return;
diff --git a/web_src/js/features/common-organization.ts b/web_src/js/features/common-organization.ts
index 47a61ece22..a1f19bedea 100644
--- a/web_src/js/features/common-organization.ts
+++ b/web_src/js/features/common-organization.ts
@@ -6,7 +6,7 @@ export function initCommonOrganization() {
return;
}
- document.querySelector('.organization.settings.options #org_name')?.addEventListener('input', function () {
+ document.querySelector<HTMLInputElement>('.organization.settings.options #org_name')?.addEventListener('input', function () {
const nameChanged = this.value.toLowerCase() !== this.getAttribute('data-org-name').toLowerCase();
toggleElem('#org-name-change-prompt', nameChanged);
});
diff --git a/web_src/js/features/common-page.ts b/web_src/js/features/common-page.ts
index 56c5915b6d..5a02ee7a6a 100644
--- a/web_src/js/features/common-page.ts
+++ b/web_src/js/features/common-page.ts
@@ -2,6 +2,8 @@ import {GET} from '../modules/fetch.ts';
import {showGlobalErrorMessage} from '../bootstrap.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
import {queryElems} from '../utils/dom.ts';
+import {registerGlobalInitFunc, registerGlobalSelectorFunc} from '../modules/observer.ts';
+import {initAvatarUploaderWithCropper} from './comp/Cropper.ts';
const {appUrl} = window.config;
@@ -28,51 +30,78 @@ export function initFootLanguageMenu() {
}
export function initGlobalDropdown() {
- // Semantic UI modules.
- const $uiDropdowns = fomanticQuery('.ui.dropdown');
-
// do not init "custom" dropdowns, "custom" dropdowns are managed by their own code.
- $uiDropdowns.filter(':not(.custom)').dropdown({hideDividers: 'empty'});
+ registerGlobalSelectorFunc('.ui.dropdown:not(.custom)', (el) => {
+ const $dropdown = fomanticQuery(el);
+ if ($dropdown.data('module-dropdown')) return; // do not re-init if other code has already initialized it.
- // The "jump" means this dropdown is mainly used for "menu" purpose,
- // clicking an item will jump to somewhere else or trigger an action/function.
- // When a dropdown is used for non-refresh actions with tippy,
- // it must have this "jump" class to hide the tippy when dropdown is closed.
- $uiDropdowns.filter('.jump').dropdown('setting', {
- action: 'hide',
- onShow() {
- // hide associated tooltip while dropdown is open
- this._tippy?.hide();
- this._tippy?.disable();
- },
- onHide() {
- this._tippy?.enable();
- // eslint-disable-next-line unicorn/no-this-assignment
- const elDropdown = this;
+ $dropdown.dropdown('setting', {hideDividers: 'empty'});
- // hide all tippy elements of items after a while. eg: use Enter to click "Copy Link" in the Issue Context Menu
- setTimeout(() => {
- const $dropdown = fomanticQuery(elDropdown);
- if ($dropdown.dropdown('is hidden')) {
- queryElems(elDropdown, '.menu > .item', (el) => el._tippy?.hide());
- }
- }, 2000);
- },
- });
+ if (el.classList.contains('jump')) {
+ // The "jump" means this dropdown is mainly used for "menu" purpose,
+ // clicking an item will jump to somewhere else or trigger an action/function.
+ // When a dropdown is used for non-refresh actions with tippy,
+ // it must have this "jump" class to hide the tippy when dropdown is closed.
+ $dropdown.dropdown('setting', {
+ action: 'hide',
+ onShow() {
+ // hide associated tooltip while dropdown is open
+ this._tippy?.hide();
+ this._tippy?.disable();
+ },
+ onHide() {
+ this._tippy?.enable();
+ // eslint-disable-next-line unicorn/no-this-assignment
+ const elDropdown = this;
+
+ // hide all tippy elements of items after a while. eg: use Enter to click "Copy Link" in the Issue Context Menu
+ setTimeout(() => {
+ const $dropdown = fomanticQuery(elDropdown);
+ if ($dropdown.dropdown('is hidden')) {
+ queryElems(elDropdown, '.menu > .item', (el) => el._tippy?.hide());
+ }
+ }, 2000);
+ },
+ });
+ }
- // Special popup-directions, prevent Fomantic from guessing the popup direction.
- // With default "direction: auto", if the viewport height is small, Fomantic would show the popup upward,
- // if the dropdown is at the beginning of the page, then the top part would be clipped by the window view.
- // eg: Issue List "Sort" dropdown
- // But we can not set "direction: downward" for all dropdowns, because there is a bug in dropdown menu positioning when calculating the "left" position,
- // which would make some dropdown popups slightly shift out of the right viewport edge in some cases.
- // eg: the "Create New Repo" menu on the navbar.
- $uiDropdowns.filter('.upward').dropdown('setting', 'direction', 'upward');
- $uiDropdowns.filter('.downward').dropdown('setting', 'direction', 'downward');
+ // Special popup-directions, prevent Fomantic from guessing the popup direction.
+ // With default "direction: auto", if the viewport height is small, Fomantic would show the popup upward,
+ // if the dropdown is at the beginning of the page, then the top part would be clipped by the window view.
+ // eg: Issue List "Sort" dropdown
+ // But we can not set "direction: downward" for all dropdowns, because there is a bug in dropdown menu positioning when calculating the "left" position,
+ // which would make some dropdown popups slightly shift out of the right viewport edge in some cases.
+ // eg: the "Create New Repo" menu on the navbar.
+ if (el.classList.contains('upward')) $dropdown.dropdown('setting', 'direction', 'upward');
+ if (el.classList.contains('downward')) $dropdown.dropdown('setting', 'direction', 'downward');
+ });
}
export function initGlobalTabularMenu() {
- fomanticQuery('.ui.menu.tabular:not(.custom) .item').tab({autoTabActivation: false});
+ fomanticQuery('.ui.menu.tabular:not(.custom) .item').tab();
+}
+
+export function initGlobalAvatarUploader() {
+ registerGlobalInitFunc('initAvatarUploader', initAvatarUploaderWithCropper);
+}
+
+// for performance considerations, it only uses performant syntax
+function attachInputDirAuto(el: Partial<HTMLInputElement | HTMLTextAreaElement>) {
+ if (el.type !== 'hidden' &&
+ el.type !== 'checkbox' &&
+ el.type !== 'radio' &&
+ el.type !== 'range' &&
+ el.type !== 'color') {
+ el.dir = 'auto';
+ }
+}
+
+export function initGlobalInput() {
+ registerGlobalSelectorFunc('input, textarea', attachInputDirAuto);
+ registerGlobalInitFunc('initInputAutoFocusEnd', (el: HTMLInputElement) => {
+ el.focus(); // expects only one such element on one page. If there are many, then the last one gets the focus.
+ el.setSelectionRange(el.value.length, el.value.length);
+ });
}
/**
diff --git a/web_src/js/features/comp/ComboMarkdownEditor.ts b/web_src/js/features/comp/ComboMarkdownEditor.ts
index bba50a1296..afa3957e21 100644
--- a/web_src/js/features/comp/ComboMarkdownEditor.ts
+++ b/web_src/js/features/comp/ComboMarkdownEditor.ts
@@ -1,7 +1,7 @@
import '@github/markdown-toolbar-element';
import '@github/text-expander-element';
import {attachTribute} from '../tribute.ts';
-import {hideElem, showElem, autosize, isElemVisible} from '../../utils/dom.ts';
+import {hideElem, showElem, autosize, isElemVisible, generateElemId} from '../../utils/dom.ts';
import {
EventUploadStateChanged,
initEasyMDEPaste,
@@ -25,14 +25,12 @@ import {createTippy} from '../../modules/tippy.ts';
import {fomanticQuery} from '../../modules/fomantic/base.ts';
import type EasyMDE from 'easymde';
-let elementIdCounter = 0;
-
/**
* validate if the given textarea is non-empty.
- * @param {HTMLElement} textarea - The textarea element to be validated.
+ * @param {HTMLTextAreaElement} textarea - The textarea element to be validated.
* @returns {boolean} returns true if validation succeeded.
*/
-export function validateTextareaNonEmpty(textarea) {
+export function validateTextareaNonEmpty(textarea: HTMLTextAreaElement) {
// When using EasyMDE, the original edit area HTML element is hidden, breaking HTML5 input validation.
// The workaround (https://github.com/sparksuite/simplemde-markdown-editor/issues/324) doesn't work with contenteditable, so we just show an alert.
if (!textarea.value) {
@@ -49,16 +47,25 @@ export function validateTextareaNonEmpty(textarea) {
return true;
}
+type Heights = {
+ minHeight?: string,
+ height?: string,
+ maxHeight?: string,
+};
+
type ComboMarkdownEditorOptions = {
- editorHeights?: {minHeight?: string, height?: string, maxHeight?: string},
+ editorHeights?: Heights,
easyMDEOptions?: EasyMDE.Options,
};
+type ComboMarkdownEditorTextarea = HTMLTextAreaElement & {_giteaComboMarkdownEditor: any};
+type ComboMarkdownEditorContainer = HTMLElement & {_giteaComboMarkdownEditor?: any};
+
export class ComboMarkdownEditor {
static EventEditorContentChanged = EventEditorContentChanged;
static EventUploadStateChanged = EventUploadStateChanged;
- public container : HTMLElement;
+ public container: HTMLElement;
options: ComboMarkdownEditorOptions;
@@ -70,7 +77,7 @@ export class ComboMarkdownEditor {
easyMDEToolbarActions: any;
easyMDEToolbarDefault: any;
- textarea: HTMLTextAreaElement & {_giteaComboMarkdownEditor: any};
+ textarea: ComboMarkdownEditorTextarea;
textareaMarkdownToolbar: HTMLElement;
textareaAutosize: any;
@@ -81,7 +88,7 @@ export class ComboMarkdownEditor {
previewUrl: string;
previewContext: string;
- constructor(container, options:ComboMarkdownEditorOptions = {}) {
+ constructor(container: ComboMarkdownEditorContainer, options:ComboMarkdownEditorOptions = {}) {
if (container._giteaComboMarkdownEditor) throw new Error('ComboMarkdownEditor already initialized');
container._giteaComboMarkdownEditor = this;
this.options = options;
@@ -98,7 +105,7 @@ export class ComboMarkdownEditor {
await this.switchToUserPreference();
}
- applyEditorHeights(el, heights) {
+ applyEditorHeights(el: HTMLElement, heights: Heights) {
if (!heights) return;
if (heights.minHeight) el.style.minHeight = heights.minHeight;
if (heights.height) el.style.height = heights.height;
@@ -116,7 +123,7 @@ export class ComboMarkdownEditor {
setupTextarea() {
this.textarea = this.container.querySelector('.markdown-text-editor');
this.textarea._giteaComboMarkdownEditor = this;
- this.textarea.id = `_combo_markdown_editor_${String(elementIdCounter++)}`;
+ this.textarea.id = generateElemId(`_combo_markdown_editor_`);
this.textarea.addEventListener('input', () => triggerEditorContentChanged(this.container));
this.applyEditorHeights(this.textarea, this.options.editorHeights);
@@ -204,16 +211,16 @@ export class ComboMarkdownEditor {
// Fomantic Tab requires the "data-tab" to be globally unique.
// So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic.
+ const tabIdSuffix = generateElemId();
this.tabEditor = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-writer');
this.tabPreviewer = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-previewer');
- this.tabEditor.setAttribute('data-tab', `markdown-writer-${elementIdCounter}`);
- this.tabPreviewer.setAttribute('data-tab', `markdown-previewer-${elementIdCounter}`);
+ this.tabEditor.setAttribute('data-tab', `markdown-writer-${tabIdSuffix}`);
+ this.tabPreviewer.setAttribute('data-tab', `markdown-previewer-${tabIdSuffix}`);
const panelEditor = this.container.querySelector('.ui.tab[data-tab-panel="markdown-writer"]');
const panelPreviewer = this.container.querySelector('.ui.tab[data-tab-panel="markdown-previewer"]');
- panelEditor.setAttribute('data-tab', `markdown-writer-${elementIdCounter}`);
- panelPreviewer.setAttribute('data-tab', `markdown-previewer-${elementIdCounter}`);
- elementIdCounter++;
+ panelEditor.setAttribute('data-tab', `markdown-writer-${tabIdSuffix}`);
+ panelPreviewer.setAttribute('data-tab', `markdown-previewer-${tabIdSuffix}`);
this.tabEditor.addEventListener('click', () => {
requestAnimationFrame(() => {
@@ -283,7 +290,7 @@ export class ComboMarkdownEditor {
];
}
- parseEasyMDEToolbar(easyMde: typeof EasyMDE, actions) {
+ parseEasyMDEToolbar(easyMde: typeof EasyMDE, actions: any) {
this.easyMDEToolbarActions = this.easyMDEToolbarActions || easyMDEToolbarActions(easyMde, this);
const processed = [];
for (const action of actions) {
@@ -332,21 +339,21 @@ export class ComboMarkdownEditor {
this.easyMDE = new EasyMDE(easyMDEOpt);
this.easyMDE.codemirror.on('change', () => triggerEditorContentChanged(this.container));
this.easyMDE.codemirror.setOption('extraKeys', {
- 'Cmd-Enter': (cm) => handleGlobalEnterQuickSubmit(cm.getTextArea()),
- 'Ctrl-Enter': (cm) => handleGlobalEnterQuickSubmit(cm.getTextArea()),
- Enter: (cm) => {
+ 'Cmd-Enter': (cm: any) => handleGlobalEnterQuickSubmit(cm.getTextArea()),
+ 'Ctrl-Enter': (cm: any) => handleGlobalEnterQuickSubmit(cm.getTextArea()),
+ Enter: (cm: any) => {
const tributeContainer = document.querySelector<HTMLElement>('.tribute-container');
if (!tributeContainer || tributeContainer.style.display === 'none') {
cm.execCommand('newlineAndIndent');
}
},
- Up: (cm) => {
+ Up: (cm: any) => {
const tributeContainer = document.querySelector<HTMLElement>('.tribute-container');
if (!tributeContainer || tributeContainer.style.display === 'none') {
return cm.execCommand('goLineUp');
}
},
- Down: (cm) => {
+ Down: (cm: any) => {
const tributeContainer = document.querySelector<HTMLElement>('.tribute-container');
if (!tributeContainer || tributeContainer.style.display === 'none') {
return cm.execCommand('goLineDown');
@@ -354,14 +361,14 @@ export class ComboMarkdownEditor {
},
});
this.applyEditorHeights(this.container.querySelector('.CodeMirror-scroll'), this.options.editorHeights);
- await attachTribute(this.easyMDE.codemirror.getInputField(), {mentions: true, emoji: true});
+ await attachTribute(this.easyMDE.codemirror.getInputField());
if (this.dropzone) {
initEasyMDEPaste(this.easyMDE, this.dropzone);
}
hideElem(this.textareaMarkdownToolbar);
}
- value(v = undefined) {
+ value(v: any = undefined) {
if (v === undefined) {
if (this.easyMDE) {
return this.easyMDE.value();
@@ -402,7 +409,7 @@ export class ComboMarkdownEditor {
}
}
-export function getComboMarkdownEditor(el) {
+export function getComboMarkdownEditor(el: any) {
if (!el) return null;
if (el.length) el = el[0];
return el._giteaComboMarkdownEditor;
diff --git a/web_src/js/features/comp/ConfirmModal.ts b/web_src/js/features/comp/ConfirmModal.ts
index 1ce490ec2e..97a73eace6 100644
--- a/web_src/js/features/comp/ConfirmModal.ts
+++ b/web_src/js/features/comp/ConfirmModal.ts
@@ -1,24 +1,33 @@
import {svg} from '../../svg.ts';
-import {htmlEscape} from 'escape-goat';
+import {html, htmlRaw} from '../../utils/html.ts';
import {createElementFromHTML} from '../../utils/dom.ts';
import {fomanticQuery} from '../../modules/fomantic/base.ts';
const {i18n} = window.config;
-export function confirmModal({header = '', content = '', confirmButtonColor = 'primary'} = {}): Promise<boolean> {
- return new Promise((resolve) => {
- const headerHtml = header ? `<div class="header">${htmlEscape(header)}</div>` : '';
- const modal = createElementFromHTML(`
- <div class="ui g-modal-confirm modal">
- ${headerHtml}
- <div class="content">${htmlEscape(content)}</div>
- <div class="actions">
- <button class="ui cancel button">${svg('octicon-x')} ${htmlEscape(i18n.modal_cancel)}</button>
- <button class="ui ${confirmButtonColor} ok button">${svg('octicon-check')} ${htmlEscape(i18n.modal_confirm)}</button>
- </div>
+type ConfirmModalOptions = {
+ header?: string;
+ content?: string;
+ confirmButtonColor?: 'primary' | 'red' | 'green' | 'blue';
+}
+
+export function createConfirmModal({header = '', content = '', confirmButtonColor = 'primary'}:ConfirmModalOptions = {}): HTMLElement {
+ const headerHtml = header ? html`<div class="header">${header}</div>` : '';
+ return createElementFromHTML(html`
+ <div class="ui g-modal-confirm modal">
+ ${htmlRaw(headerHtml)}
+ <div class="content">${content}</div>
+ <div class="actions">
+ <button class="ui cancel button">${htmlRaw(svg('octicon-x'))} ${i18n.modal_cancel}</button>
+ <button class="ui ${confirmButtonColor} ok button">${htmlRaw(svg('octicon-check'))} ${i18n.modal_confirm}</button>
</div>
- `);
- document.body.append(modal);
+ </div>
+ `.trim());
+}
+
+export function confirmModal(modal: HTMLElement | ConfirmModalOptions): Promise<boolean> {
+ if (!(modal instanceof HTMLElement)) modal = createConfirmModal(modal);
+ return new Promise((resolve) => {
const $modal = fomanticQuery(modal);
$modal.modal({
onApprove() {
diff --git a/web_src/js/features/comp/Cropper.ts b/web_src/js/features/comp/Cropper.ts
index e65dcfbe13..aaa1691152 100644
--- a/web_src/js/features/comp/Cropper.ts
+++ b/web_src/js/features/comp/Cropper.ts
@@ -6,7 +6,7 @@ type CropperOpts = {
fileInput: HTMLInputElement,
}
-export async function initCompCropper({container, fileInput, imageSource}: CropperOpts) {
+async function initCompCropper({container, fileInput, imageSource}: CropperOpts) {
const {default: Cropper} = await import(/* webpackChunkName: "cropperjs" */'cropperjs');
let currentFileName = '';
let currentFileLastModified = 0;
@@ -38,3 +38,10 @@ export async function initCompCropper({container, fileInput, imageSource}: Cropp
}
});
}
+
+export async function initAvatarUploaderWithCropper(fileInput: HTMLInputElement) {
+ const panel = fileInput.nextElementSibling as HTMLElement;
+ if (!panel?.matches('.cropper-panel')) throw new Error('Missing cropper panel for avatar uploader');
+ const imageSource = panel.querySelector<HTMLImageElement>('.cropper-source');
+ await initCompCropper({container: panel, fileInput, imageSource});
+}
diff --git a/web_src/js/features/comp/EditorMarkdown.test.ts b/web_src/js/features/comp/EditorMarkdown.test.ts
index 7b4b44e83c..9f34d77348 100644
--- a/web_src/js/features/comp/EditorMarkdown.test.ts
+++ b/web_src/js/features/comp/EditorMarkdown.test.ts
@@ -1,4 +1,166 @@
-import {initTextareaMarkdown} from './EditorMarkdown.ts';
+import {initTextareaMarkdown, markdownHandleIndention, textareaSplitLines} from './EditorMarkdown.ts';
+
+test('textareaSplitLines', () => {
+ let ret = textareaSplitLines('a\nbc\nd', 0);
+ expect(ret).toEqual({lines: ['a', 'bc', 'd'], lengthBeforePosLine: 0, posLineIndex: 0, inlinePos: 0});
+
+ ret = textareaSplitLines('a\nbc\nd', 1);
+ expect(ret).toEqual({lines: ['a', 'bc', 'd'], lengthBeforePosLine: 0, posLineIndex: 0, inlinePos: 1});
+
+ ret = textareaSplitLines('a\nbc\nd', 2);
+ expect(ret).toEqual({lines: ['a', 'bc', 'd'], lengthBeforePosLine: 2, posLineIndex: 1, inlinePos: 0});
+
+ ret = textareaSplitLines('a\nbc\nd', 3);
+ expect(ret).toEqual({lines: ['a', 'bc', 'd'], lengthBeforePosLine: 2, posLineIndex: 1, inlinePos: 1});
+
+ ret = textareaSplitLines('a\nbc\nd', 4);
+ expect(ret).toEqual({lines: ['a', 'bc', 'd'], lengthBeforePosLine: 2, posLineIndex: 1, inlinePos: 2});
+
+ ret = textareaSplitLines('a\nbc\nd', 5);
+ expect(ret).toEqual({lines: ['a', 'bc', 'd'], lengthBeforePosLine: 5, posLineIndex: 2, inlinePos: 0});
+
+ ret = textareaSplitLines('a\nbc\nd', 6);
+ expect(ret).toEqual({lines: ['a', 'bc', 'd'], lengthBeforePosLine: 5, posLineIndex: 2, inlinePos: 1});
+});
+
+test('markdownHandleIndention', () => {
+ const testInput = (input: string, expected?: string) => {
+ const inputPos = input.indexOf('|');
+ input = input.replace('|', '');
+ const ret = markdownHandleIndention({value: input, selStart: inputPos, selEnd: inputPos});
+ if (expected === null) {
+ expect(ret).toEqual({handled: false});
+ } else {
+ const expectedPos = expected.indexOf('|');
+ expected = expected.replace('|', '');
+ expect(ret).toEqual({
+ handled: true,
+ valueSelection: {value: expected, selStart: expectedPos, selEnd: expectedPos},
+ });
+ }
+ };
+
+ testInput(`
+ a|b
+`, `
+ a
+ |b
+`);
+
+ testInput(`
+1. a
+2. |
+`, `
+1. a
+|
+`);
+
+ testInput(`
+|1. a
+`, null); // let browser handle it
+
+ testInput(`
+1. a
+1. b|c
+`, `
+1. a
+2. b
+3. |c
+`);
+
+ testInput(`
+2. a
+2. b|
+
+1. x
+1. y
+`, `
+1. a
+2. b
+3. |
+
+1. x
+1. y
+`);
+
+ testInput(`
+2. a
+2. b
+
+1. x|
+1. y
+`, `
+2. a
+2. b
+
+1. x
+2. |
+3. y
+`);
+
+ testInput(`
+1. a
+2. b|
+3. c
+`, `
+1. a
+2. b
+3. |
+4. c
+`);
+
+ testInput(`
+1. a
+ 1. b
+ 2. b
+ 3. b
+ 4. b
+1. c|
+`, `
+1. a
+ 1. b
+ 2. b
+ 3. b
+ 4. b
+2. c
+3. |
+`);
+
+ testInput(`
+1. a
+2. a
+3. a
+4. a
+5. a
+6. a
+7. a
+8. a
+9. b|c
+`, `
+1. a
+2. a
+3. a
+4. a
+5. a
+6. a
+7. a
+8. a
+9. b
+10. |c
+`);
+
+ // this is a special case, it's difficult to re-format the parent level at the moment, so leave it to the future
+ testInput(`
+1. a
+ 2. b|
+3. c
+`, `
+1. a
+ 1. b
+ 2. |
+3. c
+`);
+});
test('EditorMarkdown', () => {
const textarea = document.createElement('textarea');
@@ -32,10 +194,10 @@ test('EditorMarkdown', () => {
testInput({value: '1. \n2. ', pos: 3}, {value: '\n2. ', pos: 0});
testInput('- x', '- x\n- ');
- testInput('1. foo', '1. foo\n1. ');
- testInput({value: '1. a\n2. b\n3. c', pos: 4}, {value: '1. a\n1. \n2. b\n3. c', pos: 8});
+ testInput('1. foo', '1. foo\n2. ');
+ testInput({value: '1. a\n2. b\n3. c', pos: 4}, {value: '1. a\n2. \n3. b\n4. c', pos: 8});
testInput('- [ ]', '- [ ]\n- ');
testInput('- [ ] foo', '- [ ] foo\n- [ ] ');
testInput('* [x] foo', '* [x] foo\n* [ ] ');
- testInput('1. [x] foo', '1. [x] foo\n1. [ ] ');
+ testInput('1. [x] foo', '1. [x] foo\n2. [ ] ');
});
diff --git a/web_src/js/features/comp/EditorMarkdown.ts b/web_src/js/features/comp/EditorMarkdown.ts
index 5e2ef121f5..6e66c15763 100644
--- a/web_src/js/features/comp/EditorMarkdown.ts
+++ b/web_src/js/features/comp/EditorMarkdown.ts
@@ -1,10 +1,10 @@
export const EventEditorContentChanged = 'ce-editor-content-changed';
-export function triggerEditorContentChanged(target) {
+export function triggerEditorContentChanged(target: HTMLElement) {
target.dispatchEvent(new CustomEvent(EventEditorContentChanged, {bubbles: true}));
}
-export function textareaInsertText(textarea, value) {
+export function textareaInsertText(textarea: HTMLTextAreaElement, value: string) {
const startPos = textarea.selectionStart;
const endPos = textarea.selectionEnd;
textarea.value = textarea.value.substring(0, startPos) + value + textarea.value.substring(endPos);
@@ -14,7 +14,13 @@ export function textareaInsertText(textarea, value) {
triggerEditorContentChanged(textarea);
}
-function handleIndentSelection(textarea, e) {
+type TextareaValueSelection = {
+ value: string;
+ selStart: number;
+ selEnd: number;
+}
+
+function handleIndentSelection(textarea: HTMLTextAreaElement, e: KeyboardEvent) {
const selStart = textarea.selectionStart;
const selEnd = textarea.selectionEnd;
if (selEnd === selStart) return; // do not process when no selection
@@ -56,58 +62,135 @@ function handleIndentSelection(textarea, e) {
triggerEditorContentChanged(textarea);
}
-function handleNewline(textarea: HTMLTextAreaElement, e: Event) {
- const selStart = textarea.selectionStart;
- const selEnd = textarea.selectionEnd;
- if (selEnd !== selStart) return; // do not process when there is a selection
+type MarkdownHandleIndentionResult = {
+ handled: boolean;
+ valueSelection?: TextareaValueSelection;
+}
+
+type TextLinesBuffer = {
+ lines: string[];
+ lengthBeforePosLine: number;
+ posLineIndex: number;
+ inlinePos: number
+}
+
+export function textareaSplitLines(value: string, pos: number): TextLinesBuffer {
+ const lines = value.split('\n');
+ let lengthBeforePosLine = 0, inlinePos = 0, posLineIndex = 0;
+ for (; posLineIndex < lines.length; posLineIndex++) {
+ const lineLength = lines[posLineIndex].length + 1;
+ if (lengthBeforePosLine + lineLength > pos) {
+ inlinePos = pos - lengthBeforePosLine;
+ break;
+ }
+ lengthBeforePosLine += lineLength;
+ }
+ return {lines, lengthBeforePosLine, posLineIndex, inlinePos};
+}
+
+function markdownReformatListNumbers(linesBuf: TextLinesBuffer, indention: string) {
+ const reDeeperIndention = new RegExp(`^${indention}\\s+`);
+ const reSameLevel = new RegExp(`^${indention}([0-9]+)\\.`);
+ let firstLineIdx: number;
+ for (firstLineIdx = linesBuf.posLineIndex - 1; firstLineIdx >= 0; firstLineIdx--) {
+ const line = linesBuf.lines[firstLineIdx];
+ if (!reDeeperIndention.test(line) && !reSameLevel.test(line)) break;
+ }
+ firstLineIdx++;
+ let num = 1;
+ for (let i = firstLineIdx; i < linesBuf.lines.length; i++) {
+ const oldLine = linesBuf.lines[i];
+ const sameLevel = reSameLevel.test(oldLine);
+ if (!sameLevel && !reDeeperIndention.test(oldLine)) break;
+ if (sameLevel) {
+ const newLine = `${indention}${num}.${oldLine.replace(reSameLevel, '')}`;
+ linesBuf.lines[i] = newLine;
+ num++;
+ if (linesBuf.posLineIndex === i) {
+ // need to correct the cursor inline position if the line length changes
+ linesBuf.inlinePos += newLine.length - oldLine.length;
+ linesBuf.inlinePos = Math.max(0, linesBuf.inlinePos);
+ linesBuf.inlinePos = Math.min(newLine.length, linesBuf.inlinePos);
+ }
+ }
+ }
+ recalculateLengthBeforeLine(linesBuf);
+}
+
+function recalculateLengthBeforeLine(linesBuf: TextLinesBuffer) {
+ linesBuf.lengthBeforePosLine = 0;
+ for (let i = 0; i < linesBuf.posLineIndex; i++) {
+ linesBuf.lengthBeforePosLine += linesBuf.lines[i].length + 1;
+ }
+}
- const value = textarea.value;
+export function markdownHandleIndention(tvs: TextareaValueSelection): MarkdownHandleIndentionResult {
+ const unhandled: MarkdownHandleIndentionResult = {handled: false};
+ if (tvs.selEnd !== tvs.selStart) return unhandled; // do not process when there is a selection
- // find the current line
- // * if selStart is 0, lastIndexOf(..., -1) is the same as lastIndexOf(..., 0)
- // * if lastIndexOf reruns -1, lineStart is 0 and it is still correct.
- const lineStart = value.lastIndexOf('\n', selStart - 1) + 1;
- let lineEnd = value.indexOf('\n', selStart);
- lineEnd = lineEnd < 0 ? value.length : lineEnd;
- let line = value.slice(lineStart, lineEnd);
- if (!line) return; // if the line is empty, do nothing, let the browser handle it
+ const linesBuf = textareaSplitLines(tvs.value, tvs.selStart);
+ const line = linesBuf.lines[linesBuf.posLineIndex] ?? '';
+ if (!line) return unhandled; // if the line is empty, do nothing, let the browser handle it
// parse the indention
- const indention = /^\s*/.exec(line)[0];
- line = line.slice(indention.length);
+ let lineContent = line;
+ const indention = /^\s*/.exec(lineContent)[0];
+ lineContent = lineContent.slice(indention.length);
+ if (linesBuf.inlinePos <= indention.length) return unhandled; // if cursor is at the indention, do nothing, let the browser handle it
// parse the prefixes: "1. ", "- ", "* ", there could also be " [ ] " or " [x] " for task lists
// there must be a space after the prefix because none of "1.foo" / "-foo" is a list item
- const prefixMatch = /^([0-9]+\.|[-*])(\s\[([ x])\])?\s/.exec(line);
+ const prefixMatch = /^([0-9]+\.|[-*])(\s\[([ x])\])?\s/.exec(lineContent);
let prefix = '';
if (prefixMatch) {
prefix = prefixMatch[0];
- if (lineStart + prefix.length > selStart) prefix = ''; // do not add new line if cursor is at prefix
+ if (prefix.length > linesBuf.inlinePos) prefix = ''; // do not add new line if cursor is at prefix
}
- line = line.slice(prefix.length);
- if (!indention && !prefix) return; // if no indention and no prefix, do nothing, let the browser handle it
+ lineContent = lineContent.slice(prefix.length);
+ if (!indention && !prefix) return unhandled; // if no indention and no prefix, do nothing, let the browser handle it
- e.preventDefault();
- if (!line) {
+ if (!lineContent) {
// clear current line if we only have i.e. '1. ' and the user presses enter again to finish creating a list
- textarea.value = value.slice(0, lineStart) + value.slice(lineEnd);
- textarea.setSelectionRange(selStart - prefix.length, selStart - prefix.length);
+ linesBuf.lines[linesBuf.posLineIndex] = '';
+ linesBuf.inlinePos = 0;
} else {
- // start a new line with the same indention and prefix
+ // start a new line with the same indention
let newPrefix = prefix;
- // a simple approach, otherwise it needs to parse the lines after the current line
if (/^\d+\./.test(prefix)) newPrefix = `1. ${newPrefix.slice(newPrefix.indexOf('.') + 2)}`;
newPrefix = newPrefix.replace('[x]', '[ ]');
- const newLine = `\n${indention}${newPrefix}`;
- textarea.value = value.slice(0, selStart) + newLine + value.slice(selEnd);
- textarea.setSelectionRange(selStart + newLine.length, selStart + newLine.length);
+
+ const inlinePos = linesBuf.inlinePos;
+ linesBuf.lines[linesBuf.posLineIndex] = line.substring(0, inlinePos);
+ const newLineLeft = `${indention}${newPrefix}`;
+ const newLine = `${newLineLeft}${line.substring(inlinePos)}`;
+ linesBuf.lines.splice(linesBuf.posLineIndex + 1, 0, newLine);
+ linesBuf.posLineIndex++;
+ linesBuf.inlinePos = newLineLeft.length;
+ recalculateLengthBeforeLine(linesBuf);
}
+
+ markdownReformatListNumbers(linesBuf, indention);
+ const newPos = linesBuf.lengthBeforePosLine + linesBuf.inlinePos;
+ return {handled: true, valueSelection: {value: linesBuf.lines.join('\n'), selStart: newPos, selEnd: newPos}};
+}
+
+function handleNewline(textarea: HTMLTextAreaElement, e: Event) {
+ const ret = markdownHandleIndention({value: textarea.value, selStart: textarea.selectionStart, selEnd: textarea.selectionEnd});
+ if (!ret.handled) return;
+ e.preventDefault();
+ textarea.value = ret.valueSelection.value;
+ textarea.setSelectionRange(ret.valueSelection.selStart, ret.valueSelection.selEnd);
triggerEditorContentChanged(textarea);
}
-export function initTextareaMarkdown(textarea) {
+function isTextExpanderShown(textarea: HTMLElement): boolean {
+ return Boolean(textarea.closest('text-expander')?.querySelector('.suggestions'));
+}
+
+export function initTextareaMarkdown(textarea: HTMLTextAreaElement) {
textarea.addEventListener('keydown', (e) => {
+ if (isTextExpanderShown(textarea)) return;
if (e.key === 'Tab' && !e.ctrlKey && !e.metaKey && !e.altKey) {
// use Tab/Shift-Tab to indent/unindent the selected lines
handleIndentSelection(textarea, e);
diff --git a/web_src/js/features/comp/EditorUpload.test.ts b/web_src/js/features/comp/EditorUpload.test.ts
index 55f3f74389..e6e5f4de13 100644
--- a/web_src/js/features/comp/EditorUpload.test.ts
+++ b/web_src/js/features/comp/EditorUpload.test.ts
@@ -1,4 +1,4 @@
-import {removeAttachmentLinksFromMarkdown} from './EditorUpload.ts';
+import {pasteAsMarkdownLink, removeAttachmentLinksFromMarkdown} from './EditorUpload.ts';
test('removeAttachmentLinksFromMarkdown', () => {
expect(removeAttachmentLinksFromMarkdown('a foo b', 'foo')).toBe('a foo b');
@@ -12,3 +12,13 @@ test('removeAttachmentLinksFromMarkdown', () => {
expect(removeAttachmentLinksFromMarkdown('a <img src="/attachments/foo"> b', 'foo')).toBe('a b');
expect(removeAttachmentLinksFromMarkdown('a <img src="/attachments/foo" width="100"/> b', 'foo')).toBe('a b');
});
+
+test('preparePasteAsMarkdownLink', () => {
+ expect(pasteAsMarkdownLink({value: 'foo', selectionStart: 0, selectionEnd: 0}, 'bar')).toBeNull();
+ expect(pasteAsMarkdownLink({value: 'foo', selectionStart: 0, selectionEnd: 0}, 'https://gitea.com')).toBeNull();
+ expect(pasteAsMarkdownLink({value: 'foo', selectionStart: 0, selectionEnd: 3}, 'bar')).toBeNull();
+ expect(pasteAsMarkdownLink({value: 'foo', selectionStart: 0, selectionEnd: 3}, 'https://gitea.com')).toBe('[foo](https://gitea.com)');
+ expect(pasteAsMarkdownLink({value: '..(url)', selectionStart: 3, selectionEnd: 6}, 'https://gitea.com')).toBe('[url](https://gitea.com)');
+ expect(pasteAsMarkdownLink({value: '[](url)', selectionStart: 3, selectionEnd: 6}, 'https://gitea.com')).toBeNull();
+ expect(pasteAsMarkdownLink({value: 'https://example.com', selectionStart: 0, selectionEnd: 19}, 'https://gitea.com')).toBeNull();
+});
diff --git a/web_src/js/features/comp/EditorUpload.ts b/web_src/js/features/comp/EditorUpload.ts
index 89982747ea..bf78f58daf 100644
--- a/web_src/js/features/comp/EditorUpload.ts
+++ b/web_src/js/features/comp/EditorUpload.ts
@@ -8,43 +8,46 @@ import {
generateMarkdownLinkForAttachment,
} from '../dropzone.ts';
import type CodeMirror from 'codemirror';
+import type EasyMDE from 'easymde';
+import type {DropzoneFile} from 'dropzone';
let uploadIdCounter = 0;
export const EventUploadStateChanged = 'ce-upload-state-changed';
-export function triggerUploadStateChanged(target) {
+export function triggerUploadStateChanged(target: HTMLElement) {
target.dispatchEvent(new CustomEvent(EventUploadStateChanged, {bubbles: true}));
}
-function uploadFile(dropzoneEl, file) {
+function uploadFile(dropzoneEl: HTMLElement, file: File) {
return new Promise((resolve) => {
const curUploadId = uploadIdCounter++;
- file._giteaUploadId = curUploadId;
+ (file as any)._giteaUploadId = curUploadId;
const dropzoneInst = dropzoneEl.dropzone;
- const onUploadDone = ({file}) => {
+ const onUploadDone = ({file}: {file: any}) => {
if (file._giteaUploadId === curUploadId) {
dropzoneInst.off(DropzoneCustomEventUploadDone, onUploadDone);
resolve(file);
}
};
dropzoneInst.on(DropzoneCustomEventUploadDone, onUploadDone);
- dropzoneInst.handleFiles([file]);
+ // FIXME: this is not entirely correct because `file` does not satisfy DropzoneFile (we have abused the Dropzone for long time)
+ dropzoneInst.addFile(file as DropzoneFile);
});
}
class TextareaEditor {
- editor : HTMLTextAreaElement;
+ editor: HTMLTextAreaElement;
- constructor(editor) {
+ constructor(editor: HTMLTextAreaElement) {
this.editor = editor;
}
- insertPlaceholder(value) {
+ insertPlaceholder(value: string) {
textareaInsertText(this.editor, value);
}
- replacePlaceholder(oldVal, newVal) {
+ replacePlaceholder(oldVal: string, newVal: string) {
const editor = this.editor;
const startPos = editor.selectionStart;
const endPos = editor.selectionEnd;
@@ -65,11 +68,11 @@ class TextareaEditor {
class CodeMirrorEditor {
editor: CodeMirror.EditorFromTextArea;
- constructor(editor) {
+ constructor(editor: CodeMirror.EditorFromTextArea) {
this.editor = editor;
}
- insertPlaceholder(value) {
+ insertPlaceholder(value: string) {
const editor = this.editor;
const startPoint = editor.getCursor('start');
const endPoint = editor.getCursor('end');
@@ -80,7 +83,7 @@ class CodeMirrorEditor {
triggerEditorContentChanged(editor.getTextArea());
}
- replacePlaceholder(oldVal, newVal) {
+ replacePlaceholder(oldVal: string, newVal: string) {
const editor = this.editor;
const endPoint = editor.getCursor('end');
if (editor.getSelection() === oldVal) {
@@ -96,7 +99,7 @@ class CodeMirrorEditor {
}
}
-async function handleUploadFiles(editor, dropzoneEl, files, e) {
+async function handleUploadFiles(editor: CodeMirrorEditor | TextareaEditor, dropzoneEl: HTMLElement, files: Array<File> | FileList, e: Event) {
e.preventDefault();
for (const file of files) {
const name = file.name.slice(0, file.name.lastIndexOf('.'));
@@ -109,29 +112,38 @@ async function handleUploadFiles(editor, dropzoneEl, files, e) {
}
}
-export function removeAttachmentLinksFromMarkdown(text, fileUuid) {
+export function removeAttachmentLinksFromMarkdown(text: string, fileUuid: string) {
text = text.replace(new RegExp(`!?\\[([^\\]]+)\\]\\(/?attachments/${fileUuid}\\)`, 'g'), '');
- text = text.replace(new RegExp(`<img[^>]+src="/?attachments/${fileUuid}"[^>]*>`, 'g'), '');
+ text = text.replace(new RegExp(`[<]img[^>]+src="/?attachments/${fileUuid}"[^>]*>`, 'g'), '');
return text;
}
-function handleClipboardText(textarea, e, {text, isShiftDown}) {
+export function pasteAsMarkdownLink(textarea: {value: string, selectionStart: number, selectionEnd: number}, pastedText: string): string | null {
+ const {value, selectionStart, selectionEnd} = textarea;
+ const selectedText = value.substring(selectionStart, selectionEnd);
+ const trimmedText = pastedText.trim();
+ const beforeSelection = value.substring(0, selectionStart);
+ const afterSelection = value.substring(selectionEnd);
+ const isInMarkdownLink = beforeSelection.endsWith('](') && afterSelection.startsWith(')');
+ const asMarkdownLink = selectedText && isUrl(trimmedText) && !isUrl(selectedText) && !isInMarkdownLink;
+ return asMarkdownLink ? `[${selectedText}](${trimmedText})` : null;
+}
+
+function handleClipboardText(textarea: HTMLTextAreaElement, e: ClipboardEvent, pastedText: string, isShiftDown: boolean) {
// pasting with "shift" means "paste as original content" in most applications
if (isShiftDown) return; // let the browser handle it
// when pasting links over selected text, turn it into [text](link)
- const {value, selectionStart, selectionEnd} = textarea;
- const selectedText = value.substring(selectionStart, selectionEnd);
- const trimmedText = text.trim();
- if (selectedText && isUrl(trimmedText) && !isUrl(selectedText)) {
+ const pastedAsMarkdown = pasteAsMarkdownLink(textarea, pastedText);
+ if (pastedAsMarkdown) {
e.preventDefault();
- replaceTextareaSelection(textarea, `[${selectedText}](${trimmedText})`);
+ replaceTextareaSelection(textarea, pastedAsMarkdown);
}
// else, let the browser handle it
}
// extract text and images from "paste" event
-function getPastedContent(e) {
+function getPastedContent(e: ClipboardEvent) {
const images = [];
for (const item of e.clipboardData?.items ?? []) {
if (item.type?.startsWith('image/')) {
@@ -142,8 +154,8 @@ function getPastedContent(e) {
return {text, images};
}
-export function initEasyMDEPaste(easyMDE, dropzoneEl) {
- const editor = new CodeMirrorEditor(easyMDE.codemirror);
+export function initEasyMDEPaste(easyMDE: EasyMDE, dropzoneEl: HTMLElement) {
+ const editor = new CodeMirrorEditor(easyMDE.codemirror as any);
easyMDE.codemirror.on('paste', (_, e) => {
const {images} = getPastedContent(e);
if (!images.length) return;
@@ -160,28 +172,28 @@ export function initEasyMDEPaste(easyMDE, dropzoneEl) {
});
}
-export function initTextareaEvents(textarea, dropzoneEl) {
+export function initTextareaEvents(textarea: HTMLTextAreaElement, dropzoneEl: HTMLElement) {
let isShiftDown = false;
- textarea.addEventListener('keydown', (e) => {
+ textarea.addEventListener('keydown', (e: KeyboardEvent) => {
if (e.shiftKey) isShiftDown = true;
});
- textarea.addEventListener('keyup', (e) => {
+ textarea.addEventListener('keyup', (e: KeyboardEvent) => {
if (!e.shiftKey) isShiftDown = false;
});
- textarea.addEventListener('paste', (e) => {
+ textarea.addEventListener('paste', (e: ClipboardEvent) => {
const {images, text} = getPastedContent(e);
if (images.length && dropzoneEl) {
handleUploadFiles(new TextareaEditor(textarea), dropzoneEl, images, e);
} else if (text) {
- handleClipboardText(textarea, e, {text, isShiftDown});
+ handleClipboardText(textarea, e, text, isShiftDown);
}
});
- textarea.addEventListener('drop', (e) => {
+ textarea.addEventListener('drop', (e: DragEvent) => {
if (!e.dataTransfer.files.length) return;
if (!dropzoneEl) return;
handleUploadFiles(new TextareaEditor(textarea), dropzoneEl, e.dataTransfer.files, e);
});
- dropzoneEl?.dropzone.on(DropzoneCustomEventRemovedFile, ({fileUuid}) => {
+ dropzoneEl?.dropzone.on(DropzoneCustomEventRemovedFile, ({fileUuid}: {fileUuid: string}) => {
const newText = removeAttachmentLinksFromMarkdown(textarea.value, fileUuid);
if (textarea.value !== newText) textarea.value = newText;
});
diff --git a/web_src/js/features/comp/LabelEdit.ts b/web_src/js/features/comp/LabelEdit.ts
index 7bceb636bb..423440129c 100644
--- a/web_src/js/features/comp/LabelEdit.ts
+++ b/web_src/js/features/comp/LabelEdit.ts
@@ -1,5 +1,6 @@
import {toggleElem} from '../../utils/dom.ts';
import {fomanticQuery} from '../../modules/fomantic/base.ts';
+import {submitFormFetchAction} from '../common-fetch-action.ts';
function nameHasScope(name: string): boolean {
return /.*[^/]\/[^/].*/.test(name);
@@ -18,6 +19,8 @@ export function initCompLabelEdit(pageSelector: string) {
const elExclusiveField = elModal.querySelector('.label-exclusive-input-field');
const elExclusiveInput = elModal.querySelector<HTMLInputElement>('.label-exclusive-input');
const elExclusiveWarning = elModal.querySelector('.label-exclusive-warning');
+ const elExclusiveOrderField = elModal.querySelector<HTMLInputElement>('.label-exclusive-order-input-field');
+ const elExclusiveOrderInput = elModal.querySelector<HTMLInputElement>('.label-exclusive-order-input');
const elIsArchivedField = elModal.querySelector('.label-is-archived-input-field');
const elIsArchivedInput = elModal.querySelector<HTMLInputElement>('.label-is-archived-input');
const elDescInput = elModal.querySelector<HTMLInputElement>('.label-desc-input');
@@ -29,6 +32,13 @@ export function initCompLabelEdit(pageSelector: string) {
const showExclusiveWarning = hasScope && elExclusiveInput.checked && elModal.hasAttribute('data-need-warn-exclusive');
toggleElem(elExclusiveWarning, showExclusiveWarning);
if (!hasScope) elExclusiveInput.checked = false;
+ toggleElem(elExclusiveOrderField, elExclusiveInput.checked);
+
+ if (parseInt(elExclusiveOrderInput.value) <= 0) {
+ elExclusiveOrderInput.style.color = 'var(--color-placeholder-text) !important';
+ } else {
+ elExclusiveOrderInput.style.color = null;
+ }
};
const showLabelEditModal = (btn:HTMLElement) => {
@@ -36,6 +46,7 @@ export function initCompLabelEdit(pageSelector: string) {
const form = elModal.querySelector<HTMLFormElement>('form');
elLabelId.value = btn.getAttribute('data-label-id') || '';
elNameInput.value = btn.getAttribute('data-label-name') || '';
+ elExclusiveOrderInput.value = btn.getAttribute('data-label-exclusive-order') || '0';
elIsArchivedInput.checked = btn.getAttribute('data-label-is-archived') === 'true';
elExclusiveInput.checked = btn.getAttribute('data-label-exclusive') === 'true';
elDescInput.value = btn.getAttribute('data-label-description') || '';
@@ -60,7 +71,8 @@ export function initCompLabelEdit(pageSelector: string) {
form.reportValidity();
return false;
}
- form.submit();
+ submitFormFetchAction(form);
+ return false;
},
}).modal('show');
};
diff --git a/web_src/js/features/comp/QuickSubmit.ts b/web_src/js/features/comp/QuickSubmit.ts
index 385acb319f..0a41f69132 100644
--- a/web_src/js/features/comp/QuickSubmit.ts
+++ b/web_src/js/features/comp/QuickSubmit.ts
@@ -1,6 +1,6 @@
import {querySingleVisibleElem} from '../../utils/dom.ts';
-export function handleGlobalEnterQuickSubmit(target) {
+export function handleGlobalEnterQuickSubmit(target: HTMLElement) {
let form = target.closest('form');
if (form) {
if (!form.checkValidity()) {
diff --git a/web_src/js/features/comp/ReactionSelector.ts b/web_src/js/features/comp/ReactionSelector.ts
index e93e3b8377..bb54593f11 100644
--- a/web_src/js/features/comp/ReactionSelector.ts
+++ b/web_src/js/features/comp/ReactionSelector.ts
@@ -1,37 +1,31 @@
import {POST} from '../../modules/fetch.ts';
-import {fomanticQuery} from '../../modules/fomantic/base.ts';
import type {DOMEvent} from '../../utils/dom.ts';
+import {registerGlobalEventFunc} from '../../modules/observer.ts';
-export function initCompReactionSelector(parent: ParentNode = document) {
- for (const container of parent.querySelectorAll<HTMLElement>('.issue-content, .diff-file-body')) {
- container.addEventListener('click', async (e: DOMEvent<MouseEvent>) => {
- // there are 2 places for the "reaction" buttons, one is the top-right reaction menu, one is the bottom of the comment
- const target = e.target.closest('.comment-reaction-button');
- if (!target) return;
- e.preventDefault();
+export function initCompReactionSelector() {
+ registerGlobalEventFunc('click', 'onCommentReactionButtonClick', async (target: HTMLElement, e: DOMEvent<MouseEvent>) => {
+ // there are 2 places for the "reaction" buttons, one is the top-right reaction menu, one is the bottom of the comment
+ e.preventDefault();
- if (target.classList.contains('disabled')) return;
+ if (target.classList.contains('disabled')) return;
- const actionUrl = target.closest('[data-action-url]').getAttribute('data-action-url');
- const reactionContent = target.getAttribute('data-reaction-content');
+ const actionUrl = target.closest('[data-action-url]').getAttribute('data-action-url');
+ const reactionContent = target.getAttribute('data-reaction-content');
- const commentContainer = target.closest('.comment-container');
+ const commentContainer = target.closest('.comment-container');
- const bottomReactions = commentContainer.querySelector('.bottom-reactions'); // may not exist if there is no reaction
- const bottomReactionBtn = bottomReactions?.querySelector(`a[data-reaction-content="${CSS.escape(reactionContent)}"]`);
- const hasReacted = bottomReactionBtn?.getAttribute('data-has-reacted') === 'true';
+ const bottomReactions = commentContainer.querySelector('.bottom-reactions'); // may not exist if there is no reaction
+ const bottomReactionBtn = bottomReactions?.querySelector(`a[data-reaction-content="${CSS.escape(reactionContent)}"]`);
+ const hasReacted = bottomReactionBtn?.getAttribute('data-has-reacted') === 'true';
- const res = await POST(`${actionUrl}/${hasReacted ? 'unreact' : 'react'}`, {
- data: new URLSearchParams({content: reactionContent}),
- });
-
- const data = await res.json();
- bottomReactions?.remove();
- if (data.html) {
- commentContainer.insertAdjacentHTML('beforeend', data.html);
- const bottomReactionsDropdowns = commentContainer.querySelectorAll('.bottom-reactions .dropdown.select-reaction');
- fomanticQuery(bottomReactionsDropdowns).dropdown(); // re-init the dropdown
- }
+ const res = await POST(`${actionUrl}/${hasReacted ? 'unreact' : 'react'}`, {
+ data: new URLSearchParams({content: reactionContent}),
});
- }
+
+ const data = await res.json();
+ bottomReactions?.remove();
+ if (data.html) {
+ commentContainer.insertAdjacentHTML('beforeend', data.html);
+ }
+ });
}
diff --git a/web_src/js/features/comp/SearchUserBox.ts b/web_src/js/features/comp/SearchUserBox.ts
index 2e3b3f83be..4b13a2141f 100644
--- a/web_src/js/features/comp/SearchUserBox.ts
+++ b/web_src/js/features/comp/SearchUserBox.ts
@@ -1,4 +1,4 @@
-import {htmlEscape} from 'escape-goat';
+import {htmlEscape} from '../../utils/html.ts';
import {fomanticQuery} from '../../modules/fomantic/base.ts';
const {appSubUrl} = window.config;
@@ -14,7 +14,7 @@ export function initCompSearchUserBox() {
minCharacters: 2,
apiSettings: {
url: `${appSubUrl}/user/search_candidates?q={query}`,
- onResponse(response) {
+ onResponse(response: any) {
const resultItems = [];
const searchQuery = searchUserBox.querySelector('input').value;
const searchQueryUppercase = searchQuery.toUpperCase();
diff --git a/web_src/js/features/comp/TextExpander.ts b/web_src/js/features/comp/TextExpander.ts
index e0c4abed75..2d79fe5029 100644
--- a/web_src/js/features/comp/TextExpander.ts
+++ b/web_src/js/features/comp/TextExpander.ts
@@ -1,18 +1,25 @@
import {matchEmoji, matchMention, matchIssue} from '../../utils/match.ts';
import {emojiString} from '../emoji.ts';
import {svg} from '../../svg.ts';
-import {parseIssueHref, parseIssueNewHref} from '../../utils.ts';
+import {parseIssueHref, parseRepoOwnerPathInfo} from '../../utils.ts';
import {createElementFromAttrs, createElementFromHTML} from '../../utils/dom.ts';
import {getIssueColor, getIssueIcon} from '../issue.ts';
import {debounce} from 'perfect-debounce';
+import type TextExpanderElement from '@github/text-expander-element';
+import type {TextExpanderChangeEvent, TextExpanderResult} from '@github/text-expander-element';
-const debouncedSuggestIssues = debounce((key: string, text: string) => new Promise<{matched:boolean; fragment?: HTMLElement}>(async (resolve) => {
- let issuePathInfo = parseIssueHref(window.location.href);
- if (!issuePathInfo.ownerName) issuePathInfo = parseIssueNewHref(window.location.href);
- if (!issuePathInfo.ownerName) return resolve({matched: false});
+async function fetchIssueSuggestions(key: string, text: string): Promise<TextExpanderResult> {
+ const issuePathInfo = parseIssueHref(window.location.href);
+ if (!issuePathInfo.ownerName) {
+ const repoOwnerPathInfo = parseRepoOwnerPathInfo(window.location.pathname);
+ issuePathInfo.ownerName = repoOwnerPathInfo.ownerName;
+ issuePathInfo.repoName = repoOwnerPathInfo.repoName;
+ // then no issuePathInfo.indexString here, it is only used to exclude the current issue when "matchIssue"
+ }
+ if (!issuePathInfo.ownerName) return {matched: false};
const matches = await matchIssue(issuePathInfo.ownerName, issuePathInfo.repoName, issuePathInfo.indexString, text);
- if (!matches.length) return resolve({matched: false});
+ if (!matches.length) return {matched: false};
const ul = createElementFromAttrs('ul', {class: 'suggestions'});
for (const issue of matches) {
@@ -24,11 +31,40 @@ const debouncedSuggestIssues = debounce((key: string, text: string) => new Promi
);
ul.append(li);
}
- resolve({matched: true, fragment: ul});
-}), 100);
+ return {matched: true, fragment: ul};
+}
+
+export function initTextExpander(expander: TextExpanderElement) {
+ if (!expander) return;
+
+ const textarea = expander.querySelector<HTMLTextAreaElement>('textarea');
-export function initTextExpander(expander) {
- expander?.addEventListener('text-expander-change', ({detail: {key, provide, text}}) => {
+ // help to fix the text-expander "multiword+promise" bug: do not show the popup when there is no "#" before current line
+ const shouldShowIssueSuggestions = () => {
+ const posVal = textarea.value.substring(0, textarea.selectionStart);
+ const lineStart = posVal.lastIndexOf('\n');
+ const keyStart = posVal.lastIndexOf('#');
+ return keyStart > lineStart;
+ };
+
+ const debouncedIssueSuggestions = debounce(async (key: string, text: string): Promise<TextExpanderResult> => {
+ // https://github.com/github/text-expander-element/issues/71
+ // Upstream bug: when using "multiword+promise", TextExpander will get wrong "key" position.
+ // To reproduce, comment out the "shouldShowIssueSuggestions" check, use the "await sleep" below,
+ // then use content "close #20\nclose #20\nclose #20" (3 lines), keep changing the last line `#20` part from the end (including removing the `#`)
+ // There will be a JS error: Uncaught (in promise) IndexSizeError: Failed to execute 'setStart' on 'Range': The offset 28 is larger than the node's length (27).
+
+ // check the input before the request, to avoid emitting empty query to backend (still related to the upstream bug)
+ if (!shouldShowIssueSuggestions()) return {matched: false};
+ // await sleep(Math.random() * 1000); // help to reproduce the text-expander bug
+ const ret = await fetchIssueSuggestions(key, text);
+ // check the input again to avoid text-expander using incorrect position (upstream bug)
+ if (!shouldShowIssueSuggestions()) return {matched: false};
+ return ret;
+ }, 300); // to match onInputDebounce delay
+
+ expander.addEventListener('text-expander-change', (e: TextExpanderChangeEvent) => {
+ const {key, text, provide} = e.detail;
if (key === ':') {
const matches = matchEmoji(text);
if (!matches.length) return provide({matched: false});
@@ -61,6 +97,7 @@ export function initTextExpander(expander) {
li.append(img);
const nameSpan = document.createElement('span');
+ nameSpan.classList.add('name');
nameSpan.textContent = name;
li.append(nameSpan);
@@ -76,10 +113,11 @@ export function initTextExpander(expander) {
provide({matched: true, fragment: ul});
} else if (key === '#') {
- provide(debouncedSuggestIssues(key, text));
+ provide(debouncedIssueSuggestions(key, text));
}
});
- expander?.addEventListener('text-expander-value', ({detail}) => {
+
+ expander.addEventListener('text-expander-value', ({detail}: Record<string, any>) => {
if (detail?.item) {
// add a space after @mentions and #issue as it's likely the user wants one
const suffix = ['@', '#'].includes(detail.key) ? ' ' : '';
diff --git a/web_src/js/features/comp/WebHookEditor.ts b/web_src/js/features/comp/WebHookEditor.ts
index 203396af80..794b3c99ca 100644
--- a/web_src/js/features/comp/WebHookEditor.ts
+++ b/web_src/js/features/comp/WebHookEditor.ts
@@ -6,7 +6,7 @@ export function initCompWebHookEditor() {
return;
}
- for (const input of document.querySelectorAll('.events.checkbox input')) {
+ for (const input of document.querySelectorAll<HTMLInputElement>('.events.checkbox input')) {
input.addEventListener('change', function () {
if (this.checked) {
showElem('.events.fields');
@@ -14,7 +14,7 @@ export function initCompWebHookEditor() {
});
}
- for (const input of document.querySelectorAll('.non-events.checkbox input')) {
+ for (const input of document.querySelectorAll<HTMLInputElement>('.non-events.checkbox input')) {
input.addEventListener('change', function () {
if (this.checked) {
hideElem('.events.fields');
@@ -34,7 +34,7 @@ export function initCompWebHookEditor() {
}
// Test delivery
- document.querySelector('#test-delivery')?.addEventListener('click', async function () {
+ document.querySelector<HTMLButtonElement>('#test-delivery')?.addEventListener('click', async function () {
this.classList.add('is-loading', 'disabled');
await POST(this.getAttribute('data-link'));
setTimeout(() => {
diff --git a/web_src/js/features/contextpopup.ts b/web_src/js/features/contextpopup.ts
index 33eead8431..7477331dbe 100644
--- a/web_src/js/features/contextpopup.ts
+++ b/web_src/js/features/contextpopup.ts
@@ -4,11 +4,11 @@ import {parseIssueHref} from '../utils.ts';
import {createTippy} from '../modules/tippy.ts';
export function initContextPopups() {
- const refIssues = document.querySelectorAll('.ref-issue');
+ const refIssues = document.querySelectorAll<HTMLElement>('.ref-issue');
attachRefIssueContextPopup(refIssues);
}
-export function attachRefIssueContextPopup(refIssues) {
+export function attachRefIssueContextPopup(refIssues: NodeListOf<HTMLElement>) {
for (const refIssue of refIssues) {
if (refIssue.classList.contains('ref-external-issue')) continue;
diff --git a/web_src/js/features/copycontent.ts b/web_src/js/features/copycontent.ts
index af867463b2..0fec2a6235 100644
--- a/web_src/js/features/copycontent.ts
+++ b/web_src/js/features/copycontent.ts
@@ -2,26 +2,24 @@ import {clippie} from 'clippie';
import {showTemporaryTooltip} from '../modules/tippy.ts';
import {convertImage} from '../utils.ts';
import {GET} from '../modules/fetch.ts';
+import {registerGlobalEventFunc} from '../modules/observer.ts';
const {i18n} = window.config;
export function initCopyContent() {
- const btn = document.querySelector('#copy-content');
- if (!btn || btn.classList.contains('disabled')) return;
+ registerGlobalEventFunc('click', 'onCopyContentButtonClick', async (btn: HTMLElement) => {
+ if (btn.classList.contains('disabled') || btn.classList.contains('is-loading')) return;
+ const rawFileLink = btn.getAttribute('data-raw-file-link');
- btn.addEventListener('click', async () => {
- if (btn.classList.contains('is-loading')) return;
- let content;
- let isRasterImage = false;
- const link = btn.getAttribute('data-link');
+ let content, isRasterImage = false;
- // when data-link is present, we perform a fetch. this is either because
- // the text to copy is not in the DOM or it is an image which should be
+ // when "data-raw-link" is present, we perform a fetch. this is either because
+ // the text to copy is not in the DOM, or it is an image that should be
// fetched to copy in full resolution
- if (link) {
+ if (rawFileLink) {
btn.classList.add('is-loading', 'loading-icon-2px');
try {
- const res = await GET(link, {credentials: 'include', redirect: 'follow'});
+ const res = await GET(rawFileLink, {credentials: 'include', redirect: 'follow'});
const contentType = res.headers.get('content-type');
if (contentType.startsWith('image/') && !contentType.startsWith('image/svg')) {
@@ -40,13 +38,13 @@ export function initCopyContent() {
content = Array.from(lineEls, (el) => el.textContent).join('');
}
- // try copy original first, if that fails and it's an image, convert it to png
+ // try copy original first, if that fails, and it's an image, convert it to png
const success = await clippie(content);
if (success) {
showTemporaryTooltip(btn, i18n.copy_success);
} else {
if (isRasterImage) {
- const success = await clippie(await convertImage(content, 'image/png'));
+ const success = await clippie(await convertImage(content as Blob, 'image/png'));
showTemporaryTooltip(btn, success ? i18n.copy_success : i18n.copy_error);
} else {
showTemporaryTooltip(btn, i18n.copy_error);
diff --git a/web_src/js/features/dashboard.ts b/web_src/js/features/dashboard.ts
new file mode 100644
index 0000000000..71a0df3a64
--- /dev/null
+++ b/web_src/js/features/dashboard.ts
@@ -0,0 +1,9 @@
+import {createApp} from 'vue';
+import DashboardRepoList from '../components/DashboardRepoList.vue';
+
+export function initDashboardRepoList() {
+ const el = document.querySelector('#dashboard-repo-list');
+ if (el) {
+ createApp(DashboardRepoList).mount(el);
+ }
+}
diff --git a/web_src/js/features/dropzone.ts b/web_src/js/features/dropzone.ts
index 666c645230..20f7ceb6c3 100644
--- a/web_src/js/features/dropzone.ts
+++ b/web_src/js/features/dropzone.ts
@@ -1,21 +1,23 @@
import {svg} from '../svg.ts';
-import {htmlEscape} from 'escape-goat';
+import {html} from '../utils/html.ts';
import {clippie} from 'clippie';
import {showTemporaryTooltip} from '../modules/tippy.ts';
import {GET, POST} from '../modules/fetch.ts';
import {showErrorToast} from '../modules/toast.ts';
import {createElementFromHTML, createElementFromAttrs} from '../utils/dom.ts';
import {isImageFile, isVideoFile} from '../utils.ts';
-import type {DropzoneFile} from 'dropzone/index.js';
+import type {DropzoneFile, DropzoneOptions} from 'dropzone/index.js';
const {csrfToken, i18n} = window.config;
+type CustomDropzoneFile = DropzoneFile & {uuid: string};
+
// dropzone has its owner event dispatcher (emitter)
export const DropzoneCustomEventReloadFiles = 'dropzone-custom-reload-files';
export const DropzoneCustomEventRemovedFile = 'dropzone-custom-removed-file';
export const DropzoneCustomEventUploadDone = 'dropzone-custom-upload-done';
-async function createDropzone(el, opts) {
+async function createDropzone(el: HTMLElement, opts: DropzoneOptions) {
const [{default: Dropzone}] = await Promise.all([
import(/* webpackChunkName: "dropzone" */'dropzone'),
import(/* webpackChunkName: "dropzone" */'dropzone/dist/dropzone.css'),
@@ -23,7 +25,7 @@ async function createDropzone(el, opts) {
return new Dropzone(el, opts);
}
-export function generateMarkdownLinkForAttachment(file, {width, dppx}: {width?: number, dppx?: number} = {}) {
+export function generateMarkdownLinkForAttachment(file: Partial<CustomDropzoneFile>, {width, dppx}: {width?: number, dppx?: number} = {}) {
let fileMarkdown = `[${file.name}](/attachments/${file.uuid})`;
if (isImageFile(file)) {
fileMarkdown = `!${fileMarkdown}`;
@@ -31,19 +33,19 @@ export function generateMarkdownLinkForAttachment(file, {width, dppx}: {width?:
// Scale down images from HiDPI monitors. This uses the <img> tag because it's the only
// method to change image size in Markdown that is supported by all implementations.
// Make the image link relative to the repo path, then the final URL is "/sub-path/owner/repo/attachments/{uuid}"
- fileMarkdown = `<img width="${Math.round(width / dppx)}" alt="${htmlEscape(file.name)}" src="attachments/${htmlEscape(file.uuid)}">`;
+ fileMarkdown = html`<img width="${Math.round(width / dppx)}" alt="${file.name}" src="attachments/${file.uuid}">`;
} else {
// Markdown always renders the image with a relative path, so the final URL is "/sub-path/owner/repo/attachments/{uuid}"
// TODO: it should also use relative path for consistency, because absolute is ambiguous for "/sub-path/attachments" or "/attachments"
fileMarkdown = `![${file.name}](/attachments/${file.uuid})`;
}
} else if (isVideoFile(file)) {
- fileMarkdown = `<video src="attachments/${htmlEscape(file.uuid)}" title="${htmlEscape(file.name)}" controls></video>`;
+ fileMarkdown = html`<video src="attachments/${file.uuid}" title="${file.name}" controls></video>`;
}
return fileMarkdown;
}
-function addCopyLink(file) {
+function addCopyLink(file: Partial<CustomDropzoneFile>) {
// Create a "Copy Link" element, to conveniently copy the image or file link as Markdown to the clipboard
// The "<a>" element has a hardcoded cursor: pointer because the default is overridden by .dropzone
const copyLinkEl = createElementFromHTML(`
@@ -58,6 +60,8 @@ function addCopyLink(file) {
file.previewTemplate.append(copyLinkEl);
}
+type FileUuidDict = Record<string, {submitted: boolean}>;
+
/**
* @param {HTMLElement} dropzoneEl
*/
@@ -67,7 +71,7 @@ export async function initDropzone(dropzoneEl: HTMLElement) {
const attachmentBaseLinkUrl = dropzoneEl.getAttribute('data-link-url');
let disableRemovedfileEvent = false; // when resetting the dropzone (removeAllFiles), disable the "removedfile" event
- let fileUuidDict = {}; // to record: if a comment has been saved, then the uploaded files won't be deleted from server when clicking the Remove in the dropzone
+ let fileUuidDict: FileUuidDict = {}; // to record: if a comment has been saved, then the uploaded files won't be deleted from server when clicking the Remove in the dropzone
const opts: Record<string, any> = {
url: dropzoneEl.getAttribute('data-upload-url'),
headers: {'X-Csrf-Token': csrfToken},
@@ -89,7 +93,7 @@ export async function initDropzone(dropzoneEl: HTMLElement) {
// "http://localhost:3000/owner/repo/issues/[object%20Event]"
// the reason is that the preview "callback(dataURL)" is assign to "img.onerror" then "thumbnail" uses the error object as the dataURL and generates '<img src="[object Event]">'
const dzInst = await createDropzone(dropzoneEl, opts);
- dzInst.on('success', (file: DropzoneFile & {uuid: string}, resp: any) => {
+ dzInst.on('success', (file: CustomDropzoneFile, resp: any) => {
file.uuid = resp.uuid;
fileUuidDict[file.uuid] = {submitted: false};
const input = createElementFromAttrs('input', {name: 'files', type: 'hidden', id: `dropzone-file-${resp.uuid}`, value: resp.uuid});
@@ -98,7 +102,7 @@ export async function initDropzone(dropzoneEl: HTMLElement) {
dzInst.emit(DropzoneCustomEventUploadDone, {file});
});
- dzInst.on('removedfile', async (file: DropzoneFile & {uuid: string}) => {
+ dzInst.on('removedfile', async (file: CustomDropzoneFile) => {
if (disableRemovedfileEvent) return;
dzInst.emit(DropzoneCustomEventRemovedFile, {fileUuid: file.uuid});
diff --git a/web_src/js/features/emoji.ts b/web_src/js/features/emoji.ts
index 933aa951c5..69afe491e2 100644
--- a/web_src/js/features/emoji.ts
+++ b/web_src/js/features/emoji.ts
@@ -1,4 +1,5 @@
import emojis from '../../../assets/emoji.json' with {type: 'json'};
+import {html} from '../utils/html.ts';
const {assetUrlPrefix, customEmojis} = window.config;
@@ -15,24 +16,23 @@ export const emojiKeys = Object.keys(tempMap).sort((a, b) => {
return a.localeCompare(b);
});
-const emojiMap = {};
+const emojiMap: Record<string, string> = {};
for (const key of emojiKeys) {
emojiMap[key] = tempMap[key];
}
// retrieve HTML for given emoji name
-export function emojiHTML(name) {
+export function emojiHTML(name: string) {
let inner;
if (Object.hasOwn(customEmojis, name)) {
- inner = `<img alt=":${name}:" src="${assetUrlPrefix}/img/emoji/${name}.png">`;
+ inner = html`<img alt=":${name}:" src="${assetUrlPrefix}/img/emoji/${name}.png">`;
} else {
inner = emojiString(name);
}
-
- return `<span class="emoji" title=":${name}:">${inner}</span>`;
+ return html`<span class="emoji" title=":${name}:">${inner}</span>`;
}
// retrieve string for given emoji name
-export function emojiString(name) {
+export function emojiString(name: string) {
return emojiMap[name] || `:${name}:`;
}
diff --git a/web_src/js/features/file-fold.ts b/web_src/js/features/file-fold.ts
index 6fe068341a..74b36c0096 100644
--- a/web_src/js/features/file-fold.ts
+++ b/web_src/js/features/file-fold.ts
@@ -5,15 +5,15 @@ import {svg} from '../svg.ts';
// The fold arrow is the icon displayed on the upper left of the file box, especially intended for components having the 'fold-file' class.
// The file content box is the box that should be hidden or shown, especially intended for components having the 'file-content' class.
//
-export function setFileFolding(fileContentBox, foldArrow, newFold) {
+export function setFileFolding(fileContentBox: Element, foldArrow: HTMLElement, newFold: boolean) {
foldArrow.innerHTML = svg(`octicon-chevron-${newFold ? 'right' : 'down'}`, 18);
- fileContentBox.setAttribute('data-folded', newFold);
+ fileContentBox.setAttribute('data-folded', String(newFold));
if (newFold && fileContentBox.getBoundingClientRect().top < 0) {
fileContentBox.scrollIntoView();
}
}
// Like `setFileFolding`, except that it automatically inverts the current file folding state.
-export function invertFileFolding(fileContentBox, foldArrow) {
+export function invertFileFolding(fileContentBox:HTMLElement, foldArrow: HTMLElement) {
setFileFolding(fileContentBox, foldArrow, fileContentBox.getAttribute('data-folded') !== 'true');
}
diff --git a/web_src/js/features/file-view.ts b/web_src/js/features/file-view.ts
new file mode 100644
index 0000000000..d803f53c0d
--- /dev/null
+++ b/web_src/js/features/file-view.ts
@@ -0,0 +1,76 @@
+import type {FileRenderPlugin} from '../render/plugin.ts';
+import {newRenderPlugin3DViewer} from '../render/plugins/3d-viewer.ts';
+import {newRenderPluginPdfViewer} from '../render/plugins/pdf-viewer.ts';
+import {registerGlobalInitFunc} from '../modules/observer.ts';
+import {createElementFromHTML, showElem, toggleClass} from '../utils/dom.ts';
+import {html} from '../utils/html.ts';
+import {basename} from '../utils.ts';
+
+const plugins: FileRenderPlugin[] = [];
+
+function initPluginsOnce(): void {
+ if (plugins.length) return;
+ plugins.push(newRenderPlugin3DViewer(), newRenderPluginPdfViewer());
+}
+
+function findFileRenderPlugin(filename: string, mimeType: string): FileRenderPlugin | null {
+ return plugins.find((plugin) => plugin.canHandle(filename, mimeType)) || null;
+}
+
+function showRenderRawFileButton(elFileView: HTMLElement, renderContainer: HTMLElement | null): void {
+ const toggleButtons = elFileView.querySelector('.file-view-toggle-buttons');
+ showElem(toggleButtons);
+ const displayingRendered = Boolean(renderContainer);
+ toggleClass(toggleButtons.querySelectorAll('.file-view-toggle-source'), 'active', !displayingRendered); // it may not exist
+ toggleClass(toggleButtons.querySelector('.file-view-toggle-rendered'), 'active', displayingRendered);
+ // TODO: if there is only one button, hide it?
+}
+
+async function renderRawFileToContainer(container: HTMLElement, rawFileLink: string, mimeType: string) {
+ const elViewRawPrompt = container.querySelector('.file-view-raw-prompt');
+ if (!rawFileLink || !elViewRawPrompt) throw new Error('unexpected file view container');
+
+ let rendered = false, errorMsg = '';
+ try {
+ const plugin = findFileRenderPlugin(basename(rawFileLink), mimeType);
+ if (plugin) {
+ container.classList.add('is-loading');
+ container.setAttribute('data-render-name', plugin.name); // not used yet
+ await plugin.render(container, rawFileLink);
+ rendered = true;
+ }
+ } catch (e) {
+ errorMsg = `${e}`;
+ } finally {
+ container.classList.remove('is-loading');
+ }
+
+ if (rendered) {
+ elViewRawPrompt.remove();
+ return;
+ }
+
+ // remove all children from the container, and only show the raw file link
+ container.replaceChildren(elViewRawPrompt);
+
+ if (errorMsg) {
+ const elErrorMessage = createElementFromHTML(html`<div class="ui error message">${errorMsg}</div>`);
+ elViewRawPrompt.insertAdjacentElement('afterbegin', elErrorMessage);
+ }
+}
+
+export function initRepoFileView(): void {
+ registerGlobalInitFunc('initRepoFileView', async (elFileView: HTMLElement) => {
+ initPluginsOnce();
+ const rawFileLink = elFileView.getAttribute('data-raw-file-link');
+ const mimeType = elFileView.getAttribute('data-mime-type') || ''; // not used yet
+ // TODO: we should also provide the prefetched file head bytes to let the plugin decide whether to render or not
+ const plugin = findFileRenderPlugin(basename(rawFileLink), mimeType);
+ if (!plugin) return;
+
+ const renderContainer = elFileView.querySelector<HTMLElement>('.file-view-render-container');
+ showRenderRawFileButton(elFileView, renderContainer);
+ // maybe in the future multiple plugins can render the same file, so we should not assume only one plugin will render it
+ if (renderContainer) await renderRawFileToContainer(renderContainer, rawFileLink, mimeType);
+ });
+}
diff --git a/web_src/js/features/heatmap.ts b/web_src/js/features/heatmap.ts
index 53eebc93e5..7cec82108b 100644
--- a/web_src/js/features/heatmap.ts
+++ b/web_src/js/features/heatmap.ts
@@ -7,7 +7,7 @@ export function initHeatmap() {
if (!el) return;
try {
- const heatmap = {};
+ const heatmap: Record<string, number> = {};
for (const {contributions, timestamp} of JSON.parse(el.getAttribute('data-heatmap-data'))) {
// Convert to user timezone and sum contributions by date
const dateStr = new Date(timestamp * 1000).toDateString();
diff --git a/web_src/js/features/imagediff.ts b/web_src/js/features/imagediff.ts
index cd61888f83..20682f74d9 100644
--- a/web_src/js/features/imagediff.ts
+++ b/web_src/js/features/imagediff.ts
@@ -3,7 +3,7 @@ import {hideElem, loadElem, queryElemChildren, queryElems} from '../utils/dom.ts
import {parseDom} from '../utils.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
-function getDefaultSvgBoundsIfUndefined(text, src) {
+function getDefaultSvgBoundsIfUndefined(text: string, src: string) {
const defaultSize = 300;
const maxSize = 99999;
@@ -38,7 +38,7 @@ function getDefaultSvgBoundsIfUndefined(text, src) {
return null;
}
-function createContext(imageAfter, imageBefore) {
+function createContext(imageAfter: HTMLImageElement, imageBefore: HTMLImageElement) {
const sizeAfter = {
width: imageAfter?.width || 0,
height: imageAfter?.height || 0,
@@ -75,7 +75,7 @@ class ImageDiff {
this.containerEl = containerEl;
containerEl.setAttribute('data-image-diff-loaded', 'true');
- fomanticQuery(containerEl).find('.ui.menu.tabular .item').tab({autoTabActivation: false});
+ fomanticQuery(containerEl).find('.ui.menu.tabular .item').tab();
// the container may be hidden by "viewed" checkbox, so use the parent's width for reference
this.diffContainerWidth = Math.max(containerEl.closest('.diff-file-box').clientWidth - 300, 100);
@@ -123,7 +123,7 @@ class ImageDiff {
queryElemChildren(containerEl, '.image-diff-tabs', (el) => el.classList.remove('is-loading'));
}
- initSideBySide(sizes) {
+ initSideBySide(sizes: Record<string, any>) {
let factor = 1;
if (sizes.maxSize.width > (this.diffContainerWidth - 24) / 2) {
factor = (this.diffContainerWidth - 24) / 2 / sizes.maxSize.width;
@@ -176,7 +176,7 @@ class ImageDiff {
}
}
- initSwipe(sizes) {
+ initSwipe(sizes: Record<string, any>) {
let factor = 1;
if (sizes.maxSize.width > this.diffContainerWidth - 12) {
factor = (this.diffContainerWidth - 12) / sizes.maxSize.width;
@@ -215,14 +215,14 @@ class ImageDiff {
this.containerEl.querySelector('.swipe-bar').addEventListener('mousedown', (e) => {
e.preventDefault();
- this.initSwipeEventListeners(e.currentTarget);
+ this.initSwipeEventListeners(e.currentTarget as HTMLElement);
});
}
- initSwipeEventListeners(swipeBar) {
- const swipeFrame = swipeBar.parentNode;
+ initSwipeEventListeners(swipeBar: HTMLElement) {
+ const swipeFrame = swipeBar.parentNode as HTMLElement;
const width = swipeFrame.clientWidth;
- const onSwipeMouseMove = (e) => {
+ const onSwipeMouseMove = (e: MouseEvent) => {
e.preventDefault();
const rect = swipeFrame.getBoundingClientRect();
const value = Math.max(0, Math.min(e.clientX - rect.left, width));
@@ -237,7 +237,7 @@ class ImageDiff {
document.addEventListener('mouseup', removeEventListeners);
}
- initOverlay(sizes) {
+ initOverlay(sizes: Record<string, any>) {
let factor = 1;
if (sizes.maxSize.width > this.diffContainerWidth - 12) {
factor = (this.diffContainerWidth - 12) / sizes.maxSize.width;
diff --git a/web_src/js/features/install.ts b/web_src/js/features/install.ts
index 725dcafab0..ca4bcce881 100644
--- a/web_src/js/features/install.ts
+++ b/web_src/js/features/install.ts
@@ -12,11 +12,12 @@ export function initInstall() {
initPreInstall();
}
}
+
function initPreInstall() {
const defaultDbUser = 'gitea';
const defaultDbName = 'gitea';
- const defaultDbHosts = {
+ const defaultDbHosts: Record<string, string> = {
mysql: '127.0.0.1:3306',
postgres: '127.0.0.1:5432',
mssql: '127.0.0.1:1433',
@@ -27,7 +28,7 @@ function initPreInstall() {
const dbName = document.querySelector<HTMLInputElement>('#db_name');
// Database type change detection.
- document.querySelector('#db_type').addEventListener('change', function () {
+ document.querySelector<HTMLInputElement>('#db_type').addEventListener('change', function () {
const dbType = this.value;
hideElem('div[data-db-setting-for]');
showElem(`div[data-db-setting-for=${dbType}]`);
@@ -59,26 +60,26 @@ function initPreInstall() {
}
// TODO: better handling of exclusive relations.
- document.querySelector('#offline-mode input').addEventListener('change', function () {
+ document.querySelector<HTMLInputElement>('#offline-mode input').addEventListener('change', function () {
if (this.checked) {
document.querySelector<HTMLInputElement>('#disable-gravatar input').checked = true;
document.querySelector<HTMLInputElement>('#federated-avatar-lookup input').checked = false;
}
});
- document.querySelector('#disable-gravatar input').addEventListener('change', function () {
+ document.querySelector<HTMLInputElement>('#disable-gravatar input').addEventListener('change', function () {
if (this.checked) {
document.querySelector<HTMLInputElement>('#federated-avatar-lookup input').checked = false;
} else {
document.querySelector<HTMLInputElement>('#offline-mode input').checked = false;
}
});
- document.querySelector('#federated-avatar-lookup input').addEventListener('change', function () {
+ document.querySelector<HTMLInputElement>('#federated-avatar-lookup input').addEventListener('change', function () {
if (this.checked) {
document.querySelector<HTMLInputElement>('#disable-gravatar input').checked = false;
document.querySelector<HTMLInputElement>('#offline-mode input').checked = false;
}
});
- document.querySelector('#enable-openid-signin input').addEventListener('change', function () {
+ document.querySelector<HTMLInputElement>('#enable-openid-signin input').addEventListener('change', function () {
if (this.checked) {
if (!document.querySelector<HTMLInputElement>('#disable-registration input').checked) {
document.querySelector<HTMLInputElement>('#enable-openid-signup input').checked = true;
@@ -87,7 +88,7 @@ function initPreInstall() {
document.querySelector<HTMLInputElement>('#enable-openid-signup input').checked = false;
}
});
- document.querySelector('#disable-registration input').addEventListener('change', function () {
+ document.querySelector<HTMLInputElement>('#disable-registration input').addEventListener('change', function () {
if (this.checked) {
document.querySelector<HTMLInputElement>('#enable-captcha input').checked = false;
document.querySelector<HTMLInputElement>('#enable-openid-signup input').checked = false;
@@ -95,7 +96,7 @@ function initPreInstall() {
document.querySelector<HTMLInputElement>('#enable-openid-signup input').checked = true;
}
});
- document.querySelector('#enable-captcha input').addEventListener('change', function () {
+ document.querySelector<HTMLInputElement>('#enable-captcha input').addEventListener('change', function () {
if (this.checked) {
document.querySelector<HTMLInputElement>('#disable-registration input').checked = false;
}
@@ -103,7 +104,7 @@ function initPreInstall() {
}
function initPostInstall() {
- const el = document.querySelector('#goto-user-login');
+ const el = document.querySelector('#goto-after-install');
if (!el) return;
const targetUrl = el.getAttribute('href');
diff --git a/web_src/js/features/issue.ts b/web_src/js/features/issue.ts
index a56015a2a2..911cf713d9 100644
--- a/web_src/js/features/issue.ts
+++ b/web_src/js/features/issue.ts
@@ -1,17 +1,21 @@
import type {Issue} from '../types.ts';
+// the getIssueIcon/getIssueColor logic should be kept the same as "templates/shared/issueicon.tmpl"
+
export function getIssueIcon(issue: Issue) {
if (issue.pull_request) {
if (issue.state === 'open') {
- if (issue.pull_request.draft === true) {
+ if (issue.pull_request.draft) {
return 'octicon-git-pull-request-draft'; // WIP PR
}
return 'octicon-git-pull-request'; // Open PR
- } else if (issue.pull_request.merged === true) {
+ } else if (issue.pull_request.merged) {
return 'octicon-git-merge'; // Merged PR
}
- return 'octicon-git-pull-request'; // Closed PR
- } else if (issue.state === 'open') {
+ return 'octicon-git-pull-request-closed'; // Closed PR
+ }
+
+ if (issue.state === 'open') {
return 'octicon-issue-opened'; // Open Issue
}
return 'octicon-issue-closed'; // Closed Issue
@@ -19,12 +23,17 @@ export function getIssueIcon(issue: Issue) {
export function getIssueColor(issue: Issue) {
if (issue.pull_request) {
- if (issue.pull_request.draft === true) {
- return 'grey'; // WIP PR
- } else if (issue.pull_request.merged === true) {
+ if (issue.state === 'open') {
+ if (issue.pull_request.draft) {
+ return 'grey'; // WIP PR
+ }
+ return 'green'; // Open PR
+ } else if (issue.pull_request.merged) {
return 'purple'; // Merged PR
}
+ return 'red'; // Closed PR
}
+
if (issue.state === 'open') {
return 'green'; // Open Issue
}
diff --git a/web_src/js/features/notification.ts b/web_src/js/features/notification.ts
index adfdb157a1..dc0acb0244 100644
--- a/web_src/js/features/notification.ts
+++ b/web_src/js/features/notification.ts
@@ -1,6 +1,5 @@
-import $ from 'jquery';
import {GET} from '../modules/fetch.ts';
-import {toggleElem, type DOMEvent} from '../utils/dom.ts';
+import {toggleElem, type DOMEvent, createElementFromHTML} from '../utils/dom.ts';
import {logoutFromWorker} from '../modules/worker.ts';
const {appSubUrl, notificationSettings, assetVersionEncoded} = window.config;
@@ -158,7 +157,8 @@ async function updateNotificationTable() {
}
const data = await response.text();
- if ($(data).data('sequence-number') === notificationSequenceNumber) {
+ const el = createElementFromHTML(data);
+ if (parseInt(el.getAttribute('data-sequence-number')) === notificationSequenceNumber) {
notificationDiv.outerHTML = data;
initNotificationsTable();
}
diff --git a/web_src/js/features/org-team.ts b/web_src/js/features/org-team.ts
index e4e98fd990..d07818b0ac 100644
--- a/web_src/js/features/org-team.ts
+++ b/web_src/js/features/org-team.ts
@@ -1,35 +1,34 @@
-import $ from 'jquery';
-import {hideElem, showElem} from '../utils/dom.ts';
+import {queryElems, toggleElem} from '../utils/dom.ts';
+import {fomanticQuery} from '../modules/fomantic/base.ts';
const {appSubUrl} = window.config;
-export function initOrgTeamSettings() {
- // Change team access mode
- $('.organization.new.team input[name=permission]').on('change', () => {
- const val = $('input[name=permission]:checked', '.organization.new.team').val();
- if (val === 'admin') {
- hideElem('.organization.new.team .team-units');
- } else {
- showElem('.organization.new.team .team-units');
- }
- });
+function initOrgTeamSettings() {
+ // on the page "page-content organization new team"
+ const pageContent = document.querySelector('.page-content.organization.new.team');
+ if (!pageContent) return;
+ queryElems(pageContent, 'input[name=permission]', (el) => el.addEventListener('change', () => {
+ // Change team access mode
+ const val = pageContent.querySelector<HTMLInputElement>('input[name=permission]:checked')?.value;
+ toggleElem(pageContent.querySelectorAll('.team-units'), val !== 'admin');
+ }));
}
-export function initOrgTeamSearchRepoBox() {
- const $searchRepoBox = $('#search-repo-box');
+function initOrgTeamSearchRepoBox() {
+ // on the page "page-content organization teams"
+ const $searchRepoBox = fomanticQuery('#search-repo-box');
$searchRepoBox.search({
minCharacters: 2,
apiSettings: {
url: `${appSubUrl}/repo/search?q={query}&uid=${$searchRepoBox.data('uid')}`,
- onResponse(response) {
+ onResponse(response: any) {
const items = [];
- $.each(response.data, (_i, item) => {
+ for (const item of response.data) {
items.push({
title: item.repository.full_name.split('/')[1],
description: item.repository.full_name,
});
- });
-
+ }
return {results: items};
},
},
@@ -37,3 +36,9 @@ export function initOrgTeamSearchRepoBox() {
showNoResults: false,
});
}
+
+export function initOrgTeam() {
+ if (!document.querySelector('.page-content.organization')) return;
+ initOrgTeamSettings();
+ initOrgTeamSearchRepoBox();
+}
diff --git a/web_src/js/features/pull-view-file.ts b/web_src/js/features/pull-view-file.ts
index 36fe4bc4df..1124886238 100644
--- a/web_src/js/features/pull-view-file.ts
+++ b/web_src/js/features/pull-view-file.ts
@@ -1,4 +1,4 @@
-import {diffTreeStore} from '../modules/stores.ts';
+import {diffTreeStore, diffTreeStoreSetViewed} from '../modules/diff-file.ts';
import {setFileFolding} from './file-fold.ts';
import {POST} from '../modules/fetch.ts';
@@ -38,7 +38,7 @@ export function initViewedCheckboxListenerFor() {
// The checkbox consists of a div containing the real checkbox with its label and the CSRF token,
// hence the actual checkbox first has to be found
- const checkbox = form.querySelector('input[type=checkbox]');
+ const checkbox = form.querySelector<HTMLInputElement>('input[type=checkbox]');
checkbox.addEventListener('input', function() {
// Mark the file as viewed visually - will especially change the background
if (this.checked) {
@@ -58,14 +58,11 @@ export function initViewedCheckboxListenerFor() {
const fileName = checkbox.getAttribute('name');
- // check if the file is in our difftreestore and if we find it -> change the IsViewed status
- const fileInPageData = diffTreeStore().files.find((x) => x.Name === fileName);
- if (fileInPageData) {
- fileInPageData.IsViewed = this.checked;
- }
+ // check if the file is in our diffTreeStore and if we find it -> change the IsViewed status
+ diffTreeStoreSetViewed(diffTreeStore(), fileName, this.checked);
// Unfortunately, actual forms cause too many problems, hence another approach is needed
- const files = {};
+ const files: Record<string, boolean> = {};
files[fileName] = this.checked;
const data: Record<string, any> = {files};
const headCommitSHA = form.getAttribute('data-headcommit');
@@ -82,13 +79,13 @@ export function initViewedCheckboxListenerFor() {
export function initExpandAndCollapseFilesButton() {
// expand btn
document.querySelector(expandFilesBtnSelector)?.addEventListener('click', () => {
- for (const box of document.querySelectorAll('.file-content[data-folded="true"]')) {
+ for (const box of document.querySelectorAll<HTMLElement>('.file-content[data-folded="true"]')) {
setFileFolding(box, box.querySelector('.fold-file'), false);
}
});
// collapse btn, need to exclude the div of “show moreâ€
document.querySelector(collapseFilesBtnSelector)?.addEventListener('click', () => {
- for (const box of document.querySelectorAll('.file-content:not([data-folded="true"])')) {
+ for (const box of document.querySelectorAll<HTMLElement>('.file-content:not([data-folded="true"])')) {
if (box.getAttribute('id') === 'diff-incomplete') continue;
setFileFolding(box, box.querySelector('.fold-file'), true);
}
diff --git a/web_src/js/features/repo-actions.ts b/web_src/js/features/repo-actions.ts
new file mode 100644
index 0000000000..8d93fce53f
--- /dev/null
+++ b/web_src/js/features/repo-actions.ts
@@ -0,0 +1,48 @@
+import {createApp} from 'vue';
+import RepoActionView from '../components/RepoActionView.vue';
+
+export function initRepositoryActionView() {
+ const el = document.querySelector('#repo-action-view');
+ if (!el) return;
+
+ // TODO: the parent element's full height doesn't work well now,
+ // but we can not pollute the global style at the moment, only fix the height problem for pages with this component
+ const parentFullHeight = document.querySelector<HTMLElement>('body > div.full.height');
+ if (parentFullHeight) parentFullHeight.style.paddingBottom = '0';
+
+ const view = createApp(RepoActionView, {
+ runIndex: el.getAttribute('data-run-index'),
+ jobIndex: el.getAttribute('data-job-index'),
+ actionsURL: el.getAttribute('data-actions-url'),
+ locale: {
+ approve: el.getAttribute('data-locale-approve'),
+ cancel: el.getAttribute('data-locale-cancel'),
+ rerun: el.getAttribute('data-locale-rerun'),
+ rerun_all: el.getAttribute('data-locale-rerun-all'),
+ scheduled: el.getAttribute('data-locale-runs-scheduled'),
+ commit: el.getAttribute('data-locale-runs-commit'),
+ pushedBy: el.getAttribute('data-locale-runs-pushed-by'),
+ artifactsTitle: el.getAttribute('data-locale-artifacts-title'),
+ areYouSure: el.getAttribute('data-locale-are-you-sure'),
+ artifactExpired: el.getAttribute('data-locale-artifact-expired'),
+ confirmDeleteArtifact: el.getAttribute('data-locale-confirm-delete-artifact'),
+ showTimeStamps: el.getAttribute('data-locale-show-timestamps'),
+ showLogSeconds: el.getAttribute('data-locale-show-log-seconds'),
+ showFullScreen: el.getAttribute('data-locale-show-full-screen'),
+ downloadLogs: el.getAttribute('data-locale-download-logs'),
+ status: {
+ unknown: el.getAttribute('data-locale-status-unknown'),
+ waiting: el.getAttribute('data-locale-status-waiting'),
+ running: el.getAttribute('data-locale-status-running'),
+ success: el.getAttribute('data-locale-status-success'),
+ failure: el.getAttribute('data-locale-status-failure'),
+ cancelled: el.getAttribute('data-locale-status-cancelled'),
+ skipped: el.getAttribute('data-locale-status-skipped'),
+ blocked: el.getAttribute('data-locale-status-blocked'),
+ },
+ logsAlwaysAutoScroll: el.getAttribute('data-locale-logs-always-auto-scroll'),
+ logsAlwaysExpandRunning: el.getAttribute('data-locale-logs-always-expand-running'),
+ },
+ });
+ view.mount(el);
+}
diff --git a/web_src/js/features/repo-code.test.ts b/web_src/js/features/repo-code.test.ts
deleted file mode 100644
index 27554aa847..0000000000
--- a/web_src/js/features/repo-code.test.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import {singleAnchorRegex, rangeAnchorRegex} from './repo-code.ts';
-
-test('singleAnchorRegex', () => {
- expect(singleAnchorRegex.test('#L0')).toEqual(false);
- expect(singleAnchorRegex.test('#L1')).toEqual(true);
- expect(singleAnchorRegex.test('#L01')).toEqual(false);
- expect(singleAnchorRegex.test('#n0')).toEqual(false);
- expect(singleAnchorRegex.test('#n1')).toEqual(true);
- expect(singleAnchorRegex.test('#n01')).toEqual(false);
-});
-
-test('rangeAnchorRegex', () => {
- expect(rangeAnchorRegex.test('#L0-L10')).toEqual(false);
- expect(rangeAnchorRegex.test('#L1-L10')).toEqual(true);
- expect(rangeAnchorRegex.test('#L01-L10')).toEqual(false);
- expect(rangeAnchorRegex.test('#L1-L01')).toEqual(false);
-});
diff --git a/web_src/js/features/repo-code.ts b/web_src/js/features/repo-code.ts
index a8d6e8f97d..bf7fd762b0 100644
--- a/web_src/js/features/repo-code.ts
+++ b/web_src/js/features/repo-code.ts
@@ -1,12 +1,7 @@
-import $ from 'jquery';
import {svg} from '../svg.ts';
-import {invertFileFolding} from './file-fold.ts';
import {createTippy} from '../modules/tippy.ts';
-import {clippie} from 'clippie';
import {toAbsoluteUrl} from '../utils.ts';
-
-export const singleAnchorRegex = /^#(L|n)([1-9][0-9]*)$/;
-export const rangeAnchorRegex = /^#(L[1-9][0-9]*)-(L[1-9][0-9]*)$/;
+import {addDelegatedEventListener} from '../utils/dom.ts';
function changeHash(hash: string) {
if (window.history.pushState) {
@@ -16,20 +11,11 @@ function changeHash(hash: string) {
}
}
-function isBlame() {
- return Boolean(document.querySelector('div.blame'));
-}
+// it selects the code lines defined by range: `L1-L3` (3 lines) or `L2` (singe line)
+function selectRange(range: string): Element {
+ for (const el of document.querySelectorAll('.code-view tr.active')) el.classList.remove('active');
+ const elLineNums = document.querySelectorAll(`.code-view td.lines-num span[data-line-number]`);
-function getLineEls() {
- return document.querySelectorAll(`.code-view td.lines-code${isBlame() ? '.blame-code' : ''}`);
-}
-
-function selectRange($linesEls, $selectionEndEl, $selectionStartEls?) {
- for (const el of $linesEls) {
- el.closest('tr').classList.remove('active');
- }
-
- // add hashchange to permalink
const refInNewIssue = document.querySelector('a.ref-in-new-issue');
const copyPermalink = document.querySelector('a.copy-line-permalink');
const viewGitBlame = document.querySelector('a.view_git_blame');
@@ -56,40 +42,34 @@ function selectRange($linesEls, $selectionEndEl, $selectionStartEls?) {
if (!copyPermalink) return;
let link = copyPermalink.getAttribute('data-url');
link = `${link.replace(/#L\d+$|#L\d+-L\d+$/, '')}#${anchor}`;
- copyPermalink.setAttribute('data-url', link);
+ copyPermalink.setAttribute('data-clipboard-text', link);
+ copyPermalink.setAttribute('data-clipboard-text-type', 'url');
};
- if ($selectionStartEls) {
- let a = parseInt($selectionEndEl[0].getAttribute('rel').slice(1));
- let b = parseInt($selectionStartEls[0].getAttribute('rel').slice(1));
- let c;
- if (a !== b) {
- if (a > b) {
- c = a;
- a = b;
- b = c;
- }
- const classes = [];
- for (let i = a; i <= b; i++) {
- classes.push(`[rel=L${i}]`);
- }
- $linesEls.filter(classes.join(',')).each(function () {
- this.closest('tr').classList.add('active');
- });
- changeHash(`#L${a}-L${b}`);
-
- updateIssueHref(`L${a}-L${b}`);
- updateViewGitBlameFragment(`L${a}-L${b}`);
- updateCopyPermalinkUrl(`L${a}-L${b}`);
- return;
- }
+ const rangeFields = range ? range.split('-') : [];
+ const start = rangeFields[0] ?? '';
+ if (!start) return null;
+ const stop = rangeFields[1] || start;
+
+ // format is i.e. 'L14-L26'
+ let startLineNum = parseInt(start.substring(1));
+ let stopLineNum = parseInt(stop.substring(1));
+ if (startLineNum > stopLineNum) {
+ const tmp = startLineNum;
+ startLineNum = stopLineNum;
+ stopLineNum = tmp;
+ range = `${stop}-${start}`;
}
- $selectionEndEl[0].closest('tr').classList.add('active');
- changeHash(`#${$selectionEndEl[0].getAttribute('rel')}`);
- updateIssueHref($selectionEndEl[0].getAttribute('rel'));
- updateViewGitBlameFragment($selectionEndEl[0].getAttribute('rel'));
- updateCopyPermalinkUrl($selectionEndEl[0].getAttribute('rel'));
+ const first = elLineNums[startLineNum - 1] ?? null;
+ for (let i = startLineNum - 1; i <= stopLineNum - 1 && i < elLineNums.length; i++) {
+ elLineNums[i].closest('tr').classList.add('active');
+ }
+ changeHash(`#${range}`);
+ updateIssueHref(range);
+ updateViewGitBlameFragment(range);
+ updateCopyPermalinkUrl(range);
+ return first;
}
function showLineButton() {
@@ -103,6 +83,8 @@ function showLineButton() {
// find active row and add button
const tr = document.querySelector('.code-view tr.active');
+ if (!tr) return;
+
const td = tr.querySelector('td.lines-num');
const btn = document.createElement('button');
btn.classList.add('code-line-button', 'ui', 'basic', 'button');
@@ -128,62 +110,39 @@ function showLineButton() {
}
export function initRepoCodeView() {
- if ($('.code-view .lines-num').length > 0) {
- $(document).on('click', '.lines-num span', function (e) {
- const linesEls = getLineEls();
- const selectedEls = Array.from(linesEls).filter((el) => {
- return el.matches(`[rel=${this.getAttribute('id')}]`);
- });
-
- let from;
- if (e.shiftKey) {
- from = Array.from(linesEls).filter((el) => {
- return el.closest('tr').classList.contains('active');
- });
- }
- selectRange($(linesEls), $(selectedEls), from ? $(from) : null);
- window.getSelection().removeAllRanges();
- showLineButton();
- });
-
- $(window).on('hashchange', () => {
- let m = rangeAnchorRegex.exec(window.location.hash);
- const $linesEls = $(getLineEls());
- let $first;
- if (m) {
- $first = $linesEls.filter(`[rel=${m[1]}]`);
- if ($first.length) {
- selectRange($linesEls, $first, $linesEls.filter(`[rel=${m[2]}]`));
-
- // show code view menu marker (don't show in blame page)
- if (!isBlame()) {
- showLineButton();
- }
-
- $('html, body').scrollTop($first.offset().top - 200);
- return;
- }
- }
- m = singleAnchorRegex.exec(window.location.hash);
- if (m) {
- $first = $linesEls.filter(`[rel=L${m[2]}]`);
- if ($first.length) {
- selectRange($linesEls, $first);
-
- // show code view menu marker (don't show in blame page)
- if (!isBlame()) {
- showLineButton();
- }
-
- $('html, body').scrollTop($first.offset().top - 200);
- }
- }
- }).trigger('hashchange');
- }
- $(document).on('click', '.fold-file', ({currentTarget}) => {
- invertFileFolding(currentTarget.closest('.file-content'), currentTarget);
- });
- $(document).on('click', '.copy-line-permalink', async ({currentTarget}) => {
- await clippie(toAbsoluteUrl(currentTarget.getAttribute('data-url')));
+ // When viewing a file or blame, there is always a ".file-view" element,
+ // but the ".code-view" class is only present when viewing the "code" of a file; it is not present when viewing a PDF file.
+ // Since the ".file-view" will be dynamically reloaded when navigating via the left file tree (eg: view a PDF file, then view a source code file, etc.)
+ // the "code-view" related event listeners should always be added when the current page contains ".file-view" element.
+ if (!document.querySelector('.repo-view-container .file-view')) return;
+
+ // "file code view" and "blame" pages need this "line number button" feature
+ let selRangeStart: string;
+ addDelegatedEventListener(document, 'click', '.code-view .lines-num span', (el: HTMLElement, e: KeyboardEvent) => {
+ if (!selRangeStart || !e.shiftKey) {
+ selRangeStart = el.getAttribute('id');
+ selectRange(selRangeStart);
+ } else {
+ const selRangeStop = el.getAttribute('id');
+ selectRange(`${selRangeStart}-${selRangeStop}`);
+ }
+ window.getSelection().removeAllRanges();
+ showLineButton();
});
+
+ // apply the selected range from the URL hash
+ const onHashChange = () => {
+ if (!window.location.hash) return;
+ if (!document.querySelector('.code-view .lines-num')) return;
+ const range = window.location.hash.substring(1);
+ const first = selectRange(range);
+ if (first) {
+ // set scrollRestoration to 'manual' when there is a hash in the URL, so that the scroll position will not be remembered after refreshing
+ if (window.history.scrollRestoration !== 'manual') window.history.scrollRestoration = 'manual';
+ first.scrollIntoView({block: 'start'});
+ showLineButton();
+ }
+ };
+ onHashChange();
+ window.addEventListener('hashchange', onHashChange);
}
diff --git a/web_src/js/features/repo-commit.ts b/web_src/js/features/repo-commit.ts
index 56493443d9..98ec2328ec 100644
--- a/web_src/js/features/repo-commit.ts
+++ b/web_src/js/features/repo-commit.ts
@@ -1,27 +1,26 @@
import {createTippy} from '../modules/tippy.ts';
import {toggleElem} from '../utils/dom.ts';
+import {registerGlobalEventFunc, registerGlobalInitFunc} from '../modules/observer.ts';
export function initRepoEllipsisButton() {
- for (const button of document.querySelectorAll('.js-toggle-commit-body')) {
- button.addEventListener('click', function (e) {
- e.preventDefault();
- const expanded = this.getAttribute('aria-expanded') === 'true';
- toggleElem(this.parentElement.querySelector('.commit-body'));
- this.setAttribute('aria-expanded', String(!expanded));
- });
- }
+ registerGlobalEventFunc('click', 'onRepoEllipsisButtonClick', async (el: HTMLInputElement, e: Event) => {
+ e.preventDefault();
+ const expanded = el.getAttribute('aria-expanded') === 'true';
+ toggleElem(el.parentElement.querySelector('.commit-body'));
+ el.setAttribute('aria-expanded', String(!expanded));
+ });
}
export function initCommitStatuses() {
- for (const element of document.querySelectorAll('[data-tippy="commit-statuses"]')) {
- const top = document.querySelector('.repository.file.list') || document.querySelector('.repository.diff');
-
- createTippy(element, {
- content: element.nextElementSibling,
- placement: top ? 'top-start' : 'bottom-start',
+ registerGlobalInitFunc('initCommitStatuses', (el: HTMLElement) => {
+ const nextEl = el.nextElementSibling;
+ if (!nextEl.matches('.tippy-target')) throw new Error('Expected next element to be a tippy target');
+ createTippy(el, {
+ content: nextEl,
+ placement: 'bottom-start',
interactive: true,
role: 'dialog',
theme: 'box-with-header',
});
- }
+ });
}
diff --git a/web_src/js/features/repo-common.test.ts b/web_src/js/features/repo-common.test.ts
new file mode 100644
index 0000000000..33a29ecb2c
--- /dev/null
+++ b/web_src/js/features/repo-common.test.ts
@@ -0,0 +1,22 @@
+import {sanitizeRepoName, substituteRepoOpenWithUrl} from './repo-common.ts';
+
+test('substituteRepoOpenWithUrl', () => {
+ // For example: "x-github-client://openRepo/https://github.com/go-gitea/gitea"
+ expect(substituteRepoOpenWithUrl('proto://a/{url}', 'https://gitea')).toEqual('proto://a/https://gitea');
+ expect(substituteRepoOpenWithUrl('proto://a?link={url}', 'https://gitea')).toEqual('proto://a?link=https%3A%2F%2Fgitea');
+});
+
+test('sanitizeRepoName', () => {
+ expect(sanitizeRepoName(' a b ')).toEqual('a-b');
+ expect(sanitizeRepoName('a-b_c.git ')).toEqual('a-b_c');
+ expect(sanitizeRepoName('/x.git/')).toEqual('-x.git-');
+ expect(sanitizeRepoName('.profile')).toEqual('.profile');
+ expect(sanitizeRepoName('.profile.')).toEqual('.profile');
+ expect(sanitizeRepoName('.pro..file')).toEqual('.pro.file');
+
+ expect(sanitizeRepoName('foo.rss.atom.git.wiki')).toEqual('foo');
+
+ expect(sanitizeRepoName('.')).toEqual('');
+ expect(sanitizeRepoName('..')).toEqual('');
+ expect(sanitizeRepoName('-')).toEqual('');
+});
diff --git a/web_src/js/features/repo-common.ts b/web_src/js/features/repo-common.ts
index 90860720e4..ebb6881c67 100644
--- a/web_src/js/features/repo-common.ts
+++ b/web_src/js/features/repo-common.ts
@@ -1,4 +1,4 @@
-import {queryElems} from '../utils/dom.ts';
+import {queryElems, type DOMEvent} from '../utils/dom.ts';
import {POST} from '../modules/fetch.ts';
import {showErrorToast} from '../modules/toast.ts';
import {sleep} from '../utils.ts';
@@ -7,10 +7,10 @@ import {createApp} from 'vue';
import {toOriginUrl} from '../utils/url.ts';
import {createTippy} from '../modules/tippy.ts';
-async function onDownloadArchive(e) {
+async function onDownloadArchive(e: DOMEvent<MouseEvent>) {
e.preventDefault();
// there are many places using the "archive-link", eg: the dropdown on the repo code page, the release list
- const el = e.target.closest('a.archive-link[href]');
+ const el = e.target.closest<HTMLAnchorElement>('a.archive-link[href]');
const targetLoading = el.closest('.ui.dropdown') ?? el;
targetLoading.classList.add('is-loading', 'loading-icon-2px');
try {
@@ -42,23 +42,60 @@ export function initRepoActivityTopAuthorsChart() {
}
}
+export function substituteRepoOpenWithUrl(tmpl: string, url: string): string {
+ const pos = tmpl.indexOf('{url}');
+ if (pos === -1) return tmpl;
+ const posQuestionMark = tmpl.indexOf('?');
+ const needEncode = posQuestionMark >= 0 && posQuestionMark < pos;
+ return tmpl.replace('{url}', needEncode ? encodeURIComponent(url) : url);
+}
+
function initCloneSchemeUrlSelection(parent: Element) {
const elCloneUrlInput = parent.querySelector<HTMLInputElement>('.repo-clone-url');
- const tabSsh = parent.querySelector('.repo-clone-ssh');
const tabHttps = parent.querySelector('.repo-clone-https');
+ const tabSsh = parent.querySelector('.repo-clone-ssh');
+ const tabTea = parent.querySelector('.repo-clone-tea');
const updateClonePanelUi = function() {
- const scheme = localStorage.getItem('repo-clone-protocol') || 'https';
- const isSSH = scheme === 'ssh' && Boolean(tabSsh) || scheme !== 'ssh' && !tabHttps;
+ let scheme = localStorage.getItem('repo-clone-protocol');
+ if (!['https', 'ssh', 'tea'].includes(scheme)) {
+ scheme = 'https';
+ }
+
+ // Fallbacks if the scheme preference is not available in the tabs, for example: empty repo page, there are only HTTPS and SSH
+ if (scheme === 'tea' && !tabTea) {
+ scheme = 'https';
+ }
+ if (scheme === 'https' && !tabHttps) {
+ scheme = 'ssh';
+ } else if (scheme === 'ssh' && !tabSsh) {
+ scheme = 'https';
+ }
+
+ const isHttps = scheme === 'https';
+ const isSsh = scheme === 'ssh';
+ const isTea = scheme === 'tea';
+
if (tabHttps) {
tabHttps.textContent = window.origin.split(':')[0].toUpperCase(); // show "HTTP" or "HTTPS"
- tabHttps.classList.toggle('active', !isSSH);
+ tabHttps.classList.toggle('active', isHttps);
}
if (tabSsh) {
- tabSsh.classList.toggle('active', isSSH);
+ tabSsh.classList.toggle('active', isSsh);
+ }
+ if (tabTea) {
+ tabTea.classList.toggle('active', isTea);
+ }
+
+ let tab: Element;
+ if (isHttps) {
+ tab = tabHttps;
+ } else if (isSsh) {
+ tab = tabSsh;
+ } else if (isTea) {
+ tab = tabTea;
}
- const tab = isSSH ? tabSsh : tabHttps;
if (!tab) return;
const link = toOriginUrl(tab.getAttribute('data-link'));
@@ -70,18 +107,22 @@ function initCloneSchemeUrlSelection(parent: Element) {
}
}
for (const el of parent.querySelectorAll<HTMLAnchorElement>('.js-clone-url-editor')) {
- el.href = el.getAttribute('data-href-template').replace('{url}', encodeURIComponent(link));
+ el.href = substituteRepoOpenWithUrl(el.getAttribute('data-href-template'), link);
}
};
updateClonePanelUi();
// tabSsh or tabHttps might not both exist, eg: guest view, or one is disabled by the server
+ tabHttps?.addEventListener('click', () => {
+ localStorage.setItem('repo-clone-protocol', 'https');
+ updateClonePanelUi();
+ });
tabSsh?.addEventListener('click', () => {
localStorage.setItem('repo-clone-protocol', 'ssh');
updateClonePanelUi();
});
- tabHttps?.addEventListener('click', () => {
- localStorage.setItem('repo-clone-protocol', 'https');
+ tabTea?.addEventListener('click', () => {
+ localStorage.setItem('repo-clone-protocol', 'tea');
updateClonePanelUi();
});
elCloneUrlInput.addEventListener('focus', () => {
@@ -99,6 +140,7 @@ function initClonePanelButton(btn: HTMLButtonElement) {
placement: 'bottom-end',
interactive: true,
hideOnClick: true,
+ arrow: false,
});
}
@@ -107,7 +149,7 @@ export function initRepoCloneButtons() {
queryElems(document, '.clone-buttons-combo', initCloneSchemeUrlSelection);
}
-export async function updateIssuesMeta(url, action, issue_ids, id) {
+export async function updateIssuesMeta(url: string, action: string, issue_ids: string, id: string) {
try {
const response = await POST(url, {data: new URLSearchParams({action, issue_ids, id})});
if (!response.ok) {
@@ -117,3 +159,19 @@ export async function updateIssuesMeta(url, action, issue_ids, id) {
console.error(error);
}
}
+
+export function sanitizeRepoName(name: string): string {
+ name = name.trim().replace(/[^-.\w]/g, '-');
+ for (let lastName = ''; lastName !== name;) {
+ lastName = name;
+ name = name.replace(/\.+$/g, '');
+ name = name.replace(/\.{2,}/g, '.');
+ for (const ext of ['.git', '.wiki', '.rss', '.atom']) {
+ if (name.endsWith(ext)) {
+ name = name.substring(0, name.length - ext.length);
+ }
+ }
+ }
+ if (['.', '..', '-'].includes(name)) name = '';
+ return name;
+}
diff --git a/web_src/js/features/repo-diff-filetree.ts b/web_src/js/features/repo-diff-filetree.ts
index bc275a90f6..cc4576a846 100644
--- a/web_src/js/features/repo-diff-filetree.ts
+++ b/web_src/js/features/repo-diff-filetree.ts
@@ -1,6 +1,5 @@
import {createApp} from 'vue';
import DiffFileTree from '../components/DiffFileTree.vue';
-import DiffFileList from '../components/DiffFileList.vue';
export function initDiffFileTree() {
const el = document.querySelector('#diff-file-tree');
@@ -9,11 +8,3 @@ export function initDiffFileTree() {
const fileTreeView = createApp(DiffFileTree);
fileTreeView.mount(el);
}
-
-export function initDiffFileList() {
- const fileListElement = document.querySelector('#diff-file-list');
- if (!fileListElement) return;
-
- const fileListView = createApp(DiffFileList);
- fileListView.mount(fileListElement);
-}
diff --git a/web_src/js/features/repo-diff.ts b/web_src/js/features/repo-diff.ts
index 2b405abb9b..ad1da5c2fa 100644
--- a/web_src/js/features/repo-diff.ts
+++ b/web_src/js/features/repo-diff.ts
@@ -1,40 +1,31 @@
-import $ from 'jquery';
-import {initCompReactionSelector} from './comp/ReactionSelector.ts';
import {initRepoIssueContentHistory} from './repo-issue-content.ts';
-import {initDiffFileTree, initDiffFileList} from './repo-diff-filetree.ts';
+import {initDiffFileTree} from './repo-diff-filetree.ts';
import {initDiffCommitSelect} from './repo-diff-commitselect.ts';
import {validateTextareaNonEmpty} from './comp/ComboMarkdownEditor.ts';
import {initViewedCheckboxListenerFor, countAndUpdateViewedFiles, initExpandAndCollapseFilesButton} from './pull-view-file.ts';
import {initImageDiff} from './imagediff.ts';
import {showErrorToast} from '../modules/toast.ts';
-import {
- submitEventSubmitter,
- queryElemSiblings,
- hideElem,
- showElem,
- animateOnce,
- addDelegatedEventListener,
- createElementFromHTML,
-} from '../utils/dom.ts';
+import {submitEventSubmitter, queryElemSiblings, hideElem, showElem, animateOnce, addDelegatedEventListener, createElementFromHTML, queryElems} from '../utils/dom.ts';
import {POST, GET} from '../modules/fetch.ts';
-import {fomanticQuery} from '../modules/fomantic/base.ts';
import {createTippy} from '../modules/tippy.ts';
+import {invertFileFolding} from './file-fold.ts';
+import {parseDom} from '../utils.ts';
+import {registerGlobalSelectorFunc} from '../modules/observer.ts';
-const {pageData, i18n} = window.config;
+const {i18n} = window.config;
-function initRepoDiffFileViewToggle() {
- $('.file-view-toggle').on('click', function () {
- for (const el of queryElemSiblings(this)) {
- el.classList.remove('active');
- }
- this.classList.add('active');
+function initRepoDiffFileBox(el: HTMLElement) {
+ // switch between "rendered" and "source", for image and CSV files
+ queryElems(el, '.file-view-toggle', (btn) => btn.addEventListener('click', () => {
+ queryElemSiblings(btn, '.file-view-toggle', (el) => el.classList.remove('active'));
+ btn.classList.add('active');
- const target = document.querySelector(this.getAttribute('data-toggle-selector'));
- if (!target) return;
+ const target = document.querySelector(btn.getAttribute('data-toggle-selector'));
+ if (!target) throw new Error('Target element not found');
hideElem(queryElemSiblings(target));
showElem(target);
- });
+ }));
}
function initRepoDiffConversationForm() {
@@ -82,7 +73,6 @@ function initRepoDiffConversationForm() {
el.classList.add('tw-invisible');
}
}
- fomanticQuery(newConversationHolder.querySelectorAll('.ui.dropdown')).dropdown();
// the default behavior is to add a pending review, so if no submitter, it also means "pending_review"
if (!submitter || submitter?.matches('button[name="pending_review"]')) {
@@ -102,22 +92,21 @@ function initRepoDiffConversationForm() {
}
});
- $(document).on('click', '.resolve-conversation', async function (e) {
+ addDelegatedEventListener(document, 'click', '.resolve-conversation', async (el, e) => {
e.preventDefault();
- const comment_id = $(this).data('comment-id');
- const origin = $(this).data('origin');
- const action = $(this).data('action');
- const url = $(this).data('update-url');
+ const comment_id = el.getAttribute('data-comment-id');
+ const origin = el.getAttribute('data-origin');
+ const action = el.getAttribute('data-action');
+ const url = el.getAttribute('data-update-url');
try {
const response = await POST(url, {data: new URLSearchParams({origin, action, comment_id})});
const data = await response.text();
- if ($(this).closest('.conversation-holder').length) {
- const $conversation = $(data);
- $(this).closest('.conversation-holder').replaceWith($conversation);
- $conversation.find('.dropdown').dropdown();
- initCompReactionSelector($conversation[0]);
+ const elConversationHolder = el.closest('.conversation-holder');
+ if (elConversationHolder) {
+ const elNewConversation = createElementFromHTML(data);
+ elConversationHolder.replaceWith(elNewConversation);
} else {
window.location.reload();
}
@@ -127,24 +116,19 @@ function initRepoDiffConversationForm() {
});
}
-export function initRepoDiffConversationNav() {
+function initRepoDiffConversationNav() {
// Previous/Next code review conversation
- $(document).on('click', '.previous-conversation', (e) => {
- const $conversation = $(e.currentTarget).closest('.comment-code-cloud');
- const $conversations = $('.comment-code-cloud:not(.tw-hidden)');
- const index = $conversations.index($conversation);
- const previousIndex = index > 0 ? index - 1 : $conversations.length - 1;
- const $previousConversation = $conversations.eq(previousIndex);
- const anchor = $previousConversation.find('.comment').first()[0].getAttribute('id');
- window.location.href = `#${anchor}`;
- });
- $(document).on('click', '.next-conversation', (e) => {
- const $conversation = $(e.currentTarget).closest('.comment-code-cloud');
- const $conversations = $('.comment-code-cloud:not(.tw-hidden)');
- const index = $conversations.index($conversation);
- const nextIndex = index < $conversations.length - 1 ? index + 1 : 0;
- const $nextConversation = $conversations.eq(nextIndex);
- const anchor = $nextConversation.find('.comment').first()[0].getAttribute('id');
+ addDelegatedEventListener(document, 'click', '.previous-conversation, .next-conversation', (el, e) => {
+ e.preventDefault();
+ const isPrevious = el.matches('.previous-conversation');
+ const elCurConversation = el.closest('.comment-code-cloud');
+ const elAllConversations = document.querySelectorAll('.comment-code-cloud:not(.tw-hidden)');
+ const index = Array.from(elAllConversations).indexOf(elCurConversation);
+ const previousIndex = index > 0 ? index - 1 : elAllConversations.length - 1;
+ const nextIndex = index < elAllConversations.length - 1 ? index + 1 : 0;
+ const navIndex = isPrevious ? previousIndex : nextIndex;
+ const elNavConversation = elAllConversations[navIndex];
+ const anchor = elNavConversation.querySelector('.comment').id;
window.location.href = `#${anchor}`;
});
}
@@ -160,6 +144,7 @@ function initDiffHeaderPopup() {
// Will be called when the show more (files) button has been pressed
function onShowMoreFiles() {
+ // TODO: replace these calls with the "observer.ts" methods
initRepoIssueContentHistory();
initViewedCheckboxListenerFor();
countAndUpdateViewedFiles();
@@ -167,81 +152,112 @@ function onShowMoreFiles() {
initDiffHeaderPopup();
}
-export async function loadMoreFiles(url) {
- const target = document.querySelector('a#diff-show-more-files');
- if (target?.classList.contains('disabled') || pageData.diffFileInfo.isLoadingNewData) {
- return;
+async function loadMoreFiles(btn: Element): Promise<boolean> {
+ if (btn.classList.contains('disabled')) {
+ return false;
}
- pageData.diffFileInfo.isLoadingNewData = true;
- target?.classList.add('disabled');
-
+ btn.classList.add('disabled');
+ const url = btn.getAttribute('data-href');
try {
const response = await GET(url);
const resp = await response.text();
- const $resp = $(resp);
+ const respDoc = parseDom(resp, 'text/html');
+ const respFileBoxes = respDoc.querySelector('#diff-file-boxes');
// the response is a full HTML page, we need to extract the relevant contents:
- // 1. append the newly loaded file list items to the existing list
- $('#diff-incomplete').replaceWith($resp.find('#diff-file-boxes').children());
- // 2. re-execute the script to append the newly loaded items to the JS variables to refresh the DiffFileTree
- $('body').append($resp.find('script#diff-data-script'));
-
+ // * append the newly loaded file list items to the existing list
+ document.querySelector('#diff-incomplete').replaceWith(...Array.from(respFileBoxes.children));
onShowMoreFiles();
+ return true;
} catch (error) {
console.error('Error:', error);
showErrorToast('An error occurred while loading more files.');
} finally {
- target?.classList.remove('disabled');
- pageData.diffFileInfo.isLoadingNewData = false;
+ btn.classList.remove('disabled');
}
+ return false;
}
function initRepoDiffShowMore() {
- $(document).on('click', 'a#diff-show-more-files', (e) => {
+ addDelegatedEventListener(document, 'click', 'a#diff-show-more-files', (el, e) => {
e.preventDefault();
-
- const linkLoadMore = e.target.getAttribute('data-href');
- loadMoreFiles(linkLoadMore);
+ loadMoreFiles(el);
});
- $(document).on('click', 'a.diff-load-button', async (e) => {
+ addDelegatedEventListener(document, 'click', 'a.diff-load-button', async (el, e) => {
e.preventDefault();
- const $target = $(e.target);
-
- if (e.target.classList.contains('disabled')) {
- return;
- }
+ if (el.classList.contains('disabled')) return;
- e.target.classList.add('disabled');
-
- const url = $target.data('href');
+ el.classList.add('disabled');
+ const url = el.getAttribute('data-href');
try {
const response = await GET(url);
const resp = await response.text();
-
- if (!resp) {
- return;
- }
- $target.parent().replaceWith($(resp).find('#diff-file-boxes .diff-file-body .file-body').children());
+ const respDoc = parseDom(resp, 'text/html');
+ const respFileBody = respDoc.querySelector('#diff-file-boxes .diff-file-body .file-body');
+ const respFileBodyChildren = Array.from(respFileBody.children); // respFileBody.children will be empty after replaceWith
+ el.parentElement.replaceWith(...respFileBodyChildren);
+ for (const el of respFileBodyChildren) window.htmx.process(el);
+ // FIXME: calling onShowMoreFiles is not quite right here.
+ // But since onShowMoreFiles mixes "init diff box" and "init diff body" together,
+ // so it still needs to call it to make the "ImageDiff" and something similar work.
onShowMoreFiles();
} catch (error) {
console.error('Error:', error);
} finally {
- e.target.classList.remove('disabled');
+ el.classList.remove('disabled');
}
});
}
+async function loadUntilFound() {
+ const hashTargetSelector = window.location.hash;
+ if (!hashTargetSelector.startsWith('#diff-') && !hashTargetSelector.startsWith('#issuecomment-')) {
+ return;
+ }
+
+ while (true) {
+ // use getElementById to avoid querySelector throws an error when the hash is invalid
+ // eslint-disable-next-line unicorn/prefer-query-selector
+ const targetElement = document.getElementById(hashTargetSelector.substring(1));
+ if (targetElement) {
+ targetElement.scrollIntoView();
+ return;
+ }
+
+ // the button will be refreshed after each "load more", so query it every time
+ const showMoreButton = document.querySelector('#diff-show-more-files');
+ if (!showMoreButton) {
+ return; // nothing more to load
+ }
+
+ // Load more files, await ensures we don't block progress
+ const ok = await loadMoreFiles(showMoreButton);
+ if (!ok) return; // failed to load more files
+ }
+}
+
+function initRepoDiffHashChangeListener() {
+ window.addEventListener('hashchange', loadUntilFound);
+ loadUntilFound();
+}
+
export function initRepoDiffView() {
- initRepoDiffConversationForm();
- if (!$('#diff-file-list').length) return;
+ initRepoDiffConversationForm(); // such form appears on the "conversation" page and "diff" page
+
+ if (!document.querySelector('#diff-file-boxes')) return;
+ initRepoDiffConversationNav(); // "previous" and "next" buttons only appear on "diff" page
initDiffFileTree();
- initDiffFileList();
initDiffCommitSelect();
initRepoDiffShowMore();
initDiffHeaderPopup();
- initRepoDiffFileViewToggle();
initViewedCheckboxListenerFor();
initExpandAndCollapseFilesButton();
+ initRepoDiffHashChangeListener();
+
+ registerGlobalSelectorFunc('#diff-file-boxes .diff-file-box', initRepoDiffFileBox);
+ addDelegatedEventListener(document, 'click', '.fold-file', (el) => {
+ invertFileFolding(el.closest('.file-content'), el);
+ });
}
diff --git a/web_src/js/features/repo-editor.ts b/web_src/js/features/repo-editor.ts
index 64d0402d84..f3ca13460c 100644
--- a/web_src/js/features/repo-editor.ts
+++ b/web_src/js/features/repo-editor.ts
@@ -1,13 +1,13 @@
-import {htmlEscape} from 'escape-goat';
+import {html, htmlRaw} from '../utils/html.ts';
import {createCodeEditor} from './codeeditor.ts';
import {hideElem, queryElems, showElem, createElementFromHTML} from '../utils/dom.ts';
-import {initMarkupContent} from '../markup/content.ts';
import {attachRefIssueContextPopup} from './contextpopup.ts';
import {POST} from '../modules/fetch.ts';
import {initDropzone} from './dropzone.ts';
import {confirmModal} from './comp/ConfirmModal.ts';
import {applyAreYouSure, ignoreAreYouSure} from '../vendor/jquery.are-you-sure.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
+import {submitFormFetchAction} from './common-fetch-action.ts';
function initEditPreviewTab(elForm: HTMLFormElement) {
const elTabMenu = elForm.querySelector('.repo-editor-menu');
@@ -38,9 +38,6 @@ export function initRepoEditor() {
const dropzoneUpload = document.querySelector<HTMLElement>('.page-content.repository.editor.upload .dropzone');
if (dropzoneUpload) initDropzone(dropzoneUpload);
- const editArea = document.querySelector<HTMLTextAreaElement>('.page-content.repository.editor textarea#edit_area');
- if (!editArea) return;
-
for (const el of queryElems<HTMLInputElement>(document, '.js-quick-pull-choice-option')) {
el.addEventListener('input', () => {
if (el.value === 'commit-to-new-branch') {
@@ -55,6 +52,7 @@ export function initRepoEditor() {
}
const filenameInput = document.querySelector<HTMLInputElement>('#file-name');
+ if (!filenameInput) return;
function joinTreePath() {
const parts = [];
for (const el of document.querySelectorAll('.breadcrumb span.section')) {
@@ -89,10 +87,10 @@ export function initRepoEditor() {
if (i < parts.length - 1) {
if (trimValue.length) {
const linkElement = createElementFromHTML(
- `<span class="section"><a href="#">${htmlEscape(value)}</a></span>`,
+ html`<span class="section"><a href="#">${value}</a></span>`,
);
const dividerElement = createElementFromHTML(
- `<div class="breadcrumb-divider">/</div>`,
+ html`<div class="breadcrumb-divider">/</div>`,
);
links.push(linkElement);
dividers.push(dividerElement);
@@ -115,7 +113,7 @@ export function initRepoEditor() {
if (!warningDiv) {
warningDiv = document.createElement('div');
warningDiv.classList.add('ui', 'warning', 'message', 'flash-message', 'flash-warning', 'space-related');
- warningDiv.innerHTML = '<p>File path contains leading or trailing whitespace.</p>';
+ warningDiv.innerHTML = html`<p>File path contains leading or trailing whitespace.</p>`;
// Add display 'block' because display is set to 'none' in formantic\build\semantic.css
warningDiv.style.display = 'block';
const inputContainer = document.querySelector('.repo-editor-header');
@@ -145,32 +143,34 @@ export function initRepoEditor() {
});
const elForm = document.querySelector<HTMLFormElement>('.repository.editor .edit.form');
- initEditPreviewTab(elForm);
- (async () => {
- const editor = await createCodeEditor(editArea, filenameInput);
+ // on the upload page, there is no editor(textarea)
+ const editArea = document.querySelector<HTMLTextAreaElement>('.page-content.repository.editor textarea#edit_area');
+ if (!editArea) return;
- // Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage
- // to enable or disable the commit button
- const commitButton = document.querySelector<HTMLButtonElement>('#commit-button');
- const dirtyFileClass = 'dirty-file';
+ // Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage
+ // to enable or disable the commit button
+ const commitButton = document.querySelector<HTMLButtonElement>('#commit-button');
+ const dirtyFileClass = 'dirty-file';
- // Disabling the button at the start
- if (document.querySelector<HTMLInputElement>('input[name="page_has_posted"]').value !== 'true') {
- commitButton.disabled = true;
- }
+ const syncCommitButtonState = () => {
+ const dirty = elForm.classList.contains(dirtyFileClass);
+ commitButton.disabled = !dirty;
+ };
+ // Registering a custom listener for the file path and the file content
+ // FIXME: it is not quite right here (old bug), it causes double-init, the global areYouSure "dirty" class will also be added
+ applyAreYouSure(elForm, {
+ silent: true,
+ dirtyClass: dirtyFileClass,
+ fieldSelector: ':input:not(.commit-form-wrapper :input)',
+ change: syncCommitButtonState,
+ });
+ syncCommitButtonState(); // disable the "commit" button when no content changes
- // Registering a custom listener for the file path and the file content
- // FIXME: it is not quite right here (old bug), it causes double-init, the global areYouSure "dirty" class will also be added
- applyAreYouSure(elForm, {
- silent: true,
- dirtyClass: dirtyFileClass,
- fieldSelector: ':input:not(.commit-form-wrapper :input)',
- change($form) {
- const dirty = $form[0]?.classList.contains(dirtyFileClass);
- commitButton.disabled = !dirty;
- },
- });
+ initEditPreviewTab(elForm);
+
+ (async () => {
+ const editor = await createCodeEditor(editArea, filenameInput);
// Update the editor from query params, if available,
// only after the dirtyFileClass initialization
@@ -180,7 +180,7 @@ export function initRepoEditor() {
editor.setValue(value);
}
- commitButton?.addEventListener('click', async (e) => {
+ commitButton.addEventListener('click', async (e) => {
// A modal which asks if an empty file should be committed
if (!editArea.value) {
e.preventDefault();
@@ -189,15 +189,15 @@ export function initRepoEditor() {
content: elForm.getAttribute('data-text-empty-confirm-content'),
})) {
ignoreAreYouSure(elForm);
- elForm.submit();
+ submitFormFetchAction(elForm);
}
}
});
})();
}
-export function renderPreviewPanelContent(previewPanel: Element, content: string) {
- previewPanel.innerHTML = content;
- initMarkupContent();
+export function renderPreviewPanelContent(previewPanel: Element, htmlContent: string) {
+ // the content is from the server, so it is safe to use innerHTML
+ previewPanel.innerHTML = html`<div class="render-content markup">${htmlRaw(htmlContent)}</div>`;
attachRefIssueContextPopup(previewPanel.querySelectorAll('p .ref-issue'));
}
diff --git a/web_src/js/features/repo-findfile.ts b/web_src/js/features/repo-findfile.ts
index 6500978bc8..59c827126f 100644
--- a/web_src/js/features/repo-findfile.ts
+++ b/web_src/js/features/repo-findfile.ts
@@ -4,13 +4,15 @@ import {pathEscapeSegments} from '../utils/url.ts';
import {GET} from '../modules/fetch.ts';
const threshold = 50;
-let files = [];
-let repoFindFileInput, repoFindFileTableBody, repoFindFileNoResult;
+let files: Array<string> = [];
+let repoFindFileInput: HTMLInputElement;
+let repoFindFileTableBody: HTMLElement;
+let repoFindFileNoResult: HTMLElement;
// return the case-insensitive sub-match result as an array: [unmatched, matched, unmatched, matched, ...]
// res[even] is unmatched, res[odd] is matched, see unit tests for examples
// argument subLower must be a lower-cased string.
-export function strSubMatch(full, subLower) {
+export function strSubMatch(full: string, subLower: string) {
const res = [''];
let i = 0, j = 0;
const fullLower = full.toLowerCase();
@@ -38,7 +40,7 @@ export function strSubMatch(full, subLower) {
return res;
}
-export function calcMatchedWeight(matchResult) {
+export function calcMatchedWeight(matchResult: Array<any>) {
let weight = 0;
for (let i = 0; i < matchResult.length; i++) {
if (i % 2 === 1) { // matches are on odd indices, see strSubMatch
@@ -49,7 +51,7 @@ export function calcMatchedWeight(matchResult) {
return weight;
}
-export function filterRepoFilesWeighted(files, filter) {
+export function filterRepoFilesWeighted(files: Array<string>, filter: string) {
let filterResult = [];
if (filter) {
const filterLower = filter.toLowerCase();
@@ -71,7 +73,7 @@ export function filterRepoFilesWeighted(files, filter) {
return filterResult;
}
-function filterRepoFiles(filter) {
+function filterRepoFiles(filter: string) {
const treeLink = repoFindFileInput.getAttribute('data-url-tree-link');
repoFindFileTableBody.innerHTML = '';
diff --git a/web_src/js/features/repo-graph.ts b/web_src/js/features/repo-graph.ts
index 6d1629a1c1..036a55f715 100644
--- a/web_src/js/features/repo-graph.ts
+++ b/web_src/js/features/repo-graph.ts
@@ -1,4 +1,4 @@
-import {hideElem, showElem, type DOMEvent} from '../utils/dom.ts';
+import {toggleClass} from '../utils/dom.ts';
import {GET} from '../modules/fetch.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
@@ -6,87 +6,59 @@ export function initRepoGraphGit() {
const graphContainer = document.querySelector<HTMLElement>('#git-graph-container');
if (!graphContainer) return;
- document.querySelector('#flow-color-monochrome')?.addEventListener('click', () => {
- document.querySelector('#flow-color-monochrome').classList.add('active');
- document.querySelector('#flow-color-colored')?.classList.remove('active');
- graphContainer.classList.remove('colored');
- graphContainer.classList.add('monochrome');
+ const elColorMonochrome = document.querySelector<HTMLElement>('#flow-color-monochrome');
+ const elColorColored = document.querySelector<HTMLElement>('#flow-color-colored');
+ const toggleColorMode = (mode: 'monochrome' | 'colored') => {
+ toggleClass(graphContainer, 'monochrome', mode === 'monochrome');
+ toggleClass(graphContainer, 'colored', mode === 'colored');
+
+ toggleClass(elColorMonochrome, 'active', mode === 'monochrome');
+ toggleClass(elColorColored, 'active', mode === 'colored');
+
const params = new URLSearchParams(window.location.search);
- params.set('mode', 'monochrome');
- const queryString = params.toString();
- if (queryString) {
- window.history.replaceState({}, '', `?${queryString}`);
- } else {
- window.history.replaceState({}, '', window.location.pathname);
- }
- for (const link of document.querySelectorAll('.pagination a')) {
+ params.set('mode', mode);
+ window.history.replaceState(null, '', `?${params.toString()}`);
+ for (const link of document.querySelectorAll('#git-graph-body .pagination a')) {
const href = link.getAttribute('href');
if (!href) continue;
const url = new URL(href, window.location.href);
const params = url.searchParams;
- params.set('mode', 'monochrome');
+ params.set('mode', mode);
url.search = `?${params.toString()}`;
link.setAttribute('href', url.href);
}
- });
+ };
+ elColorMonochrome.addEventListener('click', () => toggleColorMode('monochrome'));
+ elColorColored.addEventListener('click', () => toggleColorMode('colored'));
- document.querySelector('#flow-color-colored')?.addEventListener('click', () => {
- document.querySelector('#flow-color-colored').classList.add('active');
- document.querySelector('#flow-color-monochrome')?.classList.remove('active');
- graphContainer.classList.add('colored');
- graphContainer.classList.remove('monochrome');
- for (const link of document.querySelectorAll('.pagination a')) {
- const href = link.getAttribute('href');
- if (!href) continue;
- const url = new URL(href, window.location.href);
- const params = url.searchParams;
- params.delete('mode');
- url.search = `?${params.toString()}`;
- link.setAttribute('href', url.href);
- }
- const params = new URLSearchParams(window.location.search);
- params.delete('mode');
- const queryString = params.toString();
- if (queryString) {
- window.history.replaceState({}, '', `?${queryString}`);
- } else {
- window.history.replaceState({}, '', window.location.pathname);
- }
- });
+ const elGraphBody = document.querySelector<HTMLElement>('#git-graph-body');
const url = new URL(window.location.href);
const params = url.searchParams;
- const updateGraph = () => {
+ const loadGitGraph = async () => {
const queryString = params.toString();
const ajaxUrl = new URL(url);
ajaxUrl.searchParams.set('div-only', 'true');
- window.history.replaceState({}, '', queryString ? `?${queryString}` : window.location.pathname);
- document.querySelector('#pagination').innerHTML = '';
- hideElem('#rel-container');
- hideElem('#rev-container');
- showElem('#loading-indicator');
- (async () => {
- const response = await GET(String(ajaxUrl));
- const html = await response.text();
- const div = document.createElement('div');
- div.innerHTML = html;
- document.querySelector('#pagination').innerHTML = div.querySelector('#pagination').innerHTML;
- document.querySelector('#rel-container').innerHTML = div.querySelector('#rel-container').innerHTML;
- document.querySelector('#rev-container').innerHTML = div.querySelector('#rev-container').innerHTML;
- hideElem('#loading-indicator');
- showElem('#rel-container');
- showElem('#rev-container');
- })();
+ window.history.replaceState(null, '', queryString ? `?${queryString}` : window.location.pathname);
+
+ elGraphBody.classList.add('is-loading');
+ try {
+ const resp = await GET(ajaxUrl.toString());
+ elGraphBody.innerHTML = await resp.text();
+ } finally {
+ elGraphBody.classList.remove('is-loading');
+ }
};
+
const dropdownSelected = params.getAll('branch');
if (params.has('hide-pr-refs') && params.get('hide-pr-refs') === 'true') {
dropdownSelected.splice(0, 0, '...flow-hide-pr-refs');
}
- const flowSelectRefsDropdown = document.querySelector('#flow-select-refs-dropdown');
- fomanticQuery(flowSelectRefsDropdown).dropdown('set selected', dropdownSelected);
- fomanticQuery(flowSelectRefsDropdown).dropdown({
- clearable: true,
- fullTextSeach: 'exact',
+ const $dropdown = fomanticQuery('#flow-select-refs-dropdown');
+ $dropdown.dropdown({clearable: true});
+ $dropdown.dropdown('set selected', dropdownSelected);
+ // must add the callback after setting the selected items, otherwise each "selected" item will trigger the callback
+ $dropdown.dropdown('setting', {
onRemove(toRemove: string) {
if (toRemove === '...flow-hide-pr-refs') {
params.delete('hide-pr-refs');
@@ -99,7 +71,7 @@ export function initRepoGraphGit() {
}
}
}
- updateGraph();
+ loadGitGraph();
},
onAdd(toAdd: string) {
if (toAdd === '...flow-hide-pr-refs') {
@@ -107,49 +79,7 @@ export function initRepoGraphGit() {
} else {
params.append('branch', toAdd);
}
- updateGraph();
+ loadGitGraph();
},
});
-
- graphContainer.addEventListener('mouseenter', (e: DOMEvent<MouseEvent>) => {
- if (e.target.matches('#rev-list li')) {
- const flow = e.target.getAttribute('data-flow');
- if (flow === '0') return;
- document.querySelector(`#flow-${flow}`)?.classList.add('highlight');
- e.target.classList.add('hover');
- for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) {
- item.classList.add('highlight');
- }
- } else if (e.target.matches('#rel-container .flow-group')) {
- e.target.classList.add('highlight');
- const flow = e.target.getAttribute('data-flow');
- for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) {
- item.classList.add('highlight');
- }
- } else if (e.target.matches('#rel-container .flow-commit')) {
- const rev = e.target.getAttribute('data-rev');
- document.querySelector(`#rev-list li#commit-${rev}`)?.classList.add('hover');
- }
- });
-
- graphContainer.addEventListener('mouseleave', (e: DOMEvent<MouseEvent>) => {
- if (e.target.matches('#rev-list li')) {
- const flow = e.target.getAttribute('data-flow');
- if (flow === '0') return;
- document.querySelector(`#flow-${flow}`)?.classList.remove('highlight');
- e.target.classList.remove('hover');
- for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) {
- item.classList.remove('highlight');
- }
- } else if (e.target.matches('#rel-container .flow-group')) {
- e.target.classList.remove('highlight');
- const flow = e.target.getAttribute('data-flow');
- for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) {
- item.classList.remove('highlight');
- }
- } else if (e.target.matches('#rel-container .flow-commit')) {
- const rev = e.target.getAttribute('data-rev');
- document.querySelector(`#rev-list li#commit-${rev}`)?.classList.remove('hover');
- }
- });
}
diff --git a/web_src/js/features/repo-home.ts b/web_src/js/features/repo-home.ts
index 9c1e317486..04a1288626 100644
--- a/web_src/js/features/repo-home.ts
+++ b/web_src/js/features/repo-home.ts
@@ -89,10 +89,10 @@ export function initRepoTopicBar() {
url: `${appSubUrl}/explore/topics/search?q={query}`,
throttle: 500,
cache: false,
- onResponse(res) {
+ onResponse(this: any, res: any) {
const formattedResponse = {
success: false,
- results: [],
+ results: [] as Array<Record<string, any>>,
};
const query = stripTags(this.urlData.query.trim());
let found_query = false;
@@ -134,12 +134,12 @@ export function initRepoTopicBar() {
return formattedResponse;
},
},
- onLabelCreate(value) {
+ onLabelCreate(value: string) {
value = value.toLowerCase().trim();
this.attr('data-value', value).contents().first().replaceWith(value);
return fomanticQuery(this);
},
- onAdd(addedValue, _addedText, $addedChoice) {
+ onAdd(addedValue: string, _addedText: any, $addedChoice: any) {
addedValue = addedValue.toLowerCase().trim();
$addedChoice[0].setAttribute('data-value', addedValue);
$addedChoice[0].setAttribute('data-text', addedValue);
diff --git a/web_src/js/features/repo-issue-content.ts b/web_src/js/features/repo-issue-content.ts
index 2279c26beb..056b810be8 100644
--- a/web_src/js/features/repo-issue-content.ts
+++ b/web_src/js/features/repo-issue-content.ts
@@ -33,7 +33,7 @@ function showContentHistoryDetail(issueBaseUrl: string, commentId: string, histo
$fomanticDropdownOptions.dropdown({
showOnFocus: false,
allowReselection: true,
- async onChange(_value, _text, $item) {
+ async onChange(_value: string, _text: string, $item: any) {
const optionItem = $item.data('option-item');
if (optionItem === 'delete') {
if (window.confirm(i18nTextDeleteFromHistoryConfirm)) {
@@ -115,7 +115,7 @@ function showContentHistoryMenu(issueBaseUrl: string, elCommentItem: Element, co
onHide() {
$fomanticDropdown.dropdown('change values', null);
},
- onChange(value, itemHtml, $item) {
+ onChange(value: string, itemHtml: string, $item: any) {
if (value && !$item.find('[data-history-is-deleted=1]').length) {
showContentHistoryDetail(issueBaseUrl, commentId, value, itemHtml);
}
diff --git a/web_src/js/features/repo-issue-edit.ts b/web_src/js/features/repo-issue-edit.ts
index 38dfea4743..e89e5a787a 100644
--- a/web_src/js/features/repo-issue-edit.ts
+++ b/web_src/js/features/repo-issue-edit.ts
@@ -2,33 +2,32 @@ import {handleReply} from './repo-issue.ts';
import {getComboMarkdownEditor, initComboMarkdownEditor, ComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';
import {POST} from '../modules/fetch.ts';
import {showErrorToast} from '../modules/toast.ts';
-import {hideElem, querySingleVisibleElem, showElem} from '../utils/dom.ts';
+import {hideElem, querySingleVisibleElem, showElem, type DOMEvent} from '../utils/dom.ts';
import {attachRefIssueContextPopup} from './contextpopup.ts';
-import {initCommentContent, initMarkupContent} from '../markup/content.ts';
import {triggerUploadStateChanged} from './comp/EditorUpload.ts';
import {convertHtmlToMarkdown} from '../markup/html2markdown.ts';
import {applyAreYouSure, reinitializeAreYouSure} from '../vendor/jquery.are-you-sure.ts';
-async function tryOnEditContent(e) {
+async function tryOnEditContent(e: DOMEvent<MouseEvent>) {
const clickTarget = e.target.closest('.edit-content');
if (!clickTarget) return;
e.preventDefault();
- const segment = clickTarget.closest('.header').nextElementSibling;
+ const segment = clickTarget.closest('.comment-header').nextElementSibling;
const editContentZone = segment.querySelector('.edit-content-zone');
const renderContent = segment.querySelector('.render-content');
const rawContent = segment.querySelector('.raw-content');
let comboMarkdownEditor : ComboMarkdownEditor;
- const cancelAndReset = (e) => {
+ const cancelAndReset = (e: Event) => {
e.preventDefault();
showElem(renderContent);
hideElem(editContentZone);
comboMarkdownEditor.dropzoneReloadFiles();
};
- const saveAndRefresh = async (e) => {
+ const saveAndRefresh = async (e: Event) => {
e.preventDefault();
// we are already in a form, do not bubble up to the document otherwise there will be other "form submit handlers"
// at the moment, the form submit event conflicts with initRepoDiffConversationForm (global '.conversation-holder form' event handler)
@@ -60,7 +59,7 @@ async function tryOnEditContent(e) {
} else {
renderContent.innerHTML = data.content;
rawContent.textContent = comboMarkdownEditor.value();
- const refIssues = renderContent.querySelectorAll('p .ref-issue');
+ const refIssues = renderContent.querySelectorAll<HTMLElement>('p .ref-issue');
attachRefIssueContextPopup(refIssues);
}
const content = segment;
@@ -74,8 +73,6 @@ async function tryOnEditContent(e) {
content.querySelector('.dropzone-attachments').outerHTML = data.attachments;
}
comboMarkdownEditor.dropzoneSubmitReload();
- initMarkupContent();
- initCommentContent();
} catch (error) {
showErrorToast(`Failed to save the content: ${error}`);
console.error(error);
@@ -125,7 +122,7 @@ function extractSelectedMarkdown(container: HTMLElement) {
return convertHtmlToMarkdown(el);
}
-async function tryOnQuoteReply(e) {
+async function tryOnQuoteReply(e: Event) {
const clickTarget = (e.target as HTMLElement).closest('.quote-reply');
if (!clickTarget) return;
@@ -135,11 +132,11 @@ async function tryOnQuoteReply(e) {
const targetMarkupToQuote = targetRawToQuote.parentElement.querySelector<HTMLElement>('.render-content.markup');
let contentToQuote = extractSelectedMarkdown(targetMarkupToQuote);
if (!contentToQuote) contentToQuote = targetRawToQuote.textContent;
- const quotedContent = `${contentToQuote.replace(/^/mg, '> ')}\n`;
+ const quotedContent = `${contentToQuote.replace(/^/mg, '> ')}\n\n`;
let editor;
if (clickTarget.classList.contains('quote-reply-diff')) {
- const replyBtn = clickTarget.closest('.comment-code-cloud').querySelector('button.comment-form-reply');
+ const replyBtn = clickTarget.closest('.comment-code-cloud').querySelector<HTMLElement>('button.comment-form-reply');
editor = await handleReply(replyBtn);
} else {
// for normal issue/comment page
diff --git a/web_src/js/features/repo-issue-list.ts b/web_src/js/features/repo-issue-list.ts
index 74d4362bfd..762fbf51bb 100644
--- a/web_src/js/features/repo-issue-list.ts
+++ b/web_src/js/features/repo-issue-list.ts
@@ -1,12 +1,13 @@
import {updateIssuesMeta} from './repo-common.ts';
-import {toggleElem, isElemHidden, queryElems} from '../utils/dom.ts';
-import {htmlEscape} from 'escape-goat';
+import {toggleElem, queryElems, isElemVisible} from '../utils/dom.ts';
+import {html} from '../utils/html.ts';
import {confirmModal} from './comp/ConfirmModal.ts';
import {showErrorToast} from '../modules/toast.ts';
import {createSortable} from '../modules/sortable.ts';
import {DELETE, POST} from '../modules/fetch.ts';
import {parseDom} from '../utils.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
+import type {SortableEvent} from 'sortablejs';
function initRepoIssueListCheckboxes() {
const issueSelectAll = document.querySelector<HTMLInputElement>('.issue-checkbox-all');
@@ -32,8 +33,8 @@ function initRepoIssueListCheckboxes() {
toggleElem('#issue-filters', !anyChecked);
toggleElem('#issue-actions', anyChecked);
// there are two panels but only one select-all checkbox, so move the checkbox to the visible panel
- const panels = document.querySelectorAll('#issue-filters, #issue-actions');
- const visiblePanel = Array.from(panels).find((el) => !isElemHidden(el));
+ const panels = document.querySelectorAll<HTMLElement>('#issue-filters, #issue-actions');
+ const visiblePanel = Array.from(panels).find((el) => isElemVisible(el));
const toolbarLeft = visiblePanel.querySelector('.issue-list-toolbar-left');
toolbarLeft.prepend(issueSelectAll);
};
@@ -104,7 +105,7 @@ function initDropdownUserRemoteSearch(el: Element) {
$searchDropdown.dropdown('setting', {
fullTextSearch: true,
selectOnKeydown: false,
- action: (_text, value) => {
+ action: (_text: string, value: string) => {
window.location.href = actionJumpUrl.replace('{username}', encodeURIComponent(value));
},
});
@@ -133,14 +134,14 @@ function initDropdownUserRemoteSearch(el: Element) {
$searchDropdown.dropdown('setting', 'apiSettings', {
cache: false,
url: `${searchUrl}&q={query}`,
- onResponse(resp) {
+ onResponse(resp: any) {
// the content is provided by backend IssuePosters handler
processedResults.length = 0;
for (const item of resp.results) {
- let html = `<img class="ui avatar tw-align-middle" src="${htmlEscape(item.avatar_link)}" aria-hidden="true" alt="" width="20" height="20"><span class="gt-ellipsis">${htmlEscape(item.username)}</span>`;
- if (item.full_name) html += `<span class="search-fullname tw-ml-2">${htmlEscape(item.full_name)}</span>`;
+ let nameHtml = html`<img class="ui avatar tw-align-middle" src="${item.avatar_link}" aria-hidden="true" alt width="20" height="20"><span class="gt-ellipsis">${item.username}</span>`;
+ if (item.full_name) nameHtml += html`<span class="search-fullname tw-ml-2">${item.full_name}</span>`;
if (selectedUsername.toLowerCase() === item.username.toLowerCase()) selectedUsername = item.username;
- processedResults.push({value: item.username, name: html});
+ processedResults.push({value: item.username, name: nameHtml});
}
resp.results = processedResults;
return resp;
@@ -153,7 +154,7 @@ function initDropdownUserRemoteSearch(el: Element) {
const dropdownSetup = {...$searchDropdown.dropdown('internal', 'setup')};
const dropdownTemplates = $searchDropdown.dropdown('setting', 'templates');
$searchDropdown.dropdown('internal', 'setup', dropdownSetup);
- dropdownSetup.menu = function (values) {
+ dropdownSetup.menu = function (values: any) {
// remove old dynamic items
for (const el of elMenu.querySelectorAll(':scope > .dynamic-item')) {
el.remove();
@@ -193,7 +194,7 @@ function initPinRemoveButton() {
}
}
-async function pinMoveEnd(e) {
+async function pinMoveEnd(e: SortableEvent) {
const url = e.item.getAttribute('data-move-url');
const id = Number(e.item.getAttribute('data-issue-id'));
await POST(url, {data: {id, position: e.newIndex + 1}});
diff --git a/web_src/js/features/repo-issue-pr-form.ts b/web_src/js/features/repo-issue-pr-form.ts
deleted file mode 100644
index 94a2857340..0000000000
--- a/web_src/js/features/repo-issue-pr-form.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import {createApp} from 'vue';
-import PullRequestMergeForm from '../components/PullRequestMergeForm.vue';
-
-export function initRepoPullRequestMergeForm() {
- const el = document.querySelector('#pull-request-merge-form');
- if (!el) return;
-
- const view = createApp(PullRequestMergeForm);
- view.mount(el);
-}
diff --git a/web_src/js/features/repo-issue-pr-status.ts b/web_src/js/features/repo-issue-pr-status.ts
deleted file mode 100644
index 8426b389f0..0000000000
--- a/web_src/js/features/repo-issue-pr-status.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export function initRepoPullRequestCommitStatus() {
- for (const btn of document.querySelectorAll('.commit-status-hide-checks')) {
- const panel = btn.closest('.commit-status-panel');
- const list = panel.querySelector<HTMLElement>('.commit-status-list');
- btn.addEventListener('click', () => {
- list.style.maxHeight = list.style.maxHeight ? '' : '0px'; // toggle
- btn.textContent = btn.getAttribute(list.style.maxHeight ? 'data-show-all' : 'data-hide-all');
- });
- }
-}
diff --git a/web_src/js/features/repo-issue-pull.ts b/web_src/js/features/repo-issue-pull.ts
new file mode 100644
index 0000000000..c415dad08f
--- /dev/null
+++ b/web_src/js/features/repo-issue-pull.ts
@@ -0,0 +1,133 @@
+import {createApp} from 'vue';
+import PullRequestMergeForm from '../components/PullRequestMergeForm.vue';
+import {GET, POST} from '../modules/fetch.ts';
+import {fomanticQuery} from '../modules/fomantic/base.ts';
+import {createElementFromHTML} from '../utils/dom.ts';
+
+function initRepoPullRequestUpdate(el: HTMLElement) {
+ const prUpdateButtonContainer = el.querySelector('#update-pr-branch-with-base');
+ if (!prUpdateButtonContainer) return;
+
+ const prUpdateButton = prUpdateButtonContainer.querySelector<HTMLButtonElement>(':scope > button');
+ const prUpdateDropdown = prUpdateButtonContainer.querySelector(':scope > .ui.dropdown');
+ prUpdateButton.addEventListener('click', async function (e) {
+ e.preventDefault();
+ const redirect = this.getAttribute('data-redirect');
+ this.classList.add('is-loading');
+ let response: Response;
+ try {
+ response = await POST(this.getAttribute('data-do'));
+ } catch (error) {
+ console.error(error);
+ } finally {
+ this.classList.remove('is-loading');
+ }
+ let data: Record<string, any>;
+ try {
+ data = await response?.json(); // the response is probably not a JSON
+ } catch (error) {
+ console.error(error);
+ }
+ if (data?.redirect) {
+ window.location.href = data.redirect;
+ } else if (redirect) {
+ window.location.href = redirect;
+ } else {
+ window.location.reload();
+ }
+ });
+
+ fomanticQuery(prUpdateDropdown).dropdown({
+ onChange(_text: string, _value: string, $choice: any) {
+ const choiceEl = $choice[0];
+ const url = choiceEl.getAttribute('data-do');
+ if (url) {
+ const buttonText = prUpdateButton.querySelector('.button-text');
+ if (buttonText) {
+ buttonText.textContent = choiceEl.textContent;
+ }
+ prUpdateButton.setAttribute('data-do', url);
+ }
+ },
+ });
+}
+
+function initRepoPullRequestCommitStatus(el: HTMLElement) {
+ for (const btn of el.querySelectorAll('.commit-status-hide-checks')) {
+ const panel = btn.closest('.commit-status-panel');
+ const list = panel.querySelector<HTMLElement>('.commit-status-list');
+ btn.addEventListener('click', () => {
+ list.style.maxHeight = list.style.maxHeight ? '' : '0px'; // toggle
+ btn.textContent = btn.getAttribute(list.style.maxHeight ? 'data-show-all' : 'data-hide-all');
+ });
+ }
+}
+
+function initRepoPullRequestMergeForm(box: HTMLElement) {
+ const el = box.querySelector('#pull-request-merge-form');
+ if (!el) return;
+
+ const view = createApp(PullRequestMergeForm);
+ view.mount(el);
+}
+
+function executeScripts(elem: HTMLElement) {
+ for (const oldScript of elem.querySelectorAll('script')) {
+ // TODO: that's the only way to load the data for the merge form. In the future
+ // we need to completely decouple the page data and embedded script
+ // eslint-disable-next-line github/no-dynamic-script-tag
+ const newScript = document.createElement('script');
+ for (const attr of oldScript.attributes) {
+ if (attr.name === 'type' && attr.value === 'module') continue;
+ newScript.setAttribute(attr.name, attr.value);
+ }
+ newScript.text = oldScript.text;
+ document.body.append(newScript);
+ }
+}
+
+export function initRepoPullMergeBox(el: HTMLElement) {
+ initRepoPullRequestCommitStatus(el);
+ initRepoPullRequestUpdate(el);
+ initRepoPullRequestMergeForm(el);
+
+ const reloadingIntervalValue = el.getAttribute('data-pull-merge-box-reloading-interval');
+ if (!reloadingIntervalValue) return;
+
+ const reloadingInterval = parseInt(reloadingIntervalValue);
+ const pullLink = el.getAttribute('data-pull-link');
+ let timerId: number;
+
+ let reloadMergeBox: () => Promise<void>;
+ const stopReloading = () => {
+ if (!timerId) return;
+ clearTimeout(timerId);
+ timerId = null;
+ };
+ const startReloading = () => {
+ if (timerId) return;
+ setTimeout(reloadMergeBox, reloadingInterval);
+ };
+ const onVisibilityChange = () => {
+ if (document.hidden) {
+ stopReloading();
+ } else {
+ startReloading();
+ }
+ };
+ reloadMergeBox = async () => {
+ const resp = await GET(`${pullLink}/merge_box`);
+ stopReloading();
+ if (!resp.ok) {
+ startReloading();
+ return;
+ }
+ document.removeEventListener('visibilitychange', onVisibilityChange);
+ const newElem = createElementFromHTML(await resp.text());
+ executeScripts(newElem);
+ el.replaceWith(newElem);
+ };
+
+ document.addEventListener('visibilitychange', onVisibilityChange);
+ startReloading();
+}
diff --git a/web_src/js/features/repo-issue-sidebar-combolist.ts b/web_src/js/features/repo-issue-sidebar-combolist.ts
index 24d620547f..f25c0a77c6 100644
--- a/web_src/js/features/repo-issue-sidebar-combolist.ts
+++ b/web_src/js/features/repo-issue-sidebar-combolist.ts
@@ -1,6 +1,6 @@
import {fomanticQuery} from '../modules/fomantic/base.ts';
import {POST} from '../modules/fetch.ts';
-import {queryElemChildren, queryElems, toggleElem} from '../utils/dom.ts';
+import {addDelegatedEventListener, queryElemChildren, queryElems, toggleElem} from '../utils/dom.ts';
// if there are draft comments, confirm before reloading, to avoid losing comments
function issueSidebarReloadConfirmDraftComment() {
@@ -22,7 +22,7 @@ function issueSidebarReloadConfirmDraftComment() {
window.location.reload();
}
-class IssueSidebarComboList {
+export class IssueSidebarComboList {
updateUrl: string;
updateAlgo: string;
selectionMode: string;
@@ -30,9 +30,11 @@ class IssueSidebarComboList {
elList: HTMLElement;
elComboValue: HTMLInputElement;
initialValues: string[];
+ container: HTMLElement;
- constructor(private container: HTMLElement) {
- this.updateUrl = this.container.getAttribute('data-update-url');
+ constructor(container: HTMLElement) {
+ this.container = container;
+ this.updateUrl = container.getAttribute('data-update-url');
this.updateAlgo = container.getAttribute('data-update-algo');
this.selectionMode = container.getAttribute('data-selection-mode');
if (!['single', 'multiple'].includes(this.selectionMode)) throw new Error(`Invalid data-update-on: ${this.selectionMode}`);
@@ -46,7 +48,7 @@ class IssueSidebarComboList {
return Array.from(this.elDropdown.querySelectorAll('.menu > .item.checked'), (el) => el.getAttribute('data-value'));
}
- updateUiList(changedValues) {
+ updateUiList(changedValues: Array<string>) {
const elEmptyTip = this.elList.querySelector('.item.empty-list');
queryElemChildren(this.elList, '.item:not(.empty-list)', (el) => el.remove());
for (const value of changedValues) {
@@ -60,7 +62,7 @@ class IssueSidebarComboList {
toggleElem(elEmptyTip, !hasItems);
}
- async updateToBackend(changedValues) {
+ async updateToBackend(changedValues: Array<string>) {
if (this.updateAlgo === 'diff') {
for (const value of this.initialValues) {
if (!changedValues.includes(value)) {
@@ -93,9 +95,7 @@ class IssueSidebarComboList {
}
}
- async onItemClick(e) {
- const elItem = (e.target as HTMLElement).closest('.item');
- if (!elItem) return;
+ async onItemClick(elItem: HTMLElement, e: Event) {
e.preventDefault();
if (elItem.hasAttribute('data-can-change') && elItem.getAttribute('data-can-change') !== 'true') return;
@@ -144,16 +144,13 @@ class IssueSidebarComboList {
}
this.initialValues = this.collectCheckedValues();
- this.elDropdown.addEventListener('click', (e) => this.onItemClick(e));
+ addDelegatedEventListener(this.elDropdown, 'click', '.item', (el, e) => this.onItemClick(el, e));
fomanticQuery(this.elDropdown).dropdown('setting', {
action: 'nothing', // do not hide the menu if user presses Enter
fullTextSearch: 'exact',
+ hideDividers: 'empty',
onHide: () => this.onHide(),
});
}
}
-
-export function initIssueSidebarComboList(container: HTMLElement) {
- new IssueSidebarComboList(container).init();
-}
diff --git a/web_src/js/features/repo-issue-sidebar.md b/web_src/js/features/repo-issue-sidebar.md
index 6de013f1c2..e1ce0927e1 100644
--- a/web_src/js/features/repo-issue-sidebar.md
+++ b/web_src/js/features/repo-issue-sidebar.md
@@ -22,10 +22,13 @@ A sidebar combo (dropdown+list) is like this:
When the selected items change, the `combo-value` input will be updated.
If there is `data-update-url`, it also calls backend to attach/detach the changed items.
-Also, the changed items will be syncronized to the `ui list` items.
+Also, the changed items will be synchronized to the `ui list` items.
The items with the same data-scope only allow one selected at a time.
The dropdown selection could work in 2 modes:
* single: only one item could be selected, it updates immediately when the item is selected.
* multiple: multiple items could be selected, it defers the update until the dropdown is hidden.
+
+When using "scrolling menu", the items must be in the same level,
+otherwise keyboard (ArrowUp/ArrowDown/Enter) won't work.
diff --git a/web_src/js/features/repo-issue-sidebar.ts b/web_src/js/features/repo-issue-sidebar.ts
index e0f68aa059..290e1ae000 100644
--- a/web_src/js/features/repo-issue-sidebar.ts
+++ b/web_src/js/features/repo-issue-sidebar.ts
@@ -1,7 +1,6 @@
-import $ from 'jquery';
import {POST} from '../modules/fetch.ts';
import {queryElems, toggleElem} from '../utils/dom.ts';
-import {initIssueSidebarComboList} from './repo-issue-sidebar-combolist.ts';
+import {IssueSidebarComboList} from './repo-issue-sidebar-combolist.ts';
function initBranchSelector() {
// TODO: RemoveIssueRef: see "repo/issue/branch_selector_field.tmpl"
@@ -9,9 +8,8 @@ function initBranchSelector() {
if (!elSelectBranch) return;
const urlUpdateIssueRef = elSelectBranch.getAttribute('data-url-update-issueref');
- const $selectBranch = $(elSelectBranch);
- const $branchMenu = $selectBranch.find('.reference-list-menu');
- $branchMenu.find('.item:not(.no-select)').on('click', async function (e) {
+ const elBranchMenu = elSelectBranch.querySelector('.reference-list-menu');
+ queryElems(elBranchMenu, '.item:not(.no-select)', (el) => el.addEventListener('click', async function (e) {
e.preventDefault();
const selectedValue = this.getAttribute('data-id'); // eg: "refs/heads/my-branch"
const selectedText = this.getAttribute('data-name'); // eg: "my-branch"
@@ -29,7 +27,7 @@ function initBranchSelector() {
document.querySelector<HTMLInputElement>(selectedHiddenSelector).value = selectedValue;
elSelectBranch.querySelector('.text-branch-name').textContent = selectedText;
}
- });
+ }));
}
function initRepoIssueDue() {
@@ -50,5 +48,5 @@ export function initRepoIssueSidebar() {
initRepoIssueDue();
// init the combo list: a dropdown for selecting items, and a list for showing selected items and related actions
- queryElems<HTMLElement>(document, '.issue-sidebar-combo', (el) => initIssueSidebarComboList(el));
+ queryElems<HTMLElement>(document, '.issue-sidebar-combo', (el) => new IssueSidebarComboList(el).init());
}
diff --git a/web_src/js/features/repo-issue.ts b/web_src/js/features/repo-issue.ts
index a9dda39a7f..49e8fc40a2 100644
--- a/web_src/js/features/repo-issue.ts
+++ b/web_src/js/features/repo-issue.ts
@@ -1,5 +1,4 @@
-import $ from 'jquery';
-import {htmlEscape} from 'escape-goat';
+import {html, htmlEscape} from '../utils/html.ts';
import {createTippy, showTemporaryTooltip} from '../modules/tippy.ts';
import {
addDelegatedEventListener,
@@ -18,38 +17,40 @@ import {showErrorToast} from '../modules/toast.ts';
import {initRepoIssueSidebar} from './repo-issue-sidebar.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
import {ignoreAreYouSure} from '../vendor/jquery.are-you-sure.ts';
+import {registerGlobalInitFunc} from '../modules/observer.ts';
const {appSubUrl} = window.config;
-export function initRepoIssueSidebarList() {
+export function initRepoIssueSidebarDependency() {
+ const elDropdown = document.querySelector('#new-dependency-drop-list');
+ if (!elDropdown) return;
+
const issuePageInfo = parseIssuePageInfo();
- const crossRepoSearch = $('#crossRepoSearch').val();
+ const crossRepoSearch = elDropdown.getAttribute('data-issue-cross-repo-search');
let issueSearchUrl = `${issuePageInfo.repoLink}/issues/search?q={query}&type=${issuePageInfo.issueDependencySearchType}`;
if (crossRepoSearch === 'true') {
issueSearchUrl = `${appSubUrl}/issues/search?q={query}&priority_repo_id=${issuePageInfo.repoId}&type=${issuePageInfo.issueDependencySearchType}`;
}
- fomanticQuery('#new-dependency-drop-list').dropdown({
+ fomanticQuery(elDropdown).dropdown({
fullTextSearch: true,
apiSettings: {
+ cache: false,
+ rawResponse: true,
url: issueSearchUrl,
- onResponse(response) {
- const filteredResponse = {success: true, results: []};
- const currIssueId = $('#new-dependency-drop-list').data('issue-id');
+ onResponse(response: any) {
+ const filteredResponse = {success: true, results: [] as Array<Record<string, any>>};
+ const currIssueId = elDropdown.getAttribute('data-issue-id');
// Parse the response from the api to work with our dropdown
- $.each(response, (_i, issue) => {
+ for (const issue of response) {
// Don't list current issue in the dependency list.
- if (issue.id === currIssueId) {
- return;
- }
+ if (String(issue.id) === currIssueId) continue;
filteredResponse.results.push({
- name: `<div class="gt-ellipsis">#${issue.number} ${htmlEscape(issue.title)}</div>
-<div class="text small tw-break-anywhere">${htmlEscape(issue.repository.full_name)}</div>`,
value: issue.id,
+ name: html`<div class="gt-ellipsis">#${issue.number} ${issue.title}</div><div class="text small tw-break-anywhere">${issue.repository.full_name}</div>`,
});
- });
+ }
return filteredResponse;
},
- cache: false,
},
});
}
@@ -181,24 +182,6 @@ export function initRepoIssueCommentDelete() {
});
}
-export function initRepoIssueDependencyDelete() {
- // Delete Issue dependency
- $(document).on('click', '.delete-dependency-button', (e) => {
- const id = e.currentTarget.getAttribute('data-id');
- const type = e.currentTarget.getAttribute('data-type');
-
- $('.remove-dependency').modal({
- closable: false,
- duration: 200,
- onApprove: () => {
- $('#removeDependencyID').val(id);
- $('#dependencyType').val(type);
- $('#removeDependencyForm').trigger('submit');
- },
- }).modal('show');
- });
-}
-
export function initRepoIssueCodeCommentCancel() {
// Cancel inline code comment
document.addEventListener('click', (e: DOMEvent<MouseEvent>) => {
@@ -214,59 +197,6 @@ export function initRepoIssueCodeCommentCancel() {
});
}
-export function initRepoPullRequestUpdate() {
- // Pull Request update button
- const pullUpdateButton = document.querySelector('.update-button > button');
- if (!pullUpdateButton) return;
-
- pullUpdateButton.addEventListener('click', async function (e) {
- e.preventDefault();
- const redirect = this.getAttribute('data-redirect');
- this.classList.add('is-loading');
- let response: Response;
- try {
- response = await POST(this.getAttribute('data-do'));
- } catch (error) {
- console.error(error);
- } finally {
- this.classList.remove('is-loading');
- }
- let data: Record<string, any>;
- try {
- data = await response?.json(); // the response is probably not a JSON
- } catch (error) {
- console.error(error);
- }
- if (data?.redirect) {
- window.location.href = data.redirect;
- } else if (redirect) {
- window.location.href = redirect;
- } else {
- window.location.reload();
- }
- });
-
- $('.update-button > .dropdown').dropdown({
- onChange(_text, _value, $choice) {
- const choiceEl = $choice[0];
- const url = choiceEl.getAttribute('data-do');
- if (url) {
- const buttonText = pullUpdateButton.querySelector('.button-text');
- if (buttonText) {
- buttonText.textContent = choiceEl.textContent;
- }
- pullUpdateButton.setAttribute('data-do', url);
- }
- },
- });
-}
-
-export function initRepoPullRequestMergeInstruction() {
- $('.show-instruction').on('click', () => {
- toggleElem($('.instruct-content'));
- });
-}
-
export function initRepoPullRequestAllowMaintainerEdit() {
const wrapper = document.querySelector('#allow-edits-from-maintainers');
if (!wrapper) return;
@@ -293,54 +223,8 @@ export function initRepoPullRequestAllowMaintainerEdit() {
});
}
-export function initRepoIssueReferenceRepositorySearch() {
- $('.issue_reference_repository_search')
- .dropdown({
- apiSettings: {
- url: `${appSubUrl}/repo/search?q={query}&limit=20`,
- onResponse(response) {
- const filteredResponse = {success: true, results: []};
- $.each(response.data, (_r, repo) => {
- filteredResponse.results.push({
- name: htmlEscape(repo.repository.full_name),
- value: repo.repository.full_name,
- });
- });
- return filteredResponse;
- },
- cache: false,
- },
- onChange(_value, _text, $choice) {
- const $form = $choice.closest('form');
- if (!$form.length) return;
-
- $form[0].setAttribute('action', `${appSubUrl}/${_text}/issues/new`);
- },
- fullTextSearch: true,
- });
-}
-
-export function initRepoIssueWipTitle() {
- $('.title_wip_desc > a').on('click', (e) => {
- e.preventDefault();
-
- const $issueTitle = $('#issue_title');
- $issueTitle.trigger('focus');
- const value = ($issueTitle.val() as string).trim().toUpperCase();
-
- const wipPrefixes = $('.title_wip_desc').data('wip-prefixes');
- for (const prefix of wipPrefixes) {
- if (value.startsWith(prefix.toUpperCase())) {
- return;
- }
- }
-
- $issueTitle.val(`${wipPrefixes[0]} ${$issueTitle.val()}`);
- });
-}
-
export function initRepoIssueComments() {
- if (!$('.repository.view.issue .timeline').length) return;
+ if (!document.querySelector('.repository.view.issue .timeline')) return;
document.addEventListener('click', (e: DOMEvent<MouseEvent>) => {
const urlTarget = document.querySelector(':target');
@@ -352,15 +236,15 @@ export function initRepoIssueComments() {
if (!/^(issue|pull)(comment)?-\d+$/.test(urlTargetId)) return;
if (!e.target.closest(`#${urlTargetId}`)) {
- const scrollPosition = $(window).scrollTop();
- window.location.hash = '';
- $(window).scrollTop(scrollPosition);
+ // if the user clicks outside the comment, remove the hash from the url
+ // use empty hash and state to avoid scrolling
+ window.location.hash = ' ';
window.history.pushState(null, null, ' ');
}
});
}
-export async function handleReply(el) {
+export async function handleReply(el: HTMLElement) {
const form = el.closest('.comment-code-cloud').querySelector('.comment-form');
const textarea = form.querySelector('textarea');
@@ -373,25 +257,13 @@ export async function handleReply(el) {
export function initRepoPullRequestReview() {
if (window.location.hash && window.location.hash.startsWith('#issuecomment-')) {
- // set scrollRestoration to 'manual' when there is a hash in url, so that the scroll position will not be remembered after refreshing
- if (window.history.scrollRestoration !== 'manual') {
- window.history.scrollRestoration = 'manual';
- }
const commentDiv = document.querySelector(window.location.hash);
if (commentDiv) {
// get the name of the parent id
const groupID = commentDiv.closest('div[id^="code-comments-"]')?.getAttribute('id');
if (groupID && groupID.startsWith('code-comments-')) {
const id = groupID.slice(14);
- const ancestorDiffBox = commentDiv.closest('.diff-file-box');
- // on pages like conversation, there is no diff header
- const diffHeader = ancestorDiffBox?.querySelector('.diff-file-header');
-
- // offset is for scrolling
- let offset = 30;
- if (diffHeader) {
- offset += $('.diff-detail-box').outerHeight() + $(diffHeader).outerHeight();
- }
+ const ancestorDiffBox = commentDiv.closest<HTMLElement>('.diff-file-box');
hideElem(`#show-outdated-${id}`);
showElem(`#code-comments-${id}, #code-preview-${id}, #hide-outdated-${id}`);
@@ -399,48 +271,45 @@ export function initRepoPullRequestReview() {
if (ancestorDiffBox?.getAttribute('data-folded') === 'true') {
setFileFolding(ancestorDiffBox, ancestorDiffBox.querySelector('.fold-file'), false);
}
-
- window.scrollTo({
- top: $(commentDiv).offset().top - offset,
- behavior: 'instant',
- });
}
+ // set scrollRestoration to 'manual' when there is a hash in url, so that the scroll position will not be remembered after refreshing
+ if (window.history.scrollRestoration !== 'manual') window.history.scrollRestoration = 'manual';
+ // wait for a while because some elements (eg: image, editor, etc.) may change the viewport's height.
+ setTimeout(() => commentDiv.scrollIntoView({block: 'start'}), 100);
}
}
- $(document).on('click', '.show-outdated', function (e) {
+ addDelegatedEventListener(document, 'click', '.show-outdated', (el, e) => {
e.preventDefault();
- const id = this.getAttribute('data-comment');
- hideElem(this);
+ const id = el.getAttribute('data-comment');
+ hideElem(el);
showElem(`#code-comments-${id}`);
showElem(`#code-preview-${id}`);
showElem(`#hide-outdated-${id}`);
});
- $(document).on('click', '.hide-outdated', function (e) {
+ addDelegatedEventListener(document, 'click', '.hide-outdated', (el, e) => {
e.preventDefault();
- const id = this.getAttribute('data-comment');
- hideElem(this);
+ const id = el.getAttribute('data-comment');
+ hideElem(el);
hideElem(`#code-comments-${id}`);
hideElem(`#code-preview-${id}`);
showElem(`#show-outdated-${id}`);
});
- $(document).on('click', 'button.comment-form-reply', async function (e) {
+ addDelegatedEventListener(document, 'click', 'button.comment-form-reply', (el, e) => {
e.preventDefault();
- await handleReply(this);
+ handleReply(el);
});
// The following part is only for diff views
- if (!$('.repository.pull.diff').length) return;
-
- const $reviewBtn = $('.js-btn-review');
- const $panel = $reviewBtn.parent().find('.review-box-panel');
- const $closeBtn = $panel.find('.close');
+ if (!document.querySelector('.repository.pull.diff')) return;
- if ($reviewBtn.length && $panel.length) {
- const tippy = createTippy($reviewBtn[0], {
- content: $panel[0],
+ const elReviewBtn = document.querySelector('.js-btn-review');
+ const elReviewPanel = document.querySelector('.review-box-panel.tippy-target');
+ if (elReviewBtn && elReviewPanel) {
+ const tippy = createTippy(elReviewBtn, {
+ content: elReviewPanel,
theme: 'default',
placement: 'bottom',
trigger: 'click',
@@ -448,11 +317,7 @@ export function initRepoPullRequestReview() {
interactive: true,
hideOnClick: true,
});
-
- $closeBtn.on('click', (e) => {
- e.preventDefault();
- tippy.hide();
- });
+ elReviewPanel.querySelector('.close').addEventListener('click', () => tippy.hide());
}
addDelegatedEventListener(document, 'click', '.add-code-comment', async (el, e) => {
@@ -493,43 +358,79 @@ export function initRepoPullRequestReview() {
}
export function initRepoIssueReferenceIssue() {
+ const elDropdown = document.querySelector('.issue_reference_repository_search');
+ if (!elDropdown) return;
+ const form = elDropdown.closest('form');
+ fomanticQuery(elDropdown).dropdown({
+ fullTextSearch: true,
+ apiSettings: {
+ cache: false,
+ rawResponse: true,
+ url: `${appSubUrl}/repo/search?q={query}&limit=20`,
+ onResponse(response: any) {
+ const filteredResponse = {success: true, results: [] as Array<Record<string, any>>};
+ for (const repo of response.data) {
+ filteredResponse.results.push({
+ name: htmlEscape(repo.repository.full_name),
+ value: repo.repository.full_name,
+ });
+ }
+ return filteredResponse;
+ },
+ },
+ onChange(_value: string, _text: string, _$choice: any) {
+ form.setAttribute('action', `${appSubUrl}/${_text}/issues/new`);
+ },
+ });
+
// Reference issue
- $(document).on('click', '.reference-issue', function (e) {
- const target = this.getAttribute('data-target');
+ addDelegatedEventListener(document, 'click', '.reference-issue', (el, e) => {
+ e.preventDefault();
+ const target = el.getAttribute('data-target');
const content = document.querySelector(`#${target}`)?.textContent ?? '';
- const poster = this.getAttribute('data-poster-username');
- const reference = toAbsoluteUrl(this.getAttribute('data-reference'));
- const modalSelector = this.getAttribute('data-modal');
+ const poster = el.getAttribute('data-poster-username');
+ const reference = toAbsoluteUrl(el.getAttribute('data-reference'));
+ const modalSelector = el.getAttribute('data-modal');
const modal = document.querySelector(modalSelector);
- const textarea = modal.querySelector('textarea[name="content"]');
+ const textarea = modal.querySelector<HTMLTextAreaElement>('textarea[name="content"]');
textarea.value = `${content}\n\n_Originally posted by @${poster} in ${reference}_`;
- $(modal).modal('show');
- e.preventDefault();
+ fomanticQuery(modal).modal('show');
});
}
+export function initRepoIssueWipNewTitle() {
+ // Toggle WIP for new PR
+ queryElems(document, '.title_wip_desc > a', (el) => el.addEventListener('click', (e) => {
+ e.preventDefault();
+ const wipPrefixes = JSON.parse(el.closest('.title_wip_desc').getAttribute('data-wip-prefixes'));
+ const titleInput = document.querySelector<HTMLInputElement>('#issue_title');
+ const titleValue = titleInput.value;
+ for (const prefix of wipPrefixes) {
+ if (titleValue.startsWith(prefix.toUpperCase())) {
+ return;
+ }
+ }
+ titleInput.value = `${wipPrefixes[0]} ${titleValue}`;
+ }));
+}
+
export function initRepoIssueWipToggle() {
- // Toggle WIP
- $('.toggle-wip a, .toggle-wip button').on('click', async (e) => {
+ // Toggle WIP for existing PR
+ registerGlobalInitFunc('initPullRequestWipToggle', (toggleWip) => toggleWip.addEventListener('click', async (e) => {
e.preventDefault();
- const toggleWip = e.currentTarget.closest('.toggle-wip');
const title = toggleWip.getAttribute('data-title');
const wipPrefix = toggleWip.getAttribute('data-wip-prefix');
const updateUrl = toggleWip.getAttribute('data-update-url');
- try {
- const params = new URLSearchParams();
- params.append('title', title?.startsWith(wipPrefix) ? title.slice(wipPrefix.length).trim() : `${wipPrefix.trim()} ${title}`);
-
- const response = await POST(updateUrl, {data: params});
- if (!response.ok) {
- throw new Error('Failed to toggle WIP status');
- }
- window.location.reload();
- } catch (error) {
- console.error(error);
+ const params = new URLSearchParams();
+ params.append('title', title?.startsWith(wipPrefix) ? title.slice(wipPrefix.length).trim() : `${wipPrefix.trim()} ${title}`);
+ const response = await POST(updateUrl, {data: params});
+ if (!response.ok) {
+ showErrorToast(`Failed to toggle 'work in progress' status`);
+ return;
}
- });
+ window.location.reload();
+ }));
}
export function initRepoIssueTitleEdit() {
@@ -602,11 +503,11 @@ export function initRepoIssueBranchSelect() {
});
}
-async function initSingleCommentEditor($commentForm) {
+async function initSingleCommentEditor(commentForm: HTMLFormElement) {
// pages:
// * normal new issue/pr page: no status-button, no comment-button (there is only a normal submit button which can submit empty content)
// * issue/pr view page: with comment form, has status-button and comment-button
- const editor = await initComboMarkdownEditor($commentForm[0].querySelector('.combo-markdown-editor'));
+ const editor = await initComboMarkdownEditor(commentForm.querySelector('.combo-markdown-editor'));
const statusButton = document.querySelector<HTMLButtonElement>('#status-button');
const commentButton = document.querySelector<HTMLButtonElement>('#comment-button');
const syncUiState = () => {
@@ -624,27 +525,27 @@ async function initSingleCommentEditor($commentForm) {
syncUiState();
}
-function initIssueTemplateCommentEditors($commentForm) {
+function initIssueTemplateCommentEditors(commentForm: HTMLFormElement) {
// pages:
// * new issue with issue template
- const $comboFields = $commentForm.find('.combo-editor-dropzone');
+ const comboFields = commentForm.querySelectorAll<HTMLElement>('.combo-editor-dropzone');
const initCombo = async (elCombo: HTMLElement) => {
- const $formField = $(elCombo.querySelector('.form-field-real'));
+ const fieldTextarea = elCombo.querySelector<HTMLTextAreaElement>('.form-field-real');
const dropzoneContainer = elCombo.querySelector<HTMLElement>('.form-field-dropzone');
const markdownEditor = elCombo.querySelector<HTMLElement>('.combo-markdown-editor');
const editor = await initComboMarkdownEditor(markdownEditor);
- editor.container.addEventListener(ComboMarkdownEditor.EventEditorContentChanged, () => $formField.val(editor.value()));
+ editor.container.addEventListener(ComboMarkdownEditor.EventEditorContentChanged, () => fieldTextarea.value = editor.value());
- $formField.on('focus', async () => {
+ fieldTextarea.addEventListener('focus', async () => {
// deactivate all markdown editors
- showElem($commentForm.find('.combo-editor-dropzone .form-field-real'));
- hideElem($commentForm.find('.combo-editor-dropzone .combo-markdown-editor'));
- hideElem($commentForm.find('.combo-editor-dropzone .form-field-dropzone'));
+ showElem(commentForm.querySelectorAll('.combo-editor-dropzone .form-field-real'));
+ hideElem(commentForm.querySelectorAll('.combo-editor-dropzone .combo-markdown-editor'));
+ hideElem(commentForm.querySelectorAll('.combo-editor-dropzone .form-field-dropzone'));
// activate this markdown editor
- hideElem($formField);
+ hideElem(fieldTextarea);
showElem(markdownEditor);
showElem(dropzoneContainer);
@@ -653,21 +554,21 @@ function initIssueTemplateCommentEditors($commentForm) {
});
};
- for (const el of $comboFields) {
+ for (const el of comboFields) {
initCombo(el);
}
}
export function initRepoCommentFormAndSidebar() {
- const $commentForm = $('.comment.form');
- if (!$commentForm.length) return;
+ const commentForm = document.querySelector<HTMLFormElement>('.comment.form');
+ if (!commentForm) return;
- if ($commentForm.find('.field.combo-editor-dropzone').length) {
+ if (commentForm.querySelector('.field.combo-editor-dropzone')) {
// at the moment, if a form has multiple combo-markdown-editors, it must be an issue template form
- initIssueTemplateCommentEditors($commentForm);
- } else if ($commentForm.find('.combo-markdown-editor').length) {
+ initIssueTemplateCommentEditors(commentForm);
+ } else if (commentForm.querySelector('.combo-markdown-editor')) {
// it's quite unclear about the "comment form" elements, sometimes it's for issue comment, sometimes it's for file editor/uploader message
- initSingleCommentEditor($commentForm);
+ initSingleCommentEditor(commentForm);
}
initRepoIssueSidebar();
diff --git a/web_src/js/features/repo-legacy.ts b/web_src/js/features/repo-legacy.ts
index 33f02be865..249d181b25 100644
--- a/web_src/js/features/repo-legacy.ts
+++ b/web_src/js/features/repo-legacy.ts
@@ -1,30 +1,28 @@
+import {registerGlobalInitFunc} from '../modules/observer.ts';
import {
initRepoCommentFormAndSidebar,
initRepoIssueBranchSelect, initRepoIssueCodeCommentCancel, initRepoIssueCommentDelete,
- initRepoIssueComments, initRepoIssueDependencyDelete, initRepoIssueReferenceIssue,
- initRepoIssueTitleEdit, initRepoIssueWipToggle,
- initRepoPullRequestUpdate,
+ initRepoIssueComments, initRepoIssueReferenceIssue,
+ initRepoIssueTitleEdit, initRepoIssueWipNewTitle, initRepoIssueWipToggle,
} from './repo-issue.ts';
import {initUnicodeEscapeButton} from './repo-unicode-escape.ts';
import {initRepoCloneButtons} from './repo-common.ts';
import {initCitationFileCopyContent} from './citation.ts';
import {initCompLabelEdit} from './comp/LabelEdit.ts';
-import {initRepoDiffConversationNav} from './repo-diff.ts';
import {initCompReactionSelector} from './comp/ReactionSelector.ts';
import {initRepoSettings} from './repo-settings.ts';
-import {initRepoPullRequestMergeForm} from './repo-issue-pr-form.ts';
-import {initRepoPullRequestCommitStatus} from './repo-issue-pr-status.ts';
import {hideElem, queryElemChildren, queryElems, showElem} from '../utils/dom.ts';
import {initRepoIssueCommentEdit} from './repo-issue-edit.ts';
import {initRepoMilestone} from './repo-milestone.ts';
import {initRepoNew} from './repo-new.ts';
import {createApp} from 'vue';
import RepoBranchTagSelector from '../components/RepoBranchTagSelector.vue';
+import {initRepoPullMergeBox} from './repo-issue-pull.ts';
-function initRepoBranchTagSelector(selector: string) {
- for (const elRoot of document.querySelectorAll(selector)) {
+function initRepoBranchTagSelector() {
+ registerGlobalInitFunc('initRepoBranchTagSelector', async (elRoot: HTMLInputElement) => {
createApp(RepoBranchTagSelector, {elRoot}).mount(elRoot);
- }
+ });
}
export function initBranchSelectorTabs() {
@@ -43,7 +41,7 @@ export function initRepository() {
const pageContent = document.querySelector('.page-content.repository');
if (!pageContent) return;
- initRepoBranchTagSelector('.js-branch-tag-selector');
+ initRepoBranchTagSelector();
initRepoCommentFormAndSidebar();
// Labels
@@ -54,6 +52,7 @@ export function initRepository() {
initRepoCloneButtons();
initCitationFileCopyContent();
initRepoSettings();
+ initRepoIssueWipNewTitle();
// Issues
if (pageContent.matches('.page-content.repository.view.issue')) {
@@ -64,17 +63,13 @@ export function initRepository() {
initRepoIssueWipToggle();
initRepoIssueComments();
- initRepoDiffConversationNav();
initRepoIssueReferenceIssue();
initRepoIssueCommentDelete();
- initRepoIssueDependencyDelete();
initRepoIssueCodeCommentCancel();
- initRepoPullRequestUpdate();
initCompReactionSelector();
- initRepoPullRequestMergeForm();
- initRepoPullRequestCommitStatus();
+ registerGlobalInitFunc('initRepoPullMergeBox', initRepoPullMergeBox);
}
initUnicodeEscapeButton();
diff --git a/web_src/js/features/repo-migrate.ts b/web_src/js/features/repo-migrate.ts
index b75289feec..0788f83215 100644
--- a/web_src/js/features/repo-migrate.ts
+++ b/web_src/js/features/repo-migrate.ts
@@ -1,11 +1,11 @@
-import {hideElem, showElem} from '../utils/dom.ts';
+import {hideElem, showElem, type DOMEvent} from '../utils/dom.ts';
import {GET, POST} from '../modules/fetch.ts';
export function initRepoMigrationStatusChecker() {
const repoMigrating = document.querySelector('#repo_migrating');
if (!repoMigrating) return;
- document.querySelector('#repo_migrating_retry')?.addEventListener('click', doMigrationRetry);
+ document.querySelector<HTMLButtonElement>('#repo_migrating_retry')?.addEventListener('click', doMigrationRetry);
const repoLink = repoMigrating.getAttribute('data-migrating-repo-link');
@@ -55,7 +55,7 @@ export function initRepoMigrationStatusChecker() {
syncTaskStatus(); // no await
}
-async function doMigrationRetry(e) {
+async function doMigrationRetry(e: DOMEvent<MouseEvent>) {
await POST(e.target.getAttribute('data-migrating-task-retry-url'));
window.location.reload();
}
diff --git a/web_src/js/features/repo-migration.ts b/web_src/js/features/repo-migration.ts
index fb9c822f98..4914e47267 100644
--- a/web_src/js/features/repo-migration.ts
+++ b/web_src/js/features/repo-migration.ts
@@ -1,4 +1,5 @@
import {hideElem, showElem, toggleElem} from '../utils/dom.ts';
+import {sanitizeRepoName} from './repo-common.ts';
const service = document.querySelector<HTMLInputElement>('#service_type');
const user = document.querySelector<HTMLInputElement>('#auth_username');
@@ -25,13 +26,19 @@ export function initRepoMigration() {
});
lfs?.addEventListener('change', setLFSSettingsVisibility);
- const cloneAddr = document.querySelector<HTMLInputElement>('#clone_addr');
- cloneAddr?.addEventListener('change', () => {
- const repoName = document.querySelector<HTMLInputElement>('#repo_name');
- if (cloneAddr.value && !repoName?.value) { // Only modify if repo_name input is blank
- repoName.value = /^(.*\/)?((.+?)(\.git)?)$/.exec(cloneAddr.value)[3];
- }
- });
+ const elCloneAddr = document.querySelector<HTMLInputElement>('#clone_addr');
+ const elRepoName = document.querySelector<HTMLInputElement>('#repo_name');
+ if (elCloneAddr && elRepoName) {
+ let repoNameChanged = false;
+ elRepoName.addEventListener('input', () => {repoNameChanged = true});
+ elCloneAddr.addEventListener('input', () => {
+ if (repoNameChanged) return;
+ let repoNameFromUrl = elCloneAddr.value.split(/[?#]/)[0];
+ repoNameFromUrl = /^(.*\/)?((.+?)\/?)$/.exec(repoNameFromUrl)[3];
+ repoNameFromUrl = repoNameFromUrl.split(/[?#]/)[0];
+ elRepoName.value = sanitizeRepoName(repoNameFromUrl);
+ });
+ }
}
function checkAuth() {
diff --git a/web_src/js/features/repo-new.ts b/web_src/js/features/repo-new.ts
index 8a77a77b4a..e2aa13f490 100644
--- a/web_src/js/features/repo-new.ts
+++ b/web_src/js/features/repo-new.ts
@@ -1,11 +1,14 @@
-import {hideElem, showElem, toggleElem} from '../utils/dom.ts';
-import {htmlEscape} from 'escape-goat';
+import {hideElem, querySingleVisibleElem, showElem, toggleElem} from '../utils/dom.ts';
+import {htmlEscape} from '../utils/html.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
+import {sanitizeRepoName} from './repo-common.ts';
const {appSubUrl} = window.config;
function initRepoNewTemplateSearch(form: HTMLFormElement) {
- const inputRepoOwnerUid = form.querySelector<HTMLInputElement>('#uid');
+ const elSubmitButton = querySingleVisibleElem<HTMLInputElement>(form, '.ui.primary.button');
+ const elCreateRepoErrorMessage = form.querySelector('#create-repo-error-message');
+ const elRepoOwnerDropdown = form.querySelector('#repo_owner_dropdown');
const elRepoTemplateDropdown = form.querySelector<HTMLInputElement>('#repo_template_search');
const inputRepoTemplate = form.querySelector<HTMLInputElement>('#repo_template');
const elTemplateUnits = form.querySelector('#template_units');
@@ -18,12 +21,24 @@ function initRepoNewTemplateSearch(form: HTMLFormElement) {
inputRepoTemplate.addEventListener('change', checkTemplate);
checkTemplate();
- const $dropdown = fomanticQuery(elRepoTemplateDropdown);
+ const $repoOwnerDropdown = fomanticQuery(elRepoOwnerDropdown);
+ const $repoTemplateDropdown = fomanticQuery(elRepoTemplateDropdown);
const onChangeOwner = function () {
- $dropdown.dropdown('setting', {
+ const ownerId = $repoOwnerDropdown.dropdown('get value');
+ const $ownerItem = $repoOwnerDropdown.dropdown('get item', ownerId);
+ hideElem(elCreateRepoErrorMessage);
+ elSubmitButton.disabled = false;
+ if ($ownerItem?.length) {
+ const elOwnerItem = $ownerItem[0];
+ elCreateRepoErrorMessage.textContent = elOwnerItem.getAttribute('data-create-repo-disallowed-prompt') ?? '';
+ const hasError = Boolean(elCreateRepoErrorMessage.textContent);
+ toggleElem(elCreateRepoErrorMessage, hasError);
+ elSubmitButton.disabled = hasError;
+ }
+ $repoTemplateDropdown.dropdown('setting', {
apiSettings: {
- url: `${appSubUrl}/repo/search?q={query}&template=true&priority_owner_id=${inputRepoOwnerUid.value}`,
- onResponse(response) {
+ url: `${appSubUrl}/repo/search?q={query}&template=true&priority_owner_id=${ownerId}`,
+ onResponse(response: any) {
const results = [];
results.push({name: '', value: ''}); // empty item means not using template
for (const tmplRepo of response.data) {
@@ -32,14 +47,14 @@ function initRepoNewTemplateSearch(form: HTMLFormElement) {
value: String(tmplRepo.repository.id),
});
}
- $dropdown.fomanticExt.onResponseKeepSelectedItem($dropdown, inputRepoTemplate.value);
+ $repoTemplateDropdown.fomanticExt.onResponseKeepSelectedItem($repoTemplateDropdown, inputRepoTemplate.value);
return {results};
},
cache: false,
},
});
};
- inputRepoOwnerUid.addEventListener('change', onChangeOwner);
+ $repoOwnerDropdown.dropdown('setting', 'onChange', onChangeOwner);
onChangeOwner();
}
@@ -66,7 +81,7 @@ export function initRepoNew() {
let help = form.querySelector(`.help[data-help-for-repo-name="${CSS.escape(inputRepoName.value)}"]`);
if (!help) help = form.querySelector(`.help[data-help-for-repo-name=""]`);
showElem(help);
- const repoNamePreferPrivate = {'.profile': false, '.profile-private': true};
+ const repoNamePreferPrivate: Record<string, boolean> = {'.profile': false, '.profile-private': true};
const preferPrivate = repoNamePreferPrivate[inputRepoName.value];
// inputPrivate might be disabled because site admin "force private"
if (preferPrivate !== undefined && !inputPrivate.closest('.disabled, [disabled]')) {
@@ -74,6 +89,10 @@ export function initRepoNew() {
}
};
inputRepoName.addEventListener('input', updateUiRepoName);
+ inputRepoName.addEventListener('change', () => {
+ inputRepoName.value = sanitizeRepoName(inputRepoName.value);
+ updateUiRepoName();
+ });
updateUiRepoName();
initRepoNewTemplateSearch(form);
diff --git a/web_src/js/features/repo-projects.ts b/web_src/js/features/repo-projects.ts
index 11f5c19c8d..ad0feb6101 100644
--- a/web_src/js/features/repo-projects.ts
+++ b/web_src/js/features/repo-projects.ts
@@ -2,8 +2,9 @@ import {contrastColor} from '../utils/color.ts';
import {createSortable} from '../modules/sortable.ts';
import {POST, request} from '../modules/fetch.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
-import {queryElemChildren, queryElems} from '../utils/dom.ts';
+import {queryElemChildren, queryElems, toggleElem} from '../utils/dom.ts';
import type {SortableEvent} from 'sortablejs';
+import {toggleFullScreen} from '../utils.ts';
function updateIssueCount(card: HTMLElement): void {
const parent = card.parentElement;
@@ -34,8 +35,8 @@ async function moveIssue({item, from, to, oldIndex}: SortableEvent): Promise<voi
}
async function initRepoProjectSortable(): Promise<void> {
- // the HTML layout is: #project-board > .board > .project-column .cards > .issue-card
- const mainBoard = document.querySelector('#project-board > .board.sortable');
+ // the HTML layout is: #project-board.board > .project-column .cards > .issue-card
+ const mainBoard = document.querySelector('#project-board');
let boardColumns = mainBoard.querySelectorAll<HTMLElement>('.project-column');
createSortable(mainBoard, {
group: 'project-column',
@@ -113,7 +114,6 @@ function initRepoProjectColumnEdit(writableProjectBoard: Element): void {
window.location.reload(); // newly added column, need to reload the page
return;
}
- fomanticQuery(elModal).modal('hide');
// update the newly saved column title and color in the project board (to avoid reload)
const elEditButton = writableProjectBoard.querySelector<HTMLButtonElement>(`.show-project-column-modal-edit[${attrDataColumnId}="${columnId}"]`);
@@ -133,13 +133,32 @@ function initRepoProjectColumnEdit(writableProjectBoard: Element): void {
elBoardColumn.style.removeProperty('color');
queryElemChildren<HTMLElement>(elBoardColumn, '.divider', (divider) => divider.style.removeProperty('color'));
}
+
+ fomanticQuery(elModal).modal('hide');
} finally {
elForm.classList.remove('is-loading');
}
});
}
+function initRepoProjectToggleFullScreen(): void {
+ const enterFullscreenBtn = document.querySelector('.screen-full');
+ const exitFullscreenBtn = document.querySelector('.screen-normal');
+ if (!enterFullscreenBtn || !exitFullscreenBtn) return;
+
+ const toggleFullscreenState = (isFullScreen: boolean) => {
+ toggleFullScreen('.projects-view', isFullScreen);
+ toggleElem(enterFullscreenBtn, !isFullScreen);
+ toggleElem(exitFullscreenBtn, isFullScreen);
+ };
+
+ enterFullscreenBtn.addEventListener('click', () => toggleFullscreenState(true));
+ exitFullscreenBtn.addEventListener('click', () => toggleFullscreenState(false));
+}
+
export function initRepoProject(): void {
+ initRepoProjectToggleFullScreen();
+
const writableProjectBoard = document.querySelector('#project-board[data-project-borad-writable="true"]');
if (!writableProjectBoard) return;
diff --git a/web_src/js/features/repo-settings.ts b/web_src/js/features/repo-settings.ts
index 1d2d447205..be1821664f 100644
--- a/web_src/js/features/repo-settings.ts
+++ b/web_src/js/features/repo-settings.ts
@@ -1,9 +1,9 @@
-import $ from 'jquery';
import {minimatch} from 'minimatch';
import {createMonaco} from './codeeditor.ts';
-import {onInputDebounce, queryElems, toggleElem} from '../utils/dom.ts';
+import {onInputDebounce, queryElems, toggleClass, toggleElem} from '../utils/dom.ts';
import {POST} from '../modules/fetch.ts';
import {initRepoSettingsBranchesDrag} from './repo-settings-branches.ts';
+import {fomanticQuery} from '../modules/fomantic/base.ts';
const {appSubUrl, csrfToken} = window.config;
@@ -11,11 +11,12 @@ function initRepoSettingsCollaboration() {
// Change collaborator access mode
for (const dropdownEl of queryElems(document, '.page-content.repository .ui.dropdown.access-mode')) {
const textEl = dropdownEl.querySelector(':scope > .text');
- $(dropdownEl).dropdown({
- async action(text, value) {
+ const $dropdown = fomanticQuery(dropdownEl);
+ $dropdown.dropdown({
+ async action(text: string, value: string) {
dropdownEl.classList.add('is-loading', 'loading-icon-2px');
const lastValue = dropdownEl.getAttribute('data-last-value');
- $(dropdownEl).dropdown('hide');
+ $dropdown.dropdown('hide');
try {
const uid = dropdownEl.getAttribute('data-uid');
await POST(dropdownEl.getAttribute('data-url'), {data: new URLSearchParams({uid, 'mode': value})});
@@ -32,9 +33,9 @@ function initRepoSettingsCollaboration() {
// set to the really selected value, defer to next tick to make sure `action` has finished
// its work because the calling order might be onHide -> action
setTimeout(() => {
- const $item = $(dropdownEl).dropdown('get item', dropdownEl.getAttribute('data-last-value'));
+ const $item = $dropdown.dropdown('get item', dropdownEl.getAttribute('data-last-value'));
if ($item) {
- $(dropdownEl).dropdown('set selected', dropdownEl.getAttribute('data-last-value'));
+ $dropdown.dropdown('set selected', dropdownEl.getAttribute('data-last-value'));
} else {
textEl.textContent = '(none)'; // prevent from misleading users when the access mode is undefined
}
@@ -48,52 +49,52 @@ function initRepoSettingsSearchTeamBox() {
const searchTeamBox = document.querySelector('#search-team-box');
if (!searchTeamBox) return;
- $(searchTeamBox).search({
+ fomanticQuery(searchTeamBox).search({
minCharacters: 2,
+ searchFields: ['name', 'description'],
+ showNoResults: false,
+ rawResponse: true,
apiSettings: {
url: `${appSubUrl}/org/${searchTeamBox.getAttribute('data-org-name')}/teams/-/search?q={query}`,
headers: {'X-Csrf-Token': csrfToken},
- onResponse(response) {
- const items = [];
- $.each(response.data, (_i, item) => {
+ onResponse(response: any) {
+ const items: Array<Record<string, any>> = [];
+ for (const item of response.data) {
items.push({
title: item.name,
description: `${item.permission} access`, // TODO: translate this string
});
- });
-
+ }
return {results: items};
},
},
- searchFields: ['name', 'description'],
- showNoResults: false,
});
}
function initRepoSettingsGitHook() {
- if (!$('.edit.githook').length) return;
+ if (!document.querySelector('.page-content.repository.settings.edit.githook')) return;
const filename = document.querySelector('.hook-filename').textContent;
- createMonaco($('#content')[0] as HTMLTextAreaElement, filename, {language: 'shell'});
+ createMonaco(document.querySelector<HTMLTextAreaElement>('#content'), filename, {language: 'shell'});
}
function initRepoSettingsBranches() {
if (!document.querySelector('.repository.settings.branches')) return;
- for (const el of document.querySelectorAll('.toggle-target-enabled')) {
+ for (const el of document.querySelectorAll<HTMLInputElement>('.toggle-target-enabled')) {
el.addEventListener('change', function () {
const target = document.querySelector(this.getAttribute('data-target'));
target?.classList.toggle('disabled', !this.checked);
});
}
- for (const el of document.querySelectorAll('.toggle-target-disabled')) {
+ for (const el of document.querySelectorAll<HTMLInputElement>('.toggle-target-disabled')) {
el.addEventListener('change', function () {
const target = document.querySelector(this.getAttribute('data-target'));
if (this.checked) target?.classList.add('disabled'); // only disable, do not auto enable
});
}
- document.querySelector('#dismiss_stale_approvals')?.addEventListener('change', function () {
+ document.querySelector<HTMLInputElement>('#dismiss_stale_approvals')?.addEventListener('change', function () {
document.querySelector('#ignore_stale_approvals_box')?.classList.toggle('disabled', this.checked);
});
@@ -107,7 +108,7 @@ function initRepoSettingsBranches() {
let matched = false;
const statusCheck = el.getAttribute('data-status-check');
for (const pattern of validPatterns) {
- if (minimatch(statusCheck, pattern)) {
+ if (minimatch(statusCheck, pattern, {noext: true})) { // https://github.com/go-gitea/gitea/issues/33121 disable extended glob syntax
matched = true;
break;
}
@@ -120,32 +121,23 @@ function initRepoSettingsBranches() {
}
function initRepoSettingsOptions() {
- if ($('.repository.settings.options').length > 0) {
- // Enable or select internal/external wiki system and issue tracker.
- $('.enable-system').on('change', function (this: HTMLInputElement) { // eslint-disable-line @typescript-eslint/no-deprecated
- if (this.checked) {
- $($(this).data('target')).removeClass('disabled');
- if (!$(this).data('context')) $($(this).data('context')).addClass('disabled');
- } else {
- $($(this).data('target')).addClass('disabled');
- if (!$(this).data('context')) $($(this).data('context')).removeClass('disabled');
- }
- });
- $('.enable-system-radio').on('change', function (this: HTMLInputElement) { // eslint-disable-line @typescript-eslint/no-deprecated
- if (this.value === 'false') {
- $($(this).data('target')).addClass('disabled');
- if ($(this).data('context') !== undefined) $($(this).data('context')).removeClass('disabled');
- } else if (this.value === 'true') {
- $($(this).data('target')).removeClass('disabled');
- if ($(this).data('context') !== undefined) $($(this).data('context')).addClass('disabled');
- }
- });
- const $trackerIssueStyleRadios = $('.js-tracker-issue-style');
- $trackerIssueStyleRadios.on('change input', () => {
- const checkedVal = $trackerIssueStyleRadios.filter(':checked').val();
- $('#tracker-issue-style-regex-box').toggleClass('disabled', checkedVal !== 'regexp');
- });
- }
+ const pageContent = document.querySelector('.page-content.repository.settings.options');
+ if (!pageContent) return;
+
+ // Enable or select internal/external wiki system and issue tracker.
+ queryElems<HTMLInputElement>(pageContent, '.enable-system', (el) => el.addEventListener('change', () => {
+ toggleClass(el.getAttribute('data-target'), 'disabled', !el.checked);
+ toggleClass(el.getAttribute('data-context'), 'disabled', el.checked);
+ }));
+ queryElems<HTMLInputElement>(pageContent, '.enable-system-radio', (el) => el.addEventListener('change', () => {
+ toggleClass(el.getAttribute('data-target'), 'disabled', el.value === 'false');
+ toggleClass(el.getAttribute('data-context'), 'disabled', el.value === 'true');
+ }));
+
+ queryElems<HTMLInputElement>(pageContent, '.js-tracker-issue-style', (el) => el.addEventListener('change', () => {
+ const checkedVal = el.value;
+ pageContent.querySelector('#tracker-issue-style-regex-box').classList.toggle('disabled', checkedVal !== 'regexp');
+ }));
}
export function initRepoSettings() {
diff --git a/web_src/js/features/repo-view-file-tree.ts b/web_src/js/features/repo-view-file-tree.ts
new file mode 100644
index 0000000000..f52b64cc51
--- /dev/null
+++ b/web_src/js/features/repo-view-file-tree.ts
@@ -0,0 +1,37 @@
+import {createApp} from 'vue';
+import {toggleElem} from '../utils/dom.ts';
+import {POST} from '../modules/fetch.ts';
+import ViewFileTree from '../components/ViewFileTree.vue';
+import {registerGlobalEventFunc} from '../modules/observer.ts';
+
+const {appSubUrl} = window.config;
+
+async function toggleSidebar(btn: HTMLElement) {
+ const elToggleShow = document.querySelector('.repo-view-file-tree-toggle-show');
+ const elFileTreeContainer = document.querySelector('.repo-view-file-tree-container');
+ const shouldShow = btn.getAttribute('data-toggle-action') === 'show';
+ toggleElem(elFileTreeContainer, shouldShow);
+ toggleElem(elToggleShow, !shouldShow);
+
+ // FIXME: need to remove "full height" style from parent element
+
+ if (!elFileTreeContainer.hasAttribute('data-user-is-signed-in')) return;
+ await POST(`${appSubUrl}/user/settings/update_preferences`, {
+ data: {codeViewShowFileTree: shouldShow},
+ });
+}
+
+export async function initRepoViewFileTree() {
+ const sidebar = document.querySelector<HTMLElement>('.repo-view-file-tree-container');
+ const repoViewContent = document.querySelector('.repo-view-content');
+ if (!sidebar || !repoViewContent) return;
+
+ registerGlobalEventFunc('click', 'onRepoViewFileTreeToggle', toggleSidebar);
+
+ const fileTree = sidebar.querySelector('#view-file-tree');
+ createApp(ViewFileTree, {
+ repoLink: fileTree.getAttribute('data-repo-link'),
+ treePath: fileTree.getAttribute('data-tree-path'),
+ currentRefNameSubURL: fileTree.getAttribute('data-current-ref-name-sub-url'),
+ }).mount(fileTree);
+}
diff --git a/web_src/js/features/repo-wiki.ts b/web_src/js/features/repo-wiki.ts
index 484c628f9f..6ae0947077 100644
--- a/web_src/js/features/repo-wiki.ts
+++ b/web_src/js/features/repo-wiki.ts
@@ -1,8 +1,8 @@
-import {initMarkupContent} from '../markup/content.ts';
import {validateTextareaNonEmpty, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';
import {fomanticMobileScreen} from '../modules/fomantic.ts';
import {POST} from '../modules/fetch.ts';
import type {ComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';
+import {html, htmlRaw} from '../utils/html.ts';
async function initRepoWikiFormEditor() {
const editArea = document.querySelector<HTMLTextAreaElement>('.repository.wiki .combo-markdown-editor textarea');
@@ -31,8 +31,7 @@ async function initRepoWikiFormEditor() {
const response = await POST(editor.previewUrl, {data: formData});
const data = await response.text();
lastContent = newContent;
- previewTarget.innerHTML = `<div class="markup ui segment">${data}</div>`;
- initMarkupContent();
+ previewTarget.innerHTML = html`<div class="render-content markup ui segment">${htmlRaw(data)}</div>`;
} catch (error) {
console.error('Error rendering preview:', error);
} finally {
@@ -70,7 +69,7 @@ async function initRepoWikiFormEditor() {
});
}
-function collapseWikiTocForMobile(collapse) {
+function collapseWikiTocForMobile(collapse: boolean) {
if (collapse) {
document.querySelector('.wiki-content-toc details')?.removeAttribute('open');
}
diff --git a/web_src/js/features/scoped-access-token.ts b/web_src/js/features/scoped-access-token.ts
deleted file mode 100644
index c498d4c011..0000000000
--- a/web_src/js/features/scoped-access-token.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import {createApp} from 'vue';
-
-export async function initScopedAccessTokenCategories() {
- const el = document.querySelector('#scoped-access-token-selector');
- if (!el) return;
-
- const {default: ScopedAccessTokenSelector} = await import(/* webpackChunkName: "scoped-access-token-selector" */'../components/ScopedAccessTokenSelector.vue');
- try {
- const View = createApp(ScopedAccessTokenSelector, {
- isAdmin: JSON.parse(el.getAttribute('data-is-admin')),
- noAccessLabel: el.getAttribute('data-no-access-label'),
- readLabel: el.getAttribute('data-read-label'),
- writeLabel: el.getAttribute('data-write-label'),
- });
- View.mount(el);
- } catch (err) {
- console.error('ScopedAccessTokenSelector failed to load', err);
- el.textContent = el.getAttribute('data-locale-component-failed-to-load');
- }
-}
diff --git a/web_src/js/features/sshkey-helper.ts b/web_src/js/features/sshkey-helper.ts
index 9234e3ec44..860bc5b294 100644
--- a/web_src/js/features/sshkey-helper.ts
+++ b/web_src/js/features/sshkey-helper.ts
@@ -1,6 +1,6 @@
export function initSshKeyFormParser() {
// Parse SSH Key
- document.querySelector('#ssh-key-content')?.addEventListener('input', function () {
+ document.querySelector<HTMLTextAreaElement>('#ssh-key-content')?.addEventListener('input', function () {
const arrays = this.value.split(' ');
const title = document.querySelector<HTMLInputElement>('#ssh-key-title');
if (!title.value && arrays.length === 3 && arrays[2] !== '') {
diff --git a/web_src/js/features/stopwatch.ts b/web_src/js/features/stopwatch.ts
index af52be4e24..07f9c435b8 100644
--- a/web_src/js/features/stopwatch.ts
+++ b/web_src/js/features/stopwatch.ts
@@ -1,6 +1,6 @@
import {createTippy} from '../modules/tippy.ts';
import {GET} from '../modules/fetch.ts';
-import {hideElem, showElem} from '../utils/dom.ts';
+import {hideElem, queryElems, showElem} from '../utils/dom.ts';
import {logoutFromWorker} from '../modules/worker.ts';
const {appSubUrl, notificationSettings, enableTimeTracking, assetVersionEncoded} = window.config;
@@ -38,7 +38,7 @@ export function initStopwatch() {
}
let usingPeriodicPoller = false;
- const startPeriodicPoller = (timeout) => {
+ const startPeriodicPoller = (timeout: number) => {
if (timeout <= 0 || !Number.isFinite(timeout)) return;
usingPeriodicPoller = true;
setTimeout(() => updateStopwatchWithCallback(startPeriodicPoller, timeout), timeout);
@@ -103,7 +103,7 @@ export function initStopwatch() {
startPeriodicPoller(notificationSettings.MinTimeout);
}
-async function updateStopwatchWithCallback(callback, timeout) {
+async function updateStopwatchWithCallback(callback: (timeout: number) => void, timeout: number) {
const isSet = await updateStopwatch();
if (!isSet) {
@@ -125,7 +125,7 @@ async function updateStopwatch() {
return updateStopwatchData(data);
}
-function updateStopwatchData(data) {
+function updateStopwatchData(data: any) {
const watch = data[0];
const btnEls = document.querySelectorAll('.active-stopwatch');
if (!watch) {
@@ -134,7 +134,7 @@ function updateStopwatchData(data) {
const {repo_owner_name, repo_name, issue_index, seconds} = watch;
const issueUrl = `${appSubUrl}/${repo_owner_name}/${repo_name}/issues/${issue_index}`;
document.querySelector('.stopwatch-link')?.setAttribute('href', issueUrl);
- document.querySelector('.stopwatch-commit')?.setAttribute('action', `${issueUrl}/times/stopwatch/toggle`);
+ document.querySelector('.stopwatch-commit')?.setAttribute('action', `${issueUrl}/times/stopwatch/stop`);
document.querySelector('.stopwatch-cancel')?.setAttribute('action', `${issueUrl}/times/stopwatch/cancel`);
const stopwatchIssue = document.querySelector('.stopwatch-issue');
if (stopwatchIssue) stopwatchIssue.textContent = `${repo_owner_name}/${repo_name}#${issue_index}`;
@@ -144,23 +144,10 @@ function updateStopwatchData(data) {
return Boolean(data.length);
}
-// TODO: This flickers on page load, we could avoid this by making a custom
-// element to render time periods. Feeding a datetime in backend does not work
-// when time zone between server and client differs.
-function updateStopwatchTime(seconds) {
- if (!Number.isFinite(seconds)) return;
- const datetime = (new Date(Date.now() - seconds * 1000)).toISOString();
- for (const parent of document.querySelectorAll('.header-stopwatch-dot')) {
- const existing = parent.querySelector(':scope > relative-time');
- if (existing) {
- existing.setAttribute('datetime', datetime);
- } else {
- const el = document.createElement('relative-time');
- el.setAttribute('format', 'micro');
- el.setAttribute('datetime', datetime);
- el.setAttribute('lang', 'en-US');
- el.setAttribute('title', ''); // make <relative-time> show no title and therefor no tooltip
- parent.append(el);
- }
- }
+// TODO: This flickers on page load, we could avoid this by making a custom element to render time periods.
+function updateStopwatchTime(seconds: number) {
+ const hours = seconds / 3600 || 0;
+ const minutes = seconds / 60 || 0;
+ const timeText = hours >= 1 ? `${Math.round(hours)}h` : `${Math.round(minutes)}m`;
+ queryElems(document, '.header-stopwatch-dot', (el) => el.textContent = timeText);
}
diff --git a/web_src/js/features/tablesort.ts b/web_src/js/features/tablesort.ts
index 15ea358fa3..0648ffd067 100644
--- a/web_src/js/features/tablesort.ts
+++ b/web_src/js/features/tablesort.ts
@@ -9,7 +9,7 @@ export function initTableSort() {
}
}
-function tableSort(normSort, revSort, isDefault) {
+function tableSort(normSort: string, revSort: string, isDefault: string) {
if (!normSort) return false;
if (!revSort) revSort = '';
diff --git a/web_src/js/features/tribute.ts b/web_src/js/features/tribute.ts
index fa65bcbb28..43c21ebe6d 100644
--- a/web_src/js/features/tribute.ts
+++ b/web_src/js/features/tribute.ts
@@ -1,14 +1,16 @@
import {emojiKeys, emojiHTML, emojiString} from './emoji.ts';
-import {htmlEscape} from 'escape-goat';
+import {html, htmlRaw} from '../utils/html.ts';
-function makeCollections({mentions, emoji}) {
- const collections = [];
+type TributeItem = Record<string, any>;
- if (emoji) {
- collections.push({
+export async function attachTribute(element: HTMLElement) {
+ const {default: Tribute} = await import(/* webpackChunkName: "tribute" */'tributejs');
+
+ const collections = [
+ { // emojis
trigger: ':',
requireLeadingSpace: true,
- values: (query, cb) => {
+ values: (query: string, cb: (matches: Array<string>) => void) => {
const matches = [];
for (const name of emojiKeys) {
if (name.includes(query)) {
@@ -18,39 +20,30 @@ function makeCollections({mentions, emoji}) {
}
cb(matches);
},
- lookup: (item) => item,
- selectTemplate: (item) => {
+ lookup: (item: TributeItem) => item,
+ selectTemplate: (item: TributeItem) => {
if (item === undefined) return null;
return emojiString(item.original);
},
- menuItemTemplate: (item) => {
- return `<div class="tribute-item">${emojiHTML(item.original)}<span>${htmlEscape(item.original)}</span></div>`;
+ menuItemTemplate: (item: TributeItem) => {
+ return html`<div class="tribute-item">${htmlRaw(emojiHTML(item.original))}<span>${item.original}</span></div>`;
},
- });
- }
-
- if (mentions) {
- collections.push({
+ }, { // mentions
values: window.config.mentionValues ?? [],
requireLeadingSpace: true,
- menuItemTemplate: (item) => {
- return `
+ menuItemTemplate: (item: TributeItem) => {
+ const fullNameHtml = item.original.fullname && item.original.fullname !== '' ? html`<span class="fullname">${item.original.fullname}</span>` : '';
+ return html`
<div class="tribute-item">
- <img src="${htmlEscape(item.original.avatar)}" width="21" height="21"/>
- <span class="name">${htmlEscape(item.original.name)}</span>
- ${item.original.fullname && item.original.fullname !== '' ? `<span class="fullname">${htmlEscape(item.original.fullname)}</span>` : ''}
+ <img alt src="${item.original.avatar}" width="21" height="21"/>
+ <span class="name">${item.original.name}</span>
+ ${htmlRaw(fullNameHtml)}
</div>
`;
},
- });
- }
+ },
+ ];
- return collections;
-}
-
-export async function attachTribute(element, {mentions, emoji}) {
- const {default: Tribute} = await import(/* webpackChunkName: "tribute" */'tributejs');
- const collections = makeCollections({mentions, emoji});
// @ts-expect-error TS2351: This expression is not constructable (strange, why)
const tribute = new Tribute({collection: collections, noMatchTemplate: ''});
tribute.attach(element);
diff --git a/web_src/js/features/user-auth-webauthn.ts b/web_src/js/features/user-auth-webauthn.ts
index 70516c280d..1f336b9741 100644
--- a/web_src/js/features/user-auth-webauthn.ts
+++ b/web_src/js/features/user-auth-webauthn.ts
@@ -1,5 +1,5 @@
import {encodeURLEncodedBase64, decodeURLEncodedBase64} from '../utils.ts';
-import {showElem} from '../utils/dom.ts';
+import {hideElem, showElem} from '../utils/dom.ts';
import {GET, POST} from '../modules/fetch.ts';
const {appSubUrl} = window.config;
@@ -11,6 +11,12 @@ export async function initUserAuthWebAuthn() {
return;
}
+ // webauthn is only supported on secure contexts
+ if (!window.isSecureContext) {
+ hideElem(elSignInPasskeyBtn);
+ return;
+ }
+
if (!detectWebAuthnSupport()) {
return;
}
@@ -114,7 +120,7 @@ async function login2FA() {
}
}
-async function verifyAssertion(assertedCredential) {
+async function verifyAssertion(assertedCredential: any) { // TODO: Credential type does not work
// Move data into Arrays in case it is super long
const authData = new Uint8Array(assertedCredential.response.authenticatorData);
const clientDataJSON = new Uint8Array(assertedCredential.response.clientDataJSON);
@@ -148,7 +154,7 @@ async function verifyAssertion(assertedCredential) {
window.location.href = reply?.redirect ?? `${appSubUrl}/`;
}
-async function webauthnRegistered(newCredential) {
+async function webauthnRegistered(newCredential: any) { // TODO: Credential type does not work
const attestationObject = new Uint8Array(newCredential.response.attestationObject);
const clientDataJSON = new Uint8Array(newCredential.response.clientDataJSON);
const rawId = new Uint8Array(newCredential.rawId);
diff --git a/web_src/js/features/user-settings.ts b/web_src/js/features/user-settings.ts
index c097df7b6c..6fbb56e540 100644
--- a/web_src/js/features/user-settings.ts
+++ b/web_src/js/features/user-settings.ts
@@ -1,19 +1,9 @@
import {hideElem, showElem} from '../utils/dom.ts';
-import {initCompCropper} from './comp/Cropper.ts';
-
-function initUserSettingsAvatarCropper() {
- const fileInput = document.querySelector<HTMLInputElement>('#new-avatar');
- const container = document.querySelector<HTMLElement>('.user.settings.profile .cropper-panel');
- const imageSource = container.querySelector<HTMLImageElement>('.cropper-source');
- initCompCropper({container, fileInput, imageSource});
-}
export function initUserSettings() {
if (!document.querySelector('.user.settings.profile')) return;
- initUserSettingsAvatarCropper();
-
- const usernameInput = document.querySelector('#username');
+ const usernameInput = document.querySelector<HTMLInputElement>('#username');
if (!usernameInput) return;
usernameInput.addEventListener('input', function () {
const prompt = document.querySelector('#name-change-prompt');
diff --git a/web_src/js/globals.d.ts b/web_src/js/globals.d.ts
index 0c540ac296..00f1744a95 100644
--- a/web_src/js/globals.d.ts
+++ b/web_src/js/globals.d.ts
@@ -25,11 +25,6 @@ declare module 'htmx.org/dist/htmx.esm.js' {
export default value;
}
-declare module 'uint8-to-base64' {
- export function encode(arrayBuffer: Uint8Array): string;
- export function decode(base64str: string): Uint8Array;
-}
-
declare module 'swagger-ui-dist/swagger-ui-es-bundle.js' {
const value = await import('swagger-ui-dist');
export default value.SwaggerUIBundle;
@@ -55,24 +50,22 @@ interface Element {
_tippy: import('tippy.js').Instance;
}
-type Writable<T> = { -readonly [K in keyof T]: T[K] };
-
interface Window {
+ __webpack_public_path__: string;
config: import('./web_src/js/types.ts').Config;
$: typeof import('@types/jquery'),
jQuery: typeof import('@types/jquery'),
- htmx: Omit<typeof import('htmx.org/dist/htmx.esm.js').default, 'config'> & {
- config?: Writable<typeof import('htmx.org').default.config>,
- },
- ui?: any,
+ htmx: typeof import('htmx.org').default,
_globalHandlerErrors: Array<ErrorEvent & PromiseRejectionEvent> & {
_inited: boolean,
push: (e: ErrorEvent & PromiseRejectionEvent) => void | number,
},
- __webpack_public_path__: string;
+ codeEditors: any[], // export editor for customization
+
+ // various captcha plugins
grecaptcha: any,
turnstile: any,
hcaptcha: any,
- codeEditors: any[],
- updateCloneStates: () => void,
+
+ // do not add more properties here unless it is a must
}
diff --git a/web_src/js/globals.ts b/web_src/js/globals.ts
index 24974da90e..955515d250 100644
--- a/web_src/js/globals.ts
+++ b/web_src/js/globals.ts
@@ -1,5 +1,2 @@
import jquery from 'jquery';
-import htmx from 'htmx.org/dist/htmx.esm.js';
-
-window.$ = window.jQuery = jquery;
-window.htmx = htmx;
+window.$ = window.jQuery = jquery; // only for Fomantic UI
diff --git a/web_src/js/htmx.ts b/web_src/js/htmx.ts
index 3f9a5a815c..9d433dfd57 100644
--- a/web_src/js/htmx.ts
+++ b/web_src/js/htmx.ts
@@ -1,21 +1,26 @@
-import {showErrorToast} from './modules/toast.ts';
-import 'idiomorph/dist/idiomorph-ext.js'; // https://github.com/bigskysoftware/idiomorph#htmx
+import htmx from 'htmx.org';
+import 'idiomorph/htmx';
import type {HtmxResponseInfo} from 'htmx.org';
+import {showErrorToast} from './modules/toast.ts';
type HtmxEvent = Event & {detail: HtmxResponseInfo};
-// https://htmx.org/reference/#config
-window.htmx.config.requestClass = 'is-loading';
-window.htmx.config.scrollIntoViewOnBoost = false;
+export function initHtmx() {
+ window.htmx = htmx;
+
+ // https://htmx.org/reference/#config
+ htmx.config.requestClass = 'is-loading';
+ htmx.config.scrollIntoViewOnBoost = false;
-// https://htmx.org/events/#htmx:sendError
-document.body.addEventListener('htmx:sendError', (event: Partial<HtmxEvent>) => {
- // TODO: add translations
- showErrorToast(`Network error when calling ${event.detail.requestConfig.path}`);
-});
+ // https://htmx.org/events/#htmx:sendError
+ document.body.addEventListener('htmx:sendError', (event: Partial<HtmxEvent>) => {
+ // TODO: add translations
+ showErrorToast(`Network error when calling ${event.detail.requestConfig.path}`);
+ });
-// https://htmx.org/events/#htmx:responseError
-document.body.addEventListener('htmx:responseError', (event: Partial<HtmxEvent>) => {
- // TODO: add translations
- showErrorToast(`Error ${event.detail.xhr.status} when calling ${event.detail.requestConfig.path}`);
-});
+ // https://htmx.org/events/#htmx:responseError
+ document.body.addEventListener('htmx:responseError', (event: Partial<HtmxEvent>) => {
+ // TODO: add translations
+ showErrorToast(`Error ${event.detail.xhr.status} when calling ${event.detail.requestConfig.path}`);
+ });
+}
diff --git a/web_src/js/index-domready.ts b/web_src/js/index-domready.ts
new file mode 100644
index 0000000000..4d7ab98db0
--- /dev/null
+++ b/web_src/js/index-domready.ts
@@ -0,0 +1,177 @@
+import './globals.ts';
+import '../fomantic/build/fomantic.js';
+import '../../node_modules/easymde/dist/easymde.min.css'; // TODO: lazy load in "switchToEasyMDE"
+
+import {initHtmx} from './htmx.ts';
+import {initDashboardRepoList} from './features/dashboard.ts';
+import {initGlobalCopyToClipboardListener} from './features/clipboard.ts';
+import {initContextPopups} from './features/contextpopup.ts';
+import {initRepoGraphGit} from './features/repo-graph.ts';
+import {initHeatmap} from './features/heatmap.ts';
+import {initImageDiff} from './features/imagediff.ts';
+import {initRepoMigration} from './features/repo-migration.ts';
+import {initRepoProject} from './features/repo-projects.ts';
+import {initTableSort} from './features/tablesort.ts';
+import {initAdminUserListSearchForm} from './features/admin/users.ts';
+import {initAdminConfigs} from './features/admin/config.ts';
+import {initMarkupAnchors} from './markup/anchors.ts';
+import {initNotificationCount, initNotificationsTable} from './features/notification.ts';
+import {initRepoIssueContentHistory} from './features/repo-issue-content.ts';
+import {initStopwatch} from './features/stopwatch.ts';
+import {initFindFileInRepo} from './features/repo-findfile.ts';
+import {initMarkupContent} from './markup/content.ts';
+import {initRepoFileView} from './features/file-view.ts';
+import {initUserAuthOauth2, initUserCheckAppUrl} from './features/user-auth.ts';
+import {initRepoPullRequestAllowMaintainerEdit, initRepoPullRequestReview, initRepoIssueSidebarDependency, initRepoIssueFilterItemLabel} from './features/repo-issue.ts';
+import {initRepoEllipsisButton, initCommitStatuses} from './features/repo-commit.ts';
+import {initRepoTopicBar} from './features/repo-home.ts';
+import {initAdminCommon} from './features/admin/common.ts';
+import {initRepoCodeView} from './features/repo-code.ts';
+import {initSshKeyFormParser} from './features/sshkey-helper.ts';
+import {initUserSettings} from './features/user-settings.ts';
+import {initRepoActivityTopAuthorsChart, initRepoArchiveLinks} from './features/repo-common.ts';
+import {initRepoMigrationStatusChecker} from './features/repo-migrate.ts';
+import {initRepoDiffView} from './features/repo-diff.ts';
+import {initOrgTeam} from './features/org-team.ts';
+import {initUserAuthWebAuthn, initUserAuthWebAuthnRegister} from './features/user-auth-webauthn.ts';
+import {initRepoRelease, initRepoReleaseNew} from './features/repo-release.ts';
+import {initRepoEditor} from './features/repo-editor.ts';
+import {initCompSearchUserBox} from './features/comp/SearchUserBox.ts';
+import {initInstall} from './features/install.ts';
+import {initCompWebHookEditor} from './features/comp/WebHookEditor.ts';
+import {initRepoBranchButton} from './features/repo-branch.ts';
+import {initCommonOrganization} from './features/common-organization.ts';
+import {initRepoWikiForm} from './features/repo-wiki.ts';
+import {initRepository, initBranchSelectorTabs} from './features/repo-legacy.ts';
+import {initCopyContent} from './features/copycontent.ts';
+import {initCaptcha} from './features/captcha.ts';
+import {initRepositoryActionView} from './features/repo-actions.ts';
+import {initGlobalTooltips} from './modules/tippy.ts';
+import {initGiteaFomantic} from './modules/fomantic.ts';
+import {initSubmitEventPolyfill} from './utils/dom.ts';
+import {initRepoIssueList} from './features/repo-issue-list.ts';
+import {initCommonIssueListQuickGoto} from './features/common-issue-list.ts';
+import {initRepoContributors} from './features/contributors.ts';
+import {initRepoCodeFrequency} from './features/code-frequency.ts';
+import {initRepoRecentCommits} from './features/recent-commits.ts';
+import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.ts';
+import {initGlobalSelectorObserver} from './modules/observer.ts';
+import {initRepositorySearch} from './features/repo-search.ts';
+import {initColorPickers} from './features/colorpicker.ts';
+import {initAdminSelfCheck} from './features/admin/selfcheck.ts';
+import {initOAuth2SettingsDisableCheckbox} from './features/oauth2-settings.ts';
+import {initGlobalFetchAction} from './features/common-fetch-action.ts';
+import {initFootLanguageMenu, initGlobalAvatarUploader, initGlobalDropdown, initGlobalInput, initGlobalTabularMenu, initHeadNavbarContentToggle} from './features/common-page.ts';
+import {initGlobalButtonClickOnEnter, initGlobalButtons, initGlobalDeleteButton} from './features/common-button.ts';
+import {initGlobalComboMarkdownEditor, initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm} from './features/common-form.ts';
+import {callInitFunctions} from './modules/init.ts';
+import {initRepoViewFileTree} from './features/repo-view-file-tree.ts';
+
+const initStartTime = performance.now();
+const initPerformanceTracer = callInitFunctions([
+ initHtmx,
+ initSubmitEventPolyfill,
+ initGiteaFomantic,
+
+ initGlobalAvatarUploader,
+ initGlobalDropdown,
+ initGlobalTabularMenu,
+ initGlobalFetchAction,
+ initGlobalTooltips,
+ initGlobalButtonClickOnEnter,
+ initGlobalButtons,
+ initGlobalCopyToClipboardListener,
+ initGlobalEnterQuickSubmit,
+ initGlobalFormDirtyLeaveConfirm,
+ initGlobalComboMarkdownEditor,
+ initGlobalDeleteButton,
+ initGlobalInput,
+
+ initCommonOrganization,
+ initCommonIssueListQuickGoto,
+
+ initCompSearchUserBox,
+ initCompWebHookEditor,
+
+ initInstall,
+
+ initHeadNavbarContentToggle,
+ initFootLanguageMenu,
+
+ initContextPopups,
+ initHeatmap,
+ initImageDiff,
+ initMarkupAnchors,
+ initMarkupContent,
+ initSshKeyFormParser,
+ initStopwatch,
+ initTableSort,
+ initFindFileInRepo,
+ initCopyContent,
+
+ initAdminCommon,
+ initAdminUserListSearchForm,
+ initAdminConfigs,
+ initAdminSelfCheck,
+
+ initDashboardRepoList,
+
+ initNotificationCount,
+ initNotificationsTable,
+
+ initOrgTeam,
+
+ initRepoActivityTopAuthorsChart,
+ initRepoArchiveLinks,
+ initRepoBranchButton,
+ initRepoCodeView,
+ initBranchSelectorTabs,
+ initRepoEllipsisButton,
+ initRepoDiffCommitBranchesAndTags,
+ initRepoEditor,
+ initRepoGraphGit,
+ initRepoIssueContentHistory,
+ initRepoIssueList,
+ initRepoIssueFilterItemLabel,
+ initRepoIssueSidebarDependency,
+ initRepoMigration,
+ initRepoMigrationStatusChecker,
+ initRepoProject,
+ initRepoPullRequestAllowMaintainerEdit,
+ initRepoPullRequestReview,
+ initRepoRelease,
+ initRepoReleaseNew,
+ initRepoTopicBar,
+ initRepoViewFileTree,
+ initRepoWikiForm,
+ initRepository,
+ initRepositoryActionView,
+ initRepositorySearch,
+ initRepoContributors,
+ initRepoCodeFrequency,
+ initRepoRecentCommits,
+
+ initCommitStatuses,
+ initCaptcha,
+
+ initUserCheckAppUrl,
+ initUserAuthOauth2,
+ initUserAuthWebAuthn,
+ initUserAuthWebAuthnRegister,
+ initUserSettings,
+ initRepoDiffView,
+ initColorPickers,
+
+ initOAuth2SettingsDisableCheckbox,
+
+ initRepoFileView,
+]);
+
+// it must be the last one, then the "querySelectorAll" only needs to be executed once for global init functions.
+initGlobalSelectorObserver(initPerformanceTracer);
+if (initPerformanceTracer) initPerformanceTracer.printResults();
+
+const initDur = performance.now() - initStartTime;
+if (initDur > 500) {
+ console.error(`slow init functions took ${initDur.toFixed(3)}ms`);
+}
diff --git a/web_src/js/index.ts b/web_src/js/index.ts
index 4d400d3b8f..e78b3cb64f 100644
--- a/web_src/js/index.ts
+++ b/web_src/js/index.ts
@@ -1,219 +1,13 @@
// bootstrap module must be the first one to be imported, it handles webpack lazy-loading and global errors
import './bootstrap.ts';
-import './htmx.ts';
+import './webcomponents/index.ts';
+import {onDomReady} from './utils/dom.ts';
-import {initDashboardRepoList} from './components/DashboardRepoList.vue';
+// TODO: There is a bug in htmx, it incorrectly checks "readyState === 'complete'" when the DOM tree is ready and won't trigger DOMContentLoaded
+// Then importing the htmx in our onDomReady will make htmx skip its initialization.
+// If the bug would be fixed (https://github.com/bigskysoftware/htmx/pull/3365), then we can only import htmx in "onDomReady"
+import 'htmx.org';
-import {initGlobalCopyToClipboardListener} from './features/clipboard.ts';
-import {initContextPopups} from './features/contextpopup.ts';
-import {initRepoGraphGit} from './features/repo-graph.ts';
-import {initHeatmap} from './features/heatmap.ts';
-import {initImageDiff} from './features/imagediff.ts';
-import {initRepoMigration} from './features/repo-migration.ts';
-import {initRepoProject} from './features/repo-projects.ts';
-import {initTableSort} from './features/tablesort.ts';
-import {initAutoFocusEnd} from './features/autofocus-end.ts';
-import {initAdminUserListSearchForm} from './features/admin/users.ts';
-import {initAdminConfigs} from './features/admin/config.ts';
-import {initMarkupAnchors} from './markup/anchors.ts';
-import {initNotificationCount, initNotificationsTable} from './features/notification.ts';
-import {initRepoIssueContentHistory} from './features/repo-issue-content.ts';
-import {initStopwatch} from './features/stopwatch.ts';
-import {initFindFileInRepo} from './features/repo-findfile.ts';
-import {initCommentContent, initMarkupContent} from './markup/content.ts';
-import {initPdfViewer} from './render/pdf.ts';
-
-import {initUserAuthOauth2, initUserCheckAppUrl} from './features/user-auth.ts';
-import {
- initRepoIssueReferenceRepositorySearch,
- initRepoIssueWipTitle,
- initRepoPullRequestMergeInstruction,
- initRepoPullRequestAllowMaintainerEdit,
- initRepoPullRequestReview, initRepoIssueSidebarList, initRepoIssueFilterItemLabel,
-} from './features/repo-issue.ts';
-import {initRepoEllipsisButton, initCommitStatuses} from './features/repo-commit.ts';
-import {initRepoTopicBar} from './features/repo-home.ts';
-import {initAdminCommon} from './features/admin/common.ts';
-import {initRepoCodeView} from './features/repo-code.ts';
-import {initSshKeyFormParser} from './features/sshkey-helper.ts';
-import {initUserSettings} from './features/user-settings.ts';
-import {initRepoActivityTopAuthorsChart, initRepoArchiveLinks} from './features/repo-common.ts';
-import {initRepoMigrationStatusChecker} from './features/repo-migrate.ts';
-import {initRepoDiffView} from './features/repo-diff.ts';
-import {initOrgTeamSearchRepoBox, initOrgTeamSettings} from './features/org-team.ts';
-import {initUserAuthWebAuthn, initUserAuthWebAuthnRegister} from './features/user-auth-webauthn.ts';
-import {initRepoRelease, initRepoReleaseNew} from './features/repo-release.ts';
-import {initRepoEditor} from './features/repo-editor.ts';
-import {initCompSearchUserBox} from './features/comp/SearchUserBox.ts';
-import {initInstall} from './features/install.ts';
-import {initCompWebHookEditor} from './features/comp/WebHookEditor.ts';
-import {initRepoBranchButton} from './features/repo-branch.ts';
-import {initCommonOrganization} from './features/common-organization.ts';
-import {initRepoWikiForm} from './features/repo-wiki.ts';
-import {initRepository, initBranchSelectorTabs} from './features/repo-legacy.ts';
-import {initCopyContent} from './features/copycontent.ts';
-import {initCaptcha} from './features/captcha.ts';
-import {initRepositoryActionView} from './components/RepoActionView.vue';
-import {initGlobalTooltips} from './modules/tippy.ts';
-import {initGiteaFomantic} from './modules/fomantic.ts';
-import {initSubmitEventPolyfill, onDomReady} from './utils/dom.ts';
-import {initRepoIssueList} from './features/repo-issue-list.ts';
-import {initCommonIssueListQuickGoto} from './features/common-issue-list.ts';
-import {initRepoContributors} from './features/contributors.ts';
-import {initRepoCodeFrequency} from './features/code-frequency.ts';
-import {initRepoRecentCommits} from './features/recent-commits.ts';
-import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.ts';
-import {initDirAuto} from './modules/dirauto.ts';
-import {initRepositorySearch} from './features/repo-search.ts';
-import {initColorPickers} from './features/colorpicker.ts';
-import {initAdminSelfCheck} from './features/admin/selfcheck.ts';
-import {initOAuth2SettingsDisableCheckbox} from './features/oauth2-settings.ts';
-import {initGlobalFetchAction} from './features/common-fetch-action.ts';
-import {initScopedAccessTokenCategories} from './features/scoped-access-token.ts';
-import {
- initFootLanguageMenu,
- initGlobalDropdown,
- initGlobalTabularMenu,
- initHeadNavbarContentToggle,
-} from './features/common-page.ts';
-import {
- initGlobalButtonClickOnEnter,
- initGlobalButtons,
- initGlobalDeleteButton,
-} from './features/common-button.ts';
-import {
- initGlobalComboMarkdownEditor,
- initGlobalEnterQuickSubmit,
- initGlobalFormDirtyLeaveConfirm,
-} from './features/common-form.ts';
-
-initGiteaFomantic();
-initDirAuto();
-initSubmitEventPolyfill();
-
-function callInitFunctions(functions: (() => any)[]) {
- // Start performance trace by accessing a URL by "https://localhost/?_ui_performance_trace=1" or "https://localhost/?key=value&_ui_performance_trace=1"
- // It is a quick check, no side effect so no need to do slow URL parsing.
- const initStart = performance.now();
- if (window.location.search.includes('_ui_performance_trace=1')) {
- let results: {name: string, dur: number}[] = [];
- for (const func of functions) {
- const start = performance.now();
- func();
- results.push({name: func.name, dur: performance.now() - start});
- }
- results = results.sort((a, b) => b.dur - a.dur);
- for (let i = 0; i < 20 && i < results.length; i++) {
- // eslint-disable-next-line no-console
- console.log(`performance trace: ${results[i].name} ${results[i].dur.toFixed(3)}`);
- }
- } else {
- for (const func of functions) {
- func();
- }
- }
- const initDur = performance.now() - initStart;
- if (initDur > 500) {
- console.error(`slow init functions took ${initDur.toFixed(3)}ms`);
- }
-}
-
-onDomReady(() => {
- callInitFunctions([
- initGlobalDropdown,
- initGlobalTabularMenu,
- initGlobalFetchAction,
- initGlobalTooltips,
- initGlobalButtonClickOnEnter,
- initGlobalButtons,
- initGlobalCopyToClipboardListener,
- initGlobalEnterQuickSubmit,
- initGlobalFormDirtyLeaveConfirm,
- initGlobalComboMarkdownEditor,
- initGlobalDeleteButton,
-
- initCommonOrganization,
- initCommonIssueListQuickGoto,
-
- initCompSearchUserBox,
- initCompWebHookEditor,
-
- initInstall,
-
- initHeadNavbarContentToggle,
- initFootLanguageMenu,
-
- initCommentContent,
- initContextPopups,
- initHeatmap,
- initImageDiff,
- initMarkupAnchors,
- initMarkupContent,
- initSshKeyFormParser,
- initStopwatch,
- initTableSort,
- initAutoFocusEnd,
- initFindFileInRepo,
- initCopyContent,
-
- initAdminCommon,
- initAdminUserListSearchForm,
- initAdminConfigs,
- initAdminSelfCheck,
-
- initDashboardRepoList,
-
- initNotificationCount,
- initNotificationsTable,
-
- initOrgTeamSearchRepoBox,
- initOrgTeamSettings,
-
- initRepoActivityTopAuthorsChart,
- initRepoArchiveLinks,
- initRepoBranchButton,
- initRepoCodeView,
- initBranchSelectorTabs,
- initRepoEllipsisButton,
- initRepoDiffCommitBranchesAndTags,
- initRepoEditor,
- initRepoGraphGit,
- initRepoIssueContentHistory,
- initRepoIssueList,
- initRepoIssueFilterItemLabel,
- initRepoIssueSidebarList,
- initRepoIssueReferenceRepositorySearch,
- initRepoIssueWipTitle,
- initRepoMigration,
- initRepoMigrationStatusChecker,
- initRepoProject,
- initRepoPullRequestMergeInstruction,
- initRepoPullRequestAllowMaintainerEdit,
- initRepoPullRequestReview,
- initRepoRelease,
- initRepoReleaseNew,
- initRepoTopicBar,
- initRepoWikiForm,
- initRepository,
- initRepositoryActionView,
- initRepositorySearch,
- initRepoContributors,
- initRepoCodeFrequency,
- initRepoRecentCommits,
-
- initCommitStatuses,
- initCaptcha,
-
- initUserCheckAppUrl,
- initUserAuthOauth2,
- initUserAuthWebAuthn,
- initUserAuthWebAuthnRegister,
- initUserSettings,
- initRepoDiffView,
- initPdfViewer,
- initScopedAccessTokenCategories,
- initColorPickers,
-
- initOAuth2SettingsDisableCheckbox,
- ]);
+onDomReady(async () => {
+ await import(/* webpackChunkName: "index-domready" */'./index-domready.ts');
});
diff --git a/web_src/js/markup/anchors.ts b/web_src/js/markup/anchors.ts
index 483d72bd5b..a0d49911fe 100644
--- a/web_src/js/markup/anchors.ts
+++ b/web_src/js/markup/anchors.ts
@@ -5,21 +5,24 @@ const removePrefix = (str: string): string => str.replace(/^user-content-/, '');
const hasPrefix = (str: string): boolean => str.startsWith('user-content-');
// scroll to anchor while respecting the `user-content` prefix that exists on the target
-function scrollToAnchor(encodedId: string): void {
- if (!encodedId) return;
- const id = decodeURIComponent(encodedId);
- const prefixedId = addPrefix(id);
- let el = document.querySelector(`#${prefixedId}`);
+function scrollToAnchor(encodedId?: string): void {
+ // FIXME: need to rewrite this function with new a better markup anchor generation logic, too many tricks here
+ let elemId: string;
+ try {
+ elemId = decodeURIComponent(encodedId ?? '');
+ } catch {} // ignore the errors, since the "encodedId" is from user's input
+ if (!elemId) return;
+
+ const prefixedId = addPrefix(elemId);
+ // eslint-disable-next-line unicorn/prefer-query-selector
+ let el = document.getElementById(prefixedId);
// check for matching user-generated `a[name]`
- if (!el) {
- el = document.querySelector(`a[name="${CSS.escape(prefixedId)}"]`);
- }
+ el = el ?? document.querySelector(`a[name="${CSS.escape(prefixedId)}"]`);
// compat for links with old 'user-content-' prefixed hashes
- if (!el && hasPrefix(id)) {
- return document.querySelector(`#${id}`)?.scrollIntoView();
- }
+ // eslint-disable-next-line unicorn/prefer-query-selector
+ el = (!el && hasPrefix(elemId)) ? document.getElementById(elemId) : el;
el?.scrollIntoView();
}
diff --git a/web_src/js/markup/asciicast.ts b/web_src/js/markup/asciicast.ts
index 97b18743a1..125bba447b 100644
--- a/web_src/js/markup/asciicast.ts
+++ b/web_src/js/markup/asciicast.ts
@@ -1,17 +1,17 @@
-export async function renderAsciicast() {
- const els = document.querySelectorAll('.asciinema-player-container');
- if (!els.length) return;
+import {queryElems} from '../utils/dom.ts';
- const [player] = await Promise.all([
- import(/* webpackChunkName: "asciinema-player" */'asciinema-player'),
- import(/* webpackChunkName: "asciinema-player" */'asciinema-player/dist/bundle/asciinema-player.css'),
- ]);
+export async function initMarkupRenderAsciicast(elMarkup: HTMLElement): Promise<void> {
+ queryElems(elMarkup, '.asciinema-player-container', async (el) => {
+ const [player] = await Promise.all([
+ // @ts-expect-error: module exports no types
+ import(/* webpackChunkName: "asciinema-player" */'asciinema-player'),
+ import(/* webpackChunkName: "asciinema-player" */'asciinema-player/dist/bundle/asciinema-player.css'),
+ ]);
- for (const el of els) {
player.create(el.getAttribute('data-asciinema-player-src'), el, {
// poster (a preview frame) to display until the playback is started.
// Set it to 1 hour (also means the end if the video is shorter) to make the preview frame show more.
poster: 'npt:1:0:0',
});
- }
+ });
}
diff --git a/web_src/js/markup/codecopy.ts b/web_src/js/markup/codecopy.ts
index f45b7a8e04..b37aa3a236 100644
--- a/web_src/js/markup/codecopy.ts
+++ b/web_src/js/markup/codecopy.ts
@@ -1,4 +1,5 @@
import {svg} from '../svg.ts';
+import {queryElems} from '../utils/dom.ts';
export function makeCodeCopyButton(): HTMLButtonElement {
const button = document.createElement('button');
@@ -7,15 +8,15 @@ export function makeCodeCopyButton(): HTMLButtonElement {
return button;
}
-export function renderCodeCopy(): void {
- const els = document.querySelectorAll('.markup .code-block code');
- if (!els.length) return;
-
- for (const el of els) {
- if (!el.textContent) continue;
+export function initMarkupCodeCopy(elMarkup: HTMLElement): void {
+ // .markup .code-block code
+ queryElems(elMarkup, '.code-block code', (el) => {
+ if (!el.textContent) return;
const btn = makeCodeCopyButton();
// remove final trailing newline introduced during HTML rendering
btn.setAttribute('data-clipboard-text', el.textContent.replace(/\r?\n$/, ''));
- el.after(btn);
- }
+ // we only want to use `.code-block-container` if it exists, no matter `.code-block` exists or not.
+ const btnContainer = el.closest('.code-block-container') ?? el.closest('.code-block');
+ btnContainer.append(btn);
+ });
}
diff --git a/web_src/js/markup/content.ts b/web_src/js/markup/content.ts
index b9190b15ce..55db4aa810 100644
--- a/web_src/js/markup/content.ts
+++ b/web_src/js/markup/content.ts
@@ -1,18 +1,17 @@
-import {renderMermaid} from './mermaid.ts';
-import {renderMath} from './math.ts';
-import {renderCodeCopy} from './codecopy.ts';
-import {renderAsciicast} from './asciicast.ts';
+import {initMarkupCodeMermaid} from './mermaid.ts';
+import {initMarkupCodeMath} from './math.ts';
+import {initMarkupCodeCopy} from './codecopy.ts';
+import {initMarkupRenderAsciicast} from './asciicast.ts';
import {initMarkupTasklist} from './tasklist.ts';
+import {registerGlobalSelectorFunc} from '../modules/observer.ts';
// code that runs for all markup content
export function initMarkupContent(): void {
- renderMermaid();
- renderMath();
- renderCodeCopy();
- renderAsciicast();
-}
-
-// code that only runs for comments
-export function initCommentContent(): void {
- initMarkupTasklist();
+ registerGlobalSelectorFunc('.markup', (el: HTMLElement) => {
+ initMarkupCodeCopy(el);
+ initMarkupTasklist(el);
+ initMarkupCodeMermaid(el);
+ initMarkupCodeMath(el);
+ initMarkupRenderAsciicast(el);
+ });
}
diff --git a/web_src/js/markup/html2markdown.ts b/web_src/js/markup/html2markdown.ts
index fc2083e86d..5866d0d259 100644
--- a/web_src/js/markup/html2markdown.ts
+++ b/web_src/js/markup/html2markdown.ts
@@ -1,7 +1,9 @@
-import {htmlEscape} from 'escape-goat';
+import {html, htmlRaw} from '../utils/html.ts';
+
+type Processor = (el: HTMLElement) => string | HTMLElement | void;
type Processors = {
- [tagName: string]: (el: HTMLElement) => string | HTMLElement | void;
+ [tagName: string]: Processor;
}
type ProcessorContext = {
@@ -11,7 +13,7 @@ type ProcessorContext = {
}
function prepareProcessors(ctx:ProcessorContext): Processors {
- const processors = {
+ const processors: Processors = {
H1(el: HTMLElement) {
const level = parseInt(el.tagName.slice(1));
el.textContent = `${'#'.repeat(level)} ${el.textContent.trim()}`;
@@ -36,10 +38,10 @@ function prepareProcessors(ctx:ProcessorContext): Processors {
IMG(el: HTMLElement) {
const alt = el.getAttribute('alt') || 'image';
const src = el.getAttribute('src');
- const widthAttr = el.hasAttribute('width') ? ` width="${htmlEscape(el.getAttribute('width') || '')}"` : '';
- const heightAttr = el.hasAttribute('height') ? ` height="${htmlEscape(el.getAttribute('height') || '')}"` : '';
+ const widthAttr = el.hasAttribute('width') ? htmlRaw` width="${el.getAttribute('width') || ''}"` : '';
+ const heightAttr = el.hasAttribute('height') ? htmlRaw` height="${el.getAttribute('height') || ''}"` : '';
if (widthAttr || heightAttr) {
- return `<img alt="${htmlEscape(alt)}"${widthAttr}${heightAttr} src="${htmlEscape(src)}">`;
+ return html`<img alt="${alt}"${widthAttr}${heightAttr} src="${src}">`;
}
return `![${alt}](${src})`;
},
diff --git a/web_src/js/markup/math.ts b/web_src/js/markup/math.ts
index 4777805e3c..bc118137a1 100644
--- a/web_src/js/markup/math.ts
+++ b/web_src/js/markup/math.ts
@@ -1,4 +1,5 @@
import {displayError} from './common.ts';
+import {queryElems} from '../utils/dom.ts';
function targetElement(el: Element): {target: Element, displayAsBlock: boolean} {
// The target element is either the parent "code block with loading indicator", or itself
@@ -11,27 +12,25 @@ function targetElement(el: Element): {target: Element, displayAsBlock: boolean}
};
}
-export async function renderMath(): Promise<void> {
- const els = document.querySelectorAll('.markup code.language-math');
- if (!els.length) return;
+export async function initMarkupCodeMath(elMarkup: HTMLElement): Promise<void> {
+ // .markup code.language-math'
+ queryElems(elMarkup, 'code.language-math', async (el) => {
+ const [{default: katex}] = await Promise.all([
+ import(/* webpackChunkName: "katex" */'katex'),
+ import(/* webpackChunkName: "katex" */'katex/dist/katex.css'),
+ ]);
- const [{default: katex}] = await Promise.all([
- import(/* webpackChunkName: "katex" */'katex'),
- import(/* webpackChunkName: "katex" */'katex/dist/katex.css'),
- ]);
+ const MAX_CHARS = 1000;
+ const MAX_SIZE = 25;
+ const MAX_EXPAND = 1000;
- const MAX_CHARS = 1000;
- const MAX_SIZE = 25;
- const MAX_EXPAND = 1000;
-
- for (const el of els) {
const {target, displayAsBlock} = targetElement(el);
- if (target.hasAttribute('data-render-done')) continue;
+ if (target.hasAttribute('data-render-done')) return;
const source = el.textContent;
if (source.length > MAX_CHARS) {
displayError(target, new Error(`Math source of ${source.length} characters exceeds the maximum allowed length of ${MAX_CHARS}.`));
- continue;
+ return;
}
try {
const tempEl = document.createElement(displayAsBlock ? 'p' : 'span');
@@ -44,5 +43,5 @@ export async function renderMath(): Promise<void> {
} catch (error) {
displayError(target, error);
}
- }
+ });
}
diff --git a/web_src/js/markup/mermaid.ts b/web_src/js/markup/mermaid.ts
index 2dbed280c2..33d9a1ed9b 100644
--- a/web_src/js/markup/mermaid.ts
+++ b/web_src/js/markup/mermaid.ts
@@ -1,6 +1,8 @@
import {isDarkTheme} from '../utils.ts';
import {makeCodeCopyButton} from './codecopy.ts';
import {displayError} from './common.ts';
+import {queryElems} from '../utils/dom.ts';
+import {html, htmlRaw} from '../utils/html.ts';
const {mermaidMaxSourceCharacters} = window.config;
@@ -10,34 +12,32 @@ body {margin: 0; padding: 0; overflow: hidden}
#mermaid {display: block; margin: 0 auto}
blockquote, dd, dl, figure, h1, h2, h3, h4, h5, h6, hr, p, pre {margin: 0}`;
-export async function renderMermaid(): Promise<void> {
- const els = document.querySelectorAll('.markup code.language-mermaid');
- if (!els.length) return;
+export async function initMarkupCodeMermaid(elMarkup: HTMLElement): Promise<void> {
+ // .markup code.language-mermaid
+ queryElems(elMarkup, 'code.language-mermaid', async (el) => {
+ const {default: mermaid} = await import(/* webpackChunkName: "mermaid" */'mermaid');
- const {default: mermaid} = await import(/* webpackChunkName: "mermaid" */'mermaid');
+ mermaid.initialize({
+ startOnLoad: false,
+ theme: isDarkTheme() ? 'dark' : 'neutral',
+ securityLevel: 'strict',
+ suppressErrorRendering: true,
+ });
- mermaid.initialize({
- startOnLoad: false,
- theme: isDarkTheme() ? 'dark' : 'neutral',
- securityLevel: 'strict',
- suppressErrorRendering: true,
- });
-
- for (const el of els) {
const pre = el.closest('pre');
- if (pre.hasAttribute('data-render-done')) continue;
+ if (pre.hasAttribute('data-render-done')) return;
const source = el.textContent;
if (mermaidMaxSourceCharacters >= 0 && source.length > mermaidMaxSourceCharacters) {
displayError(pre, new Error(`Mermaid source of ${source.length} characters exceeds the maximum allowed length of ${mermaidMaxSourceCharacters}.`));
- continue;
+ return;
}
try {
await mermaid.parse(source);
} catch (err) {
displayError(pre, err);
- continue;
+ return;
}
try {
@@ -46,8 +46,8 @@ export async function renderMermaid(): Promise<void> {
const {svg} = await mermaid.render('mermaid', source);
const iframe = document.createElement('iframe');
- iframe.classList.add('markup-render', 'tw-invisible');
- iframe.srcdoc = `<html><head><style>${iframeCss}</style></head><body>${svg}</body></html>`;
+ iframe.classList.add('markup-content-iframe', 'tw-invisible');
+ iframe.srcdoc = html`<html><head><style>${htmlRaw(iframeCss)}</style></head><body>${htmlRaw(svg)}</body></html>`;
const mermaidBlock = document.createElement('div');
mermaidBlock.classList.add('mermaid-block', 'is-loading', 'tw-hidden');
@@ -85,5 +85,5 @@ export async function renderMermaid(): Promise<void> {
} catch (err) {
displayError(pre, err);
}
- }
+ });
}
diff --git a/web_src/js/markup/tasklist.ts b/web_src/js/markup/tasklist.ts
index 95db7fc845..dc4bbd9519 100644
--- a/web_src/js/markup/tasklist.ts
+++ b/web_src/js/markup/tasklist.ts
@@ -7,80 +7,80 @@ const preventListener = (e: Event) => e.preventDefault();
* Attaches `input` handlers to markdown rendered tasklist checkboxes in comments.
*
* When a checkbox value changes, the corresponding [ ] or [x] in the markdown string
- * is set accordingly and sent to the server. On success it updates the raw-content on
+ * is set accordingly and sent to the server. On success, it updates the raw-content on
* error it resets the checkbox to its original value.
*/
-export function initMarkupTasklist(): void {
- for (const el of document.querySelectorAll(`.markup[data-can-edit=true]`) || []) {
- const container = el.parentNode;
- const checkboxes = el.querySelectorAll<HTMLInputElement>(`.task-list-item input[type=checkbox]`);
+export function initMarkupTasklist(elMarkup: HTMLElement): void {
+ if (!elMarkup.matches('[data-can-edit=true]')) return;
- for (const checkbox of checkboxes) {
- if (checkbox.hasAttribute('data-editable')) {
- return;
- }
+ const container = elMarkup.parentNode;
+ const checkboxes = elMarkup.querySelectorAll<HTMLInputElement>(`.task-list-item input[type=checkbox]`);
- checkbox.setAttribute('data-editable', 'true');
- checkbox.addEventListener('input', async () => {
- const checkboxCharacter = checkbox.checked ? 'x' : ' ';
- const position = parseInt(checkbox.getAttribute('data-source-position')) + 1;
+ for (const checkbox of checkboxes) {
+ if (checkbox.hasAttribute('data-editable')) {
+ return;
+ }
- const rawContent = container.querySelector('.raw-content');
- const oldContent = rawContent.textContent;
+ checkbox.setAttribute('data-editable', 'true');
+ checkbox.addEventListener('input', async () => {
+ const checkboxCharacter = checkbox.checked ? 'x' : ' ';
+ const position = parseInt(checkbox.getAttribute('data-source-position')) + 1;
- const encoder = new TextEncoder();
- const buffer = encoder.encode(oldContent);
- // Indexes may fall off the ends and return undefined.
- if (buffer[position - 1] !== '['.codePointAt(0) ||
- buffer[position] !== ' '.codePointAt(0) && buffer[position] !== 'x'.codePointAt(0) ||
- buffer[position + 1] !== ']'.codePointAt(0)) {
- // Position is probably wrong. Revert and don't allow change.
- checkbox.checked = !checkbox.checked;
- throw new Error(`Expected position to be space or x and surrounded by brackets, but it's not: position=${position}`);
- }
- buffer.set(encoder.encode(checkboxCharacter), position);
- const newContent = new TextDecoder().decode(buffer);
+ const rawContent = container.querySelector('.raw-content');
+ const oldContent = rawContent.textContent;
- if (newContent === oldContent) {
- return;
- }
+ const encoder = new TextEncoder();
+ const buffer = encoder.encode(oldContent);
+ // Indexes may fall off the ends and return undefined.
+ if (buffer[position - 1] !== '['.codePointAt(0) ||
+ buffer[position] !== ' '.codePointAt(0) && buffer[position] !== 'x'.codePointAt(0) ||
+ buffer[position + 1] !== ']'.codePointAt(0)) {
+ // Position is probably wrong. Revert and don't allow change.
+ checkbox.checked = !checkbox.checked;
+ throw new Error(`Expected position to be space or x and surrounded by brackets, but it's not: position=${position}`);
+ }
+ buffer.set(encoder.encode(checkboxCharacter), position);
+ const newContent = new TextDecoder().decode(buffer);
- // Prevent further inputs until the request is done. This does not use the
- // `disabled` attribute because it causes the border to flash on click.
- for (const checkbox of checkboxes) {
- checkbox.addEventListener('click', preventListener);
- }
+ if (newContent === oldContent) {
+ return;
+ }
- try {
- const editContentZone = container.querySelector<HTMLDivElement>('.edit-content-zone');
- const updateUrl = editContentZone.getAttribute('data-update-url');
- const context = editContentZone.getAttribute('data-context');
- const contentVersion = editContentZone.getAttribute('data-content-version');
+ // Prevent further inputs until the request is done. This does not use the
+ // `disabled` attribute because it causes the border to flash on click.
+ for (const checkbox of checkboxes) {
+ checkbox.addEventListener('click', preventListener);
+ }
- const requestBody = new FormData();
- requestBody.append('ignore_attachments', 'true');
- requestBody.append('content', newContent);
- requestBody.append('context', context);
- requestBody.append('content_version', contentVersion);
- const response = await POST(updateUrl, {data: requestBody});
- const data = await response.json();
- if (response.status === 400) {
- showErrorToast(data.errorMessage);
- return;
- }
- editContentZone.setAttribute('data-content-version', data.contentVersion);
- rawContent.textContent = newContent;
- } catch (err) {
- checkbox.checked = !checkbox.checked;
- console.error(err);
- }
+ try {
+ const editContentZone = container.querySelector<HTMLDivElement>('.edit-content-zone');
+ const updateUrl = editContentZone.getAttribute('data-update-url');
+ const context = editContentZone.getAttribute('data-context');
+ const contentVersion = editContentZone.getAttribute('data-content-version');
- // Enable input on checkboxes again
- for (const checkbox of checkboxes) {
- checkbox.removeEventListener('click', preventListener);
+ const requestBody = new FormData();
+ requestBody.append('ignore_attachments', 'true');
+ requestBody.append('content', newContent);
+ requestBody.append('context', context);
+ requestBody.append('content_version', contentVersion);
+ const response = await POST(updateUrl, {data: requestBody});
+ const data = await response.json();
+ if (response.status === 400) {
+ showErrorToast(data.errorMessage);
+ return;
}
- });
- }
+ editContentZone.setAttribute('data-content-version', data.contentVersion);
+ rawContent.textContent = newContent;
+ } catch (err) {
+ checkbox.checked = !checkbox.checked;
+ console.error(err);
+ }
+
+ // Enable input on checkboxes again
+ for (const checkbox of checkboxes) {
+ checkbox.removeEventListener('click', preventListener);
+ }
+ });
// Enable the checkboxes as they are initially disabled by the markdown renderer
for (const checkbox of checkboxes) {
diff --git a/web_src/js/modules/diff-file.test.ts b/web_src/js/modules/diff-file.test.ts
new file mode 100644
index 0000000000..f0438538a0
--- /dev/null
+++ b/web_src/js/modules/diff-file.test.ts
@@ -0,0 +1,51 @@
+import {diffTreeStoreSetViewed, reactiveDiffTreeStore} from './diff-file.ts';
+
+test('diff-tree', () => {
+ const store = reactiveDiffTreeStore({
+ 'TreeRoot': {
+ 'FullName': '',
+ 'DisplayName': '',
+ 'EntryMode': '',
+ 'IsViewed': false,
+ 'NameHash': '....',
+ 'DiffStatus': '',
+ 'FileIcon': '',
+ 'Children': [
+ {
+ 'FullName': 'dir1',
+ 'DisplayName': 'dir1',
+ 'EntryMode': 'tree',
+ 'IsViewed': false,
+ 'NameHash': '....',
+ 'DiffStatus': '',
+ 'FileIcon': '',
+ 'Children': [
+ {
+ 'FullName': 'dir1/test.txt',
+ 'DisplayName': 'test.txt',
+ 'DiffStatus': 'added',
+ 'NameHash': '....',
+ 'EntryMode': '',
+ 'IsViewed': false,
+ 'FileIcon': '',
+ 'Children': null,
+ },
+ ],
+ },
+ {
+ 'FullName': 'other.txt',
+ 'DisplayName': 'other.txt',
+ 'NameHash': '........',
+ 'DiffStatus': 'added',
+ 'EntryMode': '',
+ 'IsViewed': false,
+ 'FileIcon': '',
+ 'Children': null,
+ },
+ ],
+ },
+ }, '', '');
+ diffTreeStoreSetViewed(store, 'dir1/test.txt', true);
+ expect(store.fullNameMap['dir1/test.txt'].IsViewed).toBe(true);
+ expect(store.fullNameMap['dir1'].IsViewed).toBe(true);
+});
diff --git a/web_src/js/modules/diff-file.ts b/web_src/js/modules/diff-file.ts
new file mode 100644
index 0000000000..2cec7bc6b3
--- /dev/null
+++ b/web_src/js/modules/diff-file.ts
@@ -0,0 +1,82 @@
+import {reactive} from 'vue';
+import type {Reactive} from 'vue';
+
+const {pageData} = window.config;
+
+export type DiffStatus = '' | 'added' | 'modified' | 'deleted' | 'renamed' | 'copied' | 'typechange';
+
+export type DiffTreeEntry = {
+ FullName: string,
+ DisplayName: string,
+ NameHash: string,
+ DiffStatus: DiffStatus,
+ EntryMode: string,
+ IsViewed: boolean,
+ Children: DiffTreeEntry[],
+ FileIcon: string,
+ ParentEntry?: DiffTreeEntry,
+}
+
+type DiffFileTreeData = {
+ TreeRoot: DiffTreeEntry,
+};
+
+type DiffFileTree = {
+ folderIcon: string;
+ folderOpenIcon: string;
+ diffFileTree: DiffFileTreeData;
+ fullNameMap?: Record<string, DiffTreeEntry>
+ fileTreeIsVisible: boolean;
+ selectedItem: string;
+}
+
+let diffTreeStoreReactive: Reactive<DiffFileTree>;
+export function diffTreeStore() {
+ if (!diffTreeStoreReactive) {
+ diffTreeStoreReactive = reactiveDiffTreeStore(pageData.DiffFileTree, pageData.FolderIcon, pageData.FolderOpenIcon);
+ }
+ return diffTreeStoreReactive;
+}
+
+export function diffTreeStoreSetViewed(store: Reactive<DiffFileTree>, fullName: string, viewed: boolean) {
+ const entry = store.fullNameMap[fullName];
+ if (!entry) return;
+ entry.IsViewed = viewed;
+ for (let parent = entry.ParentEntry; parent; parent = parent.ParentEntry) {
+ parent.IsViewed = isEntryViewed(parent);
+ }
+}
+
+function fillFullNameMap(map: Record<string, DiffTreeEntry>, entry: DiffTreeEntry) {
+ map[entry.FullName] = entry;
+ if (!entry.Children) return;
+ entry.IsViewed = isEntryViewed(entry);
+ for (const child of entry.Children) {
+ child.ParentEntry = entry;
+ fillFullNameMap(map, child);
+ }
+}
+
+export function reactiveDiffTreeStore(data: DiffFileTreeData, folderIcon: string, folderOpenIcon: string): Reactive<DiffFileTree> {
+ const store = reactive({
+ diffFileTree: data,
+ folderIcon,
+ folderOpenIcon,
+ fileTreeIsVisible: false,
+ selectedItem: '',
+ fullNameMap: {},
+ });
+ fillFullNameMap(store.fullNameMap, data.TreeRoot);
+ return store;
+}
+
+function isEntryViewed(entry: DiffTreeEntry): boolean {
+ if (entry.Children) {
+ let count = 0;
+ for (const child of entry.Children) {
+ if (child.IsViewed) count++;
+ }
+ return count === entry.Children.length;
+ }
+ return entry.IsViewed;
+}
diff --git a/web_src/js/modules/dirauto.ts b/web_src/js/modules/dirauto.ts
deleted file mode 100644
index 7058a59b09..0000000000
--- a/web_src/js/modules/dirauto.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import {isDocumentFragmentOrElementNode} from '../utils/dom.ts';
-
-type DirElement = HTMLInputElement | HTMLTextAreaElement;
-
-// for performance considerations, it only uses performant syntax
-function attachDirAuto(el: DirElement) {
- if (el.type !== 'hidden' &&
- el.type !== 'checkbox' &&
- el.type !== 'radio' &&
- el.type !== 'range' &&
- el.type !== 'color') {
- el.dir = 'auto';
- }
-}
-
-export function initDirAuto(): void {
- const observer = new MutationObserver((mutationList) => {
- const len = mutationList.length;
- for (let i = 0; i < len; i++) {
- const mutation = mutationList[i];
- const len = mutation.addedNodes.length;
- for (let i = 0; i < len; i++) {
- const addedNode = mutation.addedNodes[i] as HTMLElement;
- if (!isDocumentFragmentOrElementNode(addedNode)) continue;
- if (addedNode.nodeName === 'INPUT' || addedNode.nodeName === 'TEXTAREA') {
- attachDirAuto(addedNode as DirElement);
- }
- const children = addedNode.querySelectorAll<DirElement>('input, textarea');
- const len = children.length;
- for (let childIdx = 0; childIdx < len; childIdx++) {
- attachDirAuto(children[childIdx]);
- }
- }
- }
- });
-
- const docNodes = document.querySelectorAll<DirElement>('input, textarea');
- const len = docNodes.length;
- for (let i = 0; i < len; i++) {
- attachDirAuto(docNodes[i]);
- }
-
- observer.observe(document, {subtree: true, childList: true});
-}
diff --git a/web_src/js/modules/fomantic.ts b/web_src/js/modules/fomantic.ts
index 18a3c18c9c..4b1dbc4f62 100644
--- a/web_src/js/modules/fomantic.ts
+++ b/web_src/js/modules/fomantic.ts
@@ -1,5 +1,4 @@
import $ from 'jquery';
-import {initFomanticApiPatch} from './fomantic/api.ts';
import {initAriaCheckboxPatch} from './fomantic/checkbox.ts';
import {initAriaFormFieldPatch} from './fomantic/form.ts';
import {initAriaDropdownPatch} from './fomantic/dropdown.ts';
@@ -7,14 +6,13 @@ import {initAriaModalPatch} from './fomantic/modal.ts';
import {initFomanticTransition} from './fomantic/transition.ts';
import {initFomanticDimmer} from './fomantic/dimmer.ts';
import {svg} from '../svg.ts';
+import {initFomanticTab} from './fomantic/tab.ts';
export const fomanticMobileScreen = window.matchMedia('only screen and (max-width: 767.98px)');
export function initGiteaFomantic() {
// our extensions
$.fn.fomanticExt = {};
- // Silence fomantic's error logging when tabs are used without a target content element
- $.fn.tab.settings.silent = true;
// By default, use "exact match" for full text search
$.fn.dropdown.settings.fullTextSearch = 'exact';
// Do not use "cursor: pointer" for dropdown labels
@@ -27,7 +25,7 @@ export function initGiteaFomantic() {
initFomanticTransition();
initFomanticDimmer();
- initFomanticApiPatch();
+ initFomanticTab();
// Use the patches to improve accessibility, these patches are designed to be as independent as possible, make it easy to modify or remove in the future.
initAriaCheckboxPatch();
diff --git a/web_src/js/modules/fomantic/api.ts b/web_src/js/modules/fomantic/api.ts
deleted file mode 100644
index 97430450e2..0000000000
--- a/web_src/js/modules/fomantic/api.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import $ from 'jquery';
-import type {FomanticInitFunction} from '../../types.ts';
-
-export function initFomanticApiPatch() {
- //
- // Fomantic API module has some very buggy behaviors:
- //
- // If encodeParameters=true, it calls `urlEncodedValue` to encode the parameter.
- // However, `urlEncodedValue` just tries to "guess" whether the parameter is already encoded, by decoding the parameter and encoding it again.
- //
- // There are 2 problems:
- // 1. It may guess wrong, and skip encoding a parameter which looks like encoded.
- // 2. If the parameter can't be decoded, `decodeURIComponent` will throw an error, and the whole request will fail.
- //
- // This patch only fixes the second error behavior at the moment.
- //
- const patchKey = '_giteaFomanticApiPatch';
- const oldApi = $.api;
- $.api = $.fn.api = function(...args: Parameters<FomanticInitFunction>) {
- const apiCall = oldApi.bind(this);
- const ret = oldApi.apply(this, args);
-
- if (typeof args[0] !== 'string') {
- const internalGet = apiCall('internal', 'get');
- if (!internalGet.urlEncodedValue[patchKey]) {
- const oldUrlEncodedValue = internalGet.urlEncodedValue;
- internalGet.urlEncodedValue = function (value: any) {
- try {
- return oldUrlEncodedValue(value);
- } catch {
- // if Fomantic API module's `urlEncodedValue` throws an error, we encode it by ourselves.
- return encodeURIComponent(value);
- }
- };
- internalGet.urlEncodedValue[patchKey] = true;
- }
- }
- return ret;
- };
- $.api.settings = oldApi.settings;
-}
diff --git a/web_src/js/modules/fomantic/base.ts b/web_src/js/modules/fomantic/base.ts
index 18f91932a9..1970941e18 100644
--- a/web_src/js/modules/fomantic/base.ts
+++ b/web_src/js/modules/fomantic/base.ts
@@ -1,9 +1,5 @@
import $ from 'jquery';
-let ariaIdCounter = 0;
-
-export function generateAriaId() {
- return `_aria_auto_id_${ariaIdCounter++}`;
-}
+import {generateElemId} from '../../utils/dom.ts';
export function linkLabelAndInput(label: Element, input: Element) {
const labelFor = label.getAttribute('for');
@@ -12,7 +8,7 @@ export function linkLabelAndInput(label: Element, input: Element) {
if (inputId && !labelFor) { // missing "for"
label.setAttribute('for', inputId);
} else if (!inputId && !labelFor) { // missing both "id" and "for"
- const id = generateAriaId();
+ const id = generateElemId('_aria_label_input_');
input.setAttribute('id', id);
label.setAttribute('for', id);
}
diff --git a/web_src/js/modules/fomantic/dimmer.ts b/web_src/js/modules/fomantic/dimmer.ts
index 4e05cac0cd..cbdfac23cb 100644
--- a/web_src/js/modules/fomantic/dimmer.ts
+++ b/web_src/js/modules/fomantic/dimmer.ts
@@ -3,7 +3,7 @@ import {queryElemChildren} from '../../utils/dom.ts';
export function initFomanticDimmer() {
// stand-in for removed dimmer module
- $.fn.dimmer = function (arg0: string, arg1: any) {
+ $.fn.dimmer = function (this: any, arg0: string, arg1: any) {
if (arg0 === 'add content') {
const $el = arg1;
const existingDimmer = document.querySelector('body > .ui.dimmer');
diff --git a/web_src/js/modules/fomantic/dropdown.test.ts b/web_src/js/modules/fomantic/dropdown.test.ts
index 587e0bca7c..dd3497c8fc 100644
--- a/web_src/js/modules/fomantic/dropdown.test.ts
+++ b/web_src/js/modules/fomantic/dropdown.test.ts
@@ -23,7 +23,27 @@ test('hideScopedEmptyDividers-simple', () => {
`);
});
-test('hideScopedEmptyDividers-hidden1', () => {
+test('hideScopedEmptyDividers-items-all-filtered', () => {
+ const container = createElementFromHTML(`<div>
+<div class="any"></div>
+<div class="divider"></div>
+<div class="item filtered">a</div>
+<div class="item filtered">b</div>
+<div class="divider"></div>
+<div class="any"></div>
+</div>`);
+ hideScopedEmptyDividers(container);
+ expect(container.innerHTML).toEqual(`
+<div class="any"></div>
+<div class="divider hidden transition"></div>
+<div class="item filtered">a</div>
+<div class="item filtered">b</div>
+<div class="divider"></div>
+<div class="any"></div>
+`);
+});
+
+test('hideScopedEmptyDividers-hide-last', () => {
const container = createElementFromHTML(`<div>
<div class="item">a</div>
<div class="divider" data-scope="b"></div>
@@ -37,7 +57,7 @@ test('hideScopedEmptyDividers-hidden1', () => {
`);
});
-test('hideScopedEmptyDividers-hidden2', () => {
+test('hideScopedEmptyDividers-scoped-items', () => {
const container = createElementFromHTML(`<div>
<div class="item" data-scope="">a</div>
<div class="divider" data-scope="b"></div>
diff --git a/web_src/js/modules/fomantic/dropdown.ts b/web_src/js/modules/fomantic/dropdown.ts
index 9bdc9bfc33..2af428f24e 100644
--- a/web_src/js/modules/fomantic/dropdown.ts
+++ b/web_src/js/modules/fomantic/dropdown.ts
@@ -1,7 +1,6 @@
import $ from 'jquery';
-import {generateAriaId} from './base.ts';
import type {FomanticInitFunction} from '../../types.ts';
-import {queryElems} from '../../utils/dom.ts';
+import {generateElemId, queryElems} from '../../utils/dom.ts';
const ariaPatchKey = '_giteaAriaPatchDropdown';
const fomanticDropdownFn = $.fn.dropdown;
@@ -11,24 +10,34 @@ export function initAriaDropdownPatch() {
if ($.fn.dropdown === ariaDropdownFn) throw new Error('initAriaDropdownPatch could only be called once');
$.fn.dropdown = ariaDropdownFn;
$.fn.fomanticExt.onResponseKeepSelectedItem = onResponseKeepSelectedItem;
+ $.fn.fomanticExt.onDropdownAfterFiltered = onDropdownAfterFiltered;
(ariaDropdownFn as FomanticInitFunction).settings = fomanticDropdownFn.settings;
}
// the patched `$.fn.dropdown` function, it passes the arguments to Fomantic's `$.fn.dropdown` function, and:
-// * it does the one-time attaching on the first call
-// * it delegates the `onLabelCreate` to the patched `onLabelCreate` to add necessary aria attributes
-function ariaDropdownFn(...args: Parameters<FomanticInitFunction>) {
+// * it does the one-time element event attaching on the first call
+// * it delegates the module internal functions like `onLabelCreate` to the patched functions to add more features.
+function ariaDropdownFn(this: any, ...args: Parameters<FomanticInitFunction>) {
const ret = fomanticDropdownFn.apply(this, args);
- // if the `$().dropdown()` call is without arguments, or it has non-string (object) argument,
- // it means that this call will reset the dropdown internal settings, then we need to re-delegate the callbacks.
- const needDelegate = (!args.length || typeof args[0] !== 'string');
- for (const el of this) {
+ for (let el of this) {
+ // dropdown will replace '<select class="ui dropdown"/>' to '<div class="ui dropdown"><select (hidden)></select><div class="menu">...</div></div>'
+ // so we need to correctly find the closest '.ui.dropdown' element, it is the real fomantic dropdown module.
+ el = el.closest('.ui.dropdown');
if (!el[ariaPatchKey]) {
- attachInit(el);
+ // the elements don't belong to the dropdown "module" and won't be reset
+ // so we only need to initialize them once.
+ attachInitElements(el);
}
- if (needDelegate) {
- delegateOne($(el));
+
+ // if the `$().dropdown()` is called without arguments, or it has non-string (object) argument,
+ // it means that such call will reset the dropdown "module" including internal settings,
+ // then we need to re-delegate the callbacks.
+ const $dropdown = $(el);
+ const dropdownModule = $dropdown.data('module-dropdown');
+ if (!dropdownModule.giteaDelegated) {
+ dropdownModule.giteaDelegated = true;
+ delegateDropdownModule($dropdown);
}
}
return ret;
@@ -37,8 +46,8 @@ function ariaDropdownFn(...args: Parameters<FomanticInitFunction>) {
// make the item has role=option/menuitem, add an id if there wasn't one yet, make items as non-focusable
// the elements inside the dropdown menu item should not be focusable, the focus should always be on the dropdown primary element.
function updateMenuItem(dropdown: HTMLElement, item: HTMLElement) {
- if (!item.id) item.id = generateAriaId();
- item.setAttribute('role', dropdown[ariaPatchKey].listItemRole);
+ if (!item.id) item.id = generateElemId('_aria_dropdown_item_');
+ item.setAttribute('role', (dropdown as any)[ariaPatchKey].listItemRole);
item.setAttribute('tabindex', '-1');
for (const el of item.querySelectorAll('a, input, button')) el.setAttribute('tabindex', '-1');
}
@@ -49,7 +58,7 @@ function updateMenuItem(dropdown: HTMLElement, item: HTMLElement) {
function updateSelectionLabel(label: HTMLElement) {
// the "label" is like this: "<a|div class="ui label" data-value="1">the-label-name <i|svg class="delete icon"/></a>"
if (!label.id) {
- label.id = generateAriaId();
+ label.id = generateElemId('_aria_dropdown_label_');
}
label.tabIndex = -1;
@@ -61,37 +70,17 @@ function updateSelectionLabel(label: HTMLElement) {
}
}
-function processMenuItems($dropdown, dropdownCall) {
- const hideEmptyDividers = dropdownCall('setting', 'hideDividers') === 'empty';
+function onDropdownAfterFiltered(this: any) {
+ const $dropdown = $(this).closest('.ui.dropdown'); // "this" can be the "ui dropdown" or "<select>"
+ const hideEmptyDividers = $dropdown.dropdown('setting', 'hideDividers') === 'empty';
const itemsMenu = $dropdown[0].querySelector('.scrolling.menu') || $dropdown[0].querySelector('.menu');
- if (hideEmptyDividers) hideScopedEmptyDividers(itemsMenu);
+ if (hideEmptyDividers && itemsMenu) hideScopedEmptyDividers(itemsMenu);
}
// delegate the dropdown's template functions and callback functions to add aria attributes.
-function delegateOne($dropdown: any) {
+function delegateDropdownModule($dropdown: any) {
const dropdownCall = fomanticDropdownFn.bind($dropdown);
- // If there is a "search input" in the "menu", Fomantic will only "focus the input" but not "toggle the menu" when the "dropdown icon" is clicked.
- // Actually, Fomantic UI doesn't support such layout/usage. It needs to patch the "focusSearch" / "blurSearch" functions to make sure it toggles the menu.
- const oldFocusSearch = dropdownCall('internal', 'focusSearch');
- const oldBlurSearch = dropdownCall('internal', 'blurSearch');
- // * If the "dropdown icon" is clicked, Fomantic calls "focusSearch", so show the menu
- dropdownCall('internal', 'focusSearch', function () { dropdownCall('show'); oldFocusSearch.call(this) });
- // * If the "dropdown icon" is clicked again when the menu is visible, Fomantic calls "blurSearch", so hide the menu
- dropdownCall('internal', 'blurSearch', function () { oldBlurSearch.call(this); dropdownCall('hide') });
-
- const oldFilterItems = dropdownCall('internal', 'filterItems');
- dropdownCall('internal', 'filterItems', function (...args: any[]) {
- oldFilterItems.call(this, ...args);
- processMenuItems($dropdown, dropdownCall);
- });
-
- const oldShow = dropdownCall('internal', 'show');
- dropdownCall('internal', 'show', function (...args: any[]) {
- oldShow.call(this, ...args);
- processMenuItems($dropdown, dropdownCall);
- });
-
// the "template" functions are used for dynamic creation (eg: AJAX)
const dropdownTemplates = {...dropdownCall('setting', 'templates'), t: performance.now()};
const dropdownTemplatesMenuOld = dropdownTemplates.menu;
@@ -110,7 +99,7 @@ function delegateOne($dropdown: any) {
// the `onLabelCreate` is used to add necessary aria attributes for dynamically created selection labels
const dropdownOnLabelCreateOld = dropdownCall('setting', 'onLabelCreate');
- dropdownCall('setting', 'onLabelCreate', function(value: any, text: string) {
+ dropdownCall('setting', 'onLabelCreate', function(this: any, value: any, text: string) {
const $label = dropdownOnLabelCreateOld.call(this, value, text);
updateSelectionLabel($label[0]);
return $label;
@@ -137,13 +126,13 @@ function delegateOne($dropdown: any) {
function attachStaticElements(dropdown: HTMLElement, focusable: HTMLElement, menu: HTMLElement) {
// prepare static dropdown menu list popup
if (!menu.id) {
- menu.id = generateAriaId();
+ menu.id = generateElemId('_aria_dropdown_menu_');
}
$(menu).find('> .item').each((_, item) => updateMenuItem(dropdown, item));
// this role could only be changed after its content is ready, otherwise some browsers+readers (like Chrome+AppleVoice) crash
- menu.setAttribute('role', dropdown[ariaPatchKey].listPopupRole);
+ menu.setAttribute('role', (dropdown as any)[ariaPatchKey].listPopupRole);
// prepare selection label items
for (const label of dropdown.querySelectorAll<HTMLElement>('.ui.label')) {
@@ -151,8 +140,8 @@ function attachStaticElements(dropdown: HTMLElement, focusable: HTMLElement, men
}
// make the primary element (focusable) aria-friendly
- focusable.setAttribute('role', focusable.getAttribute('role') ?? dropdown[ariaPatchKey].focusableRole);
- focusable.setAttribute('aria-haspopup', dropdown[ariaPatchKey].listPopupRole);
+ focusable.setAttribute('role', focusable.getAttribute('role') ?? (dropdown as any)[ariaPatchKey].focusableRole);
+ focusable.setAttribute('aria-haspopup', (dropdown as any)[ariaPatchKey].listPopupRole);
focusable.setAttribute('aria-controls', menu.id);
focusable.setAttribute('aria-expanded', 'false');
@@ -163,9 +152,8 @@ function attachStaticElements(dropdown: HTMLElement, focusable: HTMLElement, men
}
}
-function attachInit(dropdown: HTMLElement) {
- dropdown[ariaPatchKey] = {};
- if (dropdown.classList.contains('custom')) return;
+function attachInitElements(dropdown: HTMLElement) {
+ (dropdown as any)[ariaPatchKey] = {};
// Dropdown has 2 different focusing behaviors
// * with search input: the input is focused, and it works with aria-activedescendant pointing another sibling element.
@@ -204,9 +192,9 @@ function attachInit(dropdown: HTMLElement) {
// Since #19861 we have prepared the "combobox" solution, but didn't get enough time to put it into practice and test before.
const isComboBox = dropdown.querySelectorAll('input').length > 0;
- dropdown[ariaPatchKey].focusableRole = isComboBox ? 'combobox' : 'menu';
- dropdown[ariaPatchKey].listPopupRole = isComboBox ? 'listbox' : '';
- dropdown[ariaPatchKey].listItemRole = isComboBox ? 'option' : 'menuitem';
+ (dropdown as any)[ariaPatchKey].focusableRole = isComboBox ? 'combobox' : 'menu';
+ (dropdown as any)[ariaPatchKey].listPopupRole = isComboBox ? 'listbox' : '';
+ (dropdown as any)[ariaPatchKey].listItemRole = isComboBox ? 'option' : 'menuitem';
attachDomEvents(dropdown, focusable, menu);
attachStaticElements(dropdown, focusable, menu);
@@ -229,7 +217,7 @@ function attachDomEvents(dropdown: HTMLElement, focusable: HTMLElement, menu: HT
// if the popup is visible and has an active/selected item, use its id as aria-activedescendant
if (menuVisible) {
focusable.setAttribute('aria-activedescendant', active.id);
- } else if (dropdown[ariaPatchKey].listPopupRole === 'menu') {
+ } else if ((dropdown as any)[ariaPatchKey].listPopupRole === 'menu') {
// for menu, when the popup is hidden, no need to keep the aria-activedescendant, and clear the active/selected item
focusable.removeAttribute('aria-activedescendant');
active.classList.remove('active', 'selected');
@@ -239,12 +227,13 @@ function attachDomEvents(dropdown: HTMLElement, focusable: HTMLElement, menu: HT
dropdown.addEventListener('keydown', (e: KeyboardEvent) => {
// here it must use keydown event before dropdown's keyup handler, otherwise there is no Enter event in our keyup handler
if (e.key === 'Enter') {
- const dropdownCall = fomanticDropdownFn.bind($(dropdown));
- let $item = dropdownCall('get item', dropdownCall('get value'));
- if (!$item) $item = $(menu).find('> .item.selected'); // when dropdown filters items by input, there is no "value", so query the "selected" item
+ const elItem = menu.querySelector<HTMLElement>(':scope > .item.selected, .menu > .item.selected');
// if the selected item is clickable, then trigger the click event.
// we can not click any item without check, because Fomantic code might also handle the Enter event. that would result in double click.
- if ($item?.[0]?.matches('a, .js-aria-clickable')) $item[0].click();
+ if (elItem?.matches('a, .js-aria-clickable') && !elItem.matches('.tw-hidden, .filtered')) {
+ e.preventDefault();
+ elItem.click();
+ }
}
});
@@ -253,7 +242,7 @@ function attachDomEvents(dropdown: HTMLElement, focusable: HTMLElement, menu: HT
// when the popup is hiding, it's better to have a small "delay", because there is a Fomantic UI animation
// without the delay for hiding, the UI will be somewhat laggy and sometimes may get stuck in the animation.
const deferredRefreshAriaActiveItem = (delay = 0) => { setTimeout(refreshAriaActiveItem, delay) };
- dropdown[ariaPatchKey].deferredRefreshAriaActiveItem = deferredRefreshAriaActiveItem;
+ (dropdown as any)[ariaPatchKey].deferredRefreshAriaActiveItem = deferredRefreshAriaActiveItem;
dropdown.addEventListener('keyup', (e) => { if (e.key.startsWith('Arrow')) deferredRefreshAriaActiveItem(); });
// if the dropdown has been opened by focus, do not trigger the next click event again.
@@ -305,9 +294,11 @@ export function hideScopedEmptyDividers(container: Element) {
const visibleItems: Element[] = [];
const curScopeVisibleItems: Element[] = [];
let curScope: string = '', lastVisibleScope: string = '';
- const isScopedDivider = (item: Element) => item.matches('.divider') && item.hasAttribute('data-scope');
+ const isDivider = (item: Element) => item.classList.contains('divider');
+ const isScopedDivider = (item: Element) => isDivider(item) && item.hasAttribute('data-scope');
const hideDivider = (item: Element) => item.classList.add('hidden', 'transition'); // dropdown has its own classes to hide items
-
+ const showDivider = (item: Element) => item.classList.remove('hidden', 'transition');
+ const isHidden = (item: Element) => item.classList.contains('hidden') || item.classList.contains('filtered') || item.classList.contains('tw-hidden');
const handleScopeSwitch = (itemScope: string) => {
if (curScopeVisibleItems.length === 1 && isScopedDivider(curScopeVisibleItems[0])) {
hideDivider(curScopeVisibleItems[0]);
@@ -323,13 +314,16 @@ export function hideScopedEmptyDividers(container: Element) {
curScopeVisibleItems.length = 0;
};
+ // reset hidden dividers
+ queryElems(container, '.divider', showDivider);
+
// hide the scope dividers if the scope items are empty
for (const item of container.children) {
const itemScope = item.getAttribute('data-scope') || '';
if (itemScope !== curScope) {
handleScopeSwitch(itemScope);
}
- if (!item.classList.contains('filtered') && !item.classList.contains('tw-hidden')) {
+ if (!isHidden(item)) {
curScopeVisibleItems.push(item as HTMLElement);
}
}
@@ -337,20 +331,20 @@ export function hideScopedEmptyDividers(container: Element) {
// hide all leading and trailing dividers
while (visibleItems.length) {
- if (!visibleItems[0].matches('.divider')) break;
+ if (!isDivider(visibleItems[0])) break;
hideDivider(visibleItems[0]);
visibleItems.shift();
}
while (visibleItems.length) {
- if (!visibleItems[visibleItems.length - 1].matches('.divider')) break;
+ if (!isDivider(visibleItems[visibleItems.length - 1])) break;
hideDivider(visibleItems[visibleItems.length - 1]);
visibleItems.pop();
}
// hide all duplicate dividers, hide current divider if next sibling is still divider
// no need to update "visibleItems" array since this is the last loop
- for (const item of visibleItems) {
- if (!item.matches('.divider')) continue;
- if (item.nextElementSibling?.matches('.divider')) hideDivider(item);
+ for (let i = 0; i < visibleItems.length - 1; i++) {
+ if (!visibleItems[i].matches('.divider')) continue;
+ if (visibleItems[i + 1].matches('.divider')) hideDivider(visibleItems[i]);
}
}
@@ -363,7 +357,7 @@ function onResponseKeepSelectedItem(dropdown: typeof $|HTMLElement, selectedValu
// then the dropdown only shows other items and will select another (wrong) one.
// It can't be easily fix by using setTimeout(patch, 0) in `onResponse` because the `onResponse` is called before another `setTimeout(..., timeLeft)`
// Fortunately, the "timeLeft" is controlled by "loadingDuration" which is always zero at the moment, so we can use `setTimeout(..., 10)`
- const elDropdown = (dropdown instanceof HTMLElement) ? dropdown : dropdown[0];
+ const elDropdown = (dropdown instanceof HTMLElement) ? dropdown : (dropdown as any)[0];
setTimeout(() => {
queryElems(elDropdown, `.menu .item[data-value="${CSS.escape(selectedValue)}"].filtered`, (el) => el.classList.remove('filtered'));
$(elDropdown).dropdown('set selected', selectedValue ?? '');
diff --git a/web_src/js/modules/fomantic/modal.ts b/web_src/js/modules/fomantic/modal.ts
index fb80047d01..a96c7785e1 100644
--- a/web_src/js/modules/fomantic/modal.ts
+++ b/web_src/js/modules/fomantic/modal.ts
@@ -1,5 +1,7 @@
import $ from 'jquery';
import type {FomanticInitFunction} from '../../types.ts';
+import {queryElems} from '../../utils/dom.ts';
+import {hideToastsFrom} from '../toast.ts';
const fomanticModalFn = $.fn.modal;
@@ -8,11 +10,13 @@ export function initAriaModalPatch() {
if ($.fn.modal === ariaModalFn) throw new Error('initAriaModalPatch could only be called once');
$.fn.modal = ariaModalFn;
(ariaModalFn as FomanticInitFunction).settings = fomanticModalFn.settings;
+ $.fn.fomanticExt.onModalBeforeHidden = onModalBeforeHidden;
+ $.fn.modal.settings.onApprove = onModalApproveDefault;
}
// the patched `$.fn.modal` modal function
// * it does the one-time attaching on the first call
-function ariaModalFn(...args: Parameters<FomanticInitFunction>) {
+function ariaModalFn(this: any, ...args: Parameters<FomanticInitFunction>) {
const ret = fomanticModalFn.apply(this, args);
if (args[0] === 'show' || args[0]?.autoShow) {
for (const el of this) {
@@ -27,3 +31,33 @@ function ariaModalFn(...args: Parameters<FomanticInitFunction>) {
}
return ret;
}
+
+function onModalBeforeHidden(this: any) {
+ const $modal = $(this);
+ const elModal = $modal[0];
+ hideToastsFrom(elModal.closest('.ui.dimmer') ?? document.body);
+
+ // reset the form after the modal is hidden, after other modal events and handlers (e.g. "onApprove", form submit)
+ setTimeout(() => {
+ queryElems(elModal, 'form', (form: HTMLFormElement) => form.reset());
+ }, 0);
+}
+
+function onModalApproveDefault(this: any) {
+ const $modal = $(this);
+ const selectors = $modal.modal('setting', 'selector');
+ const elModal = $modal[0];
+ const elApprove = elModal.querySelector(selectors.approve);
+ const elForm = elApprove?.closest('form');
+ if (!elForm) return true; // no form, just allow closing the modal
+
+ // "form-fetch-action" can handle network errors gracefully,
+ // so keep the modal dialog to make users can re-submit the form if anything wrong happens.
+ if (elForm.matches('.form-fetch-action')) return false;
+
+ // There is an abuse for the "modal" + "form" combination, the "Approve" button is a traditional form submit button in the form.
+ // Then "approve" and "submit" occur at the same time, the modal will be closed immediately before the form is submitted.
+ // So here we prevent the modal from closing automatically by returning false, add the "is-loading" class to the form element.
+ elForm.classList.add('is-loading');
+ return false;
+}
diff --git a/web_src/js/modules/fomantic/tab.ts b/web_src/js/modules/fomantic/tab.ts
new file mode 100644
index 0000000000..ceae9dd098
--- /dev/null
+++ b/web_src/js/modules/fomantic/tab.ts
@@ -0,0 +1,19 @@
+import $ from 'jquery';
+import {queryElemSiblings} from '../../utils/dom.ts';
+
+export function initFomanticTab() {
+ $.fn.tab = function (this: any) {
+ for (const elBtn of this) {
+ const tabName = elBtn.getAttribute('data-tab');
+ if (!tabName) continue;
+ elBtn.addEventListener('click', () => {
+ const elTab = document.querySelector(`.ui.tab[data-tab="${tabName}"]`);
+ queryElemSiblings(elTab, `.ui.tab`, (el) => el.classList.remove('active'));
+ queryElemSiblings(elBtn, `[data-tab]`, (el) => el.classList.remove('active'));
+ elBtn.classList.add('active');
+ elTab.classList.add('active');
+ });
+ }
+ return this;
+ };
+}
diff --git a/web_src/js/modules/init.ts b/web_src/js/modules/init.ts
new file mode 100644
index 0000000000..538fafd83f
--- /dev/null
+++ b/web_src/js/modules/init.ts
@@ -0,0 +1,26 @@
+export class InitPerformanceTracer {
+ results: {name: string, dur: number}[] = [];
+ recordCall(name: string, func: ()=>void) {
+ const start = performance.now();
+ func();
+ this.results.push({name, dur: performance.now() - start});
+ }
+ printResults() {
+ this.results = this.results.sort((a, b) => b.dur - a.dur);
+ for (let i = 0; i < 20 && i < this.results.length; i++) {
+ console.info(`performance trace: ${this.results[i].name} ${this.results[i].dur.toFixed(3)}`);
+ }
+ }
+}
+
+export function callInitFunctions(functions: (() => any)[]): InitPerformanceTracer | null {
+ // Start performance trace by accessing a URL by "https://localhost/?_ui_performance_trace=1" or "https://localhost/?key=value&_ui_performance_trace=1"
+ // It is a quick check, no side effect so no need to do slow URL parsing.
+ const perfTracer = !window.location.search.includes('_ui_performance_trace=1') ? null : new InitPerformanceTracer();
+ if (perfTracer) {
+ for (const func of functions) perfTracer.recordCall(func.name, func);
+ } else {
+ for (const func of functions) func();
+ }
+ return perfTracer;
+}
diff --git a/web_src/js/modules/observer.ts b/web_src/js/modules/observer.ts
new file mode 100644
index 0000000000..3305c2f29d
--- /dev/null
+++ b/web_src/js/modules/observer.ts
@@ -0,0 +1,112 @@
+import {isDocumentFragmentOrElementNode} from '../utils/dom.ts';
+import type {Promisable} from 'type-fest';
+import type {InitPerformanceTracer} from './init.ts';
+
+let globalSelectorObserverInited = false;
+
+type SelectorHandler = {selector: string, handler: (el: HTMLElement) => void};
+const selectorHandlers: SelectorHandler[] = [];
+
+type GlobalEventFunc<T extends HTMLElement, E extends Event> = (el: T, e: E) => Promisable<void>;
+const globalEventFuncs: Record<string, GlobalEventFunc<HTMLElement, Event>> = {};
+
+type GlobalInitFunc<T extends HTMLElement> = (el: T) => Promisable<void>;
+const globalInitFuncs: Record<string, GlobalInitFunc<HTMLElement>> = {};
+
+// It handles the global events for all `<div data-global-click="onSomeElemClick"></div>` elements.
+export function registerGlobalEventFunc<T extends HTMLElement, E extends Event>(event: string, name: string, func: GlobalEventFunc<T, E>) {
+ globalEventFuncs[`${event}:${name}`] = func as GlobalEventFunc<HTMLElement, Event>;
+}
+
+// It handles the global init functions by a selector, for example:
+// > registerGlobalSelectorObserver('.ui.dropdown:not(.custom)', (el) => { initDropdown(el, ...) });
+// ATTENTION: For most cases, it's recommended to use registerGlobalInitFunc instead,
+// Because this selector-based approach is less efficient and less maintainable.
+// But if there are already a lot of elements on many pages, this selector-based approach is more convenient for exiting code.
+export function registerGlobalSelectorFunc(selector: string, handler: (el: HTMLElement) => void) {
+ selectorHandlers.push({selector, handler});
+ // Then initAddedElementObserver will call this handler for all existing elements after all handlers are added.
+ // This approach makes the init stage only need to do one "querySelectorAll".
+ if (!globalSelectorObserverInited) return;
+ for (const el of document.querySelectorAll<HTMLElement>(selector)) {
+ handler(el);
+ }
+}
+
+// It handles the global init functions for all `<div data-global-int="initSomeElem"></div>` elements.
+export function registerGlobalInitFunc<T extends HTMLElement>(name: string, handler: GlobalInitFunc<T>) {
+ globalInitFuncs[name] = handler as GlobalInitFunc<HTMLElement>;
+ // The "global init" functions are managed internally and called by callGlobalInitFunc
+ // They must be ready before initGlobalSelectorObserver is called.
+ if (globalSelectorObserverInited) throw new Error('registerGlobalInitFunc() must be called before initGlobalSelectorObserver()');
+}
+
+function callGlobalInitFunc(el: HTMLElement) {
+ const initFunc = el.getAttribute('data-global-init');
+ const func = globalInitFuncs[initFunc];
+ if (!func) throw new Error(`Global init function "${initFunc}" not found`);
+
+ // when an element node is removed and added again, it should not be re-initialized again.
+ type GiteaGlobalInitElement = Partial<HTMLElement> & {_giteaGlobalInited: boolean};
+ if ((el as GiteaGlobalInitElement)._giteaGlobalInited) return;
+ (el as GiteaGlobalInitElement)._giteaGlobalInited = true;
+
+ func(el);
+}
+
+function attachGlobalEvents() {
+ // add global "[data-global-click]" event handler
+ document.addEventListener('click', (e) => {
+ const elem = (e.target as HTMLElement).closest<HTMLElement>('[data-global-click]');
+ if (!elem) return;
+ const funcName = elem.getAttribute('data-global-click');
+ const func = globalEventFuncs[`click:${funcName}`];
+ if (!func) throw new Error(`Global event function "click:${funcName}" not found`);
+ func(elem, e);
+ });
+}
+
+export function initGlobalSelectorObserver(perfTracer?: InitPerformanceTracer): void {
+ if (globalSelectorObserverInited) throw new Error('initGlobalSelectorObserver() already called');
+ globalSelectorObserverInited = true;
+
+ attachGlobalEvents();
+
+ selectorHandlers.push({selector: '[data-global-init]', handler: callGlobalInitFunc});
+ const observer = new MutationObserver((mutationList) => {
+ const len = mutationList.length;
+ for (let i = 0; i < len; i++) {
+ const mutation = mutationList[i];
+ const len = mutation.addedNodes.length;
+ for (let i = 0; i < len; i++) {
+ const addedNode = mutation.addedNodes[i] as HTMLElement;
+ if (!isDocumentFragmentOrElementNode(addedNode)) continue;
+
+ for (const {selector, handler} of selectorHandlers) {
+ if (addedNode.matches(selector)) {
+ handler(addedNode);
+ }
+ for (const el of addedNode.querySelectorAll<HTMLElement>(selector)) {
+ handler(el);
+ }
+ }
+ }
+ }
+ });
+ if (perfTracer) {
+ for (const {selector, handler} of selectorHandlers) {
+ perfTracer.recordCall(`initGlobalSelectorObserver ${selector}`, () => {
+ for (const el of document.querySelectorAll<HTMLElement>(selector)) {
+ handler(el);
+ }
+ });
+ }
+ } else {
+ for (const {selector, handler} of selectorHandlers) {
+ for (const el of document.querySelectorAll<HTMLElement>(selector)) {
+ handler(el);
+ }
+ }
+ }
+ observer.observe(document, {subtree: true, childList: true});
+}
diff --git a/web_src/js/modules/stores.ts b/web_src/js/modules/stores.ts
deleted file mode 100644
index 942a7bc508..0000000000
--- a/web_src/js/modules/stores.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import {reactive} from 'vue';
-import type {Reactive} from 'vue';
-
-let diffTreeStoreReactive: Reactive<Record<string, any>>;
-export function diffTreeStore() {
- if (!diffTreeStoreReactive) {
- diffTreeStoreReactive = reactive(window.config.pageData.diffFileInfo);
- window.config.pageData.diffFileInfo = diffTreeStoreReactive;
- }
- return diffTreeStoreReactive;
-}
diff --git a/web_src/js/modules/tippy.ts b/web_src/js/modules/tippy.ts
index aaaf580de1..2a1d998d76 100644
--- a/web_src/js/modules/tippy.ts
+++ b/web_src/js/modules/tippy.ts
@@ -2,6 +2,7 @@ import tippy, {followCursor} from 'tippy.js';
import {isDocumentFragmentOrElementNode} from '../utils/dom.ts';
import {formatDatetime} from '../utils/time.ts';
import type {Content, Instance, Placement, Props} from 'tippy.js';
+import {html} from '../utils/html.ts';
type TippyOpts = {
role?: string,
@@ -9,7 +10,7 @@ type TippyOpts = {
} & Partial<Props>;
const visibleInstances = new Set<Instance>();
-const arrowSvg = `<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`;
+const arrowSvg = html`<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`;
export function createTippy(target: Element, opts: TippyOpts = {}): Instance {
// the callback functions should be destructured from opts,
@@ -40,18 +41,20 @@ export function createTippy(target: Element, opts: TippyOpts = {}): Instance {
}
}
visibleInstances.add(instance);
+ target.setAttribute('aria-controls', instance.popper.id);
return onShow?.(instance);
},
- arrow: arrow || (theme === 'bare' ? false : arrowSvg),
+ arrow: arrow ?? (theme === 'bare' ? false : arrowSvg),
// HTML role attribute, ideally the default role would be "popover" but it does not exist
role: role || 'menu',
// CSS theme, either "default", "tooltip", "menu", "box-with-header" or "bare"
theme: theme || role || 'default',
+ offset: [0, arrow ? 10 : 6],
plugins: [followCursor],
...other,
} satisfies Partial<Props>);
- if (role === 'menu') {
+ if (instance.props.role === 'menu') {
target.setAttribute('aria-haspopup', 'true');
}
@@ -121,7 +124,7 @@ function switchTitleToTooltip(target: Element): void {
* Some browsers like PaleMoon don't support "addEventListener('mouseenter', capture)"
* The tippy by default uses "mouseenter" event to show, so we use "mouseover" event to switch to tippy
*/
-function lazyTooltipOnMouseHover(e: Event): void {
+function lazyTooltipOnMouseHover(this: HTMLElement, e: Event): void {
e.target.removeEventListener('mouseover', lazyTooltipOnMouseHover, true);
attachTooltip(this);
}
@@ -179,13 +182,25 @@ export function initGlobalTooltips(): void {
}
export function showTemporaryTooltip(target: Element, content: Content): void {
- // if the target is inside a dropdown, the menu will be hidden soon
- // so display the tooltip on the dropdown instead
- target = target.closest('.ui.dropdown') || target;
- const tippy = target._tippy ?? attachTooltip(target, content);
- tippy.setContent(content);
- if (!tippy.state.isShown) tippy.show();
- tippy.setProps({
+ // if the target is inside a dropdown or tippy popup, the menu will be hidden soon
+ // so display the tooltip on the "aria-controls" element or dropdown instead
+ let refClientRect: DOMRect;
+ const popupTippyId = target.closest(`[data-tippy-root]`)?.id;
+ if (popupTippyId) {
+ // for example, the "Copy Permalink" button in the "File View" page for the selected lines
+ target = document.body;
+ refClientRect = document.querySelector(`[aria-controls="${CSS.escape(popupTippyId)}"]`)?.getBoundingClientRect();
+ refClientRect = refClientRect ?? new DOMRect(0, 0, 0, 0); // fallback to empty rect if not found, tippy doesn't accept null
+ } else {
+ // for example, the "Copy Link" button in the issue header dropdown menu
+ target = target.closest('.ui.dropdown') ?? target;
+ refClientRect = target.getBoundingClientRect();
+ }
+ const tooltipTippy = target._tippy ?? attachTooltip(target, content);
+ tooltipTippy.setContent(content);
+ tooltipTippy.setProps({getReferenceClientRect: () => refClientRect});
+ if (!tooltipTippy.state.isShown) tooltipTippy.show();
+ tooltipTippy.setProps({
onHidden: (tippy) => {
// reset the default tooltip content, if no default, then this temporary tooltip could be destroyed
if (!attachTooltip(target)) {
diff --git a/web_src/js/modules/toast.ts b/web_src/js/modules/toast.ts
index 36e2321743..ed807a4977 100644
--- a/web_src/js/modules/toast.ts
+++ b/web_src/js/modules/toast.ts
@@ -1,6 +1,6 @@
-import {htmlEscape} from 'escape-goat';
+import {htmlEscape} from '../utils/html.ts';
import {svg} from '../svg.ts';
-import {animateOnce, showElem} from '../utils/dom.ts';
+import {animateOnce, queryElems, showElem} from '../utils/dom.ts';
import Toastify from 'toastify-js'; // don't use "async import", because when network error occurs, the "async import" also fails and nothing is shown
import type {Intent} from '../types.ts';
import type {SvgName} from '../svg.ts';
@@ -37,17 +37,20 @@ const levels: ToastLevels = {
type ToastOpts = {
useHtmlBody?: boolean,
- preventDuplicates?: boolean,
+ preventDuplicates?: boolean | string,
} & Options;
+type ToastifyElement = HTMLElement & {_giteaToastifyInstance?: Toast };
+
// See https://github.com/apvarun/toastify-js#api for options
function showToast(message: string, level: Intent, {gravity, position, duration, useHtmlBody, preventDuplicates = true, ...other}: ToastOpts = {}): Toast {
const body = useHtmlBody ? String(message) : htmlEscape(message);
- const key = `${level}-${body}`;
+ const parent = document.querySelector('.ui.dimmer.active') ?? document.body;
+ const duplicateKey = preventDuplicates ? (preventDuplicates === true ? `${level}-${body}` : preventDuplicates) : '';
- // prevent showing duplicate toasts with same level and message, and give a visual feedback for end users
+ // prevent showing duplicate toasts with the same level and message, and give visual feedback for end users
if (preventDuplicates) {
- const toastEl = document.querySelector(`.toastify[data-toast-unique-key="${CSS.escape(key)}"]`);
+ const toastEl = parent.querySelector(`:scope > .toastify.on[data-toast-unique-key="${CSS.escape(duplicateKey)}"]`);
if (toastEl) {
const toastDupNumEl = toastEl.querySelector('.toast-duplicate-number');
showElem(toastDupNumEl);
@@ -59,6 +62,7 @@ function showToast(message: string, level: Intent, {gravity, position, duration,
const {icon, background, duration: levelDuration} = levels[level ?? 'info'];
const toast = Toastify({
+ selector: parent,
text: `
<div class='toast-icon'>${svg(icon)}</div>
<div class='toast-body'><span class="toast-duplicate-number tw-hidden">1</span>${body}</div>
@@ -74,7 +78,8 @@ function showToast(message: string, level: Intent, {gravity, position, duration,
toast.showToast();
toast.toastElement.querySelector('.toast-close').addEventListener('click', () => toast.hideToast());
- toast.toastElement.setAttribute('data-toast-unique-key', key);
+ toast.toastElement.setAttribute('data-toast-unique-key', duplicateKey);
+ (toast.toastElement as ToastifyElement)._giteaToastifyInstance = toast;
return toast;
}
@@ -89,3 +94,15 @@ export function showWarningToast(message: string, opts?: ToastOpts): Toast {
export function showErrorToast(message: string, opts?: ToastOpts): Toast {
return showToast(message, 'error', opts);
}
+
+function hideToastByElement(el: Element): void {
+ (el as ToastifyElement)?._giteaToastifyInstance?.hideToast();
+}
+
+export function hideToastsFrom(parent: Element): void {
+ queryElems(parent, ':scope > .toastify.on', hideToastByElement);
+}
+
+export function hideToastsAll(): void {
+ queryElems(document, '.toastify.on', hideToastByElement);
+}
diff --git a/web_src/js/render/pdf.ts b/web_src/js/render/pdf.ts
deleted file mode 100644
index f31f161e6e..0000000000
--- a/web_src/js/render/pdf.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import {htmlEscape} from 'escape-goat';
-
-export async function initPdfViewer() {
- const els = document.querySelectorAll('.pdf-content');
- if (!els.length) return;
-
- const pdfobject = await import(/* webpackChunkName: "pdfobject" */'pdfobject');
-
- for (const el of els) {
- const src = el.getAttribute('data-src');
- const fallbackText = el.getAttribute('data-fallback-button-text');
- pdfobject.embed(src, el, {
- fallbackLink: htmlEscape`
- <a role="button" class="ui basic button pdf-fallback-button" href="[url]">${fallbackText}</a>
- `,
- });
- el.classList.remove('is-loading');
- }
-}
diff --git a/web_src/js/render/plugin.ts b/web_src/js/render/plugin.ts
new file mode 100644
index 0000000000..a8dd0a7c05
--- /dev/null
+++ b/web_src/js/render/plugin.ts
@@ -0,0 +1,10 @@
+export type FileRenderPlugin = {
+ // unique plugin name
+ name: string;
+
+ // test if plugin can handle a specified file
+ canHandle: (filename: string, mimeType: string) => boolean;
+
+ // render file content
+ render: (container: HTMLElement, fileUrl: string, options?: any) => Promise<void>;
+}
diff --git a/web_src/js/render/plugins/3d-viewer.ts b/web_src/js/render/plugins/3d-viewer.ts
new file mode 100644
index 0000000000..2a0929359d
--- /dev/null
+++ b/web_src/js/render/plugins/3d-viewer.ts
@@ -0,0 +1,60 @@
+import type {FileRenderPlugin} from '../plugin.ts';
+import {extname} from '../../utils.ts';
+
+// support common 3D model file formats, use online-3d-viewer library for rendering
+
+// eslint-disable-next-line multiline-comment-style
+/* a simple text STL file example:
+solid SimpleTriangle
+ facet normal 0 0 1
+ outer loop
+ vertex 0 0 0
+ vertex 1 0 0
+ vertex 0 1 0
+ endloop
+ endfacet
+endsolid SimpleTriangle
+*/
+
+export function newRenderPlugin3DViewer(): FileRenderPlugin {
+ // Some extensions are text-based formats:
+ // .3mf .amf .brep: XML
+ // .fbx: XML or BINARY
+ // .dae .gltf: JSON
+ // .ifc, .igs, .iges, .stp, .step are: TEXT
+ // .stl .ply: TEXT or BINARY
+ // .obj .off .wrl: TEXT
+ // So we need to be able to render when the file is recognized as plaintext file by backend.
+ //
+ // It needs more logic to make it overall right (render a text 3D model automatically):
+ // we need to distinguish the ambiguous filename extensions.
+ // For example: "*.obj, *.off, *.step" might be or not be a 3D model file.
+ // So when it is a text file, we can't assume that "we only render it by 3D plugin",
+ // otherwise the end users would be impossible to view its real content when the file is not a 3D model.
+ const SUPPORTED_EXTENSIONS = [
+ '.3dm', '.3ds', '.3mf', '.amf', '.bim', '.brep',
+ '.dae', '.fbx', '.fcstd', '.glb', '.gltf',
+ '.ifc', '.igs', '.iges', '.stp', '.step',
+ '.stl', '.obj', '.off', '.ply', '.wrl',
+ ];
+
+ return {
+ name: '3d-model-viewer',
+
+ canHandle(filename: string, _mimeType: string): boolean {
+ const ext = extname(filename).toLowerCase();
+ return SUPPORTED_EXTENSIONS.includes(ext);
+ },
+
+ async render(container: HTMLElement, fileUrl: string): Promise<void> {
+ // TODO: height and/or max-height?
+ const OV = await import(/* webpackChunkName: "online-3d-viewer" */'online-3d-viewer');
+ const viewer = new OV.EmbeddedViewer(container, {
+ backgroundColor: new OV.RGBAColor(59, 68, 76, 0),
+ defaultColor: new OV.RGBColor(65, 131, 196),
+ edgeSettings: new OV.EdgeSettings(false, new OV.RGBColor(0, 0, 0), 1),
+ });
+ viewer.LoadModelFromUrlList([fileUrl]);
+ },
+ };
+}
diff --git a/web_src/js/render/plugins/pdf-viewer.ts b/web_src/js/render/plugins/pdf-viewer.ts
new file mode 100644
index 0000000000..40623be055
--- /dev/null
+++ b/web_src/js/render/plugins/pdf-viewer.ts
@@ -0,0 +1,20 @@
+import type {FileRenderPlugin} from '../plugin.ts';
+
+export function newRenderPluginPdfViewer(): FileRenderPlugin {
+ return {
+ name: 'pdf-viewer',
+
+ canHandle(filename: string, _mimeType: string): boolean {
+ return filename.toLowerCase().endsWith('.pdf');
+ },
+
+ async render(container: HTMLElement, fileUrl: string): Promise<void> {
+ const PDFObject = await import(/* webpackChunkName: "pdfobject" */'pdfobject');
+ // TODO: the PDFObject library does not support dynamic height adjustment,
+ container.style.height = `${window.innerHeight - 100}px`;
+ if (!PDFObject.default.embed(fileUrl, container)) {
+ throw new Error('Unable to render the PDF file');
+ }
+ },
+ };
+}
diff --git a/web_src/js/standalone/devtest.ts b/web_src/js/standalone/devtest.ts
index 3489697a2f..e6baf6c9ce 100644
--- a/web_src/js/standalone/devtest.ts
+++ b/web_src/js/standalone/devtest.ts
@@ -1,7 +1,7 @@
import {showInfoToast, showWarningToast, showErrorToast} from '../modules/toast.ts';
function initDevtestToast() {
- const levelMap = {info: showInfoToast, warning: showWarningToast, error: showErrorToast};
+ const levelMap: Record<string, any> = {info: showInfoToast, warning: showWarningToast, error: showErrorToast};
for (const el of document.querySelectorAll('.toast-test-button')) {
el.addEventListener('click', () => {
const level = el.getAttribute('data-toast-level');
diff --git a/web_src/js/standalone/swagger.ts b/web_src/js/standalone/swagger.ts
index 63b676b2ea..4b17ba21a8 100644
--- a/web_src/js/standalone/swagger.ts
+++ b/web_src/js/standalone/swagger.ts
@@ -14,7 +14,7 @@ window.addEventListener('load', async () => {
return 0;
});
- const ui = SwaggerUI({
+ SwaggerUI({
spec,
dom_id: '#swagger-ui',
deepLinking: true,
@@ -27,6 +27,4 @@ window.addEventListener('load', async () => {
SwaggerUI.plugins.DownloadUrl,
],
});
-
- window.ui = ui;
});
diff --git a/web_src/js/svg.test.ts b/web_src/js/svg.test.ts
index 7f3e0496ec..715b739a82 100644
--- a/web_src/js/svg.test.ts
+++ b/web_src/js/svg.test.ts
@@ -16,12 +16,11 @@ test('svgParseOuterInner', () => {
test('SvgIcon', () => {
const root = document.createElement('div');
- createApp({render: () => h(SvgIcon, {name: 'octicon-link', size: 24, class: 'base', className: 'extra'})}).mount(root);
+ createApp({render: () => h(SvgIcon, {name: 'octicon-link', size: 24, class: 'base'})}).mount(root);
const node = root.firstChild as Element;
expect(node.nodeName).toEqual('svg');
expect(node.getAttribute('width')).toEqual('24');
expect(node.getAttribute('height')).toEqual('24');
expect(node.classList.contains('octicon-link')).toBeTruthy();
expect(node.classList.contains('base')).toBeTruthy();
- expect(node.classList.contains('extra')).toBeTruthy();
});
diff --git a/web_src/js/svg.ts b/web_src/js/svg.ts
index 90b12fa87d..50c9536f37 100644
--- a/web_src/js/svg.ts
+++ b/web_src/js/svg.ts
@@ -1,5 +1,6 @@
-import {h} from 'vue';
+import {defineComponent, h, type PropType} from 'vue';
import {parseDom, serializeXml} from './utils.ts';
+import {html, htmlRaw} from './utils/html.ts';
import giteaDoubleChevronLeft from '../../public/assets/img/svg/gitea-double-chevron-left.svg';
import giteaDoubleChevronRight from '../../public/assets/img/svg/gitea-double-chevron-right.svg';
import giteaEmptyCheckbox from '../../public/assets/img/svg/gitea-empty-checkbox.svg';
@@ -28,12 +29,15 @@ import octiconEye from '../../public/assets/img/svg/octicon-eye.svg';
import octiconFile from '../../public/assets/img/svg/octicon-file.svg';
import octiconFileDirectoryFill from '../../public/assets/img/svg/octicon-file-directory-fill.svg';
import octiconFileDirectoryOpenFill from '../../public/assets/img/svg/octicon-file-directory-open-fill.svg';
+import octiconFileSubmodule from '../../public/assets/img/svg/octicon-file-submodule.svg';
+import octiconFileSymlinkFile from '../../public/assets/img/svg/octicon-file-symlink-file.svg';
import octiconFilter from '../../public/assets/img/svg/octicon-filter.svg';
import octiconGear from '../../public/assets/img/svg/octicon-gear.svg';
import octiconGitBranch from '../../public/assets/img/svg/octicon-git-branch.svg';
import octiconGitCommit from '../../public/assets/img/svg/octicon-git-commit.svg';
import octiconGitMerge from '../../public/assets/img/svg/octicon-git-merge.svg';
import octiconGitPullRequest from '../../public/assets/img/svg/octicon-git-pull-request.svg';
+import octiconGitPullRequestClosed from '../../public/assets/img/svg/octicon-git-pull-request-closed.svg';
import octiconGitPullRequestDraft from '../../public/assets/img/svg/octicon-git-pull-request-draft.svg';
import octiconGrabber from '../../public/assets/img/svg/octicon-grabber.svg';
import octiconHeading from '../../public/assets/img/svg/octicon-heading.svg';
@@ -104,12 +108,15 @@ const svgs = {
'octicon-file': octiconFile,
'octicon-file-directory-fill': octiconFileDirectoryFill,
'octicon-file-directory-open-fill': octiconFileDirectoryOpenFill,
+ 'octicon-file-submodule': octiconFileSubmodule,
+ 'octicon-file-symlink-file': octiconFileSymlinkFile,
'octicon-filter': octiconFilter,
'octicon-gear': octiconGear,
'octicon-git-branch': octiconGitBranch,
'octicon-git-commit': octiconGitCommit,
'octicon-git-merge': octiconGitMerge,
'octicon-git-pull-request': octiconGitPullRequest,
+ 'octicon-git-pull-request-closed': octiconGitPullRequestClosed,
'octicon-git-pull-request-draft': octiconGitPullRequestDraft,
'octicon-grabber': octiconGrabber,
'octicon-heading': octiconHeading,
@@ -192,19 +199,18 @@ export function svgParseOuterInner(name: SvgName) {
return {svgOuter, svgInnerHtml};
}
-export const SvgIcon = {
+export const SvgIcon = defineComponent({
name: 'SvgIcon',
props: {
- name: {type: String, required: true},
+ name: {type: String as PropType<SvgName>, required: true},
size: {type: Number, default: 16},
- className: {type: String, default: ''},
symbolId: {type: String},
},
render() {
let {svgOuter, svgInnerHtml} = svgParseOuterInner(this.name);
// https://vuejs.org/guide/extras/render-function.html#creating-vnodes
// the `^` is used for attr, set SVG attributes like 'width', `aria-hidden`, `viewBox`, etc
- const attrs = {};
+ const attrs: Record<string, any> = {};
for (const attr of svgOuter.attributes) {
if (attr.name === 'class') continue;
attrs[`^${attr.name}`] = attr.value;
@@ -212,18 +218,10 @@ export const SvgIcon = {
attrs[`^width`] = this.size;
attrs[`^height`] = this.size;
- // make the <SvgIcon class="foo" class-name="bar"> classes work together
- const classes = [];
- for (const cls of svgOuter.classList) {
- classes.push(cls);
- }
- // TODO: drop the `className/class-name` prop in the future, only use "class" prop
- if (this.className) {
- classes.push(...this.className.split(/\s+/).filter(Boolean));
- }
+ const classes = Array.from(svgOuter.classList);
if (this.symbolId) {
classes.push('tw-hidden', 'svg-symbol-container');
- svgInnerHtml = `<symbol id="${this.symbolId}" viewBox="${attrs['^viewBox']}">${svgInnerHtml}</symbol>`;
+ svgInnerHtml = html`<symbol id="${this.symbolId}" viewBox="${attrs['^viewBox']}">${htmlRaw(svgInnerHtml)}</symbol>`;
}
// create VNode
return h('svg', {
@@ -232,4 +230,4 @@ export const SvgIcon = {
innerHTML: svgInnerHtml,
});
},
-};
+});
diff --git a/web_src/js/types.ts b/web_src/js/types.ts
index e7c9ac0df4..1b5e652f66 100644
--- a/web_src/js/types.ts
+++ b/web_src/js/types.ts
@@ -22,6 +22,8 @@ export type Config = {
i18n: Record<string, string>,
}
+export type IntervalId = ReturnType<typeof setInterval>;
+
export type Intent = 'error' | 'warning' | 'info';
export type RequestData = string | FormData | URLSearchParams | Record<string, any>;
@@ -30,6 +32,11 @@ export type RequestOpts = {
data?: RequestData,
} & RequestInit;
+export type RepoOwnerPathInfo = {
+ ownerName: string,
+ repoName: string,
+}
+
export type IssuePathInfo = {
ownerName: string,
repoName: string,
diff --git a/web_src/js/utils.test.ts b/web_src/js/utils.test.ts
index bbe328f658..f1025471a4 100644
--- a/web_src/js/utils.test.ts
+++ b/web_src/js/utils.test.ts
@@ -1,9 +1,15 @@
import {
- basename, extname, isObject, stripTags, parseIssueHref,
+ dirname, basename, extname, isObject, stripTags, parseIssueHref,
parseUrl, translateMonth, translateDay, blobToDataURI,
- toAbsoluteUrl, encodeURLEncodedBase64, decodeURLEncodedBase64, isImageFile, isVideoFile, parseIssueNewHref,
+ toAbsoluteUrl, encodeURLEncodedBase64, decodeURLEncodedBase64, isImageFile, isVideoFile, parseRepoOwnerPathInfo,
} from './utils.ts';
+test('dirname', () => {
+ expect(dirname('/path/to/file.js')).toEqual('/path/to');
+ expect(dirname('/path/to')).toEqual('/path');
+ expect(dirname('file.js')).toEqual('');
+});
+
test('basename', () => {
expect(basename('/path/to/file.js')).toEqual('file.js');
expect(basename('/path/to/file')).toEqual('file');
@@ -45,11 +51,14 @@ test('parseIssueHref', () => {
expect(parseIssueHref('')).toEqual({ownerName: undefined, repoName: undefined, type: undefined, index: undefined});
});
-test('parseIssueNewHref', () => {
- expect(parseIssueNewHref('/owner/repo/issues/new')).toEqual({ownerName: 'owner', repoName: 'repo', pathType: 'issues'});
- expect(parseIssueNewHref('/owner/repo/issues/new?query')).toEqual({ownerName: 'owner', repoName: 'repo', pathType: 'issues'});
- expect(parseIssueNewHref('/sub/owner/repo/issues/new#hash')).toEqual({ownerName: 'owner', repoName: 'repo', pathType: 'issues'});
- expect(parseIssueNewHref('/sub/owner/repo/compare/feature/branch-1...fix/branch-2')).toEqual({ownerName: 'owner', repoName: 'repo', pathType: 'pulls'});
+test('parseRepoOwnerPathInfo', () => {
+ expect(parseRepoOwnerPathInfo('/owner/repo/issues/new')).toEqual({ownerName: 'owner', repoName: 'repo'});
+ expect(parseRepoOwnerPathInfo('/owner/repo/releases')).toEqual({ownerName: 'owner', repoName: 'repo'});
+ expect(parseRepoOwnerPathInfo('/other')).toEqual({});
+ window.config.appSubUrl = '/sub';
+ expect(parseRepoOwnerPathInfo('/sub/owner/repo/issues/new')).toEqual({ownerName: 'owner', repoName: 'repo'});
+ expect(parseRepoOwnerPathInfo('/sub/owner/repo/compare/feature/branch-1...fix/branch-2')).toEqual({ownerName: 'owner', repoName: 'repo'});
+ window.config.appSubUrl = '';
});
test('parseUrl', () => {
diff --git a/web_src/js/utils.ts b/web_src/js/utils.ts
index efa144c3c7..e33b1413e8 100644
--- a/web_src/js/utils.ts
+++ b/web_src/js/utils.ts
@@ -1,5 +1,12 @@
import {decode, encode} from 'uint8-to-base64';
-import type {IssuePageInfo, IssuePathInfo} from './types.ts';
+import type {IssuePageInfo, IssuePathInfo, RepoOwnerPathInfo} from './types.ts';
+import {toggleClass, toggleElem} from './utils/dom.ts';
+
+// transform /path/to/file.ext to /path/to
+export function dirname(path: string): string {
+ const lastSlashIndex = path.lastIndexOf('/');
+ return lastSlashIndex < 0 ? '' : path.substring(0, lastSlashIndex);
+}
// transform /path/to/file.ext to file.ext
export function basename(path: string): string {
@@ -32,16 +39,17 @@ export function stripTags(text: string): string {
}
export function parseIssueHref(href: string): IssuePathInfo {
+ // FIXME: it should use pathname and trim the appSubUrl ahead
const path = (href || '').replace(/[#?].*$/, '');
const [_, ownerName, repoName, pathType, indexString] = /([^/]+)\/([^/]+)\/(issues|pulls)\/([0-9]+)/.exec(path) || [];
return {ownerName, repoName, pathType, indexString};
}
-export function parseIssueNewHref(href: string): IssuePathInfo {
- const path = (href || '').replace(/[#?].*$/, '');
- const [_, ownerName, repoName, pathTypeField] = /([^/]+)\/([^/]+)\/(issues\/new|compare\/.+\.\.\.)/.exec(path) || [];
- const pathType = pathTypeField.startsWith('issues/new') ? 'issues' : 'pulls';
- return {ownerName, repoName, pathType};
+export function parseRepoOwnerPathInfo(pathname: string): RepoOwnerPathInfo {
+ const appSubUrl = window.config.appSubUrl;
+ if (appSubUrl && pathname.startsWith(appSubUrl)) pathname = pathname.substring(appSubUrl.length);
+ const [_, ownerName, repoName] = /([^/]+)\/([^/]+)/.exec(pathname) || [];
+ return {ownerName, repoName};
}
export function parseIssuePageInfo(): IssuePageInfo {
@@ -165,10 +173,31 @@ export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
-export function isImageFile({name, type}: {name: string, type?: string}): boolean {
+export function isImageFile({name, type}: {name?: string, type?: string}): boolean {
return /\.(avif|jpe?g|png|gif|webp|svg|heic)$/i.test(name || '') || type?.startsWith('image/');
}
-export function isVideoFile({name, type}: {name: string, type?: string}): boolean {
+export function isVideoFile({name, type}: {name?: string, type?: string}): boolean {
return /\.(mpe?g|mp4|mkv|webm)$/i.test(name || '') || type?.startsWith('video/');
}
+
+export function toggleFullScreen(fullscreenElementsSelector: string, isFullScreen: boolean, sourceParentSelector?: string): void {
+ // hide other elements
+ const headerEl = document.querySelector('#navbar');
+ const contentEl = document.querySelector('.page-content');
+ const footerEl = document.querySelector('.page-footer');
+ toggleElem(headerEl, !isFullScreen);
+ toggleElem(contentEl, !isFullScreen);
+ toggleElem(footerEl, !isFullScreen);
+
+ const sourceParentEl = sourceParentSelector ? document.querySelector(sourceParentSelector) : contentEl;
+
+ const fullScreenEl = document.querySelector(fullscreenElementsSelector);
+ const outerEl = document.querySelector('.full.height');
+ toggleClass(fullscreenElementsSelector, 'fullscreen', isFullScreen);
+ if (isFullScreen) {
+ outerEl.append(fullScreenEl);
+ } else {
+ sourceParentEl.append(fullScreenEl);
+ }
+}
diff --git a/web_src/js/utils/dom.test.ts b/web_src/js/utils/dom.test.ts
index 6e71596850..057ea9808c 100644
--- a/web_src/js/utils/dom.test.ts
+++ b/web_src/js/utils/dom.test.ts
@@ -1,4 +1,10 @@
-import {createElementFromAttrs, createElementFromHTML, queryElemChildren, querySingleVisibleElem} from './dom.ts';
+import {
+ createElementFromAttrs,
+ createElementFromHTML,
+ queryElemChildren,
+ querySingleVisibleElem,
+ toggleElem,
+} from './dom.ts';
test('createElementFromHTML', () => {
expect(createElementFromHTML('<a>foo<span>bar</span></a>').outerHTML).toEqual('<a>foo<span>bar</span></a>');
@@ -19,10 +25,14 @@ test('createElementFromAttrs', () => {
});
test('querySingleVisibleElem', () => {
- let el = createElementFromHTML('<div><span>foo</span></div>');
+ let el = createElementFromHTML('<div></div>');
+ expect(querySingleVisibleElem(el, 'span')).toBeNull();
+ el = createElementFromHTML('<div><span>foo</span></div>');
expect(querySingleVisibleElem(el, 'span').textContent).toEqual('foo');
el = createElementFromHTML('<div><span style="display: none;">foo</span><span>bar</span></div>');
expect(querySingleVisibleElem(el, 'span').textContent).toEqual('bar');
+ el = createElementFromHTML('<div><span class="some-class tw-hidden">foo</span><span>bar</span></div>');
+ expect(querySingleVisibleElem(el, 'span').textContent).toEqual('bar');
el = createElementFromHTML('<div><span>foo</span><span>bar</span></div>');
expect(() => querySingleVisibleElem(el, 'span')).toThrowError('Expected exactly one visible element');
});
@@ -32,3 +42,13 @@ test('queryElemChildren', () => {
const children = queryElemChildren(el, '.a');
expect(children.length).toEqual(1);
});
+
+test('toggleElem', () => {
+ const el = createElementFromHTML('<p><div>a</div><div class="tw-hidden">b</div></p>');
+ toggleElem(el.children);
+ expect(el.outerHTML).toEqual('<p><div class="tw-hidden">a</div><div class="">b</div></p>');
+ toggleElem(el.children, false);
+ expect(el.outerHTML).toEqual('<p><div class="tw-hidden">a</div><div class="tw-hidden">b</div></p>');
+ toggleElem(el.children, true);
+ expect(el.outerHTML).toEqual('<p><div class="">a</div><div class="">b</div></p>');
+});
diff --git a/web_src/js/utils/dom.ts b/web_src/js/utils/dom.ts
index e24cb29bac..3b14b9bcea 100644
--- a/web_src/js/utils/dom.ts
+++ b/web_src/js/utils/dom.ts
@@ -9,55 +9,50 @@ type ElementsCallback<T extends Element> = (el: T) => Promisable<any>;
type ElementsCallbackWithArgs = (el: Element, ...args: any[]) => Promisable<any>;
export type DOMEvent<E extends Event, T extends Element = HTMLElement> = E & { target: Partial<T>; };
-function elementsCall(el: ElementArg, func: ElementsCallbackWithArgs, ...args: any[]) {
+function elementsCall(el: ElementArg, func: ElementsCallbackWithArgs, ...args: any[]): ArrayLikeIterable<Element> {
if (typeof el === 'string' || el instanceof String) {
el = document.querySelectorAll(el as string);
}
if (el instanceof Node) {
func(el, ...args);
+ return [el];
} else if (el.length !== undefined) {
// this works for: NodeList, HTMLCollection, Array, jQuery
- for (const e of (el as ArrayLikeIterable<Element>)) {
- func(e, ...args);
- }
- } else {
- throw new Error('invalid argument to be shown/hidden');
+ const elems = el as ArrayLikeIterable<Element>;
+ for (const elem of elems) func(elem, ...args);
+ return elems;
}
+ throw new Error('invalid argument to be shown/hidden');
+}
+
+export function toggleClass(el: ElementArg, className: string, force?: boolean): ArrayLikeIterable<Element> {
+ return elementsCall(el, (e: Element) => {
+ if (force === true) {
+ e.classList.add(className);
+ } else if (force === false) {
+ e.classList.remove(className);
+ } else if (force === undefined) {
+ e.classList.toggle(className);
+ } else {
+ throw new Error('invalid force argument');
+ }
+ });
}
/**
- * @param el Element
+ * @param el ElementArg
* @param force force=true to show or force=false to hide, undefined to toggle
*/
-function toggleShown(el: Element, force: boolean) {
- if (force === true) {
- el.classList.remove('tw-hidden');
- } else if (force === false) {
- el.classList.add('tw-hidden');
- } else if (force === undefined) {
- el.classList.toggle('tw-hidden');
- } else {
- throw new Error('invalid force argument');
- }
-}
-
-export function showElem(el: ElementArg) {
- elementsCall(el, toggleShown, true);
-}
-
-export function hideElem(el: ElementArg) {
- elementsCall(el, toggleShown, false);
+export function toggleElem(el: ElementArg, force?: boolean): ArrayLikeIterable<Element> {
+ return toggleClass(el, 'tw-hidden', force === undefined ? force : !force);
}
-export function toggleElem(el: ElementArg, force?: boolean) {
- elementsCall(el, toggleShown, force);
+export function showElem(el: ElementArg): ArrayLikeIterable<Element> {
+ return toggleElem(el, true);
}
-export function isElemHidden(el: ElementArg) {
- const res: boolean[] = [];
- elementsCall(el, (e) => res.push(e.classList.contains('tw-hidden')));
- if (res.length > 1) throw new Error(`isElemHidden doesn't work for multiple elements`);
- return res[0];
+export function hideElem(el: ElementArg): ArrayLikeIterable<Element> {
+ return toggleElem(el, false);
}
function applyElemsCallback<T extends Element>(elems: ArrayLikeIterable<T>, fn?: ElementsCallback<T>): ArrayLikeIterable<T> {
@@ -87,7 +82,7 @@ export function queryElemChildren<T extends Element>(parent: Element | ParentNod
}
// it works like parent.querySelectorAll: all descendants are selected
-// in the future, all "queryElems(document, ...)" should be refactored to use a more specific parent
+// in the future, all "queryElems(document, ...)" should be refactored to use a more specific parent if the targets are not for page-level components.
export function queryElems<T extends HTMLElement>(parent: Element | ParentNode, selector: string, fn?: ElementsCallback<T>): ArrayLikeIterable<T> {
return applyElemsCallback<T>(parent.querySelectorAll(selector), fn);
}
@@ -166,6 +161,7 @@ export function autosize(textarea: HTMLTextAreaElement, {viewportMarginBottom =
function resizeToFit() {
if (isUserResized) return;
if (textarea.offsetWidth <= 0 && textarea.offsetHeight <= 0) return;
+ const previousMargin = textarea.style.marginBottom;
try {
const {top, bottom} = overflowOffset();
@@ -181,6 +177,9 @@ export function autosize(textarea: HTMLTextAreaElement, {viewportMarginBottom =
const curHeight = parseFloat(computedStyle.height);
const maxHeight = curHeight + bottom - adjustedViewportMarginBottom;
+ // In Firefox, setting auto height momentarily may cause the page to scroll up
+ // unexpectedly, prevent this by setting a temporary margin.
+ textarea.style.marginBottom = `${textarea.clientHeight}px`;
textarea.style.height = 'auto';
let newHeight = textarea.scrollHeight + borderAddOn;
@@ -201,6 +200,12 @@ export function autosize(textarea: HTMLTextAreaElement, {viewportMarginBottom =
textarea.style.height = `${newHeight}px`;
lastStyleHeight = textarea.style.height;
} finally {
+ // restore previous margin
+ if (previousMargin) {
+ textarea.style.marginBottom = previousMargin;
+ } else {
+ textarea.style.removeProperty('margin-bottom');
+ }
// ensure that the textarea is fully scrolled to the end, when the cursor
// is at the end during an input event
if (textarea.selectionStart === textarea.selectionEnd &&
@@ -255,12 +260,12 @@ export function loadElem(el: LoadableElement, src: string) {
// it can't use other transparent polyfill patches because PaleMoon also doesn't support "addEventListener(capture)"
const needSubmitEventPolyfill = typeof SubmitEvent === 'undefined';
-export function submitEventSubmitter(e) {
+export function submitEventSubmitter(e: any) {
e = e.originalEvent ?? e; // if the event is wrapped by jQuery, use "originalEvent", otherwise, use the event itself
return needSubmitEventPolyfill ? (e.target._submitter || null) : e.submitter;
}
-function submitEventPolyfillListener(e) {
+function submitEventPolyfillListener(e: DOMEvent<Event>) {
const form = e.target.closest('form');
if (!form) return;
form._submitter = e.target.closest('button:not([type]), button[type="submit"], input[type="submit"]');
@@ -273,28 +278,24 @@ export function initSubmitEventPolyfill() {
document.body.addEventListener('focus', submitEventPolyfillListener);
}
-/**
- * Check if an element is visible, equivalent to jQuery's `:visible` pseudo.
- * Note: This function doesn't account for all possible visibility scenarios.
- */
-export function isElemVisible(element: HTMLElement): boolean {
- if (!element) return false;
- // checking element.style.display is not necessary for browsers, but it is required by some tests with happy-dom because happy-dom doesn't really do layout
- return Boolean((element.offsetWidth || element.offsetHeight || element.getClientRects().length) && element.style.display !== 'none');
+export function isElemVisible(el: HTMLElement): boolean {
+ // Check if an element is visible, equivalent to jQuery's `:visible` pseudo.
+ // This function DOESN'T account for all possible visibility scenarios, its behavior is covered by the tests of "querySingleVisibleElem"
+ if (!el) return false;
+ // checking el.style.display is not necessary for browsers, but it is required by some tests with happy-dom because happy-dom doesn't really do layout
+ return !el.classList.contains('tw-hidden') && Boolean((el.offsetWidth || el.offsetHeight || el.getClientRects().length) && el.style.display !== 'none');
}
// replace selected text in a textarea while preserving editor history, e.g. CTRL-Z works after this
export function replaceTextareaSelection(textarea: HTMLTextAreaElement, text: string) {
const before = textarea.value.slice(0, textarea.selectionStart ?? undefined);
const after = textarea.value.slice(textarea.selectionEnd ?? undefined);
- let success = true;
+ let success = false;
textarea.contentEditable = 'true';
try {
success = document.execCommand('insertText', false, text); // eslint-disable-line @typescript-eslint/no-deprecated
- } catch {
- success = false;
- }
+ } catch {} // ignore the error if execCommand is not supported or failed
textarea.contentEditable = 'false';
if (success && !textarea.value.slice(0, textarea.selectionStart ?? undefined).endsWith(text)) {
@@ -307,10 +308,10 @@ export function replaceTextareaSelection(textarea: HTMLTextAreaElement, text: st
}
}
-// Warning: Do not enter any unsanitized variables here
export function createElementFromHTML<T extends HTMLElement>(htmlString: string): T {
htmlString = htmlString.trim();
- // some tags like "tr" are special, it must use a correct parent container to create
+ // There is no way to create some elements without a proper parent, jQuery's approach: https://github.com/jquery/jquery/blob/main/src/manipulation/wrapMap.js
+ // eslint-disable-next-line github/unescaped-html-literal
if (htmlString.startsWith('<tr')) {
const container = document.createElement('table');
container.innerHTML = htmlString;
@@ -355,10 +356,24 @@ export function querySingleVisibleElem<T extends HTMLElement>(parent: Element, s
return candidates.length ? candidates[0] as T : null;
}
-export function addDelegatedEventListener<T extends HTMLElement, E extends Event>(parent: Node, type: string, selector: string, listener: (elem: T, e: E) => void | Promise<any>, options?: boolean | AddEventListenerOptions) {
+export function addDelegatedEventListener<T extends HTMLElement, E extends Event>(parent: Node, type: string, selector: string, listener: (elem: T, e: E) => Promisable<void>, options?: boolean | AddEventListenerOptions) {
parent.addEventListener(type, (e: Event) => {
const elem = (e.target as HTMLElement).closest(selector);
- if (!elem) return;
+ // It strictly checks "parent contains the target elem" to avoid side effects of selector running on outside the parent.
+ // Keep in mind that the elem could have been removed from parent by other event handlers before this event handler is called.
+ // For example, tippy popup item, the tippy popup could be hidden and removed from DOM before this.
+ // It is the caller's responsibility to make sure the elem is still in parent's DOM when this event handler is called.
+ if (!elem || (parent !== document && !parent.contains(elem))) return;
listener(elem as T, e as E);
}, options);
}
+
+// Returns whether a click event is a left-click without any modifiers held
+export function isPlainClick(e: MouseEvent) {
+ return e.button === 0 && !e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey;
+}
+
+let elemIdCounter = 0;
+export function generateElemId(prefix: string = ''): string {
+ return `${prefix}${elemIdCounter++}`;
+}
diff --git a/web_src/js/utils/html.test.ts b/web_src/js/utils/html.test.ts
new file mode 100644
index 0000000000..3028b7bb0a
--- /dev/null
+++ b/web_src/js/utils/html.test.ts
@@ -0,0 +1,8 @@
+import {html, htmlEscape, htmlRaw} from './html.ts';
+
+test('html', async () => {
+ expect(html`<a>${'<>&\'"'}</a>`).toBe(`<a>&lt;&gt;&amp;&#39;&quot;</a>`);
+ expect(html`<a>${htmlRaw('<img>')}</a>`).toBe(`<a><img></a>`);
+ expect(html`<a>${htmlRaw`<img ${'&'}>`}</a>`).toBe(`<a><img &amp;></a>`);
+ expect(htmlEscape(`<a></a>`)).toBe(`&lt;a&gt;&lt;/a&gt;`);
+});
diff --git a/web_src/js/utils/html.ts b/web_src/js/utils/html.ts
new file mode 100644
index 0000000000..1252032ee0
--- /dev/null
+++ b/web_src/js/utils/html.ts
@@ -0,0 +1,32 @@
+export function htmlEscape(s: string, ...args: Array<any>): string {
+ if (args.length !== 0) throw new Error('use html or htmlRaw instead of htmlEscape'); // check legacy usages
+ return s.replace(/&/g, '&amp;')
+ .replace(/"/g, '&quot;')
+ .replace(/'/g, '&#39;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;');
+}
+
+class rawObject {
+ private readonly value: string;
+ constructor(v: string) { this.value = v }
+ toString(): string { return this.value }
+}
+
+export function html(tmpl: TemplateStringsArray, ...parts: Array<any>): string {
+ let output = tmpl[0];
+ for (let i = 0; i < parts.length; i++) {
+ const value = parts[i];
+ const valueEscaped = (value instanceof rawObject) ? value.toString() : htmlEscape(String(value));
+ output = output + valueEscaped + tmpl[i + 1];
+ }
+ return output;
+}
+
+export function htmlRaw(s: string|TemplateStringsArray, ...tmplParts: Array<any>): rawObject {
+ if (typeof s === 'string') {
+ if (tmplParts.length !== 0) throw new Error("either htmlRaw('str') or htmlRaw`tmpl`");
+ return new rawObject(s);
+ }
+ return new rawObject(html(s, ...tmplParts));
+}
diff --git a/web_src/js/utils/image.test.ts b/web_src/js/utils/image.test.ts
index da0605f1d0..49856c891c 100644
--- a/web_src/js/utils/image.test.ts
+++ b/web_src/js/utils/image.test.ts
@@ -4,7 +4,7 @@ const pngNoPhys = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA
const pngPhys = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAEElEQVQI12OQNZcAIgYIBQAL8gGxdzzM0A==';
const pngEmpty = 'data:image/png;base64,';
-async function dataUriToBlob(datauri) {
+async function dataUriToBlob(datauri: string) {
return await (await globalThis.fetch(datauri)).blob();
}
diff --git a/web_src/js/utils/time.ts b/web_src/js/utils/time.ts
index 6951ebfedb..c63498345f 100644
--- a/web_src/js/utils/time.ts
+++ b/web_src/js/utils/time.ts
@@ -54,7 +54,7 @@ export type DayDataObject = {
}
export function fillEmptyStartDaysWithZeroes(startDays: number[], data: DayDataObject): DayData[] {
- const result = {};
+ const result: Record<string, any> = {};
for (const startDay of startDays) {
result[startDay] = data[startDay] || {'week': startDay, 'additions': 0, 'deletions': 0, 'commits': 0};
diff --git a/web_src/js/webcomponents/absolute-date.test.ts b/web_src/js/webcomponents/absolute-date.test.ts
index a3866829a7..bf591358bd 100644
--- a/web_src/js/webcomponents/absolute-date.test.ts
+++ b/web_src/js/webcomponents/absolute-date.test.ts
@@ -20,7 +20,7 @@ test('toAbsoluteLocaleDate', () => {
// test different timezone
const oldTZ = process.env.TZ;
process.env.TZ = 'America/New_York';
- expect(new Date('2024-03-15').toLocaleString()).toEqual('3/14/2024, 8:00:00 PM');
- expect(toAbsoluteLocaleDate('2024-03-15')).toEqual('3/15/2024, 12:00:00 AM');
+ expect(new Date('2024-03-15').toLocaleString('en-US')).toEqual('3/14/2024, 8:00:00 PM');
+ expect(toAbsoluteLocaleDate('2024-03-15', 'en-US')).toEqual('3/15/2024, 12:00:00 AM');
process.env.TZ = oldTZ;
});
diff --git a/web_src/js/webcomponents/absolute-date.ts b/web_src/js/webcomponents/absolute-date.ts
index 8eb1c3e37e..23a8606673 100644
--- a/web_src/js/webcomponents/absolute-date.ts
+++ b/web_src/js/webcomponents/absolute-date.ts
@@ -15,7 +15,7 @@ window.customElements.define('absolute-date', class extends HTMLElement {
initialized = false;
update = () => {
- const opt: Intl.DateTimeFormatOptions = {};
+ const opt: Record<string, string> = {};
for (const attr of ['year', 'month', 'weekday', 'day']) {
if (this.getAttribute(attr)) opt[attr] = this.getAttribute(attr);
}
diff --git a/web_src/js/webcomponents/overflow-menu.ts b/web_src/js/webcomponents/overflow-menu.ts
index 4e729a268a..ae93f2b758 100644
--- a/web_src/js/webcomponents/overflow-menu.ts
+++ b/web_src/js/webcomponents/overflow-menu.ts
@@ -1,6 +1,6 @@
import {throttle} from 'throttle-debounce';
import {createTippy} from '../modules/tippy.ts';
-import {isDocumentFragmentOrElementNode} from '../utils/dom.ts';
+import {addDelegatedEventListener, isDocumentFragmentOrElementNode} from '../utils/dom.ts';
import octiconKebabHorizontal from '../../../public/assets/img/svg/octicon-kebab-horizontal.svg';
window.customElements.define('overflow-menu', class extends HTMLElement {
@@ -12,10 +12,14 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
mutationObserver: MutationObserver;
lastWidth: number;
+ updateButtonActivationState() {
+ if (!this.button || !this.tippyContent) return;
+ this.button.classList.toggle('active', Boolean(this.tippyContent.querySelector('.item.active')));
+ }
+
updateItems = throttle(100, () => {
if (!this.tippyContent) {
const div = document.createElement('div');
- div.classList.add('tippy-target');
div.tabIndex = -1; // for initial focus, programmatic focus only
div.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
@@ -64,9 +68,10 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
}
}
});
- this.append(div);
+ div.classList.add('tippy-target');
+ this.handleItemClick(div, '.tippy-target > .item');
this.tippyContent = div;
- }
+ } // end if: no tippyContent and create a new one
const itemFlexSpace = this.menuItemsEl.querySelector<HTMLSpanElement>('.item-flex-space');
const itemOverFlowMenuButton = this.querySelector<HTMLButtonElement>('.overflow-menu-button');
@@ -88,7 +93,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
const menuRight = this.offsetLeft + this.offsetWidth;
const menuItems = this.menuItemsEl.querySelectorAll<HTMLElement>('.item, .item-flex-space');
let afterFlexSpace = false;
- for (const item of menuItems) {
+ for (const [idx, item] of menuItems.entries()) {
if (item.classList.contains('item-flex-space')) {
afterFlexSpace = true;
continue;
@@ -96,7 +101,10 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
if (afterFlexSpace) item.setAttribute('data-after-flex-space', 'true');
const itemRight = item.offsetLeft + item.offsetWidth;
if (menuRight - itemRight < 38) { // roughly the width of .overflow-menu-button with some extra space
- this.tippyItems.push(item);
+ const onlyLastItem = idx === menuItems.length - 1 && this.tippyItems.length === 0;
+ const lastItemFit = onlyLastItem && menuRight - itemRight > 0;
+ const moveToPopup = !onlyLastItem || !lastItemFit;
+ if (moveToPopup) this.tippyItems.push(item);
}
}
itemFlexSpace?.style.removeProperty('display');
@@ -107,6 +115,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
const btn = this.querySelector('.overflow-menu-button');
btn?._tippy?.destroy();
btn?.remove();
+ this.button = null;
return;
}
@@ -126,18 +135,17 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
// update existing tippy
if (this.button?._tippy) {
this.button._tippy.setContent(this.tippyContent);
+ this.updateButtonActivationState();
return;
}
// create button initially
- const btn = document.createElement('button');
- btn.classList.add('overflow-menu-button');
- btn.setAttribute('aria-label', window.config.i18n.more_items);
- btn.innerHTML = octiconKebabHorizontal;
- this.append(btn);
- this.button = btn;
-
- createTippy(btn, {
+ this.button = document.createElement('button');
+ this.button.classList.add('overflow-menu-button');
+ this.button.setAttribute('aria-label', window.config.i18n.more_items);
+ this.button.innerHTML = octiconKebabHorizontal;
+ this.append(this.button);
+ createTippy(this.button, {
trigger: 'click',
hideOnClick: true,
interactive: true,
@@ -151,6 +159,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
}, 0);
},
});
+ this.updateButtonActivationState();
});
init() {
@@ -187,6 +196,14 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
}
});
this.resizeObserver.observe(this);
+ this.handleItemClick(this, '.overflow-menu-items > .item');
+ }
+
+ handleItemClick(el: Element, selector: string) {
+ addDelegatedEventListener(el, 'click', selector, () => {
+ this.button?._tippy?.hide();
+ this.updateButtonActivationState();
+ });
}
connectedCallback() {
diff --git a/web_src/js/webcomponents/polyfill.test.ts b/web_src/js/webcomponents/polyfill.test.ts
new file mode 100644
index 0000000000..4fb4621547
--- /dev/null
+++ b/web_src/js/webcomponents/polyfill.test.ts
@@ -0,0 +1,7 @@
+import {weakRefClass} from './polyfills.ts';
+
+test('polyfillWeakRef', () => {
+ const WeakRef = weakRefClass();
+ const r = new WeakRef(123);
+ expect(r.deref()).toEqual(123);
+});
diff --git a/web_src/js/webcomponents/polyfills.ts b/web_src/js/webcomponents/polyfills.ts
index 4a84ee9562..9575324b5a 100644
--- a/web_src/js/webcomponents/polyfills.ts
+++ b/web_src/js/webcomponents/polyfills.ts
@@ -16,3 +16,19 @@ try {
return intlNumberFormat(locales, options);
};
}
+
+export function weakRefClass() {
+ const weakMap = new WeakMap();
+ return class {
+ constructor(target: any) {
+ weakMap.set(this, target);
+ }
+ deref() {
+ return weakMap.get(this);
+ }
+ };
+}
+
+if (!window.WeakRef) {
+ window.WeakRef = weakRefClass() as any;
+}
diff --git a/web_src/svg/gitea-feishu.svg b/web_src/svg/gitea-feishu.svg
new file mode 100644
index 0000000000..57941978d1
--- /dev/null
+++ b/web_src/svg/gitea-feishu.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="7 7 26 26" width="20" height="20"><path d="M21.069 20.504l.063-.06.125-.122.085-.084.256-.254.348-.344.299-.296.281-.278.293-.289.269-.266.374-.37.218-.206.419-.359.404-.306.598-.386.617-.33.606-.265.348-.127.177-.058a14.78 14.78 0 0 0-2.793-5.603c-.252-.318-.639-.502-1.047-.502H12.221c-.196 0-.277.249-.119.364a31.49 31.49 0 0 1 8.943 10.162c.008-.007.016-.015.025-.023z" fill="#00d6b9"/><path d="M16.791 30c5.57 0 10.423-3.074 12.955-7.618.089-.159.175-.321.258-.484a6.12 6.12 0 0 1-.425.699c-.055.078-.111.155-.17.23a6.29 6.29 0 0 1-.225.274c-.062.07-.123.138-.188.206a5.61 5.61 0 0 1-.407.384 5.53 5.53 0 0 1-.24.195 7.12 7.12 0 0 1-.292.21c-.063.043-.126.084-.191.122s-.134.081-.204.119c-.14.078-.282.149-.428.215a5.53 5.53 0 0 1-.385.157 5.81 5.81 0 0 1-.43.138 5.91 5.91 0 0 1-.661.143c-.162.025-.325.044-.491.055-.173.012-.348.016-.525.014-.193-.003-.388-.015-.585-.037-.144-.015-.289-.037-.433-.062-.126-.022-.252-.049-.38-.079l-.2-.051-.555-.155-.275-.081-.41-.125-.334-.107-.317-.104-.215-.073-.26-.091-.186-.066-.367-.134-.212-.081-.284-.11-.299-.119-.193-.079-.24-.1-.185-.078-.192-.084-.166-.073-.152-.067-.153-.07-.159-.073-.2-.093-.208-.099-.222-.108-.189-.093c-3.335-1.668-6.295-3.89-8.822-6.583-.126-.134-.349-.045-.349.138l.005 9.52v.773c0 .448.222.87.595 1.118C10.946 29.092 13.762 30 16.791 30z" fill="#3370ff"/><path d="M29.746 22.382h0l.051-.093-.051.093zm.231-.435l.014-.025.007-.012-.021.037z" fill="#133c92"/><path d="M33.151 16.582c-1.129-.556-2.399-.869-3.744-.869a8.45 8.45 0 0 0-2.303.317l-.252.075-.177.058-.348.127-.606.265-.617.33-.598.386-.404.306-.419.359-.218.206-.374.37-.269.266-.293.289-.281.278-.299.296-.348.344-.256.254-.085.084-.125.122-.063.06-.095.09-.105.099c-.924.848-1.956 1.581-3.072 2.175l.2.093.159.073.153.07.152.067.166.073.192.084.185.078.24.1.193.079.299.119.284.11.212.081.367.134.186.066.26.09.215.073.317.104.334.107.41.125.275.081.555.155.2.051.379.079.433.062.585.037.525-.014.491-.055a5.61 5.61 0 0 0 .66-.143l.43-.138.385-.158.427-.215.204-.119.191-.122.292-.21.24-.195.407-.384.188-.206.225-.274.17-.23a6.13 6.13 0 0 0 .421-.693l.144-.288 1.305-2.599-.003.006a8.07 8.07 0 0 1 1.697-2.439z" fill="#133c9a"/></svg>
diff --git a/web_src/svg/material-folder-generic.svg b/web_src/svg/material-folder-generic.svg
new file mode 100644
index 0000000000..a6c6262c17
--- /dev/null
+++ b/web_src/svg/material-folder-generic.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 4H4c-1.11 0-2 .89-2 2v12a2 2 0 002 2h16a2 2 0 002-2V8a2 2 0 00-2-2h-8l-2-2z" fill="#42a5f5"/></svg> \ No newline at end of file
diff --git a/web_src/svg/material-folder-symlink.svg b/web_src/svg/material-folder-symlink.svg
new file mode 100644
index 0000000000..2db7bcd4de
--- /dev/null
+++ b/web_src/svg/material-folder-symlink.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 4H4c-1.11 0-2 .89-2 2v12a2 2 0 002 2h16a2 2 0 002-2V8a2 2 0 00-2-2h-8l-2-2z" fill="#42a5f5" opacity=".745"/><path d="M16.972 10.757v2.641h-6.561v5.281h6.561v2.641l6.562-5.281-6.562-5.282z" opacity=".81" fill="#c5e5fd"/></svg> \ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
index 276c758e2c..92f479bc0c 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -76,16 +76,10 @@ export default {
mode: isProduction ? 'production' : 'development',
entry: {
index: [
- fileURLToPath(new URL('web_src/js/globals.ts', import.meta.url)),
- fileURLToPath(new URL('web_src/fomantic/build/semantic.js', import.meta.url)),
fileURLToPath(new URL('web_src/js/index.ts', import.meta.url)),
- fileURLToPath(new URL('node_modules/easymde/dist/easymde.min.css', import.meta.url)),
- fileURLToPath(new URL('web_src/fomantic/build/semantic.css', import.meta.url)),
+ fileURLToPath(new URL('web_src/fomantic/build/fomantic.css', import.meta.url)),
fileURLToPath(new URL('web_src/css/index.css', import.meta.url)),
],
- webcomponents: [
- fileURLToPath(new URL('web_src/js/webcomponents/index.ts', import.meta.url)),
- ],
swagger: [
fileURLToPath(new URL('web_src/js/standalone/swagger.ts', import.meta.url)),
fileURLToPath(new URL('web_src/css/standalone/swagger.css', import.meta.url)),
@@ -249,7 +243,6 @@ export default {
},
override: {
'khroma@*': {licenseName: 'MIT'}, // https://github.com/fabiospampinato/khroma/pull/33
- 'idiomorph@0.3.0': {licenseName: 'BSD-2-Clause'}, // https://github.com/bigskysoftware/idiomorph/pull/37
},
emitError: true,
allow: '(Apache-2.0 OR 0BSD OR BSD-2-Clause OR BSD-3-Clause OR MIT OR ISC OR CPAL-1.0 OR Unlicense OR EPL-1.0 OR EPL-2.0)',